diff --git a/.github/workflows/generate_documentation.yml b/.github/workflows/generate_documentation.yml index 6075e3029acd..59e6d1144c68 100644 --- a/.github/workflows/generate_documentation.yml +++ b/.github/workflows/generate_documentation.yml @@ -21,7 +21,7 @@ jobs: run: | ~/dmdoc touch dmdoc/.nojekyll - echo codedocs.tgstation13.org > dmdoc/CNAME + echo docs.cm-ss13.com > dmdoc/CNAME - name: Deploy uses: JamesIves/github-pages-deploy-action@3.7.1 with: diff --git a/code/__DEFINES/conflict.dm b/code/__DEFINES/conflict.dm index 09be4793c552..141d69c69b11 100644 --- a/code/__DEFINES/conflict.dm +++ b/code/__DEFINES/conflict.dm @@ -232,6 +232,7 @@ //OB timings #define OB_TRAVEL_TIMING 12 SECONDS #define OB_CRASHING_DOWN 1 SECONDS +#define OB_CLUSTER_DURATION 45 SECONDS //================================================= //Health of various items diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 3c2527136ed6..662bcb458c55 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -113,7 +113,8 @@ #define SS_INIT_INPUT 85 #define SS_INIT_FAIL_TO_TOPIC 84 #define SS_INIT_TOPIC 83 -#define SS_INIT_RUST 26 +#define SS_INIT_RUST 30 +#define SS_INIT_INFLUXDRIVER 28 #define SS_INIT_SUPPLY_SHUTTLE 25 #define SS_INIT_GARBAGE 24 #define SS_INIT_EVENTS 23.5 @@ -133,7 +134,9 @@ #define SS_INIT_MORE_INIT 16 #define SS_INIT_AIR 15 #define SS_INIT_TELEPORTER 13 -#define SS_INIT_LIGHTING 12 +#define SS_INIT_INFLUXMCSTATS 12 +#define SS_INIT_INFLUXSTATS 11 +#define SS_INIT_LIGHTING 10 #define SS_INIT_DEFCON 9 #define SS_INIT_LAW 6 #define SS_INIT_FZ_TRANSITIONS 5 @@ -212,12 +215,15 @@ #define SS_PRIORITY_UNSPECIFIED 30 #define SS_PRIORITY_PROCESS 25 #define SS_PRIORITY_SOUNDSCAPE 24 +#define SS_PRIORITY_INFLUXDRIVER 23 #define SS_PRIORITY_PAGER_STATUS 22 #define SS_PRIORITY_LIGHTING 20 #define SS_PRIORITY_TRACKING 19 +#define SS_PRIORITY_DATABASE 15 #define SS_PRIORITY_MINIMAPS 11 #define SS_PRIORITY_PING 10 -#define SS_PRIORITY_DATABASE 15 +#define SS_PRIORITY_INFLUXMCSTATS 9 +#define SS_PRIORITY_INFLUXSTATS 8 #define SS_PRIORITY_PLAYTIME 5 #define SS_PRIORITY_PERFLOGGING 4 #define SS_PRIORITY_CORPSESPAWNER 3 diff --git a/code/__DEFINES/weapon_stats.dm b/code/__DEFINES/weapon_stats.dm index 58dad90b0710..bef8413e9615 100644 --- a/code/__DEFINES/weapon_stats.dm +++ b/code/__DEFINES/weapon_stats.dm @@ -1,7 +1,7 @@ #define HUMAN_UNIVERSAL_DAMAGEMULT 1 #define RECOIL_BUILDUP_VIEWPUNCH_MULTIPLIER 0.1 - +#define BASE_VELOCITY_BONUS 0 #define PROJ_BASE_ACCURACY_MULT 0.01 #define PROJ_BASE_DAMAGE_MULT 0.01 diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 83da41b515a6..d4d9eb320633 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -164,6 +164,11 @@ t = "0[t]" return t +/proc/pad_trailing(text, padding, size) + while (length(text) < size) + text = "[text][padding]" + return text + //Adds 'u' number of spaces ahead of the text 't' /proc/add_lspace(t, u) while(length(t) < u) diff --git a/code/_globalvars/global_lists.dm b/code/_globalvars/global_lists.dm index ecbeeb532f54..9f748dad35cf 100644 --- a/code/_globalvars/global_lists.dm +++ b/code/_globalvars/global_lists.dm @@ -12,6 +12,7 @@ GLOBAL_LIST_EMPTY(fax_contents) //List of fax contents to maintain it even if so GLOBAL_LIST_EMPTY(canvas_drawings) //List of canvas drawings GLOBAL_LIST_EMPTY(failed_fultons) //A list of fultoned items which weren't collected and fell back down +GLOBAL_LIST_EMPTY(larva_burst_by_hive) GLOBAL_LIST_INIT_TYPED(custom_huds_list, /datum/custom_hud, setup_all_huds()) GLOBAL_LIST_INIT_TYPED(custom_human_huds, /datum/custom_hud, setup_human_huds()) diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 29dd0a88d3e0..7988ff6d1a95 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -529,6 +529,25 @@ This maintains a list of ip addresses that are able to bypass topic filtering. /datum/config_entry/string/round_results_webhook_url +/// InfluxDB v2 Host to connect to for sending statistics (over HTTP API) +/datum/config_entry/string/influxdb_host +/// InfluxDB v2 Bucket to send staistics to +/datum/config_entry/string/influxdb_bucket +/// InfluxDB v2 Organization to access buckets of +/datum/config_entry/string/influxdb_org +/// InfluxDB v2 API Token to access the organization and bucket +/datum/config_entry/string/influxdb_token + +/// How often to snapshot general game statistics to influxdb driver +/datum/config_entry/number/influxdb_stats_period + config_entry_value = 30 +/// How often to snapshot MC statistics +/datum/config_entry/number/influxdb_mcstats_period + config_entry_value = 60 +/// How often to send queued influxdb statistics +/datum/config_entry/number/influxdb_send_period + config_entry_value = 10 + /// logs all timers in buckets on automatic bucket reset (Useful for timer debugging) /datum/config_entry/flag/log_timers_on_bucket_reset diff --git a/code/controllers/subsystem/influxdriver.dm b/code/controllers/subsystem/influxdriver.dm new file mode 100644 index 000000000000..7e5289dfc518 --- /dev/null +++ b/code/controllers/subsystem/influxdriver.dm @@ -0,0 +1,132 @@ +/// Sends collected statistics to an influxdb v2 backend periodically +SUBSYSTEM_DEF(influxdriver) + name = "InfluxDB Driver" + wait = 10 SECONDS + init_order = SS_INIT_INFLUXDRIVER + priority = SS_PRIORITY_INFLUXDRIVER + runlevels = RUNLEVELS_DEFAULT|RUNLEVEL_LOBBY + + var/list/send_queue = list() + + /// Maximum amount of metric lines to send at most in one request + /// This is neccessary because sending a lot of metrics can get expensive + /// and drive the subsystem into overtime, but we can't split the work as it'd be even less efficient + var/max_batch = 150 + + /// Last timestamp in microseconds + var/timestamp_cache_realtime + /// Last tick time the timestamp was taken at + var/timestamp_cache_worldtime + +/datum/controller/subsystem/influxdriver/Initialize() + var/period = text2num(CONFIG_GET(number/influxdb_send_period)) + if(isnum(period)) + wait = max(period * (1 SECONDS), 2 SECONDS) + return SS_INIT_SUCCESS + +/datum/controller/subsystem/influxdriver/stat_entry(msg) + msg += "period=[wait] queue=[length(send_queue)]" + return ..() + +/datum/controller/subsystem/influxdriver/proc/unix_timestamp_string() // pending change to rust-g + return RUSTG_CALL(RUST_G, "unix_timestamp")() + +/datum/controller/subsystem/influxdriver/proc/update_timestamp() + PRIVATE_PROC(TRUE) + // We make only one request to rustg per game tick, so we cache the result per world.time + var/whole_timestamp = unix_timestamp_string() // Format "7129739474.4758981" - timestamp with up to 7-8 decimals + var/list/tsparts = splittext(whole_timestamp, ".") + var/fractional = copytext(pad_trailing(tsparts[2], "0", 6), 1, 7) // in microseconds + timestamp_cache_worldtime = world.time + timestamp_cache_realtime = "[tsparts[1]][fractional]" + +/datum/controller/subsystem/influxdriver/fire(resumed) + var/maxlen = min(length(send_queue)+1, max_batch) + var/list/queue = send_queue.Copy(1, maxlen) + send_queue.Cut(1, maxlen) + flush_queue(queue) + +/// Flushes measurements batch to InfluxDB backend +/datum/controller/subsystem/influxdriver/proc/flush_queue(list/queue) + PRIVATE_PROC(TRUE) + + var/host = CONFIG_GET(string/influxdb_host) + var/token = CONFIG_GET(string/influxdb_token) + var/bucket = CONFIG_GET(string/influxdb_bucket) + var/org = CONFIG_GET(string/influxdb_org) + + if(!host || !token || !bucket || !org) + can_fire = FALSE + return + + if(!length(queue)) + return // Nothing to do + + var/url = "[host]/api/v2/write?org=[org]&bucket=[bucket]&precision=us" // microseconds + var/list/headers = list() + headers["Authorization"] = "Token [token]" + headers["Content-Type"] = "text/plain; charset=utf-8" + headers["Accept"] = "application/json" + + var/datum/http_request/request = new + var/payload = "" + for(var/line in queue) + payload += "[line]\n" + request.prepare(RUSTG_HTTP_METHOD_POST, url, payload, headers) + request.begin_async() + // TODO possibly check back result of request later + +/// Enqueues sending to InfluxDB Backend selected measurement values - round_id and timestamp are filled in automatically +/datum/controller/subsystem/influxdriver/proc/enqueue_stats(measurement, list/tags, list/fields) + . = FALSE + var/valid = FALSE + var/serialized = "[measurement],round_id=[GLOB.round_id]" + if(tags) + for(var/tag in tags) + var/serialized_tag = serialize_field(tag, tags[tag]) + if(serialized_tag) + serialized += ",[serialized_tag]" + serialized += " " + var/comma = "" + for(var/field in fields) + var/serialized_field = serialize_field(field, fields[field]) + if(serialized_field) + valid = TRUE + serialized += "[comma][serialized_field]" + comma = "," + if(!valid) + CRASH("Attempted to serialize to InfluxDB backend an invalid measurement (likely has no fields)") + if(timestamp_cache_worldtime != world.time) + update_timestamp() + serialized += " [timestamp_cache_realtime]" + send_queue += serialized + return TRUE + +/// Enqueues sending varied stats in a dumb and simpler format directly as: measurement count= +/datum/controller/subsystem/influxdriver/proc/enqueue_stats_crude(measurement, value, field_name = "count") + . = FALSE + var/serialized_field = serialize_field(field_name, value) + if(!length(serialized_field)) + return + if(timestamp_cache_worldtime != world.time) + update_timestamp() + var/serialized = "[measurement],round_id=[GLOB.round_id] [serialized_field] [timestamp_cache_realtime]" + send_queue += serialized + return TRUE + +/// Puts a single field or tag value into InfluxDB Line format +/datum/controller/subsystem/influxdriver/proc/serialize_field(field, value) + var/static/regex/whitelistedCharacters = regex(@{"([^a-zA-Z0-9_]+)"}, "g") + var/sanitized_field = whitelistedCharacters.Replace("[field]", "") + if(!length(sanitized_field) || copytext(sanitized_field, 1, 2) == "_") + CRASH("Invalid tag/field for InfluxDB serialization: '[sanitized_field]' (original: '[field]')") + var/sanitized_value + if(isnum(value)) + sanitized_value = value + else if(istext(value)) + sanitized_value = whitelistedCharacters.Replace("[value]", "") + if(!length(sanitized_value) || copytext(sanitized_value, 1, 2) == "_") + CRASH("Invalid value for InfluxDB serialization: '[sanitized_value]' (original: '[value]')") + else + CRASH("Invalid value type passed for InfluxDB serialization: '[value]'") + return "[sanitized_field]=[sanitized_value]" diff --git a/code/controllers/subsystem/influxmcstats.dm b/code/controllers/subsystem/influxmcstats.dm new file mode 100644 index 000000000000..a1bf171d81a3 --- /dev/null +++ b/code/controllers/subsystem/influxmcstats.dm @@ -0,0 +1,47 @@ +SUBSYSTEM_DEF(influxmcstats) + name = "InfluxDB MC Stats" + wait = 60 SECONDS + priority = SS_PRIORITY_INFLUXMCSTATS + init_order = SS_INIT_INFLUXMCSTATS + runlevels = RUNLEVEL_LOBBY|RUNLEVELS_DEFAULT + flags = SS_KEEP_TIMING + var/checkpoint = 0 + var/list/subsystem_name_cache = list() + +/datum/controller/subsystem/influxmcstats/Initialize() + var/period = text2num(CONFIG_GET(number/influxdb_mcstats_period)) + if(isnum(period)) + wait = max(period * (1 SECONDS), 10 SECONDS) + return SS_INIT_SUCCESS + +/datum/controller/subsystem/influxmcstats/stat_entry(msg) + msg += "period=[wait] checkpoint=[checkpoint]" + return ..() + +/datum/controller/subsystem/influxmcstats/fire(resumed) + if(!SSinfluxdriver.can_fire) + can_fire = FALSE + return + + var/list/data = list() + data["time_dilation_current"] = SStime_track.time_dilation_current + data["time_dilation_avg"] = SStime_track.time_dilation_avg + data["time_dilation_avg_slow"] = SStime_track.time_dilation_avg_slow + data["time_dilation_avg_fast"] = SStime_track.time_dilation_avg_fast + SSinfluxdriver.enqueue_stats("tidi", null, data) + + SSinfluxdriver.enqueue_stats("cpu", null, list("cpu" = world.cpu, "map_cpu" = world.map_cpu)) + + var/static/regex/get_last_path_element = regex(@{"/([^/]+)$"}) + checkpoint++ + for(var/datum/controller/subsystem/SS in Master.subsystems) + if(!SS.can_fire) + continue + if(!subsystem_name_cache[SS.type]) + get_last_path_element.Find("[SS.type]") + subsystem_name_cache[SS.type] = "SS[get_last_path_element.group[1]]" + var/SSname = subsystem_name_cache[SS.type] + if(!SSname) + stack_trace("Influx MC Stats couldnt name a subsystem, type=[SS.type]") + continue + SSinfluxdriver.enqueue_stats("sstimings", list("ss" = SSname), list("cost" = SS.cost, "tick_overrun" = SS.tick_overrun, "tick_usage" = SS.tick_usage, "wait" = SS.wait)) diff --git a/code/controllers/subsystem/influxstats.dm b/code/controllers/subsystem/influxstats.dm new file mode 100644 index 000000000000..01015b83191d --- /dev/null +++ b/code/controllers/subsystem/influxstats.dm @@ -0,0 +1,156 @@ +/// Sends generic round running statistics to the InfluxDB backend +SUBSYSTEM_DEF(influxstats) + name = "InfluxDB Game Stats" + wait = 60 SECONDS + priority = SS_PRIORITY_INFLUXSTATS + init_order = SS_INIT_INFLUXSTATS + runlevels = RUNLEVEL_LOBBY|RUNLEVELS_DEFAULT + flags = SS_KEEP_TIMING + + var/checkpoint = 0 //! Counter of data snapshots sent + var/step = 1 //! Current task in progress, for pausing/resuming + +/datum/controller/subsystem/influxstats/Initialize() + var/period = text2num(CONFIG_GET(number/influxdb_stats_period)) + if(isnum(period)) + wait = max(period * (1 SECONDS), 10 SECONDS) + return SS_INIT_SUCCESS + +/datum/controller/subsystem/influxstats/stat_entry(msg) + msg += "period=[wait] checkpoint=[checkpoint] step=[step]" + return ..() + +/datum/controller/subsystem/influxstats/fire(resumed) + if(!SSinfluxdriver.can_fire) + can_fire = FALSE + return + + checkpoint++ + while(step < 5) // Yes, you could make one SS per stats category, and have proper scheduling and variable periods, but... + switch(step++) + if(1) // Connected players statistics + run_player_statistics() + if(2) // Job occupations + if(SSticker.current_state == GAME_STATE_PLAYING) + run_job_statistics() + if(3) // Round-wide gameplay statistics held in entity + if(SSticker.current_state == GAME_STATE_PLAYING) + run_round_statistics() + if(4) // Handpicked Round-wide gameplay statistics + if(SSticker.current_state == GAME_STATE_PLAYING) + run_special_round_statistics() + if(MC_TICK_CHECK) + return + + step = 1 + +/datum/controller/subsystem/influxstats/proc/flatten_entity_list(list/data) + var/list/result = list() + for(var/key in data) + var/datum/entity/statistic/entry = data[key] + result[key] = entry.value + return result + +/datum/controller/subsystem/influxstats/proc/run_special_round_statistics() + for(var/hive_tag in GLOB.hive_datum) + var/datum/hive_status/hive = GLOB.hive_datum[hive_tag] + SSinfluxdriver.enqueue_stats("pooled_larva", list("hive" = hive.reporting_id), list("count" = hive.stored_larva)) + var/burst_larvas = GLOB.larva_burst_by_hive[hive] || 0 + SSinfluxdriver.enqueue_stats("burst_larva", list("hive" = hive.reporting_id), list("count" = burst_larvas)) + +/datum/controller/subsystem/influxstats/proc/run_round_statistics() + var/datum/entity/statistic/round/stats = SSticker?.mode?.round_stats + if(!stats) + return // Sadge + + SSinfluxdriver.enqueue_stats_crude("chestbursts", stats.total_larva_burst) + SSinfluxdriver.enqueue_stats_crude("hugged", stats.total_huggers_applied) + SSinfluxdriver.enqueue_stats_crude("friendlyfire", stats.total_friendly_fire_instances) + + var/list/participants = flatten_entity_list(stats.participants) + if(length(participants)) + SSinfluxdriver.enqueue_stats("participants", list(), participants) + + var/list/total_deaths = flatten_entity_list(stats.total_deaths) + if(length(total_deaths)) + SSinfluxdriver.enqueue_stats("deaths", list(), total_deaths) + + SSinfluxdriver.enqueue_stats("shots", list(), + list("fired" = stats.total_projectiles_fired, "hits" = stats.total_projectiles_hit, + "hits_human" = stats.total_projectiles_hit_human, "hits_xeno" = stats.total_projectiles_hit_xeno) + ) + +/datum/controller/subsystem/influxstats/proc/run_player_statistics() + var/staff_count = 0 + var/mentor_count = 0 + for(var/client/client in GLOB.admins) + if(CLIENT_IS_STAFF(client)) + staff_count++ + else if(CLIENT_HAS_RIGHTS(client, R_MENTOR)) + mentor_count++ + SSinfluxdriver.enqueue_stats("online", null, list("count" = length(GLOB.clients))) + + var/list/adm = get_admin_counts() + var/present_admins = length(adm["present"]) + var/afk_admins = length(adm["afk"]) + SSinfluxdriver.enqueue_stats("online_staff", null, list("total" = staff_count, "mentors" = mentor_count, "present" = present_admins, "afk" = afk_admins)) + + // Grab ahelp stats + SSinfluxdriver.enqueue_stats("tickets", null, list( + "open" = length(GLOB.ahelp_tickets.active_tickets), + "closed" = length(GLOB.ahelp_tickets.closed_tickets), + "resolved" = length(GLOB.ahelp_tickets.resolved_tickets), + )) + +/datum/controller/subsystem/influxstats/proc/run_job_statistics() + var/list/team_job_stats = list() + var/list/squad_job_stats = ROLES_SQUAD_ALL.Copy() + for(var/squad in squad_job_stats) + squad_job_stats[squad] = list() + + for(var/client/client in GLOB.clients) + var/team + var/mob/mob = client.mob + if(!mob || mob.statistic_exempt) + continue + var/area/area = get_area(mob) + if(!area || area.statistic_exempt) + continue + var/job = mob.job + if(isobserver(mob) || mob.stat == DEAD) + job = JOB_OBSERVER + team = "observers" + else if(!job) + continue + else if(mob.faction == FACTION_MARINE || mob.faction == FACTION_SURVIVOR) + team = "humans" + var/mob/living/carbon/human/employed_human = mob + if(istype(employed_human)) + var/squad = employed_human.assigned_squad?.name + if(squad in squad_job_stats) + squad_job_stats[squad][job] = (squad_job_stats[squad][job] || 0) + 1 + continue // Defer to squad stats instead + // else: So you're in the USCM and have a job but aren't an human? Tell me more Dr Jones... + else if(ishuman(mob)) + team = "humans_others" + else if(isxeno(mob)) + var/mob/living/xeno_enabled_mob = mob + var/datum/hive_status/hive = GLOB.hive_datum[xeno_enabled_mob.hivenumber] + if(!hive) + team = "xenos_others" + else + team = "xenos_[hive.reporting_id]" + else + team = "others" + LAZYINITLIST(team_job_stats[team]) + if(!team_job_stats[team][job]) + team_job_stats[team][job] = 0 + team_job_stats[team][job] += 1 + + for(var/team in team_job_stats) + for(var/job in team_job_stats[team]) + SSinfluxdriver.enqueue_stats("job_stats", list("team" = team, "job" = job), list("count" = team_job_stats[team][job])) + + for(var/squad in squad_job_stats) + for(var/job in squad_job_stats[squad]) + SSinfluxdriver.enqueue_stats("job_stats", list("team" = "humans", "job" = job, "squad" = squad), list("count" = squad_job_stats[squad][job])) diff --git a/code/datums/statistics/entities/panel_stats.dm b/code/datums/statistics/entities/panel_stats.dm index 44f95f2d91d9..d6e391e1731f 100644 --- a/code/datums/statistics/entities/panel_stats.dm +++ b/code/datums/statistics/entities/panel_stats.dm @@ -741,7 +741,6 @@ "total_projectiles_hit_xeno" = total_projectiles_hit_xeno, "total_slashes" = total_slashes, "total_friendly_fire_instances" = total_friendly_fire_instances, - "total_friendly_fire_kills" = total_friendly_fire_kills, "total_huggers_applied" = total_huggers_applied, "total_larva_burst" = total_larva_burst, "participants" = participants_list, diff --git a/code/datums/statistics/entities/round_stats.dm b/code/datums/statistics/entities/round_stats.dm index ec6aa4ac5bdc..0e1fb6e387db 100644 --- a/code/datums/statistics/entities/round_stats.dm +++ b/code/datums/statistics/entities/round_stats.dm @@ -20,7 +20,6 @@ var/total_projectiles_hit_human = 0 var/total_projectiles_hit_xeno = 0 var/total_friendly_fire_instances = 0 - var/total_friendly_fire_kills = 0 var/total_slashes = 0 // untracked data @@ -360,7 +359,6 @@ stats += "Total shots fired: [total_projectiles_fired]\n" stats += "Total friendly fire instances: [total_friendly_fire_instances]\n" - stats += "Total friendly fire kills: [total_friendly_fire_kills]\n" stats += "Marines remaining: [end_of_round_marines]\n" stats += "Xenos remaining: [end_of_round_xenos]\n" diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 05355feeb154..e6c215d0208f 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -149,7 +149,7 @@ /obj/structure/closet/proc/toggle(mob/living/user) user.next_move = world.time + 5 - if(!(src.opened ? src.close() : src.open())) + if(!(src.opened ? src.close(user) : src.open())) to_chat(user, SPAN_NOTICE("It won't budge!")) return diff --git a/code/modules/admin/tabs/event_tab.dm b/code/modules/admin/tabs/event_tab.dm index 20d98776c5ab..86f54b510ccf 100644 --- a/code/modules/admin/tabs/event_tab.dm +++ b/code/modules/admin/tabs/event_tab.dm @@ -830,7 +830,6 @@ if(isnull(OBShell.double_explosion_delay)) return statsmessage = "Custom HE OB ([OBShell.name]) Stats from [key_name(usr)]: Clear Power: [OBShell.clear_power], Clear Falloff: [OBShell.clear_falloff], Clear Delay: [OBShell.clear_delay], Blast Power: [OBShell.standard_power], Blast Falloff: [OBShell.standard_falloff], Blast Delay: [OBShell.double_explosion_delay]." warhead = OBShell - qdel(OBShell) if("Custom Cluster") var/obj/structure/ob_ammo/warhead/cluster/OBShell = new OBShell.name = input("What name should the warhead have?", "Set name", "Cluster orbital warhead") @@ -847,7 +846,6 @@ if(isnull(OBShell.explosion_falloff)) return statsmessage = "Custom Cluster OB ([OBShell.name]) Stats from [key_name(usr)]: Salvos: [OBShell.total_amount], Shot per Salvo: [OBShell.instant_amount], Explosion Power: [OBShell.explosion_power], Explosion Falloff: [OBShell.explosion_falloff]." warhead = OBShell - qdel(OBShell) if("Custom Incendiary") var/obj/structure/ob_ammo/warhead/incendiary/OBShell = new OBShell.name = input("What name should the warhead have?", "Set name", "Incendiary orbital warhead") @@ -876,19 +874,28 @@ if(isnull(OBShell.fire_color)) return statsmessage = "Custom Incendiary OB ([OBShell.name]) Stats from [key_name(usr)]: Clear Power: [OBShell.clear_power], Clear Falloff: [OBShell.clear_falloff], Clear Delay: [OBShell.clear_delay], Fire Distance: [OBShell.distance], Fire Duration: [OBShell.fire_level], Fire Strength: [OBShell.burn_level]." warhead = OBShell - qdel(OBShell) if(custom) - if(alert(usr, statsmessage, "Confirm Stats", "Yes", "No") != "Yes") return + if(alert(usr, statsmessage, "Confirm Stats", "Yes", "No") != "Yes") + qdel(warhead) + return message_admins(statsmessage) var/turf/target = get_turf(usr.loc) if(alert(usr, "Fire or Spawn Warhead?", "Mode", "Fire", "Spawn") == "Fire") - if(alert("Are you SURE you want to do this? It will create an OB explosion!",, "Yes", "No") != "Yes") return + if(alert("Are you SURE you want to do this? It will create an OB explosion!",, "Yes", "No") != "Yes") + qdel(warhead) + return + message_admins("[key_name(usr)] has fired \an [warhead.name] at ([target.x],[target.y],[target.z]).") warhead.warhead_impact(target) - QDEL_IN(warhead, OB_CRASHING_DOWN) + + if(istype(warhead, /obj/structure/ob_ammo/warhead/cluster)) + // so the user's screen can shake for the duration of the cluster, otherwise we get a runtime. + QDEL_IN(warhead, OB_CLUSTER_DURATION) + else + QDEL_IN(warhead, OB_CRASHING_DOWN) else warhead.loc = target diff --git a/code/modules/character_traits/biology_traits.dm b/code/modules/character_traits/biology_traits.dm index e7625c391b75..87fb0b70ec36 100644 --- a/code/modules/character_traits/biology_traits.dm +++ b/code/modules/character_traits/biology_traits.dm @@ -59,11 +59,13 @@ return ADD_TRAIT(target, TRAIT_LISPING, TRAIT_SOURCE_QUIRK) + target.speech_problem_flag = TRUE ..() /datum/character_trait/biology/lisp/unapply_trait(mob/living/carbon/human/target) REMOVE_TRAIT(target, TRAIT_LISPING, TRAIT_SOURCE_QUIRK) + target.speech_problem_flag = FALSE ..() /datum/character_trait/biology/bad_leg diff --git a/code/modules/cm_marines/dropship_ammo.dm b/code/modules/cm_marines/dropship_ammo.dm index b4b585e1de89..3c01688b70d7 100644 --- a/code/modules/cm_marines/dropship_ammo.dm +++ b/code/modules/cm_marines/dropship_ammo.dm @@ -287,9 +287,8 @@ /obj/structure/ship_ammo/rocket/widowmaker/detonate_on(turf/impact) impact.ceiling_debris_check(3) - spawn(5) - cell_explosion(impact, 300, 40, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, create_cause_data(initial(name), source_mob)) //Your standard HE splash damage rocket. Good damage, good range, good speed, it's an all rounder - qdel(src) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(cell_explosion), impact, 300, 40, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, create_cause_data(initial(name)), source_mob), 0.5 SECONDS) //Your standard HE splash damage rocket. Good damage, good range, good speed, it's an all rounder + QDEL_IN(src, 0.5 SECONDS) /obj/structure/ship_ammo/rocket/banshee name = "\improper AGM-227 'Banshee'" @@ -301,10 +300,9 @@ /obj/structure/ship_ammo/rocket/banshee/detonate_on(turf/impact) impact.ceiling_debris_check(3) - spawn(5) - cell_explosion(impact, 175, 20, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, create_cause_data(initial(name), source_mob)) //Small explosive power with a small fall off for a big explosion range - fire_spread(impact, create_cause_data(initial(name), source_mob), 4, 15, 50, "#00b8ff") //Very intense but the fire doesn't last very long - qdel(src) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(cell_explosion), impact, 175, 20, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, create_cause_data(initial(name)), source_mob), 0.5 SECONDS) //Small explosive power with a small fall off for a big explosion range + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(fire_spread), impact, create_cause_data(initial(name), source_mob), 4, 15, 50, "#00b8ff"), 0.5 SECONDS) //Very intense but the fire doesn't last very long + QDEL_IN(src, 0.5 SECONDS) /obj/structure/ship_ammo/rocket/keeper name = "\improper GBU-67 'Keeper II'" @@ -317,9 +315,8 @@ /obj/structure/ship_ammo/rocket/keeper/detonate_on(turf/impact) impact.ceiling_debris_check(3) - spawn(5) - cell_explosion(impact, 450, 100, EXPLOSION_FALLOFF_SHAPE_EXPONENTIAL, null, create_cause_data(initial(name), source_mob)) //Insane fall off combined with insane damage makes the Keeper useful for single targets, but very bad against multiple. - qdel(src) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(cell_explosion), impact, 450, 100, EXPLOSION_FALLOFF_SHAPE_EXPONENTIAL, null, create_cause_data(initial(name)), source_mob), 0.5 SECONDS) //Insane fall off combined with insane damage makes the Keeper useful for single targets, but very bad against multiple. + QDEL_IN(src, 0.5 SECONDS) /obj/structure/ship_ammo/rocket/harpoon name = "\improper AGM-84 'Harpoon'" @@ -333,9 +330,8 @@ //Looks kinda OP but all it can actually do is just to blow windows and some of other things out, cant do much damage. /obj/structure/ship_ammo/rocket/harpoon/detonate_on(turf/impact) impact.ceiling_debris_check(3) - spawn(5) - cell_explosion(impact, 150, 16, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, create_cause_data(initial(name), source_mob)) - qdel(src) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(cell_explosion), impact, 150, 16, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, create_cause_data(initial(name)), source_mob), 0.5 SECONDS) + QDEL_IN(src, 0.5 SECONDS) /obj/structure/ship_ammo/rocket/napalm name = "\improper XN-99 'Napalm'" @@ -347,10 +343,9 @@ /obj/structure/ship_ammo/rocket/napalm/detonate_on(turf/impact) impact.ceiling_debris_check(3) - spawn(5) - cell_explosion(impact, 200, 25, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, create_cause_data(initial(name), source_mob)) - fire_spread(impact, create_cause_data(initial(name), source_mob), 6, 60, 30, "#EE6515") //Color changed into napalm's color to better convey how intense the fire actually is. - qdel(src) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(cell_explosion), impact, 200, 25, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, create_cause_data(initial(name)), source_mob), 0.5 SECONDS) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(fire_spread), impact, create_cause_data(initial(name), source_mob), 6, 60, 30, "#EE6515"), 0.5 SECONDS) //Color changed into napalm's color to better convey how intense the fire actually is. + QDEL_IN(src, 0.5 SECONDS) diff --git a/code/modules/cm_marines/equipment/gear.dm b/code/modules/cm_marines/equipment/gear.dm index 98e7dbcf49df..ff6c715b520b 100644 --- a/code/modules/cm_marines/equipment/gear.dm +++ b/code/modules/cm_marines/equipment/gear.dm @@ -58,6 +58,8 @@ var/is_animating = FALSE var/first_open = TRUE exit_stun = 0 + /// used to implement a delay before tarp can be entered again after opened (anti-exploit) + COOLDOWN_DECLARE(toggle_delay) /obj/structure/closet/bodybag/tarp/snow icon_state = "snowtarp_closed" @@ -91,9 +93,9 @@ exit_stun = 1 can_store_dead = TRUE -/obj/structure/closet/bodybag/tarp/reactive/scout/close() +/obj/structure/closet/bodybag/tarp/reactive/scout/close(mob/user) if(!skillcheck(usr, SKILL_SPEC_WEAPONS, SKILL_SPEC_ALL) && usr.skills.get_skill_level(SKILL_SPEC_WEAPONS) != SKILL_SPEC_SCOUT) - to_chat(usr, SPAN_WARNING("You don't seem to know how to use [src]...")) + to_chat(user, SPAN_WARNING("You don't seem to know how to use [src]...")) return . = ..() @@ -137,10 +139,14 @@ return /obj/structure/closet/bodybag/tarp/open() + COOLDOWN_START(src, toggle_delay, 3 SECONDS) //3 seconds must pass before tarp can be closed . = ..() handle_cloaking() -/obj/structure/closet/bodybag/tarp/close() +/obj/structure/closet/bodybag/tarp/close(mob/user) + if(!COOLDOWN_FINISHED(src, toggle_delay)) + to_chat(user, SPAN_WARNING("It is too soon to close [src]!")) + return FALSE . = ..() handle_cloaking() diff --git a/code/modules/cm_marines/orbital_cannon.dm b/code/modules/cm_marines/orbital_cannon.dm index 14e990809f4f..ad214c954915 100644 --- a/code/modules/cm_marines/orbital_cannon.dm +++ b/code/modules/cm_marines/orbital_cannon.dm @@ -353,6 +353,9 @@ var/list/ob_type_fuel_requirements /obj/structure/ob_ammo/warhead name = "theoretical orbital ammo" var/warhead_kind + var/shake_frequency + var/max_shake_factor + var/max_knockdown_time /obj/structure/ob_ammo/warhead/proc/warhead_impact(turf/target) // make damn sure everyone hears it @@ -398,10 +401,37 @@ var/list/ob_type_fuel_requirements return TRUE return FALSE +/// proc designed for handling ob camera shakes, takes the target location as input and calculates camera shake based off user location. +/obj/structure/ob_ammo/warhead/proc/handle_ob_shake(turf/epicenter) + + var/radius_size = 30 + + for(var/mob/living/user in urange(radius_size, epicenter)) + + var/distance = get_accurate_dist(get_turf(user), epicenter) + var/distance_percent = ((radius_size - distance) / radius_size) + var/total_shake_factor = abs(max_shake_factor * distance_percent) + + // it's of type cluster. + if(!max_knockdown_time) + shake_camera(user, 0.5, total_shake_factor, shake_frequency) + continue + + shake_camera(user, 3, total_shake_factor, shake_frequency) + user.KnockDown(rand(max_knockdown_time * distance_percent, (max_knockdown_time * distance_percent + 1))) + + if(!user.knocked_down) + continue + to_chat(user, SPAN_WARNING("You are thrown off balance and fall to the ground!")) + /obj/structure/ob_ammo/warhead/explosive name = "\improper HE orbital warhead" warhead_kind = "explosive" icon_state = "ob_warhead_1" + shake_frequency = 3 + max_shake_factor = 15 + max_knockdown_time = 6 + var/clear_power = 1200 var/clear_falloff = 400 var/standard_power = 600 @@ -419,16 +449,20 @@ var/list/ob_type_fuel_requirements var/datum/cause_data/cause_data = create_cause_data(initial(name), source_mob) cell_explosion(target, clear_power, clear_falloff, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, cause_data) //break shit around sleep(clear_delay) - //ACTUALLY BLOW SHIT UP + + // Explosion if turf is not a wall. if(!target.density) cell_explosion(target, standard_power, standard_falloff, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, cause_data) + handle_ob_shake(target) sleep(double_explosion_delay) cell_explosion(target, standard_power, standard_falloff, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, cause_data) return + // Checks turf around the target for(var/turf/T in range(2, target)) if(!T.density) cell_explosion(target, standard_power, standard_falloff, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, cause_data) + handle_ob_shake(target) sleep(double_explosion_delay) cell_explosion(target, standard_power, standard_falloff, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, cause_data) return @@ -437,6 +471,9 @@ var/list/ob_type_fuel_requirements name = "\improper Incendiary orbital warhead" warhead_kind = "incendiary" icon_state = "ob_warhead_2" + shake_frequency = 1 + max_shake_factor = 8 + max_knockdown_time = 3 var/clear_power = 1200 var/clear_falloff = 400 var/clear_delay = 3 @@ -457,6 +494,8 @@ var/list/ob_type_fuel_requirements sleep(10) var/datum/cause_data/cause_data = create_cause_data(initial(name), source_mob) cell_explosion(target, clear_power, clear_falloff, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, cause_data) //break shit around + handle_ob_shake(target) + sleep(clear_delay) fire_spread(target, cause_data, distance, fire_level, burn_level, fire_color, fire_type, TURF_PROTECTION_OB) @@ -464,6 +503,9 @@ var/list/ob_type_fuel_requirements name = "\improper Cluster orbital warhead" warhead_kind = "cluster" icon_state = "ob_warhead_3" + shake_frequency = 2 + max_shake_factor = 1 + var/total_amount = 75 // how many times will the shell fire? var/instant_amount = 3 // how many explosions per time it fires? var/explosion_power = 350 @@ -492,11 +534,13 @@ var/list/ob_type_fuel_requirements if(protected_by_pylon(TURF_PROTECTION_OB, U)) //If the turf somehow gained OB protection while the cluster was firing continue fire_in_a_hole(U) + sleep(delay_between_clusters) /obj/structure/ob_ammo/warhead/cluster/proc/fire_in_a_hole(turf/loc) new /obj/effect/overlay/temp/blinking_laser (loc) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(cell_explosion), loc, explosion_power, explosion_falloff, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, create_cause_data(initial(name), source_mob)), 1 SECONDS) + addtimer(CALLBACK(src, PROC_REF(handle_ob_shake), loc), 1 SECONDS) /obj/structure/ob_ammo/ob_fuel name = "solid fuel" @@ -607,3 +651,4 @@ var/list/ob_type_fuel_requirements return TRUE tgui_interact(user) + diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index 00659389decb..5c685cc3acac 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -287,7 +287,7 @@ In most cases it makes more sense to use apply_damage() instead! And make sure t apply_damage(burn, BURN, picked, sharp, edge) UpdateDamageIcon() updatehealth() - speech_problem_flag = 1 + speech_problem_flag = TRUE //Heal MANY limbs, in random order @@ -308,7 +308,7 @@ In most cases it makes more sense to use apply_damage() instead! And make sure t parts -= picked updatehealth() - speech_problem_flag = 1 + speech_problem_flag = TRUE if(update) UpdateDamageIcon() // damage MANY limbs, in random order diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index 7f9801145a8e..989ee52fa63c 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -64,7 +64,7 @@ var/voice - var/speech_problem_flag = 0 + var/speech_problem_flag = FALSE var/special_voice = "" // For changing our voice. Used by a symptom. diff --git a/code/modules/mob/living/carbon/human/life/handle_disabilities.dm b/code/modules/mob/living/carbon/human/life/handle_disabilities.dm index 46b1f3e15515..9ab234212108 100644 --- a/code/modules/mob/living/carbon/human/life/handle_disabilities.dm +++ b/code/modules/mob/living/carbon/human/life/handle_disabilities.dm @@ -17,7 +17,7 @@ return if(disabilities & TOURETTES) - speech_problem_flag = 1 + speech_problem_flag = TRUE if((prob(10) && knocked_out <= 1)) apply_effect(10, STUN) spawn() @@ -36,7 +36,7 @@ return if(disabilities & NERVOUS) - speech_problem_flag = 1 + speech_problem_flag = TRUE if(prob(10)) stuttering = max(10, stuttering) return diff --git a/code/modules/mob/living/carbon/human/life/handle_regular_status_updates.dm b/code/modules/mob/living/carbon/human/life/handle_regular_status_updates.dm index 4a6596104d4a..5c951a8112bf 100644 --- a/code/modules/mob/living/carbon/human/life/handle_regular_status_updates.dm +++ b/code/modules/mob/living/carbon/human/life/handle_regular_status_updates.dm @@ -59,7 +59,7 @@ if(regular_update && halloss > 0) apply_damage(-3, HALLOSS) else if(sleeping) - speech_problem_flag = 1 + speech_problem_flag = TRUE if(regular_update) handle_dreams() apply_damage(-3, HALLOSS) @@ -122,7 +122,7 @@ handle_statuses() if(paralyzed) - speech_problem_flag = 1 + speech_problem_flag = TRUE apply_effect(1, WEAKEN) silent = 1 blinded = TRUE diff --git a/code/modules/mob/living/carbon/human/life/life_helpers.dm b/code/modules/mob/living/carbon/human/life/life_helpers.dm index 0339bf6ec742..fedeaf9fd48c 100644 --- a/code/modules/mob/living/carbon/human/life/life_helpers.dm +++ b/code/modules/mob/living/carbon/human/life/life_helpers.dm @@ -216,18 +216,18 @@ /mob/living/carbon/human/handle_silent() if(..()) - speech_problem_flag = 1 + speech_problem_flag = TRUE return silent /mob/living/carbon/human/handle_slurring() if(..()) - speech_problem_flag = 1 + speech_problem_flag = TRUE return slurring /mob/living/carbon/human/handle_stunned() if(stunned) adjust_effect(-species.stun_reduction, STUN, EFFECT_FLAG_LIFE) - speech_problem_flag = 1 + speech_problem_flag = TRUE return stunned /mob/living/carbon/human/handle_dazed() @@ -237,7 +237,7 @@ var/final_reduction = skill_resistance + 1 adjust_effect(-final_reduction, DAZE, EFFECT_FLAG_LIFE) if(dazed) - speech_problem_flag = 1 + speech_problem_flag = TRUE return dazed /mob/living/carbon/human/handle_knocked_down() @@ -262,7 +262,7 @@ /mob/living/carbon/human/handle_stuttering() if(..()) - speech_problem_flag = 1 + speech_problem_flag = TRUE return stuttering #define HUMAN_TIMER_TO_EFFECT_CONVERSION (0.05) //(1/20) //once per 2 seconds, with effect equal to endurance, which is used later diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index 46a44fc16611..4b86c827a069 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -262,20 +262,20 @@ for it but just ignore it. /mob/living/carbon/human/proc/handle_speech_problems(message) var/list/returns[3] var/verb = "says" - var/handled = 0 + var/handled = FALSE if(silent) message = "" - handled = 1 + handled = TRUE if(sdisabilities & DISABILITY_MUTE) message = "" - handled = 1 + handled = TRUE if(wear_mask) if(istype(wear_mask, /obj/item/clothing/mask/horsehead)) var/obj/item/clothing/mask/horsehead/hoers = wear_mask if(hoers.voicechange) message = pick("NEEIIGGGHHHH!", "NEEEIIIIGHH!", "NEIIIGGHH!", "HAAWWWWW!", "HAAAWWW!") verb = pick("whinnies","neighs", "says") - handled = 1 + handled = TRUE var/braindam = getBrainLoss() if(slurring || stuttering || dazed || braindam >= 60) @@ -283,17 +283,17 @@ for it but just ignore it. if(slurring) message = slur(message) verb = pick("stammers","stutters") - handled = 1 + handled = TRUE if(stuttering) message = NewStutter(message) verb = pick("stammers", "stutters") - handled = 1 + handled = TRUE if(dazed) message = DazedText(message) verb = pick("mumbles", "babbles") - handled = 1 + handled = TRUE if(braindam >= 60) - handled = 1 + handled = TRUE if(prob(braindam/4)) message = stutter(message, stuttering) verb = pick("stammers", "stutters") @@ -301,6 +301,7 @@ for it but just ignore it. message = uppertext(message) verb = pick("yells like an idiot","says rather loudly") if(HAS_TRAIT(src, TRAIT_LISPING)) + handled = TRUE var/old_message = message message = lisp_replace(message) if(old_message != message) diff --git a/code/modules/mob/living/carbon/xenomorph/Embryo.dm b/code/modules/mob/living/carbon/xenomorph/Embryo.dm index d3c2b725d7ef..8b890de8a727 100644 --- a/code/modules/mob/living/carbon/xenomorph/Embryo.dm +++ b/code/modules/mob/living/carbon/xenomorph/Embryo.dm @@ -272,6 +272,7 @@ if(round_statistics) round_statistics.total_larva_burst++ + GLOB.larva_burst_by_hive[hive] = (GLOB.larva_burst_by_hive[hive] || 0) + 1 burstcount++ if(!larva_embryo.ckey && larva_embryo.burrowable && loc && is_ground_level(loc.z) && (locate(/obj/structure/bed/nest) in loc) && hive.living_xeno_queen && hive.living_xeno_queen.z == loc.z) diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/lurker/lurker_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/lurker/lurker_powers.dm index 0c4ba1f2e86d..b58d94a6ed5b 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/lurker/lurker_powers.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/lurker/lurker_powers.dm @@ -288,6 +288,11 @@ if(!do_after(xeno, 0.8 SECONDS, INTERRUPT_NO_NEEDHAND, BUSY_ICON_HOSTILE, numticks = 2)) // would be 0.75 but that doesn't really work with numticks return + // To make sure that the headbite does nothing if the target is moved away. + if(!xeno.Adjacent(target_carbon)) + to_chat(xeno, SPAN_XENOHIGHDANGER("You missed! Your target was moved away before you could finish headbiting them!")) + return + if(target_carbon.stat == DEAD) to_chat(xeno, SPAN_XENODANGER("They died before you could finish headbiting them! Be more careful next time!")) return diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm index 7e1f31e66e67..7506788c2576 100644 --- a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm +++ b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm @@ -239,6 +239,9 @@ // Used for the faction of the xenomorph. Not recommended to modify. var/internal_faction + /// Short Hive ID as string used in stats reporting + var/reporting_id = "normal" + var/hivenumber = XENO_HIVE_NORMAL var/mob/living/carbon/xenomorph/queen/living_xeno_queen var/egg_planting_range = 15 @@ -1053,6 +1056,7 @@ /datum/hive_status/corrupted name = "Corrupted Hive" + reporting_id = "corrupted" hivenumber = XENO_HIVE_CORRUPTED prefix = "Corrupted " color = "#80ff80" @@ -1076,6 +1080,7 @@ /datum/hive_status/alpha name = "Alpha Hive" + reporting_id = "alpha" hivenumber = XENO_HIVE_ALPHA prefix = "Alpha " color = "#ff4040" @@ -1086,6 +1091,7 @@ /datum/hive_status/bravo name = "Bravo Hive" + reporting_id = "bravo" hivenumber = XENO_HIVE_BRAVO prefix = "Bravo " color = "#ffff80" @@ -1096,6 +1102,7 @@ /datum/hive_status/charlie name = "Charlie Hive" + reporting_id = "charlie" hivenumber = XENO_HIVE_CHARLIE prefix = "Charlie " color = "#bb40ff" @@ -1106,6 +1113,7 @@ /datum/hive_status/delta name = "Delta Hive" + reporting_id = "delta" hivenumber = XENO_HIVE_DELTA prefix = "Delta " color = "#8080ff" @@ -1116,6 +1124,7 @@ /datum/hive_status/feral name = "Feral Hive" + reporting_id = "feral" hivenumber = XENO_HIVE_FERAL prefix = "Feral " color = "#828296" @@ -1131,6 +1140,7 @@ /datum/hive_status/forsaken name = "Forsaken Hive" + reporting_id = "forsaken" hivenumber = XENO_HIVE_FORSAKEN prefix = "Forsaken " color = "#cc8ec4" @@ -1149,6 +1159,7 @@ /datum/hive_status/yautja name = "Hellhound Pack" + reporting_id = "hellhounds" hivenumber = XENO_HIVE_YAUTJA internal_faction = FACTION_YAUTJA @@ -1165,6 +1176,7 @@ /datum/hive_status/mutated name = "Mutated Hive" + reporting_id = "mutated" hivenumber = XENO_HIVE_MUTATED prefix = "Mutated " color = "#6abd99" @@ -1175,6 +1187,7 @@ /datum/hive_status/corrupted/tamed name = "Tamed Hive" + reporting_id = "tamed" hivenumber = XENO_HIVE_TAMED prefix = "Tamed " color = "#80ff80" diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 5528755cf87c..263f5b07cca4 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -309,6 +309,7 @@ damage_mult = BASE_BULLET_DAMAGE_MULT damage_falloff_mult = DAMAGE_FALLOFF_TIER_10 damage_buildup_mult = DAMAGE_BUILDUP_TIER_1 + velocity_add = BASE_VELOCITY_BONUS recoil = RECOIL_OFF recoil_unwielded = RECOIL_OFF movement_onehanded_acc_penalty_mult = MOVEMENT_ACCURACY_PENALTY_MULT_TIER_1 diff --git a/colonialmarines.dme b/colonialmarines.dme index ebd73b3544dd..204c144c8916 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -241,6 +241,9 @@ #include "code\controllers\subsystem\htmlui.dm" #include "code\controllers\subsystem\human.dm" #include "code\controllers\subsystem\inactivity.dm" +#include "code\controllers\subsystem\influxdriver.dm" +#include "code\controllers\subsystem\influxmcstats.dm" +#include "code\controllers\subsystem\influxstats.dm" #include "code\controllers\subsystem\input.dm" #include "code\controllers\subsystem\interior.dm" #include "code\controllers\subsystem\item_cleanup.dm" diff --git a/html/changelogs/AutoChangeLog-pr-3675.yml b/html/changelogs/AutoChangeLog-pr-3675.yml deleted file mode 100644 index 744b83d85b08..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3675.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "TheGamerdk" -delete-after: True -changes: - - rscadd: "ARES will announce how many Foxtrot marines that woke up." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-3732.yml b/html/changelogs/AutoChangeLog-pr-3732.yml new file mode 100644 index 000000000000..1fcec99781cb --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3732.yml @@ -0,0 +1,4 @@ +author: "blackdragonTOW" +delete-after: True +changes: + - refactor: "refactored dropship_ammo so that missiles don't use Spawn()" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-3737.yml b/html/changelogs/AutoChangeLog-pr-3737.yml deleted file mode 100644 index 770af461be67..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3737.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "ihatethisengine" -delete-after: True -changes: - - rscadd: "Added cameras to dropship deployable sentries." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-3761.yml b/html/changelogs/AutoChangeLog-pr-3761.yml deleted file mode 100644 index 75a499cd7e48..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3761.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Drathek" -delete-after: True -changes: - - spellcheck: "Tweaked message when ghosting while nested" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-3762.yml b/html/changelogs/AutoChangeLog-pr-3762.yml new file mode 100644 index 000000000000..708eac96abb7 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3762.yml @@ -0,0 +1,4 @@ +author: "ghostsheet" +delete-after: True +changes: + - bugfix: "Fixed Extended Barrel bug of building up free bullet velocity." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-3766.yml b/html/changelogs/AutoChangeLog-pr-3766.yml deleted file mode 100644 index 6132f005140c..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3766.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Drathek" -delete-after: True -changes: - - bugfix: "Fixed missing xeno ban check for playing as a facehugger" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-3767.yml b/html/changelogs/AutoChangeLog-pr-3767.yml deleted file mode 100644 index 468759acbbad..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3767.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "harryob" -delete-after: True -changes: - - rscadd: "you can no longer implant yourself with a motion detector" \ No newline at end of file diff --git a/html/changelogs/archive/2023-07.yml b/html/changelogs/archive/2023-07.yml new file mode 100644 index 000000000000..eb01ed3f4007 --- /dev/null +++ b/html/changelogs/archive/2023-07.yml @@ -0,0 +1,29 @@ +2023-07-01: + Ben10083: + - bugfix: lisps now occur over radio as well + - refactor: Relevant variables relating to speech problems now use true and false + instead of 1 and 0 + - bugfix: fixes exploit relating to cloaking tarps by adding a delay before tarp + can be closed again. + Drathek: + - bugfix: Fixed missing xeno ban check for playing as a facehugger + - spellcheck: Tweaked message when ghosting while nested + TheGamerdk: + - rscadd: ARES will announce how many Foxtrot marines that woke up. + harryob: + - rscadd: you can no longer implant yourself with a motion detector + ihatethisengine: + - rscadd: Added cameras to dropship deployable sentries. +2023-07-02: + Clairion: + - bugfix: Vampire Lurker headbite will no longer trigger if the target is moved + away during windup. + Cthulhu80: + - rscadd: added camera shake and stun to OB. + fira: + - rscadd: Added InfluxDB backed metrics logging for some of the most used game statistics. + This will allow to graph them over time and give better insight as to what happens + in rounds. + theselfish: + - qol: Made the MP beret to be clickable. + - imageadd: Updated the MP beret obj sprite to be easier to click. diff --git a/icons/obj/items/clothing/cm_hats.dmi b/icons/obj/items/clothing/cm_hats.dmi index 33f600015a6f..7f798c2cd0bc 100644 Binary files a/icons/obj/items/clothing/cm_hats.dmi and b/icons/obj/items/clothing/cm_hats.dmi differ diff --git a/nano/templates/cm_stat_panel.tmpl b/nano/templates/cm_stat_panel.tmpl index 97feebde5971..26de08f17054 100644 --- a/nano/templates/cm_stat_panel.tmpl +++ b/nano/templates/cm_stat_panel.tmpl @@ -159,9 +159,6 @@ Used in: /code/datums/statistics/entities/player_entity.dm
Total Friendly Fire Kills:
-
- {{:data.round.total_friendly_fire_kills}} -
Total Slashes: