Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add functions to get actors adjacent and with specified distance #344

Open
wants to merge 2 commits into
base: development
Choose a base branch
from

Conversation

LordMidas
Copy link
Member

@LordMidas LordMidas commented Apr 11, 2024

Adds a more efficient way to get adjacent actors instead of having to iterate over all actors on the battlefield. Primarily targets issue #343.

Usage for adjacent actors:

// To get all adjacent actors: 
::Tactical.Entities.getAdjacentActors(_tile);

// To get ALLIED adjacent actors:
::Tactical.Entities.getAdjacentActors(_tile).filter(@(_, a) a.isAlliedWith(myActor));

// To get Hostile adjacent actors
::Tactical.Entities.getAdjacentActors(_tile).filter(@(_, a) !a.isAlliedWith(myActor));

// To get Same Faction adjacent actors:
::Tactical.Entities.getAdjacentActors(_tile).filter(@(_, a) a.getFaction() == myActor.getFaction());

// To get ALLIED non-faction adjacent actors:
::Tactical.Entities.getAdjacentActors(_tile).filter(@(_, a) a.isAlliedWith(myActor) && a.getFaction() != myActor.getFaction());

Copy link
Member

@Enduriel Enduriel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't actually think we should be adding a generic handler for distance here, we can optimize the distance=1 case, so let's, but otherwise we should allow modders to do it themselves, no reason to add extra code to handle this situation in my opinion.

@LordMidas
Copy link
Member Author

LordMidas commented Apr 11, 2024

I don't actually think we should be adding a generic handler for distance here, we can optimize the distance=1 case, so let's, but otherwise we should allow modders to do it themselves, no reason to add extra code to handle this situation in my opinion.

I don't think there's a clean way to handle the distance == 1 case without adding this extra code because the already present functions call getActorsByFunction which is distance agnostic and therefore always iterates on all actors. Secondly, we need a function to get all actors at a certain distance irrespective of faction allegiance - that doesn't currently exist.

@TaroEld
Copy link
Member

TaroEld commented Apr 11, 2024

I don't actually think we should be adding a generic handler for distance here, we can optimize the distance=1 case, so let's, but otherwise we should allow modders to do it themselves, no reason to add extra code to handle this situation in my opinion.

I'm of the opposite opinion, might as well make it useful.

q.getActorsWithinRange <- function( _tile, _max = 1, _min = 1)
{
	// add validation
	if (_max == 1)
	{
		local ret = [];

		if (_min == 0 &&  _tile.IsOccupiedByActor)
			ret.push(_tile.getEntity());

		for (local i = 0; i < 6; i++)
		{
			if (!_tile.hasNextTile(i))
				continue;

			local nextTile = _tile.getNextTile(i);
			if (nextTile.IsOccupiedByActor)
				ret.push(nextTile.getEntity());
		}

		return ret;
	}
	else
	{
		return this.getActorsByFunction(function(_actor) {
			if (!_actor.isPlacedOnMap())
				return false;
			local distance = _tile.getDistanceTo(_actor.getTile());
			return (distance <= _max && distance >= _min)
		})
	}
}
q.getAlliedActorsWithinRange <- function(_actor, _tile = null, _max = 1, _min = 1)
{
	return this.getActorsWithinRange(_tile == null ? _actor.getTile() : _tile, _max, _min).filter(@(_, a) a.isAlliedWith(_actor));
}
q.getHostileActorsWithinRange <- function(_actor, _tile = null, _max = 1, _min = 1)
{
	return this.getActorsWithinRange(_tile == null ? _actor.getTile() : _tile, _max, _min).filter(@(_, a) !a.isAlliedWith(_actor));
}
q.getSameFactionActorsWithinRange <- function(_actor, _tile = null, _max = 1, _min = 1)
{
	return this.getActorsWithinRange(_tile == null ? _actor.getTile() : _tile, _max, _min).filter(@(_, a) a.getFaction() == _actor.getFaction());
}
q.getOtherFactionActorsWithinRange <- function(_actor, _tile = null, _max = 1, _min = 1)
{
	return this.getActorsWithinRange(_tile == null ? _actor.getTile() : _tile, _max, _min).filter(@(_, a) a.getFaction() != _actor.getFaction());
}

// edited to grab the tile if none is specified for max convenience

@Enduriel
Copy link
Member

Ok, fair, in that case I'd change reorganize the main function so that getNeighboringActors is separate. I don't feel great about having _actor, _tile = null, but to be fair I'm not really sure how to organize that differently

q.getActorsWithinRange <- function( _tile, _max = 1, _min = 1)
{
	// add validation
	if (_max == 1)
	{
		local ret = this.getNeighboringActors(_tile);
		if (_min == 0 && _tile.IsOccupiedByActor)
			ret.push(_tile);
		return ret;
	}
	else
	{
		return this.getActorsByFunction(function(_actor) {
			if (!_actor.isPlacedOnMap())
				return false;
			local distance = _tile.getDistanceTo(_actor.getTile());
			return (distance <= _max && distance >= _min)
		})
	}
}

q.getNeighboringActors <- function ( _tile ) {
	local ret = [];
	for (local i = 0; i < 6; i++)
	{
		if (!_tile.hasNextTile(i))
			continue;

		local nextTile = _tile.getNextTile(i);
		if (nextTile.IsOccupiedByActor)
			ret.push(nextTile.getEntity());
	}
	return ret;
}
q.getAlliedActorsWithinRange <- function( _tile , _max = 1, _min = 1)
{
	return this.getActorsWithinRange(_tile == null ? _actor.getTile() : _tile, _max, _min).filter(@(_, a) a.isAlliedWith(_actor));
}
q.getHostileActorsWithinRange <- function(_actor, _tile = null, _max = 1, _min = 1)
{
	return this.getActorsWithinRange(_tile == null ? _actor.getTile() : _tile, _max, _min).filter(@(_, a) !a.isAlliedWith(_actor));
}
q.getSameFactionActorsWithinRange <- function(_actor, _tile = null, _max = 1, _min = 1)
{
	return this.getActorsWithinRange(_tile == null ? _actor.getTile() : _tile, _max, _min).filter(@(_, a) a.getFaction() == _actor.getFaction());
}
q.getOtherFactionActorsWithinRange <- function(_actor, _tile = null, _max = 1, _min = 1)
{
	return this.getActorsWithinRange(_tile == null ? _actor.getTile() : _tile, _max, _min).filter(@(_, a) a.getFaction() != _actor.getFaction());
}

@TaroEld
Copy link
Member

TaroEld commented Apr 11, 2024

getAlliedActorsWithinRange lacks the _actor parameter and null default value for the tile, otherwise LGTM. Also yes, I understand, but I think this still provides the path of least resistance for most applications.

@Darxo
Copy link

Darxo commented Apr 11, 2024

I prefer a version where it is apparent from the function name, that it gathers the actors at a distance of 1 tile, like q.getNeighboringActors <- function ( _tile ) from enduriels post.

Otherwise it will look in code like this: [...].getActorsWithinRange(actor.getTile())
And it's not clear what range that exactly is for someone unfamiliar with that msu function.
Adjacent actors is one likely interpretation but the range of the currently equipped weapons default attack or the viewing distance of that actor are not too far off.

@TaroEld
Copy link
Member

TaroEld commented Apr 11, 2024

With Endys version, we'll have the best of both worlds, the clearly specific neighbours and the more generic range option.

@LordMidas LordMidas force-pushed the feat-port-to-modern-hooks branch from de55d30 to 983c30b Compare April 13, 2024 04:38
Base automatically changed from feat-port-to-modern-hooks to development April 13, 2024 04:39
@LordMidas LordMidas force-pushed the feat-functions-to-get-actors-adjacent-and-within-distance branch from 43f75a9 to 566aa3e Compare April 21, 2024 03:33
@LordMidas
Copy link
Member Author

LordMidas commented Apr 21, 2024

I have updated the PR with the improved functionality of within range. I don't think we need to add individual functions for each faction type - that can be easily done by the modder by using .filter on the output of these two functions getActorsWithinRange and getAdjacentActors.

Question 1 : Should we allow passing a function as a parameter into getActorsWithinRange so that the filtering happens much more efficiently i.e. the first time the actors are iterated upon rather than filtering them out later? So:

getActorsWithinRange(myTile, 5, 1, @(a) a.isAlliedWith(myActor));

// instead of the current:
getActorsWithinRange(myTile, 5, 1).filter(@(a) a.isAlliedWith(myActor));

Question 2: Should the default value of _min be 0? I think that makes more sense in terms of how vanilla works with tiles.

@TaroEld
Copy link
Member

TaroEld commented Apr 26, 2024

I'd not add the function parameter.

@LordMidas LordMidas linked an issue Jul 5, 2024 that may be closed by this pull request
@LordMidas LordMidas force-pushed the feat-functions-to-get-actors-adjacent-and-within-distance branch from e22360a to 15e50d9 Compare July 12, 2024 22:27
@Suor
Copy link
Contributor

Suor commented Sep 2, 2024

There is Tactical.queryActorsInRange() already, why not use that?

@TaroEld
Copy link
Member

TaroEld commented Sep 3, 2024

There is Tactical.queryActorsInRange() already, why not use that?

Yes, we too realized that, I think the final conclusion was 'ok we dont need this'.

@LordMidas
Copy link
Member Author

Yeah these were created before we realized that the vanilla function exists. However, we still need to optimize our existing functions especially for the case of adjacent actors. Also a new function to get adjacent actors like getAdjacentActors is a compelling addition as that is a very commonly accessed situation.

Also, the vanilla function is slower than our implementation of getActorsByFunction (especially when there's fewer actors on the battlefield). But that's a minor point. Nevertheless we need to optimize our existing functions for the adjacency case.

@Suor
Copy link
Contributor

Suor commented Sep 4, 2024

Vanilla function lacks usability too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[REQUEST] Add new getAdjacentActors function for actor.nut
5 participants