Control a Block's Properties with On/Off Logic Signals

    Joined
    Aug 11, 2014
    Messages
    11
    Reaction score
    4
    • Legacy Citizen
    • Purchased!
    There are two separate, but related, elements introduced in this Suggestion.

    Introducing
    • GUI/Properties Template
    • Property Block
    Elements involved
    • Source Block (e.g. AI block; not sure what the naming convention is for this type of block)
    • Logic Block
    • GUI/Properties Template
    • Property Block
    GUI/Properties Template

    Suggesting a generic template Object between any property-laden block type, its GUI rendering, and external access/modification (as by Property Blocks).

    Note that, though arranged as a JSON object, this is more akin pseudo-code.

    (AI block is something like this)
    [

    {
    name:'Type',
    type:'select',
    options:[

    'turret',
    'ship'
    ],
    default:'turret'
    },
    {

    name:'Activated'
    type:'checkbox',
    default:false
    },
    {

    name:'Target'
    type:'select',
    options:['any','selected'],
    default:'any'
    }
    ]

    (Individual object instances would appear as thus)
    {

    Type:'turret',
    Activated:true,
    Target:'selected'
    }

    Property Block

    {
    propertyName:<text input>
    }

    Property Blocks, too, would be modifiable via GUI on selection, though they'd have only either one or two text input fields to designate the property they're targeting. If property name comparison is somehow too computation-heavy, they can be assigned values for a select list, based on their targeted Source Block.

    I believe blocks that may target other blocks in a 1:n ratio with 'c'+'v' are called Control Blocks. A Property Block would be one of these.

    [Bobby AI-Block] <------ [Property Block] <------ [Logic Block]

    Things occur in this order
    1. Logic Block signal fires (say, from 0 to 1)
    2. Property block receives signal, and sends a message to Bobby AI-Block containing the property name and its new value
    3. Bobby AI-Block receives message, and acts on these rules:
    • select-list properties
      • 0: set to default value or do nothing
      • 1: set to item in list matching Property Block's property name
    • checkbox properties
      • 0: set to false (uncheck)
      • 1: set to true (check that box!)
    Taking the possible reverse order into account, that would be...

    [Bobby AI-Block] ------> [Property Block] ------> [Logic Block]
    1. Player changes AI-Block property through the GUI
    2. AI-Block sends a message to all Property Blocks it's pointing to
    3. Receiving Property Blocks check the message (property-name:value) against their own set property name, and, if matching, fire off that value to any Logic Blocks they're pointing to under these rules:
    • select-list properties
      • name matches: fire 1
      • name does not match: do nothing
    • checkbox properties
      • name matches: fire passed value (0 or 1)
      • name does not match: do nothing
    A fully-controllabe Bobby AI-Block setup would look something like this:

    Source Block, Property Block, Logic Block
    [AI] <---- [Type.turret] <------ [0/1]
    |b | <---- [Type.ship] <-------- [0/1]
    |l | <---- [Activated] <-------- [0/1]
    |o | <---- [Target.any] <------- [0/1]
    [k ] <---- [Target.selected] <-- [0/1]


    If a property block has two fields, the first may supply the property name, and the next its selection, should the property refer to a select list. If a single input field, then perhaps a standard '.' may act as a delimiter between the two values in the single provided string. Or a ':', or whichever, really doesn't matter.

    And there you go!

    And there you go! Properties programmable through in-game logic blocks! Woohoo!

    This first occurred to me after an AI-controlled turret fired on a neutral ship, because I didn't know what I was doing and focused on it with 'f'. I realized the utility of being able to activate or deactivate AI blocks remotely, and fitting that into the game's Logic blocks would be ideal.

    In any case, this could see utility for all sorts of things. If any block with its own GUI-changeable properties could also be modified via Logic Blocks, the interstellar void is the limit!

    Not to mention, if the generic template setup is also in place, it'll be ready for the eventual modding community's adding their own blocks, expecting to be able to work them all into more complex in-game, logic-controlled behavior.
     
    • Like
    Reactions: NeonSturm

    NeonSturm

    StormMaker
    Joined
    Dec 31, 2013
    Messages
    5,110
    Reaction score
    617
    • Wired for Logic
    • Thinking Positive
    • Legacy Citizen 5
    1. One problem is that we don't have a splitter block jet.
    A way to access multiple properties via logic, and at least 2 are required for a serial communication interface (1 clock, 1 data)​
    2. The second is that logic currently requires a huge amount of blocks - thus decreases the abilities of small ships maybe too much.


    1. To solve the first, I would suggest that computers forward 0/1 values to a line of buttons touching them, but pointing away from them in a strait line.

    2. To solve the second issue, we either need to treat bits differently per block category, so that logic blocks have 4-8 bits (made a suggestion)

    or have a computer block and a logic array (like weapon computers) and enter a program into the computer.
    It can have a minimum of linked logic modules or it cease to work, run slower/faster with more logic modules (tick count, max logic steps per tick)...

    I have a WIP example of a possible interface (But the update logic state process is not implemented and you can't assign blocks names jet)
    Code:
    # just copy the path from a file browser into your console (after the change directory command)
    cd /home/USER/Downloads/ #Linux
    cd C:\...\Downloads\ #Windows
    
    #Create bytecode (.java -> .class) class file will be in the same folder
    javac ./Logic.java
    # run it
    java ./Logic
    JavaScript:
    import static java.lang.Math.*;
    import java.util.Scanner;
    import java.util.InputMismatchException;
    import java.util.LinkedList;
    import java.util.ListIterator;
    
    public class Logic
    {
       public static final String READ_LINE = "input>  ";
       public static final String WRITE_LINE = "result>  ";
       public static final String WRITE_ERROR = "ERROR>  ";
    
       public static void main( String p[] )
       {
         Logic.Element[] logicElements = new Logic.Element[100];
    
         Scanner scanner = new Scanner( System.in );
         String[] action;
    
         int selected = 0;
         do
         {
           System.out.print("\n"+ READ_LINE);
           action = scanner.nextLine().split("[ ]+");
    
           if( action == null || action.length < 1 || action[0].equals("") )
           {
             System.out.println( WRITE_ERROR +"No arguments!" );
             System.out.println( WRITE_ERROR +"Available commands: {a|add, s|select, t|trigger, c|use_input, v|use_output, d|destroy}, exit");
             continue;
           }
           if( action[0].equals("exit") ) break;
    
           int i = selected;
           if( action.length >1 ) try
           {
             i = Integer.parseInt( action[1] );
           }
           catch(NumberFormatException e)
           {
             System.out.println( WRITE_ERROR +"The first argument has to be a number!" );
             System.out.println( WRITE_ERROR +"Available commands: {a|add, s|select, t|trigger, c|use_input, v|use_output, d|destroy}, exit");
             continue;
           }
    
         Element.tick();
    
           switch( action[0] )
           {
           case "add":
           case "a":
             if( action.length < 2 )
             {
               System.out.println( WRITE_ERROR +"Not enough arguments!" );
               System.out.println( WRITE_ERROR +"Syntax: a|add int:index {button,all,nall,one,none,even,odd}");
             }
             else
             {
               if( logicElements[i] != null )
                 System.out.println( WRITE_ERROR + "Index "+ i +" is not empty" );
               else switch( action[2] )
               {
               case "button":
                 System.out.println( WRITE_LINE + "Add a new Button-element to index "+ i );
                 logicElements[i] = new Logic.Button();
               break;
               case "all":
                 System.out.println( WRITE_LINE + "Add a new All-element to index "+ i );
                 logicElements[i] = new Logic.All();
               break;
               case "nall":
                 System.out.println( WRITE_LINE + "Add a new NAll-element to index "+ i );
                 logicElements[i] = new Logic.NAll();
               break;
               case "one":
                 System.out.println( WRITE_LINE + "Add a new One-element to index "+ i );
                 logicElements[i] = new Logic.One();
               break;
               case "none":
                 System.out.println( WRITE_LINE + "Add a new None-element to index "+ i );
                 logicElements[i] = new Logic.NOne();
               break;
               case "even":
                 System.out.println( WRITE_LINE + "Add a new Even-element to index "+ i );
                 logicElements[i] = new Logic.Even();
               break;
               case "odd":
                 System.out.println( WRITE_LINE + "Add a new Odd-element to index "+ i );
                 logicElements[i] = new Logic.Odd();
               break;
               default:
                 System.out.println( WRITE_ERROR +"Not a valid element. Try: {button, all, nall, one, none, even, odd}");
               }
             }
             break;
           case "select":
           case "s":
             if( action.length < 1 )
             {
               System.out.println( WRITE_ERROR +"Not enough arguments!" );
               System.out.println( WRITE_ERROR +"Syntax: s|select int:index");
             }
             else
             {
               if( logicElements[i] == null )
                 System.out.println( WRITE_ERROR + "Index "+ i +" is not set" );
               else
               {
                 selected = i;
                 System.out.println( WRITE_LINE + "Seclected index "+ i +": "+ logicElements[i].toString() );
               }
             }
             break;
           case "trigger":
           case "t":
             if( action.length < 1 )
             {
               System.out.println( WRITE_ERROR +"Not enough arguments!" );
               System.out.println( WRITE_ERROR +"Syntax: t|trigger int:index");
             }
             else
             {
               if( logicElements[i] == null )
                 System.out.println( WRITE_ERROR + "Index "+ i +" is not set" );
               else
               {
                 System.out.println( WRITE_LINE + "Trigger index "+ i +": "+ logicElements[i].toString() );
                 logicElements[i].activate();
                 System.out.println( WRITE_LINE + "Triggered index "+ i +": "+ logicElements[i].toString() );
               }
             }
             break;
           case "use_input":
           case "c":
             if( action.length < 1 )
             {
               System.out.println( WRITE_ERROR +"Not enough arguments!" );
               System.out.println( WRITE_ERROR +"Syntax: c|connect int:index(input)");
               System.out.println( WRITE_ERROR +"! Use together with s|select(signal destination)");
             }
             else
             {
               if( logicElements[i] == null )
                 System.out.println( WRITE_ERROR + "Index "+ i +" is not set" );
               else if( i == selected )
                 System.out.println( WRITE_ERROR + "Can not connect to itself. Selected index: " +i );
               else
               {
                 if
                 (   !logicElements[i].outputs.remove(logicElements[selected])
                 &   !logicElements[selected].inputs.remove(logicElements[i])
                 ){
                   logicElements[i].outputs.add(logicElements[selected]);
                   logicElements[selected].inputs.add(logicElements[i]);
                   System.out.println( WRITE_LINE + "Connected input "+ i +": "+ logicElements[i].toString() );
                 }
                 else System.out.println( WRITE_LINE + "Dis-connected input "+ i +": "+ logicElements[i].toString() );
                 logicElements[selected].update();
               }
             }
             break;
           case "use_output":
           case "v":
             if( action.length < 1 )
             {
               System.out.println( WRITE_ERROR +"Not enough arguments!" );
               System.out.println( WRITE_ERROR +"Syntax: c|connect int:index(output)");
               System.out.println( WRITE_ERROR +"! Use together with s|select(signal source)");
             }
             else
             {
               if( logicElements[i] == null )
                 System.out.println( WRITE_ERROR + "Index "+ i +" is not set" );
               else if( i == selected )
                 System.out.println( WRITE_ERROR + "Can not connect to itself. Selected index: " +i );
               else
               {
                 if
                 (   !logicElements[i].inputs.remove(logicElements[selected])
                 &   !logicElements[selected].outputs.remove(logicElements[i])
                 ){
                   logicElements[i].inputs.add(logicElements[selected]);
                   logicElements[selected].outputs.add(logicElements[i]);
                   System.out.println( WRITE_LINE + "Connected output "+ i +": "+ logicElements[i].toString() );
                 }
                 else System.out.println( WRITE_LINE + "Dis-connected output "+ i +": "+ logicElements[i].toString() );
                 logicElements[selected].update();
               }
             }
             break;
           case "destroy":
           case "d":
             if( action.length < 1 )
             {
               System.out.println( WRITE_ERROR +"Not enough arguments!" );
               System.out.println( WRITE_ERROR +"Syntax: d|destroy int:index");
             }
             else
             {
               if( logicElements[i] == null )
                 System.out.println( WRITE_ERROR + "Index "+ i +" is not set" );
               else
               {
                 System.out.println( WRITE_LINE + "Destroyed index "+ i +": "+ logicElements[i].toString() );
                 logicElements[i].destroy();
                 logicElements[i] = null;
               }
             }
             break;
           default:
             System.out.println( WRITE_ERROR +"Not a valid command!" );
             System.out.println( WRITE_ERROR +"Available commands: {a|add, s|select, t|trigger, c|use_input, v|use_output, d|destroy}, exit");
           }
         }
         while(true);
       }
    
       public static class Element
       {
         protected static Logic.Element Element;
    
         protected boolean updating = false;
         protected LinkedList<Logic.Element> updatedElements = new LinkedList<Logic.Element>();
    
         // ((super.fn))
         public int state = 0;
         public LinkedList<Logic.Element> inputs = new LinkedList<Logic.Element>();
         public LinkedList<Logic.Element> outputs = new LinkedList<Logic.Element>();
    
         public void update()
         {
           this.updating = false;
           int state = 0;
           ListIterator ili = inputs.listIterator();
           while( ili.hasNext() )
           {
             state = this.process(state, ((Element)ili.next()).state);
           }
           if( (state = this.finalProcess(state)) != this.state )
           {
             this.state = state;
             if( !this.updating ) // do not add it twice
             {
               this.updating = true;
               Element.updatedElements.add( this );
             }
           }
         }
    
         public Element()
         {
           if( Element.Element == null ) Element.Element = this;
    
           ListIterator ueli = Element.updatedElements.listIterator();
           while( ueli.hasNext() )
           {
             ListIterator oli = ((Element)ueli.next()).outputs.listIterator();
             while( oli.hasNext() )
             {
               ((Element)oli.next()).update();
             }
           }
         }
    
         public void activate(){}
    
         public void destroy()
         {
           if( this.updating ) Element.updatedElements.remove( this );
           else this.updating = true;
    
           ListIterator ili = this.inputs.listIterator();
           while( ili.hasNext() )
           {
             ((Element)ili.next()).outputs.remove( this );
           }
           inputs.clear();
    
           ListIterator oli = this.outputs.listIterator();
           while( oli.hasNext() )
           {
             Logic.Element le = (Element)oli.next();
             le.inputs.remove( this );
             le.update();
           }
           outputs.clear();
         }
    
         protected static int process(int state1, int state2){return state1;}
         protected static int finalProcess(int state){return state;}
    
         public String toString(){ return "Logic.Element{ state: "+ state +" }"; }
       }
    
       public static class Button extends Element
       {
         protected int pass = 0x00FF;
    
         public void activate()
         {
           if( this.state == 0x0000 )
           {
             System.out.print( READ_LINE +"Enter a valid pass as int> " );
             try
             {
               int pass = Integer.parseInt( (new Scanner(System.in)).nextLine() );
               this.pass = pass < 0x0100 ? pass : this.pass;
             }
             catch(NumberFormatException e)
             {
               System.out.println( WRITE_LINE +"Using previously entered pass" );
             }
    
             this.state = this.pass;
           }
           else this.state = 0x0000;
           if( !this.updating ) // do not add it twice
           {
             this.updating = true;
             Element.updatedElements.add( this );
           }
         }
    
         protected static int process(int state, int state2)
         {
           return 0x0000;
         }
    
         public String toString(){ return "Logic.Button{ state: "+ state +", filter: "+ pass +" }"; }
       }
       public static class All extends Element
       {
         protected static int process(int state, int state2)
         {
           return state & state2;
         }
    
         public String toString(){ return "Logic.All{ state: "+ state +" }"; }
       }
       public static class NAll extends Element
       {
         protected static int process(int state, int state2)
         {
           return state & state2;
         }
         protected static int finalProcess(int state)
         {
           return ~state;
         }
    
         public String toString(){ return "Logic.NAll{ state: "+ state +" }"; }
       }
       public static class One extends Element
       {
         protected static int process(int state, int state2)
         {
           return state | state2;
         }
    
         public String toString(){ return "Logic.One{ state: "+ state +" }"; }
       }
       public static class NOne extends Element
       {
         protected static int process(int state, int state2)
         {
           return state | state2;
         }
         protected static int finalProcess(int state)
         {
           return ~state;
         }
    
         public String toString(){ return "Logic.NOne{ state: "+ state +" }"; }
       }
       public static class Odd extends Element
       {
         protected static int process(int state, int state2)
         {
           return state ^ state2;
         }
    
         public String toString(){ return "Logic.Odd{ state: "+ state +" }"; }
       }
       public static class Even extends Element
       {
         protected static int process(int state, int state2)
         {
           return state ^ state2;
         }
         protected static int finalProcess(int state)
         {
           return ~state;
         }
    
         public String toString(){ return "Logic.Even{ state: "+ state +" }"; }
       }
    }
     
    Joined
    Aug 11, 2014
    Messages
    11
    Reaction score
    4
    • Legacy Citizen
    • Purchased!
    If I understand your points correctly, point 2 is outside the scope of the original suggestion, but makes a good... point. I'm still looking over your code, and may have more to say on it later.

    Regarding point 1, though, any block can act as a splitter as long as you provide for it multiple targets ('c'+'v'). Granted, I usually opt for using the standard button block (the toggle-able one with the hand on it) for both splitting and organization, but that, at least, is possible with any, is it not?

    I think the scale of logic gates used individually is appropriate, despite the amount of space they take, if only because it is consistent with other block games (Minecraft, in particular), where stated largeness is the expectation. I do, however, enjoy the idea you suggested of an alternative logic block/gate arrangement inside the computer block, itself.