Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verbs module: Do, LOOC, Subtle and more! #68

Merged
merged 8 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions code/__DEFINES/~doppler_defines/admin.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#define MUTE_LOOC (1<<6)
1 change: 1 addition & 0 deletions code/__DEFINES/~doppler_defines/banning.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#define BAN_LOOC "LOOC"
4 changes: 4 additions & 0 deletions code/__DEFINES/~doppler_defines/keybindings.dm
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
#define COMSIG_KB_LIVING_COMBAT_INDICATOR "keybinding_living_combat_indicator"
#define COMSIG_KB_MOB_PIXEL_SHIFT_DOWN "keybinding_mob_pixel_shift_down"
#define COMSIG_KB_MOB_PIXEL_SHIFT_UP "keybinding_mob_pixel_shift_up"
#define COMSIG_KB_CLIENT_LOOC_DOWN "keybinding_client_looc_down"
#define COMSIG_KB_CLIENT_WHISPER_DOWN "keybinding_client_whisper_down"
#define COMSIG_KB_CLIENT_DO_DOWN "keybinding_client_do_down"
#define COMSIG_KB_CLIENT_DO_LONGER_DOWN "keybinding_client_do_longer_down"
11 changes: 11 additions & 0 deletions code/__DEFINES/~doppler_defines/logging.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Logging types for log_message()
#define LOG_SUBTLE (1 << 23)

//Individual logging panel pages
#undef INDIVIDUAL_EMOTE_LOG
#define INDIVIDUAL_EMOTE_LOG (LOG_EMOTE | LOG_SUBTLE)
#undef INDIVIDUAL_SHOW_ALL_LOG
#define INDIVIDUAL_SHOW_ALL_LOG (LOG_ATTACK | LOG_SAY | LOG_WHISPER | LOG_EMOTE | LOG_SUBTLE | LOG_DSAY | LOG_PDA | LOG_CHAT | LOG_COMMENT | LOG_TELECOMMS | LOG_OOC | LOG_ADMIN | LOG_OWNERSHIP | LOG_GAME | LOG_ADMIN_PRIVATE | LOG_ASAY | LOG_MECHA | LOG_VIRUS | LOG_SHUTTLE | LOG_ECON)

// Game categories
#define LOG_CATEGORY_GAME_SUBTLE "game-subtle"
1 change: 1 addition & 0 deletions code/__DEFINES/~doppler_defines/say.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#define LOOC_RANGE 7
2 changes: 2 additions & 0 deletions code/__DEFINES/~doppler_defines/span.dm
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
#define span_cyan(str) ("<span class='cyan'>" + str + "</span>")
#define span_orange(str) ("<span class='orange'>" + str + "</span>")
#define span_yellow(str) ("<span class='yellow'>" + str + "</span>")
#define span_rlooc(str) ("<span class='rlooc'>" + str + "</span>")
#define span_emote(str) ("<span class='emote'>" + str + "</span>")
3 changes: 3 additions & 0 deletions code/__DEFINES/~doppler_defines/speech_channels.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#define LOOC_CHANNEL "LOOC" // LOOC
#define WHIS_CHANNEL "Whis" // Whisper
#define DO_CHANNEL "Do" // Do
4 changes: 4 additions & 0 deletions code/__HELPERS/logging/_logging.dm
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ GLOBAL_LIST_INIT(testing_global_profiler, list("_PROFILE_NAME" = "Global"))
log_emote(log_text, data)
if(LOG_RADIO_EMOTE)
log_radio_emote(log_text, data)
//DOPPLER EDIT ADDITION BEGIN
if(LOG_SUBTLE)
log_subtle(log_text, data)
//DOPPLER EDIT ADDITION END
if(LOG_DSAY)
log_dsay(log_text, data)
if(LOG_PDA)
Expand Down
4 changes: 4 additions & 0 deletions code/__HELPERS/logging/mob.dm
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@
colored_message = "(EMOTE) [colored_message]"
if(LOG_RADIO_EMOTE)
colored_message = "(RADIOEMOTE) [colored_message]"
//DOPPLER EDIT ADDITION BEGIN
if(LOG_SUBTLE)
colored_message = "(EMOTE) (SUBTLE) [colored_message]"
//DOPPLER EDIT ADDITION END

var/list/timestamped_message = list("\[[time_stamp(format = "YYYY-MM-DD hh:mm:ss")]\] [key_name_and_tag(src)] [loc_name(src)] (Event #[LAZYLEN(logging[smessage_type])])" = colored_message)

Expand Down
12 changes: 12 additions & 0 deletions code/__HELPERS/~doppler_helpers/chat.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Returns a boolean based on whether or not the string contains a comma or an apostrophe,
* to be used for emotes to decide whether or not to have a space between the name of the user
* and the emote.
*
* Requires the message to be HTML decoded beforehand. Not doing it here for performance reasons.
*
* Returns TRUE if there should be a space, FALSE if there shouldn't.
*/
/proc/should_have_space_before_emote(string)
var/static/regex/no_spacing_emote_characters = regex(@"(,|')")
return no_spacing_emote_characters.Find(string) ? FALSE : TRUE
3 changes: 3 additions & 0 deletions code/__HELPERS/~doppler_helpers/logging.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/// This logs subtle emotes in game.log
/proc/log_subtle(text, list/data)
logger.Log(LOG_CATEGORY_GAME_SUBTLE, text, data)
32 changes: 32 additions & 0 deletions code/__HELPERS/~doppler_helpers/verbs.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/** Get all hearers in range, ignores walls and such. Code stolen from `/proc/get_hearers_in_view()`
* Much faster and less expensive than range()
*/
/proc/get_hearers_in_looc_range(atom/source, range_radius = LOOC_RANGE)
var/turf/center_turf = get_turf(source)
if(!center_turf)
return

. = list()
var/old_luminosity = center_turf.luminosity
if(range_radius <= 0) //special case for if only source cares
for(var/atom/movable/target as anything in center_turf)
var/list/recursive_contents = target.important_recursive_contents?[RECURSIVE_CONTENTS_HEARING_SENSITIVE]
if(recursive_contents)
. += recursive_contents
return .

var/list/hearables_from_grid = SSspatial_grid.orthogonal_range_search(source, RECURSIVE_CONTENTS_HEARING_SENSITIVE, range_radius)

if(!length(hearables_from_grid))//we know that something is returned by the grid, but we dont know if we need to actually filter down the output
return .

var/list/assigned_oranges_ears = SSspatial_grid.assign_oranges_ears(hearables_from_grid)

for(var/mob/oranges_ear/ear in range(range_radius, center_turf))
. += ear.references

for(var/mob/oranges_ear/remaining_ear as anything in assigned_oranges_ears) //we need to clean up our mess
remaining_ear.unassign()

center_turf.luminosity = old_luminosity
return .
2 changes: 2 additions & 0 deletions code/_globalvars/~doppler_globalvars/configuration.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// LOOC Module
GLOBAL_VAR_INIT(looc_allowed, TRUE)
34 changes: 34 additions & 0 deletions code/datums/emotes.dm
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
var/is_important = emote_type & EMOTE_IMPORTANT
var/is_visual = emote_type & EMOTE_VISIBLE
var/is_audible = emote_type & EMOTE_AUDIBLE
var/space = should_have_space_before_emote(html_decode(msg)[1]) ? " " : "" // DOPPLER EDIT ADDITION

// Emote doesn't get printed to chat, runechat only
if(emote_type & EMOTE_RUNECHAT)
Expand Down Expand Up @@ -155,24 +156,57 @@
deaf_message = "<span class='emote'>You see how <b>[user]</b> [msg]</span>",
self_message = msg,
audible_message_flags = EMOTE_MESSAGE|ALWAYS_SHOW_SELF_MESSAGE,
separation = space, // DOPPLER EDIT ADDITION
)
// Emote is entirely audible, no visible component
else if(is_audible)
user.audible_message(
message = msg,
self_message = msg,
audible_message_flags = EMOTE_MESSAGE,
separation = space, // DOPPLER EDIT ADDITION
)
// Emote is entirely visible, no audible component
else if(is_visual)
user.visible_message(
message = msg,
self_message = msg,
visible_message_flags = EMOTE_MESSAGE|ALWAYS_SHOW_SELF_MESSAGE,
separation = space, // DOPPLER EDIT ADDITION
)
else
CRASH("Emote [type] has no valid emote type set!")

// DOPPLER EDIT ADDITION START - AI QOL - RELAY EMOTES OVER HOLOPADS
var/obj/effect/overlay/holo_pad_hologram/hologram = GLOB.hologram_impersonators[user]
if(hologram)
if(is_important)
for(var/mob/living/viewer in viewers(world.view, hologram))
to_chat(viewer, msg)
else if(is_visual && is_audible)
hologram.audible_message(
message = msg,
deaf_message = "<span class='emote'>You see how <b>[user]</b> [msg]</span>",
self_message = msg,
audible_message_flags = EMOTE_MESSAGE|ALWAYS_SHOW_SELF_MESSAGE,
separation = space,
)
else if(is_audible)
hologram.audible_message(
message = msg,
self_message = msg,
audible_message_flags = EMOTE_MESSAGE,
separation = space,
)
else if(is_visual)
hologram.visible_message(
message = msg,
self_message = msg,
visible_message_flags = EMOTE_MESSAGE|ALWAYS_SHOW_SELF_MESSAGE,
separation = space,
)
// DOPPLER EDIT ADDITION END

if(!isnull(user.client))
var/dchatmsg = "<b>[user]</b> [msg]"
for(var/mob/ghost as anything in GLOB.dead_mob_list - viewers(get_turf(user)))
Expand Down
6 changes: 4 additions & 2 deletions code/game/machinery/hologram.dm
Original file line number Diff line number Diff line change
Expand Up @@ -550,13 +550,15 @@ Possible to do for anyone motivated enough:

if(AI)
AI.eyeobj.setLoc(get_turf(src)) //ensure the AI camera moves to the holopad
hologram.Impersonation = AI //DOPPLER EDIT ADDITION -- Customization; puts the AI core as the impersonated mob so that the examine proc can be redirected
else //make it like real life
hologram.Impersonation = user
hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it.
//hologram.mouse_opacity = MOUSE_OPACITY_TRANSPARENT//So you can't click on it. // DOPPLER EDIT -- Customization; Making holograms clickable/examinable
hologram.layer = FLY_LAYER //Above all the other objects/mobs. Or the vast majority of them.
SET_PLANE_EXPLICIT(hologram, ABOVE_GAME_PLANE, src)
hologram.set_anchored(TRUE)//So space wind cannot drag it.
hologram.name = "[user.name] (Hologram)"//If someone decides to right click.
//hologram.name = "[user.name] (Hologram)"//If someone decides to right click. // DOPPLER EDIT
hologram.name = user.name //DOPPLER EDIT -- Make the name exact, so that the double-emotes are less jarring in the chat
set_holo(user, hologram)

set_holo(user, hologram)
Expand Down
3 changes: 3 additions & 0 deletions code/modules/admin/sql_ban_system.dm
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,9 @@
ROLE_WIZARD,
ROLE_VOIDWALKER,
),
"Doppler Ban Options" = list(
BAN_LOOC, // DOPPLER EDIT ADDITION - LOOC muting
), // DOPPLER EDIT ADDITION - EXTRA_BANS
)
for(var/department in long_job_lists)
output += "<div class='column'><label class='rolegroup long [ckey(department)]'>[tgui_fancy ? "<input type='checkbox' name='[department]' class='hidden' onClick='header_click_all_checkboxes(this)'>" : ""][department]</label><div class='content'>"
Expand Down
5 changes: 5 additions & 0 deletions code/modules/admin/verbs/admin.dm
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,11 @@ ADMIN_VERB(drop_everything, R_ADMIN, "Drop Everything", ADMIN_VERB_NO_DESCRIPTIO
if(MUTE_DEADCHAT)
mute_string = "deadchat and DSAY"
feedback_string = "Deadchat"
// DOPPLER EDIT ADDITION START - LOOC muting.
if(MUTE_LOOC)
mute_string = "LOOC"
feedback_string = "LOOC"
// DOPPLER EDIT ADDITION END - LOOC muting.
if(MUTE_INTERNET_REQUEST)
mute_string = "internet sound requests"
feedback_string = "Internet Sound Requests"
Expand Down
5 changes: 4 additions & 1 deletion code/modules/admin/verbs/admingame.dm
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,10 @@ ADMIN_VERB_ONLY_CONTEXT_MENU(show_player_panel, R_ADMIN, "Show Player Panel", mo
body += "<A href='?_src_=holder;[HrefToken()];mute=[player.ckey];mute_type=[MUTE_PRAY]'><font color='[(muted & MUTE_PRAY)?"red":"blue"]'>PRAY</font></a> | "
body += "<A href='?_src_=holder;[HrefToken()];mute=[player.ckey];mute_type=[MUTE_ADMINHELP]'><font color='[(muted & MUTE_ADMINHELP)?"red":"blue"]'>ADMINHELP</font></a> | "
body += "<A href='?_src_=holder;[HrefToken()];mute=[player.ckey];mute_type=[MUTE_INTERNET_REQUEST]'><font color='[(muted & MUTE_INTERNET_REQUEST)?"red":"blue"]'>WEBREQ</font></a> | "
body += "<A href='?_src_=holder;[HrefToken()];mute=[player.ckey];mute_type=[MUTE_DEADCHAT]'><font color='[(muted & MUTE_DEADCHAT)?"red":"blue"]'>DEADCHAT</font></a>\]"
//DOPPLER EDIT ADDITION BEGIN - LOOC muting.
body += "<A href='?_src_=holder;[HrefToken()];mute=[player.ckey];mute_type=[MUTE_DEADCHAT]'><font color='[(muted & MUTE_DEADCHAT)?"red":"blue"]'>DEADCHAT</font></a> | "
body += "<A href='?_src_=holder;[HrefToken()];mute=[player.ckey];mute_type=[MUTE_LOOC]'><font color='[(muted & MUTE_LOOC)?"red":"blue"]'>LOOC</font></a>\]"
//DOPPLER EDIT ADDITION END - LOOC muting.
body += "(<A href='?_src_=holder;[HrefToken()];mute=[player.ckey];mute_type=[MUTE_ALL]'><font color='[(muted & MUTE_ALL)?"red":"blue"]'>toggle all</font></a>)"

body += "<br><br>"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
return FALSE
return ..()

/mob/living/simple_animal/hostile/megafauna/dragon/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, visible_message_flags = NONE)
/mob/living/simple_animal/hostile/megafauna/dragon/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, visible_message_flags = NONE, separation = " ") // DOPPLER EDIT ADDITION - separation
if(swooping & SWOOP_INVULNERABLE) //to suppress attack messages without overriding every single proc that could send a message saying we got hit
return
return ..()
Expand Down
35 changes: 29 additions & 6 deletions code/modules/mob/mob.dm
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@
* * ignored_mobs (optional) doesn't show any message to any mob in this list.
* * visible_message_flags (optional) is the type of message being sent.
*/
/atom/proc/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, visible_message_flags = NONE)
/atom/proc/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, visible_message_flags = NONE, separation = " ") // DOPPLER EDIT ADDITION - separation
var/turf/T = get_turf(src)
if(!T)
return
Expand All @@ -276,12 +276,23 @@
var/list/hearers = get_hearers_in_view(vision_distance, src) //caches the hearers and then removes ignored mobs.
hearers -= ignored_mobs

//DOPPLER EDIT ADDITION BEGIN - AI QoL
for(var/mob/camera/ai_eye/ai_eye in hearers)
if(ai_eye.ai?.client && !(ai_eye.ai.stat == DEAD))
hearers -= ai_eye
hearers |= ai_eye.ai

for(var/obj/effect/overlay/holo_pad_hologram/holo in hearers)
if(holo.Impersonation?.client)
hearers |= holo.Impersonation
//DOPPLER EDIT ADDITION END - AI QoL

if(self_message)
hearers -= src

var/raw_msg = message
if(visible_message_flags & EMOTE_MESSAGE)
message = "<span class='emote'><b>[src]</b> [message]</span>"
message = "<span class='emote'><b>[src]</b>[separation][message]</span>" // DOPPLER EDIT ADDITION - Better emotes

for(var/mob/M in hearers)
if(!M.client)
Expand Down Expand Up @@ -311,7 +322,7 @@


///Adds the functionality to self_message.
/mob/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, visible_message_flags = NONE)
/mob/visible_message(message, self_message, blind_message, vision_distance = DEFAULT_MESSAGE_RANGE, list/ignored_mobs, visible_message_flags = NONE, separation = " ") // DOPPLER EDIT ADDITION - separation
. = ..()
if(!self_message)
return
Expand Down Expand Up @@ -342,13 +353,25 @@
* * self_message (optional) is what the src mob hears.
* * audible_message_flags (optional) is the type of message being sent.
*/
/atom/proc/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, audible_message_flags = NONE)
/atom/proc/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, audible_message_flags = NONE, separation = " ") // DOPPLER EDIT ADDITION - separation
var/list/hearers = get_hearers_in_view(hearing_distance, src)

//DOPPLER EDIT ADDITION BEGIN - AI QoL
for(var/mob/camera/ai_eye/ai_eye in hearers)
if(ai_eye.ai?.client && !(ai_eye.ai.stat == DEAD))
hearers -= ai_eye
hearers |= ai_eye.ai

for(var/obj/effect/overlay/holo_pad_hologram/holo in hearers)
if(holo.Impersonation?.client)
hearers |= holo.Impersonation
//DOPPLER EDIT ADDITION END - AI QoL

if(self_message)
hearers -= src
var/raw_msg = message
if(audible_message_flags & EMOTE_MESSAGE)
message = "<span class='emote'><b>[src]</b> [message]</span>"
message = "<span class='emote'><b>[src]</b>[separation][message]</span>" //DOPPLER EDIT CHANGE
for(var/mob/M in hearers)
if(audible_message_flags & EMOTE_MESSAGE && runechat_prefs_check(M, audible_message_flags) && M.can_hear())
M.create_chat_message(src, raw_message = raw_msg, runechat_flags = audible_message_flags)
Expand All @@ -365,7 +388,7 @@
* * deaf_message (optional) is what deaf people will see.
* * hearing_distance (optional) is the range, how many tiles away the message can be heard.
*/
/mob/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, audible_message_flags = NONE)
/mob/audible_message(message, deaf_message, hearing_distance = DEFAULT_MESSAGE_RANGE, self_message, audible_message_flags = NONE, separation = " ") // DOPPLER EDIT ADDITION - separation
. = ..()
if(!self_message)
return
Expand Down
2 changes: 1 addition & 1 deletion code/modules/tgui_input/say_modal/modal.dm
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
if(!payload?["channel"])
CRASH("No channel provided to an open TGUI-Say")
window_open = TRUE
if(payload["channel"] != OOC_CHANNEL && payload["channel"] != ADMIN_CHANNEL)
if(payload["channel"] != OOC_CHANNEL && payload["channel"] != ADMIN_CHANNEL && payload["channel"] != LOOC_CHANNEL) // DOPPLER EDIT CHANGE (Add LOOC_CHANNEL)
start_thinking()
if(!client.typing_indicators)
log_speech_indicators("[key_name(client)] started typing at [loc_name(client.mob)], indicators DISABLED.")
Expand Down
12 changes: 11 additions & 1 deletion code/modules/tgui_input/say_modal/speech.dm
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
/datum/tgui_say/proc/alter_entry(payload)
var/entry = payload["entry"]
/// No OOC leaks
if(!entry || payload["channel"] == OOC_CHANNEL || payload["channel"] == ME_CHANNEL)
if(!entry || payload["channel"] == OOC_CHANNEL || payload["channel"] == ME_CHANNEL || payload["channel"] == LOOC_CHANNEL) // DOPPLER EDIT CHANGE - VERBS
return pick(hurt_phrases)
/// Random trimming for larger sentences
if(length(entry) > 50)
Expand Down Expand Up @@ -47,6 +47,16 @@
if(ADMIN_CHANNEL)
SSadmin_verbs.dynamic_invoke_verb(client, /datum/admin_verb/cmd_admin_say, entry)
return TRUE
// DOPPLER EDIT ADDITION START - VERBS
if(LOOC_CHANNEL)
client.looc(entry)
return TRUE
if(WHIS_CHANNEL)
client.mob.whisper_verb(entry)
return TRUE
if(DO_CHANNEL)
client.mob.do_verb(entry)
// DOPPLER EDIT ADDITION END
return FALSE

/**
Expand Down
8 changes: 8 additions & 0 deletions modular_doppler/administration/code/preferences.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/datum/preference/toggle/admin
abstract_type = /datum/preference/toggle/admin

/datum/preference/toggle/admin/is_accessible(datum/preferences/preferences)
if (!..(preferences))
return FALSE

return is_admin(preferences.parent)
14 changes: 14 additions & 0 deletions modular_doppler/emotes/code/hologram.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
GLOBAL_LIST_EMPTY(hologram_impersonators)

/obj/machinery/holopad/set_holo(mob/living/user, obj/effect/overlay/holo_pad_hologram/holo)
if(holo.Impersonation)
GLOB.hologram_impersonators[user] = holo
holo.become_hearing_sensitive() // Well, we need to show up on "get_hearers_in_view()"
. = ..()

/obj/machinery/holopad/clear_holo(mob/living/user)
var/obj/effect/overlay/holo_pad_hologram/hologram = GLOB.hologram_impersonators[user]
if(hologram)
hologram.lose_hearing_sensitivity()
GLOB.hologram_impersonators -= user
. = ..()
Loading
Loading