Skip to content

Commit

Permalink
Dynamic human icons (#3462)
Browse files Browse the repository at this point in the history
<!-- Write **BELOW** The Headers and **ABOVE** The comments else it may
not be viewable. -->
<!-- You can view Contributing.MD for a detailed description of the pull
request process. -->

## About The Pull Request
You can now generate simple mob icons via corpse spawners and its cached
so its not terribly expensive
port of tgstation/tgstation#72517
and to fix some unit tests ports (which will also minorly improve init
times i suspect) that changes how overlays render.
tgstation/tgstation#69696
tgstation/tgstation#73965
tgstation/tgstation#71706
currently only applied to frontiersmen but its very simple to do it for
any of them

![image](https://github.com/user-attachments/assets/a677e6c9-5279-4f17-ad82-c8e3a418bfbb)

- [x] FIX THAT TEST BEFORE MERG

<!-- Describe The Pull Request. Please be sure every change is
documented or this can delay review and even discourage maintainers from
merging your PR! -->

## Why It's Good For The Game
No more ancient simple mob sprites once everything is moved over
<!-- Please add a short description of why you think these changes would
benefit the game. If you can't justify it in words, it might not be
worth adding. -->

## Changelog

:cl: FalloutFalcon, Fikou, a hood by Viro
refactor: Humanoid mobs automatically generate their sprites, they no
longer will be outdated.
refactor: Ports some tg overlay improvments.
/:cl:

<!-- Both :cl:'s are required for the changelog to work! You can put
your name to the right of the first :cl: if you want to overwrite your
GitHub username as author ingame. -->
<!-- You can use multiple of the same prefix (they're only used for the
icon ingame) and delete the unneeded ones. Despite some of the tags,
changelogs should generally represent how a player might be affected by
the changes rather than a summary of the PR's contents. -->

---------

Signed-off-by: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com>
  • Loading branch information
FalloutFalcon authored Feb 6, 2025
1 parent 1f10735 commit c57da5b
Show file tree
Hide file tree
Showing 26 changed files with 427 additions and 328 deletions.
26 changes: 12 additions & 14 deletions code/__DEFINES/flags.dm
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,29 @@ GLOBAL_LIST_INIT(bitflags, list(1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 204
#define CONDUCT_1 (1<<5)
/// For machines and structures that should not break into parts, eg, holodeck stuff
#define NODECONSTRUCT_1 (1<<7)
/// atom queued to SSoverlay
#define OVERLAY_QUEUED_1 (1<<8)
/// item has priority to check when entering or leaving
#define ON_BORDER_1 (1<<9)
#define ON_BORDER_1 (1<<8)
//Whether or not this atom shows screentips when hovered over
#define NO_SCREENTIPS_1 (1<<10)
#define NO_SCREENTIPS_1 (1<<9)
/// Prevent clicking things below it on the same turf eg. doors/ fulltile windows
#define PREVENT_CLICK_UNDER_1 (1<<11)
#define HOLOGRAM_1 (1<<12)
#define PREVENT_CLICK_UNDER_1 (1<<10)
#define HOLOGRAM_1 (1<<11)
/// Prevents mobs from getting chainshocked by teslas and the supermatter
#define SHOCKED_1 (1<<13)
#define SHOCKED_1 (1<<12)
///Whether /atom/Initialize() has already run for the object
#define INITIALIZED_1 (1<<14)
#define INITIALIZED_1 (1<<13)
/// was this spawned by an admin? used for stat tracking stuff.
#define ADMIN_SPAWNED_1 (1<<15)
#define ADMIN_SPAWNED_1 (1<<14)
/// should not get harmed if this gets caught by an explosion?
#define PREVENT_CONTENTS_EXPLOSION_1 (1<<16)
#define PREVENT_CONTENTS_EXPLOSION_1 (1<<15)
/// should the contents of this atom be acted upon
#define RAD_PROTECT_CONTENTS_1 (1 << 17)
#define RAD_PROTECT_CONTENTS_1 (1 << 16)
/// should this object be allowed to be contaminated
#define RAD_NO_CONTAMINATE_1 (1 << 18)
#define RAD_NO_CONTAMINATE_1 (1 << 17)
///Use when this shouldn't be obscured by large icons, like trees.
#define SHOW_BEHIND_LARGE_ICONS_1 (1<<12)
#define SHOW_BEHIND_LARGE_ICONS_1 (1<<18)
/// Should we use the initial icon for display? Mostly used by overlay only objects
#define HTML_USE_INITAL_ICON_1 (1<<20)
#define HTML_USE_INITAL_ICON_1 (1<<19)

// Update flags for [/atom/proc/update_appearance]
/// Update the atom's name
Expand Down
3 changes: 3 additions & 0 deletions code/__DEFINES/mobs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -444,3 +444,6 @@

//Saves a proc call, life is suffering. If who has no targets_from var, we assume it's just who
#define GET_TARGETS_FROM(who) (who.targets_from ? who.get_targets_from() : who)

/// In dynamic human icon gen we don't replace the held item.
#define NO_REPLACE 0
27 changes: 27 additions & 0 deletions code/__DEFINES/overlays.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/* I do want this however this currently only fails on decals specificly in create and destroy.
Updating the overlays in genral was already pretty unatomic.area
I've added porting some updated decal code from tg to resolve this to my list.
WARNING("Too many overlays on [changed_on.type] - [length(changed_on.overlays)], refusing to update and cutting.\
\n What follows is a printout of all existing overlays at the time of the overflow \n[text_lays]"); \
*/

// A reasonable number of maximum overlays an object needs
// If you think you need more, rethink it
#define MAX_ATOM_OVERLAYS 100

/// Checks if an atom has reached the overlay limit, and make a loud error if it does.
#define VALIDATE_OVERLAY_LIMIT(changed_on) \
if(length(changed_on.overlays) >= MAX_ATOM_OVERLAYS) { \
changed_on.overlays.Cut(); \
} \

/// Performs any operations that ought to run after an appearance change
#define POST_OVERLAY_CHANGE(changed_on) \
if(alternate_appearances) { \
for(var/I in changed_on.alternate_appearances){\
var/datum/atom_hud/alternate_appearance/AA = changed_on.alternate_appearances[I];\
if(AA.transfer_overlays){\
AA.copy_overlays(changed_on, TRUE);\
}\
} \
}
6 changes: 1 addition & 5 deletions code/__DEFINES/stat_tracking.dm
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@
#define STAT_LOG_ENTRY(entrylist, entryname) \
var/list/STAT_ENTRY = entrylist[entryname] || (entrylist[entryname] = new /list(STAT_ENTRY_LENGTH)); \
STAT_ENTRY[STAT_ENTRY_TIME] += STAT_TIME; \
var/STAT_INCR_AMOUNT = min(1, 2**round((STAT_ENTRY[STAT_ENTRY_COUNT] || 0)/SHORT_REAL_LIMIT)); \
if (STAT_INCR_AMOUNT == 1 || prob(100/STAT_INCR_AMOUNT)) { \
STAT_ENTRY[STAT_ENTRY_COUNT] += STAT_INCR_AMOUNT; \
}; \

STAT_ENTRY[STAT_ENTRY_COUNT] += 1;


25 changes: 0 additions & 25 deletions code/__DEFINES/subsystems.dm
Original file line number Diff line number Diff line change
Expand Up @@ -241,31 +241,6 @@
#define SSEXPLOSIONS_TURFS 2
#define SSEXPLOSIONS_THROWS 3

//! ## Overlays subsystem

///Compile all the overlays for an atom from the cache lists
// |= on overlays is not actually guaranteed to not add same appearances but we're optimistically using it anyway.
#define COMPILE_OVERLAYS(A) \
do{ \
var/list/ad = A.add_overlays; \
var/list/rm = A.remove_overlays; \
if(LAZYLEN(rm)){ \
A.overlays -= rm; \
rm.Cut(); \
} \
if(LAZYLEN(ad)){ \
A.overlays |= ad; \
ad.Cut(); \
} \
for(var/I in A.alternate_appearances){ \
var/datum/atom_hud/alternate_appearance/AA = A.alternate_appearances[I]; \
if(AA.transfer_overlays){ \
AA.copy_overlays(A, TRUE); \
} \
} \
A.flags_1 &= ~OVERLAY_QUEUED_1; \
}while(FALSE)

// Vote subsystem counting methods
/// First past the post. One selection per person, and the selection with the most votes wins.
#define VOTE_COUNT_METHOD_SINGLE 1
Expand Down
65 changes: 65 additions & 0 deletions code/__HELPERS/dynamic_human_icon_gen.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
///Global list of all dynamically generated icons, for caching, so we don't have to generate multiple times.
GLOBAL_LIST_EMPTY(dynamic_human_appearances)

/// Creates a human with the given parameters and returns an appearance of it
/proc/get_dynamic_human_appearance(outfit_path, species_path = /datum/species/human, mob_spawn_path, r_hand, l_hand, bloody_slots = NONE, animated = TRUE)
if(!species_path)
return FALSE
if(!ispath(species_path))
stack_trace("Attempted to call get_dynamic_human_appearance() with an instantiated species_path. Pass the species datum typepath instead.")
return FALSE
var/arg_string = "[outfit_path]_[species_path]_[mob_spawn_path]_[l_hand]_[r_hand]_[bloody_slots]"
if(GLOB.dynamic_human_appearances[arg_string]) //if already exists in our cache, just return that
return GLOB.dynamic_human_appearances[arg_string]
var/mob/living/carbon/human/dummy/consistent/dummy = new()
dummy.set_species(species_path)
dummy.stat = CONSCIOUS //He needs to be alive or he has no eyes. Scary
dummy.underwear = "Nude"
dummy.undershirt = "Nude"
dummy.socks = "Nude"
if(outfit_path)
var/datum/outfit/outfit = new outfit_path()
if(r_hand != NO_REPLACE) //we can still override to be null, no replace means just use outfit's
outfit.r_hand = r_hand
if(l_hand != NO_REPLACE)
outfit.l_hand = l_hand
dummy.equipOutfit(outfit, visualsOnly = TRUE)
else if(mob_spawn_path)
var/obj/effect/mob_spawn/human/spawner = new mob_spawn_path(null)
if(r_hand != NO_REPLACE)
spawner.r_hand = r_hand
if(l_hand != NO_REPLACE)
spawner.l_hand = l_hand
spawner.special(dummy, dummy)
spawner.equip(dummy)
for(var/obj/item/carried_item in dummy)
if(dummy.is_holding(carried_item))
var/datum/component/two_handed/twohanded = carried_item.GetComponent(/datum/component/two_handed)
if(twohanded)
twohanded.wield(dummy)
/*
var/datum/component/transforming/transforming = carried_item.GetComponent(/datum/component/transforming)
if(transforming)
transforming.set_active(carried_item)
*/
if(bloody_slots & carried_item.slot_flags)
carried_item.add_mob_blood(dummy)
//dummy.update_held_items()
dummy.regenerate_icons()
var/mutable_appearance/output = dummy.appearance
GLOB.dynamic_human_appearances[arg_string] = output
qdel(dummy)
return output

///This exists to apply the icons async, as that cannot be done in Initialize because of possible sleeps.
/proc/apply_dynamic_human_appearance(atom/target, outfit_path, species_path = /datum/species/human, mob_spawn_path, r_hand, l_hand, bloody_slots = NONE)
INVOKE_ASYNC(GLOBAL_PROC, GLOBAL_PROC_REF(set_dynamic_human_appearance), args)

///This proc gets an argument of a target and runs
/proc/set_dynamic_human_appearance(list/arguments)
var/atom/target = arguments[1] //1st argument is the target
var/dynamic_appearance = get_dynamic_human_appearance(arglist(arguments.Copy(2))) //the rest of the arguments starting from 2 matter to the proc
target.icon = 'icons/mob/human.dmi'
target.icon_state = ""
target.appearance_flags |= KEEP_TOGETHER
target.copy_overlays(dynamic_appearance, TRUE)
1 change: 0 additions & 1 deletion code/__HELPERS/icons.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1084,7 +1084,6 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)


var/icon/out_icon = icon('icons/effects/effects.dmi', "nothing")
COMPILE_OVERLAYS(body)
for(var/D in showDirs)
body.setDir(D)
var/icon/partial = getFlatIcon(body, defdir=D)
Expand Down
4 changes: 0 additions & 4 deletions code/_compile_options.dm
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,6 @@
#define SHIP_PLACEMENT_TEST
#endif

// A reasonable number of maximum overlays an object needs
// If you think you need more, rethink it
#define MAX_ATOM_OVERLAYS 100

#if defined(OPENDREAM)
#if !defined(CIBUILDING)
#warn You are building with OpenDream. Remember to build TGUI manually.
Expand Down
1 change: 0 additions & 1 deletion code/_globalvars/bitfields.dm
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ DEFINE_BITFIELD(flags_1, list(
"NO_LAVA_GEN_1" = NO_LAVA_GEN_1,
"NO_RUINS_1" = NO_RUINS_1,
"ON_BORDER_1" = ON_BORDER_1,
"OVERLAY_QUEUED_1" = OVERLAY_QUEUED_1,
"PREVENT_CLICK_UNDER_1" = PREVENT_CLICK_UNDER_1,
"PREVENT_CONTENTS_EXPLOSION_1" = PREVENT_CONTENTS_EXPLOSION_1,
"RAD_NO_CONTAMINATE_1" = RAD_NO_CONTAMINATE_1,
Expand Down
2 changes: 2 additions & 0 deletions code/_globalvars/misc.dm
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ GLOBAL_LIST_EMPTY(poll_options)
GLOBAL_PROTECT(poll_options)

GLOBAL_VAR_INIT(internal_tick_usage, 0.2 * world.tick_lag) //This var is updated every tick by a DLL if present, used to reduce lag

GLOBAL_VAR_INIT(running_create_and_destroy, FALSE)
Loading

0 comments on commit c57da5b

Please sign in to comment.