Welcome to Keen Software House Forums! Log in or Sign up to interact with the KSH community.
  1. You are currently browsing our forum as a guest. Create your own forum account to access all forum functionality.

Scripting noob question.

Discussion in 'Programming Questions and Suggestions' started by HurricaneGirl, Apr 10, 2015.

Thread Status:
This last post in this thread was made more than 31 days old.
  1. HurricaneGirl Apprentice Engineer

    Messages:
    170
    So I need a script and while I know a tiny bit about programming, I have NO clue where to find the functions of in game scripting. Can anyone tell me where to find the list? I'd rather not have to pick apart other people's scripts to try and figure out how to do things.

    thanks!
     
  2. Malware Master Engineer

    Messages:
    9,862
    You can find a help file in the Tools folder of your game installation path. But let me warn you, the API is a right mess, and even some bog-standard C# stuff fails in K#. However more likely than not there are people here willing to help if you need it. We have been through much of the troubles ourselves :)
     
  3. HurricaneGirl Apprentice Engineer

    Messages:
    170
    Well I posted it to the Script Request subforum and had no responses to it in nearly a week. I really need this script for my server and just figured I'd try and make a go of it myself. :)
     
  4. Malware Master Engineer

    Messages:
    9,862
    @HurricaneGirl Ok, I just found your post. So what you want to know is whether the grid the programmable block belongs to is moving?
     
  5. HurricaneGirl Apprentice Engineer

    Messages:
    170
    I believe so. I need a script that will identify if a beacon is being moved. If it is being moved, I would like that beacon turned off, then the script should start a timer block to turn the beacon back on (unless the script can so so itself)
     
  6. Malware Master Engineer

    Messages:
    9,862
    Intriguing... I've already started to look at it.
     
  7. HurricaneGirl Apprentice Engineer

    Messages:
    170
    Thank you so much!!
     
  8. Malware Master Engineer

    Messages:
    9,862
    @HurricaneGirl OK, Instruction time.

    First things first: Here's the script itself:
    Code:
    // If you want the script to work on a specific beacon, set its name here.
    // Otherwise it will simply work on _all_ beacons.
    public const string BeaconName = null;
    // The motion tolerance. If we have moved more than this value per second,
    // we count the grid as moved. This value _must_ be above zero.
    public const double Epsilon = 0.001;
    // The number of timer ticks before the beacons are reactivated after
    // stopping.
    public const int TicksBeforeReactivation = 180; // Assuming a 1-second timer, 3 minutes
    IMyProgrammableBlock _terminal;
    IMyBeacon _beacon;
    Vector3D? _lastPosition;
    int _tickCount;
    public void Main()
    {
        if (_terminal == null)
        {
            // We're not initialized, let's do so now.
            Initialize();
        }
        // First we determine the current running state. If the Storage property is "MOVED", we have detected
        // motion and should count towards reenabling the beacon.
        var state = string.IsNullOrEmpty(Storage) ? "IDLE" : Storage;
        switch (state)
        {
            case "IDLE":
            {
                var mps = DetectMotion();
                _terminal.SetCustomName("We are stopped (" + mps + "mps)");
                break;
            }
            case "MOVED":
            {
                var mps = DetectStop();
                _terminal.SetCustomName("We are moving (" + mps + "mps)");
                DetectStop();
                break;
            }
            case "STOPPED":
                if (_tickCount > 0) _terminal.SetCustomName(string.Format("We have stopped for {0} ticks.", _tickCount));
                if (!TimeIsElapsed())
                    DetectMotion();
                break;
        }
    }
    public double DetectMotion()
    {
        var currentPosition = _terminal.GetPosition();
        var mpt = 0.0;
        // If the previous position is known, we need to see if there was a change.
        if (_lastPosition != null)
        {
            // If the distance between the current- and previous positions are greater
            // than the configured epsilon, we have detected motion.
            mpt = Vector3D.Distance(currentPosition, _lastPosition.Value);
            if (mpt > Epsilon)
            {
                // Change the state,
                Storage = "MOVED";
                // Reset the timing counter
                _tickCount = 0;
                // and disable the relevant beacons.
                ChangeBeaconStates(false);
            }
        }
        _lastPosition = currentPosition;
        return mpt;
    }
    public double DetectStop()
    {
        var currentPosition = _terminal.GetPosition();
        var mpt = 0.0;
        // If the previous position is known, we need to see if there was a change.
        if (_lastPosition != null)
        {
            // If the distance between the current- and previous positions are less
            // than the configured epsilon, we have detected no motion.
            mpt = Vector3D.Distance(currentPosition, _lastPosition.Value);
            if (mpt <= Epsilon)
            {
                // Change the state,
                Storage = "STOPPED";
                // and disable the relevant beacons.
                ChangeBeaconStates(false);
            }
        }
        _lastPosition = currentPosition;
        return mpt;
    }
    public bool TimeIsElapsed()
    {
        // Check if the tick counter has passed our configured number.
        // If we have, reset to the starting state and reenable the beacon(s).
        if (_tickCount >= TicksBeforeReactivation)
        {
            Storage = "IDLE";
            _tickCount = 0;
            ChangeBeaconStates(true);
            return true;
        }
        // Not yet, increase the number and exit.
        _tickCount++;
        return false;
    }
    public void Initialize()
    {
        _terminal = FindRunningTerminal();
        if (BeaconName != null)
        {
            _beacon = GridTerminalSystem.GetBlockWithName(BeaconName) as IMyBeacon;
            if (_beacon == null)
                throw new Exception("Could not find a beacon named " + BeaconName);
        }
    }
    public void ChangeBeaconStates(bool newState)
    {
        // If we have a specified beacon, change only that state. Otherwise change
        // the states of every beacon on the grid.
        if (_beacon != null)
            _beacon.RequestEnable(newState);
        else
        {
            var beacons = new List<IMyTerminalBlock>();
            GridTerminalSystem.GetBlocksOfType<IMyBeacon>(beacons);
            beacons.ForEach(beacon => ((IMyFunctionalBlock)beacon).RequestEnable(newState));
        }
    }
    public IMyProgrammableBlock FindRunningTerminal()
    {
        var list = new List<IMyTerminalBlock>();
        GridTerminalSystem.GetBlocksOfType<IMyProgrammableBlock>(list, block => ((IMyProgrammableBlock)block).IsRunning);
        if (list.Count != 1)
            throw new Exception("Unexpected error: Could not find exactly one running program. Keen has changed something important!");
        return list[0] as IMyProgrammableBlock;
    }
    
    You'll need to set up a timer, 1 second interval, restart itself, run a programmable block.
    The code goes into the PB obviously. More likely than not, you can just leave the code as-is. However if you have multiple beacons but only want to disable/enable one, put its name into the BeaconName constant.

    If you need a higher performance, you could in theory set the timer to trigger itself immediately, but firstly I don't know how reliable that is on a DS, and it will be difficult, I think, to adjust the Epsilon speed tolerance for it.

    Now I know I suck at explaining, so it's better if you try it. It should be simple enough. If you need tweaks and can't do it yourself, let me know.

    [Edit] With performance I mean the resolution of the timer itself, not code performance :p
     
  9. HurricaneGirl Apprentice Engineer

    Messages:
    170
    Thanks so much! I can't wait to try it!
     
  10. HurricaneGirl Apprentice Engineer

    Messages:
    170
    It works very very well!! Thank you @LordDevious ! I haven't tried it on a ship with multiple beacons yet, but I found that if I change the 'null' to the beacon's name, it won't work. Am I doing something wrong there?
     
    Last edited: Apr 13, 2015
  11. Malware Master Engineer

    Messages:
    9,862
    Mno, more likely than not that would be me ;) I'll try to remember to check it for you when I can. I thought I tested that... Be aware the name must be very exact though, it's case sensitive.
     
    • Like Like x 1
  12. Malware Master Engineer

    Messages:
    9,862
    Hm. It works for me... Have you verified that the ownerships match? The script must follow the same ownership rules you do.

    Also the script currently needs to be recompiled as beacon ownerships change. I'll see if I can make that part a bit easier. However the way programmable blocks work, if their ownerships change, they require a rebuild, and there's nothing I can do about that.

    [Edit] It kind of depends how you want this to work. The way it works today is the most informative, because it will fail if there is no matching beacon. However if it does fail you need to rebuild it again. If you do not want it to fail on a missing beacon, it can't inform you of a missing beacon.

    So: If you want it to match a specific beacon:
    • Make sure there is a beacon of the specific name.
    • Make sure the beacon and the relevant programmable block has the same ownership.
    • Recompile the script.
    • ???
    • Profit.
     
    Last edited: Apr 15, 2015
  13. HurricaneGirl Apprentice Engineer

    Messages:
    170
    I hadn't thought about recompiling. I'll give it a try! Thanks!

    Do I need to change every instance of 'null' or just the first one?
     
  14. Wicorel Senior Engineer

    Messages:
    1,262
    Just the first one.
     
  15. Malware Master Engineer

    Messages:
    9,862
    Please make sure you include a quote or a tag, or I won't see that you've asked a question :) Glad @Wicorel could give you a correct answer :)
     
    • Like Like x 1
Thread Status:
This last post in this thread was made more than 31 days old.