mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-30 08:37:43 +01:00
Merge pull request #190 from nevimer/status_indicators
Status Indicators
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
#define STUNNED "stunned"
|
||||
#define WEAKEN "weakened"
|
||||
#define PARALYSIS "paralysis"
|
||||
#define SLEEPING "sleeping"
|
||||
#define CONFUSED "confused"
|
||||
#define PLANE_STATUS_INDICATOR -12 //Status Indicators that show over mobs' heads when certain things like stuns affect them.
|
||||
#define STATUS_LAYER -2.1
|
||||
#define STATUS_INDICATOR_Y_OFFSET 2 // Offset from the edge of the icon sprite, so 32 pixels plus whatever number is here.
|
||||
#define STATUS_INDICATOR_ICON_X_SIZE 0 // Don't need to care about the Y size due to the origin being on the bottom side.
|
||||
#define STATUS_INDICATOR_ICON_MARGIN 0 // The space between two status indicators. We don't do this with the current icons.
|
||||
#define DEFAULT_MOB_SCALE 1
|
||||
@@ -0,0 +1,3 @@
|
||||
/mob/living/Initialize(mapload)
|
||||
. = ..()
|
||||
AddComponent(/datum/component/status_indicator)
|
||||
@@ -0,0 +1,220 @@
|
||||
|
||||
GLOBAL_LIST_INIT(potential_indicators, list(
|
||||
STUNNED = image(icon = 'modular_zubbers/icons/mob/status_indicators.dmi', icon_state = STUNNED),
|
||||
WEAKEN = image(icon = 'modular_zubbers/icons/mob/status_indicators.dmi', icon_state = WEAKEN),
|
||||
PARALYSIS = image(icon = 'modular_zubbers/icons/mob/status_indicators.dmi', icon_state = PARALYSIS),
|
||||
SLEEPING = image(icon = 'modular_zubbers/icons/mob/status_indicators.dmi', icon_state = SLEEPING),
|
||||
CONFUSED = image(icon = 'modular_zubbers/icons/mob/status_indicators.dmi', icon_state = CONFUSED),
|
||||
))
|
||||
|
||||
/datum/component/status_indicator
|
||||
var/list/status_indicators = null // Will become a list as needed. Contains our status indicator objects. Note, they are actually added to overlays, this just keeps track of what exists.
|
||||
var/mob/living/attached_mob
|
||||
|
||||
/// Returns true if the mob is weakened. Also known as floored.
|
||||
/datum/component/status_indicator/proc/is_weakened()
|
||||
if(!indicator_fakeouts() && \
|
||||
attached_mob.IsKnockdown() || \
|
||||
HAS_TRAIT(attached_mob, TRAIT_FLOORED) && \
|
||||
!HAS_TRAIT_FROM(attached_mob, TRAIT_FLOORED, BUCKLED_TRAIT)
|
||||
)
|
||||
return TRUE
|
||||
|
||||
/// Returns true if the mob is stunned.
|
||||
/datum/component/status_indicator/proc/is_stunned()
|
||||
if(!indicator_fakeouts() && \
|
||||
HAS_TRAIT_FROM(attached_mob, TRAIT_INCAPACITATED, TRAIT_STATUS_EFFECT(STAT_TRAIT)) || \
|
||||
HAS_TRAIT(attached_mob, TRAIT_CRITICAL_CONDITION) || \
|
||||
HAS_TRAIT_FROM(attached_mob, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(STAT_TRAIT)) || \
|
||||
HAS_TRAIT_FROM(attached_mob, TRAIT_IMMOBILIZED, CHOKEHOLD_TRAIT) || \
|
||||
HAS_TRAIT_FROM(attached_mob, TRAIT_INCAPACITATED, TRAIT_STATUS_EFFECT(STAT_TRAIT)) || \
|
||||
HAS_TRAIT_FROM(attached_mob, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(STAT_TRAIT))
|
||||
)
|
||||
return TRUE
|
||||
|
||||
/// Returns true if the mob is paralyzed - for can't fight back purposes.
|
||||
/datum/component/status_indicator/proc/is_paralyzed()
|
||||
if(!indicator_fakeouts() && \
|
||||
attached_mob.IsParalyzed() || \
|
||||
HAS_TRAIT_FROM(attached_mob, TRAIT_FLOORED, CHOKEHOLD_TRAIT) || \
|
||||
HAS_TRAIT_FROM(attached_mob, TRAIT_IMMOBILIZED, TRAIT_STATUS_EFFECT(STAT_TRAIT)) || \
|
||||
HAS_TRAIT(attached_mob, TRAIT_CRITICAL_CONDITION) || \
|
||||
HAS_TRAIT_FROM(attached_mob, TRAIT_INCAPACITATED, STAMINA))
|
||||
return TRUE
|
||||
|
||||
/// Returns true if the mob is unconcious for any reason.
|
||||
/datum/component/status_indicator/proc/is_unconcious()
|
||||
if(!indicator_fakeouts() && HAS_TRAIT(attached_mob, TRAIT_KNOCKEDOUT))
|
||||
return TRUE
|
||||
|
||||
/// Returns true if the mob has confusion.
|
||||
/datum/component/status_indicator/proc/is_confused()
|
||||
if(!indicator_fakeouts() && attached_mob.has_status_effect(/datum/status_effect/confusion))
|
||||
return TRUE
|
||||
|
||||
/datum/component/status_indicator/RegisterWithParent()
|
||||
attached_mob = parent
|
||||
// The Basics
|
||||
RegisterSignal(parent, COMSIG_LIVING_DEATH, PROC_REF(cut_indicators_overlays))
|
||||
RegisterSignal(parent, COMSIG_LIVING_LIFE, PROC_REF(status_indicator_evaluate))
|
||||
// When things actually happen
|
||||
RegisterSignal(parent, COMSIG_LIVING_STATUS_STUN, PROC_REF(status_indicator_evaluate))
|
||||
RegisterSignal(parent, COMSIG_LIVING_STATUS_KNOCKDOWN, PROC_REF(status_indicator_evaluate))
|
||||
RegisterSignal(parent, COMSIG_LIVING_STATUS_PARALYZE, PROC_REF(status_indicator_evaluate))
|
||||
RegisterSignal(parent, COMSIG_LIVING_STATUS_IMMOBILIZE, PROC_REF(status_indicator_evaluate))
|
||||
RegisterSignal(parent, COMSIG_LIVING_STATUS_UNCONSCIOUS, PROC_REF(status_indicator_evaluate))
|
||||
RegisterSignal(parent, COMSIG_MOB_LOGIN, PROC_REF(apply_pref_on_login))
|
||||
|
||||
/datum/component/status_indicator/proc/apply_pref_on_login()
|
||||
var/atom/movable/screen/plane_master/game_world_upper_fov_hidden/status_indicator/local_status = locate() in attached_mob.client.screen
|
||||
if(local_status)
|
||||
. = attached_mob.client.prefs.read_preference(/datum/preference/toggle/enable_status_indicators)
|
||||
local_status.alpha = (.) ? 255 : 0
|
||||
|
||||
/datum/component/status_indicator/UnregisterFromParent()
|
||||
QDEL_NULL(status_indicators)
|
||||
UnregisterSignal(attached_mob, COMSIG_LIVING_DEATH)
|
||||
UnregisterSignal(attached_mob, COMSIG_LIVING_LIFE)
|
||||
UnregisterSignal(attached_mob, COMSIG_LIVING_STATUS_STUN)
|
||||
UnregisterSignal(attached_mob, COMSIG_LIVING_STATUS_KNOCKDOWN)
|
||||
UnregisterSignal(attached_mob, COMSIG_LIVING_STATUS_PARALYZE)
|
||||
UnregisterSignal(attached_mob, COMSIG_LIVING_STATUS_IMMOBILIZE)
|
||||
UnregisterSignal(attached_mob, COMSIG_LIVING_STATUS_UNCONSCIOUS)
|
||||
attached_mob = null
|
||||
/// This proc makes it so that mobs that have status indicators are checked to remove them, especially in fakeout situations.
|
||||
/datum/component/status_indicator/proc/check_indicators()
|
||||
if(status_indicators)
|
||||
status_indicator_evaluate()
|
||||
|
||||
/// Receives signals to update on carbon health updates. Checks if the mob is dead - if true, removes all the indicators. Then, we determine what status indicators the mob should carry or remove.
|
||||
/datum/component/status_indicator/proc/status_indicator_evaluate()
|
||||
SIGNAL_HANDLER
|
||||
if(attached_mob.stat == DEAD)
|
||||
return
|
||||
else
|
||||
weaken_indicator_update()
|
||||
paralyzed_indicator_update()
|
||||
stunned_indicator_update()
|
||||
unconcious_indicator_update()
|
||||
confused_indicator_update()
|
||||
return
|
||||
/// Cases in which no status indicators should appear above a mob, such as changeling revive and regen coma.
|
||||
/datum/component/status_indicator/proc/indicator_fakeouts()
|
||||
if(HAS_TRAIT(attached_mob, TRAIT_DEATHCOMA))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/component/status_indicator/proc/weaken_indicator_update()
|
||||
SIGNAL_HANDLER
|
||||
is_weakened() ? add_status_indicator(WEAKEN) : remove_status_indicator(WEAKEN)
|
||||
/datum/component/status_indicator/proc/paralyzed_indicator_update()
|
||||
SIGNAL_HANDLER
|
||||
is_paralyzed() ? add_status_indicator(PARALYSIS) : remove_status_indicator(PARALYSIS)
|
||||
/datum/component/status_indicator/proc/stunned_indicator_update()
|
||||
SIGNAL_HANDLER
|
||||
is_stunned() ? add_status_indicator(STUNNED) : remove_status_indicator(STUNNED)
|
||||
/datum/component/status_indicator/proc/unconcious_indicator_update()
|
||||
SIGNAL_HANDLER
|
||||
is_unconcious() ? add_status_indicator(SLEEPING) : remove_status_indicator(SLEEPING)
|
||||
/datum/component/status_indicator/proc/confused_indicator_update()
|
||||
SIGNAL_HANDLER
|
||||
is_confused() ? add_status_indicator(CONFUSED) : remove_status_indicator(CONFUSED)
|
||||
|
||||
/// Adds a status indicator to the mob. Takes an image as an argument. If it exists, it won't dupe it.
|
||||
/datum/component/status_indicator/proc/add_status_indicator(image/prospective_indicator)
|
||||
if(get_status_indicator(prospective_indicator)) // No duplicates, please.
|
||||
return
|
||||
|
||||
prospective_indicator = GLOB.potential_indicators[prospective_indicator]
|
||||
prospective_indicator.loc = src
|
||||
LAZYADD(status_indicators, prospective_indicator)
|
||||
handle_status_indicators(prospective_indicator)
|
||||
|
||||
/// Similar to add_status_indicator() but removes it instead, and nulls the list if it becomes empty as a result.
|
||||
/datum/component/status_indicator/proc/remove_status_indicator(image/prospective_indicator)
|
||||
prospective_indicator = get_status_indicator(prospective_indicator)
|
||||
|
||||
attached_mob.cut_overlay(prospective_indicator)
|
||||
LAZYREMOVE(status_indicators, prospective_indicator)
|
||||
handle_status_indicators(prospective_indicator)
|
||||
|
||||
/// Finds a status indicator on a mob.
|
||||
/datum/component/status_indicator/proc/get_status_indicator(image/prospective_indicator)
|
||||
|
||||
for(var/image/indicator in status_indicators)
|
||||
if(indicator.icon_state == prospective_indicator)
|
||||
return indicator
|
||||
return LAZYACCESS(status_indicators, LAZYFIND(status_indicators, prospective_indicator))
|
||||
|
||||
/// Cuts all the indicators on a mob in a loop.
|
||||
/datum/component/status_indicator/proc/cut_indicators_overlays()
|
||||
SIGNAL_HANDLER
|
||||
for(var/prospective_indicator in status_indicators)
|
||||
attached_mob.cut_overlay(prospective_indicator)
|
||||
|
||||
/// Refreshes the indicators over a mob's head. Should only be called when adding or removing a status indicator with the above procs,
|
||||
/// or when the mob changes size visually for some reason.
|
||||
/datum/component/status_indicator/proc/handle_status_indicators(image/prospective_indicator)
|
||||
// First, get rid of all the overlays.
|
||||
|
||||
cut_indicators_overlays()
|
||||
|
||||
if(!LAZYLEN(status_indicators))
|
||||
return
|
||||
|
||||
var/mob/living/carbon/my_carbon_mob = attached_mob
|
||||
|
||||
var/icon_scale = get_icon_scale(my_carbon_mob)
|
||||
|
||||
if(my_carbon_mob.stat == DEAD)
|
||||
cut_indicators_overlays()
|
||||
return
|
||||
|
||||
// Now put them back on in the right spot.
|
||||
var/our_sprite_x = 16 * icon_scale
|
||||
var/our_sprite_y = 24 * icon_scale
|
||||
|
||||
var/x_offset = our_sprite_x // Add your own offset here later if you want.
|
||||
var/y_offset = our_sprite_y + STATUS_INDICATOR_Y_OFFSET
|
||||
|
||||
// Calculates how 'long' the row of indicators and the margin between them should be.
|
||||
// The goal is to have the center of that row be horizontally aligned with the sprite's center.
|
||||
var/expected_status_indicator_length = (STATUS_INDICATOR_ICON_X_SIZE * status_indicators.len) + (STATUS_INDICATOR_ICON_MARGIN * max(status_indicators.len - 1, 0))
|
||||
var/current_x_position = (x_offset / 2) - (expected_status_indicator_length / 2)
|
||||
|
||||
// In /mob/living's `update_transform()`, the sprite is horizontally shifted when scaled up, so that the center of the sprite doesn't move to the right.
|
||||
// Because of that, this adjustment needs to happen with the future indicator row as well, or it will look bad.
|
||||
current_x_position -= 16 * (icon_scale - DEFAULT_MOB_SCALE)
|
||||
|
||||
// Now the indicator row can actually be built.
|
||||
for(var/all_indicators in status_indicators)
|
||||
var/image/indicator = all_indicators
|
||||
|
||||
// This is a semi-HUD element, in a similar manner as medHUDs, in that they're 'above' everything else in the world,
|
||||
// but don't pierce obfuscation layers such as blindness or darkness, unlike actual HUD elements like inventory slots.
|
||||
indicator.plane = GAME_PLANE_UPPER_FOV_HIDDEN
|
||||
indicator.layer = STATUS_LAYER
|
||||
indicator.appearance_flags = PIXEL_SCALE|TILE_BOUND|NO_CLIENT_COLOR|RESET_COLOR|RESET_ALPHA|RESET_TRANSFORM|KEEP_APART
|
||||
indicator.pixel_y = y_offset
|
||||
indicator.pixel_x = current_x_position
|
||||
my_carbon_mob.add_overlay(indicator)
|
||||
// Adding the margin space every time saves a conditional check on the last iteration,
|
||||
// and it won't cause any issues since no more icons will be added, and the var is not used for anything else.
|
||||
current_x_position += STATUS_INDICATOR_ICON_X_SIZE + STATUS_INDICATOR_ICON_MARGIN
|
||||
|
||||
/datum/component/status_indicator/proc/get_icon_scale(livingmob)
|
||||
if(!iscarbon(livingmob)) // normal mobs are always 1 for scale - hopefully all borgs and simplemobs get this one
|
||||
return DEFAULT_MOB_SCALE
|
||||
var/mob/living/carbon/passed_mob = livingmob // we're possibly a player! We have size prefs!
|
||||
var/mysize = (passed_mob.dna?.current_body_size ? passed_mob.dna.current_body_size : DEFAULT_MOB_SCALE)
|
||||
return mysize
|
||||
|
||||
/atom/movable/screen/plane_master/game_world_upper_fov_hidden/status_indicator
|
||||
name = "Status Indicator Plane"
|
||||
documentation = "Status Indicator Plane"
|
||||
plane = GAME_PLANE_UPPER_FOV_HIDDEN
|
||||
start_hidden = FALSE
|
||||
|
||||
#undef STATUS_INDICATOR_Y_OFFSET
|
||||
#undef STATUS_INDICATOR_ICON_X_SIZE
|
||||
#undef STATUS_INDICATOR_ICON_MARGIN
|
||||
@@ -0,0 +1,26 @@
|
||||
/datum/preference/toggle/enable_status_indicators
|
||||
category = PREFERENCE_CATEGORY_GAME_PREFERENCES
|
||||
savefile_key = "enable_status_indicators"
|
||||
savefile_identifier = PREFERENCE_PLAYER
|
||||
|
||||
/datum/preference/toggle/enable_status_indicators/create_default_value()
|
||||
return TRUE
|
||||
|
||||
|
||||
/datum/preference/toggle/enable_status_indicators/apply_to_client(client/client, value)
|
||||
if(client.mob)
|
||||
var/datum/component/status_indicator/mystatus = client.mob.GetComponent(/datum/component/status_indicator)
|
||||
if(mystatus)
|
||||
mystatus.apply_pref_on_login()
|
||||
|
||||
/* if(client && client.prefs)
|
||||
. = client?.prefs?.read_preference(/datum/preference/toggle/enable_status_indicators)
|
||||
var/atom/movable/screen/plane_master/status/status_indicators = locate() in client.screen
|
||||
if(isnull(status_indicators))
|
||||
new /atom/movable/screen/plane_master/status in client.screen
|
||||
status_indicators = locate() in client.screen
|
||||
if(length(status_indicators) < 1)
|
||||
pop(status_indicators)
|
||||
(.) ? (status_indicators.alpha = 255) : (status_indicators.alpha = 0)
|
||||
|
||||
*/
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.6 KiB |
@@ -390,6 +390,7 @@
|
||||
#include "code\__DEFINES\~~bubber_defines\access.dm"
|
||||
#include "code\__DEFINES\~~bubber_defines\quirk_whitelist.dm"
|
||||
#include "code\__DEFINES\~~bubber_defines\species.dm"
|
||||
#include "code\__DEFINES\~~bubber_defines\status_indicator_defines.dm"
|
||||
#include "code\__DEFINES\~~bubber_defines\traits.dm"
|
||||
#include "code\__HELPERS\_auxtools_api.dm"
|
||||
#include "code\__HELPERS\_lists.dm"
|
||||
@@ -6724,6 +6725,8 @@
|
||||
#include "modular_zubbers\code\modules\mod\mod_theme.dm"
|
||||
#include "modular_zubbers\code\modules\mod\mod_types.dm"
|
||||
#include "modular_zubbers\code\modules\projectiles\guns\energy\pulse.dm"
|
||||
#include "modular_zubbers\code\modules\status_indicators\status_indicator.dm"
|
||||
#include "modular_zubbers\code\modules\status_indicators\status_indicator_pref.dm"
|
||||
#include "modular_zubbers\code\modules\surgery\bodyparts\species_parts\misc_bodyparts.dm"
|
||||
#include "modular_zubbers\code\modules\vending\wardrobe.dm"
|
||||
// END_INCLUDE
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import { CheckboxInput, FeatureToggle } from '../../base';
|
||||
|
||||
export const enable_status_indicators: FeatureToggle = {
|
||||
name: 'Display Status Indicators',
|
||||
category: 'GAMEPLAY',
|
||||
description: 'This toggles whether or not you will see status indicators.',
|
||||
component: CheckboxInput,
|
||||
};
|
||||
Reference in New Issue
Block a user