From f87aa0ac5b2e8cc7f12dfa27dde38c10f6b1495e Mon Sep 17 00:00:00 2001 From: Letter N <24603524+LetterN@users.noreply.github.com> Date: Mon, 8 Feb 2021 18:46:05 +0800 Subject: [PATCH] toast. And DiscoFox:tm: --- code/__DEFINES/achievements.dm | 99 ++ code/__DEFINES/colors.dm | 6 + code/__DEFINES/medal.dm | 29 - code/__DEFINES/misc.dm | 7 +- code/__DEFINES/subsystems.dm | 2 +- code/__DEFINES/vv.dm | 3 + code/__HELPERS/filters.dm | 319 ++++++ code/__HELPERS/roundend.dm | 35 + .../lists/{medals.dm => achievements.dm} | 0 code/_onclick/item_attack.dm | 3 + code/controllers/subsystem/achievements.dm | 74 ++ code/controllers/subsystem/medals.dm | 87 -- code/controllers/subsystem/shuttle.dm | 69 +- code/datums/achievements/_achievement_data.dm | 148 +++ code/datums/achievements/_awards.dm | 115 +++ code/datums/achievements/boss_achievements.dm | 130 +++ code/datums/achievements/boss_scores.dm | 54 + code/datums/achievements/hardcore_random.dm | 4 + .../datums/achievements/mafia_achievements.dm | 97 ++ code/datums/achievements/misc_achievements.dm | 155 +++ .../datums/achievements/skill_achievements.dm | 10 + code/datums/components/pellet_cloud.dm | 5 + code/game/atoms.dm | 51 +- code/game/gamemodes/meteor/meteors.dm | 83 +- code/game/machinery/autolathe.dm | 82 +- code/game/machinery/computer/arcade.dm | 74 +- code/game/machinery/computer/arcade/battle.dm | 517 +++++++--- .../machinery/computer/arcade/minesweeper.dm | 2 +- .../machinery/computer/arcade/misc_arcade.dm | 13 +- .../machinery/computer/arcade/orion_trail.dm | 420 +++++++- code/game/objects/items/his_grace.dm | 25 +- code/game/objects/items/weaponry.dm | 195 ++-- .../structures/lavaland/necropolis_tendril.dm | 6 +- code/game/objects/structures/watercloset.dm | 9 +- code/game/turfs/simulated/minerals.dm | 10 +- code/modules/admin/admin_verbs.dm | 233 +++-- code/modules/admin/holder2.dm | 2 + code/modules/admin/topic.dm | 3 - code/modules/admin/verbs/debug.dm | 7 +- code/modules/admin/verbs/diagnostics.dm | 28 + code/modules/admin/{ => verbs}/secrets.dm | 928 ++++++++---------- .../admin/view_variables/filterrific.dm | 99 ++ .../view_variables/mass_edit_variables.dm | 7 +- .../admin/view_variables/modify_variables.dm | 2 +- code/modules/admin/view_variables/topic.dm | 6 + .../admin/view_variables/topic_basic.dm | 2 +- .../eldritch_cult/knowledge/ash_lore.dm | 3 +- .../eldritch_cult/knowledge/flesh_lore.dm | 7 +- .../eldritch_cult/knowledge/rust_lore.dm | 3 +- .../modules/awaymissions/super_secret_room.dm | 2 +- code/modules/client/client_procs.dm | 23 + code/modules/client/player_details.dm | 17 + code/modules/events/immovable_rod.dm | 1 + code/modules/mafia/controller.dm | 366 +++---- code/modules/mafia/roles.dm | 81 +- .../mob/living/carbon/human/human_defines.dm | 1 + .../mob/living/simple_animal/bot/cleanbot.dm | 57 +- .../hostile/megafauna/blood_drunk_miner.dm | 3 + .../hostile/megafauna/bubblegum.dm | 21 +- .../hostile/megafauna/colossus.dm | 7 +- .../hostile/megafauna/demonic_frost_miner.dm | 3 + .../simple_animal/hostile/megafauna/drake.dm | 5 +- .../hostile/megafauna/hierophant.dm | 5 +- .../simple_animal/hostile/megafauna/legion.dm | 5 +- .../hostile/megafauna/megafauna.dm | 83 +- .../hostile/megafauna/swarmer.dm | 5 +- code/modules/surgery/surgery_step.dm | 15 +- code/modules/vending/_vending.dm | 3 + icons/UI_Icons/Achievements/Boss/bbgum.png | Bin 0 -> 4864 bytes icons/UI_Icons/Achievements/Boss/colossus.png | Bin 0 -> 6049 bytes icons/UI_Icons/Achievements/Boss/drake.png | Bin 0 -> 7847 bytes .../UI_Icons/Achievements/Boss/hierophant.png | Bin 0 -> 2345 bytes icons/UI_Icons/Achievements/Boss/legion.png | Bin 0 -> 7945 bytes icons/UI_Icons/Achievements/Boss/miner.png | Bin 0 -> 3454 bytes icons/UI_Icons/Achievements/Boss/swarmer.png | Bin 0 -> 4002 bytes icons/UI_Icons/Achievements/Boss/tendril.png | Bin 0 -> 6594 bytes .../UI_Icons/Achievements/Mafia/assistant.png | Bin 0 -> 2374 bytes .../Achievements/Mafia/changeling.png | Bin 0 -> 2363 bytes .../UI_Icons/Achievements/Mafia/chaplain.png | Bin 0 -> 2367 bytes icons/UI_Icons/Achievements/Mafia/clown.png | Bin 0 -> 2363 bytes .../UI_Icons/Achievements/Mafia/detective.png | Bin 0 -> 2357 bytes .../UI_Icons/Achievements/Mafia/fugitive.png | Bin 0 -> 2387 bytes icons/UI_Icons/Achievements/Mafia/hated.png | Bin 0 -> 2467 bytes icons/UI_Icons/Achievements/Mafia/hop.png | Bin 0 -> 2358 bytes icons/UI_Icons/Achievements/Mafia/lawyer.png | Bin 0 -> 2362 bytes icons/UI_Icons/Achievements/Mafia/md.png | Bin 0 -> 2352 bytes .../UI_Icons/Achievements/Mafia/nightmare.png | Bin 0 -> 2350 bytes .../UI_Icons/Achievements/Mafia/obsessed.png | Bin 0 -> 2391 bytes .../Achievements/Mafia/psychologist.png | Bin 0 -> 2348 bytes .../Achievements/Mafia/thoughtfeeder.png | Bin 0 -> 2366 bytes icons/UI_Icons/Achievements/Mafia/traitor.png | Bin 0 -> 2330 bytes .../UI_Icons/Achievements/Misc/ascension.png | Bin 0 -> 3110 bytes .../UI_Icons/Achievements/Misc/ashascend.png | Bin 0 -> 9641 bytes .../UI_Icons/Achievements/Misc/clownking.png | Bin 0 -> 5118 bytes .../Achievements/Misc/clownthanks.png | Bin 0 -> 3441 bytes .../Achievements/Misc/featofstrength.png | Bin 0 -> 2969 bytes .../Achievements/Misc/fleshascend.png | Bin 0 -> 8138 bytes .../Achievements/Misc/frenchingthebubble.png | Bin 0 -> 4864 bytes icons/UI_Icons/Achievements/Misc/helbital.png | Bin 0 -> 3413 bytes icons/UI_Icons/Achievements/Misc/jackpot.png | Bin 0 -> 3037 bytes .../UI_Icons/Achievements/Misc/longshift.png | Bin 0 -> 2436 bytes icons/UI_Icons/Achievements/Misc/meteors.png | Bin 0 -> 7797 bytes icons/UI_Icons/Achievements/Misc/rule8.png | Bin 0 -> 2924 bytes .../UI_Icons/Achievements/Misc/rustascend.png | Bin 0 -> 8390 bytes icons/UI_Icons/Achievements/Misc/snail.png | Bin 0 -> 2766 bytes .../UI_Icons/Achievements/Misc/timewaste.png | Bin 0 -> 1468 bytes .../Achievements/Misc/toolbox_soul.png | Bin 0 -> 6579 bytes icons/UI_Icons/Achievements/Misc/upgrade.png | Bin 0 -> 3292 bytes .../UI_Icons/Achievements/Misc/voidascend.png | Bin 0 -> 982 bytes icons/UI_Icons/Achievements/Skills/mining.png | Bin 0 -> 3406 bytes icons/UI_Icons/Achievements/baseboss.png | Bin 0 -> 1697 bytes icons/UI_Icons/Achievements/basemafia.png | Bin 0 -> 2251 bytes icons/UI_Icons/Achievements/basemisc.png | Bin 0 -> 2244 bytes icons/UI_Icons/Achievements/baseskill.png | Bin 0 -> 2247 bytes icons/UI_Icons/Achievements/default.png | Bin 0 -> 3483 bytes icons/program_icons/robotact.gif | Bin 0 -> 134 bytes strings/arcade.json | 282 ++++++ tgstation.dme | 18 +- 118 files changed, 3889 insertions(+), 1382 deletions(-) create mode 100644 code/__DEFINES/achievements.dm delete mode 100644 code/__DEFINES/medal.dm create mode 100644 code/__HELPERS/filters.dm rename code/_globalvars/lists/{medals.dm => achievements.dm} (100%) mode change 100755 => 100644 create mode 100644 code/controllers/subsystem/achievements.dm delete mode 100644 code/controllers/subsystem/medals.dm create mode 100644 code/datums/achievements/_achievement_data.dm create mode 100644 code/datums/achievements/_awards.dm create mode 100644 code/datums/achievements/boss_achievements.dm create mode 100644 code/datums/achievements/boss_scores.dm create mode 100644 code/datums/achievements/hardcore_random.dm create mode 100644 code/datums/achievements/mafia_achievements.dm create mode 100644 code/datums/achievements/misc_achievements.dm create mode 100644 code/datums/achievements/skill_achievements.dm rename code/modules/admin/{ => verbs}/secrets.dm (53%) create mode 100644 code/modules/admin/view_variables/filterrific.dm create mode 100644 icons/UI_Icons/Achievements/Boss/bbgum.png create mode 100644 icons/UI_Icons/Achievements/Boss/colossus.png create mode 100644 icons/UI_Icons/Achievements/Boss/drake.png create mode 100644 icons/UI_Icons/Achievements/Boss/hierophant.png create mode 100644 icons/UI_Icons/Achievements/Boss/legion.png create mode 100644 icons/UI_Icons/Achievements/Boss/miner.png create mode 100644 icons/UI_Icons/Achievements/Boss/swarmer.png create mode 100644 icons/UI_Icons/Achievements/Boss/tendril.png create mode 100644 icons/UI_Icons/Achievements/Mafia/assistant.png create mode 100644 icons/UI_Icons/Achievements/Mafia/changeling.png create mode 100644 icons/UI_Icons/Achievements/Mafia/chaplain.png create mode 100644 icons/UI_Icons/Achievements/Mafia/clown.png create mode 100644 icons/UI_Icons/Achievements/Mafia/detective.png create mode 100644 icons/UI_Icons/Achievements/Mafia/fugitive.png create mode 100644 icons/UI_Icons/Achievements/Mafia/hated.png create mode 100644 icons/UI_Icons/Achievements/Mafia/hop.png create mode 100644 icons/UI_Icons/Achievements/Mafia/lawyer.png create mode 100644 icons/UI_Icons/Achievements/Mafia/md.png create mode 100644 icons/UI_Icons/Achievements/Mafia/nightmare.png create mode 100644 icons/UI_Icons/Achievements/Mafia/obsessed.png create mode 100644 icons/UI_Icons/Achievements/Mafia/psychologist.png create mode 100644 icons/UI_Icons/Achievements/Mafia/thoughtfeeder.png create mode 100644 icons/UI_Icons/Achievements/Mafia/traitor.png create mode 100644 icons/UI_Icons/Achievements/Misc/ascension.png create mode 100644 icons/UI_Icons/Achievements/Misc/ashascend.png create mode 100644 icons/UI_Icons/Achievements/Misc/clownking.png create mode 100644 icons/UI_Icons/Achievements/Misc/clownthanks.png create mode 100644 icons/UI_Icons/Achievements/Misc/featofstrength.png create mode 100644 icons/UI_Icons/Achievements/Misc/fleshascend.png create mode 100644 icons/UI_Icons/Achievements/Misc/frenchingthebubble.png create mode 100644 icons/UI_Icons/Achievements/Misc/helbital.png create mode 100644 icons/UI_Icons/Achievements/Misc/jackpot.png create mode 100644 icons/UI_Icons/Achievements/Misc/longshift.png create mode 100644 icons/UI_Icons/Achievements/Misc/meteors.png create mode 100644 icons/UI_Icons/Achievements/Misc/rule8.png create mode 100644 icons/UI_Icons/Achievements/Misc/rustascend.png create mode 100644 icons/UI_Icons/Achievements/Misc/snail.png create mode 100644 icons/UI_Icons/Achievements/Misc/timewaste.png create mode 100644 icons/UI_Icons/Achievements/Misc/toolbox_soul.png create mode 100644 icons/UI_Icons/Achievements/Misc/upgrade.png create mode 100644 icons/UI_Icons/Achievements/Misc/voidascend.png create mode 100644 icons/UI_Icons/Achievements/Skills/mining.png create mode 100644 icons/UI_Icons/Achievements/baseboss.png create mode 100644 icons/UI_Icons/Achievements/basemafia.png create mode 100644 icons/UI_Icons/Achievements/basemisc.png create mode 100644 icons/UI_Icons/Achievements/baseskill.png create mode 100644 icons/UI_Icons/Achievements/default.png create mode 100644 icons/program_icons/robotact.gif create mode 100644 strings/arcade.json diff --git a/code/__DEFINES/achievements.dm b/code/__DEFINES/achievements.dm new file mode 100644 index 0000000000..1d2eb90ee4 --- /dev/null +++ b/code/__DEFINES/achievements.dm @@ -0,0 +1,99 @@ +// Keep the identifiers here below 32 characters, you can put the full display name in the actual achievement datum + +#define ACHIEVEMENT_DEFAULT "default" +#define ACHIEVEMENT_SCORE "score" + +//Misc Medal hub IDs +#define MEDAL_METEOR "Your Life Before Your Eyes" +#define MEDAL_PULSE "Jackpot" +#define MEDAL_TIMEWASTE "Overextended The Joke" +#define MEDAL_RODSUPLEX "Feat of Strength" +#define MEDAL_CLOWNCARKING "Round and Full" +#define MEDAL_THANKSALOT "The Best Driver" +#define MEDAL_HELBITALJANKEN "Hel-bent on Winning" +#define MEDAL_MATERIALCRAFT "Getting an Upgrade" +#define MEDAL_DISKPLEASE "Disk, Please!" +#define MEDAL_GAMER "I'm Not Important" +#define MEDAL_VENDORSQUISH "Teenage Anarchist" +#define MEDAL_SWIRLIE "Bowl-d" +#define MEDAL_SELFOUCH "Hands???" +#define MEDAL_SANDMAN "Mister Sandman" +#define MEDAL_CLEANBOSS "Cleanboss" +#define MEDAL_RULE8 "Rule 8" +#define MEDAL_LONGSHIFT "longshift" +#define MEDAL_SNAIL "KKKiiilll mmmeee" +#define MEDAL_LOOKOUTSIR "Look Out, Sir!" +#define MEDAL_GOTTEM "GOTTEM" +#define MEDAL_ASCENSION "Ascension" +#define MEDAL_FRENCHING "FrenchingTheBubble" +#define MEDAL_ASH_ASCENSION "Ash" +#define MEDAL_FLESH_ASCENSION "Flesh" +#define MEDAL_RUST_ASCENSION "Rust" +#define MEDAL_VOID_ASCENSION "Void" +#define MEDAL_TOOLBOX_SOUL "Toolsoul" + +//Skill medal hub IDs +#define MEDAL_LEGENDARY_MINER "Legendary Miner" + +//Mafia medal hub IDs (wins) +#define MAFIA_MEDAL_ASSISTANT "Assistant" +#define MAFIA_MEDAL_DETECTIVE "Detective" +#define MAFIA_MEDAL_PSYCHOLOGIST "Psychologist" +#define MAFIA_MEDAL_CHAPLAIN "Chaplain" +#define MAFIA_MEDAL_MD "Medical Doctor" +#define MAFIA_MEDAL_LAWYER "Lawyer" +#define MAFIA_MEDAL_HOP "Head of Personnel" +#define MAFIA_MEDAL_CHANGELING "CHANGELING" +#define MAFIA_MEDAL_THOUGHTFEEDER "Thoughtfeeder" +#define MAFIA_MEDAL_TRAITOR "Traitor" +#define MAFIA_MEDAL_NIGHTMARE "Nightmare" +#define MAFIA_MEDAL_FUGITIVE "Fugitive" +#define MAFIA_MEDAL_OBSESSED "Obsessed" +#define MAFIA_MEDAL_CLOWN "Clown" + +//Mafia medal hub IDs (misc stuff) +#define MAFIA_MEDAL_HATED "Universally Hated" + +//Boss medals + +// Medal hub IDs for boss medals (Pre-fixes) +#define BOSS_MEDAL_ANY "Boss Killer" +#define BOSS_MEDAL_MINER "Blood-drunk Miner Killer" +#define BOSS_MEDAL_FROSTMINER "Demonic-frost Miner Killer" +#define BOSS_MEDAL_BUBBLEGUM "Bubblegum Killer" +#define BOSS_MEDAL_COLOSSUS "Colossus Killer" +#define BOSS_MEDAL_DRAKE "Drake Killer" +#define BOSS_MEDAL_HIEROPHANT "Hierophant Killer" +#define BOSS_MEDAL_LEGION "Legion Killer" +#define BOSS_MEDAL_TENDRIL "Tendril Exterminator" +#define BOSS_MEDAL_SWARMERS "Swarmer Beacon Killer" +#define BOSS_MEDAL_WENDIGO "Wendigo Killer" +#define BOSS_MEDAL_KINGGOAT "King Goat Killer" + +#define BOSS_MEDAL_MINER_CRUSHER "Blood-drunk Miner Crusher" +#define BOSS_MEDAL_FROSTMINER_CRUSHER "Demonic-frost Miner Crusher" +#define BOSS_MEDAL_BUBBLEGUM_CRUSHER "Bubblegum Crusher" +#define BOSS_MEDAL_COLOSSUS_CRUSHER "Colossus Crusher" +#define BOSS_MEDAL_DRAKE_CRUSHER "Drake Crusher" +#define BOSS_MEDAL_HIEROPHANT_CRUSHER "Hierophant Crusher" +#define BOSS_MEDAL_LEGION_CRUSHER "Legion Crusher" +#define BOSS_MEDAL_SWARMERS_CRUSHER "Swarmer Beacon Crusher" +#define BOSS_MEDAL_WENDIGO_CRUSHER "Wendigo Crusher" +#define BOSS_MEDAL_KINGGOAT_CRUSHER "King Goat Crusher" + +// Medal hub IDs for boss-kill scores +#define BOSS_SCORE "Bosses Killed" +#define MINER_SCORE "BDMs Killed" +#define FROST_MINER_SCORE "DFMs Killed" +#define BUBBLEGUM_SCORE "Bubblegum Killed" +#define COLOSSUS_SCORE "Colossus Killed" +#define DRAKE_SCORE "Drakes Killed" +#define HIEROPHANT_SCORE "Hierophants Killed" +#define LEGION_SCORE "Legion Killed" +#define SWARMER_BEACON_SCORE "Swarmer Beacs Killed" +#define WENDIGO_SCORE "Wendigos Killed" +#define KINGGOAT_SCORE "King Goat Killed" +#define TENDRIL_CLEAR_SCORE "Tendrils Killed" + +// DB IDs for hardcore random mode +#define HARDCORE_RANDOM_SCORE "Hardcore Random Score" diff --git a/code/__DEFINES/colors.dm b/code/__DEFINES/colors.dm index fb461acfa4..9f45d8da79 100644 --- a/code/__DEFINES/colors.dm +++ b/code/__DEFINES/colors.dm @@ -52,3 +52,9 @@ #define COLOR_ASSEMBLY_BLUE "#38559E" #define COLOR_ASSEMBLY_PURPLE "#6F6192" #define COLOR_ASSEMBLY_PINK "#ff4adc" + +#define COLOR_WHITE "#FFFFFF" +#define COLOR_VERY_LIGHT_GRAY "#EEEEEE" +#define COLOR_SILVER "#C0C0C0" +#define COLOR_GRAY "#808080" +#define COLOR_HALF_TRANSPARENT_BLACK "#0000007A" diff --git a/code/__DEFINES/medal.dm b/code/__DEFINES/medal.dm deleted file mode 100644 index e723c7504e..0000000000 --- a/code/__DEFINES/medal.dm +++ /dev/null @@ -1,29 +0,0 @@ -// Medal names -#define BOSS_KILL_MEDAL "Killer" -#define ALL_KILL_MEDAL "Exterminator" //Killing all of x type -#define BOSS_KILL_MEDAL_CRUSHER "Crusher" - -//Defines for boss medals -#define BOSS_MEDAL_MINER "Blood-drunk Miner" -#define BOSS_MEDAL_BUBBLEGUM "Bubblegum" -#define BOSS_MEDAL_COLOSSUS "Colossus" -#define BOSS_MEDAL_DRAKE "Drake" -#define BOSS_MEDAL_HIEROPHANT "Hierophant" -#define BOSS_MEDAL_LEGION "Legion" -#define BOSS_MEDAL_TENDRIL "Tendril" -#define BOSS_MEDAL_SWARMERS "Swarmer Beacon" - -// Score names -#define HIEROPHANT_SCORE "Hierophants Killed" -#define BOSS_SCORE "Bosses Killed" -#define BUBBLEGUM_SCORE "Bubblegum Killed" -#define COLOSSUS_SCORE "Colossus Killed" -#define DRAKE_SCORE "Drakes Killed" -#define LEGION_SCORE "Legion Killed" -#define SWARMER_BEACON_SCORE "Swarmer Beacons Killed" -#define TENDRIL_CLEAR_SCORE "Tendrils Killed" - -//Misc medals -#define MEDAL_METEOR "Your Life Before Your Eyes" -#define MEDAL_PULSE "Jackpot" -#define MEDAL_TIMEWASTE "Overextended The Joke" diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 30162594d6..51d1b618fc 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -435,8 +435,13 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S //text files #define BRAIN_DAMAGE_FILE "traumas.json" #define ION_FILE "ion_laws.json" -#define REDPILL_FILE "redpill.json" #define PIRATE_NAMES_FILE "pirates.json" +#define REDPILL_FILE "redpill.json" +#define ARCADE_FILE "arcade.json" +// #define BOOMER_FILE "boomer.json" +// #define LOCATIONS_FILE "locations.json" +// #define WANTED_FILE "wanted_message.json" +// #define VISTA_FILE "steve.json" #define FLESH_SCAR_FILE "wounds/flesh_scar_desc.json" #define BONE_SCAR_FILE "wounds/bone_scar_desc.json" #define SCAR_LOC_FILE "wounds/scar_loc.json" diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index cbf701e1d3..77186cd07e 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -108,7 +108,7 @@ #define INIT_ORDER_SOUNDS 83 #define INIT_ORDER_INSTRUMENTS 82 #define INIT_ORDER_VIS 80 -// #define INIT_ORDER_ACHIEVEMENTS 77 +#define INIT_ORDER_ACHIEVEMENTS 77 #define INIT_ORDER_RESEARCH 75 #define INIT_ORDER_EVENTS 70 #define INIT_ORDER_JOBS 65 diff --git a/code/__DEFINES/vv.dm b/code/__DEFINES/vv.dm index fe46fdc710..99a2e9d0ab 100644 --- a/code/__DEFINES/vv.dm +++ b/code/__DEFINES/vv.dm @@ -90,6 +90,9 @@ #define VV_HK_TRIGGER_EMP "empulse" #define VV_HK_TRIGGER_EXPLOSION "explode" #define VV_HK_AUTO_RENAME "auto_rename" +// #define VV_HK_RADIATE "radiate" +#define VV_HK_EDIT_FILTERS "edit_filters" +// #define VV_HK_ADD_AI "add_ai" // /obj #define VV_HK_OSAY "osay" diff --git a/code/__HELPERS/filters.dm b/code/__HELPERS/filters.dm new file mode 100644 index 0000000000..7be7ca5d73 --- /dev/null +++ b/code/__HELPERS/filters.dm @@ -0,0 +1,319 @@ +#define ICON_NOT_SET "Not Set" + +//This is stored as a nested list instead of datums or whatever because it json encodes nicely for usage in tgui +GLOBAL_LIST_INIT(master_filter_info, list( + "alpha" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "icon" = ICON_NOT_SET, + "render_source" = "", + "flags" = 0 + ), + "flags" = list( + "MASK_INVERSE" = MASK_INVERSE, + "MASK_SWAP" = MASK_SWAP + ) + ), + "angular_blur" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 1 + ) + ), + /* Not supported because making a proper matrix editor on the frontend would be a huge dick pain. + Uncomment if you ever implement it + "color" = list( + "defaults" = list( + "color" = matrix(), + "space" = FILTER_COLOR_RGB + ) + ), + */ + "displace" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = null, + "icon" = ICON_NOT_SET, + "render_source" = "" + ) + ), + "drop_shadow" = list( + "defaults" = list( + "x" = 1, + "y" = -1, + "size" = 1, + "offset" = 0, + "color" = COLOR_HALF_TRANSPARENT_BLACK + ) + ), + "blur" = list( + "defaults" = list( + "size" = 1 + ) + ), + "layer" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "icon" = ICON_NOT_SET, + "render_source" = "", + "flags" = FILTER_OVERLAY, + "color" = "", + "transform" = null, + "blend_mode" = BLEND_DEFAULT + ) + ), + "motion_blur" = list( + "defaults" = list( + "x" = 0, + "y" = 0 + ) + ), + "outline" = list( + "defaults" = list( + "size" = 0, + "color" = COLOR_BLACK, + "flags" = NONE + ), + "flags" = list( + "OUTLINE_SHARP" = OUTLINE_SHARP, + "OUTLINE_SQUARE" = OUTLINE_SQUARE + ) + ), + "radial_blur" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 0.01 + ) + ), + "rays" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 16, + "color" = COLOR_WHITE, + "offset" = 0, + "density" = 10, + "threshold" = 0.5, + "factor" = 0, + "flags" = FILTER_OVERLAY | FILTER_UNDERLAY + ), + "flags" = list( + "FILTER_OVERLAY" = FILTER_OVERLAY, + "FILTER_UNDERLAY" = FILTER_UNDERLAY + ) + ), + "ripple" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 1, + "repeat" = 2, + "radius" = 0, + "falloff" = 1, + "flags" = NONE + ), + "flags" = list( + "WAVE_BOUNDED" = WAVE_BOUNDED + ) + ), + "wave" = list( + "defaults" = list( + "x" = 0, + "y" = 0, + "size" = 1, + "offset" = 0, + "flags" = NONE + ), + "flags" = list( + "WAVE_SIDEWAYS" = WAVE_SIDEWAYS, + "WAVE_BOUNDED" = WAVE_BOUNDED + ) + ) +)) + +#undef ICON_NOT_SET + +//Helpers to generate lists for filter helpers +//This is the only practical way of writing these that actually produces sane lists +/proc/alpha_mask_filter(x, y, icon/icon, render_source, flags) + . = list("type" = "alpha") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(icon)) + .["icon"] = icon + if(!isnull(render_source)) + .["render_source"] = render_source + if(!isnull(flags)) + .["flags"] = flags + +/proc/angular_blur_filter(x, y, size) + . = list("type" = "angular_blur") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(size)) + .["size"] = size + +/proc/color_matrix_filter(matrix/in_matrix, space) + . = list("type" = "color") + .["color"] = in_matrix + if(!isnull(space)) + .["space"] = space + +/proc/displacement_map_filter(icon, render_source, x, y, size = 32) + . = list("type" = "displace") + if(!isnull(icon)) + .["icon"] = icon + if(!isnull(render_source)) + .["render_source"] = render_source + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(size)) + .["size"] = size + +/proc/drop_shadow_filter(x, y, size, offset, color) + . = list("type" = "drop_shadow") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(size)) + .["size"] = size + if(!isnull(offset)) + .["offset"] = offset + if(!isnull(color)) + .["color"] = color + +/proc/gauss_blur_filter(size) + . = list("type" = "blur") + if(!isnull(size)) + .["size"] = size + +/proc/layering_filter(icon, render_source, x, y, flags, color, transform, blend_mode) + . = list("type" = "layer") + if(!isnull(icon)) + .["icon"] = icon + if(!isnull(render_source)) + .["render_source"] = render_source + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(color)) + .["color"] = color + if(!isnull(flags)) + .["flags"] = flags + if(!isnull(transform)) + .["transform"] = transform + if(!isnull(blend_mode)) + .["blend_mode"] = blend_mode + +/proc/motion_blur_filter(x, y) + . = list("type" = "motion_blur") + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + +/proc/outline_filter(size, color, flags) + . = list("type" = "outline") + if(!isnull(size)) + .["size"] = size + if(!isnull(color)) + .["color"] = color + if(!isnull(flags)) + .["flags"] = flags + +/proc/radial_blur_filter(size, x, y) + . = list("type" = "radial_blur") + if(!isnull(size)) + .["size"] = size + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + +/proc/rays_filter(size, color, offset, density, threshold, factor, x, y, flags) + . = list("type" = "rays") + if(!isnull(size)) + .["size"] = size + if(!isnull(color)) + .["color"] = color + if(!isnull(offset)) + .["offset"] = offset + if(!isnull(density)) + .["density"] = density + if(!isnull(threshold)) + .["threshold"] = threshold + if(!isnull(factor)) + .["factor"] = factor + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(flags)) + .["flags"] = flags + +/proc/ripple_filter(radius, size, falloff, repeat, x, y, flags) + . = list("type" = "ripple") + if(!isnull(radius)) + .["radius"] = radius + if(!isnull(size)) + .["size"] = size + if(!isnull(falloff)) + .["falloff"] = falloff + if(!isnull(repeat)) + .["repeat"] = repeat + if(!isnull(flags)) + .["flags"] = flags + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + +/proc/wave_filter(x, y, size, offset, flags) + . = list("type" = "wave") + if(!isnull(size)) + .["size"] = size + if(!isnull(x)) + .["x"] = x + if(!isnull(y)) + .["y"] = y + if(!isnull(offset)) + .["offset"] = offset + if(!isnull(flags)) + .["flags"] = flags + +/proc/apply_wibbly_filters(atom/in_atom, length) + for(var/i in 1 to 7) + //This is a very baffling and strange way of doing this but I am just preserving old functionality + var/X + var/Y + var/rsq + do + X = 60*rand() - 30 + Y = 60*rand() - 30 + rsq = X*X + Y*Y + while(rsq<100 || rsq>900) // Yeah let's just loop infinitely due to bad luck what's the worst that could happen? + var/random_roll = rand() + in_atom.add_filter("wibbly-[i]", 5, wave_filter(x = X, y = Y, size = rand() * 2.5 + 0.5, offset = random_roll)) + var/filter = in_atom.get_filter("wibbly-[i]") + animate(filter, offset = random_roll, time = 0, loop = -1, flags = ANIMATION_PARALLEL) + animate(offset = random_roll - 1, time = rand() * 20 + 10) + +/proc/remove_wibbly_filters(atom/in_atom) + var/filter + for(var/i in 1 to 7) + filter = in_atom.get_filter("wibbly-[i]") + animate(filter) + in_atom.remove_filter("wibbly-[i]") diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index a860a8cd3f..39c1b9103f 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -169,6 +169,28 @@ file_data["wanted"] = list("author" = "[GLOB.news_network.wanted_issue.scannedUser]", "criminal" = "[GLOB.news_network.wanted_issue.criminal]", "description" = "[GLOB.news_network.wanted_issue.body]", "photo file" = "[GLOB.news_network.wanted_issue.photo_file]") WRITE_FILE(json_file, json_encode(file_data)) +///Handles random hardcore point rewarding if it applies. +/datum/controller/subsystem/ticker/proc/HandleRandomHardcoreScore(client/player_client) + if(!ishuman(player_client.mob)) + return FALSE + var/mob/living/carbon/human/human_mob = player_client.mob + if(!human_mob.hardcore_survival_score) ///no score no glory + return FALSE + + if(human_mob.mind && (human_mob.mind.special_role || length(human_mob.mind.antag_datums) > 0)) + var/didthegamerwin = TRUE + for(var/a in human_mob.mind.antag_datums) + var/datum/antagonist/antag_datum = a + for(var/i in antag_datum.objectives) + var/datum/objective/objective_datum = i + if(!objective_datum.check_completion()) + didthegamerwin = FALSE + if(!didthegamerwin) + return FALSE + player_client.give_award(/datum/award/score/hardcore_random, human_mob, round(human_mob.hardcore_survival_score)) + else if(human_mob.onCentCom()) + player_client.give_award(/datum/award/score/hardcore_random, human_mob, round(human_mob.hardcore_survival_score)) + /datum/controller/subsystem/ticker/proc/declare_completion() set waitfor = FALSE @@ -186,6 +208,19 @@ C.RollCredits() C.playtitlemusic(40) CONFIG_SET(flag/suicide_allowed,TRUE) // EORG suicides allowed + + var/speed_round = FALSE + if(world.time - SSticker.round_start_time <= 300 SECONDS) + speed_round = TRUE + + for(var/client/C in GLOB.clients) + if(!C.credits) + C.RollCredits() + C.playtitlemusic(40) + if(speed_round) + C.give_award(/datum/award/achievement/misc/speed_round, C.mob) + HandleRandomHardcoreScore(C) + var/popcount = gather_roundend_feedback() display_report(popcount) diff --git a/code/_globalvars/lists/medals.dm b/code/_globalvars/lists/achievements.dm old mode 100755 new mode 100644 similarity index 100% rename from code/_globalvars/lists/medals.dm rename to code/_globalvars/lists/achievements.dm diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index 7a614da07b..f63e594675 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -97,6 +97,9 @@ M.lastattacker = user.real_name M.lastattackerckey = user.ckey + if(force && M == user && user.client) + user.client.give_award(/datum/award/achievement/misc/selfouch, user) + user.do_attack_animation(M) M.attacked_by(src, user, attackchain_flags, damage_multiplier) diff --git a/code/controllers/subsystem/achievements.dm b/code/controllers/subsystem/achievements.dm new file mode 100644 index 0000000000..554481b93a --- /dev/null +++ b/code/controllers/subsystem/achievements.dm @@ -0,0 +1,74 @@ +SUBSYSTEM_DEF(achievements) + name = "Achievements" + flags = SS_NO_FIRE + init_order = INIT_ORDER_ACHIEVEMENTS + var/achievements_enabled = FALSE + + ///List of achievements + var/list/datum/award/achievement/achievements = list() + ///List of scores + var/list/datum/award/score/scores = list() + ///List of all awards + var/list/datum/award/awards = list() + +/datum/controller/subsystem/achievements/Initialize(timeofday) + if(!SSdbcore.Connect()) + return ..() + achievements_enabled = TRUE + + for(var/T in subtypesof(/datum/award/achievement)) + var/instance = new T + achievements[T] = instance + awards[T] = instance + + for(var/T in subtypesof(/datum/award/score)) + var/instance = new T + scores[T] = instance + awards[T] = instance + + update_metadata() + + for(var/i in GLOB.clients) + var/client/C = i + if(!C.player_details.achievements.initialized) + C.player_details.achievements.InitializeData() + + return ..() + +/datum/controller/subsystem/achievements/Shutdown() + save_achievements_to_db() + +/datum/controller/subsystem/achievements/proc/save_achievements_to_db() + var/list/cheevos_to_save = list() + for(var/ckey in GLOB.player_details) + var/datum/player_details/PD = GLOB.player_details[ckey] + if(!PD || !PD.achievements) + continue + cheevos_to_save += PD.achievements.get_changed_data() + if(!length(cheevos_to_save)) + return + SSdbcore.MassInsert(format_table_name("achievements"),cheevos_to_save,duplicate_key = TRUE) + +//Update the metadata if any are behind +/datum/controller/subsystem/achievements/proc/update_metadata() + var/list/current_metadata = list() + //select metadata here + var/datum/DBQuery/Q = SSdbcore.NewQuery("SELECT achievement_key,achievement_version FROM [format_table_name("achievement_metadata")]") + if(!Q.Execute(async = TRUE)) + qdel(Q) + return + else + while(Q.NextRow()) + current_metadata[Q.item[1]] = text2num(Q.item[2]) + qdel(Q) + + var/list/to_update = list() + for(var/T in awards) + var/datum/award/A = awards[T] + if(!A.database_id) + continue + if(!current_metadata[A.database_id] || current_metadata[A.database_id] < A.achievement_version) + to_update += list(A.get_metadata_row()) + + if(to_update.len) + SSdbcore.MassInsert(format_table_name("achievement_metadata"),to_update,duplicate_key = TRUE) diff --git a/code/controllers/subsystem/medals.dm b/code/controllers/subsystem/medals.dm deleted file mode 100644 index 36be23973c..0000000000 --- a/code/controllers/subsystem/medals.dm +++ /dev/null @@ -1,87 +0,0 @@ -SUBSYSTEM_DEF(medals) - name = "Medals" - flags = SS_NO_FIRE - var/hub_enabled = FALSE - -/datum/controller/subsystem/medals/Initialize(timeofday) - if(CONFIG_GET(string/medal_hub_address) && CONFIG_GET(string/medal_hub_password)) - hub_enabled = TRUE - return ..() - -/datum/controller/subsystem/medals/proc/UnlockMedal(medal, client/player) - set waitfor = FALSE - if(!medal || !hub_enabled) - return - if(isnull(world.SetMedal(medal, player, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password)))) - hub_enabled = FALSE - log_game("MEDAL ERROR: Could not contact hub to award medal:[medal] player:[player.key]") - message_admins("Error! Failed to contact hub to award [medal] medal to [player.key]!") - return - to_chat(player, "Achievement unlocked: [medal]!") - - -/datum/controller/subsystem/medals/proc/SetScore(score, client/player, increment, force) - set waitfor = FALSE - if(!score || !hub_enabled) - return - - var/list/oldscore = GetScore(score, player, TRUE) - if(increment) - if(!oldscore[score]) - oldscore[score] = 1 - else - oldscore[score] = (text2num(oldscore[score]) + 1) - else - oldscore[score] = force - - var/newscoreparam = list2params(oldscore) - - if(isnull(world.SetScores(player.ckey, newscoreparam, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password)))) - hub_enabled = FALSE - log_game("SCORE ERROR: Could not contact hub to set score. Score:[score] player:[player.key]") - message_admins("Error! Failed to contact hub to set [score] score for [player.key]!") - -/datum/controller/subsystem/medals/proc/GetScore(score, client/player, returnlist) - if(!score || !hub_enabled) - return - - var/scoreget = world.GetScores(player.ckey, score, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password)) - if(isnull(scoreget)) - hub_enabled = FALSE - log_game("SCORE ERROR: Could not contact hub to get score. Score:[score] player:[player.key]") - message_admins("Error! Failed to contact hub to get score: [score] for [player.key]!") - return - . = params2list(scoreget) - if(!returnlist) - return .[score] - -/datum/controller/subsystem/medals/proc/CheckMedal(medal, client/player) - if(!medal || !hub_enabled) - return - - if(isnull(world.GetMedal(medal, player, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password)))) - hub_enabled = FALSE - log_game("MEDAL ERROR: Could not contact hub to get medal:[medal] player: [player.key]") - message_admins("Error! Failed to contact hub to get [medal] medal for [player.key]!") - return - to_chat(player, "[medal] is unlocked") - -/datum/controller/subsystem/medals/proc/LockMedal(medal, client/player) - if(!player || !medal || !hub_enabled) - return - var/result = world.ClearMedal(medal, player, CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password)) - switch(result) - if(null) - hub_enabled = FALSE - log_game("MEDAL ERROR: Could not contact hub to clear medal:[medal] player:[player.key]") - message_admins("Error! Failed to contact hub to clear [medal] medal for [player.key]!") - if(TRUE) - message_admins("Medal: [medal] removed for [player.key]") - if(FALSE) - message_admins("Medal: [medal] was not found for [player.key]. Unable to clear.") - - -/datum/controller/subsystem/medals/proc/ClearScore(client/player) - if(isnull(world.SetScores(player.ckey, "", CONFIG_GET(string/medal_hub_address), CONFIG_GET(string/medal_hub_password)))) - log_game("MEDAL ERROR: Could not contact hub to clear scores for [player.key]!") - message_admins("Error! Failed to contact hub to clear scores for [player.key]!") diff --git a/code/controllers/subsystem/shuttle.dm b/code/controllers/subsystem/shuttle.dm index b74f1d46d3..66aa12ac1a 100644 --- a/code/controllers/subsystem/shuttle.dm +++ b/code/controllers/subsystem/shuttle.dm @@ -65,6 +65,8 @@ SUBSYSTEM_DEF(shuttle) var/datum/turf_reservation/preview_reservation + var/shuttle_loading + /datum/controller/subsystem/shuttle/Initialize(timeofday) ordernum = rand(1, 9000) @@ -664,7 +666,7 @@ SUBSYSTEM_DEF(shuttle) emergencyNoRecall = TRUE endvote_passed = TRUE -/datum/controller/subsystem/shuttle/proc/action_load(datum/map_template/shuttle/loading_template, obj/docking_port/stationary/destination_port) +/datum/controller/subsystem/shuttle/proc/action_load(datum/map_template/shuttle/loading_template, obj/docking_port/stationary/destination_port, replace = FALSE) // Check for an existing preview if(preview_shuttle && (loading_template != preview_template)) preview_shuttle.jumpToNullSpace() @@ -673,8 +675,8 @@ SUBSYSTEM_DEF(shuttle) QDEL_NULL(preview_reservation) if(!preview_shuttle) - if(load_template(loading_template)) - preview_shuttle.linkup(loading_template, destination_port) + load_template(loading_template) + preview_shuttle.linkup(loading_template, destination_port) preview_template = loading_template // get the existing shuttle information, if any @@ -684,7 +686,7 @@ SUBSYSTEM_DEF(shuttle) if(istype(destination_port)) D = destination_port - else if(existing_shuttle) + else if(existing_shuttle && replace) timer = existing_shuttle.timer mode = existing_shuttle.mode D = existing_shuttle.get_docked() @@ -703,11 +705,12 @@ SUBSYSTEM_DEF(shuttle) WARNING("Template shuttle [preview_shuttle] cannot dock at [D] ([result]).") return - if(existing_shuttle) + if(existing_shuttle && replace) existing_shuttle.jumpToNullSpace() var/list/force_memory = preview_shuttle.movement_force preview_shuttle.movement_force = list("KNOCKDOWN" = 0, "THROW" = 0) + preview_shuttle.mode = SHUTTLE_PREARRIVAL//No idle shuttle moving. Transit dock get removed if shuttle moves too long. preview_shuttle.initiate_docking(D) preview_shuttle.movement_force = force_memory @@ -718,7 +721,7 @@ SUBSYSTEM_DEF(shuttle) preview_shuttle.timer = timer preview_shuttle.mode = mode - preview_shuttle.register() + preview_shuttle.register(replace) // TODO indicate to the user that success happened, rather than just // blanking the modification tab @@ -848,7 +851,8 @@ SUBSYSTEM_DEF(shuttle) return data /datum/controller/subsystem/shuttle/ui_act(action, params) - if(..()) + . = ..() + if(.) return var/mob/user = usr @@ -891,22 +895,10 @@ SUBSYSTEM_DEF(shuttle) SSblackbox.record_feedback("text", "shuttle_manipulator", 1, "[M.name]") break - if("preview") - if(S) - . = TRUE - unload_preview() - load_template(S) - if(preview_shuttle) - preview_template = S - user.forceMove(get_turf(preview_shuttle)) if("load") - if(existing_shuttle == backup_shuttle) - // TODO make the load button disabled - WARNING("The shuttle that the selected shuttle will replace \ - is the backup shuttle. Backup shuttle is required to be \ - intact for round sanity.") - else if(S) + if(S && !shuttle_loading) . = TRUE + shuttle_loading = TRUE // If successful, returns the mobile docking port var/obj/docking_port/mobile/mdp = action_load(S) if(mdp) @@ -914,3 +906,38 @@ SUBSYSTEM_DEF(shuttle) message_admins("[key_name_admin(usr)] loaded [mdp] with the shuttle manipulator.") log_admin("[key_name(usr)] loaded [mdp] with the shuttle manipulator.") SSblackbox.record_feedback("text", "shuttle_manipulator", 1, "[mdp.name]") + shuttle_loading = FALSE + + if("preview") + //if(preview_shuttle && (loading_template != preview_template)) + if(S && !shuttle_loading) + . = TRUE + shuttle_loading = TRUE + unload_preview() + load_template(S) + if(preview_shuttle) + preview_template = S + user.forceMove(get_turf(preview_shuttle)) + shuttle_loading = FALSE + + if("replace") + if(existing_shuttle == backup_shuttle) + // TODO make the load button disabled + WARNING("The shuttle that the selected shuttle will replace \ + is the backup shuttle. Backup shuttle is required to be \ + intact for round sanity.") + else if(S && !shuttle_loading) + . = TRUE + shuttle_loading = TRUE + // If successful, returns the mobile docking port + var/obj/docking_port/mobile/mdp = action_load(S, replace = TRUE) + if(mdp) + user.forceMove(get_turf(mdp)) + message_admins("[key_name_admin(usr)] load/replaced [mdp] with the shuttle manipulator.") + log_admin("[key_name(usr)] load/replaced [mdp] with the shuttle manipulator.") + SSblackbox.record_feedback("text", "shuttle_manipulator", 1, "[mdp.name]") + shuttle_loading = FALSE + if(emergency == mdp) //you just changed the emergency shuttle, there are events in game + captains that can change your snowflake choice. + var/set_purchase = alert(usr, "Do you want to also disable shuttle purchases/random events that would change the shuttle?", "Butthurt Admin Prevention", "Yes, disable purchases/events", "No, I want to possibly get owned") + if(set_purchase == "Yes, disable purchases/events") + SSshuttle.shuttle_purchased = SHUTTLEPURCHASE_FORCED diff --git a/code/datums/achievements/_achievement_data.dm b/code/datums/achievements/_achievement_data.dm new file mode 100644 index 0000000000..2c904b8b9c --- /dev/null +++ b/code/datums/achievements/_achievement_data.dm @@ -0,0 +1,148 @@ +///Datum that handles +/datum/achievement_data + ///Ckey of this achievement data's owner + var/owner_ckey + ///Up to date list of all achievements and their info. + var/data = list() + ///Original status of achievement. + var/original_cached_data = list() + ///Have we done our set-up yet? + var/initialized = FALSE + +/datum/achievement_data/New(ckey) + owner_ckey = ckey + if(SSachievements.initialized && !initialized) + InitializeData() + +/datum/achievement_data/proc/InitializeData() + initialized = TRUE + load_all_achievements() //So we know which achievements we have unlocked so far. + +///Gets list of changed rows in MassInsert format +/datum/achievement_data/proc/get_changed_data() + . = list() + for(var/T in data) + var/datum/award/A = SSachievements.awards[T] + if(data[T] != original_cached_data[T])//If our data from before is not the same as now, save it to db. + var/deets = A.get_changed_rows(owner_ckey,data[T]) + if(deets) + . += list(deets) + +/datum/achievement_data/proc/load_all_achievements() + set waitfor = FALSE + + var/list/kv = list() + var/datum/DBQuery/Query = SSdbcore.NewQuery( + "SELECT achievement_key,value FROM [format_table_name("achievements")] WHERE ckey = [owner_ckey]", + ) + if(!Query.Execute()) + qdel(Query) + return + while(Query.NextRow()) + var/key = Query.item[1] + var/value = text2num(Query.item[2]) + kv[key] = value + qdel(Query) + + for(var/T in subtypesof(/datum/award)) + var/datum/award/A = SSachievements.awards[T] + if(!A || !A.name) //Skip abstract achievements types + continue + if(!data[T]) + data[T] = A.parse_value(kv[A.database_id]) + original_cached_data[T] = data[T] + +///Updates local cache with db data for the given achievement type if it wasn't loaded yet. +/datum/achievement_data/proc/get_data(achievement_type) + var/datum/award/A = SSachievements.awards[achievement_type] + if(!A.name) + return FALSE + if(!data[achievement_type]) + data[achievement_type] = A.load(owner_ckey) + original_cached_data[achievement_type] = data[achievement_type] + +///Unlocks an achievement of a specific type. achievement type is a typepath to the award, user is the mob getting the award, and value is an optional value to be used for defining a score to add to the leaderboard +/datum/achievement_data/proc/unlock(achievement_type, mob/user, value = 1) + set waitfor = FALSE + + if(!SSachievements.achievements_enabled) + return + var/datum/award/A = SSachievements.awards[achievement_type] + get_data(achievement_type) //Get the current status first if necessary + if(istype(A, /datum/award/achievement)) + if(data[achievement_type]) //You already unlocked it so don't bother running the unlock proc + return + data[achievement_type] = TRUE + A.on_unlock(user) //Only on default achievement, as scores keep going up. + else if(istype(A, /datum/award/score)) + data[achievement_type] += value + +///Getter for the status/score of an achievement +/datum/achievement_data/proc/get_achievement_status(achievement_type) + return data[achievement_type] + +///Resets an achievement to default values. +/datum/achievement_data/proc/reset(achievement_type) + if(!SSachievements.achievements_enabled) + return + var/datum/award/A = SSachievements.awards[achievement_type] + get_data(achievement_type) + if(istype(A, /datum/award/achievement)) + data[achievement_type] = FALSE + else if(istype(A, /datum/award/score)) + data[achievement_type] = 0 + +/datum/achievement_data/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/spritesheet/simple/achievements), + ) + +/datum/achievement_data/ui_state(mob/user) + return GLOB.always_state + +/datum/achievement_data/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Achievements") + ui.open() + +/datum/achievement_data/ui_data(mob/user) + var/ret_data = list() // screw standards (qustinnus you must rename src.data ok) + ret_data["categories"] = list("Bosses", "Misc", "Mafia", "Scores") + ret_data["achievements"] = list() + ret_data["user_key"] = user.ckey + + var/datum/asset/spritesheet/simple/assets = get_asset_datum(/datum/asset/spritesheet/simple/achievements) + //This should be split into static data later + for(var/achievement_type in SSachievements.awards) + if(!SSachievements.awards[achievement_type].name) //No name? we a subtype. + continue + if(isnull(data[achievement_type])) //We're still loading + continue + var/list/this = list( + "name" = SSachievements.awards[achievement_type].name, + "desc" = SSachievements.awards[achievement_type].desc, + "category" = SSachievements.awards[achievement_type].category, + "icon_class" = assets.icon_class_name(SSachievements.awards[achievement_type].icon), + "value" = data[achievement_type], + "score" = ispath(achievement_type,/datum/award/score) + ) + ret_data["achievements"] += list(this) + + return ret_data + +/datum/achievement_data/ui_static_data(mob/user) + . = ..() + .["highscore"] = list() + for(var/score in SSachievements.scores) + var/datum/award/score/S = SSachievements.scores[score] + if(!S.name || !S.track_high_scores || !S.high_scores.len) + continue + .["highscore"] += list(list("name" = S.name,"scores" = S.high_scores)) + +/client/verb/checkachievements() + set category = "OOC" + set name = "Check achievements" + set desc = "See all of your achievements!" + + player_details.achievements.ui_interact(usr) diff --git a/code/datums/achievements/_awards.dm b/code/datums/achievements/_awards.dm new file mode 100644 index 0000000000..63bc7f6ce7 --- /dev/null +++ b/code/datums/achievements/_awards.dm @@ -0,0 +1,115 @@ +/datum/award + ///Name of the achievement, If null it won't show up in the achievement browser. (Handy for inheritance trees) + var/name + var/desc = "You did it." + ///Found in UI_Icons/Achievements + var/icon = "default" + var/category = "Normal" + + ///What ID do we use in db, limited to 32 characters + var/database_id + //Bump this up if you're changing outdated table identifier and/or achievement type + var/achievement_version = 2 + + //Value returned on db connection failure, in case we want to differ 0 and nonexistent later on + var/default_value = FALSE + +///This proc loads the achievement data from the hub. +/datum/award/proc/load(key) + if(!SSdbcore.Connect()) + return default_value + if(!key || !database_id || !name) + return default_value + var/raw_value = get_raw_value(key) + return parse_value(raw_value) + +///This saves the changed data to the hub. +/datum/award/proc/get_changed_rows(key, value) + if(!database_id || !key || !name) + return + return list( + "ckey" = key, + "achievement_key" = database_id, + "value" = value, + ) + +/datum/award/proc/get_metadata_row() + return list( + "achievement_key" = database_id, + "achievement_version" = achievement_version, + "achievement_type" = "award", + "achievement_name" = name, + "achievement_description" = desc, + ) + +///Get raw numerical achievement value from the database +/datum/award/proc/get_raw_value(key) + var/datum/DBQuery/Q = SSdbcore.NewQuery( + "SELECT value FROM [format_table_name("achievements")] WHERE ckey = [key] AND achievement_key = [database_id]" + ) + if(!Q.Execute(async = TRUE)) + qdel(Q) + return 0 + var/result = 0 + if(Q.NextRow()) + result = text2num(Q.item[1]) + qdel(Q) + return result + +//Should return sanitized value for achievement cache +/datum/award/proc/parse_value(raw_value) + return default_value + +///Can be overriden for achievement specific events +/datum/award/proc/on_unlock(mob/user) + return + +///Achievements are one-off awards for usually doing cool things. +/datum/award/achievement + desc = "Achievement for epic people" + +/datum/award/achievement/get_metadata_row() + . = ..() + .["achievement_type"] = "achievement" + +/datum/award/achievement/parse_value(raw_value) + return raw_value > 0 + +/datum/award/achievement/on_unlock(mob/user) + . = ..() + to_chat(user, "Achievement unlocked: [name]!") + +///Scores are for leaderboarded things, such as killcount of a specific boss +/datum/award/score + desc = "you did it sooo many times." + category = "Scores" + default_value = 0 + + var/track_high_scores = TRUE + var/list/high_scores = list() + +/datum/award/score/New() + . = ..() + if(track_high_scores) + LoadHighScores() + +/datum/award/score/get_metadata_row() + . = ..() + .["achievement_type"] = "score" + +/datum/award/score/proc/LoadHighScores() + var/datum/DBQuery/Q = SSdbcore.NewQuery( + "SELECT ckey,value FROM [format_table_name("achievements")] WHERE achievement_key = [database_id] ORDER BY value DESC LIMIT 50" + ) + if(!Q.Execute(async = TRUE)) + qdel(Q) + return + else + while(Q.NextRow()) + var/key = Q.item[1] + var/score = text2num(Q.item[2]) + high_scores[key] = score + qdel(Q) + +/datum/award/score/parse_value(raw_value) + return isnum(raw_value) ? raw_value : 0 diff --git a/code/datums/achievements/boss_achievements.dm b/code/datums/achievements/boss_achievements.dm new file mode 100644 index 0000000000..6625bbb439 --- /dev/null +++ b/code/datums/achievements/boss_achievements.dm @@ -0,0 +1,130 @@ +/datum/award/achievement/boss + category = "Bosses" + icon = "baseboss" + +/datum/award/achievement/boss/tendril_exterminator + name = "Tendril Exterminator" + desc = "Watch your step" + database_id = BOSS_MEDAL_TENDRIL + icon = "tendril" + +/datum/award/achievement/boss/boss_killer + name = "Boss Killer" + desc = "You've come a long ways from asking how to switch hands." + database_id = "Boss Killer" + icon = "firstboss" + +/datum/award/achievement/boss/blood_miner_kill + name = "Blood-Drunk Miner Killer" + desc = "I guess he couldn't handle his drink that well." + database_id = BOSS_MEDAL_MINER + icon = "miner" + +/datum/award/achievement/boss/demonic_miner_kill + name = "Demonic-Frost Miner Killer" + desc = "Definitely harder than the Blood-Drunk Miner." + database_id = BOSS_MEDAL_FROSTMINER + +/datum/award/achievement/boss/bubblegum_kill + name = "Bubblegum Killer" + desc = "I guess he wasn't made of candy after all" + database_id = BOSS_MEDAL_BUBBLEGUM + icon = "bbgum" + +/datum/award/achievement/boss/colossus_kill + name = "Colossus Killer" + desc = "The bigger they are... the better the loot" + database_id = BOSS_MEDAL_COLOSSUS + icon = "colossus" + +/datum/award/achievement/boss/drake_kill + name = "Drake Killer" + desc = "Now I can wear Rune Platebodies!" + database_id = BOSS_MEDAL_DRAKE + icon = "drake" + +/datum/award/achievement/boss/hierophant_kill + name = "Hierophant Killer" + desc = "Hierophant, but not triumphant." + database_id = BOSS_MEDAL_HIEROPHANT + icon = "hierophant" + +/datum/award/achievement/boss/legion_kill + name = "Legion Killer" + desc = "We were many..now we are none." + database_id = BOSS_MEDAL_LEGION + icon = "legion" + +/datum/award/achievement/boss/swarmer_beacon_kill + name = "Swarm Beacon Killer" + desc = "GET THEM OFF OF ME!" + database_id = BOSS_MEDAL_SWARMERS + icon = "swarmer" + +/datum/award/achievement/boss/wendigo_kill + name = "Wendigo Killer" + desc = "You've now ruined years of mythical storytelling." + database_id = BOSS_MEDAL_WENDIGO + +/datum/award/achievement/boss/blood_miner_crusher + name = "Blood-Drunk Miner Crusher" + desc = "I guess he couldn't handle his drink that well." + database_id = BOSS_MEDAL_MINER_CRUSHER + icon = "miner" + +/datum/award/achievement/boss/demonic_miner_crusher + name = "Demonic-Frost Miner Crusher" + desc = "Definitely harder than the Blood-Drunk Miner." + database_id = BOSS_MEDAL_FROSTMINER_CRUSHER + +/datum/award/achievement/boss/bubblegum_crusher + name = "Bubblegum Crusher" + desc = "I guess he wasn't made of candy after all" + database_id = BOSS_MEDAL_BUBBLEGUM_CRUSHER + icon = "bbgum" + +/datum/award/achievement/boss/colossus_crusher + name = "Colossus Crusher" + desc = "The bigger they are... the better the loot" + database_id = BOSS_MEDAL_COLOSSUS_CRUSHER + icon = "colossus" + +/datum/award/achievement/boss/drake_crusher + name = "Drake Crusher" + desc = "Now I can wear Rune Platebodies!" + database_id = BOSS_MEDAL_DRAKE_CRUSHER + icon = "drake" + +/datum/award/achievement/boss/hierophant_crusher + name = "Hierophant Crusher" + desc = "Hierophant, but not triumphant." + database_id = BOSS_MEDAL_HIEROPHANT_CRUSHER + icon = "hierophant" + +/datum/award/achievement/boss/legion_crusher + name = "Legion Crusher" + desc = "We were many... now we are none." + database_id = BOSS_MEDAL_LEGION_CRUSHER + +/datum/award/achievement/boss/swarmer_beacon_crusher + name = "Swarm Beacon Crusher" + desc = "GET THEM OFF OF ME!" + database_id = BOSS_MEDAL_SWARMERS_CRUSHER + +/datum/award/achievement/boss/wendigo_crusher + name = "Wendigo Crusher" + desc = "You've now ruined years of mythical storytelling." + database_id = BOSS_MEDAL_WENDIGO_CRUSHER + +//should be removed soon +/datum/award/achievement/boss/king_goat_kill + name = "King Goat Killer" + desc = "The king is dead, long live the king!" + database_id = BOSS_MEDAL_KINGGOAT + icon = "goatboss" + +/datum/award/achievement/boss/king_goat_crusher + name = "King Goat Crusher" + desc = "The king is dead, long live the king!" + database_id = BOSS_MEDAL_KINGGOAT_CRUSHER + icon = "goatboss" diff --git a/code/datums/achievements/boss_scores.dm b/code/datums/achievements/boss_scores.dm new file mode 100644 index 0000000000..fdb9efa7c7 --- /dev/null +++ b/code/datums/achievements/boss_scores.dm @@ -0,0 +1,54 @@ +/datum/award/score/tendril_score + name = "Tendril Score" + desc = "Watch your step" + database_id = TENDRIL_CLEAR_SCORE + +/datum/award/score/boss_score + name = "Bosses Killed" + desc = "You've killed HOW many?" + database_id = BOSS_SCORE + +/datum/award/score/blood_miner_score + name = "Blood-Drunk Miners Killed" + desc = "You've killed HOW many?" + database_id = MINER_SCORE + +/datum/award/score/demonic_miner_score + name = "Demonic-Frost Miners Killed" + desc = "You've killed HOW many?" + database_id = FROST_MINER_SCORE + +/datum/award/score/bubblegum_score + name = "Bubblegums Killed" + desc = "You've killed HOW many?" + database_id = BUBBLEGUM_SCORE + +/datum/award/score/colussus_score + name = "Colossus Killed" + desc = "You've killed HOW many?" + database_id = COLOSSUS_SCORE + +/datum/award/score/drake_score + name = "Drakes Killed" + desc = "You've killed HOW many?" + database_id = DRAKE_SCORE + +/datum/award/score/hierophant_score + name = "Hierophants Killed" + desc = "You've killed HOW many?" + database_id = HIEROPHANT_SCORE + +/datum/award/score/legion_score + name = "Legions Killed" + desc = "You've killed HOW many?" + database_id = LEGION_SCORE + +/datum/award/score/swarmer_beacon_score + name = "Swarmer Beacons Killed" + desc = "You've killed HOW many?" + database_id = SWARMER_BEACON_SCORE + +/datum/award/score/wendigo_score + name = "Wendigos Killed" + desc = "You've killed HOW many?" + database_id = WENDIGO_SCORE diff --git a/code/datums/achievements/hardcore_random.dm b/code/datums/achievements/hardcore_random.dm new file mode 100644 index 0000000000..c5e2692552 --- /dev/null +++ b/code/datums/achievements/hardcore_random.dm @@ -0,0 +1,4 @@ +/datum/award/score/hardcore_random + name = "Hardcore random points" + desc = "Well, I might be a blind, deaf, crippled guy, but hey, at least I'm alive." + database_id = HARDCORE_RANDOM_SCORE diff --git a/code/datums/achievements/mafia_achievements.dm b/code/datums/achievements/mafia_achievements.dm new file mode 100644 index 0000000000..414917413a --- /dev/null +++ b/code/datums/achievements/mafia_achievements.dm @@ -0,0 +1,97 @@ +/datum/award/achievement/mafia + category = "Mafia" + icon = "basemafia" + +///ALL THE ACHIEVEMENTS FOR WINNING A ROUND AS A ROLE/// + +/datum/award/achievement/mafia/assistant + name = "Assistant Victory" + desc = "If you got killed instead of someone more important, you just flexed the true strength of your \"\"\"\"role\"\"\"\"." + database_id = MAFIA_MEDAL_ASSISTANT + icon = "assistant" + +/datum/award/achievement/mafia/detective + name = "Detective Victory" + desc = "If you did this with a Medical Doctor in the game, i'm not really that impressed." + database_id = MAFIA_MEDAL_DETECTIVE + icon = "detective" + +/datum/award/achievement/mafia/psychologist + name = "Psychologist Victory" + desc = "You learned how to not reveal someone random night one! Or... maybe you're just a lucky bastard." + database_id = MAFIA_MEDAL_PSYCHOLOGIST + icon = "psychologist" + +/datum/award/achievement/mafia/chaplain + name = "Chaplain Victory" + desc = "Useless... until the one night the thoughtfeeder confidently claims themselves as detective. Mafia's true bullshit detector." + database_id = MAFIA_MEDAL_CHAPLAIN + icon = "chaplain" + +/datum/award/achievement/mafia/md + name = "Medical Doctor Victory" + desc = "Congratulations on learning how to not talk!" + database_id = MAFIA_MEDAL_MD + icon = "md" + +/datum/award/achievement/mafia/lawyer + name = "Lawyer Victory" + desc = "Oh don't mind me, i'm just the worst rol- Oops, I just instantly ended the game." + database_id = MAFIA_MEDAL_LAWYER + icon = "lawyer" + +/datum/award/achievement/mafia/hop + name = "Head of Personnel Victory" + desc = "King of Assistants, waster of a single mafia's night, thrower of games." + database_id = MAFIA_MEDAL_HOP + icon = "hop" + +/datum/award/achievement/mafia/changeling + name = "Changeling Victory" + desc = "I think the changelings are metacomming." + database_id = MAFIA_MEDAL_CHANGELING + icon = "changeling" + +/datum/award/achievement/mafia/thoughtfeeder + name = "Thoughtfeeder Victory" + desc = "Clown's best friend. And Obsessed. And fugitive? Whose side are you on?!" + database_id = MAFIA_MEDAL_THOUGHTFEEDER + icon = "thoughtfeeder" + +/datum/award/achievement/mafia/traitor + name = "Traitor Victory" + desc = "Guys, we still have two more changelings to ki-!! TRAITOR VICTORY !!" + database_id = MAFIA_MEDAL_TRAITOR + icon = "traitor" + +/datum/award/achievement/mafia/nightmare + name = "Nightmare Victory" + desc = "DID YOUR LIGHT FLICKER?!" + database_id = MAFIA_MEDAL_NIGHTMARE + icon = "nightmare" + +/datum/award/achievement/mafia/fugitive + name = "Fugitive Victory" + desc = "I'm just the description on an achievement, but if you end up having to choose between town and changelings, go changelings." + database_id = MAFIA_MEDAL_FUGITIVE + icon = "fugitive" + +/datum/award/achievement/mafia/obsessed + name = "Obsessed Victory" + desc = "You got your target lynched, so instead of being spiteful and annoying, you're just smug and annoying." + database_id = MAFIA_MEDAL_OBSESSED + icon = "obsessed" + +/datum/award/achievement/mafia/clown + name = "Clown Victory" + desc = "Did you know this works on traitors, despite their immunity? If you hit the jackpot and manage to kill one, they'll salt into the next dimension. Clown tips!" + database_id = MAFIA_MEDAL_CLOWN + icon = "clown" + +///ALL THE ACHIEVEMENTS FOR MISC MAFIA ODDITIES/// + +/datum/award/achievement/mafia/universally_hated + name = "Universally Hated" + desc = "Managed to get more than 12 votes when put up on trial, jesus christ." + database_id = MAFIA_MEDAL_HATED + icon = "hated" diff --git a/code/datums/achievements/misc_achievements.dm b/code/datums/achievements/misc_achievements.dm new file mode 100644 index 0000000000..a99a25ec77 --- /dev/null +++ b/code/datums/achievements/misc_achievements.dm @@ -0,0 +1,155 @@ +/datum/award/achievement/misc + category = "Misc" + icon = "basemisc" + +/datum/award/achievement/misc/meteor_examine + name = "Your Life Before Your Eyes" + desc = "Take a close look at hurtling space debris" + database_id = MEDAL_METEOR + icon = "meteors" + +/datum/award/achievement/misc/pulse + name = "Jackpot" + desc = "Win a pulse rifle from an arcade machine" + database_id = MEDAL_PULSE + icon = "jackpot" + +/datum/award/achievement/misc/time_waste + name = "Time waster" + desc = "Speak no evil, hear no evil, see just errors" + database_id = MEDAL_TIMEWASTE + icon = "timewaste" + +/datum/award/achievement/misc/feat_of_strength + name = "Feat of Strength" + desc = "If the rod is immovable, is it passing you or are you passing it?" + database_id = MEDAL_RODSUPLEX + icon = "featofstrength" + +/datum/award/achievement/misc/round_and_full + name = "Round and Full" + desc = "Well at least you aren't down the river, I hear they eat people there." + database_id = MEDAL_CLOWNCARKING + icon = "clownking" + +/datum/award/achievement/misc/the_best_driver + name = "The Best Driver" + desc = "100 honks later" + database_id = MEDAL_THANKSALOT + icon = "clownthanks" + +/datum/award/achievement/misc/helbitaljanken + name = "Helbitaljanken" + desc = "You janked hard" + database_id = MEDAL_HELBITALJANKEN + icon = "helbital" + +/datum/award/achievement/misc/getting_an_upgrade + name = "Getting an upgrade" + desc = "Make your first unique material item!" + database_id = MEDAL_MATERIALCRAFT + +/datum/award/achievement/misc/rocket_holdup + name = "Disk, Please!" + desc = "Is the man currently pointing a loaded rocket launcher at your head point blank really dumb enough to pull the trigger? Do you really want to find out?" + database_id = MEDAL_DISKPLEASE + +/datum/award/achievement/misc/gamer + name = "My Watchlist Status is Not Important" + desc = "You may be under the impression that violent video games are a harmless pastime, but the security and medical personnel swarming your location with batons and knockout gas look like they disagree." + database_id = MEDAL_GAMER + +/datum/award/achievement/misc/vendor_squish + name = "I Was a Teenage Anarchist" + desc = "You were doing a great job sticking it to the system until that vending machine decided to fight back." + database_id = MEDAL_VENDORSQUISH + +/datum/award/achievement/misc/swirlie + name = "A Bowl-d New World" + desc = "There's a lot of grisly ways to kick it on the Spinward Periphery, but drowning to death in a toilet probably wasn't what you had in mind. Probably." + database_id = MEDAL_SWIRLIE + +/datum/award/achievement/misc/selfouch + name = "How Do I Switch Hands???" + desc = "If you saw someone casually club themselves upside the head with a toolbox anywhere in the galaxy but here, you'd probably be pretty concerned for them." + database_id = MEDAL_SELFOUCH + +/datum/award/achievement/misc/sandman + name = "Mister Sandman" + desc = "Mechanically speaking, there's no real benefit to being unconscious during surgery. Weird how insistent this doctor is about using the N2O anyway though, huh?" + database_id = MEDAL_SANDMAN + +/datum/award/achievement/misc/cleanboss + name = "One Lean, Mean, Cleaning Machine" + desc = "How does it feel to know that your workplace values a mop bucket on wheels more than you?" // i can do better than this give me time + database_id = MEDAL_CLEANBOSS + +/datum/award/achievement/misc/rule8 + name = "Rule 8" + desc = "Call an admin this is ILLEGAL!!" + database_id = MEDAL_RULE8 + icon = "rule8" + +/datum/award/achievement/misc/speed_round + name = "Long shift" + desc = "Well, that didn't take long." + database_id = MEDAL_LONGSHIFT + icon = "longshift" + +/datum/award/achievement/misc/snail + name = "KKKiiilll mmmeee" + desc = "You were a little too ambitious, but hey, I guess you're still alive?" + database_id = MEDAL_SNAIL + icon = "snail" + +/datum/award/achievement/misc/lookoutsir + name = "Look Out, Sir!" + desc = "Either awarded for making the ultimate sacrifice for your comrades, or a really dumb attempt at grenade jumping." + database_id = MEDAL_LOOKOUTSIR + +/datum/award/achievement/misc/gottem + name = "HA, GOTTEM" + desc = "Made you look!" + database_id = MEDAL_GOTTEM + +/datum/award/achievement/misc/ascension + name = "Ascension" + desc = "Caedite eos. Novit enim Dominus qui sunt eius." + database_id = MEDAL_ASCENSION + icon = "ascension" + +/datum/award/achievement/misc/frenching + name = "Frenching" + desc = "Just a taste, for science!" + database_id = MEDAL_FRENCHING + icon = "frenching" + +/datum/award/achievement/misc/ash_ascension + name = "Nightwatcher's Eyes" + desc = "You've risen above the flames, became one with the ashes. You've been reborn as one with the Nightwatcher." + database_id = MEDAL_ASH_ASCENSION + icon = "ashascend" + +/datum/award/achievement/misc/flesh_ascension + name = "Vortex of Arms" + desc = "You've became something more, something greater. A piece of the emperor resides within you, and you within him." + database_id = MEDAL_FLESH_ASCENSION + icon = "fleshascend" + +/datum/award/achievement/misc/rust_ascension + name = "Hills of Rust" + desc = "You've summoned a piece of the Hill of rust, and so the Hills welcome you." + database_id = MEDAL_RUST_ASCENSION + icon = "rustascend" + +/datum/award/achievement/misc/void_ascension + name = "All that perish" + desc = "Place of a diffrent being, diffrent time. Everything ends there... but maybe it is just the beginning?" + database_id = MEDAL_VOID_ASCENSION + icon = "voidascend" + +/datum/award/achievement/misc/toolbox_soul + name = "SOUL'd Out" + desc = "My eternal soul was destroyed to make a toolbox look funny and all I got was this achievement..." + database_id = MEDAL_TOOLBOX_SOUL + icon = "toolbox_soul" diff --git a/code/datums/achievements/skill_achievements.dm b/code/datums/achievements/skill_achievements.dm new file mode 100644 index 0000000000..7da936c61f --- /dev/null +++ b/code/datums/achievements/skill_achievements.dm @@ -0,0 +1,10 @@ +/datum/award/achievement/skill + category = "Skills" + icon = "baseskill" + +/datum/award/achievement/skill/legendary_miner + name = "Legendary miner" + desc = "No mere rock can stop me!" + database_id = MEDAL_LEGENDARY_MINER + icon = "mining" + diff --git a/code/datums/components/pellet_cloud.dm b/code/datums/components/pellet_cloud.dm index 6404be94c4..f88b9e1869 100644 --- a/code/datums/components/pellet_cloud.dm +++ b/code/datums/components/pellet_cloud.dm @@ -275,6 +275,11 @@ else target.visible_message("[target] is hit by a [proj_name][hit_part ? " in the [hit_part.name]" : ""]!", null, null, COMBAT_MESSAGE_RANGE, target) to_chat(target, "You're hit by a [proj_name][hit_part ? " in the [hit_part.name]" : ""]!") + + for(var/M in purple_hearts) + var/mob/living/martyr = M + if(martyr.stat == DEAD && martyr.client) + martyr.client.give_award(/datum/award/achievement/misc/lookoutsir, martyr) UnregisterSignal(parent, COMSIG_PARENT_PREQDELETED) if(queued_delete) qdel(parent) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 189640a1c6..363bcbc29e 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -882,6 +882,9 @@ VV_DROPDOWN_OPTION(VV_HK_ADD_REAGENT, "Add Reagent") VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EMP, "EMP Pulse") VV_DROPDOWN_OPTION(VV_HK_TRIGGER_EXPLOSION, "Explosion") + // VV_DROPDOWN_OPTION(VV_HK_RADIATE, "Radiate") + VV_DROPDOWN_OPTION(VV_HK_EDIT_FILTERS, "Edit Filters") + // VV_DROPDOWN_OPTION(VV_HK_ADD_AI, "Add AI controller") /atom/vv_do_topic(list/href_list) . = ..() @@ -925,6 +928,9 @@ var/newname = input(usr, "What do you want to rename this to?", "Automatic Rename") as null|text if(newname) vv_auto_rename(newname) + if(href_list[VV_HK_EDIT_FILTERS] && check_rights(R_VAREDIT)) + var/client/C = usr.client + C?.open_filter_editor(src) /atom/vv_get_header() . = ..() @@ -1145,7 +1151,6 @@ victim.log_message(message, LOG_ATTACK, color="blue") -// Filter stuff /atom/proc/add_filter(name,priority,list/params) LAZYINITLIST(filter_data) var/list/p = params.Copy() @@ -1161,16 +1166,50 @@ var/list/arguments = data.Copy() arguments -= "priority" filters += filter(arglist(arguments)) + UNSETEMPTY(filter_data) + +/atom/proc/transition_filter(name, time, list/new_params, easing, loop) + var/filter = get_filter(name) + if(!filter) + return + + var/list/old_filter_data = filter_data[name] + + var/list/params = old_filter_data.Copy() + for(var/thing in new_params) + params[thing] = new_params[thing] + + animate(filter, new_params, time = time, easing = easing, loop = loop) + for(var/param in params) + filter_data[name][param] = params[param] + +/atom/proc/change_filter_priority(name, new_priority) + if(!filter_data || !filter_data[name]) + return + + filter_data[name]["priority"] = new_priority + update_filters() + +/obj/item/update_filters() + . = ..() + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() /atom/proc/get_filter(name) if(filter_data && filter_data[name]) return filters[filter_data.Find(name)] -/atom/proc/remove_filter(name) - if(filter_data && filter_data[name]) - filter_data -= name - update_filters() - return TRUE +/atom/proc/remove_filter(name_or_names) + if(!filter_data) + return + + var/list/names = islist(name_or_names) ? name_or_names : list(name_or_names) + + for(var/name in names) + if(filter_data[name]) + filter_data -= name + update_filters() /atom/proc/intercept_zImpact(atom/movable/AM, levels = 1) . |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels) diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm index 5cfec2376a..19e6768d71 100644 --- a/code/game/gamemodes/meteor/meteors.dm +++ b/code/game/gamemodes/meteor/meteors.dm @@ -1,4 +1,6 @@ #define DEFAULT_METEOR_LIFETIME 1800 +#define MAP_EDGE_PAD 5 + GLOBAL_VAR_INIT(meteor_wave_delay, 625) //minimum wait between waves in tenths of seconds //set to at least 100 unless you want evarr ruining every round @@ -30,7 +32,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event var/turf/pickedgoal var/max_i = 10//number of tries to spawn meteor. while(!isspaceturf(pickedstart)) - var/startSide = dir || pick(GLOB.cardinals) + var/startSide = (dir ? dir : pick(GLOB.cardinals)) var/startZ = pick(SSmapping.levels_by_trait(ZTRAIT_STATION)) pickedstart = spaceDebrisStartLoc(startSide, startZ) pickedgoal = spaceDebrisFinishLoc(startSide, startZ) @@ -46,17 +48,17 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event var/startx switch(startSide) if(NORTH) - starty = world.maxy-(TRANSITIONEDGE+2) - startx = rand((TRANSITIONEDGE+2), world.maxx-(TRANSITIONEDGE+2)) + starty = world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD) + startx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD)) if(EAST) - starty = rand((TRANSITIONEDGE+2),world.maxy-(TRANSITIONEDGE+2)) - startx = world.maxx-(TRANSITIONEDGE+2) + starty = rand((TRANSITIONEDGE + MAP_EDGE_PAD),world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD)) + startx = world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD) if(SOUTH) - starty = (TRANSITIONEDGE+2) - startx = rand((TRANSITIONEDGE+2), world.maxx-(TRANSITIONEDGE+2)) + starty = (TRANSITIONEDGE + MAP_EDGE_PAD) + startx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD)) if(WEST) - starty = rand((TRANSITIONEDGE+2), world.maxy-(TRANSITIONEDGE+2)) - startx = (TRANSITIONEDGE+2) + starty = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD)) + startx = (TRANSITIONEDGE + MAP_EDGE_PAD) . = locate(startx, starty, Z) /proc/spaceDebrisFinishLoc(startSide, Z) @@ -64,17 +66,17 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event var/endx switch(startSide) if(NORTH) - endy = (TRANSITIONEDGE+1) - endx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + endy = (TRANSITIONEDGE + MAP_EDGE_PAD) + endx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD)) if(EAST) - endy = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1)) - endx = (TRANSITIONEDGE+1) + endy = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD)) + endx = (TRANSITIONEDGE + MAP_EDGE_PAD) if(SOUTH) - endy = world.maxy-(TRANSITIONEDGE+1) - endx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1)) + endy = world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD) + endx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD)) if(WEST) - endy = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1)) - endx = world.maxx-(TRANSITIONEDGE+1) + endy = rand((TRANSITIONEDGE + MAP_EDGE_PAD),world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD)) + endx = world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD) . = locate(endx, endy, Z) /////////////////////// @@ -82,7 +84,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event ////////////////////// /obj/effect/meteor - name = "the concept of meteor" + name = "\proper the concept of meteor" desc = "You should probably run instead of gawking at this." icon = 'icons/obj/meteor.dmi' icon_state = "small" @@ -92,7 +94,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event var/hitpwr = 2 //Level of ex_act to be called on hit. var/dest pass_flags = PASSTABLE - var/heavy = 0 + var/heavy = FALSE var/meteorsound = 'sound/effects/meteorimpact.ogg' var/z_original var/threat = 0 // used for determining which meteors are most interesting @@ -108,12 +110,12 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event . = ..() //process movement... + var/turf/T = get_turf(loc) if(.)//.. if did move, ram the turf we get in - var/turf/T = get_turf(loc) ram_turf(T) - if(prob(10) && !isspaceturf(T) && !istype(T, /turf/closed/mineral) && !istype(T, /turf/open/floor/plating/asteroid))//randomly takes a 'hit' from ramming - get_hit() + if(prob(10) && !isspaceturf(T) && !istype(T, /turf/closed/mineral) && !istype(T, /turf/open/floor/plating/asteroid))//randomly takes a 'hit' from ramming, and ignore spare ruin aseroids + get_hit() /obj/effect/meteor/Destroy() if (timerid) @@ -135,24 +137,25 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event /obj/effect/meteor/Bump(atom/A) if(A) ram_turf(get_turf(A)) - playsound(src.loc, meteorsound, 40, 1) - if(!istype(A, /turf/closed/mineral) && !istype(A, /turf/open/floor/plating/asteroid)) + playsound(src.loc, meteorsound, 40, TRUE) + if(!istype(A, /turf/closed/mineral) && !istype(A, /turf/open/floor/plating/asteroid)) // ignore localstation ruins get_hit() /obj/effect/meteor/proc/ram_turf(turf/T) //first bust whatever is in the turf - for(var/atom/A in T) - if(A != src) - if(isliving(A)) - A.visible_message("[src] slams into [A].", "[src] slams into you!.") - A.ex_act(hitpwr) + for(var/thing in T) + if(thing == src) + continue + if(isliving(thing)) + var/mob/living/living_thing = thing + living_thing.visible_message("[src] slams into [living_thing].", "[src] slams into you!.") + A.ex_act(hitpwr) //then, ram the turf if it still exists if(T) T.ex_act(hitpwr) - //process getting 'hit' by colliding with a dense object //or randomly when ramming turfs /obj/effect/meteor/proc/get_hit() @@ -162,13 +165,10 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event meteor_effect() qdel(src) -/obj/effect/meteor/ex_act() - return - /obj/effect/meteor/examine(mob/user) + . = ..() if(!(flags_1 & ADMIN_SPAWNED_1) && isliving(user)) - SSmedals.UnlockMedal(MEDAL_METEOR, user.client) - return ..() + user.client.give_award(/datum/award/achievement/misc/meteor_examine, user) /obj/effect/meteor/attackby(obj/item/I, mob/user, params) if(I.tool_behaviour == TOOL_MINING) @@ -232,7 +232,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event name = "big meteor" icon_state = "large" hits = 6 - heavy = 1 + heavy = TRUE dropamt = 4 threat = 10 @@ -245,7 +245,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event name = "flaming meteor" icon_state = "flaming" hits = 5 - heavy = 1 + heavy = TRUE meteorsound = 'sound/effects/bamf.ogg' meteordrop = list(/obj/item/stack/ore/plasma) threat = 20 @@ -258,7 +258,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event /obj/effect/meteor/irradiated name = "glowing meteor" icon_state = "glowing" - heavy = 1 + heavy = TRUE meteordrop = list(/obj/item/stack/ore/uranium) threat = 15 @@ -275,7 +275,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event icon_state = "meateor" desc = "Just... don't think too hard about where this thing came from." hits = 2 - heavy = 1 + heavy = TRUE meteorsound = 'sound/effects/blobattack.ogg' meteordrop = list(/obj/item/reagent_containers/food/snacks/meat/slab/human, /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant, /obj/item/organ/heart, /obj/item/organ/lungs, /obj/item/organ/tongue, /obj/item/organ/appendix/) var/meteorgibs = /obj/effect/gibspawner/generic @@ -327,7 +327,7 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event desc = "Your life briefly passes before your eyes the moment you lay them on this monstrosity." hits = 30 hitpwr = 1 - heavy = 1 + heavy = TRUE meteorsound = 'sound/effects/bamf.ogg' meteordrop = list(/obj/item/stack/ore/plasma) threat = 50 @@ -358,7 +358,7 @@ GLOBAL_LIST_INIT(meteorsSPOOKY, list(/obj/effect/meteor/pumpkin)) icon = 'icons/obj/meteor_spooky.dmi' icon_state = "pumpkin" hits = 10 - heavy = 1 + heavy = TRUE dropamt = 1 meteordrop = list(/obj/item/clothing/head/hardhat/pumpkinhead, /obj/item/reagent_containers/food/snacks/grown/pumpkin) threat = 100 @@ -368,3 +368,4 @@ GLOBAL_LIST_INIT(meteorsSPOOKY, list(/obj/effect/meteor/pumpkin)) meteorsound = pick('sound/hallucinations/im_here1.ogg','sound/hallucinations/im_here2.ogg') ////////////////////////// #undef DEFAULT_METEOR_LIFETIME +#undef MAP_EDGE_PAD diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 1f0687151d..d8a5f7d2c7 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -1,6 +1,6 @@ -#define AUTOLATHE_MAIN_MENU 1 -#define AUTOLATHE_CATEGORY_MENU 2 -#define AUTOLATHE_SEARCH_MENU 3 +#define AUTOLATHE_MAIN_MENU 1 +#define AUTOLATHE_CATEGORY_MENU 2 +#define AUTOLATHE_SEARCH_MENU 3 /obj/machinery/autolathe name = "autolathe" @@ -17,7 +17,7 @@ var/list/L = list() var/list/LL = list() var/hacked = FALSE - var/disabled = 0 + var/disabled = FALSE var/shocked = FALSE var/hack_wire var/disable_wire @@ -27,13 +27,13 @@ var/prod_coeff = 1 var/datum/design/being_built + var/datum/techweb/stored_research var/list/datum/design/matching_designs var/selected_category var/screen = 1 var/base_price = 25 var/hacked_price = 50 - var/datum/techweb/specialized/autounlocking/stored_research = /datum/techweb/specialized/autounlocking/autolathe var/list/categories = list( "Tools", "Electronics", @@ -46,19 +46,13 @@ "Dinnerware", "Imported" ) - var/list/allowed_materials - - /// Base print speed - var/base_print_speed = 10 /obj/machinery/autolathe/Initialize() - var/list/mats = allowed_materials - if(!mats) - mats = SSmaterials.materialtypes_by_category[MAT_CATEGORY_RIGID] - AddComponent(/datum/component/material_container, mats, _show_on_examine=TRUE, _after_insert=CALLBACK(src, .proc/AfterMaterialInsert)) + AddComponent(/datum/component/material_container, SSmaterials.materialtypes_by_category[MAT_CATEGORY_RIGID], 0, TRUE, null, null, CALLBACK(src, .proc/AfterMaterialInsert)) . = ..() + wires = new /datum/wires/autolathe(src) - stored_research = new stored_research + stored_research = new /datum/techweb/specialized/autounlocking/autolathe matching_designs = list() /obj/machinery/autolathe/Destroy() @@ -83,7 +77,7 @@ if(AUTOLATHE_SEARCH_MENU) dat = search_win(user) - var/datum/browser/popup = new(user, name, name, 400, 500) + var/datum/browser/popup = new(user, "autolathe", name, 400, 500) popup.set_content(dat) popup.open() @@ -114,9 +108,9 @@ return TRUE if(istype(O, /obj/item/disk/design_disk)) - user.visible_message("[user] begins to load \the [O] in \the [src]...", - "You begin to load a design from \the [O]...", - "You hear the chatter of a floppy drive.") + user.visible_message("[user] begins to load \the [O] in \the [src]...", + "You begin to load a design from \the [O]...", + "You hear the chatter of a floppy drive.") busy = TRUE var/obj/item/disk/design_disk/D = O if(do_after(user, 14.4, target = src)) @@ -128,14 +122,16 @@ return ..() -/obj/machinery/autolathe/proc/AfterMaterialInsert(obj/item/item_inserted, id_inserted, amount_inserted) + +/obj/machinery/autolathe/proc/AfterMaterialInsert(item_inserted, id_inserted, amount_inserted) if(istype(item_inserted, /obj/item/stack/ore/bluespace_crystal)) use_power(MINERAL_MATERIAL_AMOUNT / 10) - else if(item_inserted.custom_materials?.len && item_inserted.custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)]) + else if(custom_materials && custom_materials.len && custom_materials[SSmaterials.GetMaterialRef(/datum/material/glass)]) flick("autolathe_r",src)//plays glass insertion animation by default otherwise else flick("autolathe_o",src)//plays metal insertion animation + use_power(min(1000, amount_inserted / 100)) updateUsrDialog() @@ -187,7 +183,7 @@ if(materials.materials[i] > 0) list_to_show += i - used_material = input("Choose [used_material]", "Custom Material") as null|anything in list_to_show + used_material = input("Choose [used_material]", "Custom Material") as null|anything in sortList(list_to_show, /proc/cmp_typepaths_asc) if(!used_material) return //Didn't pick any material, so you can't build shit either. custom_materials[used_material] += amount_needed @@ -198,8 +194,8 @@ busy = TRUE use_power(power) icon_state = "autolathe_n" - var/time = is_stack ? 10 : base_print_speed * coeff * multiplier - addtimer(CALLBACK(src, .proc/make_item, power, materials_used, custom_materials, multiplier, coeff, is_stack), time) + var/time = is_stack ? 32 : (32 * coeff * multiplier) ** 0.8 + addtimer(CALLBACK(src, .proc/make_item, power, materials_used, custom_materials, multiplier, coeff, is_stack, usr), time) else to_chat(usr, "Not enough materials for this operation.") @@ -218,10 +214,11 @@ return -/obj/machinery/autolathe/proc/make_item(power, var/list/materials_used, var/list/picked_materials, multiplier, coeff, is_stack) +/obj/machinery/autolathe/proc/make_item(power, list/materials_used, list/picked_materials, multiplier, coeff, is_stack, mob/user) var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) var/atom/A = drop_location() use_power(power) + materials.use_materials(materials_used) if(is_stack) @@ -235,6 +232,11 @@ if(length(picked_materials)) new_item.set_custom_materials(picked_materials, 1 / multiplier) //Ensure we get the non multiplied amount + for(var/x in picked_materials) + var/datum/material/M = x + if(!istype(M, /datum/material/glass) && !istype(M, /datum/material/iron)) + user.client.give_award(/datum/award/achievement/misc/getting_an_upgrade, user) + icon_state = "autolathe" busy = FALSE @@ -246,12 +248,10 @@ T += MB.rating*75000 var/datum/component/material_container/materials = GetComponent(/datum/component/material_container) materials.max_amount = T - var/manips = 0 - var/total_manip_rating = 0 + T=1.2 for(var/obj/item/stock_parts/manipulator/M in component_parts) - total_manip_rating += M.rating - manips++ - prod_coeff = STANDARD_PART_LEVEL_LATHE_COEFFICIENT(total_manip_rating / (manips? manips : 1)) + T -= M.rating*0.2 + prod_coeff = min(1,max(0,T)) // Coeff going 1 -> 0,8 -> 0,6 -> 0,4 /obj/machinery/autolathe/examine(mob/user) . += ..() @@ -376,6 +376,7 @@ return materials.has_materials(required_materials) + /obj/machinery/autolathe/proc/get_design_cost(datum/design/D) var/coeff = (ispath(D.build_path, /obj/item/stack) ? 1 : prod_coeff) var/dat @@ -416,9 +417,7 @@ hacked = state for(var/id in SSresearch.techweb_designs) var/datum/design/D = SSresearch.techweb_design_by_id(id) - if(D.build_type & stored_research.design_autounlock_skip_types) - continue - if((D.build_type & stored_research.design_autounlock_buildtypes) && ("hacked" in D.category)) + if((D.build_type & AUTOLATHE) && ("hacked" in D.category)) if(hacked) stored_research.add_design(D) else @@ -428,19 +427,24 @@ . = ..() adjust_hacked(TRUE) +//Called when the object is constructed by an autolathe +//Has a reference to the autolathe so you can do !!FUN!! things with hacked lathes +/obj/item/proc/autolathe_crafted(obj/machinery/autolathe/A) + return + /obj/machinery/autolathe/secure name = "secured autolathe" desc = "It produces items using metal and glass. This model was reprogrammed without some of the more hazardous designs." circuit = /obj/item/circuitboard/machine/autolathe/secure - stored_research = /datum/techweb/specialized/autounlocking/autolathe/public - base_print_speed = 20 + +/obj/machinery/autolathe/secure/Initialize() + . = ..() + stored_research = new /datum/techweb/specialized/autounlocking/autolathe/public /obj/machinery/autolathe/toy name = "autoylathe" desc = "It produces toys using plastic, metal and glass." circuit = /obj/item/circuitboard/machine/autolathe/toy - - stored_research = /datum/techweb/specialized/autounlocking/autolathe/toy categories = list( "Toys", "Figurines", @@ -453,12 +457,8 @@ "Misc", "Imported" ) - allowed_materials = list( - /datum/material/iron, - /datum/material/glass, - /datum/material/plastic - ) /obj/machinery/autolathe/toy/hacked/Initialize() . = ..() adjust_hacked(TRUE) + stored_research = new /datum/techweb/specialized/autounlocking/autolathe/toy diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm index 077571b931..8085d69f49 100644 --- a/code/game/machinery/computer/arcade.dm +++ b/code/game/machinery/computer/arcade.dm @@ -3,14 +3,7 @@ #define ARCADE_WEIGHT_RARE 1 #define ARCADE_RATIO_PLUSH 0.20 // average 1 out of 6 wins is a plush. -/obj/machinery/computer/arcade - name = "random arcade" - desc = "random arcade machine" - icon_state = "arcade" - icon_keyboard = null - icon_screen = "invaders" - clockwork = TRUE //it'd look weird - var/list/prizes = list( +GLOBAL_LIST_INIT(arcade_prize_pool, list( /obj/item/toy/balloon = ARCADE_WEIGHT_USELESS, /obj/item/toy/beach_ball = ARCADE_WEIGHT_USELESS, /obj/item/toy/cattoy = ARCADE_WEIGHT_USELESS, @@ -70,9 +63,16 @@ /obj/item/clothing/mask/fakemoustache/italian = ARCADE_WEIGHT_RARE, /obj/item/clothing/suit/hooded/wintercoat/ratvar/fake = ARCADE_WEIGHT_TRICK, /obj/item/clothing/suit/hooded/wintercoat/narsie/fake = ARCADE_WEIGHT_TRICK - ) +)) +/obj/machinery/computer/arcade + name = "random arcade" + desc = "random arcade machine" + icon_state = "arcade" + icon_keyboard = "no_keyboard" + icon_screen = "invaders" light_color = LIGHT_COLOR_GREEN + var/list/prize_override /obj/machinery/computer/arcade/proc/Reset() return @@ -96,39 +96,51 @@ prizes[/obj/item/toy/plush/random] = counterlist_sum(prizes) * ARCADE_RATIO_PLUSH Reset() -/obj/machinery/computer/arcade/proc/prizevend(mob/user, list/rarity_classes) - SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "arcade", /datum/mood_event/arcade) +/obj/machinery/computer/arcade/proc/prizevend(mob/user, prizes = 1) + // if(user.mind?.get_skill_level(/datum/skill/gaming) >= SKILL_LEVEL_LEGENDARY && HAS_TRAIT(user, TRAIT_GAMERGOD)) + // visible_message("[user] inputs an intense cheat code!",\ + // "You hear a flurry of buttons being pressed.") + // say("CODE ACTIVATED: EXTRA PRIZES.") + // prizes *= 2 + for(var/i = 0, i < prizes, i++) + SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, "arcade", /datum/mood_event/arcade) + if(prob(0.0001)) //1 in a million + new /obj/item/gun/energy/pulse/prize(src) + visible_message("[src] dispenses.. woah, a gun! Way past cool.", "You hear a chime and a shot.") + user.client.give_award(/datum/award/achievement/misc/pulse, user) + return - if(prob(1) && prob(1) && prob(1)) //Proper 1 in a million - new /obj/item/gun/energy/pulse/prize(src) - SSmedals.UnlockMedal(MEDAL_PULSE, usr.client) + var/prizeselect + if(prize_override) + prizeselect = pickweight(prize_override) + else + prizeselect = pickweight(GLOB.arcade_prize_pool) + var/atom/movable/the_prize = new prizeselect(get_turf(src)) + playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3) + visible_message("[src] dispenses [the_prize]!", "You hear a chime and a clunk.") - if(!contents.len) - var/list/toy_raffle - if(rarity_classes) - for(var/A in prizes) - if(prizes[A] in rarity_classes) - LAZYSET(toy_raffle, A, prizes[A]) - if(!toy_raffle) - toy_raffle = prizes - var/prizeselect = pickweight(toy_raffle) - new prizeselect(src) - - var/atom/movable/prize = pick(contents) - visible_message("[src] dispenses [prize]!", "You hear a chime and a clunk.") - - prize.forceMove(get_turf(src)) /obj/machinery/computer/arcade/emp_act(severity) . = ..() + var/override = FALSE + if(prize_override) + override = TRUE if(stat & (NOPOWER|BROKEN) || . & EMP_PROTECT_SELF) return var/empprize = null - var/num_of_prizes = rand(round(severity/50),round(severity/100)) + var/num_of_prizes = 0 + switch(severity) + if(1) + num_of_prizes = rand(1,4) + if(2) + num_of_prizes = rand(0,2) for(var/i = num_of_prizes; i > 0; i--) - empprize = pickweight(prizes) + if(override) + empprize = pickweight(prize_override) + else + empprize = pickweight(GLOB.arcade_prize_pool) new empprize(loc) explosion(loc, -1, 0, 1+num_of_prizes, flame_range = 1+num_of_prizes) diff --git a/code/game/machinery/computer/arcade/battle.dm b/code/game/machinery/computer/arcade/battle.dm index 96f224f4cf..8240a22290 100644 --- a/code/game/machinery/computer/arcade/battle.dm +++ b/code/game/machinery/computer/arcade/battle.dm @@ -1,130 +1,399 @@ // ** BATTLE ** // - - /obj/machinery/computer/arcade/battle name = "arcade machine" desc = "Does not support Pinball." icon_state = "arcade" circuit = /obj/item/circuitboard/computer/arcade/battle - var/enemy_name = "Space Villain" - var/temp = "Winners don't use space drugs" //Temporary message, for attack messages, etc - var/player_hp = 30 //Player health/attack points - var/player_mp = 10 - var/enemy_hp = 45 //Enemy health/attack points - var/enemy_mp = 20 - var/gameover = FALSE - var/blocked = FALSE //Player cannot attack/heal while set - var/turtle = 0 - var/turn_speed = 5 //Measured in deciseconds. + var/enemy_name = "Space Villain" + ///Enemy health/attack points + var/enemy_hp = 100 + var/enemy_mp = 40 + ///Temporary message, for attack messages, etc + var/temp = "

Winners don't use space drugs

" + ///the list of passive skill the enemy currently has. the actual passives are added in the enemy_setup() proc + var/list/enemy_passive + ///if all the enemy's weakpoints have been triggered becomes TRUE + var/finishing_move = FALSE + ///linked to passives, when it's equal or above the max_passive finishing move will become TRUE + var/pissed_off = 0 + ///the number of passives the enemy will start with + var/max_passive = 3 + ///weapon wielded by the enemy, the shotgun doesn't count. + var/chosen_weapon + + ///Player health + var/player_hp = 85 + ///player magic points + var/player_mp = 20 + ///used to remember the last three move of the player before this turn. + var/list/last_three_move + ///if the enemy or player died. restart the game when TRUE + var/gameover = FALSE + ///the player cannot make any move while this is set to TRUE. should only TRUE during enemy turns. + var/blocked = FALSE + ///used to clear the enemy_action proc timer when the game is restarted + var/timer_id + ///weapon used by the enemy, pure fluff.for certain actions + var/list/weapons + ///unique to the emag mode, acts as a time limit where the player dies when it reaches 0. + var/bomb_cooldown = 19 + +///creates the enemy base stats for a new round along with the enemy passives +/obj/machinery/computer/arcade/battle/proc/enemy_setup(player_skill) + player_hp = 85 + player_mp = 20 + enemy_hp = 100 + enemy_mp = 40 + gameover = FALSE + blocked = FALSE + finishing_move = FALSE + pissed_off = 0 + last_three_move = null + + enemy_passive = list("short_temper" = TRUE, "poisonous" = TRUE, "smart" = TRUE, "shotgun" = TRUE, "magical" = TRUE, "chonker" = TRUE) + for(var/i = LAZYLEN(enemy_passive); i > max_passive; i--) //we'll remove passives from the list until we have the number of passive we want + var/picked_passive = pick(enemy_passive) + LAZYREMOVE(enemy_passive, picked_passive) + + if(LAZYACCESS(enemy_passive, "chonker")) + enemy_hp += 20 + + if(LAZYACCESS(enemy_passive, "shotgun")) + chosen_weapon = "shotgun" + else if(weapons) + chosen_weapon = pick(weapons) + else + chosen_weapon = "null gun" //if the weapons list is somehow empty, shouldn't happen but runtimes are sneaky bastards. + + if(player_skill) + player_hp += player_skill * 2 /obj/machinery/computer/arcade/battle/Reset() + max_passive = 3 var/name_action var/name_part1 var/name_part2 - name_action = pick("Defeat ", "Annihilate ", "Save ", "Strike ", "Stop ", "Destroy ", "Robust ", "Romance ", "Pwn ", "Own ", "Ban ") + if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) + name_action = pick_list(ARCADE_FILE, "rpg_action_halloween") + name_part1 = pick_list(ARCADE_FILE, "rpg_adjective_halloween") + name_part2 = pick_list(ARCADE_FILE, "rpg_enemy_halloween") + weapons = strings(ARCADE_FILE, "rpg_weapon_halloween") + else if(SSevents.holidays && SSevents.holidays[CHRISTMAS]) + name_action = pick_list(ARCADE_FILE, "rpg_action_xmas") + name_part1 = pick_list(ARCADE_FILE, "rpg_adjective_xmas") + name_part2 = pick_list(ARCADE_FILE, "rpg_enemy_xmas") + weapons = strings(ARCADE_FILE, "rpg_weapon_xmas") + else if(SSevents.holidays && SSevents.holidays[VALENTINES]) + name_action = pick_list(ARCADE_FILE, "rpg_action_valentines") + name_part1 = pick_list(ARCADE_FILE, "rpg_adjective_valentines") + name_part2 = pick_list(ARCADE_FILE, "rpg_enemy_valentines") + weapons = strings(ARCADE_FILE, "rpg_weapon_valentines") + else + name_action = pick_list(ARCADE_FILE, "rpg_action") + name_part1 = pick_list(ARCADE_FILE, "rpg_adjective") + name_part2 = pick_list(ARCADE_FILE, "rpg_enemy") + weapons = strings(ARCADE_FILE, "rpg_weapon") - name_part1 = pick("the Automatic ", "Farmer ", "Lord ", "Professor ", "the Cuban ", "the Evil ", "the Dread King ", "the Space ", "Lord ", "the Great ", "Duke ", "General ") - name_part2 = pick("Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "slime", "Griefer", "ERPer", "Lizard Man", "Unicorn", "Bloopers") + enemy_name = ("The " + name_part1 + " " + name_part2) + name = (name_action + " " + enemy_name) - enemy_name = replacetext((name_part1 + name_part2), "the ", "") - name = (name_action + name_part1 + name_part2) + enemy_setup(0) //in the case it's reset we assume the player skill is 0 because the VOID isn't a gamer /obj/machinery/computer/arcade/battle/ui_interact(mob/user) . = ..() + screen_setup(user) + +///sets up the main screen for the user +/obj/machinery/computer/arcade/battle/proc/screen_setup(mob/user) var/dat = "Close" dat += "

[enemy_name]

" - dat += "

[temp]

" + dat += "[temp]" dat += "
Health: [player_hp] | Magic: [player_mp] | Enemy Health: [enemy_hp]
" if (gameover) dat += "
New Game" else - dat += "
Attack | " - dat += "Heal | " - dat += "Recharge Power" + dat += "
Light attack" + dat += "
Defend" + dat += "
Counter attack" + dat += "
Power attack" dat += "
" - var/datum/browser/popup = new(user, "arcade", "Space Villain 2000") - popup.set_content(dat) - popup.open() + if(user.client) //mainly here to avoid a runtime when the player gets gibbed when losing the emag mode. + var/datum/browser/popup = new(user, "arcade", "Space Villain 2000") + popup.set_content(dat) + popup.open() /obj/machinery/computer/arcade/battle/Topic(href, href_list) if(..()) return + var/gamerSkill = 0 + // if(usr?.mind) + // gamerSkill = usr.mind.get_skill_level(/datum/skill/gaming) if (!blocked && !gameover) + var/attackamt = rand(5,7) + rand(0, gamerSkill) + + if(finishing_move) //time to bonk that fucker,cuban pete will sometime survive a finishing move. + attackamt *= 100 + + //light attack suck absolute ass but it doesn't cost any MP so it's pretty good to finish an enemy off if (href_list["attack"]) - blocked = TRUE - var/attackamt = rand(2,6) - temp = "You attack for [attackamt] damage!" - playsound(loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3) - updateUsrDialog() - if(turtle > 0) - turtle-- - - sleep(turn_speed) + temp = "

you do quick jab for [attackamt] of damage!

" enemy_hp -= attackamt - arcade_action(usr) + arcade_action(usr,"attack",attackamt) - else if (href_list["heal"]) - blocked = TRUE - var/pointamt = rand(1,3) - var/healamt = rand(6,8) - temp = "You use [pointamt] magic to heal for [healamt] damage!" - playsound(loc, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3) - updateUsrDialog() - turtle++ + //defend lets you gain back MP and take less damage from non magical attack. + else if(href_list["defend"]) + temp = "

you take a defensive stance and gain back 10 mp!

" + player_mp += 10 + arcade_action(usr,"defend",attackamt) + playsound(src, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3) - sleep(turn_speed) - player_mp -= pointamt - player_hp += healamt - blocked = TRUE - updateUsrDialog() - arcade_action(usr) + //mainly used to counter short temper and their absurd damage, will deal twice the damage the player took of a non magical attack. + else if(href_list["counter_attack"] && player_mp >= 10) + temp = "

you prepare yourself to counter the next attack!

" + player_mp -= 10 + arcade_action(usr,"counter_attack",attackamt) + playsound(src, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3) - else if (href_list["charge"]) - blocked = TRUE - var/chargeamt = rand(4,7) - temp = "You regain [chargeamt] points" - playsound(loc, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3) - player_mp += chargeamt - if(turtle > 0) - turtle-- + else if(href_list["counter_attack"] && player_mp < 10) + temp = "

you don't have the mp necessary to counter attack and defend yourself instead

" + player_mp += 10 + arcade_action(usr,"defend",attackamt) + playsound(src, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3) - updateUsrDialog() - sleep(turn_speed) - arcade_action(usr) + //power attack deals twice the amount of damage but is really expensive MP wise, mainly used with combos to get weakpoints. + else if (href_list["power_attack"] && player_mp >= 20) + temp = "

You attack [enemy_name] with all your might for [attackamt * 2] damage!

" + enemy_hp -= attackamt * 2 + player_mp -= 20 + arcade_action(usr,"power_attack",attackamt) + + else if(href_list["power_attack"] && player_mp < 20) + temp = "

You don't have the mp necessary for a power attack and settle for a light attack!

" + enemy_hp -= attackamt + arcade_action(usr,"attack",attackamt) if (href_list["close"]) usr.unset_machine() usr << browse(null, "window=arcade") else if (href_list["newgame"]) //Reset everything - temp = "New Round" - player_hp = initial(player_hp) - player_mp = initial(player_mp) - enemy_hp = initial(enemy_hp) - enemy_mp = initial(enemy_mp) - gameover = FALSE - turtle = 0 + temp = "

New Round

" if(obj_flags & EMAGGED) Reset() obj_flags &= ~EMAGGED + enemy_setup(gamerSkill) + screen_setup(usr) + + add_fingerprint(usr) updateUsrDialog() return -/obj/machinery/computer/arcade/battle/proc/arcade_action(mob/user) - if ((enemy_mp <= 0) || (enemy_hp <= 0)) +///happens after a player action and before the enemy turn. the enemy turn will be cancelled if there's a gameover. +/obj/machinery/computer/arcade/battle/proc/arcade_action(mob/user,player_stance,attackamt) + screen_setup(user) + blocked = TRUE + if(player_stance == "attack" || player_stance == "power_attack") + if(attackamt > 40) + playsound(src, 'sound/arcade/boom.ogg', 50, TRUE, extrarange = -3) + else + playsound(src, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3) + + timer_id = addtimer(CALLBACK(src, .proc/enemy_action,player_stance,user),1 SECONDS,TIMER_STOPPABLE) + gameover_check(user) + +///the enemy turn, the enemy's action entirely depend on their current passive and a teensy tiny bit of randomness +/obj/machinery/computer/arcade/battle/proc/enemy_action(player_stance,mob/user) + var/list/list_temp = list() + + switch(LAZYLEN(last_three_move)) //we keep the last three action of the player in a list here + if(0 to 2) + LAZYADD(last_three_move, player_stance) + if(3) + for(var/i in 1 to 2) + last_three_move[i] = last_three_move[i + 1] + last_three_move[3] = player_stance + + if(4 to INFINITY) + last_three_move = null //this shouldn't even happen but we empty the list if it somehow goes above 3 + + var/enemy_stance + var/attack_amount = rand(8,10) //making the attack amount not vary too much so that it's easier to see if the enemy has a shotgun + + if(player_stance == "defend") + attack_amount -= 5 + + //if emagged, cuban pete will set up a bomb acting up as a timer. when it reaches 0 the player fucking dies + if(obj_flags & EMAGGED) + switch(bomb_cooldown--) + if(18) + list_temp += "

[enemy_name] takes two valve tank and links them together, what's he planning?

" + if(15) + list_temp += "

[enemy_name] adds a remote control to the tan- ho god is that a bomb?

" + if(12) + list_temp += "

[enemy_name] throws the bomb next to you, you'r too scared to pick it up.

" + if(6) + list_temp += "

[enemy_name]'s hand brushes the remote linked to the bomb, your heart skipped a beat.

" + if(2) + list_temp += "

[enemy_name] is going to press the button! It's now or never!

" + if(0) + player_hp -= attack_amount * 1000 //hey it's a maxcap we might as well go all in + + //yeah I used the shotgun as a passive, you know why? because the shotgun gives +5 attack which is pretty good + if(LAZYACCESS(enemy_passive, "shotgun")) + if(weakpoint_check("shotgun","defend","defend","power_attack")) + list_temp += "

You manage to disarm [enemy_name] with a surprise power attack and shoot him with his shotgun until it runs out of ammo!

" + enemy_hp -= 10 + chosen_weapon = "empty shotgun" + else + attack_amount += 5 + + //heccing chonker passive, only gives more HP at the start of a new game but has one of the hardest weakpoint to trigger. + if(LAZYACCESS(enemy_passive, "chonker")) + if(weakpoint_check("chonker","power_attack","power_attack","power_attack")) + list_temp += "

After a lot of power attacks you manage to tip over [enemy_name] as they fall over their enormous weight

" + enemy_hp -= 30 + + //smart passive trait, mainly works in tandem with other traits, makes the enemy unable to be counter_attacked + if(LAZYACCESS(enemy_passive, "smart")) + if(weakpoint_check("smart","defend","defend","attack")) + list_temp += "

[enemy_name] is confused by your illogical strategy!

" + attack_amount -= 5 + + else if(attack_amount >= player_hp) + player_hp -= attack_amount + list_temp += "

[enemy_name] figures out you are really close to death and finishes you off with their [chosen_weapon]!

" + enemy_stance = "attack" + + else if(player_stance == "counter_attack") + list_temp += "

[enemy_name] is not taking your bait.

" + if(LAZYACCESS(enemy_passive, "short_temper")) + list_temp += "However controlling their hatred of you still takes a toll on their mental and physical health!" + enemy_hp -= 5 + enemy_mp -= 5 + enemy_stance = "defensive" + + //short temper passive trait, gets easily baited into being counter attacked but will bypass your counter when low on HP + if(LAZYACCESS(enemy_passive, "short_temper")) + if(weakpoint_check("short_temper","counter_attack","counter_attack","counter_attack")) + list_temp += "

[enemy_name] is getting frustrated at all your counter attacks and throws a tantrum!

" + enemy_hp -= attack_amount + + else if(player_stance == "counter_attack") + if(!(LAZYACCESS(enemy_passive, "smart")) && enemy_hp > 30) + list_temp += "

[enemy_name] took the bait and allowed you to counter attack for [attack_amount * 2] damage!

" + player_hp -= attack_amount + enemy_hp -= attack_amount * 2 + enemy_stance = "attack" + + else if(enemy_hp <= 30) //will break through the counter when low enough on HP even when smart. + list_temp += "

[enemy_name] is getting tired of your tricks and breaks through your counter with their [chosen_weapon]!

" + player_hp -= attack_amount + enemy_stance = "attack" + + else if(!enemy_stance) + var/added_temp + + if(rand()) + added_temp = "you for [attack_amount + 5] damage!" + player_hp -= attack_amount + 5 + enemy_stance = "attack" + else + added_temp = "the wall, breaking their skull in the process and losing [attack_amount] hp!" //[enemy_name] you have a literal dent in your skull + enemy_hp -= attack_amount + enemy_stance = "attack" + + list_temp += "

[enemy_name] grits their teeth and charge right into [added_temp]

" + + //in the case none of the previous passive triggered, Mainly here to set an enemy stance for passives that needs it like the magical passive. + if(!enemy_stance) + enemy_stance = pick("attack","defensive") + if(enemy_stance == "attack") + player_hp -= attack_amount + list_temp += "

[enemy_name] attacks you for [attack_amount] points of damage with their [chosen_weapon]

" + if(player_stance == "counter_attack") + enemy_hp -= attack_amount * 2 + list_temp += "

You counter [enemy_name]'s attack and deal [attack_amount * 2] points of damage!

" + + if(enemy_stance == "defensive" && enemy_mp < 15) + list_temp += "

[enemy_name] take some time to get some mp back!

" + enemy_mp += attack_amount + + else if (enemy_stance == "defensive" && enemy_mp >= 15 && !(LAZYACCESS(enemy_passive, "magical"))) + list_temp += "

[enemy_name] quickly heal themselves for 5 hp!

" + enemy_mp -= 15 + enemy_hp += 5 + + //magical passive trait, recharges MP nearly every turn it's not blasting you with magic. + if(LAZYACCESS(enemy_passive, "magical")) + if(player_mp >= 50) + list_temp += "

the huge amount of magical energy you have acumulated throws [enemy_name] off balance!

" + enemy_mp = 0 + LAZYREMOVE(enemy_passive, "magical") + pissed_off++ + + else if(LAZYACCESS(enemy_passive, "smart") && player_stance == "counter_attack" && enemy_mp >= 20) + list_temp += "

[enemy_name] blasts you with magic from afar for 10 points of damage before you can counter!

" + player_hp -= 10 + enemy_mp -= 20 + + else if(enemy_hp >= 20 && enemy_mp >= 40 && enemy_stance == "defensive") + list_temp += "

[enemy_name] Blasts you with magic from afar!

" + enemy_mp -= 40 + player_hp -= 30 + enemy_stance = "attack" + + else if(enemy_hp < 20 && enemy_mp >= 20 && enemy_stance == "defensive") //it's a pretty expensive spell so they can't spam it that much + list_temp += "

[enemy_name] heal themselves with magic and gain back 20 hp!

" + enemy_hp += 20 + enemy_mp -= 30 + else + list_temp += "

[enemy_name]'s magical nature lets them get some mp back!

" + enemy_mp += attack_amount + + //poisonous passive trait, while it's less damage added than the shotgun it acts up even when the enemy doesn't attack at all. + if(LAZYACCESS(enemy_passive, "poisonous")) + if(weakpoint_check("poisonous","attack","attack","attack")) + list_temp += "

your flurry of attack throws back the poisonnous gas at [enemy_name] and makes them choke on it!

" + enemy_hp -= 5 + else + list_temp += "

the stinky breath of [enemy_name] hurts you for 3 hp!

" + player_hp -= 3 + + //if all passive's weakpoint have been triggered, set finishing_move to TRUE + if(pissed_off >= max_passive && !finishing_move) + list_temp += "

You have weakened [enemy_name] enough for them to show their weak point, you will do 10 times as much damage with your next attack!

" + finishing_move = TRUE + + playsound(src, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3) + + temp = list_temp.Join() + gameover_check(user) + screen_setup(user) + blocked = FALSE + + +/obj/machinery/computer/arcade/battle/proc/gameover_check(mob/user) + var/xp_gained = 0 + if(enemy_hp <= 0) if(!gameover) + if(timer_id) + deltimer(timer_id) + timer_id = null + if(player_hp <= 0) + player_hp = 1 //let's just pretend the enemy didn't kill you so not both the player and enemy look dead. gameover = TRUE - temp = "[enemy_name] has fallen! Rejoice!" - playsound(loc, 'sound/arcade/win.ogg', 50, TRUE, extrarange = -3) + blocked = FALSE + temp = "

[enemy_name] has fallen! Rejoice!

" + playsound(loc, 'sound/arcade/win.ogg', 50, TRUE) if(obj_flags & EMAGGED) new /obj/effect/spawner/newbomb/timer/syndicate(loc) @@ -133,78 +402,76 @@ log_game("[key_name(usr)] has outbombed Cuban Pete and been awarded a bomb.") Reset() obj_flags &= ~EMAGGED + xp_gained += 100 else prizevend(user) + xp_gained += 50 SSblackbox.record_feedback("nested tally", "arcade_results", 1, list("win", (obj_flags & EMAGGED ? "emagged":"normal"))) - - else if ((obj_flags & EMAGGED) && (turtle >= 4)) - var/boomamt = rand(5,10) - temp = "[enemy_name] throws a bomb, exploding you for [boomamt] damage!" - playsound(loc, 'sound/arcade/boom.ogg', 50, TRUE, extrarange = -3) - player_hp -= boomamt - - else if ((enemy_mp <= 5) && (prob(70))) - var/stealamt = rand(2,3) - temp = "[enemy_name] steals [stealamt] of your power!" - playsound(loc, 'sound/arcade/steal.ogg', 50, TRUE, extrarange = -3) - player_mp -= stealamt - updateUsrDialog() - - if (player_mp <= 0) - gameover = TRUE - sleep(turn_speed) - temp = "You have been drained! GAME OVER" - playsound(loc, 'sound/arcade/lose.ogg', 50, TRUE, extrarange = -3) - if(obj_flags & EMAGGED) - usr.gib() - SSblackbox.record_feedback("nested tally", "arcade_results", 1, list("loss", "mana", (obj_flags & EMAGGED ? "emagged":"normal"))) - - else if ((enemy_hp <= 10) && (enemy_mp > 4)) - temp = "[enemy_name] heals for 4 health!" - playsound(loc, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3) - enemy_hp += 4 - enemy_mp -= 4 - - else - var/attackamt = rand(3,6) - temp = "[enemy_name] attacks for [attackamt] damage!" - playsound(loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3) - player_hp -= attackamt - - if ((player_mp <= 0) || (player_hp <= 0)) + else if(player_hp <= 0) + if(timer_id) + deltimer(timer_id) + timer_id = null gameover = TRUE - temp = "You have been crushed! GAME OVER" - playsound(loc, 'sound/arcade/lose.ogg', 50, TRUE, extrarange = -3) + temp = "

You have been crushed! GAME OVER

" + playsound(loc, 'sound/arcade/lose.ogg', 50, TRUE) + xp_gained += 10//pity points if(obj_flags & EMAGGED) - usr.gib() + var/mob/living/living_user = user + if (istype(living_user)) + living_user.gib() SSblackbox.record_feedback("nested tally", "arcade_results", 1, list("loss", "hp", (obj_flags & EMAGGED ? "emagged":"normal"))) - blocked = FALSE - return + // if(gameover) + // user?.mind?.adjust_experience(/datum/skill/gaming, xp_gained+1)//always gain at least 1 point of XP + + +///used to check if the last three move of the player are the one we want in the right order and if the passive's weakpoint has been triggered yet +/obj/machinery/computer/arcade/battle/proc/weakpoint_check(passive,first_move,second_move,third_move) + if(LAZYLEN(last_three_move) < 3) + return FALSE + + if(last_three_move[1] == first_move && last_three_move[2] == second_move && last_three_move[3] == third_move && LAZYACCESS(enemy_passive, passive)) + LAZYREMOVE(enemy_passive, passive) + pissed_off++ + return TRUE + else + return FALSE + + +/obj/machinery/computer/arcade/battle/Destroy() + enemy_passive = null + weapons = null + last_three_move = null + return ..() //well boys we did it, lists are no more /obj/machinery/computer/arcade/battle/examine_more(mob/user) - to_chat(user, "Scribbled on the side of the Arcade Machine you notice some writing...\ - \nmagical -> >=50 power\ - \nsmart -> defend, defend, light attack\ - \nshotgun -> defend, defend, power attack\ - \nshort temper -> counter, counter, counter\ - \npoisonous -> light attack, light attack, light attack\ - \nchonker -> power attack, power attack, power attack") - return ..() + var/list/msg = list("You notice some writing scribbled on the side of [src]...") + msg += "\tsmart -> defend, defend, light attack" + msg += "\tshotgun -> defend, defend, power attack" + msg += "\tshort temper -> counter, counter, counter" + msg += "\tpoisonous -> light attack, light attack, light attack" + msg += "\tchonker -> power attack, power attack, power attack" + return msg /obj/machinery/computer/arcade/battle/emag_act(mob/user) . = ..() if(obj_flags & EMAGGED) return + to_chat(user, "A mesmerizing Rhumba beat starts playing from the arcade machine's speakers!") - temp = "If you die in the game, you die for real!" - player_hp = 30 - player_mp = 10 - enemy_hp = 45 - enemy_mp = 20 + temp = "

If you die in the game, you die for real!

" + max_passive = 6 + bomb_cooldown = 18 + var/gamerSkill = 0 + // if(usr?.mind) + // gamerSkill = usr.mind.get_skill_level(/datum/skill/gaming) + enemy_setup(gamerSkill) + enemy_hp += 100 //extra HP just to make cuban pete even more bullshit + player_hp += 30 //the player will also get a few extra HP in order to have a fucking chance + + screen_setup(user) gameover = FALSE - blocked = FALSE obj_flags |= EMAGGED diff --git a/code/game/machinery/computer/arcade/minesweeper.dm b/code/game/machinery/computer/arcade/minesweeper.dm index 3f7ef778b0..a7369348dc 100644 --- a/code/game/machinery/computer/arcade/minesweeper.dm +++ b/code/game/machinery/computer/arcade/minesweeper.dm @@ -268,7 +268,7 @@ visible_message("[src] dispenses [itemname]!", "You hear a chime and a clunk.") DISABLE_BITFIELD(obj_flags, EMAGGED) else - var/dope_prizes = (area >= 480) ? list(ARCADE_WEIGHT_RARE) : (area >= 256) ? list(ARCADE_WEIGHT_RARE, ARCADE_WEIGHT_TRICK) : null + var/dope_prizes = (area >= 480) ? 6 : (area >= 256) ? 4 : 2 prizevend(user, dope_prizes) if(game_status == MINESWEEPER_GAME_WON) diff --git a/code/game/machinery/computer/arcade/misc_arcade.dm b/code/game/machinery/computer/arcade/misc_arcade.dm index 50633192ce..5c887e3726 100644 --- a/code/game/machinery/computer/arcade/misc_arcade.dm +++ b/code/game/machinery/computer/arcade/misc_arcade.dm @@ -8,7 +8,7 @@ icon_state = "arcade" circuit = /obj/item/circuitboard/computer/arcade/amputation -/obj/machinery/computer/arcade/amputation/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags) +/obj/machinery/computer/arcade/amputation/on_attack_hand(mob/user) if(!iscarbon(user)) return var/mob/living/carbon/c_user = user @@ -24,8 +24,13 @@ var/obj/item/bodypart/chopchop = c_user.get_bodypart(which_hand) chopchop.dismember() qdel(chopchop) - playsound(loc, 'sound/arcade/win.ogg', 50, TRUE, extrarange = -3) - for(var/i=1; i<=rand(3,5); i++) - prizevend(user) + // user.mind?.adjust_experience(/datum/skill/gaming, 100) + playsound(loc, 'sound/arcade/win.ogg', 50, TRUE) + prizevend(user, rand(3,5)) else to_chat(c_user, "You (wisely) decide against putting your hand in the machine.") + +/obj/machinery/computer/arcade/amputation/festive //dispenses wrapped gifts instead of arcade prizes, also known as the ancap christmas tree + name = "Mediborg's Festive Amputation Adventure" + desc = "A picture of a blood-soaked medical cyborg wearing a Santa hat flashes on the screen. The mediborg has a speech bubble that says, \"Put your hand in the machine if you aren't a coward!\"" + prize_override = list(/obj/item/a_gift/anything = 1) diff --git a/code/game/machinery/computer/arcade/orion_trail.dm b/code/game/machinery/computer/arcade/orion_trail.dm index 441010906c..08ce7d92e5 100644 --- a/code/game/machinery/computer/arcade/orion_trail.dm +++ b/code/game/machinery/computer/arcade/orion_trail.dm @@ -1,5 +1,3 @@ - - // *** THE ORION TRAIL ** // #define ORION_TRAIL_WINTURN 9 @@ -15,6 +13,8 @@ #define ORION_TRAIL_COLLISION "Collision" #define ORION_TRAIL_SPACEPORT "Spaceport" #define ORION_TRAIL_BLACKHOLE "BlackHole" +#define ORION_TRAIL_OLDSHIP "Old Ship" +#define ORION_TRAIL_SEARCH "Old Ship Search" #define ORION_STATUS_START 1 #define ORION_STATUS_NORMAL 2 @@ -44,7 +44,8 @@ ORION_TRAIL_LING = 3, ORION_TRAIL_MALFUNCTION = 2, ORION_TRAIL_COLLISION = 1, - ORION_TRAIL_SPACEPORT = 2 + ORION_TRAIL_SPACEPORT = 2, + ORION_TRAIL_OLDSHIP = 2 ) var/list/stops = list() var/list/stopblurbs = list() @@ -55,13 +56,27 @@ var/gameStatus = ORION_STATUS_START var/canContinueEvent = 0 + var/obj/item/radio/Radio + var/list/gamers = list() + var/killed_crew = 0 + + +/obj/machinery/computer/arcade/orion_trail/Initialize() + . = ..() + Radio = new /obj/item/radio(src) + Radio.listening = 0 + +/obj/machinery/computer/arcade/orion_trail/Destroy() + QDEL_NULL(Radio) + return ..() + /obj/machinery/computer/arcade/orion_trail/kobayashi name = "Kobayashi Maru control computer" desc = "A test for cadets" icon = 'icons/obj/machines/particle_accelerator.dmi' icon_state = "control_boxp" events = list("Raiders" = 3, "Interstellar Flux" = 1, "Illness" = 3, "Breakdown" = 2, "Malfunction" = 2, "Collision" = 1, "Spaceport" = 2) - prizes = list(/obj/item/paper/fluff/holodeck/trek_diploma = 1) + prize_override = list(/obj/item/paper/fluff/holodeck/trek_diploma = 1) settlers = list("Kirk","Worf","Gene") /obj/machinery/computer/arcade/orion_trail/Reset() @@ -96,14 +111,52 @@ event = null gameStatus = ORION_STATUS_NORMAL lings_aboard = 0 + killed_crew = 0 //spaceport junk spaceport_raided = 0 spaceport_freebie = 0 last_spaceport_action = "" -/obj/machinery/computer/arcade/orion_trail/ui_interact(mob/user) +/obj/machinery/computer/arcade/orion_trail/proc/report_player(mob/gamer) + if(gamers[gamer] == -2) + return // enough harassing them + + if(gamers[gamer] == -1) + say("WARNING: Continued antisocial behavior detected: Dispensing self-help literature.") + new /obj/item/paper/pamphlet/violent_video_games(drop_location()) + gamers[gamer]-- + return + + if(!(gamer in gamers)) + gamers[gamer] = 0 + + gamers[gamer]++ // How many times the player has 'prestiged' (massacred their crew) + + if(gamers[gamer] > 2 && prob(20 * gamers[gamer])) + + Radio.set_frequency(FREQ_SECURITY) + Radio.talk_into(src, "SECURITY ALERT: Crewmember [gamer] recorded displaying antisocial tendencies in [get_area(src)]. Please watch for violent behavior.", FREQ_SECURITY) + + Radio.set_frequency(FREQ_MEDICAL) + Radio.talk_into(src, "PSYCH ALERT: Crewmember [gamer] recorded displaying antisocial tendencies in [get_area(src)]. Please schedule psych evaluation.", FREQ_MEDICAL) + + gamers[gamer] = -1 + + gamer.client.give_award(/datum/award/achievement/misc/gamer, gamer) // PSYCH REPORT NOTE: patient kept rambling about how they did it for an "achievement", recommend continued holding for observation + // gamer.mind?.adjust_experience(/datum/skill/gaming, 50) // cheevos make u better + + if(!isnull(GLOB.data_core.general)) + for(var/datum/data/record/R in GLOB.data_core.general) + if(R.fields["name"] == gamer.name) + R.fields["m_stat"] = "*Unstable*" + return + +/obj/machinery/computer/arcade/orion_trail/ui_interact(mob/_user) . = ..() + if (!isliving(_user)) + return + var/mob/living/user = _user if(fuel <= 0 || food <=0 || settlers.len == 0) gameStatus = ORION_STATUS_GAMEOVER event = null @@ -136,6 +189,8 @@ desc = "Learn how our ancestors got to Orion, and have fun in the process!" dat += "

May They Rest In Peace

" + // user?.mind?.adjust_experience(/datum/skill/gaming, 10)//learning from your mistakes is the first rule of roguelikes + else if(event) dat = eventdat else if(gameStatus == ORION_STATUS_NORMAL) @@ -174,20 +229,32 @@ return busy = TRUE + var/gamerSkillLevel = 0 + var/gamerSkill = 0 + var/gamerSkillRands = 0 + + // if(usr?.mind) + // gamerSkillLevel = usr.mind.get_skill_level(/datum/skill/gaming) + // gamerSkill = usr.mind.get_skill_modifier(/datum/skill/gaming, SKILL_PROBS_MODIFIER) + // gamerSkillRands = usr.mind.get_skill_modifier(/datum/skill/gaming, SKILL_RANDS_MODIFIER) + + + var/xp_gained = 0 if (href_list["continue"]) //Continue your travels if(gameStatus == ORION_STATUS_NORMAL && !event && turns != 7) if(turns >= ORION_TRAIL_WINTURN) win(usr) + xp_gained += 34 else food -= (alive+lings_aboard)*2 fuel -= 5 - if(turns == 2 && prob(30)) + if(turns == 2 && prob(30-gamerSkill)) event = ORION_TRAIL_COLLISION event() - else if(prob(75)) + else if(prob(75-gamerSkill)) event = pickweight(events) if(lings_aboard) - if(event == ORION_TRAIL_LING || prob(55)) + if(event == ORION_TRAIL_LING || prob(55-gamerSkill)) event = ORION_TRAIL_LING_ATTACK event() turns += 1 @@ -195,15 +262,18 @@ var/mob/living/carbon/M = usr //for some vars switch(event) if(ORION_TRAIL_RAIDERS) - if(prob(50)) + if(prob(50-gamerSkill)) to_chat(usr, "You hear battle shouts. The tramping of boots on cold metal. Screams of agony. The rush of venting air. Are you going insane?") M.hallucination += 30 else to_chat(usr, "Something strikes you from behind! It hurts like hell and feel like a blunt weapon, but nothing is there...") M.take_bodypart_damage(30) - playsound(loc, 'sound/weapons/genhit2.ogg', 100, 1) + playsound(loc, 'sound/weapons/genhit2.ogg', 100, TRUE) if(ORION_TRAIL_ILLNESS) - var/severity = rand(1,3) //pray to RNGesus. PRAY, PIGS + var/maxSeverity = 3 + // if(gamerSkillLevel >= SKILL_LEVEL_EXPERT) + // maxSeverity = 2 //part of gitting gud is rng mitigation + var/severity = rand(1,maxSeverity) //pray to RNGesus. PRAY, PIGS if(severity == 1) to_chat(M, "You suddenly feel slightly nauseated." ) if(severity == 2) @@ -215,16 +285,16 @@ sleep(30) M.vomit(10, distance = 5) if(ORION_TRAIL_FLUX) - if(prob(75)) + if(prob(75-gamerSkill)) M.DefaultCombatKnockdown(60) say("A sudden gust of powerful wind slams [M] into the floor!") M.take_bodypart_damage(25) - playsound(loc, 'sound/weapons/genhit.ogg', 100, 1) + playsound(loc, 'sound/weapons/genhit.ogg', 100, TRUE) else to_chat(M, "A violent gale blows past you, and you barely manage to stay standing!") if(ORION_TRAIL_COLLISION) //by far the most damaging event - if(prob(90)) - playsound(loc, 'sound/effects/bang.ogg', 100, 1) + if(prob(90-gamerSkill)) + playsound(loc, 'sound/effects/bang.ogg', 100, TRUE) var/turf/open/floor/F for(F in orange(1, src)) F.ScrapeAway() @@ -232,15 +302,15 @@ if(hull) sleep(10) say("A new floor suddenly appears around [src]. What the hell?") - playsound(loc, 'sound/weapons/genhit.ogg', 100, 1) + playsound(loc, 'sound/weapons/genhit.ogg', 100, TRUE) var/turf/open/space/T for(T in orange(1, src)) T.PlaceOnTop(/turf/open/floor/plating) else say("Something slams into the floor around [src] - luckily, it didn't get through!") - playsound(loc, 'sound/effects/bang.ogg', 50, 1) + playsound(loc, 'sound/effects/bang.ogg', 50, TRUE) if(ORION_TRAIL_MALFUNCTION) - playsound(loc, 'sound/effects/empulse.ogg', 50, 1) + playsound(loc, 'sound/effects/empulse.ogg', 50, TRUE) visible_message("[src] malfunctions, randomizing in-game stats!") var/oldfood = food var/oldfuel = fuel @@ -254,7 +324,7 @@ audible_message("[src] lets out a somehow ominous chime.") food = oldfood fuel = oldfuel - playsound(loc, 'sound/machines/chime.ogg', 50, 1) + playsound(loc, 'sound/machines/chime.ogg', 50, TRUE) else if(href_list["newgame"]) //Reset everything if(gameStatus == ORION_STATUS_START) @@ -266,6 +336,10 @@ food = 80 fuel = 60 settlers = list("Harry","Larry","Bob") + else if(href_list["search"]) //search old ship + if(event == ORION_TRAIL_OLDSHIP) + event = ORION_TRAIL_SEARCH + event() else if(href_list["slow"]) //slow down if(event == ORION_TRAIL_FLUX) food -= (alive+lings_aboard)*2 @@ -302,11 +376,11 @@ event = null else if(href_list["blackhole"]) //keep speed past a black hole if(turns == 7) - if(prob(75)) + if(prob(75-gamerSkill)) event = ORION_TRAIL_BLACKHOLE event() if(obj_flags & EMAGGED) - playsound(loc, 'sound/effects/supermatter.ogg', 100, 1) + playsound(loc, 'sound/effects/supermatter.ogg', 100, TRUE) say("A miniature black hole suddenly appears in front of [src], devouring [usr] alive!") if(isliving(usr)) var/mob/living/L = usr @@ -328,22 +402,29 @@ else if(href_list["killcrew"]) //shoot a crewmember if(gameStatus == ORION_STATUS_NORMAL || event == ORION_TRAIL_LING) var/sheriff = remove_crewmember() //I shot the sheriff - playsound(loc,'sound/weapons/gunshot.ogg', 100, 1) + playsound(loc,'sound/weapons/gun/pistol/shot.ogg', 100, TRUE) + killed_crew++ + + var/mob/living/user = usr if(settlers.len == 0 || alive == 0) say("The last crewmember [sheriff], shot themselves, GAME OVER!") if(obj_flags & EMAGGED) - usr.death(0) - obj_flags &= EMAGGED + user.death(FALSE) gameStatus = ORION_STATUS_GAMEOVER event = null + + if(killed_crew >= 4) + xp_gained -= 15//no cheating by spamming game overs + report_player(usr) else if(obj_flags & EMAGGED) if(usr.name == sheriff) say("The crew of the ship chose to kill [usr.name]!") - usr.death(0) + user.death(FALSE) if(event == ORION_TRAIL_LING) //only ends the ORION_TRAIL_LING event, since you can do this action in multiple places event = null + killed_crew-- // the kill was valid //Spaceport specific interactions //they get a header because most of them don't reset event (because it's a shop, you leave when you want to) @@ -356,6 +437,7 @@ fuel -= 10 food -= 10 event() + killed_crew-- // I mean not really but you know else if(href_list["sellcrew"]) //sell a crewmember if(gameStatus == ORION_STATUS_MARKET) @@ -377,15 +459,16 @@ else if(href_list["raid_spaceport"]) if(gameStatus == ORION_STATUS_MARKET) if(!spaceport_raided) - var/success = min(15 * alive,100) //default crew (4) have a 60% chance + var/success = min(15 * alive + gamerSkill,100) //default crew (4) have a 60% chance spaceport_raided = 1 var/FU = 0 var/FO = 0 if(prob(success)) - FU = rand(5,15) - FO = rand(5,15) + FU = rand(5 + gamerSkillRands,15 + gamerSkillRands) + FO = rand(5 + gamerSkillRands,15 + gamerSkillRands) last_spaceport_action = "You successfully raided the spaceport! You gained [FU] Fuel and [FO] Food! (+[FU]FU,+[FO]FO)" + xp_gained += 10 else FU = rand(-5,-15) FO = rand(-5,-15) @@ -444,7 +527,7 @@ add_fingerprint(usr) updateUsrDialog() busy = FALSE - return + // usr?.mind?.adjust_experience(/datum/skill/gaming, xp_gained+1) /obj/machinery/computer/arcade/orion_trail/proc/event() @@ -686,8 +769,279 @@ eventdat += "

Depart Spaceport

" +/obj/machinery/computer/arcade/orion_trail/proc/event() + eventdat = "

[event]

" + canContinueEvent = 0 + switch(event) + if(ORION_TRAIL_RAIDERS) + eventdat += "Raiders have come aboard your ship!" + if(prob(50)) + var/sfood = rand(1,10) + var/sfuel = rand(1,10) + food -= sfood + fuel -= sfuel + eventdat += "
They have stolen [sfood] Food and [sfuel] Fuel." + else if(prob(10)) + var/deadname = remove_crewmember() + eventdat += "
[deadname] tried to fight back, but was killed." + else + eventdat += "
Fortunately, you fended them off without any trouble." + eventdat += "

Continue

" + eventdat += "

Close

" + canContinueEvent = 1 + + if(ORION_TRAIL_FLUX) + eventdat += "This region of space is highly turbulent.
If we go slowly we may avoid more damage, but if we keep our speed we won't waste supplies." + eventdat += "
What will you do?" + eventdat += "

Slow Down Continue

" + eventdat += "

Close

" + + if(ORION_TRAIL_OLDSHIP) + eventdat += "
Your crew spots an old ship floating through space. It might have some supplies, but then again it looks rather unsafe." + eventdat += "

Search itLeave it

Close

" + canContinueEvent = 1 + + if(ORION_TRAIL_SEARCH) + switch(rand(100)) + if(0 to 15) + var/rescued = add_crewmember() + var/oldfood = rand(1,7) + var/oldfuel = rand(4,10) + food += oldfood + fuel += oldfuel + eventdat += "
As you look through it you find some supplies and a living person!" + eventdat += "
[rescued] was rescued from the abandoned ship!" + eventdat += "
You found [oldfood] Food and [oldfuel] Fuel." + if(15 to 35) + var/lfuel = rand(4,7) + var/deadname = remove_crewmember() + fuel -= lfuel + eventdat += "
[deadname] was lost deep in the wreckage, and your own vessel lost [lfuel] Fuel maneuvering to the the abandoned ship." + if(35 to 65) + var/oldfood = rand(5,11) + food += oldfood + engine++ + eventdat += "
You found [oldfood] Food and some parts amongst the wreck." + else + eventdat += "
As you look through the wreck you cannot find much of use." + eventdat += "

Continue

" + eventdat += "

Close

" + canContinueEvent = 1 + + if(ORION_TRAIL_ILLNESS) + eventdat += "A deadly illness has been contracted!" + var/deadname = remove_crewmember() + eventdat += "
[deadname] was killed by the disease." + eventdat += "

Continue

" + eventdat += "

Close

" + canContinueEvent = 1 + + if(ORION_TRAIL_BREAKDOWN) + eventdat += "Oh no! The engine has broken down!" + eventdat += "
You can repair it with an engine part, or you can make repairs for 3 days." + if(engine >= 1) + eventdat += "

Use PartWait

" + else + eventdat += "

Wait

" + eventdat += "

Close

" + + if(ORION_TRAIL_MALFUNCTION) + eventdat += "The ship's systems are malfunctioning!" + eventdat += "
You can replace the broken electronics with spares, or you can spend 3 days troubleshooting the AI." + if(electronics >= 1) + eventdat += "

Use PartWait

" + else + eventdat += "

Wait

" + eventdat += "

Close

" + + if(ORION_TRAIL_COLLISION) + eventdat += "Something hit us! Looks like there's some hull damage." + if(prob(25)) + var/sfood = rand(5,15) + var/sfuel = rand(5,15) + food -= sfood + fuel -= sfuel + eventdat += "
[sfood] Food and [sfuel] Fuel was vented out into space." + if(prob(10)) + var/deadname = remove_crewmember() + eventdat += "
[deadname] was killed by rapid depressurization." + eventdat += "
You can repair the damage with hull plates, or you can spend the next 3 days welding scrap together." + if(hull >= 1) + eventdat += "

Use PartWait

" + else + eventdat += "

Wait

" + eventdat += "

Close

" + + if(ORION_TRAIL_BLACKHOLE) + eventdat += "You were swept away into the black hole." + eventdat += "

Oh...

" + eventdat += "

Close

" + settlers = list() + + if(ORION_TRAIL_LING) + eventdat += "Strange reports warn of changelings infiltrating crews on trips to Orion..." + if(settlers.len <= 2) + eventdat += "
Your crew's chance of reaching Orion is so slim the changelings likely avoided your ship..." + eventdat += "

Continue

" + eventdat += "

Close

" + if(prob(10)) // "likely", I didn't say it was guaranteed! + lings_aboard = min(++lings_aboard,2) + else + if(lings_aboard) //less likely to stack lings + if(prob(20)) + lings_aboard = min(++lings_aboard,2) + else if(prob(70)) + lings_aboard = min(++lings_aboard,2) + + eventdat += "

Kill a Crewmember

" + eventdat += "

Risk it

" + eventdat += "

Close

" + canContinueEvent = 1 + + if(ORION_TRAIL_LING_ATTACK) + if(lings_aboard <= 0) //shouldn't trigger, but hey. + eventdat += "Haha, fooled you, there are no changelings on board!" + eventdat += "
(You should report this to a coder :S)" + else + var/ling1 = remove_crewmember() + var/ling2 = "" + if(lings_aboard >= 2) + ling2 = remove_crewmember() + + eventdat += "Changelings among your crew suddenly burst from hiding and attack!" + if(ling2) + eventdat += "
[ling1] and [ling2]'s arms twist and contort into grotesque blades!" + else + eventdat += "
[ling1]'s arm twists and contorts into a grotesque blade!" + + var/chance2attack = alive*20 + if(prob(chance2attack)) + var/chancetokill = 30*lings_aboard-(5*alive) //eg: 30*2-(10) = 50%, 2 lings, 2 crew is 50% chance + if(prob(chancetokill)) + var/deadguy = remove_crewmember() + var/murder_text = pick("The changeling[ling2 ? "s" : ""] bring[ling2 ? "" : "s"] down [deadguy] and disembowel[ling2 ? "" : "s"] them in a spray of gore!", \ + "[ling2 ? pick(ling1, ling2) : ling1] corners [deadguy] and impales them through the stomach!", \ + "[ling2 ? pick(ling1, ling2) : ling1] decapitates [deadguy] in a single cleaving arc!") + eventdat += "
[murder_text]" + else + eventdat += "

You valiantly fight off the changeling[ling2 ? "s":""]!" + if(ling2) + food += 30 + lings_aboard = max(0,lings_aboard-2) + else + food += 15 + lings_aboard = max(0,--lings_aboard) + eventdat += "
Well, it's perfectly good food...\ +
You cut the changeling[ling2 ? "s" : ""] into meat, gaining [ling2 ? "30" : "15"] Food!" + else + eventdat += "

[pick("Sensing unfavorable odds", "After a failed attack", "Suddenly breaking nerve")], \ + the changeling[ling2 ? "s":""] vanish[ling2 ? "" : "es"] into space through the airlocks! You're safe... for now." + if(ling2) + lings_aboard = max(0,lings_aboard-2) + else + lings_aboard = max(0,--lings_aboard) + + eventdat += "

Continue

" + eventdat += "

Close

" + canContinueEvent = 1 + + + if(ORION_TRAIL_SPACEPORT) + gameStatus = ORION_STATUS_MARKET + if(spaceport_raided) + eventdat += "The spaceport is on high alert! You've been barred from docking by the local authorities after your failed raid." + if(last_spaceport_action) + eventdat += "
Last Spaceport Action: [last_spaceport_action]" + eventdat += "

Depart Spaceport

" + eventdat += "

Close

" + else + eventdat += "Your jump into the sector yields a spaceport - a lucky find!" + eventdat += "
This spaceport is home to travellers who failed to reach Orion, but managed to find a different home..." + eventdat += "
Trading terms: FU = Fuel, FO = Food" + if(last_spaceport_action) + eventdat += "
Last action: [last_spaceport_action]" + eventdat += "

Crew:

" + eventdat += english_list(settlers) + eventdat += "
Food: [food] | Fuel: [fuel]" + eventdat += "
Engine Parts: [engine] | Hull Panels: [hull] | Electronics: [electronics]" + + + //If your crew is pathetic you can get freebies (provided you haven't already gotten one from this port) + if(!spaceport_freebie && (fuel < 20 || food < 20)) + spaceport_freebie++ + var/FU = 10 + var/FO = 10 + var/freecrew = 0 + if(prob(30)) + FU = 25 + FO = 25 + + if(prob(10)) + add_crewmember() + freecrew++ + + eventdat += "
The traders of the spaceport take pity on you, and generously give you some free supplies! (+[FU]FU, +[FO]FO)" + if(freecrew) + eventdat += "
You also gain a new crewmember!" + + fuel += FU + food += FO + + //CREW INTERACTIONS + eventdat += "

Crew Management:

" + + //Buy crew + if(food >= 10 && fuel >= 10) + eventdat += "

Hire a New Crewmember (-10FU, -10FO)

" + else + eventdat += "

You cannot afford a new crewmember.

" + + //Sell crew + if(settlers.len > 1) + eventdat += "

Sell Crew for Fuel and Food (+7FU, +7FO)

" + else + eventdat += "

You have no other crew to sell.

" + + //BUY/SELL STUFF + eventdat += "

Spare Parts:

" + + //Engine parts + if(fuel > 5) + eventdat += "

Buy Engine Parts (-5FU)

" + else + eventdat += "

You cannot afford engine parts." + + //Hull plates + if(fuel > 5) + eventdat += "

Buy Hull Plates (-5FU)

" + else + eventdat += "

You cannot afford hull plates." + + //Electronics + if(fuel > 5) + eventdat += "

Buy Spare Electronics (-5FU)

" + else + eventdat += "

You cannot afford spare electronics." + + //Trade + if(fuel > 5) + eventdat += "

Trade Fuel for Food (-5FU,+5FO)

" + else + eventdat += "

You don't have 5FU to trade. 5) + eventdat += "

Trade Food for Fuel (+5FU,-5FO)

" + else + eventdat += "

You don't have 5FO to trade.You flip the switch on the underside of [src].") active = 1 visible_message("[src] softly beeps and whirs to life!") - playsound(loc, 'sound/machines/defib_SaftyOn.ogg', 25, 1) + playsound(loc, 'sound/machines/defib_SaftyOn.ogg', 25, TRUE) say("This is ship ID #[rand(1,1000)] to Orion Port Authority. We're coming in for landing, over.") sleep(20) visible_message("[src] begins to vibrate...") say("Uh, Port? Having some issues with our reactor, could you check it out? Over.") sleep(30) say("Oh, God! Code Eight! CODE EIGHT! IT'S GONNA BL-") - playsound(loc, 'sound/machines/buzz-sigh.ogg', 25, 1) + playsound(loc, 'sound/machines/buzz-sigh.ogg', 25, TRUE) sleep(3.6) visible_message("[src] explodes!") explosion(loc, 2,4,8, flame_range = 16) diff --git a/code/game/objects/items/his_grace.dm b/code/game/objects/items/his_grace.dm index 6e270b6374..eafa03ca61 100644 --- a/code/game/objects/items/his_grace.dm +++ b/code/game/objects/items/his_grace.dm @@ -12,7 +12,7 @@ lefthand_file = 'icons/mob/inhands/equipment/toolbox_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/toolbox_righthand.dmi' icon = 'icons/obj/items_and_weapons.dmi' - w_class = WEIGHT_CLASS_BULKY + w_class = WEIGHT_CLASS_GIGANTIC force = 12 total_mass = TOTAL_MASS_NORMAL_ITEM // average toolbox attack_verb = list("robusted") @@ -70,19 +70,19 @@ else . += "[src] is latched closed." -/obj/item/his_grace/relaymove(mob/living/user) //Allows changelings, etc. to climb out of Him after they revive, provided He isn't active +/obj/item/his_grace/relaymove(mob/living/user, direction) //Allows changelings, etc. to climb out of Him after they revive, provided He isn't active if(!awakened) user.forceMove(get_turf(src)) user.visible_message("[user] scrambles out of [src]!", "You climb out of [src]!") -/obj/item/his_grace/process() +/obj/item/his_grace/process(delta_time) if(!bloodthirst) drowse() return if(bloodthirst < HIS_GRACE_CONSUME_OWNER && !ascended) - adjust_bloodthirst(1 + FLOOR(LAZYLEN(contents) * 0.5, 1)) //Maybe adjust this? + adjust_bloodthirst((1 + FLOOR(LAZYLEN(contents) * 0.5, 1)) * delta_time) //Maybe adjust this? else - adjust_bloodthirst(1) //don't cool off rapidly once we're at the point where His Grace consumes all. + adjust_bloodthirst(1 * delta_time) //don't cool off rapidly once we're at the point where His Grace consumes all. var/mob/living/master = get_atom_on_turf(src, /mob/living) if(istype(master) && (src in master.held_items)) switch(bloodthirst) @@ -94,7 +94,7 @@ REMOVE_TRAIT(src, TRAIT_NODROP, HIS_GRACE_TRAIT) master.DefaultCombatKnockdown(60) master.adjustBruteLoss(master.maxHealth) - playsound(master, 'sound/effects/splat.ogg', 100, 0) + playsound(master, 'sound/effects/splat.ogg', 100, FALSE) else master.apply_status_effect(STATUS_EFFECT_HISGRACE) return @@ -115,8 +115,8 @@ if(!L.stat) L.visible_message("[src] lunges at [L]!", "[src] lunges at you!") do_attack_animation(L, null, src) - playsound(L, 'sound/weapons/smash.ogg', 50, 1) - playsound(L, 'sound/misc/desceration-01.ogg', 50, 1) + playsound(L, 'sound/weapons/smash.ogg', 50, TRUE) + playsound(L, 'sound/misc/desceration-01.ogg', 50, TRUE) L.adjustBruteLoss(force) adjust_bloodthirst(-5) //Don't stop attacking they're right there! else @@ -137,6 +137,8 @@ move_gracefully() /obj/item/his_grace/proc/move_gracefully() + SIGNAL_HANDLER + if(!awakened) return var/static/list/transforms @@ -161,7 +163,7 @@ return var/turf/T = get_turf(src) T.visible_message("[src] slowly stops rattling and falls still, His latch snapping shut.") - playsound(loc, 'sound/weapons/batonextend.ogg', 100, 1) + playsound(loc, 'sound/weapons/batonextend.ogg', 100, TRUE) name = initial(name) desc = initial(desc) icon_state = initial(icon_state) @@ -178,8 +180,8 @@ var/victims = 0 meal.visible_message("[src] swings open and devours [meal]!", "[src] consumes you!") meal.adjustBruteLoss(200) - playsound(meal, 'sound/misc/desceration-02.ogg', 75, 1) - playsound(src, 'sound/items/eatfood.ogg', 100, 1) + playsound(meal, 'sound/misc/desceration-02.ogg', 75, TRUE) + playsound(src, 'sound/items/eatfood.ogg', 100, TRUE) meal.forceMove(src) force_bonus += HIS_GRACE_FORCE_BONUS prev_bloodthirst = bloodthirst @@ -253,3 +255,4 @@ if(istype(master)) master.visible_message("Gods will be watching.") name = "[master]'s mythical toolbox of three powers" + master.client?.give_award(/datum/award/achievement/misc/ascension, master) diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm index 07900d6bbf..f20fbc21ee 100644 --- a/code/game/objects/items/weaponry.dm +++ b/code/game/objects/items/weaponry.dm @@ -24,9 +24,9 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 */ /obj/item/banhammer/attack(mob/M, mob/user) if(user.zone_selected == BODY_ZONE_HEAD) - M.visible_message("[user] are stroking the head of [M] with a bangammer", "[user] are stroking the head with a bangammer", "you hear a bangammer stroking a head"); + M.visible_message("[user] are stroking the head of [M] with a bangammer.", "[user] are stroking your head with a bangammer.", "You hear a bangammer stroking a head.") // see above comment else - M.visible_message("[M] has been banned FOR NO REISIN by [user]", "You have been banned FOR NO REISIN by [user]", "you hear a banhammer banning someone") + M.visible_message("[M] has been banned FOR NO REISIN by [user]!", "You have been banned FOR NO REISIN by [user]!", "You hear a banhammer banning someone.") playsound(loc, 'sound/effects/adminhelp.ogg', 15) //keep it at 15% volume so people don't jump out of their skin too much if(user.a_intent != INTENT_HELP) return ..(M, user) @@ -89,7 +89,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/claymore/highlander //ALL COMMENTS MADE REGARDING THIS SWORD MUST BE MADE IN ALL CAPS desc = "THERE CAN BE ONLY ONE, AND IT WILL BE YOU!!!\nActivate it in your hand to point to the nearest victim." flags_1 = CONDUCT_1 - item_flags = DROPDEL + item_flags = DROPDEL //WOW BRO YOU LOST AN ARM, GUESS WHAT YOU DONT GET YOUR SWORD ANYMORE //I CANT BELIEVE SPOOKYDONUT WOULD BREAK THE REQUIREMENTS slot_flags = null block_chance = 0 //RNG WON'T HELP YOU NOW, PANSY light_range = 3 @@ -130,8 +130,6 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/claymore/highlander/dropped(mob/living/user) . = ..() user.unignore_slowdown(HIGHLANDER) - if(!QDELETED(src)) - qdel(src) //If this ever happens, it's because you lost an arm /obj/item/claymore/highlander/examine(mob/user) . = ..() @@ -141,8 +139,8 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/claymore/highlander/attack(mob/living/target, mob/living/user) . = ..() - if(!QDELETED(target) && iscarbon(target) && target.stat == DEAD && target.mind && target.mind.special_role == "highlander") - user.fully_heal() //STEAL THE LIFE OF OUR FALLEN FOES + if(!QDELETED(target) && target.stat == DEAD && target.mind && target.mind.special_role == "highlander") + user.fully_heal(admin_revive = FALSE) //STEAL THE LIFE OF OUR FALLEN FOES add_notch(user) target.visible_message("[target] crumbles to dust beneath [user]'s blows!", "As you fall, your body crumbles to dust!") target.dust() @@ -150,9 +148,13 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/claymore/highlander/attack_self(mob/living/user) var/closest_victim var/closest_distance = 255 - for(var/mob/living/carbon/human/H in GLOB.player_list - user) - if(H.client && H.mind.special_role == "highlander" && (!closest_victim || get_dist(user, closest_victim) < closest_distance)) - closest_victim = H + for(var/mob/living/carbon/human/scot in GLOB.player_list - user) + if(scot.mind.special_role == "highlander" && (!closest_victim || get_dist(user, closest_victim) < closest_distance)) + closest_victim = scot + for(var/mob/living/silicon/robot/siliscot in GLOB.player_list - user) + if(siliscot.mind.special_role == "highlander" && (!closest_victim || get_dist(user, closest_victim) < closest_distance)) + closest_victim = siliscot + if(!closest_victim) to_chat(user, "[src] thrums for a moment and falls dark. Perhaps there's nobody nearby.") return @@ -161,7 +163,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/claymore/highlander/run_block(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, final_block_chance, list/block_return) if((attack_type & ATTACK_TYPE_PROJECTILE) && is_energy_reflectable_projectile(object)) return BLOCK_SUCCESS | BLOCK_SHOULD_REDIRECT | BLOCK_PHYSICAL_EXTERNAL | BLOCK_REDIRECTED - return ..() + return ..() //YOU THINK YOUR PUNY LASERS CAN STOP ME? /obj/item/claymore/highlander/proc/add_notch(mob/living/user) //DYNAMIC CLAYMORE PROGRESSION SYSTEM - THIS IS THE FUTURE notches++ @@ -214,7 +216,22 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 remove_atom_colour(ADMIN_COLOUR_PRIORITY) name = new_name - playsound(user, 'sound/items/screwdriver2.ogg', 50, 1) + playsound(user, 'sound/items/screwdriver2.ogg', 50, TRUE) + +/obj/item/claymore/highlander/robot //BLOODTHIRSTY BORGS NOW COME IN PLAID + icon = 'icons/obj/items_cyborg.dmi' + icon_state = "claymore_cyborg" + var/mob/living/silicon/robot/robot + +/obj/item/claymore/highlander/robot/Initialize() + var/obj/item/robot_module/kiltkit = loc + robot = kiltkit.loc + if(!istype(robot)) + qdel(src) + return ..() + +/obj/item/claymore/highlander/robot/process() + loc.layer = LARGE_MOB_LAYER /obj/item/katana name = "katana" @@ -227,7 +244,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK force = 40 throwforce = 10 - w_class = WEIGHT_CLASS_BULKY + w_class = WEIGHT_CLASS_HUGE hitsound = 'sound/weapons/bladeslice.ogg' attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") block_chance = 50 @@ -417,7 +434,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 desc = "A misnomer of sorts, this is effectively a blunt katana made from steelwood, a dense organic wood derived from steelcaps. Why steelwood? Druids can use it. Duh." icon_state = "bokken_steel" item_state = "bokken_steel" - force = 12 + force = 12 stamina_damage_increment = 3 /obj/item/melee/bokken/waki @@ -427,7 +444,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 item_state = "wakibokken" slot_flags = ITEM_SLOT_BELT w_class = WEIGHT_CLASS_NORMAL - force = 6 + force = 6 stamina_damage_increment = 4 block_parry_data = /datum/block_parry_data/bokken/waki default_parry_data = /datum/block_parry_data/bokken/waki @@ -442,7 +459,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 parry_time_perfect_leeway = 1 parry_imperfect_falloff_percent = 7.5 parry_efficiency_to_counterattack = 120 - parry_efficiency_considered_successful = 65 + parry_efficiency_considered_successful = 65 parry_efficiency_perfect = 120 parry_efficiency_perfect_override = list( TEXT_ATTACK_TYPE_PROJECTILE = 30, @@ -455,10 +472,10 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /datum/block_parry_data/bokken/waki/quick_parry //For the parry spammer in you parry_stamina_cost = 2 // Slam that parry button parry_time_active = 2.5 - parry_time_perfect = 1 + parry_time_perfect = 1 parry_time_perfect_leeway = 1 parry_failed_stagger_duration = 1 SECONDS - parry_failed_clickcd_duration = 1 SECONDS + parry_failed_clickcd_duration = 1 SECONDS /datum/block_parry_data/bokken/waki/quick_parry/proj parry_efficiency_perfect_override = list() @@ -468,7 +485,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 desc = "A misnomer of sorts, this is effectively a blunt wakizashi made from steelwood, a dense organic wood derived from steelcaps. Why steelwood? Druids can use it. Duh." icon_state = "wakibokken_steel" item_state = "wakibokken_steel" - force = 8 + force = 8 stamina_damage_increment = 2 /obj/item/melee/bokken/debug @@ -575,7 +592,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 user.put_in_hands(S) to_chat(user, "You fasten the glass shard to the top of the rod with the cable.") - else if(istype(I, /obj/item/assembly/igniter) && !HAS_TRAIT(I, TRAIT_NODROP)) + else if(istype(I, /obj/item/assembly/igniter) && !(HAS_TRAIT(I, TRAIT_NODROP))) var/obj/item/melee/baton/cattleprod/P = new /obj/item/melee/baton/cattleprod remove_item_from_storage(user) @@ -598,13 +615,13 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi' righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi' force = 2 - throwforce = 10 //This is never used on mobs since this has a 100% embed chance. + throwforce = 10 //10 + 2 (WEIGHT_CLASS_SMALL) * 4 (EMBEDDED_IMPACT_PAIN_MULTIPLIER) = 18 damage on hit due to guaranteed embedding throw_speed = 4 embedding = list("pain_mult" = 4, "embed_chance" = 100, "fall_chance" = 0, "embed_chance_turf_mod" = 15) armour_penetration = 40 w_class = WEIGHT_CLASS_SMALL - sharpness = SHARP_EDGED + sharpness = SHARP_POINTY custom_materials = list(/datum/material/iron=500, /datum/material/glass=500) resistance_flags = FIRE_PROOF @@ -639,27 +656,23 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 attack_verb = list("stubbed", "poked") resistance_flags = FIRE_PROOF var/extended = 0 - var/extended_force = 20 - var/extended_throwforce = 23 - var/extended_icon_state = "switchblade_ext" - var/retracted_icon_state = "switchblade" /obj/item/switchblade/attack_self(mob/user) extended = !extended - playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, 1) + playsound(src.loc, 'sound/weapons/batonextend.ogg', 50, TRUE) if(extended) - force = extended_force + force = 20 w_class = WEIGHT_CLASS_NORMAL - throwforce = extended_throwforce - icon_state = extended_icon_state + throwforce = 23 + icon_state = "switchblade_ext" attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") hitsound = 'sound/weapons/bladeslice.ogg' sharpness = SHARP_EDGED else - force = initial(force) + force = 3 w_class = WEIGHT_CLASS_SMALL - throwforce = initial(throwforce) - icon_state = retracted_icon_state + throwforce = 5 + icon_state = "switchblade" attack_verb = list("stubbed", "poked") hitsound = 'sound/weapons/genhit.ogg' sharpness = SHARP_NONE @@ -750,6 +763,10 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 user.visible_message("[user] is inhaling [src]! It looks like [user.p_theyre()] trying to visit the astral plane!") return (OXYLOSS) +// /obj/item/ectoplasm/angelic +// icon = 'icons/obj/wizard.dmi' +// icon_state = "angelplasm" + /obj/item/mounted_chainsaw name = "mounted chainsaw" desc = "A chainsaw that has replaced your arm." @@ -758,7 +775,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 lefthand_file = 'icons/mob/inhands/weapons/chainsaw_lefthand.dmi' righthand_file = 'icons/mob/inhands/weapons/chainsaw_righthand.dmi' item_flags = ABSTRACT | DROPDEL - w_class = WEIGHT_CLASS_BULKY + w_class = WEIGHT_CLASS_HUGE force = 24 throwforce = 0 throw_range = 0 @@ -801,7 +818,13 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/statuebust/Initialize() . = ..() AddElement(/datum/element/art, impressiveness) - addtimer(CALLBACK(src, /datum.proc/_AddElement, list(/datum/element/beauty, 1000)), 0) + // AddComponent(/datum/component/beauty, 1000) + +// /obj/item/statuebust/hippocratic +// name = "hippocrates bust" +// desc = "A bust of the famous Greek physician Hippocrates of Kos, often referred to as the father of western medicine." +// icon_state = "hippocratic" +// impressiveness = 50 /obj/item/tailclub name = "tail club" @@ -825,8 +848,8 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 icon_state = "catwhip" /obj/item/melee/skateboard - name = "improvised skateboard" - desc = "A skateboard. It can be placed on its wheels and ridden, or used as a strong weapon." + name = "skateboard" + desc = "A skateboard. It can be placed on its wheels and ridden, or used as a radical weapon." icon_state = "skateboard" item_state = "skateboard" force = 12 @@ -839,17 +862,22 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/melee/skateboard/attack_self(mob/user) if(!user.canUseTopic(src, TRUE, FALSE, TRUE)) return - var/obj/vehicle/ridden/scooter/skateboard/S = new board_item_type(get_turf(user)) + var/obj/vehicle/ridden/scooter/skateboard/S = new board_item_type(get_turf(user))//this probably has fucky interactions with telekinesis but for the record it wasn't my fault S.buckle_mob(user) qdel(src) +/obj/item/melee/skateboard/improvised + name = "improvised skateboard" + desc = "A jury-rigged skateboard. It can be placed on its wheels and ridden, or used as a radical weapon." + // board_item_type = /obj/vehicle/ridden/scooter/skateboard/improvised + /obj/item/melee/skateboard/pro name = "skateboard" - desc = "A RaDSTORMz brand professional skateboard. It looks sturdy and well made." + desc = "An EightO brand professional skateboard. It looks sturdy and well made." icon_state = "skateboard2" item_state = "skateboard2" board_item_type = /obj/vehicle/ridden/scooter/skateboard/pro - custom_premium_price = 500 + custom_premium_price = PAYCHECK_HARD * 5 /obj/item/melee/skateboard/hoverboard name = "hoverboard" @@ -857,10 +885,10 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 icon_state = "hoverboard_red" item_state = "hoverboard_red" board_item_type = /obj/vehicle/ridden/scooter/skateboard/hoverboard - custom_premium_price = 2015 + custom_premium_price = PAYCHECK_COMMAND * 5.4 //If I can't make it a meme I'll make it RAD /obj/item/melee/skateboard/hoverboard/admin - name = "\improper Board Of Directors" + name = "Board Of Directors" desc = "The engineering complexity of a spaceship concentrated inside of a board. Just as expensive, too." icon_state = "hoverboard_nt" item_state = "hoverboard_nt" @@ -879,11 +907,19 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 throwforce = 12 attack_verb = list("beat", "smacked") custom_materials = list(/datum/material/wood = MINERAL_MATERIAL_AMOUNT * 3.5) - w_class = WEIGHT_CLASS_BULKY + w_class = WEIGHT_CLASS_HUGE var/homerun_ready = 0 var/homerun_able = 0 total_mass = 2.7 //a regular wooden major league baseball bat weighs somewhere between 2 to 3.4 pounds, according to google +/obj/item/melee/baseball_bat/Initialize() + . = ..() + if(prob(1)) + name = "cricket bat" + desc = "You've got red on you." + icon_state = "baseball_bat_brit" + item_state = "baseball_bat_brit" + /obj/item/melee/baseball_bat/chaplain name = "blessed baseball bat" desc = "There ain't a cult in the league that can withstand a swatter." @@ -908,11 +944,11 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 ..() return if(homerun_ready) - to_chat(user, "You're already ready to do a home run!") + to_chat(user, "You're already ready to do a home run!") ..() return to_chat(user, "You begin gathering strength...") - playsound(get_turf(src), 'sound/magic/lightning_chargeup.ogg', 65, 1) + playsound(get_turf(src), 'sound/magic/lightning_chargeup.ogg', 65, TRUE) if(do_after(user, 90, target = src)) to_chat(user, "You gather power! Time for a home run!") homerun_ready = 1 @@ -920,12 +956,14 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/melee/baseball_bat/attack(mob/living/target, mob/living/user) . = ..() + if(HAS_TRAIT(user, TRAIT_PACIFISM)) + return var/atom/throw_target = get_edge_target_turf(target, user.dir) if(homerun_ready) user.visible_message("It's a home run!") target.throw_at(throw_target, rand(8,10), 14, user) target.ex_act(EXPLODE_HEAVY) - playsound(get_turf(src), 'sound/weapons/homerun.ogg', 100, 1) + playsound(get_turf(src), 'sound/weapons/homerun.ogg', 100, TRUE) homerun_ready = 0 return else if(!target.anchored) @@ -956,7 +994,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/melee/flyswatter name = "flyswatter" - desc = "Useful for killing insects of all sizes." + desc = "Useful for killing pests of all sizes." icon = 'icons/obj/items_and_weapons.dmi' icon_state = "flyswatter" item_state = "flyswatter" @@ -969,7 +1007,6 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 w_class = WEIGHT_CLASS_SMALL //Things in this list will be instantly splatted. Flyman weakness is handled in the flyman species weakness proc. var/list/strong_against - var/list/spider_panic /obj/item/melee/flyswatter/Initialize() . = ..() @@ -977,13 +1014,11 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /mob/living/simple_animal/hostile/poison/bees/, /mob/living/simple_animal/butterfly, /mob/living/simple_animal/cockroach, - /obj/item/queen_bee - )) - spider_panic = typecacheof(list( - /mob/living/simple_animal/banana_spider, - /mob/living/simple_animal/hostile/poison/giant_spider, + /obj/item/queen_bee, + /obj/structure/spider/spiderling )) + /obj/item/melee/flyswatter/afterattack(atom/target, mob/user, proximity_flag) . = ..() if(proximity_flag) @@ -993,11 +1028,6 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 if(istype(target, /mob/living/)) var/mob/living/bug = target bug.death(1) - if(is_type_in_typecache(target, spider_panic)) - to_chat(user, "You easily land a critical blow on the [target].") - if(istype(target, /mob/living/)) - var/mob/living/bug = target - bug.adjustBruteLoss(35) //What kinda mad man would go into melee with a spider?! else qdel(target) @@ -1007,7 +1037,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 icon_state = "madeyoulook" force = 0 throwforce = 0 - item_flags = DROPDEL | ABSTRACT + item_flags = DROPDEL | ABSTRACT // | HAND_ITEM attack_verb = list("bopped") /obj/item/circlegame/Initialize() @@ -1030,6 +1060,8 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /// Stage 1: The mistake is made /obj/item/circlegame/proc/ownerExamined(mob/living/owner, mob/living/sucker) + SIGNAL_HANDLER + if(!istype(sucker) || !in_range(owner, sucker)) return addtimer(CALLBACK(src, .proc/waitASecond, owner, sucker), 4) @@ -1076,6 +1108,10 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 to_chat(owner, "[sucker] looks down at your [src.name] before trying to avert [sucker.p_their()] eyes, but it's too late!") to_chat(sucker, "[owner] sees the fear in your eyes as you try to look away from [owner.p_their()] [src.name]!") + owner.face_atom(sucker) + if(owner.client) + owner.client.give_award(/datum/award/achievement/misc/gottem, owner) // then everybody clapped + playsound(get_turf(owner), 'sound/effects/hit_punch.ogg', 50, TRUE, -1) owner.do_attack_animation(sucker) @@ -1103,7 +1139,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 item_state = "nothing" force = 0 throwforce = 0 - item_flags = DROPDEL | ABSTRACT + item_flags = DROPDEL | ABSTRACT // | HAND_ITEM attack_verb = list("slapped") hitsound = 'sound/effects/snap.ogg' @@ -1112,16 +1148,12 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 var/mob/living/carbon/human/L = M if(L && L.dna && L.dna.species) L.dna.species.stop_wagging_tail(M) - if(user.a_intent != INTENT_HARM && ((user.zone_selected == BODY_ZONE_PRECISE_MOUTH) || (user.zone_selected == BODY_ZONE_PRECISE_EYES) || (user.zone_selected == BODY_ZONE_HEAD))) - user.do_attack_animation(M) - playsound(M, 'sound/weapons/slap.ogg', 50, 1, -1) - user.visible_message("[user] slaps [M]!", - "You slap [M]!",\ - "You hear a slap.") - return - else - ..() - + user.do_attack_animation(M) + playsound(M, 'sound/weapons/slap.ogg', 50, TRUE, -1) + user.visible_message("[user] slaps [M]!", + "You slap [M]!",\ + "You hear a slap.") + return /obj/item/proc/can_trigger_gun(mob/living/user) if(!user.can_use_guns(src)) return FALSE @@ -1138,6 +1170,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 force = 0 throwforce = 5 reach = 2 + var/min_reach = 2 /obj/item/extendohand/acme name = "\improper ACME Extendo-Hand" @@ -1145,13 +1178,26 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /obj/item/extendohand/attack(atom/M, mob/living/carbon/human/user) var/dist = get_dist(M, user) - if(dist < reach) + if(dist < min_reach) to_chat(user, "[M] is too close to use [src] on.") return M.attack_hand(user) -//HF blade +// /obj/item/gohei +// name = "gohei" +// desc = "A wooden stick with white streamers at the end. Originally used by shrine maidens to purify things. Now used by the station's valued weeaboos." +// force = 5 +// throwforce = 5 +// hitsound = "swing_hit" +// attack_verb_continuous = list("whacks", "thwacks", "wallops", "socks") +// attack_verb_simple = list("whack", "thwack", "wallop", "sock") +// icon = 'icons/obj/items_and_weapons.dmi' +// icon_state = "gohei" +// inhand_icon_state = "gohei" +// lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi' +// righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi' +//HF blade /obj/item/vibro_weapon icon_state = "hfrequency0" lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi' @@ -1160,6 +1206,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 desc = "A potent weapon capable of cutting through nearly anything. Wielding it in two hands will allow you to deflect gunfire." armour_penetration = 100 block_chance = 40 + force = 20 throwforce = 20 throw_speed = 4 sharpness = SHARP_EDGED @@ -1182,10 +1229,14 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301 /// triggered on wield of two handed item /obj/item/vibro_weapon/proc/on_wield(obj/item/source, mob/user) + SIGNAL_HANDLER + wielded = TRUE /// triggered on unwield of two handed item /obj/item/vibro_weapon/proc/on_unwield(obj/item/source, mob/user) + SIGNAL_HANDLER + wielded = FALSE /obj/item/vibro_weapon/update_icon_state() diff --git a/code/game/objects/structures/lavaland/necropolis_tendril.dm b/code/game/objects/structures/lavaland/necropolis_tendril.dm index edc4f0c91f..67341160de 100644 --- a/code/game/objects/structures/lavaland/necropolis_tendril.dm +++ b/code/game/objects/structures/lavaland/necropolis_tendril.dm @@ -52,12 +52,12 @@ GLOBAL_LIST_INIT(tendrils, list()) last_tendril = FALSE if(last_tendril && !(flags_1 & ADMIN_SPAWNED_1)) - if(SSmedals.hub_enabled) + if(SSachievements.achievements_enabled) for(var/mob/living/L in view(7,src)) if(L.stat || !L.client) continue - SSmedals.UnlockMedal("[BOSS_MEDAL_TENDRIL] [ALL_KILL_MEDAL]", L.client) - SSmedals.SetScore(TENDRIL_CLEAR_SCORE, L.client, 1) + L.client.give_award(/datum/award/achievement/boss/tendril_exterminator, L) + L.client.give_award(/datum/award/score/tendril_score, L) //Progresses score by one GLOB.tendrils -= src QDEL_NULL(emitted_light) QDEL_NULL(gps) diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 2214f9e4bb..23900e8913 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -47,14 +47,19 @@ if(open) GM.visible_message("[user] starts to give [GM] a swirlie!", "[user] starts to give you a swirlie...") swirlie = GM - if(do_after(user, 30, 0, target = src)) - GM.visible_message("[user] gives [GM] a swirlie!", "[user] gives you a swirlie!", "You hear a toilet flushing.") + var/was_alive = (swirlie.stat != DEAD) + if(do_after(user, 3 SECONDS, target = src, timed_action_flags = IGNORE_HELD_ITEM)) + GM.visible_message("[user] gives [GM] a swirlie!", "[user] gives you a swirlie!", "You hear a toilet flushing.") if(iscarbon(GM)) var/mob/living/carbon/C = GM if(!C.internal) + log_combat(user, C, "swirlied (oxy)") C.adjustOxyLoss(5) else + log_combat(user, GM, "swirlied (oxy)") GM.adjustOxyLoss(5) + if(was_alive && swirlie.stat == DEAD && swirlie.client) + swirlie.client.give_award(/datum/award/achievement/misc/swirlie, swirlie) // just like space high school all over again! swirlie = null else playsound(src.loc, 'sound/effects/bang.ogg', 25, 1) diff --git a/code/game/turfs/simulated/minerals.dm b/code/game/turfs/simulated/minerals.dm index 0f1ec6fa85..e75c7dde55 100644 --- a/code/game/turfs/simulated/minerals.dm +++ b/code/game/turfs/simulated/minerals.dm @@ -872,20 +872,18 @@ /turf/closed/mineral/strong/gets_drilled(mob/user) if(!ishuman(user)) return // see attackby - /* var/mob/living/carbon/human/H = user - if(!(H.mind.get_skill_level(/datum/skill/mining) >= SKILL_LEVEL_MASTER)) - return - */ + // if(!(H.mind?.get_skill_level(/datum/skill/mining) >= SKILL_LEVEL_MASTER)) + // return drop_ores() -// H.client.give_award(/datum/award/achievement/skill/legendary_miner, H) + H.client.give_award(/datum/award/achievement/skill/legendary_miner, H) var/flags = NONE if(defer_change) // TODO: make the defer change var a var for any changeturf flag flags = CHANGETURF_DEFER_CHANGE ScrapeAway(flags=flags) addtimer(CALLBACK(src, .proc/AfterChange), 1, TIMER_UNIQUE) playsound(src, 'sound/effects/break_stone.ogg', 50, TRUE) //beautiful destruction -// H.mind.adjust_experience(/datum/skill/mining, 100) //yay! + // H.mind?.adjust_experience(/datum/skill/mining, 100) //yay! /turf/closed/mineral/strong/proc/drop_ores() if(prob(10)) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index e2c12353f7..43feed2811 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -6,13 +6,21 @@ GLOBAL_PROTECT(admin_verbs_default) return list( /client/proc/deadmin, /*destroys our own admin datum so we can play as a regular player*/ /client/proc/cmd_admin_say, /*admin-only ooc chat*/ - /client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/ - /client/proc/deadchat, - /client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/ + /client/proc/hide_verbs, /*hides all our adminverbs*/ + /client/proc/hide_most_verbs, /*hides all our hideable adminverbs*/ /client/proc/debug_variables, /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/ - /client/proc/toggleprayers, - /client/proc/toggleadminhelpsound, - /client/proc/debugstatpanel, + /client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/ + /client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/ + /client/proc/secrets, + /client/proc/toggle_hear_radio, /*allows admins to hide all radio output*/ + /client/proc/reload_admins, + /client/proc/reestablish_db_connection, /*reattempt a connection to the database*/ + /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/ + /client/proc/cmd_admin_pm_panel, /*admin-pm list*/ + /client/proc/stop_sounds, + /client/proc/mark_datum_mapview, + /client/proc/debugstatpanel + // /client/proc/fix_air /*resets air in designated radius to its default atmos composition*/ ) GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin()) GLOBAL_PROTECT(admin_verbs_admin) @@ -24,6 +32,7 @@ GLOBAL_PROTECT(admin_verbs_admin) /datum/verbs/menu/Admin/verb/playerpanel, /client/proc/game_panel, /*game panel, allows to change game-mode etc*/ /client/proc/check_ai_laws, /*shows AI and borg laws*/ + // /client/proc/ghost_pool_protection, /*opens a menu for toggling ghost roles*/ /datum/admins/proc/toggleooc, /*toggles ooc on/off for everyone*/ /datum/admins/proc/toggleooclocal, /*toggles looc on/off for everyone*/ /datum/admins/proc/toggleoocdead, /*toggles ooc on/off for everyone who is dead*/ @@ -35,15 +44,15 @@ GLOBAL_PROTECT(admin_verbs_admin) /client/proc/admin_ghost, /*allows us to ghost/reenter body at will*/ /client/proc/toggle_view_range, /*changes how far we can see*/ /client/proc/getserverlogs, /*for accessing server logs*/ - /client/proc/cmd_admin_subtle_message, /*send an message to somebody as a 'voice in their head'*/ - /client/proc/cmd_admin_headset_message, /*send an message to somebody through their headset as CentCom*/ + /client/proc/getcurrentlogs, /*for accessing server logs for the current round*/ + /client/proc/cmd_admin_subtle_message, /*send a message to somebody as a 'voice in their head'*/ + /client/proc/cmd_admin_headset_message, /*send a message to somebody through their headset as CentCom*/ /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/ /client/proc/cmd_admin_check_contents, /*displays the contents of an instance*/ /client/proc/centcom_podlauncher,/*Open a window to launch a Supplypod and configure it or it's contents*/ /client/proc/check_antagonists, /*shows all antags*/ /datum/admins/proc/access_news_network, /*allows access of newscasters*/ /client/proc/jumptocoord, /*we ghost and jump to a coordinate*/ - /client/proc/getcurrentlogs, /*for accessing server logs for the current round*/ /client/proc/Getmob, /*teleports a mob to our location*/ /client/proc/Getkey, /*teleports a mob with a certain ckey to our location*/ // /client/proc/sendmob, /*sends a mob somewhere*/ -Removed due to it needing two sorting procs to work, which were executed every time an admin right-clicked. ~Errorage @@ -53,6 +62,8 @@ GLOBAL_PROTECT(admin_verbs_admin) /client/proc/jumptoturf, /*allows us to jump to a specific turf*/ /client/proc/admin_call_shuttle, /*allows us to call the emergency shuttle*/ /client/proc/admin_cancel_shuttle, /*allows us to cancel the emergency shuttle, sending it back to centcom*/ + // /client/proc/admin_disable_shuttle, /*allows us to disable the emergency shuttle admin-wise so that it cannot be called*/ + // /client/proc/admin_enable_shuttle, /*undoes the above*/ /client/proc/cmd_admin_direct_narrate, /*send text directly to a player with no padding. Useful for narratives and fluff-text*/ /client/proc/cmd_admin_world_narrate, /*sends text to all players with no padding*/ /client/proc/cmd_admin_local_narrate, /*sends text to all mobs within view of atom*/ @@ -64,26 +75,18 @@ GLOBAL_PROTECT(admin_verbs_admin) /client/proc/toggle_combo_hud, // toggle display of the combination pizza antag and taco sci/med/eng hud /client/proc/toggle_AI_interact, /*toggle admin ability to interact with machines as an AI*/ /datum/admins/proc/open_shuttlepanel, /* Opens shuttle manipulator UI */ + /client/proc/deadchat, + /client/proc/toggleprayers, + // /client/proc/toggle_prayer_sound, + // /client/proc/colorasay, + // /client/proc/resetasaycolor, + /client/proc/toggleadminhelpsound, /client/proc/respawn_character, - /client/proc/secrets, - /client/proc/toggle_hear_radio, /*allows admins to hide all radio output*/ - /client/proc/reload_admins, - /client/proc/reestablish_db_connection, /*reattempt a connection to the database*/ - /client/proc/cmd_admin_pm_context, /*right-click adminPM interface*/ - /client/proc/cmd_admin_pm_panel, /*admin-pm list*/ - /client/proc/panicbunker, - /client/proc/addbunkerbypass, - /client/proc/revokebunkerbypass, - /client/proc/stop_sounds, - /client/proc/mark_datum_mapview, - /client/proc/hide_verbs, /*hides all our adminverbs*/ - /client/proc/hide_most_verbs, /*hides all our hideable adminverbs*/ - /datum/admins/proc/open_borgopanel, - /client/proc/admin_cmd_respawn_return_to_lobby, - /client/proc/admin_cmd_remove_ghost_respawn_timer + /client/proc/admin_cmd_respawn_return_to_lobby, //CIT + /client/proc/admin_cmd_remove_ghost_respawn_timer, //CIT + /datum/admins/proc/open_borgopanel ) - -GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/DB_ban_panel, /client/proc/stickybanpanel)) +GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/DB_ban_panel, /client/proc/stickybanpanel)) GLOBAL_PROTECT(admin_verbs_ban) GLOBAL_LIST_INIT(admin_verbs_sounds, list(/client/proc/play_local_sound, /client/proc/play_sound, /client/proc/manual_play_web_sound, /client/proc/set_round_end_sound)) GLOBAL_PROTECT(admin_verbs_sounds) @@ -110,13 +113,14 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list( /client/proc/show_tip, /client/proc/smite, /client/proc/admin_away, - /client/proc/cmd_admin_toggle_fov, + /client/proc/cmd_admin_toggle_fov, //CIT CHANGE - FOV /client/proc/roll_dices //CIT CHANGE - Adds dice verb )) GLOBAL_PROTECT(admin_verbs_fun) GLOBAL_LIST_INIT(admin_verbs_spawn, list(/datum/admins/proc/spawn_atom, /datum/admins/proc/podspawn_atom, /datum/admins/proc/spawn_cargo, /datum/admins/proc/spawn_objasmob, /client/proc/respawn_character)) GLOBAL_PROTECT(admin_verbs_spawn) GLOBAL_LIST_INIT(admin_verbs_server, world.AVerbsServer()) +GLOBAL_PROTECT(admin_verbs_server) /world/proc/AVerbsServer() return list( /datum/admins/proc/startnow, @@ -126,17 +130,22 @@ GLOBAL_LIST_INIT(admin_verbs_server, world.AVerbsServer()) /datum/admins/proc/toggleaban, /client/proc/everyone_random, /datum/admins/proc/toggleAI, - /datum/admins/proc/toggleMulticam, - /datum/admins/proc/toggledynamicvote, + /datum/admins/proc/toggleMulticam, //CIT + /datum/admins/proc/toggledynamicvote, //CIT /client/proc/cmd_admin_delete, /*delete an instance/object/mob/etc*/ /client/proc/cmd_debug_del_all, /client/proc/toggle_random_events, /client/proc/forcerandomrotate, /client/proc/adminchangemap, - /client/proc/toggle_hub + /client/proc/panicbunker, + /client/proc/addbunkerbypass, //CIT + /client/proc/revokebunkerbypass, //CIT + // /client/proc/toggle_interviews, + /client/proc/toggle_hub, + /client/proc/toggle_cdn ) -GLOBAL_PROTECT(admin_verbs_server) GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug()) +GLOBAL_PROTECT(admin_verbs_debug) /world/proc/AVerbsDebug() return list( /client/proc/restart_controller, @@ -174,27 +183,37 @@ GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug()) /client/proc/cmd_display_init_log, /client/proc/cmd_display_overlay_log, /client/proc/reload_configuration, + // /client/proc/atmos_control, + // /client/proc/reload_cards, + // /client/proc/validate_cards, + // /client/proc/test_cardpack_distribution, + // /client/proc/print_cards, + #ifdef TESTING + /client/proc/check_missing_sprites, + #endif /datum/admins/proc/create_or_modify_area, #ifdef REFERENCE_TRACKING /datum/admins/proc/view_refs, /datum/admins/proc/view_del_failures, #endif - /client/proc/generate_wikichem_list, //DO NOT PRESS UNLESS YOU WANT SUPERLAG + // /client/proc/check_timer_sources, + /client/proc/toggle_cdn, + /client/proc/generate_wikichem_list //DO NOT PRESS UNLESS YOU WANT SUPERLAG ) -GLOBAL_PROTECT(admin_verbs_debug) GLOBAL_LIST_INIT(admin_verbs_possess, list(/proc/possess, /proc/release)) GLOBAL_PROTECT(admin_verbs_possess) GLOBAL_LIST_INIT(admin_verbs_permissions, list(/client/proc/edit_admin_permissions)) GLOBAL_PROTECT(admin_verbs_permissions) GLOBAL_LIST_INIT(admin_verbs_poll, list(/client/proc/create_poll)) +GLOBAL_PROTECT(admin_verbs_poll) //verbs which can be hidden - needs work -GLOBAL_PROTECT(admin_verbs_poll) GLOBAL_LIST_INIT(admin_verbs_hideable, list( /client/proc/set_ooc, /client/proc/reset_ooc, /client/proc/deadmin, /datum/admins/proc/show_traitor_panel, + // /datum/admins/proc/show_skill_panel, /datum/admins/proc/toggleenter, /datum/admins/proc/toggleguests, /datum/admins/proc/announce, @@ -239,7 +258,7 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list( /client/proc/Debug2, /client/proc/reload_admins, /client/proc/cmd_debug_make_powernets, - /client/proc/startSinglo, + /client/proc/startSinglo, // tg removed this /client/proc/cmd_debug_mob_lists, /client/proc/cmd_debug_del_all, /client/proc/enable_debug_verbs, @@ -247,8 +266,9 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list( /proc/release, /client/proc/reload_admins, /client/proc/panicbunker, - /client/proc/addbunkerbypass, - /client/proc/revokebunkerbypass, + /client/proc/addbunkerbypass, //CIT + /client/proc/revokebunkerbypass, //CIT + // /client/proc/toggle_interviews, /client/proc/admin_change_sec_level, /client/proc/toggle_nuke, /client/proc/cmd_display_del_log, @@ -322,7 +342,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) remove_verb(src, /client/proc/hide_most_verbs) add_verb(src, /client/proc/show_verbs) - to_chat(src, "Most of your adminverbs have been hidden.") + to_chat(src, "Most of your adminverbs have been hidden.", confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Hide Most Adminverbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return @@ -333,7 +353,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) remove_admin_verbs() add_verb(src, /client/proc/show_verbs) - to_chat(src, "Almost all of your adminverbs have been hidden.") + to_chat(src, "Almost all of your adminverbs have been hidden.", confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Hide All Adminverbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! return @@ -344,7 +364,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) remove_verb(src, /client/proc/show_verbs) add_admin_verbs() - to_chat(src, "All of your adminverbs are now visible.") + to_chat(src, "All of your adminverbs are now visible.", confidential = TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Adminverbs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -354,7 +374,8 @@ GLOBAL_PROTECT(admin_verbs_hideable) set category = "Admin.Game" set name = "Aghost" if(!holder) - return FALSE + return + . = TRUE if(isobserver(mob)) //re-enter var/mob/dead/observer/ghost = mob @@ -367,7 +388,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) ghost.reenter_corpse() SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin Reenter") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! else if(isnewplayer(mob)) - to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first.") + to_chat(src, "Error: Aghost: Can't admin-ghost whilst in the lobby. Join or Observe first.", confidential = TRUE) return FALSE else //ghostize @@ -375,10 +396,10 @@ GLOBAL_PROTECT(admin_verbs_hideable) message_admins("[key_name_admin(usr)] admin ghosted.") var/mob/body = mob body.ghostize(1, voluntary = TRUE) + init_verbs() if(body && !body.key) body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus SSblackbox.record_feedback("tally", "admin_verb", 1, "Admin Ghost") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - return TRUE /client/proc/invisimin() set name = "Invisimin" @@ -387,10 +408,10 @@ GLOBAL_PROTECT(admin_verbs_hideable) if(holder && mob) if(mob.invisibility == INVISIBILITY_OBSERVER) mob.invisibility = initial(mob.invisibility) - to_chat(mob, "Invisimin off. Invisibility reset.") + to_chat(mob, "Invisimin off. Invisibility reset.", confidential = TRUE) else mob.invisibility = INVISIBILITY_OBSERVER - to_chat(mob, "Invisimin on. You are now as invisible as a ghost.") + to_chat(mob, "Invisimin on. You are now as invisible as a ghost.", confidential = TRUE) /client/proc/check_antagonists() set name = "Check Antagonists" @@ -402,15 +423,21 @@ GLOBAL_PROTECT(admin_verbs_hideable) message_admins("[key_name_admin(usr)] checked antagonists.") SSblackbox.record_feedback("tally", "admin_verb", 1, "Check Antagonists") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/client/proc/unban_panel() - set name = "Unban Panel" - set category = "Admin" - if(holder) - if(CONFIG_GET(flag/ban_legacy_system)) - holder.unbanpanel() - else - holder.DB_ban_panel() - SSblackbox.record_feedback("tally", "admin_verb", 1, "Unban Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +// /client/proc/ban_panel() +// set name = "Banning Panel" +// set category = "Admin" +// if(!check_rights(R_BAN)) +// return +// holder.ban_panel() +// SSblackbox.record_feedback("tally", "admin_verb", 1, "Banning Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + +// /client/proc/unban_panel() +// set name = "Unbanning Panel" +// set category = "Admin" +// if(!check_rights(R_BAN)) +// return +// holder.unban_panel() +// SSblackbox.record_feedback("tally", "admin_verb", 1, "Unbanning Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/game_panel() set name = "Game Panel" @@ -419,13 +446,13 @@ GLOBAL_PROTECT(admin_verbs_hideable) holder.Game() SSblackbox.record_feedback("tally", "admin_verb", 1, "Game Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! -/client/proc/secrets() - set name = "Secrets" - set category = "Admin.Game" - if (holder) - holder.Secrets() - SSblackbox.record_feedback("tally", "admin_verb", 1, "Secrets Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - +// /client/proc/poll_panel() +// set name = "Server Poll Management" +// set category = "Admin" +// if(!check_rights(R_POLL)) +// return +// holder.poll_list_panel() +// SSblackbox.record_feedback("tally", "admin_verb", 1, "Server Poll Management") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/findStealthKey(txt) if(txt) @@ -457,7 +484,10 @@ GLOBAL_PROTECT(admin_verbs_hideable) if(isobserver(mob)) mob.invisibility = initial(mob.invisibility) mob.alpha = initial(mob.alpha) - mob.name = initial(mob.name) + if(mob.mind) + mob.name = mob.mind.name + else + mob.name = mob.real_name mob.mouse_opacity = initial(mob.mouse_opacity) else var/new_key = ckeyEx(stripped_input(usr, "Enter your desired display name.", "Fake Key", key, 26)) @@ -485,7 +515,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) switch(choice) if(null) - return 0 + return if("Small Bomb (1, 2, 3, 3)") explosion(epicenter, 1, 2, 3, 3, TRUE, TRUE) if("Medium Bomb (2, 3, 4, 4)") @@ -538,7 +568,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) if (isnull(ex_power)) return var/range = round((2 * ex_power)**GLOB.DYN_EX_SCALE) - to_chat(usr, "Estimated Explosive Range: (Devastation: [round(range*0.25)], Heavy: [round(range*0.5)], Light: [round(range)])") + to_chat(usr, "Estimated Explosive Range: (Devastation: [round(range*0.25)], Heavy: [round(range*0.5)], Light: [round(range)])", confidential = TRUE) /client/proc/get_dynex_power() set category = "Debug" @@ -549,7 +579,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) if (isnull(ex_range)) return var/power = (0.5 * ex_range)**(1/GLOB.DYN_EX_SCALE) - to_chat(usr, "Estimated Explosive Power: [power]") + to_chat(usr, "Estimated Explosive Power: [power]", confidential = TRUE) /client/proc/set_dynex_scale() set category = "Debug" @@ -563,6 +593,55 @@ GLOBAL_PROTECT(admin_verbs_hideable) log_admin("[key_name(usr)] has modified Dynamic Explosion Scale: [ex_scale]") message_admins("[key_name_admin(usr)] has modified Dynamic Explosion Scale: [ex_scale]") +// /client/proc/atmos_control() +// set name = "Atmos Control Panel" +// set category = "Debug" +// if(!check_rights(R_DEBUG)) +// return +// SSair.ui_interact(mob) + +// /client/proc/reload_cards() +// set name = "Reload Cards" +// set category = "Debug" +// if(!check_rights(R_DEBUG)) +// return +// if(!SStrading_card_game.loaded) +// message_admins("The card subsystem is not currently loaded") +// return +// reloadAllCardFiles(SStrading_card_game.card_files, SStrading_card_game.card_directory) + +// /client/proc/validate_cards() +// set name = "Validate Cards" +// set category = "Debug" +// if(!check_rights(R_DEBUG)) +// return +// if(!SStrading_card_game.loaded) +// message_admins("The card subsystem is not currently loaded") +// return +// var/message = checkCardpacks(SStrading_card_game.card_packs) +// message += checkCardDatums() +// if(message) +// message_admins(message) + +// /client/proc/test_cardpack_distribution() +// set name = "Test Cardpack Distribution" +// set category = "Debug" +// if(!check_rights(R_DEBUG)) +// return +// if(!SStrading_card_game.loaded) +// message_admins("The card subsystem is not currently loaded") +// return +// var/pack = input("Which pack should we test?", "You fucked it didn't you") as null|anything in sortList(SStrading_card_game.card_packs) +// var/batchCount = input("How many times should we open it?", "Don't worry, I understand") as null|num +// var/batchSize = input("How many cards per batch?", "I hope you remember to check the validation") as null|num +// var/guar = input("Should we use the pack's guaranteed rarity? If so, how many?", "We've all been there. Man you should have seen the old system") as null|num +// checkCardDistribution(pack, batchSize, batchCount, guar) + +// /client/proc/print_cards() +// set name = "Print Cards" +// set category = "Debug" +// printAllCards() + /client/proc/give_spell(mob/T in GLOB.mob_list) set category = "Admin.Fun" set name = "Give Spell" @@ -572,13 +651,13 @@ GLOBAL_PROTECT(admin_verbs_hideable) var/type_length = length_char("/obj/effect/proc_holder/spell") + 2 for(var/A in GLOB.spells) spell_list[copytext_char("[A]", type_length)] = A - var/obj/effect/proc_holder/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in spell_list + var/obj/effect/proc_holder/spell/S = input("Choose the spell to give to that guy", "ABRAKADABRA") as null|anything in sortList(spell_list) if(!S) return SSblackbox.record_feedback("tally", "admin_verb", 1, "Give Spell") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! log_admin("[key_name(usr)] gave [key_name(T)] the spell [S].") - message_admins("[key_name_admin(usr)] gave [key_name(T)] the spell [S].") + message_admins("[key_name_admin(usr)] gave [key_name_admin(T)] the spell [S].") S = spell_list[S] if(T.mind) @@ -592,12 +671,12 @@ GLOBAL_PROTECT(admin_verbs_hideable) set name = "Remove Spell" set desc = "Remove a spell from the selected mob." - if(T && T.mind) - var/obj/effect/proc_holder/spell/S = input("Choose the spell to remove", "NO ABRAKADABRA") as null|anything in T.mind.spell_list + if(T?.mind) + var/obj/effect/proc_holder/spell/S = input("Choose the spell to remove", "NO ABRAKADABRA") as null|anything in sortList(T.mind.spell_list) if(S) T.mind.RemoveSpell(S) log_admin("[key_name(usr)] removed the spell [S] from [key_name(T)].") - message_admins("[key_name_admin(usr)] removed the spell [S] from [key_name(T)].") + message_admins("[key_name_admin(usr)] removed the spell [S] from [key_name_admin(T)].") SSblackbox.record_feedback("tally", "admin_verb", 1, "Remove Spell") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/give_disease(mob/living/T in GLOB.mob_living_list) @@ -605,15 +684,15 @@ GLOBAL_PROTECT(admin_verbs_hideable) set name = "Give Disease" set desc = "Gives a Disease to a mob." if(!istype(T)) - to_chat(src, "You can only give a disease to a mob of type /mob/living.") + to_chat(src, "You can only give a disease to a mob of type /mob/living.", confidential = TRUE) return - var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in SSdisease.diseases + var/datum/disease/D = input("Choose the disease to give to that guy", "ACHOO") as null|anything in sortList(SSdisease.diseases, /proc/cmp_typepaths_asc) if(!D) return T.ForceContractDisease(new D, FALSE, TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Give Disease") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! log_admin("[key_name(usr)] gave [key_name(T)] the disease [D].") - message_admins("[key_name_admin(usr)] gave [key_name(T)] the disease [D].") + message_admins("[key_name_admin(usr)] gave [key_name_admin(T)] the disease [D].") /client/proc/object_say(obj/O in world) set category = "Admin.Events" @@ -655,8 +734,8 @@ GLOBAL_PROTECT(admin_verbs_hideable) holder.deactivate() to_chat(src, "You are now a normal player.") - log_admin("[src] deadmined themself.") - message_admins("[src] deadmined themself.") + log_admin("[src] deadminned themselves.") + message_admins("[src] deadminned themselves.") SSblackbox.record_feedback("tally", "admin_verb", 1, "Deadmin") /client/proc/readmin() @@ -679,7 +758,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) if (!holder) return //This can happen if an admin attempts to vv themself into somebody elses's deadmin datum by getting ref via brute force - to_chat(src, "You are now an admin.") + to_chat(src, "You are now an admin.", confidential = TRUE) message_admins("[src] re-adminned themselves.") log_admin("[src] re-adminned themselves.") SSblackbox.record_feedback("tally", "admin_verb", 1, "Readmin") diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index 8f4a9742ea..1430d5d77a 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -28,6 +28,8 @@ GLOBAL_PROTECT(href_token) var/deadmined + var/datum/filter_editor/filteriffic + /datum/admins/CanProcCall(procname) . = ..() if(!check_rights(R_SENSITIVE)) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index e0434a6fea..bd4b4cf0c7 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -2500,9 +2500,6 @@ break return - else if(href_list["secrets"]) - Secrets_topic(href_list["secrets"],href_list) - else if(href_list["ac_view_wanted"]) //Admin newscaster Topic() stuff be here if(!check_rights(R_ADMIN)) return diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 5704448053..29ee35c117 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -790,11 +790,12 @@ if(!check_rights(R_DEBUG)) return - SSmedals.hub_enabled = !SSmedals.hub_enabled + SSachievements.achievements_enabled = !SSachievements.achievements_enabled - message_admins("[key_name_admin(src)] [SSmedals.hub_enabled ? "disabled" : "enabled"] the medal hub lockout.") + message_admins("[key_name_admin(src)] [SSachievements.achievements_enabled ? "disabled" : "enabled"] the medal hub lockout.") SSblackbox.record_feedback("tally", "admin_verb", 1, "Toggle Medal Disable") // If... - log_admin("[key_name(src)] [SSmedals.hub_enabled ? "disabled" : "enabled"] the medal hub lockout.") + log_admin("[key_name(src)] [SSachievements.achievements_enabled ? "disabled" : "enabled"] the medal hub lockout.") + /client/proc/view_runtimes() set category = "Debug" diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index 6f8e9e703f..358e7f1bec 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -63,3 +63,31 @@ load_admins() SSblackbox.record_feedback("tally", "admin_verb", 1, "Reload All Admins") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! message_admins("[key_name_admin(usr)] manually reloaded admins") + +/client/proc/toggle_cdn() + set name = "Toggle CDN" + set category = "Server" + var/static/admin_disabled_cdn_transport = null + if (alert(usr, "Are you sure you want to toggle the CDN asset transport?", "Confirm", "Yes", "No") != "Yes") + return + var/current_transport = CONFIG_GET(string/asset_transport) + if (!current_transport || current_transport == "simple") + if (admin_disabled_cdn_transport) + CONFIG_SET(string/asset_transport, admin_disabled_cdn_transport) + admin_disabled_cdn_transport = null + SSassets.OnConfigLoad() + message_admins("[key_name_admin(usr)] re-enabled the CDN asset transport") + log_admin("[key_name(usr)] re-enabled the CDN asset transport") + else + to_chat(usr, "The CDN is not enabled!") + if (alert(usr, "The CDN asset transport is not enabled! If you having issues with assets you can also try disabling filename mutations.", "The CDN asset transport is not enabled!", "Try disabling filename mutations", "Nevermind") == "Try disabling filename mutations") + SSassets.transport.dont_mutate_filenames = !SSassets.transport.dont_mutate_filenames + message_admins("[key_name_admin(usr)] [(SSassets.transport.dont_mutate_filenames ? "disabled" : "re-enabled")] asset filename transforms") + log_admin("[key_name(usr)] [(SSassets.transport.dont_mutate_filenames ? "disabled" : "re-enabled")] asset filename transforms") + else + admin_disabled_cdn_transport = current_transport + CONFIG_SET(string/asset_transport, "simple") + SSassets.OnConfigLoad() + SSassets.transport.dont_mutate_filenames = TRUE + message_admins("[key_name_admin(usr)] disabled the CDN asset transport") + log_admin("[key_name(usr)] disabled the CDN asset transport") diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/verbs/secrets.dm similarity index 53% rename from code/modules/admin/secrets.dm rename to code/modules/admin/verbs/secrets.dm index ffe5371619..342b8f0bfe 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/verbs/secrets.dm @@ -1,132 +1,176 @@ -/datum/admins/proc/Secrets() - if(!check_rights(0)) + + +/client/proc/secrets() //Creates a verb for admins to open up the ui + set name = "Secrets" + set desc = "Abuse harder than you ever have before with this handy dandy semi-misc stuff menu" + set category = "Admin.Game" + SSblackbox.record_feedback("tally", "admin_verb", 1, "Secrets Panel") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! + var/datum/secrets_menu/tgui = new(usr)//create the datum + tgui.ui_interact(usr)//datum has a tgui component, here we open the window + +/datum/secrets_menu + var/client/holder //client of whoever is using this datum + var/is_debugger = FALSE + var/is_funmin = FALSE + +/datum/secrets_menu/New(user)//user can either be a client or a mob due to byondcode(tm) + if (istype(user, /client)) + var/client/user_client = user + holder = user_client //if its a client, assign it to holder + else + var/mob/user_mob = user + holder = user_mob.client //if its a mob, assign the mob's client to holder + + is_debugger = check_rights(R_DEBUG) + is_funmin = check_rights(R_FUN) + +/datum/secrets_menu/ui_state(mob/user) + return GLOB.admin_state + +/datum/secrets_menu/ui_close() + qdel(src) + +/datum/secrets_menu/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Secrets") + ui.open() + +/datum/secrets_menu/ui_data(mob/user) + var/list/data = list() + data["is_debugger"] = is_debugger + data["is_funmin"] = is_funmin + return data + +/datum/secrets_menu/ui_act(action, params) + . = ..() + if(.) + return + if((action != "admin_log" || action != "show_admins" || action != "mentor_log") && !check_rights(R_ADMIN)) return - - var/list/dat = list("The first rule of adminbuse is: you don't talk about the adminbuse.


") - - dat +={" - General Secrets
-
- Admin Log
- Show Admin List
- Mentor Log
-
- "} - - if(check_rights(R_ADMIN,0)) - dat += {" - Admin Secrets
-
- Cure all diseases currently in existence
- Bombing List
- Show current traitors and objectives
- Show last [length(GLOB.lastsignalers)] signalers
- Show last [length(GLOB.lawchanges)] law changes
- Show AI Laws
- Show Game Mode
- Show Crew Manifest
- List DNA (Blood)
- List Fingerprints
- Enable/Disable CTF

- Reset Thunderdome to default state
- Rename Station Name
- Reset Station Name
- Set Night Shift Mode
-
- Shuttles
-
- Move Ferry
- Toggle Arrivals Ferry
- Move Mining Shuttle
- Move Labor Shuttle
-
- "} - - if(check_rights(R_FUN,0)) - dat += {" - Fun Secrets
-
- Trigger a Virus Outbreak
- Turn all humans into monkeys
- Chinese Cartoons
- Change the species of all humans
- Make all areas powered
- Make all areas unpowered
- Power all SMES
- Triple AI mode (needs to be used in the lobby)
- Everyone is the traitor
- AK-47s For Everyone!
- Summon Guns
- Summon Magic
- Summon Events (Toggle)
- There can only be one!
- There can only be one! (40-second delay)
- Make all players stupid
- Egalitarian Station Mode
- Anarcho-Capitalist Station Mode
- Break all lights
- Fix all lights
- The floor is lava! (DANGEROUS: extremely lame)
- Spawn a custom portal storm
-
- Change bomb cap
- Mass Purrbation
- Mass Remove Purrbation
- "} - - dat += "
" - - if(check_rights(R_DEBUG,0)) - dat += {" - Security Level Elevated
-
- Change all maintenance doors to engie/brig access only
- Change all maintenance doors to brig access only
- Remove cap on security officers
-
- "} - - usr << browse(dat.Join(), "window=secrets") - return - - - - - -/datum/admins/proc/Secrets_topic(item,href_list) var/datum/round_event/E - var/ok = 0 - switch(item) + var/ok = FALSE + switch(action) + //Generic Buttons anyone can use. if("admin_log") var/dat = "Admin Log
" for(var/l in GLOB.admin_log) dat += "
  • [l]
  • " if(!GLOB.admin_log.len) dat += "No-one has done anything this round!" - usr << browse(dat, "window=admin_log") - - if("mentor_log") - CitadelMentorLogSecret() - + holder << browse(dat, "window=admin_log") if("show_admins") var/dat = "Current admins:
    " if(GLOB.admin_datums) for(var/ckey in GLOB.admin_datums) var/datum/admins/D = GLOB.admin_datums[ckey] dat += "[ckey] - [D.rank.name]
    " - usr << browse(dat, "window=showadmins;size=600x500") + holder << browse(dat, "window=showadmins;size=600x500") + if("mentor_log") + var/dat = "Mentor Log
    " + for(var/l in GLOB.mentorlog) + dat += "
  • [l]
  • " - if("tdomereset") - if(!check_rights(R_ADMIN)) + if(!GLOB.mentorlog.len) + dat += "No mentors have done anything this round!" + usr << browse(dat, "window=mentor_log") + + //Buttons for debug. + if("maint_access_engiebrig") + if(!is_debugger) return + for(var/obj/machinery/door/airlock/maintenance/M in GLOB.machines) + M.check_access() + if (ACCESS_MAINT_TUNNELS in M.req_access) + M.req_access = list() + M.req_one_access = list(ACCESS_BRIG,ACCESS_ENGINE) + message_admins("[key_name_admin(holder)] made all maint doors engineering and brig access-only.") + if("maint_access_brig") + if(!is_debugger) + return + for(var/obj/machinery/door/airlock/maintenance/M in GLOB.machines) + M.check_access() + if (ACCESS_MAINT_TUNNELS in M.req_access) + M.req_access = list(ACCESS_BRIG) + message_admins("[key_name_admin(holder)] made all maint doors brig access-only.") + if("infinite_sec") + if(!is_debugger) + return + var/datum/job/J = SSjob.GetJob("Security Officer") + if(!J) + return + J.total_positions = -1 + J.spawn_positions = -1 + message_admins("[key_name_admin(holder)] has removed the cap on security officers.") + //Buttons for helpful stuff. This is where people land in the tgui + if("clear_virus") + var/choice = input("Are you sure you want to cure all disease?") in list("Yes", "Cancel") + if(choice == "Yes") + message_admins("[key_name_admin(holder)] has cured all diseases.") + for(var/thing in SSdisease.active_diseases) + var/datum/disease/D = thing + D.cure(0) + if("list_bombers") + var/dat = "Bombing List
    " + for(var/l in GLOB.bombers) + dat += text("[l]
    ") + holder << browse(dat, "window=bombers") + + if("list_signalers") + var/dat = "Showing last [length(GLOB.lastsignalers)] signalers.
    " + for(var/sig in GLOB.lastsignalers) + dat += "[sig]
    " + holder << browse(dat, "window=lastsignalers;size=800x500") + if("list_lawchanges") + var/dat = "Showing last [length(GLOB.lawchanges)] law changes.
    " + for(var/sig in GLOB.lawchanges) + dat += "[sig]
    " + holder << browse(dat, "window=lawchanges;size=800x500") + if("showailaws") + holder.holder.output_ai_laws()//huh, inconvenient var naming, huh? + if("showgm") + if(!SSticker.HasRoundStarted()) + alert("The game hasn't started yet!") + else if (SSticker.mode) + alert("The game mode is [SSticker.mode.name]") + else + alert("For some reason there's a SSticker, but not a game mode") + if("manifest") + var/dat = "Showing Crew Manifest.
    " + dat += "" + for(var/datum/data/record/t in GLOB.data_core.general) + dat += "" + dat += "
    NamePosition
    [t.fields["name"]][t.fields["rank"]]
    " + holder << browse(dat, "window=manifest;size=440x410") + if("dna") + var/dat = "Showing DNA from blood.
    " + dat += "" + for(var/i in GLOB.human_list) + var/mob/living/carbon/human/H = i + if(H.ckey) + dat += "" + dat += "
    NameDNABlood Type
    [H][H.dna.unique_enzymes][H.dna.blood_type]
    " + holder << browse(dat, "window=DNA;size=440x410") + if("fingerprints") + var/dat = "Showing Fingerprints.
    " + dat += "" + for(var/i in GLOB.human_list) + var/mob/living/carbon/human/H = i + if(H.ckey) + dat += "" + dat += "
    NameFingerprints
    [H][md5(H.dna.uni_identity)]
    " + holder << browse(dat, "window=fingerprints;size=440x410") + if("ctfbutton") + toggle_all_ctf(holder) + if("tdomereset") var/delete_mobs = alert("Clear all mobs?","Confirm","Yes","No","Cancel") if(delete_mobs == "Cancel") return - log_admin("[key_name(usr)] reset the thunderdome to default with delete_mobs==[delete_mobs].", 1) - message_admins("[key_name_admin(usr)] reset the thunderdome to default with delete_mobs==[delete_mobs].") + log_admin("[key_name(holder)] reset the thunderdome to default with delete_mobs==[delete_mobs].", 1) + message_admins("[key_name_admin(holder)] reset the thunderdome to default with delete_mobs==[delete_mobs].") - var/area/thunderdome = locate(/area/tdome/arena) + var/area/thunderdome = GLOB.areas_by_type[/area/tdome/arena] if(delete_mobs == "Yes") for(var/mob/living/mob in thunderdome) qdel(mob) //Clear mobs @@ -134,31 +178,24 @@ if(!istype(obj, /obj/machinery/camera) && !istype(obj, /obj/effect/abstract/proximity_checker)) qdel(obj) //Clear objects - var/area/template = locate(/area/tdome/arena_source) + var/area/template = GLOB.areas_by_type[/area/tdome/arena_source] template.copy_contents_to(thunderdome) - - if("clear_virus") - - var/choice = input("Are you sure you want to cure all disease?") in list("Yes", "Cancel") - if(choice == "Yes") - message_admins("[key_name_admin(usr)] has cured all diseases.") - for(var/thing in SSdisease.active_diseases) - var/datum/disease/D = thing - D.cure(0) if("set_name") - if(!check_rights(R_ADMIN)) - return - var/new_name = input(usr, "Please input a new name for the station.", "What?", "") as text|null + var/new_name = input(holder, "Please input a new name for the station.", "What?", "") as text|null if(!new_name) return set_station_name(new_name) - log_admin("[key_name(usr)] renamed the station to \"[new_name]\".") - message_admins("[key_name_admin(usr)] renamed the station to: [new_name].") + log_admin("[key_name(holder)] renamed the station to \"[new_name]\".") + message_admins("[key_name_admin(holder)] renamed the station to: [new_name].") + priority_announce("[command_name()] has renamed the station to \"[new_name]\".") + if("reset_name") + var/new_name = new_station_name() + set_station_name(new_name) + log_admin("[key_name(holder)] reset the station name.") + message_admins("[key_name_admin(holder)] reset the station name.") priority_announce("[command_name()] has renamed the station to \"[new_name]\".") if("night_shift_set") - if(!check_rights(R_ADMIN)) - return - var/val = alert(usr, "What do you want to set night shift to? This will override the automatic system until set to automatic again.", "Night Shift", "On", "Off", "Automatic") + var/val = alert(holder, "What do you want to set night shift to? This will override the automatic system until set to automatic again.", "Night Shift", "On", "Off", "Automatic") switch(val) if("Automatic") if(CONFIG_GET(flag/enable_night_shifts)) @@ -172,321 +209,102 @@ if("Off") SSnightshift.can_fire = FALSE SSnightshift.update_nightshift(FALSE, TRUE) - - if("reset_name") - if(!check_rights(R_ADMIN)) - return - var/new_name = new_station_name() - set_station_name(new_name) - log_admin("[key_name(usr)] reset the station name.") - message_admins("[key_name_admin(usr)] reset the station name.") - priority_announce("[command_name()] has renamed the station to \"[new_name]\".") - - if("list_bombers") - if(!check_rights(R_ADMIN)) - return - var/dat = "Bombing List
    " - for(var/l in GLOB.bombers) - dat += text("[l]
    ") - usr << browse(dat, "window=bombers") - - if("list_signalers") - if(!check_rights(R_ADMIN)) - return - var/dat = "Showing last [length(GLOB.lastsignalers)] signalers.
    " - for(var/sig in GLOB.lastsignalers) - dat += "[sig]
    " - usr << browse(dat, "window=lastsignalers;size=800x500") - - if("list_lawchanges") - if(!check_rights(R_ADMIN)) - return - var/dat = "Showing last [length(GLOB.lawchanges)] law changes.
    " - for(var/sig in GLOB.lawchanges) - dat += "[sig]
    " - usr << browse(dat, "window=lawchanges;size=800x500") - - if("moveminingshuttle") - if(!check_rights(R_ADMIN)) - return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Send Mining Shuttle")) - if(!SSshuttle.toggleShuttle("mining","mining_home","mining_away")) - message_admins("[key_name_admin(usr)] moved mining shuttle") - log_admin("[key_name(usr)] moved the mining shuttle") - - if("movelaborshuttle") - if(!check_rights(R_ADMIN)) - return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Send Labor Shuttle")) - if(!SSshuttle.toggleShuttle("laborcamp","laborcamp_home","laborcamp_away")) - message_admins("[key_name_admin(usr)] moved labor shuttle") - log_admin("[key_name(usr)] moved the labor shuttle") - if("moveferry") - if(!check_rights(R_ADMIN)) - return SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Send CentCom Ferry")) if(!SSshuttle.toggleShuttle("ferry","ferry_home","ferry_away")) - message_admins("[key_name_admin(usr)] moved the CentCom ferry") - log_admin("[key_name(usr)] moved the CentCom ferry") - + message_admins("[key_name_admin(holder)] moved the CentCom ferry") + log_admin("[key_name(holder)] moved the CentCom ferry") if("togglearrivals") - if(!check_rights(R_ADMIN)) - return var/obj/docking_port/mobile/arrivals/A = SSshuttle.arrivals if(A) var/new_perma = !A.perma_docked A.perma_docked = new_perma SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Permadock Arrivals Shuttle", "[new_perma ? "Enabled" : "Disabled"]")) - message_admins("[key_name_admin(usr)] [new_perma ? "stopped" : "started"] the arrivals shuttle") - log_admin("[key_name(usr)] [new_perma ? "stopped" : "started"] the arrivals shuttle") + message_admins("[key_name_admin(holder)] [new_perma ? "stopped" : "started"] the arrivals shuttle") + log_admin("[key_name(holder)] [new_perma ? "stopped" : "started"] the arrivals shuttle") else - to_chat(usr, "There is no arrivals shuttle") - if("showailaws") - if(!check_rights(R_ADMIN)) - return - output_ai_laws() - if("showgm") - if(!check_rights(R_ADMIN)) - return - if(!SSticker.HasRoundStarted()) - alert("The game hasn't started yet!") - else if (SSticker.mode) - alert("The game mode is [SSticker.mode.name]") - else alert("For some reason there's a SSticker, but not a game mode") - if("manifest") - if(!check_rights(R_ADMIN)) - return - var/dat = "Showing Crew Manifest.
    " - dat += "" - for(var/datum/data/record/t in GLOB.data_core.general) - dat += "" - dat += "
    NamePosition
    [t.fields["name"]][t.fields["rank"]]
    " - usr << browse(dat, "window=manifest;size=440x410") - if("DNA") - if(!check_rights(R_ADMIN)) - return - var/dat = "Showing DNA from blood.
    " - dat += "" - for(var/mob/living/carbon/human/H in GLOB.carbon_list) - if(H.ckey) - dat += "" - dat += "
    NameDNABlood Type
    [H][H.dna.unique_enzymes][H.dna.blood_type]
    " - usr << browse(dat, "window=DNA;size=440x410") - if("fingerprints") - if(!check_rights(R_ADMIN)) - return - var/dat = "Showing Fingerprints.
    " - dat += "" - for(var/mob/living/carbon/human/H in GLOB.carbon_list) - if(H.ckey) - dat += "" - dat += "
    NameFingerprints
    [H][md5(H.dna.uni_identity)]
    " - usr << browse(dat, "window=fingerprints;size=440x410") - - if("monkey") - if(!check_rights(R_FUN)) - return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Monkeyize All Humans")) - for(var/mob/living/carbon/human/H in GLOB.carbon_list) - spawn(0) - H.monkeyize() - ok = 1 - - if("allspecies") - if(!check_rights(R_FUN)) - return - var/result = input(usr, "Please choose a new species","Species") as null|anything in GLOB.species_list - if(result) - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Mass Species Change", "[result]")) - log_admin("[key_name(usr)] turned all humans into [result]", 1) - message_admins("\blue [key_name_admin(usr)] turned all humans into [result]") - var/newtype = GLOB.species_list[result] - for(var/mob/living/carbon/human/H in GLOB.carbon_list) - H.set_species(newtype) - - if("tripleAI") - if(!check_rights(R_FUN)) - return - usr.client.triple_ai() - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Triple AI")) - - if("power") - if(!check_rights(R_FUN)) - return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Power All APCs")) - log_admin("[key_name(usr)] made all areas powered", 1) - message_admins("[key_name_admin(usr)] made all areas powered") - power_restore() - - if("unpower") - if(!check_rights(R_FUN)) - return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Depower All APCs")) - log_admin("[key_name(usr)] made all areas unpowered", 1) - message_admins("[key_name_admin(usr)] made all areas unpowered") - power_failure() - - if("quickpower") - if(!check_rights(R_FUN)) - return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Power All SMESs")) - log_admin("[key_name(usr)] made all SMESs powered", 1) - message_admins("[key_name_admin(usr)] made all SMESs powered") - power_restore_quick() - - if("traitor_all") - if(!check_rights(R_FUN)) - return - if(!SSticker.HasRoundStarted()) - alert("The game hasn't started yet!") - return - var/objective = stripped_input(usr, "Enter an objective") - if(!objective) - return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Traitor All", "[objective]")) - for(var/mob/living/H in GLOB.player_list) - if(!(ishuman(H)||istype(H, /mob/living/silicon/))) - continue - if(H.stat == DEAD || !H.client || !H.mind || ispAI(H)) - continue - if(is_special_character(H)) - continue - var/datum/antagonist/traitor/T = new() - T.give_objectives = FALSE - var/datum/objective/new_objective = new - new_objective.owner = H - new_objective.explanation_text = objective - T.add_objective(new_objective) - H.mind.add_antag_datum(T) - message_admins("[key_name_admin(usr)] used everyone is a traitor secret. Objective is [objective]") - log_admin("[key_name(usr)] used everyone is a traitor secret. Objective is [objective]") - - if("changebombcap") - if(!check_rights(R_FUN)) - return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Bomb Cap")) - - var/newBombCap = input(usr,"What would you like the new bomb cap to be. (entered as the light damage range (the 3rd number in common (1,2,3) notation)) Must be above 4)", "New Bomb Cap", GLOB.MAX_EX_LIGHT_RANGE) as num|null - if (!CONFIG_SET(number/bombcap, newBombCap)) - return - - message_admins("[key_name_admin(usr)] changed the bomb cap to [GLOB.MAX_EX_DEVESTATION_RANGE], [GLOB.MAX_EX_HEAVY_RANGE], [GLOB.MAX_EX_LIGHT_RANGE]") - log_admin("[key_name(usr)] changed the bomb cap to [GLOB.MAX_EX_DEVESTATION_RANGE], [GLOB.MAX_EX_HEAVY_RANGE], [GLOB.MAX_EX_LIGHT_RANGE]") - - if("blackout") - if(!check_rights(R_FUN)) - return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Break All Lights")) - message_admins("[key_name_admin(usr)] broke all lights") - for(var/obj/machinery/light/L in GLOB.machines) - L.break_light_tube() - - if("anime") - if(!check_rights(R_FUN)) - return - var/animetype = alert("Would you like to have the clothes be changed?",,"Yes","No","Cancel") - - var/droptype - if(animetype =="Yes") - droptype = alert("Make the uniforms Nodrop?",,"Yes","No","Cancel") - - if(animetype == "Cancel" || droptype == "Cancel") - return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Chinese Cartoons")) - message_admins("[key_name_admin(usr)] made everything kawaii.") - for(var/mob/living/carbon/human/H in GLOB.carbon_list) - SEND_SOUND(H, sound(get_announcer_sound("animes"))) - - if(H.dna.species.id == "human") - if(H.dna.features["tail_human"] == "None" || H.dna.features["ears"] == "None") - var/obj/item/organ/ears/cat/ears = new - var/obj/item/organ/tail/cat/tail = new - ears.Insert(H, drop_if_replaced=FALSE) - tail.Insert(H, drop_if_replaced=FALSE) - var/list/honorifics = list("[MALE]" = list("kun"), "[FEMALE]" = list("chan","tan"), "[NEUTER]" = list("san"), "[PLURAL]" = list("san")) //John Robust -> Robust-kun - var/list/names = splittext(H.real_name," ") - var/forename = names.len > 1 ? names[2] : names[1] - var/newname = "[forename]-[pick(honorifics["[H.gender]"])]" - H.fully_replace_character_name(H.real_name,newname) - H.update_mutant_bodyparts() - if(animetype == "Yes") - var/seifuku = pick(typesof(/obj/item/clothing/under/costume/schoolgirl)) - var/obj/item/clothing/under/costume/schoolgirl/I = new seifuku - var/olduniform = H.w_uniform - H.temporarilyRemoveItemFromInventory(H.w_uniform, TRUE, FALSE) - H.equip_to_slot_or_del(I, SLOT_W_UNIFORM) - qdel(olduniform) - if(droptype == "Yes") - ADD_TRAIT(I, TRAIT_NODROP, ADMIN_TRAIT) - else - to_chat(H, "You're not kawaii enough for this.") - - if("whiteout") - if(!check_rights(R_FUN)) - return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Fix All Lights")) - message_admins("[key_name_admin(usr)] fixed all lights") - for(var/obj/machinery/light/L in GLOB.machines) - L.fix() - - if("floorlava") - SSweather.run_weather(/datum/weather/floor_is_lava) - + to_chat(holder, "There is no arrivals shuttle.", confidential = TRUE) + if("moveminingshuttle") + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Send Mining Shuttle")) + if(!SSshuttle.toggleShuttle("mining","mining_home","mining_away")) + message_admins("[key_name_admin(usr)] moved mining shuttle") + log_admin("[key_name(usr)] moved the mining shuttle") + if("movelaborshuttle") + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Send Labor Shuttle")) + if(!SSshuttle.toggleShuttle("laborcamp","laborcamp_home","laborcamp_away")) + message_admins("[key_name_admin(holder)] moved labor shuttle") + log_admin("[key_name(holder)] moved the labor shuttle") + //!fun! buttons. if("virus") - if(!check_rights(R_FUN)) + if(!is_funmin) return SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Virus Outbreak")) switch(alert("Do you want this to be a random disease or do you have something in mind?",,"Make Your Own","Random","Choose")) if("Make Your Own") - AdminCreateVirus(usr.client) + AdminCreateVirus(holder) if("Random") - E = new /datum/round_event/disease_outbreak() + var/datum/round_event_control/disease_outbreak/DC = locate(/datum/round_event_control/disease_outbreak) in SSevents.control + E = DC.runEvent() if("Choose") - var/virus = input("Choose the virus to spread", "BIOHAZARD") as null|anything in typesof(/datum/disease) - E = new /datum/round_event/disease_outbreak{}() - var/datum/round_event/disease_outbreak/DO = E + var/virus = input("Choose the virus to spread", "BIOHAZARD") as null|anything in sortList(typesof(/datum/disease), /proc/cmp_typepaths_asc) + var/datum/round_event_control/disease_outbreak/DC = locate(/datum/round_event_control/disease_outbreak) in SSevents.control + var/datum/round_event/disease_outbreak/DO = DC.runEvent() DO.virus_type = virus - - if("stupify") - if(!check_rights(R_FUN)) + E = DO + if("allspecies") + if(!is_funmin) return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Mass Braindamage")) - for(var/mob/living/carbon/human/H in GLOB.player_list) - to_chat(H, "You suddenly feel stupid.") - H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 60, 80) - message_admins("[key_name_admin(usr)] made everybody stupid") - - if("eagles")//SCRAW - if(!check_rights(R_FUN)) + var/result = input(holder, "Please choose a new species","Species") as null|anything in GLOB.species_list + if(result) + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Mass Species Change", "[result]")) + log_admin("[key_name(holder)] turned all humans into [result]", 1) + message_admins("\blue [key_name_admin(holder)] turned all humans into [result]") + var/newtype = GLOB.species_list[result] + for(var/i in GLOB.human_list) + var/mob/living/carbon/human/H = i + H.set_species(newtype) + if("power") + if(!is_funmin) return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Egalitarian Station")) - for(var/obj/machinery/door/airlock/W in GLOB.machines) - if(is_station_level(W.z) && !istype(get_area(W), /area/bridge) && !istype(get_area(W), /area/crew_quarters) && !istype(get_area(W), /area/security/prison)) - W.req_access = list() - message_admins("[key_name_admin(usr)] activated Egalitarian Station mode") - priority_announce("CentCom airlock control override activated. Please take this time to get acquainted with your coworkers.", null, "commandreport") - - if("ak47s") - if(!check_rights(R_FUN)) + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Power All APCs")) + log_admin("[key_name(holder)] made all areas powered", 1) + message_admins("[key_name_admin(holder)] made all areas powered") + power_restore() + if("unpower") + if(!is_funmin) return - message_admins("[key_name_admin(usr)] activated AK-47s for Everyone!") - usr.client.ak47s() - sound_to_playing_players('sound/misc/ak47s.ogg') - - if("ancap") - if(!check_rights(R_FUN)) + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Depower All APCs")) + log_admin("[key_name(holder)] made all areas unpowered", 1) + message_admins("[key_name_admin(holder)] made all areas unpowered") + power_failure() + if("quickpower") + if(!is_funmin) return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Anarcho-capitalist Station")) - SSeconomy.full_ancap = !SSeconomy.full_ancap - message_admins("[key_name_admin(usr)] toggled Anarcho-capitalist mode") - if(SSeconomy.full_ancap) - priority_announce("The NAP is now in full effect.", null, "commandreport") + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Power All SMESs")) + log_admin("[key_name(holder)] made all SMESs powered", 1) + message_admins("[key_name_admin(holder)] made all SMESs powered") + power_restore_quick() + // if("anon_name") + // if(!is_funmin) + // return + // holder.anon_names() + // SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Anonymous Names")) + if("tripleAI") + if(!is_funmin) + return + holder.triple_ai() + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Triple AI")) + if("onlyone") + if(!is_funmin) + return + var/response = alert("Delay by 40 seconds?", "There can, in fact, only be one", "Instant!", "40 seconds (crush the hope of a normal shift)") + if(response == "Instant!") + holder.only_one() else - priority_announce("The NAP has been revoked.", null, "commandreport") - + holder.only_one_delayed() + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("There Can Be Only One")) if("guns") - if(!check_rights(R_FUN)) + if(!is_funmin) return SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Summon Guns")) var/survivor_probability = 0 @@ -496,23 +314,21 @@ if("All Antags!") survivor_probability = 100 - rightandwrong(SUMMON_GUNS, usr, survivor_probability) - + rightandwrong(SUMMON_GUNS, holder, survivor_probability) if("magic") - if(!check_rights(R_FUN)) + if(!is_funmin) return SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Summon Magic")) var/survivor_probability = 0 - switch(alert("Do you want this to create survivors antagonists?",,"No Antags","Some Antags","All Antags!")) + switch(alert("Do you want this to create magician antagonists?",,"No Antags","Some Antags","All Antags!")) if("Some Antags") survivor_probability = 25 if("All Antags!") survivor_probability = 100 - rightandwrong(SUMMON_MAGIC, usr, survivor_probability) - + rightandwrong(SUMMON_MAGIC, holder, survivor_probability) if("events") - if(!check_rights(R_FUN)) + if(!is_funmin) return if(!SSevents.wizardmode) if(alert("Do you want to toggle summon events on?",,"Yes","No") == "Yes") @@ -528,78 +344,41 @@ SSevents.toggleWizardmode() SSevents.resetFrequency() SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Summon Events", "Disable")) - - if("dorf") - if(!check_rights(R_FUN)) + if("eagles") + if(!is_funmin) return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Dwarf Beards")) - for(var/mob/living/carbon/human/B in GLOB.carbon_list) - B.facial_hair_style = "Dward Beard" - B.update_hair() - message_admins("[key_name_admin(usr)] activated dorf mode") - - if("onlyone") - if(!check_rights(R_FUN)) + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Egalitarian Station")) + for(var/obj/machinery/door/airlock/W in GLOB.machines) + if(is_station_level(W.z) && !istype(get_area(W), /area/bridge) && !istype(get_area(W), /area/crew_quarters) && !istype(get_area(W), /area/security/prison)) + W.req_access = list() + message_admins("[key_name_admin(holder)] activated Egalitarian Station mode") + priority_announce("CentCom airlock control override activated. Please take this time to get acquainted with your coworkers.", null, "commandreport") + if("ancap") + if(!is_funmin) return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("There Can Be Only One")) - usr.client.only_one() - sound_to_playing_players('sound/misc/highlander.ogg') - - if("delayed_onlyone") - if(!check_rights(R_FUN)) + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Anarcho-capitalist Station")) + SSeconomy.full_ancap = !SSeconomy.full_ancap + message_admins("[key_name_admin(holder)] toggled Anarcho-capitalist mode") + if(SSeconomy.full_ancap) + priority_announce("The NAP is now in full effect.", null, "commandreport") + else + priority_announce("The NAP has been revoked.", null, "commandreport") + if("blackout") + if(!is_funmin) return - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("There Can Be Only One")) - usr.client.only_one_delayed() - sound_to_playing_players('sound/misc/highlander_delayed.ogg') - - if("maint_access_brig") - if(!check_rights(R_DEBUG)) + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Break All Lights")) + message_admins("[key_name_admin(holder)] broke all lights") + for(var/obj/machinery/light/L in GLOB.machines) + L.break_light_tube() + if("whiteout") + if(!is_funmin) return - for(var/obj/machinery/door/airlock/maintenance/M in GLOB.machines) - M.check_access() - if (ACCESS_MAINT_TUNNELS in M.req_access) - M.req_access = list(ACCESS_BRIG) - message_admins("[key_name_admin(usr)] made all maint doors brig access-only.") - if("maint_access_engiebrig") - if(!check_rights(R_DEBUG)) - return - for(var/obj/machinery/door/airlock/maintenance/M in GLOB.machines) - M.check_access() - if (ACCESS_MAINT_TUNNELS in M.req_access) - M.req_access = list() - M.req_one_access = list(ACCESS_BRIG,ACCESS_ENGINE) - message_admins("[key_name_admin(usr)] made all maint doors engineering and brig access-only.") - if("infinite_sec") - if(!check_rights(R_DEBUG)) - return - var/datum/job/J = SSjob.GetJob("Security Officer") - if(!J) - return - J.total_positions = -1 - J.spawn_positions = -1 - message_admins("[key_name_admin(usr)] has removed the cap on security officers.") - - if("ctfbutton") - if(!check_rights(R_ADMIN)) - return - toggle_all_ctf(usr) - if("masspurrbation") - if(!check_rights(R_FUN)) - return - mass_purrbation() - message_admins("[key_name_admin(usr)] has put everyone on \ - purrbation!") - log_admin("[key_name(usr)] has put everyone on purrbation.") - if("massremovepurrbation") - if(!check_rights(R_FUN)) - return - mass_remove_purrbation() - message_admins("[key_name_admin(usr)] has removed everyone from \ - purrbation.") - log_admin("[key_name(usr)] has removed everyone from purrbation.") - + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Fix All Lights")) + message_admins("[key_name_admin(holder)] fixed all lights") + for(var/obj/machinery/light/L in GLOB.machines) + L.fix() if("customportal") - if(!check_rights(R_FUN)) + if(!is_funmin) return var/list/settings = list( @@ -620,14 +399,14 @@ ) ) - message_admins("[key_name(usr)] is creating a custom portal storm...") - var/list/prefreturn = presentpreflikepicker(usr,"Customize Portal Storm", "Customize Portal Storm", Button1="Ok", width = 600, StealFocus = 1,Timeout = 0, settings=settings) + message_admins("[key_name(holder)] is creating a custom portal storm...") + var/list/prefreturn = presentpreflikepicker(holder,"Customize Portal Storm", "Customize Portal Storm", Button1="Ok", width = 600, StealFocus = 1,Timeout = 0, settings=settings) if (prefreturn["button"] == 1) var/list/prefs = settings["mainsettings"] if (prefs["amount"]["value"] < 1 || prefs["portalnum"]["value"] < 1) - to_chat(usr, "Number of portals and mobs to spawn must be at least 1") + to_chat(holder, "Number of portals and mobs to spawn must be at least 1.", confidential = TRUE) return var/mob/pathToSpawn = prefs["typepath"]["value"] @@ -635,7 +414,7 @@ pathToSpawn = text2path(pathToSpawn) if (!ispath(pathToSpawn)) - to_chat(usr, "Invalid path [pathToSpawn]") + to_chat(holder, "Invalid path [pathToSpawn].", confidential = TRUE) return var/list/candidates = list() @@ -653,8 +432,8 @@ var/mutable_appearance/storm = mutable_appearance('icons/obj/tesla_engine/energy_ball.dmi', "energy_ball_fast", FLY_LAYER) storm.color = prefs["color"]["value"] - message_admins("[key_name_admin(usr)] has created a customized portal storm that will spawn [prefs["portalnum"]["value"]] portals, each of them spawning [prefs["amount"]["value"]] of [pathToSpawn]") - log_admin("[key_name(usr)] has created a customized portal storm that will spawn [prefs["portalnum"]["value"]] portals, each of them spawning [prefs["amount"]["value"]] of [pathToSpawn]") + message_admins("[key_name_admin(holder)] has created a customized portal storm that will spawn [prefs["portalnum"]["value"]] portals, each of them spawning [prefs["amount"]["value"]] of [pathToSpawn]") + log_admin("[key_name(holder)] has created a customized portal storm that will spawn [prefs["portalnum"]["value"]] portals, each of them spawning [prefs["amount"]["value"]] of [pathToSpawn]") var/outfit = prefs["humanoutfit"]["value"] if (!ispath(outfit)) @@ -668,20 +447,157 @@ addtimer(CALLBACK(GLOBAL_PROC, .proc/doPortalSpawn, get_random_station_turf(), pathToSpawn, length(ghostcandidates), storm, ghostcandidates, outfit), i*prefs["delay"]["value"]) else if (prefs["playersonly"]["value"] != "Yes") addtimer(CALLBACK(GLOBAL_PROC, .proc/doPortalSpawn, get_random_station_turf(), pathToSpawn, prefs["amount"]["value"], storm, null, outfit), i*prefs["delay"]["value"]) + if("changebombcap") + if(!is_funmin) + return + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Bomb Cap")) + var/newBombCap = input(holder,"What would you like the new bomb cap to be. (entered as the light damage range (the 3rd number in common (1,2,3) notation)) Must be above 4)", "New Bomb Cap", GLOB.MAX_EX_LIGHT_RANGE) as num|null + if (!CONFIG_SET(number/bombcap, newBombCap)) + return + + message_admins("[key_name_admin(holder)] changed the bomb cap to [GLOB.MAX_EX_DEVESTATION_RANGE], [GLOB.MAX_EX_HEAVY_RANGE], [GLOB.MAX_EX_LIGHT_RANGE]") + log_admin("[key_name(holder)] changed the bomb cap to [GLOB.MAX_EX_DEVESTATION_RANGE], [GLOB.MAX_EX_HEAVY_RANGE], [GLOB.MAX_EX_LIGHT_RANGE]") + //buttons that are fun for exactly you and nobody else. + if("monkey") + if(!is_funmin) + return + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Monkeyize All Humans")) + for(var/i in GLOB.human_list) + var/mob/living/carbon/human/H = i + INVOKE_ASYNC(H, /mob/living/carbon.proc/monkeyize) + ok = TRUE + if("traitor_all") + if(!is_funmin) + return + if(!SSticker.HasRoundStarted()) + alert("The game hasn't started yet!") + return + var/objective = stripped_input(holder, "Enter an objective") + if(!objective) + return + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Traitor All", "[objective]")) + for(var/mob/living/H in GLOB.player_list) + if(!(ishuman(H)||istype(H, /mob/living/silicon/))) + continue + if(H.stat == DEAD || !H.mind || ispAI(H)) + continue + if(is_special_character(H)) + continue + var/datum/antagonist/traitor/T = new() + T.give_objectives = FALSE + var/datum/objective/new_objective = new + new_objective.owner = H + new_objective.explanation_text = objective + T.add_objective(new_objective) + H.mind.add_antag_datum(T) + message_admins("[key_name_admin(holder)] used everyone is a traitor secret. Objective is [objective]") + log_admin("[key_name(holder)] used everyone is a traitor secret. Objective is [objective]") + if("ak47s") + if(!is_funmin) + return + if(!SSticker.HasRoundStarted()) + alert("The game hasn't started yet!") + return + message_admins("[key_name_admin(holder)] activated AK-47s for Everyone!") + holder.ak47s() + sound_to_playing_players('sound/misc/ak47s.ogg') + + if("massbraindamage") + if(!is_funmin) + return + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Mass Braindamage")) + for(var/mob/living/carbon/human/H in GLOB.player_list) + to_chat(H, "You suddenly feel stupid.", confidential = TRUE) + H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 60, 80) + message_admins("[key_name_admin(holder)] made everybody brain damaged") + if("floorlava") + SSweather.run_weather(/datum/weather/floor_is_lava) + if("anime") + if(!is_funmin) + return + var/animetype = alert("Would you like to have the clothes be changed?",,"Yes","No","Cancel") + + var/droptype + if(animetype =="Yes") + droptype = alert("Make the uniforms Nodrop?",,"Yes","No","Cancel") + + if(animetype == "Cancel" || droptype == "Cancel") + return + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Chinese Cartoons")) + message_admins("[key_name_admin(holder)] made everything kawaii.") + for(var/i in GLOB.human_list) + var/mob/living/carbon/human/H = i + SEND_SOUND(H, sound(get_announcer_sound("animes"))) + + if(H.dna.species.id == "human") + if(H.dna.features["tail_human"] == "None" || H.dna.features["ears"] == "None") + var/obj/item/organ/ears/cat/ears = new + var/obj/item/organ/tail/cat/tail = new + ears.Insert(H, drop_if_replaced=FALSE) + tail.Insert(H, drop_if_replaced=FALSE) + var/list/honorifics = list("[MALE]" = list("kun"), "[FEMALE]" = list("chan","tan"), "[NEUTER]" = list("san"), "[PLURAL]" = list("san")) //John Robust -> Robust-kun + var/list/names = splittext(H.real_name," ") + var/forename = names.len > 1 ? names[2] : names[1] + var/newname = "[forename]-[pick(honorifics["[H.gender]"])]" + H.fully_replace_character_name(H.real_name,newname) + H.update_mutant_bodyparts() + if(animetype == "Yes") + var/seifuku = pick(typesof(/obj/item/clothing/under/costume/schoolgirl)) + var/obj/item/clothing/under/costume/schoolgirl/I = new seifuku + var/olduniform = H.w_uniform + H.temporarilyRemoveItemFromInventory(H.w_uniform, TRUE, FALSE) + H.equip_to_slot_or_del(I, ITEM_SLOT_ICLOTHING) + qdel(olduniform) + if(droptype == "Yes") + ADD_TRAIT(I, TRAIT_NODROP, ADMIN_TRAIT) + else + to_chat(H, "You're not kawaii enough for this!", confidential = TRUE) + if("masspurrbation") + if(!is_funmin) + return + mass_purrbation() + message_admins("[key_name_admin(holder)] has put everyone on \ + purrbation!") + log_admin("[key_name(holder)] has put everyone on purrbation.") + if("massremovepurrbation") + if(!is_funmin) + return + mass_remove_purrbation() + message_admins("[key_name_admin(holder)] has removed everyone from \ + purrbation.") + log_admin("[key_name(holder)] has removed everyone from purrbation.") + // if("massimmerse") // my immursion is ruinned :( + // if(!is_funmin) + // return + // mass_immerse() + // message_admins("[key_name_admin(holder)] has Fully Immersed + // everyone!") + // log_admin("[key_name(holder)] has Fully Immersed everyone.") + // if("unmassimmerse") + // if(!is_funmin) + // return + // mass_immerse(remove=TRUE) + // message_admins("[key_name_admin(holder)] has Un-Fully Immersed + // everyone!") + // log_admin("[key_name(holder)] has Un-Fully Immersed everyone.") if(E) E.processing = FALSE if(E.announceWhen>0) - if(alert(usr, "Would you like to alert the crew?", "Alert", "Yes", "No") == "No") - E.announceWhen = -1 + switch(alert(holder, "Would you like to alert the crew?", "Alert", "Yes", "No", "Cancel")) + if("Cancel") + E.kill() + return + if("No") + E.announceWhen = -1 E.processing = TRUE - if (usr) - log_admin("[key_name(usr)] used secret [item]") - if (ok) - to_chat(world, text("A secret has been activated by []!", usr.key)) + if(holder) + log_admin("[key_name(holder)] used secret [action]") + if(ok) + to_chat(world, text("A secret has been activated by []!", holder.key), confidential = TRUE) /proc/portalAnnounce(announcement, playlightning) - set waitfor = 0 + set waitfor = FALSE if (playlightning) sound_to_playing_players('sound/magic/lightning_chargeup.ogg') sleep(80) @@ -697,11 +613,11 @@ var/mob/chosen = players[1] if (chosen.client) chosen.client.prefs.copy_to(spawnedMob) - chosen.transfer_ckey(spawnedMob) + spawnedMob.key = chosen.key players -= chosen if (ishuman(spawnedMob) && ispath(humanoutfit, /datum/outfit)) var/mob/living/carbon/human/H = spawnedMob H.equipOutfit(humanoutfit) var/turf/T = get_step(loc, SOUTHWEST) flick_overlay_static(portal_appearance, T, 15) - playsound(T, 'sound/magic/lightningbolt.ogg', rand(80, 100), 1) + playsound(T, 'sound/magic/lightningbolt.ogg', rand(80, 100), TRUE) diff --git a/code/modules/admin/view_variables/filterrific.dm b/code/modules/admin/view_variables/filterrific.dm new file mode 100644 index 0000000000..e651028cbe --- /dev/null +++ b/code/modules/admin/view_variables/filterrific.dm @@ -0,0 +1,99 @@ +/datum/filter_editor + var/atom/target + +/datum/filter_editor/New(atom/target) + src.target = target + +/datum/filter_editor/ui_state(mob/user) + return GLOB.admin_state + +/datum/filter_editor/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Filteriffic") + ui.open() + +/datum/filter_editor/ui_static_data(mob/user) + var/list/data = list() + data["filter_info"] = GLOB.master_filter_info + return data + +/datum/filter_editor/ui_data() + var/list/data = list() + data["target_name"] = target.name + data["target_filter_data"] = target.filter_data + return data + +/datum/filter_editor/ui_act(action, list/params) + . = ..() + if(.) + return + + switch(action) + if("add_filter") + var/target_name = params["name"] + while(target.filter_data && target.filter_data[target_name]) + target_name = "[target_name]-dupe" + target.add_filter(target_name, params["priority"], list("type" = params["type"])) + . = TRUE + if("remove_filter") + target.remove_filter(params["name"]) + . = TRUE + if("rename_filter") + var/list/filter_data = target.filter_data[params["name"]] + target.remove_filter(params["name"]) + target.add_filter(params["new_name"], filter_data["priority"], filter_data) + . = TRUE + if("edit_filter") + target.remove_filter(params["name"]) + target.add_filter(params["name"], params["priority"], params["new_filter"]) + . = TRUE + if("change_priority") + var/new_priority = params["new_priority"] + target.change_filter_priority(params["name"], new_priority) + . = TRUE + if("transition_filter_value") + target.transition_filter(params["name"], 4, params["new_data"]) + . = TRUE + if("modify_filter_value") + var/list/old_filter_data = target.filter_data[params["name"]] + var/list/new_filter_data = old_filter_data.Copy() + for(var/entry in params["new_data"]) + new_filter_data[entry] = params["new_data"][entry] + for(var/entry in new_filter_data) + if(entry == GLOB.master_filter_info[old_filter_data["type"]]["defaults"][entry]) + new_filter_data.Remove(entry) + target.remove_filter(params["name"]) + target.add_filter(params["name"], old_filter_data["priority"], new_filter_data) + . = TRUE + if("modify_color_value") + var/new_color = input(usr, "Pick new filter color", "Filteriffic Colors!") as color|null + if(new_color) + target.transition_filter(params["name"], 4, list("color" = new_color)) + . = TRUE + if("modify_icon_value") + var/icon/new_icon = input("Pick icon:", "Icon") as null|icon + if(new_icon) + target.filter_data[params["name"]]["icon"] = new_icon + target.update_filters() + . = TRUE + if("mass_apply") + if(!check_rights_for(usr.client, R_FUN)) + to_chat(usr, "Someone awarded you a heart![heart_reason ? " They said: [heart_reason]!" : ""]") + if(!src) + return + prefs.hearted_until = world.realtime + (24 HOURS) + prefs.hearted = TRUE + if(!src) + return + prefs.save_preferences() + /// compiles a full list of verbs and sends it to the browser /client/proc/init_verbs() if(IsAdminAdvancedProcCall()) @@ -1048,3 +1067,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( return TRUE return FALSE +/client/proc/open_filter_editor(atom/in_atom) + if(holder) + holder.filteriffic = new /datum/filter_editor(in_atom) + holder.filteriffic.ui_interact(mob) diff --git a/code/modules/client/player_details.dm b/code/modules/client/player_details.dm index 6b2a936533..0c06d96b64 100644 --- a/code/modules/client/player_details.dm +++ b/code/modules/client/player_details.dm @@ -3,4 +3,21 @@ var/list/logging = list() var/list/post_login_callbacks = list() var/list/post_logout_callbacks = list() + var/list/played_names = list() //List of names this key played under this round var/byond_version = "Unknown" + var/datum/achievement_data/achievements + +/datum/player_details/New(key) + achievements = new(key) + +/proc/log_played_names(ckey, ...) + if(!ckey) + return + if(args.len < 2) + return + var/list/names = args.Copy(2) + var/datum/player_details/P = GLOB.player_details[ckey] + if(P) + for(var/name in names) + if(name) + P.played_names |= name diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm index 90ebe7f6a1..a9ee0f5412 100644 --- a/code/modules/events/immovable_rod.dm +++ b/code/modules/events/immovable_rod.dm @@ -160,6 +160,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 wizard.apply_damage(25, BRUTE) qdel(src) else + U.client.give_award(/datum/award/achievement/misc/feat_of_strength, U) //rod-form wizards would probably make this a lot easier to get so keep it to regular rods only U.visible_message("[U] suplexes [src] into the ground!", "You suplex [src] into the ground!") new /obj/structure/festivus/anchored(drop_location()) new /obj/effect/anomaly/flux(drop_location()) diff --git a/code/modules/mafia/controller.dm b/code/modules/mafia/controller.dm index cd8c382f30..54dcaebeec 100644 --- a/code/modules/mafia/controller.dm +++ b/code/modules/mafia/controller.dm @@ -1,21 +1,21 @@ /** - * The mafia controller handles the mafia minigame in progress. - * It is first created when the first ghost signs up to play. - */ + * The mafia controller handles the mafia minigame in progress. + * It is first created when the first ghost signs up to play. + */ /datum/mafia_controller ///list of observers that should get game updates. var/list/spectators = list() ///all roles in the game, dead or alive. check their game status if you only want living or dead. var/list/all_roles = list() - ///exists to speed up role retrieval, it's a dict. player_role_lookup[player ckey] will give you the role they play + ///exists to speed up role retrieval, it's a dict. `player_role_lookup[player ckey]` will give you the role they play var/list/player_role_lookup = list() ///what part of the game you're playing in. day phases, night phases, judgement phases, etc. var/phase = MAFIA_PHASE_SETUP ///how long the game has gone on for, changes with every sunrise. day one, night one, day two, etc. var/turn = 0 - ///for debugging and testing a full game, or adminbuse. If this is not null, it will use this as a setup. clears when game is over + ///for debugging and testing a full game, or adminbuse. If this is not empty, it will use this as a setup. clears when game is over var/list/custom_setup = list() ///first day has no voting, and thus is shorter var/first_day_phase_period = 20 SECONDS @@ -82,20 +82,20 @@ qdel(map_deleter) /** - * Triggers at beginning of the game when there is a confirmed list of valid, ready players. - * Creates a 100% ready game that has NOT started (no players in bodies) - * Followed by start game - * - * Does the following: - * * Picks map, and loads it - * * Grabs landmarks if it is the first time it's loading - * * Sets up the role list - * * Puts players in each role randomly - * Arguments: - * * setup_list: list of all the datum setups (fancy list of roles) that would work for the game - * * ready_players: list of filtered, sane players (so not playing or disconnected) for the game to put into roles - */ -/datum/mafia_controller/proc/prepare_game(setup_list, ready_players) + * Triggers at beginning of the game when there is a confirmed list of valid, ready players. + * Creates a 100% ready game that has NOT started (no players in bodies) + * Followed by start game + * + * Does the following: + * * Picks map, and loads it + * * Grabs landmarks if it is the first time it's loading + * * Sets up the role list + * * Puts players in each role randomly + * Arguments: + * * setup_list: list of all the datum setups (fancy list of roles) that would work for the game + * * ready_players: list of filtered, sane players (so not playing or disconnected) for the game to put into roles + */ +/datum/mafia_controller/proc/prepare_game(setup_list,ready_players) var/list/possible_maps = subtypesof(/datum/map_template/mafia) var/turf/spawn_area = get_turf(locate(/obj/effect/landmark/mafia_game_area) in GLOB.landmarks_list) @@ -166,23 +166,23 @@ to_chat(M, "[link] MAFIA: [msg] [team_suffix]") /** - * The game by this point is now all set up, and so we can put people in their bodies and start the first phase. - * - * Does the following: - * * Creates bodies for all of the roles with the first proc - * * Starts the first day manually (so no timer) with the second proc - */ + * The game by this point is now all set up, and so we can put people in their bodies and start the first phase. + * + * Does the following: + * * Creates bodies for all of the roles with the first proc + * * Starts the first day manually (so no timer) with the second proc + */ /datum/mafia_controller/proc/start_game() create_bodies() start_day() /** - * How every day starts. - * - * What players do in this phase: - * * If day one, just a small starting period to see who is in the game and check role, leading to the night phase. - * * Otherwise, it's a longer period used to discuss events that happened during the night, leading to the voting phase. - */ + * How every day starts. + * + * What players do in this phase: + * * If day one, just a small starting period to see who is in the game and check role, leading to the night phase. + * * Otherwise, it's a longer period used to discuss events that happened during the night, leading to the voting phase. + */ /datum/mafia_controller/proc/start_day() turn += 1 phase = MAFIA_PHASE_DAY @@ -198,12 +198,12 @@ SStgui.update_uis(src) /** - * Players have finished the discussion period, and now must put up someone to the chopping block. - * - * What players do in this phase: - * * Vote on which player to put up for lynching, leading to the judgement phase. - * * If no votes are case, the judgement phase is skipped, leading to the night phase. - */ + * Players have finished the discussion period, and now must put up someone to the chopping block. + * + * What players do in this phase: + * * Vote on which player to put up for lynching, leading to the judgement phase. + * * If no votes are case, the judgement phase is skipped, leading to the night phase. + */ /datum/mafia_controller/proc/start_voting_phase() phase = MAFIA_PHASE_VOTING next_phase_timer = addtimer(CALLBACK(src, .proc/check_trial, TRUE),voting_phase_period,TIMER_STOPPABLE) //be verbose! @@ -211,21 +211,21 @@ SStgui.update_uis(src) /** - * Players have voted someone up, and now the person must defend themselves while the town votes innocent or guilty. - * - * What players do in this phase: - * * Vote innocent or guilty, if they are not on trial. - * * Defend themselves and wait for judgement, if they are. - * * Leads to the lynch phase. - * Arguments: - * * verbose: boolean, announces whether there were votes or not. after judgement it goes back here with no voting period to end the day. - */ + * Players have voted someone up, and now the person must defend themselves while the town votes innocent or guilty. + * + * What players do in this phase: + * * Vote innocent or guilty, if they are not on trial. + * * Defend themselves and wait for judgement, if they are. + * * Leads to the lynch phase. + * Arguments: + * * verbose: boolean, announces whether there were votes or not. after judgement it goes back here with no voting period to end the day. + */ /datum/mafia_controller/proc/check_trial(verbose = TRUE) var/datum/mafia_role/loser = get_vote_winner("Day")//, majority_of_town = TRUE) - // var/loser_votes = get_vote_count(loser,"Day") + var/loser_votes = get_vote_count(loser,"Day") if(loser) - // if(loser_votes > 12) - // loser.body.client?.give_award(/datum/award/achievement/mafia/universally_hated, loser.body) + if(loser_votes > 12) + award_role(/datum/award/achievement/mafia/universally_hated, loser) send_message("[loser.body.real_name] wins the day vote, Listen to their defense and vote \"INNOCENT\" or \"GUILTY\"!") //refresh the lists judgement_abstain_votes = list() @@ -248,12 +248,12 @@ SStgui.update_uis(src) /** - * Players have voted innocent or guilty on the person on trial, and that person is now killed or returned home. - * - * What players do in this phase: - * * r/watchpeopledie - * * If the accused is killed, their true role is revealed to the rest of the players. - */ + * Players have voted innocent or guilty on the person on trial, and that person is now killed or returned home. + * + * What players do in this phase: + * * r/watchpeopledie + * * If the accused is killed, their true role is revealed to the rest of the players. + */ /datum/mafia_controller/proc/lynch() for(var/i in judgement_innocent_votes) var/datum/mafia_role/role = i @@ -276,25 +276,25 @@ next_phase_timer = addtimer(CALLBACK(src, .proc/check_trial, FALSE),judgement_lynch_period,TIMER_STOPPABLE)// small pause to see the guy dead, no verbosity since we already did this /** - * Teenie helper proc to move players back to their home. - * Used in the above, but also used in the debug button "send all players home" - * Arguments: - * * role: mafia role that is getting sent back to the game. - */ + * Teenie helper proc to move players back to their home. + * Used in the above, but also used in the debug button "send all players home" + * Arguments: + * * role: mafia role that is getting sent back to the game. + */ /datum/mafia_controller/proc/send_home(datum/mafia_role/role) role.body.forceMove(get_turf(role.assigned_landmark)) /** - * Checks to see if a faction (or solo antagonist) has won. - * - * Calculates in this order: - * * counts up town, mafia, and solo - * * solos can count as town members for the purposes of mafia winning - * * sends the amount of living people to the solo antagonists, and see if they won OR block the victory of the teams - * * checks if solos won from above, then if town, then if mafia - * * starts the end of the game if a faction won - * * returns TRUE if someone won the game, halting other procs from continuing in the case of a victory - */ + * Checks to see if a faction (or solo antagonist) has won. + * + * Calculates in this order: + * * counts up town, mafia, and solo + * * solos can count as town members for the purposes of mafia winning + * * sends the amount of living people to the solo antagonists, and see if they won OR block the victory of the teams + * * checks if solos won from above, then if town, then if mafia + * * starts the end of the game if a faction won + * * returns TRUE if someone won the game, halting other procs from continuing in the case of a victory + */ /datum/mafia_controller/proc/check_victory() //needed for achievements var/list/total_town = list() @@ -336,8 +336,7 @@ var/solo_end = FALSE for(var/datum/mafia_role/winner in total_victors) send_message("!! [uppertext(winner.name)] VICTORY !!") - // var/client/winner_client = GLOB.directory[winner.player_key] - // winner_client?.give_award(winner.winner_award, winner.body) + award_role(winner.winner_award, winner) solo_end = TRUE if(solo_end) start_the_end() @@ -345,28 +344,39 @@ if(blocked_victory) return FALSE if(alive_mafia == 0) - // for(var/datum/mafia_role/townie in total_town) - // var/client/townie_client = GLOB.directory[townie.player_key] - // townie_client?.give_award(townie.winner_award, townie.body) + for(var/datum/mafia_role/townie in total_town) + award_role(townie.winner_award, townie) start_the_end("!! TOWN VICTORY !!") return TRUE else if(alive_mafia >= alive_town) //guess could change if town nightkill is added start_the_end("!! MAFIA VICTORY !!") - // for(var/datum/mafia_role/changeling in total_mafia) - // var/client/changeling_client = GLOB.directory[changeling.player_key] - // changeling_client?.give_award(changeling.winner_award, changeling.body) + for(var/datum/mafia_role/changeling in total_mafia) + award_role(changeling.winner_award, changeling) return TRUE /** - * The end of the game is in two procs, because we want a bit of time for players to see eachothers roles. - * Because of how check_victory works, the game is halted in other places by this point. - * - * What players do in this phase: - * * See everyone's role postgame - * * See who won the game - * Arguments: - * * message: string, if non-null it sends it to all players. used to announce team victories while solos are handled in check victory - */ + * Lets the game award roles with all their checks and sanity, prevents achievements given out for debug games + * + * Arguments: + * * award: path of the award + * * role: mafia_role datum to reward. + */ +/datum/mafia_controller/proc/award_role(award, datum/mafia_role/rewarded) + if(custom_setup.len) + return + var/client/role_client = GLOB.directory[rewarded.player_key] + role_client?.give_award(award, rewarded.body) + +/** + * The end of the game is in two procs, because we want a bit of time for players to see eachothers roles. + * Because of how check_victory works, the game is halted in other places by this point. + * + * What players do in this phase: + * * See everyone's role postgame + * * See who won the game + * Arguments: + * * message: string, if non-null it sends it to all players. used to announce team victories while solos are handled in check victory + */ /datum/mafia_controller/proc/start_the_end(message) SEND_SIGNAL(src,COMSIG_MAFIA_GAME_END) if(message) @@ -377,8 +387,8 @@ next_phase_timer = addtimer(CALLBACK(src,.proc/end_game),victory_lap_period,TIMER_STOPPABLE) /** - * Cleans up the game, resetting variables back to the beginning and removing the map with the generator. - */ + * Cleans up the game, resetting variables back to the beginning and removing the map with the generator. + */ /datum/mafia_controller/proc/end_game() map_deleter.generate() //remove the map, it will be loaded at the start of the next one QDEL_LIST(all_roles) @@ -392,17 +402,17 @@ phase = MAFIA_PHASE_SETUP /** - * After the voting and judgement phases, the game goes to night shutting the windows and beginning night with a proc. - */ + * After the voting and judgement phases, the game goes to night shutting the windows and beginning night with a proc. + */ /datum/mafia_controller/proc/lockdown() toggle_night_curtains(close=TRUE) start_night() /** - * Shuts poddoors attached to mafia. - * Arguments: - * * close: boolean, the state you want the curtains in. - */ + * Shuts poddoors attached to mafia. + * Arguments: + * * close: boolean, the state you want the curtains in. + */ /datum/mafia_controller/proc/toggle_night_curtains(close) for(var/obj/machinery/door/poddoor/D in GLOB.machines) //I really dislike pathing of these if(D.id != "mafia") //so as to not trigger shutters on station, lol @@ -413,12 +423,12 @@ INVOKE_ASYNC(D, /obj/machinery/door/poddoor.proc/open) /** - * The actual start of night for players. Mostly info is given at the start of the night as the end of the night is when votes and actions are submitted and tried. - * - * What players do in this phase: - * * Mafia are told to begin voting on who to kill - * * Powers that are picked during the day announce themselves right now - */ + * The actual start of night for players. Mostly info is given at the start of the night as the end of the night is when votes and actions are submitted and tried. + * + * What players do in this phase: + * * Mafia are told to begin voting on who to kill + * * Powers that are picked during the day announce themselves right now + */ /datum/mafia_controller/proc/start_night() phase = MAFIA_PHASE_NIGHT send_message("Night [turn] started! Lockdown will end in 45 seconds.") @@ -427,16 +437,16 @@ SStgui.update_uis(src) /** - * The end of the night, and a series of signals for the order of events on a night. - * - * Order of events, and what they mean: - * * Start of resolve (NIGHT_START) is for activating night abilities that MUST go first - * * Action phase (NIGHT_ACTION_PHASE) is for non-lethal day abilities - * * Mafia then tallies votes and kills the highest voted person (note: one random voter visits that person for the purposes of roleblocking) - * * Killing phase (NIGHT_KILL_PHASE) is for lethal night abilities - * * End of resolve (NIGHT_END) is for cleaning up abilities that went off and i guess doing some that must go last - * * Finally opens the curtains and calls the start of day phase, completing the cycle until check victory returns TRUE - */ + * The end of the night, and a series of signals for the order of events on a night. + * + * Order of events, and what they mean: + * * Start of resolve (NIGHT_START) is for activating night abilities that MUST go first + * * Action phase (NIGHT_ACTION_PHASE) is for non-lethal day abilities + * * Mafia then tallies votes and kills the highest voted person (note: one random voter visits that person for the purposes of roleblocking) + * * Killing phase (NIGHT_KILL_PHASE) is for lethal night abilities + * * End of resolve (NIGHT_END) is for cleaning up abilities that went off and i guess doing some that must go last + * * Finally opens the curtains and calls the start of day phase, completing the cycle until check victory returns TRUE + */ /datum/mafia_controller/proc/resolve_night() SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_START) SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_ACTION_PHASE) @@ -457,15 +467,15 @@ SStgui.update_uis(src) /** - * Proc that goes off when players vote for something with their mafia panel. - * - * If teams, it hides the tally overlay and only sends the vote messages to the team that is voting - * Arguments: - * * voter: the mafia role that is trying to vote for... - * * target: the mafia role that is getting voted for - * * vote_type: type of vote submitted (is this the day vote? is this the mafia night vote?) - * * teams: see mafia team defines for what to put in, makes the messages only send to a specific team (so mafia night votes only sending messages to mafia at night) - */ + * Proc that goes off when players vote for something with their mafia panel. + * + * If teams, it hides the tally overlay and only sends the vote messages to the team that is voting + * Arguments: + * * voter: the mafia role that is trying to vote for... + * * target: the mafia role that is getting voted for + * * vote_type: type of vote submitted (is this the day vote? is this the mafia night vote?) + * * teams: see mafia team defines for what to put in, makes the messages only send to a specific team (so mafia night votes only sending messages to mafia at night) + */ /datum/mafia_controller/proc/vote_for(datum/mafia_role/voter,datum/mafia_role/target,vote_type, teams) if(!votes[vote_type]) votes[vote_type] = list() @@ -485,8 +495,8 @@ old.body.update_icon() /** - * Clears out the votes of a certain type (day votes, mafia kill votes) while leaving others untouched - */ + * Clears out the votes of a certain type (day votes, mafia kill votes) while leaving others untouched + */ /datum/mafia_controller/proc/reset_votes(vote_type) var/list/bodies_to_update = list() for(var/vote in votes[vote_type]) @@ -497,11 +507,11 @@ M.update_icon() /** - * Returns how many people voted for the role, in whatever vote (day vote, night kill vote) - * Arguments: - * * role: the mafia role the proc tries to get the amount of votes for - * * vote_type: the vote type (getting how many day votes were for the role, or mafia night votes for the role) - */ + * Returns how many people voted for the role, in whatever vote (day vote, night kill vote) + * Arguments: + * * role: the mafia role the proc tries to get the amount of votes for + * * vote_type: the vote type (getting how many day votes were for the role, or mafia night votes for the role) + */ /datum/mafia_controller/proc/get_vote_count(role,vote_type) . = 0 for(var/v in votes[vote_type]) @@ -510,11 +520,11 @@ . += votee.vote_power /** - * Returns whichever role got the most votes, in whatever vote (day vote, night kill vote) - * returns null if no votes - * Arguments: - * * vote_type: the vote type (getting the role that got the most day votes, or the role that got the most mafia votes) - */ + * Returns whichever role got the most votes, in whatever vote (day vote, night kill vote) + * returns null if no votes + * Arguments: + * * vote_type: the vote type (getting the role that got the most day votes, or the role that got the most mafia votes) + */ /datum/mafia_controller/proc/get_vote_winner(vote_type) var/list/tally = list() for(var/votee in votes[vote_type]) @@ -526,21 +536,23 @@ return length(tally) ? tally[1] : null /** - * Returns a random person who voted for whatever vote (day vote, night kill vote) - * Arguments: - * * vote_type: vote type (getting a random day voter, or mafia night voter) - */ + * Returns a random person who voted for whatever vote (day vote, night kill vote) + * Arguments: + * * vote_type: vote type (getting a random day voter, or mafia night voter) + */ /datum/mafia_controller/proc/get_random_voter(vote_type) if(length(votes[vote_type])) return pick(votes[vote_type]) /** - * Adds mutable appearances to people who get publicly voted on (so not night votes) showing how many people are picking them - * Arguments: - * * source: the body of the role getting the overlays - * * overlay_list: signal var passing the overlay list of the mob - */ + * Adds mutable appearances to people who get publicly voted on (so not night votes) showing how many people are picking them + * Arguments: + * * source: the body of the role getting the overlays + * * overlay_list: signal var passing the overlay list of the mob + */ /datum/mafia_controller/proc/display_votes(atom/source, list/overlay_list) + SIGNAL_HANDLER + if(phase != MAFIA_PHASE_VOTING) return var/v = get_vote_count(player_role_lookup[source],"Day") @@ -548,14 +560,14 @@ overlay_list += MA /** - * Called when the game is setting up, AFTER map is loaded but BEFORE the phase timers start. Creates and places each role's body and gives the correct player key - * - * Notably: - * * Toggles godmode so the mafia players cannot kill themselves - * * Adds signals for voting overlays, see display_votes proc - * * gives mafia panel - * * sends the greeting text (goals, role name, etc) - */ + * Called when the game is setting up, AFTER map is loaded but BEFORE the phase timers start. Creates and places each role's body and gives the correct player key + * + * Notably: + * * Toggles godmode so the mafia players cannot kill themselves + * * Adds signals for voting overlays, see display_votes proc + * * gives mafia panel + * * sends the greeting text (goals, role name, etc) + */ /datum/mafia_controller/proc/create_bodies() for(var/datum/mafia_role/role in all_roles) var/mob/living/carbon/human/H = new(get_turf(role.assigned_landmark)) @@ -716,13 +728,14 @@ if(GLOB.mafia_signup[C.ckey]) GLOB.mafia_signup -= C.ckey to_chat(usr, "You unregister from Mafia.") - return + return TRUE else GLOB.mafia_signup[C.ckey] = C to_chat(usr, "You sign up for Mafia.") if(phase == MAFIA_PHASE_SETUP) check_signups() try_autostart() + return TRUE if("mf_spectate") if(C.ckey in spectators) to_chat(usr, "You will no longer get messages from the game.") @@ -730,6 +743,7 @@ else to_chat(usr, "You will now get messages from the game.") spectators += C.ckey + return TRUE if(user_role.game_status == MAFIA_DEAD) return //User actions (just living) @@ -800,13 +814,13 @@ . += L[key] /** - * Returns a semirandom setup, with... - * Town, Two invest roles, one protect role, sometimes a misc role, and the rest assistants for town. - * Mafia, 2 normal mafia and one special. - * Neutral, two disruption roles, sometimes one is a killing. - * - * See _defines.dm in the mafia folder for a rundown on what these groups of roles include. - */ + * Returns a semirandom setup, with... + * Town, Two invest roles, one protect role, sometimes a misc role, and the rest assistants for town. + * Mafia, 2 normal mafia and one special. + * Neutral, two disruption roles, sometimes one is a killing. + * + * See _defines.dm in the mafia folder for a rundown on what these groups of roles include. + */ /datum/mafia_controller/proc/generate_random_setup() var/invests_left = 2 var/protects_left = 1 @@ -845,8 +859,8 @@ return random_setup /** - * Helper proc that adds a random role of a type to a setup. if it doesn't exist in the setup, it adds the path to the list and otherwise bumps the path in the list up one - */ + * Helper proc that adds a random role of a type to a setup. if it doesn't exist in the setup, it adds the path to the list and otherwise bumps the path in the list up one + */ /datum/mafia_controller/proc/add_setup_role(setup_list, wanted_role_type) var/list/role_type_paths = list() for(var/path in typesof(/datum/mafia_role)) @@ -868,17 +882,17 @@ setup_list[mafia_path] = 1 /** - * Called when enough players have signed up to fill a setup. DOESN'T NECESSARILY MEAN THE GAME WILL START. - * - * Checks for a custom setup, if so gets the required players from that and if not it sets the player requirement to required_player(max_player) and generates one IF basic setup starts a game. - * Checks if everyone signed up is an observer, and is still connected. If people aren't, they're removed from the list. - * If there aren't enough players post sanity, it aborts. otherwise, it selects enough people for the game and starts preparing the game for real. - */ + * Called when enough players have signed up to fill a setup. DOESN'T NECESSARILY MEAN THE GAME WILL START. + * + * Checks for a custom setup, if so gets the required players from that and if not it sets the player requirement to MAFIA_MAX_PLAYER_COUNT and generates one IF basic setup starts a game. + * Checks if everyone signed up is an observer, and is still connected. If people aren't, they're removed from the list. + * If there aren't enough players post sanity, it aborts. otherwise, it selects enough people for the game and starts preparing the game for real. + */ /datum/mafia_controller/proc/basic_setup() var/req_players var/list/setup = custom_setup if(!setup.len) - req_players = required_player //max_player + req_players = max_player //MAFIA_MAX_PLAYER_COUNT else req_players = assoc_value_sum(setup) @@ -918,10 +932,10 @@ start_game() /** - * Called when someone signs up, and sees if there are enough people in the signup list to begin. - * - * Only checks if everyone is actually valid to start (still connected and an observer) if there are enough players (basic_setup) - */ + * Called when someone signs up, and sees if there are enough people in the signup list to begin. + * + * Only checks if everyone is actually valid to start (still connected and an observer) if there are enough players (basic_setup) + */ /datum/mafia_controller/proc/try_autostart() if(phase != MAFIA_PHASE_SETUP) // || !(GLOB.ghost_role_flags & GHOSTROLE_MINIGAME)) return @@ -929,10 +943,10 @@ basic_setup() /** - * Filters inactive player into a different list until they reconnect, and removes players who are no longer ghosts. - * - * If a disconnected player gets a non-ghost mob and reconnects, they will be first put back into mafia_signup then filtered by that. - */ + * Filters inactive player into a different list until they reconnect, and removes players who are no longer ghosts. + * + * If a disconnected player gets a non-ghost mob and reconnects, they will be first put back into mafia_signup then filtered by that. + */ /datum/mafia_controller/proc/check_signups() for(var/bad_key in GLOB.mafia_bad_signup) if(GLOB.directory[bad_key])//they have reconnected if we can search their key and get a client @@ -962,8 +976,8 @@ parent.ui_interact(owner) /** - * Creates the global datum for playing mafia games, destroys the last if that's required and returns the new. - */ + * Creates the global datum for playing mafia games, destroys the last if that's required and returns the new. + */ /proc/create_mafia_game() if(GLOB.mafia_game) QDEL_NULL(GLOB.mafia_game) diff --git a/code/modules/mafia/roles.dm b/code/modules/mafia/roles.dm index 2461a93976..34cbd75bfc 100644 --- a/code/modules/mafia/roles.dm +++ b/code/modules/mafia/roles.dm @@ -19,7 +19,7 @@ var/list/actions = list() var/list/targeted_actions = list() //what the role gets when it wins a game - // var/winner_award = /datum/award/achievement/mafia/assistant + var/winner_award = /datum/award/achievement/mafia/assistant //so mafia have to also kill them to have a majority var/solo_counts_as_town = FALSE //(don't set this for town) @@ -124,7 +124,7 @@ desc = "You can investigate a single person each night to learn their team." revealed_outfit = /datum/outfit/mafia/detective role_type = TOWN_INVEST - // winner_award = /datum/award/achievement/mafia/detective + winner_award = /datum/award/achievement/mafia/detective hud_icon = "huddetective" revealed_icon = "detective" @@ -151,6 +151,8 @@ current_investigation = target /datum/mafia_role/detective/proc/investigate(datum/mafia_controller/game) + SIGNAL_HANDLER + var/datum/mafia_role/target = current_investigation if(target) if(target.detect_immune) @@ -178,7 +180,7 @@ desc = "You can visit someone ONCE PER GAME to reveal their true role in the morning!" revealed_outfit = /datum/outfit/mafia/psychologist role_type = TOWN_INVEST - // winner_award = /datum/award/achievement/mafia/psychologist + winner_award = /datum/award/achievement/mafia/psychologist hud_icon = "hudpsychologist" revealed_icon = "psychologist" @@ -202,6 +204,8 @@ current_target = target /datum/mafia_role/psychologist/proc/therapy_reveal(datum/mafia_controller/game) + SIGNAL_HANDLER + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"reveal",current_target) & MAFIA_PREVENT_ACTION || game_status != MAFIA_ALIVE) //Got lynched or roleblocked by a lawyer. current_target = null if(current_target) @@ -218,7 +222,7 @@ role_type = TOWN_INVEST hud_icon = "hudchaplain" revealed_icon = "chaplain" - // winner_award = /datum/award/achievement/mafia/chaplain + winner_award = /datum/award/achievement/mafia/chaplain targeted_actions = list("Pray") var/current_target @@ -238,6 +242,8 @@ current_target = target /datum/mafia_role/chaplain/proc/commune(datum/mafia_controller/game) + SIGNAL_HANDLER + var/datum/mafia_role/target = current_target if(target) to_chat(body,"You invoke spirit of [target.body.real_name] and learn their role was [target.name].") @@ -251,7 +257,7 @@ role_type = TOWN_PROTECT hud_icon = "hudmedicaldoctor" revealed_icon = "medicaldoctor" - // winner_award = /datum/award/achievement/mafia/md + winner_award = /datum/award/achievement/mafia/md targeted_actions = list("Protect") var/datum/mafia_role/current_protected @@ -277,16 +283,22 @@ current_protected = target /datum/mafia_role/md/proc/protect(datum/mafia_controller/game) + SIGNAL_HANDLER + if(current_protected) RegisterSignal(current_protected,COMSIG_MAFIA_ON_KILL,.proc/prevent_kill) add_note("N[game.turn] - Protected [current_protected.body.real_name]") /datum/mafia_role/md/proc/prevent_kill(datum/source) + SIGNAL_HANDLER + to_chat(body,"The person you protected tonight was attacked!") to_chat(current_protected.body,"You were attacked last night, but someone nursed you back to life!") return MAFIA_PREVENT_KILL /datum/mafia_role/md/proc/end_protection(datum/mafia_controller/game) + SIGNAL_HANDLER + if(current_protected) UnregisterSignal(current_protected,COMSIG_MAFIA_ON_KILL) current_protected = null @@ -298,7 +310,7 @@ role_type = TOWN_PROTECT hud_icon = "hudlawyer" revealed_icon = "lawyer" - // winner_award = /datum/award/achievement/mafia/lawyer + winner_award = /datum/award/achievement/mafia/lawyer targeted_actions = list("Advise") var/datum/mafia_role/current_target @@ -310,6 +322,8 @@ RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/release) /datum/mafia_role/lawyer/proc/roleblock_text(datum/mafia_controller/game) + SIGNAL_HANDLER + if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"roleblock",current_target) & MAFIA_PREVENT_ACTION || game_status != MAFIA_ALIVE) //Got lynched or roleblocked by another lawyer. current_target = null if(current_target) @@ -335,16 +349,22 @@ to_chat(body,"You will block [target.body.real_name] tonight.") /datum/mafia_role/lawyer/proc/try_to_roleblock(datum/mafia_controller/game) + SIGNAL_HANDLER + if(current_target) RegisterSignal(current_target,COMSIG_MAFIA_CAN_PERFORM_ACTION, .proc/prevent_action) /datum/mafia_role/lawyer/proc/release(datum/mafia_controller/game) + SIGNAL_HANDLER + . = ..() if(current_target) UnregisterSignal(current_target, COMSIG_MAFIA_CAN_PERFORM_ACTION) current_target = null /datum/mafia_role/lawyer/proc/prevent_action(datum/source) + SIGNAL_HANDLER + if(game_status == MAFIA_ALIVE) //in case we got killed while imprisoning sk - bad luck edge return MAFIA_PREVENT_ACTION @@ -355,7 +375,7 @@ role_type = TOWN_MISC hud_icon = "hudheadofpersonnel" revealed_icon = "headofpersonnel" - // winner_award = /datum/award/achievement/mafia/hop + winner_award = /datum/award/achievement/mafia/hop targeted_actions = list("Reveal") @@ -378,7 +398,7 @@ role_type = MAFIA_REGULAR hud_icon = "hudchangeling" revealed_icon = "changeling" - // winner_award = /datum/award/achievement/mafia/changeling + winner_award = /datum/award/achievement/mafia/changeling revealed_outfit = /datum/outfit/mafia/changeling special_theme = "syndicate" @@ -389,6 +409,8 @@ RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/mafia_text) /datum/mafia_role/mafia/proc/mafia_text(datum/mafia_controller/source) + SIGNAL_HANDLER + to_chat(body,"Vote for who to kill tonight. The killer will be chosen randomly from voters.") //better detective for mafia @@ -398,7 +420,7 @@ role_type = MAFIA_SPECIAL hud_icon = "hudthoughtfeeder" revealed_icon = "thoughtfeeder" - // winner_award = /datum/award/achievement/mafia/thoughtfeeder + winner_award = /datum/award/achievement/mafia/thoughtfeeder targeted_actions = list("Learn Role") var/datum/mafia_role/current_investigation @@ -418,6 +440,8 @@ current_investigation = target /datum/mafia_role/mafia/thoughtfeeder/proc/investigate(datum/mafia_controller/game) + SIGNAL_HANDLER + var/datum/mafia_role/target = current_investigation current_investigation = null if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"thoughtfeed",target) & MAFIA_PREVENT_ACTION) @@ -441,7 +465,7 @@ win_condition = "kill everyone." team = MAFIA_TEAM_SOLO role_type = NEUTRAL_KILL - // winner_award = /datum/award/achievement/mafia/traitor + winner_award = /datum/award/achievement/mafia/traitor targeted_actions = list("Night Kill") revealed_outfit = /datum/outfit/mafia/traitor @@ -464,6 +488,8 @@ return TRUE //while alive, town AND mafia cannot win (though since mafia know who is who it's pretty easy to win from that point) /datum/mafia_role/traitor/proc/nightkill_immunity(datum/source,datum/mafia_controller/game,lynch) + SIGNAL_HANDLER + if(game.phase == MAFIA_PHASE_NIGHT && !lynch) to_chat(body,"You were attacked, but they'll have to try harder than that to put you down.") return MAFIA_PREVENT_KILL @@ -481,6 +507,8 @@ to_chat(body,"You will attempt to kill [target.body.real_name] tonight.") /datum/mafia_role/traitor/proc/try_to_kill(datum/mafia_controller/source) + SIGNAL_HANDLER + var/datum/mafia_role/target = current_victim current_victim = null if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,source,"traitor kill",target) & MAFIA_PREVENT_ACTION) @@ -500,7 +528,7 @@ special_theme = "neutral" hud_icon = "hudnightmare" revealed_icon = "nightmare" - // winner_award = /datum/award/achievement/mafia/nightmare + winner_award = /datum/award/achievement/mafia/nightmare targeted_actions = list("Flicker", "Hunt") var/list/flickering = list() @@ -543,6 +571,8 @@ to_chat(body,"You will hunt everyone in a flickering room down tonight.") /datum/mafia_role/nightmare/proc/flicker_or_hunt(datum/mafia_controller/source) + SIGNAL_HANDLER + if(game_status != MAFIA_ALIVE || !flicker_target) return if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,source,"nightmare actions",flicker_target) & MAFIA_PREVENT_ACTION) @@ -576,7 +606,7 @@ special_theme = "neutral" hud_icon = "hudfugitive" revealed_icon = "fugitive" - // winner_award = /datum/award/achievement/mafia/fugitive + winner_award = /datum/award/achievement/mafia/fugitive actions = list("Self Preservation") var/charges = 2 @@ -604,11 +634,15 @@ protection_status = !protection_status /datum/mafia_role/fugitive/proc/night_start(datum/mafia_controller/game) + SIGNAL_HANDLER + if(protection_status == FUGITIVE_WILL_PRESERVE) to_chat(body,"Your preparations are complete. Nothing could kill you tonight!") RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/prevent_death) /datum/mafia_role/fugitive/proc/night_end(datum/mafia_controller/game) + SIGNAL_HANDLER + if(protection_status == FUGITIVE_WILL_PRESERVE) charges-- UnregisterSignal(src,COMSIG_MAFIA_ON_KILL) @@ -616,13 +650,16 @@ protection_status = FUGITIVE_NOT_PRESERVING /datum/mafia_role/fugitive/proc/prevent_death(datum/mafia_controller/game) + SIGNAL_HANDLER + to_chat(body,"You were attacked! Luckily, you were ready for this!") return MAFIA_PREVENT_KILL /datum/mafia_role/fugitive/proc/survived(datum/mafia_controller/game) + SIGNAL_HANDLER + if(game_status == MAFIA_ALIVE) - // var/client/winner_client = GLOB.directory[player_key] - // winner_client?.give_award(winner_award, body) + game.award_role(winner_award, src) game.send_message("!! FUGITIVE VICTORY !!") #undef FUGITIVE_NOT_PRESERVING @@ -640,7 +677,7 @@ hud_icon = "hudobsessed" revealed_icon = "obsessed" - // winner_award = /datum/award/achievement/mafia/obsessed + winner_award = /datum/award/achievement/mafia/obsessed revealed_outfit = /datum/outfit/mafia/obsessed // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these) solo_counts_as_town = TRUE //after winning or whatever, can side with whoever. they've already done their objective! @@ -652,6 +689,8 @@ RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/find_obsession) /datum/mafia_role/obsessed/proc/find_obsession(datum/mafia_controller/game) + SIGNAL_HANDLER + var/list/all_roles_shuffle = shuffle(game.all_roles) for(var/role in all_roles_shuffle) var/datum/mafia_role/possible = role @@ -667,13 +706,14 @@ UnregisterSignal(game,COMSIG_MAFIA_SUNDOWN) /datum/mafia_role/obsessed/proc/check_victory(datum/source,datum/mafia_controller/game,lynch) + SIGNAL_HANDLER + UnregisterSignal(source,COMSIG_MAFIA_ON_KILL) if(game_status == MAFIA_DEAD) return if(lynch) game.send_message("!! OBSESSED VICTORY !!") - // var/client/winner_client = GLOB.directory[player_key] - // winner_client?.give_award(winner_award, body) + game.award_role(winner_award, src) reveal_role(game, FALSE) else to_chat(body, "You have failed your objective to lynch [obsession.body]!") @@ -689,17 +729,18 @@ special_theme = "neutral" hud_icon = "hudclown" revealed_icon = "clown" - // winner_award = /datum/award/achievement/mafia/clown + winner_award = /datum/award/achievement/mafia/clown /datum/mafia_role/clown/New(datum/mafia_controller/game) . = ..() RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/prank) /datum/mafia_role/clown/proc/prank(datum/source,datum/mafia_controller/game,lynch) + SIGNAL_HANDLER + if(lynch) var/datum/mafia_role/victim = pick(game.judgement_guilty_votes + game.judgement_abstain_votes) game.send_message("[body.real_name] WAS A CLOWN! HONK! They take down [victim.body.real_name] with their last prank.") game.send_message("!! CLOWN VICTORY !!") - // var/client/winner_client = GLOB.directory[player_key] - // winner_client?.give_award(winner_award, body) + game.award_role(winner_award, src) victim.kill(game,FALSE) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index a82151cc1d..50f2922df7 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -75,6 +75,7 @@ var/lastpuke = 0 var/account_id var/last_fire_update + var/hardcore_survival_score = 0 /// Unarmed parry data for human /datum/block_parry_data/unarmed/human diff --git a/code/modules/mob/living/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm index e7c5644e26..deb3a101e0 100644 --- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm +++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm @@ -14,8 +14,8 @@ model = "Cleanbot" bot_core_type = /obj/machinery/bot_core/cleanbot window_id = "autoclean" - window_name = "Automatic Station Cleaner v1.3" - pass_flags = PASSMOB + window_name = "Automatic Station Cleaner v1.4" + pass_flags = PASSMOB // | PASSFLAPS path_image_color = "#993299" weather_immunities = list("lava","ash") @@ -25,6 +25,7 @@ var/blood = 1 var/trash = 0 var/pests = 0 + var/drawn = 0 var/list/target_types var/obj/effect/decal/cleanable/target @@ -53,6 +54,9 @@ var/list/prefixes var/list/suffixes + var/ascended = FALSE // if we have all the top titles, grant achievements to living mobs that gaze upon our cleanbot god + + /mob/living/simple_animal/bot/cleanbot/proc/deputize(obj/item/W, mob/user) if(in_range(src, user)) to_chat(user, "You attach \the [W] to \the [src].") @@ -66,6 +70,8 @@ /mob/living/simple_animal/bot/cleanbot/proc/update_titles() var/working_title = "" + ascended = TRUE + for(var/pref in prefixes) for(var/title in pref) if(title in stolen_valor) @@ -73,6 +79,8 @@ if(title in officers) commissioned = TRUE break + else + ascended = FALSE // we didn't have the first entry in the list if we got here, so we're not achievement worthy yet working_title += chosen_name @@ -81,6 +89,8 @@ if(title in stolen_valor) working_title += " " + suf[title] break + else + ascended = FALSE name = working_title @@ -89,8 +99,12 @@ if(weapon) . += " Is that \a [weapon] taped to it...?" + if(ascended && user.stat == CONSCIOUS && user.client) + user.client.give_award(/datum/award/achievement/misc/cleanboss, user) + /mob/living/simple_animal/bot/cleanbot/Initialize() . = ..() + chosen_name = name get_targets() icon_state = "cleanbot[on]" @@ -98,7 +112,6 @@ var/datum/job/janitor/J = new/datum/job/janitor access_card.access += J.get_access() prev_access = access_card.access - stolen_valor = list() prefixes = list(command, security, engineering) @@ -123,7 +136,7 @@ /mob/living/simple_animal/bot/cleanbot/bot_reset() ..() - if(weapon && (emagged == 2)) + if(weapon && emagged == 2) weapon.force = weapon_orig_force ignore_list = list() //Allows the bot to clean targets it previously ignored due to being unreachable. target = null @@ -151,7 +164,7 @@ C.Knockdown(20) /mob/living/simple_animal/bot/cleanbot/attackby(obj/item/W, mob/user, params) - if(istype(W, /obj/item/card/id)||istype(W, /obj/item/pda)) + if(W.GetID()) if(bot_core.allowed(user) && !open && !emagged) locked = !locked to_chat(user, "You [ locked ? "lock" : "unlock"] \the [src] behaviour controls.") @@ -161,7 +174,7 @@ if(open) to_chat(user, "Please close the access panel before locking it.") else - to_chat(user, "The [src] doesn't seem to respect your authority.") + to_chat(user, "\The [src] doesn't seem to respect your authority.") else if(istype(W, /obj/item/kitchen/knife) && user.a_intent != INTENT_HARM) to_chat(user, "You start attaching \the [W] to \the [src]...") @@ -203,7 +216,8 @@ return ..() /mob/living/simple_animal/bot/cleanbot/emag_act(mob/user) - . = ..() + ..() + if(emagged == 2) if(weapon) weapon.force = weapon_orig_force @@ -259,6 +273,9 @@ if(!target && trash) //Then for trash. target = scan(/obj/item/trash) + // if(!target && trash) //Search for dead mices. + // target = scan(/obj/item/food/deadmouse) + if(!target && auto_patrol) //Search for cleanables it can see. if(mode == BOT_IDLE || mode == BOT_START_PATROL) start_patrol() @@ -335,13 +352,18 @@ target_types += /mob/living/simple_animal/cockroach target_types += /mob/living/simple_animal/mouse + if(drawn) + target_types += /obj/effect/decal/cleanable/crayon + if(trash) target_types += /obj/item/trash target_types += /obj/item/reagent_containers/food/snacks/meat/slab/human target_types = typecacheof(target_types) -/mob/living/simple_animal/bot/cleanbot/UnarmedAttack(atom/A, proximity, intent = a_intent, flags = NONE) +/mob/living/simple_animal/bot/cleanbot/UnarmedAttack(atom/A) + if(HAS_TRAIT(src, TRAIT_HANDS_BLOCKED)) + return if(istype(A, /obj/effect/decal/cleanable)) anchored = TRUE icon_state = "cleanbot-c" @@ -361,8 +383,9 @@ icon_state = "cleanbot[on]" else if(istype(A, /obj/item) || istype(A, /obj/effect/decal/remains)) visible_message("[src] sprays hydrofluoric acid at [A]!") - playsound(src, 'sound/effects/spray2.ogg', 50, 1, -6) + playsound(src, 'sound/effects/spray2.ogg', 50, TRUE, -6) A.acid_act(75, 10) + target = null else if(istype(A, /mob/living/simple_animal/cockroach) || istype(A, /mob/living/simple_animal/mouse)) var/mob/living/simple_animal/M = target if(!M.stat) @@ -383,7 +406,7 @@ "FREED AT LEST FROM FILTHY PROGRAMMING.") say(phrase) victim.emote("scream") - playsound(src.loc, 'sound/effects/spray2.ogg', 50, 1, -6) + playsound(src.loc, 'sound/effects/spray2.ogg', 50, TRUE, -6) victim.acid_act(5, 100) else if(A == src) // Wets floors and spawns foam randomly if(prob(75)) @@ -412,10 +435,14 @@ do_sparks(3, TRUE, src) ..() +/mob/living/simple_animal/bot/cleanbot/medbay + name = "Scrubs, MD" + bot_core_type = /obj/machinery/bot_core/cleanbot/medbay + on = FALSE + /obj/machinery/bot_core/cleanbot req_one_access = list(ACCESS_JANITOR, ACCESS_ROBOTICS) - /mob/living/simple_animal/bot/cleanbot/get_controls(mob/user) var/dat dat += hack(user) @@ -424,9 +451,10 @@ Status: [on ? "On" : "Off"]
    Behaviour controls are [locked ? "locked" : "unlocked"]
    Maintenance panel panel is [open ? "opened" : "closed"]"}) - if(!locked || hasSiliconAccessInArea(user)|| IsAdminGhost(user)) + if(!locked || issilicon(user)|| IsAdminGhost(user)) dat += "
    Clean Blood: [blood ? "Yes" : "No"]" dat += "
    Clean Trash: [trash ? "Yes" : "No"]" + dat += "
    Clean Graffiti: [drawn ? "Yes" : "No"]" dat += "
    Exterminate Pests: [pests ? "Yes" : "No"]" dat += "

    Patrol Station: [auto_patrol ? "Yes" : "No"]" return dat @@ -442,5 +470,10 @@ Maintenance panel panel is [open ? "opened" : "closed"]"}) pests = !pests if("trash") trash = !trash + if("drawn") + drawn = !drawn get_targets() update_controls() + +/obj/machinery/bot_core/cleanbot/medbay + req_one_access = list(ACCESS_JANITOR, ACCESS_ROBOTICS, ACCESS_MEDICAL) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm index 2a5f279386..ffad0ca3ee 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm @@ -44,6 +44,9 @@ Difficulty: Medium wander = FALSE del_on_death = TRUE blood_volume = BLOOD_VOLUME_NORMAL + achievement_type = /datum/award/achievement/boss/blood_miner_kill + crusher_achievement_type = /datum/award/achievement/boss/blood_miner_crusher + score_achievement_type = /datum/award/score/blood_miner_score medal_type = BOSS_MEDAL_MINER var/obj/item/melee/transforming/cleaving_saw/miner/miner_saw var/time_until_next_transform = 0 diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index 519d6402e6..d5b78b14b6 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -51,8 +51,11 @@ Difficulty: Hard crusher_loot = list(/obj/structure/closet/crate/necropolis/bubblegum/crusher) loot = list(/obj/structure/closet/crate/necropolis/bubblegum) var/charging = 0 - medal_type = BOSS_MEDAL_BUBBLEGUM - score_type = BUBBLEGUM_SCORE + + achievement_type = /datum/award/achievement/boss/bubblegum_kill + crusher_achievement_type = /datum/award/achievement/boss/bubblegum_crusher + score_achievement_type = /datum/award/score/bubblegum_score + deathmessage = "sinks into a pool of blood, fleeing the battle. You've won, for now... " death_sound = 'sound/magic/enter_blood.ogg' @@ -153,6 +156,20 @@ Difficulty: Hard charging = 0 Goto(target, move_to_delay, minimum_distance) +/** + * Attack by override for bubblegum + * + * This is used to award the frenching achievement for hitting bubblegum with a tongue + * + * Arguments: + * * obj/item/W the item hitting bubblegum + * * mob/user The user of the item + * * params, extra parameters + */ +/mob/living/simple_animal/hostile/megafauna/bubblegum/attackby(obj/item/W, mob/user, params) + . = ..() + if(istype(W, /obj/item/organ/tongue)) + user.client?.give_award(/datum/award/achievement/misc/frenching, user) /mob/living/simple_animal/hostile/megafauna/bubblegum/Bump(atom/A) if(charging) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index a584d34995..73efad31a5 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -43,9 +43,10 @@ Difficulty: Very Hard move_to_delay = 10 ranged = 1 pixel_x = -32 - del_on_death = 1 - medal_type = BOSS_MEDAL_COLOSSUS - score_type = COLOSSUS_SCORE + del_on_death = TRUE + achievement_type = /datum/award/achievement/boss/colossus_kill + crusher_achievement_type = /datum/award/achievement/boss/colossus_crusher + score_achievement_type = /datum/award/score/colussus_score crusher_loot = list(/obj/structure/closet/crate/necropolis/colossus/crusher) loot = list(/obj/structure/closet/crate/necropolis/colossus) butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/animalhide/ashdrake = 10, /obj/item/stack/sheet/bone = 30) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner.dm index 5e866f95f1..2bcca74f30 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner.dm @@ -31,6 +31,9 @@ Difficulty: Extremely Hard wander = FALSE del_on_death = TRUE blood_volume = BLOOD_VOLUME_NORMAL + achievement_type = /datum/award/achievement/boss/demonic_miner_kill + crusher_achievement_type = /datum/award/achievement/boss/demonic_miner_crusher + score_achievement_type = /datum/award/score/demonic_miner_score deathmessage = "falls to the ground, decaying into plasma particles." deathsound = "bodyfall" attack_action_types = list(/datum/action/innate/megafauna_attack/frost_orbs, diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm index 477483862b..b2c0807222 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm @@ -64,8 +64,9 @@ Difficulty: Medium guaranteed_butcher_results = list(/obj/item/stack/sheet/animalhide/ashdrake = 10) var/swooping = NONE var/swoop_cooldown = 0 - medal_type = BOSS_MEDAL_DRAKE - score_type = DRAKE_SCORE + achievement_type = /datum/award/achievement/boss/drake_kill + crusher_achievement_type = /datum/award/achievement/boss/drake_crusher + score_achievement_type = /datum/award/score/drake_score deathmessage = "collapses into a pile of bones, its flesh sloughing away." death_sound = 'sound/magic/demon_dies.ogg' var/datum/action/small_sprite/smallsprite = new/datum/action/small_sprite/drake() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index 006bef974d..32300dea18 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -61,8 +61,9 @@ Difficulty: Normal loot = list(/obj/item/hierophant_club) crusher_loot = list(/obj/item/hierophant_club) wander = FALSE - medal_type = BOSS_MEDAL_HIEROPHANT - score_type = HIEROPHANT_SCORE + achievement_type = /datum/award/achievement/boss/hierophant_kill + crusher_achievement_type = /datum/award/achievement/boss/hierophant_crusher + score_achievement_type = /datum/award/score/hierophant_score del_on_death = TRUE death_sound = 'sound/magic/repulse.ogg' diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm index 174883650d..07c1957da2 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm @@ -42,8 +42,9 @@ SHITCODE AHEAD. BE ADVISED. Also comment extravaganza retreat_distance = 5 minimum_distance = 5 ranged_cooldown_time = 10 - medal_type = BOSS_MEDAL_LEGION - score_type = LEGION_SCORE + achievement_type = /datum/award/achievement/boss/legion_kill + crusher_achievement_type = /datum/award/achievement/boss/legion_crusher + score_achievement_type = /datum/award/score/legion_score pixel_y = -16 pixel_x = -32 loot = list(/obj/item/stack/sheet/bone = 3) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm index 7009f13f36..e2d6602a88 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm @@ -28,24 +28,31 @@ layer = LARGE_MOB_LAYER //Looks weird with them slipping under mineral walls and cameras and shit otherwise flags_1 = PREVENT_CONTENTS_EXPLOSION_1 | HEAR_1 has_field_of_vision = FALSE //You are a frikkin boss - /// Crusher loot dropped when fauna killed with a crusher + /// Crusher loot dropped when the megafauna is killed with a crusher var/list/crusher_loot - var/medal_type - /// Score given to players when the fauna is killed - var/score_type = BOSS_SCORE - /// If the megafauna is actually killed (vs entering another phase) + /// Achievement given to surrounding players when the megafauna is killed + var/achievement_type + /// Crusher achievement given to players when megafauna is killed + var/crusher_achievement_type + /// Score given to players when megafauna is killed + var/score_achievement_type + /// If the megafauna was actually killed (not just dying, then transforming into another type) var/elimination = 0 /// Modifies attacks when at lower health var/anger_modifier = 0 /// Internal tracking GPS inside fauna var/obj/item/gps/internal - /// Next time fauna can use a melee attack + /// Next time the megafauna can use a melee attack var/recovery_time = 0 - - var/true_spawn = TRUE // if this is a megafauna that should grant achievements, or have a gps signal + /// If this is a megafauna that is real (has achievements, gps signal) + var/true_spawn = TRUE + /// Range the megafauna can move from their nest (if they have one var/nest_range = 10 - var/chosen_attack = 1 // chosen attack num + /// The chosen attack by the megafauna + var/chosen_attack = 1 + /// Attack actions, sets chosen_attack to the number in the action var/list/attack_action_types = list() + /// If there is a small sprite icon for players controlling the megafauna to use var/small_sprite_type /mob/living/simple_animal/hostile/megafauna/Initialize(mapload) @@ -73,23 +80,22 @@ return return ..() -/mob/living/simple_animal/hostile/megafauna/death(gibbed) +/mob/living/simple_animal/hostile/megafauna/death(gibbed, list/force_grant) if(health > 0) return - else - var/datum/status_effect/crusher_damage/C = has_status_effect(STATUS_EFFECT_CRUSHERDAMAGETRACKING) - var/crusher_kill = FALSE - if(C && crusher_loot && C.total_damage >= maxHealth * 0.6) - spawn_crusher_loot() - crusher_kill = TRUE - if(!(flags_1 & ADMIN_SPAWNED_1)) - var/tab = "megafauna_kills" - if(crusher_kill) - tab = "megafauna_kills_crusher" + var/datum/status_effect/crusher_damage/crusher_dmg = has_status_effect(STATUS_EFFECT_CRUSHERDAMAGETRACKING) + var/crusher_kill = FALSE + if(crusher_dmg && crusher_loot && crusher_dmg.total_damage >= maxHealth * 0.6) + spawn_crusher_loot() + crusher_kill = TRUE + if(true_spawn && !(flags_1 & ADMIN_SPAWNED_1)) + var/tab = "megafauna_kills" + if(crusher_kill) + tab = "megafauna_kills_crusher" + if(!elimination) //used so the achievment only occurs for the last legion to die. + grant_achievement(achievement_type, score_achievement_type, crusher_kill, force_grant) SSblackbox.record_feedback("tally", tab, 1, "[initial(name)]") - if(!elimination) //used so the achievment only occurs for the last legion to die. - grant_achievement(medal_type, score_type, crusher_kill) - ..() + return ..() /mob/living/simple_animal/hostile/megafauna/proc/spawn_crusher_loot() loot = crusher_loot @@ -143,26 +149,29 @@ if(EXPLODE_LIGHT) adjustBruteLoss(50) -/mob/living/simple_animal/hostile/megafauna/proc/SetRecoveryTime(buffer_time) +/// Sets the next time the megafauna can use a melee or ranged attack, in deciseconds +/mob/living/simple_animal/hostile/megafauna/proc/SetRecoveryTime(buffer_time, ranged_buffer_time) recovery_time = world.time + buffer_time - ranged_cooldown = max(ranged_cooldown, world.time + buffer_time) // CITADEL BANDAID FIX FOR MEGAFAUNA NOT RESPECTING RECOVERY TIME. + ranged_cooldown = world.time + buffer_time + if(ranged_buffer_time) + ranged_cooldown = world.time + ranged_buffer_time -/mob/living/simple_animal/hostile/megafauna/proc/grant_achievement(medaltype, scoretype, crusher_kill) - if(!medal_type || (flags_1 & ADMIN_SPAWNED_1)) //Don't award medals if the medal type isn't set +/// Grants medals and achievements to surrounding players +/mob/living/simple_animal/hostile/megafauna/proc/grant_achievement(medaltype, scoretype, crusher_kill, list/grant_achievement = list()) + if(!achievement_type || (flags_1 & ADMIN_SPAWNED_1) || !SSachievements.achievements_enabled) //Don't award medals if the medal type isn't set return FALSE - if(!SSmedals.hub_enabled) // This allows subtypes to carry on other special rewards not tied with medals. (such as bubblegum's arena shuttle) - return TRUE - - for(var/mob/living/L in view(7,src)) + if(!grant_achievement.len) + for(var/mob/living/L in view(7,src)) + grant_achievement += L + for(var/mob/living/L in grant_achievement) if(L.stat || !L.client) continue - var/client/C = L.client - SSmedals.UnlockMedal("Boss [BOSS_KILL_MEDAL]", C) - SSmedals.UnlockMedal("[medaltype] [BOSS_KILL_MEDAL]", C) + L.client.give_award(/datum/award/achievement/boss/boss_killer, L) + L.client.give_award(achievement_type, L) if(crusher_kill && istype(L.get_active_held_item(), /obj/item/kinetic_crusher)) - SSmedals.UnlockMedal("[medaltype] [BOSS_KILL_MEDAL_CRUSHER]", C) - SSmedals.SetScore(BOSS_SCORE, C, 1) - SSmedals.SetScore(score_type, C, 1) + L.client.give_award(crusher_achievement_type, L) + L.client.give_award(/datum/award/score/boss_score, L) //Score progression for bosses killed in general + L.client.give_award(score_achievement_type, L) //Score progression for specific boss killed return TRUE /datum/action/innate/megafauna_attack diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm index db6468d1b5..923a626b28 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm @@ -48,8 +48,9 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa health = 750 maxHealth = 750 //""""low-ish"""" HP because it's a passive boss, and the swarm itself is the real foe mob_biotypes = MOB_ROBOTIC - medal_type = BOSS_MEDAL_SWARMERS - score_type = SWARMER_BEACON_SCORE + achievement_type = /datum/award/achievement/boss/swarmer_beacon_kill + crusher_achievement_type = /datum/award/achievement/boss/swarmer_beacon_crusher + score_achievement_type = /datum/award/score/swarmer_beacon_score faction = list("mining", "boss", "swarmer") weather_immunities = list("lava","ash") stop_automated_movement = TRUE diff --git a/code/modules/surgery/surgery_step.dm b/code/modules/surgery/surgery_step.dm index 900d452547..7948db2dfd 100644 --- a/code/modules/surgery/surgery_step.dm +++ b/code/modules/surgery/surgery_step.dm @@ -81,13 +81,14 @@ surgery.status++ if(surgery.status > surgery.steps.len) surgery.complete() - surgery.step_in_progress = FALSE - return advance - else - surgery.step_in_progress = FALSE - if(repeatable) - return FALSE //This is how the repeatable surgery detects it shouldn't cycle - return TRUE //Stop the attack chain! - Except on repeatable steps, because otherwise we land in an infinite loop. + + if(target.stat == DEAD && was_sleeping && user.client) + user.client.give_award(/datum/award/achievement/misc/sandman, user) + + surgery.step_in_progress = FALSE + if(repeatable) + return FALSE //This is how the repeatable surgery detects it shouldn't cycle + return advance //Stop the attack chain! - Except on repeatable steps, because otherwise we land in an infinite loop. /datum/surgery_step/proc/preop(mob/user, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery) diff --git a/code/modules/vending/_vending.dm b/code/modules/vending/_vending.dm index a115300085..20133ee99b 100644 --- a/code/modules/vending/_vending.dm +++ b/code/modules/vending/_vending.dm @@ -559,6 +559,9 @@ GLOBAL_LIST_EMPTY(vending_products) if(crit_case) L.apply_damage(squish_damage, forced=TRUE) + if(was_alive && L.stat == DEAD && L.client) + L.client.give_award(/datum/award/achievement/misc/vendor_squish, L) // good job losing a fight with an inanimate object idiot + L.Paralyze(60) L.emote("scream") playsound(L, 'sound/effects/blobattack.ogg', 40, TRUE) diff --git a/icons/UI_Icons/Achievements/Boss/bbgum.png b/icons/UI_Icons/Achievements/Boss/bbgum.png new file mode 100644 index 0000000000000000000000000000000000000000..a0962fb1ced0f6df721a22c0fd8785e326a3e4a9 GIT binary patch literal 4864 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 b|JMHkW8YiG?!TZL00000NkvXXu0mjfpe$H2 literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Boss/drake.png b/icons/UI_Icons/Achievements/Boss/drake.png new file mode 100644 index 0000000000000000000000000000000000000000..19ecc5ae941ae630433e434b46595a80f53ce9b2 GIT binary patch literal 7847 zcmV;Y9$4XtP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02p*dSaefwW^{L9 za%BK;VQFr3E^cLXAT%y9E;jv63FrU-9vDePK~#8N-JE%JRo8XK^Jo5uV?1M$2H2lAW(<89}H{z{YL1?_X9HULqex;Q~0sqMfqhx8=n+t zb4*yD?*~Fp-Y>|IM}=Mi@Az+F$lx#aZ?ty@w%IrK_1pIk?6liQ_So%X!pVJh`|N>1 zaLl6 zUn1;vg8b||-*``)D2#M%=)8oaE!w2*p?(JYydk_V!iApvWg(Jdgad-}rr+PT-u`UY zHv8WGfnlD5(O~0-XG4YI-2BNC@37-#i$Pq74x+5FA_ej6GLV5h@tp@h&fz|L%_~ zbWGh1?QJ8A2E^f=om;$U0FF&zHpGI^lcR+Q3N`}+gC!pRF5))GI-_Q<>T z?Am3gsw?g=mhXrY6e#*Tf*r3cb8XyJun6D;H#kPP4ush!0_lNseHX|> z$OeI7D-$N!`ko%!SCBI#n1?TOH?cfA6XRSPtGc>u;>3H42H+SNrf~nIhG^)?yFnc> z1HMbgZ*SjVKRh(DR|SDlcvp2rm0dWr-(FH2dga9{cJu0GJFfFyjA+69G2XOi^G17F z^==4j8fUt;M&o53KRUSEKG!#tl3>HwFdOfN(32xU9TW)vOow0Cv2ny$6$XrY_0oB} ze)W>=>F=|Hy9P!SY<=9I2qOu$d;2!Ke(kcodFyq%aqf(}9@b|l#QBTw4N+_<2(xu0 zgq{oo4GTY^qd!tGy?t_jIH4$bgHNqnZ3A02+nN>2t+i#26U<)=B3iJZXdVdG+|=mW zdh?CfZC6kC(6ulQcR(}gANLPU2-q}g(Lzr~Lk%(vooA6(-F|-bV!e<&**=~+*{;-8 z+Ey9Z(bi`DJNm4)rq((ZEU@~zI#(_z41$4Z!SZr*t#PJ?VpX+eWo5gzdOO-}d10PQ zRTgPdpuVwUr)7{odeKi(Ei9&f!ee(&UQwrfg) zRhN`nO?8bm$ne<>GwrpHylmZz7CB+OXYsi&C(~x7BoB$!I(M#}Ja*I$?%U()(ne)* ziM1!ixwh$pzUVU+(E^_KHQ}BULFmaK)QFo6UCfGM7~)y;Cylo?aTBapIn}HQgMC;a_&wK1q%Ng^a6YOegoISIo+ZtxnTXSQhty|P#TYHvTx4fkxH^-`{Pqzz8 zI;}oC+nNgU?aH~c_WX%st}Jy{bT78^DRD!BWeW5`U-a1&@zgMGp^$BX*>fOF3y;u~ zKoy&WUy!?uHgy2ryLKjM7$#1%dU;mrm@$_4zyntQ*dy*bg6&O>x90RTyLjfbJ%9YD z6&DxTxgFbWo`zd(et}(4An#w%Ys==(x1yp#>ydX+mOA|s3&Cg$q<#3qmMAQgchM(( z$K+)KDMutvBe@-MMG$&Y6<8a(<<4l5QV%cIDFxsqy5qU>stn!z1c`+w)IR>`kdb)P zn$$$wxOT0r?(Ma!yH?r3J-cm5XQ#DiXW5ZCvutr&t1Z^|O-ov>slLHBtXX5rk`t^e zaq^HDd=kU59)HYA)jpnfN}jepF~&(iaLMhUmwgWn!nC>L1W;iX(r!r+*6>Jx+>MmU z?oLmYSCz|vGz8P#-$*=@F4gz8j0VK^4cnB^0P3$~!O)p@|2rY0*?oT{m;u!g!?JNw)T+q`y- zLrrCsrE3BxEGV#fHKjINF$}@x%$hYM*cN#gZPQ1U1mi}{lYkpFEa4KXmtk{6uzyF3 zXnlr#0V>3P*5Ix~NP}U35OTQjov(`m2_enPu72Gp(;v!?U5n8da9M&uSR+378dWn?9yKIv4^P z^}RuW3!GwEJ^^fxh_*LcpJ88AfCTlw34XE8iU@W^!+Jw;B>(cf{aKU!E?l7~|9uch) z(d4@bwzi|qDm4RMK7PP1Z0)vFE84BMYmuEfb=+zyDy*ci$erWb#`z7d?4qWT1$0)N#-x16k z&*vfu)~2@UqgXMlNTBZmd0M9WhZEd`7YkoOPHYH93xndKp%J*ebRUH*vA}~nYsqI-h|j^Ote4~vLFoK;^-d0*tV(Xg96~pGZpx?Ey z!>UTlY)wO%m1rpyCYWyn!7?6u%=JZ|E@2iQCZ7!PA^V48tg#F&B7(g=Ofa2bfjt=P zgU9d z!!b{Sk#R#9f*XW#&6ET26WnYSMwW93wk##V=2TbN8Lb)D^!6yTueYV`?N+CRm23AB zj37T!=YLp1*cq{hvNnb7OR)J=bnRJ))^Gc?MTGB3?i$Ch$mF8I}Aw(^PQnd zFm9KzeGBH@kwp4qK9?wa@0!(Q#ab4vS+>+AsilftYZfhZ*MenzegA#4`su5yvgsO@ z^hw`vfXnpAkR+Dz8Ef_*ST5akg6V*DqkGrEKam~{=+qBK;ul1jm1-m!$AI8FS40tP z`?4j&1mkn>w$1MI*y?9&o0dwmiVLk(!5-17ltRpHXt2_(>F(OjC5v6zB^~WUZTNQZ z8U(9U-}LGG&k`RvaqLBmd00x5h+t^+K?LJX827$HXoV2aA^Y&%5@4pps#A>W7f}e7 zXb5!a{9+MgcwodTtqc1Cf&mQof}P&A(^@p_)(YK1$J{x#>Y0@`zcAmO$WoWMW6J&P<@f&a0B56M+9q&i+xcZc$cFF!FYWrreu}Qh+Qj0jD0Td z;%XhQ(sHOncbnh~={5numeo|*mc@&P*{l2v z@i@n|^G6R4m5&x|?SfWoD=oIh^fcEeeZ!$dp#n~Dg9q6^oQY+={%F7}2ob^7A{g&y zxi@3N3P*#Ehy&y{#D#j1Ae~qwVv8dv(?S@+kWW{BgkU>XEVC_j)vi9kyTF+Z>+GTy z+)J|3-HtMcb$p;LmDY0}`@DT&&*e3`4`#rAwQcX!&tiZaWM^hw`v@O4SbUMwCo zFar_nA;NBFy-3vw%S4DEi|c1=s20iqFiU}drv!U``xf_n@|B~9>{wy8)k=VJW%un( z4K|>KHOm}?BORz!!n?M8zSftWR-&w*vL`mLx7RM8w}Ts>wOyJlz(omPI__yF7;8!T zh67x(H7s)kQjP+(N$`g|HjG-dBSV4zy9nlXR^5whVTA*rakfkRtiz6K+4ymmuIW5q zgt(iKx2N49RVw?6d=n^{+5`AJF6tJZZcv<{W) znXDl#G z;brbz;pE9ynVMp+T)tpe_U*Rrs&Xqxh$AN@N5pjr0xR0;icm@kE|4D29nM zn~m6jEu-=>GX6oq{t3a{m(YR%&?q~qM>PKDamOSEv4A_p0=rbhCqtsKk;mpC=b7Rw zG#v9~7=rN_Y|4WVYA8Q$%gc(aIyc9zojYTP)~|KLuwAnkxL%ZFM_XpO;|dv@Gj6=C zR;qDgSHBIcS!we$dGHL98$g?0F!}@Z4F|ZC32@6(8=QwTf?)%;j5fvaI>jUynLQn5 zN*IRY`Q*WEoMI8GMJT!YTxKGHAptno{L~XJ{YcZ0%#p`+{7^tu89Cxe{z(d5=w%_{6AY3ASF#uNhB`9qJbjaG`&= z!4a--<{GgMTNpL53O4E?$-WYcTH*p^IEF-t$dD8Q=khX=8Hpj8?S|ze?sMux4_Up8 z`q<0Y?Qq*%1^ZmfOP^|;#RYEo9Z-84Cq8AZYNJB&rts-;R;ADDXHVO@))rTmZR|5! zHrO7;FJc!dP5u*4C?q`Y`pDHWeZv7Ro);Y93TL?EVH}TYuPkav-ZSXaAg+5-3tHT| zh@mG^976*4=T4GoS7WG#93DuFDHC$#RoMbVsCeuX?sX>wt1d3KqNyn^;4>ROpPgn0 z(^IWkZIsL4g$Z%CQeJhSZ>uYlnUZR)GiNx#4#>k0ge@FAiYMQAa*;v+xfVPCoZtpW zxWXCkynetjHr+FhMFstPrWG{CaP-?I3@_pq9rB$|h+#mP?l42clj{@Mi{cpLFy`i! zi}o=EaE4-aN}`Kj4f39y^)+@WCta~eDT?CEk&=9uta6Pq(~?uHO>52bTQ=IBMGIWu zPu8%cZTg@uj@?Z}K+j2^ocCPw)pn!K@g1A)IV_{nin}JRG{i9BPS7~h{<9*JDZ#i? zkRymf3^CZxhisR?tW=_Fo@|-~%TOFERf_P@gZr#sVl?GuyY^?x16T%KD#^1II-a2+ z)URRKT~q0<<;TTar6vi6^KR8^miOVY#6&LyA%H&V8xC-R6WrhkS2$Pd7#pw!o7~6| zhmk}>n}}d8apmVIBQA|-gd}f!XjD4Yot80Ltd7&fJn>olmed4M#xR+!Xg9M0MzU zhQ7lEPH=-GT;U9N@BP?>ZFdtadhtEt_6`j&zR8G*5s)C5(^;+VR<*z`0N)uBQI*|8#aat_TCK1tDPV8Th#n2?2avJDzi~Y zV|2oBoFf9#dx8J1B$(V@+ zUNDA`yTc-idw*Q@-bk5buPJMv{kWD+%FPh$jBWsLBioSq-s|Eg4041koZ*fQ z?iqyIYmtE1NZn|`MtTUs8$0ghIbLCm7VL&PVDk+EO%c!HF+82SX91$1fyNbQir;< zabdwc*Cv9|J|4obLNGq511F9>PdLLJ8?XhNunimU5KLM`9<%Y1oqLnV2^JBJ-y6DL zg#`t>5fRLL51q6qu%6R7Vg=^_f-x+-VAMr0jA(yyP%O}0Sz$}n2D=DN@{+pDELVrv z#4u!=rbq1|O#S$$G;uuQt^>+ahq|;uTfSZTLKwiWL%d*c;@ITG^bW>=wV59*w%FY0B3gFg=Gs}2bk5shE6y5Ac{3q~xUae_P9 z@!Et*?mL1(s6(GVc7%xqI6k8FV_QL<70H_h6oU|BwgN9XTY&^Zusj_j7{_?t3|*%T zqEd&tCkA%d%J#Nl?T;iH+~7zVICttCf?*5au`OCK_nXT>@46AxC|WSQZ0LnLD!U=c zOcoK0X`7t^9*E-dyF%T}_xO>HfZ zP#F@Ap;;_&t}Z^#4Ohxihu1(6m*WMBMYJ8=mp_IF+~CM}Y?!0`4jZt=3x)kA7}pT@BY?yO9bAFAkiaZ2Dmlx!UtovS6J{~k+=}_gq>?gJatA% ziFM4ZcZ+De24qWwR0)-$^o6=pjpzxV}Du6=h~x zcgyTM`tzUgft%l0_B>o-H^M| zu#W-61wZUwl}J2IhD4oaplHE7!56n~w0R9P-C8g&DampYlPq1wQ=WR-R<+Hu{c{@a zX#L=GOD`D5?Cx@nGHH0%*s+!_k;~uoLId{_qY{E8d z>`IEapM3qVy;x{)@0Wss3i2Ci`@t7JX9Lw`3bunD)S}=>X9&hng#g14&w>cszQi%g zc=CL5jq;a{9kNYJyRA4o+p?#nSy6V5tzNjm_O#5hi&_%-ArS$IHg}u=D$M%whwofDXP??L zU^l0yy1US*ctIy#6fc;|w{!=&TixoMYeDiHBiNyeVtcNvzz&xbIImf)WR)S<`h)_$ zGIlRmupWY8Fl8u99qQ5sZMBsY-O*n(xJDU|pn%0X=D|sS<}`cj^hx`}vnLVjhiDL{ z%^fE^RG~@u#XFbJ*=toLwn;&LUSuZpcLyRMTCgzkoTDD=K$29}gawLW2@ibOQne^Y zu#xIvAiERn@soP6cTXF%HIipVgEAgL!8qQWnrPQG5&hwr6NnZ2PiH(-ao{VbkJ`Iu zPg&ozWLv8-kI`YC5JZq@u#6YXe`d&V7S20#Ge)WhxQ1bbf>H_Xg^ES6ASmy}I;@5M zt!H}d&#zzhLvbKX3y;u~L8yV7Uw-b0eg5EH+n%0k-AvD#J!2&asN=~pMDa3mED}<_ zhqdX;Q--qC@!sLvi>`Ao{PbIYZU6PBU$waz)9j1;cG@>yf5nS+GfaaZLQe*vMtJ}A zYwuh=Z(mfBd9)zYx|F+&5wLmA3ahjT&C;xkw; zi0EsGwKO%!PE;1!w_dzx-+bws7wi7~z!az<_Y>x?YK(v9*6Vhyw#;6hnqWWu#$OJ3 z+okhoZD8BrOK;I++3~1PNgn#{1tW*Mw0x-(V}U%kNpUc`J_~7HDQ6GAE-%k2lLz12 z@qz&arH^1;Ums4_E}gfZe*5e8<9Gkq{_WOF_OaGR`_ZT0uy0>HZXjX>y{#6&wdf36+vS?rcb z^g&tC!boSsr=S?+JrdWw)yW=~yy0>?V(lczK62^jYBMX+UYt{0s zoT=07nx_62)K;!m427wwu1(sek0mu#uFvQ;4oc)N=;r?5i*MV%-h9!%vU}(U3-90G zFnFi~hUeEl)3?FiIe*%|`TM_TeTx?jBk76uEbMTCt;)@|^Cylu<8mZcv2g1nFI$O{ z*BoX2P4X^=WON(89fl;&5{u*Gtx-wq)lfmk-rj-{SN$TWHdxN__w70BMceF`w9$O-i-M^uUZlJcvT_ji5zq8cl~ck z!yrRQMf@M%y1&)>6+s)nTYr2z5@aBg#BbqWE8qUW{{c0puCQJPo6!IO002ovPDHLk FV1nlaDp3Fc literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Boss/hierophant.png b/icons/UI_Icons/Achievements/Boss/hierophant.png new file mode 100644 index 0000000000000000000000000000000000000000..a8f2e6a45edc56d7130e9f28253e8c081699ba3c GIT binary patch literal 2345 zcmV+^3D)+BP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02p*dSaefwW^{L9 za%BK;VQFr3E^cLXAT%y9E;jv63FrU-2%1SmK~#8N?VL@E9Yqv|vz$d1S!9t#en`mH zY{Vca23fb#>gP#D1vgs7ksa3i`%B+Y3{5s=8IZXEYl1&d29xXTT9~3S0-9;4Zii z9)cu;`wZjXc+3sJb9nBI7kAgVfn9(M?ghue2KXDOl%_C_TYz!y^RC$iZeT2sk^SH` zz|!#u!L=*N=>4@6?lWwCMULUI-(NZ_=i0cy zWAGU`2@ZN`iG>^379{!r_z-M^G*f5aNNz8lNq$;cfP|GDDNTV$#>F@pcYdApnm0iw z;@rTdAd&mP8j#ZT$+_9&yG!Sid#g+1B_}rTuHqVtQu=sJUOP*}I*59^x`A~;qF9N~ zfj@!#$9cPr-f32nPQUP+;Qg%>{A0G3&e~GPG|hu34+%H04oJvRz$cj`@SBA*X?^5n zrUDdo`PG+&#?lDV@a^KfN+V3;Q7;d5a09D?1S|noxXD)wZzjL3UGfuO5tVi9*fHVa zD=)T_2AC$MjcLr%y5!}hDsEsIxG0+lY#vC_mx|m;Y#pqu<>h4|mNddNn%k6VZ*+^g zfs??6_ypYIg*VU7B@f%6Kvm-5C3NPY1gtCf+>9$K0zbm?7Ka4L{kHB=J<~%pM0&pOQXk^qOX5Ww*f*yflp1h9jhR`wBp-C@F+0cM05 zDiWq!k+3}6e0p)llqJB0sr>g^Vf^)2e&cgbY(XfkiLiXrFnosbMVFN!zk8;o?6?&s zBN8#XK^~#BP?#m(xbMLYpEjA+!LVj9wmtPC@k;|PUu`cSLJYE@o;_8H0G;lqdX64DgL z!MGTwcdeLk3PL9A6oqkqmduRt4vq;k$EF4|%cmPN&c>~WHW&0MXe;=>h0#qW>^g-> z`%9)AIS0oi6lSGaK^`hgKl>C$H<_?a3ghg)C_~U?gu+b~=A6qfA&RO~buodlkkOe=6)Dg~=E9 zz{pGzrXq;_UUXcq!hm`jgfJBqGU=FU3hVWjB+P6KG(PWIQ3-Cz-YR{<4BApw6x<(;YrA<>7FGnQYMUlpPI0pCnTqK&gc*|SA}&pL|w*^ zOPCp=zGcVsJNpr&3PToF^dkso?8xLfPGR&;?2^F>gNy8EHcqmWnLHvalg9OivMS6y zxkH93j4tSeZq$<%#>JWP2=z|xlA#JS&54tqu!CS*CII))5w{Fd7#x_%DEVU5Tv#TJ zD_Lc|p&Lm@3{jY@xlBvfP3tHW#!}XLLS0=3Cv1E|-Pls-F<}G;aM`ac&U7Q|fT0O9 z3scuaKVx4A4q_AIFP_kLQgBs!d}>Zz*wq!} zaZXEDv&C5oB9O^;Tq4(1gLbOI&|fs;T^pVQiyLS`B%J@s#h0=eGsXhbkZB4tO^7%P zObH^&moRhLZ)|DP686LL1(g+V3A2I-^F{Jf(jpE6^)3u)Hzh0nB6)=o11V`zr$u}a zPza}BQjnF!@Tjs-!J9gZdFkdg3<@s5+f(W^K0RYLIaCfsbs;N#(MSF?WFESG(T9Qy zMSqnH#c+)hMd{=^?(kX*-Mb_bM3+E*kA+MU)cOW8CL$+C~`DWV$=kd0CMK z31E*vx>2Of>#!(>P!!Sc-m#@14Z-WMn8xY84og8o^o46O%E6-F^o45_#FEyyWT9~# zyl{BF*4PP z^&Y1x*pkS7hCS`CCG|i?*c8zh-%5|6C5+?gdhu-$$RHmQPvEPSM|=JQ8dw&!84{_) P00000NkvXXu0mjf0ODMv literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Boss/legion.png b/icons/UI_Icons/Achievements/Boss/legion.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9470a8a9739bc427a9264efc63ca52fc84c49d GIT binary patch literal 7945 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 IM6N<$f}#+CIsgCw literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Boss/tendril.png b/icons/UI_Icons/Achievements/Boss/tendril.png new file mode 100644 index 0000000000000000000000000000000000000000..3b6b908d3ebb676b8dc2cd0a8d3d7c69f39c8b2b GIT binary patch literal 6594 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 AbN~PV literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Mafia/assistant.png b/icons/UI_Icons/Achievements/Mafia/assistant.png new file mode 100644 index 0000000000000000000000000000000000000000..0cfc93b166fbb925f5af4236f8d332914c6c7a8c GIT binary patch literal 2374 zcmV-M3Ay%(P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2-`_SK~#8N?VMd~ zR7DiWXJ)tGTS{A?Rj?Kcf)o-Wg!16a3WgVmMPD?*#Aq~{K!O5>2Ms0gA{0vki4O)} zY@#o!YiMao!@O z_OB)#H}i(r`7MXssEes$2JFXE9#_T~tK^!tmA+O|<6U~q3?-#k5_={@ohI0sw?1|E z88uN=RDso3d(u``lzeonscoCaxLa_RVN|iqv>cY5k;}5uvRMXQnRE#*)RpGc9~<|L zF->aBFa2z6e1xuHHu7YM4L|X+(eWYm3?wDmi+7mI;5;Z=5(YPPexXL%}4ME99=!b-9$|z z%Vc9zRU@7Jt4Ma&hycSNKA7YGmQJN8T>e z>iUV`5!awN6^Th=*1&$M_G}YUJ4u6hDZbAmO9Cj2Pje}Zb@zI2Qb##W{0iXUro|%LrJ$Q5zo`80l5uhQ|i<$$`nL%Ke*<7}d|L z83BWfkU64#LsqpD-S35hGFFuLf>9fvdeDED-R)`ByQ!45F0c4-CSCNIfnf%eT>FJ! zZQ$I&$`+NYK|K-FKhnc)KfJ~?nj{F?UtEgqZ+!0VGOEMUGXbkV?%71sPXl$qqI72# zTVAx5Wu@mBwTU5&ZLJTlDxZ$J=mT>6DtGL4HyPE_Dr{g1mXwY32RSbCr7LWJ8UsAWEWmQv@JJg!wnd- z&<&FTmMOFalNQ#p)YaB|UD~%sRY*5M0gp;7HVS+xSzwo(MIbSpvgCn*OpB%bj@l;3Fxt6>TP!DFzmr)FrV+)Z9-}y*APa+o>gTeJbDpvK z`So+YMKf0#9vfuW?_Z+vUyeB>FP-Sn23Z)km9bNb&A(KI1)=R=02buX9yE%T0v1+Z z?a3fyUh*QGYnN2Se5W+t^u#yHu5@06m*FG3D%`QpJ%I9ng{`b8`5G@Qum_*&uplr1 z3os!zpBcsf1H0AKwhW)C;B$p5lLfJdXd#n{$%tdkB)7xA%iB#k8cSWm@3Y z@AUduS{UNdZruA5z*^u|sQ4`eve~khgFB2n<`jr2#a+ArgC$vAb19PzSdkN}2jH4v zlC~zxWCK=l*AFpYaztl5cy%8D_{sPvt_LS8)C)lk2UV%T%0U$6{}FDSSt~BEABkJ&U%I( zkfX2d7D#kLA>PH=vKE#`5Lg9mAg)=mi58k!s?wG&j9*tfn^b6L?jT!ky@F4q(Am;uvLH5-qjy@)w;xAxW-kqL_Cj1=a3l8; zOo&Y&PAmXy`vLboT3psjS~nrzFx90P(E1uoh)tl|1%N?F`$Z-zk~5K$ld3}7!2m44 z#3)t@0%qh$*I=MCUMr=f&~}w3NGzVt8xH~oAxE0X4j_{i0kWwkv>9y&1F$fP1DXU3 z)2HC_Fz!>)%FH%{fmL$-Bw!E{%E=(xk@kyB)>P5MflfWunc5BpMsbiVf$8f@z6%~M z(teRvlI%hoJ07yf>qDGl5`P@STEkZ|F;TfBJ--P+`suUd9 zl1JJvGFg$FnTSF+gk>pM(MXavHSrh*jA_VZ%}TIi7%;%MKDerP!)PAEU`m}ZASBJ8 zY(3I`kq3_C%tR1e2h4_kZCs66=!Q`}=)+VZ9wh^(6n>UkBgXP5nTfIwE1H2+GCh;0 z=!aB9al9UfMRwH=4}MOM!!m_;o5+!tTpSXd%|kbhmjlwtxH+#I+zQTCC3Gn9R3 z!^4+~PKmak<5u>b%707*qoM6N<$f@0umegFUf literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Mafia/changeling.png b/icons/UI_Icons/Achievements/Mafia/changeling.png new file mode 100644 index 0000000000000000000000000000000000000000..9e44c902633876bda1357d73e9b0debd89e2a328 GIT binary patch literal 2363 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 literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Mafia/clown.png b/icons/UI_Icons/Achievements/Mafia/clown.png new file mode 100644 index 0000000000000000000000000000000000000000..4f2d91947159fa5b74fc64b95911ebaba1af6982 GIT binary patch literal 2363 zcmV-B3B>k^P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2+&DHK~#8N?VL|c zR7V`gXJ(iGizujIs}@D8Et;6B#X~P!ZF!k;q^wOqHG_h414{g-wrHaKwlOCF$ z%ApquP3=kSUNoV#Hm3fmRZvv4s0aef0 z^P73|8_rTd%Rx_mUlDsl@W5)$MHQ2BH5IR*%PBd_=Zsh2bCSs}F4-{M`sr$8oVQ77 z{9{PRjgmoje#>Du>XJ&*1pDE%$CWq2YPn&&O>b+d@h-C_Mx^vg(w+&?Xb5&@$EWUR zx+b9{RIsKxPtK~Er61jF?bvEC?iQTo=_;0=Q^X4MidjKUA!4 zPldsaBlPVbQ1^18OwyY<`EyxGVVSxh&`f}U&wSmi&-aw|4?m-CCwn8P8~dpnH#h>~ zOA@YA6~>Xc3f5TfnJWTvFa5{&D{CTanO8X96se$$IUPCrlkp{B_(?nxj;39qhGZyT}I?q-ijZ@^rEAf5~SrUYTe@Z3rbq{)PQF3#9BYxY+&f zKKB{3AU0+sSo867UywB&;+#1O^X8CH%So_#x^{NZKh&#)^NsYIg9DR?YWHtGs;j3> zOoAaq$R9GkA*@`?{*>7vsX3?rcA#?K_y z2F@Lghn=P!GHP_!={4kRZ|i;8JXR=W}dFO`{S!BrlT(UhyuUb?R(v=x_U~Y7EFmHWefd6k&EJ~nWXl%3LVrW(+Vq6xr0m|)TdaHF zp|Ug~`MPMj=aj96gNM(8Sg@>25DTCUOUqWW;(}7TXXS>FXAn>7>WEC)ay=SufX_lV z%mlG4&=w3@n9EXETkmxl-|DJ}Oxk+HS@f+tF&e_br>oDJ42N)%Xlt0DSF}~Nc@iS} zFhuZN%4Yf!$4U|_T|$RgLCA5d>n}LBfaJV3(u^Rig`qEFx`PNFL20Io1xm=5p{;1M zVDe-ne->6SOr+^ID>2=f?Gb!DL0i#gqDpBHh6!B~OEivv?4a9{l@;q|-P|zIVD5RV z>oS&ydZd$Ajb9p?U}0F#qL&h^I%Jzz0BuIw1!twgZ|u>iHw(;CQ%&v6)hA6{Nc2%~0pSvIBK?_?|v-E2`u)rF8wu=Rz0knW7 zq|Il#_ZN07xi6qENa}S?~=#0&fnEuj)1}664-Kt z!fXyyr=l>|+-Rh>XEoRTT2J?T@DR3Q?n|m78CaIJk$x2BUXpQ>HDX-(Mfw5Bw6v~g zch$pS;-sLysVof3v^L=hYC~FKl*GaEHEQPJun8 zxJ$WU@KLznvqCScJcU@=Kr0Gj%?-HLHc6X_SlU1wx z!4`K-n_!$P!LY&1COkoHNGslSyk!^X;#8@tLTzPAHtDchap&=M)-(8sW=6(tfkY=1 z;#r(cc#x_Cpw*ZK#5IdIh0dF72>7q7ovm=AgT(`UzQERNdeeeRq&i%eD|XCfyjTS42Q0knW7x>zwrFr6b^ z4@ajTD`h2UyGj!zEym}Kj}Z(YN1BxFMf!eF@97Nc%-5YbJ5$$=^yW zv=Ju_=;DY>DcF0v++Jbutw{4ljx^GKGexxf)E0ygXdBuX&9MwoKZglTl1JJvGFg$F znU0_v!m^ZDiAa*S2t9@YJ`I_y>2?eQ9DMtut7bP$VyFyX#{2Kk@kx`a3p7@ z0|*^38U~C}<+IQY6ME2xMLHfOgP;^~mYO3b^C+26?8Ay;Kqb>Nd78dR?I@nD$6=9O z-9-eS(&Ml!@N5$~(vpkAg0ng3rnBY1HLN;dB0XykTnoiM(l18rLnC9Psm@cps%B~R z&DM^CG@`yqMZu$05_BwSTl_4CWkTifN7GnSa002ovPDHLkV1n;VYpMVM literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Mafia/detective.png b/icons/UI_Icons/Achievements/Mafia/detective.png new file mode 100644 index 0000000000000000000000000000000000000000..edeba8c402d190d174f4eec3f95f9c839cce7721 GIT binary patch literal 2357 zcmV-53Ci|~P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2+B!BK~#8N?VMd~ zR7DiWXJ)tGTlxjGDqx`?Xdxj&EDye{V0?jC^hFa)OpHboNKnA=paB9eLa`)}_+WUF zL|?Eq;uGM$KvIktA`}r?XrYjnmVR!xE#1B2`OlrZcH7-{X}jO|pWN)-ncF4v(ufTJX$xbfWDBb$$YGItWNNN0Q zNXM6FKnGsbGUVZ23OYpL-rvu2i((kqEQ6XL8P*tvH zm@1~g8tOb*t812ha=WQ*tHHQiaF(O1SZ-DU%g-rfd0F`^hpt?@1Q+Vca+;5g`^T6e z4Hl3AHa0#&myeA+9cI3#UN$;Dte%0SWC6~l=cSs;)2)~9|F&(vdq`K!yJ7FM>4;%13VS}Un^y_4ABc(dr0r53~ z>rjRBsCfl;w%$`L0&)-ikMC2~MAlN0ztDt~SH_%qDnh(WE=*AEG?y(+kr)6~m zMDU1fNSvCFNn*~xey;Ou71B6KgLoyrFCa?-D2z{u6vn!TeRrwL_Lv1ywi#UPdS|cu zoLMkC%?ViJv5Q}kH67%fIr4KB5U3RdtVGu)hXzOb6gc15pgB1(d9Zfh<|DfLnKdh5 za1nAxjPJ;*cA)z$Ev;hZMN4&Ua;j_K0ejHfYIaj8&$zteY)*JXUKt0HRJ*2B-DU)_P(5CeRwj#<;mA)EwlEfe&KwyHKyqljY| zJa{f;BmIbD6~Rgs*I~AT%W z-=ayBd}BlG=A)}L{>w3kWoO;7!!?0Ux)co}}OtFrBT-Ge9(TG;BEW#8h31@_=G9To%zU;!q? z<_lf?Kd{?PZ7cAZ2|ib}vRDwC8g)Zbz_7r^dqeD=;_Z$tQ(yrmU;{=dQ^4>>8Uq~O z8^-EYSSkuEzyxf-2xSVGetmCu$1t*(Sd2KA% zj_ORx&o%>%RCrcn-S72uzZ)-MD~exK74eW|S*6wLV~T#P3ALp6ub9vGHmi@gT3Tm+ zyLuT+Iw7cUDhtClt*!71vLRNOB@y|TG=n`X3l}VE;$@X5LOEG^`i`vxFcrUrKsH<9 z<=_r|$D9H&rMRq^3H7je(vMy^ZOSA++yK#faJzL>r zu>mWg^qbYD3hgW$;tK^ruj$JLR3dlBR;YqZq&yE+rl&8&S$rVT z$yTU>Of(|}4Ecu0l0a4%5}jP^e9;}a=#woF+ph|SdKot8`O$B~@bM}wTb5H}ax$h`y;ViSxL3j*7=-~EslmuDoc zn~-mq=~4`6eGMkWCfMzQz#ydkB9j%#naIh>RH5x)02W}Pik<={ljzec$J4P<@vNa_%@O-Nu=h{ b%8dO7G8w0S3A+C)00000NkvXXu0mjf_zGv& literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Mafia/fugitive.png b/icons/UI_Icons/Achievements/Mafia/fugitive.png new file mode 100644 index 0000000000000000000000000000000000000000..86beb21b1bd796e0f720046766da1651ddeca081 GIT binary patch literal 2387 zcmV-Z39R;sP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2+0EqAxyR!iz=|NKnA=prHgF6pD~Q;)5|U zu@An~)`(Al`+}wtF@&21TIdZd1$y1y=Guv(F-*0BV zne%fJ@dA8KGTFf;8>U+yU5$+MMk$S( zhIBM!53mbc4!KbmRiY-?Pp3Vuv=LUp4dZQkTS1L?nKd&+O0OjCnGiLGU}xU@)ZL?N zB1%LBtF7{+l#~~KbgRC3tHHQiaF(X4Sb9np%S>CqGEy>G8eQph2`KkE( zG?-ud*~sV+U4v}s$p9OC;$_351L`wCO6KQWdR{6o`o8Ip*59@raQEw~IVU25{c_Y( zU~uCwefv)8UQU!rdb2P+hh=BxsS5(l1_;hVDDP#3$?d7T+FNswrNb1AmH^SQf2SBJ&41gkyn*+kP%9d*I{RA&ZT zmA{r{q-N>b_yA*DQ|C41(@__FK#pI<_Py?UT|FgX3#P=9@*Mp@mW%wUm1J2dj#OP6 zU-@r zVIqiSfwo}M!djNP+Ip|c_)b@a0cZd% zpb2U7nJ#`R*sc2Jm3U1CpDSD}El8Vkbwen@u)xN1L+qa7>5h#Fw16hi1{$GE35F-q z7~t^SFj9}g;t^;8O`r`lLYWdwKfbrSV-PG&ER8tUOkS7qkd{KOGMXJ{`$Rg?7#C)@+iY2_}sAr2q$g_xbC|kBse=K_BTnxEk5|o~eW=KFZx!!(J#d7!!{+2+< z5ek#pQJsp+R5Q>>g=f`O{eFV(_uwIHLC%Y+A{qoNtF**?)wp&x=sOed59so*#yVI9r}(rd2QAC{}5C9>hjkEk$=&^8pB*)O>Zpn9x#6{nqwG3sIIjAiD11B za$?N@+-QmwZ&=zuE4v>~69F^B4b09aIF>eMmo($xRkZSaV9kmXuZe^zj|P@D&Wq}ZPO`~9(E5Q(8W)qx%Hl!7=J$7yv=i<~N zSAhy;iZ=1ETXDDZ2J0GlKr16-w?Lv33h^w?COAmd0nlpP2I87!o5rr2YzT~B*V^k< zXy<}{zE~jin%PwK4h{Y zKsMPxo6&Y?04;QJfJuU3`V?Fq#(gqcnbl@!5G=WVl3)M{Stk!nxqDZ4?EH_kHrRpqg3{++OSlL{tQQ;Z|D(d0$h@j6_HX_`R|O@c zg;@+|h)<|bU&1ym(teT2nn>Jz_^%QRZNy0fx;P|L3ikdkw^tZ^E7E+CBaO7*L=mlx z+5#5>Z9^NwIhG;p4fU}}@<{tdCM%LN;}LX2Se6ni5=rtFV~=6Ln1)Q&csqsx1ANni zYi2i$VyFyX$EEMk@kx`a3p8O18^NM8~Tk=HD;k3M)aT$i+DUr22LsbEVV|A z=20?Zu@5Vn0VjDxx@6kHaFnYJmqoug76o;Mpc}q$L-J1ZNA;O=IQ2HS9WI zAssUZu8qY$(r<>?hepnj+A7b2lJdf`TlLKcX-0jMiULQgMCe%3#-=}7_mWT%3q^{k zU}J$RVjX-dIp0pdcvc>-^3bI`n@|Veh60mBd>*aL*#GpIs@yb6Iwk-B002ovPDHLk FV1ldEWMlvU literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Mafia/hated.png b/icons/UI_Icons/Achievements/Mafia/hated.png new file mode 100644 index 0000000000000000000000000000000000000000..00ea735200c37f768f9b9bd0596ceda843752cdf GIT binary patch literal 2467 zcmV;U30(GxP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2{=haK~#8N?VMd~ zR7DiW&&+PW3$-m$kgr0)qGEz#EDye{kZ26VR$qL8#1}ymP1K6u%{ z!51t>{7A&?3nVp)5=B60p+LdXmX?0EboY+uKlk2syWQ>Q?!9++`%iB6-kI%^`Eh3E z%-nNEkPAA#JDA^7M2|@kUMqyGBBj=H@oK*ON>IKau?XK&q}qg{VZIIV)kH!xDP{a) zDBt;#0Xnhy-2mzwm1DrZIuI<*8=+cZ7*FtHEjPZ{teK&d>Q~I3q sI`-^qf!kJ1 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 literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Mafia/lawyer.png b/icons/UI_Icons/Achievements/Mafia/lawyer.png new file mode 100644 index 0000000000000000000000000000000000000000..1c9bba203647ee9d174d3c16d1a69fb9f1a85bc8 GIT binary patch literal 2362 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 literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Mafia/md.png b/icons/UI_Icons/Achievements/Mafia/md.png new file mode 100644 index 0000000000000000000000000000000000000000..32bc972a4aec4ab56ae0b9d88ac70600b8a82fd7 GIT binary patch literal 2352 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 UIGL*9Gynhq07*qoM6N<$f^2VJ1^@s6 literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Mafia/obsessed.png b/icons/UI_Icons/Achievements/Mafia/obsessed.png new file mode 100644 index 0000000000000000000000000000000000000000..b92ba5b582ff709e3890282bca7e28989e380356 GIT binary patch literal 2391 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�O(y zVjp~|tr4Fn?h7QQZ*=#J-+#`WwWYglX}kCHPfqsi%;}Q(@y*OP zbG~mli-H=Dd(sE9*xQ2pH*hXWnUou-cpY6%$yqvQya>-pCOf%g<8cMs^A zuqv#;>Z?5|WfjGr+-qvxX)x{*!G*d~oaSSbz6oYX zgZZVOO-zo_HOj`Gjj+*YUN$~CqMm`IWPZ-2=e3HG?^~|6|F-L>dstU3xxxbMm(!ji zgBvI5*Y{EPa-vMqmz?xvEH|@2T>!Kg0pP%BFB=?v%7(@V=-0{KNYsse)QxK$e(@E7 z>rjPDs3iqfSL0bG{PGa}j~`LiMAnj*x!i=5QNWyz6#eAV%5V4x9)Y8)Z?uP~8DyDk zf~snylYhOG-E~QTVGtkA@_)*OsG_gAu%-f6A$uin4NG;Vn-4`u=*ExxAF^I=r)716 zMDUbrSX@|&Nn+8!ey;ZH6w)|PgLnzP=a3}<6vn4$3S-^l{s+`$d(8qVTMaG_d~n!( z(JWY)76q)~%;jTbO;wyTM`l_Mfm%zz@^o!%XlSfofg7C|GA9Qnt16FdKc%Z*SPKFM z7a@Jj_?E0{FS_6A{8Cn!yIR-Arn&|nu}6I^W;d0x*5wr+FQkjUFfh!3k{iDgtgW0o zSn0}gHK@mf2FLo?gYMhRph<$DUE@;hdjCszr>>rtUI>>GH3oQ$S%3x0N(5LC+E84uo@Hm`(>*IRguDoUQdfu6oGsV0;Z}@U=!S^^ z%M#jxNegRP>S|lOF5^306;iXd9)1@6>R!x-7~tb|%$iLO;Us7qnV?U!Rke8*g&f1+ z!E-4a=tmsu30Azg4ht1rj{BW|V{r3J&MQN$2!dJ|`ZAU~@ZjN;CORxAm&P)*6>Szw zo(nae1qFtMH2q}>mOHWT!7ruIR(j4p{S8i!xD(rxbgqRlb4HVn70_PpPD z1KUG=(#cDo-x``>Vc5^2k6i3JBpWOUZARM#XZgZs9Mr`DO|vjKsD3V6Ip=9_yu2mq zTQt$q=)^F)^W-{>|8l}1dC63VcAAA@TNyj0*!)XYSPsov!gU>ZzSuBW6g}NarU|3+|y&-l_@pi|SDX;(&umK~KDPVXbjR6kt z4a4;+EFJ|GU;;K^gfaz8zrMG-V-Q(PEJhq_Ca=rrp{0DJHm$AT3a}wom?aVU7q!72mW2xzG}~o87Y(JQWavA#4!~6W76RF91($<6 z^c`~w#FXMLTEOPAAgkPZE*i4ffE78hW&o}gCTVM?EH+>jGyO1>1x3@cf=M8>8#jpD zvlUzx8?X{O4qinoM?=;aWnG~(SA95Tu>mV|NhA$K4Olz~VeeW#0Bgn_7MZc2sn!-P zi+WxNao1`VjB^DH0cN&>E5L?W@%l53`#Bfq^Ib(Mlqu4Rhuw;Y-FH~uNEfY)i~|CR zPAJ5?I9tJmR2>Mcrfnc@T(xcLy2%y^<}_p5g?mvLYvWcFaQf(9MBA4m_7xUCvl&Q zR%W#s41y)s&j1D?p_~k|4Qao~WK9-z4|SNS&eV1=(8bdf6qvcbD$ugFY<9<54njO5tazHDV->l9?*|u%ZP>CDSu`fqqCu6vygu zSY%gi@Zh6*9F`@#+eD7E~PBRM|)REl~EMkqcCPwI{o* zqPYBCQ|ob>QQx7Wz|ksUI+nD#fqZ@!Xy!&M=LY-A95SB)F)Jbt^fc407*qoM6N<$f`bfCTL1t6 literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Mafia/traitor.png b/icons/UI_Icons/Achievements/Mafia/traitor.png new file mode 100644 index 0000000000000000000000000000000000000000..28c8c5219172dd0e7ca648e07228c27f6fd4d5e3 GIT binary patch literal 2330 zcmV+#3FY>QP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D2(L**K~#8N?VMX| zR7DuaXJ)te?Hy=UsD*;977`+a^5Dw~#utc1UwpvC7mX&6pn%~)LkT=66iWh$55~mA zKKN2wBR)~w7fdPD!{#66&E zBC3c2tFQJXl~xpec(KY+`bZu2DAjbcBsQ^|JBF5%n2JO6KQWdS0q1{;uWs_FwlNa}VpPC09g%{e0R} zXmH~s{rdaVy__hM^d>7chh?Yds|$b@BLEy2?PY_bPuS4-0R1}I8;QEHkGgTK!!N!d za2={}3ALoa>S{bW!Y>cgfBdMjCbE{?^i?LLw0!1tBWoT%uUx6E)7&0dZCaWrs?mDHb=dA?+ zgNu+lW_&|dwIAJYU0w+*$X=&wSEjlKAF_viEoL{BvexAlA1tJc&KnqJK*^0?2-bGa z9js(cxf;}CL4#v`?0)wxX3!)-(Ej97?0xGqcc-qNlja4i{;X#wO+O9P1q+g$X>4P` z7M7Nrp=)D97~5L9Zz`XTx@Z?UewF(UyPI_NteP`01xw0C`a*__{HYCOSxJs$T^n2S z`6t=^p4)7gERH(p2=$iTbA>i1V5TD~ufwu(GuE*6t6!$SA#`nQiq@p8{mDJnJN#H# znvi_!T)OA1t;PV4F$=I@S@8f1LK}+mH?hpLJi2EEhL9KGPwMKhnziM6Hr$Re3*9gt zU|B+2Flk{eOI>Z7*JXUGtHNr=*2B-DU)_({5CeRyj#)FwA)EwlGZXZRwyHMIps-^Y zJa{f;1O13&6TylV*I}W8%W<#sZwzjJ$$4eC6+uV~Ltn;n2Od0}(s+jjWz$%OwxZ2~ z$#dbxvyi~Bkfv`IW4W`^J@};*+KM)lN)&?-n${(;MdR?xR=UmJRJe7;tqmhBtUd2_ zUdQ%OpLFt)XSar?Ss3=S=p`Gw4v7W}LYvWc!C9X08HaRnKtUD;2i4DIE9X4rmFKrD z`xeb|X>?+k-F|$H#(z2Cki2-RLmOma*jC0)DK`HS6&8fHg8^8OLwj5oD+Mg9zS@&Q z$h_o5IJc~=i26?HO4C!{IJ?pD2fPd)*;W3&Bkm!T2P~|#qUdWpu)rRCqQio~04%_S z*nFys{|9!rsdWQh6T#=2t1K48rb6A26fi8X@!Sx*r+B(!%M@6E3D|%U$`mj>k;VXr z=Z2Ab6c&pD3orp2FhZFErXSzi-7$nLCKe-(HIvt6^w3hsRZg?xVxLIIo5m+c*)5ta zX}a{fyka%4JMG!+5b_dY9m?14&>xGQa;}EmFbS!F9cD;CG8GvhrN!pq&iw#)W{cxTV2<^rVBKK^Cmc<6FgpPw((aN?$%VGmo z=#oepuz}4WguQF|0IV5zSY+A?ErhshH4DbM0)_xHTcH(TL#%lHnZ^U0i}QJ|LKVss zZNc^rjJ&$lb9OtN;@!FM*Zm=?hVZ7bH5_3RZxLW~6{2-w;_6$O=QElPyzV0VZGr zMkrIj5YmHuLu5%HE6kQDumBUVvBI9rE=elgx&8)TkwRz7l*NMBOpo4azS4FU$(j8$ z$k_{VbHR<=OE4iefjF@Mu)W9Jk7#jOD{0+?e8WVSVnFL_Fd;U9ZWjOsA?+8LtVqs8 zPEMio+dt~ zK79?_v`G6!CTl$DLC-%57TSoD26S;)%?j+@18%P{_#UMBB1al&zwrX?A+-fA1loo+ z&gEE!Id7;>O_E32FEUw?oEeKkH-u#=SdmDQw>0$_28?OQWR10B7%;%Mblo(&VI+@X zu%u2H5Rzt4whd{&$OA`mW-JJ<17<_NF{#EZbi;@q^kFF$kCK5?3O`G&5uePMRwH&4}MvX!?J{Do5+!tT$~V`Wucp{lmpkW>wtyyiaBs? zs_Y~E7AX7B$OWpt+LKvYQB;1nsr5L`sIOB|;AoWy9ZTBW@_YMX0u`}Pq=*8WD!3xn z!MBq0ee{PH!^4+|lU7y*{@H~;_u07*qoM6N<$g0^Bu A*Z=?k literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Misc/ascension.png b/icons/UI_Icons/Achievements/Misc/ascension.png new file mode 100644 index 0000000000000000000000000000000000000000..ca7e875bd38718d1328bf74d0011a00f3bd4d27b GIT binary patch literal 3110 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 literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Misc/clownthanks.png b/icons/UI_Icons/Achievements/Misc/clownthanks.png new file mode 100644 index 0000000000000000000000000000000000000000..214a4e0a74d0893c8c369498205dcefd74bc6050 GIT binary patch literal 3441 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 Tq751}00000NkvXXu0mjf!f~@Q literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Misc/featofstrength.png b/icons/UI_Icons/Achievements/Misc/featofstrength.png new file mode 100644 index 0000000000000000000000000000000000000000..5cfc5240bd1742b472723c95d67dd8efceaba293 GIT binary patch literal 2969 zcmV;K3ug3*P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!T4&ydO)FX;?T1DXkq>R+ zG?ALBRBhE%C8|^@O5!+GRGe;`hSpI^aY=v>Y%m0#_Y`6 z)PLzRvvX$`?vHcMoSC`jD$0R&#^VJ=bom}-t5wQUWm?lJK3vW%sz?`*QVZoot=lxI zE4-KB)~pmYYeyf`5&dB=mA><2bv61zRcOFoi2t#8fbwe`M}3|@*6`qq-JXewA@}f~ z4VFbOTjbP#ulmtTURR(B6j*J%F0X1y#WxyTk8dEQs}-qyuZ!}EVw7J}NO^@ZZUxd5 zj~*?WpC{)e28KvG4r$w_q5c6H>bpXNz5O)U(?>(RHPS@3(iE>+R`Hwtr%%4|!|I2+ zy{@b)5MWQo>lZ4=-pxP0ncXWY2l+$6teI3ex0Kr~0dzeAz`hHe)YoyDu3YL8Y9g%@ z?8bX-EBXY3JL4VAnz9P)x%exyY;A4hfBOzt6UQeiylJ*TiZP@}p35%VauPO!yCm@X z+Ap$IYUZa0xa+y7a(#JbKCXV`6BjL)7TXjF;U)aSz63L%gM6*{crr7M6Uhm{ke`l9~8|dnliv$0$ zYsdYYp7Oe{SrY<=6rtZK_yt?lm+=d8%5J67Tiw)Yvg-23ZFI5aV{ucqw!J-8$=4_1 zqSp+}y#hL~GpsvR#G?7D%O$8!1@&LNOrIQXk&6UF`4aRZ>{LP)2i+3?5fRMUe$-R!*7JVWL9H(RSz~q#L&4D`%!Vg0vO}UzXb)&ft-h z-srHP*ql;Hz%gdh>iJCZEG;nHNV8=v!|l#w&)~Buj1^;Mm4SgAOwlNpbRW?~T6wp0 zVdbYL-MwMZ!o6o((=kpp^u?%h%2FQUbpU{r5F!!;t%E!aMT*2D2v>A3G34{hFb<`KY!^gec>ywCczqbVMtgYXfYdM#1de5BaHxu_l7~e3d^Je zM+T(-$h|fiQ4@x_kB}fwg|^JPsG5 z@@NtIp}RhNiMBp`FF9}jJv1vbObjE-yJr%5cq1LA0Be6|oxpaFxW& zzey3l(zzro>Yw|T=EN0^@?yzpZRG$ai5Dia@CZjg^^} zPX2zhu&paq7KeU$nuwWuxex#t2Lp?1eTI9yqgD`g7$zkFb` zx0f4X)*=K}77Cs40q^2+rFXYKFWXGOi~ued@KCA)ft7`-FutkmLG6yuSFr^d4XCoRGV1Sl zFXskoI`<3GB@GK^GkW&UJEu-O%aJXCAQ$G1xPbxJLkt5b2;gEd_gQ*VQ&SSGfg7Y{ zL12K|OE6(JN%t7>#O|55>VC^!vAOu>n`qvu<=)Zk%RutGDF8IRe^B=Q&??ug9c4v0 zH%QBZ+CMx=J^#IctK*A1?%(t^@9;=qKaT%taYPR_V?y06cikR%9g#nN!HS^nzAga; zXeEU+Nte$L{jm-*`?K$Tz_9dyt@6tKrO+c@(=`0K=PwP$rcHCBTA( zEq-vlH2Y91wY9a;cmMVWns`wxZZ?H6b~K-s ztR7>}RNg%1i$Je9Yc~p)ZvM<|S-oodp&1z~GBTEA&O?7{tjmTF;LrX0C^adcr*KI)If1+@qN__Pcpc{u1VrMf0SR^+qT(?Kpl~ z0zI3+xJ#JFJ!*fEJj#Ai$%@!E6$LkhWg*{U%gTO{ypewy2277aCF@jMh5?}kF$-=8 z%L;561|zs93d;Gb>!kd5b(s`rP6f$AA4g6|fJe-7RAfV-3w;<(#ie8rtB_~GtZ+39 zZ7CVQrWY%kfKW`&{|LT~P)qT7vMz_kc6EwJ`&dqw!!p8zf2k9<=t)INR?e6#3$Ed* z18$@z&4O!wO)u#;LAgt2O)naG&8m&pl~gUMSl!rqd?#N~@8d&B{|snZ(&qiAPi|vS zflU+%6qsK~Ma;stO6f=Wug*Nrd|cAKoF44M8|fQn;oD4-i^P;%TAAp7rGXR8s<@qj P00000NkvXXu0mjfqwSqf literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Misc/fleshascend.png b/icons/UI_Icons/Achievements/Misc/fleshascend.png new file mode 100644 index 0000000000000000000000000000000000000000..109085f8eac301e1abd29e6ffde2227fd14395c7 GIT binary patch literal 8138 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 literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Misc/frenchingthebubble.png b/icons/UI_Icons/Achievements/Misc/frenchingthebubble.png new file mode 100644 index 0000000000000000000000000000000000000000..d94c3e9cc45d8b1db8016ed7ce8469dd2e7233c2 GIT binary patch literal 4864 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 literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Misc/jackpot.png b/icons/UI_Icons/Achievements/Misc/jackpot.png new file mode 100644 index 0000000000000000000000000000000000000000..e171c5cb7c99a975c1d8f6736020e75eeb836c2b GIT binary patch literal 3037 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 literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Misc/longshift.png b/icons/UI_Icons/Achievements/Misc/longshift.png new file mode 100644 index 0000000000000000000000000000000000000000..5f511e21fd50a48af3b7f0158a2af573e0ba1de9 GIT binary patch literal 2436 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 Hu0mjf4)7YD literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Misc/rule8.png b/icons/UI_Icons/Achievements/Misc/rule8.png new file mode 100644 index 0000000000000000000000000000000000000000..2bc518d5ab5d450cfa953aef7c75f9f9a402137c GIT binary patch literal 2924 zcmV-y3zPJTP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!T0OoQ6A znDS7nJXC#Y{SZY(#Dw;xAzLW~xoWARCczZjVDlB*V1vJH?A_`4&z;!}>;1aBYkPOa zPa4nMd)N2w{P)b>eee&WYRh>QL_v6@-vSflC54>4EOGSG_A@Mcd+{=|OcaNk7i#)-9 z-*Fr|;ZW%Gi~i=j`p}}XsDiZxP8Bvi-1tIgU-xbj(l1C9=|falRzgJ;rBqm2!l_s# z!L>&!C@NH+nV+2_848gM25D}3mgc5r=-%Ws-J6)AInE7|C@3Tan(7+=`2N)^e?8#e zHLee3oJA4rH-Xb@MJV_xpZs}lUIl6=ZxmNoQt7HHPSr}Fbbx?Ux9`x@@F>mP9aE|) zq@3qw+#YlaUSZ*`>%AExqkcoR%KX4q;%9~XI!z4RHdZ&v`QdS=;&>)*5@`$87U#1tJ>pEpCqt^y#r1zR?rfhBP6HdkV zQ)!}}1#|C!p|@GA$A!fb$}4=05u!Hxzx zo(M_t7XMRiK}iwSezB2Ous@Ycpl=)cs7Lv9Y;1#$kbC$0_jKyR2}xQoh{eCkJNz$Y z#nlyTS@q;w;X0VPMsRMPZe8eQi(~(dy~AMy5Oee4%5W;^3eDn z+#|7xD;WGNxDL1*qJz)XJ}VwhfsfW~ZBRMts(i~h#1sr3JU7@@G+O)$xpp`70_uj= zqdF+Av#NUxqP8!L4cR=qSn;}=hS*MNWS`{@-I62n!ioAc`&#I|V%CTm|9K67`OYimlQ5v#1t^fi%Rza3|Y6_*i)P@eHUd>de$b1GO`P1%lxbjehE& z#)i3wvEHc7Cjq9;!l757h1#%KAQ+x!aaM(=4%s%4P6e-`q0XqgAW<_Pa(<$Bhq>>3 zu7euv6G9YyZu^!6k7yG1ST>ZS(Hn2*4d&!YU2sWMu~eFv0eu=U*QnJw z_|tF~mDSvDNVDTpbo2bjJl>SEQuyj@hxR@Ta~~^F+1HgO)VkNR<12j-hx}@*1HsQW0{QvLucK!aJ{oOypSW)>T z%-qZjeW&CRRR*4#AlSA|7nYYHog2D!4t8s@(t4zO#3qeAV=T+Ix_DTc?1fg2nnfe19CIDgeSM$J*g_>^Sq3%lgSCN1(8|G- zBOJ&#M>QG{%_J|ZD0j7iRt`I7%IU%^8qY-bVOfr`tYn*`!8{9uR*rNhv*mSjw6R4? z7NZTc!d((G|MI^7G6JR2EDKA*)7ly!m~y-@ku6`b#qwfV2I2GR5*6_9QYzC*NiaO3 zu~Qaf#7adpqYboj%miFBO)?*`#R4WzmasOgRsOca1fQUd>jH z22Nm7FK^o^+d*suOkp)b3|FkbpBo1qJrX>xFw zo~V1YeaFU4&tadkFmSg2@=@;D<`J}s!DhB&{}|?1&qOmcKm){HLKD^|JWnhv*a82p z8;E3;!H|kK%oQM<5zj8C3p8*8RYf~%Gpu*t|D(JX_}e<4oa@1ax`)2B)$`2hKfm+~ zU2^&Q(bsi}=pjz=Ch{i2cB^l5vOUJg$@Sg)f2d0?&$azrmy!dX{f&Qp1Xp4^8_x%uf}_i4{^debeYgz>e&4Nl6HR9or!SjVHODHx)|bBlo83AO~Arz zp)%RY8%&*{LA2odsMo}#iwZf$gL1LK>u@O+U4e7-^3G@UR$>dWn;<@GhCA5T!s(pa za!I!d8pf^<(s1`xF3J9JQMoDUzy|d}LHqcN1`+m)NY1yO(KU;h#bIK zArx|ZxC3n0;qbdBXtV!Gy_Fuuh2Se^Cct!`|JxHG;oHp>8ARkN!hYca=L6{AT^E7| zQO87LfrNqqgpMHko_~cHCTH`9-SV%co^w!9G44B4`9S45pqw`7-NNJ=n(sCyyk zX=7&Hr?TEemy8lw}wU2;g#LknbX5y9)o^Qcoo{=?6nZ z_Gyu1!q-v@eem579X$UO=g|zkF7%PKg&A;xVln$wbN3wh?T8RKaX8CN&G+3L%^M(rST-)RB|y9{e3|r1NIs+gPZZL{2WPO!R+Z W<|TNGou}Xc0000lFXMHV7L2oZ#w7lMM|0r9|t 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 U@F%BWTmS$707*qoM6N<$g8fxXz5oCK literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Misc/timewaste.png b/icons/UI_Icons/Achievements/Misc/timewaste.png new file mode 100644 index 0000000000000000000000000000000000000000..24d23025fb05a683df0c93f5519b1b5a932588fa GIT binary patch literal 1468 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 literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Misc/upgrade.png b/icons/UI_Icons/Achievements/Misc/upgrade.png new file mode 100644 index 0000000000000000000000000000000000000000..e73bb512eb196049a39b6844a500f090958c5f7c GIT binary patch literal 3292 zcmV<23?uW2P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!TdUID`!R1_2u4OD09^)aO}5bQ&zQ>gr*j81XJidZ@oiK9+i60|d7 z>s0MHl^Lw9BMJEEXt?c&f{2icf|L*vj3fa=9wdRtz4!F@-Mf2pa~~nOIp-z;zQbnE z**&>oKliu0zumLDf(AhAJf4(PYPnhX#FfGj(@c}DXg5+X-FXqxpViL-p= z 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 aO!R;K;L`(25M1>D0000Nkl2 z+javX2*jTL|1aBz)|6OXxeVxT=bXGGiZ~01=%&y{)LN^hUn!-abrFx9Iap7Sb>2PC znFNLp#7P>>OKW5+8PVj032#Jk{f!>D#Au8t;^0D~i7KT`V+stHDazhqPfdSyG)*X%$-lRC`*{=pM3GF$Vn*qGQ^!K_pqHKttN)j|ew>8fl^! z=9%yv$Rd*KIVG%FSN4Q=)CN22x?BW?gmG8Y4Crn1Gq$QfVs#x9B&VcZx}?Oqq*my) z)LDP2+-5!2aT@+eu(%@1brBU=6xy8n1wfp*UI9^N=Ca!7M7$A}73fbTgJZS;KrHM( z;pH2vBsJ3V5WyG)KF?7y`^Un(tC3Z~t8t|(90FXAkxs|Bq&LsXLLtHgdr$vC&3)(= z5o1gBYi{|85{k~lG?v9yG`9{b9O06yiF$StXqOHl%%Wt}Vk0SDYEY~-FG(d;bE32Y z+TASr)mncoOsf#Wjkq$FcBK^9l__c4tuahH*}nabJ?A#Utd_AmFH;e=TiFvZBy1Kh zP-Pr1@jG_~vl6xFyA_a?*o>;K1V<#UcfxkUtZ7B|)q(~WVVR3S?{1Qkk2LOT=HVhN zGY3L$x2)}mgoL5bC|5g|N(;do(_XzTb>UI&}#fBNCSfxlx$i%+YLd zA|9$%=C+`3u{bYjtuL3M)!{{$)h&14-=Oau?Dj1wdwE6M^bGPU}Qtc%ESY9zg{#g#)^ zn)Lyo9^SM!YD0cvj9X!5T||m^jqU>P1G*xM=zv2IBr^7_s@MPS0FQ;Otc%F-g6UHs zA&W@y&i9i^lafW`cuD>#(2zx>&%@kxvy+O5TC#`?tE7jtlPa$kvWP%`C8O-OJ(0DN zMdTptXW+L#D>@zQAlR%hWTtmm3_MoF0h{Hi)`qYu4*2X&Ar4JcL)ff#5f?>NL-=ee z%ORr3>rR0}=al;RYnOm|Hp!iww0Ud|G(CY*WD?Zl3nyeVU}tuMasU7T07*qoM6N<$ Ef*%6U{Qv*} literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/Skills/mining.png b/icons/UI_Icons/Achievements/Skills/mining.png new file mode 100644 index 0000000000000000000000000000000000000000..7d87d135b438363ca7393ef8a4c08f534772fb4c GIT binary patch literal 3406 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 literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/baseboss.png b/icons/UI_Icons/Achievements/baseboss.png new file mode 100644 index 0000000000000000000000000000000000000000..df2227b391d0aeaf09e2b1975f8cfe05fe528b1f GIT binary patch literal 1697 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 Z(SO?tXcI%|J@fzo002ovPDHLkV1fXyD--|# literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/basemisc.png b/icons/UI_Icons/Achievements/basemisc.png new file mode 100644 index 0000000000000000000000000000000000000000..1346d1c001dd623977de09f9e2866849c4d4f3bd GIT binary patch literal 2244 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 S<*5At0000Mv< literal 0 HcmV?d00001 diff --git a/icons/UI_Icons/Achievements/baseskill.png b/icons/UI_Icons/Achievements/baseskill.png new file mode 100644 index 0000000000000000000000000000000000000000..de6ff8a2e5f2d6cd52e622c4beec808840b9b0a3 GIT binary patch literal 2247 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 JPDHLkV1ne5yIcSO literal 0 HcmV?d00001 diff --git a/icons/program_icons/robotact.gif b/icons/program_icons/robotact.gif new file mode 100644 index 0000000000000000000000000000000000000000..cf11eb184bd44ad4ffdbbc7cc47fde4ee4d55f94 GIT binary patch literal 134 zcmZ?wbhEHb6k`x$SjfP@V8L+UzyXFIKo*1Ie{Mh5kYH!W09PYD17=2$&`%al4F+Zg z9gsXo3j>qOoc@)kZ5bBN5xi)vTd>QXue4!<>BGXlr$th0Pi;B>Yl6<-Tie->|G4(~ iqsi2n$CW(P8uv#BsueqAUXo{X-V