initial commit - cross reference with 5th port - obviously has compile errors
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
Asset cache quick users guide:
|
||||
|
||||
Make a datum at the bottom of this file with your assets for your thing.
|
||||
The simple subsystem will most like be of use for most cases.
|
||||
Then call get_asset_datum() with the type of the datum you created and store the return
|
||||
Then call .send(client) on that stored return value.
|
||||
|
||||
You can set verify to TRUE if you want send() to sleep until the client has the assets.
|
||||
*/
|
||||
|
||||
|
||||
// Amount of time(ds) MAX to send per asset, if this get exceeded we cancel the sleeping.
|
||||
// This is doubled for the first asset, then added per asset after
|
||||
#define ASSET_CACHE_SEND_TIMEOUT 7
|
||||
|
||||
//When sending mutiple assets, how many before we give the client a quaint little sending resources message
|
||||
#define ASSET_CACHE_TELL_CLIENT_AMOUNT 8
|
||||
|
||||
/client
|
||||
var/list/cache = list() // List of all assets sent to this client by the asset cache.
|
||||
var/list/completed_asset_jobs = list() // List of all completed jobs, awaiting acknowledgement.
|
||||
var/list/sending = list()
|
||||
var/last_asset_job = 0 // Last job done.
|
||||
|
||||
//This proc sends the asset to the client, but only if it needs it.
|
||||
//This proc blocks(sleeps) unless verify is set to false
|
||||
/proc/send_asset(var/client/client, var/asset_name, var/verify = TRUE)
|
||||
if(!istype(client))
|
||||
if(ismob(client))
|
||||
var/mob/M = client
|
||||
if(M.client)
|
||||
client = M.client
|
||||
|
||||
else
|
||||
return 0
|
||||
|
||||
else
|
||||
return 0
|
||||
|
||||
if(client.cache.Find(asset_name) || client.sending.Find(asset_name))
|
||||
return 0
|
||||
|
||||
client << browse_rsc(SSasset.cache[asset_name], asset_name)
|
||||
if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip.
|
||||
if (client)
|
||||
client.cache += asset_name
|
||||
return 1
|
||||
if (!client)
|
||||
return 0
|
||||
|
||||
client.sending |= asset_name
|
||||
var/job = ++client.last_asset_job
|
||||
|
||||
client << browse({"
|
||||
<script>
|
||||
window.location.href="?asset_cache_confirm_arrival=[job]"
|
||||
</script>
|
||||
"}, "window=asset_cache_browser")
|
||||
|
||||
var/t = 0
|
||||
var/timeout_time = (ASSET_CACHE_SEND_TIMEOUT * client.sending.len) + ASSET_CACHE_SEND_TIMEOUT
|
||||
while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
|
||||
sleep(1) // Lock up the caller until this is received.
|
||||
t++
|
||||
|
||||
if(client)
|
||||
client.sending -= asset_name
|
||||
client.cache |= asset_name
|
||||
client.completed_asset_jobs -= job
|
||||
|
||||
return 1
|
||||
|
||||
//This proc blocks(sleeps) unless verify is set to false
|
||||
/proc/send_asset_list(var/client/client, var/list/asset_list, var/verify = TRUE)
|
||||
if(!istype(client))
|
||||
if(ismob(client))
|
||||
var/mob/M = client
|
||||
if(M.client)
|
||||
client = M.client
|
||||
|
||||
else
|
||||
return 0
|
||||
|
||||
else
|
||||
return 0
|
||||
|
||||
var/list/unreceived = asset_list - (client.cache + client.sending)
|
||||
if(!unreceived || !unreceived.len)
|
||||
return 0
|
||||
if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
|
||||
client << "Sending Resources..."
|
||||
for(var/asset in unreceived)
|
||||
if (asset in SSasset.cache)
|
||||
client << browse_rsc(SSasset.cache[asset], asset)
|
||||
|
||||
if(!verify || !winexists(client, "asset_cache_browser")) // Can't access the asset cache browser, rip.
|
||||
if (client)
|
||||
client.cache += unreceived
|
||||
return 1
|
||||
if (!client)
|
||||
return 0
|
||||
client.sending |= unreceived
|
||||
var/job = ++client.last_asset_job
|
||||
|
||||
client << browse({"
|
||||
<script>
|
||||
window.location.href="?asset_cache_confirm_arrival=[job]"
|
||||
</script>
|
||||
"}, "window=asset_cache_browser")
|
||||
|
||||
var/t = 0
|
||||
var/timeout_time = ASSET_CACHE_SEND_TIMEOUT * client.sending.len
|
||||
while(client && !client.completed_asset_jobs.Find(job) && t < timeout_time) // Reception is handled in Topic()
|
||||
sleep(1) // Lock up the caller until this is received.
|
||||
t++
|
||||
|
||||
if(client)
|
||||
client.sending -= unreceived
|
||||
client.cache |= unreceived
|
||||
client.completed_asset_jobs -= job
|
||||
|
||||
return 1
|
||||
|
||||
//This proc will download the files without clogging up the browse() queue, used for passively sending files on connection start.
|
||||
//The proc calls procs that sleep for long times.
|
||||
/proc/getFilesSlow(var/client/client, var/list/files, var/register_asset = TRUE)
|
||||
for(var/file in files)
|
||||
if (!client)
|
||||
break
|
||||
if (register_asset)
|
||||
register_asset(file,files[file])
|
||||
send_asset(client,file)
|
||||
sleep(0) //queuing calls like this too quickly can cause issues in some client versions
|
||||
|
||||
//This proc "registers" an asset, it adds it to the cache for further use, you cannot touch it from this point on or you'll fuck things up.
|
||||
//if it's an icon or something be careful, you'll have to copy it before further use.
|
||||
/proc/register_asset(var/asset_name, var/asset)
|
||||
SSasset.cache[asset_name] = asset
|
||||
|
||||
//These datums are used to populate the asset cache, the proc "register()" does this.
|
||||
|
||||
//all of our asset datums, used for referring to these later
|
||||
/var/global/list/asset_datums = list()
|
||||
|
||||
//get a assetdatum or make a new one
|
||||
/proc/get_asset_datum(var/type)
|
||||
if (!(type in asset_datums))
|
||||
return new type()
|
||||
return asset_datums[type]
|
||||
|
||||
/datum/asset/New()
|
||||
asset_datums[type] = src
|
||||
|
||||
/datum/asset/proc/register()
|
||||
return
|
||||
|
||||
/datum/asset/proc/send(client)
|
||||
return
|
||||
|
||||
//If you don't need anything complicated.
|
||||
/datum/asset/simple
|
||||
var/assets = list()
|
||||
var/verify = FALSE
|
||||
|
||||
/datum/asset/simple/register()
|
||||
for(var/asset_name in assets)
|
||||
register_asset(asset_name, assets[asset_name])
|
||||
/datum/asset/simple/send(client)
|
||||
send_asset_list(client,assets,verify)
|
||||
|
||||
|
||||
//DEFINITIONS FOR ASSET DATUMS START HERE.
|
||||
|
||||
|
||||
/datum/asset/simple/tgui
|
||||
assets = list(
|
||||
"tgui.css" = 'tgui/assets/tgui.css',
|
||||
"tgui.js" = 'tgui/assets/tgui.js'
|
||||
)
|
||||
|
||||
/datum/asset/simple/pda
|
||||
assets = list(
|
||||
"pda_atmos.png" = 'icons/pda_icons/pda_atmos.png',
|
||||
"pda_back.png" = 'icons/pda_icons/pda_back.png',
|
||||
"pda_bell.png" = 'icons/pda_icons/pda_bell.png',
|
||||
"pda_blank.png" = 'icons/pda_icons/pda_blank.png',
|
||||
"pda_boom.png" = 'icons/pda_icons/pda_boom.png',
|
||||
"pda_bucket.png" = 'icons/pda_icons/pda_bucket.png',
|
||||
"pda_medbot.png" = 'icons/pda_icons/pda_medbot.png',
|
||||
"pda_floorbot.png" = 'icons/pda_icons/pda_floorbot.png',
|
||||
"pda_cleanbot.png" = 'icons/pda_icons/pda_cleanbot.png',
|
||||
"pda_crate.png" = 'icons/pda_icons/pda_crate.png',
|
||||
"pda_cuffs.png" = 'icons/pda_icons/pda_cuffs.png',
|
||||
"pda_eject.png" = 'icons/pda_icons/pda_eject.png',
|
||||
"pda_flashlight.png" = 'icons/pda_icons/pda_flashlight.png',
|
||||
"pda_honk.png" = 'icons/pda_icons/pda_honk.png',
|
||||
"pda_mail.png" = 'icons/pda_icons/pda_mail.png',
|
||||
"pda_medical.png" = 'icons/pda_icons/pda_medical.png',
|
||||
"pda_menu.png" = 'icons/pda_icons/pda_menu.png',
|
||||
"pda_mule.png" = 'icons/pda_icons/pda_mule.png',
|
||||
"pda_notes.png" = 'icons/pda_icons/pda_notes.png',
|
||||
"pda_power.png" = 'icons/pda_icons/pda_power.png',
|
||||
"pda_rdoor.png" = 'icons/pda_icons/pda_rdoor.png',
|
||||
"pda_reagent.png" = 'icons/pda_icons/pda_reagent.png',
|
||||
"pda_refresh.png" = 'icons/pda_icons/pda_refresh.png',
|
||||
"pda_scanner.png" = 'icons/pda_icons/pda_scanner.png',
|
||||
"pda_signaler.png" = 'icons/pda_icons/pda_signaler.png',
|
||||
"pda_status.png" = 'icons/pda_icons/pda_status.png'
|
||||
)
|
||||
|
||||
/datum/asset/simple/paper
|
||||
assets = list(
|
||||
"large_stamp-clown.png" = 'icons/stamp_icons/large_stamp-clown.png',
|
||||
"large_stamp-deny.png" = 'icons/stamp_icons/large_stamp-deny.png',
|
||||
"large_stamp-ok.png" = 'icons/stamp_icons/large_stamp-ok.png',
|
||||
"large_stamp-hop.png" = 'icons/stamp_icons/large_stamp-hop.png',
|
||||
"large_stamp-cmo.png" = 'icons/stamp_icons/large_stamp-cmo.png',
|
||||
"large_stamp-ce.png" = 'icons/stamp_icons/large_stamp-ce.png',
|
||||
"large_stamp-hos.png" = 'icons/stamp_icons/large_stamp-hos.png',
|
||||
"large_stamp-rd.png" = 'icons/stamp_icons/large_stamp-rd.png',
|
||||
"large_stamp-cap.png" = 'icons/stamp_icons/large_stamp-cap.png',
|
||||
"large_stamp-qm.png" = 'icons/stamp_icons/large_stamp-qm.png',
|
||||
"large_stamp-law.png" = 'icons/stamp_icons/large_stamp-law.png'
|
||||
)
|
||||
|
||||
|
||||
//Registers HTML Interface assets.
|
||||
/datum/asset/HTML_interface/register()
|
||||
for(var/path in typesof(/datum/html_interface))
|
||||
var/datum/html_interface/hi = new path()
|
||||
hi.registerResources()
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
/*
|
||||
Client Colour Priority System By RemieRichards
|
||||
A System that gives finer control over which client.colour value to display on screen
|
||||
so that the "highest priority" one is always displayed as opposed to the default of
|
||||
"whichever was set last is displayed"
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Define subtypes of this datum
|
||||
*/
|
||||
/datum/client_colour
|
||||
var/colour = "" //Any client.color-valid value
|
||||
var/priority = 1 //Since only one client.color can be rendered on screen, we take the one with the highest priority value:
|
||||
//eg: "Bloody screen" > "goggles colour" as the former is much more important
|
||||
|
||||
|
||||
/mob
|
||||
var/list/client_colours = list()
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Adds an instance of colour_type to the mob's client_colours list
|
||||
colour_type - a typepath (subtyped from /datum/client_colour)
|
||||
*/
|
||||
/mob/proc/add_client_colour(colour_type)
|
||||
if(!ispath(/datum/client_colour))
|
||||
return
|
||||
|
||||
var/datum/client_colour/CC = new colour_type()
|
||||
client_colours |= CC
|
||||
sortTim(client_colours, /proc/cmp_clientcolour_priority)
|
||||
update_client_colour()
|
||||
|
||||
|
||||
/*
|
||||
Removes an instance of colour_type from the mob's client_colours list
|
||||
colour_type - a typepath (subtyped from /datum/client_colour)
|
||||
*/
|
||||
/mob/proc/remove_client_colour(colour_type)
|
||||
if(!ispath(/datum/client_colour))
|
||||
return
|
||||
|
||||
for(var/cc in client_colours)
|
||||
var/datum/client_colour/CC = cc
|
||||
if(CC.type == colour_type)
|
||||
client_colours -= CC
|
||||
qdel(CC)
|
||||
break
|
||||
update_client_colour()
|
||||
|
||||
|
||||
/*
|
||||
Resets the mob's client.color to null, and then sets it to the highest priority
|
||||
client_colour datum, if one exists
|
||||
*/
|
||||
/mob/proc/update_client_colour()
|
||||
if(!client)
|
||||
return
|
||||
client.color = ""
|
||||
if(!client_colours.len)
|
||||
return
|
||||
var/datum/client_colour/CC = client_colours[1]
|
||||
if(CC)
|
||||
client.color = CC.colour
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
|
||||
/client
|
||||
////////////////
|
||||
//ADMIN THINGS//
|
||||
////////////////
|
||||
var/datum/admins/holder = null
|
||||
var/datum/click_intercept = null // Needs to implement InterceptClickOn(user,params,atom) proc
|
||||
var/AI_Interact = 0
|
||||
|
||||
var/jobbancache = null //Used to cache this client's jobbans to save on DB queries
|
||||
var/last_message = "" //Contains the last message sent by this client - used to protect against copy-paste spamming.
|
||||
var/last_message_count = 0 //contins a number of how many times a message identical to last_message was sent.
|
||||
|
||||
/////////
|
||||
//OTHER//
|
||||
/////////
|
||||
var/datum/preferences/prefs = null
|
||||
var/move_delay = 1
|
||||
var/moving = null
|
||||
|
||||
var/area = null
|
||||
|
||||
///////////////
|
||||
//SOUND STUFF//
|
||||
///////////////
|
||||
var/ambience_playing= null
|
||||
var/played = 0
|
||||
////////////
|
||||
//SECURITY//
|
||||
////////////
|
||||
// comment out the line below when debugging locally to enable the options & messages menu
|
||||
control_freak = 1
|
||||
|
||||
////////////////////////////////////
|
||||
//things that require the database//
|
||||
////////////////////////////////////
|
||||
var/player_age = "Requires database" //So admins know why it isn't working - Used to determine how old the account is - in days.
|
||||
var/related_accounts_ip = "Requires database" //So admins know why it isn't working - Used to determine what other accounts previously logged in from this ip
|
||||
var/related_accounts_cid = "Requires database" //So admins know why it isn't working - Used to determine what other accounts previously logged in from this computer id
|
||||
|
||||
preload_rsc = PRELOAD_RSC
|
||||
|
||||
var/global/obj/screen/click_catcher/void
|
||||
|
||||
// Used by html_interface module.
|
||||
var/hi_last_pos
|
||||
|
||||
var/ip_intel = "Disabled"
|
||||
|
||||
//datum that controls the displaying and hiding of tooltips
|
||||
var/datum/tooltip/tooltips
|
||||
|
||||
//Used for var edit flagging, also defined in datums (clients are not a child of datums for some reason)
|
||||
var/var_edited = 0
|
||||
@@ -0,0 +1,383 @@
|
||||
////////////
|
||||
//SECURITY//
|
||||
////////////
|
||||
#define UPLOAD_LIMIT 1048576 //Restricts client uploads to the server to 1MB //Could probably do with being lower.
|
||||
|
||||
/*
|
||||
When somebody clicks a link in game, this Topic is called first.
|
||||
It does the stuff in this proc and then is redirected to the Topic() proc for the src=[0xWhatever]
|
||||
(if specified in the link). ie locate(hsrc).Topic()
|
||||
|
||||
Such links can be spoofed.
|
||||
|
||||
Because of this certain things MUST be considered whenever adding a Topic() for something:
|
||||
- Can it be fed harmful values which could cause runtimes?
|
||||
- Is the Topic call an admin-only thing?
|
||||
- If so, does it have checks to see if the person who called it (usr.client) is an admin?
|
||||
- Are the processes being called by Topic() particularly laggy?
|
||||
- If so, is there any protection against somebody spam-clicking a link?
|
||||
If you have any questions about this stuff feel free to ask. ~Carn
|
||||
*/
|
||||
/client/Topic(href, href_list, hsrc)
|
||||
if(!usr || usr != mob) //stops us calling Topic for somebody else's client. Also helps prevent usr=null
|
||||
return
|
||||
// asset_cache
|
||||
if(href_list["asset_cache_confirm_arrival"])
|
||||
//src << "ASSET JOB [href_list["asset_cache_confirm_arrival"]] ARRIVED."
|
||||
var/job = text2num(href_list["asset_cache_confirm_arrival"])
|
||||
completed_asset_jobs += job
|
||||
return
|
||||
// Admin PM
|
||||
if(href_list["priv_msg"])
|
||||
if (href_list["ahelp_reply"])
|
||||
cmd_ahelp_reply(href_list["priv_msg"])
|
||||
return
|
||||
cmd_admin_pm(href_list["priv_msg"],null)
|
||||
return
|
||||
|
||||
//Logs all hrefs
|
||||
if(config && config.log_hrefs && href_logfile)
|
||||
href_logfile << "<small>[time2text(world.timeofday,"hh:mm")] [src] (usr:[usr])</small> || [hsrc ? "[hsrc] " : ""][href]<br>"
|
||||
|
||||
switch(href_list["_src_"])
|
||||
if("holder")
|
||||
hsrc = holder
|
||||
if("usr")
|
||||
hsrc = mob
|
||||
if("prefs")
|
||||
return prefs.process_link(usr,href_list)
|
||||
if("vars")
|
||||
return view_var_Topic(href,href_list,hsrc)
|
||||
|
||||
..() //redirect to hsrc.Topic()
|
||||
|
||||
/client/proc/is_content_unlocked()
|
||||
if(!prefs.unlock_content)
|
||||
src << "Become a BYOND member to access member-perks and features, as well as support the engine that makes this game possible. Only 10 bucks for 3 months! <a href='http://www.byond.com/membership'>Click Here to find out more</a>."
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/client/proc/handle_spam_prevention(message, mute_type)
|
||||
if(config.automute_on && !holder && src.last_message == message)
|
||||
src.last_message_count++
|
||||
if(src.last_message_count >= SPAM_TRIGGER_AUTOMUTE)
|
||||
src << "<span class='danger'>You have exceeded the spam filter limit for identical messages. An auto-mute was applied.</span>"
|
||||
cmd_admin_mute(src, mute_type, 1)
|
||||
return 1
|
||||
if(src.last_message_count >= SPAM_TRIGGER_WARNING)
|
||||
src << "<span class='danger'>You are nearing the spam filter limit for identical messages.</span>"
|
||||
return 0
|
||||
else
|
||||
last_message = message
|
||||
src.last_message_count = 0
|
||||
return 0
|
||||
|
||||
//This stops files larger than UPLOAD_LIMIT being sent from client to server via input(), client.Import() etc.
|
||||
/client/AllowUpload(filename, filelength)
|
||||
if(filelength > UPLOAD_LIMIT)
|
||||
src << "<font color='red'>Error: AllowUpload(): File Upload too large. Upload Limit: [UPLOAD_LIMIT/1024]KiB.</font>"
|
||||
return 0
|
||||
/* //Don't need this at the moment. But it's here if it's needed later.
|
||||
//Helps prevent multiple files being uploaded at once. Or right after eachother.
|
||||
var/time_to_wait = fileaccess_timer - world.time
|
||||
if(time_to_wait > 0)
|
||||
src << "<font color='red'>Error: AllowUpload(): Spam prevention. Please wait [round(time_to_wait/10)] seconds.</font>"
|
||||
return 0
|
||||
fileaccess_timer = world.time + FTPDELAY */
|
||||
return 1
|
||||
|
||||
|
||||
///////////
|
||||
//CONNECT//
|
||||
///////////
|
||||
#if (PRELOAD_RSC == 0)
|
||||
var/list/external_rsc_urls
|
||||
var/next_external_rsc = 0
|
||||
#endif
|
||||
|
||||
|
||||
/client/New(TopicData)
|
||||
|
||||
TopicData = null //Prevent calls to client.Topic from connect
|
||||
|
||||
if(connection != "seeker" && connection != "web")//Invalid connection type.
|
||||
return null
|
||||
|
||||
#if (PRELOAD_RSC == 0)
|
||||
if(external_rsc_urls && external_rsc_urls.len)
|
||||
next_external_rsc = Wrap(next_external_rsc+1, 1, external_rsc_urls.len+1)
|
||||
preload_rsc = external_rsc_urls[next_external_rsc]
|
||||
#endif
|
||||
|
||||
clients += src
|
||||
directory[ckey] = src
|
||||
|
||||
//Admin Authorisation
|
||||
var/localhost_addresses = list("127.0.0.1", "::1")
|
||||
if(address && (address in localhost_addresses))
|
||||
var/datum/admin_rank/localhost_rank = new("!localhost!", 65535)
|
||||
if(localhost_rank)
|
||||
var/datum/admins/localhost_holder = new(localhost_rank, ckey)
|
||||
localhost_holder.associate(src)
|
||||
if(protected_config.autoadmin)
|
||||
if(!admin_datums[ckey])
|
||||
var/datum/admin_rank/autorank
|
||||
for(var/datum/admin_rank/R in admin_ranks)
|
||||
if(R.name == protected_config.autoadmin_rank)
|
||||
autorank = R
|
||||
break
|
||||
if(!autorank)
|
||||
world << "Autoadmin rank not found"
|
||||
else
|
||||
var/datum/admins/D = new(autorank, ckey)
|
||||
admin_datums[ckey] = D
|
||||
holder = admin_datums[ckey]
|
||||
if(holder)
|
||||
admins |= src
|
||||
holder.owner = src
|
||||
|
||||
//preferences datum - also holds some persistant data for the client (because we may as well keep these datums to a minimum)
|
||||
prefs = preferences_datums[ckey]
|
||||
if(!prefs)
|
||||
prefs = new /datum/preferences(src)
|
||||
preferences_datums[ckey] = prefs
|
||||
prefs.last_ip = address //these are gonna be used for banning
|
||||
prefs.last_id = computer_id //these are gonna be used for banning
|
||||
|
||||
. = ..() //calls mob.Login()
|
||||
|
||||
if (byond_version < config.client_error_version) //Out of date client.
|
||||
src << "<span class='danger'><b>Your version of byond is too old:</b></span>"
|
||||
src << config.client_error_message
|
||||
src << "Your version: [byond_version]"
|
||||
src << "Required version: [config.client_error_version] or later"
|
||||
src << "Visit http://www.byond.com/download/ to get the latest version of byond."
|
||||
if (holder)
|
||||
src << "Because you are an admin, you are being allowed to walk past this limitation, But it is still STRONGLY suggested you upgrade"
|
||||
else
|
||||
del(src)
|
||||
return 0
|
||||
else if (byond_version < config.client_warn_version) //We have words for this client.
|
||||
src << "<span class='danger'><b>Your version of byond may be getting out of date:</b></span>"
|
||||
src << config.client_warn_message
|
||||
src << "Your version: [byond_version]"
|
||||
src << "Required version to remove this message: [config.client_warn_version] or later"
|
||||
src << "Visit http://www.byond.com/download/ to get the latest version of byond."
|
||||
|
||||
if (connection == "web")
|
||||
if (!config.allowwebclient)
|
||||
src << "Web client is disabled"
|
||||
del(src)
|
||||
return 0
|
||||
if (config.webclientmembersonly && !IsByondMember())
|
||||
src << "Sorry, but the web client is restricted to byond members only."
|
||||
del(src)
|
||||
return 0
|
||||
|
||||
if( (world.address == address || !address) && !host )
|
||||
host = key
|
||||
world.update_status()
|
||||
|
||||
if(holder)
|
||||
add_admin_verbs()
|
||||
admin_memo_output("Show")
|
||||
adminGreet()
|
||||
if((global.comms_key == "default_pwd" || length(global.comms_key) <= 6) && global.comms_allowed) //It's the default value or less than 6 characters long, but it somehow didn't disable comms.
|
||||
src << "<span class='danger'>The server's API key is either too short or is the default value! Consider changing it immediately!</span>"
|
||||
|
||||
add_verbs_from_config()
|
||||
set_client_age_from_db()
|
||||
|
||||
if (isnum(player_age) && player_age == -1) //first connection
|
||||
if (config.panic_bunker && !holder && !(ckey in deadmins))
|
||||
log_access("Failed Login: [key] - New account attempting to connect during panic bunker")
|
||||
message_admins("<span class='adminnotice'>Failed Login: [key] - New account attempting to connect during panic bunker</span>")
|
||||
src << "Sorry but the server is currently not accepting connections from never before seen players."
|
||||
del(src)
|
||||
return 0
|
||||
|
||||
if (config.notify_new_player_age >= 0)
|
||||
message_admins("New user: [key_name_admin(src)] is connecting here for the first time.")
|
||||
if (config.irc_first_connection_alert)
|
||||
send2irc_adminless_only("New-user", "[key_name(src)] is connecting for the first time!")
|
||||
|
||||
player_age = 0 // set it from -1 to 0 so the job selection code doesn't have a panic attack
|
||||
|
||||
else if (isnum(player_age) && player_age < config.notify_new_player_age)
|
||||
message_admins("New user: [key_name_admin(src)] just connected with an age of [player_age] day[(player_age==1?"":"s")]")
|
||||
|
||||
if(!IsGuestKey(key) && dbcon.IsConnected())
|
||||
findJoinDate()
|
||||
|
||||
sync_client_with_db()
|
||||
|
||||
check_ip_intel()
|
||||
|
||||
send_resources()
|
||||
|
||||
if(!void)
|
||||
void = new()
|
||||
void = void.MakeGreed()
|
||||
|
||||
screen += void
|
||||
|
||||
if(prefs.lastchangelog != changelog_hash) //bolds the changelog button on the interface so we know there are updates.
|
||||
src << "<span class='info'>You have unread updates in the changelog.</span>"
|
||||
if(config.aggressive_changelog)
|
||||
changelog()
|
||||
else
|
||||
winset(src, "infowindow.changelog", "font-style=bold")
|
||||
|
||||
if(ckey in clientmessages)
|
||||
for(var/message in clientmessages[ckey])
|
||||
src << message
|
||||
clientmessages.Remove(ckey)
|
||||
|
||||
if(config && config.autoconvert_notes)
|
||||
convert_notes_sql(ckey)
|
||||
|
||||
if(!winexists(src, "asset_cache_browser")) // The client is using a custom skin, tell them.
|
||||
src << "<span class='warning'>Unable to access asset cache browser, if you are using a custom skin file, please allow DS to download the updated version, if you are not, then make a bug report. This is not a critical issue but can cause issues with resource downloading, as it is impossible to know when extra resources arrived to you.</span>"
|
||||
|
||||
|
||||
//This is down here because of the browse() calls in tooltip/New()
|
||||
if(!tooltips)
|
||||
tooltips = new /datum/tooltip(src)
|
||||
|
||||
|
||||
//////////////
|
||||
//DISCONNECT//
|
||||
//////////////
|
||||
|
||||
/client/Del()
|
||||
if(holder)
|
||||
adminGreet(1)
|
||||
holder.owner = null
|
||||
admins -= src
|
||||
directory -= ckey
|
||||
clients -= src
|
||||
return ..()
|
||||
|
||||
|
||||
/client/proc/set_client_age_from_db()
|
||||
if (IsGuestKey(src.key))
|
||||
return
|
||||
|
||||
establish_db_connection()
|
||||
if(!dbcon.IsConnected())
|
||||
return
|
||||
|
||||
var/sql_ckey = sanitizeSQL(src.ckey)
|
||||
|
||||
var/DBQuery/query = dbcon.NewQuery("SELECT id, datediff(Now(),firstseen) as age FROM [format_table_name("player")] WHERE ckey = '[sql_ckey]'")
|
||||
if (!query.Execute())
|
||||
return
|
||||
|
||||
while (query.NextRow())
|
||||
player_age = text2num(query.item[2])
|
||||
return
|
||||
|
||||
//no match mark it as a first connection for use in client/New()
|
||||
player_age = -1
|
||||
|
||||
|
||||
/client/proc/sync_client_with_db()
|
||||
if (IsGuestKey(src.key))
|
||||
return
|
||||
|
||||
establish_db_connection()
|
||||
if (!dbcon.IsConnected())
|
||||
return
|
||||
|
||||
var/sql_ckey = sanitizeSQL(src.ckey)
|
||||
|
||||
var/DBQuery/query_ip = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE ip = '[address]' AND ckey != '[sql_ckey]'")
|
||||
query_ip.Execute()
|
||||
related_accounts_ip = ""
|
||||
while(query_ip.NextRow())
|
||||
related_accounts_ip += "[query_ip.item[1]], "
|
||||
|
||||
var/DBQuery/query_cid = dbcon.NewQuery("SELECT ckey FROM [format_table_name("player")] WHERE computerid = '[computer_id]' AND ckey != '[sql_ckey]'")
|
||||
query_cid.Execute()
|
||||
related_accounts_cid = ""
|
||||
while (query_cid.NextRow())
|
||||
related_accounts_cid += "[query_cid.item[1]], "
|
||||
|
||||
var/watchreason = check_watchlist(sql_ckey)
|
||||
if(watchreason)
|
||||
message_admins("<font color='red'><B>Notice: </B></font><font color='blue'>[key_name_admin(src)] is on the watchlist and has just connected - Reason: [watchreason]</font>")
|
||||
send2irc_adminless_only("Watchlist", "[key_name(src)] is on the watchlist and has just connected - Reason: [watchreason]")
|
||||
|
||||
var/admin_rank = "Player"
|
||||
if (src.holder && src.holder.rank)
|
||||
admin_rank = src.holder.rank.name
|
||||
|
||||
var/sql_ip = sanitizeSQL(src.address)
|
||||
var/sql_computerid = sanitizeSQL(src.computer_id)
|
||||
var/sql_admin_rank = sanitizeSQL(admin_rank)
|
||||
|
||||
|
||||
var/DBQuery/query_insert = dbcon.NewQuery("INSERT INTO [format_table_name("player")] (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, '[sql_ckey]', Now(), Now(), '[sql_ip]', '[sql_computerid]', '[sql_admin_rank]') ON DUPLICATE KEY UPDATE lastseen = VALUES(lastseen), ip = VALUES(ip), computerid = VALUES(computerid), lastadminrank = VALUES(lastadminrank)")
|
||||
query_insert.Execute()
|
||||
|
||||
//Logging player access
|
||||
var/serverip = "[world.internet_address]:[world.port]"
|
||||
var/DBQuery/query_accesslog = dbcon.NewQuery("INSERT INTO `[format_table_name("connection_log")]` (`id`,`datetime`,`serverip`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),'[serverip]','[sql_ckey]','[sql_ip]','[sql_computerid]');")
|
||||
query_accesslog.Execute()
|
||||
|
||||
/client/proc/check_ip_intel()
|
||||
set waitfor = 0 //we sleep when getting the intel, no need to hold up the client connection while we sleep
|
||||
if (config.ipintel_email)
|
||||
var/datum/ipintel/res = get_ip_intel(address)
|
||||
if (res.intel >= config.ipintel_rating_bad)
|
||||
message_admins("<span class='adminnotice'>Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a Proxy/VPN.</span>")
|
||||
ip_intel = res.intel
|
||||
|
||||
|
||||
/client/proc/add_verbs_from_config()
|
||||
if(config.see_own_notes)
|
||||
verbs += /client/proc/self_notes
|
||||
|
||||
|
||||
#undef TOPIC_SPAM_DELAY
|
||||
#undef UPLOAD_LIMIT
|
||||
#undef MIN_CLIENT_VERSION
|
||||
|
||||
//checks if a client is afk
|
||||
//3000 frames = 5 minutes
|
||||
/client/proc/is_afk(duration=3000)
|
||||
if(inactivity > duration)
|
||||
return inactivity
|
||||
return 0
|
||||
|
||||
// Byond seemingly calls stat, each tick.
|
||||
// Calling things each tick can get expensive real quick.
|
||||
// So we slow this down a little.
|
||||
// See: http://www.byond.com/docs/ref/info.html#/client/proc/Stat
|
||||
/client/Stat()
|
||||
. = ..()
|
||||
if (holder)
|
||||
sleep(1)
|
||||
else
|
||||
sleep(5)
|
||||
stoplag()
|
||||
|
||||
//send resources to the client. It's here in its own proc so we can move it around easiliy if need be
|
||||
/client/proc/send_resources()
|
||||
//get the common files
|
||||
getFiles(
|
||||
'html/search.js',
|
||||
'html/panels.css',
|
||||
'html/browser/common.css',
|
||||
'html/browser/scannernew.css',
|
||||
'html/browser/playeroptions.css',
|
||||
)
|
||||
spawn (10) //removing this spawn causes all clients to not get verbs.
|
||||
//Precache the client with all other assets slowly, so as to not block other browse() calls
|
||||
getFilesSlow(src, SSasset.cache, register_asset = FALSE)
|
||||
|
||||
|
||||
//Hook, override it to run code when dir changes
|
||||
//Like for /atoms, but clients are their own snowflake FUCK
|
||||
/client/proc/setDir(newdir)
|
||||
dir = newdir
|
||||
@@ -0,0 +1,9 @@
|
||||
var/list/clientmessages = list()
|
||||
|
||||
proc/addclientmessage(var/ckey, var/message)
|
||||
ckey = ckey(ckey)
|
||||
if (!ckey || !message)
|
||||
return
|
||||
if (!(ckey in clientmessages))
|
||||
clientmessages[ckey] = list()
|
||||
clientmessages[ckey] += message
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,486 @@
|
||||
//This is the lowest supported version, anything below this is completely obsolete and the entire savefile will be wiped.
|
||||
#define SAVEFILE_VERSION_MIN 8
|
||||
|
||||
//This is the current version, anything below this will attempt to update (if it's not obsolete)
|
||||
#define SAVEFILE_VERSION_MAX 15
|
||||
/*
|
||||
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
|
||||
This proc checks if the current directory of the savefile S needs updating
|
||||
It is to be used by the load_character and load_preferences procs.
|
||||
(S.cd=="/" is preferences, S.cd=="/character[integer]" is a character slot, etc)
|
||||
|
||||
if the current directory's version is below SAVEFILE_VERSION_MIN it will simply wipe everything in that directory
|
||||
(if we're at root "/" then it'll just wipe the entire savefile, for instance.)
|
||||
|
||||
if its version is below SAVEFILE_VERSION_MAX but above the minimum, it will load data but later call the
|
||||
respective update_preferences() or update_character() proc.
|
||||
Those procs allow coders to specify format changes so users do not lose their setups and have to redo them again.
|
||||
|
||||
Failing all that, the standard sanity checks are performed. They simply check the data is suitable, reverting to
|
||||
initial() values if necessary.
|
||||
*/
|
||||
/datum/preferences/proc/savefile_needs_update(savefile/S)
|
||||
var/savefile_version
|
||||
S["version"] >> savefile_version
|
||||
|
||||
if(savefile_version < SAVEFILE_VERSION_MIN)
|
||||
S.dir.Cut()
|
||||
return -2
|
||||
if(savefile_version < SAVEFILE_VERSION_MAX)
|
||||
return savefile_version
|
||||
return -1
|
||||
|
||||
|
||||
/datum/preferences/proc/update_antagchoices(current_version)
|
||||
if((!islist(be_special) || old_be_special ) && current_version < 12)
|
||||
//Archived values of when antag pref defines were a bitfield+fitflags
|
||||
var/B_traitor = 1
|
||||
var/B_operative = 2
|
||||
var/B_changeling = 4
|
||||
var/B_wizard = 8
|
||||
var/B_malf = 16
|
||||
var/B_rev = 32
|
||||
var/B_alien = 64
|
||||
var/B_pai = 128
|
||||
var/B_cultist = 256
|
||||
var/B_blob = 512
|
||||
var/B_ninja = 1024
|
||||
var/B_monkey = 2048
|
||||
var/B_gang = 4096
|
||||
var/B_abductor = 16384
|
||||
|
||||
var/list/archived = list(B_traitor,B_operative,B_changeling,B_wizard,B_malf,B_rev,B_alien,B_pai,B_cultist,B_blob,B_ninja,B_monkey,B_gang,B_abductor)
|
||||
|
||||
be_special = list()
|
||||
|
||||
for(var/flag in archived)
|
||||
if(old_be_special & flag)
|
||||
//this is shitty, but this proc should only be run once per player and then never again for the rest of eternity,
|
||||
switch(flag)
|
||||
if(1) //why aren't these the variables above? Good question, it's because byond complains the expression isn't constant, when it is.
|
||||
be_special += ROLE_TRAITOR
|
||||
if(2)
|
||||
be_special += ROLE_OPERATIVE
|
||||
if(4)
|
||||
be_special += ROLE_CHANGELING
|
||||
if(8)
|
||||
be_special += ROLE_WIZARD
|
||||
if(16)
|
||||
be_special += ROLE_MALF
|
||||
if(32)
|
||||
be_special += ROLE_REV
|
||||
if(64)
|
||||
be_special += ROLE_ALIEN
|
||||
if(128)
|
||||
be_special += ROLE_PAI
|
||||
if(256)
|
||||
be_special += ROLE_CULTIST
|
||||
if(512)
|
||||
be_special += ROLE_BLOB
|
||||
if(1024)
|
||||
be_special += ROLE_NINJA
|
||||
if(2048)
|
||||
be_special += ROLE_MONKEY
|
||||
if(4096)
|
||||
be_special += ROLE_GANG
|
||||
if(16384)
|
||||
be_special += ROLE_ABDUCTOR
|
||||
|
||||
|
||||
/datum/preferences/proc/update_preferences(current_version)
|
||||
if(current_version < 10)
|
||||
toggles |= MEMBER_PUBLIC
|
||||
if(current_version < 11)
|
||||
chat_toggles = TOGGLES_DEFAULT_CHAT
|
||||
toggles = TOGGLES_DEFAULT
|
||||
if(current_version < 12)
|
||||
ignoring = list()
|
||||
if(current_version < 15)
|
||||
toggles |= SOUND_ANNOUNCEMENTS
|
||||
|
||||
//should this proc get fairly long (say 3 versions long),
|
||||
//just increase SAVEFILE_VERSION_MIN so it's not as far behind
|
||||
//SAVEFILE_VERSION_MAX and then delete any obsolete if clauses
|
||||
//from this proc.
|
||||
//It's only really meant to avoid annoying frequent players
|
||||
//if your savefile is 3 months out of date, then 'tough shit'.
|
||||
/datum/preferences/proc/update_character(current_version)
|
||||
if(current_version < 9) //an example, underwear were an index for a hardcoded list, converting to a string
|
||||
if(gender == MALE)
|
||||
switch(underwear)
|
||||
if(1)
|
||||
underwear = "Mens White"
|
||||
if(2)
|
||||
underwear = "Mens Grey"
|
||||
if(3)
|
||||
underwear = "Mens Green"
|
||||
if(4)
|
||||
underwear = "Mens Blue"
|
||||
if(5)
|
||||
underwear = "Mens Black"
|
||||
if(6)
|
||||
underwear = "Mankini"
|
||||
if(7)
|
||||
underwear = "Mens Hearts Boxer"
|
||||
if(8)
|
||||
underwear = "Mens Black Boxer"
|
||||
if(9)
|
||||
underwear = "Mens Grey Boxer"
|
||||
if(10)
|
||||
underwear = "Mens Striped Boxer"
|
||||
if(11)
|
||||
underwear = "Mens Kinky"
|
||||
if(12)
|
||||
underwear = "Mens Red"
|
||||
if(13)
|
||||
underwear = "Nude"
|
||||
else
|
||||
switch(underwear)
|
||||
if(1)
|
||||
underwear = "Ladies Red"
|
||||
if(2)
|
||||
underwear = "Ladies White"
|
||||
if(3)
|
||||
underwear = "Ladies Yellow"
|
||||
if(4)
|
||||
underwear = "Ladies Blue"
|
||||
if(5)
|
||||
underwear = "Ladies Black"
|
||||
if(6)
|
||||
underwear = "Ladies Thong"
|
||||
if(7)
|
||||
underwear = "Babydoll"
|
||||
if(8)
|
||||
underwear = "Ladies Baby-Blue"
|
||||
if(9)
|
||||
underwear = "Ladies Green"
|
||||
if(10)
|
||||
underwear = "Ladies Pink"
|
||||
if(11)
|
||||
underwear = "Ladies Kinky"
|
||||
if(12)
|
||||
underwear = "Tankini"
|
||||
if(13)
|
||||
underwear = "Nude"
|
||||
|
||||
if(pref_species && !(pref_species.id in roundstart_species))
|
||||
var/rando_race = pick(config.roundstart_races)
|
||||
pref_species = new rando_race()
|
||||
|
||||
if(current_version < 13 || !istext(backbag))
|
||||
switch(backbag)
|
||||
if(2)
|
||||
backbag = DSATCHEL
|
||||
else
|
||||
backbag = DBACKPACK
|
||||
|
||||
|
||||
/datum/preferences/proc/load_path(ckey,filename="preferences.sav")
|
||||
if(!ckey)
|
||||
return
|
||||
path = "data/player_saves/[copytext(ckey,1,2)]/[ckey]/[filename]"
|
||||
|
||||
/datum/preferences/proc/load_preferences()
|
||||
if(!path)
|
||||
return 0
|
||||
if(!fexists(path))
|
||||
return 0
|
||||
|
||||
var/savefile/S = new /savefile(path)
|
||||
if(!S)
|
||||
return 0
|
||||
S.cd = "/"
|
||||
|
||||
var/needs_update = savefile_needs_update(S)
|
||||
if(needs_update == -2) //fatal, can't load any data
|
||||
return 0
|
||||
|
||||
//general preferences
|
||||
S["ooccolor"] >> ooccolor
|
||||
S["lastchangelog"] >> lastchangelog
|
||||
S["UI_style"] >> UI_style
|
||||
S["hotkeys"] >> hotkeys
|
||||
S["tgui_fancy"] >> tgui_fancy
|
||||
S["tgui_lock"] >> tgui_lock
|
||||
|
||||
if(islist(S["be_special"]))
|
||||
S["be_special"] >> be_special
|
||||
else //force update and store the old bitflag version of be_special
|
||||
needs_update = 11
|
||||
S["be_special"] >> old_be_special
|
||||
|
||||
S["default_slot"] >> default_slot
|
||||
S["chat_toggles"] >> chat_toggles
|
||||
S["toggles"] >> toggles
|
||||
S["ghost_form"] >> ghost_form
|
||||
S["ghost_orbit"] >> ghost_orbit
|
||||
S["ghost_accs"] >> ghost_accs
|
||||
S["ghost_others"] >> ghost_others
|
||||
S["preferred_map"] >> preferred_map
|
||||
S["ignoring"] >> ignoring
|
||||
S["ghost_hud"] >> ghost_hud
|
||||
S["inquisitive_ghost"] >> inquisitive_ghost
|
||||
|
||||
//try to fix any outdated data if necessary
|
||||
if(needs_update >= 0)
|
||||
update_preferences(needs_update) //needs_update = savefile_version if we need an update (positive integer)
|
||||
update_antagchoices(needs_update)
|
||||
|
||||
//Sanitize
|
||||
ooccolor = sanitize_ooccolor(sanitize_hexcolor(ooccolor, 6, 1, initial(ooccolor)))
|
||||
lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog))
|
||||
UI_style = sanitize_inlist(UI_style, list("Midnight", "Plasmafire", "Retro", "Slimecore", "Operative"), initial(UI_style))
|
||||
hotkeys = sanitize_integer(hotkeys, 0, 1, initial(hotkeys))
|
||||
tgui_fancy = sanitize_integer(tgui_fancy, 0, 1, initial(tgui_fancy))
|
||||
tgui_lock = sanitize_integer(tgui_lock, 0, 1, initial(tgui_lock))
|
||||
default_slot = sanitize_integer(default_slot, 1, max_save_slots, initial(default_slot))
|
||||
toggles = sanitize_integer(toggles, 0, 65535, initial(toggles))
|
||||
ghost_form = sanitize_inlist(ghost_form, ghost_forms, initial(ghost_form))
|
||||
ghost_orbit = sanitize_inlist(ghost_orbit, ghost_orbits, initial(ghost_orbit))
|
||||
ghost_accs = sanitize_inlist(ghost_accs, ghost_accs_options, GHOST_ACCS_DEFAULT_OPTION)
|
||||
ghost_others = sanitize_inlist(ghost_others, ghost_others_options, GHOST_OTHERS_DEFAULT_OPTION)
|
||||
|
||||
return 1
|
||||
|
||||
/datum/preferences/proc/save_preferences()
|
||||
if(!path)
|
||||
return 0
|
||||
var/savefile/S = new /savefile(path)
|
||||
if(!S)
|
||||
return 0
|
||||
S.cd = "/"
|
||||
|
||||
S["version"] << SAVEFILE_VERSION_MAX //updates (or failing that the sanity checks) will ensure data is not invalid at load. Assume up-to-date
|
||||
|
||||
//general preferences
|
||||
S["ooccolor"] << ooccolor
|
||||
S["lastchangelog"] << lastchangelog
|
||||
S["UI_style"] << UI_style
|
||||
S["hotkeys"] << hotkeys
|
||||
S["tgui_fancy"] << tgui_fancy
|
||||
S["tgui_lock"] << tgui_lock
|
||||
S["be_special"] << be_special
|
||||
S["default_slot"] << default_slot
|
||||
S["toggles"] << toggles
|
||||
S["chat_toggles"] << chat_toggles
|
||||
S["ghost_form"] << ghost_form
|
||||
S["ghost_orbit"] << ghost_orbit
|
||||
S["ghost_accs"] << ghost_accs
|
||||
S["ghost_others"] << ghost_others
|
||||
S["preferred_map"] << preferred_map
|
||||
S["ignoring"] << ignoring
|
||||
S["ghost_hud"] << ghost_hud
|
||||
S["inquisitive_ghost"] << inquisitive_ghost
|
||||
|
||||
return 1
|
||||
|
||||
/datum/preferences/proc/load_character(slot)
|
||||
if(!path)
|
||||
return 0
|
||||
if(!fexists(path))
|
||||
return 0
|
||||
var/savefile/S = new /savefile(path)
|
||||
if(!S)
|
||||
return 0
|
||||
S.cd = "/"
|
||||
if(!slot)
|
||||
slot = default_slot
|
||||
slot = sanitize_integer(slot, 1, max_save_slots, initial(default_slot))
|
||||
if(slot != default_slot)
|
||||
default_slot = slot
|
||||
S["default_slot"] << slot
|
||||
|
||||
S.cd = "/character[slot]"
|
||||
var/needs_update = savefile_needs_update(S)
|
||||
if(needs_update == -2) //fatal, can't load any data
|
||||
return 0
|
||||
|
||||
//Species
|
||||
var/species_id
|
||||
S["species"] >> species_id
|
||||
if(config.mutant_races && species_id && (species_id in roundstart_species))
|
||||
var/newtype = roundstart_species[species_id]
|
||||
pref_species = new newtype()
|
||||
else
|
||||
var/rando_race = pick(config.roundstart_races)
|
||||
pref_species = new rando_race()
|
||||
|
||||
if(!S["features["mcolor"]"] || S["features["mcolor"]"] == "#000")
|
||||
S["features["mcolor"]"] << "#FFF"
|
||||
|
||||
//Character
|
||||
S["OOC_Notes"] >> metadata
|
||||
S["real_name"] >> real_name
|
||||
S["name_is_always_random"] >> be_random_name
|
||||
S["body_is_always_random"] >> be_random_body
|
||||
S["gender"] >> gender
|
||||
S["age"] >> age
|
||||
S["hair_color"] >> hair_color
|
||||
S["facial_hair_color"] >> facial_hair_color
|
||||
S["eye_color"] >> eye_color
|
||||
S["skin_tone"] >> skin_tone
|
||||
S["hair_style_name"] >> hair_style
|
||||
S["facial_style_name"] >> facial_hair_style
|
||||
S["underwear"] >> underwear
|
||||
S["undershirt"] >> undershirt
|
||||
S["socks"] >> socks
|
||||
S["backbag"] >> backbag
|
||||
S["feature_mcolor"] >> features["mcolor"]
|
||||
S["feature_lizard_tail"] >> features["tail_lizard"]
|
||||
S["feature_lizard_snout"] >> features["snout"]
|
||||
S["feature_lizard_horns"] >> features["horns"]
|
||||
S["feature_lizard_frills"] >> features["frills"]
|
||||
S["feature_lizard_spines"] >> features["spines"]
|
||||
S["feature_lizard_body_markings"] >> features["body_markings"]
|
||||
if(!config.mutant_humans)
|
||||
features["tail_human"] = "none"
|
||||
features["ears"] = "none"
|
||||
else
|
||||
S["feature_human_tail"] >> features["tail_human"]
|
||||
S["feature_human_ears"] >> features["ears"]
|
||||
S["clown_name"] >> custom_names["clown"]
|
||||
S["mime_name"] >> custom_names["mime"]
|
||||
S["ai_name"] >> custom_names["ai"]
|
||||
S["cyborg_name"] >> custom_names["cyborg"]
|
||||
S["religion_name"] >> custom_names["religion"]
|
||||
S["deity_name"] >> custom_names["deity"]
|
||||
|
||||
//Jobs
|
||||
S["userandomjob"] >> userandomjob
|
||||
S["job_civilian_high"] >> job_civilian_high
|
||||
S["job_civilian_med"] >> job_civilian_med
|
||||
S["job_civilian_low"] >> job_civilian_low
|
||||
S["job_medsci_high"] >> job_medsci_high
|
||||
S["job_medsci_med"] >> job_medsci_med
|
||||
S["job_medsci_low"] >> job_medsci_low
|
||||
S["job_engsec_high"] >> job_engsec_high
|
||||
S["job_engsec_med"] >> job_engsec_med
|
||||
S["job_engsec_low"] >> job_engsec_low
|
||||
|
||||
//try to fix any outdated data if necessary
|
||||
if(needs_update >= 0)
|
||||
update_character(needs_update) //needs_update == savefile_version if we need an update (positive integer)
|
||||
|
||||
//Sanitize
|
||||
metadata = sanitize_text(metadata, initial(metadata))
|
||||
real_name = reject_bad_name(real_name)
|
||||
if(!features["mcolor"] || features["mcolor"] == "#000")
|
||||
features["mcolor"] = pick("FFFFFF","7F7F7F", "7FFF7F", "7F7FFF", "FF7F7F", "7FFFFF", "FF7FFF", "FFFF7F")
|
||||
if(!real_name)
|
||||
real_name = random_unique_name(gender)
|
||||
be_random_name = sanitize_integer(be_random_name, 0, 1, initial(be_random_name))
|
||||
be_random_body = sanitize_integer(be_random_body, 0, 1, initial(be_random_body))
|
||||
gender = sanitize_gender(gender)
|
||||
if(gender == MALE)
|
||||
hair_style = sanitize_inlist(hair_style, hair_styles_male_list)
|
||||
facial_hair_style = sanitize_inlist(facial_hair_style, facial_hair_styles_male_list)
|
||||
underwear = sanitize_inlist(underwear, underwear_m)
|
||||
undershirt = sanitize_inlist(undershirt, undershirt_m)
|
||||
else
|
||||
hair_style = sanitize_inlist(hair_style, hair_styles_female_list)
|
||||
facial_hair_style = sanitize_inlist(facial_hair_style, facial_hair_styles_female_list)
|
||||
underwear = sanitize_inlist(underwear, underwear_f)
|
||||
undershirt = sanitize_inlist(undershirt, undershirt_f)
|
||||
socks = sanitize_inlist(socks, socks_list)
|
||||
age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
|
||||
hair_color = sanitize_hexcolor(hair_color, 3, 0)
|
||||
facial_hair_color = sanitize_hexcolor(facial_hair_color, 3, 0)
|
||||
eye_color = sanitize_hexcolor(eye_color, 3, 0)
|
||||
skin_tone = sanitize_inlist(skin_tone, skin_tones)
|
||||
backbag = sanitize_inlist(backbag, backbaglist, initial(backbag))
|
||||
features["mcolor"] = sanitize_hexcolor(features["mcolor"], 3, 0)
|
||||
features["tail_lizard"] = sanitize_inlist(features["tail_lizard"], tails_list_lizard)
|
||||
features["tail_human"] = sanitize_inlist(features["tail_human"], tails_list_human, "None")
|
||||
features["snout"] = sanitize_inlist(features["snout"], snouts_list)
|
||||
features["horns"] = sanitize_inlist(features["horns"], horns_list)
|
||||
features["ears"] = sanitize_inlist(features["ears"], ears_list, "None")
|
||||
features["frills"] = sanitize_inlist(features["frills"], frills_list)
|
||||
features["spines"] = sanitize_inlist(features["spines"], spines_list)
|
||||
features["body_markings"] = sanitize_inlist(features["body_markings"], body_markings_list)
|
||||
|
||||
userandomjob = sanitize_integer(userandomjob, 0, 1, initial(userandomjob))
|
||||
job_civilian_high = sanitize_integer(job_civilian_high, 0, 65535, initial(job_civilian_high))
|
||||
job_civilian_med = sanitize_integer(job_civilian_med, 0, 65535, initial(job_civilian_med))
|
||||
job_civilian_low = sanitize_integer(job_civilian_low, 0, 65535, initial(job_civilian_low))
|
||||
job_medsci_high = sanitize_integer(job_medsci_high, 0, 65535, initial(job_medsci_high))
|
||||
job_medsci_med = sanitize_integer(job_medsci_med, 0, 65535, initial(job_medsci_med))
|
||||
job_medsci_low = sanitize_integer(job_medsci_low, 0, 65535, initial(job_medsci_low))
|
||||
job_engsec_high = sanitize_integer(job_engsec_high, 0, 65535, initial(job_engsec_high))
|
||||
job_engsec_med = sanitize_integer(job_engsec_med, 0, 65535, initial(job_engsec_med))
|
||||
job_engsec_low = sanitize_integer(job_engsec_low, 0, 65535, initial(job_engsec_low))
|
||||
|
||||
return 1
|
||||
|
||||
/datum/preferences/proc/save_character()
|
||||
if(!path)
|
||||
return 0
|
||||
var/savefile/S = new /savefile(path)
|
||||
if(!S)
|
||||
return 0
|
||||
S.cd = "/character[default_slot]"
|
||||
|
||||
S["version"] << SAVEFILE_VERSION_MAX //load_character will sanitize any bad data, so assume up-to-date.
|
||||
|
||||
//Character
|
||||
S["OOC_Notes"] << metadata
|
||||
S["real_name"] << real_name
|
||||
S["name_is_always_random"] << be_random_name
|
||||
S["body_is_always_random"] << be_random_body
|
||||
S["gender"] << gender
|
||||
S["age"] << age
|
||||
S["hair_color"] << hair_color
|
||||
S["facial_hair_color"] << facial_hair_color
|
||||
S["eye_color"] << eye_color
|
||||
S["skin_tone"] << skin_tone
|
||||
S["hair_style_name"] << hair_style
|
||||
S["facial_style_name"] << facial_hair_style
|
||||
S["underwear"] << underwear
|
||||
S["undershirt"] << undershirt
|
||||
S["socks"] << socks
|
||||
S["backbag"] << backbag
|
||||
S["species"] << pref_species.id
|
||||
S["feature_mcolor"] << features["mcolor"]
|
||||
S["feature_lizard_tail"] << features["tail_lizard"]
|
||||
S["feature_human_tail"] << features["tail_human"]
|
||||
S["feature_lizard_snout"] << features["snout"]
|
||||
S["feature_lizard_horns"] << features["horns"]
|
||||
S["feature_human_ears"] << features["ears"]
|
||||
S["feature_lizard_frills"] << features["frills"]
|
||||
S["feature_lizard_spines"] << features["spines"]
|
||||
S["feature_lizard_body_markings"] << features["body_markings"]
|
||||
S["clown_name"] << custom_names["clown"]
|
||||
S["mime_name"] << custom_names["mime"]
|
||||
S["ai_name"] << custom_names["ai"]
|
||||
S["cyborg_name"] << custom_names["cyborg"]
|
||||
S["religion_name"] << custom_names["religion"]
|
||||
S["deity_name"] << custom_names["deity"]
|
||||
|
||||
//Jobs
|
||||
S["userandomjob"] << userandomjob
|
||||
S["job_civilian_high"] << job_civilian_high
|
||||
S["job_civilian_med"] << job_civilian_med
|
||||
S["job_civilian_low"] << job_civilian_low
|
||||
S["job_medsci_high"] << job_medsci_high
|
||||
S["job_medsci_med"] << job_medsci_med
|
||||
S["job_medsci_low"] << job_medsci_low
|
||||
S["job_engsec_high"] << job_engsec_high
|
||||
S["job_engsec_med"] << job_engsec_med
|
||||
S["job_engsec_low"] << job_engsec_low
|
||||
|
||||
return 1
|
||||
|
||||
|
||||
#undef SAVEFILE_VERSION_MAX
|
||||
#undef SAVEFILE_VERSION_MIN
|
||||
/*
|
||||
//DEBUG
|
||||
//Some crude tools for testing savefiles
|
||||
//path is the savefile path
|
||||
/client/verb/savefile_export(path as text)
|
||||
var/savefile/S = new /savefile(path)
|
||||
S.ExportText("/",file("[path].txt"))
|
||||
//path is the savefile path
|
||||
/client/verb/savefile_import(path as text)
|
||||
var/savefile/S = new /savefile(path)
|
||||
S.ImportText("/",file("[path].txt"))
|
||||
*/
|
||||
@@ -0,0 +1,359 @@
|
||||
//toggles
|
||||
/client/verb/toggle_ghost_ears()
|
||||
set name = "Show/Hide GhostEars"
|
||||
set category = "Preferences"
|
||||
set desc = ".Toggle Between seeing all mob speech, and only speech of nearby mobs"
|
||||
prefs.chat_toggles ^= CHAT_GHOSTEARS
|
||||
src << "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTEARS) ? "see all speech in the world" : "only see speech from nearby mobs"]."
|
||||
prefs.save_preferences()
|
||||
feedback_add_details("admin_verb","TGE") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/verb/toggle_ghost_sight()
|
||||
set name = "Show/Hide GhostSight"
|
||||
set category = "Preferences"
|
||||
set desc = ".Toggle Between seeing all mob emotes, and only emotes of nearby mobs"
|
||||
prefs.chat_toggles ^= CHAT_GHOSTSIGHT
|
||||
src << "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTSIGHT) ? "see all emotes in the world" : "only see emotes from nearby mobs"]."
|
||||
prefs.save_preferences()
|
||||
feedback_add_details("admin_verb","TGS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/verb/toggle_ghost_whispers()
|
||||
set name = "Show/Hide GhostWhispers"
|
||||
set category = "Preferences"
|
||||
set desc = ".Toggle between hearing all whispers, and only whispers of nearby mobs"
|
||||
prefs.chat_toggles ^= CHAT_GHOSTWHISPER
|
||||
src << "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTWHISPER) ? "see all whispers in the world" : "only see whispers from nearby mobs"]."
|
||||
prefs.save_preferences()
|
||||
feedback_add_details("admin_verb","TGW") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/verb/toggle_ghost_radio()
|
||||
set name = "Show/Hide GhostRadio"
|
||||
set category = "Preferences"
|
||||
set desc = ".Enable or disable hearing radio chatter as a ghost"
|
||||
prefs.chat_toggles ^= CHAT_GHOSTRADIO
|
||||
src << "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTRADIO) ? "see radio chatter" : "not see radio chatter"]."
|
||||
prefs.save_preferences()
|
||||
feedback_add_details("admin_verb","TGR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! //social experiment, increase the generation whenever you copypaste this shamelessly GENERATION 1
|
||||
|
||||
/client/verb/toggle_ghost_pda()
|
||||
set name = "Show/Hide GhostPDA"
|
||||
set category = "Preferences"
|
||||
set desc = ".Toggle Between seeing all mob pda messages, and only pda messages of nearby mobs"
|
||||
prefs.chat_toggles ^= CHAT_GHOSTPDA
|
||||
src << "As a ghost, you will now [(prefs.chat_toggles & CHAT_GHOSTPDA) ? "see all pda messages in the world" : "only see pda messages from nearby mobs"]."
|
||||
prefs.save_preferences()
|
||||
feedback_add_details("admin_verb","TGP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/toggle_hear_radio()
|
||||
set name = "Show/Hide RadioChatter"
|
||||
set category = "Preferences"
|
||||
set desc = "Toggle seeing radiochatter from nearby radios and speakers"
|
||||
if(!holder) return
|
||||
prefs.chat_toggles ^= CHAT_RADIO
|
||||
prefs.save_preferences()
|
||||
usr << "You will [(prefs.chat_toggles & CHAT_RADIO) ? "now" : "no longer"] see radio chatter from nearby radios or speakers"
|
||||
feedback_add_details("admin_verb","THR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/verb/toggle_deathrattle()
|
||||
set name = "Toggle Deathrattle"
|
||||
set category = "Preferences"
|
||||
set desc = "Toggle recieving a message in deadchat when sentient mobs \
|
||||
die."
|
||||
prefs.toggles ^= DISABLE_DEATHRATTLE
|
||||
prefs.save_preferences()
|
||||
usr << "You will \
|
||||
[(prefs.toggles & DISABLE_DEATHRATTLE) ? "no longer" : "now"] get \
|
||||
messages when a sentient mob dies."
|
||||
feedback_add_details("admin_verb", "TDR") // If you are copy-pasting this, maybe you should spend some time reading the comments.
|
||||
|
||||
/client/verb/toggle_arrivalrattle()
|
||||
set name = "Toggle Arrivalrattle"
|
||||
set category = "Preferences"
|
||||
set desc = "Toggle recieving a message in deadchat when someone joins \
|
||||
the station."
|
||||
prefs.toggles ^= DISABLE_ARRIVALRATTLE
|
||||
usr << "You will \
|
||||
[(prefs.toggles & DISABLE_ARRIVALRATTLE) ? "no longer" : "now"] get \
|
||||
messages when someone joins the station."
|
||||
prefs.save_preferences()
|
||||
feedback_add_details("admin_verb", "TAR") // If you are copy-pasting this, maybe you should rethink where your life went so wrong.
|
||||
|
||||
/client/proc/toggleadminhelpsound()
|
||||
set name = "Hear/Silence Adminhelps"
|
||||
set category = "Preferences"
|
||||
set desc = "Toggle hearing a notification when admin PMs are received"
|
||||
if(!holder)
|
||||
return
|
||||
prefs.toggles ^= SOUND_ADMINHELP
|
||||
prefs.save_preferences()
|
||||
usr << "You will [(prefs.toggles & SOUND_ADMINHELP) ? "now" : "no longer"] hear a sound when adminhelps arrive."
|
||||
feedback_add_details("admin_verb","AHS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/toggleannouncelogin()
|
||||
set name = "Do/Don't Announce Login"
|
||||
set category = "Preferences"
|
||||
set desc = "Toggle if you want an announcement to admins when you login during a round"
|
||||
if(!holder)
|
||||
return
|
||||
prefs.toggles ^= ANNOUNCE_LOGIN
|
||||
prefs.save_preferences()
|
||||
usr << "You will [(prefs.toggles & ANNOUNCE_LOGIN) ? "now" : "no longer"] have an announcement to other admins when you login."
|
||||
feedback_add_details("admin_verb","TAL") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/deadchat()
|
||||
set name = "Show/Hide Deadchat"
|
||||
set category = "Preferences"
|
||||
set desc ="Toggles seeing deadchat"
|
||||
prefs.chat_toggles ^= CHAT_DEAD
|
||||
prefs.save_preferences()
|
||||
src << "You will [(prefs.chat_toggles & CHAT_DEAD) ? "now" : "no longer"] see deadchat."
|
||||
feedback_add_details("admin_verb","TDV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/proc/toggleprayers()
|
||||
set name = "Show/Hide Prayers"
|
||||
set category = "Preferences"
|
||||
set desc = "Toggles seeing prayers"
|
||||
prefs.chat_toggles ^= CHAT_PRAYER
|
||||
prefs.save_preferences()
|
||||
src << "You will [(prefs.chat_toggles & CHAT_PRAYER) ? "now" : "no longer"] see prayerchat."
|
||||
feedback_add_details("admin_verb","TP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/verb/toggleprayersounds()
|
||||
set name = "Hear/Silence Prayer Sounds"
|
||||
set category = "Preferences"
|
||||
set desc = "Toggles hearing pray sounds."
|
||||
prefs.toggles ^= SOUND_PRAYERS
|
||||
prefs.save_preferences()
|
||||
if(prefs.toggles & SOUND_PRAYERS)
|
||||
src << "You will now hear prayer sounds."
|
||||
else
|
||||
src << "You will no longer prayer sounds."
|
||||
feedback_add_details("admin_verb", "PSounds")
|
||||
|
||||
/client/verb/togglemidroundantag()
|
||||
set name = "Toggle Midround Antagonist"
|
||||
set category = "Preferences"
|
||||
set desc = "Toggles whether or not you will be considered for antagonist status given during a round."
|
||||
prefs.toggles ^= MIDROUND_ANTAG
|
||||
prefs.save_preferences()
|
||||
src << "You will [(prefs.toggles & MIDROUND_ANTAG) ? "now" : "no longer"] be considered for midround antagonist positions."
|
||||
feedback_add_details("admin_verb","TMidroundA") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/verb/toggletitlemusic()
|
||||
set name = "Hear/Silence LobbyMusic"
|
||||
set category = "Preferences"
|
||||
set desc = "Toggles hearing the GameLobby music"
|
||||
prefs.toggles ^= SOUND_LOBBY
|
||||
prefs.save_preferences()
|
||||
if(prefs.toggles & SOUND_LOBBY)
|
||||
src << "You will now hear music in the game lobby."
|
||||
if(istype(mob, /mob/new_player))
|
||||
playtitlemusic()
|
||||
else
|
||||
src << "You will no longer hear music in the game lobby."
|
||||
if(istype(mob, /mob/new_player))
|
||||
mob.stopLobbySound()
|
||||
feedback_add_details("admin_verb","TLobby") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/verb/togglemidis()
|
||||
set name = "Hear/Silence Midis"
|
||||
set category = "Preferences"
|
||||
set desc = "Toggles hearing sounds uploaded by admins"
|
||||
prefs.toggles ^= SOUND_MIDI
|
||||
prefs.save_preferences()
|
||||
if(prefs.toggles & SOUND_MIDI)
|
||||
src << "You will now hear any sounds uploaded by admins."
|
||||
if(admin_sound)
|
||||
src << admin_sound
|
||||
else
|
||||
src << "You will no longer hear sounds uploaded by admins; any currently playing midis have been disabled."
|
||||
if(admin_sound && !(admin_sound.status & SOUND_PAUSED))
|
||||
admin_sound.status |= SOUND_PAUSED
|
||||
src << admin_sound
|
||||
admin_sound.status ^= SOUND_PAUSED
|
||||
feedback_add_details("admin_verb","TMidi") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/verb/stop_client_sounds()
|
||||
set name = "Stop Sounds"
|
||||
set category = "Preferences"
|
||||
set desc = "Kills all currently playing sounds, use if admin taste in midis a shite"
|
||||
src << sound(null)
|
||||
feedback_add_details("admin_verb","SAPS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/verb/listen_ooc()
|
||||
set name = "Show/Hide OOC"
|
||||
set category = "Preferences"
|
||||
set desc = "Toggles seeing OutOfCharacter chat"
|
||||
prefs.chat_toggles ^= CHAT_OOC
|
||||
prefs.save_preferences()
|
||||
src << "You will [(prefs.chat_toggles & CHAT_OOC) ? "now" : "no longer"] see messages on the OOC channel."
|
||||
feedback_add_details("admin_verb","TOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/verb/Toggle_Soundscape() //All new ambience should be added here so it works with this verb until someone better at things comes up with a fix that isn't awful
|
||||
set name = "Hear/Silence Ambience"
|
||||
set category = "Preferences"
|
||||
set desc = "Toggles hearing ambient sound effects"
|
||||
prefs.toggles ^= SOUND_AMBIENCE
|
||||
prefs.save_preferences()
|
||||
if(prefs.toggles & SOUND_AMBIENCE)
|
||||
src << "You will now hear ambient sounds."
|
||||
else
|
||||
src << "You will no longer hear ambient sounds."
|
||||
src << sound(null, repeat = 0, wait = 0, volume = 0, channel = 1)
|
||||
src << sound(null, repeat = 0, wait = 0, volume = 0, channel = 2)
|
||||
feedback_add_details("admin_verb","TAmbi") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
// This needs a toggle because you people are awful and spammed terrible music
|
||||
/client/verb/toggle_instruments()
|
||||
set name = "Hear/Silence Instruments"
|
||||
set category = "Preferences"
|
||||
set desc = "Toggles hearing musical instruments like the violin and piano"
|
||||
prefs.toggles ^= SOUND_INSTRUMENTS
|
||||
prefs.save_preferences()
|
||||
if(prefs.toggles & SOUND_INSTRUMENTS)
|
||||
src << "You will now hear people playing musical instruments."
|
||||
else
|
||||
src << "You will no longer hear musical instruments."
|
||||
feedback_add_details("admin_verb","TInstru") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
//Lots of people get headaches from the normal ship ambience, this is to prevent that
|
||||
/client/verb/toggle_ship_ambience()
|
||||
set name = "Hear/Silence Ship Ambience"
|
||||
set category = "Preferences"
|
||||
set desc = "Toggles hearing generalized ship ambience, no matter your area."
|
||||
prefs.toggles ^= SOUND_SHIP_AMBIENCE
|
||||
prefs.save_preferences()
|
||||
if(prefs.toggles & SOUND_SHIP_AMBIENCE)
|
||||
src << "You will now hear ship ambience."
|
||||
else
|
||||
src << "You will no longer hear ship ambience."
|
||||
src << sound(null, repeat = 0, wait = 0, volume = 0, channel = 2)
|
||||
src.ambience_playing = 0
|
||||
feedback_add_details("admin_verb", "SAmbi") //If you are copy-pasting this, I bet you read this comment expecting to see the same thing :^)
|
||||
|
||||
var/global/list/ghost_forms = list("ghost","ghostking","ghostian2","skeleghost","ghost_red","ghost_black", \
|
||||
"ghost_blue","ghost_yellow","ghost_green","ghost_pink", \
|
||||
"ghost_cyan","ghost_dblue","ghost_dred","ghost_dgreen", \
|
||||
"ghost_dcyan","ghost_grey","ghost_dyellow","ghost_dpink", "ghost_purpleswirl","ghost_funkypurp","ghost_pinksherbert","ghost_blazeit",\
|
||||
"ghost_mellow","ghost_rainbow","ghost_camo","ghost_fire", "catghost")
|
||||
/client/proc/pick_form()
|
||||
if(!is_content_unlocked())
|
||||
alert("This setting is for accounts with BYOND premium only.")
|
||||
return
|
||||
var/new_form = input(src, "Thanks for supporting BYOND - Choose your ghostly form:","Thanks for supporting BYOND",null) as null|anything in ghost_forms
|
||||
if(new_form)
|
||||
prefs.ghost_form = new_form
|
||||
prefs.save_preferences()
|
||||
if(istype(mob,/mob/dead/observer))
|
||||
var/mob/dead/observer/O = mob
|
||||
O.update_icon(new_form)
|
||||
|
||||
var/global/list/ghost_orbits = list(GHOST_ORBIT_CIRCLE,GHOST_ORBIT_TRIANGLE,GHOST_ORBIT_SQUARE,GHOST_ORBIT_HEXAGON,GHOST_ORBIT_PENTAGON)
|
||||
|
||||
/client/proc/pick_ghost_orbit()
|
||||
if(!is_content_unlocked())
|
||||
alert("This setting is for accounts with BYOND premium only.")
|
||||
return
|
||||
var/new_orbit = input(src, "Thanks for supporting BYOND - Choose your ghostly orbit:","Thanks for supporting BYOND",null) as null|anything in ghost_orbits
|
||||
if(new_orbit)
|
||||
prefs.ghost_orbit = new_orbit
|
||||
prefs.save_preferences()
|
||||
if(istype(mob, /mob/dead/observer))
|
||||
var/mob/dead/observer/O = mob
|
||||
O.ghost_orbit = new_orbit
|
||||
|
||||
/client/proc/pick_ghost_accs()
|
||||
var/new_ghost_accs = alert("Do you want your ghost to show full accessories where possible, hide accessories but still use the directional sprites where possible, or also ignore the directions and stick to the default sprites?",,"full accessories", "only directional sprites", "default sprites")
|
||||
if(new_ghost_accs)
|
||||
switch(new_ghost_accs)
|
||||
if("full accessories")
|
||||
prefs.ghost_accs = GHOST_ACCS_FULL
|
||||
if("only directional sprites")
|
||||
prefs.ghost_accs = GHOST_ACCS_DIR
|
||||
if("default sprites")
|
||||
prefs.ghost_accs = GHOST_ACCS_NONE
|
||||
prefs.save_preferences()
|
||||
if(istype(mob, /mob/dead/observer))
|
||||
var/mob/dead/observer/O = mob
|
||||
O.update_icon()
|
||||
|
||||
/client/verb/pick_ghost_customization()
|
||||
set name = "Ghost Customization"
|
||||
set category = "Preferences"
|
||||
set desc = "Customize your ghastly appearance."
|
||||
if(is_content_unlocked())
|
||||
switch(alert("Which setting do you want to change?",,"Ghost Form","Ghost Orbit","Ghost Accessories"))
|
||||
if("Ghost Form")
|
||||
pick_form()
|
||||
if("Ghost Orbit")
|
||||
pick_ghost_orbit()
|
||||
if("Ghost Accessories")
|
||||
pick_ghost_accs()
|
||||
else
|
||||
pick_ghost_accs()
|
||||
|
||||
/client/verb/pick_ghost_others()
|
||||
set name = "Ghosts of Others"
|
||||
set category = "Preferences"
|
||||
set desc = "Change display settings for the ghosts of other players."
|
||||
var/new_ghost_others = alert("Do you want the ghosts of others to show up as their own setting, as their default sprites or always as the default white ghost?",,"Their Setting", "Default Sprites", "White Ghost")
|
||||
if(new_ghost_others)
|
||||
switch(new_ghost_others)
|
||||
if("Their Setting")
|
||||
prefs.ghost_others = GHOST_OTHERS_THEIR_SETTING
|
||||
if("Default Sprites")
|
||||
prefs.ghost_others = GHOST_OTHERS_DEFAULT_SPRITE
|
||||
if("White Ghost")
|
||||
prefs.ghost_others = GHOST_OTHERS_SIMPLE
|
||||
prefs.save_preferences()
|
||||
if(istype(mob, /mob/dead/observer))
|
||||
var/mob/dead/observer/O = mob
|
||||
O.updateghostsight()
|
||||
|
||||
/client/verb/toggle_intent_style()
|
||||
set name = "Toggle Intent Selection Style"
|
||||
set category = "Preferences"
|
||||
set desc = "Toggle between directly clicking the desired intent or clicking to rotate through."
|
||||
prefs.toggles ^= INTENT_STYLE
|
||||
src << "[(prefs.toggles & INTENT_STYLE) ? "Clicking directly on intents selects them." : "Clicking on intents rotates selection clockwise."]"
|
||||
prefs.save_preferences()
|
||||
feedback_add_details("admin_verb","ITENTS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
/client/verb/setup_character()
|
||||
set name = "Game Preferences"
|
||||
set category = "Preferences"
|
||||
set desc = "Allows you to access the Setup Character screen. Changes to your character won't take effect until next round, but other changes will."
|
||||
prefs.current_tab = 1
|
||||
prefs.ShowChoices(usr)
|
||||
|
||||
/client/verb/toggle_ghost_hud_pref()
|
||||
set name = "Toggle Ghost HUD"
|
||||
set category = "Preferences"
|
||||
set desc = "Hide/Show Ghost HUD"
|
||||
|
||||
prefs.ghost_hud = !prefs.ghost_hud
|
||||
src << "Ghost HUD will now be [prefs.ghost_hud ? "visible" : "hidden"]."
|
||||
prefs.save_preferences()
|
||||
if(istype(mob,/mob/dead/observer))
|
||||
mob.hud_used.show_hud()
|
||||
|
||||
/client/verb/toggle_inquisition() // warning: unexpected inquisition
|
||||
set name = "Toggle Inquisitiveness"
|
||||
set desc = "Sets whether your ghost examines everything on click by default"
|
||||
set category = "Preferences"
|
||||
|
||||
prefs.inquisitive_ghost = !prefs.inquisitive_ghost
|
||||
prefs.save_preferences()
|
||||
if(prefs.inquisitive_ghost)
|
||||
src << "<span class='notice'>You will now examine everything you click on.</span>"
|
||||
else
|
||||
src << "<span class='notice'>You will no longer examine things you click on.</span>"
|
||||
|
||||
/client/verb/toggle_announcement_sound()
|
||||
set name = "Hear/Silence Announcements"
|
||||
set category = "Preferences"
|
||||
set desc = ".Toggles hearing Central Command, Captain, VOX, and other announcement sounds"
|
||||
prefs.toggles ^= SOUND_ANNOUNCEMENTS
|
||||
src << "You will now [(prefs.toggles & SOUND_ANNOUNCEMENTS) ? "hear announcement sounds" : "no longer hear announcements"]."
|
||||
prefs.save_preferences()
|
||||
feedback_add_details("admin_verb","TAS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
Reference in New Issue
Block a user