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.
Maintain map of additional data for enemies
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!)
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!)
-
- 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)
-
- Posts: 36
- Joined: Sat Mar 13, 2021 6:47 pm
Re: Maintain map of additional data for enemies
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
And for damage you cycle through the array and call DamageMobj for each actor https://zdoom.org/wiki/DamageMobj
-
- 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
Using DamageMobj with code 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.
Code: Select all
Actor a = previousTargets[targetIndex];
a.DamageMobj(self, self, damage, 0, 0);
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.
-
- Posts: 36
- Joined: Sat Mar 13, 2021 6:47 pm
Re: Maintain map of additional data for enemies
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
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
-
- 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
Once more you're right, thank you. The rest of my code had the problem slowing it down.
(This code is in two more loops)
I can't seem to properly check if 'other' is in previousTargets
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;
}
I can't seem to properly check if 'other' is in previousTargets
-
- Posts: 36
- Joined: Sat Mar 13, 2021 6:47 pm
Re: Maintain map of additional data for enemies
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
-
- 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
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;
}
}