Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ghost Content and Spooky Things #780

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions code/__DEFINES/credits.dm
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#define BLACKBOX_FEEDBACK_NUM(key) (SSblackbox.feedback[key] ? SSblackbox.feedback[key].json["data"] : null)

#define BLACKBOX_FEEDBACK_NESTED_TALLY(key) (SSblackbox.feedback[key] ? SSblackbox.feedback[key].json["data"] : null)
5 changes: 5 additions & 0 deletions code/__DEFINES/dcs/signals/signals_area.dm
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,8 @@
#define COMSIG_ALARM_TRIGGERED "comsig_alarm_triggered"
///Send when an alarm source is cleared (alarm_type, area/source_area)
#define COMSIG_ALARM_CLEARED "comsig_alarm_clear"


// Spook level signals
///from base of area/proc/adjust_spook_level(): (area, old_spook_level)
#define AREA_SPOOK_LEVEL_CHANGED "comsig_area_spook_level_changed"
17 changes: 17 additions & 0 deletions code/__DEFINES/spooky_defines.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#define SPOOK_LEVEL_WEAK_POWERS 10
#define SPOOK_LEVEL_MEDIUM_POWERS 30
#define SPOOK_LEVEL_DESTRUCTIVE_POWERS 70

#define SPOOK_LEVEL_OBJECT_ROTATION SPOOK_LEVEL_WEAK_POWERS

// Spook amounts
#define SPOOK_AMT_CORPSE 5
#define SPOOK_AMT_BLOOD_SPLATTER 2
#define SPOOK_AMT_BLOOD_STREAK 1
#define SPOOK_AMT_BLOOD_DROP 0.5

#define RECORD_GHOST_POWER(power) \
do {\
var/area/A = get_area(power.owner); \
SSblackbox.record_feedback("nested tally", "ghost_power_used", 1, list(A?.name || "NULL", power.name)); \
} while (FALSE)
5 changes: 0 additions & 5 deletions code/_onclick/hud/rendering/plane_master.dm
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,12 @@
/atom/movable/screen/plane_master/floor
name = "floor plane master"
plane = FLOOR_PLANE
appearance_flags = PLANE_MASTER
blend_mode = BLEND_OVERLAY

///Contains most things in the game world
/atom/movable/screen/plane_master/game_world
name = "game world plane master"
plane = GAME_PLANE
appearance_flags = PLANE_MASTER //should use client color
blend_mode = BLEND_OVERLAY

/atom/movable/screen/plane_master/seethrough
Expand All @@ -52,20 +50,17 @@
/atom/movable/screen/plane_master/massive_obj
name = "massive object plane master"
plane = MASSIVE_OBJ_PLANE
appearance_flags = PLANE_MASTER //should use client color
blend_mode = BLEND_OVERLAY

/atom/movable/screen/plane_master/ghost
name = "ghost plane master"
plane = GHOST_PLANE
appearance_flags = PLANE_MASTER //should use client color
blend_mode = BLEND_OVERLAY
render_relay_plane = RENDER_PLANE_NON_GAME

/atom/movable/screen/plane_master/point
name = "point plane master"
plane = POINT_PLANE
appearance_flags = PLANE_MASTER //should use client color
blend_mode = BLEND_OVERLAY

/**
Expand Down
2 changes: 0 additions & 2 deletions code/controllers/configuration/entries/game_options.dm
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,6 @@
min_val = 0
max_val = 100

/datum/config_entry/flag/ghost_interaction

/datum/config_entry/flag/near_death_experience //If carbons can hear ghosts when unconscious and very close to death

/datum/config_entry/flag/silent_ai
Expand Down
8 changes: 8 additions & 0 deletions code/controllers/subsystem/blackbox.dm
Original file line number Diff line number Diff line change
Expand Up @@ -233,27 +233,35 @@ Versioning
return
if(!islist(FV.json["data"]))
FV.json["data"] = list()

if(overwrite)
FV.json["data"] = data
else
FV.json["data"] |= data

if("amount")
FV.json["data"] += increment

if("tally")
if(!islist(FV.json["data"]))
FV.json["data"] = list()
FV.json["data"]["[data]"] += increment

if("nested tally")
if(!islist(data))
return

if(!islist(FV.json["data"]))
FV.json["data"] = list()
FV.json["data"] = record_feedback_recurse_list(FV.json["data"], data, increment)

if("associative")
if(!islist(data))
return

if(!islist(FV.json["data"]))
FV.json["data"] = list()

var/pos = length(FV.json["data"]) + 1
FV.json["data"]["[pos]"] = list() //in 512 "pos" can be replaced with "[FV.json["data"].len+1]"
for(var/i in data)
Expand Down
19 changes: 10 additions & 9 deletions code/controllers/subsystem/credits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -117,22 +117,23 @@ SUBSYSTEM_DEF(credits)
if(customized_name)
episode_name = customized_name
return

var/list/drafted_names = list()
var/list/name_reasons = list()
var/list/is_rare_assoc_list = list()

for(var/datum/episode_name/N as anything in episode_names)
drafted_names["[N.thename]"] = N.weight
name_reasons["[N.thename]"] = N.reason
is_rare_assoc_list["[N.thename]"] = N.rare
episode_name = pick_weight(drafted_names)
episode_reason = name_reasons[episode_name]
if(is_rare_assoc_list[episode_name] == TRUE)
drafted_names[N] = N.weight

var/datum/episode_name/chosen = pick_weight(drafted_names)
episode_name = chosen.thename
episode_reason = chosen.reason
if(chosen.rare)
rare_episode_name = TRUE

/datum/controller/subsystem/credits/proc/finalize_episodestring()
var/season = time2text(world.timeofday,"YY")
var/episodenum = GLOB.round_id || 1
episode_string = "<h1><span id='episodenumber'>SEASON [season] EPISODE [episodenum]</span><br><span id='episodename' title='[episode_reason]'>[episode_name]</span></h1><br><div style='padding-bottom: 75px;'></div>"
var/reason = episode_reason ? "<br><h3>[episode_reason]</h3>" : ""
episode_string = "<h1><span id='episodenumber'>SEASON [season] EPISODE [episodenum]</span><br><span id='episodename'>[episode_name]</span></h1>[reason]<br><div style='padding-bottom: 75px;'></div>"
log_game("So ends [is_rerun() ? "another rerun of " : ""]SEASON [season] EPISODE [episodenum] - [episode_name] ... [customized_ss]")

/datum/controller/subsystem/credits/proc/finalize_disclaimerstring()
Expand Down
39 changes: 34 additions & 5 deletions code/datums/actions/cooldown_action.dm
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
check_flags = NONE
transparent_when_unavailable = FALSE

/// If TRUE, will log action usage to the owner's action log.
var/write_log = FALSE

/// The actual next time this ability can be used
var/next_use_time = 0
/// The stat panel this action shows up in the stat panel in. If null, will not show up.
Expand Down Expand Up @@ -216,13 +219,20 @@

/// Intercepts client owner clicks to activate the ability
/datum/action/cooldown/proc/InterceptClickOn(mob/living/caller, params, atom/target)
if(!IsAvailable(feedback = TRUE))
return FALSE
if(!target)
return FALSE
. = TRUE
if(istext(params))
params = params2list(params)

if(params?[RIGHT_CLICK])
unset_click_ability(caller, TRUE)
return

if(!target || !IsAvailable(feedback = TRUE))
return

// The actual action begins here
if(!PreActivate(target))
return FALSE
return TRUE

// And if we reach here, the action was complete successfully
if(unset_after_click)
Expand All @@ -235,13 +245,21 @@
/datum/action/cooldown/proc/PreActivate(atom/target)
if(SEND_SIGNAL(owner, COMSIG_MOB_ABILITY_STARTED, src) & COMPONENT_BLOCK_ABILITY_START)
return

if(!is_valid_target(target))
return

// Note, that PreActivate handles no cooldowns at all by default.
// Be sure to call StartCooldown() in Activate() where necessary.
. = Activate(target)

// There is a possibility our action (or owner) is qdeleted in Activate().
if(!QDELETED(src) && !QDELETED(owner))
SEND_SIGNAL(owner, COMSIG_MOB_ABILITY_FINISHED, src)

if(owner?.ckey)
owner.log_message("used action [name][target != owner ? " on / at [target]":""].", LOG_ATTACK)

/// To be implemented by subtypes (if not generic)
/datum/action/cooldown/proc/Activate(atom/target)
var/total_delay = 0
Expand All @@ -251,6 +269,7 @@
addtimer(CALLBACK(ability, PROC_REF(Activate), target), total_delay)
total_delay += initialized_actions[ability]
StartCooldown()
return TRUE

/// Cancels melee attacks if they are on cooldown.
/datum/action/cooldown/proc/handle_melee_attack(mob/source, mob/target)
Expand Down Expand Up @@ -330,3 +349,13 @@
SEND_SIGNAL(src, COMSIG_ACTION_SET_STATPANEL, stat_panel_data)

return stat_panel_data

/**
* Check if the target we're casting on is a valid target.
* For no-target (self cast) actions, the target being checked (cast_on) is the caster.
* For click_to_activate actions, the target being checked is the clicked atom.
*
* Return TRUE if cast_on is valid, FALSE otherwise
*/
/datum/action/cooldown/proc/is_valid_target(atom/cast_on)
return TRUE
6 changes: 4 additions & 2 deletions code/datums/components/rotation.dm
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,10 @@
/datum/component/simple_rotation/proc/CanUserRotate(mob/user, degrees)
if(isliving(user) && user.canUseTopic(parent, USE_CLOSE|USE_DEXTERITY))
return TRUE
if((rotation_flags & ROTATION_GHOSTS_ALLOWED) && isobserver(user) && CONFIG_GET(flag/ghost_interaction))
return TRUE
if((rotation_flags & ROTATION_GHOSTS_ALLOWED) && isobserver(user))
var/area/A = get_area(parent)
if(A?.spook_level >= SPOOK_LEVEL_OBJECT_ROTATION)
return TRUE
return FALSE

/datum/component/simple_rotation/proc/CanBeRotated(mob/user, degrees)
Expand Down
9 changes: 9 additions & 0 deletions code/game/area/areas.dm
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
/// If a room is too big it doesn't have beauty.
var/beauty_threshold = 150

/// Used by ghosts to grant new powers. See /datum/component/spook_factor
var/spook_level

/// For space, the asteroid, etc. Used with blueprints or with weather to determine if we are adding a new area (vs editing a station room)
var/outdoors = FALSE

Expand Down Expand Up @@ -530,3 +533,9 @@ GLOBAL_LIST_EMPTY(teleportlocs)

for(var/datum/listener in airalarms + firealarms + firedoors)
SEND_SIGNAL(listener, COMSIG_FIRE_ALERT, code)

/// Adjusts the spook level and sends out a signal
/area/proc/adjust_spook_level(adj)
var/old = spook_level
spook_level += adj
SEND_SIGNAL(src, AREA_SPOOK_LEVEL_CHANGED, src, old)
4 changes: 4 additions & 0 deletions code/game/area/areas/station.dm
Original file line number Diff line number Diff line change
Expand Up @@ -935,6 +935,8 @@
min_ambience_cooldown = 90 SECONDS
max_ambience_cooldown = 180 SECONDS

spook_level = SPOOK_AMT_CORPSE * -2 // We can expect like two dudes to be dead in here at all times.

/area/station/medical/abandoned
name = "\improper Abandoned Medbay"
icon_state = "abandoned_medbay"
Expand Down Expand Up @@ -1010,6 +1012,8 @@
sound_environment = SOUND_AREA_SMALL_ENCLOSED
lightswitch = FALSE

spook_level = SPOOK_AMT_CORPSE * -10 // The morgue lays spirits to rest or something

/area/station/medical/chemistry
name = "Chemistry"
icon_state = "chem"
Expand Down
2 changes: 1 addition & 1 deletion code/game/machinery/doors/door.dm
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,7 @@ DEFINE_INTERACTABLE(/obj/machinery/door)
. = ..()

/obj/machinery/door/proc/knock_on(mob/user)
user.changeNext_move(CLICK_CD_MELEE)
user?.changeNext_move(CLICK_CD_MELEE)
playsound(src, knock_sound, 100, TRUE)

#undef DOOR_CLOSE_WAIT
12 changes: 12 additions & 0 deletions code/game/objects/effects/decals/cleanable/humans.dm
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
desc = "It's red and gooey. Perhaps it's the chef's cooking?"
icon = 'icons/effects/blood.dmi'
icon_state = "floor1"
appearance_flags = TILE_BOUND|PIXEL_SCALE|LONG_GLIDE|NO_CLIENT_COLOR
random_icon_states = list("floor1", "floor2", "floor3", "floor4", "floor5", "floor6", "floor7")
blood_state = BLOOD_STATE_HUMAN
bloodiness = BLOOD_AMOUNT_PER_DECAL
beauty = -100
clean_type = CLEAN_TYPE_BLOOD

var/smell_intensity = INTENSITY_STRONG
var/spook_factor = SPOOK_AMT_BLOOD_SPLATTER

var/should_dry = TRUE
/// How long should it take for blood to dry?
var/dry_duration = 10 MINUTES
Expand Down Expand Up @@ -54,6 +58,8 @@
bloodiness = 0
color = COLOR_GRAY //not all blood splatters have their own sprites... It still looks pretty nice
qdel(GetComponent(/datum/component/smell))
if(spook_factor)
AddComponent(/datum/component/spook_factor, spook_factor)
return PROCESS_KILL

/obj/effect/decal/cleanable/blood/replace_decal(obj/effect/decal/cleanable/blood/C)
Expand All @@ -69,6 +75,7 @@
/obj/effect/decal/cleanable/blood/old/Initialize(mapload, list/datum/disease/diseases)
add_blood_DNA(list("Non-human DNA" = random_blood_type())) // Needs to happen before ..()
. = ..()
AddComponent(/datum/component/spook_factor, SPOOK_AMT_BLOOD_SPLATTER)

/obj/effect/decal/cleanable/blood/splatter
icon_state = "gibbl1"
Expand Down Expand Up @@ -125,13 +132,15 @@
drydesc = "They look bloody and gruesome while some terrible smell fills the air."
decal_reagent = /datum/reagent/liquidgibs
reagent_amount = 5

smell_intensity = INTENSITY_STRONG
///Information about the diseases our streaking spawns
var/list/streak_diseases

/obj/effect/decal/cleanable/blood/gibs/Initialize(mapload, list/datum/disease/diseases)
. = ..()
RegisterSignal(src, COMSIG_MOVABLE_PIPE_EJECTING, PROC_REF(on_pipe_eject))
AddComponent(/datum/component/spook_factor, SPOOK_AMT_BLOOD_STREAK)

/obj/effect/decal/cleanable/blood/gibs/replace_decal(obj/effect/decal/cleanable/C)
return FALSE //Never fail to place us
Expand Down Expand Up @@ -233,6 +242,7 @@
smell_intensity = INTENSITY_SUBTLE
dry_duration = 4 MINUTES

spook_factor = SPOOK_AMT_BLOOD_DROP
/// Keeps track of how many drops of blood this decal has. See blood.dm
var/drips = 1

Expand Down Expand Up @@ -467,6 +477,8 @@ GLOBAL_LIST_EMPTY(bloody_footprints_cache)
color = "#ff0000"
smell_intensity = INTENSITY_SUBTLE

spook_factor = SPOOK_AMT_BLOOD_STREAK

/obj/effect/decal/cleanable/blood/squirt/Initialize(mapload, direction, list/blood_dna)
. = ..()
dir = direction
Expand Down
7 changes: 5 additions & 2 deletions code/game/objects/structures/window.dm
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@

return TRUE

/obj/structure/window/proc/knock_on()
playsound(src, knock_sound, 50, TRUE)

/obj/structure/window/proc/on_exit(datum/source, atom/movable/leaving, direction)
SIGNAL_HANDLER

Expand All @@ -151,7 +154,7 @@
user.changeNext_move(CLICK_CD_MELEE)
user.visible_message(span_notice("Something knocks on [src]."))
add_fingerprint(user)
playsound(src, knock_sound, 50, TRUE)
knock_on()
return COMPONENT_CANCEL_ATTACK_CHAIN


Expand All @@ -171,7 +174,7 @@
if(!user.combat_mode)
user.visible_message(span_notice("[user] knocks on [src]."), \
span_notice("You knock on [src]."))
playsound(src, knock_sound, 50, TRUE)
knock_on()
else
user.visible_message(span_warning("[user] bashes [src]!"), \
span_warning("You bash [src]!"))
Expand Down
2 changes: 1 addition & 1 deletion code/modules/admin/admin_verbs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ GLOBAL_PROTECT(admin_verbs_hideable)
log_admin("[key_name(usr)] admin ghosted.")
message_admins("[key_name_admin(usr)] admin ghosted.")
var/mob/body = mob
body.ghostize(1)
body.ghostize(TRUE, TRUE)
init_verbs()
if(body && !body.key)
body.key = "@[key]" //Haaaaaaaack. But the people have spoken. If it breaks; blame adminbus
Expand Down
Loading
Loading