Maintain map of additional data for enemies

Ask about ACS, DECORATE, ZScript, or any other scripting questions here!

Moderator: GZDoom Developers

Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.

Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)
Post Reply
nootsynootsy
Posts: 4
Joined: Fri May 26, 2023 2:03 pm
Preferred Pronouns: He/Him
Operating System Version (Optional): Windows 11
Graphics Processor: ATI/AMD (Modern GZDoom)

Maintain map of additional data for enemies

Post by nootsynootsy »

Hi everyone! I'm coding my first weapon with ZScript and am trying to implement a 'chain-lightning' effect that decreases in damage with range. My idea was to use a sort of Djisktra (or maybe other graph solving algorithm) by making up the edges as distances as I go by finding nearby targets with BlockThingsIterator. I need to maintain a list of previous targets for this to work of course and that quickly becomes a problem when dealing with possibly a hundred enemies or so in a single tic. I can't believe I was unable to find how to either maintain or access an already existing map of every enemy in the level or tacking on additional data to actors to avoid having to extend every enemy in the game, so that's my question: How can I quickly access additional data for enemies.

Additionally after creating the list of enemies and how much damage to do to them I want to loop through and hurt them, and I feel like I'm going insane being unable to find a function that just applies damage to an actor.
7Soul
Posts: 41
Joined: Sat Mar 13, 2021 6:47 pm

Re: Maintain map of additional data for enemies

Post by 7Soul »

I don't know why you'd need to deal with hundreds of actors per chain, just the ones in the BlockThingsIterator need to be checked (unless the range is huge). You can put the already-hit actors in an array, and just check if the new target isn't already in the array. This tutorial explains how to use arrays in ZScript: https://github.com/jekyllgrim/ZScript_B ... _Arrays.md

And for damage you cycle through the array and call DamageMobj for each actor https://zdoom.org/wiki/DamageMobj
nootsynootsy
Posts: 4
Joined: Fri May 26, 2023 2:03 pm
Preferred Pronouns: He/Him
Operating System Version (Optional): Windows 11
Graphics Processor: ATI/AMD (Modern GZDoom)

Re: Maintain map of additional data for enemies

Post by nootsynootsy »

Using DamageMobj with code

Code: Select all

Actor a = previousTargets[targetIndex];
a.DamageMobj(self, self, damage, 0, 0);
gives me the error 'cannot convert to name' which I can't find anything about that error.

My range is indeed quite large, ~500 units, depending on how the balance shakes out. But the idea is it can only really arc between actors at 150 units apart or so. So it can be used in crowded combat scenarios or to shoot around walls. (I don't know how to post a picture here without making an imgur account apparently) That's why I don't want to just BlockThingsIterator around the first target hit, I know I'm probably making it more complicated than it needs to be, but I would like my described behavior.
7Soul
Posts: 41
Joined: Sat Mar 13, 2021 6:47 pm

Re: Maintain map of additional data for enemies

Post by 7Soul »

Name is a type of variable. The 4th parameter for DamageMobj is the damage type, so add 'None' between damage and 0

You're worrying too much about performance before even knowing if you'll have performance issues. Not to mention, you'll want to stop the iterator as soon as you find a valid target
nootsynootsy
Posts: 4
Joined: Fri May 26, 2023 2:03 pm
Preferred Pronouns: He/Him
Operating System Version (Optional): Windows 11
Graphics Processor: ATI/AMD (Modern GZDoom)

Re: Maintain map of additional data for enemies

Post by nootsynootsy »

Once more you're right, thank you. The rest of my code had the problem slowing it down.

Code: Select all

Actor other = i.thing;
bool targetedAlready = false;
for (int j = 0; j < previousTargets.Size(); j = j + 1) {
	//IsPointerEqual(previousTargets[j], other) //didn't work :(
	if (other == previousTargets[j]) {
		targetedAlready = true;
	}
}
if (targetedAlready) { //if other has already been dequeued
	console.printf(String.Format("found previous target"));
	continue;
}
(This code is in two more loops)
I can't seem to properly check if 'other' is in previousTargets
7Soul
Posts: 41
Joined: Sat Mar 13, 2021 6:47 pm

Re: Maintain map of additional data for enemies

Post by 7Soul »

Try printing other.getClassName() and previousTargets[j].getClassName() to at least know if those variables actually hold a reference to an actor, they might just be empty. You'd better post the entire function here too
nootsynootsy
Posts: 4
Joined: Fri May 26, 2023 2:03 pm
Preferred Pronouns: He/Him
Operating System Version (Optional): Windows 11
Graphics Processor: ATI/AMD (Modern GZDoom)

Re: Maintain map of additional data for enemies

Post by nootsynootsy »

The code sucks right now as I try to figure it out, hopefully the comments are useful. The bug is probably just a bad algorithm and not not understanding Zscript functions.

Code: Select all

action int breadthFirstBolt(actor currentTarget, int maxrange, double traveledDistance) {
	
		PlayerPawn p = PlayerPawn(players[consoleplayer].mo); //This is not the correct way to go about this
	
		Array<actor> previousTargets;
		Array<double> previousTargetDistances;
		Array<actor> targetQueue;
		Array<double> targetQueueDistances;
		targetQueue.Push(currentTarget);
		targetQueueDistances.Push(traveledDistance);
		
		while (targetQueue.Size() > 0) {
			currentTarget = targetQueue[0]; //dequeue next actor and solidify their damage
			previousTargets.Push(currentTarget);
			traveledDistance = targetQueueDistances[0];
			previousTargetDistances.Push(targetQueueDistances[0]);
			targetQueue.Delete(0);
			targetQueueDistances.Delete(0);
		
			//ty zdoom forums and Cherno
			for (let i = BlockThingsIterator.Create(currentTarget, maxrange/4); i.Next();) {
				Actor other = i.thing;
				bool targetedAlready = false;
				
				for (int j = 0; j < previousTargets.Size(); j = j + 1) { //check if new actor is already dequeued
				
					//IsPointerEqual(previousTargets[j], other)
					console.printf("is %s a %s?", other.getClassName(), previousTargets[j].getClassName());
						if (other == previousTargets[j]) {
							targetedAlready = true;
						}
					}
				if (targetedAlready) {
					console.printf(String.Format("found previous target"));
					continue;
				}
				
				double distance = currentTarget.Distance3D(other);
				double pathDistance = traveledDistance + distance;
				if (pathDistance > maxrange || distance > maxrange/4) { //recheck distance
					console.printf(String.Format("target is %d units far away", distance));
					continue;
				}
				//TODO: if two targets don't have line of sight don't chain
				
				int otherIndex = targetQueue.Find(other);
				bool queuedAlready = false;
				for (int j = 0; j < targetQueue.Size(); j = j + 1) { //check if new actor is already queued
				//IsPointerEqual(previousTargets[j], other)
				console.printf("is %s a %s?", other.getClassName(), previousTargets[j].getClassName());
					if (other == targetQueue[j]) {
						queuedAlready = true;
					}
				}
				if (queuedAlready && pathDistance < targetQueueDistances[otherIndex]) {	//remove actor if already queued and this path is shorter
					targetQueue.Delete(otherIndex);
					targetQueueDistances.Delete(otherIndex); //remove from list
				}
				
				int i = 0;
				while (i < targetQueueDistances.Size() && targetQueueDistances[i] < pathDistance) { //find index to queue actor
					i = i + 1;
				}
				targetQueue.Insert(i, other); //queue the target
				targetQueueDistances.Insert(i, pathDistance);
			}
		}
		
		int targetIndex = 0; //now that finished go through targets and damage
		for (targetIndex = 0; targetIndex < previousTargets.Size(); targetIndex = targetIndex + 1) {
			int damage = (maxrange - previousTargetDistances[targetIndex])/10;
			console.printf(String.Format("dealt %f damage to %s, %f units far away", damage, previousTargets[targetIndex].GetClassName(), previousTargetDistances[targetIndex]));
			previousTargets[targetIndex].DamageMobj(self, self, damage, 'None', 0);
		}
		return 0;
	}
}
Post Reply

Return to “Scripting”