//-------------------------------------------------------------------------
/*
Copyright (C) 1996, 2003 - 3D Realms Entertainment
Copyright (C) 2000, 2003 - Matt Saettler (EDuke Enhancements)
Copyright (C) 2020 - Christoph Oelckers

This file is part of Enhanced Duke Nukem 3D version 1.5 - Atomic Edition

Duke Nukem 3D is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

Original Source: 1996 - Todd Replogle
Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms

EDuke enhancements integrated: 04/13/2003 - Matt Saettler

Note: EDuke source was in transition.  Changes are in-progress in the
source as it is released.

*/
//-------------------------------------------------------------------------
#include "ns.h"
#include "concmd.h"
#include "duke3d.h"
#include "gamevar.h"
#include "mapinfo.h"
#include "gamestate.h"

BEGIN_DUKE_NS

// Player Actions - used by ifp instruction.
enum playeraction_t {
	pstanding                   = 0x00000001,
	pwalking                    = 0x00000002,
	prunning                    = 0x00000004,
	pducking                    = 0x00000008,
	pfalling                    = 0x00000010,
	pjumping                    = 0x00000020,
	phigher                     = 0x00000040,
	pwalkingback                = 0x00000080,
	prunningback                = 0x00000100,
	pkicking                    = 0x00000200,
	pshrunk                     = 0x00000400,
	pjetpack                    = 0x00000800,
	ponsteroids                 = 0x00001000,
	ponground                   = 0x00002000,
	palive                      = 0x00004000,
	pdead                       = 0x00008000,
	pfacing                     = 0x00010000
};



struct ParseState
{
	int g_i, g_p;
	int g_x;
	int* g_t;
	uint8_t killit_flag;
	spritetype* g_sp;
	int* insptr;

	int parse(void);
	void parseifelse(int condition);
};

int furthestcanseepoint(int i, spritetype* ts, int* dax, int* day);
bool ifsquished(int i, int p);
void fakebubbaspawn(int g_i, int g_p);
void tearitup(int sect);
void destroyit(int g_i);
void mamaspawn(int g_i);
void forceplayerangle(struct player_struct* p);

bool killthesprite = false;

void addspritetodelete(int spnum)
{
	killthesprite = true;
}

//---------------------------------------------------------------------------
//
// 
//
//---------------------------------------------------------------------------

void ParseState::parseifelse(int condition)
{
	if( condition )
	{
		// skip 'else' pointer.. and...
		insptr+=2;
		parse();
	}
	else
	{
		insptr = &ScriptCode[*(insptr+1)];
		if(*insptr == 10)
		{
			// else...

			// skip 'else' and...
			insptr+=2;
			parse();
		}
	}
}

//---------------------------------------------------------------------------
//
// 
//
//---------------------------------------------------------------------------

static int ifcanshoottarget(int g_i, int g_p, int g_x)
{
	int j;
	auto g_sp = &sprite[g_i];
	if (g_x > 1024)
	{
		short temphit, sclip, angdif;

		if (badguy(g_sp) && g_sp->xrepeat > 56)
		{
			sclip = 3084;
			angdif = 48;
		}
		else
		{
			sclip = 768;
			angdif = 16;
		}

		j = hitasprite(g_i, &temphit);
		if (j == (1 << 30))
		{
			return 1;
		}
		if (j > sclip)
		{
			if (temphit >= 0 && sprite[temphit].picnum == g_sp->picnum)
				j = 0;
			else
			{
				g_sp->ang += angdif; j = hitasprite(g_i, &temphit); g_sp->ang -= angdif;
				if (j > sclip)
				{
					if (temphit >= 0 && sprite[temphit].picnum == g_sp->picnum)
						j = 0;
					else
					{
						g_sp->ang -= angdif; j = hitasprite(g_i, &temphit); g_sp->ang += angdif;
						if (j > 768)
						{
							if (temphit >= 0 && sprite[temphit].picnum == g_sp->picnum)
								j = 0;
							else j = 1;
						}
						else j = 0;
					}
				}
				else j = 0;
			}
		}
		else j = 0;
	}
	else j = 1;
	return j;
}

//---------------------------------------------------------------------------
//
// 
//
//---------------------------------------------------------------------------

static bool ifcansee(int g_i, int g_p)
{
	auto g_sp = &sprite[g_i];
	spritetype* s;
	int j;

	// select sprite for monster to target
	// if holoduke is on, let them target holoduke first.
	// 
	if (ps[g_p].holoduke_on >= 0 && !isRR())
	{
		s = &sprite[ps[g_p].holoduke_on];
		j = cansee(g_sp->x, g_sp->y, g_sp->z - (krand() & ((32 << 8) - 1)), g_sp->sectnum,
			s->x, s->y, s->z, s->sectnum);

		if (j == 0)
		{
			// they can't see player's holoduke
			// check for player...
			s = &sprite[ps[g_p].i];
		}
	}
	else s = &sprite[ps[g_p].i];	// holoduke not on. look for player

	// can they see player, (or player's holoduke)
	j = cansee(g_sp->x, g_sp->y, g_sp->z - (krand() & ((47 << 8))), g_sp->sectnum,
		s->x, s->y, s->z - ((isRR()? 28 : 24) << 8), s->sectnum);

	if (j == 0)
	{
		// they can't see it.

		// Huh?.  This does nothing....
		// (the result is always j==0....)
		if ((abs(hittype[g_i].lastvx - g_sp->x) + abs(hittype[g_i].lastvy - g_sp->y)) <
			(abs(hittype[g_i].lastvx - s->x) + abs(hittype[g_i].lastvy - s->y)))
			j = 0;

		// um yeah, this if() will always fire....
		if (j == 0)
		{
			// search around for target player

			// also modifies 'target' x&y if found..

			j = furthestcanseepoint(g_i, s, &hittype[g_i].lastvx, &hittype[g_i].lastvy);

			if (j == -1) j = 0;
			else j = 1;
		}
	}
	else
	{
		// else, they did see it.
		// save where we were looking...
		hittype[g_i].lastvx = s->x;
		hittype[g_i].lastvy = s->y;
	}

	if (j == 1 && (g_sp->statnum == 1 || g_sp->statnum == 6))
		hittype[g_i].timetosleep = SLEEPTIME;

	return j == 1;
}


// int *it = 0x00589a04;

int ParseState::parse(void)
{
	int j, l, s;

	if(killit_flag) return 1;

	switch (*insptr)
	{
	case concmd_ifrnd:
	{
		insptr++;
		// HACK ALERT! The fire animation uses a broken ifrnd setup to delay its start because original CON has no variables.
		// But the chosen random value of 16/255 is too low and can cause delays of a second or more.
		int spnum = sprite[g_i].picnum;
		if (spnum == TILE_FIRE && g_t[4] == 0 && *insptr == 16)
		{
			parseifelse(rnd(64));
			break;
		}
		parseifelse(rnd(*insptr));
		break;
	}
	case concmd_ifcanshoottarget:
		parseifelse(ifcanshoottarget(g_i, g_p, g_x));
		break;
	case concmd_ifcanseetarget:
		j = cansee(g_sp->x, g_sp->y, g_sp->z - ((krand() & 41) << 8), g_sp->sectnum, ps[g_p].posx, ps[g_p].posy, ps[g_p].posz/*-((krand()&41)<<8)*/, sprite[ps[g_p].i].sectnum);
		parseifelse(j);
		if (j) hittype[g_i].timetosleep = SLEEPTIME;
		break;
	case concmd_ifnocover:
		j = cansee(g_sp->x, g_sp->y, g_sp->z, g_sp->sectnum, ps[g_p].posx, ps[g_p].posy, ps[g_p].posz, sprite[ps[g_p].i].sectnum);
		parseifelse(j);
		if (j) hittype[g_i].timetosleep = SLEEPTIME;
		break;

	case concmd_ifactornotstayput:
		parseifelse(hittype[g_i].actorstayput == -1);
		break;
	case concmd_ifcansee:
		parseifelse(ifcansee(g_i, g_p));
		break;

	case concmd_ifhitweapon:
		parseifelse(fi.ifhitbyweapon(g_i) >= 0);
		break;
	case concmd_ifsquished:
		parseifelse(ifsquished(g_i, g_p) == 1);
		break;
	case concmd_ifdead:
	{
		j = g_sp->extra;
		if (g_sp->picnum == TILE_APLAYER)
			j--;
		parseifelse(j < 0);
	}
	break;
	case concmd_ai:
		insptr++;
		g_t[5] = *insptr;
		g_t[4] = ScriptCode[g_t[5]];		  // Action
		g_t[1] = ScriptCode[g_t[5] + 1];		// move
		g_sp->hitag = ScriptCode[g_t[5] + 2];	  // Ai
		g_t[0] = g_t[2] = g_t[3] = 0;
		if (g_sp->hitag & random_angle)
			g_sp->ang = krand() & 2047;
		insptr++;
		break;
	case concmd_action:
		insptr++;
		g_t[2] = 0;
		g_t[3] = 0;
		g_t[4] = *insptr;
		insptr++;
		break;

	case concmd_ifpdistl:
		insptr++;
		parseifelse(g_x < *insptr);
		if (g_x > MAXSLEEPDIST && hittype[g_i].timetosleep == 0)
			hittype[g_i].timetosleep = SLEEPTIME;
		break;
	case concmd_ifpdistg:
		insptr++;
		parseifelse(g_x > * insptr);
		if (g_x > MAXSLEEPDIST && hittype[g_i].timetosleep == 0)
			hittype[g_i].timetosleep = SLEEPTIME;
		break;
	case concmd_else:
		insptr = &ScriptCode[*(insptr + 1)];
		break;
	case concmd_addstrength:
		insptr++;
		g_sp->extra += *insptr;
		insptr++;
		break;
	case concmd_strength:
		insptr++;
		g_sp->extra = *insptr;
		insptr++;
		break;
	case concmd_smacksprite:
		switch (krand() & 1)
		{
		case 0:
			g_sp->ang = (+512 + g_sp->ang + (krand() & 511)) & 2047;
			break;
		case 1:
			g_sp->ang = (-512 + g_sp->ang - (krand() & 511)) & 2047;
			break;
		}
		insptr++;
		break;
	case concmd_fakebubba:
		insptr++;
		fakebubbaspawn(g_i, g_p);
		break;

	case concmd_rndmove:
		g_sp->ang = krand() & 2047;
		g_sp->xvel = 25;
		insptr++;
		break;
	case concmd_mamatrigger:
		operateactivators(667, ps[g_p].i);
		insptr++;
		break;
	case concmd_mamaspawn:
		mamaspawn(g_i);
		insptr++;
		break;
	case concmd_mamaquake:
		if (g_sp->pal == 31)
			earthquaketime = 4;
		else if (g_sp->pal == 32)
			earthquaketime = 6;
		insptr++;
		break;
	case concmd_garybanjo:
		if (banjosound == 0)
		{
			short rnum = (krand() & 3) + 1;
			if (rnum == 4)
			{
				banjosound = 262;
			}
			else if (rnum == 1)
			{
				banjosound = 272;
			}
			else if (rnum == 2)
			{
				banjosound = 273;
			}
			else
			{
				banjosound = 273;
			}
			S_PlayActorSound(banjosound, g_i, CHAN_WEAPON);
		}
		else if (!S_CheckSoundPlaying(g_i, banjosound))
			S_PlayActorSound(banjosound, g_i, CHAN_WEAPON);
		insptr++;
		break;
	case concmd_motoloopsnd:
		if (!S_CheckSoundPlaying(g_i, 411))
			S_PlayActorSound(411, g_i, CHAN_VOICE);
		insptr++;
		break;
	case concmd_ifgotweaponce:
		insptr++;

		if (ud.coop >= 1 && ud.multimode > 1)
		{
			if (*insptr == 0)
			{
				for (j = 0; j < ps[g_p].weapreccnt; j++)
					if (ps[g_p].weaprecs[j] == g_sp->picnum)
						break;

				parseifelse(j < ps[g_p].weapreccnt&& g_sp->owner == g_i);
			}
			else if (ps[g_p].weapreccnt < 16)
			{
				ps[g_p].weaprecs[ps[g_p].weapreccnt++] = g_sp->picnum;
				parseifelse(g_sp->owner == g_i);
			}
		}
		else parseifelse(0);
		break;
	case concmd_getlastpal:
		insptr++;
		if (g_sp->picnum == TILE_APLAYER)
			g_sp->pal = ps[g_sp->yvel].palookup;
		else
		{
			// Copied from DukeGDX.
			if (g_sp->picnum == TILE_EGG && hittype[g_i].temp_data[5] == TILE_EGG + 2 && g_sp->pal == 1) 
			{
				ps[connecthead].max_actors_killed++; //revive the egg
				hittype[g_i].temp_data[5] = 0;
			}
			g_sp->pal = hittype[g_i].tempang;
		}
		hittype[g_i].tempang = 0;
		break;
	case concmd_tossweapon:
		insptr++;
		fi.checkweapons(&ps[g_sp->yvel]);
		break;
	case concmd_nullop:
		insptr++;
		break;
	case concmd_mikesnd:
		insptr++;
		if (!S_CheckSoundPlaying(g_i, g_sp->yvel))
			S_PlayActorSound(g_sp->yvel, g_i, CHAN_VOICE);
		break;
	case concmd_pkick:
		insptr++;

		if (ud.multimode > 1 && g_sp->picnum == TILE_APLAYER)
		{
			if (ps[otherp].quick_kick == 0)
				ps[otherp].quick_kick = 14;
		}
		else if (g_sp->picnum != TILE_APLAYER && ps[g_p].quick_kick == 0)
			ps[g_p].quick_kick = 14;
		break;
	case concmd_sizeto:
		insptr++;

		// JBF 20030805: As I understand it, if xrepeat becomes 0 it basically kills the
		// sprite, which is why the "sizeto 0 41" calls in 1.3d became "sizeto 4 41" in
		// 1.4, so instead of patching the CONs I'll surruptitiously patch the code here
		//if (!PLUTOPAK && *insptr == 0) *insptr = 4;

		j = ((*insptr) - g_sp->xrepeat) << 1;
		g_sp->xrepeat += ksgn(j);

		insptr++;

		if ((g_sp->picnum == TILE_APLAYER && g_sp->yrepeat < 36) || *insptr < g_sp->yrepeat || ((g_sp->yrepeat * (tilesiz[g_sp->picnum].y + 8)) << 2) < (hittype[g_i].floorz - hittype[g_i].ceilingz))
		{
			j = ((*insptr) - g_sp->yrepeat) << 1;
			if (abs(j)) g_sp->yrepeat += ksgn(j);
		}

		insptr++;

		break;
	case concmd_sizeat:
		insptr++;
		g_sp->xrepeat = (uint8_t)*insptr;
		insptr++;
		g_sp->yrepeat = (uint8_t)*insptr;
		insptr++;
		break;
	case concmd_shoot:
		insptr++;
		fi.shoot(g_i, (short)*insptr);
		insptr++;
		break;
	case concmd_ifsoundid:
		insptr++;
		parseifelse((short)*insptr == ambientlotag[g_sp->ang]);
		break;
	case concmd_ifsounddist:
		insptr++;
		if (*insptr == 0)
			parseifelse(ambienthitag[g_sp->ang] > g_x);
		else if (*insptr == 1)
			parseifelse(ambienthitag[g_sp->ang] < g_x);
		break;
	case concmd_soundtag:
		insptr++;
		S_PlayActorSound(ambientlotag[g_sp->ang], g_i);
		break;
	case concmd_soundtagonce:
		insptr++;
		if (!S_CheckSoundPlaying(g_i, ambientlotag[g_sp->ang]))
			S_PlayActorSound(ambientlotag[g_sp->ang], g_i);
		break;
	case concmd_soundonce:
		insptr++;
		if (!S_CheckSoundPlaying(g_i, *insptr++))
			S_PlayActorSound(*(insptr - 1), g_i);
		break;
	case concmd_stopsound:
		insptr++;
		if (S_CheckSoundPlaying(g_i, *insptr))
			S_StopSound((int)*insptr);
		insptr++;
		break;
	case concmd_globalsound:
		insptr++;
		if (g_p == screenpeek || ud.coop == 1)
			S_PlayActorSound((int)*insptr, ps[screenpeek].i);
		insptr++;
		break;
	case concmd_smackbubba:
		insptr++;
		if (!isRRRA() || g_sp->pal != 105)
		{
			setnextmap(false);
		}
		break;
	case concmd_mamaend:
		insptr++;
		ps[myconnectindex].MamaEnd = 150;
		break;

	case concmd_ifactorhealthg:
		insptr++;
		parseifelse(g_sp->extra > (short)*insptr);
		break;
	case concmd_ifactorhealthl:
		insptr++;
		parseifelse(g_sp->extra < (short)*insptr);
		break;
	case concmd_sound:
		insptr++;
		S_PlayActorSound((short) *insptr,g_i);
		insptr++;
		break;
	case concmd_tip:
		insptr++;
		ps[g_p].tipincs = 26;
		break;
	case concmd_iftipcow:
	case concmd_ifhittruck: // both have the same code.
		if (hittype[g_i].spriteextra == 1) // 
		{
			j = 1;
			hittype[g_i].spriteextra++;
		}
		else
			j = 0;
		parseifelse(j > 0);
		break;
	case concmd_tearitup:
		insptr++;
		tearitup(g_sp->sectnum);
		break;
	case concmd_fall:
		insptr++;
		g_sp->xoffset = 0;
		g_sp->yoffset = 0;
		fi.fall(g_i, g_p);
		break;
	case concmd_enda:
	case concmd_break:
	case concmd_ends:
	case concmd_endevent:
		return 1;
	case concmd_rightbrace:
		insptr++;
		return 1;
	case concmd_addammo:
		insptr++;
		if( ps[g_p].ammo_amount[*insptr] >= max_ammo_amount[*insptr] )
		{
			killit_flag = 2;
			break;
		}
		addammo( *insptr, &ps[g_p], *(insptr+1) );
		if(ps[g_p].curr_weapon == KNEE_WEAPON)
			if( ps[g_p].gotweapon[*insptr] )
				fi.addweapon( &ps[g_p], *insptr );
		insptr += 2;
		break;
	case concmd_money:
		insptr++;
		fi.lotsofmoney(g_sp,*insptr);
		insptr++;
		break;
	case concmd_mail:
		insptr++;
		fi.lotsofmail(g_sp,*insptr);
		insptr++;
		break;
	case concmd_sleeptime:
		insptr++;
		hittype[g_i].timetosleep = (short)*insptr;
		insptr++;
		break;
	case concmd_paper:
		insptr++;
		fi.lotsofpaper(g_sp,*insptr);
		insptr++;
		break;
	case concmd_addkills:
		insptr++;
		if (isRR())
		{
			if (hittype[g_i].spriteextra < 1 || hittype[g_i].spriteextra == 128)
			{
				if (actorfella(g_i))
					ps[g_p].actors_killed += *insptr;
			}
		}
		else ps[g_p].actors_killed += *insptr;
		hittype[g_i].actorstayput = -1;
		insptr++;
		break;
	case concmd_lotsofglass:
		insptr++;
		spriteglass(g_i,*insptr);
		insptr++;
		break;
	case concmd_killit:
		insptr++;
		killit_flag = 1;
		break;
	case concmd_addweapon:
		insptr++;
		if( ps[g_p].gotweapon[*insptr] == 0 ) fi.addweapon( &ps[g_p], *insptr );
		else if( ps[g_p].ammo_amount[*insptr] >= max_ammo_amount[*insptr] )
		{
				killit_flag = 2;
				break;
		}
		addammo( *insptr, &ps[g_p], *(insptr+1) );
		if(ps[g_p].curr_weapon == KNEE_WEAPON)
			if( ps[g_p].gotweapon[*insptr] )
				fi.addweapon( &ps[g_p], *insptr );
		insptr+=2;
		break;
	case concmd_debug:
		insptr++;
		Printf("%d\n",*insptr);
		insptr++;
		break;
	case concmd_endofgame:
		insptr++;
		ps[g_p].timebeforeexit = *insptr;
		ps[g_p].customexitsound = -1;
		ud.eog = true;
		insptr++;
		break;

	case concmd_isdrunk: // todo: move out to player_r.
		insptr++;
		ps[g_p].drink_amt += *insptr;
		j = sprite[ps[g_p].i].extra;
		if (j > 0)
			j += *insptr;
		if (j > max_player_health * 2)
			j = max_player_health * 2;
		if (j < 0)
			j = 0;

		if (ud.god == 0)
		{
			if (*insptr > 0)
			{
				if ((j - *insptr) < (max_player_health >> 2) &&
					j >= (max_player_health >> 2))
					S_PlayActorSound(DUKE_GOTHEALTHATLOW, ps[g_p].i);

				ps[g_p].last_extra = j;
			}

			sprite[ps[g_p].i].extra = j;
		}
		if (ps[g_p].drink_amt > 100)
			ps[g_p].drink_amt = 100;

		if (sprite[ps[g_p].i].extra >= max_player_health)
		{
			sprite[ps[g_p].i].extra = max_player_health;
			ps[g_p].last_extra = max_player_health;
		}
		insptr++;
		break;
	case concmd_strafeleft:
		insptr++;
		fi.movesprite(g_i, sintable[(g_sp->ang + 1024) & 2047] >> 10, sintable[(g_sp->ang + 512) & 2047] >> 10, g_sp->zvel, CLIPMASK0);
		break;
	case concmd_straferight:
		insptr++;
		fi.movesprite(g_i, sintable[(g_sp->ang - 0) & 2047] >> 10, sintable[(g_sp->ang - 512) & 2047] >> 10, g_sp->zvel, CLIPMASK0);
		break;
	case concmd_larrybird:
		insptr++;
		ps[g_p].posz = sector[sprite[ps[g_p].i].sectnum].ceilingz;
		sprite[ps[g_p].i].z = ps[g_p].posz;
		break;
	case concmd_destroyit:
		insptr++;
		destroyit(g_i);
		break;
	case concmd_iseat: // move out to player_r.
		insptr++;
		ps[g_p].eat += *insptr;
		if (ps[g_p].eat > 100)
		{
			ps[g_p].eat = 100;
		}
		ps[g_p].drink_amt -= *insptr;
		if (ps[g_p].drink_amt < 0)
			ps[g_p].drink_amt = 0;
		j = sprite[ps[g_p].i].extra;
		if (g_sp->picnum != TILE_ATOMICHEALTH)
		{
			if (j > max_player_health && *insptr > 0)
			{
				insptr++;
				break;
			}
			else
			{
				if (j > 0)
					j += (*insptr) * 3;
				if (j > max_player_health && *insptr > 0)
					j = max_player_health;
			}
		}
		else
		{
			if (j > 0)
				j += *insptr;
			if (j > (max_player_health << 1))
				j = (max_player_health << 1);
		}

		if (j < 0) j = 0;

		if (ud.god == 0)
		{
			if (*insptr > 0)
			{
				if ((j - *insptr) < (max_player_health >> 2) &&
					j >= (max_player_health >> 2))
					S_PlayActorSound(229, ps[g_p].i);

				ps[g_p].last_extra = j;
			}

			sprite[ps[g_p].i].extra = j;
		}

		insptr++;
		break;

	case concmd_addphealth: // todo: move out to player.
		insptr++;

		if(!isRR() && ps[g_p].newowner >= 0)
		{
			ps[g_p].newowner = -1;
			ps[g_p].posx = ps[g_p].oposx;
			ps[g_p].posy = ps[g_p].oposy;
			ps[g_p].posz = ps[g_p].oposz;
			ps[g_p].q16ang = ps[g_p].oq16ang;
			updatesector(ps[g_p].posx,ps[g_p].posy,&ps[g_p].cursectnum);
			setpal(&ps[g_p]);

			j = headspritestat[1];
			while (j >= 0)
			{
				if (sprite[j].picnum == TILE_CAMERA1)
					sprite[j].yvel = 0;
				j = nextspritestat[j];
			}
		}

		j = sprite[ps[g_p].i].extra;

		if(g_sp->picnum != TILE_ATOMICHEALTH)
		{
			if( j > max_player_health && *insptr > 0 )
			{
				insptr++;
				break;
			}
			else
			{
				if(j > 0)
					j += *insptr;
				if ( j > max_player_health && *insptr > 0 )
					j = max_player_health;
			}
		}
		else
		{
			if( j > 0 )
				j += *insptr;
			if ( j > (max_player_health<<1) )
				j = (max_player_health<<1);
		}

		if(j < 0) j = 0;

		if(ud.god == 0)
		{
			if(*insptr > 0)
			{
				if( ( j - *insptr ) < (max_player_health>>2) &&
					j >= (max_player_health>>2) )
						S_PlayActorSound(isRR()? 229 : DUKE_GOTHEALTHATLOW,ps[g_p].i);

				ps[g_p].last_extra = j;
			}

			sprite[ps[g_p].i].extra = j;
		}

		insptr++;
		break;

	case concmd_state:
		{
			auto tempscrptr = insptr + 2;
			insptr = &ScriptCode[*(insptr + 1)];
			while (1) if (parse()) break;
			insptr = tempscrptr;
		}
		break;
	case concmd_leftbrace:
		insptr++;
		while (1) if (parse()) break;
		break;
	case concmd_move:
		g_t[0]=0;
		insptr++;
		g_t[1] = *insptr;
		insptr++;
		g_sp->hitag = *insptr;
		insptr++;
		if(g_sp->hitag&random_angle)
			g_sp->ang = krand()&2047;
		break;
	case concmd_spawn:
		insptr++;
		if(g_sp->sectnum >= 0 && g_sp->sectnum < MAXSECTORS)
			fi.spawn(g_i,*insptr);
		insptr++;
		break;
	case concmd_ifwasweapon:
	case concmd_ifspawnedby:	// these two are the same
		insptr++;
		parseifelse( hittype[g_i].picnum == *insptr);
		break;
	case concmd_ifai:
		insptr++;
		parseifelse(g_t[5] == *insptr);
		break;
	case concmd_ifaction:
		insptr++;
		parseifelse(g_t[4] == *insptr);
		break;
	case concmd_ifactioncount:
		insptr++;
		parseifelse(g_t[2] >= *insptr);
		break;
	case concmd_resetactioncount:
		insptr++;
		g_t[2] = 0;
		break;
	case concmd_debris:
	{
			short dnum;

			insptr++;
			dnum = *insptr;
			insptr++;
			bool weap = fi.spawnweapondebris(g_sp->picnum, dnum);

			if(g_sp->sectnum >= 0 && g_sp->sectnum < MAXSECTORS)
				for(j=(*insptr)-1;j>=0;j--)
			{
				if(weap)
					s = 0;
				else s = (krand()%3);

				l = EGS(g_sp->sectnum,
					g_sp->x + (krand() & 255) - 128, g_sp->y + (krand() & 255) - 128, g_sp->z - (8 << 8) - (krand() & 8191),
					dnum + s, g_sp->shade, 32 + (krand() & 15), 32 + (krand() & 15),
					krand() & 2047, (krand() & 127) + 32, -(krand() & 2047), g_i, 5);
				if(weap)
					sprite[l].yvel = weaponsandammosprites[j%14];
				else sprite[l].yvel = -1;
				sprite[l].pal = g_sp->pal;
			}
			insptr++;
		}
		break;
	case concmd_count:
		insptr++;
		g_t[0] = (short) *insptr;
		insptr++;
		break;
	case concmd_cstator:
		insptr++;
		g_sp->cstat |= (short)*insptr;
		insptr++;
		break;
	case concmd_clipdist:
		insptr++;
		g_sp->clipdist = (short) *insptr;
		insptr++;
		break;
	case concmd_cstat:
		insptr++;
		g_sp->cstat = (short) *insptr;
		insptr++;
		break;
	case concmd_newpic:
		insptr++;
		g_sp->picnum = (short)*insptr;
		insptr++;
		break;
	case concmd_ifmove:
		insptr++;
		parseifelse(g_t[1] == *insptr);
		break;
	case concmd_resetplayer:
		insptr++;

		if(ud.multimode < 2)
		{
			gameaction = ga_autoloadgame;
			killit_flag = 2;
		}
		else
		{
			// I am not convinced this is even remotely smart to be executed from here...
			pickrandomspot(g_p);
			g_sp->x = hittype[g_i].bposx = ps[g_p].bobposx = ps[g_p].oposx = ps[g_p].posx;
			g_sp->y = hittype[g_i].bposy = ps[g_p].bobposy = ps[g_p].oposy = ps[g_p].posy;
			g_sp->z = hittype[g_i].bposy = ps[g_p].oposz = ps[g_p].posz;
			updatesector(ps[g_p].posx, ps[g_p].posy, &ps[g_p].cursectnum);
			setsprite(ps[g_p].i, ps[g_p].posx, ps[g_p].posy, ps[g_p].posz + PHEIGHT);
			g_sp->cstat = 257;

			g_sp->shade = -12;
			g_sp->clipdist = 64;
			g_sp->xrepeat = 42;
			g_sp->yrepeat = 36;
			g_sp->owner = g_i;
			g_sp->xoffset = 0;
			g_sp->pal = ps[g_p].palookup;

			ps[g_p].last_extra = g_sp->extra = max_player_health;
			ps[g_p].wantweaponfire = -1;
			ps[g_p].sethoriz(100);
			ps[g_p].on_crane = -1;
			ps[g_p].frag_ps = g_p;
			ps[g_p].sethorizoff(0);
			ps[g_p].opyoff = 0;
			ps[g_p].wackedbyactor = -1;
			ps[g_p].shield_amount = max_armour_amount;
			ps[g_p].dead_flag = 0;
			ps[g_p].pals.a = 0;
			ps[g_p].footprintcount = 0;
			ps[g_p].weapreccnt = 0;
			ps[g_p].ftq = 0;
			ps[g_p].posxv = ps[g_p].posyv = 0;
			if (!isRR()) ps[g_p].setrotscrnang(0);

			ps[g_p].falling_counter = 0;

			hittype[g_i].extra = -1;
			hittype[g_i].owner = g_i;

			hittype[g_i].cgg = 0;
			hittype[g_i].movflag = 0;
			hittype[g_i].tempang = 0;
			hittype[g_i].actorstayput = -1;
			hittype[g_i].dispicnum = 0;
			hittype[g_i].owner = ps[g_p].i;
			hittype[g_i].temp_data[4] = 0;

			resetinventory(g_p);
			resetweapons(g_p);
		}
		setpal(&ps[g_p]);
		break;
	case concmd_ifcoop:
		parseifelse(ud.coop || numplayers > 2);
		break;
	case concmd_ifonmud:
		parseifelse(abs(g_sp->z - sector[g_sp->sectnum].floorz) < (32 << 8) && sector[g_sp->sectnum].floorpicnum == 3073); // eew, hard coded tile numbers... :?
		break;
	case concmd_ifonwater:
		parseifelse( abs(g_sp->z-sector[g_sp->sectnum].floorz) < (32<<8) && sector[g_sp->sectnum].lotag == ST_1_ABOVE_WATER);
		break;
	case concmd_ifmotofast:
		parseifelse(ps[g_p].MotoSpeed > 60);
		break;
	case concmd_ifonmoto:
		parseifelse(ps[g_p].OnMotorcycle == 1);
		break;
	case concmd_ifonboat:
		parseifelse(ps[g_p].OnBoat == 1);
		break;
	case concmd_ifsizedown:
		g_sp->xrepeat--;
		g_sp->yrepeat--;
		parseifelse(g_sp->xrepeat <= 5);
		break;
	case concmd_ifwind:
		parseifelse(WindTime > 0);
		break;

	case concmd_ifinwater:
		parseifelse( sector[g_sp->sectnum].lotag == 2);
		break;
	case concmd_ifcount:
		insptr++;
		parseifelse(g_t[0] >= *insptr);
		break;
	case concmd_ifactor:
		insptr++;
		parseifelse(g_sp->picnum == *insptr);
		break;
	case concmd_resetcount:
		insptr++;
		g_t[0] = 0;
		break;
	case concmd_addinventory:
		insptr+=2;
		switch(*(insptr-1))
		{
			case 0:
				ps[g_p].steroids_amount = *insptr;
				ps[g_p].inven_icon = 2;
				break;
			case 1:
				ps[g_p].shield_amount +=		  *insptr;// 100;
				if(ps[g_p].shield_amount > max_player_health)
					ps[g_p].shield_amount = max_player_health;
				break;
			case 2:
				ps[g_p].scuba_amount =			   *insptr;// 1600;
				ps[g_p].inven_icon = 6;
				break;
			case 3:
				ps[g_p].holoduke_amount =		   *insptr;// 1600;
				ps[g_p].inven_icon = 3;
				break;
			case 4:
				ps[g_p].jetpack_amount =		   *insptr;// 1600;
				ps[g_p].inven_icon = 4;
				break;
			case 6:
				if (isRR())
				{
					switch (g_sp->lotag)
					{
					case 100: ps[g_p].keys[1] = 1; break;
					case 101: ps[g_p].keys[2] = 1; break;
					case 102: ps[g_p].keys[3] = 1; break;
					case 103: ps[g_p].keys[4] = 1; break;
					}
				}
				else
				{
					switch (g_sp->pal)
					{
					case  0: ps[g_p].got_access |= 1; break;
					case 21: ps[g_p].got_access |= 2; break;
					case 23: ps[g_p].got_access |= 4; break;
					}
				}
				break;
			case 7:
				ps[g_p].heat_amount = *insptr;
				ps[g_p].inven_icon = 5;
				break;
			case 9:
				ps[g_p].inven_icon = 1;
				ps[g_p].firstaid_amount = *insptr;
				break;
			case 10:
				ps[g_p].inven_icon = 7;
				ps[g_p].boot_amount = *insptr;
				break;
		}
		insptr++;
		break;
	case concmd_hitradius:
		fi.hitradius(g_i,*(insptr+1),*(insptr+2),*(insptr+3),*(insptr+4),*(insptr+5));
		insptr+=6;
		break;
	case concmd_ifp:
	{
			insptr++;

			l = *insptr;
			j = 0;

			s = g_sp->xvel;

			// sigh... this was yet another place where number literals were used as bit masks for every single value, making the code totally unreadable.
			if( (l& pducking) && ps[g_p].on_ground && (PlayerInput(g_p, SB_CROUCH) ^ !!(ps[g_p].crouch_toggle) ))
					j = 1;
			else if( (l& pfalling) && ps[g_p].jumping_counter == 0 && !ps[g_p].on_ground &&	ps[g_p].poszv > 2048 )
					j = 1;
			else if( (l& pjumping) && ps[g_p].jumping_counter > 348 )
					j = 1;
			else if( (l& pstanding) && s >= 0 && s < 8)
					j = 1;
			else if( (l& pwalking) && s >= 8 && !(PlayerInput(g_p, SB_RUN)) )
					j = 1;
			else if( (l& prunning) && s >= 8 && PlayerInput(g_p, SB_RUN) )
					j = 1;
			else if( (l& phigher) && ps[g_p].posz < (g_sp->z-(48<<8)) )
					j = 1;
			else if( (l& pwalkingback) && s <= -8 && !(PlayerInput(g_p, SB_RUN)) )
					j = 1;
			else if( (l& prunningback) && s <= -8 && (PlayerInput(g_p, SB_RUN)) )
					j = 1;
			else if( (l& pkicking) && ( ps[g_p].quick_kick > 0 || ( ps[g_p].curr_weapon == KNEE_WEAPON && ps[g_p].kickback_pic > 0 ) ) )
					j = 1;
			else if( (l& pshrunk) && sprite[ps[g_p].i].xrepeat < (isRR() ? 8 : 32))
					j = 1;
			else if( (l& pjetpack) && ps[g_p].jetpack_on )
					j = 1;
			else if( (l& ponsteroids) && ps[g_p].steroids_amount > 0 && ps[g_p].steroids_amount < 400 )
					j = 1;
			else if( (l& ponground) && ps[g_p].on_ground)
					j = 1;
			else if( (l& palive) && sprite[ps[g_p].i].xrepeat > (isRR() ? 8 : 32) && sprite[ps[g_p].i].extra > 0 && ps[g_p].timebeforeexit == 0 )
					j = 1;
			else if( (l& pdead) && sprite[ps[g_p].i].extra <= 0)
					j = 1;
			else if( (l& pfacing) )
			{
				if (g_sp->picnum == TILE_APLAYER && ud.multimode > 1)
					j = getincangle(ps[otherp].getang(), getangle(ps[g_p].posx - ps[otherp].posx, ps[g_p].posy - ps[otherp].posy));
				else
					j = getincangle(ps[g_p].getang(), getangle(g_sp->x - ps[g_p].posx, g_sp->y - ps[g_p].posy));

				if( j > -128 && j < 128 )
					j = 1;
				else
					j = 0;
			}

			parseifelse((int) j);

		}
		break;
	case concmd_ifstrength:
		insptr++;
		parseifelse(g_sp->extra <= *insptr);
		break;
	case concmd_guts:
		insptr += 2;
		fi.guts(g_sp,*(insptr-1),*insptr,g_p);
		insptr++;
		break;
	case concmd_slapplayer:
		insptr++;
		forceplayerangle(&ps[g_p]);
		ps[g_p].posxv -= sintable[(ps[g_p].getang() + 512) & 2047] << 7;
		ps[g_p].posyv -= sintable[ps[g_p].getang() & 2047] << 7;
		return 0;
	case concmd_wackplayer:
		insptr++;
		if (!isRR())
			forceplayerangle(&ps[g_p]);
		else
		{
			ps[g_p].posxv -= sintable[(ps[g_p].getang() + 512) & 2047] << 10;
			ps[g_p].posyv -= sintable[ps[g_p].getang() & 2047] << 10;
			ps[g_p].jumping_counter = 767;
			ps[g_p].jumping_toggle = 1;
		}
		return 0;
	case concmd_ifgapzl:
		insptr++;
		parseifelse( (( hittype[g_i].floorz - hittype[g_i].ceilingz ) >> 8 ) < *insptr);
		break;
	case concmd_ifhitspace:
		parseifelse(PlayerInput(g_p, SB_OPEN));
		break;
	case concmd_ifoutside:
		parseifelse(sector[g_sp->sectnum].ceilingstat & 1);
		break;
	case concmd_ifmultiplayer:
		parseifelse(ud.multimode > 1);
		break;
	case concmd_operate:
		insptr++;
		if( sector[g_sp->sectnum].lotag == 0 )
		{
			int16_t neartagsector, neartagwall, neartagsprite;
			int32_t neartaghitdist;
			neartag(g_sp->x,g_sp->y,g_sp->z-(32<<8),g_sp->sectnum,g_sp->ang,&neartagsector,&neartagwall,&neartagsprite,&neartaghitdist,768L,1);
			if( neartagsector >= 0 && isanearoperator(sector[neartagsector].lotag) )
				if( (sector[neartagsector].lotag&0xff) == ST_23_SWINGING_DOOR || sector[neartagsector].floorz == sector[neartagsector].ceilingz )
					if( (sector[neartagsector].lotag&16384) == 0 )
						if( (sector[neartagsector].lotag&32768) == 0 )
					{
						j = headspritesect[neartagsector];
						while(j >= 0)
						{
							if(sprite[j].picnum == ACTIVATOR)
								break;
							j = nextspritesect[j];
						}
						if(j == -1)
							operatesectors(neartagsector,g_i);
					}
		}
		break;
	case concmd_ifinspace:
		parseifelse(fi.ceilingspace(g_sp->sectnum));
		break;

	case concmd_spritepal:
		insptr++;
		if(g_sp->picnum != TILE_APLAYER)
			hittype[g_i].tempang = g_sp->pal;
		g_sp->pal = *insptr;
		insptr++;
		break;

	case concmd_cactor:
		insptr++;
		g_sp->picnum = *insptr;
		insptr++;
		break;

	case concmd_ifbulletnear:
		parseifelse( dodge(g_sp) == 1);
		break;
	case concmd_ifrespawn:
		if( badguy(g_sp) )
			parseifelse( ud.respawn_monsters );
		else if( inventory(g_sp) )
			parseifelse( ud.respawn_inventory );
		else
			parseifelse( ud.respawn_items );
		break;
	case concmd_iffloordistl:
		insptr++;
		parseifelse( (hittype[g_i].floorz - g_sp->z) <= ((*insptr)<<8));
		break;
	case concmd_ifceilingdistl:
		insptr++;
		parseifelse( ( g_sp->z - hittype[g_i].ceilingz ) <= ((*insptr)<<8));
		break;
	case concmd_palfrom:
		insptr++;
		SetPlayerPal(&ps[g_p], PalEntry(insptr[0], insptr[1], insptr[2], insptr[3]));
		insptr += 4;
		break;

/*		  case 74:
		insptr++;
		getglobalz(g_i);
		parseifelse( (( hittype[g_i].floorz - hittype[g_i].ceilingz ) >> 8 ) >= *insptr);
		break;
*/
	case concmd_addlog:
	{	int l;
		int lFile;
		insptr++;
		lFile=*(insptr++);	// file
		l=*(insptr++);	// line
		// this was only printing file name and line number as debug output.
		break;
	}
	case concmd_addlogvar:
	{	int l;
		int lFile;
		insptr++;
		lFile=*(insptr++);	// file
		l=*(insptr++);	// l=Line number, *instpr=varID
		if( (*insptr >= iGameVarCount)
			|| *insptr < 0
			)
		{
			// invalid varID
			insptr++;
			break;	// out of switch
		}
		DPrintf(DMSG_NOTIFY, "ADDLOGVAR: ");
			
		if( aGameVars[*insptr].dwFlags & GAMEVAR_FLAG_READONLY)
		{
			DPrintf(DMSG_NOTIFY, " (read-only)");
		}
		if( aGameVars[*insptr].dwFlags & GAMEVAR_FLAG_PERPLAYER)
		{
			DPrintf(DMSG_NOTIFY, " (Per Player. Player=%d)",g_p);
		}
		else if( aGameVars[*insptr].dwFlags & GAMEVAR_FLAG_PERACTOR)
		{
			DPrintf(DMSG_NOTIFY, " (Per Actor. Actor=%d)",g_i);
		}
		else
		{
			DPrintf(DMSG_NOTIFY, " (Global)");
		}
		DPrintf(DMSG_NOTIFY, " =%d",	GetGameVarID(*insptr, g_i, g_p));
		insptr++;
		break;
	}
	case concmd_setvar:
	{	int i;
		insptr++;
		i=*(insptr++);	// ID of def
		SetGameVarID(i, *insptr, g_i, g_p );
		insptr++;
		break;
	}
	case concmd_setvarvar:
	{	int i;
		insptr++;
		i=*(insptr++);	// ID of def
		SetGameVarID(i, GetGameVarID(*insptr, g_i, g_p), g_i, g_p );
//			aGameVars[i].lValue = aGameVars[*insptr].lValue;
		insptr++;
		break;
	}
	case concmd_addvar:
	{	int i;		
		insptr++;
		i=*(insptr++);	// ID of def
//sprintf(g_szBuf,"AddVar %d to Var ID=%d, g_i=%d, g_p=%d\n",*insptr, i, g_i, g_p);
//AddLog(g_szBuf);
		SetGameVarID(i, GetGameVarID(i, g_i, g_p) + *insptr, g_i, g_p );
		insptr++;
		break;
	}
		
	case concmd_addvarvar:
	{	int i;
		insptr++;
		i=*(insptr++);	// ID of def
		SetGameVarID(i, GetGameVarID(i, g_i, g_p) + GetGameVarID(*insptr, g_i, g_p), g_i, g_p );
		insptr++;
		break;
	}
	case concmd_ifvarvare:
	{
		int i;
		insptr++;
		i=*(insptr++);	// ID of def
		j=0;
		if(GetGameVarID(i, g_i, g_p) == GetGameVarID(*(insptr), g_i, g_p) )
		{
			j=1;
		}
		parseifelse( j );
		break;
	}
	case concmd_ifvarvarg:
	{
		int i;
		insptr++;
		i=*(insptr++);	// ID of def
		j=0;
		if(GetGameVarID(i, g_i, g_p) > GetGameVarID(*(insptr), g_i, g_p) )
		{
			j=1;
		}
		parseifelse( j );
		break;
	}
	case concmd_ifvarvarl:
	{
		int i;
		insptr++;
		i=*(insptr++);	// ID of def
		j=0;
		if(GetGameVarID(i, g_i, g_p) < GetGameVarID(*(insptr), g_i, g_p) )
		{
			j=1;
		}
		parseifelse( j );
		break;
	}
	case concmd_ifvare:
	{
		int i;
		insptr++;
		i=*(insptr++);	// ID of def
		j=0;
		if(GetGameVarID(i, g_i, g_p) == *insptr)
		{
			j=1;
		}
		parseifelse( j );
		break;
	}
	case concmd_ifvarg:
	{
		int i;
		insptr++;
		i=*(insptr++);	// ID of def
		j=0;
		if(GetGameVarID(i, g_i, g_p) > *insptr)
		{
			j=1;
		}
		parseifelse( j );
		break;
	}
	case concmd_ifvarl:
	{
		int i;
		insptr++;
		i=*(insptr++);	// ID of def
		j=0;
		if(GetGameVarID(i, g_i, g_p) < *insptr)
		{
			j=1;
		}
		parseifelse( j );
		break;
	}
	case concmd_ifphealthl:
		insptr++;
		parseifelse( sprite[ps[g_p].i].extra < *insptr);
		break;

	case concmd_ifpinventory:
	{
			insptr++;
			j = 0;
			switch(*(insptr++))
			{
				case 0:if( ps[g_p].steroids_amount != *insptr)
						j = 1;
					break;
				case 1:if(ps[g_p].shield_amount != max_player_health )
						j = 1;
					break;
				case 2:if(ps[g_p].scuba_amount != *insptr) j = 1;break;
				case 3:if(ps[g_p].holoduke_amount != *insptr) j = 1;break;
				case 4:if(ps[g_p].jetpack_amount != *insptr) j = 1;break;
				case 6:
					if (isRR())
					{
						switch (g_sp->lotag)
						{
						case 100: if (ps[g_p].keys[1]) j = 1; break;
						case 101: if (ps[g_p].keys[2]) j = 1; break;
						case 102: if (ps[g_p].keys[3]) j = 1; break;
						case 103: if (ps[g_p].keys[4]) j = 1; break;
						}
					}
					else
					{
						switch (g_sp->pal)
						{
						case  0: if (ps[g_p].got_access & 1) j = 1; break;
						case 21: if (ps[g_p].got_access & 2) j = 1; break;
						case 23: if (ps[g_p].got_access & 4) j = 1; break;
						}
					}
					break;
				case 7:if(ps[g_p].heat_amount != *insptr) j = 1;break;
				case 9:
					if(ps[g_p].firstaid_amount != *insptr) j = 1;break;
				case 10:
					if(ps[g_p].boot_amount != *insptr) j = 1;break;
			}

			parseifelse(j);
			break;
		}
	case concmd_pstomp:
		insptr++;
		if( ps[g_p].knee_incs == 0 && sprite[ps[g_p].i].xrepeat >= (isRR()? 9: 40) )
			if( cansee(g_sp->x,g_sp->y,g_sp->z-(4<<8),g_sp->sectnum,ps[g_p].posx,ps[g_p].posy,ps[g_p].posz+(16<<8),sprite[ps[g_p].i].sectnum) )
		{
			ps[g_p].knee_incs = 1;
			if(ps[g_p].weapon_pos == 0)
				ps[g_p].weapon_pos = -1;
			ps[g_p].actorsqu = g_i;
		}
		break;
	case concmd_ifawayfromwall:
	{
		short s1;

		s1 = g_sp->sectnum;

		j = 0;

		updatesector(g_sp->x + 108, g_sp->y + 108, &s1);
		if (s1 == g_sp->sectnum)
		{
			updatesector(g_sp->x - 108, g_sp->y - 108, &s1);
			if (s1 == g_sp->sectnum)
			{
				updatesector(g_sp->x + 108, g_sp->y - 108, &s1);
				if (s1 == g_sp->sectnum)
				{
					updatesector(g_sp->x - 108, g_sp->y + 108, &s1);
					if (s1 == g_sp->sectnum)
						j = 1;
				}
			}
		}
		parseifelse(j);
		break;
	}

	case concmd_quote:
		insptr++;
		FTA(*insptr,&ps[g_p]);
		insptr++;
		break;
	case concmd_ifinouterspace:
		parseifelse( fi.floorspace(g_sp->sectnum));
		break;
	case concmd_ifnotmoving:
		parseifelse( (hittype[g_i].movflag&49152) > 16384 );
		break;
	case concmd_respawnhitag:
		insptr++;
		fi.respawnhitag(g_sp);
		break;
	case concmd_ifspritepal:
		insptr++;
		parseifelse( g_sp->pal == *insptr);
		break;

	case concmd_ifangdiffl:
		insptr++;
		j = abs(getincangle(ps[g_p].getang(),g_sp->ang));
		parseifelse( j <= *insptr);
		break;

	case concmd_ifnosounds:
		parseifelse(!S_CheckAnyActorSoundPlaying(g_i) );
		break;

	case concmd_ifplaybackon: //Twentieth Anniversary World Tour
		parseifelse(false);
		break;

	default:
		Printf(TEXTCOLOR_RED "Unrecognized PCode of %d  in parse.  Killing current sprite.\n",*insptr);
		Printf(TEXTCOLOR_RED "Offset=%0X\n",int(insptr-ScriptCode.Data()));
		killit_flag = 1;
		break;
	}
	return 0;
}

void execute(int i,int p,int x)
{
	if (actorinfo[sprite[i].picnum].scriptaddress == 0) return;

	int done;
	spritetype* g_sp;

	ParseState s;
	s.g_i = i;	// Sprite ID
	s.g_p = p;	// Player ID
	s.g_x = x;	// ??
	g_sp = s.g_sp = &sprite[i];	// Pointer to sprite structure
	s.g_t = &hittype[i].temp_data[0];	// Sprite's 'extra' data

	if (actorinfo[g_sp->picnum].scriptaddress == 0) return;
	s.insptr = &ScriptCode[4 + (actorinfo[g_sp->picnum].scriptaddress)];

	s.killit_flag = 0;

	if(g_sp->sectnum < 0 || g_sp->sectnum >= MAXSECTORS)
	{
		if(badguy(g_sp))
			ps[p].actors_killed++;
		deletesprite(i);
		return;
	}

	if (s.g_t[4])
	{
		// This code was utterly cryptic in the original source.
		auto ptr = &ScriptCode[s.g_t[4]];
		int numframes = ptr[1];
		int increment = ptr[3];
		int delay =  ptr[4];

		g_sp->lotag += TICSPERFRAME;
		if (g_sp->lotag > delay)
		{
			s.g_t[2]++;
			g_sp->lotag = 0;
			s.g_t[3] += increment;
		}
		if (abs(s.g_t[3]) >= abs(numframes * increment))
			s.g_t[3] = 0;
	}

	do
		done = s.parse();
	while( done == 0 );

	if(s.killit_flag == 1)
	{
		// if player was set to squish, first stop that...
		if(ps[p].actorsqu == i)
			ps[p].actorsqu = -1;
		killthesprite = true;
	}
	else
	{
		fi.move(i, p, x);

		if (g_sp->statnum == STAT_ACTOR)
		{
			if (badguy(g_sp))
			{
				if (g_sp->xrepeat > 60) goto quit;
				if (ud.respawn_monsters == 1 && g_sp->extra <= 0) goto quit;
			}
			else if (ud.respawn_items == 1 && (g_sp->cstat & 32768)) goto quit;

			if (hittype[i].timetosleep > 1)
				hittype[i].timetosleep--;
			else if (hittype[i].timetosleep == 1)
				changespritestat(i, STAT_ZOMBIEACTOR);
		}

		else if (g_sp->statnum == STAT_STANDABLE)
			fi.checktimetosleep(i);
	}
quit:
	if (killthesprite) deletesprite(i);
	killthesprite = false;
}


//---------------------------------------------------------------------------
//
// 
//
//---------------------------------------------------------------------------

void OnEvent(int iEventID, int p, int i, int x)
{
	char done;

	if (iEventID >= MAXGAMEEVENTS)
	{
		Printf("Invalid Event ID\n");
		return;
	}
	if (apScriptGameEvent[iEventID] == 0)
	{
		return;
	}

	ParseState s;
	s.g_i = i;	// current sprite ID
	s.g_p = p;	/// current player ID
	s.g_x = x;	// ?
	s.g_sp = &sprite[i];
	s.g_t = &hittype[i].temp_data[0];

	s.insptr = &ScriptCode[apScriptGameEvent[iEventID]];

	s.killit_flag = 0;
	do
		done = s.parse();
	while (done == 0);
}

CCMD(endofgame)
{
	ps[0].timebeforeexit = 120;
	ps[0].customexitsound = -1;
	ChangeLevel(nullptr, -1);
}

END_DUKE_NS
