From 96b578e761329e7b11aa8acfd884a087ebef8a49 Mon Sep 17 00:00:00 2001 From: Ben <91219575+Ben10083@users.noreply.github.com> Date: Fri, 30 Jun 2023 16:42:40 -0400 Subject: [PATCH 01/19] Prevents spamming open and close tarps (#3768) # About the pull request Fixes #3734 Deals with exploit that allows the user to spam open and close a tarp, making it hard for the Xeno to hit the marine, this makes it so when tarp is opened, 3 seconds must pass before it can be closed again. If 3 seconds isn't enough, delay can be lengthened. # Explain why it's good for the game I was tasked by the Xeno Agents to buff Xeno win rate by any means necessary, including fixing exploits. # Testing Photographs and Procedure
Screenshots & Videos https://github.com/cmss13-devs/cmss13/assets/91219575/999e0f72-a802-41d3-b1d8-5a4569d84e5a Before ![image](https://github.com/cmss13-devs/cmss13/assets/91219575/7eb71b25-4077-4042-bab6-760f0c429190) After
# Changelog :cl: fix: fixes exploit relating to cloaking tarps by adding a delay before tarp can be closed again. /:cl: --------- Co-authored-by: harryob --- .../objects/structures/crates_lockers/closets.dm | 2 +- code/modules/cm_marines/equipment/gear.dm | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) 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/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() From f77fe82c83c538ac86ca1dbb43f370bee104417a Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Fri, 30 Jun 2023 21:53:17 +0100 Subject: [PATCH 02/19] Automatic changelog for PR #3768 [ci skip] --- html/changelogs/AutoChangeLog-pr-3768.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-3768.yml diff --git a/html/changelogs/AutoChangeLog-pr-3768.yml b/html/changelogs/AutoChangeLog-pr-3768.yml new file mode 100644 index 000000000000..fc5830aa66ed --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3768.yml @@ -0,0 +1,4 @@ +author: "Ben10083" +delete-after: True +changes: + - bugfix: "fixes exploit relating to cloaking tarps by adding a delay before tarp can be closed again." \ No newline at end of file From 3795db8835e9795ccede3f58bf9bcf955be21e7f Mon Sep 17 00:00:00 2001 From: harryob Date: Fri, 30 Jun 2023 22:34:48 +0100 Subject: [PATCH 03/19] fixes the generate_documentation workflow --- .github/workflows/generate_documentation.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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: From db2384b624fcaef906934fe9a0736fa46b57a37c Mon Sep 17 00:00:00 2001 From: Ben <91219575+Ben10083@users.noreply.github.com> Date: Fri, 30 Jun 2023 18:41:20 -0400 Subject: [PATCH 04/19] People with lisp now lisp on radio (#3760) # About the pull request The lisp trait did not affect a flag for speech, making it so lisping does not occur over radio, this should make it so what they say is transmitted properly. # Explain why it's good for the game Fixes an oversight and allows for lisp speech to be handled by the handle_speech_problems method. Also realism or something # Testing Photographs and Procedure
Screenshots & Videos ![image](https://github.com/cmss13-devs/cmss13/assets/91219575/7526b49e-ff74-4979-8024-71768501e43a) Poor marine trying to alert command
# Changelog :cl: fix: lisps now occur over radio as well refactor: Relevant variables relating to speech problems now use true and false instead of 1 and 0 /:cl: --- code/modules/character_traits/biology_traits.dm | 2 ++ .../mob/living/carbon/human/human_damage.dm | 4 ++-- .../mob/living/carbon/human/human_defines.dm | 2 +- .../carbon/human/life/handle_disabilities.dm | 4 ++-- .../human/life/handle_regular_status_updates.dm | 4 ++-- .../living/carbon/human/life/life_helpers.dm | 10 +++++----- code/modules/mob/living/carbon/human/say.dm | 17 +++++++++-------- 7 files changed, 23 insertions(+), 20 deletions(-) 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/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) From 0a68bd3a78c4e9b409490e1e3df089f3b10f0dd3 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Fri, 30 Jun 2023 23:51:53 +0100 Subject: [PATCH 05/19] Automatic changelog for PR #3760 [ci skip] --- html/changelogs/AutoChangeLog-pr-3760.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-3760.yml diff --git a/html/changelogs/AutoChangeLog-pr-3760.yml b/html/changelogs/AutoChangeLog-pr-3760.yml new file mode 100644 index 000000000000..dfb133be6ba8 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3760.yml @@ -0,0 +1,5 @@ +author: "Ben10083" +delete-after: True +changes: + - 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" \ No newline at end of file From f194c10a3201b9b62d71e85d3dd2d6d8956659fa Mon Sep 17 00:00:00 2001 From: Changelogs Date: Sat, 1 Jul 2023 01:53:02 +0000 Subject: [PATCH 06/19] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-3675.yml | 4 ---- html/changelogs/AutoChangeLog-pr-3737.yml | 4 ---- html/changelogs/AutoChangeLog-pr-3760.yml | 5 ----- html/changelogs/AutoChangeLog-pr-3761.yml | 4 ---- html/changelogs/AutoChangeLog-pr-3766.yml | 4 ---- html/changelogs/AutoChangeLog-pr-3767.yml | 4 ---- html/changelogs/AutoChangeLog-pr-3768.yml | 4 ---- html/changelogs/archive/2023-07.yml | 16 ++++++++++++++++ 8 files changed, 16 insertions(+), 29 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-3675.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3737.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3760.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3761.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3766.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3767.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3768.yml create mode 100644 html/changelogs/archive/2023-07.yml 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-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-3760.yml b/html/changelogs/AutoChangeLog-pr-3760.yml deleted file mode 100644 index dfb133be6ba8..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3760.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Ben10083" -delete-after: True -changes: - - 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" \ 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-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/AutoChangeLog-pr-3768.yml b/html/changelogs/AutoChangeLog-pr-3768.yml deleted file mode 100644 index fc5830aa66ed..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3768.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Ben10083" -delete-after: True -changes: - - bugfix: "fixes exploit relating to cloaking tarps by adding a delay before tarp can be closed again." \ 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..36734311f334 --- /dev/null +++ b/html/changelogs/archive/2023-07.yml @@ -0,0 +1,16 @@ +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. From b64af8edd16fb4a690614072951c8ea2a0b4b704 Mon Sep 17 00:00:00 2001 From: fira Date: Sat, 1 Jul 2023 03:16:41 +0100 Subject: [PATCH 07/19] InfluxDB Metrics Backend (#3601) # About the pull request Wider scope version of #3480 This ships the following game metrics to InfluxDB: * Job occupations, tagged by "team" (humans/factions, xenos/hives...) * Online Players/Staff/Mentor * AHelp ticket statistics * Round-based statistics that are already DB-logged, such as chestbursts, FF hits, shots fired * Amount of pooled larvas * MC/SS timing info that was previously sent to SQL It uses two separate subsystems, one for collecting metrics, the other for shipping them - and provides procs for easily sending more data to wire up other code. # Explain why it's good for the game Gives fine grained control over game data that we don't have. Currently SOME of these are logged to SQL, and we'd need to create our own vis UI. Backing them by a TSDB allows to view evolutions over time, and means we can plug in standard dashboarding tooling like Grafana. # Testing Photographs and Procedure ![image](https://github.com/cmss13-devs/cmss13/assets/604624/fd3e9245-dea0-4acd-8f4f-68dff9ccba14) (okay it's technically from a TM of the older PR) # Changelog :cl: add: 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. /:cl: --- code/__DEFINES/subsystems.dm | 12 +- code/__HELPERS/text.dm | 5 + code/_globalvars/global_lists.dm | 1 + .../configuration/entries/general.dm | 19 +++ code/controllers/subsystem/influxdriver.dm | 132 +++++++++++++++ code/controllers/subsystem/influxmcstats.dm | 47 ++++++ code/controllers/subsystem/influxstats.dm | 156 ++++++++++++++++++ .../datums/statistics/entities/panel_stats.dm | 1 - .../datums/statistics/entities/round_stats.dm | 2 - .../mob/living/carbon/xenomorph/Embryo.dm | 1 + .../living/carbon/xenomorph/xeno_defines.dm | 13 ++ colonialmarines.dme | 3 + nano/templates/cm_stat_panel.tmpl | 3 - 13 files changed, 386 insertions(+), 9 deletions(-) create mode 100644 code/controllers/subsystem/influxdriver.dm create mode 100644 code/controllers/subsystem/influxmcstats.dm create mode 100644 code/controllers/subsystem/influxstats.dm 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/__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 f0279907f635..586d5e71a92d 100644 --- a/code/_globalvars/global_lists.dm +++ b/code/_globalvars/global_lists.dm @@ -10,6 +10,7 @@ GLOBAL_LIST_EMPTY(GeneralFaxes) //Inter-machine faxes GLOBAL_LIST_EMPTY(fax_contents) //List of fax contents to maintain it even if source paper is deleted 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/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/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/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/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:
From 4ca3edc5eeaa3f1ad8d711696887637199d542c0 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sat, 1 Jul 2023 03:25:57 +0100 Subject: [PATCH 08/19] Automatic changelog for PR #3601 [ci skip] --- html/changelogs/AutoChangeLog-pr-3601.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-3601.yml diff --git a/html/changelogs/AutoChangeLog-pr-3601.yml b/html/changelogs/AutoChangeLog-pr-3601.yml new file mode 100644 index 000000000000..f8efe976ab18 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3601.yml @@ -0,0 +1,4 @@ +author: "fira" +delete-after: True +changes: + - 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." \ No newline at end of file From ddb37366cd9707e6acf655c6729905c43e475244 Mon Sep 17 00:00:00 2001 From: ClairionCM <115504494+ClairionCM@users.noreply.github.com> Date: Sat, 1 Jul 2023 06:23:32 +0200 Subject: [PATCH 09/19] Fixes ranged Vampire Lurker headbiting (#3773) # About the pull request Fixes a bug where lurker headbite would still trigger after a target was moved away during windup by adding a proximity check after the headbite timer. # Explain why it's good for the game fixes #3643 # Changelog :cl: Clairion fix: Vampire Lurker headbite will no longer trigger if the target is moved away during windup. /:cl: --- .../carbon/xenomorph/abilities/lurker/lurker_powers.dm | 5 +++++ 1 file changed, 5 insertions(+) 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 From 22077c8c9d118b986d18973c1f076cde5e24c3e9 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sat, 1 Jul 2023 05:31:35 +0100 Subject: [PATCH 10/19] Automatic changelog for PR #3773 [ci skip] --- html/changelogs/AutoChangeLog-pr-3773.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-3773.yml diff --git a/html/changelogs/AutoChangeLog-pr-3773.yml b/html/changelogs/AutoChangeLog-pr-3773.yml new file mode 100644 index 000000000000..30ca2b7da175 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3773.yml @@ -0,0 +1,4 @@ +author: "Clairion" +delete-after: True +changes: + - bugfix: "Vampire Lurker headbite will no longer trigger if the target is moved away during windup." \ No newline at end of file From 840be46fea3ca37f5f93da56c3bc17218cfb20fb Mon Sep 17 00:00:00 2001 From: Cursor <102828457+theselfish@users.noreply.github.com> Date: Sat, 1 Jul 2023 08:27:23 +0100 Subject: [PATCH 11/19] Makes the MP Beret clickable. (#3622) # About the pull request Title. # Explain why it's good for the game Trying to click a tiny little sprite to take off a piece of headware is bad. Seriously. Other hats that suffer from this that I DID NOT fix because I'm not a spriter and I cannot make new sprites. (This one is from an old version of the dmi file that I had when I made my previous PRs); -Normal and tan berets. -Boonie Hats -Doo-Rags. -Patrol Caps # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: qol: Made the MP beret to be clickable. imageadd: Updated the MP beret obj sprite to be easier to click. /:cl: --- icons/obj/items/clothing/cm_hats.dmi | Bin 44665 -> 49494 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/icons/obj/items/clothing/cm_hats.dmi b/icons/obj/items/clothing/cm_hats.dmi index 33f600015a6f2ffe8ce2cf2e35e35a321721c332..7f798c2cd0bc278bf355977f90a64d45c0ac5af5 100644 GIT binary patch literal 49494 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%0AVg7(Jls$=9UN;n<;H*zrNJkpTB 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 z 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 F`X777jp6_R From ac81e1596f2e752dbdf58424f7b4e3d410146237 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sat, 1 Jul 2023 08:36:20 +0100 Subject: [PATCH 12/19] Automatic changelog for PR #3622 [ci skip] --- html/changelogs/AutoChangeLog-pr-3622.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-3622.yml diff --git a/html/changelogs/AutoChangeLog-pr-3622.yml b/html/changelogs/AutoChangeLog-pr-3622.yml new file mode 100644 index 000000000000..262dc7e2a99a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3622.yml @@ -0,0 +1,5 @@ +author: "theselfish" +delete-after: True +changes: + - qol: "Made the MP beret to be clickable." + - imageadd: "Updated the MP beret obj sprite to be easier to click." \ No newline at end of file From b00aea01aafba508ef82f79901a208a88251dcd9 Mon Sep 17 00:00:00 2001 From: Cthulhu80 <122310258+Cthulhu80@users.noreply.github.com> Date: Sat, 1 Jul 2023 05:47:02 -0400 Subject: [PATCH 13/19] added camera shake to ob (#3658) # About the pull request Added a camera shake and stun feature based on the user's distance to the OB. This is a copy of an existing branch that I somehow managed to mess up -> https://github.com/cmss13-devs/cmss13/pull/3605 # Explain why it's good for the game OBs should feel like OBs, and adding this feature just helps make OBs feel like an actual large explosion. # Changelog :cl: add: added camera shake and stun to OB. /:cl: --- code/__DEFINES/conflict.dm | 1 + code/modules/admin/tabs/event_tab.dm | 19 ++++++--- code/modules/cm_marines/orbital_cannon.dm | 47 ++++++++++++++++++++++- 3 files changed, 60 insertions(+), 7 deletions(-) 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/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/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) + From e9cd8b429c54278d2d44d518277d8213c9756b5e Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sat, 1 Jul 2023 10:56:12 +0100 Subject: [PATCH 14/19] Automatic changelog for PR #3658 [ci skip] --- html/changelogs/AutoChangeLog-pr-3658.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-3658.yml diff --git a/html/changelogs/AutoChangeLog-pr-3658.yml b/html/changelogs/AutoChangeLog-pr-3658.yml new file mode 100644 index 000000000000..f46a1a1b02c2 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-3658.yml @@ -0,0 +1,4 @@ +author: "Cthulhu80" +delete-after: True +changes: + - rscadd: "added camera shake and stun to OB." \ No newline at end of file From 8f495255db1dc028b743558caf632d16dfb7f93b Mon Sep 17 00:00:00 2001 From: Changelogs Date: Sun, 2 Jul 2023 01:53:04 +0000 Subject: [PATCH 15/19] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-3601.yml | 4 ---- html/changelogs/AutoChangeLog-pr-3622.yml | 5 ----- html/changelogs/AutoChangeLog-pr-3658.yml | 4 ---- html/changelogs/AutoChangeLog-pr-3773.yml | 4 ---- html/changelogs/archive/2023-07.yml | 13 +++++++++++++ 5 files changed, 13 insertions(+), 17 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-3601.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3622.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3658.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-3773.yml diff --git a/html/changelogs/AutoChangeLog-pr-3601.yml b/html/changelogs/AutoChangeLog-pr-3601.yml deleted file mode 100644 index f8efe976ab18..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3601.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "fira" -delete-after: True -changes: - - 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." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-3622.yml b/html/changelogs/AutoChangeLog-pr-3622.yml deleted file mode 100644 index 262dc7e2a99a..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3622.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "theselfish" -delete-after: True -changes: - - qol: "Made the MP beret to be clickable." - - imageadd: "Updated the MP beret obj sprite to be easier to click." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-3658.yml b/html/changelogs/AutoChangeLog-pr-3658.yml deleted file mode 100644 index f46a1a1b02c2..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3658.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Cthulhu80" -delete-after: True -changes: - - rscadd: "added camera shake and stun to OB." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-3773.yml b/html/changelogs/AutoChangeLog-pr-3773.yml deleted file mode 100644 index 30ca2b7da175..000000000000 --- a/html/changelogs/AutoChangeLog-pr-3773.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Clairion" -delete-after: True -changes: - - bugfix: "Vampire Lurker headbite will no longer trigger if the target is moved away during windup." \ No newline at end of file diff --git a/html/changelogs/archive/2023-07.yml b/html/changelogs/archive/2023-07.yml index 36734311f334..eb01ed3f4007 100644 --- a/html/changelogs/archive/2023-07.yml +++ b/html/changelogs/archive/2023-07.yml @@ -14,3 +14,16 @@ - 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. From 51bbd781ad5416e48d4eaafe148f769371eb8b8d Mon Sep 17 00:00:00 2001 From: ghostsheet <43085828+ghostsheet@users.noreply.github.com> Date: Sun, 2 Jul 2023 14:28:11 +1000 Subject: [PATCH 16/19] EB Velocity Bugfix (#3762) # About the pull request Extended barrels are currently bugged, each time you recalculate attachment stats, it gets +1 velocity. This fixes that, no more infinitely building up bullet velocity. # Explain why it's good for the game Bug Bad # Changelog :cl: ghostsheet fix: Fixed Extended Barrel bug of building up free bullet velocity. /:cl: --- code/__DEFINES/weapon_stats.dm | 2 +- code/modules/projectiles/gun.dm | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) 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/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 From b32e80faacde97d6d25714991b810f9ff5e2e05a Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sun, 2 Jul 2023 05:36:40 +0100 Subject: [PATCH 17/19] Automatic changelog for PR #3762 [ci skip] --- html/changelogs/AutoChangeLog-pr-3762.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-3762.yml 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 From d87e833204462d76534e3b19ff2204f9a19974e6 Mon Sep 17 00:00:00 2001 From: blackdragonTOW <31581761+blackdragonTOW@users.noreply.github.com> Date: Sat, 1 Jul 2023 22:59:47 -0700 Subject: [PATCH 18/19] Dropship Missile Refactor (#3732) # About the pull request Refactors Dropship Missiles to not use Spawn(). # Explain why it's good for the game While proposing a new CAS feature, this part of the code uses a function we shouldn't be using. With a suggestion from Harry I simply swapped out the parts that we could replace without having to do a bigger overhaul. # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: refactor: refactored dropship_ammo so that missiles don't use Spawn() /:cl: --- code/modules/cm_marines/dropship_ammo.dm | 29 ++++++++++-------------- 1 file changed, 12 insertions(+), 17 deletions(-) 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) From 44fd728fdf249775337ab750682ed730062d50ee Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sun, 2 Jul 2023 07:08:20 +0100 Subject: [PATCH 19/19] Automatic changelog for PR #3732 [ci skip] --- html/changelogs/AutoChangeLog-pr-3732.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-3732.yml 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