EverestAPI / EverestAPI/Everest

add attribute to make entities always load in the first room entered, regardless of what room they are placed in

Abierto
#653 0 comentarios 0 reacciones 0 asignados Ver en GitHub
enhancement
Lenguaje dominante
C#
Estrellas
516
Forks
106
Merge medio
4 d 9 h
PR fusionados (30 d)
5

Descripción

### i am planning on implementing this myself

Yes

### describe your request

i don't really have a good name for this attribute _(currently i'm calling it `GlobalEntityAttribute`, but global is already a different thing, so if anyone else has a better name i'm happy to change it)_, but it's useful for a variety of things, principally map-wide controllers.
currently i have this in an as-of-yet unreleased helper, so i have a working implementation, but it seems common enough to deserve a place in Everest.

it works by filtering out any entitydata entries corresponding to classes with the annotation during `MapData.Load`, storing them in a `List>` associated with the mapdata, and then adding them to the level in `LevelLoader.LoadingThread`.
the way the entity names corresponding to attribute-having classes are obtained currently requires the entities to be tracked, but this could be easily changed.

### additional context

full example code

```cs
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public class GlobalEntityAttribute : Attribute {
private static string[] _entityNames;
private static string[] entityNames {
get {
if (_entityNames != null)
return _entityNames;

_entityNames = Tracker.TrackedEntityTypes
.SelectMany((pair) => {
if (pair.Key.IsDefined(typeof(GlobalEntityAttribute))) {
var attrs = pair.Key
.GetCustomAttributes()
.SelectMany((a) =>
a.IDs.SelectMany((id) => {
var parts = id.Split('=');
if (parts.Length > 0)
return new string[] { parts[0] };
return new string[] { };
})
);
return attrs.ToList();
}
return new List();
}).ToArray();

return _entityNames;
}
}

internal static void Load() {
Everest.Events.LevelLoader.OnLoadingThread += onLoad;
On.Celeste.MapData.Load += onMapDataLoad;
}

internal static void Unload() {
Everest.Events.LevelLoader.OnLoadingThread -= onLoad;
On.Celeste.MapData.Load -= onMapDataLoad;
}

private static void onMapDataLoad(On.Celeste.MapData.orig_Load orig, MapData mapData) {
orig(mapData);

var dyndata = DynamicData.For(mapData);

List> entities;
if (dyndata.TryGet("global_entities", out entities)) {
entities.Clear();
} else {
entities = new();
dyndata.Set("global_entities", entities);
}

var names = entityNames;
foreach (var levelData in mapData.Levels) {
levelData.Entities.RemoveAll((e) => {
if (!names.Contains(e.Name))
return false;

entities.Add(new(e, levelData.Position));
return true;
});
}
}

private static void onLoad(Level level) {
var mapData = level.Session.MapData;
var dyndata = DynamicData.For(mapData);

if (dyndata.TryGet>>("global_entities", out var shadowData)) {
foreach (var shadow in shadowData) {
var loader = Level.EntityLoaders[shadow.Item1.Name];
var entity = loader.Invoke(level, shadow.Item1.Level, shadow.Item2, shadow.Item1);
if (entity is not null)
level.Add(entity);
}
}
}
}
```

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.