Merge branch 'RCD-Updates' of https://github.com/r4d6/Citadel-Station-13 into RCD-Updates
This commit is contained in:
@@ -967,10 +967,6 @@
|
||||
},
|
||||
/turf/open/floor/plating,
|
||||
/area/awaymission/cabin)
|
||||
"dw" = (
|
||||
/obj/effect/mapping_helpers/planet_z,
|
||||
/turf/closed/indestructible/rock/snow,
|
||||
/area/space)
|
||||
|
||||
(1,1,1) = {"
|
||||
aa
|
||||
@@ -1227,7 +1223,7 @@ aa
|
||||
aa
|
||||
aa
|
||||
aa
|
||||
dw
|
||||
aa
|
||||
"}
|
||||
(2,1,1) = {"
|
||||
aa
|
||||
|
||||
@@ -4,9 +4,15 @@
|
||||
/turf/closed/indestructible/rock/snow,
|
||||
/area/awaymission/snowdin/cave/mountain)
|
||||
"ab" = (
|
||||
/obj/effect/mapping_helpers/planet_z,
|
||||
/turf/closed/indestructible/rock/snow,
|
||||
/area/awaymission/snowdin/cave/mountain)
|
||||
/obj/effect/turf_decal/tile/neutral{
|
||||
dir = 1
|
||||
},
|
||||
/obj/effect/turf_decal/tile/neutral{
|
||||
dir = 8
|
||||
},
|
||||
/mob/living/simple_animal/hostile/netherworld/migo,
|
||||
/turf/open/floor/plasteel,
|
||||
/area/awaymission/snowdin/post/mining_dock)
|
||||
"ac" = (
|
||||
/turf/closed/indestructible/rock/snow,
|
||||
/area/awaymission/snowdin/cave/mountain)
|
||||
@@ -10267,16 +10273,6 @@
|
||||
/obj/item/shard,
|
||||
/turf/open/floor/plating,
|
||||
/area/awaymission/snowdin/post/mining_dock)
|
||||
"xA" = (
|
||||
/mob/living/simple_animal/hostile/netherworld/migo,
|
||||
/obj/effect/turf_decal/tile/neutral{
|
||||
dir = 1
|
||||
},
|
||||
/obj/effect/turf_decal/tile/neutral{
|
||||
dir = 8
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/awaymission/snowdin/post/mining_dock)
|
||||
"xB" = (
|
||||
/obj/effect/turf_decal/stripes/line{
|
||||
dir = 4
|
||||
@@ -16013,7 +16009,7 @@ ac
|
||||
ac
|
||||
"}
|
||||
(2,1,1) = {"
|
||||
ab
|
||||
ac
|
||||
ac
|
||||
ac
|
||||
ac
|
||||
@@ -66518,7 +66514,7 @@ wD
|
||||
wT
|
||||
xe
|
||||
xs
|
||||
xA
|
||||
ab
|
||||
xI
|
||||
xN
|
||||
wL
|
||||
|
||||
@@ -2251,10 +2251,6 @@
|
||||
initial_gas_mix = "n2=23;o2=14"
|
||||
},
|
||||
/area/awaymission/caves/BMP_asteroid)
|
||||
"gW" = (
|
||||
/obj/effect/mapping_helpers/planet_z,
|
||||
/turf/closed/indestructible/rock,
|
||||
/area/space/nearstation)
|
||||
"gX" = (
|
||||
/obj/effect/baseturf_helper/lava,
|
||||
/turf/closed/mineral/volcanic,
|
||||
@@ -2536,7 +2532,7 @@ aa
|
||||
aa
|
||||
aa
|
||||
aa
|
||||
gW
|
||||
aa
|
||||
"}
|
||||
(2,1,1) = {"
|
||||
aa
|
||||
|
||||
@@ -7215,10 +7215,6 @@
|
||||
heat_capacity = 1e+006
|
||||
},
|
||||
/area/awaymission/moonoutpost19/research)
|
||||
"oV" = (
|
||||
/obj/effect/mapping_helpers/planet_z,
|
||||
/turf/open/space,
|
||||
/area/space)
|
||||
"vV" = (
|
||||
/obj/machinery/door/airlock/external,
|
||||
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
|
||||
@@ -7512,7 +7508,7 @@ aa
|
||||
aa
|
||||
aa
|
||||
aa
|
||||
oV
|
||||
aa
|
||||
"}
|
||||
(2,1,1) = {"
|
||||
aa
|
||||
|
||||
@@ -4,9 +4,15 @@
|
||||
/turf/closed/indestructible/rock/snow,
|
||||
/area/awaymission/snowdin/cave/mountain)
|
||||
"ab" = (
|
||||
/obj/effect/mapping_helpers/planet_z,
|
||||
/turf/closed/indestructible/rock/snow,
|
||||
/area/awaymission/snowdin/cave/mountain)
|
||||
/obj/effect/turf_decal/tile/neutral{
|
||||
dir = 1
|
||||
},
|
||||
/obj/effect/turf_decal/tile/neutral{
|
||||
dir = 8
|
||||
},
|
||||
/mob/living/simple_animal/hostile/netherworld/migo,
|
||||
/turf/open/floor/plasteel,
|
||||
/area/awaymission/snowdin/post/mining_dock)
|
||||
"ac" = (
|
||||
/turf/closed/indestructible/rock/snow,
|
||||
/area/awaymission/snowdin/cave/mountain)
|
||||
@@ -10331,16 +10337,6 @@
|
||||
/obj/item/shard,
|
||||
/turf/open/floor/plating,
|
||||
/area/awaymission/snowdin/post/mining_dock)
|
||||
"xA" = (
|
||||
/mob/living/simple_animal/hostile/netherworld/migo,
|
||||
/obj/effect/turf_decal/tile/neutral{
|
||||
dir = 1
|
||||
},
|
||||
/obj/effect/turf_decal/tile/neutral{
|
||||
dir = 8
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/awaymission/snowdin/post/mining_dock)
|
||||
"xB" = (
|
||||
/obj/effect/turf_decal/stripes/line{
|
||||
dir = 4
|
||||
@@ -16108,7 +16104,7 @@ ac
|
||||
ac
|
||||
"}
|
||||
(2,1,1) = {"
|
||||
ab
|
||||
ac
|
||||
ac
|
||||
ac
|
||||
ac
|
||||
@@ -66613,7 +66609,7 @@ wD
|
||||
wT
|
||||
xe
|
||||
xs
|
||||
xA
|
||||
ab
|
||||
xI
|
||||
xN
|
||||
wL
|
||||
|
||||
@@ -5,6 +5,16 @@
|
||||
"ab" = (
|
||||
/turf/open/space,
|
||||
/area/space)
|
||||
"ac" = (
|
||||
/obj/effect/turf_decal/tile/blue{
|
||||
dir = 1
|
||||
},
|
||||
/obj/effect/turf_decal/tile/blue{
|
||||
dir = 8
|
||||
},
|
||||
/mob/living/simple_animal/hostile/syndicate/melee/sword,
|
||||
/turf/open/floor/plasteel,
|
||||
/area/awaymission/spacebattle/cruiser)
|
||||
"ad" = (
|
||||
/obj/structure/shuttle/engine/propulsion/right{
|
||||
dir = 1
|
||||
@@ -2597,16 +2607,6 @@
|
||||
/obj/effect/spawner/lootdrop/armory_contraband,
|
||||
/turf/open/floor/plating,
|
||||
/area/awaymission/spacebattle/cruiser)
|
||||
"jK" = (
|
||||
/mob/living/simple_animal/hostile/syndicate/melee/sword,
|
||||
/obj/effect/turf_decal/tile/blue{
|
||||
dir = 1
|
||||
},
|
||||
/obj/effect/turf_decal/tile/blue{
|
||||
dir = 8
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/awaymission/spacebattle/cruiser)
|
||||
"jL" = (
|
||||
/obj/machinery/door/poddoor{
|
||||
id = "spacebattlearmory";
|
||||
@@ -2894,10 +2894,6 @@
|
||||
/obj/item/mecha_parts/mecha_equipment/weapon/energy/ion,
|
||||
/turf/open/floor/plating,
|
||||
/area/awaymission/spacebattle/cruiser)
|
||||
"kM" = (
|
||||
/obj/effect/mapping_helpers/planet_z,
|
||||
/turf/closed/mineral/random,
|
||||
/area/space/nearstation)
|
||||
"vw" = (
|
||||
/turf/closed/wall/mineral/plastitanium/nodiagonal,
|
||||
/area/awaymission/spacebattle/syndicate5)
|
||||
@@ -3175,7 +3171,7 @@ aa
|
||||
aa
|
||||
aa
|
||||
aa
|
||||
kM
|
||||
aa
|
||||
"}
|
||||
(2,1,1) = {"
|
||||
aa
|
||||
@@ -35686,10 +35682,10 @@ eC
|
||||
cn
|
||||
fL
|
||||
cp
|
||||
jK
|
||||
ac
|
||||
fs
|
||||
fO
|
||||
jK
|
||||
ac
|
||||
eM
|
||||
eM
|
||||
eM
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
"ag" = (
|
||||
/turf/closed/wall/mineral/titanium,
|
||||
/area/awaymission/undergroundoutpost45/central)
|
||||
"ah" = (
|
||||
/turf/open/space,
|
||||
/area/space/nearstation)
|
||||
"aj" = (
|
||||
/obj/effect/decal/cleanable/dirt,
|
||||
/turf/open/floor/plasteel{
|
||||
@@ -13945,10 +13948,6 @@
|
||||
temperature = 363.9
|
||||
},
|
||||
/area/awaymission/undergroundoutpost45/caves)
|
||||
"zi" = (
|
||||
/obj/effect/mapping_helpers/planet_z,
|
||||
/turf/open/space,
|
||||
/area/space/nearstation)
|
||||
"KE" = (
|
||||
/obj/machinery/atmospherics/components/unary/vent_pump/on{
|
||||
dir = 4
|
||||
@@ -14211,7 +14210,7 @@ aa
|
||||
aa
|
||||
aa
|
||||
aa
|
||||
zi
|
||||
ah
|
||||
"}
|
||||
(2,1,1) = {"
|
||||
aa
|
||||
|
||||
@@ -3571,14 +3571,6 @@
|
||||
},
|
||||
/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
|
||||
/area/lavaland/surface/outdoors)
|
||||
"Wt" = (
|
||||
/obj/effect/baseturf_helper/lava_land/surface,
|
||||
/turf/closed/wall,
|
||||
/area/mine/laborcamp/security)
|
||||
"Wz" = (
|
||||
/obj/effect/mapping_helpers/planet_z,
|
||||
/turf/open/lava/smooth/lava_land_surface,
|
||||
/area/lavaland/surface/outdoors)
|
||||
"WA" = (
|
||||
/obj/effect/mapping_helpers/airlock/cyclelink_helper{
|
||||
dir = 8
|
||||
@@ -3630,26 +3622,6 @@
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/mine/production)
|
||||
"WF" = (
|
||||
/obj/effect/baseturf_helper/lava_land/surface,
|
||||
/turf/closed/wall,
|
||||
/area/mine/laborcamp)
|
||||
"WH" = (
|
||||
/obj/effect/baseturf_helper/lava_land/surface,
|
||||
/turf/closed/wall,
|
||||
/area/mine/eva)
|
||||
"WI" = (
|
||||
/obj/effect/baseturf_helper/lava_land/surface,
|
||||
/turf/closed/wall,
|
||||
/area/mine/production)
|
||||
"WJ" = (
|
||||
/obj/effect/baseturf_helper/lava_land/surface,
|
||||
/turf/closed/wall/r_wall,
|
||||
/area/mine/maintenance)
|
||||
"WK" = (
|
||||
/obj/effect/baseturf_helper/lava_land/surface,
|
||||
/turf/closed/wall,
|
||||
/area/mine/living_quarters)
|
||||
|
||||
(1,1,1) = {"
|
||||
aa
|
||||
@@ -3906,7 +3878,7 @@ aj
|
||||
aj
|
||||
aj
|
||||
aj
|
||||
Wz
|
||||
aj
|
||||
"}
|
||||
(2,1,1) = {"
|
||||
aa
|
||||
@@ -8751,7 +8723,7 @@ aq
|
||||
WB
|
||||
aq
|
||||
bi
|
||||
WF
|
||||
aq
|
||||
WC
|
||||
aq
|
||||
bZ
|
||||
@@ -9012,7 +8984,7 @@ az
|
||||
az
|
||||
aq
|
||||
ca
|
||||
Wt
|
||||
ca
|
||||
ca
|
||||
aj
|
||||
aj
|
||||
@@ -12102,7 +12074,7 @@ ai
|
||||
cQ
|
||||
dk
|
||||
dA
|
||||
WJ
|
||||
cQ
|
||||
ed
|
||||
er
|
||||
eM
|
||||
@@ -13134,7 +13106,7 @@ dR
|
||||
ef
|
||||
es
|
||||
dZ
|
||||
WK
|
||||
cM
|
||||
fg
|
||||
cM
|
||||
cM
|
||||
@@ -20320,9 +20292,9 @@ bt
|
||||
bH
|
||||
bV
|
||||
cq
|
||||
WH
|
||||
bf
|
||||
bq
|
||||
bq
|
||||
WI
|
||||
db
|
||||
bP
|
||||
bP
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#define CURRENT_LIVING_PLAYERS 1
|
||||
#define CURRENT_LIVING_ANTAGS 2
|
||||
#define CURRENT_DEAD_PLAYERS 3
|
||||
#define CURRENT_OBSERVERS 4
|
||||
|
||||
#define NO_ASSASSIN (1<<0)
|
||||
#define WAROPS_ALWAYS_ALLOWED (1<<1)
|
||||
|
||||
#define ONLY_RULESET (1<<0)
|
||||
#define HIGHLANDER_RULESET (1<<1)
|
||||
#define TRAITOR_RULESET (1<<2)
|
||||
#define MINOR_RULESET (1<<3)
|
||||
|
||||
#define RULESET_STOP_PROCESSING 1
|
||||
@@ -38,8 +38,6 @@ require only minor tweaks.
|
||||
#define ZTRAIT_SPACE_RUINS "Space Ruins"
|
||||
#define ZTRAIT_LAVA_RUINS "Lava Ruins"
|
||||
#define ZTRAIT_ISOLATED_RUINS "Isolated Ruins" //Placing ruins on z levels with this trait will use turf reservation instead of usual placement.
|
||||
// prevents certain turfs from being stripped by a singularity
|
||||
#define ZTRAIT_PLANET "Planet"
|
||||
|
||||
// number - bombcap is multiplied by this before being applied to bombs
|
||||
#define ZTRAIT_BOMBCAP_MULTIPLIER "Bombcap Multiplier"
|
||||
@@ -60,11 +58,18 @@ require only minor tweaks.
|
||||
// CROSSLINKED - mixed in with the cross-linked space pool
|
||||
#define CROSSLINKED "Cross"
|
||||
|
||||
// string - type path of the z-level's baseturf (defaults to space)
|
||||
#define ZTRAIT_BASETURF "Baseturf"
|
||||
|
||||
// default trait definitions, used by SSmapping
|
||||
#define ZTRAITS_CENTCOM list(ZTRAIT_CENTCOM = TRUE)
|
||||
#define ZTRAITS_STATION list(ZTRAIT_LINKAGE = CROSSLINKED, ZTRAIT_STATION = TRUE)
|
||||
#define ZTRAITS_SPACE list(ZTRAIT_LINKAGE = CROSSLINKED, ZTRAIT_SPACE_RUINS = TRUE)
|
||||
#define ZTRAITS_LAVALAND list(ZTRAIT_MINING = TRUE, ZTRAIT_LAVA_RUINS = TRUE, ZTRAIT_BOMBCAP_MULTIPLIER = 5)
|
||||
#define ZTRAITS_LAVALAND list(\
|
||||
ZTRAIT_MINING = TRUE, \
|
||||
ZTRAIT_LAVA_RUINS = TRUE, \
|
||||
ZTRAIT_BOMBCAP_MULTIPLIER = 5, \
|
||||
ZTRAIT_BASETURF = /turf/open/lava/smooth/lava_land_surface)
|
||||
#define ZTRAITS_REEBE list(ZTRAIT_REEBE = TRUE, ZTRAIT_BOMBCAP_MULTIPLIER = 0.5)
|
||||
|
||||
#define DL_NAME "name"
|
||||
|
||||
@@ -169,6 +169,7 @@
|
||||
#define TRAIT_EMPATH "empath"
|
||||
#define TRAIT_FRIENDLY "friendly"
|
||||
#define TRAIT_CULT_EYES "cult_eyes"
|
||||
#define TRAIT_AUTO_CATCH_ITEM "auto_catch_item"
|
||||
#define TRAIT_CLOWN_MENTALITY "clown_mentality" // The future is now, clownman.
|
||||
#define TRAIT_FREESPRINT "free_sprinting"
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#define PLURALITY_VOTING 0
|
||||
#define APPROVAL_VOTING 1
|
||||
#define RANKED_CHOICE_VOTING 2
|
||||
#define SCORE_VOTING 3
|
||||
|
||||
GLOBAL_LIST_INIT(vote_score_options,list("Bad","Poor","Acceptable","Good","Great"))
|
||||
@@ -157,6 +157,8 @@
|
||||
WRITE_LOG(GLOB.config_error_log, text)
|
||||
SEND_TEXT(world.log, text)
|
||||
|
||||
/proc/log_mapping(text)
|
||||
WRITE_LOG(GLOB.world_map_error_log, text)
|
||||
|
||||
/* For logging round startup. */
|
||||
/proc/start_log(log)
|
||||
|
||||
@@ -12,6 +12,3 @@
|
||||
#define is_reserved_level(z) SSmapping.level_trait(z, ZTRAIT_RESERVED)
|
||||
|
||||
#define is_away_level(z) SSmapping.level_trait(z, ZTRAIT_AWAY)
|
||||
|
||||
// If true, the singularity cannot strip away asteroid turf on this Z
|
||||
#define is_planet_level(z) SSmapping.level_trait(z, ZTRAIT_PLANET)
|
||||
|
||||
@@ -758,16 +758,6 @@ GLOBAL_LIST_INIT(can_embed_types, typecacheof(list(
|
||||
/obj/item/stack/rods,
|
||||
/obj/item/pipe)))
|
||||
|
||||
/proc/can_embed(obj/item/W)
|
||||
if(W.get_sharpness())
|
||||
return 1
|
||||
if(is_pointed(W))
|
||||
return 1
|
||||
|
||||
if(is_type_in_typecache(W, GLOB.can_embed_types))
|
||||
return 1
|
||||
|
||||
|
||||
/*
|
||||
Checks if that loc and dir has an item on the wall
|
||||
*/
|
||||
|
||||
@@ -28,6 +28,8 @@ GLOBAL_VAR(world_job_debug_log)
|
||||
GLOBAL_PROTECT(world_job_debug_log)
|
||||
GLOBAL_VAR(world_virus_log)
|
||||
GLOBAL_PROTECT(world_virus_log)
|
||||
GLOBAL_VAR(world_map_error_log)
|
||||
GLOBAL_PROTECT(world_map_error_log)
|
||||
|
||||
GLOBAL_LIST_EMPTY(bombers)
|
||||
GLOBAL_PROTECT(bombers)
|
||||
|
||||
@@ -98,17 +98,19 @@
|
||||
take_damage(I.force, I.damtype, "melee", 1)
|
||||
|
||||
/mob/living/attacked_by(obj/item/I, mob/living/user)
|
||||
//CIT CHANGES START HERE - combatmode and resting checks
|
||||
var/totitemdamage = I.force
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/tempcarb = user
|
||||
if(!tempcarb.combatmode)
|
||||
totitemdamage *= 0.5
|
||||
if(user.resting)
|
||||
totitemdamage *= 0.5
|
||||
//CIT CHANGES END HERE
|
||||
if(user != src && check_shields(I, totitemdamage, "the [I.name]", MELEE_ATTACK, I.armour_penetration))
|
||||
return FALSE
|
||||
send_item_attack_message(I, user)
|
||||
if(I.force)
|
||||
//CIT CHANGES START HERE - combatmode and resting checks
|
||||
var/totitemdamage = I.force
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/tempcarb = user
|
||||
if(!tempcarb.combatmode)
|
||||
totitemdamage *= 0.5
|
||||
if(user.resting)
|
||||
totitemdamage *= 0.5
|
||||
//CIT CHANGES END HERE
|
||||
apply_damage(totitemdamage, I.damtype) //CIT CHANGE - replaces I.force with totitemdamage
|
||||
if(I.damtype == BRUTE && !HAS_TRAIT(src, TRAIT_NOMARROW))
|
||||
if(prob(33))
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
var/list/modes // allowed modes
|
||||
var/list/gamemode_cache
|
||||
var/list/votable_modes // votable modes
|
||||
var/list/storyteller_cache
|
||||
var/list/mode_names
|
||||
var/list/mode_reports
|
||||
var/list/mode_false_report_weight
|
||||
@@ -37,6 +38,7 @@
|
||||
CRASH("/datum/controller/configuration/Load() called more than once!")
|
||||
InitEntries()
|
||||
LoadModes()
|
||||
storyteller_cache = typecacheof(/datum/dynamic_storyteller, TRUE)
|
||||
if(fexists("[directory]/config.txt") && LoadEntries("config.txt") <= 1)
|
||||
var/list/legacy_configs = list("game_options.txt", "dbconfig.txt", "comms.txt")
|
||||
for(var/I in legacy_configs)
|
||||
@@ -227,6 +229,7 @@
|
||||
for(var/T in gamemode_cache)
|
||||
// I wish I didn't have to instance the game modes in order to look up
|
||||
// their information, but it is the only way (at least that I know of).
|
||||
// for future reference: just use initial() lol
|
||||
var/datum/game_mode/M = new T()
|
||||
|
||||
if(M.config_tag)
|
||||
@@ -317,6 +320,14 @@
|
||||
return new T
|
||||
return new /datum/game_mode/extended()
|
||||
|
||||
/datum/controller/configuration/proc/pick_storyteller(storyteller_name)
|
||||
for(var/T in storyteller_cache)
|
||||
var/datum/dynamic_storyteller/S = T
|
||||
var/name = initial(S.name)
|
||||
if(name && name == storyteller_name)
|
||||
return T
|
||||
return /datum/dynamic_storyteller/classic
|
||||
|
||||
/datum/controller/configuration/proc/get_runnable_modes()
|
||||
var/list/datum/game_mode/runnable_modes = new
|
||||
var/list/probabilities = Get(/datum/config_entry/keyed_list/probability)
|
||||
|
||||
@@ -315,7 +315,7 @@ SUBSYSTEM_DEF(air)
|
||||
var/starting_ats = active_turfs.len
|
||||
sleep(world.tick_lag)
|
||||
var/timer = world.timeofday
|
||||
warning("There are [starting_ats] active turfs at roundstart, this is a mapping error caused by a difference of the air between the adjacent turfs. You can see its coordinates using \"Mapping -> Show roundstart AT list\" verb (debug verbs required)")
|
||||
log_mapping("There are [starting_ats] active turfs at roundstart caused by a difference of the air between the adjacent turfs. You can see its coordinates using \"Mapping -> Show roundstart AT list\" verb (debug verbs required).")
|
||||
for(var/turf/T in active_turfs)
|
||||
GLOB.active_turfs_startlist += T
|
||||
|
||||
|
||||
@@ -13,13 +13,14 @@ SUBSYSTEM_DEF(persistence)
|
||||
var/list/saved_messages = list()
|
||||
var/list/saved_modes = list(1,2,3)
|
||||
var/list/saved_dynamic_rules = list(list(),list(),list())
|
||||
var/list/saved_threat_levels = list(1,1,1)
|
||||
var/list/saved_storytellers = list("foo","bar","baz","foo again","bar again")
|
||||
var/list/saved_maps
|
||||
var/list/saved_trophies = list()
|
||||
var/list/spawned_objects = list()
|
||||
var/list/antag_rep = list()
|
||||
var/list/antag_rep_change = list()
|
||||
var/list/picture_logging_information = list()
|
||||
var/list/saved_votes = list()
|
||||
var/list/obj/structure/sign/picture_frame/photo_frames
|
||||
var/list/obj/item/storage/photo_album/photo_albums
|
||||
|
||||
@@ -29,9 +30,12 @@ SUBSYSTEM_DEF(persistence)
|
||||
LoadChiselMessages()
|
||||
LoadTrophies()
|
||||
LoadRecentModes()
|
||||
LoadRecentThreats()
|
||||
LoadRecentStorytellers()
|
||||
LoadRecentRulesets()
|
||||
LoadRecentMaps()
|
||||
LoadPhotoPersistence()
|
||||
for(var/client/C in GLOB.clients)
|
||||
LoadSavedVote(C.ckey)
|
||||
if(CONFIG_GET(flag/use_antag_rep))
|
||||
LoadAntagReputation()
|
||||
LoadRandomizedRecipes()
|
||||
@@ -169,14 +173,23 @@ SUBSYSTEM_DEF(persistence)
|
||||
return
|
||||
saved_modes = json["data"]
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/LoadRecentThreats()
|
||||
var/json_file = file("data/RecentThreatLevels.json")
|
||||
/datum/controller/subsystem/persistence/proc/LoadRecentRulesets()
|
||||
var/json_file = file("data/RecentRulesets.json")
|
||||
if(!fexists(json_file))
|
||||
return
|
||||
var/list/json = json_decode(file2text(json_file))
|
||||
if(!json)
|
||||
return
|
||||
saved_threat_levels = json["data"]
|
||||
saved_dynamic_rules = json["data"]
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/LoadRecentStorytellers()
|
||||
var/json_file = file("data/RecentStorytellers.json")
|
||||
if(!fexists(json_file))
|
||||
return
|
||||
var/list/json = json_decode(file2text(json_file))
|
||||
if(!json)
|
||||
return
|
||||
saved_storytellers = json["data"]
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/LoadRecentMaps()
|
||||
var/json_file = file("data/RecentMaps.json")
|
||||
@@ -197,6 +210,15 @@ SUBSYSTEM_DEF(persistence)
|
||||
return
|
||||
antag_rep = json_decode(json)
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/LoadSavedVote(var/ckey)
|
||||
var/json_file = file("data/player_saves/[copytext(ckey,1,2)]/[ckey]/SavedVotes.json")
|
||||
if(!fexists(json_file))
|
||||
return
|
||||
var/list/json = json_decode(file2text(json_file))
|
||||
if(!json)
|
||||
return
|
||||
saved_votes[ckey] = json["data"]
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/SetUpTrophies(list/trophy_items)
|
||||
for(var/A in GLOB.trophy_cases)
|
||||
var/obj/structure/displaycase/trophy/T = A
|
||||
@@ -230,7 +252,7 @@ SUBSYSTEM_DEF(persistence)
|
||||
CollectRoundtype()
|
||||
if(istype(SSticker.mode, /datum/game_mode/dynamic))
|
||||
var/datum/game_mode/dynamic/mode = SSticker.mode
|
||||
CollectThreatLevel(mode)
|
||||
CollectStoryteller(mode)
|
||||
CollectRulesets(mode)
|
||||
RecordMaps()
|
||||
SavePhotoPersistence() //THIS IS PERSISTENCE, NOT THE LOGGING PORTION.
|
||||
@@ -388,13 +410,16 @@ SUBSYSTEM_DEF(persistence)
|
||||
fdel(json_file)
|
||||
WRITE_FILE(json_file, json_encode(file_data))
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/CollectThreatLevel(var/datum/game_mode/dynamic/mode)
|
||||
saved_threat_levels[3] = saved_threat_levels[2]
|
||||
saved_threat_levels[2] = saved_threat_levels [1]
|
||||
saved_threat_levels[1] = mode.threat_level
|
||||
var/json_file = file("data/RecentThreatLevels.json")
|
||||
/datum/controller/subsystem/persistence/proc/CollectStoryteller(var/datum/game_mode/dynamic/mode)
|
||||
saved_storytellers.len = 5
|
||||
saved_storytellers[5] = saved_storytellers[4]
|
||||
saved_storytellers[4] = saved_storytellers[3]
|
||||
saved_storytellers[3] = saved_storytellers[2]
|
||||
saved_storytellers[2] = saved_storytellers[1]
|
||||
saved_storytellers[1] = mode.storyteller.name
|
||||
var/json_file = file("data/RecentStorytellers.json")
|
||||
var/list/file_data = list()
|
||||
file_data["data"] = saved_threat_levels
|
||||
file_data["data"] = saved_storytellers
|
||||
fdel(json_file)
|
||||
WRITE_FILE(json_file, json_encode(file_data))
|
||||
|
||||
@@ -402,8 +427,9 @@ SUBSYSTEM_DEF(persistence)
|
||||
saved_dynamic_rules[3] = saved_dynamic_rules[2]
|
||||
saved_dynamic_rules[2] = saved_dynamic_rules[1]
|
||||
saved_dynamic_rules[1] = list()
|
||||
for(var/datum/dynamic_ruleset/ruleset in mode.executed_rules)
|
||||
saved_dynamic_rules[1] += ruleset.config_tag
|
||||
for(var/r in mode.executed_rules)
|
||||
var/datum/dynamic_ruleset/rule = r
|
||||
saved_dynamic_rules[1] += rule.config_tag
|
||||
var/json_file = file("data/RecentRulesets.json")
|
||||
var/list/file_data = list()
|
||||
file_data["data"] = saved_dynamic_rules
|
||||
@@ -473,3 +499,11 @@ SUBSYSTEM_DEF(persistence)
|
||||
|
||||
fdel(json_file)
|
||||
WRITE_FILE(json_file, json_encode(file_data))
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/SaveSavedVotes()
|
||||
for(var/ckey in saved_votes)
|
||||
var/json_file = file("data/player_saves/[copytext(ckey,1,2)]/[ckey]/SavedVotes.json")
|
||||
var/list/file_data = list()
|
||||
file_data["data"] = saved_votes[ckey]
|
||||
fdel(json_file)
|
||||
WRITE_FILE(json_file, json_encode(file_data))
|
||||
|
||||
@@ -6,6 +6,7 @@ PROCESSING_SUBSYSTEM_DEF(nanites)
|
||||
var/list/datum/nanite_cloud_backup/cloud_backups = list()
|
||||
var/list/mob/living/nanite_monitored_mobs = list()
|
||||
var/list/datum/nanite_program/relay/nanite_relays = list()
|
||||
var/neural_network_count = 0
|
||||
|
||||
/datum/controller/subsystem/processing/nanites/proc/check_hardware(datum/nanite_cloud_backup/backup)
|
||||
if(QDELETED(backup.storage) || (backup.storage.stat & (NOPOWER|BROKEN)))
|
||||
|
||||
@@ -483,7 +483,10 @@ SUBSYSTEM_DEF(ticker)
|
||||
SSticker.timeLeft = 900
|
||||
SSticker.modevoted = TRUE
|
||||
var/dynamic = CONFIG_GET(flag/dynamic_voting)
|
||||
SSvote.initiate_vote(dynamic ? "dynamic" : "roundtype","server",TRUE)
|
||||
if(dynamic)
|
||||
SSvote.initiate_vote("dynamic","server",hideresults=TRUE,votesystem=SCORE_VOTING,forced=TRUE,vote_time = 2 MINUTES)
|
||||
else
|
||||
SSvote.initiate_vote("roundtype","server",hideresults=TRUE,votesystem=PLURALITY_VOTING,forced=TRUE, vote_time = 2 MINUTES)
|
||||
|
||||
/datum/controller/subsystem/ticker/Recover()
|
||||
current_state = SSticker.current_state
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#define VOTE_COOLDOWN 10
|
||||
|
||||
SUBSYSTEM_DEF(vote)
|
||||
name = "Vote"
|
||||
wait = 10
|
||||
@@ -8,13 +10,17 @@ SUBSYSTEM_DEF(vote)
|
||||
|
||||
var/initiator = null
|
||||
var/started_time = null
|
||||
var/time_remaining = 0
|
||||
var/end_time = 0
|
||||
var/mode = null
|
||||
var/vote_system = PLURALITY_VOTING
|
||||
var/question = null
|
||||
var/list/choices = list()
|
||||
var/list/choice_descs = list() // optional descriptions
|
||||
var/list/voted = list()
|
||||
var/list/voting = list()
|
||||
var/list/saved = list()
|
||||
var/list/generated_actions = list()
|
||||
var/next_pop = 0
|
||||
|
||||
var/obfuscated = FALSE//CIT CHANGE - adds obfuscated/admin-only votes
|
||||
|
||||
@@ -22,28 +28,30 @@ SUBSYSTEM_DEF(vote)
|
||||
|
||||
/datum/controller/subsystem/vote/fire() //called by master_controller
|
||||
if(mode)
|
||||
time_remaining = round((started_time + CONFIG_GET(number/vote_period) - world.time)/10)
|
||||
|
||||
if(time_remaining < 0)
|
||||
if(end_time < world.time)
|
||||
result()
|
||||
SSpersistence.SaveSavedVotes()
|
||||
for(var/client/C in voting)
|
||||
C << browse(null, "window=vote;can_close=0")
|
||||
reset()
|
||||
else
|
||||
else if(next_pop < world.time)
|
||||
var/datum/browser/client_popup
|
||||
for(var/client/C in voting)
|
||||
client_popup = new(C, "vote", "Voting Panel")
|
||||
client_popup = new(C, "vote", "Voting Panel", nwidth=600,nheight=700)
|
||||
client_popup.set_window_options("can_close=0")
|
||||
client_popup.set_content(interface(C))
|
||||
client_popup.open(0)
|
||||
next_pop = world.time+VOTE_COOLDOWN
|
||||
|
||||
|
||||
|
||||
/datum/controller/subsystem/vote/proc/reset()
|
||||
initiator = null
|
||||
time_remaining = 0
|
||||
end_time = 0
|
||||
mode = null
|
||||
question = null
|
||||
choices.Cut()
|
||||
choice_descs.Cut()
|
||||
voted.Cut()
|
||||
voting.Cut()
|
||||
obfuscated = FALSE //CIT CHANGE - obfuscated votes
|
||||
@@ -84,17 +92,114 @@ SUBSYSTEM_DEF(vote)
|
||||
. += option
|
||||
return .
|
||||
|
||||
/datum/controller/subsystem/vote/proc/calculate_condorcet_votes(var/blackbox_text)
|
||||
// https://en.wikipedia.org/wiki/Schulze_method#Implementation
|
||||
var/list/d[][] = new/list(choices.len,choices.len) // the basic vote matrix, how many times a beats b
|
||||
for(var/ckey in voted)
|
||||
var/list/this_vote = voted[ckey]
|
||||
for(var/a in 1 to choices.len)
|
||||
for(var/b in a+1 to choices.len)
|
||||
var/a_rank = this_vote.Find(a)
|
||||
var/b_rank = this_vote.Find(b)
|
||||
a_rank = a_rank ? a_rank : choices.len+1
|
||||
b_rank = b_rank ? b_rank : choices.len+1
|
||||
if(a_rank<b_rank)
|
||||
d[a][b]++
|
||||
else if(b_rank<a_rank)
|
||||
d[b][a]++
|
||||
//if equal, do nothing
|
||||
var/list/p[][] = new/list(choices.len,choices.len) //matrix of shortest path from a to b
|
||||
for(var/i in 1 to choices.len)
|
||||
for(var/j in 1 to choices.len)
|
||||
if(i != j)
|
||||
var/pref_number = d[i][j]
|
||||
var/opposite_pref = d[j][i]
|
||||
if(pref_number>opposite_pref)
|
||||
p[i][j] = d[i][j]
|
||||
p[j][i] = 0
|
||||
else
|
||||
p[i][j] = 0
|
||||
p[j][i] = d[i][j]
|
||||
for(var/i in 1 to choices.len)
|
||||
for(var/j in 1 to choices.len)
|
||||
if(i != j)
|
||||
for(var/k in 1 to choices.len) // YEAH O(n^3) !!
|
||||
if(i != k && j != k)
|
||||
p[j][k] = max(p[j][k],min(p[j][i], p[i][k]))
|
||||
//one last pass, now that we've done the math
|
||||
for(var/i in 1 to choices.len)
|
||||
for(var/j in 1 to choices.len)
|
||||
if(i != j)
|
||||
SSblackbox.record_feedback("nested tally","voting",p[i][j],list(blackbox_text,"Shortest Paths",choices[i],choices[j]))
|
||||
if(p[i][j] >= p[j][i])
|
||||
choices[choices[i]]++ // higher shortest path = better candidate, so we add to choices here
|
||||
// choices[choices[i]] is the schulze ranking, here, rather than raw vote numbers
|
||||
|
||||
/datum/controller/subsystem/vote/proc/calculate_majority_judgement_vote(var/blackbox_text)
|
||||
// https://en.wikipedia.org/wiki/Majority_judgment
|
||||
var/list/scores_by_choice = list()
|
||||
for(var/choice in choices)
|
||||
scores_by_choice[choice] = list()
|
||||
for(var/ckey in voted)
|
||||
var/list/this_vote = voted[ckey]
|
||||
var/list/pretty_vote = list()
|
||||
for(var/choice in this_vote)
|
||||
sorted_insert(scores_by_choice[choice],this_vote[choice],/proc/cmp_numeric_asc)
|
||||
// START BALLOT GATHERING
|
||||
pretty_vote += choice
|
||||
pretty_vote[choice] = GLOB.vote_score_options[this_vote[choice]]
|
||||
SSblackbox.record_feedback("associative","voting_ballots",1,pretty_vote)
|
||||
// END BALLOT GATHERING
|
||||
for(var/score_name in scores_by_choice)
|
||||
var/list/score = scores_by_choice[score_name]
|
||||
for(var/indiv_score in score)
|
||||
SSblackbox.record_feedback("nested tally","voting",1,list(blackbox_text,"Scores",score_name,GLOB.vote_score_options[indiv_score]))
|
||||
if(score.len == 0)
|
||||
scores_by_choice -= score_name
|
||||
while(scores_by_choice.len > 1)
|
||||
var/highest_median = 0
|
||||
for(var/score_name in scores_by_choice) // first get highest median
|
||||
var/list/score = scores_by_choice[score_name]
|
||||
if(!score.len)
|
||||
scores_by_choice -= score_name
|
||||
continue
|
||||
var/median = score[max(1,round(score.len/2))]
|
||||
if(median >= highest_median)
|
||||
highest_median = median
|
||||
for(var/score_name in scores_by_choice) // then, remove
|
||||
var/list/score = scores_by_choice[score_name]
|
||||
var/median = score[max(1,round(score.len/2))]
|
||||
if(median < highest_median)
|
||||
scores_by_choice -= score_name
|
||||
for(var/score_name in scores_by_choice) // after removals
|
||||
var/list/score = scores_by_choice[score_name]
|
||||
if(score.len == 0)
|
||||
choices[score_name] += 100 // we're in a tie situation--just go with the first one
|
||||
return
|
||||
var/median_pos = max(1,round(score.len/2))
|
||||
score.Cut(median_pos,median_pos+1)
|
||||
choices[score_name]++
|
||||
|
||||
/datum/controller/subsystem/vote/proc/announce_result()
|
||||
var/list/winners = get_result()
|
||||
var/vote_title_text
|
||||
var/text
|
||||
if(question)
|
||||
text += "<b>[question]</b>"
|
||||
vote_title_text = "[question]"
|
||||
else
|
||||
text += "<b>[capitalize(mode)] Vote</b>"
|
||||
vote_title_text = "[capitalize(mode)] Vote"
|
||||
if(vote_system == RANKED_CHOICE_VOTING)
|
||||
calculate_condorcet_votes(vote_title_text)
|
||||
if(vote_system == SCORE_VOTING)
|
||||
calculate_majority_judgement_vote(vote_title_text)
|
||||
var/list/winners = get_result()
|
||||
var/was_roundtype_vote = mode == "roundtype" || mode == "dynamic"
|
||||
if(winners.len > 0)
|
||||
if(question)
|
||||
text += "<b>[question]</b>"
|
||||
else
|
||||
text += "<b>[capitalize(mode)] Vote</b>"
|
||||
if(was_roundtype_vote)
|
||||
stored_gamemode_votes = list()
|
||||
if(!obfuscated && vote_system == RANKED_CHOICE_VOTING)
|
||||
text += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!"
|
||||
for(var/i=1,i<=choices.len,i++)
|
||||
var/votes = choices[choices[i]]
|
||||
if(!votes)
|
||||
@@ -116,17 +221,25 @@ SUBSYSTEM_DEF(vote)
|
||||
log_vote(text)
|
||||
remove_action_buttons()
|
||||
to_chat(world, "\n<font color='purple'>[text]</font>")
|
||||
switch(vote_system)
|
||||
if(APPROVAL_VOTING,PLURALITY_VOTING)
|
||||
for(var/i=1,i<=choices.len,i++)
|
||||
SSblackbox.record_feedback("nested tally","voting",choices[choices[i]],list(vote_title_text,choices[i]))
|
||||
if(RANKED_CHOICE_VOTING)
|
||||
for(var/i=1,i<=voted.len,i++)
|
||||
var/list/myvote = voted[voted[i]]
|
||||
for(var/j=1,j<=myvote.len,j++)
|
||||
SSblackbox.record_feedback("nested tally","voting",1,list(vote_title_text,"[j]\th",choices[myvote[j]]))
|
||||
if(obfuscated) //CIT CHANGE - adds obfuscated votes. this messages admins with the vote's true results
|
||||
var/admintext = "Obfuscated results"
|
||||
if(vote_system == RANKED_CHOICE_VOTING)
|
||||
admintext += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!"
|
||||
for(var/i=1,i<=choices.len,i++)
|
||||
var/votes = choices[choices[i]]
|
||||
admintext += "\n<b>[choices[i]]:</b> [votes]"
|
||||
message_admins(admintext)
|
||||
return .
|
||||
|
||||
#define PEACE "calm"
|
||||
#define CHAOS "chaotic"
|
||||
|
||||
/datum/controller/subsystem/vote/proc/result()
|
||||
. = announce_result()
|
||||
var/restart = 0
|
||||
@@ -152,33 +265,15 @@ SUBSYSTEM_DEF(vote)
|
||||
if("dynamic")
|
||||
if(SSticker.current_state > GAME_STATE_PREGAME)//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 = "dynamic"
|
||||
if("extended" in choices)
|
||||
if(. == "extended")
|
||||
GLOB.dynamic_forced_extended = TRUE // we still do the rest of the stuff
|
||||
choices[PEACE] += choices["extended"]
|
||||
var/mean = 0
|
||||
var/voters = 0
|
||||
for(var/client/c in GLOB.clients)
|
||||
var/vote = c.prefs.preferred_chaos
|
||||
if(vote)
|
||||
voters += 1
|
||||
switch(vote)
|
||||
if(CHAOS_NONE)
|
||||
mean -= 0.1
|
||||
if(CHAOS_LOW)
|
||||
mean -= 0.05
|
||||
if(CHAOS_HIGH)
|
||||
mean += 0.05
|
||||
if(CHAOS_MAX)
|
||||
mean += 0.1
|
||||
mean/=voters
|
||||
if(voted.len != 0)
|
||||
mean += (choices[PEACE]*-1+choices[CHAOS])/voted.len
|
||||
GLOB.dynamic_curve_centre = mean*20
|
||||
GLOB.dynamic_curve_width = CLAMP(2-abs(mean*5),0.5,4)
|
||||
to_chat(world,"<span class='boldannounce'>Dynamic curve centre set to [GLOB.dynamic_curve_centre] and width set to [GLOB.dynamic_curve_width].</span>")
|
||||
log_admin("Dynamic curve centre set to [GLOB.dynamic_curve_centre] and width set to [GLOB.dynamic_curve_width]")
|
||||
if(. == "Secret")
|
||||
GLOB.master_mode = "secret"
|
||||
SSticker.save_mode(.)
|
||||
message_admins("The gamemode has been voted for, and has been changed to: [GLOB.master_mode]")
|
||||
log_admin("Gamemode has been voted for and switched to: [GLOB.master_mode].")
|
||||
else
|
||||
GLOB.master_mode = "dynamic"
|
||||
var/datum/dynamic_storyteller/S = config.pick_storyteller(.)
|
||||
GLOB.dynamic_storyteller_type = S
|
||||
if("map")
|
||||
var/datum/map_config/VM = config.maplist[.]
|
||||
message_admins("The map has been voted for and will change to: [VM.map_name]")
|
||||
@@ -196,27 +291,58 @@ SUBSYSTEM_DEF(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.")
|
||||
|
||||
|
||||
return .
|
||||
|
||||
/datum/controller/subsystem/vote/proc/submit_vote(vote)
|
||||
/datum/controller/subsystem/vote/proc/submit_vote(vote, score = 0)
|
||||
if(mode)
|
||||
if(CONFIG_GET(flag/no_dead_vote) && usr.stat == DEAD && !usr.client.holder)
|
||||
return 0
|
||||
if(!(usr.ckey in voted))
|
||||
if(vote && 1<=vote && vote<=choices.len)
|
||||
voted += usr.ckey
|
||||
voted[usr.ckey] = vote
|
||||
choices[choices[vote]]++ //check this
|
||||
return vote
|
||||
else if(vote && 1<=vote && vote<=choices.len)
|
||||
choices[choices[voted[usr.ckey]]]--
|
||||
voted[usr.ckey] = vote
|
||||
choices[choices[vote]]++
|
||||
return vote
|
||||
if(vote && ISINRANGE(vote, 1, choices.len))
|
||||
switch(vote_system)
|
||||
if(PLURALITY_VOTING)
|
||||
if(usr.ckey in voted)
|
||||
choices[choices[voted[usr.ckey]]]--
|
||||
voted[usr.ckey] = vote
|
||||
choices[choices[vote]]++
|
||||
return vote
|
||||
else
|
||||
voted += usr.ckey
|
||||
voted[usr.ckey] = vote
|
||||
choices[choices[vote]]++ //check this
|
||||
return vote
|
||||
if(APPROVAL_VOTING)
|
||||
if(usr.ckey in voted)
|
||||
if(vote in voted[usr.ckey])
|
||||
voted[usr.ckey] -= vote
|
||||
choices[choices[vote]]--
|
||||
else
|
||||
voted[usr.ckey] += vote
|
||||
choices[choices[vote]]++
|
||||
else
|
||||
voted += usr.ckey
|
||||
voted[usr.ckey] = list(vote)
|
||||
choices[choices[vote]]++
|
||||
return vote
|
||||
if(RANKED_CHOICE_VOTING)
|
||||
if(usr.ckey in voted)
|
||||
if(vote in voted[usr.ckey])
|
||||
voted[usr.ckey] -= vote
|
||||
else
|
||||
voted += usr.ckey
|
||||
voted[usr.ckey] = list()
|
||||
voted[usr.ckey] += vote
|
||||
saved -= usr.ckey
|
||||
if(SCORE_VOTING)
|
||||
if(!(usr.ckey in voted))
|
||||
voted += usr.ckey
|
||||
voted[usr.ckey] = list()
|
||||
voted[usr.ckey][choices[vote]] = score
|
||||
saved -= usr.ckey
|
||||
return 0
|
||||
|
||||
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, hideresults)//CIT CHANGE - adds hideresults argument to votes to allow for obfuscated votes
|
||||
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, hideresults, votesystem = PLURALITY_VOTING, forced = FALSE,vote_time = -1)//CIT CHANGE - adds hideresults argument to votes to allow for obfuscated votes
|
||||
vote_system = votesystem
|
||||
if(!mode)
|
||||
if(started_time)
|
||||
var/next_allowed_time = (started_time + CONFIG_GET(number/vote_delay))
|
||||
@@ -257,11 +383,17 @@ SUBSYSTEM_DEF(vote)
|
||||
if("roundtype") //CIT CHANGE - adds the roundstart secret/extended vote
|
||||
choices.Add("secret", "extended")
|
||||
if("dynamic")
|
||||
var/saved_threats = SSpersistence.saved_threat_levels
|
||||
if((saved_threats[1]+saved_threats[2]+saved_threats[3])>150)
|
||||
choices.Add("extended",PEACE,CHAOS)
|
||||
else
|
||||
choices.Add(PEACE,CHAOS)
|
||||
for(var/T in config.storyteller_cache)
|
||||
var/datum/dynamic_storyteller/S = T
|
||||
var/recent_rounds = 0
|
||||
for(var/i in 1 to SSpersistence.saved_storytellers.len)
|
||||
if(SSpersistence.saved_storytellers[i] == initial(S.name))
|
||||
recent_rounds++
|
||||
if(recent_rounds < initial(S.weight))
|
||||
choices.Add(initial(S.name))
|
||||
choice_descs.Add(initial(S.desc))
|
||||
choices.Add("Secret")
|
||||
choice_descs.Add("Standard secret. Switches mode if it wins.")
|
||||
if("custom")
|
||||
question = stripped_input(usr,"What is the vote for?")
|
||||
if(!question)
|
||||
@@ -280,9 +412,11 @@ SUBSYSTEM_DEF(vote)
|
||||
if(mode == "custom")
|
||||
text += "\n[question]"
|
||||
log_vote(text)
|
||||
var/vp = CONFIG_GET(number/vote_period)
|
||||
var/vp = vote_time
|
||||
if(vp == -1)
|
||||
vp = CONFIG_GET(number/vote_period)
|
||||
to_chat(world, "\n<font color='purple'><b>[text]</b>\nType <b>vote</b> or click <a href='?src=[REF(src)]'>here</a> to place your votes.\nYou have [DisplayTimeText(vp)] to vote.</font>")
|
||||
time_remaining = round(vp/10)
|
||||
end_time = started_time+vp
|
||||
for(var/c in GLOB.clients)
|
||||
SEND_SOUND(c, sound('sound/misc/server-ready.ogg'))
|
||||
var/client/C = c
|
||||
@@ -292,6 +426,11 @@ SUBSYSTEM_DEF(vote)
|
||||
C.player_details.player_actions += V
|
||||
V.Grant(C.mob)
|
||||
generated_actions += V
|
||||
if(forced)
|
||||
var/datum/browser/popup = new(C, "vote", "Voting Panel",nwidth=600,nheight=700)
|
||||
popup.set_window_options("can_close=0")
|
||||
popup.set_content(SSvote.interface(C))
|
||||
popup.open(0)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@@ -311,14 +450,71 @@ SUBSYSTEM_DEF(vote)
|
||||
. += "<h2>Vote: '[question]'</h2>"
|
||||
else
|
||||
. += "<h2>Vote: [capitalize(mode)]</h2>"
|
||||
. += "Time Left: [time_remaining] s<hr><ul>"
|
||||
for(var/i=1,i<=choices.len,i++)
|
||||
var/votes = choices[choices[i]]
|
||||
var/ivotedforthis = ((C.ckey in voted) && (voted[C.ckey] == i) ? TRUE : FALSE)
|
||||
if(!votes)
|
||||
votes = 0
|
||||
. += "<li>[ivotedforthis ? "<b>" : ""]<a href='?src=[REF(src)];vote=[i]'>[choices[i]]</a> ([obfuscated ? (admin ? "??? ([votes])" : "???") : votes] votes)[ivotedforthis ? "</b>" : ""]</li>" // CIT CHANGE - adds obfuscated votes
|
||||
. += "</ul><hr>"
|
||||
switch(vote_system)
|
||||
if(PLURALITY_VOTING)
|
||||
. += "<h3>Vote one.</h3>"
|
||||
if(APPROVAL_VOTING)
|
||||
. += "<h3>Vote any number of choices.</h3>"
|
||||
if(RANKED_CHOICE_VOTING)
|
||||
. += "<h3>Vote by order of preference. Revoting will demote to the bottom. 1 is your favorite, and higher numbers are worse.</h3>"
|
||||
if(SCORE_VOTING)
|
||||
. += "<h3>Grade the candidates by how much you like them.</h3>"
|
||||
. += "<h3>No-votes have no power--your opinion is only heard if you vote!</h3>"
|
||||
. += "Time Left: [DisplayTimeText(end_time-world.time)]<hr><ul>"
|
||||
switch(vote_system)
|
||||
if(PLURALITY_VOTING, APPROVAL_VOTING)
|
||||
for(var/i=1,i<=choices.len,i++)
|
||||
var/votes = choices[choices[i]]
|
||||
var/ivotedforthis = FALSE
|
||||
switch(vote_system)
|
||||
if(PLURALITY_VOTING)
|
||||
ivotedforthis = ((C.ckey in voted) && (voted[C.ckey] == i))
|
||||
if(APPROVAL_VOTING)
|
||||
ivotedforthis = ((C.ckey in voted) && (i in voted[C.ckey]))
|
||||
if(!votes)
|
||||
votes = 0
|
||||
. += "<li>[ivotedforthis ? "<b>" : ""]<a href='?src=[REF(src)];vote=[i]'>[choices[i]]</a> ([obfuscated ? (admin ? "??? ([votes])" : "???") : votes] votes)[ivotedforthis ? "</b>" : ""]</li>" // CIT CHANGE - adds obfuscated votes
|
||||
if(choice_descs.len >= i)
|
||||
. += "<li>[choice_descs[i]]</li>"
|
||||
. += "</ul><hr>"
|
||||
if(RANKED_CHOICE_VOTING)
|
||||
var/list/myvote = voted[C.ckey]
|
||||
for(var/i=1,i<=choices.len,i++)
|
||||
var/vote = (myvote ? (myvote.Find(i)) : 0)
|
||||
if(vote)
|
||||
. += "<li><b><a href='?src=[REF(src)];vote=[i]'>[choices[i]]</a> ([vote])</b></li>"
|
||||
else
|
||||
. += "<li><a href='?src=[REF(src)];vote=[i]'>[choices[i]]</a></li>"
|
||||
if(choice_descs.len >= i)
|
||||
. += "<li>[choice_descs[i]]</li>"
|
||||
. += "</ul><hr>"
|
||||
if(!(C.ckey in saved))
|
||||
. += "(<a href='?src=[REF(src)];vote=save'>Save vote</a>)"
|
||||
else
|
||||
. += "(Saved!)"
|
||||
. += "(<a href='?src=[REF(src)];vote=load'>Load vote from save</a>)"
|
||||
. += "(<a href='?src=[REF(src)];vote=reset'>Reset votes</a>)"
|
||||
if(SCORE_VOTING)
|
||||
var/list/myvote = voted[C.ckey]
|
||||
for(var/i=1,i<=choices.len,i++)
|
||||
. += "<li><b>[choices[i]]</b>"
|
||||
for(var/r in 1 to GLOB.vote_score_options.len)
|
||||
. += " <a href='?src=[REF(src)];vote=[i];score=[r]'>"
|
||||
if((choices[i] in myvote) && myvote[choices[i]] == r)
|
||||
. +="<b>([GLOB.vote_score_options[r]])</b>"
|
||||
else
|
||||
. +="[GLOB.vote_score_options[r]]"
|
||||
. += "</a>"
|
||||
. += "</li>"
|
||||
if(choice_descs.len >= i)
|
||||
. += "<li>[choice_descs[i]]</li>"
|
||||
. += "</ul><hr>"
|
||||
if(!(C.ckey in saved))
|
||||
. += "(<a href='?src=[REF(src)];vote=save'>Save vote</a>)"
|
||||
else
|
||||
. += "(Saved!)"
|
||||
. += "(<a href='?src=[REF(src)];vote=load'>Load vote from save</a>)"
|
||||
. += "(<a href='?src=[REF(src)];vote=reset'>Reset votes</a>)"
|
||||
if(admin)
|
||||
. += "(<a href='?src=[REF(src)];vote=cancel'>Cancel Vote</a>) "
|
||||
else
|
||||
@@ -376,8 +572,31 @@ SUBSYSTEM_DEF(vote)
|
||||
if("custom")
|
||||
if(usr.client.holder)
|
||||
initiate_vote("custom",usr.key)
|
||||
if("reset")
|
||||
if(usr.ckey in voted)
|
||||
voted -= usr.ckey
|
||||
if("save")
|
||||
if(usr.ckey in voted)
|
||||
if(!(usr.ckey in SSpersistence.saved_votes))
|
||||
SSpersistence.saved_votes[usr.ckey] = list()
|
||||
SSpersistence.saved_votes[usr.ckey][mode] = voted[usr.ckey]
|
||||
saved += usr.ckey
|
||||
if("load")
|
||||
if(!(usr.ckey in SSpersistence.saved_votes))
|
||||
SSpersistence.LoadSavedVote(usr.ckey)
|
||||
if(!(usr.ckey in SSpersistence.saved_votes))
|
||||
SSpersistence.saved_votes[usr.ckey] = list()
|
||||
if(usr.ckey in voted)
|
||||
SSpersistence.saved_votes[usr.ckey][mode] = voted[usr.ckey]
|
||||
else
|
||||
SSpersistence.saved_votes[usr.ckey][mode] = list()
|
||||
voted[usr.ckey] = SSpersistence.saved_votes[usr.ckey][mode]
|
||||
saved += usr.ckey
|
||||
else
|
||||
submit_vote(round(text2num(href_list["vote"])))
|
||||
if(vote_system == SCORE_VOTING)
|
||||
submit_vote(round(text2num(href_list["vote"])),round(text2num(href_list["score"])))
|
||||
else
|
||||
submit_vote(round(text2num(href_list["vote"])))
|
||||
usr.vote()
|
||||
|
||||
/datum/controller/subsystem/vote/proc/remove_action_buttons()
|
||||
@@ -392,7 +611,7 @@ SUBSYSTEM_DEF(vote)
|
||||
set category = "OOC"
|
||||
set name = "Vote"
|
||||
|
||||
var/datum/browser/popup = new(src, "vote", "Voting Panel")
|
||||
var/datum/browser/popup = new(src, "vote", "Voting Panel",nwidth=600,nheight=700)
|
||||
popup.set_window_options("can_close=0")
|
||||
popup.set_content(SSvote.interface(client))
|
||||
popup.open(0)
|
||||
@@ -419,6 +638,3 @@ SUBSYSTEM_DEF(vote)
|
||||
var/datum/player_details/P = GLOB.player_details[owner.ckey]
|
||||
if(P)
|
||||
P.player_actions -= src
|
||||
|
||||
#undef PEACE
|
||||
#undef CHAOS
|
||||
|
||||
@@ -11,8 +11,9 @@
|
||||
var/list/datum/nanite_program/programs = list()
|
||||
var/max_programs = NANITE_PROGRAM_LIMIT
|
||||
|
||||
var/stealth = FALSE //if TRUE, does not appear on HUDs and health scans, and does not display the program list on nanite scans
|
||||
|
||||
var/stealth = FALSE //if TRUE, does not appear on HUDs and health scans
|
||||
var/diagnostics = TRUE //if TRUE, displays program list when scanned by nanite scanners
|
||||
|
||||
/datum/component/nanites/Initialize(amount = 100, cloud = 0)
|
||||
if(!isliving(parent) && !istype(parent, /datum/nanite_cloud_backup))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
@@ -252,8 +253,8 @@
|
||||
to_chat(user, "<span class='info'>Cloud ID: [cloud_id ? cloud_id : "Disabled"]</span>")
|
||||
to_chat(user, "<span class='info'>================</span>")
|
||||
to_chat(user, "<span class='info'>Program List:</span>")
|
||||
if(stealth)
|
||||
to_chat(user, "<span class='alert'>%#$ENCRYPTED&^@</span>")
|
||||
if(!diagnostics)
|
||||
to_chat(user, "<span class='alert'>Diagnostics Disabled</span>")
|
||||
else
|
||||
for(var/X in programs)
|
||||
var/datum/nanite_program/NP = X
|
||||
|
||||
@@ -150,7 +150,9 @@
|
||||
if(!.)
|
||||
return
|
||||
ADD_TRAIT(H, TRAIT_NOGUNS, RISING_BASS_TRAIT)
|
||||
ADD_TRAIT(H, TRAIT_AUTO_CATCH_ITEM, RISING_BASS_TRAIT)
|
||||
|
||||
/datum/martial_art/the_rising_bass/on_remove(mob/living/carbon/human/H)
|
||||
. = ..()
|
||||
REMOVE_TRAIT(H, TRAIT_NOGUNS, RISING_BASS_TRAIT)
|
||||
REMOVE_TRAIT(H, TRAIT_NOGUNS, RISING_BASS_TRAIT)
|
||||
REMOVE_TRAIT(H, TRAIT_AUTO_CATCH_ITEM, RISING_BASS_TRAIT)
|
||||
@@ -292,6 +292,7 @@
|
||||
var/mob/living/carbon/C = owner
|
||||
for(var/X in C.bodyparts)
|
||||
var/obj/item/bodypart/BP = X
|
||||
BP.max_damage *= 10
|
||||
BP.brute_dam *= 10
|
||||
BP.burn_dam *= 10
|
||||
owner.toxloss *= 10
|
||||
@@ -377,6 +378,7 @@
|
||||
var/obj/item/bodypart/BP = X
|
||||
BP.brute_dam *= 0.1
|
||||
BP.burn_dam *= 0.1
|
||||
BP.max_damage /= 10
|
||||
owner.toxloss *= 0.1
|
||||
owner.oxyloss *= 0.1
|
||||
owner.cloneloss *= 0.1
|
||||
|
||||
+2
-2
@@ -47,7 +47,7 @@
|
||||
/atom/New(loc, ...)
|
||||
//atom creation method that preloads variables at creation
|
||||
if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New()
|
||||
GLOB._preloader.load(src)
|
||||
world.preloader_load(src)
|
||||
|
||||
if(datum_flags & DF_USE_TAG)
|
||||
GenerateTag()
|
||||
@@ -166,7 +166,7 @@
|
||||
|
||||
return FALSE
|
||||
|
||||
/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0)
|
||||
/atom/proc/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
|
||||
SEND_SIGNAL(src, COMSIG_ATOM_HULK_ATTACK, user)
|
||||
if(does_attack_animation)
|
||||
user.changeNext_move(CLICK_CD_MELEE)
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
#define CURRENT_LIVING_PLAYERS 1
|
||||
#define CURRENT_LIVING_ANTAGS 2
|
||||
#define CURRENT_DEAD_PLAYERS 3
|
||||
#define CURRENT_OBSERVERS 4
|
||||
|
||||
#define ONLY_RULESET 1
|
||||
#define HIGHLANDER_RULESET 2
|
||||
#define TRAITOR_RULESET 4
|
||||
#define MINOR_RULESET 8
|
||||
|
||||
#define RULESET_STOP_PROCESSING 1
|
||||
|
||||
// -- Injection delays
|
||||
GLOBAL_VAR_INIT(dynamic_latejoin_delay_min, (10 MINUTES))
|
||||
@@ -52,6 +41,8 @@ GLOBAL_LIST_EMPTY(dynamic_forced_roundstart_ruleset)
|
||||
// Forced threat level, setting this to zero or higher forces the roundstart threat to the value.
|
||||
GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
|
||||
GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
|
||||
|
||||
/datum/game_mode/dynamic
|
||||
name = "dynamic mode"
|
||||
config_tag = "dynamic"
|
||||
@@ -60,7 +51,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
announce_text = "Dynamic mode!" // This needs to be changed maybe
|
||||
|
||||
reroll_friendly = FALSE;
|
||||
|
||||
// Current storyteller
|
||||
var/datum/dynamic_storyteller/storyteller = null
|
||||
// Threat logging vars
|
||||
/// The "threat cap", threat shouldn't normally go above this and is used in ruleset calculations
|
||||
var/threat_level = 0
|
||||
@@ -164,6 +156,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
|
||||
dat += "Threat to Spend: <b>[threat]</b> <a href='?src=\ref[src];[HrefToken()];adjustthreat=1'>\[Adjust\]</A> <a href='?src=\ref[src];[HrefToken()];threatlog=1'>\[View Log\]</a><br/>"
|
||||
dat += "<br/>"
|
||||
dat += "Storyteller: <b>[storyteller.name]</b><br/>"
|
||||
dat += "Parameters: centre = [GLOB.dynamic_curve_centre] ; width = [GLOB.dynamic_curve_width].<br/>"
|
||||
dat += "<i>On average, <b>[peaceful_percentage]</b>% of the rounds are more peaceful.</i><br/>"
|
||||
dat += "Forced extended: <a href='?src=\ref[src];[HrefToken()];forced_extended=1'><b>[GLOB.dynamic_forced_extended ? "On" : "Off"]</b></a><br/>"
|
||||
@@ -178,7 +171,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
dat += "[DR.ruletype] - <b>[DR.name]</b><br>"
|
||||
else
|
||||
dat += "none.<br>"
|
||||
dat += "<br>Injection Timers: (<b>[get_injection_chance(TRUE)]%</b> chance)<BR>"
|
||||
dat += "<br>Injection Timers: (<b>[storyteller.get_injection_chance(TRUE)]%</b> chance)<BR>"
|
||||
dat += "Latejoin: [(latejoin_injection_cooldown-world.time)>60*10 ? "[round((latejoin_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(latejoin_injection_cooldown-world.time)] seconds"] <a href='?src=\ref[src];[HrefToken()];injectlate=1'>\[Now!\]</a><BR>"
|
||||
dat += "Midround: [(midround_injection_cooldown-world.time)>60*10 ? "[round((midround_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(midround_injection_cooldown-world.time)] seconds"] <a href='?src=\ref[src];[HrefToken()];injectmid=1'>\[Now!\]</a><BR>"
|
||||
dat += "Event: [(event_injection_cooldown-world.time)>60*10 ? "[round((event_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(event_injection_cooldown-world.time)] seconds"] <a href='?src=\ref[src];[HrefToken()];forceevent=1'>\[Now!\]</a><BR>"
|
||||
@@ -336,6 +329,9 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",peaceful_percentage,"Percent of same-vote rounds that are more peaceful")
|
||||
|
||||
/datum/game_mode/dynamic/can_start()
|
||||
storyteller = new GLOB.dynamic_storyteller_type // this is where all the initialization happens
|
||||
storyteller.on_start()
|
||||
SSblackbox.record_feedback("text","dynamic_storyteller",1,storyteller.name)
|
||||
message_admins("Dynamic mode parameters for the round:")
|
||||
message_admins("Centre is [GLOB.dynamic_curve_centre], Width is [GLOB.dynamic_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].")
|
||||
message_admins("Stacking limit is [GLOB.dynamic_stacking_limit], Classic secret is [GLOB.dynamic_classic_secret ? "Enabled" : "Disabled"], High population limit is [GLOB.dynamic_high_pop_limit].")
|
||||
@@ -345,19 +341,12 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
if(GLOB.dynamic_forced_threat_level >= 0)
|
||||
threat_level = round(GLOB.dynamic_forced_threat_level, 0.1)
|
||||
threat = threat_level
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Threat level (forced by admins)")
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Threat level (forced)")
|
||||
else
|
||||
generate_threat()
|
||||
|
||||
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_latejoin_delay_max + GLOB.dynamic_first_latejoin_delay_min)
|
||||
latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_first_latejoin_delay_min, GLOB.dynamic_first_latejoin_delay_max)) + world.time
|
||||
storyteller.start_injection_cooldowns()
|
||||
|
||||
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_midround_delay_min + GLOB.dynamic_first_midround_delay_max)
|
||||
midround_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_first_midround_delay_min, GLOB.dynamic_first_midround_delay_max)) + world.time
|
||||
|
||||
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
|
||||
event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time)
|
||||
|
||||
log_game("DYNAMIC: Dynamic Mode initialized with a Threat Level of... [threat_level]!")
|
||||
initial_threat_level = threat_level
|
||||
return TRUE
|
||||
@@ -391,7 +380,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
log_game("DYNAMIC: [roundstart_rules.len] rules.")
|
||||
return TRUE
|
||||
SSblackbox.record_feedback("tally","dynamic",roundstart_rules.len,"Roundstart rules considered")
|
||||
SSblackbox.record_feedback("tally","dynamic",roundstart_rules.len,"Players readied up")
|
||||
SSblackbox.record_feedback("tally","dynamic",roundstart_pop_ready,"Players readied up")
|
||||
if(GLOB.dynamic_forced_roundstart_ruleset.len > 0)
|
||||
rigged_roundstart()
|
||||
else
|
||||
@@ -429,13 +418,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
if (GLOB.dynamic_forced_extended)
|
||||
log_game("DYNAMIC: Starting a round of forced extended.")
|
||||
return TRUE
|
||||
var/list/drafted_rules = list()
|
||||
for (var/datum/dynamic_ruleset/roundstart/rule in roundstart_rules)
|
||||
if (rule.acceptable(roundstart_pop_ready, threat_level) && threat >= rule.cost) // If we got the population and threat required
|
||||
rule.candidates = candidates.Copy()
|
||||
rule.trim_candidates()
|
||||
if (rule.ready() && rule.candidates.len > 0)
|
||||
drafted_rules[rule] = rule.weight
|
||||
var/list/drafted_rules = storyteller.roundstart_draft()
|
||||
if(!drafted_rules.len)
|
||||
message_admins("Not enough threat level for roundstart antags!")
|
||||
log_game("DYNAMIC: Not enough threat level for roundstart antags!")
|
||||
@@ -653,7 +636,6 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
if (rule.persistent)
|
||||
current_rules += rule
|
||||
return TRUE
|
||||
rule.clean_up()
|
||||
stack_trace("The [rule.ruletype] rule \"[rule.name]\" failed to execute.")
|
||||
return FALSE
|
||||
|
||||
@@ -667,62 +649,41 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
current_rules -= rule
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Rulesets finished")
|
||||
SSblackbox.record_feedback("associative","dynamic_rulesets_finished",1,rule.get_blackbox_info())
|
||||
|
||||
|
||||
storyteller.do_process()
|
||||
|
||||
if (midround_injection_cooldown < world.time)
|
||||
if (GLOB.dynamic_forced_extended)
|
||||
return
|
||||
|
||||
// Somehow it managed to trigger midround multiple times so this was moved here.
|
||||
// There is no way this should be able to trigger an injection twice now.
|
||||
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min)
|
||||
midround_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_midround_delay_min, GLOB.dynamic_midround_delay_max)) + world.time)
|
||||
midround_injection_cooldown = storyteller.get_midround_cooldown() + world.time
|
||||
|
||||
// Time to inject some threat into the round
|
||||
if(EMERGENCY_ESCAPED_OR_ENDGAMED) // Unless the shuttle is gone
|
||||
return
|
||||
if((world.realtime - SSshuttle.realtimeofstart) > SSshuttle.auto_call) // no rules after shuttle is auto-called
|
||||
return
|
||||
message_admins("DYNAMIC: Checking for midround injection.")
|
||||
log_game("DYNAMIC: Checking for midround injection.")
|
||||
|
||||
update_playercounts()
|
||||
if (get_injection_chance())
|
||||
if (prob(storyteller.get_injection_chance()))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Attempted midround injections")
|
||||
var/cur_threat_frac = threat/threat_level
|
||||
var/list/drafted_rules = list()
|
||||
var/antag_num = current_players[CURRENT_LIVING_ANTAGS].len
|
||||
for (var/datum/dynamic_ruleset/midround/rule in midround_rules)
|
||||
// if there are antags OR the rule is an antag rule, antag_acceptable will be true.
|
||||
if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost)
|
||||
// Classic secret : only autotraitor/minor roles
|
||||
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
|
||||
continue
|
||||
rule.trim_candidates()
|
||||
if (rule.ready())
|
||||
if(!antag_num)
|
||||
drafted_rules[rule] = round(rule.get_weight() + (rule.cost * cur_threat_frac))
|
||||
else
|
||||
drafted_rules[rule] = rule.get_weight()
|
||||
else if(threat < rule.cost)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
|
||||
var/list/drafted_rules = storyteller.midround_draft()
|
||||
if (drafted_rules.len > 0)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Successful midround injections")
|
||||
picking_midround_latejoin_rule(drafted_rules)
|
||||
else
|
||||
midround_injection_cooldown = (midround_injection_cooldown + world.time)/2
|
||||
// get_injection_chance can do things on fail
|
||||
|
||||
if(event_injection_cooldown < world.time)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Attempted event injections")
|
||||
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
|
||||
event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time)
|
||||
event_injection_cooldown = storyteller.get_event_cooldown() + world.time
|
||||
message_admins("DYNAMIC: Doing event injection.")
|
||||
log_game("DYNAMIC: Doing event injection.")
|
||||
update_playercounts()
|
||||
var/list/drafted_rules = list()
|
||||
for(var/datum/dynamic_ruleset/event/rule in events)
|
||||
if(rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost)
|
||||
if(rule.ready())
|
||||
drafted_rules[rule] = rule.get_weight()
|
||||
else if(threat < rule.cost)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
|
||||
var/list/drafted_rules = storyteller.event_draft()
|
||||
if(drafted_rules.len > 0)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Successful event injections")
|
||||
picking_midround_latejoin_rule(drafted_rules)
|
||||
@@ -748,31 +709,6 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
continue
|
||||
current_players[CURRENT_DEAD_PLAYERS].Add(M) // Players who actually died (and admins who ghosted, would be nice to avoid counting them somehow)
|
||||
|
||||
/// Gets the chance for latejoin and midround injection, the dry_run argument is only used for forced injection.
|
||||
/datum/game_mode/dynamic/proc/get_injection_chance(dry_run = FALSE)
|
||||
if(forced_injection)
|
||||
forced_injection = !dry_run
|
||||
return 100
|
||||
var/chance = 0
|
||||
// If the high pop override is in effect, we reduce the impact of population on the antag injection chance
|
||||
var/high_pop_factor = (current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit)
|
||||
var/max_pop_per_antag = max(5,15 - round(threat_level/10) - round(current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5)))
|
||||
if (!current_players[CURRENT_LIVING_ANTAGS].len)
|
||||
chance += 80 // No antags at all? let's boost those odds!
|
||||
else
|
||||
var/current_pop_per_antag = current_players[CURRENT_LIVING_PLAYERS].len / current_players[CURRENT_LIVING_ANTAGS].len
|
||||
if (current_pop_per_antag > max_pop_per_antag)
|
||||
chance += min(50, 25+10*(current_pop_per_antag-max_pop_per_antag))
|
||||
else
|
||||
chance += 25-10*(max_pop_per_antag-current_pop_per_antag)
|
||||
if (current_players[CURRENT_DEAD_PLAYERS].len > current_players[CURRENT_LIVING_PLAYERS].len)
|
||||
chance -= 30 // More than half the crew died? ew, let's calm down on antags
|
||||
if (threat > 70)
|
||||
chance += 15
|
||||
if (threat < 30)
|
||||
chance -= 15
|
||||
return round(max(0,chance))
|
||||
|
||||
/// Removes type from the list
|
||||
/datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type)
|
||||
for(var/I in type_list)
|
||||
@@ -803,7 +739,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
return
|
||||
if(EMERGENCY_ESCAPED_OR_ENDGAMED) // No more rules after the shuttle has left
|
||||
return
|
||||
|
||||
if((world.realtime - SSshuttle.realtimeofstart) > SSshuttle.auto_call) // no rules after shuttle is auto-called
|
||||
return
|
||||
update_playercounts()
|
||||
|
||||
if (forced_latejoin_rule)
|
||||
@@ -814,28 +751,12 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
picking_midround_latejoin_rule(list(forced_latejoin_rule), forced = TRUE)
|
||||
forced_latejoin_rule = null
|
||||
|
||||
else if (latejoin_injection_cooldown < world.time && prob(get_injection_chance()))
|
||||
else if (latejoin_injection_cooldown < world.time && prob(storyteller.get_injection_chance()))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Attempted latejoin injections")
|
||||
var/list/drafted_rules = list()
|
||||
for (var/datum/dynamic_ruleset/latejoin/rule in latejoin_rules)
|
||||
if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost)
|
||||
// Classic secret : only autotraitor/minor roles
|
||||
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
|
||||
continue
|
||||
// No stacking : only one round-ender, unless threat level > stacking_limit.
|
||||
if (threat_level > GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
|
||||
if(rule.flags & HIGHLANDER_RULESET && highlander_executed)
|
||||
continue
|
||||
|
||||
rule.candidates = list(newPlayer)
|
||||
rule.trim_candidates()
|
||||
if (rule.ready())
|
||||
drafted_rules[rule] = rule.get_weight()
|
||||
|
||||
var/list/drafted_rules = storyteller.latejoin_draft(newPlayer)
|
||||
if (drafted_rules.len > 0 && picking_midround_latejoin_rule(drafted_rules))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Successful latejoin injections")
|
||||
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min)
|
||||
latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max)) + world.time
|
||||
latejoin_injection_cooldown = storyteller.get_latejoin_cooldown() + world.time
|
||||
|
||||
/// Refund threat, but no more than threat_level.
|
||||
/datum/game_mode/dynamic/proc/refund_threat(regain)
|
||||
|
||||
@@ -80,9 +80,13 @@
|
||||
/// Delay for when execute will get called from the time of post_setup (roundstart) or process (midround/latejoin).
|
||||
/// Make sure your ruleset works with execute being called during the game when using this, and that the clean_up proc reverts it properly in case of faliure.
|
||||
var/delay = 0
|
||||
/// List of tags for use in storytellers.
|
||||
var/list/property_weights = list()
|
||||
/// Whether or not recent-round weight values are taken into account for this ruleset.
|
||||
/// Weight reduction uses the same values as secret's recent-round mode weight reduction.
|
||||
var/always_max_weight = FALSE
|
||||
/// Weight reduction by recent-rounds. Saved on new.
|
||||
var/weight_mult = 1
|
||||
|
||||
/datum/dynamic_ruleset/New()
|
||||
..()
|
||||
@@ -96,13 +100,11 @@
|
||||
var/high_population_requirements = CONFIG_GET(keyed_list/dynamic_high_population_requirement)
|
||||
var/list/repeated_mode_adjust = CONFIG_GET(number_list/repeated_mode_adjust)
|
||||
if(config_tag in weights)
|
||||
var/weight_mult = 1
|
||||
if(!always_max_weight && SSpersistence.saved_dynamic_rules.len == 3 && repeated_mode_adjust.len == 3)
|
||||
var/saved_dynamic_rules = SSpersistence.saved_dynamic_rules
|
||||
for(var/i in 1 to 3)
|
||||
if(config_tag in saved_dynamic_rules[i])
|
||||
weight_mult -= (repeated_mode_adjust[i]/100)
|
||||
weight = weights[config_tag] * weight_mult
|
||||
if(config_tag in costs)
|
||||
cost = costs[config_tag]
|
||||
if(config_tag in requirementses)
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
cost = 10
|
||||
blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear)
|
||||
requirements = list(70,60,50,50,40,40,40,30,20,15)
|
||||
property_weights = list("story_potential" = 1, "trust" = 1, "chaos" = 1)
|
||||
high_population_requirement = 15
|
||||
|
||||
/datum/dynamic_ruleset/event/pirates/ready(forced = FALSE)
|
||||
@@ -81,6 +82,7 @@
|
||||
cost = 10
|
||||
requirements = list(70,60,50,50,40,40,40,30,20,15)
|
||||
high_population_requirement = 15
|
||||
property_weights = list("chaos" = 1, "valid" = 1)
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
@@ -100,6 +102,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5) // yes, can happen on fake-extended
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("chaos" = 1, "extended" = 2)
|
||||
|
||||
/datum/dynamic_ruleset/event/ventclog/ready()
|
||||
if(mode.threat_level > 30 && mode.threat >= 5 && prob(20))
|
||||
@@ -133,10 +136,11 @@
|
||||
required_enemies = list(1,1,0,0,0,0,0,0,0,0)
|
||||
weight = 4
|
||||
// no repeatable weight decrease. too variable to be unfun multiple times in one round
|
||||
cost = 3
|
||||
cost = 1
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("story_potential" = 1, "extended" = 1)
|
||||
always_max_weight = TRUE
|
||||
|
||||
//////////////////////////////////////////////
|
||||
@@ -156,6 +160,7 @@
|
||||
repeatable_weight_decrease = 2
|
||||
requirements = list(60,50,40,30,30,30,30,30,30,30)
|
||||
high_population_requirement = 30
|
||||
property_weights = list("extended" = -2)
|
||||
|
||||
/datum/dynamic_ruleset/event/meteor_wave/ready()
|
||||
if(mode.threat_level > 40 && mode.threat >= 25 && prob(20))
|
||||
@@ -190,6 +195,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/anomaly_flux
|
||||
name = "Anomaly: Hyper-Energetic Flux"
|
||||
@@ -203,6 +209,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 10
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/anomaly_gravitational
|
||||
name = "Anomaly: Gravitational"
|
||||
@@ -214,6 +221,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/anomaly_pyroclastic
|
||||
name = "Anomaly: Pyroclastic"
|
||||
@@ -227,6 +235,7 @@
|
||||
requirements = list(10,10,10,10,10,10,10,10,10,10)
|
||||
high_population_requirement = 10
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/anomaly_vortex
|
||||
name = "Anomaly: Vortex"
|
||||
@@ -240,6 +249,7 @@
|
||||
requirements = list(10,10,10,10,10,10,10,10,10,10)
|
||||
high_population_requirement = 10
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
@@ -259,6 +269,7 @@
|
||||
requirements = list(10,10,10,10,10,10,10,10,10,10)
|
||||
high_population_requirement = 10
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = -1, "chaos" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/carp_migration
|
||||
name = "Carp Migration"
|
||||
@@ -270,6 +281,7 @@
|
||||
requirements = list(10,10,10,10,10,10,10,10,10,10)
|
||||
high_population_requirement = 10
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/communications_blackout
|
||||
name = "Communications Blackout"
|
||||
@@ -283,6 +295,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1, "chaos" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/processor_overload
|
||||
name = "Processor Overload"
|
||||
@@ -296,6 +309,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1, "chaos" = 1)
|
||||
always_max_weight = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/event/space_dust
|
||||
@@ -310,6 +324,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
always_max_weight = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/event/major_dust
|
||||
@@ -324,6 +339,7 @@
|
||||
requirements = list(10,10,10,10,10,10,10,10,10,10)
|
||||
high_population_requirement = 10
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/electrical_storm
|
||||
name = "Electrical Storm"
|
||||
@@ -337,6 +353,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/heart_attack
|
||||
name = "Random Heart Attack"
|
||||
@@ -350,6 +367,7 @@
|
||||
requirements = list(101,101,101,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
always_max_weight = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/event/radiation_storm
|
||||
@@ -362,3 +380,4 @@
|
||||
required_enemies = list(1,1,1,1,1,1,1,1,1,1)
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
property_weights = list("extended" = 1,"chaos" = 1)
|
||||
|
||||
@@ -69,8 +69,15 @@
|
||||
high_population_requirement = 15
|
||||
repeatable = TRUE
|
||||
flags = TRAITOR_RULESET
|
||||
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1)
|
||||
always_max_weight = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/infiltrator/execute()
|
||||
. = ..()
|
||||
for(var/datum/mind/M in assigned)
|
||||
log_admin("[M.name] was made into a traitor by dynamic.")
|
||||
message_admins("[M.name] was made into a traitor by dynamic.")
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// REVOLUTIONARY PROVOCATEUR //
|
||||
@@ -94,6 +101,7 @@
|
||||
requirements = list(101,101,70,40,40,40,40,40,40,40)
|
||||
high_population_requirement = 40
|
||||
flags = HIGHLANDER_RULESET
|
||||
property_weights = list("trust" = -2, "chaos" = 2, "extended" = -2, "valid" = 2, "conversion" = 1)
|
||||
var/required_heads_of_staff = 3
|
||||
var/finished = FALSE
|
||||
var/datum/team/revolution/revolution
|
||||
@@ -123,6 +131,8 @@
|
||||
revolution.update_objectives()
|
||||
revolution.update_heads()
|
||||
SSshuttle.registerHostileEnvironment(src)
|
||||
log_admin("[M.name] was made into a revolutionary by dynamic.")
|
||||
message_admins("[M.name] was made into a revolutionary by dynamic.")
|
||||
return TRUE
|
||||
else
|
||||
log_game("DYNAMIC: [ruletype] [name] discarded [M.name] from head revolutionary due to ineligibility.")
|
||||
@@ -187,30 +197,31 @@
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// VAMPIRE //
|
||||
// BLOODSUCKERS //
|
||||
// //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
/datum/dynamic_ruleset/latejoin/vampire
|
||||
name = "vampire"
|
||||
config_tag = "vampire_latejoin"
|
||||
antag_flag = ROLE_VAMPIRE
|
||||
antag_datum = ANTAG_DATUM_VAMPIRE
|
||||
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain")
|
||||
/datum/dynamic_ruleset/latejoin/bloodsucker
|
||||
name = "Bloodsucker Infiltrator"
|
||||
config_tag = "latejoin_bloodsucker"
|
||||
antag_datum = ANTAG_DATUM_BLOODSUCKER
|
||||
antag_flag = ROLE_TRAITOR
|
||||
restricted_roles = list("AI", "Cyborg")
|
||||
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
|
||||
required_candidates = 1
|
||||
weight = 5
|
||||
cost = 15
|
||||
requirements = list(80,70,60,50,40,20,20,15,15,15)
|
||||
weight = 3
|
||||
cost = 10
|
||||
property_weights = list("story_potential" = 2, "extended" = 2, "trust" = -2, "valid" = 1)
|
||||
requirements = list(70,65,60,55,50,45,40,35,30,30)
|
||||
high_population_requirement = 30
|
||||
repeatable = TRUE
|
||||
high_population_requirement = 15
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/vampire/pre_execute()
|
||||
/datum/dynamic_ruleset/latejoin/bloodsucker/execute()
|
||||
var/mob/M = pick(candidates)
|
||||
candidates -= M
|
||||
assigned += M.mind
|
||||
M.mind.restricted_roles = restricted_roles
|
||||
M.mind.special_role = ROLE_VAMPIRE
|
||||
M.mind.special_role = antag_flag
|
||||
if(mode.make_bloodsucker(M.mind))
|
||||
mode.bloodsuckers += M
|
||||
log_admin("[M.name] was made into a bloodsucker by dynamic.")
|
||||
message_admins("[M.name] was made into a bloodsucker by dynamic.")
|
||||
return TRUE
|
||||
*/
|
||||
|
||||
@@ -108,8 +108,12 @@
|
||||
|
||||
candidates = pollGhostCandidates("The mode is looking for volunteers to become a [name]", antag_flag, SSticker.mode, antag_flag, poll_time = 300)
|
||||
|
||||
if(!candidates || candidates.len <= required_candidates)
|
||||
if(!candidates || candidates.len < required_candidates)
|
||||
message_admins("The ruleset [name] did not receive enough applications.")
|
||||
if(candidates)
|
||||
message_admins("Only received [candidates.len], needed [required_candidates].")
|
||||
else
|
||||
message_admins("There were no candidates.")
|
||||
log_game("DYNAMIC: The ruleset [name] did not receive enough applications.")
|
||||
return FALSE
|
||||
|
||||
@@ -180,6 +184,7 @@
|
||||
repeatable = TRUE
|
||||
high_population_requirement = 15
|
||||
flags = TRAITOR_RULESET
|
||||
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1)
|
||||
always_max_weight = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/midround/autotraitor/acceptable(population = 0, threat = 0)
|
||||
@@ -214,6 +219,8 @@
|
||||
living_players -= M
|
||||
var/datum/antagonist/traitor/newTraitor = new
|
||||
M.mind.add_antag_datum(newTraitor)
|
||||
log_admin("[M] was made into a traitor by dynamic.")
|
||||
message_admins("[M] was made into a traitor by dynamic.")
|
||||
return TRUE
|
||||
|
||||
|
||||
@@ -237,6 +244,7 @@
|
||||
requirements = list(101,101,70,50,50,50,40,30,30,30)
|
||||
high_population_requirement = 30
|
||||
required_type = /mob/living/silicon/ai
|
||||
property_weights = list("story_potential" = 2, "trust" = 1, "chaos" = 2)
|
||||
var/ion_announce = 33
|
||||
var/removeDontImproveChance = 10
|
||||
|
||||
@@ -261,6 +269,8 @@
|
||||
var/datum/antagonist/traitor/AI = new
|
||||
M.mind.special_role = antag_flag
|
||||
M.mind.add_antag_datum(AI)
|
||||
log_admin("[M] was made into a malf AI by dynamic.")
|
||||
message_admins("[M] was made into a malf AI by dynamic.")
|
||||
if(prob(ion_announce))
|
||||
priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", "ionstorm")
|
||||
if(prob(removeDontImproveChance))
|
||||
@@ -289,6 +299,7 @@
|
||||
requirements = list(90,90,70,50,50,50,50,40,30,30)
|
||||
high_population_requirement = 30
|
||||
repeatable = TRUE
|
||||
property_weights = list("story_potential" = 2, "trust" = 1, "chaos" = 2, "extended" = -2)
|
||||
var/datum/mind/wizard
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/wizard/ready(forced = FALSE)
|
||||
@@ -337,6 +348,7 @@
|
||||
cost = 35
|
||||
requirements = list(90,90,90,80,70,60,50,40,40,40)
|
||||
high_population_requirement = 40
|
||||
property_weights = list("story_potential" = 2, "trust" = 2, "chaos" = 2, "extended" = -2, "valid" = 2)
|
||||
var/operative_cap = list(2,2,3,3,4,5,5,5,5,5)
|
||||
var/datum/team/nuclear/nuke_team
|
||||
flags = HIGHLANDER_RULESET
|
||||
@@ -390,6 +402,7 @@
|
||||
requirements = list(101,101,101,80,60,50,50,50,50,50)
|
||||
high_population_requirement = 50
|
||||
repeatable = TRUE
|
||||
property_weights = list("story_potential" = -1, "trust" = 2, "chaos" = 2, "extended" = -2, "valid" = 2)
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/blob/ready(forced = FALSE)
|
||||
if (required_candidates > (dead_players.len + list_observers.len))
|
||||
@@ -421,6 +434,7 @@
|
||||
high_population_requirement = 50
|
||||
repeatable_weight_decrease = 2
|
||||
repeatable = TRUE
|
||||
property_weights = list("story_potential" = -1, "trust" = 1, "chaos" = 2, "extended" = -2, "valid" = 2)
|
||||
var/list/vents = list()
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/ready(forced = FALSE)
|
||||
@@ -476,6 +490,7 @@
|
||||
high_population_requirement = 50
|
||||
repeatable_weight_decrease = 2
|
||||
repeatable = TRUE
|
||||
property_weights = list("story_potential" = 1, "trust" = 1, "extended" = 1, "valid" = 2, "integrity" = 2)
|
||||
var/list/spawn_locs = list()
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/nightmare/execute()
|
||||
@@ -521,6 +536,7 @@
|
||||
weight = 4
|
||||
cost = 5
|
||||
requirements = list(30,30,20,20,15,10,10,10,10,5) // yes, it can even happen in "extended"!
|
||||
property_weights = list("story_potential" = 1, "extended" = 1, "valid" = -2)
|
||||
high_population_requirement = 5
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease/ready(forced = FALSE)
|
||||
@@ -555,14 +571,18 @@
|
||||
cost = 5
|
||||
requirements = list(30,30,30,30,20,15,15,15,15,15)
|
||||
high_population_requirement = 15
|
||||
property_weights = list("story_potential" = -2, "extended" = -1)
|
||||
var/list/spawn_locs = list()
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/revenant/ready(forced = FALSE)
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/revenant/acceptable(population = 0,threat = 0)
|
||||
var/deadMobs = 0
|
||||
for(var/mob/M in GLOB.dead_mob_list)
|
||||
deadMobs++
|
||||
if(deadMobs < REVENANT_SPAWN_THRESHOLD)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/revenant/ready(forced = FALSE)
|
||||
if(required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
@@ -607,6 +627,7 @@
|
||||
weight = 4
|
||||
cost = 15
|
||||
requirements = list(101,101,101,90,80,70,60,50,40,30)
|
||||
property_weights = list("story_potential" = -2, "extended" = -2, "integrity" = 2, "valid" = 2, "trust" = 2)
|
||||
high_population_requirement = 30
|
||||
var/list/spawn_locs = list()
|
||||
|
||||
@@ -659,6 +680,7 @@
|
||||
blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear)
|
||||
high_population_requirement = 15
|
||||
var/datum/team/abductor_team/team
|
||||
property_weights = list("story_potential" = 1, "extended" = -2, "valid" = 1, "trust" = -1, "chaos" = 2)
|
||||
repeatable_weight_decrease = 4
|
||||
repeatable = TRUE
|
||||
|
||||
@@ -699,6 +721,7 @@
|
||||
cost = 15
|
||||
requirements = list(101,101,101,90,80,70,60,50,40,30)
|
||||
high_population_requirement = 30
|
||||
property_weights = list("story_potential" = 1, "extended" = -2, "valid" = 2)
|
||||
var/list/spawn_locs = list()
|
||||
var/spawn_loc
|
||||
|
||||
@@ -745,31 +768,3 @@
|
||||
|
||||
#undef ABDUCTOR_MAX_TEAMS
|
||||
#undef REVENANT_SPAWN_THRESHOLD
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// BLOODSUCKERS //
|
||||
// //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/bloodsucker
|
||||
name = "Bloodsucker Infiltrator"
|
||||
config_tag = "latejoin_bloodsucker"
|
||||
antag_datum = ANTAG_DATUM_BLOODSUCKER
|
||||
antag_flag = ROLE_TRAITOR
|
||||
restricted_roles = list("AI", "Cyborg")
|
||||
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
|
||||
required_candidates = 1
|
||||
weight = 3
|
||||
cost = 10
|
||||
requirements = list(90,80,70,60,55,50,45,40,35,30)
|
||||
high_population_requirement = 30
|
||||
repeatable = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/bloodsucker/execute()
|
||||
var/mob/M = pick(candidates)
|
||||
assigned += M.mind
|
||||
M.mind.special_role = antag_flag
|
||||
if(mode.make_bloodsucker(M.mind))
|
||||
mode.bloodsuckers += M
|
||||
return TRUE
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
requirements = list(50,50,50,50,50,50,50,50,50,50)
|
||||
high_population_requirement = 40
|
||||
antag_cap = list(1,1,1,1,2,2,2,2,3,3)
|
||||
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1, "valid" = 1)
|
||||
always_max_weight = TRUE
|
||||
var/autotraitor_cooldown = 450 // 15 minutes (ticks once per 2 sec)
|
||||
|
||||
@@ -61,6 +62,7 @@
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
high_population_requirement = 101
|
||||
antag_cap = list(2,2,2,2,2,2,2,2,2,2) // Can pick 3 per team, but rare enough it doesn't matter.
|
||||
property_weights = list("story_potential" = 1, "trust" = -1, "extended" = 1, "valid" = 1)
|
||||
var/list/datum/team/brother_team/pre_brother_teams = list()
|
||||
var/const/min_team_size = 2
|
||||
|
||||
@@ -108,6 +110,7 @@
|
||||
cost = 15
|
||||
scaling_cost = 15
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
property_weights = list("trust" = -2, "valid" = 2)
|
||||
high_population_requirement = 10
|
||||
antag_cap = list(1,1,1,1,1,2,2,2,2,3)
|
||||
var/team_mode_probability = 30
|
||||
@@ -160,6 +163,7 @@
|
||||
cost = 30
|
||||
requirements = list(101,101,101,60,50,50,50,50,50,50)
|
||||
high_population_requirement = 50
|
||||
property_weights = list("story_potential" = 2, "trust" = 1, "chaos" = 2, "extended" = -2, "valid" = 2)
|
||||
var/list/roundstart_wizards = list()
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/wizard/acceptable(population=0, threat=0)
|
||||
@@ -222,6 +226,7 @@
|
||||
weight = 3
|
||||
cost = 30
|
||||
requirements = list(101,101,101,80,70,60,50,50,50,50)
|
||||
property_weights = list("story_potential" = -1, "trust" = -1, "chaos" = 1, "conversion" = 1, "extended" = -2, "valid" = 2)
|
||||
high_population_requirement = 50
|
||||
flags = HIGHLANDER_RULESET
|
||||
antag_cap = list(2,2,2,3,3,4,4,4,4,4)
|
||||
@@ -283,6 +288,7 @@
|
||||
high_population_requirement = 50
|
||||
flags = HIGHLANDER_RULESET
|
||||
antag_cap = list(1,1,2,3,4,5,5,5,5,5)
|
||||
property_weights = list("story_potential" = 2, "trust" = 2, "chaos" = 2, "extended" = -2, "valid" = 2)
|
||||
var/datum/team/nuclear/nuke_team
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/nuclear/ready(forced = FALSE)
|
||||
@@ -373,6 +379,7 @@
|
||||
flags = HIGHLANDER_RULESET
|
||||
// I give up, just there should be enough heads with 35 players...
|
||||
minimum_players = 35
|
||||
property_weights = list("trust" = -2, "chaos" = 2, "extended" = -2, "valid" = 2, "conversion" = 1)
|
||||
var/datum/team/revolution/revolution
|
||||
var/finished = FALSE
|
||||
|
||||
@@ -490,6 +497,7 @@
|
||||
weight = 3
|
||||
cost = 0
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
property_weights = list("extended" = 2)
|
||||
high_population_requirement = 101
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/extended/pre_execute()
|
||||
@@ -517,6 +525,7 @@
|
||||
high_population_requirement = 50
|
||||
flags = HIGHLANDER_RULESET
|
||||
antag_cap = list(2,3,3,4,4,4,4,4,4,4)
|
||||
property_weights = list("trust" = 2, "chaos" = 2, "extended" = -2, "conversion" = 1, "valid" = 2)
|
||||
var/ark_time
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/clockcult/pre_execute()
|
||||
@@ -616,6 +625,8 @@
|
||||
antag_leader_datum = /datum/antagonist/nukeop/leader/clownop
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
high_population_requirement = 101
|
||||
property_weights = list("trust" = 2, "chaos" = 2, "extended" = -2, "story_potential" = 2, "valid" = 2)
|
||||
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/nuclear/clown_ops/pre_execute()
|
||||
. = ..()
|
||||
@@ -647,6 +658,7 @@
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
high_population_requirement = 101
|
||||
antag_cap = list(1,1,1,2,2,2,3,3,3,4)
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/devil/pre_execute()
|
||||
var/num_devils = antag_cap[indice_pop]
|
||||
@@ -698,6 +710,7 @@
|
||||
cost = 0
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
high_population_requirement = 101
|
||||
property_weights = list("extended" = -2, "chaos" = 2, "conversion" = 1, "valid" = 2)
|
||||
var/players_per_carrier = 30
|
||||
var/monkeys_to_win = 1
|
||||
var/escaped_monkeys = 0
|
||||
@@ -759,6 +772,7 @@
|
||||
cost = 0
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
high_population_requirement = 101
|
||||
property_weights = list("extended" = -2, "chaos" = 2, "trust" = 2)
|
||||
var/meteordelay = 2000
|
||||
var/nometeors = 0
|
||||
var/rampupdelta = 5
|
||||
@@ -799,7 +813,8 @@
|
||||
weight = 2
|
||||
cost = 15
|
||||
scaling_cost = 10
|
||||
requirements = list(90,80,70,60,50,50,50,50,50,50)
|
||||
property_weights = list("story_potential" = 1, "extended" = 1, "trust" = -2, "valid" = 1)
|
||||
requirements = list(70,65,60,55,50,50,50,50,50,50)
|
||||
high_population_requirement = 50
|
||||
antag_cap = list(1,1,1,1,1,2,2,2,2,2)
|
||||
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
/datum/dynamic_storyteller
|
||||
var/name = "none"
|
||||
var/desc = "A coder's idiocy."
|
||||
var/list/property_weights = list()
|
||||
var/curve_centre = 0
|
||||
var/curve_width = 1.8
|
||||
var/forced_threat_level = -1
|
||||
var/flags = 0
|
||||
var/weight = 3 // how many rounds need to have been recently played for this storyteller to be left out of the vote
|
||||
var/datum/game_mode/dynamic/mode = null
|
||||
|
||||
/**
|
||||
Property weights are:
|
||||
"story_potential" -- essentially how many different ways the antag can be played.
|
||||
"trust" -- How much it makes the crew trust each other. Negative values means they're suspicious. Team antags are like this.
|
||||
"chaos" -- How chaotic it makes the round. Has some overlap with "valid" and somewhat contradicts "extended".
|
||||
"valid" -- How likely the non-antag-enemy crew are to get involved, e.g. nukies encouraging the warden to
|
||||
let everyone into the armory, wizard moving around and being a nuisance, nightmare busting lights.
|
||||
"extended" -- How much the antag is conducive to a long round. Nukies and cults are bad for this; Wizard is less bad; and so on.
|
||||
"conversion" -- Basically a bool. Conversion antags, well, convert. It's its own class for a good reason.
|
||||
*/
|
||||
|
||||
/datum/dynamic_storyteller/New()
|
||||
..()
|
||||
if (istype(SSticker.mode, /datum/game_mode/dynamic))
|
||||
mode = SSticker.mode
|
||||
GLOB.dynamic_curve_centre = curve_centre
|
||||
GLOB.dynamic_curve_width = curve_width
|
||||
GLOB.dynamic_forced_threat_level = forced_threat_level
|
||||
|
||||
/datum/dynamic_storyteller/proc/start_injection_cooldowns()
|
||||
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_latejoin_delay_max + GLOB.dynamic_first_latejoin_delay_min)
|
||||
mode.latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_first_latejoin_delay_min, GLOB.dynamic_first_latejoin_delay_max)) + world.time
|
||||
|
||||
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_midround_delay_min + GLOB.dynamic_first_midround_delay_max)
|
||||
mode.midround_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_first_midround_delay_min, GLOB.dynamic_first_midround_delay_max)) + world.time
|
||||
|
||||
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
|
||||
mode.event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time)
|
||||
|
||||
/datum/dynamic_storyteller/proc/do_process()
|
||||
return
|
||||
|
||||
/datum/dynamic_storyteller/proc/on_start()
|
||||
return
|
||||
|
||||
/datum/dynamic_storyteller/proc/get_midround_cooldown()
|
||||
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min)
|
||||
return round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_midround_delay_min, GLOB.dynamic_midround_delay_max))
|
||||
|
||||
/datum/dynamic_storyteller/proc/get_event_cooldown()
|
||||
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
|
||||
return round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max))
|
||||
|
||||
/datum/dynamic_storyteller/proc/get_latejoin_cooldown()
|
||||
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min)
|
||||
return round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max))
|
||||
|
||||
/datum/dynamic_storyteller/proc/get_injection_chance(dry_run = FALSE)
|
||||
if(mode.forced_injection)
|
||||
mode.forced_injection = !dry_run
|
||||
return 100
|
||||
var/chance = 0
|
||||
// If the high pop override is in effect, we reduce the impact of population on the antag injection chance
|
||||
var/high_pop_factor = (mode.current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit)
|
||||
var/max_pop_per_antag = max(5,15 - round(mode.threat_level/10) - round(mode.current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5)))
|
||||
if (!mode.current_players[CURRENT_LIVING_ANTAGS].len)
|
||||
chance += 80 // No antags at all? let's boost those odds!
|
||||
else
|
||||
var/current_pop_per_antag = mode.current_players[CURRENT_LIVING_PLAYERS].len / mode.current_players[CURRENT_LIVING_ANTAGS].len
|
||||
if (current_pop_per_antag > max_pop_per_antag)
|
||||
chance += min(50, 25+10*(current_pop_per_antag-max_pop_per_antag))
|
||||
else
|
||||
chance += 25-10*(max_pop_per_antag-current_pop_per_antag)
|
||||
if (mode.current_players[CURRENT_DEAD_PLAYERS].len > mode.current_players[CURRENT_LIVING_PLAYERS].len)
|
||||
chance -= 30 // More than half the crew died? ew, let's calm down on antags
|
||||
if (mode.threat > 70)
|
||||
chance += 15
|
||||
if (mode.threat < 30)
|
||||
chance -= 15
|
||||
return round(max(0,chance))
|
||||
|
||||
/datum/dynamic_storyteller/proc/roundstart_draft()
|
||||
var/list/drafted_rules = list()
|
||||
for (var/datum/dynamic_ruleset/roundstart/rule in mode.roundstart_rules)
|
||||
if (rule.acceptable(mode.roundstart_pop_ready, mode.threat_level) && mode.threat >= rule.cost) // If we got the population and threat required
|
||||
rule.candidates = mode.candidates.Copy()
|
||||
rule.trim_candidates()
|
||||
if (rule.ready() && rule.candidates.len > 0)
|
||||
var/property_weight = 0
|
||||
for(var/property in property_weights)
|
||||
if(property in rule.property_weights) // just treat it as 0 if it's not in there
|
||||
property_weight += rule.property_weights[property] * property_weights[property]
|
||||
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
|
||||
return drafted_rules
|
||||
|
||||
/datum/dynamic_storyteller/proc/midround_draft()
|
||||
var/list/drafted_rules = list()
|
||||
for (var/datum/dynamic_ruleset/midround/rule in mode.midround_rules)
|
||||
// if there are antags OR the rule is an antag rule, antag_acceptable will be true.
|
||||
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
|
||||
// Classic secret : only autotraitor/minor roles
|
||||
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
|
||||
continue
|
||||
rule.trim_candidates()
|
||||
if (rule.ready())
|
||||
var/property_weight = 0
|
||||
for(var/property in property_weights)
|
||||
if(property in rule.property_weights)
|
||||
property_weight += rule.property_weights[property] * property_weights[property]
|
||||
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
|
||||
else if(mode.threat < rule.cost)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
|
||||
return drafted_rules
|
||||
|
||||
/datum/dynamic_storyteller/proc/latejoin_draft(mob/living/carbon/human/newPlayer)
|
||||
var/list/drafted_rules = list()
|
||||
for (var/datum/dynamic_ruleset/latejoin/rule in mode.latejoin_rules)
|
||||
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
|
||||
// Classic secret : only autotraitor/minor roles
|
||||
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
|
||||
continue
|
||||
// No stacking : only one round-ender, unless threat level > stacking_limit.
|
||||
if (mode.threat_level > GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
|
||||
if(rule.flags & HIGHLANDER_RULESET && mode.highlander_executed)
|
||||
continue
|
||||
|
||||
rule.candidates = list(newPlayer)
|
||||
rule.trim_candidates()
|
||||
if (rule.ready())
|
||||
var/property_weight = 0
|
||||
for(var/property in property_weights)
|
||||
if(property in rule.property_weights)
|
||||
property_weight += rule.property_weights[property] * property_weights[property]
|
||||
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
|
||||
else if(mode.threat < rule.cost)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
|
||||
return drafted_rules
|
||||
|
||||
/datum/dynamic_storyteller/proc/event_draft()
|
||||
var/list/drafted_rules = list()
|
||||
for(var/datum/dynamic_ruleset/event/rule in mode.events)
|
||||
if(rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
|
||||
if(rule.ready())
|
||||
var/property_weight = 0
|
||||
for(var/property in property_weights)
|
||||
if(property in rule.property_weights)
|
||||
property_weight += rule.property_weights[property] * property_weights[property]
|
||||
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
|
||||
else if(mode.threat < rule.cost)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
|
||||
return drafted_rules
|
||||
|
||||
|
||||
/datum/dynamic_storyteller/cowabunga
|
||||
name = "Chaotic"
|
||||
curve_centre = 10
|
||||
desc = "Chaos: high. Variation: high. Likely antags: clock cult, revs, wizard."
|
||||
property_weights = list("extended" = -1, "chaos" = 10)
|
||||
weight = 2
|
||||
flags = WAROPS_ALWAYS_ALLOWED
|
||||
var/refund_cooldown
|
||||
|
||||
/datum/dynamic_storyteller/cowabunga/get_midround_cooldown()
|
||||
return ..() / 4
|
||||
|
||||
/datum/dynamic_storyteller/cowabunga/get_latejoin_cooldown()
|
||||
return ..() / 4
|
||||
|
||||
/datum/dynamic_storyteller/cowabunga/do_process()
|
||||
if(refund_cooldown < world.time)
|
||||
mode.refund_threat(10)
|
||||
mode.log_threat("Cowabunga it is. Refunded 10 threat. Threat is now [mode.threat].")
|
||||
refund_cooldown = world.time + 300 SECONDS
|
||||
|
||||
/datum/dynamic_storyteller/team
|
||||
name = "Teamwork"
|
||||
desc = "Chaos: high. Variation: low. Likely antags: nukies, clockwork cult, wizard, blob, xenomorph."
|
||||
curve_centre = 2
|
||||
curve_width = 1.5
|
||||
weight = 2
|
||||
flags = WAROPS_ALWAYS_ALLOWED
|
||||
property_weights = list("valid" = 3, "trust" = 5)
|
||||
|
||||
/datum/dynamic_storyteller/team/get_injection_chance(dry_run = FALSE)
|
||||
return (mode.current_players[CURRENT_LIVING_ANTAGS].len ? 0 : ..())
|
||||
|
||||
/datum/dynamic_storyteller/conversion
|
||||
name = "Conversion"
|
||||
desc = "Chaos: high. Variation: medium. Likely antags: cults, bloodsuckers, revs."
|
||||
curve_centre = 3
|
||||
curve_width = 1
|
||||
weight = 2
|
||||
flags = WAROPS_ALWAYS_ALLOWED
|
||||
property_weights = list("valid" = 1, "conversion" = 20)
|
||||
|
||||
/datum/dynamic_storyteller/classic
|
||||
name = "Random"
|
||||
desc = "Chaos: varies. Variation: highest. No special weights attached."
|
||||
weight = 6
|
||||
curve_width = 4
|
||||
|
||||
/datum/dynamic_storyteller/memes
|
||||
name = "Story"
|
||||
desc = "Chaos: varies. Variation: high. Likely antags: abductors, nukies, wizard, traitor."
|
||||
curve_width = 4
|
||||
property_weights = list("story_potential" = 10)
|
||||
|
||||
/datum/dynamic_storyteller/suspicion
|
||||
name = "Intrigue"
|
||||
desc = "Chaos: low. Variation: high. Likely antags: traitor, bloodsucker. Rare: revs, blood cult."
|
||||
curve_width = 4
|
||||
property_weights = list("trust" = -5)
|
||||
|
||||
/datum/dynamic_storyteller/liteextended
|
||||
name = "Calm"
|
||||
desc = "Chaos: low. Variation: medium. Likely antags: bloodsuckers, traitors, sentient disease, revenant."
|
||||
curve_centre = -5
|
||||
curve_width = 0.5
|
||||
flags = NO_ASSASSIN
|
||||
weight = 2
|
||||
property_weights = list("extended" = 1, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10)
|
||||
|
||||
/datum/dynamic_storyteller/liteextended/get_injection_chance(dry_run = FALSE)
|
||||
return ..()/2
|
||||
|
||||
/datum/dynamic_storyteller/extended
|
||||
name = "Extended"
|
||||
desc = "Chaos: none. Variation: none. Likely antags: none."
|
||||
curve_centre = -20
|
||||
weight = 2
|
||||
curve_width = 0.5
|
||||
|
||||
/datum/dynamic_storyteller/extended/on_start()
|
||||
GLOB.dynamic_forced_extended = TRUE
|
||||
@@ -180,7 +180,7 @@
|
||||
limit--
|
||||
while(!FoundDoor && limit)
|
||||
if (!FoundDoor)
|
||||
log_world("### MAP WARNING, [src] at [AREACOORD(src)] failed to find a valid airlock to cyclelink with!")
|
||||
log_mapping("[src] at [AREACOORD(src)] failed to find a valid airlock to cyclelink with!")
|
||||
return
|
||||
FoundDoor.cyclelinkedairlock = src
|
||||
cyclelinkedairlock = FoundDoor
|
||||
|
||||
@@ -262,14 +262,14 @@
|
||||
occupant_message("Deconstructing [W]...")
|
||||
if(do_after_cooldown(W))
|
||||
chassis.spark_system.start()
|
||||
W.ScrapeAway()
|
||||
W.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
playsound(W, 'sound/items/deconstruct.ogg', 50, 1)
|
||||
else if(isfloorturf(target))
|
||||
var/turf/open/floor/F = target
|
||||
occupant_message("Deconstructing [F]...")
|
||||
if(do_after_cooldown(target))
|
||||
chassis.spark_system.start()
|
||||
F.ScrapeAway()
|
||||
F.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
playsound(F, 'sound/items/deconstruct.ogg', 50, 1)
|
||||
else if (istype(target, /obj/machinery/door/airlock))
|
||||
occupant_message("Deconstructing [target]...")
|
||||
@@ -282,7 +282,7 @@
|
||||
var/turf/open/space/S = target
|
||||
occupant_message("Building Floor...")
|
||||
if(do_after_cooldown(S))
|
||||
S.PlaceOnTop(/turf/open/floor/plating)
|
||||
S.PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
|
||||
playsound(S, 'sound/items/deconstruct.ogg', 50, 1)
|
||||
chassis.spark_system.start()
|
||||
else if(isfloorturf(target))
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
if(metal)
|
||||
var/turf/T = get_turf(src)
|
||||
if(isspaceturf(T)) //Block up any exposed space
|
||||
T.PlaceOnTop(/turf/open/floor/plating/foam)
|
||||
T.PlaceOnTop(/turf/open/floor/plating/foam, flags = CHANGETURF_INHERIT_AIR)
|
||||
for(var/direction in GLOB.cardinals)
|
||||
var/turf/cardinal_turf = get_step(T, direction)
|
||||
if(get_area(cardinal_turf) != get_area(T)) //We're at an area boundary, so let's block off this turf!
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
name = "forcefield projector"
|
||||
desc = "An experimental device that can create several forcefields at a distance."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "signmaker_engi"
|
||||
icon_state = "signmaker_forcefield"
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
item_flags = NOBLUDGEON
|
||||
|
||||
@@ -79,7 +79,7 @@
|
||||
/obj/item/holosign_creator/atmos
|
||||
name = "ATMOS holofan projector"
|
||||
desc = "A holographic projector that creates holographic barriers that prevent changes in atmosphere conditions."
|
||||
icon_state = "signmaker_engi"
|
||||
icon_state = "signmaker_atmos"
|
||||
holosign_type = /obj/structure/holosign/barrier/atmos
|
||||
creation_time = 0
|
||||
max_signs = 3
|
||||
|
||||
@@ -374,7 +374,7 @@
|
||||
It appears to have a wooden grip and a shaved down guard."
|
||||
icon_state = "cxsword_hilt_traitor"
|
||||
force_on = 30
|
||||
armour_penetration = 50
|
||||
armour_penetration = 35
|
||||
embedding = list("embedded_pain_multiplier" = 10, "embed_chance" = 75, "embedded_fall_chance" = 0, "embedded_impact_pain_multiplier" = 10)
|
||||
block_chance = 50
|
||||
hitsound_on = 'sound/weapons/blade1.ogg'
|
||||
|
||||
@@ -216,10 +216,11 @@
|
||||
return
|
||||
else
|
||||
if(last_hit < world.time)
|
||||
if(target.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
|
||||
playsound(target, 'sound/weapons/genhit.ogg', 50, 1)
|
||||
return
|
||||
if(ishuman(target))
|
||||
var/mob/living/carbon/human/H = target
|
||||
if (H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
|
||||
return
|
||||
if(check_martial_counter(H, user))
|
||||
return
|
||||
playsound(get_turf(src), 'sound/effects/woodhit.ogg', 75, 1, -1)
|
||||
|
||||
@@ -11,11 +11,9 @@
|
||||
var/charge_cost = 30
|
||||
|
||||
/obj/item/borg/stun/attack(mob/living/M, mob/living/user)
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(H.check_shields(src, 0, "[M]'s [name]", MELEE_ATTACK))
|
||||
playsound(M, 'sound/weapons/genhit.ogg', 50, 1)
|
||||
return FALSE
|
||||
if(M.check_shields(src, 0, "[M]'s [name]", MELEE_ATTACK))
|
||||
playsound(M, 'sound/weapons/genhit.ogg', 50, 1)
|
||||
return FALSE
|
||||
if(iscyborg(user))
|
||||
var/mob/living/silicon/robot/R = user
|
||||
if(!R.cell.use(charge_cost))
|
||||
|
||||
@@ -192,9 +192,14 @@
|
||||
var/obj/O
|
||||
if(R.max_res_amount > 1) //Is it a stack?
|
||||
O = new R.result_type(usr.drop_location(), R.res_amount * multiplier)
|
||||
else
|
||||
else if(ispath(R.result_type, /turf))
|
||||
var/turf/T = usr.drop_location()
|
||||
if(!isturf(T))
|
||||
return
|
||||
T.PlaceOnTop(R.result_type, flags = CHANGETURF_INHERIT_AIR)
|
||||
O = new R.result_type(usr.drop_location())
|
||||
O.setDir(usr.dir)
|
||||
if(O)
|
||||
O.setDir(usr.dir)
|
||||
use(R.req_amount * multiplier)
|
||||
|
||||
//START: oh fuck i'm so sorry
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
/obj/item/radio,
|
||||
/obj/item/clothing/gloves,
|
||||
/obj/item/holosign_creator,
|
||||
/obj/item/forcefield_projector,
|
||||
/obj/item/assembly/signaler
|
||||
))
|
||||
STR.can_hold = can_hold
|
||||
@@ -458,8 +459,7 @@
|
||||
/obj/item/extinguisher/mini,
|
||||
/obj/item/radio,
|
||||
/obj/item/clothing/gloves,
|
||||
/obj/item/holosign_creator/atmos,
|
||||
/obj/item/holosign_creator/engineering,
|
||||
/obj/item/holosign_creator,
|
||||
/obj/item/forcefield_projector,
|
||||
/obj/item/assembly/signaler,
|
||||
/obj/item/lightreplacer,
|
||||
@@ -571,6 +571,7 @@
|
||||
/obj/item/reagent_containers/spray,
|
||||
/obj/item/soap,
|
||||
/obj/item/holosign_creator,
|
||||
/obj/item/forcefield_projector,
|
||||
/obj/item/key/janitor,
|
||||
/obj/item/clothing/gloves,
|
||||
/obj/item/melee/flyswatter,
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
. = ..()
|
||||
if(preload_cell_type)
|
||||
if(!ispath(preload_cell_type,/obj/item/stock_parts/cell))
|
||||
log_world("### MAP WARNING, [src] at [AREACOORD(src)] had an invalid preload_cell_type: [preload_cell_type].")
|
||||
log_mapping("[src] at [AREACOORD(src)] had an invalid preload_cell_type: [preload_cell_type].")
|
||||
else
|
||||
cell = new preload_cell_type(src)
|
||||
update_icon()
|
||||
@@ -168,11 +168,9 @@
|
||||
|
||||
|
||||
/obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/user)
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) //No message; check_shields() handles that
|
||||
playsound(L, 'sound/weapons/genhit.ogg', 50, 1)
|
||||
return FALSE
|
||||
if(L.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) //No message; check_shields() handles that
|
||||
playsound(L, 'sound/weapons/genhit.ogg', 50, 1)
|
||||
return FALSE
|
||||
var/stunpwr = stunforce
|
||||
var/obj/item/stock_parts/cell/our_cell = get_cell()
|
||||
if(!our_cell)
|
||||
|
||||
@@ -872,7 +872,7 @@
|
||||
user.visible_message("<span class='danger'>[user] blasts \the [target] with \the [src]!</span>")
|
||||
playsound(target, 'sound/magic/disintegrate.ogg', 100, 1)
|
||||
W.break_wall()
|
||||
W.ScrapeAway()
|
||||
W.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
return
|
||||
|
||||
//HF blade
|
||||
|
||||
@@ -605,7 +605,7 @@
|
||||
rank = "Gunner"
|
||||
|
||||
/obj/effect/mob_spawn/human/ghostcafe
|
||||
name = "ghost cafe sleeper"
|
||||
name = "Ghost Cafe Sleeper"
|
||||
uses = -1
|
||||
icon = 'icons/obj/machines/sleeper.dmi'
|
||||
icon_state = "sleeper"
|
||||
@@ -633,6 +633,7 @@
|
||||
uniform = /obj/item/clothing/under/color/random
|
||||
shoes = /obj/item/clothing/shoes/sneakers/black
|
||||
id = /obj/item/card/id
|
||||
r_hand = /obj/item/storage/box/syndie_kit/chameleon/ghostcafe
|
||||
|
||||
|
||||
/datum/outfit/ghostcafe/pre_equip(mob/living/carbon/human/H, visualsOnly = FALSE, client/preference_source)
|
||||
@@ -649,3 +650,17 @@
|
||||
else
|
||||
uniform = /obj/item/clothing/under/skirt/color/random
|
||||
|
||||
/obj/item/storage/box/syndie_kit/chameleon/ghostcafe
|
||||
name = "ghost cafe costuming kit"
|
||||
desc = "Look just the way you did in life - or better!"
|
||||
|
||||
/obj/item/storage/box/syndie_kit/chameleon/ghostcafe/PopulateContents() // Doesn't contain a PDA, for isolation reasons.
|
||||
new /obj/item/clothing/under/chameleon(src)
|
||||
new /obj/item/clothing/suit/chameleon(src)
|
||||
new /obj/item/clothing/gloves/chameleon(src)
|
||||
new /obj/item/clothing/shoes/chameleon(src)
|
||||
new /obj/item/clothing/glasses/chameleon(src)
|
||||
new /obj/item/clothing/head/chameleon(src)
|
||||
new /obj/item/clothing/mask/chameleon(src)
|
||||
new /obj/item/storage/backpack/chameleon(src)
|
||||
new /obj/item/clothing/neck/cloak/chameleon(src)
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
to_chat(user, "<span class='notice'>You build a floor.</span>")
|
||||
var/turf/T = src.loc
|
||||
if(isspaceturf(T))
|
||||
T.PlaceOnTop(/turf/open/floor/plating)
|
||||
T.PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
|
||||
qdel(src)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
@@ -102,13 +102,13 @@
|
||||
/obj/structure/stairs/proc/force_open_above()
|
||||
var/turf/open/openspace/T = get_step_multiz(get_turf(src), UP)
|
||||
if(T && !istype(T))
|
||||
T.ChangeTurf(/turf/open/openspace)
|
||||
T.ChangeTurf(/turf/open/openspace, flags = CHANGETURF_INHERIT_AIR)
|
||||
|
||||
/obj/structure/stairs/proc/on_multiz_new(turf/source, dir)
|
||||
if(dir == UP)
|
||||
var/turf/open/openspace/T = get_step_multiz(get_turf(src), UP)
|
||||
if(T && !istype(T))
|
||||
T.ChangeTurf(/turf/open/openspace)
|
||||
T.ChangeTurf(/turf/open/openspace, flags = CHANGETURF_INHERIT_AIR)
|
||||
|
||||
/obj/structure/stairs/intercept_zImpact(atom/movable/AM, levels = 1)
|
||||
. = ..()
|
||||
|
||||
@@ -10,4 +10,9 @@
|
||||
|
||||
/turf/baseturf_skipover/shuttle
|
||||
name = "Shuttle baseturf skipover"
|
||||
desc = "Acts as the bottom of the shuttle, if this isn't here the shuttle floor is broken through."
|
||||
desc = "Acts as the bottom of the shuttle, if this isn't here the shuttle floor is broken through."
|
||||
|
||||
/turf/baseturf_bottom
|
||||
name = "Z-level baseturf placeholder"
|
||||
desc = "Marker for z-level baseturf, usually resolves to space."
|
||||
baseturfs = /turf/baseturf_bottom
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// This is a list of turf types we dont want to assign to baseturfs unless through initialization or explicitly
|
||||
GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
|
||||
/turf/open/space,
|
||||
/turf/baseturf_bottom
|
||||
)))
|
||||
|
||||
/turf/proc/empty(turf_type=/turf/open/space, baseturf_type, list/ignore_typecache, flags)
|
||||
@@ -56,12 +57,20 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
|
||||
// Creates a new turf
|
||||
// new_baseturfs can be either a single type or list of types, formated the same as baseturfs. see turf.dm
|
||||
/turf/proc/ChangeTurf(path, list/new_baseturfs, flags)
|
||||
if(!path)
|
||||
return
|
||||
if(path == /turf/open/space/basic)
|
||||
// basic doesn't initialize and this will cause issues
|
||||
// no warning though because this can happen naturaly as a result of it being built on top of
|
||||
path = /turf/open/space
|
||||
switch(path)
|
||||
if(null)
|
||||
return
|
||||
if(/turf/baseturf_bottom)
|
||||
path = SSmapping.level_trait(z, ZTRAIT_BASETURF) || /turf/open/space
|
||||
if (!ispath(path))
|
||||
path = text2path(path)
|
||||
if (!ispath(path))
|
||||
warning("Z-level [z] has invalid baseturf '[SSmapping.level_trait(z, ZTRAIT_BASETURF)]'")
|
||||
path = /turf/open/space
|
||||
if(/turf/open/space/basic)
|
||||
// basic doesn't initialize and this will cause issues
|
||||
// no warning though because this can happen naturaly as a result of it being built on top of
|
||||
path = /turf/open/space
|
||||
if(!GLOB.use_preloader && path == type && !(flags & CHANGETURF_FORCEOP)) // Don't no-op if the map loader requires it to be reconstructed
|
||||
return src
|
||||
if(flags & CHANGETURF_SKIP)
|
||||
@@ -128,16 +137,15 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
|
||||
/turf/open/ChangeTurf(path, list/new_baseturfs, flags)
|
||||
if ((flags & CHANGETURF_INHERIT_AIR) && ispath(path, /turf/open))
|
||||
SSair.remove_from_active(src)
|
||||
var/stashed_air = air
|
||||
air = null // so that it doesn't get deleted
|
||||
var/datum/gas_mixture/stashed_air = new()
|
||||
stashed_air.copy_from(air)
|
||||
. = ..()
|
||||
if (!. || . == src) // changeturf failed or didn't do anything
|
||||
air = stashed_air
|
||||
if (!.) // changeturf failed or didn't do anything
|
||||
QDEL_NULL(stashed_air)
|
||||
return
|
||||
var/turf/open/newTurf = .
|
||||
if (!istype(newTurf.air, /datum/gas_mixture/immutable/space))
|
||||
QDEL_NULL(newTurf.air)
|
||||
newTurf.air = stashed_air
|
||||
newTurf.air.copy_from(stashed_air)
|
||||
QDEL_NULL(stashed_air)
|
||||
SSair.add_to_active(newTurf)
|
||||
else
|
||||
if(ispath(path,/turf/closed))
|
||||
@@ -215,7 +223,7 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
|
||||
newT.assemble_baseturfs(initial(fake_turf_type.baseturfs)) // The baseturfs list is created like roundstart
|
||||
if(!length(newT.baseturfs))
|
||||
newT.baseturfs = list(baseturfs)
|
||||
newT.baseturfs -= newT.baseturfs & GLOB.blacklisted_automated_baseturfs
|
||||
newT.baseturfs -= GLOB.blacklisted_automated_baseturfs
|
||||
newT.baseturfs.Insert(1, old_baseturfs) // The old baseturfs are put underneath
|
||||
return newT
|
||||
if(!length(baseturfs))
|
||||
@@ -315,5 +323,5 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
|
||||
SSair.add_to_active(src)
|
||||
|
||||
/turf/proc/ReplaceWithLattice()
|
||||
ScrapeAway()
|
||||
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
new /obj/structure/lattice(locate(x, y, z))
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
icon = 'icons/turf/walls.dmi'
|
||||
explosion_block = 50
|
||||
|
||||
/turf/closed/indestructible/TerraformTurf(path, defer_change = FALSE, ignore_air = FALSE)
|
||||
/turf/closed/indestructible/TerraformTurf(path, new_baseturf, flags, defer_change = FALSE, ignore_air = FALSE)
|
||||
return
|
||||
|
||||
/turf/closed/indestructible/acid_act(acidpwr, acid_volume, acid_id)
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
/turf/open/indestructible/singularity_act()
|
||||
return
|
||||
|
||||
/turf/open/indestructible/TerraformTurf(path, defer_change = FALSE, ignore_air = FALSE)
|
||||
/turf/open/indestructible/TerraformTurf(path, new_baseturf, flags, defer_change = FALSE, ignore_air = FALSE)
|
||||
return
|
||||
|
||||
/turf/open/indestructible/sound
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
qdel(L)
|
||||
playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
|
||||
to_chat(user, "<span class='notice'>You build a floor.</span>")
|
||||
PlaceOnTop(/turf/open/floor/plating)
|
||||
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You need one floor tile to build a floor!</span>")
|
||||
else
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
switch(passed_mode)
|
||||
if(RCD_FLOORWALL)
|
||||
to_chat(user, "<span class='notice'>You build a floor.</span>")
|
||||
PlaceOnTop(/turf/open/floor/plating)
|
||||
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
|
||||
to_chat(user, "<span class='notice'>You build a floor.</span>")
|
||||
// Create a floor, which has this chasm underneath it
|
||||
PlaceOnTop(/turf/open/floor/plating)
|
||||
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You need one floor tile to build a floor!</span>")
|
||||
else
|
||||
|
||||
@@ -62,29 +62,29 @@
|
||||
if(severity != 1 && shielded && target != src)
|
||||
return
|
||||
if(target == src)
|
||||
ScrapeAway()
|
||||
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
return
|
||||
if(target != null)
|
||||
severity = 3
|
||||
|
||||
switch(severity)
|
||||
if(1)
|
||||
ScrapeAway(2)
|
||||
ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
|
||||
if(2)
|
||||
switch(pick(1,2;75,3))
|
||||
if(1)
|
||||
if(!length(baseturfs) || !ispath(baseturfs[baseturfs.len-1], /turf/open/floor))
|
||||
ScrapeAway()
|
||||
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
ReplaceWithLattice()
|
||||
else
|
||||
ScrapeAway(2)
|
||||
ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
|
||||
if(prob(33))
|
||||
new /obj/item/stack/sheet/metal(src)
|
||||
if(2)
|
||||
ScrapeAway(2)
|
||||
ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
|
||||
if(3)
|
||||
if(prob(80))
|
||||
ScrapeAway()
|
||||
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
else
|
||||
break_tile()
|
||||
hotspot_expose(1000,CELL_VOLUME)
|
||||
@@ -135,7 +135,7 @@
|
||||
burnt = 1
|
||||
|
||||
/turf/open/floor/proc/make_plating()
|
||||
return ScrapeAway()
|
||||
return ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
|
||||
/turf/open/floor/ChangeTurf(path, new_baseturf, flags)
|
||||
if(!isfloorturf(src))
|
||||
@@ -213,15 +213,15 @@
|
||||
/turf/open/floor/narsie_act(force, ignore_mobs, probability = 20)
|
||||
. = ..()
|
||||
if(.)
|
||||
ChangeTurf(/turf/open/floor/engine/cult)
|
||||
ChangeTurf(/turf/open/floor/engine/cult, flags = CHANGETURF_INHERIT_AIR)
|
||||
|
||||
/turf/open/floor/ratvar_act(force, ignore_mobs)
|
||||
. = ..()
|
||||
if(.)
|
||||
ChangeTurf(/turf/open/floor/clockwork)
|
||||
ChangeTurf(/turf/open/floor/clockwork, flags = CHANGETURF_INHERIT_AIR)
|
||||
|
||||
/turf/open/floor/acid_melt()
|
||||
ScrapeAway()
|
||||
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
|
||||
/turf/open/floor/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
|
||||
switch(the_rcd.mode)
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
name = "plating"
|
||||
icon_state = "plating"
|
||||
intact = FALSE
|
||||
baseturfs = /turf/open/space
|
||||
baseturfs = /turf/baseturf_bottom
|
||||
footstep = FOOTSTEP_PLATING
|
||||
barefootstep = FOOTSTEP_HARD_BAREFOOT
|
||||
clawfootstep = FOOTSTEP_HARD_CLAW
|
||||
@@ -61,7 +61,7 @@
|
||||
to_chat(user, "<span class='notice'>You begin reinforcing the floor...</span>")
|
||||
if(do_after(user, 30, target = src))
|
||||
if (R.get_amount() >= 2 && !istype(src, /turf/open/floor/engine))
|
||||
PlaceOnTop(/turf/open/floor/engine)
|
||||
PlaceOnTop(/turf/open/floor/engine, flags = CHANGETURF_INHERIT_AIR)
|
||||
playsound(src, 'sound/items/deconstruct.ogg', 80, 1)
|
||||
R.use(2)
|
||||
to_chat(user, "<span class='notice'>You reinforce the floor.</span>")
|
||||
@@ -76,7 +76,7 @@
|
||||
var/obj/item/stack/tile/W = C
|
||||
if(!W.use(1))
|
||||
return
|
||||
var/turf/open/floor/T = PlaceOnTop(W.turf_type)
|
||||
var/turf/open/floor/T = PlaceOnTop(W.turf_type, flags = CHANGETURF_INHERIT_AIR)
|
||||
if(istype(W, /obj/item/stack/tile/light)) //TODO: get rid of this ugly check somehow
|
||||
var/obj/item/stack/tile/light/L = W
|
||||
var/turf/open/floor/light/F = T
|
||||
@@ -117,7 +117,7 @@
|
||||
qdel(L)
|
||||
to_chat(user, "<span class='notice'>You reinforce the foamed plating with tiling.</span>")
|
||||
playsound(src, 'sound/weapons/Genhit.ogg', 50, TRUE)
|
||||
ChangeTurf(/turf/open/floor/plating)
|
||||
ChangeTurf(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
|
||||
else
|
||||
playsound(src, 'sound/weapons/tap.ogg', 100, TRUE) //The attack sound is muffled by the foam itself
|
||||
user.changeNext_move(CLICK_CD_MELEE)
|
||||
@@ -125,7 +125,7 @@
|
||||
if(prob(I.force * 20 - 25))
|
||||
user.visible_message("<span class='danger'>[user] smashes through [src]!</span>", \
|
||||
"<span class='danger'>You smash through [src] with [I]!</span>")
|
||||
ScrapeAway()
|
||||
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
else
|
||||
to_chat(user, "<span class='danger'>You hit [src], to no effect!</span>")
|
||||
|
||||
@@ -136,13 +136,13 @@
|
||||
/turf/open/floor/plating/foam/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode)
|
||||
if(passed_mode == RCD_FLOORWALL)
|
||||
to_chat(user, "<span class='notice'>You build a floor.</span>")
|
||||
ChangeTurf(/turf/open/floor/plating)
|
||||
ChangeTurf(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/turf/open/floor/plating/foam/ex_act()
|
||||
..()
|
||||
ScrapeAway()
|
||||
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
|
||||
/turf/open/floor/plating/foam/tool_act(mob/living/user, obj/item/I, tool_type)
|
||||
return
|
||||
|
||||
@@ -76,11 +76,6 @@
|
||||
for(var/obj/item/stack/ore/O in src)
|
||||
SEND_SIGNAL(W, COMSIG_PARENT_ATTACKBY, O)
|
||||
|
||||
/turf/open/floor/plating/asteroid/singularity_act()
|
||||
if(is_planet_level(z))
|
||||
return ..()
|
||||
ScrapeAway()
|
||||
|
||||
/turf/open/floor/plating/asteroid/ex_act(severity, target)
|
||||
. = SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, severity, target)
|
||||
contents_explosion(severity, target)
|
||||
@@ -132,6 +127,7 @@
|
||||
|
||||
/turf/open/floor/plating/asteroid/airless
|
||||
initial_gas_mix = AIRLESS_ATMOS
|
||||
baseturfs = /turf/open/floor/plating/asteroid/airless
|
||||
turf_type = /turf/open/floor/plating/asteroid/airless
|
||||
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
return TRUE
|
||||
if(floor_tile)
|
||||
new floor_tile(src, 2)
|
||||
ScrapeAway()
|
||||
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
return TRUE
|
||||
|
||||
/turf/open/floor/engine/acid_act(acidpwr, acid_volume)
|
||||
@@ -56,23 +56,23 @@
|
||||
if(severity != 1 && shielded && target != src)
|
||||
return
|
||||
if(target == src)
|
||||
ScrapeAway()
|
||||
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
return
|
||||
switch(severity)
|
||||
if(1)
|
||||
if(prob(80))
|
||||
if(!length(baseturfs) || !ispath(baseturfs[baseturfs.len-1], /turf/open/floor))
|
||||
ScrapeAway()
|
||||
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
ReplaceWithLattice()
|
||||
else
|
||||
ScrapeAway(2)
|
||||
ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
|
||||
else if(prob(50))
|
||||
ScrapeAway(2)
|
||||
ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
|
||||
else
|
||||
ScrapeAway()
|
||||
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
if(2)
|
||||
if(prob(50))
|
||||
ScrapeAway()
|
||||
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
|
||||
/turf/open/floor/engine/singularity_pull(S, current_size)
|
||||
..()
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
switch(passed_mode)
|
||||
if(RCD_FLOORWALL)
|
||||
to_chat(user, "<span class='notice'>You build a floor.</span>")
|
||||
PlaceOnTop(/turf/open/floor/plating)
|
||||
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
qdel(L)
|
||||
playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
|
||||
to_chat(user, "<span class='notice'>You build a floor.</span>")
|
||||
PlaceOnTop(/turf/open/floor/plating)
|
||||
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You need one floor tile to build a floor!</span>")
|
||||
else
|
||||
@@ -212,7 +212,7 @@
|
||||
switch(passed_mode)
|
||||
if(RCD_FLOORWALL)
|
||||
to_chat(user, "<span class='notice'>You build a floor.</span>")
|
||||
PlaceOnTop(/turf/open/floor/plating)
|
||||
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// A list will be created in initialization that figures out the baseturf's baseturf etc.
|
||||
// In the case of a list it is sorted from bottom layer to top.
|
||||
// This shouldn't be modified directly, use the helper procs.
|
||||
var/list/baseturfs = /turf/open/space
|
||||
var/list/baseturfs = /turf/baseturf_bottom
|
||||
|
||||
var/temperature = T20C
|
||||
var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed
|
||||
@@ -391,7 +391,7 @@
|
||||
continue
|
||||
if(O.invisibility == INVISIBILITY_MAXIMUM)
|
||||
O.singularity_act()
|
||||
ScrapeAway()
|
||||
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
return(2)
|
||||
|
||||
/turf/proc/can_have_cabling()
|
||||
@@ -564,4 +564,4 @@
|
||||
//Whatever happens after high temperature fire dies out or thermite reaction works.
|
||||
//Should return new turf
|
||||
/turf/proc/Melt()
|
||||
return ScrapeAway()
|
||||
return ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
|
||||
@@ -108,6 +108,7 @@ GLOBAL_VAR(restart_counter)
|
||||
GLOB.world_href_log = "[GLOB.log_directory]/hrefs.log"
|
||||
GLOB.sql_error_log = "[GLOB.log_directory]/sql.log"
|
||||
GLOB.world_qdel_log = "[GLOB.log_directory]/qdel.log"
|
||||
GLOB.world_map_error_log = "[GLOB.log_directory]/map_errors.log"
|
||||
GLOB.world_runtime_log = "[GLOB.log_directory]/runtime.log"
|
||||
GLOB.query_debug_log = "[GLOB.log_directory]/query_debug.log"
|
||||
GLOB.world_job_debug_log = "[GLOB.log_directory]/job_debug.log"
|
||||
|
||||
@@ -693,7 +693,7 @@
|
||||
var/prev_dynamic_voting = CONFIG_GET(flag/dynamic_voting)
|
||||
CONFIG_SET(flag/dynamic_voting,!prev_dynamic_voting)
|
||||
if (!prev_dynamic_voting)
|
||||
to_chat(world, "<B>Vote is now between extended and dynamic chaos.</B>")
|
||||
to_chat(world, "<B>Vote is now a ranked choice of dynamic storytellers.</B>")
|
||||
else
|
||||
to_chat(world, "<B>Vote is now between extended and secret.</B>")
|
||||
log_admin("[key_name(usr)] [prev_dynamic_voting ? "disabled" : "enabled"] dynamic voting.")
|
||||
|
||||
@@ -35,6 +35,9 @@ GLOBAL_LIST_INIT(admin_verbs_debug_mapping, list(
|
||||
/client/proc/cmd_admin_grantfullaccess,
|
||||
/client/proc/cmd_admin_areatest_all,
|
||||
/client/proc/cmd_admin_areatest_station,
|
||||
#ifdef TESTING
|
||||
/client/proc/see_dirty_varedits,
|
||||
#endif
|
||||
/client/proc/cmd_admin_test_atmos_controllers,
|
||||
/client/proc/cmd_admin_rejuvenate,
|
||||
/datum/admins/proc/show_traitor_panel,
|
||||
@@ -84,8 +87,24 @@ GLOBAL_PROTECT(admin_verbs_debug_mapping)
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Range") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Range")
|
||||
|
||||
#ifdef TESTING
|
||||
GLOBAL_LIST_EMPTY(dirty_vars)
|
||||
|
||||
|
||||
/client/proc/see_dirty_varedits()
|
||||
set category = "Mapping"
|
||||
set name = "Dirty Varedits"
|
||||
|
||||
var/list/dat = list()
|
||||
dat += "<h3>Abandon all hope ye who enter here</h3><br><br>"
|
||||
for(var/thing in GLOB.dirty_vars)
|
||||
dat += "[thing]<br>"
|
||||
CHECK_TICK
|
||||
var/datum/browser/popup = new(usr, "dirty_vars", "Dirty Varedits", 900, 750)
|
||||
popup.set_content(dat.Join())
|
||||
popup.open()
|
||||
#endif
|
||||
|
||||
/client/proc/sec_camera_report()
|
||||
set category = "Mapping"
|
||||
set name = "Camera Report"
|
||||
|
||||
@@ -484,11 +484,9 @@
|
||||
|
||||
user.do_attack_animation(L)
|
||||
|
||||
if(ishuman(L))
|
||||
var/mob/living/carbon/human/H = L
|
||||
if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
|
||||
playsound(H, 'sound/weapons/genhit.ogg', 50, TRUE)
|
||||
return FALSE
|
||||
if(L.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK))
|
||||
playsound(L, 'sound/weapons/genhit.ogg', 50, TRUE)
|
||||
return FALSE
|
||||
|
||||
switch (mode)
|
||||
if(BATON_STUN)
|
||||
|
||||
@@ -139,7 +139,7 @@
|
||||
var/new_thing_type = fabrication_values["new_obj_type"]
|
||||
if(isturf(target)) //if our target is a turf, we're just going to ChangeTurf it and assume it'll work out.
|
||||
var/turf/T = target
|
||||
T.ChangeTurf(new_thing_type)
|
||||
T.ChangeTurf(new_thing_type, flags = CHANGETURF_INHERIT_AIR)
|
||||
else
|
||||
if(new_thing_type)
|
||||
if(fabrication_values["dir_in_new"])
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
if(anchored)
|
||||
T.PlaceOnTop(/turf/closed/wall/clockwork)
|
||||
else
|
||||
T.PlaceOnTop(/turf/open/floor/clockwork)
|
||||
T.PlaceOnTop(/turf/open/floor/clockwork, flags = CHANGETURF_INHERIT_AIR)
|
||||
new /obj/structure/falsewall/brass(T)
|
||||
qdel(src)
|
||||
else
|
||||
|
||||
@@ -239,9 +239,9 @@
|
||||
var/turf/T = safepick(validturfs)
|
||||
if(T)
|
||||
if(istype(T, /turf/open/floor/plating))
|
||||
T.PlaceOnTop(/turf/open/floor/engine/cult)
|
||||
T.PlaceOnTop(/turf/open/floor/engine/cult, flags = CHANGETURF_INHERIT_AIR)
|
||||
else
|
||||
T.ChangeTurf(/turf/open/floor/engine/cult)
|
||||
T.ChangeTurf(/turf/open/floor/engine/cult, flags = CHANGETURF_INHERIT_AIR)
|
||||
else
|
||||
var/turf/open/floor/engine/cult/F = safepick(cultturfs)
|
||||
if(F)
|
||||
|
||||
@@ -76,9 +76,10 @@ GLOBAL_VAR_INIT(war_declared, FALSE)
|
||||
CONFIG_SET(number/shuttle_refuel_delay, max(CONFIG_GET(number/shuttle_refuel_delay), CHALLENGE_SHUTTLE_DELAY))
|
||||
if(istype(SSticker.mode, /datum/game_mode/dynamic))
|
||||
var/datum/game_mode/dynamic/mode = SSticker.mode
|
||||
var/threat_spent = CONFIG_GET(number/dynamic_warops_cost)
|
||||
mode.spend_threat(threat_spent)
|
||||
mode.log_threat("Nuke ops spent [threat_spent] on war ops.")
|
||||
if(!(mode.storyteller.flags & WAROPS_ALWAYS_ALLOWED))
|
||||
var/threat_spent = CONFIG_GET(number/dynamic_warops_cost)
|
||||
mode.spend_threat(threat_spent)
|
||||
mode.log_threat("Nuke ops spent [threat_spent] on war ops.")
|
||||
SSblackbox.record_feedback("amount", "nuclear_challenge_mode", 1)
|
||||
|
||||
qdel(src)
|
||||
@@ -101,12 +102,13 @@ GLOBAL_VAR_INIT(war_declared, FALSE)
|
||||
return FALSE
|
||||
if(istype(SSticker.mode, /datum/game_mode/dynamic))
|
||||
var/datum/game_mode/dynamic/mode = SSticker.mode
|
||||
if(mode.threat_level < CONFIG_GET(number/dynamic_warops_requirement))
|
||||
to_chat(user, "Due to the dynamic space in which the station resides, you are too deep into Nanotrasen territory to reasonably go loud.")
|
||||
return FALSE
|
||||
else if(mode.threat < CONFIG_GET(number/dynamic_warops_cost))
|
||||
to_chat(user, "Due to recent threats on the station, Nanotrasen is looking too closely for a war declaration to be wise.")
|
||||
return FALSE
|
||||
if(!(mode.storyteller.flags & WAROPS_ALWAYS_ALLOWED))
|
||||
if(mode.threat_level < CONFIG_GET(number/dynamic_warops_requirement))
|
||||
to_chat(user, "Due to the dynamic space in which the station resides, you are too deep into Nanotrasen territory to reasonably go loud.")
|
||||
return FALSE
|
||||
else if(mode.threat < CONFIG_GET(number/dynamic_warops_cost))
|
||||
to_chat(user, "Due to recent threats on the station, Nanotrasen is looking too closely for a war declaration to be wise.")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/nuclear_challenge/clownops
|
||||
|
||||
@@ -80,6 +80,8 @@
|
||||
if(istype(SSticker.mode,/datum/game_mode/dynamic))
|
||||
mode = SSticker.mode
|
||||
is_dynamic = TRUE
|
||||
if(mode.storyteller.flags & NO_ASSASSIN)
|
||||
is_hijacker = FALSE
|
||||
if(GLOB.joined_player_list.len>=GLOB.dynamic_high_pop_limit)
|
||||
is_hijacker = (prob(10) && mode.threat_level > CONFIG_GET(number/dynamic_hijack_high_population_requirement))
|
||||
else
|
||||
@@ -180,7 +182,7 @@
|
||||
destroy_objective.owner = owner
|
||||
destroy_objective.find_target()
|
||||
add_objective(destroy_objective)
|
||||
else if(prob(30))
|
||||
else if(prob(30) || (mode.storyteller.flags & NO_ASSASSIN))
|
||||
var/datum/objective/maroon/maroon_objective = new
|
||||
maroon_objective.owner = owner
|
||||
maroon_objective.find_target()
|
||||
|
||||
@@ -57,10 +57,10 @@
|
||||
if(item.parent)
|
||||
var/static/pipenetwarnings = 10
|
||||
if(pipenetwarnings > 0)
|
||||
warning("build_pipeline(): [item.type] added to a pipenet while still having one. (pipes leading to the same spot stacking in one turf) Nearby: ([item.x], [item.y], [item.z])")
|
||||
log_mapping("build_pipeline(): [item.type] added to a pipenet while still having one. (pipes leading to the same spot stacking in one turf) Nearby: ([item.x], [item.y], [item.z]).")
|
||||
pipenetwarnings -= 1
|
||||
if(pipenetwarnings == 0)
|
||||
warning("build_pipeline(): further messages about pipenets will be suppressed")
|
||||
log_mapping("build_pipeline(): further messages about pipenets will be suppressed")
|
||||
members += item
|
||||
possible_expansions += item
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
pixel_y = (new_layer - PIPING_LAYER_DEFAULT) * PIPING_LAYER_P_Y
|
||||
|
||||
/obj/machinery/meter/process_atmos()
|
||||
if(!target)
|
||||
if(!(target?.flags_1 & INITIALIZED_1))
|
||||
icon_state = "meterX"
|
||||
return 0
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
if(istype(object,/turf) && left_click && !alt_click && !ctrl_click)
|
||||
var/turf/T = object
|
||||
if(isspaceturf(object))
|
||||
T.PlaceOnTop(/turf/open/floor/plating)
|
||||
T.PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
|
||||
else if(isplatingturf(object))
|
||||
T.PlaceOnTop(/turf/open/floor/plasteel)
|
||||
T.PlaceOnTop(/turf/open/floor/plasteel, flags = CHANGETURF_INHERIT_AIR)
|
||||
else if(isfloorturf(object))
|
||||
T.PlaceOnTop(/turf/closed/wall)
|
||||
else if(iswallturf(object))
|
||||
@@ -35,7 +35,7 @@
|
||||
log_admin("Build Mode: [key_name(c)] deleted [object] at [AREACOORD(object)]")
|
||||
if(isturf(object))
|
||||
var/turf/T = object
|
||||
T.ScrapeAway()
|
||||
T.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
|
||||
else if(isobj(object))
|
||||
qdel(object)
|
||||
return
|
||||
|
||||
@@ -13,17 +13,16 @@
|
||||
//////////////////// Paperwork and Writing Supplies //////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* I did it Kevin
|
||||
/datum/supply_pack/misc/abandonedcrate
|
||||
name = "Abandoned Crate"
|
||||
desc = "Someone keeps finding these locked crates out in the boonies. How about you take a crack at it, we've had our fill. WARNING: EXPLOSIVE"
|
||||
name = "Loot Box"
|
||||
desc = "Try your luck with these highly secure loot boxes! Solve the lock, win great prizes! WARNING: EXPLOSIVE FAILURE."
|
||||
contraband = TRUE
|
||||
cost = 12800
|
||||
cost = 15000
|
||||
contains = list(/obj/structure/closet/crate/secure/loot)
|
||||
crate_name = "abandoned crate"
|
||||
crate_type = /obj/structure/closet/crate/large
|
||||
dangerous = TRUE
|
||||
*/
|
||||
|
||||
/datum/supply_pack/misc/artsupply
|
||||
name = "Art Supplies"
|
||||
desc = "Make some happy little accidents with six canvasses, two easels, two boxes of crayons, and a rainbow crayon!"
|
||||
|
||||
@@ -195,6 +195,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/auto_fit_viewport = TRUE
|
||||
|
||||
var/uplink_spawn_loc = UPLINK_PDA
|
||||
|
||||
var/sprint_spacebar = FALSE
|
||||
var/sprint_toggle = FALSE
|
||||
|
||||
var/list/exp = list()
|
||||
var/list/menuoptions
|
||||
@@ -889,6 +892,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
dat += "</a><br>"
|
||||
dat += "<b>Ambient Occlusion:</b> <a href='?_src_=prefs;preference=ambientocclusion'>[ambientocclusion ? "Enabled" : "Disabled"]</a><br>"
|
||||
dat += "<b>Fit Viewport:</b> <a href='?_src_=prefs;preference=auto_fit_viewport'>[auto_fit_viewport ? "Auto" : "Manual"]</a><br>"
|
||||
dat += "<b>Sprint Key:</b> <a href='?_src_=prefs;preference=sprint_key'>[sprint_spacebar ? "Space" : "Shift"]</a><br>"
|
||||
dat += "<b>Toggle Sprint:</b> <a href='?_src_=prefs;preference=sprint_toggle'>[sprint_toggle ? "Enabled" : "Disabled"]</a><br>"
|
||||
|
||||
if (CONFIG_GET(flag/maprotation) && CONFIG_GET(flag/tgstyle_maprotation))
|
||||
var/p_map = preferred_map
|
||||
@@ -2239,6 +2244,12 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
if(auto_fit_viewport && parent)
|
||||
parent.fit_viewport()
|
||||
|
||||
if("sprint_key")
|
||||
sprint_spacebar = !sprint_spacebar
|
||||
|
||||
if("sprint_toggle")
|
||||
sprint_toggle = !sprint_toggle
|
||||
|
||||
if("save")
|
||||
save_preferences()
|
||||
save_character()
|
||||
|
||||
@@ -167,6 +167,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
S["parallax"] >> parallax
|
||||
S["ambientocclusion"] >> ambientocclusion
|
||||
S["auto_fit_viewport"] >> auto_fit_viewport
|
||||
S["sprint_spacebar"] >> sprint_spacebar
|
||||
S["sprint_toggle"] >> sprint_toggle
|
||||
S["menuoptions"] >> menuoptions
|
||||
S["enable_tips"] >> enable_tips
|
||||
S["tip_delay"] >> tip_delay
|
||||
@@ -204,6 +206,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
parallax = sanitize_integer(parallax, PARALLAX_INSANE, PARALLAX_DISABLE, null)
|
||||
ambientocclusion = sanitize_integer(ambientocclusion, 0, 1, initial(ambientocclusion))
|
||||
auto_fit_viewport = sanitize_integer(auto_fit_viewport, 0, 1, initial(auto_fit_viewport))
|
||||
sprint_spacebar = sanitize_integer(sprint_spacebar, 0, 1, initial(sprint_spacebar))
|
||||
sprint_toggle = sanitize_integer(sprint_toggle, 0, 1, initial(sprint_toggle))
|
||||
ghost_form = sanitize_inlist(ghost_form, GLOB.ghost_forms, initial(ghost_form))
|
||||
ghost_orbit = sanitize_inlist(ghost_orbit, GLOB.ghost_orbits, initial(ghost_orbit))
|
||||
ghost_accs = sanitize_inlist(ghost_accs, GLOB.ghost_accs_options, GHOST_ACCS_DEFAULT_OPTION)
|
||||
@@ -264,6 +268,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
|
||||
WRITE_FILE(S["parallax"], parallax)
|
||||
WRITE_FILE(S["ambientocclusion"], ambientocclusion)
|
||||
WRITE_FILE(S["auto_fit_viewport"], auto_fit_viewport)
|
||||
WRITE_FILE(S["sprint_spacebar"], sprint_spacebar)
|
||||
WRITE_FILE(S["sprint_toggle"], sprint_toggle)
|
||||
WRITE_FILE(S["menuoptions"], menuoptions)
|
||||
WRITE_FILE(S["enable_tips"], enable_tips)
|
||||
WRITE_FILE(S["tip_delay"], tip_delay)
|
||||
|
||||
@@ -219,7 +219,7 @@
|
||||
name = "space cola snowcone"
|
||||
desc = "Space Cola drizzled over a snowball in a paper cup."
|
||||
icon_state = "soda_sc"
|
||||
list_reagents = list("nutriment" = 1, "space_cola" = 5)
|
||||
list_reagents = list("nutriment" = 1, "cola" = 5)
|
||||
tastes = list("ice" = 1, "water" = 1, "cola" = 5)
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/snowcones/spacemountainwind
|
||||
@@ -248,4 +248,4 @@
|
||||
desc = "A very colorful snowball in a paper cup."
|
||||
icon_state = "rainbow_sc"
|
||||
list_reagents = list("nutriment" = 5, "laughter" = 25)
|
||||
tastes = list("ice" = 1, "water" = 1, "sunlight" = 5, "light" = 5, "slime" = 5, "paint" = 3, "clouds" = 3)
|
||||
tastes = list("ice" = 1, "water" = 1, "sunlight" = 5, "light" = 5, "slime" = 5, "paint" = 3, "clouds" = 3)
|
||||
|
||||
@@ -8,6 +8,8 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico
|
||||
//On client, created on login
|
||||
/datum/chatOutput
|
||||
var/client/owner //client ref
|
||||
var/total_checks = 0
|
||||
var/last_check = 0
|
||||
var/loaded = FALSE // Has the client loaded the browser output area?
|
||||
var/list/messageQueue //If they haven't loaded chat, this is where messages will go until they do
|
||||
var/cookieSent = FALSE // Has the client sent a cookie for analysis
|
||||
@@ -150,6 +152,18 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico
|
||||
|
||||
//Called by client, sent data to investigate (cookie history so far)
|
||||
/datum/chatOutput/proc/analyzeClientData(cookie = "")
|
||||
//Spam check
|
||||
if(world.time > last_check + (3 SECONDS))
|
||||
last_check = world.time
|
||||
total_checks = 0
|
||||
|
||||
total_checks += 1
|
||||
|
||||
if(total_checks > SPAM_TRIGGER_AUTOMUTE)
|
||||
message_admins("[key_name(owner)] kicked for goonchat topic spam")
|
||||
qdel(owner)
|
||||
return
|
||||
|
||||
if(!cookie)
|
||||
return
|
||||
|
||||
@@ -158,13 +172,22 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("tmp/iconCache.sav")) //Cache of ico
|
||||
if (connData && islist(connData) && connData.len > 0 && connData["connData"])
|
||||
connectionHistory = connData["connData"] //lol fuck
|
||||
var/list/found = new()
|
||||
for(var/i in connectionHistory.len to 1 step -1)
|
||||
if(connectionHistory.len > 5)
|
||||
message_admins("[key_name(src.owner)] was kicked for an invalid ban cookie)")
|
||||
qdel(owner)
|
||||
return
|
||||
|
||||
for(var/i in min(connectionHistory.len, 5) to 1 step -1)
|
||||
if(QDELETED(owner))
|
||||
//he got cleaned up before we were done
|
||||
return
|
||||
var/list/row = src.connectionHistory[i]
|
||||
if (!row || row.len < 3 || (!row["ckey"] || !row["compid"] || !row["ip"])) //Passed malformed history object
|
||||
return
|
||||
if (world.IsBanned(row["ckey"], row["ip"], row["compid"], real_bans_only=TRUE))
|
||||
found = row
|
||||
break
|
||||
CHECK_TICK
|
||||
|
||||
//Uh oh this fucker has a history of playing on a banned account!!
|
||||
if (found.len > 0)
|
||||
|
||||
@@ -421,8 +421,8 @@ function handleClientData(ckey, ip, compid) {
|
||||
return; //Record already exists
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.clientData.length >= opts.clientDataLimit) {
|
||||
//Lets make sure we obey our limit (can connect from server with higher limit)
|
||||
while (opts.clientData.length >= opts.clientDataLimit) {
|
||||
opts.clientData.shift();
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -59,9 +59,7 @@
|
||||
return
|
||||
var/area/AS = get_area(src)
|
||||
if(istype(AS, /area/holodeck))
|
||||
log_world("### MAPPING ERROR")
|
||||
log_world("Holodeck computer cannot be in a holodeck.")
|
||||
log_world("This would cause circular power dependency.")
|
||||
log_mapping("Holodeck computer cannot be in a holodeck, This would cause circular power dependency.")
|
||||
qdel(src)
|
||||
return
|
||||
else
|
||||
|
||||
@@ -58,13 +58,23 @@
|
||||
return
|
||||
switch(_key)
|
||||
if("Shift")
|
||||
sprint_hotkey(TRUE)
|
||||
if(!user.prefs.sprint_spacebar)
|
||||
user.prefs.sprint_toggle ? togglesprint() : sprint_hotkey(TRUE) //Yes, this looks hacky. Yes, this works.
|
||||
return
|
||||
if("Space")
|
||||
if(user.prefs.sprint_spacebar)
|
||||
user.prefs.sprint_toggle ? togglesprint() : sprint_hotkey(TRUE)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/human/key_up(_key, client/user)
|
||||
switch(_key)
|
||||
if("Shift")
|
||||
sprint_hotkey(FALSE)
|
||||
if(!user.prefs.sprint_spacebar && !user.prefs.sprint_toggle)
|
||||
sprint_hotkey(FALSE)
|
||||
return
|
||||
if("Space")
|
||||
if(user.prefs.sprint_spacebar && !user.prefs.sprint_toggle)
|
||||
sprint_hotkey(FALSE)
|
||||
return
|
||||
return ..()
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
//initialize things that are normally initialized after map load
|
||||
parsed.initTemplateBounds()
|
||||
smooth_zlevel(world.maxz)
|
||||
log_game("Z-level [name] loaded at at [x],[y],[world.maxz]")
|
||||
log_game("Z-level [name] loaded at [x],[y],[world.maxz]")
|
||||
|
||||
return level
|
||||
|
||||
@@ -84,6 +84,13 @@
|
||||
if(T.y+height > world.maxy)
|
||||
return
|
||||
|
||||
var/list/border = block(locate(max(T.x-1, 1), max(T.y-1, 1), T.z),
|
||||
locate(min(T.x+width+1, world.maxx), min(T.y+height+1, world.maxy), T.z))
|
||||
for(var/L in border)
|
||||
var/turf/turf_to_disable = L
|
||||
SSair.remove_from_active(turf_to_disable) //stop processing turfs along the border to prevent runtimes, we return it in initTemplateBounds()
|
||||
turf_to_disable.atmos_adjacent_turfs?.Cut()
|
||||
|
||||
// Accept cached maps, but don't save them automatically - we don't want
|
||||
// ruins clogging up memory for the whole round.
|
||||
var/datum/parsed_map/parsed = cached_map || new(file(mappath))
|
||||
@@ -100,7 +107,7 @@
|
||||
//initialize things that are normally initialized after map load
|
||||
parsed.initTemplateBounds()
|
||||
|
||||
log_game("[name] loaded at at [T.x],[T.y],[T.z]")
|
||||
log_game("[name] loaded at [T.x],[T.y],[T.z]")
|
||||
return bounds
|
||||
|
||||
/datum/map_template/proc/get_affected_turfs(turf/T, centered = FALSE)
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
/obj/effect/baseturf_helper/LateInitialize()
|
||||
if(!baseturf_to_replace)
|
||||
baseturf_to_replace = typecacheof(/turf/open/space)
|
||||
baseturf_to_replace = typecacheof(list(/turf/open/space,/turf/baseturf_bottom))
|
||||
else if(!length(baseturf_to_replace))
|
||||
baseturf_to_replace = list(baseturf_to_replace = TRUE)
|
||||
else if(baseturf_to_replace[baseturf_to_replace[1]] != TRUE) // It's not associative
|
||||
@@ -45,7 +45,6 @@
|
||||
thing.PlaceOnBottom(null, baseturf)
|
||||
else if(baseturf_to_replace[thing.baseturfs])
|
||||
thing.assemble_baseturfs(baseturf)
|
||||
return
|
||||
else
|
||||
thing.PlaceOnBottom(null, baseturf)
|
||||
|
||||
@@ -107,16 +106,16 @@
|
||||
/obj/effect/mapping_helpers/airlock/cyclelink_helper/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!mapload)
|
||||
log_world("### MAP WARNING, [src] spawned outside of mapload!")
|
||||
log_mapping("[src] spawned outside of mapload!")
|
||||
return
|
||||
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
|
||||
if(airlock)
|
||||
if(airlock.cyclelinkeddir)
|
||||
log_world("### MAP WARNING, [src] at [AREACOORD(src)] tried to set [airlock] cyclelinkeddir, but it's already set!")
|
||||
log_mapping("[src] at [AREACOORD(src)] tried to set [airlock] cyclelinkeddir, but it's already set!")
|
||||
else
|
||||
airlock.cyclelinkeddir = dir
|
||||
else
|
||||
log_world("### MAP WARNING, [src] failed to find an airlock at [AREACOORD(src)]")
|
||||
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
|
||||
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/locked
|
||||
@@ -126,16 +125,16 @@
|
||||
/obj/effect/mapping_helpers/airlock/locked/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!mapload)
|
||||
log_world("### MAP WARNING, [src] spawned outside of mapload!")
|
||||
log_mapping("[src] spawned outside of mapload!")
|
||||
return
|
||||
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
|
||||
if(airlock)
|
||||
if(airlock.locked)
|
||||
log_world("### MAP WARNING, [src] at [AREACOORD(src)] tried to bolt [airlock] but it's already locked!")
|
||||
log_mapping("[src] at [AREACOORD(src)] tried to bolt [airlock] but it's already locked!")
|
||||
else
|
||||
airlock.locked = TRUE
|
||||
else
|
||||
log_world("### MAP WARNING, [src] failed to find an airlock at [AREACOORD(src)]")
|
||||
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
|
||||
|
||||
/obj/effect/mapping_helpers/airlock/unres
|
||||
name = "airlock unresctricted side helper"
|
||||
@@ -144,13 +143,13 @@
|
||||
/obj/effect/mapping_helpers/airlock/unres/Initialize(mapload)
|
||||
. = ..()
|
||||
if(!mapload)
|
||||
log_world("### MAP WARNING, [src] spawned outside of mapload!")
|
||||
log_mapping("[src] spawned outside of mapload!")
|
||||
return
|
||||
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
|
||||
if(airlock)
|
||||
airlock.unres_sides ^= dir
|
||||
else
|
||||
log_world("### MAP WARNING, [src] failed to find an airlock at [AREACOORD(src)]")
|
||||
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
|
||||
|
||||
|
||||
//needs to do its thing before spawn_rivers() is called
|
||||
@@ -164,17 +163,6 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
|
||||
var/turf/T = get_turf(src)
|
||||
T.flags_1 |= NO_LAVA_GEN_1
|
||||
|
||||
/// Adds the map it is on to the z_is_planet list
|
||||
/obj/effect/mapping_helpers/planet_z
|
||||
name = "planet z helper"
|
||||
layer = POINT_LAYER
|
||||
|
||||
/obj/effect/mapping_helpers/planet_z/Initialize()
|
||||
. = ..()
|
||||
var/datum/space_level/S = SSmapping.get_level(z)
|
||||
S.traits[ZTRAIT_PLANET] = TRUE
|
||||
|
||||
|
||||
//This helper applies components to things on the map directly.
|
||||
/obj/effect/mapping_helpers/component_injector
|
||||
name = "Component Injector"
|
||||
|
||||
@@ -8,18 +8,26 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new)
|
||||
var/list/attributes
|
||||
var/target_path
|
||||
|
||||
/datum/map_preloader/proc/setup(list/the_attributes, path)
|
||||
/world/proc/preloader_setup(list/the_attributes, path)
|
||||
if(the_attributes.len)
|
||||
GLOB.use_preloader = TRUE
|
||||
attributes = the_attributes
|
||||
target_path = path
|
||||
var/datum/map_preloader/preloader_local = GLOB._preloader
|
||||
preloader_local.attributes = the_attributes
|
||||
preloader_local.target_path = path
|
||||
|
||||
/datum/map_preloader/proc/load(atom/what)
|
||||
/world/proc/preloader_load(atom/what)
|
||||
GLOB.use_preloader = FALSE
|
||||
for(var/attribute in attributes)
|
||||
var/value = attributes[attribute]
|
||||
var/datum/map_preloader/preloader_local = GLOB._preloader
|
||||
for(var/attribute in preloader_local.attributes)
|
||||
var/value = preloader_local.attributes[attribute]
|
||||
if(islist(value))
|
||||
value = deepCopyList(value)
|
||||
#ifdef TESTING
|
||||
if(what.vars[attribute] == value)
|
||||
var/message = "<font color=green>[what.type]</font> at [AREACOORD(what)] - <b>VAR:</b> <font color=red>[attribute] = [isnull(value) ? "null" : (isnum(value) ? value : "\"[value]\"")]</font>"
|
||||
log_mapping("DIRTY VAR: [message]")
|
||||
GLOB.dirty_vars += message
|
||||
#endif
|
||||
what.vars[attribute] = value
|
||||
|
||||
/area/template_noop
|
||||
|
||||
@@ -306,8 +306,8 @@
|
||||
//first instance the /area and remove it from the members list
|
||||
index = members.len
|
||||
if(members[index] != /area/template_noop)
|
||||
GLOB._preloader.setup(members_attributes[index])//preloader for assigning set variables on atom creation
|
||||
var/atype = members[index]
|
||||
world.preloader_setup(members_attributes[index], atype)//preloader for assigning set variables on atom creation
|
||||
var/atom/instance = areaCache[atype]
|
||||
if (!instance)
|
||||
instance = GLOB.areas_by_type[atype]
|
||||
@@ -318,7 +318,7 @@
|
||||
instance.contents.Add(crds)
|
||||
|
||||
if(GLOB.use_preloader && instance)
|
||||
GLOB._preloader.load(instance)
|
||||
world.preloader_load(instance)
|
||||
|
||||
//then instance the /turf and, if multiple tiles are presents, simulates the DMM underlays piling effect
|
||||
|
||||
@@ -354,7 +354,7 @@
|
||||
|
||||
//Instance an atom at (x,y,z) and gives it the variables in attributes
|
||||
/datum/parsed_map/proc/instance_atom(path,list/attributes, turf/crds, no_changeturf, placeOnTop)
|
||||
GLOB._preloader.setup(attributes, path)
|
||||
world.preloader_setup(attributes, path)
|
||||
|
||||
if(crds)
|
||||
if(ispath(path, /turf))
|
||||
@@ -368,7 +368,7 @@
|
||||
. = create_atom(path, crds)//first preloader pass
|
||||
|
||||
if(GLOB.use_preloader && .)//second preloader pass, for those atoms that don't ..() in New()
|
||||
GLOB._preloader.load(.)
|
||||
world.preloader_load(.)
|
||||
|
||||
//custom CHECK_TICK here because we don't want things created while we're sleeping to not initialize
|
||||
if(TICK_CHECK)
|
||||
|
||||
@@ -926,7 +926,7 @@
|
||||
timer = world.time + create_delay + 1
|
||||
if(do_after(user, create_delay, target = T))
|
||||
var/old_name = T.name
|
||||
if(T.TerraformTurf(turf_type))
|
||||
if(T.TerraformTurf(turf_type, flags = CHANGETURF_INHERIT_AIR))
|
||||
user.visible_message("<span class='danger'>[user] turns \the [old_name] into [transform_string]!</span>")
|
||||
message_admins("[ADMIN_LOOKUPFLW(user)] fired the lava staff at [ADMIN_VERBOSEJMP(T)]")
|
||||
log_game("[key_name(user)] fired the lava staff at [AREACOORD(T)].")
|
||||
@@ -937,7 +937,7 @@
|
||||
qdel(L)
|
||||
else
|
||||
var/old_name = T.name
|
||||
if(T.TerraformTurf(reset_turf_type))
|
||||
if(T.TerraformTurf(reset_turf_type, flags = CHANGETURF_INHERIT_AIR))
|
||||
user.visible_message("<span class='danger'>[user] turns \the [old_name] into [reset_string]!</span>")
|
||||
timer = world.time + reset_cooldown
|
||||
playsound(T,'sound/magic/fireball.ogg', 200, 1)
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
/obj/machinery/mineral/ore_redemption/examine(mob/user)
|
||||
. = ..()
|
||||
if(in_range(user, src) || isobserver(user))
|
||||
. += "<span class='notice'>The status display reads: Smelting <b>[sheet_per_ore]</b> sheet(s) per piece of ore.<br>Ore pickup speed at <b>[ore_pickup_rate]</b>.</span>"
|
||||
. += "<span class='notice'>The status display reads: Smelting <b>[sheet_per_ore]</b> sheet(s) per piece of ore.<br>Reward point generation at <b>[point_upgrade*100]%</b>.<br>Ore pickup speed at <b>[ore_pickup_rate]</b>.</span>"
|
||||
|
||||
/obj/machinery/mineral/ore_redemption/proc/smelt_ore(obj/item/stack/ore/O)
|
||||
var/datum/component/material_container/mat_container = materials.mat_container
|
||||
@@ -63,7 +63,7 @@
|
||||
ore_buffer -= O
|
||||
|
||||
if(O && O.refined_type)
|
||||
points += O.points * O.amount
|
||||
points += O.points * point_upgrade * O.amount
|
||||
|
||||
var/material_amount = mat_container.get_item_material_amount(O)
|
||||
|
||||
|
||||
@@ -6,21 +6,21 @@
|
||||
return 2 //no ears
|
||||
|
||||
/mob/living/carbon/alien/hitby(atom/movable/AM, skipcatch, hitpush)
|
||||
..(AM, skipcatch = TRUE, hitpush = FALSE)
|
||||
return ..(AM, skipcatch = TRUE, hitpush = FALSE)
|
||||
|
||||
/mob/living/carbon/alien/can_embed(obj/item/I)
|
||||
return FALSE
|
||||
|
||||
/*Code for aliens attacking aliens. Because aliens act on a hivemind, I don't see them as very aggressive with each other.
|
||||
As such, they can either help or harm other aliens. Help works like the human help command while harm is a simple nibble.
|
||||
In all, this is a lot like the monkey code. /N
|
||||
*/
|
||||
/mob/living/carbon/alien/attack_alien(mob/living/carbon/alien/M)
|
||||
if(isturf(loc) && istype(loc.loc, /area/start))
|
||||
to_chat(M, "No attacking people at spawn, you jackass.")
|
||||
. = ..()
|
||||
if(!.) // the attack was blocked or was help/grab intent
|
||||
return
|
||||
|
||||
switch(M.a_intent)
|
||||
|
||||
if ("help")
|
||||
if (INTENT_HELP)
|
||||
if(!recoveringstam)
|
||||
resting = 0
|
||||
AdjustStun(-60)
|
||||
@@ -28,11 +28,7 @@ In all, this is a lot like the monkey code. /N
|
||||
AdjustUnconscious(-60)
|
||||
AdjustSleeping(-100)
|
||||
visible_message("<span class='notice'>[M.name] nuzzles [src] trying to wake [p_them()] up!</span>")
|
||||
|
||||
if ("grab")
|
||||
grabbedby(M)
|
||||
|
||||
else
|
||||
if(INTENT_DISARM, INTENT_HARM)
|
||||
if(health > 0)
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_BITE)
|
||||
playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1)
|
||||
@@ -50,28 +46,31 @@ In all, this is a lot like the monkey code. /N
|
||||
|
||||
|
||||
/mob/living/carbon/alien/attack_hand(mob/living/carbon/human/M)
|
||||
if(..()) //to allow surgery to return properly.
|
||||
return 0
|
||||
|
||||
. = ..()
|
||||
if(.) //To allow surgery to return properly.
|
||||
return
|
||||
switch(M.a_intent)
|
||||
if("help")
|
||||
if(INTENT_HELP)
|
||||
help_shake_act(M)
|
||||
if("grab")
|
||||
if(INTENT_GRAB)
|
||||
grabbedby(M)
|
||||
if ("harm")
|
||||
if (INTENT_HARM)
|
||||
if(HAS_TRAIT(M, TRAIT_PACIFISM))
|
||||
to_chat(M, "<span class='notice'>You don't want to hurt [src]!</span>")
|
||||
return TRUE
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
|
||||
return 1
|
||||
if("disarm")
|
||||
if(INTENT_DISARM)
|
||||
if(HAS_TRAIT(M, TRAIT_PACIFISM))
|
||||
to_chat(M, "<span class='notice'>You don't want to hurt [src]!</span>")
|
||||
return TRUE
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_DISARM)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
|
||||
/mob/living/carbon/alien/attack_paw(mob/living/carbon/monkey/M)
|
||||
if(..())
|
||||
if (stat != DEAD)
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
|
||||
apply_damage(rand(1, 3), BRUTE, affecting)
|
||||
. = ..()
|
||||
if(.) //successful monkey bite.
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
|
||||
apply_damage(rand(1, 3), BRUTE, affecting)
|
||||
|
||||
|
||||
/mob/living/carbon/alien/attack_animal(mob/living/simple_animal/M)
|
||||
@@ -93,13 +92,15 @@ In all, this is a lot like the monkey code. /N
|
||||
adjustStaminaLoss(damage)
|
||||
|
||||
/mob/living/carbon/alien/attack_slime(mob/living/simple_animal/slime/M)
|
||||
if(..()) //successful slime attack
|
||||
var/damage = rand(5, 35)
|
||||
if(M.is_adult)
|
||||
damage = rand(10, 40)
|
||||
adjustBruteLoss(damage)
|
||||
log_combat(M, src, "attacked")
|
||||
updatehealth()
|
||||
. = ..()
|
||||
if(!.) //unsuccessful slime attack
|
||||
return
|
||||
var/damage = rand(5, 35)
|
||||
if(M.is_adult)
|
||||
damage = rand(10, 40)
|
||||
adjustBruteLoss(damage)
|
||||
log_combat(M, src, "attacked")
|
||||
updatehealth()
|
||||
|
||||
/mob/living/carbon/alien/ex_act(severity, target, origin)
|
||||
if(origin && istype(origin, /datum/spacevine_mutation) && isvineimmune(src))
|
||||
|
||||
@@ -63,12 +63,7 @@
|
||||
if(A)
|
||||
if(isliving(A))
|
||||
var/mob/living/L = A
|
||||
var/blocked = FALSE
|
||||
if(ishuman(A))
|
||||
var/mob/living/carbon/human/H = A
|
||||
if(H.check_shields(src, 0, "the [name]", attack_type = LEAP_ATTACK))
|
||||
blocked = TRUE
|
||||
if(!blocked)
|
||||
if(!L.check_shields(src, 0, "the [name]", attack_type = LEAP_ATTACK))
|
||||
L.visible_message("<span class ='danger'>[src] pounces on [L]!</span>", "<span class ='userdanger'>[src] pounces on you!</span>")
|
||||
L.Knockdown(100)
|
||||
sleep(2)//Runtime prevention (infinite bump() calls on hulks)
|
||||
|
||||
@@ -5,9 +5,11 @@
|
||||
else
|
||||
..()
|
||||
|
||||
/mob/living/carbon/alien/humanoid/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0)
|
||||
/mob/living/carbon/alien/humanoid/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
|
||||
if(user.a_intent == INTENT_HARM)
|
||||
..(user, 1)
|
||||
. = ..(user, TRUE)
|
||||
if(.)
|
||||
return
|
||||
adjustBruteLoss(15)
|
||||
var/hitverb = "punched"
|
||||
if(mob_size < MOB_SIZE_LARGE)
|
||||
@@ -21,46 +23,46 @@
|
||||
return 1
|
||||
|
||||
/mob/living/carbon/alien/humanoid/attack_hand(mob/living/carbon/human/M)
|
||||
if(..())
|
||||
switch(M.a_intent)
|
||||
if ("harm")
|
||||
var/damage = rand(1, 9)
|
||||
if (prob(90))
|
||||
playsound(loc, "punch", 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has punched [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has punched [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
if ((stat != DEAD) && (damage > 9 || prob(5)))//Regular humans have a very small chance of knocking an alien down.
|
||||
Unconscious(40)
|
||||
visible_message("<span class='danger'>[M] has knocked [src] down!</span>", \
|
||||
"<span class='userdanger'>[M] has knocked [src] down!</span>")
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
|
||||
apply_damage(damage, BRUTE, affecting)
|
||||
log_combat(M, src, "attacked")
|
||||
. = ..()
|
||||
if(.) //To allow surgery to return properly.
|
||||
return
|
||||
switch(M.a_intent)
|
||||
if (INTENT_HARM)
|
||||
var/damage = rand(1, 9)
|
||||
if (prob(90))
|
||||
playsound(loc, "punch", 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has punched [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has punched [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
if ((stat != DEAD) && (damage > 9 || prob(5)))//Regular humans have a very small chance of knocking an alien down.
|
||||
Unconscious(40)
|
||||
visible_message("<span class='danger'>[M] has knocked [src] down!</span>", \
|
||||
"<span class='userdanger'>[M] has knocked [src] down!</span>")
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
|
||||
apply_damage(damage, BRUTE, affecting)
|
||||
log_combat(M, src, "attacked")
|
||||
else
|
||||
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has attempted to punch [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has attempted to punch [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
|
||||
if (INTENT_DISARM)
|
||||
if (!lying)
|
||||
if (prob(5))
|
||||
Unconscious(40)
|
||||
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
log_combat(M, src, "pushed")
|
||||
visible_message("<span class='danger'>[M] has pushed down [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has pushed down [src]!</span>")
|
||||
else
|
||||
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
visible_message("<span class='userdanger'>[M] has attempted to punch [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has attempted to punch [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
|
||||
if ("disarm")
|
||||
if (!lying)
|
||||
if (prob(5))
|
||||
Unconscious(40)
|
||||
if (prob(50))
|
||||
dropItemToGround(get_active_held_item())
|
||||
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
log_combat(M, src, "pushed")
|
||||
visible_message("<span class='danger'>[M] has pushed down [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has pushed down [src]!</span>")
|
||||
visible_message("<span class='danger'>[M] has disarmed [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has disarmed [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
else
|
||||
if (prob(50))
|
||||
dropItemToGround(get_active_held_item())
|
||||
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has disarmed [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has disarmed [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
else
|
||||
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
visible_message("<span class='userdanger'>[M] has attempted to disarm [src]!</span>",\
|
||||
"<span class='userdanger'>[M] has attempted to disarm [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
|
||||
|
||||
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has attempted to disarm [src]!</span>",\
|
||||
"<span class='userdanger'>[M] has attempted to disarm [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
|
||||
/mob/living/carbon/alien/humanoid/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
|
||||
if(!no_effect && !visual_effect_icon)
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
|
||||
|
||||
/mob/living/carbon/alien/larva/attack_hand(mob/living/carbon/human/M)
|
||||
if(..())
|
||||
var/damage = rand(1, 9)
|
||||
if (prob(90))
|
||||
playsound(loc, "punch", 25, 1, -1)
|
||||
log_combat(M, src, "attacked")
|
||||
visible_message("<span class='danger'>[M] has kicked [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has kicked [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
if ((stat != DEAD) && (damage > 4.9))
|
||||
Unconscious(rand(100,200))
|
||||
. = ..()
|
||||
if(. || M.a_intent == INTENT_HELP || M.a_intent == INTENT_GRAB)
|
||||
return
|
||||
var/damage = rand(1, 9)
|
||||
if (prob(90))
|
||||
playsound(loc, "punch", 25, 1, -1)
|
||||
log_combat(M, src, "attacked")
|
||||
visible_message("<span class='danger'>[M] has kicked [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has kicked [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
if ((stat != DEAD) && (damage > 4.9))
|
||||
Unconscious(rand(100,200))
|
||||
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
|
||||
apply_damage(damage, BRUTE, affecting)
|
||||
else
|
||||
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has attempted to kick [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has attempted to kick [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
|
||||
apply_damage(damage, BRUTE, affecting)
|
||||
else
|
||||
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has attempted to kick [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has attempted to kick [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
|
||||
/mob/living/carbon/alien/larva/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0)
|
||||
/mob/living/carbon/alien/larva/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
|
||||
if(user.a_intent == INTENT_HARM)
|
||||
..(user, 1)
|
||||
. = ..(user, TRUE)
|
||||
if(.)
|
||||
return
|
||||
playsound(loc, "punch", 25, 1, -1)
|
||||
visible_message("<span class='danger'>[user] has pummeled [src]!</span>", \
|
||||
"<span class='userdanger'>[user] has pummeled [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
adjustBruteLoss(5 + rand(1,9))
|
||||
new /datum/forced_movement(src, get_step_away(user,src, 30), 1)
|
||||
return 1
|
||||
|
||||
@@ -48,41 +48,42 @@
|
||||
if(affecting && affecting.dismemberable && affecting.get_damage() >= (affecting.max_damage - P.dismemberment))
|
||||
affecting.dismember(P.damtype)
|
||||
|
||||
/mob/living/carbon/proc/can_catch_item(skip_throw_mode_check)
|
||||
. = FALSE
|
||||
if(mind)
|
||||
if(mind.martial_art && mind.martial_art.dodge_chance == 100)
|
||||
return TRUE
|
||||
if(!skip_throw_mode_check && !in_throw_mode)
|
||||
/mob/living/carbon/catch_item(obj/item/I, skip_throw_mode_check = FALSE)
|
||||
. = ..()
|
||||
if(!HAS_TRAIT(src, TRAIT_AUTO_CATCH_ITEM) && !skip_throw_mode_check && !in_throw_mode)
|
||||
return
|
||||
if(get_active_held_item())
|
||||
if(get_active_held_item() || restrained())
|
||||
return
|
||||
if(restrained())
|
||||
return
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE)
|
||||
if(!skipcatch) //ugly, but easy
|
||||
if(can_catch_item())
|
||||
if(istype(AM, /obj/item))
|
||||
var/obj/item/I = AM
|
||||
if (mind)
|
||||
if (mind.martial_art && mind.martial_art.dodge_chance == 100) //autocatch for rising bass
|
||||
if (get_active_held_item())
|
||||
visible_message("<span class='warning'>[I] falls to the ground as [src] chops it out of the air!</span>")
|
||||
return 1
|
||||
if(!in_throw_mode)
|
||||
throw_mode_on()
|
||||
if(isturf(I.loc))
|
||||
I.attack_hand(src)
|
||||
if(get_active_held_item() == I) //if our attack_hand() picks up the item...
|
||||
visible_message("<span class='warning'>[src] catches [I]!</span>") //catch that sucker!
|
||||
throw_mode_off()
|
||||
return 1
|
||||
..()
|
||||
I.attack_hand(src)
|
||||
if(get_active_held_item() == I) //if our attack_hand() picks up the item...
|
||||
visible_message("<span class='warning'>[src] catches [I]!</span>") //catch that sucker!
|
||||
throw_mode_off()
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/embed_item(obj/item/I)
|
||||
throw_alert("embeddedobject", /obj/screen/alert/embeddedobject)
|
||||
var/obj/item/bodypart/L = pick(bodyparts)
|
||||
L.embedded_objects |= I
|
||||
I.add_mob_blood(src)//it embedded itself in you, of course it's bloody!
|
||||
I.forceMove(src)
|
||||
L.receive_damage(I.w_class*I.embedding.embedded_impact_pain_multiplier)
|
||||
visible_message("<span class='danger'>[I] embeds itself in [src]'s [L.name]!</span>","<span class='userdanger'>[I] embeds itself in your [L.name]!</span>")
|
||||
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "embedded", /datum/mood_event/embedded)
|
||||
|
||||
/mob/living/carbon/attacked_by(obj/item/I, mob/living/user)
|
||||
//CIT CHANGES START HERE - combatmode and resting checks
|
||||
var/totitemdamage = I.force
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/tempcarb = user
|
||||
if(!tempcarb.combatmode)
|
||||
totitemdamage *= 0.5
|
||||
if(user.resting)
|
||||
totitemdamage *= 0.5
|
||||
if(!combatmode)
|
||||
totitemdamage *= 1.5
|
||||
//CIT CHANGES END HERE
|
||||
if(user != src && check_shields(I, totitemdamage, "the [I.name]", MELEE_ATTACK, I.armour_penetration))
|
||||
return FALSE
|
||||
var/obj/item/bodypart/affecting
|
||||
if(user == src)
|
||||
affecting = get_bodypart(check_zone(user.zone_selected)) //we're self-mutilating! yay!
|
||||
@@ -93,17 +94,6 @@
|
||||
SEND_SIGNAL(I, COMSIG_ITEM_ATTACK_ZONE, src, user, affecting)
|
||||
send_item_attack_message(I, user, affecting.name)
|
||||
if(I.force)
|
||||
//CIT CHANGES START HERE - combatmode and resting checks
|
||||
var/totitemdamage = I.force
|
||||
if(iscarbon(user))
|
||||
var/mob/living/carbon/tempcarb = user
|
||||
if(!tempcarb.combatmode)
|
||||
totitemdamage *= 0.5
|
||||
if(user.resting)
|
||||
totitemdamage *= 0.5
|
||||
if(!combatmode)
|
||||
totitemdamage *= 1.5
|
||||
//CIT CHANGES END HERE
|
||||
apply_damage(totitemdamage, I.damtype, affecting) //CIT CHANGE - replaces I.force with totitemdamage
|
||||
if(I.damtype == BRUTE && affecting.status == BODYPART_ORGANIC)
|
||||
var/basebloodychance = affecting.brute_dam + totitemdamage
|
||||
@@ -137,7 +127,9 @@
|
||||
|
||||
//ATTACK HAND IGNORING PARENT RETURN VALUE
|
||||
/mob/living/carbon/attack_hand(mob/living/carbon/human/user)
|
||||
|
||||
. = ..()
|
||||
if(.) //was the attack blocked?
|
||||
return
|
||||
for(var/thing in diseases)
|
||||
var/datum/disease/D = thing
|
||||
if(D.spread_flags & DISEASE_SPREAD_CONTACT_SKIN)
|
||||
@@ -152,8 +144,7 @@
|
||||
if(user.a_intent == INTENT_HELP || user.a_intent == INTENT_DISARM)
|
||||
for(var/datum/surgery/S in surgeries)
|
||||
if(S.next_step(user, user.a_intent))
|
||||
return 1
|
||||
return 0
|
||||
return TRUE
|
||||
|
||||
|
||||
/mob/living/carbon/attack_paw(mob/living/carbon/monkey/M)
|
||||
@@ -173,7 +164,8 @@
|
||||
help_shake_act(M)
|
||||
return 0
|
||||
|
||||
if(..()) //successful monkey bite.
|
||||
. = ..()
|
||||
if(.) //successful monkey bite.
|
||||
for(var/thing in M.diseases)
|
||||
var/datum/disease/D = thing
|
||||
ForceContractDisease(D)
|
||||
@@ -181,26 +173,27 @@
|
||||
|
||||
|
||||
/mob/living/carbon/attack_slime(mob/living/simple_animal/slime/M)
|
||||
if(..()) //successful slime attack
|
||||
if(M.powerlevel > 0)
|
||||
var/stunprob = M.powerlevel * 7 + 10 // 17 at level 1, 80 at level 10
|
||||
if(prob(stunprob))
|
||||
M.powerlevel -= 3
|
||||
if(M.powerlevel < 0)
|
||||
M.powerlevel = 0
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(M.powerlevel > 0)
|
||||
var/stunprob = M.powerlevel * 7 + 10 // 17 at level 1, 80 at level 10
|
||||
if(prob(stunprob))
|
||||
M.powerlevel -= 3
|
||||
if(M.powerlevel < 0)
|
||||
M.powerlevel = 0
|
||||
|
||||
visible_message("<span class='danger'>The [M.name] has shocked [src]!</span>", \
|
||||
"<span class='userdanger'>The [M.name] has shocked [src]!</span>")
|
||||
visible_message("<span class='danger'>The [M.name] has shocked [src]!</span>", \
|
||||
"<span class='userdanger'>The [M.name] has shocked [src]!</span>")
|
||||
|
||||
do_sparks(5, TRUE, src)
|
||||
var/power = M.powerlevel + rand(0,3)
|
||||
Knockdown(power*20)
|
||||
if(stuttering < power)
|
||||
stuttering = power
|
||||
if (prob(stunprob) && M.powerlevel >= 8)
|
||||
adjustFireLoss(M.powerlevel * rand(6,10))
|
||||
updatehealth()
|
||||
return 1
|
||||
do_sparks(5, TRUE, src)
|
||||
var/power = M.powerlevel + rand(0,3)
|
||||
Knockdown(power*20)
|
||||
if(stuttering < power)
|
||||
stuttering = power
|
||||
if (prob(stunprob) && M.powerlevel >= 8)
|
||||
adjustFireLoss(M.powerlevel * rand(6,10))
|
||||
updatehealth()
|
||||
|
||||
/mob/living/carbon/proc/dismembering_strike(mob/living/attacker, dam_zone)
|
||||
if(!attacker.limb_destroyer)
|
||||
@@ -332,12 +325,12 @@
|
||||
|
||||
else
|
||||
return
|
||||
|
||||
|
||||
else if(check_zone(M.zone_selected) == "r_arm" || check_zone(M.zone_selected) == "l_arm")
|
||||
M.visible_message( \
|
||||
"<span class='notice'>[M] shakes [src]'s hand.</span>", \
|
||||
"<span class='notice'>You shake [src]'s hand.</span>", )
|
||||
|
||||
|
||||
else
|
||||
M.visible_message("<span class='notice'>[M] hugs [src] to make [p_them()] feel better!</span>", \
|
||||
"<span class='notice'>You hug [src] to make [p_them()] feel better!</span>")
|
||||
|
||||
@@ -67,66 +67,36 @@
|
||||
P.setAngle(rand(0, 360))//SHING
|
||||
return FALSE
|
||||
|
||||
if(!(P.original == src && P.firer == src)) //can't block or reflect when shooting yourself
|
||||
if(P.is_reflectable)
|
||||
if(check_reflect(def_zone)) // Checks if you've passed a reflection% check
|
||||
visible_message("<span class='danger'>The [P.name] gets reflected by [src]!</span>", \
|
||||
"<span class='userdanger'>The [P.name] gets reflected by [src]!</span>")
|
||||
// Find a turf near or on the original location to bounce to
|
||||
if(P.starting)
|
||||
var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
|
||||
var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
|
||||
var/turf/curloc = get_turf(src)
|
||||
return ..()
|
||||
|
||||
// redirect the projectile
|
||||
P.original = locate(new_x, new_y, P.z)
|
||||
P.starting = curloc
|
||||
P.firer = src
|
||||
P.yo = new_y - curloc.y
|
||||
P.xo = new_x - curloc.x
|
||||
var/new_angle_s = P.Angle + rand(120,240)
|
||||
while(new_angle_s > 180) // Translate to regular projectile degrees
|
||||
new_angle_s -= 360
|
||||
P.setAngle(new_angle_s)
|
||||
/mob/living/carbon/human/check_reflect(def_zone)
|
||||
if(wear_suit?.IsReflect(def_zone))
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
return -1 // complete projectile permutation
|
||||
|
||||
if(check_shields(P, P.damage, "the [P.name]", PROJECTILE_ATTACK, P.armour_penetration))
|
||||
P.on_hit(src, 100, def_zone)
|
||||
return 2
|
||||
|
||||
return (..(P , def_zone))
|
||||
|
||||
/mob/living/carbon/human/proc/check_reflect(def_zone) //Reflection checks for anything in your l_hand, r_hand, or wear_suit based on the reflection chance of the object
|
||||
if(wear_suit)
|
||||
if(wear_suit.IsReflect(def_zone) == 1)
|
||||
return 1
|
||||
for(var/obj/item/I in held_items)
|
||||
if(I.IsReflect(def_zone) == 1)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/mob/living/carbon/human/proc/check_shields(atom/AM, var/damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0)
|
||||
/mob/living/carbon/human/check_shields(atom/AM, damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
var/block_chance_modifier = round(damage / -3)
|
||||
|
||||
for(var/obj/item/I in held_items)
|
||||
if(!istype(I, /obj/item/clothing))
|
||||
var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example
|
||||
if(I.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type))
|
||||
return 1
|
||||
if(wear_suit)
|
||||
var/final_block_chance = wear_suit.block_chance - (CLAMP((armour_penetration-wear_suit.armour_penetration)/2,0,100)) + block_chance_modifier
|
||||
if(wear_suit.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type))
|
||||
return 1
|
||||
return TRUE
|
||||
if(w_uniform)
|
||||
var/final_block_chance = w_uniform.block_chance - (CLAMP((armour_penetration-w_uniform.armour_penetration)/2,0,100)) + block_chance_modifier
|
||||
if(w_uniform.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type))
|
||||
return 1
|
||||
return TRUE
|
||||
if(wear_neck)
|
||||
var/final_block_chance = wear_neck.block_chance - (CLAMP((armour_penetration-wear_neck.armour_penetration)/2,0,100)) + block_chance_modifier
|
||||
if(wear_neck.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type))
|
||||
return 1
|
||||
return 0
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/carbon/human/can_embed(obj/item/I)
|
||||
if(I.get_sharpness() || is_pointed(I) || is_type_in_typecache(I, GLOB.can_embed_types))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/carbon/human/proc/check_block()
|
||||
if(mind)
|
||||
@@ -135,39 +105,7 @@
|
||||
return FALSE
|
||||
|
||||
/mob/living/carbon/human/hitby(atom/movable/AM, skipcatch = FALSE, hitpush = TRUE, blocked = FALSE)
|
||||
if(dna && dna.species)
|
||||
var/spec_return = dna.species.spec_hitby(AM, src)
|
||||
if(spec_return)
|
||||
return spec_return
|
||||
var/obj/item/I
|
||||
var/throwpower = 30
|
||||
if(istype(AM, /obj/item))
|
||||
I = AM
|
||||
throwpower = I.throwforce
|
||||
if(I.thrownby == src) //No throwing stuff at yourself to trigger hit reactions
|
||||
return ..()
|
||||
if(check_shields(AM, throwpower, "\the [AM.name]", THROWN_PROJECTILE_ATTACK))
|
||||
hitpush = FALSE
|
||||
skipcatch = TRUE
|
||||
blocked = TRUE
|
||||
else if(I)
|
||||
if(I.throw_speed >= EMBED_THROWSPEED_THRESHOLD && !(mind.martial_art && mind.martial_art.dodge_chance == 100))
|
||||
if(can_embed(I))
|
||||
if(prob(I.embedding.embed_chance) && !HAS_TRAIT(src, TRAIT_PIERCEIMMUNE))
|
||||
throw_alert("embeddedobject", /obj/screen/alert/embeddedobject)
|
||||
var/obj/item/bodypart/L = pick(bodyparts)
|
||||
L.embedded_objects |= I
|
||||
I.add_mob_blood(src)//it embedded itself in you, of course it's bloody!
|
||||
I.forceMove(src)
|
||||
L.receive_damage(I.w_class*I.embedding.embedded_impact_pain_multiplier)
|
||||
visible_message("<span class='danger'>[I] embeds itself in [src]'s [L.name]!</span>","<span class='userdanger'>[I] embeds itself in your [L.name]!</span>")
|
||||
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "embedded", /datum/mood_event/embedded)
|
||||
hitpush = FALSE
|
||||
skipcatch = TRUE //can't catch the now embedded item
|
||||
if (mind)
|
||||
if (mind.martial_art && mind.martial_art.dodge_chance == 100)
|
||||
skipcatch = FALSE
|
||||
return ..()
|
||||
return dna?.species?.spec_hitby(AM, src) || ..()
|
||||
|
||||
/mob/living/carbon/human/grabbedby(mob/living/carbon/user, supress_message = 0)
|
||||
if(user == src && pulling && !pulling.anchored && grab_state >= GRAB_AGGRESSIVE && (HAS_TRAIT(src, TRAIT_FAT)) && ismonkey(pulling))
|
||||
@@ -201,12 +139,12 @@
|
||||
return dna.species.spec_attacked_by(I, user, affecting, a_intent, src)
|
||||
|
||||
|
||||
/mob/living/carbon/human/attack_hulk(mob/living/carbon/human/user, does_attack_animation = 0)
|
||||
/mob/living/carbon/human/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
|
||||
if(user.a_intent == INTENT_HARM)
|
||||
var/hulk_verb = pick("smash","pummel")
|
||||
if(check_shields(user, 15, "the [hulk_verb]ing"))
|
||||
. = ..(user, TRUE)
|
||||
if(.)
|
||||
return
|
||||
..(user, 1)
|
||||
var/hulk_verb = pick("smash","pummel")
|
||||
playsound(loc, user.dna.species.attack_sound, 25, 1, -1)
|
||||
var/message = "[user] has [hulk_verb]ed [src]!"
|
||||
visible_message("<span class='danger'>[message]</span>", \
|
||||
@@ -215,7 +153,8 @@
|
||||
return 1
|
||||
|
||||
/mob/living/carbon/human/attack_hand(mob/user)
|
||||
if(..()) //to allow surgery to return properly.
|
||||
. = ..()
|
||||
if(.) //To allow surgery to return properly.
|
||||
return
|
||||
if(ishuman(user))
|
||||
var/mob/living/carbon/human/H = user
|
||||
@@ -227,8 +166,7 @@
|
||||
if(!affecting)
|
||||
affecting = get_bodypart(BODY_ZONE_CHEST)
|
||||
if(M.a_intent == INTENT_HELP)
|
||||
..() //shaking
|
||||
return 0
|
||||
return ..() //shaking
|
||||
|
||||
if(M.a_intent == INTENT_DISARM) //Always drop item in hand, if no item, get stunned instead.
|
||||
var/obj/item/I = get_active_held_item()
|
||||
@@ -249,78 +187,69 @@
|
||||
if(can_inject(M, 1, affecting))//Thick suits can stop monkey bites.
|
||||
if(..()) //successful monkey bite, this handles disease contraction.
|
||||
var/damage = rand(1, 3)
|
||||
if(check_shields(M, damage, "the [M.name]"))
|
||||
return 0
|
||||
if(stat != DEAD)
|
||||
apply_damage(damage, BRUTE, affecting, run_armor_check(affecting, "melee"))
|
||||
apply_damage(damage, BRUTE, affecting, run_armor_check(affecting, "melee"))
|
||||
return 1
|
||||
|
||||
/mob/living/carbon/human/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
if(check_shields(M, 0, "the M.name"))
|
||||
visible_message("<span class='danger'>[M] attempted to touch [src]!</span>")
|
||||
return 0
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
if(M.a_intent == INTENT_HARM)
|
||||
if (w_uniform)
|
||||
w_uniform.add_fingerprint(M)
|
||||
var/damage = prob(90) ? 20 : 0
|
||||
if(!damage)
|
||||
playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has lunged at [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has lunged at [src]!</span>")
|
||||
return 0
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
|
||||
if(!affecting)
|
||||
affecting = get_bodypart(BODY_ZONE_CHEST)
|
||||
var/armor_block = run_armor_check(affecting, "melee", null, null,10)
|
||||
|
||||
if(..())
|
||||
if(M.a_intent == INTENT_HARM)
|
||||
if (w_uniform)
|
||||
w_uniform.add_fingerprint(M)
|
||||
var/damage = prob(90) ? 20 : 0
|
||||
if(!damage)
|
||||
playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has lunged at [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has lunged at [src]!</span>")
|
||||
return 0
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
|
||||
if(!affecting)
|
||||
affecting = get_bodypart(BODY_ZONE_CHEST)
|
||||
var/armor_block = run_armor_check(affecting, "melee", null, null,10)
|
||||
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has slashed at [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has slashed at [src]!</span>")
|
||||
log_combat(M, src, "attacked")
|
||||
if(!dismembering_strike(M, M.zone_selected)) //Dismemberment successful
|
||||
return 1
|
||||
apply_damage(damage, BRUTE, affecting, armor_block)
|
||||
|
||||
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has slashed at [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has slashed at [src]!</span>")
|
||||
log_combat(M, src, "attacked")
|
||||
if(!dismembering_strike(M, M.zone_selected)) //Dismemberment successful
|
||||
return 1
|
||||
apply_damage(damage, BRUTE, affecting, armor_block)
|
||||
|
||||
if(M.a_intent == INTENT_DISARM) //Always drop item in hand, if no item, get stun instead.
|
||||
var/obj/item/I = get_active_held_item()
|
||||
if(I && dropItemToGround(I))
|
||||
playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] disarmed [src]!</span>", \
|
||||
"<span class='userdanger'>[M] disarmed [src]!</span>")
|
||||
if(M.a_intent == INTENT_DISARM) //Always drop item in hand, if no item, get stun instead.
|
||||
var/obj/item/I = get_active_held_item()
|
||||
if(I && dropItemToGround(I))
|
||||
playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] disarmed [src]!</span>", \
|
||||
"<span class='userdanger'>[M] disarmed [src]!</span>")
|
||||
else
|
||||
playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1)
|
||||
if(!lying) //CITADEL EDIT
|
||||
Knockdown(100, TRUE, FALSE, 30, 25)
|
||||
else
|
||||
playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1)
|
||||
if(!lying) //CITADEL EDIT
|
||||
Knockdown(100, TRUE, FALSE, 30, 25)
|
||||
else
|
||||
Knockdown(100)
|
||||
log_combat(M, src, "tackled")
|
||||
visible_message("<span class='danger'>[M] has tackled down [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has tackled down [src]!</span>")
|
||||
|
||||
Knockdown(100)
|
||||
log_combat(M, src, "tackled")
|
||||
visible_message("<span class='danger'>[M] has tackled down [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has tackled down [src]!</span>")
|
||||
|
||||
/mob/living/carbon/human/attack_larva(mob/living/carbon/alien/larva/L)
|
||||
|
||||
if(..()) //successful larva bite.
|
||||
var/damage = rand(1, 3)
|
||||
if(check_shields(L, damage, "the [L.name]"))
|
||||
return 0
|
||||
if(stat != DEAD)
|
||||
L.amount_grown = min(L.amount_grown + damage, L.max_grown)
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(L.zone_selected))
|
||||
if(!affecting)
|
||||
affecting = get_bodypart(BODY_ZONE_CHEST)
|
||||
var/armor_block = run_armor_check(affecting, "melee")
|
||||
apply_damage(damage, BRUTE, affecting, armor_block)
|
||||
. = ..()
|
||||
if(!.) //unsuccessful larva bite.
|
||||
return
|
||||
var/damage = rand(1, 3)
|
||||
if(stat != DEAD)
|
||||
L.amount_grown = min(L.amount_grown + damage, L.max_grown)
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(L.zone_selected))
|
||||
if(!affecting)
|
||||
affecting = get_bodypart(BODY_ZONE_CHEST)
|
||||
var/armor_block = run_armor_check(affecting, "melee")
|
||||
apply_damage(damage, BRUTE, affecting, armor_block)
|
||||
|
||||
|
||||
/mob/living/carbon/human/attack_animal(mob/living/simple_animal/M)
|
||||
. = ..()
|
||||
if(.)
|
||||
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
|
||||
if(check_shields(M, damage, "the [M.name]", MELEE_ATTACK, M.armour_penetration))
|
||||
return FALSE
|
||||
var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
|
||||
if(!dam_zone) //Dismemberment successful
|
||||
return TRUE
|
||||
@@ -332,23 +261,22 @@
|
||||
|
||||
|
||||
/mob/living/carbon/human/attack_slime(mob/living/simple_animal/slime/M)
|
||||
if(..()) //successful slime attack
|
||||
var/damage = rand(5, 25)
|
||||
if(M.is_adult)
|
||||
damage = rand(10, 35)
|
||||
. = ..()
|
||||
if(!.) //unsuccessful slime attack
|
||||
return
|
||||
var/damage = rand(5, 25)
|
||||
if(M.is_adult)
|
||||
damage = rand(10, 35)
|
||||
|
||||
if(check_shields(M, damage, "the [M.name]"))
|
||||
return 0
|
||||
var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
|
||||
if(!dam_zone) //Dismemberment successful
|
||||
return 1
|
||||
|
||||
var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
|
||||
if(!dam_zone) //Dismemberment successful
|
||||
return 1
|
||||
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone))
|
||||
if(!affecting)
|
||||
affecting = get_bodypart(BODY_ZONE_CHEST)
|
||||
var/armor_block = run_armor_check(affecting, "melee")
|
||||
apply_damage(damage, BRUTE, affecting, armor_block)
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone))
|
||||
if(!affecting)
|
||||
affecting = get_bodypart(BODY_ZONE_CHEST)
|
||||
var/armor_block = run_armor_check(affecting, "melee")
|
||||
apply_damage(damage, BRUTE, affecting, armor_block)
|
||||
|
||||
/mob/living/carbon/human/mech_melee_attack(obj/mecha/M)
|
||||
if(M.occupant.a_intent == INTENT_HARM)
|
||||
|
||||
@@ -1685,11 +1685,6 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
|
||||
attacker_style = M.mind.martial_art
|
||||
if(attacker_style?.pacifism_check && HAS_TRAIT(M, TRAIT_PACIFISM)) // most martial arts are quite harmful, alas.
|
||||
attacker_style = null
|
||||
if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(M, 0, M.name, attack_type = UNARMED_ATTACK))
|
||||
log_combat(M, H, "attempted to touch")
|
||||
H.visible_message("<span class='warning'>[M] attempted to touch [H]!</span>")
|
||||
return 0
|
||||
SEND_SIGNAL(M, COMSIG_MOB_ATTACK_HAND, M, H, attacker_style)
|
||||
switch(M.a_intent)
|
||||
if("help")
|
||||
help(M, H, attacker_style)
|
||||
|
||||
@@ -369,6 +369,23 @@
|
||||
retaliate(L)
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/monkey/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
if(M.a_intent == INTENT_HARM && prob(MONKEY_RETALIATE_HARM_PROB))
|
||||
retaliate(M)
|
||||
else if(M.a_intent == INTENT_DISARM && prob(MONKEY_RETALIATE_DISARM_PROB))
|
||||
retaliate(M)
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/monkey/attack_larva(mob/living/carbon/alien/larva/L)
|
||||
if(L.a_intent == INTENT_HARM && prob(MONKEY_RETALIATE_HARM_PROB))
|
||||
retaliate(L)
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/monkey/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
|
||||
if(user.a_intent == INTENT_HARM && prob(MONKEY_RETALIATE_HARM_PROB))
|
||||
retaliate(user)
|
||||
return ..()
|
||||
|
||||
/mob/living/carbon/monkey/attack_paw(mob/living/L)
|
||||
if(L.a_intent == INTENT_HARM && prob(MONKEY_RETALIATE_HARM_PROB))
|
||||
retaliate(L)
|
||||
|
||||
@@ -6,37 +6,55 @@
|
||||
..()
|
||||
|
||||
/mob/living/carbon/monkey/attack_paw(mob/living/M)
|
||||
if(..()) //successful monkey bite.
|
||||
var/dam_zone = pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone))
|
||||
if(!affecting)
|
||||
affecting = get_bodypart(BODY_ZONE_CHEST)
|
||||
if(M.limb_destroyer)
|
||||
dismembering_strike(M, affecting.body_zone)
|
||||
if(stat != DEAD)
|
||||
var/dmg = rand(1, 5)
|
||||
apply_damage(dmg, BRUTE, affecting)
|
||||
. = ..()
|
||||
if(!.) //unsuccessful monkey bite.
|
||||
return
|
||||
var/dam_zone = pick(BODY_ZONE_CHEST, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone))
|
||||
if(!affecting)
|
||||
affecting = get_bodypart(BODY_ZONE_CHEST)
|
||||
if(M.limb_destroyer)
|
||||
dismembering_strike(M, affecting.body_zone)
|
||||
var/dmg = rand(1, 5)
|
||||
apply_damage(dmg, BRUTE, affecting)
|
||||
|
||||
/mob/living/carbon/monkey/attack_larva(mob/living/carbon/alien/larva/L)
|
||||
if(..()) //successful larva bite.
|
||||
var/damage = rand(1, 3)
|
||||
if(stat != DEAD)
|
||||
L.amount_grown = min(L.amount_grown + damage, L.max_grown)
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(L.zone_selected))
|
||||
if(!affecting)
|
||||
affecting = get_bodypart(BODY_ZONE_CHEST)
|
||||
apply_damage(damage, BRUTE, affecting)
|
||||
. = ..()
|
||||
if(!.) //unsuccessful larva bite
|
||||
return
|
||||
var/damage = rand(1, 3)
|
||||
if(stat != DEAD)
|
||||
L.amount_grown = min(L.amount_grown + damage, L.max_grown)
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(L.zone_selected))
|
||||
if(!affecting)
|
||||
affecting = get_bodypart(BODY_ZONE_CHEST)
|
||||
apply_damage(damage, BRUTE, affecting)
|
||||
|
||||
/mob/living/carbon/monkey/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
|
||||
. = ..(user, TRUE)
|
||||
if(.)
|
||||
return
|
||||
var/hulk_verb = pick("smash","pummel")
|
||||
playsound(loc, user.dna.species.attack_sound, 25, 1, -1)
|
||||
var/message = "[user] has [hulk_verb]ed [src]!"
|
||||
visible_message("<span class='danger'>[message]</span>", \
|
||||
"<span class='userdanger'>[message]</span>")
|
||||
adjustBruteLoss(15)
|
||||
return TRUE
|
||||
|
||||
/mob/living/carbon/monkey/attack_hand(mob/living/carbon/human/M)
|
||||
if(..()) //To allow surgery to return properly.
|
||||
. = ..()
|
||||
if(.) //To allow surgery to return properly.
|
||||
return
|
||||
|
||||
switch(M.a_intent)
|
||||
if("help")
|
||||
if(INTENT_HELP)
|
||||
help_shake_act(M)
|
||||
if("grab")
|
||||
if(INTENT_GRAB)
|
||||
grabbedby(M)
|
||||
if("harm")
|
||||
if(INTENT_HARM)
|
||||
if(HAS_TRAIT(M, TRAIT_PACIFISM))
|
||||
to_chat(M, "<span class='notice'>You don't want to hurt [src]!</span>")
|
||||
return
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
|
||||
if (prob(75))
|
||||
visible_message("<span class='danger'>[M] has punched [name]!</span>", \
|
||||
@@ -60,7 +78,7 @@
|
||||
playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has attempted to punch [name]!</span>", \
|
||||
"<span class='userdanger'>[M] has attempted to punch [name]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
if("disarm")
|
||||
if(INTENT_DISARM)
|
||||
if(!IsUnconscious())
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_DISARM)
|
||||
if (prob(25))
|
||||
@@ -74,50 +92,51 @@
|
||||
visible_message("<span class='danger'>[M] has disarmed [src]!</span>", "<span class='userdanger'>[M] has disarmed [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
|
||||
/mob/living/carbon/monkey/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
if(..()) //if harm or disarm intent.
|
||||
if (M.a_intent == INTENT_HARM)
|
||||
if ((prob(95) && health > 0))
|
||||
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
|
||||
var/damage = rand(15, 30)
|
||||
if (damage >= 25)
|
||||
damage = rand(20, 40)
|
||||
if(AmountUnconscious() < 300)
|
||||
Unconscious(rand(200, 300))
|
||||
visible_message("<span class='danger'>[M] has wounded [name]!</span>", \
|
||||
"<span class='userdanger'>[M] has wounded [name]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
else
|
||||
visible_message("<span class='danger'>[M] has slashed [name]!</span>", \
|
||||
"<span class='userdanger'>[M] has slashed [name]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
|
||||
log_combat(M, src, "attacked")
|
||||
if(!affecting)
|
||||
affecting = get_bodypart(BODY_ZONE_CHEST)
|
||||
if(!dismembering_strike(M, affecting.body_zone)) //Dismemberment successful
|
||||
return 1
|
||||
apply_damage(damage, BRUTE, affecting)
|
||||
|
||||
. = ..()
|
||||
if(!.) // the attack was blocked or was help/grab intent
|
||||
return
|
||||
if (M.a_intent == INTENT_HARM)
|
||||
if ((prob(95) && health > 0))
|
||||
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
|
||||
var/damage = rand(15, 30)
|
||||
if (damage >= 25)
|
||||
damage = rand(20, 40)
|
||||
if(AmountUnconscious() < 300)
|
||||
Unconscious(rand(200, 300))
|
||||
visible_message("<span class='danger'>[M] has wounded [name]!</span>", \
|
||||
"<span class='userdanger'>[M] has wounded [name]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
else
|
||||
playsound(loc, 'sound/weapons/slashmiss.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has attempted to lunge at [name]!</span>", \
|
||||
"<span class='userdanger'>[M] has attempted to lunge at [name]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
visible_message("<span class='danger'>[M] has slashed [name]!</span>", \
|
||||
"<span class='userdanger'>[M] has slashed [name]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
|
||||
if (M.a_intent == INTENT_DISARM)
|
||||
var/obj/item/I = null
|
||||
playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1)
|
||||
if(prob(95))
|
||||
Knockdown(20)
|
||||
visible_message("<span class='danger'>[M] has tackled down [name]!</span>", \
|
||||
"<span class='userdanger'>[M] has tackled down [name]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(M.zone_selected))
|
||||
log_combat(M, src, "attacked")
|
||||
if(!affecting)
|
||||
affecting = get_bodypart(BODY_ZONE_CHEST)
|
||||
if(!dismembering_strike(M, affecting.body_zone)) //Dismemberment successful
|
||||
return 1
|
||||
apply_damage(damage, BRUTE, affecting)
|
||||
|
||||
else
|
||||
playsound(loc, 'sound/weapons/slashmiss.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has attempted to lunge at [name]!</span>", \
|
||||
"<span class='userdanger'>[M] has attempted to lunge at [name]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
|
||||
else
|
||||
var/obj/item/I = null
|
||||
playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1)
|
||||
if(prob(95))
|
||||
Knockdown(20)
|
||||
visible_message("<span class='danger'>[M] has tackled down [name]!</span>", \
|
||||
"<span class='userdanger'>[M] has tackled down [name]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
else
|
||||
I = get_active_held_item()
|
||||
if(dropItemToGround(I))
|
||||
visible_message("<span class='danger'>[M] has disarmed [name]!</span>", "<span class='userdanger'>[M] has disarmed [name]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
else
|
||||
I = get_active_held_item()
|
||||
if(dropItemToGround(I))
|
||||
visible_message("<span class='danger'>[M] has disarmed [name]!</span>", "<span class='userdanger'>[M] has disarmed [name]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
else
|
||||
I = null
|
||||
log_combat(M, src, "disarmed", "[I ? " removing \the [I]" : ""]")
|
||||
updatehealth()
|
||||
|
||||
I = null
|
||||
log_combat(M, src, "disarmed", "[I ? " removing \the [I]" : ""]")
|
||||
updatehealth()
|
||||
|
||||
/mob/living/carbon/monkey/attack_animal(mob/living/simple_animal/M)
|
||||
. = ..()
|
||||
@@ -132,17 +151,19 @@
|
||||
apply_damage(damage, M.melee_damage_type, affecting)
|
||||
|
||||
/mob/living/carbon/monkey/attack_slime(mob/living/simple_animal/slime/M)
|
||||
if(..()) //successful slime attack
|
||||
var/damage = rand(5, 35)
|
||||
if(M.is_adult)
|
||||
damage = rand(20, 40)
|
||||
var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
|
||||
if(!dam_zone) //Dismemberment successful
|
||||
return 1
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone))
|
||||
if(!affecting)
|
||||
affecting = get_bodypart(BODY_ZONE_CHEST)
|
||||
apply_damage(damage, BRUTE, affecting)
|
||||
. = ..()
|
||||
if(!.) //unsuccessful slime attack
|
||||
return
|
||||
var/damage = rand(5, 35)
|
||||
if(M.is_adult)
|
||||
damage = rand(20, 40)
|
||||
var/dam_zone = dismembering_strike(M, pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG))
|
||||
if(!dam_zone) //Dismemberment successful
|
||||
return 1
|
||||
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(dam_zone))
|
||||
if(!affecting)
|
||||
affecting = get_bodypart(BODY_ZONE_CHEST)
|
||||
apply_damage(damage, BRUTE, affecting)
|
||||
|
||||
/mob/living/carbon/monkey/acid_act(acidpwr, acid_volume, bodyzone_hit)
|
||||
. = 1
|
||||
|
||||
@@ -36,7 +36,50 @@
|
||||
/mob/living/proc/on_hit(obj/item/projectile/P)
|
||||
return
|
||||
|
||||
/mob/living/proc/check_shields(atom/AM, damage, attack_text = "the attack", attack_type = MELEE_ATTACK, armour_penetration = 0)
|
||||
var/block_chance_modifier = round(damage / -3)
|
||||
for(var/obj/item/I in held_items)
|
||||
if(!istype(I, /obj/item/clothing))
|
||||
var/final_block_chance = I.block_chance - (CLAMP((armour_penetration-I.armour_penetration)/2,0,100)) + block_chance_modifier //So armour piercing blades can still be parried by other blades, for example
|
||||
if(I.hit_reaction(src, AM, attack_text, final_block_chance, damage, attack_type))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/proc/check_reflect(def_zone) //Reflection checks for anything in your hands, based on the reflection chance of the object(s)
|
||||
for(var/obj/item/I in held_items)
|
||||
if(I.IsReflect(def_zone))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/proc/reflect_bullet_check(obj/item/projectile/P, def_zone)
|
||||
if(P.is_reflectable && check_reflect(def_zone)) // Checks if you've passed a reflection% check
|
||||
visible_message("<span class='danger'>The [P.name] gets reflected by [src]!</span>", \
|
||||
"<span class='userdanger'>The [P.name] gets reflected by [src]!</span>")
|
||||
// Find a turf near or on the original location to bounce to
|
||||
if(P.starting)
|
||||
var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
|
||||
var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
|
||||
var/turf/curloc = get_turf(src)
|
||||
// redirect the projectile
|
||||
P.original = locate(new_x, new_y, P.z)
|
||||
P.starting = curloc
|
||||
P.firer = src
|
||||
P.yo = new_y - curloc.y
|
||||
P.xo = new_x - curloc.x
|
||||
var/new_angle_s = P.Angle + rand(120,240)
|
||||
while(new_angle_s > 180) // Translate to regular projectile degrees
|
||||
new_angle_s -= 360
|
||||
P.setAngle(new_angle_s)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/mob/living/bullet_act(obj/item/projectile/P, def_zone)
|
||||
if(P.original != src || P.firer != src) //try to block or reflect the bullet, can't do so when shooting oneself
|
||||
if(reflect_bullet_check(P, def_zone))
|
||||
return -1 // complete projectile permutation
|
||||
if(check_shields(P, P.damage, "the [P.name]", PROJECTILE_ATTACK, P.armour_penetration))
|
||||
P.on_hit(src, 100, def_zone)
|
||||
return 2
|
||||
var/armor = run_armor_check(def_zone, P.flag, null, null, P.armour_penetration, null)
|
||||
if(!P.nodamage)
|
||||
apply_damage(P.damage, P.damage_type, def_zone, armor)
|
||||
@@ -55,9 +98,32 @@
|
||||
else
|
||||
return 0
|
||||
|
||||
/mob/living/proc/catch_item(obj/item/I, skip_throw_mode_check = FALSE)
|
||||
return FALSE
|
||||
|
||||
/mob/living/proc/embed_item(obj/item/I)
|
||||
return
|
||||
|
||||
/mob/living/proc/can_embed(obj/item/I)
|
||||
return FALSE
|
||||
|
||||
/mob/living/hitby(atom/movable/AM, skipcatch, hitpush = TRUE, blocked = FALSE)
|
||||
if(istype(AM, /obj/item))
|
||||
var/obj/item/I = AM
|
||||
var/obj/item/I
|
||||
var/throwpower = 30
|
||||
if(isitem(AM))
|
||||
I = AM
|
||||
throwpower = I.throwforce
|
||||
if(check_shields(AM, throwpower, "\the [AM.name]", THROWN_PROJECTILE_ATTACK))
|
||||
hitpush = FALSE
|
||||
skipcatch = TRUE
|
||||
blocked = TRUE
|
||||
else if(I && I.throw_speed >= EMBED_THROWSPEED_THRESHOLD && can_embed(I, src) && prob(I.embedding.embed_chance) && !HAS_TRAIT(src, TRAIT_PIERCEIMMUNE) && (!HAS_TRAIT(src, TRAIT_AUTO_CATCH_ITEM) || incapacitated() || get_active_held_item()))
|
||||
embed_item(I)
|
||||
hitpush = FALSE
|
||||
skipcatch = TRUE //can't catch the now embedded item
|
||||
if(I)
|
||||
if(!skipcatch && isturf(I.loc) && catch_item(I))
|
||||
return TRUE
|
||||
var/zone = ran_zone(BODY_ZONE_CHEST, 65)//Hits a random part of the body, geared towards the chest
|
||||
var/dtype = BRUTE
|
||||
var/volume = I.get_volume_by_throwforce_and_or_w_class()
|
||||
@@ -214,6 +280,24 @@
|
||||
Move(user.loc)
|
||||
return 1
|
||||
|
||||
/mob/living/attack_hand(mob/user)
|
||||
..() //Ignoring parent return value here.
|
||||
SEND_SIGNAL(src, COMSIG_MOB_ATTACK_HAND, user)
|
||||
if((user != src) && user.a_intent != INTENT_HELP && check_shields(user, 0, user.name, attack_type = UNARMED_ATTACK))
|
||||
log_combat(user, src, "attempted to touch")
|
||||
visible_message("<span class='warning'>[user] attempted to touch [src]!</span>")
|
||||
return TRUE
|
||||
|
||||
/mob/living/attack_hulk(mob/living/carbon/human/user, does_attack_animation = FALSE)
|
||||
if(user.a_intent == INTENT_HARM)
|
||||
if(HAS_TRAIT(user, TRAIT_PACIFISM))
|
||||
to_chat(user, "<span class='notice'>You don't want to hurt [src]!</span>")
|
||||
return TRUE
|
||||
var/hulk_verb = pick("smash","pummel")
|
||||
if(user != src && check_shields(user, 15, "the [hulk_verb]ing"))
|
||||
return TRUE
|
||||
..()
|
||||
return FALSE
|
||||
|
||||
/mob/living/attack_slime(mob/living/simple_animal/slime/M)
|
||||
if(!SSticker.HasRoundStarted())
|
||||
@@ -229,6 +313,12 @@
|
||||
to_chat(M, "<span class='notice'>You don't want to hurt anyone!</span>")
|
||||
return FALSE
|
||||
|
||||
var/damage = rand(5, 35)
|
||||
if(M.is_adult)
|
||||
damage = rand(20, 40)
|
||||
if(check_shields(M, damage, "the [M.name]"))
|
||||
return FALSE
|
||||
|
||||
if (stat != DEAD)
|
||||
log_combat(M, src, "attacked")
|
||||
M.do_attack_animation(src)
|
||||
@@ -245,7 +335,8 @@
|
||||
if(HAS_TRAIT(M, TRAIT_PACIFISM))
|
||||
to_chat(M, "<span class='notice'>You don't want to hurt anyone!</span>")
|
||||
return FALSE
|
||||
|
||||
if(check_shields(M, rand(M.melee_damage_lower, M.melee_damage_upper), "the [M.name]", MELEE_ATTACK, M.armour_penetration))
|
||||
return FALSE
|
||||
if(M.attack_sound)
|
||||
playsound(loc, M.attack_sound, 50, 1, 1)
|
||||
M.do_attack_animation(src)
|
||||
@@ -256,10 +347,6 @@
|
||||
|
||||
|
||||
/mob/living/attack_paw(mob/living/carbon/monkey/M)
|
||||
if(isturf(loc) && istype(loc.loc, /area/start))
|
||||
to_chat(M, "No attacking people at spawn, you jackass.")
|
||||
return FALSE
|
||||
|
||||
if (M.a_intent == INTENT_HARM)
|
||||
if(HAS_TRAIT(M, TRAIT_PACIFISM))
|
||||
to_chat(M, "<span class='notice'>You don't want to hurt anyone!</span>")
|
||||
@@ -268,6 +355,8 @@
|
||||
if(M.is_muzzled() || (M.wear_mask && M.wear_mask.flags_cover & MASKCOVERSMOUTH))
|
||||
to_chat(M, "<span class='warning'>You can't bite with your mouth covered!</span>")
|
||||
return FALSE
|
||||
if(check_shields(M, 0, "the [M.name]"))
|
||||
return FALSE
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_BITE)
|
||||
if (prob(75))
|
||||
log_combat(M, src, "attacked")
|
||||
@@ -282,15 +371,16 @@
|
||||
|
||||
/mob/living/attack_larva(mob/living/carbon/alien/larva/L)
|
||||
switch(L.a_intent)
|
||||
if("help")
|
||||
if(INTENT_HELP)
|
||||
visible_message("<span class='notice'>[L.name] rubs its head against [src].</span>")
|
||||
return FALSE
|
||||
|
||||
else
|
||||
if(HAS_TRAIT(L, TRAIT_PACIFISM))
|
||||
to_chat(L, "<span class='notice'>You don't want to hurt anyone!</span>")
|
||||
return
|
||||
|
||||
return FALSE
|
||||
if(L != src && check_shields(L, rand(1, 3), "the [L.name]"))
|
||||
return FALSE
|
||||
L.do_attack_animation(src)
|
||||
if(prob(90))
|
||||
log_combat(L, src, "attacked")
|
||||
@@ -301,24 +391,29 @@
|
||||
else
|
||||
visible_message("<span class='danger'>[L.name] has attempted to bite [src]!</span>", \
|
||||
"<span class='userdanger'>[L.name] has attempted to bite [src]!</span>", null, COMBAT_MESSAGE_RANGE)
|
||||
return FALSE
|
||||
|
||||
/mob/living/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
if((M != src) && M.a_intent != INTENT_HELP && check_shields(M, 0, "the [M.name]"))
|
||||
visible_message("<span class='danger'>[M] attempted to touch [src]!</span>")
|
||||
return FALSE
|
||||
switch(M.a_intent)
|
||||
if ("help")
|
||||
visible_message("<span class='notice'>[M] caresses [src] with its scythe like arm.</span>")
|
||||
if (INTENT_HELP)
|
||||
if(!isalien(src)) //I know it's ugly, but the alien vs alien attack_alien behaviour is a bit different.
|
||||
visible_message("<span class='notice'>[M] caresses [src] with its scythe like arm.</span>")
|
||||
return FALSE
|
||||
if ("grab")
|
||||
if (INTENT_GRAB)
|
||||
grabbedby(M)
|
||||
return FALSE
|
||||
if("harm")
|
||||
if(INTENT_HARM)
|
||||
if(HAS_TRAIT(M, TRAIT_PACIFISM))
|
||||
to_chat(M, "<span class='notice'>You don't want to hurt anyone!</span>")
|
||||
return FALSE
|
||||
M.do_attack_animation(src)
|
||||
if(!isalien(src))
|
||||
M.do_attack_animation(src)
|
||||
return TRUE
|
||||
if("disarm")
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_DISARM)
|
||||
if(INTENT_DISARM)
|
||||
if(!isalien(src))
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_DISARM)
|
||||
return TRUE
|
||||
|
||||
/mob/living/ex_act(severity, target, origin)
|
||||
|
||||
@@ -260,15 +260,6 @@
|
||||
viewalerts = 1
|
||||
src << browse(dat, "window=aialerts&can_close=0")
|
||||
|
||||
/mob/living/silicon/ai/proc/ai_roster()
|
||||
var/dat = "<html><head><title>Crew Roster</title></head><body><b>Crew Roster:</b><br><br>"
|
||||
|
||||
dat += GLOB.data_core.get_manifest()
|
||||
dat += "</body></html>"
|
||||
|
||||
src << browse(dat, "window=airoster")
|
||||
onclose(src, "airoster")
|
||||
|
||||
/mob/living/silicon/ai/proc/ai_call_shuttle()
|
||||
if(control_disabled)
|
||||
to_chat(usr, "<span class='warning'>Wireless control is disabled!</span>")
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
|
||||
/mob/living/silicon/ai/attacked_by(obj/item/I, mob/living/user, def_zone)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return FALSE
|
||||
if(I.force && I.damtype != STAMINA && stat != DEAD) //only sparks if real damage is dealt.
|
||||
spark_system.start()
|
||||
return ..()
|
||||
|
||||
|
||||
/mob/living/silicon/ai/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
if(!SSticker.HasRoundStarted())
|
||||
to_chat(M, "You cannot attack people before the game has started.")
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/silicon/ai/attack_slime(mob/living/simple_animal/slime/user)
|
||||
return //immune to slimes
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user