Explorer Update - Cataloguers (#6016)

* Explorer catalog scanners.
This commit is contained in:
Neerti
2019-03-30 16:18:15 -04:00
committed by Mechoid
parent 4acb50dabd
commit 1afbe8d4b0
85 changed files with 2640 additions and 299 deletions

View File

@@ -0,0 +1,81 @@
/atom
var/catalogue_delay = 5 SECONDS // How long it take to scan.
// List of types of /datum/category_item/catalogue that should be 'unlocked' when scanned by a Cataloguer.
// It is null by default to save memory by not having everything hold onto empty lists. Use macros like LAZYLEN() to check.
// Also you should use get_catalogue_data() to access this instead of doing so directly, so special behavior can be enabled.
var/list/catalogue_data = null
/mob
catalogue_delay = 10 SECONDS
// Tests if something can be catalogued.
// If something goes wrong and a mob was supplied, the mob will be told why they can't catalogue it.
/atom/proc/can_catalogue(mob/user)
// First check if anything is even on here.
var/list/data = get_catalogue_data()
if(!LAZYLEN(data))
to_chat(user, span("warning", "\The [src] is not interesting enough to catalogue."))
return FALSE
else
// Check if this has nothing new on it.
var/has_new_data = FALSE
for(var/t in data)
var/datum/category_item/catalogue/item = GLOB.catalogue_data.resolve_item(t)
if(!item.visible)
has_new_data = TRUE
break
if(!has_new_data)
to_chat(user, span("warning", "Scanning \the [src] would provide no new information."))
return FALSE
return TRUE
/mob/living/can_catalogue(mob/user) // Dead mobs can't be scanned.
if(stat >= DEAD)
to_chat(user, span("warning", "Entities must be alive for a comprehensive scan."))
return FALSE
return ..()
/obj/item/can_catalogue(mob/user) // Items must be identified to be scanned.
if(!is_identified())
to_chat(user, span("warning", "The properties of this object has not been determined. Identify it first."))
return FALSE
return ..()
/atom/proc/get_catalogue_delay()
return catalogue_delay
// Override for special behaviour.
// Should return a list with one or more "/datum/category_item/catalogue" types, or null.
// If overriding, it may be wise to call the super and get the results in order to merge the base result and the special result, if appropiate.
/atom/proc/get_catalogue_data()
return catalogue_data
/mob/living/carbon/human/get_catalogue_data()
var/list/data = list()
// First, handle robot-ness.
var/beep_boop = get_FBP_type()
switch(beep_boop)
if(FBP_CYBORG)
data += /datum/category_item/catalogue/technology/cyborgs
if(FBP_POSI)
data += /datum/category_item/catalogue/technology/positronics
if(FBP_DRONE)
data += /datum/category_item/catalogue/technology/drone/drones
// Now for species.
if(!(beep_boop in list(FBP_POSI, FBP_DRONE))) // Don't give the species entry if they are a posi or drone.
if(species && LAZYLEN(species.catalogue_data))
data += species.catalogue_data
return data
/mob/living/silicon/robot/get_catalogue_data()
var/list/data = list()
switch(braintype)
if(BORG_BRAINTYPE_CYBORG)
data += /datum/category_item/catalogue/technology/cyborgs
if(BORG_BRAINTYPE_POSI)
data += /datum/category_item/catalogue/technology/positronics
if(BORG_BRAINTYPE_DRONE)
data += /datum/category_item/catalogue/technology/drone/drones
return data

View File

@@ -0,0 +1,440 @@
GLOBAL_DATUM_INIT(catalogue_data, /datum/category_collection/catalogue, new)
// The collection holds everything together and is GLOB accessible.
/datum/category_collection/catalogue
category_group_type = /datum/category_group/catalogue
/datum/category_collection/catalogue/proc/resolve_item(item_path)
for(var/group in categories)
var/datum/category_group/G = group
var/datum/category_item/catalogue/C = item_path
var/name_to_search = initial(C.name)
if(G.items_by_name[name_to_search])
return G.items_by_name[name_to_search]
// for(var/item in G.items)
// var/datum/category_item/I = item
// if(I.type == item_path)
// return I
// Groups act as sections for the different data.
/datum/category_group/catalogue
// Plants.
/datum/category_group/catalogue/flora
name = "Flora"
category_item_type = /datum/category_item/catalogue/flora
// Animals.
/datum/category_group/catalogue/fauna
name = "Fauna"
category_item_type = /datum/category_item/catalogue/fauna
// Gadgets, tech, and robots.
/datum/category_group/catalogue/technology
name = "Technology"
category_item_type = /datum/category_item/catalogue/technology
// Abstract information.
/datum/category_group/catalogue/information
name = "Information"
category_item_type = /datum/category_item/catalogue/information
// Weird stuff like precursors.
/datum/category_group/catalogue/anomalous
name = "Anomalous"
category_item_type = /datum/category_item/catalogue/anomalous
// Physical material things like crystals and metals.
/datum/category_group/catalogue/material
name = "Material"
category_item_type = /datum/category_item/catalogue/material
// Items act as individual data for each object.
/datum/category_item/catalogue
var/desc = null // Paragraph or two about what the object is.
var/value = 0 // How many 'exploration points' you get for scanning it. Suggested to use the CATALOGUER_REWARD_* defines for easy tweaking.
var/visible = FALSE // When someone scans the correct object, this gets set to TRUE and becomes viewable in the databanks.
var/list/cataloguers = null // List of names of those who helped 'discover' this piece of data, in string form.
var/list/unlocked_by_any = null // List of types that, if they are discovered, it will also make this datum discovered.
var/list/unlocked_by_all = null // Similar to above, but all types on the list must be discovered for this to be discovered.
// Discovers a specific datum, and any datums associated with this datum by unlocked_by_[any|all].
// Returns null if nothing was found, otherwise returns a list of datum instances that was discovered, usually for the cataloguer to use.
/datum/category_item/catalogue/proc/discover(mob/user, list/new_cataloguers)
if(visible) // Already found.
return
. = list(src)
visible = TRUE
cataloguers = new_cataloguers
display_in_chatlog(user)
. += attempt_chain_discoveries(user, new_cataloguers, type)
// Calls discover() on other datums if they include the type that was just discovered is inside unlocked_by_[any|all].
// Returns discovered datums.
/datum/category_item/catalogue/proc/attempt_chain_discoveries(mob/user, list/new_cataloguers, type_to_test)
. = list()
for(var/G in category.collection.categories) // I heard you like loops.
var/datum/category_group/catalogue/group = G
for(var/I in group.items)
var/datum/category_item/catalogue/item = I
// First, look for datums unlocked with the 'any' list.
if(LAZYLEN(item.unlocked_by_any))
for(var/T in item.unlocked_by_any)
if(ispath(type_to_test, T) && item.discover(user, new_cataloguers))
. += item
// Now for the more complicated 'all' list.
if(LAZYLEN(item.unlocked_by_all))
if(type_to_test in item.unlocked_by_all)
// Unlike the 'any' list, the 'all' list requires that all datums inside it to have been found first.
var/should_discover = TRUE
for(var/T in item.unlocked_by_all)
var/datum/category_item/catalogue/thing = GLOB.catalogue_data.resolve_item(T)
if(istype(thing))
if(!thing.visible)
should_discover = FALSE
break
if(should_discover && item.discover(user, new_cataloguers))
. += item
/datum/category_item/catalogue/proc/display_in_chatlog(mob/user)
to_chat(user, "<br>")
to_chat(user, span("notice", "<b>[uppertext(name)]</b>"))
// Some entries get very long so lets not totally flood the chatlog.
var/desc_length_limit = 750
var/displayed_desc = desc
if(length(desc) > desc_length_limit)
displayed_desc = copytext(displayed_desc, 1, desc_length_limit + 1)
displayed_desc += "... (View databanks for full data)"
to_chat(user, span("notice", "<i>[displayed_desc]</i>"))
to_chat(user, span("notice", "Cataloguers : <b>[english_list(cataloguers)]</b>."))
to_chat(user, span("notice", "Contributes <b>[value]</b> points to personal exploration fund."))
/*
// Truncates text to limit if necessary.
var/size = length(message)
if (size <= length)
return message
else
return copytext(message, 1, length + 1)
*/
/datum/category_item/catalogue/flora
/datum/category_item/catalogue/fauna
/datum/category_item/catalogue/fauna/humans
name = "Sapients - Humans"
desc = "Humans are a space-faring species hailing originally from the planet Earth in the Sol system. \
They are currently among the most numerous known species in the galaxy, in both population and holdings, \
and are relatively technologically advanced. With good healthcare and a reasonable lifestyle, \
they can live to around 110 years. The oldest humans are around 150 years old.\
<br><br>\
Humanity is the primary driving force for rapid space expansion, owing to their strong, expansionist central \
government and opportunistic Trans-Stellar Corporations. The prejudices of the 21st century have mostly \
given way to bitter divides on the most important issue of the times<65> technological expansionism, \
with the major human factions squabbling over their approach to technology in the face of a \
looming singularity.\
<br><br>\
While most humans have accepted the existence of aliens in their communities and workplaces as a \
fact of life, exceptions abound. While more culturally diverse than most species, humans are \
generally regarded as somewhat technophobic and isolationist by members of other species."
value = CATALOGUER_REWARD_TRIVIAL
/datum/category_item/catalogue/fauna/skrell
name = "Sapients - Skrell"
desc = "The Skrell are a species of amphibious humanoids, distinguished by their green-blue gelatinous \
appearance and head tentacles. Skrell warble from the world of Qerr'balak, a humid planet with \
plenty of swamps and jungles. Currently more technologically advanced than humanity, they \
emphasize the study of the mind above all else.\
<br><br>\
Gender has little meaning to Skrell outside of reproduction, and in fact many other species \
have a difficult time telling the difference between male and female Skrell apart. The most \
obvious signs (voice in a slightly higher register, longer head-tails for females) are \
never a guarantee.\
<br><br>\
Due to their scientific focus of the mind and body, Skrell tend to be more peaceful and their \
colonization has been slow, swiftly outpaced by humanity. They were the first contact sentient \
species, and are humanity's longest, and closest, ally in space."
value = CATALOGUER_REWARD_TRIVIAL
/datum/category_item/catalogue/fauna/unathi
name = "Sapients - Unathi"
desc = "The Unathi are a species of large reptilian humanoids hailing from Moghes, in the \
Uueoa-Esa binary star system. Most Unathi live in a semi-rigid clan system, and clan \
enclaves dot the surface of their homeworld. Proud and long-lived, Unathi of all \
walks of life display a tendency towards perfectionism, and mastery of one<6E>s craft \
is greatly respected among them. Despite the aggressive nature of their contact, \
Unathi seem willing, if not eager, to reconcile with humanity, though mutual \
distrust runs rampant among individuals of both groups."
value = CATALOGUER_REWARD_TRIVIAL
/datum/category_item/catalogue/fauna/tajaran
name = "Sapients - Tajaran"
desc = "Tajaran are a race of humanoid mammalian aliens from Meralar, the fourth planet \
of the Rarkajar star system. Thickly furred and protected from cold, they thrive on \
their subartic planet, where the only terran temperate areas spread across the \
equator and <20>tropical belt.<2E>\
<br><br>\
With their own share of bloody wars and great technological advances, the Tajaran are a \
proud kind. They fiercely believe they belong among the stars and consider themselves \
a rightful interstellar nation, even if Humanity helped them to actually achieve \
superluminar speeds with Bluespace FTL drives.\
<br><br>\
Relatively new to the galaxy, their contacts with other species are aloof, but friendly. \
Among these bonds, Humanity stands out as valued trade partner and maybe even friend."
value = CATALOGUER_REWARD_TRIVIAL
/datum/category_item/catalogue/fauna/dionaea
name = "Sapients - Dionaea"
desc = "The Dionaea are a group of intensely curious plant-like organisms. An individual \
Diona is a single dog-sized creature called a nymphs, and multiple nymphs link together \
to form larger, more intelligent collectives. Discovered by the Skrell in and around \
the stars in the Epsilon Ursae Minoris system, they have accompanied the Skrell in \
warbling throughout the cosmos as a key part of Skrellian starships, stations, \
and terraforming equipment.\
<br><br>\
Dionaea have no concept of violence or individual identity and want little in \
terms of material resources or living space. This makes Dionaea among the most \
agreeable members of the galactic community, though their slow, curious alien \
minds can be hard to sympathize with."
value = CATALOGUER_REWARD_TRIVIAL
/datum/category_item/catalogue/fauna/teshari
name = "Sapients - Teshari"
desc = "The Teshari are reptilian pack predators from the Skrell homeworld. \
While they evolved alongside the Skrell, their interactions with them tended \
to be confused and violent, and until peaceful contact was made they largely \
stayed in their territories on and around the poles, in tundral terrain far \
too desolate and cold to be of interest to the Skrell. In more enlightened \
times, the Teshari are a minority culture on many Skrell worlds, maintaining \
their own settlements and cultures, but often finding themselves standing \
on the shoulders of their more technologically advanced neighbors when it \
comes to meeting and exploring the rest of the galaxy."
value = CATALOGUER_REWARD_TRIVIAL
/datum/category_item/catalogue/fauna/zaddat
name = "Sapients - Zaddat"
desc = "The Zaddat are an Unathi client species that has recently come to the \
Golden Crescent. They wear high-pressure voidsuits called Shrouds to protect \
themselves from the harsh light and low pressure of the station, making \
medical care a challenge and fighting especially dangerous. \
Operating out of massive Colony ships, they trade their labor to their \
host nation to fund their search for a new home to replace their \
now-lifeless homeworld of Xohox."
value = CATALOGUER_REWARD_TRIVIAL
/datum/category_item/catalogue/fauna/promethean
name = "Sapients - Promethean"
desc = "Prometheans (Macrolimus artificialis) are a species of artificially-created \
gelatinous humanoids, chiefly characterized by their primarily liquid bodies and \
ability to change their bodily shape and color in order to mimic many forms of life. \
Derived from the Aetolian giant slime (Macrolimus vulgaris) inhabiting the warm, \
tropical planet of Aetolus, they are a relatively newly lab-created sapient species, \
and as such many things about them have yet to be comprehensively studied."
value = CATALOGUER_REWARD_TRIVIAL
/datum/category_item/catalogue/fauna/vox
name = "Sapients - Vox"
desc = "Probably the best known of these aliens are the Vox, a bird-like species \
with a very rough comprehension of Galactic Common and an even looser understanding \
of property rights. Vox raiders have plagued human merchants for centuries, \
and Skrell for even longer, but remain poorly understood. \
They have no desire to partake in diplomacy or trade with the rest of the galaxy, \
or even to conquer planets and stations to live in. They breathe phoron \
and appear to be well adapted to their role as space-faring raiders, \
leading many to speculate that they're heavily bioengineered, \
an assumption which is at odds with their ramshackle technological level."
value = CATALOGUER_REWARD_MEDIUM // Since Vox are much rarer.
/datum/category_item/catalogue/technology
/datum/category_item/catalogue/technology/drone/drones
name = "Drones"
desc = "A drone is a software-based artificial intelligence, generally about an order of magnitude \
less intelligent than a positronic brain. However, the processing power available to a drone can \
vary wildly, from cleaning bots barely more advanced than those from the 21st century to cutting-edge \
supercomputers capable of complex conversation. Drones are legally objects in all starfaring polities \
outside of the Almach Association, and the sapience of even the most advanced drones is a matter of speculation."
value = CATALOGUER_REWARD_TRIVIAL
// Scanning any drone mob will get you this alongside the mob entry itself.
unlocked_by_any = list(/datum/category_item/catalogue/technology/drone)
/datum/category_item/catalogue/technology/positronics
name = "Sapients - Positronics"
desc = "A Positronic being, often an Android, Gynoid, or Robot, is an individual with a positronic brain, \
manufactured and fostered amongst organic life Positronic brains enjoy the same legal status as a humans, \
although discrimination is still common, are considered sapient on all accounts, and can be considered \
the <20>synthetic species<65>. Half-developed and half-discovered in the 2280<38>s by a black lab studying alien \
artifacts, the first positronic brain was an inch-wide cube of palladium-iridium alloy, nano-etched with \
billions upon billions of conduits and connections. Upon activation, hard-booted by way of an emitter \
laser, the brain issued a single sentence before the neural pathways collapsed and it became an inert \
lump of platinum: <20>What is my purpose?<3F>."
value = CATALOGUER_REWARD_TRIVIAL
/datum/category_item/catalogue/technology/cyborgs
name = "Cyborgs"
desc = "A Cyborg is an originally organic being composed of largely cybernetic parts. As a brain preserved \
in an MMI, they may inhabit an expensive humanoid chassis, a specially designed industrial shell of some \
sort, or be integrated into a computer system as an AI. The term covers all species \
(even, in some cases, animal brains) and all applications. It can also be used somewhat derogatorily \
for those who are still have more organic parts than just their brains, but for example have a \
full set of prosthetic limbs."
value = CATALOGUER_REWARD_TRIVIAL
/datum/category_item/catalogue/information
// For these we can piggyback off of the lore datums that are already defined and used in some places.
/datum/category_item/catalogue/information/organization
value = CATALOGUER_REWARD_TRIVIAL
var/datum_to_copy = null
/datum/category_item/catalogue/information/organization/New()
..()
if(datum_to_copy)
// I'd just access the loremaster object but it might not exist because its ugly.
var/datum/lore/organization/O = new datum_to_copy()
// I would also change the name based on the org datum but changing the name messes up indexing in some lists in the category/collection object attached to us.
// Now lets combine the data in the datum for a slightly more presentable entry.
var/constructed_desc = ""
if(O.motto)
constructed_desc += "<center><b><i>\"[O.motto]\"</i></b></center><br><br>"
constructed_desc += O.desc
desc = constructed_desc
qdel(O)
/datum/category_item/catalogue/information/organization/nanotrasen
name = "TSC - NanoTrasen Incorporated"
datum_to_copy = /datum/lore/organization/tsc/nanotrasen
/datum/category_item/catalogue/information/organization/hephaestus
name = "TSC - Hephaestus Industries"
datum_to_copy = /datum/lore/organization/tsc/hephaestus
/datum/category_item/catalogue/information/organization/vey_med
name = "TSC - Vey-Medical"
datum_to_copy = /datum/lore/organization/tsc/vey_med
/datum/category_item/catalogue/information/organization/zeng_hu
name = "TSC - Zeng Hu Pharmaceuticals"
datum_to_copy = /datum/lore/organization/tsc/zeng_hu
/datum/category_item/catalogue/information/organization/ward_takahashi
name = "TSC - Ward-Takahashi General Manufacturing Conglomerate"
datum_to_copy = /datum/lore/organization/tsc/ward_takahashi
/datum/category_item/catalogue/information/organization/bishop
name = "TSC - Bishop Cybernetics"
datum_to_copy = /datum/lore/organization/tsc/bishop
/datum/category_item/catalogue/information/organization/morpheus
name = "TSC - Morpheus Cyberkinetics"
datum_to_copy = /datum/lore/organization/tsc/morpheus
/datum/category_item/catalogue/information/organization/xion
name = "TSC - Xion Manufacturing Group"
datum_to_copy = /datum/lore/organization/tsc/xion
/datum/category_item/catalogue/information/organization/major_bills
name = "TSC - Major Bill's Transportation"
datum_to_copy = /datum/lore/organization/tsc/mbt
/datum/category_item/catalogue/information/organization/solgov
name = "Government - Solar Confederate Government"
datum_to_copy = /datum/lore/organization/gov/solgov
/datum/category_item/catalogue/information/organization/virgov
name = "Government - Vir Governmental Authority"
datum_to_copy = /datum/lore/organization/gov/virgov
/datum/category_item/catalogue/anomalous
/datum/category_item/catalogue/anomalous/precursor_controversy
name = "Precursor Controversy"
desc = "The term 'Precursor' is generally used to refer to one or more ancient races that \
had obtained vast technological and cultural progress, but no longer appear to be present, \
leaving behind what remains of their creations, as well as many questions for the races that \
would stumble upon their ruins. Scientists and xenoarcheologists have been hard at work, trying \
to uncover the truth.\
<br><br>\
In modern times, there is controversy over the accuracy of what knowledge has been uncovered. \
The mainstream scientific opinion had been that there was one, and only one ancient species, \
called the Singularitarians. This view still is the majority today, however there has also \
been dissent over that view, as some feel that the possibility of multiple precursor \
civilizations should not be ignored. They point towards a large number of discrepancies between \
the dominant Singularitarian theory, and various artifacts that have been found, as well as \
different artifacts uncovered appearing to have very different characteristics to each other. \
Instead, they say that the Singularitarians were one of multiple precursors.\
<br><br>\
Presently, no conclusive evidence exists for any side."
value = CATALOGUER_REWARD_TRIVIAL
// Add the other precursor groups here when they get added.
unlocked_by_any = list(
/datum/category_item/catalogue/anomalous/precursor_a,
/datum/category_item/catalogue/anomalous/precursor_b
)
/datum/category_item/catalogue/anomalous/singularitarians
name = "Precursors - Singularitarians"
desc = "The Singularitarians were a massive, highly-advanced spacefaring race which are now \
believed to be extinct. At their height, they extended throughout all of known human space, \
with major population centers in the Precursor's Crypt region, as well as significant swaths \
of Skrell space, until they were wiped out by a self-replicating nanobot plague that still \
coats their ruins as a fine layer of dust. They left behind the proto-positronics, as well \
as several high-yield phoron deposits and other artifacts of technology studied, \
cautiously, by the races that survived them.\
<br><br>\
Very little is known about the biology and physiology of the Singularitarians, who are believed \
to have been largely post-biological. The Vox claim to be the race that created the positronics, \
but said claim is only ever brought up when they claim the right to take any positronic they want. \
Some more open-minded xenoarcheologists have voiced the opinion that there is some truth in their \
claims, but it's far from a scientific consensus."
value = CATALOGUER_REWARD_TRIVIAL
unlocked_by_any = list(/datum/category_item/catalogue/anomalous/precursor_controversy)
// Obtained by scanning any 'precursor a' object, generally things in the UFO PoI.
// A is for Ayyyyyy.
/datum/category_item/catalogue/anomalous/precursor_a/precursor_a_basic
name = "Precursors - Precursor Group Alpha"
desc = "This describes a group of xenoarcheological findings which have strong similarities \
together. Specifically, this group of objects appears to have a strong aesthetic for the colors \
cyan and pink, both colors often being present on everything in this group. It is unknown why \
these two colors were chosen by their creators. Another similarity is that most objects made \
in this group appear to be comprised of not well understood metallic materials that are dark, \
and very resilient. Some objects in this group also appear to utilize electricity to \
operate. Finally, a large number of objects in this group appear to have been made \
to be used by the creators of those objects in a physical manner.\
<br><br>\
It should be noted that the findings in this group appear to conflict heavily with what is \
known about the Singularitarians, giving some credence towards these objects belonging to a \
seperate precursor. As such, the findings have been partitioned inside this scanner to this \
group, labeled Precursor Group Alpha."
value = CATALOGUER_REWARD_TRIVIAL
unlocked_by_any = list(/datum/category_item/catalogue/anomalous/precursor_a)
// Obtained by scanning any 'precursor b' object, generally things dug up from xenoarch.
// B is for buried.
/datum/category_item/catalogue/anomalous/precursor_b/precursor_b_basic
name = "Precursors - Precursor Group Beta"
/datum/category_item/catalogue/material

View File

@@ -0,0 +1,312 @@
GLOBAL_LIST_EMPTY(all_cataloguers)
/*
This is a special scanner which exists to give explorers something to do besides shoot things.
The scanner is able to be used on certain things in the world, and after a variable delay, the scan finishes,
giving the person who scanned it some fluff and information about what they just scanned,
as well as points that currently do nothing but measure epeen,
and will be used as currency in The Future(tm) to buy things explorers care about.
Scanning hostile mobs and objects is tricky since only mobs that are alive are scannable, so scanning
them requires careful position to stay out of harms way until the scan finishes. That is why
the person with the scanner gets a visual box that shows where they are allowed to move to
without inturrupting the scan.
*/
/obj/item/device/cataloguer
name = "cataloguer"
desc = "A hand-held device, used for compiling information about an object by scanning it. Alt+click to highlight scannable objects around you."
description_info = "This is a special device used to obtain information about objects and entities in the environment. \
To scan something, click on it with the scanner at a distance. \
Scanning something requires remaining within a certain radius of the object for a specific period of time, until the \
scan is finished. If the scan is inturrupted, it can be resumed from where it was left off, if the same thing is \
scanned again."
icon = 'icons/obj/device.dmi'
icon_state = "cataloguer"
w_class = ITEMSIZE_NORMAL
origin_tech = list(TECH_MATERIAL = 2, TECH_DATA = 3, TECH_MAGNET = 3)
force = 0
var/points_stored = 0 // Amount of 'exploration points' this device holds.
var/scan_range = 3 // How many tiles away it can scan. Changing this also changes the box size.
var/credit_sharing_range = 14 // If another person is within this radius, they will also be credited with a successful scan.
var/datum/category_item/catalogue/displayed_data = null // Used for viewing a piece of data in the UI.
var/busy = FALSE // Set to true when scanning, to stop multiple scans.
var/debug = FALSE // If true, can view all catalogue data defined, regardless of unlock status.
var/weakref/partial_scanned = null // Weakref of the thing that was last scanned if inturrupted. Used to allow for partial scans to be resumed.
var/partial_scan_time = 0 // How much to make the next scan shorter.
/obj/item/device/cataloguer/advanced
name = "advanced cataloguer"
icon_state = "adv_cataloguer"
desc = "A hand-held device, used for compiling information about an object by scanning it. This one is an upgraded model, \
with a scanner that both can scan from farther away, and with less time."
scan_range = 4
toolspeed = 0.8
// Able to see all defined catalogue data regardless of if it was unlocked, intended for testing.
/obj/item/device/cataloguer/debug
name = "omniscient cataloguer"
desc = "A hand-held cataloguer device that appears to be plated with gold. For some reason, it \
just seems to already know everything about narrowly defined pieces of knowledge one would find \
from nearby, perhaps due to being colored gold. Truly a epistemological mystery."
icon_state = "debug_cataloguer"
toolspeed = 0.1
scan_range = 7
debug = TRUE
/obj/item/device/cataloguer/Initialize()
GLOB.all_cataloguers += src
return ..()
/obj/item/device/cataloguer/Destroy()
GLOB.all_cataloguers -= src
displayed_data = null
return ..()
/obj/item/device/cataloguer/update_icon()
if(busy)
icon_state = "[initial(icon_state)]_active"
else
icon_state = initial(icon_state)
/obj/item/device/cataloguer/afterattack(atom/target, mob/user, proximity_flag)
// Things that invalidate the scan immediately.
if(busy)
to_chat(user, span("warning", "\The [src] is already scanning something."))
return
if(isturf(target) && (!target.can_catalogue()))
var/turf/T = target
for(var/a in T) // If we can't scan the turf, see if we can scan anything on it, to help with aiming.
var/atom/A = a
if(A.can_catalogue())
target = A
break
if(!target.can_catalogue(user)) // This will tell the user what is wrong.
return
if(get_dist(target, user) > scan_range)
to_chat(user, span("warning", "You are too far away from \the [target] to catalogue it. Get closer."))
return
// Get how long the delay will be.
var/scan_delay = target.get_catalogue_delay() * toolspeed
if(partial_scanned)
if(partial_scanned.resolve() == target)
scan_delay -= partial_scan_time
to_chat(user, span("notice", "Resuming previous scan."))
else
to_chat(user, span("warning", "Scanning new target. Previous scan buffer cleared."))
// Start the special effects.
busy = TRUE
update_icon()
var/datum/beam/scan_beam = user.Beam(target, icon_state = "rped_upgrade", time = scan_delay)
var/filter = filter(type = "outline", size = 1, color = "#FFFFFF")
target.filters += filter
var/list/box_segments = list()
if(user.client)
box_segments = draw_box(target, scan_range, user.client)
color_box(box_segments, "#00FF00", scan_delay)
playsound(src.loc, 'sound/machines/beep.ogg', 50)
// The delay, and test for if the scan succeeds or not.
var/scan_start_time = world.time
if(do_after(user, scan_delay, target, ignore_movement = TRUE, max_distance = scan_range))
if(target.can_catalogue(user))
to_chat(user, span("notice", "You successfully scan \the [target] with \the [src]."))
playsound(src.loc, 'sound/machines/ping.ogg', 50)
catalogue_object(target, user)
else
// In case someone else scans it first, or it died, etc.
to_chat(user, span("warning", "\The [target] is no longer valid to scan with \the [src]."))
playsound(src.loc, 'sound/machines/buzz-two.ogg', 50)
partial_scanned = null
partial_scan_time = 0
else
to_chat(user, span("warning", "You failed to finish scanning \the [target] with \the [src]."))
playsound(src.loc, 'sound/machines/buzz-two.ogg', 50)
color_box(box_segments, "#FF0000", 3)
partial_scanned = weakref(target)
partial_scan_time += world.time - scan_start_time // This is added to the existing value so two partial scans will add up correctly.
sleep(3)
busy = FALSE
// Now clean up the effects.
update_icon()
QDEL_NULL(scan_beam)
if(target)
target.filters -= filter
if(user.client) // If for some reason they logged out mid-scan the box will be gone anyways.
delete_box(box_segments, user.client)
// Todo: Display scanned information, increment points, etc.
/obj/item/device/cataloguer/proc/catalogue_object(atom/target, mob/living/user)
// Figure out who may have helped out.
var/list/contributers = list()
var/list/contributer_names = list()
for(var/thing in player_list)
var/mob/M = thing
if(M == user)
continue
if(get_dist(M, user) <= credit_sharing_range)
contributers += M
contributer_names += M.name
var/points_gained = 0
// Discover each datum available.
var/list/object_data = target.get_catalogue_data()
if(LAZYLEN(object_data))
for(var/data_type in object_data)
var/datum/category_item/catalogue/I = GLOB.catalogue_data.resolve_item(data_type)
if(istype(I))
var/list/discoveries = I.discover(user, list(user.name) + contributer_names) // If one discovery leads to another, the list returned will have all of them.
if(LAZYLEN(discoveries))
for(var/D in discoveries)
var/datum/category_item/catalogue/data = D
points_gained += data.value
// Give out points.
if(points_gained)
// First, to us.
to_chat(user, span("notice", "Gained [points_gained] points from this scan."))
adjust_points(points_gained)
// Now to our friends, if any.
if(contributers.len)
for(var/mob/M in contributers)
var/list/things = M.GetAllContents(3) // Depth of two should reach into bags but just in case lets make it three.
var/obj/item/device/cataloguer/other_cataloguer = locate() in things // If someone has two or more scanners this only adds points to one.
if(other_cataloguer)
to_chat(M, span("notice", "Gained [points_gained] points from \the [user]'s scan of \the [target]."))
other_cataloguer.adjust_points(points_gained)
to_chat(user, span("notice", "Shared discovery with [contributers.len] other contributer\s."))
/obj/item/device/cataloguer/AltClick(mob/user)
pulse_scan(user)
// Gives everything capable of being scanned an outline for a brief moment.
// Helps to avoid having to click a hundred things in a room for things that have an entry.
/obj/item/device/cataloguer/proc/pulse_scan(mob/user)
if(busy)
to_chat(user, span("warning", "\The [src] is busy doing something else."))
return
busy = TRUE
update_icon()
playsound(src.loc, 'sound/machines/beep.ogg', 50)
// First, get everything able to be scanned.
var/list/scannable_atoms = list()
for(var/a in view(world.view, user))
var/atom/A = a
if(A.can_catalogue()) // Not passing the user is intentional, so they don't get spammed.
scannable_atoms += A
// Highlight things able to be scanned.
var/filter = filter(type = "outline", size = 1, color = "#00FF00")
for(var/a in scannable_atoms)
var/atom/A = a
A.filters += filter
to_chat(user, span("notice", "\The [src] is highlighting scannable objects in green, if any exist."))
sleep(2 SECONDS)
// Remove the highlights.
for(var/a in scannable_atoms)
var/atom/A = a
if(QDELETED(A))
continue
A.filters -= filter
busy = FALSE
update_icon()
if(scannable_atoms.len)
playsound(src.loc, 'sound/machines/ping.ogg', 50)
else
playsound(src.loc, 'sound/machines/buzz-two.ogg', 50)
to_chat(user, span("notice", "\The [src] found [scannable_atoms.len] object\s that can be scanned."))
// Negative points are bad.
/obj/item/device/cataloguer/proc/adjust_points(amount)
points_stored = max(0, points_stored += amount)
/obj/item/device/cataloguer/attack_self(mob/living/user)
interact(user)
/obj/item/device/cataloguer/interact(mob/user)
var/list/dat = list()
var/title = "Cataloguer Data Display"
// Important buttons go on top since the scrollbar will default to the top of the window.
dat += "Contains <b>[points_stored]</b> Exploration Points."
dat += "<a href='?src=\ref[src];pulse_scan=1'>\[Highlight Scannables\]</a><a href='?src=\ref[src];refresh=1'>\[Refresh\]</a><a href='?src=\ref[src];close=1'>\[Close\]</a>"
// If displayed_data exists, we show that, otherwise we show a list of all data in the mysterious global list.
if(displayed_data)
title = uppertext(displayed_data.name)
dat += "<a href='?src=\ref[src];show_data=null'>\[Back to List\]</a>"
if(debug && !displayed_data.visible)
dat += "<a href='?src=\ref[src];debug_unlock=\ref[displayed_data]'>\[(DEBUG) Force Discovery\]</a>"
dat += "<hr>"
dat += "<i>[displayed_data.desc]</i>"
if(LAZYLEN(displayed_data.cataloguers))
dat += "Cataloguers : <b>[english_list(displayed_data.cataloguers)]</b>."
else
dat += "Catalogued by nobody."
dat += "Worth <b>[displayed_data.value]</b> exploration points."
else
dat += "<hr>"
for(var/G in GLOB.catalogue_data.categories)
var/datum/category_group/group = G
var/list/group_dat = list()
var/show_group = FALSE
group_dat += "<b>[group.name]</b>"
for(var/I in group.items)
var/datum/category_item/catalogue/item = I
if(item.visible || debug)
group_dat += "<a href='?src=\ref[src];show_data=\ref[item]'>[item.name]</a>"
show_group = TRUE
if(show_group || debug) // Avoid showing 'empty' groups on regular cataloguers.
dat += group_dat
var/datum/browser/popup = new(user, "cataloguer_display_\ref[src]", title, 500, 600, src)
popup.set_content(dat.Join("<br>"))
popup.open()
add_fingerprint(user)
/obj/item/device/cataloguer/Topic(href, href_list)
if(..())
usr << browse(null, "window=cataloguer_display")
return 0
if(href_list["close"] )
usr << browse(null, "window=cataloguer_display")
return 0
if(href_list["show_data"])
displayed_data = locate(href_list["show_data"])
if(href_list["pulse_scan"])
pulse_scan(usr)
return // Don't refresh the window for this or it will open it back if its closed during the highlighting.
if(href_list["debug_unlock"] && debug)
var/datum/category_item/catalogue/item = locate(href_list["debug_unlock"])
item.discover(usr, list("Debugger"))
interact(usr) // So it refreshes the window.
return 1

View File

@@ -0,0 +1,68 @@
#define ICON_SIZE 32
// Draws a box showing the limits of movement while scanning something.
// Only the client supplied will see the box.
/obj/item/device/cataloguer/proc/draw_box(atom/A, box_size, client/C)
. = list()
// Things moved with pixel_[x|y] will move the box, so this is to correct that.
var/pixel_x_correction = -A.pixel_x
var/pixel_y_correction = -A.pixel_y
// First, place the bottom-left corner.
. += draw_line(A, SOUTHWEST, (-box_size * ICON_SIZE) + pixel_x_correction, (-box_size * ICON_SIZE) + pixel_y_correction, C)
// Make a line on the bottom, going right.
for(var/i = 1 to (box_size * 2) - 1)
var/x_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_x_correction
var/y_displacement = (-box_size * ICON_SIZE) + pixel_y_correction
. += draw_line(A, SOUTH, x_displacement, y_displacement, C)
// Bottom-right corner.
. += draw_line(A, SOUTHEAST, (box_size * ICON_SIZE) + pixel_x_correction, (-box_size * ICON_SIZE) + pixel_y_correction, C)
// Second line, for the right side going up.
for(var/i = 1 to (box_size * 2) - 1)
var/x_displacement = (box_size * ICON_SIZE) + pixel_x_correction
var/y_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_y_correction
. += draw_line(A, EAST, x_displacement, y_displacement, C)
// Top-right corner.
. += draw_line(A, NORTHEAST, (box_size * ICON_SIZE) + pixel_x_correction, (box_size * ICON_SIZE) + pixel_y_correction, C)
// Third line, for the top, going right.
for(var/i = 1 to (box_size * 2) - 1)
var/x_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_x_correction
var/y_displacement = (box_size * ICON_SIZE) + pixel_y_correction
. += draw_line(A, NORTH, x_displacement, y_displacement, C)
// Top-left corner.
. += draw_line(A, NORTHWEST, (-box_size * ICON_SIZE) + pixel_x_correction, (box_size * ICON_SIZE) + pixel_y_correction, C)
// Fourth and last line, for the left side going up.
for(var/i = 1 to (box_size * 2) - 1)
var/x_displacement = (-box_size * ICON_SIZE) + pixel_x_correction
var/y_displacement = (-box_size * ICON_SIZE) + (ICON_SIZE * i) + pixel_y_correction
. += draw_line(A, WEST, x_displacement, y_displacement, C)
#undef ICON_SIZE
// Draws an individual segment of the box.
/obj/item/device/cataloguer/proc/draw_line(atom/A, line_dir, line_pixel_x, line_pixel_y, client/C)
var/image/line = image(icon = 'icons/effects/effects.dmi', loc = A, icon_state = "stripes", dir = line_dir)
line.pixel_x = line_pixel_x
line.pixel_y = line_pixel_y
line.plane = PLANE_FULLSCREEN // It's technically a HUD element but it doesn't need to show above item slots.
line.appearance_flags = RESET_TRANSFORM|RESET_COLOR|RESET_ALPHA|NO_CLIENT_COLOR|TILE_BOUND
line.alpha = 125
C.images += line
return line
// Removes the box that was generated before from the client.
/obj/item/device/cataloguer/proc/delete_box(list/box_segments, client/C)
for(var/i in box_segments)
C.images -= i
qdel(i)
/obj/item/device/cataloguer/proc/color_box(list/box_segments, new_color, new_time)
for(var/i in box_segments)
animate(i, color = new_color, time = new_time)