MSUTeam / MSU

Modding Standards and Utilities for Battle Brothers
22 stars 4 forks source link

[REQUEST] new function onSpawnEntity(_entity) function for entities/items/skills #301

Open Darxo opened 1 year ago

Darxo commented 1 year ago

Is your feature request related to a problem? Please describe. Currently no skill, item or entity can actively react to an entity spawning mid fight. This ability would be useful for

Describe the solution you'd like There is the function spawnEntity in the BB root table that is used to spawn any entity via its scriptPath mid-fight. That function should be hooked so that it calls the following new onSpawnEntity function on the spawned entity before returning it

Add this new function to the entity.nut:

function onSpawnEntity()
{
    if (!this.World.getTime().IsDaytime && player.getBaseProperties().IsAffectedByNight)
    {
        this.getSkills().add(::new("scripts/skills/special/night_effect"));
    }

    foreach (i, faction in this.getAllInstances())
    {
        foreach (actor in faction)
        {
            actor.getSkills().onSpawnEntity(this);
            actor.getItems().onSpawnEntity(this);
            actor.getSkills().update();
        }
    }
}

Similar functions would be added to skill_container.nut, skill.nut aswell as item_container.nut and item.nut. But here they wouldn't contain any inherent effects.

So as a nice side-effect the night effect is now automatically added to every entitiy that is spawned mid-fight. No longer do we need to do this manually for unleashable animals for example.

Enduriel commented 1 year ago

This is actually a pretty cool idea I think.

LordMidas commented 12 months ago

I've been working on this for quite some time and unfortunately it doesn't have a simple/clean solution. Ideally I'd like the onSpawnEntity event to trigger after the entity has been properly set up i.e. its faction has been assigned and its items have been equipped.

My current solution consists of all of the following:

An alternative is to call onSpawn from onPlacedOnMap in actor (will still require that Boolean above) - however, the issue here is also that this function is called when the entity's faction is 0 and its item container is empty.

Another alternative is to call onSpawn from addInstance in tactical_entity_manager. However, that event is called via setFaction in actor, so the faction is properly set, but it is called before their items have been set.

Summary: Using hooks on setupEntity, onResurrect, addEntity, insertEntity, I am able to trigger the event reliably for entities at the start of a combat with all the info (faction, items etc.) been properly set up for the entity spawned. However, currently I do not have a solution for entities spawned mid-combat via the ::Tactical.spawnEntity function as during this time their faction and items are 0.

LordMidas commented 12 months ago

After a lot more working here is a "dirty" solution that I have that currently seems to work. We basically call onSpawn from the character's onSkillsUpdated function but we build in several checks via Booleans here and there to ensure that it is really only called on the first "valid" skill container update after being spawned i.e. the update that happens after all faction and item setup is complete.

So:

  1. Entities set this.m.HasOnSpawnBeenCalled to false after init. This means that the next call to onSpawn will be allowed. This ensures that skills added during onInit do not trigger the onSpawn event.
  2. When their instance is added to the tactical entity manager, it sets this.m.IsInstanceAdded to true.
  3. When their equipment is being assigned in assignRandomEquipment, then any skills added there do not call onSpawn because of our hook on onSkillsUpdated which checks for IsAssigningRandomEquipment to be false.
  4. Once all their equipment has been assigned, the manual call to this.onSpawn() triggers the event. If they were not assigning any equipment, then the event is triggered due to the hook on onSkillsUpdated which is called when the character's skill container updates after spawning. The this.m.IsInstanceAdded check in this case ensures that entities that spawn during the combat do not trigger the event before their faction has been properly set.

Other thoughts: While this may work, and if it really works and is robust enough then perhaps it is worth it to do it like this. However, if it is fragile and if there are still edge cases where it may break, then perhaps it is better to just use a normal hook on addInstance and trigger onSpawn with faction info but with the limitation that the item container will be empty (and any skills added in assignRandomEquipment or similar functions will not be present yet). We can then document this so people know.