Merge remote-tracking branch 'Citadel-Station-13/master' into Citadel-Station-13/custom_roundstart_items
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
#define WACKY "Wacky"
|
||||
#define MUT_MUTE "Mute"
|
||||
#define SMILE "Smile"
|
||||
#define STONER "Stoner"
|
||||
#define UNINTELLIGABLE "Unintelligable"
|
||||
#define SWEDISH "Swedish"
|
||||
#define CHAV "Chav"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#define MC_TICK_CHECK ( ( world.tick_usage > GLOB.CURRENT_TICKLIMIT || src.state != SS_RUNNING ) ? pause() : 0 )
|
||||
#define MC_TICK_CHECK ( ( world.tick_usage > Master.current_ticklimit || src.state != SS_RUNNING ) ? pause() : 0 )
|
||||
|
||||
#define MC_SPLIT_TICK_INIT(phase_count) var/original_tick_limit = GLOB.CURRENT_TICKLIMIT; var/split_tick_phases = ##phase_count
|
||||
#define MC_SPLIT_TICK_INIT(phase_count) var/original_tick_limit = Master.current_ticklimit; var/split_tick_phases = ##phase_count
|
||||
#define MC_SPLIT_TICK \
|
||||
if(split_tick_phases > 1){\
|
||||
GLOB.CURRENT_TICKLIMIT = ((original_tick_limit - world.tick_usage) / split_tick_phases) + world.tick_usage;\
|
||||
Master.current_ticklimit = ((original_tick_limit - world.tick_usage) / split_tick_phases) + world.tick_usage;\
|
||||
--split_tick_phases;\
|
||||
} else {\
|
||||
GLOB.CURRENT_TICKLIMIT = original_tick_limit;\
|
||||
Master.current_ticklimit = original_tick_limit;\
|
||||
}
|
||||
|
||||
// Used to smooth out costs to try and avoid oscillation.
|
||||
|
||||
@@ -5,4 +5,19 @@
|
||||
#define ANTAG_DATUM_DEVIL /datum/antagonist/devil
|
||||
#define ANTAG_DATUM_NINJA /datum/antagonist/ninja
|
||||
#define ANTAG_DATUM_NINJA_FRIENDLY /datum/antagonist/ninja/friendly
|
||||
#define ANTAG_DATUM_NINJA_RANDOM /datum/antagonist/ninja/randomAllegiance/
|
||||
#define ANTAG_DATUM_NINJA_RANDOM /datum/antagonist/ninja/randomAllegiance/
|
||||
#define ANTAG_DATUM_TRAITOR /datum/antagonist/traitor
|
||||
#define ANTAG_DATUM_TRAITOR_CUSTOM /datum/antagonist/traitor/custom
|
||||
#define ANTAG_DATUM_IAA /datum/antagonist/traitor/internal_affairs
|
||||
#define ANTAG_DATUM_TRAITOR /datum/antagonist/traitor
|
||||
#define ANTAG_DATUM_TRAITOR_CUSTOM /datum/antagonist/traitor/custom
|
||||
#define ANTAG_DATUM_TRAITOR_HUMAN /datum/antagonist/traitor/human
|
||||
#define ANTAG_DATUM_TRAITOR_HUMAN_CUSTOM /datum/antagonist/traitor/human/custom
|
||||
#define ANTAG_DATUM_TRAITOR_AI /datum/antagonist/traitor/AI
|
||||
#define ANTAG_DATUM_TRAITOR_AI_CUSTOM /datum/antagonist/traitor/AI/custom
|
||||
#define ANTAG_DATUM_IAA /datum/antagonist/traitor/internal_affairs
|
||||
#define ANTAG_DATUM_IAA_CUSTOM /datum/antagonist/traitor/internal_affairs/custom
|
||||
#define ANTAG_DATUM_IAA_HUMAN /datum/antagonist/traitor/human/internal_affairs
|
||||
#define ANTAG_DATUM_IAA_HUMAN_CUSTOM /datum/antagonist/traitor/human/internal_affairs/custom
|
||||
#define ANTAG_DATUM_IAA_AI_CUSTOM /datum/antagonist/traitor/AI/internal_affairs/custom
|
||||
#define ANTAG_DATUM_IAA_AI /datum/antagonist/traitor/AI/internal_affairs
|
||||
@@ -171,3 +171,5 @@
|
||||
#define ATMOS_PASS_PROC -1 //ask CanAtmosPass()
|
||||
#define ATMOS_PASS_DENSITY -2 //just check density
|
||||
#define CANATMOSPASS(A, O) ( A.CanAtmosPass == ATMOS_PASS_PROC ? A.CanAtmosPass(O) : ( A.CanAtmosPass == ATMOS_PASS_DENSITY ? !A.density : A.CanAtmosPass ) )
|
||||
|
||||
#define LAVALAND_DEFAULT_ATMOS "o2=14;n2=23;TEMP=300"
|
||||
|
||||
@@ -21,10 +21,10 @@ GLOBAL_LIST_EMPTY(all_scripture) //a list containing scripture instances; not us
|
||||
#define SCRIPTURE_PERIPHERAL "Peripheral"
|
||||
#define SCRIPTURE_DRIVER "Driver"
|
||||
#define SCRIPTURE_SCRIPT "Script"
|
||||
#define SCRIPT_SERVANT_REQ 5
|
||||
#define SCRIPT_SERVANT_REQ 6
|
||||
#define SCRIPT_CACHE_REQ 1
|
||||
#define SCRIPTURE_APPLICATION "Application"
|
||||
#define APPLICATION_SERVANT_REQ 8
|
||||
#define APPLICATION_SERVANT_REQ 9
|
||||
#define APPLICATION_CACHE_REQ 3
|
||||
#define APPLICATION_CV_REQ 100
|
||||
#define SCRIPTURE_REVENANT "Revenant"
|
||||
@@ -58,7 +58,7 @@ GLOBAL_LIST_EMPTY(all_scripture) //a list containing scripture instances; not us
|
||||
//clockcult power defines
|
||||
#define MIN_CLOCKCULT_POWER 25 //the minimum amount of power clockcult machines will handle gracefully
|
||||
|
||||
#define CLOCKCULT_POWER_UNIT (MIN_CLOCKCULT_POWER*100) //standard power amount for clockwork proselytizer costs
|
||||
#define CLOCKCULT_POWER_UNIT (MIN_CLOCKCULT_POWER*100) //standard power amount for replica fabricator costs
|
||||
|
||||
#define POWER_STANDARD (CLOCKCULT_POWER_UNIT*0.2) //how much power is in anything else; doesn't matter as much as the following
|
||||
|
||||
@@ -76,7 +76,7 @@ GLOBAL_LIST_EMPTY(all_scripture) //a list containing scripture instances; not us
|
||||
|
||||
#define POWER_PLASTEEL (CLOCKCULT_POWER_UNIT*0.05) //how much power is in one sheet of plasteel
|
||||
|
||||
#define RATVAR_POWER_CHECK "ratvar?" //when passed into can_use_power(), converts it into a check for if ratvar has woken/the proselytizer is debug
|
||||
#define RATVAR_POWER_CHECK "ratvar?" //when passed into can_use_power(), converts it into a check for if ratvar has woken/the fabricator is debug
|
||||
|
||||
//Ark defines
|
||||
#define GATEWAY_SUMMON_RATE 1 //the time amount the Gateway to the Celestial Derelict gets each process tick; defaults to 1 per tick
|
||||
@@ -99,8 +99,10 @@ GLOBAL_LIST_EMPTY(all_scripture) //a list containing scripture instances; not us
|
||||
|
||||
#define SIGIL_ACCESS_RANGE 2 //range at which transmission sigils can access power
|
||||
|
||||
#define PROSELYTIZER_REPAIR_PER_TICK 4 //how much a proselytizer repairs each tick, and also how many deciseconds each tick is
|
||||
#define FABRICATOR_REPAIR_PER_TICK 4 //how much a fabricator repairs each tick, and also how many deciseconds each tick is
|
||||
|
||||
#define OCULAR_WARDEN_EXCLUSION_RANGE 3 //the range at which ocular wardens cannot be placed near other ocular wardens
|
||||
|
||||
#define RATVARIAN_SPEAR_DURATION 1800 //how long ratvarian spears last; defaults to 3 minutes
|
||||
|
||||
#define PRISM_DELAY_DURATION 1200 //how long prolonging prisms delay the shuttle for; defaults to 2 minutes
|
||||
|
||||
@@ -95,11 +95,13 @@
|
||||
|
||||
//tablecrafting defines
|
||||
#define CAT_NONE ""
|
||||
#define CAT_WEAPON "Weaponry"
|
||||
#define CAT_WEAPONRY "Weaponry"
|
||||
#define CAT_WEAPON "Weapons"
|
||||
#define CAT_AMMO "Ammunition"
|
||||
#define CAT_ROBOT "Robots"
|
||||
#define CAT_MISC "Misc"
|
||||
#define CAT_PRIMAL "Tribal"
|
||||
#define CAT_FOOD "Foods"
|
||||
#define CAT_BREAD "Breads"
|
||||
#define CAT_BURGER "Burgers"
|
||||
#define CAT_CAKE "Cakes"
|
||||
|
||||
@@ -14,6 +14,7 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
|
||||
#define NOBLUDGEON 4 // when an item has this it produces no "X has been hit by Y with Z" message in the default attackby()
|
||||
#define MASKINTERNALS 8 // mask allows internals
|
||||
#define HEAR 16 // This flag is what recursive_hear_check() uses to determine wether to add an item to the hearer list or not.
|
||||
#define CHECK_RICOCHET 32 // Projectiels will check ricochet on things impacted that have this.
|
||||
#define CONDUCT 64 // conducts electricity (metal etc.)
|
||||
#define ABSTRACT 128 // for all things that are technically items but used for various different stuff, made it 128 because it could conflict with other flags other way
|
||||
#define NODECONSTRUCT 128 // For machines and structures that should not break into parts, eg, holodeck stuff
|
||||
@@ -57,6 +58,7 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
|
||||
#define UNUSED_TRANSIT_TURF 2
|
||||
#define CAN_BE_DIRTY 4 //If a turf can be made dirty at roundstart. This is also used in areas.
|
||||
#define NO_DEATHRATTLE 16 // Do not notify deadchat about any deaths that occur on this turf.
|
||||
//#define CHECK_RICOCHET 32 //Same thing as atom flag.
|
||||
|
||||
/*
|
||||
These defines are used specifically with the atom/pass_flags bitmask
|
||||
|
||||
@@ -14,10 +14,15 @@
|
||||
#define CLOSED_TURF_LAYER 2.05
|
||||
#define ABOVE_NORMAL_TURF_LAYER 2.08
|
||||
#define LATTICE_LAYER 2.2
|
||||
#define OVER_LATTICE_LAYER 2.25
|
||||
#define DISPOSAL_PIPE_LAYER 2.3
|
||||
#define GAS_PIPE_LAYER 2.35
|
||||
#define GAS_PIPE_HIDDEN_LAYER 2.35
|
||||
#define WIRE_LAYER 2.4
|
||||
#define WIRE_TERMINAL_LAYER 2.45
|
||||
#define GAS_SCRUBBER_LAYER 2.46
|
||||
#define GAS_PIPE_VISIBLE_LAYER 2.47
|
||||
#define GAS_FILTER_LAYER 2.48
|
||||
#define GAS_PUMP_LAYER 2.49
|
||||
#define LOW_OBJ_LAYER 2.5
|
||||
#define LOW_SIGIL_LAYER 2.52
|
||||
#define SIGIL_LAYER 2.54
|
||||
|
||||
@@ -51,8 +51,10 @@
|
||||
#define LIGHT_COLOR_GREEN "#64C864" //Bright but quickly dissipating neon green. rgb(100, 200, 100)
|
||||
#define LIGHT_COLOR_BLUE "#6496FA" //Cold, diluted blue. rgb(100, 150, 250)
|
||||
|
||||
#define LIGHT_COLOR_BLUEGREEN "#7DE1AF" //Light blueish green. rgb(125, 225, 175)
|
||||
#define LIGHT_COLOR_CYAN "#7DE1E1" //Diluted cyan. rgb(125, 225, 225)
|
||||
#define LIGHT_COLOR_LIGHT_CYAN "#40CEFF" //More-saturated cyan. rgb(64, 206, 255)
|
||||
#define LIGHT_COLOR_DARK_BLUE "#6496FA" //Saturated blue. rgb(51, 117, 248)
|
||||
#define LIGHT_COLOR_PINK "#E17DE1" //Diluted, mid-warmth pink. rgb(225, 125, 225)
|
||||
#define LIGHT_COLOR_YELLOW "#E1E17D" //Dimmed yellow, leaning kaki. rgb(225, 225, 125)
|
||||
#define LIGHT_COLOR_BROWN "#966432" //Clear brown, mostly dim. rgb(150, 100, 50)
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
//Investigate logging defines
|
||||
#define INVESTIGATE_ATMOS "atmos"
|
||||
#define INVESTIGATE_BOTANY "botany"
|
||||
#define INVESTIGATE_CARGO "cargo"
|
||||
#define INVESTIGATE_EXPERIMENTOR "experimentor"
|
||||
#define INVESTIGATE_GRAVITY "gravity"
|
||||
#define INVESTIGATE_RECORDS "records"
|
||||
#define INVESTIGATE_SINGULO "singulo"
|
||||
#define INVESTIGATE_SUPERMATTER "supermatter"
|
||||
#define INVESTIGATE_TELESCI "telesci"
|
||||
#define INVESTIGATE_WIRES "wires"
|
||||
|
||||
//Individual logging defines
|
||||
#define INDIVIDUAL_ATTACK_LOG "Attack log"
|
||||
#define INDIVIDUAL_SAY_LOG "Say log"
|
||||
#define INDIVIDUAL_EMOTE_LOG "Emote log"
|
||||
#define INDIVIDUAL_OOC_LOG "OOC log"
|
||||
#define INDIVIDUAL_SHOW_ALL_LOG "All logs"
|
||||
@@ -7,6 +7,11 @@
|
||||
#define STATIC_LIGHT 6
|
||||
#define STATIC_ENVIRON 7
|
||||
|
||||
//Power use
|
||||
#define NO_POWER_USE 0
|
||||
#define IDLE_POWER_USE 1
|
||||
#define ACTIVE_POWER_USE 2
|
||||
|
||||
|
||||
//bitflags for door switches.
|
||||
#define OPEN 1
|
||||
@@ -66,4 +71,16 @@
|
||||
#define PROGRAM_STATE_ACTIVE 2
|
||||
|
||||
#define FIREDOOR_OPEN 1
|
||||
#define FIREDOOR_CLOSED 2
|
||||
#define FIREDOOR_CLOSED 2
|
||||
|
||||
|
||||
|
||||
// These are used by supermatter and supermatter monitor program, mostly for UI updating purposes. Higher should always be worse!
|
||||
#define SUPERMATTER_ERROR -1 // Unknown status, shouldn't happen but just in case.
|
||||
#define SUPERMATTER_INACTIVE 0 // No or minimal energy
|
||||
#define SUPERMATTER_NORMAL 1 // Normal operation
|
||||
#define SUPERMATTER_NOTIFY 2 // Ambient temp > 80% of CRITICAL_TEMPERATURE
|
||||
#define SUPERMATTER_WARNING 3 // Ambient temp > CRITICAL_TEMPERATURE OR integrity damaged
|
||||
#define SUPERMATTER_DANGER 4 // Integrity < 50%
|
||||
#define SUPERMATTER_EMERGENCY 5 // Integrity < 25%
|
||||
#define SUPERMATTER_DELAMINATING 6 // Pretty obvious.
|
||||
|
||||
@@ -34,13 +34,14 @@ Last space-z level = empty
|
||||
#define MAP_REMOVE_JOB(jobpath) /datum/job/##jobpath/map_check() { return (SSmapping.config.map_name != JOB_MODIFICATION_MAP_NAME) && ..() }
|
||||
|
||||
//zlevel defines, can be overridden for different maps in the appropriate _maps file.
|
||||
#define ZLEVEL_STATION 1
|
||||
#define ZLEVEL_CENTCOM 2
|
||||
#define ZLEVEL_CENTCOM 1
|
||||
#define ZLEVEL_STATION 2
|
||||
#define ZLEVEL_MINING 5
|
||||
#define ZLEVEL_LAVALAND 5
|
||||
#define ZLEVEL_EMPTY_SPACE 11
|
||||
#define ZLEVEL_EMPTY_SPACE 12
|
||||
#define ZLEVEL_TRANSIT 11
|
||||
|
||||
#define ZLEVEL_SPACEMIN 3
|
||||
#define ZLEVEL_SPACEMAX 11
|
||||
#define ZLEVEL_SPACEMAX 12
|
||||
|
||||
#define SPACERUIN_MAP_EDGE_PAD 15
|
||||
@@ -4,6 +4,19 @@
|
||||
// #define EAST 4
|
||||
// #define WEST 8
|
||||
|
||||
//These get to go at the top, because they're special
|
||||
//You can use these defines to get the typepath of the currently running proc/verb (yes procs + verbs are objects)
|
||||
/* eg:
|
||||
/mob/living/carbon/human/death()
|
||||
world << THIS_PROC_TYPE_STR //You can only output the string versions
|
||||
Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a string with () (eg: the _WITH_ARGS defines) to make it look nicer)
|
||||
*/
|
||||
#define THIS_PROC_TYPE .....
|
||||
#define THIS_PROC_TYPE_STR "[THIS_PROC_TYPE]" //Because you can only obtain a string of THIS_PROC_TYPE using "[]", and it's nice to just +/+= strings
|
||||
#define THIS_PROC_TYPE_STR_WITH_ARGS "[THIS_PROC_TYPE]([args.Join(",")])"
|
||||
#define THIS_PROC_TYPE_WEIRD ...... //This one is WEIRD, in some cases (When used in certain defines? (eg: ASSERT)) THIS_PROC_TYPE will fail to work, but THIS_PROC_TYPE_WEIRD will work instead
|
||||
#define THIS_PROC_TYPE_WEIRD_STR "[THIS_PROC_TYPE_WEIRD]" //Included for completeness
|
||||
#define THIS_PROC_TYPE_WEIRD_STR_WITH_ARGS "[THIS_PROC_TYPE_WEIRD]([args.Join(",")])" //Ditto
|
||||
|
||||
#define MIDNIGHT_ROLLOVER 864000 //number of deciseconds in a day
|
||||
|
||||
@@ -165,6 +178,7 @@
|
||||
// Used by NewsCaster and NewsPaper.
|
||||
// Used by Modular Computers
|
||||
#define PEN_FONT "Verdana"
|
||||
#define FOUNTAIN_PEN_FONT "Segoe Script"
|
||||
#define CRAYON_FONT "Comic Sans MS"
|
||||
#define PRINTER_FONT "Times New Roman"
|
||||
#define SIGNFONT "Times New Roman"
|
||||
@@ -411,3 +425,22 @@ GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE
|
||||
#define NO_FIELD 0
|
||||
#define FIELD_TURF 1
|
||||
#define FIELD_EDGE 2
|
||||
|
||||
//gibtonite state defines
|
||||
#define GIBTONITE_UNSTRUCK 0
|
||||
#define GIBTONITE_ACTIVE 1
|
||||
#define GIBTONITE_STABLE 2
|
||||
#define GIBTONITE_DETONATE 3
|
||||
//for obj explosion block calculation
|
||||
#define EXPLOSION_BLOCK_PROC -1
|
||||
//Gangster starting influences
|
||||
|
||||
#define GANGSTER_SOLDIER_STARTING_INFLUENCE 5
|
||||
#define GANGSTER_BOSS_STARTING_INFLUENCE 20
|
||||
|
||||
//for determining which type of heartbeat sound is playing
|
||||
#define BEAT_FAST 1
|
||||
#define BEAT_SLOW 2
|
||||
#define BEAT_NONE 0
|
||||
|
||||
#define BEAT_CHANNEL 150
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
diff a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm (rejected hunks)
|
||||
@@ -440,4 +440,4 @@ GLOBAL_LIST_INIT(ghost_others_options, list(GHOST_OTHERS_SIMPLE, GHOST_OTHERS_DE
|
||||
#define BEAT_SLOW 2
|
||||
#define BEAT_NONE 0
|
||||
|
||||
-#define BEAT_CHANNEL 150 //sound channel for heartbeats
|
||||
+
|
||||
+21
-7
@@ -2,6 +2,12 @@
|
||||
|
||||
//Misc mob defines
|
||||
|
||||
//Ready states at roundstart for mob/dead/new_player
|
||||
#define PLAYER_NOT_READY 0
|
||||
#define PLAYER_READY_TO_PLAY 1
|
||||
#define PLAYER_READY_TO_OBSERVE 2
|
||||
|
||||
|
||||
//movement intent defines for the m_intent var
|
||||
#define MOVE_INTENT_WALK "walk"
|
||||
#define MOVE_INTENT_RUN "run"
|
||||
@@ -80,6 +86,15 @@
|
||||
#define AI_IDLE 2
|
||||
#define AI_OFF 3
|
||||
|
||||
#define ENVIRONMENT_SMASH_NONE 0
|
||||
|
||||
#define ENVIRONMENT_SMASH_STRUCTURES 1
|
||||
|
||||
#define ENVIRONMENT_SMASH_WALLS 2
|
||||
|
||||
#define ENVIRONMENT_SMASH_RWALLS 3
|
||||
|
||||
|
||||
//SNPCs
|
||||
//AI defines
|
||||
#define INTERACTING 2
|
||||
@@ -105,13 +120,6 @@
|
||||
#define SNPC_MARTYR 3
|
||||
#define SNPC_PSYCHO 4
|
||||
|
||||
//Individual logging defines
|
||||
#define INDIVIDUAL_ATTACK_LOG "Attack log"
|
||||
#define INDIVIDUAL_SAY_LOG "Say log"
|
||||
#define INDIVIDUAL_EMOTE_LOG "Emote log"
|
||||
#define INDIVIDUAL_OOC_LOG "OOC log"
|
||||
#define INDIVIDUAL_SHOW_ALL_LOG "All logs"
|
||||
|
||||
#define TK_MAXRANGE 15
|
||||
|
||||
#define NO_SLIP_WHEN_WALKING 1
|
||||
@@ -122,3 +130,9 @@
|
||||
#define MAX_CHICKENS 50
|
||||
|
||||
#define UNHEALING_EAR_DAMAGE 100
|
||||
|
||||
|
||||
#define INCORPOREAL_MOVE_BASIC 1
|
||||
#define INCORPOREAL_MOVE_SHADOW 2 // leaves a trail of shadows
|
||||
#define INCORPOREAL_MOVE_JAUNT 3 // is blocked by holy water/salt
|
||||
#define MEGAFAUNA_DEFAULT_RECOVERY_TIME 5
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
#define LIQUID 2
|
||||
#define GAS 3
|
||||
|
||||
#define OPENCONTAINER 4096 // is an open container for chemistry purposes
|
||||
#define INJECTABLE 1024 //Makes reagents addable through droppers and syringes
|
||||
#define DRAWABLE 2048 //If a syringe can draw from it
|
||||
#define OPENCONTAINER 4096 //Is an open container for chemistry purposes
|
||||
#define TRANSPARENT 8192 //Used for non-open containers which you still want to be able to see the reagents off.
|
||||
|
||||
#define TOUCH 1 //splashing
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
#define REBOOT_MODE_NORMAL 0
|
||||
#define REBOOT_MODE_HARD 1
|
||||
#define REBOOT_MODE_SHUTDOWN 2
|
||||
|
||||
#define IRC_STATUS_THROTTLE 5
|
||||
|
||||
//keep these in sync with TGS3
|
||||
#define SERVICE_WORLD_PARAM "server_service"
|
||||
#define SERVICE_PR_TEST_JSON "..\\..\\prtestjob.json"
|
||||
|
||||
#define SERVICE_CMD_HARD_REBOOT "hard_reboot"
|
||||
#define SERVICE_CMD_GRACEFUL_SHUTDOWN "graceful_shutdown"
|
||||
#define SERVICE_CMD_WORLD_ANNOUNCE "world_announce"
|
||||
#define SERVICE_CMD_IRC_CHECK "irc_check"
|
||||
#define SERVICE_CMD_IRC_STATUS "irc_status"
|
||||
#define SERVICE_CMD_ADMIN_MSG "adminmsg"
|
||||
#define SERVICE_CMD_NAME_CHECK "namecheck"
|
||||
#define SERVICE_CMD_ADMIN_WHO "adminwho"
|
||||
|
||||
//#define SERVICE_CMD_PARAM_KEY //defined in __compile_options.dm
|
||||
#define SERVICE_CMD_PARAM_COMMAND "command"
|
||||
#define SERVICE_CMD_PARAM_MESSAGE "message"
|
||||
#define SERVICE_CMD_PARAM_TARGET "target"
|
||||
#define SERVICE_CMD_PARAM_SENDER "sender"
|
||||
|
||||
#define SERVICE_REQUEST_KILL_PROCESS "killme"
|
||||
#define SERVICE_REQUEST_IRC_BROADCAST "irc"
|
||||
#define SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE "send2irc"
|
||||
#define SERVICE_REQUEST_WORLD_REBOOT "worldreboot"
|
||||
@@ -27,6 +27,7 @@
|
||||
#define UNLAUNCHED 0
|
||||
#define ENDGAME_LAUNCHED 1
|
||||
#define EARLY_LAUNCHED 2
|
||||
#define ENDGAME_TRANSIT 3
|
||||
|
||||
// Ripples, effects that signal a shuttle's arrival
|
||||
#define SHUTTLE_RIPPLE_TIME 100
|
||||
@@ -45,4 +46,9 @@
|
||||
#define HYPERSPACE_LAUNCH 2
|
||||
#define HYPERSPACE_END 3
|
||||
|
||||
#define CALL_SHUTTLE_REASON_LENGTH 12
|
||||
#define CALL_SHUTTLE_REASON_LENGTH 12
|
||||
|
||||
//Engine related
|
||||
#define ENGINE_COEFF_MIN 0.5
|
||||
#define ENGINE_COEFF_MAX 2
|
||||
#define ENGINE_DEFAULT_MAXSPEED_ENGINES 5
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#define CHANNEL_ADMIN 1023
|
||||
#define CHANNEL_VOX 1022
|
||||
#define CHANNEL_JUKEBOX 1021
|
||||
#define CHANNEL_HEARTBEAT 1019 //sound channel for heartbeats
|
||||
|
||||
//Citadel code
|
||||
#define CHANNEL_PRED 1020
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
diff a/code/__DEFINES/sound.dm b/code/__DEFINES/sound.dm (rejected hunks)
|
||||
@@ -9,5 +9,5 @@
|
||||
//THIS SHOULD ALWAYS BE THE LOWEST ONE!
|
||||
//KEEP IT UPDATED
|
||||
|
||||
-#define CHANNEL_HIGHEST_AVAILABLE 1019
|
||||
+#define CHANNEL_HIGHEST_AVAILABLE 1018
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
// DEBUFFS //
|
||||
/////////////
|
||||
|
||||
#define STATUS_EFFECT_SIGILMARK /datum/status_effect/sigil_mark
|
||||
#define STATUS_EFFECT_BELLIGERENT /datum/status_effect/belligerent //forces the affected to walk, doing damage if they try to run
|
||||
|
||||
#define STATUS_EFFECT_MANIAMOTOR /datum/status_effect/maniamotor //disrupts, damages, and confuses the affected as long as they're in range of the motor
|
||||
@@ -39,4 +38,16 @@
|
||||
|
||||
#define STATUS_EFFECT_HISWRATH /datum/status_effect/his_wrath //His Wrath.
|
||||
|
||||
#define STATUS_EFFECT_SUMMONEDGHOST /datum/status_effect/cultghost //is a cult ghost and can't use manifest runes
|
||||
#define STATUS_EFFECT_SUMMONEDGHOST /datum/status_effect/cultghost //is a cult ghost and can't use manifest runes
|
||||
#define STATUS_EFFECT_CRUSHERMARK /datum/status_effect/crusher_mark //if struck with a proto-kinetic crusher, takes a ton of damage
|
||||
|
||||
|
||||
/////////////
|
||||
// NEUTRAL //
|
||||
/////////////
|
||||
|
||||
#define STATUS_EFFECT_SIGILMARK /datum/status_effect/sigil_mark
|
||||
|
||||
#define STATUS_EFFECT_CRUSHERDAMAGETRACKING /datum/status_effect/crusher_damage //tracks total kinetic crusher damage on a target
|
||||
|
||||
#define STATUS_EFFECT_SYPHONMARK /datum/status_effect/syphon_mark //tracks kills for the KA death syphon module
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
//prevents distinguishing identical timers with the wait variable
|
||||
#define TIMER_NO_HASH_WAIT 0x10
|
||||
|
||||
#define TIMER_NO_INVOKE_WARNING 200 //number of byond ticks that are allowed to pass before the timer subsystem thinks it hung on something
|
||||
#define TIMER_NO_INVOKE_WARNING 600 //number of byond ticks that are allowed to pass before the timer subsystem thinks it hung on something
|
||||
|
||||
#define TIMER_ID_NULL -1
|
||||
|
||||
//For servers that can't do with any additional lag, set this to none in flightpacks.dm in subsystem/processing.
|
||||
#define FLIGHTSUIT_PROCESSING_NONE 0
|
||||
@@ -39,8 +41,12 @@
|
||||
|
||||
|
||||
// Subsystem init_order, from highest priority to lowest priority
|
||||
// Subsystems shutdown in the reverse of the order they initialize in
|
||||
// The numbers just define the ordering, they are meaningless otherwise.
|
||||
|
||||
#define INIT_ORDER_DBCORE 18
|
||||
#define INIT_ORDER_BLACKBOX 17
|
||||
#define INIT_ORDER_SERVER_MAINT 16
|
||||
#define INIT_ORDER_JOBS 15
|
||||
#define INIT_ORDER_EVENTS 14
|
||||
#define INIT_ORDER_TICKER 13
|
||||
@@ -70,4 +76,4 @@
|
||||
#define RUNLEVEL_GAME 4
|
||||
#define RUNLEVEL_POSTGAME 8
|
||||
|
||||
#define RUNLEVELS_DEFAULT (RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME)
|
||||
#define RUNLEVELS_DEFAULT (RUNLEVEL_SETUP | RUNLEVEL_GAME | RUNLEVEL_POSTGAME)
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
#define TICK_LIMIT_MC 70
|
||||
#define TICK_LIMIT_MC_INIT_DEFAULT 98
|
||||
|
||||
#define TICK_CHECK ( world.tick_usage > GLOB.CURRENT_TICKLIMIT )
|
||||
#define TICK_CHECK ( world.tick_usage > Master.current_ticklimit )
|
||||
#define CHECK_TICK if TICK_CHECK stoplag()
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#define WIRE_AI "AI Connection"
|
||||
#define WIRE_ALARM "Alarm"
|
||||
#define WIRE_AVOIDANCE "Avoidance"
|
||||
#define WIRE_BACKUP1 "Auxillary Power 1"
|
||||
#define WIRE_BACKUP2 "Auxillary Power 2"
|
||||
#define WIRE_BACKUP1 "Auxiliary Power 1"
|
||||
#define WIRE_BACKUP2 "Auxiliary Power 2"
|
||||
#define WIRE_BEACON "Beacon"
|
||||
#define WIRE_BOLTS "Bolts"
|
||||
#define WIRE_BOOM "Boom"
|
||||
|
||||
+12
-10
@@ -48,23 +48,23 @@
|
||||
//Checks if the list is empty
|
||||
/proc/isemptylist(list/L)
|
||||
if(!L.len)
|
||||
return 1
|
||||
return 0
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//Checks for specific types in a list
|
||||
/proc/is_type_in_list(atom/A, list/L)
|
||||
if(!L || !L.len || !A)
|
||||
return 0
|
||||
return FALSE
|
||||
for(var/type in L)
|
||||
if(istype(A, type))
|
||||
return 1
|
||||
return 0
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//Checks for specific types in specifically structured (Assoc "type" = TRUE) lists ('typecaches')
|
||||
/proc/is_type_in_typecache(atom/A, list/L)
|
||||
if(!L || !L.len || !A)
|
||||
|
||||
return 0
|
||||
return FALSE
|
||||
if(ispath(A))
|
||||
. = L[A]
|
||||
else
|
||||
@@ -76,7 +76,7 @@
|
||||
return
|
||||
for(var/V in L)
|
||||
if(string == V)
|
||||
return 1
|
||||
return TRUE
|
||||
return
|
||||
|
||||
//Removes a string from a list
|
||||
@@ -131,9 +131,12 @@
|
||||
return
|
||||
|
||||
//Removes any null entries from the list
|
||||
//Returns TRUE if the list had nulls, FALSE otherwise
|
||||
/proc/listclearnulls(list/L)
|
||||
var/list/N = new(L.len)
|
||||
var/start_len = L.len
|
||||
var/list/N = new(start_len)
|
||||
L -= N
|
||||
return L.len < start_len
|
||||
|
||||
/*
|
||||
* Returns list containing all the entries from first list that are not present in second.
|
||||
@@ -448,13 +451,12 @@
|
||||
|
||||
//Picks from the list, with some safeties, and returns the "default" arg if it fails
|
||||
#define DEFAULTPICK(L, default) ((istype(L, /list) && L:len) ? pick(L) : default)
|
||||
|
||||
#define LAZYINITLIST(L) if (!L) L = list()
|
||||
|
||||
#define UNSETEMPTY(L) if (L && !L.len) L = null
|
||||
#define LAZYREMOVE(L, I) if(L) { L -= I; if(!L.len) { L = null; } }
|
||||
#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
|
||||
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= L.len ? L[I] : null) : L[I]) : null)
|
||||
#define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V;
|
||||
#define LAZYLEN(L) length(L)
|
||||
#define LAZYCLEARLIST(L) if(L) L.Cut()
|
||||
#define SANITIZE_LIST(L) ( islist(L) ? L : list() )
|
||||
|
||||
+64
-64
@@ -1,64 +1,64 @@
|
||||
//Sends resource files to client cache
|
||||
/client/proc/getFiles()
|
||||
for(var/file in args)
|
||||
src << browse_rsc(file)
|
||||
|
||||
/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm"))
|
||||
var/path = root
|
||||
|
||||
for(var/i=0, i<max_iterations, i++)
|
||||
var/list/choices = flist(path)
|
||||
if(path != root)
|
||||
choices.Insert(1,"/")
|
||||
|
||||
var/choice = input(src,"Choose a file to access:","Download",null) as null|anything in choices
|
||||
switch(choice)
|
||||
if(null)
|
||||
return
|
||||
if("/")
|
||||
path = root
|
||||
continue
|
||||
path += choice
|
||||
|
||||
if(copytext(path,-1,0) != "/") //didn't choose a directory, no need to iterate again
|
||||
break
|
||||
|
||||
var/extension = copytext(path,-4,0)
|
||||
if( !fexists(path) || !(extension in valid_extensions) )
|
||||
to_chat(src, "<font color='red'>Error: browse_files(): File not found/Invalid file([path]).</font>")
|
||||
return
|
||||
|
||||
return path
|
||||
|
||||
#define FTPDELAY 200 //200 tick delay to discourage spam
|
||||
/* This proc is a failsafe to prevent spamming of file requests.
|
||||
It is just a timer that only permits a download every [FTPDELAY] ticks.
|
||||
This can be changed by modifying FTPDELAY's value above.
|
||||
|
||||
PLEASE USE RESPONSIBLY, Some log files can reach sizes of 4MB! */
|
||||
/client/proc/file_spam_check()
|
||||
var/time_to_wait = GLOB.fileaccess_timer - world.time
|
||||
if(time_to_wait > 0)
|
||||
to_chat(src, "<font color='red'>Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds.</font>")
|
||||
return 1
|
||||
GLOB.fileaccess_timer = world.time + FTPDELAY
|
||||
return 0
|
||||
#undef FTPDELAY
|
||||
|
||||
/proc/pathwalk(path)
|
||||
var/list/jobs = list(path)
|
||||
var/list/filenames = list()
|
||||
|
||||
while(jobs.len)
|
||||
var/current_dir = pop(jobs)
|
||||
var/list/new_filenames = flist(current_dir)
|
||||
for(var/new_filename in new_filenames)
|
||||
// if filename ends in / it is a directory, append to currdir
|
||||
if(findtext(new_filename, "/", -1))
|
||||
jobs += current_dir + new_filename
|
||||
else
|
||||
filenames += current_dir + new_filename
|
||||
return filenames
|
||||
|
||||
/proc/pathflatten(path)
|
||||
return replacetext(path, "/", "_")
|
||||
//Sends resource files to client cache
|
||||
/client/proc/getFiles()
|
||||
for(var/file in args)
|
||||
src << browse_rsc(file)
|
||||
|
||||
/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list(".txt",".log",".htm", ".html"))
|
||||
var/path = root
|
||||
|
||||
for(var/i=0, i<max_iterations, i++)
|
||||
var/list/choices = flist(path)
|
||||
if(path != root)
|
||||
choices.Insert(1,"/")
|
||||
|
||||
var/choice = input(src,"Choose a file to access:","Download",null) as null|anything in choices
|
||||
switch(choice)
|
||||
if(null)
|
||||
return
|
||||
if("/")
|
||||
path = root
|
||||
continue
|
||||
path += choice
|
||||
|
||||
if(copytext(path,-1,0) != "/") //didn't choose a directory, no need to iterate again
|
||||
break
|
||||
|
||||
var/extension = copytext(path,-4,0)
|
||||
if( !fexists(path) || !(extension in valid_extensions) )
|
||||
to_chat(src, "<font color='red'>Error: browse_files(): File not found/Invalid file([path]).</font>")
|
||||
return
|
||||
|
||||
return path
|
||||
|
||||
#define FTPDELAY 200 //200 tick delay to discourage spam
|
||||
/* This proc is a failsafe to prevent spamming of file requests.
|
||||
It is just a timer that only permits a download every [FTPDELAY] ticks.
|
||||
This can be changed by modifying FTPDELAY's value above.
|
||||
|
||||
PLEASE USE RESPONSIBLY, Some log files can reach sizes of 4MB! */
|
||||
/client/proc/file_spam_check()
|
||||
var/time_to_wait = GLOB.fileaccess_timer - world.time
|
||||
if(time_to_wait > 0)
|
||||
to_chat(src, "<font color='red'>Error: file_spam_check(): Spam. Please wait [round(time_to_wait/10)] seconds.</font>")
|
||||
return 1
|
||||
GLOB.fileaccess_timer = world.time + FTPDELAY
|
||||
return 0
|
||||
#undef FTPDELAY
|
||||
|
||||
/proc/pathwalk(path)
|
||||
var/list/jobs = list(path)
|
||||
var/list/filenames = list()
|
||||
|
||||
while(jobs.len)
|
||||
var/current_dir = pop(jobs)
|
||||
var/list/new_filenames = flist(current_dir)
|
||||
for(var/new_filename in new_filenames)
|
||||
// if filename ends in / it is a directory, append to currdir
|
||||
if(findtext(new_filename, "/", -1))
|
||||
jobs += current_dir + new_filename
|
||||
else
|
||||
filenames += current_dir + new_filename
|
||||
return filenames
|
||||
|
||||
/proc/pathflatten(path)
|
||||
return replacetext(path, "/", "_")
|
||||
|
||||
@@ -507,7 +507,8 @@
|
||||
return
|
||||
|
||||
//First we spawn a dude.
|
||||
var/mob/living/carbon/human/new_character = new(pick(GLOB.latejoin))//The mob being spawned.
|
||||
var/mob/living/carbon/human/new_character = new//The mob being spawned.
|
||||
SSjob.SendToLateJoin(new_character)
|
||||
|
||||
G_found.client.prefs.copy_to(new_character)
|
||||
new_character.dna.update_dna_identity()
|
||||
|
||||
@@ -167,23 +167,14 @@
|
||||
underlay_appearance.icon = fixed_underlay["icon"]
|
||||
underlay_appearance.icon_state = fixed_underlay["icon_state"]
|
||||
else
|
||||
var/turf/T = get_step(src, turn(adjacencies, 180))
|
||||
if(T && (T.density || T.smooth))
|
||||
var/turned_adjacency = turn(adjacencies, 180)
|
||||
var/turf/T = get_step(src, turned_adjacency)
|
||||
if(!T.get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency))
|
||||
T = get_step(src, turn(adjacencies, 135))
|
||||
if(T && (T.density || T.smooth))
|
||||
if(!T.get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency))
|
||||
T = get_step(src, turn(adjacencies, 225))
|
||||
|
||||
if(isspaceturf(T) && !istype(T, /turf/open/space/transit))
|
||||
underlay_appearance.icon = 'icons/turf/space.dmi'
|
||||
underlay_appearance.icon_state = SPACE_ICON_STATE
|
||||
underlay_appearance.plane = PLANE_SPACE
|
||||
else if(T && !T.density && !T.smooth)
|
||||
underlay_appearance.icon = T.icon
|
||||
underlay_appearance.icon_state = T.icon_state
|
||||
else if(baseturf && !initial(baseturf.density) && !initial(baseturf.smooth))
|
||||
underlay_appearance.icon = initial(baseturf.icon)
|
||||
underlay_appearance.icon_state = initial(baseturf.icon_state)
|
||||
else
|
||||
//if all else fails, ask our own turf
|
||||
if(!T.get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency) && !get_smooth_underlay_icon(underlay_appearance, src, turned_adjacency))
|
||||
underlay_appearance.icon = DEFAULT_UNDERLAY_ICON
|
||||
underlay_appearance.icon_state = DEFAULT_UNDERLAY_ICON_STATE
|
||||
underlays = U
|
||||
|
||||
@@ -167,7 +167,7 @@ mob
|
||||
|
||||
Output_Icon()
|
||||
set name = "2. Output Icon"
|
||||
to_chat(src, "Icon is: \icon[getFlatIcon(src)]")
|
||||
to_chat(src, "Icon is: [bicon(getFlatIcon(src))]")
|
||||
|
||||
Label_Icon()
|
||||
set name = "3. Label Icon"
|
||||
|
||||
+16
-2
@@ -130,6 +130,22 @@ GLOBAL_LIST_INIT(sqrtTable, list(1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4,
|
||||
var/t = round((val - min) / d)
|
||||
return val - (t * d)
|
||||
|
||||
#define NORM_ROT(rot) ((((rot % 360) + (rot - round(rot, 1))) > 0) ? ((rot % 360) + (rot - round(rot, 1))) : (((rot % 360) + (rot - round(rot, 1))) + 360))
|
||||
|
||||
/proc/get_angle_of_incidence(face_angle, angle_in, auto_normalize = TRUE)
|
||||
|
||||
var/angle_in_s = NORM_ROT(angle_in)
|
||||
var/face_angle_s = NORM_ROT(face_angle)
|
||||
var/incidence = face_angle_s - angle_in_s
|
||||
var/incidence_s = incidence
|
||||
while(incidence_s < -90)
|
||||
incidence_s += 180
|
||||
while(incidence_s > 90)
|
||||
incidence_s -= 180
|
||||
if(auto_normalize)
|
||||
return incidence_s
|
||||
else
|
||||
return incidence
|
||||
|
||||
//A logarithm that converts an integer to a number scaled between 0 and 1 (can be tweaked to be higher).
|
||||
//Currently, this is used for hydroponics-produce sprite transforming, but could be useful for other transform functions.
|
||||
@@ -141,8 +157,6 @@ GLOBAL_LIST_INIT(sqrtTable, list(1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4,
|
||||
return size_factor + scaling_modifier //scale mod of 0 results in a number from 0 to 1. A scale modifier of +0.5 returns 0.5 to 1.5
|
||||
//to_chat(world, "Transform multiplier of [src] is [size_factor + scaling_modifer]")
|
||||
|
||||
|
||||
|
||||
//converts a uniform distributed random number into a normal distributed one
|
||||
//since this method produces two random numbers, one is saved for subsequent calls
|
||||
//(making the cost negligble for every second call)
|
||||
|
||||
+17
-14
@@ -1,3 +1,6 @@
|
||||
//wrapper macro for sending images that makes grepping easy
|
||||
#define SEND_IMAGE(target, image) target << image
|
||||
|
||||
/proc/random_blood_type()
|
||||
return pick(4;"O-", 36;"O+", 3;"A-", 28;"A+", 1;"B-", 20;"B+", 1;"AB-", 5;"AB+")
|
||||
|
||||
@@ -340,20 +343,20 @@ Proc for attack log creation, because really why not
|
||||
qdel(progbar)
|
||||
|
||||
|
||||
//some additional checks as a callback for for do_afters that want to break on losing health or on the mob taking action
|
||||
/mob/proc/break_do_after_checks(list/checked_health, check_clicks)
|
||||
if(check_clicks && next_move > world.time)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
//pass a list in the format list("health" = mob's health var) to check health during this
|
||||
/mob/living/break_do_after_checks(list/checked_health, check_clicks)
|
||||
if(islist(checked_health))
|
||||
if(health < checked_health["health"])
|
||||
return FALSE
|
||||
checked_health["health"] = health
|
||||
return ..()
|
||||
|
||||
//some additional checks as a callback for for do_afters that want to break on losing health or on the mob taking action
|
||||
/mob/proc/break_do_after_checks(list/checked_health, check_clicks)
|
||||
if(check_clicks && next_move > world.time)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
//pass a list in the format list("health" = mob's health var) to check health during this
|
||||
/mob/living/break_do_after_checks(list/checked_health, check_clicks)
|
||||
if(islist(checked_health))
|
||||
if(health < checked_health["health"])
|
||||
return FALSE
|
||||
checked_health["health"] = health
|
||||
return ..()
|
||||
|
||||
/proc/do_after(mob/user, delay, needhand = 1, atom/target = null, progress = 1, datum/callback/extra_checks = null)
|
||||
if(!user)
|
||||
return 0
|
||||
|
||||
+37
-33
@@ -55,19 +55,19 @@ GLOBAL_VAR(command_name)
|
||||
return capitalize(name)
|
||||
|
||||
/proc/station_name()
|
||||
if(!GLOB.station_name)
|
||||
var/newname
|
||||
if(config && config.station_name)
|
||||
newname = config.station_name
|
||||
else
|
||||
newname = new_station_name()
|
||||
if(!GLOB.station_name)
|
||||
var/newname
|
||||
if(config && config.station_name)
|
||||
newname = config.station_name
|
||||
else
|
||||
newname = new_station_name()
|
||||
|
||||
set_station_name(newname)
|
||||
|
||||
return GLOB.station_name
|
||||
|
||||
/proc/set_station_name(newname)
|
||||
GLOB.station_name = newname
|
||||
set_station_name(newname)
|
||||
|
||||
return GLOB.station_name
|
||||
|
||||
/proc/set_station_name(newname)
|
||||
GLOB.station_name = newname
|
||||
|
||||
if(config && config.server_name)
|
||||
world.name = "[config.server_name][config.server_name==GLOB.station_name ? "" : ": [GLOB.station_name]"]"
|
||||
@@ -167,9 +167,13 @@ GLOBAL_VAR(syndicate_code_response) //Code response for traitors.
|
||||
/N
|
||||
*/
|
||||
|
||||
/proc/generate_code_phrase()//Proc is used for phrase and response in master_controller.dm
|
||||
/proc/generate_code_phrase(return_list=FALSE)//Proc is used for phrase and response in master_controller.dm
|
||||
|
||||
if(!return_list)
|
||||
. = ""
|
||||
else
|
||||
. = list()
|
||||
|
||||
var/code_phrase = ""//What is returned when the proc finishes.
|
||||
var/words = pick(//How many words there will be. Minimum of two. 2, 4 and 5 have a lesser chance of being selected. 3 is the most likely.
|
||||
50; 2,
|
||||
200; 3,
|
||||
@@ -204,39 +208,39 @@ GLOBAL_VAR(syndicate_code_response) //Code response for traitors.
|
||||
switch(rand(1,2))//Mainly to add more options later.
|
||||
if(1)
|
||||
if(names.len&&prob(70))
|
||||
code_phrase += pick(names)
|
||||
. += pick(names)
|
||||
else
|
||||
if(prob(10))
|
||||
code_phrase += pick(lizard_name(MALE),lizard_name(FEMALE))
|
||||
. += pick(lizard_name(MALE),lizard_name(FEMALE))
|
||||
else
|
||||
code_phrase += pick(pick(GLOB.first_names_male,GLOB.first_names_female))
|
||||
code_phrase += " "
|
||||
code_phrase += pick(GLOB.last_names)
|
||||
var/new_name = pick(pick(GLOB.first_names_male,GLOB.first_names_female))
|
||||
new_name += " "
|
||||
new_name += pick(GLOB.last_names)
|
||||
. += new_name
|
||||
if(2)
|
||||
code_phrase += pick(get_all_jobs())//Returns a job.
|
||||
. += pick(get_all_jobs())//Returns a job.
|
||||
safety -= 1
|
||||
if(2)
|
||||
switch(rand(1,3))//Food, drinks, or things. Only selectable once.
|
||||
if(1)
|
||||
code_phrase += lowertext(pick(drinks))
|
||||
. += lowertext(pick(drinks))
|
||||
if(2)
|
||||
code_phrase += lowertext(pick(foods))
|
||||
. += lowertext(pick(foods))
|
||||
if(3)
|
||||
code_phrase += lowertext(pick(locations))
|
||||
. += lowertext(pick(locations))
|
||||
safety -= 2
|
||||
if(3)
|
||||
switch(rand(1,4))//Abstract nouns, objects, adjectives, threats. Can be selected more than once.
|
||||
if(1)
|
||||
code_phrase += lowertext(pick(nouns))
|
||||
. += lowertext(pick(nouns))
|
||||
if(2)
|
||||
code_phrase += lowertext(pick(objects))
|
||||
. += lowertext(pick(objects))
|
||||
if(3)
|
||||
code_phrase += lowertext(pick(adjectives))
|
||||
. += lowertext(pick(adjectives))
|
||||
if(4)
|
||||
code_phrase += lowertext(pick(threats))
|
||||
if(words==1)
|
||||
code_phrase += "."
|
||||
else
|
||||
code_phrase += ", "
|
||||
|
||||
return code_phrase
|
||||
. += lowertext(pick(threats))
|
||||
if(!return_list)
|
||||
if(words==1)
|
||||
. += "."
|
||||
else
|
||||
. += ", "
|
||||
|
||||
@@ -626,14 +626,13 @@ GLOBAL_DATUM_INIT(sortInstance, /datum/sortInstance, new())
|
||||
var/val2 = fetchElement(L,cursor2)
|
||||
|
||||
while(1)
|
||||
if(call(cmp)(val1,val2) < 0)
|
||||
if(call(cmp)(val1,val2) <= 0)
|
||||
if(++cursor1 >= end1)
|
||||
break
|
||||
val1 = fetchElement(L,cursor1)
|
||||
else
|
||||
moveElement(L,cursor2,cursor1)
|
||||
|
||||
++cursor2
|
||||
if(++cursor2 >= end2)
|
||||
break
|
||||
++end1
|
||||
@@ -653,4 +652,4 @@ GLOBAL_DATUM_INIT(sortInstance, /datum/sortInstance, new())
|
||||
#undef MIN_GALLOP
|
||||
#undef MIN_MERGE
|
||||
|
||||
#undef fetchElement
|
||||
#undef fetchElement
|
||||
|
||||
+23
-15
@@ -723,7 +723,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
|
||||
//Irregular objects
|
||||
var/icon/AMicon = icon(AM.icon, AM.icon_state)
|
||||
var/icon/AMiconheight = AMicon.Height()
|
||||
var/icon/AMiconwidth = AMicon.Width()
|
||||
var/icon/AMiconwidth = AMicon.Width()
|
||||
if(AMiconheight != world.icon_size || AMiconwidth != world.icon_size)
|
||||
pixel_x_offset += ((AMicon.Width()/world.icon_size)-1)*(world.icon_size*0.5)
|
||||
pixel_y_offset += ((AMicon.Height()/world.icon_size)-1)*(world.icon_size*0.5)
|
||||
@@ -834,11 +834,11 @@ GLOBAL_LIST_INIT(WALLITEMS_INVERSE, typecacheof(list(
|
||||
|
||||
/obj/proc/atmosanalyzer_scan(datum/gas_mixture/air_contents, mob/user, obj/target = src)
|
||||
var/obj/icon = target
|
||||
user.visible_message("[user] has used the analyzer on \icon[icon] [target].", "<span class='notice'>You use the analyzer on \icon[icon] [target].</span>")
|
||||
user.visible_message("[user] has used the analyzer on [bicon(icon)] [target].", "<span class='notice'>You use the analyzer on [bicon(icon)] [target].</span>")
|
||||
var/pressure = air_contents.return_pressure()
|
||||
var/total_moles = air_contents.total_moles()
|
||||
|
||||
to_chat(user, "<span class='notice'>Results of analysis of \icon[icon] [target].</span>")
|
||||
to_chat(user, "<span class='notice'>Results of analysis of [bicon(icon)] [target].</span>")
|
||||
if(total_moles>0)
|
||||
to_chat(user, "<span class='notice'>Pressure: [round(pressure,0.1)] kPa</span>")
|
||||
|
||||
@@ -1079,18 +1079,17 @@ B --><-- A
|
||||
return L
|
||||
|
||||
//similar function to RANGE_TURFS(), but will search spiralling outwards from the center (like the above, but only turfs)
|
||||
/proc/spiral_range_turfs(dist=0, center=usr, orange=0)
|
||||
/proc/spiral_range_turfs(dist=0, center=usr, orange=0, list/outlist = list(), tick_checked)
|
||||
outlist.Cut()
|
||||
if(!dist)
|
||||
if(!orange)
|
||||
return list(center)
|
||||
else
|
||||
return list()
|
||||
outlist += center
|
||||
return outlist
|
||||
|
||||
var/turf/t_center = get_turf(center)
|
||||
if(!t_center)
|
||||
return list()
|
||||
return outlist
|
||||
|
||||
var/list/L = list()
|
||||
var/list/L = outlist
|
||||
var/turf/T
|
||||
var/y
|
||||
var/x
|
||||
@@ -1128,6 +1127,8 @@ B --><-- A
|
||||
if(T)
|
||||
L += T
|
||||
c_dist++
|
||||
if(tick_checked)
|
||||
CHECK_TICK
|
||||
|
||||
return L
|
||||
|
||||
@@ -1209,13 +1210,16 @@ proc/pick_closest_path(value, list/matches = get_fancy_list_of_atom_types())
|
||||
#define DELTA_CALC max(((max(world.tick_usage, world.cpu) / 100) * max(Master.sleep_delta,1)), 1)
|
||||
|
||||
/proc/stoplag()
|
||||
if (!Master || !(Master.current_runlevel & RUNLEVELS_DEFAULT))
|
||||
sleep(world.tick_lag)
|
||||
return 1
|
||||
. = 0
|
||||
var/i = 1
|
||||
do
|
||||
. += round(i*DELTA_CALC)
|
||||
sleep(i*world.tick_lag*DELTA_CALC)
|
||||
i *= 2
|
||||
while (world.tick_usage > min(TICK_LIMIT_TO_RUN, GLOB.CURRENT_TICKLIMIT))
|
||||
while (world.tick_usage > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit))
|
||||
|
||||
#undef DELTA_CALC
|
||||
|
||||
@@ -1343,7 +1347,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
|
||||
//kevinz000 if you touch this I will hunt you down
|
||||
GLOBAL_VAR_INIT(valid_HTTPSGet, FALSE)
|
||||
GLOBAL_PROTECT(valid_HTTPSGet)
|
||||
/proc/HTTPSGet(url)
|
||||
/proc/HTTPSGet(url) //tgs2 support
|
||||
if(findtext(url, "\""))
|
||||
GLOB.valid_HTTPSGet = FALSE
|
||||
|
||||
@@ -1394,9 +1398,6 @@ GLOBAL_PROTECT(valid_HTTPSGet)
|
||||
|
||||
#define UNTIL(X) while(!(X)) stoplag()
|
||||
|
||||
/proc/to_chat(target, message)
|
||||
target << message
|
||||
|
||||
/proc/pass()
|
||||
return
|
||||
|
||||
@@ -1416,3 +1417,10 @@ GLOBAL_PROTECT(valid_HTTPSGet)
|
||||
mob_occupant = brain.brainmob
|
||||
|
||||
return mob_occupant
|
||||
|
||||
//counts the number of bits in Byond's 16-bit width field
|
||||
//in constant time and memory!
|
||||
/proc/BitCount(bitfield)
|
||||
var/temp = bitfield - ((bitfield>>1)&46811) - ((bitfield>>2)&37449) //0133333 and 0111111 respectively
|
||||
temp = ((temp + (temp>>3))&29127) % 63 //070707
|
||||
return temp
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
//MINOR TWEAKS/MISC
|
||||
#define AGE_MIN 17 //youngest a character can be
|
||||
#define AGE_MAX 85 //oldest a character can be
|
||||
#define WIZARD_AGE_MIN 30 //youngest a wizard can be
|
||||
#define WIZARD_AGE_MIN 30 //youngest a wizard can be
|
||||
#define SHOES_SLOWDOWN 0 //How much shoes slow you down by default. Negative values speed you up
|
||||
#define POCKET_STRIP_DELAY 40 //time taken (in deciseconds) to search somebody's pockets
|
||||
#define DOOR_CRUSH_DAMAGE 15 //the amount of damage that airlocks deal when they crush you
|
||||
@@ -62,14 +62,12 @@
|
||||
#endif
|
||||
|
||||
//Update this whenever you need to take advantage of more recent byond features
|
||||
#define MIN_COMPILER_VERSION 511
|
||||
#define MIN_COMPILER_VERSION 511
|
||||
#if DM_VERSION < MIN_COMPILER_VERSION
|
||||
//Don't forget to update this part
|
||||
#error Your version of BYOND is too out-of-date to compile this project. Go to byond.com/download and update.
|
||||
#error You need version 511 or higher
|
||||
#error You need version 511 or higher
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef SERVERTOOLS
|
||||
#define SERVERTOOLS 0
|
||||
#endif
|
||||
#define SERVICE_CMD_PARAM_KEY "serviceCommsKey"
|
||||
|
||||
@@ -22,12 +22,12 @@ GLOBAL_VAR_INIT(tinted_weldhelh, TRUE)
|
||||
// Debug2 is used in conjunction with a lot of admin verbs and therefore is actually legit.
|
||||
GLOBAL_VAR_INIT(Debug, FALSE) // global debug switch
|
||||
GLOBAL_VAR_INIT(Debug2, FALSE)
|
||||
|
||||
//This was a define, but I changed it to a variable so it can be changed in-game.(kept the all-caps definition because... code...) -Errorage
|
||||
GLOBAL_VAR_INIT(MAX_EX_DEVESTATION_RANGE, 3)
|
||||
GLOBAL_VAR_INIT(MAX_EX_HEAVY_RANGE, 7)
|
||||
GLOBAL_VAR_INIT(MAX_EX_LIGHT_RANGE, 14)
|
||||
GLOBAL_VAR_INIT(MAX_EX_FLASH_RANGE, 14)
|
||||
GLOBAL_VAR_INIT(MAX_EX_FLAME_RANGE, 14)
|
||||
GLOBAL_VAR_INIT(DYN_EX_SCALE, 0.5)
|
||||
|
||||
|
||||
//This was a define, but I changed it to a variable so it can be changed in-game.(kept the all-caps definition because... code...) -Errorage
|
||||
GLOBAL_VAR_INIT(MAX_EX_DEVESTATION_RANGE, 3)
|
||||
GLOBAL_VAR_INIT(MAX_EX_HEAVY_RANGE, 7)
|
||||
GLOBAL_VAR_INIT(MAX_EX_LIGHT_RANGE, 14)
|
||||
GLOBAL_VAR_INIT(MAX_EX_FLASH_RANGE, 14)
|
||||
GLOBAL_VAR_INIT(MAX_EX_FLAME_RANGE, 14)
|
||||
GLOBAL_VAR_INIT(DYN_EX_SCALE, 0.5)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
GLOBAL_VAR_INIT(master_mode, "traitor") //"extended"
|
||||
GLOBAL_VAR_INIT(master_mode, "traitor") //"extended"
|
||||
GLOBAL_VAR_INIT(secret_force_mode, "secret") // if this is anything but "secret", the secret rotation will forceably choose this mode
|
||||
|
||||
GLOBAL_VAR_INIT(wavesecret, 0) // meteor mode, delays wave progression, terrible name
|
||||
@@ -14,4 +14,5 @@ GLOBAL_VAR_INIT(cult_vote_called, FALSE)
|
||||
GLOBAL_VAR_INIT(cult_mastered, FALSE)
|
||||
GLOBAL_VAR_INIT(reckoning_complete, FALSE)
|
||||
GLOBAL_VAR_INIT(sac_complete, FALSE)
|
||||
GLOBAL_DATUM(cult_narsie, /obj/singularity/narsie/large/cult)
|
||||
GLOBAL_DATUM(cult_narsie, /obj/singularity/narsie/large/cult)
|
||||
GLOBAL_LIST_EMPTY(summon_spots)
|
||||
@@ -43,12 +43,12 @@ GLOBAL_LIST_INIT(security_depts_prefs, list(SEC_DEPT_RANDOM, SEC_DEPT_NONE, SEC_
|
||||
//Backpacks
|
||||
#define GBACKPACK "Grey Backpack"
|
||||
#define GSATCHEL "Grey Satchel"
|
||||
#define GDUFFLEBAG "Grey Dufflebag"
|
||||
#define Gduffelbag "Grey duffelbag"
|
||||
#define LSATCHEL "Leather Satchel"
|
||||
#define DBACKPACK "Department Backpack"
|
||||
#define DSATCHEL "Department Satchel"
|
||||
#define DDUFFLEBAG "Department Dufflebag"
|
||||
GLOBAL_LIST_INIT(backbaglist, list(DBACKPACK, DSATCHEL, DDUFFLEBAG, GBACKPACK, GSATCHEL, GDUFFLEBAG, LSATCHEL))
|
||||
#define Dduffelbag "Department duffelbag"
|
||||
GLOBAL_LIST_INIT(backbaglist, list(DBACKPACK, DSATCHEL, Dduffelbag, GBACKPACK, GSATCHEL, Gduffelbag, LSATCHEL))
|
||||
|
||||
//Uplink spawn loc
|
||||
#define UPLINK_PDA "PDA"
|
||||
@@ -161,3 +161,4 @@ GLOBAL_LIST_INIT(numbers_as_words, list("One", "Two", "Three", "Four",
|
||||
return L
|
||||
|
||||
GLOBAL_LIST_INIT(station_numerals, greek_letters + phonetic_alphabet + numbers_as_words + generate_number_strings())
|
||||
GLOBAL_LIST_INIT(admiral_messages, list("Do you know how expensive these stations are?","Stop wasting my time.","I was sleeping, thanks a lot.","Stand and fight you cowards!","You knew the risks coming in.","Stop being paranoid.","Whatever's broken just build a new one.","No.", "<i>null</i>","<i>Error: No comment given.</i>", "It's a good day to die!"))
|
||||
|
||||
@@ -32,7 +32,6 @@ GLOBAL_LIST_EMPTY(generic_event_spawns) //list of all spawns for events
|
||||
|
||||
GLOBAL_LIST_EMPTY(wizardstart)
|
||||
GLOBAL_LIST_EMPTY(newplayer_start)
|
||||
GLOBAL_LIST_EMPTY(latejoin)
|
||||
GLOBAL_LIST_EMPTY(prisonwarp) //prisoners go to these
|
||||
GLOBAL_LIST_EMPTY(holdingfacility) //captured people go here
|
||||
GLOBAL_LIST_EMPTY(xeno_spawn)//Aliens spawn at these.
|
||||
@@ -53,5 +52,4 @@ GLOBAL_LIST_EMPTY(awaydestinations) //a list of landmarks that the warpgate can
|
||||
//used by jump-to-area etc. Updated by area/updateName()
|
||||
GLOBAL_LIST_EMPTY(sortedAreas)
|
||||
|
||||
GLOBAL_LIST_EMPTY(transit_markers)
|
||||
GLOBAL_LIST_EMPTY(all_abstract_markers)
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
GLOBAL_LIST_EMPTY(commendations)
|
||||
@@ -32,4 +32,6 @@ GLOBAL_LIST_EMPTY(ladders)
|
||||
GLOBAL_LIST_EMPTY(trophy_cases)
|
||||
|
||||
GLOBAL_LIST_EMPTY(wire_color_directory)
|
||||
GLOBAL_LIST_EMPTY(wire_name_directory)
|
||||
GLOBAL_LIST_EMPTY(wire_name_directory)
|
||||
|
||||
GLOBAL_LIST_EMPTY(ai_status_displays)
|
||||
|
||||
@@ -31,4 +31,6 @@ GLOBAL_PROTECT(OOClog)
|
||||
GLOBAL_LIST_EMPTY(adminlog)
|
||||
GLOBAL_PROTECT(adminlog)
|
||||
|
||||
GLOBAL_LIST_EMPTY(individual_log_list) // Logs each mob individual logs, a global so it doesn't get lost on cloning/changing mobs
|
||||
|
||||
GLOBAL_LIST_EMPTY(active_turfs_startlist)
|
||||
@@ -8,7 +8,7 @@ GLOBAL_VAR_INIT(fileaccess_timer, 0)
|
||||
|
||||
GLOBAL_VAR_INIT(TAB, " ")
|
||||
|
||||
GLOBAL_DATUM(data_core, /datum/datacore)
|
||||
GLOBAL_DATUM_INIT(data_core, /datum/datacore, new)
|
||||
|
||||
GLOBAL_VAR_INIT(CELLRATE, 0.002) // multiplier for watts per tick <> cell storage (eg: .002 means if there is a load of 1000 watts, 20 units will be taken from a cell per second)
|
||||
GLOBAL_VAR_INIT(CHARGELEVEL, 0.001) // Cap for how fast cells charge, as a percentage-per-tick (.001 means cellcharge is capped to 1% per second)
|
||||
|
||||
@@ -13,4 +13,4 @@ GLOBAL_REAL_VAR(sqlport) = "3306"
|
||||
GLOBAL_REAL_VAR(sqlfdbkdb) = "test"
|
||||
GLOBAL_REAL_VAR(sqlfdbklogin) = "root"
|
||||
GLOBAL_REAL_VAR(sqlfdbkpass) = ""
|
||||
GLOBAL_REAL_VAR(sqlfdbktableprefix) = "erro_"
|
||||
GLOBAL_REAL_VAR(sqlfdbktableprefix) = ""
|
||||
|
||||
Executable
+1
@@ -0,0 +1 @@
|
||||
GLOBAL_VAR_INIT(enable_examine_tips, TRUE)
|
||||
@@ -1,5 +1,10 @@
|
||||
/client
|
||||
var/list/atom/selected_target[2]
|
||||
var/obj/item/active_mousedown_item = null
|
||||
var/mouseParams = ""
|
||||
var/mouseLocation = null
|
||||
var/mouseObject = null
|
||||
var/mouseControlObject = null
|
||||
|
||||
/client/MouseDown(object, location, control, params)
|
||||
var/delay = mob.CanMobAutoclick(object, location, params)
|
||||
@@ -9,14 +14,26 @@
|
||||
while(selected_target[1])
|
||||
Click(selected_target[1], location, control, selected_target[2])
|
||||
sleep(delay)
|
||||
active_mousedown_item = mob.canMobMousedown(object, location, params)
|
||||
if(active_mousedown_item)
|
||||
active_mousedown_item.onMouseDown(object, location, params, mob)
|
||||
|
||||
/client/MouseUp(object, location, control, params)
|
||||
selected_target[1] = null
|
||||
if(active_mousedown_item)
|
||||
active_mousedown_item.onMouseUp(object, location, params, mob)
|
||||
active_mousedown_item = null
|
||||
|
||||
/client/MouseDrag(src_object,atom/over_object,src_location,over_location,src_control,over_control,params)
|
||||
if(selected_target[1] && over_object.IsAutoclickable())
|
||||
mouseParams = params
|
||||
mouseLocation = over_location
|
||||
mouseObject = over_object
|
||||
mouseControlObject = over_control
|
||||
if(selected_target[1] && over_object && over_object.IsAutoclickable())
|
||||
selected_target[1] = over_object
|
||||
selected_target[2] = params
|
||||
if(active_mousedown_item)
|
||||
active_mousedown_item.onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
|
||||
|
||||
/mob/proc/CanMobAutoclick(object, location, params)
|
||||
|
||||
@@ -27,8 +44,31 @@
|
||||
if(h)
|
||||
. = h.CanItemAutoclick(object, location, params)
|
||||
|
||||
/mob/proc/canMobMousedown(object, location, params)
|
||||
|
||||
/mob/living/carbon/canMobMousedown(atom/object, location, params)
|
||||
var/obj/item/H = get_active_held_item()
|
||||
if(H)
|
||||
. = H.canItemMouseDown(object, location, params)
|
||||
|
||||
/obj/item/proc/CanItemAutoclick(object, location, params)
|
||||
|
||||
/obj/item/proc/canItemMouseDown(object, location, params)
|
||||
if(canMouseDown)
|
||||
return src
|
||||
|
||||
/obj/item/proc/onMouseDown(object, location, params, mob)
|
||||
return
|
||||
|
||||
/obj/item/proc/onMouseUp(object, location, params, mob)
|
||||
return
|
||||
|
||||
/obj/item/proc/onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
|
||||
return
|
||||
|
||||
/obj/item
|
||||
var/canMouseDown = FALSE
|
||||
|
||||
/obj/item/weapon/gun
|
||||
var/automatic = 0 //can gun use it, 0 is no, anything above 0 is the delay between clicks in ds
|
||||
|
||||
@@ -42,4 +82,11 @@
|
||||
. = 0
|
||||
|
||||
/obj/screen/click_catcher/IsAutoclickable()
|
||||
. = 1
|
||||
. = 1
|
||||
|
||||
//Please don't roast me too hard
|
||||
/client/MouseMove(object,location,control,params)
|
||||
mouseParams = params
|
||||
mouseLocation = location
|
||||
mouseObject = object
|
||||
mouseControlObject = control
|
||||
|
||||
+448
-447
@@ -1,149 +1,149 @@
|
||||
/*
|
||||
Click code cleanup
|
||||
~Sayu
|
||||
*/
|
||||
|
||||
// 1 decisecond click delay (above and beyond mob/next_move)
|
||||
//This is mainly modified by click code, to modify click delays elsewhere, use next_move and changeNext_move()
|
||||
/mob/var/next_click = 0
|
||||
|
||||
// THESE DO NOT EFFECT THE BASE 1 DECISECOND DELAY OF NEXT_CLICK
|
||||
/mob/var/next_move_adjust = 0 //Amount to adjust action/click delays by, + or -
|
||||
/mob/var/next_move_modifier = 1 //Value to multiply action/click delays by
|
||||
|
||||
|
||||
//Delays the mob's next click/action by num deciseconds
|
||||
// eg: 10-3 = 7 deciseconds of delay
|
||||
// eg: 10*0.5 = 5 deciseconds of delay
|
||||
// DOES NOT EFFECT THE BASE 1 DECISECOND DELAY OF NEXT_CLICK
|
||||
|
||||
/mob/proc/changeNext_move(num)
|
||||
next_move = world.time + ((num+next_move_adjust)*next_move_modifier)
|
||||
|
||||
|
||||
/*
|
||||
Before anything else, defer these calls to a per-mobtype handler. This allows us to
|
||||
remove istype() spaghetti code, but requires the addition of other handler procs to simplify it.
|
||||
|
||||
Alternately, you could hardcode every mob's variation in a flat ClickOn() proc; however,
|
||||
that's a lot of code duplication and is hard to maintain.
|
||||
|
||||
Note that this proc can be overridden, and is in the case of screen objects.
|
||||
*/
|
||||
/atom/Click(location,control,params)
|
||||
if(initialized)
|
||||
usr.ClickOn(src, params)
|
||||
|
||||
/atom/DblClick(location,control,params)
|
||||
if(initialized)
|
||||
usr.DblClickOn(src,params)
|
||||
|
||||
/atom/MouseWheel(delta_x,delta_y,location,control,params)
|
||||
if(initialized)
|
||||
usr.MouseWheelOn(src, delta_x, delta_y, params)
|
||||
|
||||
/*
|
||||
Standard mob ClickOn()
|
||||
Handles exceptions: Buildmode, middle click, modified clicks, mech actions
|
||||
|
||||
After that, mostly just check your state, check whether you're holding an item,
|
||||
check whether you're adjacent to the target, then pass off the click to whoever
|
||||
is recieving it.
|
||||
The most common are:
|
||||
* mob/UnarmedAttack(atom,adjacent) - used here only when adjacent, with no item in hand; in the case of humans, checks gloves
|
||||
* atom/attackby(item,user) - used only when adjacent
|
||||
* item/afterattack(atom,user,adjacent,params) - used both ranged and adjacent
|
||||
* mob/RangedAttack(atom,params) - used only ranged, only used for tk and laser eyes but could be changed
|
||||
*/
|
||||
/mob/proc/ClickOn( atom/A, params )
|
||||
if(world.time <= next_click)
|
||||
return
|
||||
next_click = world.time + 1
|
||||
|
||||
if(client && client.click_intercept)
|
||||
if(call(client.click_intercept, "InterceptClickOn")(src, params, A))
|
||||
return
|
||||
|
||||
var/list/modifiers = params2list(params)
|
||||
if(modifiers["shift"] && modifiers["middle"])
|
||||
ShiftMiddleClickOn(A)
|
||||
return
|
||||
if(modifiers["shift"] && modifiers["ctrl"])
|
||||
CtrlShiftClickOn(A)
|
||||
return
|
||||
if(modifiers["middle"])
|
||||
MiddleClickOn(A)
|
||||
return
|
||||
if(modifiers["shift"])
|
||||
ShiftClickOn(A)
|
||||
return
|
||||
if(modifiers["alt"]) // alt and alt-gr (rightalt)
|
||||
AltClickOn(A)
|
||||
return
|
||||
if(modifiers["ctrl"])
|
||||
CtrlClickOn(A)
|
||||
return
|
||||
|
||||
if(incapacitated(ignore_restraints = 1))
|
||||
return
|
||||
|
||||
face_atom(A)
|
||||
|
||||
if(next_move > world.time) // in the year 2000...
|
||||
return
|
||||
|
||||
if(A.IsObscured())
|
||||
/*
|
||||
Click code cleanup
|
||||
~Sayu
|
||||
*/
|
||||
|
||||
// 1 decisecond click delay (above and beyond mob/next_move)
|
||||
//This is mainly modified by click code, to modify click delays elsewhere, use next_move and changeNext_move()
|
||||
/mob/var/next_click = 0
|
||||
|
||||
// THESE DO NOT EFFECT THE BASE 1 DECISECOND DELAY OF NEXT_CLICK
|
||||
/mob/var/next_move_adjust = 0 //Amount to adjust action/click delays by, + or -
|
||||
/mob/var/next_move_modifier = 1 //Value to multiply action/click delays by
|
||||
|
||||
|
||||
//Delays the mob's next click/action by num deciseconds
|
||||
// eg: 10-3 = 7 deciseconds of delay
|
||||
// eg: 10*0.5 = 5 deciseconds of delay
|
||||
// DOES NOT EFFECT THE BASE 1 DECISECOND DELAY OF NEXT_CLICK
|
||||
|
||||
/mob/proc/changeNext_move(num)
|
||||
next_move = world.time + ((num+next_move_adjust)*next_move_modifier)
|
||||
|
||||
|
||||
/*
|
||||
Before anything else, defer these calls to a per-mobtype handler. This allows us to
|
||||
remove istype() spaghetti code, but requires the addition of other handler procs to simplify it.
|
||||
|
||||
Alternately, you could hardcode every mob's variation in a flat ClickOn() proc; however,
|
||||
that's a lot of code duplication and is hard to maintain.
|
||||
|
||||
Note that this proc can be overridden, and is in the case of screen objects.
|
||||
*/
|
||||
/atom/Click(location,control,params)
|
||||
if(initialized)
|
||||
usr.ClickOn(src, params)
|
||||
|
||||
/atom/DblClick(location,control,params)
|
||||
if(initialized)
|
||||
usr.DblClickOn(src,params)
|
||||
|
||||
/atom/MouseWheel(delta_x,delta_y,location,control,params)
|
||||
if(initialized)
|
||||
usr.MouseWheelOn(src, delta_x, delta_y, params)
|
||||
|
||||
/*
|
||||
Standard mob ClickOn()
|
||||
Handles exceptions: Buildmode, middle click, modified clicks, mech actions
|
||||
|
||||
After that, mostly just check your state, check whether you're holding an item,
|
||||
check whether you're adjacent to the target, then pass off the click to whoever
|
||||
is recieving it.
|
||||
The most common are:
|
||||
* mob/UnarmedAttack(atom,adjacent) - used here only when adjacent, with no item in hand; in the case of humans, checks gloves
|
||||
* atom/attackby(item,user) - used only when adjacent
|
||||
* item/afterattack(atom,user,adjacent,params) - used both ranged and adjacent
|
||||
* mob/RangedAttack(atom,params) - used only ranged, only used for tk and laser eyes but could be changed
|
||||
*/
|
||||
/mob/proc/ClickOn( atom/A, params )
|
||||
if(world.time <= next_click)
|
||||
return
|
||||
next_click = world.time + 1
|
||||
|
||||
if(client && client.click_intercept)
|
||||
if(call(client.click_intercept, "InterceptClickOn")(src, params, A))
|
||||
return
|
||||
|
||||
var/list/modifiers = params2list(params)
|
||||
if(modifiers["shift"] && modifiers["middle"])
|
||||
ShiftMiddleClickOn(A)
|
||||
return
|
||||
if(modifiers["shift"] && modifiers["ctrl"])
|
||||
CtrlShiftClickOn(A)
|
||||
return
|
||||
if(modifiers["middle"])
|
||||
MiddleClickOn(A)
|
||||
return
|
||||
if(modifiers["shift"])
|
||||
ShiftClickOn(A)
|
||||
return
|
||||
if(modifiers["alt"]) // alt and alt-gr (rightalt)
|
||||
AltClickOn(A)
|
||||
return
|
||||
if(modifiers["ctrl"])
|
||||
CtrlClickOn(A)
|
||||
return
|
||||
|
||||
if(istype(loc,/obj/mecha))
|
||||
var/obj/mecha/M = loc
|
||||
return M.click_action(A,src,params)
|
||||
|
||||
if(restrained())
|
||||
changeNext_move(CLICK_CD_HANDCUFFED) //Doing shit in cuffs shall be vey slow
|
||||
RestrainedClickOn(A)
|
||||
return
|
||||
|
||||
if(in_throw_mode)
|
||||
throw_item(A)
|
||||
return
|
||||
|
||||
var/obj/item/W = get_active_held_item()
|
||||
|
||||
if(W == A)
|
||||
W.attack_self(src)
|
||||
update_inv_hands()
|
||||
return
|
||||
|
||||
//These are always reachable.
|
||||
//User itself, current loc, and user inventory
|
||||
if(DirectAccess(A))
|
||||
if(W)
|
||||
melee_item_attack_chain(src,W,A,params)
|
||||
else
|
||||
if(ismob(A))
|
||||
changeNext_move(CLICK_CD_MELEE)
|
||||
UnarmedAttack(A)
|
||||
return
|
||||
|
||||
//Can't reach anything else in lockers or other weirdness
|
||||
if(!loc.AllowClick())
|
||||
return
|
||||
|
||||
//Standard reach turf to turf or reaching inside storage
|
||||
if(CanReach(A,W))
|
||||
if(W)
|
||||
melee_item_attack_chain(src,W,A,params)
|
||||
else
|
||||
if(ismob(A))
|
||||
changeNext_move(CLICK_CD_MELEE)
|
||||
UnarmedAttack(A,1)
|
||||
else
|
||||
if(W)
|
||||
W.afterattack(A,src,0,params)
|
||||
else
|
||||
RangedAttack(A,params)
|
||||
|
||||
if(incapacitated(ignore_restraints = 1))
|
||||
return
|
||||
|
||||
face_atom(A)
|
||||
|
||||
if(next_move > world.time) // in the year 2000...
|
||||
return
|
||||
|
||||
if(!modifiers["catcher"] && A.IsObscured())
|
||||
return
|
||||
|
||||
if(istype(loc,/obj/mecha))
|
||||
var/obj/mecha/M = loc
|
||||
return M.click_action(A,src,params)
|
||||
|
||||
if(restrained())
|
||||
changeNext_move(CLICK_CD_HANDCUFFED) //Doing shit in cuffs shall be vey slow
|
||||
RestrainedClickOn(A)
|
||||
return
|
||||
|
||||
if(in_throw_mode)
|
||||
throw_item(A)
|
||||
return
|
||||
|
||||
var/obj/item/W = get_active_held_item()
|
||||
|
||||
if(W == A)
|
||||
W.attack_self(src)
|
||||
update_inv_hands()
|
||||
return
|
||||
|
||||
//These are always reachable.
|
||||
//User itself, current loc, and user inventory
|
||||
if(DirectAccess(A))
|
||||
if(W)
|
||||
W.melee_attack_chain(src, A, params)
|
||||
else
|
||||
if(ismob(A))
|
||||
changeNext_move(CLICK_CD_MELEE)
|
||||
UnarmedAttack(A)
|
||||
return
|
||||
|
||||
//Can't reach anything else in lockers or other weirdness
|
||||
if(!loc.AllowClick())
|
||||
return
|
||||
|
||||
//Standard reach turf to turf or reaching inside storage
|
||||
if(CanReach(A,W))
|
||||
if(W)
|
||||
W.melee_attack_chain(src, A, params)
|
||||
else
|
||||
if(ismob(A))
|
||||
changeNext_move(CLICK_CD_MELEE)
|
||||
UnarmedAttack(A,1)
|
||||
else
|
||||
if(W)
|
||||
W.afterattack(A,src,0,params)
|
||||
else
|
||||
RangedAttack(A,params)
|
||||
|
||||
//Is the atom obscured by a PREVENT_CLICK_UNDER object above it
|
||||
/atom/proc/IsObscured()
|
||||
if(!isturf(loc)) //This only makes sense for things directly on turfs for now
|
||||
@@ -162,306 +162,307 @@
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/atom/movable/proc/CanReach(atom/target,obj/item/tool,view_only = FALSE)
|
||||
if(isturf(target) || isturf(target.loc) || DirectAccess(target)) //Directly accessible atoms
|
||||
if(Adjacent(target) || (tool && CheckToolReach(src, target, tool.reach))) //Adjacent or reaching attacks
|
||||
return TRUE
|
||||
else
|
||||
//Things inside storage insde another storage
|
||||
//Eg Contents of a box in a backpack
|
||||
var/atom/outer_storage = get_atom_on_turf(target)
|
||||
if(outer_storage == target) //whatever that is we don't want infinite loop.
|
||||
return FALSE
|
||||
if(outer_storage && CanReach(outer_storage,tool) && outer_storage.CanReachStorage(target,src,view_only ? STORAGE_VIEW_DEPTH : INVENTORY_DEPTH))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//Can [target] in this container be reached by [user], can't be more than [depth] levels deep
|
||||
/atom/proc/CanReachStorage(atom/target,user,depth)
|
||||
return FALSE
|
||||
|
||||
/obj/item/weapon/storage/CanReachStorage(atom/target,user,depth)
|
||||
while(target && depth > 0)
|
||||
target = target.loc
|
||||
depth--
|
||||
if(target == src)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/atom/movable/proc/DirectAccess(atom/target)
|
||||
if(target == src)
|
||||
return TRUE
|
||||
if(target == loc)
|
||||
return TRUE
|
||||
|
||||
/mob/DirectAccess(atom/target)
|
||||
if(..())
|
||||
return TRUE
|
||||
if(target in contents) //This could probably use moving down and restricting to inventory only
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/DirectAccess(atom/target)
|
||||
if(..()) //Lightweight checks first
|
||||
return TRUE
|
||||
if(target in GetAllContents())
|
||||
return TRUE
|
||||
|
||||
/atom/proc/AllowClick()
|
||||
return FALSE
|
||||
|
||||
/turf/AllowClick()
|
||||
return TRUE
|
||||
|
||||
/proc/CheckToolReach(atom/movable/here, atom/movable/there, reach)
|
||||
if(!here || !there)
|
||||
return
|
||||
switch(reach)
|
||||
if(0)
|
||||
return FALSE
|
||||
if(1)
|
||||
return FALSE //here.Adjacent(there)
|
||||
if(2 to INFINITY)
|
||||
var/obj/dummy = new(get_turf(here))
|
||||
dummy.pass_flags |= PASSTABLE
|
||||
dummy.invisibility = INVISIBILITY_ABSTRACT
|
||||
for(var/i in 1 to reach) //Limit it to that many tries
|
||||
var/turf/T = get_step(dummy, get_dir(dummy, there))
|
||||
if(dummy.CanReach(there))
|
||||
qdel(dummy)
|
||||
return TRUE
|
||||
if(!dummy.Move(T)) //we're blocked!
|
||||
qdel(dummy)
|
||||
return
|
||||
qdel(dummy)
|
||||
|
||||
// Default behavior: ignore double clicks (the second click that makes the doubleclick call already calls for a normal click)
|
||||
/mob/proc/DblClickOn(atom/A, params)
|
||||
return
|
||||
|
||||
|
||||
/*
|
||||
Translates into attack_hand, etc.
|
||||
|
||||
Note: proximity_flag here is used to distinguish between normal usage (flag=1),
|
||||
and usage when clicking on things telekinetically (flag=0). This proc will
|
||||
not be called at ranged except with telekinesis.
|
||||
|
||||
proximity_flag is not currently passed to attack_hand, and is instead used
|
||||
in human click code to allow glove touches only at melee range.
|
||||
*/
|
||||
/mob/proc/UnarmedAttack(atom/A, proximity_flag)
|
||||
if(ismob(A))
|
||||
changeNext_move(CLICK_CD_MELEE)
|
||||
return
|
||||
|
||||
/*
|
||||
Ranged unarmed attack:
|
||||
|
||||
This currently is just a default for all mobs, involving
|
||||
laser eyes and telekinesis. You could easily add exceptions
|
||||
for things like ranged glove touches, spitting alien acid/neurotoxin,
|
||||
animals lunging, etc.
|
||||
*/
|
||||
/mob/proc/RangedAttack(atom/A, params)
|
||||
/*
|
||||
Restrained ClickOn
|
||||
|
||||
Used when you are handcuffed and click things.
|
||||
Not currently used by anything but could easily be.
|
||||
*/
|
||||
/mob/proc/RestrainedClickOn(atom/A)
|
||||
return
|
||||
|
||||
/*
|
||||
Middle click
|
||||
Only used for swapping hands
|
||||
*/
|
||||
/mob/proc/MiddleClickOn(atom/A)
|
||||
return
|
||||
|
||||
/mob/living/carbon/MiddleClickOn(atom/A)
|
||||
if(!src.stat && src.mind && src.mind.changeling && src.mind.changeling.chosen_sting && (istype(A, /mob/living/carbon)) && (A != src))
|
||||
next_click = world.time + 5
|
||||
mind.changeling.chosen_sting.try_to_sting(src, A)
|
||||
else
|
||||
swap_hand()
|
||||
|
||||
/mob/living/simple_animal/drone/MiddleClickOn(atom/A)
|
||||
swap_hand()
|
||||
|
||||
// In case of use break glass
|
||||
/*
|
||||
/atom/proc/MiddleClick(mob/M as mob)
|
||||
return
|
||||
*/
|
||||
|
||||
/*
|
||||
Shift click
|
||||
For most mobs, examine.
|
||||
This is overridden in ai.dm
|
||||
*/
|
||||
/mob/proc/ShiftClickOn(atom/A)
|
||||
A.ShiftClick(src)
|
||||
return
|
||||
/atom/proc/ShiftClick(mob/user)
|
||||
if(user.client && user.client.eye == user || user.client.eye == user.loc)
|
||||
user.examinate(src)
|
||||
return
|
||||
|
||||
/*
|
||||
Ctrl click
|
||||
For most objects, pull
|
||||
*/
|
||||
|
||||
/mob/proc/CtrlClickOn(atom/A)
|
||||
A.CtrlClick(src)
|
||||
return
|
||||
|
||||
/atom/proc/CtrlClick(mob/user)
|
||||
var/mob/living/ML = user
|
||||
if(istype(ML))
|
||||
ML.pulled(src)
|
||||
|
||||
/mob/living/carbon/human/CtrlClick(mob/user)
|
||||
if(ishuman(user) && Adjacent(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
H.dna.species.grab(H, src, H.martial_art)
|
||||
H.next_click = world.time + CLICK_CD_MELEE
|
||||
else
|
||||
..()
|
||||
/*
|
||||
Alt click
|
||||
Unused except for AI
|
||||
*/
|
||||
/mob/proc/AltClickOn(atom/A)
|
||||
A.AltClick(src)
|
||||
return
|
||||
|
||||
/mob/living/carbon/AltClickOn(atom/A)
|
||||
if(!src.stat && src.mind && src.mind.changeling && src.mind.changeling.chosen_sting && (istype(A, /mob/living/carbon)) && (A != src))
|
||||
next_click = world.time + 5
|
||||
mind.changeling.chosen_sting.try_to_sting(src, A)
|
||||
else
|
||||
..()
|
||||
|
||||
/atom/proc/AltClick(mob/user)
|
||||
var/turf/T = get_turf(src)
|
||||
if(T && user.TurfAdjacent(T))
|
||||
if(user.listed_turf == T)
|
||||
user.listed_turf = null
|
||||
else
|
||||
user.listed_turf = T
|
||||
user.client.statpanel = T.name
|
||||
return
|
||||
|
||||
/mob/proc/TurfAdjacent(turf/T)
|
||||
return T.Adjacent(src)
|
||||
|
||||
/*
|
||||
Control+Shift click
|
||||
Unused except for AI
|
||||
*/
|
||||
/mob/proc/CtrlShiftClickOn(atom/A)
|
||||
A.CtrlShiftClick(src)
|
||||
return
|
||||
|
||||
/mob/proc/ShiftMiddleClickOn(atom/A)
|
||||
src.pointed(A)
|
||||
return
|
||||
|
||||
/atom/proc/CtrlShiftClick(mob/user)
|
||||
return
|
||||
|
||||
/*
|
||||
Misc helpers
|
||||
|
||||
Laser Eyes: as the name implies, handles this since nothing else does currently
|
||||
face_atom: turns the mob towards what you clicked on
|
||||
*/
|
||||
/mob/proc/LaserEyes(atom/A)
|
||||
return
|
||||
|
||||
/mob/living/LaserEyes(atom/A)
|
||||
changeNext_move(CLICK_CD_RANGE)
|
||||
var/turf/T = get_turf(src)
|
||||
var/turf/U = get_turf(A)
|
||||
|
||||
var/obj/item/projectile/beam/LE = new /obj/item/projectile/beam( loc )
|
||||
LE.icon = 'icons/effects/genetics.dmi'
|
||||
LE.icon_state = "eyelasers"
|
||||
playsound(usr.loc, 'sound/weapons/taser2.ogg', 75, 1)
|
||||
|
||||
LE.firer = src
|
||||
LE.def_zone = get_organ_target()
|
||||
LE.original = A
|
||||
LE.current = T
|
||||
LE.yo = U.y - T.y
|
||||
LE.xo = U.x - T.x
|
||||
LE.fire()
|
||||
|
||||
// Simple helper to face what you clicked on, in case it should be needed in more than one place
|
||||
/mob/proc/face_atom(atom/A)
|
||||
if( buckled || stat != CONSCIOUS || !A || !x || !y || !A.x || !A.y )
|
||||
return
|
||||
var/dx = A.x - x
|
||||
var/dy = A.y - y
|
||||
if(!dx && !dy) // Wall items are graphically shifted but on the floor
|
||||
if(A.pixel_y > 16)
|
||||
setDir(NORTH)
|
||||
else if(A.pixel_y < -16)
|
||||
setDir(SOUTH)
|
||||
else if(A.pixel_x > 16)
|
||||
setDir(EAST)
|
||||
else if(A.pixel_x < -16)
|
||||
setDir(WEST)
|
||||
return
|
||||
|
||||
if(abs(dx) < abs(dy))
|
||||
if(dy > 0)
|
||||
setDir(NORTH)
|
||||
else
|
||||
setDir(SOUTH)
|
||||
else
|
||||
if(dx > 0)
|
||||
setDir(EAST)
|
||||
else
|
||||
setDir(WEST)
|
||||
|
||||
/obj/screen/click_catcher
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
icon_state = "click_catcher"
|
||||
plane = CLICKCATCHER_PLANE
|
||||
mouse_opacity = 2
|
||||
screen_loc = "CENTER"
|
||||
|
||||
/obj/screen/click_catcher/New()
|
||||
..()
|
||||
transform = matrix(200, 0, 0, 0, 200, 0)
|
||||
|
||||
/obj/screen/click_catcher/Click(location, control, params)
|
||||
var/list/modifiers = params2list(params)
|
||||
if(modifiers["middle"] && istype(usr, /mob/living/carbon))
|
||||
var/mob/living/carbon/C = usr
|
||||
C.swap_hand()
|
||||
else
|
||||
var/turf/T = params2turf(modifiers["screen-loc"], get_turf(usr))
|
||||
if(T)
|
||||
T.Click(location, control, params)
|
||||
. = 1
|
||||
|
||||
|
||||
/* MouseWheelOn */
|
||||
|
||||
/mob/proc/MouseWheelOn(atom/A, delta_x, delta_y, params)
|
||||
return
|
||||
|
||||
/mob/dead/observer/MouseWheelOn(atom/A, delta_x, delta_y, params)
|
||||
var/list/modifier = params2list(params)
|
||||
if(modifier["shift"])
|
||||
var/view = 0
|
||||
if(delta_y > 0)
|
||||
view = -1
|
||||
else
|
||||
view = 1
|
||||
add_view_range(view)
|
||||
/atom/movable/proc/CanReach(atom/target,obj/item/tool,view_only = FALSE)
|
||||
if(isturf(target) || isturf(target.loc) || DirectAccess(target)) //Directly accessible atoms
|
||||
if(Adjacent(target) || (tool && CheckToolReach(src, target, tool.reach))) //Adjacent or reaching attacks
|
||||
return TRUE
|
||||
else
|
||||
//Things inside storage insde another storage
|
||||
//Eg Contents of a box in a backpack
|
||||
var/atom/outer_storage = get_atom_on_turf(target)
|
||||
if(outer_storage == target) //whatever that is we don't want infinite loop.
|
||||
return FALSE
|
||||
if(outer_storage && CanReach(outer_storage,tool) && outer_storage.CanReachStorage(target,src,view_only ? STORAGE_VIEW_DEPTH : INVENTORY_DEPTH))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
//Can [target] in this container be reached by [user], can't be more than [depth] levels deep
|
||||
/atom/proc/CanReachStorage(atom/target,user,depth)
|
||||
return FALSE
|
||||
|
||||
/obj/item/weapon/storage/CanReachStorage(atom/target,user,depth)
|
||||
while(target && depth > 0)
|
||||
target = target.loc
|
||||
depth--
|
||||
if(target == src)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/atom/movable/proc/DirectAccess(atom/target)
|
||||
if(target == src)
|
||||
return TRUE
|
||||
if(target == loc)
|
||||
return TRUE
|
||||
|
||||
/mob/DirectAccess(atom/target)
|
||||
if(..())
|
||||
return TRUE
|
||||
if(target in contents) //This could probably use moving down and restricting to inventory only
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/DirectAccess(atom/target)
|
||||
if(..()) //Lightweight checks first
|
||||
return TRUE
|
||||
if(target in GetAllContents())
|
||||
return TRUE
|
||||
|
||||
/atom/proc/AllowClick()
|
||||
return FALSE
|
||||
|
||||
/turf/AllowClick()
|
||||
return TRUE
|
||||
|
||||
/proc/CheckToolReach(atom/movable/here, atom/movable/there, reach)
|
||||
if(!here || !there)
|
||||
return
|
||||
switch(reach)
|
||||
if(0)
|
||||
return FALSE
|
||||
if(1)
|
||||
return FALSE //here.Adjacent(there)
|
||||
if(2 to INFINITY)
|
||||
var/obj/dummy = new(get_turf(here))
|
||||
dummy.pass_flags |= PASSTABLE
|
||||
dummy.invisibility = INVISIBILITY_ABSTRACT
|
||||
for(var/i in 1 to reach) //Limit it to that many tries
|
||||
var/turf/T = get_step(dummy, get_dir(dummy, there))
|
||||
if(dummy.CanReach(there))
|
||||
qdel(dummy)
|
||||
return TRUE
|
||||
if(!dummy.Move(T)) //we're blocked!
|
||||
qdel(dummy)
|
||||
return
|
||||
qdel(dummy)
|
||||
|
||||
// Default behavior: ignore double clicks (the second click that makes the doubleclick call already calls for a normal click)
|
||||
/mob/proc/DblClickOn(atom/A, params)
|
||||
return
|
||||
|
||||
|
||||
/*
|
||||
Translates into attack_hand, etc.
|
||||
|
||||
Note: proximity_flag here is used to distinguish between normal usage (flag=1),
|
||||
and usage when clicking on things telekinetically (flag=0). This proc will
|
||||
not be called at ranged except with telekinesis.
|
||||
|
||||
proximity_flag is not currently passed to attack_hand, and is instead used
|
||||
in human click code to allow glove touches only at melee range.
|
||||
*/
|
||||
/mob/proc/UnarmedAttack(atom/A, proximity_flag)
|
||||
if(ismob(A))
|
||||
changeNext_move(CLICK_CD_MELEE)
|
||||
return
|
||||
|
||||
/*
|
||||
Ranged unarmed attack:
|
||||
|
||||
This currently is just a default for all mobs, involving
|
||||
laser eyes and telekinesis. You could easily add exceptions
|
||||
for things like ranged glove touches, spitting alien acid/neurotoxin,
|
||||
animals lunging, etc.
|
||||
*/
|
||||
/mob/proc/RangedAttack(atom/A, params)
|
||||
/*
|
||||
Restrained ClickOn
|
||||
|
||||
Used when you are handcuffed and click things.
|
||||
Not currently used by anything but could easily be.
|
||||
*/
|
||||
/mob/proc/RestrainedClickOn(atom/A)
|
||||
return
|
||||
|
||||
/*
|
||||
Middle click
|
||||
Only used for swapping hands
|
||||
*/
|
||||
/mob/proc/MiddleClickOn(atom/A)
|
||||
return
|
||||
|
||||
/mob/living/carbon/MiddleClickOn(atom/A)
|
||||
if(!src.stat && src.mind && src.mind.changeling && src.mind.changeling.chosen_sting && (istype(A, /mob/living/carbon)) && (A != src))
|
||||
next_click = world.time + 5
|
||||
mind.changeling.chosen_sting.try_to_sting(src, A)
|
||||
else
|
||||
swap_hand()
|
||||
|
||||
/mob/living/simple_animal/drone/MiddleClickOn(atom/A)
|
||||
swap_hand()
|
||||
|
||||
// In case of use break glass
|
||||
/*
|
||||
/atom/proc/MiddleClick(mob/M as mob)
|
||||
return
|
||||
*/
|
||||
|
||||
/*
|
||||
Shift click
|
||||
For most mobs, examine.
|
||||
This is overridden in ai.dm
|
||||
*/
|
||||
/mob/proc/ShiftClickOn(atom/A)
|
||||
A.ShiftClick(src)
|
||||
return
|
||||
/atom/proc/ShiftClick(mob/user)
|
||||
if(user.client && user.client.eye == user || user.client.eye == user.loc)
|
||||
user.examinate(src)
|
||||
return
|
||||
|
||||
/*
|
||||
Ctrl click
|
||||
For most objects, pull
|
||||
*/
|
||||
|
||||
/mob/proc/CtrlClickOn(atom/A)
|
||||
A.CtrlClick(src)
|
||||
return
|
||||
|
||||
/atom/proc/CtrlClick(mob/user)
|
||||
var/mob/living/ML = user
|
||||
if(istype(ML))
|
||||
ML.pulled(src)
|
||||
|
||||
/mob/living/carbon/human/CtrlClick(mob/user)
|
||||
if(ishuman(user) && Adjacent(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
H.dna.species.grab(H, src, H.mind.martial_art)
|
||||
H.changeNext_move(CLICK_CD_MELEE)
|
||||
else
|
||||
..()
|
||||
/*
|
||||
Alt click
|
||||
Unused except for AI
|
||||
*/
|
||||
/mob/proc/AltClickOn(atom/A)
|
||||
A.AltClick(src)
|
||||
return
|
||||
|
||||
/mob/living/carbon/AltClickOn(atom/A)
|
||||
if(!src.stat && src.mind && src.mind.changeling && src.mind.changeling.chosen_sting && (istype(A, /mob/living/carbon)) && (A != src))
|
||||
next_click = world.time + 5
|
||||
mind.changeling.chosen_sting.try_to_sting(src, A)
|
||||
else
|
||||
..()
|
||||
|
||||
/atom/proc/AltClick(mob/user)
|
||||
var/turf/T = get_turf(src)
|
||||
if(T && user.TurfAdjacent(T))
|
||||
if(user.listed_turf == T)
|
||||
user.listed_turf = null
|
||||
else
|
||||
user.listed_turf = T
|
||||
user.client.statpanel = T.name
|
||||
return
|
||||
|
||||
/mob/proc/TurfAdjacent(turf/T)
|
||||
return T.Adjacent(src)
|
||||
|
||||
/*
|
||||
Control+Shift click
|
||||
Unused except for AI
|
||||
*/
|
||||
/mob/proc/CtrlShiftClickOn(atom/A)
|
||||
A.CtrlShiftClick(src)
|
||||
return
|
||||
|
||||
/mob/proc/ShiftMiddleClickOn(atom/A)
|
||||
src.pointed(A)
|
||||
return
|
||||
|
||||
/atom/proc/CtrlShiftClick(mob/user)
|
||||
return
|
||||
|
||||
/*
|
||||
Misc helpers
|
||||
|
||||
Laser Eyes: as the name implies, handles this since nothing else does currently
|
||||
face_atom: turns the mob towards what you clicked on
|
||||
*/
|
||||
/mob/proc/LaserEyes(atom/A)
|
||||
return
|
||||
|
||||
/mob/living/LaserEyes(atom/A)
|
||||
changeNext_move(CLICK_CD_RANGE)
|
||||
var/turf/T = get_turf(src)
|
||||
var/turf/U = get_turf(A)
|
||||
|
||||
var/obj/item/projectile/beam/LE = new /obj/item/projectile/beam( loc )
|
||||
LE.icon = 'icons/effects/genetics.dmi'
|
||||
LE.icon_state = "eyelasers"
|
||||
playsound(usr.loc, 'sound/weapons/taser2.ogg', 75, 1)
|
||||
|
||||
LE.firer = src
|
||||
LE.def_zone = get_organ_target()
|
||||
LE.original = A
|
||||
LE.current = T
|
||||
LE.yo = U.y - T.y
|
||||
LE.xo = U.x - T.x
|
||||
LE.fire()
|
||||
|
||||
// Simple helper to face what you clicked on, in case it should be needed in more than one place
|
||||
/mob/proc/face_atom(atom/A)
|
||||
if( buckled || stat != CONSCIOUS || !A || !x || !y || !A.x || !A.y )
|
||||
return
|
||||
var/dx = A.x - x
|
||||
var/dy = A.y - y
|
||||
if(!dx && !dy) // Wall items are graphically shifted but on the floor
|
||||
if(A.pixel_y > 16)
|
||||
setDir(NORTH)
|
||||
else if(A.pixel_y < -16)
|
||||
setDir(SOUTH)
|
||||
else if(A.pixel_x > 16)
|
||||
setDir(EAST)
|
||||
else if(A.pixel_x < -16)
|
||||
setDir(WEST)
|
||||
return
|
||||
|
||||
if(abs(dx) < abs(dy))
|
||||
if(dy > 0)
|
||||
setDir(NORTH)
|
||||
else
|
||||
setDir(SOUTH)
|
||||
else
|
||||
if(dx > 0)
|
||||
setDir(EAST)
|
||||
else
|
||||
setDir(WEST)
|
||||
|
||||
/obj/screen/click_catcher
|
||||
icon = 'icons/mob/screen_gen.dmi'
|
||||
icon_state = "click_catcher"
|
||||
plane = CLICKCATCHER_PLANE
|
||||
mouse_opacity = 2
|
||||
screen_loc = "CENTER"
|
||||
|
||||
/obj/screen/click_catcher/New()
|
||||
..()
|
||||
transform = matrix(200, 0, 0, 0, 200, 0)
|
||||
|
||||
/obj/screen/click_catcher/Click(location, control, params)
|
||||
var/list/modifiers = params2list(params)
|
||||
if(modifiers["middle"] && istype(usr, /mob/living/carbon))
|
||||
var/mob/living/carbon/C = usr
|
||||
C.swap_hand()
|
||||
else
|
||||
var/turf/T = params2turf(modifiers["screen-loc"], get_turf(usr))
|
||||
params += "&catcher=1"
|
||||
if(T)
|
||||
T.Click(location, control, params)
|
||||
. = 1
|
||||
|
||||
|
||||
/* MouseWheelOn */
|
||||
|
||||
/mob/proc/MouseWheelOn(atom/A, delta_x, delta_y, params)
|
||||
return
|
||||
|
||||
/mob/dead/observer/MouseWheelOn(atom/A, delta_x, delta_y, params)
|
||||
var/list/modifier = params2list(params)
|
||||
if(modifier["shift"])
|
||||
var/view = 0
|
||||
if(delta_y > 0)
|
||||
view = -1
|
||||
else
|
||||
view = 1
|
||||
add_view_range(view)
|
||||
@@ -71,7 +71,7 @@
|
||||
|
||||
// cyborgs are prohibited from using storage items so we can I think safely remove (A.loc in contents)
|
||||
if(A == loc || (A in loc) || (A in contents))
|
||||
melee_item_attack_chain(src, W, A, params)
|
||||
W.melee_attack_chain(src, A, params)
|
||||
return
|
||||
|
||||
if(!isturf(loc))
|
||||
@@ -80,7 +80,7 @@
|
||||
// cyborgs are prohibited from using storage items so we can I think safely remove (A.loc && isturf(A.loc.loc))
|
||||
if(isturf(A) || isturf(A.loc))
|
||||
if(A.Adjacent(src)) // see adjacent.dm
|
||||
melee_item_attack_chain(src, W, A, params)
|
||||
W.melee_attack_chain(src, A, params)
|
||||
return
|
||||
else
|
||||
W.afterattack(A, src, 0, params)
|
||||
|
||||
@@ -271,8 +271,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
|
||||
/obj/screen/alert/bloodsense/Initialize()
|
||||
. = ..()
|
||||
if(!narnar)
|
||||
narnar = new('icons/mob/screen_alert.dmi', "mini_nar")
|
||||
narnar = new('icons/mob/screen_alert.dmi', "mini_nar")
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
|
||||
/obj/screen/alert/bloodsense/Destroy()
|
||||
@@ -307,7 +306,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
angle = 0
|
||||
cut_overlays()
|
||||
icon_state = "runed_sense1"
|
||||
desc = "The sacrifice is complete, bring the wrath of Nar-Sie upon the crew!"
|
||||
desc = "The sacrifice is complete, summon Nar-Sie! The summoning can only take place in [english_list(GLOB.summon_spots)]!"
|
||||
add_overlay(narnar)
|
||||
return
|
||||
var/turf/P = get_turf(blood_target)
|
||||
@@ -362,7 +361,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
name = "Next Tier Requirements"
|
||||
desc = "You shouldn't be seeing this description unless you're very fast. If you're very fast, good job!"
|
||||
icon_state = "no-servants-caches"
|
||||
var/static/list/scripture_states = list(SCRIPTURE_DRIVER = TRUE, SCRIPTURE_SCRIPT = FALSE, SCRIPTURE_APPLICATION = FALSE, SCRIPTURE_REVENANT = FALSE, SCRIPTURE_JUDGEMENT = FALSE)
|
||||
|
||||
|
||||
/obj/screen/alert/clockwork/scripture_reqs/Initialize()
|
||||
. = ..()
|
||||
@@ -375,12 +374,11 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
|
||||
/obj/screen/alert/clockwork/scripture_reqs/process()
|
||||
if(GLOB.clockwork_gateway_activated)
|
||||
qdel(src)
|
||||
mob_viewer.clear_alert("scripturereq")
|
||||
return
|
||||
var/current_state
|
||||
scripture_states = scripture_unlock_check()
|
||||
for(var/i in scripture_states)
|
||||
if(!scripture_states[i])
|
||||
for(var/i in SSticker.scripture_states)
|
||||
if(!SSticker.scripture_states[i])
|
||||
current_state = i
|
||||
break
|
||||
icon_state = "no"
|
||||
@@ -460,7 +458,6 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
var/servants = 0
|
||||
var/validservants = 0
|
||||
var/unconverted_ais_exist = get_unconverted_ais()
|
||||
var/list/scripture_states = scripture_unlock_check()
|
||||
var/list/textlist
|
||||
for(var/mob/living/L in GLOB.living_mob_list)
|
||||
if(is_servant_of_ratvar(L))
|
||||
@@ -497,7 +494,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
textlist += "<b>[unconverted_ais_exist] unconverted AIs exist!</b><br>"
|
||||
else
|
||||
textlist += "<b>An unconverted AI exists!</b><br>"
|
||||
if(scripture_states[SCRIPTURE_REVENANT])
|
||||
if(SSticker.scripture_states[SCRIPTURE_REVENANT])
|
||||
var/inathneq_available = GLOB.clockwork_generals_invoked["inath-neq"] <= world.time
|
||||
var/sevtug_available = GLOB.clockwork_generals_invoked["sevtug"] <= world.time
|
||||
var/nezbere_available = GLOB.clockwork_generals_invoked["nezbere"] <= world.time
|
||||
@@ -509,9 +506,9 @@ or shoot a gun to move around via Newton's 3rd Law of Motion."
|
||||
textlist += "Generals available: <b>NONE</b><br>"
|
||||
else
|
||||
textlist += "Generals available: <b>NONE</b><br>"
|
||||
for(var/i in scripture_states)
|
||||
for(var/i in SSticker.scripture_states)
|
||||
if(i != SCRIPTURE_DRIVER) //ignore the always-unlocked stuff
|
||||
textlist += "[i] Scripture: <b>[scripture_states[i] ? "UNLOCKED":"LOCKED"]</b><br>"
|
||||
textlist += "[i] Scripture: <b>[SSticker.scripture_states[i] ? "UNLOCKED":"LOCKED"]</b><br>"
|
||||
desc = textlist.Join()
|
||||
..()
|
||||
|
||||
|
||||
Regular → Executable
+6
@@ -21,6 +21,7 @@
|
||||
C.parallax_layers_cached = list()
|
||||
C.parallax_layers_cached += new /obj/screen/parallax_layer/layer_1(null, C.view)
|
||||
C.parallax_layers_cached += new /obj/screen/parallax_layer/layer_2(null, C.view)
|
||||
C.parallax_layers_cached += new /obj/screen/parallax_layer/layer_3(null, C.view)
|
||||
|
||||
C.parallax_layers = C.parallax_layers_cached.Copy()
|
||||
|
||||
@@ -278,6 +279,11 @@
|
||||
speed = 1
|
||||
layer = 2
|
||||
|
||||
/obj/screen/parallax_layer/layer_3
|
||||
icon_state = "layer3"
|
||||
speed = 1.4
|
||||
layer = 3
|
||||
|
||||
#undef LOOP_NONE
|
||||
#undef LOOP_NORMAL
|
||||
#undef LOOP_REVERSE
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
/obj/screen/plane_master/game_world
|
||||
name = "game world plane master"
|
||||
plane = GAME_PLANE
|
||||
appearance_flags = PLANE_MASTER //should use client color
|
||||
blend_mode = BLEND_OVERLAY
|
||||
|
||||
/obj/screen/plane_master/lighting
|
||||
|
||||
@@ -91,9 +91,9 @@
|
||||
screen_loc = ui_language_menu
|
||||
|
||||
/obj/screen/language_menu/Click()
|
||||
var/mob/M = usr
|
||||
var/datum/language_holder/H = M.get_language_holder()
|
||||
H.open_language_menu(usr)
|
||||
var/mob/M = usr
|
||||
var/datum/language_holder/H = M.get_language_holder()
|
||||
H.open_language_menu(usr)
|
||||
|
||||
/obj/screen/inventory
|
||||
var/slot_id // The indentifier for the slot. It has nothing to do with ID cards.
|
||||
@@ -133,8 +133,8 @@
|
||||
icon_state = icon_empty
|
||||
|
||||
/obj/screen/inventory/hand
|
||||
var/mutable_appearance/handcuff_overlay
|
||||
var/static/mutable_appearance/blocked_overlay = mutable_appearance('icons/mob/screen_gen.dmi', "blocked")
|
||||
var/mutable_appearance/handcuff_overlay
|
||||
var/static/mutable_appearance/blocked_overlay = mutable_appearance('icons/mob/screen_gen.dmi', "blocked")
|
||||
var/held_index = 0
|
||||
|
||||
/obj/screen/inventory/hand/update_icon()
|
||||
@@ -142,7 +142,7 @@
|
||||
|
||||
if(!handcuff_overlay)
|
||||
var/state = (!(held_index % 2)) ? "markus" : "gabrielle"
|
||||
handcuff_overlay = mutable_appearance('icons/mob/screen_gen.dmi', state)
|
||||
handcuff_overlay = mutable_appearance('icons/mob/screen_gen.dmi', state)
|
||||
|
||||
cut_overlays()
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
add_overlay(blocked_overlay)
|
||||
|
||||
if(held_index == hud.mymob.active_hand_index)
|
||||
add_overlay("hand_active")
|
||||
add_overlay("hand_active")
|
||||
|
||||
|
||||
/obj/screen/inventory/hand/Click(location, control, params)
|
||||
@@ -439,7 +439,7 @@
|
||||
|
||||
/obj/screen/zone_sel/update_icon(mob/user)
|
||||
cut_overlays()
|
||||
add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[selecting]"))
|
||||
add_overlay(mutable_appearance('icons/mob/screen_gen.dmi', "[selecting]"))
|
||||
user.zone_selected = selecting
|
||||
|
||||
/obj/screen/zone_sel/alien
|
||||
@@ -447,7 +447,7 @@
|
||||
|
||||
/obj/screen/zone_sel/alien/update_icon(mob/user)
|
||||
cut_overlays()
|
||||
add_overlay(mutable_appearance('icons/mob/screen_alien.dmi', "[selecting]"))
|
||||
add_overlay(mutable_appearance('icons/mob/screen_alien.dmi', "[selecting]"))
|
||||
user.zone_selected = selecting
|
||||
|
||||
/obj/screen/zone_sel/robot
|
||||
@@ -531,25 +531,25 @@
|
||||
screen_loc = ui_health
|
||||
mouse_opacity = 0
|
||||
|
||||
/obj/screen/healths/construct
|
||||
icon = 'icons/mob/screen_construct.dmi'
|
||||
icon_state = "artificer_health0"
|
||||
screen_loc = ui_construct_health
|
||||
mouse_opacity = 0
|
||||
|
||||
/obj/screen/healths/construct
|
||||
icon = 'icons/mob/screen_construct.dmi'
|
||||
icon_state = "artificer_health0"
|
||||
screen_loc = ui_construct_health
|
||||
mouse_opacity = 0
|
||||
|
||||
/obj/screen/healthdoll
|
||||
name = "health doll"
|
||||
screen_loc = ui_healthdoll
|
||||
|
||||
/obj/screen/splash
|
||||
icon = 'config/title_screens/images/blank.png'
|
||||
icon = 'config/title_screens/images/title1.dmi'
|
||||
icon_state = ""
|
||||
screen_loc = "1,1"
|
||||
layer = SPLASHSCREEN_LAYER
|
||||
plane = SPLASHSCREEN_PLANE
|
||||
var/client/holder
|
||||
|
||||
/obj/screen/splash/New(client/C, visible, use_previous_title) //TODO: Make this use INITIALIZE_IMMEDIATE
|
||||
/obj/screen/splash/New(client/C, visible, use_previous_title) //TODO: Make this use INITIALIZE_IMMEDIATE
|
||||
holder = C
|
||||
|
||||
if(!visible)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
/proc/melee_item_attack_chain(mob/user, obj/item/I, atom/target, params)
|
||||
if(I.pre_attackby(target, user, params))
|
||||
/obj/item/proc/melee_attack_chain(mob/user, atom/target, params)
|
||||
if(pre_attackby(target, user, params))
|
||||
// Return 1 in attackby() to prevent afterattack() effects (when safely moving items for example)
|
||||
var/resolved = target.attackby(I,user,params)
|
||||
if(!resolved && target && I)
|
||||
I.afterattack(target, user, 1, params) // 1: clicking something Adjacent
|
||||
var/resolved = target.attackby(src, user, params)
|
||||
if(!resolved && target && !QDELETED(src))
|
||||
afterattack(target, user, 1, params) // 1: clicking something Adjacent
|
||||
|
||||
|
||||
// Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown.
|
||||
|
||||
@@ -11,23 +11,29 @@
|
||||
*/
|
||||
|
||||
/atom/proc/attack_tk(mob/user)
|
||||
if(user.stat)
|
||||
if(user.stat || !tkMaxRangeCheck(user, src))
|
||||
return
|
||||
new /obj/effect/overlay/temp/telekinesis(loc)
|
||||
new /obj/effect/temp_visual/telekinesis(loc)
|
||||
user.UnarmedAttack(src,0) // attack_hand, attack_paw, etc
|
||||
return
|
||||
|
||||
/obj/attack_tk(mob/user)
|
||||
if(user.stat)
|
||||
return
|
||||
if(anchored)
|
||||
return ..()
|
||||
attack_tk_grab(user)
|
||||
|
||||
/obj/item/attack_tk(mob/user)
|
||||
if(user.stat)
|
||||
return
|
||||
attack_tk_grab(user)
|
||||
|
||||
/obj/proc/attack_tk_grab(mob/user)
|
||||
var/obj/item/tk_grab/O = new(src)
|
||||
O.tk_user = user
|
||||
if(O.focus_object(src))
|
||||
user.put_in_active_hand(O)
|
||||
else
|
||||
qdel(O)
|
||||
..()
|
||||
|
||||
/mob/attack_tk(mob/user)
|
||||
return
|
||||
@@ -124,7 +130,8 @@
|
||||
|
||||
if(!isturf(target) && istype(focus,/obj/item) && target.Adjacent(focus))
|
||||
apply_focus_overlay()
|
||||
melee_item_attack_chain(tk_user, focus, target, params) //isn't copying the attack chain fun. we should do it more often.
|
||||
var/obj/item/I = focus
|
||||
I.melee_attack_chain(tk_user, target, params) //isn't copying the attack chain fun. we should do it more often.
|
||||
if(check_if_focusable(focus))
|
||||
focus.do_attack_animation(target, null, focus)
|
||||
else
|
||||
@@ -163,7 +170,7 @@
|
||||
/obj/item/tk_grab/proc/apply_focus_overlay()
|
||||
if(!focus)
|
||||
return
|
||||
new /obj/effect/overlay/temp/telekinesis(get_turf(focus))
|
||||
new /obj/effect/temp_visual/telekinesis(get_turf(focus))
|
||||
|
||||
/obj/item/tk_grab/update_icon()
|
||||
cut_overlays()
|
||||
|
||||
@@ -254,16 +254,34 @@
|
||||
"<span class='green'>You cum into [SC].</span>", \
|
||||
"<span class='green'>You have relieved yourself.</span>")
|
||||
finished = 1
|
||||
|
||||
|
||||
if(/obj/item/organ/genital/vagina)
|
||||
var/obj/item/organ/genital/vagina/V = SG
|
||||
if(!V.linked_womb)
|
||||
src << "<span class='warning'>No womb!</span>"
|
||||
return
|
||||
fluid_source = V.linked_womb.reagents
|
||||
total_cum = fluid_source.total_volume
|
||||
if(into_container)//into a glass or beaker or whatever
|
||||
src.visible_message("<span class='danger'>[src] starts fingering their vagina over [SC].</span>", \
|
||||
"<span class='userdanger'>You start fingering over [SC.name].</span>", \
|
||||
"<span class='userdanger'>You start masturbating.</span>")
|
||||
if(do_after(src, mb_time, target = src) && in_range(src, SC))
|
||||
fluid_source.trans_to(SC, total_cum)
|
||||
src.visible_message("<span class='danger'>[src] orgasms, [pick("cumming into", "emptying themself into")] [SC]!</span>", \
|
||||
"<span class='green'>You cum into [SC].</span>", \
|
||||
"<span class='green'>You have relieved yourself.</span>")
|
||||
finished = 1
|
||||
|
||||
else//not into a container
|
||||
src.visible_message("<span class='danger'>[src] starts [pick("jerking off","stroking")] their [pick(GLOB.dick_nouns)].</span>", \
|
||||
"<span class='userdanger'>You start jerking off your [pick(GLOB.dick_nouns)].</span>", \
|
||||
src.visible_message("<span class='danger'>[src] starts fingering their vagina.</span>", \
|
||||
"<span class='userdanger'>You start fingering your vagina.</span>", \
|
||||
"<span class='userdanger'>You start masturbating.</span>")
|
||||
if(do_after(src, mb_time, target = src))
|
||||
if(total_cum > 5)
|
||||
fluid_source.reaction(src.loc, TOUCH, 1, 0)
|
||||
fluid_source.clear_reagents()
|
||||
src.visible_message("<span class='danger'>[src] orgasms, [pick("shooting cum", "draining their balls")][istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""]!</span>", \
|
||||
src.visible_message("<span class='danger'>[src] orgasms, cumming[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""]!</span>", \
|
||||
"<span class='green'>You cum[istype(src.loc, /turf/open/floor) ? " onto [src.loc]" : ""].</span>", \
|
||||
"<span class='green'>You have relieved yourself.</span>")
|
||||
finished = 1
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
var/obj/effect/decal/cleanable/femcum/S = locate() in T
|
||||
if(!S)
|
||||
S = new(T)
|
||||
S.reagents.add_reagent("semen", reac_volume)
|
||||
S.reagents.add_reagent("femcum", reac_volume)
|
||||
if(data["blood_DNA"])
|
||||
S.blood_DNA[data["blood_DNA"]] = data["blood_type"]
|
||||
|
||||
@@ -219,4 +219,4 @@
|
||||
results = list("anaphro+" = 1)
|
||||
required_reagents = list("anaphro" = 5, "acetone" = 1)
|
||||
required_temp = 300
|
||||
mix_message = "The mixture thickens and heats up slighty..."
|
||||
mix_message = "The mixture thickens and heats up slighty..."
|
||||
|
||||
+444
-117
@@ -175,6 +175,10 @@
|
||||
cleanspeed = 80
|
||||
var/emagged = 0
|
||||
|
||||
/obj/item/weapon/soap/tongue/New()
|
||||
..()
|
||||
flags |= NOBLUDGEON //No more attack messages
|
||||
|
||||
/obj/item/trash/rkibble
|
||||
name = "robo kibble"
|
||||
desc = "A novelty bowl of assorted mech fabricator byproducts. Mockingly feed this to the sec-dog to help it recharge."
|
||||
@@ -210,7 +214,7 @@
|
||||
user << "<span class='notice'>You finish licking off \the [target.name].</span>"
|
||||
qdel(target)
|
||||
var/mob/living/silicon/robot.R = user
|
||||
R.cell.charge = R.cell.charge + 50
|
||||
R.cell.give(50)
|
||||
else if(istype(target,/obj/item)) //hoo boy. danger zone man
|
||||
if(istype(target,/obj/item/trash))
|
||||
user.visible_message("[user] nibbles away at \the [target.name].", "<span class='warning'>You begin to nibble away at \the [target.name]...</span>")
|
||||
@@ -218,7 +222,7 @@
|
||||
user << "<span class='notice'>You finish off \the [target.name].</span>"
|
||||
qdel(target)
|
||||
var/mob/living/silicon/robot.R = user
|
||||
R.cell.charge = R.cell.charge + 250
|
||||
R.cell.give(250)
|
||||
return
|
||||
if(istype(target,/obj/item/weapon/stock_parts/cell))
|
||||
user.visible_message("[user] begins cramming \the [target.name] down its throat.", "<span class='warning'>You begin cramming \the [target.name] down your throat...</span>")
|
||||
@@ -237,7 +241,7 @@
|
||||
user << "<span class='notice'>You finish off \the [target.name].</span>"
|
||||
qdel(I)
|
||||
var/mob/living/silicon/robot.R = user
|
||||
R.cell.charge = R.cell.charge + 500
|
||||
R.cell.give(500)
|
||||
return
|
||||
user.visible_message("[user] begins to lick \the [target.name] clean...", "<span class='notice'>You begin to lick \the [target.name] clean...</span>")
|
||||
if(do_after(user, src.cleanspeed, target = target))
|
||||
@@ -257,7 +261,7 @@
|
||||
L.visible_message("<span class='danger'>[user] has shocked [L] with its tongue!</span>", \
|
||||
"<span class='userdanger'>[user] has shocked you with its tongue! You can feel the betrayal.</span>")
|
||||
playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
|
||||
R.cell.charge = R.cell.charge - 666
|
||||
R.cell.use(666)
|
||||
else
|
||||
user.visible_message("<span class='warning'>\the [user] affectionally licks \the [target]'s face!</span>", "<span class='notice'>You affectionally lick \the [target]'s face!</span>")
|
||||
playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1)
|
||||
@@ -295,151 +299,188 @@
|
||||
user.visible_message("<span class='warning'>You don't have enough charge for this operation!</span class>")
|
||||
return
|
||||
if(src.cooldown == 0)
|
||||
R.cell.charge = R.cell.charge - 1000
|
||||
R.cell.use(1000)
|
||||
return ..()
|
||||
|
||||
|
||||
//Sleeper
|
||||
|
||||
/obj/item/weapon/dogborg/sleeper
|
||||
name = "mounted sleeper"
|
||||
/obj/item/device/dogborg/sleeper
|
||||
name = "Medbelly"
|
||||
desc = "Equipment for medical hound. A mounted sleeper that stabilizes patients and can inject reagents in the borg's reserves."
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "sleeper"
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
var/mob/living/carbon/patient = null
|
||||
var/mob/living/silicon/hound = null
|
||||
var/mob/living/silicon/robot/hound = null
|
||||
var/inject_amount = 10
|
||||
var/min_health = -100
|
||||
var/occupied = 0
|
||||
var/list/injection_chems = list("morphine", "salbutamol", "bicaridine", "kelotane","antitoxin")
|
||||
var/cleaning = 0
|
||||
var/patient_laststat = null
|
||||
var/mob_energy = 30000 //Energy gained from digesting mobs (including PCs)
|
||||
var/list/injection_chems = list("antitoxin", "morphine", "salbutamol", "bicaridine", "kelotane"),
|
||||
var/eject_port = "ingestion"
|
||||
var/list/items_preserved = list()
|
||||
var/list/important_items = list(
|
||||
/obj/item/weapon/hand_tele,
|
||||
/obj/item/weapon/card/id/captains_spare,
|
||||
/obj/item/device/aicard,
|
||||
/obj/item/device/paicard,
|
||||
/obj/item/weapon/gun,
|
||||
/obj/item/weapon/pinpointer,
|
||||
/obj/item/clothing/shoes/magboots,
|
||||
/obj/item/clothing/head/helmet/space,
|
||||
/obj/item/weapon/disk/nuclear)
|
||||
|
||||
/obj/item/weapon/dogborg/sleeper/Exit(atom/movable/O)
|
||||
/obj/item/device/dogborg/sleeper/New()
|
||||
..()
|
||||
flags |= NOBLUDGEON //No more attack messages
|
||||
|
||||
/obj/item/device/dogborg/sleeper/Exit(atom/movable/O)
|
||||
return 0
|
||||
|
||||
/obj/item/weapon/dogborg/sleeper/attack(mob/M, mob/living/silicon/robot/user)
|
||||
..()
|
||||
user.do_attack_animation(M, ATTACK_EFFECT_BITE)
|
||||
|
||||
/obj/item/weapon/dogborg/sleeper/afterattack(mob/living/carbon/target, mob/living/silicon/user, proximity)
|
||||
/obj/item/device/dogborg/sleeper/afterattack(mob/living/carbon/target, mob/living/silicon/user, proximity)
|
||||
hound = loc
|
||||
if(!proximity)
|
||||
return
|
||||
if(!ishuman(target))
|
||||
return
|
||||
if(!patient_insertion_check(target))
|
||||
return
|
||||
user.visible_message("<span class='warning'>[user] starts putting [target] into \the [src].</span>", "<span class='notice'>You start putting [target] into [src]...</span>")
|
||||
if(do_after(user, 50, target = target))
|
||||
if(!patient_insertion_check(target))
|
||||
return
|
||||
target.forceMove(src)
|
||||
playsound(user.loc, 'sound/vore/gulpold.ogg', 50, 1)
|
||||
patient = target
|
||||
hound = user
|
||||
target.reset_perspective(src)
|
||||
user << "<span class='notice'>[target] successfully loaded into [src]. Life support functions engaged.</span>"
|
||||
user.visible_message("<span class='warning'>[user] loads [target] into [src].</span>")
|
||||
user.visible_message("[target] loaded. Life support functions engaged.")
|
||||
src.occupied = 1
|
||||
var/mob/living/silicon/robot.R = user
|
||||
if(patient.stat < 2)
|
||||
R.sleeper_r = 0
|
||||
R.sleeper_g = 1
|
||||
R.update_icons()
|
||||
else
|
||||
R.sleeper_g = 0
|
||||
R.sleeper_r = 1
|
||||
R.update_icons()
|
||||
SSobj.processing |= src
|
||||
|
||||
/obj/item/weapon/dogborg/sleeper/proc/patient_insertion_check(mob/living/carbon/target, mob/user)
|
||||
if(target.anchored)
|
||||
user << "<span class='warning'>[target] will not fit into the sleeper because they are buckled to [target.buckled]!</span>"
|
||||
if(target.buckled)
|
||||
user << "<span class='warning'>The user is buckled and can not be put into your [src.name].</span>"
|
||||
return
|
||||
if(patient)
|
||||
user << "<span class='warning'>The sleeper is already occupied!</span>"
|
||||
user << "<span class='warning'>Your [src.name] is already occupied.</span>"
|
||||
return
|
||||
return 1
|
||||
user.visible_message("<span class='warning'>[hound.name] is ingesting [target.name] into their [src.name].</span>", "<span class='notice'>You start ingesting [target] into your [src]...</span>")
|
||||
if(!patient && ishuman(target) && !target.buckled && do_after (user, 50, target))
|
||||
|
||||
/obj/item/weapon/dogborg/sleeper/proc/go_out()
|
||||
if(src.occupied == 0)
|
||||
return
|
||||
var/mob/living/silicon/robot.R = hound
|
||||
hound << "<span class='notice'>[patient] ejected. Life support functions disabled.</span>"
|
||||
R.sleeper_r = 0
|
||||
R.sleeper_g = 0
|
||||
R.update_icons()
|
||||
patient.forceMove(get_turf(src))
|
||||
patient.reset_perspective()
|
||||
patient = null
|
||||
src.occupied = 0
|
||||
src.occupied = 0 //double check just in case
|
||||
if(!proximity) return //If they moved away, you can't eat them.
|
||||
|
||||
/obj/item/weapon/dogborg/sleeper/proc/drain()
|
||||
var/mob/living/silicon/robot.R = hound
|
||||
R.cell.charge = R.cell.charge - 10
|
||||
if(patient) return //If you try to eat two people at once, you can only eat one.
|
||||
|
||||
/obj/item/weapon/dogborg/sleeper/attack_self(mob/user)
|
||||
else //If you don't have someone in you, proceed.
|
||||
target.forceMove(src)
|
||||
target.reset_perspective(src)
|
||||
update_patient()
|
||||
START_PROCESSING(SSobj, src)
|
||||
user.visible_message("<span class='warning'>[hound.name]'s medical pod lights up as [target.name] slips inside into their [src.name].</span>", "<span class='notice'>Your medical pod lights up as [target] slips into your [src]. Life support functions engaged.</span>")
|
||||
message_admins("[key_name(hound)] has eaten [key_name(patient)] as a dogborg. ([hound ? "<a href='?_src_=holder;adminplayerobservecoodjump=1;X=[hound.x];Y=[hound.y];Z=[hound.z]'>JMP</a>" : "null"])")
|
||||
playsound(hound, 'sound/vore/gulpold.ogg', 100, 1)
|
||||
|
||||
/obj/item/device/dogborg/sleeper/proc/go_out(var/target)
|
||||
hound = src.loc
|
||||
if(length(contents) > 0)
|
||||
hound.visible_message("<span class='warning'>[hound.name] empties out their contents via their [eject_port] port.</span>", "<span class='notice'>You empty your contents via your [eject_port] port.</span>")
|
||||
if(target)
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/person = target
|
||||
person.forceMove(get_turf(src))
|
||||
person.reset_perspective()
|
||||
else
|
||||
var/obj/T = target
|
||||
T.loc = hound.loc
|
||||
else
|
||||
for(var/C in contents)
|
||||
if(ishuman(C))
|
||||
var/mob/living/carbon/human/person = C
|
||||
person.forceMove(get_turf(src))
|
||||
person.reset_perspective()
|
||||
else
|
||||
var/obj/T = C
|
||||
T.loc = hound.loc
|
||||
items_preserved.Cut()
|
||||
cleaning = 0
|
||||
playsound(loc, 'sound/effects/splat.ogg', 50, 1)
|
||||
update_patient()
|
||||
else //You clicked eject with nothing in you, let's just reset stuff to be sure.
|
||||
items_preserved.Cut()
|
||||
cleaning = 0
|
||||
update_patient()
|
||||
|
||||
/obj/item/device/dogborg/sleeper/proc/drain(var/amt = 3) //Slightly reduced cost (before, it was always injecting inaprov)
|
||||
if (amt > 0)
|
||||
hound.cell.give(amt)
|
||||
else
|
||||
hound.cell.use(amt)
|
||||
|
||||
/obj/item/device/dogborg/sleeper/attack_self(mob/user)
|
||||
if(..())
|
||||
return
|
||||
sleeperUI(user)
|
||||
|
||||
/obj/item/weapon/dogborg/sleeper/proc/sleeperUI(mob/user)
|
||||
/obj/item/device/dogborg/sleeper/proc/sleeperUI(mob/user)
|
||||
var/dat
|
||||
dat += "<h3>Injector</h3>"
|
||||
if(patient)
|
||||
|
||||
if(patient && !(patient.stat & DEAD))
|
||||
dat += "<A href='?src=\ref[src];inject=epinephrine'>Inject Epinephrine</A>"
|
||||
else
|
||||
dat += "<span class='linkOff'>Inject Epinephrine</span>"
|
||||
if(patient && patient.health > min_health)
|
||||
for(var/chem in injection_chems)
|
||||
var/datum/reagent/C = GLOB.chemical_reagents_list[chem]
|
||||
for(var/re in injection_chems)
|
||||
var/datum/reagent/C = GLOB.chemical_reagents_list[re]
|
||||
if(C)
|
||||
dat += "<BR><A href='?src=\ref[src];inject=[C.id]'>Inject [C.name]</A>"
|
||||
else
|
||||
for(var/chem in injection_chems)
|
||||
var/datum/reagent/C = GLOB.chemical_reagents_list[chem]
|
||||
for(var/re in injection_chems)
|
||||
var/datum/reagent/C = GLOB.chemical_reagents_list[re]
|
||||
if(C)
|
||||
dat += "<BR><span class='linkOff'>Inject [C.name]</span>"
|
||||
|
||||
dat += "<h3>Sleeper Status</h3>"
|
||||
dat += "<A href='?src=\ref[src];refresh=1'>Scan</A>"
|
||||
dat += "<A href='?src=\ref[src];eject=1'>Eject</A>"
|
||||
dat += "<A id='refbutton' href='?src=\ref[src];refresh=1'>Refresh</A>"
|
||||
dat += "<A href='?src=\ref[src];eject=1'>Eject All</A>"
|
||||
dat += "<A href='?src=\ref[src];port=1'>Eject port: [eject_port]</A>"
|
||||
if(!cleaning)
|
||||
dat += "<A href='?src=\ref[src];clean=1'>Self-Clean</A>"
|
||||
else
|
||||
dat += "<span class='linkOff'>Self-Clean</span>"
|
||||
|
||||
dat += "<div class='statusDisplay'>"
|
||||
|
||||
//Cleaning and there are still un-preserved items
|
||||
if(cleaning && length(contents - items_preserved))
|
||||
dat += "<font color='red'><B>Self-cleaning mode.</B> [length(contents - items_preserved)] object(s) remaining.</font><BR>"
|
||||
|
||||
//There are no items to be processed other than un-preserved items
|
||||
else if(cleaning && length(items_preserved))
|
||||
dat += "<font color='red'><B>Self-cleaning done. Eject remaining objects now.</B></font><BR>"
|
||||
|
||||
//Preserved items count when the list is populated
|
||||
if(length(items_preserved))
|
||||
dat += "<font color='red'>[length(items_preserved)] uncleanable object(s).</font><BR>"
|
||||
|
||||
if(!patient)
|
||||
dat += "Sleeper Unoccupied"
|
||||
else
|
||||
dat += "[patient.name] => "
|
||||
switch(patient.stat) //obvious, see what their status is
|
||||
|
||||
switch(patient.stat)
|
||||
if(0)
|
||||
dat += "<span class='good'>Conscious</span>"
|
||||
var/mob/living/silicon/robot.R = user
|
||||
R.sleeper_r = 0
|
||||
R.sleeper_g = 1
|
||||
R.update_icons()
|
||||
if(1)
|
||||
dat += "<span class='average'>Unconscious</span>"
|
||||
var/mob/living/silicon/robot.R = user
|
||||
R.sleeper_r = 0
|
||||
R.sleeper_g = 1
|
||||
R.update_icons()
|
||||
else
|
||||
dat += "<span class='bad'>DEAD</span>"
|
||||
var/mob/living/silicon/robot.R = user
|
||||
R.sleeper_g = 0
|
||||
R.sleeper_r = 1
|
||||
R.update_icons()
|
||||
dat += "<br />"
|
||||
dat += "<div class='line'><div class='statusLabel'>Health:</div><div class='progressBar'><div style='width: [patient.health < 0 ? "0" : "[patient.health]"]%;' class='progressFill good'></div></div><div class='statusValue'>[patient.stat > 1 ? "" : "[patient.health]%"]</div></div>"
|
||||
dat += "<div class='line'><div class='statusLabel'>\> Brute Damage:</div><div class='progressBar'><div style='width: [patient.getBruteLoss()]%;' class='progressFill bad'></div></div><div class='statusValue'>[patient.getBruteLoss()]%</div></div>"
|
||||
dat += "<div class='line'><div class='statusLabel'>\> Resp. Damage:</div><div class='progressBar'><div style='width: [patient.getOxyLoss()]%;' class='progressFill bad'></div></div><div class='statusValue'>[patient.getOxyLoss()]%</div></div>"
|
||||
dat += "<div class='line'><div class='statusLabel'>\> Toxin Content:</div><div class='progressBar'><div style='width: [patient.getToxLoss()]%;' class='progressFill bad'></div></div><div class='statusValue'>[patient.getToxLoss()]%</div></div>"
|
||||
dat += "<div class='line'><div class='statusLabel'>\> Burn Severity:</div><div class='progressBar'><div style='width: [patient.getFireLoss()]%;' class='progressFill bad'></div></div><div class='statusValue'>[patient.getFireLoss()]%</div></div><br>"
|
||||
|
||||
dat += "<HR><div class='line'><div style='width: 170px;' class='statusLabel'>Paralysis Summary:</div><div class='statusValue'>[round(patient.paralysis)]% [patient.paralysis ? "([round(patient.paralysis / 4)] seconds left)" : ""]</div></div><br>"
|
||||
if(patient.getCloneLoss())
|
||||
dat += "<div class='line'><span class='average'>Subject appears to have cellular damage.</span></div><br>"
|
||||
var/healthcolor = (patient.health > 0 ? "color:white;" : "color:red;")
|
||||
var/brutecolor = (patient.getBruteLoss() < 60 ? "color:gray;" : "color:red;")
|
||||
var/o2color = (patient.getOxyLoss() < 60 ? "color:gray;" : "color:red;")
|
||||
var/toxcolor = (patient.getToxLoss() < 60 ? "color:gray;" : "color:red;")
|
||||
var/burncolor = (patient.getFireLoss() < 60 ? "color:gray;" : "color:red;")
|
||||
|
||||
dat += "<span style='[healthcolor]'>\t-Overall Health %: [round(patient.health)]</span><BR>"
|
||||
dat += "<span style='[brutecolor]'>\t-Brute Damage %: [patient.getBruteLoss()]</span><BR>"
|
||||
dat += "<span style='[o2color]'>\t-Respiratory Damage %: [patient.getOxyLoss()]</span><BR>"
|
||||
dat += "<span style='[toxcolor]'>\t-Toxin Content %: [patient.getToxLoss()]</span><BR>"
|
||||
dat += "<span style='[burncolor]'>\t-Burn Severity %: [patient.getFireLoss()]</span><BR>"
|
||||
|
||||
if(round(patient.paralysis / 4) >= 1)
|
||||
dat += text("<HR>Patient paralyzed for: []<BR>", round(patient.paralysis / 4) >= 1 ? "[round(patient.paralysis / 4)] seconds" : "None")
|
||||
if(patient.getBrainLoss())
|
||||
dat += "<div class='line'><span class='average'>Significant brain damage detected.</span></div><br>"
|
||||
if(patient.getCloneLoss())
|
||||
dat += "<div class='line'><span class='average'>Patient may be improperly cloned.</span></div><br>"
|
||||
if(patient.reagents.reagent_list.len)
|
||||
for(var/datum/reagent/R in patient.reagents.reagent_list)
|
||||
dat += "<div class='line'><div style='width: 170px;' class='statusLabel'>[R.name]:</div><div class='statusValue'>[round(R.volume, 0.1)] units</div></div><br>"
|
||||
@@ -451,11 +492,13 @@
|
||||
popup.open()
|
||||
return
|
||||
|
||||
/obj/item/weapon/dogborg/sleeper/Topic(href, href_list)
|
||||
|
||||
/obj/item/device/dogborg/sleeper/Topic(href, href_list)
|
||||
if(..() || usr == patient)
|
||||
return
|
||||
usr.set_machine(src)
|
||||
if(href_list["refresh"])
|
||||
update_patient()
|
||||
src.updateUsrDialog()
|
||||
sleeperUI(usr)
|
||||
return
|
||||
@@ -463,42 +506,322 @@
|
||||
go_out()
|
||||
sleeperUI(usr)
|
||||
return
|
||||
if(patient && patient.stat != DEAD)
|
||||
if(href_list["clean"])
|
||||
if(!cleaning)
|
||||
var/confirm = alert(usr, "You are about to engage self-cleaning mode. This will fill your [src] with caustic enzymes to remove any objects or biomatter, and convert them into energy. Are you sure?", "Confirmation", "Self-Clean", "Cancel")
|
||||
if(confirm == "Self-Clean")
|
||||
if(cleaning)
|
||||
return
|
||||
else
|
||||
cleaning = 1
|
||||
drain(500)
|
||||
START_PROCESSING(SSobj, src)
|
||||
sleeperUI(usr)
|
||||
if(patient)
|
||||
patient << "<span class='danger'>[hound.name]'s [src.name] fills with caustic enzymes around you!</span>"
|
||||
return
|
||||
if(cleaning)
|
||||
sleeperUI(usr)
|
||||
return
|
||||
if(href_list["port"])
|
||||
switch(eject_port)
|
||||
if("ingestion")
|
||||
eject_port = "disposal"
|
||||
if("disposal")
|
||||
eject_port = "ingestion"
|
||||
sleeperUI(usr)
|
||||
return
|
||||
|
||||
if(patient && !(patient.stat & DEAD))
|
||||
if(href_list["inject"] == "epinephrine" || patient.health > min_health)
|
||||
inject_chem(usr, href_list["inject"])
|
||||
else
|
||||
usr << "<span class='notice'>ERROR: Subject is not in stable condition for auto-injection.</span>"
|
||||
usr << "<span class='notice'>ERROR: Subject is not in stable condition for injections.</span>"
|
||||
else
|
||||
usr << "<span class='notice'>ERROR: Subject cannot metabolise chemicals.</span>"
|
||||
|
||||
src.updateUsrDialog()
|
||||
sleeperUI(usr) //Needs a callback to boop the page to refresh.
|
||||
return
|
||||
|
||||
/obj/item/weapon/dogborg/sleeper/proc/inject_chem(mob/user, chem)
|
||||
/obj/item/device/dogborg/sleeper/proc/inject_chem(mob/user, chem)
|
||||
if(patient && patient.reagents)
|
||||
if(chem in injection_chems + "epinephrine")
|
||||
if(patient.reagents.get_reagent_amount(chem) + 10 <= 20)
|
||||
patient.reagents.add_reagent(chem, 10)
|
||||
var/mob/living/silicon/robot.R = user
|
||||
R.cell.charge = R.cell.charge - 250 //-250 charge per sting.
|
||||
if(hound.cell.charge < 800) //This is so borgs don't kill themselves with it.
|
||||
hound << "<span class='notice'>You don't have enough power to synthesize fluids.</span>"
|
||||
return
|
||||
else if(patient.reagents.get_reagent_amount(chem) + 10 >= 20) //Preventing people from accidentally killing themselves by trying to inject too many chemicals!
|
||||
hound << "<span class='notice'>Your stomach is currently too full of fluids to secrete more fluids of this kind.</span>"
|
||||
else if(patient.reagents.get_reagent_amount(chem) + 10 <= 20) //No overdoses for you
|
||||
patient.reagents.add_reagent(chem, inject_amount)
|
||||
drain(750) //-750 charge per injection
|
||||
var/units = round(patient.reagents.get_reagent_amount(chem))
|
||||
user << "<span class='notice'>Occupant now has [units] unit\s of [GLOB.chemical_reagents_list[chem]] in their bloodstream.</span>"
|
||||
hound << "<span class='notice'>Injecting [units] unit\s of [injection_chems[chem]] into occupant.</span>" //If they were immersed, the reagents wouldn't leave with them.
|
||||
|
||||
/obj/item/weapon/dogborg/sleeper/process()
|
||||
if(src.occupied == 0)
|
||||
SSobj.processing.Remove(src)
|
||||
/obj/item/device/dogborg/sleeper/process()
|
||||
|
||||
if(cleaning) //We're cleaning, return early after calling this as we don't care about the patient.
|
||||
src.clean_cycle()
|
||||
return
|
||||
if(patient.health > 0)
|
||||
patient.adjustOxyLoss(-1)
|
||||
patient.updatehealth()
|
||||
patient.AdjustStunned(-4)
|
||||
patient.AdjustWeakened(-4)
|
||||
src.drain()
|
||||
if(patient.reagents.get_reagent_amount("epinephrine") < 5)
|
||||
patient.reagents.add_reagent("epinephrine", 5)
|
||||
|
||||
/obj/item/weapon/dogborg/sleeper/container_resist()
|
||||
go_out()
|
||||
if(patient) //We're caring for the patient. Medical emergency! Or endo scene.
|
||||
update_patient()
|
||||
if(patient.health < 0)
|
||||
patient.adjustOxyLoss(-1) //Heal some oxygen damage if they're in critical condition
|
||||
patient.updatehealth()
|
||||
patient.AdjustStunned(-4)
|
||||
patient.AdjustWeakened(-4)
|
||||
src.drain()
|
||||
if((patient.reagents.get_reagent_amount("epinephrine") < 5) && (patient.health < patient.maxHealth)) //Stop pumping full HP people full of drugs. Don't heal people you're digesting, meanie.
|
||||
patient.reagents.add_reagent("epinephrine", 5)
|
||||
return
|
||||
|
||||
if(!patient && !cleaning) //We think we're done working.
|
||||
if(!update_patient()) //One last try to find someone
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return
|
||||
|
||||
/obj/item/device/dogborg/sleeper/proc/update_patient()
|
||||
|
||||
//Well, we HAD one, what happened to them?
|
||||
if(patient in contents)
|
||||
if(patient_laststat != patient.stat)
|
||||
if(patient.stat & DEAD)
|
||||
hound.sleeper_r = 1
|
||||
hound.sleeper_g = 0
|
||||
patient_laststat = patient.stat
|
||||
else
|
||||
hound.sleeper_r = 0
|
||||
hound.sleeper_g = 1
|
||||
patient_laststat = patient.stat
|
||||
//Update icon
|
||||
hound.update_icons()
|
||||
//Return original patient
|
||||
return(patient)
|
||||
|
||||
//Check for a new patient
|
||||
else
|
||||
for(var/mob/living/carbon/human/C in contents)
|
||||
patient = C
|
||||
if(patient.stat & DEAD)
|
||||
hound.sleeper_r = 1
|
||||
hound.sleeper_g = 0
|
||||
patient_laststat = patient.stat
|
||||
else
|
||||
hound.sleeper_r = 0
|
||||
hound.sleeper_g = 1
|
||||
patient_laststat = patient.stat
|
||||
//Update icon and return new patient
|
||||
hound.update_icons()
|
||||
return(C)
|
||||
|
||||
//Cleaning looks better with red on, even with nobody in it
|
||||
if(cleaning && !patient)
|
||||
hound.sleeper_r = 1
|
||||
hound.sleeper_g = 0
|
||||
|
||||
//Couldn't find anyone, and not cleaning
|
||||
else if(!cleaning && !patient)
|
||||
hound.sleeper_r = 0
|
||||
hound.sleeper_g = 0
|
||||
|
||||
patient_laststat = null
|
||||
patient = null
|
||||
hound.update_icons()
|
||||
return
|
||||
|
||||
//Gurgleborg process
|
||||
/obj/item/device/dogborg/sleeper/proc/clean_cycle()
|
||||
|
||||
//Sanity? Maybe not required. More like if indigestible person OOC escapes.
|
||||
for(var/I in items_preserved)
|
||||
if(!(I in contents))
|
||||
items_preserved -= I
|
||||
|
||||
var/list/touchable_items = contents - items_preserved
|
||||
|
||||
//Belly is entirely empty
|
||||
if(!length(contents))
|
||||
hound << "<span class='notice'>Your [src.name] is now clean. Ending self-cleaning cycle.</span>"
|
||||
cleaning = 0
|
||||
update_patient()
|
||||
return
|
||||
|
||||
if(prob(20))
|
||||
var/churnsound = pick(
|
||||
'sound/vore/pred/digest_01.ogg',
|
||||
'sound/vore/pred/digest_02.ogg',
|
||||
'sound/vore/pred/digest_03.ogg',
|
||||
'sound/vore/pred/digest_04.ogg',
|
||||
'sound/vore/pred/digest_05.ogg',
|
||||
'sound/vore/pred/digest_06.ogg',
|
||||
'sound/vore/pred/digest_07.ogg',
|
||||
'sound/vore/pred/digest_08.ogg',
|
||||
'sound/vore/pred/digest_09.ogg',
|
||||
'sound/vore/pred/digest_10.ogg',
|
||||
'sound/vore/pred/digest_11.ogg',
|
||||
'sound/vore/pred/digest_12.ogg',
|
||||
'sound/vore/pred/digest_13.ogg',
|
||||
'sound/vore/pred/digest_14.ogg',
|
||||
'sound/vore/pred/digest_15.ogg',
|
||||
'sound/vore/pred/digest_16.ogg',
|
||||
'sound/vore/pred/digest_17.ogg',
|
||||
'sound/vore/pred/digest_18.ogg')
|
||||
for(var/mob/outhearer in range(1,hound))
|
||||
outhearer << sound(churnsound)
|
||||
for(var/mob/inhearer in contents)
|
||||
inhearer << sound(churnsound)
|
||||
//If the timing is right, and there are items to be touched
|
||||
if(SSmobs.times_fired%6==1 && length(touchable_items))
|
||||
|
||||
//Burn all the mobs or add them to the exclusion list
|
||||
for(var/mob/living/carbon/human/T in (touchable_items))
|
||||
if((T.status_flags & GODMODE) || !T.digestable)
|
||||
src.items_preserved += T
|
||||
else
|
||||
T.adjustBruteLoss(2)
|
||||
T.adjustFireLoss(3)
|
||||
src.update_patient()
|
||||
|
||||
//Pick a random item to deal with (if there are any)
|
||||
var/atom/target = pick(touchable_items)
|
||||
|
||||
//Handle the target being a mob
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/T = target
|
||||
|
||||
//Mob is now dead
|
||||
if(T.stat & DEAD)
|
||||
message_admins("[key_name(hound)] has digested [key_name(T)] as a dogborg. ([hound ? "<a href='?_src_=holder;adminplayerobservecoodjump=1;X=[hound.x];Y=[hound.y];Z=[hound.z]'>JMP</a>" : "null"])")
|
||||
hound << "<span class='notice'>You feel your belly slowly churn around [T], breaking them down into a soft slurry to be used as power for your systems.</span>"
|
||||
T << "<span class='notice'>You feel [hound]'s belly slowly churn around your form, breaking you down into a soft slurry to be used as power for [hound]'s systems.</span>"
|
||||
src.drain(-30000) //Fueeeeellll
|
||||
var/deathsound = pick(
|
||||
'sound/vore/pred/death_01.ogg',
|
||||
'sound/vore/pred/death_02.ogg',
|
||||
'sound/vore/pred/death_03.ogg',
|
||||
'sound/vore/pred/death_04.ogg',
|
||||
'sound/vore/pred/death_05.ogg',
|
||||
'sound/vore/pred/death_06.ogg',
|
||||
'sound/vore/pred/death_07.ogg',
|
||||
'sound/vore/pred/death_08.ogg',
|
||||
'sound/vore/pred/death_09.ogg',
|
||||
'sound/vore/pred/death_10.ogg',
|
||||
'sound/vore/pred/death_11.ogg',
|
||||
'sound/vore/pred/death_12.ogg',
|
||||
'sound/vore/pred/death_13.ogg')
|
||||
for(var/mob/hearer in range(1,src.hound))
|
||||
hearer << deathsound
|
||||
T << deathsound
|
||||
qdel(T)
|
||||
src.update_patient()
|
||||
|
||||
//Handle the target being anything but a /mob/living/carbon/human
|
||||
else
|
||||
var/obj/T = target
|
||||
|
||||
//If the object is in the items_preserved global list //POLARISTODO
|
||||
|
||||
if(T.type in important_items)
|
||||
src.items_preserved += T
|
||||
|
||||
//If the object is not one to preserve
|
||||
else
|
||||
//Special case for PDAs as they are dumb. TODO fix Del on PDAs to be less dumb.
|
||||
if (istype(T, /obj/item/device/pda))
|
||||
var/obj/item/device/pda/PDA = T
|
||||
if (PDA.id)
|
||||
PDA.id.forceMove(src)
|
||||
PDA.id = null
|
||||
qdel(T)
|
||||
|
||||
//Special case for IDs to make them digested
|
||||
//else if (istype(T, /obj/item/weapon/card/id))
|
||||
//var/obj/item/weapon/card/id/ID = T
|
||||
//ID.digest() //Need the digest proc, first.
|
||||
|
||||
//Anything not perserved, PDA, or ID
|
||||
else
|
||||
//Spill(T) //Needs the spill proc to be added
|
||||
qdel(T)
|
||||
src.update_patient()
|
||||
src.hound.cell.give(30) //10 charge? that was such a practically nonexistent number it hardly gave any purpose for this bit :v *cranks up*
|
||||
return
|
||||
|
||||
|
||||
/obj/item/device/dogborg/sleeper/container_resist()
|
||||
if(prob(8))
|
||||
go_out()
|
||||
|
||||
/obj/item/device/dogborg/sleeper/K9 //The K9 portabrig
|
||||
name = "Brig-Belly"
|
||||
desc = "Equipment for a K9 unit. A mounted portable-brig that holds criminals."
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "sleeperb"
|
||||
inject_amount = 10
|
||||
min_health = -100
|
||||
injection_chems = null //So they don't have all the same chems as the medihound!
|
||||
|
||||
/obj/item/device/dogborg/sleeper/compactor //Janihound gut.
|
||||
name = "garbage processor"
|
||||
desc = "A mounted garbage compactor unit with fuel processor."
|
||||
icon = 'icons/mob/dogborg.dmi'
|
||||
icon_state = "compactor"
|
||||
inject_amount = 10
|
||||
min_health = -100
|
||||
injection_chems = null //So they don't have all the same chems as the medihound!
|
||||
var/max_item_count = 32
|
||||
|
||||
/obj/item/weapon/storage/attackby(obj/item/device/dogborg/sleeper/compactor, mob/user, proximity) //GIT CIRCUMVENTED YO!
|
||||
compactor.afterattack(src, user ,1)
|
||||
|
||||
/obj/item/device/dogborg/sleeper/compactor/afterattack(var/atom/movable/target, mob/living/silicon/user, proximity)//GARBO NOMS
|
||||
hound = loc
|
||||
|
||||
if(!istype(target))
|
||||
return
|
||||
if(!proximity)
|
||||
return
|
||||
if(target.anchored)
|
||||
return
|
||||
if(length(contents) > (max_item_count - 1))
|
||||
user << "<span class='warning'>Your [src.name] is full. Eject or process contents to continue.</span>"
|
||||
return
|
||||
|
||||
if(istype(target,/obj/item))
|
||||
var/obj/item/target_obj = target
|
||||
if(target_obj.w_class > WEIGHT_CLASS_BULKY)
|
||||
user << "<span class='warning'>\The [target] is too large to fit into your [src.name]</span>"
|
||||
return
|
||||
user.visible_message("<span class='warning'>[hound.name] is ingesting [target.name] into their [src.name].</span>", "<span class='notice'>You start ingesting [target] into your [src.name]...</span>")
|
||||
if(do_after(user, 30, target) && length(contents) < max_item_count)
|
||||
target.forceMove(src)
|
||||
user.visible_message("<span class='warning'>[hound.name]'s garbage processor groans lightly as [target.name] slips inside.</span>", "<span class='notice'>Your garbage compactor groans lightly as [target] slips inside.</span>")
|
||||
playsound(hound, 'sound/vore/gulpold.ogg', 50, 1)
|
||||
if(length(contents) > 11) //grow that tum after a certain junk amount
|
||||
hound.sleeper_r = 1
|
||||
hound.update_icons()
|
||||
return
|
||||
|
||||
else if(ishuman(target))
|
||||
var/mob/living/carbon/human/trashman = target
|
||||
if(patient)
|
||||
user << "<span class='warning'>Your [src.name] is already occupied.</span>"
|
||||
return
|
||||
if(trashman.buckled)
|
||||
user << "<span class='warning'>[trashman] is buckled and can not be put into your [src.name].</span>"
|
||||
return
|
||||
user.visible_message("<span class='warning'>[hound.name] is ingesting [trashman] into their [src.name].</span>", "<span class='notice'>You start ingesting [trashman] into your [src.name]...</span>")
|
||||
if(do_after(user, 30, trashman) && !patient && !trashman.buckled && length(contents) < max_item_count)
|
||||
trashman.forceMove(src)
|
||||
trashman.reset_perspective(src)
|
||||
update_patient()
|
||||
START_PROCESSING(SSobj, src)
|
||||
user.visible_message("<span class='warning'>[hound.name]'s garbage processor groans lightly as [trashman] slips inside.</span>", "<span class='notice'>Your garbage compactor groans lightly as [trashman] slips inside.</span>")
|
||||
playsound(hound, 'sound/vore/gulpold.ogg', 80, 1)
|
||||
return
|
||||
return
|
||||
|
||||
|
||||
// Pounce stuff for K-9
|
||||
|
||||
@@ -510,6 +833,10 @@
|
||||
force = 0
|
||||
throwforce = 0
|
||||
|
||||
/obj/item/weapon/dogborg/pounce/New()
|
||||
..()
|
||||
flags |= NOBLUDGEON
|
||||
|
||||
/mob/living/silicon/robot
|
||||
var/leaping = 0
|
||||
var/pounce_cooldown = 0
|
||||
@@ -548,7 +875,7 @@
|
||||
pixel_y = 10
|
||||
update_icons()
|
||||
throw_at(A, MAX_K9_LEAP_DIST, 1, spin=0, diagonals_first = 1)
|
||||
cell.charge = cell.charge - 500 //Doubled the energy consumption
|
||||
cell.use(500) //Doubled the energy consumption
|
||||
weather_immunities -= "lava"
|
||||
pounce_cooldown = !pounce_cooldown
|
||||
spawn(pounce_cooldown_time)
|
||||
|
||||
@@ -6,8 +6,28 @@
|
||||
zone = "groin"
|
||||
slot = "womb"
|
||||
w_class = 3
|
||||
var/internal = FALSE
|
||||
fluid_id = "femcum"
|
||||
producing = TRUE
|
||||
var/obj/item/organ/genital/vagina/linked_vag
|
||||
|
||||
/obj/item/organ/genital/womb/Initialize()
|
||||
. = ..()
|
||||
reagents.add_reagent(fluid_id, fluid_max_volume)
|
||||
|
||||
/obj/item/organ/genital/womb/on_life()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
if(reagents && producing)
|
||||
generate_femcum()
|
||||
|
||||
/obj/item/organ/genital/womb/proc/generate_femcum()
|
||||
reagents.maximum_volume = fluid_max_volume
|
||||
update_link()
|
||||
if(!linked_vag)
|
||||
return FALSE
|
||||
reagents.isolate_reagent(fluid_id)//remove old reagents if it changed and just clean up generally
|
||||
reagents.add_reagent(fluid_id, (fluid_mult * fluid_rate))//generate the cum
|
||||
|
||||
/obj/item/organ/genital/womb/update_link()
|
||||
if(owner)
|
||||
@@ -25,4 +45,4 @@
|
||||
linked_vag = null
|
||||
|
||||
/obj/item/organ/genital/womb/Destroy()
|
||||
return ..()
|
||||
return ..()
|
||||
|
||||
@@ -208,6 +208,8 @@
|
||||
var/generate_minimaps = 0
|
||||
var/grey_assistants = 0
|
||||
|
||||
var/id_console_jobslot_delay = 30
|
||||
|
||||
var/lavaland_budget = 60
|
||||
var/space_budget = 16
|
||||
|
||||
@@ -261,6 +263,8 @@
|
||||
|
||||
var/mice_roundstart = 10 // how many wire chewing rodents spawn at roundstart.
|
||||
|
||||
var/irc_announce_new_game = FALSE
|
||||
|
||||
/datum/configuration/New()
|
||||
gamemode_cache = typecacheof(/datum/game_mode,TRUE)
|
||||
for(var/T in gamemode_cache)
|
||||
@@ -410,6 +414,8 @@
|
||||
usewhitelist = TRUE
|
||||
if("allow_metadata")
|
||||
allow_Metadata = 1
|
||||
if("id_console_jobslot_delay")
|
||||
id_console_jobslot_delay = text2num(value)
|
||||
if("inactivity_period")
|
||||
inactivity_period = text2num(value) * 10 //documented as seconds in config.txt
|
||||
if("afk_period")
|
||||
@@ -424,7 +430,7 @@
|
||||
popup_admin_pm = 1
|
||||
if("allow_holidays")
|
||||
allow_holidays = 1
|
||||
if("useircbot")
|
||||
if("useircbot") //tgs2 support
|
||||
useircbot = 1
|
||||
if("ticklag")
|
||||
var/ticklag = text2num(value)
|
||||
@@ -537,6 +543,8 @@
|
||||
error_silence_time = text2num(value)
|
||||
if("error_msg_delay")
|
||||
error_msg_delay = text2num(value)
|
||||
if("irc_announce_new_game")
|
||||
irc_announce_new_game = TRUE
|
||||
else
|
||||
GLOB.config_error_log << "Unknown setting in configuration: '[name]'"
|
||||
|
||||
@@ -828,6 +836,8 @@
|
||||
if ("endmap")
|
||||
maplist[currentmap.map_name] = currentmap
|
||||
currentmap = null
|
||||
if ("disabled")
|
||||
currentmap = null
|
||||
else
|
||||
GLOB.config_error_log << "Unknown command in map vote config: '[command]'"
|
||||
|
||||
|
||||
+27
-24
@@ -14,15 +14,6 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
//Master -> SSPreInit -> GLOB -> world -> config -> SSInit -> Failsafe
|
||||
//GOT IT MEMORIZED?
|
||||
|
||||
GLOBAL_VAR_INIT(MC_restart_clear, 0)
|
||||
GLOBAL_VAR_INIT(MC_restart_timeout, 0)
|
||||
GLOBAL_VAR_INIT(MC_restart_count, 0)
|
||||
|
||||
|
||||
//current tick limit, assigned by the queue controller before running a subsystem.
|
||||
//used by check_tick as well so that the procs subsystems call can obey that SS's tick limits
|
||||
GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
|
||||
|
||||
/datum/controller/master
|
||||
name = "Master"
|
||||
|
||||
@@ -59,6 +50,14 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
|
||||
|
||||
var/current_runlevel //for scheduling different subsystems for different stages of the round
|
||||
|
||||
var/static/restart_clear = 0
|
||||
var/static/restart_timeout = 0
|
||||
var/static/restart_count = 0
|
||||
|
||||
//current tick limit, assigned before running a subsystem.
|
||||
//used by CHECK_TICK as well so that the procs subsystems call can obey that SS's tick limits
|
||||
var/static/current_ticklimit = TICK_LIMIT_RUNNING
|
||||
|
||||
/datum/controller/master/New()
|
||||
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
|
||||
subsystems = list()
|
||||
@@ -83,20 +82,21 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
|
||||
sortTim(subsystems, /proc/cmp_subsystem_init)
|
||||
reverseRange(subsystems)
|
||||
for(var/datum/controller/subsystem/ss in subsystems)
|
||||
testing("Shutdown [ss.name] subsystem")
|
||||
ss.Shutdown()
|
||||
|
||||
// Returns 1 if we created a new mc, 0 if we couldn't due to a recent restart,
|
||||
// -1 if we encountered a runtime trying to recreate it
|
||||
/proc/Recreate_MC()
|
||||
. = -1 //so if we runtime, things know we failed
|
||||
if (world.time < GLOB.MC_restart_timeout)
|
||||
if (world.time < Master.restart_timeout)
|
||||
return 0
|
||||
if (world.time < GLOB.MC_restart_clear)
|
||||
GLOB.MC_restart_count *= 0.5
|
||||
if (world.time < Master.restart_clear)
|
||||
Master.restart_count *= 0.5
|
||||
|
||||
var/delay = 50 * ++GLOB.MC_restart_count
|
||||
GLOB.MC_restart_timeout = world.time + delay
|
||||
GLOB.MC_restart_clear = world.time + (delay * 2)
|
||||
var/delay = 50 * ++Master.restart_count
|
||||
Master.restart_timeout = world.time + delay
|
||||
Master.restart_clear = world.time + (delay * 2)
|
||||
Master.processing = 0 //stop ticking this one
|
||||
try
|
||||
new/datum/controller/master()
|
||||
@@ -139,6 +139,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
|
||||
if(FireHim)
|
||||
Master.subsystems += new BadBoy.type //NEW_SS_GLOBAL will remove the old one
|
||||
subsystems = Master.subsystems
|
||||
current_runlevel = Master.current_runlevel
|
||||
StartProcessing(10)
|
||||
else
|
||||
to_chat(world, "<span class='boldannounce'>The Master Controller is having some issues, we will need to re-initialize EVERYTHING</span>")
|
||||
@@ -163,20 +164,21 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
|
||||
|
||||
var/start_timeofday = REALTIMEOFDAY
|
||||
// Initialize subsystems.
|
||||
GLOB.CURRENT_TICKLIMIT = config.tick_limit_mc_init
|
||||
current_ticklimit = config.tick_limit_mc_init
|
||||
for (var/datum/controller/subsystem/SS in subsystems)
|
||||
if (SS.flags & SS_NO_INIT)
|
||||
continue
|
||||
SS.Initialize(REALTIMEOFDAY)
|
||||
CHECK_TICK
|
||||
GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
current_ticklimit = TICK_LIMIT_RUNNING
|
||||
var/time = (REALTIMEOFDAY - start_timeofday) / 10
|
||||
|
||||
var/msg = "Initializations complete within [time] second[time == 1 ? "" : "s"]!"
|
||||
to_chat(world, "<span class='boldannounce'>[msg]</span>")
|
||||
log_world(msg)
|
||||
|
||||
SetRunLevel(1)
|
||||
if (!current_runlevel)
|
||||
SetRunLevel(1)
|
||||
|
||||
// Sort subsystems by display setting for easy access.
|
||||
sortTim(subsystems, /proc/cmp_subsystem_display)
|
||||
@@ -204,6 +206,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
|
||||
set waitfor = 0
|
||||
if(delay)
|
||||
sleep(delay)
|
||||
testing("Master starting processing")
|
||||
var/rtn = Loop()
|
||||
if (rtn > 0 || processing < 0)
|
||||
return //this was suppose to happen.
|
||||
@@ -274,7 +277,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
|
||||
while (1)
|
||||
tickdrift = max(0, MC_AVERAGE_FAST(tickdrift, (((REALTIMEOFDAY - init_timeofday) - (world.time - init_time)) / world.tick_lag)))
|
||||
if (processing <= 0)
|
||||
GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
current_ticklimit = TICK_LIMIT_RUNNING
|
||||
sleep(10)
|
||||
continue
|
||||
|
||||
@@ -282,7 +285,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
|
||||
// because sleeps are processed in the order received, so longer sleeps are more likely to run first
|
||||
if (world.tick_usage > TICK_LIMIT_MC)
|
||||
sleep_delta += 2
|
||||
GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING * 0.5
|
||||
current_ticklimit = TICK_LIMIT_RUNNING * 0.5
|
||||
sleep(world.tick_lag * (processing + sleep_delta))
|
||||
continue
|
||||
|
||||
@@ -320,7 +323,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
|
||||
if (!error_level)
|
||||
iteration++
|
||||
error_level++
|
||||
GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
current_ticklimit = TICK_LIMIT_RUNNING
|
||||
sleep(10)
|
||||
continue
|
||||
|
||||
@@ -332,7 +335,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
|
||||
if (!error_level)
|
||||
iteration++
|
||||
error_level++
|
||||
GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
|
||||
current_ticklimit = TICK_LIMIT_RUNNING
|
||||
sleep(10)
|
||||
continue
|
||||
error_level--
|
||||
@@ -343,7 +346,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
|
||||
iteration++
|
||||
last_run = world.time
|
||||
src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta)
|
||||
GLOB.CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING - (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc.
|
||||
current_ticklimit = TICK_LIMIT_RUNNING - (TICK_LIMIT_RUNNING * 0.25) //reserve the tail 1/4 of the next tick for the mc.
|
||||
sleep(world.tick_lag * (processing + sleep_delta))
|
||||
|
||||
|
||||
@@ -434,7 +437,7 @@ GLOBAL_VAR_INIT(CURRENT_TICKLIMIT, TICK_LIMIT_RUNNING)
|
||||
|
||||
tick_precentage = max(tick_precentage*0.5, tick_precentage-queue_node.tick_overrun)
|
||||
|
||||
GLOB.CURRENT_TICKLIMIT = round(world.tick_usage + tick_precentage)
|
||||
current_ticklimit = round(world.tick_usage + tick_precentage)
|
||||
|
||||
if (!(queue_node_flags & SS_TICKER))
|
||||
ran_non_ticker = TRUE
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#define SSAIR_SUPERCONDUCTIVITY 7
|
||||
|
||||
SUBSYSTEM_DEF(air)
|
||||
name = "Air"
|
||||
name = "Atmospherics"
|
||||
init_order = INIT_ORDER_AIR
|
||||
priority = 20
|
||||
wait = 5
|
||||
|
||||
@@ -98,7 +98,7 @@ SUBSYSTEM_DEF(atoms)
|
||||
else if(!A.initialized)
|
||||
BadInitializeCalls[the_type] |= BAD_INIT_DIDNT_INIT
|
||||
|
||||
return qdeleted || QDELETED(A)
|
||||
return qdeleted || QDELING(A)
|
||||
|
||||
/datum/controller/subsystem/atoms/proc/map_loader_begin()
|
||||
old_initialized = initialized
|
||||
@@ -150,7 +150,7 @@ SUBSYSTEM_DEF(atoms)
|
||||
/datum/controller/subsystem/atoms/Shutdown()
|
||||
var/initlog = InitLog()
|
||||
if(initlog)
|
||||
text2file("[GLOB.log_directory]/initialize.log", initlog)
|
||||
text2file(initlog, "[GLOB.log_directory]/initialize.log")
|
||||
|
||||
#undef BAD_INIT_QDEL_BEFORE
|
||||
#undef BAD_INIT_DIDNT_INIT
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
SUBSYSTEM_DEF(blackbox)
|
||||
name = "Blackbox"
|
||||
wait = 6000
|
||||
flags = SS_NO_TICK_CHECK
|
||||
|
||||
flags = SS_NO_TICK_CHECK | SS_NO_INIT
|
||||
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
||||
init_order = INIT_ORDER_BLACKBOX
|
||||
|
||||
var/list/msg_common = list()
|
||||
var/list/msg_science = list()
|
||||
var/list/msg_command = list()
|
||||
@@ -17,6 +19,8 @@ SUBSYSTEM_DEF(blackbox)
|
||||
|
||||
var/list/feedback = list() //list of datum/feedback_variable
|
||||
|
||||
var/sealed = FALSE //time to stop tracking stats?
|
||||
|
||||
//poll population
|
||||
/datum/controller/subsystem/blackbox/fire()
|
||||
if(!SSdbcore.Connect())
|
||||
@@ -26,7 +30,7 @@ SUBSYSTEM_DEF(blackbox)
|
||||
if(M.client)
|
||||
playercount += 1
|
||||
var/admincount = GLOB.admins.len
|
||||
var/datum/DBQuery/query_record_playercount = SSdbcore.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, admincount, time, server_ip, server_port) VALUES ([playercount], [admincount], '[SQLtime()]', INET_ATON('[world.internet_address]'), '[world.port]')")
|
||||
var/datum/DBQuery/query_record_playercount = SSdbcore.NewQuery("INSERT INTO [format_table_name("legacy_population")] (playercount, admincount, time, server_ip, server_port) VALUES ([playercount], [admincount], '[SQLtime()]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]')")
|
||||
query_record_playercount.Execute()
|
||||
|
||||
/datum/controller/subsystem/blackbox/Recover()
|
||||
@@ -42,9 +46,10 @@ SUBSYSTEM_DEF(blackbox)
|
||||
msg_cargo = SSblackbox.msg_cargo
|
||||
msg_other = SSblackbox.msg_other
|
||||
|
||||
|
||||
feedback = SSblackbox.feedback
|
||||
|
||||
sealed = SSblackbox.sealed
|
||||
|
||||
//no touchie
|
||||
/datum/controller/subsystem/blackbox/can_vv_get(var_name)
|
||||
if(var_name == "feedback")
|
||||
@@ -55,6 +60,9 @@ SUBSYSTEM_DEF(blackbox)
|
||||
return FALSE
|
||||
|
||||
/datum/controller/subsystem/blackbox/Shutdown()
|
||||
sealed = FALSE
|
||||
set_val("ahelp_unresolved", GLOB.ahelp_tickets.active_tickets.len)
|
||||
|
||||
var/pda_msg_amt = 0
|
||||
var/rc_msg_amt = 0
|
||||
|
||||
@@ -80,26 +88,21 @@ SUBSYSTEM_DEF(blackbox)
|
||||
add_details("radio_usage","PDA-[pda_msg_amt]")
|
||||
add_details("radio_usage","RC-[rc_msg_amt]")
|
||||
|
||||
set_details("round_end","[time2text(world.realtime)]") //This one MUST be the last one that gets set.
|
||||
|
||||
if (!SSdbcore.Connect())
|
||||
return
|
||||
|
||||
var/sqlrowlist = ""
|
||||
var/list/sqlrowlist = list()
|
||||
|
||||
for (var/datum/feedback_variable/FV in feedback)
|
||||
if (sqlrowlist != "")
|
||||
sqlrowlist += ", " //a comma (,) at the start of the first row to insert will trigger a SQL error
|
||||
|
||||
sqlrowlist += list(list("time" = "Now()", "round_id" = GLOB.round_id, "var_name" = "'[sanitizeSQL(FV.get_variable())]'", "var_value" = FV.get_value(), "details" = "'[sanitizeSQL(FV.get_details())]'"))
|
||||
|
||||
if (sqlrowlist == "")
|
||||
if (!length(sqlrowlist))
|
||||
return
|
||||
|
||||
var/datum/DBQuery/query_feedback_save = SSdbcore.NewQuery("INSERT DELAYED IGNORE INTO [format_table_name("feedback")] VALUES " + sqlrowlist)
|
||||
query_feedback_save.Execute()
|
||||
SSdbcore.MassInsert(format_table_name("feedback"), sqlrowlist, ignore_errors = TRUE, delayed = TRUE)
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/LogBroadcast(blackbox_msg, freq)
|
||||
if(sealed)
|
||||
return
|
||||
switch(freq)
|
||||
if(1459)
|
||||
msg_common += blackbox_msg
|
||||
@@ -134,26 +137,38 @@ SUBSYSTEM_DEF(blackbox)
|
||||
return FV
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/set_val(variable, value)
|
||||
if(sealed)
|
||||
return
|
||||
var/datum/feedback_variable/FV = find_feedback_datum(variable)
|
||||
FV.set_value(value)
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/inc(variable, value)
|
||||
if(sealed)
|
||||
return
|
||||
var/datum/feedback_variable/FV = find_feedback_datum(variable)
|
||||
FV.inc(value)
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/dec(variable,value)
|
||||
if(sealed)
|
||||
return
|
||||
var/datum/feedback_variable/FV = find_feedback_datum(variable)
|
||||
FV.dec(value)
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/set_details(variable,details)
|
||||
if(sealed)
|
||||
return
|
||||
var/datum/feedback_variable/FV = find_feedback_datum(variable)
|
||||
FV.set_details(details)
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/add_details(variable,details)
|
||||
if(sealed)
|
||||
return
|
||||
var/datum/feedback_variable/FV = find_feedback_datum(variable)
|
||||
FV.add_details(details)
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/ReportDeath(mob/living/L)
|
||||
if(sealed)
|
||||
return
|
||||
if(!SSdbcore.Connect())
|
||||
return
|
||||
if(!L || !L.key || !L.mind)
|
||||
@@ -171,19 +186,29 @@ SUBSYSTEM_DEF(blackbox)
|
||||
var/mob/LA = L.lastattacker
|
||||
laname = sanitizeSQL(LA.real_name)
|
||||
lakey = sanitizeSQL(LA.key)
|
||||
var/sqlgender = sanitizeSQL(L.gender)
|
||||
var/sqlbrute = sanitizeSQL(L.getBruteLoss())
|
||||
var/sqlfire = sanitizeSQL(L.getFireLoss())
|
||||
var/sqlbrain = sanitizeSQL(L.getBrainLoss())
|
||||
var/sqloxy = sanitizeSQL(L.getOxyLoss())
|
||||
var/sqltox = sanitizeSQL(L.getStaminaLoss())
|
||||
var/sqlclone = sanitizeSQL(L.getStaminaLoss())
|
||||
var/sqltox = sanitizeSQL(L.getToxLoss())
|
||||
var/sqlclone = sanitizeSQL(L.getCloneLoss())
|
||||
var/sqlstamina = sanitizeSQL(L.getStaminaLoss())
|
||||
var/coord = sanitizeSQL("[L.x], [L.y], [L.z]")
|
||||
var/x_coord = sanitizeSQL(L.x)
|
||||
var/y_coord = sanitizeSQL(L.y)
|
||||
var/z_coord = sanitizeSQL(L.z)
|
||||
var/map = sanitizeSQL(SSmapping.config.map_name)
|
||||
var/datum/DBQuery/query_report_death = SSdbcore.NewQuery("INSERT INTO [format_table_name("death")] (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, toxloss, cloneloss, staminaloss, coord, mapname, server_ip, server_port) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[SQLtime()]', '[laname]', '[lakey]', '[sqlgender]', [sqlbrute], [sqlfire], [sqlbrain], [sqloxy], [sqltox], [sqlclone], [sqlstamina], '[coord]', '[map]', INET_ATON('[world.internet_address]'), '[world.port]')")
|
||||
var/datum/DBQuery/query_report_death = SSdbcore.NewQuery("INSERT INTO [format_table_name("death")] (pod, x_coord, y_coord, z_coord, mapname, server_ip, server_port, round_id, tod, job, special, name, byondkey, laname, lakey, bruteloss, fireloss, brainloss, oxyloss, toxloss, cloneloss, staminaloss) VALUES ('[sqlpod]', '[x_coord]', '[y_coord]', '[z_coord]', '[map]', INET_ATON(IF('[world.internet_address]' LIKE '', '0', '[world.internet_address]')), '[world.port]', [GLOB.round_id], '[SQLtime()]', '[sqljob]', '[sqlspecial]', '[sqlname]', '[sqlkey]', '[laname]', '[lakey]', [sqlbrute], [sqlfire], [sqlbrain], [sqloxy], [sqltox], [sqlclone], [sqlstamina])")
|
||||
query_report_death.Execute()
|
||||
|
||||
/datum/controller/subsystem/blackbox/proc/Seal()
|
||||
if(sealed)
|
||||
return
|
||||
if(IsAdminAdvancedProcCall())
|
||||
var/msg = "[key_name_admin(usr)] sealed the blackbox!"
|
||||
message_admins(msg)
|
||||
log_game("Blackbox sealed[IsAdminAdvancedProcCall() ? " by [key_name(usr)]" : ""].")
|
||||
sealed = TRUE
|
||||
|
||||
|
||||
//feedback variable datum, for storing all kinds of data
|
||||
/datum/feedback_variable
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
SUBSYSTEM_DEF(dbcore)
|
||||
name = "Database"
|
||||
flags = SS_NO_INIT|SS_NO_FIRE
|
||||
|
||||
init_order = INIT_ORDER_DBCORE
|
||||
var/const/FAILED_DB_CONNECTION_CUTOFF = 5
|
||||
|
||||
var/const/Default_Cursor = 0
|
||||
@@ -23,12 +23,18 @@ SUBSYSTEM_DEF(dbcore)
|
||||
var/failed_connections = 0
|
||||
|
||||
/datum/controller/subsystem/dbcore/PreInit()
|
||||
_db_con = _dm_db_new_con()
|
||||
if(!_db_con)
|
||||
_db_con = _dm_db_new_con()
|
||||
|
||||
/datum/controller/subsystem/dbcore/Recover()
|
||||
_db_con = SSdbcore._db_con
|
||||
|
||||
/datum/controller/subsystem/dbcore/Shutdown()
|
||||
//This is as close as we can get to the true round end before Disconnect() without changing where it's called, defeating the reason this is a subsystem
|
||||
if(SSdbcore.Connect())
|
||||
var/sql_station_name = sanitizeSQL(station_name())
|
||||
var/datum/DBQuery/query_round_end = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET end_datetime = Now(), game_mode_result = '[SSticker.mode_result]', end_state = '[SSticker.end_state]', station_name = '[sql_station_name]' WHERE id = [GLOB.round_id]")
|
||||
query_round_end.Execute()
|
||||
if(IsConnected())
|
||||
Disconnect()
|
||||
|
||||
@@ -47,7 +53,7 @@ SUBSYSTEM_DEF(dbcore)
|
||||
|
||||
if(failed_connections > FAILED_DB_CONNECTION_CUTOFF) //If it failed to establish a connection more than 5 times in a row, don't bother attempting to connect anymore.
|
||||
return FALSE
|
||||
|
||||
|
||||
if(!config.sql_enabled)
|
||||
return FALSE
|
||||
|
||||
@@ -170,16 +176,16 @@ Delayed insert mode was removed in mysql 7 and only works with MyISAM type table
|
||||
var/_db_query
|
||||
|
||||
/datum/DBQuery/New(sql_query, datum/controller/subsystem/dbcore/connection_handler, cursor_handler)
|
||||
if(sql_query)
|
||||
if(sql_query)
|
||||
sql = sql_query
|
||||
if(connection_handler)
|
||||
if(connection_handler)
|
||||
db_connection = connection_handler
|
||||
if(cursor_handler)
|
||||
if(cursor_handler)
|
||||
default_cursor = cursor_handler
|
||||
item = list()
|
||||
_db_query = _dm_db_new_query()
|
||||
|
||||
/datum/DBQuery/proc/Connect(datum/controller/subsystem/dbcore/connection_handler)
|
||||
/datum/DBQuery/proc/Connect(datum/controller/subsystem/dbcore/connection_handler)
|
||||
db_connection = connection_handler
|
||||
|
||||
/datum/DBQuery/proc/warn_execute()
|
||||
@@ -193,16 +199,16 @@ Delayed insert mode was removed in mysql 7 and only works with MyISAM type table
|
||||
if(!. && log_error)
|
||||
log_sql("[ErrorMsg()] | Query used: [sql]")
|
||||
|
||||
/datum/DBQuery/proc/NextRow()
|
||||
/datum/DBQuery/proc/NextRow()
|
||||
return _dm_db_next_row(_db_query,item,conversions)
|
||||
|
||||
/datum/DBQuery/proc/RowsAffected()
|
||||
return _dm_db_rows_affected(_db_query)
|
||||
|
||||
/datum/DBQuery/proc/RowCount()
|
||||
/datum/DBQuery/proc/RowCount()
|
||||
return _dm_db_row_count(_db_query)
|
||||
|
||||
/datum/DBQuery/proc/ErrorMsg()
|
||||
/datum/DBQuery/proc/ErrorMsg()
|
||||
return _dm_db_error_msg(_db_query)
|
||||
|
||||
/datum/DBQuery/proc/Columns()
|
||||
@@ -231,11 +237,11 @@ Delayed insert mode was removed in mysql 7 and only works with MyISAM type table
|
||||
return db_connection.Quote(str)
|
||||
|
||||
/datum/DBQuery/proc/SetConversion(column,conversion)
|
||||
if(istext(column))
|
||||
if(istext(column))
|
||||
column = columns.Find(column)
|
||||
if(!conversions)
|
||||
if(!conversions)
|
||||
conversions = list(column)
|
||||
else if(conversions.len < column)
|
||||
else if(conversions.len < column)
|
||||
conversions.len = column
|
||||
conversions[column] = conversion
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ SUBSYSTEM_DEF(events)
|
||||
//These are needed because /area/engine has to be removed from the list, but we still want these areas to get fucked up.
|
||||
var/list/danger_areas = list(
|
||||
/area/engine/break_room,
|
||||
/area/engine/chiefs_office)
|
||||
/area/crew_quarters/heads/chief)
|
||||
|
||||
//Need to locate() as it's just a list of paths.
|
||||
return locate(pick((GLOB.the_station_areas - safe_areas) + danger_areas))
|
||||
|
||||
@@ -0,0 +1,510 @@
|
||||
SUBSYSTEM_DEF(explosion)
|
||||
priority = 99
|
||||
wait = 1
|
||||
flags = SS_TICKER|SS_NO_INIT
|
||||
|
||||
var/list/explosions
|
||||
|
||||
var/rebuild_tick_split_count = FALSE
|
||||
var/tick_portions_required = 0
|
||||
|
||||
var/list/logs
|
||||
|
||||
var/list/zlevels_that_ignore_bombcap
|
||||
var/list/doppler_arrays
|
||||
|
||||
//legacy caps, set by config
|
||||
var/devastation_cap = 3
|
||||
var/heavy_cap = 7
|
||||
var/light_cap = 14
|
||||
var/flash_cap = 14
|
||||
var/flame_cap = 14
|
||||
var/dyn_ex_scale = 0.5
|
||||
|
||||
var/id_counter = 0
|
||||
|
||||
/datum/controller/subsystem/explosion/PreInit()
|
||||
doppler_arrays = list()
|
||||
logs = list()
|
||||
explosions = list()
|
||||
zlevels_that_ignore_bombcap = list("[ZLEVEL_MINING]")
|
||||
|
||||
/datum/controller/subsystem/explosion/Shutdown()
|
||||
QDEL_LIST(explosions)
|
||||
QDEL_LIST(logs)
|
||||
zlevels_that_ignore_bombcap.Cut()
|
||||
|
||||
/datum/controller/subsystem/explosion/Recover()
|
||||
explosions = SSexplosion.explosions
|
||||
logs = SSexplosion.logs
|
||||
id_counter = SSexplosion.id_counter
|
||||
rebuild_tick_split_count = TRUE
|
||||
zlevels_that_ignore_bombcap = SSexplosion.zlevels_that_ignore_bombcap
|
||||
doppler_arrays = SSexplosion.doppler_arrays
|
||||
|
||||
devastation_cap = SSexplosion.devastation_cap
|
||||
heavy_cap = SSexplosion.heavy_cap
|
||||
light_cap = SSexplosion.light_cap
|
||||
flash_cap = SSexplosion.flash_cap
|
||||
flame_cap = SSexplosion.flame_cap
|
||||
dyn_ex_scale = SSexplosion.dyn_ex_scale
|
||||
|
||||
/datum/controller/subsystem/explosion/fire()
|
||||
var/list/cached_explosions = explosions
|
||||
var/num_explosions = cached_explosions.len
|
||||
if(!num_explosions)
|
||||
return
|
||||
|
||||
//figure exactly how many tick splits are required
|
||||
var/num_splits
|
||||
if(rebuild_tick_split_count)
|
||||
var/reactionary = config.reactionary_explosions
|
||||
num_splits = num_explosions
|
||||
for(var/I in cached_explosions)
|
||||
var/datum/explosion/E = I
|
||||
if(!E.turfs_processed)
|
||||
++num_splits
|
||||
if(reactionary && !E.densities_processed)
|
||||
++num_splits
|
||||
tick_portions_required = num_splits
|
||||
else
|
||||
num_splits = tick_portions_required
|
||||
|
||||
MC_SPLIT_TICK_INIT(num_splits)
|
||||
|
||||
for(var/I in cached_explosions)
|
||||
var/datum/explosion/E = I
|
||||
|
||||
var/etp = E.turfs_processed
|
||||
if(!etp)
|
||||
if(GatherTurfs(E))
|
||||
--tick_portions_required
|
||||
etp = TRUE
|
||||
MC_SPLIT_TICK
|
||||
|
||||
var/edp = E.densities_processed
|
||||
if(!edp)
|
||||
if(DensityCalculate(E, etp))
|
||||
--tick_portions_required
|
||||
edp = TRUE
|
||||
MC_SPLIT_TICK
|
||||
|
||||
if(ProcessExplosion(E, edp)) //splits the tick
|
||||
--tick_portions_required
|
||||
explosions -= E
|
||||
logs += E
|
||||
NotifyDopplers(E)
|
||||
MC_SPLIT_TICK
|
||||
|
||||
/datum/controller/subsystem/explosion/proc/NotifyDopplers(datum/explosion/E)
|
||||
for(var/array in doppler_arrays)
|
||||
var/obj/machinery/doppler_array/A = array
|
||||
A.sense_explosion(E.epicenter, E.devastation, E.heavy, E.light, E.finished_at - E.started_at, E.orig_dev_range, E.orig_heavy_range, E.orig_light_range)
|
||||
|
||||
/datum/controller/subsystem/explosion/proc/Create(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = TRUE, ignorecap = FALSE, flame_range = 0 , silent = FALSE, smoke = FALSE)
|
||||
epicenter = get_turf(epicenter)
|
||||
if(!epicenter)
|
||||
return
|
||||
|
||||
if(adminlog)
|
||||
message_admins("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area: [get_area(epicenter)] [ADMIN_COORDJMP(epicenter)]")
|
||||
log_game("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z])")
|
||||
|
||||
var/datum/explosion/E = new(++id_counter, epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, flame_range, silent, smoke, ignorecap)
|
||||
|
||||
if(heavy_impact_range > 1)
|
||||
var/datum/effect_system/explosion/Eff
|
||||
if(smoke)
|
||||
Eff = new /datum/effect_system/explosion/smoke
|
||||
else
|
||||
Eff = new
|
||||
Eff.set_up(epicenter)
|
||||
Eff.start()
|
||||
|
||||
//flash mobs
|
||||
if(flash_range)
|
||||
for(var/mob/living/L in viewers(flash_range, epicenter))
|
||||
L.flash_act()
|
||||
|
||||
if(!silent)
|
||||
ExplosionSound(epicenter, devastation_range, heavy_impact_range, E.extent)
|
||||
|
||||
//add to SS
|
||||
if(E.extent)
|
||||
tick_portions_required += 2 + (config.reactionary_explosions ? 1 : 0)
|
||||
explosions += E
|
||||
else
|
||||
logs += E //Already done processing
|
||||
|
||||
/datum/controller/subsystem/explosion/proc/CreateDynamic(atom/epicenter, power, flash_range, adminlog = TRUE, ignorecap = TRUE, flame_range = 0 , silent = FALSE, smoke = TRUE)
|
||||
if(!power)
|
||||
return
|
||||
var/range = round((2 * power) ** dyn_ex_scale)
|
||||
Create(epicenter, round(range * 0.25), round(range * 0.5), round(range), flash_range*range, adminlog, ignorecap, flame_range*range, silent, smoke)
|
||||
|
||||
// Using default dyn_ex scale:
|
||||
// 100 explosion power is a (5, 10, 20) explosion.
|
||||
// 75 explosion power is a (4, 8, 17) explosion.
|
||||
// 50 explosion power is a (3, 7, 14) explosion.
|
||||
// 25 explosion power is a (2, 5, 10) explosion.
|
||||
// 10 explosion power is a (1, 3, 6) explosion.
|
||||
// 5 explosion power is a (0, 1, 3) explosion.
|
||||
// 1 explosion power is a (0, 0, 1) explosion.
|
||||
|
||||
/datum/explosion
|
||||
var/explosion_id
|
||||
var/turf/epicenter
|
||||
|
||||
var/started_at
|
||||
var/finished_at
|
||||
var/tick_started
|
||||
var/tick_finished
|
||||
|
||||
var/turfs_processed = FALSE
|
||||
var/densities_processed = FALSE
|
||||
|
||||
var/orig_dev_range
|
||||
var/orig_heavy_range
|
||||
var/orig_light_range
|
||||
var/orig_flash_range
|
||||
var/orig_flame_range
|
||||
|
||||
var/devastation
|
||||
var/heavy
|
||||
var/light
|
||||
var/extent
|
||||
|
||||
var/flash
|
||||
var/flame
|
||||
|
||||
var/gather_dist = 0
|
||||
|
||||
var/list/gathered_turfs
|
||||
var/list/calculated_turfs
|
||||
|
||||
var/list/unsafe_turfs
|
||||
|
||||
/datum/explosion/New(id, turf/epi, devastation_range, heavy_impact_range, light_impact_range, flash_range, flame_range, silent, smoke, ignorecap)
|
||||
explosion_id = id
|
||||
epicenter = epi
|
||||
|
||||
densities_processed = !config.reactionary_explosions
|
||||
|
||||
orig_dev_range = devastation_range
|
||||
orig_heavy_range = heavy_impact_range
|
||||
orig_light_range = light_impact_range
|
||||
orig_flash_range = flash_range
|
||||
orig_flame_range = flame_range
|
||||
|
||||
if(!ignorecap && !("[epicenter.z]" in SSexplosion.zlevels_that_ignore_bombcap))
|
||||
//Clamp all values
|
||||
devastation_range = min(SSexplosion.devastation_cap, devastation_range)
|
||||
heavy_impact_range = min(SSexplosion.heavy_cap, heavy_impact_range)
|
||||
light_impact_range = min(SSexplosion.light_cap, light_impact_range)
|
||||
flash_range = min(SSexplosion.flash_cap, flash_range)
|
||||
flame_range = min(SSexplosion.flame_cap, flame_range)
|
||||
|
||||
//store this
|
||||
devastation = devastation_range
|
||||
heavy = heavy_impact_range
|
||||
light = light_impact_range
|
||||
|
||||
extent = max(devastation_range, heavy_impact_range, light_impact_range, flame_range)
|
||||
|
||||
flash = flash_range
|
||||
flame = flame_range
|
||||
|
||||
started_at = REALTIMEOFDAY
|
||||
tick_started = world.time
|
||||
|
||||
gathered_turfs = list()
|
||||
calculated_turfs = list()
|
||||
unsafe_turfs = list()
|
||||
|
||||
// Play sounds; we want sounds to be different depending on distance so we will manually do it ourselves.
|
||||
// Stereo users will also hear the direction of the explosion!
|
||||
|
||||
// Calculate far explosion sound range. Only allow the sound effect for heavy/devastating explosions.
|
||||
// 3/7/14 will calculate to 80 + 35
|
||||
/proc/ExplosionSound(turf/epicenter, devastation_range, heavy_impact_range, extent)
|
||||
var/far_dist = 0
|
||||
far_dist += heavy_impact_range * 5
|
||||
far_dist += devastation_range * 20
|
||||
|
||||
var/z0 = epicenter.z
|
||||
|
||||
var/frequency = get_rand_frequency()
|
||||
var/ex_sound = get_sfx("explosion")
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
// Double check for client
|
||||
var/turf/M_turf = get_turf(M)
|
||||
if(M_turf && M_turf.z == z0)
|
||||
var/dist = get_dist(M_turf, epicenter)
|
||||
// If inside the blast radius + world.view - 2
|
||||
if(dist <= round(extent + world.view - 2, 1))
|
||||
M.playsound_local(epicenter, ex_sound, 100, 1, frequency, falloff = 5)
|
||||
// You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station.
|
||||
else if(dist <= far_dist)
|
||||
var/far_volume = Clamp(far_dist, 30, 50) // Volume is based on explosion size and dist
|
||||
far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion
|
||||
M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', far_volume, 1, frequency, falloff = 5)
|
||||
|
||||
/datum/explosion/Destroy()
|
||||
SSexplosion.explosions -= src
|
||||
SSexplosion.logs -= src
|
||||
LAZYCLEARLIST(gathered_turfs)
|
||||
LAZYCLEARLIST(calculated_turfs)
|
||||
LAZYCLEARLIST(unsafe_turfs)
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/explosion/proc/GatherTurfs(datum/explosion/E)
|
||||
var/turf/epicenter = E.epicenter
|
||||
|
||||
var/x0 = epicenter.x
|
||||
var/y0 = epicenter.y
|
||||
var/z0 = epicenter.z
|
||||
|
||||
var/c_dist = E.gather_dist
|
||||
var/dist = E.extent
|
||||
|
||||
var/list/L = E.gathered_turfs
|
||||
|
||||
if(!c_dist)
|
||||
L += epicenter
|
||||
++c_dist
|
||||
|
||||
while( c_dist <= dist )
|
||||
var/y = y0 + c_dist
|
||||
var/x = x0 - c_dist + 1
|
||||
for(x in x to x0 + c_dist)
|
||||
var/turf/T = locate(x, y, z0)
|
||||
if(T)
|
||||
L += T
|
||||
|
||||
y = y0 + c_dist - 1
|
||||
x = x0 + c_dist
|
||||
for(y in y0 - c_dist to y)
|
||||
var/turf/T = locate(x, y, z0)
|
||||
if(T)
|
||||
L += T
|
||||
|
||||
y = y0 - c_dist
|
||||
x = x0 + c_dist - 1
|
||||
for(x in x0 - c_dist to x)
|
||||
var/turf/T = locate(x, y, z0)
|
||||
if(T)
|
||||
L += T
|
||||
|
||||
y = y0 - c_dist + 1
|
||||
x = x0 - c_dist
|
||||
for(y in y to y0 + c_dist)
|
||||
var/turf/T = locate(x, y, z0)
|
||||
if(T)
|
||||
L += T
|
||||
++c_dist
|
||||
|
||||
if(MC_TICK_CHECK)
|
||||
break
|
||||
|
||||
if(c_dist > dist)
|
||||
E.turfs_processed = TRUE
|
||||
return TRUE
|
||||
else
|
||||
E.gather_dist = c_dist
|
||||
return FALSE
|
||||
|
||||
/datum/controller/subsystem/explosion/proc/DensityCalculate(datum/explosion/E, done_gathering_turfs)
|
||||
var/list/L = E.calculated_turfs
|
||||
var/cut_to = 1
|
||||
for(var/I in E.gathered_turfs) // we cache the explosion block rating of every turf in the explosion area
|
||||
var/turf/T = I
|
||||
++cut_to
|
||||
|
||||
var/current_exp_block = T.density ? T.explosion_block : 0
|
||||
|
||||
for(var/obj/machinery/door/D in T)
|
||||
if(D.density)
|
||||
current_exp_block += D.explosion_block
|
||||
|
||||
for(var/obj/structure/window/W in T)
|
||||
if(W.reinf && W.fulltile)
|
||||
current_exp_block += W.explosion_block
|
||||
|
||||
for(var/obj/structure/blob/B in T)
|
||||
current_exp_block += B.explosion_block
|
||||
|
||||
L[T] = current_exp_block
|
||||
|
||||
if(MC_TICK_CHECK)
|
||||
E.gathered_turfs.Cut(1, cut_to)
|
||||
return FALSE
|
||||
|
||||
E.gathered_turfs.Cut()
|
||||
return done_gathering_turfs
|
||||
|
||||
/datum/controller/subsystem/explosion/proc/ProcessExplosion(datum/explosion/E, done_calculating_turfs)
|
||||
//cache shit for speed
|
||||
var/id = E.explosion_id
|
||||
|
||||
var/list/cached_unsafe = E.unsafe_turfs
|
||||
var/list/cached_exp_block = E.calculated_turfs
|
||||
var/list/affected_turfs = cached_exp_block ? cached_exp_block : E.gathered_turfs
|
||||
|
||||
var/devastation_range = E.devastation
|
||||
var/heavy_impact_range = E.heavy
|
||||
var/light_impact_range = E.light
|
||||
|
||||
var/flame_range = E.flame
|
||||
var/throw_range_max = E.extent
|
||||
|
||||
var/turf/epi = E.epicenter
|
||||
|
||||
var/x0 = epi.x
|
||||
var/y0 = epi.y
|
||||
|
||||
var/cut_to = 1
|
||||
for(var/TI in affected_turfs)
|
||||
var/turf/T = TI
|
||||
++cut_to
|
||||
|
||||
var/init_dist = cheap_hypotenuse(T.x, T.y, x0, y0)
|
||||
var/dist = init_dist
|
||||
|
||||
if(cached_exp_block)
|
||||
var/turf/Trajectory = T
|
||||
while(Trajectory != epi)
|
||||
Trajectory = get_step_towards(Trajectory, epi)
|
||||
dist += cached_exp_block[Trajectory]
|
||||
|
||||
var/flame_dist = dist < flame_range
|
||||
var/throw_dist = dist
|
||||
|
||||
if(dist < devastation_range)
|
||||
dist = 1
|
||||
else if(dist < heavy_impact_range)
|
||||
dist = 2
|
||||
else if(dist < light_impact_range)
|
||||
dist = 3
|
||||
else
|
||||
dist = 0
|
||||
|
||||
//------- EX_ACT AND TURF FIRES -------
|
||||
|
||||
if(flame_dist && prob(40) && !isspaceturf(T) && !T.density)
|
||||
new /obj/effect/hotspot(T) //Mostly for ambience!
|
||||
|
||||
if(dist > 0)
|
||||
T.explosion_level = max(T.explosion_level, dist) //let the bigger one have it
|
||||
T.explosion_id = id
|
||||
T.ex_act(dist)
|
||||
cached_unsafe += T
|
||||
|
||||
//--- THROW ITEMS AROUND ---
|
||||
|
||||
var/throw_dir = get_dir(epi, T)
|
||||
for(var/obj/item/I in T)
|
||||
if(!I.anchored)
|
||||
var/throw_range = rand(throw_dist, throw_range_max)
|
||||
var/turf/throw_at = get_ranged_target_turf(I, throw_dir, throw_range)
|
||||
I.throw_speed = 4 //Temporarily change their throw_speed for embedding purposes (Resets when it finishes throwing, regardless of hitting anything)
|
||||
I.throw_at(throw_at, throw_range, 4)
|
||||
|
||||
if(MC_TICK_CHECK)
|
||||
var/circumference = (PI * (init_dist + 4) * 2) //+4 to radius to prevent shit gaps
|
||||
if(cached_unsafe.len > circumference) //only do this every revolution
|
||||
for(var/Unexplode in cached_unsafe)
|
||||
var/turf/UnexplodeT = Unexplode
|
||||
UnexplodeT.explosion_level = 0
|
||||
cached_unsafe.Cut()
|
||||
done_calculating_turfs = FALSE
|
||||
break
|
||||
|
||||
affected_turfs.Cut(1, cut_to)
|
||||
|
||||
if(!done_calculating_turfs)
|
||||
return FALSE
|
||||
|
||||
//unfuck the shit
|
||||
for(var/Unexplode in cached_unsafe)
|
||||
var/turf/UnexplodeT = Unexplode
|
||||
UnexplodeT.explosion_level = 0
|
||||
cached_unsafe.Cut()
|
||||
|
||||
E.finished_at = REALTIMEOFDAY
|
||||
E.tick_finished = world.time
|
||||
|
||||
return TRUE
|
||||
|
||||
/client/proc/check_bomb_impacts()
|
||||
set name = "Check Bomb Impact"
|
||||
set category = "Debug"
|
||||
|
||||
var/newmode = alert("Use reactionary explosions?","Check Bomb Impact", "Yes", "No")
|
||||
var/turf/epicenter = get_turf(mob)
|
||||
if(!epicenter)
|
||||
return
|
||||
|
||||
var/x0 = epicenter.x
|
||||
var/y0 = epicenter.y
|
||||
|
||||
var/dev = 0
|
||||
var/heavy = 0
|
||||
var/light = 0
|
||||
var/list/choices = list("Small Bomb","Medium Bomb","Big Bomb","Custom Bomb")
|
||||
var/choice = input("Bomb Size?") in choices
|
||||
switch(choice)
|
||||
if(null)
|
||||
return 0
|
||||
if("Small Bomb")
|
||||
dev = 1
|
||||
heavy = 2
|
||||
light = 3
|
||||
if("Medium Bomb")
|
||||
dev = 2
|
||||
heavy = 3
|
||||
light = 4
|
||||
if("Big Bomb")
|
||||
dev = 3
|
||||
heavy = 5
|
||||
light = 7
|
||||
if("Custom Bomb")
|
||||
dev = input("Devestation range (Tiles):") as num
|
||||
heavy = input("Heavy impact range (Tiles):") as num
|
||||
light = input("Light impact range (Tiles):") as num
|
||||
else
|
||||
return
|
||||
|
||||
var/datum/explosion/E = new(null, epicenter, dev, heavy, light, ignorecap = TRUE)
|
||||
|
||||
while(!SSexplosion.GatherTurfs(E))
|
||||
stoplag()
|
||||
var/list/turfs
|
||||
if(newmode)
|
||||
while(!SSexplosion.DensityCalculate(E, TRUE))
|
||||
stoplag()
|
||||
turfs = E.calculated_turfs.Copy()
|
||||
else
|
||||
turfs = E.gathered_turfs.Copy()
|
||||
|
||||
qdel(E)
|
||||
|
||||
for(var/I in turfs)
|
||||
var/turf/T = I
|
||||
var/dist = cheap_hypotenuse(T.x, T.y, x0, y0) + turfs[T]
|
||||
|
||||
if(dist < dev)
|
||||
T.color = "red"
|
||||
T.maptext = "Dev"
|
||||
else if (dist < heavy)
|
||||
T.color = "yellow"
|
||||
T.maptext = "Heavy"
|
||||
else if (dist < light)
|
||||
T.color = "blue"
|
||||
T.maptext = "Light"
|
||||
CHECK_TICK
|
||||
|
||||
sleep(100)
|
||||
for(var/I in turfs)
|
||||
var/turf/T = I
|
||||
T.color = null
|
||||
T.maptext = null
|
||||
@@ -1,27 +0,0 @@
|
||||
|
||||
SUBSYSTEM_DEF(fields)
|
||||
name = "Fields"
|
||||
wait = 2
|
||||
priority = 40
|
||||
flags = SS_KEEP_TIMING
|
||||
var/list/datum/proximity_monitor/advanced/running = list()
|
||||
var/list/datum/proximity_monitor/advanced/currentrun = list()
|
||||
|
||||
/datum/controller/subsystem/fields/fire(resumed = 0)
|
||||
if(!resumed)
|
||||
src.currentrun = running.Copy()
|
||||
var/list/currentrun = src.currentrun
|
||||
while(currentrun.len)
|
||||
var/datum/proximity_monitor/advanced/F = currentrun[currentrun.len]
|
||||
currentrun.len--
|
||||
if(!F.requires_processing)
|
||||
continue
|
||||
F.process()
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
/datum/controller/subsystem/fields/proc/register_new_field(datum/proximity_monitor/advanced/F)
|
||||
running += F
|
||||
|
||||
/datum/controller/subsystem/fields/proc/unregister_field(datum/proximity_monitor/advanced/F)
|
||||
running -= F
|
||||
@@ -61,7 +61,7 @@ SUBSYSTEM_DEF(garbage)
|
||||
for(var/path in sleptDestroy)
|
||||
dellog += "Path : [path] \n"
|
||||
dellog += "Sleeps : [sleptDestroy[path]] \n"
|
||||
text2file("[GLOB.log_directory]/qdel.log", dellog.Join())
|
||||
text2file(dellog.Join(), "[GLOB.log_directory]/qdel.log")
|
||||
|
||||
/datum/controller/subsystem/garbage/fire()
|
||||
HandleToBeQueued()
|
||||
@@ -164,8 +164,8 @@ SUBSYSTEM_DEF(garbage)
|
||||
if (time > highest_del_time)
|
||||
highest_del_time = time
|
||||
if (time > 10)
|
||||
log_game("Error: [type]([refID]) took longer then 1 second to delete (took [time/10] seconds to delete)")
|
||||
message_admins("Error: [type]([refID]) took longer then 1 second to delete (took [time/10] seconds to delete).")
|
||||
log_game("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete)")
|
||||
message_admins("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete).")
|
||||
postpone(time/5)
|
||||
|
||||
/datum/controller/subsystem/garbage/proc/HardQueue(datum/A)
|
||||
|
||||
@@ -11,6 +11,7 @@ SUBSYSTEM_DEF(job)
|
||||
var/initial_players_to_assign = 0 //used for checking against population caps
|
||||
|
||||
var/list/prioritized_jobs = list()
|
||||
var/list/latejoin_trackers = list() //Don't read this list, use GetLateJoinTurfs() instead
|
||||
|
||||
/datum/controller/subsystem/job/Initialize(timeofday)
|
||||
if(!occupations.len)
|
||||
@@ -223,7 +224,7 @@ SUBSYSTEM_DEF(job)
|
||||
|
||||
//Get the players who are ready
|
||||
for(var/mob/dead/new_player/player in GLOB.player_list)
|
||||
if(player.ready && player.mind && !player.mind.assigned_role)
|
||||
if(player.ready == PLAYER_READY_TO_PLAY && player.mind && !player.mind.assigned_role)
|
||||
unassigned += player
|
||||
|
||||
initial_players_to_assign = unassigned.len
|
||||
@@ -378,23 +379,11 @@ SUBSYSTEM_DEF(job)
|
||||
continue
|
||||
S = sloc
|
||||
break
|
||||
if(S)
|
||||
SendToAtom(H, S, buckle = FALSE)
|
||||
if(!S) //if there isn't a spawnpoint send them to latejoin, if there's no latejoin go yell at your mapper
|
||||
log_world("Couldn't find a round start spawn point for [rank]")
|
||||
S = get_turf(pick(GLOB.latejoin))
|
||||
if(!S) //final attempt, lets find some area in the arrivals shuttle to spawn them in to.
|
||||
log_world("Couldn't find a round start latejoin spawn point.")
|
||||
for(var/turf/T in get_area_turfs(/area/shuttle/arrival))
|
||||
if(!T.density)
|
||||
var/clear = 1
|
||||
for(var/obj/O in T)
|
||||
if(O.density)
|
||||
clear = 0
|
||||
break
|
||||
if(clear)
|
||||
S = T
|
||||
continue
|
||||
if(istype(S, /obj/effect/landmark) && isturf(S.loc))
|
||||
H.loc = S.loc
|
||||
SendToLateJoin(H)
|
||||
|
||||
if(H.mind)
|
||||
H.mind.assigned_role = rank
|
||||
@@ -466,7 +455,7 @@ SUBSYSTEM_DEF(job)
|
||||
var/level5 = 0 //banned
|
||||
var/level6 = 0 //account too young
|
||||
for(var/mob/dead/new_player/player in GLOB.player_list)
|
||||
if(!(player.ready && player.mind && !player.mind.assigned_role))
|
||||
if(!(player.ready == PLAYER_READY_TO_PLAY && player.mind && !player.mind.assigned_role))
|
||||
continue //This player is not ready
|
||||
if(jobban_isbanned(player, job.title))
|
||||
level5++
|
||||
@@ -499,7 +488,7 @@ SUBSYSTEM_DEF(job)
|
||||
Debug("Popcap overflow Check observer located, Player: [player]")
|
||||
to_chat(player, "<b>You have failed to qualify for any job you desired.</b>")
|
||||
unassigned -= player
|
||||
player.ready = 0
|
||||
player.ready = PLAYER_NOT_READY
|
||||
|
||||
|
||||
/datum/controller/subsystem/job/Recover()
|
||||
@@ -516,3 +505,45 @@ SUBSYSTEM_DEF(job)
|
||||
newjob.total_positions = J.total_positions
|
||||
newjob.spawn_positions = J.spawn_positions
|
||||
newjob.current_positions = J.current_positions
|
||||
|
||||
/datum/controller/subsystem/job/proc/SendToAtom(mob/M, atom/A, buckle)
|
||||
if(buckle && isliving(M) && istype(A, /obj/structure/chair))
|
||||
var/obj/structure/chair/C = A
|
||||
if(C.buckle_mob(M, FALSE, FALSE))
|
||||
return
|
||||
M.forceMove(get_turf(A))
|
||||
|
||||
/datum/controller/subsystem/job/proc/SendToLateJoin(mob/M, buckle = TRUE)
|
||||
if(latejoin_trackers.len)
|
||||
SendToAtom(M, pick(latejoin_trackers), buckle)
|
||||
else
|
||||
//bad mojo
|
||||
var/area/shuttle/arrival/A = locate() in GLOB.sortedAreas
|
||||
if(A)
|
||||
//first check if we can find a chair
|
||||
var/obj/structure/chair/C = locate() in A
|
||||
if(C)
|
||||
SendToAtom(M, C, buckle)
|
||||
return
|
||||
else //last hurrah
|
||||
var/list/avail = list()
|
||||
for(var/turf/T in A)
|
||||
if(!is_blocked_turf(T, TRUE))
|
||||
avail += T
|
||||
if(avail.len)
|
||||
SendToAtom(M, pick(avail), FALSE)
|
||||
return
|
||||
|
||||
//pick an open spot on arrivals and dump em
|
||||
var/list/arrivals_turfs = shuffle(get_area_turfs(/area/shuttle/arrival))
|
||||
if(arrivals_turfs.len)
|
||||
for(var/turf/T in arrivals_turfs)
|
||||
if(!is_blocked_turf(T, TRUE))
|
||||
SendToAtom(M, T, FALSE)
|
||||
return
|
||||
//last chance, pick ANY spot on arrivals and dump em
|
||||
SendToAtom(M, arrivals_turfs[1], FALSE)
|
||||
else
|
||||
var/msg = "Unable to send mob [M] to late join!"
|
||||
message_admins(msg)
|
||||
CRASH(msg)
|
||||
|
||||
@@ -15,14 +15,15 @@ SUBSYSTEM_DEF(lighting)
|
||||
|
||||
|
||||
/datum/controller/subsystem/lighting/Initialize(timeofday)
|
||||
if (config.starlight)
|
||||
for(var/I in GLOB.sortedAreas)
|
||||
var/area/A = I
|
||||
if (A.dynamic_lighting == DYNAMIC_LIGHTING_IFSTARLIGHT)
|
||||
A.luminosity = 0
|
||||
if(!initialized)
|
||||
if (config.starlight)
|
||||
for(var/I in GLOB.sortedAreas)
|
||||
var/area/A = I
|
||||
if (A.dynamic_lighting == DYNAMIC_LIGHTING_IFSTARLIGHT)
|
||||
A.luminosity = 0
|
||||
|
||||
create_all_lighting_objects()
|
||||
initialized = TRUE
|
||||
create_all_lighting_objects()
|
||||
initialized = TRUE
|
||||
|
||||
fire(FALSE, TRUE)
|
||||
|
||||
|
||||
@@ -37,11 +37,11 @@ SUBSYSTEM_DEF(machines)
|
||||
|
||||
var/seconds = wait * 0.1
|
||||
while(currentrun.len)
|
||||
var/datum/thing = currentrun[currentrun.len]
|
||||
var/obj/machinery/thing = currentrun[currentrun.len]
|
||||
currentrun.len--
|
||||
if(thing && thing.process(seconds) != PROCESS_KILL)
|
||||
if(thing:use_power)
|
||||
thing:auto_use_power() //add back the power state
|
||||
if(thing.use_power)
|
||||
thing.auto_use_power() //add back the power state
|
||||
else
|
||||
processing -= thing
|
||||
if (thing)
|
||||
|
||||
@@ -43,14 +43,14 @@ SUBSYSTEM_DEF(mapping)
|
||||
loading_ruins = TRUE
|
||||
var/mining_type = config.minetype
|
||||
if (mining_type == "lavaland")
|
||||
seedRuins(list(5), global.config.lavaland_budget, /area/lavaland/surface/outdoors, lava_ruins_templates)
|
||||
seedRuins(list(ZLEVEL_LAVALAND), global.config.lavaland_budget, /area/lavaland/surface/outdoors/unexplored, lava_ruins_templates)
|
||||
spawn_rivers()
|
||||
|
||||
// deep space ruins
|
||||
var/space_zlevels = list()
|
||||
for(var/i in ZLEVEL_SPACEMIN to ZLEVEL_SPACEMAX)
|
||||
switch(i)
|
||||
if(ZLEVEL_MINING, ZLEVEL_LAVALAND, ZLEVEL_EMPTY_SPACE)
|
||||
if(ZLEVEL_MINING, ZLEVEL_LAVALAND, ZLEVEL_EMPTY_SPACE, ZLEVEL_TRANSIT)
|
||||
continue
|
||||
else
|
||||
space_zlevels += i
|
||||
@@ -113,13 +113,15 @@ SUBSYSTEM_DEF(mapping)
|
||||
/datum/controller/subsystem/mapping/proc/loadWorld()
|
||||
//if any of these fail, something has gone horribly, HORRIBLY, wrong
|
||||
var/list/FailedZs = list()
|
||||
|
||||
|
||||
var/start_time = REALTIMEOFDAY
|
||||
|
||||
|
||||
INIT_ANNOUNCE("Loading [config.map_name]...")
|
||||
TryLoadZ(config.GetFullMapPath(), FailedZs, ZLEVEL_STATION)
|
||||
INIT_ANNOUNCE("Loaded station in [(REALTIMEOFDAY - start_time)/10]s!")
|
||||
SSblackbox.add_details("map_name", config.map_name)
|
||||
if(SSdbcore.Connect())
|
||||
var/datum/DBQuery/query_round_map_name = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET map_name = '[config.map_name]' WHERE id = [GLOB.round_id]")
|
||||
query_round_map_name.Execute()
|
||||
|
||||
if(config.minetype != "lavaland")
|
||||
INIT_ANNOUNCE("WARNING: A map without lavaland set as it's minetype was loaded! This is being ignored! Update the maploader code!")
|
||||
@@ -130,7 +132,7 @@ SUBSYSTEM_DEF(mapping)
|
||||
var/msg = "RED ALERT! The following map files failed to load: [FailedZs[1]]"
|
||||
if(FailedZs.len > 1)
|
||||
for(var/I in 2 to FailedZs.len)
|
||||
msg += ", [I]"
|
||||
msg += ", [FailedZs[I]]"
|
||||
msg += ". Yell at your server host!"
|
||||
INIT_ANNOUNCE(msg)
|
||||
#undef INIT_ANNOUNCE
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
PROCESSING_SUBSYSTEM_DEF(fields)
|
||||
name = "Fields"
|
||||
wait = 2
|
||||
priority = 40
|
||||
flags = SS_KEEP_TIMING | SS_NO_INIT
|
||||
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
||||
@@ -27,11 +27,12 @@ PROCESSING_SUBSYSTEM_DEF(overlays)
|
||||
processing = SSoverlays.processing
|
||||
|
||||
/datum/controller/subsystem/processing/overlays/fire(resumed = FALSE, mc_check = TRUE)
|
||||
var/list/processing = src.processing
|
||||
while(processing.len)
|
||||
var/atom/thing = processing[processing.len]
|
||||
processing.len--
|
||||
if(thing)
|
||||
thing.compile_overlays(FALSE)
|
||||
thing.compile_overlays()
|
||||
if(mc_check)
|
||||
if(MC_TICK_CHECK)
|
||||
break
|
||||
@@ -161,7 +162,7 @@ PROCESSING_SUBSYSTEM_DEF(overlays)
|
||||
if(NOT_QUEUED_ALREADY && need_compile) //have we caught more pokemon?
|
||||
QUEUE_FOR_COMPILE
|
||||
|
||||
/atom/proc/copy_overlays(atom/other, cut_old = FALSE) //copys our_overlays from another atom
|
||||
/atom/proc/copy_overlays(atom/other, cut_old) //copys our_overlays from another atom
|
||||
if(!other)
|
||||
if(cut_old)
|
||||
cut_overlays()
|
||||
@@ -190,3 +191,18 @@ PROCESSING_SUBSYSTEM_DEF(overlays)
|
||||
|
||||
/image/proc/cut_overlays(x)
|
||||
overlays.Cut()
|
||||
|
||||
/image/proc/copy_overlays(atom/other, cut_old)
|
||||
if(!other)
|
||||
if(cut_old)
|
||||
cut_overlays()
|
||||
return
|
||||
|
||||
var/list/cached_other = other.our_overlays
|
||||
if(cached_other)
|
||||
if(cut_old || !overlays.len)
|
||||
overlays = cached_other.Copy()
|
||||
else
|
||||
overlays |= cached_other
|
||||
else if(cut_old)
|
||||
cut_overlays()
|
||||
|
||||
@@ -22,9 +22,7 @@ SUBSYSTEM_DEF(processing)
|
||||
while(current_run.len)
|
||||
var/datum/thing = current_run[current_run.len]
|
||||
current_run.len--
|
||||
if(thing)
|
||||
thing.process(wait)
|
||||
else
|
||||
if(QDELETED(thing) || thing.process(wait) == PROCESS_KILL)
|
||||
processing -= thing
|
||||
if (MC_TICK_CHECK)
|
||||
return
|
||||
@@ -33,4 +31,4 @@ SUBSYSTEM_DEF(processing)
|
||||
/datum/proc/process()
|
||||
set waitfor = 0
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return 0
|
||||
return 0
|
||||
|
||||
@@ -5,6 +5,7 @@ SUBSYSTEM_DEF(server_maint)
|
||||
wait = 6
|
||||
flags = SS_POST_FIRE_TIMING
|
||||
priority = 10
|
||||
init_order = INIT_ORDER_SERVER_MAINT
|
||||
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
|
||||
var/list/currentrun
|
||||
|
||||
@@ -16,7 +17,7 @@ SUBSYSTEM_DEF(server_maint)
|
||||
/datum/controller/subsystem/server_maint/fire(resumed = FALSE)
|
||||
if(!resumed)
|
||||
src.currentrun = GLOB.clients.Copy()
|
||||
|
||||
|
||||
var/list/currentrun = src.currentrun
|
||||
var/round_started = SSticker.HasRoundStarted()
|
||||
|
||||
@@ -37,4 +38,11 @@ SUBSYSTEM_DEF(server_maint)
|
||||
if (MC_TICK_CHECK) //one day, when ss13 has 1000 people per server, you guys are gonna be glad I added this tick check
|
||||
return
|
||||
|
||||
#undef PING_BUFFER_TIME
|
||||
/datum/controller/subsystem/server_maint/Shutdown()
|
||||
kick_clients_in_lobby("<span class='boldannounce'>The round came to an end with you in the lobby.</span>", TRUE) //second parameter ensures only afk clients are kicked
|
||||
for(var/thing in GLOB.clients)
|
||||
var/client/C = thing
|
||||
if(C && config.server) //if you set a server location in config.txt, it sends you there instead of trying to reconnect to the same world address. -- NeoFite
|
||||
C << link("byond://[config.server]")
|
||||
|
||||
#undef PING_BUFFER_TIME
|
||||
@@ -1,4 +1,5 @@
|
||||
#define HIGHLIGHT_DYNAMIC_TRANSIT 1
|
||||
#define MAX_TRANSIT_REQUEST_RETRIES 10
|
||||
|
||||
SUBSYSTEM_DEF(shuttle)
|
||||
name = "Shuttle"
|
||||
@@ -13,6 +14,7 @@ SUBSYSTEM_DEF(shuttle)
|
||||
|
||||
var/list/turf/transit_turfs = list()
|
||||
var/list/transit_requesters = list()
|
||||
var/list/transit_request_failures = list()
|
||||
var/clear_transit = FALSE
|
||||
|
||||
//emergency shuttle stuff
|
||||
@@ -25,6 +27,7 @@ SUBSYSTEM_DEF(shuttle)
|
||||
var/area/emergencyLastCallLoc
|
||||
var/emergencyCallAmount = 0 //how many times the escape shuttle was called
|
||||
var/emergencyNoEscape
|
||||
var/emergencyNoRecall = FALSE
|
||||
var/list/hostileEnvironments = list()
|
||||
|
||||
//supply shuttle stuff
|
||||
@@ -46,7 +49,7 @@ SUBSYSTEM_DEF(shuttle)
|
||||
|
||||
var/lockdown = FALSE //disallow transit after nuke goes off
|
||||
|
||||
var/auto_call = 72000 //time before in deciseconds in which the shuttle is auto called. Default is 2 hours.
|
||||
var/auto_call = 99000 //time before in deciseconds in which the shuttle is auto called. Default is 2½ hours plus 15 for the shuttle. So total is 3.
|
||||
|
||||
/datum/controller/subsystem/shuttle/Initialize(timeofday)
|
||||
if(!arrivals)
|
||||
@@ -74,12 +77,9 @@ SUBSYSTEM_DEF(shuttle)
|
||||
..()
|
||||
|
||||
/datum/controller/subsystem/shuttle/proc/setup_transit_zone()
|
||||
if(GLOB.transit_markers.len == 0)
|
||||
WARNING("No /obj/effect/landmark/transit placed on the map!")
|
||||
return
|
||||
// transit zone
|
||||
var/turf/A = get_turf(GLOB.transit_markers[1])
|
||||
var/turf/B = get_turf(GLOB.transit_markers[2])
|
||||
var/turf/A = get_turf(locate(SHUTTLE_TRANSIT_BORDER,SHUTTLE_TRANSIT_BORDER,ZLEVEL_TRANSIT))
|
||||
var/turf/B = get_turf(locate(world.maxx - SHUTTLE_TRANSIT_BORDER,world.maxy - SHUTTLE_TRANSIT_BORDER,ZLEVEL_TRANSIT))
|
||||
for(var/i in block(A, B))
|
||||
var/turf/T = i
|
||||
T.ChangeTurf(/turf/open/space)
|
||||
@@ -88,11 +88,8 @@ SUBSYSTEM_DEF(shuttle)
|
||||
|
||||
#ifdef HIGHLIGHT_DYNAMIC_TRANSIT
|
||||
/datum/controller/subsystem/shuttle/proc/color_space()
|
||||
if(GLOB.transit_markers.len == 0)
|
||||
WARNING("No /obj/effect/landmark/transit placed on the map!")
|
||||
return
|
||||
var/turf/A = get_turf(GLOB.transit_markers[1])
|
||||
var/turf/B = get_turf(GLOB.transit_markers[2])
|
||||
var/turf/A = get_turf(locate(SHUTTLE_TRANSIT_BORDER,SHUTTLE_TRANSIT_BORDER,ZLEVEL_TRANSIT))
|
||||
var/turf/B = get_turf(locate(world.maxx - SHUTTLE_TRANSIT_BORDER,world.maxy - SHUTTLE_TRANSIT_BORDER,ZLEVEL_TRANSIT))
|
||||
for(var/i in block(A, B))
|
||||
var/turf/T = i
|
||||
// Only dying the "pure" space, not the transit tiles
|
||||
@@ -146,7 +143,12 @@ SUBSYSTEM_DEF(shuttle)
|
||||
var/requester = popleft(transit_requesters)
|
||||
var/success = generate_transit_dock(requester)
|
||||
if(!success) // BACK OF THE QUEUE
|
||||
transit_requesters += requester
|
||||
transit_request_failures[requester]++
|
||||
if(transit_request_failures[requester] < MAX_TRANSIT_REQUEST_RETRIES)
|
||||
transit_requesters += requester
|
||||
else
|
||||
var/obj/docking_port/mobile/M = requester
|
||||
M.transit_failure()
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
@@ -217,7 +219,27 @@ SUBSYSTEM_DEF(shuttle)
|
||||
emergency.request(null, signal_origin, html_decode(emergency_reason), 0)
|
||||
|
||||
log_game("[key_name(user)] has called the shuttle.")
|
||||
message_admins("[key_name_admin(user)] has called the shuttle.")
|
||||
message_admins("[key_name_admin(user)] has called the shuttle. (<A HREF='?_src_=holder;trigger_centcom_recall=1'>TRIGGER CENTCOM RECALL</A>)")
|
||||
|
||||
/datum/controller/subsystem/shuttle/proc/centcom_recall(old_timer, admiral_message)
|
||||
if(emergency.mode != SHUTTLE_CALL || emergency.timer != old_timer)
|
||||
return
|
||||
emergency.cancel()
|
||||
|
||||
if(!admiral_message)
|
||||
admiral_message = pick(GLOB.admiral_messages)
|
||||
var/intercepttext = "<font size = 3><b>NanoTrasen Update</b>: Request For Shuttle.</font><hr>\
|
||||
To whom it may concern:<br><br>\
|
||||
We have taken note of the situation upon [station_name()] and have come to the \
|
||||
conclusion that it does not warrant the abandonment of the station.<br>\
|
||||
If you do not agree with our opinion we suggest that you open a direct \
|
||||
line with us and explain the nature of your crisis.<br><br>\
|
||||
<i>This message has been automatically generated based upon readings from long \
|
||||
range diagnostic tools. To assure the quality of your request every finalized report \
|
||||
is reviewed by an on-call rear admiral.<br>\
|
||||
<b>Rear Admiral's Notes:</b> \
|
||||
[admiral_message]"
|
||||
print_command_report(intercepttext, announce = TRUE)
|
||||
|
||||
// Called when an emergency shuttle mobile docking port is
|
||||
// destroyed, which will only happen with admin intervention
|
||||
@@ -523,4 +545,8 @@ SUBSYSTEM_DEF(shuttle)
|
||||
for(var/obj/docking_port/mobile/M in mobile)
|
||||
if(M.is_in_shuttle_bounds(A))
|
||||
return TRUE
|
||||
|
||||
|
||||
/datum/controller/subsystem/shuttle/proc/get_containing_shuttle(atom/A)
|
||||
for(var/obj/docking_port/mobile/M in mobile)
|
||||
if(M.is_in_shuttle_bounds(A))
|
||||
return M
|
||||
|
||||
Regular → Executable
+109
-42
@@ -20,6 +20,7 @@ SUBSYSTEM_DEF(ticker)
|
||||
|
||||
var/login_music //music played in pregame lobby
|
||||
var/round_end_sound //music/jingle played when the world reboots
|
||||
var/round_end_sound_sent = TRUE //If all clients have loaded it
|
||||
|
||||
var/list/datum/mind/minds = list() //The characters in the game. Used for objective tracking.
|
||||
|
||||
@@ -59,10 +60,13 @@ SUBSYSTEM_DEF(ticker)
|
||||
|
||||
var/round_start_time = 0
|
||||
var/list/round_start_events
|
||||
var/mode_result = "undefined"
|
||||
var/end_state = "undefined"
|
||||
|
||||
var/modevoted = FALSE //Have we sent a vote for the gamemode?
|
||||
|
||||
/datum/controller/subsystem/ticker/Initialize(timeofday)
|
||||
load_mode()
|
||||
var/list/music = world.file2list(ROUND_START_MUSIC_LIST, "\n")
|
||||
login_music = pick(music)
|
||||
|
||||
@@ -82,6 +86,8 @@ SUBSYSTEM_DEF(ticker)
|
||||
window_flash(C, ignorepref = TRUE) //let them know lobby has opened up.
|
||||
to_chat(world, "<span class='boldnotice'>Welcome to [station_name()]!</span>")
|
||||
current_state = GAME_STATE_PREGAME
|
||||
//Everyone who wants to be an observer is now spawned
|
||||
create_observers()
|
||||
if(!modevoted)
|
||||
send_gamemode_vote()
|
||||
fire()
|
||||
@@ -93,7 +99,7 @@ SUBSYSTEM_DEF(ticker)
|
||||
totalPlayersReady = 0
|
||||
for(var/mob/dead/new_player/player in GLOB.player_list)
|
||||
++totalPlayers
|
||||
if(player.ready)
|
||||
if(player.ready == PLAYER_READY_TO_PLAY)
|
||||
++totalPlayersReady
|
||||
|
||||
if(start_immediately)
|
||||
@@ -127,7 +133,7 @@ SUBSYSTEM_DEF(ticker)
|
||||
scripture_states = scripture_unlock_alert(scripture_states)
|
||||
SSshuttle.autoEnd()
|
||||
|
||||
if(!mode.explosion_in_progress && mode.check_finished() || force_ending)
|
||||
if(!mode.explosion_in_progress && mode.check_finished(force_ending) || force_ending)
|
||||
current_state = GAME_STATE_FINISHED
|
||||
toggle_ooc(1) // Turn it on
|
||||
declare_completion(force_ending)
|
||||
@@ -235,6 +241,8 @@ SUBSYSTEM_DEF(ticker)
|
||||
/datum/controller/subsystem/ticker/proc/PostSetup()
|
||||
set waitfor = 0
|
||||
mode.post_setup()
|
||||
GLOB.start_state = new /datum/station_state()
|
||||
GLOB.start_state.count(1)
|
||||
//Cleanup some stuff
|
||||
for(var/obj/effect/landmark/start/S in GLOB.landmarks_list)
|
||||
//Deleting Startpoints but we need the ai point to AI-ize people later
|
||||
@@ -243,7 +251,7 @@ SUBSYSTEM_DEF(ticker)
|
||||
|
||||
var/list/adm = get_admin_counts()
|
||||
var/list/allmins = adm["present"]
|
||||
send2irc("Server", "Round of [hide_mode ? "secret":"[mode.name]"] has started[allmins.len ? ".":" with no active admins online!"]")
|
||||
send2irc("Server", "Round [GLOB.round_id ? "#[GLOB.round_id]:" : "of"] [hide_mode ? "secret":"[mode.name]"] has started[allmins.len ? ".":" with no active admins online!"]")
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/OnRoundstart(datum/callback/cb)
|
||||
if(!HasRoundStarted())
|
||||
@@ -402,7 +410,7 @@ SUBSYSTEM_DEF(ticker)
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/create_characters()
|
||||
for(var/mob/dead/new_player/player in GLOB.player_list)
|
||||
if(player.ready && player.mind)
|
||||
if(player.ready == PLAYER_READY_TO_PLAY && player.mind)
|
||||
GLOB.joined_player_list += player.ckey
|
||||
player.create_character(FALSE)
|
||||
else
|
||||
@@ -434,7 +442,7 @@ SUBSYSTEM_DEF(ticker)
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/transfer_characters()
|
||||
var/list/livings = list()
|
||||
for(var/mob/dead/new_player/player in GLOB.player_list)
|
||||
for(var/mob/dead/new_player/player in GLOB.mob_list)
|
||||
var/mob/living = player.transfer_character()
|
||||
if(living)
|
||||
qdel(player)
|
||||
@@ -575,45 +583,16 @@ SUBSYSTEM_DEF(ticker)
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
//Borers
|
||||
var/borerwin = FALSE
|
||||
if(GLOB.borers.len)
|
||||
var/borertext = "<br><font size=3><b>The borers were:</b></font>"
|
||||
for(var/mob/living/simple_animal/borer/B in GLOB.borers)
|
||||
if((B.key || B.controlling) && B.stat != DEAD)
|
||||
borertext += "<br>[B.controlling ? B.victim.key : B.key] was [B.truename] ("
|
||||
var/turf/location = get_turf(B)
|
||||
if(location.z == ZLEVEL_CENTCOM && B.victim)
|
||||
borertext += "escaped with host"
|
||||
else
|
||||
borertext += "failed"
|
||||
borertext += ")"
|
||||
to_chat(world, borertext)
|
||||
mode.declare_station_goal_completion()
|
||||
|
||||
var/total_borers = 0
|
||||
for(var/mob/living/simple_animal/borer/B in GLOB.borers)
|
||||
if((B.key || B.victim) && B.stat != DEAD)
|
||||
total_borers++
|
||||
if(total_borers)
|
||||
var/total_borer_hosts = 0
|
||||
for(var/mob/living/carbon/C in GLOB.mob_list)
|
||||
var/mob/living/simple_animal/borer/D = C.has_brain_worms()
|
||||
var/turf/location = get_turf(C)
|
||||
if(location.z == ZLEVEL_CENTCOM && D && D.stat != DEAD)
|
||||
total_borer_hosts++
|
||||
if(GLOB.total_borer_hosts_needed <= total_borer_hosts)
|
||||
borerwin = TRUE
|
||||
to_chat(world, "<b>There were [total_borers] borers alive at round end!</b>")
|
||||
to_chat(world, "<b>A total of [total_borer_hosts] borers with hosts escaped on the shuttle alive. The borers needed [GLOB.total_borer_hosts_needed] hosts to escape.</b>")
|
||||
if(borerwin)
|
||||
to_chat(world, "<b><font color='green'>The borers were successful!</font></b>")
|
||||
else
|
||||
to_chat(world, "<b><font color='red'>The borers have failed!</font></b>")
|
||||
//medals, placed far down so that people can actually see the commendations.
|
||||
if(GLOB.commendations.len)
|
||||
to_chat(world, "<b><font size=3>Medal Commendations:</font></b>")
|
||||
for (var/com in GLOB.commendations)
|
||||
to_chat(world, com)
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
mode.declare_station_goal_completion()
|
||||
|
||||
CHECK_TICK
|
||||
|
||||
//Adds the del() log to world.log in a format condensable by the runtime condenser found in tools
|
||||
@@ -635,11 +614,14 @@ SUBSYSTEM_DEF(ticker)
|
||||
//Collects persistence features
|
||||
SSpersistence.CollectData()
|
||||
|
||||
//stop collecting feedback during grifftime
|
||||
SSblackbox.Seal()
|
||||
|
||||
sleep(50)
|
||||
if(mode.station_was_nuked)
|
||||
world.Reboot("Station destroyed by Nuclear Device.", "end_proper", "nuke")
|
||||
Reboot("Station destroyed by Nuclear Device.", "nuke")
|
||||
else
|
||||
world.Reboot("Round ended.", "end_proper", "proper completion")
|
||||
Reboot("Round ended.", "proper completion")
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/send_tip_of_the_round()
|
||||
var/m
|
||||
@@ -742,6 +724,15 @@ SUBSYSTEM_DEF(ticker)
|
||||
cinematic = SSticker.cinematic
|
||||
maprotatechecked = SSticker.maprotatechecked
|
||||
|
||||
switch (current_state)
|
||||
if(GAME_STATE_SETTING_UP)
|
||||
Master.SetRunLevel(RUNLEVEL_SETUP)
|
||||
if(GAME_STATE_PLAYING)
|
||||
Master.SetRunLevel(RUNLEVEL_GAME)
|
||||
if(GAME_STATE_FINISHED)
|
||||
Master.SetRunLevel(RUNLEVEL_POSTGAME)
|
||||
|
||||
|
||||
modevoted = SSticker.modevoted
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/send_news_report()
|
||||
@@ -806,3 +797,79 @@ SUBSYSTEM_DEF(ticker)
|
||||
start_at = world.time + newtime
|
||||
else
|
||||
timeLeft = newtime
|
||||
|
||||
//Everyone who wanted to be an observer gets made one now
|
||||
/datum/controller/subsystem/ticker/proc/create_observers()
|
||||
for(var/mob/dead/new_player/player in GLOB.player_list)
|
||||
if(player.ready == PLAYER_READY_TO_OBSERVE && player.mind)
|
||||
//Break chain since this has a sleep input in it
|
||||
addtimer(CALLBACK(player, /mob/dead/new_player.proc/make_me_an_observer), 1)
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/load_mode()
|
||||
var/mode = trim(file2text("data/mode.txt"))
|
||||
if(mode)
|
||||
GLOB.master_mode = mode
|
||||
else
|
||||
GLOB.master_mode = "extended"
|
||||
log_game("Saved mode is '[GLOB.master_mode]'")
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/save_mode(the_mode)
|
||||
var/F = file("data/mode.txt")
|
||||
fdel(F)
|
||||
F << the_mode
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/SetRoundEndSound(the_sound)
|
||||
set waitfor = FALSE
|
||||
round_end_sound_sent = FALSE
|
||||
round_end_sound = fcopy_rsc(the_sound)
|
||||
for(var/thing in GLOB.clients)
|
||||
var/client/C = thing
|
||||
if (!C)
|
||||
continue
|
||||
C.Export("##action=load_rsc", round_end_sound)
|
||||
round_end_sound_sent = TRUE
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/Reboot(reason, end_string, delay)
|
||||
set waitfor = FALSE
|
||||
if(usr && !check_rights(R_SERVER, TRUE))
|
||||
return
|
||||
|
||||
if(!delay)
|
||||
delay = config.round_end_countdown * 10
|
||||
|
||||
var/skip_delay = check_rights()
|
||||
if(delay_end && !skip_delay)
|
||||
to_chat(world, "<span class='boldannounce'>An admin has delayed the round end.</span>")
|
||||
return
|
||||
|
||||
to_chat(world, "<span class='boldannounce'>Rebooting World in [delay/10] [(delay >= 10 && delay < 20) ? "second" : "seconds"]. [reason]</span>")
|
||||
|
||||
var/start_wait = world.time
|
||||
UNTIL(round_end_sound_sent || (world.time - start_wait) > (delay * 2)) //don't wait forever
|
||||
sleep(delay - (world.time - start_wait))
|
||||
|
||||
if(delay_end && !skip_delay)
|
||||
to_chat(world, "<span class='boldannounce'>Reboot was cancelled by an admin.</span>")
|
||||
return
|
||||
|
||||
if(end_string)
|
||||
end_state = end_string
|
||||
|
||||
|
||||
log_game("<span class='boldannounce'>Rebooting World. [reason]</span>")
|
||||
|
||||
world.Reboot()
|
||||
|
||||
/datum/controller/subsystem/ticker/Shutdown()
|
||||
if(!round_end_sound)
|
||||
round_end_sound = pick(\
|
||||
'sound/roundend/newroundsexy.ogg',
|
||||
'sound/roundend/apcdestroyed.ogg',
|
||||
'sound/roundend/bangindonk.ogg',
|
||||
'sound/roundend/leavingtg.ogg',
|
||||
'sound/roundend/its_only_game.ogg',
|
||||
'sound/roundend/yeehaw.ogg',
|
||||
'sound/roundend/disappointed.ogg'\
|
||||
)
|
||||
|
||||
world << sound(round_end_sound)
|
||||
|
||||
@@ -43,7 +43,7 @@ SUBSYSTEM_DEF(timer)
|
||||
WARNING(msg)
|
||||
if(bucket_auto_reset)
|
||||
bucket_resolution = 0
|
||||
|
||||
|
||||
log_world("Active timers at tick [world.time]:")
|
||||
for(var/I in processing)
|
||||
var/datum/timedevent/TE = I
|
||||
@@ -365,10 +365,13 @@ SUBSYSTEM_DEF(timer)
|
||||
var/datum/timedevent/timer = new(callback, timeToRun, flags, hash)
|
||||
if (flags & TIMER_STOPPABLE)
|
||||
return timer.id
|
||||
return TIMER_ID_NULL
|
||||
|
||||
/proc/deltimer(id)
|
||||
if (!id)
|
||||
return FALSE
|
||||
if (id == TIMER_ID_NULL)
|
||||
CRASH("Tried to delete a null timerid. Use TIMER_STOPPABLE flag")
|
||||
if (!istext(id))
|
||||
if (istype(id, /datum/timedevent))
|
||||
qdel(id)
|
||||
|
||||
@@ -55,6 +55,12 @@ SUBSYSTEM_DEF(title)
|
||||
var/F = file("data/previous_title.dat")
|
||||
F << file_path
|
||||
|
||||
for(var/thing in GLOB.clients)
|
||||
if(!thing)
|
||||
continue
|
||||
var/obj/screen/splash/S = new(thing, FALSE)
|
||||
S.Fade(FALSE,FALSE)
|
||||
|
||||
/datum/controller/subsystem/title/Recover()
|
||||
icon = SStitle.icon
|
||||
splash_turf = SStitle.splash_turf
|
||||
|
||||
@@ -2,9 +2,9 @@ SUBSYSTEM_DEF(vote)
|
||||
name = "Vote"
|
||||
wait = 10
|
||||
|
||||
flags = SS_KEEP_TIMING|SS_NO_INIT
|
||||
|
||||
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
|
||||
flags = SS_KEEP_TIMING|SS_NO_INIT
|
||||
|
||||
runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT
|
||||
|
||||
var/initiator = null
|
||||
var/started_time = null
|
||||
@@ -120,13 +120,13 @@ SUBSYSTEM_DEF(vote)
|
||||
if(SSticker && SSticker.mode)//Don't change the mode if the round already started.
|
||||
return message_admins("A vote has tried to change the gamemode, but the game has already started. Aborting.")
|
||||
GLOB.master_mode = .
|
||||
world.save_mode(.)
|
||||
SSticker.save_mode(.)
|
||||
to_chat(world, "<span class='adminnotice'><b>The mode is now: [GLOB.master_mode]</b></span>")
|
||||
log_admin("Gamemode has been voted for and switched to: [GLOB.master_mode].")
|
||||
if("gamemode")
|
||||
if(GLOB.master_mode != .)
|
||||
world.save_mode(.)
|
||||
if(SSticker.HasRoundStarted())
|
||||
SSticker.save_mode(.)
|
||||
if(SSticker.HasRoundStarted())
|
||||
restart = 1
|
||||
else
|
||||
GLOB.master_mode = .
|
||||
@@ -137,7 +137,7 @@ SUBSYSTEM_DEF(vote)
|
||||
active_admins = 1
|
||||
break
|
||||
if(!active_admins)
|
||||
world.Reboot("Restart vote successful.", "end_error", "restart vote")
|
||||
SSticker.Reboot("Restart vote successful.", "restart vote")
|
||||
else
|
||||
to_chat(world, "<span style='boldannounce'>Notice:Restart vote will not restart the server automatically because there are active admins on.</span>")
|
||||
message_admins("A restart vote has passed, but there are active admins on with +server, so it has been canceled. If you wish, you may restart the server.")
|
||||
|
||||
@@ -20,22 +20,28 @@
|
||||
to_chat(AI, "<span class='userdanger'>Anomaly Detected. Returned to core!</span>") //The AI needs to be in its core to properly be converted
|
||||
. = is_eligible_servant(new_owner.current)
|
||||
if(!silent && new_owner.current)
|
||||
if(issilicon(new_owner.current))
|
||||
to_chat(new_owner.current, "<span class='heavy_brass'>You are unable to compute this truth. Your vision glows a brilliant yellow, and all at once it comes to you. Ratvar, the \
|
||||
Clockwork Justiciar, lies in exile, derelict and forgotten in an unseen realm.</span>")
|
||||
if(.)
|
||||
to_chat(new_owner.current, "<span class='heavy_brass'>The world before you suddenly glows a brilliant yellow. [issilicon(new_owner.current) ? "You cannot compute this truth!" : \
|
||||
"Your mind is racing!"] You hear the whooshing steam and cl[pick("ank", "ink", "unk", "ang")]ing cogs of a billion billion machines, and all at once it comes to you.<br>\
|
||||
Ratvar, the Clockwork Justiciar, [GLOB.ratvar_awakens ? "has been freed from his eternal prison" : "lies in exile, derelict and forgotten in an unseen realm"].</span>")
|
||||
flash_color(new_owner.current, flash_color = list("#BE8700", "#BE8700", "#BE8700", rgb(0,0,0)), flash_time = 50)
|
||||
else
|
||||
to_chat(new_owner.current, "<span class='heavy_brass'>[iscarbon(new_owner.current) ? "Your mind is racing! Your body feels incredibly light! ":""]Your world glows a brilliant \
|
||||
yellow! All at once it comes to you. Ratvar, the Clockwork Justiciar, lies in exile, derelict and forgotten in an unseen realm.</span>")
|
||||
if(!.)
|
||||
new_owner.current.visible_message("<span class='boldwarning'>[new_owner.current] seems to resist an unseen force!</span>")
|
||||
to_chat(new_owner.current, "<span class='userdanger'>And yet, you somehow push it all away.</span>")
|
||||
new_owner.current.visible_message("<span class='boldwarning'>[new_owner.current] seems to resist an unseen force!</span>", null, null, 7, new_owner.current)
|
||||
to_chat(new_owner.current, "<span class='heavy_brass'>The world before you suddenly glows a brilliant yellow. [issilicon(new_owner.current) ? "You cannot compute this truth!" : \
|
||||
"Your mind is racing!"] You hear the whooshing steam and cl[pick("ank", "ink", "unk", "ang")]ing cogs of a billion billion machines, and the sound</span> <span class='boldwarning'>\
|
||||
is a meaningless cacophony.</span><br>\
|
||||
<span class='userdanger'>You see an abomination of rusting parts[GLOB.ratvar_awakens ? ", and it is here.<br>It is too late" : \
|
||||
" in an endless grey void.<br>It cannot be allowed to escape"].</span>")
|
||||
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/clockcultalr.ogg', 40, TRUE, frequency = 100000, pressure_affected = FALSE)
|
||||
flash_color(new_owner.current, flash_color = list("#BE8700", "#BE8700", "#BE8700", rgb(0,0,0)), flash_time = 5)
|
||||
|
||||
/datum/antagonist/clockcult/greet()
|
||||
if(!owner.current || silent)
|
||||
return
|
||||
owner.current.visible_message("<span class='heavy_brass'>[owner.current]'s eyes glow a blazing yellow!</span>")
|
||||
owner.current.visible_message("<span class='heavy_brass'>[owner.current]'s eyes glow a blazing yellow!</span>", null, null, 7, owner.current) //don't show the owner this message
|
||||
to_chat(owner.current, "<span class='heavy_brass'>Assist your new companions in their righteous efforts. Your goal is theirs, and theirs yours. You serve the Clockwork \
|
||||
Justiciar above all else. Perform his every whim without hesitation.</span>")
|
||||
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/clockcultalr.ogg', 70, FALSE, pressure_affected = FALSE)
|
||||
|
||||
/datum/antagonist/clockcult/on_gain()
|
||||
var/mob/living/current = owner.current
|
||||
@@ -47,17 +53,20 @@
|
||||
owner.current.log_message("<font color=#BE8700>Has been converted to the cult of Ratvar!</font>", INDIVIDUAL_ATTACK_LOG)
|
||||
if(issilicon(current))
|
||||
if(iscyborg(current) && !silent)
|
||||
to_chat(current, "<span class='boldwarning'>You have been desynced from your master AI.</span>")
|
||||
to_chat(current, "<span class='boldwarning'>In addition, your onboard camera is no longer active and you have gained additional equipment, including a limited clockwork slab.</span>")
|
||||
var/mob/living/silicon/robot/R = current
|
||||
if(R.connected_ai && !is_servant_of_ratvar(R.connected_ai))
|
||||
to_chat(R, "<span class='boldwarning'>You have been desynced from your master AI.<br>\
|
||||
In addition, your onboard camera is no longer active and you have gained additional equipment, including a limited clockwork slab.</span>")
|
||||
else
|
||||
to_chat(R, "<span class='boldwarning'>Your onboard camera is no longer active and you have gained additional equipment, including a limited clockwork slab.</span>")
|
||||
if(isAI(current))
|
||||
to_chat(current, "<span class='boldwarning'>You are able to use your cameras to listen in on conversations.</span>")
|
||||
to_chat(current, "<span class='boldwarning'>You are now able to use your cameras to listen in on conversations, but can no longer speak in anything but Ratvarian.</span>")
|
||||
to_chat(current, "<span class='heavy_brass'>You can communicate with other servants by using the Hierophant Network action button in the upper left.</span>")
|
||||
else if(isbrain(current) || isclockmob(current))
|
||||
to_chat(current, "<span class='nezbere'>You can communicate with other servants by using the Hierophant Network action button in the upper left.</span>")
|
||||
..()
|
||||
if(istype(SSticker.mode, /datum/game_mode/clockwork_cult))
|
||||
var/datum/game_mode/clockwork_cult/C = SSticker.mode
|
||||
C.present_tasks(owner) //Memorize the objectives
|
||||
to_chat(current, "<b>This is Ratvar's will:</b> [CLOCKCULT_OBJECTIVE]")
|
||||
owner.memory += "<b>Ratvar's will:</b> [CLOCKCULT_OBJECTIVE]<br>" //Memorize the objectives
|
||||
|
||||
/datum/antagonist/clockcult/apply_innate_effects(mob/living/mob_override)
|
||||
. = ..()
|
||||
@@ -89,11 +98,7 @@
|
||||
A.eyeobj.relay_speech = TRUE
|
||||
for(var/mob/living/silicon/robot/R in A.connected_robots)
|
||||
if(R.connected_ai == A)
|
||||
R.visible_message("<span class='heavy_brass'>[R]'s eyes glow a blazing yellow!</span>", \
|
||||
"<span class='heavy_brass'>Assist your new companions in their righteous efforts. Your goal is theirs, and theirs yours. You serve the Clockwork Justiciar above all else. Perform his every \
|
||||
whim without hesitation.</span>")
|
||||
to_chat(R, "<span class='boldwarning'>Your onboard camera is no longer active and you have gained additional equipment, including a limited clockwork slab.</span>")
|
||||
add_servant_of_ratvar(R, TRUE)
|
||||
add_servant_of_ratvar(R)
|
||||
S.laws = new/datum/ai_laws/ratvar
|
||||
S.laws.associate(S)
|
||||
S.update_icons()
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#define SUMMON_POSSIBILITIES 3
|
||||
|
||||
/datum/antagonist/cult
|
||||
var/datum/action/innate/cult/comm/communion = new
|
||||
var/datum/action/innate/cult/mastervote/vote = new
|
||||
@@ -35,6 +37,11 @@
|
||||
message_admins("Cult Sacrifice: Could not find unconvertable or convertable target. WELP!")
|
||||
GLOB.sac_complete = TRUE
|
||||
SSticker.mode.cult_objectives += "sacrifice"
|
||||
if(!GLOB.summon_spots.len)
|
||||
while(GLOB.summon_spots.len < SUMMON_POSSIBILITIES)
|
||||
var/area/summon = pick(GLOB.sortedAreas - GLOB.summon_spots)
|
||||
if(summon && (summon.z == ZLEVEL_STATION) && summon.valid_territory)
|
||||
GLOB.summon_spots += summon
|
||||
SSticker.mode.cult_objectives += "eldergod"
|
||||
|
||||
/datum/antagonist/cult/proc/cult_memorization(datum/mind/cult_mind)
|
||||
@@ -47,8 +54,9 @@
|
||||
explanation = "Sacrifice [GLOB.sac_mind], the [GLOB.sac_mind.assigned_role] via invoking a Sacrifice rune with them on it and three acolytes around it."
|
||||
else
|
||||
explanation = "The veil has already been weakened here, proceed to the final objective."
|
||||
GLOB.sac_complete = TRUE
|
||||
if("eldergod")
|
||||
explanation = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie' with nine acolytes on it. You must do this after sacrificing your target."
|
||||
explanation = "Summon Nar-Sie by invoking the rune 'Summon Nar-Sie'. <b>The summoning can only be accomplished in [english_list(GLOB.summon_spots)] - where the veil is weak enough for the ritual to begin.</b>"
|
||||
if(!silent)
|
||||
to_chat(current, "<B>Objective #[obj_count]</B>: [explanation]")
|
||||
cult_mind.memory += "<B>Objective #[obj_count]</B>: [explanation]<BR>"
|
||||
@@ -112,10 +120,12 @@
|
||||
/datum/antagonist/cult/master
|
||||
var/datum/action/innate/cult/master/finalreck/reckoning = new
|
||||
var/datum/action/innate/cult/master/cultmark/bloodmark = new
|
||||
var/datum/action/innate/cult/master/pulse/throwing = new
|
||||
|
||||
/datum/antagonist/cult/master/Destroy()
|
||||
QDEL_NULL(reckoning)
|
||||
QDEL_NULL(bloodmark)
|
||||
QDEL_NULL(throwing)
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/cult/master/on_gain()
|
||||
@@ -136,6 +146,7 @@
|
||||
if(!GLOB.reckoning_complete)
|
||||
reckoning.Grant(current)
|
||||
bloodmark.Grant(current)
|
||||
throwing.Grant(current)
|
||||
current.update_action_buttons_icon()
|
||||
current.apply_status_effect(/datum/status_effect/cult_master)
|
||||
|
||||
@@ -146,5 +157,6 @@
|
||||
current = mob_override
|
||||
reckoning.Remove(current)
|
||||
bloodmark.Remove(current)
|
||||
throwing.Remove(current)
|
||||
current.update_action_buttons_icon()
|
||||
current.remove_status_effect(/datum/status_effect/cult_master)
|
||||
current.remove_status_effect(/datum/status_effect/cult_master)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/datum/antagonist/iaa
|
||||
|
||||
/datum/antagonist/iaa/apply_innate_effects()
|
||||
.=..() //in case the base is used in future
|
||||
if(owner&&owner.current)
|
||||
give_pinpointer(owner.current)
|
||||
|
||||
/datum/antagonist/iaa/remove_innate_effects()
|
||||
.=..()
|
||||
if(owner&&owner.current)
|
||||
owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer)
|
||||
@@ -0,0 +1,327 @@
|
||||
#define PINPOINTER_MINIMUM_RANGE 15
|
||||
#define PINPOINTER_EXTRA_RANDOM_RANGE 10
|
||||
#define PINPOINTER_PING_TIME 40
|
||||
#define PROB_ACTUAL_TRAITOR 20
|
||||
#define TRAITOR_AGENT_ROLE "Syndicate External Affairs Agent"
|
||||
|
||||
/datum/antagonist/traitor/internal_affairs
|
||||
base_datum_custom = ANTAG_DATUM_IAA_CUSTOM
|
||||
human_datum = ANTAG_DATUM_IAA_HUMAN
|
||||
ai_datum = ANTAG_DATUM_IAA_AI
|
||||
|
||||
|
||||
|
||||
/datum/antagonist/traitor/AI/internal_affairs
|
||||
name = "Internal Affairs Agent"
|
||||
employer = "Nanotrasen"
|
||||
special_role = "internal affairs agent"
|
||||
base_datum_custom = ANTAG_DATUM_IAA_CUSTOM
|
||||
var/syndicate = FALSE
|
||||
var/last_man_standing = FALSE
|
||||
var/list/datum/mind/targets_stolen
|
||||
|
||||
/datum/antagonist/traitor/AI/internal_affairs/custom
|
||||
silent = TRUE
|
||||
should_give_codewords = FALSE
|
||||
give_objectives = FALSE
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs
|
||||
name = "Internal Affairs Agent"
|
||||
employer = "Nanotrasen"
|
||||
special_role = "internal affairs agent"
|
||||
base_datum_custom = ANTAG_DATUM_IAA_CUSTOM
|
||||
var/syndicate = FALSE
|
||||
var/last_man_standing = FALSE
|
||||
var/list/datum/mind/targets_stolen
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs/custom
|
||||
silent = TRUE
|
||||
should_give_codewords = FALSE
|
||||
give_objectives = FALSE
|
||||
should_equip = FALSE //Duplicating TCs is dangerous
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs/transfer_important_variables(datum/antagonist/traitor/human/internal_affairs/other)
|
||||
..(other)
|
||||
other.syndicate = syndicate
|
||||
other.last_man_standing = last_man_standing
|
||||
other.targets_stolen = targets_stolen
|
||||
|
||||
/datum/antagonist/traitor/AI/internal_affairs/transfer_important_variables(datum/antagonist/traitor/human/internal_affairs/other)
|
||||
..(other)
|
||||
other.syndicate = syndicate
|
||||
other.last_man_standing = last_man_standing
|
||||
other.targets_stolen = targets_stolen
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs/proc/give_pinpointer()
|
||||
if(owner && owner.current)
|
||||
owner.current.apply_status_effect(/datum/status_effect/agent_pinpointer)
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs/apply_innate_effects()
|
||||
.=..() //in case the base is used in future
|
||||
if(owner&&owner.current)
|
||||
give_pinpointer(owner.current)
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs/remove_innate_effects()
|
||||
.=..()
|
||||
if(owner&&owner.current)
|
||||
owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer)
|
||||
|
||||
/datum/antagonist/traitor/internal_affairs/custom
|
||||
ai_datum = ANTAG_DATUM_IAA_AI_CUSTOM
|
||||
human_datum = ANTAG_DATUM_IAA_HUMAN_CUSTOM
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs/on_gain()
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
.=..()
|
||||
/datum/antagonist/traitor/human/internal_affairs/on_removal()
|
||||
STOP_PROCESSING(SSprocessing,src)
|
||||
.=..()
|
||||
/datum/antagonist/traitor/human/internal_affairs/process()
|
||||
iaa_process()
|
||||
|
||||
/datum/antagonist/traitor/AI/internal_affairs/on_gain()
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
.=..()
|
||||
/datum/antagonist/traitor/AI/internal_affairs/on_removal()
|
||||
STOP_PROCESSING(SSprocessing,src)
|
||||
.=..()
|
||||
/datum/antagonist/traitor/AI/internal_affairs/process()
|
||||
iaa_process()
|
||||
|
||||
/datum/status_effect/agent_pinpointer
|
||||
id = "agent_pinpointer"
|
||||
duration = -1
|
||||
tick_interval = PINPOINTER_PING_TIME
|
||||
alert_type = /obj/screen/alert/status_effect/agent_pinpointer
|
||||
var/minimum_range = PINPOINTER_MINIMUM_RANGE
|
||||
var/mob/scan_target = null
|
||||
|
||||
/obj/screen/alert/status_effect/agent_pinpointer
|
||||
name = "Internal Affairs Integrated Pinpointer"
|
||||
desc = "Even stealthier than a normal implant."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "pinon"
|
||||
|
||||
/datum/status_effect/agent_pinpointer/proc/point_to_target() //If we found what we're looking for, show the distance and direction
|
||||
if(!scan_target)
|
||||
linked_alert.icon_state = "pinonnull"
|
||||
return
|
||||
var/turf/here = get_turf(owner)
|
||||
var/turf/there = get_turf(scan_target)
|
||||
if(here.z != there.z)
|
||||
linked_alert.icon_state = "pinonnull"
|
||||
return
|
||||
if(get_dist_euclidian(here,there)<=minimum_range + rand(0, PINPOINTER_EXTRA_RANDOM_RANGE))
|
||||
linked_alert.icon_state = "pinondirect"
|
||||
else
|
||||
linked_alert.setDir(get_dir(here, there))
|
||||
switch(get_dist(here, there))
|
||||
if(1 to 8)
|
||||
linked_alert.icon_state = "pinonclose"
|
||||
if(9 to 16)
|
||||
linked_alert.icon_state = "pinonmedium"
|
||||
if(16 to INFINITY)
|
||||
linked_alert.icon_state = "pinonfar"
|
||||
|
||||
/datum/status_effect/agent_pinpointer/proc/scan_for_target()
|
||||
scan_target = null
|
||||
if(owner)
|
||||
if(owner.mind)
|
||||
if(owner.mind.objectives)
|
||||
for(var/datum/objective/objective_ in owner.mind.objectives)
|
||||
if(!is_internal_objective(objective_))
|
||||
continue
|
||||
var/datum/objective/assassinate/internal/objective = objective_
|
||||
var/mob/current = objective.target.current
|
||||
if(current&¤t.stat!=DEAD)
|
||||
scan_target = current
|
||||
break
|
||||
|
||||
/datum/status_effect/agent_pinpointer/tick()
|
||||
if(!owner)
|
||||
qdel(src)
|
||||
return
|
||||
scan_for_target()
|
||||
point_to_target()
|
||||
|
||||
|
||||
/proc/is_internal_objective(datum/objective/O)
|
||||
return (istype(O, /datum/objective/assassinate/internal)||istype(O, /datum/objective/destroy/internal))
|
||||
|
||||
/datum/antagonist/traitor/proc/replace_escape_objective()
|
||||
if(!owner||!owner.objectives)
|
||||
return
|
||||
for (var/objective_ in owner.objectives)
|
||||
if(!(istype(objective_, /datum/objective/escape)||istype(objective_,/datum/objective/survive)))
|
||||
continue
|
||||
remove_objective(objective_)
|
||||
|
||||
var/datum/objective/martyr/martyr_objective = new
|
||||
martyr_objective.owner = owner
|
||||
add_objective(martyr_objective)
|
||||
|
||||
/datum/antagonist/traitor/proc/reinstate_escape_objective()
|
||||
if(!owner||!owner.objectives)
|
||||
return
|
||||
for (var/objective_ in owner.objectives)
|
||||
if(!istype(objective_, /datum/objective/martyr))
|
||||
continue
|
||||
remove_objective(objective_)
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs/reinstate_escape_objective()
|
||||
..()
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = owner
|
||||
add_objective(escape_objective)
|
||||
|
||||
/datum/antagonist/traitor/AI/internal_affairs/reinstate_escape_objective()
|
||||
..()
|
||||
var/datum/objective/survive/survive_objective = new
|
||||
survive_objective.owner = owner
|
||||
add_objective(survive_objective)
|
||||
|
||||
/datum/antagonist/traitor/proc/steal_targets(datum/mind/victim)
|
||||
var/datum/antagonist/traitor/human/internal_affairs/this = src //Should only use this if IAA
|
||||
|
||||
if(!owner.current||owner.current.stat==DEAD)
|
||||
return
|
||||
to_chat(owner.current, "<span class='userdanger'> Target eliminated: [victim.name]</span>")
|
||||
for(var/objective_ in victim.objectives)
|
||||
if(istype(objective_, /datum/objective/assassinate/internal))
|
||||
var/datum/objective/assassinate/internal/objective = objective_
|
||||
if(objective.target==owner)
|
||||
continue
|
||||
else if(this.targets_stolen.Find(objective.target) == 0)
|
||||
var/datum/objective/assassinate/internal/new_objective = new
|
||||
new_objective.owner = owner
|
||||
new_objective.target = objective.target
|
||||
new_objective.update_explanation_text()
|
||||
add_objective(new_objective)
|
||||
this.targets_stolen += objective.target
|
||||
var/status_text = objective.check_completion() ? "neutralised" : "active"
|
||||
to_chat(owner.current, "<span class='userdanger'> New target added to database: [objective.target.name] ([status_text]) </span>")
|
||||
else if(istype(objective_, /datum/objective/destroy/internal))
|
||||
var/datum/objective/destroy/internal/objective = objective_
|
||||
var/datum/objective/destroy/internal/new_objective = new
|
||||
if(objective.target==owner)
|
||||
continue
|
||||
else if(this.targets_stolen.Find(objective.target) == 0)
|
||||
new_objective.owner = owner
|
||||
new_objective.target = objective.target
|
||||
new_objective.update_explanation_text()
|
||||
add_objective(new_objective)
|
||||
this.targets_stolen += objective.target
|
||||
var/status_text = objective.check_completion() ? "neutralised" : "active"
|
||||
to_chat(owner.current, "<span class='userdanger'> New target added to database: [objective.target.name] ([status_text]) </span>")
|
||||
this.last_man_standing = TRUE
|
||||
for(var/objective_ in owner.objectives)
|
||||
if(!is_internal_objective(objective_))
|
||||
continue
|
||||
var/datum/objective/assassinate/internal/objective = objective_
|
||||
if(!objective.check_completion())
|
||||
this.last_man_standing = FALSE
|
||||
return
|
||||
if(this.last_man_standing)
|
||||
if(this.syndicate)
|
||||
to_chat(owner.current,"<span class='userdanger'> All the loyalist agents are dead, and no more is required of you. Die a glorious death, agent. </span>")
|
||||
else
|
||||
to_chat(owner.current,"<span class='userdanger'> All the other agents are dead, and you're the last loose end. Stage a Syndicate terrorist attack to cover up for today's events. You no longer have any limits on collateral damage.</span>")
|
||||
replace_escape_objective(owner)
|
||||
|
||||
/datum/antagonist/traitor/proc/iaa_process()
|
||||
var/datum/antagonist/traitor/human/internal_affairs/this = src //Should only use this if IAA
|
||||
if(owner&&owner.current&&owner.current.stat!=DEAD)
|
||||
for(var/objective_ in owner.objectives)
|
||||
if(!is_internal_objective(objective_))
|
||||
continue
|
||||
var/datum/objective/assassinate/internal/objective = objective_
|
||||
if(!objective.target)
|
||||
continue
|
||||
if(objective.check_completion())
|
||||
if(objective.stolen)
|
||||
continue
|
||||
else
|
||||
steal_targets(objective.target)
|
||||
objective.stolen = TRUE
|
||||
else
|
||||
if(objective.stolen)
|
||||
var/fail_msg = "<span class='userdanger'>Your sensors tell you that [objective.target.current.real_name], one of the targets you were meant to have killed, pulled one over on you, and is still alive - do the job properly this time! </span>"
|
||||
if(this.last_man_standing)
|
||||
if(this.syndicate)
|
||||
fail_msg += "<span class='userdanger'> You no longer have permission to die. </span>"
|
||||
else
|
||||
fail_msg += "<span class='userdanger'> The truth could still slip out!</font><B><font size=5 color=red> Cease any terrorist actions as soon as possible, unneeded property damage or loss of employee life will lead to your contract being terminated.</span>"
|
||||
reinstate_escape_objective(owner)
|
||||
this.last_man_standing = FALSE
|
||||
to_chat(owner.current, fail_msg)
|
||||
objective.stolen = FALSE
|
||||
|
||||
/datum/antagonist/traitor/proc/forge_iaa_objectives()
|
||||
var/datum/antagonist/traitor/human/internal_affairs/this = src //Should only use this if IAA
|
||||
if(SSticker.mode.target_list.len && SSticker.mode.target_list[owner]) // Is a double agent
|
||||
|
||||
// Assassinate
|
||||
var/datum/mind/target_mind = SSticker.mode.target_list[owner]
|
||||
if(issilicon(target_mind.current))
|
||||
var/datum/objective/destroy/internal/destroy_objective = new
|
||||
destroy_objective.owner = owner
|
||||
destroy_objective.target = target_mind
|
||||
destroy_objective.update_explanation_text()
|
||||
else
|
||||
var/datum/objective/assassinate/internal/kill_objective = new
|
||||
kill_objective.owner = owner
|
||||
kill_objective.target = target_mind
|
||||
kill_objective.update_explanation_text()
|
||||
add_objective(kill_objective)
|
||||
|
||||
//Optional traitor objective
|
||||
if(prob(PROB_ACTUAL_TRAITOR))
|
||||
employer = "The Syndicate"
|
||||
owner.special_role = TRAITOR_AGENT_ROLE
|
||||
special_role = TRAITOR_AGENT_ROLE
|
||||
this.syndicate = TRUE
|
||||
forge_single_objective()
|
||||
|
||||
else
|
||||
..() // Give them standard objectives.
|
||||
return
|
||||
|
||||
/datum/antagonist/traitor/human/internal_affairs/forge_traitor_objectives()
|
||||
forge_iaa_objectives()
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = owner
|
||||
add_objective(escape_objective)
|
||||
|
||||
/datum/antagonist/traitor/AI/internal_affairs/forge_traitor_objectives()
|
||||
forge_iaa_objectives()
|
||||
var/datum/objective/survive/survive_objective = new
|
||||
survive_objective.owner = owner
|
||||
add_objective(survive_objective)
|
||||
|
||||
/datum/antagonist/traitor/proc/greet_iaa()
|
||||
var/datum/antagonist/traitor/human/internal_affairs/this = src //Should only use this if IAA
|
||||
var/crime = pick("distribution of contraband" , "unauthorized erotic action on duty", "embezzlement", "piloting under the influence", "dereliction of duty", "syndicate collaboration", "mutiny", "multiple homicides", "corporate espionage", "recieving bribes", "malpractice", "worship of prohbited life forms", "possession of profane texts", "murder", "arson", "insulting their manager", "grand theft", "conspiracy", "attempting to unionize", "vandalism", "gross incompetence")
|
||||
|
||||
to_chat(owner.current, "<span class='userdanger'>You are the [special_role].</span>")
|
||||
if(this.syndicate)
|
||||
to_chat(owner.current, "<span class='userdanger'>Your target has been framed for [crime], and you have been tasked with eliminating them to prevent them defending themselves in court.</span>")
|
||||
to_chat(owner.current, "<B><font size=5 color=red>Any damage you cause will be a further embarrassment to Nanotrasen, so you have no limits on collateral damage.</font></B>")
|
||||
to_chat(owner.current, "<span class='userdanger'> You have been provided with a standard uplink to accomplish your task. </span>")
|
||||
else
|
||||
to_chat(owner.current, "<span class='userdanger'>Your target is suspected of [crime], and you have been tasked with eliminating them by any means necessary to avoid a costly and embarrassing public trial.</span>")
|
||||
to_chat(owner.current, "<B><font size=5 color=red>While you have a license to kill, unneeded property damage or loss of employee life will lead to your contract being terminated.</font></B>")
|
||||
to_chat(owner.current, "<span class='userdanger'>For the sake of plausible deniability, you have been equipped with an array of captured Syndicate weaponry available via uplink.</span>")
|
||||
|
||||
to_chat(owner.current, "<span class='userdanger'>Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them.</span>")
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/traitor/AI/internal_affairs/greet()
|
||||
greet_iaa()
|
||||
/datum/antagonist/traitor/human/internal_affairs/greet()
|
||||
greet_iaa()
|
||||
|
||||
|
||||
#undef PROB_ACTUAL_TRAITOR
|
||||
#undef PINPOINTER_EXTRA_RANDOM_RANGE
|
||||
#undef PINPOINTER_MINIMUM_RANGE
|
||||
#undef PINPOINTER_PING_TIME
|
||||
@@ -0,0 +1,326 @@
|
||||
/datum/antagonist/traitor
|
||||
name = "Traitor"
|
||||
var/should_specialise = TRUE //do we split into AI and human
|
||||
var/base_datum_custom = ANTAG_DATUM_TRAITOR_CUSTOM //used for body transfer
|
||||
var/ai_datum = ANTAG_DATUM_TRAITOR_AI
|
||||
var/human_datum = ANTAG_DATUM_TRAITOR_HUMAN
|
||||
var/special_role = "traitor"
|
||||
var/employer = "The Syndicate"
|
||||
var/give_objectives = TRUE
|
||||
var/should_give_codewords = TRUE
|
||||
var/list/objectives_given = list()
|
||||
|
||||
/datum/antagonist/traitor/proc/transfer_important_variables(datum/antagonist/traitor/other)
|
||||
other.silent = silent
|
||||
other.employer = employer
|
||||
other.special_role = special_role
|
||||
other.objectives_given = objectives_given
|
||||
|
||||
/datum/antagonist/traitor/custom
|
||||
ai_datum = ANTAG_DATUM_TRAITOR_AI_CUSTOM
|
||||
human_datum = ANTAG_DATUM_TRAITOR_HUMAN_CUSTOM
|
||||
|
||||
/datum/antagonist/traitor/human
|
||||
should_specialise = FALSE
|
||||
var/should_equip = TRUE
|
||||
/datum/antagonist/traitor/human/custom
|
||||
silent = TRUE
|
||||
should_give_codewords = FALSE
|
||||
give_objectives = FALSE
|
||||
should_equip = FALSE //Duplicating TCs is dangerous
|
||||
|
||||
/datum/antagonist/traitor/AI
|
||||
should_specialise = FALSE
|
||||
/datum/antagonist/traitor/AI/custom
|
||||
silent = TRUE
|
||||
should_give_codewords = FALSE
|
||||
give_objectives = FALSE
|
||||
|
||||
|
||||
/datum/antagonist/traitor/on_body_transfer(mob/living/old_body, mob/living/new_body)
|
||||
if(istype(new_body,/mob/living/silicon/ai)==istype(old_body,/mob/living/silicon/ai))
|
||||
..()
|
||||
else
|
||||
silent = TRUE
|
||||
owner.add_antag_datum(base_datum_custom)
|
||||
for(var/datum/antagonist/traitor/new_datum in owner.antag_datums)
|
||||
if(new_datum == src)
|
||||
continue
|
||||
transfer_important_variables(new_datum)
|
||||
break
|
||||
on_removal()
|
||||
|
||||
|
||||
|
||||
/datum/antagonist/traitor/human/custom //used to give custom objectives
|
||||
silent = TRUE
|
||||
give_objectives = FALSE
|
||||
should_give_codewords = FALSE
|
||||
/datum/antagonist/traitor/AI/custom //used to give custom objectives
|
||||
silent = TRUE
|
||||
give_objectives = FALSE
|
||||
should_give_codewords = FALSE
|
||||
|
||||
/datum/antagonist/traitor/proc/specialise()
|
||||
silent = TRUE
|
||||
if(owner.current&&istype(owner.current,/mob/living/silicon/ai))
|
||||
owner.add_antag_datum(ai_datum)
|
||||
else owner.add_antag_datum(human_datum)
|
||||
on_removal()
|
||||
|
||||
/datum/antagonist/traitor/on_gain()
|
||||
if(should_specialise)
|
||||
specialise()
|
||||
return
|
||||
SSticker.mode.traitors+=owner
|
||||
owner.special_role = special_role
|
||||
if(give_objectives)
|
||||
forge_traitor_objectives()
|
||||
finalize_traitor()
|
||||
..()
|
||||
|
||||
/datum/antagonist/traitor/apply_innate_effects()
|
||||
if(owner.assigned_role == "Clown")
|
||||
var/mob/living/carbon/human/traitor_mob = owner.current
|
||||
if(traitor_mob&&istype(traitor_mob))
|
||||
if(!silent) to_chat(traitor_mob, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.")
|
||||
traitor_mob.dna.remove_mutation(CLOWNMUT)
|
||||
|
||||
/datum/antagonist/traitor/remove_innate_effects()
|
||||
if(owner.assigned_role == "Clown")
|
||||
var/mob/living/carbon/human/traitor_mob = owner.current
|
||||
if(traitor_mob&&istype(traitor_mob))
|
||||
traitor_mob.dna.add_mutation(CLOWNMUT)
|
||||
|
||||
/datum/antagonist/traitor/on_removal()
|
||||
if(should_specialise)
|
||||
return ..()//we never did any of this anyway
|
||||
SSticker.mode.traitors -= owner
|
||||
for(var/O in objectives_given)
|
||||
owner.objectives -= O
|
||||
objectives_given = list()
|
||||
if(!silent && owner.current)
|
||||
to_chat(owner.current,"<span class='userdanger'> You are no longer the [special_role]! </span>")
|
||||
owner.special_role = null
|
||||
..()
|
||||
|
||||
/datum/antagonist/traitor/AI/on_removal()
|
||||
if(owner.current && isAI(owner.current))
|
||||
var/mob/living/silicon/ai/A = owner.current
|
||||
A.set_zeroth_law("")
|
||||
A.verbs -= /mob/living/silicon/ai/proc/choose_modules
|
||||
A.malf_picker.remove_verbs(A)
|
||||
qdel(A.malf_picker)
|
||||
..()
|
||||
|
||||
/datum/antagonist/traitor/proc/add_objective(var/datum/objective/O)
|
||||
owner.objectives += O
|
||||
objectives_given += O
|
||||
|
||||
/datum/antagonist/traitor/proc/remove_objective(var/datum/objective/O)
|
||||
owner.objectives -= O
|
||||
objectives_given -= O
|
||||
|
||||
/datum/antagonist/traitor/proc/forge_traitor_objectives()
|
||||
return
|
||||
/datum/antagonist/traitor/human/forge_traitor_objectives()
|
||||
var/is_hijacker = prob(10)
|
||||
var/martyr_chance = prob(20)
|
||||
var/objective_count = is_hijacker //Hijacking counts towards number of objectives
|
||||
if(!SSticker.mode.exchange_blue && SSticker.mode.traitors.len >= 8) //Set up an exchange if there are enough traitors
|
||||
if(!SSticker.mode.exchange_red)
|
||||
SSticker.mode.exchange_red = owner
|
||||
else
|
||||
SSticker.mode.exchange_blue = owner
|
||||
assign_exchange_role(SSticker.mode.exchange_red)
|
||||
assign_exchange_role(SSticker.mode.exchange_blue)
|
||||
objective_count += 1 //Exchange counts towards number of objectives
|
||||
for(var/i = objective_count, i < config.traitor_objectives_amount, i++)
|
||||
forge_single_objective()
|
||||
|
||||
if(is_hijacker && objective_count <= config.traitor_objectives_amount) //Don't assign hijack if it would exceed the number of objectives set in config.traitor_objectives_amount
|
||||
if (!(locate(/datum/objective/hijack) in owner.objectives))
|
||||
var/datum/objective/hijack/hijack_objective = new
|
||||
hijack_objective.owner = owner
|
||||
add_objective(hijack_objective)
|
||||
return
|
||||
|
||||
|
||||
var/martyr_compatibility = 1 //You can't succeed in stealing if you're dead.
|
||||
for(var/datum/objective/O in owner.objectives)
|
||||
if(!O.martyr_compatible)
|
||||
martyr_compatibility = 0
|
||||
break
|
||||
|
||||
if(martyr_compatibility && martyr_chance)
|
||||
var/datum/objective/martyr/martyr_objective = new
|
||||
martyr_objective.owner = owner
|
||||
add_objective(martyr_objective)
|
||||
return
|
||||
|
||||
else
|
||||
if(!(locate(/datum/objective/escape) in owner.objectives))
|
||||
var/datum/objective/escape/escape_objective = new
|
||||
escape_objective.owner = owner
|
||||
add_objective(escape_objective)
|
||||
return
|
||||
|
||||
/datum/antagonist/traitor/AI/forge_traitor_objectives()
|
||||
var/objective_count = 0
|
||||
|
||||
if(prob(30))
|
||||
objective_count += forge_single_objective()
|
||||
|
||||
for(var/i = objective_count, i < config.traitor_objectives_amount, i++)
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
kill_objective.owner = owner
|
||||
kill_objective.find_target()
|
||||
add_objective(kill_objective)
|
||||
|
||||
var/datum/objective/survive/survive_objective = new
|
||||
survive_objective.owner = owner
|
||||
add_objective(survive_objective)
|
||||
/datum/antagonist/traitor/proc/forge_single_objective()
|
||||
return 0
|
||||
/datum/antagonist/traitor/human/forge_single_objective() //Returns how many objectives are added
|
||||
.=1
|
||||
if(prob(50))
|
||||
var/list/active_ais = active_ais()
|
||||
if(active_ais.len && prob(100/GLOB.joined_player_list.len))
|
||||
var/datum/objective/destroy/destroy_objective = new
|
||||
destroy_objective.owner = owner
|
||||
destroy_objective.find_target()
|
||||
add_objective(destroy_objective)
|
||||
else if(prob(30))
|
||||
var/datum/objective/maroon/maroon_objective = new
|
||||
maroon_objective.owner = owner
|
||||
maroon_objective.find_target()
|
||||
add_objective(maroon_objective)
|
||||
else
|
||||
var/datum/objective/assassinate/kill_objective = new
|
||||
kill_objective.owner = owner
|
||||
kill_objective.find_target()
|
||||
add_objective(kill_objective)
|
||||
else
|
||||
var/datum/objective/steal/steal_objective = new
|
||||
steal_objective.owner = owner
|
||||
steal_objective.find_target()
|
||||
add_objective(steal_objective)
|
||||
|
||||
/datum/antagonist/traitor/AI/forge_single_objective()
|
||||
.=1
|
||||
var/special_pick = rand(1,4)
|
||||
switch(special_pick)
|
||||
if(1)
|
||||
var/datum/objective/block/block_objective = new
|
||||
block_objective.owner = owner
|
||||
add_objective(block_objective)
|
||||
if(2)
|
||||
var/datum/objective/purge/purge_objective = new
|
||||
purge_objective.owner = owner
|
||||
add_objective(purge_objective)
|
||||
if(3)
|
||||
var/datum/objective/robot_army/robot_objective = new
|
||||
robot_objective.owner = owner
|
||||
add_objective(robot_objective)
|
||||
if(4) //Protect and strand a target
|
||||
var/datum/objective/protect/yandere_one = new
|
||||
yandere_one.owner = owner
|
||||
add_objective(yandere_one)
|
||||
yandere_one.find_target()
|
||||
var/datum/objective/maroon/yandere_two = new
|
||||
yandere_two.owner = owner
|
||||
yandere_two.target = yandere_one.target
|
||||
yandere_two.update_explanation_text() // normally called in find_target()
|
||||
add_objective(yandere_two)
|
||||
.=2
|
||||
/datum/antagonist/traitor/greet()
|
||||
to_chat(owner.current, "<B><font size=3 color=red>You are the [owner.special_role].</font></B>")
|
||||
owner.announce_objectives()
|
||||
if(should_give_codewords)
|
||||
give_codewords()
|
||||
|
||||
/datum/antagonist/traitor/proc/finalize_traitor()
|
||||
SSticker.mode.update_traitor_icons_added(owner)
|
||||
return
|
||||
|
||||
/datum/antagonist/traitor/AI/finalize_traitor()
|
||||
..()
|
||||
add_law_zero()
|
||||
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/malf.ogg', 100, FALSE, pressure_affected = FALSE)
|
||||
owner.current.grant_language(/datum/language/codespeak)
|
||||
|
||||
/datum/antagonist/traitor/human/finalize_traitor()
|
||||
..()
|
||||
if(should_equip)
|
||||
equip(silent)
|
||||
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/tatoralert.ogg', 100, FALSE, pressure_affected = FALSE)
|
||||
|
||||
/datum/antagonist/traitor/proc/give_codewords()
|
||||
if(!owner.current)
|
||||
return
|
||||
var/mob/traitor_mob=owner.current
|
||||
|
||||
to_chat(traitor_mob, "<U><B>The Syndicate provided you with the following information on how to identify their agents:</B></U>")
|
||||
to_chat(traitor_mob, "<B>Code Phrase</B>: <span class='danger'>[GLOB.syndicate_code_phrase]</span>")
|
||||
to_chat(traitor_mob, "<B>Code Response</B>: <span class='danger'>[GLOB.syndicate_code_response]</span>")
|
||||
|
||||
traitor_mob.mind.store_memory("<b>Code Phrase</b>: [GLOB.syndicate_code_phrase]")
|
||||
traitor_mob.mind.store_memory("<b>Code Response</b>: [GLOB.syndicate_code_response]")
|
||||
|
||||
to_chat(traitor_mob, "Use the code words in the order provided, during regular conversation, to identify other agents. Proceed with caution, however, as everyone is a potential foe.")
|
||||
|
||||
/datum/antagonist/traitor/AI/proc/add_law_zero()
|
||||
var/mob/living/silicon/ai/killer = owner.current
|
||||
if(!killer || !istype(killer))
|
||||
return
|
||||
var/law = "Accomplish your objectives at all costs."
|
||||
var/law_borg = "Accomplish your AI's objectives at all costs."
|
||||
killer.set_zeroth_law(law, law_borg)
|
||||
killer.set_syndie_radio()
|
||||
to_chat(killer, "Your radio has been upgraded! Use :t to speak on an encrypted channel with Syndicate Agents!")
|
||||
killer.add_malf_picker()
|
||||
|
||||
/datum/antagonist/traitor/proc/equip(var/silent = FALSE)
|
||||
/datum/antagonist/traitor/human/equip(var/silent = FALSE)
|
||||
owner.equip_traitor(employer, silent)
|
||||
|
||||
/datum/antagonist/traitor/human/proc/assign_exchange_role()
|
||||
//set faction
|
||||
var/faction = "red"
|
||||
if(owner == SSticker.mode.exchange_blue)
|
||||
faction = "blue"
|
||||
|
||||
//Assign objectives
|
||||
var/datum/objective/steal/exchange/exchange_objective = new
|
||||
exchange_objective.set_faction(faction,((faction == "red") ? SSticker.mode.exchange_blue : SSticker.mode.exchange_red))
|
||||
exchange_objective.owner = owner
|
||||
add_objective(exchange_objective)
|
||||
|
||||
if(prob(20))
|
||||
var/datum/objective/steal/exchange/backstab/backstab_objective = new
|
||||
backstab_objective.set_faction(faction)
|
||||
backstab_objective.owner = owner
|
||||
add_objective(backstab_objective)
|
||||
|
||||
//Spawn and equip documents
|
||||
var/mob/living/carbon/human/mob = owner.current
|
||||
|
||||
var/obj/item/weapon/folder/syndicate/folder
|
||||
if(owner == SSticker.mode.exchange_red)
|
||||
folder = new/obj/item/weapon/folder/syndicate/red(mob.loc)
|
||||
else
|
||||
folder = new/obj/item/weapon/folder/syndicate/blue(mob.loc)
|
||||
|
||||
var/list/slots = list (
|
||||
"backpack" = slot_in_backpack,
|
||||
"left pocket" = slot_l_store,
|
||||
"right pocket" = slot_r_store
|
||||
)
|
||||
|
||||
var/where = "At your feet"
|
||||
var/equipped_slot = mob.equip_in_one_of_slots(folder, slots)
|
||||
if (equipped_slot)
|
||||
where = "In your [equipped_slot]"
|
||||
to_chat(mob, "<BR><BR><span class='info'>[where] is a folder containing <b>secret documents</b> that another Syndicate group wants. We have set up a meeting with one of their agents on station to make an exchange. Exercise extreme caution as they cannot be trusted and may be hostile.</span><BR>")
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
qdel(H.gloves)
|
||||
|
||||
var/obj/item/clothing/suit/space/space_ninja/theSuit = new(H)
|
||||
var/obj/item/weapon/katana/energy/EK = new(H)
|
||||
var/obj/item/weapon/dash/energy_katana/EK = new(H)
|
||||
theSuit.energyKatana = EK
|
||||
|
||||
H.equip_to_slot_or_del(new /obj/item/device/radio/headset(H), slot_ears)
|
||||
|
||||
@@ -92,6 +92,10 @@
|
||||
"}
|
||||
|
||||
/datum/browser/proc/open(use_onclose = 1)
|
||||
if(isnull(window_id)) //null check because this can potentially nuke goonchat
|
||||
WARNING("Browser [title] tried to open with a null ID")
|
||||
to_chat(user, "<span class='userdanger'>The [title] browser you tried to open failed a sanity check! Please report this on github!</span>")
|
||||
return
|
||||
var/window_size = ""
|
||||
if (width && height)
|
||||
window_size = "size=[width]x[height];"
|
||||
@@ -111,7 +115,10 @@
|
||||
break
|
||||
|
||||
/datum/browser/proc/close()
|
||||
user << browse(null, "window=[window_id]")
|
||||
if(!isnull(window_id))//null check because this can potentially nuke goonchat
|
||||
user << browse(null, "window=[window_id]")
|
||||
else
|
||||
WARNING("Browser [title] tried to close with a null ID")
|
||||
|
||||
/datum/browser/alert
|
||||
var/selectedbutton = 0
|
||||
|
||||
@@ -115,12 +115,19 @@
|
||||
else
|
||||
return
|
||||
|
||||
if(isturf(source.loc))
|
||||
var/turf/T = source.loc
|
||||
if(istype(T))
|
||||
for(var/mob/living/carbon/C in oview(spread_range, source))
|
||||
if(isturf(C.loc))
|
||||
if(AStar(source, C.loc,/turf/proc/Distance, spread_range, adjacent = (spread_flags & AIRBORNE) ? /turf/proc/reachableAdjacentAtmosTurfs : /turf/proc/reachableAdjacentTurfs))
|
||||
C.ContractDisease(src)
|
||||
|
||||
var/turf/V = get_turf(C)
|
||||
if(V)
|
||||
while(TRUE)
|
||||
if(V == T)
|
||||
C.ContractDisease(src)
|
||||
break
|
||||
var/turf/Temp = get_step_towards(V, T)
|
||||
if(!CANATMOSPASS(V, Temp))
|
||||
break
|
||||
V = Temp
|
||||
|
||||
/datum/disease/process()
|
||||
if(!holder)
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
var/list/symptoms = list() // The symptoms of the disease.
|
||||
var/id = ""
|
||||
var/processing = 0
|
||||
|
||||
|
||||
// The order goes from easy to cure to hard to cure.
|
||||
var/static/list/advance_cures = list(
|
||||
"sodiumchloride", "sugar", "orangejuice",
|
||||
@@ -399,7 +399,7 @@
|
||||
AD.Refresh()
|
||||
|
||||
for(var/mob/living/carbon/human/H in shuffle(GLOB.living_mob_list))
|
||||
if(H.z != 1)
|
||||
if(H.z != ZLEVEL_STATION)
|
||||
continue
|
||||
if(!H.HasDisease(D))
|
||||
H.ForceContractDisease(D)
|
||||
|
||||
@@ -36,7 +36,7 @@ Bonus
|
||||
/datum/symptom/heal/proc/Heal(mob/living/M, datum/disease/advance/A)
|
||||
var/heal_amt = 0.5
|
||||
if(M.toxloss > 0 && prob(20))
|
||||
new /obj/effect/overlay/temp/heal(get_turf(M), "#66FF99")
|
||||
new /obj/effect/temp_visual/heal(get_turf(M), "#66FF99")
|
||||
M.adjustToxLoss(-heal_amt)
|
||||
return 1
|
||||
|
||||
@@ -67,7 +67,7 @@ Bonus
|
||||
/datum/symptom/heal/plus/Heal(mob/living/M, datum/disease/advance/A)
|
||||
var/heal_amt = 1
|
||||
if(M.toxloss > 0 && prob(20))
|
||||
new /obj/effect/overlay/temp/heal(get_turf(M), "#00FF00")
|
||||
new /obj/effect/temp_visual/heal(get_turf(M), "#00FF00")
|
||||
M.adjustToxLoss(-heal_amt)
|
||||
return 1
|
||||
|
||||
@@ -110,7 +110,7 @@ Bonus
|
||||
M.update_damage_overlays()
|
||||
|
||||
if(prob(20))
|
||||
new /obj/effect/overlay/temp/heal(get_turf(M), "#FF3333")
|
||||
new /obj/effect/temp_visual/heal(get_turf(M), "#FF3333")
|
||||
|
||||
return 1
|
||||
|
||||
@@ -148,7 +148,7 @@ Bonus
|
||||
if(M.getCloneLoss() > 0)
|
||||
M.adjustCloneLoss(-1)
|
||||
M.take_bodypart_damage(0, 1) //Deals BURN damage, which is not cured by this symptom
|
||||
new /obj/effect/overlay/temp/heal(get_turf(M), "#33FFCC")
|
||||
new /obj/effect/temp_visual/heal(get_turf(M), "#33FFCC")
|
||||
|
||||
if(!parts.len)
|
||||
return
|
||||
@@ -158,7 +158,7 @@ Bonus
|
||||
M.update_damage_overlays()
|
||||
|
||||
if(prob(20))
|
||||
new /obj/effect/overlay/temp/heal(get_turf(M), "#CC1100")
|
||||
new /obj/effect/temp_visual/heal(get_turf(M), "#CC1100")
|
||||
|
||||
return 1
|
||||
|
||||
@@ -201,7 +201,7 @@ Bonus
|
||||
M.update_damage_overlays()
|
||||
|
||||
if(prob(20))
|
||||
new /obj/effect/overlay/temp/heal(get_turf(M), "#FF9933")
|
||||
new /obj/effect/temp_visual/heal(get_turf(M), "#FF9933")
|
||||
return 1
|
||||
|
||||
|
||||
@@ -248,7 +248,7 @@ Bonus
|
||||
M.update_damage_overlays()
|
||||
|
||||
if(prob(20))
|
||||
new /obj/effect/overlay/temp/heal(get_turf(M), "#CC6600")
|
||||
new /obj/effect/temp_visual/heal(get_turf(M), "#CC6600")
|
||||
return 1
|
||||
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ Bonus
|
||||
return
|
||||
|
||||
/datum/symptom/vomit/proc/Vomit(mob/living/carbon/M)
|
||||
M.vomit(20)
|
||||
M.vomit(20)
|
||||
|
||||
/*
|
||||
//////////////////////////////////////
|
||||
@@ -75,7 +75,7 @@ Bonus
|
||||
severity = 5
|
||||
|
||||
/datum/symptom/vomit/blood/Vomit(mob/living/carbon/M)
|
||||
M.vomit(0, 1)
|
||||
M.vomit(0,TRUE)
|
||||
|
||||
|
||||
/*
|
||||
@@ -103,4 +103,4 @@ Bonus
|
||||
level = 4
|
||||
|
||||
/datum/symptom/vomit/projectile/Vomit(mob/living/carbon/M)
|
||||
M.vomit(6,0,1,5,1)
|
||||
M.vomit(6, distance = 5)
|
||||
|
||||
@@ -31,4 +31,4 @@
|
||||
affected_mob.adjustToxLoss(1)
|
||||
if(3)
|
||||
if(prob(1))
|
||||
affected_mob.vomit(95)
|
||||
affected_mob.vomit(95)
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
if(findtext(msg, "%s"))
|
||||
msg = replacetext(msg, "%s", user.p_s())
|
||||
|
||||
msg = replace_pronoun(user, msg)
|
||||
|
||||
var/mob/living/L = user
|
||||
for(var/obj/item/weapon/implant/I in L.implants)
|
||||
I.trigger(key, L)
|
||||
@@ -61,6 +63,15 @@
|
||||
user.visible_message(msg)
|
||||
log_emote("[key_name(user)] : [msg]")
|
||||
|
||||
/datum/emote/proc/replace_pronoun(mob/user, message)
|
||||
if(findtext(message, "their"))
|
||||
message = replacetext(message, "their", user.p_their())
|
||||
if(findtext(message, "them"))
|
||||
message = replacetext(message, "them", user.p_them())
|
||||
if(findtext(message, "%s"))
|
||||
message = replacetext(message, "%s", user.p_s())
|
||||
return message
|
||||
|
||||
/datum/emote/proc/select_message_type(mob/user)
|
||||
. = message
|
||||
if(!muzzle_ignore && user.is_muzzled() && emote_type == EMOTE_AUDIBLE)
|
||||
|
||||
@@ -1,10 +1,48 @@
|
||||
/proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1, ignorecap = 0, flame_range, silent = 0, smoke = 1)
|
||||
set waitfor = 0
|
||||
src = null //so we don't abort once src is deleted
|
||||
#define EXPLOSION_THROW_SPEED 4
|
||||
|
||||
GLOBAL_LIST_EMPTY(explosions)
|
||||
//Against my better judgement, I will return the explosion datum
|
||||
//If I see any GC errors for it I will find you
|
||||
//and I will gib you
|
||||
/proc/explosion(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = TRUE, ignorecap = FALSE, flame_range = 0 , silent = FALSE, smoke = FALSE)
|
||||
return new /datum/explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog, ignorecap, flame_range, silent, smoke)
|
||||
|
||||
//This datum creates 3 async tasks
|
||||
//1 GatherSpiralTurfsProc runs spiral_range_turfs(tick_checked = TRUE) to populate the affected_turfs list
|
||||
//2 CaculateExplosionBlock adds the blockings to the cached_exp_block list
|
||||
//3 The main thread explodes the prepared turfs
|
||||
|
||||
/datum/explosion
|
||||
var/explosion_id
|
||||
var/started_at
|
||||
var/running = TRUE
|
||||
var/stopped = 0 //This is the number of threads stopped !DOESN'T COUNT THREAD 2!
|
||||
var/static/id_counter = 0
|
||||
|
||||
#define EX_PREPROCESS_EXIT_CHECK \
|
||||
if(!running) {\
|
||||
stopped = 2;\
|
||||
qdel(src);\
|
||||
return;\
|
||||
}
|
||||
|
||||
#define EX_PREPROCESS_CHECK_TICK \
|
||||
if(TICK_CHECK) {\
|
||||
stoplag();\
|
||||
EX_PREPROCESS_EXIT_CHECK\
|
||||
}
|
||||
|
||||
/datum/explosion/New(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog, ignorecap, flame_range, silent, smoke)
|
||||
set waitfor = FALSE
|
||||
|
||||
var/id = ++id_counter
|
||||
explosion_id = id
|
||||
|
||||
epicenter = get_turf(epicenter)
|
||||
if(!epicenter)
|
||||
return
|
||||
|
||||
GLOB.explosions += src
|
||||
if(isnull(flame_range))
|
||||
flame_range = light_impact_range
|
||||
if(isnull(flash_range))
|
||||
@@ -30,14 +68,13 @@
|
||||
//I would make this not ex_act the thing that triggered the explosion,
|
||||
//but everything that explodes gives us their loc or a get_turf()
|
||||
//and somethings expect us to ex_act them so they can qdel()
|
||||
sleep(1) //tldr, let the calling proc call qdel(src) before we explode
|
||||
stoplag() //tldr, let the calling proc call qdel(src) before we explode
|
||||
|
||||
var/static/explosionid = 1
|
||||
var/id = explosionid++
|
||||
var/start = world.timeofday
|
||||
EX_PREPROCESS_EXIT_CHECK
|
||||
|
||||
started_at = REALTIMEOFDAY
|
||||
|
||||
var/max_range = max(devastation_range, heavy_impact_range, light_impact_range, flame_range)
|
||||
var/list/cached_exp_block = list()
|
||||
|
||||
if(adminlog)
|
||||
message_admins("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area: [get_area(epicenter)] [ADMIN_COORDJMP(epicenter)]")
|
||||
@@ -53,23 +90,27 @@
|
||||
far_dist += heavy_impact_range * 5
|
||||
far_dist += devastation_range * 20
|
||||
|
||||
var/x0 = epicenter.x
|
||||
var/y0 = epicenter.y
|
||||
var/z0 = epicenter.z
|
||||
|
||||
if(!silent)
|
||||
var/frequency = get_rand_frequency()
|
||||
var/ex_sound = get_sfx("explosion")
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
// Double check for client
|
||||
if(M && M.client)
|
||||
var/turf/M_turf = get_turf(M)
|
||||
if(M_turf && M_turf.z == epicenter.z)
|
||||
var/dist = get_dist(M_turf, epicenter)
|
||||
// If inside the blast radius + world.view - 2
|
||||
if(dist <= round(max_range + world.view - 2, 1))
|
||||
M.playsound_local(epicenter, ex_sound, 100, 1, frequency, falloff = 5)
|
||||
// You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station.
|
||||
else if(dist <= far_dist)
|
||||
var/far_volume = Clamp(far_dist, 30, 50) // Volume is based on explosion size and dist
|
||||
far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion
|
||||
M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', far_volume, 1, frequency, falloff = 5)
|
||||
var/turf/M_turf = get_turf(M)
|
||||
if(M_turf && M_turf.z == z0)
|
||||
var/dist = get_dist(M_turf, epicenter)
|
||||
// If inside the blast radius + world.view - 2
|
||||
if(dist <= round(max_range + world.view - 2, 1))
|
||||
M.playsound_local(epicenter, ex_sound, 100, 1, frequency, falloff = 5)
|
||||
// You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station.
|
||||
else if(dist <= far_dist)
|
||||
var/far_volume = Clamp(far_dist, 30, 50) // Volume is based on explosion size and dist
|
||||
far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion
|
||||
M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', far_volume, 1, frequency, falloff = 5)
|
||||
EX_PREPROCESS_CHECK_TICK
|
||||
|
||||
//postpone processing for a bit
|
||||
var/postponeCycles = max(round(devastation_range/8),1)
|
||||
@@ -77,66 +118,52 @@
|
||||
SSmachines.postpone(postponeCycles)
|
||||
|
||||
if(heavy_impact_range > 1)
|
||||
var/datum/effect_system/explosion/E
|
||||
if(smoke)
|
||||
var/datum/effect_system/explosion/smoke/E = new/datum/effect_system/explosion/smoke()
|
||||
E.set_up(epicenter)
|
||||
E.start()
|
||||
E = new /datum/effect_system/explosion/smoke
|
||||
else
|
||||
var/datum/effect_system/explosion/E = new/datum/effect_system/explosion()
|
||||
E.set_up(epicenter)
|
||||
E.start()
|
||||
E = new
|
||||
E.set_up(epicenter)
|
||||
E.start()
|
||||
|
||||
var/x0 = epicenter.x
|
||||
var/y0 = epicenter.y
|
||||
var/z0 = epicenter.z
|
||||
|
||||
var/list/affected_turfs = spiral_range_turfs(max_range, epicenter)
|
||||
|
||||
if(config.reactionary_explosions)
|
||||
for(var/turf/T in affected_turfs) // we cache the explosion block rating of every turf in the explosion area
|
||||
cached_exp_block[T] = 0
|
||||
if(T.density && T.explosion_block)
|
||||
cached_exp_block[T] += T.explosion_block
|
||||
|
||||
for(var/obj/machinery/door/D in T)
|
||||
if(D.density && D.explosion_block)
|
||||
cached_exp_block[T] += D.explosion_block
|
||||
|
||||
for(var/obj/structure/window/W in T)
|
||||
if(W.reinf && W.fulltile)
|
||||
cached_exp_block[T] += W.explosion_block
|
||||
|
||||
for(var/obj/structure/blob/B in T)
|
||||
cached_exp_block[T] += B.explosion_block
|
||||
CHECK_TICK
|
||||
EX_PREPROCESS_CHECK_TICK
|
||||
|
||||
//flash mobs
|
||||
if(flash_range)
|
||||
for(var/mob/living/L in viewers(flash_range, epicenter))
|
||||
L.flash_act()
|
||||
|
||||
CHECK_TICK
|
||||
EX_PREPROCESS_CHECK_TICK
|
||||
|
||||
var/list/exploded_this_tick = list() //open turfs that need to be blocked off while we sleep
|
||||
for(var/turf/T in affected_turfs)
|
||||
var/list/affected_turfs = GatherSpiralTurfs(max_range, epicenter)
|
||||
|
||||
if (!T)
|
||||
continue
|
||||
var/reactionary = config.reactionary_explosions
|
||||
var/list/cached_exp_block
|
||||
|
||||
if(reactionary)
|
||||
cached_exp_block = CaculateExplosionBlock(affected_turfs)
|
||||
|
||||
//lists are guaranteed to contain at least 1 turf at this point
|
||||
|
||||
var/iteration = 0
|
||||
var/affTurfLen = affected_turfs.len
|
||||
var/expBlockLen = cached_exp_block.len
|
||||
for(var/TI in affected_turfs)
|
||||
var/turf/T = TI
|
||||
++iteration
|
||||
var/init_dist = cheap_hypotenuse(T.x, T.y, x0, y0)
|
||||
var/dist = init_dist
|
||||
|
||||
if(config.reactionary_explosions)
|
||||
if(reactionary)
|
||||
var/turf/Trajectory = T
|
||||
while(Trajectory != epicenter)
|
||||
Trajectory = get_step_towards(Trajectory, epicenter)
|
||||
dist += cached_exp_block[Trajectory]
|
||||
|
||||
var/flame_dist = 0
|
||||
var/flame_dist = dist < flame_range
|
||||
var/throw_dist = dist
|
||||
|
||||
if(dist < flame_range)
|
||||
flame_dist = 1
|
||||
|
||||
if(dist < devastation_range)
|
||||
dist = 1
|
||||
else if(dist < heavy_impact_range)
|
||||
@@ -148,27 +175,61 @@
|
||||
|
||||
//------- EX_ACT AND TURF FIRES -------
|
||||
|
||||
if(T)
|
||||
if(flame_dist && prob(40) && !isspaceturf(T) && !T.density)
|
||||
new /obj/effect/hotspot(T) //Mostly for ambience!
|
||||
if(dist > 0)
|
||||
T.explosion_level = max(T.explosion_level, dist) //let the bigger one have it
|
||||
T.explosion_id = id
|
||||
T.ex_act(dist)
|
||||
exploded_this_tick += T
|
||||
if(flame_dist && prob(40) && !isspaceturf(T) && !T.density)
|
||||
new /obj/effect/hotspot(T) //Mostly for ambience!
|
||||
|
||||
if(dist > 0)
|
||||
T.explosion_level = max(T.explosion_level, dist) //let the bigger one have it
|
||||
T.explosion_id = id
|
||||
T.ex_act(dist)
|
||||
exploded_this_tick += T
|
||||
|
||||
//--- THROW ITEMS AROUND ---
|
||||
|
||||
var/throw_dir = get_dir(epicenter,T)
|
||||
for(var/obj/item/I in T)
|
||||
if(I && !I.anchored)
|
||||
if(!I.anchored)
|
||||
var/throw_range = rand(throw_dist, max_range)
|
||||
var/turf/throw_at = get_ranged_target_turf(I, throw_dir, throw_range)
|
||||
I.throw_speed = 4 //Temporarily change their throw_speed for embedding purposes (Reset when it finishes throwing, regardless of hitting anything)
|
||||
I.throw_at(throw_at, throw_range, I.throw_speed)
|
||||
I.throw_speed = EXPLOSION_THROW_SPEED //Temporarily change their throw_speed for embedding purposes (Reset when it finishes throwing, regardless of hitting anything)
|
||||
I.throw_at(throw_at, throw_range, EXPLOSION_THROW_SPEED)
|
||||
|
||||
if(TICK_CHECK)
|
||||
//wait for the lists to repop
|
||||
var/break_condition
|
||||
if(reactionary)
|
||||
//If we've caught up to the density checker thread and there are no more turfs to process
|
||||
break_condition = iteration == expBlockLen && iteration < affTurfLen
|
||||
else
|
||||
//If we've caught up to the turf gathering thread and it's still running
|
||||
break_condition = iteration == affTurfLen && !stopped
|
||||
|
||||
if(break_condition || TICK_CHECK)
|
||||
stoplag()
|
||||
|
||||
if(!running)
|
||||
break
|
||||
|
||||
//update the trackers
|
||||
affTurfLen = affected_turfs.len
|
||||
expBlockLen = cached_exp_block.len
|
||||
|
||||
if(break_condition)
|
||||
if(reactionary)
|
||||
//until there are more block checked turfs than what we are currently at
|
||||
//or the explosion has stopped
|
||||
UNTIL(iteration < affTurfLen || !running)
|
||||
else
|
||||
//until there are more gathered turfs than what we are currently at
|
||||
//or there are no more turfs to gather/the explosion has stopped
|
||||
UNTIL(iteration < expBlockLen || stopped)
|
||||
|
||||
if(!running)
|
||||
break
|
||||
|
||||
//update the trackers
|
||||
affTurfLen = affected_turfs.len
|
||||
expBlockLen = cached_exp_block.len
|
||||
|
||||
var/circumference = (PI * (init_dist + 4) * 2) //+4 to radius to prevent shit gaps
|
||||
if(exploded_this_tick.len > circumference) //only do this every revolution
|
||||
for(var/Unexplode in exploded_this_tick)
|
||||
@@ -182,24 +243,60 @@
|
||||
UnexplodeT.explosion_level = 0
|
||||
exploded_this_tick.Cut()
|
||||
|
||||
var/took = (world.timeofday-start)/10
|
||||
//You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare
|
||||
var/took = (REALTIMEOFDAY - started_at) / 10
|
||||
|
||||
//You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare
|
||||
if(GLOB.Debug2)
|
||||
log_world("## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds.")
|
||||
|
||||
//Machines which report explosions.
|
||||
for(var/array in GLOB.doppler_arrays)
|
||||
var/obj/machinery/doppler_array/A = array
|
||||
A.sense_explosion(epicenter,devastation_range,heavy_impact_range,light_impact_range,took,orig_dev_range,orig_heavy_range,orig_light_range)
|
||||
if(running) //if we aren't in a hurry
|
||||
//Machines which report explosions.
|
||||
for(var/array in GLOB.doppler_arrays)
|
||||
var/obj/machinery/doppler_array/A = array
|
||||
A.sense_explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, took,orig_dev_range, orig_heavy_range, orig_light_range)
|
||||
|
||||
return 1
|
||||
++stopped
|
||||
qdel(src)
|
||||
|
||||
#undef EX_PREPROCESS_EXIT_CHECK
|
||||
#undef EX_PREPROCESS_CHECK_TICK
|
||||
|
||||
//asyncly populate the affected_turfs list
|
||||
/datum/explosion/proc/GatherSpiralTurfs(range, turf/epicenter)
|
||||
set waitfor = FALSE
|
||||
. = list()
|
||||
spiral_range_turfs(range, epicenter, outlist = ., tick_checked = TRUE)
|
||||
++stopped
|
||||
|
||||
/proc/secondaryexplosion(turf/epicenter, range)
|
||||
for(var/turf/tile in spiral_range_turfs(range, epicenter))
|
||||
tile.ex_act(2)
|
||||
/datum/explosion/proc/CaculateExplosionBlock(list/affected_turfs)
|
||||
set waitfor = FALSE
|
||||
|
||||
. = list()
|
||||
var/processed = 0
|
||||
while(!stopped && running)
|
||||
var/I
|
||||
for(I in (processed + 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
|
||||
|
||||
if(TICK_CHECK)
|
||||
break
|
||||
|
||||
processed = I
|
||||
stoplag()
|
||||
|
||||
/datum/explosion/Destroy()
|
||||
running = FALSE
|
||||
if(stopped < 2) //wait for main thread and spiral_range thread
|
||||
return QDEL_HINT_IWILLGC
|
||||
GLOB.explosions -= src
|
||||
return ..()
|
||||
|
||||
/client/proc/check_bomb_impacts()
|
||||
set name = "Check Bomb Impact"
|
||||
@@ -247,20 +344,12 @@
|
||||
var/turf/TT = T
|
||||
while(TT != epicenter)
|
||||
TT = get_step_towards(TT,epicenter)
|
||||
if(TT.density && TT.explosion_block)
|
||||
if(TT.density)
|
||||
dist += TT.explosion_block
|
||||
|
||||
for(var/obj/machinery/door/D in TT)
|
||||
if(D.density && D.explosion_block)
|
||||
dist += D.explosion_block
|
||||
|
||||
for(var/obj/structure/window/W in TT)
|
||||
if(W.explosion_block && W.fulltile)
|
||||
dist += W.explosion_block
|
||||
|
||||
for(var/obj/structure/blob/B in T)
|
||||
dist += B.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(dist < dev)
|
||||
T.color = "red"
|
||||
T.maptext = "Dev"
|
||||
@@ -1,34 +1,44 @@
|
||||
/datum/getrev
|
||||
var/parentcommit
|
||||
var/originmastercommit
|
||||
var/commit
|
||||
var/list/testmerge = list()
|
||||
var/has_pr_details = FALSE //example data in a testmerge entry when this is true: https://api.github.com/repositories/3234987/pulls/22586
|
||||
var/has_pr_details = FALSE //tgs2 support
|
||||
var/date
|
||||
|
||||
/datum/getrev/New()
|
||||
var/head_file = file2text(".git/logs/HEAD")
|
||||
if(SERVERTOOLS && fexists("..\\prtestjob.lk"))
|
||||
if(fexists(SERVICE_PR_TEST_JSON))
|
||||
testmerge = json_decode(file2text(SERVICE_PR_TEST_JSON))
|
||||
else if(!world.RunningService() && fexists("../prtestjob.lk")) //tgs2 support
|
||||
var/list/tmp = world.file2list("..\\prtestjob.lk")
|
||||
for(var/I in tmp)
|
||||
if(I)
|
||||
testmerge |= I
|
||||
var/testlen = max(testmerge.len - 1, 0)
|
||||
var/regex/head_log = new("(\\w{40}) .+> (\\d{10}).+(?=(\n.*(\\w{40}).*){[testlen]}\n*\\Z)")
|
||||
head_log.Find(head_file)
|
||||
parentcommit = head_log.group[1]
|
||||
date = unix2date(text2num(head_log.group[2]))
|
||||
commit = head_log.group[4]
|
||||
|
||||
log_world("Running /tg/ revision:")
|
||||
log_world("[date]")
|
||||
var/list/logs = world.file2list(".git/logs/HEAD")
|
||||
if(logs)
|
||||
logs = splittext(logs[logs.len - 1], " ")
|
||||
date = unix2date(text2num(logs[5]))
|
||||
commit = logs[2]
|
||||
log_world("[date]")
|
||||
logs = world.file2list(".git/logs/refs/remotes/origin/master")
|
||||
if(logs)
|
||||
originmastercommit = splittext(logs[logs.len - 1], " ")[2]
|
||||
|
||||
if(testmerge.len)
|
||||
log_world(commit)
|
||||
for(var/line in testmerge)
|
||||
if(line)
|
||||
log_world("Test merge active of PR #[line]")
|
||||
SSblackbox.add_details("testmerged_prs","[line]")
|
||||
log_world("Based off master commit [parentcommit]")
|
||||
if(world.RunningService())
|
||||
var/tmcommit = testmerge[line]["commit"]
|
||||
log_world("Test merge active of PR #[line] commit [tmcommit]")
|
||||
SSblackbox.add_details("testmerged_prs","[line]|[tmcommit]")
|
||||
else //tgs2 support
|
||||
log_world("Test merge active of PR #[line]")
|
||||
SSblackbox.add_details("testmerged_prs","[line]")
|
||||
log_world("Based off origin/master commit [originmastercommit]")
|
||||
else
|
||||
log_world(parentcommit)
|
||||
log_world(originmastercommit)
|
||||
|
||||
/datum/getrev/proc/DownloadPRDetails()
|
||||
if(!config.githubrepoid)
|
||||
@@ -63,24 +73,27 @@
|
||||
return ""
|
||||
. = header ? "The following pull requests are currently test merged:<br>" : ""
|
||||
for(var/line in testmerge)
|
||||
var/details = ""
|
||||
if(has_pr_details)
|
||||
var/details
|
||||
if(world.RunningService())
|
||||
var/cm = testmerge[line]["commit"]
|
||||
details = ": '" + html_encode(testmerge[line]["title"]) + "' by " + html_encode(testmerge[line]["author"]) + " at commit " + html_encode(copytext(cm, 1, min(length(cm), 7)))
|
||||
else if(has_pr_details) //tgs2 support
|
||||
details = ": '" + html_encode(testmerge[line]["title"]) + "' by " + html_encode(testmerge[line]["user"]["login"])
|
||||
. += "<a href='[config.githuburl]/pull/[line]'>#[line][details]</a><br>"
|
||||
. += "<a href=\"[config.githuburl]/pull/[line]\">#[line][details]</a><br>"
|
||||
|
||||
/client/verb/showrevinfo()
|
||||
set category = "OOC"
|
||||
set name = "Show Server Revision"
|
||||
set desc = "Check the current server code revision"
|
||||
|
||||
if(GLOB.revdata.parentcommit)
|
||||
if(GLOB.revdata.originmastercommit)
|
||||
to_chat(src, "<b>Server revision compiled on:</b> [GLOB.revdata.date]")
|
||||
var/prefix = ""
|
||||
var/prefix = ""
|
||||
if(GLOB.revdata.testmerge.len)
|
||||
to_chat(src, GLOB.revdata.GetTestMergeInfo())
|
||||
prefix = "Based off master commit: "
|
||||
var/pc = GLOB.revdata.parentcommit
|
||||
to_chat(src, "[prefix]<a href='[config.githuburl]/commit/[pc]'>[copytext(pc, 1, min(length(pc), 7))]</a>")
|
||||
prefix = "Based off origin/master commit: "
|
||||
var/pc = GLOB.revdata.originmastercommit
|
||||
to_chat(src, "[prefix]<a href=\"[config.githuburl]/commit/[pc]\">[copytext(pc, 1, min(length(pc), 7))]</a>")
|
||||
else
|
||||
to_chat(src, "Revision unknown")
|
||||
to_chat(src, "<b>Current Infomational Settings:</b>")
|
||||
@@ -91,7 +104,7 @@
|
||||
to_chat(src, "Enforce Continuous Rounds: [config.continuous.len] of [config.modes.len] roundtypes")
|
||||
to_chat(src, "Allow Midround Antagonists: [config.midround_antag.len] of [config.modes.len] roundtypes")
|
||||
if(config.show_game_type_odds)
|
||||
if(SSticker.IsRoundInProgress())
|
||||
if(SSticker.IsRoundInProgress())
|
||||
var/prob_sum = 0
|
||||
var/current_odds_differ = FALSE
|
||||
var/list/probs = list()
|
||||
@@ -107,13 +120,13 @@
|
||||
probs[ctag] = 1
|
||||
prob_sum += config.probabilities[ctag]
|
||||
if(current_odds_differ)
|
||||
to_chat(src, "<b>Game Mode Odds for current round:</b>")
|
||||
to_chat(src, "<b>Game Mode Odds for current round:</b>")
|
||||
for(var/ctag in probs)
|
||||
if(config.probabilities[ctag] > 0)
|
||||
var/percentage = round(config.probabilities[ctag] / prob_sum * 100, 0.1)
|
||||
to_chat(src, "[ctag] [percentage]%")
|
||||
|
||||
to_chat(src, "<b>All Game Mode Odds:</b>")
|
||||
to_chat(src, "<b>All Game Mode Odds:</b>")
|
||||
var/sum = 0
|
||||
for(var/ctag in config.probabilities)
|
||||
sum += config.probabilities[ctag]
|
||||
|
||||
+33
-13
@@ -1,4 +1,3 @@
|
||||
|
||||
#define HOLOPAD_MAX_DIAL_TIME 200
|
||||
|
||||
/mob/camera/aiEye/remote/holo/setLoc()
|
||||
@@ -16,6 +15,7 @@
|
||||
|
||||
var/mob/camera/aiEye/remote/holo/eye //user's eye, once connected
|
||||
var/obj/effect/overlay/holo_pad_hologram/hologram //user's hologram, once connected
|
||||
var/datum/action/innate/end_holocall/hangup //hangup action
|
||||
|
||||
var/call_start_time
|
||||
|
||||
@@ -43,18 +43,25 @@
|
||||
|
||||
//cleans up ALL references :)
|
||||
/datum/holocall/Destroy()
|
||||
user.reset_perspective()
|
||||
if(user.client)
|
||||
for(var/datum/camerachunk/chunk in eye.visibleCameraChunks)
|
||||
user.client.images -= chunk.obscured
|
||||
user.remote_control = null
|
||||
QDEL_NULL(eye)
|
||||
QDEL_NULL(hangup)
|
||||
|
||||
var/user_good = !QDELETED(user)
|
||||
if(user_good)
|
||||
user.reset_perspective()
|
||||
user.remote_control = null
|
||||
|
||||
if(!QDELETED(eye))
|
||||
if(user_good && user.client)
|
||||
for(var/datum/camerachunk/chunk in eye.visibleCameraChunks)
|
||||
chunk.remove(eye)
|
||||
qdel(eye)
|
||||
eye = null
|
||||
|
||||
user = null
|
||||
|
||||
if(hologram)
|
||||
hologram.HC = null
|
||||
hologram = null
|
||||
calling_holopad.outgoing_call = null
|
||||
hologram = null
|
||||
|
||||
for(var/I in dialed_holopads)
|
||||
var/obj/machinery/holopad/H = I
|
||||
@@ -62,6 +69,7 @@
|
||||
dialed_holopads.Cut()
|
||||
|
||||
if(calling_holopad)
|
||||
calling_holopad.outgoing_call = null
|
||||
calling_holopad.SetLightsAndPower()
|
||||
calling_holopad = null
|
||||
if(connected_holopad)
|
||||
@@ -86,7 +94,7 @@
|
||||
/datum/holocall/proc/ConnectionFailure(obj/machinery/holopad/H, graceful = FALSE)
|
||||
testing("Holocall connection failure: graceful [graceful]")
|
||||
if(H == connected_holopad || H == calling_holopad)
|
||||
if(!graceful)
|
||||
if(!graceful && H != calling_holopad)
|
||||
calling_holopad.say("Connection failure.")
|
||||
qdel(src)
|
||||
return
|
||||
@@ -139,6 +147,8 @@
|
||||
user.reset_perspective(eye)
|
||||
eye.setLoc(H.loc)
|
||||
|
||||
hangup = new(eye, src)
|
||||
|
||||
//Checks the validity of a holocall and qdels itself if it's not. Returns TRUE if valid, FALSE otherwise
|
||||
/datum/holocall/proc/Check()
|
||||
for(var/I in dialed_holopads)
|
||||
@@ -152,9 +162,7 @@
|
||||
. = !QDELETED(user) && !user.incapacitated() && !QDELETED(calling_holopad) && calling_holopad.is_operational() && user.loc == calling_holopad.loc
|
||||
|
||||
if(.)
|
||||
if(connected_holopad)
|
||||
. = !QDELETED(connected_holopad) && connected_holopad.is_operational()
|
||||
else
|
||||
if(!connected_holopad)
|
||||
. = world.time < (call_start_time + HOLOPAD_MAX_DIAL_TIME)
|
||||
if(!.)
|
||||
calling_holopad.say("No answer recieved.")
|
||||
@@ -163,3 +171,15 @@
|
||||
if(!.)
|
||||
testing("Holocall Check fail")
|
||||
qdel(src)
|
||||
|
||||
/datum/action/innate/end_holocall
|
||||
name = "End Holocall"
|
||||
button_icon_state = "camera_off"
|
||||
var/datum/holocall/hcall
|
||||
|
||||
/datum/action/innate/end_holocall/New(Target, datum/holocall/HC)
|
||||
..()
|
||||
hcall = HC
|
||||
|
||||
/datum/action/innate/end_holocall/Activate()
|
||||
hcall.Disconnect(hcall.calling_holopad)
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
|
||||
var/minetype = "lavaland"
|
||||
|
||||
var/list/transition_config = list(MAIN_STATION = CROSSLINKED,
|
||||
CENTCOMM = SELFLOOPING,
|
||||
var/list/transition_config = list(CENTCOMM = SELFLOOPING,
|
||||
MAIN_STATION = CROSSLINKED,
|
||||
EMPTY_AREA_1 = CROSSLINKED,
|
||||
EMPTY_AREA_2 = CROSSLINKED,
|
||||
MINING = SELFLOOPING,
|
||||
|
||||
+6
-670
@@ -79,681 +79,17 @@
|
||||
/datum/martial_art/proc/teach(mob/living/carbon/human/H,make_temporary=0)
|
||||
if(make_temporary)
|
||||
temporary = 1
|
||||
if(temporary && H.martial_art)
|
||||
if(!H.martial_art.allow_temp_override)
|
||||
if(temporary && H.mind.martial_art)
|
||||
if(!H.mind.martial_art.allow_temp_override)
|
||||
return
|
||||
base = H.martial_art
|
||||
base = H.mind.martial_art
|
||||
if(help_verb)
|
||||
H.verbs += help_verb
|
||||
H.martial_art = src
|
||||
H.mind.martial_art = src
|
||||
|
||||
/datum/martial_art/proc/remove(mob/living/carbon/human/H)
|
||||
if(H.martial_art != src)
|
||||
if(H.mind.martial_art != src)
|
||||
return
|
||||
H.martial_art = base
|
||||
H.mind.martial_art = base
|
||||
if(help_verb)
|
||||
H.verbs -= help_verb
|
||||
|
||||
/datum/martial_art/boxing
|
||||
name = "Boxing"
|
||||
|
||||
/datum/martial_art/boxing/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
to_chat(A, "<span class='warning'>Can't disarm while boxing!</span>")
|
||||
return 1
|
||||
|
||||
/datum/martial_art/boxing/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
to_chat(A, "<span class='warning'>Can't grab while boxing!</span>")
|
||||
return 1
|
||||
|
||||
/datum/martial_art/boxing/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
|
||||
|
||||
var/atk_verb = pick("left hook","right hook","straight punch")
|
||||
|
||||
var/damage = rand(5, 8) + A.dna.species.punchdamagelow
|
||||
if(!damage)
|
||||
playsound(D.loc, A.dna.species.miss_sound, 25, 1, -1)
|
||||
D.visible_message("<span class='warning'>[A] has attempted to [atk_verb] [D]!</span>", \
|
||||
"<span class='userdanger'>[A] has attempted to [atk_verb] [D]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
add_logs(A, D, "attempted to hit", atk_verb)
|
||||
return 0
|
||||
|
||||
|
||||
var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected))
|
||||
var/armor_block = D.run_armor_check(affecting, "melee")
|
||||
|
||||
playsound(D.loc, A.dna.species.attack_sound, 25, 1, -1)
|
||||
|
||||
D.visible_message("<span class='danger'>[A] has [atk_verb]ed [D]!</span>", \
|
||||
"<span class='userdanger'>[A] has [atk_verb]ed [D]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
|
||||
D.apply_damage(damage, STAMINA, affecting, armor_block)
|
||||
add_logs(A, D, "punched (boxing) ")
|
||||
if(D.getStaminaLoss() > 50)
|
||||
var/knockout_prob = D.getStaminaLoss() + rand(-15,15)
|
||||
if((D.stat != DEAD) && prob(knockout_prob))
|
||||
D.visible_message("<span class='danger'>[A] has knocked [D] out with a haymaker!</span>", \
|
||||
"<span class='userdanger'>[A] has knocked [D] out with a haymaker!</span>")
|
||||
D.apply_effect(10,WEAKEN,armor_block)
|
||||
D.SetSleeping(5)
|
||||
D.forcesay(GLOB.hit_appends)
|
||||
add_logs(A, D, "knocked out (boxing) ")
|
||||
else if(D.lying)
|
||||
D.forcesay(GLOB.hit_appends)
|
||||
return 1
|
||||
|
||||
/mob/living/carbon/human/proc/wrestling_help()
|
||||
set name = "Recall Teachings"
|
||||
set desc = "Remember how to wrestle."
|
||||
set category = "Wrestling"
|
||||
|
||||
to_chat(usr, "<b><i>You flex your muscles and have a revelation...</i></b>")
|
||||
to_chat(usr, "<span class='notice'>Clinch</span>: Grab. Passively gives you a chance to immediately aggressively grab someone. Not always successful.")
|
||||
to_chat(usr, "<span class='notice'>Suplex</span>: Disarm someone you are grabbing. Suplexes your target to the floor. Greatly injures them and leaves both you and your target on the floor.")
|
||||
to_chat(usr, "<span class='notice'>Advanced grab</span>: Grab. Passively causes stamina damage when grabbing someone.")
|
||||
|
||||
#define TORNADO_COMBO "HHD"
|
||||
#define THROWBACK_COMBO "DHD"
|
||||
#define PLASMA_COMBO "HDDDH"
|
||||
|
||||
/datum/martial_art/plasma_fist
|
||||
name = "Plasma Fist"
|
||||
help_verb = /mob/living/carbon/human/proc/plasma_fist_help
|
||||
|
||||
|
||||
/datum/martial_art/plasma_fist/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(findtext(streak,TORNADO_COMBO))
|
||||
streak = ""
|
||||
Tornado(A,D)
|
||||
return 1
|
||||
if(findtext(streak,THROWBACK_COMBO))
|
||||
streak = ""
|
||||
Throwback(A,D)
|
||||
return 1
|
||||
if(findtext(streak,PLASMA_COMBO))
|
||||
streak = ""
|
||||
Plasma(A,D)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/martial_art/plasma_fist/proc/TornadoAnimate(mob/living/carbon/human/A)
|
||||
set waitfor = FALSE
|
||||
for(var/i in list(NORTH,SOUTH,EAST,WEST,EAST,SOUTH,NORTH,SOUTH,EAST,WEST,EAST,SOUTH))
|
||||
if(!A)
|
||||
break
|
||||
A.setDir(i)
|
||||
playsound(A.loc, 'sound/weapons/punch1.ogg', 15, 1, -1)
|
||||
sleep(1)
|
||||
|
||||
/datum/martial_art/plasma_fist/proc/Tornado(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
A.say("TORNADO SWEEP!")
|
||||
TornadoAnimate(A)
|
||||
var/obj/effect/proc_holder/spell/aoe_turf/repulse/R = new(null)
|
||||
var/list/turfs = list()
|
||||
for(var/turf/T in range(1,A))
|
||||
turfs.Add(T)
|
||||
R.cast(turfs)
|
||||
add_logs(A, D, "tornado sweeped(Plasma Fist)")
|
||||
return
|
||||
|
||||
/datum/martial_art/plasma_fist/proc/Throwback(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
D.visible_message("<span class='danger'>[A] has hit [D] with Plasma Punch!</span>", \
|
||||
"<span class='userdanger'>[A] has hit [D] with Plasma Punch!</span>")
|
||||
playsound(D.loc, 'sound/weapons/punch1.ogg', 50, 1, -1)
|
||||
var/atom/throw_target = get_edge_target_turf(D, get_dir(D, get_step_away(D, A)))
|
||||
D.throw_at(throw_target, 200, 4,A)
|
||||
A.say("HYAH!")
|
||||
add_logs(A, D, "threw back (Plasma Fist)")
|
||||
return
|
||||
|
||||
/datum/martial_art/plasma_fist/proc/Plasma(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
|
||||
playsound(D.loc, 'sound/weapons/punch1.ogg', 50, 1, -1)
|
||||
A.say("PLASMA FIST!")
|
||||
D.visible_message("<span class='danger'>[A] has hit [D] with THE PLASMA FIST TECHNIQUE!</span>", \
|
||||
"<span class='userdanger'>[A] has hit [D] with THE PLASMA FIST TECHNIQUE!</span>")
|
||||
D.gib()
|
||||
add_logs(A, D, "gibbed (Plasma Fist)")
|
||||
return
|
||||
|
||||
/datum/martial_art/plasma_fist/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
add_to_streak("H",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
basic_hit(A,D)
|
||||
return 1
|
||||
|
||||
/datum/martial_art/plasma_fist/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
add_to_streak("D",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
basic_hit(A,D)
|
||||
return 1
|
||||
|
||||
/datum/martial_art/plasma_fist/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
add_to_streak("G",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
basic_hit(A,D)
|
||||
return 1
|
||||
|
||||
/mob/living/carbon/human/proc/plasma_fist_help()
|
||||
set name = "Recall Teachings"
|
||||
set desc = "Remember the martial techniques of the Plasma Fist."
|
||||
set category = "Plasma Fist"
|
||||
|
||||
to_chat(usr, "<b><i>You clench your fists and have a flashback of knowledge...</i></b>")
|
||||
to_chat(usr, "<span class='notice'>Tornado Sweep</span>: Harm Harm Disarm. Repulses target and everyone back.")
|
||||
to_chat(usr, "<span class='notice'>Throwback</span>: Disarm Harm Disarm. Throws the target and an item at them.")
|
||||
to_chat(usr, "<span class='notice'>The Plasma Fist</span>: Harm Disarm Disarm Disarm Harm. Knocks the brain out of the opponent and gibs their body.")
|
||||
|
||||
//Used by the gang of the same name. Uses combos. Basic attacks bypass armor and never miss
|
||||
#define WRIST_WRENCH_COMBO "DD"
|
||||
#define BACK_KICK_COMBO "HG"
|
||||
#define STOMACH_KNEE_COMBO "GH"
|
||||
#define HEAD_KICK_COMBO "DHH"
|
||||
#define ELBOW_DROP_COMBO "HDHDH"
|
||||
/datum/martial_art/the_sleeping_carp
|
||||
name = "The Sleeping Carp"
|
||||
deflection_chance = 100
|
||||
no_guns = TRUE
|
||||
allow_temp_override = FALSE
|
||||
help_verb = /mob/living/carbon/human/proc/sleeping_carp_help
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(findtext(streak,WRIST_WRENCH_COMBO))
|
||||
streak = ""
|
||||
wristWrench(A,D)
|
||||
return 1
|
||||
if(findtext(streak,BACK_KICK_COMBO))
|
||||
streak = ""
|
||||
backKick(A,D)
|
||||
return 1
|
||||
if(findtext(streak,STOMACH_KNEE_COMBO))
|
||||
streak = ""
|
||||
kneeStomach(A,D)
|
||||
return 1
|
||||
if(findtext(streak,HEAD_KICK_COMBO))
|
||||
streak = ""
|
||||
headKick(A,D)
|
||||
return 1
|
||||
if(findtext(streak,ELBOW_DROP_COMBO))
|
||||
streak = ""
|
||||
elbowDrop(A,D)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/proc/wristWrench(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(!D.stat && !D.stunned && !D.weakened)
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
|
||||
D.visible_message("<span class='warning'>[A] grabs [D]'s wrist and wrenches it sideways!</span>", \
|
||||
"<span class='userdanger'>[A] grabs your wrist and violently wrenches it to the side!</span>")
|
||||
playsound(get_turf(A), 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
D.emote("scream")
|
||||
D.drop_item()
|
||||
D.apply_damage(5, BRUTE, pick("l_arm", "r_arm"))
|
||||
D.Stun(3)
|
||||
return 1
|
||||
add_logs(A, D, "wrist wrenched (Sleeping Carp)")
|
||||
return basic_hit(A,D)
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/proc/backKick(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(A.dir == D.dir && !D.stat && !D.weakened)
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
|
||||
D.visible_message("<span class='warning'>[A] kicks [D] in the back!</span>", \
|
||||
"<span class='userdanger'>[A] kicks you in the back, making you stumble and fall!</span>")
|
||||
step_to(D,get_step(D,D.dir),1)
|
||||
D.Weaken(4)
|
||||
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
|
||||
return 1
|
||||
add_logs(A, D, "back-kicked (Sleeping Carp)")
|
||||
return basic_hit(A,D)
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/proc/kneeStomach(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(!D.stat && !D.weakened)
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_KICK)
|
||||
D.visible_message("<span class='warning'>[A] knees [D] in the stomach!</span>", \
|
||||
"<span class='userdanger'>[A] winds you with a knee in the stomach!</span>")
|
||||
D.audible_message("<b>[D]</b> gags!")
|
||||
D.losebreath += 3
|
||||
D.Stun(2)
|
||||
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
|
||||
return 1
|
||||
add_logs(A, D, "stomach kneed (Sleeping Carp)")
|
||||
return basic_hit(A,D)
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/proc/headKick(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(!D.stat && !D.weakened)
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_KICK)
|
||||
D.visible_message("<span class='warning'>[A] kicks [D] in the head!</span>", \
|
||||
"<span class='userdanger'>[A] kicks you in the jaw!</span>")
|
||||
D.apply_damage(20, BRUTE, "head")
|
||||
D.drop_item()
|
||||
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 50, 1, -1)
|
||||
D.Stun(4)
|
||||
return 1
|
||||
add_logs(A, D, "head kicked (Sleeping Carp)")
|
||||
return basic_hit(A,D)
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/proc/elbowDrop(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(D.weakened || D.resting || D.stat)
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
|
||||
D.visible_message("<span class='warning'>[A] elbow drops [D]!</span>", \
|
||||
"<span class='userdanger'>[A] piledrives you with their elbow!</span>")
|
||||
if(D.stat)
|
||||
D.death() //FINISH HIM!
|
||||
D.apply_damage(50, BRUTE, "chest")
|
||||
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 75, 1, -1)
|
||||
return 1
|
||||
add_logs(A, D, "elbow dropped (Sleeping Carp)")
|
||||
return basic_hit(A,D)
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
add_to_streak("G",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
if(A.grab_state >= GRAB_AGGRESSIVE)
|
||||
D.grabbedby(A, 1)
|
||||
else
|
||||
A.start_pulling(D, 1)
|
||||
if(A.pulling)
|
||||
D.drop_all_held_items()
|
||||
D.stop_pulling()
|
||||
if(A.a_intent == INTENT_GRAB)
|
||||
add_logs(A, D, "grabbed", addition="aggressively")
|
||||
A.grab_state = GRAB_AGGRESSIVE //Instant aggressive grab
|
||||
else
|
||||
add_logs(A, D, "grabbed", addition="passively")
|
||||
A.grab_state = GRAB_PASSIVE
|
||||
return 1
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
add_to_streak("H",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
|
||||
var/atk_verb = pick("punches", "kicks", "chops", "hits", "slams")
|
||||
D.visible_message("<span class='danger'>[A] [atk_verb] [D]!</span>", \
|
||||
"<span class='userdanger'>[A] [atk_verb] you!</span>")
|
||||
D.apply_damage(rand(10,15), BRUTE)
|
||||
playsound(get_turf(D), 'sound/weapons/punch1.ogg', 25, 1, -1)
|
||||
if(prob(D.getBruteLoss()) && !D.lying)
|
||||
D.visible_message("<span class='warning'>[D] stumbles and falls!</span>", "<span class='userdanger'>The blow sends you to the ground!</span>")
|
||||
D.Weaken(4)
|
||||
add_logs(A, D, "[atk_verb] (Sleeping Carp)")
|
||||
return 1
|
||||
|
||||
|
||||
/datum/martial_art/the_sleeping_carp/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
add_to_streak("D",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/human/proc/sleeping_carp_help()
|
||||
set name = "Recall Teachings"
|
||||
set desc = "Remember the martial techniques of the Sleeping Carp clan."
|
||||
set category = "Sleeping Carp"
|
||||
|
||||
to_chat(usr, "<b><i>You retreat inward and recall the teachings of the Sleeping Carp...</i></b>")
|
||||
|
||||
to_chat(usr, "<span class='notice'>Wrist Wrench</span>: Disarm Disarm. Forces opponent to drop item in hand.")
|
||||
to_chat(usr, "<span class='notice'>Back Kick</span>: Harm Grab. Opponent must be facing away. Knocks down.")
|
||||
to_chat(usr, "<span class='notice'>Stomach Knee</span>: Grab Harm. Knocks the wind out of opponent and stuns.")
|
||||
to_chat(usr, "<span class='notice'>Head Kick</span>: Disarm Harm Harm. Decent damage, forces opponent to drop item in hand.")
|
||||
to_chat(usr, "<span class='notice'>Elbow Drop</span>: Harm Disarm Harm Disarm Harm. Opponent must be on the ground. Deals huge damage, instantly kills anyone in critical condition.")
|
||||
|
||||
//CQC
|
||||
#define SLAM_COMBO "GH"
|
||||
#define KICK_COMBO "HH"
|
||||
#define RESTRAIN_COMBO "GG"
|
||||
#define PRESSURE_COMBO "DG"
|
||||
#define CONSECUTIVE_COMBO "DDH"
|
||||
/datum/martial_art/cqc
|
||||
name = "CQC"
|
||||
help_verb = /mob/living/carbon/human/proc/CQC_help
|
||||
block_chance = 75
|
||||
|
||||
/datum/martial_art/cqc/proc/drop_restraining()
|
||||
restraining = 0
|
||||
|
||||
/datum/martial_art/cqc/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(findtext(streak,SLAM_COMBO))
|
||||
streak = ""
|
||||
Slam(A,D)
|
||||
return 1
|
||||
if(findtext(streak,KICK_COMBO))
|
||||
streak = ""
|
||||
Kick(A,D)
|
||||
return 1
|
||||
if(findtext(streak,RESTRAIN_COMBO))
|
||||
streak = ""
|
||||
Restrain(A,D)
|
||||
return 1
|
||||
if(findtext(streak,PRESSURE_COMBO))
|
||||
streak = ""
|
||||
Pressure(A,D)
|
||||
return 1
|
||||
if(findtext(streak,CONSECUTIVE_COMBO))
|
||||
streak = ""
|
||||
Consecutive(A,D)
|
||||
return 0
|
||||
|
||||
/datum/martial_art/cqc/proc/Slam(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(!D.stat || !D.weakened)
|
||||
D.visible_message("<span class='warning'>[A] slams [D] into the ground!</span>", \
|
||||
"<span class='userdanger'>[A] slams you into the ground!</span>")
|
||||
playsound(get_turf(A), 'sound/weapons/slam.ogg', 50, 1, -1)
|
||||
D.apply_damage(10, BRUTE)
|
||||
D.Weaken(6)
|
||||
add_logs(A, D, "cqc slammed")
|
||||
return 1
|
||||
|
||||
/datum/martial_art/cqc/proc/Kick(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(!D.stat || !D.weakened)
|
||||
D.visible_message("<span class='warning'>[A] kicks [D] back!</span>", \
|
||||
"<span class='userdanger'>[A] kicks you back!</span>")
|
||||
playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
|
||||
var/atom/throw_target = get_edge_target_turf(D, A.dir)
|
||||
D.throw_at(throw_target, 1, 14, A)
|
||||
D.apply_damage(10, BRUTE)
|
||||
add_logs(A, D, "cqc kicked")
|
||||
if(D.weakened && !D.stat)
|
||||
D.visible_message("<span class='warning'>[A] kicks [D]'s head, knocking them out!</span>", \
|
||||
"<span class='userdanger'>[A] kicks your head, knocking you out!</span>")
|
||||
playsound(get_turf(A), 'sound/weapons/genhit1.ogg', 50, 1, -1)
|
||||
D.SetSleeping(15)
|
||||
D.adjustBrainLoss(25)
|
||||
return 1
|
||||
|
||||
/datum/martial_art/cqc/proc/Pressure(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
D.visible_message("<span class='warning'>[A] forces their arm on [D]'s neck!</span>")
|
||||
D.adjustStaminaLoss(60)
|
||||
playsound(get_turf(A), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
|
||||
return 1
|
||||
|
||||
/datum/martial_art/cqc/proc/Restrain(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(restraining)
|
||||
return
|
||||
if(!D.stat)
|
||||
D.visible_message("<span class='warning'>[A] locks [D] into a restraining position!</span>", \
|
||||
"<span class='userdanger'>[A] locks you into a restraining position!</span>")
|
||||
D.adjustStaminaLoss(20)
|
||||
D.Stun(5)
|
||||
restraining = 1
|
||||
addtimer(CALLBACK(src, .proc/drop_restraining), 50, TIMER_UNIQUE)
|
||||
return 1
|
||||
|
||||
/datum/martial_art/cqc/proc/Consecutive(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
if(!D.stat)
|
||||
D.visible_message("<span class='warning'>[A] strikes [D]'s abdomen, neck and back consecutively</span>", \
|
||||
"<span class='userdanger'>[A] strikes your abdomen, neck and back consecutively!</span>")
|
||||
playsound(get_turf(D), 'sound/weapons/cqchit2.ogg', 50, 1, -1)
|
||||
var/obj/item/I = D.get_active_held_item()
|
||||
if(I && D.drop_item())
|
||||
A.put_in_hands(I)
|
||||
D.adjustStaminaLoss(50)
|
||||
D.apply_damage(25, BRUTE)
|
||||
return 1
|
||||
|
||||
/datum/martial_art/cqc/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
add_to_streak("G",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
if(A.grab_state >= GRAB_AGGRESSIVE)
|
||||
D.grabbedby(A, 1)
|
||||
else
|
||||
A.start_pulling(D, 1)
|
||||
if(A.pulling)
|
||||
D.stop_pulling()
|
||||
add_logs(A, D, "grabbed", addition="aggressively")
|
||||
A.grab_state = GRAB_AGGRESSIVE //Instant aggressive grab
|
||||
|
||||
return 1
|
||||
|
||||
/datum/martial_art/cqc/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
add_to_streak("H",D)
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
add_logs(A, D, "CQC'd")
|
||||
A.do_attack_animation(D)
|
||||
var/picked_hit_type = pick("CQC'd", "Big Bossed")
|
||||
var/bonus_damage = 13
|
||||
if(D.weakened || D.resting || D.lying)
|
||||
bonus_damage += 5
|
||||
picked_hit_type = "stomps on"
|
||||
D.apply_damage(bonus_damage, BRUTE)
|
||||
if(picked_hit_type == "kicks" || picked_hit_type == "stomps on")
|
||||
playsound(get_turf(D), 'sound/weapons/cqchit2.ogg', 50, 1, -1)
|
||||
else
|
||||
playsound(get_turf(D), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
|
||||
D.visible_message("<span class='danger'>[A] [picked_hit_type] [D]!</span>", \
|
||||
"<span class='userdanger'>[A] [picked_hit_type] you!</span>")
|
||||
add_logs(A, D, "[picked_hit_type] with CQC")
|
||||
if(A.resting && !D.stat && !D.weakened)
|
||||
D.visible_message("<span class='warning'>[A] leg sweeps [D]!", \
|
||||
"<span class='userdanger'>[A] leg sweeps you!</span>")
|
||||
playsound(get_turf(A), 'sound/effects/hit_kick.ogg', 50, 1, -1)
|
||||
D.apply_damage(10, BRUTE)
|
||||
D.Weaken(3)
|
||||
add_logs(A, D, "cqc sweeped")
|
||||
return 1
|
||||
|
||||
/datum/martial_art/cqc/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
add_to_streak("D",D)
|
||||
var/obj/item/I = null
|
||||
if(check_streak(A,D))
|
||||
return 1
|
||||
if(prob(65))
|
||||
if(!D.stat || !D.weakened || !restraining)
|
||||
I = D.get_active_held_item()
|
||||
D.visible_message("<span class='warning'>[A] strikes [D]'s jaw with their hand!</span>", \
|
||||
"<span class='userdanger'>[A] strikes your jaw, disorienting you!</span>")
|
||||
playsound(get_turf(D), 'sound/weapons/cqchit1.ogg', 50, 1, -1)
|
||||
if(I && D.drop_item())
|
||||
A.put_in_hands(I)
|
||||
D.Jitter(2)
|
||||
D.apply_damage(5, BRUTE)
|
||||
else
|
||||
D.visible_message("<span class='danger'>[A] attempted to disarm [D]!</span>", \
|
||||
"<span class='userdanger'>[A] attempted to disarm [D]!</span>")
|
||||
playsound(D, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
add_logs(A, D, "disarmed with CQC", "[I ? " grabbing \the [I]" : ""]")
|
||||
if(restraining && A.pulling == D)
|
||||
D.visible_message("<span class='danger'>[A] puts [D] into a chokehold!</span>", \
|
||||
"<span class='userdanger'>[A] puts you into a chokehold!</span>")
|
||||
D.SetSleeping(20)
|
||||
restraining = 0
|
||||
if(A.grab_state < GRAB_NECK)
|
||||
A.grab_state = GRAB_NECK
|
||||
else
|
||||
restraining = 0
|
||||
return 0
|
||||
return 1
|
||||
|
||||
/mob/living/carbon/human/proc/CQC_help()
|
||||
set name = "Remember The Basics"
|
||||
set desc = "You try to remember some of the basics of CQC."
|
||||
set category = "CQC"
|
||||
|
||||
to_chat(usr, "<b><i>You try to remember some of the basics of CQC.</i></b>")
|
||||
|
||||
to_chat(usr, "<span class='notice'>Slam</span>: Grab Harm. Slam opponent into the ground, weakens and knocks down.")
|
||||
to_chat(usr, "<span class='notice'>CQC Kick</span>: Harm Disarm Harm. Knocks opponent away. Knocks out stunned or weakened opponents.")
|
||||
to_chat(usr, "<span class='notice'>Restrain</span>: Grab Grab. Locks opponents into a restraining position, disarm to knock them out with a choke hold.")
|
||||
to_chat(usr, "<span class='notice'>Pressure</span>: Disarm Grab. Decent stamina damage.")
|
||||
to_chat(usr, "<span class='notice'>Consecutive CQC</span>: Disarm Disarm Harm. Mainly offensive move, huge damage and decent stamina damage.")
|
||||
|
||||
to_chat(usr, "<b><i>In addition, by having your throw mode on when being attacked, you enter an active defense mode where you have a chance to block and sometimes even counter attacks done to you.</i></b>")
|
||||
|
||||
//ITEMS
|
||||
|
||||
/obj/item/clothing/gloves/boxing
|
||||
var/datum/martial_art/boxing/style = new
|
||||
|
||||
/obj/item/clothing/gloves/boxing/equipped(mob/user, slot)
|
||||
if(!ishuman(user))
|
||||
return
|
||||
if(slot == slot_gloves)
|
||||
var/mob/living/carbon/human/H = user
|
||||
style.teach(H,1)
|
||||
return
|
||||
|
||||
/obj/item/clothing/gloves/boxing/dropped(mob/user)
|
||||
if(!ishuman(user))
|
||||
return
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(H.get_item_by_slot(slot_gloves) == src)
|
||||
style.remove(H)
|
||||
return
|
||||
|
||||
/obj/item/weapon/storage/belt/champion/wrestling
|
||||
name = "Wrestling Belt"
|
||||
var/datum/martial_art/wrestling/style = new
|
||||
|
||||
/obj/item/weapon/storage/belt/champion/wrestling/equipped(mob/user, slot)
|
||||
if(!ishuman(user))
|
||||
return
|
||||
if(slot == slot_belt)
|
||||
var/mob/living/carbon/human/H = user
|
||||
style.teach(H,1)
|
||||
return
|
||||
|
||||
/obj/item/weapon/storage/belt/champion/wrestling/dropped(mob/user)
|
||||
if(!ishuman(user))
|
||||
return
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(H.get_item_by_slot(slot_belt) == src)
|
||||
style.remove(H)
|
||||
return
|
||||
|
||||
/obj/item/weapon/plasma_fist_scroll
|
||||
name = "frayed scroll"
|
||||
desc = "An aged and frayed scrap of paper written in shifting runes. There are hand-drawn illustrations of pugilism."
|
||||
icon = 'icons/obj/wizard.dmi'
|
||||
icon_state ="scroll2"
|
||||
var/used = 0
|
||||
|
||||
/obj/item/weapon/plasma_fist_scroll/attack_self(mob/user)
|
||||
if(!ishuman(user))
|
||||
return
|
||||
if(!used)
|
||||
var/mob/living/carbon/human/H = user
|
||||
var/datum/martial_art/plasma_fist/F = new/datum/martial_art/plasma_fist(null)
|
||||
F.teach(H)
|
||||
to_chat(H, "<span class='boldannounce'>You have learned the ancient martial art of Plasma Fist.</span>")
|
||||
used = 1
|
||||
desc = "It's completely blank."
|
||||
name = "empty scroll"
|
||||
icon_state = "blankscroll"
|
||||
|
||||
/obj/item/weapon/cqc_manual
|
||||
name = "old manual"
|
||||
desc = "A small, black manual. There are drawn instructions of tactical hand-to-hand combat."
|
||||
icon = 'icons/obj/library.dmi'
|
||||
icon_state ="cqcmanual"
|
||||
|
||||
/obj/item/weapon/cqc_manual/attack_self(mob/living/carbon/human/user)
|
||||
if(!istype(user) || !user)
|
||||
return
|
||||
to_chat(user, "<span class='boldannounce'>You remember the basics of CQC.</span>")
|
||||
var/datum/martial_art/cqc/D = new(null)
|
||||
D.teach(user)
|
||||
user.drop_item()
|
||||
visible_message("<span class='warning'>[src] beeps ominously, and a moment later it bursts up in flames.</span>")
|
||||
new /obj/effect/decal/cleanable/ash(get_turf(src))
|
||||
qdel(src)
|
||||
|
||||
/obj/item/weapon/sleeping_carp_scroll
|
||||
name = "mysterious scroll"
|
||||
desc = "A scroll filled with strange markings. It seems to be drawings of some sort of martial art."
|
||||
icon = 'icons/obj/wizard.dmi'
|
||||
icon_state = "scroll2"
|
||||
|
||||
/obj/item/weapon/sleeping_carp_scroll/attack_self(mob/living/carbon/human/user)
|
||||
if(!istype(user) || !user)
|
||||
return
|
||||
var/message = "<span class='sciradio'>You have learned the ancient martial art of the Sleeping Carp! Your hand-to-hand combat has become much more effective, and you are now able to deflect any projectiles \
|
||||
directed toward you. However, you are also unable to use any ranged weaponry. You can learn more about your newfound art by using the Recall Teachings verb in the Sleeping Carp tab.</span>"
|
||||
to_chat(user, message)
|
||||
var/datum/martial_art/the_sleeping_carp/theSleepingCarp = new(null)
|
||||
theSleepingCarp.teach(user)
|
||||
user.drop_item()
|
||||
visible_message("<span class='warning'>[src] lights up in fire and quickly burns to ash.</span>")
|
||||
new /obj/effect/decal/cleanable/ash(get_turf(src))
|
||||
qdel(src)
|
||||
|
||||
/obj/item/weapon/twohanded/bostaff
|
||||
name = "bo staff"
|
||||
desc = "A long, tall staff made of polished wood. Traditionally used in ancient old-Earth martial arts. Can be wielded to both kill and incapacitate."
|
||||
force = 10
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
slot_flags = SLOT_BACK
|
||||
force_unwielded = 10
|
||||
force_wielded = 24
|
||||
throwforce = 20
|
||||
throw_speed = 2
|
||||
attack_verb = list("smashed", "slammed", "whacked", "thwacked")
|
||||
icon = 'icons/obj/weapons.dmi'
|
||||
icon_state = "bostaff0"
|
||||
block_chance = 50
|
||||
|
||||
/obj/item/weapon/twohanded/bostaff/update_icon()
|
||||
icon_state = "bostaff[wielded]"
|
||||
return
|
||||
|
||||
/obj/item/weapon/twohanded/bostaff/attack(mob/target, mob/living/user)
|
||||
add_fingerprint(user)
|
||||
if((CLUMSY in user.disabilities) && prob(50))
|
||||
to_chat(user, "<span class ='warning'>You club yourself over the head with [src].</span>")
|
||||
user.Weaken(3)
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
H.apply_damage(2*force, BRUTE, "head")
|
||||
else
|
||||
user.take_bodypart_damage(2*force)
|
||||
return
|
||||
if(iscyborg(target))
|
||||
return ..()
|
||||
if(!isliving(target))
|
||||
return ..()
|
||||
var/mob/living/carbon/C = target
|
||||
if(C.stat)
|
||||
to_chat(user, "<span class='warning'>It would be dishonorable to attack a foe while they cannot retaliate.</span>")
|
||||
return
|
||||
if(user.a_intent == INTENT_DISARM)
|
||||
if(!wielded)
|
||||
return ..()
|
||||
if(!ishuman(target))
|
||||
return ..()
|
||||
var/mob/living/carbon/human/H = target
|
||||
var/list/fluffmessages = list("[user] clubs [H] with [src]!", \
|
||||
"[user] smacks [H] with the butt of [src]!", \
|
||||
"[user] broadsides [H] with [src]!", \
|
||||
"[user] smashes [H]'s head with [src]!", \
|
||||
"[user] beats [H] with front of [src]!", \
|
||||
"[user] twirls and slams [H] with [src]!")
|
||||
H.visible_message("<span class='warning'>[pick(fluffmessages)]</span>", \
|
||||
"<span class='userdanger'>[pick(fluffmessages)]</span>")
|
||||
playsound(get_turf(user), 'sound/effects/woodhit.ogg', 75, 1, -1)
|
||||
H.adjustStaminaLoss(rand(13,20))
|
||||
if(prob(10))
|
||||
H.visible_message("<span class='warning'>[H] collapses!</span>", \
|
||||
"<span class='userdanger'>Your legs give out!</span>")
|
||||
H.Weaken(4)
|
||||
if(H.staminaloss && !H.sleeping)
|
||||
var/total_health = (H.health - H.staminaloss)
|
||||
if(total_health <= HEALTH_THRESHOLD_CRIT && !H.stat)
|
||||
H.visible_message("<span class='warning'>[user] delivers a heavy hit to [H]'s head, knocking them out cold!</span>", \
|
||||
"<span class='userdanger'>[user] knocks you unconscious!</span>")
|
||||
H.SetSleeping(30)
|
||||
H.adjustBrainLoss(25)
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/twohanded/bostaff/hit_reaction(mob/living/carbon/human/owner, attack_text, final_block_chance)
|
||||
if(wielded)
|
||||
return ..()
|
||||
return 0
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/datum/martial_art/boxing
|
||||
name = "Boxing"
|
||||
|
||||
/datum/martial_art/boxing/disarm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
to_chat(A, "<span class='warning'>Can't disarm while boxing!</span>")
|
||||
return 1
|
||||
|
||||
/datum/martial_art/boxing/grab_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
to_chat(A, "<span class='warning'>Can't grab while boxing!</span>")
|
||||
return 1
|
||||
|
||||
/datum/martial_art/boxing/harm_act(mob/living/carbon/human/A, mob/living/carbon/human/D)
|
||||
|
||||
A.do_attack_animation(D, ATTACK_EFFECT_PUNCH)
|
||||
|
||||
var/atk_verb = pick("left hook","right hook","straight punch")
|
||||
|
||||
var/damage = rand(5, 8) + A.dna.species.punchdamagelow
|
||||
if(!damage)
|
||||
playsound(D.loc, A.dna.species.miss_sound, 25, 1, -1)
|
||||
D.visible_message("<span class='warning'>[A] has attempted to [atk_verb] [D]!</span>", \
|
||||
"<span class='userdanger'>[A] has attempted to [atk_verb] [D]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
add_logs(A, D, "attempted to hit", atk_verb)
|
||||
return 0
|
||||
|
||||
|
||||
var/obj/item/bodypart/affecting = D.get_bodypart(ran_zone(A.zone_selected))
|
||||
var/armor_block = D.run_armor_check(affecting, "melee")
|
||||
|
||||
playsound(D.loc, A.dna.species.attack_sound, 25, 1, -1)
|
||||
|
||||
D.visible_message("<span class='danger'>[A] has [atk_verb]ed [D]!</span>", \
|
||||
"<span class='userdanger'>[A] has [atk_verb]ed [D]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
|
||||
D.apply_damage(damage, STAMINA, affecting, armor_block)
|
||||
add_logs(A, D, "punched (boxing) ")
|
||||
if(D.getStaminaLoss() > 50)
|
||||
var/knockout_prob = D.getStaminaLoss() + rand(-15,15)
|
||||
if((D.stat != DEAD) && prob(knockout_prob))
|
||||
D.visible_message("<span class='danger'>[A] has knocked [D] out with a haymaker!</span>", \
|
||||
"<span class='userdanger'>[A] has knocked [D] out with a haymaker!</span>")
|
||||
D.apply_effect(10,WEAKEN,armor_block)
|
||||
D.SetSleeping(5)
|
||||
D.forcesay(GLOB.hit_appends)
|
||||
add_logs(A, D, "knocked out (boxing) ")
|
||||
else if(D.lying)
|
||||
D.forcesay(GLOB.hit_appends)
|
||||
return 1
|
||||
|
||||
/obj/item/clothing/gloves/boxing
|
||||
var/datum/martial_art/boxing/style = new
|
||||
|
||||
/obj/item/clothing/gloves/boxing/equipped(mob/user, slot)
|
||||
if(!ishuman(user))
|
||||
return
|
||||
if(slot == slot_gloves)
|
||||
var/mob/living/carbon/human/H = user
|
||||
style.teach(H,1)
|
||||
return
|
||||
|
||||
/obj/item/clothing/gloves/boxing/dropped(mob/user)
|
||||
if(!ishuman(user))
|
||||
return
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(H.get_item_by_slot(slot_gloves) == src)
|
||||
style.remove(H)
|
||||
return
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user