Merge branch 'master' into upstream-merge-34067

This commit is contained in:
deathride58
2018-01-08 20:01:59 +00:00
committed by GitHub
745 changed files with 25216 additions and 18453 deletions
+18 -3
View File
@@ -53,8 +53,9 @@
. = !(IsAdminAdvancedProcCall() && GLOB.LastAdminCalledProc == "ValidateAndSet" && GLOB.LastAdminCalledTargetRef == "[REF(src)]")
if(!.)
log_admin_private("Config set of [type] to [str_val] attempted by [key_name(usr)]")
/datum/config_entry/proc/ValidateAndSet(str_val)
VASProcCallGuard(str_val)
CRASH("Invalid config entry type!")
/datum/config_entry/proc/ValidateKeyedList(str_val, list_mode, splitter)
@@ -78,12 +79,12 @@
if(LIST_MODE_TEXT)
temp = key_value
continue_check = temp
if(continue_check && ValidateKeyName(key_name))
if(continue_check && ValidateListEntry(key_name, temp))
value[key_name] = temp
return TRUE
return FALSE
/datum/config_entry/proc/ValidateKeyName(key_name)
/datum/config_entry/proc/ValidateListEntry(key_name, key_value)
return TRUE
/datum/config_entry/string
@@ -95,6 +96,8 @@
return var_name != "auto_trim" && ..()
/datum/config_entry/string/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
value = auto_trim ? trim(str_val) : str_val
return TRUE
@@ -106,6 +109,8 @@
var/min_val = -INFINITY
/datum/config_entry/number/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
var/temp = text2num(trim(str_val))
if(!isnull(temp))
value = CLAMP(integer ? round(temp) : temp, min_val, max_val)
@@ -123,6 +128,8 @@
abstract_type = /datum/config_entry/flag
/datum/config_entry/flag/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
value = text2num(trim(str_val)) != 0
return TRUE
@@ -131,6 +138,8 @@
value = list()
/datum/config_entry/number_list/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
str_val = trim(str_val)
var/list/new_list = list()
var/list/values = splittext(str_val," ")
@@ -150,6 +159,8 @@
dupes_allowed = TRUE
/datum/config_entry/keyed_flag_list/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
return ValidateKeyedList(str_val, LIST_MODE_FLAG, " ")
/datum/config_entry/keyed_number_list
@@ -162,6 +173,8 @@
return var_name != "splitter" && ..()
/datum/config_entry/keyed_number_list/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
return ValidateKeyedList(str_val, LIST_MODE_NUM, splitter)
/datum/config_entry/keyed_string_list
@@ -174,6 +187,8 @@
return var_name != "splitter" && ..()
/datum/config_entry/keyed_string_list/ValidateAndSet(str_val)
if(!VASProcCallGuard(str_val))
return FALSE
return ValidateKeyedList(str_val, LIST_MODE_TEXT, splitter)
#undef LIST_MODE_NUM
@@ -97,19 +97,13 @@ GLOBAL_PROTECT(config_dir)
if(!entry)
continue
if(entry == "$include")
if(!value)
log_config("Warning: Invalid $include directive: [value]")
else
LoadEntries(value, stack)
++.
continue
if(entry == "$include")
if(!value)
log_config("Warning: Invalid $include directive: [value]")
else
LoadEntries(value, stack)
++.
continue
var/datum/config_entry/E = _entries[entry]
@@ -2,13 +2,21 @@
protection = CONFIG_ENTRY_HIDDEN
/datum/config_entry/string/comms_key/ValidateAndSet(str_val)
return str_val != "default_pwd" && length(str_val) > 6 && ..()
return str_val != "default_pwd" && length(str_val) > 6 && ..()
/datum/config_entry/keyed_string_list/cross_server
protection = CONFIG_ENTRY_LOCKED
/datum/config_entry/string/cross_server_address/ValidateAndSet(str_val)
return str_val != "byond:\\address:port" && ..()
/datum/config_entry/keyed_string_list/cross_server/ValidateAndSet(str_val)
. = ..()
if(.)
var/list/newv = list()
for(var/I in value)
newv[replacetext(I, "+", " ")] = value[I]
value = newv
/datum/config_entry/keyed_string_list/cross_server/ValidateListEntry(key_name, key_value)
return key_value != "byond:\\address:port" && ..()
/datum/config_entry/string/cross_comms_name
@@ -17,4 +25,4 @@ GLOBAL_VAR_INIT(medals_enabled, TRUE) //will be auto set to false if the game fa
/datum/config_entry/string/medal_hub_address
/datum/config_entry/string/medal_hub_password
protection = CONFIG_ENTRY_HIDDEN
protection = CONFIG_ENTRY_HIDDEN
@@ -2,27 +2,27 @@
/datum/config_entry/keyed_number_list/probability
/datum/config_entry/keyed_number_list/probability/ValidateKeyName(key_name)
/datum/config_entry/keyed_number_list/probability/ValidateListEntry(key_name)
return key_name in config.modes
/datum/config_entry/keyed_number_list/max_pop
/datum/config_entry/keyed_number_list/max_pop/ValidateKeyName(key_name)
/datum/config_entry/keyed_number_list/max_pop/ValidateListEntry(key_name)
return key_name in config.modes
/datum/config_entry/keyed_number_list/min_pop
/datum/config_entry/keyed_number_list/min_pop/ValidateKeyName(key_name)
/datum/config_entry/keyed_number_list/min_pop/ValidateListEntry(key_name, key_value)
return key_name in config.modes
/datum/config_entry/keyed_flag_list/continuous // which roundtypes continue if all antagonists die
/datum/config_entry/keyed_flag_list/continuous/ValidateKeyName(key_name)
/datum/config_entry/keyed_flag_list/continuous/ValidateListEntry(key_name, key_value)
return key_name in config.modes
/datum/config_entry/keyed_flag_list/midround_antag // which roundtypes use the midround antagonist system
/datum/config_entry/keyed_flag_list/midround_antag/ValidateKeyName(key_name)
/datum/config_entry/keyed_flag_list/midround_antag/ValidateListEntry(key_name, key_value)
return key_name in config.modes
/datum/config_entry/keyed_string_list/policy
+1 -1
View File
@@ -394,4 +394,4 @@ SUBSYSTEM_DEF(air)
#undef SSAIR_EXCITEDGROUPS
#undef SSAIR_HIGHPRESSURE
#undef SSAIR_HOTSPOT
#undef SSAIR_SUPERCONDUCTIVITY
#undef SSAIR_SUPERCONDUCTIVITY
+19 -26
View File
@@ -8,11 +8,10 @@ SUBSYSTEM_DEF(blackbox)
var/list/feedback = list() //list of datum/feedback_variable
var/triggertime = 0
var/sealed = FALSE //time to stop tracking stats?
var/list/research_levels = list() //list of highest tech levels attained that isn't lost lost by destruction of RD computers
var/list/versions = list("time_dilation_current" = 2,
"science_techweb_unlock" = 2,
"antagonists" = 3) //associative list of any feedback variables that have had their format changed since creation and their current version, remember to update this
var/list/versions = list("antagonists" = 3,
"admin_secrets_fun_used" = 2,
"time_dilation_current" = 3,
"science_techweb_unlock" = 2) //associative list of any feedback variables that have had their format changed since creation and their current version, remember to update this
/datum/controller/subsystem/blackbox/Initialize()
triggertime = world.time
@@ -59,13 +58,11 @@ SUBSYSTEM_DEF(blackbox)
/datum/controller/subsystem/blackbox/Shutdown()
sealed = FALSE
record_feedback("tally", "ahelp_stats", GLOB.ahelp_tickets.active_tickets.len, "unresolved")
for (var/obj/machinery/message_server/MS in GLOB.message_servers)
for (var/obj/machinery/telecomms/message_server/MS in GLOB.telecomms_list)
if (MS.pda_msgs.len)
record_feedback("tally", "radio_usage", MS.pda_msgs.len, "PDA")
if (MS.rc_msgs.len)
record_feedback("tally", "radio_usage", MS.rc_msgs.len, "request console")
if(research_levels.len)
SSblackbox.record_feedback("associative", "high_research_level", 1, research_levels)
if (!SSdbcore.Connect())
return
@@ -92,39 +89,35 @@ SUBSYSTEM_DEF(blackbox)
sealed = TRUE
return TRUE
/datum/controller/subsystem/blackbox/proc/log_research(tech, level)
if(!(tech in research_levels) || research_levels[tech] < level)
research_levels[tech] = level
/datum/controller/subsystem/blackbox/proc/LogBroadcast(freq)
if(sealed)
return
switch(freq)
if(1459)
if(FREQ_COMMON)
record_feedback("tally", "radio_usage", 1, "common")
if(GLOB.SCI_FREQ)
if(FREQ_SCIENCE)
record_feedback("tally", "radio_usage", 1, "science")
if(GLOB.COMM_FREQ)
if(FREQ_COMMAND)
record_feedback("tally", "radio_usage", 1, "command")
if(GLOB.MED_FREQ)
if(FREQ_MEDICAL)
record_feedback("tally", "radio_usage", 1, "medical")
if(GLOB.ENG_FREQ)
if(FREQ_ENGINEERING)
record_feedback("tally", "radio_usage", 1, "engineering")
if(GLOB.SEC_FREQ)
if(FREQ_SECURITY)
record_feedback("tally", "radio_usage", 1, "security")
if(GLOB.SYND_FREQ)
if(FREQ_SYNDICATE)
record_feedback("tally", "radio_usage", 1, "syndicate")
if(GLOB.SERV_FREQ)
if(FREQ_SERVICE)
record_feedback("tally", "radio_usage", 1, "service")
if(GLOB.SUPP_FREQ)
if(FREQ_SUPPLY)
record_feedback("tally", "radio_usage", 1, "supply")
if(GLOB.CENTCOM_FREQ)
if(FREQ_CENTCOM)
record_feedback("tally", "radio_usage", 1, "centcom")
if(GLOB.AIPRIV_FREQ)
if(FREQ_AI_PRIVATE)
record_feedback("tally", "radio_usage", 1, "ai private")
if(GLOB.REDTEAM_FREQ)
if(FREQ_CTF_RED)
record_feedback("tally", "radio_usage", 1, "CTF red team")
if(GLOB.BLUETEAM_FREQ)
if(FREQ_CTF_BLUE)
record_feedback("tally", "radio_usage", 1, "CTF blue team")
else
record_feedback("tally", "radio_usage", 1, "other")
@@ -194,7 +187,7 @@ Versioning
"gun_fired" = 2)
*/
/datum/controller/subsystem/blackbox/proc/record_feedback(key_type, key, increment, data, overwrite)
if(sealed || !key_type || !istext(key) || !isnum(increment || !data))
if(sealed || !key_type || !istext(key) || !isnum(increment) || !data)
return
var/datum/feedback_variable/FV = find_feedback_datum(key, key_type)
switch(key_type)
@@ -41,4 +41,3 @@ SUBSYSTEM_DEF(idlenpcpool)
SA.consider_wakeup()
if (MC_TICK_CHECK)
return
+92 -1
View File
@@ -1,10 +1,101 @@
SUBSYSTEM_DEF(input)
name = "Input"
wait = 1 //SS_TICKER means this runs every tick
flags = SS_TICKER | SS_NO_INIT
init_order = INIT_ORDER_INPUT
flags = SS_TICKER
priority = FIRE_PRIORITY_INPUT
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
var/list/macro_sets
/datum/controller/subsystem/input/Initialize()
setup_default_macro_sets()
initialized = TRUE
refresh_client_macro_sets()
return ..()
// This is for when macro sets are eventualy datumized
/datum/controller/subsystem/input/proc/setup_default_macro_sets()
var/list/static/default_macro_sets
if(default_macro_sets)
macro_sets = default_macro_sets
return
default_macro_sets = list(
"default" = list(
"Tab" = "\".winset \\\"input.focus=true?map.focus=true input.background-color=[COLOR_INPUT_DISABLED]:input.focus=true input.background-color=[COLOR_INPUT_ENABLED]\\\"\"",
"O" = "ooc",
"T" = "say",
"M" = "me",
"Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"", // This makes it so backspace can remove default inputs
"Any" = "\"KeyDown \[\[*\]\]\"",
"Any+UP" = "\"KeyUp \[\[*\]\]\"",
),
"old_default" = list(
"Tab" = "\".winset \\\"mainwindow.macro=old_hotkeys map.focus=true input.background-color=[COLOR_INPUT_DISABLED]\\\"\"",
"Ctrl+T" = "say",
"Ctrl+O" = "ooc",
),
"old_hotkeys" = list(
"Tab" = "\".winset \\\"mainwindow.macro=old_default input.focus=true input.background-color=[COLOR_INPUT_ENABLED]\\\"\"",
"O" = "ooc",
"T" = "say",
"M" = "me",
"Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"", // This makes it so backspace can remove default inputs
"Any" = "\"KeyDown \[\[*\]\]\"",
"Any+UP" = "\"KeyUp \[\[*\]\]\"",
),
)
// Because i'm lazy and don't want to type all these out twice
var/list/old_default = default_macro_sets["old_default"]
var/list/static/oldmode_keys = list(
"North", "East", "South", "West",
"Northeast", "Southeast", "Northwest", "Southwest",
"Insert", "Delete", "Ctrl", "Alt",
"F1", "F2", "F5", "F6", "F7", "F8", "F12",
)
for(var/i in 1 to oldmode_keys.len)
var/key = oldmode_keys[i]
old_default[key] = "\"KeyDown [key]\""
old_default["[key]+UP"] = "\"KeyUp [key]\""
var/list/static/oldmode_ctrl_override_keys = list(
"W" = "W", "A" = "A", "S" = "S", "D" = "D", // movement
"1" = "1", "2" = "2", "3" = "3", "4" = "4", // intent
"B" = "B", // resist
"E" = "E", // quick equip
"F" = "F", // intent left
"G" = "G", // intent right
"H" = "H", // stop pulling
"Q" = "Q", // drop
"R" = "R", // throw
"X" = "X", // switch hands
"Y" = "Y", // activate item
"Z" = "Z", // activate item
)
for(var/i in 1 to oldmode_ctrl_override_keys.len)
var/key = oldmode_ctrl_override_keys[i]
var/override = oldmode_ctrl_override_keys[key]
old_default["Ctrl+[key]"] = "\"KeyDown [override]\""
old_default["Ctrl+[key]+UP"] = "\"KeyUp [override]\""
macro_sets = default_macro_sets
// Badmins just wanna have fun ♪
/datum/controller/subsystem/input/proc/refresh_client_macro_sets()
var/list/clients = GLOB.clients
for(var/i in 1 to clients.len)
var/client/user = clients[i]
user.set_macros()
/datum/controller/subsystem/input/fire()
var/list/clients = GLOB.clients // Let's sing the list cache song
for(var/i in 1 to clients.len)
+2 -13
View File
@@ -107,9 +107,6 @@ SUBSYSTEM_DEF(job)
if(player.mind && job.title in player.mind.restricted_roles)
Debug("FOC incompatible with antagonist role, Player: [player]")
continue
if(CONFIG_GET(flag/enforce_human_authority) && !player.client.prefs.pref_species.qualifies_for_rank(job.title, player.client.prefs.features))
Debug("FOC non-human failed, Player: [player]")
continue
if(player.client.prefs.GetJobDepartment(job, level) & job.flag)
Debug("FOC pass, Player: [player], Level:[level]")
candidates += player
@@ -144,11 +141,6 @@ SUBSYSTEM_DEF(job)
Debug("GRJ incompatible with antagonist role, Player: [player], Job: [job.title]")
continue
if(CONFIG_GET(flag/enforce_human_authority) && !player.client.prefs.pref_species.qualifies_for_rank(job.title, player.client.prefs.features))
Debug("GRJ non-human failed, Player: [player]")
continue
if((job.current_positions < job.spawn_positions) || job.spawn_positions == -1)
Debug("GRJ Random job given, Player: [player], Job: [job]")
if(AssignRole(player, job.title))
@@ -319,10 +311,6 @@ SUBSYSTEM_DEF(job)
Debug("DO incompatible with antagonist role, Player: [player], Job:[job.title]")
continue
if(CONFIG_GET(flag/enforce_human_authority) && !player.client.prefs.pref_species.qualifies_for_rank(job.title, player.client.prefs.features))
Debug("DO non-human failed, Player: [player], Job:[job.title]")
continue
// If the player wants that job on this level, then try give it to him.
if(player.client.prefs.GetJobDepartment(job, level) & job.flag)
@@ -424,7 +412,8 @@ SUBSYSTEM_DEF(job)
if(job && H)
job.after_spawn(H, M)
handle_roundstart_items(H, M.ckey, H.mind.assigned_role, H.mind.special_role)
handle_roundstart_items(H, M.ckey, H.mind.assigned_role, H.mind.special_role) //CIT CHANGE - makes donators spawn with their items
return H
+1 -1
View File
@@ -22,7 +22,7 @@ SUBSYSTEM_DEF(lighting)
create_all_lighting_objects()
initialized = TRUE
fire(FALSE, TRUE)
..()
+1 -1
View File
@@ -62,4 +62,4 @@ SUBSYSTEM_DEF(machines)
if (istype(SSmachines.processing))
processing = SSmachines.processing
if (istype(SSmachines.powernets))
powernets = SSmachines.powernets
powernets = SSmachines.powernets
+2 -2
View File
@@ -135,12 +135,12 @@ SUBSYSTEM_DEF(npcpool)
if(facCount == 1 && helpProb)
helpProb = 100
if(prob(helpProb) && candidate.takeDelegate(check,FALSE))
--canBeUsed.len
candidate.eye_color = "yellow"
candidate.update_icons()
if(!currentrun.len || MC_TICK_CHECK) //don't change SS state if it isn't necessary
return
+2
View File
@@ -40,3 +40,5 @@ SUBSYSTEM_DEF(orbit)
O.Check(targetloc)
if (MC_TICK_CHECK)
return
+1 -1
View File
@@ -250,4 +250,4 @@ SUBSYSTEM_DEF(persistence)
var/list/file_data = list()
file_data["data"] = saved_modes
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
WRITE_FILE(json_file, json_encode(file_data))
+3 -16
View File
@@ -14,35 +14,22 @@ SUBSYSTEM_DEF(radio)
/datum/controller/subsystem/radio/proc/add_object(obj/device, new_frequency as num, filter = null as text|null)
var/f_text = num2text(new_frequency)
var/datum/radio_frequency/frequency = frequencies[f_text]
if(!frequency)
frequency = new
frequency.frequency = new_frequency
frequencies[f_text] = frequency
frequencies[f_text] = frequency = new(new_frequency)
frequency.add_listener(device, filter)
return frequency
/datum/controller/subsystem/radio/proc/remove_object(obj/device, old_frequency)
var/f_text = num2text(old_frequency)
var/datum/radio_frequency/frequency = frequencies[f_text]
if(frequency)
frequency.remove_listener(device)
if(frequency.devices.len == 0)
qdel(frequency)
frequencies -= f_text
// let's don't delete frequencies in case a non-listener keeps a reference
return 1
/datum/controller/subsystem/radio/proc/return_frequency(new_frequency as num)
var/f_text = num2text(new_frequency)
var/datum/radio_frequency/frequency = frequencies[f_text]
if(!frequency)
frequency = new
frequency.frequency = new_frequency
frequencies[f_text] = frequency
frequencies[f_text] = frequency = new(new_frequency)
return frequency
+1
View File
@@ -54,6 +54,7 @@ SUBSYSTEM_DEF(research)
bitcoins = single_server_income
break //Just need one to work.
var/income_time_difference = world.time - last_income
science_tech.last_bitcoins = bitcoins // Doesn't take tick drift into account
bitcoins *= income_time_difference / 10
science_tech.research_points += bitcoins
last_income = world.time
+6 -5
View File
@@ -54,7 +54,7 @@ SUBSYSTEM_DEF(shuttle)
var/lockdown = FALSE //disallow transit after nuke goes off
var/auto_call = 99000 //time before in deciseconds in which the shuttle is auto called. Default is 2½ hours plus 15 for the shuttle. So total is 3.
var/auto_call = 99000 //CIT CHANGE - time before in deciseconds in which the shuttle is auto called. Default is 2½ hours plus 15 for the shuttle. So total is 3.
/datum/controller/subsystem/shuttle/Initialize(timeofday)
if(!arrivals)
@@ -158,7 +158,7 @@ SUBSYSTEM_DEF(shuttle)
break
/datum/controller/subsystem/shuttle/proc/CheckAutoEvac()
if(emergencyNoEscape || emergencyNoRecall || !emergency)
if(emergencyNoEscape || emergencyNoRecall || !emergency || !SSticker.HasRoundStarted())
return
var/threshold = CONFIG_GET(number/emergency_shuttle_autocall_threshold)
@@ -262,7 +262,7 @@ SUBSYSTEM_DEF(shuttle)
if(!admiral_message)
admiral_message = pick(GLOB.admiral_messages)
var/intercepttext = "<font size = 3><b>NanoTrasen Update</b>: Request For Shuttle.</font><hr>\
var/intercepttext = "<font size = 3><b>Nanotrasen Update</b>: Request For Shuttle.</font><hr>\
To whom it may concern:<br><br>\
We have taken note of the situation upon [station_name()] and have come to the \
conclusion that it does not warrant the abandonment of the station.<br>\
@@ -387,7 +387,7 @@ SUBSYSTEM_DEF(shuttle)
emergency.setTimer(emergencyDockTime)
priority_announce("Hostile environment resolved. \
You have 3 minutes to board the Emergency Shuttle.",
null, 'sound/AI/shuttledock.ogg', "Priority")
null, 'sound/ai/shuttledock.ogg', "Priority")
//try to move/request to dockHome if possible, otherwise dockAway. Mainly used for admin buttons
/datum/controller/subsystem/shuttle/proc/toggleShuttle(shuttleId, dockHome, dockAway, timed)
@@ -431,7 +431,8 @@ SUBSYSTEM_DEF(shuttle)
if(!(M in transit_requesters))
transit_requesters += M
/datum/controller/subsystem/shuttle/proc/autoEnd()
/datum/controller/subsystem/shuttle/proc/autoEnd() //CIT CHANGE - allows shift to end after 3 hours has passed.
if(world.time > auto_call && EMERGENCY_IDLE_OR_RECALLED) //3 hours
SSshuttle.emergency.request(null, 1.5)
priority_announce("The shift has come to an end and the shuttle called.")
+1 -1
View File
@@ -1,6 +1,6 @@
// The Squeak
// because this is about placement of mice mobs, and nothing to do with
// mice - the computer peripheral
// mice - the computer peripheral
SUBSYSTEM_DEF(squeak)
name = "Squeak"
+1 -1
View File
@@ -27,6 +27,6 @@ SUBSYSTEM_DEF(stickyban)
ban["existing_user_matches_this_round"] = list()
ban["admin_matches_this_round"] = list()
cache[ckey] = ban
for (var/bannedckey in cache)
world.SetConfig("ban", bannedckey, list2stickyban(cache[bannedckey]))
+1
View File
@@ -631,6 +631,7 @@ SUBSYSTEM_DEF(ticker)
world.Reboot()
/datum/controller/subsystem/ticker/Shutdown()
gather_newscaster() //called here so we ensure the log is created even upon admin reboot
if(!round_end_sound)
round_end_sound = pick(\
'sound/roundend/newroundsexy.ogg',
+1 -1
View File
@@ -35,4 +35,4 @@ SUBSYSTEM_DEF(time_track)
last_tick_realtime = current_realtime
last_tick_byond_time = current_byondtime
last_tick_tickcount = current_tickcount
SSblackbox.record_feedback("associative", "time_dilation_current", 1, list("[time_dilation_current]" = "[SQLtime()]"))
SSblackbox.record_feedback("associative", "time_dilation_current", 1, list("[SQLtime()]" = list("current" = "[time_dilation_current]", "avg_fast" = "[time_dilation_avg_fast]", "avg" = "[time_dilation_avg]", "avg_slow" = "[time_dilation_avg_slow]")))
+1 -1
View File
@@ -411,4 +411,4 @@ SUBSYSTEM_DEF(timer)
#undef BUCKET_LEN
#undef BUCKET_POS
#undef BUCKET_POS