mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-22 11:37:40 +01:00
Roundstart blob removal attempt 3 (#20403)
This commit is contained in:
@@ -412,22 +412,10 @@
|
||||
dat += "<tr><td><i>Head not found!</i></td></tr>"
|
||||
dat += "</table>"
|
||||
|
||||
if(GAMEMODE_IS_BLOB)
|
||||
var/datum/game_mode/blob/mode = SSticker.mode
|
||||
dat += "<br><table cellspacing=5><tr><td><B>Blob</B></td><td></td><td></td></tr>"
|
||||
dat += "<tr><td><i>Progress: [GLOB.blobs.len]/[mode.blobwincount]</i></td></tr>"
|
||||
|
||||
for(var/datum/mind/blob in mode.infected_crew)
|
||||
var/mob/M = blob.current
|
||||
if(M)
|
||||
dat += "<tr><td>[ADMIN_PP(M,"[M.real_name]")][M.client ? "" : " <i>(ghost)</i>"][M.stat == 2 ? " <b><font color=red>(DEAD)</font></b>" : ""]</td>"
|
||||
dat += "<td><A href='?priv_msg=[M.client?.ckey]'>PM</A> [ADMIN_FLW(M, "FLW")]</td>"
|
||||
else
|
||||
dat += "<tr><td><i>Blob not found!</i></td></tr>"
|
||||
dat += "</table>"
|
||||
|
||||
if(SSticker.mode.blob_overminds.len)
|
||||
dat += check_role_table("Blob Overminds", SSticker.mode.blob_overminds)
|
||||
dat += "<i>Blob Tiles: [length(GLOB.blobs)]</i>"
|
||||
|
||||
if(SSticker.mode.changelings.len)
|
||||
dat += check_role_table("Changelings", SSticker.mode.changelings)
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
|
||||
////////////////
|
||||
// BASE TYPE //
|
||||
////////////////
|
||||
|
||||
//Do not spawn
|
||||
/mob/living/simple_animal/hostile/blob
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
pass_flags = PASSBLOB
|
||||
status_flags = NONE //No throwing blobspores into deep space to despawn, or throwing blobbernaughts, which are bigger than you.
|
||||
faction = list(ROLE_BLOB)
|
||||
bubble_icon = "blob"
|
||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
minbodytemp = 0
|
||||
maxbodytemp = 360
|
||||
universal_speak = TRUE //So mobs can understand them when a blob uses Blob Broadcast
|
||||
sentience_type = SENTIENCE_OTHER
|
||||
gold_core_spawnable = NO_SPAWN
|
||||
can_be_on_fire = TRUE
|
||||
fire_damage = 3
|
||||
var/mob/camera/blob/overmind = null
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/proc/adjustcolors(a_color)
|
||||
if(a_color)
|
||||
color = a_color
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blob_act()
|
||||
if(stat != DEAD && health < maxHealth)
|
||||
for(var/i in 1 to 2)
|
||||
var/obj/effect/temp_visual/heal/H = new /obj/effect/temp_visual/heal(get_turf(src)) //hello yes you are being healed
|
||||
if(overmind)
|
||||
H.color = overmind.blob_reagent_datum.complementary_color
|
||||
if(H.color == COMPLEMENTARY_COLOR_RIPPING_TENDRILS) //the colour define for ripping tendrils
|
||||
H.color = COLOR_HEALING_GREEN //bye red cross
|
||||
else
|
||||
H.color = COLOR_BLACK
|
||||
adjustHealth(-maxHealth * 0.0125)
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/Process_Spacemove(movement_dir = 0)
|
||||
// Use any nearby blob structures to allow space moves.
|
||||
for(var/obj/structure/blob/B in range(1, src))
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
////////////////
|
||||
// BLOB SPORE //
|
||||
////////////////
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore
|
||||
name = "blob"
|
||||
desc = "Some blob thing."
|
||||
icon_state = "blobpod"
|
||||
icon_living = "blobpod"
|
||||
health = 40
|
||||
maxHealth = 40
|
||||
melee_damage_lower = 2
|
||||
melee_damage_upper = 4
|
||||
obj_damage = 20
|
||||
environment_smash = ENVIRONMENT_SMASH_STRUCTURES
|
||||
attacktext = "hits"
|
||||
attack_sound = 'sound/weapons/genhit1.ogg'
|
||||
flying = TRUE
|
||||
speak_emote = list("pulses")
|
||||
var/obj/structure/blob/factory/factory = null
|
||||
var/list/human_overlays = list()
|
||||
var/mob/living/carbon/human/oldguy
|
||||
var/is_zombie = FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/CanPass(atom/movable/mover, turf/target, height=0)
|
||||
if(istype(mover, /obj/structure/blob))
|
||||
return 1
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/Initialize(mapload, obj/structure/blob/factory/linked_node)
|
||||
. = ..()
|
||||
if(istype(linked_node))
|
||||
factory = linked_node
|
||||
factory.spores += src
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/Life(seconds, times_fired)
|
||||
|
||||
if(!is_zombie && isturf(src.loc))
|
||||
for(var/mob/living/carbon/human/H in oview(src, 1)) //Only for corpse right next to/on same tile
|
||||
if(H.stat == DEAD || (!H.check_death_method() && H.health <= HEALTH_THRESHOLD_DEAD))
|
||||
Zombify(H)
|
||||
break
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/proc/Zombify(mob/living/carbon/human/H)
|
||||
if(!H.check_death_method())
|
||||
H.death()
|
||||
var/obj/item/organ/external/head/head_organ = H.get_organ("head")
|
||||
is_zombie = TRUE
|
||||
if(H.wear_suit)
|
||||
var/obj/item/clothing/suit/armor/A = H.wear_suit
|
||||
if(A.armor && A.armor.getRating(MELEE))
|
||||
maxHealth += A.armor.getRating(MELEE) //That zombie's got armor, I want armor!
|
||||
maxHealth += 40
|
||||
health = maxHealth
|
||||
name = "blob zombie"
|
||||
desc = "A shambling corpse animated by the blob."
|
||||
mob_biotypes |= MOB_HUMANOID
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 15
|
||||
icon = H.icon
|
||||
speak_emote = list("groans")
|
||||
icon_state = "zombie2_s"
|
||||
if(head_organ && !(NO_HAIR in H.dna.species.species_traits))
|
||||
head_organ.h_style = null
|
||||
H.update_hair()
|
||||
human_overlays = H.overlays
|
||||
update_icons()
|
||||
H.forceMove(src)
|
||||
oldguy = H
|
||||
visible_message("<span class='warning'>The corpse of [H.name] suddenly rises!</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/death(gibbed)
|
||||
// Only execute the below if we successfuly died
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
// On death, create a small smoke of harmful gas (s-Acid)
|
||||
var/datum/effect_system/smoke_spread/chem/S = new
|
||||
var/turf/location = get_turf(src)
|
||||
|
||||
// Create the reagents to put into the air
|
||||
create_reagents(8)
|
||||
|
||||
if(overmind && overmind.blob_reagent_datum)
|
||||
reagents.add_reagent(overmind.blob_reagent_datum.id, 8)
|
||||
else
|
||||
reagents.add_reagent("spore", 8)
|
||||
|
||||
// Setup up the smoke spreader and start it.
|
||||
S.set_up(reagents, location, TRUE)
|
||||
S.start()
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/Destroy()
|
||||
if(factory)
|
||||
factory.spores -= src
|
||||
factory = null
|
||||
if(oldguy)
|
||||
oldguy.forceMove(get_turf(src))
|
||||
oldguy = null
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/update_icons()
|
||||
..()
|
||||
|
||||
adjustcolors(overmind?.blob_reagent_datum?.complementary_color)
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/adjustcolors(a_color)
|
||||
color = a_color
|
||||
|
||||
if(is_zombie)
|
||||
overlays.Cut()
|
||||
overlays = human_overlays
|
||||
var/image/I = image('icons/mob/blob.dmi', icon_state = "blob_head")
|
||||
I.color = color
|
||||
overlays += I
|
||||
|
||||
/////////////////
|
||||
// BLOBBERNAUT //
|
||||
/////////////////
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut
|
||||
name = "blobbernaut"
|
||||
desc = "Some HUGE blob thing."
|
||||
icon_state = "blobbernaut"
|
||||
icon_living = "blobbernaut"
|
||||
icon_dead = "blobbernaut_dead"
|
||||
health = 200
|
||||
maxHealth = 200
|
||||
melee_damage_lower = 10
|
||||
melee_damage_upper = 15
|
||||
obj_damage = 60
|
||||
attacktext = "hits"
|
||||
attack_sound = 'sound/effects/blobattack.ogg'
|
||||
speak_emote = list("gurgles")
|
||||
minbodytemp = 0
|
||||
maxbodytemp = 360
|
||||
force_threshold = 10
|
||||
mob_size = MOB_SIZE_LARGE
|
||||
environment_smash = ENVIRONMENT_SMASH_STRUCTURES
|
||||
pressure_resistance = 50
|
||||
see_in_dark = 8
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
|
||||
move_resist = MOVE_FORCE_OVERPOWERING
|
||||
a_intent = INTENT_HARM
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/Life(seconds, times_fired)
|
||||
if(stat != DEAD && (getBruteLoss() || getFireLoss())) // Heal on blob structures
|
||||
if(locate(/obj/structure/blob) in get_turf(src))
|
||||
adjustBruteLoss(-0.25)
|
||||
adjustFireLoss(-0.25)
|
||||
if(on_fire)
|
||||
adjust_fire_stacks(-1) // Slowly extinguish the flames
|
||||
else
|
||||
adjustBruteLoss(0.2) // If you are at full health, you won't lose health. You'll need it. However the moment anybody sneezes on you, the decaying will begin.
|
||||
adjustFireLoss(0.2)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/Initialize(mapload)
|
||||
. = ..()
|
||||
if(name == "blobbernaut")
|
||||
name = text("blobbernaut ([rand(1, 1000)])")
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/death(gibbed)
|
||||
// Only execute the below if we successfully died
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
flick("blobbernaut_death", src)
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/verb/communicate_overmind()
|
||||
set category = "Blobbernaut"
|
||||
set name = "Blob Telepathy"
|
||||
set desc = "Send a message to the Overmind"
|
||||
|
||||
if(stat != DEAD)
|
||||
blob_talk()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/proc/blob_talk()
|
||||
var/message = input(src, "Announce to the overmind", "Blob Telepathy")
|
||||
var/rendered
|
||||
var/follow_text
|
||||
if(message)
|
||||
for(var/mob/M in GLOB.mob_list)
|
||||
follow_text = isobserver(M) ? " ([ghost_follow_link(src, ghost = M)])" : ""
|
||||
rendered = "<font color=\"#EE4000\"><i><span class='game say'>Blob Telepathy, <span class='name'>[name]([overmind])</span>[follow_text] <span class='message'>states, \"[message]\"</span></span></i></font>"
|
||||
if(isovermind(M) || isobserver(M) || istype(M, /mob/living/simple_animal/hostile/blob/blobbernaut))
|
||||
M.show_message(rendered, EMOTE_AUDIBLE)
|
||||
@@ -0,0 +1,494 @@
|
||||
// Point controlling procs
|
||||
|
||||
/mob/camera/blob/proc/can_buy(cost = 15)
|
||||
if(blob_points < cost)
|
||||
to_chat(src, "<span class='warning'>You cannot afford this!</span>")
|
||||
return 0
|
||||
add_points(-cost)
|
||||
return 1
|
||||
|
||||
// Power verbs
|
||||
|
||||
/mob/camera/blob/verb/transport_core()
|
||||
set category = "Blob"
|
||||
set name = "Jump to Core"
|
||||
set desc = "Transport back to your core."
|
||||
|
||||
if(blob_core)
|
||||
src.loc = blob_core.loc
|
||||
|
||||
/mob/camera/blob/verb/jump_to_node()
|
||||
set category = "Blob"
|
||||
set name = "Jump to Node"
|
||||
set desc = "Transport back to a selected node."
|
||||
|
||||
if(GLOB.blob_nodes.len)
|
||||
var/list/nodes = list()
|
||||
for(var/i = 1; i <= GLOB.blob_nodes.len; i++)
|
||||
var/obj/structure/blob/node/B = GLOB.blob_nodes[i]
|
||||
nodes["Blob Node #[i] ([get_location_name(B)])"] = B
|
||||
var/node_name = input(src, "Choose a node to jump to.", "Node Jump") in nodes
|
||||
var/obj/structure/blob/node/chosen_node = nodes[node_name]
|
||||
if(chosen_node)
|
||||
src.loc = chosen_node.loc
|
||||
|
||||
/mob/camera/blob/verb/toggle_node_req()
|
||||
set category = "Blob"
|
||||
set name = "Toggle Node Requirement"
|
||||
set desc = "Toggle requiring nodes to place resource and factory blobs."
|
||||
nodes_required = !nodes_required
|
||||
if(nodes_required)
|
||||
to_chat(src, "<span class='warning'>You now require a nearby node or core to place factory and resource blobs.</span>")
|
||||
else
|
||||
to_chat(src, "<span class='warning'>You no longer require a nearby node or core to place factory and resource blobs.</span>")
|
||||
|
||||
/mob/camera/blob/verb/create_shield_power()
|
||||
set category = "Blob"
|
||||
set name = "Create/Upgrade Shield Blob (15)"
|
||||
set desc = "Create/Upgrade a shield blob. Using this on an existing shield blob turns it into a reflective blob, capable of reflecting most energy projectiles but making it much weaker than usual to brute attacks."
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
create_shield(T)
|
||||
|
||||
/mob/camera/blob/proc/create_shield(turf/T)
|
||||
|
||||
var/obj/structure/blob/B = locate(/obj/structure/blob) in T
|
||||
var/obj/structure/blob/shield/S = locate(/obj/structure/blob/shield) in T
|
||||
|
||||
if(!S)
|
||||
if(!B)//We are on a blob
|
||||
to_chat(src, "There is no blob here!")
|
||||
return
|
||||
|
||||
else if(!istype(B, /obj/structure/blob/normal))
|
||||
to_chat(src, "Unable to use this blob, find a normal one.")
|
||||
return
|
||||
|
||||
else if(!can_buy(15))
|
||||
return
|
||||
|
||||
B.color = blob_reagent_datum.color
|
||||
B.change_to(/obj/structure/blob/shield)
|
||||
else
|
||||
|
||||
if(istype(S, /obj/structure/blob/shield/reflective))
|
||||
to_chat(src, "<span class='warning'>There's already a reflector blob here!</span>")
|
||||
return
|
||||
|
||||
|
||||
else if(S.obj_integrity < S.max_integrity * 0.5)
|
||||
to_chat(src, "<span class='warning'>This shield blob is too damaged to be modified properly!</span>")
|
||||
return
|
||||
|
||||
else if (!can_buy(15))
|
||||
return
|
||||
|
||||
to_chat(src, "<span class='warning'>You secrete a reflective ooze over the shield blob, allowing it to reflect energy projectiles at the cost of reduced intregrity.</span>")
|
||||
|
||||
S.change_to(/obj/structure/blob/shield/reflective)
|
||||
S.color = blob_reagent_datum.color
|
||||
return
|
||||
|
||||
/mob/camera/blob/verb/create_resource()
|
||||
set category = "Blob"
|
||||
set name = "Create Resource Blob (40)"
|
||||
set desc = "Create a resource tower which will generate points for you."
|
||||
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
if(!T)
|
||||
return
|
||||
|
||||
var/obj/structure/blob/B = (locate(/obj/structure/blob) in T)
|
||||
|
||||
if(!B)//We are on a blob
|
||||
to_chat(src, "There is no blob here!")
|
||||
return
|
||||
|
||||
if(!istype(B, /obj/structure/blob/normal))
|
||||
to_chat(src, "Unable to use this blob, find a normal one.")
|
||||
return
|
||||
|
||||
for(var/obj/structure/blob/resource/blob in orange(4, T))
|
||||
to_chat(src, "There is a resource blob nearby, move more than 4 tiles away from it!")
|
||||
return
|
||||
|
||||
if(nodes_required)
|
||||
if(!(locate(/obj/structure/blob/node) in orange(3, T)) && !(locate(/obj/structure/blob/core) in orange(4, T)))
|
||||
to_chat(src, "<span class='warning'>You need to place this blob closer to a node or core!</span>")
|
||||
return //handholdotron 2000
|
||||
|
||||
if(!can_buy(40))
|
||||
return
|
||||
|
||||
B.color = blob_reagent_datum.color
|
||||
B.change_to(/obj/structure/blob/resource)
|
||||
var/obj/structure/blob/resource/R = locate() in T
|
||||
if(R)
|
||||
R.overmind = src
|
||||
|
||||
return
|
||||
|
||||
/mob/camera/blob/verb/create_node()
|
||||
set category = "Blob"
|
||||
set name = "Create Node Blob (60)"
|
||||
set desc = "Create a Node."
|
||||
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
if(!T)
|
||||
return
|
||||
|
||||
var/obj/structure/blob/B = (locate(/obj/structure/blob) in T)
|
||||
|
||||
if(!B)//We are on a blob
|
||||
to_chat(src, "There is no blob here!")
|
||||
return
|
||||
|
||||
if(!istype(B, /obj/structure/blob/normal))
|
||||
to_chat(src, "Unable to use this blob, find a normal one.")
|
||||
return
|
||||
|
||||
for(var/obj/structure/blob/node/blob in orange(5, T))
|
||||
to_chat(src, "There is another node nearby, move more than 5 tiles away from it!")
|
||||
return
|
||||
|
||||
if(!can_buy(60))
|
||||
return
|
||||
|
||||
B.change_to(/obj/structure/blob/node)
|
||||
var/obj/structure/blob/node/R = locate() in T
|
||||
if(R)
|
||||
R.adjustcolors(blob_reagent_datum.color)
|
||||
R.overmind = src
|
||||
return
|
||||
|
||||
|
||||
/mob/camera/blob/verb/create_factory()
|
||||
set category = "Blob"
|
||||
set name = "Create Factory Blob (60)"
|
||||
set desc = "Create a Spore producing blob."
|
||||
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
if(!T)
|
||||
return
|
||||
|
||||
var/obj/structure/blob/B = locate(/obj/structure/blob) in T
|
||||
if(!B)
|
||||
to_chat(src, "You must be on a blob!")
|
||||
return
|
||||
|
||||
if(!istype(B, /obj/structure/blob/normal))
|
||||
to_chat(src, "Unable to use this blob, find a normal one.")
|
||||
return
|
||||
|
||||
for(var/obj/structure/blob/factory/blob in orange(7, T))
|
||||
to_chat(src, "There is a factory blob nearby, move more than 7 tiles away from it!")
|
||||
return
|
||||
|
||||
if(nodes_required)
|
||||
if(!(locate(/obj/structure/blob/node) in orange(3, T)) && !(locate(/obj/structure/blob/core) in orange(4, T)))
|
||||
to_chat(src, "<span class='warning'>You need to place this blob closer to a node or core!</span>")
|
||||
return //handholdotron 2000
|
||||
|
||||
if(!can_buy(60))
|
||||
return
|
||||
|
||||
B.change_to(/obj/structure/blob/factory)
|
||||
B.color = blob_reagent_datum.color
|
||||
var/obj/structure/blob/factory/R = locate() in T
|
||||
if(R)
|
||||
R.overmind = src
|
||||
return
|
||||
|
||||
|
||||
/mob/camera/blob/verb/create_blobbernaut()
|
||||
set category = "Blob"
|
||||
set name = "Create Blobbernaut (60)"
|
||||
set desc = "Create a powerful blob-being, a Blobbernaut"
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
if(!T)
|
||||
return
|
||||
|
||||
var/obj/structure/blob/B = locate(/obj/structure/blob) in T
|
||||
if(!B)
|
||||
to_chat(src, "You must be on a blob!")
|
||||
return
|
||||
|
||||
if(!istype(B, /obj/structure/blob/factory))
|
||||
to_chat(src, "Unable to use this blob, find a factory blob.")
|
||||
return
|
||||
|
||||
if(!can_buy(60))
|
||||
return
|
||||
|
||||
var/mob/living/simple_animal/hostile/blob/blobbernaut/blobber = new /mob/living/simple_animal/hostile/blob/blobbernaut (get_turf(B))
|
||||
if(blobber)
|
||||
qdel(B)
|
||||
add_mob_to_overmind(blobber)
|
||||
blobber.AIStatus = AI_OFF
|
||||
blobber.LoseTarget()
|
||||
spawn()
|
||||
var/list/candidates = SSghost_spawns.poll_candidates("Do you want to play as a blobbernaut?", ROLE_BLOB, TRUE, 10 SECONDS, source = blobber, role_cleanname = "blobbernaut")
|
||||
if(length(candidates) && !QDELETED(blobber))
|
||||
var/mob/C = pick(candidates)
|
||||
if(C)
|
||||
blobber.key = C.key
|
||||
to_chat(blobber, "<span class='biggerdanger'>You are a blobbernaut! You must assist all blob lifeforms in their mission to consume everything!</span>")
|
||||
to_chat(blobber, "<span class='danger'>You heal while standing on blob structures, however you will decay slowly if you are damaged outside of the blob.</span>")
|
||||
if(!blobber.ckey)
|
||||
blobber.AIStatus = AI_ON
|
||||
return
|
||||
|
||||
|
||||
/mob/camera/blob/verb/relocate_core()
|
||||
set category = "Blob"
|
||||
set name = "Relocate Core (80)"
|
||||
set desc = "Relocates your core to the node you are on, your old core will be turned into a node."
|
||||
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
if(!T)
|
||||
return
|
||||
|
||||
var/obj/structure/blob/node/B = locate(/obj/structure/blob/node) in T
|
||||
if(!B)
|
||||
to_chat(src, "You must be on a blob node!")
|
||||
return
|
||||
|
||||
if(!can_buy(80))
|
||||
return
|
||||
|
||||
// The old switcharoo.
|
||||
var/turf/old_turf = blob_core.loc
|
||||
blob_core.loc = T
|
||||
B.loc = old_turf
|
||||
return
|
||||
|
||||
|
||||
/mob/camera/blob/verb/revert()
|
||||
set category = "Blob"
|
||||
set name = "Remove Blob"
|
||||
set desc = "Removes a blob. You will receive 30% cost refund for special Blob structures."
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
remove_blob(T)
|
||||
|
||||
/mob/camera/blob/proc/remove_blob(turf/T)
|
||||
|
||||
var/obj/structure/blob/B = locate(/obj/structure/blob) in T
|
||||
if(!T)
|
||||
return
|
||||
if(!B)
|
||||
to_chat(src, "<span class='warning'>There is no blob there!</span>")
|
||||
return
|
||||
if(B.point_return < 0)
|
||||
to_chat(src, "<span class='warning'>Unable to remove this blob.</span>")
|
||||
return
|
||||
if(max_blob_points < B.point_return + blob_points)
|
||||
to_chat(src, "<span class='warning'>You have too many resources to remove this blob!</span>")
|
||||
return
|
||||
if(B.point_return)
|
||||
add_points(B.point_return)
|
||||
to_chat(src, "<span class='notice'>Gained [B.point_return] resources from removing \the [B].</span>")
|
||||
qdel(B)
|
||||
return
|
||||
|
||||
|
||||
/mob/camera/blob/verb/expand_blob_power()
|
||||
set category = "Blob"
|
||||
set name = "Expand/Attack Blob (5)"
|
||||
set desc = "Attempts to create a new blob in this tile. If the tile isn't clear we will attack it, which might clear it."
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
expand_blob(T)
|
||||
|
||||
/mob/camera/blob/proc/expand_blob(turf/T)
|
||||
if(!T)
|
||||
return
|
||||
|
||||
if(!can_attack())
|
||||
return
|
||||
var/obj/structure/blob/B = locate() in T
|
||||
if(B)
|
||||
var/mob/living/M = locate() in T
|
||||
if(M)
|
||||
if(!can_buy(5))
|
||||
return
|
||||
to_chat(src, "You attack [M]!")
|
||||
blob_core.chemical_attack(T)
|
||||
return
|
||||
to_chat(src, "There is a blob here!")
|
||||
return
|
||||
|
||||
var/obj/structure/blob/OB = locate() in circlerange(T, 1)
|
||||
if(!OB)
|
||||
var/obj/structure/blob/DB = locate() in range(T, 1)
|
||||
if(!DB)
|
||||
to_chat(src, "There is no blob adjacent to you.")
|
||||
return
|
||||
if(!can_buy(5))
|
||||
return
|
||||
DB.double_expand(T, 0, blob_reagent_datum.color, src)
|
||||
DB.color = blob_reagent_datum.color
|
||||
return
|
||||
|
||||
if(!can_buy(5))
|
||||
return
|
||||
last_attack = world.time
|
||||
OB.expand(T, 0, blob_reagent_datum.color, src)
|
||||
blob_core.chemical_attack(T)
|
||||
OB.color = blob_reagent_datum.color
|
||||
return
|
||||
|
||||
|
||||
/mob/camera/blob/verb/rally_spores_power()
|
||||
set category = "Blob"
|
||||
set name = "Rally Spores"
|
||||
set desc = "Rally the spores to move to your location."
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
rally_spores(T)
|
||||
|
||||
/mob/camera/blob/proc/rally_spores(turf/T)
|
||||
to_chat(src, "You rally your spores.")
|
||||
|
||||
var/list/surrounding_turfs = block(locate(T.x - 1, T.y - 1, T.z), locate(T.x + 1, T.y + 1, T.z))
|
||||
if(!surrounding_turfs.len)
|
||||
return
|
||||
|
||||
for(var/mob/living/simple_animal/hostile/blob/blobspore/BS in GLOB.alive_mob_list)
|
||||
if(isturf(BS.loc) && get_dist(BS, T) <= 35)
|
||||
BS.LoseTarget()
|
||||
BS.Goto(pick(surrounding_turfs), BS.move_to_delay)
|
||||
return
|
||||
|
||||
/mob/camera/blob/verb/split_consciousness()
|
||||
set category = "Blob"
|
||||
set name = "Split consciousness (100) (One use)"
|
||||
set desc = "Expend resources to attempt to produce another sentient overmind."
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
return
|
||||
if(split_used)
|
||||
to_chat(src, "<span class='warning'>You have already produced an offspring.</span>")
|
||||
return
|
||||
if(is_offspring)
|
||||
to_chat(src, "<span class='warning'>You cannot split as an offspring of another Blob.</span>")
|
||||
return
|
||||
|
||||
var/obj/structure/blob/N = (locate(/obj/structure/blob) in T)
|
||||
if(!N)
|
||||
to_chat(src, "<span class='warning'>A node is required to birth your offspring.</span>")
|
||||
return
|
||||
if(!istype(N, /obj/structure/blob/node))
|
||||
to_chat(src, "<span class='warning'>A node is required to birth your offspring.</span>")
|
||||
return
|
||||
if(!can_buy(100))
|
||||
return
|
||||
|
||||
split_used = TRUE
|
||||
new /obj/structure/blob/core(get_turf(N), null, blob_core.point_rate, TRUE)
|
||||
qdel(N)
|
||||
|
||||
/mob/camera/blob/verb/blob_broadcast()
|
||||
set category = "Blob"
|
||||
set name = "Blob Broadcast"
|
||||
set desc = "Speak with your blob spores and blobbernauts as your mouthpieces. This action is free."
|
||||
|
||||
var/speak_text = clean_input("What would you like to say with your minions?", "Blob Broadcast", null)
|
||||
|
||||
if(!speak_text)
|
||||
return
|
||||
else
|
||||
to_chat(usr, "You broadcast with your minions, <B>[speak_text]</B>")
|
||||
for(var/mob/living/simple_animal/hostile/blob_minion in blob_mobs)
|
||||
if(blob_minion.stat == CONSCIOUS)
|
||||
blob_minion.say(speak_text)
|
||||
return
|
||||
|
||||
/mob/camera/blob/verb/create_storage()
|
||||
set category = "Blob"
|
||||
set name = "Create Storage Blob (40)"
|
||||
set desc = "Create a storage tower which will store extra resources for you. This increases your max resource cap by 50."
|
||||
|
||||
|
||||
var/turf/T = get_turf(src)
|
||||
|
||||
if(!T)
|
||||
return
|
||||
|
||||
var/obj/structure/blob/B = (locate(/obj/structure/blob) in T)
|
||||
|
||||
if(!B)//We are on a blob
|
||||
to_chat(src, "There is no blob here!")
|
||||
return
|
||||
|
||||
if(!istype(B, /obj/structure/blob/normal))
|
||||
to_chat(src, "Unable to use this blob, find a normal one.")
|
||||
return
|
||||
|
||||
for(var/obj/structure/blob/storage/blob in orange(3, T))
|
||||
to_chat(src, "There is a storage blob nearby, move more than 4 tiles away from it!")
|
||||
return
|
||||
|
||||
if(!can_buy(40))
|
||||
return
|
||||
|
||||
B.color = blob_reagent_datum.color
|
||||
B.change_to(/obj/structure/blob/storage)
|
||||
var/obj/structure/blob/storage/R = locate() in T
|
||||
if(R)
|
||||
R.overmind = src
|
||||
R.update_max_blob_points(50)
|
||||
|
||||
return
|
||||
|
||||
|
||||
/mob/camera/blob/verb/chemical_reroll()
|
||||
set category = "Blob"
|
||||
set name = "Reactive Chemical Adaptation (50)"
|
||||
set desc = "Replaces your chemical with a random, different one."
|
||||
|
||||
if(!can_buy(50))
|
||||
return
|
||||
|
||||
var/datum/reagent/blob/B = pick((subtypesof(/datum/reagent/blob) - blob_reagent_datum.type))
|
||||
blob_reagent_datum = new B
|
||||
|
||||
color = blob_reagent_datum.complementary_color
|
||||
|
||||
for(var/obj/structure/blob/BL in GLOB.blobs)
|
||||
BL.adjustcolors(blob_reagent_datum.color)
|
||||
|
||||
for(var/mob/living/simple_animal/hostile/blob/BLO)
|
||||
BLO.adjustcolors(blob_reagent_datum.complementary_color)
|
||||
|
||||
to_chat(src, "Your reagent is now: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> - [blob_reagent_datum.description]")
|
||||
|
||||
/mob/camera/blob/verb/blob_help()
|
||||
set category = "Blob"
|
||||
set name = "*Blob Help*"
|
||||
set desc = "Help on how to blob."
|
||||
to_chat(src, "<b>As the overmind, you can control the blob!</b>")
|
||||
to_chat(src, "Your blob reagent is: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> - [blob_reagent_datum.description]")
|
||||
to_chat(src, "<b>You can expand, which will attack people, damage objects, or place a Normal Blob if the tile is clear.</b>")
|
||||
to_chat(src, "<i>Normal Blobs</i> will expand your reach and can be upgraded into special blobs that perform certain functions.")
|
||||
to_chat(src, "<b>You can upgrade normal blobs into the following types of blob:</b>")
|
||||
to_chat(src, "<i>Shield Blobs</i> are strong and expensive blobs which take more damage. In additon, they are fireproof and can block air, use these to protect yourself from station fires. Upgrading them again will result in a <i>Reflective blob</i>, capable of reflecting laser projectiles at the cost of the strong blob's extra health.")
|
||||
to_chat(src, "<i>Resource Blobs</i> are blobs which produce more resources for you, build as many of these as possible to consume the station. This type of blob must be placed near node blobs or your core to work.")
|
||||
to_chat(src, "<i>Factory Blobs</i> are blobs that spawn blob spores which will attack nearby enemies. This type of blob must be placed near node blobs or your core to work.")
|
||||
to_chat(src, "<i>Blobbernauts</i> can be produced from factories for a cost, and are hard to kill, powerful, but ultimately dumb. The factory used to create one will be destroyed in the process.")
|
||||
to_chat(src, "<i>Storage Blobs</i> are storage towers which will store extra resources for you. This increases your max resource cap by 50.")
|
||||
to_chat(src, "<i>Node Blobs</i> are blobs which grow, like the core. Like the core it can activate resource and factory blobs.")
|
||||
to_chat(src, "<b>In addition to the buttons on your HUD, there are a few click shortcuts to speed up expansion and defense.</b>")
|
||||
to_chat(src, "<b>Shortcuts:</b> Click = Expand Blob <b>|</b> CTRL Click = Create Shield Blob <b>|</b> Middle Mouse Click = Rally Spores <b>|</b> Alt Click = Remove Blob")
|
||||
to_chat(src, "Attempting to talk will send a message to all other <b>overminds</b>, allowing you to coordinate with them.")
|
||||
@@ -0,0 +1,144 @@
|
||||
/obj/structure/blob/core
|
||||
name = "blob core"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blank_blob"
|
||||
max_integrity = 400
|
||||
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 75, ACID = 90)
|
||||
fire_resist = 2
|
||||
point_return = -1
|
||||
var/overmind_get_delay = 0 // we don't want to constantly try to find an overmind, do it every 5 minutes
|
||||
var/resource_delay = 0
|
||||
var/point_rate = 2
|
||||
var/is_offspring = null
|
||||
var/selecting = 0
|
||||
|
||||
/obj/structure/blob/core/Initialize(mapload, client/new_overmind = null, new_rate = 2, offspring)
|
||||
. = ..()
|
||||
GLOB.blob_cores += src
|
||||
START_PROCESSING(SSobj, src)
|
||||
GLOB.poi_list |= src
|
||||
adjustcolors(color) //so it atleast appears
|
||||
if(offspring)
|
||||
is_offspring = TRUE
|
||||
if(overmind)
|
||||
adjustcolors(overmind.blob_reagent_datum.color)
|
||||
if(!overmind)
|
||||
create_overmind(new_overmind)
|
||||
point_rate = new_rate
|
||||
|
||||
|
||||
/obj/structure/blob/core/adjustcolors(a_color)
|
||||
overlays.Cut()
|
||||
color = null
|
||||
var/image/I = new('icons/mob/blob.dmi', "blob")
|
||||
I.color = a_color
|
||||
overlays += I
|
||||
var/image/C = new('icons/mob/blob.dmi', "blob_core_overlay")
|
||||
overlays += C
|
||||
|
||||
|
||||
/obj/structure/blob/core/Destroy()
|
||||
GLOB.blob_cores -= src
|
||||
if(overmind)
|
||||
overmind.blob_core = null
|
||||
overmind = null
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
GLOB.poi_list.Remove(src)
|
||||
return ..()
|
||||
|
||||
/obj/structure/blob/core/ex_act(severity)
|
||||
var/damage = 50 - 10 * severity //remember, the core takes half brute damage, so this is 20/15/10 damage based on severity
|
||||
take_damage(damage, BRUTE, BOMB, 0)
|
||||
|
||||
/obj/structure/blob/core/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir, overmind_reagent_trigger = 1)
|
||||
. = ..()
|
||||
if(obj_integrity > 0)
|
||||
if(overmind) //we should have an overmind, but...
|
||||
overmind.update_health_hud()
|
||||
|
||||
/obj/structure/blob/core/RegenHealth()
|
||||
return // Don't regen, we handle it in Life()
|
||||
|
||||
/obj/structure/blob/core/Life(seconds, times_fired)
|
||||
if(!overmind)
|
||||
create_overmind()
|
||||
else
|
||||
if(resource_delay <= world.time)
|
||||
resource_delay = world.time + 10 // 1 second
|
||||
overmind.add_points(point_rate)
|
||||
obj_integrity = min(max_integrity, obj_integrity + 1)
|
||||
if(overmind)
|
||||
overmind.update_health_hud()
|
||||
if(overmind)
|
||||
for(var/i = 1; i < 8; i += i)
|
||||
Pulse(0, i, overmind.blob_reagent_datum.color)
|
||||
else
|
||||
for(var/i = 1; i < 8; i += i)
|
||||
Pulse(0, i, color)
|
||||
for(var/b_dir in GLOB.alldirs)
|
||||
if(!prob(5))
|
||||
continue
|
||||
var/obj/structure/blob/normal/B = locate() in get_step(src, b_dir)
|
||||
if(B)
|
||||
B.change_to(/obj/structure/blob/shield/core)
|
||||
if(B && overmind)
|
||||
B.color = overmind.blob_reagent_datum.color
|
||||
else
|
||||
B.color = color
|
||||
color = null
|
||||
..()
|
||||
|
||||
|
||||
/obj/structure/blob/core/proc/create_overmind(client/new_overmind, override_delay)
|
||||
if(overmind_get_delay > world.time && !override_delay)
|
||||
return
|
||||
|
||||
overmind_get_delay = world.time + 5 MINUTES
|
||||
|
||||
if(overmind)
|
||||
qdel(overmind)
|
||||
|
||||
INVOKE_ASYNC(src, PROC_REF(get_new_overmind), new_overmind)
|
||||
|
||||
/obj/structure/blob/core/proc/get_new_overmind(client/new_overmind)
|
||||
var/mob/C = null
|
||||
var/list/candidates = list()
|
||||
if(!new_overmind)
|
||||
// sendit
|
||||
if(is_offspring)
|
||||
candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob offspring?", ROLE_BLOB, TRUE, 10 SECONDS, source = src)
|
||||
else
|
||||
candidates = SSghost_spawns.poll_candidates("Do you want to play as a blob?", ROLE_BLOB, TRUE, 10 SECONDS, source = src)
|
||||
|
||||
if(length(candidates))
|
||||
C = pick(candidates)
|
||||
else
|
||||
C = new_overmind
|
||||
|
||||
if(C && !QDELETED(src))
|
||||
var/mob/camera/blob/B = new(loc)
|
||||
B.is_offspring = is_offspring
|
||||
B.key = C.key
|
||||
B.blob_core = src
|
||||
overmind = B
|
||||
color = overmind.blob_reagent_datum.color
|
||||
if(B.mind && !B.mind.special_role)
|
||||
B.mind.make_Overmind()
|
||||
|
||||
/obj/structure/blob/core/proc/lateblobtimer()
|
||||
addtimer(CALLBACK(src, PROC_REF(lateblobcheck)), 50)
|
||||
|
||||
/obj/structure/blob/core/proc/lateblobcheck()
|
||||
if(overmind)
|
||||
overmind.add_points(60)
|
||||
if(overmind.mind)
|
||||
overmind.mind.make_Overmind()
|
||||
else
|
||||
log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks a overmind.mind.")
|
||||
else
|
||||
log_debug("/obj/structure/blob/core/proc/lateblobcheck: Blob core lacks an overmind.")
|
||||
|
||||
/obj/structure/blob/core/onTransitZ(old_z, new_z)
|
||||
if(overmind && is_station_level(new_z))
|
||||
overmind.forceMove(get_turf(src))
|
||||
return ..()
|
||||
@@ -0,0 +1,28 @@
|
||||
/obj/structure/blob/captured_nuke //alternative to blob just straight up destroying nukes
|
||||
name = "blob captured nuke"
|
||||
icon_state = "blob"
|
||||
desc = "A Nuclear Warhead tangled in blob tendrils pulsating with a horrific green glow."
|
||||
max_integrity = 100
|
||||
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 65, ACID = 90)
|
||||
point_return = 0
|
||||
|
||||
/obj/structure/blob/captured_nuke/Initialize(mapload, obj/machinery/nuclearbomb/N)
|
||||
. = ..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
N?.forceMove(src)
|
||||
update_icon(UPDATE_OVERLAYS)
|
||||
|
||||
/obj/structure/blob/captured_nuke/update_overlays()
|
||||
. = ..()
|
||||
var/image/nuke_overlay = image('icons/mob/blob.dmi', "blob_nuke_overlay")
|
||||
nuke_overlay.appearance_flags = RESET_COLOR
|
||||
. += nuke_overlay
|
||||
|
||||
/obj/structure/blob/captured_nuke/Destroy()
|
||||
for(var/obj/machinery/nuclearbomb/O in contents)
|
||||
O.forceMove(loc)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/structure/blob/captured_nuke/Life(seconds, times_fired)
|
||||
obj_integrity = min(max_integrity, obj_integrity + 1)
|
||||
@@ -0,0 +1,27 @@
|
||||
/obj/structure/blob/factory
|
||||
name = "factory blob"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blob_factory"
|
||||
max_integrity = 200
|
||||
point_return = 18
|
||||
var/list/spores = list()
|
||||
var/max_spores = 3
|
||||
var/spore_delay = 0
|
||||
|
||||
/obj/structure/blob/factory/Destroy()
|
||||
for(var/mob/living/simple_animal/hostile/blob/blobspore/spore in spores)
|
||||
if(spore.factory == src)
|
||||
spore.factory = null
|
||||
spores = null
|
||||
return ..()
|
||||
|
||||
/obj/structure/blob/factory/run_action()
|
||||
if(spores.len >= max_spores)
|
||||
return
|
||||
if(spore_delay > world.time)
|
||||
return
|
||||
flick("blob_factory_glow", src)
|
||||
spore_delay = world.time + 100 // 10 seconds
|
||||
var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore(src.loc, src)
|
||||
if(overmind)
|
||||
overmind.add_mob_to_overmind(BS)
|
||||
@@ -0,0 +1,36 @@
|
||||
/obj/structure/blob/node
|
||||
name = "blob node"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blank_blob"
|
||||
max_integrity = 200
|
||||
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 65, ACID = 90)
|
||||
point_return = 18
|
||||
|
||||
/obj/structure/blob/node/Initialize(mapload)
|
||||
. = ..()
|
||||
GLOB.blob_nodes += src
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/structure/blob/node/adjustcolors(a_color)
|
||||
overlays.Cut()
|
||||
color = null
|
||||
var/image/I = new('icons/mob/blob.dmi', "blob")
|
||||
I.color = a_color
|
||||
src.overlays += I
|
||||
var/image/C = new('icons/mob/blob.dmi', "blob_node_overlay")
|
||||
src.overlays += C
|
||||
|
||||
/obj/structure/blob/node/Destroy()
|
||||
GLOB.blob_nodes -= src
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/structure/blob/node/Life(seconds, times_fired)
|
||||
if(overmind)
|
||||
for(var/i = 1; i < 8; i += i)
|
||||
Pulse(5, i, overmind.blob_reagent_datum.color)
|
||||
else
|
||||
for(var/i = 1; i < 8; i += i)
|
||||
Pulse(5, i, color)
|
||||
obj_integrity = min(max_integrity, obj_integrity + 1)
|
||||
color = null
|
||||
@@ -0,0 +1,15 @@
|
||||
/obj/structure/blob/resource
|
||||
name = "resource blob"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blob_resource"
|
||||
max_integrity = 60
|
||||
point_return = 12
|
||||
var/resource_delay = 0
|
||||
|
||||
/obj/structure/blob/resource/run_action()
|
||||
if(resource_delay > world.time)
|
||||
return
|
||||
flick("blob_resource_glow", src)
|
||||
resource_delay = world.time + 40 // 4 seconds
|
||||
if(overmind)
|
||||
overmind.add_points(1)
|
||||
@@ -0,0 +1,76 @@
|
||||
/obj/structure/blob/shield
|
||||
name = "strong blob"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blob_shield"
|
||||
desc = "Some blob creature thingy"
|
||||
max_integrity = 150
|
||||
brute_resist = 0.25
|
||||
explosion_block = 3
|
||||
atmosblock = TRUE
|
||||
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 90, ACID = 90)
|
||||
|
||||
/obj/structure/blob/shield/core
|
||||
point_return = 0
|
||||
|
||||
/obj/structure/blob/shield/check_integrity()
|
||||
var/old_compromised_integrity = compromised_integrity
|
||||
if(obj_integrity < max_integrity * 0.5)
|
||||
compromised_integrity = TRUE
|
||||
else
|
||||
compromised_integrity = FALSE
|
||||
if(old_compromised_integrity != compromised_integrity)
|
||||
update_state()
|
||||
update_appearance(UPDATE_NAME|UPDATE_DESC|UPDATE_ICON_STATE)
|
||||
|
||||
/obj/structure/blob/shield/update_state()
|
||||
if(compromised_integrity)
|
||||
atmosblock = FALSE
|
||||
else
|
||||
atmosblock = TRUE
|
||||
air_update_turf(1)
|
||||
|
||||
/obj/structure/blob/shield/update_name()
|
||||
. = ..()
|
||||
if(compromised_integrity)
|
||||
name = "weakened [initial(name)]"
|
||||
else
|
||||
name = initial(name)
|
||||
|
||||
/obj/structure/blob/shield/update_desc()
|
||||
. = ..()
|
||||
if(compromised_integrity)
|
||||
desc = "A wall of twitching tendrils."
|
||||
else
|
||||
desc = initial(desc)
|
||||
|
||||
/obj/structure/blob/shield/update_icon_state()
|
||||
if(compromised_integrity)
|
||||
icon_state = "[initial(icon_state)]_damaged"
|
||||
else
|
||||
icon_state = initial(icon_state)
|
||||
|
||||
/obj/structure/blob/shield/CanPass(atom/movable/mover, turf/target, height=0)
|
||||
if(istype(mover) && mover.checkpass(PASSBLOB)) return 1
|
||||
return 0
|
||||
|
||||
/obj/structure/blob/shield/reflective
|
||||
name = "reflective blob"
|
||||
desc = "A solid wall of slightly twitching tendrils with a reflective glow."
|
||||
icon_state = "blob_glow"
|
||||
max_integrity = 100
|
||||
brute_resist = 0.5
|
||||
explosion_block = 2
|
||||
point_return = 9
|
||||
flags_2 = CHECK_RICOCHET_2
|
||||
|
||||
/obj/structure/blob/shield/reflective/handle_ricochet(obj/item/projectile/P)
|
||||
var/turf/p_turf = get_turf(P)
|
||||
var/face_direction = get_dir(src, p_turf)
|
||||
var/face_angle = dir2angle(face_direction)
|
||||
var/incidence_s = GET_ANGLE_OF_INCIDENCE(face_angle, (P.Angle + 180))
|
||||
if(abs(incidence_s) > 90 && abs(incidence_s) < 270)
|
||||
return FALSE
|
||||
var/new_angle_s = SIMPLIFY_DEGREES(face_angle + incidence_s)
|
||||
P.set_angle(new_angle_s)
|
||||
visible_message("<span class='warning'>[P] reflects off [src]!</span>")
|
||||
return TRUE
|
||||
@@ -0,0 +1,16 @@
|
||||
/obj/structure/blob/storage
|
||||
name = "storage blob"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blob_resource"
|
||||
max_integrity = 30
|
||||
fire_resist = 2
|
||||
point_return = 12
|
||||
|
||||
/obj/structure/blob/storage/obj_destruction(damage_flag)
|
||||
if(overmind)
|
||||
overmind.max_blob_points -= 50
|
||||
..()
|
||||
|
||||
/obj/structure/blob/storage/proc/update_max_blob_points(new_point_increase)
|
||||
if(overmind)
|
||||
overmind.max_blob_points += new_point_increase
|
||||
@@ -0,0 +1,123 @@
|
||||
/mob/camera/blob
|
||||
name = "Blob Overmind"
|
||||
real_name = "Blob Overmind"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "marker"
|
||||
|
||||
see_in_dark = 8
|
||||
invisibility = INVISIBILITY_OBSERVER
|
||||
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
|
||||
|
||||
pass_flags = PASSBLOB
|
||||
faction = list(ROLE_BLOB)
|
||||
|
||||
var/obj/structure/blob/core/blob_core = null // The blob overmind's core
|
||||
var/blob_points = 0
|
||||
var/max_blob_points = 100
|
||||
var/last_attack = 0
|
||||
var/nodes_required = TRUE //if the blob needs nodes to place resource and factory blobs
|
||||
var/split_used = FALSE
|
||||
var/is_offspring = FALSE
|
||||
var/datum/reagent/blob/blob_reagent_datum = new/datum/reagent/blob()
|
||||
var/list/blob_mobs = list()
|
||||
|
||||
/mob/camera/blob/New()
|
||||
var/new_name = "[initial(name)] ([rand(1, 999)])"
|
||||
name = new_name
|
||||
real_name = new_name
|
||||
last_attack = world.time
|
||||
var/list/possible_reagents = list()
|
||||
for(var/type in subtypesof(/datum/reagent/blob))
|
||||
possible_reagents.Add(new type)
|
||||
blob_reagent_datum = pick(possible_reagents)
|
||||
if(blob_core)
|
||||
blob_core.adjustcolors(blob_reagent_datum.color)
|
||||
|
||||
color = blob_reagent_datum.complementary_color
|
||||
..()
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/mob/camera/blob/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/mob/camera/blob/process()
|
||||
if(!blob_core)
|
||||
qdel(src)
|
||||
|
||||
/mob/camera/blob/Login()
|
||||
..()
|
||||
sync_mind()
|
||||
blob_help()
|
||||
update_health_hud()
|
||||
|
||||
/mob/camera/blob/update_health_hud()
|
||||
if(blob_core && hud_used)
|
||||
hud_used.blobhealthdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font face='Small Fonts' color='#e36600'>[round(blob_core.obj_integrity)]</font></div>"
|
||||
|
||||
/mob/camera/blob/proc/add_points(points)
|
||||
if(points != 0)
|
||||
blob_points = clamp(blob_points + points, 0, max_blob_points)
|
||||
if(hud_used)
|
||||
hud_used.blobpwrdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font face='Small Fonts' color='#82ed00'>[round(src.blob_points)]</font></div>"
|
||||
|
||||
/mob/camera/blob/say(message)
|
||||
if(!message)
|
||||
return
|
||||
|
||||
if(src.client)
|
||||
if(check_mute(client.ckey, MUTE_IC))
|
||||
to_chat(src, "You cannot send IC messages (muted).")
|
||||
return
|
||||
if(src.client.handle_spam_prevention(message, MUTE_IC))
|
||||
return
|
||||
|
||||
if(stat)
|
||||
return
|
||||
|
||||
blob_talk(message)
|
||||
|
||||
/mob/camera/blob/proc/add_mob_to_overmind(mob/living/simple_animal/hostile/blob/B)
|
||||
B.color = blob_reagent_datum?.complementary_color
|
||||
B.overmind = src
|
||||
blob_mobs += B
|
||||
RegisterSignal(B, COMSIG_PARENT_QDELETING, PROC_REF(on_blob_mob_death))
|
||||
|
||||
/mob/camera/blob/proc/on_blob_mob_death(mob/living/simple_animal/hostile/blob/B)
|
||||
blob_mobs -= B
|
||||
|
||||
/mob/camera/blob/proc/blob_talk(message)
|
||||
log_say("(BLOB) [message]", src)
|
||||
|
||||
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
|
||||
|
||||
if(!message)
|
||||
return
|
||||
|
||||
var/rendered
|
||||
var/follow_text
|
||||
for(var/mob/M in GLOB.mob_list)
|
||||
follow_text = isobserver(M) ? " ([ghost_follow_link(src, ghost=M)])" : ""
|
||||
rendered = "<font color=\"#EE4000\"><i><span class='game say'>Blob Telepathy, <span class='name'>[name]([blob_reagent_datum.name])</span>[follow_text] <span class='message'>states, \"[message]\"</span></span></i></font>"
|
||||
if(isovermind(M) || isobserver(M) || istype(M, /mob/living/simple_animal/hostile/blob/blobbernaut))
|
||||
M.show_message(rendered, EMOTE_AUDIBLE)
|
||||
|
||||
/mob/camera/blob/blob_act(obj/structure/blob/B)
|
||||
return
|
||||
|
||||
/mob/camera/blob/Stat()
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
if(blob_core)
|
||||
stat(null, "Core Health: [blob_core.obj_integrity]")
|
||||
stat(null, "Power Stored: [blob_points]/[max_blob_points]")
|
||||
|
||||
/mob/camera/blob/Move(NewLoc, Dir = 0)
|
||||
var/obj/structure/blob/B = locate() in range("3x3", NewLoc)
|
||||
if(B)
|
||||
loc = NewLoc
|
||||
else
|
||||
return 0
|
||||
|
||||
/mob/camera/blob/proc/can_attack()
|
||||
return (world.time > (last_attack + CLICK_CD_RANGE))
|
||||
@@ -0,0 +1,312 @@
|
||||
//I will need to recode parts of this but I am way too tired atm
|
||||
|
||||
GLOBAL_LIST_EMPTY(blobs)
|
||||
GLOBAL_LIST_EMPTY(blob_cores)
|
||||
GLOBAL_LIST_EMPTY(blob_nodes)
|
||||
|
||||
/obj/structure/blob
|
||||
name = "blob"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
light_range = 3
|
||||
desc = "Some blob creature thingy"
|
||||
density = FALSE
|
||||
opacity = FALSE
|
||||
anchored = TRUE
|
||||
max_integrity = 30
|
||||
armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 80, ACID = 70)
|
||||
flags_2 = CRITICAL_ATOM_2
|
||||
var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed.
|
||||
var/health_timestamp = 0
|
||||
var/brute_resist = 0.5 //multiplies brute damage by this
|
||||
var/fire_resist = 1 //multiplies burn damage by this
|
||||
var/atmosblock = FALSE //if the blob blocks atmos and heat spread
|
||||
/// If a threshold is reached, resulting in shifting variables
|
||||
var/compromised_integrity = FALSE
|
||||
var/mob/camera/blob/overmind
|
||||
|
||||
/obj/structure/blob/Initialize(mapload)
|
||||
. = ..()
|
||||
GLOB.blobs += src
|
||||
setDir(pick(GLOB.cardinal))
|
||||
check_integrity()
|
||||
if(atmosblock)
|
||||
air_update_turf(TRUE)
|
||||
ConsumeTile()
|
||||
|
||||
/obj/structure/blob/Destroy()
|
||||
if(atmosblock)
|
||||
atmosblock = FALSE
|
||||
air_update_turf(1)
|
||||
GLOB.blobs -= src
|
||||
overmind = null // let us not have gc issues
|
||||
if(isturf(loc)) //Necessary because Expand() is screwed up and spawns a blob and then deletes it
|
||||
playsound(src.loc, 'sound/effects/splat.ogg', 50, 1)
|
||||
return ..()
|
||||
|
||||
/obj/structure/blob/BlockSuperconductivity()
|
||||
return atmosblock
|
||||
|
||||
/obj/structure/blob/CanPass(atom/movable/mover, turf/target, height=0)
|
||||
if(height==0)
|
||||
return 1
|
||||
if(istype(mover) && mover.checkpass(PASSBLOB))
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/obj/structure/blob/CanAtmosPass(turf/T)
|
||||
return !atmosblock
|
||||
|
||||
/obj/structure/blob/CanPathfindPass(obj/item/card/id/ID, dir, caller, no_id = FALSE)
|
||||
. = 0
|
||||
if(ismovable(caller))
|
||||
var/atom/movable/mover = caller
|
||||
. = . || mover.checkpass(PASSBLOB)
|
||||
|
||||
/obj/structure/blob/process()
|
||||
Life()
|
||||
return
|
||||
|
||||
/obj/structure/blob/blob_act(obj/structure/blob/B)
|
||||
return
|
||||
|
||||
/obj/structure/blob/proc/Life()
|
||||
return
|
||||
|
||||
/obj/structure/blob/proc/check_integrity()
|
||||
return
|
||||
|
||||
/obj/structure/blob/proc/update_state()
|
||||
return
|
||||
|
||||
/obj/structure/blob/proc/RegenHealth()
|
||||
// All blobs heal over time when pulsed, but it has a cool down
|
||||
if(health_timestamp > world.time)
|
||||
return 0
|
||||
var/turf/T = get_turf(src)
|
||||
chemical_attack(T)
|
||||
if(obj_integrity < max_integrity)
|
||||
obj_integrity = min(max_integrity, obj_integrity + 1)
|
||||
check_integrity()
|
||||
health_timestamp = world.time + 10 // 1 seconds
|
||||
|
||||
/obj/structure/blob/proc/chemical_attack(turf/T)
|
||||
for(var/mob/living/L in T)
|
||||
if(ROLE_BLOB in L.faction) //no friendly/dead fire
|
||||
continue
|
||||
if(!overmind)
|
||||
continue
|
||||
var/mob_protection = L.get_permeability_protection()
|
||||
overmind.blob_reagent_datum.reaction_mob(L, REAGENT_TOUCH, 25, 1, mob_protection)
|
||||
overmind.blob_reagent_datum.send_message(L)
|
||||
L.blob_act(src)
|
||||
|
||||
/obj/structure/blob/proc/Pulse(pulse = 0, origin_dir = 0, a_color)//Todo: Fix spaceblob expand
|
||||
RegenHealth()
|
||||
|
||||
if(run_action())//If we can do something here then we dont need to pulse more
|
||||
return
|
||||
|
||||
if(pulse > 30)
|
||||
return//Inf loop check
|
||||
|
||||
//Looking for another blob to pulse
|
||||
var/list/dirs = list(1,2,4,8)
|
||||
dirs.Remove(origin_dir)//Dont pulse the guy who pulsed us
|
||||
for(var/i = 1 to 4)
|
||||
if(!dirs.len) break
|
||||
var/dirn = pick(dirs)
|
||||
dirs.Remove(dirn)
|
||||
var/turf/T = get_step(src, dirn)
|
||||
var/obj/structure/blob/B = (locate(/obj/structure/blob) in T)
|
||||
if(!B)
|
||||
expand(T, 1, a_color, overmind)//No blob here so try and expand
|
||||
return
|
||||
B.adjustcolors(a_color)
|
||||
|
||||
B.Pulse((pulse+1),get_dir(src.loc,T), a_color)
|
||||
return
|
||||
return
|
||||
|
||||
|
||||
/obj/structure/blob/proc/run_action()
|
||||
return 0
|
||||
|
||||
/obj/structure/blob/proc/ConsumeTile()
|
||||
for(var/atom/A in loc)
|
||||
A.blob_act(src)
|
||||
if(iswallturf(loc))
|
||||
loc.blob_act(src) //don't ask how a wall got on top of the core, just eat it
|
||||
|
||||
/obj/structure/blob/proc/expand(turf/T = null, prob = 1, a_color, _overmind = null, turf/double_target = null)
|
||||
if(prob && !prob(obj_integrity))
|
||||
return
|
||||
if(isspaceturf(T) && prob(75)) return
|
||||
if(!T)
|
||||
var/list/dirs = list(1,2,4,8)
|
||||
for(var/i = 1 to 4)
|
||||
var/dirn = pick(dirs)
|
||||
dirs.Remove(dirn)
|
||||
T = get_step(src, dirn)
|
||||
if(!(locate(/obj/structure/blob) in T)) break
|
||||
else T = null
|
||||
|
||||
if(!T) return 0
|
||||
var/obj/structure/blob/normal/B = new /obj/structure/blob/normal(src.loc, min(obj_integrity, 30))
|
||||
B.color = a_color
|
||||
B.density = TRUE
|
||||
B.overmind = _overmind
|
||||
if(T.Enter(B,src))//Attempt to move into the tile
|
||||
B.density = initial(B.density)
|
||||
B.loc = T
|
||||
if(double_target)
|
||||
if(!overmind.can_buy(5))
|
||||
overmind.last_attack = world.time
|
||||
return
|
||||
B.expand(double_target, 0, overmind.blob_reagent_datum.color, overmind)
|
||||
overmind.blob_core.chemical_attack(T)
|
||||
overmind.last_attack = world.time
|
||||
else
|
||||
T.blob_act()//If we cant move in hit the turf
|
||||
B.loc = null //So we don't play the splat sound, see Destroy()
|
||||
qdel(B)
|
||||
|
||||
for(var/atom/A in T)//Hit everything in the turf
|
||||
if(isliving(A) && !A.density && overmind) // Crawling mob / small mob? Extra damage.
|
||||
var/mob/living/M = A
|
||||
var/mob_protection = M.get_permeability_protection()
|
||||
overmind.blob_reagent_datum.reaction_mob(M, REAGENT_TOUCH, 25, 1, mob_protection)
|
||||
overmind.blob_reagent_datum.send_message(M)
|
||||
A.blob_act(src)
|
||||
return 1
|
||||
|
||||
/obj/structure/blob/proc/double_expand(turf/T = null, prob = 1, a_color, _overmind = null)
|
||||
for(var/turf/adjacent in circlerange(T, 1))
|
||||
if(adjacent in circlerange(src, 1))
|
||||
expand(adjacent, 0, overmind.blob_reagent_datum.color, overmind, T)
|
||||
overmind.blob_core.chemical_attack(adjacent)
|
||||
color = overmind.blob_reagent_datum.color
|
||||
return
|
||||
|
||||
/obj/structure/blob/Crossed(mob/living/L, oldloc)
|
||||
..()
|
||||
L.blob_act(src)
|
||||
|
||||
/obj/structure/blob/zap_act(power, zap_flags)
|
||||
take_damage(power * 0.0025, BURN, ENERGY)
|
||||
power -= power * 0.0025 //You don't get to do it for free
|
||||
return ..() //You don't get to do it for free
|
||||
|
||||
/obj/structure/blob/hulk_damage()
|
||||
return 15
|
||||
|
||||
/obj/structure/blob/attack_animal(mob/living/simple_animal/M)
|
||||
if(ROLE_BLOB in M.faction) //sorry, but you can't kill the blob as a blobbernaut
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/structure/blob/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
|
||||
switch(damage_type)
|
||||
if(BRUTE)
|
||||
if(damage_amount)
|
||||
playsound(src.loc, 'sound/effects/attackblob.ogg', 50, TRUE)
|
||||
else
|
||||
playsound(src, 'sound/weapons/tap.ogg', 50, TRUE)
|
||||
if(BURN)
|
||||
playsound(src.loc, 'sound/items/welder.ogg', 100, TRUE)
|
||||
|
||||
/obj/structure/blob/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir)
|
||||
switch(damage_type)
|
||||
if(BRUTE)
|
||||
damage_amount *= brute_resist
|
||||
if(BURN)
|
||||
damage_amount *= fire_resist
|
||||
if(CLONE)
|
||||
else
|
||||
return 0
|
||||
var/armor_protection = 0
|
||||
if(damage_flag)
|
||||
armor_protection = armor.getRating(damage_flag)
|
||||
damage_amount = round(damage_amount * (100 - armor_protection)*0.01, 0.1)
|
||||
if(overmind && damage_flag)
|
||||
damage_amount = overmind.blob_reagent_datum.damage_reaction(src, damage_amount, damage_type, damage_flag)
|
||||
return damage_amount
|
||||
|
||||
/obj/structure/blob/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
|
||||
. = ..()
|
||||
if(. && obj_integrity > 0)
|
||||
check_integrity()
|
||||
|
||||
/obj/structure/blob/proc/change_to(type)
|
||||
if(!ispath(type))
|
||||
error("[type] is an invalid type for the blob.")
|
||||
var/obj/structure/blob/B = new type(src.loc)
|
||||
if(!istype(type, /obj/structure/blob/core) || !istype(type, /obj/structure/blob/node))
|
||||
B.color = color
|
||||
else
|
||||
B.adjustcolors(color)
|
||||
qdel(src)
|
||||
|
||||
/obj/structure/blob/proc/adjustcolors(a_color)
|
||||
if(a_color)
|
||||
color = a_color
|
||||
|
||||
/obj/structure/blob/examine(mob/user)
|
||||
. = ..()
|
||||
. += "It looks like it's made of [get_chem_name()]."
|
||||
. += "It looks like this chemical does: [get_chem_desc()]"
|
||||
|
||||
|
||||
/obj/structure/blob/proc/get_chem_name()
|
||||
for(var/mob/camera/blob/B in GLOB.mob_list)
|
||||
if(lowertext(B.blob_reagent_datum.color) == lowertext(src.color)) // Goddamit why we use strings for these
|
||||
return B.blob_reagent_datum.name
|
||||
return "unknown"
|
||||
|
||||
/obj/structure/blob/proc/get_chem_desc()
|
||||
for(var/mob/camera/blob/B in GLOB.mob_list)
|
||||
if(lowertext(B.blob_reagent_datum.color) == lowertext(src.color)) // Goddamit why we use strings for these
|
||||
return B.blob_reagent_datum.description
|
||||
return "something unknown"
|
||||
|
||||
/obj/structure/blob/normal
|
||||
icon_state = "blob"
|
||||
light_range = 0
|
||||
obj_integrity = 21 //doesn't start at full health
|
||||
max_integrity = 25
|
||||
brute_resist = 0.25
|
||||
|
||||
/obj/structure/blob/normal/check_integrity()
|
||||
var/old_compromised_integrity = compromised_integrity
|
||||
if(obj_integrity <= 15)
|
||||
compromised_integrity = TRUE
|
||||
else
|
||||
compromised_integrity = FALSE
|
||||
if(old_compromised_integrity != compromised_integrity)
|
||||
update_state()
|
||||
update_appearance(UPDATE_NAME|UPDATE_DESC|UPDATE_ICON_STATE)
|
||||
|
||||
/obj/structure/blob/normal/update_state()
|
||||
if(compromised_integrity)
|
||||
brute_resist = 0.5
|
||||
else
|
||||
brute_resist = 0.25
|
||||
|
||||
/obj/structure/blob/normal/update_name()
|
||||
. = ..()
|
||||
if(compromised_integrity)
|
||||
name = "fragile blob"
|
||||
else
|
||||
name = "[overmind ? "blob" : "dead blob"]"
|
||||
|
||||
/obj/structure/blob/normal/update_desc()
|
||||
. = ..()
|
||||
if(compromised_integrity)
|
||||
desc = "A thin lattice of slightly twitching tendrils."
|
||||
else
|
||||
desc = "A thick wall of [overmind ? "writhing" : "lifeless"] tendrils."
|
||||
|
||||
/obj/structure/blob/normal/update_icon_state()
|
||||
if(compromised_integrity)
|
||||
icon_state = "blob_damaged"
|
||||
else
|
||||
icon_state = "blob"
|
||||
@@ -26,10 +26,14 @@
|
||||
var/mob/M = pick(candidates)
|
||||
B.key = M.key
|
||||
B.mind.special_role = SPECIAL_ROLE_BLOB
|
||||
SSticker.mode.update_blob_icons_added(B.mind)
|
||||
B.forceMove(vent)
|
||||
B.add_ventcrawl(vent)
|
||||
|
||||
// Mark it on antag HUD
|
||||
var/datum/atom_hud/antag/antaghud = GLOB.huds[ANTAG_HUD_BLOB]
|
||||
antaghud.join_hud(B.mind.current)
|
||||
set_antag_hud(B.mind.current, "hudblob")
|
||||
|
||||
to_chat(B, "<span class='userdanger'>You are now a mouse, infected with blob spores. Find somewhere isolated... before you burst and become the blob! Use ventcrawl (alt-click on vents) to move around.</span>")
|
||||
to_chat(B, "<span class='motd'>For more information, check the wiki page: ([GLOB.configuration.url.wiki_url]/index.php/Blob)</span>")
|
||||
notify_ghosts("Infected Mouse has appeared in [get_area(B)].", source = B, action = NOTIFY_FOLLOW)
|
||||
|
||||
Reference in New Issue
Block a user