Skip to content

Commit

Permalink
Puts all traits in the globalvars file + CI Testing (tgstation#79642)
Browse files Browse the repository at this point in the history
## About The Pull Request

Fixes tgstation#76349

I didn't know that people needed to add any new traits to a global list
so they can be easily read in View Variables, and was pretty shocked to
find out many other people didn't know it was a thing. Let's make it a
thing by testing it using a new CI Python Linter to check this. But oh
no-


![image](https://github.com/tgstation/tgstation/assets/34697715/c093f1a8-00ce-40a6-8e1d-f344107ce7b8)

There were about 200+ missing traits. Alright, so let's do the
following:

* Move trait defines to their own dedicated folder in the `_DEFINES`
folder.
* Split up the traits mega-file into different files, for better
organization. One for the macros, one for the sources, and a few for the
"trait declarations"
* Run the linter a load of times and add everything to the globalvars
file, removing anything that's no longer used and figuring out where the
best categorization of it is. also minor code improvements. also rename
all of the ones that look weird. also fix list indentations
* Also alphabetize the lists because it's easy
* Move everything to a new `traits_by_type` list, while keeping the
admin one the way it is for the time being while we figure out a better
way to show that list to admins.
* Profit
## Why It's Good For The Game

Mapping trait injectors will now work for any type of trait. You
shouldn't add any trait via this injector though, but you're no longer
limited to coders remembering to add it to that critical list you
needed.

Lays the framework for a better view variables experience. This work is
too lengthy to presently do, but hopefully we can get this done sooner
rather than later. we will need a code-accessible way to view these
traits for such a framework to be implemented, so let's just do that.

Future steps are to break down the mega-declarations file into a folder
full of separate files by typepath, but that requires a lot of auditing.
Does need to happen one day though, there's a lot of mob traits mingled
with datum traits and auuugh we gotta do this later this PR is already
massive.

there's probably ways to game this but this catches _my_ mistakes so
good luck to everyone else (it should work for 99% of everyone)
## Changelog

Nothing applicable to players. However, to mappers, the mapping trait
injector should always be able to add any kind of trait (which is rather
good for the times when you need it).
  • Loading branch information
san7890 authored Nov 18, 2023
1 parent 7996ada commit 71b45e5
Show file tree
Hide file tree
Showing 18 changed files with 1,374 additions and 696 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci_suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ jobs:
- name: Check Define Sanity
if: steps.linter-setup.conclusion == 'success' && !cancelled()
run: tools/bootstrap/python -m define_sanity.check
- name: Check Trait Validity
if: steps.linter-setup.conclusion == 'success' && !cancelled()
run: tools/bootstrap/python -m trait_validity.check
- name: Run DreamChecker
if: steps.linter-setup.conclusion == 'success' && !cancelled()
shell: bash
Expand Down
122 changes: 122 additions & 0 deletions code/__DEFINES/traits/_traits.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
#define SIGNAL_ADDTRAIT(trait_ref) "addtrait [trait_ref]"
#define SIGNAL_REMOVETRAIT(trait_ref) "removetrait [trait_ref]"

// trait accessor defines
#define ADD_TRAIT(target, trait, source) \
do { \
var/list/_L; \
if (!target._status_traits) { \
target._status_traits = list(); \
_L = target._status_traits; \
_L[trait] = list(source); \
SEND_SIGNAL(target, SIGNAL_ADDTRAIT(trait), trait); \
} else { \
_L = target._status_traits; \
if (_L[trait]) { \
_L[trait] |= list(source); \
} else { \
_L[trait] = list(source); \
SEND_SIGNAL(target, SIGNAL_ADDTRAIT(trait), trait); \
} \
} \
} while (0)
#define REMOVE_TRAIT(target, trait, sources) \
do { \
var/list/_L = target._status_traits; \
var/list/_S; \
if (sources && !islist(sources)) { \
_S = list(sources); \
} else { \
_S = sources\
}; \
if (_L?[trait]) { \
for (var/_T in _L[trait]) { \
if ((!_S && (_T != ROUNDSTART_TRAIT)) || (_T in _S)) { \
_L[trait] -= _T \
} \
};\
if (!length(_L[trait])) { \
_L -= trait; \
SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(trait), trait); \
}; \
if (!length(_L)) { \
target._status_traits = null \
}; \
} \
} while (0)
#define REMOVE_TRAIT_NOT_FROM(target, trait, sources) \
do { \
var/list/_traits_list = target._status_traits; \
var/list/_sources_list; \
if (sources && !islist(sources)) { \
_sources_list = list(sources); \
} else { \
_sources_list = sources\
}; \
if (_traits_list?[trait]) { \
for (var/_trait_source in _traits_list[trait]) { \
if (!(_trait_source in _sources_list)) { \
_traits_list[trait] -= _trait_source \
} \
};\
if (!length(_traits_list[trait])) { \
_traits_list -= trait; \
SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(trait), trait); \
}; \
if (!length(_traits_list)) { \
target._status_traits = null \
}; \
} \
} while (0)
#define REMOVE_TRAITS_NOT_IN(target, sources) \
do { \
var/list/_L = target._status_traits; \
var/list/_S = sources; \
if (_L) { \
for (var/_T in _L) { \
_L[_T] &= _S;\
if (!length(_L[_T])) { \
_L -= _T; \
SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(_T), _T); \
}; \
};\
if (!length(_L)) { \
target._status_traits = null\
};\
}\
} while (0)

#define REMOVE_TRAITS_IN(target, sources) \
do { \
var/list/_L = target._status_traits; \
var/list/_S = sources; \
if (sources && !islist(sources)) { \
_S = list(sources); \
} else { \
_S = sources\
}; \
if (_L) { \
for (var/_T in _L) { \
_L[_T] -= _S;\
if (!length(_L[_T])) { \
_L -= _T; \
SEND_SIGNAL(target, SIGNAL_REMOVETRAIT(_T)); \
}; \
};\
if (!length(_L)) { \
target._status_traits = null\
};\
}\
} while (0)

#define HAS_TRAIT(target, trait) (target._status_traits?[trait] ? TRUE : FALSE)
#define HAS_TRAIT_FROM(target, trait, source) (HAS_TRAIT(target, trait) && (source in target._status_traits[trait]))
#define HAS_TRAIT_FROM_ONLY(target, trait, source) (HAS_TRAIT(target, trait) && (source in target._status_traits[trait]) && (length(target._status_traits[trait]) == 1))
#define HAS_TRAIT_NOT_FROM(target, trait, source) (HAS_TRAIT(target, trait) && (length(target._status_traits[trait] - source) > 0))
/// Returns a list of trait sources for this trait. Only useful for wacko cases and internal futzing
/// You should not be using this
#define GET_TRAIT_SOURCES(target, trait) (target._status_traits?[trait] || list())
/// Returns the amount of sources for a trait. useful if you don't want to have a "thing counter" stuck around all the time
#define COUNT_TRAIT_SOURCES(target, trait) length(GET_TRAIT_SOURCES(target, trait))
/// A simple helper for checking traits in a mob's mind
#define HAS_MIND_TRAIT(target, trait) (HAS_TRAIT(target, trait) || (target.mind ? HAS_TRAIT(target.mind, trait) : FALSE))
Loading

0 comments on commit 71b45e5

Please sign in to comment.