refactors clickcatchers/parallax/fullsceren (#15460)

* :)

* that

* move those there

* refactor that too

* wew

* stuff

* almost.

* sigh

* just need speed

* stuf

* pain

* hm

* tweaks

* that

* eh

* wack

* a

* done

* that's important

* wacky

* all that

* fixes

* typo

* that

* a

* funny

* that

* that

* woo

* help im losing my fucking mind

* okay

* fix
This commit is contained in:
silicons
2022-02-07 15:17:24 -08:00
committed by GitHub
parent e089be0de9
commit af6e43c595
84 changed files with 1323 additions and 889 deletions
@@ -0,0 +1,182 @@
GLOBAL_LIST_EMPTY(active_alternate_appearances)
/atom
var/list/alternate_appearances
/atom/proc/remove_alt_appearance(key)
if(alternate_appearances)
for(var/K in alternate_appearances)
var/datum/atom_hud/alternate_appearance/AA = alternate_appearances[K]
if(AA.appearance_key == key)
AA.remove_from_hud(src)
break
/atom/proc/add_alt_appearance(type, key, ...)
if(!type || !key)
return
if(alternate_appearances && alternate_appearances[key])
return
var/list/arguments = args.Copy(2)
new type(arglist(arguments))
/datum/atom_hud/alternate_appearance
var/appearance_key
/datum/atom_hud/alternate_appearance/New(key)
..()
GLOB.active_alternate_appearances += src
appearance_key = key
/datum/atom_hud/alternate_appearance/Destroy()
GLOB.active_alternate_appearances -= src
return ..()
/datum/atom_hud/alternate_appearance/proc/onNewMob(mob/M)
if(mobShouldSee(M))
add_hud_to(M)
/datum/atom_hud/alternate_appearance/proc/mobShouldSee(mob/M)
return FALSE
/datum/atom_hud/alternate_appearance/add_to_hud(atom/A, image/I)
. = ..()
if(.)
LAZYINITLIST(A.alternate_appearances)
A.alternate_appearances[appearance_key] = src
/datum/atom_hud/alternate_appearance/remove_from_hud(atom/A)
. = ..()
if(.)
LAZYREMOVE(A.alternate_appearances, appearance_key)
//an alternate appearance that attaches a single image to a single atom
/datum/atom_hud/alternate_appearance/basic
var/atom/target
var/image/theImage
var/add_ghost_version = FALSE
var/ghost_appearance
/datum/atom_hud/alternate_appearance/basic/New(key, image/I, target_sees_appearance = TRUE)
..()
theImage = I
target = I.loc
hud_icons = list(appearance_key)
add_to_hud(target, I)
if(target_sees_appearance && ismob(target))
add_hud_to(target)
if(add_ghost_version)
var/image/ghost_image = image(icon = I.icon , icon_state = I.icon_state, loc = I.loc)
ghost_image.override = FALSE
ghost_image.alpha = 128
ghost_appearance = new /datum/atom_hud/alternate_appearance/basic/observers(key + "_observer", ghost_image, FALSE)
/datum/atom_hud/alternate_appearance/basic/Destroy()
. = ..()
if(ghost_appearance)
QDEL_NULL(ghost_appearance)
/datum/atom_hud/alternate_appearance/basic/add_to_hud(atom/A)
LAZYINITLIST(A.hud_list)
A.hud_list[appearance_key] = theImage
. = ..()
/datum/atom_hud/alternate_appearance/basic/remove_from_hud(atom/A)
. = ..()
A.hud_list -= appearance_key
if(. && !QDELETED(src))
qdel(src)
/datum/atom_hud/alternate_appearance/basic/everyone
add_ghost_version = TRUE
/datum/atom_hud/alternate_appearance/basic/everyone/New()
..()
for(var/mob in GLOB.mob_list)
if(mobShouldSee(mob))
add_hud_to(mob)
/datum/atom_hud/alternate_appearance/basic/everyone/mobShouldSee(mob/M)
return !isobserver(M)
/datum/atom_hud/alternate_appearance/basic/silicons
/datum/atom_hud/alternate_appearance/basic/silicons/New()
..()
for(var/mob in GLOB.silicon_mobs)
if(mobShouldSee(mob))
add_hud_to(mob)
/datum/atom_hud/alternate_appearance/basic/silicons/mobShouldSee(mob/M)
if(issilicon(M))
return TRUE
return FALSE
/datum/atom_hud/alternate_appearance/basic/observers
add_ghost_version = FALSE //just in case, to prevent infinite loops
/datum/atom_hud/alternate_appearance/basic/observers/New()
..()
for(var/mob in GLOB.dead_mob_list)
if(mobShouldSee(mob))
add_hud_to(mob)
/datum/atom_hud/alternate_appearance/basic/observers/mobShouldSee(mob/M)
return isobserver(M)
/datum/atom_hud/alternate_appearance/basic/noncult
/datum/atom_hud/alternate_appearance/basic/noncult/New()
..()
for(var/mob in GLOB.player_list)
if(mobShouldSee(mob))
add_hud_to(mob)
/datum/atom_hud/alternate_appearance/basic/noncult/mobShouldSee(mob/M)
if(!iscultist(M))
return TRUE
return FALSE
/datum/atom_hud/alternate_appearance/basic/cult
/datum/atom_hud/alternate_appearance/basic/cult/New()
..()
for(var/mob in GLOB.player_list)
if(mobShouldSee(mob))
add_hud_to(mob)
/datum/atom_hud/alternate_appearance/basic/cult/mobShouldSee(mob/M)
if(iscultist(M))
return TRUE
return FALSE
/datum/atom_hud/alternate_appearance/basic/blessedAware
/datum/atom_hud/alternate_appearance/basic/blessedAware/New()
..()
for(var/mob in GLOB.mob_list)
if(mobShouldSee(mob))
add_hud_to(mob)
/datum/atom_hud/alternate_appearance/basic/blessedAware/mobShouldSee(mob/M)
if(M.mind && (M.mind.assigned_role == "Chaplain"))
return TRUE
if (istype(M, /mob/living/simple_animal/hostile/construct/wraith))
return TRUE
if(isrevenant(M) || iseminence(M) || iswizard(M))
return TRUE
return FALSE
/datum/atom_hud/alternate_appearance/basic/onePerson
var/mob/seer
/datum/atom_hud/alternate_appearance/basic/onePerson/mobShouldSee(mob/M)
if(M == seer)
return TRUE
return FALSE
/datum/atom_hud/alternate_appearance/basic/onePerson/New(key, image/I, mob/living/M)
..(key, I, FALSE)
seer = M
add_hud_to(seer)
+126
View File
@@ -0,0 +1,126 @@
/* HUD DATUMS */
GLOBAL_LIST_EMPTY(all_huds)
//GLOBAL HUD LIST
GLOBAL_LIST_INIT(huds, list(
DATA_HUD_SECURITY_BASIC = new/datum/atom_hud/data/human/security/basic(),
DATA_HUD_SECURITY_ADVANCED = new/datum/atom_hud/data/human/security/advanced(),
DATA_HUD_MEDICAL_BASIC = new/datum/atom_hud/data/human/medical/basic(),
DATA_HUD_MEDICAL_ADVANCED = new/datum/atom_hud/data/human/medical/advanced(),
DATA_HUD_DIAGNOSTIC_BASIC = new/datum/atom_hud/data/diagnostic/basic(),
DATA_HUD_DIAGNOSTIC_ADVANCED = new/datum/atom_hud/data/diagnostic/advanced(),
DATA_HUD_ABDUCTOR = new/datum/atom_hud/abductor(),
DATA_HUD_SENTIENT_DISEASE = new/datum/atom_hud/sentient_disease(),
DATA_HUD_AI_DETECT = new/datum/atom_hud/ai_detector(),
ANTAG_HUD_CULT = new/datum/atom_hud/antag(),
ANTAG_HUD_REV = new/datum/atom_hud/antag(),
ANTAG_HUD_OPS = new/datum/atom_hud/antag(),
ANTAG_HUD_WIZ = new/datum/atom_hud/antag(),
ANTAG_HUD_SHADOW = new/datum/atom_hud/antag(),
ANTAG_HUD_TRAITOR = new/datum/atom_hud/antag/hidden(),
ANTAG_HUD_NINJA = new/datum/atom_hud/antag/hidden(),
ANTAG_HUD_CHANGELING = new/datum/atom_hud/antag/hidden(),
ANTAG_HUD_ABDUCTOR = new/datum/atom_hud/antag/hidden(),
ANTAG_HUD_DEVIL = new/datum/atom_hud/antag(),
ANTAG_HUD_SINTOUCHED = new/datum/atom_hud/antag/hidden(),
ANTAG_HUD_SOULLESS = new/datum/atom_hud/antag/hidden(),
ANTAG_HUD_CLOCKWORK = new/datum/atom_hud/antag(),
ANTAG_HUD_BROTHER = new/datum/atom_hud/antag/hidden(),
ANTAG_HUD_BLOODSUCKER = new/datum/atom_hud/antag/bloodsucker(),
ANTAG_HUD_FUGITIVE = new/datum/atom_hud/antag(),
ANTAG_HUD_HERETIC = new/datum/atom_hud/antag/hidden()
))
/datum/atom_hud
var/list/atom/hudatoms = list() //list of all atoms which display this hud
var/list/hudusers = list() //list with all mobs who can see the hud
var/list/hud_icons = list() //these will be the indexes for the atom's hud_list
var/list/next_time_allowed = list() //mobs associated with the next time this hud can be added to them
var/list/queued_to_see = list() //mobs that have triggered the cooldown and are queued to see the hud, but do not yet
/datum/atom_hud/New()
GLOB.all_huds += src
/datum/atom_hud/Destroy()
for(var/v in hudusers)
remove_hud_from(v)
for(var/v in hudatoms)
remove_from_hud(v)
GLOB.all_huds -= src
return ..()
/datum/atom_hud/proc/remove_hud_from(mob/M)
if(!M || !hudusers[M])
return
if (!--hudusers[M])
hudusers -= M
if(queued_to_see[M])
queued_to_see -= M
else
for(var/atom/A in hudatoms)
remove_from_single_hud(M, A)
/datum/atom_hud/proc/remove_from_hud(atom/A)
if(!A)
return FALSE
for(var/mob/M in hudusers)
remove_from_single_hud(M, A)
hudatoms -= A
return TRUE
/datum/atom_hud/proc/remove_from_single_hud(mob/M, atom/A) //unsafe, no sanity apart from client
if(!M || !M.client || !A)
return
for(var/i in hud_icons)
M.client.images -= A.hud_list[i]
/datum/atom_hud/proc/add_hud_to(mob/M)
if(!M)
return
if(!hudusers[M])
hudusers[M] = 1
if(next_time_allowed[M] > world.time)
if(!queued_to_see[M])
addtimer(CALLBACK(src, .proc/show_hud_images_after_cooldown, M), next_time_allowed[M] - world.time)
queued_to_see[M] = TRUE
else
next_time_allowed[M] = world.time + ADD_HUD_TO_COOLDOWN
for(var/atom/A in hudatoms)
add_to_single_hud(M, A)
else
hudusers[M]++
/datum/atom_hud/proc/show_hud_images_after_cooldown(M)
if(queued_to_see[M])
queued_to_see -= M
next_time_allowed[M] = world.time + ADD_HUD_TO_COOLDOWN
for(var/atom/A in hudatoms)
add_to_single_hud(M, A)
/datum/atom_hud/proc/add_to_hud(atom/A)
if(!A)
return FALSE
hudatoms |= A
for(var/mob/M in hudusers)
if(!queued_to_see[M])
add_to_single_hud(M, A)
return TRUE
/datum/atom_hud/proc/add_to_single_hud(mob/M, atom/A) //unsafe, no sanity apart from client
if(!M || !M.client || !A)
return
for(var/i in hud_icons)
if(A.hud_list[i])
M.client.images |= A.hud_list[i]
//MOB PROCS
/mob/proc/reload_huds()
for(var/datum/atom_hud/hud in GLOB.all_huds)
if(hud && hud.hudusers[src])
for(var/atom/A in hud.hudatoms)
hud.add_to_single_hud(src, A)
/mob/dead/new_player/reload_huds()
return
+523
View File
@@ -0,0 +1,523 @@
/*
* Data HUDs have been rewritten in a more generic way.
* In short, they now use an observer-listener pattern.
* See code/datum/hud.dm for the generic hud datum.
* Update the HUD icons when needed with the appropriate hook. (see below)
*/
/* DATA HUD DATUMS */
/atom/proc/add_to_all_human_data_huds()
for(var/datum/atom_hud/data/human/hud in GLOB.huds)
hud.add_to_hud(src)
/atom/proc/remove_from_all_data_huds()
for(var/datum/atom_hud/data/hud in GLOB.huds)
hud.remove_from_hud(src)
/datum/atom_hud/data
/datum/atom_hud/data/human/medical
hud_icons = list(STATUS_HUD, HEALTH_HUD, NANITE_HUD, RAD_HUD)
/datum/atom_hud/data/human/medical/basic
/datum/atom_hud/data/human/medical/basic/proc/check_sensors(mob/living/carbon/human/H)
if(!istype(H))
return 0
var/obj/item/clothing/under/U = H.w_uniform
if(!istype(U))
return 0
if(U.sensor_mode <= SENSOR_VITALS)
return 0
return 1
/datum/atom_hud/data/human/medical/basic/add_to_single_hud(mob/M, mob/living/carbon/H)
if(check_sensors(H))
..()
/datum/atom_hud/data/human/medical/basic/proc/update_suit_sensors(mob/living/carbon/H)
check_sensors(H) ? add_to_hud(H) : remove_from_hud(H)
/datum/atom_hud/data/human/medical/advanced
/datum/atom_hud/data/human/security
/datum/atom_hud/data/human/security/basic
hud_icons = list(ID_HUD)
/datum/atom_hud/data/human/security/advanced
hud_icons = list(ID_HUD, IMPTRACK_HUD, IMPLOYAL_HUD, IMPCHEM_HUD, WANTED_HUD, NANITE_HUD)
/datum/atom_hud/data/diagnostic
/datum/atom_hud/data/diagnostic/basic
hud_icons = list(DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_CIRCUIT_HUD, DIAG_TRACK_HUD, DIAG_AIRLOCK_HUD, DIAG_NANITE_FULL_HUD)
/datum/atom_hud/data/diagnostic/advanced
hud_icons = list(DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_CIRCUIT_HUD, DIAG_TRACK_HUD, DIAG_AIRLOCK_HUD, DIAG_NANITE_FULL_HUD, DIAG_PATH_HUD)
/datum/atom_hud/data/bot_path
hud_icons = list(DIAG_PATH_HUD)
/datum/atom_hud/abductor
hud_icons = list(GLAND_HUD)
/datum/atom_hud/sentient_disease
hud_icons = list(SENTIENT_DISEASE_HUD)
/datum/atom_hud/ai_detector
hud_icons = list(AI_DETECT_HUD)
/datum/atom_hud/ai_detector/add_hud_to(mob/M)
..()
if(M && (hudusers.len == 1))
for(var/V in GLOB.aiEyes)
var/mob/camera/aiEye/E = V
E.update_ai_detect_hud()
/* MED/SEC/DIAG HUD HOOKS */
/*
* THESE HOOKS SHOULD BE CALLED BY THE MOB SHOWING THE HUD
*/
/***********************************************
Medical HUD! Basic mode needs suit sensors on.
************************************************/
//HELPERS
//called when a carbon changes virus
/mob/living/carbon/proc/check_virus()
var/threat
for(var/thing in diseases)
var/datum/disease/D = thing
if(!(D.visibility_flags & HIDDEN_SCANNER))
if(!threat || D.severity > threat) //a buffing virus gets an icon
threat = D.severity
return threat
//helper for getting the appropriate health status
/proc/RoundHealth(mob/living/M)
if(M.stat == DEAD || (HAS_TRAIT(M, TRAIT_FAKEDEATH)))
return "health-100" //what's our health? it doesn't matter, we're dead, or faking
var/maxi_health = M.maxHealth
if(iscarbon(M) && M.health < 0)
maxi_health = 100 //so crit shows up right for aliens and other high-health carbon mobs; noncarbons don't have crit.
var/resulthealth = (M.health / maxi_health) * 100
switch(resulthealth)
if(100 to INFINITY)
return "health100"
if(90.625 to 100)
return "health93.75"
if(84.375 to 90.625)
return "health87.5"
if(78.125 to 84.375)
return "health81.25"
if(71.875 to 78.125)
return "health75"
if(65.625 to 71.875)
return "health68.75"
if(59.375 to 65.625)
return "health62.5"
if(53.125 to 59.375)
return "health56.25"
if(46.875 to 53.125)
return "health50"
if(40.625 to 46.875)
return "health43.75"
if(34.375 to 40.625)
return "health37.5"
if(28.125 to 34.375)
return "health31.25"
if(21.875 to 28.125)
return "health25"
if(15.625 to 21.875)
return "health18.75"
if(9.375 to 15.625)
return "health12.5"
if(1 to 9.375)
return "health6.25"
if(-50 to 1)
return "health0"
if(-85 to -50)
return "health-50"
if(-99 to -85)
return "health-85"
else
return "health-100"
//HOOKS
//called when a human changes suit sensors
/mob/living/carbon/proc/update_suit_sensors()
var/datum/atom_hud/data/human/medical/basic/B = GLOB.huds[DATA_HUD_MEDICAL_BASIC]
B.update_suit_sensors(src)
//called when a living mob changes health
/mob/living/proc/med_hud_set_health()
var/image/holder = hud_list[HEALTH_HUD]
holder.icon_state = "hud[RoundHealth(src)]"
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
med_hud_set_radstatus()
//for carbon suit sensors
/mob/living/carbon/med_hud_set_health()
..()
//called when a carbon changes stat, virus or XENO_HOST
/mob/living/proc/med_hud_set_status()
var/image/holder = hud_list[STATUS_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH)))
holder.icon_state = "huddead"
else
holder.icon_state = "hudhealthy"
/mob/living/carbon/med_hud_set_status()
var/image/holder = hud_list[STATUS_HUD]
var/icon/I = icon(icon, icon_state, dir)
var/virus_threat = check_virus()
holder.pixel_y = I.Height() - world.icon_size
if(HAS_TRAIT(src, TRAIT_XENO_HOST))
holder.icon_state = "hudxeno"
else if(stat == DEAD || (HAS_TRAIT(src, TRAIT_FAKEDEATH)))
if(tod)
var/tdelta = round(world.time - timeofdeath)
if(tdelta < (DEFIB_TIME_LIMIT * 10))
var/obj/item/organ/heart/He = getorgan(/obj/item/organ/heart)
if(He)
holder.icon_state = "huddefib"
if(He.organ_flags & ORGAN_FAILING)
holder.icon_state = "huddefibheart"
else
holder.icon_state = "huddefibheart"
return
holder.icon_state = "huddead"
else
switch(virus_threat)
if(DISEASE_SEVERITY_BIOHAZARD)
holder.icon_state = "hudill5"
if(DISEASE_SEVERITY_DANGEROUS)
holder.icon_state = "hudill4"
if(DISEASE_SEVERITY_HARMFUL)
holder.icon_state = "hudill3"
if(DISEASE_SEVERITY_MEDIUM)
holder.icon_state = "hudill2"
if(DISEASE_SEVERITY_MINOR)
holder.icon_state = "hudill1"
if(DISEASE_SEVERITY_NONTHREAT)
holder.icon_state = "hudill0"
if(DISEASE_SEVERITY_POSITIVE)
holder.icon_state = "hudbuff"
if(null)
holder.icon_state = "hudhealthy"
/mob/living/proc/med_hud_set_radstatus()
var/image/radholder = hud_list[RAD_HUD]
var/icon/I = icon(icon, icon_state, dir)
radholder.pixel_y = I.Height() - world.icon_size
var/mob/living/M = src
var/rads = M.radiation
switch(rads)
if(-INFINITY to RAD_MOB_SAFE)
radholder.icon_state = "hudradsafe"
if((RAD_MOB_SAFE+1) to RAD_MOB_MUTATE)
radholder.icon_state = "hudraddanger"
if((RAD_MOB_MUTATE+1) to RAD_MOB_VOMIT)
radholder.icon_state = "hudradlethal"
if((RAD_MOB_VOMIT+1) to INFINITY)
radholder.icon_state = "hudradnuke"
/***********************************************
Security HUDs! Basic mode shows only the job.
************************************************/
//HOOKS
/mob/living/carbon/human/proc/sec_hud_set_ID()
var/image/holder = hud_list[ID_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
holder.icon_state = "hudno_id"
if(wear_id?.GetID())
holder.icon_state = "hud[ckey(wear_id.GetJobName())]"
sec_hud_set_security_status()
/mob/living/proc/sec_hud_set_implants()
var/image/holder
for(var/i in list(IMPTRACK_HUD, IMPLOYAL_HUD, IMPCHEM_HUD))
holder = hud_list[i]
holder.icon_state = null
for(var/obj/item/implant/I in implants)
if(istype(I, /obj/item/implant/tracking))
holder = hud_list[IMPTRACK_HUD]
var/icon/IC = icon(icon, icon_state, dir)
holder.pixel_y = IC.Height() - world.icon_size
holder.icon_state = "hud_imp_tracking"
else if(istype(I, /obj/item/implant/chem))
holder = hud_list[IMPCHEM_HUD]
var/icon/IC = icon(icon, icon_state, dir)
holder.pixel_y = IC.Height() - world.icon_size
holder.icon_state = "hud_imp_chem"
if(HAS_TRAIT(src, TRAIT_MINDSHIELD))
holder = hud_list[IMPLOYAL_HUD]
var/icon/IC = icon(icon, icon_state, dir)
holder.pixel_y = IC.Height() - world.icon_size
holder.icon_state = "hud_imp_loyal"
/mob/living/carbon/human/proc/sec_hud_set_security_status()
var/image/holder = hud_list[WANTED_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
var/perpname = get_face_name(get_id_name(""))
if(perpname && GLOB.data_core)
var/datum/data/record/R = find_record("name", perpname, GLOB.data_core.security)
if(R)
switch(R.fields["criminal"])
if("*Arrest*")
holder.icon_state = "hudwanted"
return
if("Incarcerated")
holder.icon_state = "hudincarcerated"
return
if("Paroled")
holder.icon_state = "hudparolled"
return
if("Discharged")
holder.icon_state = "huddischarged"
return
holder.icon_state = null
/***********************************************
Diagnostic HUDs!
************************************************/
/mob/living/proc/hud_set_nanite_indicator()
var/image/holder = hud_list[NANITE_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
holder.icon_state = null
if(src in SSnanites.nanite_monitored_mobs)
holder.icon_state = "nanite_ping"
//For Diag health and cell bars!
/proc/RoundDiagBar(value)
switch(value * 100)
if(95 to INFINITY)
return "max"
if(80 to 100)
return "good"
if(60 to 80)
return "high"
if(40 to 60)
return "med"
if(20 to 40)
return "low"
if(1 to 20)
return "crit"
else
return "dead"
//Sillycone hooks
/mob/living/silicon/proc/diag_hud_set_health()
var/image/holder = hud_list[DIAG_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
if(stat == DEAD)
holder.icon_state = "huddiagdead"
else
holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]"
/mob/living/silicon/proc/diag_hud_set_status()
var/image/holder = hud_list[DIAG_STAT_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
switch(stat)
if(CONSCIOUS)
holder.icon_state = "hudstat"
if(UNCONSCIOUS)
holder.icon_state = "hudoffline"
else
holder.icon_state = "huddead2"
//Borgie battery tracking!
/mob/living/silicon/robot/proc/diag_hud_set_borgcell()
var/image/holder = hud_list[DIAG_BATT_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
if(cell)
var/chargelvl = (cell.charge/cell.maxcharge)
holder.icon_state = "hudbatt[RoundDiagBar(chargelvl)]"
else
holder.icon_state = "hudnobatt"
//borg-AI shell tracking
/mob/living/silicon/robot/proc/diag_hud_set_aishell() //Shows tracking beacons on the mech
var/image/holder = hud_list[DIAG_TRACK_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
if(!shell) //Not an AI shell
holder.icon_state = null
else if(deployed) //AI shell in use by an AI
holder.icon_state = "hudtrackingai"
else //Empty AI shell
holder.icon_state = "hudtracking"
//AI side tracking of AI shell control
/mob/living/silicon/ai/proc/diag_hud_set_deployed() //Shows tracking beacons on the mech
var/image/holder = hud_list[DIAG_TRACK_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
if(!deployed_shell)
holder.icon_state = null
else //AI is currently controlling a shell
holder.icon_state = "hudtrackingai"
/*~~~~~~~~~~~~~~~~~~~~
BIG STOMPY MECHS
~~~~~~~~~~~~~~~~~~~~~*/
/obj/mecha/proc/diag_hud_set_mechhealth()
var/image/holder = hud_list[DIAG_MECH_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
holder.icon_state = "huddiag[RoundDiagBar(obj_integrity/max_integrity)]"
/obj/mecha/proc/diag_hud_set_mechcell()
var/image/holder = hud_list[DIAG_BATT_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
if(cell)
var/chargelvl = cell.charge/cell.maxcharge
holder.icon_state = "hudbatt[RoundDiagBar(chargelvl)]"
else
holder.icon_state = "hudnobatt"
/obj/mecha/proc/diag_hud_set_mechstat()
var/image/holder = hud_list[DIAG_STAT_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
holder.icon_state = null
if(internal_damage)
holder.icon_state = "hudwarn"
/obj/mecha/proc/diag_hud_set_mechtracking() //Shows tracking beacons on the mech
var/image/holder = hud_list[DIAG_TRACK_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
var/new_icon_state //This var exists so that the holder's icon state is set only once in the event of multiple mech beacons.
for(var/obj/item/mecha_parts/mecha_tracking/T in trackers)
if(T.ai_beacon) //Beacon with AI uplink
new_icon_state = "hudtrackingai"
break //Immediately terminate upon finding an AI beacon to ensure it is always shown over the normal one, as mechs can have several trackers.
else
new_icon_state = "hudtracking"
holder.icon_state = new_icon_state
/*~~~~~~~~~
Bots!
~~~~~~~~~~*/
/mob/living/simple_animal/bot/proc/diag_hud_set_bothealth()
var/image/holder = hud_list[DIAG_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
holder.icon_state = "huddiag[RoundDiagBar(health/maxHealth)]"
/mob/living/simple_animal/bot/proc/diag_hud_set_botstat() //On (With wireless on or off), Off, EMP'ed
var/image/holder = hud_list[DIAG_STAT_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
if(on)
holder.icon_state = "hudstat"
else if(stat) //Generally EMP causes this
holder.icon_state = "hudoffline"
else //Bot is off
holder.icon_state = "huddead2"
/mob/living/simple_animal/bot/proc/diag_hud_set_botmode() //Shows a bot's current operation
var/image/holder = hud_list[DIAG_BOT_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
if(client) //If the bot is player controlled, it will not be following mode logic!
holder.icon_state = "hudsentient"
return
switch(mode)
if(BOT_SUMMON, BOT_RESPONDING) //Responding to PDA or AI summons
holder.icon_state = "hudcalled"
if(BOT_CLEANING, BOT_REPAIRING, BOT_HEALING) //Cleanbot cleaning, Floorbot fixing, or Medibot Healing
holder.icon_state = "hudworking"
if(BOT_PATROL, BOT_START_PATROL) //Patrol mode
holder.icon_state = "hudpatrol"
if(BOT_PREP_ARREST, BOT_ARREST, BOT_HUNT) //STOP RIGHT THERE, CRIMINAL SCUM!
holder.icon_state = "hudalert"
if(BOT_MOVING, BOT_DELIVER, BOT_GO_HOME, BOT_NAV) //Moving to target for normal bots, moving to deliver or go home for MULES.
holder.icon_state = "hudmove"
else
holder.icon_state = ""
/*~~~~~~~~~~~~
Circutry!
~~~~~~~~~~~~~*/
/obj/item/electronic_assembly/proc/diag_hud_set_circuithealth(hide = FALSE)
var/image/holder = hud_list[DIAG_CIRCUIT_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
if((!isturf(loc))||hide) //if not on the ground dont show overlay
holder.icon_state = null
else
holder.icon_state = "huddiag[RoundDiagBar(obj_integrity/max_integrity)]"
/obj/item/electronic_assembly/proc/diag_hud_set_circuitcell(hide = FALSE)
var/image/holder = hud_list[DIAG_BATT_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
if((!isturf(loc))||hide) //if not on the ground dont show overlay
holder.icon_state = null
else if(battery)
var/chargelvl = battery.charge/battery.maxcharge
holder.icon_state = "hudbatt[RoundDiagBar(chargelvl)]"
else
holder.icon_state = "hudnobatt"
/obj/item/electronic_assembly/proc/diag_hud_set_circuitstat(hide = FALSE) //On, On and dangerous, or Off
var/image/holder = hud_list[DIAG_STAT_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
if((!isturf(loc))||hide) //if not on the ground don't show overlay
holder.icon_state = null
else if(!battery)
holder.icon_state = "hudoffline"
else if(battery.charge == 0)
holder.icon_state = "hudoffline"
else if(combat_circuits) //has a circuit that can harm people
holder.icon_state = prefered_hud_icon + "-red"
else //Bot is on and not dangerous
holder.icon_state = prefered_hud_icon
/obj/item/electronic_assembly/proc/diag_hud_set_circuittracking(hide = FALSE)
var/image/holder = hud_list[DIAG_TRACK_HUD]
var/icon/I = icon(icon, icon_state, dir)
holder.pixel_y = I.Height() - world.icon_size
if((!isturf(loc))||hide) //if not on the ground dont show overlay
holder.icon_state = null
else if(long_range_circuits)
holder.icon_state = "hudtracking"
else
holder.icon_state = null
/*~~~~~~~~~~~~
Airlocks!
~~~~~~~~~~~~~*/
/obj/machinery/door/airlock/proc/diag_hud_set_electrified()
var/image/holder = hud_list[DIAG_AIRLOCK_HUD]
if(secondsElectrified != 0)
holder.icon_state = "electrified"
else
holder.icon_state = ""
@@ -0,0 +1,74 @@
/atom/movable/screen/click_catcher
icon = 'icons/screen/clickcatcher.dmi'
icon_state = "catcher"
appearance_flags = TILE_BOUND | NO_CLIENT_COLOR | RESET_TRANSFORM | RESET_COLOR | RESET_ALPHA
plane = CLICKCATCHER_PLANE
plane = CLICKCATCHER_PLANE
mouse_opacity = MOUSE_OPACITY_OPAQUE
screen_loc = "CENTER"
/*
#define MAX_SAFE_BYOND_ICON_SCALE_TILES (MAX_SAFE_BYOND_ICON_SCALE_PX / world.icon_size)
#define MAX_SAFE_BYOND_ICON_SCALE_PX (33 * 32) //Not using world.icon_size on purpose.
/atom/movable/screen/click_catcher/proc/UpdateFill(view_size_x = 15, view_size_y = 15)
var/icon/newicon = icon('icons/mob/screen_gen.dmi', "catcher")
var/ox = min(MAX_SAFE_BYOND_ICON_SCALE_TILES, view_size_x)
var/oy = min(MAX_SAFE_BYOND_ICON_SCALE_TILES, view_size_y)
var/px = view_size_x * world.icon_size
var/py = view_size_y * world.icon_size
var/sx = min(MAX_SAFE_BYOND_ICON_SCALE_PX, px)
var/sy = min(MAX_SAFE_BYOND_ICON_SCALE_PX, py)
newicon.Scale(sx, sy)
icon = newicon
screen_loc = "CENTER-[(ox-1)*0.5],CENTER-[(oy-1)*0.5]"
var/matrix/M = new
M.Scale(px/sx, py/sy)
transform = M
#undef MAX_SAFE_BYOND_ICON_SCALE_TILES
#undef MAX_SAFE_BYOND_ICON_SCALE_PX
*/
/atom/movable/screen/click_catcher/proc/UpdateFill(view_size_x, view_size_y)
screen_loc = "1,1 to [view_size_x],[view_size_y]"
/atom/movable/screen/click_catcher/Click(location, control, params)
var/list/modifiers = params2list(params)
if(modifiers["middle"] && iscarbon(usr))
var/mob/living/carbon/C = usr
C.swap_hand()
else
var/turf/T = Parse(modifiers["screen-loc"], get_turf(usr.client?.eye || usr), usr.client)
params += "&catcher=1"
if(T)
T.Click(location, control, params)
return TRUE
/atom/movable/screen/click_catcher/proc/Parse(scr_loc, turf/origin, client/C)
// screen-loc: Pixel coordinates in screen_loc format ("[tile_x]:[pixel_x],[tile_y]:[pixel_y]")
if(!scr_loc)
return null
var/tX = splittext(scr_loc, ",")
var/tY = splittext(tX[2], ":")
var/tZ = origin.z
tY = tY[1]
tX = splittext(tX[1], ":")
tX = tX[1]
var/list/actual_view = getviewsize(C ? C.view : world.view)
tX = clamp(origin.x + text2num(tX) - round(actual_view[1] / 2) - 1, 1, world.maxx)
tY = clamp(origin.y + text2num(tY) - round(actual_view[2] / 2) - 1, 1, world.maxy)
return locate(tX, tY, tZ)
/**
* Makes a clickcatcher if necessary, and ensures it's fit to our size.
*/
/client/proc/update_clickcatcher(list/view_override)
if(!click_catcher)
click_catcher = new
screen |= click_catcher
if(view_override)
click_catcher.UpdateFill(view_override[1], view_override[2])
else
var/list/view_list = getviewsize(view)
click_catcher.UpdateFill(view_list[1], view_list[2])
+234
View File
@@ -0,0 +1,234 @@
/**
* Adds a fullscreen overlay
*
* @params
* - category - string - must exist. will overwrite any other screen in this category. defaults to type.
* - type - the typepath of the screen
* - severity - severity - different screen objects have differing severities
*/
/mob/proc/overlay_fullscreen(category, type, severity)
ASSERT(type)
if(!category)
category = type
var/atom/movable/screen/fullscreen/screen = fullscreens[category]
if (!screen || screen.type != type)
// needs to be recreated
clear_fullscreen(category, 0)
fullscreens[category] = screen = new type
screen.SetSeverity(severity)
if(client && screen.ShouldShow(src))
screen.SetView(client.view)
client.screen += screen
return screen
/**
* Wipes a fullscreen of a certain category
*
* Second argument is for animation delay.
*/
/mob/proc/clear_fullscreen(category, animated = 10)
if(!fullscreens)
return
var/atom/movable/screen/fullscreen/screen = fullscreens[category]
fullscreens -= category
if(!screen)
return
if(animated > 0)
animate(screen, alpha = 0, time = animated)
addtimer(CALLBACK(src, .proc/_remove_fullscreen_direct, screen), animated, TIMER_CLIENT_TIME)
else
if(client)
client.screen -= screen
qdel(screen)
/mob/proc/_remove_fullscreen_direct(atom/movable/screen/fullscreen/screen)
if(client)
client.screen -= screen
qdel(screen)
/**
* Wipes all fullscreens
*/
/mob/proc/wipe_fullscreens()
for(var/category in fullscreens)
clear_fullscreen(category)
/**
* Removes fullscreens from client but not the mob
*/
/mob/proc/hide_fullscreens()
if(client)
for(var/category in fullscreens)
client.screen -= fullscreens[category]
/**
* Ensures all fullscreens are on client.
*/
/mob/proc/reload_fullscreen()
if(client)
var/atom/movable/screen/fullscreen/screen
for(var/category in fullscreens)
screen = fullscreens[category]
if(screen.ShouldShow(src))
screen.SetView(client.view)
client.screen |= screen
else
client.screen -= screen
/atom/movable/screen/fullscreen
icon = 'icons/screen/fullscreen_15x15.dmi'
icon_state = "default"
screen_loc = "CENTER-7,CENTER-7"
layer = FULLSCREEN_LAYER
plane = FULLSCREEN_PLANE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
/// current view we're adapted to
var/view_current
/// min severity
var/severity_min = 0
/// max severity
var/severity_max = INFINITY
/// current severity
var/severity = 0
/// show this while dead
var/show_when_dead = FALSE
/atom/movable/screen/fullscreen/proc/SetSeverity(severity)
src.severity = clamp(severity, severity_min, severity_max)
icon_state = "[initial(icon_state)][severity]"
/atom/movable/screen/fullscreen/proc/SetView(client_view)
view_current = client_view
/atom/movable/screen/fullscreen/proc/ShouldShow(mob/M)
if(!show_when_dead && M.stat == DEAD)
return FALSE
return TRUE
/atom/movable/screen/fullscreen/Destroy()
SetSeverity(0)
return ..()
/atom/movable/screen/fullscreen/scaled
icon = 'icons/screen/fullscreen_15x15.dmi'
screen_loc = "CENTER-7,CENTER-7"
/// size of sprite in tiles
var/size_x = 15
/// size of sprite in tiles
var/size_y = 15
/atom/movable/screen/fullscreen/scaled/SetView(client_view)
if(view_current != client_view)
var/list/actualview = getviewsize(client_view)
view_current = client_view
transform = matrix(actualview[1] / size_x, 0, 0, 0, actualview[2] / size_y, 0)
return ..()
/atom/movable/screen/fullscreen/scaled/brute
icon_state = "brutedamageoverlay"
layer = UI_DAMAGE_LAYER
plane = FULLSCREEN_PLANE
/atom/movable/screen/fullscreen/scaled/oxy
icon_state = "oxydamageoverlay"
layer = UI_DAMAGE_LAYER
plane = FULLSCREEN_PLANE
/atom/movable/screen/fullscreen/scaled/crit
icon_state = "passage"
layer = CRIT_LAYER
plane = FULLSCREEN_PLANE
/atom/movable/screen/fullscreen/scaled/crit/vision
icon_state = "oxydamageoverlay"
layer = BLIND_LAYER
/atom/movable/screen/fullscreen/scaled/blind
icon_state = "blackimageoverlay"
layer = BLIND_LAYER
plane = FULLSCREEN_PLANE
/atom/movable/screen/fullscreen/scaled/curse
icon_state = "curse"
layer = CURSE_LAYER
plane = FULLSCREEN_PLANE
/atom/movable/screen/fullscreen/scaled/impaired
icon_state = "impairedoverlay"
/atom/movable/screen/fullscreen/scaled/emergency_meeting
icon_state = "emergency_meeting"
show_when_dead = TRUE
layer = CURSE_LAYER
plane = SPLASHSCREEN_PLANE
/atom/movable/screen/fullscreen/tiled/blurry
icon = 'icons/mob/screen_gen.dmi'
screen_loc = "WEST,SOUTH to EAST,NORTH"
icon_state = "cloudy"
/atom/movable/screen/fullscreen/tiled/flash
icon = 'icons/mob/screen_gen.dmi'
screen_loc = "WEST,SOUTH to EAST,NORTH"
icon_state = "flash"
/atom/movable/screen/fullscreen/tiled/flash/static
icon = 'icons/mob/screen_gen.dmi'
screen_loc = "WEST,SOUTH to EAST,NORTH"
icon_state = "noise"
/atom/movable/screen/fullscreen/tiled/high
icon = 'icons/mob/screen_gen.dmi'
screen_loc = "WEST,SOUTH to EAST,NORTH"
icon_state = "druggy"
/atom/movable/screen/fullscreen/tiled/color_vision
icon = 'icons/mob/screen_gen.dmi'
screen_loc = "WEST,SOUTH to EAST,NORTH"
icon_state = "flash"
alpha = 80
/atom/movable/screen/fullscreen/tiled/color_vision/green
color = "#00ff00"
/atom/movable/screen/fullscreen/tiled/color_vision/red
color = "#ff0000"
/atom/movable/screen/fullscreen/tiled/color_vision/blue
color = "#0000ff"
/atom/movable/screen/fullscreen/tiled/cinematic_backdrop
icon = 'icons/mob/screen_gen.dmi'
screen_loc = "WEST,SOUTH to EAST,NORTH"
icon_state = "flash"
plane = SPLASHSCREEN_PLANE
layer = SPLASHSCREEN_LAYER - 1
color = "#000000"
show_when_dead = TRUE
/atom/movable/screen/fullscreen/special/lighting_backdrop
icon = 'icons/mob/screen_gen.dmi'
icon_state = "flash"
transform = matrix(200, 0, 0, 0, 200, 0)
plane = LIGHTING_PLANE
blend_mode = BLEND_OVERLAY
show_when_dead = TRUE
//Provides darkness to the back of the lighting plane
/atom/movable/screen/fullscreen/special/lighting_backdrop/lit
invisibility = INVISIBILITY_LIGHTING
layer = BACKGROUND_LAYER+21
color = "#000"
show_when_dead = TRUE
//Provides whiteness in case you don't see lights so everything is still visible
/atom/movable/screen/fullscreen/special/lighting_backdrop/unlit
layer = BACKGROUND_LAYER+20
show_when_dead = TRUE
/atom/movable/screen/fullscreen/special/see_through_darkness
icon_state = "nightvision"
plane = LIGHTING_PLANE
layer = LIGHTING_LAYER
blend_mode = BLEND_ADD
show_when_dead = TRUE
+21
View File
@@ -0,0 +1,21 @@
/**
* initializes screen rendering. call on mob new
*/
/mob/proc/init_rendering()
/**
* loads screen rendering. call on mob login
*/
/mob/proc/reload_rendering()
if(!client.parallax_holder)
client.CreateParallax()
else
client.parallax_holder.Reset(force = TRUE)
client.update_clickcatcher()
reload_fullscreen()
/**
* destroys screen rendering. call on mob del
*/
/mob/proc/dispose_rendering()
wipe_fullscreens()
+28
View File
@@ -0,0 +1,28 @@
/**
* Holds parallax information.
*/
/datum/parallax
/// List of parallax objects - these are cloned to a parallax holder using Clone on each.
var/list/atom/movable/screen/parallax_layer/objects
/// Parallax layers
var/layers = 0
/datum/parallax/New(create_objects = TRUE)
if(create_objects)
objects = CreateObjects()
layers = objects.len
/datum/parallax/Destroy()
QDEL_LIST(objects)
return ..()
/**
* Gets a new version of the objects inside - used when applying to a holder.
*/
/datum/parallax/proc/GetObjects()
. = list()
for(var/atom/movable/screen/parallax_layer/layer in objects)
. += layer.Clone()
/datum/parallax/proc/CreateObjects()
. = objects = list()
+311
View File
@@ -0,0 +1,311 @@
/**
* # Parallax holders
*
* Holds all the information about a client's parallax
*
* Not on mob because parallax is area based, not mob based.
*
* How parallax works:
* - Layers - normal layers, scroll with movement to relative position, can scroll
* - Absolute - absolute layers, scroll with movement to absolute position, cannot scroll
* - Vis - vis_contents-like model - things in this are directly applied and get no processing whatsoever. Things like overmap ships can use this.
*/
/datum/parallax_holder
/// Client that owns us
var/client/owner
/// The parallax object we're currently rendering
var/datum/parallax/parallax
/// Eye we were last anchored to - used to detect eye changes
var/atom/cached_eye
/// force this eye as the "real" eye - useful for secondary maps
var/atom/forced_eye
/// last turf loc
var/turf/last
/// last area - for parallax scrolling/loop animations
var/area/last_area
/// Holder object for vis
var/atom/movable/screen/parallax_vis/vis_holder
/// are we not on the main map? if so, put map id here
var/secondary_map
/// all layers
var/list/atom/movable/screen/parallax_layer/layers
/// vis contents
var/list/atom/movable/vis
/// currently scrolling?
var/scrolling = FALSE
/// current scroll speed in DS per scroll
var/scroll_speed
/// current scroll turn - applied after angle. if angle is 0 (picture moving north) and turn is 90, it would be like if you turned your viewport 90 deg clockwise.
var/scroll_turn
/// override planemaster we manipulate for turning and other effects
var/atom/movable/screen/plane_master/parallax/planemaster_override
/datum/parallax_holder/New(client/C, secondary_map, forced_eye, planemaster_override)
owner = C
if(!owner)
CRASH("No client")
src.secondary_map = secondary_map
src.forced_eye = forced_eye
src.planemaster_override = planemaster_override
Reset()
/datum/parallax_holder/Destroy()
if(owner)
if(owner.parallax_holder == src)
owner.parallax_holder = null
Remove()
HardResetAnimations()
QDEL_NULL(vis_holder)
QDEL_NULL(parallax)
layers = null
vis = null
last = null
forced_eye = cached_eye = null
owner = null
return ..()
/datum/parallax_holder/proc/Reset(auto_z_change, force)
if(!(cached_eye = Eye()))
// if no eye, tear down
last = cached_eye = last_area = null
SetParallax(null, null, auto_z_change)
return
// first, check loc
var/turf/T = get_turf(cached_eye)
if(!T)
// if in nullspace, tear down
last = cached_eye = last_area = null
SetParallax(null, null, auto_z_change)
return
// set last loc and eye
last = T
last_area = T.loc
// rebuild parallax
SetParallax(SSparallax.get_parallax_datum(T.z), null, auto_z_change, force)
// hard reset positions to correct positions
for(var/atom/movable/screen/parallax_layer/L in layers)
L.ResetPosition(T.x, T.y)
// better updates via client_mobs_in_contents can be created again when important recursive contents is ported!
/datum/parallax_holder/proc/Update(full)
if(!full && !cached_eye || (get_turf(cached_eye) == last))
return
if(!owner) // why are we here
if(!QDELETED(src))
qdel(src)
return
if(cached_eye != Eye())
// eye mismatch, reset
Reset()
return
var/turf/T = get_turf(cached_eye)
if(!last || T.z != last.z)
// z mismatch, reset
Reset()
return
// get rel offsets
var/rel_x = T.x - last.x
var/rel_y = T.y - last.y
// set last
last = T
// move
for(var/atom/movable/screen/parallax_layer/L in layers)
L.RelativePosition(T.x, T.y, rel_x, rel_y)
// process scrolling/movedir
if(last_area != T.loc)
last_area = T.loc
UpdateMotion()
/**
* Gets the eye we should be centered on
*/
/datum/parallax_holder/proc/Eye()
return forced_eye || owner?.eye
/**
* Gets the base parallax planemaster for things like turning
*/
/datum/parallax_holder/proc/GetPlaneMaster()
return planemaster_override || (owner && (locate(/atom/movable/screen/plane_master/parallax) in owner?.screen))
/**
* Syncs us to our parallax objects. Does NOT check if we should have those objects, that's Reset()'s job.
*
* Doesn't move/update positions/screen locs either.
*
* Also ensures movedirs are correct for the eye's pos.
*/
/datum/parallax_holder/proc/Sync(auto_z_change, force)
layers = list()
for(var/atom/movable/screen/parallax_layer/L in parallax.objects)
layers += L
L.map_id = secondary_map
if(!istype(vis_holder))
vis_holder = new /atom/movable/screen/parallax_vis
var/turf/T = get_turf(cached_eye)
vis_holder.vis_contents = vis = T? SSparallax.get_parallax_vis_contents(T.z) : list()
UpdateMotion(auto_z_change, force)
/**
* Updates motion if needed
*/
/datum/parallax_holder/proc/UpdateMotion(auto_z_change, force)
var/turf/T = get_turf(cached_eye)
if(!T)
if(scroll_speed || scroll_turn)
HardResetAnimations()
return
var/list/ret = SSparallax.get_parallax_motion(T.z)
if(ret)
Animation(ret[1], ret[2], auto_z_change? 0 : ret[3], auto_z_change? 0 : ret[4], force)
else
var/area/A = T.loc
Animation(A.parallax_move_speed, A.parallax_move_angle, auto_z_change? 0 : null, auto_z_change? 0 : null, force)
/datum/parallax_holder/proc/Apply(client/C = owner)
if(QDELETED(C))
return
. = list()
for(var/atom/movable/screen/parallax_layer/L in layers)
if(L.parallax_intensity > owner.prefs.parallax)
continue
if(!L.ShouldSee(C, last))
continue
L.SetView(C.view, TRUE)
. |= L
C.screen |= .
if(!secondary_map)
var/atom/movable/screen/plane_master/parallax_white/PM = locate() in C.screen
if(PM)
PM.color = list(
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
1, 1, 1, 1,
0, 0, 0, 0
)
/datum/parallax_holder/proc/Remove(client/C = owner)
if(QDELETED(C))
return
C.screen -= layers
if(!secondary_map)
var/atom/movable/screen/plane_master/parallax_white/PM = locate() in C.screen
if(PM)
PM.color = initial(PM.color)
/datum/parallax_holder/proc/SetParallaxType(path)
if(!ispath(path, /datum/parallax))
CRASH("Invalid path")
SetParallax(new path)
/datum/parallax_holder/proc/SetParallax(datum/parallax/P, delete_old = TRUE, auto_z_change, force)
if(P == parallax)
return
Remove()
if(delete_old && istype(parallax) && !QDELETED(parallax))
qdel(parallax)
HardResetAnimations()
parallax = P
if(!parallax)
return
Sync(auto_z_change, force)
Apply()
/**
* Runs a modifier to parallax as an animation.
*
* @params
* speed - ds per loop
* turn - angle clockwise from north to turn the motion to
* windup - ds to spend on windups. 0 for immediate.
* turn_speed - ds to spend on turning. 0 for immediate.
*/
/datum/parallax_holder/proc/Animation(speed = 25, turn = 0, windup = speed, turn_speed = speed, force)
// Parallax doesn't currently use this method of rotating.
// #if !PARALLAX_ROTATION_ANIMATIONS
// turn_speed = 0
// #endif
if(speed == 0)
StopScrolling(turn = turn, time = windup)
return
// if(turn != scroll_turn && GetPlaneMaster())
// // first handle turn. we turn the planemaster
// var/matrix/turn_transform = matrix()
// turn_transform.Turn(turn)
// scroll_turn = turn
// animate(GetPlaneMaster(), transform = turn_transform, time = turn_speed, easing = QUAD_EASING | EASE_IN, flags = ANIMATION_END_NOW | ANIMATION_LINEAR_TRANSFORM)
if(scroll_speed == speed && !force)
// we're done
return
// speed diff?
scroll_speed = speed
scrolling = TRUE
// always scroll from north; turn handles everything
for(var/atom/movable/screen/parallax_layer/P in layers)
if(P.absolute)
continue
var/matrix/translate_matrix = matrix()
translate_matrix.Translate(cos(turn) * 480, sin(turn) * 480)
var/matrix/target_matrix = matrix()
var/move_speed = speed * P.speed
// do the first segment by shifting down one screen
P.transform = translate_matrix
animate(P, transform = target_matrix, time = move_speed, easing = QUAD_EASING|EASE_IN, flags = ANIMATION_END_NOW)
// queue up another incase lag makes QueueLoop not fire on time, this time by shifting up
animate(transform = translate_matrix, time = 0)
animate(transform = target_matrix, time = move_speed)
P.QueueLoop(move_speed, speed * P.speed, translate_matrix, target_matrix)
/**
* Smoothly stops the animation, turning to a certain angle as needed.
*/
/datum/parallax_holder/proc/StopScrolling(turn = 0, time = 30)
// reset turn
if(turn != scroll_turn && GetPlaneMaster())
var/matrix/turn_transform = matrix()
turn_transform.Turn(turn)
scroll_turn = turn
animate(GetPlaneMaster(), transform = turn_transform, time = time, easing = QUAD_EASING | EASE_OUT, flags = ANIMATION_END_NOW | ANIMATION_LINEAR_TRANSFORM)
if(scroll_speed == 0)
// we're done
scrolling = FALSE
scroll_speed = 0
return
scrolling = FALSE
scroll_speed = 0
// someone can do the math for "stop after a smooth iteration" later.
for(var/atom/movable/screen/parallax_layer/P in layers)
if(P.absolute)
continue
P.CancelAnimation()
var/matrix/translate_matrix = matrix()
translate_matrix.Translate(cos(turn) * 480, sin(turn) * 480)
P.transform = translate_matrix
animate(P, transform = matrix(), time = time, easing = QUAD_EASING | EASE_OUT)
/**
* fully resets animation state
*/
/datum/parallax_holder/proc/HardResetAnimations()
// reset vars
scroll_turn = 0
scroll_speed = 0
scrolling = FALSE
// reset turn
if(GetPlaneMaster())
animate(GetPlaneMaster(), transform = matrix(), time = 0, flags = ANIMATION_END_NOW)
// reset objects
for(var/atom/movable/screen/parallax_layer/P in layers)
if(P.absolute)
continue
P.CancelAnimation()
animate(P, transform = matrix(), time = 0, flags = ANIMATION_END_NOW)
/client/proc/CreateParallax()
if(!parallax_holder)
parallax_holder = new(src)
/atom/movable/screen/parallax_vis
screen_loc = "CENTER,CENTER"
+137
View File
@@ -0,0 +1,137 @@
/atom/movable/screen/parallax_layer
icon = 'icons/screen/parallax.dmi'
blend_mode = BLEND_ADD
plane = PLANE_SPACE_PARALLAX
screen_loc = "CENTER-7,CENTER-7"
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
appearance_flags = PIXEL_SCALE | KEEP_TOGETHER
// notice - all parallax layers are 15x15 tiles. They roll over every 240 pixels.
/// pixel x/y shift per real x/y
var/speed = 1
/// current cached offset x
var/offset_x = 0
/// current cached offset y
var/offset_y = 0
/// normal centered x
var/center_x = 0
/// normal centered y
var/center_y = 0
/// absolute - always determine shift x/y as a function of real x/y instead of allowing for relative scroll.
var/absolute = FALSE
/// parallax level required to see this
var/parallax_intensity = PARALLAX_INSANE
/// current view we're adapted to
var/view_current
/// dynamic self tile - tile to our view size. set this to false for static parallax layers.
var/dynamic_self_tile = TRUE
/// map id
var/map_id
/// queued animation timerid
var/queued_animation
/atom/movable/screen/parallax_layer/proc/ResetPosition(x, y)
// remember that our offsets/directiosn are relative to the player's viewport
// this means we need to scroll reverse to them.
offset_x = -(center_x + speed * x)
offset_y = -(center_y + speed * y)
if(!absolute)
if(offset_x > 240)
offset_x -= 480
if(offset_x < -240)
offset_x += 480
if(offset_y > 240)
offset_y -= 480
if(offset_y < -240)
offset_y += 480
screen_loc = "[map_id && "[map_id]:"]CENTER-7:[round(offset_x,1)],CENTER-7:[round(offset_y,1)]"
/atom/movable/screen/parallax_layer/proc/RelativePosition(x, y, rel_x, rel_y)
if(absolute)
return ResetPosition(x, y)
offset_x -= rel_x * speed
offset_y -= rel_y * speed
if(offset_x > 240)
offset_x -= 480
if(offset_x < -240)
offset_x += 480
if(offset_y > 240)
offset_y -= 480
if(offset_y < -240)
offset_y += 480
screen_loc = "[map_id && "[map_id]:"]CENTER-7:[round(offset_x,1)],CENTER-7:[round(offset_y,1)]"
/atom/movable/screen/parallax_layer/proc/SetView(client_view = world.view, force_update = FALSE)
if(view_current == client_view && !force_update)
return
view_current = client_view
if(!dynamic_self_tile)
return
var/list/real_view = getviewsize(client_view)
var/count_x = CEILING((real_view[1] / 2) / 15, 1) + 1
var/count_y = CEILING((real_view[2] / 2) / 15, 1) + 1
var/list/new_overlays = GetOverlays()
for(var/x in -count_x to count_x)
for(var/y in -count_y to count_y)
if(!x && !y)
continue
var/mutable_appearance/clone = new
// appearance clone
clone.icon = icon
clone.icon_state = icon_state
clone.overlays = GetOverlays()
// do NOT inherit our overlays! parallax layers should never have overlays,
// because if it inherited us it'll result in exponentially increasing overlays
// due to cut_overlays() above over there being a queue operation and not instant!
// clone.overlays = list()
// currently instantly using overlays =.
// clone.blend_mode = blend_mode
// clone.mouse_opacity = mouse_opacity
// clone.plane = plane
// clone.layer = layer
// shift to position
clone.transform = matrix(1, 0, x * 480, 0, 1, y * 480)
new_overlays += clone
overlays = new_overlays
/atom/movable/screen/parallax_layer/proc/ShouldSee(client/C, atom/location)
return TRUE
/**
* Return "natural" overlays, as we're goin to do some fuckery to overlays above.
*/
/atom/movable/screen/parallax_layer/proc/GetOverlays()
return list()
/atom/movable/screen/parallax_layer/proc/Clone()
var/atom/movable/screen/parallax_layer/layer = new type
layer.speed = speed
layer.offset_x = offset_x
layer.offset_y = offset_y
layer.absolute = absolute
layer.parallax_intensity = parallax_intensity
layer.view_current = view_current
layer.appearance = appearance
/atom/movable/screen/parallax_layer/proc/default_x()
return center_x
/atom/movable/screen/parallax_layer/proc/default_y()
return center_y
/atom/movable/screen/parallax_layer/proc/QueueLoop(delay, speed, matrix/translate_matrix, matrix/target_matrix)
if(queued_animation)
CancelAnimation()
queued_animation = addtimer(CALLBACK(src, .proc/_loop, speed, translate_matrix, target_matrix), delay, TIMER_STOPPABLE)
/atom/movable/screen/parallax_layer/proc/_loop(speed, matrix/translate_matrix = matrix(1, 0, 0, 0, 1, 480), matrix/target_matrix = matrix())
transform = translate_matrix
animate(src, transform = target_matrix, time = speed, loop = -1)
animate(transform = translate_matrix, time = 0)
queued_animation = null
/atom/movable/screen/parallax_layer/proc/CancelAnimation()
if(queued_animation)
deltimer(queued_animation)
queued_animation = null
+66
View File
@@ -0,0 +1,66 @@
/datum/parallax/space
var/static/planet_offset_x = rand(100, 160)
var/static/planet_offset_y = rand(100, 160)
var/static/random_layer = pickweightAllowZero(list(
/atom/movable/screen/parallax_layer/space/random/asteroids = 35,
/atom/movable/screen/parallax_layer/space/random/space_gas = 35,
null = 30
))
var/static/random_gas_color = pick(COLOR_TEAL, COLOR_GREEN, COLOR_YELLOW, COLOR_CYAN, COLOR_ORANGE, COLOR_PURPLE)
/datum/parallax/space/CreateObjects()
. = ..()
. += new /atom/movable/screen/parallax_layer/space/layer_1
. += new /atom/movable/screen/parallax_layer/space/layer_2
. += new /atom/movable/screen/parallax_layer/space/layer_3
var/atom/movable/screen/parallax_layer/space/planet/P = new
P.pixel_x = planet_offset_x
P.pixel_y = planet_offset_y
. += P
if(random_layer)
. += new random_layer
if(ispath(random_layer, /atom/movable/screen/parallax_layer/space/random/space_gas))
var/atom/movable/screen/parallax_layer/space/random/space_gas/SG = locate(random_layer) in objects
SG.add_atom_colour(random_gas_color, ADMIN_COLOUR_PRIORITY)
/atom/movable/screen/parallax_layer/space/layer_1
icon_state = "layer1"
speed = 0.6
layer = 1
parallax_intensity = PARALLAX_LOW
/atom/movable/screen/parallax_layer/space/layer_2
icon_state = "layer2"
speed = 1
layer = 2
parallax_intensity = PARALLAX_MED
/atom/movable/screen/parallax_layer/space/layer_3
icon_state = "layer3"
speed = 1.4
layer = 3
parallax_intensity = PARALLAX_HIGH
/atom/movable/screen/parallax_layer/space/random
blend_mode = BLEND_OVERLAY
speed = 3
layer = 3
parallax_intensity = PARALLAX_INSANE
/atom/movable/screen/parallax_layer/space/random/space_gas
icon_state = "space_gas"
/atom/movable/screen/parallax_layer/space/random/asteroids
icon_state = "asteroids"
/atom/movable/screen/parallax_layer/space/planet
icon_state = "planet"
blend_mode = BLEND_OVERLAY
absolute = TRUE //Status of seperation
speed = 3
layer = 30
dynamic_self_tile = FALSE
/atom/movable/screen/parallax_layer/space/planet/ShouldSee(client/C, atom/location)
var/turf/T = get_turf(location)
return ..() && T && is_station_level(T.z)