Merge branch 'Skyrat-SS13:master' into master

This commit is contained in:
ReturnToZender
2023-01-10 18:59:01 -06:00
committed by GitHub
198 changed files with 1879 additions and 766 deletions
+18 -4
View File
@@ -2,22 +2,36 @@ Any time you make a change to the schema files, remember to increment the databa
Make sure to also update `DB_MAJOR_VERSION` and `DB_MINOR_VERSION`, which can be found in `code/__DEFINES/subsystem.dm`.
The latest database version is 5.24 (5.22 for /tg/); The query to update the schema revision table is:
The latest database version is 5.25 (5.23 for /tg/); The query to update the schema revision table is:
INSERT INTO `schema_revision` (`major`, `minor`) VALUES (5, 24);
INSERT INTO `schema_revision` (`major`, `minor`) VALUES (5, 25);
or
INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (5, 24);
INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (5, 25);
In any query remember to add a prefix to the table names if you use one.
-----------------------------------------------------
Version 5.25, 28 December 2022, by Mothblocks
Added `tutorial_completions` to mark what ckeys have completed contextual tutorials.
```
CREATE TABLE `tutorial_completions` (
`id` INT NOT NULL AUTO_INCREMENT,
`ckey` VARCHAR(32) NOT NULL,
`tutorial_key` VARCHAR(64) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `ckey_tutorial_unique` (`ckey`, `tutorial_key`));
```
-----------------------------------------------------
Version 5.24, 22 December 2021, by Mothblocks
Fixes a bug in `telemetry_connections` that limited the range of IPs.
```
ALTER TABLE `telemetry_connections` MODIFY COLUMN `address` INT(10) UNSIGNED NOT NULL;
```
-----------------------------------------------------
-----------------------------------------------------
Version 5.23, 15 December 2021, by Mothblocks
Adds `telemetry_connections` table for tracking tgui telemetry.
+8
View File
@@ -716,6 +716,14 @@ CREATE TABLE `telemetry_connections` (
UNIQUE INDEX `unique_constraints` (`ckey` , `telemetry_ckey` , `address` , `computer_id`)
);
DROP TABLE IF EXISTS `tutorial_completions`;
CREATE TABLE `tutorial_completions` (
`id` INT NOT NULL AUTO_INCREMENT,
`ckey` VARCHAR(32) NOT NULL,
`tutorial_key` VARCHAR(64) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `ckey_tutorial_unique` (`ckey`, `tutorial_key`));
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+8
View File
@@ -700,6 +700,14 @@ CREATE TABLE `SS13_telemetry_connections` (
UNIQUE INDEX `unique_constraints` (`ckey` , `telemetry_ckey` , `address` , `computer_id`)
);
DROP TABLE IF EXISTS `SS13_tutorial_completions`;
CREATE TABLE `SS13_tutorial_completions` (
`id` INT NOT NULL AUTO_INCREMENT,
`ckey` VARCHAR(32) NOT NULL,
`tutorial_key` VARCHAR(64) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE INDEX `ckey_tutorial_unique` (`ckey`, `tutorial_key`));
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
+23 -24
View File
@@ -35,7 +35,7 @@
#define MEDAL_VOID_ASCENSION "Void"
#define MEDAL_BLADE_ASCENSION "Blade"
#define MEDAL_TOOLBOX_SOUL "Toolsoul"
#define MEDAL_CHEM_TUT "Beginner Chemist"
#define MEDAL_CHEM_TUT "Beginner Chemist"
#define MEDAL_HOT_DAMN "Hot Damn!"
#define MEDAL_CAYENNE_DISK "Very Important Piscis"
#define MEDAL_TRAM_SURFER "Tram Surfer"
@@ -69,24 +69,23 @@
//Mafia medal hub IDs (misc stuff)
#define MAFIA_MEDAL_HATED "Universally Hated"
#define MAFIA_MEDAL_CHARISMATIC "Charismatic"
#define MAFIA_MEDAL_VIP "VIP"
//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_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_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_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"
@@ -100,18 +99,18 @@
#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 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"
#define WENDIGO_SCORE "Wendigos Killed"
#define KINGGOAT_SCORE "King Goat Killed"
#define TENDRIL_CLEAR_SCORE "Tendrils Killed"
// DB ID for hardcore random mode
#define HARDCORE_RANDOM_SCORE "Hardcore Random Score"
+10 -12
View File
@@ -27,26 +27,24 @@
#define DIAG_MECH_HUD "11"
/// Bot HUDs
#define DIAG_BOT_HUD "12"
/// Circuit assembly health bar
#define DIAG_CIRCUIT_HUD "13"
/// Mech/Silicon tracking beacon, Circutry long range icon
#define DIAG_TRACK_HUD "14"
#define DIAG_TRACK_HUD "13"
/// Airlock shock overlay
#define DIAG_AIRLOCK_HUD "15"
#define DIAG_AIRLOCK_HUD "14"
/// Bot path indicators
#define DIAG_PATH_HUD "16"
#define DIAG_PATH_HUD "15"
/// Gland indicators for abductors
#define GLAND_HUD "17"
#define SENTIENT_DISEASE_HUD "18"
#define AI_DETECT_HUD "19"
#define GLAND_HUD "16"
#define SENTIENT_DISEASE_HUD "17"
#define AI_DETECT_HUD "18"
/// Displays launchpads' targeting reticle
#define DIAG_LAUNCHPAD_HUD "22"
#define DIAG_LAUNCHPAD_HUD "19"
//for antag huds. these are used at the /mob level
#define ANTAG_HUD "23"
#define ANTAG_HUD "20"
// for fans to identify pins
#define FAN_HUD "24"
#define FAN_HUD "21"
/// Mech camera HUD
#define DIAG_CAMERA_HUD "25"
#define DIAG_CAMERA_HUD "22"
// SKYRAT EDIT ADDITION BEGIN - gun permits
/// ammo of guns
+4 -4
View File
@@ -2,13 +2,13 @@
/// Minimum alpha of footprints
#define BLOODY_FOOTPRINT_BASE_ALPHA 20
/// How much blood a regular blood splatter contains
#define BLOOD_AMOUNT_PER_DECAL 50
#define BLOOD_AMOUNT_PER_DECAL 50
/// How much blood an item can have stuck on it
#define BLOOD_ITEM_MAX 200
#define BLOOD_ITEM_MAX 200
/// How much blood a blood decal can contain
#define BLOOD_POOL_MAX 300
#define BLOOD_POOL_MAX 300
/// How much blood a footprint need to at least contain
#define BLOOD_FOOTPRINTS_MIN 5
#define BLOOD_FOOTPRINTS_MIN 5
//Bloody shoe blood states
/// Red blood
+24 -23
View File
@@ -28,8 +28,9 @@
#define COLOR_ALMOST_BLACK "#333333"
#define COLOR_FULL_TONER_BLACK "#101010"
#define COLOR_PRISONER_BLACK "#292929"
#define COLOR_NEARLY_ALL_BLACK "#111111"
#define COLOR_BLACK "#000000"
#define COLOR_HALF_TRANSPARENT_BLACK "#0000007A"
#define COLOR_HALF_TRANSPARENT_BLACK "#0000007A"
#define COLOR_RED "#FF0000"
#define COLOR_SYNDIE_RED "#F10303"
@@ -120,30 +121,30 @@
#define COLOR_SOAPSTONE_GOLD "#FFD900"
#define COLOR_SOAPSTONE_DIAMOND "#00ffee"
#define COLOR_GREEN_GRAY "#99BB76"
#define COLOR_RED_GRAY "#B4696A"
#define COLOR_PALE_BLUE_GRAY "#98C5DF"
#define COLOR_PALE_GREEN_GRAY "#B7D993"
#define COLOR_PALE_RED_GRAY "#D59998"
#define COLOR_GREEN_GRAY "#99BB76"
#define COLOR_RED_GRAY "#B4696A"
#define COLOR_PALE_BLUE_GRAY "#98C5DF"
#define COLOR_PALE_GREEN_GRAY "#B7D993"
#define COLOR_PALE_RED_GRAY "#D59998"
#define COLOR_PALE_PURPLE_GRAY "#CBB1CA"
#define COLOR_PURPLE_GRAY "#AE8CA8"
#define COLOR_PURPLE_GRAY "#AE8CA8"
//Color defines used by the assembly detailer.
#define COLOR_ASSEMBLY_BLACK "#545454"
#define COLOR_ASSEMBLY_BGRAY "#9497AB"
#define COLOR_ASSEMBLY_WHITE "#E2E2E2"
#define COLOR_ASSEMBLY_RED "#CC4242"
#define COLOR_ASSEMBLY_ORANGE "#E39751"
#define COLOR_ASSEMBLY_BEIGE "#AF9366"
#define COLOR_ASSEMBLY_BROWN "#97670E"
#define COLOR_ASSEMBLY_GOLD "#AA9100"
#define COLOR_ASSEMBLY_YELLOW "#CECA2B"
#define COLOR_ASSEMBLY_GURKHA "#999875"
#define COLOR_ASSEMBLY_LGREEN "#789876"
#define COLOR_ASSEMBLY_GREEN "#44843C"
#define COLOR_ASSEMBLY_LBLUE "#5D99BE"
#define COLOR_ASSEMBLY_BLUE "#38559E"
#define COLOR_ASSEMBLY_PURPLE "#6F6192"
#define COLOR_ASSEMBLY_BLACK "#545454"
#define COLOR_ASSEMBLY_BGRAY "#9497AB"
#define COLOR_ASSEMBLY_WHITE "#E2E2E2"
#define COLOR_ASSEMBLY_RED "#CC4242"
#define COLOR_ASSEMBLY_ORANGE "#E39751"
#define COLOR_ASSEMBLY_BEIGE "#AF9366"
#define COLOR_ASSEMBLY_BROWN "#97670E"
#define COLOR_ASSEMBLY_GOLD "#AA9100"
#define COLOR_ASSEMBLY_YELLOW "#CECA2B"
#define COLOR_ASSEMBLY_GURKHA "#999875"
#define COLOR_ASSEMBLY_LGREEN "#789876"
#define COLOR_ASSEMBLY_GREEN "#44843C"
#define COLOR_ASSEMBLY_LBLUE "#5D99BE"
#define COLOR_ASSEMBLY_BLUE "#38559E"
#define COLOR_ASSEMBLY_PURPLE "#6F6192"
///Colors for grayscale tools
#define COLOR_TOOL_BLUE "#1861d5"
@@ -303,4 +304,4 @@ GLOBAL_LIST_INIT(cable_colors, list(
CABLE_COLOR_WHITE = CABLE_HEX_COLOR_WHITE,
CABLE_COLOR_YELLOW = CABLE_HEX_COLOR_YELLOW,
CABLE_COLOR_BROWN = CABLE_HEX_COLOR_BROWN
))
))
+2 -2
View File
@@ -56,9 +56,9 @@
#define FIRELOSS (1<<1)
#define TOXLOSS (1<<2)
#define OXYLOSS (1<<3)
#define SHAME (1<<4)
#define SHAME (1<<4)
#define MANUAL_SUICIDE (1<<5) //suicide_act will do the actual killing.
#define MANUAL_SUICIDE_NONLETHAL (1<<6) //when the suicide is conditionally lethal
#define MANUAL_SUICIDE_NONLETHAL (1<<6) //when the suicide is conditionally lethal
#define EFFECT_STUN "stun"
#define EFFECT_KNOCKDOWN "knockdown"
@@ -11,3 +11,5 @@
/// When returned on a signal hooked to [COMSIG_ATOM_EXPLODE], [COMSIG_ATOM_INTERNAL_EXPLOSION], or [COMSIG_AREA_INTERNAL_EXPLOSION] it prevents the explosion from being propagated further.
#define COMSIG_CANCEL_EXPLOSION (1<<0)
/// from [/atom/movable/proc/set_explosion_resistance] : (old_block, new_block)
#define COMSIG_MOVABLE_EXPLOSION_BLOCK_CHANGED "explosion_block_changed"
@@ -133,5 +133,9 @@
/// From /mob/living/befriend() : (mob/living/new_friend)
#define COMSIG_LIVING_BEFRIENDED "living_befriended"
/// From /obj/item/proc/pickup(): (/obj/item/picked_up_item)
#define COMSIG_LIVING_PICKED_UP_ITEM "living_picked_up_item"
/// From /mob/living/unfriend() : (mob/living/old_friend)
#define COMSIG_LIVING_UNFRIENDED "living_unfriended"
@@ -116,9 +116,12 @@
#define MOB_DEADSAY_SIGNAL_INTERCEPT (1<<0)
///from /mob/living/emote(): ()
#define COMSIG_MOB_EMOTE "mob_emote"
///from base of mob/swap_hand(): (obj/item)
#define COMSIG_MOB_SWAP_HANDS "mob_swap_hands"
///from base of mob/swap_hand(): (obj/item/currently_held_item)
#define COMSIG_MOB_SWAPPING_HANDS "mob_swapping_hands"
#define COMPONENT_BLOCK_SWAP (1<<0)
/// from base of mob/swap_hand(): ()
/// Performed after the hands are swapped.
#define COMSIG_MOB_SWAP_HANDS "mob_swap_hands"
///from base of /mob/verb/pointed: (atom/A)
#define COMSIG_MOB_POINTED "mob_pointed"
///Mob is trying to open the wires of a target [/atom], from /datum/wires/interactable(): (atom/target)
@@ -171,5 +174,9 @@
///from living/flash_act(), when a mob is successfully flashed.
#define COMSIG_MOB_FLASHED "mob_flashed"
/// from mob/get_status_tab_items(): (list/items)
#define COMSIG_MOB_GET_STATUS_TAB_ITEMS "mob_get_status_tab_items"
/// from mob/proc/dropItemToGround()
#define COMSIG_MOB_DROPPING_ITEM "mob_dropping_item"
-3
View File
@@ -18,9 +18,6 @@
/// Gibtonite will now explode
#define GIBTONITE_DETONATE 3
/// For object explosion block calculation
#define EXPLOSION_BLOCK_PROC -1
/// A wrapper for [/atom/proc/ex_act] to ensure that the explosion propagation and attendant signal are always handled.
#define EX_ACT(target, args...)\
if(!(target.flags_1 & PREVENT_CONTENTS_EXPLOSION_1)) { \
-8
View File
@@ -12,13 +12,5 @@
/// Font used when signing on paper.
#define SIGNATURE_FONT "Segoe Script"
//pda fonts
#define MONO "Monospaced"
#define VT "VT323"
#define ORBITRON "Orbitron"
#define SHARE "Share Tech Mono"
GLOBAL_LIST_INIT(pda_styles, sort_list(list(MONO, VT, ORBITRON, SHARE)))
/// Emoji icon set
#define EMOJI_SET 'modular_skyrat/master_files/icons/emoji.dmi' // SKYRAT EDIT - ORIGINAL: 'icons/ui_icons/emoji/emoji.dmi'
+1 -1
View File
@@ -117,7 +117,7 @@ DEFINE_BITFIELD(food_flags, list(
///Amount of reagents you start with on crafted food excluding the used parts
#define CRAFTED_FOOD_BASE_REAGENT_MODIFIER 0.7
///Modifier of reagents you get when crafting food from the parts used
#define CRAFTED_FOOD_INGREDIENT_REAGENT_MODIFIER 0.5
#define CRAFTED_FOOD_INGREDIENT_REAGENT_MODIFIER 0.5
#define IS_EDIBLE(O) (O.GetComponent(/datum/component/edible))
+2 -2
View File
@@ -135,8 +135,8 @@
#define ui_borg_lamp "CENTER-3:16, SOUTH:5"
#define ui_borg_tablet "CENTER-4:16, SOUTH:5"
#define ui_inv1 "CENTER-2:16,SOUTH:5"
#define ui_inv2 "CENTER-1 :16,SOUTH:5"
#define ui_inv3 "CENTER :16,SOUTH:5"
#define ui_inv2 "CENTER-1 :16,SOUTH:5"
#define ui_inv3 "CENTER :16,SOUTH:5"
#define ui_borg_module "CENTER+1:16,SOUTH:5"
#define ui_borg_store "CENTER+2:16,SOUTH:5"
#define ui_borg_camera "CENTER+3:21,SOUTH:5"
+2 -2
View File
@@ -146,8 +146,8 @@ DEFINE_BITFIELD(smoothing_flags, list(
#define SMOOTH_GROUP_WINDOW_FULLTILE_PLASTITANIUM S_OBJ(24) ///turf/closed/indestructible/opsglass, /obj/structure/window/reinforced/plasma/plastitanium
#define SMOOTH_GROUP_WINDOW_FULLTILE_SHUTTLE S_OBJ(25) ///obj/structure/window/reinforced/shuttle
#define SMOOTH_GROUP_LATTICE S_OBJ(31) ///obj/structure/lattice
#define SMOOTH_GROUP_CATWALK S_OBJ(32) ///obj/structure/lattice/catwalk
#define SMOOTH_GROUP_LATTICE S_OBJ(31) ///obj/structure/lattice
#define SMOOTH_GROUP_CATWALK S_OBJ(32) ///obj/structure/lattice/catwalk
#define SMOOTH_GROUP_AIRLOCK S_OBJ(41) ///obj/machinery/door/airlock
+1
View File
@@ -279,3 +279,4 @@ GLOBAL_LIST_INIT(book_types, typecacheof(list(
#define is_unassigned_job(job_type) (istype(job_type, /datum/job/unassigned))
#define isprojectilespell(thing) (istype(thing, /datum/action/cooldown/spell/pointed/projectile))
#define is_multi_tile_object(atom) (atom.bound_width > world.icon_size || atom.bound_height > world.icon_size)
+3
View File
@@ -240,6 +240,9 @@
///Layer for screentips
#define SCREENTIP_LAYER 4
/// Layer for tutorial instructions
#define TUTORIAL_INSTRUCTIONS_LAYER 5
#define LOBBY_BACKGROUND_LAYER 3
#define LOBBY_BUTTON_LAYER 4
+5 -16
View File
@@ -12,18 +12,18 @@
//Bay lighting engine shit, not in /code/modules/lighting because BYOND is being shit about it
/// frequency, in 1/10ths of a second, of the lighting process
#define LIGHTING_INTERVAL 5
#define LIGHTING_INTERVAL 5
#define MINIMUM_USEFUL_LIGHT_RANGE 1.4
/// type of falloff to use for lighting; 1 for circular, 2 for square
#define LIGHTING_FALLOFF 1
#define LIGHTING_FALLOFF 1
/// use lambertian shading for light sources
#define LIGHTING_LAMBERTIAN 0
#define LIGHTING_LAMBERTIAN 0
/// height off the ground of light sources on the pseudo-z-axis, you should probably leave this alone
#define LIGHTING_HEIGHT 1
#define LIGHTING_HEIGHT 1
/// Value used to round lumcounts, values smaller than 1/129 don't matter (if they do, thanks sinking points), greater values will make lighting less precise, but in turn increase performance, VERY SLIGHTLY.
#define LIGHTING_ROUND_VALUE (1 / 64)
#define LIGHTING_ROUND_VALUE (1 / 64)
/// icon used for lighting shading effects
#define LIGHTING_ICON 'icons/effects/lighting_object.dmi'
@@ -32,17 +32,6 @@
/// Set to zero to disable soft lighting. Luminosity changes then work if it's lit at all.
#define LIGHTING_SOFT_THRESHOLD 0
/// If I were you I'd leave this alone.
#define LIGHTING_BASE_MATRIX \
list \
( \
1, 1, 1, 0, \
1, 1, 1, 0, \
1, 1, 1, 0, \
1, 1, 1, 0, \
0, 0, 0, 1 \
) \
///How many tiles standard fires glow.
#define LIGHT_RANGE_FIRE 3
+4
View File
@@ -237,5 +237,9 @@
#define GET_TRUE_DIST(a, b) (a == null || b == null) ? -1 : max(abs(a.x -b.x), abs(a.y-b.y), abs(a.z-b.z))
//We used to use linear regression to approximate the answer, but Mloc realized this was actually faster.
//And lo and behold, it is, and it's more accurate to boot.
#define CHEAP_HYPOTENUSE(Ax, Ay, Bx, By) (sqrt(abs(Ax - Bx) ** 2 + abs(Ay - By) ** 2)) //A squared + B squared = C squared
/// The number of cells in a taxicab circle (rasterized diamond) of radius X.
#define DIAMOND_AREA(X) (1 + 2*(X)*((X)+1))
+2 -1
View File
@@ -1,2 +1,3 @@
/// Helper macro for compile time translation
/// Helper macro for creating a matrix at the given offsets.
/// Works at compile time.
#define TRANSLATE_MATRIX(offset_x, offset_y) matrix(1, 0, (offset_x), 0, 1, (offset_y))
+12 -12
View File
@@ -8,7 +8,7 @@
//movement intent defines for the m_intent var
#define MOVE_INTENT_WALK "walk"
#define MOVE_INTENT_RUN "run"
#define MOVE_INTENT_RUN "run"
//Blood levels
#define BLOOD_VOLUME_MAX_LETHAL 2150
@@ -35,8 +35,8 @@
#define MOB_SIZE_HUGE 4 // Use this for things you don't want bluespace body-bagged
//Ventcrawling defines
#define VENTCRAWLER_NONE 0
#define VENTCRAWLER_NUDE 1
#define VENTCRAWLER_NONE 0
#define VENTCRAWLER_NUDE 1
#define VENTCRAWLER_ALWAYS 2
//Mob bio-types flags
@@ -183,12 +183,12 @@
#define BRAIN_TRAUMA_SPECIAL /datum/brain_trauma/special
#define BRAIN_TRAUMA_MAGIC /datum/brain_trauma/magic
#define TRAUMA_RESILIENCE_BASIC 1 //Curable with chems
#define TRAUMA_RESILIENCE_SURGERY 2 //Curable with brain surgery
#define TRAUMA_RESILIENCE_LOBOTOMY 3 //Curable with lobotomy
#define TRAUMA_RESILIENCE_WOUND 4 //Curable by healing the head wound
#define TRAUMA_RESILIENCE_MAGIC 5 //Curable only with magic
#define TRAUMA_RESILIENCE_ABSOLUTE 6 //This is here to stay
#define TRAUMA_RESILIENCE_BASIC 1 //Curable with chems
#define TRAUMA_RESILIENCE_SURGERY 2 //Curable with brain surgery
#define TRAUMA_RESILIENCE_LOBOTOMY 3 //Curable with lobotomy
#define TRAUMA_RESILIENCE_WOUND 4 //Curable by healing the head wound
#define TRAUMA_RESILIENCE_MAGIC 5 //Curable only with magic
#define TRAUMA_RESILIENCE_ABSOLUTE 6 //This is here to stay
//Limit of traumas for each resilience tier
#define TRAUMA_LIMIT_BASIC 3
@@ -349,7 +349,7 @@
//determines if a mob can smash through it
#define ENVIRONMENT_SMASH_NONE 0
#define ENVIRONMENT_SMASH_STRUCTURES 1 //crates, lockers, ect
#define ENVIRONMENT_SMASH_WALLS 2 //walls
#define ENVIRONMENT_SMASH_WALLS 2 //walls
#define ENVIRONMENT_SMASH_RWALLS 3 //rwalls
#define NO_SLIP_WHEN_WALKING (1<<0)
@@ -420,12 +420,12 @@
//#define AGE_MIN 17 //youngest a character can be //ORIGINAL
#define AGE_MIN 18 //youngest a character can be //SKYRAT EDIT CHANGE - age
#define AGE_MAX 85 //oldest a character can be
#define AGE_MINOR 20 //legal age of space drinking and smoking
#define AGE_MINOR 20 //legal age of space drinking and smoking
#define WIZARD_AGE_MIN 30 //youngest a wizard can be
#define APPRENTICE_AGE_MIN 29 //youngest an apprentice can be
#define SHOES_SLOWDOWN 0 //How much shoes slow you down by default. Negative values speed you up
#define SHOES_SPEED_SLIGHT SHOES_SLOWDOWN - 1 // slightest speed boost to movement
#define SHOES_SPEED_SLIGHT SHOES_SLOWDOWN - 1 // slightest speed boost to movement
#define POCKET_STRIP_DELAY (4 SECONDS) //time taken to search somebody's pockets
#define DOOR_CRUSH_DAMAGE 15 //the amount of damage that airlocks deal when they crush you
+1 -1
View File
@@ -25,7 +25,7 @@
#define PROGRAM_CATEGORY_CREW "Crew"
#define PROGRAM_CATEGORY_ENGI "Engineering"
#define PROGRAM_CATEGORY_SUPL "Supply"
#define PROGRAM_CATEGORY_SCI "Science"
#define PROGRAM_CATEGORY_SCI "Science"
#define PROGRAM_CATEGORY_MISC "Other"
#define DETOMATIX_RESIST_MINOR 1
+1 -1
View File
@@ -14,7 +14,7 @@
#define MONKEY_SYRINGE_RETALIATION_PROB 20
// Probability per Life tick that the monkey will:
/// probability that monkey aggro against the mob pulling it
/// probability that monkey aggro against the mob pulling it
#define MONKEY_PULL_AGGRO_PROB 5
/// probability that monkey will get into mischief, i.e. finding/stealing items
#define MONKEY_SHENANIGAN_PROB 20
+1 -1
View File
@@ -78,7 +78,7 @@
#define NETWORK_PORT_UPDATE(LIST) if(LIST) { LIST["_updated"] = TRUE }
#define NETWORK_PORT_CLEAR_UPDATE(LIST) if(LIST) { LIST["_updated"] = FALSE }
#define NETWORK_PORT_SET_UPDATE(LIST) if(LIST) { LIST["_updated"] = TRUE }
#define NETWORK_PORT_DISCONNECT(LIST) if(LIST) { LIST["_disconnected"] = TRUE }
#define NETWORK_PORT_DISCONNECT(LIST) if(LIST) { LIST["_disconnected"] = TRUE }
/// Error codes
#define NETWORK_ERROR_OK null
+3 -3
View File
@@ -4,9 +4,9 @@
#define MACHINERY_LAYER_1 1
#define SOLAR_TRACK_OFF 0
#define SOLAR_TRACK_TIMED 1
#define SOLAR_TRACK_AUTO 2
#define SOLAR_TRACK_OFF 0
#define SOLAR_TRACK_TIMED 1
#define SOLAR_TRACK_AUTO 2
///conversion ratio from joules to watts
#define WATTS / 0.002
+1 -1
View File
@@ -31,7 +31,7 @@
#define CHAT_GHOSTWHISPER (1<<7)
#define CHAT_GHOSTPDA (1<<8)
#define CHAT_GHOSTRADIO (1<<9)
#define CHAT_BANKCARD (1<<10)
#define CHAT_BANKCARD (1<<10)
#define CHAT_GHOSTLAWS (1<<11)
#define CHAT_LOGIN_LOGOUT (1<<12)
+1 -1
View File
@@ -29,7 +29,7 @@ Ask Mothblocks if they're around
#define RAD_NO_INSULATION 1.0 // For things that shouldn't become irradiated for whatever reason
#define RAD_VERY_LIGHT_INSULATION 0.9 // What girders have
#define RAD_LIGHT_INSULATION 0.8
#define RAD_MEDIUM_INSULATION 0.7 // What common walls have
#define RAD_MEDIUM_INSULATION 0.7 // What common walls have
#define RAD_HEAVY_INSULATION 0.6 // What reinforced walls have
#define RAD_EXTREME_INSULATION 0.5 // What rad collectors have
#define RAD_FULL_INSULATION 0 // Completely stops radiation from coming through
+20 -20
View File
@@ -60,19 +60,19 @@
// Frequencies are always odd numbers and range from 1201 to 1599.
#define FREQ_UPLINK 1211 // Dummy loopback frequency, used for radio uplink. Not seen in game.
#define FREQ_SYNDICATE 1213 // Nuke op comms frequency, dark brown
#define FREQ_CTF_RED 1215 // CTF red team comms frequency, red
#define FREQ_CTF_BLUE 1217 // CTF blue team comms frequency, blue
#define FREQ_CTF_GREEN 1219 // CTF green team comms frequency, green
#define FREQ_CTF_YELLOW 1221 // CTF yellow team comms frequency, yellow
#define FREQ_CENTCOM 1337 // CentCom comms frequency, gray
#define FREQ_SUPPLY 1347 // Supply comms frequency, light brown
#define FREQ_SERVICE 1349 // Service comms frequency, green
#define FREQ_SCIENCE 1351 // Science comms frequency, plum
#define FREQ_COMMAND 1353 // Command comms frequency, gold
#define FREQ_MEDICAL 1355 // Medical comms frequency, soft blue
#define FREQ_ENGINEERING 1357 // Engineering comms frequency, orange
#define FREQ_SECURITY 1359 // Security comms frequency, red
#define FREQ_SYNDICATE 1213 // Nuke op comms frequency, dark brown
#define FREQ_CTF_RED 1215 // CTF red team comms frequency, red
#define FREQ_CTF_BLUE 1217 // CTF blue team comms frequency, blue
#define FREQ_CTF_GREEN 1219 // CTF green team comms frequency, green
#define FREQ_CTF_YELLOW 1221 // CTF yellow team comms frequency, yellow
#define FREQ_CENTCOM 1337 // CentCom comms frequency, gray
#define FREQ_SUPPLY 1347 // Supply comms frequency, light brown
#define FREQ_SERVICE 1349 // Service comms frequency, green
#define FREQ_SCIENCE 1351 // Science comms frequency, plum
#define FREQ_COMMAND 1353 // Command comms frequency, gold
#define FREQ_MEDICAL 1355 // Medical comms frequency, soft blue
#define FREQ_ENGINEERING 1357 // Engineering comms frequency, orange
#define FREQ_SECURITY 1359 // Security comms frequency, red
#define FREQ_HOLOGRID_SOLUTION 1433
#define FREQ_STATUS_DISPLAYS 1435
@@ -82,23 +82,23 @@
// This represents 1/8th of the available spectrum.
#define FREQ_NAV_BEACON 1445
#define FREQ_AI_PRIVATE 1447 // AI private comms frequency, magenta
#define FREQ_AI_PRIVATE 1447 // AI private comms frequency, magenta
#define FREQ_PRESSURE_PLATE 1447
#define FREQ_ELECTROPACK 1449
#define FREQ_MAGNETS 1449
#define FREQ_LOCATOR_IMPLANT 1451
#define FREQ_SIGNALER 1457 // the default for new signalers
#define FREQ_COMMON 1459 // Common comms frequency, dark green
#define FREQ_SIGNALER 1457 // the default for new signalers
#define FREQ_COMMON 1459 // Common comms frequency, dark green
#define MAX_FREQ 1489 // ------------------------------------------------------
#define MAX_FREE_FREQ 1599 // -------------------------------------------------
// Transmission types.
#define TRANSMISSION_WIRE 0 // some sort of wired connection, not used
#define TRANSMISSION_RADIO 1 // electromagnetic radiation (default)
#define TRANSMISSION_SUBSPACE 2 // subspace transmission (headsets only)
#define TRANSMISSION_SUPERSPACE 3 // reaches independent (CentCom) radios only
#define TRANSMISSION_WIRE 0 // some sort of wired connection, not used
#define TRANSMISSION_RADIO 1 // electromagnetic radiation (default)
#define TRANSMISSION_SUBSPACE 2 // subspace transmission (headsets only)
#define TRANSMISSION_SUPERSPACE 3 // reaches independent (CentCom) radios only
// Filter types, used as an optimization to avoid unnecessary proc calls.
#define RADIO_SIGNALER "signaler"
+1 -1
View File
@@ -1,3 +1,3 @@
#define RESONATOR_MODE_AUTO 1
#define RESONATOR_MODE_AUTO 1
#define RESONATOR_MODE_MANUAL 2
#define RESONATOR_MODE_MATRIX 3
+1 -1
View File
@@ -20,7 +20,7 @@
*
* make sure you add an update to the schema_version stable in the db changelog
*/
#define DB_MINOR_VERSION 24
#define DB_MINOR_VERSION 25
//! ## Timing subsystem
+2
View File
@@ -510,6 +510,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_PARALYSIS "paralysis"
/// Used for limbs.
#define TRAIT_DISABLED_BY_WOUND "disabled-by-wound"
/// This movable atom has the explosive block element
#define TRAIT_BLOCKING_EXPLOSIVES "blocking_explosives"
/// Mobs with this trait can't send the mining shuttle console when used outside the station itself
#define TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION "forbid_mining_shuttle_console_outside_station"
+1 -1
View File
@@ -15,7 +15,7 @@
#define RECT_TURFS(H_RADIUS, V_RADIUS, CENTER) \
block( \
locate(max(CENTER.x-(H_RADIUS),1), max(CENTER.y-(V_RADIUS),1), CENTER.z), \
locate(max(CENTER.x-(H_RADIUS),1), max(CENTER.y-(V_RADIUS),1), CENTER.z), \
locate(min(CENTER.x+(H_RADIUS),world.maxx), min(CENTER.y+(V_RADIUS),world.maxy), CENTER.z) \
)
-6
View File
@@ -11,12 +11,6 @@
return null
return format_text ? format_text(checked_area.name) : checked_area.name
//We used to use linear regression to approximate the answer, but Mloc realized this was actually faster.
//And lo and behold, it is, and it's more accurate to boot.
///Calculate the hypotenuse cheaply (this should be in maths.dm)
/proc/cheap_hypotenuse(Ax, Ay, Bx, By)
return sqrt(abs(Ax - Bx) ** 2 + abs(Ay - By) ** 2) //A squared + B squared = C squared
/** toggle_organ_decay
* inputs: first_object (object to start with)
* outputs:
+16 -4
View File
@@ -18,14 +18,26 @@
y += view_size[2]
if(findtext(screen_loc, "SOUTH"))
y += world.icon_size
// Cut out everything we just parsed
screen_loc = cut_relative_direction(screen_loc)
var/list/x_and_y = splittext(screen_loc, ",")
var/list/x_pack = splittext(x_and_y[1], ":")
var/list/y_pack = splittext(x_and_y[2], ":")
x += text2num(x_pack[1]) * world.icon_size
y += text2num(y_pack[1]) * world.icon_size
var/x_coord = x_pack[1]
var/y_coord = y_pack[1]
if (findtext(x_coord, "CENTER"))
x += view_size[1] / 2
if (findtext(y_coord, "CENTER"))
y += view_size[2] / 2
x_coord = text2num(cut_relative_direction(x_coord))
y_coord = text2num(cut_relative_direction(y_coord))
x += x_coord * world.icon_size
y += y_coord * world.icon_size
if(length(x_pack) > 1)
x += text2num(x_pack[2])
+2 -2
View File
@@ -48,7 +48,7 @@
/atom/movable/screen/ghost/minigames_menu
name ="Minigames"
icon_state = "minigames"
/atom/movable/screen/ghost/minigames_menu/Click()
var/mob/dead/observer/observer = usr
observer.open_minigames_menu()
@@ -88,7 +88,7 @@
static_inventory += using
using = new /atom/movable/screen/language_menu
using.screen_loc = ui_ghost_language_menu
using.screen_loc = ui_ghost_language_menu
using.icon = ui_style
using.hud = src
static_inventory += using
+9 -1
View File
@@ -59,7 +59,15 @@
if (after_attack_secondary_result == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN || after_attack_secondary_result == SECONDARY_ATTACK_CONTINUE_CHAIN)
return TRUE
return afterattack(target, user, TRUE, params) == TRUE
var/afterattack_result = afterattack(target, user, TRUE, params)
if (!(afterattack_result & AFTERATTACK_PROCESSED_ITEM) && isitem(target))
if (isnull(user.get_inactive_held_item()))
SStutorials.suggest_tutorial(user, /datum/tutorial/switch_hands, params2list(params))
else
SStutorials.suggest_tutorial(user, /datum/tutorial/drop, params2list(params))
return afterattack_result & TRUE //this is really stupid but its needed because afterattack can return TRUE | FLAGS.
/// Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown.
/obj/item/proc/attack_self(mob/user, modifiers)
@@ -414,3 +414,4 @@
/datum/config_entry/flag/disallow_circuit_sounds
/datum/config_entry/flag/give_tutorials_without_db
@@ -649,3 +649,5 @@
default = 50
/datum/config_entry/string/morgue_cadaver_override_species
/datum/config_entry/flag/toast_notification_on_init
+3
View File
@@ -267,6 +267,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
log_world(msg)
if(world.system_type == MS_WINDOWS && CONFIG_GET(flag/toast_notification_on_init) && !length(GLOB.clients))
world.shelleo("start /min powershell -ExecutionPolicy Bypass -File tools/initToast/initToast.ps1 -name \"[world.name]\" -icon %CD%\\icons\\ui_icons\\common\\tg_16.png -port [world.port]")
// Set world options.
world.change_fps(CONFIG_GET(number/fps))
var/initialized_tod = REALTIMEOFDAY
+110 -139
View File
@@ -119,30 +119,31 @@ SUBSYSTEM_DEF(explosions)
var/x0 = epicenter.x
var/y0 = epicenter.y
var/list/wipe_colours = list()
for(var/turf/T in spiral_range_turfs(max_range, epicenter))
wipe_colours += T
var/dist = cheap_hypotenuse(T.x, T.y, x0, y0)
var/list/cached_exp_block = list()
for(var/turf/explode in prepare_explosion_turfs(max_range, epicenter))
wipe_colours += explode
var/our_x = explode.x
var/our_y = explode.y
var/dist = CHEAP_HYPOTENUSE(our_x, our_y, x0, y0)
if(newmode == "Yes")
var/turf/TT = T
while(TT != epicenter)
TT = get_step_towards(TT,epicenter)
if(TT.density)
dist += TT.explosion_block
for(var/obj/O in T)
var/the_block = O.explosion_block
dist += the_block == EXPLOSION_BLOCK_PROC ? O.GetExplosionBlock() : the_block
if(explode != epicenter)
var/our_block = cached_exp_block[get_step_towards(explode, epicenter)]
dist += our_block
cached_exp_block[explode] = our_block + explode.explosive_resistance
else
cached_exp_block[explode] = explode.explosive_resistance
dist = round(dist, 0.01)
if(dist < dev)
T.color = "red"
T.maptext = MAPTEXT("Dev")
explode.color = "red"
explode.maptext = MAPTEXT("[dist]")
else if (dist < heavy)
T.color = "yellow"
T.maptext = MAPTEXT("Heavy")
explode.color = "yellow"
explode.maptext = MAPTEXT("[dist]")
else if (dist < light)
T.color = "blue"
T.maptext = MAPTEXT("Light")
explode.color = "blue"
explode.maptext = MAPTEXT("[dist]")
else
continue
@@ -398,84 +399,82 @@ SUBSYSTEM_DEF(explosions)
for(var/mob/living/L in viewers(flash_range, epicenter))
L.flash_act()
var/list/affected_turfs = GatherSpiralTurfs(max_range, epicenter)
var/list/affected_turfs = prepare_explosion_turfs(max_range, epicenter)
var/reactionary = CONFIG_GET(flag/reactionary_explosions)
var/list/cached_exp_block
if(reactionary)
cached_exp_block = CaculateExplosionBlock(affected_turfs)
// this list is setup in the form position -> block for that position
// we assert that turfs will be processed closed to farthest, so we can build this as we go along
// This is gonna be an array, index'd by turfs
var/list/cached_exp_block = list()
//lists are guaranteed to contain at least 1 turf at this point
//we presuppose that we'll be iterating away from the epicenter
for(var/turf/explode as anything in affected_turfs)
var/our_x = explode.x
var/our_y = explode.y
var/dist = CHEAP_HYPOTENUSE(our_x, our_y, x0, y0)
for(var/TI in affected_turfs)
var/turf/T = TI
var/init_dist = cheap_hypotenuse(T.x, T.y, x0, y0)
var/dist = init_dist
// Using this pattern, block will flow out from blocking turfs, essentially caching the recursion
// This is safe because if get_step_towards is ever anything but caridnally off, it'll do a diagonal move
// So we always sample from a "loop" closer
// It's kind of behaviorly unimpressive that that's a problem for the future
if(reactionary)
var/turf/Trajectory = T
while(Trajectory != epicenter)
Trajectory = get_step_towards(Trajectory, epicenter)
dist += cached_exp_block[Trajectory]
if(explode == epicenter)
cached_exp_block[explode] = explode.explosive_resistance
else
var/our_block = cached_exp_block[get_step_towards(explode, epicenter)]
dist += our_block
cached_exp_block[explode] = our_block + explode.explosive_resistance
var/flame_dist = dist < flame_range
var/throw_dist = dist
var/severity = EXPLODE_NONE
if(dist < devastation_range)
dist = EXPLODE_DEVASTATE
severity = EXPLODE_DEVASTATE
else if(dist < heavy_impact_range)
dist = EXPLODE_HEAVY
severity = EXPLODE_HEAVY
else if(dist < light_impact_range)
dist = EXPLODE_LIGHT
else
dist = EXPLODE_NONE
severity = EXPLODE_LIGHT
if(T == epicenter) // Ensures explosives detonating from bags trigger other explosives in that bag
if(explode == epicenter) // Ensures explosives detonating from bags trigger other explosives in that bag
var/list/items = list()
for(var/I in T)
var/atom/A = I
if (length(A.contents) && !(A.flags_1 & PREVENT_CONTENTS_EXPLOSION_1)) //The atom/contents_explosion() proc returns null if the contents ex_acting has been handled by the atom, and TRUE if it hasn't.
items += A.get_all_contents(ignore_flag_1 = PREVENT_CONTENTS_EXPLOSION_1)
if(isliving(A))
items -= A //Stops mobs from taking double damage from explosions originating from them/their turf, such as from projectiles
for(var/thing in items)
var/atom/movable/movable_thing = thing
if(QDELETED(movable_thing))
continue
switch(dist)
if(EXPLODE_DEVASTATE)
SSexplosions.high_mov_atom += movable_thing
if(EXPLODE_HEAVY)
SSexplosions.med_mov_atom += movable_thing
if(EXPLODE_LIGHT)
SSexplosions.low_mov_atom += movable_thing
switch(dist)
for(var/atom/holder as anything in explode)
if (length(holder.contents) && !(holder.flags_1 & PREVENT_CONTENTS_EXPLOSION_1)) //The atom/contents_explosion() proc returns null if the contents ex_acting has been handled by the atom, and TRUE if it hasn't.
items += holder.get_all_contents(ignore_flag_1 = PREVENT_CONTENTS_EXPLOSION_1)
if(isliving(holder))
items -= holder //Stops mobs from taking double damage from explosions originating from them/their turf, such as from projectiles
switch(severity)
if(EXPLODE_DEVASTATE)
SSexplosions.high_mov_atom += items
if(EXPLODE_HEAVY)
SSexplosions.med_mov_atom += items
if(EXPLODE_LIGHT)
SSexplosions.low_mov_atom += items
switch(severity)
if(EXPLODE_DEVASTATE)
SSexplosions.highturf += T
SSexplosions.highturf += explode
if(EXPLODE_HEAVY)
SSexplosions.medturf += T
SSexplosions.medturf += explode
if(EXPLODE_LIGHT)
SSexplosions.lowturf += T
SSexplosions.lowturf += explode
//SKYRAT EDIT ADDITION
for(var/obj/machinery/light/iterating_light in T)
for(var/obj/machinery/light/iterating_light in explode)
iterating_light.start_flickering()
//SKYRAT EDIT END
if(flame_dist && prob(40) && !isspaceturf(T) && !T.density)
flameturf += T
if(prob(40) && dist < flame_range && !isspaceturf(explode) && !explode.density)
flameturf += explode
//--- THROW ITEMS AROUND ---
var/throw_dir = get_dir(epicenter,T)
var/throw_range = max_range-throw_dist
var/list/throwingturf = T.explosion_throw_details
if (throwingturf)
if (throwingturf[1] < throw_range)
throwingturf[1] = throw_range
throwingturf[2] = throw_dir
if (explode.explosion_throw_details)
var/list/throwingturf = explode.explosion_throw_details
if (throwingturf[1] < max_range - dist)
throwingturf[1] = max_range - dist
throwingturf[2] = get_dir(epicenter, explode)
throwingturf[3] = max_range
else
T.explosion_throw_details = list(throw_range, throw_dir, max_range)
throwturf += T
explode.explosion_throw_details = list(max_range - dist, get_dir(epicenter, explode), max_range)
throwturf += explode
var/took = (REALTIMEOFDAY - started_at) / 10
@@ -593,68 +592,40 @@ SUBSYSTEM_DEF(explosions)
#undef FREQ_UPPER
#undef FREQ_LOWER
/datum/controller/subsystem/explosions/proc/GatherSpiralTurfs(range, turf/epicenter)
/// Returns a list of turfs in X range from the epicenter
/// Returns in a unique order, spiraling outwards
/// This is done to ensure our progressive cache of blast resistance is always valid
/// This is quite fast
/proc/prepare_explosion_turfs(range, turf/epicenter)
var/list/outlist = list()
var/center = epicenter
var/dist = range
if(!dist)
outlist += center
return outlist
// Add in the center
outlist += epicenter
var/turf/t_center = get_turf(center)
if(!t_center)
return outlist
var/our_x = epicenter.x
var/our_y = epicenter.y
var/our_z = epicenter.z
var/list/L = outlist
var/turf/T
var/y
var/x
var/c_dist = 1
L += t_center
var/max_x = world.maxx
var/max_y = world.maxy
for(var/i in 1 to range)
var/lowest_x = our_x - i
var/lowest_y = our_y - i
var/highest_x = our_x + i
var/highest_y = our_y + i
// top left to one before top right
if(highest_y <= max_y)
outlist += block(locate(max(lowest_x, 1), highest_y, our_z), locate(min(highest_x - 1, max_x), highest_y, our_z))
// top right to one before bottom right
if(highest_x <= max_x)
outlist += block(locate(highest_x, min(highest_y, max_y), our_z), locate(highest_x, max(lowest_y + 1, 1), our_z))
// bottom right to one before bottom left
if(lowest_y >= 1)
outlist += block(locate(min(highest_x, max_x), lowest_y, our_z), locate(max(lowest_x + 1, 1), lowest_y, our_z))
// bottom left to one before top left
if(lowest_x >= 1)
outlist += block(locate(lowest_x, max(lowest_y, 1), our_z), locate(lowest_x, min(highest_y - 1, max_y), our_z))
while( c_dist <= dist )
y = t_center.y + c_dist
x = t_center.x - c_dist + 1
for(x in x to t_center.x+c_dist)
T = locate(x,y,t_center.z)
if(T)
L += T
y = t_center.y + c_dist - 1
x = t_center.x + c_dist
for(y in t_center.y-c_dist to y)
T = locate(x,y,t_center.z)
if(T)
L += T
y = t_center.y - c_dist
x = t_center.x + c_dist - 1
for(x in t_center.x-c_dist to x)
T = locate(x,y,t_center.z)
if(T)
L += T
y = t_center.y - c_dist + 1
x = t_center.x - c_dist
for(y in y to t_center.y+c_dist)
T = locate(x,y,t_center.z)
if(T)
L += T
c_dist++
. = L
/datum/controller/subsystem/explosions/proc/CaculateExplosionBlock(list/affected_turfs)
. = list()
var/I
for(I in 1 to affected_turfs.len) // we cache the explosion block rating of every turf in the explosion area
var/turf/T = affected_turfs[I]
var/current_exp_block = T.density ? T.explosion_block : 0
for(var/obj/O in T)
var/the_block = O.explosion_block
current_exp_block += the_block == EXPLOSION_BLOCK_PROC ? O.GetExplosionBlock() : the_block
.[T] = current_exp_block
return outlist
/datum/controller/subsystem/explosions/fire(resumed = 0)
if (!is_exploding())
@@ -743,15 +714,15 @@ SUBSYSTEM_DEF(explosions)
for (var/thing in throw_turf)
if (!thing)
continue
var/turf/T = thing
var/list/L = T.explosion_throw_details
T.explosion_throw_details = null
if (length(L) != 3)
var/turf/explode = thing
var/list/details = explode.explosion_throw_details
explode.explosion_throw_details = null
if (length(details) != 3)
continue
var/throw_range = L[1]
var/throw_dir = L[2]
var/max_range = L[3]
for(var/atom/movable/A in T)
var/throw_range = details[1]
var/throw_dir = details[2]
var/max_range = details[3]
for(var/atom/movable/A in explode)
if(QDELETED(A))
continue
if(!A.anchored && A.move_resist != INFINITY)
+111
View File
@@ -0,0 +1,111 @@
/// Namespace for housing code relating to giving contextual tutorials to users.
SUBSYSTEM_DEF(tutorials)
name = "Tutorials"
flags = SS_NO_FIRE
/// A mapping of /datum/tutorial type to their manager singleton.
/// You probably shouldn't be indexing this directly.
var/list/datum/tutorial_manager/tutorial_managers = list()
VAR_PRIVATE/list/datum/tutorial_manager/tutorial_managers_by_key = list()
/datum/controller/subsystem/tutorials/Initialize()
init_tutorial_managers()
load_initial_tutorial_completions()
RegisterSignal(SSdcs, COMSIG_GLOB_CLIENT_CONNECT, PROC_REF(on_client_connect))
return SS_INIT_SUCCESS
/// Will suggest the passed tutorial type to the user.
/// Will check that they should actually see it, e.g. hasn't completed it yet, etc.
/// Then, calls `/datum/tutorial/subtype/perform` with the extra arguments passed in.
/datum/controller/subsystem/tutorials/proc/suggest_tutorial(mob/user, datum/tutorial/tutorial_type, ...)
var/datum/tutorial_manager/tutorial_manager = tutorial_managers[tutorial_type]
if (isnull(tutorial_manager))
CRASH("[tutorial_type] is not a valid tutorial type")
if (!tutorial_manager.should_run(user))
return
INVOKE_ASYNC(tutorial_manager, TYPE_PROC_REF(/datum/tutorial_manager, try_perform), user, args.Copy(3))
/datum/controller/subsystem/tutorials/proc/init_tutorial_managers()
PRIVATE_PROC(TRUE)
for (var/datum/tutorial/tutorial_type as anything in subtypesof(/datum/tutorial))
var/datum/tutorial_manager/tutorial_manager = new /datum/tutorial_manager(tutorial_type)
tutorial_managers[tutorial_type] = tutorial_manager
tutorial_managers_by_key[tutorial_manager.get_key()] = tutorial_manager
/datum/controller/subsystem/tutorials/proc/load_initial_tutorial_completions()
PRIVATE_PROC(TRUE)
set waitfor = FALSE // There's no reason to halt init for this
var/list/ckey_options = list()
var/list/ckeys = list()
for (var/client/client as anything in GLOB.clients)
var/ckey = client?.ckey
if (!ckey)
continue // client shenanigans, never trust
var/index = ckeys.len + 1
ckey_options += ":ckey[index]"
ckeys["ckey[index]"] = ckey
if (ckey_options.len == 0)
return
var/datum/db_query/select_all_query = SSdbcore.NewQuery(
"SELECT ckey, tutorial_key FROM [format_table_name("tutorial_completions")] WHERE ckey in ([ckey_options.Join(", ")])",
ckeys,
)
if (!select_all_query.Execute())
qdel(select_all_query)
return
while (select_all_query.NextRow())
var/ckey = select_all_query.item[1]
var/tutorial_key = select_all_query.item[2]
mark_ckey_completed_tutorial(ckey, tutorial_key)
qdel(select_all_query)
/datum/controller/subsystem/tutorials/proc/on_client_connect(datum/source, client/client)
SIGNAL_HANDLER
var/ckey = client.ckey
if (!ckey)
return
INVOKE_ASYNC(src, PROC_REF(check_completed_tutorials_for_ckey), ckey)
/datum/controller/subsystem/tutorials/proc/check_completed_tutorials_for_ckey(ckey)
if (!SSdbcore.IsConnected())
return
var/datum/db_query/select_tutorials_for_ckey = SSdbcore.NewQuery(
"SELECT tutorial_key FROM [format_table_name("tutorial_completions")] WHERE ckey = :ckey",
list("ckey" = ckey)
)
if (!select_tutorials_for_ckey.Execute())
return
while (select_tutorials_for_ckey.NextRow())
var/tutorial_key = select_tutorials_for_ckey.item[1]
mark_ckey_completed_tutorial(ckey, tutorial_key)
qdel(select_tutorials_for_ckey)
/datum/controller/subsystem/tutorials/proc/mark_ckey_completed_tutorial(ckey, tutorial_key)
var/datum/tutorial_manager/tutorial_manager = tutorial_managers_by_key[tutorial_key]
if (isnull(tutorial_manager))
// Not necessarily a bug.
// Could be an outdated server or a removed tutorial.
return
tutorial_manager.mark_as_completed(ckey)
+1
View File
@@ -127,6 +127,7 @@
/datum/component/drift/proc/loop_death(datum/source)
SIGNAL_HANDLER
drifting_loop = null
UnregisterSignal(parent, COMSIG_MOVABLE_NEWTONIAN_MOVE) // We won't block a component from replacing us anymore
/datum/component/drift/proc/handle_move(datum/source, old_loc)
SIGNAL_HANDLER
+2 -2
View File
@@ -173,7 +173,7 @@
return // blocked wield from item
wielded = TRUE
ADD_TRAIT(parent, TRAIT_WIELDED, REF(src))
RegisterSignal(user, COMSIG_MOB_SWAP_HANDS, PROC_REF(on_swap_hands))
RegisterSignal(user, COMSIG_MOB_SWAPPING_HANDS, PROC_REF(on_swapping_hands))
wield_callback?.Invoke(parent, user)
// update item stats and name
@@ -307,7 +307,7 @@
/**
* on_swap_hands Triggers on swapping hands, blocks swap if the other hand is busy
*/
/datum/component/two_handed/proc/on_swap_hands(mob/user, obj/item/held_item)
/datum/component/two_handed/proc/on_swapping_hands(mob/user, obj/item/held_item)
SIGNAL_HANDLER
if(!held_item)
+56
View File
@@ -0,0 +1,56 @@
/// Apply this element to a movable atom when you want it to block explosions
/// It will mirror the blocking down to that movable's turf, keeping explosion work cheap
/datum/element/blocks_explosives
/datum/element/blocks_explosives/Attach(datum/target)
if(!ismovable(target))
return
. = ..()
ADD_TRAIT(target, TRAIT_BLOCKING_EXPLOSIVES, TRAIT_GENERIC)
var/atom/movable/moving_target = target
RegisterSignal(moving_target, COMSIG_MOVABLE_MOVED, PROC_REF(blocker_moved))
RegisterSignal(moving_target, COMSIG_MOVABLE_EXPLOSION_BLOCK_CHANGED, PROC_REF(blocking_changed))
moving_target.explosive_resistance = moving_target.explosion_block
if(length(moving_target.locs) > 1)
for(var/atom/location as anything in moving_target.locs)
block_loc(location, moving_target.explosion_block)
else if(moving_target.loc)
block_loc(moving_target.loc, moving_target.explosion_block)
/datum/element/blocks_explosives/Detach(datum/source)
. = ..()
REMOVE_TRAIT(source, TRAIT_BLOCKING_EXPLOSIVES, TRAIT_GENERIC)
/// Call this when our blocking well, changes. we'll update our turf(s) with the details
/datum/element/blocks_explosives/proc/blocking_changed(atom/movable/target, old_block, new_block)
if(length(target.locs) > 1)
for(var/atom/location as anything in target.locs)
unblock_loc(location, old_block)
block_loc(location, new_block)
else if(target.loc)
unblock_loc(target.loc, old_block)
block_loc(target.loc, new_block)
/// Applies a block amount to a turf. proc for convenince
/datum/element/blocks_explosives/proc/block_loc(atom/location, block_amount)
location.explosive_resistance += block_amount
/// Removes a block amount from a turf. proc for convenince
/datum/element/blocks_explosives/proc/unblock_loc(atom/location, block_amount)
location.explosive_resistance -= block_amount
/// Essentially just blocking_changed except we remove from the old loc, and add to the new one
/datum/element/blocks_explosives/proc/blocker_moved(atom/movable/target, atom/old_loc, dir, forced, list/old_locs)
if(length(old_locs) > 1)
for(var/atom/location as anything in old_locs)
unblock_loc(location, target.explosion_block)
else if(old_loc)
unblock_loc(old_loc, target.explosion_block)
if(length(target.locs) > 1)
for(var/atom/location as anything in target.locs)
block_loc(location, target.explosion_block)
else if(target.loc)
block_loc(target.loc, target.explosion_block)
+3
View File
@@ -76,6 +76,9 @@
if(available - the_stack.amount < 0)
return FALSE
else if(istype(to_insert, /obj/item/circuitboard/machine) || istype(to_insert, /obj/item/circuitboard/computer))
return TRUE
//check normal insertion of other stock parts
else if(!to_insert.get_part_rating())
return FALSE
+6 -2
View File
@@ -39,8 +39,8 @@
///HUD images that this atom can provide.
var/list/hud_possible
///Value used to increment ex_act() if reactionary_explosions is on
var/explosion_block = 0
///How much this atom resists explosions by, in the end
var/explosive_resistance = 0
/**
* used to store the different colors on an atom
@@ -1879,6 +1879,10 @@
pixel_y = pixel_y + base_pixel_y - .
// Not a valid operation, turfs and movables handle block differently
/atom/proc/set_explosion_block(explosion_block)
return
/**
* Returns true if this atom has gravity for the passed in turf
*
+17 -1
View File
@@ -96,6 +96,10 @@
/// Whether a user will face atoms on entering them with a mouse. Despite being a mob variable, it is here for performances //SKYRAT EDIT ADDITION
var/face_mouse = FALSE //SKYRAT EDIT ADDITION
/// Value used to increment ex_act() if reactionary_explosions is on
/// How much we as a source block explosions by
/// Will not automatically apply to the turf below you, you need to apply /datum/element/block_explosives in conjunction with this
var/explosion_block = 0
/mutable_appearance/emissive_blocker
@@ -107,6 +111,11 @@
/atom/movable/Initialize(mapload)
. = ..()
#ifdef UNIT_TESTS
if(explosion_block && !HAS_TRAIT(src, TRAIT_BLOCKING_EXPLOSIVES))
stack_trace("[type] blocks explosives, but does not have the managing element applied")
#endif
switch(blocks_emissive)
if(EMISSIVE_BLOCK_GENERIC)
var/static/mutable_appearance/emissive_blocker/blocker = new()
@@ -518,7 +527,7 @@
if(set_dir_on_move && dir != direction && !face_mouse) // SKYRAT EDIT CHANGE
setDir(direction)
var/is_multi_tile_object = bound_width > 32 || bound_height > 32
var/is_multi_tile_object = is_multi_tile_object(src)
var/list/old_locs
if(is_multi_tile_object && isturf(loc))
@@ -1135,6 +1144,13 @@
return TRUE
/atom/movable/set_explosion_block(explosion_block)
var/old_block = src.explosion_block
explosive_resistance -= old_block
src.explosion_block = explosion_block
explosive_resistance += explosion_block
SEND_SIGNAL(src, COMSIG_MOVABLE_EXPLOSION_BLOCK_CHANGED, old_block, explosion_block)
/atom/movable/proc/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
set waitfor = FALSE
var/hitpush = TRUE
+2 -2
View File
@@ -55,10 +55,10 @@
/datum/atom_hud/data/diagnostic
/datum/atom_hud/data/diagnostic/basic
hud_icons = list(DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_CIRCUIT_HUD, DIAG_TRACK_HUD, DIAG_CAMERA_HUD, DIAG_AIRLOCK_HUD, DIAG_LAUNCHPAD_HUD)
hud_icons = list(DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_TRACK_HUD, DIAG_CAMERA_HUD, DIAG_AIRLOCK_HUD, DIAG_LAUNCHPAD_HUD)
/datum/atom_hud/data/diagnostic/advanced
hud_icons = list(DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_CIRCUIT_HUD, DIAG_TRACK_HUD, DIAG_CAMERA_HUD, DIAG_AIRLOCK_HUD, DIAG_LAUNCHPAD_HUD, DIAG_PATH_HUD)
hud_icons = list(DIAG_HUD, DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_BOT_HUD, DIAG_TRACK_HUD, DIAG_CAMERA_HUD, DIAG_AIRLOCK_HUD, DIAG_LAUNCHPAD_HUD, DIAG_PATH_HUD)
/datum/atom_hud/data/bot_path
// This hud exists so the bot can see itself, that's all
+67 -21
View File
@@ -101,6 +101,37 @@
amt += req_components[path]
return amt
/**
* install the circuitboard in this frame
* * board - the machine circuitboard to install
* * user - the player
* * by_hand - is the player installing the board by hand or from the RPED. Used to decide how to transfer the board into the frame
*/
/obj/structure/frame/machine/proc/install_board(obj/item/circuitboard/machine/board, mob/user, by_hand)
if(!board.build_path)
to_chat(user, span_warning("This circuitboard seems to be broken."))
return
if(!anchored && board.needs_anchored)
to_chat(user, span_warning("The frame needs to be secured first!"))
return
if(by_hand && !user.transferItemToLoc(board, src))
return
else if(!board.forceMove(src))
return
playsound(src.loc, 'sound/items/deconstruct.ogg', 50, TRUE)
to_chat(user, span_notice("You add the circuit board to the frame."))
circuit = board
icon_state = "box_2"
state = 3
components = list()
//add circuit board as the first component to the list of components
//required for part_replacer to locate it while exchanging parts so it does not early return in /obj/machinery/proc/exchange_parts
components += circuit
req_components = board.req_components.Copy()
update_namelist(board.specific_parts)
return TRUE
/obj/structure/frame/machine/attackby(obj/item/P, mob/living/user, params)
switch(state)
if(1)
@@ -152,27 +183,35 @@
set_anchored(!anchored)
return
if(istype(P, /obj/item/storage/part_replacer) && P.contents.len)
var/obj/item/storage/part_replacer/replacer = P
// map of circuitboard names to the board
var/list/circuit_boards = list()
for(var/obj/item/circuitboard/machine/board in replacer.contents)
circuit_boards[board.name] = board
if(!length(circuit_boards))
return
//if there is only one board directly install it else pick from list
var/obj/item/circuitboard/machine/target_board
if(circuit_boards.len == 1)
for(var/board_name in circuit_boards)
target_board = circuit_boards[board_name]
else
var/option = tgui_input_list(user, "Select Circuitboard To Install"," Available Boards", circuit_boards)
target_board = circuit_boards[option]
if(!target_board)
return
//install board
if(install_board(target_board, user, FALSE))
user.Beam(src, icon_state = "rped_upgrade", time = 5)
replacer.play_rped_sound()
//attack this frame again with the rped so it can install stock parts since its now in state 3
attackby(replacer, user, params)
return
if(istype(P, /obj/item/circuitboard/machine))
var/obj/item/circuitboard/machine/board = P
if(!board.build_path)
to_chat(user, span_warning("This circuitboard seems to be broken."))
return
if(!anchored && board.needs_anchored)
to_chat(user, span_warning("The frame needs to be secured first!"))
return
if(!user.transferItemToLoc(board, src))
return
playsound(src.loc, 'sound/items/deconstruct.ogg', 50, TRUE)
to_chat(user, span_notice("You add the circuit board to the frame."))
circuit = board
icon_state = "box_2"
state = 3
components = list()
//add circuit board as the first component to the list of components
//required for part_replacer to locate it while exchanging parts so it does not early return in /obj/machinery/proc/exchange_parts
components += circuit
req_components = board.req_components.Copy()
update_namelist(board.specific_parts)
var/obj/item/circuitboard/machine/machine_board = P
install_board(machine_board, user, TRUE)
return
else if(istype(P, /obj/item/circuitboard))
@@ -251,7 +290,14 @@
qdel(src)
return
if(istype(P, /obj/item/storage/part_replacer) && P.contents.len && get_req_components_amt())
if(istype(P, /obj/item/storage/part_replacer))
/**
* more efficient return so no if conditions after this are executed.
* Required when the rped is re attacking the frame after installing circuitboard so it returns quickly
*/
if(!P.contents.len || !get_req_components_amt())
return
var/obj/item/storage/part_replacer/replacer = P
var/list/added_components = list()
var/list/part_list = replacer.get_sorted_parts() //parts sorted in order of tier
+17 -4
View File
@@ -55,6 +55,7 @@
acid = 70
/obj/machinery/door/Initialize(mapload)
AddElement(/datum/element/blocks_explosives)
. = ..()
set_init_door_layer()
update_freelook_sight()
@@ -70,7 +71,7 @@
//doors only block while dense though so we have to use the proc
real_explosion_block = explosion_block
explosion_block = EXPLOSION_BLOCK_PROC
update_explosive_block()
RegisterSignal(SSsecurity_level, COMSIG_SECURITY_LEVEL_CHANGED, PROC_REF(check_security_level))
var/static/list/loc_connections = list(
@@ -501,9 +502,6 @@
//if it blows up a wall it should blow up a door
return ..(severity ? min(EXPLODE_DEVASTATE, severity + 1) : EXPLODE_NONE, target)
/obj/machinery/door/GetExplosionBlock()
return density ? real_explosion_block : 0
/obj/machinery/door/power_change()
. = ..()
if(. && !(machine_stat & NOPOWER))
@@ -519,4 +517,19 @@
INVOKE_ASYNC(src, PROC_REF(open))
/obj/machinery/door/set_density(new_value)
. = ..()
update_explosive_block()
/obj/machinery/door/proc/update_explosive_block()
set_explosion_block(real_explosion_block)
// Kinda roundabout, essentially if we're dense, we respect real_explosion_block
// Otherwise, we block nothing
/obj/machinery/door/set_explosion_block(explosion_block)
real_explosion_block = explosion_block
if(density)
return ..()
return ..(0)
#undef DOOR_CLOSE_WAIT
+1
View File
@@ -678,6 +678,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/e
/obj/item/proc/pickup(mob/user)
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ITEM_PICKUP, user)
SEND_SIGNAL(user, COMSIG_LIVING_PICKED_UP_ITEM, src)
item_flags |= IN_INVENTORY
/// called when "found" in pockets and storage items. Returns 1 if the search should end.
+1 -1
View File
@@ -1054,7 +1054,7 @@ GLOBAL_VAR_INIT(icon_holographic_window, init_holographic_window())
if(istype(O))
var/x0 = O.x
var/y0 = O.y
var/contender = cheap_hypotenuse(start.x, start.y, x0, y0)
var/contender = CHEAP_HYPOTENUSE(start.x, start.y, x0, y0)
if(!winner)
winner = O
winning_dist = contender
+1 -11
View File
@@ -45,8 +45,8 @@
name = "retro identification card"
desc = "A card used to provide ID and determine access across the station."
icon_state = "card_grey"
worn_icon_state = "card_retro"
inhand_icon_state = "card-id"
worn_icon_state = "nothing"
lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi'
slot_flags = ITEM_SLOT_ID
@@ -897,7 +897,6 @@
name = "identification card"
desc = "A card used to provide ID and determine access across the station. Has an integrated digital display and advanced microchips."
icon_state = "card_grey"
worn_icon_state = "card_grey"
wildcard_slots = WILDCARD_LIMIT_GREY
flags_1 = UNPAINTABLE_1
@@ -1059,13 +1058,11 @@
name = "rainbow identification card"
desc = "A rainbow card, promoting fun in a 'business proper' sense!"
icon_state = "card_rainbow"
worn_icon_state = "card_rainbow"
/obj/item/card/id/advanced/silver
name = "silver identification card"
desc = "A silver card which shows honour and dedication."
icon_state = "card_silver"
worn_icon_state = "card_silver"
inhand_icon_state = "silver_id"
assigned_icon_state = "assigned_silver"
wildcard_slots = WILDCARD_LIMIT_SILVER
@@ -1084,7 +1081,6 @@
name = "gold identification card"
desc = "A golden card which shows power and might."
icon_state = "card_gold"
worn_icon_state = "card_gold"
inhand_icon_state = "gold_id"
assigned_icon_state = "assigned_gold"
wildcard_slots = WILDCARD_LIMIT_GOLD
@@ -1119,7 +1115,6 @@
name = "\improper CentCom ID"
desc = "An ID straight from Central Command."
icon_state = "card_centcom"
worn_icon_state = "card_centcom"
assigned_icon_state = "assigned_centcom"
registered_name = JOB_CENTCOM
registered_age = null
@@ -1165,7 +1160,6 @@
name = "black identification card"
desc = "This card is telling you one thing and one thing alone. The person holding this card is an utter badass."
icon_state = "card_black"
worn_icon_state = "card_black"
assigned_icon_state = "assigned_syndicate"
wildcard_slots = WILDCARD_LIMIT_GOLD
@@ -1219,7 +1213,6 @@
name = "\improper Debug ID"
desc = "A debug ID card. Has ALL the all access, you really shouldn't have this."
icon_state = "card_centcom"
worn_icon_state = "card_centcom"
assigned_icon_state = "assigned_centcom"
trim = /datum/id_trim/admin
wildcard_slots = WILDCARD_LIMIT_ADMIN
@@ -1236,7 +1229,6 @@
name = "prisoner ID card"
desc = "You are a number, you are not a free man."
icon_state = "card_prisoner"
worn_icon_state = "card_prisoner"
inhand_icon_state = "orange-id"
lefthand_file = 'icons/mob/inhands/equipment/idcards_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi'
@@ -1353,7 +1345,6 @@
registered_name = "Highlander"
desc = "There can be only one!"
icon_state = "card_black"
worn_icon_state = "card_black"
assigned_icon_state = "assigned_syndicate"
trim = /datum/id_trim/highlander
wildcard_slots = WILDCARD_LIMIT_ADMIN
@@ -1664,7 +1655,6 @@
/// A special variant of the classic chameleon ID card which accepts all access.
/obj/item/card/id/advanced/chameleon/black
icon_state = "card_black"
worn_icon_state = "card_black"
assigned_icon_state = "assigned_syndicate"
wildcard_slots = WILDCARD_LIMIT_GOLD
@@ -204,6 +204,7 @@
desc = "A hand-held long-range environmental scanner which reports current gas levels."
name = "long-range gas analyzer"
icon_state = "analyzerranged"
worn_icon_state = "analyzer"
w_class = WEIGHT_CLASS_NORMAL
custom_materials = list(/datum/material/iron = 100, /datum/material/glass = 20, /datum/material/gold = 300, /datum/material/bluespace=200)
grind_results = list(/datum/reagent/mercury = 5, /datum/reagent/iron = 5, /datum/reagent/silicon = 5)
+1 -1
View File
@@ -145,7 +145,7 @@
desc = "An ominous card that contains the location of the station, and when applied to a communications console, \
the ability to long-distance contact the Syndicate fleet."
icon_state = "battlecruisercaller"
worn_icon_state = "battlecruisercaller"
worn_icon_state = "emag"
///whether we have called the battlecruiser
var/used = FALSE
/// The battlecruiser team that the battlecruiser will get added to
+1
View File
@@ -240,6 +240,7 @@
acid = 100
/obj/structure/light_puzzle/Initialize(mapload)
AddElement(/datum/element/blocks_explosives)
. = ..()
var/generated_board = -1
while(generated_board in banned_combinations)
+2
View File
@@ -272,6 +272,7 @@
name = "Crusader's Hood"
desc = "A brownish hood."
icon = 'icons/obj/clothing/head/chaplain.dmi'
worn_icon = 'icons/mob/clothing/head/chaplain.dmi'
icon_state = "crusader"
inhand_icon_state = null
w_class = WEIGHT_CLASS_NORMAL
@@ -300,6 +301,7 @@
name = "Prophet's Hat"
desc = "A religious-looking hat."
icon_state = null
worn_icon = 'icons/mob/clothing/head/helmet.dmi'
inhand_icon_state = null
flags_1 = 0
armor_type = /datum/armor/crusader_prophet
+3 -1
View File
@@ -208,7 +208,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/item/storage/secure/safe, 32)
name = "captain's spare ID safe"
desc = "In case of emergency, do not break glass. All Captains and Acting Captains are provided with codes to access this safe. \
It is made out of the same material as the station's Black Box and is designed to resist all conventional weaponry. \
There appears to be a small amount of surface corrosion. It doesn't look like it could withstand much of an explosion."
There appears to be a small amount of surface corrosion. It doesn't look like it could withstand much of an explosion.\
It remains quite flush against the wall, and there only seems to be enough room to fit something as slim as an ID card."
can_hack_open = FALSE
armor_type = /datum/armor/safe_caps_spare
max_integrity = 300
@@ -227,6 +228,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/item/storage/secure/safe/caps_spare, 32)
/obj/item/storage/secure/safe/caps_spare/Initialize(mapload)
. = ..()
atom_storage.set_holdable(can_hold_list = list(/obj/item/card/id))
lock_code = SSid_access.spare_id_safe_code
lock_set = TRUE
atom_storage.locked = TRUE
@@ -154,6 +154,7 @@
/obj/item/storage/wallet/random
icon_state = "random_wallet" // for mapping purposes
worn_icon_state = "wallet"
/obj/item/storage/wallet/random/Initialize(mapload)
. = ..()
@@ -335,6 +335,7 @@ LINEN BINS
name = "random bedsheet"
desc = "If you're reading this description ingame, something has gone wrong! Honk!"
bedsheet_type = BEDSHEET_ABSTRACT
item_flags = ABSTRACT
var/static/list/bedsheet_list
var/spawn_type = BEDSHEET_SINGLE
@@ -360,6 +361,7 @@ LINEN BINS
name = "random dorms bedsheet"
desc = "If you're reading this description ingame, something has gone wrong! Honk!"
bedsheet_type = BEDSHEET_DOUBLE
item_flags = ABSTRACT
slot_flags = null
/obj/item/bedsheet/dorms/Initialize(mapload)
@@ -541,6 +543,7 @@ LINEN BINS
/obj/item/bedsheet/dorms_double
icon_state = "random_bedsheet"
item_flags = ABSTRACT
bedsheet_type = BEDSHEET_ABSTRACT
/obj/item/bedsheet/dorms_double/Initialize(mapload)
+4 -6
View File
@@ -40,6 +40,7 @@
acid = 100
/obj/structure/window/Initialize(mapload, direct)
AddElement(/datum/element/blocks_explosives)
. = ..()
if(direct)
setDir(direct)
@@ -55,9 +56,9 @@
setDir()
AddElement(/datum/element/can_barricade)
//windows only block while reinforced and fulltile, so we'll use the proc
real_explosion_block = explosion_block
explosion_block = EXPLOSION_BLOCK_PROC
//windows only block while reinforced and fulltile
if(!reinf || !fulltile)
set_explosion_block(0)
flags_1 |= ALLOW_DARK_PAINTS_1
RegisterSignal(src, COMSIG_OBJ_PAINTED, PROC_REF(on_painted))
@@ -424,9 +425,6 @@
return TRUE
/obj/structure/window/GetExplosionBlock()
return reinf && fulltile ? real_explosion_block : 0
/obj/structure/window/spawner/east
dir = EAST
+5
View File
@@ -83,6 +83,9 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
//SKYRAT EDIT END
var/old_rcd_memory = rcd_memory
var/old_always_lit = always_lit
var/old_explosion_throw_details = explosion_throw_details
// We get just the bits of explosive_resistance that aren't the turf
var/old_explosive_resistance = explosive_resistance - get_explosive_block()
var/old_lattice_underneath = lattice_underneath
var/old_bp = blueprint_data
@@ -122,6 +125,8 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
new_turf.blueprint_data = old_bp
new_turf.rcd_memory = old_rcd_memory
new_turf.explosion_throw_details = old_explosion_throw_details
new_turf.explosive_resistance += old_explosive_resistance
lighting_corner_NE = old_lighting_corner_NE
lighting_corner_SE = old_lighting_corner_SE
+3 -3
View File
@@ -20,7 +20,7 @@
name = "wall"
desc = "Effectively impervious to conventional methods of destruction."
icon = 'icons/turf/walls.dmi'
explosion_block = 50
explosive_resistance = 50
/turf/closed/indestructible/rust_heretic_act()
return
@@ -279,7 +279,7 @@ INITIALIZE_IMMEDIATE(/turf/closed/indestructible/splashscreen)
desc = "A seemingly impenetrable wall."
icon = 'icons/turf/walls.dmi'
icon_state = "necro"
explosion_block = 50
explosive_resistance = 50
baseturfs = /turf/closed/indestructible/necropolis
/turf/closed/indestructible/necropolis/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
@@ -307,7 +307,7 @@ INITIALIZE_IMMEDIATE(/turf/closed/indestructible/splashscreen)
smoothing_flags = SMOOTH_BITMASK
smoothing_groups = SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_BOSS_WALLS
canSmoothWith = SMOOTH_GROUP_BOSS_WALLS
explosion_block = 50
explosive_resistance = 50
baseturfs = /turf/closed/indestructible/riveted/boss
/turf/closed/indestructible/riveted/boss/see_through
+8 -8
View File
@@ -15,7 +15,7 @@
base_icon_state = "gold_wall"
sheet_type = /obj/item/stack/sheet/mineral/gold
hardness = 65 //gold is soft
explosion_block = 0 //gold is a soft metal you dingus.
explosive_resistance = 0 //gold is a soft metal you dingus.
smoothing_groups = SMOOTH_GROUP_GOLD_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_GOLD_WALLS
custom_materials = list(/datum/material/gold = 4000)
@@ -42,7 +42,7 @@
sheet_type = /obj/item/stack/sheet/mineral/diamond
hardness = 5 //diamond is very hard
slicing_duration = 200 //diamond wall takes twice as much time to slice
explosion_block = 3
explosive_resistance = 3
smoothing_flags = SMOOTH_BITMASK
smoothing_groups = SMOOTH_GROUP_DIAMOND_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_DIAMOND_WALLS
@@ -72,7 +72,7 @@
base_icon_state = "sandstone_wall"
sheet_type = /obj/item/stack/sheet/mineral/sandstone
hardness = 50 //moh says this is apparently 6-7 on it's scale
explosion_block = 0
explosive_resistance = 0
smoothing_flags = SMOOTH_BITMASK
smoothing_groups = SMOOTH_GROUP_SANDSTONE_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_SANDSTONE_WALLS
@@ -158,7 +158,7 @@
sheet_type = /obj/item/stack/sheet/mineral/wood
hardness = 80
turf_flags = IS_SOLID
explosion_block = 0
explosive_resistance = 0
smoothing_flags = SMOOTH_BITMASK
smoothing_groups = SMOOTH_GROUP_WOOD_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_WOOD_WALLS
@@ -215,7 +215,7 @@
base_icon_state = "snow_wall"
smoothing_flags = SMOOTH_BITMASK
hardness = 80
explosion_block = 0
explosive_resistance = 0
slicing_duration = 30
sheet_type = /obj/item/stack/sheet/mineral/snow
canSmoothWith = null
@@ -236,7 +236,7 @@
sheet_type = /obj/item/stack/sheet/mineral/abductor
hardness = 10
slicing_duration = 200 //alien wall takes twice as much time to slice
explosion_block = 3
explosive_resistance = 3
smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS
smoothing_groups = SMOOTH_GROUP_ABDUCTOR_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS
canSmoothWith = SMOOTH_GROUP_ABDUCTOR_WALLS
@@ -250,7 +250,7 @@
icon = 'icons/turf/walls/shuttle_wall.dmi'
icon_state = "shuttle_wall-0"
base_icon_state = "shuttle_wall"
explosion_block = 3
explosive_resistance = 3
flags_1 = CAN_BE_DIRTY_1
flags_ricochet = RICOCHET_SHINY | RICOCHET_HARD
sheet_type = /obj/item/stack/sheet/mineral/titanium
@@ -314,7 +314,7 @@
icon = 'icons/turf/walls/plastitanium_wall.dmi'
icon_state = "plastitanium_wall-0"
base_icon_state = "plastitanium_wall"
explosion_block = 4
explosive_resistance = 4
sheet_type = /obj/item/stack/sheet/mineral/plastitanium
hardness = 25 //upgrade on titanium
smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS
+2 -2
View File
@@ -12,7 +12,7 @@
sheet_type = /obj/item/stack/sheet/plasteel
sheet_amount = 1
girder_type = /obj/structure/girder/reinforced
explosion_block = 2
explosive_resistance = 2
rad_insulation = RAD_HEAVY_INSULATION
heat_capacity = 312500 //a little over 5 cm thick , 312500 for 1 m by 2.5 m by 0.25 m plasteel wall. also indicates the temperature at wich the wall will melt (currently only able to melt with H/E pipes)
///Dismantled state, related to deconstruction.
@@ -233,7 +233,7 @@
icon = 'icons/turf/walls/plastitanium_wall.dmi'
icon_state = "plastitanium_wall-0"
base_icon_state = "plastitanium_wall"
explosion_block = 20
explosive_resistance = 20
sheet_type = /obj/item/stack/sheet/mineral/plastitanium
hardness = 25 //plastitanium
turf_flags = IS_SOLID
+2 -2
View File
@@ -6,7 +6,7 @@
icon = 'icons/turf/walls/wall.dmi'
icon_state = "wall-0"
base_icon_state = "wall"
explosion_block = 1
explosive_resistance = 1
thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT
heat_capacity = 62500 //a little over 5 cm thick , 62500 for 1 m by 2.5 m by 0.25 m iron wall. also indicates the temperature at wich the wall will melt (currently only able to melt with H/E pipes)
@@ -266,7 +266,7 @@
return null
/turf/closed/wall/acid_act(acidpwr, acid_volume)
if(explosion_block >= 2)
if(get_explosive_block() >= 2)
acidpwr = min(acidpwr, 50) //we reduce the power so strong walls never get melted.
return ..()
+1 -1
View File
@@ -5,7 +5,7 @@
dir = SOUTH
baseturfs = /turf/open/space/transit
flags_1 = NOJAUNT //This line goes out to every wizard that ever managed to escape the den. I'm sorry.
explosion_block = INFINITY
explosive_resistance = INFINITY
/turf/open/space/transit/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir)
. = ..()
+32
View File
@@ -92,6 +92,12 @@ GLOBAL_LIST_EMPTY(station_turfs)
/// Sorry for the mess
var/area/in_contents_of
#endif
/// How much explosive resistance this turf is providing to itself
/// Defaults to -1, interpreted as initial(explosive_resistance)
/// This is an optimization to prevent turfs from needing to set these on init
/// This would either be expensive, or impossible to manage. Let's just avoid it yes?
/// Never directly access this, use get_explosive_block() instead
var/inherent_explosive_resistance = -1
/turf/vv_edit_var(var_name, new_value)
var/static/list/banned_edits = list(NAMEOF_STATIC(src, x), NAMEOF_STATIC(src, y), NAMEOF_STATIC(src, z))
@@ -681,6 +687,26 @@ GLOBAL_LIST_EMPTY(station_turfs)
continue
movable_content.wash(clean_types)
/turf/set_density(new_value)
var/old_density = density
. = ..()
if(old_density == density)
return
if(old_density)
explosive_resistance -= get_explosive_block()
if(density)
explosive_resistance += get_explosive_block()
/// Wrapper around inherent_explosive_resistance
/// We assume this proc is cold, so we can move the "what is our block" into it
/turf/proc/get_explosive_block()
if(inherent_explosive_resistance != -1)
return inherent_explosive_resistance
if(explosive_resistance)
return initial(explosive_resistance)
return 0
/**
* Returns adjacent turfs to this turf that are reachable, in all cardinal directions
*
@@ -710,3 +736,9 @@ GLOBAL_LIST_EMPTY(station_turfs)
/turf/proc/TakeTemperature(temp)
temperature += temp
// I'm sorry, this is the only way that both makes sense and is cheap
/turf/set_explosion_block(explosion_block)
explosive_resistance -= get_explosive_block()
inherent_explosive_resistance = explosion_block
explosive_resistance += get_explosive_block()
@@ -32,6 +32,7 @@
overmind.blobstrain.on_gain()
update_appearance()
AddComponent(/datum/component/stationloving, FALSE, TRUE)
AddElement(/datum/element/blocks_explosives)
return ..()
/obj/structure/blob/special/core/Destroy()
@@ -16,6 +16,10 @@
fire = 90
acid = 90
/obj/structure/blob/shield/Initialize(mapload, owner_overmind)
AddElement(/datum/element/blocks_explosives)
return ..()
/obj/structure/blob/shield/scannerreport()
if(atmosblock)
return "Will prevent the spread of atmospheric changes."
@@ -476,6 +476,7 @@
name = "flesh mass"
icon_state = "lingspacesuit_t"
icon = 'icons/obj/clothing/suits/costume.dmi'
worn_icon = 'icons/mob/clothing/suits/costume.dmi'
desc = "A huge, bulky mass of pressure and temperature-resistant organic tissue, evolved to facilitate space travel."
item_flags = DROPDEL
clothing_flags = STOPSPRESSUREDAMAGE //Not THICKMATERIAL because it's organic tissue, so if somebody tries to inject something into it, it still ends up in your blood. (also balance but muh fluff)
+1 -1
View File
@@ -5,7 +5,7 @@
icon_state = "cordon"
invisibility = INVISIBILITY_ABSTRACT
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
explosion_block = INFINITY
explosive_resistance = INFINITY
rad_insulation = RAD_FULL_INSULATION
opacity = TRUE
density = TRUE
-1
View File
@@ -165,7 +165,6 @@
resistance_flags = NONE
species_exception = list(/datum/species/golem)
// Lemom todo: and here
/obj/item/clothing/suit/hazardvest/worn_overlays(mutable_appearance/standing, isinhands, icon_file)
. = ..()
if(!isinhands)
@@ -33,12 +33,13 @@
throw_range = 3
attack_verb_continuous = list("pompfs")
attack_verb_simple = list("pompf")
greyscale_colors = "#000000" //only here for unit testing. overriden in initialize()
greyscale_config = /datum/greyscale_config/flower_simple
greyscale_config_worn = /datum/greyscale_config/flower_simple_worn
/obj/item/food/grown/rainbow_flower/Initialize(mapload)
. = ..()
if(greyscale_colors)
if(greyscale_colors != initial(greyscale_colors))
return
var/flower_color = rand(1,8)
@@ -26,6 +26,7 @@
var/slicing_duration = 100
/obj/structure/tramwall/Initialize(mapload)
AddElement(/datum/element/blocks_explosives)
. = ..()
var/obj/item/stack/initialized_mineral = new mineral
set_custom_materials(initialized_mineral.mats_per_unit, mineral_amount)
+1
View File
@@ -56,6 +56,7 @@
name = "\improper Nanotrasen space pods"
desc = "Flex your money, AND ignore what everyone else says, all at once!"
icon_state = "spacepods"
worn_icon = 'icons/mob/clothing/ears.dmi'
inhand_icon_state = null
slot_flags = ITEM_SLOT_EARS
strip_delay = 100 //air pods don't fall out
@@ -251,7 +251,7 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999))
icon_state = "hotelwall"
smoothing_groups = SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_HOTEL_WALLS
canSmoothWith = SMOOTH_GROUP_HOTEL_WALLS
explosion_block = INFINITY
explosive_resistance = INFINITY
/turf/open/indestructible/hotelwood
desc = "Stylish dark wood with extra reinforcement. Secured firmly to the floor to prevent tampering."
@@ -271,7 +271,7 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999))
base_icon_state = "bluespace"
baseturfs = /turf/open/space/bluespace
flags_1 = NOJAUNT
explosion_block = INFINITY
explosive_resistance = INFINITY
var/obj/item/hilbertshotel/parentSphere
/turf/open/space/bluespace/Initialize(mapload)
@@ -290,7 +290,7 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999))
/turf/closed/indestructible/hoteldoor
name = "Hotel Door"
icon_state = "hoteldoor"
explosion_block = INFINITY
explosive_resistance = INFINITY
var/obj/item/hilbertshotel/parentSphere
/turf/closed/indestructible/hoteldoor/proc/promptExit(mob/living/user)
+6
View File
@@ -280,9 +280,15 @@
* If the item can be dropped, it will be forceMove()'d to the ground and the turf's Entered() will be called.
*/
/mob/proc/dropItemToGround(obj/item/I, force = FALSE, silent = FALSE, invdrop = TRUE)
if (isnull(I))
return TRUE
SEND_SIGNAL(src, COMSIG_MOB_DROPPING_ITEM)
. = doUnEquip(I, force, drop_location(), FALSE, invdrop = invdrop, silent = silent)
if(!. || !I) //ensure the item exists and that it was dropped properly.
return
if(!(I.item_flags & NO_PIXEL_RANDOM_DROP))
I.pixel_x = I.base_pixel_x + rand(-6, 6)
I.pixel_y = I.base_pixel_y + rand(-6, 6)
@@ -86,7 +86,7 @@
/// Kills the rat and changes its icon state to be splatted (bloody).
/mob/living/basic/mouse/proc/splat()
icon_dead = "mouse_[body_color]_splat"
adjust_health(-maxHealth)
adjust_health(maxHealth)
// On revival, re-add the mouse to the ratcap, or block it if we're at it
/mob/living/basic/mouse/revive(full_heal_flags = NONE, excess_healing = 0, force_grab_ghost = FALSE)
+1 -1
View File
@@ -30,7 +30,7 @@
QDEL_NULL(dna)
GLOB.carbon_list -= src
/mob/living/carbon/swap_hand(held_index)
/mob/living/carbon/perform_hand_swap(held_index)
. = ..()
if(!.)
return
@@ -155,7 +155,7 @@ There are several things that need to be remembered:
var/icon_file
if(!icon_exists(icon_file, RESOLVE_ICON_STATE(worn_item)))
icon_file = 'icons/mob/simple/mob.dmi'
icon_file = 'icons/mob/clothing/id.dmi'
id_overlay = wear_id.build_worn_icon(default_layer = ID_LAYER, default_icon_file = icon_file)
@@ -397,7 +397,7 @@
if(slot_num > 4) // not >3 otherwise cycling with just one item on module 3 wouldn't work
slot_num = 1 //Wrap around.
/mob/living/silicon/robot/swap_hand()
/mob/living/silicon/robot/perform_hand_swap()
cycle_modules()
/mob/living/silicon/robot/can_hold_items(obj/item/I)
@@ -616,7 +616,7 @@
else
mode()
/mob/living/simple_animal/swap_hand(hand_index)
/mob/living/simple_animal/perform_hand_swap(hand_index)
. = ..()
if(!.)
return
+14 -2
View File
@@ -885,11 +885,23 @@
return data
/mob/proc/swap_hand()
/mob/proc/swap_hand(held_index)
SHOULD_NOT_OVERRIDE(TRUE) // Override perform_hand_swap instead
var/obj/item/held_item = get_active_held_item()
if(SEND_SIGNAL(src, COMSIG_MOB_SWAP_HANDS, held_item) & COMPONENT_BLOCK_SWAP)
if(SEND_SIGNAL(src, COMSIG_MOB_SWAPPING_HANDS, held_item) & COMPONENT_BLOCK_SWAP)
to_chat(src, span_warning("Your other hand is too busy holding [held_item]."))
return FALSE
var/result = perform_hand_swap(held_index)
if (result)
SEND_SIGNAL(src, COMSIG_MOB_SWAP_HANDS)
return result
/// Performs the actual ritual of swapping hands, such as setting the held index variables
/mob/proc/perform_hand_swap(held_index)
PROTECTED_PROC(TRUE)
return TRUE
/mob/proc/activate_hand(selhand)
@@ -31,13 +31,13 @@
var/corpse_theme = pick_weight(list(
"Miner" = 66,
"Ashwalker" = 10,
"Golem" = 10,
"Clown" = 10,
"Golem" = 10,
pick(list(
"Shadow",
"Cultist",
"Dame",
"Operative",
"Cultist",
"Shadow",
)) = 4,
))
switch(corpse_theme)
@@ -47,16 +47,16 @@
outfit = /datum/outfit/consumed_ashwalker
if("Clown")
outfit = /datum/outfit/consumed_clown
if("Golem")
outfit = /datum/outfit/consumed_golem
if("Cultist")
outfit = /datum/outfit/consumed_cultist
if("Dame")
outfit = /datum/outfit/consumed_dame
if("Golem")
outfit = /datum/outfit/consumed_golem
if("Operative")
outfit = /datum/outfit/syndicatecommandocorpse
if("Shadow")
outfit = /datum/outfit/consumed_shadowperson
if("Cultist")
outfit = /datum/outfit/consumed_cultist
. = ..()
/datum/outfit/consumed_miner
@@ -70,31 +70,56 @@
if(visualsOnly)
regular_uniform = TRUE //assume human
else
var/new_species_type = pick_weight(list(/datum/species/human = 70, /datum/species/lizard = 26, /datum/species/fly = 2, /datum/species/plasmaman = 2))
var/new_species_type = pick_weight(list(
/datum/species/human = 70,
/datum/species/lizard = 26,
/datum/species/fly = 2,
/datum/species/plasmaman = 2,
))
miner.set_species(new_species_type)
if(new_species_type != /datum/species/plasmaman)
regular_uniform = TRUE
else
uniform = /obj/item/clothing/under/plasmaman
head = /obj/item/clothing/head/helmet/space/plasmaman
belt = /obj/item/tank/internals/plasmaman/belt
head = /obj/item/clothing/head/helmet/space/plasmaman
if(new_species_type == /datum/species/lizard)
shoes = null //digitigrade says no
if(regular_uniform)
uniform = /obj/item/clothing/under/rank/cargo/miner/lavaland
if(prob(4))
belt = pick_weight(list(/obj/item/storage/belt/mining = 2, /obj/item/storage/belt/mining/alt = 2))
belt = pick_weight(list(
/obj/item/storage/belt/mining = 2,
/obj/item/storage/belt/mining/alt = 2,
))
else if(prob(10))
belt = pick_weight(list(/obj/item/pickaxe = 8, /obj/item/pickaxe/mini = 4, /obj/item/pickaxe/silver = 2, /obj/item/pickaxe/diamond = 1))
belt = pick_weight(list(
/obj/item/pickaxe = 8,
/obj/item/pickaxe/mini = 4,
/obj/item/pickaxe/silver = 2,
/obj/item/pickaxe/diamond = 1,
))
else
belt = /obj/item/tank/internals/emergency_oxygen/engi
if(prob(20))
suit = pick_weight(list(/obj/item/clothing/suit/hooded/explorer = 18, /obj/item/clothing/suit/hooded/cloak/goliath = 2))
suit = pick_weight(list(
/obj/item/clothing/suit/hooded/explorer = 18,
/obj/item/clothing/suit/hooded/cloak/goliath = 2,
))
if(prob(30))
r_pocket = pick_weight(list(/obj/item/stack/marker_beacon = 20, /obj/item/stack/spacecash/c1000 = 7, /obj/item/reagent_containers/hypospray/medipen/survival = 2, /obj/item/borg/upgrade/modkit/damage = 1 ))
r_pocket = pick_weight(list(
/obj/item/stack/marker_beacon = 20,
/obj/item/stack/spacecash/c1000 = 7,
/obj/item/reagent_containers/hypospray/medipen/survival = 2,
/obj/item/borg/upgrade/modkit/damage = 1,
))
if(prob(10))
l_pocket = pick_weight(list(/obj/item/stack/spacecash/c1000 = 7, /obj/item/reagent_containers/hypospray/medipen/survival = 2, /obj/item/borg/upgrade/modkit/cooldown = 1 ))
l_pocket = pick_weight(list(
/obj/item/stack/spacecash/c1000 = 7,
/obj/item/reagent_containers/hypospray/medipen/survival = 2,
/obj/item/borg/upgrade/modkit/cooldown = 1,
))
/datum/outfit/consumed_ashwalker
name = "Legion-Consumed Ashwalker"
@@ -106,17 +131,20 @@
if(prob(95))
head = /obj/item/clothing/head/helmet/gladiator
else
head = /obj/item/clothing/head/helmet/skull
suit = /obj/item/clothing/suit/armor/bone
gloves = /obj/item/clothing/gloves/bracer
head = /obj/item/clothing/head/helmet/skull
if(prob(5))
back = pick_weight(list(/obj/item/spear/bonespear = 3, /obj/item/fireaxe/boneaxe = 2))
back = pick_weight(list(
/obj/item/spear/bonespear = 3,
/obj/item/fireaxe/boneaxe = 2,
))
if(prob(10))
belt = /obj/item/storage/belt/mining/primitive
if(prob(30))
r_pocket = /obj/item/knife/combat/bone
if(prob(30))
l_pocket = /obj/item/knife/combat/bone
if(prob(30))
r_pocket = /obj/item/knife/combat/bone
//takes a lot from the clown job, notably NO PDA and different backpack loot + pocket goodies
/datum/outfit/consumed_clown
@@ -140,7 +168,14 @@
if(!visualsOnly)
clown.fully_replace_character_name(clown.name, pick(GLOB.clown_names))
if(prob(70))
var/backpack_loot = pick(list(/obj/item/stamp/clown = 1, /obj/item/reagent_containers/spray/waterflower = 1, /obj/item/food/grown/banana = 1, /obj/item/megaphone/clown = 1, /obj/item/reagent_containers/cup/soda_cans/canned_laughter = 1, /obj/item/pneumatic_cannon/pie))
var/backpack_loot = pick(list(
/obj/item/food/grown/banana = 1,
/obj/item/megaphone/clown = 1,
/obj/item/pneumatic_cannon/pie,
/obj/item/reagent_containers/cup/soda_cans/canned_laughter = 1,
/obj/item/reagent_containers/spray/waterflower = 1,
/obj/item/stamp/clown = 1,
))
if(backpack_loot == /obj/item/pneumatic_cannon/pie)
drop_a_pie_cannon = TRUE
else
@@ -148,7 +183,10 @@
if(prob(30))
backpack_contents += list(/obj/item/stack/sheet/mineral/bananium = pick_weight(list( 1 = 3, 2 = 2, 3 = 1)))
if(prob(10))
l_pocket = pick_weight(list(/obj/item/bikehorn/golden = 3, /obj/item/bikehorn/airhorn = 1))
l_pocket = pick_weight(list(
/obj/item/bikehorn/golden = 3,
/obj/item/bikehorn/airhorn = 1,
))
if(prob(10))
r_pocket = /obj/item/implanter/sad_trombone
@@ -163,26 +201,46 @@
/datum/outfit/consumed_golem/pre_equip(mob/living/carbon/human/golem, visualsOnly = FALSE)
if(!visualsOnly)
golem.set_species(pick(/datum/species/golem/adamantine, /datum/species/golem/plasma, /datum/species/golem/diamond, /datum/species/golem/gold, /datum/species/golem/silver, /datum/species/golem/plasteel, /datum/species/golem/titanium, /datum/species/golem/plastitanium))
golem.set_species(pick(
/datum/species/golem/adamantine,
/datum/species/golem/diamond,
/datum/species/golem/gold,
/datum/species/golem/plasma,
/datum/species/golem/plasteel,
/datum/species/golem/plastitanium,
/datum/species/golem/silver,
/datum/species/golem/titanium,
))
if(prob(30))
glasses = pick_weight(list(/obj/item/clothing/glasses/meson = 2, /obj/item/clothing/glasses/hud/health = 2, /obj/item/clothing/glasses/hud/diagnostic =2, /obj/item/clothing/glasses/science = 2, /obj/item/clothing/glasses/welding = 2, /obj/item/clothing/glasses/night = 1))
glasses = pick_weight(list(
/obj/item/clothing/glasses/hud/diagnostic = 2,
/obj/item/clothing/glasses/hud/health = 2,
/obj/item/clothing/glasses/meson = 2,
/obj/item/clothing/glasses/science = 2,
/obj/item/clothing/glasses/welding = 2,
/obj/item/clothing/glasses/night = 1,
))
if(prob(10) && !visualsOnly) //visualsonly = not a golem = can't put things in the belt slot without a jumpsuit
belt = pick(list(/obj/item/storage/belt/mining/vendor, /obj/item/storage/belt/utility/full))
belt = pick(list(
/obj/item/crowbar/power,
/obj/item/screwdriver/power,
/obj/item/storage/belt/mining/vendor,
/obj/item/storage/belt/utility/full,
/obj/item/weldingtool/experimental,
))
if(prob(50))
neck = /obj/item/bedsheet/rd/royal_cape
if(prob(10) && !visualsOnly) //visualsonly = not a golem = can't put things in the pockets without a jumpsuit
l_pocket = pick(list(/obj/item/crowbar/power, /obj/item/screwdriver/power, /obj/item/weldingtool/electric)) // SKYRAT EDIT CHANGE FROM /obj/item/weldingtool/experimental
//this is so pointlessly gendered but whatever bro i'm here to refactor not judge
/datum/outfit/consumed_dame
name = "Legion-Consumed Dame"
uniform = /obj/item/clothing/under/costume/maid
gloves = /obj/item/clothing/gloves/color/white
shoes = /obj/item/clothing/shoes/laceup
head = /obj/item/clothing/head/helmet/knight
suit = /obj/item/clothing/suit/armor/riot/knight
r_pocket = /obj/item/tank/internals/emergency_oxygen
gloves = /obj/item/clothing/gloves/color/white
head = /obj/item/clothing/head/helmet/knight
mask = /obj/item/clothing/mask/breath
shoes = /obj/item/clothing/shoes/laceup
r_pocket = /obj/item/tank/internals/emergency_oxygen
/datum/outfit/consumed_dame/pre_equip(mob/living/carbon/human/dame, visualsOnly = FALSE)
if(!visualsOnly)
@@ -197,14 +255,15 @@
/datum/outfit/consumed_shadowperson
name = "Legion-Consumed Shadowperson"
r_pocket = /obj/item/reagent_containers/pill/shadowtoxin
accessory = /obj/item/clothing/accessory/medal/plasma/nobel_science
uniform = /obj/item/clothing/under/color/black
shoes = /obj/item/clothing/shoes/sneakers/black
suit = /obj/item/clothing/suit/toggle/labcoat
glasses = /obj/item/clothing/glasses/blindfold
back = /obj/item/tank/internals/oxygen
glasses = /obj/item/clothing/glasses/blindfold
mask = /obj/item/clothing/mask/breath
shoes = /obj/item/clothing/shoes/sneakers/black
r_pocket = /obj/item/reagent_containers/pill/shadowtoxin
accessory = /obj/item/clothing/accessory/medal/plasma/nobel_science
/datum/outfit/consumed_shadowperson/pre_equip(mob/living/carbon/human/shadowperson, visualsOnly = FALSE)
if(visualsOnly)
@@ -217,5 +276,10 @@
suit = /obj/item/clothing/suit/hooded/cultrobes
suit_store = /obj/item/tome
back = /obj/item/storage/backpack/cultpack
backpack_contents = list(
/obj/item/cult_shift = 1,
/obj/item/flashlight/flare/culttorch = 1,
/obj/item/reagent_containers/cup/beaker/unholywater = 1,
/obj/item/stack/sheet/runed_metal = 15,
)
r_pocket = /obj/item/clothing/glasses/hud/health/night/cultblind
backpack_contents = list(/obj/item/reagent_containers/cup/beaker/unholywater = 1, /obj/item/cult_shift = 1, /obj/item/flashlight/flare/culttorch = 1, /obj/item/stack/sheet/runed_metal = 15)
@@ -322,13 +322,7 @@
var/decrement = 1
var/atom/location = loc
if (reactionary)
if(location.density || !isturf(location))
decrement += location.explosion_block
for(var/obj/thing in location)
if (thing == src)
continue
var/the_block = thing.explosion_block
decrement += the_block == EXPLOSION_BLOCK_PROC ? thing.GetExplosionBlock() : the_block
decrement += location.explosive_resistance
range = max(range - decrement + 1, 0) // Already decremented by 1 in the parent. Exists so that if we pass through something with negative block it extends the range.
heavy_ex_range = max(heavy_ex_range - decrement, 0)
+1 -1
View File
@@ -260,7 +260,7 @@
desc = "Used to set the destination of properly wrapped packages."
icon = 'icons/obj/device.dmi'
icon_state = "cargo tagger"
worn_icon_state = "cargo tagger"
worn_icon_state = "cargotagger"
var/currTag = 0 //Destinations are stored in code\globalvars\lists\flavor_misc.dm
var/locked_destination = FALSE //if true, users can't open the destination tag window to prevent changing the tagger's current destination
w_class = WEIGHT_CLASS_TINY
+17 -10
View File
@@ -18,6 +18,12 @@ If you create T5+ please take a pass at mech_fabricator.dm. The parts being good
. = ..()
create_storage(type = /datum/storage/rped)
// check to see if this rped have atleast one circuitboard
/obj/item/storage/part_replacer/proc/has_an_circuitboard()
for(var/obj/item/circuitboard/machine/board in contents)
return TRUE
return FALSE
/obj/item/storage/part_replacer/pre_attack(obj/attacked_object, mob/living/user, params)
if(!ismachinery(attacked_object) && !istype(attacked_object, /obj/structure/frame/machine))
return ..()
@@ -37,13 +43,12 @@ If you create T5+ please take a pass at mech_fabricator.dm. The parts being good
return TRUE
var/obj/structure/frame/machine/attacked_frame = attacked_object
if(!attacked_frame.components)
return ..()
// no point attacking the frame with the rped if the frame doesn't have wiring or it doesn't have components & rped has no circuitboard to offer as an component.
if(attacked_frame.state == 1 || (!attacked_frame.components && !has_an_circuitboard()))
return TRUE
attacked_frame.attackby(src, user)
if(works_from_distance)
user.Beam(attacked_frame, icon_state = "rped_upgrade", time = 5)
attacked_frame.attackby(src, user)
return TRUE
/obj/item/storage/part_replacer/afterattack(obj/attacked_object, mob/living/user, adjacent, params)
@@ -65,13 +70,12 @@ If you create T5+ please take a pass at mech_fabricator.dm. The parts being good
return
var/obj/structure/frame/machine/attacked_frame = attacked_object
if(!attacked_frame.components)
return ..()
// no point attacking the frame with the rped if the frame doesn't have wiring or it doesn't have components & rped has no circuitboard to offer as an component.
if(attacked_frame.state == 1 || (!attacked_frame.components && !has_an_circuitboard()))
return
attacked_frame.attackby(src, user)
if(works_from_distance)
user.Beam(attacked_frame, icon_state = "rped_upgrade", time = 5)
attacked_frame.attackby(src, user)
/obj/item/storage/part_replacer/proc/play_rped_sound()
//Plays the sound for RPED exhanging or installing parts.
@@ -220,6 +224,9 @@ If you create T5+ please take a pass at mech_fabricator.dm. The parts being good
var/list/part_list = list()
//Assemble a list of current parts, then sort them by their rating!
for(var/obj/item/component_part in contents)
//No need to put circuit boards in this list
if(istype(component_part, /obj/item/circuitboard))
continue
part_list += component_part
//Sort the parts. This ensures that higher tier items are applied first.
part_list = sortTim(part_list, GLOBAL_PROC_REF(cmp_rped_sort))
+257
View File
@@ -0,0 +1,257 @@
/// The base for a contextual tutorial.
/// In order to give a tutorial to someone, use `SStutorials.suggest_tutorial(user, /datum/tutorial/subtype)`
/datum/tutorial
/// If set, any account who started playing before this date will not be given this tutorial.
/// Date is in YYYY-MM-DD format.
var/grandfather_date
/// The mob we are giving the tutorial to
VAR_PROTECTED/mob/user
VAR_PRIVATE/atom/movable/screen/tutorial_instruction/instruction_screen
/datum/tutorial/New(mob/user)
src.user = user
RegisterSignal(user, COMSIG_PARENT_QDELETING, PROC_REF(destroy_self))
RegisterSignal(user.client, COMSIG_PARENT_QDELETING, PROC_REF(destroy_self))
/datum/tutorial/Destroy(force, ...)
user.client?.screen -= instruction_screen
QDEL_NULL(instruction_screen)
user = null
return ..()
/// Gets the [`/datum/tutorial_manager`] that owns this tutorial.
/datum/tutorial/proc/manager()
RETURN_TYPE(/datum/tutorial_manager)
return SStutorials.tutorial_managers[type]
/// The actual steps of the tutorial. Is given any excess arguments of suggest_tutorial.
/// Must be overridden.
/datum/tutorial/proc/perform()
SHOULD_CALL_PARENT(FALSE)
CRASH("[type] does not override perform()")
/// Returns TRUE/FALSE if this tutorial should be given.
/// If FALSE, does not mean it won't come back later.
/datum/tutorial/proc/should_perform()
SHOULD_CALL_PARENT(FALSE)
return TRUE
/// Called by the tutorial when the user has successfully completed it.
/// Will mark it as completed in the datbaase and kick off destruction of the tutorial.
/datum/tutorial/proc/complete()
SIGNAL_HANDLER
PROTECTED_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
manager().complete(user)
perform_base_completion_effects()
/// As opposed to `complete()`, this merely hides the tutorial.
/// This should be used when the user doesn't need the tutorial anymore, but didn't
/// actually properly finish it.
/datum/tutorial/proc/dismiss()
SIGNAL_HANDLER
PROTECTED_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
manager().dismiss(user)
perform_base_completion_effects()
#define INSTRUCTION_SCREEN_DELAY (1 SECONDS)
/datum/tutorial/proc/perform_base_completion_effects()
SHOULD_NOT_OVERRIDE(TRUE)
var/delay = perform_completion_effects_with_delay()
if (!isnull(instruction_screen))
animate(instruction_screen, time = INSTRUCTION_SCREEN_DELAY, alpha = 0, easing = SINE_EASING)
delay += INSTRUCTION_SCREEN_DELAY
QDEL_IN(src, delay)
/// Called when the tutorial is being hidden, but before it is deleted.
/// You should unregister signals and fade out any of your creations in here.
/// Returns how long extra to delay the deletion.
/datum/tutorial/proc/perform_completion_effects_with_delay()
SHOULD_CALL_PARENT(FALSE)
PROTECTED_PROC(TRUE)
return 0
#undef INSTRUCTION_SCREEN_DELAY
/datum/tutorial/proc/destroy_self()
SIGNAL_HANDLER
PRIVATE_PROC(TRUE)
SHOULD_NOT_OVERRIDE(TRUE)
manager().dismiss(user)
qdel(src)
/// Shows a large piece of text on the user's screen with the given message.
/// If a message already exists, will fade it out and replace it.
/datum/tutorial/proc/show_instruction(message)
PROTECTED_PROC(TRUE)
if (isnull(instruction_screen))
instruction_screen = new(null, message, user.client)
user.client?.screen += instruction_screen
else
instruction_screen.change_message(message)
/// Given a keybind and a message, will replace %KEY% in `message` with the first keybind they have.
/// As a fallback, will return the third parameter, `message_without_keybinds`, if none are set.
/datum/tutorial/proc/keybinding_message(datum/keybinding/keybinding_type, message, message_without_keybinds)
PROTECTED_PROC(TRUE)
var/list/keybinds = user.client?.prefs.key_bindings[initial(keybinding_type.name)]
return keybinds?.len > 0 ? replacetext(message, "%KEY%", "<b>[keybinds[1]]</b>") : message_without_keybinds
/// Creates a UI element with the given `icon_state`, starts it at `initial_screen_loc`, and animates it to `target_screen_loc`.
/// Waits `animate_start_time` before moving.
/datum/tutorial/proc/animate_ui_element(icon_state, initial_screen_loc, target_screen_loc, animate_start_time)
PROTECTED_PROC(TRUE)
var/atom/movable/screen/preview = new
preview.icon = ui_style2icon(user.client?.prefs.read_preference(/datum/preference/choiced/ui_style) || GLOB.available_ui_styles[1])
preview.icon_state = icon_state
preview.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
preview.screen_loc = "1,1"
var/view = user.client?.view
var/list/origin_offsets = screen_loc_to_offset(initial_screen_loc, view)
// A little offset to the right
var/matrix/origin_transform = TRANSLATE_MATRIX(origin_offsets[1] - world.icon_size * 0.5, origin_offsets[2] - world.icon_size * 1.5)
var/list/target_offsets = screen_loc_to_offset(target_screen_loc, view)
// `- world.icon_Size * 0.5` to patch over a likely bug in screen_loc_to_offset with CENTER, needs more looking at
var/matrix/animate_to_transform = TRANSLATE_MATRIX(target_offsets[1] - world.icon_size * 1.5, target_offsets[2] - world.icon_size)
preview.transform = origin_transform
preview.alpha = 0
animate(preview, time = animate_start_time, alpha = 255, easing = CUBIC_EASING)
animate(1.4 SECONDS)
animate(transform = animate_to_transform, time = 2 SECONDS, easing = SINE_EASING | EASE_IN)
animate(alpha = 0, time = 2.4 SECONDS, easing = CUBIC_EASING | EASE_IN, flags = ANIMATION_PARALLEL)
user.client?.screen += preview
return preview
/// A singleton that manages when to create tutorials of a specific tutorial type.
/datum/tutorial_manager
VAR_PRIVATE/datum/tutorial/tutorial_type
/// ckeys that we know have finished the tutorial
VAR_PRIVATE/list/finished_ckeys = list()
/// ckeys that have performed the tutorial, but have not completed it.
/// Doesn't mean that they can still see the tutorial, might have meant the tutorial was dismissed
/// without being completed, such as during a log out.
VAR_PRIVATE/list/performing_ckeys = list()
/datum/tutorial_manager/New(tutorial_type)
ASSERT(ispath(tutorial_type, /datum/tutorial))
src.tutorial_type = tutorial_type
/datum/tutorial_manager/Destroy(force, ...)
if (!force)
stack_trace("Something is trying to destroy [type], which is a singleton")
return QDEL_HINT_LETMELIVE
return ..()
/// Checks if we should perform the tutorial for the given user, and performs if so.
/// Use `SStutorials.suggest_tutorial` instead of calling this directly.
/datum/tutorial_manager/proc/try_perform(mob/user, list/arguments)
var/datum/tutorial/tutorial = new tutorial_type(user)
if (!tutorial.should_perform(user))
qdel(tutorial)
return
performing_ckeys[user.ckey] = TRUE
tutorial.perform(arglist(arguments))
/// Checks if the user should be given this tutorial
/datum/tutorial_manager/proc/should_run(mob/user)
var/ckey = user.ckey
if (isnull(ckey))
return FALSE
if (ckey in finished_ckeys)
return FALSE
if (ckey in performing_ckeys)
return FALSE
if (!SSdbcore.IsConnected())
return CONFIG_GET(flag/give_tutorials_without_db)
var/player_join_date = user.client?.player_join_date
if (isnull(player_join_date))
return FALSE
// This works because ISO-8601 is cool
var/grandfather_date = initial(tutorial_type.grandfather_date)
if (!isnull(grandfather_date) && player_join_date < grandfather_date)
return FALSE
return TRUE
/// Marks the tutorial as completed.
/// Call `/datum/tutorial/proc/complete()` instead.
/datum/tutorial_manager/proc/complete(mob/user)
set waitfor = FALSE
ASSERT(!isnull(user.ckey))
finished_ckeys[user.ckey] = TRUE
performing_ckeys -= user.ckey
SSblackbox.record_feedback("tally", "tutorial_completed", 1, "[tutorial_type]")
log_game("[key_name(user)] completed the [tutorial_type] tutorial.")
if (SSdbcore.IsConnected())
INVOKE_ASYNC(src, PROC_REF(log_completion_to_database), user.ckey)
/datum/tutorial_manager/proc/log_completion_to_database(ckey)
PRIVATE_PROC(TRUE)
var/datum/db_query/insert_tutorial_query = SSdbcore.NewQuery(
"INSERT INTO [format_table_name("tutorial_completions")] (ckey, tutorial_key) VALUES (:ckey, :tutorial_key) ON DUPLICATE KEY UPDATE tutorial_key = tutorial_key",
list(
"ckey" = ckey,
"tutorial_key" = get_key(),
)
)
insert_tutorial_query.warn_execute()
qdel(insert_tutorial_query)
/// Dismisses the tutorial, not marking it as completed in the database.
/// Call `/datum/tutorial/proc/dismiss()` instead.
/datum/tutorial_manager/proc/dismiss(mob/user)
performing_ckeys -= user.ckey
/// Given a ckey, will mark them as being completed without affecting the database.
/// Call `/datum/tutorial/proc/complete()` instead.
/datum/tutorial_manager/proc/mark_as_completed(ckey)
finished_ckeys[ckey] = TRUE
performing_ckeys -= ckey
/// Gives the key that will be saved in the database.
/// Must be 64 characters or less.
/datum/tutorial_manager/proc/get_key()
SHOULD_BE_PURE(TRUE)
return copytext("[tutorial_type]", length("[/datum/tutorial]") + 2)
@@ -0,0 +1,53 @@
/atom/movable/screen/tutorial_instruction
icon = 'icons/effects/alphacolors.dmi'
icon_state = "white"
color = COLOR_NEARLY_ALL_BLACK
alpha = 0
screen_loc = "TOP-2,CENTER"
layer = TUTORIAL_INSTRUCTIONS_LAYER
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
var/client/client
var/atom/movable/screen/tutorial_instruction_text/instruction_text
/atom/movable/screen/tutorial_instruction/Initialize(mapload, message, client/client)
. = ..()
transform = transform.Scale(36, 2.5)
src.client = client
animate(src, alpha = 245, time = 0.8 SECONDS, easing = SINE_EASING)
instruction_text = new(src, message, client)
vis_contents += instruction_text
/atom/movable/screen/tutorial_instruction/Destroy()
client = null
QDEL_NULL(instruction_text)
return ..()
/atom/movable/screen/tutorial_instruction/proc/change_message(message)
instruction_text.change_message(message)
/atom/movable/screen/tutorial_instruction_text
maptext_height = 480
maptext_y = -2
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
layer = TUTORIAL_INSTRUCTIONS_LAYER
/atom/movable/screen/tutorial_instruction_text/Initialize(mapload, message, client/client)
. = ..()
var/view = client?.view_size.getView()
maptext_width = view ? view_to_pixels(view)[1] : 480
pixel_x = (maptext_width - world.icon_size) * -0.5
change_message(message)
/atom/movable/screen/tutorial_instruction_text/proc/change_message(message)
// We don't use MAPTEXT macro here because it doesn't handle big text
message = "<span style='font-family: \"VCR OSD Mono\"; font-size: 22px; text-align: center'>[message]</span>"
animate(src, alpha = 0, time = (maptext ? 0.5 SECONDS : 0), easing = SINE_EASING)
animate(alpha = 255, time = 0.5 SECONDS, maptext = message)
+109
View File
@@ -0,0 +1,109 @@
#define TIME_TO_START_MOVING_DROP_ICON (0.5 SECONDS)
#define STAGE_DROP_ITEM "STAGE_DROP_ITEM"
#define STAGE_PICK_SOMETHING_UP "STAGE_PICK_SOMETHING_UP"
/// Tutorial for showing how to drop items.
/// Fired when clicking on an item with another item with a filled inactive hand.
/datum/tutorial/drop
grandfather_date = "2023-01-07"
var/stage = STAGE_DROP_ITEM
var/atom/movable/screen/drop_preview
var/obj/last_held_item
/datum/tutorial/drop/Destroy(force, ...)
last_held_item = null
user.client?.screen -= drop_preview
QDEL_NULL(drop_preview)
return ..()
/datum/tutorial/drop/perform(list/params)
create_drop_preview(params[SCREEN_LOC])
addtimer(CALLBACK(src, PROC_REF(show_instructions)), TIME_TO_START_MOVING_DROP_ICON)
RegisterSignal(user, COMSIG_MOB_DROPPING_ITEM, PROC_REF(on_dropped_item))
RegisterSignal(user, COMSIG_MOB_SWAP_HANDS, PROC_REF(on_swap_hands))
RegisterSignal(user, COMSIG_LIVING_PICKED_UP_ITEM, PROC_REF(on_pick_up_item))
update_held_item()
/datum/tutorial/drop/perform_completion_effects_with_delay()
UnregisterSignal(user, list(COMSIG_MOB_DROPPING_ITEM, COMSIG_MOB_SWAP_HANDS, COMSIG_LIVING_PICKED_UP_ITEM))
if (!isnull(last_held_item))
UnregisterSignal(last_held_item, COMSIG_MOVABLE_MOVED)
return 0
/datum/tutorial/drop/proc/create_drop_preview(initial_screen_loc)
drop_preview = animate_ui_element(
"act_drop",
initial_screen_loc,
ui_drop_throw,
TIME_TO_START_MOVING_DROP_ICON,
)
/datum/tutorial/drop/proc/show_instructions()
if (QDELETED(src))
return
switch (stage)
if (STAGE_DROP_ITEM)
show_instruction(keybinding_message(
/datum/keybinding/mob/drop_item,
"Press '%KEY%' to drop your current item",
"Click '<b>DROP</b>' to drop your current item",
))
if (STAGE_PICK_SOMETHING_UP)
show_instruction("Pick something up!")
/datum/tutorial/drop/proc/on_swap_hands()
SIGNAL_HANDLER
if (isnull(user.get_active_held_item()))
if (stage != STAGE_PICK_SOMETHING_UP)
stage = STAGE_PICK_SOMETHING_UP
show_instructions()
else if (stage == STAGE_PICK_SOMETHING_UP)
stage = STAGE_DROP_ITEM
show_instructions()
update_held_item()
/datum/tutorial/drop/proc/on_dropped_item()
SIGNAL_HANDLER
stage = STAGE_PICK_SOMETHING_UP
show_instructions()
/datum/tutorial/drop/proc/on_pick_up_item()
SIGNAL_HANDLER
if (stage != STAGE_PICK_SOMETHING_UP)
dismiss()
return
complete()
// Exists so that if we, say, place the item on a table, we don't count that as completion
/datum/tutorial/drop/proc/update_held_item()
if (!isnull(last_held_item))
UnregisterSignal(last_held_item, COMSIG_MOVABLE_MOVED)
last_held_item = user.get_active_held_item()
if (isnull(last_held_item))
return
RegisterSignal(last_held_item, COMSIG_MOVABLE_MOVED, PROC_REF(on_held_item_moved))
/datum/tutorial/drop/proc/on_held_item_moved()
SIGNAL_HANDLER
if (stage == STAGE_PICK_SOMETHING_UP)
return
dismiss()
#undef STAGE_DROP_ITEM
#undef STAGE_PICK_SOMETHING_UP
#undef TIME_TO_START_MOVING_DROP_ICON
@@ -0,0 +1,86 @@
#define TIME_TO_START_MOVING_HAND_ICON (0.5 SECONDS)
#define STAGE_SHOULD_SWAP_HAND "STAGE_SHOULD_SWAP_HAND"
#define STAGE_PICK_UP_ITEM "STAGE_PICK_UP_ITEM"
/// Tutorial for showing how to switch hands.
/// Fired when clicking on an item with another item with an empty inactive hand.
/datum/tutorial/switch_hands
grandfather_date = "2023-01-07"
var/stage = STAGE_SHOULD_SWAP_HAND
var/atom/movable/screen/hand_preview
// So that they don't just drop the item
var/hand_to_watch
/datum/tutorial/switch_hands/New(mob/user)
. = ..()
hand_to_watch = (user.active_hand_index % user.held_items.len) + 1
/datum/tutorial/switch_hands/Destroy(force, ...)
user.client?.screen -= hand_preview
QDEL_NULL(hand_preview)
return ..()
/datum/tutorial/switch_hands/perform(list/params)
create_hand_preview(params[SCREEN_LOC])
addtimer(CALLBACK(src, PROC_REF(show_instructions)), TIME_TO_START_MOVING_HAND_ICON)
RegisterSignal(user, COMSIG_MOB_SWAP_HANDS, PROC_REF(on_swap_hands))
RegisterSignal(user, COMSIG_LIVING_PICKED_UP_ITEM, PROC_REF(on_pick_up_item))
/datum/tutorial/switch_hands/perform_completion_effects_with_delay()
UnregisterSignal(user, list(COMSIG_MOB_SWAP_HANDS, COMSIG_LIVING_PICKED_UP_ITEM))
return 0
/datum/tutorial/switch_hands/proc/create_hand_preview(initial_screen_loc)
hand_preview = animate_ui_element(
"hand_[hand_to_watch % 2 == 0 ? "r" : "l"]",
initial_screen_loc,
ui_hand_position(hand_to_watch),
TIME_TO_START_MOVING_HAND_ICON,
)
/datum/tutorial/switch_hands/proc/show_instructions()
if (QDELETED(src))
return
switch (stage)
if (STAGE_SHOULD_SWAP_HAND)
var/hand_name = hand_to_watch % 2 == 0 ? "right" : "left"
show_instruction(keybinding_message(
/datum/keybinding/mob/swap_hands,
"Press '%KEY%' to use your [hand_name] hand",
"Click '<b>SWAP</b>' to use your [hand_name] hand",
))
if (STAGE_PICK_UP_ITEM)
show_instruction("Pick something up!")
/datum/tutorial/switch_hands/proc/on_swap_hands()
SIGNAL_HANDLER
if (isnull(user.get_active_held_item()))
stage = STAGE_PICK_UP_ITEM
show_instructions()
else if (isnull(user.get_inactive_held_item()))
stage = STAGE_SHOULD_SWAP_HAND
show_instructions()
else
// You somehow got an item in both hands during the tutorial without switching hands.
// Good job I guess?
complete()
/datum/tutorial/switch_hands/proc/on_pick_up_item()
SIGNAL_HANDLER
if (user.active_hand_index != hand_to_watch)
return
complete()
#undef STAGE_PICK_UP_ITEM
#undef STAGE_SHOULD_SWAP_HAND
#undef TIME_TO_START_MOVING_HAND_ICON
+2
View File
@@ -208,10 +208,12 @@
#include "tgui_create_message.dm"
#include "timer_sanity.dm"
#include "traitor.dm"
#include "tutorial_sanity.dm"
#include "unit_test.dm"
#include "verify_config_tags.dm"
#include "verify_emoji_names.dm"
#include "wizard_loadout.dm"
#include "worn_icons.dm"
#ifdef REFERENCE_TRACKING_DEBUG //Don't try and parse this file if ref tracking isn't turned on. IE: don't parse ref tracking please mr linter
#include "find_reference_sanity.dm"
#endif
@@ -0,0 +1,19 @@
/// Verifies that every tutorial has properly set variables
/datum/unit_test/tutorial_sanity
/datum/unit_test/tutorial_sanity/Run()
var/regex/regex_valid_date = regex(@"\d{4}-\d{2}-\d{2}")
var/list/keys = list()
for (var/datum/tutorial/tutorial_type as anything in SStutorials.tutorial_managers)
var/datum/tutorial_manager/tutorial_manager = SStutorials.tutorial_managers[tutorial_type]
var/grandfather_date = initial(tutorial_type.grandfather_date)
if (!isnull(grandfather_date))
TEST_ASSERT(regex_valid_date.Find(grandfather_date), "[tutorial_type] has an invalid grandfather_date ([grandfather_date])")
var/key = tutorial_manager.get_key()
TEST_ASSERT(!(key in keys), "[key] shows up twice")
TEST_ASSERT(length(key) < 64, "[key] is more than 64 characters, it won't fit in the SQL table.")
TEST_ASSERT_EQUAL(SStutorials.tutorial_managers.len, length(subtypesof(/datum/tutorial)), "Expected tutorial_managers to have one of every tutorial")
+117
View File
@@ -0,0 +1,117 @@
/// Makes sure suit slot items aren't using CS:S fallbacks.
/datum/unit_test/worn_icons
var/static/list/possible_icon_states = list()
/// additional_icon_location is for downstream modularity support for finding missing sprites in additonal DMI file locations.
/// Make sure this location is also present in tools/deploy.sh
/// If you need additional paths ontop of this second one, you can add another generate_possible_icon_states_list("your/folder/path/") below the if(additional_icon_location) block in Run(), and make sure to add that path to tools/deploy.sh as well.
var/additional_icon_location = null
/datum/unit_test/worn_icons/proc/generate_possible_icon_states_list(directory_path)
if(!directory_path)
directory_path = "icons/mob/clothing/"
for(var/file_path in flist(directory_path))
if(findtext(file_path, ".dmi"))
for(var/sprite_icon in icon_states("[directory_path][file_path]", 1)) //2nd arg = 1 enables 64x64+ icon support, otherwise you'll end up with "sword0_1" instead of "sword"
possible_icon_states[sprite_icon] += list("[directory_path][file_path]")
else
possible_icon_states += generate_possible_icon_states_list("[directory_path][file_path]")
/datum/unit_test/worn_icons/Run()
generate_possible_icon_states_list()
if(additional_icon_location)
generate_possible_icon_states_list(additional_icon_location)
var/list/already_warned_icons = list()
for(var/obj/item/item_path as anything in (subtypesof(/obj/item) - typesof(/obj/item/mod)))
var/cached_slot_flags = initial(item_path.slot_flags)
if(!cached_slot_flags || (cached_slot_flags & ITEM_SLOT_LPOCKET) || (cached_slot_flags & ITEM_SLOT_RPOCKET) || initial(item_path.item_flags) & ABSTRACT)
continue
if(initial(item_path.greyscale_colors) && initial(item_path.greyscale_config_worn)) //GAGS has its own unit test.
continue
var/worn_icon = initial(item_path.worn_icon) //override icon file. where our sprite is contained if set. (ie modularity stuff)
var/worn_icon_state = initial(item_path.worn_icon_state) //overrides icon_state.
var/icon_state = worn_icon_state || initial(item_path.icon_state) //icon_state. what sprite name we are looking for.
if(isnull(icon_state))
continue //no sprite for the item.
if(icon_state in already_warned_icons)
continue
var/match_message
if(icon_state in possible_icon_states)
for(var/file_place in possible_icon_states[icon_state])
match_message += (match_message ? " & '[file_place]'" : " - Matching sprite found in: '[file_place]'")
if(worn_icon) //easiest to check since we override everything. this automatically includes downstream support.
if(!(icon_state in icon_states(worn_icon, 1)))
TEST_FAIL("[item_path] using invalid [worn_icon_state ? "worn_icon_state" : "icon_state"], \"[icon_state]\" in worn_icon override file, '[worn_icon]'[match_message]")
continue
var/icon_file //checks against all the default icon locations if one isn't defined.
var/fail_reasons
var/spacer
if(cached_slot_flags & ITEM_SLOT_BACK)
icon_file = 'icons/mob/clothing/back.dmi'
if(!(icon_state in icon_states(icon_file, 1)))
already_warned_icons += icon_state
fail_reasons += "[item_path] using invalid [worn_icon_state ? "worn_icon_state" : "icon_state"], \"[icon_state]\" in '[icon_file]'[match_message]"
spacer = "\n\t"
if(cached_slot_flags & ITEM_SLOT_ID)
icon_file = 'icons/mob/clothing/id.dmi'
if(!(icon_state in icon_states(icon_file, 1)))
already_warned_icons += icon_state
fail_reasons += "[spacer][item_path] using invalid [worn_icon_state ? "worn_icon_state" : "icon_state"], \"[icon_state]\" in '[icon_file]'[match_message]"
spacer = "\n\t"
if(cached_slot_flags & ITEM_SLOT_GLOVES)
icon_file = 'icons/mob/clothing/hands.dmi'
if(!(icon_state in icon_states(icon_file, 1)))
already_warned_icons += icon_state
fail_reasons += "[spacer][item_path] using invalid [worn_icon_state ? "worn_icon_state" : "icon_state"], \"[icon_state]\" in '[icon_file]'[match_message]"
spacer = "\n\t"
if(cached_slot_flags & ITEM_SLOT_EYES)
icon_file = 'icons/mob/clothing/eyes.dmi'
if(!(icon_state in icon_states(icon_file, 1)))
already_warned_icons += icon_state
fail_reasons += "[spacer][item_path] using invalid [worn_icon_state ? "worn_icon_state" : "icon_state"], \"[icon_state]\" in '[icon_file]'[match_message]"
spacer = "\n\t"
if(cached_slot_flags & ITEM_SLOT_EARS)
icon_file = 'icons/mob/clothing/ears.dmi'
if(!(icon_state in icon_states(icon_file, 1)))
already_warned_icons += icon_state
fail_reasons += "[spacer][item_path] using invalid [worn_icon_state ? "worn_icon_state" : "icon_state"], \"[icon_state]\" in '[icon_file]'[match_message]"
spacer = "\n\t"
if(cached_slot_flags & ITEM_SLOT_NECK)
icon_file = 'icons/mob/clothing/neck.dmi'
if(!(icon_state in icon_states(icon_file, 1)))
already_warned_icons += icon_state
fail_reasons += "[spacer][item_path] using invalid [worn_icon_state ? "worn_icon_state" : "icon_state"], \"[icon_state]\" in '[icon_file]'[match_message]"
spacer = "\n\t"
if(cached_slot_flags & ITEM_SLOT_MASK)
icon_file = 'icons/mob/clothing/mask.dmi'
if(!(icon_state in icon_states(icon_file, 1)))
already_warned_icons += icon_state
fail_reasons += "[spacer][item_path] using invalid [worn_icon_state ? "worn_icon_state" : "icon_state"], \"[icon_state]\" in '[icon_file]'[match_message]"
spacer = "\n\t"
if(cached_slot_flags & ITEM_SLOT_BELT)
icon_file = 'icons/mob/clothing/belt.dmi'
if(!(icon_state in icon_states(icon_file, 1)))
already_warned_icons += icon_state
fail_reasons += "[spacer][item_path] using invalid [worn_icon_state ? "worn_icon_state" : "icon_state"], \"[icon_state]\" in '[icon_file]'[match_message]"
spacer = "\n\t"
if(fail_reasons)
TEST_FAIL(fail_reasons)
+4
View File
@@ -648,3 +648,7 @@ PR_ANNOUNCEMENTS_PER_ROUND 5
## Uncomment to block granting profiling privileges to users with R_DEBUG, for performance purposes
#FORBID_ADMIN_PROFILING
## Comment to disable sending a toast notification on the host server when initializations complete.
## Even if this is enabled, a notification will only be sent if there are no clients connected.
TOAST_NOTIFICATION_ON_INIT
+5
View File
@@ -556,3 +556,8 @@ MAXFINE 2000
## Comment if you wish to enable title music playing at the lobby screen. This flag is disabled by default to facilitate better code testing on local machines.
## Do keep in mind that this flag will not affect individual player's preferences: if they opt-out on your server, it will never play for them.
DISALLOW_TITLE_MUSIC
## If enabled, then when the database is disabled, all players will get tutorials.
## This is primarily useful for developing tutorials. If you have a proper DB setup, you
## don't need (or want) this.
#GIVE_TUTORIALS_WITHOUT_DB
@@ -1,5 +0,0 @@
author: "SkyratBot"
delete-after: True
changes:
- rscadd: "you can flip eyepatches"
- bugfix: "blindfolds now take into consideration both eye colors for their color"
@@ -1,4 +0,0 @@
author: "Big chungus wholesome 100"
delete-after: True
changes:
- bugfix: "Amputated and reattached limbs now get wounded based on their own biology, not the current owner's biology."

Some files were not shown because too many files have changed in this diff Show More