is there any way to get an entity's location or the distance to an other entity or the player?
If not, does that mean that the only way to let an entity react with other entities nearby is to place a scriptarea for every one?
Also when using the PlayerLookAtCallback a maxDistance would be nice... For example when looking at a picture a message like "What a beautiful picture" should appear, but not when the player is like 20 m away and can hardly see the picture at all. Any solution?
Thanks
(This post was last modified: 09-04-2012, 12:29 PM by craven7.)
If distance is all you are interested in there are some pretty simple hacks that can be used. How precise do you want the distance calculation to be, and how frequently will it be called - if you want too much precision or too much frequency then you will struggle.
Also, do you want object origin->object origin distance, or object origin->nearest point on another object distance?
(This post was last modified: 09-04-2012, 12:12 PM by Apjjm.)
It doesn't have to be that precise... for example player is walking down a corridor and everytime he comes near a door, the door closes. Let's say, check for a nearby door every 0.5 seconds... shouldn't be too costly?
Okay, I have written up a distance checking example map: Download
The sample map will report the distance between the barrel and the box when you use the lantern, and also has a distance callback if the barrel and box are ever within 2 units-of-distance of each other.
How to use it
Spoiler below!
Firstly, I have included python
(3.2) script which generates entities based on a file called "unitSphere.txt". This is in ->entities->SphereGen.
The map script file also contains some constants (which refer directly to the parameters at the start of the python file) which you would need to alter.
The script file contains a couple of helper functions:
The callback works is the same as the one from AddEntityCollideCallback. Note that if you want the routine to work with the player, specify "Player" in the asEntity field (* is also supported here, but not in asBase). asBase MUST be an entity which supportes AddAttachedPropToProp. Also note that the asParent feild in the collide callback will be the entity matched by asEntity, and asChild will be the name of the range finder entity (Not asBase).
There is also a RemoveEntityCollideCallback variant, which takes a range parameter.
You may specify multiple range callbacks per entity pairs (E.g. a callback for distance =4, 5 and 6), but not multiple callbacks for the same combination of parent,child and distance.
Finally, there are two find-distance between entity functions. These are FAR slower than the range callback stuff, so use that if you just want to test if x within a certain distance of y. But if you MUST know the distance:
These both do the same thing. GetEntityDistanceLinear is slower except when the objects are extremely close.
asBase must again be a prop which allows attachments, whereas asEntity can be anything, including "Player", and maybe astrix too.
So basically you create a sphere which origin is the entity which I check for its closeness? If we take my example with the doors which shall open when the player approaches, every door "gets" a sphere around itself and if the player collides with it, we know that he's in range.
Imho pretty strange that you have to do a workaround with loading the needed sphere entity instead of supporting a function for distance but ok.
Strangely when i first started your testmap, ED was always -1 || -1.
I reloaded and now it's working.
Thank you very much, I will use this method in my custom story. Should I
ever come to release it, how do you want to be mentioned in credits?
(This post was last modified: 09-04-2012, 04:44 PM by craven7.)
Yeah, amnesia scripting doesn't expose positions, as I'm assuming frictional didn't think such behaviour would be needed (also, it probably simplified saving the state of the game, as only stuff using SetLocalVar/SetGlobalVar is saved - rather than saving the state of classes etc in the script).
You are correct that my approach is using attached spheres to the origin of props, the callback code just attaches one sphere and creates a collision callback on that sphere. The distance code uses binary (or linear) search over the range of sphere sizes to find the first distance at which the range spheres and the target prop intersect.
With regards to the reload - amnesia can do that if you add the entities to the folder whilst it is running. It probably caches a mapping from file names -> file paths so that no file paths need to be specified in scripting, but that cache wasn't updated (i'm guessing it's probably constructed when the resources file is loaded).
Don't worry about putting me in the credits, but if you want to include me it's entirely up to you how you mention me .
Edit 2:
I think it is just that the "In Range" message is a little deceptive. The range message is displaying the name of the child entity, rather than the parent entity. Try the following:
Yes, that's why I replaced the grunt(base) with the doors(entity). But now I have to get the entities name's by using string operations which isn't so nice after all. I'd appreciate to have a more "direct" reference, despite knowing that it's only a string and not an object.
///////////////////////////////
//+ Entity Within Range Callback
///////////////////////////////
//Add a callback for when asEntity is within <maxRange> of asBase (collision callback). asBase must allow attachments.
//Duplicate identical attachments (range+base+entity are identical) behaviour is undefined.
void AddEntityWithinRangeCollideCallback(string &in asBase, string &in asEntity, string &in asCallback, int alStates, float maxRange)
{
float range = GetRangeValCeil(maxRange);
string attachName = "RANGECB_"+asBase+asEntity+range;
AddAttachedPropToProp(asBase,attachName,GetRangeFileName(range),0,0,0,0,0,0);
AddEntityCollideCallback(asEntity,attachName,asCallback,false,alStates);
//Set local vars for getter functions
SetLocalVarString("Base_" + attachName,asBase);
SetLocalVarFloat("Range_" + attachName,maxRange);
}
//Remove a range callback and associated attachments
void RemoveEntityWithinRangeCollideCallback(string &in asBase, string &in asEntity, float maxRange)
{
float range = GetRangeValCeil(maxRange);
string attachName = "RANGECB_"+asBase+asEntity+range;
RemoveEntityCollideCallback(asEntity,attachName);
RemoveAttachedPropFromProp(asBase,attachName);
//Reset Local vars
SetLocalVarString("Base_" + attachName,"");
SetLocalVarFloat("Range_" + attachName,0);
}
//Get the base entity from the range callback
string GetBaseFromRangeCallback(string &in asChild)
{
return GetLocalVarString("Base_" + asChild);
}
//Get the base entity from the range callback
float GetMaxRangeFromRangeCallback(string &in asChild)
{
return GetLocalVarFloat("Range_" + asChild);
}
///////////////////////////////
//- Entity Within Range Callback
///////////////////////////////
string[] stringSplit(string &in asString, string asDelim)
{
string[] output = {""};
//For each character in the input, check against the output.
int s1 = int(asString.length()); int s2 = int(asDelim.length());
int lastMatch = 0;
//Add all but final elements
for(int i=0; i<=s1 - s2; i++)
{
if(StringSub(asString,i,s2) == asDelim)
{
//Add element to output
output[output.length() - 1] = StringSub(asString,lastMatch,i-lastMatch);
output.resize(output.length() + 1);
//Move search along
i += s2;
lastMatch = i;
}
}
//Add lastMatch -> final
output[output.length() - 1] = StringSub(asString,lastMatch,s1 - lastMatch);
return output;
}
You can then manually split asChild into an array using: