Merge branch 'master' into upstream-merge-30218

This commit is contained in:
LetterJay
2017-08-28 09:03:46 -05:00
committed by GitHub
62 changed files with 332 additions and 790 deletions
+1 -2
View File
@@ -97,8 +97,7 @@
WRITE_FILE(GLOB.world_game_log, "\[[time_stamp()]]CHAT: [text]")
/proc/log_sql(text)
if(config.sql_enabled)
WRITE_FILE(GLOB.world_game_log, "\[[time_stamp()]]SQL: [text]")
WRITE_FILE(GLOB.sql_error_log, "\[[time_stamp()]]SQL: [text]")
//This replaces world.log so it displays both in DD and the file
/proc/log_world(text)
+1 -1
View File
@@ -20,7 +20,7 @@
A non null 'fixed_underlay' list var will skip copying the previous turf appearance and always use the list. If the list is
not set properly, the underlay will default to regular floor plating.
To see an example of a diagonal wall, see '/turf/closed/wall/shuttle' and its subtypes.
To see an example of a diagonal wall, see '/turf/closed/wall/mineral/titanium' and its subtypes.
*/
//Redefinitions of the diagonal directions so they can be stored in one var without conflicts
+4 -2
View File
@@ -72,7 +72,9 @@
return .
//Splits the text of a file at seperator and returns them in a list.
/world/proc/file2list(filename, seperator="\n")
/world/proc/file2list(filename, seperator="\n", trim = TRUE)
if (trim)
return splittext(trim(file2text(filename)),seperator)
return splittext(file2text(filename),seperator)
//Turns a direction into text
@@ -551,4 +553,4 @@
return /atom
else
return /datum
return text2path(copytext(string_type, 1, last_slash))
return text2path(copytext(string_type, 1, last_slash))
+1
View File
@@ -6,5 +6,6 @@
#define POLL_IGNORE_ALIEN_LARVA "alien_larva"
#define POLL_IGNORE_CLOCKWORK_MARAUDER "clockwork_marauder"
#define POLL_IGNORE_SYNDICATE "syndicate"
#define POLL_IGNORE_HOLOPARASITE "holoparasite"
GLOBAL_LIST_EMPTY(poll_ignore)
+2
View File
@@ -12,6 +12,8 @@ GLOBAL_VAR(round_id)
GLOBAL_PROTECT(round_id)
GLOBAL_VAR(config_error_log)
GLOBAL_PROTECT(config_error_log)
GLOBAL_VAR(sql_error_log)
GLOBAL_PROTECT(sql_error_log)
GLOBAL_LIST_EMPTY(bombers)
GLOBAL_PROTECT(bombers)
+2 -2
View File
@@ -188,7 +188,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
if(form == BLOOD_LIZARD && SOULVALUE < BLOOD_THRESHOLD)
regress_humanoid()
if(SOULVALUE < 0)
remove_spells()
give_appropriate_spells()
to_chat(owner.current, "<span class='warning'>As punishment for your failures, all of your powers except contract creation have been revoked.")
/datum/antagonist/devil/proc/regress_humanoid()
@@ -408,7 +408,7 @@ GLOBAL_LIST_INIT(devil_suffix, list(" the Red", " the Soulless", " the Master",
reviveNumber += LOSS_PER_DEATH
update_hud()
if(body)
body.revive(1,0)
body.revive(TRUE, TRUE) //Adminrevive also recovers organs, preventing someone from resurrecting without a heart.
if(istype(body.loc, /obj/effect/dummy/slaughter/))
body.forceMove(get_turf(body))//Fixes dying while jaunted leaving you permajaunted.
if(istype(body, /mob/living/carbon/true_devil))
+8 -6
View File
@@ -27,7 +27,7 @@
var/config_max_users = 0
var/config_min_users = 0
var/voteweight = 1
var/allow_custom_shuttles = "yes"
/datum/map_config/New(filename = "data/next_map.json", default_to_box, delete_after)
if(default_to_box)
return
@@ -44,12 +44,12 @@
if(!json)
log_world("Could not open map_config: [filename]")
return
json = file2text(json)
if(!json)
log_world("map_config is not text: [filename]")
return
json = json_decode(json)
if(!json)
log_world("map_config is not json: [filename]")
@@ -58,7 +58,7 @@
if(!ValidateJSON(json))
log_world("map_config failed to validate for above reason: [filename]")
return
config_filename = filename
map_name = json["map_name"]
@@ -66,6 +66,7 @@
map_file = json["map_file"]
minetype = json["minetype"]
allow_custom_shuttles = json["allow_custom_shuttles"]
var/list/jtcl = json["transition_config"]
@@ -74,7 +75,7 @@
for(var/I in jtcl)
transition_config[TransitionStringToEnum(I)] = TransitionStringToEnum(jtcl[I])
defaulted = FALSE
#define CHECK_EXISTS(X) if(!istext(json[X])) { log_world(X + "missing from json!"); return; }
@@ -84,6 +85,7 @@
CHECK_EXISTS("map_file")
CHECK_EXISTS("minetype")
CHECK_EXISTS("transition_config")
CHECK_EXISTS("allow_custom_shuttles")
var/path = GetFullMapPath(json["map_path"], json["map_file"])
if(!fexists(path))
@@ -92,7 +94,7 @@
if(json["transition_config"] != "default")
if(!islist(json["transition_config"]))
log_world("transition_config is not a list!")
log_world("transition_config is not a list!")
return
var/list/jtcl = json["transition_config"]
@@ -27,9 +27,6 @@
/turf/closed/wall/mineral/cult/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent) //no metal
return list("operation_time" = 80, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_TOTAL, "spawn_dir" = SOUTH)
/turf/closed/wall/shuttle/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent) //two sheets of metal
return list("operation_time" = 50, "new_obj_type" = /turf/closed/wall/clockwork, "power_cost" = POWER_WALL_TOTAL - (POWER_METAL * 2), "spawn_dir" = SOUTH)
/turf/closed/wall/r_wall/fabrication_vals(mob/living/user, obj/item/clockwork/replica_fabricator/fabricator, silent)
return FALSE
+1 -1
View File
@@ -431,7 +431,7 @@
/datum/gang_item/equipment/dominator/purchase(mob/living/carbon/user, datum/gang/gang, obj/item/device/gangtool/gangtool)
var/area/usrarea = get_area(user.loc)
var/usrturf = get_turf(user.loc)
if(initial(usrarea.name) == "Space" || isspaceturf(usrturf) || usr.z != ZLEVEL_STATION)
if(initial(usrarea.name) == "Space" || isspaceturf(usrturf) || usr.z != ZLEVEL_STATION || !usrarea.valid_territory)
to_chat(user, "<span class='warning'>You can only use this on the station!</span>")
return FALSE
@@ -456,7 +456,8 @@
dat += "<BR>\[ <A HREF='?src=\ref[src];operation=announce'>Make a Captain's Announcement</A> \]"
if(config.cross_allowed)
dat += "<BR>\[ <A HREF='?src=\ref[src];operation=crossserver'>Send a message to an allied station</A> \]"
dat += "<BR>\[ <A HREF='?src=\ref[src];operation=purchase_menu'>Purchase Shuttle</A> \]"
if(SSmapping.config.allow_custom_shuttles == "yes")
dat += "<BR>\[ <A HREF='?src=\ref[src];operation=purchase_menu'>Purchase Shuttle</A> \]"
dat += "<BR>\[ <A HREF='?src=\ref[src];operation=changeseclevel'>Change Alert Level</A> \]"
dat += "<BR>\[ <A HREF='?src=\ref[src];operation=emergencyaccess'>Emergency Maintenance Access</A> \]"
dat += "<BR>\[ <A HREF='?src=\ref[src];operation=nukerequest'>Request Nuclear Authentication Codes</A> \]"
@@ -14,6 +14,7 @@
machinetype = 6
var/intercept = FALSE // if TRUE, broadcasts all syndie messages to syndicate channel
/obj/machinery/telecomms/allinone/receive_signal(datum/signal/signal)
if(!on) // has to be on to receive messages
@@ -426,7 +426,10 @@
signal.frequency = freqnum // Quick frequency set
for(var/obj/machinery/telecomms/receiver/R in GLOB.telecomms_list)
R.receive_signal(signal)
// Allinone can act as receivers. (Unless of course whoever coded this last time forgot to put it in somewhere!)
for(var/obj/machinery/telecomms/allinone/R in GLOB.telecomms_list)
R.receive_signal(signal)
spawn(20) // wait a little...
@@ -44,7 +44,7 @@
active = TRUE
var/turf/boomturf = get_turf(imp_in)
var/area/A = get_area(boomturf)
message_admins("[ADMIN_LOOKUPFLW(imp_in)] has activated their [name] at [A.name] [ADMIN_JMP(boomturf)].")
message_admins("[ADMIN_LOOKUPFLW(imp_in)] has activated their [name] at [A.name] [ADMIN_JMP(boomturf)], with cause of [cause].")
//If the delay is short, just blow up already jeez
if(delay <= 7)
explosion(src,heavy,medium,weak,weak, flame_range = weak)
+6 -3
View File
@@ -18,8 +18,11 @@
return (BRUTELOSS|FIRELOSS|TOXLOSS|OXYLOSS)
/obj/item/banhammer/attack(mob/M, mob/user)
to_chat(M, "<font color='red'><b> You have been banned FOR NO REISIN by [user]<b></font>")
to_chat(user, "<font color='red'>You have <b>BANNED</b> [M]</font>")
if(user.zone_selected == "head")
M.visible_message("<span class='danger'>[user] are stroking the head of [M] with a bangammer</span>", "<span class='userdanger'>[user] are stroking the head with a bangammer</span>", "you hear a bangammer stroking a head");
else
M.visible_message("<span class='danger'>[M] has been banned FOR NO REISIN by [user]</span>", "<span class='userdanger'>You have been banned FOR NO REISIN by [user]</span>", "you hear a banhammer banning someone")
playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much
/obj/item/sord
@@ -586,4 +589,4 @@
/obj/item/proc/can_trigger_gun(mob/living/user)
if(!user.can_use_guns(src))
return FALSE
return TRUE
return TRUE
+1 -1
View File
@@ -303,7 +303,7 @@
mineral = /obj/item/stack/sheet/mineral/titanium
walltype = /turf/closed/wall/mineral/titanium
smooth = SMOOTH_MORE
canSmoothWith = list(/turf/closed/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock/, /turf/closed/wall/shuttle, /obj/structure/window/shuttle, /obj/structure/shuttle/engine, /obj/structure/shuttle/engine/heater, )
canSmoothWith = list(/turf/closed/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/shuttle, /obj/structure/shuttle/engine/heater)
/obj/structure/falsewall/plastitanium
name = "wall"
+44 -11
View File
@@ -24,16 +24,30 @@
//The code below here isn't exactly optimal, but because of the individual decals that each area uses it's still applicable.
//high dirt - 1/3
if(istype(A, /area/science/test_area) || istype(A, /area/mine/production) || istype(A, /area/mine/living_quarters) || istype(A, /area/mine/north_outpost) || istype(A, /area/mine/west_outpost) || istype(A, /area/ruin/space))
//high dirt - 1/3 chance.
var/static/list/high_dirt_areas = typecacheof(list(/area/science/test_area,
/area/mine/production,
/area/mine/living_quarters,
/area/mine/north_outpost,
/area/mine/west_outpost,
/area/ruin/space))
if(is_type_in_typecache(A, high_dirt_areas))
new /obj/effect/decal/cleanable/dirt(src) //vanilla, but it works
return
if(prob(80)) //mid dirt - 1/15
return
if(istype(A, /area/engine) || istype(A, /area/crew_quarters/heads/chief) || istype(A, /area/ruin/space/derelict/assembly_line) || istype(A, /area/science/robotics) || istype(A, /area/maintenance) || istype(A, /area/construction))
//Blood, sweat, and oil. Oh, and dirt.
//Construction zones. Blood, sweat, and oil. Oh, and dirt.
var/static/list/engine_dirt_areas = typecacheof(list(/area/engine,
/area/crew_quarters/heads/chief,
/area/ruin/space/derelict/assembly_line,
/area/science/robotics,
/area/maintenance,
/area/construction,
/area/survivalpod))
if(is_type_in_typecache(A, engine_dirt_areas))
if(prob(3))
new /obj/effect/decal/cleanable/blood/old(src)
else
@@ -46,7 +60,10 @@
new /obj/effect/decal/cleanable/dirt(src)
return
if(istype(A, /area/crew_quarters/toilet))
//Bathrooms. Blood, vomit, and shavings in the sinks.
var/static/list/bathroom_dirt_areas = typecacheof(list( /area/crew_quarters/toilet,
/area/awaymission/research/interior/bathroom))
if(is_type_in_typecache(A, bathroom_dirt_areas))
if(prob(40))
if(prob(90))
new /obj/effect/decal/cleanable/vomit/old(src)
@@ -54,15 +71,23 @@
new /obj/effect/decal/cleanable/blood/old(src)
return
if(istype(A, /area/quartermaster))
//Hangars and pods covered in oil.
var/static/list/oily_areas = typecacheof(list(/area/quartermaster))
if(is_type_in_typecache(A, oily_areas))
if(prob(25))
new /obj/effect/decal/cleanable/oil(src)
return
if(prob(75)) //low dirt - 1/60
return
if(istype(A, /area/ai_monitored/turret_protected) || istype(A, /area/prison) || istype(A, /area/security) || istype(A, /area/crew_quarters/heads/hos)) //chance of incident
//Areas where gibs will be present. Robusting probably happened some time ago.
var/static/list/gib_covered_areas = typecacheof(list(/area/ai_monitored/turret_protected,
/area/prison,
/area/security,
/area/crew_quarters/heads/hos))
if(is_type_in_typecache(A, gib_covered_areas))
if(prob(20))
if(prob(5))
new /obj/effect/decal/cleanable/blood/gibs/old(src)
@@ -70,8 +95,10 @@
new /obj/effect/decal/cleanable/blood/old(src)
return
if(istype(A, /area/crew_quarters/kitchen) || istype(A, /area/crew_quarters/cafeteria)) //Kitchen messes
//Kitchen areas. Broken eggs, flour, spilled milk (no crying allowed.)
var/static/list/kitchen_dirt_areas = typecacheof(list(/area/crew_quarters/kitchen,
/area/crew_quarters/cafeteria))
if(is_type_in_typecache(A, kitchen_dirt_areas))
if(prob(60))
if(prob(50))
new /obj/effect/decal/cleanable/egg_smudge(src)
@@ -79,7 +106,10 @@
new /obj/effect/decal/cleanable/flour(src)
return
if(istype(A, /area/medical) || istype(A, /area/crew_quarters/heads/cmo)) //Kept clean, but chance of blood
//Medical areas. Mostly clean by space-OSHA standards, but has some blood and oil spread about.
var/static/list/medical_dirt_areas = typecacheof(list(/area/medical,
/area/crew_quarters/heads/cmo))
if(is_type_in_typecache(A, medical_dirt_areas))
if(prob(66))
if(prob(5))
new /obj/effect/decal/cleanable/blood/gibs/old(src)
@@ -92,7 +122,10 @@
new /obj/effect/decal/cleanable/vomit/old(src)
return
if(istype(A, /area/science) || istype(A, /area/crew_quarters/heads/hor))
//Science messes. Mostly green glowy stuff -WHICH YOU SHOULD NOT INJEST-.
var/static/list/science_dirt_areas = typecacheof(list(/area/science,
/area/crew_quarters/heads/hor))
if(is_type_in_typecache(A, medical_dirt_areas))
if(prob(20))
new /obj/effect/decal/cleanable/greenglow(src) //this cleans itself up but it might startle you when you see it.
return
@@ -178,7 +178,7 @@
flags_1 = CAN_BE_DIRTY_1 | CHECK_RICOCHET_1
sheet_type = /obj/item/stack/sheet/mineral/titanium
smooth = SMOOTH_MORE|SMOOTH_DIAGONAL
canSmoothWith = list(/turf/closed/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock/, /turf/closed/wall/shuttle, /obj/structure/window/shuttle, /obj/structure/shuttle/engine/heater, /obj/structure/falsewall/titanium)
canSmoothWith = list(/turf/closed/wall/mineral/titanium, /obj/machinery/door/airlock/shuttle, /obj/machinery/door/airlock, /obj/structure/window/shuttle, /obj/structure/shuttle/engine/heater, /obj/structure/falsewall/titanium)
/turf/closed/wall/mineral/titanium/nodiagonal
smooth = SMOOTH_MORE
+2 -1
View File
@@ -3,7 +3,7 @@
SetupExternalRSC()
GLOB.config_error_log = GLOB.world_href_log = GLOB.world_runtime_log = GLOB.world_attack_log = GLOB.world_game_log = file("data/logs/config_error.log") //temporary file used to record errors with loading config, moved to log directory once logging is set bl
GLOB.config_error_log = GLOB.sql_error_log = GLOB.world_href_log = GLOB.world_runtime_log = GLOB.world_attack_log = GLOB.world_game_log = file("data/logs/config_error.log") //temporary file used to record errors with loading config, moved to log directory once logging is set bl
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
@@ -79,6 +79,7 @@
GLOB.world_attack_log = file("[GLOB.log_directory]/attack.log")
GLOB.world_runtime_log = file("[GLOB.log_directory]/runtime.log")
GLOB.world_href_log = file("[GLOB.log_directory]/hrefs.html")
GLOB.sql_error_log = file("[GLOB.log_directory]/sql.log")
WRITE_FILE(GLOB.world_game_log, "\n\nStarting up round ID [GLOB.round_id]. [time_stamp()]\n---------------------")
WRITE_FILE(GLOB.world_attack_log, "\n\nStarting up round ID [GLOB.round_id]. [time_stamp()]\n---------------------")
WRITE_FILE(GLOB.world_runtime_log, "\n\nStarting up round ID [GLOB.round_id]. [time_stamp()]\n---------------------")
+5 -7
View File
@@ -24,7 +24,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
QDEL_NULL(cstatclick)
QDEL_NULL(rstatclick)
return ..()
/datum/admin_help_tickets/proc/TicketByID(id)
var/list/lists = list(active_tickets, closed_tickets, resolved_tickets)
for(var/I in lists)
@@ -185,8 +185,6 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
TimeoutVerb()
var/parsed_message = keywords_lookup(msg)
statclick = new(null, src)
_interactions = list()
@@ -194,7 +192,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
AddInteraction("<font color='blue'>[key_name_admin(usr)] PM'd [LinkedReplyName()]</font>")
message_admins("<font color='blue'>Ticket [TicketHref("#[id]")] created</font>")
else
MessageNoRecipient(msg, parsed_message)
MessageNoRecipient(msg)
//send it to irc if nobody is on and tell us how many were on
var/admin_number_present = send2irc_adminless_only(initiator_ckey, "Ticket #[id]: [name]")
@@ -249,13 +247,13 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
//message from the initiator without a target, all admins will see this
//won't bug irc
/datum/admin_help/proc/MessageNoRecipient(msg, parsed_msg)
/datum/admin_help/proc/MessageNoRecipient(msg)
var/ref_src = "\ref[src]"
//Message to be sent to all admins
var/admin_msg = "<span class='adminnotice'><span class='adminhelp'>Ticket [TicketHref("#[id]", ref_src)]</span><b>: [LinkedReplyName(ref_src)] [FullMonty(ref_src)]:</b> [parsed_msg]</span>"
var/admin_msg = "<span class='adminnotice'><span class='adminhelp'>Ticket [TicketHref("#[id]", ref_src)]</span><b>: [LinkedReplyName(ref_src)] [FullMonty(ref_src)]:</b> [keywords_lookup(msg)]</span>"
AddInteraction("<font color='red'>[LinkedReplyName(ref_src)]: [msg]</font>")
//send this msg to all admins
for(var/client/X in GLOB.admins)
if(X.prefs.toggles & SOUND_ADMINHELP)
+7 -1
View File
@@ -9,6 +9,11 @@
freq = pick(0.5, 0.7, 0.8, 0.85, 0.9, 0.95, 1.1, 1.2, 1.4, 1.6, 2.0, 2.5)
to_chat(src, "You feel the Honkmother messing with your song...")
var/vol = input(usr, "What volume would you like the sound to play at?",, 100) as null|num
if(!vol)
return
vol = Clamp(vol, 1, 100)
var/sound/admin_sound = new()
admin_sound.file = S
admin_sound.priority = 250
@@ -17,7 +22,8 @@
admin_sound.wait = 1
admin_sound.repeat = 0
admin_sound.status = SOUND_STREAM
admin_sound.volume = vol
var/res = alert(usr, "Show the title of this song to the players?",, "No", "Yes", "Cancel")
switch(res)
if("Yes")
@@ -5,6 +5,7 @@
dynamic_lighting = DYNAMIC_LIGHTING_FORCED
requires_power = FALSE
has_gravity = TRUE
valid_territory = FALSE
//Survival Capsule
/obj/item/survivalcapsule
@@ -1,4 +1,4 @@
datum/species/mammal
/datum/species/mammal
name = "Mammal"
id = "mammal"
default_color = "4B4B4B"
@@ -229,7 +229,7 @@ datum/species/mammal
whitelist = list("rubyflamewing")
blacklisted = 0
datum/species/guilmon
/datum/species/guilmon
name = "Guilmon"
id = "guilmon"
default_color = "4B4B4B"
@@ -490,7 +490,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
return
used = TRUE
to_chat(user, "[use_message]")
var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_PAI, null, FALSE, 100)
var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the [mob_name] of [user.real_name]?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_HOLOPARASITE)
var/mob/dead/observer/theghost = null
if(candidates.len)
+9 -2
View File
@@ -120,8 +120,15 @@
if(auto_name)
name = "\improper [get_area(src)] APC"
pixel_x = (src.tdir & 3)? 0 : (src.tdir == 4 ? 26 : -27)
pixel_y = (src.tdir & 3)? (src.tdir == 1 ? 25 : -25) : 0
switch(tdir)
if(NORTH)
pixel_y = 23
if(SOUTH)
pixel_y = -23
if(EAST)
pixel_x = 24
if(WEST)
pixel_x = -25
if (building)
area = get_area(src)
opened = 1
@@ -3,7 +3,7 @@
/datum/mapGeneratorModule/border/shuttleWalls
spawnableAtoms = list()
spawnableTurfs = list(/turf/closed/wall/shuttle = 100)
spawnableTurfs = list(/turf/closed/wall/mineral/titanium = 100)
// Generators
/datum/mapGenerator/shuttle/full
@@ -106,6 +106,11 @@
metabolization_rate = 0.05
taste_description = "salt"
/datum/reagent/blackpowder/on_mob_life(mob/living/M)
..()
if(isplasmaman(M))
M.hallucination += 10
/datum/reagent/blackpowder/on_ex_act()
var/location = get_turf(holder.my_atom)
var/datum/effect_system/reagents_explosion/e = new()
+2 -2
View File
@@ -1393,8 +1393,8 @@ GLOBAL_LIST_EMPTY(uplink_items) // Global list so we only initialize this once.
if(crate_value < I.cost)
continue
crate_value -= I.cost
new I.item(C)
U.purchase_log += "<big>[icon2base64html(I.item)]</big>"
var/obj/goods = new I.item(C)
U.purchase_log += "<big>[icon2base64html(goods)]</big>"
SSblackbox.add_details("traitor_uplink_items_bought", "[name]|[cost]")
return C