Skip to content

Commit

Permalink
Ghost Content and Spooky Things (#780)
Browse files Browse the repository at this point in the history
* THERE WILL BE BLOOD

* start

* more spooky

* even more spooky

* move

* more abilties

* shatter

* spooky sources

* ghost light

* change ghost level

* give info

* fix

* fixed

* fix params

* fix ranged ability not being unset
  • Loading branch information
Kapu1178 authored Feb 12, 2024
1 parent e4e7655 commit 86b9069
Show file tree
Hide file tree
Showing 38 changed files with 550 additions and 59 deletions.
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 @@ -575,7 +575,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

0 comments on commit 86b9069

Please sign in to comment.