From aa8b2e4e05bfa4e0cb25cb8176bbc3d061f3e4cc Mon Sep 17 00:00:00 2001
From: helixhorned <helixhorned@1a8010ca-5511-0410-912e-c29ae57300e0>
Date: Wed, 3 Aug 2011 17:22:25 +0000
Subject: [PATCH] Lots of M32script tweaks:

* fix breaking out of 'switch' blocks and compilation of the 'default' case
* Have a way of assigning 'special function' menu [' F] entries to script states. Writing a string literal after the state name will register the state under that name. Menu names are limited to 24 characters and it's possible to have up to 16 of them.
* new branching command 'ifinteractive', true if a state runs from the menu mentioned above.
* new command: getnumberfromuser <<retvar>> "query_string" <maxnum> <flags>

See 'state collect_teleporting_sectors' in a.m32 for a combined usage of the new functionality.

git-svn-id: https://svn.eduke32.com/eduke32@1955 1a8010ca-5511-0410-912e-c29ae57300e0
---
 polymer/eduke32/build/src/build.c |  48 ++++--
 polymer/eduke32/samples/a.m32     |  17 +-
 polymer/eduke32/samples/tests.m32 |  68 ++++++++
 polymer/eduke32/source/astub.c    | 254 ++++++++++++++++++++----------
 polymer/eduke32/source/m32def.c   |  33 +++-
 polymer/eduke32/source/m32def.h   |  27 +++-
 polymer/eduke32/source/m32exec.c  |  73 +++++----
 7 files changed, 381 insertions(+), 139 deletions(-)

diff --git a/polymer/eduke32/build/src/build.c b/polymer/eduke32/build/src/build.c
index 5504cdd5f..aca5e414d 100644
--- a/polymer/eduke32/build/src/build.c
+++ b/polymer/eduke32/build/src/build.c
@@ -7722,21 +7722,26 @@ int32_t getnumber_autocomplete(const char *namestart, char ch, int32_t *danum, i
     return 0;
 }
 
-// sign is now used for more than one flag:
+// sign is now used for more than one flag (also _getnumber256):
 //  1: sign
 //  2: autocomplete names
 //  4: autocomplete taglabels
 //  8: return -1 if cancelled
 int32_t _getnumber16(const char *namestart, int32_t num, int32_t maxnumber, char sign, void *(func)(int32_t))
 {
-    char buffer[80], ch;
+    char buffer[80], ournamestart[80-17], ch;
     int32_t n, danum, oldnum;
-    uint8_t flags = (sign>>1)&7;
+    uint8_t flags = (sign&(2|4|8))>>1;
     sign &= 1;
 
     danum = num;
     oldnum = danum;
 
+    // need to have 4+11+2==17 chars room at the end
+    // ("^011", max. string length of an int32, "_ ")
+    Bstrncpy(ournamestart, namestart, sizeof(ournamestart));
+    ournamestart[sizeof(ournamestart)-1] = 0;
+
     bflushchars();
     while (keystatus[0x1] == 0)
     {
@@ -7746,14 +7751,16 @@ int32_t _getnumber16(const char *namestart, int32_t num, int32_t maxnumber, char
         idle();
         ch = bgetchar();
 
-        Bsprintf(buffer,"%s^011%d", namestart, danum);
-        n = Bstrlen(buffer);
-        if (totalclock & 32) Bstrcat(buffer,"_ ");
+        Bsprintf(buffer, "%s^011%d", ournamestart, danum);
+        n = Bstrlen(buffer);  // maximum is 62+4+11 == 77
+        if (totalclock & 32)
+            Bstrcat(buffer,"_ ");
+        // max strlen now 79
         _printmessage16("%s", buffer);
 
         if (func != NULL)
         {
-            Bsprintf(buffer, "%s", (char *)func(danum));
+            Bsnprintf(buffer, sizeof(buffer), "%s", (char *)func(danum));
             // printext16(200L-24, ydim-STATUS2DSIZ+20L, editorcolors[9], editorcolors[0], buffer, 0);
             printext16(n<<3, ydim-STATUS2DSIZ+128, editorcolors[11], -1, buffer,0);
         }
@@ -7762,7 +7769,7 @@ int32_t _getnumber16(const char *namestart, int32_t num, int32_t maxnumber, char
 
         n = 0;
         if (getnumber_internal1(ch, &danum, maxnumber, sign) ||
-            (n=getnumber_autocomplete(namestart, ch, &danum, flags)))
+            (n=getnumber_autocomplete(ournamestart, ch, &danum, flags&(1+2))))
         {
             if (flags==1 || n==0)
                 printmessage16("%s", buffer);
@@ -7794,14 +7801,19 @@ static void getnumber_clearline(void)
 // sign: |16: don't draw scene
 int32_t _getnumber256(const char *namestart, int32_t num, int32_t maxnumber, char sign, void *(func)(int32_t))
 {
-    char buffer[80], ch;
+    char buffer[80], ournamestart[80-13], ch;
     int32_t danum, oldnum;
-    uint8_t flags = (sign>>1)&(3|8);
+    uint8_t flags = (sign&(2|4|8|16))>>1;
     sign &= 1;
 
     danum = num;
     oldnum = danum;
 
+    // need to have 11+2==13 chars room at the end
+    // (max. string length of an int32, "_ ")
+    Bstrncpy(ournamestart, namestart, sizeof(ournamestart));
+    ournamestart[sizeof(ournamestart)-1] = 0;
+
     bflushchars();
     while (keystatus[0x1] == 0)
     {
@@ -7842,19 +7854,22 @@ int32_t _getnumber256(const char *namestart, int32_t num, int32_t maxnumber, cha
 
         getnumber_clearline();
 
-        Bsprintf(buffer,"%s%d",namestart,danum);
-        if (totalclock & 32) Bstrcat(buffer,"_ ");
+        Bsprintf(buffer,"%s%d",ournamestart,danum);
+        // max strlen now 66+11==77
+        if (totalclock & 32)
+            Bstrcat(buffer,"_ ");
+        // max strlen now 79
         printmessage256(0, 0, buffer);
         if (func != NULL)
         {
-            Bsprintf(buffer, "%s", (char *)func(danum));
+            Bsnprintf(buffer, sizeof(buffer), "%s", (char *)func(danum));
             printmessage256(0, 9, buffer);
         }
 
         showframe(1);
 
         if (getnumber_internal1(ch, &danum, maxnumber, sign) ||
-            getnumber_autocomplete(namestart, ch, &danum, flags&(1+2)))
+            getnumber_autocomplete(ournamestart, ch, &danum, flags&(1+2)))
         {
             if (danum != oldnum)
                 asksave = 1;
@@ -7863,6 +7878,9 @@ int32_t _getnumber256(const char *namestart, int32_t num, int32_t maxnumber, cha
         }
     }
 
+    if (keystatus[0x1] && (flags&4))
+        oldnum = -1;
+
     clearkeys();
 
     lockclock = totalclock;  //Reset timing
@@ -9172,7 +9190,7 @@ void _printmessage16(const char *fmt, ...)
     va_list va;
 
     va_start(va, fmt);
-    Bvsnprintf(tmpstr, 156, fmt, va);
+    Bvsnprintf(tmpstr, sizeof(tmpstr), fmt, va);
     va_end(va);
 
     i = 0;
diff --git a/polymer/eduke32/samples/a.m32 b/polymer/eduke32/samples/a.m32
index df21aec9c..71ef7d9ea 100644
--- a/polymer/eduke32/samples/a.m32
+++ b/polymer/eduke32/samples/a.m32
@@ -86,6 +86,7 @@ gamearray parm 8
 
 // prints out maphack light definitions based on SE lights in map
 defstate printlights
+    "Print Polymer lights"
     var flags
 
     print "--PRLIGHTS--"
@@ -119,6 +120,7 @@ defstate printlights
 ends
 
 defstate insertlights
+    "Insert active SE lights"
     var sectnum
 
     set k 0
@@ -204,8 +206,7 @@ defstate fiddlewithlights
         ifeitherctrl mul j 10
 
         switch k
-  // "break" breaks too far or there's something wrong with the switch -- needs to be looked at
-//        case 0: break;
+        case 0: break;
         case 1:
         {
             set k pr_parallaxscale
@@ -250,7 +251,7 @@ defstate fiddlewithlights
         ifeitherctrl mul j 10
 
         switch k
-//        case 0: break;
+        case 0: break;
         case 3:
         {
             set k pr_specularfactor
@@ -500,7 +501,16 @@ defstate try_nextsector
 ends
 
 defstate collect_teleporting_sectors // (sec)
+    "Collect telep. sectors"
     var numsects
+
+    set numsects numsectors, sub numsects 1
+    ifinteractive
+    {
+        getnumberfromuser sec "starting sectnum: " numsects 8
+        ifl sec 0, return
+    }
+
     collectsectors collectedsectors sec numsects try_nextsector
     for i range numsects
         sethighlightsector collectedsectors[i] 1
@@ -1186,6 +1196,7 @@ defstate chselshade
 ends
 
 defstate correctslopes
+    "Correct cstat&2/heinum"
     for i allsectors
     {
         set j sector[i].ceilingstat, and j 2
diff --git a/polymer/eduke32/samples/tests.m32 b/polymer/eduke32/samples/tests.m32
index d804746d8..551f13dba 100644
--- a/polymer/eduke32/samples/tests.m32
+++ b/polymer/eduke32/samples/tests.m32
@@ -25,6 +25,7 @@ ends
 // various tests of m32-script features
 
 defstate arraytest
+    "Array test"
     getarraysize ar tmp
     resizearray ar 65536
     getticks parm[2]
@@ -42,6 +43,7 @@ defstate arraytest
 ends
 
 defstate itertest
+    "Iteration test"
     var gi gj gk
     // iteration and break test
 
@@ -82,6 +84,7 @@ define MIN_CONSTANT -2147483648
 
 // tests various combinations of constants and labels
 defstate consttest
+    "Constants test"
     quote " --- Constants test ---", quote " "
 
     quote "Should be 0:"
@@ -154,6 +157,7 @@ defstate consttest
 ends
 
 defstate anmtest
+    "Sprite yoffsets"
     var yo
 
     for i range MAXTILES
@@ -175,3 +179,67 @@ defstate anmtest
         }
     }
 ends
+
+// switch/break/default regression test, inspired by
+// http://forums.duke4.net/topic/1348-mapster32-problems-and-bugs/page__view__findpost__p__101510
+defstate switchtest
+    "'switch' test"
+    set i 3622
+    whilevarn i 3779
+    {
+        // test for many things at once:
+
+        // sorting of key values
+        switch i
+        case 3625 set j 666  /* fall-through */
+        default set j -1 break
+        case 3622 set j 3919 break
+        case 3626 set j -100
+        case 3623 set j 3685 break
+        case 3627 set j 1234 break
+        endswitch
+
+        // 'break' should always lead us here
+        ifle i 3627
+        {
+            qsprintf TQUOTE "j=%d" j
+            quote TQUOTE
+        }
+
+        add i 1
+    }
+
+    quote "----"
+
+    // same thing, slightly different syntax
+    set i 3622
+    whilevarn i 3779
+    {
+        switch i
+        {
+            case 3625 set j 666
+            default set j -1 break
+            case 3622 set j 3919 break
+            case 3626 set j -100
+            case 3623 set j 3685 break
+            case 3627 set j 1234 break
+        }
+        endswitch
+
+        ifle i 3627
+        {
+            qsprintf TQUOTE "j=%d" j
+            quote TQUOTE
+        }
+
+        add i 1
+    }
+
+    // correct output:
+    // j=3919
+    // j=3685
+    // j=-1
+    // j=-1
+    // j=3685
+    // j=1234
+ends
diff --git a/polymer/eduke32/source/astub.c b/polymer/eduke32/source/astub.c
index 5a17ccf1f..165af4257 100644
--- a/polymer/eduke32/source/astub.c
+++ b/polymer/eduke32/source/astub.c
@@ -9133,7 +9133,7 @@ static int32_t osdcmd_do(const osdfuncparm_t *parm)
 {
     intptr_t oscrofs;
     char *tp;
-    int32_t i, j, slen, ofs;
+    int32_t i, j, slen, ofs, dontsavehist;
     int32_t onumconstants=g_numSavedConstants;
 
     if (parm->numparms==0)
@@ -9147,6 +9147,10 @@ static int32_t osdcmd_do(const osdfuncparm_t *parm)
     if (!tp) goto OUTOFMEM;
     Bmemcpy(tp, parm->raw+ofs, slen);
 
+    // explicitly typed space at beginning of command (really? not eaten by OSD
+    // code?) or M32script call from 'special functions' menu
+    dontsavehist = (slen==0 || tp[0]==' ');
+
     // needed so that subsequent commands won't execute old stuff.
     tp[slen] = '\n';
     tp[slen+1] = '\0';
@@ -9184,23 +9188,17 @@ static int32_t osdcmd_do(const osdfuncparm_t *parm)
             vm.g_sp = &sprite[vm.g_i];
         }
 
+        // If OSD is down, that would interfere with user input, so don't consider
+        // m32script executed from the console as 'interactive'. Which leaves only
+        // that from the 'special functions' menu
+        if (OSD_GetRowsCur() < 0)
+            vm.miscflags |= VMFLAG_MISC_INTERACTIVE;
+
         VM_Execute(0);
 
-        if (vm.updatehighlight)
-        {
-            update_highlight();
-            vm.updatehighlight = 0;
-        }
+        M32_PostScriptExec();
 
-        if (vm.updatehighlightsector)
-        {
-            update_highlightsector();
-            if (qsetmode != 200)
-                ovh_whiteoutgrab(1);
-            vm.updatehighlightsector = 0;
-        }
-
-        if (!(vm.flags&VMFLAG_ERROR))
+        if (!(vm.flags&VMFLAG_ERROR) && !dontsavehist)
         {
             int32_t idx, dosave=1;
 
@@ -12507,9 +12505,10 @@ static void GenericSpriteSearch(void)
     keystatus[KEYSC_ESC] = 0;
 }
 
-// Build edit
+////////// SPECIAL FUNCTIONS MENU //////////
 
-static const char *FuncMenuStrings[] =
+static int32_t numMenuFunctions = 8;
+static char *funcMenuStrings[8*3] =
 {
     "Replace invalid tiles",
     "Delete all spr of tile #",
@@ -12519,38 +12518,111 @@ static const char *FuncMenuStrings[] =
     "Resize selection",
     "Global shade divide",
     "Global visibility divide"
+    // dynamic menu entries start here
 };
 
+static ofstype funcMenuStatenum[8*2];
+
+void registerMenuFunction(const char *funcname, int32_t stateidx)
+{
+    char fn[25];
+    int32_t i;
+
+    if (funcname == NULL)  // unregister stateidx
+    {
+        int32_t j;
+
+        for (i=8; i<numMenuFunctions; i++)
+            if (funcMenuStatenum[i-8]==stateidx)
+            {
+                Bfree(funcMenuStrings[i]);
+
+                for (j=i; j<numMenuFunctions-1; j++)
+                {
+                    funcMenuStatenum[j] = funcMenuStatenum[j+1];
+                    funcMenuStrings[j] = funcMenuStrings[j+1];
+                }
+
+                funcMenuStatenum[j] = 0;
+                funcMenuStrings[j] = NULL;
+
+                numMenuFunctions--;
+
+                break;
+            }
+
+        return;
+    }
+
+    // register menu entry named FUNCNAME to call the M32script
+    // state with index STATEIDX
+    Bstrncpy(fn, funcname, sizeof(fn));
+    fn[sizeof(fn)-1] = 0;
+
+    for (i=8; i<numMenuFunctions; i++)
+    {
+        if (funcMenuStatenum[i-8]==stateidx)
+        {
+            // same stateidx, different name
+            Bfree(funcMenuStrings[i]);
+            funcMenuStrings[i] = Bstrdup(fn);
+            return;
+        }
+        else if (!Bstrcmp(funcMenuStrings[i], fn))
+        {
+            // same name, different stateidx
+            funcMenuStatenum[i-8] = stateidx;
+            return;
+        }
+    }
+
+    if (numMenuFunctions == 3*8)
+        return;  // max reached
+
+    funcMenuStrings[numMenuFunctions] = Bstrdup(fn);
+    funcMenuStatenum[numMenuFunctions-8] = stateidx;
+
+    numMenuFunctions++;
+}
+
 #define MENU_Y_SPACING 8
 #define MENU_BASE_Y ydim-STATUS2DSIZ+32
 
+static int32_t correct_picnum(int16_t *picnumptr)
+{
+    int32_t picnum = *picnumptr;
+
+    if ((unsigned)picnum >= MAXTILES || tilesizx[picnum] <= 0)
+    {
+        *picnumptr = 0;
+        return 1;
+    }
+
+    return 0;
+}
 
 static void FuncMenuOpts(void)
 {
     int32_t x = 8;
     int32_t y = MENU_BASE_Y+16;
-    int32_t i = 0;
-    //  int32_t x2 = 0;
-    //    static int32_t x2_max = 0;
+    int32_t i;
 
-    int32_t numopts = (sizeof(FuncMenuStrings)/sizeof(FuncMenuStrings[0]));
-
-    do
+    for (i=0; i<numMenuFunctions; i++)
     {
-        //        x2 =
-        printext16(x,y,editorcolors[11],editorcolors[0],FuncMenuStrings[i],0);
-        //    if (x2 > x2_max) x2_max = x2;
+        if (i==8 || i==16)
+        {
+            x += 208;
+            y = MENU_BASE_Y+16;
+        }
+
+        printext16(x,y,editorcolors[11],editorcolors[0],funcMenuStrings[i],0);
         y += MENU_Y_SPACING;
     }
-    while (++i < numopts);
-    //    drawline16(x-1,y,x2_max+1,y,1);
-    //  drawline16(x-1,MENU_BASE_Y-4,x-1,y,1);
 
-    //    x2 =
-    printext16(x,MENU_BASE_Y,editorcolors[11],-1,"Special functions",0);
-    //    drawline16(x-1,MENU_BASE_Y-4,x2+1,MENU_BASE_Y-4,1);
-    //  drawline16(x2_max+1,MENU_BASE_Y+16-4,x2_max+1,y-1,1);
-    //drawline16(x2+1,MENU_BASE_Y+16-1,x2_max+1,MENU_BASE_Y+16-1,1);
+    printext16(numMenuFunctions>8 ? 216 : 8, MENU_BASE_Y,
+               editorcolors[11], -1, "Special functions", 0);
+
+    clearkeys();
 }
 
 static void FuncMenu(void)
@@ -12558,6 +12630,15 @@ static void FuncMenu(void)
     char disptext[80];
     int32_t col=0, row=0, rowmax=7, dispwidth = 24, editval = 0, i = -1, j;
     int32_t xpos = 8, ypos = MENU_BASE_Y+16;
+    int32_t crowmax[3] = {7, -1, -1};
+
+    if (numMenuFunctions > 16)
+    {
+        crowmax[2] = numMenuFunctions-16-1;
+        crowmax[1] = 7;
+    }
+    else if (numMenuFunctions > 8)
+        crowmax[1] = numMenuFunctions-8-1;
 
     drawgradient();
 
@@ -12589,27 +12670,15 @@ static void FuncMenu(void)
                 row--;
             }
         }
-#if 0
+#if 1
         if (PRESSED_KEYSC(LEFT))
         {
-            /*            if (col == 2)
-            {
-            printext16(xpos,ypos+row*8,editorcolors[11],0,disptext,0);
-            col = 1;
-            xpos = 200;
-            rowmax = 6;
-            dispwidth = 24;
-            disptext[dispwidth] = 0;
-            if (row > rowmax) row = rowmax;
-            }
-            else */
-            if (col == 1)
+            if (col==1 || col==2)
             {
                 printext16(xpos,ypos+row*8,editorcolors[11],0,disptext,0);
-                col = 0;
-                xpos = 8;
-                rowmax = 7;
-                dispwidth = 24;
+                col--;
+                xpos -= 208;
+                rowmax = crowmax[col];
                 disptext[dispwidth] = 0;
                 if (row > rowmax) row = rowmax;
             }
@@ -12617,26 +12686,15 @@ static void FuncMenu(void)
 
         if (PRESSED_KEYSC(RIGHT))
         {
-            if (col == 0)
+            if ((col==0 || col==1) && crowmax[col+1]>=0)
             {
                 printext16(xpos,ypos+row*8,editorcolors[11],0,disptext,0);
-                col = 1;
-                xpos = 200;
-                rowmax = 0;
-                dispwidth = 24;
+                col++;
+                xpos += 208;
+                rowmax = crowmax[col];
                 disptext[dispwidth] = 0;
                 if (row > rowmax) row = rowmax;
             }
-            /*            else if (col == 1)
-            {
-            printext16(xpos,ypos+row*8,editorcolors[11],0,disptext,0);
-            col = 2;
-            xpos = 400;
-            rowmax = 6;
-            dispwidth = 26;
-            disptext[dispwidth] = 0;
-            if (row > rowmax) row = rowmax;
-            } */
         }
 #endif
         if (PRESSED_KEYSC(ENTER))
@@ -12644,33 +12702,60 @@ static void FuncMenu(void)
 
         switch (col)
         {
+        case 1:
+        case 2:
+        {
+            for (i=Bsnprintf(disptext,dispwidth,"%s",funcMenuStrings[col*8 + row]); i < dispwidth; i++)
+                disptext[i] = ' ';
+
+            if (editval)
+            {
+                char *statename = statesinfo[funcMenuStatenum[(col-1)*8 + row]].name;
+                int32_t snlen = Bstrlen(statename);
+                char *tmpscript = Bmalloc(1+5+1+snlen+1);
+
+                if (!tmpscript)
+                    break;
+
+                tmpscript[0] = ' ';  // don't save in history
+                Bmemcpy(&tmpscript[1], "state", 5);
+                tmpscript[1+5] = ' ';
+                Bmemcpy(&tmpscript[1+5+1], statename, snlen);
+                tmpscript[1+5+1+snlen] = 0;
+
+                M32RunScript(tmpscript);
+                Bfree(tmpscript);
+
+                if (vm.flags&VMFLAG_ERROR)
+                    printmessage16("There were errors while executing the menu function");
+                else
+                    printmessage16("Menu function executed successfully");
+            }
+
+            break;
+        }
         case 0:
             switch (row)
             {
             case 0:
             {
-                for (i=Bsprintf(disptext,"%s",FuncMenuStrings[row]); i < dispwidth; i++) disptext[i] = ' ';
+                for (i=Bsprintf(disptext,"%s",funcMenuStrings[row]); i < dispwidth; i++) disptext[i] = ' ';
                 if (editval)
                 {
                     j = 0;
                     for (i=0; i<MAXSECTORS; i++)
                     {
-                        if (tilesizx[sector[i].ceilingpicnum] <= 0)
-                            sector[i].ceilingpicnum = 0,j++;
-                        if (tilesizx[sector[i].floorpicnum] <= 0)
-                            sector[i].floorpicnum = 0,j++;
+                        j += correct_picnum(&sector[i].ceilingpicnum);
+                        j += correct_picnum(&sector[i].floorpicnum);
                     }
                     for (i=0; i<MAXWALLS; i++)
                     {
-                        if (tilesizx[wall[i].picnum] <= 0)
-                            wall[i].picnum = 0,j++;
-                        if (tilesizx[wall[i].overpicnum] <= 0)
-                            wall[i].overpicnum = 0,j++;
+                        j += correct_picnum(&wall[i].picnum);
+                        j += correct_picnum(&wall[i].overpicnum);
                     }
                     for (i=0; i<MAXSPRITES; i++)
                     {
-                        if (tilesizx[sprite[i].picnum] <= 0)
-                            sprite[i].picnum = 0,j++;
+                        j += correct_picnum(&sprite[i].picnum);
                     }
                     printmessage16("Replaced %d invalid tiles",j);
                 }
@@ -12678,7 +12763,7 @@ static void FuncMenu(void)
             break;
             case 1:
             {
-                for (i=Bsprintf(disptext,"%s",FuncMenuStrings[row]); i < dispwidth; i++) disptext[i] = ' ';
+                for (i=Bsprintf(disptext,"%s",funcMenuStrings[row]); i < dispwidth; i++) disptext[i] = ' ';
                 if (editval)
                 {
                     Bsprintf(tempbuf,"Delete all sprites of tile #: ");
@@ -12697,7 +12782,7 @@ static void FuncMenu(void)
             break;
             case 2:
             {
-                for (i=Bsprintf(disptext,"%s",FuncMenuStrings[row]); i < dispwidth; i++) disptext[i] = ' ';
+                for (i=Bsprintf(disptext,"%s",funcMenuStrings[row]); i < dispwidth; i++) disptext[i] = ' ';
                 if (editval)
                 {
                     j=getnumber16("Set map sky shade:    ",0,128,1);
@@ -12713,7 +12798,7 @@ static void FuncMenu(void)
             break;
             case 3:
             {
-                for (i=Bsprintf(disptext,"%s",FuncMenuStrings[row]); i < dispwidth; i++) disptext[i] = ' ';
+                for (i=Bsprintf(disptext,"%s",funcMenuStrings[row]); i < dispwidth; i++) disptext[i] = ' ';
                 if (editval)
                 {
                     j=getnumber16("Set map sky height:    ",0,16777216,1);
@@ -12732,7 +12817,7 @@ static void FuncMenu(void)
             break;
             case 4:
             {
-                for (i=Bsprintf(disptext,"%s",FuncMenuStrings[row]); i < dispwidth; i++) disptext[i] = ' ';
+                for (i=Bsprintf(disptext,"%s",funcMenuStrings[row]); i < dispwidth; i++) disptext[i] = ' ';
                 if (editval)
                 {
                     j=getnumber16("Z offset:    ",0,16777216,1);
@@ -12753,7 +12838,7 @@ static void FuncMenu(void)
             break;
             case 5:
             {
-                for (i=Bsprintf(disptext,"%s",FuncMenuStrings[row]); i < dispwidth; i++) disptext[i] = ' ';
+                for (i=Bsprintf(disptext,"%s",funcMenuStrings[row]); i < dispwidth; i++) disptext[i] = ' ';
                 if (editval)
                 {
                     j=getnumber16("Percentage of original:    ",100,1000,0);
@@ -12794,7 +12879,7 @@ static void FuncMenu(void)
             break;
             case 6:
             {
-                for (i=Bsprintf(disptext,"%s",FuncMenuStrings[row]); i < dispwidth; i++) disptext[i] = ' ';
+                for (i=Bsprintf(disptext,"%s",funcMenuStrings[row]); i < dispwidth; i++) disptext[i] = ' ';
                 if (editval)
                 {
                     j=getnumber16("Shade divisor:    ",1,128,1);
@@ -12817,7 +12902,7 @@ static void FuncMenu(void)
             break;
             case 7:
             {
-                for (i=Bsprintf(disptext,"%s",FuncMenuStrings[row]); i < dispwidth; i++) disptext[i] = ' ';
+                for (i=Bsprintf(disptext,"%s",funcMenuStrings[row]); i < dispwidth; i++) disptext[i] = ' ';
                 if (editval)
                 {
                     j=getnumber16("Visibility divisor:    ",1,128,0);
@@ -12838,6 +12923,7 @@ static void FuncMenu(void)
             }
             break;
         }
+
         printext16(xpos,ypos+row*MENU_Y_SPACING,editorcolors[11],editorcolors[1],disptext,0);
 
         showframe(1);
diff --git a/polymer/eduke32/source/m32def.c b/polymer/eduke32/source/m32def.c
index 35fe15311..79211ae6c 100644
--- a/polymer/eduke32/source/m32def.c
+++ b/polymer/eduke32/source/m32def.c
@@ -300,6 +300,7 @@ const char *keyw[] =
     "ifaimingsprite",
     "ifaimingwall",
     "ifaimingsector",
+    "ifinteractive",
 
 // BUILD functions
     "resetkey",
@@ -359,6 +360,7 @@ const char *keyw[] =
     "drawlabel",
     "getnumber16",
     "getnumber256",
+    "getnumberfromuser",
     "qsprintf",
     "qstrcat",
     "qstrcpy",
@@ -953,7 +955,7 @@ static int32_t parse_integer_literal(int32_t *num)
         long lnum;
         errno = 0;
         lnum = Bstrtol(textptr, NULL, 10);
-        if (errno || (sizeof(long)>4 && (lnum<INT_MIN || lnum>INT_MAX)))
+        if (errno || (sizeof(long)>4 && (lnum<INT32_MIN || lnum>INT32_MAX)))
         {
             C_CUSTOMERROR("integer literal exceeds bitwidth.");
             return 1;
@@ -1828,6 +1830,29 @@ static int32_t C_ParseCommand(void)
             statesinfo[j].numlocals = 0;
             Bsprintf(g_szCurrentBlockName, "%s", statesinfo[j].name);
 
+            if (C_GetKeyword() < 0)
+            {
+                ofstype *oscriptptr = g_scriptPtr;
+
+                if (C_GetNextVarOrString() == 1)  // inline string
+                {
+                    const char *menufuncname = (const char *)(oscriptptr+1);
+                    registerMenuFunction(menufuncname, j);
+
+                    g_scriptPtr = oscriptptr;
+                }
+                else
+                {
+                    C_CUSTOMERROR("expected inline string to be used as menu name.");
+                    return 1;
+                }
+            }
+            else if (j != g_stateCount)
+            {
+                // unregister that state with the menu if redefining and no menu name
+                registerMenuFunction(NULL, j);
+            }
+
             return 0;
         }
 
@@ -2380,7 +2405,7 @@ repeatcase:
             C_CUSTOMERROR("found `default' statement when not in switch");
             return 1;
         }
-        if (cs.caseScriptPtr && cs.caseScriptPtr[0]!=0)
+        if (cs.caseScriptPtr && cs.caseScriptPtr[0]!=-1)
         {
             C_CUSTOMERROR("multiple `default' statements found in switch");
         }
@@ -2934,6 +2959,7 @@ repeatcase:
     case CON_IFAIMINGSPRITE:
     case CON_IFAIMINGWALL:
     case CON_IFAIMINGSECTOR:
+    case CON_IFINTERACTIVE:
     {
         ofstype offset;
         ofstype lastScriptOfs = (g_scriptPtr-script-1);
@@ -3272,10 +3298,13 @@ repeatcase:
 
     case CON_GETNUMBER16:
     case CON_GETNUMBER256:
+    case CON_GETNUMBERFROMUSER:  // <<retvar>> "quote" <max> <flags>(1|2|4|8|(16))
         C_GetNextVarType(GV_WRITABLE);
         if (C_GetNextVarOrString()==-1)
             return 1;
         C_GetNextVar();
+        if (tw==CON_GETNUMBERFROMUSER)
+            C_GetNextVar();
         return 0;
 
     case CON_QSPRINTF:
diff --git a/polymer/eduke32/source/m32def.h b/polymer/eduke32/source/m32def.h
index 0210b6d03..33fa091d6 100644
--- a/polymer/eduke32/source/m32def.h
+++ b/polymer/eduke32/source/m32def.h
@@ -56,6 +56,9 @@ extern instype *g_scriptPtr;
 void C_Compile(const char *filenameortext, int32_t isfilename);
 void C_CompilationInfo(void);
 
+void registerMenuFunction(const char *funcname, ofstype scriptofs);
+void M32_PostScriptExec(void);
+
 typedef struct
 {
     int32_t ofs;  // offset into script[]
@@ -89,6 +92,19 @@ typedef struct {
 
 extern const tokenmap_t iter_tokens[];
 
+enum vmflags
+{
+    VMFLAG_RETURN = 1,
+    VMFLAG_BREAK = 2,
+    VMFLAG_ERROR = 4,
+};
+
+enum miscvmflags
+{
+    VMFLAG_MISC_UPDATEHL = 1,
+    VMFLAG_MISC_UPDATEHLSECT = 2,
+    VMFLAG_MISC_INTERACTIVE = 4,
+};
 
 typedef struct {
     int32_t g_i;
@@ -97,13 +113,10 @@ typedef struct {
     int32_t g_st;
     spritetype *g_sp;
     uint32_t flags; //g_errorFlag, g_returnFlag;
-    uint32_t updatehighlight;
-    uint32_t updatehighlightsector;
-} vmstate_t;
 
-#define VMFLAG_RETURN 1
-#define VMFLAG_BREAK 2
-#define VMFLAG_ERROR 4
+    // 1:updatehighlight, 2:updatehighlightsector, 4:interactive (from menu)?
+    uint32_t miscflags;
+} vmstate_t;
 
 extern vmstate_t vm;
 extern vmstate_t vm_default;
@@ -434,6 +447,7 @@ enum ScriptKeywords_t
     CON_IFAIMINGSPRITE,
     CON_IFAIMINGWALL,
     CON_IFAIMINGSECTOR,
+    CON_IFINTERACTIVE,
 
 // BUILD functions
     CON_RESETKEY,
@@ -495,6 +509,7 @@ enum ScriptKeywords_t
     CON_DRAWLABEL,
     CON_GETNUMBER16,
     CON_GETNUMBER256,
+    CON_GETNUMBERFROMUSER,
     CON_QSPRINTF,
     CON_QSTRCAT,
     CON_QSTRCPY,
diff --git a/polymer/eduke32/source/m32exec.c b/polymer/eduke32/source/m32exec.c
index 1d7e9b07f..fd8968d38 100644
--- a/polymer/eduke32/source/m32exec.c
+++ b/polymer/eduke32/source/m32exec.c
@@ -38,12 +38,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 vmstate_t vm;
 vmstate_t vm_default =
 {
-    -1,
-    0,
-    NULL,
-    0,
-    0,
-    0
+    -1,   // g_i
+    0,    // g_st
+    NULL, // g_sp
+    0,    // flags
+    0,    // miscflags
 };
 
 int32_t g_errorLineNum, g_tw;
@@ -135,6 +134,23 @@ void VM_ScriptInfo(void)
     }
 }
 
+void M32_PostScriptExec(void)
+{
+    if (vm.miscflags&VMFLAG_MISC_UPDATEHL)
+    {
+        update_highlight();
+        vm.miscflags &= ~VMFLAG_MISC_UPDATEHL;
+    }
+
+    if (vm.miscflags&VMFLAG_MISC_UPDATEHLSECT)
+    {
+        update_highlightsector();
+        if (qsetmode != 200)
+            ovh_whiteoutgrab(1);
+        vm.miscflags &= ~VMFLAG_MISC_UPDATEHLSECT;
+    }
+}
+
 void VM_OnEvent(register int32_t iEventID, register int32_t iActor)
 {
     if (iEventID < 0 || iEventID >= MAXEVENTS)
@@ -188,19 +204,7 @@ void VM_OnEvent(register int32_t iEventID, register int32_t iActor)
             message("ERROR executing %s. Event disabled.", label+(iEventID*MAXLABELLEN));
         }
 
-        if (vm.updatehighlight)
-        {
-            update_highlight();
-            vm.updatehighlight = 0;
-        }
-
-        if (vm.updatehighlightsector)
-        {
-            update_highlightsector();
-            if (qsetmode != 200)
-                ovh_whiteoutgrab(1);
-            vm.updatehighlightsector = 0;
-        }
+        M32_PostScriptExec();
 
         // restore old values...
         Bmemcpy(&vm, &vm_backup, sizeof(vmstate_t));
@@ -461,6 +465,7 @@ skip_check:
 //                    }
                 }
                 insptr = (instype *)(lCodeInsPtr + lEnd);
+                vm.flags &= ~VMFLAG_BREAK;
                 //Bsprintf(g_szBuf,"insptr=%d. ",     (int32_t)insptr); AddLog(g_szBuf);
                 //AddLog("Done Processing Switch");
                 continue;
@@ -1776,7 +1781,7 @@ badindex:
                 int64_t dax=Gv_GetVarX(*insptr++), day=Gv_GetVarX(*insptr++);
                 int64_t hypsq = dax*dax + day*day;
 
-                if (hypsq > (int64_t)INT_MAX)
+                if (hypsq > (int64_t)INT32_MAX)
                     Gv_SetVarX(retvar, (int32_t)sqrt((double)hypsq));
                 else
                     Gv_SetVarX(retvar, ksqrt((int32_t)hypsq));
@@ -2204,7 +2209,7 @@ badindex:
                         show2dwall[index>>3] &= ~(1<<(index&7));
                 }
 
-                vm.updatehighlight = 1;
+                vm.miscflags |= VMFLAG_MISC_UPDATEHL;
 
                 continue;
             }
@@ -2227,7 +2232,7 @@ badindex:
                 else
                     hlsectorbitmap[index>>3] &= ~(1<<(index&7));
 
-                vm.updatehighlightsector = 1;
+                vm.miscflags |= VMFLAG_MISC_UPDATEHLSECT;
 
                 continue;
             }
@@ -2368,8 +2373,9 @@ badindex:
             OSD_Printf("%s", ScriptQuotes[*insptr++]);
             continue;
 
-        case CON_GETNUMBER16:
-        case CON_GETNUMBER256:
+        case CON_GETNUMBER16:  /* deprecated */
+        case CON_GETNUMBER256:  /* deprecated */
+        case CON_GETNUMBERFROMUSER:
             insptr++;
             {
                 int32_t var=*insptr++, quote=*insptr++;
@@ -2378,18 +2384,24 @@ badindex:
                     continue;
 
                 {
-                    int32_t max=Gv_GetVarX(*insptr++), sign=(max<=0);
+                    int32_t max=Gv_GetVarX(*insptr++);
+                    int32_t sign = (tw==CON_GETNUMBERFROMUSER) ? Gv_GetVarX(*insptr++) : (max<=0);
                     char buf[64];  // buffers in getnumber* are 80 bytes long
 
-                    // no danger of accessing unallocated memory since we took care in C_SetScriptSize()
-                    Bmemcpy(buf, quotetext, sizeof(buf));
+                    Bstrncpy(buf, quotetext, sizeof(buf));
                     buf[sizeof(buf)-1]='\0';
 
                     if (max==0)
-                        max = INT_MAX;
+                        max = INT32_MAX;
 
 //OSD_Printf("max:%d, sign:%d\n", max, sign);
-                    if (tw==CON_GETNUMBER16)
+                    if (tw==CON_GETNUMBERFROMUSER)
+                    {
+                        Gv_SetVarX(var, (qsetmode==200) ?
+                                   getnumber256(quotetext, Gv_GetVarX(var), max, sign) :
+                                   getnumber16(quotetext, Gv_GetVarX(var), max, sign));
+                    }
+                    else if (tw==CON_GETNUMBER16)
                         Gv_SetVarX(var, getnumber16(quotetext, Gv_GetVarX(var), max, sign));
                     else
                         Gv_SetVarX(var, getnumber256(quotetext, Gv_GetVarX(var), max, sign));
@@ -2995,6 +3007,9 @@ dodefault:
         case CON_IFAIMINGSECTOR:
             VM_DoConditional(AIMING_AT_CEILING_OR_FLOOR);
             continue;
+        case CON_IFINTERACTIVE:
+            VM_DoConditional(vm.miscflags&VMFLAG_MISC_INTERACTIVE);
+            continue;
 
         case CON_GETSOUNDFLAGS:
             insptr++;