diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 28f69ba1795..5bb1d0eb69f 100644 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -408,12 +408,65 @@ SUBSYSTEM_DEF(ticker) if(player.mind.assigned_role != player.mind.special_role) SSjobs.AssignRank(player, player.mind.assigned_role, FALSE) SSjobs.EquipRank(player, player.mind.assigned_role, FALSE) - EquipCustomItems(player) + equip_cuis(player) + if(captainless) for(var/mob/M in GLOB.player_list) if(!isnewplayer(M)) to_chat(M, "Captainship not forced on anyone.") +/datum/controller/subsystem/ticker/proc/equip_cuis(mob/living/carbon/human/H) + if(!H.client) + return // If they are spawning without a client (somehow), they *cant* have a CUI list + for(var/datum/custom_user_item/cui in H.client.cui_entries) + // Skip items with invalid character names + if((cui.characer_name != H.real_name) && !cui.all_characters_allowed) + continue + + var/ok = FALSE + + if(!cui.all_jobs_allowed) + var/alt_blocked = FALSE + if(H.mind.role_alt_title) + if(!(H.mind.role_alt_title in cui.allowed_jobs)) + alt_blocked = TRUE + if(!(H.mind.assigned_role in cui.allowed_jobs) || alt_blocked) + continue + + var/obj/item/I = new cui.object_typepath() + var/name_override = cui.item_name_override + var/desc_override = cui.item_desc_override + + if(name_override) + I.name = name_override + if(desc_override) + I.desc = desc_override + + if(istype(H.back, /obj/item/storage)) // Try to place it in something on the mob's back + var/obj/item/storage/S = H.back + if(length(S.contents) < S.storage_slots) + I.forceMove(H.back) + ok = TRUE + to_chat(H, "Your [I.name] has been added to your [H.back.name].") + + if(!ok) + for(var/obj/item/storage/S in H.contents) // Try to place it in any item that can store stuff, on the mob. + if(length(S.contents) < S.storage_slots) + I.forceMove(S) + ok = TRUE + to_chat(H, "Your [I.name] has been added to your [S.name].") + break + + if(!ok) // Finally, since everything else failed, place it on the ground + var/turf/T = get_turf(H) + if(T) + I.forceMove(T) + to_chat(H, "Your [I.name] is on the [T.name] below you.") + else + to_chat(H, "Your [I.name] couldnt spawn anywhere on you or even on the floor below you. Please file a bug report.") + qdel(I) + + /datum/controller/subsystem/ticker/proc/send_tip_of_the_round() var/m if(selected_tip) diff --git a/code/datums/custom_user_item.dm b/code/datums/custom_user_item.dm new file mode 100644 index 00000000000..9680a990488 --- /dev/null +++ b/code/datums/custom_user_item.dm @@ -0,0 +1,66 @@ +/** + * # Custom User Item + * + * Holder for CUIs + * + * This datum is a holder that is essentially a "model" of the `customuseritems` + * database table, and is used for giving people their CUIs on spawn. + * It is instanced as part of the client data loading framework on the client. + * + */ +/datum/custom_user_item + /// Can this be used on all characters? + var/all_characters_allowed = FALSE + /// Name of the character that can have this item. + var/characer_name + /// Are all jobs allowed? + var/all_jobs_allowed = FALSE + /// List of allowed jobs + var/list/allowed_jobs = list() + /// Custom item typepath + var/object_typepath + /// Custom item name override + var/item_name_override + /// Custom item description override + var/item_desc_override + /// Raw job mask + var/raw_job_mask + + +/** + * CUI Info Parser + * + * Parses all the raw info into usable stuff, and also does validity checks + * Returns TRUE if its a valid item, and FALSE if not + * + * Arguments: + * * owning_ckey - Player who owns this item. Used for logging purposes. + */ +/datum/custom_user_item/proc/parse_info(owning_ckey) + . = FALSE // Setting this here means it will return false even if it runtimes + + // Sort path + if(!object_typepath || !ispath(object_typepath)) + stack_trace("Incorrect database entry found in table 'customuseritems' path value is [object_typepath ? object_typepath : "(NULL)"], which doesnt exist. Ask the host to look at CUI entries for [owning_ckey]") + return + + // Sort job mask + if(raw_job_mask == "*") + all_jobs_allowed = TRUE + else + var/list/local_allowed_jobs = splittext(raw_job_mask, ",") + for(var/i in 1 to length(local_allowed_jobs)) + if(istext(local_allowed_jobs[i])) + local_allowed_jobs[i] = trim(local_allowed_jobs[i]) + + allowed_jobs = local_allowed_jobs + + // Sort character name + if(characer_name == "*") + all_characters_allowed = TRUE + + return TRUE + + +/datum/custom_user_item/vv_edit_var(var_name, var_value) + return FALSE // fuck off diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index f68e20b1903..7c011962c08 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -117,10 +117,16 @@ /// Client's pAI save var/datum/pai_save/pai_save + /// List of the clients CUIs + var/list/datum/custom_user_item/cui_entries = list() + /client/vv_edit_var(var_name, var_value) switch(var_name) // I know we will never be in a world where admins are editing client vars to let people bypass TOS // But guess what, if I have the ability to overengineer something, I am going to do it if("tos_consent") return FALSE + // Dont fuck with this + if("cui_entries") + return FALSE return ..() diff --git a/code/modules/client/login_processing/45-cuis.dm b/code/modules/client/login_processing/45-cuis.dm new file mode 100644 index 00000000000..88bfca44638 --- /dev/null +++ b/code/modules/client/login_processing/45-cuis.dm @@ -0,0 +1,20 @@ +/datum/client_login_processor/cuis + priority = 45 + +/datum/client_login_processor/cuis/get_query(client/C) + var/datum/db_query/query = SSdbcore.NewQuery("SELECT cuiRealName, cuiPath, cuiItemName, cuiDescription, cuiJobMask FROM customuseritems WHERE cuiCKey=:ckey", list( + "ckey" = C.ckey + )) + return query + +/datum/client_login_processor/cuis/process_result(datum/db_query/Q, client/C) + while(Q.NextRow()) + var/datum/custom_user_item/cui = new() + cui.characer_name = Q.item[1] + cui.object_typepath = text2path(Q.item[2]) + cui.item_name_override = Q.item[3] + cui.item_desc_override = Q.item[4] + cui.raw_job_mask = Q.item[5] + + if(cui.parse_info(C.ckey)) + C.cui_entries += cui diff --git a/code/modules/customitems/item_spawning.dm b/code/modules/customitems/item_spawning.dm deleted file mode 100644 index 5f998861cf6..00000000000 --- a/code/modules/customitems/item_spawning.dm +++ /dev/null @@ -1,101 +0,0 @@ -/proc/EquipCustomItems(mob/living/carbon/human/M) - if(!SSdbcore.IsConnected()) - return - - // Grab the info we want. - var/datum/db_query/query = SSdbcore.NewQuery({" - SELECT cuiPath, cuiPropAdjust, cuiJobMask, cuiDescription, cuiItemName FROM customuseritems - WHERE cuiCKey=:ckey AND (cuiRealName=:realname OR cuiRealName='*')"}, list( - "ckey" = M.ckey, - "realname" = M.real_name - )) - if(!query.warn_execute(async = FALSE)) // Dont make this async. Youll make roundstart slow. Trust me. - qdel(query) - return - - while(query.NextRow()) - var/path = text2path(query.item[1]) - var/propadjust = query.item[2] - var/jobmask = query.item[3] - var/ok = 0 - if(!path || !ispath(path)) - log_debug("Incorrect database entry found in table 'customuseritems' path value = [path], cuiPath is null. cuiCKey='[M.ckey]' AND (cuiRealName='[M.real_name]' OR cuiRealName='*'") - continue - if(jobmask != "*") - var/list/allowed_jobs = splittext(jobmask,",") - for(var/i = 1, i <= allowed_jobs.len, i++) - if(istext(allowed_jobs[i])) - allowed_jobs[i] = trim(allowed_jobs[i]) - var/alt_blocked = 0 - if(M.mind.role_alt_title) - if(!(M.mind.role_alt_title in allowed_jobs)) - alt_blocked = 1 - if(!(M.mind.assigned_role in allowed_jobs) || alt_blocked) - continue - - var/obj/item/Item = new path() - var/description = query.item[4] - var/newname = query.item[5] - if(istype(Item,/obj/item/card/id)) - var/obj/item/card/id/I = Item - for(var/obj/item/card/id/C in M) - //default settings - I.name = "[M.real_name]'s ID Card ([M.mind.role_alt_title ? M.mind.role_alt_title : M.mind.assigned_role])" - I.registered_name = M.real_name - I.access = C.access - I.assignment = C.assignment - I.blood_type = C.blood_type - I.dna_hash = C.dna_hash - I.fingerprint_hash = C.fingerprint_hash - qdel(C) - ok = M.equip_or_collect(I, slot_wear_id, 0) //if 1, last argument deletes on fail - break - else if(istype(M.back, /obj/item/storage)) // Try to place it in something on the mob's back - var/obj/item/storage/S = M.back - if(S.contents.len < S.storage_slots) - Item.loc = M.back - ok = 1 - to_chat(M, "Your [Item.name] has been added to your [M.back.name].") - if(ok == 0) - for(var/obj/item/storage/S in M.contents) // Try to place it in any item that can store stuff, on the mob. - if(S.contents.len < S.storage_slots) - Item.loc = S - ok = 1 - to_chat(M, "Your [Item.name] has been added to your [S.name].") - break - if(description) - Item.desc = description - if(newname) - Item.name = newname - - if(ok == 0) // Finally, since everything else failed, place it on the ground - Item.loc = get_turf(M.loc) - - HackProperties(Item,propadjust) - M.regenerate_icons() - qdel(query) - -// This is hacky, but since it's difficult as fuck to make a proper parser in BYOND without killing the server, here it is. - N3X -/proc/HackProperties(mob/living/carbon/human/M, obj/item/I, script) - var/list/statements = splittext(script,";") - if(statements.len == 0) - return - for(var/statement in statements) - var/list/assignmentChunks = splittext(statement,"=") - var/varname = assignmentChunks[1] - var/list/typeChunks=splittext(script,":") - var/desiredType=typeChunks[1] - switch(desiredType) - if("string") - var/output = typeChunks[2] - output = replacetext(output,"{REALNAME}", M.real_name) - output = replacetext(output,"{ROLE}", M.mind.assigned_role) - output = replacetext(output,"{ROLE_ALT}", "[M.mind.role_alt_title ? M.mind.role_alt_title : M.mind.assigned_role]") - I.vars[varname]=output - if("number") - I.vars[varname]=text2num(typeChunks[2]) - if("icon") - if(typeChunks.len==2) - I.vars[varname]=new /icon(typeChunks[2]) - if(typeChunks.len==3) - I.vars[varname]=new /icon(typeChunks[2],typeChunks[3]) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 04aca83e512..9370829c746 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -359,7 +359,7 @@ character.buckled.dir = character.dir character = SSjobs.EquipRank(character, rank, 1) //equips the human - EquipCustomItems(character) + SSticker.equip_cuis(character) // Gives them their CUIs SSticker.mode.latespawn(character) diff --git a/paradise.dme b/paradise.dme index 3335290c461..2fe863f5060 100644 --- a/paradise.dme +++ b/paradise.dme @@ -278,6 +278,7 @@ #include "code\datums\callback.dm" #include "code\datums\chatmessage.dm" #include "code\datums\click_intercept.dm" +#include "code\datums\custom_user_item.dm" #include "code\datums\datacore.dm" #include "code\datums\datum.dm" #include "code\datums\datumvars.dm" @@ -1386,6 +1387,7 @@ #include "code\modules\client\login_processing\38-alts_cid.dm" #include "code\modules\client\login_processing\39-cid_count.dm" #include "code\modules\client\login_processing\40-pai_save.dm" +#include "code\modules\client\login_processing\45-cuis.dm" #include "code\modules\client\login_processing\__client_login_processor.dm" #include "code\modules\client\preference\character.dm" #include "code\modules\client\preference\link_processing.dm" @@ -1480,7 +1482,6 @@ #include "code\modules\crafting\recipes.dm" #include "code\modules\crafting\tailoring.dm" #include "code\modules\customitems\item_defines.dm" -#include "code\modules\customitems\item_spawning.dm" #include "code\modules\detective_work\detective_work.dm" #include "code\modules\detective_work\evidence.dm" #include "code\modules\detective_work\footprints_and_rag.dm"