//Version 7
//Copyright (C) 2015-2016 Kenneth Frank Henderson, Jr.
//mailto:KenFH77@gmail.com

/*
 Patched:
Compatible with Zandronum ?-2.1.2-?, Wrath of Cronos 1.7b-1.8a2, Heretic, Hexen 1/DK, Doom/FreeDoom 1/2/TNT/Plutonia, Chex, Strife?
WoC now brings weak/inactive/late-joining players up to reasonable XP/Gold as-needed; minimum level of 75% of highest approximate average is maintained even if all clients disconnect/spectate; set CVar KFHJ_WoC_DisableAutoLevel to disable this
CVARINFO'd CVars now have KFHJ_* equivalents to override after CVARINFO loads and circumvent the bug in Zandronum 2.0-2.1.2
New CVar KFHJ_WoC_MinLevel to start games above level 1
Voting, information and teleport system, execute "puke 888 256 256 256" for information, use to work around bugs in maps that e.g. don't support coop fully

 Handled by ZADmFlags=64:
Unification of Coop Key inventory
 Handled by ZADmFlags=16 (total:80):
Nonblocking players

TODO:
WoC versions with Merchant now place one guaranteed in every normal level?
Store player data to database or client cvars? (BestBot servers don't support persistant databases?, Client CVars are tricky until Zan3.0)
*/

#Library "KFHJWOCP"
#include "zcommon.acs"

#define	KFHJScr_MultiFunc0	876
#define	KFHJScr_Open		(KFHJScr_MultiFunc0+ 1)
#define	KFHJScr_Enter		(KFHJScr_MultiFunc0+ 2)
#define	KFHJScr_Death		(KFHJScr_MultiFunc0+ 3)
#define	KFHJScr_MultiFuncNet0	888

#define	KFHJ_PLAYERTID	1256
#define	KFHJ_MAXPLAYERS	64
#define	KFHJ_MAXTEAMS	64

int	KFHJVM_MinLevel=1;

#define	KFHJHUDM_TempID		27622
#define	KFHJHUDM_VoteID		27623
#define	KFHJHUDM_VoteX		1.50
#define	KFHJHUDM_VoteY		0.20
#define	KFHJHUDM_VoteTime	5.00

int	KFHJVM_VoteTime=-1;
int	KFHJVM_VoteValue=-1;
int	KFHJVM_Votes[KFHJ_MAXPLAYERS];

#define	KFHJVM_MarksSize	13
#define	KFHJVM_MarksOfsDeath	 1
#define	KFHJVM_MarksOfsUser	 7
int	KFHJVM_Marks[KFHJ_MAXPLAYERS*KFHJVM_MarksSize];

function void	KFHJFun_LogClient(str s)
{
	HudMessage	(s:s	;HUDMSG_LOG	,KFHJHUDM_TempID,CR_UNTRANSLATED,0,0,1.0);
	HudMessage	(s:""	;HUDMSG_PLAIN	,KFHJHUDM_TempID,CR_UNTRANSLATED,0,0,1.0);
}

function void	KFHJFun_LogPrintClient(str s)
{
	HudMessage	(s:s	;HUDMSG_LOG	,KFHJHUDM_TempID,CR_UNTRANSLATED,0,0,5.0);
}

function void	KFHJFun_LogPrintBoldClient(str s)
{
	HudMessageBold	(s:s	;HUDMSG_LOG	,KFHJHUDM_TempID,CR_UNTRANSLATED,0,0,5.0);
}

function void	KFHJFun_SomethingBroke(str s)
{
	str e=StrParam(s:"BROKEN ",s:s);
	if(ConsolePlayerNumber()<0){Log(s:e);}
	KFHJFun_LogPrintBoldClient(e);
}

function int	KFHJFun_RequestScriptPuke(int s,int arg0,int arg1,int arg2,int arg3)
{
//	return	RequestScriptPuke(s,arg0,arg1,arg2,arg3);
//	return	RequestScriptPuke(s,arg0,arg1,arg2);
//	ConsoleCommand(StrParam(s:"puke ",d:s,s:" ",d:arg0,s:" ",d:arg1,s:" ",d:arg2,s:" ",d:arg3));
//Needed as of Zandronum 2.1.2, puke only takes 3 args plus script
	ConsoleCommand(StrParam(s:"puke ",d:s,s:" ",d:arg0,s:" ",d:arg1,s:" ",d:arg2));
	return	1;
}

function int	KFHJFun_StrLenDecoded(str s)
{
	int	l=0,n=StrLen(s),i,c;

 for(i=0;i<n;i++)
 {
  switch(GetChar(s,i))
  {
   case 0x1C:	//Color code, encoded; seemingly the only \ code that still encodes to a multibyte sequence
	i++;	//Unfortunately, THIS COULD CHANGE, which would break this, but it works for now, and it's better than having the padding wrong already
	if(GetChar(s,i)=='[')
	{
	 do
	 {
		i++;
		c=GetChar(s,i);
	 }while((c!=']')&&(i<n));
	}
	continue;
   case '\a':
	continue;
//Formating: \t \n \r \b \? \v \f all have potential consequences, but shouldn't show up in names
/*
//Apparently by the time they're ACS strings \ escapes have already been processed, which means we have to depend on what they encode TO...
// \OOO \xXX \t \n \r \a \b \? \v \f \<Enter> should all encode to single characters and do not need handling once encoding, except for what they encode to in some cases
   case '\\':	//Escape sequence
    if(i==(n-1)){l++;continue;}
    i++;
    switch(GetChar(s,i))
    {
     case '\\':	//'\'
	l++;i++;
	continue;
     case 'c':	//Color code
	i++;
	if(GetChar(s,i)=='[')
	{
	 do
	 {
		i++;
		c=GetChar(s,i);
	 }while((c!=']')&&(i<n));
	}
	continue;
     default:
	l++;
	continue;
    }
*/
   default:
	break;
  }
  l++;
 }

	return	l;
}

/*
	KFHJFun_DebugPrintString(StrParam(n:PlayerNumber()+1));
function void	KFHJFun_DebugPrintString(str s)
{
	str	t,s0="",s1="",s2="";
	int	n=StrLen(s),i=0;

	for(;i<n;i++)
	{
		t=StrParam(d:i);
		while(StrLen(t)<2){t=StrParam(s:"0",s:t);}
		s2=StrParam(s:s2,s:t);
		t=StrParam(x:GetChar(s,i));
		while(StrLen(t)<2){t=StrParam(s:"0",s:t);}
		s0=StrParam(s:s0,s:t);
		s1=StrParam(s:s1,c:GetChar(s,i));
		s1=StrParam(s:s1,s:" ");
	}
	Print(s:" ",s:s2,s:";",d:n,s:"\n\"",s:s0,s:"\"\n\"",s:s1,s:"\"");
}
*/

script KFHJScr_MultiFunc0(int arg1,int arg2,int arg3,int arg4) Net
{
  int i,j,k,l,m;
  str s,t,s0,s1;
  switch(arg1)
  {
   case   0:
   switch(arg2)
   {
    case   0:
    switch(arg3)
    {
     case   0:
	i=PlayerNumber();
	if(	(KFHJVM_VoteTime<Timer()	) ||
		(KFHJVM_Votes[0]<-8		) ||
		(i<0				) ||
		(i>=KFHJ_MAXPLAYERS		) )
	{ACS_ExecuteAlways(KFHJScr_MultiFunc0,0,256,256,256);terminate;}
	KFHJVM_Votes[i]=-1;
	terminate;
     default:
	break;
    }
    break;
    default:
	break;
   }
   break;
   case   1:
   switch(arg2)
   {
    case   0:
    switch(arg3)
    {
     case   0:
	i=PlayerNumber();
	if(	(KFHJVM_VoteTime<Timer()	) ||
		(KFHJVM_Votes[0]<-8		) ||
		(i<0				) ||
		(i>=KFHJ_MAXPLAYERS		) )
	{ACS_ExecuteAlways(KFHJScr_MultiFunc0,0,256,256,256);terminate;}
	KFHJVM_Votes[i]=1;
	terminate;
     default:
	break;
    }
    break;
    default:
	break;
   }
   break;
   case   2:
   switch(arg2)
   {
    case   0:
    switch(arg3)
    {
     case   0:
	i=PlayerNumber();
	if(	(KFHJVM_VoteTime<Timer()	) ||
		(KFHJVM_Votes[0]<-8		) ||
		(i<0				) ||
		(i>=KFHJ_MAXPLAYERS		) )
	{ACS_ExecuteAlways(KFHJScr_MultiFunc0,0,256,256,256);terminate;}
	KFHJVM_Votes[i]=0;
	terminate;
     default:
	break;
    }
    break;
    default:
	break;
   }
   break;
   case 256:
   switch(arg2)
   {
    case 256:
    switch(arg3)
    {
     case 256:
	KFHJFun_LogClient(StrParam(s:"\n
Puke 888 256  256  256  This message\n
Puke 888 256  256  257  Player List\n
Puke 888 256  256  258  Team List\n
Puke 888 256  256  259  Game Info\n
Puke 888 999 1000    #  Teleport to player #\n
Puke 888 999 1001    #  Teleport to where player # last died (risky)\n
Puke 888 999  999  999  Mark\n
Puke 888 999  999 1000  Recall\n
Puke 888   1    0    0  Vote Yes\n
Puke 888   0    0    0  Vote No\n
Puke 888   2    0    0  Vote Abstain\n
Puke 888 256  257    #  Call Vote:Ban Votes\n
Puke 888 256  258    #  Call Vote:Level\n
Puke 888 256  259    #  Call Vote:Gold\n"));
	terminate;
     case 257:
	s=StrParam(d:PlayerCount(),s:" player(s), you are #",d:PlayerNumber(),s:", your TID is #",d:KFHJ_PLAYERTID+PlayerNumber(),s:"/",d:ActivatorTID(),s:":",d:ThingCount(T_NONE,ActivatorTID()),s:", Class:",x:ClassifyActor(0),s:", Console is #",d:ConsolePlayerNumber(),s:"\nTe PC DC Li  # Player Name                        # I S B Ge PlaColor ");
	for(i=0;i<KFHJ_MAXPLAYERS;i++)
	{
		if(	(!PlayerInGame(i)	) &&
			(!PlayerIsSpectator(i)	) &&
			(!PlayerIsBot(i)	) )
		{continue;}
//		{break;}
		s0=StrParam(d:GetPlayerInfo(i,PLAYERINFO_TEAM));
		while(StrLen(s0)< 2){s0=StrParam(s:" ",s:s0);}
		s=StrParam(s:s,s:"\n",s:s0);
		s0=StrParam(d:PlayerClass(i));
		while(StrLen(s0)< 3){s0=StrParam(s:" ",s:s0);}
		s1=StrParam(d:GetPlayerInfo(i,PLAYERINFO_PLAYERCLASS));
		while(StrLen(s1)< 3){s1=StrParam(s:" ",s:s1);}
		s=StrParam(s:s,s:s0,s:s1);
		s0=StrParam(d:GetPlayerLivesLeft(i));
		while(StrLen(s0)< 3){s0=StrParam(s:" ",s:s0);}
		s=StrParam(s:s,s:s0);
		s0=StrParam(d:i);
		while(StrLen(s0)< 3){s0=StrParam(s:" ",s:s0);}
		s1=StrParam(s:"\"",n:i+1,s:"\"");
		while(KFHJFun_StrLenDecoded(s1)<33){s1=StrParam(s:s1,s:" ");}
		s=StrParam(s:s,s:s0,s:" ",s:s1,s:s0);
		s0=StrParam(d:GetPlayerInfo(i,PLAYERINFO_GENDER));
		while(StrLen(s0)< 3){s0=StrParam(s:" ",s:s0);}
		s1=StrParam(x:GetPlayerInfo(i,PLAYERINFO_COLOR));
		while(StrLen(s1)< 8){s1=StrParam(s:"0",s:s1);}
		s=StrParam(s:s,
		 s:" ",d:PlayerInGame(i),s:" ",d:PlayerIsSpectator(i),s:" ",d:PlayerIsBot(i),
		 s:s0,s:" ",s:s1);
//		 s:" ",s:d:PlayerIsLoggedIn(i),s:" ",s:GetPlayerAccountName(i)
//PlayerFrags(), GetWeapon(), GetActorProperty(TID,APROP_Health) PlayerArmorPoints()
//Obsolete: PlayerTeam() PlayerHealth()
//RedScore() RedTeamScore() BlueScore() BlueTeamScore() RedCount() RedTeamCount() BlueCount() BlueTeamCount() GetTeamScore(Team)
	}
	KFHJFun_LogClient(s);
	terminate;
     case 258:
	s=StrParam(s:"V Li Pl Fra Dea # Score Spre Point Wi Team Name                        # Ca As RetTc Col PlSta");
	for(i=0;i<KFHJ_MAXTEAMS;i++)
	{
		j=GetTeamProperty(i,TPROP_IsValid);
//		if(!j){continue;}
		if(!j){break;}
		s0=StrParam(d:GetTeamProperty(i,TPROP_NumLivePlayers));
		while(StrLen(s0)< 3){s0=StrParam(s:" ",s:s0);}
		s1=StrParam(d:GetTeamProperty(i,TPROP_NumPlayers));
		while(StrLen(s1)< 3){s1=StrParam(s:" ",s:s1);}
		s=StrParam(s:s,s:"\n",d:j,s:s0,s:s1);
		s0=StrParam(d:GetTeamProperty(i,TPROP_FragCount));
		while(StrLen(s0)< 4){s0=StrParam(s:" ",s:s0);}
		s1=StrParam(d:GetTeamProperty(i,TPROP_DeathCount));
		while(StrLen(s1)< 4){s1=StrParam(s:" ",s:s1);}
		s=StrParam(s:s,s:s0,s:s1);
		s0=StrParam(d:GetTeamProperty(i,TPROP_Score));
		while(StrLen(s0)< 6){s0=StrParam(s:" ",s:s0);}
		s1=StrParam(d:GetTeamProperty(i,TPROP_Spread));
		while(StrLen(s1)< 5){s1=StrParam(s:" ",s:s1);}
		s=StrParam(s:s,s:" ",d:i,s:s0,s:s1);
		s0=StrParam(d:GetTeamProperty(i,TPROP_PointCount));
		while(StrLen(s0)< 6){s0=StrParam(s:" ",s:s0);}
		s1=StrParam(d:GetTeamProperty(i,TPROP_WinCount));
		while(StrLen(s1)< 3){s1=StrParam(s:" ",s:s1);}
		s=StrParam(s:s,s:s0,s:s1);
		s0=StrParam(s:"\"",s:GetTeamProperty(i,TPROP_Name),s:"\"");
		while(KFHJFun_StrLenDecoded(s0)<33){s0=StrParam(s:s0,s:" ");}
		s=StrParam(s:s,s:" ",s:s0);
		s0=StrParam(d:GetTeamProperty(i,TPROP_Carrier));
		while(StrLen(s0)< 3){s0=StrParam(s:" ",s:s0);}
		s1=StrParam(d:GetTeamProperty(i,TPROP_Assister));
		while(StrLen(s1)< 3){s1=StrParam(s:" ",s:s1);}
		s=StrParam(s:s,d:i,s:s0,s:s1);
		s0=StrParam(d:GetTeamProperty(i,TPROP_ReturnTics));
		while(StrLen(s0)< 6){s0=StrParam(s:" ",s:s0);}
		s=StrParam(s:s,s:s0);
		s0=StrParam(d:GetTeamProperty(i,TPROP_TextColor));
		while(StrLen(s0)< 4){s0=StrParam(s:" ",s:s0);}
		s1=StrParam(d:GetTeamProperty(i,TPROP_PlayerStartNum));
		while(StrLen(s1)< 6){s1=StrParam(s:" ",s:s1);}
		s=StrParam(s:s,s:s0,s:s1);
	 if(j)
	 { //Part of this crashes if !GetTeamProperty(i,TPROP_IsValid)
		s=StrParam(s:s,
		 s:" \"",s:GetTeamProperty(i,TPROP_TeamItem),
		 s:"\" \"",s:GetTeamProperty(i,TPROP_WinnerTheme),
		 s:"\" \"",s:GetTeamProperty(i,TPROP_LoserTheme),s:"\"");
	 }
	}
	KFHJFun_LogClient(s);
	terminate;
     case 259:
	s0=StrParam(d:GetLevelInfo(LEVELINFO_CLUSTERNUM));
	while(StrLen(s0)< 2){s0=StrParam(s:" ",s:s0);}
	s1=StrParam(d:GetLevelInfo(LEVELINFO_LEVELNUM));
	while(StrLen(s1)< 3){s1=StrParam(s:" ",s:s1);}
	s=StrParam(s:s0,s:"C",s:s1);
	s0=StrParam(d:GetLevelInfo(LEVELINFO_PAR_TIME));
	while(StrLen(s0)< 4){s0=StrParam(s:" ",s:s0);}
	s1=StrParam(d:GetLevelInfo(LEVELINFO_SUCK_TIME));
	while(StrLen(s1)< 2){s1=StrParam(s:" ",s:s1);}
	s=StrParam(s:s,s:"L",s:s0,s:"Par",s:s1);
//PlayerOnTeam() is ACC's misnaming of IsMultiplayer()
//d:IsMultiplayer(),s:"IM ",
//d:GetGameModeState(),s:"GS "
	j=GetLevelInfo(LEVELINFO_FOUND_SECRETS);
	k=GetLevelInfo(LEVELINFO_TOTAL_SECRETS);
	s0=StrParam(f:((j<<16)/k)*100);
	m=StrLen(s0);for(l=0;l<m;l++){if(GetChar(s0,l)=='.'){break;}}
	if(l>=m){s0=StrParam(s:s0,s:".");}
	while(l<3){l++;s0=StrParam(s:" ",s:s0);}
	while(StrLen(s0)< 8){s0=StrParam(s:s0,s:"0");}
	s0=StrLeft(s0,8);
	s=StrParam(d:GameType(),s:"GT ",d:SinglePlayer(),s:"S ",d:PlayerOnTeam(),s:"IM ",d:IsOneFlagCTF(),s:"OF",
	 s:", Skill ",d:GameSkill(),s:":\"",n:PRINTNAME_SKILL,
	 s:"\", ",d:GetInvasionState(),
	 s:"IS ",d:GetInvasionWave(),
	 s:"W\n",s:s,
	 s:"Su \"",n:PRINTNAME_LEVEL,s:"\":\"",n:PRINTNAME_LEVELNAME,
	 s:"\"\n",
	 s:s0);
	s0=StrParam(d:j);
	while(StrLen(s0)< 6){s0=StrParam(s:" ",s:s0);}
	s1=StrParam(d:k);
	while(StrLen(s1)< 5){s1=StrParam(s:" ",s:s1);}
	s=StrParam(s:s,s:"% Secrets ",s:s0,s:"/",s:s1);

	j=GetLevelInfo(LEVELINFO_FOUND_ITEMS);
	k=GetLevelInfo(LEVELINFO_TOTAL_ITEMS);
	s0=StrParam(f:((j<<16)/k)*100);
	m=StrLen(s0);for(l=0;l<m;l++){if(GetChar(s0,l)=='.'){break;}}
	if(l>=m){s0=StrParam(s:s0,s:".");}
	while(l<3){l++;s0=StrParam(s:" ",s:s0);}
	while(StrLen(s0)< 8){s0=StrParam(s:s0,s:"0");}
	s0=StrLeft(s0,8);
	s=StrParam(s:s,s:"\n",s:s0);
	s0=StrParam(d:j);
	while(StrLen(s0)< 6){s0=StrParam(s:" ",s:s0);}
	s1=StrParam(d:k);
	while(StrLen(s1)< 5){s1=StrParam(s:" ",s:s1);}
	s=StrParam(s:s,s:"% Items   ",s:s0,s:"/",s:s1);

	j=GetLevelInfo(LEVELINFO_KILLED_MONSTERS);
	k=GetLevelInfo(LEVELINFO_TOTAL_MONSTERS);
//	s0=StrParam(f:FixedDiv((j<<16),(k<<16)/100));
	s0=StrParam(f:((j<<16)/k)*100);
	m=StrLen(s0);for(l=0;l<m;l++){if(GetChar(s0,l)=='.'){break;}}
	if(l>=m){s0=StrParam(s:s0,s:".");}
/*
	s0=StrParam(f:(j<<16)/k);
	m=StrLen(s0);if(GetChar(s0,0)=='0'){m--;s0=StrRight(s0,m);}
	for(l=0;l<m;l++){if(GetChar(s0,l)=='.'){break;}}
	if(l>=m){m++;s0=StrParam(s:s0,s:".");}
	while(m<l+3){m++;s0=StrParam(s:s0,s:"0");}
	s0=StrParam(s:StrLeft(s0,l),s:"",s:StrMid(s0,l+1,2),s:".",s:StrRight(s0,m-(l+3)));
	l+=2;m=StrLen(s0);if((GetChar(s0,0)=='0')&&GetChar(s0,1)=='0'){m--;l--;s0=StrRight(s0,m);}
	if(GetChar(s0,0)=='.'){l++;s0=StrParam(s:"0",s:s0);}
*/
	while(l<3){l++;s0=StrParam(s:" ",s:s0);}
	while(StrLen(s0)< 8){s0=StrParam(s:s0,s:"0");}
	s0=StrLeft(s0,8);
	s=StrParam(s:s,s:"\n",s:s0);
	s0=StrParam(d:j);
	while(StrLen(s0)< 6){s0=StrParam(s:" ",s:s0);}
	s1=StrParam(d:k);
	while(StrLen(s1)< 5){s1=StrParam(s:" ",s:s1);}
	s=StrParam(s:s,s:"% Monsters",s:s0,s:"/",s:s1);
	if(!Random(0,7))
	{
		s=StrParam(s:s,s:"\nThere is a spider behind you.");
		if(!Random(0,7)){s=StrParam(s:s,s:" It seeks your life.");}
	}
	j=GetCVar("KFHJ_WoC_MinLevel");
	if(KFHJVM_MinLevel<j){KFHJVM_MinLevel=j;}
	s=StrParam(s:s,s:"\nMinimum level is ",d:KFHJVM_MinLevel,s:".");
	k=Timer();
	if(KFHJVM_VoteTime>=k)	{s=StrParam(s:s,s:"\nA vote cannot be called for ",f:((KFHJVM_VoteTime-k)<<16)/35,s:" seconds.");}
	else			{s=StrParam(s:s,s:"\nA vote may be called.");}
	i=PlayerNumber();
 if(	(i>=0				) &&
	(i< KFHJ_MAXPLAYERS		) )
 {
	j=KFHJVM_Marks[i*KFHJVM_MarksSize];
	k=Timer();
	if(j>=k)		{s=StrParam(s:s,s:"\nYou cannot teleport     for ",f:((j-k)<<16)/35,s:" seconds.");}
	else			{s=StrParam(s:s,s:"\nYou    may teleport.");}
	s=StrParam(s:s,s:"\nYour mark is  @ ",
		f:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser  ],s:"X ",
		f:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+1],s:"Y ",
		f:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+2],s:"Z ",
		f:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+3],s:" ",
		f:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+4],s:" ",
		f:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+5],s:".");
	s=StrParam(s:s,s:"\nYou last died @ ",
		f:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath  ],s:"X ",
		f:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath+1],s:"Y ",
		f:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath+2],s:"Z ",
		f:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath+3],s:" ",
		f:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath+4],s:" ",
		f:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath+5],s:".");
 }
	i=GetActorProperty(0,APROP_Health);
	if(i>1000000)	{i=(i*8)/(GetActorProperty(0,APROP_SpawnHealth)  );}
	else		{i=(i  )/(GetActorProperty(0,APROP_SpawnHealth)/8);}
	if(!Random(0,7+i)){s=StrParam(s:s,s:"\nDeath is closer than you think.");}
//	 f:GetActorRoll		(0),s:"\n ",
	s=StrParam(s:s,s:"\nYou are       @ ",
	 f:GetActorX		(0),s:"X ",
	 f:GetActorY		(0),s:"Y ",
	 f:GetActorX		(0),s:"Z ",
	 f:GetActorAngle	(0),s:" ",
	 f:GetActorPitch	(0),s:" ",
	 f:0			   ,s:"\n Vel:",
	 f:GetActorVelX		(0),s:"X ",
	 f:GetActorVelY		(0),s:"Y ",
	 f:GetActorVelZ		(0),s:"Z ",
	 f:GetActorFloorZ(0),s:"F ",f:GetActorCeilingZ(0),s:"C ",
	 d:GetActorLightLevel(0),s:"L ",d:GetAirSupply(PlayerNumber()),
	 s:"A Class:\"",s:GetActorClass(0),s:"\"");
	KFHJFun_LogClient(s);
	terminate;
     default:
	break;
    }
    break;
    case 257:
    case 258:
    case 259:
	if(KFHJVM_VoteTime>=Timer())
	{
		KFHJFun_LogPrintClient(StrParam(s:"A vote cannot be called for ",d:(KFHJVM_VoteTime-Timer())/35,s:" seconds."));
		terminate;
	}
	KFHJVM_VoteValue=arg3;
	t="";
	switch(arg2)
	{
	 case 257:
		if(	(KFHJVM_VoteValue<1	) ||
			(KFHJVM_VoteValue>30	) )
		{
			KFHJFun_LogPrintClient(StrParam(s:"Out of range! (1-30)"));
			terminate;
		}
		s=" has voted to ban votes for ";t=" minute(s)";
		break;
	 case 258:
		if(	(KFHJVM_VoteValue<1	) ||
			(KFHJVM_VoteValue>999	) )
		{
			KFHJFun_LogPrintClient(StrParam(s:"Out of range! (1-999)"));
			terminate;
		}
		s=" has voted to set \cdminimum level ";
		break;
	 case 259:
		if(	(KFHJVM_VoteValue<0		) ||
			(KFHJVM_VoteValue>999999999	) )
		{
			KFHJFun_LogPrintClient(StrParam(s:"Out of range! (0-999999999)"));
			terminate;
		}
		s=" has voted to apply \cfminimum gold ";
		break;
	 default:KFHJFun_SomethingBroke("");terminate;
	}

	for(i=(KFHJ_MAXPLAYERS-1);i>=0;i--){KFHJVM_Votes[i]=0;}
	KFHJVM_VoteTime	=Timer()+(60*35);
	if(KFHJVM_VoteTime<=Timer()){KFHJFun_SomethingBroke("");terminate;}

	if(ConsolePlayerNumber()<0){Log(n:0,s:s,d:KFHJVM_VoteValue,s:t,s:".");}
	KFHJFun_LogPrintBoldClient(StrParam(n:0,s:"\ck",s:s,d:KFHJVM_VoteValue,s:t,
	 s:".\n\ckIn the console, use\cd \"puke 888 1\" to vote Yes,\cg \"puke 888\" to vote No,\cf or \"puke 888 2\" to Abstain."));

	while(KFHJVM_VoteTime>Timer())
	{
		if(PlayerNumber()<0){KFHJVM_Votes[0]=-9;break;}
		j=0;k=0;l=0;
		for(i=(KFHJ_MAXPLAYERS-1);i>=0;i--)
		{
			if((!PlayerInGame(i))&&(!PlayerIsSpectator(i))){continue;}
			if(PlayerIsSpectator(i))	{m=1;}
			else				{m=2;}
			if	((KFHJVM_Votes[i])>0)	{j+=m;}
			else if	((KFHJVM_Votes[i])<0)	{k+=m;}
			else				{l+=m;}
		}

		if(	(	(j>(k+l)	) ||
				(k>(j+l)	)
			) &&
			((KFHJVM_VoteTime-Timer())<(50*35)	) )
		{break;}

		HudMessageBold(n:0,s:s,d:KFHJVM_VoteValue,s:t,s:"\n\cd",
		 d:j,s:"Yes\cg ",d:k,s:"No\cf ",d:l,s:"Abstain\ck ",d:(KFHJVM_VoteTime-Timer())/35,s:"s remain";
		 HUDMSG_PLAIN,KFHJHUDM_VoteID,CR_YELLOW,KFHJHUDM_VoteX,KFHJHUDM_VoteY,0);

		Delay(const:1);
	}

	Log(s:" # Va Votes: (",d:j,s:"Y ",d:k,s:"N ",d:l,s:"A ",d:(KFHJVM_VoteTime-Timer())/35,s:"s)");

	KFHJVM_VoteTime	=Timer()+(10*35);
	if(PlayerCount()<2){KFHJVM_VoteTime=Timer();}
	if(KFHJVM_Votes[0]<-8)
	{
		Log(s:"Vote Aborted!");
		HudMessageBold(s:"Vote Aborted!";HUDMSG_PLAIN,KFHJHUDM_VoteID,CR_YELLOW,KFHJHUDM_VoteX,KFHJHUDM_VoteY,KFHJHUDM_VoteTime);
		terminate;
	}

	for(i=0;i<KFHJ_MAXPLAYERS;i++)
	{
		if((!PlayerInGame(i))&&(!PlayerIsSpectator(i))){continue;}
		if((KFHJVM_Votes[i])==0)	{m=0;}
		else
		{
		 if(PlayerIsSpectator(i))	{m=1;}
		 else				{m=2;}
		 if	((KFHJVM_Votes[i])<0)	{m=-m;}
		}
		s0=StrParam(d:i);
		while(StrLen(s0)< 2){s0=StrParam(s:" ",s:s0);}
		s1=StrParam(d:m);
		while(StrLen(s1)< 3){s1=StrParam(s:" ",s:s1);}
		Log(s:s0,s:s1,s:" \"",n:i+1,s:"\"");
	}

	KFHJVM_Votes[0]=-9;
	if(j<=k)
	{
		if(	((j*100) < ((l*25)+(k*50))	) &&
			(PlayerCount()>3		) )
		{ //Stahp voet
			s="Vote Failed \cgwith Prejudice\cr!";
			KFHJVM_VoteTime	=Timer()+(5*60*35);
		}
		else
		{
			s="Vote Failed!";
			if	(!Random(0,7)){s="Boat Bailed!";}
			else if	(!Random(0,7)){s="Fission Mailed!";}
		}
		if(ConsolePlayerNumber()<0){Log(s:s);}
		HudMessageBold(s:s;HUDMSG_PLAIN,KFHJHUDM_VoteID,CR_DARKRED,KFHJHUDM_VoteX,KFHJHUDM_VoteY,KFHJHUDM_VoteTime);
		terminate;
	}
	s="Vote Passed!";
	if(!Random(0,7)){s="Moat Pest!";}
	if(ConsolePlayerNumber()<0){Log(s:s);}
	HudMessageBold(s:s;HUDMSG_PLAIN,KFHJHUDM_VoteID,CR_GREEN,KFHJHUDM_VoteX,KFHJHUDM_VoteY,KFHJHUDM_VoteTime);

	switch(arg2)
	{
	 case 257:
		KFHJVM_VoteTime	=Timer()+(KFHJVM_VoteValue*60*35);
		break;
	 case 258:
		KFHJVM_MinLevel=KFHJVM_VoteValue;
		break;
	 case 259:
		for(i=(KFHJ_MAXPLAYERS-1);i>=0;i--)
		{
			if(!PlayerInGame(i)	){continue;}
			j=CheckActorInventory(KFHJ_PLAYERTID+i,"Money");
			if(j<KFHJVM_VoteValue)GiveActorInventory(KFHJ_PLAYERTID+i,"Money",KFHJVM_VoteValue-j);
		}
		break;
	 default:KFHJFun_SomethingBroke("");terminate;
	}
	terminate;
/*
    case 999:
//	ACS_ExecuteAlways(KFHJScr_MultiFunc0,0,256,999,"");
	for(i=(KFHJ_MAXPLAYERS-1);i>=0;i--)
	{
		if((!PlayerInGame(i))&&(!PlayerIsSpectator(i))){continue;}
		SetActivator(KFHJ_PLAYERTID+i,AAPTR_DEFAULT);
		Print(s:arg3);
		PrintBold(s:arg3);
	}
	if(ConsolePlayerNumber()>=0){break;}
	SetActivator(0,AAPTR_NULL);
	Print(s:arg3);
	break;
*/
    default:
	break;
   }
   break;
   case 999:
   switch(arg2)
   {
    case  999:
    switch(arg3)
    {
     case  999:
	i=PlayerNumber();
	if(	(i<0				) ||
		(i>=KFHJ_MAXPLAYERS		) )
	{KFHJFun_SomethingBroke("");terminate;}
	KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser  ]=GetActorX	(0);
	KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+1]=GetActorY	(0);
	KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+2]=GetActorZ	(0);
	KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+3]=GetActorAngle	(0);
	KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+4]=GetActorPitch	(0);
	KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+5]=0;
//	KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+5]=GetActorRoll	(0);
	KFHJFun_LogPrintClient(StrParam(s:"You have marked ",
		d:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser  ]>>16,s:"X ",
		d:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+1]>>16,s:"Y ",
		d:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+2]>>16,s:"Z ",
		f:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+3],s:" ",
		f:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+4],s:" ",
		f:KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+5],s:"!"));
	break;
     case 1000:
	i=PlayerNumber();
	if(	(i<0				) ||
		(i>=KFHJ_MAXPLAYERS		) )
	{KFHJFun_SomethingBroke("");terminate;}
	j=KFHJVM_Marks[i*KFHJVM_MarksSize];
	if(j>=Timer())
	{
		KFHJFun_LogPrintClient(StrParam(s:"You must wait ",d:(j-Timer())/35,s:" seconds."));
		terminate;
	}
	j=SetActorPosition(0,
		KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser  ],
		KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+1],
		KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+2],True);
//GetSectorFloorZ(int tag,int x,int y);
/*	Warp(KFHJ_PLAYERTID+i,
		KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser  ],
		KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+1],
		KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+2],
		KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+3],
		WARPF_ABSOLUTEPOSITION|WARPF_ABSOLUTEANGLE|);
*/
	if(j<1)
	{
		KFHJFun_LogPrintClient(StrParam(s:"Failed!"));
		terminate;
	}
	KFHJVM_Marks[i*KFHJVM_MarksSize]=Timer()+(60*35);
	SetActorAngle	(0,KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+3]);
	SetActorPitch	(0,KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+4]);
//	SetActorRoll	(0,KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsUser+5]);
	break;
     default:
	break;
    }
    break;
    case 1000:
	if(GetCVar("SV_UnblockPlayers")<1)
	{
		KFHJFun_LogPrintClient(StrParam(s:"SV_UnblockPlayers MUST be True to teleport directly onto other players."));
		terminate;
	}
	i=PlayerNumber();
	if(	(i<0				) ||
		(i>=KFHJ_MAXPLAYERS		) )
	{KFHJFun_SomethingBroke("");terminate;}
	if(	(arg3<0							) ||
		(arg3>=KFHJ_MAXPLAYERS					) ||
		(!PlayerInGame(arg3)					) ||
		(GetActorProperty(KFHJ_PLAYERTID+arg3,APROP_Health)<1	) )
	{
		KFHJFun_LogPrintClient(StrParam(s:"Invalid target!"));
		terminate;
	}
	j=KFHJVM_Marks[i*KFHJVM_MarksSize];
	if(j>=Timer())
	{
		KFHJFun_LogPrintClient(StrParam(s:"You must wait ",d:(j-Timer())/35,s:" seconds."));
		terminate;
	}
	j=SetActorPosition(0,
		GetActorX	(KFHJ_PLAYERTID+arg3),
		GetActorY	(KFHJ_PLAYERTID+arg3),
		GetActorZ	(KFHJ_PLAYERTID+arg3),True);
	if(j<1)
	{
		KFHJFun_LogPrintClient(StrParam(s:"Failed!"));
		terminate;
	}
	if(PlayerNumber()==arg3)
	{
		KFHJFun_LogPrintClient(StrParam(s:"Well, that was productive, wasn't it?"));
	}
	KFHJVM_Marks[i*KFHJVM_MarksSize]=Timer()+(60*35);
	SetActorAngle	(0,GetActorAngle	(KFHJ_PLAYERTID+arg3));
	SetActorPitch	(0,GetActorPitch	(KFHJ_PLAYERTID+arg3));
//	SetActorRoll	(0,GetActorRoll		(KFHJ_PLAYERTID+arg3));
	break;
    case 1001:
	i=PlayerNumber();
	if(	(i<0				) ||
		(i>=KFHJ_MAXPLAYERS		) )
	{KFHJFun_SomethingBroke("");terminate;}
	if(	(arg3<0							) ||
		(arg3>=KFHJ_MAXPLAYERS					) )
	{
		KFHJFun_LogPrintClient(StrParam(s:"Invalid target!"));
		terminate;
	}
	j=KFHJVM_Marks[i*KFHJVM_MarksSize];
	if(j>=Timer())
	{
		KFHJFun_LogPrintClient(StrParam(s:"You must wait ",d:(j-Timer())/35,s:" seconds."));
		terminate;
	}
	j=SetActorPosition(0,
		KFHJVM_Marks[(arg3*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath  ],
		KFHJVM_Marks[(arg3*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath+1],
		KFHJVM_Marks[(arg3*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath+2],True);
	if(j<1)
	{
		KFHJFun_LogPrintClient(StrParam(s:"Failed!"));
		terminate;
	}
	KFHJVM_Marks[i*KFHJVM_MarksSize]=Timer()+(60*35);
	SetActorAngle	(0,KFHJVM_Marks[(arg3*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath+3]);
	SetActorPitch	(0,KFHJVM_Marks[(arg3*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath+4]);
//	SetActorRoll	(0,KFHJVM_Marks[(arg3*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath+5]);
	break;
    default:
	break;
   }
   break;
   default:
	break;
  }
}

script KFHJScr_MultiFuncNet0(int arg1,int arg2,int arg3,int arg4)	Net Clientside
{
	KFHJFun_RequestScriptPuke(-KFHJScr_MultiFunc0,arg1,arg2,arg3,arg4);
}

#define	KFHJConst_CVarZero	-2147483646

script KFHJScr_Open	Open
{
	str	s;
	int	i;

//Overrides to get around the glaring bug with CVARINFO in Zandronum 2.0-2.1.2
 i=GetCVar("KFHJ_WoC_ExpDeath"			);if(i!=0){if(i==KFHJConst_CVarZero){i=0;}
ConsoleCommand(StrParam(s:"set ",s:						"WoC_ExpDeath"
,s:" ",d:i));Log(s:"Override:WoC_ExpDeath:"			,i:GetCVar(	"WoC_ExpDeath"				));}

 i=GetCVar("KFHJ_WoC_ExpDeathAmount"		);if(i!=0){if(i==KFHJConst_CVarZero){i=0;}
ConsoleCommand(StrParam(s:"set ",s:						"WoC_ExpDeathAmount"
,s:" ",d:i));Log(s:"Override:WoC_ExpDeathAmount:"		,i:GetCVar(	"WoC_ExpDeathAmount"			));}

 i=GetCVar("KFHJ_WoC_GoldDeath"			);if(i!=0){if(i==KFHJConst_CVarZero){i=0;}
ConsoleCommand(StrParam(s:"set ",s:						"WoC_GoldDeath"
,s:" ",d:i));Log(s:"Override:WoC_GoldDeath:"			,i:GetCVar(	"WoC_GoldDeath"				));}

 i=GetCVar("KFHJ_WoC_GoldDeathAmount"		);if(i!=0){if(i==KFHJConst_CVarZero){i=0;}
ConsoleCommand(StrParam(s:"set ",s:						"WoC_GoldDeathAmount"
,s:" ",d:i));Log(s:"Override:WoC_GoldDeathAmount:"		,i:GetCVar(	"WoC_GoldDeathAmount"			));}

 i=GetCVar("KFHJ_WoC_MaxSpeed"			);if(i!=0){if(i==KFHJConst_CVarZero){i=0;}
ConsoleCommand(StrParam(s:"set ",s:						"WoC_MaxSpeed"
,s:" ",d:i));Log(s:"Override:WoC_MaxSpeed:"			,i:GetCVar(	"WoC_MaxSpeed"				));}

 i=GetCVar("KFHJ_WoC_MaxJump"			);if(i!=0){if(i==KFHJConst_CVarZero){i=0;}
ConsoleCommand(StrParam(s:"set ",s:						"WoC_MaxJump"
,s:" ",d:i));Log(s:"Override:WoC_MaxJump:"			,i:GetCVar(	"WoC_MaxJump"				));}

 i=GetCVar("KFHJ_WoC_DisableKillingSpree"	);if(i!=0){if(i==KFHJConst_CVarZero){i=0;}
ConsoleCommand(StrParam(s:"set ",s:						"WoC_DisableKillingSpree"
,s:" ",d:i));Log(s:"Override:WoC_DisableKillingSpree:"		,i:GetCVar(	"WoC_DisableKillingSpree"		));}

 i=GetCVar("KFHJ_WoC_DisableAwards"		);if(i!=0){if(i==KFHJConst_CVarZero){i=0;}
ConsoleCommand(StrParam(s:"set ",s:						"WoC_DisableAwards"
,s:" ",d:i));Log(s:"Override:WoC_DisableAwards:"		,i:GetCVar(	"WoC_DisableAwards"			));}

 i=GetCVar("KFHJ_WoC_JumpModifier"		);if(i!=0){if(i==KFHJConst_CVarZero){i=0;}
ConsoleCommand(StrParam(s:"set ",s:						"WoC_JumpModifier"
,s:" ",d:i));Log(s:"Override:WoC_JumpModifier:"			,i:GetCVar(	"WoC_JumpModifier"			));}

 i=GetCVar("KFHJ_WoC_SetMonstersOnce"		);if(i!=0){if(i==KFHJConst_CVarZero){i=0;}
ConsoleCommand(StrParam(s:"set ",s:						"WoC_SetMonstersOnce"
,s:" ",d:i));Log(s:"Override:WoC_SetMonstersOnce:"		,i:GetCVar(	"WoC_SetMonstersOnce"			));}

 i=GetCVar("KFHJ_WoC_DamageIndicator"		);if(i!=0){if(i==KFHJConst_CVarZero){i=0;}
ConsoleCommand(StrParam(s:"set ",s:						"WoC_DamageIndicator"
,s:" ",d:i));Log(s:"Override:WoC_DamageIndicator:"		,i:GetCVar(	"WoC_DamageIndicator"			));}

 i=GetCVar("KFHJ_WoC_GoreLevel"			);if(i!=0){if(i==KFHJConst_CVarZero){i=0;}
ConsoleCommand(StrParam(s:"set ",s:						"WoC_GoreLevel"
,s:" ",d:i));Log(s:"Override:WoC_GoreLevel:"			,i:GetCVar(	"WoC_GoreLevel"				));}

//WoC_EnemyHealthBar/WoC_AutoPetLeash/WoC_RandomSkills/WoC_RandomStats
}

script KFHJScr_Enter	Enter
{
 int	i,j,k,l;

 if(	(CheckInventory("Level")>0		) &&
	(!GetCVar("KFHJ_WoC_DisableAutoLevel")	) )
 {
  if(PlayerCount()>1)
  {
	j=PlayerCount()/2;	//((PlayerCount()-1)+1)/2;
	for(i=0;i<KFHJ_MAXPLAYERS;i++)
	{
		if(!PlayerInGame(i)	){continue;}
		if(i==PlayerNumber()	){continue;}
		j+=CheckActorInventory(KFHJ_PLAYERTID+i,"Level");
	}
	j=((j*3)/4)/(PlayerCount()-1);
	if(KFHJVM_MinLevel<j){KFHJVM_MinLevel=j;}
  }
  else
  {
	j=((CheckActorInventory(0,"Level"))*3)/4;
	if(KFHJVM_MinLevel<j){KFHJVM_MinLevel=j;}
  }
	j=GetCVar("KFHJ_WoC_MinLevel");
	if(KFHJVM_MinLevel<j){KFHJVM_MinLevel=j;}

	k=(GameSkill()*25)+100;
	j=-(CheckInventory("XPTracker"));
	for(i=CheckInventory("Level");i<KFHJVM_MinLevel;i++)
	{
		j+=i*k;
	}

	if(j>0)
	{
	 if(	(CheckInventory("XPTracker"	)<=0	) &&
		(CheckInventory("Level"		)==1	) )
	 {
		GiveInventory("Money"		,KFHJVM_MinLevel*250);
	 }
	 else
	 {
		GiveInventory("Money"		,(KFHJVM_MinLevel-CheckInventory("Level"))*10);
	 }

		GiveInventory("XP"		,j);
		GiveInventory("XPTracker"	,j);
	}
 }

 Delay( (Random(0,35*30)) + (35*45) );
 restart;
}

script KFHJScr_Death	Death
{
	int	i=PlayerNumber();
	if(	(i<0				) ||
		(i>=KFHJ_MAXPLAYERS		) )
	{KFHJFun_SomethingBroke("");terminate;}
	KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath  ]=GetActorX	(0);
	KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath+1]=GetActorY	(0);
	KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath+2]=GetActorZ	(0);
	KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath+3]=GetActorAngle	(0);
	KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath+4]=GetActorPitch	(0);
	KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath+5]=0;
//	KFHJVM_Marks[(i*KFHJVM_MarksSize)+KFHJVM_MarksOfsDeath+5]=GetActorRoll	(0);
}