1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 2010 EDuke32 developers and contributors
4 
5 This file is part of EDuke32.
6 
7 EDuke32 is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License version 2
9 as published by the Free Software Foundation.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 
15 See the GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 */
21 //-------------------------------------------------------------------------
22 
23 // This object is shared by the editors of *all* Build games!
24 
25 #include "compat.h"
26 
27 #include "m32script.h"
28 #include "m32def.h"
29 
30 #include "sounds_mapster32.h"
31 
32 #include "osd.h"
33 #include "keys.h"
34 #include "common.h"
35 
36 #include "colmatch.h"
37 
38 // from macros.h
39 #define rnd(X) ((krand()>>8)>=(255-(X)))
40 
41 vmstate_t vm;
42 vmstate_t vm_default =
43 {
44     -1,   // g_i
45     0,    // g_st
46     NULL, // g_sp
47     0,    // flags
48     0,    // miscflags
49 };
50 
51 int32_t g_errorLineNum, g_tw;
52 
53 uint8_t aEventEnabled[MAXEVENTS];
54 
55 uint32_t m32_drawlinepat=0xffffffff;
56 int32_t m32_script_expertmode = 0;
57 
58 instype *insptr;
59 
60 static instype *x_sortingstateptr;
61 
62 //#include "m32structures.cpp"
63 
64 #ifdef DEBUGGINGAIDS
X_Disasm(ofstype beg,int32_t size)65 void X_Disasm(ofstype beg, int32_t size)
66 {
67     instype *p;
68 
69     if (!apScript) return;
70     if (beg<0 || beg+size>g_scriptSize) return;
71 
72     initprintf("beg=%d, size=%d:  ", beg, size);
73     for (p=apScript+beg; p<apScript+beg+size; p++)
74     {
75         if (*p>>12 && (*p&0xFFF)<CON_END)
76             initprintf("%s ", keyw[*p&0xFFF]);
77         else
78             initprintf("%d ", *p);
79     }
80     initprintf("\n");
81 }
82 #endif
83 
VM_ScriptInfo(void)84 void VM_ScriptInfo(void)
85 {
86     if (apScript)
87     {
88         instype *p;
89         if (insptr)
90             for (p=max(insptr-20,apScript); p<min(insptr+20, apScript+g_scriptSize); p++)
91             {
92                 if (p==insptr) initprintf("<<");
93 
94                 if (*p>>12 && (*p&0xFFF)<CON_END)
95                     initprintf("\n%5d: L%5d:  %s ",(int32_t)(p-apScript),(int32_t)(*p>>12),keyw[*p&0xFFF]);
96                 else initprintf(" %d",*p);
97 
98                 if (p==insptr) initprintf(">>");
99             }
100         initprintf(" \n");
101         if (vm.spriteNum >= 0)
102             initprintf("current sprite: %d\n",vm.spriteNum);
103         if (g_tw>=0 && g_tw<CON_END)
104             initprintf("g_errorLineNum: %d, g_tw: %s\n",g_errorLineNum,keyw[g_tw]);
105         else
106             initprintf("g_errorLineNum: %d, g_tw: %d\n",g_errorLineNum,g_tw);
107     }
108 }
109 
M32_PostScriptExec(void)110 void M32_PostScriptExec(void)
111 {
112     if (vm.miscflags&VMFLAG_MISC_UPDATEHL)
113     {
114         update_highlight();
115         vm.miscflags &= ~VMFLAG_MISC_UPDATEHL;
116     }
117 
118     if (vm.miscflags&VMFLAG_MISC_UPDATEHLSECT)
119     {
120         update_highlightsector();
121         if (!in3dmode())
122             ovh_whiteoutgrab(1);
123         vm.miscflags &= ~VMFLAG_MISC_UPDATEHLSECT;
124     }
125 }
126 
VM_OnEvent(int32_t iEventID,int32_t spriteNum)127 void VM_OnEvent(int32_t iEventID, int32_t spriteNum)
128 {
129     if (iEventID < 0 || iEventID >= MAXEVENTS)
130     {
131         M32_PRINTERROR("Invalid event ID");
132         return;
133     }
134 
135     if (aEventOffsets[iEventID] < 0 || !aEventEnabled[iEventID])
136     {
137         //Bsprintf(g_szBuf,"No event found for %d",iEventID);
138         //AddLog(g_szBuf);
139         return;
140     }
141 
142     {
143         instype *const oinsptr=insptr;
144         vmstate_t vm_backup;
145         void *const olocalvars = aGameArrays[M32_LOCAL_ARRAY_ID].vals;
146 #ifdef M32_LOCALS_VARARRAY
147         int32_t localvars[aEventNumLocals[iEventID]];
148 #else
149         int32_t localvars[M32_LOCALS_FIXEDNUM];
150 #endif
151 
152         // Initialize 'state'-local variables to 0.
153         if (aEventNumLocals[iEventID] > 0)
154             Bmemset(localvars, 0, aEventNumLocals[iEventID]*sizeof(int32_t));
155 
156         Bmemcpy(&vm_backup, &vm, sizeof(vmstate_t));
157 
158         vm.spriteNum = spriteNum;    // current sprite ID
159         if (vm.spriteNum >= 0)
160             vm.pSprite = &sprite[vm.spriteNum];
161 
162         vm.g_st = 1+iEventID;
163 
164         vm.flags = 0;
165 
166         insptr = apScript + aEventOffsets[iEventID];
167 
168         aGameArrays[M32_LOCAL_ARRAY_ID].vals = localvars;
169         VM_Execute(0);
170         aGameArrays[M32_LOCAL_ARRAY_ID].vals = olocalvars;
171 
172         if (vm.flags&VMFLAG_ERROR)
173         {
174             aEventEnabled[iEventID] = 0;
175             message("ERROR executing %s. Event disabled.", label+(iEventID*MAXLABELLEN));
176         }
177 
178         M32_PostScriptExec();
179 
180         // restore old values...
181         Bmemcpy(&vm, &vm_backup, sizeof(vmstate_t));
182         insptr = oinsptr;
183 
184         //AddLog("End of Execution");
185     }
186 }
187 
G_GetAngleDelta(int32_t a,int32_t na)188 static int32_t G_GetAngleDelta(int32_t a,int32_t na)
189 {
190     a &= 2047;
191     na &= 2047;
192 
193     if (klabs(a-na) < 1024)
194     {
195 //        OSD_Printf("G_GetAngleDelta() returning %d\n",na-a);
196         return na-a;
197     }
198 
199     if (na > 1024) na -= 2048;
200     if (a > 1024) a -= 2048;
201 
202 //    OSD_Printf("G_GetAngleDelta() returning %d\n",na-a);
203     return na-a;
204 }
205 
VM_DoConditional(int32_t condition)206 static inline void __fastcall VM_DoConditional(int32_t condition)
207 {
208     if (condition)
209     {
210         // skip 'else' pointer.. and...
211         insptr+=2;
212         VM_Execute(1);
213         return;
214     }
215 
216     insptr++;
217     insptr += *insptr;
218     if (((*insptr)&0xFFF) == CON_ELSE)
219     {
220         // else...
221         // skip 'else' and...
222         insptr+=2;
223         VM_Execute(1);
224     }
225 }
226 
X_DoSortDefault(const void * lv,const void * rv)227 static int X_DoSortDefault(const void *lv, const void *rv)
228 {
229     return *(int32_t const *)rv - *(int32_t const *)lv;
230 }
231 
X_DoSort(const void * lv,const void * rv)232 static int X_DoSort(const void *lv, const void *rv)
233 {
234     m32_sortvar1 = *(int32_t const *)lv;
235     m32_sortvar2 = *(int32_t const *)rv;
236     insptr = x_sortingstateptr;
237     VM_Execute(0);
238     return g_iReturnVar;
239 }
240 
241 // in interactive execution, allow the current sprite index to be the aimed-at sprite (in 3d mode)
242 #define X_ERROR_INVALIDCI()                                                                                                 \
243     if ((vm.spriteNum < 0 || vm.spriteNum >= MAXSPRITES) &&                                                                 \
244         (vm.g_st != 0 || searchstat != 3 || (vm.spriteNum = searchwall, vm.pSprite = &sprite[vm.spriteNum], 0)))            \
245     {                                                                                                                       \
246         M32_ERROR("Current sprite index invalid!");                                                                         \
247         continue;                                                                                                           \
248     }
249 
250 #define X_ERROR_INVALIDSPRI(dasprite)                                                                                       \
251     if (dasprite < 0 || dasprite >= MAXSPRITES)                                                                             \
252     {                                                                                                                       \
253         M32_ERROR("Invalid sprite index %d!", dasprite);                                                                    \
254         continue;                                                                                                           \
255     }
256 
257 #define X_ERROR_INVALIDSECT(dasect)                                                                                         \
258     if (dasect < 0 || dasect >= numsectors)                                                                                 \
259     {                                                                                                                       \
260         M32_ERROR("Invalid sector index %d!", dasect);                                                                      \
261         continue;                                                                                                           \
262     }
263 
264 #define X_ERROR_INVALIDSP()                                                                                                 \
265     if (!vm.pSprite && (vm.g_st != 0 || searchstat != 3 || (vm.pSprite = &sprite[searchwall], 0)))                          \
266     {                                                                                                                       \
267         M32_ERROR("Current sprite invalid!");                                                                               \
268         continue;                                                                                                           \
269     }
270 
271 #define X_ERROR_INVALIDQUOTE(q, array)                                                                                      \
272     if (q < 0 || q >= MAXQUOTES)                                                                                            \
273     {                                                                                                                       \
274         M32_ERROR("Invalid quote number %d!", q);                                                                           \
275         continue;                                                                                                           \
276     }                                                                                                                       \
277     else if (array[q] == NULL)                                                                                              \
278     {                                                                                                                       \
279         M32_ERROR("Null quote %d!", q);                                                                                     \
280         continue;                                                                                                           \
281     }
282 
GetMaybeInlineQuote(int32_t quotei)283 static char *GetMaybeInlineQuote(int32_t quotei)
284 {
285     char *quotetext;
286     if (quotei==-1)
287     {
288         quotetext = (char *)insptr;
289         while (*insptr++) /* skip the string */;
290     }
291     else
292     {
293         quotei = Gv_GetVar(quotei);
294         do { X_ERROR_INVALIDQUOTE(quotei, apStrings) } while (0);
295         if (vm.flags&VMFLAG_ERROR)
296             return NULL;
297         quotetext = apStrings[quotei];
298     }
299 
300     return quotetext;
301 }
302 
CheckArray(int aidx)303 static int CheckArray(int aidx)
304 {
305     if (!(aidx >= 0 && aidx < g_gameArrayCount))
306         M32_ERROR("Invalid array %d!", aidx);
307 
308     return (vm.flags&VMFLAG_ERROR);
309 }
310 
VM_Execute(int32_t once)311 int32_t VM_Execute(int32_t once)
312 {
313     int32_t tw = *insptr;
314 
315     // jump directly into the loop, saving us from the checks during the first iteration
316     goto skip_check;
317 
318     while (!once)
319     {
320         if (vm.flags)
321             return 1;
322 
323         tw = *insptr;
324 
325 skip_check:
326         //      Bsprintf(g_szBuf,"Parsing: %d",*insptr);
327         //      AddLog(g_szBuf);
328 
329         g_errorLineNum = tw>>12;
330         g_tw = (tw &= 0xFFF);
331 
332         switch (tw)
333         {
334 // *** basic commands
335         case CON_NULLOP:
336             insptr++;
337             continue;
338 
339         case CON_STATE:
340         {
341             instype *const tempscrptr = insptr+2;
342             const int32_t stateidx = *(insptr+1), o_g_st = vm.g_st, oret=vm.flags&VMFLAG_RETURN;
343             void *const olocalvars = aGameArrays[M32_LOCAL_ARRAY_ID].vals;
344 #ifdef M32_LOCALS_VARARRAY
345             int32_t localvars[statesinfo[stateidx].numlocals];
346 #else
347             int32_t localvars[M32_LOCALS_FIXEDNUM];
348 #endif
349 
350             // needed since any read access before initialization would cause undefined behaviour
351             if (statesinfo[stateidx].numlocals > 0)
352                 Bmemset(localvars, 0, statesinfo[stateidx].numlocals*sizeof(int32_t));
353 
354             insptr = apScript + statesinfo[stateidx].ofs;
355             vm.g_st = 1+MAXEVENTS+stateidx;
356             aGameArrays[M32_LOCAL_ARRAY_ID].vals = localvars;
357             VM_Execute(0);
358             aGameArrays[M32_LOCAL_ARRAY_ID].vals = olocalvars;
359             vm.g_st = o_g_st;
360             vm.flags &= ~VMFLAG_RETURN;
361             vm.flags |= oret;
362             insptr = tempscrptr;
363         }
364         continue;
365 
366         case CON_RETURN:
367             vm.flags |= VMFLAG_RETURN;
368             return 1;
369         case CON_BREAK:
370             vm.flags |= VMFLAG_BREAK;
371             // XXX: may not be cleared subsequently?
372             fallthrough__;
373         case CON_ENDS:
374             return 1;
375 
376         case CON_ELSE:
377             insptr++;
378             insptr += *insptr;
379             continue;
380 
381         case CON_ENDSWITCH:
382             vm.flags &= ~VMFLAG_BREAK;
383             fallthrough__;
384         case CON_ENDEVENT:
385             insptr++;
386             return 1;
387 
388         case CON_SWITCH:
389             insptr++; // p-code
390             {
391                 // command format:
392                 // variable ID to check
393                 // script offset to 'end'
394                 // count of case statements
395                 // script offset to default case (null if none)
396                 // For each case: value, ptr to code
397                 //AddLog("Processing Switch...");
398                 int32_t lValue=Gv_GetVar(*insptr++), lEnd=*insptr++, lCases=*insptr++;
399                 instype *lpDefault=insptr++, *lpCases=insptr, *lCodeInsPtr;
400                 int32_t bMatched=0, lCheckCase;
401                 int32_t left,right;
402 
403                 insptr += lCases*2;
404                 lCodeInsPtr = insptr;
405                 //Bsprintf(g_szBuf,"lEnd= %d *lpDefault=%d",lEnd,*lpDefault); AddLog(g_szBuf);
406                 //Bsprintf(g_szBuf,"Checking %d cases for %d",lCases, lValue); AddLog(g_szBuf);
407                 left = 0;
408                 right = lCases-1;
409                 while (!bMatched)
410                 {
411                     //Bsprintf(g_szBuf,"Checking #%d Value= %d",lCheckCase, lpCases[lCheckCase*2]); AddLog(g_szBuf);
412                     lCheckCase=(left+right)/2;
413                     //                initprintf("(%2d..%2d..%2d) [%2d..%2d..%2d]==%2d\n",left,lCheckCase,right,lpCases[left*2],lpCases[lCheckCase*2],lpCases[right*2],lValue);
414                     if (lpCases[lCheckCase*2] > lValue)
415                         right = lCheckCase-1;
416                     else if (lpCases[lCheckCase*2] < lValue)
417                         left = lCheckCase+1;
418                     else if (lpCases[lCheckCase*2] == lValue)
419                     {
420                         //AddLog("Found Case Match");
421                         //Bsprintf(g_szBuf,"insptr=%d. lCheckCase=%d, offset=%d, &script[0]=%d", (int32_t)insptr,(int32_t)lCheckCase,lpCases[lCheckCase*2+1],(int32_t)&script[0]); AddLog(g_szBuf);
422                         // fake a 2-d Array
423                         insptr = lCodeInsPtr + lpCases[lCheckCase*2+1];
424                         //Bsprintf(g_szBuf,"insptr=%d. ",     (int32_t)insptr); AddLog(g_szBuf);
425                         VM_Execute(0);
426                         //AddLog("Done Executing Case");
427                         bMatched=1;
428                     }
429 
430                     if (right-left < 0)
431                         break;
432                 }
433 
434                 if (!bMatched)
435                 {
436                     if (*lpDefault >= 0)
437                     {
438                         //AddLog("No Matching Case: Using Default");
439                         insptr = lCodeInsPtr + *lpDefault;
440                         VM_Execute(0);
441                     }
442 //                    else
443 //                    {
444 //                        //AddLog("No Matching Case: No Default to use");
445 //                    }
446                 }
447                 insptr = (instype *)(lCodeInsPtr + lEnd);
448                 vm.flags &= ~VMFLAG_BREAK;
449                 //Bsprintf(g_szBuf,"insptr=%d. ",     (int32_t)insptr); AddLog(g_szBuf);
450                 //AddLog("Done Processing Switch");
451                 continue;
452             }
453 
454         case CON_GETCURRADDRESS:
455             insptr++;
456             {
457                 int32_t j=*insptr++;
458                 Gv_SetVar(j, insptr-apScript);
459             }
460             continue;
461 
462         case CON_JUMP:
463             insptr++;
464             {
465                 int32_t j = Gv_GetVar(*insptr++);
466                 if (j<0 || j>=(g_scriptPtr-apScript))
467                 {
468                     M32_ERROR("script index out of bounds (%d)",  j);
469                     continue;
470                 }
471                 insptr = (instype *)(j+apScript);
472             }
473             continue;
474 
475         case CON_RIGHTBRACE:
476             insptr++;
477             return 1;
478         case CON_LEFTBRACE:
479             insptr++;
480             VM_Execute(0);
481             continue;
482 
483 // *** arrays
484         case CON_SETARRAY:
485             insptr++;
486             {
487                 const int32_t j=*insptr++;
488                 const int32_t index = Gv_GetVar(*insptr++);
489                 const int32_t value = Gv_GetVar(*insptr++);
490 
491                 CheckArray(j);
492 
493                 if (aGameArrays[j].dwFlags & GAMEARRAY_READONLY)
494                     M32_ERROR("Tried to set on read-only array `%s'", aGameArrays[j].szLabel);
495 
496                 if (!(index >= 0 && index < aGameArrays[j].size))
497                     M32_ERROR("Array index %d out of bounds", index);
498 
499                 if (vm.flags&VMFLAG_ERROR)
500                     continue;
501 
502                 // NOTE: Other array types not implemented, since they're read-only.
503                 ((int32_t *)aGameArrays[j].vals)[index] = value;
504                 continue;
505             }
506 
507         case CON_GETARRAYSIZE:
508             insptr++;
509             {
510                 const int32_t j=*insptr++;
511 
512                 if (CheckArray(j))
513                     continue;
514 
515                 Gv_SetVar(*insptr++, Gv_GetArraySize(j));
516             }
517             continue;
518 
519         case CON_RESIZEARRAY:
520             insptr++;
521             {
522                 const int32_t j=*insptr++;
523                 const int32_t asize = Gv_GetVar(*insptr++);
524 
525                 CheckArray(j);
526 
527                 if (aGameArrays[j].dwFlags & GAMEARRAY_READONLY)
528                     M32_ERROR("Tried to resize read-only array `%s'", aGameArrays[j].szLabel);
529 
530                 if (!(asize >= 1 && asize <= 65536))
531                     M32_ERROR("Invalid array size %d (must be between 1 and 65536)", asize);
532 
533                 if (vm.flags&VMFLAG_ERROR)
534                     continue;
535 
536 //                OSD_Printf(OSDTEXT_GREEN "CON_RESIZEARRAY: resizing array %s from %d to %d\n", aGameArrays[j].szLabel, aGameArrays[j].size, asize);
537                 aGameArrays[j].vals = Xrealloc(aGameArrays[j].vals, sizeof(int32_t) * asize);
538                 aGameArrays[j].size = asize;
539 
540                 continue;
541             }
542 
543         case CON_COPY:
544             insptr++;
545             {
546                 const int32_t si=*insptr++;
547                 int32_t sidx = Gv_GetVar(*insptr++);
548                 const int32_t di=*insptr++;
549                 int32_t didx = Gv_GetVar(*insptr++);
550                 int32_t numelts = Gv_GetVar(*insptr++);
551 
552                 CheckArray(si);
553                 CheckArray(di);
554 
555                 if (aGameArrays[di].dwFlags & GAMEARRAY_READONLY)
556                     M32_ERROR("Array %d is read-only!", di);
557                 if (vm.flags&VMFLAG_ERROR)
558                     continue;
559 
560                 const int32_t ssiz = Gv_GetArraySize(si);
561                 const int32_t dsiz = Gv_GetArraySize(di);
562 
563                 if ((uint32_t)sidx >= (uint32_t)ssiz)
564                     M32_ERROR("Invalid source index %d", sidx);
565                 if ((uint32_t)didx >= (uint32_t)dsiz)
566                     M32_ERROR("Invalid destination index %d", didx);
567                 if (vm.flags&VMFLAG_ERROR)
568                     continue;
569 
570                 if (numelts > ssiz-sidx)
571                     numelts = ssiz-sidx;
572                 if (numelts > dsiz-didx)
573                     numelts = dsiz-didx;
574 
575                 const gamearray_t *const sar = &aGameArrays[si];
576                 gamearray_t *const dar = &aGameArrays[di];
577 
578                 switch (sar->dwFlags & GAMEARRAY_TYPE_MASK)
579                 {
580                 case 0:
581                 case GAMEARRAY_INT32:
582                     if (sar->dwFlags & GAMEARRAY_STRIDE2)
583                     {
584                         for (; numelts>0; numelts--, sidx += 2)
585                             ((int32_t *)dar->vals)[didx++] = ((int32_t *)sar->vals)[sidx];
586                     }
587                     else
588                     {
589                         Bmemcpy((int32_t *)dar->vals + didx, (int32_t *)sar->vals + sidx,
590                                 numelts * sizeof(int32_t));
591                     }
592                     break;
593                 case GAMEARRAY_INT16:
594                     for (; numelts>0; numelts--)
595                         ((int32_t *)dar->vals)[didx++] = ((int16_t *)sar->vals)[sidx++];
596                     break;
597                 case GAMEARRAY_UINT8:
598                     for (; numelts>0; numelts--)
599                         ((int32_t *)dar->vals)[didx++] = ((uint8_t *)sar->vals)[sidx++];
600                     break;
601                 }
602                 continue;
603             }
604 
605 // *** var & varvar ops
606         case CON_RANDVAR:
607             insptr++;
608             Gv_SetVar(*insptr, mulscale16(krand(), *(insptr+1)+1));
609             insptr += 2;
610             continue;
611 
612         case CON_DISPLAYRANDVAR:
613             insptr++;
614             Gv_SetVar(*insptr, mulscale15(system_15bit_rand(), *(insptr+1)+1));
615             insptr += 2;
616             continue;
617 
618         case CON_SETVAR:
619             insptr++;
620             Gv_SetVar(*insptr, *(insptr+1));
621             insptr += 2;
622             continue;
623 
624         case CON_SETVARVAR:
625             insptr++;
626             {
627                 int32_t j=*insptr++;
628                 Gv_SetVar(j, Gv_GetVar(*insptr++));
629             }
630             continue;
631 
632         case CON_MULVAR:
633             insptr++;
634             Gv_SetVar(*insptr, Gv_GetVar(*insptr) * *(insptr+1));
635             insptr += 2;
636             continue;
637 
638         case CON_DIVVAR:
639             insptr++;
640             if (*(insptr+1) == 0)
641             {
642                 M32_ERROR("Divide by zero.");
643                 insptr += 2;
644                 continue;
645             }
646             Gv_SetVar(*insptr, Gv_GetVar(*insptr) / *(insptr+1));
647             insptr += 2;
648             continue;
649 
650         case CON_MODVAR:
651             insptr++;
652             if (*(insptr+1) == 0)
653             {
654                 M32_ERROR("Mod by zero.");
655                 insptr += 2;
656                 continue;
657             }
658             Gv_SetVar(*insptr,Gv_GetVar(*insptr)%*(insptr+1));
659             insptr += 2;
660             continue;
661 
662         case CON_ANDVAR:
663             insptr++;
664             Gv_SetVar(*insptr,Gv_GetVar(*insptr) & *(insptr+1));
665             insptr += 2;
666             continue;
667 
668         case CON_ORVAR:
669             insptr++;
670             Gv_SetVar(*insptr,Gv_GetVar(*insptr) | *(insptr+1));
671             insptr += 2;
672             continue;
673 
674         case CON_XORVAR:
675             insptr++;
676             Gv_SetVar(*insptr,Gv_GetVar(*insptr) ^ *(insptr+1));
677             insptr += 2;
678             continue;
679 
680         case CON_RANDVARVAR:
681             insptr++;
682             {
683                 int32_t j=*insptr++;
684                 Gv_SetVar(j,mulscale16(krand(), Gv_GetVar(*insptr++)+1));
685             }
686             continue;
687 
688         case CON_DISPLAYRANDVARVAR:
689             insptr++;
690             {
691                 int32_t j=*insptr++;
692                 Gv_SetVar(j,mulscale15(system_15bit_rand(), Gv_GetVar(*insptr++)+1));
693             }
694             continue;
695 
696         case CON_MULVARVAR:
697             insptr++;
698             {
699                 int32_t j=*insptr++;
700                 Gv_SetVar(j, Gv_GetVar(j)*Gv_GetVar(*insptr++));
701             }
702             continue;
703 
704         case CON_DIVVARVAR:
705             insptr++;
706             {
707                 int32_t j=*insptr++;
708                 int32_t l2=Gv_GetVar(*insptr++);
709 
710                 if (l2==0)
711                 {
712                     M32_ERROR("Divide by zero.");
713                     continue;
714                 }
715                 Gv_SetVar(j, Gv_GetVar(j)/l2);
716                 continue;
717             }
718 
719         case CON_MODVARVAR:
720             insptr++;
721             {
722                 int32_t j=*insptr++;
723                 int32_t l2=Gv_GetVar(*insptr++);
724 
725                 if (l2==0)
726                 {
727                     M32_ERROR("Mod by zero.");
728                     continue;
729                 }
730 
731                 Gv_SetVar(j, Gv_GetVar(j) % l2);
732                 continue;
733             }
734 
735         case CON_ANDVARVAR:
736             insptr++;
737             {
738                 int32_t j=*insptr++;
739                 Gv_SetVar(j, Gv_GetVar(j) & Gv_GetVar(*insptr++));
740             }
741             continue;
742 
743         case CON_XORVARVAR:
744             insptr++;
745             {
746                 int32_t j=*insptr++;
747                 Gv_SetVar(j, Gv_GetVar(j) ^ Gv_GetVar(*insptr++));
748             }
749             continue;
750 
751         case CON_ORVARVAR:
752             insptr++;
753             {
754                 int32_t j=*insptr++;
755                 Gv_SetVar(j, Gv_GetVar(j) | Gv_GetVar(*insptr++));
756             }
757             continue;
758 
759         case CON_SUBVAR:
760             insptr++;
761             Gv_SetVar(*insptr, Gv_GetVar(*insptr) - *(insptr+1));
762             insptr += 2;
763             continue;
764 
765         case CON_SUBVARVAR:
766             insptr++;
767             {
768                 int32_t j=*insptr++;
769                 Gv_SetVar(j, Gv_GetVar(j) - Gv_GetVar(*insptr++));
770             }
771             continue;
772 
773         case CON_ADDVAR:
774             insptr++;
775             Gv_SetVar(*insptr, Gv_GetVar(*insptr) + *(insptr+1));
776             insptr += 2;
777             continue;
778 
779         case CON_ADDVARVAR:
780             insptr++;
781             {
782                 int32_t j=*insptr++;
783                 Gv_SetVar(j, Gv_GetVar(j) + Gv_GetVar(*insptr++));
784             }
785             continue;
786 
787         case CON_SHIFTVARL:
788             insptr++;
789             Gv_SetVar(*insptr, Gv_GetVar(*insptr) << *(insptr+1));
790             insptr += 2;
791             continue;
792 
793         case CON_SHIFTVARVARL:
794             insptr++;
795             {
796                 int32_t j=*insptr++;
797                 Gv_SetVar(j, Gv_GetVar(j) << Gv_GetVar(*insptr++));
798             }
799             continue;
800 
801         case CON_SHIFTVARR:
802             insptr++;
803             Gv_SetVar(*insptr, Gv_GetVar(*insptr) >> *(insptr+1));
804             insptr += 2;
805             continue;
806 
807         case CON_SHIFTVARVARR:
808             insptr++;
809             {
810                 int32_t j=*insptr++;
811                 Gv_SetVar(j, Gv_GetVar(j) >> Gv_GetVar(*insptr++));
812             }
813             continue;
814 
815         case CON_SIN:
816             insptr++;
817             Gv_SetVar(*insptr, sintable[Gv_GetVar(*(insptr+1))&2047]);
818             insptr += 2;
819             continue;
820 
821         case CON_COS:
822             insptr++;
823             Gv_SetVar(*insptr, sintable[(Gv_GetVar(*(insptr+1))+512)&2047]);
824             insptr += 2;
825             continue;
826 
827         case CON_DISPLAYRAND:
828             insptr++;
829             Gv_SetVar(*insptr++, system_15bit_rand());
830             continue;
831 
832 // *** other math
833         case CON_FTOI:
834             insptr++;
835             {
836                 union { int32_t ival; float fval; };
837 
838                 ival=Gv_GetVar(*insptr);
839                 int32_t const scale=*(insptr+1);
840 // rounding must absolutely be!
841 //OSD_Printf("ftoi: bits:%8x, scale=%d, fval=%f, (int32_t)(fval*scale)=%d\n", bits, scale, fval, (int32_t)(fval*scale));
842                 Gv_SetVar(*insptr, (int32_t)Blrintf(fval * scale));
843             }
844             insptr += 2;
845             continue;
846 
847         case CON_ITOF:
848             insptr++;
849             {
850                 union { int32_t ival; float fval; };
851 
852                 ival=Gv_GetVar(*insptr);
853                 int32_t const scale=*(insptr+1);
854                 fval = (float)ival/(float)scale;
855                 Gv_SetVar(*insptr, ival);
856             }
857             insptr += 2;
858             continue;
859 
860         case CON_CLAMP:
861             insptr++;
862             {
863                 int32_t var=*insptr++, min=Gv_GetVar(*insptr++), max=Gv_GetVar(*insptr++);
864                 int32_t val=Gv_GetVar(var);
865 
866                 if (val<min) Gv_SetVar(var, min);
867                 else if (val>max) Gv_SetVar(var, max);
868             }
869             continue;
870 
871         case CON_INV:
872             Gv_SetVar(*(insptr+1), -Gv_GetVar(*(insptr+1)));
873             insptr += 2;
874             continue;
875 
876         case CON_SQRT:
877             insptr++;
878             {
879                 // syntax sqrt <invar> <outvar>
880                 int32_t lInVarID=*insptr++, lOutVarID=*insptr++;
881 
882                 Gv_SetVar(lOutVarID, ksqrt((uint32_t)Gv_GetVar(lInVarID)));
883                 continue;
884             }
885 
886         case CON_LDIST:
887         case CON_DIST:
888             insptr++;
889             {
890                 int32_t distvar = *insptr++, xvar = Gv_GetVar(*insptr++), yvar = Gv_GetVar(*insptr++);
891 
892                 if (xvar < 0 || xvar >= MAXSPRITES || sprite[xvar].statnum==MAXSTATUS)
893                 {
894                     M32_ERROR("invalid sprite %d", xvar);
895                 }
896                 if (yvar < 0 || yvar >= MAXSPRITES || sprite[yvar].statnum==MAXSTATUS)
897                 {
898                     M32_ERROR("invalid sprite %d", yvar);
899                 }
900                 if (vm.flags&VMFLAG_ERROR) continue;
901 
902                 if (tw==CON_DIST)
903                     Gv_SetVar(distvar, dist(&sprite[xvar],&sprite[yvar]));
904                 else
905                     Gv_SetVar(distvar, ldist(&sprite[xvar],&sprite[yvar]));
906                 continue;
907             }
908 
909         case CON_GETANGLE:
910             insptr++;
911             {
912                 int32_t angvar = *insptr++;
913                 int32_t xvar = Gv_GetVar(*insptr++);
914                 int32_t yvar = Gv_GetVar(*insptr++);
915 
916                 Gv_SetVar(angvar, getangle(xvar,yvar));
917                 continue;
918             }
919 
920         case CON_GETINCANGLE:
921             insptr++;
922             {
923                 int32_t angvar = *insptr++;
924                 int32_t xvar = Gv_GetVar(*insptr++);
925                 int32_t yvar = Gv_GetVar(*insptr++);
926 
927                 Gv_SetVar(angvar, G_GetAngleDelta(xvar,yvar));
928                 continue;
929             }
930 
931         case CON_A2XY:
932         case CON_AH2XYZ:
933             insptr++;
934             {
935                 int32_t ang=Gv_GetVar(*insptr++), horiz=(tw==CON_A2XY)?100:Gv_GetVar(*insptr++);
936                 int32_t xvar=*insptr++, yvar=*insptr++;
937 
938                 int32_t x = sintable[(ang+512)&2047];
939                 int32_t y = sintable[ang&2047];
940 
941                 if (tw==CON_AH2XYZ)
942                 {
943                     int32_t zvar=*insptr++, z=0;
944 
945                     horiz -= 100;
946                     if (horiz)
947                     {
948                         int32_t veclen = ksqrt(200*200 + horiz*horiz);
949                         int32_t dacos = divscale14(200, veclen);
950 
951                         x = mulscale14(x, dacos);
952                         y = mulscale14(y, dacos);
953                         z = divscale14(-horiz, veclen);
954                     }
955 
956                     Gv_SetVar(zvar, z);
957                 }
958 
959                 Gv_SetVar(xvar, x);
960                 Gv_SetVar(yvar, y);
961 
962                 continue;
963             }
964 
965         case CON_MULSCALE:
966             insptr++;
967             {
968                 int32_t var1 = *insptr++, var2 = Gv_GetVar(*insptr++);
969                 int32_t var3 = Gv_GetVar(*insptr++), var4 = Gv_GetVar(*insptr++);
970 
971                 Gv_SetVar(var1, mulscale(var2, var3, var4));
972                 continue;
973             }
974         case CON_DIVSCALE:
975             insptr++;
976             {
977                 int32_t var1 = *insptr++, var2 = Gv_GetVar(*insptr++);
978                 int32_t var3 = Gv_GetVar(*insptr++), var4 = Gv_GetVar(*insptr++);
979 
980                 Gv_SetVar(var1, divscale(var2, var3, var4));
981                 continue;
982             }
983         case CON_SCALEVAR:
984             insptr++;
985             {
986                 int32_t var1 = *insptr++, var2 = Gv_GetVar(*insptr++);
987                 int32_t var3 = Gv_GetVar(*insptr++), var4 = Gv_GetVar(*insptr++);
988 
989                 Gv_SetVar(var1, scale(var2, var3, var4));
990                 continue;
991             }
992 
993 // *** if & while
994         case CON_IFVARVARAND:
995             insptr++;
996             {
997                 int32_t j = Gv_GetVar(*insptr++);
998                 j &= Gv_GetVar(*insptr++);
999                 insptr--;
1000                 VM_DoConditional(j);
1001             }
1002             continue;
1003 
1004         case CON_IFVARVAROR:
1005             insptr++;
1006             {
1007                 int32_t j = Gv_GetVar(*insptr++);
1008                 j |= Gv_GetVar(*insptr++);
1009                 insptr--;
1010                 VM_DoConditional(j);
1011             }
1012             continue;
1013 
1014         case CON_IFVARVARXOR:
1015             insptr++;
1016             {
1017                 int32_t j = Gv_GetVar(*insptr++);
1018                 j ^= Gv_GetVar(*insptr++);
1019                 insptr--;
1020                 VM_DoConditional(j);
1021             }
1022             continue;
1023 
1024         case CON_IFVARVAREITHER:
1025             insptr++;
1026             {
1027                 int32_t j = Gv_GetVar(*insptr++);
1028                 int32_t l = Gv_GetVar(*insptr++);
1029                 insptr--;
1030                 VM_DoConditional(j || l);
1031             }
1032             continue;
1033 
1034         case CON_IFVARVARBOTH:
1035             insptr++;
1036             {
1037                 int32_t j = Gv_GetVar(*insptr++);
1038                 int32_t l = Gv_GetVar(*insptr++);
1039                 insptr--;
1040                 VM_DoConditional(j && l);
1041             }
1042             continue;
1043 
1044         case CON_IFVARVARN:
1045             insptr++;
1046             {
1047                 int32_t j = Gv_GetVar(*insptr++);
1048                 j = (j != Gv_GetVar(*insptr++));
1049                 insptr--;
1050                 VM_DoConditional(j);
1051             }
1052             continue;
1053 
1054         case CON_IFVARVARE:
1055             insptr++;
1056             {
1057                 int32_t j = Gv_GetVar(*insptr++);
1058                 j = (j == Gv_GetVar(*insptr++));
1059                 insptr--;
1060                 VM_DoConditional(j);
1061             }
1062             continue;
1063 
1064         case CON_IFVARVARG:
1065             insptr++;
1066             {
1067                 int32_t j = Gv_GetVar(*insptr++);
1068                 j = (j > Gv_GetVar(*insptr++));
1069                 insptr--;
1070                 VM_DoConditional(j);
1071             }
1072             continue;
1073 
1074         case CON_IFVARVARGE:
1075             insptr++;
1076             {
1077                 int32_t j = Gv_GetVar(*insptr++);
1078                 j = (j >= Gv_GetVar(*insptr++));
1079                 insptr--;
1080                 VM_DoConditional(j);
1081             }
1082             continue;
1083 
1084         case CON_IFVARVARL:
1085             insptr++;
1086             {
1087                 int32_t j = Gv_GetVar(*insptr++);
1088                 j = (j < Gv_GetVar(*insptr++));
1089                 insptr--;
1090                 VM_DoConditional(j);
1091             }
1092             continue;
1093 
1094         case CON_IFVARVARLE:
1095             insptr++;
1096             {
1097                 int32_t j = Gv_GetVar(*insptr++);
1098                 j = (j <= Gv_GetVar(*insptr++));
1099                 insptr--;
1100                 VM_DoConditional(j);
1101             }
1102             continue;
1103 
1104         case CON_IFVARVARA:
1105             insptr++;
1106             {
1107                 int32_t j = Gv_GetVar(*insptr++);
1108                 j = ((uint32_t)j > (uint32_t)Gv_GetVar(*insptr++));
1109                 insptr--;
1110                 VM_DoConditional(j);
1111             }
1112             continue;
1113 
1114         case CON_IFVARVARAE:
1115             insptr++;
1116             {
1117                 int32_t j = Gv_GetVar(*insptr++);
1118                 j = ((uint32_t)j >= (uint32_t)Gv_GetVar(*insptr++));
1119                 insptr--;
1120                 VM_DoConditional(j);
1121             }
1122             continue;
1123 
1124         case CON_IFVARVARB:
1125             insptr++;
1126             {
1127                 int32_t j = Gv_GetVar(*insptr++);
1128                 j = ((uint32_t)j < (uint32_t)Gv_GetVar(*insptr++));
1129                 insptr--;
1130                 VM_DoConditional(j);
1131             }
1132             continue;
1133 
1134         case CON_IFVARVARBE:
1135             insptr++;
1136             {
1137                 int32_t j = Gv_GetVar(*insptr++);
1138                 j = ((uint32_t)j <= (uint32_t)Gv_GetVar(*insptr++));
1139                 insptr--;
1140                 VM_DoConditional(j);
1141             }
1142             continue;
1143 
1144         case CON_IFVARE:
1145             insptr++;
1146             {
1147                 int32_t j=Gv_GetVar(*insptr++);
1148                 VM_DoConditional(j == *insptr);
1149             }
1150             continue;
1151 
1152         case CON_IFVARN:
1153             insptr++;
1154             {
1155                 int32_t j=Gv_GetVar(*insptr++);
1156                 VM_DoConditional(j != *insptr);
1157             }
1158             continue;
1159 
1160         case CON_WHILEVARN:
1161         {
1162             instype *savedinsptr=insptr+2;
1163             int32_t j;
1164             do
1165             {
1166                 insptr=savedinsptr;
1167                 j = (Gv_GetVar(*(insptr-1)) != *insptr);
1168                 VM_DoConditional(j);
1169             }
1170             while (j && !vm.flags);
1171             vm.flags &= ~VMFLAG_BREAK;
1172             continue;
1173         }
1174 
1175         case CON_WHILEVARL:
1176         {
1177             instype *savedinsptr=insptr+2;
1178             int32_t j;
1179             do
1180             {
1181                 insptr=savedinsptr;
1182                 j = (Gv_GetVar(*(insptr-1)) < *insptr);
1183                 VM_DoConditional(j);
1184             }
1185             while (j && !vm.flags);
1186             vm.flags &= ~VMFLAG_BREAK;
1187             continue;
1188         }
1189 
1190         case CON_WHILEVARVARN:
1191         {
1192             int32_t j;
1193             instype *savedinsptr=insptr+2;
1194             do
1195             {
1196                 insptr=savedinsptr;
1197                 j = Gv_GetVar(*(insptr-1));
1198                 j = (j != Gv_GetVar(*insptr++));
1199                 insptr--;
1200                 VM_DoConditional(j);
1201             }
1202             while (j && !vm.flags);
1203             vm.flags &= ~VMFLAG_BREAK;
1204             continue;
1205         }
1206 
1207         case CON_WHILEVARVARL:
1208         {
1209             int32_t j;
1210             instype *savedinsptr=insptr+2;
1211             do
1212             {
1213                 insptr=savedinsptr;
1214                 j = Gv_GetVar(*(insptr-1));
1215                 j = (j < Gv_GetVar(*insptr++));
1216                 insptr--;
1217                 VM_DoConditional(j);
1218             }
1219             while (j && !vm.flags);
1220             vm.flags &= ~VMFLAG_BREAK;
1221             continue;
1222         }
1223 
1224         case CON_COLLECTSECTORS:
1225             insptr++;
1226             {
1227                 const int32_t aridx=*insptr++, startsectnum=Gv_GetVar(*insptr++);
1228                 const int32_t numsectsVar=*insptr++, state=*insptr++;
1229 
1230                 if (CheckArray(aridx))
1231                     continue;
1232 
1233                 gamearray_t *const gar = &aGameArrays[aridx];
1234                 Bassert((gar->dwFlags & (GAMEARRAY_READONLY|GAMEARRAY_VARSIZE)) == 0);
1235 
1236                 const int32_t o_g_st=vm.g_st, arsize = gar->size;
1237                 instype *const end=insptr;
1238                 int16_t sectcnt, numsects=0;
1239 
1240                 // XXX: relies on -fno-strict-aliasing
1241                 int16_t *const sectlist = (int16_t *)gar->vals;  // actually an int32_t array
1242                 int32_t *const sectlist32 = (int32_t *)sectlist;
1243 
1244                 int32_t j, startwall, endwall, ns;
1245                 static uint8_t sectbitmap[(MAXSECTORS+7)>>3];
1246 
1247                 X_ERROR_INVALIDSECT(startsectnum);
1248                 if (arsize < numsectors)
1249                 {
1250                     M32_ERROR("Array size must be at least numsectors (=%d) for collecting!",
1251                               numsectors);
1252                     continue;
1253                 }
1254 
1255                 // collect!
1256                 bfirst_search_init(sectlist, sectbitmap, &numsects, MAXSECTORS, startsectnum);
1257 
1258                 for (sectcnt=0; sectcnt<numsects; sectcnt++)
1259                     for (WALLS_OF_SECTOR(sectlist[sectcnt], j))
1260                         if ((ns=wall[j].nextsector) >= 0 && wall[j].nextsector<numsectors)
1261                         {
1262                             if (sectbitmap[ns>>3]&pow2char[ns&7])
1263                                 continue;
1264                             vm.g_st = 1+MAXEVENTS+state;
1265                             insptr = apScript + statesinfo[state].ofs;
1266                             g_iReturnVar = ns;
1267                             VM_Execute(0);
1268                             if (g_iReturnVar)
1269                                 bfirst_search_try(sectlist, sectbitmap, &numsects, wall[j].nextsector);
1270                         }
1271 
1272                 // short->int sector list
1273                 for (j=numsects-1; j>=0; j--)
1274                     sectlist32[j] = sectlist[j];
1275 
1276                 Gv_SetVar(numsectsVar, numsects);
1277                 g_iReturnVar = 0;
1278 
1279                 // restore some VM state
1280                 vm.g_st = o_g_st;
1281                 insptr = end;
1282             }
1283             continue;
1284 
1285         case CON_SORT:
1286             insptr++;
1287             {
1288                 const int32_t aridx=*insptr++, count=Gv_GetVar(*insptr++), state=*insptr++;
1289                 const int32_t o_g_st = vm.g_st;
1290                 instype *const end = insptr;
1291 
1292                 if (CheckArray(aridx))
1293                     continue;
1294 
1295                 if (count <= 0)
1296                     continue;
1297 
1298                 gamearray_t *const gar = &aGameArrays[aridx];
1299                 Bassert((gar->dwFlags & (GAMEARRAY_READONLY|GAMEARRAY_VARSIZE)) == 0);
1300 
1301                 if (count > gar->size)
1302                 {
1303                     M32_ERROR("Count of elements to sort (%d) exceeds array size (%d)!",
1304                               count, gar->size);
1305                     continue;
1306                 }
1307 
1308                 if (state < 0)
1309                 {
1310                     qsort(gar->vals, count, sizeof(int32_t), X_DoSortDefault);
1311                 }
1312                 else
1313                 {
1314                     x_sortingstateptr = apScript + statesinfo[state].ofs;
1315                     vm.g_st = 1+MAXEVENTS+state;
1316                     qsort(gar->vals, count, sizeof(int32_t), X_DoSort);
1317                     vm.g_st = o_g_st;
1318                     insptr = end;
1319                 }
1320             }
1321             continue;
1322 
1323         case CON_FOR:  // special-purpose iteration
1324             insptr++;
1325             {
1326                 const int32_t var = *insptr++, how = *insptr++;
1327                 const int32_t parm2 = how<=ITER_DRAWNSPRITES ? 0 : Gv_GetVar(*insptr++);
1328                 instype *const end = insptr + *insptr, *const beg = ++insptr;
1329                 const int32_t vm_i_bak = vm.spriteNum;
1330                 auto const vm_sp_bak = vm.pUSprite;
1331 
1332                 if (vm.flags&VMFLAG_ERROR)
1333                     continue;
1334 
1335                 switch (how)
1336                 {
1337                 case ITER_ALLSPRITES:
1338                     for (bssize_t jj=0; jj<MAXSPRITES && !vm.flags; jj++)
1339                     {
1340                         if (sprite[jj].statnum == MAXSTATUS)
1341                             continue;
1342                         Gv_SetVar(var, jj);
1343                         vm.spriteNum = jj;
1344                         vm.pSprite = &sprite[jj];
1345                         insptr = beg;
1346                         VM_Execute(1);
1347                     }
1348                     break;
1349                 case ITER_ALLSECTORS:
1350                     for (bssize_t jj=0; jj<numsectors && !vm.flags; jj++)
1351                     {
1352                         Gv_SetVar(var, jj);
1353                         insptr = beg;
1354                         VM_Execute(1);
1355                     }
1356                     break;
1357                 case ITER_ALLWALLS:
1358                     for (bssize_t jj=0; jj<numwalls && !vm.flags; jj++)
1359                     {
1360                         Gv_SetVar(var, jj);
1361                         insptr = beg;
1362                         VM_Execute(1);
1363                     }
1364                     break;
1365                 case ITER_ACTIVELIGHTS:
1366 #ifdef POLYMER
1367                     for (bssize_t jj=0; jj<PR_MAXLIGHTS; jj++)
1368                     {
1369                         if (!prlights[jj].flags.active)
1370                             continue;
1371 
1372                         Gv_SetVar(var, jj);
1373                         insptr = beg;
1374                         VM_Execute(1);
1375                     }
1376 #else
1377                     M32_ERROR("Polymer not compiled in, iteration over lights forbidden.");
1378 #endif
1379                     break;
1380 
1381                 case ITER_SELSPRITES:
1382                     for (bssize_t ii=0; ii<highlightcnt && !vm.flags; ii++)
1383                     {
1384                         int jj = highlight[ii];
1385                         if (jj&0xc000)
1386                         {
1387                             jj &= (MAXSPRITES-1);
1388                             Gv_SetVar(var, jj);
1389                             vm.spriteNum = jj;
1390                             vm.pSprite = &sprite[jj];
1391                             insptr = beg;
1392                             VM_Execute(1);
1393                         }
1394                     }
1395                     break;
1396                 case ITER_SELSECTORS:
1397                     for (bssize_t ii=0; ii<highlightsectorcnt && !vm.flags; ii++)
1398                     {
1399                         int jj=highlightsector[ii];
1400                         Gv_SetVar(var, jj);
1401                         insptr = beg;
1402                         VM_Execute(1);
1403                     }
1404                     break;
1405                 case ITER_SELWALLS:
1406                     for (bssize_t ii=0; ii<highlightcnt && !vm.flags; ii++)
1407                     {
1408                         int jj=highlight[ii];
1409                         if (jj&0xc000)
1410                             continue;
1411                         Gv_SetVar(var, jj);
1412                         insptr = beg;
1413                         VM_Execute(1);
1414                     }
1415                     break;
1416                 case ITER_DRAWNSPRITES:
1417                 {
1418                     uspritetype lastSpriteBackup;
1419                     auto const lastSpritePtr = (uspritetype *)&sprite[MAXSPRITES-1];
1420 
1421                     // Back up sprite MAXSPRITES-1.
1422                     Bmemcpy(&lastSpriteBackup, lastSpritePtr, sizeof(uspritetype));
1423 
1424                     EDUKE32_STATIC_ASSERT(sizeof(uspritetype) == sizeof(tspritetype)); // see TSPRITE_SIZE
1425                     for (bssize_t ii=0; ii<spritesortcnt && !vm.flags; ii++)
1426                     {
1427                         vm.pUSprite = lastSpritePtr;
1428                         Bmemcpy(lastSpritePtr, &tsprite[ii], sizeof(tspritetype));
1429 
1430                         Gv_SetVar(var, ii);
1431                         insptr = beg;
1432                         VM_Execute(1);
1433 
1434                         // Copy over potentially altered tsprite.
1435                         Bmemcpy(&tsprite[ii], lastSpritePtr, sizeof(tspritetype));
1436                     }
1437 
1438                     // Restore sprite MAXSPRITES-1.
1439                     Bmemcpy(lastSpritePtr, &lastSpriteBackup, sizeof(uspritetype));
1440                     break;
1441                 }
1442                 case ITER_SPRITESOFSECTOR:
1443                     if (parm2 < 0 || parm2 >= MAXSECTORS)
1444                         goto badindex;
1445                     for (bssize_t jj=headspritesect[parm2]; jj>=0 && !vm.flags; jj=nextspritesect[jj])
1446                     {
1447                         Gv_SetVar(var, jj);
1448                         vm.spriteNum = jj;
1449                         vm.pSprite = &sprite[jj];
1450                         insptr = beg;
1451                         VM_Execute(1);
1452                     }
1453                     break;
1454                 case ITER_WALLSOFSECTOR:
1455                     if (parm2 < 0 || parm2 >= MAXSECTORS)
1456                         goto badindex;
1457                     for (bssize_t jj=sector[parm2].wallptr, endwall=jj+sector[parm2].wallnum-1;
1458                             jj<=endwall && !vm.flags; jj++)
1459                     {
1460                         Gv_SetVar(var, jj);
1461                         insptr = beg;
1462                         VM_Execute(1);
1463                     }
1464                     break;
1465                 case ITER_LOOPOFWALL:
1466                     if (parm2 < 0 || parm2 >= numwalls)
1467                         goto badindex;
1468                     {
1469                         int jj = parm2;
1470                         do
1471                         {
1472                             Gv_SetVar(var, jj);
1473                             insptr = beg;
1474                             VM_Execute(1);
1475                             jj = wall[jj].point2;
1476                         }
1477                         while (jj != parm2 && !vm.flags);
1478                     }
1479                     break;
1480                 case ITER_RANGE:
1481                     for (bssize_t jj=0; jj<parm2 && !vm.flags; jj++)
1482                     {
1483                         Gv_SetVar(var, jj);
1484                         insptr = beg;
1485                         VM_Execute(1);
1486                     }
1487                     break;
1488                 default:
1489                     M32_ERROR("Unknown iteration type %d!", how);
1490                     continue;
1491 badindex:
1492                     OSD_Printf("%sLine %d, %s %s: index %d out of range!\n", osd->draw.highlight,
1493                         g_errorLineNum,keyw[g_tw], iter_tokens[how].token, parm2);
1494                     vm.flags |= VMFLAG_ERROR;
1495                     continue;
1496                 }
1497                 vm.spriteNum = vm_i_bak;
1498                 vm.pUSprite  = vm_sp_bak;
1499                 vm.flags     &= ~VMFLAG_BREAK;
1500                 insptr       = end;
1501             }
1502             continue;
1503 
1504         case CON_IFVARAND:
1505             insptr++;
1506             {
1507                 int32_t j=Gv_GetVar(*insptr++);
1508                 VM_DoConditional(j & *insptr);
1509             }
1510             continue;
1511 
1512         case CON_IFVAROR:
1513             insptr++;
1514             {
1515                 int32_t j=Gv_GetVar(*insptr++);
1516                 VM_DoConditional(j | *insptr);
1517             }
1518             continue;
1519 
1520         case CON_IFVARXOR:
1521             insptr++;
1522             {
1523                 int32_t j=Gv_GetVar(*insptr++);
1524                 VM_DoConditional(j ^ *insptr);
1525             }
1526             continue;
1527 
1528         case CON_IFVAREITHER:
1529             insptr++;
1530             {
1531                 int32_t j=Gv_GetVar(*insptr++);
1532                 VM_DoConditional(j || *insptr);
1533             }
1534             continue;
1535 
1536         case CON_IFVARBOTH:
1537             insptr++;
1538             {
1539                 int32_t j=Gv_GetVar(*insptr++);
1540                 VM_DoConditional(j && *insptr);
1541             }
1542             continue;
1543 
1544         case CON_IFVARG:
1545             insptr++;
1546             {
1547                 int32_t j=Gv_GetVar(*insptr++);
1548                 VM_DoConditional(j > *insptr);
1549             }
1550             continue;
1551 
1552         case CON_IFVARGE:
1553             insptr++;
1554             {
1555                 int32_t j=Gv_GetVar(*insptr++);
1556                 VM_DoConditional(j >= *insptr);
1557             }
1558             continue;
1559 
1560         case CON_IFVARL:
1561             insptr++;
1562             {
1563                 int32_t j=Gv_GetVar(*insptr++);
1564                 VM_DoConditional(j < *insptr);
1565             }
1566             continue;
1567 
1568         case CON_IFVARLE:
1569             insptr++;
1570             {
1571                 int32_t j=Gv_GetVar(*insptr++);
1572                 VM_DoConditional(j <= *insptr);
1573             }
1574             continue;
1575 
1576         case CON_IFVARA:
1577             insptr++;
1578             {
1579                 int32_t j=Gv_GetVar(*insptr++);
1580                 VM_DoConditional((uint32_t)j > (uint32_t)*insptr);
1581             }
1582             continue;
1583 
1584         case CON_IFVARAE:
1585             insptr++;
1586             {
1587                 int32_t j=Gv_GetVar(*insptr++);
1588                 VM_DoConditional((uint32_t)j >= (uint32_t)*insptr);
1589             }
1590             continue;
1591 
1592         case CON_IFVARB:
1593             insptr++;
1594             {
1595                 int32_t j=Gv_GetVar(*insptr++);
1596                 VM_DoConditional((uint32_t)j < (uint32_t)*insptr);
1597             }
1598             continue;
1599 
1600         case CON_IFVARBE:
1601             insptr++;
1602             {
1603                 int32_t j=Gv_GetVar(*insptr++);
1604                 VM_DoConditional((uint32_t)j <= (uint32_t)*insptr);
1605             }
1606             continue;
1607 
1608         case CON_IFRND:
1609             VM_DoConditional(rnd(Gv_GetVar(*(++insptr))));
1610             continue;
1611 
1612         case CON_IFHITKEY:
1613         case CON_IFHOLDKEY:
1614         case CON_RESETKEY:
1615         case CON_SETKEY:
1616             insptr++;
1617             {
1618                 int32_t key=Gv_GetVar(*insptr);
1619                 if (key<0 || key >= (int32_t)ARRAY_SIZE(keystatus))
1620                 {
1621                     M32_ERROR("Invalid key %d!", key);
1622                     continue;
1623                 }
1624 
1625                 if (tw == CON_IFHITKEY || tw == CON_IFHOLDKEY)
1626                     VM_DoConditional(keystatus[key]);
1627                 else
1628                     insptr++;
1629 
1630                 if (tw != CON_IFHOLDKEY)
1631                 {
1632                     if (!(key==0 || key==KEYSC_ESC || key==KEYSC_TILDE || key==KEYSC_gENTER ||
1633                             key==KEYSC_LALT || key==KEYSC_RALT || key==KEYSC_LCTRL || key==KEYSC_RCTRL ||
1634                             key==KEYSC_LSHIFT || key==KEYSC_RSHIFT))
1635                         keystatus[key] = (tw==CON_SETKEY);
1636                 }
1637             }
1638             continue;
1639 
1640         case CON_IFEITHERALT:
1641             VM_DoConditional(keystatus[KEYSC_LALT]||keystatus[KEYSC_RALT]);
1642             continue;
1643 
1644         case CON_IFEITHERCTRL:
1645             VM_DoConditional(keystatus[KEYSC_LCTRL]||keystatus[KEYSC_RCTRL]);
1646             continue;
1647 
1648         case CON_IFEITHERSHIFT:
1649             VM_DoConditional(keystatus[KEYSC_LSHIFT]||keystatus[KEYSC_RSHIFT]);
1650             continue;
1651 
1652 // vvv CURSPR
1653         case CON_IFSPRITEPAL:
1654             insptr++;
1655             X_ERROR_INVALIDSP();
1656             VM_DoConditional(vm.pSprite->pal == Gv_GetVar(*insptr));
1657             continue;
1658 
1659         case CON_IFHIGHLIGHTED:
1660             insptr++;
1661             {
1662                 int32_t id=*insptr++, index=Gv_GetVar(*insptr);
1663 
1664                 if (index<0 || (id==M32_SPRITE_VAR_ID && index>=MAXSPRITES) || (id==M32_WALL_VAR_ID && index>=numwalls))
1665                 {
1666                     M32_ERROR("%s index %d out of range!", id==M32_SPRITE_VAR_ID?"Sprite":"Wall", index);
1667                     continue;
1668                 }
1669 
1670                 if (id==M32_SPRITE_VAR_ID)
1671                     VM_DoConditional(show2dsprite[index>>3]&pow2char[index&7]);
1672                 else
1673                     VM_DoConditional(show2dwall[index>>3]&pow2char[index&7]);
1674             }
1675             continue;
1676 
1677         case CON_IFANGDIFFL:
1678             insptr++;
1679             {
1680                 int32_t j;
1681                 X_ERROR_INVALIDSP();
1682                 j = klabs(G_GetAngleDelta(ang, vm.pSprite->ang));
1683                 VM_DoConditional(j <= Gv_GetVar(*insptr));
1684             }
1685             continue;
1686 
1687         case CON_IFAWAYFROMWALL:
1688         {
1689             int16_t s1;
1690             int32_t j = 0;
1691 
1692             X_ERROR_INVALIDSP();
1693             s1 = vm.pSprite->sectnum;
1694             updatesector(vm.pSprite->x+108,vm.pSprite->y+108,&s1);
1695             if (s1 == vm.pSprite->sectnum)
1696             {
1697                 updatesector(vm.pSprite->x-108,vm.pSprite->y-108,&s1);
1698                 if (s1 == vm.pSprite->sectnum)
1699                 {
1700                     updatesector(vm.pSprite->x+108,vm.pSprite->y-108,&s1);
1701                     if (s1 == vm.pSprite->sectnum)
1702                     {
1703                         updatesector(vm.pSprite->x-108,vm.pSprite->y+108,&s1);
1704                         if (s1 == vm.pSprite->sectnum)
1705                             j = 1;
1706                     }
1707                 }
1708             }
1709             VM_DoConditional(j);
1710         }
1711         continue;
1712 
1713         case CON_IFCANSEE:
1714         {
1715             int32_t j;
1716 
1717             X_ERROR_INVALIDSP();
1718             j = cansee(vm.pSprite->x,vm.pSprite->y,vm.pSprite->z/*-((krand()&41)<<8)*/,vm.pSprite->sectnum,
1719                        pos.x, pos.y, pos.z /*-((krand()&41)<<8)*/, cursectnum);
1720             VM_DoConditional(j);
1721         }
1722         continue;
1723 
1724         case CON_IFONWATER:
1725             X_ERROR_INVALIDSP();
1726             VM_DoConditional(sector[vm.pSprite->sectnum].lotag == 1 && klabs(vm.pSprite->z-sector[vm.pSprite->sectnum].floorz) < (32<<8));
1727             continue;
1728 
1729         case CON_IFINWATER:
1730             X_ERROR_INVALIDSP();
1731             VM_DoConditional(sector[vm.pSprite->sectnum].lotag == 2);
1732             continue;
1733 
1734         case CON_IFACTOR:
1735             insptr++;
1736             X_ERROR_INVALIDSP();
1737             VM_DoConditional(vm.pSprite->picnum == Gv_GetVar(*insptr));
1738             continue;
1739 
1740         case CON_IFINSIDE:
1741             insptr++;
1742             {
1743                 int32_t x=Gv_GetVar(*insptr++), y=Gv_GetVar(*insptr++), sectnum=Gv_GetVar(*insptr++), res;
1744 
1745                 res = inside(x, y, sectnum);
1746                 if (res == -1)
1747                 {
1748                     M32_ERROR("Sector index %d out of range!", sectnum);
1749                     continue;
1750                 }
1751                 insptr--;
1752                 VM_DoConditional(res);
1753             }
1754             continue;
1755 
1756         case CON_IFOUTSIDE:
1757             X_ERROR_INVALIDSP();
1758             VM_DoConditional(sector[vm.pSprite->sectnum].ceilingstat&1);
1759             continue;
1760 
1761         case CON_IFPDISTL:
1762             insptr++;
1763             {
1764                 X_ERROR_INVALIDSP();
1765                 VM_DoConditional(dist((spritetype *)&pos, vm.pSprite) < Gv_GetVar(*insptr));
1766             }
1767             continue;
1768 
1769         case CON_IFPDISTG:
1770             insptr++;
1771             {
1772                 X_ERROR_INVALIDSP();
1773                 VM_DoConditional(dist((spritetype *)&pos, vm.pSprite) > Gv_GetVar(*insptr));
1774             }
1775             continue;
1776 // ^^^
1777 
1778 // *** BUILD functions
1779         case CON_INSERTSPRITE:
1780             insptr++;
1781             {
1782                 int32_t dasectnum = Gv_GetVar(*insptr++), ret;
1783 
1784                 X_ERROR_INVALIDSECT(dasectnum);
1785                 if (Numsprites >= MAXSPRITES)
1786                 {
1787                     M32_ERROR("Maximum number of sprites reached.");
1788                     continue;
1789                 }
1790 
1791                 ret = insertsprite(dasectnum, 0);
1792                 vm.spriteNum = ret;
1793                 vm.pSprite = &sprite[ret];
1794             }
1795             continue;
1796 
1797         case CON_DUPSPRITE:
1798         case CON_TDUPSPRITE:
1799             insptr++;
1800             {
1801                 int32_t ospritenum = Gv_GetVar(*insptr++), nspritenum;
1802 
1803                 if (ospritenum<0 || ospritenum>=MAXSPRITES || sprite[ospritenum].statnum==MAXSTATUS)
1804                 {
1805                     M32_ERROR("Tried to duplicate nonexistent sprite %d", ospritenum);
1806                 }
1807                 if ((tw==CON_DUPSPRITE && Numsprites >= MAXSPRITES) ||
1808                         (tw==CON_DUPSPRITE && spritesortcnt >= maxspritesonscreen))
1809                 {
1810                     M32_ERROR("Maximum number of sprites reached.");
1811                 }
1812 
1813                 if (vm.flags&VMFLAG_ERROR)
1814                     continue;
1815 
1816                 if (tw==CON_DUPSPRITE)
1817                 {
1818                     nspritenum = insertsprite(sprite[ospritenum].sectnum, sprite[ospritenum].statnum);
1819 
1820                     if (nspritenum < 0)
1821                     {
1822                         M32_ERROR("Internal error.");
1823                         continue;
1824                     }
1825 
1826                     Bmemcpy(&sprite[nspritenum], &sprite[ospritenum], sizeof(spritetype));
1827                     vm.spriteNum = nspritenum;
1828                     vm.pSprite = &sprite[nspritenum];
1829                 }
1830                 else
1831                 {
1832                     tspriteptr_t tsp = renderAddTSpriteFromSprite(ospritenum);
1833                     vm.spriteNum = -1;
1834                     EDUKE32_STATIC_ASSERT(sizeof(uspritetype) == sizeof(tspritetype)); // see TSPRITE_SIZE
1835                     vm.pUSprite = (uspriteptr_t)tsp;
1836                 }
1837             }
1838             continue;
1839 
1840         case CON_DELETESPRITE:
1841             insptr++;
1842             {
1843                 int32_t daspritenum = Gv_GetVar(*insptr++), ret;
1844 
1845                 X_ERROR_INVALIDSPRI(daspritenum);
1846                 ret = deletesprite(daspritenum);
1847                 g_iReturnVar = ret;
1848             }
1849             continue;
1850 
1851         case CON_GETSPRITELINKTYPE:
1852             insptr++;
1853             {
1854                 int32_t spritenum=Gv_GetVar(*insptr++), resvar = *insptr++;
1855 
1856                 X_ERROR_INVALIDSPRI(spritenum);
1857                 Gv_SetVar(resvar, taglab_linktags(1, spritenum));
1858             }
1859             continue;
1860 
1861         case CON_LASTWALL:
1862             insptr++;
1863             {
1864                 int32_t dapoint = Gv_GetVar(*insptr++), resvar=*insptr++;
1865 
1866                 if (dapoint<0 || dapoint>=numwalls)
1867                 {
1868                     M32_ERROR("Invalid wall %d", dapoint);
1869                     continue;
1870                 }
1871 
1872                 Gv_SetVar(resvar, lastwall(dapoint));
1873             }
1874             continue;
1875 
1876         case CON_GETZRANGE:
1877             insptr++;
1878             {
1879                 vec3_t vect;
1880 
1881                 vect.x = Gv_GetVar(*insptr++);
1882                 vect.y = Gv_GetVar(*insptr++);
1883                 vect.z = Gv_GetVar(*insptr++);
1884 
1885                 {
1886                     int32_t sectnum=Gv_GetVar(*insptr++);
1887                     int32_t ceilzvar=*insptr++, ceilhitvar=*insptr++, florzvar=*insptr++, florhitvar=*insptr++;
1888                     int32_t walldist=Gv_GetVar(*insptr++), clipmask=Gv_GetVar(*insptr++);
1889                     int32_t ceilz, ceilhit, florz, florhit;
1890 
1891                     X_ERROR_INVALIDSECT(sectnum);
1892                     getzrange(&vect, sectnum, &ceilz, &ceilhit, &florz, &florhit, walldist, clipmask);
1893                     Gv_SetVar(ceilzvar, ceilz);
1894                     Gv_SetVar(ceilhitvar, ceilhit);
1895                     Gv_SetVar(florzvar, florz);
1896                     Gv_SetVar(florhitvar, florhit);
1897                 }
1898                 continue;
1899             }
1900 
1901         case CON_CALCHYPOTENUSE:
1902             insptr++;
1903             {
1904                 int32_t retvar=*insptr++;
1905                 int64_t dax=Gv_GetVar(*insptr++), day=Gv_GetVar(*insptr++);
1906                 int64_t hypsq = dax*dax + day*day;
1907 
1908                 if (hypsq > (int64_t)INT32_MAX)
1909                     Gv_SetVar(retvar, (int32_t)sqrt((double)hypsq));
1910                 else
1911                     Gv_SetVar(retvar, ksqrt((uint32_t)hypsq));
1912 
1913                 continue;
1914             }
1915 
1916         case CON_LINEINTERSECT:
1917         case CON_RAYINTERSECT:
1918             insptr++;
1919             {
1920                 int32_t x1=Gv_GetVar(*insptr++), y1=Gv_GetVar(*insptr++), z1=Gv_GetVar(*insptr++);
1921                 int32_t x2=Gv_GetVar(*insptr++), y2=Gv_GetVar(*insptr++), z2=Gv_GetVar(*insptr++);
1922                 int32_t x3=Gv_GetVar(*insptr++), y3=Gv_GetVar(*insptr++), x4=Gv_GetVar(*insptr++), y4=Gv_GetVar(*insptr++);
1923                 int32_t intxvar=*insptr++, intyvar=*insptr++, intzvar=*insptr++, retvar=*insptr++;
1924                 int32_t intx, inty, intz, ret;
1925 
1926                 if (tw==CON_LINEINTERSECT)
1927                     ret = lintersect(x1, y1, z1, x2, y2, z2, x3, y3, x4, y4, &intx, &inty, &intz);
1928                 else
1929                     ret = rayintersect(x1, y1, z1, x2, y2, z2, x3, y3, x4, y4, &intx, &inty, &intz);
1930 
1931                 Gv_SetVar(retvar, ret);
1932                 if (ret)
1933                 {
1934                     Gv_SetVar(intxvar, intx);
1935                     Gv_SetVar(intyvar, inty);
1936                     Gv_SetVar(intzvar, intz);
1937                 }
1938 
1939                 continue;
1940             }
1941 
1942         case CON_CLIPMOVE:
1943             insptr++;
1944             {
1945                 vec3_t vect;
1946                 int32_t retvar=*insptr++, xvar=*insptr++, yvar=*insptr++, z=Gv_GetVar(*insptr++), sectnumvar=*insptr++;
1947                 int32_t xvect=Gv_GetVar(*insptr++), yvect=Gv_GetVar(*insptr++);
1948                 int32_t walldist=Gv_GetVar(*insptr++), floordist=Gv_GetVar(*insptr++), ceildist=Gv_GetVar(*insptr++);
1949                 int32_t clipmask=Gv_GetVar(*insptr++);
1950                 int16_t sectnum;
1951 
1952                 vect.x = Gv_GetVar(xvar);
1953                 vect.y = Gv_GetVar(yvar);
1954                 vect.z = z;
1955                 sectnum = Gv_GetVar(sectnumvar);
1956 
1957                 X_ERROR_INVALIDSECT(sectnum);
1958 
1959                 Gv_SetVar(retvar, clipmove(&vect, &sectnum, xvect, yvect, walldist, floordist, ceildist, clipmask));
1960                 Gv_SetVar(sectnumvar, sectnum);
1961                 Gv_SetVar(xvar, vect.x);
1962                 Gv_SetVar(yvar, vect.y);
1963 
1964                 continue;
1965             }
1966 
1967         case CON_HITSCAN:
1968             insptr++;
1969             {
1970                 vec3_t vect;
1971                 hitdata_t hit;
1972 
1973                 vect.x = Gv_GetVar(*insptr++);
1974                 vect.y = Gv_GetVar(*insptr++);
1975                 vect.z = Gv_GetVar(*insptr++);
1976 
1977                 {
1978                     int32_t sectnum=Gv_GetVar(*insptr++);
1979                     int32_t vx=Gv_GetVar(*insptr++), vy=Gv_GetVar(*insptr++), vz=Gv_GetVar(*insptr++);
1980                     int32_t hitsectvar=*insptr++, hitwallvar=*insptr++, hitspritevar=*insptr++;
1981                     int32_t hitxvar=*insptr++, hityvar=*insptr++, hitzvar=*insptr++, cliptype=Gv_GetVar(*insptr++);
1982 
1983                     X_ERROR_INVALIDSECT(sectnum);
1984                     hitscan((const vec3_t *)&vect, sectnum, vx, vy, vz, &hit, cliptype);
1985                     Gv_SetVar(hitsectvar, hit.sect);
1986                     Gv_SetVar(hitwallvar, hit.wall);
1987                     Gv_SetVar(hitspritevar, hit.sprite);
1988                     Gv_SetVar(hitxvar, hit.pos.x);
1989                     Gv_SetVar(hityvar, hit.pos.y);
1990                     Gv_SetVar(hitzvar, hit.pos.z);
1991                 }
1992                 continue;
1993             }
1994 
1995         case CON_CANSEE:
1996             insptr++;
1997             {
1998                 int32_t x1=Gv_GetVar(*insptr++), y1=Gv_GetVar(*insptr++), z1=Gv_GetVar(*insptr++);
1999                 int32_t sect1=Gv_GetVar(*insptr++);
2000                 int32_t x2=Gv_GetVar(*insptr++), y2=Gv_GetVar(*insptr++), z2=Gv_GetVar(*insptr++);
2001                 int32_t sect2=Gv_GetVar(*insptr++), rvar=*insptr++;
2002 
2003                 X_ERROR_INVALIDSECT(sect1);
2004                 X_ERROR_INVALIDSECT(sect2);
2005 
2006                 Gv_SetVar(rvar, cansee(x1,y1,z1,sect1,x2,y2,z2,sect2));
2007                 continue;
2008             }
2009 
2010         case CON_ROTATEPOINT:
2011             insptr++;
2012             {
2013                 vec2_t pivot = { Gv_GetVar(*insptr), Gv_GetVar(*(insptr+1)) };
2014                 vec2_t p = { Gv_GetVar(*(insptr+2)), Gv_GetVar(*(insptr+3)) };
2015                 insptr += 4;
2016                 int32_t daang=Gv_GetVar(*insptr++);
2017                 int32_t x2var=*insptr++, y2var=*insptr++;
2018                 vec2_t p2;
2019 
2020                 rotatepoint(pivot,p,daang,&p2);
2021                 Gv_SetVar(x2var, p2.x);
2022                 Gv_SetVar(y2var, p2.y);
2023                 continue;
2024             }
2025 
2026         case CON_NEARTAG:
2027             insptr++;
2028             {
2029                 // neartag(int32_t x, int32_t y, int32_t z, short sectnum, short ang,  //Starting position & angle
2030                 //         short *neartagsector,    //Returns near sector if sector[].tag != 0
2031                 //         short *neartagwall,      //Returns near wall if wall[].tag != 0
2032                 //         short *neartagsprite,    //Returns near sprite if sprite[].tag != 0
2033                 //         int32_t *neartaghitdist, //Returns actual distance to object (scale: 1024=largest grid size)
2034                 //         int32_t neartagrange,    //Choose maximum distance to scan (scale: 1024=largest grid size)
2035                 //         char tagsearch)          //1-lotag only, 2-hitag only, 3-lotag&hitag
2036 
2037                 int32_t x=Gv_GetVar(*insptr++), y=Gv_GetVar(*insptr++), z=Gv_GetVar(*insptr++);
2038                 int32_t sectnum=Gv_GetVar(*insptr++), ang=Gv_GetVar(*insptr++);
2039                 int32_t neartagsectorvar=*insptr++, neartagwallvar=*insptr++, neartagspritevar=*insptr++, neartaghitdistvar=*insptr++;
2040                 int32_t neartagrange=Gv_GetVar(*insptr++), tagsearch=Gv_GetVar(*insptr++);
2041 
2042                 int16_t neartagsector, neartagwall, neartagsprite;
2043                 int32_t neartaghitdist;
2044 
2045                 X_ERROR_INVALIDSECT(sectnum);
2046                 neartag(x, y, z, sectnum, ang, &neartagsector, &neartagwall, &neartagsprite,
2047                         &neartaghitdist, neartagrange, tagsearch, NULL);
2048 
2049                 Gv_SetVar(neartagsectorvar, neartagsector);
2050                 Gv_SetVar(neartagwallvar, neartagwall);
2051                 Gv_SetVar(neartagspritevar, neartagsprite);
2052                 Gv_SetVar(neartaghitdistvar, neartaghitdist);
2053                 continue;
2054             }
2055 
2056         case CON_BSETSPRITE:  // was CON_SETSPRITE
2057             insptr++;
2058             {
2059                 int32_t spritenum = Gv_GetVar(*insptr++);
2060                 vec3_t davector;
2061 
2062                 davector.x = Gv_GetVar(*insptr++);
2063                 davector.y = Gv_GetVar(*insptr++);
2064                 davector.z = Gv_GetVar(*insptr++);
2065 
2066                 X_ERROR_INVALIDSPRI(spritenum);
2067                 setsprite(spritenum, &davector);
2068                 continue;
2069             }
2070 
2071         case CON_GETFLORZOFSLOPE:
2072         case CON_GETCEILZOFSLOPE:
2073             insptr++;
2074             {
2075                 int32_t sectnum = Gv_GetVar(*insptr++), x = Gv_GetVar(*insptr++), y = Gv_GetVar(*insptr++);
2076                 int32_t var=*insptr++;
2077 
2078                 X_ERROR_INVALIDSECT(sectnum);
2079                 if (tw == CON_GETFLORZOFSLOPE)
2080                     Gv_SetVar(var, getflorzofslope(sectnum,x,y));
2081                 else
2082                     Gv_SetVar(var, getceilzofslope(sectnum,x,y));
2083                 continue;
2084             }
2085 
2086         case CON_ALIGNFLORSLOPE:
2087         case CON_ALIGNCEILSLOPE:
2088             insptr++;
2089             {
2090                 int32_t sectnum = Gv_GetVar(*insptr++), x = Gv_GetVar(*insptr++), y = Gv_GetVar(*insptr++);
2091                 int32_t z=Gv_GetVar(*insptr++);
2092 
2093                 X_ERROR_INVALIDSECT(sectnum);
2094                 if (tw == CON_ALIGNFLORSLOPE)
2095                     alignflorslope(sectnum, x,y,z);
2096                 else
2097                     alignceilslope(sectnum, x,y,z);
2098                 continue;
2099             }
2100 
2101 // CURSPR
2102         case CON_SETFIRSTWALL:
2103             insptr++;
2104             {
2105                 int32_t sect=Gv_GetVar(*insptr++), wal=Gv_GetVar(*insptr++);
2106 
2107                 X_ERROR_INVALIDSECT(sect);
2108                 setfirstwall(sect, wal);
2109             }
2110             continue;
2111 
2112         case CON_UPDATECURSECTNUM:
2113             insptr++;
2114             updatesectorz(pos.x, pos.y, pos.z, &cursectnum);
2115             continue;
2116 
2117         case CON_UPDATESECTOR:
2118         case CON_UPDATESECTORZ:
2119         case CON_UPDATESECTORNEIGHBOR:
2120         case CON_UPDATESECTORNEIGHBORZ:
2121             insptr++;
2122             {
2123                 int32_t x=Gv_GetVar(*insptr++), y=Gv_GetVar(*insptr++);
2124                 int32_t z=(tw==CON_UPDATESECTORZ || tw==CON_UPDATESECTORNEIGHBORZ)?Gv_GetVar(*insptr++):0;
2125                 int32_t var=*insptr++;
2126                 int16_t w;
2127 
2128                 X_ERROR_INVALIDCI();
2129                 w=sprite[vm.spriteNum].sectnum;
2130 
2131                 switch (tw)
2132                 {
2133                 case CON_UPDATESECTORNEIGHBORZ:
2134                     updatesectorneighborz(x,y,z,&w,getsectordist({x, y}, w));
2135                     continue;
2136                 case CON_UPDATESECTORZ:
2137                     updatesectorz(x,y,z,&w);
2138                     continue;
2139                 case CON_UPDATESECTORNEIGHBOR:
2140                     updatesectorneighbor(x,y,&w,getsectordist({x, y}, w));
2141                     continue;
2142                 default:
2143                     updatesector(x,y,&w);
2144                     continue;
2145                 }
2146 
2147                 Gv_SetVar(var, w);
2148                 continue;
2149             }
2150 
2151         case CON_HEADSPRITESTAT:
2152             insptr++;
2153             {
2154                 int32_t i=*insptr++;
2155                 int32_t j=Gv_GetVar(*insptr++);
2156                 if (j < 0 || j > MAXSTATUS)
2157                 {
2158                     M32_ERROR("invalid status list %d", j);
2159                     continue;
2160                 }
2161                 Gv_SetVar(i,headspritestat[j]);
2162                 continue;
2163             }
2164 
2165         case CON_PREVSPRITESTAT:
2166             insptr++;
2167             {
2168                 int32_t i=*insptr++;
2169                 int32_t j=Gv_GetVar(*insptr++);
2170 
2171                 X_ERROR_INVALIDSPRI(j);
2172                 Gv_SetVar(i,prevspritestat[j]);
2173                 continue;
2174             }
2175 
2176         case CON_NEXTSPRITESTAT:
2177             insptr++;
2178             {
2179                 int32_t i=*insptr++;
2180                 int32_t j=Gv_GetVar(*insptr++);
2181 
2182                 X_ERROR_INVALIDSPRI(j);
2183                 Gv_SetVar(i,nextspritestat[j]);
2184                 continue;
2185             }
2186 
2187         case CON_HEADSPRITESECT:
2188             insptr++;
2189             {
2190                 int32_t i=*insptr++;
2191                 int32_t j=Gv_GetVar(*insptr++);
2192 
2193                 X_ERROR_INVALIDSECT(j);
2194                 Gv_SetVar(i,headspritesect[j]);
2195                 continue;
2196             }
2197 
2198         case CON_PREVSPRITESECT:
2199             insptr++;
2200             {
2201                 int32_t i=*insptr++;
2202                 int32_t j=Gv_GetVar(*insptr++);
2203 
2204                 X_ERROR_INVALIDSPRI(j);
2205                 Gv_SetVar(i,prevspritesect[j]);
2206                 continue;
2207             }
2208 
2209         case CON_NEXTSPRITESECT:
2210             insptr++;
2211             {
2212                 int32_t i=*insptr++;
2213                 int32_t j=Gv_GetVar(*insptr++);
2214 
2215                 X_ERROR_INVALIDSPRI(j);
2216                 Gv_SetVar(i,nextspritesect[j]);
2217                 continue;
2218             }
2219 
2220         case CON_CANSEESPR:
2221             insptr++;
2222             {
2223                 int32_t lVar1 = Gv_GetVar(*insptr++), lVar2 = Gv_GetVar(*insptr++), res;
2224 
2225                 if (lVar1<0 || lVar1>=MAXSPRITES || sprite[lVar1].statnum==MAXSTATUS)
2226                 {
2227                     M32_ERROR("Invalid sprite %d", lVar1);
2228                 }
2229                 if (lVar2<0 || lVar2>=MAXSPRITES || sprite[lVar2].statnum==MAXSTATUS)
2230                 {
2231                     M32_ERROR("Invalid sprite %d", lVar2);
2232                 }
2233 
2234                 if (vm.flags&VMFLAG_ERROR) res=0;
2235                 else res=cansee(sprite[lVar1].x,sprite[lVar1].y,sprite[lVar1].z,sprite[lVar1].sectnum,
2236                                     sprite[lVar2].x,sprite[lVar2].y,sprite[lVar2].z,sprite[lVar2].sectnum);
2237 
2238                 Gv_SetVar(*insptr++, res);
2239                 continue;
2240             }
2241 
2242         case CON_CHANGESPRITESTAT:
2243         case CON_CHANGESPRITESECT:
2244             insptr++;
2245             {
2246                 int32_t i = Gv_GetVar(*insptr++);
2247                 int32_t j = Gv_GetVar(*insptr++);
2248 
2249                 X_ERROR_INVALIDSPRI(i);
2250                 if (j<0 || j >= (tw==CON_CHANGESPRITESTAT?MAXSTATUS:numsectors))
2251                 {
2252                     M32_ERROR("Invalid %s: %d", tw==CON_CHANGESPRITESTAT?"statnum":"sector", j);
2253                     continue;
2254                 }
2255 
2256                 if (tw == CON_CHANGESPRITESTAT)
2257                 {
2258                     if (sprite[i].statnum == j) continue;
2259                     changespritestat(i,j);
2260                 }
2261                 else
2262                 {
2263                     if (sprite[i].sectnum == j) continue;
2264                     changespritesect(i,j);
2265                 }
2266                 continue;
2267             }
2268 
2269         case CON_DRAGPOINT:
2270             insptr++;
2271             {
2272                 int32_t wallnum = Gv_GetVar(*insptr++), newx = Gv_GetVar(*insptr++), newy = Gv_GetVar(*insptr++);
2273 
2274                 if (wallnum<0 || wallnum>=numwalls)
2275                 {
2276                     M32_ERROR("Invalid wall %d", wallnum);
2277                     continue;
2278                 }
2279                 dragpoint(wallnum,newx,newy,0);
2280                 continue;
2281             }
2282 
2283         case CON_SECTOROFWALL:
2284             insptr++;
2285             {
2286                 int32_t j = *insptr++;
2287                 Gv_SetVar(j, sectorofwall(Gv_GetVar(*insptr++)));
2288             }
2289             continue;
2290 
2291         case CON_FIXREPEATS:
2292             insptr++;
2293             fixrepeats(Gv_GetVar(*insptr++));
2294             continue;
2295 
2296         case CON_GETCLOSESTCOL:
2297             insptr++;
2298             {
2299                 int32_t r = Gv_GetVar(*insptr++), g = Gv_GetVar(*insptr++), b = Gv_GetVar(*insptr++);
2300                 Gv_SetVar(*insptr++, paletteGetClosestColor(r, g, b));
2301                 continue;
2302             }
2303 
2304 // *** stuff
2305         case CON_UPDATEHIGHLIGHT:
2306             insptr++;
2307             update_highlight();
2308             continue;
2309 
2310         case CON_UPDATEHIGHLIGHTSECTOR:
2311             insptr++;
2312             update_highlightsector();
2313             continue;
2314 
2315         case CON_SETHIGHLIGHT:
2316             insptr++;
2317             {
2318                 int32_t what=Gv_GetVar(*insptr++), index=Gv_GetVar(*insptr++), doset = Gv_GetVar(*insptr++);
2319 
2320                 if (highlightsectorcnt >= 0)
2321                 {
2322                     M32_ERROR("sector highlight active or pending, cannot highlight sprites/walls");
2323                     continue;
2324                 }
2325 
2326                 if (what&16384)
2327                 {
2328                     index &= ~16384;
2329                     if (index < 0 || index>=MAXSPRITES || sprite[index].statnum==MAXSTATUS)
2330                     {
2331                         M32_ERROR("Invalid sprite index %d", index);
2332                         continue;
2333                     }
2334 
2335                     if (doset)
2336                         show2dsprite[index>>3] |= pow2char[index&7];
2337                     else
2338                         show2dsprite[index>>3] &= ~pow2char[index&7];
2339                 }
2340                 else
2341                 {
2342                     if (index < 0 || index>=numwalls)
2343                     {
2344                         M32_ERROR("Invalid wall index %d", index);
2345                         continue;
2346                     }
2347 
2348                     if (doset)
2349                         show2dwall[index>>3] |= pow2char[index&7];
2350                     else
2351                         show2dwall[index>>3] &= ~pow2char[index&7];
2352                 }
2353 
2354                 vm.miscflags |= VMFLAG_MISC_UPDATEHL;
2355 
2356                 continue;
2357             }
2358 
2359         case CON_SETHIGHLIGHTSECTOR:
2360             insptr++;
2361             {
2362                 int32_t index=Gv_GetVar(*insptr++), doset = Gv_GetVar(*insptr++);
2363 
2364                 if (highlightcnt >= 0)
2365                 {
2366                     M32_ERROR("sprite/wall highlight active or pending, cannot highlight sectors");
2367                     continue;
2368                 }
2369 
2370                 X_ERROR_INVALIDSECT(index);
2371 
2372                 if (doset)
2373                     hlsectorbitmap[index>>3] |= pow2char[index&7];
2374                 else
2375                     hlsectorbitmap[index>>3] &= ~pow2char[index&7];
2376 
2377                 vm.miscflags |= VMFLAG_MISC_UPDATEHLSECT;
2378 
2379                 continue;
2380             }
2381 
2382         case CON_GETTIMEDATE:
2383             insptr++;
2384             {
2385                 int32_t v1=*insptr++,v2=*insptr++,v3=*insptr++,v4=*insptr++,v5=*insptr++,v6=*insptr++,v7=*insptr++,v8=*insptr++;
2386                 time_t rawtime;
2387                 struct tm *ti;
2388 
2389                 time(&rawtime);
2390                 ti = localtime(&rawtime);
2391                 // initprintf("Time&date: %s\n",asctime (ti));
2392 
2393                 Gv_SetVar(v1, ti->tm_sec);
2394                 Gv_SetVar(v2, ti->tm_min);
2395                 Gv_SetVar(v3, ti->tm_hour);
2396                 Gv_SetVar(v4, ti->tm_mday);
2397                 Gv_SetVar(v5, ti->tm_mon);
2398                 Gv_SetVar(v6, ti->tm_year+1900);
2399                 Gv_SetVar(v7, ti->tm_wday);
2400                 Gv_SetVar(v8, ti->tm_yday);
2401                 continue;
2402             }
2403 
2404         case CON_ADDLOG:
2405         {
2406             insptr++;
2407 
2408             OSD_Printf("L=%d\n", g_errorLineNum);
2409             continue;
2410         }
2411 
2412         case CON_ADDLOGVAR:
2413             insptr++;
2414             {
2415                 char buf[80] = "", buf2[80] = "";
2416                 int32_t code = (int32_t)*insptr, val = Gv_GetVar(code);
2417                 int32_t negate=code&M32_FLAG_NEGATE;
2418 
2419                 if (code & (0xFFFFFFFF-(MAXGAMEVARS-1)))
2420                 {
2421                     if ((code&M32_VARTYPE_MASK)==M32_FLAG_ARRAY || (code&M32_VARTYPE_MASK)==M32_FLAG_STRUCT)
2422                     {
2423                         if (code&M32_FLAG_CONSTANT)
2424                             Bsprintf(buf2, "%d", (code>>16)&0xffff);
2425                         else
2426                         {
2427                             char *label = aGameVars[(code>>16)&(MAXGAMEVARS-1)].szLabel;
2428                             Bsprintf(buf2, "%s", label?label:"???");
2429                         }
2430                     }
2431                     else if ((code&M32_VARTYPE_MASK)==M32_FLAG_LOCAL)
2432                         Bsprintf(buf2, "%d", code&(MAXGAMEVARS-1));
2433 
2434                     if ((code&0x0000FFFC) == M32_FLAG_CONSTANT) // addlogvar for a constant.. why not? :P
2435                     {
2436                         switch (code&3)
2437                         {
2438                         case 0: Bsprintf(buf, "(immediate constant)"); break;
2439                         case 1: Bsprintf(buf, "(indirect constant)"); break;
2440                         case 2: Bsprintf(buf, "(label constant)"); break;
2441                         default: Bsprintf(buf, "(??? constant)"); break;
2442                         }
2443                     }
2444                     else
2445                     {
2446                         switch (code&M32_VARTYPE_MASK)
2447                         {
2448                         case M32_FLAG_ARRAY:
2449                             Bsnprintf(buf, sizeof(buf), "%s[%s]", aGameArrays[code&(MAXGAMEARRAYS-1)].szLabel
2450                                       ? aGameArrays[code&(MAXGAMEARRAYS-1)].szLabel : "???", buf2);
2451                             break;
2452                         case M32_FLAG_STRUCT:
2453                         {
2454                             int32_t memberid=(code>>2)&63, lightp = (memberid >= LIGHT_X);
2455                             const char *pp1[4] = {"sprite","sector","wall","tsprite"};
2456                             memberlabel_t const *pp2[4] = {SpriteLabels, SectorLabels, WallLabels, SpriteLabels};
2457                             if (lightp)
2458                             {
2459                                 pp1[3] = "light";
2460                                 pp2[3] = LightLabels;
2461                                 memberid -= LIGHT_X;
2462                             }
2463 
2464                             Bsnprintf(buf, sizeof(buf), "%s[%s].%s", pp1[code&3], buf2, pp2[code&3][memberid].name);
2465                         }
2466                         break;
2467                         case M32_FLAG_VAR:
2468                             Bstrcpy(buf, "???");
2469                             break;
2470                         case M32_FLAG_LOCAL:
2471                             Bsnprintf(buf, sizeof(buf), ".local[%s]", buf2);
2472                             break;
2473                         }
2474                     }
2475                 }
2476                 else
2477                 {
2478                     if (aGameVars[code].dwFlags & GAMEVAR_PERBLOCK)
2479                     {
2480                         Bsprintf(buf2, "(%s", vm.g_st==0? "top-level) " : vm.g_st<=MAXEVENTS? "event" : "state");
2481                         if (vm.g_st >= 1+MAXEVENTS && vm.g_st <1+MAXEVENTS+g_stateCount)
2482                             Bsprintf(buf, " `%s') ", statesinfo[vm.g_st-1-MAXEVENTS].name);
2483                         else if (vm.g_st > 0)
2484                             Bsprintf(buf, " %d) ", vm.g_st-1);
2485                         Bstrcat(buf2, buf);
2486                     }
2487 
2488                     Bsnprintf(buf, sizeof(buf), "%s%s", buf2, aGameVars[code].szLabel ? aGameVars[code].szLabel : "???");
2489                 }
2490 
2491                 OSD_Printf("L%d: %s%s=%d\n", g_errorLineNum, negate?"-":"", buf, val);
2492 
2493                 insptr++;
2494                 continue;
2495             }
2496 
2497         case CON_DEBUG:
2498             insptr++;
2499             initprintf("%d\n",*insptr++);
2500             continue;
2501 
2502 // *** strings
2503         case CON_REDEFINEQUOTE:
2504             insptr++;
2505             {
2506                 int32_t q = *insptr++, i = *insptr++;
2507                 X_ERROR_INVALIDQUOTE(q, apStrings);
2508                 X_ERROR_INVALIDQUOTE(i, apXStrings);
2509                 Bstrcpy(apStrings[q],apXStrings[i]);
2510                 continue;
2511             }
2512 
2513         case CON_GETNUMBER16:  /* deprecated */
2514         case CON_GETNUMBER256:  /* deprecated */
2515         case CON_GETNUMBERFROMUSER:
2516             insptr++;
2517             {
2518                 int32_t var=*insptr++, quote=*insptr++;
2519                 const char *quotetext = GetMaybeInlineQuote(quote);
2520                 if (vm.flags&VMFLAG_ERROR)
2521                     continue;
2522 
2523                 {
2524                     int32_t max=Gv_GetVar(*insptr++);
2525                     int32_t sign = (tw==CON_GETNUMBERFROMUSER) ? Gv_GetVar(*insptr++) : (max<=0);
2526                     char buf[64];  // buffers in getnumber* are 80 bytes long
2527 
2528                     Bstrncpyz(buf, quotetext, sizeof(buf));
2529 
2530                     if (max==0)
2531                         max = INT32_MAX;
2532                     else
2533                         max = klabs(max);
2534 
2535 //OSD_Printf("max:%d, sign:%d\n", max, sign);
2536                     if (tw==CON_GETNUMBERFROMUSER)
2537                     {
2538                         Gv_SetVar(var, in3dmode() ?
2539                                    getnumber256(quotetext, Gv_GetVar(var), max, sign) :
2540                                    getnumber16(quotetext, Gv_GetVar(var), max, sign));
2541                     }
2542                     else if (tw==CON_GETNUMBER16)
2543                         Gv_SetVar(var, getnumber16(quotetext, Gv_GetVar(var), max, sign));
2544                     else
2545                         Gv_SetVar(var, getnumber256(quotetext, Gv_GetVar(var), max, sign));
2546                 }
2547             }
2548             continue;
2549 
2550         case CON_PRINT:
2551         case CON_QUOTE:
2552         case CON_ERRORINS:
2553         case CON_PRINTMESSAGE16:
2554         case CON_PRINTMESSAGE256:
2555         case CON_PRINTEXT256:
2556         case CON_PRINTEXT16:
2557         case CON_DRAWLABEL:
2558             insptr++;
2559             {
2560                 int32_t i=*insptr++;
2561                 const char *quotetext = GetMaybeInlineQuote(i);
2562                 if (vm.flags&VMFLAG_ERROR)
2563                     continue;
2564 
2565                 {
2566                     int32_t x=(tw>=CON_PRINTMESSAGE256)?Gv_GetVar(*insptr++):0;
2567                     int32_t y=(tw>=CON_PRINTMESSAGE256)?Gv_GetVar(*insptr++):0;
2568 
2569                     int32_t col=(tw>=CON_PRINTEXT256)?Gv_GetVar(*insptr++):0;
2570                     int32_t backcol=(tw>=CON_PRINTEXT256)?Gv_GetVar(*insptr++):0;
2571                     int32_t fontsize=(tw>=CON_PRINTEXT256)?Gv_GetVar(*insptr++):0;
2572 
2573                     if (tw==CON_PRINT || tw==CON_ERRORINS)
2574                     {
2575                         OSD_Printf("%s\n", quotetext);
2576                         if (tw==CON_ERRORINS)
2577                             vm.flags |= VMFLAG_ERROR;
2578                     }
2579                     else if (tw==CON_QUOTE)
2580                     {
2581                         message("%s", quotetext);
2582                     }
2583                     else if (tw==CON_PRINTMESSAGE16)
2584                     {
2585                         if (!in3dmode())
2586                             printmessage16("%s", quotetext);
2587                     }
2588                     else if (tw==CON_PRINTMESSAGE256)
2589                     {
2590                         if (in3dmode())
2591                             printmessage256(x, y, quotetext);
2592                     }
2593                     else if (tw==CON_PRINTEXT256)
2594                     {
2595                         if (in3dmode())
2596                         {
2597                             if (col>=256)
2598                                 col=0;
2599                             else if (col < 0 && col >= -255)
2600                                 col = editorcolors[-col];
2601 
2602                             if (backcol<0 || backcol>=256)
2603                                 backcol=-1;
2604 
2605                             printext256(x, y, col, backcol, quotetext, fontsize);
2606                         }
2607                     }
2608                     else if (tw==CON_PRINTEXT16)
2609                     {
2610                         if (!in3dmode())
2611                             printext16(x, y, editorcolors[col&255], backcol<0 ? -1 : editorcolors[backcol&255],
2612                                        quotetext, fontsize);
2613                     }
2614                     else if (tw==CON_DRAWLABEL)
2615                     {
2616                         if (!in3dmode())
2617                         {
2618                             drawsmallabel(quotetext,
2619                                           editorcolors[backcol&255],  // col
2620                                           fontsize < 0 ? -1 : editorcolors[fontsize&255], editorcolors[fontsize&255] - 3, // backcol
2621                                           x, y, col);  // x y z
2622                         }
2623                     }
2624                 }
2625             }
2626             continue;
2627 
2628         case CON_QSTRLEN:
2629             insptr++;
2630             {
2631                 int32_t i=*insptr++, quote=*insptr++;
2632                 const char *quotetext = GetMaybeInlineQuote(quote);
2633                 if (vm.flags&VMFLAG_ERROR)
2634                     continue;
2635 
2636                 Gv_SetVar(i, Bstrlen(quotetext));
2637                 continue;
2638             }
2639 
2640         case CON_QSUBSTR:
2641             insptr++;
2642             {
2643                 int32_t q1 = Gv_GetVar(*insptr++);
2644                 int32_t q2 = *insptr++;
2645                 const char *q2text = GetMaybeInlineQuote(q2);
2646                 if (vm.flags&VMFLAG_ERROR)
2647                     continue;
2648 
2649                 X_ERROR_INVALIDQUOTE(q1, apStrings);
2650 
2651                 {
2652                     int32_t st = Gv_GetVar(*insptr++);
2653                     int32_t ln = Gv_GetVar(*insptr++);
2654                     char *s1 = apStrings[q1];
2655                     const char *s2 = q2text;
2656 
2657                     while (*s2 && st--) s2++;
2658                     while ((*s1 = *s2) && ln--)
2659                     {
2660                         s1++;
2661                         s2++;
2662                     }
2663                     *s1=0;
2664                 }
2665                 continue;
2666             }
2667 
2668         case CON_QSTRNCAT:
2669         case CON_QSTRCAT:
2670         case CON_QSTRCPY:
2671 ///        case CON_QGETSYSSTR:
2672             insptr++;
2673             {
2674                 int32_t i = Gv_GetVar(*insptr++);
2675                 int32_t j = *insptr++;
2676 
2677                 const char *quotetext = GetMaybeInlineQuote(j);
2678                 if (vm.flags&VMFLAG_ERROR)
2679                     continue;
2680 
2681                 X_ERROR_INVALIDQUOTE(i, apStrings);
2682 
2683                 switch (tw)
2684                 {
2685                 case CON_QSTRCAT:
2686                     Bstrncat(apStrings[i], quotetext, (MAXQUOTELEN-1)-Bstrlen(apStrings[i]));
2687                     break;
2688                 case CON_QSTRNCAT:
2689                     Bstrncat(apStrings[i], quotetext, Gv_GetVar(*insptr++));
2690                     break;
2691                 case CON_QSTRCPY:
2692                     Bstrcpy(apStrings[i], quotetext);
2693                     break;
2694                 }
2695                 continue;
2696             }
2697 
2698         case CON_QSPRINTF:
2699             insptr++;
2700             {
2701                 int32_t dq=Gv_GetVar(*insptr++), sq=*insptr++;
2702                 const char *sourcetext = GetMaybeInlineQuote(sq);
2703                 if (vm.flags&VMFLAG_ERROR)
2704                     continue;
2705 
2706                 X_ERROR_INVALIDQUOTE(dq, apStrings);
2707 
2708                 {
2709                     int32_t arg[32], numvals=0, i=0, j=0, k=0;
2710                     int32_t len = Bstrlen(sourcetext);
2711                     char tmpbuf[MAXQUOTELEN<<1];
2712 
2713                     while (*insptr != -1 && numvals < 32)
2714                         arg[numvals++] = Gv_GetVar(*insptr++);
2715 
2716                     insptr++; // skip the NOP
2717 
2718                     i = 0;
2719                     do
2720                     {
2721                         while (k < len && j < MAXQUOTELEN && sourcetext[k] != '%')
2722                             tmpbuf[j++] = sourcetext[k++];
2723 
2724                         if (sourcetext[k] == '%')
2725                         {
2726                             k++;
2727 
2728                             if (i>=numvals) goto dodefault;
2729 
2730                             switch (sourcetext[k])
2731                             {
2732                             case 'l':
2733                                 if (sourcetext[k+1] != 'd')
2734                                 {
2735                                     // write the % and l
2736                                     tmpbuf[j++] = sourcetext[k-1];
2737                                     tmpbuf[j++] = sourcetext[k++];
2738                                     break;
2739                                 }
2740                                 k++;
2741                                 fallthrough__;
2742                             case 'd':
2743                             {
2744                                 char buf[16];
2745                                 int32_t ii = 0;
2746 
2747                                 Bsprintf(buf, "%d", arg[i++]);
2748 
2749                                 ii = Bstrlen(buf);
2750                                 Bmemcpy(&tmpbuf[j], buf, ii);
2751                                 j += ii;
2752                                 k++;
2753                             }
2754                             break;
2755 
2756                             case 'f':
2757                             {
2758                                 char buf[64];
2759                                 int32_t ii = 0;
2760                                 union { int32_t ival; float fval; };
2761                                 ival = arg[i++];
2762 
2763                                 Bsprintf(buf, "%f", fval);
2764 
2765                                 ii = Bstrlen(buf);
2766                                 Bmemcpy(&tmpbuf[j], buf, ii);
2767                                 j += ii;
2768                                 k++;
2769                             }
2770                             break;
2771 
2772                             case 's':
2773                             {
2774                                 if (arg[i]>=0 && arg[i]<MAXQUOTES && apStrings[arg[i]])
2775                                 {
2776                                     int32_t ii = Bstrlen(apStrings[arg[i]]);
2777                                     Bmemcpy(&tmpbuf[j], apStrings[arg[i]], ii);
2778                                     j += ii;
2779                                 }
2780                                 k++;
2781                             }
2782                             break;
2783 
2784 dodefault:
2785                             default:
2786                                 tmpbuf[j++] = sourcetext[k-1];
2787                                 break;
2788                             }
2789                         }
2790                     }
2791                     while (k < len && j < MAXQUOTELEN);
2792 
2793                     tmpbuf[j] = '\0';
2794                     Bmemcpy(apStrings[dq], tmpbuf, MAXQUOTELEN);
2795                     apStrings[dq][MAXQUOTELEN-1] = '\0';
2796                     continue;
2797                 }
2798             }
2799 
2800 // *** findnear*
2801 // CURSPR vvv
2802         case CON_FINDNEARSPRITE:
2803         case CON_FINDNEARSPRITE3D:
2804         case CON_FINDNEARSPRITEVAR:
2805         case CON_FINDNEARSPRITE3DVAR:
2806             insptr++;
2807             {
2808                 // syntax findnearactor(var) <type> <maxdist(var)> <getvar>
2809                 // gets the sprite ID of the nearest actor within max dist
2810                 // that is of <type> into <getvar>
2811                 // -1 for none found
2812                 // <type> <maxdist(varid)> <varid>
2813                 int32_t lType=*insptr++;
2814                 int32_t lMaxDist = (tw==CON_FINDNEARSPRITE || tw==CON_FINDNEARSPRITE3D)?
2815                                    *insptr++ : Gv_GetVar(*insptr++);
2816                 int32_t lVarID=*insptr++;
2817                 int32_t lFound=-1, j, k = MAXSTATUS-1;
2818 
2819                 X_ERROR_INVALIDCI();
2820                 do
2821                 {
2822                     j=headspritestat[k];    // all sprites
2823                     if (tw==CON_FINDNEARSPRITE3D || tw==CON_FINDNEARSPRITE3DVAR)
2824                     {
2825                         while (j>=0)
2826                         {
2827                             if (sprite[j].picnum == lType && j != vm.spriteNum && dist(&sprite[vm.spriteNum], &sprite[j]) < lMaxDist)
2828                             {
2829                                 lFound=j;
2830                                 j = MAXSPRITES;
2831                                 break;
2832                             }
2833                             j = nextspritestat[j];
2834                         }
2835                         if (j == MAXSPRITES)
2836                             break;
2837                         continue;
2838                     }
2839 
2840                     while (j>=0)
2841                     {
2842                         if (sprite[j].picnum == lType && j != vm.spriteNum && ldist(&sprite[vm.spriteNum], &sprite[j]) < lMaxDist)
2843                         {
2844                             lFound=j;
2845                             j = MAXSPRITES;
2846                             break;
2847                         }
2848                         j = nextspritestat[j];
2849                     }
2850 
2851                     if (j == MAXSPRITES)
2852                         break;
2853                 }
2854                 while (k--);
2855                 Gv_SetVar(lVarID, lFound);
2856                 continue;
2857             }
2858 
2859         case CON_FINDNEARSPRITEZVAR:
2860         case CON_FINDNEARSPRITEZ:
2861             insptr++;
2862             {
2863                 // syntax findnearactor(var) <type> <maxdist(var)> <getvar>
2864                 // gets the sprite ID of the nearest actor within max dist
2865                 // that is of <type> into <getvar>
2866                 // -1 for none found
2867                 // <type> <maxdist(varid)> <varid>
2868                 int32_t lType=*insptr++;
2869                 int32_t lMaxDist = (tw==CON_FINDNEARSPRITEZVAR) ? Gv_GetVar(*insptr++) : *insptr++;
2870                 int32_t lMaxZDist = (tw==CON_FINDNEARSPRITEZVAR) ? Gv_GetVar(*insptr++) : *insptr++;
2871                 int32_t lVarID=*insptr++;
2872                 int32_t lFound=-1, lTemp, lTemp2, j, k=MAXSTATUS-1;
2873 
2874                 X_ERROR_INVALIDCI();
2875                 do
2876                 {
2877                     j=headspritestat[k];    // all sprites
2878                     if (j == -1) continue;
2879                     do
2880                     {
2881                         if (sprite[j].picnum == lType && j != vm.spriteNum)
2882                         {
2883                             lTemp=ldist(&sprite[vm.spriteNum], &sprite[j]);
2884                             if (lTemp < lMaxDist)
2885                             {
2886                                 lTemp2=klabs(sprite[vm.spriteNum].z-sprite[j].z);
2887                                 if (lTemp2 < lMaxZDist)
2888                                 {
2889                                     lFound=j;
2890                                     j = MAXSPRITES;
2891                                     break;
2892                                 }
2893                             }
2894                         }
2895                         j = nextspritestat[j];
2896                     }
2897                     while (j>=0);
2898                     if (j == MAXSPRITES)
2899                         break;
2900                 }
2901                 while (k--);
2902                 Gv_SetVar(lVarID, lFound);
2903 
2904                 continue;
2905             }
2906 // ^^^
2907 
2908         case CON_GETTICKS:
2909             insptr++;
2910             {
2911                 int32_t j=*insptr++;
2912                 Gv_SetVar(j, timerGetTicks());
2913             }
2914             continue;
2915 
2916         case CON_SETASPECT:
2917             insptr++;
2918             {
2919                 int32_t daxrange = Gv_GetVar(*insptr++), dayxaspect = Gv_GetVar(*insptr++);
2920                 if (daxrange < (1<<12)) daxrange = (1<<12);
2921                 if (daxrange > (1<<20)) daxrange = (1<<20);
2922                 if (dayxaspect < (1<<12)) dayxaspect = (1<<12);
2923                 if (dayxaspect > (1<<20)) dayxaspect = (1<<20);
2924                 renderSetAspect(daxrange, dayxaspect);
2925                 continue;
2926             }
2927 
2928 // vvv CURSPR
2929         case CON_SETI:
2930         {
2931             int32_t newcurspritei;
2932 
2933             insptr++;
2934             newcurspritei = Gv_GetVar(*insptr++);
2935             X_ERROR_INVALIDSPRI(newcurspritei);
2936             vm.spriteNum = newcurspritei;
2937             vm.pSprite = &sprite[vm.spriteNum];
2938             continue;
2939         }
2940 
2941         case CON_SIZEAT:
2942             insptr += 3;
2943             X_ERROR_INVALIDSP();
2944             vm.pSprite->xrepeat = (uint8_t) Gv_GetVar(*(insptr-2));
2945             vm.pSprite->yrepeat = (uint8_t) Gv_GetVar(*(insptr-1));
2946 #ifdef USE_STRUCT_TRACKERS
2947             if (vm.spriteNum != -1) spritechanged[vm.spriteNum]++;
2948 #endif
2949             continue;
2950 
2951         case CON_CSTAT:
2952             insptr += 2;
2953             X_ERROR_INVALIDSP();
2954             vm.pSprite->cstat = (int16_t) *(insptr-1);
2955 #ifdef USE_STRUCT_TRACKERS
2956             if (vm.spriteNum != -1) spritechanged[vm.spriteNum]++;
2957 #endif
2958             continue;
2959 
2960         case CON_CSTATOR:
2961             insptr += 2;
2962             X_ERROR_INVALIDSP();
2963             vm.pSprite->cstat |= (int16_t) Gv_GetVar(*(insptr-1));
2964 #ifdef USE_STRUCT_TRACKERS
2965             if (vm.spriteNum != -1) spritechanged[vm.spriteNum]++;
2966 #endif
2967             continue;
2968 
2969         case CON_CLIPDIST:
2970             insptr += 2;
2971             X_ERROR_INVALIDSP();
2972             vm.pSprite->clipdist = (uint8_t) Gv_GetVar(*(insptr-1));
2973 #ifdef USE_STRUCT_TRACKERS
2974             if (vm.spriteNum != -1) spritechanged[vm.spriteNum]++;
2975 #endif
2976             continue;
2977 
2978         case CON_SPRITEPAL:
2979             insptr += 2;
2980             X_ERROR_INVALIDSP();
2981             vm.pSprite->pal = Gv_GetVar(*(insptr-1));
2982 #ifdef USE_STRUCT_TRACKERS
2983             if (vm.spriteNum != -1) spritechanged[vm.spriteNum]++;
2984 #endif
2985             continue;
2986 
2987         case CON_CACTOR:
2988             insptr += 2;
2989             X_ERROR_INVALIDSP();
2990             vm.pSprite->picnum = Gv_GetVar(*(insptr-1));
2991 #ifdef USE_STRUCT_TRACKERS
2992             if (vm.spriteNum != -1) spritechanged[vm.spriteNum]++;
2993 #endif
2994             continue;
2995 
2996         case CON_SPGETLOTAG:
2997             insptr++;
2998             X_ERROR_INVALIDSP();
2999             Gv_SetVar(M32_LOTAG_VAR_ID, vm.pSprite->lotag);
3000             continue;
3001 
3002         case CON_SPGETHITAG:
3003             insptr++;
3004             X_ERROR_INVALIDSP();
3005             Gv_SetVar(M32_HITAG_VAR_ID, vm.pSprite->hitag);
3006             continue;
3007 
3008         case CON_SECTGETLOTAG:
3009             insptr++;
3010             X_ERROR_INVALIDSP();
3011             Gv_SetVar(M32_LOTAG_VAR_ID, sector[vm.pSprite->sectnum].lotag);
3012             continue;
3013 
3014         case CON_SECTGETHITAG:
3015             insptr++;
3016             X_ERROR_INVALIDSP();
3017             Gv_SetVar(M32_HITAG_VAR_ID, sector[vm.pSprite->sectnum].hitag);
3018             continue;
3019 
3020         case CON_GETTEXTUREFLOOR:
3021             insptr++;
3022             X_ERROR_INVALIDSP();
3023             Gv_SetVar(M32_TEXTURE_VAR_ID, sector[vm.pSprite->sectnum].floorpicnum);
3024             continue;
3025 
3026         case CON_GETTEXTURECEILING:
3027             insptr++;
3028             X_ERROR_INVALIDSP();
3029             Gv_SetVar(M32_TEXTURE_VAR_ID, sector[vm.pSprite->sectnum].ceilingpicnum);
3030             continue;
3031 // ^^^
3032         case CON_DRAWLINE16:
3033         case CON_DRAWLINE16B:
3034         case CON_DRAWLINE16Z:
3035             insptr++;
3036             {
3037                 int32_t x1=Gv_GetVar(*insptr++), y1=Gv_GetVar(*insptr++);
3038                 int32_t z1=tw==CON_DRAWLINE16Z?Gv_GetVar(*insptr++):0;
3039                 int32_t x2=Gv_GetVar(*insptr++), y2=Gv_GetVar(*insptr++);
3040                 int32_t z2=tw==CON_DRAWLINE16Z?Gv_GetVar(*insptr++):0;
3041                 int32_t col=Gv_GetVar(*insptr++), odrawlinepat=drawlinepat;
3042                 int32_t xofs=0, yofs=0;
3043 
3044                 if (tw==CON_DRAWLINE16B || tw==CON_DRAWLINE16Z)
3045                 {
3046                     editorGet2dScreenCoordinates(&x1,&y1, x1-pos.x,y1-pos.y, zoom);
3047                     editorGet2dScreenCoordinates(&x2,&y2, x2-pos.x,y2-pos.y, zoom);
3048 
3049                     if (tw==CON_DRAWLINE16Z && m32_sideview)
3050                     {
3051                         y1 += getscreenvdisp(z1-pos.z,zoom);
3052                         y2 += getscreenvdisp(z2-pos.z,zoom);
3053                     }
3054 
3055                     xofs = halfxdim16;
3056                     yofs = midydim16;
3057                 }
3058 
3059                 drawlinepat = m32_drawlinepat;
3060                 editorDraw2dLine(xofs+x1,yofs+y1, xofs+x2,yofs+y2, col>=0?editorcolors[col&15]:((-col)&255));
3061                 drawlinepat = odrawlinepat;
3062                 continue;
3063             }
3064        case CON_DRAWLINE256:
3065             insptr++;
3066             {
3067                 int32_t x1=Gv_GetVar(*insptr++), y1=Gv_GetVar(*insptr++);
3068                 int32_t x2=Gv_GetVar(*insptr++), y2=Gv_GetVar(*insptr++);
3069                 int32_t col=Gv_GetVar(*insptr++);
3070                 renderDrawLine(x1, y1, x2, y2, col);
3071                 continue;
3072             }
3073         case CON_DRAWCIRCLE16:
3074         case CON_DRAWCIRCLE16B:
3075         case CON_DRAWCIRCLE16Z:
3076             insptr++;
3077             {
3078                 int32_t x1=Gv_GetVar(*insptr++), y1=Gv_GetVar(*insptr++);
3079                 int32_t z1 = tw==CON_DRAWCIRCLE16Z ? Gv_GetVar(*insptr++) : 0;
3080                 int32_t r=Gv_GetVar(*insptr++);
3081                 int32_t col=Gv_GetVar(*insptr++), odrawlinepat=drawlinepat;
3082                 int32_t xofs=0, yofs=0, eccen=16384;
3083 
3084                 if (tw==CON_DRAWCIRCLE16B || tw==CON_DRAWCIRCLE16Z)
3085                 {
3086                     editorGet2dScreenCoordinates(&x1,&y1, x1-pos.x,y1-pos.y, zoom);
3087                     if (m32_sideview)
3088                         y1 += getscreenvdisp(z1-pos.z, zoom);
3089                     r = mulscale14(r,zoom);
3090                     eccen = scalescreeny(eccen);
3091                     xofs = halfxdim16;
3092                     yofs = midydim16;
3093                 }
3094 
3095                 drawlinepat = m32_drawlinepat;
3096                 editorDraw2dCircle(xofs+x1, yofs+y1, r, eccen, col>=0?editorcolors[col&15]:((-col)&255));
3097                 drawlinepat = odrawlinepat;
3098                 continue;
3099             }
3100 
3101         case CON_ROTATESPRITEA:
3102         case CON_ROTATESPRITE16:
3103         case CON_ROTATESPRITE:
3104             insptr++;
3105             {
3106                 int32_t x=Gv_GetVar(*insptr++),   y=Gv_GetVar(*insptr++),           z=Gv_GetVar(*insptr++);
3107                 int32_t a=Gv_GetVar(*insptr++),   tilenum=Gv_GetVar(*insptr++),     shade=Gv_GetVar(*insptr++);
3108                 int32_t pal=Gv_GetVar(*insptr++), orientation=Gv_GetVar(*insptr++);
3109                 int32_t alpha = (tw == CON_ROTATESPRITEA) ? Gv_GetVar(*insptr++) : 0;
3110                 int32_t x1=Gv_GetVar(*insptr++),  y1=Gv_GetVar(*insptr++);
3111                 int32_t x2=Gv_GetVar(*insptr++),  y2=Gv_GetVar(*insptr++);
3112 
3113                 if (tw != CON_ROTATESPRITE16 && !(orientation&ROTATESPRITE_FULL16))
3114                 {
3115                     x<<=16;
3116                     y<<=16;
3117                 }
3118 
3119                 orientation &= (ROTATESPRITE_MAX-1);
3120 
3121                 rotatesprite_(x,y,z,a,tilenum,shade,pal,2|orientation,alpha,0,x1,y1,x2,y2);
3122                 continue;
3123             }
3124 
3125         case CON_SETGAMEPALETTE:
3126             insptr++;
3127             SetGamePalette(Gv_GetVar(*insptr++));
3128             continue;
3129 
3130 // *** sounds
3131         case CON_IFSOUND:
3132             insptr++;
3133             {
3134                 int32_t j=Gv_GetVar(*insptr);
3135                 if (S_InvalidSound(j))
3136                 {
3137                     M32_ERROR("Invalid sound %d", j);
3138                     insptr++;
3139                     continue;
3140                 }
3141                 VM_DoConditional(S_CheckSoundPlaying(vm.spriteNum,j));
3142             }
3143             continue;
3144 
3145         case CON_IFNOSOUNDS:
3146             VM_DoConditional(S_SoundsPlaying(vm.spriteNum) < 0);
3147         continue;
3148 
3149         case CON_IFIN3DMODE:
3150             VM_DoConditional(in3dmode());
3151             continue;
3152 
3153         // ifaimingsprite and -wall also work in 2d mode, but you must "and" with 16383 yourself
3154         case CON_IFAIMINGSPRITE:
3155             VM_DoConditional(AIMING_AT_SPRITE || (!in3dmode() && pointhighlight>=16384));
3156             continue;
3157         case CON_IFAIMINGWALL:
3158             VM_DoConditional(AIMING_AT_WALL_OR_MASK || (!in3dmode() && linehighlight>=0));
3159             continue;
3160         case CON_IFAIMINGSECTOR:
3161             VM_DoConditional(AIMING_AT_CEILING_OR_FLOOR);
3162             continue;
3163         case CON_IFINTERACTIVE:
3164             VM_DoConditional(vm.miscflags&VMFLAG_MISC_INTERACTIVE);
3165             continue;
3166 
3167         case CON_GETSOUNDFLAGS:
3168             insptr++;
3169             {
3170                 int32_t j=Gv_GetVar(*insptr++), var=*insptr++;
3171                 if (S_InvalidSound(j))
3172                 {
3173                     M32_ERROR("Invalid sound %d", j);
3174                     insptr++;
3175                     continue;
3176                 }
3177 
3178                 Gv_SetVar(var, S_SoundFlags(j));
3179             }
3180             continue;
3181 
3182         case CON_SOUNDVAR:
3183         case CON_STOPSOUNDVAR:
3184         case CON_SOUNDONCEVAR:
3185         case CON_GLOBALSOUNDVAR:
3186             insptr++;
3187             {
3188                 int32_t j=Gv_GetVar(*insptr++);
3189 
3190                 if (S_InvalidSound(j))
3191                 {
3192                     M32_ERROR("Invalid sound %d", j);
3193                     continue;
3194                 }
3195 
3196                 switch (tw)
3197                 {
3198                 case CON_SOUNDONCEVAR:
3199                     if (!S_CheckSoundPlaying(vm.spriteNum,j))
3200                         A_PlaySound((int16_t)j,vm.spriteNum);
3201                     break;
3202                 case CON_GLOBALSOUNDVAR:
3203                     A_PlaySound((int16_t)j,-1);
3204                     break;
3205                 case CON_STOPSOUNDVAR:
3206                     if (S_CheckSoundPlaying(vm.spriteNum,j))
3207                         S_StopSound((int16_t)j);
3208                     break;
3209                 case CON_SOUNDVAR:
3210                     A_PlaySound((int16_t)j,vm.spriteNum);
3211                     break;
3212                 }
3213             }
3214             continue;
3215 
3216         case CON_STOPALLSOUNDS:
3217             insptr++;
3218             S_StopAllSounds();
3219             continue;
3220 
3221         default:
3222             VM_ScriptInfo();
3223 
3224             OSD_Printf("\nAn error has occurred in the Mapster32 virtual machine.\n\n"
3225                        "Please e-mail the file mapster32.log along with every M32 file\n"
3226                        "you're using and instructions how to reproduce this error to\n"
3227                        "development@voidpoint.com.\n\n"
3228                        "Thank you!\n");
3229             vm.flags |= VMFLAG_ERROR;
3230             Bfflush(NULL);
3231             return 1;
3232         }
3233     }
3234 
3235     return 0;
3236 }
3237