Author Topic: On container searches, loops, vector distance, and named bricks  (Read 3263 times)

Not sure if you can actually delay a container search, but if you can't my best guess would be to search and find all objects and push them into an array and just re-loop through that array with the delay?

Not sure if you can actually delay a container search, but if you can't my best guess would be to search and find all objects and push them into an array and just re-loop through that array with the delay?
Well if there is no way to delay a container search then I assume it is not one of those loops that run super fast and can freeze Blockland like Wallet warned in his Torque scripting lessons?

My problem with this is that the distance value is not updating in the bottom print because the while loop only runs once. If I understand it correctly shouldn't the while loop be running as long as there is any brick inside the search radius?

Well if there is no way to delay a container search then I assume it is not one of those loops that run super fast and can freeze Blockland like Wallet warned in his Torque scripting lessons?

My problem with this is that the distance value is not updating in the bottom print because the while loop only runs once. If I understand it correctly shouldn't the while loop be running as long as there is any brick inside the search radius?
a search will loop through all bricks in range once and then return nothing

a search will loop through all bricks in range once and then return nothing
Oh. Well that would be it then.

I made a loop that crashed Blockland. How do I fix this? I assume I need to slow it down?

Code: [Select]
function brickDetector_OFF_Image::onFire(%this, %player, %slot)
{
%player.schedule(0,"playThread",2,shiftRight);
%player.schedule(200,"playThread",1,armReadyRight);
%player.unMountImage(0);
%player.mountimage(brickDetector_ON_Image, 0);

%name="_asd";
%radius=32;
%loop=1;

initContainerRadiusSearch(%pos, %radius, $TypeMasks::FxBrickAlwaysObjectType);

while (%loop)
{
while (isObject(%obj = containerSearchNext()))
{
if (%obj.getName() $= %name)
{
%pos=%player.getPosition();
%detectorMsg="\c6Detecting: \c5" @ %name @ "\c6 | Distance: \c5" @ mFloatLength(vectorDist(%pos, %obj.getPosition()),0) @ " Bricks";
commandToClient(%player.client,'bottomPrint',%detectorMsg,0,0);
break;
}
}
}
}

function brickDetector_ON_Image::onFire(%this, %player, %slot)
{
%obj.schedule(0,"playThread",2,shiftRight);
%obj.schedule(200,"playThread",1,armReadyRight);
%obj.unMountImage(0);
%obj.mountimage(brickDetector_OFF_Image, 0);
commandToClient(%player.client,'ClearBottomPrint');
%loop=0;
}

%loop is always true. The loop while (%loop) runs forever.

To do what (I think) you are trying to do, you need to add a delay in between loops. As all code is essentially executed instantly, you cannot do this with a simple while loop.
You need to look up about schedules. They're Torques method of telling itself that in Xms, run a function. Do a search in coding help, theres bound to be loads of topics about it.

So this project was not intended to be a project but it is now. I've already spent several days longer on this than I initially planned. Oh well I'm learning bits and pieces of TorqueScript and I already have plans for expanding this for the future.

By Port's suggestions I've split the actual script into it's own function which I'm calling from the onFire method(or function?). The code is much cleaner now. Here's some code that actually works.
Code: [Select]
function Player::updateBrickDetector(%this)
{
cancel(%this.updateBrickDetector);

%name="_asd";
%radius=32;

initContainerRadiusSearch(%pos, %radius, $TypeMasks::FxBrickAlwaysObjectType);

while (isObject(%obj = containerSearchNext()))
{
if (%obj.getName() $= %name)
{
%pos=%this.getPosition();
%distance=mFloatLength(vectorDist(%pos, %obj.getPosition()),0);
%detectorMsg="\c6Detecting: \c3" @ getSubStr(%name, 1, strLen(%name)) @ "\c6 | Distance: \c3" @ %distance @ " Bricks";

commandToClient(%this.client,'bottomPrint',%detectorMsg,0,0);
break;
}
}

if(%this.getMountedImage(0) == nameToID(brickDetector_ON_Image) && isObject(%this) && (%this.getState() !$= "Dead"))
{
%this.updateBrickDetector = %this.schedule(1000, updateBrickDetector);
}
else
{
return;
}
}

Problems:
The brick is tracked way beyond the container search radius. Some black magic right there.

It is not tracking the closest brick to the player. I think %obj.getPosition() is not updating correctly. For some bizarre reason it only detects the oldest placed named brick. If that is destroyed it starts tracking the second oldest brick with the correct name regardless of player position.

Problems:
The brick is tracked way beyond the container search radius. Some black magic right there.

It is not tracking the closest brick to the player. I think %obj.getPosition() is not updating correctly. For some bizarre reason it only detects the oldest placed named brick. If that is destroyed it starts tracking the second oldest brick with the correct name regardless of player position.

The cause of both: You're still not defining %pos when you initiate the search. Change it to initContainerRadiusSearch(%pos = %this.getPosition(), %radius, $TypeMasks::FxBrickAlwaysObjectType); and remove the line defining the same variable later on. If you want the search to be from the center of the player rather than the feet, use .getHackPosition() instead.

The cause of both: You're still not defining %pos when you initiate the search. Change it to initContainerRadiusSearch(%pos = %this.getPosition(), %radius, $TypeMasks::FxBrickAlwaysObjectType); and remove the line defining the same variable later on. If you want the search to be from the center of the player rather than the feet, use .getHackPosition() instead.
Just move this line
%pos=%this.getPosition();
Above
initContainerRadiusSearch

A 32 unit radius is very large. A brick is 0.5 units wide, so you are searching a sphere with diameter of 128 bricks. Thats pretty big, not sure if its intentional or not.

A 32 unit radius is very large. A brick is 0.5 units wide, so you are searching a sphere with diameter of 128 bricks. Thats pretty big, not sure if its intentional or not.
Correct. It is detecting bricks within a 64 stud radius of the player.

So far so good. I put in a server command to change the brick to track, as well a change an "out of range" thing. Everything seems to be working great. I also changed the tracking radius to be a part of the item image so it's easier to change for those who aren't familiar with scripting. Plus I shortened the name of the tool.
Code: [Select]
function serverCmdTrack(%client, %arg)
{
%client.brickTrackerBrickName = %arg;
%msg = "\c6Now tracking \c3" @ %arg;

commandToClient(%client,'CenterPrint',%msg,2,0);
}

function Player::updateBrickTracker(%this)
{
cancel(%this.updateBrickTracker);

%name="_" @ %this.client.brickTrackerBrickName;
%radius=((nameToID(brickTracker_ON_Image).trackerRangeStuds)/2);
%trackerMsg="\c6Tracking: \c3" @ getSubStr(%name, 1, strLen(%name)) @ "\c6 | Distance: \c3Out of Range";

commandToClient(%this.client,'bottomPrint',%trackerMsg,0,0);

initContainerRadiusSearch(%pos = %this.getPosition(), %radius, $TypeMasks::FxBrickAlwaysObjectType);

while (isObject(%obj = containerSearchNext()))
{
if (%obj.getName() $= %name)
{
%distance=mFloatLength((vectorDist(%pos, %obj.getPosition())*2),0);
%trackerMsg="\c6Tracking: \c3" @ getSubStr(%name, 1, strLen(%name)) @ "\c6 | Distance: \c3" @ %distance @ " Bricks";

commandToClient(%this.client,'bottomPrint',%trackerMsg,0,0);
break;
}
}

if(%this.getMountedImage(0) == nameToID(brickTracker_ON_Image) && isObject(%this) && (%this.getState() !$= "Dead"))
{
%this.updateBrickTracker = %this.schedule(1000, updateBrickTracker);
}
else
{
return;
}
}
Next I'll test and tie some loose ends like stopping tracking when the player dies, the item is thrown away and such.
« Last Edit: December 30, 2013, 01:14:55 AM by Demian »

If you'd rather have unlimited distance, here's an excerpt of something I wrote a while back:
Code: [Select]
%list = "";
%fieldCount = 0;
%NTname = "_" @ %BrickName;
for(%i = 0; %i < mainBrickGroup.getCount(); %i++)
{
%group = mainBrickGroup.getObject(%i);
for(%j = 0; %j < %group.NTNameCount; %j++)
{
%name = %group.NTName[%j];
if(%name $= %NTname)
{
for(%k = 0; %k < %group.NTObjectCount[%name]; %k++)
{
%list = setField(%list, %fieldCount, %group.NTObject[%name, %k]);
%fieldCount++;
}
}
}
}
That'll compile a list of all the bricks of the name you want, and doesn't even look at bricks that don't have it. Since you only wanted the closest one, instead of keeping a list, just keep the closest brick so far and its distance for comparison. I think this would be more efficient than an enormous radius search followed by looping over all the contents, at least for bigger/more detailed builds.