diff --git a/code/__HELPERS/maths.dm b/code/__HELPERS/maths.dm index 8f2c0425671..8ce74747102 100644 --- a/code/__HELPERS/maths.dm +++ b/code/__HELPERS/maths.dm @@ -13,6 +13,11 @@ var/list/sqrtTable = list(1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 10) */ + +// Returns y so that y/x = a/b. +#define RULE_OF_THREE(a, b, x) ((a*x)/b) +#define tan(x) (sin(x)/cos(x)) + /proc/Atan2(x, y) if (!x && !y) return 0 @@ -35,6 +40,20 @@ proc/arctan(x) else return 0 +// -- Returns a Lorentz-distributed number. +// -- The probability density function has centre x0 and width s. + +/proc/lorentz_distribution(var/x0, var/s) + var/x = rand() + var/y = s*tan_rad(PI*(x-0.5)) + x0 + return y + +// -- Returns the Lorentz cummulative distribution of the real x. + +/proc/lorentz_cummulative_distribution(var/x, var/x0, var/s) + var/y = (1/PI)*ToRadians(arctan((x-x0)/s)) + 1/2 + return y + //Moved to macros.dm to reduce pure calling overhead, this was being called shitloads, like, most calls of all procs. /* /proc/Clamp(const/val, const/min, const/max) @@ -188,9 +207,13 @@ proc/arctan(x) /* * Tangent. */ -/proc/Tan(const/x) +/proc/Tan(const/x) return sin(x) / cos(x) +/proc/tan_rad(const/x) // This one assumes that x is in radians. + return Tan(ToDegrees(x)) + + /proc/ToDegrees(const/radians) // 180 / Pi return radians * 57.2957795 diff --git a/code/datums/gamemode/dynamic/dynamic.dm b/code/datums/gamemode/dynamic/dynamic.dm index 003493a66d7..86bd810e8da 100644 --- a/code/datums/gamemode/dynamic/dynamic.dm +++ b/code/datums/gamemode/dynamic/dynamic.dm @@ -9,8 +9,47 @@ var/list/threat_by_job = list( "Detective" = 3, ) +var/dynamic_curve_centre = 0 +var/dynamic_curve_width = 1.8 + #define BASE_SOLO_REFUND 10 +/proc/lorentz2threat(var/x) + var/y + switch (x) + // Left end of the tail, the lowest bound is -inf. + // 0 to 10. + if (-INFINITY to -20) + y = rand(0, 10) + // Porportional conversion from the lorentz variable to the threa. + + // First, we use a rule of three to get a number from -40 to -30. + // Then we shift it by 50 to get a number from 10 to 20. + // The same process is done for other intervalls. + if (-20 to -10) + y = RULE_OF_THREE(-40, -20, x) + 50 + if (-10 to -5) + y = RULE_OF_THREE(-30, -10, x) + 50 + if (-5 to -2.5) + y = RULE_OF_THREE(-20, -5, x) + 50 + if (-2.5 to -0) + y = RULE_OF_THREE(-10, -2.5, x) + 50 + if (0 to 2.5) + y = RULE_OF_THREE(10, 2.5, x) + 50 + if (2.5 to 5) + y = RULE_OF_THREE(20, 5, x) + 50 + if (5 to 10) + y = RULE_OF_THREE(30, 10, x) + 50 + if (10 to 20) + y = RULE_OF_THREE(40, 20, x) + 50 + + // Right end of the tail, higher bound is +inf. + + if (20 to INFINITY) + y = rand(90, 100) + + return y + /datum/gamemode/dynamic name = "Dynamic Mode" @@ -43,11 +82,19 @@ var/list/threat_by_job = list( var/datum/stat/dynamic_mode/dynamic_stats = null var/pop_last_updated = 0 + var/relative_threat = 0 // Relative threat, Lorentz-distributed. + var/curve_centre_of_round = 0 + var/curve_width_of_round = 1.8 + + var/peaceful_percentage = 50 + /datum/gamemode/dynamic/AdminPanelEntry() var/dat = list() dat += "Dynamic Mode \[VV\]
" dat += "Threat Level: [threat_level]
" dat += "Threat to Spend: [threat] \[Adjust\] \[View Log\]
" + dat += "Parameters: centre = [curve_centre_of_round] ; width = [curve_width_of_round].
" + dat += "On average, [peaceful_percentage]% of the rounds are more peaceful.
" dat += "Executed rulesets: " if (executed_rules.len > 0) dat += "
" @@ -112,13 +159,30 @@ var/list/threat_by_job = list( send2maindiscord("Dynamic mode threat: **[threat_level]**, rulesets: [jointext(rules, ", ")]") /datum/gamemode/dynamic/can_start() - threat_level = rand(1,100)*0.6 + rand(1,100)*0.4//https://docs.google.com/spreadsheets/d/1QLN_OBHqeL4cm9zTLEtxlnaJHHUu0IUPzPbsI-DFFmc/edit#gid=499381388 + + // Old equation. + //threat_level = rand(1,100)*0.6 + rand(1,100)*0.4//https://docs.google.com/spreadsheets/d/1QLN_OBHqeL4cm9zTLEtxlnaJHHUu0IUPzPbsI-DFFmc/edit#gid=499381388 + + // New equation : https://docs.google.com/spreadsheets/d/1qnQm5hDdwZoyVmBCtf6-jwwHKEaCnYa3ljmYPs7gkSE/edit#gid=0 + relative_threat = lorentz_distribution(dynamic_curve_centre, dynamic_curve_width) + threat_level = lorentz2threat(relative_threat) + threat = round(threat, 0.1) + peaceful_percentage = round(lorentz_cummulative_distribution(relative_threat, curve_centre_of_round, curve_width_of_round), 0.01)*100 + + curve_centre_of_round = dynamic_curve_centre + curve_width_of_round = dynamic_curve_width + threat = threat_level starting_threat = threat_level + latejoin_injection_cooldown = rand(330,510) midround_injection_cooldown = rand(600,1050) message_admins("Dynamic Mode initialized with a Threat Level of... [threat_level]!") log_admin("Dynamic Mode initialized with a Threat Level of... [threat_level]!") + + message_admins("Parameters were: centre = [curve_centre_of_round], width = [curve_width_of_round].") + log_admin("Parameters were: centre = [curve_centre_of_round], width = [curve_width_of_round].") + if (config.high_population_override) message_admins("High Population Override is in effect! Threat Level will have more impact on which roles will appear, and player population less.") log_admin("High Population Override is in effect! Threat Level will have more impact on which roles will appear, and player population less.") diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 8008692a49b..8e53790d8a8 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -687,6 +687,9 @@ var/global/floorIsLava = 0 for(var/datum/dynamic_ruleset/roundstart/rule in forced_roundstart_ruleset) dat += {"-> [rule.name] <-
"} dat += "(Clear Rulesets)
" + dat += "Parameters:
" + dat += {"Curve centre: -> [dynamic_curve_centre] <-
"} + dat += {"Curve width: -> [dynamic_curve_width] <-
"} else dat += "(Force Next Latejoin Ruleset)
" if (ticker && ticker.mode && istype(ticker.mode,/datum/gamemode/dynamic)) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index a8518ef11b3..439ae945096 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1557,6 +1557,39 @@ mode.picking_specific_rule(midround_rules[added_rule],1) + else if(href_list["f_dynamic_roundstart_centre"]) + if(!check_rights(R_ADMIN)) + return + + if(ticker && ticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(master_mode != "Dynamic Mode") + return alert(usr, "The game mode has to be Dynamic Mode!", null, null, null, null) + + var/new_centre = input(usr,"Change the centre of the dynamic mode threat curve. A negative value will give a more peaceful round ; a positive value, a round with higher threat. Any number between -5 and +5 is allowed.", "Change curve centre", null) as num + if (new_centre < -5 || new_centre > 5) + return alert(usr, "Only values between -5 and +5 are allowed.", null, null, null, null) + + log_admin("[key_name(usr)] changed the distribution curve center to [new_centre].") + message_admins("[key_name(usr)] changed the distribution curve center to [new_centre]", 1) + dynamic_curve_centre = new_centre + + else if(href_list["f_dynamic_roundstart_width"]) + if(!check_rights(R_ADMIN)) + return + + if(ticker && ticker.mode) + return alert(usr, "The game has already started.", null, null, null, null) + if(master_mode != "Dynamic Mode") + return alert(usr, "The game mode has to be Dynamic Mode!", null, null, null, null) + + var/new_width = input(usr,"Change the width of the dynamic mode threat curve. A higher value will favour extreme rounds ; a lower value, a round closer to the average. Any Number between 0.5 and 4 are allowed.", "Change curve width", null) as num + if (new_width < 0.5 || new_width > 4) + return alert(usr, "Only values between 0.5 and +2.5 are allowed.", null, null, null, null) + + log_admin("[key_name(usr)] changed the distribution curve width to [new_width].") + message_admins("[key_name(usr)] changed the distribution curve width to [new_width]", 1) + dynamic_curve_width = new_width else if(href_list["c_mode2"]) if(!check_rights(R_ADMIN|R_SERVER))