mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-08-21 11:07:12 +01:00
Merge remote-tracking branch 'origin/master' into TGRefundPort
This commit is contained in:
@@ -1,137 +0,0 @@
|
||||
GLOBAL_LIST_EMPTY(antagonists)
|
||||
|
||||
/datum/antagonist
|
||||
var/name = "Antagonist"
|
||||
var/roundend_category = "other antagonists" //Section of roundend report, datums with same category will be displayed together, also default header for the section
|
||||
var/show_in_roundend = TRUE //Set to false to hide the antagonists from roundend report
|
||||
var/datum/mind/owner //Mind that owns this datum
|
||||
var/silent = FALSE //Silent will prevent the gain/lose texts to show
|
||||
var/can_coexist_with_others = TRUE //Whether or not the person will be able to have more than one datum
|
||||
var/list/typecache_datum_blacklist = list() //List of datums this type can't coexist with
|
||||
var/delete_on_mind_deletion = TRUE
|
||||
var/job_rank
|
||||
var/replace_banned = TRUE //Should replace jobbaned player with ghosts if granted.
|
||||
var/list/objectives = list()
|
||||
var/antag_memory = ""//These will be removed with antag datum
|
||||
|
||||
/datum/antagonist/New()
|
||||
GLOB.antagonists += src
|
||||
typecache_datum_blacklist = typecacheof(typecache_datum_blacklist)
|
||||
|
||||
/datum/antagonist/Destroy()
|
||||
GLOB.antagonists -= src
|
||||
if(owner)
|
||||
LAZYREMOVE(owner.antag_datums, src)
|
||||
owner = null
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/proc/can_be_owned(datum/mind/new_owner)
|
||||
. = TRUE
|
||||
var/datum/mind/tested = new_owner || owner
|
||||
if(tested.has_antag_datum(type))
|
||||
return FALSE
|
||||
for(var/i in tested.antag_datums)
|
||||
var/datum/antagonist/A = i
|
||||
if(is_type_in_typecache(src, A.typecache_datum_blacklist))
|
||||
return FALSE
|
||||
|
||||
//This will be called in add_antag_datum before owner assignment.
|
||||
//Should return antag datum without owner.
|
||||
/datum/antagonist/proc/specialization(datum/mind/new_owner)
|
||||
return src
|
||||
|
||||
/datum/antagonist/proc/on_body_transfer(mob/living/old_body, mob/living/new_body)
|
||||
remove_innate_effects(old_body)
|
||||
apply_innate_effects(new_body)
|
||||
|
||||
//This handles the application of antag huds/special abilities
|
||||
/datum/antagonist/proc/apply_innate_effects(mob/living/mob_override)
|
||||
return
|
||||
|
||||
//This handles the removal of antag huds/special abilities
|
||||
/datum/antagonist/proc/remove_innate_effects(mob/living/mob_override)
|
||||
return
|
||||
|
||||
//Assign default team and creates one for one of a kind team antagonists
|
||||
/datum/antagonist/proc/create_team(datum/team/team)
|
||||
return
|
||||
|
||||
//Proc called when the datum is given to a mind.
|
||||
/datum/antagonist/proc/on_gain()
|
||||
if(owner && owner.current)
|
||||
if(!silent)
|
||||
greet()
|
||||
apply_innate_effects()
|
||||
if(is_banned(owner.current) && replace_banned)
|
||||
replace_banned_player()
|
||||
|
||||
/datum/antagonist/proc/is_banned(mob/M)
|
||||
if(!M)
|
||||
return FALSE
|
||||
. = (jobban_isbanned(M, ROLE_SYNDICATE) || (job_rank && jobban_isbanned(M, job_rank)))
|
||||
|
||||
/datum/antagonist/proc/replace_banned_player()
|
||||
set waitfor = FALSE
|
||||
|
||||
var/list/mob/dead/observer/candidates = pollCandidates("Do you want to play as a [name]?", job_rank, TRUE, 50)
|
||||
if(LAZYLEN(candidates))
|
||||
var/mob/dead/observer/C = pick(candidates)
|
||||
to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
|
||||
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(owner.current)]) to replace a jobbaned player.")
|
||||
owner.current.ghostize(0)
|
||||
owner.current.key = C.key
|
||||
|
||||
/datum/antagonist/proc/on_removal()
|
||||
remove_innate_effects()
|
||||
if(owner)
|
||||
LAZYREMOVE(owner.antag_datums, src)
|
||||
if(!silent && owner.current)
|
||||
farewell()
|
||||
owner.objectives -= objectives
|
||||
var/datum/team/team = get_team()
|
||||
if(team)
|
||||
team.remove_member(owner)
|
||||
qdel(src)
|
||||
|
||||
/datum/antagonist/proc/greet()
|
||||
return
|
||||
|
||||
/datum/antagonist/proc/farewell()
|
||||
return
|
||||
|
||||
|
||||
//Returns the team antagonist belongs to if any.
|
||||
/datum/antagonist/proc/get_team()
|
||||
return
|
||||
|
||||
//Individual roundend report
|
||||
/datum/antagonist/proc/roundend_report()
|
||||
var/list/report = list()
|
||||
|
||||
if(!owner)
|
||||
CRASH("antagonist datum without owner")
|
||||
|
||||
report += printplayer(owner)
|
||||
|
||||
var/objectives_complete = TRUE
|
||||
if(owner.objectives.len)
|
||||
report += printobjectives(owner)
|
||||
for(var/datum/objective/objective in owner.objectives)
|
||||
if(!objective.check_completion())
|
||||
objectives_complete = FALSE
|
||||
break
|
||||
|
||||
if(owner.objectives.len == 0 || objectives_complete)
|
||||
report += "<span class='greentext big'>The [name] was successful!</span>"
|
||||
else
|
||||
report += "<span class='redtext big'>The [name] has failed!</span>"
|
||||
|
||||
return report.Join("<br>")
|
||||
|
||||
//Displayed at the start of roundend_category section, default to roundend_category header
|
||||
/datum/antagonist/proc/roundend_report_header()
|
||||
return "<span class='header'>The [roundend_category] were:</span><br>"
|
||||
|
||||
//Displayed at the end of roundend_category section
|
||||
/datum/antagonist/proc/roundend_report_footer()
|
||||
return
|
||||
@@ -1,19 +0,0 @@
|
||||
//Returns MINDS of the assigned antags of given type/subtypes
|
||||
/proc/get_antag_minds(antag_type, specific = FALSE)
|
||||
. = list()
|
||||
for(var/datum/antagonist/A in GLOB.antagonists)
|
||||
if(!A.owner)
|
||||
continue
|
||||
if(!antag_type || !specific && istype(A, antag_type) || specific && A.type == antag_type)
|
||||
. += A.owner
|
||||
|
||||
//Get all teams [of type team_type]
|
||||
/proc/get_all_teams(team_type)
|
||||
. = list()
|
||||
for(var/V in GLOB.antagonists)
|
||||
var/datum/antagonist/A = V
|
||||
if(!A.owner)
|
||||
continue
|
||||
var/datum/team/T = A.get_team()
|
||||
if(!team_type || istype(T, team_type))
|
||||
. |= T
|
||||
@@ -1,90 +0,0 @@
|
||||
/datum/atom_hud/antag
|
||||
hud_icons = list(SPECIALROLE_HUD,NATIONS_HUD)
|
||||
var/self_visible = TRUE
|
||||
|
||||
/datum/atom_hud/antag/hidden
|
||||
self_visible = FALSE
|
||||
|
||||
/datum/atom_hud/antag/proc/join_hud(mob/M, slave)
|
||||
//sees_hud should be set to 0 if the mob does not get to see it's own hud type.
|
||||
if(!istype(M))
|
||||
CRASH("join_hud(): [M] ([M.type]) is not a mob!")
|
||||
if(M.mind.antag_hud && !slave) //note: please let this runtime if a mob has no mind, as mindless mobs shouldn't be getting antagged
|
||||
M.mind.antag_hud.leave_hud(M)
|
||||
add_to_hud(M)
|
||||
if(self_visible)
|
||||
add_hud_to(M)
|
||||
M.mind.antag_hud = src
|
||||
|
||||
/datum/atom_hud/antag/proc/leave_hud(mob/M)
|
||||
if(!M)
|
||||
return
|
||||
if(!istype(M))
|
||||
CRASH("leave_hud(): [M] ([M.type]) is not a mob!")
|
||||
remove_from_hud(M)
|
||||
remove_hud_from(M)
|
||||
if(M.mind)
|
||||
M.mind.antag_hud = null
|
||||
|
||||
|
||||
//GAME_MODE PROCS
|
||||
//called to set a mob's antag icon state
|
||||
/proc/set_antag_hud(mob/M, new_icon_state)
|
||||
if(!istype(M))
|
||||
CRASH("set_antag_hud(): [M] ([M.type]) is not a mob!")
|
||||
var/image/holder = M.hud_list[SPECIALROLE_HUD]
|
||||
if(holder)
|
||||
holder.icon_state = new_icon_state
|
||||
if(M.mind || new_icon_state) //in mindless mobs, only null is acceptable, otherwise we're antagging a mindless mob, meaning we should runtime
|
||||
M.mind.antag_hud_icon_state = new_icon_state
|
||||
|
||||
//Nations Icons
|
||||
/proc/set_nations_hud(mob/M, new_icon_state)
|
||||
if(!istype(M))
|
||||
CRASH("set_antag_hud(): [M] ([M.type]) is not a mob!")
|
||||
var/image/holder = M.hud_list[NATIONS_HUD]
|
||||
if(holder)
|
||||
holder.icon_state = new_icon_state
|
||||
if(M.mind || new_icon_state) //in mindless mobs, only null is acceptable, otherwise we're antagging a mindless mob, meaning we should runtime
|
||||
M.mind.antag_hud_icon_state = new_icon_state
|
||||
|
||||
//MIND PROCS
|
||||
//these are called by mind.transfer_to()
|
||||
/datum/mind/proc/transfer_antag_huds(datum/atom_hud/antag/newhud)
|
||||
leave_all_huds()
|
||||
set_antag_hud(current, antag_hud_icon_state)
|
||||
if(newhud)
|
||||
newhud.join_hud(current)
|
||||
|
||||
/datum/mind/proc/leave_all_huds()
|
||||
for(var/datum/atom_hud/antag/hud in huds)
|
||||
if(current in hud.hudusers)
|
||||
hud.leave_hud(current)
|
||||
|
||||
for(var/datum/atom_hud/data/hud in huds)
|
||||
if(current in hud.hudusers)
|
||||
hud.remove_hud_from(current)
|
||||
|
||||
|
||||
///Master Servent Datum Sytems,Based on TG Gang system//
|
||||
|
||||
/datum/mindslaves
|
||||
var/name = "ERROR"
|
||||
var/list/datum/mind/masters = list()
|
||||
var/list/datum/mind/serv = list()
|
||||
var/datum/atom_hud/antag/thrallhud
|
||||
var/icontype
|
||||
|
||||
/datum/mindslaves/New(loc,mastername)
|
||||
|
||||
name = mastername
|
||||
thrallhud = new()
|
||||
|
||||
/datum/mindslaves/proc/add_serv_hud(datum/mind/serv_mind, icon)
|
||||
thrallhud.join_hud(serv_mind.current, 1)
|
||||
icontype = "hud[icon]"
|
||||
set_antag_hud(serv_mind.current, icontype)
|
||||
|
||||
/datum/mindslaves/proc/leave_serv_hud(datum/mind/free_mind)
|
||||
thrallhud.leave_hud(free_mind.current)
|
||||
set_antag_hud(free_mind.current, null)
|
||||
@@ -1,138 +0,0 @@
|
||||
/obj/item/antag_spawner
|
||||
throw_speed = 1
|
||||
throw_range = 5
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
var/used = FALSE
|
||||
|
||||
/obj/item/antag_spawner/proc/spawn_antag(client/C, turf/T, type = "")
|
||||
return
|
||||
|
||||
/obj/item/antag_spawner/proc/equip_antag(mob/target)
|
||||
return
|
||||
|
||||
|
||||
/obj/item/antag_spawner/borg_tele
|
||||
name = "syndicate cyborg teleporter"
|
||||
desc = "A single-use teleporter used to deploy a Syndicate Cyborg on the field."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "locator"
|
||||
var/checking = FALSE
|
||||
var/TC_cost = 0
|
||||
var/borg_to_spawn
|
||||
var/list/possible_types = list("Assault", "Medical")
|
||||
|
||||
/obj/item/antag_spawner/borg_tele/attack_self(mob/user)
|
||||
if(used)
|
||||
to_chat(user, "<span class='warning'>[src] is out of power!</span>")
|
||||
return
|
||||
if(!(user.mind in ticker.mode.syndicates))
|
||||
to_chat(user, "<span class='danger'>AUTHENTICATION FAILURE. ACCESS DENIED.</span>")
|
||||
return FALSE
|
||||
if(checking)
|
||||
to_chat(user, "<span class='warning'>[src] is already checking for possible borgs.</span>")
|
||||
return
|
||||
borg_to_spawn = input("What type of borg would you like to teleport?", "Cyborg Type", type) as null|anything in possible_types
|
||||
if(!borg_to_spawn || checking || used)
|
||||
return
|
||||
checking = TRUE
|
||||
to_chat(user, "<span class='notice'>The device is now checking for possible borgs.</span>")
|
||||
var/list/borg_candidates = pollCandidates("Do you want to play as a Syndicate [borg_to_spawn] borg?", ROLE_OPERATIVE, 1)
|
||||
if(borg_candidates.len > 0 && !used)
|
||||
checking = FALSE
|
||||
used = TRUE
|
||||
var/mob/M = pick(borg_candidates)
|
||||
var/client/C = M.client
|
||||
spawn_antag(C, get_turf(src.loc), "syndieborg")
|
||||
else
|
||||
checking = FALSE
|
||||
to_chat(user, "<span class='notice'>Unable to connect to Syndicate command. Please wait and try again later or refund your teleporter through your uplink.</span>")
|
||||
return
|
||||
|
||||
/obj/item/antag_spawner/borg_tele/spawn_antag(client/C, turf/T, type = "")
|
||||
if(!borg_to_spawn) //If there's no type at all, let it still be used but don't do anything
|
||||
used = FALSE
|
||||
return
|
||||
var/datum/effect_system/spark_spread/S = new /datum/effect_system/spark_spread
|
||||
S.set_up(4, 1, src)
|
||||
S.start()
|
||||
var/mob/living/silicon/robot/R
|
||||
switch(borg_to_spawn)
|
||||
if("Medical")
|
||||
R = new /mob/living/silicon/robot/syndicate/medical(T)
|
||||
else
|
||||
R = new /mob/living/silicon/robot/syndicate(T) //Assault borg by default
|
||||
R.key = C.key
|
||||
ticker.mode.syndicates += R.mind
|
||||
ticker.mode.update_synd_icons_added(R.mind)
|
||||
R.mind.special_role = SPECIAL_ROLE_NUKEOPS
|
||||
R.faction = list("syndicate")
|
||||
|
||||
/obj/item/antag_spawner/slaughter_demon //Warning edgiest item in the game
|
||||
name = "vial of blood"
|
||||
desc = "A magically infused bottle of blood, distilled from countless murder victims. Used in unholy rituals to attract horrifying creatures."
|
||||
icon = 'icons/obj/wizard.dmi'
|
||||
icon_state = "vial"
|
||||
var/shatter_msg = "<span class='notice'>You shatter the bottle, no \
|
||||
turning back now!</span>"
|
||||
var/veil_msg = "<span class='warning'>You sense a dark presence lurking \
|
||||
just beyond the veil...</span>"
|
||||
var/objective_verb = "Kill"
|
||||
var/mob/living/demon_type = /mob/living/simple_animal/slaughter
|
||||
|
||||
/obj/item/antag_spawner/slaughter_demon/attack_self(mob/user)
|
||||
if(level_blocks_magic(user.z))//this is to make sure the wizard does NOT summon a demon from the Den..
|
||||
to_chat(user, "<span class='notice'>You should probably wait until you reach the station.</span>")
|
||||
return
|
||||
|
||||
if(used)
|
||||
to_chat(user, "<span class='notice'>This bottle already has a broken seal.</span>")
|
||||
return
|
||||
used = TRUE
|
||||
to_chat(user, "<span class='notice'>You break the seal on the bottle, calling upon the dire spirits of the underworld...</span>")
|
||||
|
||||
var/list/candidates = pollCandidates("Do you want to play as a slaughter demon summoned by [user.real_name]?", ROLE_DEMON, 1, 100)
|
||||
|
||||
if(candidates.len > 0)
|
||||
var/mob/C = pick(candidates)
|
||||
spawn_antag(C, get_turf(src.loc), initial(demon_type.name), user)
|
||||
to_chat(user, "[shatter_msg]")
|
||||
to_chat(user, "[veil_msg]")
|
||||
playsound(user.loc, 'sound/effects/Glassbr1.ogg', 100, 1)
|
||||
qdel(src)
|
||||
else
|
||||
used = FALSE
|
||||
to_chat(user, "<span class='notice'>The demons do not respond to your summon. Perhaps you should try again later.</span>")
|
||||
|
||||
/obj/item/antag_spawner/slaughter_demon/spawn_antag(client/C, turf/T, type = "", mob/user)
|
||||
var /obj/effect/dummy/slaughter/holder = new /obj/effect/dummy/slaughter(T)
|
||||
var/mob/living/simple_animal/slaughter/S = new demon_type(holder)
|
||||
S.vialspawned = TRUE
|
||||
S.holder = holder
|
||||
S.key = C.key
|
||||
S.mind.assigned_role = S.name
|
||||
S.mind.special_role = S.name
|
||||
ticker.mode.traitors += S.mind
|
||||
var/datum/objective/assassinate/KillDaWiz = new /datum/objective/assassinate
|
||||
KillDaWiz.owner = S.mind
|
||||
KillDaWiz.target = user.mind
|
||||
KillDaWiz.explanation_text = "[objective_verb] [user.real_name], the one who was foolish enough to summon you."
|
||||
S.mind.objectives += KillDaWiz
|
||||
var/datum/objective/KillDaCrew = new /datum/objective
|
||||
KillDaCrew.owner = S.mind
|
||||
KillDaCrew.explanation_text = "[objective_verb] everyone else while you're at it."
|
||||
S.mind.objectives += KillDaCrew
|
||||
S.mind.objectives += KillDaCrew
|
||||
to_chat(S, "<B>Objective #[1]</B>: [KillDaWiz.explanation_text]")
|
||||
to_chat(S, "<B>Objective #[2]</B>: [KillDaCrew.explanation_text]")
|
||||
|
||||
|
||||
/obj/item/antag_spawner/slaughter_demon/laughter
|
||||
name = "vial of tickles"
|
||||
desc = "A magically infused bottle of clown love, distilled from \
|
||||
countless hugging attacks. Used in funny rituals to attract \
|
||||
adorable creatures."
|
||||
color = "#FF69B4" // HOT PINK
|
||||
veil_msg = "<span class='warning'>You sense an adorable presence \
|
||||
lurking just beyond the veil...</span>"
|
||||
objective_verb = "Hug and Tickle"
|
||||
demon_type = /mob/living/simple_animal/slaughter/laughter
|
||||
@@ -1,24 +0,0 @@
|
||||
//A barebones antagonist team.
|
||||
/datum/team
|
||||
var/list/datum/mind/members = list()
|
||||
var/name = "team"
|
||||
var/member_name = "member"
|
||||
var/list/objectives = list() //common objectives, these won't be added or removed automatically, subtypes handle this, this is here for bookkeeping purposes.
|
||||
|
||||
/datum/team/New(starting_members)
|
||||
. = ..()
|
||||
if(starting_members)
|
||||
if(islist(starting_members))
|
||||
for(var/datum/mind/M in starting_members)
|
||||
add_member(M)
|
||||
else
|
||||
add_member(starting_members)
|
||||
|
||||
/datum/team/proc/is_solo()
|
||||
return members.len == 1
|
||||
|
||||
/datum/team/proc/add_member(datum/mind/new_member)
|
||||
members |= new_member
|
||||
|
||||
/datum/team/proc/remove_member(datum/mind/member)
|
||||
members -= member
|
||||
@@ -438,5 +438,3 @@ var/list/advance_cures = list(
|
||||
var/datum/symptom/S = i
|
||||
total_transmittable += S.transmittable
|
||||
return total_transmittable
|
||||
|
||||
#undef RANDOM_STARTING_LEVEL
|
||||
|
||||
@@ -48,8 +48,8 @@
|
||||
var/damage = rand(1, 5)
|
||||
if(prob(80))
|
||||
playsound(affected_mob.loc, "punch", 25, 1, -1)
|
||||
affected_mob.visible_message("<span class='danger'>[affected_mob] hits [M] with their thrashing!</span>")
|
||||
affected_mob.visible_message("<span class='danger'>[affected_mob] hits [M] with [affected_mob.p_their()] thrashing!</span>")
|
||||
M.adjustBruteLoss(damage)
|
||||
else
|
||||
playsound(affected_mob.loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
affected_mob.visible_message("<span class='danger'>[affected_mob] fails to hit [M] with their thrashing!</span>")
|
||||
affected_mob.visible_message("<span class='danger'>[affected_mob] fails to hit [M] with [affected_mob.p_their()] thrashing!</span>")
|
||||
@@ -0,0 +1,190 @@
|
||||
#define HOLOPAD_MAX_DIAL_TIME 200
|
||||
|
||||
/mob/camera/aiEye/remote/holo/setLoc()
|
||||
. = ..()
|
||||
var/obj/machinery/hologram/holopad/H = origin
|
||||
H.move_hologram(eye_user, loc)
|
||||
|
||||
//this datum manages it's own references
|
||||
|
||||
/datum/holocall
|
||||
var/mob/living/user //the one that called
|
||||
var/obj/machinery/hologram/holopad/calling_holopad //the one that sent the call
|
||||
var/obj/machinery/hologram/holopad/connected_holopad //the one that answered the call (may be null)
|
||||
var/list/dialed_holopads //all things called, will be cleared out to just connected_holopad once answered
|
||||
|
||||
var/mob/camera/aiEye/remote/holo/eye //user's eye, once connected
|
||||
var/obj/effect/overlay/holo_pad_hologram/hologram //user's hologram, once connected
|
||||
var/datum/action/innate/end_holocall/hangup //hangup action
|
||||
|
||||
var/call_start_time
|
||||
|
||||
//creates a holocall made by `caller` from `calling_pad` to `callees`
|
||||
/datum/holocall/New(mob/living/caller, obj/machinery/hologram/holopad/calling_pad, list/callees)
|
||||
call_start_time = world.time
|
||||
user = caller
|
||||
calling_pad.outgoing_call = src
|
||||
calling_holopad = calling_pad
|
||||
dialed_holopads = list()
|
||||
|
||||
for(var/I in callees)
|
||||
var/obj/machinery/hologram/holopad/H = I
|
||||
if(!QDELETED(H) && !(H.stat & NOPOWER))
|
||||
dialed_holopads += H
|
||||
var/area/area = get_area(H)
|
||||
LAZYADD(H.holo_calls, src)
|
||||
H.atom_say("[area] pad beeps: Incoming call from [caller]!")
|
||||
|
||||
if(!dialed_holopads.len)
|
||||
calling_holopad.atom_say("Connection failure.")
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
//cleans up ALL references :)
|
||||
/datum/holocall/Destroy()
|
||||
QDEL_NULL(hangup)
|
||||
|
||||
var/user_good = !QDELETED(user)
|
||||
if(user_good)
|
||||
user.reset_perspective()
|
||||
user.remote_control = null
|
||||
|
||||
if(!QDELETED(eye))
|
||||
eye.RemoveImages()
|
||||
QDEL_NULL(eye)
|
||||
|
||||
if(connected_holopad && !QDELETED(hologram))
|
||||
hologram = null
|
||||
connected_holopad.clear_holo(user)
|
||||
|
||||
user = null
|
||||
|
||||
//Hologram survived holopad destro
|
||||
if(!QDELETED(hologram))
|
||||
hologram.HC = null
|
||||
QDEL_NULL(hologram)
|
||||
|
||||
|
||||
for(var/I in dialed_holopads)
|
||||
var/obj/machinery/hologram/holopad/H = I
|
||||
LAZYREMOVE(H.holo_calls, src)
|
||||
dialed_holopads.Cut()
|
||||
|
||||
if(calling_holopad)
|
||||
calling_holopad.outgoing_call = null
|
||||
calling_holopad.SetLightsAndPower()
|
||||
calling_holopad = null
|
||||
if(connected_holopad)
|
||||
connected_holopad.SetLightsAndPower()
|
||||
connected_holopad = null
|
||||
|
||||
|
||||
return ..()
|
||||
|
||||
|
||||
//Gracefully disconnects a holopad `H` from a call. Pads not in the call are ignored. Notifies participants of the disconnection
|
||||
/datum/holocall/proc/Disconnect(obj/machinery/hologram/holopad/H)
|
||||
if(H == connected_holopad)
|
||||
var/area/A = get_area(connected_holopad)
|
||||
calling_holopad.atom_say("[A] holopad disconnected.")
|
||||
else if(H == calling_holopad && connected_holopad)
|
||||
connected_holopad.atom_say("[user] disconnected.")
|
||||
|
||||
user.unset_machine(H)
|
||||
if(istype(hangup))
|
||||
hangup.Remove(user)
|
||||
|
||||
ConnectionFailure(H, TRUE)
|
||||
|
||||
//Forcefully disconnects a holopad `H` from a call. Pads not in the call are ignored.
|
||||
/datum/holocall/proc/ConnectionFailure(obj/machinery/hologram/holopad/H, graceful = FALSE)
|
||||
if(H == connected_holopad || H == calling_holopad)
|
||||
if(!graceful && H != calling_holopad)
|
||||
calling_holopad.atom_say("Connection failure.")
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
LAZYREMOVE(H.holo_calls, src)
|
||||
dialed_holopads -= H
|
||||
if(!dialed_holopads.len)
|
||||
if(graceful)
|
||||
calling_holopad.atom_say("Call rejected.")
|
||||
qdel(src)
|
||||
|
||||
//Answers a call made to a holopad `H` which cannot be the calling holopad. Pads not in the call are ignored
|
||||
/datum/holocall/proc/Answer(obj/machinery/hologram/holopad/H)
|
||||
if(H == calling_holopad)
|
||||
return
|
||||
|
||||
if(!(H in dialed_holopads))
|
||||
return
|
||||
|
||||
if(connected_holopad)
|
||||
return
|
||||
|
||||
for(var/I in dialed_holopads)
|
||||
if(I == H)
|
||||
continue
|
||||
Disconnect(I)
|
||||
|
||||
for(var/I in H.holo_calls)
|
||||
var/datum/holocall/HC = I
|
||||
if(HC != src)
|
||||
HC.Disconnect(H)
|
||||
|
||||
connected_holopad = H
|
||||
|
||||
if(!Check())
|
||||
return
|
||||
|
||||
hologram = H.activate_holo(user)
|
||||
hologram.HC = src
|
||||
|
||||
user.unset_machine(H)
|
||||
//eyeobj code is horrid, this is the best copypasta I could make
|
||||
eye = new()
|
||||
eye.origin = H
|
||||
eye.eye_initialized = TRUE
|
||||
eye.eye_user = user
|
||||
eye.name = "Camera Eye ([user.name])"
|
||||
user.remote_control = eye
|
||||
user.remote_view = 1
|
||||
user.reset_perspective(eye)
|
||||
eye.setLoc(get_turf(H))
|
||||
|
||||
hangup = new(eye,src)
|
||||
hangup.Grant(user)
|
||||
|
||||
//Checks the validity of a holocall and qdels itself if it's not. Returns TRUE if valid, FALSE otherwise
|
||||
/datum/holocall/proc/Check()
|
||||
for(var/I in dialed_holopads)
|
||||
var/obj/machinery/hologram/holopad/H = I
|
||||
if((H.stat & NOPOWER))
|
||||
ConnectionFailure(H)
|
||||
|
||||
if(QDELETED(src))
|
||||
return FALSE
|
||||
|
||||
. = !QDELETED(user) && !user.incapacitated() && !QDELETED(calling_holopad) && !(calling_holopad.stat & NOPOWER) && user.loc == calling_holopad.loc
|
||||
|
||||
if(.)
|
||||
if(!connected_holopad)
|
||||
. = world.time < (call_start_time + HOLOPAD_MAX_DIAL_TIME)
|
||||
if(!.)
|
||||
calling_holopad.atom_say("No answer received.")
|
||||
calling_holopad.temp = ""
|
||||
|
||||
else if(!.)
|
||||
qdel(src)
|
||||
|
||||
/datum/action/innate/end_holocall
|
||||
name = "End Holocall"
|
||||
button_icon_state = "camera_off"
|
||||
var/datum/holocall/hcall
|
||||
|
||||
/datum/action/innate/end_holocall/New(Target, datum/holocall/HC)
|
||||
..()
|
||||
hcall = HC
|
||||
|
||||
/datum/action/innate/end_holocall/Activate()
|
||||
hcall.Disconnect(hcall.calling_holopad)
|
||||
@@ -0,0 +1,47 @@
|
||||
/datum/looping_sound/active_outside_ashstorm
|
||||
mid_sounds = list(
|
||||
'sound/weather/ashstorm/outside/active_mid1.ogg' = 1,
|
||||
'sound/weather/ashstorm/outside/active_mid1.ogg' = 1,
|
||||
'sound/weather/ashstorm/outside/active_mid1.ogg' = 1
|
||||
)
|
||||
mid_length = 80
|
||||
start_sound = 'sound/weather/ashstorm/outside/active_start.ogg'
|
||||
start_length = 130
|
||||
end_sound = 'sound/weather/ashstorm/outside/active_end.ogg'
|
||||
volume = 80
|
||||
|
||||
/datum/looping_sound/active_inside_ashstorm
|
||||
mid_sounds = list(
|
||||
'sound/weather/ashstorm/inside/active_mid1.ogg' = 1,
|
||||
'sound/weather/ashstorm/inside/active_mid2.ogg' = 1,
|
||||
'sound/weather/ashstorm/inside/active_mid3.ogg' = 1
|
||||
)
|
||||
mid_length = 80
|
||||
start_sound = 'sound/weather/ashstorm/inside/active_start.ogg'
|
||||
start_length = 130
|
||||
end_sound = 'sound/weather/ashstorm/inside/active_end.ogg'
|
||||
volume = 60
|
||||
|
||||
/datum/looping_sound/weak_outside_ashstorm
|
||||
mid_sounds = list(
|
||||
'sound/weather/ashstorm/outside/weak_mid1.ogg' = 1,
|
||||
'sound/weather/ashstorm/outside/weak_mid2.ogg' = 1,
|
||||
'sound/weather/ashstorm/outside/weak_mid3.ogg' = 1
|
||||
)
|
||||
mid_length = 80
|
||||
start_sound = 'sound/weather/ashstorm/outside/weak_start.ogg'
|
||||
start_length = 130
|
||||
end_sound = 'sound/weather/ashstorm/outside/weak_end.ogg'
|
||||
volume = 50
|
||||
|
||||
/datum/looping_sound/weak_inside_ashstorm
|
||||
mid_sounds = list(
|
||||
'sound/weather/ashstorm/inside/weak_mid1.ogg' = 1,
|
||||
'sound/weather/ashstorm/inside/weak_mid2.ogg' = 1,
|
||||
'sound/weather/ashstorm/inside/weak_mid3.ogg' = 1
|
||||
)
|
||||
mid_length = 80
|
||||
start_sound = 'sound/weather/ashstorm/inside/weak_start.ogg'
|
||||
start_length = 130
|
||||
end_sound = 'sound/weather/ashstorm/inside/weak_end.ogg'
|
||||
volume = 30
|
||||
+30
-51
@@ -177,7 +177,7 @@
|
||||
if(ismindshielded(H))
|
||||
text = "Mindshield Implant:<a href='?src=[UID()];implant=remove'>Remove</a>|<b><font color='green'>Implanted</font></b></br>"
|
||||
else
|
||||
text = "Mindshield Implant:<b>No Implant</b>|<a href='?src=[UID()];implant=add'>Implant him!</a></br>"
|
||||
text = "Mindshield Implant:<b>No Implant</b>|<a href='?src=[UID()];implant=add'>Implant [H.p_them()]!</a></br>"
|
||||
sections["implant"] = text
|
||||
/** REVOLUTION ***/
|
||||
text = "revolution"
|
||||
@@ -483,9 +483,11 @@
|
||||
var/new_memo = copytext(input("Write new memory", "Memory", memory) as null|message,1,MAX_MESSAGE_LEN)
|
||||
if(isnull(new_memo))
|
||||
return
|
||||
memory = new_memo
|
||||
log_admin("[key_name(usr)] has edited [key_name(current)]'s memory")
|
||||
message_admins("[key_name_admin(usr)] has edited [key_name_admin(current)]'s memory")
|
||||
var/confirmed = alert(usr, "Are you sure?", "Edit Memory", "Yes", "No")
|
||||
if(confirmed == "Yes") // Because it is too easy to accidentally wipe someone's memory
|
||||
memory = new_memo
|
||||
log_admin("[key_name(usr)] has edited [key_name(current)]'s memory")
|
||||
message_admins("[key_name_admin(usr)] has edited [key_name_admin(current)]'s memory")
|
||||
|
||||
else if(href_list["obj_edit"] || href_list["obj_add"])
|
||||
var/datum/objective/objective
|
||||
@@ -630,7 +632,7 @@
|
||||
new_objective = new /datum/objective/escape/escape_with_identity
|
||||
new_objective.owner = src
|
||||
new_objective.target = new_target
|
||||
new_objective.explanation_text = "Escape on the shuttle or an escape pod with the identity of [targ.current.real_name], the [targ.assigned_role] while wearing their identification card."
|
||||
new_objective.explanation_text = "Escape on the shuttle or an escape pod with the identity of [targ.current.real_name], the [targ.assigned_role] while wearing [targ.current.p_their()] identification card."
|
||||
if("custom")
|
||||
var/expl = sanitize(copytext(input("Custom objective:", "Objective", objective ? objective.explanation_text : "") as text|null,1,MAX_MESSAGE_LEN))
|
||||
if(!expl)
|
||||
@@ -742,7 +744,7 @@
|
||||
if(src in ticker.mode.revolutionaries)
|
||||
ticker.mode.revolutionaries -= src
|
||||
ticker.mode.update_rev_icons_removed(src)
|
||||
to_chat(current, "<span class='warning'>\red <FONT size = 3><B>You have proven your devotion to revolution! You are a head revolutionary now!</B></FONT></span>")
|
||||
to_chat(current, "<span class='userdanger'>You have proven your devotion to revolution! You are a head revolutionary now!</span>")
|
||||
else if(!(src in ticker.mode.head_revolutionaries))
|
||||
to_chat(current, "<span class='notice'>You are a member of the revolutionaries' leadership now!</span>")
|
||||
else
|
||||
@@ -1217,12 +1219,8 @@
|
||||
message_admins("[key_name_admin(usr)] has given [key_name_admin(current)] an uplink")
|
||||
|
||||
else if(href_list["obj_announce"])
|
||||
var/obj_count = 1
|
||||
to_chat(current, "<BR><span class='userdanger'>Your current objectives:</span>")
|
||||
for(var/datum/objective/objective in objectives)
|
||||
to_chat(current, "<B>Objective #[obj_count]</B>: [objective.explanation_text]")
|
||||
obj_count++
|
||||
current << 'sound/ambience/alarm4.ogg'
|
||||
announce_objectives()
|
||||
SEND_SOUND(current, sound('sound/ambience/alarm4.ogg'))
|
||||
log_admin("[key_name(usr)] has announced [key_name(current)]'s objectives")
|
||||
message_admins("[key_name_admin(usr)] has announced [key_name_admin(current)]'s objectives")
|
||||
|
||||
@@ -1282,6 +1280,14 @@
|
||||
else if(A.type == datum_type)
|
||||
return A
|
||||
|
||||
/datum/mind/proc/announce_objectives()
|
||||
var/obj_count = 1
|
||||
to_chat(current, "<span class='notice'>Your current objectives:</span>")
|
||||
for(var/objective in objectives)
|
||||
var/datum/objective/O = objective
|
||||
to_chat(current, "<B>Objective #[obj_count]</B>: [O.explanation_text]")
|
||||
obj_count++
|
||||
|
||||
/datum/mind/proc/find_syndicate_uplink()
|
||||
var/list/L = current.get_contents()
|
||||
for(var/obj/item/I in L)
|
||||
@@ -1294,7 +1300,7 @@
|
||||
if(H)
|
||||
qdel(H)
|
||||
|
||||
/datum/mind/proc/make_Tratior()
|
||||
/datum/mind/proc/make_Traitor()
|
||||
if(!(src in ticker.mode.traitors))
|
||||
ticker.mode.traitors += src
|
||||
special_role = SPECIAL_ROLE_TRAITOR
|
||||
@@ -1334,7 +1340,16 @@
|
||||
|
||||
ticker.mode.equip_syndicate(current)
|
||||
|
||||
/datum/mind/proc/make_Changling()
|
||||
/datum/mind/proc/make_Vampire()
|
||||
if(!(src in ticker.mode.vampires))
|
||||
ticker.mode.vampires += src
|
||||
ticker.mode.grant_vampire_powers(current)
|
||||
special_role = SPECIAL_ROLE_VAMPIRE
|
||||
ticker.mode.forge_vampire_objectives(src)
|
||||
ticker.mode.greet_vampire(src)
|
||||
ticker.mode.update_change_icons_added(src)
|
||||
|
||||
/datum/mind/proc/make_Changeling()
|
||||
if(!(src in ticker.mode.changelings))
|
||||
ticker.mode.changelings += src
|
||||
ticker.mode.grant_changeling_powers(current)
|
||||
@@ -1363,42 +1378,6 @@
|
||||
ticker.mode.greet_wizard(src)
|
||||
ticker.mode.update_wiz_icons_added(src)
|
||||
|
||||
|
||||
/datum/mind/proc/make_Cultist()
|
||||
if(!(src in ticker.mode.cult))
|
||||
ticker.mode.cult += src
|
||||
ticker.mode.update_cult_icons_added(src)
|
||||
special_role = SPECIAL_ROLE_CULTIST
|
||||
to_chat(current, "<font color=\"cultitalic\"><b><i>You catch a glimpse of the Realm of [ticker.cultdat.entity_name], [ticker.cultdat.entity_title2]. You now see how flimsy the world is, you see that it should be open to the knowledge of [ticker.cultdat.entity_name].</b></i></font>")
|
||||
to_chat(current, "<font color=\"cultitalic\"><b><i>Assist your new compatriots in their dark dealings. Their goal is yours, and yours is theirs. You serve the Dark One above all else. Bring It back.</b></i></font>")
|
||||
var/datum/game_mode/cult/cult = ticker.mode
|
||||
if(GAMEMODE_IS_CULT)
|
||||
cult.memorize_cult_objectives(src)
|
||||
else
|
||||
var/explanation = "Summon [ticker.cultdat.entity_name] via the use of the appropriate rune. It will only work if nine cultists stand on and around it."
|
||||
to_chat(current, "<B>Objective #1</B>: [explanation]")
|
||||
current.memory += "<B>Objective #1</B>: [explanation]<BR>"
|
||||
|
||||
|
||||
var/mob/living/carbon/human/H = current
|
||||
if(istype(H))
|
||||
var/obj/item/tome/T = new(H)
|
||||
|
||||
var/list/slots = list (
|
||||
"backpack" = slot_in_backpack,
|
||||
"left pocket" = slot_l_store,
|
||||
"right pocket" = slot_r_store,
|
||||
"left hand" = slot_l_hand,
|
||||
"right hand" = slot_r_hand,
|
||||
)
|
||||
var/where = H.equip_in_one_of_slots(T, slots)
|
||||
if(!where)
|
||||
else
|
||||
to_chat(H, "A tome, a message from your new master, appears in your [where].")
|
||||
|
||||
if(!ticker.mode.equip_cultist(current))
|
||||
to_chat(H, "Summoning an amulet from your Master failed.")
|
||||
|
||||
/datum/mind/proc/make_Rev()
|
||||
if(ticker.mode.head_revolutionaries.len>0)
|
||||
// copy targets
|
||||
@@ -1549,7 +1528,7 @@
|
||||
ticker.mode.implanter[ref] = implanters
|
||||
ticker.mode.traitors += src
|
||||
special_role = "traitor"
|
||||
to_chat(current, "<span class='warning'><B>You're now a loyal zealot of [missionary.name]!</B> You now must lay down your life to protect them and assist in their goals at any cost.</span>")
|
||||
to_chat(current, "<span class='warning'><B>You're now a loyal zealot of [missionary.name]!</B> You now must lay down your life to protect [missionary.p_them()] and assist in [missionary.p_their()] goals at any cost.</span>")
|
||||
var/datum/objective/protect/mindslave/MS = new
|
||||
MS.owner = src
|
||||
MS.target = missionary.mind
|
||||
|
||||
@@ -171,10 +171,13 @@ var/list/spells = typesof(/obj/effect/proc_holder/spell) //needed for the badmin
|
||||
/obj/effect/proc_holder/spell/proc/invocation(mob/user = usr) //spelling the spell out and setting it on recharge/reducing charges amount
|
||||
switch(invocation_type)
|
||||
if("shout")
|
||||
if(prob(50))//Auto-mute? Fuck that noise
|
||||
user.say(invocation)
|
||||
if(!user.IsVocal())
|
||||
user.emote("makes frantic gestures!")
|
||||
else
|
||||
user.say(replacetext(invocation," ","`"))
|
||||
if(prob(50))//Auto-mute? Fuck that noise
|
||||
user.say(invocation)
|
||||
else
|
||||
user.say(replacetext(invocation," ","`"))
|
||||
if("whisper")
|
||||
if(prob(50))
|
||||
user.whisper(invocation)
|
||||
|
||||
@@ -10,6 +10,6 @@
|
||||
/obj/effect/proc_holder/spell/targeted/emplosion/cast(list/targets, mob/user = usr)
|
||||
|
||||
for(var/mob/living/target in targets)
|
||||
empulse(target.loc, emp_heavy, emp_light)
|
||||
empulse(target.loc, emp_heavy, emp_light, 1)
|
||||
|
||||
return
|
||||
return
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
if(!usr.mind.miming)
|
||||
to_chat(usr, "<span class='notice'>You must dedicate yourself to silence first.</span>")
|
||||
return
|
||||
invocation = "<B>[usr.real_name]</B> looks as if a wall is in front of them."
|
||||
invocation = "<B>[usr.real_name]</B> looks as if a wall is in front of [usr.p_them()]."
|
||||
else
|
||||
invocation_type ="none"
|
||||
..()
|
||||
|
||||
@@ -32,7 +32,7 @@ Also, you never added distance checking after target is selected. I've went ahea
|
||||
return
|
||||
|
||||
if(!target.key || !target.mind)
|
||||
to_chat(user, "They appear to be catatonic. Not even magic can affect their vacant mind.")
|
||||
to_chat(user, "[target.p_they(TRUE)] appear[target.p_s()] to be catatonic. Not even magic can affect [target.p_their()] vacant mind.")
|
||||
return
|
||||
|
||||
if(user.suiciding)
|
||||
|
||||
@@ -100,6 +100,7 @@ var/list/uplink_items = list()
|
||||
if(I)
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/A = user
|
||||
log_game("[key_name(user)] purchased [I.name]")
|
||||
A.put_in_any_hand_if_possible(I)
|
||||
|
||||
if(istype(I,/obj/item/storage/box/) && I.contents.len>0)
|
||||
@@ -1337,7 +1338,7 @@ var/list/uplink_items = list()
|
||||
|
||||
/datum/uplink_item/badass/rapid
|
||||
name = "Gloves of the North Star"
|
||||
desc = "These gloves let the user punch people very fast. Does not improve weapon attack speed."
|
||||
desc = "These gloves let the user punch people very fast. Does not improve weapon attack speed or the meaty fists of a hulk."
|
||||
reference = "RPGD"
|
||||
item = /obj/item/clothing/gloves/fingerless/rapid
|
||||
cost = 8
|
||||
@@ -1424,6 +1425,7 @@ var/list/uplink_items = list()
|
||||
for(var/category in temp_uplink_list)
|
||||
buyable_items += temp_uplink_list[category]
|
||||
var/list/bought_items = list()
|
||||
var/list/itemlog = list()
|
||||
U.uses -= cost
|
||||
U.used_TC = 20
|
||||
var/remaining_TC = 50
|
||||
@@ -1439,8 +1441,10 @@ var/list/uplink_items = list()
|
||||
continue
|
||||
bought_items += I.item
|
||||
remaining_TC -= I.cost
|
||||
itemlog += I.name // To make the name more readable for the log compared to just i.item
|
||||
|
||||
U.purchase_log += "<BIG>[bicon(C)]</BIG>"
|
||||
for(var/item in bought_items)
|
||||
new item(C)
|
||||
U.purchase_log += "<BIG>[bicon(item)]</BIG>"
|
||||
log_game("[key_name(usr)] purchased a surplus crate with [jointext(itemlog, ", ")]")
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
//The effects of weather occur across an entire z-level. For instance, lavaland has periodic ash storms that scorch most unprotected creatures.
|
||||
|
||||
#define STARTUP_STAGE 1
|
||||
#define MAIN_STAGE 2
|
||||
#define WIND_DOWN_STAGE 3
|
||||
#define END_STAGE 4
|
||||
|
||||
/datum/weather
|
||||
var/name = "space wind"
|
||||
var/desc = "Heavy gusts of wind blanket the area, periodically knocking down anyone caught in the open."
|
||||
@@ -29,24 +24,25 @@
|
||||
|
||||
var/area_type = /area/space //Types of area to affect
|
||||
var/list/impacted_areas = list() //Areas to be affected by the weather, calculated when the weather begins
|
||||
var/target_z = MAIN_STATION //The z-level to affect
|
||||
var/list/protected_areas = list()//Areas that are protected and excluded from the affected areas.
|
||||
var/impacted_z_levels // The list of z-levels that this weather is actively affecting
|
||||
|
||||
var/overlay_layer = 10 //Since it's above everything else, this is the layer used by default. 2 is below mobs and walls if you need to use that.
|
||||
var/overlay_layer = AREA_LAYER //Since it's above everything else, this is the layer used by default. TURF_LAYER is below mobs and walls if you need to use that.
|
||||
var/aesthetic = FALSE //If the weather has no purpose other than looks
|
||||
var/immunity_type = "storm" //Used by mobs to prevent them from being affected by the weather
|
||||
|
||||
var/stage = END_STAGE //The stage of the weather, from 1-4
|
||||
|
||||
var/probability = FALSE //Percent chance to happen if there are other possible weathers on the z-level
|
||||
// These are read by the weather subsystem and used to determine when and where to run the weather.
|
||||
var/probability = 0 // Weight amongst other eligible weather. If zero, will never happen randomly.
|
||||
var/target_trait = STATION_LEVEL // The z-level trait to affect when run randomly or when not overridden.
|
||||
|
||||
/datum/weather/New()
|
||||
var/barometer_predictable = FALSE
|
||||
var/next_hit_time = 0 //For barometers to know when the next storm will hit
|
||||
|
||||
/datum/weather/New(z_levels)
|
||||
..()
|
||||
weather_master.existing_weather |= src
|
||||
|
||||
/datum/weather/Destroy()
|
||||
weather_master.existing_weather -= src
|
||||
return ..()
|
||||
impacted_z_levels = z_levels
|
||||
|
||||
/datum/weather/proc/telegraph()
|
||||
if(stage == STARTUP_STAGE)
|
||||
@@ -59,17 +55,18 @@
|
||||
affectareas -= get_areas(V)
|
||||
for(var/V in affectareas)
|
||||
var/area/A = V
|
||||
if(is_on_level_name(A,target_z))
|
||||
if(A.z in impacted_z_levels)
|
||||
impacted_areas |= A
|
||||
weather_duration = rand(weather_duration_lower, weather_duration_upper)
|
||||
START_PROCESSING(SSweather, src)
|
||||
update_areas()
|
||||
for(var/V in player_list)
|
||||
var/mob/M = V
|
||||
if(is_on_level_name(M,target_z))
|
||||
for(var/M in player_list)
|
||||
var/turf/mob_turf = get_turf(M)
|
||||
if(mob_turf && (mob_turf.z in impacted_z_levels))
|
||||
if(telegraph_message)
|
||||
to_chat(M, telegraph_message)
|
||||
if(telegraph_sound)
|
||||
M << sound(telegraph_sound)
|
||||
SEND_SOUND(M, sound(telegraph_sound))
|
||||
addtimer(CALLBACK(src, .proc/start), telegraph_duration)
|
||||
|
||||
/datum/weather/proc/start()
|
||||
@@ -77,14 +74,13 @@
|
||||
return
|
||||
stage = MAIN_STAGE
|
||||
update_areas()
|
||||
for(var/V in player_list)
|
||||
var/mob/M = V
|
||||
if(is_on_level_name(M,target_z))
|
||||
for(var/M in player_list)
|
||||
var/turf/mob_turf = get_turf(M)
|
||||
if(mob_turf && (mob_turf.z in impacted_z_levels))
|
||||
if(weather_message)
|
||||
to_chat(M, weather_message)
|
||||
if(weather_sound)
|
||||
M << sound(weather_sound)
|
||||
weather_master.processing_weather |= src
|
||||
SEND_SOUND(M, sound(weather_sound))
|
||||
addtimer(CALLBACK(src, .proc/wind_down), weather_duration)
|
||||
|
||||
/datum/weather/proc/wind_down()
|
||||
@@ -92,24 +88,25 @@
|
||||
return
|
||||
stage = WIND_DOWN_STAGE
|
||||
update_areas()
|
||||
for(var/V in player_list)
|
||||
var/mob/M = V
|
||||
if(is_on_level_name(M,target_z))
|
||||
for(var/M in player_list)
|
||||
var/turf/mob_turf = get_turf(M)
|
||||
if(mob_turf && (mob_turf.z in impacted_z_levels))
|
||||
if(end_message)
|
||||
to_chat(M, end_message)
|
||||
if(end_sound)
|
||||
M << sound(end_sound)
|
||||
weather_master.processing_weather -= src
|
||||
SEND_SOUND(M, sound(end_sound))
|
||||
addtimer(CALLBACK(src, .proc/end), end_duration)
|
||||
|
||||
/datum/weather/proc/end()
|
||||
if(stage == END_STAGE)
|
||||
return
|
||||
return 1
|
||||
stage = END_STAGE
|
||||
STOP_PROCESSING(SSweather, src)
|
||||
update_areas()
|
||||
|
||||
/datum/weather/proc/can_impact(mob/living/L) //Can this weather impact a mob?
|
||||
if(!is_on_level_name(L,target_z))
|
||||
/datum/weather/proc/can_weather_act(mob/living/L) //Can this weather impact a mob?
|
||||
var/turf/mob_turf = get_turf(L)
|
||||
if(mob_turf && !(mob_turf.z in impacted_z_levels))
|
||||
return
|
||||
if(immunity_type in L.weather_immunities)
|
||||
return
|
||||
@@ -117,7 +114,7 @@
|
||||
return
|
||||
return 1
|
||||
|
||||
/datum/weather/proc/impact(mob/living/L) //What effect does this weather have on the hapless mob?
|
||||
/datum/weather/proc/weather_act(mob/living/L) //What effect does this weather have on the hapless mob?
|
||||
return
|
||||
|
||||
/datum/weather/proc/update_areas()
|
||||
@@ -136,8 +133,8 @@
|
||||
N.icon_state = end_overlay
|
||||
if(END_STAGE)
|
||||
N.color = null
|
||||
N.icon_state = initial(N.icon_state)
|
||||
N.icon_state = ""
|
||||
N.icon = 'icons/turf/areas.dmi'
|
||||
N.layer = 10 //Just default back to normal area stuff since I assume setting a var is faster than initial
|
||||
N.layer = AREA_LAYER //Just default back to normal area stuff since I assume setting a var is faster than initial
|
||||
N.invisibility = INVISIBILITY_MAXIMUM
|
||||
N.opacity = 0
|
||||
N.set_opacity(FALSE)
|
||||
|
||||
@@ -1,118 +0,0 @@
|
||||
//Different types of weather.
|
||||
|
||||
/datum/weather/floor_is_lava //The Floor is Lava: Makes all turfs damage anyone on them unless they're standing on a solid object.
|
||||
name = "the floor is lava"
|
||||
desc = "The ground turns into surprisingly cool lava, lightly damaging anything on the floor."
|
||||
|
||||
telegraph_message = "<span class='warning'>Waves of heat emanate from the ground...</span>"
|
||||
telegraph_duration = 150
|
||||
|
||||
weather_message = "<span class='userdanger'>The floor is lava! Get on top of something!</span>"
|
||||
weather_duration_lower = 300
|
||||
weather_duration_upper = 600
|
||||
weather_overlay = "lava"
|
||||
|
||||
end_message = "<span class='danger'>The ground cools and returns to its usual form.</span>"
|
||||
end_duration = 0
|
||||
|
||||
area_type = /area
|
||||
target_z = MAIN_STATION
|
||||
|
||||
overlay_layer = 2 //Covers floors only
|
||||
immunity_type = "lava"
|
||||
|
||||
/datum/weather/floor_is_lava/impact(mob/living/L)
|
||||
for(var/obj/structure/O in L.loc)
|
||||
if(O.density)
|
||||
return
|
||||
if(L.loc.density)
|
||||
return
|
||||
if(!L.client) //Only sentient people are going along with it!
|
||||
return
|
||||
L.adjustFireLoss(3)
|
||||
|
||||
/datum/weather/floor_is_lava/fake
|
||||
name = "fake lava"
|
||||
aesthetic = TRUE
|
||||
|
||||
/datum/weather/advanced_darkness //Advanced Darkness: Restricts the vision of all affected mobs to a single tile in the cardinal directions.
|
||||
name = "advanced darkness"
|
||||
desc = "Everything in the area is effectively blinded, unable to see more than a foot or so around itself."
|
||||
|
||||
telegraph_message = "<span class='warning'>The lights begin to dim... is the power going out?</span>"
|
||||
telegraph_duration = 150
|
||||
|
||||
weather_message = "<span class='userdanger'>This isn't your everyday darkness... this is <i>advanced</i> darkness!</span>"
|
||||
weather_duration_lower = 300
|
||||
weather_duration_upper = 300
|
||||
|
||||
end_message = "<span class='danger'>At last, the darkness recedes.</span>"
|
||||
end_duration = 0
|
||||
|
||||
area_type = /area
|
||||
target_z = MAIN_STATION
|
||||
|
||||
/datum/weather/advanced_darkness/update_areas()
|
||||
for(var/V in impacted_areas)
|
||||
var/area/A = V
|
||||
if(stage == MAIN_STAGE)
|
||||
A.invisibility = 0
|
||||
A.opacity = 1
|
||||
A.layer = overlay_layer
|
||||
A.icon = 'icons/effects/weather_effects.dmi'
|
||||
A.icon_state = "darkness"
|
||||
else
|
||||
A.invisibility = INVISIBILITY_MAXIMUM
|
||||
A.opacity = 0
|
||||
|
||||
|
||||
/datum/weather/ash_storm //Ash Storms: Common happenings on lavaland. Heavily obscures vision and deals heavy fire damage to anyone caught outside.
|
||||
name = "ash storm"
|
||||
desc = "An intense atmospheric storm lifts ash off of the planet's surface and billows it down across the area, dealing intense fire damage to the unprotected."
|
||||
|
||||
telegraph_message = "<span class='boldwarning'>An eerie moan rises on the wind. Sheets of burning ash blacken the horizon. Seek shelter.</span>"
|
||||
telegraph_duration = 300
|
||||
telegraph_sound = 'sound/lavaland/ash_storm_windup.ogg'
|
||||
telegraph_overlay = "light_ash"
|
||||
|
||||
weather_message = "<span class='userdanger'><i>Smoldering clouds of scorching ash billow down around you! Get inside!</i></span>"
|
||||
weather_duration_lower = 600
|
||||
weather_duration_upper = 1500
|
||||
weather_sound = 'sound/lavaland/ash_storm_start.ogg'
|
||||
weather_overlay = "ash_storm"
|
||||
|
||||
end_message = "<span class='boldannounce'>The shrieking wind whips away the last of the ash falls to its usual murmur. It should be safe to go outside now.</span>"
|
||||
end_duration = 300
|
||||
end_sound = 'sound/lavaland/ash_storm_end.ogg'
|
||||
end_overlay = "light_ash"
|
||||
|
||||
area_type = /area/mine/dangerous
|
||||
target_z = MINING
|
||||
|
||||
immunity_type = "ash"
|
||||
|
||||
probability = 90
|
||||
|
||||
/datum/weather/ash_storm/impact(mob/living/L)
|
||||
if(istype(L.loc, /obj/mecha))
|
||||
return
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
var/thermal_protection = H.get_thermal_protection()
|
||||
if(thermal_protection >= FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT)
|
||||
return
|
||||
L.adjustFireLoss(4)
|
||||
|
||||
/datum/weather/ash_storm/emberfall //Emberfall: An ash storm passes by, resulting in harmless embers falling like snow. 10% to happen in place of an ash storm.
|
||||
name = "emberfall"
|
||||
desc = "A passing ash storm blankets the area in harmless embers."
|
||||
|
||||
weather_message = "<span class='notice'>Gentle embers waft down around you like grotesque snow. The storm seems to have passed you by...</span>"
|
||||
weather_sound = 'sound/lavaland/ash_storm_windup.ogg'
|
||||
weather_overlay = "light_ash"
|
||||
|
||||
end_message = "<span class='notice'>The emberfall slows, stops. Another layer of hardened soot to the basalt beneath your feet.</span>"
|
||||
|
||||
aesthetic = TRUE
|
||||
|
||||
probability = 10
|
||||
@@ -0,0 +1,108 @@
|
||||
//Ash storms happen frequently on lavaland. They heavily obscure vision, and cause high fire damage to anyone caught outside.
|
||||
/datum/weather/ash_storm
|
||||
name = "ash storm"
|
||||
desc = "An intense atmospheric storm lifts ash off of the planet's surface and billows it down across the area, dealing intense fire damage to the unprotected."
|
||||
|
||||
telegraph_message = "<span class='boldwarning'>An eerie moan rises on the wind. Sheets of burning ash blacken the horizon. Seek shelter.</span>"
|
||||
telegraph_duration = 300
|
||||
telegraph_overlay = "light_ash"
|
||||
|
||||
weather_message = "<span class='userdanger'><i>Smoldering clouds of scorching ash billow down around you! Get inside!</i></span>"
|
||||
weather_duration_lower = 600
|
||||
weather_duration_upper = 1200
|
||||
weather_overlay = "ash_storm"
|
||||
|
||||
end_message = "<span class='boldannounce'>The shrieking wind whips away the last of the ash and falls to its usual murmur. It should be safe to go outside now.</span>"
|
||||
end_duration = 300
|
||||
end_overlay = "light_ash"
|
||||
|
||||
area_type = /area/mine/dangerous // /area/lavaland/surface/outdoors
|
||||
target_trait = ORE_LEVEL
|
||||
|
||||
immunity_type = "ash"
|
||||
|
||||
// probability = 90
|
||||
|
||||
barometer_predictable = TRUE
|
||||
|
||||
var/datum/looping_sound/active_outside_ashstorm/sound_ao = new(list(), FALSE, TRUE)
|
||||
var/datum/looping_sound/active_inside_ashstorm/sound_ai = new(list(), FALSE, TRUE)
|
||||
var/datum/looping_sound/weak_outside_ashstorm/sound_wo = new(list(), FALSE, TRUE)
|
||||
var/datum/looping_sound/weak_inside_ashstorm/sound_wi = new(list(), FALSE, TRUE)
|
||||
|
||||
/datum/weather/ash_storm/telegraph()
|
||||
. = ..()
|
||||
var/list/inside_areas = list()
|
||||
var/list/outside_areas = list()
|
||||
var/list/eligible_areas = list()
|
||||
for(var/z in impacted_z_levels)
|
||||
eligible_areas += space_manager.areas_in_z["[z]"]
|
||||
for(var/i in 1 to eligible_areas.len)
|
||||
var/area/place = eligible_areas[i]
|
||||
if(place.outdoors)
|
||||
outside_areas += place
|
||||
else
|
||||
inside_areas += place
|
||||
CHECK_TICK
|
||||
|
||||
sound_ao.output_atoms = outside_areas
|
||||
sound_ai.output_atoms = inside_areas
|
||||
sound_wo.output_atoms = outside_areas
|
||||
sound_wi.output_atoms = inside_areas
|
||||
|
||||
sound_wo.start()
|
||||
sound_wi.start()
|
||||
|
||||
/datum/weather/ash_storm/start()
|
||||
. = ..()
|
||||
sound_wo.stop()
|
||||
sound_wi.stop()
|
||||
|
||||
sound_ao.start()
|
||||
sound_ai.start()
|
||||
|
||||
/datum/weather/ash_storm/wind_down()
|
||||
. = ..()
|
||||
sound_ao.stop()
|
||||
sound_ai.stop()
|
||||
|
||||
sound_wo.start()
|
||||
sound_wi.start()
|
||||
|
||||
/datum/weather/ash_storm/end()
|
||||
. = ..()
|
||||
sound_wo.stop()
|
||||
sound_wi.stop()
|
||||
|
||||
/datum/weather/ash_storm/proc/is_ash_immune(atom/L)
|
||||
while(L && !isturf(L))
|
||||
if(ismecha(L)) //Mechs are immune
|
||||
return TRUE
|
||||
if(ishuman(L)) //Are you immune?
|
||||
var/mob/living/carbon/human/H = L
|
||||
var/thermal_protection = H.get_thermal_protection()
|
||||
if(thermal_protection >= FIRE_IMMUNITY_SUIT_MAX_TEMP_PROTECT)
|
||||
return TRUE
|
||||
L = L.loc //Matryoshka check
|
||||
return FALSE //RIP you
|
||||
|
||||
/datum/weather/ash_storm/weather_act(mob/living/L)
|
||||
if(is_ash_immune(L))
|
||||
return
|
||||
L.adjustFireLoss(4)
|
||||
|
||||
|
||||
//Emberfalls are the result of an ash storm passing by close to the playable area of lavaland. They have a 10% chance to trigger in place of an ash storm.
|
||||
/datum/weather/ash_storm/emberfall
|
||||
name = "emberfall"
|
||||
desc = "A passing ash storm blankets the area in harmless embers."
|
||||
|
||||
weather_message = "<span class='notice'>Gentle embers waft down around you like grotesque snow. The storm seems to have passed you by...</span>"
|
||||
weather_overlay = "light_ash"
|
||||
|
||||
end_message = "<span class='notice'>The emberfall slows, stops. Another layer of hardened soot to the basalt beneath your feet.</span>"
|
||||
end_sound = null
|
||||
|
||||
aesthetic = TRUE
|
||||
|
||||
// probability = 10
|
||||
@@ -0,0 +1,39 @@
|
||||
//Causes fire damage to anyone not standing on a dense object.
|
||||
/datum/weather/floor_is_lava
|
||||
name = "the floor is lava"
|
||||
desc = "The ground turns into surprisingly cool lava, lightly damaging anything on the floor."
|
||||
|
||||
telegraph_message = "<span class='warning'>You feel the ground beneath you getting hot. Waves of heat distort the air.</span>"
|
||||
telegraph_duration = 150
|
||||
|
||||
weather_message = "<span class='userdanger'>The floor is lava! Get on top of something!</span>"
|
||||
weather_duration_lower = 300
|
||||
weather_duration_upper = 600
|
||||
weather_overlay = "lava"
|
||||
|
||||
end_message = "<span class='danger'>The ground cools and returns to its usual form.</span>"
|
||||
end_duration = 0
|
||||
|
||||
area_type = /area
|
||||
protected_areas = list(/area/space)
|
||||
target_trait = STATION_LEVEL
|
||||
|
||||
overlay_layer = ABOVE_OPEN_TURF_LAYER //Covers floors only
|
||||
immunity_type = "lava"
|
||||
|
||||
|
||||
/datum/weather/floor_is_lava/weather_act(mob/living/L)
|
||||
if(issilicon(L))
|
||||
return
|
||||
for(var/obj/structure/O in L.loc)
|
||||
if(O.density || O.buckled_mob && istype(O, /obj/structure/stool/bed))
|
||||
return
|
||||
if(L.loc.density)
|
||||
return
|
||||
if(!L.client) //Only sentient people are going along with it!
|
||||
return
|
||||
L.adjustFireLoss(3)
|
||||
|
||||
/datum/weather/floor_is_lava/fake
|
||||
name = "the floor is lava (fake)"
|
||||
aesthetic = TRUE
|
||||
@@ -0,0 +1,61 @@
|
||||
//Radiation storms occur when the station passes through an irradiated area, and irradiate anyone not standing in protected areas (maintenance, emergency storage, etc.)
|
||||
/datum/weather/rad_storm
|
||||
name = "radiation storm"
|
||||
desc = "A cloud of intense radiation passes through the area dealing rad damage to those who are unprotected."
|
||||
|
||||
telegraph_duration = 400
|
||||
telegraph_message = "<span class='danger'>The air begins to grow warm.</span>"
|
||||
|
||||
weather_message = "<span class='userdanger'><i>You feel waves of heat wash over you! Find shelter!</i></span>"
|
||||
weather_overlay = "ash_storm"
|
||||
weather_duration_lower = 600
|
||||
weather_duration_upper = 1500
|
||||
weather_color = "green"
|
||||
weather_sound = 'sound/misc/bloblarm.ogg'
|
||||
|
||||
end_duration = 100
|
||||
end_message = "<span class='notice'>The air seems to be cooling off again.</span>"
|
||||
|
||||
area_type = /area
|
||||
protected_areas = list(/area/maintenance, /area/turret_protected/ai_upload, /area/turret_protected/ai_upload_foyer,
|
||||
/area/turret_protected/ai, /area/storage/emergency, /area/storage/emergency2, /area/crew_quarters/sleep, /area/security/brig, /area/shuttle)
|
||||
target_trait = STATION_LEVEL
|
||||
|
||||
immunity_type = "rad"
|
||||
|
||||
/datum/weather/rad_storm/telegraph()
|
||||
..()
|
||||
status_alarm(TRUE)
|
||||
make_maint_all_access()
|
||||
|
||||
|
||||
/datum/weather/rad_storm/weather_act(mob/living/L)
|
||||
var/resist = L.getarmor(null, "rad")
|
||||
if(prob(40))
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
if(!(RADIMMUNE in H.species.species_traits))
|
||||
if(prob(max(0, 100 - resist)))
|
||||
randmuti(H) // Applies bad mutation
|
||||
if(prob(50))
|
||||
if(prob(90))
|
||||
randmutb(H)
|
||||
else
|
||||
randmutg(H)
|
||||
domutcheck(H, null, 1)
|
||||
|
||||
L.apply_effect(20, IRRADIATE, resist)
|
||||
|
||||
/datum/weather/rad_storm/end()
|
||||
if(..())
|
||||
return
|
||||
priority_announcement.Announce("The radiation threat has passed. Please return to your workplaces.", "Anomaly Alert")
|
||||
status_alarm(FALSE)
|
||||
revoke_maint_all_access()
|
||||
|
||||
/datum/weather/rad_storm/proc/status_alarm(active) //Makes the status displays show the radiation warning for those who missed the announcement.
|
||||
if(active)
|
||||
post_status("alert", "radiation")
|
||||
else
|
||||
post_status("blank")
|
||||
post_status("shuttle")
|
||||
@@ -0,0 +1,28 @@
|
||||
/datum/weather/snow_storm
|
||||
name = "snow storm"
|
||||
desc = "Harsh snowstorms roam the topside of this arctic planet, burying any area unfortunate enough to be in its path."
|
||||
// probability = 90
|
||||
|
||||
telegraph_message = "<span class='warning'>Drifting particles of snow begin to dust the surrounding area..</span>"
|
||||
telegraph_duration = 300
|
||||
telegraph_overlay = "light_snow"
|
||||
|
||||
weather_message = "<span class='userdanger'><i>Harsh winds pick up as dense snow begins to fall from the sky! Seek shelter!</i></span>"
|
||||
weather_overlay = "snow_storm"
|
||||
weather_duration_lower = 600
|
||||
weather_duration_upper = 1500
|
||||
|
||||
end_duration = 100
|
||||
end_message = "<span class='boldannounce'>The snowfall dies down, it should be safe to go outside again.</span>"
|
||||
|
||||
// area_type = /area/awaymission/snowdin/outside
|
||||
target_trait = AWAY_LEVEL
|
||||
|
||||
immunity_type = "snow"
|
||||
|
||||
barometer_predictable = TRUE
|
||||
|
||||
|
||||
/datum/weather/snow_storm/weather_act(mob/living/L)
|
||||
L.adjust_bodytemperature(-rand(5, 15))
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
#define CAT_HIDDEN 2 // Also in code/game/machinery/vending.dm
|
||||
|
||||
/datum/wires/vending
|
||||
holder_type = /obj/machinery/vending
|
||||
wire_count = 4
|
||||
|
||||
Reference in New Issue
Block a user