TTLG|Thief|Bioshock|System Shock|Deus Ex|Mobile
Page 2 of 2 FirstFirst 12
Results 26 to 48 of 48

Thread: Understanding Squirrel Scripts

  1. #26
    Member
    Registered: Mar 2001
    Location: Ireland
    Post the code that you're trying to use.

  2. #27
    Member
    Registered: Jan 2012
    Location: Gèrmany
    What I want is to get a string config variable (ConfigGetRaw)
    But neither

    Code:
    str=string()
    Engine.ConfigGetRaw("resname_base",str)
    print(str)
    #Result empty
    Nor
    Code:
    str=string()
    Version.GetGame(str)
    print(str)
    #Result empty
    Seam to do anything with their str parameter. The return result is 1 (for success).


    EDIT: So either I'm doing something terribly wrong or something with string() is really terribly wrong. (or the functions)

    ---------------------
    Some knowledge i (think) figured out:

    string is a class here so string("init") is a class instance
    "init" get somewhere stored in it. I can not iterate it find out where or what's the index name.
    when used for example with print(string("init")) it invokes some kinda tostring() function. (also hidden)

    Any idea how the get the variables in a class.instance?

    ---
    string and int_ref got in their class body
    a constructor func
    two empty tables __setTable and __getTable
    as well as these two functions
    __overload_constructor0 and __overload_constructor1
    Last edited by Daraan; 6th Dec 2017 at 18:53.

  3. #28
    Member
    Registered: Nov 2001
    Location: uk
    I was just half way through writing something that contains most of that.
    printing anything invokes its tostring or _tostring function (depending on what it is and whether _tostring exists, that's used to override the built in tostring on something)

    I would do a foreach on it, but instances of the string class don't seem to let you do that. (I assume you equally already tried that)

    It doesn't to me look like you're doing anything wrong if that helps at all. The rather un-squirrel like syntax of all those functions in the name of making it the same as the c++ version it's a wrapper for.

  4. #29
    Member
    Registered: Nov 2001
    Location: uk
    Having poked at some stuff not in thief, you can't find out what's in a class instance unless the class implements methods for doing that (and that's normal for squirrel rather than specific to that class which is what I wanted to find out).

    if the class has a _nexti function in it then you should be able to foreach over the instance I think, you'd not generally do that unless the class was the sort of thing that you'd want to foreach over rather than just doing it because you want to treat it like a table and see what's in it because someone else wrote it and didn't tell you how it works.

    So I'm sticking with my unhelpful answer of that I agree the documentation says you should do what you're doing, it seems consistent with the rest of the script interface documentation and as far as I can see that ought to do what you'd expect.

  5. #30
    Member
    Registered: Jan 2012
    Location: Gèrmany
    Definitely a big thank you for looking into it as well.

    I want to break it down to the two main possibilities I see at the moment:
    Either the function results and 'return' of string are not in sync like
    script("init")->script.slot1
    Function(script)->script.slot2
    _tostring()->return script.slot1
    We could work around that if we knew the name of slot2

    But what I fear has the higher possibility.
    OR All the string & functions do not invoke their return part. (Maybe some typo deep inside the same function)
    Why I think that? All string & functions don't care/throw when you give them a totally wrong data type as parameters, the others specifically want a int/float_ref, vector.

  6. #31
    Member
    Registered: Sep 1999
    Location: Texas, hhhwweeee hawww
    Scripts are my weak suite because I haven't played with them at all, not even the basic ones. I've been playing in Dromed in every other aspect. I just want to thank everyone who is carving out this new opportunity in scripting. Thank you so much.

  7. #32
    ZylonBane
    Registered: Sep 2000
    Location: ZylonBane
    Here's a proof of concept script for a flashlight. I say proof of concept because, while it works, it updates at far too choppy a rate to be tolerable for actual gameplay. But if this script or something like it was implemented in native code it would probably be fine.



    It works pretty much the same as the flashlights in the original Deus Ex and Half-Life... just a dynamic light source shot out from the player view. So, not perfect, but about par for the course for engines of this vintage. Here's the script code:
    Code:
    function OnTimer() {
    	if (message().name == "flashlight") {
    		// some constants
    		local maxDist = 60;
    		local maxRadius = 15;
    		local maxLight = 200;
    		local deg2rad = PI / 180;
    		local lightObj = IsDataSet("lightObj") ? GetData("lightObj") : SetData("lightObj", Object.Named("lightsource"));
    		// get camera position/facing
    		local pos = Camera.GetPosition();
    		local face = Camera.GetFacing();
    		local faceY = face.y * deg2rad;
    		local faceZ = face.z * deg2rad;
    		// find out if there's a surface within range
    		local testPos = vector();
    		testPos.x = pos.x + maxDist * cos(faceY) * cos(faceZ);
    		testPos.y = pos.y + maxDist * cos(faceY) * sin(faceZ);
    		testPos.z = pos.z + maxDist * sin(faceY - PI);
    		local hitPos = vector();
    		// do the thing
    		if (Engine.PortalRaycast(pos, testPos, hitPos)) {
    			// calc distance to impact
    			local v = (hitPos - pos);
    			local d = sqrt(v.Dot(v));
    			local lightRad = maxRadius * (d / maxDist);
    			// step back from impact by radius of the light
    			local d2 = d - lightRad / 2;
    			if (d2 < 0) {
    				d2 = 0;
    			}
    			// calc actual light position
    			// (there's probably a better way to do this)
    			local lightPos = vector();
    			lightPos.x = pos.x + d2 * cos(faceY) * cos(faceZ);
    			lightPos.y = pos.y + d2 * cos(faceY) * sin(faceZ);
    			lightPos.z = pos.z + d2 * sin(faceY - PI);
    			// apply updated light params
    			Property.SetSimple(lightObj, "SelfLit", maxLight);
    			Property.SetSimple(lightObj, "SelfLitRad", lightRad);
    			Object.Teleport(lightObj, lightPos, face); // facing doesn't matter
    		}
    		else {
    			// disable light
    			Property.SetSimple(lightObj, "SelfLit", 0);
    		}
    	}
    }
    Note that this script uses terrain raycasting instead of object raycasting for performan-- ok actually I couldn't figure out how to get the objraycast function to work. Still, doesn't look too bad.

    I understand that NV's NVGizmos mission also had a flashlight demo that may have been similar to this in approach. I've never looked at it, so maybe I'm just reinventing the wheel here.
    Last edited by ZylonBane; 6th Mar 2018 at 20:23.

  8. #33
    Member
    Registered: Jun 2015
    Location: Germany
    I am using the Flashlight from NV in my FM Five Nights at Dayport. I like this tool, but it's more for moderns FM. In my Lost Place Contest FM, I want to use a latern with the light effect from Amnesia, but this was a fail.

  9. #34
    Member
    Registered: Sep 1999
    Location: Texas, hhhwweeee hawww
    Looks real good ZB. What about the smoothness of a flare's light? Why is that so smooth as a dynamic light and not this? I wonder if there isn't a way to focus down the light a flare does. Aside from that, I was thinking of making a AI with deadlights, I wonder how this could work coming out of an AI.

  10. #35
    ZylonBane
    Registered: Sep 2000
    Location: ZylonBane
    Flares are omnidirectional point light sources. Flashlights, on the other hand, project a directional beam. So having the illuminated area fade smoooothly out at the edges would have been wrong.

  11. #36
    Member
    Registered: Mar 2001
    Location: Ireland
    What's the timer interval on that flashlight? You seem to have left that out of the code segment.

  12. #37
    ZylonBane
    Registered: Sep 2000
    Location: ZylonBane
    You got me. The original version of that script was just piggybacking on the radiation check timer that's already on the SS2 player object. So it was running about... 10 times per second I think.

    Here's an updated version that now does object raycasting, and runs ~30 times per second. Somewhat amazingly it does both without bogging down the engine. The objraycast service is freakishly accurate too-- seems to be per-polygon, which has to be an expensive operation. This version also implements distance attenuation of the light and creating its own light emitter object.



    Dark's vertex lighting is so dodgy on large, low-poly objects that I had to implement directly adding extra light to objects to ensure that they would light up at all, which about doubled the length and complexity of the function, yet it's still very prone to visual inconsistencies. Oh well, not like the Half Life or Deus Ex flashlights were much better.

    Code:
    function OnBeginScript() {
    	SetOneShotTimer("flashlight", 0.5);
    }
    
    function OnTimer() {
    	if (message().name == "flashlight") {
    		// some constants
    		local maxDist = 60;
    		local minRadius = 3;
    		local maxRadius = 17;
    		local maxLight = 250;
    		local deg2rad = PI / 180;
    		local lightObj = GetData("flob");
    		local lastObjLit = GetData("lastObjLit");
    		// ensure flashlight object exists and ID saved
    		if (!lightObj) {
    			if (!Object.Named("flob")) {
    				lightObj = Object.Create("Marker");
    				Object.SetName(lightObj, "flob")
    			}
    			else {
    				lightObj = Object.Named("flob");
    			}
    			SetData("flob", lightObj);
    			SetData("lastObjLit", 0);
    			lastObjLit = 0;
    		}
    		// get camera position/facing
    		local pos = Camera.GetPosition();
    		local face = Camera.GetFacing();
    		local faceY = face.y * deg2rad;
    		local faceZ = face.z * deg2rad;
    		// find out if there's a surface within range
    		local testPos = vector();
    		testPos.x = pos.x + maxDist * cos(faceY) * cos(faceZ);
    		testPos.y = pos.y + maxDist * cos(faceY) * sin(faceZ);
    		testPos.z = pos.z + maxDist * sin(faceY - PI);
    		local hitPos = vector();
    		local hitObj = object();
    		local hitObjID = 0;
    		// return values: 0 nothing, 1 terrain, 2 object, 3 mesh
    		local rayHit = Engine.ObjRaycast(pos, testPos, hitPos, hitObj, 0, 0, lightObj, 0);
    		if (rayHit) {
    			// calc distance to impact
    			local v = (hitPos - pos);
    			local d = sqrt(v.Dot(v));
    			local lightRad = (maxRadius - minRadius) * (d / maxDist) + minRadius;
    			local lightBright = maxLight * (1 - (d / maxDist));
    			// step back from impact by radius of the light
    			local d2 = d - lightRad / 2;
    			// never position light behind player
    			if (d2 < 0) {
    				d2 = 0;
    			}
    			// calc actual light position
    			// (there's probably a better way to do this)
    			local lightPos = vector();
    			lightPos.x = pos.x + d2 * cos(faceY) * cos(faceZ);
    			lightPos.y = pos.y + d2 * cos(faceY) * sin(faceZ);
    			lightPos.z = pos.z + d2 * sin(faceY - PI);
    			// apply updated light params
    			Property.SetSimple(lightObj, "SelfLit", lightBright);
    			Property.SetSimple(lightObj, "SelfLitRad", lightRad);
    			Object.Teleport(lightObj, lightPos, face); // facing doesn't matter
    			// handle object lighting
    			// (objects often need some help due to vertex lighting plus low poly counts)
    			if (rayHit == 2) {
    				hitObjID = hitObj.tointeger();
    				// check for object focus change
    				if (hitObjID != abs(lastObjLit)) {
    					killObjLight(lastObjLit);
    					// don't mess with objects that already have Extra Light
    					if (Property.Possessed(hitObjID, "ExtraLight")) {
    						lastObjLit = -hitObjID;
    					}
    					else {
    						lastObjLit = hitObjID;
    						Property.Set(hitObjID, "ExtraLight", "Additive?", TRUE);
    					}
    					SetData("lastObjLit", lastObjLit);
    				}
    				if (lastObjLit > 0) {
    					// fade to target brightness
    					local curLum = Property.Get(hitObjID, "ExtraLight", "Amount (-1..1)");
    					// (lightBright / maxLight) / 2) is the target extra light
    					Property.Set(hitObjID, "ExtraLight", "Amount (-1..1)", curLum + (((lightBright / maxLight) / 2) - curLum) / 3);
    				}
    			}
    			else {
    				killObjLight(lastObjLit);
    			}
    		}
    		else {
    			// light didn't reach anything
    			Property.SetSimple(lightObj, "SelfLit", 0);
    			killObjLight(lastObjLit);
    		}
    		// again!
    		SetOneShotTimer("flashlight", 0.0333);
    	}
    }
    
    // remove extra light from last lit object
    function killObjLight(lastObjLit) {
    	if (lastObjLit != 0) {
    		SetData("lastObjLit", 0);
    		if (lastObjLit > 0) {
    			Property.Remove(lastObjLit, "ExtraLight");
    		}
    	}
    }

  13. #38
    Zombified
    Registered: Sep 2004
    Quote Originally Posted by ZylonBane View Post
    directly adding extra light to objects
    nuts. also yeah, this is completely usable - as long as it's possible to make the AIs react to the light (investigate).

  14. #39
    ZylonBane
    Registered: Sep 2000
    Location: ZylonBane
    Why nuts?

  15. #40
    Zombified
    Registered: Sep 2004
    out of the box thinking/solution. also a bit of pun, because why not.

  16. #41
    Member
    Registered: Jan 2001
    Location: Pushing my luck with Dromed
    Let's say a script is triggered by receiving a stim (I.e. the object has a receptron whose effect is "Send to Scripts"), and it needs to get the ID of the stim's source (e.g. to do something to that object).

    function OnSomeStimStimulus()
    {
    things go here
    }


    Within that it block it may look like we can get the source ID with message().source (see API-reference_messages.txt, and search for sStimMsg), but the value of that is a long number, not the ID of the source object. That's because it's getting the ID of the source that the other object is sending.

    I don't fully understand it I think sources and receptrons are links (hence the question we see when deleting them), so we can use the functions in API-reference_services.txt to see what's available for links. The source object ID is the source of the link (the dest is the stimulus itself). There are no quick functions to get the source of a link, so it takes a few lines of code to get it:

    local sourceID = message().source; //gets the ID of the source of the stimulus, similar to getting a link ID

    local sourceInfo = sLink();
    LinkTools.LinkGet(sourceID, sourceInfo); //these two lines get the source or the link itself

    local sourceObj = sourceInfo.source; //this gets the ID of the object that sent the stim

  17. #42
    ZylonBane
    Registered: Sep 2000
    Location: ZylonBane
    What's your question?

  18. #43
    Member
    Registered: Jan 2001
    Location: Pushing my luck with Dromed
    Quote Originally Posted by R Soul View Post
    The purpose of this thread is to explain things so more people can have a go and know what they're doing, rather than just copying and pasting code samples.
    I've kind of gone against what I said about copying code samples, but I think there's sufficient explanation (unless I've misunderstood the nature of S&R) to help people see why that code sample works.

  19. #44
    ZylonBane
    Registered: Sep 2000
    Location: ZylonBane
    I see. Your previous post was written in such a stream-of-consciousness manner I really couldn't tell what you were doing.

  20. #45
    Member
    Registered: Jan 2001
    Location: Pushing my luck with Dromed
    I know this isn't a wiki but fee free to suggest ways the post could be improved.

  21. #46
    Member
    Registered: Jan 2001
    Location: Pushing my luck with Dromed
    Here's a script for buttons/levers etc that adds FrobInert to them for some time and then removes it. E.g. if you have a lever that controls some mechanism, which in Dromed could be a sequence of events, you may not want the player to turn it off before the events have finished.

    It also supports design note parameters to customize things.
    TempFrobInertDelay=time in milliseconds (default: 2000)
    TempFrobInertMetaProp=alternate metaprop to add (default: FrobInert)

    I don't know if this has been mentioned, but if you have just one parameter, and it's a numerical value, you have to end the text with a ; for squirrel to recognise it.

    Code:
    class TempFrobInert extends SqRootScript
    {
    	function OnFrobWorldEnd()
    	{
    		local delayTime = 2.0; //default
    		local delayParam = GetClassName() + "Delay";
    		if(delayParam in userparams())
    		{	
    			local delayTimeMS = userparams()[delayParam];
    			delayTime = delayTimeMS/1000;
    		}
    		
    		local mprop = "FrobInert"; //default
    		local mpropParam = GetClassName() + "MetaProp";
    		if(mpropParam in userparams())
    		{
    			mprop = userparams()[mpropParam];
    		}
    		
    		Object.AddMetaProperty(self, mprop);
    		SetOneShotTimer("FrobAgain", delayTime, mprop);
    	}
    	
    	function OnTimer()
    	{
    		if(message().name == "FrobAgain")
    			Object.RemoveMetaProperty(self, message().data);
    	}
    }

  22. #47
    ZylonBane
    Registered: Sep 2000
    Location: ZylonBane
    There are a couple of ways to generate random numbers in Squirrel, and I was curious which one provided the best performance.

    The first way, standard for Dark Engine scripts, is to use Data.RandFlt0to1(), which returns exactly what it says on the tin. There are a few other random number functions in the script API, but this is the most broadly useful one.

    The second way is to use Squirrel's native random number generator. This is slightly less convenient because Squirrel's rand() function returns an integer instead of a float. To get the standard 0 - 1 range you have to use the formula rand().tofloat() / RAND_MAX.

    As a subset of the second way, so I wouldn't have to repeat that formula all over my code, I wrapped it in a shared library function. I figured this would incur a minor but acceptable performance impact. Stand by for that.

    So I stuck each method in a loop that generated a million random numbers, averaged the times together from 10 runs, and here's what I got:

    Average Times
    RandFlt0to1(): 1.85 seconds
    rand().tofloat() / RAND_MAX: 1.125 seconds
    lib.Rand(): 1.04 seconds

    (This test was run on a Core i5 750, a rather dated chip, so any modern CPU would of course have much speedier times.)

    So yeah, no surprise that the native Squirrel random function performs nearly twice as fast as the script service. But the big surprise was that the wrapped-in-a-library-function version performed even faster instead of slightly slower. I have no explanation for this, but there it is.

    Now here's a video of glass breaking.


  23. #48
    ZylonBane
    Registered: Sep 2000
    Location: ZylonBane
    The recent discussion of checking when a player is looking at an object got me thinking about this problem, so I gave it a shot in Squirrel. My first attempt involved angular diameters and vision cones, but I couldn't get the trigonometry voodoo to work right, so I had to give up on that and use the brute-force raycasting approach.

    Code:
    class ZBLookTrap extends SqRootScript {
    	static CLASSNAME = "ZBLookTrap";
    	static LINKFLAVOR = "ControlDevice"; // thief
    	//static LINKFLAVOR = "SwitchLink"; // shock
    
    	// get the ball rolling
    	function OnBeginScript() {
    		if (IsDataSet("LookTimer")) {
    			KillTimer(GetData("LookTimer"));
    		}
    		SetData("LookTimer", SetOneShotTimer("LookCheck", 0.5));
    	}
    
    	// perform the look check
    	function OnTimer() {
    		if (message().name == "LookCheck") {
    			// fetch instance params
    			local maxDist          = getParam(CLASSNAME + "MaxDist", 150);           // maximum check distance
    			local checkInt         = getParam(CLASSNAME + "CheckInt", 0.25);         // check interval, in seconds
    			local repeatOn         = getParam(CLASSNAME + "RepeatOn", false);        // send continuous TurnOns when looking
    			local ignoreAI         = getParam(CLASSNAME + "IgnoreAI", false);        // exclude AIs from line-of-sight check
    			local ignoreUnrendered = getParam(CLASSNAME + "IgnoreUnrendered", true); // exclude objects with render mode Not Rendered
    			// check for line of sight
    			local wasLooking = GetData("WasLooking");
    			local isLooking = false;
    			if (!Camera.IsRemote()) {
    				local pos = Camera.GetPosition();
    				local face = Camera.GetFacing() * (PI / 180); // get facing as radians
    				// calc distant point player is looking at
    				local testPos = vector(
    					pos.x + maxDist * cos(face.y) * cos(face.z),
    					pos.y + maxDist * cos(face.y) * sin(face.z),
    					pos.z + maxDist * sin(face.y - PI));
    				local hitObj = object();
    				isLooking = (Engine.ObjRaycast(pos, testPos, vector(), hitObj, 0, ignoreAI.tointeger() + ignoreUnrendered.tointeger() * 2, null, null) > 1) && (hitObj.tointeger() == self);
    			}
    			// detect state transitions
    			if (isLooking && (!wasLooking || repeatOn)) {
    				Link.BroadcastOnAllLinks(self, "TurnOn", LINKFLAVOR);
    				SetData("WasLooking", 1);
    			}
    			else if (!isLooking && wasLooking) {
    				Link.BroadcastOnAllLinks(self, "TurnOff", LINKFLAVOR);
    				SetData("WasLooking", 0);
    			}
    			SetData("LookTimer", SetOneShotTimer("LookCheck", checkInt));
    		}
    	}
    
    	// fetch a parameter or return default value
    	function getParam(key, defVal) {
    		return key in userparams() ? userparams()[key] : defVal;
    	}
    }
    Place on object you want to check looking at, it'll send TurnOn when looked at, TurnOff when looked away from. Supports some design note configurating:
    • ZBLookTrapMaxDist: Maximum check distance (default 150)
    • ZBLookTrapCheckInt: Check interval, in seconds (default 0.25)
    • ZBLookTrapRepeatOn: Send continuous TurnOns when looking (default false)
    • ZBLookTrapIgnoreAI: Exclude AIs from line-of-sight check (default false)
    • ZBLookTrapIgnoreUnrendered: Exclude objects with render mode Not Rendered (default true)

    Last edited by ZylonBane; 9th Apr 2020 at 02:18.

Page 2 of 2 FirstFirst 12

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •