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_GetVarX(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_GetVarX(*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_SetVarX(j, insptr-apScript);
459             }
460             continue;
461 
462         case CON_JUMP:
463             insptr++;
464             {
465                 int32_t j = Gv_GetVarX(*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_GetVarX(*insptr++);
489                 const int32_t value = Gv_GetVarX(*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_SetVarX(*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_GetVarX(*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_GetVarX(*insptr++);
548                 const int32_t di=*insptr++;
549                 int32_t didx = Gv_GetVarX(*insptr++);
550                 int32_t numelts = Gv_GetVarX(*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_SetVarX(*insptr, mulscale16(krand(), *(insptr+1)+1));
609             insptr += 2;
610             continue;
611 
612         case CON_DISPLAYRANDVAR:
613             insptr++;
614             Gv_SetVarX(*insptr, mulscale15(system_15bit_rand(), *(insptr+1)+1));
615             insptr += 2;
616             continue;
617 
618         case CON_SETVAR:
619             insptr++;
620             Gv_SetVarX(*insptr, *(insptr+1));
621             insptr += 2;
622             continue;
623 
624         case CON_SETVARVAR:
625             insptr++;
626             {
627                 int32_t j=*insptr++;
628                 Gv_SetVarX(j, Gv_GetVarX(*insptr++));
629             }
630             continue;
631 
632         case CON_MULVAR:
633             insptr++;
634             Gv_SetVarX(*insptr, Gv_GetVarX(*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_SetVarX(*insptr, Gv_GetVarX(*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_SetVarX(*insptr,Gv_GetVarX(*insptr)%*(insptr+1));
659             insptr += 2;
660             continue;
661 
662         case CON_ANDVAR:
663             insptr++;
664             Gv_SetVarX(*insptr,Gv_GetVarX(*insptr) & *(insptr+1));
665             insptr += 2;
666             continue;
667 
668         case CON_ORVAR:
669             insptr++;
670             Gv_SetVarX(*insptr,Gv_GetVarX(*insptr) | *(insptr+1));
671             insptr += 2;
672             continue;
673 
674         case CON_XORVAR:
675             insptr++;
676             Gv_SetVarX(*insptr,Gv_GetVarX(*insptr) ^ *(insptr+1));
677             insptr += 2;
678             continue;
679 
680         case CON_RANDVARVAR:
681             insptr++;
682             {
683                 int32_t j=*insptr++;
684                 Gv_SetVarX(j,mulscale16(krand(), Gv_GetVarX(*insptr++)+1));
685             }
686             continue;
687 
688         case CON_DISPLAYRANDVARVAR:
689             insptr++;
690             {
691                 int32_t j=*insptr++;
692                 Gv_SetVarX(j,mulscale15(system_15bit_rand(), Gv_GetVarX(*insptr++)+1));
693             }
694             continue;
695 
696         case CON_MULVARVAR:
697             insptr++;
698             {
699                 int32_t j=*insptr++;
700                 Gv_SetVarX(j, Gv_GetVarX(j)*Gv_GetVarX(*insptr++));
701             }
702             continue;
703 
704         case CON_DIVVARVAR:
705             insptr++;
706             {
707                 int32_t j=*insptr++;
708                 int32_t l2=Gv_GetVarX(*insptr++);
709 
710                 if (l2==0)
711                 {
712                     M32_ERROR("Divide by zero.");
713                     continue;
714                 }
715                 Gv_SetVarX(j, Gv_GetVarX(j)/l2);
716                 continue;
717             }
718 
719         case CON_MODVARVAR:
720             insptr++;
721             {
722                 int32_t j=*insptr++;
723                 int32_t l2=Gv_GetVarX(*insptr++);
724 
725                 if (l2==0)
726                 {
727                     M32_ERROR("Mod by zero.");
728                     continue;
729                 }
730 
731                 Gv_SetVarX(j, Gv_GetVarX(j) % l2);
732                 continue;
733             }
734 
735         case CON_ANDVARVAR:
736             insptr++;
737             {
738                 int32_t j=*insptr++;
739                 Gv_SetVarX(j, Gv_GetVarX(j) & Gv_GetVarX(*insptr++));
740             }
741             continue;
742 
743         case CON_XORVARVAR:
744             insptr++;
745             {
746                 int32_t j=*insptr++;
747                 Gv_SetVarX(j, Gv_GetVarX(j) ^ Gv_GetVarX(*insptr++));
748             }
749             continue;
750 
751         case CON_ORVARVAR:
752             insptr++;
753             {
754                 int32_t j=*insptr++;
755                 Gv_SetVarX(j, Gv_GetVarX(j) | Gv_GetVarX(*insptr++));
756             }
757             continue;
758 
759         case CON_SUBVAR:
760             insptr++;
761             Gv_SetVarX(*insptr, Gv_GetVarX(*insptr) - *(insptr+1));
762             insptr += 2;
763             continue;
764 
765         case CON_SUBVARVAR:
766             insptr++;
767             {
768                 int32_t j=*insptr++;
769                 Gv_SetVarX(j, Gv_GetVarX(j) - Gv_GetVarX(*insptr++));
770             }
771             continue;
772 
773         case CON_ADDVAR:
774             insptr++;
775             Gv_SetVarX(*insptr, Gv_GetVarX(*insptr) + *(insptr+1));
776             insptr += 2;
777             continue;
778 
779         case CON_ADDVARVAR:
780             insptr++;
781             {
782                 int32_t j=*insptr++;
783                 Gv_SetVarX(j, Gv_GetVarX(j) + Gv_GetVarX(*insptr++));
784             }
785             continue;
786 
787         case CON_SHIFTVARL:
788             insptr++;
789             Gv_SetVarX(*insptr, Gv_GetVarX(*insptr) << *(insptr+1));
790             insptr += 2;
791             continue;
792 
793         case CON_SHIFTVARVARL:
794             insptr++;
795             {
796                 int32_t j=*insptr++;
797                 Gv_SetVarX(j, Gv_GetVarX(j) << Gv_GetVarX(*insptr++));
798             }
799             continue;
800 
801         case CON_SHIFTVARR:
802             insptr++;
803             Gv_SetVarX(*insptr, Gv_GetVarX(*insptr) >> *(insptr+1));
804             insptr += 2;
805             continue;
806 
807         case CON_SHIFTVARVARR:
808             insptr++;
809             {
810                 int32_t j=*insptr++;
811                 Gv_SetVarX(j, Gv_GetVarX(j) >> Gv_GetVarX(*insptr++));
812             }
813             continue;
814 
815         case CON_SIN:
816             insptr++;
817             Gv_SetVarX(*insptr, sintable[Gv_GetVarX(*(insptr+1))&2047]);
818             insptr += 2;
819             continue;
820 
821         case CON_COS:
822             insptr++;
823             Gv_SetVarX(*insptr, sintable[(Gv_GetVarX(*(insptr+1))+512)&2047]);
824             insptr += 2;
825             continue;
826 
827         case CON_DISPLAYRAND:
828             insptr++;
829             Gv_SetVarX(*insptr++, system_15bit_rand());
830             continue;
831 
832 // *** other math
833         case CON_FTOI:
834             insptr++;
835             {
836                 int32_t bits=Gv_GetVarX(*insptr), scale=*(insptr+1);
837                 float fval = *((float *)&bits);
838 // rounding must absolutely be!
839 //OSD_Printf("ftoi: bits:%8x, scale=%d, fval=%f, (int32_t)(fval*scale)=%d\n", bits, scale, fval, (int32_t)(fval*scale));
840                 Gv_SetVarX(*insptr, (int32_t)Blrintf(fval * scale));
841             }
842             insptr += 2;
843             continue;
844 
845         case CON_ITOF:
846             insptr++;
847             {
848                 int32_t scaled=Gv_GetVarX(*insptr), scale=*(insptr+1);
849                 float fval = (float)scaled/(float)scale;
850                 Gv_SetVarX(*insptr, *((int32_t *)&fval));
851             }
852             insptr += 2;
853             continue;
854 
855         case CON_CLAMP:
856             insptr++;
857             {
858                 int32_t var=*insptr++, min=Gv_GetVarX(*insptr++), max=Gv_GetVarX(*insptr++);
859                 int32_t val=Gv_GetVarX(var);
860 
861                 if (val<min) Gv_SetVarX(var, min);
862                 else if (val>max) Gv_SetVarX(var, max);
863             }
864             continue;
865 
866         case CON_INV:
867             Gv_SetVarX(*(insptr+1), -Gv_GetVarX(*(insptr+1)));
868             insptr += 2;
869             continue;
870 
871         case CON_SQRT:
872             insptr++;
873             {
874                 // syntax sqrt <invar> <outvar>
875                 int32_t lInVarID=*insptr++, lOutVarID=*insptr++;
876 
877                 Gv_SetVarX(lOutVarID, ksqrt((uint32_t)Gv_GetVarX(lInVarID)));
878                 continue;
879             }
880 
881         case CON_LDIST:
882         case CON_DIST:
883             insptr++;
884             {
885                 int32_t distvar = *insptr++, xvar = Gv_GetVarX(*insptr++), yvar = Gv_GetVarX(*insptr++);
886 
887                 if (xvar < 0 || xvar >= MAXSPRITES || sprite[xvar].statnum==MAXSTATUS)
888                 {
889                     M32_ERROR("invalid sprite %d", xvar);
890                 }
891                 if (yvar < 0 || yvar >= MAXSPRITES || sprite[yvar].statnum==MAXSTATUS)
892                 {
893                     M32_ERROR("invalid sprite %d", yvar);
894                 }
895                 if (vm.flags&VMFLAG_ERROR) continue;
896 
897                 if (tw==CON_DIST)
898                     Gv_SetVarX(distvar, dist(&sprite[xvar],&sprite[yvar]));
899                 else
900                     Gv_SetVarX(distvar, ldist(&sprite[xvar],&sprite[yvar]));
901                 continue;
902             }
903 
904         case CON_GETANGLE:
905             insptr++;
906             {
907                 int32_t angvar = *insptr++;
908                 int32_t xvar = Gv_GetVarX(*insptr++);
909                 int32_t yvar = Gv_GetVarX(*insptr++);
910 
911                 Gv_SetVarX(angvar, getangle(xvar,yvar));
912                 continue;
913             }
914 
915         case CON_GETINCANGLE:
916             insptr++;
917             {
918                 int32_t angvar = *insptr++;
919                 int32_t xvar = Gv_GetVarX(*insptr++);
920                 int32_t yvar = Gv_GetVarX(*insptr++);
921 
922                 Gv_SetVarX(angvar, G_GetAngleDelta(xvar,yvar));
923                 continue;
924             }
925 
926         case CON_A2XY:
927         case CON_AH2XYZ:
928             insptr++;
929             {
930                 int32_t ang=Gv_GetVarX(*insptr++), horiz=(tw==CON_A2XY)?100:Gv_GetVarX(*insptr++);
931                 int32_t xvar=*insptr++, yvar=*insptr++;
932 
933                 int32_t x = sintable[(ang+512)&2047];
934                 int32_t y = sintable[ang&2047];
935 
936                 if (tw==CON_AH2XYZ)
937                 {
938                     int32_t zvar=*insptr++, z=0;
939 
940                     horiz -= 100;
941                     if (horiz)
942                     {
943                         int32_t veclen = ksqrt(200*200 + horiz*horiz);
944                         int32_t dacos = divscale14(200, veclen);
945 
946                         x = mulscale14(x, dacos);
947                         y = mulscale14(y, dacos);
948                         z = divscale14(-horiz, veclen);
949                     }
950 
951                     Gv_SetVarX(zvar, z);
952                 }
953 
954                 Gv_SetVarX(xvar, x);
955                 Gv_SetVarX(yvar, y);
956 
957                 continue;
958             }
959 
960         case CON_MULSCALE:
961             insptr++;
962             {
963                 int32_t var1 = *insptr++, var2 = Gv_GetVarX(*insptr++);
964                 int32_t var3 = Gv_GetVarX(*insptr++), var4 = Gv_GetVarX(*insptr++);
965 
966                 Gv_SetVarX(var1, mulscale(var2, var3, var4));
967                 continue;
968             }
969         case CON_DIVSCALE:
970             insptr++;
971             {
972                 int32_t var1 = *insptr++, var2 = Gv_GetVarX(*insptr++);
973                 int32_t var3 = Gv_GetVarX(*insptr++), var4 = Gv_GetVarX(*insptr++);
974 
975                 Gv_SetVarX(var1, divscale(var2, var3, var4));
976                 continue;
977             }
978         case CON_SCALEVAR:
979             insptr++;
980             {
981                 int32_t var1 = *insptr++, var2 = Gv_GetVarX(*insptr++);
982                 int32_t var3 = Gv_GetVarX(*insptr++), var4 = Gv_GetVarX(*insptr++);
983 
984                 Gv_SetVarX(var1, scale(var2, var3, var4));
985                 continue;
986             }
987 
988 // *** if & while
989         case CON_IFVARVARAND:
990             insptr++;
991             {
992                 int32_t j = Gv_GetVarX(*insptr++);
993                 j &= Gv_GetVarX(*insptr++);
994                 insptr--;
995                 VM_DoConditional(j);
996             }
997             continue;
998 
999         case CON_IFVARVAROR:
1000             insptr++;
1001             {
1002                 int32_t j = Gv_GetVarX(*insptr++);
1003                 j |= Gv_GetVarX(*insptr++);
1004                 insptr--;
1005                 VM_DoConditional(j);
1006             }
1007             continue;
1008 
1009         case CON_IFVARVARXOR:
1010             insptr++;
1011             {
1012                 int32_t j = Gv_GetVarX(*insptr++);
1013                 j ^= Gv_GetVarX(*insptr++);
1014                 insptr--;
1015                 VM_DoConditional(j);
1016             }
1017             continue;
1018 
1019         case CON_IFVARVAREITHER:
1020             insptr++;
1021             {
1022                 int32_t j = Gv_GetVarX(*insptr++);
1023                 int32_t l = Gv_GetVarX(*insptr++);
1024                 insptr--;
1025                 VM_DoConditional(j || l);
1026             }
1027             continue;
1028 
1029         case CON_IFVARVARBOTH:
1030             insptr++;
1031             {
1032                 int32_t j = Gv_GetVarX(*insptr++);
1033                 int32_t l = Gv_GetVarX(*insptr++);
1034                 insptr--;
1035                 VM_DoConditional(j && l);
1036             }
1037             continue;
1038 
1039         case CON_IFVARVARN:
1040             insptr++;
1041             {
1042                 int32_t j = Gv_GetVarX(*insptr++);
1043                 j = (j != Gv_GetVarX(*insptr++));
1044                 insptr--;
1045                 VM_DoConditional(j);
1046             }
1047             continue;
1048 
1049         case CON_IFVARVARE:
1050             insptr++;
1051             {
1052                 int32_t j = Gv_GetVarX(*insptr++);
1053                 j = (j == Gv_GetVarX(*insptr++));
1054                 insptr--;
1055                 VM_DoConditional(j);
1056             }
1057             continue;
1058 
1059         case CON_IFVARVARG:
1060             insptr++;
1061             {
1062                 int32_t j = Gv_GetVarX(*insptr++);
1063                 j = (j > Gv_GetVarX(*insptr++));
1064                 insptr--;
1065                 VM_DoConditional(j);
1066             }
1067             continue;
1068 
1069         case CON_IFVARVARGE:
1070             insptr++;
1071             {
1072                 int32_t j = Gv_GetVarX(*insptr++);
1073                 j = (j >= Gv_GetVarX(*insptr++));
1074                 insptr--;
1075                 VM_DoConditional(j);
1076             }
1077             continue;
1078 
1079         case CON_IFVARVARL:
1080             insptr++;
1081             {
1082                 int32_t j = Gv_GetVarX(*insptr++);
1083                 j = (j < Gv_GetVarX(*insptr++));
1084                 insptr--;
1085                 VM_DoConditional(j);
1086             }
1087             continue;
1088 
1089         case CON_IFVARVARLE:
1090             insptr++;
1091             {
1092                 int32_t j = Gv_GetVarX(*insptr++);
1093                 j = (j <= Gv_GetVarX(*insptr++));
1094                 insptr--;
1095                 VM_DoConditional(j);
1096             }
1097             continue;
1098 
1099         case CON_IFVARVARA:
1100             insptr++;
1101             {
1102                 int32_t j = Gv_GetVarX(*insptr++);
1103                 j = ((uint32_t)j > (uint32_t)Gv_GetVarX(*insptr++));
1104                 insptr--;
1105                 VM_DoConditional(j);
1106             }
1107             continue;
1108 
1109         case CON_IFVARVARAE:
1110             insptr++;
1111             {
1112                 int32_t j = Gv_GetVarX(*insptr++);
1113                 j = ((uint32_t)j >= (uint32_t)Gv_GetVarX(*insptr++));
1114                 insptr--;
1115                 VM_DoConditional(j);
1116             }
1117             continue;
1118 
1119         case CON_IFVARVARB:
1120             insptr++;
1121             {
1122                 int32_t j = Gv_GetVarX(*insptr++);
1123                 j = ((uint32_t)j < (uint32_t)Gv_GetVarX(*insptr++));
1124                 insptr--;
1125                 VM_DoConditional(j);
1126             }
1127             continue;
1128 
1129         case CON_IFVARVARBE:
1130             insptr++;
1131             {
1132                 int32_t j = Gv_GetVarX(*insptr++);
1133                 j = ((uint32_t)j <= (uint32_t)Gv_GetVarX(*insptr++));
1134                 insptr--;
1135                 VM_DoConditional(j);
1136             }
1137             continue;
1138 
1139         case CON_IFVARE:
1140             insptr++;
1141             {
1142                 int32_t j=Gv_GetVarX(*insptr++);
1143                 VM_DoConditional(j == *insptr);
1144             }
1145             continue;
1146 
1147         case CON_IFVARN:
1148             insptr++;
1149             {
1150                 int32_t j=Gv_GetVarX(*insptr++);
1151                 VM_DoConditional(j != *insptr);
1152             }
1153             continue;
1154 
1155         case CON_WHILEVARN:
1156         {
1157             instype *savedinsptr=insptr+2;
1158             int32_t j;
1159             do
1160             {
1161                 insptr=savedinsptr;
1162                 j = (Gv_GetVarX(*(insptr-1)) != *insptr);
1163                 VM_DoConditional(j);
1164             }
1165             while (j && !vm.flags);
1166             vm.flags &= ~VMFLAG_BREAK;
1167             continue;
1168         }
1169 
1170         case CON_WHILEVARL:
1171         {
1172             instype *savedinsptr=insptr+2;
1173             int32_t j;
1174             do
1175             {
1176                 insptr=savedinsptr;
1177                 j = (Gv_GetVarX(*(insptr-1)) < *insptr);
1178                 VM_DoConditional(j);
1179             }
1180             while (j && !vm.flags);
1181             vm.flags &= ~VMFLAG_BREAK;
1182             continue;
1183         }
1184 
1185         case CON_WHILEVARVARN:
1186         {
1187             int32_t j;
1188             instype *savedinsptr=insptr+2;
1189             do
1190             {
1191                 insptr=savedinsptr;
1192                 j = Gv_GetVarX(*(insptr-1));
1193                 j = (j != Gv_GetVarX(*insptr++));
1194                 insptr--;
1195                 VM_DoConditional(j);
1196             }
1197             while (j && !vm.flags);
1198             vm.flags &= ~VMFLAG_BREAK;
1199             continue;
1200         }
1201 
1202         case CON_WHILEVARVARL:
1203         {
1204             int32_t j;
1205             instype *savedinsptr=insptr+2;
1206             do
1207             {
1208                 insptr=savedinsptr;
1209                 j = Gv_GetVarX(*(insptr-1));
1210                 j = (j < Gv_GetVarX(*insptr++));
1211                 insptr--;
1212                 VM_DoConditional(j);
1213             }
1214             while (j && !vm.flags);
1215             vm.flags &= ~VMFLAG_BREAK;
1216             continue;
1217         }
1218 
1219         case CON_COLLECTSECTORS:
1220             insptr++;
1221             {
1222                 const int32_t aridx=*insptr++, startsectnum=Gv_GetVarX(*insptr++);
1223                 const int32_t numsectsVar=*insptr++, state=*insptr++;
1224 
1225                 if (CheckArray(aridx))
1226                     continue;
1227 
1228                 gamearray_t *const gar = &aGameArrays[aridx];
1229                 Bassert((gar->dwFlags & (GAMEARRAY_READONLY|GAMEARRAY_VARSIZE)) == 0);
1230 
1231                 const int32_t o_g_st=vm.g_st, arsize = gar->size;
1232                 instype *const end=insptr;
1233                 int32_t sectcnt, numsects=0;
1234 
1235                 // XXX: relies on -fno-strict-aliasing
1236                 int16_t *const sectlist = (int16_t *)gar->vals;  // actually an int32_t array
1237                 int32_t *const sectlist32 = (int32_t *)sectlist;
1238 
1239                 int32_t j, startwall, endwall, ns;
1240                 static uint8_t sectbitmap[MAXSECTORS>>3];
1241 
1242                 X_ERROR_INVALIDSECT(startsectnum);
1243                 if (arsize < numsectors)
1244                 {
1245                     M32_ERROR("Array size must be at least numsectors (=%d) for collecting!",
1246                               numsectors);
1247                     continue;
1248                 }
1249 
1250                 // collect!
1251                 bfirst_search_init(sectlist, sectbitmap, &numsects, MAXSECTORS, startsectnum);
1252 
1253                 for (sectcnt=0; sectcnt<numsects; sectcnt++)
1254                     for (WALLS_OF_SECTOR(sectlist[sectcnt], j))
1255                         if ((ns=wall[j].nextsector) >= 0 && wall[j].nextsector<numsectors)
1256                         {
1257                             if (sectbitmap[ns>>3]&(1<<(ns&7)))
1258                                 continue;
1259                             vm.g_st = 1+MAXEVENTS+state;
1260                             insptr = apScript + statesinfo[state].ofs;
1261                             g_iReturnVar = ns;
1262                             VM_Execute(0);
1263                             if (g_iReturnVar)
1264                                 bfirst_search_try(sectlist, sectbitmap, &numsects, wall[j].nextsector);
1265                         }
1266 
1267                 // short->int sector list
1268                 for (j=numsects-1; j>=0; j--)
1269                     sectlist32[j] = sectlist[j];
1270 
1271                 Gv_SetVarX(numsectsVar, numsects);
1272                 g_iReturnVar = 0;
1273 
1274                 // restore some VM state
1275                 vm.g_st = o_g_st;
1276                 insptr = end;
1277             }
1278             continue;
1279 
1280         case CON_SORT:
1281             insptr++;
1282             {
1283                 const int32_t aridx=*insptr++, count=Gv_GetVarX(*insptr++), state=*insptr++;
1284                 const int32_t o_g_st = vm.g_st;
1285                 instype *const end = insptr;
1286 
1287                 if (CheckArray(aridx))
1288                     continue;
1289 
1290                 if (count <= 0)
1291                     continue;
1292 
1293                 gamearray_t *const gar = &aGameArrays[aridx];
1294                 Bassert((gar->dwFlags & (GAMEARRAY_READONLY|GAMEARRAY_VARSIZE)) == 0);
1295 
1296                 if (count > gar->size)
1297                 {
1298                     M32_ERROR("Count of elements to sort (%d) exceeds array size (%d)!",
1299                               count, gar->size);
1300                     continue;
1301                 }
1302 
1303                 if (state < 0)
1304                 {
1305                     qsort(gar->vals, count, sizeof(int32_t), X_DoSortDefault);
1306                 }
1307                 else
1308                 {
1309                     x_sortingstateptr = apScript + statesinfo[state].ofs;
1310                     vm.g_st = 1+MAXEVENTS+state;
1311                     qsort(gar->vals, count, sizeof(int32_t), X_DoSort);
1312                     vm.g_st = o_g_st;
1313                     insptr = end;
1314                 }
1315             }
1316             continue;
1317 
1318         case CON_FOR:  // special-purpose iteration
1319             insptr++;
1320             {
1321                 const int32_t var = *insptr++, how = *insptr++;
1322                 const int32_t parm2 = how<=ITER_DRAWNSPRITES ? 0 : Gv_GetVarX(*insptr++);
1323                 instype *const end = insptr + *insptr, *const beg = ++insptr;
1324                 const int32_t vm_i_bak = vm.spriteNum;
1325                 uspritetype *const vm_sp_bak = vm.pUSprite;
1326 
1327                 if (vm.flags&VMFLAG_ERROR)
1328                     continue;
1329 
1330                 switch (how)
1331                 {
1332                 case ITER_ALLSPRITES:
1333                     for (bssize_t jj=0; jj<MAXSPRITES && !vm.flags; jj++)
1334                     {
1335                         if (sprite[jj].statnum == MAXSTATUS)
1336                             continue;
1337                         Gv_SetVarX(var, jj);
1338                         vm.spriteNum = jj;
1339                         vm.pSprite = &sprite[jj];
1340                         insptr = beg;
1341                         VM_Execute(1);
1342                     }
1343                     break;
1344                 case ITER_ALLSECTORS:
1345                     for (bssize_t jj=0; jj<numsectors && !vm.flags; jj++)
1346                     {
1347                         Gv_SetVarX(var, jj);
1348                         insptr = beg;
1349                         VM_Execute(1);
1350                     }
1351                     break;
1352                 case ITER_ALLWALLS:
1353                     for (bssize_t jj=0; jj<numwalls && !vm.flags; jj++)
1354                     {
1355                         Gv_SetVarX(var, jj);
1356                         insptr = beg;
1357                         VM_Execute(1);
1358                     }
1359                     break;
1360                 case ITER_ACTIVELIGHTS:
1361 #ifdef POLYMER
1362                     for (bssize_t jj=0; jj<PR_MAXLIGHTS; jj++)
1363                     {
1364                         if (!prlights[jj].flags.active)
1365                             continue;
1366 
1367                         Gv_SetVarX(var, jj);
1368                         insptr = beg;
1369                         VM_Execute(1);
1370                     }
1371 #else
1372                     M32_ERROR("Polymer not compiled in, iteration over lights forbidden.");
1373 #endif
1374                     break;
1375 
1376                 case ITER_SELSPRITES:
1377                     for (bssize_t ii=0; ii<highlightcnt && !vm.flags; ii++)
1378                     {
1379                         int jj = highlight[ii];
1380                         if (jj&0xc000)
1381                         {
1382                             jj &= (MAXSPRITES-1);
1383                             Gv_SetVarX(var, jj);
1384                             vm.spriteNum = jj;
1385                             vm.pSprite = &sprite[jj];
1386                             insptr = beg;
1387                             VM_Execute(1);
1388                         }
1389                     }
1390                     break;
1391                 case ITER_SELSECTORS:
1392                     for (bssize_t ii=0; ii<highlightsectorcnt && !vm.flags; ii++)
1393                     {
1394                         int jj=highlightsector[ii];
1395                         Gv_SetVarX(var, jj);
1396                         insptr = beg;
1397                         VM_Execute(1);
1398                     }
1399                     break;
1400                 case ITER_SELWALLS:
1401                     for (bssize_t ii=0; ii<highlightcnt && !vm.flags; ii++)
1402                     {
1403                         int jj=highlight[ii];
1404                         if (jj&0xc000)
1405                             continue;
1406                         Gv_SetVarX(var, jj);
1407                         insptr = beg;
1408                         VM_Execute(1);
1409                     }
1410                     break;
1411                 case ITER_DRAWNSPRITES:
1412                 {
1413                     uspritetype lastSpriteBackup;
1414                     auto const lastSpritePtr = (uspritetype *)&sprite[MAXSPRITES-1];
1415 
1416                     // Back up sprite MAXSPRITES-1.
1417                     Bmemcpy(&lastSpriteBackup, lastSpritePtr, sizeof(uspritetype));
1418 
1419                     EDUKE32_STATIC_ASSERT(sizeof(uspritetype) == sizeof(tspritetype)); // see TSPRITE_SIZE
1420                     for (bssize_t ii=0; ii<spritesortcnt && !vm.flags; ii++)
1421                     {
1422                         vm.pUSprite = lastSpritePtr;
1423                         Bmemcpy(lastSpritePtr, &tsprite[ii], sizeof(tspritetype));
1424 
1425                         Gv_SetVarX(var, ii);
1426                         insptr = beg;
1427                         VM_Execute(1);
1428 
1429                         // Copy over potentially altered tsprite.
1430                         Bmemcpy(&tsprite[ii], lastSpritePtr, sizeof(tspritetype));
1431                     }
1432 
1433                     // Restore sprite MAXSPRITES-1.
1434                     Bmemcpy(lastSpritePtr, &lastSpriteBackup, sizeof(uspritetype));
1435                     break;
1436                 }
1437                 case ITER_SPRITESOFSECTOR:
1438                     if (parm2 < 0 || parm2 >= MAXSECTORS)
1439                         goto badindex;
1440                     for (bssize_t jj=headspritesect[parm2]; jj>=0 && !vm.flags; jj=nextspritesect[jj])
1441                     {
1442                         Gv_SetVarX(var, jj);
1443                         vm.spriteNum = jj;
1444                         vm.pSprite = &sprite[jj];
1445                         insptr = beg;
1446                         VM_Execute(1);
1447                     }
1448                     break;
1449                 case ITER_WALLSOFSECTOR:
1450                     if (parm2 < 0 || parm2 >= MAXSECTORS)
1451                         goto badindex;
1452                     for (bssize_t jj=sector[parm2].wallptr, endwall=jj+sector[parm2].wallnum-1;
1453                             jj<=endwall && !vm.flags; jj++)
1454                     {
1455                         Gv_SetVarX(var, jj);
1456                         insptr = beg;
1457                         VM_Execute(1);
1458                     }
1459                     break;
1460                 case ITER_LOOPOFWALL:
1461                     if (parm2 < 0 || parm2 >= numwalls)
1462                         goto badindex;
1463                     {
1464                         int jj = parm2;
1465                         do
1466                         {
1467                             Gv_SetVarX(var, jj);
1468                             insptr = beg;
1469                             VM_Execute(1);
1470                             jj = wall[jj].point2;
1471                         }
1472                         while (jj != parm2 && !vm.flags);
1473                     }
1474                     break;
1475                 case ITER_RANGE:
1476                     for (bssize_t jj=0; jj<parm2 && !vm.flags; jj++)
1477                     {
1478                         Gv_SetVarX(var, jj);
1479                         insptr = beg;
1480                         VM_Execute(1);
1481                     }
1482                     break;
1483                 default:
1484                     M32_ERROR("Unknown iteration type %d!", how);
1485                     continue;
1486 badindex:
1487                     OSD_Printf("%sLine %d, %s %s: index %d out of range!\n", osd->draw.highlight,
1488                         g_errorLineNum,keyw[g_tw], iter_tokens[how].token, parm2);
1489                     vm.flags |= VMFLAG_ERROR;
1490                     continue;
1491                 }
1492                 vm.spriteNum = vm_i_bak;
1493                 vm.pUSprite  = vm_sp_bak;
1494                 vm.flags     &= ~VMFLAG_BREAK;
1495                 insptr       = end;
1496             }
1497             continue;
1498 
1499         case CON_IFVARAND:
1500             insptr++;
1501             {
1502                 int32_t j=Gv_GetVarX(*insptr++);
1503                 VM_DoConditional(j & *insptr);
1504             }
1505             continue;
1506 
1507         case CON_IFVAROR:
1508             insptr++;
1509             {
1510                 int32_t j=Gv_GetVarX(*insptr++);
1511                 VM_DoConditional(j | *insptr);
1512             }
1513             continue;
1514 
1515         case CON_IFVARXOR:
1516             insptr++;
1517             {
1518                 int32_t j=Gv_GetVarX(*insptr++);
1519                 VM_DoConditional(j ^ *insptr);
1520             }
1521             continue;
1522 
1523         case CON_IFVAREITHER:
1524             insptr++;
1525             {
1526                 int32_t j=Gv_GetVarX(*insptr++);
1527                 VM_DoConditional(j || *insptr);
1528             }
1529             continue;
1530 
1531         case CON_IFVARBOTH:
1532             insptr++;
1533             {
1534                 int32_t j=Gv_GetVarX(*insptr++);
1535                 VM_DoConditional(j && *insptr);
1536             }
1537             continue;
1538 
1539         case CON_IFVARG:
1540             insptr++;
1541             {
1542                 int32_t j=Gv_GetVarX(*insptr++);
1543                 VM_DoConditional(j > *insptr);
1544             }
1545             continue;
1546 
1547         case CON_IFVARGE:
1548             insptr++;
1549             {
1550                 int32_t j=Gv_GetVarX(*insptr++);
1551                 VM_DoConditional(j >= *insptr);
1552             }
1553             continue;
1554 
1555         case CON_IFVARL:
1556             insptr++;
1557             {
1558                 int32_t j=Gv_GetVarX(*insptr++);
1559                 VM_DoConditional(j < *insptr);
1560             }
1561             continue;
1562 
1563         case CON_IFVARLE:
1564             insptr++;
1565             {
1566                 int32_t j=Gv_GetVarX(*insptr++);
1567                 VM_DoConditional(j <= *insptr);
1568             }
1569             continue;
1570 
1571         case CON_IFVARA:
1572             insptr++;
1573             {
1574                 int32_t j=Gv_GetVarX(*insptr++);
1575                 VM_DoConditional((uint32_t)j > (uint32_t)*insptr);
1576             }
1577             continue;
1578 
1579         case CON_IFVARAE:
1580             insptr++;
1581             {
1582                 int32_t j=Gv_GetVarX(*insptr++);
1583                 VM_DoConditional((uint32_t)j >= (uint32_t)*insptr);
1584             }
1585             continue;
1586 
1587         case CON_IFVARB:
1588             insptr++;
1589             {
1590                 int32_t j=Gv_GetVarX(*insptr++);
1591                 VM_DoConditional((uint32_t)j < (uint32_t)*insptr);
1592             }
1593             continue;
1594 
1595         case CON_IFVARBE:
1596             insptr++;
1597             {
1598                 int32_t j=Gv_GetVarX(*insptr++);
1599                 VM_DoConditional((uint32_t)j <= (uint32_t)*insptr);
1600             }
1601             continue;
1602 
1603         case CON_IFRND:
1604             VM_DoConditional(rnd(Gv_GetVarX(*(++insptr))));
1605             continue;
1606 
1607         case CON_IFHITKEY:
1608         case CON_IFHOLDKEY:
1609         case CON_RESETKEY:
1610         case CON_SETKEY:
1611             insptr++;
1612             {
1613                 int32_t key=Gv_GetVarX(*insptr);
1614                 if (key<0 || key >= (int32_t)ARRAY_SIZE(keystatus))
1615                 {
1616                     M32_ERROR("Invalid key %d!", key);
1617                     continue;
1618                 }
1619 
1620                 if (tw == CON_IFHITKEY || tw == CON_IFHOLDKEY)
1621                     VM_DoConditional(keystatus[key]);
1622                 else
1623                     insptr++;
1624 
1625                 if (tw != CON_IFHOLDKEY)
1626                 {
1627                     if (!(key==0 || key==KEYSC_ESC || key==KEYSC_TILDE || key==KEYSC_gENTER ||
1628                             key==KEYSC_LALT || key==KEYSC_RALT || key==KEYSC_LCTRL || key==KEYSC_RCTRL ||
1629                             key==KEYSC_LSHIFT || key==KEYSC_RSHIFT))
1630                         keystatus[key] = (tw==CON_SETKEY);
1631                 }
1632             }
1633             continue;
1634 
1635         case CON_IFEITHERALT:
1636             VM_DoConditional(keystatus[KEYSC_LALT]||keystatus[KEYSC_RALT]);
1637             continue;
1638 
1639         case CON_IFEITHERCTRL:
1640             VM_DoConditional(keystatus[KEYSC_LCTRL]||keystatus[KEYSC_RCTRL]);
1641             continue;
1642 
1643         case CON_IFEITHERSHIFT:
1644             VM_DoConditional(keystatus[KEYSC_LSHIFT]||keystatus[KEYSC_RSHIFT]);
1645             continue;
1646 
1647 // vvv CURSPR
1648         case CON_IFSPRITEPAL:
1649             insptr++;
1650             X_ERROR_INVALIDSP();
1651             VM_DoConditional(vm.pSprite->pal == Gv_GetVarX(*insptr));
1652             continue;
1653 
1654         case CON_IFHIGHLIGHTED:
1655             insptr++;
1656             {
1657                 int32_t id=*insptr++, index=Gv_GetVarX(*insptr);
1658 
1659                 if (index<0 || (id==M32_SPRITE_VAR_ID && index>=MAXSPRITES) || (id==M32_WALL_VAR_ID && index>=numwalls))
1660                 {
1661                     M32_ERROR("%s index %d out of range!", id==M32_SPRITE_VAR_ID?"Sprite":"Wall", index);
1662                     continue;
1663                 }
1664 
1665                 if (id==M32_SPRITE_VAR_ID)
1666                     VM_DoConditional(show2dsprite[index>>3]&(1<<(index&7)));
1667                 else
1668                     VM_DoConditional(show2dwall[index>>3]&(1<<(index&7)));
1669             }
1670             continue;
1671 
1672         case CON_IFANGDIFFL:
1673             insptr++;
1674             {
1675                 int32_t j;
1676                 X_ERROR_INVALIDSP();
1677                 j = klabs(G_GetAngleDelta(ang, vm.pSprite->ang));
1678                 VM_DoConditional(j <= Gv_GetVarX(*insptr));
1679             }
1680             continue;
1681 
1682         case CON_IFAWAYFROMWALL:
1683         {
1684             int16_t s1;
1685             int32_t j = 0;
1686 
1687             X_ERROR_INVALIDSP();
1688             s1 = vm.pSprite->sectnum;
1689             updatesector(vm.pSprite->x+108,vm.pSprite->y+108,&s1);
1690             if (s1 == vm.pSprite->sectnum)
1691             {
1692                 updatesector(vm.pSprite->x-108,vm.pSprite->y-108,&s1);
1693                 if (s1 == vm.pSprite->sectnum)
1694                 {
1695                     updatesector(vm.pSprite->x+108,vm.pSprite->y-108,&s1);
1696                     if (s1 == vm.pSprite->sectnum)
1697                     {
1698                         updatesector(vm.pSprite->x-108,vm.pSprite->y+108,&s1);
1699                         if (s1 == vm.pSprite->sectnum)
1700                             j = 1;
1701                     }
1702                 }
1703             }
1704             VM_DoConditional(j);
1705         }
1706         continue;
1707 
1708         case CON_IFCANSEE:
1709         {
1710             int32_t j;
1711 
1712             X_ERROR_INVALIDSP();
1713             j = cansee(vm.pSprite->x,vm.pSprite->y,vm.pSprite->z/*-((krand()&41)<<8)*/,vm.pSprite->sectnum,
1714                        pos.x, pos.y, pos.z /*-((krand()&41)<<8)*/, cursectnum);
1715             VM_DoConditional(j);
1716         }
1717         continue;
1718 
1719         case CON_IFONWATER:
1720             X_ERROR_INVALIDSP();
1721             VM_DoConditional(sector[vm.pSprite->sectnum].lotag == 1 && klabs(vm.pSprite->z-sector[vm.pSprite->sectnum].floorz) < (32<<8));
1722             continue;
1723 
1724         case CON_IFINWATER:
1725             X_ERROR_INVALIDSP();
1726             VM_DoConditional(sector[vm.pSprite->sectnum].lotag == 2);
1727             continue;
1728 
1729         case CON_IFACTOR:
1730             insptr++;
1731             X_ERROR_INVALIDSP();
1732             VM_DoConditional(vm.pSprite->picnum == Gv_GetVarX(*insptr));
1733             continue;
1734 
1735         case CON_IFINSIDE:
1736             insptr++;
1737             {
1738                 int32_t x=Gv_GetVarX(*insptr++), y=Gv_GetVarX(*insptr++), sectnum=Gv_GetVarX(*insptr++), res;
1739 
1740                 res = inside(x, y, sectnum);
1741                 if (res == -1)
1742                 {
1743                     M32_ERROR("Sector index %d out of range!", sectnum);
1744                     continue;
1745                 }
1746                 insptr--;
1747                 VM_DoConditional(res);
1748             }
1749             continue;
1750 
1751         case CON_IFOUTSIDE:
1752             X_ERROR_INVALIDSP();
1753             VM_DoConditional(sector[vm.pSprite->sectnum].ceilingstat&1);
1754             continue;
1755 
1756         case CON_IFPDISTL:
1757             insptr++;
1758             {
1759                 X_ERROR_INVALIDSP();
1760                 VM_DoConditional(dist((spritetype *)&pos, vm.pSprite) < Gv_GetVarX(*insptr));
1761             }
1762             continue;
1763 
1764         case CON_IFPDISTG:
1765             insptr++;
1766             {
1767                 X_ERROR_INVALIDSP();
1768                 VM_DoConditional(dist((spritetype *)&pos, vm.pSprite) > Gv_GetVarX(*insptr));
1769             }
1770             continue;
1771 // ^^^
1772 
1773 // *** BUILD functions
1774         case CON_INSERTSPRITE:
1775             insptr++;
1776             {
1777                 int32_t dasectnum = Gv_GetVarX(*insptr++), ret;
1778 
1779                 X_ERROR_INVALIDSECT(dasectnum);
1780                 if (Numsprites >= MAXSPRITES)
1781                 {
1782                     M32_ERROR("Maximum number of sprites reached.");
1783                     continue;
1784                 }
1785 
1786                 ret = insertsprite(dasectnum, 0);
1787                 vm.spriteNum = ret;
1788                 vm.pSprite = &sprite[ret];
1789             }
1790             continue;
1791 
1792         case CON_DUPSPRITE:
1793         case CON_TDUPSPRITE:
1794             insptr++;
1795             {
1796                 int32_t ospritenum = Gv_GetVarX(*insptr++), nspritenum;
1797 
1798                 if (ospritenum<0 || ospritenum>=MAXSPRITES || sprite[ospritenum].statnum==MAXSTATUS)
1799                 {
1800                     M32_ERROR("Tried to duplicate nonexistent sprite %d", ospritenum);
1801                 }
1802                 if ((tw==CON_DUPSPRITE && Numsprites >= MAXSPRITES) ||
1803                         (tw==CON_DUPSPRITE && spritesortcnt >= maxspritesonscreen))
1804                 {
1805                     M32_ERROR("Maximum number of sprites reached.");
1806                 }
1807 
1808                 if (vm.flags&VMFLAG_ERROR)
1809                     continue;
1810 
1811                 if (tw==CON_DUPSPRITE)
1812                 {
1813                     nspritenum = insertsprite(sprite[ospritenum].sectnum, sprite[ospritenum].statnum);
1814 
1815                     if (nspritenum < 0)
1816                     {
1817                         M32_ERROR("Internal error.");
1818                         continue;
1819                     }
1820 
1821                     Bmemcpy(&sprite[nspritenum], &sprite[ospritenum], sizeof(spritetype));
1822                     vm.spriteNum = nspritenum;
1823                     vm.pSprite = &sprite[nspritenum];
1824                 }
1825                 else
1826                 {
1827                     tspriteptr_t tsp = renderAddTSpriteFromSprite(ospritenum);
1828                     vm.spriteNum = -1;
1829 
1830                     EDUKE32_STATIC_ASSERT(sizeof(uspritetype) == sizeof(tspritetype)); // see TSPRITE_SIZE
1831                     vm.pUSprite = (uspriteptr_t)tsp;
1832                 }
1833             }
1834             continue;
1835 
1836         case CON_DELETESPRITE:
1837             insptr++;
1838             {
1839                 int32_t daspritenum = Gv_GetVarX(*insptr++), ret;
1840 
1841                 X_ERROR_INVALIDSPRI(daspritenum);
1842                 ret = deletesprite(daspritenum);
1843                 g_iReturnVar = ret;
1844             }
1845             continue;
1846 
1847         case CON_GETSPRITELINKTYPE:
1848             insptr++;
1849             {
1850                 int32_t spritenum=Gv_GetVarX(*insptr++), resvar = *insptr++;
1851 
1852                 X_ERROR_INVALIDSPRI(spritenum);
1853                 Gv_SetVarX(resvar, taglab_linktags(1, spritenum));
1854             }
1855             continue;
1856 
1857         case CON_LASTWALL:
1858             insptr++;
1859             {
1860                 int32_t dapoint = Gv_GetVarX(*insptr++), resvar=*insptr++;
1861 
1862                 if (dapoint<0 || dapoint>=numwalls)
1863                 {
1864                     M32_ERROR("Invalid wall %d", dapoint);
1865                     continue;
1866                 }
1867 
1868                 Gv_SetVarX(resvar, lastwall(dapoint));
1869             }
1870             continue;
1871 
1872         case CON_GETZRANGE:
1873             insptr++;
1874             {
1875                 vec3_t vect;
1876 
1877                 vect.x = Gv_GetVarX(*insptr++);
1878                 vect.y = Gv_GetVarX(*insptr++);
1879                 vect.z = Gv_GetVarX(*insptr++);
1880 
1881                 {
1882                     int32_t sectnum=Gv_GetVarX(*insptr++);
1883                     int32_t ceilzvar=*insptr++, ceilhitvar=*insptr++, florzvar=*insptr++, florhitvar=*insptr++;
1884                     int32_t walldist=Gv_GetVarX(*insptr++), clipmask=Gv_GetVarX(*insptr++);
1885                     int32_t ceilz, ceilhit, florz, florhit;
1886 
1887                     X_ERROR_INVALIDSECT(sectnum);
1888                     getzrange(&vect, sectnum, &ceilz, &ceilhit, &florz, &florhit, walldist, clipmask);
1889                     Gv_SetVarX(ceilzvar, ceilz);
1890                     Gv_SetVarX(ceilhitvar, ceilhit);
1891                     Gv_SetVarX(florzvar, florz);
1892                     Gv_SetVarX(florhitvar, florhit);
1893                 }
1894                 continue;
1895             }
1896 
1897         case CON_CALCHYPOTENUSE:
1898             insptr++;
1899             {
1900                 int32_t retvar=*insptr++;
1901                 int64_t dax=Gv_GetVarX(*insptr++), day=Gv_GetVarX(*insptr++);
1902                 int64_t hypsq = dax*dax + day*day;
1903 
1904                 if (hypsq > (int64_t)INT32_MAX)
1905                     Gv_SetVarX(retvar, (int32_t)sqrt((double)hypsq));
1906                 else
1907                     Gv_SetVarX(retvar, ksqrt((uint32_t)hypsq));
1908 
1909                 continue;
1910             }
1911 
1912         case CON_LINEINTERSECT:
1913         case CON_RAYINTERSECT:
1914             insptr++;
1915             {
1916                 int32_t x1=Gv_GetVarX(*insptr++), y1=Gv_GetVarX(*insptr++), z1=Gv_GetVarX(*insptr++);
1917                 int32_t x2=Gv_GetVarX(*insptr++), y2=Gv_GetVarX(*insptr++), z2=Gv_GetVarX(*insptr++);
1918                 int32_t x3=Gv_GetVarX(*insptr++), y3=Gv_GetVarX(*insptr++), x4=Gv_GetVarX(*insptr++), y4=Gv_GetVarX(*insptr++);
1919                 int32_t intxvar=*insptr++, intyvar=*insptr++, intzvar=*insptr++, retvar=*insptr++;
1920                 int32_t intx, inty, intz, ret;
1921 
1922                 if (tw==CON_LINEINTERSECT)
1923                     ret = lintersect(x1, y1, z1, x2, y2, z2, x3, y3, x4, y4, &intx, &inty, &intz);
1924                 else
1925                     ret = rayintersect(x1, y1, z1, x2, y2, z2, x3, y3, x4, y4, &intx, &inty, &intz);
1926 
1927                 Gv_SetVarX(retvar, ret);
1928                 if (ret)
1929                 {
1930                     Gv_SetVarX(intxvar, intx);
1931                     Gv_SetVarX(intyvar, inty);
1932                     Gv_SetVarX(intzvar, intz);
1933                 }
1934 
1935                 continue;
1936             }
1937 
1938         case CON_CLIPMOVE:
1939             insptr++;
1940             {
1941                 vec3_t vect;
1942                 int32_t retvar=*insptr++, xvar=*insptr++, yvar=*insptr++, z=Gv_GetVarX(*insptr++), sectnumvar=*insptr++;
1943                 int32_t xvect=Gv_GetVarX(*insptr++), yvect=Gv_GetVarX(*insptr++);
1944                 int32_t walldist=Gv_GetVarX(*insptr++), floordist=Gv_GetVarX(*insptr++), ceildist=Gv_GetVarX(*insptr++);
1945                 int32_t clipmask=Gv_GetVarX(*insptr++);
1946                 int16_t sectnum;
1947 
1948                 vect.x = Gv_GetVarX(xvar);
1949                 vect.y = Gv_GetVarX(yvar);
1950                 vect.z = z;
1951                 sectnum = Gv_GetVarX(sectnumvar);
1952 
1953                 X_ERROR_INVALIDSECT(sectnum);
1954 
1955                 Gv_SetVarX(retvar, clipmove(&vect, &sectnum, xvect, yvect, walldist, floordist, ceildist, clipmask));
1956                 Gv_SetVarX(sectnumvar, sectnum);
1957                 Gv_SetVarX(xvar, vect.x);
1958                 Gv_SetVarX(yvar, vect.y);
1959 
1960                 continue;
1961             }
1962 
1963         case CON_HITSCAN:
1964             insptr++;
1965             {
1966                 vec3_t vect;
1967                 hitdata_t hit;
1968 
1969                 vect.x = Gv_GetVarX(*insptr++);
1970                 vect.y = Gv_GetVarX(*insptr++);
1971                 vect.z = Gv_GetVarX(*insptr++);
1972 
1973                 {
1974                     int32_t sectnum=Gv_GetVarX(*insptr++);
1975                     int32_t vx=Gv_GetVarX(*insptr++), vy=Gv_GetVarX(*insptr++), vz=Gv_GetVarX(*insptr++);
1976                     int32_t hitsectvar=*insptr++, hitwallvar=*insptr++, hitspritevar=*insptr++;
1977                     int32_t hitxvar=*insptr++, hityvar=*insptr++, hitzvar=*insptr++, cliptype=Gv_GetVarX(*insptr++);
1978 
1979                     X_ERROR_INVALIDSECT(sectnum);
1980                     hitscan((const vec3_t *)&vect, sectnum, vx, vy, vz, &hit, cliptype);
1981                     Gv_SetVarX(hitsectvar, hit.sect);
1982                     Gv_SetVarX(hitwallvar, hit.wall);
1983                     Gv_SetVarX(hitspritevar, hit.sprite);
1984                     Gv_SetVarX(hitxvar, hit.pos.x);
1985                     Gv_SetVarX(hityvar, hit.pos.y);
1986                     Gv_SetVarX(hitzvar, hit.pos.z);
1987                 }
1988                 continue;
1989             }
1990 
1991         case CON_CANSEE:
1992             insptr++;
1993             {
1994                 int32_t x1=Gv_GetVarX(*insptr++), y1=Gv_GetVarX(*insptr++), z1=Gv_GetVarX(*insptr++);
1995                 int32_t sect1=Gv_GetVarX(*insptr++);
1996                 int32_t x2=Gv_GetVarX(*insptr++), y2=Gv_GetVarX(*insptr++), z2=Gv_GetVarX(*insptr++);
1997                 int32_t sect2=Gv_GetVarX(*insptr++), rvar=*insptr++;
1998 
1999                 X_ERROR_INVALIDSECT(sect1);
2000                 X_ERROR_INVALIDSECT(sect2);
2001 
2002                 Gv_SetVarX(rvar, cansee(x1,y1,z1,sect1,x2,y2,z2,sect2));
2003                 continue;
2004             }
2005 
2006         case CON_ROTATEPOINT:
2007             insptr++;
2008             {
2009                 vec2_t pivot = { Gv_GetVarX(*insptr), Gv_GetVarX(*(insptr+1)) };
2010                 vec2_t p = { Gv_GetVarX(*(insptr+2)), Gv_GetVarX(*(insptr+3)) };
2011                 insptr += 4;
2012                 int32_t daang=Gv_GetVarX(*insptr++);
2013                 int32_t x2var=*insptr++, y2var=*insptr++;
2014                 vec2_t p2;
2015 
2016                 rotatepoint(pivot,p,daang,&p2);
2017                 Gv_SetVarX(x2var, p2.x);
2018                 Gv_SetVarX(y2var, p2.y);
2019                 continue;
2020             }
2021 
2022         case CON_NEARTAG:
2023             insptr++;
2024             {
2025                 // neartag(int32_t x, int32_t y, int32_t z, short sectnum, short ang,  //Starting position & angle
2026                 //         short *neartagsector,    //Returns near sector if sector[].tag != 0
2027                 //         short *neartagwall,      //Returns near wall if wall[].tag != 0
2028                 //         short *neartagsprite,    //Returns near sprite if sprite[].tag != 0
2029                 //         int32_t *neartaghitdist, //Returns actual distance to object (scale: 1024=largest grid size)
2030                 //         int32_t neartagrange,    //Choose maximum distance to scan (scale: 1024=largest grid size)
2031                 //         char tagsearch)          //1-lotag only, 2-hitag only, 3-lotag&hitag
2032 
2033                 int32_t x=Gv_GetVarX(*insptr++), y=Gv_GetVarX(*insptr++), z=Gv_GetVarX(*insptr++);
2034                 int32_t sectnum=Gv_GetVarX(*insptr++), ang=Gv_GetVarX(*insptr++);
2035                 int32_t neartagsectorvar=*insptr++, neartagwallvar=*insptr++, neartagspritevar=*insptr++, neartaghitdistvar=*insptr++;
2036                 int32_t neartagrange=Gv_GetVarX(*insptr++), tagsearch=Gv_GetVarX(*insptr++);
2037 
2038                 int16_t neartagsector, neartagwall, neartagsprite;
2039                 int32_t neartaghitdist;
2040 
2041                 X_ERROR_INVALIDSECT(sectnum);
2042                 neartag(x, y, z, sectnum, ang, &neartagsector, &neartagwall, &neartagsprite,
2043                         &neartaghitdist, neartagrange, tagsearch, NULL);
2044 
2045                 Gv_SetVarX(neartagsectorvar, neartagsector);
2046                 Gv_SetVarX(neartagwallvar, neartagwall);
2047                 Gv_SetVarX(neartagspritevar, neartagsprite);
2048                 Gv_SetVarX(neartaghitdistvar, neartaghitdist);
2049                 continue;
2050             }
2051 
2052         case CON_BSETSPRITE:  // was CON_SETSPRITE
2053             insptr++;
2054             {
2055                 int32_t spritenum = Gv_GetVarX(*insptr++);
2056                 vec3_t davector;
2057 
2058                 davector.x = Gv_GetVarX(*insptr++);
2059                 davector.y = Gv_GetVarX(*insptr++);
2060                 davector.z = Gv_GetVarX(*insptr++);
2061 
2062                 X_ERROR_INVALIDSPRI(spritenum);
2063                 setsprite(spritenum, &davector);
2064                 continue;
2065             }
2066 
2067         case CON_GETFLORZOFSLOPE:
2068         case CON_GETCEILZOFSLOPE:
2069             insptr++;
2070             {
2071                 int32_t sectnum = Gv_GetVarX(*insptr++), x = Gv_GetVarX(*insptr++), y = Gv_GetVarX(*insptr++);
2072                 int32_t var=*insptr++;
2073 
2074                 X_ERROR_INVALIDSECT(sectnum);
2075                 if (tw == CON_GETFLORZOFSLOPE)
2076                     Gv_SetVarX(var, getflorzofslope(sectnum,x,y));
2077                 else
2078                     Gv_SetVarX(var, getceilzofslope(sectnum,x,y));
2079                 continue;
2080             }
2081 
2082         case CON_ALIGNFLORSLOPE:
2083         case CON_ALIGNCEILSLOPE:
2084             insptr++;
2085             {
2086                 int32_t sectnum = Gv_GetVarX(*insptr++), x = Gv_GetVarX(*insptr++), y = Gv_GetVarX(*insptr++);
2087                 int32_t z=Gv_GetVarX(*insptr++);
2088 
2089                 X_ERROR_INVALIDSECT(sectnum);
2090                 if (tw == CON_ALIGNFLORSLOPE)
2091                     alignflorslope(sectnum, x,y,z);
2092                 else
2093                     alignceilslope(sectnum, x,y,z);
2094                 continue;
2095             }
2096 
2097 // CURSPR
2098         case CON_SETFIRSTWALL:
2099             insptr++;
2100             {
2101                 int32_t sect=Gv_GetVarX(*insptr++), wal=Gv_GetVarX(*insptr++);
2102 
2103                 X_ERROR_INVALIDSECT(sect);
2104                 setfirstwall(sect, wal);
2105             }
2106             continue;
2107 
2108         case CON_UPDATECURSECTNUM:
2109             insptr++;
2110             updatesectorz(pos.x, pos.y, pos.z, &cursectnum);
2111             continue;
2112 
2113         case CON_UPDATESECTOR:
2114         case CON_UPDATESECTORZ:
2115             insptr++;
2116             {
2117                 int32_t x=Gv_GetVarX(*insptr++), y=Gv_GetVarX(*insptr++);
2118                 int32_t z=(tw==CON_UPDATESECTORZ)?Gv_GetVarX(*insptr++):0;
2119                 int32_t var=*insptr++;
2120                 int16_t w;
2121 
2122                 X_ERROR_INVALIDCI();
2123                 w=sprite[vm.spriteNum].sectnum;
2124 
2125                 if (tw==CON_UPDATESECTOR) updatesector(x,y,&w);
2126                 else updatesectorz(x,y,z,&w);
2127 
2128                 Gv_SetVarX(var, w);
2129                 continue;
2130             }
2131 
2132         case CON_HEADSPRITESTAT:
2133             insptr++;
2134             {
2135                 int32_t i=*insptr++;
2136                 int32_t j=Gv_GetVarX(*insptr++);
2137                 if (j < 0 || j > MAXSTATUS)
2138                 {
2139                     M32_ERROR("invalid status list %d", j);
2140                     continue;
2141                 }
2142                 Gv_SetVarX(i,headspritestat[j]);
2143                 continue;
2144             }
2145 
2146         case CON_PREVSPRITESTAT:
2147             insptr++;
2148             {
2149                 int32_t i=*insptr++;
2150                 int32_t j=Gv_GetVarX(*insptr++);
2151 
2152                 X_ERROR_INVALIDSPRI(j);
2153                 Gv_SetVarX(i,prevspritestat[j]);
2154                 continue;
2155             }
2156 
2157         case CON_NEXTSPRITESTAT:
2158             insptr++;
2159             {
2160                 int32_t i=*insptr++;
2161                 int32_t j=Gv_GetVarX(*insptr++);
2162 
2163                 X_ERROR_INVALIDSPRI(j);
2164                 Gv_SetVarX(i,nextspritestat[j]);
2165                 continue;
2166             }
2167 
2168         case CON_HEADSPRITESECT:
2169             insptr++;
2170             {
2171                 int32_t i=*insptr++;
2172                 int32_t j=Gv_GetVarX(*insptr++);
2173 
2174                 X_ERROR_INVALIDSECT(j);
2175                 Gv_SetVarX(i,headspritesect[j]);
2176                 continue;
2177             }
2178 
2179         case CON_PREVSPRITESECT:
2180             insptr++;
2181             {
2182                 int32_t i=*insptr++;
2183                 int32_t j=Gv_GetVarX(*insptr++);
2184 
2185                 X_ERROR_INVALIDSPRI(j);
2186                 Gv_SetVarX(i,prevspritesect[j]);
2187                 continue;
2188             }
2189 
2190         case CON_NEXTSPRITESECT:
2191             insptr++;
2192             {
2193                 int32_t i=*insptr++;
2194                 int32_t j=Gv_GetVarX(*insptr++);
2195 
2196                 X_ERROR_INVALIDSPRI(j);
2197                 Gv_SetVarX(i,nextspritesect[j]);
2198                 continue;
2199             }
2200 
2201         case CON_CANSEESPR:
2202             insptr++;
2203             {
2204                 int32_t lVar1 = Gv_GetVarX(*insptr++), lVar2 = Gv_GetVarX(*insptr++), res;
2205 
2206                 if (lVar1<0 || lVar1>=MAXSPRITES || sprite[lVar1].statnum==MAXSTATUS)
2207                 {
2208                     M32_ERROR("Invalid sprite %d", lVar1);
2209                 }
2210                 if (lVar2<0 || lVar2>=MAXSPRITES || sprite[lVar2].statnum==MAXSTATUS)
2211                 {
2212                     M32_ERROR("Invalid sprite %d", lVar2);
2213                 }
2214 
2215                 if (vm.flags&VMFLAG_ERROR) res=0;
2216                 else res=cansee(sprite[lVar1].x,sprite[lVar1].y,sprite[lVar1].z,sprite[lVar1].sectnum,
2217                                     sprite[lVar2].x,sprite[lVar2].y,sprite[lVar2].z,sprite[lVar2].sectnum);
2218 
2219                 Gv_SetVarX(*insptr++, res);
2220                 continue;
2221             }
2222 
2223         case CON_CHANGESPRITESTAT:
2224         case CON_CHANGESPRITESECT:
2225             insptr++;
2226             {
2227                 int32_t i = Gv_GetVarX(*insptr++);
2228                 int32_t j = Gv_GetVarX(*insptr++);
2229 
2230                 X_ERROR_INVALIDSPRI(i);
2231                 if (j<0 || j >= (tw==CON_CHANGESPRITESTAT?MAXSTATUS:numsectors))
2232                 {
2233                     M32_ERROR("Invalid %s: %d", tw==CON_CHANGESPRITESTAT?"statnum":"sector", j);
2234                     continue;
2235                 }
2236 
2237                 if (tw == CON_CHANGESPRITESTAT)
2238                 {
2239                     if (sprite[i].statnum == j) continue;
2240                     changespritestat(i,j);
2241                 }
2242                 else
2243                 {
2244                     if (sprite[i].sectnum == j) continue;
2245                     changespritesect(i,j);
2246                 }
2247                 continue;
2248             }
2249 
2250         case CON_DRAGPOINT:
2251             insptr++;
2252             {
2253                 int32_t wallnum = Gv_GetVarX(*insptr++), newx = Gv_GetVarX(*insptr++), newy = Gv_GetVarX(*insptr++);
2254 
2255                 if (wallnum<0 || wallnum>=numwalls)
2256                 {
2257                     M32_ERROR("Invalid wall %d", wallnum);
2258                     continue;
2259                 }
2260                 dragpoint(wallnum,newx,newy,0);
2261                 continue;
2262             }
2263 
2264         case CON_SECTOROFWALL:
2265             insptr++;
2266             {
2267                 int32_t j = *insptr++;
2268                 Gv_SetVarX(j, sectorofwall(Gv_GetVarX(*insptr++)));
2269             }
2270             continue;
2271 
2272         case CON_FIXREPEATS:
2273             insptr++;
2274             fixrepeats(Gv_GetVarX(*insptr++));
2275             continue;
2276 
2277         case CON_GETCLOSESTCOL:
2278             insptr++;
2279             {
2280                 int32_t r = Gv_GetVarX(*insptr++), g = Gv_GetVarX(*insptr++), b = Gv_GetVarX(*insptr++);
2281                 Gv_SetVarX(*insptr++, paletteGetClosestColor(r, g, b));
2282                 continue;
2283             }
2284 
2285 // *** stuff
2286         case CON_UPDATEHIGHLIGHT:
2287             insptr++;
2288             update_highlight();
2289             continue;
2290 
2291         case CON_UPDATEHIGHLIGHTSECTOR:
2292             insptr++;
2293             update_highlightsector();
2294             continue;
2295 
2296         case CON_SETHIGHLIGHT:
2297             insptr++;
2298             {
2299                 int32_t what=Gv_GetVarX(*insptr++), index=Gv_GetVarX(*insptr++), doset = Gv_GetVarX(*insptr++);
2300 
2301                 if (highlightsectorcnt >= 0)
2302                 {
2303                     M32_ERROR("sector highlight active or pending, cannot highlight sprites/walls");
2304                     continue;
2305                 }
2306 
2307                 if (what&16384)
2308                 {
2309                     index &= ~16384;
2310                     if (index < 0 || index>=MAXSPRITES || sprite[index].statnum==MAXSTATUS)
2311                     {
2312                         M32_ERROR("Invalid sprite index %d", index);
2313                         continue;
2314                     }
2315 
2316                     if (doset)
2317                         show2dsprite[index>>3] |= (1<<(index&7));
2318                     else
2319                         show2dsprite[index>>3] &= ~(1<<(index&7));
2320                 }
2321                 else
2322                 {
2323                     if (index < 0 || index>=numwalls)
2324                     {
2325                         M32_ERROR("Invalid wall index %d", index);
2326                         continue;
2327                     }
2328 
2329                     if (doset)
2330                         show2dwall[index>>3] |= (1<<(index&7));
2331                     else
2332                         show2dwall[index>>3] &= ~(1<<(index&7));
2333                 }
2334 
2335                 vm.miscflags |= VMFLAG_MISC_UPDATEHL;
2336 
2337                 continue;
2338             }
2339 
2340         case CON_SETHIGHLIGHTSECTOR:
2341             insptr++;
2342             {
2343                 int32_t index=Gv_GetVarX(*insptr++), doset = Gv_GetVarX(*insptr++);
2344 
2345                 if (highlightcnt >= 0)
2346                 {
2347                     M32_ERROR("sprite/wall highlight active or pending, cannot highlight sectors");
2348                     continue;
2349                 }
2350 
2351                 X_ERROR_INVALIDSECT(index);
2352 
2353                 if (doset)
2354                     hlsectorbitmap[index>>3] |= (1<<(index&7));
2355                 else
2356                     hlsectorbitmap[index>>3] &= ~(1<<(index&7));
2357 
2358                 vm.miscflags |= VMFLAG_MISC_UPDATEHLSECT;
2359 
2360                 continue;
2361             }
2362 
2363         case CON_GETTIMEDATE:
2364             insptr++;
2365             {
2366                 int32_t v1=*insptr++,v2=*insptr++,v3=*insptr++,v4=*insptr++,v5=*insptr++,v6=*insptr++,v7=*insptr++,v8=*insptr++;
2367                 time_t rawtime;
2368                 struct tm *ti;
2369 
2370                 time(&rawtime);
2371                 ti = localtime(&rawtime);
2372                 // initprintf("Time&date: %s\n",asctime (ti));
2373 
2374                 Gv_SetVarX(v1, ti->tm_sec);
2375                 Gv_SetVarX(v2, ti->tm_min);
2376                 Gv_SetVarX(v3, ti->tm_hour);
2377                 Gv_SetVarX(v4, ti->tm_mday);
2378                 Gv_SetVarX(v5, ti->tm_mon);
2379                 Gv_SetVarX(v6, ti->tm_year+1900);
2380                 Gv_SetVarX(v7, ti->tm_wday);
2381                 Gv_SetVarX(v8, ti->tm_yday);
2382                 continue;
2383             }
2384 
2385         case CON_ADDLOG:
2386         {
2387             insptr++;
2388 
2389             OSD_Printf("L=%d\n", g_errorLineNum);
2390             continue;
2391         }
2392 
2393         case CON_ADDLOGVAR:
2394             insptr++;
2395             {
2396                 char buf[80] = "", buf2[80] = "";
2397                 int32_t code = (int32_t)*insptr, val = Gv_GetVarX(code);
2398                 int32_t negate=code&M32_FLAG_NEGATE;
2399 
2400                 if (code & (0xFFFFFFFF-(MAXGAMEVARS-1)))
2401                 {
2402                     if ((code&M32_VARTYPE_MASK)==M32_FLAG_ARRAY || (code&M32_VARTYPE_MASK)==M32_FLAG_STRUCT)
2403                     {
2404                         if (code&M32_FLAG_CONSTANT)
2405                             Bsprintf(buf2, "%d", (code>>16)&0xffff);
2406                         else
2407                         {
2408                             char *label = aGameVars[(code>>16)&(MAXGAMEVARS-1)].szLabel;
2409                             Bsprintf(buf2, "%s", label?label:"???");
2410                         }
2411                     }
2412                     else if ((code&M32_VARTYPE_MASK)==M32_FLAG_LOCAL)
2413                         Bsprintf(buf2, "%d", code&(MAXGAMEVARS-1));
2414 
2415                     if ((code&0x0000FFFC) == M32_FLAG_CONSTANT) // addlogvar for a constant.. why not? :P
2416                     {
2417                         switch (code&3)
2418                         {
2419                         case 0: Bsprintf(buf, "(immediate constant)"); break;
2420                         case 1: Bsprintf(buf, "(indirect constant)"); break;
2421                         case 2: Bsprintf(buf, "(label constant)"); break;
2422                         default: Bsprintf(buf, "(??? constant)"); break;
2423                         }
2424                     }
2425                     else
2426                     {
2427                         switch (code&M32_VARTYPE_MASK)
2428                         {
2429                         case M32_FLAG_ARRAY:
2430                             Bsnprintf(buf, sizeof(buf), "%s[%s]", aGameArrays[code&(MAXGAMEARRAYS-1)].szLabel
2431                                       ? aGameArrays[code&(MAXGAMEARRAYS-1)].szLabel : "???", buf2);
2432                             break;
2433                         case M32_FLAG_STRUCT:
2434                         {
2435                             int32_t memberid=(code>>2)&63, lightp = (memberid >= LIGHT_X);
2436                             const char *pp1[4] = {"sprite","sector","wall","tsprite"};
2437                             const memberlabel_t *pp2[4] = {SpriteLabels, SectorLabels, WallLabels, SpriteLabels};
2438                             if (lightp)
2439                             {
2440                                 pp1[3] = "light";
2441                                 pp2[3] = LightLabels;
2442                                 memberid -= LIGHT_X;
2443                             }
2444 
2445                             Bsnprintf(buf, sizeof(buf), "%s[%s].%s", pp1[code&3], buf2, pp2[code&3][memberid].name);
2446                         }
2447                         break;
2448                         case M32_FLAG_VAR:
2449                             Bstrcpy(buf, "???");
2450                             break;
2451                         case M32_FLAG_LOCAL:
2452                             Bsnprintf(buf, sizeof(buf), ".local[%s]", buf2);
2453                             break;
2454                         }
2455                     }
2456                 }
2457                 else
2458                 {
2459                     if (aGameVars[code].dwFlags & GAMEVAR_PERBLOCK)
2460                     {
2461                         Bsprintf(buf2, "(%s", vm.g_st==0? "top-level) " : vm.g_st<=MAXEVENTS? "event" : "state");
2462                         if (vm.g_st >= 1+MAXEVENTS && vm.g_st <1+MAXEVENTS+g_stateCount)
2463                             Bsprintf(buf, " `%s') ", statesinfo[vm.g_st-1-MAXEVENTS].name);
2464                         else if (vm.g_st > 0)
2465                             Bsprintf(buf, " %d) ", vm.g_st-1);
2466                         Bstrcat(buf2, buf);
2467                     }
2468 
2469                     Bsnprintf(buf, sizeof(buf), "%s%s", buf2, aGameVars[code].szLabel ? aGameVars[code].szLabel : "???");
2470                 }
2471 
2472                 OSD_Printf("L%d: %s%s=%d\n", g_errorLineNum, negate?"-":"", buf, val);
2473 
2474                 insptr++;
2475                 continue;
2476             }
2477 
2478         case CON_DEBUG:
2479             insptr++;
2480             initprintf("%d\n",*insptr++);
2481             continue;
2482 
2483 // *** strings
2484         case CON_REDEFINEQUOTE:
2485             insptr++;
2486             {
2487                 int32_t q = *insptr++, i = *insptr++;
2488                 X_ERROR_INVALIDQUOTE(q, apStrings);
2489                 X_ERROR_INVALIDQUOTE(i, apXStrings);
2490                 Bstrcpy(apStrings[q],apXStrings[i]);
2491                 continue;
2492             }
2493 
2494         case CON_GETNUMBER16:  /* deprecated */
2495         case CON_GETNUMBER256:  /* deprecated */
2496         case CON_GETNUMBERFROMUSER:
2497             insptr++;
2498             {
2499                 int32_t var=*insptr++, quote=*insptr++;
2500                 const char *quotetext = GetMaybeInlineQuote(quote);
2501                 if (vm.flags&VMFLAG_ERROR)
2502                     continue;
2503 
2504                 {
2505                     int32_t max=Gv_GetVarX(*insptr++);
2506                     int32_t sign = (tw==CON_GETNUMBERFROMUSER) ? Gv_GetVarX(*insptr++) : (max<=0);
2507                     char buf[64];  // buffers in getnumber* are 80 bytes long
2508 
2509                     Bstrncpyz(buf, quotetext, sizeof(buf));
2510 
2511                     if (max==0)
2512                         max = INT32_MAX;
2513                     else
2514                         max = klabs(max);
2515 
2516 //OSD_Printf("max:%d, sign:%d\n", max, sign);
2517                     if (tw==CON_GETNUMBERFROMUSER)
2518                     {
2519                         Gv_SetVarX(var, in3dmode() ?
2520                                    getnumber256(quotetext, Gv_GetVarX(var), max, sign) :
2521                                    getnumber16(quotetext, Gv_GetVarX(var), max, sign));
2522                     }
2523                     else if (tw==CON_GETNUMBER16)
2524                         Gv_SetVarX(var, getnumber16(quotetext, Gv_GetVarX(var), max, sign));
2525                     else
2526                         Gv_SetVarX(var, getnumber256(quotetext, Gv_GetVarX(var), max, sign));
2527                 }
2528             }
2529             continue;
2530 
2531         case CON_PRINT:
2532         case CON_QUOTE:
2533         case CON_ERRORINS:
2534         case CON_PRINTMESSAGE16:
2535         case CON_PRINTMESSAGE256:
2536         case CON_PRINTEXT256:
2537         case CON_PRINTEXT16:
2538         case CON_DRAWLABEL:
2539             insptr++;
2540             {
2541                 int32_t i=*insptr++;
2542                 const char *quotetext = GetMaybeInlineQuote(i);
2543                 if (vm.flags&VMFLAG_ERROR)
2544                     continue;
2545 
2546                 {
2547                     int32_t x=(tw>=CON_PRINTMESSAGE256)?Gv_GetVarX(*insptr++):0;
2548                     int32_t y=(tw>=CON_PRINTMESSAGE256)?Gv_GetVarX(*insptr++):0;
2549 
2550                     int32_t col=(tw>=CON_PRINTEXT256)?Gv_GetVarX(*insptr++):0;
2551                     int32_t backcol=(tw>=CON_PRINTEXT256)?Gv_GetVarX(*insptr++):0;
2552                     int32_t fontsize=(tw>=CON_PRINTEXT256)?Gv_GetVarX(*insptr++):0;
2553 
2554                     if (tw==CON_PRINT || tw==CON_ERRORINS)
2555                     {
2556                         OSD_Printf("%s\n", quotetext);
2557                         if (tw==CON_ERRORINS)
2558                             vm.flags |= VMFLAG_ERROR;
2559                     }
2560                     else if (tw==CON_QUOTE)
2561                     {
2562                         message("%s", quotetext);
2563                     }
2564                     else if (tw==CON_PRINTMESSAGE16)
2565                     {
2566                         if (!in3dmode())
2567                             printmessage16("%s", quotetext);
2568                     }
2569                     else if (tw==CON_PRINTMESSAGE256)
2570                     {
2571                         if (in3dmode())
2572                             printmessage256(x, y, quotetext);
2573                     }
2574                     else if (tw==CON_PRINTEXT256)
2575                     {
2576                         if (in3dmode())
2577                         {
2578                             if (col>=256)
2579                                 col=0;
2580                             else if (col < 0 && col >= -255)
2581                                 col = editorcolors[-col];
2582 
2583                             if (backcol<0 || backcol>=256)
2584                                 backcol=-1;
2585 
2586                             printext256(x, y, col, backcol, quotetext, fontsize);
2587                         }
2588                     }
2589                     else if (tw==CON_PRINTEXT16)
2590                     {
2591                         if (!in3dmode())
2592                             printext16(x, y, editorcolors[col&255], backcol<0 ? -1 : editorcolors[backcol&255],
2593                                        quotetext, fontsize);
2594                     }
2595                     else if (tw==CON_DRAWLABEL)
2596                     {
2597                         if (!in3dmode())
2598                         {
2599                             drawsmallabel(quotetext,
2600                                           editorcolors[backcol&255],  // col
2601                                           fontsize < 0 ? -1 : editorcolors[fontsize&255], editorcolors[fontsize&255] - 3, // backcol
2602                                           x, y, col);  // x y z
2603                         }
2604                     }
2605                 }
2606             }
2607             continue;
2608 
2609         case CON_QSTRLEN:
2610             insptr++;
2611             {
2612                 int32_t i=*insptr++, quote=*insptr++;
2613                 const char *quotetext = GetMaybeInlineQuote(quote);
2614                 if (vm.flags&VMFLAG_ERROR)
2615                     continue;
2616 
2617                 Gv_SetVarX(i, Bstrlen(quotetext));
2618                 continue;
2619             }
2620 
2621         case CON_QSUBSTR:
2622             insptr++;
2623             {
2624                 int32_t q1 = Gv_GetVarX(*insptr++);
2625                 int32_t q2 = *insptr++;
2626                 const char *q2text = GetMaybeInlineQuote(q2);
2627                 if (vm.flags&VMFLAG_ERROR)
2628                     continue;
2629 
2630                 X_ERROR_INVALIDQUOTE(q1, apStrings);
2631 
2632                 {
2633                     int32_t st = Gv_GetVarX(*insptr++);
2634                     int32_t ln = Gv_GetVarX(*insptr++);
2635                     char *s1 = apStrings[q1];
2636                     const char *s2 = q2text;
2637 
2638                     while (*s2 && st--) s2++;
2639                     while ((*s1 = *s2) && ln--)
2640                     {
2641                         s1++;
2642                         s2++;
2643                     }
2644                     *s1=0;
2645                 }
2646                 continue;
2647             }
2648 
2649         case CON_QSTRNCAT:
2650         case CON_QSTRCAT:
2651         case CON_QSTRCPY:
2652 ///        case CON_QGETSYSSTR:
2653             insptr++;
2654             {
2655                 int32_t i = Gv_GetVarX(*insptr++);
2656                 int32_t j = *insptr++;
2657 
2658                 const char *quotetext = GetMaybeInlineQuote(j);
2659                 if (vm.flags&VMFLAG_ERROR)
2660                     continue;
2661 
2662                 X_ERROR_INVALIDQUOTE(i, apStrings);
2663 
2664                 switch (tw)
2665                 {
2666                 case CON_QSTRCAT:
2667                     Bstrncat(apStrings[i], quotetext, (MAXQUOTELEN-1)-Bstrlen(apStrings[i]));
2668                     break;
2669                 case CON_QSTRNCAT:
2670                     Bstrncat(apStrings[i], quotetext, Gv_GetVarX(*insptr++));
2671                     break;
2672                 case CON_QSTRCPY:
2673                     Bstrcpy(apStrings[i], quotetext);
2674                     break;
2675                 }
2676                 continue;
2677             }
2678 
2679         case CON_QSPRINTF:
2680             insptr++;
2681             {
2682                 int32_t dq=Gv_GetVarX(*insptr++), sq=*insptr++;
2683                 const char *sourcetext = GetMaybeInlineQuote(sq);
2684                 if (vm.flags&VMFLAG_ERROR)
2685                     continue;
2686 
2687                 X_ERROR_INVALIDQUOTE(dq, apStrings);
2688 
2689                 {
2690                     int32_t arg[32], numvals=0, i=0, j=0, k=0;
2691                     int32_t len = Bstrlen(sourcetext);
2692                     char tmpbuf[MAXQUOTELEN<<1];
2693 
2694                     while (*insptr != -1 && numvals < 32)
2695                         arg[numvals++] = Gv_GetVarX(*insptr++);
2696 
2697                     insptr++; // skip the NOP
2698 
2699                     i = 0;
2700                     do
2701                     {
2702                         while (k < len && j < MAXQUOTELEN && sourcetext[k] != '%')
2703                             tmpbuf[j++] = sourcetext[k++];
2704 
2705                         if (sourcetext[k] == '%')
2706                         {
2707                             k++;
2708 
2709                             if (i>=numvals) goto dodefault;
2710 
2711                             switch (sourcetext[k])
2712                             {
2713                             case 'l':
2714                                 if (sourcetext[k+1] != 'd')
2715                                 {
2716                                     // write the % and l
2717                                     tmpbuf[j++] = sourcetext[k-1];
2718                                     tmpbuf[j++] = sourcetext[k++];
2719                                     break;
2720                                 }
2721                                 k++;
2722                                 fallthrough__;
2723                             case 'd':
2724                             {
2725                                 char buf[16];
2726                                 int32_t ii = 0;
2727 
2728                                 Bsprintf(buf, "%d", arg[i++]);
2729 
2730                                 ii = Bstrlen(buf);
2731                                 Bmemcpy(&tmpbuf[j], buf, ii);
2732                                 j += ii;
2733                                 k++;
2734                             }
2735                             break;
2736 
2737                             case 'f':
2738                             {
2739                                 char buf[64];
2740                                 int32_t ii = 0;
2741 
2742                                 Bsprintf(buf, "%f", *((float *)&arg[i++]));
2743 
2744                                 ii = Bstrlen(buf);
2745                                 Bmemcpy(&tmpbuf[j], buf, ii);
2746                                 j += ii;
2747                                 k++;
2748                             }
2749                             break;
2750 
2751                             case 's':
2752                             {
2753                                 if (arg[i]>=0 && arg[i]<MAXQUOTES && apStrings[arg[i]])
2754                                 {
2755                                     int32_t ii = Bstrlen(apStrings[arg[i]]);
2756                                     Bmemcpy(&tmpbuf[j], apStrings[arg[i]], ii);
2757                                     j += ii;
2758                                 }
2759                                 k++;
2760                             }
2761                             break;
2762 
2763 dodefault:
2764                             default:
2765                                 tmpbuf[j++] = sourcetext[k-1];
2766                                 break;
2767                             }
2768                         }
2769                     }
2770                     while (k < len && j < MAXQUOTELEN);
2771 
2772                     tmpbuf[j] = '\0';
2773                     Bmemcpy(apStrings[dq], tmpbuf, MAXQUOTELEN);
2774                     apStrings[dq][MAXQUOTELEN-1] = '\0';
2775                     continue;
2776                 }
2777             }
2778 
2779 // *** findnear*
2780 // CURSPR vvv
2781         case CON_FINDNEARSPRITE:
2782         case CON_FINDNEARSPRITE3D:
2783         case CON_FINDNEARSPRITEVAR:
2784         case CON_FINDNEARSPRITE3DVAR:
2785             insptr++;
2786             {
2787                 // syntax findnearactor(var) <type> <maxdist(var)> <getvar>
2788                 // gets the sprite ID of the nearest actor within max dist
2789                 // that is of <type> into <getvar>
2790                 // -1 for none found
2791                 // <type> <maxdist(varid)> <varid>
2792                 int32_t lType=*insptr++;
2793                 int32_t lMaxDist = (tw==CON_FINDNEARSPRITE || tw==CON_FINDNEARSPRITE3D)?
2794                                    *insptr++ : Gv_GetVarX(*insptr++);
2795                 int32_t lVarID=*insptr++;
2796                 int32_t lFound=-1, j, k = MAXSTATUS-1;
2797 
2798                 X_ERROR_INVALIDCI();
2799                 do
2800                 {
2801                     j=headspritestat[k];    // all sprites
2802                     if (tw==CON_FINDNEARSPRITE3D || tw==CON_FINDNEARSPRITE3DVAR)
2803                     {
2804                         while (j>=0)
2805                         {
2806                             if (sprite[j].picnum == lType && j != vm.spriteNum && dist(&sprite[vm.spriteNum], &sprite[j]) < lMaxDist)
2807                             {
2808                                 lFound=j;
2809                                 j = MAXSPRITES;
2810                                 break;
2811                             }
2812                             j = nextspritestat[j];
2813                         }
2814                         if (j == MAXSPRITES)
2815                             break;
2816                         continue;
2817                     }
2818 
2819                     while (j>=0)
2820                     {
2821                         if (sprite[j].picnum == lType && j != vm.spriteNum && ldist(&sprite[vm.spriteNum], &sprite[j]) < lMaxDist)
2822                         {
2823                             lFound=j;
2824                             j = MAXSPRITES;
2825                             break;
2826                         }
2827                         j = nextspritestat[j];
2828                     }
2829 
2830                     if (j == MAXSPRITES)
2831                         break;
2832                 }
2833                 while (k--);
2834                 Gv_SetVarX(lVarID, lFound);
2835                 continue;
2836             }
2837 
2838         case CON_FINDNEARSPRITEZVAR:
2839         case CON_FINDNEARSPRITEZ:
2840             insptr++;
2841             {
2842                 // syntax findnearactor(var) <type> <maxdist(var)> <getvar>
2843                 // gets the sprite ID of the nearest actor within max dist
2844                 // that is of <type> into <getvar>
2845                 // -1 for none found
2846                 // <type> <maxdist(varid)> <varid>
2847                 int32_t lType=*insptr++;
2848                 int32_t lMaxDist = (tw==CON_FINDNEARSPRITEZVAR) ? Gv_GetVarX(*insptr++) : *insptr++;
2849                 int32_t lMaxZDist = (tw==CON_FINDNEARSPRITEZVAR) ? Gv_GetVarX(*insptr++) : *insptr++;
2850                 int32_t lVarID=*insptr++;
2851                 int32_t lFound=-1, lTemp, lTemp2, j, k=MAXSTATUS-1;
2852 
2853                 X_ERROR_INVALIDCI();
2854                 do
2855                 {
2856                     j=headspritestat[k];    // all sprites
2857                     if (j == -1) continue;
2858                     do
2859                     {
2860                         if (sprite[j].picnum == lType && j != vm.spriteNum)
2861                         {
2862                             lTemp=ldist(&sprite[vm.spriteNum], &sprite[j]);
2863                             if (lTemp < lMaxDist)
2864                             {
2865                                 lTemp2=klabs(sprite[vm.spriteNum].z-sprite[j].z);
2866                                 if (lTemp2 < lMaxZDist)
2867                                 {
2868                                     lFound=j;
2869                                     j = MAXSPRITES;
2870                                     break;
2871                                 }
2872                             }
2873                         }
2874                         j = nextspritestat[j];
2875                     }
2876                     while (j>=0);
2877                     if (j == MAXSPRITES)
2878                         break;
2879                 }
2880                 while (k--);
2881                 Gv_SetVarX(lVarID, lFound);
2882 
2883                 continue;
2884             }
2885 // ^^^
2886 
2887         case CON_GETTICKS:
2888             insptr++;
2889             {
2890                 int32_t j=*insptr++;
2891                 Gv_SetVarX(j, timerGetTicks());
2892             }
2893             continue;
2894 
2895         case CON_SETASPECT:
2896             insptr++;
2897             {
2898                 int32_t daxrange = Gv_GetVarX(*insptr++), dayxaspect = Gv_GetVarX(*insptr++);
2899                 if (daxrange < (1<<12)) daxrange = (1<<12);
2900                 if (daxrange > (1<<20)) daxrange = (1<<20);
2901                 if (dayxaspect < (1<<12)) dayxaspect = (1<<12);
2902                 if (dayxaspect > (1<<20)) dayxaspect = (1<<20);
2903                 renderSetAspect(daxrange, dayxaspect);
2904                 continue;
2905             }
2906 
2907 // vvv CURSPR
2908         case CON_SETI:
2909         {
2910             int32_t newcurspritei;
2911 
2912             insptr++;
2913             newcurspritei = Gv_GetVarX(*insptr++);
2914             X_ERROR_INVALIDSPRI(newcurspritei);
2915             vm.spriteNum = newcurspritei;
2916             vm.pSprite = &sprite[vm.spriteNum];
2917             continue;
2918         }
2919 
2920         case CON_SIZEAT:
2921             insptr += 3;
2922             X_ERROR_INVALIDSP();
2923             vm.pSprite->xrepeat = (uint8_t) Gv_GetVarX(*(insptr-2));
2924             vm.pSprite->yrepeat = (uint8_t) Gv_GetVarX(*(insptr-1));
2925 #ifdef STRUCT_TRACKERS_ENABLED
2926             if (vm.spriteNum != -1) spritechanged[vm.spriteNum]++;
2927 #endif
2928             continue;
2929 
2930         case CON_CSTAT:
2931             insptr += 2;
2932             X_ERROR_INVALIDSP();
2933             vm.pSprite->cstat = (int16_t) *(insptr-1);
2934 #ifdef STRUCT_TRACKERS_ENABLED
2935             if (vm.spriteNum != -1) spritechanged[vm.spriteNum]++;
2936 #endif
2937             continue;
2938 
2939         case CON_CSTATOR:
2940             insptr += 2;
2941             X_ERROR_INVALIDSP();
2942             vm.pSprite->cstat |= (int16_t) Gv_GetVarX(*(insptr-1));
2943 #ifdef STRUCT_TRACKERS_ENABLED
2944             if (vm.spriteNum != -1) spritechanged[vm.spriteNum]++;
2945 #endif
2946             continue;
2947 
2948         case CON_CLIPDIST:
2949             insptr += 2;
2950             X_ERROR_INVALIDSP();
2951             vm.pSprite->clipdist = (uint8_t) Gv_GetVarX(*(insptr-1));
2952 #ifdef STRUCT_TRACKERS_ENABLED
2953             if (vm.spriteNum != -1) spritechanged[vm.spriteNum]++;
2954 #endif
2955             continue;
2956 
2957         case CON_SPRITEPAL:
2958             insptr += 2;
2959             X_ERROR_INVALIDSP();
2960             vm.pSprite->pal = Gv_GetVarX(*(insptr-1));
2961 #ifdef STRUCT_TRACKERS_ENABLED
2962             if (vm.spriteNum != -1) spritechanged[vm.spriteNum]++;
2963 #endif
2964             continue;
2965 
2966         case CON_CACTOR:
2967             insptr += 2;
2968             X_ERROR_INVALIDSP();
2969             vm.pSprite->picnum = Gv_GetVarX(*(insptr-1));
2970 #ifdef STRUCT_TRACKERS_ENABLED
2971             if (vm.spriteNum != -1) spritechanged[vm.spriteNum]++;
2972 #endif
2973             continue;
2974 
2975         case CON_SPGETLOTAG:
2976             insptr++;
2977             X_ERROR_INVALIDSP();
2978             Gv_SetVarX(M32_LOTAG_VAR_ID, vm.pSprite->lotag);
2979             continue;
2980 
2981         case CON_SPGETHITAG:
2982             insptr++;
2983             X_ERROR_INVALIDSP();
2984             Gv_SetVarX(M32_HITAG_VAR_ID, vm.pSprite->hitag);
2985             continue;
2986 
2987         case CON_SECTGETLOTAG:
2988             insptr++;
2989             X_ERROR_INVALIDSP();
2990             Gv_SetVarX(M32_LOTAG_VAR_ID, sector[vm.pSprite->sectnum].lotag);
2991             continue;
2992 
2993         case CON_SECTGETHITAG:
2994             insptr++;
2995             X_ERROR_INVALIDSP();
2996             Gv_SetVarX(M32_HITAG_VAR_ID, sector[vm.pSprite->sectnum].hitag);
2997             continue;
2998 
2999         case CON_GETTEXTUREFLOOR:
3000             insptr++;
3001             X_ERROR_INVALIDSP();
3002             Gv_SetVarX(M32_TEXTURE_VAR_ID, sector[vm.pSprite->sectnum].floorpicnum);
3003             continue;
3004 
3005         case CON_GETTEXTURECEILING:
3006             insptr++;
3007             X_ERROR_INVALIDSP();
3008             Gv_SetVarX(M32_TEXTURE_VAR_ID, sector[vm.pSprite->sectnum].ceilingpicnum);
3009             continue;
3010 // ^^^
3011         case CON_DRAWLINE16:
3012         case CON_DRAWLINE16B:
3013         case CON_DRAWLINE16Z:
3014             insptr++;
3015             {
3016                 int32_t x1=Gv_GetVarX(*insptr++), y1=Gv_GetVarX(*insptr++);
3017                 int32_t z1=tw==CON_DRAWLINE16Z?Gv_GetVarX(*insptr++):0;
3018                 int32_t x2=Gv_GetVarX(*insptr++), y2=Gv_GetVarX(*insptr++);
3019                 int32_t z2=tw==CON_DRAWLINE16Z?Gv_GetVarX(*insptr++):0;
3020                 int32_t col=Gv_GetVarX(*insptr++), odrawlinepat=drawlinepat;
3021                 int32_t xofs=0, yofs=0;
3022 
3023                 if (tw==CON_DRAWLINE16B || tw==CON_DRAWLINE16Z)
3024                 {
3025                     editorGet2dScreenCoordinates(&x1,&y1, x1-pos.x,y1-pos.y, zoom);
3026                     editorGet2dScreenCoordinates(&x2,&y2, x2-pos.x,y2-pos.y, zoom);
3027 
3028                     if (tw==CON_DRAWLINE16Z && m32_sideview)
3029                     {
3030                         y1 += getscreenvdisp(z1-pos.z,zoom);
3031                         y2 += getscreenvdisp(z2-pos.z,zoom);
3032                     }
3033 
3034                     xofs = halfxdim16;
3035                     yofs = midydim16;
3036                 }
3037 
3038                 drawlinepat = m32_drawlinepat;
3039                 editorDraw2dLine(xofs+x1,yofs+y1, xofs+x2,yofs+y2, col>=0?editorcolors[col&15]:((-col)&255));
3040                 drawlinepat = odrawlinepat;
3041                 continue;
3042             }
3043 
3044         case CON_DRAWCIRCLE16:
3045         case CON_DRAWCIRCLE16B:
3046         case CON_DRAWCIRCLE16Z:
3047             insptr++;
3048             {
3049                 int32_t x1=Gv_GetVarX(*insptr++), y1=Gv_GetVarX(*insptr++);
3050                 int32_t z1 = tw==CON_DRAWCIRCLE16Z ? Gv_GetVarX(*insptr++) : 0;
3051                 int32_t r=Gv_GetVarX(*insptr++);
3052                 int32_t col=Gv_GetVarX(*insptr++), odrawlinepat=drawlinepat;
3053                 int32_t xofs=0, yofs=0, eccen=16384;
3054 
3055                 if (tw==CON_DRAWCIRCLE16B || tw==CON_DRAWCIRCLE16Z)
3056                 {
3057                     editorGet2dScreenCoordinates(&x1,&y1, x1-pos.x,y1-pos.y, zoom);
3058                     if (m32_sideview)
3059                         y1 += getscreenvdisp(z1-pos.z, zoom);
3060                     r = mulscale14(r,zoom);
3061                     eccen = scalescreeny(eccen);
3062                     xofs = halfxdim16;
3063                     yofs = midydim16;
3064                 }
3065 
3066                 drawlinepat = m32_drawlinepat;
3067                 editorDraw2dCircle(xofs+x1, yofs+y1, r, eccen, col>=0?editorcolors[col&15]:((-col)&255));
3068                 drawlinepat = odrawlinepat;
3069                 continue;
3070             }
3071 
3072         case CON_ROTATESPRITEA:
3073         case CON_ROTATESPRITE16:
3074         case CON_ROTATESPRITE:
3075             insptr++;
3076             {
3077                 int32_t x=Gv_GetVarX(*insptr++),   y=Gv_GetVarX(*insptr++),           z=Gv_GetVarX(*insptr++);
3078                 int32_t a=Gv_GetVarX(*insptr++),   tilenum=Gv_GetVarX(*insptr++),     shade=Gv_GetVarX(*insptr++);
3079                 int32_t pal=Gv_GetVarX(*insptr++), orientation=Gv_GetVarX(*insptr++);
3080                 int32_t alpha = (tw == CON_ROTATESPRITEA) ? Gv_GetVarX(*insptr++) : 0;
3081                 int32_t x1=Gv_GetVarX(*insptr++),  y1=Gv_GetVarX(*insptr++);
3082                 int32_t x2=Gv_GetVarX(*insptr++),  y2=Gv_GetVarX(*insptr++);
3083 
3084                 if (tw != CON_ROTATESPRITE16 && !(orientation&ROTATESPRITE_FULL16))
3085                 {
3086                     x<<=16;
3087                     y<<=16;
3088                 }
3089 
3090                 orientation &= (ROTATESPRITE_MAX-1);
3091 
3092                 rotatesprite_(x,y,z,a,tilenum,shade,pal,2|orientation,alpha,0,x1,y1,x2,y2);
3093                 continue;
3094             }
3095 
3096         case CON_SETGAMEPALETTE:
3097             insptr++;
3098             SetGamePalette(Gv_GetVarX(*insptr++));
3099             continue;
3100 
3101 // *** sounds
3102         case CON_IFSOUND:
3103             insptr++;
3104             {
3105                 int32_t j=Gv_GetVarX(*insptr);
3106                 if (S_InvalidSound(j))
3107                 {
3108                     M32_ERROR("Invalid sound %d", j);
3109                     insptr++;
3110                     continue;
3111                 }
3112                 VM_DoConditional(S_CheckSoundPlaying(vm.spriteNum,j));
3113             }
3114             continue;
3115 
3116         case CON_IFNOSOUNDS:
3117             VM_DoConditional(S_SoundsPlaying(vm.spriteNum) < 0);
3118         continue;
3119 
3120         case CON_IFIN3DMODE:
3121             VM_DoConditional(in3dmode());
3122             continue;
3123 
3124         // ifaimingsprite and -wall also work in 2d mode, but you must "and" with 16383 yourself
3125         case CON_IFAIMINGSPRITE:
3126             VM_DoConditional(AIMING_AT_SPRITE || (!in3dmode() && pointhighlight>=16384));
3127             continue;
3128         case CON_IFAIMINGWALL:
3129             VM_DoConditional(AIMING_AT_WALL_OR_MASK || (!in3dmode() && linehighlight>=0));
3130             continue;
3131         case CON_IFAIMINGSECTOR:
3132             VM_DoConditional(AIMING_AT_CEILING_OR_FLOOR);
3133             continue;
3134         case CON_IFINTERACTIVE:
3135             VM_DoConditional(vm.miscflags&VMFLAG_MISC_INTERACTIVE);
3136             continue;
3137 
3138         case CON_GETSOUNDFLAGS:
3139             insptr++;
3140             {
3141                 int32_t j=Gv_GetVarX(*insptr++), var=*insptr++;
3142                 if (S_InvalidSound(j))
3143                 {
3144                     M32_ERROR("Invalid sound %d", j);
3145                     insptr++;
3146                     continue;
3147                 }
3148 
3149                 Gv_SetVarX(var, S_SoundFlags(j));
3150             }
3151             continue;
3152 
3153         case CON_SOUNDVAR:
3154         case CON_STOPSOUNDVAR:
3155         case CON_SOUNDONCEVAR:
3156         case CON_GLOBALSOUNDVAR:
3157             insptr++;
3158             {
3159                 int32_t j=Gv_GetVarX(*insptr++);
3160 
3161                 if (S_InvalidSound(j))
3162                 {
3163                     M32_ERROR("Invalid sound %d", j);
3164                     continue;
3165                 }
3166 
3167                 switch (tw)
3168                 {
3169                 case CON_SOUNDONCEVAR:
3170                     if (!S_CheckSoundPlaying(vm.spriteNum,j))
3171                         A_PlaySound((int16_t)j,vm.spriteNum);
3172                     break;
3173                 case CON_GLOBALSOUNDVAR:
3174                     A_PlaySound((int16_t)j,-1);
3175                     break;
3176                 case CON_STOPSOUNDVAR:
3177                     if (S_CheckSoundPlaying(vm.spriteNum,j))
3178                         S_StopSound((int16_t)j);
3179                     break;
3180                 case CON_SOUNDVAR:
3181                     A_PlaySound((int16_t)j,vm.spriteNum);
3182                     break;
3183                 }
3184             }
3185             continue;
3186 
3187         case CON_STOPALLSOUNDS:
3188             insptr++;
3189             S_StopAllSounds();
3190             continue;
3191 
3192         default:
3193             VM_ScriptInfo();
3194 
3195             OSD_Printf("\nAn error has occurred in the Mapster32 virtual machine.\n\n"
3196                        "Please e-mail the file mapster32.log along with every M32 file\n"
3197                        "you're using and instructions how to reproduce this error to\n"
3198                        "development@voidpoint.com.\n\n"
3199                        "Thank you!\n");
3200             vm.flags |= VMFLAG_ERROR;
3201             Bfflush(NULL);
3202             return 1;
3203         }
3204     }
3205 
3206     return 0;
3207 }
3208