Merge remote-tracking branch 'citadel/master' into combat_rework
This commit is contained in:
@@ -413,15 +413,6 @@
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
|
||||
/datum/crafting_recipe/paperwork
|
||||
name = "Filed Paper Work"
|
||||
result = /obj/item/folder/paperwork_correct
|
||||
time = 10 //Takes time for people to file and complete paper work!
|
||||
tools = list(/obj/item/pen)
|
||||
reqs = list(/obj/item/folder/paperwork = 1)
|
||||
subcategory = CAT_MISCELLANEOUS
|
||||
category = CAT_MISC
|
||||
|
||||
/datum/crafting_recipe/coconut_bong
|
||||
name = "Coconut Bong"
|
||||
result = /obj/item/bong/coconut
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
/datum/component/knockback
|
||||
/// distance the atom will be thrown
|
||||
var/throw_distance
|
||||
/// whether this can throw anchored targets (tables, etc)
|
||||
var/throw_anchored
|
||||
/// whether this is a gentle throw (default false means people thrown into walls are stunned / take damage)
|
||||
var/throw_gentle
|
||||
|
||||
/datum/component/knockback/Initialize(throw_distance=1)
|
||||
/datum/component/knockback/Initialize(throw_distance=1, throw_gentle=FALSE)
|
||||
if(!isitem(parent) && !ishostile(parent) && !isgun(parent) && !ismachinery(parent) && !isstructure(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.throw_distance = throw_distance
|
||||
src.throw_anchored = throw_anchored
|
||||
src.throw_gentle = throw_gentle
|
||||
|
||||
/datum/component/knockback/RegisterWithParent()
|
||||
. = ..()
|
||||
@@ -22,17 +27,29 @@
|
||||
. = ..()
|
||||
UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
|
||||
|
||||
/// triggered after an item attacks something
|
||||
/datum/component/knockback/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
|
||||
if(!proximity_flag)
|
||||
return
|
||||
do_knockback(target, user, get_dir(source, target))
|
||||
|
||||
/// triggered after a hostile simplemob attacks something
|
||||
/datum/component/knockback/proc/hostile_attackingtarget(mob/living/simple_animal/hostile/attacker, atom/target)
|
||||
do_knockback(target, attacker, get_dir(attacker, target))
|
||||
|
||||
/// triggered after a projectile hits something
|
||||
/datum/component/knockback/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)
|
||||
do_knockback(target, null, angle2dir(Angle))
|
||||
|
||||
|
||||
/**
|
||||
* Throw a target in a direction
|
||||
*
|
||||
* Arguments:
|
||||
* * target - Target atom to throw
|
||||
* * thrower - Thing that caused this atom to be thrown
|
||||
* * throw_dir - Direction to throw the atom
|
||||
*/
|
||||
/datum/component/knockback/proc/do_knockback(atom/target, mob/thrower, throw_dir)
|
||||
if(!ismovable(target) || throw_dir == null)
|
||||
return
|
||||
@@ -43,4 +60,4 @@
|
||||
throw_dir = turn(throw_dir, 180)
|
||||
throw_distance *= -1
|
||||
var/atom/throw_target = get_edge_target_turf(throwee, throw_dir)
|
||||
throwee.safe_throw_at(throw_target, throw_distance, 1, thrower)
|
||||
throwee.safe_throw_at(throw_target, throw_distance, 1, thrower) //, gentle = throw_gentle)
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
var/ride_check_ridden_incapacitated = FALSE
|
||||
var/list/offhands = list() // keyed list containing all the current riding offsets associated by mob
|
||||
|
||||
var/del_on_unbuckle_all = FALSE
|
||||
|
||||
/datum/component/riding/Initialize()
|
||||
if(!ismovable(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
@@ -28,8 +30,11 @@
|
||||
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/vehicle_moved)
|
||||
|
||||
/datum/component/riding/proc/vehicle_mob_unbuckle(datum/source, mob/living/M, force = FALSE)
|
||||
var/atom/movable/AM = parent
|
||||
restore_position(M)
|
||||
unequip_buckle_inhands(M)
|
||||
if(del_on_unbuckle_all && !AM.has_buckled_mobs())
|
||||
qdel(src)
|
||||
|
||||
/datum/component/riding/proc/vehicle_mob_buckle(datum/source, mob/living/M, force = FALSE)
|
||||
handle_vehicle_offsets()
|
||||
@@ -194,6 +199,7 @@
|
||||
|
||||
///////Yes, I said humans. No, this won't end well...//////////
|
||||
/datum/component/riding/human
|
||||
del_on_unbuckle_all = TRUE
|
||||
var/fireman_carrying = FALSE
|
||||
|
||||
/datum/component/riding/human/Initialize()
|
||||
@@ -261,6 +267,7 @@
|
||||
user.visible_message("<span class='warning'>[AM] pushes [user] off of [AM.p_them()]!</span>")
|
||||
|
||||
/datum/component/riding/cyborg
|
||||
del_on_unbuckle_all = TRUE
|
||||
|
||||
/datum/component/riding/cyborg/Initialize()
|
||||
. = ..()
|
||||
|
||||
@@ -1,28 +1,20 @@
|
||||
/datum/element/dusts_on_leaving_area
|
||||
element_flags = ELEMENT_DETACH | ELEMENT_BESPOKE
|
||||
id_arg_index = 2
|
||||
var/list/attached_mobs = list()
|
||||
var/list/area_types = list()
|
||||
|
||||
/datum/element/dusts_on_leaving_area/Attach(datum/target,types)
|
||||
. = ..()
|
||||
if(!ismob(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
attached_mobs += target
|
||||
area_types = types
|
||||
START_PROCESSING(SSprocessing,src)
|
||||
RegisterSignal(target,COMSIG_ENTER_AREA,.proc/check_dust)
|
||||
|
||||
/datum/element/dusts_on_leaving_area/Detach(mob/M)
|
||||
. = ..()
|
||||
if(M in attached_mobs)
|
||||
attached_mobs -= M
|
||||
if(!attached_mobs.len)
|
||||
STOP_PROCESSING(SSprocessing,src)
|
||||
UnregisterSignal(M,COMSIG_ENTER_AREA)
|
||||
|
||||
/datum/element/dusts_on_leaving_area/process()
|
||||
for(var/m in attached_mobs)
|
||||
var/mob/M = m
|
||||
var/area/A = get_area(M)
|
||||
if(!(A.type in area_types))
|
||||
M.dust(force = TRUE)
|
||||
Detach(M)
|
||||
/datum/element/dusts_on_leaving_area/proc/check_dust(datum/source, area/A)
|
||||
var/mob/M = source
|
||||
if(istype(M) && !(A.type in area_types))
|
||||
M.dust(force = TRUE)
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/datum/element/snailcrawl
|
||||
element_flags = ELEMENT_DETACH
|
||||
|
||||
/datum/element/snailcrawl/Attach(datum/target)
|
||||
. = ..()
|
||||
if(!ismovable(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
var/P
|
||||
if(iscarbon(target))
|
||||
P = .proc/snail_crawl
|
||||
else
|
||||
P = .proc/lubricate
|
||||
RegisterSignal(target, COMSIG_MOVABLE_MOVED, P)
|
||||
|
||||
/datum/element/snailcrawl/Detach(mob/living/carbon/target)
|
||||
. = ..()
|
||||
UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
|
||||
if(istype(target))
|
||||
target.remove_movespeed_modifier(/datum/movespeed_modifier/snail_crawl)
|
||||
|
||||
/datum/element/snailcrawl/proc/snail_crawl(mob/living/carbon/snail)
|
||||
if(snail.resting && !snail.buckled && lubricate(snail))
|
||||
snail.add_movespeed_modifier(/datum/movespeed_modifier/snail_crawl)
|
||||
else
|
||||
snail.remove_movespeed_modifier(/datum/movespeed_modifier/snail_crawl)
|
||||
|
||||
/datum/element/snailcrawl/proc/lubricate(atom/movable/snail)
|
||||
var/turf/open/OT = get_turf(snail)
|
||||
if(istype(OT))
|
||||
OT.MakeSlippery(TURF_WET_LUBE, 20)
|
||||
return TRUE
|
||||
@@ -1,163 +0,0 @@
|
||||
//used for holding information about unique properties of maps
|
||||
//feed it json files that match the datum layout
|
||||
//defaults to box
|
||||
// -Cyberboss
|
||||
|
||||
/datum/map_config
|
||||
// Metadata
|
||||
var/config_filename = "_maps/boxstation.json"
|
||||
var/defaulted = TRUE // set to FALSE by LoadConfig() succeeding
|
||||
// Config from maps.txt
|
||||
var/config_max_users = 0
|
||||
var/config_min_users = 0
|
||||
var/voteweight = 1
|
||||
var/max_round_search_span = 0 //If this is nonzero, then if the map has been played more than max_rounds_played within the search span (max determined by define in persistence.dm), this map won't be available.
|
||||
var/max_rounds_played = 0
|
||||
|
||||
// Config actually from the JSON - should default to Box
|
||||
var/map_name = "Box Station"
|
||||
var/map_path = "map_files/BoxStation"
|
||||
var/map_file = "BoxStation.dmm"
|
||||
|
||||
var/traits = null
|
||||
var/space_ruin_levels = 2
|
||||
var/space_empty_levels = 1
|
||||
|
||||
var/minetype = "lavaland"
|
||||
|
||||
var/maptype = MAP_TYPE_STATION //This should be used to adjust ingame behavior depending on the specific type of map being played. For instance, if an overmap were added, it'd be appropriate for it to only generate with a MAP_TYPE_SHIP
|
||||
|
||||
var/announcertype = "standard" //Determines the announcer the map uses. standard uses the default announcer, classic, but has a random chance to use other similarly-themed announcers, like medibot
|
||||
|
||||
var/allow_custom_shuttles = TRUE
|
||||
var/shuttles = list(
|
||||
"cargo" = "cargo_box",
|
||||
"ferry" = "ferry_fancy",
|
||||
"whiteship" = "whiteship_box",
|
||||
"emergency" = "emergency_box")
|
||||
|
||||
var/year_offset = 540 //The offset of ingame year from the actual IRL year. You know you want to make a map that takes place in the 90's. Don't lie.
|
||||
|
||||
/proc/load_map_config(filename = "data/next_map.json", default_to_box, delete_after, error_if_missing = TRUE)
|
||||
var/datum/map_config/config = new
|
||||
if (default_to_box)
|
||||
return config
|
||||
if (!config.LoadConfig(filename, error_if_missing))
|
||||
qdel(config)
|
||||
config = new /datum/map_config // Fall back to Box
|
||||
if (delete_after)
|
||||
fdel(filename)
|
||||
return config
|
||||
|
||||
#define CHECK_EXISTS(X) if(!istext(json[X])) { log_world("[##X] missing from json!"); return; }
|
||||
/datum/map_config/proc/LoadConfig(filename, error_if_missing)
|
||||
if(!fexists(filename))
|
||||
if(error_if_missing)
|
||||
log_world("map_config not found: [filename]")
|
||||
return
|
||||
|
||||
var/json = file(filename)
|
||||
if(!json)
|
||||
log_world("Could not open map_config: [filename]")
|
||||
return
|
||||
|
||||
json = file2text(json)
|
||||
if(!json)
|
||||
log_world("map_config is not text: [filename]")
|
||||
return
|
||||
|
||||
json = json_decode(json)
|
||||
if(!json)
|
||||
log_world("map_config is not json: [filename]")
|
||||
return
|
||||
|
||||
config_filename = filename
|
||||
|
||||
CHECK_EXISTS("map_name")
|
||||
map_name = json["map_name"]
|
||||
CHECK_EXISTS("map_path")
|
||||
map_path = json["map_path"]
|
||||
|
||||
map_file = json["map_file"]
|
||||
// "map_file": "BoxStation.dmm"
|
||||
if (istext(map_file))
|
||||
if (!fexists("_maps/[map_path]/[map_file]"))
|
||||
log_world("Map file ([map_path]/[map_file]) does not exist!")
|
||||
return
|
||||
// "map_file": ["Lower.dmm", "Upper.dmm"]
|
||||
else if (islist(map_file))
|
||||
for (var/file in map_file)
|
||||
if (!fexists("_maps/[map_path]/[file]"))
|
||||
log_world("Map file ([map_path]/[file]) does not exist!")
|
||||
return
|
||||
else
|
||||
log_world("map_file missing from json!")
|
||||
return
|
||||
|
||||
if (islist(json["shuttles"]))
|
||||
var/list/L = json["shuttles"]
|
||||
for(var/key in L)
|
||||
var/value = L[key]
|
||||
shuttles[key] = value
|
||||
else if ("shuttles" in json)
|
||||
log_world("map_config shuttles is not a list!")
|
||||
return
|
||||
|
||||
traits = json["traits"]
|
||||
// "traits": [{"Linkage": "Cross"}, {"Space Ruins": true}]
|
||||
if (islist(traits))
|
||||
// "Station" is set by default, but it's assumed if you're setting
|
||||
// traits you want to customize which level is cross-linked
|
||||
for (var/level in traits)
|
||||
if (!(ZTRAIT_STATION in level))
|
||||
level[ZTRAIT_STATION] = TRUE
|
||||
// "traits": null or absent -> default
|
||||
else if (!isnull(traits))
|
||||
log_world("map_config traits is not a list!")
|
||||
return
|
||||
|
||||
var/temp = json["space_ruin_levels"]
|
||||
if (isnum(temp))
|
||||
space_ruin_levels = temp
|
||||
else if (!isnull(temp))
|
||||
log_world("map_config space_ruin_levels is not a number!")
|
||||
return
|
||||
|
||||
temp = json["space_empty_levels"]
|
||||
if (isnum(temp))
|
||||
space_empty_levels = temp
|
||||
else if (!isnull(temp))
|
||||
log_world("map_config space_empty_levels is not a number!")
|
||||
return
|
||||
|
||||
temp = json["year_offset"]
|
||||
if (isnum(temp))
|
||||
year_offset = temp
|
||||
else if (!isnull(temp))
|
||||
log_world("map_config year_offset is not a number!")
|
||||
return
|
||||
|
||||
if ("minetype" in json)
|
||||
minetype = json["minetype"]
|
||||
|
||||
if ("maptype" in json)
|
||||
maptype = json["maptype"]
|
||||
|
||||
if ("announcertype" in json)
|
||||
announcertype = json["announcertype"]
|
||||
|
||||
allow_custom_shuttles = json["allow_custom_shuttles"] != FALSE
|
||||
|
||||
defaulted = FALSE
|
||||
return TRUE
|
||||
#undef CHECK_EXISTS
|
||||
|
||||
/datum/map_config/proc/GetFullMapPaths()
|
||||
if (istext(map_file))
|
||||
return list("_maps/[map_path]/[map_file]")
|
||||
. = list()
|
||||
for (var/file in map_file)
|
||||
. += "_maps/[map_path]/[file]"
|
||||
|
||||
/datum/map_config/proc/MakeNextMap()
|
||||
return config_filename == "data/next_map.json" || fcopy(config_filename, "data/next_map.json")
|
||||
@@ -0,0 +1,112 @@
|
||||
// Hey! Listen! Update \config\iceruinblacklist.txt with your new ruins!
|
||||
|
||||
/datum/map_template/ruin/icemoon
|
||||
prefix = "_maps/RandomRuins/IceRuins/"
|
||||
allow_duplicates = FALSE
|
||||
cost = 5
|
||||
|
||||
// above ground only
|
||||
|
||||
/datum/map_template/ruin/icemoon/lust
|
||||
name = "Ruin of Lust"
|
||||
id = "lust"
|
||||
description = "Not exactly what you expected."
|
||||
suffix = "icemoon_surface_lust.dmm"
|
||||
|
||||
/datum/map_template/ruin/icemoon/asteroid
|
||||
name = "Asteroid Site"
|
||||
id = "asteroidsite"
|
||||
description = "Surprised to see us here?"
|
||||
suffix = "icemoon_surface_asteroid.dmm"
|
||||
|
||||
/datum/map_template/ruin/icemoon/hotsprings
|
||||
name = "Hot Springs"
|
||||
id = "hotsprings"
|
||||
description = "Just relax and take a dip, nothing will go wrong, I swear!"
|
||||
suffix = "icemoon_surface_hotsprings.dmm"
|
||||
|
||||
/datum/map_template/ruin/icemoon/engioutpost
|
||||
name = "Engineer Outpost"
|
||||
id = "engioutpost"
|
||||
description = "Blown up by an unfortunate accident."
|
||||
suffix = "icemoon_surface_engioutpost.dmm"
|
||||
|
||||
/datum/map_template/ruin/icemoon/fountain
|
||||
name = "Fountain Hall"
|
||||
id = "fountain"
|
||||
description = "The fountain has a warning on the side. DANGER: May have undeclared side effects that only become obvious when implemented."
|
||||
prefix = "_maps/RandomRuins/AnywhereRuins/"
|
||||
suffix = "fountain_hall.dmm"
|
||||
|
||||
// above and below ground together
|
||||
|
||||
/datum/map_template/ruin/icemoon/mining_site
|
||||
name = "Mining Site"
|
||||
id = "miningsite"
|
||||
description = "Ruins of a site where people once mined with primitive tools for ore."
|
||||
suffix = "icemoon_surface_mining_site.dmm"
|
||||
always_place = TRUE
|
||||
always_spawn_with = list(/datum/map_template/ruin/icemoon/underground/mining_site_below = PLACE_BELOW)
|
||||
|
||||
/datum/map_template/ruin/icemoon/underground/mining_site_below
|
||||
name = "Mining Site Underground"
|
||||
id = "miningsite-underground"
|
||||
description = "Who knew ladders could be so useful?"
|
||||
suffix = "icemoon_underground_mining_site.dmm"
|
||||
unpickable = TRUE
|
||||
|
||||
// below ground only
|
||||
|
||||
/datum/map_template/ruin/icemoon/underground
|
||||
name = "underground ruin"
|
||||
|
||||
/datum/map_template/ruin/icemoon/underground/abandonedvillage
|
||||
name = "Abandoned Village"
|
||||
id = "abandonedvillage"
|
||||
description = "Who knows what lies within?"
|
||||
suffix = "icemoon_underground_abandoned_village.dmm"
|
||||
|
||||
/datum/map_template/ruin/icemoon/underground/library
|
||||
name = "Buried Library"
|
||||
id = "buriedlibrary"
|
||||
description = "A once grand library, now lost to the confines of the Ice Moon."
|
||||
suffix = "icemoon_underground_library.dmm"
|
||||
|
||||
/datum/map_template/ruin/icemoon/underground/wrath
|
||||
name = "Ruin of Wrath"
|
||||
id = "wrath"
|
||||
description = "You'll fight and fight and just keep fighting."
|
||||
suffix = "icemoon_underground_wrath.dmm"
|
||||
|
||||
/datum/map_template/ruin/icemoon/underground/lavaland
|
||||
name = "Lavaland Site"
|
||||
id = "lavalandsite"
|
||||
description = "I guess we never really left you huh?"
|
||||
suffix = "icemoon_underground_lavaland.dmm"
|
||||
|
||||
/datum/map_template/ruin/icemoon/underground/puzzle
|
||||
name = "Ancient Puzzle"
|
||||
id = "puzzle"
|
||||
description = "Mystery to be solved."
|
||||
suffix = "icemoon_underground_puzzle.dmm"
|
||||
|
||||
/datum/map_template/ruin/icemoon/underground/bathhouse
|
||||
name = "Bath House"
|
||||
id = "bathhouse"
|
||||
description = "A taste of paradise, locked in the hell of the Ice Moon."
|
||||
suffix = "icemoon_underground_bathhouse.dmm"
|
||||
|
||||
/datum/map_template/ruin/icemoon/underground/wendigo_cave
|
||||
name = "Wendigo Cave"
|
||||
id = "wendigocave"
|
||||
description = "Into the jaws of the beast."
|
||||
suffix = "icemoon_underground_wendigo_cave.dmm"
|
||||
|
||||
/datum/map_template/ruin/icemoon/underground/free_golem
|
||||
name = "Free Golem Ship"
|
||||
id = "golem-ship"
|
||||
description = "Lumbering humanoids, made out of precious metals, move inside this ship. They frequently leave to mine more minerals, which they somehow turn into more of them. \
|
||||
Seem very intent on research and individual liberty, and also geology-based naming?"
|
||||
prefix = "_maps/RandomRuins/AnywhereRuins/"
|
||||
suffix = "golem_ship.dmm"
|
||||
allow_duplicates = FALSE
|
||||
@@ -67,7 +67,8 @@
|
||||
description = "Lumbering humanoids, made out of precious metals, move inside this ship. They frequently leave to mine more minerals, which they somehow turn into more of them. \
|
||||
Seem very intent on research and individual liberty, and also geology based naming?"
|
||||
cost = 20
|
||||
suffix = "lavaland_surface_golem_ship.dmm"
|
||||
prefix = "_maps/RandomRuins/AnywhereRuins/"
|
||||
suffix = "golem_ship.dmm"
|
||||
allow_duplicates = FALSE
|
||||
|
||||
/datum/map_template/ruin/lavaland/animal_hospital
|
||||
@@ -175,7 +176,8 @@
|
||||
name = "Fountain Hall"
|
||||
id = "fountain"
|
||||
description = "The fountain has a warning on the side. DANGER: May have undeclared side effects that only become obvious when implemented."
|
||||
suffix = "lavaland_surface_fountain_hall.dmm"
|
||||
prefix = "_maps/RandomRuins/AnywhereRuins/"
|
||||
suffix = "fountain_hall.dmm"
|
||||
cost = 5
|
||||
allow_duplicates = FALSE
|
||||
|
||||
|
||||
@@ -23,14 +23,14 @@
|
||||
mappath = "[prefix][shuttle_id].dmm"
|
||||
. = ..()
|
||||
|
||||
/datum/map_template/shuttle/preload_size(path, cache)
|
||||
/datum/map_template/shuttle/preload_size(path = mappath, force_cache = FALSE)
|
||||
. = ..(path, TRUE) // Done this way because we still want to know if someone actualy wanted to cache the map
|
||||
if(!cached_map)
|
||||
return
|
||||
|
||||
discover_port_offset()
|
||||
|
||||
if(!cache)
|
||||
if(!cached_map)
|
||||
cached_map = null
|
||||
|
||||
/datum/map_template/shuttle/proc/discover_port_offset()
|
||||
@@ -53,12 +53,11 @@
|
||||
++xcrd
|
||||
--ycrd
|
||||
|
||||
/datum/map_template/shuttle/load(turf/T, centered, register=TRUE)
|
||||
/datum/map_template/shuttle/load(turf/T, centered = FALSE, orientation = SOUTH, annihilate = default_annihilate, force_cache = FALSE, rotate_placement_to_orientation = FALSE, register = TRUE)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
var/list/turfs = block( locate(.[MAP_MINX], .[MAP_MINY], .[MAP_MINZ]),
|
||||
locate(.[MAP_MAXX], .[MAP_MAXY], .[MAP_MAXZ]))
|
||||
var/list/turfs = get_last_loaded_turf_block()
|
||||
for(var/i in 1 to turfs.len)
|
||||
var/turf/place = turfs[i]
|
||||
if(istype(place, /turf/open/space)) // This assumes all shuttles are loaded in a single spot then moved to their real destination.
|
||||
|
||||
@@ -391,78 +391,34 @@
|
||||
owner.underlays -= marked_underlay //if this is being called, we should have an owner at this point.
|
||||
..()
|
||||
|
||||
/datum/status_effect/saw_bleed
|
||||
/datum/status_effect/stacking/saw_bleed
|
||||
id = "saw_bleed"
|
||||
duration = -1 //removed under specific conditions
|
||||
tick_interval = 6
|
||||
alert_type = null
|
||||
var/mutable_appearance/bleed_overlay
|
||||
var/mutable_appearance/bleed_underlay
|
||||
var/bleed_amount = 3
|
||||
var/bleed_buildup = 3
|
||||
var/delay_before_decay = 5
|
||||
delay_before_decay = 5
|
||||
stack_threshold = 10
|
||||
max_stacks = 10
|
||||
overlay_file = 'icons/effects/bleed.dmi'
|
||||
underlay_file = 'icons/effects/bleed.dmi'
|
||||
overlay_state = "bleed"
|
||||
underlay_state = "bleed"
|
||||
var/bleed_damage = 200
|
||||
var/needs_to_bleed = FALSE
|
||||
|
||||
/datum/status_effect/saw_bleed/Destroy()
|
||||
if(owner)
|
||||
owner.cut_overlay(bleed_overlay)
|
||||
owner.underlays -= bleed_underlay
|
||||
QDEL_NULL(bleed_overlay)
|
||||
return ..()
|
||||
/datum/status_effect/stacking/saw_bleed/fadeout_effect()
|
||||
new /obj/effect/temp_visual/bleed(get_turf(owner))
|
||||
|
||||
/datum/status_effect/saw_bleed/on_apply()
|
||||
if(owner.stat == DEAD)
|
||||
return FALSE
|
||||
bleed_overlay = mutable_appearance('icons/effects/bleed.dmi', "bleed[bleed_amount]")
|
||||
bleed_underlay = mutable_appearance('icons/effects/bleed.dmi', "bleed[bleed_amount]")
|
||||
var/icon/I = icon(owner.icon, owner.icon_state, owner.dir)
|
||||
var/icon_height = I.Height()
|
||||
bleed_overlay.pixel_x = -owner.pixel_x
|
||||
bleed_overlay.pixel_y = FLOOR(icon_height * 0.25, 1)
|
||||
bleed_overlay.transform = matrix() * (icon_height/world.icon_size) //scale the bleed overlay's size based on the target's icon size
|
||||
bleed_underlay.pixel_x = -owner.pixel_x
|
||||
bleed_underlay.transform = matrix() * (icon_height/world.icon_size) * 3
|
||||
bleed_underlay.alpha = 40
|
||||
owner.add_overlay(bleed_overlay)
|
||||
owner.underlays += bleed_underlay
|
||||
return ..()
|
||||
/datum/status_effect/stacking/saw_bleed/threshold_cross_effect()
|
||||
owner.adjustBruteLoss(bleed_damage)
|
||||
var/turf/T = get_turf(owner)
|
||||
new /obj/effect/temp_visual/bleed/explode(T)
|
||||
for(var/d in GLOB.alldirs)
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(T, d)
|
||||
playsound(T, "desceration", 100, TRUE, -1)
|
||||
|
||||
/datum/status_effect/saw_bleed/tick()
|
||||
if(owner.stat == DEAD)
|
||||
qdel(src)
|
||||
else
|
||||
add_bleed(-1)
|
||||
|
||||
/datum/status_effect/saw_bleed/proc/add_bleed(amount)
|
||||
owner.cut_overlay(bleed_overlay)
|
||||
owner.underlays -= bleed_underlay
|
||||
bleed_amount += amount
|
||||
if(bleed_amount)
|
||||
if(bleed_amount >= 10)
|
||||
needs_to_bleed = TRUE
|
||||
qdel(src)
|
||||
else
|
||||
if(amount > 0)
|
||||
tick_interval += delay_before_decay
|
||||
bleed_overlay.icon_state = "bleed[bleed_amount]"
|
||||
bleed_underlay.icon_state = "bleed[bleed_amount]"
|
||||
owner.add_overlay(bleed_overlay)
|
||||
owner.underlays += bleed_underlay
|
||||
else
|
||||
qdel(src)
|
||||
|
||||
/datum/status_effect/saw_bleed/on_remove()
|
||||
. = ..()
|
||||
if(needs_to_bleed)
|
||||
var/turf/T = get_turf(owner)
|
||||
new /obj/effect/temp_visual/bleed/explode(T)
|
||||
for(var/d in GLOB.alldirs)
|
||||
new /obj/effect/temp_visual/dir_setting/bloodsplatter(T, d)
|
||||
playsound(T, "desceration", 200, 1, -1)
|
||||
owner.adjustBruteLoss(bleed_damage)
|
||||
else
|
||||
new /obj/effect/temp_visual/bleed(get_turf(owner))
|
||||
/datum/status_effect/stacking/saw_bleed/bloodletting
|
||||
id = "bloodletting"
|
||||
stack_threshold = 7
|
||||
max_stacks = 7
|
||||
bleed_damage = 20
|
||||
|
||||
/datum/status_effect/neck_slice
|
||||
id = "neck_slice"
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
var/duration = -1 //How long the status effect lasts in DECISECONDS. Enter -1 for an effect that never ends unless removed through some means.
|
||||
var/tick_interval = 10 //How many deciseconds between ticks, approximately. Leave at 10 for every second.
|
||||
var/mob/living/owner //The mob affected by the status effect.
|
||||
var/status_type = STATUS_EFFECT_UNIQUE //How many of the effect can be on one mob, and what happens when you try to add another
|
||||
var/on_remove_on_mob_delete = FALSE //if we call on_remove() when the mob is deleted
|
||||
var/examine_text //If defined, this text will appear when the mob is examined - to use he, she etc. use "SUBJECTPRONOUN" and replace it in the examines themselves
|
||||
var/alert_type = /obj/screen/alert/status_effect //the alert thrown by the status effect, contains name and description
|
||||
@@ -16,6 +15,8 @@
|
||||
/// If this is TRUE, the user will have sprint forcefully disabled while this is active.
|
||||
var/blocks_sprint = FALSE
|
||||
var/obj/screen/alert/status_effect/linked_alert = null //the alert itself, if it exists
|
||||
/// How many of the effect can be on one mob, and what happens when you try to add another
|
||||
var/status_type = STATUS_EFFECT_UNIQUE
|
||||
|
||||
/datum/status_effect/New(list/arguments)
|
||||
on_creation(arglist(arguments))
|
||||
@@ -67,6 +68,9 @@
|
||||
|
||||
/datum/status_effect/proc/tick() //Called every tick.
|
||||
|
||||
/datum/status_effect/proc/before_remove() //! Called before being removed; returning FALSE will cancel removal
|
||||
return TRUE
|
||||
|
||||
/datum/status_effect/proc/on_remove() //Called whenever the buff expires or is removed; do note that at the point this is called, it is out of the owner's status_effects but owner is not yet null
|
||||
SHOULD_CALL_PARENT(TRUE)
|
||||
REMOVE_TRAIT(owner, TRAIT_COMBAT_MODE_LOCKED, src)
|
||||
@@ -123,12 +127,13 @@
|
||||
S1 = new effect(arguments)
|
||||
. = S1
|
||||
|
||||
/mob/living/proc/remove_status_effect(effect) //removes all of a given status effect from this mob, returning TRUE if at least one was removed
|
||||
/mob/living/proc/remove_status_effect(effect, ...) //removes all of a given status effect from this mob, returning TRUE if at least one was removed
|
||||
. = FALSE
|
||||
var/list/arguments = args.Copy(2)
|
||||
if(status_effects)
|
||||
var/datum/status_effect/S1 = effect
|
||||
for(var/datum/status_effect/S in status_effects)
|
||||
if(initial(S1.id) == S.id)
|
||||
if(initial(S1.id) == S.id && S.before_remove(arguments))
|
||||
qdel(S)
|
||||
. = TRUE
|
||||
|
||||
@@ -147,3 +152,129 @@
|
||||
for(var/datum/status_effect/S in status_effects)
|
||||
if(initial(S1.id) == S.id)
|
||||
. += S
|
||||
|
||||
//////////////////////
|
||||
// STACKING EFFECTS //
|
||||
//////////////////////
|
||||
|
||||
/datum/status_effect/stacking
|
||||
id = "stacking_base"
|
||||
duration = -1 //removed under specific conditions
|
||||
alert_type = null
|
||||
var/stacks = 0 //how many stacks are accumulated, also is # of stacks that target will have when first applied
|
||||
var/delay_before_decay //deciseconds until ticks start occuring, which removes stacks (first stack will be removed at this time plus tick_interval)
|
||||
tick_interval = 10 //deciseconds between decays once decay starts
|
||||
var/stack_decay = 1 //how many stacks are lost per tick (decay trigger)
|
||||
var/stack_threshold //special effects trigger when stacks reach this amount
|
||||
var/max_stacks //stacks cannot exceed this amount
|
||||
var/consumed_on_threshold = TRUE //if status should be removed once threshold is crossed
|
||||
var/threshold_crossed = FALSE //set to true once the threshold is crossed, false once it falls back below
|
||||
var/overlay_file
|
||||
var/underlay_file
|
||||
var/overlay_state // states in .dmi must be given a name followed by a number which corresponds to a number of stacks. put the state name without the number in these state vars
|
||||
var/underlay_state // the number is concatonated onto the string based on the number of stacks to get the correct state name
|
||||
var/mutable_appearance/status_overlay
|
||||
var/mutable_appearance/status_underlay
|
||||
|
||||
/datum/status_effect/stacking/proc/threshold_cross_effect() //what happens when threshold is crossed
|
||||
|
||||
/datum/status_effect/stacking/proc/stacks_consumed_effect() //runs if status is deleted due to threshold being crossed
|
||||
|
||||
/datum/status_effect/stacking/proc/fadeout_effect() //runs if status is deleted due to being under one stack
|
||||
|
||||
/datum/status_effect/stacking/proc/stack_decay_effect() //runs every time tick() causes stacks to decay
|
||||
|
||||
/datum/status_effect/stacking/proc/on_threshold_cross()
|
||||
threshold_cross_effect()
|
||||
if(consumed_on_threshold)
|
||||
stacks_consumed_effect()
|
||||
qdel(src)
|
||||
|
||||
/datum/status_effect/stacking/proc/on_threshold_drop()
|
||||
|
||||
/datum/status_effect/stacking/proc/can_have_status()
|
||||
return owner.stat != DEAD
|
||||
|
||||
/datum/status_effect/stacking/proc/can_gain_stacks()
|
||||
return owner.stat != DEAD
|
||||
|
||||
/datum/status_effect/stacking/tick()
|
||||
if(!can_have_status())
|
||||
qdel(src)
|
||||
else
|
||||
add_stacks(-stack_decay)
|
||||
stack_decay_effect()
|
||||
|
||||
/datum/status_effect/stacking/proc/add_stacks(stacks_added)
|
||||
if(stacks_added > 0 && !can_gain_stacks())
|
||||
return FALSE
|
||||
owner.cut_overlay(status_overlay)
|
||||
owner.underlays -= status_underlay
|
||||
stacks += stacks_added
|
||||
if(stacks > 0)
|
||||
if(stacks >= stack_threshold && !threshold_crossed) //threshold_crossed check prevents threshold effect from occuring if changing from above threshold to still above threshold
|
||||
threshold_crossed = TRUE
|
||||
on_threshold_cross()
|
||||
if(consumed_on_threshold)
|
||||
return
|
||||
else if(stacks < stack_threshold && threshold_crossed)
|
||||
threshold_crossed = FALSE //resets threshold effect if we fall below threshold so threshold effect can trigger again
|
||||
on_threshold_drop()
|
||||
if(stacks_added > 0)
|
||||
tick_interval += delay_before_decay //refreshes time until decay
|
||||
stacks = min(stacks, max_stacks)
|
||||
status_overlay.icon_state = "[overlay_state][stacks]"
|
||||
status_underlay.icon_state = "[underlay_state][stacks]"
|
||||
owner.add_overlay(status_overlay)
|
||||
owner.underlays += status_underlay
|
||||
else
|
||||
fadeout_effect()
|
||||
qdel(src) //deletes status if stacks fall under one
|
||||
|
||||
/datum/status_effect/stacking/on_creation(mob/living/new_owner, stacks_to_apply)
|
||||
. = ..()
|
||||
if(.)
|
||||
add_stacks(stacks_to_apply)
|
||||
|
||||
/datum/status_effect/stacking/on_apply()
|
||||
if(!can_have_status())
|
||||
return FALSE
|
||||
status_overlay = mutable_appearance(overlay_file, "[overlay_state][stacks]")
|
||||
status_underlay = mutable_appearance(underlay_file, "[underlay_state][stacks]")
|
||||
var/icon/I = icon(owner.icon, owner.icon_state, owner.dir)
|
||||
var/icon_height = I.Height()
|
||||
status_overlay.pixel_x = -owner.pixel_x
|
||||
status_overlay.pixel_y = FLOOR(icon_height * 0.25, 1)
|
||||
status_overlay.transform = matrix() * (icon_height/world.icon_size) //scale the status's overlay size based on the target's icon size
|
||||
status_underlay.pixel_x = -owner.pixel_x
|
||||
status_underlay.transform = matrix() * (icon_height/world.icon_size) * 3
|
||||
status_underlay.alpha = 40
|
||||
owner.add_overlay(status_overlay)
|
||||
owner.underlays += status_underlay
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/stacking/Destroy()
|
||||
if(owner)
|
||||
owner.cut_overlay(status_overlay)
|
||||
owner.underlays -= status_underlay
|
||||
QDEL_NULL(status_overlay)
|
||||
return ..()
|
||||
|
||||
/// Status effect from multiple sources, when all sources are removed, so is the effect
|
||||
/datum/status_effect/grouped
|
||||
status_type = STATUS_EFFECT_MULTIPLE //! Adds itself to sources and destroys itself if one exists already, there are never multiple
|
||||
var/list/sources = list()
|
||||
|
||||
/datum/status_effect/grouped/on_creation(mob/living/new_owner, source)
|
||||
var/datum/status_effect/grouped/existing = new_owner.has_status_effect(type)
|
||||
if(existing)
|
||||
existing.sources |= source
|
||||
qdel(src)
|
||||
return FALSE
|
||||
else
|
||||
sources |= source
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/grouped/before_remove(source)
|
||||
sources -= source
|
||||
return !length(sources)
|
||||
|
||||
+108
-26
@@ -1,49 +1,96 @@
|
||||
//The effects of weather occur across an entire z-level. For instance, lavaland has periodic ash storms that scorch most unprotected creatures.
|
||||
/**
|
||||
* Causes weather to occur on a z level in certain area types
|
||||
*
|
||||
* The effects of weather occur across an entire z-level. For instance, lavaland has periodic ash storms that scorch most unprotected creatures.
|
||||
* Weather always occurs on different z levels at different times, regardless of weather type.
|
||||
* Can have custom durations, targets, and can automatically protect indoor areas.
|
||||
*
|
||||
*/
|
||||
|
||||
/datum/weather
|
||||
/// name of weather
|
||||
var/name = "space wind"
|
||||
/// description of weather
|
||||
var/desc = "Heavy gusts of wind blanket the area, periodically knocking down anyone caught in the open."
|
||||
|
||||
var/telegraph_message = "<span class='warning'>The wind begins to pick up.</span>" //The message displayed in chat to foreshadow the weather's beginning
|
||||
var/telegraph_duration = 300 //In deciseconds, how long from the beginning of the telegraph until the weather begins
|
||||
var/telegraph_sound //The sound file played to everyone on an affected z-level
|
||||
var/telegraph_overlay //The overlay applied to all tiles on the z-level
|
||||
/// The message displayed in chat to foreshadow the weather's beginning
|
||||
var/telegraph_message = "<span class='warning'>The wind begins to pick up.</span>"
|
||||
|
||||
var/weather_message = "<span class='userdanger'>The wind begins to blow ferociously!</span>" //Displayed in chat once the weather begins in earnest
|
||||
var/weather_duration = 1200 //In deciseconds, how long the weather lasts once it begins
|
||||
var/weather_duration_lower = 1200 //See above - this is the lowest possible duration
|
||||
var/weather_duration_upper = 1500 //See above - this is the highest possible duration
|
||||
/// In deciseconds, how long from the beginning of the telegraph until the weather begins
|
||||
var/telegraph_duration = 300
|
||||
/// The sound file played to everyone on an affected z-level
|
||||
var/telegraph_sound
|
||||
/// The overlay applied to all tiles on the z-level
|
||||
var/telegraph_overlay
|
||||
/// Displayed in chat once the weather begins in earnest
|
||||
var/weather_message = "<span class='userdanger'>The wind begins to blow ferociously!</span>"
|
||||
///In deciseconds, how long the weather lasts once it begins
|
||||
var/weather_duration = 1200
|
||||
///See above - this is the lowest possible duration
|
||||
var/weather_duration_lower = 1200
|
||||
///See above - this is the highest possible duration
|
||||
var/weather_duration_upper = 1500
|
||||
/// Looping sound while weather is occuring
|
||||
var/weather_sound
|
||||
/// Area overlay while the weather is occuring
|
||||
var/weather_overlay
|
||||
/// Color to apply to the area while weather is occuring
|
||||
var/weather_color = null
|
||||
|
||||
var/end_message = "<span class='danger'>The wind relents its assault.</span>" //Displayed once the weather is over
|
||||
var/end_duration = 300 //In deciseconds, how long the "wind-down" graphic will appear before vanishing entirely
|
||||
/// Displayed once the weather is over
|
||||
var/end_message = "<span class='danger'>The wind relents its assault.</span>"
|
||||
/// In deciseconds, how long the "wind-down" graphic will appear before vanishing entirely
|
||||
var/end_duration = 300
|
||||
/// Sound that plays while weather is ending
|
||||
var/end_sound
|
||||
/// Area overlay while weather is ending
|
||||
var/end_overlay
|
||||
|
||||
var/area_type = /area/space //Types of area to affect
|
||||
var/list/impacted_areas = list() //Areas to be affected by the weather, calculated when the weather begins
|
||||
var/list/protected_areas = list()//Areas that are protected and excluded from the affected areas.
|
||||
var/impacted_z_levels // The list of z-levels that this weather is actively affecting
|
||||
/// Types of area to affect
|
||||
var/area_type = /area/space
|
||||
/// TRUE value protects areas with outdoors marked as false, regardless of area type
|
||||
var/protect_indoors = FALSE
|
||||
/// Areas to be affected by the weather, calculated when the weather begins
|
||||
var/list/impacted_areas = list()
|
||||
|
||||
var/overlay_layer = AREA_LAYER //Since it's above everything else, this is the layer used by default. TURF_LAYER is below mobs and walls if you need to use that.
|
||||
var/aesthetic = FALSE //If the weather has no purpose other than looks
|
||||
var/immunity_type = "storm" //Used by mobs to prevent them from being affected by the weather
|
||||
/// Areas that are protected and excluded from the affected areas.
|
||||
var/list/protected_areas = list()
|
||||
/// The list of z-levels that this weather is actively affecting
|
||||
var/impacted_z_levels
|
||||
|
||||
var/stage = END_STAGE //The stage of the weather, from 1-4
|
||||
/// Since it's above everything else, this is the layer used by default. TURF_LAYER is below mobs and walls if you need to use that.
|
||||
var/overlay_layer = AREA_LAYER
|
||||
/// Plane for the overlay
|
||||
var/overlay_plane = BLACKNESS_PLANE
|
||||
/// If the weather has no purpose but aesthetics.
|
||||
var/aesthetic = FALSE
|
||||
/// Used by mobs to prevent them from being affected by the weather
|
||||
var/immunity_type = "storm"
|
||||
|
||||
// These are read by the weather subsystem and used to determine when and where to run the weather.
|
||||
var/probability = 0 // Weight amongst other eligible weather. If zero, will never happen randomly.
|
||||
var/target_trait = ZTRAIT_STATION // The z-level trait to affect when run randomly or when not overridden.
|
||||
/// The stage of the weather, from 1-4
|
||||
var/stage = END_STAGE
|
||||
|
||||
/// Weight amongst other eligible weather. if zero, will never happen randomly.
|
||||
var/probability = 0
|
||||
/// The z-level trait to affect when run randomly or when not overridden.
|
||||
var/target_trait = ZTRAIT_STATION
|
||||
|
||||
/// Whether a barometer can predict when the weather will happen
|
||||
var/barometer_predictable = FALSE
|
||||
var/next_hit_time = 0 //For barometers to know when the next storm will hit
|
||||
/// For barometers to know when the next storm will hit
|
||||
var/next_hit_time = 0
|
||||
|
||||
/datum/weather/New(z_levels)
|
||||
..()
|
||||
impacted_z_levels = z_levels
|
||||
|
||||
/**
|
||||
* Telegraphs the beginning of the weather on the impacted z levels
|
||||
*
|
||||
* Sends sounds and details to mobs in the area
|
||||
* Calculates duration and hit areas, and makes a callback for the actual weather to start
|
||||
*
|
||||
*/
|
||||
/datum/weather/proc/telegraph()
|
||||
if(stage == STARTUP_STAGE)
|
||||
return
|
||||
@@ -58,6 +105,8 @@
|
||||
affectareas -= get_areas(V)
|
||||
for(var/V in affectareas)
|
||||
var/area/A = V
|
||||
if(protect_indoors && !A.outdoors)
|
||||
continue
|
||||
if(A.z in impacted_z_levels)
|
||||
impacted_areas |= A
|
||||
weather_duration = rand(weather_duration_lower, weather_duration_upper)
|
||||
@@ -72,6 +121,13 @@
|
||||
SEND_SOUND(M, sound(telegraph_sound))
|
||||
addtimer(CALLBACK(src, .proc/start), telegraph_duration)
|
||||
|
||||
/**
|
||||
* Starts the actual weather and effects from it
|
||||
*
|
||||
* Updates area overlays and sends sounds and messages to mobs to notify them
|
||||
* Begins dealing effects from weather to mobs in the area
|
||||
*
|
||||
*/
|
||||
/datum/weather/proc/start()
|
||||
if(stage >= MAIN_STAGE)
|
||||
return
|
||||
@@ -86,6 +142,13 @@
|
||||
SEND_SOUND(M, sound(weather_sound))
|
||||
addtimer(CALLBACK(src, .proc/wind_down), weather_duration)
|
||||
|
||||
/**
|
||||
* Weather enters the winding down phase, stops effects
|
||||
*
|
||||
* Updates areas to be in the winding down phase
|
||||
* Sends sounds and messages to mobs to notify them
|
||||
*
|
||||
*/
|
||||
/datum/weather/proc/wind_down()
|
||||
if(stage >= WIND_DOWN_STAGE)
|
||||
return
|
||||
@@ -100,6 +163,13 @@
|
||||
SEND_SOUND(M, sound(end_sound))
|
||||
addtimer(CALLBACK(src, .proc/end), end_duration)
|
||||
|
||||
/**
|
||||
* Fully ends the weather
|
||||
*
|
||||
* Effects no longer occur and area overlays are removed
|
||||
* Removes weather from processing completely
|
||||
*
|
||||
*/
|
||||
/datum/weather/proc/end()
|
||||
if(stage == END_STAGE)
|
||||
return 1
|
||||
@@ -115,7 +185,11 @@
|
||||
if(can_weather_act(L))
|
||||
weather_act(L)
|
||||
|
||||
/datum/weather/proc/can_weather_act(mob/living/L) //Can this weather impact a mob?
|
||||
/**
|
||||
* Returns TRUE if the living mob can be affected by the weather
|
||||
*
|
||||
*/
|
||||
/datum/weather/proc/can_weather_act(mob/living/L)
|
||||
var/turf/mob_turf = get_turf(L)
|
||||
if(mob_turf && !(mob_turf.z in impacted_z_levels))
|
||||
return
|
||||
@@ -123,11 +197,19 @@
|
||||
return
|
||||
if(!(get_area(L) in impacted_areas))
|
||||
return
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/datum/weather/proc/weather_act(mob/living/L) //What effect does this weather have on the hapless mob?
|
||||
/**
|
||||
* Affects the mob with whatever the weather does
|
||||
*
|
||||
*/
|
||||
/datum/weather/proc/weather_act(mob/living/L)
|
||||
return
|
||||
|
||||
/**
|
||||
* Updates the overlays on impacted areas
|
||||
*
|
||||
*/
|
||||
/datum/weather/proc/update_areas()
|
||||
for(var/V in impacted_areas)
|
||||
var/area/N = V
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
end_message = "<span class='boldannounce'>The downpour gradually slows to a light shower. It should be safe outside now.</span>"
|
||||
end_sound = 'sound/ambience/acidrain_end.ogg'
|
||||
|
||||
area_type = /area/lavaland/surface/outdoors
|
||||
target_trait = ZTRAIT_MINING
|
||||
area_type = /area
|
||||
protect_indoors = TRUE
|
||||
target_trait = ZTRAIT_ACIDRAIN
|
||||
|
||||
immunity_type = "acid" // temp
|
||||
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
end_duration = 300
|
||||
end_overlay = "light_ash"
|
||||
|
||||
area_type = /area/lavaland/surface/outdoors
|
||||
target_trait = ZTRAIT_MINING
|
||||
area_type = /area
|
||||
protect_indoors = TRUE
|
||||
target_trait = ZTRAIT_ASHSTORM
|
||||
|
||||
immunity_type = "ash"
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@
|
||||
end_duration = 100
|
||||
end_message = "<span class='boldannounce'>The snowfall dies down, it should be safe to go outside again.</span>"
|
||||
|
||||
area_type = /area/awaymission/snowdin/outside
|
||||
target_trait = ZTRAIT_AWAY
|
||||
area_type = /area
|
||||
protect_indoors = TRUE
|
||||
target_trait = ZTRAIT_SNOWSTORM
|
||||
|
||||
immunity_type = "snow"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user