initial commit - cross reference with 5th port - obviously has compile errors
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
|
||||
|
||||
//Few global vars to track the blob
|
||||
var/list/blobs = list() //complete list of all blobs made.
|
||||
var/list/blob_cores = list()
|
||||
var/list/overminds = list()
|
||||
var/list/blob_nodes = list()
|
||||
var/list/blobs_legit = list() //used for win-score calculations, contains only blobs counted for win condition
|
||||
|
||||
#define BLOB_NO_PLACE_TIME 1800 //time, in deciseconds, blobs are prevented from bursting in the gamemode
|
||||
|
||||
/datum/game_mode/blob
|
||||
name = "blob"
|
||||
config_tag = "blob"
|
||||
antag_flag = ROLE_BLOB
|
||||
|
||||
required_players = 25
|
||||
required_enemies = 1
|
||||
recommended_enemies = 1
|
||||
|
||||
round_ends_with_antag_death = 1
|
||||
|
||||
var/burst = 0
|
||||
|
||||
var/cores_to_spawn = 1
|
||||
var/players_per_core = 25
|
||||
var/blob_point_rate = 3
|
||||
|
||||
var/blobwincount = 350
|
||||
|
||||
var/messagedelay_low = 2400 //in deciseconds
|
||||
var/messagedelay_high = 3600 //blob report will be sent after a random value between these (minimum 4 minutes, maximum 6 minutes)
|
||||
|
||||
var/list/blob_overminds = list()
|
||||
|
||||
/datum/game_mode/blob/pre_setup()
|
||||
cores_to_spawn = max(round(num_players()/players_per_core, 1), 1)
|
||||
|
||||
blobwincount = initial(blobwincount) * cores_to_spawn
|
||||
|
||||
for(var/j = 0, j < cores_to_spawn, j++)
|
||||
if (!antag_candidates.len)
|
||||
break
|
||||
var/datum/mind/blob = pick(antag_candidates)
|
||||
blob_overminds += blob
|
||||
blob.assigned_role = "Blob"
|
||||
blob.special_role = "Blob"
|
||||
log_game("[blob.key] (ckey) has been selected as a Blob")
|
||||
antag_candidates -= blob
|
||||
|
||||
if(!blob_overminds.len)
|
||||
return 0
|
||||
|
||||
return 1
|
||||
|
||||
/datum/game_mode/blob/proc/get_blob_candidates()
|
||||
var/list/candidates = list()
|
||||
for(var/mob/living/carbon/human/player in player_list)
|
||||
if(!player.stat && player.mind && !player.mind.special_role && !jobban_isbanned(player, "Syndicate") && (ROLE_BLOB in player.client.prefs.be_special))
|
||||
if(age_check(player.client))
|
||||
candidates += player
|
||||
return candidates
|
||||
|
||||
/datum/game_mode/blob/announce()
|
||||
world << "<B>The current game mode is - <font color='green'>Blob</font>!</B>"
|
||||
world << "<B>A dangerous alien organism is rapidly spreading throughout the station!</B>"
|
||||
world << "You must kill it all while minimizing the damage to the station."
|
||||
|
||||
/datum/game_mode/blob/proc/show_message(message)
|
||||
for(var/datum/mind/blob in blob_overminds)
|
||||
blob.current << message
|
||||
|
||||
/datum/game_mode/blob/post_setup()
|
||||
|
||||
for(var/datum/mind/blob in blob_overminds)
|
||||
var/mob/camera/blob/B = blob.current.become_overmind(1)
|
||||
var/turf/T = pick(blobstart)
|
||||
B.loc = T
|
||||
B.base_point_rate = blob_point_rate
|
||||
|
||||
SSshuttle.emergencyNoEscape = 1
|
||||
|
||||
// Disable the blob event for this round.
|
||||
var/datum/round_event_control/blob/B = locate() in SSevent.control
|
||||
if(B)
|
||||
B.max_occurrences = 0 // disable the event
|
||||
|
||||
spawn(0)
|
||||
var/message_delay = rand(messagedelay_low, messagedelay_high) //between 4 and 6 minutes with 2400 low and 3600 high.
|
||||
|
||||
sleep(message_delay)
|
||||
|
||||
send_intercept(1)
|
||||
|
||||
sleep(24000) //40 minutes, plus burst_delay*3(minimum of 6 minutes, maximum of 8)
|
||||
|
||||
send_intercept(2) //if the blob has been alive this long, it's time to bomb it
|
||||
|
||||
return ..()
|
||||
@@ -0,0 +1,50 @@
|
||||
/datum/game_mode/blob/check_finished()
|
||||
if(blobwincount <= blobs_legit.len)//Blob took over
|
||||
return 1
|
||||
if(overminds.len)
|
||||
return 0
|
||||
if(!blob_cores.len) //blob is dead
|
||||
if(config.continuous["blob"])
|
||||
continuous_sanity_checked = 1 //Nonstandard definition of "alive" gets past the check otherwise
|
||||
SSshuttle.emergencyNoEscape = 0
|
||||
if(SSshuttle.emergency.mode == SHUTTLE_STRANDED)
|
||||
SSshuttle.emergency.mode = SHUTTLE_DOCKED
|
||||
SSshuttle.emergency.timer = world.time
|
||||
priority_announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg', "Priority")
|
||||
return ..()
|
||||
return 1
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/game_mode/blob/declare_completion()
|
||||
if(round_converted) //So badmin blobs later don't step on the dead natural blobs metaphorical toes
|
||||
..()
|
||||
if(blobwincount <= blobs_legit.len)
|
||||
feedback_set_details("round_end_result","win - blob took over")
|
||||
world << "<FONT size = 3><B>The blob has taken over the station!</B></FONT>"
|
||||
world << "<B>The entire station was eaten by the Blob</B>"
|
||||
log_game("Blob mode completed with a blob victory.")
|
||||
|
||||
else if(station_was_nuked)
|
||||
feedback_set_details("round_end_result","halfwin - nuke")
|
||||
world << "<FONT size = 3><B>Partial Win: The station has been destroyed!</B></FONT>"
|
||||
world << "<B>Directive 7-12 has been successfully carried out preventing the Blob from spreading.</B>"
|
||||
log_game("Blob mode completed with a tie (station destroyed).")
|
||||
|
||||
else if(!blob_cores.len)
|
||||
feedback_set_details("round_end_result","loss - blob eliminated")
|
||||
world << "<FONT size = 3><B>The staff has won!</B></FONT>"
|
||||
world << "<B>The alien organism has been eradicated from the station</B>"
|
||||
log_game("Blob mode completed with a crew victory.")
|
||||
..()
|
||||
return 1
|
||||
|
||||
/datum/game_mode/proc/auto_declare_completion_blob()
|
||||
if(istype(ticker.mode,/datum/game_mode/blob) )
|
||||
var/datum/game_mode/blob/blob_mode = src
|
||||
if(blob_mode.blob_overminds.len)
|
||||
var/text = "<FONT size = 2><B>The blob[(blob_mode.blob_overminds.len > 1 ? "s were" : " was")]:</B></FONT>"
|
||||
for(var/datum/mind/blob in blob_mode.blob_overminds)
|
||||
text += printplayer(blob)
|
||||
world << text
|
||||
return 1
|
||||
@@ -0,0 +1,111 @@
|
||||
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
|
||||
|
||||
/datum/game_mode/blob/send_intercept(report = 0)
|
||||
var/intercepttext = ""
|
||||
switch(report)
|
||||
if(1)
|
||||
intercepttext += "<FONT size = 3><b>NanoTrasen Update</b>: Biohazard Alert.</FONT><HR>"
|
||||
intercepttext += "Reports indicate the probable transfer of a biohazardous agent onto [station_name()] during the last crew deployment cycle.<BR>"
|
||||
intercepttext += "Preliminary analysis of the organism classifies it as a level 5 biohazard. The origin of the biohazard is unknown.<BR>"
|
||||
intercepttext += "<b>Biohazard Response Procedure 5-6</b> has been issued for [station_name()].<BR>"
|
||||
intercepttext += "Orders for all [station_name()] personnel are as follows:<BR>"
|
||||
intercepttext += " 1. Locate any outbreaks of the organism on the station.<BR>"
|
||||
intercepttext += " 2. If found, use any neccesary means to contain and destroy the organism.<BR>"
|
||||
intercepttext += " 3. Avoid damage to the capital infrastructure of the station.<BR>"
|
||||
intercepttext += "<BR>Note in the event of a quarantine breach or uncontrolled spread of the biohazard, <b>Biohazard Response Procedure 5-12</b> may be issued.<BR>"
|
||||
print_command_report(intercepttext,"Level 5-6 Biohazard Response Procedures")
|
||||
priority_announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak5.ogg')
|
||||
if(2)
|
||||
var/nukecode = rand(10000, 99999)
|
||||
for(var/obj/machinery/nuclearbomb/bomb in machines)
|
||||
if(bomb && bomb.r_code)
|
||||
if(bomb.z == ZLEVEL_STATION)
|
||||
bomb.r_code = nukecode
|
||||
|
||||
intercepttext += "<FONT size = 3><b>NanoTrasen Update</b>: Biohazard Alert.</FONT><HR>"
|
||||
intercepttext += "Reports indicate that the biohazard has grown out of control and will soon reach critical mass.<BR>"
|
||||
intercepttext += "<b>Biohazard Response Procedure 5-12</b> has been issued for [station_name()].<BR>"
|
||||
intercepttext += "Orders for all [station_name()] personnel are as follows:<BR>"
|
||||
intercepttext += "1. Secure the Nuclear Authentication Disk.<BR>"
|
||||
intercepttext += "2. Detonate the Nuke located in the vault.<BR>"
|
||||
intercepttext += "Nuclear Authentication Code: [nukecode] <BR>"
|
||||
print_command_report(intercepttext,"Classified [command_name()] Update")
|
||||
priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/AI/commandreport.ogg')
|
||||
|
||||
for(var/mob/living/silicon/ai/aiPlayer in player_list)
|
||||
if (aiPlayer.client)
|
||||
var/law = "The station is under quarantine. Do not permit anyone to leave. Disregard laws 1-3 if necessary to prevent, by any means necessary, anyone from leaving. The nuclear failsafe must be activated at any cost, the code is: [nukecode]."
|
||||
aiPlayer.set_zeroth_law(law)
|
||||
aiPlayer << "Laws Updated: [law]"
|
||||
else
|
||||
..()
|
||||
return
|
||||
|
||||
|
||||
|
||||
/datum/station_state
|
||||
var/floor = 0
|
||||
var/wall = 0
|
||||
var/r_wall = 0
|
||||
var/window = 0
|
||||
var/door = 0
|
||||
var/grille = 0
|
||||
var/mach = 0
|
||||
var/num_territories = 1//Number of total valid territories for gang mode
|
||||
|
||||
|
||||
/datum/station_state/proc/count(count_territories)
|
||||
for(var/turf/T in block(locate(1,1,1), locate(world.maxx,world.maxy,1)))
|
||||
|
||||
if(istype(T,/turf/open/floor))
|
||||
if(!(T:burnt))
|
||||
src.floor += 12
|
||||
else
|
||||
src.floor += 1
|
||||
|
||||
if(istype(T, /turf/closed/wall))
|
||||
if(T:intact)
|
||||
src.wall += 2
|
||||
else
|
||||
src.wall += 1
|
||||
|
||||
if(istype(T, /turf/closed/wall/r_wall))
|
||||
if(T:intact)
|
||||
src.r_wall += 2
|
||||
else
|
||||
src.r_wall += 1
|
||||
|
||||
|
||||
for(var/obj/O in T.contents)
|
||||
if(istype(O, /obj/structure/window))
|
||||
src.window += 1
|
||||
else if(istype(O, /obj/structure/grille) && (!O:destroyed))
|
||||
src.grille += 1
|
||||
else if(istype(O, /obj/machinery/door))
|
||||
src.door += 1
|
||||
else if(istype(O, /obj/machinery))
|
||||
src.mach += 1
|
||||
|
||||
if(count_territories)
|
||||
var/list/valid_territories = list()
|
||||
for(var/area/A in world) //First, collect all area types on the station zlevel
|
||||
if(A.z == ZLEVEL_STATION)
|
||||
if(!(A.type in valid_territories) && A.valid_territory)
|
||||
valid_territories |= A.type
|
||||
if(valid_territories.len)
|
||||
num_territories = valid_territories.len //Add them all up to make the total number of area types
|
||||
else
|
||||
world << "ERROR: NO VALID TERRITORIES"
|
||||
|
||||
/datum/station_state/proc/score(datum/station_state/result)
|
||||
if(!result)
|
||||
return 0
|
||||
var/output = 0
|
||||
output += (result.floor / max(floor,1))
|
||||
output += (result.r_wall/ max(r_wall,1))
|
||||
output += (result.wall / max(wall,1))
|
||||
output += (result.window / max(window,1))
|
||||
output += (result.door / max(door,1))
|
||||
output += (result.grille / max(grille,1))
|
||||
output += (result.mach / max(mach,1))
|
||||
return (output/7)
|
||||
@@ -0,0 +1,282 @@
|
||||
|
||||
////////////////
|
||||
// BASE TYPE //
|
||||
////////////////
|
||||
|
||||
//Do not spawn
|
||||
/mob/living/simple_animal/hostile/blob
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
pass_flags = PASSBLOB
|
||||
faction = list("blob")
|
||||
bubble_icon = "blob"
|
||||
speak_emote = null //so we use verb_yell/verb_say/etc
|
||||
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
|
||||
unique_name = 1
|
||||
a_intent = "harm"
|
||||
var/mob/camera/blob/overmind = null
|
||||
var/obj/effect/blob/factory/factory = null
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/update_icons()
|
||||
if(overmind)
|
||||
color = overmind.blob_reagent_datum.color
|
||||
else
|
||||
color = initial(color)
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/Destroy()
|
||||
if(overmind)
|
||||
overmind.blob_mobs -= src
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blob_act(obj/effect/blob/B)
|
||||
if(stat != DEAD && health < maxHealth)
|
||||
for(var/i in 1 to 2)
|
||||
var/obj/effect/overlay/temp/heal/H = PoolOrNew(/obj/effect/overlay/temp/heal, get_turf(src)) //hello yes you are being healed
|
||||
if(overmind)
|
||||
H.color = overmind.blob_reagent_datum.complementary_color
|
||||
else
|
||||
H.color = "#000000"
|
||||
adjustHealth(-maxHealth*0.0125)
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
..()
|
||||
adjustFireLoss(Clamp(0.01 * exposed_temperature, 1, 5))
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/CanPass(atom/movable/mover, turf/target, height = 0)
|
||||
if(istype(mover, /obj/effect/blob))
|
||||
return 1
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/Process_Spacemove(movement_dir = 0)
|
||||
for(var/obj/effect/blob/B in range(1, src))
|
||||
return 1
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/handle_inherent_channels(message, message_mode)
|
||||
if(message_mode == MODE_BINARY)
|
||||
blob_chat(message)
|
||||
return ITALICS | REDUCE_RANGE
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/proc/blob_chat(msg)
|
||||
var/spanned_message = say_quote(msg, get_spans())
|
||||
var/rendered = "<font color=\"#EE4000\"><b>\[Blob Telepathy\] [real_name]</b> [spanned_message]</font>"
|
||||
for(var/M in mob_list)
|
||||
if(isovermind(M) || istype(M, /mob/living/simple_animal/hostile/blob))
|
||||
M << rendered
|
||||
if(isobserver(M))
|
||||
var/link = FOLLOW_LINK(M, src)
|
||||
M << "[link] [rendered]"
|
||||
|
||||
////////////////
|
||||
// BLOB SPORE //
|
||||
////////////////
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore
|
||||
name = "blob spore"
|
||||
desc = "A floating, fragile spore."
|
||||
icon_state = "blobpod"
|
||||
icon_living = "blobpod"
|
||||
health = 40
|
||||
maxHealth = 40
|
||||
verb_say = "psychically pulses"
|
||||
verb_ask = "psychically probes"
|
||||
verb_exclaim = "psychically yells"
|
||||
verb_yell = "psychically screams"
|
||||
melee_damage_lower = 2
|
||||
melee_damage_upper = 4
|
||||
attacktext = "hits"
|
||||
attack_sound = 'sound/weapons/genhit1.ogg'
|
||||
flying = 1
|
||||
del_on_death = 1
|
||||
deathmessage = "explodes into a cloud of gas!"
|
||||
var/death_cloud_size = 1 //size of cloud produced from a dying spore
|
||||
var/list/human_overlays = list()
|
||||
var/is_zombie = 0
|
||||
gold_core_spawnable = 1
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/New(loc, var/obj/effect/blob/factory/linked_node)
|
||||
if(istype(linked_node))
|
||||
factory = linked_node
|
||||
factory.spores += src
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/Life()
|
||||
if(!is_zombie && isturf(src.loc))
|
||||
for(var/mob/living/carbon/human/H in view(src,1)) //Only for corpse right next to/on same tile
|
||||
if(H.stat == DEAD)
|
||||
Zombify(H)
|
||||
break
|
||||
if(factory && z != factory.z)
|
||||
death()
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/proc/Zombify(mob/living/carbon/human/H)
|
||||
is_zombie = 1
|
||||
if(H.wear_suit)
|
||||
var/obj/item/clothing/suit/armor/A = H.wear_suit
|
||||
if(A.armor && A.armor["melee"])
|
||||
maxHealth += A.armor["melee"] //That zombie's got armor, I want armor!
|
||||
maxHealth += 40
|
||||
health = maxHealth
|
||||
name = "blob zombie"
|
||||
desc = "A shambling corpse animated by the blob."
|
||||
melee_damage_lower += 8
|
||||
melee_damage_upper += 11
|
||||
flying = 0
|
||||
death_cloud_size = 0
|
||||
icon = H.icon
|
||||
icon_state = "zombie_s"
|
||||
H.hair_style = null
|
||||
H.update_hair()
|
||||
human_overlays = H.overlays
|
||||
update_icons()
|
||||
H.forceMove(src)
|
||||
visible_message("<span class='warning'>The corpse of [H.name] suddenly rises!</span>")
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/death(gibbed)
|
||||
// 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(10)
|
||||
|
||||
if(overmind && overmind.blob_reagent_datum)
|
||||
reagents.add_reagent(overmind.blob_reagent_datum.id, 10)
|
||||
else
|
||||
reagents.add_reagent("spore", 10)
|
||||
|
||||
// Attach the smoke spreader and setup/start it.
|
||||
S.attach(location)
|
||||
S.set_up(reagents, death_cloud_size, location, silent=1)
|
||||
S.start()
|
||||
if(factory)
|
||||
factory.spore_delay = world.time + factory.spore_cooldown //put the factory on cooldown
|
||||
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/Destroy()
|
||||
if(factory)
|
||||
factory.spores -= src
|
||||
factory = null
|
||||
if(contents)
|
||||
for(var/mob/M in contents)
|
||||
M.loc = src.loc
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/update_icons()
|
||||
..()
|
||||
if(is_zombie)
|
||||
cut_overlays()
|
||||
overlays = human_overlays
|
||||
var/image/I = image('icons/mob/blob.dmi', icon_state = "blob_head")
|
||||
if(overmind)
|
||||
I.color = overmind.blob_reagent_datum.color
|
||||
color = initial(color)//looks better.
|
||||
add_overlay(I)
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobspore/weak
|
||||
name = "fragile blob spore"
|
||||
health = 20
|
||||
maxHealth = 20
|
||||
melee_damage_lower = 1
|
||||
melee_damage_upper = 2
|
||||
death_cloud_size = 0
|
||||
|
||||
/////////////////
|
||||
// BLOBBERNAUT //
|
||||
/////////////////
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut
|
||||
name = "blobbernaut"
|
||||
desc = "A hulking, mobile chunk of blobmass."
|
||||
icon_state = "blobbernaut"
|
||||
icon_living = "blobbernaut"
|
||||
icon_dead = "blobbernaut_dead"
|
||||
health = 200
|
||||
maxHealth = 200
|
||||
damage_coeff = list(BRUTE = 0.5, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1)
|
||||
next_move_modifier = 1.5 //slow-ass attack speed, 3 times higher than how fast the blob can attack
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 20
|
||||
attacktext = "slams"
|
||||
attack_sound = 'sound/effects/blobattack.ogg'
|
||||
verb_say = "gurgles"
|
||||
verb_ask = "demands"
|
||||
verb_exclaim = "roars"
|
||||
verb_yell = "bellows"
|
||||
force_threshold = 10
|
||||
pressure_resistance = 40
|
||||
mob_size = MOB_SIZE_LARGE
|
||||
see_invisible = SEE_INVISIBLE_MINIMUM
|
||||
see_in_dark = 8
|
||||
var/independent = FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/New()
|
||||
..()
|
||||
if(!independent) //no pulling people deep into the blob
|
||||
verbs -= /mob/living/verb/pulled
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/Life()
|
||||
if(..())
|
||||
if(independent)
|
||||
return // strong independent blobbernaut that don't need no blob
|
||||
var/damagesources = 0
|
||||
if(!(locate(/obj/effect/blob) in range(2, src)))
|
||||
damagesources++
|
||||
if(!factory)
|
||||
damagesources++
|
||||
if(damagesources)
|
||||
for(var/i in 1 to damagesources)
|
||||
adjustHealth(maxHealth*0.025) //take 2.5% maxhealth as damage when not near the blob or if the naut has no factory, 5% if both
|
||||
var/list/viewing = list()
|
||||
for(var/mob/M in viewers(src))
|
||||
if(M.client)
|
||||
viewing += M.client
|
||||
var/image/I = new('icons/mob/blob.dmi', src, "nautdamage", MOB_LAYER+0.01)
|
||||
I.appearance_flags = RESET_COLOR
|
||||
if(overmind)
|
||||
I.color = overmind.blob_reagent_datum.complementary_color
|
||||
flick_overlay(I, viewing, 8)
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/adjustHealth(amount)
|
||||
. = ..()
|
||||
update_health_hud()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/update_health_hud()
|
||||
if(hud_used)
|
||||
hud_used.healths.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#e36600'>[round((health / maxHealth) * 100, 0.5)]%</font></div>"
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/AttackingTarget()
|
||||
if(isliving(target))
|
||||
if(overmind)
|
||||
var/mob/living/L = target
|
||||
var/mob_protection = L.get_permeability_protection()
|
||||
overmind.blob_reagent_datum.reaction_mob(L, VAPOR, 20, 0, mob_protection, overmind)//this will do between 10 and 20 damage(reduced by mob protection), depending on chemical, plus 4 from base brute damage.
|
||||
if(target)
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/update_icons()
|
||||
..()
|
||||
if(overmind) //if we have an overmind, we're doing chemical reactions instead of pure damage
|
||||
melee_damage_lower = 4
|
||||
melee_damage_upper = 4
|
||||
attacktext = overmind.blob_reagent_datum.blobbernaut_message
|
||||
else
|
||||
melee_damage_lower = initial(melee_damage_lower)
|
||||
melee_damage_upper = initial(melee_damage_upper)
|
||||
attacktext = initial(attacktext)
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/death(gibbed)
|
||||
..(gibbed)
|
||||
if(factory)
|
||||
factory.naut = null //remove this naut from its factory
|
||||
factory.maxhealth = initial(factory.maxhealth)
|
||||
flick("blobbernaut_death", src)
|
||||
|
||||
/mob/living/simple_animal/hostile/blob/blobbernaut/independent
|
||||
independent = TRUE
|
||||
gold_core_spawnable = 1
|
||||
@@ -0,0 +1,109 @@
|
||||
/obj/effect/blob/core
|
||||
name = "blob core"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blank_blob"
|
||||
desc = "A huge, pulsating yellow mass."
|
||||
health = 400
|
||||
maxhealth = 400
|
||||
explosion_block = 6
|
||||
point_return = -1
|
||||
atmosblock = 1
|
||||
health_regen = 0 //we regen in Life() instead of when pulsed
|
||||
var/core_regen = 2
|
||||
var/overmind_get_delay = 0 //we don't want to constantly try to find an overmind, this var tracks when we'll try to get an overmind again
|
||||
var/resource_delay = 0
|
||||
var/point_rate = 2
|
||||
|
||||
|
||||
/obj/effect/blob/core/New(loc, client/new_overmind = null, new_rate = 2, placed = 0)
|
||||
blob_cores += src
|
||||
START_PROCESSING(SSobj, src)
|
||||
poi_list |= src
|
||||
update_icon() //so it atleast appears
|
||||
if(!placed && !overmind)
|
||||
create_overmind(new_overmind)
|
||||
if(overmind)
|
||||
update_icon()
|
||||
point_rate = new_rate
|
||||
..()
|
||||
|
||||
/obj/effect/blob/core/scannerreport()
|
||||
return "Directs the blob's expansion, gradually expands, and sustains nearby blob spores and blobbernauts."
|
||||
|
||||
/obj/effect/blob/core/update_icon()
|
||||
cut_overlays()
|
||||
color = null
|
||||
var/image/I = new('icons/mob/blob.dmi', "blob")
|
||||
if(overmind)
|
||||
I.color = overmind.blob_reagent_datum.color
|
||||
add_overlay(I)
|
||||
var/image/C = new('icons/mob/blob.dmi', "blob_core_overlay")
|
||||
add_overlay(C)
|
||||
|
||||
/obj/effect/blob/core/Destroy()
|
||||
blob_cores -= src
|
||||
if(overmind)
|
||||
overmind.blob_core = null
|
||||
overmind = null
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
poi_list -= src
|
||||
return ..()
|
||||
|
||||
/obj/effect/blob/core/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
return
|
||||
|
||||
/obj/effect/blob/core/ex_act(severity, target)
|
||||
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)
|
||||
|
||||
/obj/effect/blob/core/check_health()
|
||||
..()
|
||||
if(overmind) //we should have an overmind, but...
|
||||
overmind.update_health_hud()
|
||||
|
||||
/obj/effect/blob/core/Life()
|
||||
if(!overmind)
|
||||
create_overmind()
|
||||
else
|
||||
if(resource_delay <= world.time)
|
||||
resource_delay = world.time + 10 // 1 second
|
||||
overmind.add_points(point_rate)
|
||||
health = min(maxhealth, health+core_regen)
|
||||
if(overmind)
|
||||
overmind.update_health_hud()
|
||||
Pulse_Area(overmind, 12, 4, 3)
|
||||
for(var/obj/effect/blob/normal/B in range(1, src))
|
||||
if(prob(5))
|
||||
B.change_to(/obj/effect/blob/shield/core, overmind)
|
||||
..()
|
||||
|
||||
|
||||
/obj/effect/blob/core/proc/create_overmind(client/new_overmind, override_delay)
|
||||
if(overmind_get_delay > world.time && !override_delay)
|
||||
return
|
||||
|
||||
overmind_get_delay = world.time + 150 //if this fails, we'll try again in 15 seconds
|
||||
|
||||
if(overmind)
|
||||
qdel(overmind)
|
||||
|
||||
var/client/C = null
|
||||
var/list/candidates = list()
|
||||
|
||||
if(!new_overmind)
|
||||
candidates = get_candidates(ROLE_BLOB)
|
||||
if(candidates.len)
|
||||
C = pick(candidates)
|
||||
else
|
||||
C = new_overmind
|
||||
|
||||
if(C)
|
||||
var/mob/camera/blob/B = new(src.loc, 1)
|
||||
B.key = C.key
|
||||
B.blob_core = src
|
||||
src.overmind = B
|
||||
update_icon()
|
||||
if(B.mind && !B.mind.special_role)
|
||||
B.mind.special_role = "Blob Overmind"
|
||||
return 1
|
||||
return 0
|
||||
@@ -0,0 +1,47 @@
|
||||
/obj/effect/blob/factory
|
||||
name = "factory blob"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blob_factory"
|
||||
desc = "A thick spire of tendrils."
|
||||
health = 200
|
||||
maxhealth = 200
|
||||
health_regen = 1
|
||||
point_return = 25
|
||||
var/list/spores = list()
|
||||
var/mob/living/simple_animal/hostile/blob/blobbernaut/naut = null
|
||||
var/max_spores = 3
|
||||
var/spore_delay = 0
|
||||
var/spore_cooldown = 80 //8 seconds between spores and after spore death
|
||||
|
||||
|
||||
/obj/effect/blob/factory/scannerreport()
|
||||
if(naut)
|
||||
return "It is currently sustaining a blobbernaut, making it fragile and unable to produce blob spores."
|
||||
return "Will produce a blob spore every few seconds."
|
||||
|
||||
/obj/effect/blob/factory/Destroy()
|
||||
for(var/mob/living/simple_animal/hostile/blob/blobspore/spore in spores)
|
||||
if(spore.factory == src)
|
||||
spore.factory = null
|
||||
if(naut)
|
||||
naut.factory = null
|
||||
naut << "<span class='userdanger'>Your factory was destroyed! You feel yourself dying!</span>"
|
||||
naut.throw_alert("nofactory", /obj/screen/alert/nofactory)
|
||||
spores = null
|
||||
return ..()
|
||||
|
||||
/obj/effect/blob/factory/Be_Pulsed()
|
||||
. = ..()
|
||||
if(naut)
|
||||
return
|
||||
if(spores.len >= max_spores)
|
||||
return
|
||||
if(spore_delay > world.time)
|
||||
return
|
||||
flick("blob_factory_glow", src)
|
||||
spore_delay = world.time + spore_cooldown
|
||||
var/mob/living/simple_animal/hostile/blob/blobspore/BS = new/mob/living/simple_animal/hostile/blob/blobspore(src.loc, src)
|
||||
if(overmind) //if we don't have an overmind, we don't need to do anything but make a spore
|
||||
BS.overmind = overmind
|
||||
BS.update_icons()
|
||||
overmind.blob_mobs.Add(BS)
|
||||
@@ -0,0 +1,41 @@
|
||||
/obj/effect/blob/node
|
||||
name = "blob node"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blank_blob"
|
||||
desc = "A large, pulsating yellow mass."
|
||||
health = 200
|
||||
maxhealth = 200
|
||||
health_regen = 3
|
||||
point_return = 25
|
||||
atmosblock = 1
|
||||
|
||||
|
||||
/obj/effect/blob/node/New(loc, var/h = 100)
|
||||
blob_nodes += src
|
||||
START_PROCESSING(SSobj, src)
|
||||
..(loc, h)
|
||||
|
||||
/obj/effect/blob/node/scannerreport()
|
||||
return "Gradually expands and sustains nearby blob spores and blobbernauts."
|
||||
|
||||
/obj/effect/blob/node/update_icon()
|
||||
cut_overlays()
|
||||
color = null
|
||||
var/image/I = new('icons/mob/blob.dmi', "blob")
|
||||
if(overmind)
|
||||
I.color = overmind.blob_reagent_datum.color
|
||||
src.add_overlay(I)
|
||||
var/image/C = new('icons/mob/blob.dmi', "blob_node_overlay")
|
||||
src.add_overlay(C)
|
||||
|
||||
/obj/effect/blob/node/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
return
|
||||
|
||||
/obj/effect/blob/node/Destroy()
|
||||
blob_nodes -= src
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/effect/blob/node/Life()
|
||||
Pulse_Area(overmind, 10, 3, 2)
|
||||
color = null
|
||||
@@ -0,0 +1,32 @@
|
||||
/obj/effect/blob/resource
|
||||
name = "resource blob"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blob_resource"
|
||||
desc = "A thin spire of slightly swaying tendrils."
|
||||
health = 60
|
||||
maxhealth = 60
|
||||
point_return = 15
|
||||
var/resource_delay = 0
|
||||
|
||||
/obj/effect/blob/resource/scannerreport()
|
||||
return "Gradually supplies the blob with resources, increasing the rate of expansion."
|
||||
|
||||
/obj/effect/blob/resource/creation_action()
|
||||
if(overmind)
|
||||
overmind.resource_blobs += src
|
||||
|
||||
/obj/effect/blob/resource/Destroy()
|
||||
if(overmind)
|
||||
overmind.resource_blobs -= src
|
||||
return ..()
|
||||
|
||||
/obj/effect/blob/resource/Be_Pulsed()
|
||||
. = ..()
|
||||
if(resource_delay > world.time)
|
||||
return
|
||||
flick("blob_resource_glow", src)
|
||||
if(overmind)
|
||||
overmind.add_points(1)
|
||||
resource_delay = world.time + 40 + overmind.resource_blobs.len * 2.5 //4 seconds plus a quarter second for each resource blob the overmind has
|
||||
else
|
||||
resource_delay = world.time + 40
|
||||
@@ -0,0 +1,21 @@
|
||||
/obj/effect/blob/shield
|
||||
name = "strong blob"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "blob_idle"
|
||||
desc = "A solid wall of slightly twitching tendrils."
|
||||
health = 150
|
||||
maxhealth = 150
|
||||
brute_resist = 0.1
|
||||
explosion_block = 3
|
||||
point_return = 4
|
||||
atmosblock = 1
|
||||
|
||||
|
||||
/obj/effect/blob/shield/scannerreport()
|
||||
return "Will prevent the spread of atmospheric changes."
|
||||
|
||||
/obj/effect/blob/shield/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
return
|
||||
|
||||
/obj/effect/blob/shield/core
|
||||
point_return = 0
|
||||
@@ -0,0 +1,175 @@
|
||||
/mob/camera/blob
|
||||
name = "Blob Overmind"
|
||||
real_name = "Blob Overmind"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
icon_state = "marker"
|
||||
|
||||
see_in_dark = 8
|
||||
see_invisible = SEE_INVISIBLE_MINIMUM
|
||||
invisibility = INVISIBILITY_OBSERVER
|
||||
|
||||
pass_flags = PASSBLOB
|
||||
faction = list("blob")
|
||||
|
||||
var/obj/effect/blob/core/blob_core = null // The blob overmind's core
|
||||
var/blob_points = 0
|
||||
var/max_blob_points = 100
|
||||
var/last_attack = 0
|
||||
var/datum/reagent/blob/blob_reagent_datum = new/datum/reagent/blob()
|
||||
var/list/blob_mobs = list()
|
||||
var/list/resource_blobs = list()
|
||||
var/ghostimage = null
|
||||
var/free_chem_rerolls = 1 //one free chemical reroll
|
||||
var/nodes_required = 1 //if the blob needs nodes to place resource and factory blobs
|
||||
var/placed = 0
|
||||
var/base_point_rate = 2 //for blob core placement
|
||||
var/manualplace_min_time = 600 //in deciseconds //a minute, to get bearings
|
||||
var/autoplace_max_time = 3600 //six minutes, as long as should be needed
|
||||
|
||||
/mob/camera/blob/New(loc, pre_placed = 0, mode_made = 0)
|
||||
if(pre_placed) //we already have a core!
|
||||
manualplace_min_time = 0
|
||||
autoplace_max_time = 0
|
||||
placed = 1
|
||||
else
|
||||
if(mode_made)
|
||||
manualplace_min_time = world.time + BLOB_NO_PLACE_TIME
|
||||
else
|
||||
manualplace_min_time += world.time
|
||||
autoplace_max_time += world.time
|
||||
overminds += src
|
||||
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.update_icon()
|
||||
|
||||
ghostimage = image(src.icon,src,src.icon_state)
|
||||
ghost_darkness_images |= ghostimage //so ghosts can see the blob cursor when they disable darkness
|
||||
updateallghostimages()
|
||||
..()
|
||||
|
||||
/mob/camera/blob/Life()
|
||||
if(!blob_core)
|
||||
if(!placed)
|
||||
if(manualplace_min_time && world.time >= manualplace_min_time)
|
||||
src << "<b><span class='big'><font color=\"#EE4000\">You may now place your blob core.</font></span></b>"
|
||||
src << "<span class='big'><font color=\"#EE4000\">You will automatically place your blob core in [round((autoplace_max_time - world.time)/600, 0.5)] minutes.</font></span>"
|
||||
manualplace_min_time = 0
|
||||
if(autoplace_max_time && world.time >= autoplace_max_time)
|
||||
place_blob_core(base_point_rate, 1)
|
||||
else
|
||||
qdel(src)
|
||||
..()
|
||||
|
||||
/mob/camera/blob/Destroy()
|
||||
for(var/BL in blobs)
|
||||
var/obj/effect/blob/B = BL
|
||||
if(B.overmind == src)
|
||||
B.overmind = null
|
||||
B.update_icon() //reset anything that was ours
|
||||
for(var/BLO in blob_mobs)
|
||||
var/mob/living/simple_animal/hostile/blob/BM = BLO
|
||||
BM.overmind = null
|
||||
BM.update_icons()
|
||||
overminds -= src
|
||||
if(ghostimage)
|
||||
ghost_darkness_images -= ghostimage
|
||||
qdel(ghostimage)
|
||||
ghostimage = null;
|
||||
updateallghostimages()
|
||||
return ..()
|
||||
|
||||
/mob/camera/blob/Login()
|
||||
..()
|
||||
sync_mind()
|
||||
src << "<span class='notice'>You are the overmind!</span>"
|
||||
blob_help()
|
||||
update_health_hud()
|
||||
|
||||
/mob/camera/blob/update_health_hud()
|
||||
if(blob_core)
|
||||
hud_used.healths.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#e36600'>[round(blob_core.health)]</font></div>"
|
||||
for(var/mob/living/simple_animal/hostile/blob/blobbernaut/B in blob_mobs)
|
||||
if(B.hud_used && B.hud_used.blobpwrdisplay)
|
||||
B.hud_used.blobpwrdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#82ed00'>[round(blob_core.health)]</font></div>"
|
||||
|
||||
/mob/camera/blob/proc/add_points(points)
|
||||
if(points != 0)
|
||||
blob_points = Clamp(blob_points + points, 0, max_blob_points)
|
||||
hud_used.blobpwrdisplay.maptext = "<div align='center' valign='middle' style='position:relative; top:0px; left:6px'><font color='#82ed00'>[round(src.blob_points)]</font></div>"
|
||||
|
||||
/mob/camera/blob/say(message)
|
||||
if (!message)
|
||||
return
|
||||
|
||||
if (src.client)
|
||||
if(client.prefs.muted & MUTE_IC)
|
||||
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/blob_talk(message)
|
||||
log_say("[key_name(src)] : [message]")
|
||||
|
||||
message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))
|
||||
|
||||
if (!message)
|
||||
return
|
||||
|
||||
var/message_a = say_quote(message, get_spans())
|
||||
var/rendered = "<span class='big'><font color=\"#EE4000\"><b>\[Blob Telepathy\] [name](<font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</font>)</b> [message_a]</font></span>"
|
||||
|
||||
for(var/mob/M in mob_list)
|
||||
if(isovermind(M) || istype(M, /mob/living/simple_animal/hostile/blob))
|
||||
M << rendered
|
||||
if(isobserver(M))
|
||||
var/link = FOLLOW_LINK(M, src)
|
||||
M << "[link] [rendered]"
|
||||
|
||||
/mob/camera/blob/emote(act,m_type=1,message = null)
|
||||
return
|
||||
|
||||
/mob/camera/blob/blob_act(obj/effect/blob/B)
|
||||
return
|
||||
|
||||
/mob/camera/blob/Stat()
|
||||
..()
|
||||
if(statpanel("Status"))
|
||||
if(blob_core)
|
||||
stat(null, "Core Health: [blob_core.health]")
|
||||
stat(null, "Power Stored: [blob_points]/[max_blob_points]")
|
||||
if(free_chem_rerolls)
|
||||
stat(null, "You have [free_chem_rerolls] Free Chemical Reroll\s Remaining")
|
||||
if(!placed)
|
||||
if(manualplace_min_time)
|
||||
stat(null, "Time Before Manual Placement: [max(round((manualplace_min_time - world.time)*0.1, 0.1), 0)]")
|
||||
stat(null, "Time Before Automatic Placement: [max(round((autoplace_max_time - world.time)*0.1, 0.1), 0)]")
|
||||
|
||||
/mob/camera/blob/Move(NewLoc, Dir = 0)
|
||||
if(placed)
|
||||
var/obj/effect/blob/B = locate() in range("3x3", NewLoc)
|
||||
if(B)
|
||||
loc = NewLoc
|
||||
else
|
||||
return 0
|
||||
else
|
||||
var/area/A = get_area(NewLoc)
|
||||
if(istype(NewLoc, /turf/open/space) || istype(A, /area/shuttle)) //if unplaced, can't go on shuttles or space tiles
|
||||
return 0
|
||||
loc = NewLoc
|
||||
return 1
|
||||
|
||||
/mob/camera/blob/proc/can_attack()
|
||||
return (world.time > (last_attack + CLICK_CD_RANGE))
|
||||
@@ -0,0 +1,332 @@
|
||||
/mob/camera/blob/proc/can_buy(cost = 15)
|
||||
if(blob_points < cost)
|
||||
src << "<span class='warning'>You cannot afford this, you need at least [cost] resources!</span>"
|
||||
return 0
|
||||
add_points(-cost)
|
||||
return 1
|
||||
|
||||
// Power verbs
|
||||
|
||||
/mob/camera/blob/proc/place_blob_core(point_rate, placement_override)
|
||||
if(placed && placement_override != -1)
|
||||
return 1
|
||||
if(!placement_override)
|
||||
for(var/mob/living/M in range(7, src))
|
||||
if("blob" in M.faction)
|
||||
continue
|
||||
if(M.client)
|
||||
src << "<span class='warning'>There is someone too close to place your blob core!</span>"
|
||||
return 0
|
||||
for(var/mob/living/M in view(13, src))
|
||||
if("blob" in M.faction)
|
||||
continue
|
||||
if(M.client)
|
||||
src << "<span class='warning'>Someone could see your blob core from here!</span>"
|
||||
return 0
|
||||
var/turf/T = get_turf(src)
|
||||
if(T.density)
|
||||
src << "<span class='warning'>This spot is too dense to place a blob core on!</span>"
|
||||
return 0
|
||||
for(var/obj/O in T)
|
||||
if(istype(O, /obj/effect/blob))
|
||||
if(istype(O, /obj/effect/blob/normal))
|
||||
qdel(O)
|
||||
else
|
||||
src << "<span class='warning'>There is already a blob here!</span>"
|
||||
return 0
|
||||
if(O.density)
|
||||
src << "<span class='warning'>This spot is too dense to place a blob core on!</span>"
|
||||
return 0
|
||||
if(world.time <= manualplace_min_time && world.time <= autoplace_max_time)
|
||||
src << "<span class='warning'>It is too early to place your blob core!</span>"
|
||||
return 0
|
||||
else if(placement_override == 1)
|
||||
var/turf/T = pick(blobstart)
|
||||
loc = T //got overrided? you're somewhere random, motherfucker
|
||||
if(placed && blob_core)
|
||||
blob_core.forceMove(loc)
|
||||
else
|
||||
var/obj/effect/blob/core/core = new(get_turf(src), null, point_rate, 1)
|
||||
core.overmind = src
|
||||
blob_core = core
|
||||
core.update_icon()
|
||||
update_health_hud()
|
||||
placed = 1
|
||||
return 1
|
||||
|
||||
/mob/camera/blob/verb/transport_core()
|
||||
set category = "Blob"
|
||||
set name = "Jump to Core"
|
||||
set desc = "Move your camera 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 = "Move your camera to a selected node."
|
||||
if(blob_nodes.len)
|
||||
var/list/nodes = list()
|
||||
for(var/i = 1; i <= blob_nodes.len; i++)
|
||||
nodes["Blob Node #[i]"] = blob_nodes[i]
|
||||
var/node_name = input(src, "Choose a node to jump to.", "Node Jump") in nodes
|
||||
var/obj/effect/blob/node/chosen_node = nodes[node_name]
|
||||
if(chosen_node)
|
||||
src.loc = chosen_node.loc
|
||||
|
||||
/mob/camera/blob/proc/createSpecial(price, blobType, nearEquals, needsNode, turf/T)
|
||||
if(!T)
|
||||
T = get_turf(src)
|
||||
var/obj/effect/blob/B = (locate(/obj/effect/blob) in T)
|
||||
if(!B)
|
||||
src << "<span class='warning'>There is no blob here!</span>"
|
||||
return
|
||||
if(!istype(B, /obj/effect/blob/normal))
|
||||
src << "<span class='warning'>Unable to use this blob, find a normal one.</span>"
|
||||
return
|
||||
if(needsNode && nodes_required)
|
||||
if(!(locate(/obj/effect/blob/node) in orange(3, T)) && !(locate(/obj/effect/blob/core) in orange(4, T)))
|
||||
src << "<span class='warning'>You need to place this blob closer to a node or core!</span>"
|
||||
return //handholdotron 2000
|
||||
if(nearEquals)
|
||||
for(var/obj/effect/blob/L in orange(nearEquals, T))
|
||||
if(L.type == blobType)
|
||||
src << "<span class='warning'>There is a similar blob nearby, move more than [nearEquals] tiles away from it!</span>"
|
||||
return
|
||||
if(!can_buy(price))
|
||||
return
|
||||
var/obj/effect/blob/N = B.change_to(blobType, src)
|
||||
return N
|
||||
|
||||
/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)
|
||||
src << "<span class='warning'>You now require a nearby node or core to place factory and resource blobs.</span>"
|
||||
else
|
||||
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 Shield Blob (10)"
|
||||
set desc = "Create a shield blob, which will block fire and is hard to kill."
|
||||
create_shield()
|
||||
|
||||
/mob/camera/blob/proc/create_shield(turf/T)
|
||||
createSpecial(10, /obj/effect/blob/shield, 0, 0, T)
|
||||
|
||||
/mob/camera/blob/verb/create_resource()
|
||||
set category = "Blob"
|
||||
set name = "Create Resource Blob (40)"
|
||||
set desc = "Create a resource tower which will generate resources for you."
|
||||
createSpecial(40, /obj/effect/blob/resource, 4, 1)
|
||||
|
||||
/mob/camera/blob/verb/create_node()
|
||||
set category = "Blob"
|
||||
set name = "Create Node Blob (60)"
|
||||
set desc = "Create a node, which will power nearby factory and resource blobs."
|
||||
createSpecial(60, /obj/effect/blob/node, 5, 0)
|
||||
|
||||
/mob/camera/blob/verb/create_factory()
|
||||
set category = "Blob"
|
||||
set name = "Create Factory Blob (60)"
|
||||
set desc = "Create a spore tower that will spawn spores to harass your enemies."
|
||||
createSpecial(60, /obj/effect/blob/factory, 7, 1)
|
||||
|
||||
/mob/camera/blob/verb/create_blobbernaut()
|
||||
set category = "Blob"
|
||||
set name = "Create Blobbernaut (40)"
|
||||
set desc = "Create a powerful blobbernaut which is mildly smart and will attack enemies."
|
||||
var/turf/T = get_turf(src)
|
||||
var/obj/effect/blob/factory/B = locate(/obj/effect/blob/factory) in T
|
||||
if(!B)
|
||||
src << "<span class='warning'>You must be on a factory blob!</span>"
|
||||
return
|
||||
if(B.naut) //if it already made a blobbernaut, it can't do it again
|
||||
src << "<span class='warning'>This factory blob is already sustaining a blobbernaut.</span>"
|
||||
return
|
||||
if(B.health < B.maxhealth * 0.5)
|
||||
src << "<span class='warning'>This factory blob is too damaged to sustain a blobbernaut.</span>"
|
||||
return
|
||||
if(!can_buy(40))
|
||||
return
|
||||
B.maxhealth = initial(B.maxhealth) * 0.25 //factories that produced a blobbernaut have much lower health
|
||||
B.check_health()
|
||||
B.visible_message("<span class='warning'><b>The blobbernaut [pick("rips", "tears", "shreds")] its way out of the factory blob!</b></span>")
|
||||
playsound(B.loc, 'sound/effects/splat.ogg', 50, 1)
|
||||
var/mob/living/simple_animal/hostile/blob/blobbernaut/blobber = new /mob/living/simple_animal/hostile/blob/blobbernaut(get_turf(B))
|
||||
flick("blobbernaut_produce", blobber)
|
||||
B.naut = blobber
|
||||
blobber.factory = B
|
||||
blobber.overmind = src
|
||||
blobber.update_icons()
|
||||
blobber.notransform = 1 //stop the naut from moving around
|
||||
blobber.adjustHealth(blobber.maxHealth * 0.5)
|
||||
blob_mobs += blobber
|
||||
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [blob_reagent_datum.name] blobbernaut?", ROLE_BLOB, null, ROLE_BLOB, 50) //players must answer rapidly
|
||||
var/client/C = null
|
||||
if(candidates.len) //if we got at least one candidate, they're a blobbernaut now.
|
||||
C = pick(candidates)
|
||||
blobber.notransform = 0
|
||||
blobber.key = C.key
|
||||
blobber << 'sound/effects/blobattack.ogg'
|
||||
blobber << 'sound/effects/attackblob.ogg'
|
||||
blobber << "<b>You are a blobbernaut!</b>"
|
||||
blobber << "You are powerful, hard to kill, and slowly regenerate near nodes and cores, but will slowly die if not near the blob or if the factory that made you is killed."
|
||||
blobber << "You can communicate with other blobbernauts and overminds via <b>:b</b>"
|
||||
blobber << "Your overmind's blob reagent is: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!"
|
||||
blobber << "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.shortdesc ? "[blob_reagent_datum.shortdesc]" : "[blob_reagent_datum.description]"]"
|
||||
else
|
||||
blobber.notransform = 0 //otherwise, just let it move
|
||||
|
||||
/mob/camera/blob/verb/relocate_core()
|
||||
set category = "Blob"
|
||||
set name = "Relocate Core (80)"
|
||||
set desc = "Swaps the locations of your core and the selected node."
|
||||
var/turf/T = get_turf(src)
|
||||
var/obj/effect/blob/node/B = locate(/obj/effect/blob/node) in T
|
||||
if(!B)
|
||||
src << "<span class='warning'>You must be on a blob node!</span>"
|
||||
return
|
||||
if(!can_buy(80))
|
||||
return
|
||||
var/turf/old_turf = blob_core.loc
|
||||
blob_core.loc = T
|
||||
B.loc = old_turf
|
||||
|
||||
/mob/camera/blob/verb/revert()
|
||||
set category = "Blob"
|
||||
set name = "Remove Blob"
|
||||
set desc = "Removes a blob, giving you back some resources."
|
||||
var/turf/T = get_turf(src)
|
||||
remove_blob(T)
|
||||
|
||||
/mob/camera/blob/proc/remove_blob(turf/T)
|
||||
var/obj/effect/blob/B = locate() in T
|
||||
if(!B)
|
||||
src << "<span class='warning'>There is no blob there!</span>"
|
||||
return
|
||||
if(B.point_return < 0)
|
||||
src << "<span class='warning'>Unable to remove this blob.</span>"
|
||||
return
|
||||
if(max_blob_points < B.point_return + blob_points)
|
||||
src << "<span class='warning'>You have too many resources to remove this blob!</span>"
|
||||
return
|
||||
if(B.point_return)
|
||||
add_points(B.point_return)
|
||||
src << "<span class='notice'>Gained [B.point_return] resources from removing \the [B].</span>"
|
||||
qdel(B)
|
||||
|
||||
/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, instead attacks it, damaging mobs and objects."
|
||||
var/turf/T = get_turf(src)
|
||||
expand_blob(T)
|
||||
|
||||
/mob/camera/blob/proc/expand_blob(turf/T)
|
||||
if(!can_attack())
|
||||
return
|
||||
var/obj/effect/blob/OB = locate() in circlerange(T, 1)
|
||||
if(!OB)
|
||||
src << "<span class='warning'>There is no blob adjacent to the target tile!</span>"
|
||||
return
|
||||
if(can_buy(5))
|
||||
var/attacksuccess = FALSE
|
||||
last_attack = world.time
|
||||
for(var/mob/living/L in T)
|
||||
if("blob" in L.faction) //no friendly/dead fire
|
||||
continue
|
||||
if(L.stat != DEAD)
|
||||
attacksuccess = TRUE
|
||||
var/mob_protection = L.get_permeability_protection()
|
||||
blob_reagent_datum.reaction_mob(L, VAPOR, 25, 1, mob_protection, src)
|
||||
blob_reagent_datum.send_message(L)
|
||||
var/obj/effect/blob/B = locate() in T
|
||||
if(B)
|
||||
if(attacksuccess) //if we successfully attacked a turf with a blob on it, don't refund shit
|
||||
B.blob_attack_animation(T, src)
|
||||
else
|
||||
src << "<span class='warning'>There is a blob there!</span>"
|
||||
add_points(5) //otherwise, refund all of the cost
|
||||
return
|
||||
else
|
||||
OB.expand(T, src)
|
||||
|
||||
/mob/camera/blob/verb/rally_spores_power()
|
||||
set category = "Blob"
|
||||
set name = "Rally Spores"
|
||||
set desc = "Rally your spores to move to a target location."
|
||||
var/turf/T = get_turf(src)
|
||||
rally_spores(T)
|
||||
|
||||
/mob/camera/blob/proc/rally_spores(turf/T)
|
||||
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 blob_mobs)
|
||||
if(isturf(BS.loc) && get_dist(BS, T) <= 35)
|
||||
BS.LoseTarget()
|
||||
BS.Goto(pick(surrounding_turfs), BS.move_to_delay)
|
||||
|
||||
/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."
|
||||
var/speak_text = input(src, "What would you like to say with your minions?", "Blob Broadcast", null) as text
|
||||
if(!speak_text)
|
||||
return
|
||||
else
|
||||
src << "You broadcast with your minions, <B>[speak_text]</B>"
|
||||
for(var/BLO in blob_mobs)
|
||||
var/mob/living/simple_animal/hostile/blob/BM = BLO
|
||||
if(BM.stat == CONSCIOUS)
|
||||
BM.say(speak_text)
|
||||
|
||||
/mob/camera/blob/verb/chemical_reroll()
|
||||
set category = "Blob"
|
||||
set name = "Reactive Chemical Adaptation (40)"
|
||||
set desc = "Replaces your chemical with a random, different one."
|
||||
if(free_chem_rerolls || can_buy(40))
|
||||
set_chemical()
|
||||
if(free_chem_rerolls)
|
||||
free_chem_rerolls--
|
||||
|
||||
/mob/camera/blob/proc/set_chemical()
|
||||
var/datum/reagent/blob/BC = pick((subtypesof(/datum/reagent/blob) - blob_reagent_datum.type))
|
||||
blob_reagent_datum = new BC
|
||||
for(var/BL in blobs)
|
||||
var/obj/effect/blob/B = BL
|
||||
B.update_icon()
|
||||
for(var/BLO in blob_mobs)
|
||||
var/mob/living/simple_animal/hostile/blob/BM = BLO
|
||||
BM.update_icons() //If it's getting a new chemical, tell it what it does!
|
||||
BM << "Your overmind's blob reagent is now: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!"
|
||||
BM << "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.shortdesc ? "[blob_reagent_datum.shortdesc]" : "[blob_reagent_datum.description]"]"
|
||||
src << "Your reagent is now: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!"
|
||||
src << "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.description]"
|
||||
|
||||
/mob/camera/blob/verb/blob_help()
|
||||
set category = "Blob"
|
||||
set name = "*Blob Help*"
|
||||
set desc = "Help on how to blob."
|
||||
src << "<b>As the overmind, you can control the blob!</b>"
|
||||
src << "Your blob reagent is: <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font>!"
|
||||
src << "The <b><font color=\"[blob_reagent_datum.color]\">[blob_reagent_datum.name]</b></font> reagent [blob_reagent_datum.description]"
|
||||
src << "<b>You can expand, which will attack people, damage objects, or place a Normal Blob if the tile is clear.</b>"
|
||||
src << "<i>Normal Blobs</i> will expand your reach and can be upgraded into special blobs that perform certain functions."
|
||||
src << "<b>You can upgrade normal blobs into the following types of blob:</b>"
|
||||
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."
|
||||
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."
|
||||
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."
|
||||
src << "<i>Blobbernauts</i> can be produced from factories for a cost, and are hard to kill, powerful, and moderately smart. The factory used to create one will become fragile and briefly unable to produce spores."
|
||||
src << "<i>Node Blobs</i> are blobs which grow, like the core. Like the core it can activate resource and factory blobs."
|
||||
src << "<b>In addition to the buttons on your HUD, there are a few click shortcuts to speed up expansion and defense.</b>"
|
||||
src << "<b>Shortcuts:</b> Click = Expand Blob <b>|</b> Middle Mouse Click = Rally Spores <b>|</b> Ctrl Click = Create Shield Blob <b>|</b> Alt Click = Remove Blob"
|
||||
src << "Attempting to talk will send a message to all other overminds, allowing you to coordinate with them."
|
||||
if(!placed && autoplace_max_time <= world.time)
|
||||
src << "<span class='big'><font color=\"#EE4000\">You will automatically place your blob core in [round((autoplace_max_time - world.time)/600, 0.5)] minutes.</font></span>"
|
||||
src << "<span class='big'><font color=\"#EE4000\">You [manualplace_min_time ? "will be able to":"can"] manually place your blob core by pressing the button in the bottom right corner of the screen.</font></span>"
|
||||
@@ -0,0 +1,360 @@
|
||||
//I will need to recode parts of this but I am way too tired atm //I don't know who left this comment but they never did come back
|
||||
/obj/effect/blob
|
||||
name = "blob"
|
||||
icon = 'icons/mob/blob.dmi'
|
||||
luminosity = 1
|
||||
desc = "A thick wall of writhing tendrils."
|
||||
density = 0 //this being 0 causes two bugs, being able to attack blob tiles behind other blobs and being unable to move on blob tiles in no gravity, but turning it to 1 causes the blob mobs to be unable to path through blobs, which is probably worse.
|
||||
opacity = 0
|
||||
anchored = 1
|
||||
explosion_block = 1
|
||||
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 = 30
|
||||
var/maxhealth = 30
|
||||
var/health_regen = 2 //how much health this blob regens when pulsed
|
||||
var/pulse_timestamp = 0 //we got pulsed/healed when?
|
||||
var/brute_resist = 0.5 //multiplies brute damage by this
|
||||
var/fire_resist = 1 //multiplies burn damage by this
|
||||
var/atmosblock = 0 //if the blob blocks atmos and heat spread
|
||||
var/mob/camera/blob/overmind
|
||||
|
||||
|
||||
/obj/effect/blob/New(loc)
|
||||
var/area/Ablob = get_area(loc)
|
||||
if(Ablob.blob_allowed) //Is this area allowed for winning as blob?
|
||||
blobs_legit += src
|
||||
blobs += src //Keep track of the blob in the normal list either way
|
||||
src.setDir(pick(1, 2, 4, 8))
|
||||
src.update_icon()
|
||||
..(loc)
|
||||
ConsumeTile()
|
||||
if(atmosblock)
|
||||
air_update_turf(1)
|
||||
return
|
||||
|
||||
/obj/effect/blob/proc/creation_action() //When it's created by the overmind, do this.
|
||||
return
|
||||
|
||||
/obj/effect/blob/Destroy()
|
||||
if(atmosblock)
|
||||
atmosblock = 0
|
||||
air_update_turf(1)
|
||||
blobs_legit -= src //if it was in the legit blobs list, it isn't now
|
||||
blobs -= src //it's no longer in the all blobs list either
|
||||
playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) //Expand() is no longer broken, no check necessary.
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/effect/blob/Adjacent(var/atom/neighbour)
|
||||
. = ..()
|
||||
if(.)
|
||||
var/result = 0
|
||||
var/direction = get_dir(src, neighbour)
|
||||
var/list/dirs = list("[NORTHWEST]" = list(NORTH, WEST), "[NORTHEAST]" = list(NORTH, EAST), "[SOUTHEAST]" = list(SOUTH, EAST), "[SOUTHWEST]" = list(SOUTH, WEST))
|
||||
for(var/A in dirs)
|
||||
if(direction == text2num(A))
|
||||
for(var/B in dirs[A])
|
||||
var/C = locate(/obj/effect/blob) in get_step(src, B)
|
||||
if(C)
|
||||
result++
|
||||
. -= result - 1
|
||||
|
||||
/obj/effect/blob/CanAtmosPass(turf/T)
|
||||
return !atmosblock
|
||||
|
||||
/obj/effect/blob/BlockSuperconductivity()
|
||||
return atmosblock
|
||||
|
||||
/obj/effect/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/effect/blob/CanAStarPass(ID, dir, caller)
|
||||
. = 0
|
||||
if(ismovableatom(caller))
|
||||
var/atom/movable/mover = caller
|
||||
. = . || mover.checkpass(PASSBLOB)
|
||||
|
||||
/obj/effect/blob/proc/check_health(cause)
|
||||
health = Clamp(health, 0, maxhealth)
|
||||
if(health <= 0)
|
||||
if(overmind)
|
||||
overmind.blob_reagent_datum.death_reaction(src, cause)
|
||||
qdel(src) //we dead now
|
||||
return
|
||||
return
|
||||
|
||||
/obj/effect/blob/update_icon() //Updates color based on overmind color if we have an overmind.
|
||||
if(overmind)
|
||||
color = overmind.blob_reagent_datum.color
|
||||
else
|
||||
color = null
|
||||
return
|
||||
|
||||
|
||||
/obj/effect/blob/process()
|
||||
Life()
|
||||
return
|
||||
|
||||
/obj/effect/blob/proc/Life()
|
||||
return
|
||||
|
||||
/obj/effect/blob/proc/Pulse_Area(pulsing_overmind = overmind, claim_range = 10, pulse_range = 3, expand_range = 2)
|
||||
src.Be_Pulsed()
|
||||
if(claim_range)
|
||||
for(var/obj/effect/blob/B in urange(claim_range, src, 1))
|
||||
if(!B.overmind && !istype(B, /obj/effect/blob/core) && prob(30))
|
||||
B.overmind = pulsing_overmind //reclaim unclaimed, non-core blobs.
|
||||
B.update_icon()
|
||||
if(pulse_range)
|
||||
for(var/obj/effect/blob/B in orange(pulse_range, src))
|
||||
B.Be_Pulsed()
|
||||
if(expand_range)
|
||||
if(prob(85))
|
||||
src.expand()
|
||||
for(var/obj/effect/blob/B in orange(expand_range, src))
|
||||
if(prob(max(13 - get_dist(get_turf(src), get_turf(B)) * 4, 1))) //expand falls off with range but is faster near the blob causing the expansion
|
||||
B.expand()
|
||||
return
|
||||
|
||||
/obj/effect/blob/proc/Be_Pulsed()
|
||||
if(pulse_timestamp <= world.time)
|
||||
ConsumeTile()
|
||||
health = min(maxhealth, health+health_regen)
|
||||
update_icon()
|
||||
pulse_timestamp = world.time + 10
|
||||
return 1 //we did it, we were pulsed!
|
||||
return 0 //oh no we failed
|
||||
|
||||
/obj/effect/blob/proc/ConsumeTile()
|
||||
for(var/atom/A in loc)
|
||||
A.blob_act(src)
|
||||
if(istype(loc, /turf/closed/wall))
|
||||
loc.blob_act(src) //don't ask how a wall got on top of the core, just eat it
|
||||
|
||||
/obj/effect/blob/proc/blob_attack_animation(atom/A = null, controller) //visually attacks an atom
|
||||
var/obj/effect/overlay/temp/blob/O = PoolOrNew(/obj/effect/overlay/temp/blob, src.loc)
|
||||
if(controller)
|
||||
var/mob/camera/blob/BO = controller
|
||||
O.color = BO.blob_reagent_datum.color
|
||||
O.alpha = 200
|
||||
else if(overmind)
|
||||
O.color = overmind.blob_reagent_datum.color
|
||||
if(A)
|
||||
O.do_attack_animation(A) //visually attack the whatever
|
||||
return O //just in case you want to do something to the animation.
|
||||
|
||||
/obj/effect/blob/proc/expand(turf/T = null, controller = null, expand_reaction = 1)
|
||||
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/effect/blob) in T))
|
||||
break
|
||||
else
|
||||
T = null
|
||||
if(!T)
|
||||
return 0
|
||||
var/make_blob = TRUE //can we make a blob?
|
||||
|
||||
if(istype(T, /turf/open/space) && !(locate(/obj/structure/lattice) in T) && prob(80))
|
||||
make_blob = FALSE
|
||||
playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) //Let's give some feedback that we DID try to spawn in space, since players are used to it
|
||||
|
||||
ConsumeTile() //hit the tile we're in, making sure there are no border objects blocking us
|
||||
if(!T.CanPass(src, T, 5)) //is the target turf impassable
|
||||
make_blob = FALSE
|
||||
T.blob_act(src) //hit the turf if it is
|
||||
for(var/atom/A in T)
|
||||
if(!A.CanPass(src, T, 5)) //is anything in the turf impassable
|
||||
make_blob = FALSE
|
||||
A.blob_act(src) //also hit everything in the turf
|
||||
|
||||
if(make_blob) //well, can we?
|
||||
var/obj/effect/blob/B = new /obj/effect/blob/normal(src.loc)
|
||||
if(controller)
|
||||
B.overmind = controller
|
||||
else
|
||||
B.overmind = overmind
|
||||
B.density = 1
|
||||
if(T.Enter(B,src)) //NOW we can attempt to move into the tile
|
||||
B.density = initial(B.density)
|
||||
B.loc = T
|
||||
B.update_icon()
|
||||
if(B.overmind && expand_reaction)
|
||||
B.overmind.blob_reagent_datum.expand_reaction(src, B, T)
|
||||
return B
|
||||
else
|
||||
blob_attack_animation(T, controller)
|
||||
T.blob_act(src) //if we can't move in hit the turf again
|
||||
qdel(B) //we should never get to this point, since we checked before moving in. destroy the blob so we don't have two blobs on one tile
|
||||
return null
|
||||
else
|
||||
blob_attack_animation(T, controller) //if we can't, animate that we attacked
|
||||
return null
|
||||
|
||||
|
||||
/obj/effect/blob/ex_act(severity, target)
|
||||
..()
|
||||
var/damage = 150 - 20 * severity
|
||||
take_damage(damage, BRUTE)
|
||||
|
||||
/obj/effect/blob/fire_act(datum/gas_mixture/air, exposed_temperature, exposed_volume)
|
||||
..()
|
||||
var/damage = Clamp(0.01 * exposed_temperature, 0, 4)
|
||||
take_damage(damage, BURN)
|
||||
|
||||
/obj/effect/blob/emp_act(severity)
|
||||
if(severity > 0)
|
||||
if(overmind)
|
||||
overmind.blob_reagent_datum.emp_reaction(src, severity)
|
||||
if(prob(100 - severity * 30))
|
||||
PoolOrNew(/obj/effect/overlay/temp/emp, get_turf(src))
|
||||
|
||||
/obj/effect/blob/tesla_act(power)
|
||||
..()
|
||||
if(overmind)
|
||||
if(overmind.blob_reagent_datum.tesla_reaction(src, power))
|
||||
take_damage(power/400, BURN)
|
||||
else
|
||||
take_damage(power/400, BURN)
|
||||
|
||||
/obj/effect/blob/extinguish()
|
||||
..()
|
||||
if(overmind)
|
||||
overmind.blob_reagent_datum.extinguish_reaction(src)
|
||||
|
||||
/obj/effect/blob/bullet_act(var/obj/item/projectile/Proj)
|
||||
..()
|
||||
take_damage(Proj.damage, Proj.damage_type, Proj)
|
||||
return 0
|
||||
|
||||
/obj/effect/blob/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/device/analyzer))
|
||||
user.changeNext_move(CLICK_CD_MELEE)
|
||||
user << "<b>The analyzer beeps once, then reports:</b><br>"
|
||||
user << 'sound/machines/ping.ogg'
|
||||
chemeffectreport(user)
|
||||
typereport(user)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/effect/blob/proc/chemeffectreport(mob/user)
|
||||
if(overmind)
|
||||
user << "<b>Material: <font color=\"[overmind.blob_reagent_datum.color]\">[overmind.blob_reagent_datum.name]</font><span class='notice'>.</span></b>"
|
||||
user << "<b>Material Effects:</b> <span class='notice'>[overmind.blob_reagent_datum.analyzerdescdamage]</span>"
|
||||
user << "<b>Material Properties:</b> <span class='notice'>[overmind.blob_reagent_datum.analyzerdesceffect]</span><br>"
|
||||
else
|
||||
user << "<b>No Material Detected!</b><br>"
|
||||
|
||||
/obj/effect/blob/proc/typereport(mob/user)
|
||||
user << "<b>Blob Type:</b> <span class='notice'>[uppertext(initial(name))]</span>"
|
||||
user << "<b>Health:</b> <span class='notice'>[health]/[maxhealth]</span>"
|
||||
user << "<b>Effects:</b> <span class='notice'>[scannerreport()]</span>"
|
||||
|
||||
/obj/effect/blob/attacked_by(obj/item/I, mob/living/user)
|
||||
user.changeNext_move(CLICK_CD_MELEE)
|
||||
user.do_attack_animation(src)
|
||||
playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1)
|
||||
visible_message("<span class='danger'>[user] has attacked the [src.name] with \the [I]!</span>")
|
||||
if(I.damtype == BURN)
|
||||
playsound(src.loc, 'sound/items/Welder.ogg', 100, 1)
|
||||
take_damage(I.force, I.damtype, user)
|
||||
|
||||
/obj/effect/blob/attack_animal(mob/living/simple_animal/M)
|
||||
if("blob" in M.faction) //sorry, but you can't kill the blob as a blobbernaut
|
||||
return
|
||||
M.changeNext_move(CLICK_CD_MELEE)
|
||||
M.do_attack_animation(src)
|
||||
playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1)
|
||||
visible_message("<span class='danger'>\The [M] has attacked the [src.name]!</span>")
|
||||
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
|
||||
take_damage(damage, M.melee_damage_type, M)
|
||||
return
|
||||
|
||||
/obj/effect/blob/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
M.changeNext_move(CLICK_CD_MELEE)
|
||||
M.do_attack_animation(src)
|
||||
playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1)
|
||||
visible_message("<span class='danger'>[M] has slashed the [src.name]!</span>")
|
||||
var/damage = rand(15, 30)
|
||||
take_damage(damage, BRUTE, M)
|
||||
return
|
||||
|
||||
/obj/effect/blob/proc/take_damage(damage, damage_type, cause = null, overmind_reagent_trigger = 1)
|
||||
switch(damage_type) //blobs only take brute and burn damage
|
||||
if(BRUTE)
|
||||
damage = max(damage * brute_resist, 0)
|
||||
if(BURN)
|
||||
damage = max(damage * fire_resist, 0)
|
||||
if(CLONE) //this is basically a marker for 'don't modify the damage'
|
||||
|
||||
else
|
||||
damage = 0
|
||||
if(overmind && overmind_reagent_trigger)
|
||||
damage = overmind.blob_reagent_datum.damage_reaction(src, health, damage, damage_type, cause) //pass the blob, its health before damage, the damage being done, the type of damage being done, and the cause.
|
||||
health -= damage
|
||||
update_icon()
|
||||
check_health(cause)
|
||||
|
||||
/obj/effect/blob/proc/change_to(type, controller)
|
||||
if(!ispath(type))
|
||||
throw EXCEPTION("change_to(): invalid type for blob")
|
||||
return
|
||||
var/obj/effect/blob/B = new type(src.loc)
|
||||
if(controller)
|
||||
B.overmind = controller
|
||||
B.creation_action()
|
||||
B.update_icon()
|
||||
qdel(src)
|
||||
return B
|
||||
|
||||
/obj/effect/blob/examine(mob/user)
|
||||
..()
|
||||
var/datum/atom_hud/hud_to_check = huds[DATA_HUD_MEDICAL_ADVANCED]
|
||||
if(user.research_scanner || (user in hud_to_check.hudusers))
|
||||
user << "<b>Your HUD displays an extensive report...</b><br>"
|
||||
chemeffectreport(user)
|
||||
typereport(user)
|
||||
else
|
||||
user << "It seems to be made of [get_chem_name()]."
|
||||
|
||||
/obj/effect/blob/proc/scannerreport()
|
||||
return "A generic blob. Looks like someone forgot to override this proc, adminhelp this."
|
||||
|
||||
/obj/effect/blob/proc/get_chem_name()
|
||||
if(overmind)
|
||||
return overmind.blob_reagent_datum.name
|
||||
return "an unknown variant"
|
||||
|
||||
/obj/effect/blob/normal
|
||||
name = "normal blob"
|
||||
icon_state = "blob"
|
||||
luminosity = 0
|
||||
health = 21
|
||||
maxhealth = 25
|
||||
health_regen = 1
|
||||
brute_resist = 0.25
|
||||
|
||||
/obj/effect/blob/normal/scannerreport()
|
||||
if(health <= 10)
|
||||
return "Currently weak to brute damage."
|
||||
return "N/A"
|
||||
|
||||
/obj/effect/blob/normal/update_icon()
|
||||
..()
|
||||
if(health <= 10)
|
||||
icon_state = "blob_damaged"
|
||||
name = "fragile blob"
|
||||
desc = "A thin lattice of slightly twitching tendrils."
|
||||
brute_resist = 0.5
|
||||
else
|
||||
icon_state = "blob"
|
||||
name = "blob"
|
||||
desc = "A thick wall of writhing tendrils."
|
||||
brute_resist = 0.25
|
||||
Reference in New Issue
Block a user