diff --git a/_maps/map_files/BoxStation/BoxStation.dmm b/_maps/map_files/BoxStation/BoxStation.dmm
index 8fbb97bbdd..836d5a20e6 100644
--- a/_maps/map_files/BoxStation/BoxStation.dmm
+++ b/_maps/map_files/BoxStation/BoxStation.dmm
@@ -47569,7 +47569,7 @@
/obj/structure/cable/yellow{
icon_state = "1-2"
},
-/obj/machinery/air_sensor{
+/obj/machinery/air_sensor/atmos/incinerator_tank{
pixel_x = -32;
pixel_y = -32
},
@@ -54656,6 +54656,7 @@
/obj/structure/sign/poster/contraband/robust_softdrinks{
pixel_y = 32
},
+/obj/machinery/vending/snack/orange,
/turf/open/floor/wood,
/area/service/bar)
"fZm" = (
@@ -57444,10 +57445,10 @@
/turf/open/floor/plasteel/dark,
/area/security/prison/cells)
"kgB" = (
-/obj/machinery/vending/snack/orange,
/obj/machinery/light{
dir = 1
},
+/obj/machinery/jukebox,
/turf/open/floor/wood,
/area/service/bar)
"khb" = (
diff --git a/_maps/map_files/LambdaStation/dorms.dmm b/_maps/map_files/LambdaStation/dorms.dmm
index d52c4e7870..99626ac667 100644
--- a/_maps/map_files/LambdaStation/dorms.dmm
+++ b/_maps/map_files/LambdaStation/dorms.dmm
@@ -5011,6 +5011,7 @@
dir = 1
},
/obj/machinery/light,
+/obj/machinery/jukebox,
/turf/open/floor/plasteel,
/area/service/bar)
"nb" = (
diff --git a/_maps/map_files/MetaStation/MetaStation.dmm b/_maps/map_files/MetaStation/MetaStation.dmm
index 3661b49684..dd05cf72ea 100644
--- a/_maps/map_files/MetaStation/MetaStation.dmm
+++ b/_maps/map_files/MetaStation/MetaStation.dmm
@@ -65631,6 +65631,14 @@
},
/turf/open/floor/plasteel/dark,
/area/service/chapel/main)
+"mWS" = (
+/obj/effect/turf_decal/tile/bar,
+/obj/effect/turf_decal/tile/bar{
+ dir = 1
+ },
+/obj/machinery/jukebox,
+/turf/open/floor/plasteel,
+/area/service/bar)
"mXi" = (
/turf/open/floor/wood,
/area/command/heads_quarters/captain/private)
@@ -119802,7 +119810,7 @@ teZ
teZ
wbZ
hCA
-msI
+mWS
pzY
kPZ
upO
diff --git a/_maps/map_files/OmegaStation/OmegaStation.dmm b/_maps/map_files/OmegaStation/OmegaStation.dmm
index 68dfbe5bcf..a237802205 100644
--- a/_maps/map_files/OmegaStation/OmegaStation.dmm
+++ b/_maps/map_files/OmegaStation/OmegaStation.dmm
@@ -13604,14 +13604,7 @@
/turf/open/floor/plasteel,
/area/service/bar/atrium)
"ayN" = (
-/obj/item/storage/fancy/cigarettes/cigars{
- pixel_y = 6
- },
-/obj/item/storage/fancy/cigarettes/cigars/cohiba{
- pixel_y = 3
- },
-/obj/item/storage/fancy/cigarettes/cigars/havana,
-/obj/structure/table/reinforced,
+/obj/machinery/jukebox,
/turf/open/floor/plasteel/dark/corner{
dir = 4
},
@@ -13622,6 +13615,13 @@
pixel_x = 32
},
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
+/obj/item/storage/fancy/cigarettes/cigars/havana,
+/obj/item/storage/fancy/cigarettes/cigars/cohiba{
+ pixel_y = 3
+ },
+/obj/item/storage/fancy/cigarettes/cigars{
+ pixel_y = 6
+ },
/turf/open/floor/plasteel/dark/side{
dir = 1
},
@@ -16379,9 +16379,8 @@
/area/hallway/secondary/exit)
"aEg" = (
/obj/effect/turf_decal/stripes/line,
-/obj/machinery/atmospherics/components/trinary/mixer/airmix/flipped/inverse{
- dir = 4;
- name = "air mixer"
+/obj/machinery/atmospherics/components/trinary/mixer/airmix/flipped{
+ dir = 4
},
/obj/structure/cable{
icon_state = "4-8"
@@ -84771,7 +84770,7 @@ awp
awX
axR
ayN
-aue
+asg
blj
aCb
aCe
diff --git a/_maps/map_files/generic/CentCom.dmm b/_maps/map_files/generic/CentCom.dmm
index a147773cd1..cb32e435ca 100644
--- a/_maps/map_files/generic/CentCom.dmm
+++ b/_maps/map_files/generic/CentCom.dmm
@@ -2280,7 +2280,7 @@
/turf/open/floor/plasteel,
/area/tdome/arena_source)
"fX" = (
-/turf/closed/indestructible/riveted,
+/turf/closed/indestructible/start_area,
/area/start)
"fY" = (
/obj/effect/landmark/start/new_player,
diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 1039a07982..b3dbc46806 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -539,7 +539,7 @@
#define COMSIG_MECHA_EQUIPMENT_CLICK "mecha_action_equipment_click"
/// Prevents click from happening.
#define COMPONENT_CANCEL_EQUIPMENT_CLICK (1<<0)
-
+
// /mob/living/carbon/human signals
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACK "human_melee_unarmed_attack" //from mob/living/carbon/human/UnarmedAttack(): (atom/target)
#define COMSIG_HUMAN_MELEE_UNARMED_ATTACKBY "human_melee_unarmed_attackby" //from mob/living/carbon/human/UnarmedAttack(): (mob/living/carbon/human/attacker)
diff --git a/code/__DEFINES/dcs/signals/signals_subsystem.dm b/code/__DEFINES/dcs/signals/signals_subsystem.dm
new file mode 100644
index 0000000000..c39b59262b
--- /dev/null
+++ b/code/__DEFINES/dcs/signals/signals_subsystem.dm
@@ -0,0 +1,25 @@
+// Subsystem signals. Format:
+// When the signal is called: (signal arguments)
+// All signals send the source datum of the signal as the first argument
+
+///Subsystem signals
+///From base of datum/controller/subsystem/Initialize: (start_timeofday)
+#define COMSIG_SUBSYSTEM_POST_INITIALIZE "subsystem_post_initialize"
+
+///Called when the ticker enters the pre-game phase
+#define COMSIG_TICKER_ENTER_PREGAME "comsig_ticker_enter_pregame"
+
+///Called when the ticker sets up the game for start
+#define COMSIG_TICKER_ENTER_SETTING_UP "comsig_ticker_enter_setting_up"
+
+///Called when the ticker fails to set up the game for start
+#define COMSIG_TICKER_ERROR_SETTING_UP "comsig_ticker_error_setting_up"
+
+/// Called when the round has started, but before GAME_STATE_PLAYING
+#define COMSIG_TICKER_ROUND_STARTING "comsig_ticker_round_starting"
+
+// Point of interest signals
+/// Sent from base of /datum/controller/subsystem/points_of_interest/proc/on_poi_element_added : (atom/new_poi)
+#define COMSIG_ADDED_POINT_OF_INTEREST "added_point_of_interest"
+/// Sent from base of /datum/controller/subsystem/points_of_interest/proc/on_poi_element_removed : (atom/old_poi)
+#define COMSIG_REMOVED_POINT_OF_INTEREST "removed_point_of_interest"
diff --git a/code/__DEFINES/layers_planes.dm b/code/__DEFINES/layers_planes.dm
index 2550084207..fd841ba258 100644
--- a/code/__DEFINES/layers_planes.dm
+++ b/code/__DEFINES/layers_planes.dm
@@ -187,6 +187,9 @@
#define ABOVE_HUD_LAYER 30
#define ABOVE_HUD_RENDER_TARGET "ABOVE_HUD_PLANE"
+#define LOBBY_BACKGROUND_LAYER 3
+#define LOBBY_BUTTON_LAYER 4
+
#define SPLASHSCREEN_LAYER 90
#define SPLASHSCREEN_PLANE 90
#define SPLASHSCREEN_RENDER_TARGET "SPLASHSCREEN_PLANE"
diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm
index 87e7fe3ce4..96201d71c2 100644
--- a/code/__DEFINES/logging.dm
+++ b/code/__DEFINES/logging.dm
@@ -42,6 +42,7 @@
#define LOG_MECHA (1 << 17)
#define LOG_SHUTTLE (1 << 18)
#define LOG_VICTIM (1 << 19)
+#define LOG_ECON (1 << 20)
//Individual logging panel pages
#define INDIVIDUAL_ATTACK_LOG (LOG_ATTACK | LOG_VICTIM)
@@ -50,7 +51,7 @@
#define INDIVIDUAL_COMMS_LOG (LOG_PDA | LOG_CHAT | LOG_COMMENT | LOG_TELECOMMS)
#define INDIVIDUAL_OOC_LOG (LOG_OOC | LOG_ADMIN)
#define INDIVIDUAL_OWNERSHIP_LOG (LOG_OWNERSHIP)
-#define INDIVIDUAL_SHOW_ALL_LOG (LOG_ATTACK | LOG_SAY | LOG_WHISPER | LOG_EMOTE | LOG_DSAY | LOG_PDA | LOG_CHAT | LOG_COMMENT | LOG_TELECOMMS | LOG_OOC | LOG_ADMIN | LOG_OWNERSHIP | LOG_GAME | LOG_VICTIM)
+#define INDIVIDUAL_SHOW_ALL_LOG (LOG_ATTACK | LOG_SAY | LOG_WHISPER | LOG_EMOTE | LOG_DSAY | LOG_PDA | LOG_CHAT | LOG_COMMENT | LOG_TELECOMMS | LOG_OOC | LOG_ADMIN | LOG_OWNERSHIP | LOG_GAME | LOG_VICTIM | LOG_ECON)
#define LOGSRC_CLIENT "Client"
#define LOGSRC_MOB "Mob"
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index c8c084fc18..36f427a1bb 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -5,7 +5,6 @@
// Ready states at roundstart for mob/dead/new_player
#define PLAYER_NOT_READY 0
#define PLAYER_READY_TO_PLAY 1
-#define PLAYER_READY_TO_OBSERVE 2
// movement intent defines for the m_intent var
#define MOVE_INTENT_WALK "walk"
diff --git a/code/__DEFINES/pool.dm b/code/__DEFINES/pool.dm
index 1bf39dcb1d..86088fbb79 100644
--- a/code/__DEFINES/pool.dm
+++ b/code/__DEFINES/pool.dm
@@ -8,5 +8,6 @@
GLOBAL_LIST_INIT(blacklisted_pool_reagents, list(
/datum/reagent/toxin/plasma, /datum/reagent/oxygen, /datum/reagent/nitrous_oxide, /datum/reagent/nitrogen, //gases
/datum/reagent/fermi, //blanket fermichem ban sorry. this also covers mkultra, genital enlargers, etc etc.
- /datum/reagent/consumable/semen //NO.
+ /datum/reagent/consumable/semen, //NO.
+ /datum/reagent/consumable/milk
))
diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm
index b711837232..73c5841ab9 100644
--- a/code/__DEFINES/say.dm
+++ b/code/__DEFINES/say.dm
@@ -91,6 +91,7 @@
//ambition end
#define MAX_MESSAGE_LEN 4096 //Citadel edit: What's the WORST that could happen?
#define MAX_FLAVOR_LEN 4096
+#define MAX_FLAVOR_PREVIEW_LEN 40
#define MAX_TASTE_LEN 40 //lick... vore... ew...
#define MAX_NAME_LEN 42
#define MAX_BROADCAST_LEN 512
diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index c8791eabb8..94ace816ac 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -233,6 +233,8 @@
#define TRAIT_TRASHCAN "trashcan"
///Used for fireman carry to have mobe not be dropped when passing by a prone individual.
#define TRAIT_BEING_CARRIED "being_carried"
+#define TRAIT_GLASS_BONES "glass_bones"
+#define TRAIT_PAPER_SKIN "paper_skin"
// mobility flag traits
// IN THE FUTURE, IT WOULD BE NICE TO DO SOMETHING SIMILAR TO https://github.com/tgstation/tgstation/pull/48923/files (ofcourse not nearly the same because I have my.. thoughts on it)
diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm
index 8a8ccd5023..58b7516be3 100644
--- a/code/__HELPERS/_logging.dm
+++ b/code/__HELPERS/_logging.dm
@@ -161,6 +161,10 @@
if (CONFIG_GET(flag/log_telecomms))
WRITE_LOG(GLOB.world_telecomms_log, "TCOMMS: [text]")
+/proc/log_econ(text)
+ if (CONFIG_GET(flag/log_econ))
+ WRITE_LOG(GLOB.world_econ_log, "MONEY: [text]")
+
/proc/log_chat(text)
if (CONFIG_GET(flag/log_pda))
//same thing here
diff --git a/code/__HELPERS/clients.dm b/code/__HELPERS/clients.dm
new file mode 100644
index 0000000000..3b61cf1e1c
--- /dev/null
+++ b/code/__HELPERS/clients.dm
@@ -0,0 +1,12 @@
+///Returns whether or not a player is a guest using their ckey as an input
+/proc/is_guest_key(key)
+ if(findtext(key, "Guest-", 1, 7) != 1) //was findtextEx
+ return FALSE
+
+ var/i, ch, len = length(key)
+
+ for(i = 7, i <= len, ++i) //we know the first 6 chars are Guest-
+ ch = text2ascii(key, i)
+ if (ch < 48 || ch > 57) //0-9
+ return FALSE
+ return TRUE
diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index 2f7dd7a64f..7283d7ab61 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -258,15 +258,24 @@
send2adminchat("Server", "A round of [mode.name] just ended[mode_result == "undefined" ? "." : " with a [mode_result]."] Survival rate: [survival_rate]")
- if(LAZYLEN(GLOB.round_end_notifiees))
- world.TgsTargetedChatBroadcast("[GLOB.round_end_notifiees.Join(", ")] the round has ended.", FALSE)
-
if(length(CONFIG_GET(keyed_list/cross_server)))
send_news_report()
//tell the nice people on discord what went on before the salt cannon happens.
- world.TgsTargetedChatBroadcast("The current round has ended. Please standby for your shift interlude Nanotrasen News Network's report!", FALSE)
- world.TgsTargetedChatBroadcast(send_news_report(), FALSE)
+ if(CONFIG_GET(string/chat_roundend_notice_tag))
+ var/broadcastmessage = ""
+
+ if(LAZYLEN(GLOB.round_end_notifiees))
+ broadcastmessage += "[GLOB.round_end_notifiees.Join(", ")], "
+
+
+ broadcastmessage += "[((broadcastmessage == "") ? "the" : "The")] current round has ended. Please standby for your shift interlude Nanotrasen News Network's report!\n"
+ broadcastmessage += "```\n[send_news_report()]\n```"
+
+ if(CONFIG_GET(string/chat_reboot_role))
+ broadcastmessage += "\n\n<@&[CONFIG_GET(string/chat_reboot_role)]>, the server will reboot shortly!"
+
+ send2chat(broadcastmessage, CONFIG_GET(string/chat_roundend_notice_tag))
CHECK_TICK
@@ -548,7 +557,7 @@
parts += "There were [station_vault] credits collected by crew this shift.
"
if(total_players > 0)
parts += "An average of [station_vault/total_players] credits were collected.
"
- // log_econ("Roundend credit total: [station_vault] credits. Average Credits: [station_vault/total_players]")
+ log_econ("Roundend credit total: [station_vault] credits. Average Credits: [station_vault/total_players]")
if(mr_moneybags)
parts += "The most affluent crew member at shift end was [mr_moneybags.account_holder] with [mr_moneybags.account_balance] cr!"
else
diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm
index 3f3d9c5e25..e9c38546a7 100644
--- a/code/_globalvars/logging.dm
+++ b/code/_globalvars/logging.dm
@@ -49,6 +49,8 @@ GLOBAL_VAR(tgui_log)
GLOBAL_PROTECT(tgui_log)
GLOBAL_VAR(world_shuttle_log)
GLOBAL_PROTECT(world_shuttle_log)
+GLOBAL_VAR(world_econ_log)
+GLOBAL_PROTECT(world_econ_log)
GLOBAL_VAR(perf_log)
GLOBAL_PROTECT(perf_log)
diff --git a/code/_onclick/drag_drop.dm b/code/_onclick/drag_drop.dm
index ae853a4e1a..58c182036d 100644
--- a/code/_onclick/drag_drop.dm
+++ b/code/_onclick/drag_drop.dm
@@ -101,14 +101,6 @@
..()
/client/MouseDrag(src_object,atom/over_object,src_location,over_location,src_control,over_control,params)
- var/list/L = params2list(params)
- if (L["middle"])
- if (src_object && src_location != over_location)
- middragtime = world.time
- middragatom = src_object
- else
- middragtime = 0
- middragatom = null
mouseParams = params
mouseLocation = over_location
mouseObject = over_object
@@ -121,9 +113,3 @@
/obj/item/proc/onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
return
-
-/client/MouseDrop(src_object, over_object, src_location, over_location, src_control, over_control, params)
- if (middragatom == src_object)
- middragtime = 0
- middragatom = null
- ..()
diff --git a/code/_onclick/hud/new_player.dm b/code/_onclick/hud/new_player.dm
new file mode 100644
index 0000000000..b426db1aac
--- /dev/null
+++ b/code/_onclick/hud/new_player.dm
@@ -0,0 +1,333 @@
+/datum/hud/new_player
+
+/datum/hud/new_player/proc/populate_buttons(mob/dead/new_player/owner)
+ var/list/buttons = subtypesof(/atom/movable/screen/lobby)
+ for(var/button_type in buttons)
+ var/atom/movable/screen/lobby/lobbyscreen = new button_type()
+ lobbyscreen.SlowInit()
+ lobbyscreen.hud = src
+ static_inventory += lobbyscreen
+ if(istype(lobbyscreen, /atom/movable/screen/lobby/button))
+ var/atom/movable/screen/lobby/button/lobby_button = lobbyscreen
+ lobby_button.owner = REF(owner)
+ show_hud(hud_version)
+
+/atom/movable/screen/lobby
+ plane = SPLASHSCREEN_PLANE
+ layer = LOBBY_BUTTON_LAYER
+ screen_loc = "TOP,CENTER"
+
+/// Run sleeping actions after initialize
+/atom/movable/screen/lobby/proc/SlowInit()
+ return
+
+/atom/movable/screen/lobby/background
+ layer = LOBBY_BACKGROUND_LAYER
+ icon = 'icons/hud/lobby/background.dmi'
+ icon_state = "background"
+ screen_loc = "TOP,CENTER:-61"
+
+/atom/movable/screen/lobby/button
+ ///Is the button currently enabled?
+ var/enabled = TRUE
+ ///Is the button currently being hovered over with the mouse?
+ var/highlighted = FALSE
+ /// The ref of the mob that owns this button. Only the owner can click on it.
+ var/owner
+
+/atom/movable/screen/lobby/button/Click(location, control, params)
+ if(owner != REF(usr))
+ return
+
+ . = ..()
+
+ if(!enabled)
+ return
+ flick("[base_icon_state]_pressed", src)
+ update_appearance(UPDATE_ICON)
+ return TRUE
+
+/atom/movable/screen/lobby/button/MouseEntered(location,control,params)
+ if(owner != REF(usr))
+ return
+
+ . = ..()
+ highlighted = TRUE
+ update_appearance(UPDATE_ICON)
+
+/atom/movable/screen/lobby/button/MouseExited()
+ if(owner != REF(usr))
+ return
+
+ . = ..()
+ highlighted = FALSE
+ update_appearance(UPDATE_ICON)
+
+/atom/movable/screen/lobby/button/update_icon(updates)
+ . = ..()
+ if(!enabled)
+ icon_state = "[base_icon_state]_disabled"
+ return
+ else if(highlighted)
+ icon_state = "[base_icon_state]_highlighted"
+ return
+ icon_state = base_icon_state
+
+/atom/movable/screen/lobby/button/proc/set_button_status(status)
+ if(status == enabled)
+ return FALSE
+ enabled = status
+ update_appearance(UPDATE_ICON)
+ return TRUE
+
+///Prefs menu
+/atom/movable/screen/lobby/button/character_setup
+ screen_loc = "TOP:-70,CENTER:-54"
+ icon = 'icons/hud/lobby/character_setup.dmi'
+ icon_state = "character_setup"
+ base_icon_state = "character_setup"
+
+/atom/movable/screen/lobby/button/character_setup/Click(location, control, params)
+ . = ..()
+ if(!.)
+ return
+ hud.mymob.client.prefs.current_tab = SETTINGS_TAB
+ hud.mymob.client.prefs.ShowChoices(hud.mymob)
+
+///Button that appears before the game has started
+/atom/movable/screen/lobby/button/ready
+ screen_loc = "TOP:-8,CENTER:-65"
+ icon = 'icons/hud/lobby/ready.dmi'
+ icon_state = "not_ready"
+ base_icon_state = "not_ready"
+ var/ready = FALSE
+
+/atom/movable/screen/lobby/button/ready/Initialize(mapload)
+ . = ..()
+ switch(SSticker.current_state)
+ if(GAME_STATE_PREGAME, GAME_STATE_STARTUP)
+ RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, .proc/hide_ready_button)
+ if(GAME_STATE_SETTING_UP)
+ set_button_status(FALSE)
+ RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, .proc/show_ready_button)
+ else
+ set_button_status(FALSE)
+
+/atom/movable/screen/lobby/button/ready/proc/hide_ready_button()
+ SIGNAL_HANDLER
+ set_button_status(FALSE)
+ UnregisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP)
+ RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, .proc/show_ready_button)
+
+/atom/movable/screen/lobby/button/ready/proc/show_ready_button()
+ SIGNAL_HANDLER
+ set_button_status(TRUE)
+ UnregisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP)
+ RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, .proc/hide_ready_button)
+
+/atom/movable/screen/lobby/button/ready/Click(location, control, params)
+ . = ..()
+ if(!.)
+ return
+ var/mob/dead/new_player/new_player = hud.mymob
+ ready = !ready
+ if(ready)
+ new_player.ready = PLAYER_READY_TO_PLAY
+ base_icon_state = "ready"
+ else
+ new_player.ready = PLAYER_NOT_READY
+ base_icon_state = "not_ready"
+ update_appearance(UPDATE_ICON)
+
+///Shown when the game has started
+/atom/movable/screen/lobby/button/join
+ screen_loc = "TOP:-13,CENTER:-58"
+ icon = 'icons/hud/lobby/join.dmi'
+ icon_state = "" //Default to not visible
+ base_icon_state = "join_game"
+ enabled = FALSE
+
+/atom/movable/screen/lobby/button/join/Initialize(mapload)
+ . = ..()
+ switch(SSticker.current_state)
+ if(GAME_STATE_PREGAME, GAME_STATE_STARTUP)
+ RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, .proc/show_join_button)
+ if(GAME_STATE_SETTING_UP)
+ set_button_status(TRUE)
+ RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, .proc/hide_join_button)
+ else
+ set_button_status(TRUE)
+
+/atom/movable/screen/lobby/button/join/Click(location, control, params)
+ . = ..()
+ if(!.)
+ return
+ if(!SSticker?.IsRoundInProgress())
+ to_chat(hud.mymob, span_boldwarning("The round is either not ready, or has already finished..."))
+ return
+
+ //Determines Relevent Population Cap
+ var/relevant_cap
+ var/hpc = CONFIG_GET(number/hard_popcap)
+ var/epc = CONFIG_GET(number/extreme_popcap)
+ if(hpc && epc)
+ relevant_cap = min(hpc, epc)
+ else
+ relevant_cap = max(hpc, epc)
+
+ var/mob/dead/new_player/new_player = hud.mymob
+
+ if(SSticker.queued_players.len || (relevant_cap && living_player_count() >= relevant_cap && !(ckey(new_player.key) in GLOB.admin_datums)))
+ to_chat(new_player, span_danger("[CONFIG_GET(string/hard_popcap_message)]"))
+
+ var/queue_position = SSticker.queued_players.Find(new_player)
+ if(queue_position == 1)
+ to_chat(new_player, span_notice("You are next in line to join the game. You will be notified when a slot opens up."))
+ else if(queue_position)
+ to_chat(new_player, span_notice("There are [queue_position-1] players in front of you in the queue to join the game."))
+ else
+ SSticker.queued_players += new_player
+ to_chat(new_player, span_notice("You have been added to the queue to join the game. Your position in queue is [SSticker.queued_players.len]."))
+ return
+ new_player.LateChoices()
+
+/atom/movable/screen/lobby/button/join/proc/show_join_button()
+ SIGNAL_HANDLER
+ set_button_status(TRUE)
+ UnregisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP)
+ RegisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP, .proc/hide_join_button)
+
+/atom/movable/screen/lobby/button/join/proc/hide_join_button()
+ SIGNAL_HANDLER
+ set_button_status(FALSE)
+ UnregisterSignal(SSticker, COMSIG_TICKER_ERROR_SETTING_UP)
+ RegisterSignal(SSticker, COMSIG_TICKER_ENTER_SETTING_UP, .proc/show_join_button)
+
+/atom/movable/screen/lobby/button/observe
+ screen_loc = "TOP:-40,CENTER:-54"
+ icon = 'icons/hud/lobby/observe.dmi'
+ icon_state = "observe_disabled"
+ base_icon_state = "observe"
+ enabled = FALSE
+
+/atom/movable/screen/lobby/button/observe/Initialize(mapload)
+ . = ..()
+ if(SSticker.current_state > GAME_STATE_STARTUP)
+ set_button_status(TRUE)
+ else
+ RegisterSignal(SSticker, COMSIG_TICKER_ENTER_PREGAME, .proc/enable_observing)
+
+/atom/movable/screen/lobby/button/observe/Click(location, control, params)
+ . = ..()
+ if(!.)
+ return
+ var/mob/dead/new_player/new_player = hud.mymob
+ new_player.make_me_an_observer()
+
+/atom/movable/screen/lobby/button/observe/proc/enable_observing()
+ SIGNAL_HANDLER
+ flick("[base_icon_state]_enabled", src)
+ set_button_status(TRUE)
+ UnregisterSignal(SSticker, COMSIG_TICKER_ENTER_PREGAME, .proc/enable_observing)
+
+/atom/movable/screen/lobby/button/settings
+ icon = 'icons/hud/lobby/bottom_buttons.dmi'
+ icon_state = "settings"
+ base_icon_state = "settings"
+ screen_loc = "TOP:-122,CENTER:+30"
+
+/atom/movable/screen/lobby/button/settings/Click(location, control, params)
+ . = ..()
+ if(!.)
+ return
+
+ hud.mymob.client.prefs.current_tab = GAME_PREFERENCES_TAB
+ hud.mymob.client.prefs.ShowChoices(hud.mymob)
+
+/atom/movable/screen/lobby/button/changelog_button
+ icon = 'icons/hud/lobby/bottom_buttons.dmi'
+ icon_state = "changelog"
+ base_icon_state = "changelog"
+ screen_loc ="TOP:-122,CENTER:+58"
+
+
+/atom/movable/screen/lobby/button/crew_manifest
+ icon = 'icons/hud/lobby/bottom_buttons.dmi'
+ icon_state = "crew_manifest"
+ base_icon_state = "crew_manifest"
+ screen_loc = "TOP:-122,CENTER:+2"
+
+/atom/movable/screen/lobby/button/crew_manifest/Click(location, control, params)
+ . = ..()
+ if(!.)
+ return
+ var/mob/dead/new_player/new_player = hud.mymob
+ new_player.ViewManifest()
+
+/atom/movable/screen/lobby/button/changelog_button/Click(location, control, params)
+ . = ..()
+ usr.client?.changelog()
+
+/atom/movable/screen/lobby/button/poll
+ icon = 'icons/hud/lobby/bottom_buttons.dmi'
+ icon_state = "poll"
+ base_icon_state = "poll"
+ screen_loc = "TOP:-122,CENTER:-26"
+
+ var/new_poll = FALSE
+
+/atom/movable/screen/lobby/button/poll/SlowInit(mapload)
+ . = ..()
+ if(!usr)
+ return
+ var/mob/dead/new_player/new_player = usr
+ if(is_guest_key(new_player.key))
+ set_button_status(FALSE)
+ return
+ if(!SSdbcore.Connect())
+ set_button_status(FALSE)
+ return
+ var/isadmin = FALSE
+ if(new_player.client?.holder)
+ isadmin = TRUE
+ var/datum/db_query/query_get_new_polls = SSdbcore.NewQuery({"
+ SELECT id FROM [format_table_name("poll_question")]
+ WHERE (adminonly = 0 OR :isadmin = 1)
+ AND Now() BETWEEN starttime AND endtime
+ AND deleted = 0
+ AND id NOT IN (
+ SELECT pollid FROM [format_table_name("poll_vote")]
+ WHERE ckey = :ckey
+ AND deleted = 0
+ )
+ AND id NOT IN (
+ SELECT pollid FROM [format_table_name("poll_textreply")]
+ WHERE ckey = :ckey
+ AND deleted = 0
+ )
+ "}, list("isadmin" = isadmin, "ckey" = new_player.ckey))
+ if(!query_get_new_polls.Execute())
+ qdel(query_get_new_polls)
+ set_button_status(FALSE)
+ return
+ if(query_get_new_polls.NextRow())
+ new_poll = TRUE
+ else
+ new_poll = FALSE
+ update_appearance(UPDATE_OVERLAYS)
+ qdel(query_get_new_polls)
+ if(QDELETED(new_player))
+ set_button_status(FALSE)
+ return
+
+/atom/movable/screen/lobby/button/poll/update_overlays()
+ . = ..()
+ if(new_poll)
+ . += mutable_appearance('icons/hud/lobby/poll_overlay.dmi', "new_poll")
+
+/atom/movable/screen/lobby/button/poll/Click(location, control, params)
+ . = ..()
+ if(!.)
+ return
+ var/mob/dead/new_player/new_player = hud.mymob
+ new_player.handle_player_polling()
diff --git a/code/controllers/configuration/entries/bot.dm b/code/controllers/configuration/entries/bot.dm
new file mode 100644
index 0000000000..071406ad65
--- /dev/null
+++ b/code/controllers/configuration/entries/bot.dm
@@ -0,0 +1,14 @@
+/datum/config_entry/flag/irc_announce_new_game
+ deprecated_by = /datum/config_entry/string/chat_announce_new_game
+
+/datum/config_entry/flag/irc_announce_new_game/DeprecationUpdate(value)
+ return "" //default broadcast
+
+/datum/config_entry/string/chat_announce_new_game
+ config_entry_value = null
+
+/datum/config_entry/string/chat_reboot_role
+
+/datum/config_entry/string/chat_roundend_notice_tag
+
+/datum/config_entry/string/chat_squawk_tag
diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm
index 9966f90ce7..b7a92ee48f 100644
--- a/code/controllers/configuration/entries/general.dm
+++ b/code/controllers/configuration/entries/general.dm
@@ -39,16 +39,6 @@
/datum/config_entry/flag/show_irc_name
-/datum/config_entry/flag/irc_announce_new_game
- deprecated_by = /datum/config_entry/string/chat_announce_new_game
-
-/datum/config_entry/flag/irc_announce_new_game/DeprecationUpdate(value)
- return "" //default broadcast
-
-/datum/config_entry/string/chat_announce_new_game
-
- config_entry_value = null
-
/datum/config_entry/string/default_view
config_entry_value = "15x15"
diff --git a/code/controllers/configuration/entries/logging.dm b/code/controllers/configuration/entries/logging.dm
index 52e4743a22..81013d8030 100644
--- a/code/controllers/configuration/entries/logging.dm
+++ b/code/controllers/configuration/entries/logging.dm
@@ -79,6 +79,10 @@
/datum/config_entry/flag/log_telecomms
config_entry_value = TRUE
+/// log economy
+/datum/config_entry/flag/log_econ
+ config_entry_value = TRUE
+
/// log certain expliotable parrots and other such fun things in a JSON file of twitter valid phrases.
/datum/config_entry/flag/log_twitter
config_entry_value = TRUE
diff --git a/code/controllers/subsystem/autotransfer.dm b/code/controllers/subsystem/autotransfer.dm
index 0afa07939e..4b4405c8cf 100644
--- a/code/controllers/subsystem/autotransfer.dm
+++ b/code/controllers/subsystem/autotransfer.dm
@@ -31,7 +31,7 @@ SUBSYSTEM_DEF(autotransfer)
if(world.time < targettime)
return
if(maxvotes == NO_MAXVOTES_CAP || maxvotes > curvotes)
- SSvote.initiate_vote("transfer","server")
+ SSvote.initiate_vote("transfer","server", votesystem=APPROVAL_VOTING)
targettime = targettime + voteinterval
curvotes++
else
diff --git a/code/controllers/subsystem/job.dm b/code/controllers/subsystem/job.dm
index 64af18a82a..98ebcf4606 100644
--- a/code/controllers/subsystem/job.dm
+++ b/code/controllers/subsystem/job.dm
@@ -367,9 +367,14 @@ SUBSYSTEM_DEF(job)
JobDebug("DO, Handling unrejectable unassigned")
//Mop up people who can't leave.
for(var/mob/dead/new_player/player in unassigned) //Players that wanted to back out but couldn't because they're antags (can you feel the edge case?)
- if(!GiveRandomJob(player))
- if(!AssignRole(player, SSjob.overflow_role)) //If everything is already filled, make them an assistant
- return FALSE //Living on the edge, the forced antagonist couldn't be assigned to overflow role (bans, client age) - just reroll
+ if(player.client.prefs.joblessrole == BERANDOMJOB) //Gives the player a random role if their preferences are set to it
+ if(!GiveRandomJob(player))
+ if(!AssignRole(player, SSjob.overflow_role)) //If everything is already filled, make them the overflow role
+ return FALSE //Living on the edge, the forced antagonist couldn't be assigned to overflow role (bans, client age) - just reroll
+ else //If the player prefers to return to lobby or be an assistant, give them assistant
+ if(!AssignRole(player, SSjob.overflow_role))
+ if(!GiveRandomJob(player)) //The forced antagonist couldn't be assigned to overflow role (bans, client age) - give a random role
+ return FALSE //Somehow the forced antagonist couldn't be assigned to the overflow role or the a random role - reroll
return validate_required_jobs(required_jobs)
diff --git a/code/controllers/subsystem/jukeboxes.dm b/code/controllers/subsystem/jukeboxes.dm
index 7be6d44a7f..3e87ff256a 100644
--- a/code/controllers/subsystem/jukeboxes.dm
+++ b/code/controllers/subsystem/jukeboxes.dm
@@ -1,3 +1,19 @@
+//As a brief warning to all those who dare tread upon these grounds:
+//The bulk of this code here was written years ago, back in the days of 512.
+//We were incredibly drunk back then. And nowadays, we've found that being drunk is a hard requirement for working with this code.
+//So if you're here to make changes? Brandish a glass. There are many sins here, but it's exactly as engineered as it needs to be.
+//We physically won't be able to tell you what half of this code does. The only thing that'll help you here is the ballmer peak.
+//Bottoms up, friend. And be sure to drink responsibly. Be sure to fetch some water, too; it eases the hangover. - Bhijn & Myr
+
+
+// Jukelist indices
+#define JUKE_TRACK 1
+#define JUKE_CHANNEL 2
+#define JUKE_BOX 3
+#define JUKE_FALLOFF 4
+#define JUKE_SOUND 5
+
+
SUBSYSTEM_DEF(jukeboxes)
name = "Jukeboxes"
wait = 5
@@ -25,27 +41,41 @@ SUBSYSTEM_DEF(jukeboxes)
var/channeltoreserve = pick(freejukeboxchannels)
if(!channeltoreserve)
return FALSE
+ var/sound/song_to_init = sound(T.song_path)
freejukeboxchannels -= channeltoreserve
- var/list/youvegotafreejukebox = list(T, channeltoreserve, jukebox, jukefalloff)
+ var/list/youvegotafreejukebox = list(T, channeltoreserve, jukebox, jukefalloff, song_to_init)
+
+ song_to_init.status = SOUND_MUTE
+ song_to_init.environment = 7
+ song_to_init.channel = channeltoreserve
+ song_to_init.volume = 1
+ song_to_init.falloff = jukefalloff
+ song_to_init.echo = list(0, null, -10000, null, null, null, null, null, null, null, null, null, null, 1, 1, 1, null, null)
+
activejukeboxes.len++
activejukeboxes[activejukeboxes.len] = youvegotafreejukebox
- //Due to changes in later versions of 512, SOUND_UPDATE no longer properly plays audio when a file is defined in the sound datum. As such, we are now required to init the audio before we can actually do anything with it.
- //Downsides to this? This means that you can *only* hear the jukebox audio if you were present on the server when it started playing, and it means that it's now impossible to add loops to the jukebox track list.
- var/sound/song_to_init = sound(T.song_path)
- song_to_init.status = SOUND_MUTE
for(var/mob/M in GLOB.player_list)
if(!M.client)
continue
if(!(M.client.prefs.toggles & SOUND_INSTRUMENTS))
continue
- M.playsound_local(M, null, 100, channel = youvegotafreejukebox[2], S = song_to_init)
+ SEND_SOUND(M, song_to_init)
return activejukeboxes.len
+
+//Updates jukebox by transferring to different object or modifying falloff.
+/datum/controller/subsystem/jukeboxes/proc/updatejukebox(IDtoupdate, obj/jukebox, jukefalloff)
+ if(islist(activejukeboxes[IDtoupdate]))
+ if(istype(jukebox))
+ activejukeboxes[IDtoupdate][JUKE_BOX] = jukebox
+ if(!isnull(jukefalloff))
+ activejukeboxes[IDtoupdate][JUKE_FALLOFF] = jukefalloff
+
/datum/controller/subsystem/jukeboxes/proc/removejukebox(IDtoremove)
if(islist(activejukeboxes[IDtoremove]))
- var/jukechannel = activejukeboxes[IDtoremove][2]
+ var/jukechannel = activejukeboxes[IDtoremove][JUKE_CHANNEL]
for(var/mob/M in GLOB.player_list)
if(!M.client)
continue
@@ -85,37 +115,74 @@ SUBSYSTEM_DEF(jukeboxes)
if(!jukeinfo.len)
stack_trace("Active jukebox without any associated metadata.")
continue
- var/datum/track/juketrack = jukeinfo[1]
+ var/datum/track/juketrack = jukeinfo[JUKE_TRACK]
if(!istype(juketrack))
stack_trace("Invalid jukebox track datum.")
continue
- var/obj/jukebox = jukeinfo[3]
+ var/obj/jukebox = jukeinfo[JUKE_BOX]
if(!istype(jukebox))
stack_trace("Nonexistant or invalid object associated with jukebox.")
continue
- var/sound/song_played = sound(juketrack.song_path)
+
+ var/list/audible_zlevels = get_multiz_accessible_levels(jukebox.z) //TODO - for multiz refresh, this should use the cached zlevel connections var in SSMapping. For now this is fine!
+
+ var/sound/song_played = jukeinfo[JUKE_SOUND]
var/turf/currentturf = get_turf(jukebox)
var/area/currentarea = get_area(jukebox)
var/list/hearerscache = hearers(7, jukebox)
+ var/targetfalloff = jukeinfo[JUKE_FALLOFF]
+ var/mixes = ((targetfalloff*250)-750)
+ var/inrange
+ var/pressure_factor
- song_played.falloff = jukeinfo[4]
+
+ var/datum/gas_mixture/source_env = (istype(currentturf) ? currentturf.return_air() : null)
+ var/datum/gas_mixture/hearer_env //We init this var outside of the mob loop for the sake of performance
+ var/turf/hearerturf //ditto
+
+ var/source_pressure = (istype(source_env) ? source_env.return_pressure() : 0)
+
+ song_played.falloff = targetfalloff
+ song_played.volume = min((targetfalloff * 50), 100)
for(var/mob/M in GLOB.player_list)
if(!M.client)
continue
- if(!(M.client.prefs.toggles & SOUND_INSTRUMENTS) || !M.can_hear())
- M.stop_sound_channel(jukeinfo[2])
+ if(!(M.client.prefs.toggles & SOUND_INSTRUMENTS))
+ M.stop_sound_channel(jukeinfo[JUKE_CHANNEL])
continue
- var/inrange = FALSE
- if(jukebox.z == M.z) //todo - expand this to work with mining planet z-levels when robust jukebox audio gets merged to master
- song_played.status = SOUND_UPDATE
- if(get_area(M) == currentarea)
- inrange = TRUE
- else if(M in hearerscache)
- inrange = TRUE
- else
- song_played.status = SOUND_MUTE | SOUND_UPDATE //Setting volume = 0 doesn't let the sound properties update at all, which is lame.
- M.playsound_local(currentturf, null, 100, channel = jukeinfo[2], S = song_played, envwet = (inrange ? -250 : 0), envdry = (inrange ? 0 : -10000))
+ inrange = FALSE
+ song_played.status = SOUND_MUTE | SOUND_UPDATE
+
+ if(source_pressure)
+ hearerturf = get_turf(M)
+ hearer_env = (istype(hearerturf) ? hearerturf.return_air() : null)
+ if(istype(hearer_env))
+ pressure_factor = min(source_pressure, hearer_env.return_pressure())
+
+ if(pressure_factor && targetfalloff && M.can_hear() && (M.z in audible_zlevels))
+ if(get_area(M) == currentarea)
+ inrange = TRUE
+ else if(M in hearerscache)
+ inrange = TRUE
+
+ song_played.x = (currentturf.x - M.x) * SOUND_DEFAULT_DISTANCE_MULTIPLIER
+ song_played.z = (currentturf.y - M.y) * SOUND_DEFAULT_DISTANCE_MULTIPLIER
+ song_played.y = (((currentturf.z - M.z) * 10 * SOUND_DEFAULT_DISTANCE_MULTIPLIER) + ((currentturf.z < M.z) ? -5 : 5))
+
+ if(pressure_factor < ONE_ATMOSPHERE)
+ song_played.volume = (min((targetfalloff * 50), 100) * max((pressure_factor - SOUND_MINIMUM_PRESSURE)/(ONE_ATMOSPHERE - SOUND_MINIMUM_PRESSURE), 1))
+
+ song_played.echo[1] = (inrange ? 0 : -10000)
+ song_played.echo[3] = (inrange ? mixes : max(mixes, 0))
+ song_played.status = SOUND_UPDATE
+ SEND_SOUND(M, song_played)
CHECK_TICK
return
+
+#undef JUKE_TRACK
+#undef JUKE_CHANNEL
+#undef JUKE_BOX
+#undef JUKE_FALLOFF
+#undef JUKE_SOUND
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 05bef50714..d171366760 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -164,8 +164,8 @@ SUBSYSTEM_DEF(ticker)
to_chat(world, "Welcome to [station_name()]!")
send2chat("New round starting on [SSmapping.config.map_name]!", CONFIG_GET(string/chat_announce_new_game))
current_state = GAME_STATE_PREGAME
- //Everyone who wants to be an observer is now spawned
- create_observers()
+ SEND_SIGNAL(src, COMSIG_TICKER_ENTER_PREGAME)
+
fire()
if(GAME_STATE_PREGAME)
//lobby stats for statpanels
@@ -203,6 +203,7 @@ SUBSYSTEM_DEF(ticker)
for(var/client/C in SSvote.voting)
C << browse(null, "window=vote;can_close=0")
SSvote.reset()
+ SEND_SIGNAL(src, COMSIG_TICKER_ENTER_SETTING_UP)
current_state = GAME_STATE_SETTING_UP
Master.SetRunLevel(RUNLEVEL_SETUP)
if(start_immediately)
@@ -215,6 +216,7 @@ SUBSYSTEM_DEF(ticker)
start_at = world.time + (CONFIG_GET(number/lobby_countdown) * 10)
timeLeft = null
Master.SetRunLevel(RUNLEVEL_LOBBY)
+ SEND_SIGNAL(src, COMSIG_TICKER_ERROR_SETTING_UP)
if(GAME_STATE_PLAYING)
mode.process(wait * 0.1)
@@ -378,8 +380,6 @@ SUBSYSTEM_DEF(ticker)
LAZYOR(player.client.prefs.characters_joined_as, player.new_character.real_name)
else
stack_trace("WARNING: Either a player did not have a new_character, did not have a client, or did not have preferences. This is VERY bad.")
- else
- player.new_player_panel()
CHECK_TICK
/datum/controller/subsystem/ticker/proc/collect_minds()
@@ -612,7 +612,7 @@ SUBSYSTEM_DEF(ticker)
var/list/ded = SSblackbox.first_death
if(ded.len)
var/last_words = ded["last_words"] ? " Their last words were: \"[ded["last_words"]]\"" : ""
- news_message += "\nNT Sanctioned Psykers picked up faint traces of someone near the station, allegedly having had died. Their name was: [ded["name"]], [ded["role"]], at [ded["area"]].[last_words]"
+ news_message += "\nNT Sanctioned Psykers picked up faint traces of someone near the station, allegedly having had died.\nTheir name was: [ded["name"]], [ded["role"]], at [ded["area"]].[last_words]"
else
news_message += "\nNT Sanctioned Psykers proudly confirm reports that nobody died this shift!"
@@ -633,13 +633,6 @@ SUBSYSTEM_DEF(ticker)
else
timeLeft = newtime
-//Everyone who wanted to be an observer gets made one now
-/datum/controller/subsystem/ticker/proc/create_observers()
- for(var/mob/dead/new_player/player in GLOB.player_list)
- if(player.ready == PLAYER_READY_TO_OBSERVE && player.mind)
- //Break chain since this has a sleep input in it
- addtimer(CALLBACK(player, /mob/dead/new_player.proc/make_me_an_observer), 1)
-
/datum/controller/subsystem/ticker/proc/load_mode()
var/mode = trim(file2text("data/mode.txt"))
if(mode)
diff --git a/code/controllers/subsystem/title.dm b/code/controllers/subsystem/title.dm
index 3cc3d9dd99..1dbfb1540c 100644
--- a/code/controllers/subsystem/title.dm
+++ b/code/controllers/subsystem/title.dm
@@ -10,7 +10,7 @@ SUBSYSTEM_DEF(title)
/datum/controller/subsystem/title/Initialize()
if(file_path && icon)
- return ..()
+ return
if(fexists("data/previous_title.dat"))
var/previous_path = file2text("data/previous_title.dat")
@@ -31,13 +31,16 @@ SUBSYSTEM_DEF(title)
if(length(title_screens))
file_path = "[global.config.directory]/title_screens/images/[pick(title_screens)]"
- if(!file_path || !fexists(file_path))
- file_path = "icons/default_title.dmi"
+ if(!file_path)
+ file_path = "icons/runtime/default_title.dmi"
- if(fexists(file_path))
- icon = new(fcopy_rsc(file_path))
- if(splash_turf)
- splash_turf.icon = icon
+ ASSERT(fexists(file_path))
+
+ icon = new(fcopy_rsc(file_path))
+
+ if(splash_turf)
+ splash_turf.icon = icon
+ splash_turf.handle_generic_titlescreen_sizes()
return ..()
diff --git a/code/datums/dna.dm b/code/datums/dna.dm
index 43dad6cef8..5d0ff4daec 100644
--- a/code/datums/dna.dm
+++ b/code/datums/dna.dm
@@ -77,6 +77,7 @@
new_dna.species = new species.type
new_dna.species.say_mod = species.say_mod
new_dna.species.exotic_blood_color = species.exotic_blood_color //it can change from the default value
+ new_dna.species.exotic_blood_blend_mode = species.exotic_blood_blend_mode
new_dna.species.eye_type = species.eye_type
new_dna.species.limbs_id = species.limbs_id || species.id
new_dna.real_name = real_name
diff --git a/code/datums/elements/flavor_text.dm b/code/datums/elements/flavor_text.dm
index 6217665157..b61bacbd19 100644
--- a/code/datums/elements/flavor_text.dm
+++ b/code/datums/elements/flavor_text.dm
@@ -76,10 +76,10 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
if(examine_full_view)
examine_list += "[msg]"
return
- if(length_char(msg) <= 40)
+ if(length_char(msg) <= MAX_FLAVOR_PREVIEW_LEN)
examine_list += "[msg]"
else
- examine_list += "[copytext_char(msg, 1, 37)]... More..."
+ examine_list += "[copytext_char(msg, 1, (MAX_FLAVOR_PREVIEW_LEN - 3))]... More..."
/datum/element/flavor_text/Topic(href, href_list)
. = ..()
@@ -199,7 +199,7 @@ GLOBAL_LIST_EMPTY(mobs_with_editable_flavor_text) //et tu, hacky code
if(ismob(target))
add_verb(target, /mob/proc/set_pose)
-/datum/element/flavor_Text/carbon/temporary/Detach(datum/source, force)
+/datum/element/flavor_text/carbon/temporary/Detach(datum/source, force)
. = ..()
if(ismob(source))
remove_verb(source, /mob/proc/set_pose)
diff --git a/code/datums/mutable_appearance.dm b/code/datums/mutable_appearance.dm
index 968809747b..b04f55ef0e 100644
--- a/code/datums/mutable_appearance.dm
+++ b/code/datums/mutable_appearance.dm
@@ -10,7 +10,7 @@
// And yes this does have to be in the constructor, BYOND ignores it if you set it as a normal var
// Helper similar to image()
-/proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER, plane = FLOAT_PLANE, alpha = 255, appearance_flags = NONE, color = "#FFFFFF")
+/proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER, plane = FLOAT_PLANE, alpha = 255, appearance_flags = NONE, color = "#FFFFFF", blend_mode = BLEND_DEFAULT)
var/mutable_appearance/MA = new()
MA.icon = icon
MA.icon_state = icon_state
@@ -19,5 +19,6 @@
MA.alpha = alpha
MA.appearance_flags |= appearance_flags
MA.color = color
+ MA.blend_mode = blend_mode
return MA
diff --git a/code/datums/traits/_quirk.dm b/code/datums/traits/_quirk.dm
index 99fa6e73c5..ce1796346a 100644
--- a/code/datums/traits/_quirk.dm
+++ b/code/datums/traits/_quirk.dm
@@ -14,6 +14,7 @@
/// should we immediately call on_spawn or add a timer to trigger
var/on_spawn_immediate = TRUE
var/mob/living/quirk_holder
+ var/processing_quirk = FALSE
/datum/quirk/New(mob/living/quirk_mob, spawn_effects)
if(!quirk_mob || (human_only && !ishuman(quirk_mob)) || quirk_mob.has_quirk(type))
@@ -25,7 +26,8 @@
quirk_holder.roundstart_quirks += src
if(mob_trait)
ADD_TRAIT(quirk_holder, mob_trait, ROUNDSTART_TRAIT)
- START_PROCESSING(SSquirks, src)
+ if(processing_quirk)
+ START_PROCESSING(SSquirks, src)
add()
if(spawn_effects)
if(on_spawn_immediate)
@@ -35,7 +37,8 @@
addtimer(CALLBACK(src, .proc/post_add), 30)
/datum/quirk/Destroy()
- STOP_PROCESSING(SSquirks, src)
+ if(processing_quirk)
+ STOP_PROCESSING(SSquirks, src)
remove()
if(quirk_holder)
to_chat(quirk_holder, lose_text)
diff --git a/code/datums/traits/good.dm b/code/datums/traits/good.dm
index a0f62a32b8..3bcca275a5 100644
--- a/code/datums/traits/good.dm
+++ b/code/datums/traits/good.dm
@@ -72,6 +72,7 @@
mob_trait = TRAIT_JOLLY
mood_quirk = TRUE
medical_record_text = "Patient demonstrates constant euthymia irregular for environment. It's a bit much, to be honest."
+ processing_quirk = TRUE
/datum/quirk/jolly/on_process()
if(prob(0.05))
diff --git a/code/datums/traits/negative.dm b/code/datums/traits/negative.dm
index 8958226ff4..8d3acf0f6d 100644
--- a/code/datums/traits/negative.dm
+++ b/code/datums/traits/negative.dm
@@ -25,6 +25,7 @@
lose_text = "You no longer feel depressed." //if only it were that easy!
medical_record_text = "Patient has a severe mood disorder, causing them to experience acute episodes of depression."
mood_quirk = TRUE
+ processing_quirk = TRUE
/datum/quirk/depression/on_process()
if(prob(0.05))
@@ -38,6 +39,7 @@
medical_record_text = "Patient demonstrates an unnatural attachment to a family heirloom."
var/obj/item/heirloom
var/where
+ processing_quirk = TRUE
GLOBAL_LIST_EMPTY(family_heirlooms)
@@ -102,6 +104,7 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
gain_text = "You feel smooth."
lose_text = "You feel wrinkled again."
medical_record_text = "Patient has a tumor in their brain that is slowly driving them to brain death."
+ processing_quirk = TRUE
/datum/quirk/brainproblems/on_process()
quirk_holder.adjustOrganLoss(ORGAN_SLOT_BRAIN, 0.2)
@@ -129,25 +132,38 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
value = -1
medical_record_text = "Patient demonstrates a fear of the dark. (Seriously?)"
-/datum/quirk/nyctophobia/on_process()
- var/mob/living/carbon/human/H = quirk_holder
- if(H.dna.species.id in list("shadow", "nightmare"))
- return //we're tied with the dark, so we don't get scared of it; don't cleanse outright to avoid cheese
- var/turf/T = get_turf(quirk_holder)
- var/lums = T.get_lumcount()
- if(lums <= 0.2)
- if(quirk_holder.m_intent == MOVE_INTENT_RUN)
- addtimer(CALLBACK(src, .proc/recheck),2) //0.2 seconds of being in the dark
- SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "nyctophobia", /datum/mood_event/nyctophobia)
- else
- SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "nyctophobia")
+/datum/quirk/nyctophobia/add()
+ RegisterSignal(quirk_holder, COMSIG_MOVABLE_MOVED, .proc/on_holder_moved)
-/datum/quirk/nyctophobia/proc/recheck()
- var/turf/T = get_turf(quirk_holder)
- var/lums = T.get_lumcount()
- if(lums <= 0.2) //check again, did they remain in the dark for 0.2 seconds?
- to_chat(quirk_holder, "Easy, easy, take it slow... you're in the dark...")
+/datum/quirk/nyctophobia/remove()
+ UnregisterSignal(quirk_holder, COMSIG_MOVABLE_MOVED)
+ SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "nyctophobia")
+
+/// Called when the quirk holder moves. Updates the quirk holder's mood.
+/datum/quirk/nyctophobia/proc/on_holder_moved(mob/living/source, atom/old_loc, dir, forced)
+ if(quirk_holder.stat != CONSCIOUS || quirk_holder.IsSleeping() || quirk_holder.IsUnconscious())
+ return
+
+ var/mob/living/carbon/human/human_holder = quirk_holder
+
+ if(human_holder.dna?.species.id in list(SPECIES_SHADOW, SPECIES_NIGHTMARE))
+ return
+
+ if((human_holder.sight & SEE_TURFS) == SEE_TURFS)
+ return
+
+ var/turf/holder_turf = get_turf(quirk_holder)
+
+ var/lums = holder_turf.get_lumcount()
+
+ if(lums > 0.2)
+ SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "nyctophobia")
+ return
+
+ if(quirk_holder.m_intent == MOVE_INTENT_RUN)
+ to_chat(quirk_holder, span_warning("Easy, easy, take it slow... you're in the dark..."))
quirk_holder.toggle_move_intent()
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "nyctophobia", /datum/mood_event/nyctophobia)
/datum/quirk/lightless
name = "Light Sensitivity"
@@ -157,6 +173,36 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
lose_text = "Enlightening."
medical_record_text = "Despite my warnings, the patient refuses turn on the lights, only to end up rolling down a full flight of stairs and into the cellar."
+/datum/quirk/lightless/add()
+ RegisterSignal(quirk_holder, COMSIG_MOVABLE_MOVED, .proc/on_holder_moved)
+
+/datum/quirk/lightless/remove()
+ UnregisterSignal(quirk_holder, COMSIG_MOVABLE_MOVED)
+ SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "brightlight")
+
+/datum/quirk/lightless/proc/on_holder_moved(mob/living/source, atom/old_loc, dir, forced)
+ if(quirk_holder.stat != CONSCIOUS || quirk_holder.IsSleeping() || quirk_holder.IsUnconscious())
+ return
+
+ var/mob/living/carbon/human/human_holder = quirk_holder
+
+ if(human_holder.dna?.species.id in list(SPECIES_SHADOW, SPECIES_NIGHTMARE))
+ return
+
+ if((human_holder.sight & SEE_TURFS) == SEE_TURFS)
+ return
+
+ var/turf/holder_turf = get_turf(quirk_holder)
+
+ var/lums = holder_turf.get_lumcount()
+
+ if(lums < 0.8)
+ SEND_SIGNAL(quirk_holder, COMSIG_CLEAR_MOOD_EVENT, "brightlight")
+ return
+
+ SEND_SIGNAL(quirk_holder, COMSIG_ADD_MOOD_EVENT, "brightlight", /datum/mood_event/brightlight)
+
+
/datum/quirk/lightless/on_process()
var/turf/T = get_turf(quirk_holder)
var/lums = T.get_lumcount()
@@ -236,6 +282,7 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
gain_text = "..."
lose_text = "You feel in tune with the world again."
medical_record_text = "Patient suffers from acute Reality Dissociation Syndrome and experiences vivid hallucinations."
+ processing_quirk = TRUE
/datum/quirk/insanity/on_process()
if(quirk_holder.reagents.has_reagent(/datum/reagent/toxin/mindbreaker))
@@ -261,6 +308,7 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
lose_text = "You feel easier about talking again." //if only it were that easy!
medical_record_text = "Patient is usually anxious in social encounters and prefers to avoid them."
var/dumb_thing = TRUE
+ processing_quirk = TRUE
/datum/quirk/social_anxiety/add()
RegisterSignal(quirk_holder, COMSIG_MOB_EYECONTACT, .proc/eye_contact)
@@ -425,3 +473,21 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
gain_text = "You can't smell anything!"
lose_text = "You can smell again!"
medical_record_text = "Patient suffers from anosmia and is incapable of smelling gases or particulates."
+
+/datum/quirk/paper_skin
+ name = "Paper Skin"
+ desc = "Your flesh is weaker, resulting in receiving cuts more easily."
+ value = -1
+ mob_trait = TRAIT_PAPER_SKIN
+ gain_text = "Your flesh feels weak!"
+ lose_text = "Your flesh feels more durable!"
+ medical_record_text = "Patient suffers from weak flesh, resulting in them receiving cuts far more easily."
+
+/datum/quirk/glass_bones
+ name = "Glass Bones"
+ desc = "Your bones are far more brittle, and more vulnerable to breakage."
+ value = -1
+ mob_trait = TRAIT_GLASS_BONES
+ gain_text = "Your bones feels weak!"
+ lose_text = "Your bones feels more durable!"
+ medical_record_text = "Patient suffers from brittle bones, resulting in them receiving breakages far more easily."
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 242f3123e1..c3be65243b 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -702,7 +702,7 @@
var/blood_id = get_blood_id()
if(!(blood_id in GLOB.blood_reagent_types))
return
- return list("color" = BLOOD_COLOR_HUMAN, "ANIMAL DNA" = "Y-")
+ return list("color" = BLOOD_COLOR_HUMAN, "blendmode" = BLEND_MULTIPLY, "ANIMAL DNA" = "Y-")
/mob/living/carbon/get_blood_dna_list()
var/blood_id = get_blood_id()
@@ -711,14 +711,16 @@
var/list/blood_dna = list()
if(dna)
blood_dna["color"] = dna.species.exotic_blood_color //so when combined, the list grows with the number of colors
+ blood_dna["blendmode"] = dna.species.exotic_blood_blend_mode
blood_dna[dna.unique_enzymes] = dna.blood_type
else
blood_dna["color"] = BLOOD_COLOR_HUMAN
+ blood_dna["blendmode"] = BLEND_MULTIPLY
blood_dna["UNKNOWN DNA"] = "X*"
return blood_dna
/mob/living/carbon/alien/get_blood_dna_list()
- return list("color" = BLOOD_COLOR_XENO, "UNKNOWN DNA" = "X*")
+ return list("color" = BLOOD_COLOR_XENO, "blendmode" = BLEND_MULTIPLY, "UNKNOWN DNA" = "X*")
//to add a mob's dna info into an object's blood_DNA list.
/atom/proc/transfer_mob_blood_dna(mob/living/L)
@@ -737,6 +739,7 @@
var/old = blood_DNA["color"]
blood_DNA["color"] = BlendRGB(blood_DNA["color"], new_blood_dna["color"])
changed = old != blood_DNA["color"]
+ blood_DNA["blendmode"] = new_blood_dna["blendmode"]
if(blood_DNA.len == old_length)
return FALSE
return changed
@@ -756,6 +759,7 @@
blood_DNA["color"] = blood_dna["color"]
else
blood_DNA["color"] = BlendRGB(blood_DNA["color"], blood_dna["color"])
+ blood_DNA["blendmode"] = blood_dna["blendmode"]
//to add blood from a mob onto something, and transfer their dna info
/atom/proc/add_mob_blood(mob/living/M)
@@ -826,6 +830,11 @@
/atom/proc/blood_DNA_to_color()
return (blood_DNA && blood_DNA["color"]) || BLOOD_COLOR_HUMAN
+/atom/proc/blood_DNA_to_blend()
+ if(blood_DNA && !isnull(blood_DNA["blendmode"]))
+ return blood_DNA["blendmode"]
+ return BLEND_MULTIPLY
+
/atom/proc/clean_blood()
. = blood_DNA? TRUE : FALSE
blood_DNA = null
@@ -1192,6 +1201,8 @@
log_mecha(log_text)
if(LOG_SHUTTLE)
log_shuttle(log_text)
+ if(LOG_ECON)
+ log_econ(log_text)
else
stack_trace("Invalid individual logging type: [message_type]. Defaulting to [LOG_GAME] (LOG_GAME).")
log_game(log_text)
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index e9a1fb61e0..0d95448702 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -312,6 +312,36 @@ Class Procs:
return TRUE // If we passed all of those checks, woohoo! We can interact with this machine.
+/obj/machinery/proc/can_transact(obj/item/card/id/thecard, allowdepartment, silent)
+ if(!istype(thecard))
+ if(!silent)
+ say("No card found.")
+ return FALSE
+ else if (!thecard.registered_account)
+ if(!silent)
+ say("No account found.")
+ return FALSE
+ else if(!allowdepartment && !thecard.registered_account.account_job)
+ if(!silent)
+ say("Departmental accounts have been blacklisted from personal expenses due to embezzlement.")
+ return FALSE
+ return TRUE
+
+/obj/machinery/proc/attempt_transact(obj/item/card/id/thecard, transaction_cost)
+ if(!istype(thecard))
+ return FALSE
+ var/datum/bank_account/account = thecard.registered_account
+ if(!istype(account))
+ return FALSE
+
+ if(transaction_cost)
+ if(!account.adjust_money(-transaction_cost))
+ return FALSE
+ var/datum/bank_account/D = SSeconomy.get_dep_account(payment_department)
+ if(D)
+ D.adjust_money(transaction_cost)
+ return TRUE
+
/obj/machinery/proc/check_nap_violations()
if(!SSeconomy.full_ancap)
return TRUE
diff --git a/code/game/machinery/dance_machine.dm b/code/game/machinery/dance_machine.dm
index 8b24a3804f..956b3e9c79 100644
--- a/code/game/machinery/dance_machine.dm
+++ b/code/game/machinery/dance_machine.dm
@@ -6,17 +6,22 @@
verb_say = "states"
density = TRUE
req_one_access = list(ACCESS_BAR, ACCESS_KITCHEN, ACCESS_HYDROPONICS, ACCESS_ENGINE, ACCESS_CARGO, ACCESS_THEATRE)
+ payment_department = ACCOUNT_SRV
var/active = FALSE
var/list/rangers = list()
var/stop = 0
var/volume = 70
- var/datum/track/selection = null
+ var/queuecost = PRICE_CHEAP //Set to -1 to make this jukebox require access for queueing
+ var/datum/track/playing = null
+ var/datum/track/selectedtrack = null
+ var/list/queuedplaylist = list()
+ var/queuecooldown //This var exists solely to prevent accidental repeats of John Mulaney's 'What's New Pussycat?' incident. Intentional, however......
/obj/machinery/jukebox/disco
name = "radiant dance machine mark IV"
desc = "The first three prototypes were discontinued after mass casualty incidents."
icon_state = "disco"
- req_access = list(ACCESS_ENGINE)
+ req_one_access = list(ACCESS_ENGINE)
anchored = FALSE
var/list/spotlights = list()
var/list/sparkles = list()
@@ -24,7 +29,7 @@
/obj/machinery/jukebox/disco/indestructible
name = "radiant dance machine mark V"
desc = "Now redesigned with data gathered from the extensive disco and plasma research."
- req_access = null
+ req_one_access = null
anchored = TRUE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
flags_1 = NODECONSTRUCT_1
@@ -46,6 +51,16 @@
return
return ..()
+/obj/machinery/jukebox/emag_act(mob/user)
+ . = ..()
+ if(obj_flags & EMAGGED)
+ return
+ obj_flags |= EMAGGED
+ queuecost = PRICE_FREE
+ req_one_access = null
+ to_chat(user, "You've bypassed [src]'s audio volume limiter, and enabled free play.")
+ return TRUE
+
/obj/machinery/jukebox/update_icon_state()
if(active)
icon_state = "[initial(icon_state)]-active"
@@ -56,7 +71,7 @@
if(!anchored)
to_chat(user,"This device must be anchored by a wrench!")
return UI_CLOSE
- if(!allowed(user) && !isobserver(user))
+ if((queuecost < 0 && !allowed(user)) && !isobserver(user))
to_chat(user,"Error: Access Denied.")
user.playsound_local(src, 'sound/misc/compiler-failure.ogg', 25, TRUE)
return UI_CLOSE
@@ -77,18 +92,21 @@
data["active"] = active
data["songs"] = list()
for(var/datum/track/S in SSjukeboxes.songs)
- var/list/track_data = list(
- name = S.song_name
- )
+ var/list/track_data = list(name = S.song_name)
data["songs"] += list(track_data)
+ data["queued_tracks"] = list()
+ for(var/datum/track/S in queuedplaylist)
+ var/list/track_data = list(name = S.song_name)
+ data["queued_tracks"] += list(track_data)
data["track_selected"] = null
data["track_length"] = null
- data["track_beat"] = null
- if(selection)
- data["track_selected"] = selection.song_name
- data["track_length"] = DisplayTimeText(selection.song_length)
- data["track_beat"] = selection.song_beat
+ if(playing)
+ data["track_selected"] = playing.song_name
+ data["track_length"] = DisplayTimeText(playing.song_length)
data["volume"] = volume
+ data["is_emagged"] = (obj_flags & EMAGGED)
+ data["cost_for_play"] = queuecost
+ data["has_access"] = allowed(user)
return data
/obj/machinery/jukebox/ui_act(action, list/params)
@@ -100,57 +118,91 @@
if("toggle")
if(QDELETED(src))
return
- if(!active)
- if(stop > world.time)
- to_chat(usr, "Error: The device is still resetting from the last activation, it will be ready again in [DisplayTimeText(stop-world.time)].")
- playsound(src, 'sound/misc/compiler-failure.ogg', 50, TRUE)
- return
+ if(!allowed(usr))
+ return
+ if(!active && !playing)
activate_music()
- START_PROCESSING(SSobj, src)
- return TRUE
else
stop = 0
- return TRUE
- if("select_track")
- if(active)
- to_chat(usr, "Error: You cannot change the song until the current one is over.")
+ return TRUE
+ if("add_to_queue")
+ if(QDELETED(src))
return
+ if(world.time < queuecooldown)
+ return
+ if(!istype(selectedtrack, /datum/track))
+ return
+ if(!allowed(usr) && queuecost)
+ var/obj/item/card/id/C
+ if(isliving(usr))
+ var/mob/living/L = usr
+ C = L.get_idcard(TRUE)
+ if(!can_transact(C))
+ queuecooldown = world.time + (1 SECONDS)
+ playsound(src, 'sound/misc/compiler-failure.ogg', 25, TRUE)
+ return
+ if(!attempt_transact(C, queuecost))
+ say("Insufficient funds.")
+ queuecooldown = world.time + (1 SECONDS)
+ playsound(src, 'sound/misc/compiler-failure.ogg', 25, TRUE)
+ return
+ to_chat(usr, "You spend [queuecost] credits to queue [selectedtrack.song_name].")
+ log_econ("[queuecost] credits were inserted into [src] by [key_name(usr)] (ID: [C.registered_name]) to queue [selectedtrack.song_name].")
+ queuedplaylist += selectedtrack
+ if(active)
+ say("[selectedtrack.song_name] has been added to the queue.")
+ else if(!playing)
+ activate_music()
+ playsound(src, 'sound/machines/ping.ogg', 50, TRUE)
+ queuecooldown = world.time + (3 SECONDS)
+ return TRUE
+ if("select_track")
var/list/available = list()
for(var/datum/track/S in SSjukeboxes.songs)
available[S.song_name] = S
var/selected = params["track"]
if(QDELETED(src) || !selected || !istype(available[selected], /datum/track))
return
- selection = available[selected]
+ selectedtrack = available[selected]
return TRUE
if("set_volume")
+ if(!allowed(usr))
+ return
var/new_volume = params["volume"]
if(new_volume == "reset")
volume = initial(volume)
- return TRUE
else if(new_volume == "min")
volume = 0
- return TRUE
else if(new_volume == "max")
- volume = 100
- return TRUE
+ volume = ((obj_flags & EMAGGED) ? 210 : 100)
else if(text2num(new_volume) != null)
- volume = text2num(new_volume)
- return TRUE
+ volume = clamp(0, text2num(new_volume), ((obj_flags & EMAGGED) ? 210 : 100))
+ var/wherejuke = SSjukeboxes.findjukeboxindex(src)
+ if(wherejuke)
+ SSjukeboxes.updatejukebox(wherejuke, jukefalloff = volume/35)
+ return TRUE
/obj/machinery/jukebox/proc/activate_music()
- var/jukeboxslottotake = SSjukeboxes.addjukebox(src, selection, 2)
+ if(playing || !queuedplaylist.len)
+ return FALSE
+ playing = queuedplaylist[1]
+ var/jukeboxslottotake = SSjukeboxes.addjukebox(src, playing, volume/35)
if(jukeboxslottotake)
active = TRUE
update_icon()
START_PROCESSING(SSobj, src)
- stop = world.time + selection.song_length
+ stop = world.time + playing.song_length
+ queuedplaylist.Cut(1, 2)
+ say("Now playing: [playing.song_name]")
+ playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, TRUE)
return TRUE
else
return FALSE
/obj/machinery/jukebox/disco/activate_music()
- ..()
+ . = ..()
+ if(!.)
+ return
dance_setup()
lights_spin()
@@ -243,7 +295,7 @@
S.pixel_y = 7
S.forceMove(get_turf(src))
sleep(7)
- if(selection.song_name == "Engineering's Ultimate High-Energy Hustle")
+ if(playing.song_name == "Engineering's Ultimate High-Energy Hustle")
sleep(280)
for(var/obj/reveal in sparkles)
reveal.alpha = 255
@@ -299,7 +351,7 @@
continue
if(prob(2)) // Unique effects for the dance floor that show up randomly to mix things up
INVOKE_ASYNC(src, .proc/hierofunk)
- sleep(selection.song_beat)
+ sleep(playing.song_beat)
#undef DISCO_INFENO_RANGE
@@ -431,6 +483,7 @@
return
SSjukeboxes.removejukebox(position)
STOP_PROCESSING(SSobj, src)
+ playing = null
rangers = list()
/obj/machinery/jukebox/disco/dance_over()
@@ -439,12 +492,21 @@
QDEL_LIST(sparkles)
/obj/machinery/jukebox/process()
- if(active && world.time >= stop)
- active = FALSE
- dance_over()
- playsound(src,'sound/machines/terminal_off.ogg',50,1)
- update_icon()
- stop = world.time + 100
+ if(active)
+ if(world.time >= stop)
+ active = FALSE
+ dance_over()
+ if(stop && queuedplaylist.len)
+ activate_music()
+ else
+ playsound(src,'sound/machines/terminal_off.ogg',50,1)
+ update_icon()
+ playing = null
+ stop = 0
+ else if(volume > 140) // BOOM BOOM BOOM BOOM
+ for(var/mob/living/carbon/C in hearers(round(volume/35), src)) // I WANT YOU IN MY ROOM
+ if(istype(C)) // LETS SPEND THE NIGHT TOGETHER
+ C.adjustEarDamage(max((((volume/35) - sqrt(get_dist(C, src) * 4)) - C.get_ear_protection())*0.1, 0)) // FROM NOW UNTIL FOREVER
/obj/machinery/jukebox/disco/process()
diff --git a/code/game/objects/effects/decals/cleanable/gibs.dm b/code/game/objects/effects/decals/cleanable/gibs.dm
index 2762416949..f57daac309 100644
--- a/code/game/objects/effects/decals/cleanable/gibs.dm
+++ b/code/game/objects/effects/decals/cleanable/gibs.dm
@@ -3,6 +3,7 @@
desc = "They look bloody and gruesome."
icon_state = "gibbl5"
layer = LOW_OBJ_LAYER
+ blend_mode = BLEND_DEFAULT
random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6")
mergeable_decal = FALSE
bloodiness = 0 //This isn't supposed to be bloody.
diff --git a/code/game/objects/effects/decals/cleanable/humans.dm b/code/game/objects/effects/decals/cleanable/humans.dm
index 8aea7047b6..04942a1253 100644
--- a/code/game/objects/effects/decals/cleanable/humans.dm
+++ b/code/game/objects/effects/decals/cleanable/humans.dm
@@ -3,6 +3,7 @@
desc = "It's gooey. Perhaps it's the chef's cooking?"
icon = 'icons/effects/blood.dmi'
icon_state = "floor1"
+ blend_mode = BLEND_MULTIPLY
random_icon_states = list("floor1", "floor2", "floor3", "floor4", "floor5", "floor6", "floor7")
blood_state = BLOOD_STATE_BLOOD
bloodiness = BLOOD_AMOUNT_PER_DECAL
@@ -33,16 +34,20 @@
. = ..()
if(!fixed_color)
add_atom_colour(blood_DNA_to_color(), FIXED_COLOUR_PRIORITY)
+ blend_mode = blood_DNA_to_blend()
/obj/effect/decal/cleanable/blood/PersistenceSave(list/data)
. = ..()
data["color"] = color
+ data["blendmode"] = blend_mode
/obj/effect/decal/cleanable/blood/PersistenceLoad(list/data)
. = ..()
if(data["color"])
fixed_color = TRUE
add_atom_colour(data["color"], FIXED_COLOUR_PRIORITY)
+ if(data["blendmode"])
+ blend_mode = data["blendmode"]
name = "dried blood"
desc = "Looks like it's been here a while. Eew"
bloodiness = 0
@@ -82,8 +87,8 @@
/obj/effect/decal/cleanable/trail_holder //not a child of blood on purpose
name = "blood"
icon = 'icons/effects/blood.dmi'
- icon_state = "ltrails_1"
desc = "Your instincts say you shouldn't be following these."
+ blend_mode = BLEND_MULTIPLY
random_icon_states = null
beauty = -50
persistent = TRUE
@@ -95,6 +100,7 @@
. = ..()
data["dir"] = dir
data["color"] = color
+ data["blendmode"] = blend_mode
/obj/effect/decal/cleanable/trail_holder/PersistenceLoad(list/data)
. = ..()
@@ -103,11 +109,14 @@
if(data["color"])
fixed_color = TRUE
add_atom_colour(data["color"], FIXED_COLOUR_PRIORITY)
+ if(data["blendmode"])
+ blend_mode = data["blendmode"]
/obj/effect/decal/cleanable/trail_holder/update_icon()
. = ..()
if(!fixed_color)
add_atom_colour(blood_DNA_to_color(), FIXED_COLOUR_PRIORITY)
+ blend_mode = blood_DNA_to_blend()
/obj/effect/cleanable/trail_holder/Initialize(mapload)
. = ..()
diff --git a/code/game/sound.dm b/code/game/sound.dm
index edc3019f27..9f0473faed 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -111,12 +111,15 @@ distance_multiplier - Can be used to multiply the distance at which the sound is
/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff_exponent = SOUND_FALLOFF_EXPONENT, channel = 0, pressure_affected = TRUE, sound/S, max_distance,
falloff_distance = SOUND_DEFAULT_FALLOFF_DISTANCE, distance_multiplier = SOUND_DEFAULT_DISTANCE_MULTIPLIER, envwet = -10000, envdry = 0)
- if(!client || !can_hear())
+ if(!client)
return
if(!S)
S = sound(get_sfx(soundin))
+ if(!can_hear() && !(S.status & SOUND_UPDATE)) //This is primarily to make sure sound updates still go through when a spaceman's deaf
+ return
+
S.wait = 0 //No queue
S.channel = channel || SSsounds.random_available_channel()
S.volume = vol
@@ -173,7 +176,7 @@ distance_multiplier - Can be used to multiply the distance at which the sound is
var/dz = turf_source.y - T.y // Hearing from infront/behind
S.z = dz * distance_multiplier
var/dy = (turf_source.z - T.z) * 5 * distance_multiplier // Hearing from above / below, multiplied by 5 because we assume height is further along coords.
- S.y = dy + 1
+ S.y = dy + distance_multiplier
S.falloff = isnull(max_distance)? FALLOFF_SOUNDS : max_distance //use max_distance, else just use 1 as we are a direct sound so falloff isnt relevant.
diff --git a/code/game/turfs/closed.dm b/code/game/turfs/closed.dm
index e57b0c9806..ef3c6a91d9 100644
--- a/code/game/turfs/closed.dm
+++ b/code/game/turfs/closed.dm
@@ -73,16 +73,26 @@
/turf/closed/indestructible/splashscreen
name = "Space Station 13"
- icon = 'icons/blank_title.png'
+ desc = null
+ icon = 'icons/blanks/blank_title.png'
icon_state = ""
- layer = FLY_LAYER
+ plane = SPLASHSCREEN_PLANE
bullet_bounce_sound = null
-/turf/closed/indestructible/splashscreen/New()
+INITIALIZE_IMMEDIATE(/turf/closed/indestructible/splashscreen)
+
+/turf/closed/indestructible/splashscreen/Initialize(mapload)
+ . = ..()
SStitle.splash_turf = src
if(SStitle.icon)
icon = SStitle.icon
- ..()
+ handle_generic_titlescreen_sizes()
+
+///helper proc that will center the screen if the icon is changed to a generic width, to make admins have to fudge around with pixel_x less. returns null
+/turf/closed/indestructible/splashscreen/proc/handle_generic_titlescreen_sizes()
+ var/icon/size_check = icon(SStitle.icon, icon_state)
+ var/width = size_check.Width()
+ pixel_x = (672 - width) * 0.5 //The title screen is mapped with the expectation that it's 672x480. Should probably turn the title screen size into a define some time!
/turf/closed/indestructible/splashscreen/vv_edit_var(var_name, var_value)
. = ..()
@@ -90,7 +100,13 @@
switch(var_name)
if(NAMEOF(src, icon))
SStitle.icon = icon
+ handle_generic_titlescreen_sizes()
+/turf/closed/indestructible/start_area
+ name = null
+ desc = null
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+
/turf/closed/indestructible/riveted
icon = 'icons/turf/walls/riveted.dmi'
icon_state = "riveted"
diff --git a/code/game/world.dm b/code/game/world.dm
index 092422e822..a9eda444f0 100644
--- a/code/game/world.dm
+++ b/code/game/world.dm
@@ -117,6 +117,7 @@ GLOBAL_LIST(topic_status_cache)
GLOB.world_asset_log = "[GLOB.log_directory]/asset.log"
GLOB.world_attack_log = "[GLOB.log_directory]/attack.log"
GLOB.world_victim_log = "[GLOB.log_directory]/victim.log"
+ GLOB.world_econ_log = "[GLOB.log_directory]/econ.log"
GLOB.world_pda_log = "[GLOB.log_directory]/pda.log"
GLOB.world_telecomms_log = "[GLOB.log_directory]/telecomms.log"
GLOB.world_manifest_log = "[GLOB.log_directory]/manifest.log"
diff --git a/code/modules/arousal/genitals.dm b/code/modules/arousal/genitals.dm
index f4ecafa690..3e16a46526 100644
--- a/code/modules/arousal/genitals.dm
+++ b/code/modules/arousal/genitals.dm
@@ -33,6 +33,9 @@
linked_organ = null
. = ..()
+/obj/item/organ/genital/on_life()
+ return
+
/obj/item/organ/genital/proc/set_aroused_state(new_state,cause = "manual toggle")
if(!(genital_flags & GENITAL_CAN_AROUSE))
return FALSE
diff --git a/code/modules/arousal/organs/butt.dm b/code/modules/arousal/organs/butt.dm
index 172b87f748..6cb32488a5 100644
--- a/code/modules/arousal/organs/butt.dm
+++ b/code/modules/arousal/organs/butt.dm
@@ -15,12 +15,6 @@
var/prev_size //former size value, to allow update_size() to early return should be there no significant changes.
layer_index = BUTT_LAYER_INDEX
-/obj/item/organ/genital/butt/on_life()
- if(QDELETED(src))
- return
- if(!owner)
- return
-
/obj/item/organ/genital/butt/modify_size(modifier, min = -INFINITY, max = BUTT_SIZE_MAX)
var/new_value = clamp(size_cached + modifier, min, max)
if(new_value == size_cached)
diff --git a/code/modules/atmospherics/auxgm/gas_types.dm b/code/modules/atmospherics/auxgm/gas_types.dm
index f037e1bfab..cba5dce870 100644
--- a/code/modules/atmospherics/auxgm/gas_types.dm
+++ b/code/modules/atmospherics/auxgm/gas_types.dm
@@ -69,6 +69,19 @@
fire_products = FIRE_PRODUCT_PLASMA
enthalpy = FIRE_PLASMA_ENERGY_RELEASED // 3000000, 3 megajoules, 3000 kj
+/datum/gas/nitrous_oxide
+ id = GAS_NITROUS
+ specific_heat = 40
+ name = "Nitrous Oxide"
+ gas_overlay = "nitrous_oxide"
+ moles_visible = MOLES_GAS_VISIBLE * 2
+ flags = GAS_FLAG_DANGEROUS
+ fire_products = list(GAS_N2 = 1)
+ oxidation_rate = 0.5
+ oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST + 100
+ enthalpy = 81600
+ heat_resistance = 6
+
/datum/gas/water_vapor
id = GAS_H2O
specific_heat = 40
@@ -82,18 +95,23 @@
powermix = 1
breath_reagent = /datum/reagent/water
-/datum/gas/nitrous_oxide
- id = GAS_NITROUS
- specific_heat = 40
- name = "Nitrous Oxide"
- gas_overlay = "nitrous_oxide"
- moles_visible = MOLES_GAS_VISIBLE * 2
- flags = GAS_FLAG_DANGEROUS
- fire_products = list(GAS_N2 = 1)
- oxidation_rate = 0.5
- oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST + 100
- enthalpy = 81600
- heat_resistance = 6
+
+/datum/gas/pluoxium
+ id = GAS_PLUOXIUM
+ specific_heat = 80
+ name = "Pluoxium"
+ fusion_power = 10
+ oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST * 25 // it is VERY stable
+ oxidation_rate = 8 // when it can oxidize, it can oxidize a LOT
+ enthalpy = -2000000 // but it reduces the heat output a great deal (plasma fires add 3000000 per mole)
+ powermix = -1
+ heat_penalty = -1
+ transmit_modifier = -5
+ heat_resistance = 3
+ price = 6
+
+/datum/gas/pluoxium/generate_TLV()
+ return new/datum/tlv(-1, -1, 5, 6)
/datum/gas/tritium
id = GAS_TRITIUM
@@ -111,6 +129,7 @@
fire_burn_rate = 2
fire_radiation_released = 50 // arbitrary number, basically 60 moles of trit burning will just barely start to harm you
fire_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 50
+ price = 7
/datum/gas/nitric_oxide
id = GAS_NITRIC
@@ -134,6 +153,7 @@
fire_products = list(GAS_N2 = 0.5)
enthalpy = 33200
oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 50
+ price = 3
/datum/gas/hypernoblium
id = GAS_HYPERNOB
@@ -141,6 +161,7 @@
name = "Hyper-noblium"
gas_overlay = "freon"
moles_visible = MOLES_GAS_VISIBLE
+ price = 50
/datum/gas/hydrogen
id = GAS_HYDROGEN
@@ -168,6 +189,7 @@
enthalpy = FIRE_CARBON_ENERGY_RELEASED // it is a mystery
transmit_modifier = -2
radioactivity_modifier = 5
+ price = 3
/datum/gas/stimulum
id = GAS_STIMULUM
@@ -176,22 +198,7 @@
odor_strength = 10
name = "Stimulum"
fusion_power = 7
-
-/datum/gas/pluoxium
- id = GAS_PLUOXIUM
- specific_heat = 80
- name = "Pluoxium"
- fusion_power = 10
- oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST * 25 // it is VERY stable
- oxidation_rate = 8 // when it can oxidize, it can oxidize a LOT
- enthalpy = -2000000 // but it reduces the heat output a great deal (plasma fires add 3000000 per mole)
- powermix = -1
- heat_penalty = -1
- transmit_modifier = -5
- heat_resistance = 3
-
-/datum/gas/pluoxium/generate_TLV()
- return new/datum/tlv(-1, -1, 5, 6)
+ price = 25
/datum/gas/miasma
id = GAS_MIASMA
@@ -202,6 +209,7 @@
name = "Miasma"
gas_overlay = "miasma"
moles_visible = MOLES_GAS_VISIBLE * 60
+ price = 2
/datum/gas/methane
id = GAS_METHANE
@@ -227,6 +235,7 @@
)
enthalpy = -74600
fire_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST
+ price = 1
/datum/gas/methyl_bromide
id = GAS_METHYL_BROMIDE
@@ -249,6 +258,7 @@
enthalpy = -35400
fire_burn_rate = 4 / 7 // oh no
fire_temperature = 808 // its autoignition; it apparently doesn't spark readily, so i don't put it lower
+ price = 2
/datum/gas/bromine
id = GAS_BROMINE
@@ -285,3 +295,4 @@
powermix = -1
transmit_modifier = -10
heat_penalty = -10
+ price = 10
diff --git a/code/modules/atmospherics/gasmixtures/auxgm.dm b/code/modules/atmospherics/gasmixtures/auxgm.dm
index d3de583f04..0c36457ea2 100644
--- a/code/modules/atmospherics/gasmixtures/auxgm.dm
+++ b/code/modules/atmospherics/gasmixtures/auxgm.dm
@@ -43,6 +43,7 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(GAS_O2, GAS_N2, GAS_CO2, GA
var/list/TLVs = list()
var/list/odors = list()
var/list/odor_strengths = list()
+ var/list/prices = list()
/datum/gas
var/id = ""
@@ -60,6 +61,7 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(GAS_O2, GAS_N2, GAS_CO2, GA
var/datum/reagent/breath_reagent = null // what breathing this adds to your reagents
var/datum/reagent/breath_reagent_dangerous = null // what breathing this adds to your reagents IF it's above a danger threshold
var/list/breath_alert_info = null // list for alerts that pop up when you have too much/not enough of something
+ var/price = 0 // How much this gas is worth when sold, per mole.
var/oxidation_temperature = null // temperature above which this gas is an oxidizer; null for none
var/oxidation_rate = 1 // how many moles of this can oxidize how many moles of material
var/fire_temperature = null // temperature above which gas may catch fire; null for none
@@ -135,6 +137,8 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(GAS_O2, GAS_N2, GAS_CO2, GA
if(gas.odor)
odor_strengths[g] = gas.odor_strength
odors[g] = gas.odor
+ if(gas.price)
+ prices[g] = gas.price
add_supermatter_properties(gas)
_auxtools_register_gas(gas)
if(done_initializing)
diff --git a/code/modules/cargo/exports/large_objects.dm b/code/modules/cargo/exports/large_objects.dm
index 6d965e2c5a..e464a056ab 100644
--- a/code/modules/cargo/exports/large_objects.dm
+++ b/code/modules/cargo/exports/large_objects.dm
@@ -169,13 +169,9 @@
/datum/export/large/gas_canister/get_cost(obj/O)
var/obj/machinery/portable_atmospherics/canister/C = O
var/worth = 10
- worth += C.air_contents.get_moles(GAS_BZ)*3
- worth += C.air_contents.get_moles(GAS_STIMULUM)*25
- worth += C.air_contents.get_moles(GAS_HYPERNOB)*20
- worth += C.air_contents.get_moles(GAS_MIASMA)*2
- worth += C.air_contents.get_moles(GAS_TRITIUM)*7
- worth += C.air_contents.get_moles(GAS_PLUOXIUM)*6
- worth += C.air_contents.get_moles(GAS_NITRYL)*10
+ var/list/gas_prices = GLOB.gas_data.prices
+ for(var/gas in C.air_contents.get_gases())
+ worth += C.air_contents.get_moles(gas)*gas_prices[gas]
return worth
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index 41c4480c74..294fdcc30b 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -135,10 +135,6 @@
///Used in MouseDrag to preserve the last mouse-entered object.
var/mouseObject = null
var/mouseControlObject = null
- //Middle-mouse-button click dragtime control for aimbot exploit detection.
- var/middragtime = 0
- //Middle-mouse-button clicked object control for aimbot exploit detection.
- var/atom/middragatom
/// Messages currently seen by this client
var/list/seen_messages
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index 4b93f42d4d..5387e1e5c6 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -867,10 +867,11 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
return
last_activity = world.time
last_click = world.time
- var/ab = FALSE
var/list/L = params2list(params)
- if (object && object == middragatom && L["left"])
- ab = max(0, 5 SECONDS-(world.time-middragtime)*0.1)
+
+ if(L["drag"])
+ return
+
var/mcl = CONFIG_GET(number/minute_click_limit)
if (!holder && !ignore_spam && mcl)
var/minute = round(world.time, 600)
@@ -879,20 +880,14 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if (minute != clicklimiter[CURRENT_MINUTE])
clicklimiter[CURRENT_MINUTE] = minute
clicklimiter[MINUTE_COUNT] = 0
- clicklimiter[MINUTE_COUNT] += 1+(ab)
+ clicklimiter[MINUTE_COUNT] += 1
if (clicklimiter[MINUTE_COUNT] > mcl)
var/msg = "Your previous click was ignored because you've done too many in a minute."
if (minute != clicklimiter[ADMINSWARNED_AT]) //only one admin message per-minute. (if they spam the admins can just boot/ban them)
clicklimiter[ADMINSWARNED_AT] = minute
msg += " Administrators have been informed."
- if (ab)
- log_game("[key_name(src)] is using the middle click aimbot exploit")
- message_admins("[ADMIN_LOOKUPFLW(src)] [ADMIN_KICK(usr)] is using the middle click aimbot exploit")
- add_system_note("aimbot", "Is using the middle click aimbot exploit")
- log_click(object, location, control, params, src, "lockout (spam - minute ab c [ab] s [middragtime])", TRUE)
- else
- log_click(object, location, control, params, src, "lockout (spam - minute)", TRUE)
+ log_click(object, location, control, params, src, "lockout (spam - minute)", TRUE)
log_game("[key_name(src)] Has hit the per-minute click limit of [mcl] clicks in a given game minute")
message_admins("[ADMIN_LOOKUPFLW(src)] [ADMIN_KICK(usr)] Has hit the per-minute click limit of [mcl] clicks in a given game minute")
to_chat(src, "[msg]")
@@ -906,15 +901,11 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if (second != clicklimiter[CURRENT_SECOND])
clicklimiter[CURRENT_SECOND] = second
clicklimiter[SECOND_COUNT] = 0
- clicklimiter[SECOND_COUNT] += 1+(!!ab)
+ clicklimiter[SECOND_COUNT] += 1
if (clicklimiter[SECOND_COUNT] > scl)
to_chat(src, "Your previous click was ignored because you've done too many in a second")
return
- if(ab) //Citadel edit, things with stuff.
- log_click(object, location, control, params, src, "dropped (ab c [ab] s [middragtime])", TRUE)
- return
-
if(prefs.log_clicks)
log_click(object, location, control, params, src, extra_info? "clicked ([extra_info])" : null)
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index c4733bcff9..8600e44faf 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -404,7 +404,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "
"
dat += "Flavor Text" dat += "Set Examine Text" - if(length(features["flavor_text"]) <= 40) + if(length(features["flavor_text"]) <= MAX_FLAVOR_PREVIEW_LEN) if(!length(features["flavor_text"])) dat += "\[...\]" else @@ -413,7 +413,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += "[TextPreview(features["flavor_text"])]... " dat += " Silicon Flavor Text" dat += "Set Silicon Examine Text" - if(length(features["silicon_flavor_text"]) <= 40) + if(length(features["silicon_flavor_text"]) <= MAX_FLAVOR_PREVIEW_LEN) if(!length(features["silicon_flavor_text"])) dat += "\[...\]" else @@ -423,7 +423,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) dat += " OOC notes" dat += "Set OOC notes" var/ooc_notes_len = length(features["ooc_notes"]) - if(ooc_notes_len <= 40) + if(ooc_notes_len <= MAX_FLAVOR_PREVIEW_LEN) if(!ooc_notes_len) dat += "\[...\]" else @@ -1714,7 +1714,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if("name") var/new_name = input(user, "Choose your character's name:", "Character Preference") as text|null if(new_name) - new_name = reject_bad_name(new_name) + new_name = reject_bad_name(new_name, allow_numbers = TRUE) if(new_name) real_name = new_name else diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index a4fadb9b41..a60e1dc63c 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -5,7 +5,7 @@ // You do not need to raise this if you are adding new values that have sane defaults. // Only raise this value when changing the meaning/format/name/layout of an existing value // where you would want the updater procs below to run -#define SAVEFILE_VERSION_MAX 53 +#define SAVEFILE_VERSION_MAX 54 /* SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn @@ -72,15 +72,15 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car var/job_engsec_med = 0 var/job_engsec_low = 0 - 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 + 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 //Can't use SSjob here since this happens right away on login for(var/job in subtypesof(/datum/job)) @@ -173,10 +173,10 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car var/devourable var/feeding var/lickable - S["digestable"] >> digestable - S["devourable"] >> devourable - S["feeding"] >> feeding - S["lickable"] >> lickable + S["digestable"] >> digestable + S["devourable"] >> devourable + S["feeding"] >> feeding + S["lickable"] >> lickable if(digestable) vore_flags |= DIGESTABLE if(devourable) @@ -196,8 +196,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car features["taur"] = "Cow (Spotted)" if(current_version < 31) - S["wing_color"] >> features["wings_color"] - S["horn_color"] >> features["horns_color"] + S["wing_color"] >> features["wings_color"] + S["horn_color"] >> features["horns_color"] if(current_version < 33) features["flavor_text"] = html_encode(features["flavor_text"]) @@ -346,6 +346,16 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car if(current_version < 53) parallax = PARALLAX_INSANE + // Some genius decided to make features update on the loading part, go figure. + if(current_version < 54) + var/old_silicon_flavor = S["silicon_feature_flavor_text"] + if(length(old_silicon_flavor)) + features["feature_silicon_flavor_text"] = old_silicon_flavor + var/old_flavor_text = S["flavor_text"] + // If they have the old flavor text but also have the new one, i suppose the new one is more important. + if(length(old_flavor_text) && !length(features["feature_flavor_text"])) + features["feature_flavor_text"] = old_flavor_text + /datum/preferences/proc/load_path(ckey,filename="preferences.sav") if(!ckey) return @@ -380,70 +390,70 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car . = TRUE //general preferences - S["ooccolor"] >> ooccolor - S["lastchangelog"] >> lastchangelog - S["UI_style"] >> UI_style - S["outline_color"] >> outline_color - S["outline_enabled"] >> outline_enabled - S["screentip_pref"] >> screentip_pref - S["screentip_color"] >> screentip_color - S["hotkeys"] >> hotkeys - S["chat_on_map"] >> chat_on_map - S["max_chat_length"] >> max_chat_length + S["ooccolor"] >> ooccolor + S["lastchangelog"] >> lastchangelog + S["UI_style"] >> UI_style + S["outline_color"] >> outline_color + S["outline_enabled"] >> outline_enabled + S["screentip_pref"] >> screentip_pref + S["screentip_color"] >> screentip_color + S["hotkeys"] >> hotkeys + S["chat_on_map"] >> chat_on_map + S["max_chat_length"] >> max_chat_length S["see_chat_non_mob"] >> see_chat_non_mob - S["tgui_fancy"] >> tgui_fancy - S["tgui_lock"] >> tgui_lock - S["buttons_locked"] >> buttons_locked - S["windowflash"] >> windowflashing + S["tgui_fancy"] >> tgui_fancy + S["tgui_lock"] >> tgui_lock + S["buttons_locked"] >> buttons_locked + S["windowflash"] >> windowflashing S["be_special"] >> be_special - S["default_slot"] >> default_slot - S["chat_toggles"] >> chat_toggles - S["toggles"] >> toggles - S["deadmin"] >> deadmin - 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 + S["default_slot"] >> default_slot + S["chat_toggles"] >> chat_toggles + S["toggles"] >> toggles + S["deadmin"] >> deadmin + 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 S["uses_glasses_colour"]>> uses_glasses_colour - S["clientfps"] >> clientfps - S["parallax"] >> parallax - S["ambientocclusion"] >> ambientocclusion - S["auto_fit_viewport"] >> auto_fit_viewport - S["widescreenpref"] >> widescreenpref - S["long_strip_menu"] >> long_strip_menu + S["clientfps"] >> clientfps + S["parallax"] >> parallax + S["ambientocclusion"] >> ambientocclusion + S["auto_fit_viewport"] >> auto_fit_viewport + S["widescreenpref"] >> widescreenpref + S["long_strip_menu"] >> long_strip_menu S["pixel_size"] >> pixel_size S["scaling_method"] >> scaling_method - S["hud_toggle_flash"] >> hud_toggle_flash - S["hud_toggle_color"] >> hud_toggle_color - S["menuoptions"] >> menuoptions - S["enable_tips"] >> enable_tips - S["tip_delay"] >> tip_delay - S["pda_style"] >> pda_style - S["pda_color"] >> pda_color - S["pda_skin"] >> pda_skin + S["hud_toggle_flash"] >> hud_toggle_flash + S["hud_toggle_color"] >> hud_toggle_color + S["menuoptions"] >> menuoptions + S["enable_tips"] >> enable_tips + S["tip_delay"] >> tip_delay + S["pda_style"] >> pda_style + S["pda_color"] >> pda_color + S["pda_skin"] >> pda_skin // Custom hotkeys - S["key_bindings"] >> key_bindings - S["modless_key_bindings"] >> modless_key_bindings + S["key_bindings"] >> key_bindings + S["modless_key_bindings"] >> modless_key_bindings //citadel code - S["arousable"] >> arousable - S["screenshake"] >> screenshake - S["damagescreenshake"] >> damagescreenshake - S["autostand"] >> autostand - S["cit_toggles"] >> cit_toggles - S["preferred_chaos"] >> preferred_chaos - S["auto_ooc"] >> auto_ooc - S["no_tetris_storage"] >> no_tetris_storage + S["arousable"] >> arousable + S["screenshake"] >> screenshake + S["damagescreenshake"] >> damagescreenshake + S["autostand"] >> autostand + S["cit_toggles"] >> cit_toggles + S["preferred_chaos"] >> preferred_chaos + S["auto_ooc"] >> auto_ooc + S["no_tetris_storage"] >> no_tetris_storage //favorite outfits - S["favorite_outfits"] >> favorite_outfits + S["favorite_outfits"] >> favorite_outfits var/list/parsed_favs = list() for(var/typetext in favorite_outfits) @@ -461,47 +471,47 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car update_preferences(needs_update, S) //needs_update = savefile_version if we need an update (positive integer) //Sanitize - ooccolor = sanitize_ooccolor(sanitize_hexcolor(ooccolor, 6, 1, initial(ooccolor))) - lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog)) - UI_style = sanitize_inlist(UI_style, GLOB.available_ui_styles, GLOB.available_ui_styles[1]) - hotkeys = sanitize_integer(hotkeys, 0, 1, initial(hotkeys)) - chat_on_map = sanitize_integer(chat_on_map, 0, 1, initial(chat_on_map)) + ooccolor = sanitize_ooccolor(sanitize_hexcolor(ooccolor, 6, 1, initial(ooccolor))) + lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog)) + UI_style = sanitize_inlist(UI_style, GLOB.available_ui_styles, GLOB.available_ui_styles[1]) + hotkeys = sanitize_integer(hotkeys, 0, 1, initial(hotkeys)) + chat_on_map = sanitize_integer(chat_on_map, 0, 1, initial(chat_on_map)) max_chat_length = sanitize_integer(max_chat_length, 1, CHAT_MESSAGE_MAX_LENGTH, initial(max_chat_length)) - see_chat_non_mob = sanitize_integer(see_chat_non_mob, 0, 1, initial(see_chat_non_mob)) - tgui_fancy = sanitize_integer(tgui_fancy, 0, 1, initial(tgui_fancy)) - tgui_lock = sanitize_integer(tgui_lock, 0, 1, initial(tgui_lock)) - buttons_locked = sanitize_integer(buttons_locked, 0, 1, initial(buttons_locked)) - windowflashing = sanitize_integer(windowflashing, 0, 1, initial(windowflashing)) - default_slot = sanitize_integer(default_slot, 1, max_save_slots, initial(default_slot)) - toggles = sanitize_integer(toggles, 0, 16777215, initial(toggles)) - deadmin = sanitize_integer(deadmin, 0, 16777215, initial(deadmin)) - clientfps = sanitize_integer(clientfps, 0, 1000, 0) - parallax = sanitize_integer(parallax, PARALLAX_DISABLE, PARALLAX_INSANE, null) - ambientocclusion = sanitize_integer(ambientocclusion, 0, 1, initial(ambientocclusion)) - auto_fit_viewport = sanitize_integer(auto_fit_viewport, 0, 1, initial(auto_fit_viewport)) - widescreenpref = sanitize_integer(widescreenpref, 0, 1, initial(widescreenpref)) - long_strip_menu = sanitize_integer(long_strip_menu, 0, 1, initial(long_strip_menu)) - pixel_size = sanitize_integer(pixel_size, PIXEL_SCALING_AUTO, PIXEL_SCALING_3X, initial(pixel_size)) - scaling_method = sanitize_text(scaling_method, initial(scaling_method)) + see_chat_non_mob = sanitize_integer(see_chat_non_mob, 0, 1, initial(see_chat_non_mob)) + tgui_fancy = sanitize_integer(tgui_fancy, 0, 1, initial(tgui_fancy)) + tgui_lock = sanitize_integer(tgui_lock, 0, 1, initial(tgui_lock)) + buttons_locked = sanitize_integer(buttons_locked, 0, 1, initial(buttons_locked)) + windowflashing = sanitize_integer(windowflashing, 0, 1, initial(windowflashing)) + default_slot = sanitize_integer(default_slot, 1, max_save_slots, initial(default_slot)) + toggles = sanitize_integer(toggles, 0, 16777215, initial(toggles)) + deadmin = sanitize_integer(deadmin, 0, 16777215, initial(deadmin)) + clientfps = sanitize_integer(clientfps, 0, 1000, 0) + parallax = sanitize_integer(parallax, PARALLAX_DISABLE, PARALLAX_INSANE, null) + ambientocclusion = sanitize_integer(ambientocclusion, 0, 1, initial(ambientocclusion)) + auto_fit_viewport = sanitize_integer(auto_fit_viewport, 0, 1, initial(auto_fit_viewport)) + widescreenpref = sanitize_integer(widescreenpref, 0, 1, initial(widescreenpref)) + long_strip_menu = sanitize_integer(long_strip_menu, 0, 1, initial(long_strip_menu)) + pixel_size = sanitize_integer(pixel_size, PIXEL_SCALING_AUTO, PIXEL_SCALING_3X, initial(pixel_size)) + scaling_method = sanitize_text(scaling_method, initial(scaling_method)) hud_toggle_flash = sanitize_integer(hud_toggle_flash, 0, 1, initial(hud_toggle_flash)) hud_toggle_color = sanitize_hexcolor(hud_toggle_color, 6, 1, initial(hud_toggle_color)) - ghost_form = sanitize_inlist(ghost_form, GLOB.ghost_forms, initial(ghost_form)) - ghost_orbit = sanitize_inlist(ghost_orbit, GLOB.ghost_orbits, initial(ghost_orbit)) - ghost_accs = sanitize_inlist(ghost_accs, GLOB.ghost_accs_options, GHOST_ACCS_DEFAULT_OPTION) - ghost_others = sanitize_inlist(ghost_others, GLOB.ghost_others_options, GHOST_OTHERS_DEFAULT_OPTION) - menuoptions = SANITIZE_LIST(menuoptions) - be_special = SANITIZE_LIST(be_special) - pda_style = sanitize_inlist(pda_style, GLOB.pda_styles, initial(pda_style)) - pda_color = sanitize_hexcolor(pda_color, 6, 1, initial(pda_color)) - pda_skin = sanitize_inlist(pda_skin, GLOB.pda_reskins, PDA_SKIN_ALT) - screenshake = sanitize_integer(screenshake, 0, 800, initial(screenshake)) - damagescreenshake = sanitize_integer(damagescreenshake, 0, 2, initial(damagescreenshake)) - autostand = sanitize_integer(autostand, 0, 1, initial(autostand)) - cit_toggles = sanitize_integer(cit_toggles, 0, 16777215, initial(cit_toggles)) - auto_ooc = sanitize_integer(auto_ooc, 0, 1, initial(auto_ooc)) - no_tetris_storage = sanitize_integer(no_tetris_storage, 0, 1, initial(no_tetris_storage)) - key_bindings = sanitize_islist(key_bindings, list()) - modless_key_bindings = sanitize_islist(modless_key_bindings, list()) + ghost_form = sanitize_inlist(ghost_form, GLOB.ghost_forms, initial(ghost_form)) + ghost_orbit = sanitize_inlist(ghost_orbit, GLOB.ghost_orbits, initial(ghost_orbit)) + ghost_accs = sanitize_inlist(ghost_accs, GLOB.ghost_accs_options, GHOST_ACCS_DEFAULT_OPTION) + ghost_others = sanitize_inlist(ghost_others, GLOB.ghost_others_options, GHOST_OTHERS_DEFAULT_OPTION) + menuoptions = SANITIZE_LIST(menuoptions) + be_special = SANITIZE_LIST(be_special) + pda_style = sanitize_inlist(pda_style, GLOB.pda_styles, initial(pda_style)) + pda_color = sanitize_hexcolor(pda_color, 6, 1, initial(pda_color)) + pda_skin = sanitize_inlist(pda_skin, GLOB.pda_reskins, PDA_SKIN_ALT) + screenshake = sanitize_integer(screenshake, 0, 800, initial(screenshake)) + damagescreenshake = sanitize_integer(damagescreenshake, 0, 2, initial(damagescreenshake)) + autostand = sanitize_integer(autostand, 0, 1, initial(autostand)) + cit_toggles = sanitize_integer(cit_toggles, 0, 16777215, initial(cit_toggles)) + auto_ooc = sanitize_integer(auto_ooc, 0, 1, initial(auto_ooc)) + no_tetris_storage = sanitize_integer(no_tetris_storage, 0, 1, initial(no_tetris_storage)) + key_bindings = sanitize_islist(key_bindings, list()) + modless_key_bindings = sanitize_islist(modless_key_bindings, list()) favorite_outfits = SANITIZE_LIST(favorite_outfits) verify_keybindings_valid() // one of these days this will runtime and you'll be glad that i put it in a different proc so no one gets their saves wiped @@ -663,7 +673,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car //Species var/species_id - S["species"] >> species_id + S["species"] >> species_id if(species_id) if(species_id == "avian" || species_id == "aquatic") species_id = "mammal" @@ -678,63 +688,63 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car scars_index = rand(1,5) // WHY //Character - S["real_name"] >> real_name - S["nameless"] >> nameless - S["custom_species"] >> custom_species - S["name_is_always_random"] >> be_random_name - S["body_is_always_random"] >> be_random_body - S["gender"] >> gender - S["body_model"] >> features["body_model"] - S["body_size"] >> features["body_size"] - S["age"] >> age - S["hair_color"] >> hair_color - S["facial_hair_color"] >> facial_hair_color - S["eye_type"] >> eye_type - S["left_eye_color"] >> left_eye_color - S["right_eye_color"] >> right_eye_color - S["use_custom_skin_tone"] >> use_custom_skin_tone - S["skin_tone"] >> skin_tone - S["hair_style_name"] >> hair_style - S["facial_style_name"] >> facial_hair_style - S["grad_style"] >> grad_style - S["grad_color"] >> grad_color - S["underwear"] >> underwear - S["undie_color"] >> undie_color - S["undershirt"] >> undershirt - S["shirt_color"] >> shirt_color - S["socks"] >> socks - S["socks_color"] >> socks_color - S["backbag"] >> backbag - S["jumpsuit_style"] >> jumpsuit_style - S["uplink_loc"] >> uplink_spawn_loc - S["custom_speech_verb"] >> custom_speech_verb - S["custom_tongue"] >> custom_tongue - S["additional_language"] >> additional_language - 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_legs"] >> features["legs"] - S["feature_human_tail"] >> features["tail_human"] - S["feature_human_ears"] >> features["ears"] - S["feature_deco_wings"] >> features["deco_wings"] - S["feature_insect_wings"] >> features["insect_wings"] - S["feature_insect_fluff"] >> features["insect_fluff"] - S["feature_insect_markings"] >> features["insect_markings"] - S["feature_arachnid_legs"] >> features["arachnid_legs"] - S["feature_arachnid_spinneret"] >> features["arachnid_spinneret"] - S["feature_arachnid_mandibles"] >> features["arachnid_mandibles"] - S["feature_horns_color"] >> features["horns_color"] - S["feature_wings_color"] >> features["wings_color"] - S["feature_color_scheme"] >> features["color_scheme"] + S["real_name"] >> real_name + S["nameless"] >> nameless + S["custom_species"] >> custom_species + S["name_is_always_random"] >> be_random_name + S["body_is_always_random"] >> be_random_body + S["gender"] >> gender + S["body_model"] >> features["body_model"] + S["body_size"] >> features["body_size"] + S["age"] >> age + S["hair_color"] >> hair_color + S["facial_hair_color"] >> facial_hair_color + S["eye_type"] >> eye_type + S["left_eye_color"] >> left_eye_color + S["right_eye_color"] >> right_eye_color + S["use_custom_skin_tone"] >> use_custom_skin_tone + S["skin_tone"] >> skin_tone + S["hair_style_name"] >> hair_style + S["facial_style_name"] >> facial_hair_style + S["grad_style"] >> grad_style + S["grad_color"] >> grad_color + S["underwear"] >> underwear + S["undie_color"] >> undie_color + S["undershirt"] >> undershirt + S["shirt_color"] >> shirt_color + S["socks"] >> socks + S["socks_color"] >> socks_color + S["backbag"] >> backbag + S["jumpsuit_style"] >> jumpsuit_style + S["uplink_loc"] >> uplink_spawn_loc + S["custom_speech_verb"] >> custom_speech_verb + S["custom_tongue"] >> custom_tongue + S["additional_language"] >> additional_language + 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_legs"] >> features["legs"] + S["feature_human_tail"] >> features["tail_human"] + S["feature_human_ears"] >> features["ears"] + S["feature_deco_wings"] >> features["deco_wings"] + S["feature_insect_wings"] >> features["insect_wings"] + S["feature_insect_fluff"] >> features["insect_fluff"] + S["feature_insect_markings"] >> features["insect_markings"] + S["feature_arachnid_legs"] >> features["arachnid_legs"] + S["feature_arachnid_spinneret"] >> features["arachnid_spinneret"] + S["feature_arachnid_mandibles"] >> features["arachnid_mandibles"] + S["feature_horns_color"] >> features["horns_color"] + S["feature_wings_color"] >> features["wings_color"] + S["feature_color_scheme"] >> features["color_scheme"] S["persistent_scars"] >> persistent_scars - S["scars1"] >> scars_list["1"] - S["scars2"] >> scars_list["2"] - S["scars3"] >> scars_list["3"] - S["scars4"] >> scars_list["4"] - S["scars5"] >> scars_list["5"] + S["scars1"] >> scars_list["1"] + S["scars2"] >> scars_list["2"] + S["scars3"] >> scars_list["3"] + S["scars4"] >> scars_list["4"] + S["scars5"] >> scars_list["5"] var/limbmodstr S["modified_limbs"] >> limbmodstr if(length(limbmodstr)) @@ -756,100 +766,88 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car else tcg_decks = list() - S["chosen_limb_id"] >> chosen_limb_id - S["hide_ckey"] >> hide_ckey //saved per-character + S["chosen_limb_id"] >> chosen_limb_id + S["hide_ckey"] >> hide_ckey //saved per-character //Custom names for(var/custom_name_id in GLOB.preferences_custom_names) var/savefile_slot_name = custom_name_id + "_name" //TODO remove this S[savefile_slot_name] >> custom_names[custom_name_id] - S["preferred_ai_core_display"] >> preferred_ai_core_display - S["prefered_security_department"] >> prefered_security_department + S["preferred_ai_core_display"] >> preferred_ai_core_display + S["prefered_security_department"] >> prefered_security_department //Jobs - S["joblessrole"] >> joblessrole + S["joblessrole"] >> joblessrole //Load prefs - S["job_preferences"] >> job_preferences + S["job_preferences"] >> job_preferences //Quirks - S["all_quirks"] >> all_quirks + S["all_quirks"] >> all_quirks //Records - S["security_records"] >> security_records - S["medical_records"] >> medical_records + S["security_records"] >> security_records + S["medical_records"] >> medical_records //Citadel code - S["feature_genitals_use_skintone"] >> features["genitals_use_skintone"] - S["feature_mcolor2"] >> features["mcolor2"] - S["feature_mcolor3"] >> features["mcolor3"] + S["feature_genitals_use_skintone"] >> features["genitals_use_skintone"] + S["feature_mcolor2"] >> features["mcolor2"] + S["feature_mcolor3"] >> features["mcolor3"] // note safe json decode will runtime the first time it migrates but this is fine and it solves itself don't worry about it if you see it error features["mam_body_markings"] = safe_json_decode(S["feature_mam_body_markings"]) - S["feature_mam_tail"] >> features["mam_tail"] - S["feature_mam_ears"] >> features["mam_ears"] - S["feature_mam_tail_animated"] >> features["mam_tail_animated"] - S["feature_taur"] >> features["taur"] - S["feature_mam_snouts"] >> features["mam_snouts"] - S["feature_meat"] >> features["meat_type"] + S["feature_mam_tail"] >> features["mam_tail"] + S["feature_mam_ears"] >> features["mam_ears"] + S["feature_mam_tail_animated"] >> features["mam_tail_animated"] + S["feature_taur"] >> features["taur"] + S["feature_mam_snouts"] >> features["mam_snouts"] + S["feature_meat"] >> features["meat_type"] //Xeno features - S["feature_xeno_tail"] >> features["xenotail"] - S["feature_xeno_dors"] >> features["xenodorsal"] - S["feature_xeno_head"] >> features["xenohead"] + S["feature_xeno_tail"] >> features["xenotail"] + S["feature_xeno_dors"] >> features["xenodorsal"] + S["feature_xeno_head"] >> features["xenohead"] //cock features - S["feature_has_cock"] >> features["has_cock"] - S["feature_cock_shape"] >> features["cock_shape"] - S["feature_cock_color"] >> features["cock_color"] - S["feature_cock_length"] >> features["cock_length"] - S["feature_cock_diameter"] >> features["cock_diameter"] - S["feature_cock_taur"] >> features["cock_taur"] - S["feature_cock_visibility"] >> features["cock_visibility"] + S["feature_has_cock"] >> features["has_cock"] + S["feature_cock_shape"] >> features["cock_shape"] + S["feature_cock_color"] >> features["cock_color"] + S["feature_cock_length"] >> features["cock_length"] + S["feature_cock_diameter"] >> features["cock_diameter"] + S["feature_cock_taur"] >> features["cock_taur"] + S["feature_cock_visibility"] >> features["cock_visibility"] //balls features - S["feature_has_balls"] >> features["has_balls"] - S["feature_balls_color"] >> features["balls_color"] - S["feature_balls_shape"] >> features["balls_shape"] - S["feature_balls_size"] >> features["balls_size"] - S["feature_balls_visibility"] >> features["balls_visibility"] + S["feature_has_balls"] >> features["has_balls"] + S["feature_balls_color"] >> features["balls_color"] + S["feature_balls_shape"] >> features["balls_shape"] + S["feature_balls_size"] >> features["balls_size"] + S["feature_balls_visibility"] >> features["balls_visibility"] //breasts features - S["feature_has_breasts"] >> features["has_breasts"] - S["feature_breasts_size"] >> features["breasts_size"] - S["feature_breasts_shape"] >> features["breasts_shape"] - S["feature_breasts_color"] >> features["breasts_color"] - S["feature_breasts_producing"] >> features["breasts_producing"] - S["feature_breasts_visibility"] >> features["breasts_visibility"] + S["feature_has_breasts"] >> features["has_breasts"] + S["feature_breasts_size"] >> features["breasts_size"] + S["feature_breasts_shape"] >> features["breasts_shape"] + S["feature_breasts_color"] >> features["breasts_color"] + S["feature_breasts_producing"] >> features["breasts_producing"] + S["feature_breasts_visibility"] >> features["breasts_visibility"] //vagina features - S["feature_has_vag"] >> features["has_vag"] - S["feature_vag_shape"] >> features["vag_shape"] - S["feature_vag_color"] >> features["vag_color"] - S["feature_vag_visibility"] >> features["vag_visibility"] + S["feature_has_vag"] >> features["has_vag"] + S["feature_vag_shape"] >> features["vag_shape"] + S["feature_vag_color"] >> features["vag_color"] + S["feature_vag_visibility"] >> features["vag_visibility"] //womb features - S["feature_has_womb"] >> features["has_womb"] + S["feature_has_womb"] >> features["has_womb"] //butt features - S["feature_has_butt"] >> features["has_butt"] - S["feature_butt_color"] >> features["butt_color"] - S["feature_butt_size"] >> features["butt_size"] - S["feature_butt_visibility"] >> features["butt_visibility"] + S["feature_has_butt"] >> features["has_butt"] + S["feature_butt_color"] >> features["butt_color"] + S["feature_butt_size"] >> features["butt_size"] + S["feature_butt_visibility"] >> features["butt_visibility"] - //flavor text - //Let's make our players NOT cry desperately as we wipe their savefiles of their special snowflake texts: - if((S["flavor_text"] != "") && (S["flavor_text"] != null) && S["flavor_text"]) //If old text isn't null and isn't "" but still exists. - S["flavor_text"] >> features["flavor_text"] //Load old flavortext as current dna-based flavortext + // Flavor texts, Made into a standard. + S["feature_flavor_text"] >> features["flavor_text"] + S["feature_silicon_flavor_text"] >> features["silicon_flavor_text"] + S["feature_ooc_notes"] >> features["ooc_notes"] - WRITE_FILE(S["feature_flavor_text"], features["flavor_text"]) //Save it in our new type of flavor-text - WRITE_FILE(S["flavor_text"] , "") //Remove old flavortext, completing the cut-and-paste into the new format. - - else //We have no old flavortext, default to new - S["feature_flavor_text"] >> features["flavor_text"] - - - S["silicon_feature_flavor_text"] >> features["silicon_flavor_text"] - - S["feature_ooc_notes"] >> features["ooc_notes"] - S["silicon_flavor_text"] >> features["silicon_flavor_text"] - - S["vore_flags"] >> vore_flags - S["vore_taste"] >> vore_taste - S["vore_smell"] >> vore_smell + S["vore_flags"] >> vore_flags + S["vore_taste"] >> vore_taste + S["vore_smell"] >> vore_smell var/char_vr_path = "[vr_path]/character_[default_slot]_v2.json" if(fexists(char_vr_path)) var/list/json_from_file = json_decode(file2text(char_vr_path)) @@ -869,69 +867,69 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car //Sanitize - real_name = reject_bad_name(real_name) - gender = sanitize_gender(gender, TRUE, TRUE) + real_name = reject_bad_name(real_name) + gender = sanitize_gender(gender, TRUE, TRUE) features["body_model"] = sanitize_gender(features["body_model"], FALSE, FALSE, gender == FEMALE ? FEMALE : MALE) if(!real_name) - real_name = random_unique_name(gender) - custom_species = reject_bad_name(custom_species) + real_name = random_unique_name(gender) + custom_species = reject_bad_name(custom_species) for(var/custom_name_id in GLOB.preferences_custom_names) var/namedata = GLOB.preferences_custom_names[custom_name_id] custom_names[custom_name_id] = reject_bad_name(custom_names[custom_name_id],namedata["allow_numbers"]) if(!custom_names[custom_name_id]) custom_names[custom_name_id] = get_default_name(custom_name_id) - nameless = sanitize_integer(nameless, 0, 1, initial(nameless)) - 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)) + nameless = sanitize_integer(nameless, 0, 1, initial(nameless)) + 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)) - hair_style = sanitize_inlist(hair_style, GLOB.hair_styles_list) - facial_hair_style = sanitize_inlist(facial_hair_style, GLOB.facial_hair_styles_list) - underwear = sanitize_inlist(underwear, GLOB.underwear_list) - undershirt = sanitize_inlist(undershirt, GLOB.undershirt_list) - undie_color = sanitize_hexcolor(undie_color, 6, FALSE, initial(undie_color)) - shirt_color = sanitize_hexcolor(shirt_color, 6, FALSE, initial(shirt_color)) - socks = sanitize_inlist(socks, GLOB.socks_list) - socks_color = sanitize_hexcolor(socks_color, 6, FALSE, initial(socks_color)) - age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age)) - hair_color = sanitize_hexcolor(hair_color, 6, FALSE) - facial_hair_color = sanitize_hexcolor(facial_hair_color, 6, FALSE) - grad_style = sanitize_inlist(grad_style, GLOB.hair_gradients_list, "None") - grad_color = sanitize_hexcolor(grad_color, 6, FALSE) - eye_type = sanitize_inlist(eye_type, GLOB.eye_types, DEFAULT_EYES_TYPE) - left_eye_color = sanitize_hexcolor(left_eye_color, 6, FALSE) - right_eye_color = sanitize_hexcolor(right_eye_color, 6, FALSE) + hair_style = sanitize_inlist(hair_style, GLOB.hair_styles_list) + facial_hair_style = sanitize_inlist(facial_hair_style, GLOB.facial_hair_styles_list) + underwear = sanitize_inlist(underwear, GLOB.underwear_list) + undershirt = sanitize_inlist(undershirt, GLOB.undershirt_list) + undie_color = sanitize_hexcolor(undie_color, 6, FALSE, initial(undie_color)) + shirt_color = sanitize_hexcolor(shirt_color, 6, FALSE, initial(shirt_color)) + socks = sanitize_inlist(socks, GLOB.socks_list) + socks_color = sanitize_hexcolor(socks_color, 6, FALSE, initial(socks_color)) + age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age)) + hair_color = sanitize_hexcolor(hair_color, 6, FALSE) + facial_hair_color = sanitize_hexcolor(facial_hair_color, 6, FALSE) + grad_style = sanitize_inlist(grad_style, GLOB.hair_gradients_list, "None") + grad_color = sanitize_hexcolor(grad_color, 6, FALSE) + eye_type = sanitize_inlist(eye_type, GLOB.eye_types, DEFAULT_EYES_TYPE) + left_eye_color = sanitize_hexcolor(left_eye_color, 6, FALSE) + right_eye_color = sanitize_hexcolor(right_eye_color, 6, FALSE) var/static/allow_custom_skintones if(isnull(allow_custom_skintones)) allow_custom_skintones = CONFIG_GET(flag/allow_custom_skintones) - use_custom_skin_tone = allow_custom_skintones ? sanitize_integer(use_custom_skin_tone, FALSE, TRUE, initial(use_custom_skin_tone)) : FALSE + use_custom_skin_tone = allow_custom_skintones ? sanitize_integer(use_custom_skin_tone, FALSE, TRUE, initial(use_custom_skin_tone)) : FALSE if(use_custom_skin_tone) - skin_tone = sanitize_hexcolor(skin_tone, 6, TRUE, "#FFFFFF") + skin_tone = sanitize_hexcolor(skin_tone, 6, TRUE, "#FFFFFF") else - skin_tone = sanitize_inlist(skin_tone, GLOB.skin_tones - GLOB.nonstandard_skin_tones, initial(skin_tone)) + skin_tone = sanitize_inlist(skin_tone, GLOB.skin_tones - GLOB.nonstandard_skin_tones, initial(skin_tone)) - features["horns_color"] = sanitize_hexcolor(features["horns_color"], 6, FALSE, "85615a") - features["wings_color"] = sanitize_hexcolor(features["wings_color"], 6, FALSE, "FFFFFF") - backbag = sanitize_inlist(backbag, GLOB.backbaglist, initial(backbag)) - jumpsuit_style = sanitize_inlist(jumpsuit_style, GLOB.jumpsuitlist, initial(jumpsuit_style)) - uplink_spawn_loc = sanitize_inlist(uplink_spawn_loc, GLOB.uplink_spawn_loc_list, initial(uplink_spawn_loc)) - features["mcolor"] = sanitize_hexcolor(features["mcolor"], 6, FALSE) - features["tail_lizard"] = sanitize_inlist(features["tail_lizard"], GLOB.tails_list_lizard) - features["tail_human"] = sanitize_inlist(features["tail_human"], GLOB.tails_list_human) - features["snout"] = sanitize_inlist(features["snout"], GLOB.snouts_list) - features["horns"] = sanitize_inlist(features["horns"], GLOB.horns_list) - features["ears"] = sanitize_inlist(features["ears"], GLOB.ears_list) - features["frills"] = sanitize_inlist(features["frills"], GLOB.frills_list) - features["spines"] = sanitize_inlist(features["spines"], GLOB.spines_list) - features["legs"] = sanitize_inlist(features["legs"], GLOB.legs_list, "Plantigrade") - features["deco_wings"] = sanitize_inlist(features["deco_wings"], GLOB.deco_wings_list, "None") - features["insect_fluff"] = sanitize_inlist(features["insect_fluff"], GLOB.insect_fluffs_list) - features["insect_markings"] = sanitize_inlist(features["insect_markings"], GLOB.insect_markings_list, "None") - features["insect_wings"] = sanitize_inlist(features["insect_wings"], GLOB.insect_wings_list) - features["arachnid_legs"] = sanitize_inlist(features["arachnid_legs"], GLOB.arachnid_legs_list, "Plain") - features["arachnid_spinneret"] = sanitize_inlist(features["arachnid_spinneret"], GLOB.arachnid_spinneret_list, "Plain") - features["arachnid_mandibles"] = sanitize_inlist(features["arachnid_mandibles"], GLOB.arachnid_mandibles_list, "Plain") + features["horns_color"] = sanitize_hexcolor(features["horns_color"], 6, FALSE, "85615a") + features["wings_color"] = sanitize_hexcolor(features["wings_color"], 6, FALSE, "FFFFFF") + backbag = sanitize_inlist(backbag, GLOB.backbaglist, initial(backbag)) + jumpsuit_style = sanitize_inlist(jumpsuit_style, GLOB.jumpsuitlist, initial(jumpsuit_style)) + uplink_spawn_loc = sanitize_inlist(uplink_spawn_loc, GLOB.uplink_spawn_loc_list, initial(uplink_spawn_loc)) + features["mcolor"] = sanitize_hexcolor(features["mcolor"], 6, FALSE) + features["tail_lizard"] = sanitize_inlist(features["tail_lizard"], GLOB.tails_list_lizard) + features["tail_human"] = sanitize_inlist(features["tail_human"], GLOB.tails_list_human) + features["snout"] = sanitize_inlist(features["snout"], GLOB.snouts_list) + features["horns"] = sanitize_inlist(features["horns"], GLOB.horns_list) + features["ears"] = sanitize_inlist(features["ears"], GLOB.ears_list) + features["frills"] = sanitize_inlist(features["frills"], GLOB.frills_list) + features["spines"] = sanitize_inlist(features["spines"], GLOB.spines_list) + features["legs"] = sanitize_inlist(features["legs"], GLOB.legs_list, "Plantigrade") + features["deco_wings"] = sanitize_inlist(features["deco_wings"], GLOB.deco_wings_list, "None") + features["insect_fluff"] = sanitize_inlist(features["insect_fluff"], GLOB.insect_fluffs_list) + features["insect_markings"] = sanitize_inlist(features["insect_markings"], GLOB.insect_markings_list, "None") + features["insect_wings"] = sanitize_inlist(features["insect_wings"], GLOB.insect_wings_list) + features["arachnid_legs"] = sanitize_inlist(features["arachnid_legs"], GLOB.arachnid_legs_list, "Plain") + features["arachnid_spinneret"] = sanitize_inlist(features["arachnid_spinneret"], GLOB.arachnid_spinneret_list, "Plain") + features["arachnid_mandibles"] = sanitize_inlist(features["arachnid_mandibles"], GLOB.arachnid_mandibles_list, "Plain") var/static/size_min if(!size_min) @@ -939,7 +937,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car var/static/size_max if(!size_max) size_max = CONFIG_GET(number/body_size_max) - features["body_size"] = sanitize_num_clamp(features["body_size"], size_min, size_max, RESIZE_DEFAULT_SIZE, 0.01) + features["body_size"] = sanitize_num_clamp(features["body_size"], size_min, size_max, RESIZE_DEFAULT_SIZE, 0.01) var/static/list/B_sizes if(!B_sizes) @@ -963,33 +961,33 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car var/list/L = CONFIG_GET(keyed_list/safe_visibility_toggles) safe_visibilities = L.Copy() - features["breasts_size"] = sanitize_inlist(features["breasts_size"], B_sizes, BREASTS_SIZE_DEF) - features["cock_length"] = sanitize_integer(features["cock_length"], min_D, max_D, COCK_SIZE_DEF) - features["butt_size"] = sanitize_integer(features["butt_size"], min_B, max_B, BUTT_SIZE_DEF) - features["breasts_shape"] = sanitize_inlist(features["breasts_shape"], GLOB.breasts_shapes_list, DEF_BREASTS_SHAPE) - features["cock_shape"] = sanitize_inlist(features["cock_shape"], GLOB.cock_shapes_list, DEF_COCK_SHAPE) - features["balls_shape"] = sanitize_inlist(features["balls_shape"], GLOB.balls_shapes_list, DEF_BALLS_SHAPE) - features["vag_shape"] = sanitize_inlist(features["vag_shape"], GLOB.vagina_shapes_list, DEF_VAGINA_SHAPE) - features["breasts_color"] = sanitize_hexcolor(features["breasts_color"], 6, FALSE, "FFFFFF") - features["cock_color"] = sanitize_hexcolor(features["cock_color"], 6, FALSE, "FFFFFF") - features["balls_color"] = sanitize_hexcolor(features["balls_color"], 6, FALSE, "FFFFFF") - features["vag_color"] = sanitize_hexcolor(features["vag_color"], 6, FALSE, "FFFFFF") - features["breasts_visibility"] = sanitize_inlist(features["breasts_visibility"], safe_visibilities, GEN_VISIBLE_NO_UNDIES) - features["cock_visibility"] = sanitize_inlist(features["cock_visibility"], safe_visibilities, GEN_VISIBLE_NO_UNDIES) - features["balls_visibility"] = sanitize_inlist(features["balls_visibility"], safe_visibilities, GEN_VISIBLE_NO_UNDIES) - features["vag_visibility"] = sanitize_inlist(features["vag_visibility"], safe_visibilities, GEN_VISIBLE_NO_UNDIES) - features["butt_visibility"] = sanitize_inlist(features["butt_visibility"], safe_visibilities, GEN_VISIBLE_NO_UNDIES) + features["breasts_size"] = sanitize_inlist(features["breasts_size"], B_sizes, BREASTS_SIZE_DEF) + features["cock_length"] = sanitize_integer(features["cock_length"], min_D, max_D, COCK_SIZE_DEF) + features["butt_size"] = sanitize_integer(features["butt_size"], min_B, max_B, BUTT_SIZE_DEF) + features["breasts_shape"] = sanitize_inlist(features["breasts_shape"], GLOB.breasts_shapes_list, DEF_BREASTS_SHAPE) + features["cock_shape"] = sanitize_inlist(features["cock_shape"], GLOB.cock_shapes_list, DEF_COCK_SHAPE) + features["balls_shape"] = sanitize_inlist(features["balls_shape"], GLOB.balls_shapes_list, DEF_BALLS_SHAPE) + features["vag_shape"] = sanitize_inlist(features["vag_shape"], GLOB.vagina_shapes_list, DEF_VAGINA_SHAPE) + features["breasts_color"] = sanitize_hexcolor(features["breasts_color"], 6, FALSE, "FFFFFF") + features["cock_color"] = sanitize_hexcolor(features["cock_color"], 6, FALSE, "FFFFFF") + features["balls_color"] = sanitize_hexcolor(features["balls_color"], 6, FALSE, "FFFFFF") + features["vag_color"] = sanitize_hexcolor(features["vag_color"], 6, FALSE, "FFFFFF") + features["breasts_visibility"] = sanitize_inlist(features["breasts_visibility"], safe_visibilities, GEN_VISIBLE_NO_UNDIES) + features["cock_visibility"] = sanitize_inlist(features["cock_visibility"], safe_visibilities, GEN_VISIBLE_NO_UNDIES) + features["balls_visibility"] = sanitize_inlist(features["balls_visibility"], safe_visibilities, GEN_VISIBLE_NO_UNDIES) + features["vag_visibility"] = sanitize_inlist(features["vag_visibility"], safe_visibilities, GEN_VISIBLE_NO_UNDIES) + features["butt_visibility"] = sanitize_inlist(features["butt_visibility"], safe_visibilities, GEN_VISIBLE_NO_UNDIES) - custom_speech_verb = sanitize_inlist(custom_speech_verb, GLOB.speech_verbs, "default") - custom_tongue = sanitize_inlist(custom_tongue, GLOB.roundstart_tongues, "default") - additional_language = sanitize_inlist(additional_language, GLOB.roundstart_languages, "None") + custom_speech_verb = sanitize_inlist(custom_speech_verb, GLOB.speech_verbs, "default") + custom_tongue = sanitize_inlist(custom_tongue, GLOB.roundstart_tongues, "default") + additional_language = sanitize_inlist(additional_language, GLOB.roundstart_languages, "None") - security_records = copytext(security_records, 1, MAX_FLAVOR_LEN) - medical_records = copytext(medical_records, 1, MAX_FLAVOR_LEN) + security_records = copytext(security_records, 1, MAX_FLAVOR_LEN) + medical_records = copytext(medical_records, 1, MAX_FLAVOR_LEN) - features["flavor_text"] = copytext(features["flavor_text"], 1, MAX_FLAVOR_LEN) - features["silicon_flavor_text"] = copytext(features["silicon_flavor_text"], 1, MAX_FLAVOR_LEN) - features["ooc_notes"] = copytext(features["ooc_notes"], 1, MAX_FLAVOR_LEN) + features["flavor_text"] = copytext(features["flavor_text"], 1, MAX_FLAVOR_LEN) + features["silicon_flavor_text"] = copytext(features["silicon_flavor_text"], 1, MAX_FLAVOR_LEN) + features["ooc_notes"] = copytext(features["ooc_notes"], 1, MAX_FLAVOR_LEN) //load every advanced coloring mode thing in one go //THIS MUST BE DONE AFTER ALL FEATURE SAVES OR IT WILL NOT WORK @@ -1011,11 +1009,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car message_admins("Sprite Accessory Failure (loading data): Accessory [accessory.type] is a matrixed item without any matrixed sections set!") continue if(S["feature_[primary_string]"]) - S["feature_[primary_string]"] >> features[primary_string] + S["feature_[primary_string]"] >> features[primary_string] if(S["feature_[secondary_string]"]) - S["feature_[secondary_string]"] >> features[secondary_string] + S["feature_[secondary_string]"] >> features[secondary_string] if(S["feature_[tertiary_string]"]) - S["feature_[tertiary_string]"] >> features[tertiary_string] + S["feature_[tertiary_string]"] >> features[tertiary_string] persistent_scars = sanitize_integer(persistent_scars) scars_list["1"] = sanitize_text(scars_list["1"]) @@ -1024,7 +1022,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car scars_list["4"] = sanitize_text(scars_list["4"]) scars_list["5"] = sanitize_text(scars_list["5"]) - joblessrole = sanitize_integer(joblessrole, 1, 3, initial(joblessrole)) + joblessrole = sanitize_integer(joblessrole, 1, 3, initial(joblessrole)) //Validate job prefs for(var/j in job_preferences) if(job_preferences["[j]"] != JP_LOW && job_preferences["[j]"] != JP_MEDIUM && job_preferences["[j]"] != JP_HIGH) @@ -1032,10 +1030,10 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car all_quirks = SANITIZE_LIST(all_quirks) - vore_flags = sanitize_integer(vore_flags, 0, MAX_VORE_FLAG, 0) - vore_taste = copytext(vore_taste, 1, MAX_TASTE_LEN) - vore_smell = copytext(vore_smell, 1, MAX_TASTE_LEN) - belly_prefs = SANITIZE_LIST(belly_prefs) + vore_flags = sanitize_integer(vore_flags, 0, MAX_VORE_FLAG, 0) + vore_taste = copytext(vore_taste, 1, MAX_TASTE_LEN) + vore_smell = copytext(vore_smell, 1, MAX_TASTE_LEN) + belly_prefs = SANITIZE_LIST(belly_prefs) cit_character_pref_load(S) diff --git a/code/modules/clothing/gloves/_gloves.dm b/code/modules/clothing/gloves/_gloves.dm index 4b558d6604..cec4324b3c 100644 --- a/code/modules/clothing/gloves/_gloves.dm +++ b/code/modules/clothing/gloves/_gloves.dm @@ -32,7 +32,7 @@ if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedgloves") if(blood_DNA) - . += mutable_appearance('icons/effects/blood.dmi', "bloodyhands", color = blood_DNA_to_color()) + . += mutable_appearance('icons/effects/blood.dmi', "bloodyhands", color = blood_DNA_to_color(), blend_mode = blood_DNA_to_blend()) /obj/item/clothing/gloves/update_clothes_damaged_state() ..() diff --git a/code/modules/clothing/head/_head.dm b/code/modules/clothing/head/_head.dm index 29a07fd177..884c23e7c0 100644 --- a/code/modules/clothing/head/_head.dm +++ b/code/modules/clothing/head/_head.dm @@ -55,7 +55,7 @@ if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedhelmet") if(blood_DNA) - . += mutable_appearance('icons/effects/blood.dmi', "helmetblood", color = blood_DNA_to_color()) + . += mutable_appearance('icons/effects/blood.dmi', "helmetblood", color = blood_DNA_to_color(), blend_mode = blood_DNA_to_blend()) /obj/item/clothing/head/update_clothes_damaged_state() ..() diff --git a/code/modules/clothing/masks/_masks.dm b/code/modules/clothing/masks/_masks.dm index cca13ce1d8..b30adedb4b 100644 --- a/code/modules/clothing/masks/_masks.dm +++ b/code/modules/clothing/masks/_masks.dm @@ -36,7 +36,7 @@ if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedmask") if(blood_DNA) - . += mutable_appearance('icons/effects/blood.dmi', "maskblood", color = blood_DNA_to_color()) + . += mutable_appearance('icons/effects/blood.dmi', "maskblood", color = blood_DNA_to_color(), blend_mode = blood_DNA_to_blend()) /obj/item/clothing/mask/update_clothes_damaged_state() ..() diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index f498c02cfe..f96fe2ac2a 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -113,6 +113,50 @@ to_chat(user, "Your Joy mask now has a [choice] Emotion!") return 1 +/obj/item/clothing/mask/kitsuneblk + name = "Black Kitsune Mask" + desc = "An oriental styled porcelain mask, this one is black and gold." + icon_state = "blackkitsunemask" + item_state = "blackkitsunemask" + w_class = WEIGHT_CLASS_TINY + flags_cover = MASKCOVERSMOUTH + flags_inv = HIDEFACE|HIDEFACIALHAIR + visor_flags_inv = HIDEFACE|HIDEFACIALHAIR + visor_flags_cover = MASKCOVERSMOUTH + slot_flags = ITEM_SLOT_MASK + +/obj/item/clothing/mask/kitsuneblk/attack_self(mob/user) + adjustmask(user) + +/obj/item/clothing/mask/kitsuneblk/AltClick(mob/user) + . = ..() + if(!user.canUseTopic(src, BE_CLOSE, ismonkey(user))) + return + adjustmask(user) + return TRUE + +/obj/item/clothing/mask/kitsunewhi + name = "White Kitsune Mask" + desc = "An oriental styled porcelain mask, this one is white and red." + icon_state = "whitekitsunemask" + item_state = "whitekitsunemask" + w_class = WEIGHT_CLASS_TINY + flags_cover = MASKCOVERSMOUTH + flags_inv = HIDEFACE|HIDEFACIALHAIR + visor_flags_inv = HIDEFACE|HIDEFACIALHAIR + visor_flags_cover = MASKCOVERSMOUTH + slot_flags = ITEM_SLOT_MASK + +/obj/item/clothing/mask/kitsunewhi/attack_self(mob/user) + adjustmask(user) + +/obj/item/clothing/mask/kitsunewhi/AltClick(mob/user) + . = ..() + if(!user.canUseTopic(src, BE_CLOSE, ismonkey(user))) + return + adjustmask(user) + return TRUE + /obj/item/clothing/mask/pig name = "pig mask" desc = "A rubber pig mask with a builtin voice modulator." diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm index 4f852fb301..7f4de22bb4 100644 --- a/code/modules/clothing/neck/_neck.dm +++ b/code/modules/clothing/neck/_neck.dm @@ -13,7 +13,7 @@ if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damagedmask") if(blood_DNA) - . += mutable_appearance('icons/effects/blood.dmi', "maskblood", color = blood_DNA_to_color()) + . += mutable_appearance('icons/effects/blood.dmi', "maskblood", color = blood_DNA_to_color(), blend_mode = blood_DNA_to_blend()) /obj/item/clothing/neck/tie name = "tie" diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index d8ac875df5..e17396626c 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -19,6 +19,7 @@ var/last_bloodtype = "" //used to track the last bloodtype to have graced these shoes; makes for better performing footprint shenanigans var/last_blood_DNA = "" //same as last one var/last_blood_color = "" + var/last_blood_blend = null ///Whether these shoes have laces that can be tied/untied var/can_be_tied = TRUE @@ -68,6 +69,7 @@ last_bloodtype = blood_dna[blood_dna[blood_dna.len]]//trust me this works last_blood_DNA = blood_dna[blood_dna.len] last_blood_color = blood_dna["color"] + last_blood_blend = blood_dna["blendmode"] /obj/item/clothing/shoes/worn_overlays(isinhands = FALSE, icon_file, used_state, style_flags = NONE) . = ..() @@ -82,7 +84,7 @@ . += mutable_appearance('icons/effects/item_damage.dmi', "damagedshoe") if(bloody) var/file2use = style_flags & STYLE_DIGITIGRADE ? 'icons/mob/clothing/feet_digi.dmi' : 'icons/effects/blood.dmi' - . += mutable_appearance(file2use, "shoeblood", color = blood_DNA_to_color()) + . += mutable_appearance(file2use, "shoeblood", color = blood_DNA_to_color(), blend_mode = blood_DNA_to_blend()) /obj/item/clothing/shoes/equipped(mob/user, slot) . = ..() diff --git a/code/modules/clothing/suits/_suits.dm b/code/modules/clothing/suits/_suits.dm index fa4614429c..f0e9edc450 100644 --- a/code/modules/clothing/suits/_suits.dm +++ b/code/modules/clothing/suits/_suits.dm @@ -20,7 +20,7 @@ . += mutable_appearance('icons/effects/item_damage.dmi', "damaged[blood_overlay_type]") if(blood_DNA) var/file2use = (style_flags & STYLE_ALL_TAURIC) ? 'modular_citadel/icons/mob/64x32_effects.dmi' : 'icons/effects/blood.dmi' - . += mutable_appearance(file2use, "[blood_overlay_type]blood", color = blood_DNA_to_color()) + . += mutable_appearance(file2use, "[blood_overlay_type]blood", color = blood_DNA_to_color(), blend_mode = blood_DNA_to_blend()) var/mob/living/carbon/human/M = loc if(ishuman(M) && M.w_uniform) var/obj/item/clothing/under/U = M.w_uniform diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm index e806ebf906..3f9b6639f1 100644 --- a/code/modules/clothing/under/_under.dm +++ b/code/modules/clothing/under/_under.dm @@ -29,7 +29,7 @@ if(damaged_clothes) . += mutable_appearance('icons/effects/item_damage.dmi', "damageduniform") if(blood_DNA) - . += mutable_appearance('icons/effects/blood.dmi', "uniformblood", color = blood_DNA_to_color()) + . += mutable_appearance('icons/effects/blood.dmi', "uniformblood", color = blood_DNA_to_color(), blend_mode = blood_DNA_to_blend()) if(accessory_overlay) . += accessory_overlay diff --git a/code/modules/food_and_drinks/pizzabox.dm b/code/modules/food_and_drinks/pizzabox.dm index 3c0d60907f..9d0e38cdec 100644 --- a/code/modules/food_and_drinks/pizzabox.dm +++ b/code/modules/food_and_drinks/pizzabox.dm @@ -311,6 +311,7 @@ /obj/item/reagent_containers/food/snacks/pizza/donkpocket = 0.3, /obj/item/reagent_containers/food/snacks/pizza/dank = 0.1) //pizzas here are weighted by chance to be someone's favorite var/static/list/pizza_preferences + COOLDOWN_DECLARE(next_pizza_attunement) /obj/item/pizzabox/infinite/Initialize(mapload) . = ..() @@ -323,11 +324,18 @@ . += "This pizza box is anomalous, and will produce infinite pizza." /obj/item/pizzabox/infinite/attack_self(mob/living/user) - QDEL_NULL(pizza) - if(ishuman(user)) - attune_pizza(user) + if(COOLDOWN_FINISHED(src, next_pizza_attunement)) + QDEL_NULL(pizza) + if(ishuman(user)) + attune_pizza(user) . = ..() +/obj/item/pizzabox/infinite/on_attack_hand(mob/user, act_intent, unarmed_attack_flags) + var/had_pizza = (pizza ? TRUE : FALSE) + . = ..() + if(had_pizza && !pizza) + COOLDOWN_START(src, next_pizza_attunement, (3 SECONDS)) + /obj/item/pizzabox/infinite/proc/attune_pizza(mob/living/carbon/human/noms) //tonight on "proc names I never thought I'd type" if(!pizza_preferences[noms.ckey]) pizza_preferences[noms.ckey] = pickweight(pizza_types) diff --git a/code/modules/mob/dead/new_player/login.dm b/code/modules/mob/dead/new_player/login.dm index 887556500c..dbcc2cb2b4 100644 --- a/code/modules/mob/dead/new_player/login.dm +++ b/code/modules/mob/dead/new_player/login.dm @@ -9,6 +9,12 @@ ..() + if(!age_verify()) + return + + if(!client) //client can end up null as a result of age_verify() kicking those who fail + return + var/motd = global.config.motd if(motd) to_chat(src, " [motd] ", handle_whitespace=FALSE)
@@ -22,8 +28,11 @@
sight |= SEE_TURFS
- new_player_panel()
client.playtitlemusic()
+
+ var/datum/asset/asset_datum = get_asset_datum(/datum/asset/simple/lobby)
+ asset_datum.send(client)
+
if(SSticker.current_state < GAME_STATE_SETTING_UP)
var/tl = SSticker.GetTimeLeft()
var/postfix
@@ -32,3 +41,7 @@
else
postfix = "soon"
to_chat(src, "Please set up your character and select \"Ready\". The game will start [postfix].")
+
+ var/datum/hud/new_player/NH = hud_used
+ if(istype(NH))
+ NH.populate_buttons(src)
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index ba082f4870..cfcb881977 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -1,5 +1,3 @@
-#define LINKIFY_READY(string, value) "[string]"
-
/mob/dead/new_player
var/ready = 0
var/spawning = 0//Referenced when you want to delete the new_player later on in the code.
@@ -10,6 +8,8 @@
density = FALSE
stat = DEAD
+ hud_type = /datum/hud/new_player
+ hud_possible = list()
var/mob/living/new_character //for instant transfer once the round is set up
@@ -43,69 +43,6 @@
/mob/dead/new_player/prepare_huds()
return
-/mob/dead/new_player/proc/new_player_panel()
- var/output = "Welcome, [client ? client.prefs.real_name : "Unknown User"] " - output += "\[ [LINKIFY_READY("Ready", PLAYER_READY_TO_PLAY)] | Not Ready | [LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)] \] " - if(PLAYER_READY_TO_PLAY) - output += "\[ Ready | [LINKIFY_READY("Not Ready", PLAYER_NOT_READY)] | [LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)] \] " - if(PLAYER_READY_TO_OBSERVE) - output += "\[ [LINKIFY_READY("Ready", PLAYER_READY_TO_PLAY)] | [LINKIFY_READY("Not Ready", PLAYER_NOT_READY)] | Observe \] " - else - output += "" - output += "" - output += "[LINKIFY_READY("Observe", PLAYER_READY_TO_OBSERVE)] " - - if(!IsGuestKey(src.key)) - output += playerpolls() - - output += "New Player Options ", 250, 265)
- popup.set_window_options("can_close=0")
- popup.set_content(output)
- popup.open(FALSE)
-
-/mob/dead/new_player/proc/playerpolls()
- var/output = "" //hey tg why is this a list?
- if (SSdbcore.Connect())
- var/isadmin = FALSE
- if(client?.holder)
- isadmin = TRUE
- var/datum/db_query/query_get_new_polls = SSdbcore.NewQuery({"
- SELECT id FROM [format_table_name("poll_question")]
- WHERE (adminonly = 0 OR :isadmin = 1)
- AND Now() BETWEEN starttime AND endtime
- AND deleted = 0
- AND id NOT IN (
- SELECT pollid FROM [format_table_name("poll_vote")]
- WHERE ckey = :ckey
- AND deleted = 0
- )
- AND id NOT IN (
- SELECT pollid FROM [format_table_name("poll_textreply")]
- WHERE ckey = :ckey
- AND deleted = 0
- )
- "}, list("isadmin" = isadmin, "ckey" = ckey))
- var/rs = REF(src)
- if(!query_get_new_polls.Execute())
- qdel(query_get_new_polls)
- return
- if(query_get_new_polls.NextRow())
- output += "Show Player Polls (NEW!) " - else - output += "" - qdel(query_get_new_polls) - if(QDELETED(src)) - return - return output - /mob/dead/new_player/proc/age_gate() var/list/dat = list("" @@ -152,6 +89,9 @@ if(!(client.prefs.db_flags & DB_FLAG_AGE_CONFIRMATION_INCOMPLETE)) //completed? Skip return TRUE + if(!client) + return FALSE + var/age_verification = age_gate() //ban them and kick them if(age_verification != 1) @@ -269,61 +209,12 @@ if(!age_verify()) return - //Determines Relevent Population Cap - var/relevant_cap - var/hpc = CONFIG_GET(number/hard_popcap) - var/epc = CONFIG_GET(number/extreme_popcap) - if(hpc && epc) - relevant_cap = min(hpc, epc) - else - relevant_cap = max(hpc, epc) - - if(href_list["show_preferences"]) - client.prefs.ShowChoices(src) - return 1 - - if(href_list["ready"]) - var/tready = text2num(href_list["ready"]) - //Avoid updating ready if we're after PREGAME (they should use latejoin instead) - //This is likely not an actual issue but I don't have time to prove that this - //no longer is required - if(SSticker.current_state <= GAME_STATE_PREGAME) - ready = tready - //if it's post initialisation and they're trying to observe we do the needful - if(!SSticker.current_state < GAME_STATE_PREGAME && tready == PLAYER_READY_TO_OBSERVE) - ready = tready - make_me_an_observer() - return - - if(href_list["refresh"]) - src << browse(null, "window=playersetup") //closes the player setup window - new_player_panel() - if(href_list["late_join"]) if(!SSticker || !SSticker.IsRoundInProgress()) to_chat(usr, "The round is either not ready, or has already finished...") return - - if(href_list["late_join"] == "override") - LateChoices() - return - - if(SSticker.queued_players.len || (relevant_cap && living_player_count() >= relevant_cap && !(ckey(key) in GLOB.admin_datums))) - to_chat(usr, "[CONFIG_GET(string/hard_popcap_message)]") - - var/queue_position = SSticker.queued_players.Find(usr) - if(queue_position == 1) - to_chat(usr, "You are next in line to join the game. You will be notified when a slot opens up.") - else if(queue_position) - to_chat(usr, "There are [queue_position-1] players in front of you in the queue to join the game.") - else - SSticker.queued_players += usr - to_chat(usr, "You have been added to the queue to join the game. Your position in queue is [SSticker.queued_players.len].") - return LateChoices() - - if(href_list["manifest"]) - ViewManifest() + return if(href_list["SelectedJob"]) if(!SSticker || !SSticker.IsRoundInProgress()) @@ -337,6 +228,17 @@ to_chat(usr, "There is an administrative lock on entering the game!") return + //Determines Relevent Population Cap + var/relevant_cap + var/hpc = CONFIG_GET(number/hard_popcap) + var/epc = CONFIG_GET(number/extreme_popcap) + if(hpc && epc) + relevant_cap = min(hpc, epc) + else + relevant_cap = max(hpc, epc) + + + if(SSticker.queued_players.len && !(ckey(key) in GLOB.admin_datums)) if((living_player_count() >= relevant_cap) || (src != SSticker.queued_players[1])) to_chat(usr, "Server is full.") @@ -349,6 +251,15 @@ if(!GLOB.enter_allowed) to_chat(usr, " There is an administrative lock on entering the game!") + //Determines Relevent Population Cap + var/relevant_cap + var/hpc = CONFIG_GET(number/hard_popcap) + var/epc = CONFIG_GET(number/extreme_popcap) + if(hpc && epc) + relevant_cap = min(hpc, epc) + else + relevant_cap = max(hpc, epc) + if(SSticker.queued_players.len && !(ckey(key) in GLOB.admin_datums)) if((living_player_count() >= relevant_cap) || (src != SSticker.queued_players[1])) to_chat(usr, "Server is full.") @@ -360,13 +271,6 @@ SSticker.queue_delay = 4 qdel(src) - else if(!href_list["late_join"]) - new_player_panel() - - if(href_list["showpoll"]) - handle_player_polling() - return - if(href_list["pollid"]) var/pollid = href_list["pollid"] if(istext(pollid)) @@ -460,7 +364,6 @@ if(QDELETED(src) || !src.client || this_is_like_playing_right != "Yes") ready = PLAYER_NOT_READY src << browse(null, "window=playersetup") //closes the player setup window - new_player_panel() return FALSE var/mob/dead/observer/observer = new() diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index eabf2e2d0e..2d3ceb728c 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -193,6 +193,7 @@ blood_data["blood_DNA"] = dna.unique_enzymes blood_data["bloodcolor"] = dna.species.exotic_blood_color + blood_data["bloodblend"] = dna.species.exotic_blood_blend_mode if(disease_resistances && disease_resistances.len) blood_data["resistances"] = disease_resistances.Copy() var/list/temp_chem = list() @@ -264,7 +265,7 @@ "O-" = list("O-","SY"), "O+" = list("O-", "O+","SY"), "L" = list("L","SY"), - "U" = list("A-", "A+", "B-", "B+", "O-", "O+", "AB-", "AB+", "L", "U","SY"), + "U" = list("A-", "A+", "B-", "B+", "O-", "O+", "AB-", "AB+", "L", "U","SY", "BUG"), "HF" = list("HF", "SY"), "X*" = list("X*", "SY"), "SY" = list("SY"), diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 54d6771c6c..141fbb89d7 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -85,6 +85,7 @@ FP.blood_DNA["color"] = S.last_blood_color else FP.blood_DNA["color"] = BlendRGB(FP.blood_DNA["color"], S.last_blood_color) + FP.blood_DNA["blendmode"] = S.last_blood_blend FP.update_icon() update_inv_shoes() //End bloody footprints diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm index 4eccbc62c7..3a5d35a115 100644 --- a/code/modules/mob/living/carbon/human/species.dm +++ b/code/modules/mob/living/carbon/human/species.dm @@ -65,6 +65,8 @@ GLOBAL_LIST_EMPTY(roundstart_race_names) var/exotic_bloodtype = "" /// Assume human as the default blood colour, override this default by species subtypes var/exotic_blood_color = BLOOD_COLOR_HUMAN + /// Which blend mode should this species blood use? + var/exotic_blood_blend_mode = BLEND_MULTIPLY ///What the species drops when gibbed by a gibber machine. var/meat = /obj/item/reagent_containers/food/snacks/meat/slab/human //What the species drops on gibbing var/list/gib_types = list(/obj/effect/gibspawner/human, /obj/effect/gibspawner/human/bodypartless) diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index 3b84752e1e..13c9aaa8f1 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -17,6 +17,7 @@ exotic_blood = /datum/reagent/blood/jellyblood exotic_bloodtype = "GEL" exotic_blood_color = "BLOOD_COLOR_SLIME" + exotic_blood_blend_mode = BLEND_DEFAULT damage_overlay_type = "" liked_food = TOXIC | MEAT disliked_food = null @@ -31,6 +32,7 @@ species_category = SPECIES_CATEGORY_JELLY wings_icons = SPECIES_WINGS_JELLY ass_image = 'icons/ass/assslime.png' + blacklisted_quirks = list(/datum/quirk/glass_bones) /datum/species/jelly/on_species_loss(mob/living/carbon/C) C.faction -= "slime" diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm index 9c29856084..5fb816867f 100644 --- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm +++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm @@ -26,6 +26,7 @@ wings_icons = SPECIES_WINGS_SKELETAL ass_image = 'icons/ass/assplasma.png' + blacklisted_quirks = list(/datum/quirk/paper_skin) /datum/species/plasmaman/spec_life(mob/living/carbon/human/H) var/datum/gas_mixture/environment = H.loc.return_air() diff --git a/code/modules/mob/living/carbon/human/species_types/skeletons.dm b/code/modules/mob/living/carbon/human/species_types/skeletons.dm index 5a927397fb..9c52a6a9d1 100644 --- a/code/modules/mob/living/carbon/human/species_types/skeletons.dm +++ b/code/modules/mob/living/carbon/human/species_types/skeletons.dm @@ -17,6 +17,7 @@ species_category = SPECIES_CATEGORY_SKELETON //they have their own category that's disassociated from undead, paired with plasmapeople wings_icons = SPECIES_WINGS_SKELETAL + blacklisted_quirks = list(/datum/quirk/paper_skin) /datum/species/skeleton/New() if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) //skeletons are stronger during the spooky season! diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index cb2458e376..a0620c8990 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -209,7 +209,7 @@ There are several things that need to be remembered: inv.update_icon() if(!gloves && bloody_hands) - var/mutable_appearance/bloody_overlay = mutable_appearance('icons/effects/blood.dmi', "bloodyhands", -GLOVES_LAYER, color = blood_DNA_to_color()) + var/mutable_appearance/bloody_overlay = mutable_appearance('icons/effects/blood.dmi', "bloodyhands", -GLOVES_LAYER, color = blood_DNA_to_color(), blend_mode = blood_DNA_to_blend()) if(get_num_arms(FALSE) < 2) if(has_left_hand(FALSE)) bloody_overlay.icon_state = "bloodyhands_left" diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm index fe479bb943..1f12195ad6 100644 --- a/code/modules/mob/living/simple_animal/parrot.dm +++ b/code/modules/mob/living/simple_animal/parrot.dm @@ -925,8 +925,8 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list( /mob/living/simple_animal/parrot/Poly/say(message, bubble_type,var/list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) . = ..() - if(. && !client && prob(1) && prob(1)) //Only the one true bird may speak across dimensions. - world.TgsTargetedChatBroadcast("A stray squawk is heard... \"[message]\"", FALSE) + if(. && !client && prob(1) && prob(1) && CONFIG_GET(string/chat_squawk_tag)) //Only the one true bird may speak across dimensions. + send2chat("A stray squawk is heard... \"[message]\"", CONFIG_GET(string/chat_squawk_tag)) /mob/living/simple_animal/parrot/Poly/BiologicalLife(seconds, times_fired) if(!(. = ..())) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index abe3aef82d..9e49e8556b 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -1,5 +1,5 @@ /datum/reagent/blood - data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_HUMAN, "blood_type"= null,"resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null,"quirks"=null) + data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_HUMAN, "bloodblend" = BLEND_MULTIPLY, "blood_type"= null,"resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null,"quirks"=null) name = "Blood" chemical_flags = REAGENT_ALL_PROCESS value = REAGENT_VALUE_UNCOMMON // $$$ blood ""donations"" $$$ @@ -81,6 +81,7 @@ B.blood_DNA["color"] = data["bloodcolor"] else B.blood_DNA["color"] = BlendRGB(B.blood_DNA["color"], data["bloodcolor"]) + B.blood_DNA["blendmode"] = data["bloodblend"] if(B.reagents) B.reagents.add_reagent(type, reac_volume) B.update_icon() @@ -148,7 +149,7 @@ . += D /datum/reagent/blood/synthetics - data = list("donor"=null,"viruses"=null,"blood_DNA"="REPLICATED", "bloodcolor" = BLOOD_COLOR_SYNTHETIC, "blood_type"="SY","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null) + data = list("donor"=null,"viruses"=null,"blood_DNA"="REPLICATED", "bloodcolor" = BLOOD_COLOR_SYNTHETIC, "bloodblend" = BLEND_MULTIPLY, "blood_type"="SY","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null) name = "Synthetic Blood" description = "A synthetically produced imitation of blood." taste_description = "oil" @@ -156,7 +157,7 @@ value = REAGENT_VALUE_NONE /datum/reagent/blood/jellyblood - data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_SLIME, "blood_type"="GEL","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null) + data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_SLIME, "bloodblend" = BLEND_DEFAULT, "blood_type"="GEL","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null) name = "Slime Jelly Blood" description = "A gooey semi-liquid produced from one of the deadliest lifeforms in existence. SO REAL." color = BLOOD_COLOR_SLIME @@ -165,7 +166,7 @@ pH = 4 /datum/reagent/blood/tomato - data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_HUMAN, "blood_type"="SY","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null) + data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_HUMAN, "bloodblend" = BLEND_MULTIPLY, "blood_type"="SY","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null) name = "Tomato Blood" description = "This highly resembles blood, but it doesnt actually function like it, resembling more ketchup, with a more blood-like consistency." taste_description = "sap" //Like tree sap? @@ -189,7 +190,7 @@ description = "You don't even want to think about what's in here." taste_description = "gross iron" shot_glass_icon_state = "shotglassred" - data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_HUMAN, "blood_type"= "O+","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null) + data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_HUMAN, "bloodblend" = BLEND_MULTIPLY, "blood_type"= "O+","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null) pH = 7.45 /datum/reagent/liquidgibs/xeno @@ -197,7 +198,7 @@ color = BLOOD_COLOR_XENO taste_description = "blended heresy" shot_glass_icon_state = "shotglassgreen" - data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_XENO, "blood_type"="X*","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null) + data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_XENO, "bloodblend" = BLEND_MULTIPLY, "blood_type"="X*","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null) pH = 2.5 /datum/reagent/liquidgibs/slime @@ -205,20 +206,20 @@ color = BLOOD_COLOR_SLIME taste_description = "slime" shot_glass_icon_state = "shotglassgreen" - data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_SLIME, "blood_type"="GEL","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null) + data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_SLIME, "bloodblend" = BLEND_DEFAULT, "blood_type"="GEL","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null) pH = 4 /datum/reagent/liquidgibs/synth name = "Synthetic sludge" color = BLOOD_COLOR_SYNTHETIC taste_description = "jellied plastic" - data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_SYNTHETIC, "blood_type"="SY","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null) + data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_SYNTHETIC, "bloodblend" = BLEND_MULTIPLY, "blood_type"="SY","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null) /datum/reagent/liquidgibs/oil name = "Hydraulic sludge" color = BLOOD_COLOR_OIL taste_description = "chunky burnt oil" - data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_OIL, "blood_type"="HF","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null) + data = list("donor"=null,"viruses"=null,"blood_DNA"=null, "bloodcolor" = BLOOD_COLOR_OIL, "bloodblend" = BLEND_MULTIPLY, "blood_type"="HF","resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null,"cloneable"=null,"factions"=null) pH = 9.75 /datum/reagent/vaccine diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm index c6df9ebb0c..87349058a4 100644 --- a/code/modules/reagents/reagent_containers/blood_pack.dm +++ b/code/modules/reagents/reagent_containers/blood_pack.dm @@ -13,7 +13,7 @@ /obj/item/reagent_containers/blood/Initialize(mapload) . = ..() if(blood_type != null) - reagents.add_reagent(/datum/reagent/blood, 200, list("donor"=null,"viruses"=null,"blood_DNA"=null,"bloodcolor"=bloodtype_to_color(blood_type), "blood_type"=blood_type,"resistances"=null,"trace_chem"=null)) + reagents.add_reagent(/datum/reagent/blood, 200, list("donor"=null,"viruses"=null,"blood_DNA"=null,"bloodcolor"=bloodtype_to_color(blood_type), "bloodblend" = BLEND_MULTIPLY, "blood_type"=blood_type,"resistances"=null,"trace_chem"=null)) update_icon() /obj/item/reagent_containers/blood/on_reagent_change(changetype) diff --git a/code/modules/research/bepis.dm b/code/modules/research/bepis.dm index 7b36a614a7..93df1e2351 100644 --- a/code/modules/research/bepis.dm +++ b/code/modules/research/bepis.dm @@ -103,7 +103,7 @@ return account.adjust_money(-deposit_value) //The money vanishes, not paid to any accounts. SSblackbox.record_feedback("amount", "BEPIS_credits_spent", deposit_value) - //log_econ("[deposit_value] credits were inserted into [src] by [account.account_holder]") + log_econ("[deposit_value] credits were inserted into [src] by [key_name(usr)] (account: [account.account_holder])") banked_cash += deposit_value use_power(1000 * power_saver) say("Cash deposit successful. There is [banked_cash] in the chamber.") diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index a89727d183..2d8150d087 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -228,6 +228,8 @@ var/mangled_state = get_mangled_state() var/bio_state = owner.get_biological_state() var/easy_dismember = HAS_TRAIT(owner, TRAIT_EASYDISMEMBER) // if we have easydismember, we don't reduce damage when redirecting damage to different types (slashing weapons on mangled/skinless limbs attack at 100% instead of 50%) + var/glass_bones = HAS_TRAIT(owner, TRAIT_GLASS_BONES) + var/paper_skin = HAS_TRAIT(owner, TRAIT_PAPER_SKIN) if(wounding_type == WOUND_BLUNT) if(sharpness == SHARP_EDGED) @@ -242,22 +244,29 @@ if(wounding_type == WOUND_SLASH) wounding_type = WOUND_BLUNT wounding_dmg *= (easy_dismember ? 1 : 0.5) + wounding_dmg *= (glass_bones ? 1.5 : 1) else if(wounding_type == WOUND_PIERCE) wounding_type = WOUND_BLUNT wounding_dmg *= (easy_dismember ? 1 : 0.75) + wounding_dmg *= (glass_bones ? 1.5 : 1) if((mangled_state & BODYPART_MANGLED_BONE) && try_dismember(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus)) return // if we're flesh only, all blunt attacks become weakened slashes in terms of wound damage if(BIO_JUST_FLESH) if(wounding_type == WOUND_BLUNT) wounding_type = WOUND_SLASH - wounding_dmg *= (easy_dismember ? 1 : 0.3) + wounding_dmg *= (easy_dismember ? 1 : 0.5) + wounding_dmg *= (paper_skin ? 1.5 : 1) if((mangled_state & BODYPART_MANGLED_FLESH) && try_dismember(wounding_type, wounding_dmg, wound_bonus, bare_wound_bonus)) return // standard humanoids if(BIO_FLESH_BONE) // if we've already mangled the skin (critical slash or piercing wound), then the bone is exposed, and we can damage it with sharp weapons at a reduced rate // So a big sharp weapon is still all you need to destroy a limb + if(wounding_type == WOUND_SLASH || wounding_type == WOUND_PIERCE) + wounding_dmg *= (paper_skin ? 1.5 : 1) + else + wounding_dmg *= (glass_bones ? 1.5 : 1) if(mangled_state == BODYPART_MANGLED_FLESH && sharpness) playsound(src, "sound/effects/wounds/crackandbleed.ogg", 100) if(wounding_type == WOUND_SLASH && !easy_dismember) diff --git a/code/modules/surgery/organs/ears.dm b/code/modules/surgery/organs/ears.dm index 80b7599ec3..d572deca73 100644 --- a/code/modules/surgery/organs/ears.dm +++ b/code/modules/surgery/organs/ears.dm @@ -18,10 +18,9 @@ // to hear anything. var/deaf = 0 - // `ear_damage` measures long term damage to the ears, if too high, + // `damage` in this case measures long term damage to the ears, if too high, // the person will not have either `deaf` or `ear_damage` decrease // without external aid (earmuffs, drugs) - var/ear_damage = 0 //Resistance against loud noises var/bang_protect = 0 @@ -44,16 +43,22 @@ /obj/item/organ/ears/proc/restoreEars() deaf = 0 - ear_damage = 0 + damage = 0 + prev_damage = 0 organ_flags &= ~ORGAN_FAILING var/mob/living/carbon/C = owner if(iscarbon(owner) && HAS_TRAIT(C, TRAIT_DEAF)) deaf = 1 + var/mess = check_damage_thresholds() + if(mess && owner) + to_chat(owner, mess) /obj/item/organ/ears/proc/adjustEarDamage(ddmg, ddeaf) - ear_damage = max(ear_damage + (ddmg*damage_multiplier), 0) + if(owner.status_flags & GODMODE) + return + setOrganDamage(max(damage + (ddmg*damage_multiplier), 0)) deaf = max(deaf + (ddeaf*damage_multiplier), 0) /obj/item/organ/ears/proc/minimumDeafTicks(value) diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm index cb2c02fa7c..058de79d04 100644 --- a/code/modules/surgery/organs/lungs.dm +++ b/code/modules/surgery/organs/lungs.dm @@ -300,7 +300,7 @@ to_chat(H, "Your throat closes up!") H.silent = max(H.silent, 3) else - H.adjustFireLoss(nitryl_pp/4) + H.adjustFireLoss(nitryl_pp/2) gas_breathed = breath.get_moles(GAS_NITRYL) if (gas_breathed > gas_stimulation_min) H.reagents.add_reagent(/datum/reagent/nitryl,1) diff --git a/code/modules/surgery/organs/tails.dm b/code/modules/surgery/organs/tails.dm index 289e9df9d4..8478ae5d27 100644 --- a/code/modules/surgery/organs/tails.dm +++ b/code/modules/surgery/organs/tails.dm @@ -13,6 +13,9 @@ owner.dna.species.stop_wagging_tail(owner) return ..() +/obj/item/organ/tail/on_life() + return + /obj/item/organ/tail/cat name = "cat tail" desc = "A severed cat tail. Who's wagging now?" diff --git a/code/modules/unit_tests/character_saving.dm b/code/modules/unit_tests/character_saving.dm index 8d978a630d..cca17b81e4 100644 --- a/code/modules/unit_tests/character_saving.dm +++ b/code/modules/unit_tests/character_saving.dm @@ -1,18 +1,29 @@ +#define UNIT_TEST_SAVING_FLAVOR_TEXT "Space" +#define UNIT_TEST_SAVING_SILICON_FLAVOR_TEXT "Station" +#define UNIT_TEST_SAVING_OOC_NOTES "Thirteen" + /datum/unit_test/character_saving/Run() try var/datum/preferences/P = new P.load_path("test") - P.features["flavor_text"] = "Foo" - P.features["ooc_notes"] = "Bar" + P.features["flavor_text"] = UNIT_TEST_SAVING_FLAVOR_TEXT + P.features["silicon_flavor_text"] = UNIT_TEST_SAVING_SILICON_FLAVOR_TEXT + P.features["ooc_notes"] = UNIT_TEST_SAVING_OOC_NOTES P.save_character() P.load_character() - if(P.features["flavor_text"] != "Foo") + if(P.features["flavor_text"] != UNIT_TEST_SAVING_FLAVOR_TEXT) Fail("Flavor text is failing to save.") - if(P.features["ooc_notes"] != "Bar") + if(P.features["silicon_flavor_text"] != UNIT_TEST_SAVING_SILICON_FLAVOR_TEXT) + Fail("Silicon flavor text is failing to save.") + if(P.features["ooc_notes"] != UNIT_TEST_SAVING_OOC_NOTES) Fail("OOC text is failing to save.") P.save_character() P.load_character() - if(P.features["flavor_text"] != "Foo") + if((P.features["flavor_text"] != UNIT_TEST_SAVING_FLAVOR_TEXT) || (P.features["silicon_flavor_text"] != UNIT_TEST_SAVING_SILICON_FLAVOR_TEXT) || (P.features["ooc_notes"] != UNIT_TEST_SAVING_OOC_NOTES)) Fail("Repeated saving and loading possibly causing save deletion.") catch(var/exception/e) Fail("Failed to save and load character due to exception [e.file]:[e.line], [e.name]") + +#undef UNIT_TEST_SAVING_FLAVOR_TEXT +#undef UNIT_TEST_SAVING_SILICON_FLAVOR_TEXT +#undef UNIT_TEST_SAVING_OOC_NOTES diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm index 99020ec7df..f1e4d4468b 100644 --- a/code/modules/vehicles/mecha/_mecha.dm +++ b/code/modules/vehicles/mecha/_mecha.dm @@ -272,7 +272,7 @@ /obj/vehicle/sealed/mecha/proc/update_part_values() ///Updates the values given by scanning module and capacitor tier, called when a part is removed or inserted. if(scanmod) - normal_step_energy_drain = 20 - (5 * scanmod.rating) //10 is normal, so on lowest part its worse, on second its ok and on higher its real good up to 0 on best + normal_step_energy_drain = initial(normal_step_energy_drain) * (1.5 / (scanmod.rating - 0.5)) //movement power cost is 3x of default at T1, 1x at T2, 0.6x at T3 and 0.4x at T4 step_energy_drain = normal_step_energy_drain else normal_step_energy_drain = 500 @@ -641,6 +641,9 @@ if(!Process_Spacemove(direction)) return FALSE if(!has_charge(step_energy_drain)) + if(TIMER_COOLDOWN_CHECK(src, COOLDOWN_MECHA_MESSAGE)) + to_chat(occupants, "[icon2html(src, occupants)]Insufficient power to move!") + TIMER_COOLDOWN_START(src, COOLDOWN_MECHA_MESSAGE, 2 SECONDS) return FALSE if(zoom_mode) to_chat(occupants, "[icon2html(src, occupants)]Unable to move while in zoom mode!") @@ -651,7 +654,7 @@ if(!scanmod || !capacitor) to_chat(occupants, "[icon2html(src, occupants)]Missing [scanmod? "capacitor" : "scanning module"].") return FALSE - if(lavaland_only && is_mining_level(z)) + if(lavaland_only && !is_mining_level(z)) to_chat(occupants, "[icon2html(src, occupants)]Invalid Environment.") return FALSE @@ -683,6 +686,7 @@ return TRUE set_glide_size(DELAY_TO_GLIDE_SIZE(movedelay)) + use_power(step_energy_drain) //Otherwise just walk normally . = step(src,direction, dir) diff --git a/code/modules/vehicles/mecha/combat/gygax.dm b/code/modules/vehicles/mecha/combat/gygax.dm index 29746a5fc7..ae7f846142 100644 --- a/code/modules/vehicles/mecha/combat/gygax.dm +++ b/code/modules/vehicles/mecha/combat/gygax.dm @@ -9,12 +9,13 @@ deflect_chance = 5 armor = list(MELEE = 25, BULLET = 20, LASER = 30, ENERGY = 15, BOMB = 0, BIO = 0, RAD = 0, FIRE = 100, ACID = 100) max_temperature = 25000 - leg_overload_coeff = 80 + leg_overload_coeff = 300 + overload_step_energy_drain_min = 300 force = 25 wreckage = /obj/structure/mecha_wreckage/gygax internal_damage_threshold = 35 max_equip = 3 - step_energy_drain = 3 + normal_step_energy_drain = 3 /obj/vehicle/sealed/mecha/combat/gygax/dark desc = "A lightweight exosuit, painted in a dark scheme. This model appears to have some modifications." @@ -24,7 +25,8 @@ deflect_chance = 20 armor = list(MELEE = 40, BULLET = 40, LASER = 50, ENERGY = 35, BOMB = 20, BIO = 0, RAD =20, FIRE = 100, ACID = 100) max_temperature = 35000 - leg_overload_coeff = 70 + leg_overload_coeff = 100 + overload_step_energy_drain_min = 100 force = 30 operation_req_access = list(ACCESS_SYNDICATE) internals_req_access = list(ACCESS_SYNDICATE) diff --git a/code/modules/vehicles/mecha/combat/medigax.dm b/code/modules/vehicles/mecha/combat/medigax.dm index 6b2f54a976..ec38dd37db 100644 --- a/code/modules/vehicles/mecha/combat/medigax.dm +++ b/code/modules/vehicles/mecha/combat/medigax.dm @@ -11,7 +11,7 @@ max_temperature = 25000 wreckage = /obj/structure/mecha_wreckage/odysseus internal_damage_threshold = 35 - step_energy_drain = 6 + normal_step_energy_drain = 6 infra_luminosity = 6 internals_req_access = list(ACCESS_ROBOTICS, ACCESS_MEDICAL) diff --git a/code/modules/vehicles/mecha/combat/phazon.dm b/code/modules/vehicles/mecha/combat/phazon.dm index c964b17e36..fad2b0783c 100644 --- a/code/modules/vehicles/mecha/combat/phazon.dm +++ b/code/modules/vehicles/mecha/combat/phazon.dm @@ -4,7 +4,7 @@ icon_state = "phazon" movedelay = 2 dir_in = 2 //Facing South. - step_energy_drain = 3 + normal_step_energy_drain = 3 max_integrity = 200 deflect_chance = 30 armor = list(MELEE = 30, BULLET = 30, LASER = 30, ENERGY = 30, BOMB = 30, BIO = 0, RAD = 50, FIRE = 100, ACID = 100) diff --git a/code/modules/vehicles/mecha/combat/reticence.dm b/code/modules/vehicles/mecha/combat/reticence.dm index b0c8b07aaf..300023b7ab 100644 --- a/code/modules/vehicles/mecha/combat/reticence.dm +++ b/code/modules/vehicles/mecha/combat/reticence.dm @@ -14,7 +14,7 @@ mecha_flags = CANSTRAFE | IS_ENCLOSED | HAS_LIGHTS internal_damage_threshold = 25 max_equip = 2 - step_energy_drain = 3 + normal_step_energy_drain = 3 color = "#87878715" stepsound = null turnsound = null diff --git a/code/modules/vehicles/mecha/mecha_control_console.dm b/code/modules/vehicles/mecha/mecha_control_console.dm index 38224fbbdd..ff5ea13059 100644 --- a/code/modules/vehicles/mecha/mecha_control_console.dm +++ b/code/modules/vehicles/mecha/mecha_control_console.dm @@ -118,7 +118,12 @@ return ..() /obj/item/mecha_parts/mecha_tracking/try_attach_part(mob/user, obj/vehicle/sealed/mecha/M) - if(!..()) + for(var/obj/item/I in M.contents) + if(istype(I, src)) + to_chat(user, "[M] already has a tracking beacon!") + return + . = ..() + if(!.) return M.trackers += src M.diag_hud_set_mechtracking() diff --git a/code/modules/vehicles/mecha/medical/odysseus.dm b/code/modules/vehicles/mecha/medical/odysseus.dm index bd415cd64f..dd693f9dbb 100644 --- a/code/modules/vehicles/mecha/medical/odysseus.dm +++ b/code/modules/vehicles/mecha/medical/odysseus.dm @@ -9,7 +9,7 @@ wreckage = /obj/structure/mecha_wreckage/odysseus internal_damage_threshold = 35 deflect_chance = 15 - step_energy_drain = 6 + normal_step_energy_drain = 6 internals_req_access = list(ACCESS_ROBOTICS, ACCESS_MEDICAL) /obj/vehicle/sealed/mecha/medical/odysseus/moved_inside(mob/living/carbon/human/H) diff --git a/code/modules/vehicles/mecha/working/ripley.dm b/code/modules/vehicles/mecha/working/ripley.dm index 965cdbedd9..40b2e61402 100644 --- a/code/modules/vehicles/mecha/working/ripley.dm +++ b/code/modules/vehicles/mecha/working/ripley.dm @@ -96,7 +96,7 @@ movedelay = 4 lights_power = 7 wreckage = /obj/structure/mecha_wreckage/ripley/deathripley - step_energy_drain = 0 + normal_step_energy_drain = 0 enclosed = TRUE enter_delay = 40 silicon_icon_state = null diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index 700a2fea7e..f84ced508e 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -864,21 +864,10 @@ GLOBAL_LIST_EMPTY(vending_products) if(isliving(usr)) var/mob/living/L = usr C = L.get_idcard(TRUE) - if(!C) - say("No card found.") + if(!can_transact(C)) flick(icon_deny,src) vend_ready = TRUE return - else if (!C.registered_account) - say("No account found.") - flick(icon_deny,src) - vend_ready = TRUE - return - else if(!C.registered_account.account_job) - say("Departmental accounts have been blacklisted from personal expenses due to embezzlement.") - flick(icon_deny, src) - vend_ready = TRUE - return // else if(age_restrictions && R.age_restricted && (!C.registered_age || C.registered_age < AGE_MINOR)) // say("You are not of legal age to purchase [R.name].") // if(!(usr in GLOB.narcd_underages)) @@ -901,16 +890,13 @@ GLOBAL_LIST_EMPTY(vending_products) price_to_use = 0 // it's free shut up if(coin_records.Find(R) || hidden_records.Find(R)) price_to_use = R.custom_premium_price ? R.custom_premium_price : extra_price - if(price_to_use && !account.adjust_money(-price_to_use)) + if(price_to_use && !attempt_transact(C, price_to_use)) say("You do not possess the funds to purchase [R.name].") flick(icon_deny,src) vend_ready = TRUE return - var/datum/bank_account/D = SSeconomy.get_dep_account(payment_department) - if(D) - D.adjust_money(price_to_use) - SSblackbox.record_feedback("amount", "vending_spent", price_to_use) - // log_econ("[price_to_use] credits were inserted into [src] by [D.account_holder] to buy [R].") + SSblackbox.record_feedback("amount", "vending_spent", price_to_use) + log_econ("[price_to_use] credits were inserted into [src] by [key_name(usr)] (account: [account.account_holder]) to buy [R].") if(last_shopper != REF(usr) || purchase_message_cooldown < world.time) say("Thank you for shopping with [src]!") purchase_message_cooldown = world.time + 5 SECONDS @@ -1169,7 +1155,7 @@ GLOBAL_LIST_EMPTY(vending_products) if(owner) owner.adjust_money(S.custom_price) SSblackbox.record_feedback("amount", "vending_spent", S.custom_price) - // log_econ("[S.custom_price] credits were spent on [src] buying a [S] by [owner.account_holder], owned by [private_a.account_holder].") + log_econ("[S.custom_price] credits were spent on [src] buying a [S] by [key_name(usr)] (account: [owner.account_holder]), owned by [private_a.account_holder].") vending_machine_input[N] = max(vending_machine_input[N] - 1, 0) S.forceMove(drop_location()) loaded_items-- diff --git a/config/config.txt b/config/config.txt index c0c683373d..63adaee716 100644 --- a/config/config.txt +++ b/config/config.txt @@ -15,6 +15,7 @@ $include entries/admin.txt $include entries/alert.txt $include entries/antag_rep.txt +$include entries/bot.txt $include entries/comms.txt $include entries/connections.txt $include entries/dbconfig.txt diff --git a/config/entries/bot.txt b/config/entries/bot.txt new file mode 100644 index 0000000000..eb9e8855e7 --- /dev/null +++ b/config/entries/bot.txt @@ -0,0 +1,17 @@ +## Chat Announce Options +## Various messages to be sent to game chats +## Uncommenting these will enable them, by default they will be broadcast to Game chat channels on TGS3 or non-admin channels on TGS4 +## If using TGS4, the string option can be set as a chat channel tag to limit the message to channels of that tag type (case-sensitive) +## i.e. CHAT_ANNOUNCE_NEW_GAME chat_channel_tag + +## Announcements for when a new round begins. +#CHAT_ANNOUNCE_NEW_GAME + +## Announcements for when the round ends, pending server reboot. +#CHAT_ROUNDEND_NOTICE_TAG + +## For where should the one true bird speak? +#CHAT_SQUAWK_TAG + +## Role ID that, when present, will be pinged every round end. +#CHAT_REBOOT_ROLE diff --git a/config/entries/general.txt b/config/entries/general.txt index 7fef691006..3688b8197b 100644 --- a/config/entries/general.txt +++ b/config/entries/general.txt @@ -31,15 +31,6 @@ ALLOW_HOLIDAYS ## Uncomment to show the names of the admin sending a pm from IRC instead of showing as a stealthmin. #SHOW_IRC_NAME -## Chat Announce Options -## Various messages to be sent to game chats -## Uncommenting these will enable them, by default they will be broadcast to Game chat channels on TGS3 or non-admin channels on TGS4 -## If using TGS4, the string option can be set as a chat channel tag to limit the message to channels of that tag type (case-sensitive) -## i.e. CHAT_ANNOUNCE_NEW_GAME chat_channel_tag - -## Send a message with the station name starting a new game -#CHAT_ANNOUNCE_NEW_GAME - ##Default screen resolution, in tiles. ## By default, this is 15x15, which gets simplified to 7 by BYOND, as it is a 1:1 screen ratio. ## For reference, Goonstation uses a resolution of 21x15 for it's widescreen mode. diff --git a/config/entries/logging.txt b/config/entries/logging.txt index 57588d7cd3..66a7c566ca 100644 --- a/config/entries/logging.txt +++ b/config/entries/logging.txt @@ -61,6 +61,9 @@ LOG_JOB_DEBUG ## Log shuttle related actions LOG_SHUTTLE +## Log economy-related actions +LOG_ECON + ## log all world.Topic() calls LOG_WORLD_TOPIC diff --git a/goon/icons/obj/power.dmi b/goon/icons/obj/power.dmi index bb787c0b3c..d2a623bf6c 100644 Binary files a/goon/icons/obj/power.dmi and b/goon/icons/obj/power.dmi differ diff --git a/html/changelogs/AutoChangeLog-pr-15651.yml b/html/changelogs/AutoChangeLog-pr-15651.yml new file mode 100644 index 0000000000..e63392351d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-15651.yml @@ -0,0 +1,4 @@ +author: "timothyteakettle" +delete-after: True +changes: + - rscadd: "adds paper skin and glass bones quirks" diff --git a/html/changelogs/AutoChangeLog-pr-15652.yml b/html/changelogs/AutoChangeLog-pr-15652.yml new file mode 100644 index 0000000000..751e8d8b73 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-15652.yml @@ -0,0 +1,4 @@ +author: "SandPoot" +delete-after: True +changes: + - refactor: "Completely replaces the new player panel *not the admin one* with TG's." diff --git a/html/changelogs/archive/2022-05.yml b/html/changelogs/archive/2022-05.yml index a98a2791c6..e55d2a3bca 100644 --- a/html/changelogs/archive/2022-05.yml +++ b/html/changelogs/archive/2022-05.yml @@ -41,3 +41,9 @@ 2022-05-19: timothyteakettle: - rscadd: quirks are now organised into three tabs for positive, neutral and negative +2022-05-21: + Bhijn & Myr: + - bugfix: Blood trails no longer have a duplicate directionless iconstate as their + base sprite, fixing the Wonkiness of blood trails going west/east. + SandPoot: + - bugfix: Silicon flavor texts are no longer broken... maybe? diff --git a/html/changelogs/archive/2022-06.yml b/html/changelogs/archive/2022-06.yml new file mode 100644 index 0000000000..30f111e448 --- /dev/null +++ b/html/changelogs/archive/2022-06.yml @@ -0,0 +1,58 @@ +2022-06-01: + DeltaFire15: + - bugfix: Mechs now use power when moving. + - bugfix: The check for mining-only mechs is no longer inverted to only allow non-mining + z levels. + - bugfix: Reverted an accidental buff from the /tg/ mech code port and its adjustment + to citadel's mech state. + - bugfix: 'Mechs with different base movement power cost now actually have it apply + instead of immediately being overwritten. tweak: Adjusted part impact on movement + power use to make more sense.' + timothyteakettle: + - rscadd: 'optimises quirks somewhat tweak: nyctophobia will now stop run intent + when you try to move in darkness with it on, compared to the old behaviour of + automatically turning it off in darkness when it is on and you have been in + darkness for >= 0.2 seconds tweak: nyctophobia and lightless will both not update + your mood until you move now, as part of the optimisations made by changing + them to tg code' +2022-06-02: + Bhijn & Myr: + - bugfix: You can no longer attach multiple tracking beacons to a single mech, fixing + an exploit where mechs can be nuked with minimal effort. + - bugfix: Omegastation's airmix filter is no longer inverted! The oxygen:nitrogen + levels of Omega later in the round should now be normal again. + - rscadd: 'The TGS bot is now capable of pinging the reboot role! Rejoice! tweak: + The TGS bot''s automated output can now be configured to define which channels + certain messages are output to.' + - rscadd: Jukeboxes now have a queue! You can pay 40 credits to have any song of + your choosing queued up for playback on the jukebox! + - rscadd: Jukeboxes can now be emagged, allowing you to boost their volume up to + 210%, along with removing all of its access restrictions. Additionally, a sufficiently + loud jukebox is fully capable of causing potentially permanent damage to your + ears. + - rscadd: 'Jukeboxes now support multi-z! This is self-explanatory. tweak: Volume + knobs now work on jukeboxes! tweak: Muffled jukeboxes have been given a buff. + tweak: Sounds now factor in the distance multiplier for their baseline z offset. + tweak: SOUND_UPDATE now bypasses the can_hear() check in playsound_local() (thanks + Azarak for helping figure this out!)' + - bugfix: 'Jukebox audio no longer uses playsound_local(), allowing falloff to properly + apply to jukeboxes (this fixes the bug where they were significantly quieter + than intended) tweak: When deaf, jukeboxes no longer mute the playing track + permanently.' + - bugfix: Ears are capable of taking damage again. + - rscadd: Jukeboxes have been added to Boxstation, Lambdastation, Metastation, and + Omegastation + - bugfix: Boxstation's incinerator sensor now properly reports to atmos's machinery + again + Putnam3145: + - balance: Nitryl is higher risk, can't be sold for as much + - refactor: Gas prices are now part of the gas data structure + TrojanCoyote: + - rscadd: sprites for muzzled and non muzzled mobs for the masks, along with the + adjusted versions, icons for the masks as well. + bunny232: + - rscadd: Universal blood type now accepts bug blood. +2022-06-03: + Bhijn & Myr: + - bugfix: Solar panels now actually face the proper direction. This is achieved + by reducing the iconstate to only 1 dir. diff --git a/icons/blanks/32x32.dmi b/icons/blanks/32x32.dmi new file mode 100644 index 0000000000..6c4f2b33e0 Binary files /dev/null and b/icons/blanks/32x32.dmi differ diff --git a/icons/blanks/96x96.dmi b/icons/blanks/96x96.dmi new file mode 100644 index 0000000000..d79e60c111 Binary files /dev/null and b/icons/blanks/96x96.dmi differ diff --git a/icons/blanks/blank_title.png b/icons/blanks/blank_title.png new file mode 100644 index 0000000000..387a21f03a Binary files /dev/null and b/icons/blanks/blank_title.png differ diff --git a/icons/default_title.dmi b/icons/default_title.dmi deleted file mode 100644 index 633bd434b8..0000000000 Binary files a/icons/default_title.dmi and /dev/null differ diff --git a/icons/hud/lobby/background.dmi b/icons/hud/lobby/background.dmi new file mode 100644 index 0000000000..baae06fc1b Binary files /dev/null and b/icons/hud/lobby/background.dmi differ diff --git a/icons/hud/lobby/bottom_buttons.dmi b/icons/hud/lobby/bottom_buttons.dmi new file mode 100644 index 0000000000..d0aa1228c7 Binary files /dev/null and b/icons/hud/lobby/bottom_buttons.dmi differ diff --git a/icons/hud/lobby/character_setup.dmi b/icons/hud/lobby/character_setup.dmi new file mode 100644 index 0000000000..d3b94fb43e Binary files /dev/null and b/icons/hud/lobby/character_setup.dmi differ diff --git a/icons/hud/lobby/join.dmi b/icons/hud/lobby/join.dmi new file mode 100644 index 0000000000..416f315770 Binary files /dev/null and b/icons/hud/lobby/join.dmi differ diff --git a/icons/hud/lobby/observe.dmi b/icons/hud/lobby/observe.dmi new file mode 100644 index 0000000000..f59252b3ea Binary files /dev/null and b/icons/hud/lobby/observe.dmi differ diff --git a/icons/hud/lobby/poll_overlay.dmi b/icons/hud/lobby/poll_overlay.dmi new file mode 100644 index 0000000000..2fc9716260 Binary files /dev/null and b/icons/hud/lobby/poll_overlay.dmi differ diff --git a/icons/hud/lobby/ready.dmi b/icons/hud/lobby/ready.dmi new file mode 100644 index 0000000000..d2a45a883f Binary files /dev/null and b/icons/hud/lobby/ready.dmi differ diff --git a/icons/mob/clothing/mask.dmi b/icons/mob/clothing/mask.dmi index 2413b52d71..3c50e4d42d 100644 Binary files a/icons/mob/clothing/mask.dmi and b/icons/mob/clothing/mask.dmi differ diff --git a/icons/mob/clothing/mask_muzzled.dmi b/icons/mob/clothing/mask_muzzled.dmi index 9f483c8d47..5e0bb46c34 100644 Binary files a/icons/mob/clothing/mask_muzzled.dmi and b/icons/mob/clothing/mask_muzzled.dmi differ diff --git a/icons/obj/clothing/masks.dmi b/icons/obj/clothing/masks.dmi index f5bc9e7046..b86731babd 100644 Binary files a/icons/obj/clothing/masks.dmi and b/icons/obj/clothing/masks.dmi differ diff --git a/icons/runtime/README.md b/icons/runtime/README.md new file mode 100644 index 0000000000..804cb2f285 --- /dev/null +++ b/icons/runtime/README.md @@ -0,0 +1,5 @@ +# Runtime Loaded Icons + +These icons are not compiled into the .rsc or are otherwise loaded at runtime. + +Please keep all (non-config) icons that do this in this folder as it is needed by the [deploy.sh](../../tools/deploy.sh) script to minimize build output. diff --git a/icons/runtime/default_title.dmi b/icons/runtime/default_title.dmi new file mode 100644 index 0000000000..17f4787f84 Binary files /dev/null and b/icons/runtime/default_title.dmi differ diff --git a/icons/runtime/tcg/default.dmi b/icons/runtime/tcg/default.dmi new file mode 100644 index 0000000000..f42113f30e Binary files /dev/null and b/icons/runtime/tcg/default.dmi differ diff --git a/icons/runtime/tcg/xenos.dmi b/icons/runtime/tcg/xenos.dmi new file mode 100644 index 0000000000..682b0a6966 Binary files /dev/null and b/icons/runtime/tcg/xenos.dmi differ diff --git a/modular_citadel/code/modules/client/loadout/mask.dm b/modular_citadel/code/modules/client/loadout/mask.dm index 16b47e60e7..f1cf2a5d9f 100644 --- a/modular_citadel/code/modules/client/loadout/mask.dm +++ b/modular_citadel/code/modules/client/loadout/mask.dm @@ -15,6 +15,16 @@ path = /obj/item/clothing/mask/joy cost = 3 +/datum/gear/mask/kitsune + name = "White Kitsune Mask" + path = /obj/item/clothing/mask/kitsunewhi + cost = 2 + +/datum/gear/mask/black_kitsune + name = "Black Kitsune Mask" + path = /obj/item/clothing/mask/kitsuneblk + cost = 2 + /datum/gear/mask/gas name = "Gas Mask" path = /obj/item/clothing/mask/gas diff --git a/modular_citadel/code/modules/client/preferences_savefile.dm b/modular_citadel/code/modules/client/preferences_savefile.dm index 102ae41921..4942a1041f 100644 --- a/modular_citadel/code/modules/client/preferences_savefile.dm +++ b/modular_citadel/code/modules/client/preferences_savefile.dm @@ -35,5 +35,4 @@ WRITE_FILE(S["feature_xeno_head"], features["xenohead"]) //flavor text WRITE_FILE(S["feature_flavor_text"], features["flavor_text"]) - WRITE_FILE(S["silicon_feature_flavor_text"], features["silicon_flavor_text"]) - + WRITE_FILE(S["feature_silicon_flavor_text"], features["silicon_flavor_text"]) diff --git a/tgstation.dme b/tgstation.dme index 4c13f821f3..c236cb5447 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -153,6 +153,7 @@ #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\signals.dm" +#include "code\__DEFINES\dcs\signals\signals_subsystem.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movement.dm" #include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_living.dm" #include "code\__DEFINES\mapping\maploader.dm" @@ -175,6 +176,7 @@ #include "code\__HELPERS\angles.dm" #include "code\__HELPERS\areas.dm" #include "code\__HELPERS\chat.dm" +#include "code\__HELPERS\clients.dm" #include "code\__HELPERS\cmp.dm" #include "code\__HELPERS\config.dm" #include "code\__HELPERS\custom_holoforms.dm" @@ -284,6 +286,7 @@ #include "code\_onclick\hud\map_popups.dm" #include "code\_onclick\hud\monkey.dm" #include "code\_onclick\hud\movable_screen_objects.dm" +#include "code\_onclick\hud\new_player.dm" #include "code\_onclick\hud\picture_in_picture.dm" #include "code\_onclick\hud\plane_master.dm" #include "code\_onclick\hud\radial.dm" @@ -322,6 +325,7 @@ #include "code\controllers\configuration\entries\admin.dm" #include "code\controllers\configuration\entries\alert.dm" #include "code\controllers\configuration\entries\antag_rep.dm" +#include "code\controllers\configuration\entries\bot.dm" #include "code\controllers\configuration\entries\comms.dm" #include "code\controllers\configuration\entries\connections.dm" #include "code\controllers\configuration\entries\dbconfig.dm" diff --git a/tgui/packages/tgui/interfaces/Jukebox.js b/tgui/packages/tgui/interfaces/Jukebox.js index b23e55469b..99ddf90be0 100644 --- a/tgui/packages/tgui/interfaces/Jukebox.js +++ b/tgui/packages/tgui/interfaces/Jukebox.js @@ -1,7 +1,7 @@ import { sortBy } from 'common/collections'; import { flow } from 'common/fp'; import { useBackend } from '../backend'; -import { Box, Button, Dropdown, Section, Knob, LabeledControls, LabeledList } from '../components'; +import { Box, Button, Dropdown, Section, Knob, LabeledControls, LabeledList, Stack, Tabs } from '../components'; import { Window } from '../layouts'; export const Jukebox = (props, context) => { @@ -10,98 +10,134 @@ export const Jukebox = (props, context) => { active, track_selected, track_length, - track_beat, volume, + is_emagged, + cost_for_play, + has_access, } = data; const songs = flow([ sortBy( song => song.name), ])(data.songs || []); + const queued_tracks = data.queued_tracks || []; return ( |