Initial commit.

This commit is contained in:
Jim Gray
2013-04-04 14:32:05 -07:00
parent ba5c81da32
commit d71d53e8ec
2180 changed files with 1393544 additions and 1 deletions

View File

@@ -0,0 +1,774 @@
// leave this as first line for PCH reasons...
//
#pragma warning( disable : 4786)
#pragma warning( disable : 4100)
#pragma warning( disable : 4663)
#include <windows.h>
#include "..\smartheap\smrtheap.h"
#include "../game/q_shared.h"
#include "..\qcommon\qcommon.h"
#include <stdio.h>
#include <map>
using namespace std;
#if MEM_DEBUG
#include "..\smartheap\heapagnt.h"
static const int maxStack=2048;
static int TotalMem;
static int TotalBlocks;
static int nStack;
static char StackNames[maxStack][256];
static int StackSize[maxStack];
static int StackCount[maxStack];
static int StackCache[48];
static int StackCacheAt=0;
static int CheckpointSize[3000];
static int CheckpointCount[3000];
//#define _FASTRPT_
#ifdef _FASTRPT_
class CMyStrComparator
{
public:
bool operator()(const char *s1, const char *s2) const { return(strcmp(s1, s2) < 0); }
};
hmap<const char *,int,CMyStrComparator> Lookup;
#endif
cvar_t *mem_leakfile;
cvar_t *mem_leakreport;
MEM_BOOL MEM_CALLBACK MyMemReporter2(MEM_ERROR_INFO *info)
{
static char buffer[10000];
if (!info->objectCreationInfo)
return 1;
info=info->objectCreationInfo;
int idx=info->checkpoint;
if (idx<0||idx>=1000)
{
idx=0;
}
CheckpointCount[idx]++;
CheckpointSize[idx]+=info->argSize;
dbgMemFormatCall(info,buffer,9999);
if (strstr(buffer,"ntdll"))
return 1;
if (strstr(buffer,"CLBCATQ"))
return 1;
int i;
TotalBlocks++;
if (TotalBlocks%1000==0)
{
char mess[1000];
sprintf(mess,"%d blocks processed\n",TotalBlocks);
OutputDebugString(mess);
}
for (i=strlen(buffer);i>0;i--)
{
if (buffer[i]=='\n')
break;
}
if (!i)
return 1;
buffer[i]=0;
char *buf=buffer;
while (*buf)
{
if (*buf=='\n')
{
buf++;
break;
}
buf++;
}
char *start=0;
char *altName=0;
while (*buf)
{
while (*buf==' ')
buf++;
start=buf;
while (*buf!=0&&*buf!='\n')
buf++;
if (*start)
{
if (*buf)
{
*buf=0;
buf++;
}
if (strlen(start)>255)
start[255]=0;
if (strstr(start,"std::"))
{
altName="std::??";
// start=0;
continue;
}
if (strstr(start,"Malloc"))
{
altName="Malloc??";
start=0;
continue;
}
if (strstr(start,"G_Alloc"))
{
altName="G_Alloc";
start=0;
continue;
}
if (strstr(start,"Hunk_Alloc"))
{
altName="Hunk_Alloc";
start=0;
continue;
}
if (strstr(start,"FS_LoadFile"))
{
altName="FS_LoadFile";
start=0;
continue;
}
if (strstr(start,"CopyString"))
{
altName="CopyString";
start=0;
continue;
}
break;
}
}
if (!start||!*start)
{
start=altName;
if (!start||!*start)
{
start="UNKNOWN";
}
}
#ifdef _FASTRPT_
hmap<const char *,int,CMyStrComparator>::iterator f=Lookup.find(start);
if(f==Lookup.end())
{
strcpy(StackNames[nStack++],start);
Lookup[(const char *)&StackNames[nStack-1]]=nStack-1;
StackSize[nStack-1]=info->argSize;
StackCount[nStack-1]=1;
}
else
{
StackSize[(*f).second]+=info->argSize;
StackCount[(*f).second]++;
}
#else
for (i=0;i<48;i++)
{
if (StackCache[i]<0||StackCache[i]>=nStack)
continue;
if (!strcmpi(start,StackNames[StackCache[i]]))
break;
}
if (i<48)
{
StackSize[StackCache[i]]+=info->argSize;
StackCount[StackCache[i]]++;
}
else
{
for (i=0;i<nStack;i++)
{
if (!strcmpi(start,StackNames[i]))
break;
}
if (i<nStack)
{
StackSize[i]+=info->argSize;
StackCount[i]++;
StackCache[StackCacheAt]=i;
StackCacheAt++;
if (StackCacheAt>=48)
StackCacheAt=0;
}
else if (i<maxStack)
{
strcpy(StackNames[i],start);
StackSize[i]=info->argSize;
StackCount[i]=1;
nStack++;
}
else if (nStack<maxStack)
{
nStack++;
strcpy(StackNames[maxStack-1],"*****OTHER*****");
StackSize[maxStack-1]=info->argSize;
StackCount[maxStack-1]=1;
}
else
{
StackSize[maxStack-1]+=info->argSize;
StackCount[maxStack-1]++;
}
}
#endif
TotalMem+=info->argSize;
return 1;
}
MEM_BOOL MEM_CALLBACK MyMemReporter3(MEM_ERROR_INFO *info)
{
static char buffer[10000];
if (!info->objectCreationInfo)
return 1;
info=info->objectCreationInfo;
int idx=info->checkpoint;
if (idx<0||idx>=3000)
{
idx=0;
}
CheckpointCount[idx]++;
CheckpointSize[idx]+=info->argSize;
dbgMemFormatCall(info,buffer,9999);
int i;
TotalBlocks++;
// if (TotalBlocks%1000==0)
// {
// char mess[1000];
// sprintf(mess,"%d blocks processed\n",TotalBlocks);
// OutputDebugString(mess);
// }
for (i=strlen(buffer);i>0;i--)
{
if (buffer[i]=='\n')
break;
}
if (!i)
return 1;
buffer[i]=0;
char *buf=buffer;
while (*buf)
{
if (*buf=='\n')
{
buf++;
break;
}
buf++;
}
char *start=0;
char *altName=0;
while (*buf)
{
while (*buf==' ')
buf++;
start=buf;
while (*buf!=0&&*buf!='\n')
buf++;
if (*start)
{
if (*buf)
{
*buf=0;
buf++;
}
if (strlen(start)>255)
start[255]=0;
if (strstr(start,"SV_AreaEntities"))
{
altName="SV_AreaEntities??";
start=0;
continue;
}
if (strstr(start,"SV_Trace"))
{
altName="SV_Trace??";
start=0;
continue;
}
if (strstr(start,"SV_PointContents"))
{
altName="SV_PointContents??";
start=0;
continue;
}
if (strstr(start,"CG_Trace"))
{
altName="??";
start=0;
continue;
}
if (strstr(start,"CG_PointContents"))
{
altName="??";
start=0;
continue;
}
/*
if (strstr(start,""))
{
altName="??";
start=0;
continue;
}
if (strstr(start,""))
{
altName="??";
start=0;
continue;
}
*/
break;
}
}
if (!start||!*start)
{
start=altName;
if (!start||!*start)
{
start="UNKNOWN";
}
}
#ifdef _FASTRPT_
hmap<const char *,int,CMyStrComparator>::iterator f=Lookup.find(start);
if(f==Lookup.end())
{
strcpy(StackNames[nStack++],start);
Lookup[(const char *)&StackNames[nStack-1]]=nStack-1;
StackSize[nStack-1]=info->argSize;
StackCount[nStack-1]=1;
}
else
{
StackSize[(*f).second]+=info->argSize;
StackCount[(*f).second]++;
}
#else
for (i=0;i<48;i++)
{
if (StackCache[i]<0||StackCache[i]>=nStack)
continue;
if (!strcmpi(start,StackNames[StackCache[i]]))
break;
}
if (i<48)
{
StackSize[StackCache[i]]+=info->argSize;
StackCount[StackCache[i]]++;
}
else
{
for (i=0;i<nStack;i++)
{
if (!strcmpi(start,StackNames[i]))
break;
}
if (i<nStack)
{
StackSize[i]+=info->argSize;
StackCount[i]++;
StackCache[StackCacheAt]=i;
StackCacheAt++;
if (StackCacheAt>=48)
StackCacheAt=0;
}
else if (i<maxStack)
{
strcpy(StackNames[i],start);
StackSize[i]=info->argSize;
StackCount[i]=1;
nStack++;
}
else if (nStack<maxStack)
{
nStack++;
strcpy(StackNames[maxStack-1],"*****OTHER*****");
StackSize[maxStack-1]=info->argSize;
StackCount[maxStack-1]=1;
}
else
{
StackSize[maxStack-1]+=info->argSize;
StackCount[maxStack-1]++;
}
}
#endif
TotalMem+=info->argSize;
return 1;
}
void SH_Checking_f(void);
#endif
class Leakage
{
MEM_POOL MyPool;
public:
Leakage()
{
MyPool = MemInitDefaultPool();
// MemPoolSetSmallBlockSize(MyPool, 16);
MemPoolSetSmallBlockAllocator(MyPool,MEM_SMALL_BLOCK_SH3);
#if MEM_DEBUG
dbgMemSetGuardSize(2);
EnableChecking(100000);
#endif
}
void LeakReport(void)
{
#if MEM_DEBUG
// This just makes sure we have map nodes available without allocation
// during the heap walk (which could be bad).
int i;
#ifdef _FASTRPT_
hlist<int> makeSureWeHaveNodes;
for(i=0;i<5000;i++)
{
makeSureWeHaveNodes.push_back(0);
}
makeSureWeHaveNodes.clear();
Lookup.clear();
#endif
char mess[1000];
int blocks=dbgMemTotalCount();
int mem=dbgMemTotalSize()/1024;
sprintf(mess,"Final Memory Summary %d blocks %d K\n",blocks,mem);
OutputDebugString(mess);
for (i=0;i<3000;i++)
{
CheckpointSize[i]=0;
CheckpointCount[i]=0;
}
TotalMem=0;
TotalBlocks=0;
nStack=0;
MemSetErrorHandler(MyMemReporter2);
dbgMemReportLeakage(NULL,1,1000);
MemSetErrorHandler(MemDefaultErrorHandler);
multimap<int,pair<int,char *> > sortit;
multimap<int,pair<int,char *> >::iterator j;
if (TotalBlocks)
{
// Sort by size.
Sleep(100);
OutputDebugString("**************************************\n");
OutputDebugString("**********Memory Leak Report**********\n");
OutputDebugString("*************** By Size **************\n");
OutputDebugString("**************************************\n");
sprintf(mess,"Actual leakage %d blocks %d K\n",TotalBlocks,TotalMem/1024);
OutputDebugString(mess);
sortit.clear();
for (i=0;i<nStack;i++)
sortit.insert(pair<int,pair<int,char *> >(-StackSize[i],pair<int,char *>(StackCount[i],StackNames[i])));
Sleep(5);
for (j=sortit.begin();j!=sortit.end();j++)
{
sprintf(mess,"%5d KB %6d cnt %s\n",-(*j).first/1024,(*j).second.first,(*j).second.second);
// if (!(-(*j).first/1024))
// break;
Sleep(5);
OutputDebugString(mess);
}
// Sort by count.
Sleep(100);
OutputDebugString("**************************************\n");
OutputDebugString("**********Memory Leak Report**********\n");
OutputDebugString("************** By Count **************\n");
OutputDebugString("**************************************\n");
sprintf(mess,"Actual leakage %d blocks %d K\n",TotalBlocks,TotalMem/1024);
OutputDebugString(mess);
sortit.clear();
for (i=0;i<nStack;i++)
sortit.insert(pair<int,pair<int,char *> >(-StackCount[i],pair<int,char *>(StackSize[i],StackNames[i])));
Sleep(5);
for (j=sortit.begin();j!=sortit.end();j++)
{
sprintf(mess,"%5d KB %6d cnt %s\n",(*j).second.first/1024,-(*j).first,(*j).second.second);
// if (!(-(*j).first/1024))
// break;
Sleep(5);
OutputDebugString(mess);
}
}
else
{
OutputDebugString("No Memory Leaks\n");
}
TotalMem=0;
TotalBlocks=0;
nStack=0;
MemSetErrorHandler(MyMemReporter3);
dbgMemReportLeakage(NULL,2001,2001);
MemSetErrorHandler(MemDefaultErrorHandler);
if (TotalBlocks)
{
// Sort by count.
Sleep(100);
OutputDebugString("**************************************\n");
OutputDebugString("SV_PointContents ");
sprintf(mess,"%d Calls.\n",TotalBlocks);
OutputDebugString(mess);
OutputDebugString("**************************************\n");
sortit.clear();
for (i=0;i<nStack;i++)
sortit.insert(pair<int,pair<int,char *> >(-StackCount[i],pair<int,char *>(StackSize[i],StackNames[i])));
Sleep(5);
for (j=sortit.begin();j!=sortit.end();j++)
{
sprintf(mess,"%7d cnt %s\n",-(*j).first,(*j).second.second);
Sleep(5);
OutputDebugString(mess);
}
}
TotalMem=0;
TotalBlocks=0;
nStack=0;
MemSetErrorHandler(MyMemReporter3);
dbgMemReportLeakage(NULL,2002,2002);
MemSetErrorHandler(MemDefaultErrorHandler);
if (TotalBlocks)
{
// Sort by count.
Sleep(100);
OutputDebugString("**************************************\n");
OutputDebugString("SV_Trace ");
sprintf(mess,"%d Calls.\n",TotalBlocks);
OutputDebugString(mess);
OutputDebugString("**************************************\n");
sortit.clear();
for (i=0;i<nStack;i++)
sortit.insert(pair<int,pair<int,char *> >(-StackCount[i],pair<int,char *>(StackSize[i],StackNames[i])));
Sleep(5);
for (j=sortit.begin();j!=sortit.end();j++)
{
sprintf(mess,"%7d cnt %s\n",-(*j).first,(*j).second.second);
Sleep(5);
OutputDebugString(mess);
}
}
TotalMem=0;
TotalBlocks=0;
nStack=0;
MemSetErrorHandler(MyMemReporter3);
dbgMemReportLeakage(NULL,2003,2003);
MemSetErrorHandler(MemDefaultErrorHandler);
if (TotalBlocks)
{
// Sort by count.
Sleep(100);
OutputDebugString("**************************************\n");
OutputDebugString("SV_AreaEntities ");
sprintf(mess,"%d Calls.\n",TotalBlocks);
OutputDebugString(mess);
OutputDebugString("**************************************\n");
sortit.clear();
for (i=0;i<nStack;i++)
sortit.insert(pair<int,pair<int,char *> >(-StackCount[i],pair<int,char *>(StackSize[i],StackNames[i])));
Sleep(5);
for (j=sortit.begin();j!=sortit.end();j++)
{
sprintf(mess,"%7d cnt %s\n",-(*j).first,(*j).second.second);
Sleep(5);
OutputDebugString(mess);
}
}
TotalMem=0;
TotalBlocks=0;
nStack=0;
MemSetErrorHandler(MyMemReporter3);
dbgMemReportLeakage(NULL,2004,2004);
MemSetErrorHandler(MemDefaultErrorHandler);
if (TotalBlocks)
{
// Sort by count.
Sleep(100);
OutputDebugString("**************************************\n");
OutputDebugString("CG_Trace ");
sprintf(mess,"%d Calls.\n",TotalBlocks);
OutputDebugString(mess);
OutputDebugString("**************************************\n");
sortit.clear();
for (i=0;i<nStack;i++)
sortit.insert(pair<int,pair<int,char *> >(-StackCount[i],pair<int,char *>(StackSize[i],StackNames[i])));
Sleep(5);
for (j=sortit.begin();j!=sortit.end();j++)
{
sprintf(mess,"%7d cnt %s\n",-(*j).first,(*j).second.second);
Sleep(5);
OutputDebugString(mess);
}
}
TotalMem=0;
TotalBlocks=0;
nStack=0;
MemSetErrorHandler(MyMemReporter3);
dbgMemReportLeakage(NULL,2005,2005);
MemSetErrorHandler(MemDefaultErrorHandler);
if (TotalBlocks)
{
// Sort by count.
Sleep(100);
OutputDebugString("**************************************\n");
OutputDebugString("CG_PointContents ");
sprintf(mess,"%d Calls.\n",TotalBlocks);
OutputDebugString(mess);
OutputDebugString("**************************************\n");
sortit.clear();
for (i=0;i<nStack;i++)
sortit.insert(pair<int,pair<int,char *> >(-StackCount[i],pair<int,char *>(StackSize[i],StackNames[i])));
Sleep(5);
for (j=sortit.begin();j!=sortit.end();j++)
{
sprintf(mess,"%7d cnt %s\n",-(*j).first,(*j).second.second);
Sleep(5);
OutputDebugString(mess);
}
}
#if 0 //sw doesn't have the tag stuff
// Sort by size.
Sleep(5);
OutputDebugString("***************************************\n");
OutputDebugString("By Tag, sort: size ********************\n");
OutputDebugString("size(K) count name \n");
OutputDebugString("-----------------------\n");
Sleep(5);
multimap<int,int> sorted;
for (i=0;i<1000;i++)
{
if (CheckpointCount[i])
{
sorted.insert(pair<int,int>(-CheckpointSize[i],i));
}
}
multimap<int,int>::iterator k;
for (k=sorted.begin();k!=sorted.end();k++)
{
sprintf(mess,"%8d %8d %s\n",CheckpointSize[(*k).second]/1024,CheckpointCount[(*k).second],(*k).second>=2?tagDefs[(*k).second-2]:"unknown");
Sleep(5);
OutputDebugString(mess);
}
// Sort by count.
Sleep(5);
OutputDebugString("By Tag, sort: count *******************\n");
OutputDebugString("size(K) count name \n");
OutputDebugString("-----------------------\n");
Sleep(5);
sorted.clear();
for (i=0;i<1000;i++)
{
if (CheckpointCount[i])
{
sorted.insert(pair<int,int>(-CheckpointCount[i],i));
}
}
for (k=sorted.begin();k!=sorted.end();k++)
{
sprintf(mess,"%8d %8d %s\n",CheckpointSize[(*k).second]/1024,CheckpointCount[(*k).second],(*k).second>=2?tagDefs[(*k).second-2]:"unknown");
Sleep(5);
OutputDebugString(mess);
}
#endif
#endif
}
~Leakage()
{
#if MEM_DEBUG
if (mem_leakfile && mem_leakfile->integer)
{
dbgMemSetDefaultErrorOutput(DBGMEM_OUTPUT_FILE,"leakage.out");
dbgMemReportLeakage(NULL,1,1);
dbgMemSetDefaultErrorOutput(DBGMEM_OUTPUT_PROMPT,NULL);
}
if (mem_leakreport && mem_leakreport->integer)
{
LeakReport();
}
#endif
}
#if MEM_DEBUG
void EnableChecking(int x)
{
if (x)
{
dbgMemSetSafetyLevel(MEM_SAFETY_DEBUG);
dbgMemPoolSetCheckFrequency(MyPool, x);
dbgMemSetCheckFrequency(x);
dbgMemDeferFreeing(TRUE);
dbgMemSetDeferQueueLen(50000);
}
else
{
dbgMemSetSafetyLevel(MEM_SAFETY_SOME);
dbgMemDeferFreeing(FALSE);
}
}
#endif
};
static Leakage TheLeakage;
#if MEM_DEBUG
void MEM_Checking_f(void)
{
if (Cmd_Argc() != 2)
{
Com_Printf ("mem_checking <frequency>\n");
return;
}
if (atol(Cmd_Argv(1)) > 0 && atol(Cmd_Argv(1)) < 100)
{
Com_Printf ("mem_checking frequency is too low ( < 100 )\n");
return;
}
TheLeakage.EnableChecking(atol(Cmd_Argv(1)));
}
void MEM_Report_f(void)
{
if (0)
{
dbgMemSetDefaultErrorOutput(DBGMEM_OUTPUT_FILE,"leakage.out");
dbgMemReportLeakage(NULL,1,1);
dbgMemSetDefaultErrorOutput(DBGMEM_OUTPUT_PROMPT,NULL);
}
TheLeakage.LeakReport();
}
/*
void myexit(void)
{
TheLeakage.LeakReport();
}
*/
void SH_Register(void)
{
Cmd_AddCommand ("mem_checking", MEM_Checking_f);
Cmd_AddCommand ("mem_report", MEM_Report_f);
mem_leakfile = Cvar_Get( "mem_leakfile", "0", 0 );
mem_leakreport = Cvar_Get( "mem_leakreport", "1", 0 );
// atexit(myexit);
}
#endif

BIN
code/ALut.lib Normal file

Binary file not shown.

BIN
code/EaxMan.dll Normal file

Binary file not shown.

BIN
code/IFC22.dll Normal file

Binary file not shown.

BIN
code/JediAcademy.ncb Normal file

Binary file not shown.

86
code/JediAcademy.sln Normal file
View File

@@ -0,0 +1,86 @@
Microsoft Visual Studio Solution File, Format Version 8.00
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "x_exe", "x_exe\x_exe.vcproj", "{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
ProjectSection(ProjectDependencies) = postProject
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA} = {E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB} = {68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "goblib", "goblib\goblib.vcproj", "{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "x_game", "x_game\x_game.vcproj", "{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfiguration) = preSolution
Debug = Debug
DemoDebug = DemoDebug
DemoFinal = DemoFinal
DemoRelease = DemoRelease
FinalBuild = FinalBuild
Release = Release
SHDebug = SHDebug
EndGlobalSection
GlobalSection(ProjectConfiguration) = postSolution
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.Debug.ActiveCfg = Debug|Xenon
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.Debug.Build.0 = Debug|Xenon
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.DemoDebug.ActiveCfg = DemoDebug|Xenon
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.DemoDebug.Build.0 = DemoDebug|Xenon
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.DemoFinal.ActiveCfg = DemoFinal|Xenon
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.DemoFinal.Build.0 = DemoFinal|Xenon
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.DemoRelease.ActiveCfg = DemoRelease|Xenon
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.DemoRelease.Build.0 = DemoRelease|Xenon
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.FinalBuild.ActiveCfg = FinalBuild|Xenon
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.FinalBuild.Build.0 = FinalBuild|Xenon
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.Release.ActiveCfg = Release|Xenon
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.Release.Build.0 = Release|Xenon
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.SHDebug.ActiveCfg = Debug|Xenon
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.SHDebug.Build.0 = Debug|Xenon
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.Debug.ActiveCfg = Debug|Xenon
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.Debug.Build.0 = Debug|Xenon
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.DemoDebug.ActiveCfg = DemoDebug|Xenon
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.DemoDebug.Build.0 = DemoDebug|Xenon
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.DemoFinal.ActiveCfg = DemoFinal|Xenon
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.DemoFinal.Build.0 = DemoFinal|Xenon
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.DemoRelease.ActiveCfg = DemoRelease|Xenon
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.DemoRelease.Build.0 = DemoRelease|Xenon
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.FinalBuild.ActiveCfg = FinalBuild|Xenon
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.FinalBuild.Build.0 = FinalBuild|Xenon
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.Release.ActiveCfg = Release|Xenon
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.Release.Build.0 = Release|Xenon
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.SHDebug.ActiveCfg = Debug|Xenon
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.SHDebug.Build.0 = Debug|Xenon
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.Debug.ActiveCfg = Debug|Xenon
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.Debug.Build.0 = Debug|Xenon
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.DemoDebug.ActiveCfg = DemoDebug|Xenon
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.DemoDebug.Build.0 = DemoDebug|Xenon
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.DemoFinal.ActiveCfg = DemoFinal|Xenon
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.DemoFinal.Build.0 = DemoFinal|Xenon
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.DemoRelease.ActiveCfg = DemoRelease|Xenon
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.DemoRelease.Build.0 = DemoRelease|Xenon
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.FinalBuild.ActiveCfg = FinalBuild|Xenon
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.FinalBuild.Build.0 = FinalBuild|Xenon
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.Release.ActiveCfg = Release|Xenon
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.Release.Build.0 = Release|Xenon
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.SHDebug.ActiveCfg = Debug|Xenon
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.SHDebug.Build.0 = Debug|Xenon
EndGlobalSection
GlobalSection(SolutionItems) = postSolution
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection
GlobalSection(ExtensibilityAddIns) = postSolution
EndGlobalSection
GlobalSection(DevPartner) = postSolution
EndGlobalSection
GlobalSection(DevPartner) = postSolution
EndGlobalSection
EndGlobal

76
code/JediAcademy.sln.old Normal file
View File

@@ -0,0 +1,76 @@
Microsoft Visual Studio Solution File, Format Version 7.00
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "x_exe", "x_exe\x_exe.vcproj", "{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "goblib", "goblib\goblib.vcproj", "{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "x_game", "x_game\x_game.vcproj", "{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}"
EndProject
Global
GlobalSection(SolutionConfiguration) = preSolution
ConfigName.0 = Debug
ConfigName.1 = DemoDebug
ConfigName.2 = DemoFinal
ConfigName.3 = DemoRelease
ConfigName.4 = FinalBuild
ConfigName.5 = Release
ConfigName.6 = SHDebug
EndGlobalSection
GlobalSection(ProjectDependencies) = postSolution
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.0 = {68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.1 = {E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}
EndGlobalSection
GlobalSection(ProjectConfiguration) = postSolution
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.Debug.ActiveCfg = Debug|Xbox
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.Debug.Build.0 = Debug|Xbox
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.DemoDebug.ActiveCfg = DemoDebug|Xbox
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.DemoDebug.Build.0 = DemoDebug|Xbox
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.DemoFinal.ActiveCfg = DemoFinal|Xbox
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.DemoFinal.Build.0 = DemoFinal|Xbox
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.DemoRelease.ActiveCfg = DemoRelease|Xbox
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.DemoRelease.Build.0 = DemoRelease|Xbox
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.FinalBuild.ActiveCfg = FinalBuild|Xbox
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.FinalBuild.Build.0 = FinalBuild|Xbox
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.Release.ActiveCfg = Release|Xbox
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.Release.Build.0 = Release|Xbox
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.SHDebug.ActiveCfg = Debug|Xbox
{FD1DA207-0D4A-47A8-A6AD-B81AE1BA28DA}.SHDebug.Build.0 = Debug|Xbox
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.Debug.ActiveCfg = Debug|Xbox
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.Debug.Build.0 = Debug|Xbox
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.DemoDebug.ActiveCfg = DemoDebug|Xbox
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.DemoDebug.Build.0 = DemoDebug|Xbox
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.DemoFinal.ActiveCfg = DemoFinal|Xbox
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.DemoFinal.Build.0 = DemoFinal|Xbox
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.DemoRelease.ActiveCfg = DemoRelease|Xbox
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.DemoRelease.Build.0 = DemoRelease|Xbox
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.FinalBuild.ActiveCfg = FinalBuild|Xbox
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.FinalBuild.Build.0 = FinalBuild|Xbox
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.Release.ActiveCfg = Release|Xbox
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.Release.Build.0 = Release|Xbox
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.SHDebug.ActiveCfg = Debug|Xbox
{68A067E3-BB9E-435B-9AA3-DD45DB2AA4DB}.SHDebug.Build.0 = Debug|Xbox
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.Debug.ActiveCfg = Debug|Xbox
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.Debug.Build.0 = Debug|Xbox
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.DemoDebug.ActiveCfg = DemoDebug|Xbox
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.DemoDebug.Build.0 = DemoDebug|Xbox
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.DemoFinal.ActiveCfg = DemoFinal|Xbox
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.DemoFinal.Build.0 = DemoFinal|Xbox
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.DemoRelease.ActiveCfg = DemoRelease|Xbox
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.DemoRelease.Build.0 = DemoRelease|Xbox
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.FinalBuild.ActiveCfg = FinalBuild|Xbox
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.FinalBuild.Build.0 = FinalBuild|Xbox
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.Release.ActiveCfg = Release|Xbox
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.Release.Build.0 = Release|Xbox
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.SHDebug.ActiveCfg = Debug|Xbox
{E7A12117-F3B0-4C68-9B44-66CEFCA3AEAA}.SHDebug.Build.0 = Debug|Xbox
EndGlobalSection
GlobalSection(SolutionItems) = postSolution
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection
GlobalSection(ExtensibilityAddIns) = postSolution
EndGlobalSection
GlobalSection(DevPartner) = postSolution
EndGlobalSection
GlobalSection(DevPartner) = postSolution
EndGlobalSection
EndGlobal

BIN
code/JediAcademy.suo Normal file

Binary file not shown.

BIN
code/OpenAL32.dll Normal file

Binary file not shown.

BIN
code/OpenAL32.lib Normal file

Binary file not shown.

480
code/RMG/RM_Area.cpp Normal file
View File

@@ -0,0 +1,480 @@
/************************************************************************************************
*
* Copyright (C) 2001-2002 Raven Software
*
* RM_Area.cpp
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "rm_headers.h"
#ifdef _WIN32
#pragma optimize("p", on)
#endif
/************************************************************************************************
* CRMArea::CRMArea
* constructor
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMArea::CRMArea (
float spacingRadius,
float paddingSize,
float confineRadius,
vec3_t confineOrigin,
vec3_t lookAtOrigin,
bool flatten,
int symmetric
)
{
mMoveCount = 0;
mAngle = 0;
mCollision = true;
mConfineRadius = confineRadius;
mPaddingSize = paddingSize;
mSpacingRadius = spacingRadius;
mFlatten = flatten;
mLookAt = true;
mLockOrigin = false;
mSymmetric = symmetric;
mRadius = spacingRadius;
VectorCopy ( confineOrigin, mConfineOrigin );
VectorCopy ( lookAtOrigin, mLookAtOrigin );
}
/************************************************************************************************
* CRMArea::LookAt
* Angle the area towards the given point
*
* inputs:
* lookat - the origin to look at
*
* return:
* the angle in radians that was calculated
*
************************************************************************************************/
float CRMArea::LookAt ( vec3_t lookat )
{
if (mLookAt)
{ // this area orients itself towards a point
vec3_t a;
VectorCopy ( lookat, mLookAtOrigin );
VectorSubtract ( lookat, mOrigin, a );
mAngle = atan2 ( a[1], a[0] );
}
return mAngle;
}
/************************************************************************************************
* CRMArea::Mirror
* Mirrors the area to the other side of the map. This includes mirroring the confine origin
* and lookat origin
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
void CRMArea::Mirror ( void )
{
mOrigin[0] = -mOrigin[0];
mOrigin[1] = -mOrigin[1];
mConfineOrigin[0] = -mConfineOrigin[0];
mConfineOrigin[1] = -mConfineOrigin[1];
mLookAtOrigin[0] = -mLookAtOrigin[0];
mLookAtOrigin[1] = -mLookAtOrigin[1];
}
/************************************************************************************************
* CRMAreaManager::CRMAreaManager
* constructor
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMAreaManager::CRMAreaManager ( const vec3_t mins, const vec3_t maxs)
{
VectorCopy ( mins, mMins );
VectorCopy ( maxs, mMaxs );
mWidth = mMaxs[0] - mMins[0];
mHeight = mMaxs[1] - mMins[1];
}
/************************************************************************************************
* CRMAreaManager::~CRMAreaManager
* Removes all managed areas
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMAreaManager::~CRMAreaManager ( )
{
int i;
for ( i = mAreas.size() - 1; i >=0; i -- )
{
delete mAreas[i];
}
mAreas.clear();
}
/************************************************************************************************
* CRMAreaManager::MoveArea
* Moves an area within the area manager thus shifting any other areas as needed
*
* inputs:
* area - area to be moved
* origin - new origin to attempt to move to
*
* return:
* none
*
************************************************************************************************/
void CRMAreaManager::MoveArea ( CRMArea* movedArea, vec3_t origin)
{
int index;
int size;
// Increment the addcount (this is for infinite protection)
movedArea->AddMoveCount ();
// Infinite recursion prevention
if ( movedArea->GetMoveCount() > 250 )
{
// assert ( 0 );
movedArea->EnableCollision ( false );
return;
}
// First set the area's origin, This may cause it to be in collision with
// another area but that will get fixed later
movedArea->SetOrigin ( origin );
// when symmetric we want to ensure that no instances end up on the "other" side of the imaginary diaganol that cuts the map in two
// mSymmetric tells us which side of the map is legal
if ( movedArea->GetSymmetric ( ) )
{
const vec3pair_t& bounds = TheRandomMissionManager->GetLandScape()->GetBounds();
vec3_t point;
vec3_t dir;
vec3_t tang;
bool push;
float len;
VectorSubtract( movedArea->GetOrigin(), bounds[0], point );
VectorSubtract( bounds[1], bounds[0], dir );
VectorNormalize(dir);
dir[2] = 0;
point[2] = 0;
VectorMA( bounds[0], DotProduct(point, dir), dir, tang );
VectorSubtract ( movedArea->GetOrigin(), tang, dir );
dir[2] = 0;
push = false;
len = VectorNormalize(dir);
if ( len < movedArea->GetRadius ( ) )
{
if ( movedArea->GetLockOrigin ( ) )
{
movedArea->EnableCollision ( false );
return;
}
VectorMA ( point, (movedArea->GetSpacingRadius() - len) + TheRandomMissionManager->GetLandScape()->irand(10,movedArea->GetSpacingRadius()), dir, point );
origin[0] = point[0] + bounds[0][0];
origin[1] = point[1] + bounds[0][1];
movedArea->SetOrigin ( origin );
}
switch ( movedArea->GetSymmetric ( ) )
{
case SYMMETRY_TOPLEFT:
if ( origin[1] > origin[0] )
{
movedArea->Mirror ( );
}
break;
case SYMMETRY_BOTTOMRIGHT:
if ( origin[1] < origin[0] )
{
movedArea->Mirror ( );
}
break;
default:
// unknown symmetry type
assert ( 0 );
break;
}
}
// Confine to area unless we are being pushed back by the same guy who pushed us last time (infinite loop)
if ( movedArea->GetConfineRadius() )
{
if ( movedArea->GetMoveCount() < 25 )
{
vec3_t cdiff;
float cdist;
VectorSubtract ( movedArea->GetOrigin(), movedArea->GetConfineOrigin(), cdiff );
cdiff[2] = 0;
cdist = VectorLength ( cdiff );
if ( cdist + movedArea->GetSpacingRadius() > movedArea->GetConfineRadius() )
{
cdist = movedArea->GetConfineRadius() - movedArea->GetSpacingRadius();
VectorNormalize ( cdiff );
VectorMA ( movedArea->GetConfineOrigin(), cdist, cdiff, movedArea->GetOrigin());
}
}
else
{
index = 0;
}
}
// See if it fell off the world in the x direction
if ( movedArea->GetOrigin()[0] + movedArea->GetSpacingRadius() > mMaxs[0] )
movedArea->GetOrigin()[0] = mMaxs[0] - movedArea->GetSpacingRadius() - (TheRandomMissionManager->GetLandScape()->irand(10,200));
else if ( movedArea->GetOrigin()[0] - movedArea->GetSpacingRadius() < mMins[0] )
movedArea->GetOrigin()[0] = mMins[0] + movedArea->GetSpacingRadius() + (TheRandomMissionManager->GetLandScape()->irand(10,200));
// See if it fell off the world in the y direction
if ( movedArea->GetOrigin()[1] + movedArea->GetSpacingRadius() > mMaxs[1] )
movedArea->GetOrigin()[1] = mMaxs[1] - movedArea->GetSpacingRadius() - (TheRandomMissionManager->GetLandScape()->irand(10,200));
else if ( movedArea->GetOrigin()[1] - movedArea->GetSpacingRadius() < mMins[1] )
movedArea->GetOrigin()[1] = mMins[1] + movedArea->GetSpacingRadius() + (TheRandomMissionManager->GetLandScape()->irand(10,200));
// Look at what we need to look at
movedArea->LookAt ( movedArea->GetLookAtOrigin() );
// Dont collide against things that have no collision
// if ( !movedArea->IsCollisionEnabled ( ) )
// {
// return;
// }
// See if its colliding
for(index = 0, size = mAreas.size(); index < size; index ++ )
{
CRMArea *area = mAreas[index];
vec3_t diff;
vec3_t newOrigin;
float dist;
float targetdist;
// Skip the one that was moved in the first place
if ( area == movedArea )
{
continue;
}
if ( area->GetLockOrigin ( ) && movedArea->GetLockOrigin( ) )
{
continue;
}
// Dont collide against things that have no collision
if ( !area->IsCollisionEnabled ( ) )
{
continue;
}
// Grab the distance between the two
// only want the horizontal distance -- dmv
//dist = Distance ( movedArea->GetOrigin ( ), area->GetOrigin ( ));
vec3_t maOrigin;
vec3_t aOrigin;
VectorCopy(movedArea->GetOrigin(), maOrigin);
VectorCopy(area->GetOrigin(), aOrigin);
maOrigin[2] = aOrigin[2] = 0;
dist = Distance ( maOrigin, aOrigin );
targetdist = movedArea->GetSpacingRadius() + area->GetSpacingRadius() + maximum(movedArea->GetPaddingSize(),area->GetPaddingSize());
if ( dist == 0 )
{
area->GetOrigin()[0] += (50 * (float)(TheRandomMissionManager->GetLandScape()->irand(0,99))/100.0f);
area->GetOrigin()[1] += (50 * (float)(TheRandomMissionManager->GetLandScape()->irand(0,99))/100.0f);
VectorCopy(area->GetOrigin(), aOrigin);
aOrigin[2] = 0;
dist = Distance ( maOrigin, aOrigin );
}
// Are they are enough apart?
if ( dist >= targetdist )
{
continue;
}
// Dont move a step if locked
if ( area->GetLockOrigin ( ) )
{
MoveArea ( area, area->GetOrigin ( ) );
continue;
}
// we got a collision, move the guy we hit
VectorSubtract ( area->GetOrigin(), movedArea->GetOrigin(), diff );
diff[2] = 0;
VectorNormalize ( diff );
// Push by the difference in the distance and no-collide radius
VectorMA ( area->GetOrigin(), targetdist - dist + 1 , diff, newOrigin );
// Move the area now
MoveArea ( area, newOrigin );
}
}
/************************************************************************************************
* CRMAreaManager::CreateArea
* Creates an area and adds it to the list of managed areas
*
* inputs:
* none
*
* return:
* a pointer to the newly added area class
*
************************************************************************************************/
CRMArea* CRMAreaManager::CreateArea (
vec3_t origin,
float spacingRadius,
int spacingLine,
float paddingSize,
float confineRadius,
vec3_t confineOrigin,
vec3_t lookAtOrigin,
bool flatten,
bool collide,
bool lockorigin,
int symmetric
)
{
CRMArea* area = new CRMArea ( spacingRadius, paddingSize, confineRadius, confineOrigin, lookAtOrigin, flatten, symmetric );
if ( lockorigin || spacingLine )
{
area->LockOrigin ( );
}
if (origin[0] != lookAtOrigin[0] || origin[1] != lookAtOrigin[1])
area->EnableLookAt(true);
// First add the area to the list
mAreas.push_back ( area );
area->EnableCollision(collide);
// Set the real radius which is used for center line detection
if ( spacingLine )
{
area->SetRadius ( spacingRadius + (spacingLine - 1) * spacingRadius );
}
// Now move the area around
MoveArea ( area, origin );
if ( (origin[0] != lookAtOrigin[0] || origin[1] != lookAtOrigin[1]) )
{
int i;
vec3_t linedir;
vec3_t dir;
vec3_t up = {0,0,1};
vec3_t zerodvec;
VectorClear(zerodvec);
VectorSubtract ( lookAtOrigin, origin, dir );
VectorNormalize ( dir );
dir[2] = 0;
CrossProduct ( dir, up, linedir );
for ( i = 0; i < spacingLine - 1; i ++ )
{
CRMArea* linearea;
vec3_t lineorigin;
linearea = new CRMArea ( spacingRadius, paddingSize, 0, zerodvec, zerodvec, false, symmetric );
linearea->LockOrigin ( );
linearea->EnableCollision(collide);
VectorMA ( origin, spacingRadius + (spacingRadius * 2 * i), linedir, lineorigin );
mAreas.push_back ( linearea );
MoveArea ( linearea, lineorigin );
linearea = new CRMArea ( spacingRadius, paddingSize, 0, zerodvec, zerodvec, false, symmetric );
linearea->LockOrigin ( );
linearea->EnableCollision(collide);
VectorMA ( origin, -spacingRadius - (spacingRadius * 2 * i), linedir, lineorigin );
mAreas.push_back ( linearea );
MoveArea ( linearea, lineorigin );
}
}
// Return it for convienience
return area;
}
/************************************************************************************************
* CRMAreaManager::EnumArea
* Allows for enumeration through the area list. If an invalid index is given then NULL will
* be returned;
*
* inputs:
* index - current enumeration index
*
* return:
* requested area class pointer or NULL if the index was invalid
*
************************************************************************************************/
CRMArea* CRMAreaManager::EnumArea ( const int index )
{
// This isnt an assertion case because there is no size method for
// the area manager so the areas are enumerated until NULL is returned.
if ( index < 0 || index >= mAreas.size ( ) )
{
return NULL;
}
return mAreas[index];
}
#ifdef _WIN32
#pragma optimize("p", off)
#endif

99
code/RMG/RM_Area.h Normal file
View File

@@ -0,0 +1,99 @@
/************************************************************************************************
*
* Copyright (C) 2001-2002 Raven Software
*
* RM_Area.h
*
************************************************************************************************/
#pragma once
#if !defined(RM_AREA_H_INC)
#define RM_AREA_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Area.h")
#endif
class CRMArea
{
private:
float mPaddingSize;
float mSpacingRadius;
float mConfineRadius;
float mRadius;
float mAngle;
int mMoveCount;
vec3_t mOrigin;
vec3_t mConfineOrigin;
vec3_t mLookAtOrigin;
bool mCollision;
bool mFlatten;
bool mLookAt;
bool mLockOrigin;
int mSymmetric;
public:
CRMArea ( float spacing, float padding, float confine, vec3_t confineOrigin, vec3_t lookAtOrigin, bool flatten = true, int symmetric = 0 );
void Mirror ( void );
void SetOrigin(vec3_t origin) { VectorCopy ( origin, mOrigin ); }
void SetAngle(float angle) { mAngle = angle; }
void SetSymmetric(int sym) { mSymmetric = sym; }
void EnableCollision(bool e) { mCollision = e; }
void EnableLookAt(bool la) {mLookAt = la; }
float LookAt(vec3_t lookat);
void LockOrigin( void ) { mLockOrigin = true; }
void AddMoveCount() { mMoveCount++; }
void ClearMoveCount() { mMoveCount=0; }
float GetPaddingSize() { return mPaddingSize; }
float GetSpacingRadius() { return mSpacingRadius; }
float GetRadius() { return mRadius; }
float GetConfineRadius() { return mConfineRadius; }
float GetAngle() { return mAngle; }
int GetMoveCount() { return mMoveCount; }
vec_t* GetOrigin() { return mOrigin; }
vec_t* GetConfineOrigin() { return mConfineOrigin; }
vec_t* GetLookAtOrigin() { return mLookAtOrigin; }
bool GetLookAt() { return mLookAt;}
bool GetLockOrigin() { return mLockOrigin; }
int GetSymmetric() { return mSymmetric; }
void SetRadius(float r) { mRadius = r; }
bool IsCollisionEnabled(){ return mCollision; }
bool IsFlattened (){ return mFlatten; }
};
typedef vector<CRMArea*> rmAreaVector_t;
class CRMAreaManager
{
private:
rmAreaVector_t mAreas;
vec3_t mMins;
vec3_t mMaxs;
float mWidth;
float mHeight;
public:
CRMAreaManager ( const vec3_t mins, const vec3_t maxs );
~CRMAreaManager ( );
CRMArea* CreateArea ( vec3_t origin, float spacing, int spacingline, float padding, float confine, vec3_t confineOrigin, vec3_t lookAtOrigin, bool flatten=true, bool collide=true, bool lockorigin=false, int symmetric=0);
void MoveArea ( CRMArea* area, vec3_t origin);
CRMArea* EnumArea ( const int index );
// void CreateMap ( void );
};
#endif

71
code/RMG/RM_Headers.h Normal file
View File

@@ -0,0 +1,71 @@
#pragma once
#if !defined(RM_HEADERS_H_INC)
#define RM_HEADERS_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Headers.h")
#endif
#pragma warning (push, 3)
#include <vector>
#include <list>
#pragma warning (pop)
using namespace std;
#if !defined(GENERICPARSER2_H_INC)
#include "../game/genericparser2.h"
#endif
#if !defined(CM_LOCAL_H_INC)
#include "../qcommon/cm_local.h"
#endif
#define MAX_INSTANCE_TRIES 5
// on a symmetric map which corner is the first node
typedef enum
{
SYMMETRY_NONE,
SYMMETRY_TOPLEFT,
SYMMETRY_BOTTOMRIGHT
} symmetry_t;
#if !defined(CM_TERRAINMAP_H_INC)
#include "../qcommon/cm_terrainmap.h"
#endif
#if !defined(RM_AREA_H_INC)
#include "RM_Area.h"
#endif
#if !defined(RM_PATH_H_INC)
#include "RM_Path.h"
#endif
#if !defined(RM_OBJECTIVE_H_INC)
#include "RM_Objective.h"
#endif
#if !defined(RM_INSTANCEFILE_H_INC)
#include "RM_InstanceFile.h"
#endif
#if !defined(RM_INSTANCE_H_INC)
#include "RM_Instance.h"
#endif
#if !defined(RM_MISSION_H_INC)
#include "RM_Mission.h"
#endif
#if !defined(RM_MANAGER_H_INC)
#include "RM_Manager.h"
#endif
#if !defined(RM_TERRAIN_H_INC)
#include "RM_Terrain.h"
#endif
#endif

191
code/RMG/RM_Instance.cpp Normal file
View File

@@ -0,0 +1,191 @@
#include "../server/exe_headers.h"
#include "rm_headers.h"
#include "../qcommon/cm_terrainmap.h"
/************************************************************************************************
* CRMInstance::CRMInstance
* constructs a instnace object using the given parser group
*
* inputs:
* instance: parser group containing information about the instance
*
* return:
* none
*
************************************************************************************************/
CRMInstance::CRMInstance ( CGPGroup *instGroup, CRMInstanceFile& instFile )
{
mObjective = NULL;
mSpacingRadius = 0;
mFlattenRadius = 0;
mFilter[0] = mTeamFilter[0] = 0;
mArea = NULL;
mAutomapSymbol = 0;
mEntityID = 0;
mSide = 0;
mMirror = 0;
mFlattenHeight = 66;
mSpacingLine = 0;
mSurfaceSprites = true;
mLockOrigin = false;
}
/************************************************************************************************
* CRMInstance::PreSpawn
* Prepares the instance for spawning by flattening the ground under it
*
* inputs:
* landscape: landscape the instance will be spawned on
*
* return:
* true: spawn preparation successful
* false: spawn preparation failed
*
************************************************************************************************/
bool CRMInstance::PreSpawn ( CRandomTerrain* terrain, qboolean IsServer )
{
vec3_t origin;
CArea area;
VectorCopy(GetOrigin(), origin);
if (mMirror)
{
origin[0] = TheRandomMissionManager->GetLandScape()->GetBounds()[0][0] + TheRandomMissionManager->GetLandScape()->GetBounds()[1][0] - origin[0];
origin[1] = TheRandomMissionManager->GetLandScape()->GetBounds()[0][1] + TheRandomMissionManager->GetLandScape()->GetBounds()[1][1] - origin[1];
}
const vec3_t& terxelSize = terrain->GetLandScape()->GetTerxelSize ( );
const vec3pair_t& bounds = terrain->GetLandScape()->GetBounds();
// Align the instance to the center of a terxel
origin[0] = bounds[0][0] + (int)((origin[0] - bounds[0][0] + terxelSize[0] / 2) / terxelSize[0]) * terxelSize[0];
origin[1] = bounds[0][1] + (int)((origin[1] - bounds[0][1] + terxelSize[1] / 2) / terxelSize[1]) * terxelSize[1];
// This is BAD - By copying the mirrored origin back into the instance, you've now mirrored the original instance
// so when anything from this point on looks at the instance they'll be looking at a mirrored version but will be expecting the original
// so later in the spawn functions the instance will be re-mirrored, because it thinks the mInstances have not been changed
// VectorCopy(origin, GetOrigin());
// Flatten the area below the instance
if ( GetFlattenRadius() )
{
area.Init( origin, GetFlattenRadius(), 0.0f, AT_NONE, 0, 0 );
terrain->GetLandScape()->FlattenArea( &area, mFlattenHeight | (mSurfaceSprites?0:0x80), false, true, true );
}
return true;
}
/************************************************************************************************
* CRMInstance::PostSpawn
* Finishes the spawn by linking any objectives into the world that are associated with it
*
* inputs:
* landscape: landscape the instance was spawned on
*
* return:
* true: post spawn successfull
* false: post spawn failed
*
************************************************************************************************/
bool CRMInstance::PostSpawn ( CRandomTerrain* terrain, qboolean IsServer )
{
if ( mObjective )
{
return mObjective->Link ( );
}
return true;
}
#ifndef DEDICATED
void CRMInstance::DrawAutomapSymbol()
{
// draw proper symbol on map for instance
switch (GetAutomapSymbol())
{
default:
case AUTOMAP_NONE:
if (HasObjective())
CM_TM_AddObjective(GetOrigin()[0], GetOrigin()[1], GetSide());
break;
case AUTOMAP_BLD:
CM_TM_AddBuilding(GetOrigin()[0], GetOrigin()[1], GetSide());
if (HasObjective())
CM_TM_AddObjective(GetOrigin()[0], GetOrigin()[1], GetSide());
break;
case AUTOMAP_OBJ:
CM_TM_AddObjective(GetOrigin()[0], GetOrigin()[1], GetSide());
break;
case AUTOMAP_START:
CM_TM_AddStart(GetOrigin()[0], GetOrigin()[1], GetSide());
break;
case AUTOMAP_END:
CM_TM_AddEnd(GetOrigin()[0], GetOrigin()[1], GetSide());
break;
case AUTOMAP_ENEMY:
if (HasObjective())
CM_TM_AddObjective(GetOrigin()[0], GetOrigin()[1]);
if (1 == Cvar_VariableIntegerValue("rmg_automapshowall"))
CM_TM_AddNPC(GetOrigin()[0], GetOrigin()[1], false);
break;
case AUTOMAP_FRIEND:
if (HasObjective())
CM_TM_AddObjective(GetOrigin()[0], GetOrigin()[1]);
if (1 == Cvar_VariableIntegerValue("rmg_automapshowall"))
CM_TM_AddNPC(GetOrigin()[0], GetOrigin()[1], true);
break;
case AUTOMAP_WALL:
CM_TM_AddWallRect(GetOrigin()[0], GetOrigin()[1], GetSide());
break;
}
}
#endif // !DEDICATED
/************************************************************************************************
* CRMInstance::Preview
* Renderings debug information about the instance
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
void CRMInstance::Preview ( const vec3_t from )
{
/* CEntity *tent;
// Add a cylindar for the whole settlement
tent = G_TempEntity( GetOrigin(), EV_DEBUG_CYLINDER );
VectorCopy( GetOrigin(), tent->s.origin2 );
tent->s.pos.trBase[2] += 40;
tent->s.origin2[2] += 50;
tent->s.time = 1050 + ((int)(GetSpacingRadius())<<16);
tent->s.time2 = GetPreviewColor ( );
G_AddTempEntity(tent);
// Origin line
tent = G_TempEntity( GetOrigin ( ), EV_DEBUG_LINE );
VectorCopy( GetOrigin(), tent->s.origin2 );
tent->s.origin2[2] += 400;
tent->s.time = 1050;
tent->s.weapon = 10;
tent->s.time2 = (255<<24) + (255<<16) + (255<<8) + 255;
G_AddTempEntity(tent);
if ( GetFlattenRadius ( ) )
{
// Add a cylindar for the whole settlement
tent = G_TempEntity( GetOrigin(), EV_DEBUG_CYLINDER );
VectorCopy( GetOrigin(), tent->s.origin2 );
tent->s.pos.trBase[2] += 40;
tent->s.origin2[2] += 50;
tent->s.time = 1050 + ((int)(GetFlattenRadius ( ))<<16);
tent->s.time2 = (255<<24) + (80<<16) +(80<<8) + 80;
G_AddTempEntity(tent);
}
*/
}

122
code/RMG/RM_Instance.h Normal file
View File

@@ -0,0 +1,122 @@
#pragma once
#if !defined(RM_INSTANCE_H_INC)
#define RM_INSTANCE_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Instance.h")
#endif
#if !defined(CM_LANDSCAPE_H_INC)
#include "../qcommon/cm_landscape.h"
#endif
enum CRMAutomapSymbol
{
AUTOMAP_NONE = 0,
AUTOMAP_BLD = 1,
AUTOMAP_OBJ = 2,
AUTOMAP_START= 3,
AUTOMAP_END = 4,
AUTOMAP_ENEMY= 5,
AUTOMAP_FRIEND=6,
AUTOMAP_WALL=7
};
class CRMInstance
{
protected:
char mFilter[MAX_QPATH]; // filter of entities inside of this
char mTeamFilter[MAX_QPATH]; // team specific filter
vec3pair_t mBounds; // Bounding box for instance itself
CRMArea* mArea; // Position of the instance
CRMObjective* mObjective; // Objective associated with this instance
// optional instance specific strings for objective
string mMessage; // message outputed when objective is completed
string mDescription; // description of objective
string mInfo; // more info for objective
float mSpacingRadius; // Radius to space instances with
float mFlattenRadius; // Radius to flatten under instances
int mSpacingLine; // Line of spacing radius's, forces locket
bool mLockOrigin; // Origin cant move
bool mSurfaceSprites; // allow surface sprites under instance?
int mAutomapSymbol; // show which symbol on automap 0=none
int mEntityID; // id of entity spawned
int mSide; // blue or red side
int mMirror; // mirror origin, angle
int mFlattenHeight; // height to flatten land
public:
CRMInstance ( CGPGroup* instance, CRMInstanceFile& instFile);
virtual ~CRMInstance ( ) { }
virtual bool IsValid ( ) { return true; }
virtual bool PreSpawn ( CRandomTerrain* terrain, qboolean IsServer );
virtual bool Spawn ( CRandomTerrain* terrain, qboolean IsServer ) { return false; }
virtual bool PostSpawn ( CRandomTerrain* terrain, qboolean IsServer );
virtual void Preview ( const vec3_t from );
virtual void SetArea ( CRMAreaManager* amanager, CRMArea* area ) { mArea = area; }
virtual void SetFilter ( const char *filter ) { strcpy(mFilter, filter); }
virtual void SetTeamFilter ( const char *teamFilter ) { strcpy(mTeamFilter, teamFilter); }
void SetObjective ( CRMObjective* obj ) { mObjective = obj; }
CRMObjective* GetObjective (void) {return mObjective;}
bool HasObjective () {return mObjective != NULL;}
int GetAutomapSymbol () {return mAutomapSymbol;}
void DrawAutomapSymbol ();
const char* GetMessage(void) { return mMessage.c_str(); }
const char* GetDescription(void){ return mDescription.c_str(); }
const char* GetInfo(void) { return mInfo.c_str(); }
void SetMessage(const char* msg) { mMessage = msg; }
void SetDescription(const char* desc) { mDescription = desc; }
void SetInfo(const char* info) { mInfo = info; }
void SetSide(int side) {mSide = side;}
int GetSide ( ) {return mSide;}
// NOTE: should consider making SetMirror also set all other variables that need flipping
// like the origin and Side, etc... Otherwise an Instance may have had it's origin flipped
// but then later will have mMirror set to false, but the origin is still flipped. So any functions
// that look at the instance later will see mMirror set to false, but not realize the origin has ALREADY been flipped
virtual void SetMirror(int mirror) { mMirror = mirror;}
int GetMirror ( ) { return mMirror;}
virtual bool GetSurfaceSprites ( ) { return mSurfaceSprites; }
virtual bool GetLockOrigin ( ) { return mLockOrigin; }
virtual int GetSpacingLine ( ) { return mSpacingLine; }
virtual int GetPreviewColor ( ) { return 0; }
virtual float GetSpacingRadius ( ) { return mSpacingRadius; }
virtual float GetFlattenRadius ( ) { return mFlattenRadius; }
const char *GetFilter ( ) { return mFilter; }
const char *GetTeamFilter ( ) { return mTeamFilter; }
CRMArea& GetArea ( ) { return *mArea; }
vec_t* GetOrigin ( ) {return mArea->GetOrigin(); }
float GetAngle ( ) {return mArea->GetAngle();}
void SetAngle(float ang ) { mArea->SetAngle(ang);}
const vec3pair_t& GetBounds(void) const { return(mBounds); }
void SetFlattenHeight ( int height ) { mFlattenHeight = height; }
int GetFlattenHeight ( void ) { return mFlattenHeight; }
void SetSpacingRadius (float spacing) { mSpacingRadius = spacing; }
};
typedef list<CRMInstance*>::iterator rmInstanceIter_t;
typedef list<CRMInstance*> rmInstanceList_t;
#endif

View File

@@ -0,0 +1,200 @@
/************************************************************************************************
*
* RM_InstanceFile.cpp
*
* implements the CRMInstanceFile class. This class provides functionality to load
* and create instances from an instance file. First call Open to open the instance file and
* then use CreateInstance to create new instances. When finished call Close to cleanup.
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "rm_headers.h"
//#include "rm_instance_npc.h"
#include "rm_instance_bsp.h"
#include "rm_instance_random.h"
#include "rm_instance_group.h"
#include "rm_instance_void.h"
/************************************************************************************************
* CRMInstanceFile::CRMInstanceFile
* constructor
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMInstanceFile::CRMInstanceFile ( )
{
mInstances = NULL;
}
/************************************************************************************************
* CRMInstanceFile::~CRMInstanceFile
* Destroys the instance file by freeing the parser
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMInstanceFile::~CRMInstanceFile ( )
{
Close ( );
}
/************************************************************************************************
* CRMInstanceFile::Open
* Opens the given instance file and prepares it for use in instance creation
*
* inputs:
* instance: Name of instance to open. Note that the root path will be automatically
* added and shouldnt be included in the given name
*
* return:
* true: instance file successfully loaded
* false: instance file could not be loaded for some reason
*
************************************************************************************************/
bool CRMInstanceFile::Open ( const char* instance )
{
char instanceDef[MAX_QPATH];
CGPGroup *basegroup;
// Build the filename
Com_sprintf(instanceDef, MAX_QPATH, "ext_data/rmg/%s.instance", instance );
#ifndef FINAL_BUILD
// Debug message
Com_Printf("CM_Terrain: Loading and parsing instanceDef %s.....\n", instance);
#endif
// Parse the text file using the generic parser
if(!Com_ParseTextFile(instanceDef, mParser ))
{
Com_sprintf(instanceDef, MAX_QPATH, "ext_data/arioche/%s.instance", instance );
if(!Com_ParseTextFile(instanceDef, mParser ))
{
Com_Printf(va("CM_Terrain: Could not open instance file '%s'\n", instanceDef));
return false;
}
}
// The whole file....
basegroup = mParser.GetBaseParseGroup();
// The root { } struct
mInstances = basegroup->GetSubGroups();
// The "instances" { } structure
mInstances = mInstances->GetSubGroups ( );
return true;
}
/************************************************************************************************
* CRMInstanceFile::Close
* Closes an open instance file
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
void CRMInstanceFile::Close ( void )
{
// If not open then dont close it
if ( NULL == mInstances )
{
return;
}
mParser.Clean();
mInstances = NULL;
}
/************************************************************************************************
* CRMInstanceFile::CreateInstance
* Creates an instance (to be freed by caller) using the given instance name.
*
* inputs:
* name: Name of the instance to read from the instance file
*
* return:
* NULL: instance could not be read from the instance file
* NON-NULL: instance created and returned for further use
*
************************************************************************************************/
CRMInstance* CRMInstanceFile::CreateInstance ( const char* name )
{
static int instanceID = 0;
CGPGroup* group;
CRMInstance* instance;
// Make sure we were loaded
assert ( mInstances );
// Search through the instances for the one with the given name
for ( group = mInstances; group; group = group->GetNext ( ) )
{
// Skip it if the name doesnt match
if ( stricmp ( name, group->FindPairValue ( "name", "" ) ) )
{
continue;
}
// Handle the various forms of instance types
if ( !stricmp ( group->GetName ( ), "bsp" ) )
{
instance = new CRMBSPInstance ( group, *this );
}
else if ( !stricmp ( group->GetName ( ), "npc" ) )
{
// instance = new CRMNPCInstance ( group, *this );
continue;
}
else if ( !stricmp ( group->GetName ( ), "group" ) )
{
instance = new CRMGroupInstance ( group, *this );
}
else if ( !stricmp ( group->GetName ( ), "random" ) )
{
instance = new CRMRandomInstance ( group, *this );
}
else if ( !stricmp ( group->GetName ( ), "void" ) )
{
instance = new CRMVoidInstance ( group, *this );
}
else
{
continue;
}
// If the instance isnt valid after being created then delete it
if ( !instance->IsValid ( ) )
{
delete instance;
return NULL;
}
// The instance was successfully created so return it
return instance;
}
#ifndef FINAL_BUILD
// The instance wasnt found in the file so report it
Com_Printf(va("WARNING: Instance '%s' was not found in the active instance file\n", name ));
#endif
return NULL;
}

View File

@@ -0,0 +1,28 @@
#pragma once
#if !defined(RM_INSTANCEFILE_H_INC)
#define RM_INSTANCEFILE_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_InstanceFile.h")
#endif
class CRMInstance;
class CRMInstanceFile
{
public:
CRMInstanceFile ( );
~CRMInstanceFile ( );
bool Open ( const char* instance );
void Close ( void );
CRMInstance* CreateInstance ( const char* name );
protected:
CGenericParser2 mParser;
CGPGroup* mInstances;
};
#endif

View File

@@ -0,0 +1,294 @@
/************************************************************************************************
*
* RM_Instance_BSP.cpp
*
* Implements the CRMBSPInstance class. This class is reponsible for parsing a
* bsp instance as well as spawning it into a landscape.
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "../qcommon/cm_local.h"
#include "../server/server.h"
#include "rm_headers.h"
#include "rm_instance_bsp.h"
#include "../client/vmachine.h"
/************************************************************************************************
* CRMBSPInstance::CRMBSPInstance
* constructs a building instance object using the given parser group
*
* inputs:
* instance: parser group containing information about the building instance
*
* return:
* none
*
************************************************************************************************/
CRMBSPInstance::CRMBSPInstance(CGPGroup *instGroup, CRMInstanceFile& instFile) : CRMInstance ( instGroup, instFile )
{
strcpy(mBsp, instGroup->FindPairValue("file", ""));
mAngleVariance = DEG2RAD(atof(instGroup->FindPairValue("anglevariance", "0")));
mBaseAngle = DEG2RAD(atof(instGroup->FindPairValue("baseangle", "0")));
mAngleDiff = DEG2RAD(atof(instGroup->FindPairValue("anglediff", "0")));
mSpacingRadius = atof( instGroup->FindPairValue ( "spacing", "100" ) );
mSpacingLine = atoi( instGroup->FindPairValue ( "spacingline", "0" ) );
mSurfaceSprites = (!Q_stricmp ( instGroup->FindPairValue ( "surfacesprites", "no" ), "yes")) ? true : false;
mLockOrigin = (!Q_stricmp ( instGroup->FindPairValue ( "lockorigin", "no" ), "yes")) ? true : false;
mFlattenRadius = atof( instGroup->FindPairValue ( "flatten", "0" ) );
mHoleRadius = atof( instGroup->FindPairValue ( "hole", "0" ) );
const char * automapSymName = instGroup->FindPairValue ( "automap_symbol", "building" );
if (0 == strcmpi(automapSymName, "none")) mAutomapSymbol = AUTOMAP_NONE ;
else if (0 == strcmpi(automapSymName, "building")) mAutomapSymbol = AUTOMAP_BLD ;
else if (0 == strcmpi(automapSymName, "objective")) mAutomapSymbol = AUTOMAP_OBJ ;
else if (0 == strcmpi(automapSymName, "start")) mAutomapSymbol = AUTOMAP_START;
else if (0 == strcmpi(automapSymName, "end")) mAutomapSymbol = AUTOMAP_END ;
else if (0 == strcmpi(automapSymName, "enemy")) mAutomapSymbol = AUTOMAP_ENEMY;
else if (0 == strcmpi(automapSymName, "friend")) mAutomapSymbol = AUTOMAP_FRIEND;
else if (0 == strcmpi(automapSymName, "wall")) mAutomapSymbol = AUTOMAP_WALL;
else mAutomapSymbol = atoi( automapSymName );
// optional instance objective strings
SetMessage(instGroup->FindPairValue("objective_message",""));
SetDescription(instGroup->FindPairValue("objective_description",""));
SetInfo(instGroup->FindPairValue("objective_info",""));
mBounds[0][0] = 0;
mBounds[0][1] = 0;
mBounds[1][0] = 0;
mBounds[1][1] = 0;
mBaseAngle += (TheRandomMissionManager->GetLandScape()->irand(0,mAngleVariance) - mAngleVariance/2);
}
/************************************************************************************************
* CRMBSPInstance::Spawn
* spawns a bsp into the world using the previously aquired origin
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
bool CRMBSPInstance::Spawn ( CRandomTerrain* terrain, qboolean IsServer)
{
#ifndef PRE_RELEASE_DEMO
// TEntity* ent;
float yaw;
char temp[10000];
char *savePtr;
vec3_t origin;
vec3_t notmirrored;
float water_level = terrain->GetLandScape()->GetWaterHeight();
const vec3_t& terxelSize = terrain->GetLandScape()->GetTerxelSize ( );
const vec3pair_t& bounds = terrain->GetLandScape()->GetBounds();
// If this entity somehow lost its collision flag then boot it
if ( !GetArea().IsCollisionEnabled ( ) )
{
return false;
}
// copy out the unmirrored version
VectorCopy(GetOrigin(), notmirrored);
// we want to mirror it before determining the Z value just in case the landscape isn't perfectly mirrored
if (mMirror)
{
GetOrigin()[0] = TheRandomMissionManager->GetLandScape()->GetBounds()[0][0] + TheRandomMissionManager->GetLandScape()->GetBounds()[1][0] - GetOrigin()[0];
GetOrigin()[1] = TheRandomMissionManager->GetLandScape()->GetBounds()[0][1] + TheRandomMissionManager->GetLandScape()->GetBounds()[1][1] - GetOrigin()[1];
}
// Align the instance to the center of a terxel
GetOrigin ( )[0] = bounds[0][0] + (int)((GetOrigin ( )[0] - bounds[0][0] + terxelSize[0] / 2) / terxelSize[0]) * terxelSize[0];
GetOrigin ( )[1] = bounds[0][1] + (int)((GetOrigin ( )[1] - bounds[0][1] + terxelSize[1] / 2) / terxelSize[1]) * terxelSize[1];
// Make sure the bsp is resting on the ground, not below or above it
// NOTE: This check is basically saying "is this instance not a bridge", because when instances are created they are all
// placed above the world's Z boundary, EXCEPT FOR BRIDGES. So this call to GetWorldHeight will move all other instances down to
// ground level except bridges
if ( GetOrigin()[2] > terrain->GetBounds()[1][2] )
{
if( GetFlattenRadius() )
{
terrain->GetLandScape()->GetWorldHeight ( GetOrigin(), GetBounds ( ), false );
GetOrigin()[2] += 5;
}
else if (IsServer)
{ // if this instance does not flatten the ground around it, do a trace to more accurately determine its Z value
trace_t tr;
vec3_t end;
vec3_t start;
VectorCopy(GetOrigin(), end);
VectorCopy(GetOrigin(), start);
// start the trace below the top height of the landscape
start[2] = TheRandomMissionManager->GetLandScape()->GetBounds()[1][2] - 1;
// end the trace at the bottom of the world
end[2] = MIN_WORLD_COORD;
memset ( &tr, 0, sizeof ( tr ) );
SV_Trace( &tr, start, vec3_origin, vec3_origin, end, ENTITYNUM_NONE, CONTENTS_TERRAIN|CONTENTS_SOLID, G2_NOCOLLIDE, 0); //qfalse, 0, 10 );
if( !(tr.contents & CONTENTS_TERRAIN) || (tr.fraction == 1.0) )
{
if ( 0 )
assert(0); // this should never happen
// restore the unmirrored origin
VectorCopy( notmirrored, GetOrigin() );
// don't spawn
return false;
}
// assign the Z-value to wherever it hit the terrain
GetOrigin()[2] = tr.endpos[2];
// lower it a little, otherwise the bottom of the instance might be exposed if on some weird sloped terrain
GetOrigin()[2] -= 16; // FIXME: would it be better to use a number related to the instance itself like 1/5 it's height or something...
}
}
else
{
terrain->GetLandScape()->GetWorldHeight ( GetOrigin(), GetBounds ( ), true );
}
// save away the origin
VectorCopy(GetOrigin(), origin);
// make sure not to spawn if in water
if (!HasObjective() && GetOrigin()[2] < water_level)
return false;
// restore the origin
VectorCopy(origin, GetOrigin());
if (mMirror)
{ // change blue things to red for symmetric maps
if (strlen(mFilter) > 0)
{
char * blue = strstr(mFilter,"blue");
if (blue)
{
blue[0] = (char) 0;
strcat(mFilter, "red");
SetSide(SIDE_RED);
}
}
if (strlen(mTeamFilter) > 0)
{
char * blue = strstr(mTeamFilter,"blue");
if (blue)
{
strcpy(mTeamFilter, "red");
SetSide(SIDE_RED);
}
}
yaw = RAD2DEG(mArea->GetAngle() + mBaseAngle) + 180;
}
else
{
yaw = RAD2DEG(mArea->GetAngle() + mBaseAngle);
}
/*
if( TheRandomMissionManager->GetMission()->GetSymmetric() )
{
vec3_t diagonal;
vec3_t lineToPoint;
vec3_t mins;
vec3_t maxs;
vec3_t point;
vec3_t vProj;
vec3_t vec;
float distance;
VectorCopy( TheRandomMissionManager->GetLandScape()->GetBounds()[1], maxs );
VectorCopy( TheRandomMissionManager->GetLandScape()->GetBounds()[0], mins );
VectorCopy( GetOrigin(), point );
mins[2] = maxs[2] = point[2] = 0;
VectorSubtract( point, mins, lineToPoint );
VectorSubtract( maxs, mins, diagonal);
VectorNormalize(diagonal);
VectorMA( mins, DotProduct(lineToPoint, diagonal), diagonal, vProj);
VectorSubtract(point, vProj, vec );
distance = VectorLength(vec);
// if an instance is too close to the imaginary diagonal that cuts the world in half, don't spawn it
// otherwise you can get overlapping instances
if( distance < GetSpacingRadius() )
{
#ifdef _DEBUG
mAutomapSymbol = AUTOMAP_END;
#endif
if( !HasObjective() )
{
return false;
}
}
}
*/
// Spawn in the bsp model
sprintf(temp,
"{\n"
"\"classname\" \"misc_bsp\"\n"
"\"bspmodel\" \"%s\"\n"
"\"origin\" \"%f %f %f\"\n"
"\"angles\" \"0 %f 0\"\n"
"\"filter\" \"%s\"\n"
"\"teamfilter\" \"%s\"\n"
"\"spacing\" \"%d\"\n"
"\"flatten\" \"%d\"\n"
"}\n",
mBsp,
GetOrigin()[0], GetOrigin()[1], GetOrigin()[2],
AngleNormalize360(yaw),
mFilter,
mTeamFilter,
(int)GetSpacingRadius(),
(int)GetFlattenRadius()
);
if (IsServer)
{ // only allow for true spawning on the server
savePtr = sv.entityParsePoint;
sv.entityParsePoint = temp;
// VM_Call( cgvm, GAME_SPAWN_RMG_ENTITY );
// char *s;
int bufferSize = 1024;
char buffer[1024];
// s = COM_Parse( (const char **)&sv.entityParsePoint );
Q_strncpyz( buffer, sv.entityParsePoint, bufferSize );
if ( sv.entityParsePoint && sv.entityParsePoint[0] )
{
ge->GameSpawnRMGEntity(sv.entityParsePoint);
}
sv.entityParsePoint = savePtr;
}
#ifndef DEDICATED
DrawAutomapSymbol();
#endif
Com_DPrintf( "RMG: Building '%s' spawned at (%f %f %f)\n", mBsp, GetOrigin()[0], GetOrigin()[1], GetOrigin()[2] );
// now restore the instances un-mirrored origin
// NOTE: all this origin flipping, setting the side etc... should be done when mMirror is set
// because right after this function is called, mMirror is set to 0 but all the instance data is STILL MIRRORED -- not good
VectorCopy(notmirrored, GetOrigin());
#endif // PRE_RELEASE_DEMO
return true;
}

View File

@@ -0,0 +1,35 @@
#pragma once
#if !defined(RM_INSTANCE_BSP_H_INC)
#define RM_INSTANCE_BSP_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Instance_BSP.h")
#endif
class CRMBSPInstance : public CRMInstance
{
private:
char mBsp[MAX_QPATH];
float mAngleVariance;
float mBaseAngle;
float mAngleDiff;
float mHoleRadius;
public:
CRMBSPInstance ( CGPGroup *instance, CRMInstanceFile& instFile );
virtual int GetPreviewColor ( ) { return (255<<24)+255; }
virtual float GetHoleRadius ( ) { return mHoleRadius; }
virtual bool Spawn ( CRandomTerrain* terrain, qboolean IsServer );
const char* GetModelName (void) const { return(mBsp); }
float GetAngleDiff (void) const { return(mAngleDiff); }
bool GetAngularType (void) const { return(mAngleDiff != 0.0f); }
};
#endif

View File

@@ -0,0 +1,343 @@
/************************************************************************************************
*
* RM_Instance_Group.cpp
*
* Implements the CRMGroupInstance class. This class is reponsible for parsing a
* group instance as well as spawning it into a landscape.
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "rm_headers.h"
#include "rm_instance_group.h"
/************************************************************************************************
* CRMGroupInstance::CRMGroupInstance
* constructur
*
* inputs:
* settlementID: ID of the settlement being created
*
* return:
* none
*
************************************************************************************************/
CRMGroupInstance::CRMGroupInstance ( CGPGroup *instGroup, CRMInstanceFile& instFile )
: CRMInstance ( instGroup, instFile )
{
// Grab the padding and confine radius
mPaddingSize = atof ( instGroup->FindPairValue ( "padding", va("%i", TheRandomMissionManager->GetMission()->GetDefaultPadding() ) ) );
mConfineRadius = atof ( instGroup->FindPairValue ( "confine", "0" ) );
const char * automapSymName = instGroup->FindPairValue ( "automap_symbol", "none" );
if (0 == strcmpi(automapSymName, "none")) mAutomapSymbol = AUTOMAP_NONE ;
else if (0 == strcmpi(automapSymName, "building")) mAutomapSymbol = AUTOMAP_BLD ;
else if (0 == strcmpi(automapSymName, "objective")) mAutomapSymbol = AUTOMAP_OBJ ;
else if (0 == strcmpi(automapSymName, "start")) mAutomapSymbol = AUTOMAP_START;
else if (0 == strcmpi(automapSymName, "end")) mAutomapSymbol = AUTOMAP_END ;
else if (0 == strcmpi(automapSymName, "enemy")) mAutomapSymbol = AUTOMAP_ENEMY;
else if (0 == strcmpi(automapSymName, "friend")) mAutomapSymbol = AUTOMAP_FRIEND;
else mAutomapSymbol = atoi( automapSymName );
// optional instance objective strings
SetMessage(instGroup->FindPairValue("objective_message",""));
SetDescription(instGroup->FindPairValue("objective_description",""));
SetInfo(instGroup->FindPairValue("objective_info",""));
// Iterate through the sub groups to determine the instances which make up the group
instGroup = instGroup->GetSubGroups ( );
while ( instGroup )
{
CRMInstance* instance;
const char* name;
int mincount;
int maxcount;
int count;
float minrange;
float maxrange;
// Make sure only instances are specified as sub groups
assert ( 0 == stricmp ( instGroup->GetName ( ), "instance" ) );
// Grab the name
name = instGroup->FindPairValue ( "name", "" );
// Grab the range information
minrange = atof(instGroup->FindPairValue ( "minrange", "0" ) );
maxrange = atof(instGroup->FindPairValue ( "maxrange", "0" ) );
// Grab the count information and randomly generate a count value
mincount = atoi(instGroup->FindPairValue ( "mincount", "1" ) );
maxcount = atoi(instGroup->FindPairValue ( "maxcount", "1" ) );
count = mincount;
if ( maxcount > mincount )
{
count += (TheRandomMissionManager->GetLandScape()->irand(0, maxcount-mincount));
}
// For each count create and add the instance
for ( ; count ; count -- )
{
// Create the instance
instance = instFile.CreateInstance ( name );
// Skip this instance if it couldnt be created for some reason. The CreateInstance
// method will report an error so no need to do so here.
if ( NULL == instance )
{
continue;
}
// Set the min and max range for the instance
instance->SetFilter(mFilter);
instance->SetTeamFilter(mTeamFilter);
// Add the instance to the list
mInstances.push_back ( instance );
}
// Next sub group
instGroup = instGroup->GetNext ( );
}
}
/************************************************************************************************
* CRMGroupInstance::~CRMGroupInstance
* Removes all buildings and inhabitants
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMGroupInstance::~CRMGroupInstance(void)
{
// Cleanup
RemoveInstances ( );
}
/************************************************************************************************
* CRMGroupInstance::SetFilter
* Sets a filter used to exclude instances
*
* inputs:
* filter: filter name
*
* return:
* none
*
************************************************************************************************/
void CRMGroupInstance::SetFilter( const char *filter )
{
rmInstanceIter_t it;
CRMInstance::SetFilter(filter);
for(it = mInstances.begin(); it != mInstances.end(); it++)
{
(*it)->SetFilter(filter);
}
}
/************************************************************************************************
* CRMGroupInstance::SetTeamFilter
* Sets the filter used to exclude team based instances
*
* inputs:
* teamFilter: filter name
*
* return:
* none
*
************************************************************************************************/
void CRMGroupInstance::SetTeamFilter( const char *teamFilter )
{
rmInstanceIter_t it;
CRMInstance::SetTeamFilter(teamFilter);
for(it = mInstances.begin(); it != mInstances.end(); it++)
{
(*it)->SetTeamFilter(teamFilter);
}
}
/************************************************************************************************
* CRMGroupInstance::SetMirror
* Sets the flag to mirror an instance on map
*
* inputs:
* mirror
*
* return:
* none
*
************************************************************************************************/
void CRMGroupInstance::SetMirror(int mirror)
{
rmInstanceIter_t it;
CRMInstance::SetMirror(mirror);
for(it = mInstances.begin(); it != mInstances.end(); it++)
{
(*it)->SetMirror(mirror);
}
}
/************************************************************************************************
* CRMGroupInstance::RemoveInstances
* Removes all instances associated with the group
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
void CRMGroupInstance::RemoveInstances ( )
{
rmInstanceIter_t it;
for(it = mInstances.begin(); it != mInstances.end(); it++)
{
delete *it;
}
mInstances.clear();
}
/************************************************************************************************
* CRMGroupInstance::PreSpawn
* Prepares the group for spawning by
*
* inputs:
* landscape: landscape to calculate the position within
* instance: instance to calculate the position for
*
* return:
* none
*
************************************************************************************************/
bool CRMGroupInstance::PreSpawn ( CRandomTerrain* terrain, qboolean IsServer )
{
rmInstanceIter_t it;
for(it = mInstances.begin(); it != mInstances.end(); it++ )
{
CRMInstance* instance = *it;
instance->SetFlattenHeight ( mFlattenHeight );
// Add the instance to the landscape now
instance->PreSpawn ( terrain, IsServer );
}
return CRMInstance::PreSpawn ( terrain, IsServer );
}
/************************************************************************************************
* CRMGroupInstance::Spawn
* Adds the group instance to the given landscape using the specified origin. All sub instances
* will be added to the landscape within their min and max range from the origin.
*
* inputs:
* landscape: landscape to add the instance group to
* origin: origin of the instance group
*
* return:
* none
*
************************************************************************************************/
bool CRMGroupInstance::Spawn ( CRandomTerrain* terrain, qboolean IsServer )
{
rmInstanceIter_t it;
// Spawn all the instances associated with this group
for(it = mInstances.begin(); it != mInstances.end(); it++)
{
CRMInstance* instance = *it;
instance->SetSide(GetSide()); // which side owns it?
// Add the instance to the landscape now
instance->Spawn ( terrain, IsServer );
}
#ifndef DEDICATED
DrawAutomapSymbol();
#endif
return true;
}
/************************************************************************************************
* CRMGroupInstance::Preview
* Renders debug information for the instance
*
* inputs:
* from: point to render the preview from
*
* return:
* none
*
************************************************************************************************/
void CRMGroupInstance::Preview ( const vec3_t from )
{
rmInstanceIter_t it;
CRMInstance::Preview ( from );
// Render all the instances
for(it = mInstances.begin(); it != mInstances.end(); it++)
{
CRMInstance* instance = *it;
instance->Preview ( from );
}
}
/************************************************************************************************
* CRMGroupInstance::SetArea
* Overidden to make sure the groups area doesnt eat up any room. The collision on the
* groups area will be turned off
*
* inputs:
* area: area to set
*
* return:
* none
*
************************************************************************************************/
void CRMGroupInstance::SetArea ( CRMAreaManager* amanager, CRMArea* area )
{
rmInstanceIter_t it;
bool collide = area->IsCollisionEnabled ( );
// Disable collision
area->EnableCollision ( false );
// Do what really needs to get done
CRMInstance::SetArea ( amanager, area );
// Prepare for spawn by calculating all the positions of the sub instances
// and flattening the ground below them.
for(it = mInstances.begin(); it != mInstances.end(); it++ )
{
CRMInstance *instance = *it;
CRMArea *newarea;
vec3_t origin;
// Drop it in the center of the group for now
origin[0] = GetOrigin()[0];
origin[1] = GetOrigin()[1];
origin[2] = 2500;
// Set the area of position
newarea = amanager->CreateArea ( origin, instance->GetSpacingRadius(), instance->GetSpacingLine(), mPaddingSize, mConfineRadius, GetOrigin(), GetOrigin(), instance->GetFlattenRadius()?true:false, collide, instance->GetLockOrigin(), area->GetSymmetric ( ) );
instance->SetArea ( amanager, newarea );
}
}

View File

@@ -0,0 +1,41 @@
#pragma once
#if !defined(RM_INSTANCE_GROUP_H_INC)
#define RM_INSTANCE_GROUP_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Instance_Group.h")
#endif
class CRMGroupInstance : public CRMInstance
{
protected:
rmInstanceList_t mInstances;
float mConfineRadius;
float mPaddingSize;
public:
CRMGroupInstance( CGPGroup* instGroup, CRMInstanceFile& instFile);
~CRMGroupInstance();
virtual bool PreSpawn ( CRandomTerrain* terrain, qboolean IsServer );
virtual bool Spawn ( CRandomTerrain* terrain, qboolean IsServer );
virtual void Preview ( const vec3_t from );
virtual void SetFilter ( const char *filter );
virtual void SetTeamFilter ( const char *teamFilter );
virtual void SetArea ( CRMAreaManager* amanager, CRMArea* area );
virtual int GetPreviewColor ( ) { return (255<<24)+(255<<8); }
virtual float GetSpacingRadius ( ) { return 0; }
virtual float GetFlattenRadius ( ) { return 0; }
virtual void SetMirror(int mirror);
protected:
void RemoveInstances ( );
};
#endif

View File

@@ -0,0 +1,187 @@
/************************************************************************************************
*
* RM_Instance_Random.cpp
*
* Implements the CRMRandomInstance class. This class is reponsible for parsing a
* random instance as well as spawning it into a landscape.
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "rm_headers.h"
#include "rm_instance_random.h"
/************************************************************************************************
* CRMRandomInstance::CRMRandomInstance
* constructs a random instance by choosing one of the sub instances and creating it
*
* inputs:
* instGroup: parser group containing infromation about this instance
* instFile: reference to an open instance file for creating sub instances
*
* return:
* none
*
************************************************************************************************/
CRMRandomInstance::CRMRandomInstance ( CGPGroup *instGroup, CRMInstanceFile& instFile )
: CRMInstance ( instGroup, instFile )
{
CGPGroup* group;
CGPGroup* groups[MAX_RANDOM_INSTANCES];
int numGroups;
// Build a list of the groups one can be chosen
for ( numGroups = 0, group = instGroup->GetSubGroups ( );
group;
group = group->GetNext ( ) )
{
// If this isnt an instance group then skip it
if ( stricmp ( group->GetName ( ), "instance" ) )
{
continue;
}
int multiplier = atoi(group->FindPairValue ( "multiplier", "1" ));
for ( ; multiplier > 0 && numGroups < MAX_RANDOM_INSTANCES; multiplier -- )
{
groups[numGroups++] = group;
}
}
// No groups, no instance
if ( !numGroups )
{
// Initialize this now
mInstance = NULL;
Com_Printf ( "WARNING: No sub instances specified for random instance '%s'\n", group->FindPairValue ( "name", "unknown" ) );
return;
}
// Now choose a group to parse
instGroup = groups[TheRandomMissionManager->GetLandScape()->irand(0,numGroups-1)];
// Create the child instance now. If the instance create fails then the
// IsValid routine will return false and this instance wont be added
mInstance = instFile.CreateInstance ( instGroup->FindPairValue ( "name", "" ) );
mInstance->SetFilter(mFilter);
mInstance->SetTeamFilter(mTeamFilter);
mAutomapSymbol = mInstance->GetAutomapSymbol();
SetMessage(mInstance->GetMessage());
SetDescription(mInstance->GetDescription());
SetInfo(mInstance->GetInfo());
}
/************************************************************************************************
* CRMRandomInstance::~CRMRandomInstance
* Deletes the sub instance
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMRandomInstance::~CRMRandomInstance(void)
{
if ( mInstance )
{
delete mInstance;
}
}
void CRMRandomInstance::SetMirror(int mirror)
{
CRMInstance::SetMirror(mirror);
if (mInstance)
{
mInstance->SetMirror(mirror);
}
}
void CRMRandomInstance::SetFilter( const char *filter )
{
CRMInstance::SetFilter(filter);
if (mInstance)
{
mInstance->SetFilter(filter);
}
}
void CRMRandomInstance::SetTeamFilter( const char *teamFilter )
{
CRMInstance::SetTeamFilter(teamFilter);
if (mInstance)
{
mInstance->SetTeamFilter(teamFilter);
}
}
/************************************************************************************************
* CRMRandomInstance::PreSpawn
* Prepares for the spawn of the random instance
*
* inputs:
* landscape: landscape object this instance will be spawned on
*
* return:
* true: preparation successful
* false: preparation failed
*
************************************************************************************************/
bool CRMRandomInstance::PreSpawn ( CRandomTerrain* terrain, qboolean IsServer )
{
assert ( mInstance );
mInstance->SetFlattenHeight ( GetFlattenHeight( ) );
return mInstance->PreSpawn ( terrain, IsServer );
}
/************************************************************************************************
* CRMRandomInstance::Spawn
* Spawns the instance onto the landscape
*
* inputs:
* landscape: landscape object this instance will be spawned on
*
* return:
* true: spawn successful
* false: spawn failed
*
************************************************************************************************/
bool CRMRandomInstance::Spawn ( CRandomTerrain* terrain, qboolean IsServer )
{
mInstance->SetObjective(GetObjective());
mInstance->SetSide(GetSide());
if ( !mInstance->Spawn ( terrain, IsServer ) )
{
return false;
}
return true;
}
/************************************************************************************************
* CRMRandomInstance::SetArea
* Forwards the given area off to the internal instance
*
* inputs:
* area: area to be set
*
* return:
* none
*
************************************************************************************************/
void CRMRandomInstance::SetArea ( CRMAreaManager* amanager, CRMArea* area )
{
CRMInstance::SetArea ( amanager, area );
mInstance->SetArea ( amanager, mArea );
}

View File

@@ -0,0 +1,40 @@
#pragma once
#if !defined(RM_INSTANCE_RANDOM_H_INC)
#define RM_INSTANCE_RANDOM_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Instance_Random.h")
#endif
#define MAX_RANDOM_INSTANCES 64
class CRMRandomInstance : public CRMInstance
{
protected:
CRMInstance* mInstance;
public:
CRMRandomInstance ( CGPGroup* instGroup, CRMInstanceFile& instFile );
~CRMRandomInstance ( );
virtual bool IsValid ( ) { return mInstance==NULL?false:true; }
virtual int GetPreviewColor ( ) { return mInstance->GetPreviewColor ( ); }
virtual float GetSpacingRadius ( ) { return mInstance->GetSpacingRadius ( ); }
virtual int GetSpacingLine ( ) { return mInstance->GetSpacingLine ( ); }
virtual float GetFlattenRadius ( ) { return mInstance->GetFlattenRadius ( ); }
virtual bool GetLockOrigin ( ) { return mInstance->GetLockOrigin ( ); }
virtual void SetFilter ( const char *filter );
virtual void SetTeamFilter ( const char *teamFilter );
virtual void SetArea ( CRMAreaManager* amanager, CRMArea* area );
virtual void SetMirror (int mirror);
virtual bool PreSpawn ( CRandomTerrain* terrain, qboolean IsServer );
virtual bool Spawn ( CRandomTerrain* terrain, qboolean IsServer );
};
#endif

View File

@@ -0,0 +1,53 @@
/************************************************************************************************
*
* RM_Instance_Void.cpp
*
* Implements the CRMVoidInstance class. This class just adds a void into the
* area manager to help space things out.
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "rm_headers.h"
#include "rm_instance_void.h"
/************************************************************************************************
* CRMVoidInstance::CRMVoidInstance
* constructs a void instance
*
* inputs:
* instGroup: parser group containing infromation about this instance
* instFile: reference to an open instance file for creating sub instances
*
* return:
* none
*
************************************************************************************************/
CRMVoidInstance::CRMVoidInstance ( CGPGroup *instGroup, CRMInstanceFile& instFile )
: CRMInstance ( instGroup, instFile )
{
mSpacingRadius = atof( instGroup->FindPairValue ( "spacing", "0" ) );
mFlattenRadius = atof( instGroup->FindPairValue ( "flatten", "0" ) );
}
/************************************************************************************************
* CRMVoidInstance::SetArea
* Overidden to make sure the void area doesnt continually.
*
* inputs:
* area: area to set
*
* return:
* none
*
************************************************************************************************/
void CRMVoidInstance::SetArea ( CRMAreaManager* amanager, CRMArea* area )
{
// Disable collision
area->EnableCollision ( false );
// Do what really needs to get done
CRMInstance::SetArea ( amanager, area );
}

View File

@@ -0,0 +1,18 @@
#pragma once
#if !defined(RM_INSTANCE_VOID_H_INC)
#define RM_INSTANCE_VOID_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Instance_Void.h")
#endif
class CRMVoidInstance : public CRMInstance
{
public:
CRMVoidInstance ( CGPGroup* instGroup, CRMInstanceFile& instFile );
virtual void SetArea ( CRMAreaManager* amanager, CRMArea* area );
};
#endif

402
code/RMG/RM_Manager.cpp Normal file
View File

@@ -0,0 +1,402 @@
/************************************************************************************************
*
* RM_Manager.cpp
*
* Implements the CRMManager class. The CRMManager class manages the arioche system.
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "rm_headers.h"
#include "../server/server.h"
CRMObjective *CRMManager::mCurObjective=0;
/************************************************************************************************
* TheRandomMissionManager
* Pointer to only active CRMManager class
*
************************************************************************************************/
CRMManager *TheRandomMissionManager;
/************************************************************************************************
* CRMManager::CRMManager
* constructor
*
* inputs:
*
* return:
*
************************************************************************************************/
CRMManager::CRMManager(void)
{
mLandScape = NULL;
mTerrain = NULL;
mMission = NULL;
mCurPriority = 1;
mUseTimeLimit = false;
}
/************************************************************************************************
* CRMManager::~CRMManager
* destructor
*
* inputs:
*
* return:
*
************************************************************************************************/
CRMManager::~CRMManager(void)
{
#ifndef FINAL_BUILD
Com_Printf ("... Shutting down TheRandomMissionManager\n");
#endif
#ifndef DEDICATED
CM_TM_Free();
#endif
if (mMission)
{
delete mMission;
mMission = NULL;
}
}
/************************************************************************************************
* CRMManager::SetLandscape
* Sets the landscape and terrain object used to load a mission
*
* inputs:
* landscape - landscape object
*
* return:
* none
*
************************************************************************************************/
void CRMManager::SetLandScape(CCMLandScape *landscape)
{
mLandScape = landscape;
mTerrain = landscape->GetRandomTerrain();
}
/************************************************************************************************
* CRMManager::LoadMission
* Loads the mission using the mission name stored in the ar_mission cvar
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
bool CRMManager::LoadMission ( qboolean IsServer )
{
#ifndef PRE_RELEASE_DEMO
char instances[MAX_QPATH];
char mission[MAX_QPATH];
char course[MAX_QPATH];
char map[MAX_QPATH];
char temp[MAX_QPATH];
#ifndef FINAL_BUILD
Com_Printf ("--------- Random Mission Manager ---------\n\n");
Com_Printf ("RMG version : 0.01\n\n");
#endif
if (!mTerrain)
{
return false;
}
// Grab the arioche variables
Cvar_VariableStringBuffer("rmg_usetimelimit", temp, MAX_QPATH);
if (strcmpi(temp, "yes") == 0)
{
mUseTimeLimit = true;
}
Cvar_VariableStringBuffer("rmg_instances", instances, MAX_QPATH);
Cvar_VariableStringBuffer("RMG_mission", temp, MAX_QPATH);
Cvar_VariableStringBuffer("rmg_map", map, MAX_QPATH);
sprintf(mission, "%s_%s", temp, map);
Cvar_VariableStringBuffer("rmg_course", course, MAX_QPATH);
// dump existing mission, if any
if (mMission)
{
delete mMission;
mMission = NULL;
}
// Create a new mission file
mMission = new CRMMission ( mTerrain );
// Load the mission using the arioche variables
if ( !mMission->Load ( mission, instances, course ) )
{
return false;
}
if (mUseTimeLimit)
{
Cvar_Set("rmg_timelimit", va("%d", mMission->GetTimeLimit()));
}
else
{
Cvar_Set("rmg_timelimit", "0");
}
if (IsServer)
{ // set the names of the teams
CGenericParser2 parser;
//CGPGroup* root;
Cvar_VariableStringBuffer("RMG_terrain", temp, MAX_QPATH);
/*
// Create the parser for the mission file
if(Com_ParseTextFile(va("ext_data/rmg/%s.teams", temp), parser))
{
root = parser.GetBaseParseGroup()->GetSubGroups();
if (0 == stricmp(root->GetName(), "teams"))
{
SV_SetConfigstring( CS_GAMETYPE_REDTEAM, root->FindPairValue ( "red", "marine" ));
SV_SetConfigstring( CS_GAMETYPE_BLUETEAM, root->FindPairValue ( "blue", "thug" ));
}
parser.Clean();
}
*/
//rww - This is single player, no such thing.
}
// Must have a valid landscape before we can spawn the mission
assert ( mLandScape );
#ifndef FINAL_BUILD
Com_Printf ("------------------------------------------\n");
#endif
return true;
#else
return false;
#endif // PRE_RELEASE_DEMO
}
/************************************************************************************************
* CRMManager::IsMissionComplete
* Determines whether or not all the arioche objectives have been met
*
* inputs:
* none
*
* return:
* true: all objectives have been completed
* false: one or more of the objectives has not been met
*
************************************************************************************************/
bool CRMManager::IsMissionComplete(void)
{
if ( NULL == mMission->GetCurrentObjective ( ) )
{
return true;
}
return false;
}
/************************************************************************************************
* CRMManager::HasTimeExpired
* Determines whether or not the time limit (if one) has expired
*
* inputs:
* none
*
* return:
* true: time limit has expired
* false: time limit has not expired
*
************************************************************************************************/
bool CRMManager::HasTimeExpired(void)
{
/* if (mMission->GetTimeLimit() == 0 || !mUseTimeLimit)
{ // no time limit set
return false;
}
if (mMission->GetTimeLimit() * 1000 * 60 > level.time - level.startTime)
{ // we are still under our time limit
return false;
}
// over our time limit!
return true;*/
return false;
}
/************************************************************************************************
* CRMManager::UpdateStatisticCvars
* Updates the statistic cvars with data from the game
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
void CRMManager::UpdateStatisticCvars ( void )
{
/* // No player set then nothing more to do
if ( mPlayer )
{
float accuracy;
// Calculate the accuracy
accuracy = (float)mPlayer->client->ps.persistant[PERS_SHOTS_HIT];
accuracy /= (float)mPlayer->client->ps.persistant[PERS_SHOTS];
accuracy *= 100.0f;
// set the accuracy cvar
gi.Cvar_Set ( "ar_pl_accuracy", va("%d%%",(int)accuracy) );
// Set the # of kills cvar
gi.Cvar_Set ( "ar_kills", va("%d", mPlayer->client->ps.persistant[PERS_SCORE] ) );
int hours;
int mins;
int seconds;
int tens;
int millisec = (level.time - level.startTime);
seconds = millisec / 1000;
hours = seconds / (60 * 60);
seconds -= (hours * 60 * 60);
mins = seconds / 60;
seconds -= mins * 60;
tens = seconds / 10;
seconds -= tens * 10;
gi.Cvar_Set ( "ar_duration", va("%dhr %dmin %dsec", hours, mins, seconds ) );
WpnID wpnID = TheWpnSysMgr().GetFavoriteWeapon ( );
gi.Cvar_Set ( "ar_fav_wp", CWeaponSystem::GetWpnName ( wpnID ) );
// show difficulty
char difficulty[MAX_QPATH];
gi.Cvar_VariableStringBuffer("g_skill", difficulty, MAX_QPATH);
strupr(difficulty);
gi.Cvar_Set ( "ar_diff", va("&GENERIC_%s&",difficulty) );
// compute rank
float compositeRank = 1;
int rankMax = 3; // max rank less 1
float timeRank = mUseTimeLimit ? (1.0f - (mins / (float)mMission->GetTimeLimit())) : 0;
float killRank = mPlayer->client->ps.persistant[PERS_SCORE] / (float)GetCharacterManager().GetAllSize();
killRank = (killRank > 0) ? killRank : 1.0f;
float accuRank = (accuracy > 0) ? accuracy*0.01f : 1.0f;
float weapRank = 1.0f - CWeaponSystem::GetRank(wpnID);
compositeRank = ((timeRank + killRank + accuRank + weapRank) / 3.0f) * rankMax + 1;
if (compositeRank > 4)
compositeRank = 4;
gi.Cvar_Set ( "ar_rank", va("&RMG_RANK%d&",((int)compositeRank)) );
}*/
}
/************************************************************************************************
* CRMManager::CompleteMission
* Does end-of-mission stuff (pause game, end screen, return to menu)
* <Description> *
* Input *
* <Variable>: <Description> *
* Output / Return *
* <Variable>: <Description> *
************************************************************************************************/
void CRMManager::CompleteMission(void)
{
UpdateStatisticCvars ( );
mMission->CompleteMission();
}
/************************************************************************************************
* CRMManager::FailedMission
* Does end-of-mission stuff (pause game, end screen, return to menu)
* <Description> *
* Input *
* TimeExpired: indicates if the reason failed was because of time
* Output / Return *
* <Variable>: <Description> *
************************************************************************************************/
void CRMManager::FailedMission(bool TimeExpired)
{
UpdateStatisticCvars ( );
mMission->FailedMission(TimeExpired);
}
/************************************************************************************************
* CRMManager::CompleteObjective
* Marks the given objective as completed
*
* inputs:
* obj: objective to set as completed
*
* return:
* none
*
************************************************************************************************/
void CRMManager::CompleteObjective ( CRMObjective *obj )
{
assert ( obj );
mMission->CompleteObjective ( obj );
}
/************************************************************************************************
* CRMManager::Preview
* previews the random mission genration information
*
* inputs:
* from: origin being previed from
*
* return:
* none
*
************************************************************************************************/
void CRMManager::Preview ( const vec3_t from )
{
// Dont bother if we havent reached our timer yet
/* if ( level.time < mPreviewTimer )
{
return;
}
// Let the mission do all the previewing
mMission->Preview ( from );
// Another second
mPreviewTimer = level.time + 1000;*/
}
/************************************************************************************************
* CRMManager::Preview
* previews the random mission genration information
*
* inputs:
* from: origin being previed from
*
* return:
* none
*
************************************************************************************************/
bool CRMManager::SpawnMission ( qboolean IsServer )
{
// Spawn the mission
mMission->Spawn ( mTerrain, IsServer );
return true;
}

55
code/RMG/RM_Manager.h Normal file
View File

@@ -0,0 +1,55 @@
#pragma once
#if !defined(RM_MANAGER_H_INC)
#define RM_MANAGER_H_INC
#if !defined(CM_LANDSCAPE_H_INC)
#include "../qcommon/cm_landscape.h"
#endif
class CRMManager
{
private:
CRMMission* mMission;
CCMLandScape* mLandScape;
CRandomTerrain* mTerrain;
int mPreviewTimer;
int mCurPriority;
bool mUseTimeLimit;
void UpdateStatisticCvars ( void );
public:
// Constructors
CRMManager (void);
~CRMManager (void);
bool LoadMission ( qboolean IsServer );
bool SpawnMission ( qboolean IsServer );
// Accessors
void SetLandScape (CCMLandScape *landscape);
void SetCurPriority (int priority) { mCurPriority = priority; }
CRandomTerrain* GetTerrain (void) { return mTerrain; }
CCMLandScape* GetLandScape (void) { return mLandScape; }
CRMMission* GetMission (void) { return mMission; }
int GetCurPriority (void) { return mCurPriority; }
void Preview ( const vec3_t from );
bool IsMissionComplete (void);
bool HasTimeExpired (void);
void CompleteObjective ( CRMObjective *obj );
void CompleteMission (void);
void FailedMission (bool TimeExpired);
// eek
static CRMObjective *mCurObjective;
};
extern CRMManager* TheRandomMissionManager;
#endif // RANDOMMISSION_H_INC

1930
code/RMG/RM_Mission.cpp Normal file

File diff suppressed because it is too large Load Diff

129
code/RMG/RM_Mission.h Normal file
View File

@@ -0,0 +1,129 @@
#pragma once
#if !defined(RM_MISSION_H_INC)
#define RM_MISSION_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Mission.h")
#endif
// maximum random choices
#define MAX_RANDOM_CHOICES 100
typedef vector<int> rmIntVector_t;
class CRMMission
{
private:
rmObjectiveList_t mObjectives;
rmInstanceList_t mInstances;
CRMInstanceFile mInstanceFile;
CRMObjective* mCurrentObjective;
bool mValidNodes;
bool mValidPaths;
bool mValidRivers;
bool mValidWeapons;
bool mValidAmmo;
bool mValidObjectives;
bool mValidInstances;
int mTimeLimit;
int mMaxInstancePosition;
// npc multipliers
float mAccuracyMultiplier;
float mHealthMultiplier;
// % chance that RMG pickup is actually spawned
float mPickupHealth;
float mPickupArmor;
float mPickupAmmo;
float mPickupWeapon;
float mPickupEquipment;
string mDescription;
string mExitScreen;
string mTimeExpiredScreen;
// symmetric landscape style
symmetry_t mSymmetric;
// if set to 1 in the mission file, adds an extra connecting path in symmetric maps
// to ensure both sides actually do connect
int mBackUpPath;
int mDefaultPadding;
CRMAreaManager* mAreaManager;
CRMPathManager* mPathManager;
CRandomTerrain* mLandScape;
public:
CRMMission ( CRandomTerrain* );
~CRMMission ( );
bool Load ( const char* name, const char* instances, const char* difficulty );
bool Spawn ( CRandomTerrain* terrain, qboolean IsServer );
void Preview ( const vec3_t from );
CRMObjective* FindObjective ( const char* name );
CRMObjective* GetCurrentObjective ( ) { return mCurrentObjective; }
void CompleteMission (void);
void FailedMission (bool TimeExpired);
void CompleteObjective ( CRMObjective* ojective );
int GetTimeLimit (void) { return mTimeLimit; }
int GetMaxInstancePosition (void) { return mMaxInstancePosition; }
const char* GetDescription (void) { return mDescription.c_str(); }
const char* GetExitScreen (void) { return mExitScreen.c_str(); }
int GetSymmetric (void) { return mSymmetric; }
int GetBackUpPath (void) { return mBackUpPath; }
int GetDefaultPadding (void) { return mDefaultPadding; }
// void CreateMap ( void );
bool DenyPickupHealth () {return mLandScape->flrand(0.0f,1.0f) > mPickupHealth;}
bool DenyPickupArmor () {return mLandScape->flrand(0.0f,1.0f) > mPickupArmor;}
bool DenyPickupAmmo () {return mLandScape->flrand(0.0f,1.0f) > mPickupAmmo;}
bool DenyPickupWeapon () {return mLandScape->flrand(0.0f,1.0f) > mPickupWeapon;}
bool DenyPickupEquipment () {return mLandScape->flrand(0.0f,1.0f) > mPickupEquipment;}
private:
// void PurgeUnlinkedTriggers ( );
// void PurgeTrigger ( CEntity* trigger );
void MirrorPos (vec3_t pos);
CGPGroup* ParseRandom ( CGPGroup* random );
bool ParseOrigin ( CGPGroup* originGroup, vec3_t origin, vec3_t lookat, int* flattenHeight );
bool ParseNodes ( CGPGroup* group );
bool ParsePaths ( CGPGroup *paths);
bool ParseRivers ( CGPGroup *rivers);
void PlaceBridges ();
void PlaceWallInstance(CRMInstance* instance, float xpos, float ypos, float zpos, int x, int y, float angle);
bool ParseDifficulty ( CGPGroup* difficulty, CGPGroup *parent );
bool ParseWeapons ( CGPGroup* weapons );
bool ParseAmmo ( CGPGroup* ammo );
bool ParseOutfit ( CGPGroup* outfit );
bool ParseObjectives ( CGPGroup* objectives );
bool ParseInstance ( CGPGroup* instance );
bool ParseInstances ( CGPGroup* instances );
bool ParseInstancesOnPath ( CGPGroup* group );
bool ParseWallRect ( CGPGroup* group, int side);
// void SpawnNPCTriggers ( CCMLandScape* landscape );
// void AttachNPCTriggers ( CCMLandScape* landscape );
};
#endif

134
code/RMG/RM_Objective.cpp Normal file
View File

@@ -0,0 +1,134 @@
/************************************************************************************************
*
* RM_Objective.cpp
*
* Implements the CRMObjective class. This class is reponsible for parsing an objective
* from the mission file as well as linking the objective into the world.
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "rm_headers.h"
/************************************************************************************************
* CRMObjective::CRMObjective
* Constructs a random mission objective and fills in the default properties
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMObjective::CRMObjective ( CGPGroup* group )
{
SetPriority(atoi(group->FindPairValue("priority", "0")));
SetMessage( group->FindPairValue("message",va("Objective %i Completed", GetPriority()) ) );
SetDescription(group->FindPairValue("description",va("Objective %i", GetPriority()) ) );
SetInfo(group->FindPairValue("info",va("Info %i", GetPriority()) ) );
SetTrigger(group->FindPairValue("trigger",""));
SetName(group->GetName());
/* const char * soundPath = group->FindPairValue("completed_sound", "" );
if (soundPath)
mCompleteSoundID = G_SoundIndex(soundPath);
*/
mCompleted = false;
mOrderIndex = -1;
// If no priority was specified for this objective then its active by default.
if ( GetPriority ( ) )
{
mActive = false;
}
else
{
mActive = true;
}
}
/************************************************************************************************
* CRMObjective::FindRandomTrigger
* Searches the entitySystem form a random arioche trigger that matches the objective name
*
* inputs:
* none
*
* return:
* trigger: a random trigger or NULL if one couldnt be found
*
************************************************************************************************/
/*CTriggerAriocheObjective* CRMObjective::FindRandomTrigger ( )
{
CEntity* search;
CEntity* triggers[20];
int numTriggers;
// Start off the first trigger
numTriggers = 0;
search = entitySystem->GetEntityFromClassname ( NULL, "trigger_arioche_objective" );
// Make a list of triggers
while ( numTriggers < 20 && search )
{
CTriggerAriocheObjective* trigger = (CTriggerAriocheObjective*) search;
// Move on to the next trigger
search = entitySystem->GetEntityFromClassname ( search, "trigger_arioche_objective" );
// See if this trigger is already in use
if ( trigger->GetObjective ( ) )
{
continue;
}
// If the objective names dont match then ignore this trigger
if ( stricmp ( trigger->GetObjectiveName ( ), GetTrigger() ) )
{
continue;
}
// Add the trigger to the list
triggers[numTriggers++] = trigger;
}
// If no matching triggers then just return NULL
if ( 0 == numTriggers )
{
return NULL;
}
// Return a random choice from the trigger list
return (CTriggerAriocheObjective*)triggers[TheRandomMissionManager->GetLandScape()->irand(0,numTriggers-1)];
}
*/
/************************************************************************************************
* CRMObjective::Link
* Links the objective into the world using the current state of the world to determine
* where it should link
*
* inputs:
* none
*
* return:
* true: objective successfully linked
* false: objective failed to link
*
************************************************************************************************/
bool CRMObjective::Link ( )
{
/* CTriggerAriocheObjective* trigger;
// Look for a random trigger to associate this objective to.
trigger = FindRandomTrigger ( );
if ( NULL != trigger )
{
trigger->SetObjective ( this );
}
*/
return true;
}

65
code/RMG/RM_Objective.h Normal file
View File

@@ -0,0 +1,65 @@
#pragma once
#if !defined(RM_OBJECTIVE_H_INC)
#define RM_OBJECTIVE_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Objective.h")
#endif
class CRMObjective
{
protected:
bool mCompleted; // Is objective completed?
bool mActive; // set to false if the objective requires another objective to be met first
int mPriority; // sequence in which objectives need to be completed
int mOrderIndex; // objective index in ui
int mCompleteSoundID; // sound for when objective is finished
string mMessage; // message outputed when objective is completed
string mDescription; // description of objective
string mInfo; // more info for objective
string mName; // name of objective
string mTrigger; // trigger associated with objective
public:
CRMObjective(CGPGroup *group);
~CRMObjective(void) {}
bool Link (void);
bool IsCompleted (void) const { return mCompleted; }
bool IsActive (void) const { return mActive; }
void Activate (void) { mActive = true; }
void Complete (bool comp) { mCompleted = comp;}
// Get methods
int GetPriority(void){return mPriority;}
int GetOrderIndex(void) { return mOrderIndex; }
const char* GetMessage(void) { return mMessage.c_str(); }
const char* GetDescription(void) { return mDescription.c_str(); }
const char* GetInfo(void) { return mInfo.c_str(); }
const char* GetName(void) { return mName.c_str(); }
const char* GetTrigger(void) { return mTrigger.c_str(); }
int CompleteSoundID() { return mCompleteSoundID; };
// Set methods
void SetPriority(int priority){mPriority = priority;}
void SetOrderIndex(int order) { mOrderIndex = order; }
void SetMessage(const char* msg) { mMessage = msg; }
void SetDescription(const char* desc) { mDescription = desc; }
void SetInfo(const char* info) { mInfo = info; }
void SetName(const char* name) { mName = name; }
void SetTrigger(const char* name) { mTrigger = name; }
private:
// CTriggerAriocheObjective* FindRandomTrigger ( );
};
typedef list<CRMObjective *>::iterator rmObjectiveIter_t;
typedef list<CRMObjective *> rmObjectiveList_t;
#endif

721
code/RMG/RM_Path.cpp Normal file
View File

@@ -0,0 +1,721 @@
/************************************************************************************************
*
* Copyright (C) 2001-2002 Raven Software
*
* RM_Path.cpp
*
************************************************************************************************/
#include "../server/exe_headers.h"
#include "rm_headers.h"
#define max(a,b) (((a) > (b)) ? (a) : (b))
/************************************************************************************************
* CRMNode::CRMNode
* constructor
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMNode::CRMNode ( )
{
int i;
mFlattenHeight = -1;
mPos[0] = 0;
mPos[1] = 0;
mPos[2] = 0;
// no paths
for (i = 0; i < DIR_MAX; i++)
mPathID[i] = -1;
mAreaPointPlaced = false;
}
/************************************************************************************************
* CRMPathManager::CRMPathManager
* constructor
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
CRMPathManager::CRMPathManager ( CRandomTerrain* terrain )
: mXNodes(0), mYNodes(0), mPathCount(0), mRiverCount(0), mMaxDepth(0), mDepth(0),
mPathPoints(10), mPathMinWidth(0.02f), mPathMaxWidth(0.04f), mPathDepth(0.3f), mPathDeviation(0.03f), mPathBreadth(5),
mRiverDepth(5), mRiverPoints(10), mRiverMinWidth(0.01f), mRiverMaxWidth(0.02f), mRiverBedDepth(1), mRiverDeviation(0.01f), mRiverBreadth(7),
mTerrain(terrain), mCrossed(false)
{
}
CRMPathManager::~CRMPathManager ( )
{
int i,j;
for ( i = mLocations.size() - 1; i >=0; i-- )
{
if (mLocations[i])
delete mLocations[i];
}
mLocations.clear();
for ( j = mNodes.size() - 1; j >=0; j-- )
{
if (mNodes[j])
delete mNodes[j];
}
mNodes.clear();
}
void CRMPathManager::CreateLocation ( const char* name, const int min_depth, int max_depth, const int min_paths, int max_paths )
{
int i;
// sanity checks -- dmv
if( max_paths < min_paths )
{
Com_Printf("[CreateLocation()] ERROR : max_paths < min_paths :: set max_paths = min_paths\n" );
max_paths = min_paths;
}
if( max_depth < min_depth )
{
Com_Printf("[CreateLocation()] ERROR : max_depth < min_depth :: set max_depth = min_depth\n" );
max_depth = min_depth;
}
for (i = mLocations.size()-1; i>=0; --i)
if ( !stricmp ( name, mLocations[i]->GetName ( ) ) )
{
mLocations[i]->SetMinDepth(min_depth);
mLocations[i]->SetMaxDepth(max_depth);
mLocations[i]->SetMinPaths(min_paths);
mLocations[i]->SetMaxPaths(max_paths);
return;
}
CRMLoc* pLoc= new CRMLoc(name, min_depth, max_depth, min_paths, max_paths);
mLocations.push_back(pLoc);
mMaxDepth = max(mMaxDepth, max_depth);
}
void CRMPathManager::ClearCells(int x_nodes, int y_nodes)
{
int x,y;
// clear cell array - used for generating paths
CRMCell empty;
for (x=0; x < x_nodes * y_nodes; x++)
{
if (x >= mCells.size())
mCells.push_back(empty);
else
mCells[x] = empty;
}
// set borders of world
for (y = 0; y < y_nodes; y++)
{
mCells[y * x_nodes].SetBorder(DIR_W );
mCells[y * x_nodes].SetBorder(DIR_SW );
mCells[y * x_nodes].SetBorder(DIR_NW );
mCells[y * x_nodes + x_nodes-1].SetBorder( DIR_E );
mCells[y * x_nodes + x_nodes-1].SetBorder( DIR_NE );
mCells[y * x_nodes + x_nodes-1].SetBorder( DIR_SE );
}
for (x = 0; x < x_nodes; x++)
{
mCells[x].SetBorder( DIR_N );
mCells[x].SetBorder( DIR_NE );
mCells[x].SetBorder( DIR_NW );
mCells[(y_nodes-1) * x_nodes + x].SetBorder( DIR_S );
mCells[(y_nodes-1) * x_nodes + x].SetBorder( DIR_SE );
mCells[(y_nodes-1) * x_nodes + x].SetBorder( DIR_SW );
}
}
/************************************************************************************************
* CRMPathManager::CreateArray
* Create array of nodes that are spaced over the landscape.
* Create array of cells, which is used to determine how nodes are connected.
*
* inputs:
* x_nodes, y_nodes - how many nodes in each dimension to layout
*
* return:
* true if the node array was created, false if we have a problem
*
************************************************************************************************/
bool CRMPathManager::CreateArray(const int x_nodes, const int y_nodes)
{
mXNodes = x_nodes;
mYNodes = y_nodes;
// fill node array with positions that are spaced over the landscape
int x,y;
// dump existing nodes
for ( x = mNodes.size() - 1; x >=0; x-- )
{
if (mNodes[x])
delete mNodes[x];
}
mNodes.clear();
mNodes.resize(mXNodes * mYNodes, NULL);
// add a small amount of random jitter to spots chosen
float x_rnd = 0.2f / (mXNodes+1);
float y_rnd = 0.2f / (mYNodes+1);
for (x = 0; x < mXNodes; x++)
{
float cell_x = (x + 1.0f) / (mXNodes+1);
// float cell_x = (x + 2.0f) / (mXNodes+3);
for (y = 0; y < mYNodes; y++)
{
vec3_t pos;
CRMNode * pnode = new CRMNode();
mNodes[x + y*mXNodes] = pnode;
float cell_y = (y + 1.0f) / (mYNodes+1);
// float cell_y = (y + 2.0f) / (mYNodes+3);
pos[0] = TheRandomMissionManager->GetLandScape()->flrand(cell_x - x_rnd, cell_x + x_rnd);
pos[1] = TheRandomMissionManager->GetLandScape()->flrand(cell_y - y_rnd, cell_y + y_rnd);
pos[2] = 0;
SetNodePos(x, y, pos);
}
}
ClearCells(mXNodes, mYNodes);
return true;
}
// neighbor offsets - easy way to turn a direction into the array position for a neighboring cell or node
int CRMPathManager::neighbor_x[DIR_MAX] = { 0, 1, 1, 1, 0,-1,-1,-1};
int CRMPathManager::neighbor_y[DIR_MAX] = {-1,-1, 0, 1, 1, 1, 0,-1};
/************************************************************************************************
* CRMPathManager::PlaceLocation
* This method is used to determine if a named location should be placed at this node.
*
* inputs:
* c_x, c_y - cell to examine
*
* return:
* none
*
************************************************************************************************/
void CRMPathManager::PlaceLocation(const int c_x, const int c_y)
{
if ( !Node(c_x,c_y)->IsLocation() )
{ // not currently a location
// how many paths lead to this cell?
int count_paths = 0;
int i;
for (i = 0; i<DIR_MAX; i++)
if (Node(c_x,c_y)->PathExist(i))
count_paths++;
int deepest_depth = -1;
int deepest_loc = -1;
for (i = mLocations.size()-1; i>=0; --i)
{
if (!mLocations[i]->Placed() && // node has not been placed
mLocations[i]->MinDepth() <= mDepth && // our current depth is in the proper range
mLocations[i]->MaxDepth() >= mDepth &&
mLocations[i]->MinPaths() <= count_paths && // our path count is in the proper range
mLocations[i]->MaxPaths() >= count_paths &&
mLocations[i]->MaxDepth() > deepest_depth) // and this is the deepest location of the ones that match
{
deepest_loc = i;
deepest_depth = mLocations[i]->MaxDepth();
}
}
if (deepest_loc >= 0 && deepest_loc < mLocations.size())
{ // found a location to place at this node / cell
const char * name = mLocations[deepest_loc]->GetName();
Node(c_x,c_y)->SetName(name);
mLocations[deepest_loc]->SetPlaced(true);
// need a new max depth
int max_depth = -1;
for (i = mLocations.size()-1; i>=0; --i)
{
// figure out new max depth based on the max depth of unplaced locations
if (!mLocations[i]->Placed() && // node has not been placed
mLocations[i]->MaxDepth() > max_depth) // and this is the deepest
{
max_depth = mLocations[i]->MaxDepth();
}
}
mMaxDepth = max_depth;
}
}
}
/************************************************************************************************
* CRMPathManager::PathVisit
* This method is called recursively to create a network of nodes connected with paths.
*
* inputs:
* c_x, c_y - cell to visit
*
* return:
* none
*
************************************************************************************************/
void CRMPathManager::PathVisit(const int c_x, const int c_y)
{
// does this cell have any neighbors with all walls intact?
int i,off;
// look at neighbors in random order
off = TheRandomMissionManager->GetLandScape()->irand(DIR_FIRST, DIR_MAX-1);
++mDepth; // track our depth of recursion
for (i = DIR_FIRST; i<DIR_MAX && mDepth <= mMaxDepth; i++)
{
int d = (i + off) % DIR_MAX;
if ( !Cell(c_x, c_y).Border(d) )
{ // we can move this way, since no border
int new_c_x = c_x + neighbor_x[d];
int new_c_y = c_y + neighbor_y[d];
if (Cell(new_c_x,new_c_y).Wall() == DIR_ALL)
{ // we have a new cell that has not been visited!
int new_dir;
// d is the direction relative to the current cell
// new_dir is the direction relative to the next cell (N becomes S, NE becomes SW, etc...)
if( d < HALF_DIR_MAX )
{
new_dir = d + HALF_DIR_MAX;
}
else
{
new_dir = d - HALF_DIR_MAX;
}
// knock down walls
Cell(c_x,c_y).RemoveWall(d);
Cell(new_c_x,new_c_y).RemoveWall(new_dir); //DIR_MAX - d);
// set path id
Node(c_x, c_y)->SetPath(d, mPathCount);
Node(new_c_x, new_c_y)->SetPath(new_dir, mPathCount); //DIR_MAX - d, mPathCount);
// create path between cells
mTerrain->CreatePath( mPathCount++,
-1,
0,
mPathPoints,
GetNodePos(c_x,c_y)[0],
GetNodePos(c_x,c_y)[1],
GetNodePos(new_c_x,new_c_y)[0],
GetNodePos(new_c_x,new_c_y)[1],
mPathMinWidth,
mPathMaxWidth,
mPathDepth,
mPathDeviation,
mPathBreadth );
// flatten a small spot
CArea area;
float flat_radius = mPathMaxWidth *
fabs(TheRandomMissionManager->GetLandScape()->GetBounds()[1][0] - TheRandomMissionManager->GetLandScape()->GetBounds()[0][0]);
area.Init( GetNodePos(c_x,c_y), flat_radius, 0.0f, AT_NONE, 0, 0 );
TheRandomMissionManager->GetLandScape()->FlattenArea(&area, 255 * mPathDepth, false, true, true );
// recurse
PathVisit(new_c_x, new_c_y);
}
}
}
--mDepth;
// NOTE: *whoop* hack alert, the first time this is reached, it should be the very last placed node.
if( !mCrossed && TheRandomMissionManager->GetMission()->GetSymmetric() &&
TheRandomMissionManager->GetMission()->GetBackUpPath() )
{
mCrossed = true;
int directionSet[3][3] = {DIR_NW,DIR_W,DIR_SW,DIR_N,-1,DIR_S,DIR_NE,DIR_E,DIR_SE};
int ncx = (mXNodes-1)-c_x;
int ncy = (mYNodes-1)-c_y;
int x_delta = ncx - c_x;
int y_delta = ncy - c_y;
if( x_delta < -1 )
{
x_delta = -1;
}
else if( x_delta > 1 )
{
x_delta = 1;
}
if( y_delta < -1 )
{
y_delta = -1;
}
else if( y_delta > 1 )
{
y_delta = 1;
}
// make sure the mirror is actually in a different position than then un-mirrored node
if( x_delta || y_delta )
{
int d = directionSet[x_delta][y_delta];
int new_dir;
// d is the direction relative to the current cell
// new_dir is the direction relative to the next cell (N becomes S, NE becomes SW, etc...)
if( d < HALF_DIR_MAX )
{
new_dir = d + HALF_DIR_MAX;
}
else
{
new_dir = d - HALF_DIR_MAX;
}
//NOTE: Knocking down these walls will cause instances to be created on this new artificial path
// Since this path could span more than just the normal 1 cell, these walls being knocked down are not exactly correct... but get the job done
// knock down walls
Cell(c_x,c_y).RemoveWall(d);
Cell(ncx,ncy).RemoveWall(new_dir); //DIR_MAX - d);
// set path id
Node(c_x, c_y)->SetPath(d, mPathCount);
Node(ncx, ncy)->SetPath(new_dir, mPathCount); //DIR_MAX - d, mPathCount);
// create an artificial path that crosses over to connect the symmetric and non-symmetric map parts
mTerrain->CreatePath( mPathCount++,
-1,
0,
mPathPoints,
GetNodePos(c_x,c_y)[0],
GetNodePos(c_x,c_y)[1],
GetNodePos(ncx,ncy)[0],
GetNodePos(ncx,ncy)[1],
mPathMinWidth,
mPathMaxWidth,
mPathDepth,
mPathDeviation,
mPathBreadth );
}
}
PlaceLocation(c_x, c_y);
}
/************************************************************************************************
* CRMPathManager::FindNodeByName
* Finds the managed node with the matching case-insensivity name
*
* inputs:
* name - name of the node to find
*
* return:
* a pointer to the found node or NULL if the node couldn't be found
*
************************************************************************************************/
CRMNode* CRMPathManager::FindNodeByName ( const char* name )
{
int j;
for ( j = mNodes.size() - 1; j >=0; j-- )
{
if ( !stricmp ( name, mNodes[j]->GetName ( ) ) )
return mNodes[j];
}
return NULL;
}
/************************************************************************************************
* CRMPathManager::SetPathStyle
* sets style for all paths
*
* inputs:
* settings for paths that are created
*
* return:
* none
*
************************************************************************************************/
void CRMPathManager::SetPathStyle (
const int points,
const float minwidth,
const float maxwidth,
const float depth,
const float deviation,
const float breadth
)
{
// save path style
mPathPoints = points ;
mPathMinWidth = minwidth;
mPathMaxWidth = maxwidth;
mPathDepth = depth ;
mPathDeviation= deviation;
mPathBreadth = breadth ;
}
/************************************************************************************************
* CRMPathManager::SetRiverStyle
* sets style for all rivers
*
* inputs:
* settings for river paths that are created
*
* return:
* none
*
************************************************************************************************/
void CRMPathManager::SetRiverStyle (const int depth,
const int points,
const float minwidth,
const float maxwidth,
const float beddepth,
const float deviation,
const float breadth,
string bridge_name)
{
// save river style
mRiverDepth = depth;
mRiverPoints = points ;
mRiverMinWidth = minwidth;
mRiverMaxWidth = maxwidth;
mRiverBedDepth = beddepth ;
mRiverDeviation= deviation;
mRiverBreadth = breadth ;
mRiverBridge = bridge_name;
}
vec3_t& CRMPathManager::GetRiverPos( const int x, const int y )
{
mRiverPos[0] = (float)(x + 1.0f) / (float)(mXNodes+2);
mRiverPos[1] = (float)(y + 1.0f) / (float)(mYNodes+2);
return mRiverPos;
}
void CRMPathManager::RiverVisit(const int c_x, const int c_y)
{
// does this cell have any neighbors with all walls intact?
int i,off;
// look at neighbors in random order
off = TheRandomMissionManager->GetLandScape()->irand(DIR_FIRST, DIR_MAX-1);
++mDepth; // track our depth of recursion
for (i = DIR_FIRST; i<DIR_MAX && mDepth <= mMaxDepth; i+=2)
{
int d = (i + off) % DIR_MAX;
if ( !Cell(c_x, c_y).Border(d) )
{ // we can move this way, since no border
int new_c_x = c_x + neighbor_x[d];
int new_c_y = c_y + neighbor_y[d];
if (RiverCell(new_c_x,new_c_y).Wall() == DIR_ALL)
{ // we have a new cell that has not been visited!
int new_dir;
// d is the direction relative to the current cell
// new_dir is the direction relative to the next cell (N becomes S, NE becomes SW, etc...)
if( d < HALF_DIR_MAX )
{
new_dir = d + HALF_DIR_MAX;
}
else
{
new_dir = d - HALF_DIR_MAX;
}
// knock down walls
RiverCell(c_x,c_y).RemoveWall(d);
RiverCell(new_c_x,new_c_y).RemoveWall(new_dir); //DIR_MAX - d);
// create river between cells
mTerrain->CreatePath ( mPathCount++,
-1,
0,
mRiverPoints,
GetRiverPos(c_x,c_y)[0],
GetRiverPos(c_x,c_y)[1],
GetRiverPos(new_c_x,new_c_y)[0],
GetRiverPos(new_c_x,new_c_y)[1],
mRiverMinWidth,
mRiverMaxWidth,
mRiverBedDepth,
mRiverDeviation,
mRiverBreadth );
// flatten a small spot
CArea area;
float flat_radius = mRiverMinWidth *
fabs(TheRandomMissionManager->GetLandScape()->GetBounds()[1][0] - TheRandomMissionManager->GetLandScape()->GetBounds()[0][0]);
area.Init( GetRiverPos(c_x,c_y), flat_radius, 0.0f, AT_NONE, 0, 0 );
TheRandomMissionManager->GetLandScape()->FlattenArea (&area, 255 * mRiverBedDepth, false, true, true );
// recurse
RiverVisit(new_c_x, new_c_y);
}
}
}
// --mDepth;
}
/************************************************************************************************
* CRMPathManager::GenerateRivers
* Creates a river which intersects the main path
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
void CRMPathManager::GenerateRivers ()
{
if (mRiverBedDepth == 1)
// no rivers
return;
mMaxDepth = mRiverDepth;
mDepth = 0;
int cell_x = 0;
int cell_y = 0;
// choose starting cell along an edge
int edge = TheRandomMissionManager->GetLandScape()->irand(0, 7);
switch ( edge )
{
case 0:
cell_x = mXNodes / 2; cell_y = 0;
break;
case 1:
cell_x = mXNodes; cell_y = 0;
break;
case 2:
cell_x = mXNodes; cell_y = mYNodes / 2;
break;
case 3:
cell_x = mXNodes; cell_y = mYNodes;
break;
case 4:
cell_x = mXNodes / 2; cell_y = mYNodes;
break;
case 5:
cell_x = 0; cell_y = mYNodes;
break;
case 6:
cell_x = 0; cell_y = mYNodes / 2;
break;
case 7:
cell_x = 0; cell_y = 0;
break;
}
ClearCells(mXNodes+1, mYNodes+1);
mRiverCount = mPathCount;
// visit the first cell
RiverVisit(cell_x,cell_y);
mRiverCount = mPathCount - mRiverCount;
return;
}
/************************************************************************************************
* CRMPathManager::GeneratePaths
* Creates all paths
*
* inputs:
* none
*
* return:
* none
*
************************************************************************************************/
void CRMPathManager::GeneratePaths ( symmetry_t symmetric )
{
int cell_x = 0;
int cell_y = 0;
switch ( symmetric )
{
case SYMMETRY_TOPLEFT:
cell_x = mXNodes-1;
cell_y = 0;
break;
case SYMMETRY_BOTTOMRIGHT:
cell_x = 0;
cell_y = mYNodes-1;
break;
default:
case SYMMETRY_NONE:
// choose starting cell along an edge
switch ( TheRandomMissionManager->GetLandScape()->irand(0, 7) )
{
case 0:
cell_x = mXNodes / 2;
break;
case 1:
cell_x = mXNodes-1;
break;
case 2:
cell_x = mXNodes-1; cell_y = mYNodes / 2;
break;
case 3:
cell_x = mXNodes-1; cell_y = mYNodes-1;
break;
case 4:
cell_x = mXNodes / 2; cell_y = mYNodes-1;
break;
case 5:
cell_y = mYNodes-1;
break;
case 6:
cell_y = mYNodes / 2;
break;
default:
case 7:
break;
}
break;
}
// visit the first cell
PathVisit(cell_x,cell_y);
}

223
code/RMG/RM_Path.h Normal file
View File

@@ -0,0 +1,223 @@
/************************************************************************************************
*
* Copyright (C) 2001-2002 Raven Software
*
* RM_Path.h
*
************************************************************************************************/
#pragma once
#if !defined(RM_PATH_H_INC)
#define RM_PATH_H_INC
#ifdef DEBUG_LINKING
#pragma message("...including RM_Path.h")
#endif
#if !defined(CM_RANDOMTERRAIN_H_INC)
#include "../qcommon/cm_randomterrain.h"
#endif
class CRMPathManager;
// directions you can proceed from cells
enum ERMDir
{
DIR_FIRST= 0,
DIR_N = 0,
DIR_NE,
DIR_E ,
DIR_SE,
DIR_S ,
DIR_SW,
DIR_W ,
DIR_NW,
DIR_MAX,
DIR_ALL = 255
};
#define HALF_DIR_MAX (DIR_MAX/2)
class CRMNode
{
private:
string mName; // name of node - "" if not used yet
vec3_t mPos; // where node is
int mPathID[DIR_MAX]; // path id's that lead from this node
bool mAreaPointPlaced; // false if no area point here yet.
int mFlattenHeight;
public:
CRMNode ( );
bool IsLocation() {return strlen(mName.c_str())>0;};
const char* GetName ( ) { return mName.c_str(); }
vec3_t& GetPos ( ) { return mPos; }
const float PathExist( const int dir) { return (mPathID[dir % DIR_MAX] != -1); };
const float GetPath ( const int dir) { return mPathID[dir % DIR_MAX]; };
bool AreaPoint() {return mAreaPointPlaced;};
void SetName ( const char* name ) { mName = name; }
void SetPos ( const vec3_t& v ) { VectorCopy ( v, mPos ); }
void SetPath( const int dir, const int id) { mPathID[dir % DIR_MAX] = id; };
void SetAreaPoint(bool ap) {mAreaPointPlaced = ap;};
void SetFlattenHeight(int flattenHeight) {mFlattenHeight = flattenHeight; }
int GetFlattenHeight() {return mFlattenHeight; }
};
typedef vector<CRMNode*> rmNodeVector_t;
// named spots on the map, should be placed into nodes
class CRMLoc
{
private:
string mName; // name of location
int mMinDepth;
int mMaxDepth;
int mMinPaths;
int mMaxPaths;
bool mPlaced; // location has been placed at a node
public:
CRMLoc (const char *name, const int min_depth, const int max_depth, const int min_paths =1, const int max_paths=1 )
: mMinDepth(min_depth), mMaxDepth(max_depth), mPlaced(false), mMinPaths(min_paths), mMaxPaths(max_paths)
{ mName = name; };
const char* GetName ( ) { return mName.c_str(); }
void SetName ( const char* name ) { mName = name; }
int MinDepth() {return mMinDepth;};
void SetMinDepth(const int deep) {mMinDepth = deep;};
int MaxDepth() {return mMaxDepth;};
void SetMaxDepth(const int deep) {mMaxDepth = deep;};
int MinPaths() {return mMinPaths;};
void SetMinPaths(const int paths) {mMinPaths = paths;};
int MaxPaths() {return mMaxPaths;};
void SetMaxPaths(const int paths) {mMaxPaths = paths;};
bool Placed() { return mPlaced; };
void SetPlaced(bool p) { mPlaced = p;};
};
typedef vector<CRMLoc*> rmLocVector_t;
// cells are used for figuring out node connections / paths
struct CRMCell
{
private:
int border;
int wall;
public:
CRMCell() { border = 0; wall = DIR_ALL; };
int Border() {return border;};
int Wall() {return wall;};
bool Border(const int dir) { return (border & (1<<dir))!=0; };
bool Wall(const int dir) { return (wall & (1<<dir))!=0; };
void SetBorder(const int dir) { border |= (1<<dir); };
void SetWall(const int dir) { wall |= (1<<dir); };
void RemoveWall(const int dir) { wall &= ~(1<<dir); };
};
typedef vector<CRMCell> rmCellVector_t;
class CRMPathManager
{
public:
int mXNodes; // number of nodes in the x dimension
int mYNodes; // number of nodes in the y dimension
private:
rmLocVector_t mLocations; // location, named spots to be placed at nodes
rmNodeVector_t mNodes; // nodes, spots on map that *may* be connected by paths
rmCellVector_t mCells; // array of cells for doing path generation
int mPathCount;
int mRiverCount;
int mMaxDepth; // deepest any location wants to be
int mDepth; // current depth
bool mCrossed; // used to indicate if paths crossed the imaginary diagonal that cuts symmetric maps in half
// path style
int mPathPoints;
float mPathMinWidth;
float mPathMaxWidth;
float mPathDepth;
float mPathDeviation;
float mPathBreadth;
// river style
int mRiverDepth;
int mRiverPoints;
float mRiverMinWidth;
float mRiverMaxWidth;
float mRiverBedDepth;
float mRiverDeviation;
float mRiverBreadth;
string mRiverBridge;
vec3_t mRiverPos;
static int neighbor_x[DIR_MAX];
static int neighbor_y[DIR_MAX];
CRandomTerrain* mTerrain;
public:
CRMPathManager ( CRandomTerrain* terrain );
~CRMPathManager ( );
void ClearCells (int x_nodes, int y_nodes);
bool CreateArray ( const int x_nodes, const int y_nodes );
CRMNode* FindNodeByName ( const char* name );
CRMNode* Node ( const int x, const int y ) {return mNodes[x + y*mXNodes];};
void CreateLocation ( const char* name, const int min_depth, int max_depth, const int min_paths =1, int max_paths =1 );
vec3_t& GetNodePos ( const int x, const int y ) { return mNodes[x + y*mXNodes]->GetPos(); };
void SetNodePos ( const int x, const int y, const vec3_t& pos) { mNodes[x + y*mXNodes]->SetPos(pos); };
int GetPathCount () {return mPathCount;};
int GetRiverCount () {return mRiverCount;};
float GetRiverDepth () {return mRiverBedDepth;};
float GetPathDepth () {return mPathDepth;};
const char *GetBridgeName () {return mRiverBridge.c_str();};
vec3_t& GetRiverPos ( const int x, const int y );
CRMCell& Cell ( const int x, const int y ) {return mCells[x + y*mXNodes];};
CRMCell& RiverCell ( const int x, const int y ) {return mCells[x + y*(mXNodes+1)];};
void PlaceLocation ( const int x, const int y );
void PathVisit ( const int x, const int y );
void RiverVisit ( const int x, const int y );
void SetPathStyle ( const int points = 10,
const float minwidth = 0.01f,
const float maxwidth = 0.05f,
const float depth = 0.3f,
const float deviation = 0.2f,
const float breadth = 5);
void SetRiverStyle ( const int depth = 5,
const int points = 10,
const float minwidth = 0.01,
const float maxwidth = 0.03,
const float beddepth = 0.0f,
const float deviation = 0.25f,
const float breadth = 7,
string bridge_name = "");
void GeneratePaths ( symmetry_t symmetric = SYMMETRY_NONE );
void GenerateRivers ( );
};
#endif

533
code/RMG/RM_Terrain.cpp Normal file
View File

@@ -0,0 +1,533 @@
#include "../server/exe_headers.h"
#include "../client/client.h"
#include "../qcommon/cm_local.h"
#include "../renderer/tr_types.h"
#include "rm_headers.h"
//#include "../qcommon/q_imath.h"
#pragma optimize("", off)
void R_LoadDataImage ( const char *name, byte **pic, int *width, int *height);
void R_InvertImage ( byte *data, int width, int height, int depth);
void R_Resample ( byte *source, int swidth, int sheight, byte *dest, int dwidth, int dheight, int components);
void RE_GetModelBounds (refEntity_t *refEnt, vec3_t bounds1, vec3_t bounds2);
static CRMLandScape *rm_landscape;
static CCMLandScape *origin_land;
CRMLandScape::CRMLandScape(void)
{
common = NULL;
mDensityMap = NULL;
}
CRMLandScape::~CRMLandScape(void)
{
if(mDensityMap)
{
Z_Free(mDensityMap);
mDensityMap = NULL;
}
}
void CCGHeightDetails::AddModel(const CRandomModel *hd)
{
if(mNumModels < MAX_RANDOM_MODELS)
{
mTotalFrequency += hd->GetFrequency();
mModels[mNumModels++] = *hd;
}
}
void CRMLandScape::AddModel(const int height, int maxheight, const CRandomModel *hd)
{
int i;
if(maxheight > HEIGHT_RESOLUTION)
{
maxheight = HEIGHT_RESOLUTION;
}
for(i = height; hd->GetModel() && (i < maxheight); i++)
{
mHeightDetails[i].AddModel(hd);
}
}
void CRMLandScape::LoadMiscentDef(const char *td)
{
char miscentDef[MAX_QPATH];
CGenericParser2 parse;
CGPGroup *basegroup, *classes, *items, *model;
CGPValue *pair;
Com_sprintf(miscentDef, MAX_QPATH, "ext_data/RMG/%s.miscents", Info_ValueForKey(td, "miscentDef"));
Com_DPrintf("CG_Terrain: Loading and parsing miscentDef %s.....\n", Info_ValueForKey(td, "miscentDef"));
if(!Com_ParseTextFile(miscentDef, parse))
{
Com_sprintf(miscentDef, MAX_QPATH, "ext_data/arioche/%s.miscents", Info_ValueForKey(td, "miscentDef"));
if(!Com_ParseTextFile(miscentDef, parse))
{
Com_Printf("Could not open %s\n", miscentDef);
return;
}
}
// The whole file....
basegroup = parse.GetBaseParseGroup();
// The root { } struct
classes = basegroup->GetSubGroups();
while(classes)
{
items = classes->GetSubGroups();
while(items)
{
if(!stricmp(items->GetName(), "miscent"))
{
int height, maxheight;
// Height must exist - the rest are optional
height = atol(items->FindPairValue("height", "0"));
maxheight = atol(items->FindPairValue("maxheight", "255"));
model = items->GetSubGroups();
while(model)
{
if(!stricmp(model->GetName(), "model"))
{
CRandomModel hd;
// Set defaults
hd.SetModel("");
hd.SetFrequency(1.0f);
hd.SetMinScale(1.0f);
hd.SetMaxScale(1.0f);
pair = model->GetPairs();
while(pair)
{
if(!stricmp(pair->GetName(), "name"))
{
hd.SetModel(pair->GetTopValue());
}
else if(!stricmp(pair->GetName(), "frequency"))
{
hd.SetFrequency((float)atof(pair->GetTopValue()));
}
else if(!stricmp(pair->GetName(), "minscale"))
{
hd.SetMinScale((float)atof(pair->GetTopValue()));
}
else if(!stricmp(pair->GetName(), "maxscale"))
{
hd.SetMaxScale((float)atof(pair->GetTopValue()));
}
pair = (CGPValue *)pair->GetNext();
}
AddModel(height, maxheight, &hd);
}
model = (CGPGroup *)model->GetNext();
}
}
items = (CGPGroup *)items->GetNext();
}
classes = (CGPGroup *)classes->GetNext();
}
Com_ParseTextFileDestroy(parse);
}
void CG_Decrease(byte *work, float lerp, int *info)
{
int val;
val = *work - origin_land->irand(2, 5);
*work = (byte)Com_Clamp(1, 255, val);
}
void CRMLandScape::CreateRandomDensityMap(byte *density, int width, int height, int seed)
{
// int i, border, inc;
int x, y, count;
// byte *work, *work2;
CArea *area;
vec3_t derxelSize, pos;
ivec3_t dmappos;
byte *hm_map = common->GetHeightMap();
int hm_width = common->GetRealWidth();
int hm_height = common->GetRealHeight();
int xpos, ypos, dx, dy;
byte *densityPos = density;
bool foundUneven;
// Init to linear spread
memset(density, 0, width * height);
/* // Make more prevalent towards the edges
border = Com_Clamp(6, 12, (width + height) >> 4);
for(i = 0; i < border; i++)
{
inc = (border - i + 1) * 9;
// Top line
work = density + i + (i * width);
for(x = i; x < width - i; x++, work++)
{
*work += (byte)common->irand(inc >> 1, inc);
}
// Left and right edges
work = density + i + ((i + 1) * width);
work2 = density + (width - i) + ((i + 1) * width);
for(y = i + 1; y < height - i - 2; y++, work += width, work2 += width)
{
*work += (byte)common->irand(inc >> 1, inc);
*work2 += (byte)common->irand(inc >> 1, inc);
}
// Bottom line
work = density + i + ((height - i - 1) * width);
for(x = i; x < width - i; x++, work++)
{
*work += (byte)common->irand(inc >> 1, inc);
}
}
*/
count = 0;
for(y=0;y<height;y++)
{
for(x=0;x<width;x++,densityPos++)
{
xpos = (x * hm_width / width);
ypos = (y * hm_height / height);
ypos = hm_height - ypos - 1;
if (hm_map[ypos*hm_width + xpos] < 150)
{
continue;
}
foundUneven = false;
for(dx=-4;(dx<=4 && !foundUneven);dx++)
{
for(dy=-4;(dy<=4 && !foundUneven);dy++)
{
if (dx == 0 && dy == 0)
{
continue;
}
if ((xpos+dx) >= 0 && (xpos+dx) < hm_width && (ypos+dy) >= 0 && (ypos+dy) < hm_height)
{
if (hm_map[(ypos+dy)*hm_width + (xpos+dx)] < 190)
{
*densityPos = 205;
count++;
foundUneven = true;
}
}
}
}
}
}
/* FILE *FH;
FH = fopen("c:\o.raw", "wb");
fwrite(hm_map, 1, common->GetRealWidth() * common->GetRealHeight(), FH);
fclose(FH);
FH = fopen("c:\d.raw", "wb");
fwrite(density, 1, width*height, FH);
fclose(FH);
*/
// Reduce severely for any settlements/buildings/objectives
VectorScale(common->GetSize(), 1.0f / width, derxelSize);
origin_land = common;
area = common->GetFirstArea();
while(area)
{
// Skip group types since they encompass to much open area
if ( area->GetType ( ) == AT_GROUP )
{
area = common->GetNextArea();
continue;
}
VectorSubtract(area->GetPosition(), common->GetMins(), pos);
VectorInverseScaleVector(pos, derxelSize, dmappos);
// Damn upside down gensurf
dmappos[1] = height - dmappos[1];
count = ceilf(area->GetRadius() / derxelSize[1]);
while(count > 0)
{
CM_CircularIterate(density, width, height, dmappos[0], dmappos[1], 0, count, NULL, CG_Decrease);
count--;
}
area = common->GetNextArea();
}
}
void CRMLandScape::LoadDensityMap(const char *td)
{
char densityMap[MAX_QPATH];
byte *imageData;
int iWidth, iHeight, seed;
char *ptr;
// Fill in with default values
mDensityMap = (byte *)Z_Malloc(common->GetBlockCount(), TAG_R_TERRAIN, qfalse);
memset(mDensityMap, 128, common->GetBlockCount());
// Load in density map (if any)
Com_sprintf(densityMap, MAX_QPATH, "%s", Info_ValueForKey(td, "densityMap"));
if(strlen(densityMap))
{
Com_DPrintf("CG_Terrain: Loading density map %s.....\n", densityMap);
R_LoadDataImage(densityMap, &imageData, &iWidth, &iHeight);
if(imageData)
{
if(strstr(densityMap, "density_"))
{
seed = strtoul(Info_ValueForKey(td, "seed"),&ptr,10);
CreateRandomDensityMap(imageData, iWidth, iHeight, seed);
}
R_Resample(imageData, iWidth, iHeight, mDensityMap, common->GetBlockWidth(), common->GetBlockHeight(), 1);
R_InvertImage(mDensityMap, common->GetBlockWidth(), common->GetBlockHeight(), 1);
Z_Free(imageData);
}
}
}
CRandomModel *CCGHeightDetails::GetRandomModel(CCMLandScape *land)
{
int seek, i;
seek = land->irand(0, mTotalFrequency);
for(i = 0; i < mNumModels; i++)
{
seek -= mModels[i].GetFrequency();
if(seek <= 0)
{
return(mModels + i);
}
}
assert(0);
return(NULL);
}
#ifndef DEDICATED
void CRMLandScape::Sprinkle(CCMPatch *patch, CCGHeightDetails *hd, int level)
{
int i, count, px, py;
float density;
vec3_t origin, scale, angles, bounds[2];
refEntity_t refEnt;
CRandomModel *rm;
CArea area;
// int areaTypes[] = { AT_BSP, AT_OBJECTIVE };
// TCGMiscEnt *data = (TCGMiscEnt *)cl.mSharedMemory;
// TCGTrace *td = (TCGTrace *)cl.mSharedMemory;
// memset(&refEnt, 0, sizeof(refEntity_t));
px = patch->GetHeightMapX() / common->GetTerxels();
py = patch->GetHeightMapY() / common->GetTerxels();
// Get a number -5.3f to 5.3f
density = (mDensityMap[px + (common->GetBlockWidth() * py)] - 128) / 24.0f;
// ..and multiply that into the count
count = Round(common->GetPatchScalarSize() * hd->GetAverageFrequency() * powf(2.0f, density) * 0.001);
for(i = 0; i < count; i++)
{
if(!common->irand(0, 10))
{
vec3_t temp;
trace_t tr;
float average;
rm = hd->GetRandomModel(common);
refEnt.hModel = re.RegisterModel(rm->GetModelName());
refEnt.frame = 0;
RE_GetModelBounds(&refEnt, bounds[0], bounds[1]);
// Calculate the scale using some magic to help ensure that the
// scales are never too different from eachother. Otherwise you
// could get an entity that is really small on one axis but huge
// on another.
temp[0] = common->flrand(rm->GetMinScale(), rm->GetMaxScale());
temp[1] = common->flrand(rm->GetMinScale(), rm->GetMaxScale());
temp[2] = common->flrand(rm->GetMinScale(), rm->GetMaxScale());
// Average of the three random numbers and divide that by two
average = ( ( temp[0] + temp[1] + temp[2] ) / 3) / 2;
// Add in half of the other two numbers and then subtract half the average to prevent.
// any number from going beyond the range. If all three numbers were the same then
// they would remain unchanged after this calculation.
scale[0] = temp[0] + (temp[1]+temp[2]) / 2 - average;
scale[1] = temp[1] + (temp[0]+temp[2]) / 2 - average;
scale[2] = temp[2] + (temp[0]+temp[1]) / 2 - average;
angles[0] = 0.0f;
angles[1] = common->flrand((float)-M_PI, (float)M_PI);
angles[2] = 0.0f;
VectorCopy(patch->GetMins(), origin);
origin[0] += common->flrand(0.0f, common->GetPatchWidth());
origin[1] += common->flrand(0.0f, common->GetPatchHeight());
// Get above world height
float slope = common->GetWorldHeight(origin, bounds, true);
if (slope > 1.33)
{ // spot has too steep of a slope
continue;
}
if(origin[2] < common->GetWaterHeight())
{
continue;
}
// very that we aren't dropped too low
if (origin[2] < common->CalcWorldHeight(level))
{
continue;
}
// Hack-ariffic, don't allow them to drop below the big player clip brush.
if (origin[2] < 1280 )
{
continue;
}
// FIXME: shouldn't be using a hard-coded 1280 number, only allow to spawn if inside player clip brush?
// if( !(CONTENTS_PLAYERCLIP & VM_Call( cgvm, CG_POINT_CONTENTS )) )
// {
// continue;
// }
// Simple radius check for buildings
/* area.Init(origin, VectorLength(bounds[0]));
if(common->AreaCollision(&area, areaTypes, sizeof(areaTypes) / sizeof(int)))
{
continue;
}*/
// Make sure there is no architecture around - doesn't work for ents though =(
/*
memset(td, sizeof(*td), 0);
VectorCopy(origin, td->mStart);
VectorCopy(bounds[0], td->mMins);
VectorCopy(bounds[1], td->mMaxs);
VectorCopy(origin, td->mEnd);
td->mSkipNumber = -1;
td->mMask = MASK_PLAYERSOLID;
*/
SV_Trace(&tr, origin, bounds[0], bounds[1], origin, -1, (CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY|CONTENTS_TERRAIN));
/*
VM_Call( cgvm, CG_TRACE );
if(td->mResult.surfaceFlags & SURF_NOMISCENTS)
{
continue;
}
if(td->mResult.startsolid)
{
// continue;
}
*/
if (tr.surfaceFlags & SURF_NOMISCENTS)
{
continue;
}
if (tr.startsolid)
{
// continue;
}
// Get minimum height of area
common->GetWorldHeight(origin, bounds, false);
// Account for relative origin
origin[2] -= bounds[0][2] * scale[2];
origin[2] -= common->flrand(2.0, (bounds[1][2] - bounds[0][2]) / 4);
//rwwFIXMEFIXME: Do this properly
// Spawn the client model
/*
strcpy(data->mModel, rm->GetModelName());
VectorCopy(origin, data->mOrigin);
VectorCopy(angles, data->mAngles);
VectorCopy(scale, data->mScale);
VM_Call( cgvm, CG_MISC_ENT);
*/
mModelCount++;
}
}
}
#endif // !DEDICATED
void CRMLandScape::SpawnPatchModels(CCMPatch *patch)
{
int i;
CCGHeightDetails *hd;
// Rand_Init(10);
#ifndef DEDICATED
for(i = 0; i < 4; i++)
{
hd = mHeightDetails + patch->GetHeight(i);
if(hd->GetNumModels())
{
Sprinkle(patch, hd, patch->GetHeight(i));
}
}
#endif // !DEDICATED
}
void SpawnPatchModelsWrapper(CCMPatch *patch, void *userdata)
{
CRMLandScape *landscape = (CRMLandScape *)userdata;
landscape->SpawnPatchModels(patch);
}
void RM_CreateRandomModels(int terrainId, const char *terrainInfo)
{
CRMLandScape *landscape;
landscape = rm_landscape = new CRMLandScape;
landscape->SetCommon(cmg.landScape);
Com_DPrintf("CG_Terrain: Creating random models.....\n");
landscape->LoadMiscentDef(terrainInfo);
landscape->LoadDensityMap(terrainInfo);
landscape->ClearModelCount();
CM_TerrainPatchIterate(landscape->GetCommon(), SpawnPatchModelsWrapper, landscape);
Com_DPrintf(".....%d random client models spawned\n", landscape->GetModelCount());
}
void RM_InitTerrain(void)
{
rm_landscape = NULL;
}
void RM_ShutdownTerrain(void)
{
CRMLandScape *landscape;
landscape = rm_landscape;
if(landscape)
{
// CM_ShutdownTerrain(i);
delete landscape;
rm_landscape = NULL;
}
}
// end
#pragma optimize("", on)

97
code/RMG/RM_Terrain.h Normal file
View File

@@ -0,0 +1,97 @@
#pragma once
#if !defined(RM_TERRAIN_H_INC)
#define RM_TERRAIN_H_INC
#define MAX_RANDOM_MODELS 8
class CRandomModel
{
private:
char mModelName[MAX_QPATH];
float mFrequency;
float mMinScale;
float mMaxScale;
public:
CRandomModel(void) { }
~CRandomModel(void) { }
// Accessors
const bool GetModel( void ) const { return(!!strlen(mModelName)); }
const char *GetModelName( void ) const { return(mModelName); }
void SetModel(const char *name) { Com_sprintf(mModelName, MAX_QPATH, "%s.md3", name); }
const float GetFrequency(void) const { return(mFrequency); }
void SetFrequency(const float freq) { mFrequency = freq; }
const float GetMinScale(void) const { return(mMinScale); }
void SetMinScale(const float minscale) { mMinScale = minscale; }
const float GetMaxScale(void) const { return(mMaxScale); }
void SetMaxScale(const float maxscale) { mMaxScale = maxscale; }
};
class CCGHeightDetails
{
private:
int mNumModels;
int mTotalFrequency;
CRandomModel mModels[MAX_RANDOM_MODELS];
public:
// Constructors
CCGHeightDetails( void ) { memset(this, 0, sizeof(*this)); }
~CCGHeightDetails( void ) { }
// Accessors
const int GetNumModels(void) const { return(mNumModels); }
const int GetAverageFrequency(void) const { return(mTotalFrequency / mNumModels); }
// Prototypes
void AddModel(const CRandomModel *hd);
CRandomModel *GetRandomModel(CCMLandScape *land);
};
class CCGPatch
{
private:
class CCMLandScape *owner;
class CCGLandScape *localowner;
CCMPatch *common;
public:
};
class CRMLandScape
{
private:
CCMLandScape *common;
byte *mDensityMap; // Data image of model densities
int mModelCount; // Count of spawned client models
CCGHeightDetails mHeightDetails[HEIGHT_RESOLUTION]; // Array of info specific to height
public:
CRMLandScape(void);
~CRMLandScape(void);
// Accessors
void SetCommon(CCMLandScape *landscape) { common = landscape; }
const CCMLandScape *GetCommon( void ) const { return(common); }
const thandle_t GetCommonId( void ) const { return(common->GetTerrainId()); }
const int GetTerxels(void) const { return(common->GetTerxels()); }
const int GetRealWidth(void) const { return(common->GetRealWidth()); }
const float GetPatchScalarSize(void) const { return(common->GetPatchScalarSize()); }
const CCGHeightDetails *GetHeightDetail(int height) const { return(mHeightDetails + height); }
void ClearModelCount(void) { mModelCount = 0; }
const int GetModelCount(void) const { return(mModelCount); }
// Prototypes
void SetShaders(const int height, const qhandle_t shader);
void AddModel(const int height, int maxheight, const CRandomModel *hd);
void LoadMiscentDef(const char *td);
void LoadDensityMap(const char *td);
void SpawnPatchModels(CCMPatch *patch);
void Sprinkle(CCMPatch *patch, CCGHeightDetails *hd, int level);
void CreateRandomDensityMap(byte *imageData, int width, int height, int seed);
};
void RM_CreateRandomModels(int terrainId, const char *terrainInfo);
void RM_InitTerrain(void);
void RM_ShutdownTerrain(void);
#endif // RM_TERRAIN_H_INC

BIN
code/SHDebug/HA312W32.DLL Normal file

Binary file not shown.

BIN
code/SHDebug/SHW32.DLL Normal file

Binary file not shown.

BIN
code/StarWars.opt Normal file

Binary file not shown.

231
code/bspthing/bsp.h Normal file
View File

@@ -0,0 +1,231 @@
/*
==============================================================================
Common stuff
==============================================================================
*/
#define MAX_QPATH 64 // max length of a quake game pathname
typedef float vec_t;
typedef vec_t vec2_t[2];
typedef vec_t vec3_t[3];
typedef vec_t vec4_t[4];
typedef vec_t vec5_t[5];
typedef unsigned char byte;
#define LIGHTMAP_SIZE 128
#define LIGHTMAP_BY_VERTEX -3 // pre-lit triangle models
#define POINTS_ST_SCALE 128.0f
#define POINTS_LIGHT_SCALE 65536.0f
/*
==============================================================================
.BSP file format
==============================================================================
*/
#define BSP_IDENT (('P'<<24)+('S'<<16)+('B'<<8)+'R')
#define BSP_VERSION 1
// there shouldn't be any problem with increasing these values at the
// expense of more memory allocation in the utilities
#define MAX_MAP_MODELS 0x400
#define MAX_MAP_BRUSHES 0x8000
#define MAX_MAP_ENTITIES 0x800
#define MAX_MAP_ENTSTRING 0x40000
#define MAX_MAP_SHADERS 0x400
#define MAX_MAP_AREAS 0x100 // MAX_MAP_AREA_BYTES in q_shared must match!
#define MAX_MAP_FOGS 0x100
#define MAX_MAP_PLANES 0x20000
#define MAX_MAP_NODES 0x20000
#define MAX_MAP_BRUSHSIDES 0x20000
#define MAX_MAP_LEAFS 0x20000
#define MAX_MAP_LEAFFACES 0x20000
#define MAX_MAP_LEAFBRUSHES 0x40000
#define MAX_MAP_PORTALS 0x20000
#define MAX_MAP_LIGHTING 0x800000
#define MAX_MAP_LIGHTGRID 65535
#define MAX_MAP_LIGHTGRID_ARRAY 0x100000
#define MAX_MAP_VISIBILITY 0x400000
#define MAX_MAP_DRAW_SURFS 0x20000
#define MAX_MAP_DRAW_VERTS 0x80000
#define MAX_MAP_DRAW_INDEXES 0x80000
// key / value pair sizes in the entities lump
#define MAX_KEY 32
#define MAX_VALUE 1024
// the editor uses these predefined yaw angles to orient entities up or down
#define ANGLE_UP -1
#define ANGLE_DOWN -2
#define LIGHTMAP_WIDTH 128
#define LIGHTMAP_HEIGHT 128
//=============================================================================
typedef struct {
int fileofs, filelen;
} lump_t;
#define LUMP_ENTITIES 0
#define LUMP_SHADERS 1
#define LUMP_PLANES 2
#define LUMP_NODES 3
#define LUMP_LEAFS 4
#define LUMP_LEAFSURFACES 5
#define LUMP_LEAFBRUSHES 6
#define LUMP_MODELS 7
#define LUMP_BRUSHES 8
#define LUMP_BRUSHSIDES 9
#define LUMP_DRAWVERTS 10
#define LUMP_DRAWINDEXES 11
#define LUMP_FOGS 12
#define LUMP_SURFACES 13
#define LUMP_LIGHTMAPS 14
#define LUMP_LIGHTGRID 15
#define LUMP_VISIBILITY 16
#define LUMP_LIGHTARRAY 17
#define HEADER_LUMPS 18
typedef struct {
int ident;
int version;
lump_t lumps[HEADER_LUMPS];
} dheader_t;
typedef struct {
float mins[3], maxs[3];
int firstSurface, numSurfaces;
int firstBrush, numBrushes;
} dmodel_t;
typedef struct {
char shader[MAX_QPATH];
int surfaceFlags;
int contentFlags;
} dshader_t;
// planes x^1 is allways the opposite of plane x
typedef struct {
float normal[3];
float dist;
} dplane_t;
typedef struct {
int planeNum;
int children[2]; // negative numbers are -(leafs+1), not nodes
int mins[3]; // for frustom culling
int maxs[3];
} dnode_t;
typedef struct {
int cluster; // -1 = opaque cluster (do I still store these?)
int area;
int mins[3]; // for frustum culling
int maxs[3];
int firstLeafSurface;
int numLeafSurfaces;
int firstLeafBrush;
int numLeafBrushes;
} dleaf_t;
typedef struct {
int planeNum; // positive plane side faces out of the leaf
int shaderNum;
int drawSurfNum;
} dbrushside_t;
typedef struct {
int firstSide;
int numSides;
int shaderNum; // the shader that determines the contents flags
} dbrush_t;
typedef struct {
char shader[MAX_QPATH];
int brushNum;
int visibleSide; // the brush side that ray tests need to clip against (-1 == none)
} dfog_t;
// Light Style Constants
#define MAXLIGHTMAPS 4
#define LS_NORMAL 0x00
#define LS_UNUSED 0xfe
#define LS_NONE 0xff
#define MAX_LIGHT_STYLES 64
typedef struct {
vec3_t xyz;
float st[2];
float lightmap[MAXLIGHTMAPS][2];
vec3_t normal;
byte color[MAXLIGHTMAPS][4];
} mapVert_t;
#define DRAWVERT_LIGHTMAP_SCALE 32768.0f
#define DRAWVERT_ST_SCALE 16.0f
typedef struct {
vec3_t xyz;
short dvst[2];
short dvlightmap[MAXLIGHTMAPS][2];
vec3_t normal;
byte dvcolor[MAXLIGHTMAPS][2];
} drawVert_t;
typedef struct
{
byte ambientLight[MAXLIGHTMAPS][3];
byte directLight[MAXLIGHTMAPS][3];
byte styles[MAXLIGHTMAPS];
byte latLong[2];
} dgrid_t;
typedef enum {
MST_BAD,
MST_PLANAR,
MST_PATCH,
MST_TRIANGLE_SOUP,
MST_FLARE
} mapSurfaceType_t;
typedef struct {
int shaderNum;
int fogNum;
int surfaceType;
int firstVert;
int numVerts;
int firstIndex;
int numIndexes;
byte lightmapStyles[MAXLIGHTMAPS], vertexStyles[MAXLIGHTMAPS];
int lightmapNum[MAXLIGHTMAPS];
int lightmapX[MAXLIGHTMAPS], lightmapY[MAXLIGHTMAPS];
int lightmapWidth, lightmapHeight;
vec3_t lightmapOrigin;
vec3_t lightmapVecs[3]; // for patches, [0] and [1] are lodbounds
int patchWidth;
int patchHeight;
} dsurface_t;

View File

@@ -0,0 +1,21 @@
Microsoft Visual Studio Solution File, Format Version 7.00
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bspthing", "bspthing.vcproj", "{613ECD87-EBE1-47D9-A23D-68F52218AEB6}"
EndProject
Global
GlobalSection(SolutionConfiguration) = preSolution
ConfigName.0 = Debug
ConfigName.1 = Release
EndGlobalSection
GlobalSection(ProjectDependencies) = postSolution
EndGlobalSection
GlobalSection(ProjectConfiguration) = postSolution
{613ECD87-EBE1-47D9-A23D-68F52218AEB6}.Debug.ActiveCfg = Debug|Win32
{613ECD87-EBE1-47D9-A23D-68F52218AEB6}.Debug.Build.0 = Debug|Win32
{613ECD87-EBE1-47D9-A23D-68F52218AEB6}.Release.ActiveCfg = Release|Win32
{613ECD87-EBE1-47D9-A23D-68F52218AEB6}.Release.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection
GlobalSection(ExtensibilityAddIns) = postSolution
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,113 @@
<?xml version="1.0" encoding = "Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.00"
Name="bspthing"
ProjectGUID="{613ECD87-EBE1-47D9-A23D-68F52218AEB6}"
Keyword="ManagedCProj">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="Debug"
IntermediateDirectory="Debug"
ConfigurationType="1"
CharacterSet="2"
ManagedExtensions="TRUE">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
PreprocessorDefinitions="WIN32;_DEBUG"
MinimalRebuild="FALSE"
BasicRuntimeChecks="0"
RuntimeLibrary="1"
WarningLevel="3"
DebugInformationFormat="3"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
OutputFile="$(OutDir)/bspthing.exe"
LinkIncremental="2"
GenerateDebugInformation="TRUE"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="Release"
IntermediateDirectory="Release"
ConfigurationType="1"
CharacterSet="2"
ManagedExtensions="TRUE">
<Tool
Name="VCCLCompilerTool"
Optimization="2"
InlineFunctionExpansion="1"
PreprocessorDefinitions="WIN32;NDEBUG"
MinimalRebuild="FALSE"
WarningLevel="3"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
OutputFile="$(OutDir)/bspthing.exe"
LinkIncremental="1"
GenerateDebugInformation="TRUE"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
</Configuration>
</Configurations>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm">
<File
RelativePath="main.cpp">
</File>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc">
<File
RelativePath="bsp.h">
</File>
<File
RelativePath="pbsp.h">
</File>
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;r">
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

1111
code/bspthing/main.cpp Normal file

File diff suppressed because it is too large Load Diff

132
code/bspthing/pbsp.h Normal file
View File

@@ -0,0 +1,132 @@
/*
==============================================================================
Packed .BSP structures
==============================================================================
*/
#pragma pack(push, 1)
typedef struct {
char shader[MAX_QPATH];
int brushNum;
int visibleSide; // the brush side that ray tests need to clip against (-1 == none)
} pdfog_t;
typedef struct {
int firstSide;
byte numSides;
unsigned short shaderNum; // the shader that determines the contents flags
} pdbrush_t;
typedef struct {
int planeNum; // positive plane side faces out of the leaf
byte shaderNum;
} pdbrushside_t;
typedef struct {
int planeNum;
short children[2]; // negative numbers are -(leafs+1), not nodes
short mins[3]; // for frustom culling
short maxs[3];
} pdnode_t;
typedef struct {
short cluster; // -1 = opaque cluster (do I still store these?)
signed char area;
short mins[3]; // for frustum culling
short maxs[3];
unsigned short firstLeafSurface;
unsigned short numLeafSurfaces;
unsigned short firstLeafBrush;
unsigned short numLeafBrushes;
} pdleaf_t;
typedef struct {
float mins[3], maxs[3];
int firstSurface;
unsigned short numSurfaces;
int firstBrush;
unsigned short numBrushes;
} pdmodel_t;
typedef struct {
byte flags;
byte latLong[2];
int data;
} pdgrid_t;
typedef struct {
char shader[MAX_QPATH];
int surfaceFlags;
int contentFlags;
} pdshader_t;
typedef struct {
float normal[3];
float dist;
} pdplane_t;
typedef struct {
float lightmap[MAXLIGHTMAPS][2];
float st[2];
short xyz[3];
short normal[3];
byte color[MAXLIGHTMAPS][4];
} pmapVert_t;
typedef struct {
int code;
byte shaderNum;
signed char fogNum;
unsigned int verts; // high 20 bits are first vert, low 12 are num verts
byte lightmapStyles[MAXLIGHTMAPS];
byte lightmapNum[MAXLIGHTMAPS];
short lightmapVecs[2][3]; // for patches, [0] and [1] are lodbounds
byte patchWidth;
byte patchHeight;
} pdpatch_t;
typedef struct {
int code;
byte shaderNum;
signed char fogNum;
unsigned int verts; // high 20 bits are first vert, low 12 are num verts
unsigned int indexes; // high 20 bits are first index, low 12 are num indices
byte lightmapStyles[MAXLIGHTMAPS];
byte lightmapNum[MAXLIGHTMAPS];
short lightmapVecs[3];
} pdface_t;
typedef struct {
int code;
byte shaderNum;
signed char fogNum;
unsigned int verts; // high 20 bits are first vert, low 12 are num verts
unsigned int indexes; // high 20 bits are first index, low 12 are num indices
byte lightmapStyles[MAXLIGHTMAPS];
} pdtrisurf_t;
typedef struct {
int code;
byte shaderNum;
signed char fogNum;
short origin[3];
short normal[3];
byte color[3];
} pdflare_t;
#pragma pack(pop)

105
code/cgame/FX_ATSTMain.cpp Normal file
View File

@@ -0,0 +1,105 @@
// Bowcaster Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
---------------------------
FX_ATSTMainProjectileThink
---------------------------
*/
void FX_ATSTMainProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 30 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 30.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
theFxScheduler.PlayEffect( "atst/shot", cent->lerpOrigin, forward );
}
/*
---------------------------
FX_ATSTMainHitWall
---------------------------
*/
void FX_ATSTMainHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( "atst/wall_impact", origin, normal );
}
/*
---------------------------
FX_ATSTMainHitPlayer
---------------------------
*/
void FX_ATSTMainHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
if ( humanoid )
{
theFxScheduler.PlayEffect( "atst/flesh_impact", origin, normal );
}
else
{
theFxScheduler.PlayEffect( "atst/droid_impact", origin, normal );
}
}
/*
---------------------------
FX_ATSTSideAltProjectileThink
---------------------------
*/
void FX_ATSTSideAltProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( "atst/side_alt_shot", cent->lerpOrigin, forward );
}
/*
---------------------------
FX_ATSTSideMainProjectileThink
---------------------------
*/
void FX_ATSTSideMainProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( "atst/side_main_shot", cent->lerpOrigin, forward );
}

95
code/cgame/FX_Blaster.cpp Normal file
View File

@@ -0,0 +1,95 @@
// Blaster Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
-------------------------
FX_BlasterProjectileThink
-------------------------
*/
void FX_BlasterProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if (cent->currentState.eFlags & EF_USE_ANGLEDELTA)
{
AngleVectors(cent->currentState.angles, forward, 0, 0);
}
else
{
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 75 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 75.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
if ( cent->gent && cent->gent->owner && cent->gent->owner->s.number > 0 )
{
theFxScheduler.PlayEffect( "blaster/NPCshot", cent->lerpOrigin, forward );
}
else
{
theFxScheduler.PlayEffect( cgs.effects.blasterShotEffect, cent->lerpOrigin, forward );
}
}
/*
-------------------------
FX_BlasterAltFireThink
-------------------------
*/
void FX_BlasterAltFireThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
FX_BlasterProjectileThink( cent, weapon );
}
/*
-------------------------
FX_BlasterWeaponHitWall
-------------------------
*/
void FX_BlasterWeaponHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( cgs.effects.blasterWallImpactEffect, origin, normal );
}
/*
-------------------------
FX_BlasterWeaponHitPlayer
-------------------------
*/
void FX_BlasterWeaponHitPlayer( gentity_t *hit, vec3_t origin, vec3_t normal, qboolean humanoid )
{
//temporary? just testing out the damage skin stuff -rww
if ( hit && hit->client && hit->ghoul2.size() )
{
CG_AddGhoul2Mark(cgs.media.bdecal_burnmark1, flrand(3.5, 4.0), origin, normal, hit->s.number,
hit->client->ps.origin, hit->client->renderInfo.legsYaw, hit->ghoul2, hit->s.modelScale, Q_irand(10000, 13000));
}
theFxScheduler.PlayEffect( cgs.effects.blasterFleshImpactEffect, origin, normal );
}

View File

@@ -0,0 +1,66 @@
// Bowcaster Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
---------------------------
FX_BowcasterProjectileThink
---------------------------
*/
void FX_BowcasterProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 75 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 75.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
theFxScheduler.PlayEffect( cgs.effects.bowcasterShotEffect, cent->lerpOrigin, forward );
}
/*
---------------------------
FX_BowcasterHitWall
---------------------------
*/
void FX_BowcasterHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( cgs.effects.bowcasterImpactEffect, origin, normal );
}
/*
---------------------------
FX_BowcasterHitPlayer
---------------------------
*/
void FX_BowcasterHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
theFxScheduler.PlayEffect( cgs.effects.bowcasterImpactEffect, origin, normal );
}

View File

@@ -0,0 +1,156 @@
// Bryar Pistol Weapon Effects
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
-------------------------
MAIN FIRE
-------------------------
FX_BryarProjectileThink
-------------------------
*/
void FX_BryarProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 75 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 75.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
if ( cent->gent && cent->gent->owner && cent->gent->owner->s.number > 0 )
{
theFxScheduler.PlayEffect( "bryar/NPCshot", cent->lerpOrigin, forward );
}
else
{
theFxScheduler.PlayEffect( cgs.effects.bryarShotEffect, cent->lerpOrigin, forward );
}
}
/*
-------------------------
FX_BryarHitWall
-------------------------
*/
void FX_BryarHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( cgs.effects.bryarWallImpactEffect, origin, normal );
}
/*
-------------------------
FX_BryarHitPlayer
-------------------------
*/
void FX_BryarHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
theFxScheduler.PlayEffect( cgs.effects.bryarFleshImpactEffect, origin, normal );
}
/*
-------------------------
ALT FIRE
-------------------------
FX_BryarAltProjectileThink
-------------------------
*/
void FX_BryarAltProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 75 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 75.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
// see if we have some sort of extra charge going on
for ( int t = 1; t < cent->gent->count; t++ )
{
// just add ourselves over, and over, and over when we are charged
theFxScheduler.PlayEffect( cgs.effects.bryarPowerupShotEffect, cent->lerpOrigin, forward );
}
theFxScheduler.PlayEffect( cgs.effects.bryarShotEffect, cent->lerpOrigin, forward );
}
/*
-------------------------
FX_BryarAltHitWall
-------------------------
*/
void FX_BryarAltHitWall( vec3_t origin, vec3_t normal, int power )
{
switch( power )
{
case 4:
case 5:
theFxScheduler.PlayEffect( cgs.effects.bryarWallImpactEffect3, origin, normal );
break;
case 2:
case 3:
theFxScheduler.PlayEffect( cgs.effects.bryarWallImpactEffect2, origin, normal );
break;
default:
theFxScheduler.PlayEffect( cgs.effects.bryarWallImpactEffect, origin, normal );
break;
}
}
/*
-------------------------
FX_BryarAltHitPlayer
-------------------------
*/
void FX_BryarAltHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
theFxScheduler.PlayEffect( cgs.effects.bryarFleshImpactEffect, origin, normal );
}

View File

@@ -0,0 +1,98 @@
// Concussion Rifle Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
---------------------------
FX_ConcProjectileThink
---------------------------
*/
void FX_ConcProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( "concussion/shot", cent->lerpOrigin, forward );
}
/*
---------------------------
FX_ConcHitWall
---------------------------
*/
void FX_ConcHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( "concussion/explosion", origin, normal );
}
/*
---------------------------
FX_ConcHitPlayer
---------------------------
*/
void FX_ConcHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
theFxScheduler.PlayEffect( "concussion/explosion", origin, normal );
}
/*
---------------------------
FX_ConcAltShot
---------------------------
*/
static vec3_t WHITE ={1.0f,1.0f,1.0f};
void FX_ConcAltShot( vec3_t start, vec3_t end )
{
//"concussion/beam"
FX_AddLine( -1, start, end, 0.1f, 10.0f, 0.0f,
1.0f, 0.0f, 0.0f,
WHITE, WHITE, 0.0f,
175, cgi_R_RegisterShader( "gfx/effects/blueLine" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
vec3_t BRIGHT={0.75f,0.5f,1.0f};
// add some beef
FX_AddLine( -1, start, end, 0.1f, 7.0f, 0.0f,
1.0f, 0.0f, 0.0f,
BRIGHT, BRIGHT, 0.0f,
150, cgi_R_RegisterShader( "gfx/misc/whiteline2" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
}
/*
---------------------------
FX_ConcAltMiss
---------------------------
*/
void FX_ConcAltMiss( vec3_t origin, vec3_t normal )
{
vec3_t pos, c1, c2;
VectorMA( origin, 4.0f, normal, c1 );
VectorCopy( c1, c2 );
c1[2] += 4;
c2[2] += 12;
VectorAdd( origin, normal, pos );
pos[2] += 28;
FX_AddBezier( origin, pos, c1, vec3_origin, c2, vec3_origin, 6.0f, 6.0f, 0.0f, 0.0f, 0.2f, 0.5f, WHITE, WHITE, 0.0f, 4000, cgi_R_RegisterShader( "gfx/effects/smokeTrail" ), FX_ALPHA_WAVE );
theFxScheduler.PlayEffect( "concussion/alt_miss", origin, normal );
}

92
code/cgame/FX_DEMP2.cpp Normal file
View File

@@ -0,0 +1,92 @@
// DEMP2 Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
#include "FxUtil.h"
/*
---------------------------
FX_DEMP2_ProjectileThink
---------------------------
*/
void FX_DEMP2_ProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
// theFxScheduler.PlayEffect( "demp2/shot", cent->lerpOrigin, forward );
// theFxScheduler.PlayEffect( "demp2/shot2", cent->lerpOrigin, forward );
theFxScheduler.PlayEffect( "demp2/projectile", cent->lerpOrigin, forward );
}
/*
---------------------------
FX_DEMP2_HitWall
---------------------------
*/
void FX_DEMP2_HitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( "demp2/wall_impact", origin, normal );
}
/*
---------------------------
FX_DEMP2_HitPlayer
---------------------------
*/
void FX_DEMP2_HitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
theFxScheduler.PlayEffect( "demp2/flesh_impact", origin, normal );
}
/*
---------------------------
FX_DEMP2_AltProjectileThink
---------------------------
*/
void FX_DEMP2_AltProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( "demp2/projectile", cent->lerpOrigin, forward );
}
//---------------------------------------------
void FX_DEMP2_AltDetonate( vec3_t org, float size )
{
localEntity_t *ex;
ex = CG_AllocLocalEntity();
ex->leType = LE_FADE_SCALE_MODEL;
memset( &ex->refEntity, 0, sizeof( refEntity_t ));
ex->refEntity.renderfx |= RF_VOLUMETRIC;
ex->startTime = cg.time;
ex->endTime = ex->startTime + 1300;
ex->radius = size;
ex->refEntity.customShader = cgi_R_RegisterShader( "gfx/effects/demp2shell" );
ex->refEntity.hModel = cgi_R_RegisterModel( "models/items/sphere.md3" );
VectorCopy( org, ex->refEntity.origin );
ex->color[0] = ex->color[1] = ex->color[2] = 255.0f;
}

View File

@@ -0,0 +1,98 @@
// Disruptor Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
---------------------------
FX_DisruptorMainShot
---------------------------
*/
static vec3_t WHITE ={1.0f,1.0f,1.0f};
void FX_DisruptorMainShot( vec3_t start, vec3_t end )
{
FX_AddLine( -1, start, end, 0.1f, 4.0f, 0.0f,
1.0f, 0.0f, 0.0f,
WHITE, WHITE, 0.0f,
120, cgi_R_RegisterShader( "gfx/effects/redLine" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
}
/*
---------------------------
FX_DisruptorAltShot
---------------------------
*/
void FX_DisruptorAltShot( vec3_t start, vec3_t end, qboolean fullCharge )
{
FX_AddLine( -1, start, end, 0.1f, 10.0f, 0.0f,
1.0f, 0.0f, 0.0f,
WHITE, WHITE, 0.0f,
175, cgi_R_RegisterShader( "gfx/effects/redLine" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
if ( fullCharge )
{
vec3_t YELLER={0.8f,0.7f,0.0f};
// add some beef
FX_AddLine( -1, start, end, 0.1f, 7.0f, 0.0f,
1.0f, 0.0f, 0.0f,
YELLER, YELLER, 0.0f,
150, cgi_R_RegisterShader( "gfx/misc/whiteline2" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
}
}
/*
---------------------------
FX_DisruptorAltMiss
---------------------------
*/
void FX_DisruptorAltMiss( vec3_t origin, vec3_t normal )
{
vec3_t pos, c1, c2;
VectorMA( origin, 4.0f, normal, c1 );
VectorCopy( c1, c2 );
c1[2] += 4;
c2[2] += 12;
VectorAdd( origin, normal, pos );
pos[2] += 28;
FX_AddBezier( origin, pos, c1, vec3_origin, c2, vec3_origin, 6.0f, 6.0f, 0.0f, 0.0f, 0.2f, 0.5f, WHITE, WHITE, 0.0f, 4000, cgi_R_RegisterShader( "gfx/effects/smokeTrail" ), FX_ALPHA_WAVE );
theFxScheduler.PlayEffect( "disruptor/alt_miss", origin, normal );
}
/*
---------------------------
FX_KothosBeam
---------------------------
*/
void FX_KothosBeam( vec3_t start, vec3_t end )
{
FX_AddLine( -1, start, end, 0.1f, 10.0f, 0.0f,
1.0f, 0.0f, 0.0f,
WHITE, WHITE, 0.0f,
175, cgi_R_RegisterShader( "gfx/misc/dr1" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
vec3_t YELLER={0.8f,0.7f,0.0f};
// add some beef
FX_AddLine( -1, start, end, 0.1f, 7.0f, 0.0f,
1.0f, 0.0f, 0.0f,
YELLER, YELLER, 0.0f,
150, cgi_R_RegisterShader( "gfx/misc/whiteline2" ),
0, FX_SIZE_LINEAR | FX_ALPHA_LINEAR );
}

146
code/cgame/FX_Emplaced.cpp Normal file
View File

@@ -0,0 +1,146 @@
// Emplaced Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
---------------------------
FX_EmplacedProjectileThink
---------------------------
*/
void FX_EmplacedProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 75 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 75.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
// If tie-fighter missle use green shot.
if ( cent->currentState.weapon == WP_TIE_FIGHTER )
{
theFxScheduler.PlayEffect( "ships/imp_blastershot", cent->lerpOrigin, forward );
}
else
{
if ( cent->gent && cent->gent->owner && cent->gent->owner->activator && cent->gent->owner->activator->s.number > 0 )
{
// NPC's do short shot
if ( cent->gent->alt_fire )
{
theFxScheduler.PlayEffect( "eweb/shotNPC", cent->lerpOrigin, forward );
}
else
{
theFxScheduler.PlayEffect( "emplaced/shotNPC", cent->lerpOrigin, forward );
}
}
else
{
// players do long shot
if ( cent->gent && cent->gent->alt_fire )
{
theFxScheduler.PlayEffect( "eweb/shotNPC", cent->lerpOrigin, forward );
}
else
{
theFxScheduler.PlayEffect( "emplaced/shot", cent->lerpOrigin, forward );
}
}
}
}
/*
---------------------------
FX_EmplacedHitWall
---------------------------
*/
void FX_EmplacedHitWall( vec3_t origin, vec3_t normal, qboolean eweb )
{
if ( eweb )
{
theFxScheduler.PlayEffect( "eweb/wall_impact", origin, normal );
}
else
{
theFxScheduler.PlayEffect( "emplaced/wall_impact", origin, normal );
}
}
/*
---------------------------
FX_EmplacedHitPlayer
---------------------------
*/
void FX_EmplacedHitPlayer( vec3_t origin, vec3_t normal, qboolean eweb )
{
if ( eweb )
{
theFxScheduler.PlayEffect( "eweb/flesh_impact", origin, normal );
}
else
{
theFxScheduler.PlayEffect( "emplaced/wall_impact", origin, normal );
}
}
/*
---------------------------
FX_TurretProjectileThink
---------------------------
*/
void FX_TurretProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 75 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 75.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
theFxScheduler.PlayEffect( "turret/shot", cent->lerpOrigin, forward );
}

View File

@@ -0,0 +1,73 @@
// Golan Arms Flechette Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
-------------------------
FX_FlechetteProjectileThink
-------------------------
*/
void FX_FlechetteProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
EvaluateTrajectoryDelta( &cent->gent->s.pos, cg.time, forward );
if ( VectorNormalize( forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( cgs.effects.flechetteShotEffect, cent->lerpOrigin, forward );
}
/*
-------------------------
FX_FlechetteWeaponHitWall
-------------------------
*/
void FX_FlechetteWeaponHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( cgs.effects.flechetteShotDeathEffect, origin, normal );
}
/*
-------------------------
FX_BlasterWeaponHitPlayer
-------------------------
*/
void FX_FlechetteWeaponHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
// if ( humanoid )
// {
theFxScheduler.PlayEffect( cgs.effects.flechetteFleshImpactEffect, origin, normal );
// }
// else
// {
// theFxScheduler.PlayEffect( "blaster/droid_impact", origin, normal );
// }
}
/*
-------------------------
FX_FlechetteProjectileThink
-------------------------
*/
void FX_FlechetteAltProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( cgs.effects.flechetteAltShotEffect, cent->lerpOrigin, forward );
}

View File

@@ -0,0 +1,92 @@
// Heavy Repeater Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
---------------------------
FX_RepeaterProjectileThink
---------------------------
*/
void FX_RepeaterProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( "repeater/projectile", cent->lerpOrigin, forward );
}
/*
------------------------
FX_RepeaterHitWall
------------------------
*/
void FX_RepeaterHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( "repeater/wall_impact", origin, normal );
}
/*
------------------------
FX_RepeaterHitPlayer
------------------------
*/
void FX_RepeaterHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
theFxScheduler.PlayEffect( "repeater/wall_impact", origin, normal );
// theFxScheduler.PlayEffect( "repeater/flesh_impact", origin, normal );
}
/*
------------------------------
FX_RepeaterAltProjectileThink
-----------------------------
*/
void FX_RepeaterAltProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( "repeater/alt_projectile", cent->lerpOrigin, forward );
// theFxScheduler.PlayEffect( "repeater/alt_projectile", cent->lerpOrigin, forward );
}
/*
------------------------
FX_RepeaterAltHitWall
------------------------
*/
void FX_RepeaterAltHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( "repeater/concussion", origin, normal );
// theFxScheduler.PlayEffect( "repeater/alt_wall_impact2", origin, normal );
}
/*
------------------------
FX_RepeaterAltHitPlayer
------------------------
*/
void FX_RepeaterAltHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
theFxScheduler.PlayEffect( "repeater/concussion", origin );
// theFxScheduler.PlayEffect( "repeater/alt_wall_impact2", origin, normal );
}

View File

@@ -0,0 +1,72 @@
// Noghri Rifle
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
-------------------------
FX_NoghriShotProjectileThink
-------------------------
*/
void FX_NoghriShotProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 75 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 75.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
theFxScheduler.PlayEffect( "noghri_stick/shot", cent->lerpOrigin, forward );
}
/*
-------------------------
FX_NoghriShotWeaponHitWall
-------------------------
*/
void FX_NoghriShotWeaponHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( "noghri_stick/flesh_impact", origin, normal );//no "noghri/wall_impact"?
}
/*
-------------------------
FX_NoghriShotWeaponHitPlayer
-------------------------
*/
void FX_NoghriShotWeaponHitPlayer( gentity_t *hit, vec3_t origin, vec3_t normal, qboolean humanoid )
{
//temporary? just testing out the damage skin stuff -rww
/*
if ( hit && hit->client && hit->ghoul2.size() )
{
CG_AddGhoul2Mark(cgs.media.bdecal_burnmark1, flrand(3.5, 4.0), origin, normal, hit->s.number,
hit->client->ps.origin, hit->client->renderInfo.legsYaw, hit->ghoul2, hit->s.modelScale, Q_irand(10000, 13000));
}
*/
theFxScheduler.PlayEffect( "noghri_stick/flesh_impact", origin, normal );
}

View File

@@ -0,0 +1,66 @@
// Rocket Launcher Weapon
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
---------------------------
FX_RocketProjectileThink
---------------------------
*/
void FX_RocketProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( "rocket/shot", cent->lerpOrigin, forward );
}
/*
---------------------------
FX_RocketHitWall
---------------------------
*/
void FX_RocketHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( "rocket/explosion", origin, normal );
}
/*
---------------------------
FX_RocketHitPlayer
---------------------------
*/
void FX_RocketHitPlayer( vec3_t origin, vec3_t normal, qboolean humanoid )
{
theFxScheduler.PlayEffect( "rocket/explosion", origin, normal );
}
/*
---------------------------
FX_RocketAltProjectileThink
---------------------------
*/
void FX_RocketAltProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
theFxScheduler.PlayEffect( "rocket/shot", cent->lerpOrigin, forward );
}

View File

@@ -0,0 +1,70 @@
// Tusken Rifle
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
/*
-------------------------
FX_TuskenShotProjectileThink
-------------------------
*/
void FX_TuskenShotProjectileThink( centity_t *cent, const struct weaponInfo_s *weapon )
{
vec3_t forward;
if ( VectorNormalize2( cent->gent->s.pos.trDelta, forward ) == 0.0f )
{
if ( VectorNormalize2( cent->currentState.pos.trDelta, forward ) == 0.0f )
{
forward[2] = 1.0f;
}
}
// hack the scale of the forward vector if we were just fired or bounced...this will shorten up the tail for a split second so tails don't clip so harshly
int dif = cg.time - cent->gent->s.pos.trTime;
if ( dif < 75 )
{
if ( dif < 0 )
{
dif = 0;
}
float scale = ( dif / 75.0f ) * 0.95f + 0.05f;
VectorScale( forward, scale, forward );
}
theFxScheduler.PlayEffect( "tusken/shot", cent->lerpOrigin, forward );
}
/*
-------------------------
FX_TuskenShotWeaponHitWall
-------------------------
*/
void FX_TuskenShotWeaponHitWall( vec3_t origin, vec3_t normal )
{
theFxScheduler.PlayEffect( "tusken/hitwall", origin, normal );
}
/*
-------------------------
FX_TuskenShotWeaponHitPlayer
-------------------------
*/
void FX_TuskenShotWeaponHitPlayer( gentity_t *hit, vec3_t origin, vec3_t normal, qboolean humanoid )
{
//temporary? just testing out the damage skin stuff -rww
if ( hit && hit->client && hit->ghoul2.size() )
{
CG_AddGhoul2Mark(cgs.media.bdecal_burnmark1, flrand(3.5, 4.0), origin, normal, hit->s.number,
hit->client->ps.origin, hit->client->renderInfo.legsYaw, hit->ghoul2, hit->s.modelScale, Q_irand(10000, 13000));
}
theFxScheduler.PlayEffect( "tusken/hit", origin, normal );
}

5
code/cgame/FxParsing.cpp Normal file
View File

@@ -0,0 +1,5 @@
// this include must remain at the top of every FXxxxx.CPP file
#include "common_headers.h"

6
code/cgame/FxParsing.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#if !defined(FX_PARSING_H_INC)
#define FX_PARSING_H_INC
#endif // FX_PARSING_H

2301
code/cgame/FxPrimitives.cpp Normal file

File diff suppressed because it is too large Load Diff

572
code/cgame/FxPrimitives.h Normal file
View File

@@ -0,0 +1,572 @@
#if !defined(FX_SYSTEM_H_INC)
#include "FxSystem.h"
#endif
#ifndef FX_PRIMITIVES_H_INC
#define FX_PRIMITIVES_H_INC
#define MAX_EFFECTS 1200
// Generic group flags, used by parser, then get converted to the appropriate specific flags
#define FX_PARM_MASK 0xC // use this to mask off any transition types that use a parm
#define FX_GENERIC_MASK 0xF
#define FX_LINEAR 0x1
#define FX_RAND 0x2
#define FX_NONLINEAR 0x4
#define FX_WAVE 0x8
#define FX_CLAMP 0xC
// Group flags
#define FX_ALPHA_SHIFT 0
#define FX_ALPHA_PARM_MASK 0x0000000C
#define FX_ALPHA_LINEAR 0x00000001
#define FX_ALPHA_RAND 0x00000002
#define FX_ALPHA_NONLINEAR 0x00000004
#define FX_ALPHA_WAVE 0x00000008
#define FX_ALPHA_CLAMP 0x0000000C
#define FX_RGB_SHIFT 4
#define FX_RGB_PARM_MASK 0x000000C0
#define FX_RGB_LINEAR 0x00000010
#define FX_RGB_RAND 0x00000020
#define FX_RGB_NONLINEAR 0x00000040
#define FX_RGB_WAVE 0x00000080
#define FX_RGB_CLAMP 0x000000C0
#define FX_SIZE_SHIFT 8
#define FX_SIZE_PARM_MASK 0x00000C00
#define FX_SIZE_LINEAR 0x00000100
#define FX_SIZE_RAND 0x00000200
#define FX_SIZE_NONLINEAR 0x00000400
#define FX_SIZE_WAVE 0x00000800
#define FX_SIZE_CLAMP 0x00000C00
#define FX_LENGTH_SHIFT 12
#define FX_LENGTH_PARM_MASK 0x0000C000
#define FX_LENGTH_LINEAR 0x00001000
#define FX_LENGTH_RAND 0x00002000
#define FX_LENGTH_NONLINEAR 0x00004000
#define FX_LENGTH_WAVE 0x00008000
#define FX_LENGTH_CLAMP 0x0000C000
#define FX_SIZE2_SHIFT 16
#define FX_SIZE2_PARM_MASK 0x000C0000
#define FX_SIZE2_LINEAR 0x00010000
#define FX_SIZE2_RAND 0x00020000
#define FX_SIZE2_NONLINEAR 0x00040000
#define FX_SIZE2_WAVE 0x00080000
#define FX_SIZE2_CLAMP 0x000C0000
// Feature flags
#define FX_DEPTH_HACK 0x00100000
#define FX_RELATIVE 0x00200000
#define FX_SET_SHADER_TIME 0x00400000 // by having the effects system set the shader time, we can make animating textures start at the correct time
#define FX_EXPENSIVE_PHYSICS 0x00800000
//rww - g2-related flags (these can slow things down significantly, use sparingly)
//These should be used only with particles/decals as they steal flags used by cylinders.
#define FX_GHOUL2_TRACE 0x00020000 //use in conjunction with particles - actually do full ghoul2 traces for physics collision against entities with a ghoul2 instance
//shared FX_SIZE2_RAND (used only with cylinders)
#define FX_GHOUL2_DECALS 0x00040000 //use in conjunction with decals - can project decal as a ghoul2 gore skin object onto ghoul2 models
//shared FX_SIZE2_NONLINEAR (used only with cylinders)
#define FX_ATTACHED_MODEL 0x01000000
#define FX_APPLY_PHYSICS 0x02000000
#define FX_USE_BBOX 0x04000000 // can make physics more accurate at the expense of speed
#define FX_USE_ALPHA 0x08000000 // the FX system actually uses RGB to do fades, but this will override that
// and cause it to fill in the alpha.
#define FX_EMIT_FX 0x10000000 // emitters technically don't have to emit stuff, but when they do
// this flag needs to be set
#define FX_DEATH_RUNS_FX 0x20000000 // Normal death triggers effect, but not kill_on_impact
#define FX_KILL_ON_IMPACT 0x40000000 // works just like it says, but only when physics are on.
#define FX_IMPACT_RUNS_FX 0x80000000 // an effect can call another effect when it hits something.
// Lightning flags, duplicates of existing flags, but lightning doesn't use those flags in that context...and nothing will ever use these in this context..so we are safe.
#define FX_TAPER 0x01000000 // tapers as it moves towards its endpoint
#define FX_BRANCH 0x02000000 // enables lightning branching
#define FX_GROW 0x04000000 // lightning grows from start point to end point over the course of its life
//------------------------------
class CEffect
{
protected:
vec3_t mOrigin1;
int mTimeStart;
int mTimeEnd;
unsigned int mFlags;
// Size of our object, useful for things that have physics
vec3_t mMin;
vec3_t mMax;
int mImpactFxID; // if we have an impact event, we may have to call an effect
int mDeathFxID; // if we have a death event, we may have to call an effect
refEntity_t mRefEnt;
public:
CEffect() { memset( &mRefEnt, 0, sizeof( refEntity_t )); }
virtual ~CEffect() {}
virtual void Die() {}
virtual bool Update()
{ // Game pausing can cause dumb time things to happen, so kill the effect in this instance
if ( mTimeStart > theFxHelper.mTime ) {
return false;
}
return true;
}
inline void SetSTScale(float s,float t) { mRefEnt.shaderTexCoord[0]=s;mRefEnt.shaderTexCoord[1]=t;}
inline void SetMin( const vec3_t min ) { if(min){VectorCopy(min,mMin);}else{VectorClear(mMin);} }
inline void SetMax( const vec3_t max ) { if(max){VectorCopy(max,mMax);}else{VectorClear(mMax);} }
inline void SetFlags( int flags ) { mFlags = flags; }
inline void AddFlags( int flags ) { mFlags |= flags; }
inline void ClearFlags( int flags ) { mFlags &= ~flags; }
inline void SetOrigin1( const vec3_t org ) { if(org){VectorCopy(org,mOrigin1);}else{VectorClear(mOrigin1);} }
inline void SetTimeStart( int time ) { mTimeStart = time; if (mFlags&FX_SET_SHADER_TIME) { mRefEnt.shaderTime = cg.time * 0.001f; }}
inline void SetTimeEnd( int time ) { mTimeEnd = time; }
inline void SetImpactFxID( int id ) { mImpactFxID = id; }
inline void SetDeathFxID( int id ) { mDeathFxID = id; }
};
//---------------------------------------------------
// This class is kind of an exception to the "rule".
// For now it exists only for allowing an easy way
// to get the saber slash trails rendered.
//---------------------------------------------------
class CTrail : public CEffect
{
// This is such a specific case thing, just grant public access to the goods.
protected:
void Draw();
public:
typedef struct
{
vec3_t origin;
// very specifc case, we can modulate the color and the alpha
vec3_t rgb;
vec3_t destrgb;
vec3_t curRGB;
float alpha;
float destAlpha;
float curAlpha;
// this is a very specific case thing...allow interpolating the st coords so we can map the texture
// properly as this segement progresses through it's life
float ST[2];
float destST[2];
float curST[2];
} TVert;
TVert mVerts[4];
qhandle_t mShader;
CTrail() {};
virtual ~CTrail() {};
virtual bool Update();
};
//------------------------------
class CLight : public CEffect
{
protected:
float mSizeStart;
float mSizeEnd;
float mSizeParm;
vec3_t mRGBStart;
vec3_t mRGBEnd;
float mRGBParm;
void UpdateSize();
void UpdateRGB();
void Draw()
{
theFxHelper.AddLightToScene( mOrigin1, mRefEnt.radius,
mRefEnt.lightingOrigin[0], mRefEnt.lightingOrigin[1], mRefEnt.lightingOrigin[2] );
}
public:
CLight() {}
virtual ~CLight() {}
virtual bool Update();
inline void SetSizeStart( float sz ) { mSizeStart = sz; }
inline void SetSizeEnd( float sz ) { mSizeEnd = sz; }
inline void SetSizeParm( float parm ) { mSizeParm = parm; }
inline void SetRGBStart( vec3_t rgb ) { if(rgb){VectorCopy(rgb,mRGBStart);}else{VectorClear(mRGBStart);} }
inline void SetRGBEnd( vec3_t rgb ) { if(rgb){VectorCopy(rgb,mRGBEnd);}else{VectorClear(mRGBEnd);} }
inline void SetRGBParm( float parm ) { mRGBParm = parm; }
};
//------------------------------
class CFlash : public CLight
{
protected:
void Draw();
public:
CFlash() {}
virtual ~CFlash() {}
virtual bool Update();
inline void SetShader( qhandle_t sh )
{ assert(sh);
mRefEnt.customShader = sh;
}
void Init( void );
};
//------------------------------
class CParticle : public CEffect
{
protected:
vec3_t mOrgOffset;
vec3_t mVel;
vec3_t mAccel;
float mGravity;
float mSizeStart;
float mSizeEnd;
float mSizeParm;
vec3_t mRGBStart;
vec3_t mRGBEnd;
float mRGBParm;
float mAlphaStart;
float mAlphaEnd;
float mAlphaParm;
float mRotationDelta;
float mElasticity;
short mClientID;
char mModelNum;
char mBoltNum;
bool UpdateOrigin();
void UpdateVelocity() {VectorMA( mVel, theFxHelper.mFloatFrameTime, mAccel, mVel ); }
void UpdateSize();
void UpdateRGB();
void UpdateAlpha();
void UpdateRotation() { mRefEnt.rotation += theFxHelper.mFrameTime * 0.01f * mRotationDelta; }
bool Cull();
void Draw();
public:
inline CParticle() { mRefEnt.reType = RT_SPRITE; mClientID = -1; mModelNum = -1; mBoltNum = -1; }
virtual ~CParticle() {}
virtual void Die();
virtual bool Update();
inline void SetShader( qhandle_t sh ) { mRefEnt.customShader = sh;}
inline void SetOrgOffset( const vec3_t o ) { if(o){VectorCopy(o,mOrgOffset);}else{VectorClear(mOrgOffset);}}
inline void SetVel( const vec3_t vel ) { if(vel){VectorCopy(vel,mVel);}else{VectorClear(mVel);} }
inline void SetAccel( const vec3_t ac ) { if(ac){VectorCopy(ac,mAccel);}else{VectorClear(mAccel);} }
inline void SetGravity( float grav ) { mGravity = grav; }
inline void SetSizeStart( float sz ) { mSizeStart = sz; }
inline void SetSizeEnd( float sz ) { mSizeEnd = sz; }
inline void SetSizeParm( float parm ) { mSizeParm = parm; }
inline void SetRGBStart( const vec3_t rgb ) { if(rgb){VectorCopy(rgb,mRGBStart);}else{VectorClear(mRGBStart);} }
inline void SetRGBEnd( const vec3_t rgb ) { if(rgb){VectorCopy(rgb,mRGBEnd);}else{VectorClear(mRGBEnd);} }
inline void SetRGBParm( float parm ) { mRGBParm = parm; }
inline void SetAlphaStart( float al ) { mAlphaStart = al; }
inline void SetAlphaEnd( float al ) { mAlphaEnd = al; }
inline void SetAlphaParm( float parm ) { mAlphaParm = parm; }
inline void SetRotation( float rot ) { mRefEnt.rotation = rot; }
inline void SetRotationDelta( float rot ) { mRotationDelta = rot; }
inline void SetElasticity( float el ) { mElasticity = el; }
inline void SetClient( int clientID, int modelNum = -1, int boltNum = -1 ) {mClientID = clientID; mModelNum = modelNum; mBoltNum = boltNum; }
};
//------------------------------
class CLine : public CParticle
{
protected:
vec3_t mOrigin2;
void Draw();
public:
CLine() { mRefEnt.reType = RT_LINE;}
virtual ~CLine() {}
virtual void Die() {}
virtual bool Update();
inline void SetOrigin2( const vec3_t org2 ) { VectorCopy( org2, mOrigin2 ); }
};
//------------------------------
class CBezier : public CLine
{
protected:
vec3_t mControl1;
vec3_t mControl1Vel;
vec3_t mControl2;
vec3_t mControl2Vel;
bool mInit;
void Draw();
public:
CBezier(){ mInit = false; }
virtual ~CBezier() {}
virtual void Die() {}
virtual bool Update();
void DrawSegment( vec3_t start, vec3_t end, float texcoord1, float texcoord2 );
inline void SetControlPoints( const vec3_t ctrl1, const vec3_t ctrl2 ) { VectorCopy( ctrl1, mControl1 ); VectorCopy( ctrl2, mControl2 ); }
inline void SetControlVel( const vec3_t ctrl1v, const vec3_t ctrl2v ) { VectorCopy( ctrl1v, mControl1Vel ); VectorCopy( ctrl2v, mControl2Vel ); }
};
//------------------------------
class CElectricity : public CLine
{
protected:
float mChaos;
void Draw();
public:
CElectricity() { mRefEnt.reType = RT_ELECTRICITY; }
virtual ~CElectricity() {}
virtual void Die() {}
virtual bool Update();
void Initialize();
inline void SetChaos( float chaos ) { mChaos = chaos; }
};
// Oriented quad
//------------------------------
class COrientedParticle : public CParticle
{
protected:
vec3_t mNormal;
vec3_t mNormalOffset;
bool Cull();
void Draw();
public:
COrientedParticle() { mRefEnt.reType = RT_ORIENTED_QUAD; }
virtual ~COrientedParticle() {}
virtual bool Update();
inline void SetNormal( const vec3_t norm ) { VectorCopy( norm, mNormal ); }
inline void SetNormalOffset( const vec3_t norm ) { VectorCopy( norm, mNormalOffset ); }
};
//------------------------------
class CTail : public CParticle
{
protected:
vec3_t mOldOrigin;
float mLengthStart;
float mLengthEnd;
float mLengthParm;
float mLength;
void UpdateLength();
void CalcNewEndpoint();
void Draw();
bool Cull();
public:
CTail() { mRefEnt.reType = RT_LINE; }
virtual ~CTail() {}
virtual bool Update();
inline void SetLengthStart( float len ) { mLengthStart = len; }
inline void SetLengthEnd( float len ) { mLengthEnd = len; }
inline void SetLengthParm( float len ) { mLengthParm = len; }
};
//------------------------------
class CCylinder : public CTail
{
protected:
float mSize2Start;
float mSize2End;
float mSize2Parm;
void UpdateSize2();
void Draw();
public:
CCylinder() { mRefEnt.reType = RT_CYLINDER; }
virtual ~CCylinder() {}
virtual bool Update();
inline void SetSize2Start( float sz ) { mSize2Start = sz; }
inline void SetSize2End( float sz ) { mSize2End = sz; }
inline void SetSize2Parm( float parm ) { mSize2Parm = parm; }
inline void SetNormal( const vec3_t norm ) { VectorCopy( norm, mRefEnt.axis[0] ); }
};
//------------------------------
// Emitters are derived from particles because, although they don't draw, any effect called
// from them can borrow an initial or ending value from the emitters current alpha, rgb, etc..
class CEmitter : public CParticle
{
protected:
vec3_t mOldOrigin; // we use these to do some nice
vec3_t mLastOrigin; // tricks...
vec3_t mOldVelocity; //
int mOldTime;
vec3_t mAngles; // for a rotating thing, using a delta
vec3_t mAngleDelta; // as opposed to an end angle is probably much easier
int mEmitterFxID; // if we have emitter fx, this is our id
float mDensity; // controls how often emitter chucks an effect
float mVariance; // density sloppiness
void UpdateAngles();
void Draw();
public:
CEmitter() {
// There may or may not be a model, but if there isn't one,
// we just won't bother adding the refEnt in our Draw func
mRefEnt.reType = RT_MODEL;
}
virtual ~CEmitter() {}
virtual bool Update();
inline void SetModel( qhandle_t model ) { mRefEnt.hModel = model; }
inline void SetAngles( const vec3_t ang ) { if(ang){VectorCopy(ang,mAngles);}else{VectorClear(mAngles);} }
inline void SetAngleDelta( const vec3_t ang){ if(ang){VectorCopy(ang,mAngleDelta);}else{VectorClear(mAngleDelta);} }
inline void SetEmitterFxID( int id ) { mEmitterFxID = id; }
inline void SetDensity( float density ) { mDensity = density; }
inline void SetVariance( float var ) { mVariance = var; }
inline void SetOldTime( int time ) { mOldTime = time; }
inline void SetLastOrg( const vec3_t org ) { if(org){VectorCopy(org,mLastOrigin);}else{VectorClear(mLastOrigin);} }
inline void SetLastVel( const vec3_t vel ) { if(vel){VectorCopy(vel,mOldVelocity);}else{VectorClear(mOldVelocity);}}
};
// We're getting pretty low level here, not the kind of thing to abuse considering how much overhead this
// adds to a SINGLE triangle or quad....
// The editor doesn't need to see or do anything with this
//------------------------------
#define MAX_CPOLY_VERTS 5
class CPoly : public CParticle
{
protected:
int mCount;
vec3_t mRotDelta;
int mTimeStamp;
bool Cull();
void Draw();
public:
vec3_t mOrg[MAX_CPOLY_VERTS];
vec2_t mST[MAX_CPOLY_VERTS];
float mRot[3][3];
int mLastFrameTime;
CPoly() {}
virtual ~CPoly() {}
virtual bool Update();
void PolyInit();
void CalcRotateMatrix();
void Rotate();
inline void SetNumVerts( int c ) { mCount = c; }
inline void SetRot( vec3_t r ) { if(r){VectorCopy(r,mRotDelta);}else{VectorClear(mRotDelta);}}
inline void SetMotionTimeStamp( int t ) { mTimeStamp = theFxHelper.mTime + t; }
inline int GetMotionTimeStamp() { return mTimeStamp; }
};
#endif //FX_PRIMITIVES_H_INC

2049
code/cgame/FxScheduler.cpp Normal file

File diff suppressed because it is too large Load Diff

497
code/cgame/FxScheduler.h Normal file
View File

@@ -0,0 +1,497 @@
#if !defined(FX_UTIL_H_INC)
#include "FxUtil.h"
#endif
#include "../qcommon/sstring.h"
typedef sstring_t fxString_t;
#if !defined(FX_PARSING_H_INC)
#include "FxParsing.h"
#endif
#ifndef FX_SCHEDULER_H_INC
#define FX_SCHEDULER_H_INC
using namespace std;
#define FX_FILE_PATH "effects"
#define FX_MAX_TRACE_DIST WORLD_SIZE
#define FX_MAX_EFFECTS 150 // how many effects the system can store
#define FX_MAX_EFFECT_COMPONENTS 24 // how many primitives an effect can hold, this should be plenty
#define FX_MAX_PRIM_NAME 32
//-----------------------------------------------
// These are spawn flags for primitiveTemplates
//-----------------------------------------------
#define FX_ORG_ON_SPHERE 0x00001 // Pretty dang expensive, calculates a point on a sphere/ellipsoid
#define FX_AXIS_FROM_SPHERE 0x00002 // Can be used in conjunction with org_on_sphere to cause particles to move out
// from the center of the sphere
#define FX_ORG_ON_CYLINDER 0x00004 // calculate point on cylinder/disk
#define FX_ORG2_FROM_TRACE 0x00010
#define FX_TRACE_IMPACT_FX 0x00020 // if trace impacts, we should play one of the specified impact fx files
#define FX_ORG2_IS_OFFSET 0x00040 // template specified org2 should be the offset from a trace endpos or
// passed in org2. You might use this to lend a random flair to the endpos.
// Note: this is done pre-trace, so you may have to specify large numbers for this
#define FX_CHEAP_ORG_CALC 0x00100 // Origin is calculated relative to passed in axis unless this is on.
#define FX_CHEAP_ORG2_CALC 0x00200 // Origin2 is calculated relative to passed in axis unless this is on.
#define FX_VEL_IS_ABSOLUTE 0x00400 // Velocity isn't relative to passed in axis with this flag on.
#define FX_ACCEL_IS_ABSOLUTE 0x00800 // Acceleration isn't relative to passed in axis with this flag on.
#define FX_RAND_ROT_AROUND_FWD 0x01000 // Randomly rotates up and right around forward vector
#define FX_EVEN_DISTRIBUTION 0x02000 // When you have a delay, it normally picks a random time to play. When
// this flag is on, it generates an even time distribution
#define FX_RGB_COMPONENT_INTERP 0x04000 // Picks a color on the line defined by RGB min & max, default is to pick color in cube defined by min & max
#define FX_AFFECTED_BY_WIND 0x10000 // we take into account our wind vector when we spawn in
#define FX_SND_LESS_ATTENUATION 0x20000 // attenuate sounds less
//-----------------------------------------------------------------
//
// CMediaHandles
//
// Primitive templates might want to use a list of sounds, shaders
// or models to get a bit more variation in their effects.
//
//-----------------------------------------------------------------
class CMediaHandles
{
private:
vector<int> mMediaList;
public:
void AddHandle( int item ) { mMediaList.push_back( item ); }
int GetHandle() { if (mMediaList.size()==0) {return 0;}
else {return mMediaList[irand(0,mMediaList.size()-1)];} }
void operator=(const CMediaHandles &that );
};
//-----------------------------------------------------------------
//
// CFxRange
//
// Primitive templates typically use this class to define each of
// its members. This is done to make it easier to create effects
// with a desired range of characteristics.
//
//-----------------------------------------------------------------
class CFxRange
{
private:
float mMin;
float mMax;
public:
CFxRange() {mMin=0; mMax=0;}
inline void SetRange(float min,float max) {mMin=min; mMax=max;}
inline void SetMin(float min) {mMin=min;}
inline void SetMax(float max) {mMax=max;}
inline float GetMax() const {return mMax;}
inline float GetMin() const {return mMin;}
inline float GetVal(float percent) const {if(mMin == mMax){return mMin;}
return (mMin + (mMax - mMin) * percent);}
inline float GetVal() const {if(mMin == mMax){return mMin;}
return flrand(mMin, mMax);}
inline int GetRoundedVal() const {if(mMin == mMax){return mMin;}
return (int)(flrand(mMin, mMax) + 0.5f);}
inline void ForceRange(float min,float max) {if(mMin < min){mMin=min;} if(mMin > max){mMin=max;}
if(mMax < min){mMax=min;} if(mMax > max){mMax=max;}}
inline void Sort() {if(mMin > mMax){float temp = mMin; mMin=mMax;mMax=temp;}}
void operator=(const CFxRange &that) {mMin=that.mMin; mMax=that.mMax;}
bool operator==(const CFxRange &rhs) const { return ((mMin == rhs.mMin) &&
(mMax == rhs.mMax)); }
};
//----------------------------
// Supported primitive types
//----------------------------
enum EPrimType
{
None = 0,
Particle, // sprite
Line,
Tail, // comet-like tail thing
Cylinder,
Emitter, // emits effects as it moves, can also attach a chunk
Sound,
#ifdef _IMMERSION
Force,
#endif // _IMMERSION
Decal, // projected onto architecture
OrientedParticle,
Electricity,
FxRunner,
Light,
CameraShake,
ScreenFlash
};
//-----------------------------------------------------------------
//
// CPrimitiveTemplate
//
// The primitive template is used to spawn 1 or more fx primitives
// with the range of characteristics defined by the template.
//
// As such, I just made this one huge shared class knowing that
// there won't be many of them in memory at once, and we won't
// be dynamically creating and deleting them mid-game. Also,
// note that not every primitive type will use all of these fields.
//
//-----------------------------------------------------------------
class CPrimitiveTemplate
{
public:
// These kinds of things should not even be allowed to be accessed publicly
bool mCopy;
int mRefCount; // For a copy of a primitive...when we figure out how many items we want to spawn,
// we'll store that here and then decrement us for each we actually spawn. When we
// hit zero, we are no longer used and so we can just free ourselves
char mName[FX_MAX_PRIM_NAME];
EPrimType mType;
CFxRange mSpawnDelay;
CFxRange mSpawnCount;
CFxRange mLife;
int mCullRange;
CMediaHandles mMediaHandles;
CMediaHandles mImpactFxHandles;
CMediaHandles mDeathFxHandles;
CMediaHandles mEmitterFxHandles;
CMediaHandles mPlayFxHandles;
int mFlags; // These need to get passed on to the primitive
int mSpawnFlags; // These are only used to control spawning, but never get passed to prims.
vec3_t mMin;
vec3_t mMax;
CFxRange mOrigin1X;
CFxRange mOrigin1Y;
CFxRange mOrigin1Z;
CFxRange mOrigin2X;
CFxRange mOrigin2Y;
CFxRange mOrigin2Z;
CFxRange mRadius; // spawn on sphere/ellipse/disk stuff.
CFxRange mHeight;
CFxRange mRotation;
CFxRange mRotationDelta;
CFxRange mAngle1;
CFxRange mAngle2;
CFxRange mAngle3;
CFxRange mAngle1Delta;
CFxRange mAngle2Delta;
CFxRange mAngle3Delta;
CFxRange mVelX;
CFxRange mVelY;
CFxRange mVelZ;
CFxRange mAccelX;
CFxRange mAccelY;
CFxRange mAccelZ;
CFxRange mGravity;
CFxRange mDensity;
CFxRange mVariance;
CFxRange mRedStart;
CFxRange mGreenStart;
CFxRange mBlueStart;
CFxRange mRedEnd;
CFxRange mGreenEnd;
CFxRange mBlueEnd;
CFxRange mRGBParm;
CFxRange mAlphaStart;
CFxRange mAlphaEnd;
CFxRange mAlphaParm;
CFxRange mSizeStart;
CFxRange mSizeEnd;
CFxRange mSizeParm;
CFxRange mSize2Start;
CFxRange mSize2End;
CFxRange mSize2Parm;
CFxRange mLengthStart;
CFxRange mLengthEnd;
CFxRange mLengthParm;
CFxRange mTexCoordS;
CFxRange mTexCoordT;
CFxRange mElasticity;
// Lower level parsing utilities
bool ParseVector( const char *val, vec3_t min, vec3_t max );
bool ParseFloat( const char *val, float *min, float *max );
bool ParseGroupFlags( const char *val, int *flags );
// Base key processing
// Note that these all have their own parse functions in case it becomes important to do certain kinds
// of validation specific to that type.
bool ParseMin( const char *val );
bool ParseMax( const char *val );
bool ParseDelay( const char *val );
bool ParseCount( const char *val );
bool ParseLife( const char *val );
bool ParseElasticity( const char *val );
bool ParseFlags( const char *val );
bool ParseSpawnFlags( const char *val );
bool ParseOrigin1( const char *val );
bool ParseOrigin2( const char *val );
bool ParseRadius( const char *val );
bool ParseHeight( const char *val );
bool ParseRotation( const char *val );
bool ParseRotationDelta( const char *val );
bool ParseAngle( const char *val );
bool ParseAngleDelta( const char *val );
bool ParseVelocity( const char *val );
bool ParseAcceleration( const char *val );
bool ParseGravity( const char *val );
bool ParseDensity( const char *val );
bool ParseVariance( const char *val );
// Group type processing
bool ParseRGB( CGPGroup *grp );
bool ParseAlpha( CGPGroup *grp );
bool ParseSize( CGPGroup *grp );
bool ParseSize2( CGPGroup *grp );
bool ParseLength( CGPGroup *grp );
bool ParseModels( CGPValue *grp );
bool ParseShaders( CGPValue *grp );
bool ParseSounds( CGPValue *grp );
#ifdef _IMMERSION
bool ParseForces( CGPValue *grp );
#endif // _IMMERSION
bool ParseImpactFxStrings( CGPValue *grp );
bool ParseDeathFxStrings( CGPValue *grp );
bool ParseEmitterFxStrings( CGPValue *grp );
bool ParsePlayFxStrings( CGPValue *grp );
// Group keys
bool ParseRGBStart( const char *val );
bool ParseRGBEnd( const char *val );
bool ParseRGBParm( const char *val );
bool ParseRGBFlags( const char *val );
bool ParseAlphaStart( const char *val );
bool ParseAlphaEnd( const char *val );
bool ParseAlphaParm( const char *val );
bool ParseAlphaFlags( const char *val );
bool ParseSizeStart( const char *val );
bool ParseSizeEnd( const char *val );
bool ParseSizeParm( const char *val );
bool ParseSizeFlags( const char *val );
bool ParseSize2Start( const char *val );
bool ParseSize2End( const char *val );
bool ParseSize2Parm( const char *val );
bool ParseSize2Flags( const char *val );
bool ParseLengthStart( const char *val );
bool ParseLengthEnd( const char *val );
bool ParseLengthParm( const char *val );
bool ParseLengthFlags( const char *val );
public:
CPrimitiveTemplate();
~CPrimitiveTemplate() {};
bool ParsePrimitive( CGPGroup *grp );
void operator=(const CPrimitiveTemplate &that);
};
// forward declaration
struct SEffectTemplate;
// Effects are built of one or more primitives
struct SEffectTemplate
{
bool mInUse;
bool mCopy;
char mEffectName[MAX_QPATH]; // is this extraneous??
int mPrimitiveCount;
int mRepeatDelay;
CPrimitiveTemplate *mPrimitives[FX_MAX_EFFECT_COMPONENTS];
bool operator == (const char * name) const
{
return !stricmp( mEffectName, name );
}
void operator=(const SEffectTemplate &that);
};
//-----------------------------------------------------------------
//
// CFxScheduler
//
// The scheduler not only handles requests to play an effect, it
// tracks the request throughout its life if necessary, creating
// any of the delayed components as needed.
//
//-----------------------------------------------------------------
// needs to be in global space now (loadsave stuff)
#define MAX_LOOPED_FX 32
// We hold a looped effect here
struct SLoopedEffect
{
int mId; // effect id
int mBoltInfo; // used to determine which bolt on the ghoul2 model we should be attaching this effect to
int mNextTime; //time to render again
int mLoopStopTime; //time to die
bool mPortalEffect; // rww - render this before skyportals, and not in the normal world view.
bool mIsRelative; // bolt this puppy on keep it updated
};
class CFxScheduler
{
private:
// We hold a scheduled effect here
struct SScheduledEffect
{
CPrimitiveTemplate *mpTemplate; // primitive template
int mStartTime;
char mModelNum; // uset to determine which ghoul2 model we want to bolt this effect to
char mBoltNum; // used to determine which bolt on the ghoul2 model we should be attaching this effect to
short mEntNum; // used to determine which entity this ghoul model is attached to.
short mClientID; // FIXME: redundant. this is used for muzzle bolts, merge into normal bolting
bool mPortalEffect; // rww - render this before skyportals, and not in the normal world view.
bool mIsRelative; // bolt this puppy on keep it updated
vec3_t mOrigin;
vec3_t mAxis[3];
bool operator <= (const int time) const
{
return mStartTime <= time;
}
};
/* Looped Effects get stored and reschedule at mRepeatRate */
// must be in sync with gLoopedEffectArray[MAX_LOOPED_FX]!
//
SLoopedEffect mLoopedEffectArray[MAX_LOOPED_FX];
int ScheduleLoopedEffect( int id, int boltInfo, bool isPortal, int iLoopTime, bool isRelative );
void AddLoopedEffects( );
// this makes looking up the index based on the string name much easier
typedef map<fxString_t, int> TEffectID;
typedef list<SScheduledEffect*> TScheduledEffect;
// Effects
SEffectTemplate mEffectTemplates[FX_MAX_EFFECTS];
TEffectID mEffectIDs; // if you only have the unique effect name, you'll have to use this to get the ID.
// List of scheduled effects that will need to be created at the correct time.
TScheduledEffect mFxSchedule;
// Private function prototypes
SEffectTemplate *GetNewEffectTemplate( int *id, const char *file );
void AddPrimitiveToEffect( SEffectTemplate *fx, CPrimitiveTemplate *prim );
int ParseEffect( const char *file, CGPGroup *base );
void CreateEffect( CPrimitiveTemplate *fx, const vec3_t origin, vec3_t axis[3], int lateTime, int clientID = -1, int modelNum = -1, int boltNum = -1 );
void CreateEffect( CPrimitiveTemplate *fx, int clientID, int lateTime );
public:
CFxScheduler();
void LoadSave_Read();
void LoadSave_Write();
void FX_CopeWithAnyLoadedSaveGames();
int RegisterEffect( const char *file, bool bHasCorrectPath = false ); // handles pre-caching
// Nasty overloaded madness
void PlayEffect( int id, vec3_t org, bool isPortal = false ); // uses a default up axis
void PlayEffect( int id, vec3_t org, vec3_t fwd, bool isPortal = false ); // builds arbitrary perp. right vector, does a cross product to define up
void PlayEffect( int id, vec3_t origin, vec3_t axis[3], const int boltInfo=-1, const int entNum=-1, bool isPortal = false, int iLoopTime = false, bool isRelative = false );
void PlayEffect( const char *file, vec3_t org, bool isPortal = false ); // uses a default up axis
void PlayEffect( const char *file, vec3_t org, vec3_t fwd, bool isPortal = false ); // builds arbitrary perp. right vector, does a cross product to define up
void PlayEffect( const char *file, vec3_t origin, vec3_t axis[3], const int boltInfo, const int entNum, bool isPortal = false, int iLoopTime = false, bool isRelative = false );
//for muzzle
void PlayEffect( const char *file, int clientID, bool isPortal = false );
#ifdef _IMMERSION // for ff-system
void PlayEffect( int id, int clientNum, vec3_t org, vec3_t fwd, bool isPortal = false );
void PlayEffect( const char *file, int clientNum, vec3_t origin, vec3_t forward, bool isPortal = false );
#endif // _IMMERSION
void StopEffect( const char *file, const int boltInfo, bool isPortal = false ); //find a scheduled Looping effect with these parms and kill it
void AddScheduledEffects( bool portal ); // call once per CGame frame [rww ammendment - twice now actually, but first only renders portal effects]
int NumScheduledFx() { return mFxSchedule.size(); }
void Clean(bool bRemoveTemplates = true, int idToPreserve = 0); // clean out the system
// FX Override functions
SEffectTemplate *GetEffectCopy( int fxHandle, int *newHandle );
SEffectTemplate *GetEffectCopy( const char *file, int *newHandle );
CPrimitiveTemplate *GetPrimitiveCopy( SEffectTemplate *effectCopy, const char *componentName );
};
//-------------------
// The one and only
//-------------------
extern CFxScheduler theFxScheduler;
#endif // FX_SCHEDULER_H_INC

215
code/cgame/FxSystem.cpp Normal file
View File

@@ -0,0 +1,215 @@
// this include must remain at the top of every FXxxxx.CPP file
#include "common_headers.h"
#if !defined(FX_SCHEDULER_H_INC)
#include "FxScheduler.h"
#endif
#include "cg_media.h" //for cgs.model_draw for G2
extern vmCvar_t fx_debug;
extern vmCvar_t fx_freeze;
extern void CG_ExplosionEffects( vec3_t origin, float intensity, int radius, int time );
// Stuff for the FxHelper
//------------------------------------------------------
void SFxHelper::Init()
{
mTime = 0;
}
//------------------------------------------------------
void SFxHelper::Print( const char *msg, ... )
{
#ifndef FINAL_BUILD
va_list argptr;
char text[1024];
va_start( argptr, msg );
vsprintf( text, msg, argptr );
va_end( argptr );
gi.Printf( text );
#endif
}
//------------------------------------------------------
void SFxHelper::AdjustTime( int frameTime )
{
if ( fx_freeze.integer || ( frameTime <= 0 ))
{
// Allow no time progression when we are paused.
mFrameTime = 0;
mFloatFrameTime = 0.0f;
}
else
{
if ( !cg_paused.integer )
{
if ( frameTime > 300 ) // hack for returning from paused and time bursts
{
frameTime = 300;
}
mFrameTime = frameTime;
mFloatFrameTime = mFrameTime * 0.001f;
mTime += mFrameTime;
}
}
}
//------------------------------------------------------
int SFxHelper::OpenFile( const char *file, fileHandle_t *fh, int mode )
{
// char path[256];
// sprintf( path, "%s/%s", FX_FILE_PATH, file );
return cgi_FS_FOpenFile( file, fh, FS_READ );
}
//------------------------------------------------------
int SFxHelper::ReadFile( void *data, int len, fileHandle_t fh )
{
return cgi_FS_Read( data, len, fh );
}
//------------------------------------------------------
void SFxHelper::CloseFile( fileHandle_t fh )
{
cgi_FS_FCloseFile( fh );
}
//------------------------------------------------------
void SFxHelper::PlaySound( const vec3_t org, int entityNum, int entchannel, int sfxHandle )
{
cgi_S_StartSound( org, entityNum, entchannel, sfxHandle );
}
//------------------------------------------------------
void SFxHelper::PlayLocalSound( int sfxHandle, int channelNum )
{
cgi_S_StartLocalSound(sfxHandle, channelNum);
}
//------------------------------------------------------
void SFxHelper::Trace( trace_t *tr, vec3_t start, vec3_t min, vec3_t max,
vec3_t end, int skipEntNum, int flags )
{
CG_Trace( tr, start, min, max, end, skipEntNum, flags );
}
void SFxHelper::G2Trace( trace_t *tr, vec3_t start, vec3_t min, vec3_t max,
vec3_t end, int skipEntNum, int flags )
{
//CG_Trace( tr, start, min, max, end, skipEntNum, flags, G2_COLLIDE );
gi.trace(tr, start, NULL, NULL, end, skipEntNum, flags, G2_COLLIDE);
}
//------------------------------------------------------
void SFxHelper::AddFxToScene( refEntity_t *ent )
{
cgi_R_AddRefEntityToScene( ent );
}
//------------------------------------------------------
int SFxHelper::RegisterShader( const char *shader )
{
return cgi_R_RegisterShader( shader );
}
//------------------------------------------------------
int SFxHelper::RegisterSound( const char *sound )
{
return cgi_S_RegisterSound( sound );
}
//------------------------------------------------------
int SFxHelper::RegisterModel( const char *model )
{
return cgi_R_RegisterModel( model );
}
//------------------------------------------------------
void SFxHelper::AddLightToScene( vec3_t org, float radius, float red, float green, float blue )
{
cgi_R_AddLightToScene( org, radius, red, green, blue );
}
//------------------------------------------------------
void SFxHelper::AddPolyToScene( int shader, int count, polyVert_t *verts )
{
cgi_R_AddPolyToScene( shader, count, verts );
}
//------------------------------------------------------
void SFxHelper::CameraShake( vec3_t origin, float intensity, int radius, int time )
{
CG_ExplosionEffects( origin, intensity, radius, time );
}
//------------------------------------------------------
int SFxHelper::GetOriginAxisFromBolt(const centity_t &cent, int modelNum, int boltNum, vec3_t /*out*/origin, vec3_t /*out*/axis[3])
{
if ((cg.time-cent.snapShotTime) > 200)
{ //you were added more than 200ms ago, so I say you are no longer valid/in our snapshot.
return 0;
}
int doesBoltExist;
mdxaBone_t boltMatrix;
vec3_t G2Angles = {cent.lerpAngles[0] , cent.lerpAngles[1], cent.lerpAngles[2]};
if ( cent.currentState.eType == ET_PLAYER )
{//players use cent.renderAngles
VectorCopy( cent.renderAngles, G2Angles );
if ( cent.gent //has a game entity
&& cent.gent->s.m_iVehicleNum != 0 //in a vehicle
&& cent.gent->m_pVehicle //have a valid vehicle pointer
&& cent.gent->m_pVehicle->m_pVehicleInfo->type != VH_FIGHTER //it's not a fighter
&& cent.gent->m_pVehicle->m_pVehicleInfo->type != VH_SPEEDER //not a speeder
)
{
G2Angles[PITCH]=0;
G2Angles[ROLL] =0;
}
}
// go away and get me the bolt position for this frame please
doesBoltExist = gi.G2API_GetBoltMatrix(cent.gent->ghoul2, modelNum,
boltNum, &boltMatrix, G2Angles,
cent.lerpOrigin, cg.time, cgs.model_draw,
cent.currentState.modelScale);
// set up the axis and origin we need for the actual effect spawning
origin[0] = boltMatrix.matrix[0][3];
origin[1] = boltMatrix.matrix[1][3];
origin[2] = boltMatrix.matrix[2][3];
axis[1][0] = boltMatrix.matrix[0][0];
axis[1][1] = boltMatrix.matrix[1][0];
axis[1][2] = boltMatrix.matrix[2][0];
axis[0][0] = boltMatrix.matrix[0][1];
axis[0][1] = boltMatrix.matrix[1][1];
axis[0][2] = boltMatrix.matrix[2][1];
axis[2][0] = boltMatrix.matrix[0][2];
axis[2][1] = boltMatrix.matrix[1][2];
axis[2][2] = boltMatrix.matrix[2][2];
return doesBoltExist;
}
#ifdef _IMMERSION
//------------------------------------------------------
ffHandle_t SFxHelper::RegisterForce( const char *force, int channel )
{
return cgi_FF_Register( force, channel );
}
//------------------------------------------------------
void SFxHelper::PlayForce( int entityNum, ffHandle_t ff )
{
cgi_FF_Start( ff, entityNum );
}
#endif // _IMMERSION

84
code/cgame/FxSystem.h Normal file
View File

@@ -0,0 +1,84 @@
#if !defined(CG_LOCAL_H_INC)
#include "cg_local.h"
#endif
#ifndef FX_SYSTEM_H_INC
#define FX_SYSTEM_H_INC
#define irand Q_irand
#define flrand Q_flrand
extern vmCvar_t fx_debug;
extern vmCvar_t fx_freeze;
inline void Vector2Clear(vec2_t a)
{
a[0] = 0.0f;
a[1] = 0.0f;
}
inline void Vector2Set(vec2_t a,float b,float c)
{
a[0] = b;
a[1] = c;
}
inline void Vector2Copy(vec2_t src,vec2_t dst)
{
dst[0] = src[0];
dst[1] = src[1];
}
extern void CG_CalcEntityLerpPositions( centity_t * );
struct SFxHelper
{
int mTime;
int mFrameTime;
float mFloatFrameTime;
void Init();
void AdjustTime( int time );
// These functions are wrapped and used by the fx system in case it makes things a bit more portable
void Print( const char *msg, ... );
// File handling
int OpenFile( const char *path, fileHandle_t *fh, int mode );
int ReadFile( void *data, int len, fileHandle_t fh );
void CloseFile( fileHandle_t fh );
// Sound
void PlaySound( const vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx );
void PlayLocalSound( sfxHandle_t sfx, int channelNum );
int RegisterSound( const char *sound );
#ifdef _IMMERSION
void PlayForce( int entityNum, ffHandle_t ff );
ffHandle_t RegisterForce( const char *force, int channel );
#endif // _IMMERSION
//G2
int GetOriginAxisFromBolt(const centity_t &cent, int modelNum, int boltNum, vec3_t /*out*/origin, vec3_t /*out*/*axis);
// Physics/collision
void Trace( trace_t *tr, vec3_t start, vec3_t min, vec3_t max, vec3_t end, int skipEntNum, int flags );
void G2Trace( trace_t *tr, vec3_t start, vec3_t min, vec3_t max, vec3_t end, int skipEntNum, int flags );
void AddFxToScene( refEntity_t *ent );
void AddLightToScene( vec3_t org, float radius, float red, float green, float blue );
int RegisterShader( const char *shader );
int RegisterModel( const char *model );
void AddPolyToScene( int shader, int count, polyVert_t *verts );
void CameraShake( vec3_t origin, float intensity, int radius, int time );
};
extern SFxHelper theFxHelper;
#endif // FX_SYSTEM_H_INC

2370
code/cgame/FxTemplate.cpp Normal file

File diff suppressed because it is too large Load Diff

1400
code/cgame/FxUtil.cpp Normal file

File diff suppressed because it is too large Load Diff

130
code/cgame/FxUtil.h Normal file
View File

@@ -0,0 +1,130 @@
#if !defined(FX_PRIMITIVES_H_INC)
#include "FxPrimitives.h"
#endif
#ifndef FX_UTIL_H_INC
#define FX_UTIL_H_INC
bool FX_Free( void ); // ditches all active effects;
int FX_Init( void ); // called in CG_Init to purge the fx system.
void FX_Add( bool portal ); // called every cgame frame to add all fx into the scene.
void FX_Stop( void ); // ditches all active effects without touching the templates.
bool FX_ActiveFx(void); // returns whether there are any active or scheduled effects
CParticle *FX_AddParticle( int clientID, const vec3_t org, const vec3_t vel, const vec3_t accel, float gravity,
float size1, float size2, float sizeParm,
float alpha1, float alpha2, float alphaParm,
const vec3_t rgb1, const vec3_t rgb2, float rgbParm,
float rotation, float rotationDelta,
const vec3_t min, const vec3_t max, float elasticity,
int deathID, int impactID,
int killTime, qhandle_t shader, int flags, int modelNum = -1, int boltNum = -1 );
CLine *FX_AddLine( int clientID, vec3_t start, vec3_t end,
float size1, float size2, float sizeParm,
float alpha1, float alpha2, float alphaParm,
vec3_t rgb1, vec3_t rgb2, float rgbParm,
int killTime, qhandle_t shader, int impactFX_id, int flags, int modelNum = -1, int boltNum = -1 );
CElectricity *FX_AddElectricity( int clientID, vec3_t start, vec3_t end, float size1, float size2, float sizeParm,
float alpha1, float alpha2, float alphaParm,
vec3_t sRGB, vec3_t eRGB, float rgbParm,
float chaos, int killTime, qhandle_t shader, int flags, int modelNum = -1, int boltNum = -1 );
CTail *FX_AddTail( int clientID, vec3_t org, vec3_t vel, vec3_t accel,
float size1, float size2, float sizeParm,
float length1, float length2, float lengthParm,
float alpha1, float alpha2, float alphaParm,
vec3_t rgb1, vec3_t rgb2, float rgbParm,
vec3_t min, vec3_t max, float elasticity,
int deathID, int impactID,
int killTime, qhandle_t shader, int flags, int modelNum = -1, int boltNum = -1 );
CCylinder *FX_AddCylinder( int clientID, vec3_t start, vec3_t normal,
float size1s, float size1e, float size1Parm,
float size2s, float size2e, float size2Parm,
float length1, float length2, float lengthParm,
float alpha1, float alpha2, float alphaParm,
vec3_t rgb1, vec3_t rgb2, float rgbParm,
int killTime, qhandle_t shader, int flags, int modelNum = -1, int boltNum = -1 );
CEmitter *FX_AddEmitter( vec3_t org, vec3_t vel, vec3_t accel,
float size1, float size2, float sizeParm,
float alpha1, float alpha2, float alphaParm,
vec3_t rgb1, vec3_t rgb2, float rgbParm,
vec3_t angs, vec3_t deltaAngs,
vec3_t min, vec3_t max, float elasticity,
int deathID, int impactID, int emitterID,
float density, float variance,
int killTime, qhandle_t model, int flags );
CLight *FX_AddLight( vec3_t org, float size1, float size2, float sizeParm,
vec3_t rgb1, vec3_t rgb2, float rgbParm,
int killTime, int flags );
COrientedParticle *FX_AddOrientedParticle( int clientID, vec3_t org, vec3_t norm, vec3_t vel, vec3_t accel,
float size1, float size2, float sizeParm,
float alpha1, float alpha2, float alphaParm,
vec3_t rgb1, vec3_t rgb2, float rgbParm,
float rotation, float rotationDelta,
vec3_t min, vec3_t max, float bounce,
int deathID, int impactID,
int killTime, qhandle_t shader, int flags, int modelNum = -1, int boltNum = -1 );
CPoly *FX_AddPoly( vec3_t *verts, vec2_t *st, int numVerts,
vec3_t vel, vec3_t accel,
float alpha1, float alpha2, float alphaParm,
vec3_t rgb1, vec3_t rgb2, float rgbParm,
vec3_t rotationDelta, float bounce, int motionDelay,
int killTime, qhandle_t shader, int flags );
CFlash *FX_AddFlash( vec3_t origin, vec3_t sRGB, vec3_t eRGB, float rgbParm,
int life, qhandle_t shader, int flags );
// Included for backwards compatibility with CHC and for doing quick programmatic effects.
void FX_AddSprite( vec3_t origin, vec3_t vel, vec3_t accel,
float scale, float dscale,
float sAlpha, float eAlpha,
float rotation, float bounce,
int life, qhandle_t shader, int flags = 0 );
void FX_AddSprite( vec3_t origin, vec3_t vel, vec3_t accel,
float scale, float dscale,
float sAlpha, float eAlpha,
vec3_t sRGB, vec3_t eRGB,
float rotation, float bounce,
int life, qhandle_t shader, int flags = 0 );
void FX_AddLine( vec3_t start, vec3_t end, float stScale,
float width, float dwidth,
float sAlpha, float eAlpha,
int life, qhandle_t shader, int flags = 0 );
void FX_AddLine( vec3_t start, vec3_t end, float stScale,
float width, float dwidth,
float sAlpha, float eAlpha,
vec3_t sRGB, vec3_t eRGB,
int life, qhandle_t shader, int flags = 0 );
void FX_AddQuad( vec3_t origin, vec3_t normal,
vec3_t vel, vec3_t accel,
float sradius, float eradius,
float salpha, float ealpha,
vec3_t sRGB, vec3_t eRGB,
float rotation, int life, qhandle_t shader, int flags = 0 );
CBezier *FX_AddBezier( const vec3_t start, const vec3_t end,
const vec3_t control1, const vec3_t control1Vel,
const vec3_t control2, const vec3_t control2Vel,
float size1, float size2, float sizeParm,
float alpha1, float alpha2, float alphaParm,
const vec3_t sRGB, const vec3_t eRGB, const float rgbParm,
int killTime, qhandle_t shader, int flags = 0 );
#endif //FX_UTIL_H_INC

1792
code/cgame/animtable.h Normal file

File diff suppressed because it is too large Load Diff

2003
code/cgame/cg_camera.cpp Normal file

File diff suppressed because it is too large Load Diff

165
code/cgame/cg_camera.h Normal file
View File

@@ -0,0 +1,165 @@
#ifndef __CG_CAMERA__
#define __CG_CAMERA__
//
// cg_camera.cpp
#define MAX_CAMERA_GROUP_SUBJECTS 16
#define MAX_ACCEL_PER_FRAME 10.0f
#define MAX_SHAKE_INTENSITY 16.0f
#define CAMERA_DEFAULT_FOV 90.0f
#define CAMERA_WIDESCREEN_FOV 120.0f
#define BAR_DURATION 1000.0f
#define BAR_RATIO 48.0f
#define CAMERA_MOVING 0x00000001
#define CAMERA_PANNING 0x00000002
#define CAMERA_ZOOMING 0x00000004
#define CAMERA_BAR_FADING 0x00000008
#define CAMERA_FADING 0x00000010
#define CAMERA_FOLLOWING 0x00000020
#define CAMERA_TRACKING 0x00000040
#define CAMERA_ROFFING 0x00000080
#define CAMERA_SMOOTHING 0x00000100
#define CAMERA_CUT 0x00000200
#define CAMERA_ACCEL 0x00000400
// NOTE!! This structure is now saved out as part of the load/save game, so tell me if you put any pointers or
// other goofy crap in... -Ste
//
typedef struct camera_s
{
//Position / Facing information
vec3_t origin;
vec3_t angles;
vec3_t origin2;
vec3_t angles2;
//Movement information
float move_duration;
float move_time;
int move_type; //CMOVE_LINEAR, CMOVE_BEZIER
//FOV information
float FOV;
float FOV2;
float FOV_duration;
float FOV_time;
float FOV_vel;
float FOV_acc;
//Pan information
float pan_time;
float pan_duration;
//Following information
char cameraGroup[MAX_QPATH];
float cameraGroupZOfs;
char cameraGroupTag[MAX_QPATH];
vec3_t subjectPos;
float subjectSpeed;
float followSpeed;
qboolean followInitLerp;
float distance;
qboolean distanceInitLerp;
//int aimEntNum;//FIXME: remove
//Tracking information
int trackEntNum;
vec3_t trackToOrg;
vec3_t moveDir;
float speed;
float initSpeed;
float trackInitLerp;
int nextTrackEntUpdateTime;
//Cine-bar information
float bar_alpha;
float bar_alpha_source;
float bar_alpha_dest;
float bar_time;
float bar_height_source;
float bar_height_dest;
float bar_height;
vec4_t fade_color;
vec4_t fade_source;
vec4_t fade_dest;
float fade_time;
float fade_duration;
//State information
int info_state;
//Shake information
float shake_intensity;
int shake_duration;
int shake_start;
//Smooth information
float smooth_intensity;
int smooth_duration;
int smooth_start;
vec3_t smooth_origin;
bool smooth_active; // means smooth_origin and angles are valid
// ROFF information
char sRoff[MAX_QPATH]; // name of a cached roff
int roff_frame; // current frame in the roff data
int next_roff_time; // time when it's ok to apply the next roff frame
#ifdef _XBOX
qboolean widescreen;
#endif
} camera_t;
extern bool in_camera;
extern camera_t client_camera;
void CGCam_Init( void );
void CGCam_Enable( void );
void CGCam_Disable( void );
#ifdef _XBOX
void CGCam_SetWidescreen( qboolean widescreen );
#endif
void CGCam_SetPosition( vec3_t org );
void CGCam_SetAngles( vec3_t ang );
void CGCam_SetFOV( float FOV );
#ifdef _XBOX
void CGCam_SetFOV2( float FOV2 );
#endif
void CGCam_Zoom( float FOV, float duration );
//void CGCam_Pan( vec3_t dest, float duration );
void CGCam_Pan( vec3_t dest, vec3_t panDirection, float duration );
void CGCam_Move( vec3_t dest, float duration );
void CGCam_Fade( vec4_t source, vec4_t dest, float duration );
void CGCam_UpdateFade( void );
void CGCam_Update( void );
void CGCam_RenderScene( void );
void CGCam_DrawWideScreen( void );
void CGCam_Shake( float intensity, int duration );
void CGCam_Shake( float intensity, int duration, bool rumble );
void CGCam_UpdateShake( vec3_t origin, vec3_t angles );
void CGCam_Follow( const char *cameraGroup, float speed, float initLerp );
void CGCam_Track( const char *trackName, float speed, float initLerp );
void CGCam_Distance( float distance, float initLerp );
void CGCam_Roll( float dest, float duration );
void CGCam_StartRoff( char *roff );
void CGCam_Smooth( float intensity, int duration );
void CGCam_UpdateSmooth( vec3_t origin, vec3_t angles );
#endif //__CG_CAMERA__

View File

@@ -0,0 +1,324 @@
// cg_consolecmds.c -- text commands typed in at the local console, or
// executed by a key binding
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h" //just for cgs....
void CG_TargetCommand_f( void );
extern qboolean player_locked;
extern void CMD_CGCam_Disable( void );
void CG_NextInventory_f( void );
void CG_PrevInventory_f( void );
void CG_NextForcePower_f( void );
void CG_PrevForcePower_f( void );
void CG_LoadHud_f( void );
/*
====================
CG_ColorFromString
====================
*/
/*
static void CG_SetColor_f( void) {
if (cgi_Argc()==4)
{
g_entities[0].client->renderInfo.customRGBA[0] = atoi( CG_Argv(1) );
g_entities[0].client->renderInfo.customRGBA[1] = atoi( CG_Argv(2) );
g_entities[0].client->renderInfo.customRGBA[2] = atoi( CG_Argv(3) );
}
if (cgi_Argc()==2)
{
int val = atoi( CG_Argv(1) );
if ( val < 1 || val > 7 ) {
g_entities[0].client->renderInfo.customRGBA[0] = 255;
g_entities[0].client->renderInfo.customRGBA[1] = 255;
g_entities[0].client->renderInfo.customRGBA[2] = 255;
return;
}
g_entities[0].client->renderInfo.customRGBA[0]=0;
g_entities[0].client->renderInfo.customRGBA[1]=0;
g_entities[0].client->renderInfo.customRGBA[2]=0;
if ( val & 1 ) {
g_entities[0].client->renderInfo.customRGBA[2] = 255;
}
if ( val & 2 ) {
g_entities[0].client->renderInfo.customRGBA[1] = 255;
}
if ( val & 4 ) {
g_entities[0].client->renderInfo.customRGBA[0] = 255;
}
}
}
*/
/*
=============
CG_Viewpos_f
Debugging command to print the current position
=============
*/
static void CG_Viewpos_f (void) {
CG_Printf ("%s (%i %i %i) : %i\n", cgs.mapname, (int)cg.refdef.vieworg[0],
(int)cg.refdef.vieworg[1], (int)cg.refdef.vieworg[2],
(int)cg.refdefViewAngles[YAW]);
}
void CG_WriteCam_f (void)
{
char text[1024];
char *targetname;
static int numCams;
numCams++;
targetname = (char *)CG_Argv(1);
if( !targetname || !targetname[0] )
{
targetname = "nameme!";
}
CG_Printf( "Camera #%d ('%s') written to: ", numCams, targetname );
sprintf( text, "//entity %d\n{\n\"classname\" \"ref_tag\"\n\"targetname\" \"%s\"\n\"origin\" \"%i %i %i\"\n\"angles\" \"%i %i %i\"\n\"fov\" \"%i\"\n}\n", numCams, targetname, (int)cg.refdef.vieworg[0], (int)cg.refdef.vieworg[1], (int)cg.refdef.vieworg[2], (int)cg.refdefViewAngles[0], (int)cg.refdefViewAngles[1], (int)cg.refdefViewAngles[2], cg_fov.integer );
gi.WriteCam( text );
}
void Lock_Disable ( void )
{
player_locked = qfalse;
}
extern float cg_zoomFov; //from cg_view.cpp
void CG_ToggleBinoculars( void )
{
if ( in_camera || !cg.snap )
{
return;
}
if ( cg.zoomMode == 0 || cg.zoomMode >= 2 ) // not zoomed or currently zoomed with the disruptor or LA goggles
{
if ( (cg.snap->ps.saber[0].Active() && cg.snap->ps.saberInFlight) || cg.snap->ps.stats[STAT_HEALTH] <= 0)
{//can't select binoculars when throwing saber
//FIXME: indicate this to the player
return;
}
if ( cg.snap->ps.viewEntity || ( cg_entities[cg.snap->ps.clientNum].currentState.eFlags & ( EF_LOCKED_TO_WEAPON | EF_IN_ATST )))
{
// can't zoom when you have a viewEntity or driving an atst or in an emplaced gun
return;
}
cg.zoomMode = 1;
cg.zoomLocked = qfalse;
if ( cg.snap->ps.batteryCharge )
{
// when you have batteries, you can actually zoom in
cg_zoomFov = 40.0f;
}
else if ( cg.overrides.active & CG_OVERRIDE_FOV )
{
cg_zoomFov = cg.overrides.fov;
}
else
{
cg_zoomFov = cg_fov.value;
}
cgi_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.media.zoomStart );
#ifdef _IMMERSION
cgi_FF_Start( cgs.media.zoomStartForce, cg.snap->ps.clientNum );
#endif // _IMMERSION
}
else
{
cg.zoomMode = 0;
cg.zoomTime = cg.time;
cgi_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.media.zoomEnd );
#ifdef _IMMERSION
cgi_FF_Start( cgs.media.zoomEndForce, cg.snap->ps.clientNum );
#endif // _IMMERSION
}
}
void CG_ToggleLAGoggles( void )
{
if ( in_camera || !cg.snap)
{
return;
}
if ( cg.zoomMode == 0 || cg.zoomMode < 3 ) // not zoomed or currently zoomed with the disruptor or regular binoculars
{
if ( (cg.snap->ps.saber[0].Active() && cg.snap->ps.saberInFlight) || cg.snap->ps.stats[STAT_HEALTH] <= 0 )
{//can't select binoculars when throwing saber
//FIXME: indicate this to the player
return;
}
if ( cg.snap->ps.viewEntity || ( cg_entities[cg.snap->ps.clientNum].currentState.eFlags & ( EF_LOCKED_TO_WEAPON | EF_IN_ATST )))
{
// can't zoom when you have a viewEntity or driving an atst or in an emplaced gun
return;
}
cg.zoomMode = 3;
cg.zoomLocked = qfalse;
if ( cg.overrides.active & CG_OVERRIDE_FOV )
{
cg_zoomFov = cg.overrides.fov;
}
else
{
cg_zoomFov = cg_fov.value; // does not zoom!!
}
cgi_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.media.zoomStart );
#ifdef _IMMERSION
cgi_FF_Start( cgs.media.zoomStartForce, cg.snap->ps.clientNum );
#endif // _IMMERSION
}
else
{
cg.zoomMode = 0;
cg.zoomTime = cg.time;
cgi_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.media.zoomEnd );
#ifdef _IMMERSION
cgi_FF_Start( cgs.media.zoomEndForce, cg.snap->ps.clientNum );
#endif // _IMMERSION
}
}
static void CG_InfoDown_f( void ) {
// cg.showInformation = qtrue;
}
static void CG_InfoUp_f( void )
{
// cg.showInformation = qfalse;
}
typedef struct {
char *cmd;
void (*function)(void);
} consoleCommand_t;
static consoleCommand_t commands[] = {
{ "testmodel", CG_TestModel_f },
{ "nextframe", CG_TestModelNextFrame_f },
{ "prevframe", CG_TestModelPrevFrame_f },
{ "nextskin", CG_TestModelNextSkin_f },
{ "prevskin", CG_TestModelPrevSkin_f },
/*
Ghoul2 Insert Start
*/
{ "testG2Model", CG_TestG2Model_f},
{ "testsurface", CG_TestModelSurfaceOnOff_f },
{ "testanglespre", CG_TestModelSetAnglespre_f},
{ "testanglespost", CG_TestModelSetAnglespost_f},
{ "testanimate", CG_TestModelAnimate_f},
{ "testlistbones", CG_ListModelBones_f},
{ "testlistsurfaces", CG_ListModelSurfaces_f},
/*
Ghoul2 Insert End
*/
{ "viewpos", CG_Viewpos_f },
{ "writecam", CG_WriteCam_f },
{ "+info", CG_InfoDown_f },
{ "-info", CG_InfoUp_f },
{ "weapnext", CG_NextWeapon_f },
{ "weapprev", CG_PrevWeapon_f },
{ "weapon", CG_Weapon_f },
{ "tcmd", CG_TargetCommand_f },
{ "cam_disable", CMD_CGCam_Disable }, //gets out of camera mode for debuggin
{ "cam_enable", CGCam_Enable }, //gets into camera mode for precise camera placement
{ "lock_disable", Lock_Disable }, //player can move now
{ "zoom", CG_ToggleBinoculars },
{ "la_zoom", CG_ToggleLAGoggles },
{ "invnext", CG_NextInventory_f },
{ "invprev", CG_PrevInventory_f },
{ "forcenext", CG_NextForcePower_f },
{ "forceprev", CG_PrevForcePower_f },
{ "loadhud", CG_LoadHud_f },
{ "dpweapnext", CG_DPNextWeapon_f },
{ "dpweapprev", CG_DPPrevWeapon_f },
{ "dpinvnext", CG_DPNextInventory_f },
{ "dpinvprev", CG_DPPrevInventory_f },
{ "dpforcenext", CG_DPNextForcePower_f },
{ "dpforceprev", CG_DPPrevForcePower_f },
// { "color", CG_SetColor_f },
};
//extern menuDef_t *menuScoreboard;
void Menu_Reset();
void CG_LoadHud_f( void)
{
const char *hudSet;
// cgi_UI_String_Init();
// cgi_UI_Menu_Reset();
hudSet = cg_hudFiles.string;
if (hudSet[0] == '\0')
{
hudSet = "ui/jahud.txt";
}
CG_LoadMenus(hudSet);
// menuScoreboard = NULL;
}
/*
=================
CG_ConsoleCommand
The string has been tokenized and can be retrieved with
Cmd_Argc() / Cmd_Argv()
=================
*/
qboolean CG_ConsoleCommand( void ) {
const char *cmd;
int i;
cmd = CG_Argv(0);
for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ ) {
if ( !Q_stricmp( cmd, commands[i].cmd ) ) {
commands[i].function();
return qtrue;
}
}
return qfalse;
}
/*
=================
CG_InitConsoleCommands
Let the client system know about all of our commands
so it can perform tab completion
=================
*/
void CG_InitConsoleCommands( void ) {
int i;
for ( i = 0 ; i < sizeof( commands ) / sizeof( commands[0] ) ; i++ ) {
cgi_AddCommand( commands[i].cmd );
}
}

654
code/cgame/cg_credits.cpp Normal file
View File

@@ -0,0 +1,654 @@
// Filename:- cg_credits.cpp
//
// module for end credits code
// this line must stay at top so the whole PCH thing works...
//
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#define max(a,b) (((a) > (b)) ? (a) : (b))
#define fCARD_FADESECONDS 1.0f // fade up time, also fade down time
#define fCARD_SUSTAINSECONDS 2.0f // hold time before fade down
#define fLINE_SECONDTOSCROLLUP 15.0f // how long one line takes to scroll up the screen
#define MAX_LINE_BYTES 2048
qhandle_t ghFontHandle = 0;
float gfFontScale = 1.00f;
vec4_t gv4Color = {0};
struct StringAndSize_t
{
int iStrLenPixels;
string str;
StringAndSize_t()
{
iStrLenPixels = -1;
str = "";
}
StringAndSize_t(const char *psString)
{
iStrLenPixels = -1;
str = psString;
}
StringAndSize_t & operator = (const char *psString)
{
iStrLenPixels = -1;
str = psString;
return *this;
}
const char *c_str(void)
{
return str.c_str();
}
int GetPixelLength(void)
{
if (iStrLenPixels == -1)
{
iStrLenPixels = cgi_R_Font_StrLenPixels(str.c_str(), ghFontHandle, gfFontScale);
}
return iStrLenPixels;
}
bool IsEmpty(void)
{
return str.empty();
}
};
struct CreditCard_t
{
int iTime;
StringAndSize_t strTitle;
vector<StringAndSize_t> vstrText;
CreditCard_t()
{
iTime = -1; // flag "not set yet"
}
};
struct CreditLine_t
{
int iLine;
StringAndSize_t strText;
vector<StringAndSize_t> vstrText;
bool bDotted;
};
typedef list <CreditLine_t> CreditLines_t;
typedef list <CreditCard_t> CreditCards_t;
struct CreditData_t
{
int iStartTime;
CreditCards_t CreditCards;
CreditLines_t CreditLines;
bool Running(void)
{
return !!( CreditCards.size() || CreditLines.size() );
}
};
CreditData_t CreditData;
static LPCSTR Capitalize(LPCSTR psTest)
{
static char sTemp[MAX_LINE_BYTES];
Q_strncpyz(sTemp, psTest, sizeof(sTemp));
// if (!cgi_Language_IsAsian()) // we don't have asian credits, so this is ok to do now
{
strupr(sTemp); // capitalise titles (if not asian!!!!)
}
return sTemp;
}
// cope with hyphenated names and initials (awkward gits)...
//
static bool CountsAsWhiteSpaceForCaps( unsigned /* avoid euro-char sign-extend assert within isspace()*/char c )
{
return !!(isspace(c) || c == '-' || c == '.' || c == '(' || c == ')' || c=='\'');
}
static LPCSTR UpperCaseFirstLettersOnly(LPCSTR psTest)
{
static char sTemp[MAX_LINE_BYTES];
Q_strncpyz(sTemp, psTest, sizeof(sTemp));
// if (!cgi_Language_IsAsian()) // we don't have asian credits, so this is ok to do now
{
strlwr(sTemp);
char *p = sTemp;
while (*p)
{
while (*p && CountsAsWhiteSpaceForCaps(*p)) p++;
if (*p)
{
*p = toupper(*p);
while (*p && !CountsAsWhiteSpaceForCaps(*p)) p++;
}
}
}
// now restore any weird stuff...
//
char *p = strstr(sTemp," Mc"); // eg "Mcfarrell" should be "McFarrell"
if (p && isalpha(p[3]))
{
p[3] = toupper(p[3]);
}
p = strstr(sTemp," O'"); // eg "O'flaherty" should be "O'Flaherty" (this is probably done automatically now, but wtf.
if (p && isalpha(p[3]))
{
p[3] = toupper(p[3]);
}
p = strstr(sTemp,"Lucasarts");
if (p)
{
p[5] = 'A'; // capitalise the 'A' in LucasArts (jeez...)
}
return sTemp;
}
static const char *GetSubString(string &strResult)
{
static char sTemp[MAX_LINE_BYTES];
if (!strlen(strResult.c_str()))
return NULL;
strncpy(sTemp,strResult.c_str(),sizeof(sTemp)-1);
sTemp[sizeof(sTemp)-1]='\0';
char *psSemiColon = strchr(sTemp,';');
if ( psSemiColon)
{
*psSemiColon = '\0';
strResult.erase(0,(psSemiColon-sTemp)+1);
}
else
{
// no semicolon found, probably last entry? (though i think even those have them on, oh well)
//
strResult.erase();
}
return sTemp;
}
// sort entries by their last name (starts at back of string and moves forward until start or just before whitespace)
// ...
static int SortBySurname(const void *elem1, const void *elem2)
{
StringAndSize_t *p1 = (StringAndSize_t *) elem1;
StringAndSize_t *p2 = (StringAndSize_t *) elem2;
LPCSTR psSurName1 = p1->c_str() + (strlen(p1->c_str())-1);
LPCSTR psSurName2 = p2->c_str() + (strlen(p2->c_str())-1);
while (psSurName1 > p1->c_str() && !isspace(*psSurName1)) psSurName1--;
while (psSurName2 > p2->c_str() && !isspace(*psSurName2)) psSurName2--;
if (isspace(*psSurName1)) psSurName1++;
if (isspace(*psSurName2)) psSurName2++;
return stricmp(psSurName1, psSurName2);
}
void CG_Credits_Init( LPCSTR psStripReference, vec4_t *pv4Color)
{
// Play the light side end credits music.
if ( g_entities[0].client->sess.mission_objectives[0].status != 2 )
{
cgi_S_StartBackgroundTrack( "music/endcredits.mp3", NULL, false );
}
// Play the dark side end credits music.
else
{
cgi_S_StartBackgroundTrack( "music/vjun3/vjun3_explore.mp3", NULL, false );
}
// could make these into parameters later, but for now...
//
ghFontHandle = cgs.media.qhFontMedium;
gfFontScale = 1.0f;
memcpy(gv4Color,pv4Color,sizeof(gv4Color)); // memcpy so we can poke into alpha channel
// first, ask the strlen of the final string...
//
int iStrLen = cgi_SP_GetStringTextString( psStripReference, NULL, 0 );
if (!iStrLen)
{
#ifndef FINAL_BUILD
Com_Printf("WARNING: CG_Credits_Init(): invalid text key :'%s'\n", psStripReference);
#endif
return;
}
//
// malloc space to hold it...
//
char *psMallocText = (char *) cgi_Z_Malloc( iStrLen+1, TAG_TEMP_WORKSPACE );
//
// now get the string...
//
iStrLen = cgi_SP_GetStringTextString( psStripReference, psMallocText, iStrLen+1 );
//ensure we found a match
if (!iStrLen)
{
assert(0); // should never get here now, but wtf?
cgi_Z_Free(psMallocText);
#ifndef FINAL_BUILD
Com_Printf("WARNING: CG_Credits_Init(): invalid text key :'%s'\n", psStripReference);
#endif
return;
}
// read whole string in and process as cards, lines etc...
//
typedef enum
{
eNothing = 0,
eLine,
eDotEntry,
eTitle,
eCard,
eFinished,
} Mode_e;
Mode_e eMode = eNothing;
qboolean bCardsFinished = qfalse;
int iLineNumber = 0;
const char *psTextParse = psMallocText;
while (*psTextParse != NULL)
{
// read a line...
//
char sLine[MAX_LINE_BYTES];
sLine[0]='\0';
qboolean bWasCommand = qtrue;
while (1)
{
qboolean bIsTrailingPunctuation;
int iAdvanceCount;
unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(psTextParse, &iAdvanceCount, &bIsTrailingPunctuation);
psTextParse += iAdvanceCount;
// concat onto string so far...
//
if (uiLetter == 32 && sLine[0] == '\0')
{
continue; // unless it's a space at the start of a line, in which case ignore it.
}
if (uiLetter == '\n' || uiLetter == '\0' )
{
// have we got a command word?...
//
if (!strnicmp(sLine,"(#",2))
{
// yep...
//
if (!stricmp(sLine, "(#CARD)"))
{
if (!bCardsFinished)
{
eMode = eCard;
}
else
{
#ifndef FINAL_BUILD
Com_Printf( S_COLOR_YELLOW "CG_Credits_Init(): No current support for cards after scroll!\n" );
#endif
eMode = eNothing;
}
break;
}
else
if (!stricmp(sLine, "(#TITLE)"))
{
eMode = eTitle;
bCardsFinished = qtrue;
break;
}
else
if (!stricmp(sLine, "(#LINE)"))
{
eMode = eLine;
bCardsFinished = qtrue;
break;
}
else
if (!stricmp(sLine, "(#DOTENTRY)"))
{
eMode = eDotEntry;
bCardsFinished = qtrue;
break;
}
else
{
#ifndef FINAL_BUILD
Com_Printf( S_COLOR_YELLOW "CG_Credits_Init(): bad keyword \"%s\"!\n", sLine );
#endif
eMode = eNothing;
}
}
else
{
// I guess not...
//
bWasCommand = qfalse;
break;
}
}
else
{
// must be a letter...
//
if (uiLetter > 255)
{
assert(0); // this means we're attempting to display asian credits, and we don't
// support these now because the auto-capitalisation rules etc would have to
// be inhibited.
Q_strcat(sLine, sizeof(sLine), va("%c%c",uiLetter >> 8, uiLetter & 0xFF));
}
else
{
Q_strcat(sLine, sizeof(sLine), va("%c",uiLetter & 0xFF));
}
}
}
// command?...
//
if (bWasCommand)
{
// this'll just be a mode change, so ignore...
//
}
else
{
// else we've got some text to display...
//
switch (eMode)
{
case eNothing: break;
case eLine:
{
CreditLine_t CreditLine;
CreditLine.iLine = iLineNumber++;
CreditLine.strText = sLine;
CreditData.CreditLines.push_back( CreditLine );
}
break;
case eDotEntry:
{
CreditLine_t CreditLine;
CreditLine.iLine = iLineNumber;
CreditLine.bDotted = true;
string strResult(sLine);
const char *p;
while ((p=GetSubString(strResult)) != NULL)
{
if (CreditLine.strText.IsEmpty())
{
CreditLine.strText = p;
}
else
{
CreditLine.vstrText.push_back( UpperCaseFirstLettersOnly(p) );
}
}
if (!CreditLine.strText.IsEmpty() && CreditLine.vstrText.size())
{
// sort entries RHS dotted entries by alpha...
//
qsort(&CreditLine.vstrText[0], CreditLine.vstrText.size(), sizeof(CreditLine.vstrText[0]), SortBySurname);
CreditData.CreditLines.push_back( CreditLine );
iLineNumber += CreditLine.vstrText.size();
}
}
break;
case eTitle:
{
iLineNumber++; // leading blank line
CreditLine_t CreditLine;
CreditLine.iLine = iLineNumber++;
CreditLine.strText = Capitalize(sLine);
CreditData.CreditLines.push_back( CreditLine );
iLineNumber++; // trailing blank line
break;
}
case eCard:
{
CreditCard_t CreditCard;
string strResult(sLine);
const char *p;
while ((p=GetSubString(strResult)) != NULL)
{
if (CreditCard.strTitle.IsEmpty())
{
CreditCard.strTitle = Capitalize( p );
}
else
{
CreditCard.vstrText.push_back( UpperCaseFirstLettersOnly( p ) );
}
}
if (!CreditCard.strTitle.IsEmpty())
{
// sort entries by alpha...
//
qsort(&CreditCard.vstrText[0], CreditCard.vstrText.size(), sizeof(CreditCard.vstrText[0]), SortBySurname);
CreditData.CreditCards.push_back(CreditCard);
}
}
break;
}
}
}
cgi_Z_Free(psMallocText);
CreditData.iStartTime = cg.time;
}
qboolean CG_Credits_Running( void )
{
return CreditData.Running();
}
// returns qtrue if still drawing...
//
qboolean CG_Credits_Draw( void )
{
if ( CG_Credits_Running() )
{
const int iFontHeight = (int) (1.5f * (float) cgi_R_Font_HeightPixels(ghFontHandle, gfFontScale)); // taiwanese & japanese need 1.5 fontheight spacing
// cgi_R_SetColor( *gpv4Color );
// display cards first...
//
if (CreditData.CreditCards.size())
{
// grab first card off the list (we know there's at least one here, so...)
//
CreditCard_t &CreditCard = (*CreditData.CreditCards.begin());
if (CreditCard.iTime == -1)
{
// onceonly time init...
//
CreditCard.iTime = cg.time;
}
// play with the alpha channel for fade up/down...
//
const float fMilliSecondsElapsed = cg.time - CreditCard.iTime;
const float fSecondsElapsed = fMilliSecondsElapsed / 1000.0f;
if (fSecondsElapsed < fCARD_FADESECONDS)
{
// fading up...
//
gv4Color[3] = fSecondsElapsed / fCARD_FADESECONDS;
// OutputDebugString(va("fade up: %f\n",gv4Color[3]));
}
else
if (fSecondsElapsed > fCARD_FADESECONDS + fCARD_SUSTAINSECONDS)
{
// fading down...
//
const float fFadeDownSeconds = fSecondsElapsed - (fCARD_FADESECONDS + fCARD_SUSTAINSECONDS);
gv4Color[3] = 1.0f - (fFadeDownSeconds / fCARD_FADESECONDS);
// OutputDebugString(va("fade dw: %f\n",gv4Color[3]));
}
else
{
gv4Color[3] = 1.0f;
// OutputDebugString(va("normal: %f\n",gv4Color[3]));
}
if (gv4Color[3] < 0.0f)
gv4Color[3] = 0.0f; // ... otherwise numbers that have dipped slightly -ve flash up fullbright after fade down
//
// how many lines is it?
//
int iLines = CreditCard.vstrText.size() + 2; // +2 for title itself & one seperator line
//
int iYpos = (SCREEN_HEIGHT - (iLines * iFontHeight))/2;
//
// draw it, title first...
//
int iWidth = CreditCard.strTitle.GetPixelLength();
int iXpos = (SCREEN_WIDTH - iWidth)/2;
cgi_R_Font_DrawString(iXpos, iYpos, CreditCard.strTitle.c_str(), gv4Color, ghFontHandle, -1, gfFontScale);
//
iYpos += iFontHeight*2; // skip blank line then move to main pos
//
for (int i=0; i<CreditCard.vstrText.size(); i++)
{
StringAndSize_t &StringAndSize = CreditCard.vstrText[i];
iWidth = StringAndSize.GetPixelLength();
iXpos = (SCREEN_WIDTH - iWidth)/2;
cgi_R_Font_DrawString(iXpos, iYpos, StringAndSize.c_str(), gv4Color, ghFontHandle, -1, gfFontScale);
iYpos += iFontHeight;
}
// next card?...
//
if (fSecondsElapsed > fCARD_FADESECONDS + fCARD_SUSTAINSECONDS + fCARD_FADESECONDS)
{
// yep, so erase the first entry (which will trigger the next one to be initialised on re-entry)...
//
CreditData.CreditCards.erase( CreditData.CreditCards.begin() );
if (!CreditData.CreditCards.size())
{
// all cards gone, so re-init timer for lines...
//
CreditData.iStartTime = cg.time;
}
}
//
return qtrue;
}
else
{
// doing scroll text...
//
if (CreditData.CreditLines.size())
{
// process all lines...
//
const float fMilliSecondsElapsed = cg.time - CreditData.iStartTime;
const float fSecondsElapsed = fMilliSecondsElapsed / 1000.0f;
bool bEraseOccured = false;
for (CreditLines_t::iterator it = CreditData.CreditLines.begin(); it != CreditData.CreditLines.end(); bEraseOccured ? it : ++it)
{
CreditLine_t &CreditLine = (*it);
bEraseOccured = false;
static const float fPixelsPerSecond = ((float)SCREEN_HEIGHT / fLINE_SECONDTOSCROLLUP);
int iYpos = SCREEN_HEIGHT + (CreditLine.iLine * iFontHeight);
iYpos-= (int) (fPixelsPerSecond * fSecondsElapsed);
int iTextLinesThisItem = max(CreditLine.vstrText.size(),1);
if (iYpos + (iTextLinesThisItem * iFontHeight) < 0)
{
// scrolled off top of screen, so erase it...
//
it = CreditData.CreditLines.erase( it );
bEraseOccured = true;
}
else
if (iYpos < SCREEN_HEIGHT)
{
// onscreen, so print it...
//
bool bIsDotted = !!CreditLine.vstrText.size(); // eg "STUNTS ...................... MR ED"
int iWidth = CreditLine.strText.GetPixelLength();
int iXpos = bIsDotted ? 54 : (SCREEN_WIDTH - iWidth)/2;
gv4Color[3] = 1.0f;
cgi_R_Font_DrawString(iXpos, iYpos, CreditLine.strText.c_str(), gv4Color, ghFontHandle, -1, gfFontScale);
// now print any dotted members...
//
for (int i=0; i<CreditLine.vstrText.size(); i++)
{
StringAndSize_t &StringAndSize = CreditLine.vstrText[i];
iWidth = StringAndSize.GetPixelLength();
iXpos = (SCREEN_WIDTH-4 - iWidth) - 50;
cgi_R_Font_DrawString(iXpos, iYpos, StringAndSize.c_str(), gv4Color, ghFontHandle, -1, gfFontScale);
iYpos += iFontHeight;
}
}
}
return qtrue;
}
}
}
return qfalse;
}
////////////////////// eof /////////////////////

4418
code/cgame/cg_draw.cpp Normal file

File diff suppressed because it is too large Load Diff

491
code/cgame/cg_drawtools.cpp Normal file
View File

@@ -0,0 +1,491 @@
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
/*
================
CG_DrawSides
Coords are virtual 640x480
================
*/
void CG_DrawSides(float x, float y, float w, float h, float size) {
//size *= cgs.screenXScale;
cgi_R_DrawStretchPic( x, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader );
cgi_R_DrawStretchPic( x + w - size, y, size, h, 0, 0, 0, 0, cgs.media.whiteShader );
}
void CG_DrawTopBottom(float x, float y, float w, float h, float size) {
//size *= cgs.screenYScale;
cgi_R_DrawStretchPic( x, y, w, size, 0, 0, 0, 0, cgs.media.whiteShader );
cgi_R_DrawStretchPic( x, y + h - size, w, size, 0, 0, 0, 0, cgs.media.whiteShader );
}
/*
================
CG_DrawRect
Coordinates are 640*480 virtual values
=================
*/
void CG_DrawRect( float x, float y, float width, float height, float size, const float *color ) {
cgi_R_SetColor( color );
CG_DrawTopBottom(x, y, width, height, size);
CG_DrawSides(x, y, width, height, size);
cgi_R_SetColor( NULL );
}
/*
================
CG_FillRect
Coordinates are 640*480 virtual values
=================
*/
void CG_FillRect( float x, float y, float width, float height, const float *color ) {
cgi_R_SetColor( color );
cgi_R_DrawStretchPic( x, y, width, height, 0, 0, 0, 0, cgs.media.whiteShader);
cgi_R_SetColor( NULL );
}
/*
================
CG_Scissor
Coordinates are 640*480 virtual values
=================
*/
void CG_Scissor( float x, float y, float width, float height)
{
cgi_R_Scissor( x, y, width, height);
}
/*
================
CG_DrawPic
Coordinates are 640*480 virtual values
A width of 0 will draw with the original image width
=================
*/
void CG_DrawPic( float x, float y, float width, float height, qhandle_t hShader ) {
cgi_R_DrawStretchPic( x, y, width, height, 0, 0, 1, 1, hShader );
}
/*
================
CG_DrawPic2
Coordinates are 640*480 virtual values
A width of 0 will draw with the original image width
Can also specify the exact texture coordinates
=================
*/
void CG_DrawPic2( float x, float y, float width, float height, float s1, float t1, float s2, float t2, qhandle_t hShader )
{
cgi_R_DrawStretchPic( x, y, width, height, s1, t1, s2, t2, hShader );
}
/*
================
CG_DrawRotatePic
Coordinates are 640*480 virtual values
A width of 0 will draw with the original image width
rotates around the upper right corner of the passed in point
=================
*/
void CG_DrawRotatePic( float x, float y, float width, float height,float angle, qhandle_t hShader ) {
cgi_R_DrawRotatePic( x, y, width, height, 0, 0, 1, 1, angle, hShader );
}
/*
================
CG_DrawRotatePic2
Coordinates are 640*480 virtual values
A width of 0 will draw with the original image width
Actually rotates around the center point of the passed in coordinates
=================
*/
void CG_DrawRotatePic2( float x, float y, float width, float height,float angle, qhandle_t hShader ) {
cgi_R_DrawRotatePic2( x, y, width, height, 0, 0, 1, 1, angle, hShader );
}
/*
===============
CG_DrawChar
Coordinates and size in 640*480 virtual screen size
===============
*/
void CG_DrawChar( int x, int y, int width, int height, int ch ) {
int row, col;
float frow, fcol;
float size;
float ax, ay, aw, ah;
ch &= 255;
if ( ch == ' ' ) {
return;
}
ax = x;
ay = y;
aw = width;
ah = height;
row = ch>>4;
col = ch&15;
/*
frow = row*0.0625;
fcol = col*0.0625;
size = 0.0625;
cgi_R_DrawStretchPic( ax, ay, aw, ah,
fcol, frow,
fcol + size, frow + size,
cgs.media.charsetShader );
*/
float size2;
frow = row*0.0625;
fcol = col*0.0625;
size = 0.03125;
size2 = 0.0625;
cgi_R_DrawStretchPic( ax, ay, aw, ah, fcol, frow, fcol + size, frow + size2,
cgs.media.charsetShader );
}
/*
==================
CG_DrawStringExt
Draws a multi-colored string with a drop shadow, optionally forcing
to a fixed color.
Coordinates are at 640 by 480 virtual resolution
==================
*/
void CG_DrawStringExt( int x, int y, const char *string, const float *setColor,
qboolean forceColor, qboolean shadow, int charWidth, int charHeight ) {
vec4_t color;
const char *s;
int xx;
// draw the drop shadow
if (shadow) {
color[0] = color[1] = color[2] = 0;
color[3] = setColor[3];
cgi_R_SetColor( color );
s = string;
xx = x;
while ( *s ) {
if ( Q_IsColorString( s ) ) {
s += 2;
continue;
}
CG_DrawChar( xx + 2, y + 2, charWidth, charHeight, *s );
xx += charWidth;
s++;
}
}
// draw the colored text
s = string;
xx = x;
cgi_R_SetColor( setColor );
while ( *s ) {
if ( Q_IsColorString( s ) ) {
if ( !forceColor ) {
memcpy( color, g_color_table[ColorIndex(*(s+1))], sizeof( color ) );
color[3] = setColor[3];
cgi_R_SetColor( color );
}
s += 2;
continue;
}
CG_DrawChar( xx, y, charWidth, charHeight, *s );
xx += charWidth;
s++;
}
cgi_R_SetColor( NULL );
}
void CG_DrawSmallStringColor( int x, int y, const char *s, vec4_t color ) {
CG_DrawStringExt( x, y, s, color, qtrue, qfalse, SMALLCHAR_WIDTH, SMALLCHAR_HEIGHT );
}
/*
=================
CG_DrawStrlen
Returns character count, skiping color escape codes
=================
*/
int CG_DrawStrlen( const char *str ) {
const char *s = str;
int count = 0;
while ( *s ) {
if ( Q_IsColorString( s ) ) {
s += 2;
} else {
count++;
s++;
}
}
return count;
}
/*
=============
CG_TileClearBox
This repeats a 64*64 tile graphic to fill the screen around a sized down
refresh window.
=============
*/
static void CG_TileClearBox( int x, int y, int w, int h, qhandle_t hShader ) {
float s1, t1, s2, t2;
s1 = x/64.0;
t1 = y/64.0;
s2 = (x+w)/64.0;
t2 = (y+h)/64.0;
cgi_R_DrawStretchPic( x, y, w, h, s1, t1, s2, t2, hShader );
}
/*
==============
CG_TileClear
Clear around a sized down screen
==============
*/
void CG_TileClear( void ) {
int top, bottom, left, right;
int w, h;
w = cgs.glconfig.vidWidth;
h = cgs.glconfig.vidHeight;
if ( cg.refdef.x == 0 && cg.refdef.y == 0 &&
cg.refdef.width == w && cg.refdef.height == h ) {
return; // full screen rendering
}
top = cg.refdef.y;
bottom = top + cg.refdef.height-1;
left = cg.refdef.x;
right = left + cg.refdef.width-1;
// clear above view screen
CG_TileClearBox( 0, 0, w, top, cgs.media.backTileShader );
// clear below view screen
CG_TileClearBox( 0, bottom, w, h - bottom, cgs.media.backTileShader );
// clear left of view screen
CG_TileClearBox( 0, top, left, bottom - top + 1, cgs.media.backTileShader );
// clear right of view screen
CG_TileClearBox( right, top, w - right, bottom - top + 1, cgs.media.backTileShader );
}
/*
================
CG_FadeColor
================
*/
float *CG_FadeColor( int startMsec, int totalMsec ) {
static vec4_t color;
int t;
if ( startMsec == 0 ) {
return NULL;
}
t = cg.time - startMsec;
if ( t >= totalMsec ) {
return NULL;
}
// fade out
if ( totalMsec - t < FADE_TIME ) {
color[3] = ( totalMsec - t ) * 1.0/FADE_TIME;
} else {
color[3] = 1.0;
}
color[0] = color[1] = color[2] = 1;
return color;
}
/*
==============
CG_DrawNumField
Take x,y positions as if 640 x 480 and scales them to the proper resolution
==============
*/
void CG_DrawNumField (int x, int y, int width, int value,int charWidth,int charHeight,int style,qboolean zeroFill)
{
char num[16], *ptr;
int l;
int frame;
int xWidth;
if (width < 1) {
return;
}
// draw number string
if (width > 5) {
width = 5;
}
switch ( width ) {
case 1:
value = value > 9 ? 9 : value;
value = value < 0 ? 0 : value;
break;
case 2:
value = value > 99 ? 99 : value;
value = value < -9 ? -9 : value;
break;
case 3:
value = value > 999 ? 999 : value;
value = value < -99 ? -99 : value;
break;
case 4:
value = value > 9999 ? 9999 : value;
value = value < -999 ? -999 : value;
break;
}
Com_sprintf (num, sizeof(num), "%i", value);
l = strlen(num);
if (l > width)
l = width;
// FIXME: Might need to do something different for the chunky font??
switch(style)
{
default:
case NUM_FONT_SMALL:
xWidth = charWidth;
break;
/*
case NUM_FONT_CHUNKY:
xWidth = (charWidth/1.2f) + 2;
break;
case NUM_FONT_BIG:
xWidth = (charWidth/2) + 7;//(charWidth/6);
break;
*/
}
#ifndef _XBOX
if ( zeroFill )
{
for (int i = 0; i < (width - l); i++ )
{
switch(style)
{
default:
case NUM_FONT_SMALL:
CG_DrawPic( x,y, charWidth, charHeight, cgs.media.smallnumberShaders[0] );
break;
/*
case NUM_FONT_CHUNKY:
CG_DrawPic( x,y, charWidth, charHeight, cgs.media.chunkyNumberShaders[0] );
break;
case NUM_FONT_BIG:
CG_DrawPic( x,y, charWidth, charHeight, cgs.media.numberShaders[0] );
break;
*/
}
x += 2 + (xWidth);
}
}
else
#endif
{
x += 2 + (xWidth)*(width - l);
}
ptr = num;
while (*ptr && l)
{
if (*ptr == '-')
frame = STAT_MINUS;
else
frame = *ptr -'0';
switch(style)
{
default:
case NUM_FONT_SMALL:
if(zeroFill)
{
CG_DrawPic( x-2,y, charWidth, charHeight, cgs.media.smallnumberShaders[frame] );
CG_DrawPic( x+2,y, charWidth, charHeight, cgs.media.smallnumberShaders[frame] );
CG_DrawPic( x,y-2, charWidth, charHeight, cgs.media.smallnumberShaders[frame] );
CG_DrawPic( x,y+2, charWidth, charHeight, cgs.media.smallnumberShaders[frame] );
x++; // For a one line gap
}
else {
CG_DrawPic( x,y, charWidth, charHeight, cgs.media.smallnumberShaders[frame] );
x++; // For a one line gap
}
break;
/*
case NUM_FONT_CHUNKY:
CG_DrawPic( x,y, charWidth, charHeight, cgs.media.chunkyNumberShaders[frame] );
break;
case NUM_FONT_BIG:
CG_DrawPic( x,y, charWidth, charHeight, cgs.media.numberShaders[frame] );
break;
*/
}
x += (xWidth);
ptr++;
l--;
}
}
/*
=================
CG_DrawProportionalString
=================
*/
void CG_DrawProportionalString( int x, int y, const char* str, int style, vec4_t color )
{
//assert(!style);//call this directly if you need style (OR it into the font handle)
cgi_R_Font_DrawString (x, y, str, color, cgs.media.qhFontMedium, -1, 1.0f);
}

1087
code/cgame/cg_effects.cpp Normal file

File diff suppressed because it is too large Load Diff

2673
code/cgame/cg_ents.cpp Normal file

File diff suppressed because it is too large Load Diff

1281
code/cgame/cg_event.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"

20
code/cgame/cg_headers.h Normal file
View File

@@ -0,0 +1,20 @@
// Precompiled header file for the client game
#include "cg_local.h"
// No PCH at all on Xbox build, we just include everything. Does this slow
// down builds? Somewhat. But then again, if I do change headers, I have to
// tolerate VS.net's piss poor dependency system that requires my to manually
// delete the .pch for the PC version to work at all. So, I'll live.
#ifdef _XBOX
#include "../game/g_local.h"
#include "../game/g_functions.h"
#include "../game/b_local.h"
#endif
//#include "CGEntity.h"
//#include "../game/SpawnSystem.h"
//#include "../game/EntitySystem.h"
//#include "../game/CScheduleSystem.h"
// end

648
code/cgame/cg_info.cpp Normal file
View File

@@ -0,0 +1,648 @@
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "..\game\objectives.h"
// For printing objectives
#ifdef _XBOX
static const short objectiveStartingYpos = 100; // Y starting position for objective text
static const short objectiveStartingXpos = 130; // X starting position for objective text
static const int objectiveTextBoxWidth = 400; // Width (in pixels) of text box
static const int objectiveTextBoxHeight = 310; // Height (in pixels) of text box
#else
static const short objectiveStartingYpos = 75; // Y starting position for objective text
static const short objectiveStartingXpos = 60; // X starting position for objective text
static const int objectiveTextBoxWidth = 500; // Width (in pixels) of text box
static const int objectiveTextBoxHeight = 300; // Height (in pixels) of text box
#endif // _XBOX
const char *showLoadPowersName[] =
{
"SP_INGAME_HEAL2",
"SP_INGAME_JUMP2",
"SP_INGAME_SPEED2",
"SP_INGAME_PUSH2",
"SP_INGAME_PULL2",
"SP_INGAME_MINDTRICK2",
"SP_INGAME_GRIP2",
"SP_INGAME_LIGHTNING2",
"SP_INGAME_SABER_THROW2",
"SP_INGAME_SABER_OFFENSE2",
"SP_INGAME_SABER_DEFENSE2",
NULL,
};
#define MAX_OBJ_GRAPHICS 4
#define OBJ_GRAPHIC_SIZE 240
int obj_graphics[MAX_OBJ_GRAPHICS];
qboolean CG_ForcePower_Valid(int forceKnownBits, int index);
/*
====================
ObjectivePrint_Line
Print a single mission objective
====================
*/
static void ObjectivePrint_Line(const int color, const int objectIndex, int &missionYcnt)
{
char *str,*strBegin;
int y,pixelLen,charLen,i;
const int maxHoldText = 1024;
char holdText[maxHoldText];
char finalText[2048];
qhandle_t graphic;
int iYPixelsPerLine = cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, 1.0f);
cgi_SP_GetStringTextString( va("OBJECTIVES_%s",objectiveTable[objectIndex].name) , finalText, sizeof(finalText) );
// A hack to be able to count prisoners
if (objectIndex==T2_RANCOR_OBJ5)
{
char value[64];
int currTotal, minTotal;
gi.Cvar_VariableStringBuffer("ui_prisonerobj_currtotal",value,sizeof(value));
currTotal = atoi(value);
gi.Cvar_VariableStringBuffer("ui_prisonerobj_maxtotal",value,sizeof(value));
minTotal = atoi(value);
sprintf(finalText,va(finalText,currTotal,minTotal));
}
pixelLen = cgi_R_Font_StrLenPixels(finalText, cgs.media.qhFontMedium, 1.0f);
str = finalText;
if (cgi_Language_IsAsian())
{
// this is execrable, and should NOT have had to've been done now, but...
//
extern const char *CG_DisplayBoxedText( int iBoxX, int iBoxY, int iBoxWidth, int iBoxHeight,
const char *psText, int iFontHandle, float fScale,
const vec4_t v4Color);
extern int giLinesOutput;
extern float gfAdvanceHack;
gfAdvanceHack = 1.0f; // override internal vertical advance
y = objectiveStartingYpos + (iYPixelsPerLine * missionYcnt);
// Advance line if a graphic has printed
for (i=0;i<MAX_OBJ_GRAPHICS;i++)
{
if (obj_graphics[i])
{
y += OBJ_GRAPHIC_SIZE + 4;
}
}
CG_DisplayBoxedText(
objectiveStartingXpos,
y,
objectiveTextBoxWidth,
objectiveTextBoxHeight,
finalText, // int iBoxX, int iBoxY, int iBoxWidth, int iBoxHeight, const char *psText
cgs.media.qhFontMedium, // int iFontHandle,
1.0f, // float fScale,
colorTable[color] // const vec4_t v4Color
);
gfAdvanceHack = 0.0f; // restore
missionYcnt += giLinesOutput;
}
else
{
// western...
//
if (pixelLen < objectiveTextBoxWidth) // One shot - small enough to print entirely on one line
{
y =objectiveStartingYpos + (iYPixelsPerLine * (missionYcnt));
cgi_R_Font_DrawString (
objectiveStartingXpos,
y,
str,
colorTable[color],
cgs.media.qhFontMedium,
-1,
1.0f);
++missionYcnt;
}
// Text is too long, break into lines.
else
{
char holdText2[2];
pixelLen = 0;
charLen = 0;
holdText2[1] = NULL;
strBegin = str;
while( *str )
{
holdText2[0] = *str;
pixelLen += cgi_R_Font_StrLenPixels(holdText2, cgs.media.qhFontMedium, 1.0f);
pixelLen += 2; // For kerning
++charLen;
if (pixelLen > objectiveTextBoxWidth )
{ //Reached max length of this line
//step back until we find a space
while ((charLen>10) && (*str != ' ' ))
{
--str;
--charLen;
}
if (*str==' ')
{
++str; // To get past space
}
assert( charLen<maxHoldText ); // Too big?
Q_strncpyz( holdText, strBegin, charLen);
holdText[charLen] = NULL;
strBegin = str;
pixelLen = 0;
charLen = 1;
y = objectiveStartingYpos + (iYPixelsPerLine * missionYcnt);
CG_DrawProportionalString(
objectiveStartingXpos,
y,
holdText,
CG_SMALLFONT,
colorTable[color] );
++missionYcnt;
}
else if (*(str+1) == NULL)
{
++charLen;
assert( charLen<maxHoldText ); // Too big?
y = objectiveStartingYpos + (iYPixelsPerLine * missionYcnt);
Q_strncpyz( holdText, strBegin, charLen);
CG_DrawProportionalString(
objectiveStartingXpos,
y, holdText,
CG_SMALLFONT,
colorTable[color] );
++missionYcnt;
break;
}
++str;
}
}
}
if (objectIndex == T3_BOUNTY_OBJ1)
{
y =objectiveStartingYpos + (iYPixelsPerLine * missionYcnt);
if (obj_graphics[1])
{
y += OBJ_GRAPHIC_SIZE + 4;
}
if (obj_graphics[2])
{
y += OBJ_GRAPHIC_SIZE + 4;
}
graphic = cgi_R_RegisterShaderNoMip("textures/system/viewscreen1");
CG_DrawPic( 105, 155, OBJ_GRAPHIC_SIZE * 0.90, OBJ_GRAPHIC_SIZE * 0.90, graphic );
obj_graphics[3] = qtrue;
}
}
/*
====================
CG_DrawDataPadObjectives
Draw routine for the objective info screen of the data pad.
====================
*/
void CG_DrawDataPadObjectives(const centity_t *cent )
{
int i,totalY;
int iYPixelsPerLine = cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, 1.0f);
const short titleXPos = objectiveStartingXpos - 22; // X starting position for title text
const short titleYPos = objectiveStartingYpos - 23; // Y starting position for title text
const short graphic_size = 16; // Size (width and height) of graphic used to show status of objective
const short graphicXpos = objectiveStartingXpos - graphic_size - 8; // Amount of X to backup from text starting position
const short graphicYOffset = (iYPixelsPerLine - graphic_size)/2; // Amount of Y to raise graphic so it's in the center of the text line
missionInfo_Updated = qfalse; // This will stop the text from flashing
cg.missionInfoFlashTime = 0;
// zero out objective graphics
for (i=0;i<MAX_OBJ_GRAPHICS;i++)
{
obj_graphics[i] = qfalse;
}
// Title Text at the top
char text[1024]={0};
cgi_SP_GetStringTextString( "SP_INGAME_OBJECTIVES", text, sizeof(text) );
cgi_R_Font_DrawString (titleXPos, titleYPos, text, colorTable[CT_TITLE], cgs.media.qhFontMedium, -1, 1.0f);
int missionYcnt = 0;
// Print all active objectives
for (i=0;i<MAX_OBJECTIVES;i++)
{
// Is there an objective to see?
if (cent->gent->client->sess.mission_objectives[i].display)
{
// Calculate the Y position
totalY = objectiveStartingYpos + (iYPixelsPerLine * (missionYcnt))+(iYPixelsPerLine/2);
// Draw graphics that show if mission has been accomplished or not
cgi_R_SetColor(colorTable[CT_BLUE3]);
CG_DrawPic( (graphicXpos), (totalY-graphicYOffset), graphic_size, graphic_size, cgs.media.messageObjCircle); // Circle in front
if (cent->gent->client->sess.mission_objectives[i].status == OBJECTIVE_STAT_SUCCEEDED)
{
CG_DrawPic( (graphicXpos), (totalY-graphicYOffset), graphic_size, graphic_size, cgs.media.messageLitOn); // Center Dot
}
// Print current objective text
ObjectivePrint_Line(CT_WHITE, i, missionYcnt );
}
}
// No mission text?
if (!missionYcnt)
{
// Set the message a quarter of the way down and in the center of the text box
int messageYPosition = objectiveStartingYpos + (objectiveTextBoxHeight / 4);
cgi_SP_GetStringTextString( "SP_INGAME_OBJNONE", text, sizeof(text) );
int messageXPosition = objectiveStartingXpos + (objectiveTextBoxWidth/2) - (cgi_R_Font_StrLenPixels(text, cgs.media.qhFontMedium, 1.0f) /2);
cgi_R_Font_DrawString (
messageXPosition,
messageYPosition,
text,
colorTable[CT_WHITE],
cgs.media.qhFontMedium,
-1,
1.0f);
}
}
/*
//-------------------------------------------------------
static void CG_DrawForceCount( const int force, int x, float *y, const int pad,qboolean *hasForcePowers )
{
char s[MAX_STRING_CHARS];
int val, textColor;
char text[1024]={0};
gi.Cvar_VariableStringBuffer( va("playerfplvl%d", force ),s, sizeof(s) );
sscanf( s, "%d",&val );
if ((val<1) || (val> NUM_FORCE_POWERS))
{
return;
}
textColor = CT_ICON_BLUE;
// Draw title
cgi_SP_GetStringTextString( showLoadPowersName[force], text, sizeof(text) );
CG_DrawProportionalString( x, *y, text, CG_BIGFONT, colorTable[textColor] );
// Draw icons
cgi_R_SetColor( colorTable[CT_WHITE]);
const int iconSize = 30;
if ( val >= 0 )
{
x -= 10; // Back up from title a little
for ( int i = 0; i < val; i++ )
{
CG_DrawPic( x - iconSize - i * (iconSize + 10) , *y, iconSize, iconSize, force_icons[force] );
}
}
*y += pad;
*hasForcePowers = qtrue;
}
/*
====================
CG_LoadScreen_PersonalInfo
====================
*/
/*
static void CG_LoadScreen_PersonalInfo(void)
{
float x, y;
int pad = 25;
char text[1024]={0};
qboolean hasForcePowers;
y = 65 + 30;
pad = 28;
x = 300;
hasForcePowers=qfalse;
CG_DrawForceCount( FP_HEAL, x, &y, pad,&hasForcePowers);
CG_DrawForceCount( FP_LEVITATION, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_SPEED, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_PUSH, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_PULL, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_TELEPATHY, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_GRIP, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_LIGHTNING, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_SABERTHROW, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_SABER_OFFENSE, x, &y, pad,&hasForcePowers );
CG_DrawForceCount( FP_SABER_DEFENSE, x, &y, pad,&hasForcePowers );
if (hasForcePowers)
{
cgi_SP_GetStringTextString( "SP_INGAME_CURRENTFORCEPOWERS", text, sizeof(text) );
CG_DrawProportionalString( 200, 65, text, CG_CENTER | CG_BIGFONT, colorTable[CT_WHITE] );
}
else
{ //you are only totally empty on the very first map?
// cgi_SP_GetStringTextString( "SP_INGAME_NONE", text, sizeof(text) );
// CG_DrawProportionalString( 320, y+30, text, CG_CENTER | CG_BIGFONT, colorTable[CT_ICON_BLUE] );
cgi_SP_GetStringTextString( "SP_INGAME_ALONGTIME", text, sizeof(text) );
int w = cgi_R_Font_StrLenPixels(text,cgs.media.qhFontMedium, 1.5f);
cgi_R_Font_DrawString((320)-(w/2), y+40, text, colorTable[CT_ICON_BLUE], cgs.media.qhFontMedium, -1, 1.5f);
}
}
*/
static void CG_LoadBar(void)
{
cgi_R_SetColor( colorTable[CT_WHITE]);
int glowHeight = (cg.loadLCARSStage / 9.0f) * 147;
int glowTop = (280 + 147) - glowHeight;
// Draw glow:
CG_DrawPic(280, glowTop, 73, glowHeight, cgs.media.loadTick);
// Draw saber:
CG_DrawPic(280, 265, 73, 147, cgs.media.levelLoad);
}
int CG_WeaponCheck( int weaponIndex );
int loadForcePowerLevel[NUM_FORCE_POWERS];
/*
===============
ForcePowerDataPad_Valid
===============
*/
qboolean CG_ForcePower_Valid(int forceKnownBits, int index)
{
if ((forceKnownBits & (1 << showPowers[index])) &&
loadForcePowerLevel[showPowers[index]]) // Does he have the force power?
{
return qtrue;
}
return qfalse;
}
// Get the player weapons and force power info
static void CG_GetLoadScreenInfo(int *weaponBits,int *forceBits)
{
char s[MAX_STRING_CHARS];
int iDummy,i;
float fDummy;
const char *var;
gi.Cvar_VariableStringBuffer( sCVARNAME_PLAYERSAVE, s, sizeof(s) );
// Get player weapons and force powers known
if (s[0])
{
// |general info |-force powers
sscanf( s, "%i %i %i %i %i %i %i %f %f %f %i %i",
&iDummy, // &client->ps.stats[STAT_HEALTH],
&iDummy, // &client->ps.stats[STAT_ARMOR],
&*weaponBits,// &client->ps.stats[STAT_WEAPONS],
&iDummy, // &client->ps.stats[STAT_ITEMS],
&iDummy, // &client->ps.weapon,
&iDummy, // &client->ps.weaponstate,
&iDummy, // &client->ps.batteryCharge,
&fDummy, // &client->ps.viewangles[0],
&fDummy, // &client->ps.viewangles[1],
&fDummy, // &client->ps.viewangles[2],
//force power data
&*forceBits, // &client->ps.forcePowersKnown,
&iDummy // &client->ps.forcePower,
);
}
// the new JK2 stuff - force powers, etc...
//
gi.Cvar_VariableStringBuffer( "playerfplvl", s, sizeof(s) );
i=0;
var = strtok( s, " " );
while( var != NULL )
{
/* While there are tokens in "s" */
loadForcePowerLevel[i++] = atoi(var);
/* Get next token: */
var = strtok( NULL, " " );
}
}
/*
====================
CG_DrawLoadingScreen
Load screen displays the map pic, the mission briefing and weapons/force powers
====================
*/
static void CG_DrawLoadingScreen( qhandle_t levelshot, qhandle_t levelshot2, const char *mapName)
{
int xPos,yPos,width,height;
vec4_t color;
qhandle_t background;
int weapons=0, forcepowers=0;
// Get mission briefing for load screen
if (cgi_SP_GetStringTextString( va("BRIEFINGS_%s",mapName), NULL, 0 ) == 0)
{
cgi_Cvar_Set( "ui_missionbriefing", "@BRIEFINGS_NONE" );
}
else
{
cgi_Cvar_Set( "ui_missionbriefing", va("@BRIEFINGS_%s",mapName) );
}
// Print background
if (cgi_UI_GetMenuItemInfo(
"loadScreen",
"background",
&xPos,
&yPos,
&width,
&height,
color,
&background))
{
cgi_R_SetColor( color );
CG_DrawPic( xPos, yPos, width, height, background );
}
// Print level pic
if (cgi_UI_GetMenuItemInfo(
"loadScreen",
"mappic",
&xPos,
&yPos,
&width,
&height,
color,
&background))
{
cgi_R_SetColor( color );
CG_DrawPic( xPos, yPos, width, height, levelshot );
}
// Print second level pic
if (cgi_UI_GetMenuItemInfo(
"loadScreen",
"mappic2",
&xPos,
&yPos,
&width,
&height,
color,
&background))
{
cgi_R_SetColor( color );
CG_DrawPic( xPos, yPos, width, height, levelshot2 );
}
// Removed by BTO - No more icons on the loading screen
/*
// Get player weapons and force power info
CG_GetLoadScreenInfo(&weapons,&forcepowers);
// Print weapon icons
if (weapons)
{
CG_DrawLoadWeapons(weapons);
}
// Print force power icons
if (forcepowers)
{
CG_DrawLoadForcePowers(forcepowers);
}
*/
}
/*
====================
CG_DrawInformation
Draw all the status / pacifier stuff during level loading
====================
*/
void CG_DrawInformation( void ) {
int y;
// draw the dialog background
const char *info = CG_ConfigString( CS_SERVERINFO );
const char *s = Info_ValueForKey( info, "mapname" );
extern SavedGameJustLoaded_e g_eSavedGameJustLoaded; // hack! (hey, it's the last week of coding, ok?
if ( g_eSavedGameJustLoaded != eFULL && (!strcmp(s,"yavin1") || !strcmp(s,"demo")) )//special case for first map!
{
char text[1024]={0};
//
cgi_R_SetColor( colorTable[CT_BLACK] );
CG_DrawPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, cgs.media.whiteShader );
cgi_SP_GetStringTextString( "SP_INGAME_ALONGTIME", text, sizeof(text) );
int w = cgi_R_Font_StrLenPixels(text,cgs.media.qhFontMedium, 1.0f);
cgi_R_Font_DrawString((320)-(w/2), 140, text, colorTable[CT_ICON_BLUE], cgs.media.qhFontMedium, -1, 1.0f);
}
else
{
qhandle_t levelshot = cgi_R_RegisterShaderNoMip( va( "levelshots/%s", s ) );
if (!levelshot) {
levelshot = cgi_R_RegisterShaderNoMip( "menu/art/unknownmap" );
}
qhandle_t levelshot2 = cgi_R_RegisterShaderNoMip( va( "levelshots/%s2", s ) );
if (!levelshot2) {
levelshot2 = levelshot;
}
CG_DrawLoadingScreen(levelshot, levelshot2, s);
cgi_UI_MenuPaintAll();
}
CG_LoadBar();
// the first 150 rows are reserved for the client connection
// screen to write into
// if ( cg.processedSnapshotNum == 0 )
{
// still loading
// print the current item being loaded
#ifdef _DEBUG
cgi_R_Font_DrawString( 40, 416, va("LOADING ... %s",cg.infoScreenText),colorTable[CT_LTGOLD1], cgs.media.qhFontSmall, -1, 1.0f );
#endif
}
// draw info string information
y = 20;
// map-specific message (long map name)
s = CG_ConfigString( CS_MESSAGE );
if ( s[0] )
{
if (s[0] == '@')
{
char text[1024]={0};
cgi_SP_GetStringTextString( s+1, text, sizeof(text) );
cgi_R_Font_DrawString( 15, y, va("\"%s\"",text),colorTable[CT_WHITE],cgs.media.qhFontMedium, -1, 1.0f );
}
else
{
cgi_R_Font_DrawString( 15, y, va("\"%s\"",s),colorTable[CT_WHITE],cgs.media.qhFontMedium, -1, 1.0f );
}
y += 20;
}
}

87
code/cgame/cg_lights.cpp Normal file
View File

@@ -0,0 +1,87 @@
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#if !defined(CG_LIGHTS_H_INC)
#include "cg_lights.h"
#endif
static clightstyle_t cl_lightstyle[MAX_LIGHT_STYLES];
static int lastofs;
/*
================
FX_ClearLightStyles
================
*/
void CG_ClearLightStyles (void)
{
int i;
memset (cl_lightstyle, 0, sizeof(cl_lightstyle));
lastofs = -1;
for(i=0;i<MAX_LIGHT_STYLES*3;i++)
{
CG_SetLightstyle (i);
}
}
/*
================
FX_RunLightStyles
================
*/
void CG_RunLightStyles (void)
{
int ofs;
int i;
clightstyle_t *ls;
ofs = cg.time / 50;
// if (ofs == lastofs)
// return;
lastofs = ofs;
for (i=0,ls=cl_lightstyle ; i<MAX_LIGHT_STYLES ; i++, ls++)
{
if (!ls->length)
{
ls->value[0] = ls->value[1] = ls->value[2] = ls->value[3] = 255;
}
else if (ls->length == 1)
{
ls->value[0] = ls->map[0][0];
ls->value[1] = ls->map[0][1];
ls->value[2] = ls->map[0][2];
ls->value[3] = 255; //ls->map[0][3];
}
else
{
ls->value[0] = ls->map[ofs%ls->length][0];
ls->value[1] = ls->map[ofs%ls->length][1];
ls->value[2] = ls->map[ofs%ls->length][2];
ls->value[3] = 255; //ls->map[ofs%ls->length][3];
}
trap_R_SetLightStyle(i, *(int*)ls->value);
}
}
void CG_SetLightstyle (int i)
{
const char *s;
int j, k;
s = CG_ConfigString( i+CS_LIGHT_STYLES );
j = strlen (s);
if (j >= MAX_QPATH)
{
Com_Error (ERR_DROP, "svc_lightstyle length=%i", j);
}
cl_lightstyle[(i/3)].length = j;
for (k=0 ; k<j ; k++)
{
cl_lightstyle[(i/3)].map[k][(i%3)] = (float)(s[k]-'a')/(float)('z'-'a') * 255.0;
}
}

16
code/cgame/cg_lights.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#if !defined(CG_LIGHTS_H_INC)
#define CG_LIGHTS_H_INC
typedef struct
{
int length;
color4ub_t value;
color4ub_t map[MAX_QPATH];
} clightstyle_t;
void CG_ClearLightStyles (void);
void CG_RunLightStyles (void);
void CG_SetLightstyle (int i);
#endif // CG_LIGHTS_H_INC

1233
code/cgame/cg_local.h Normal file

File diff suppressed because it is too large Load Diff

599
code/cgame/cg_localents.cpp Normal file
View File

@@ -0,0 +1,599 @@
// cg_localents.c -- every frame, generate renderer commands for locally
// processed entities, like smoke puffs, gibs, shells, etc.
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
#include "cg_media.h"
#ifdef _XBOX
#define MAX_LOCAL_ENTITIES 128
#else
#define MAX_LOCAL_ENTITIES 512
#endif
localEntity_t cg_localEntities[MAX_LOCAL_ENTITIES];
localEntity_t cg_activeLocalEntities; // double linked list
localEntity_t *cg_freeLocalEntities; // single linked list
/*
===================
CG_InitLocalEntities
This is called at startup and for tournement restarts
===================
*/
void CG_InitLocalEntities( void ) {
int i;
memset( cg_localEntities, 0, sizeof( cg_localEntities ) );
cg_activeLocalEntities.next = &cg_activeLocalEntities;
cg_activeLocalEntities.prev = &cg_activeLocalEntities;
cg_freeLocalEntities = cg_localEntities;
for ( i = 0 ; i < MAX_LOCAL_ENTITIES - 1 ; i++ ) {
cg_localEntities[i].next = &cg_localEntities[i+1];
}
}
/*
==================
CG_FreeLocalEntity
==================
*/
void CG_FreeLocalEntity( localEntity_t *le ) {
if ( !le->prev ) {
CG_Error( "CG_FreeLocalEntity: not active" );
}
// remove from the doubly linked active list
le->prev->next = le->next;
le->next->prev = le->prev;
// the free list is only singly linked
le->next = cg_freeLocalEntities;
cg_freeLocalEntities = le;
}
/*
===================
CG_AllocLocalEntity
Will allways succeed, even if it requires freeing an old active entity
===================
*/
localEntity_t *CG_AllocLocalEntity( void ) {
localEntity_t *le;
if ( !cg_freeLocalEntities ) {
// no free entities, so free the one at the end of the chain
// remove the oldest active entity
CG_FreeLocalEntity( cg_activeLocalEntities.prev );
}
le = cg_freeLocalEntities;
cg_freeLocalEntities = cg_freeLocalEntities->next;
memset( le, 0, sizeof( *le ) );
// link into the active list
le->next = cg_activeLocalEntities.next;
le->prev = &cg_activeLocalEntities;
cg_activeLocalEntities.next->prev = le;
cg_activeLocalEntities.next = le;
le->ownerGentNum = -1;
return le;
}
/*
====================================================================================
FRAGMENT PROCESSING
A fragment localentity interacts with the environment in some way (hitting walls),
or generates more localentities along a trail.
====================================================================================
*/
/*
================
CG_FragmentBounceSound
================
*/
void CG_FragmentBounceSound( localEntity_t *le, trace_t *trace )
{
// half the fragments will make a bounce sounds
if ( rand() & 1 )
{
sfxHandle_t s = 0;
switch( le->leBounceSoundType )
{
case LEBS_ROCK:
s = cgs.media.rockBounceSound[Q_irand(0,1)];
break;
case LEBS_METAL:
s = cgs.media.metalBounceSound[Q_irand(0,1)];// FIXME: make sure that this sound is registered properly...might still be rock bounce sound....
break;
}
if ( s )
{
cgi_S_StartSound( trace->endpos, ENTITYNUM_WORLD, CHAN_AUTO, s );
}
// bouncers only make the sound once...
// FIXME: arbitrary...change if it bugs you
le->leBounceSoundType = LEBS_NONE;
}
else if ( rand() & 1 )
{
// we may end up bouncing again, but each bounce reduces the chance of playing the sound again or they may make a lot of noise when they settle
// FIXME: maybe just always do this??
le->leBounceSoundType = LEBS_NONE;
}
}
/*
================
CG_ReflectVelocity
================
*/
void CG_ReflectVelocity( localEntity_t *le, trace_t *trace )
{
vec3_t velocity;
float dot;
int hitTime;
// reflect the velocity on the trace plane
hitTime = cg.time - cg.frametime + cg.frametime * trace->fraction;
EvaluateTrajectoryDelta( &le->pos, hitTime, velocity );
dot = DotProduct( velocity, trace->plane.normal );
VectorMA( velocity, -2*dot, trace->plane.normal, le->pos.trDelta );
VectorScale( le->pos.trDelta, le->bounceFactor, le->pos.trDelta );
VectorCopy( trace->endpos, le->pos.trBase );
le->pos.trTime = cg.time;
// check for stop, making sure that even on low FPS systems it doesn't bobble
if ( trace->allsolid ||
( trace->plane.normal[2] > 0 &&
( le->pos.trDelta[2] < 40 || le->pos.trDelta[2] < -cg.frametime * le->pos.trDelta[2] ) ) )
{
le->pos.trType = TR_STATIONARY;
}
}
/*
================
CG_AddFragment
================
*/
void CG_AddFragment( localEntity_t *le )
{
vec3_t newOrigin;
trace_t trace;
// used to sink into the ground, but it looks better to maybe just fade them out
int t;
t = le->endTime - cg.time;
if ( t < FRAG_FADE_TIME )
{
le->refEntity.renderfx |= RF_ALPHA_FADE;
le->refEntity.shaderRGBA[0] = le->refEntity.shaderRGBA[1] = le->refEntity.shaderRGBA[2] = 255;
le->refEntity.shaderRGBA[3] = ((float)t / FRAG_FADE_TIME) * 255.0f;
}
if ( le->pos.trType == TR_STATIONARY )
{
if ( !(cgi_CM_PointContents( le->refEntity.origin, 0 ) & CONTENTS_SOLID ))
{
// thing is no longer in solid, so let gravity take it back
VectorCopy( le->refEntity.origin, le->pos.trBase );
VectorClear( le->pos.trDelta );
le->pos.trTime = cg.time;
le->pos.trType = TR_GRAVITY;
}
cgi_R_AddRefEntityToScene( &le->refEntity );
return;
}
// calculate new position
EvaluateTrajectory( &le->pos, cg.time, newOrigin );
le->refEntity.renderfx |= RF_LIGHTING_ORIGIN;
VectorCopy( newOrigin, le->refEntity.lightingOrigin );
// trace a line from previous position to new position
CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, le->ownerGentNum, CONTENTS_SOLID );
if ( trace.fraction == 1.0 ) {
// still in free fall
VectorCopy( newOrigin, le->refEntity.origin );
if ( le->leFlags & LEF_TUMBLE ) {
vec3_t angles;
EvaluateTrajectory( &le->angles, cg.time, angles );
AnglesToAxis( angles, le->refEntity.axis );
for(int k = 0; k < 3; k++)
{
VectorScale(le->refEntity.axis[k], le->radius, le->refEntity.axis[k]);
}
}
cgi_R_AddRefEntityToScene( &le->refEntity );
return;
}
// if it is in a nodrop zone, remove it
// this keeps gibs from waiting at the bottom of pits of death
// and floating levels
if ( cgi_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP )
{
CG_FreeLocalEntity( le );
return;
}
// do a bouncy sound
CG_FragmentBounceSound( le, &trace );
// reflect the velocity on the trace plane
CG_ReflectVelocity( le, &trace );
//FIXME: if LEF_TUMBLE, change avelocity too?
cgi_R_AddRefEntityToScene( &le->refEntity );
}
/*
=====================================================================
TRIVIAL LOCAL ENTITIES
These only do simple scaling or modulation before passing to the renderer
=====================================================================
*/
/*
** CG_AddTeleporterEffect
*/
void CG_AddTeleporterEffect( localEntity_t *le ) {
refEntity_t *re;
float c;
re = &le->refEntity;
c = ( le->endTime - cg.time ) / ( float ) ( le->endTime - le->startTime );
re->shaderRGBA[0] =
re->shaderRGBA[1] =
re->shaderRGBA[2] =
re->shaderRGBA[3] = 0xff * c;
cgi_R_AddRefEntityToScene( re );
}
/*
** CG_AddFadeRGB
*/
void CG_AddFadeRGB( localEntity_t *le ) {
refEntity_t *re;
float c;
re = &le->refEntity;
c = ( le->endTime - cg.time ) * le->lifeRate;
c *= 0xff;
re->shaderRGBA[0] = le->color[0] * c;
re->shaderRGBA[1] = le->color[1] * c;
re->shaderRGBA[2] = le->color[2] * c;
re->shaderRGBA[3] = le->color[3] * c;
cgi_R_AddRefEntityToScene( re );
}
/*
==================
CG_AddPuff
==================
*/
static void CG_AddPuff( localEntity_t *le ) {
refEntity_t *re;
float c;
vec3_t delta;
float len;
re = &le->refEntity;
// fade / grow time
c = ( le->endTime - cg.time ) / (float)( le->endTime - le->startTime );
re->shaderRGBA[0] = le->color[0] * c;
re->shaderRGBA[1] = le->color[1] * c;
re->shaderRGBA[2] = le->color[2] * c;
if ( !( le->leFlags & LEF_PUFF_DONT_SCALE ) ) {
re->radius = le->radius * ( 1.0 - c ) + 8;
}
EvaluateTrajectory( &le->pos, cg.time, re->origin );
// if the view would be "inside" the sprite, kill the sprite
// so it doesn't add too much overdraw
VectorSubtract( re->origin, cg.refdef.vieworg, delta );
len = VectorLength( delta );
if ( len < le->radius ) {
CG_FreeLocalEntity( le );
return;
}
cgi_R_AddRefEntityToScene( re );
}
/*
================
CG_AddLocalLight
================
*/
static void CG_AddLocalLight( localEntity_t *le )
{
// There should be a light if this is being used, but hey...
if ( le->light )
{
float light;
light = (float)( cg.time - le->startTime ) / ( le->endTime - le->startTime );
if ( light < 0.5 )
{
light = 1.0;
}
else
{
light = 1.0 - ( light - 0.5 ) * 2;
}
light = le->light * light;
cgi_R_AddLightToScene( le->refEntity.origin, light, le->lightColor[0], le->lightColor[1], le->lightColor[2] );
}
}
//---------------------------------------------------
static void CG_AddFadeModel( localEntity_t *le )
{
refEntity_t *ent = &le->refEntity;
if ( cg.time < le->startTime )
{
CG_FreeLocalEntity( le );
return;
}
float frac = 1.0f - ((float)( cg.time - le->startTime )/(float)( le->endTime - le->startTime ));
ent->shaderRGBA[0] = le->color[0] * frac;
ent->shaderRGBA[1] = le->color[1] * frac;
ent->shaderRGBA[2] = le->color[2] * frac;
ent->shaderRGBA[3] = le->color[3] * frac;
EvaluateTrajectory( &le->pos, cg.time, ent->origin );
// add the entity
cgi_R_AddRefEntityToScene( ent );
}
// NOTE: this is 100% for the demp2 alt-fire effect, so changes to the visual effect will affect game side demp2 code
//---------------------------------------------------
static void CG_AddFadeScaleModel( localEntity_t *le )
{
refEntity_t *ent = &le->refEntity;
float frac = ( cg.time - le->startTime )/((float)( le->endTime - le->startTime ));
frac *= frac * frac; // yes, this is completely ridiculous...but it causes the shell to grow slowly then "explode" at the end
ent->nonNormalizedAxes = qtrue;
AxisCopy( axisDefault, ent->axis );
VectorScale( ent->axis[0], le->radius * frac, ent->axis[0] );
VectorScale( ent->axis[1], le->radius * frac, ent->axis[1] );
VectorScale( ent->axis[2], le->radius * 0.5f * frac, ent->axis[2] );
frac = 1.0f - frac;
ent->shaderRGBA[0] = le->color[0] * frac;
ent->shaderRGBA[1] = le->color[1] * frac;
ent->shaderRGBA[2] = le->color[2] * frac;
ent->shaderRGBA[3] = le->color[3] * frac;
// add the entity
cgi_R_AddRefEntityToScene( ent );
}
// create a quad that doesn't use a refEnt. Currently only for use with the DebugNav drawing so it doesn't have to use fx
//------------------------------------------
static void CG_AddQuad( localEntity_t *le )
{
polyVert_t verts[4];
VectorCopy( le->refEntity.origin, verts[0].xyz );
verts[0].xyz[0] -= le->radius;
verts[0].xyz[1] -= le->radius;
verts[0].st[0] = 0;
verts[0].st[1] = 0;
for ( int i = 0; i < 4; i++ )
{
verts[i].modulate[0] = le->color[0];
verts[i].modulate[1] = le->color[1];
verts[i].modulate[2] = le->color[2];
verts[i].modulate[3] = le->color[3];
}
VectorCopy( le->refEntity.origin, verts[1].xyz );
verts[1].xyz[0] -= le->radius;
verts[1].xyz[1] += le->radius;
verts[1].st[0] = 0;
verts[1].st[1] = 1;
VectorCopy( le->refEntity.origin, verts[2].xyz );
verts[2].xyz[0] += le->radius;
verts[2].xyz[1] += le->radius;
verts[2].st[0] = 1;
verts[2].st[1] = 1;
VectorCopy( le->refEntity.origin, verts[3].xyz );
verts[3].xyz[0] += le->radius;
verts[3].xyz[1] -= le->radius;
verts[3].st[0] = 1;
verts[3].st[1] = 0;
cgi_R_AddPolyToScene( le->refEntity.customShader, 4, verts );
}
// create a sprite that doesn't use a refEnt. Currently only for use with the DebugNav drawing so it doesn't have to use fx
//------------------------------------------
static void CG_AddSprite( localEntity_t *le )
{
polyVert_t verts[4];
VectorCopy( le->refEntity.origin, verts[0].xyz );
VectorMA( verts[0].xyz, -le->radius, cg.refdef.viewaxis[2], verts[0].xyz );
VectorMA( verts[0].xyz, -le->radius, cg.refdef.viewaxis[1], verts[0].xyz );
verts[0].st[0] = 0;
verts[0].st[1] = 0;
for ( int i = 0; i < 4; i++ )
{
verts[i].modulate[0] = le->color[0];
verts[i].modulate[1] = le->color[1];
verts[i].modulate[2] = le->color[2];
verts[i].modulate[3] = le->color[3];
}
VectorCopy( le->refEntity.origin, verts[1].xyz );
VectorMA( verts[1].xyz, -le->radius, cg.refdef.viewaxis[2], verts[1].xyz );
VectorMA( verts[1].xyz, le->radius, cg.refdef.viewaxis[1], verts[1].xyz );
verts[1].st[0] = 0;
verts[1].st[1] = 1;
VectorCopy( le->refEntity.origin, verts[2].xyz );
VectorMA( verts[2].xyz, le->radius, cg.refdef.viewaxis[2], verts[2].xyz );
VectorMA( verts[2].xyz, le->radius, cg.refdef.viewaxis[1], verts[2].xyz );
verts[2].st[0] = 1;
verts[2].st[1] = 1;
VectorCopy( le->refEntity.origin, verts[3].xyz );
VectorMA( verts[3].xyz, le->radius, cg.refdef.viewaxis[2], verts[3].xyz );
VectorMA( verts[3].xyz, -le->radius, cg.refdef.viewaxis[1], verts[3].xyz );
verts[3].st[0] = 1;
verts[3].st[1] = 0;
cgi_R_AddPolyToScene( le->refEntity.customShader, 4, verts );
}
/*
===================
CG_AddLine
for beams and the like.
===================
*/
void CG_AddLine( localEntity_t *le )
{
refEntity_t *re;
re = &le->refEntity;
re->reType = RT_LINE;
cgi_R_AddRefEntityToScene( re );
}
//==============================================================================
/*
===================
CG_AddLocalEntities
===================
*/
void CG_AddLocalEntities( void )
{
localEntity_t *le, *next;
// walk the list backwards, so any new local entities generated
// (trails, marks, etc) will be present this frame
le = cg_activeLocalEntities.prev;
for ( ; le != &cg_activeLocalEntities ; le = next ) {
// grab next now, so if the local entity is freed we
// still have it
next = le->prev;
if ( cg.time >= le->endTime ) {
CG_FreeLocalEntity( le );
continue;
}
switch ( le->leType ) {
default:
CG_Error( "Bad leType: %i", le->leType );
break;
case LE_MARK:
break;
case LE_FADE_MODEL:
CG_AddFadeModel( le );
break;
case LE_FADE_SCALE_MODEL:
CG_AddFadeScaleModel( le );
break;
case LE_FRAGMENT:
CG_AddFragment( le );
break;
case LE_PUFF:
CG_AddPuff( le );
break;
case LE_FADE_RGB: // teleporters, railtrails
CG_AddFadeRGB( le );
break;
case LE_LIGHT:
CG_AddLocalLight( le );
break;
case LE_LINE: // oriented lines for FX
CG_AddLine( le );
break;
// Use for debug only
case LE_QUAD:
CG_AddQuad( le );
break;
case LE_SPRITE:
CG_AddSprite( le );
}
}
}

4442
code/cgame/cg_main.cpp Normal file

File diff suppressed because it is too large Load Diff

264
code/cgame/cg_marks.cpp Normal file
View File

@@ -0,0 +1,264 @@
// cg_marks.c -- wall marks
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
/*
===================================================================
MARK POLYS
===================================================================
*/
markPoly_t cg_activeMarkPolys; // double linked list
markPoly_t *cg_freeMarkPolys; // single linked list
markPoly_t cg_markPolys[MAX_MARK_POLYS];
/*
===================
CG_InitMarkPolys
This is called at startup and for tournement restarts
===================
*/
void CG_InitMarkPolys( void ) {
int i;
memset( cg_markPolys, 0, sizeof(cg_markPolys) );
cg_activeMarkPolys.nextMark = &cg_activeMarkPolys;
cg_activeMarkPolys.prevMark = &cg_activeMarkPolys;
cg_freeMarkPolys = cg_markPolys;
for ( i = 0 ; i < MAX_MARK_POLYS - 1 ; i++ ) {
cg_markPolys[i].nextMark = &cg_markPolys[i+1];
}
}
/*
==================
CG_FreeMarkPoly
==================
*/
void CG_FreeMarkPoly( markPoly_t *le ) {
if ( !le->prevMark ) {
CG_Error( "CG_FreeLocalEntity: not active" );
}
// remove from the doubly linked active list
le->prevMark->nextMark = le->nextMark;
le->nextMark->prevMark = le->prevMark;
// the free list is only singly linked
le->nextMark = cg_freeMarkPolys;
cg_freeMarkPolys = le;
}
/*
===================
CG_AllocMark
Will allways succeed, even if it requires freeing an old active mark
===================
*/
markPoly_t *CG_AllocMark( void ) {
markPoly_t *le;
int time;
if ( !cg_freeMarkPolys ) {
// no free entities, so free the one at the end of the chain
// remove the oldest active entity
time = cg_activeMarkPolys.prevMark->time;
while (cg_activeMarkPolys.prevMark && time == cg_activeMarkPolys.prevMark->time) {
CG_FreeMarkPoly( cg_activeMarkPolys.prevMark );
}
}
le = cg_freeMarkPolys;
cg_freeMarkPolys = cg_freeMarkPolys->nextMark;
memset( le, 0, sizeof( *le ) );
// link into the active list
le->nextMark = cg_activeMarkPolys.nextMark;
le->prevMark = &cg_activeMarkPolys;
cg_activeMarkPolys.nextMark->prevMark = le;
cg_activeMarkPolys.nextMark = le;
return le;
}
/*
=================
CG_ImpactMark
origin should be a point within a unit of the plane
dir should be the plane normal
temporary marks will not be stored or randomly oriented, but immediately
passed to the renderer.
=================
*/
#define MAX_MARK_FRAGMENTS 128
#define MAX_MARK_POINTS 384
void CG_ImpactMark( qhandle_t markShader, const vec3_t origin, const vec3_t dir,
float orientation, float red, float green, float blue, float alpha,
qboolean alphaFade, float radius, qboolean temporary ) {
vec3_t axis[3];
float texCoordScale;
vec3_t originalPoints[4];
byte colors[4];
int i, j;
int numFragments;
markFragment_t markFragments[MAX_MARK_FRAGMENTS], *mf;
vec3_t markPoints[MAX_MARK_POINTS];
vec3_t projection;
if ( !cg_addMarks.integer ) {
return;
}
if ( radius <= 0 ) {
CG_Error( "CG_ImpactMark called with <= 0 radius" );
}
// create the texture axis
VectorNormalize2( dir, axis[0] );
PerpendicularVector( axis[1], axis[0] );
RotatePointAroundVector( axis[2], axis[0], axis[1], orientation );
CrossProduct( axis[0], axis[2], axis[1] );
texCoordScale = 0.5 * 1.0 / radius;
// create the full polygon
for ( i = 0 ; i < 3 ; i++ ) {
originalPoints[0][i] = origin[i] - radius * axis[1][i] - radius * axis[2][i];
originalPoints[1][i] = origin[i] + radius * axis[1][i] - radius * axis[2][i];
originalPoints[2][i] = origin[i] + radius * axis[1][i] + radius * axis[2][i];
originalPoints[3][i] = origin[i] - radius * axis[1][i] + radius * axis[2][i];
}
// get the fragments
VectorScale( dir, -20, projection );
numFragments = cgi_CM_MarkFragments( 4, (const float (*)[3])originalPoints,
projection, MAX_MARK_POINTS, markPoints[0],
MAX_MARK_FRAGMENTS, markFragments );
colors[0] = red * 255;
colors[1] = green * 255;
colors[2] = blue * 255;
colors[3] = alpha * 255;
for ( i = 0, mf = markFragments ; i < numFragments ; i++, mf++ ) {
polyVert_t *v;
polyVert_t verts[MAX_VERTS_ON_POLY];
markPoly_t *mark;
// we have an upper limit on the complexity of polygons
// that we store persistantly
if ( mf->numPoints > MAX_VERTS_ON_POLY ) {
mf->numPoints = MAX_VERTS_ON_POLY;
}
for ( j = 0, v = verts ; j < mf->numPoints ; j++, v++ ) {
vec3_t delta;
VectorCopy( markPoints[mf->firstPoint + j], v->xyz );
VectorSubtract( v->xyz, origin, delta );
v->st[0] = 0.5 + DotProduct( delta, axis[1] ) * texCoordScale;
v->st[1] = 0.5 + DotProduct( delta, axis[2] ) * texCoordScale;
*(int *)v->modulate = *(int *)colors;
}
// if it is a temporary (shadow) mark, add it immediately and forget about it
if ( temporary ) {
cgi_R_AddPolyToScene( markShader, mf->numPoints, verts );
continue;
}
// otherwise save it persistantly
mark = CG_AllocMark();
mark->time = cg.time;
mark->alphaFade = alphaFade;
mark->markShader = markShader;
mark->poly.numVerts = mf->numPoints;
mark->color[0] = colors[0];//red;
mark->color[1] = colors[1];//green;
mark->color[2] = colors[2];//blue;
mark->color[3] = colors[3];//alpha;
memcpy( mark->verts, verts, mf->numPoints * sizeof( verts[0] ) );
}
}
/*
===============
CG_AddMarks
===============
*/
#define MARK_TOTAL_TIME 10000
#define MARK_FADE_TIME 1000
void CG_AddMarks( void ) {
int j;
markPoly_t *mp, *next;
int t;
int fade;
if ( !cg_addMarks.integer ) {
return;
}
mp = cg_activeMarkPolys.nextMark;
for ( ; mp != &cg_activeMarkPolys ; mp = next ) {
// grab next now, so if the local entity is freed we
// still have it
next = mp->nextMark;
// see if it is time to completely remove it
if ( cg.time > mp->time + MARK_TOTAL_TIME ) {
CG_FreeMarkPoly( mp );
continue;
}
// fade all marks out with time
t = mp->time + MARK_TOTAL_TIME - cg.time;
if ( t < MARK_FADE_TIME ) {
fade = 255 * t / MARK_FADE_TIME;
if ( mp->alphaFade ) {
for ( j = 0 ; j < mp->poly.numVerts ; j++ ) {
mp->verts[j].modulate[3] = fade;
}
}
else
{
float f = (float)t / MARK_FADE_TIME;
for ( j = 0 ; j < mp->poly.numVerts ; j++ ) {
mp->verts[j].modulate[0] = mp->color[0] * f;
mp->verts[j].modulate[1] = mp->color[1] * f;
mp->verts[j].modulate[2] = mp->color[2] * f;
}
}
}
else
{
for ( j = 0 ; j < mp->poly.numVerts ; j++ ) {
mp->verts[j].modulate[0] = mp->color[0];
mp->verts[j].modulate[1] = mp->color[1];
mp->verts[j].modulate[2] = mp->color[2];
}
}
cgi_R_AddPolyToScene( mp->markShader, mp->poly.numVerts, mp->verts );
}
}

417
code/cgame/cg_media.h Normal file
View File

@@ -0,0 +1,417 @@
#ifndef __CG_MEDIA_H_
#define __CG_MEDIA_H_
//#define NUM_CROSSHAIRS 9
#define NUM_CROSSHAIRS 1
typedef enum {
FOOTSTEP_STONEWALK,
FOOTSTEP_STONERUN,
FOOTSTEP_METALWALK,
FOOTSTEP_METALRUN,
FOOTSTEP_PIPEWALK,
FOOTSTEP_PIPERUN,
FOOTSTEP_SPLASH,
FOOTSTEP_WADE,
FOOTSTEP_SWIM,
FOOTSTEP_SNOWWALK,
FOOTSTEP_SNOWRUN,
FOOTSTEP_SANDWALK,
FOOTSTEP_SANDRUN,
FOOTSTEP_GRASSWALK,
FOOTSTEP_GRASSRUN,
FOOTSTEP_DIRTWALK,
FOOTSTEP_DIRTRUN,
FOOTSTEP_MUDWALK,
FOOTSTEP_MUDRUN,
FOOTSTEP_GRAVELWALK,
FOOTSTEP_GRAVELRUN,
FOOTSTEP_RUGWALK,
FOOTSTEP_RUGRUN,
FOOTSTEP_WOODWALK,
FOOTSTEP_WOODRUN,
FOOTSTEP_TOTAL
} footstep_t;
#define ICON_WEAPONS 0
#define ICON_FORCE 1
#define ICON_INVENTORY 2
#define MAX_HUD_TICS 4
typedef struct HUDMenuItem_s
{
char *menuName;
char *itemName;
int xPos;
int yPos;
int width;
int height;
vec4_t color;
qhandle_t background;
} HUDMenuItem_t;
extern HUDMenuItem_t healthTics[];
extern HUDMenuItem_t armorTics[];
extern HUDMenuItem_t ammoTics[];
extern HUDMenuItem_t forceTics[];
extern HUDMenuItem_t otherHUDBits[];
typedef enum
{
OHB_HEALTHAMOUNT = 0,
OHB_ARMORAMOUNT,
OHB_FORCEAMOUNT,
OHB_AMMOAMOUNT,
OHB_SABERSTYLE_STRONG,
OHB_SABERSTYLE_MEDIUM,
OHB_SABERSTYLE_FAST,
OHB_SCANLINE_LEFT,
OHB_SCANLINE_RIGHT,
OHB_FRAME_LEFT,
OHB_FRAME_RIGHT,
OHB_MAX
} otherhudbits_t;
#define NUM_CHUNK_MODELS 4
typedef enum
{
CHUNK_METAL1 = 0,
CHUNK_METAL2,
CHUNK_ROCK1,
CHUNK_ROCK2,
CHUNK_ROCK3,
CHUNK_CRATE1,
CHUNK_CRATE2,
CHUNK_WHITE_METAL,
NUM_CHUNK_TYPES
};
// all of the model, shader, and sound references that are
// loaded at gamestate time are stored in cgMedia_t
// Other media that can be tied to clients, weapons, or items are
// stored in the clientInfo_t, itemInfo_t, weaponInfo_t, and powerupInfo_t
typedef struct {
qhandle_t charsetShader;
qhandle_t whiteShader;
qhandle_t crosshairShader[NUM_CROSSHAIRS];
qhandle_t backTileShader;
// qhandle_t noammoShader;
// qhandle_t numberShaders[11];
qhandle_t smallnumberShaders[11];
// qhandle_t chunkyNumberShaders[11];
qhandle_t loadTick;
// qhandle_t loadTickCap;
// HUD artwork
// int currentBackground;
// qhandle_t weaponbox;
// qhandle_t weaponIconBackground;
// qhandle_t forceIconBackground;
// qhandle_t inventoryIconBackground;
qhandle_t turretComputerOverlayShader;
qhandle_t turretCrossHairShader;
//Chunks
qhandle_t chunkModels[NUM_CHUNK_TYPES][4];
sfxHandle_t chunkSound;
sfxHandle_t grateSound;
sfxHandle_t rockBreakSound;
sfxHandle_t rockBounceSound[2];
sfxHandle_t metalBounceSound[2];
sfxHandle_t glassChunkSound;
sfxHandle_t crateBreakSound[2];
// Saber shaders
//-----------------------------
qhandle_t forceCoronaShader;
qhandle_t saberBlurShader;
qhandle_t swordTrailShader;
qhandle_t yellowDroppedSaberShader; // glow
qhandle_t redSaberGlowShader;
qhandle_t redSaberCoreShader;
qhandle_t orangeSaberGlowShader;
qhandle_t orangeSaberCoreShader;
qhandle_t yellowSaberGlowShader;
qhandle_t yellowSaberCoreShader;
qhandle_t greenSaberGlowShader;
qhandle_t greenSaberCoreShader;
qhandle_t blueSaberGlowShader;
qhandle_t blueSaberCoreShader;
qhandle_t purpleSaberGlowShader;
qhandle_t purpleSaberCoreShader;
qhandle_t explosionModel;
qhandle_t surfaceExplosionShader;
qhandle_t halfShieldModel;
qhandle_t solidWhiteShader;
qhandle_t electricBodyShader;
qhandle_t electricBody2Shader;
qhandle_t refractShader;
qhandle_t boltShader;
// Disruptor zoom graphics
qhandle_t disruptorMask;
qhandle_t disruptorInsert;
qhandle_t disruptorLight;
qhandle_t disruptorInsertTick;
// Binocular graphics
/*
qhandle_t binocularCircle;
qhandle_t binocularMask;
qhandle_t binocularArrow;
qhandle_t binocularTri;
qhandle_t binocularStatic;
qhandle_t binocularOverlay;
*/
// LA Goggles graphics
/*
qhandle_t laGogglesStatic;
qhandle_t laGogglesMask;
qhandle_t laGogglesSideBit;
qhandle_t laGogglesBracket;
qhandle_t laGogglesArrow;
*/
// wall mark shaders
qhandle_t scavMarkShader;
qhandle_t rivetMarkShader;
qhandle_t shadowMarkShader;
qhandle_t wakeMarkShader;
qhandle_t fsrMarkShader;
qhandle_t fslMarkShader;
qhandle_t fshrMarkShader;
qhandle_t fshlMarkShader;
qhandle_t damageBlendBlobShader;
// fonts...
//
qhandle_t qhFontSmall;
qhandle_t qhFontMedium;
// special effects models / etc.
qhandle_t personalShieldShader;
qhandle_t cloakedShader;
// Interface media
qhandle_t ammoslider;
// qhandle_t emplacedHealthBarShader;
qhandle_t DPForcePowerOverlay;
qhandle_t bdecal_burnmark1;
qhandle_t bdecal_saberglowmark;
qhandle_t messageLitOn;
qhandle_t messageLitOff;
qhandle_t messageObjCircle;
// qhandle_t batteryChargeShader;
qhandle_t useableHint;
qhandle_t levelLoad;
//new stuff for Jedi Academy
//force power icons
// qhandle_t forcePowerIcons[NUM_FORCE_POWERS];
qhandle_t rageRecShader;
qhandle_t playerShieldDamage;
qhandle_t forceSightBubble;
qhandle_t forceShell;
qhandle_t sightShell;
qhandle_t drainShader;
// sounds
sfxHandle_t disintegrateSound;
sfxHandle_t disintegrate2Sound;
sfxHandle_t disintegrate3Sound;
sfxHandle_t grenadeBounce1;
sfxHandle_t grenadeBounce2;
sfxHandle_t flechetteStickSound;
sfxHandle_t detPackStickSound;
sfxHandle_t tripMineStickSound;
sfxHandle_t selectSound;
sfxHandle_t selectSound2;
sfxHandle_t overchargeSlowSound;
sfxHandle_t overchargeFastSound;
sfxHandle_t overchargeLoopSound;
sfxHandle_t overchargeEndSound;
// sfxHandle_t useNothingSound;
sfxHandle_t footsteps[FOOTSTEP_TOTAL][4];
// sfxHandle_t talkSound;
sfxHandle_t noAmmoSound;
sfxHandle_t landSound;
sfxHandle_t rollSound;
sfxHandle_t messageLitSound;
sfxHandle_t batteryChargeSound;
sfxHandle_t watrInSound;
sfxHandle_t watrOutSound;
sfxHandle_t watrUnSound;
sfxHandle_t lavaInSound;
sfxHandle_t lavaOutSound;
sfxHandle_t lavaUnSound;
sfxHandle_t noforceSound;
// Zoom
sfxHandle_t zoomStart;
sfxHandle_t zoomLoop;
sfxHandle_t zoomEnd;
sfxHandle_t disruptorZoomLoop;
//new stuff for Jedi Academy
sfxHandle_t drainSound;
#ifdef _IMMERSION
//force feedback stuff
ffHandle_t grenadeBounce1Force;
ffHandle_t grenadeBounce2Force;
ffHandle_t selectForce;
ffHandle_t footstepForces[FOOTSTEP_TOTAL][4];
ffHandle_t noAmmoForce;
ffHandle_t landForce;
ffHandle_t messageLitForce;
ffHandle_t watrInForce;
ffHandle_t watrOutForce;
ffHandle_t watrUnForce;
ffHandle_t zoomStartForce;
ffHandle_t zoomLoopForce;
ffHandle_t zoomEndForce;
ffHandle_t disruptorZoomLoopForce;
#endif // _IMMERSION
} cgMedia_t;
// Stored FX handles
//--------------------
typedef struct
{
// BRYAR PISTOL
fxHandle_t bryarShotEffect;
fxHandle_t bryarPowerupShotEffect;
fxHandle_t bryarWallImpactEffect;
fxHandle_t bryarWallImpactEffect2;
fxHandle_t bryarWallImpactEffect3;
fxHandle_t bryarFleshImpactEffect;
// BLASTER
fxHandle_t blasterShotEffect;
fxHandle_t blasterOverchargeEffect;
fxHandle_t blasterWallImpactEffect;
fxHandle_t blasterFleshImpactEffect;
// BOWCASTER
fxHandle_t bowcasterShotEffect;
fxHandle_t bowcasterBounceEffect;
fxHandle_t bowcasterImpactEffect;
// FLECHETTE
fxHandle_t flechetteShotEffect;
fxHandle_t flechetteAltShotEffect;
fxHandle_t flechetteShotDeathEffect;
fxHandle_t flechetteFleshImpactEffect;
fxHandle_t flechetteRicochetEffect;
//FORCE
fxHandle_t forceConfusion;
fxHandle_t forceLightning;
fxHandle_t forceLightningWide;
//fxHandle_t forceInvincibility;
fxHandle_t forceHeal;
//new stuff for Jedi Academy
fxHandle_t forceDrain;
fxHandle_t forceDrainWide;
fxHandle_t forceDrained;
//footstep effects
fxHandle_t footstepMud;
fxHandle_t footstepSand;
fxHandle_t footstepSnow;
fxHandle_t footstepGravel;
//landing effects
fxHandle_t landingMud;
fxHandle_t landingSand;
fxHandle_t landingDirt;
fxHandle_t landingSnow;
fxHandle_t landingGravel;
} cgEffects_t;
// The client game static (cgs) structure hold everything
// loaded or calculated from the gamestate. It will NOT
// be cleared when a tournement restart is done, allowing
// all clients to begin playing instantly
#define STRIPED_LEVELNAME_VARIATIONS 3 // sigh, to cope with levels that use text from >1 SP file (plus 1 for common)
typedef struct {
gameState_t gameState; // gamestate from server
glconfig_t glconfig; // rendering configuration
int serverCommandSequence; // reliable command stream counter
// parsed from serverinfo
int dmflags;
int teamflags;
int timelimit;
int maxclients;
char mapname[MAX_QPATH];
char stripLevelName[STRIPED_LEVELNAME_VARIATIONS][MAX_QPATH];
//
// locally derived information from gamestate
//
qhandle_t model_draw[MAX_MODELS];
sfxHandle_t sound_precache[MAX_SOUNDS];
#ifdef _IMMERSION
ffHandle_t force_precache[MAX_FORCES];
#endif // _IMMERSION
// Ghoul2 start
qhandle_t skins[MAX_CHARSKINS];
// Ghoul2 end
int numInlineModels;
qhandle_t inlineDrawModel[MAX_SUBMODELS];
vec3_t inlineModelMidpoints[MAX_SUBMODELS];
clientInfo_t clientinfo[MAX_CLIENTS];
// media
cgMedia_t media;
// effects
cgEffects_t effects;
} cgs_t;
extern cgs_t cgs;
#endif //__CG_MEDIA_H_

8262
code/cgame/cg_players.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,364 @@
// cg_playerstate.c -- this file acts on changes in a new playerState_t
// With normal play, this will be done after local prediction, but when
// following another player or playing back a demo, it will be checked
// when the snapshot transitions like all the other entities
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
/*
==============
CG_CheckAmmo
If the ammo has gone low enough to generate the warning, play a sound
==============
*/
void CG_CheckAmmo( void )
{
// int i;
int total;
int previous;
// int weapons;
#if 0
// see about how many seconds of ammo we have remaining
weapons = cg.snap->ps.stats[ STAT_WEAPONS ];
total = 0;
for ( i = WP_SABER; i < WP_NUM_WEAPONS i++ )
{
if ( ! ( weapons & ( 1 << i ) ) )
continue;
/*
switch ( i )
{
case WP_ROCKET_LAUNCHER:
case WP_GRENADE_LAUNCHER:
case WP_RAILGUN:
case WP_SHOTGUN:
total += cg.snap->ps.ammo[i] * 1000;
break;
default:
total += cg.snap->ps.ammo[i] * 200;
break;
}
*/
if ( total >= 5000 )
{
cg.lowAmmoWarning = 0;
return;
}
}
#endif
// Don't bother drawing the ammo warning when have no weapon selected
if ( cg.weaponSelect == WP_NONE )
{
return;
}
total = cg.snap->ps.ammo[weaponData[cg.weaponSelect].ammoIndex];
if (total > weaponData[cg.weaponSelect].ammoLow) // Low on ammo?
{
cg.lowAmmoWarning = 0;
return;
}
previous = cg.lowAmmoWarning;
if (!total) // We're completely freak'in out!
{
cg.lowAmmoWarning = 2;
}
else // Got a little left
{
cg.lowAmmoWarning = 1;
}
// play a sound on transitions
if ( cg.lowAmmoWarning != previous ) {
cgi_S_StartLocalSound( cgs.media.noAmmoSound, CHAN_LOCAL_SOUND ); //"sound/weapons/noammo.wav"
#ifdef _IMMERSION
cgi_FF_Start( cgs.media.noAmmoForce, FF_CLIENT_LOCAL );
#endif // _IMMERSION
}
}
/*
==============
CG_DamageFeedback
==============
*/
void CG_DamageFeedback( int yawByte, int pitchByte, int damage ) {
float left, front, up;
float kick;
int health;
float scale;
vec3_t dir;
vec3_t angles;
float dist;
float yaw, pitch;
//FIXME: Based on MOD, do different kinds of damage effects,
// for example, Borg damage could progressively tint screen green and raise FOV?
// the lower on health you are, the greater the view kick will be
health = cg.snap->ps.stats[STAT_HEALTH];
if ( health < 40 ) {
scale = 1;
} else {
scale = 40.0 / health;
}
kick = damage * scale;
if (kick < 5)
kick = 5;
if (kick > 10)
kick = 10;
// if yaw and pitch are both 255, make the damage always centered (falling, etc)
if ( yawByte == 255 && pitchByte == 255 ) {
cg.damageX = 0;
cg.damageY = 0;
cg.v_dmg_roll = 0;
cg.v_dmg_pitch = -kick;
} else {
// positional
pitch = pitchByte / 255.0 * 360;
yaw = yawByte / 255.0 * 360;
angles[PITCH] = pitch;
angles[YAW] = yaw;
angles[ROLL] = 0;
AngleVectors( angles, dir, NULL, NULL );
VectorSubtract( vec3_origin, dir, dir );
front = DotProduct (dir, cg.refdef.viewaxis[0] );
left = DotProduct (dir, cg.refdef.viewaxis[1] );
up = DotProduct (dir, cg.refdef.viewaxis[2] );
dir[0] = front;
dir[1] = left;
dir[2] = 0;
dist = VectorLength( dir );
if ( dist < 0.1 ) {
dist = 0.1f;
}
cg.v_dmg_roll = kick * left;
cg.v_dmg_pitch = -kick * front;
if ( front <= 0.1 ) {
front = 0.1f;
}
cg.damageX = -left / front;
cg.damageY = up / dist;
}
// clamp the position
if ( cg.damageX > 1.0 ) {
cg.damageX = 1.0;
}
if ( cg.damageX < - 1.0 ) {
cg.damageX = -1.0;
}
if ( cg.damageY > 1.0 ) {
cg.damageY = 1.0;
}
if ( cg.damageY < - 1.0 ) {
cg.damageY = -1.0;
}
// don't let the screen flashes vary as much
if ( kick > 10 ) {
kick = 10;
}
cg.damageValue = kick;
cg.v_dmg_time = cg.time + DAMAGE_TIME;
cg.damageTime = cg.snap->serverTime;
#ifdef _IMMERSION
cgi_FF_Start( cgi_FF_Register( "fffx/player/damage", FF_CHANNEL_DAMAGE ), cg.snap->ps.clientNum );
#endif // _IMMERSION
#ifdef _XBOX
cgi_FF_Xbox_Damage(damage, cg.damageX);
#endif
}
/*
================
CG_Respawn
A respawn happened this snapshot
================
*/
void CG_Respawn( void ) {
// no error decay on player movement
cg.thisFrameTeleport = qtrue;
// display weapons available
// cg.weaponSelectTime = cg.time;
SetWeaponSelectTime();
// select the weapon the server says we are using
if (cg.snap->ps.weapon)
cg.weaponSelect = cg.snap->ps.weapon;
}
/*
==============
CG_CheckPlayerstateEvents
==============
*/
void CG_CheckPlayerstateEvents( playerState_t *ps, playerState_t *ops ) {
int i;
int event;
centity_t *cent;
#if 0
if ( ps->externalEvent && ps->externalEvent != ops->externalEvent ) {
cent = &cg_entities[ ps->clientNum ];
cent->currentState.event = ps->externalEvent;
cent->currentState.eventParm = ps->externalEventParm;
CG_EntityEvent( cent, cent->lerpOrigin );
}
#endif
for ( i = ps->eventSequence - MAX_PS_EVENTS ; i < ps->eventSequence ; i++ ) {
if ( ps->events[i & (MAX_PS_EVENTS-1)] != ops->events[i & (MAX_PS_EVENTS-1)]
|| i >= ops->eventSequence ) {
event = ps->events[ i & (MAX_PS_EVENTS-1) ];
cent = &cg_entities[ ps->clientNum ];
cent->currentState.event = event;
cent->currentState.eventParm = ps->eventParms[ i & (MAX_PS_EVENTS-1) ];
CG_EntityEvent( cent, cent->lerpOrigin );
}
}
}
/*
==================
CG_CheckLocalSounds
==================
*/
/*
void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) {
const char *s;
// hit changes
if ( ps->persistant[PERS_HITS] > ops->persistant[PERS_HITS] ) {
cgi_S_StartLocalSound( "sound/feedback/hit.wav" );
} else if ( ps->persistant[PERS_HITS] < ops->persistant[PERS_HITS] ) {
cgi_S_StartLocalSound( "sound/feedback/hit_teammate.wav" );
}
// score up / down changes
if ( ps->persistant[PERS_SCORE] > ops->persistant[PERS_SCORE] ) {
cgi_S_StartLocalSound( "sound/feedback/scoreup.wav" );
} else if ( ps->persistant[PERS_SCORE] < ops->persistant[PERS_SCORE] ) {
cgi_S_StartLocalSound( "sound/feedback/scoredown.wav" );
}
// reward sounds
if ( ps->persistant[PERS_REWARD_COUNT] > ops->persistant[PERS_REWARD_COUNT] ) {
switch ( ps->persistant[PERS_REWARD] ) {
case REWARD_IMPRESSIVE:
cgi_S_StartLocalSound( "sound/feedback/impressive.wav" );
break;
case REWARD_EXCELLENT:
cgi_S_StartLocalSound( "sound/feedback/excellent.wav" );
break;
case REWARD_DENIED:
cgi_S_StartLocalSound( "sound/feedback/denied.wav" );
break;
default:
CG_Error( "Bad reward_t" );
}
}
// timelimit warnings
if ( cgs.timelimit > 0 ) {
if ( cgs.timelimit > 5 && !( cg.timelimitWarnings & 1 ) && cg.time > (cgs.timelimit - 5) * 60 * 1000 ) {
cg.timelimitWarnings |= 1;
cgi_S_StartLocalSound( "sound/feedback/5_minute.wav" );
}
if ( !( cg.timelimitWarnings & 2 ) && cg.time > (cgs.timelimit - 1) * 60 * 1000 ) {
cg.timelimitWarnings |= 2;
cgi_S_StartLocalSound( "sound/feedback/1_minute.wav" );
}
if ( !( cg.timelimitWarnings & 4 ) && cg.time > ( cgs.timelimit * 60 + 2 ) * 1000 ) {
cg.timelimitWarnings |= 4;
cgi_S_StartLocalSound( "sound/feedback/sudden_death.wav" );
}
}
}
*/
/*
===============
CG_TransitionPlayerState
===============
*/
void CG_TransitionPlayerState( playerState_t *ps, playerState_t *ops ) {
// teleporting
if ( ( ps->eFlags ^ ops->eFlags ) & EF_TELEPORT_BIT ) {
cg.thisFrameTeleport = qtrue;
} else {
cg.thisFrameTeleport = qfalse;
}
// check for changing follow mode
if ( ps->clientNum != ops->clientNum ) {
cg.thisFrameTeleport = qtrue;
// make sure we don't get any unwanted transition effects
*ops = *ps;
}
// damage events (player is getting wounded)
if ( ps->damageEvent != ops->damageEvent && ps->damageCount ) {
CG_DamageFeedback( ps->damageYaw, ps->damagePitch, ps->damageCount );
}
// respawning
if ( ps->persistant[PERS_SPAWN_COUNT] != ops->persistant[PERS_SPAWN_COUNT] ) {
CG_Respawn();
}
// check for going low on ammo
CG_CheckAmmo();
// run events
CG_CheckPlayerstateEvents( ps, ops );
// smooth the ducking viewheight change
if ( ps->viewheight != ops->viewheight )
{
if ( !cg.nextFrameTeleport )
{//when we crouch/uncrouch in mid-air, our viewhieght doesn't actually change in
//absolute world coordinates, just locally.
cg.duckChange = ps->viewheight - ops->viewheight;
cg.duckTime = cg.time;
}
}
}

780
code/cgame/cg_predict.cpp Normal file
View File

@@ -0,0 +1,780 @@
// cg_predict.c -- this file generates cg.predicted_player_state by either
// interpolating between snapshots from the server or locally predicting
// ahead the client's movement
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "..\game\g_vehicles.h"
static pmove_t cg_pmove;
static int cg_numSolidEntities;
static centity_t *cg_solidEntities[MAX_ENTITIES_IN_SNAPSHOT];
#if MEM_DEBUG
#include "..\smartheap\heapagnt.h"
#define CG_TRACE_PROFILE (0)
#endif
/*
====================
CG_BuildSolidList
When a new cg.snap has been set, this function builds a sublist
of the entities that are actually solid, to make for more
efficient collision detection
====================
*/
void CG_BuildSolidList( void )
{
int i;
centity_t *cent;
vec3_t difference;
float dsquared;
cg_numSolidEntities = 0;
if(!cg.snap)
{
return;
}
for ( i = 0 ; i < cg.snap->numEntities ; i++ )
{
if ( cg.snap->entities[ i ].number < ENTITYNUM_WORLD )
{
cent = &cg_entities[ cg.snap->entities[ i ].number ];
if ( cent->gent != NULL && cent->gent->s.solid )
{
cg_solidEntities[cg_numSolidEntities] = cent;
cg_numSolidEntities++;
}
}
}
dsquared = 5000+500;
dsquared *= dsquared;
for(i=0;i<cg_numpermanents;i++)
{
cent = cg_permanents[i];
VectorSubtract(cent->lerpOrigin, cg.snap->ps.origin, difference);
if (cent->currentState.eType == ET_TERRAIN ||
((difference[0]*difference[0]) + (difference[1]*difference[1]) + (difference[2]*difference[2])) <= dsquared)
{
cent->currentValid = qtrue;
if ( cent->nextState && cent->nextState->solid )
{
cg_solidEntities[cg_numSolidEntities] = cent;
cg_numSolidEntities++;
}
}
else
{
cent->currentValid = qfalse;
}
}
}
/*
====================
CG_ClipMoveToEntities
====================
*/
void CG_ClipMoveToEntities ( const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end,
int skipNumber, int mask, trace_t *tr ) {
int i, x, zd, zu;
trace_t trace;
entityState_t *ent;
clipHandle_t cmodel;
vec3_t bmins, bmaxs;
vec3_t origin, angles;
centity_t *cent;
for ( i = 0 ; i < cg_numSolidEntities ; i++ ) {
cent = cg_solidEntities[ i ];
ent = &cent->currentState;
if ( ent->number == skipNumber ) {
continue;
}
if ( ent->eType == ET_PUSH_TRIGGER ) {
continue;
}
if ( ent->eType == ET_TELEPORT_TRIGGER ) {
continue;
}
if ( ent->solid == SOLID_BMODEL ) {
// special value for bmodel
cmodel = cgi_CM_InlineModel( ent->modelindex );
VectorCopy( cent->lerpAngles, angles );
//Hmm... this would cause traces against brush movers to snap at 20fps (as with the third person camera)...
//Let's use the lerpOrigin for now and see if it breaks anything...
//EvaluateTrajectory( &cent->currentState.pos, cg.snap->serverTime, origin );
VectorCopy( cent->lerpOrigin, origin );
} else {
// encoded bbox
x = (ent->solid & 255);
zd = ((ent->solid>>8) & 255);
zu = ((ent->solid>>16) & 255) - 32;
bmins[0] = bmins[1] = -x;
bmaxs[0] = bmaxs[1] = x;
bmins[2] = -zd;
bmaxs[2] = zu;
cmodel = cgi_CM_TempBoxModel( bmins, bmaxs );//, cent->gent->contents );
VectorCopy( vec3_origin, angles );
VectorCopy( cent->lerpOrigin, origin );
}
cgi_CM_TransformedBoxTrace ( &trace, start, end,
mins, maxs, cmodel, mask, origin, angles);
if (trace.allsolid || trace.fraction < tr->fraction) {
trace.entityNum = ent->number;
*tr = trace;
} else if (trace.startsolid) {
tr->startsolid = qtrue;
}
if ( tr->allsolid ) {
return;
}
}
}
/*
================
CG_Trace
================
*/
void CG_Trace( trace_t *result, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end,
const int skipNumber, const int mask, const EG2_Collision eG2TraceType/*=G2_NOCOLLIDE*/, const int useLod/*=0*/) {
trace_t t;
#if CG_TRACE_PROFILE
#if MEM_DEBUG
{
int old=dbgMemSetCheckpoint(2004);
malloc(1);
dbgMemSetCheckpoint(old);
}
#endif
#endif
cgi_CM_BoxTrace ( &t, start, end, mins, maxs, 0, mask);
t.entityNum = t.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
// check all other solid models
CG_ClipMoveToEntities (start, mins, maxs, end, skipNumber, mask, &t);
*result = t;
}
/*
================
CG_PointContents
================
*/
#define USE_SV_PNT_CONTENTS (1)
#if USE_SV_PNT_CONTENTS
int CG_PointContents( const vec3_t point, int passEntityNum ) {
return gi.pointcontents(point,passEntityNum );
}
#else
int CG_PointContents( const vec3_t point, int passEntityNum ) {
int i;
entityState_t *ent;
centity_t *cent;
clipHandle_t cmodel;
int contents;
#if CG_TRACE_PROFILE
#if MEM_DEBUG
{
int old=dbgMemSetCheckpoint(2005);
malloc(1);
dbgMemSetCheckpoint(old);
}
#endif
#endif
contents = cgi_CM_PointContents (point, 0);
for ( i = 0 ; i < cg_numSolidEntities ; i++ ) {
cent = cg_solidEntities[ i ];
ent = &cent->currentState;
if ( ent->number == passEntityNum ) {
continue;
}
if (ent->solid != SOLID_BMODEL) { // special value for bmodel
continue;
}
cmodel = cgi_CM_InlineModel( ent->modelindex );
if ( !cmodel ) {
continue;
}
contents |= cgi_CM_TransformedPointContents( point, cmodel, ent->origin, ent->angles );
}
return contents;
}
#endif
void CG_SetClientViewAngles( vec3_t angles, qboolean overrideViewEnt )
{
if ( cg.snap->ps.viewEntity <= 0 || cg.snap->ps.viewEntity >= ENTITYNUM_WORLD || overrideViewEnt )
{//don't clamp angles when looking through a viewEntity
for( int i = 0; i < 3; i++ )
{
cg.predicted_player_state.viewangles[i] = angles[i];
cg.predicted_player_state.delta_angles[i] = 0;
cg.snap->ps.viewangles[i] = angles[i];
cg.snap->ps.delta_angles[i] = 0;
g_entities[0].client->pers.cmd_angles[i] = ANGLE2SHORT(angles[i]);
}
cgi_SetUserCmdAngles( angles[PITCH], angles[YAW], angles[ROLL] );
}
}
extern qboolean PM_AdjustAnglesToGripper( gentity_t *gent, usercmd_t *cmd );
extern qboolean PM_AdjustAnglesForSpinningFlip( gentity_t *ent, usercmd_t *ucmd, qboolean anglesOnly );
extern qboolean G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd );
extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent );
qboolean CG_CheckModifyUCmd( usercmd_t *cmd, vec3_t viewangles )
{
qboolean overridAngles = qfalse;
if ( cg.snap->ps.viewEntity > 0 && cg.snap->ps.viewEntity < ENTITYNUM_WORLD )
{//controlling something else
memset( cmd, 0, sizeof( usercmd_t ) );
/*
//to keep pointing in same dir, need to set cmd.angles
cmd->angles[PITCH] = ANGLE2SHORT( cg.snap->ps.viewangles[PITCH] ) - cg.snap->ps.delta_angles[PITCH];
cmd->angles[YAW] = ANGLE2SHORT( cg.snap->ps.viewangles[YAW] ) - cg.snap->ps.delta_angles[YAW];
cmd->angles[ROLL] = 0;
*/
VectorCopy( g_entities[0].pos4, viewangles );
overridAngles = qtrue;
//CG_SetClientViewAngles( g_entities[cg.snap->ps.viewEntity].client->ps.viewangles, qtrue );
}
else if ( G_IsRidingVehicle( &g_entities[0] ) )
{
overridAngles = qtrue;
/*
int vehIndex = g_entities[0].owner->client->ps.vehicleIndex;
if ( vehIndex != VEHICLE_NONE
&& (vehicleData[vehIndex].type == VH_FIGHTER || (vehicleData[vehIndex].type == VH_SPEEDER )) )
{//in vehicle flight mode
float speed = VectorLength( cg.snap->ps.velocity );
if ( !speed || cg.snap->ps.groundEntityNum != ENTITYNUM_NONE )
{
cmd->rightmove = 0;
cmd->angles[PITCH] = 0;
cmd->angles[YAW] = ANGLE2SHORT( cg.snap->ps.viewangles[YAW] ) - cg.snap->ps.delta_angles[YAW];
CG_SetClientViewAngles( cg.snap->ps.viewangles, qfalse );
}
}
*/
}
if ( &g_entities[0] && g_entities[0].client )
{
if ( !PM_AdjustAnglesToGripper( &g_entities[0], cmd ) )
{
if ( PM_AdjustAnglesForSpinningFlip( &g_entities[0], cmd, qtrue ) )
{
CG_SetClientViewAngles( g_entities[0].client->ps.viewangles, qfalse );
if ( viewangles )
{
VectorCopy( g_entities[0].client->ps.viewangles, viewangles );
overridAngles = qtrue;
}
}
}
else
{
CG_SetClientViewAngles( g_entities[0].client->ps.viewangles, qfalse );
if ( viewangles )
{
VectorCopy( g_entities[0].client->ps.viewangles, viewangles );
overridAngles = qtrue;
}
}
if ( G_CheckClampUcmd( &g_entities[0], cmd ) )
{
CG_SetClientViewAngles( g_entities[0].client->ps.viewangles, qfalse );
if ( viewangles )
{
VectorCopy( g_entities[0].client->ps.viewangles, viewangles );
overridAngles = qtrue;
}
}
}
return overridAngles;
}
qboolean CG_OnMovingPlat( playerState_t *ps )
{
if ( ps->groundEntityNum != ENTITYNUM_NONE )
{
entityState_t *es = &cg_entities[ps->groundEntityNum].currentState;
if ( es->eType == ET_MOVER )
{//on a mover
if ( es->pos.trType != TR_STATIONARY )
{
if ( es->pos.trType != TR_LINEAR_STOP && es->pos.trType != TR_NONLINEAR_STOP )
{//a constant mover
if ( !VectorCompare( vec3_origin, es->pos.trDelta ) )
{//is moving
return qtrue;
}
}
else
{//a linear-stop mover
if ( es->pos.trTime+es->pos.trDuration > cg.time )
{//still moving
return qtrue;
}
}
}
}
}
return qfalse;
}
/*
========================
CG_InterpolatePlayerState
Generates cg.predicted_player_state by interpolating between
cg.snap->player_state and cg.nextFrame->player_state
========================
*/
void CG_InterpolatePlayerState( qboolean grabAngles ) {
float f;
int i;
playerState_t *out;
snapshot_t *prev, *next;
qboolean skip = qfalse;
vec3_t oldOrg;
out = &cg.predicted_player_state;
prev = cg.snap;
next = cg.nextSnap;
VectorCopy(out->origin,oldOrg);
*out = cg.snap->ps;
// if we are still allowing local input, short circuit the view angles
if ( grabAngles ) {
usercmd_t cmd;
int cmdNum;
cmdNum = cgi_GetCurrentCmdNumber();
cgi_GetUserCmd( cmdNum, &cmd );
skip = CG_CheckModifyUCmd( &cmd, out->viewangles );
if ( !skip )
{
//NULL so that it doesn't execute a block of code that must be run from game
PM_UpdateViewAngles( out, &cmd, NULL );
}
}
// if the next frame is a teleport, we can't lerp to it
if ( cg.nextFrameTeleport )
{
return;
}
if (!( !next || next->serverTime <= prev->serverTime ) )
{
f = (float)( cg.time - prev->serverTime ) / ( next->serverTime - prev->serverTime );
i = next->ps.bobCycle;
if ( i < prev->ps.bobCycle )
{
i += 256; // handle wraparound
}
out->bobCycle = prev->ps.bobCycle + f * ( i - prev->ps.bobCycle );
for ( i = 0 ; i < 3 ; i++ )
{
out->origin[i] = prev->ps.origin[i] + f * (next->ps.origin[i] - prev->ps.origin[i] );
if ( !grabAngles )
{
out->viewangles[i] = LerpAngle(
prev->ps.viewangles[i], next->ps.viewangles[i], f );
}
out->velocity[i] = prev->ps.velocity[i] +
f * (next->ps.velocity[i] - prev->ps.velocity[i] );
}
}
bool onPlat=false;
centity_t *pent=0;
if (out->groundEntityNum>0)
{
pent=&cg_entities[out->groundEntityNum];
if (pent->currentState.eType == ET_MOVER )
{
onPlat=true;
}
}
if (
cg.validPPS &&
cg_smoothPlayerPos.value>0.0f &&
cg_smoothPlayerPos.value<1.0f &&
!onPlat
)
{
// 0 = no smoothing, 1 = no movement
for (i=0;i<3;i++)
{
out->origin[i]=cg_smoothPlayerPos.value*(oldOrg[i]-out->origin[i])+out->origin[i];
}
}
else if (onPlat&&cg_smoothPlayerPlat.value>0.0f&&cg_smoothPlayerPlat.value<1.0f)
{
// if (cg.frametime<150)
// {
assert(pent);
vec3_t p1,p2,vel;
float lerpTime;
EvaluateTrajectory( &pent->currentState.pos,cg.snap->serverTime, p1 );
if ( cg.nextSnap &&cg.nextSnap->serverTime > cg.snap->serverTime && pent->nextState)
{
EvaluateTrajectory( &pent->nextState->pos,cg.nextSnap->serverTime, p2 );
lerpTime=float(cg.nextSnap->serverTime - cg.snap->serverTime);
}
else
{
EvaluateTrajectory( &pent->currentState.pos,cg.snap->serverTime+50, p2 );
lerpTime=50.0f;
}
float accel=cg_smoothPlayerPlatAccel.value*cg.frametime/lerpTime;
if (accel>20.0f)
{
accel=20.0f;
}
for (i=0;i<3;i++)
{
vel[i]=accel*(p2[i]-p1[i]);
}
VectorAdd(out->origin,vel,out->origin);
if (cg.validPPS &&
cg_smoothPlayerPlat.value>0.0f &&
cg_smoothPlayerPlat.value<1.0f
)
{
// 0 = no smoothing, 1 = no movement
for (i=0;i<3;i++)
{
out->origin[i]=cg_smoothPlayerPlat.value*(oldOrg[i]-out->origin[i])+out->origin[i];
}
}
// }
}
}
/*
===================
CG_TouchItem
===================
*/
void CG_TouchItem( centity_t *cent ) {
gitem_t *item;
// never pick an item up twice in a prediction
if ( cent->miscTime == cg.time ) {
return;
}
if ( !BG_PlayerTouchesItem( &cg.predicted_player_state, &cent->currentState, cg.time ) ) {
return;
}
if ( !BG_CanItemBeGrabbed( &cent->currentState, &cg.predicted_player_state ) ) {
return; // can't hold it
}
item = &bg_itemlist[ cent->currentState.modelindex ];
// grab it
AddEventToPlayerstate( EV_ITEM_PICKUP, cent->currentState.modelindex , &cg.predicted_player_state);
// remove it from the frame so it won't be drawn
cent->currentState.eFlags |= EF_NODRAW;
// don't touch it again this prediction
cent->miscTime = cg.time;
// if its a weapon, give them some predicted ammo so the autoswitch will work
if ( item->giType == IT_WEAPON ) {
int ammotype = weaponData[item->giTag].ammoIndex;
cg.predicted_player_state.stats[ STAT_WEAPONS ] |= 1 << item->giTag;
if ( !cg.predicted_player_state.ammo[ ammotype] ) {
cg.predicted_player_state.ammo[ ammotype ] = 1;
}
}
}
/*
=========================
CG_TouchTriggerPrediction
Predict push triggers and items
Only called for the last command
=========================
*/
void CG_TouchTriggerPrediction( void ) {
int i;
trace_t trace;
entityState_t *ent;
clipHandle_t cmodel;
centity_t *cent;
qboolean spectator;
// dead clients don't activate triggers
if ( cg.predicted_player_state.stats[STAT_HEALTH] <= 0 ) {
return;
}
spectator = ( cg.predicted_player_state.pm_type == PM_SPECTATOR );
if ( cg.predicted_player_state.pm_type != PM_NORMAL && !spectator ) {
return;
}
for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
cent = &cg_entities[ cg.snap->entities[ i ].number ];
ent = &cent->currentState;
if ( ent->eType == ET_ITEM && !spectator ) {
CG_TouchItem( cent );
continue;
}
if ( ent->eType != ET_PUSH_TRIGGER && ent->eType != ET_TELEPORT_TRIGGER ) {
continue;
}
if ( ent->solid != SOLID_BMODEL ) {
continue;
}
cmodel = cgi_CM_InlineModel( ent->modelindex );
if ( !cmodel ) {
continue;
}
cgi_CM_BoxTrace( &trace, cg.predicted_player_state.origin, cg.predicted_player_state.origin,
cg_pmove.mins, cg_pmove.maxs, cmodel, -1 );
if ( !trace.startsolid ) {
continue;
}
if ( ent->eType == ET_TELEPORT_TRIGGER ) {
cg.hyperspace = qtrue;
} else {
// we hit this push trigger
if ( spectator ) {
continue;
}
VectorCopy( ent->origin2, cg.predicted_player_state.velocity );
}
}
}
/*
=================
CG_PredictPlayerState
Generates cg.predicted_player_state for the current cg.time
cg.predicted_player_state is guaranteed to be valid after exiting.
For normal gameplay, it will be the result of predicted usercmd_t on
top of the most recent playerState_t received from the server.
Each new refdef will usually have exactly one new usercmd over the last,
but we have to simulate all unacknowledged commands since the last snapshot
received. This means that on an internet connection, quite a few
pmoves may be issued each frame.
OPTIMIZE: don't re-simulate unless the newly arrived snapshot playerState_t
differs from the predicted one.
We detect prediction errors and allow them to be decayed off over several frames
to ease the jerk.
=================
*/
extern qboolean player_locked;
void CG_PredictPlayerState( void ) {
int cmdNum, current;
playerState_t oldPlayerState;
cg.hyperspace = qfalse; // will be set if touching a trigger_teleport
// if this is the first frame we must guarantee
// predicted_player_state is valid even if there is some
// other error condition
if ( !cg.validPPS ) {
cg.validPPS = qtrue;
cg.predicted_player_state = cg.snap->ps;
}
if ( 1 )//cg_timescale.value >= 1.0f )
{
// demo playback just copies the moves
/*
if ( (cg.snap->ps.pm_flags & PMF_FOLLOW) ) {
CG_InterpolatePlayerState( qfalse );
return;
}
*/
// non-predicting local movement will grab the latest angles
CG_InterpolatePlayerState( qtrue );
return;
}
// prepare for pmove
//FIXME: is this bad???
cg_pmove.gent = NULL;
cg_pmove.ps = &cg.predicted_player_state;
cg_pmove.trace = CG_Trace;
cg_pmove.pointcontents = CG_PointContents;
cg_pmove.tracemask = MASK_PLAYERSOLID;
cg_pmove.noFootsteps = 0;//( cgs.dmflags & DF_NO_FOOTSTEPS ) > 0;
// save the state before the pmove so we can detect transitions
oldPlayerState = cg.predicted_player_state;
// if we are too far out of date, just freeze
cmdNum = cg.snap->cmdNum;
current = cgi_GetCurrentCmdNumber();
if ( current - cmdNum >= CMD_BACKUP ) {
return;
}
// get the most recent information we have
cg.predicted_player_state = cg.snap->ps;
// we should always be predicting at least one frame
if ( cmdNum >= current ) {
return;
}
// run cmds
do {
// check for a prediction error from last frame
// on a lan, this will often be the exact value
// from the snapshot, but on a wan we will have
// to predict several commands to get to the point
// we want to compare
if ( cmdNum == current - 1 ) {
vec3_t delta;
float len;
if ( cg.thisFrameTeleport ) {
// a teleport will not cause an error decay
VectorClear( cg.predictedError );
cg.thisFrameTeleport = qfalse;
} else {
vec3_t adjusted;
CG_AdjustPositionForMover( cg.predicted_player_state.origin,
cg.predicted_player_state.groundEntityNum, cg.oldTime, adjusted );
VectorSubtract( oldPlayerState.origin, adjusted, delta );
len = VectorLength( delta );
if ( len > 0.1 ) {
if ( cg_errorDecay.integer ) {
int t;
float f;
t = cg.time - cg.predictedErrorTime;
f = ( cg_errorDecay.value - t ) / cg_errorDecay.value;
if ( f < 0 ) {
f = 0;
}
VectorScale( cg.predictedError, f, cg.predictedError );
} else {
VectorClear( cg.predictedError );
}
VectorAdd( delta, cg.predictedError, cg.predictedError );
cg.predictedErrorTime = cg.oldTime;
}
}
}
// if the command can't be gotten because it is
// too far out of date, the frame is invalid
// this should never happen, because we check ranges at
// the top of the function
cmdNum++;
if ( !cgi_GetUserCmd( cmdNum, &cg_pmove.cmd ) ) {
break;
}
gentity_t *ent = &g_entities[0];//cheating and dirty, I know, but this is a SP game so prediction can cheat
if ( player_locked ||
(ent && !ent->s.number&&ent->aimDebounceTime>level.time) ||
(ent && ent->client && ent->client->ps.pm_time && (ent->client->ps.pm_flags&PMF_TIME_KNOCKBACK)) ||
(ent && ent->forcePushTime > level.time) )
{//lock out player control unless dead
//VectorClear( cg_pmove.cmd.angles );
cg_pmove.cmd.forwardmove = 0;
cg_pmove.cmd.rightmove = 0;
cg_pmove.cmd.buttons = 0;
cg_pmove.cmd.upmove = 0;
}
CG_CheckModifyUCmd( &cg_pmove.cmd, NULL );
//FIXME: prediction on clients in timescale results in jerky positional translation
Pmove( &cg_pmove );
// add push trigger movement effects
CG_TouchTriggerPrediction();
} while ( cmdNum < current );
// adjust for the movement of the groundentity
CG_AdjustPositionForMover( cg.predicted_player_state.origin,
cg.predicted_player_state.groundEntityNum,
cg.time, cg.predicted_player_state.origin );
// fire events and other transition triggered things
CG_TransitionPlayerState( &cg.predicted_player_state, &oldPlayerState );
}

214
code/cgame/cg_public.h Normal file
View File

@@ -0,0 +1,214 @@
#ifndef _CG_PUBLIC_H
#define _CG_PUBLIC_H
#define NUM_EXPLOSION_SHADERS 8
#define NUM_EXPLOSION_FRAMES 3
#define CMD_BACKUP 64
#define CMD_MASK (CMD_BACKUP - 1)
// allow a lot of command backups for very fast systems
// multiple commands may be combined into a single packet, so this
// needs to be larger than PACKET_BACKUP
#define MAX_ENTITIES_IN_SNAPSHOT 512
#define SNAPFLAG_RATE_DELAYED 1 // the server withheld a packet to save bandwidth
#define SNAPFLAG_DROPPED_COMMANDS 2 // the server lost some cmds coming from the client
// snapshots are a view of the server at a given time
// Snapshots are generated at regular time intervals by the server,
// but they may not be sent if a client's rate level is exceeded, or
// they may be dropped by the network.
struct snapshot_s
{
int snapFlags; // SNAPFLAG_RATE_DELAYED, SNAPFLAG_DROPPED_COMMANDS
int ping;
int serverTime; // server time the message is valid for (in msec)
byte areamask[MAX_MAP_AREA_BYTES]; // portalarea visibility bits
int cmdNum; // the next cmdNum the server is expecting
// client side prediction should start with this cmd
playerState_t ps; // complete information about the current player at this time
int numEntities; // all of the entities that need to be presented
entityState_t entities[MAX_ENTITIES_IN_SNAPSHOT]; // at the time of this snapshot
int numConfigstringChanges; // configstrings that have changed since the last
int configstringNum; // acknowledged snapshot_t (which is usually NOT the previous snapshot!)
int numServerCommands; // text based server commands to execute when this
int serverCommandSequence; // snapshot becomes current
};
typedef snapshot_s snapshot_t;
/*
==================================================================
functions imported from the main executable
==================================================================
*/
#define CGAME_IMPORT_API_VERSION 4
typedef enum {
CG_PRINT,
CG_ERROR,
CG_MILLISECONDS,
CG_CVAR_REGISTER,
CG_CVAR_UPDATE,
CG_CVAR_SET,
CG_ARGC,
CG_ARGV,
CG_ARGS,
CG_FS_FOPENFILE,
CG_FS_READ,
CG_FS_WRITE,
CG_FS_FCLOSEFILE,
CG_SENDCONSOLECOMMAND,
CG_ADDCOMMAND,
CG_SENDCLIENTCOMMAND,
CG_UPDATESCREEN,
CG_RMG_INIT,
CG_CM_REGISTER_TERRAIN,
CG_RE_INIT_RENDERER_TERRAIN,
CG_CM_LOADMAP,
CG_CM_NUMINLINEMODELS,
CG_CM_INLINEMODEL,
CG_CM_TEMPBOXMODEL,
CG_CM_POINTCONTENTS,
CG_CM_TRANSFORMEDPOINTCONTENTS,
CG_CM_BOXTRACE,
CG_CM_TRANSFORMEDBOXTRACE,
CG_CM_MARKFRAGMENTS,
CG_CM_SNAPPVS,
CG_S_STARTSOUND,
CG_S_STARTLOCALSOUND,
CG_S_CLEARLOOPINGSOUNDS,
CG_S_ADDLOOPINGSOUND,
CG_S_STOPSOUNDS,
CG_S_UPDATEENTITYPOSITION,
CG_S_RESPATIALIZE,
CG_S_REGISTERSOUND,
CG_S_STARTBACKGROUNDTRACK,
#ifdef _IMMERSION
CG_FF_START,
CG_FF_STOP,
CG_FF_STOPALL,
CG_FF_SHAKE,
CG_FF_REGISTER,
CG_FF_ADDLOOPINGFORCE,
#else
CG_FF_STARTFX,
CG_FF_ENSUREFX,
CG_FF_STOPFX,
CG_FF_STOPALLFX,
#endif // _IMMERSION
#ifdef _XBOX
CG_FF_XBOX_SHAKE,
CG_FF_XBOX_DAMAGE,
#endif
CG_R_LOADWORLDMAP,
CG_R_REGISTERMODEL,
CG_R_REGISTERSKIN,
CG_R_REGISTERSHADER,
CG_R_REGISTERSHADERNOMIP,
CG_R_REGISTERFONT,
CG_R_FONTSTRLENPIXELS,
CG_R_FONTSTRLENCHARS,
CG_R_FONTHEIGHTPIXELS,
CG_R_FONTDRAWSTRING,
CG_LANGUAGE_ISASIAN,
CG_LANGUAGE_USESSPACES,
CG_ANYLANGUAGE_READFROMSTRING,
CG_R_SETREFRACTIONPROP,
CG_R_CLEARSCENE,
CG_R_ADDREFENTITYTOSCENE,
CG_R_INPVS,
CG_R_GETLIGHTING,
CG_R_ADDPOLYTOSCENE,
CG_R_ADDLIGHTTOSCENE,
CG_R_RENDERSCENE,
CG_R_SETCOLOR,
CG_R_DRAWSTRETCHPIC,
//CG_R_DRAWSCREENSHOT,
CG_R_MODELBOUNDS,
CG_R_LERPTAG,
CG_R_DRAWROTATEPIC,
CG_R_DRAWROTATEPIC2,
CG_R_SETRANGEFOG,
CG_R_LA_GOGGLES,
CG_R_SCISSOR,
CG_GETGLCONFIG,
CG_GETGAMESTATE,
CG_GETCURRENTSNAPSHOTNUMBER,
CG_GETSNAPSHOT,
CG_GETDEFAULTSTATE,
CG_GETSERVERCOMMAND,
CG_GETCURRENTCMDNUMBER,
CG_GETUSERCMD,
CG_SETUSERCMDVALUE,
CG_SETUSERCMDANGLES,
CG_S_UPDATEAMBIENTSET,
CG_S_ADDLOCALSET,
CG_AS_PARSESETS,
CG_AS_ADDENTRY,
CG_AS_GETBMODELSOUND,
CG_S_GETSAMPLELENGTH,
COM_SETORGANGLES,
/*
Ghoul2 Insert Start
*/
CG_G2_LISTBONES,
CG_G2_LISTSURFACES,
CG_G2_HAVEWEGHOULMODELS,
CG_G2_SETMODELS,
/*
Ghoul2 Insert End
*/
CG_R_GET_LIGHT_STYLE,
CG_R_SET_LIGHT_STYLE,
CG_R_GET_BMODEL_VERTS,
CG_R_WORLD_EFFECT_COMMAND,
CG_CIN_PLAYCINEMATIC,
CG_CIN_STOPCINEMATIC,
CG_CIN_RUNCINEMATIC,
CG_CIN_DRAWCINEMATIC,
CG_CIN_SETEXTENTS,
CG_Z_MALLOC,
CG_Z_FREE,
CG_UI_MENU_RESET,
CG_UI_MENU_NEW,
CG_UI_SETACTIVE_MENU,
CG_UI_MENU_OPENBYNAME,
CG_UI_PARSE_INT,
CG_UI_PARSE_STRING,
CG_UI_PARSE_FLOAT,
CG_UI_STARTPARSESESSION,
CG_UI_ENDPARSESESSION,
CG_UI_PARSEEXT,
CG_UI_MENUPAINT_ALL,
CG_UI_MENUCLOSE_ALL,
CG_UI_STRING_INIT,
CG_UI_GETMENUINFO,
CG_SP_GETSTRINGTEXTSTRING,
CG_UI_GETITEMTEXT,
CG_UI_GETITEMINFO,
} cgameImport_t;
//----------------------------------------------
#endif _CG_PUBLIC_H

View File

@@ -0,0 +1,187 @@
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "..\game\objectives.h"
#include "..\game\b_local.h"
#include "..\ui\ui_shared.h"
#define SCOREBOARD_WIDTH (26*BIGCHAR_WIDTH)
/*
static void Scoreboard_Draw( void )
{
vec4_t newColor;
/*
player = g_entities[0];
if( player->client->ps.persistant[PERS_ACCURACY_SHOTS] ) {
accuracy = player->client->ps.persistant[PERS_ACCURACY_HITS] * 100 / player->client->ps.persistant[PERS_ACCURACY_SHOTS];
}
*/
/* cg.LCARSTextTime = 0; // Turn off LCARS screen
// Background
newColor[0] = colorTable[CT_BLACK][0];
newColor[1] = colorTable[CT_BLACK][1];
newColor[2] = colorTable[CT_BLACK][2];
newColor[3] = 0.5;
cgi_R_SetColor(newColor);
CG_DrawPic( 137, 73, 475, 300, cgs.media.whiteShader); // Background
CG_DrawPic( 120, 99, 18, 256, cgs.media.whiteShader); // Background
CG_DrawPic( 40, 94, 66, 266, cgs.media.whiteShader); // Background
// Right side box
cgi_R_SetColor( colorTable[CT_DKBROWN1]);
CG_DrawPic( 120, 354, 32, 32, cgs.media.status_corner_16_18);
CG_DrawPic( 94, 356, 16, 32, cgs.media.status_corner_8_16_b);
CG_DrawPic( 94, 73, 16, 32, cgs.media.status_corner_8_22);
CG_DrawPic(135,73, 302, 22, cgs.media.whiteShader); // Top
CG_DrawPic(120, 100, 18, 12, cgs.media.whiteShader); // Middle Top
CG_DrawPic(120, 353, 18, 4, cgs.media.whiteShader); // Middle Bottom
CG_DrawPic(130,357, 482, 18, cgs.media.whiteShader); // Bottom
// Left side box
cgi_R_SetColor( colorTable[CT_DKBROWN1]);
CG_DrawPic(40,73, 56, 22, cgs.media.whiteShader); // Top
CG_DrawPic(98,95, 8, 17, cgs.media.whiteShader); // Middle Top
CG_DrawPic(98,353, 8, 7, cgs.media.whiteShader); // Middle Bottom
CG_DrawPic(40,357, 58, 18, cgs.media.whiteShader); // Bottom
CG_DrawProportionalString( 356, 208, "%", UI_RIGHT | UI_SMALLFONT, colorTable[CT_LTPURPLE1] );
CG_DrawProportionalString( 610, 72, ingame_text[IGT_MISSIONANALYSIS],UI_RIGHT| CG_BIGFONT, colorTable[CT_LTORANGE] );
CG_PrintScreenGraphics(statsmenu_graphics,SMG_MAX);
}
*/
/*
=================
CG_MissionFailed
=================
*/
int statusTextIndex = -1;
void CG_MissionFailed(void)
{
char *text;
if (!cg.missionFailedScreen)
{
cgi_UI_SetActive_Menu("missionfailed_menu");
// If you're in camera mode when when you lose (Chewie kills you)
// then the above fails. This should check for that. We'll end up
// waiting until the camera stuff stops:
if( !Menu_GetFocused() )
return;
cg.missionFailedScreen = qtrue;
switch (statusTextIndex)
{
case -1: //Our HERO DIED!!!
text = "@SP_INGAME_MISSIONFAILED_PLAYER";
break;
case MISSIONFAILED_JAN:
text = "@SP_INGAME_MISSIONFAILED_JAN";
break;
case MISSIONFAILED_LUKE:
text = "@SP_INGAME_MISSIONFAILED_LUKE";
break;
case MISSIONFAILED_LANDO:
text = "@SP_INGAME_MISSIONFAILED_LANDO";
break;
case MISSIONFAILED_R5D2:
text = "@SP_INGAME_MISSIONFAILED_R5D2";
break;
case MISSIONFAILED_WARDEN:
text = "@SP_INGAME_MISSIONFAILED_WARDEN";
break;
case MISSIONFAILED_PRISONERS:
text = "@SP_INGAME_MISSIONFAILED_PRISONERS";
break;
case MISSIONFAILED_EMPLACEDGUNS:
text = "@SP_INGAME_MISSIONFAILED_EMPLACEDGUNS";
break;
case MISSIONFAILED_LADYLUCK:
text = "@SP_INGAME_MISSIONFAILED_LADYLUCK";
break;
case MISSIONFAILED_KYLECAPTURE:
text = "@SP_INGAME_MISSIONFAILED_KYLECAPTURE";
break;
case MISSIONFAILED_TOOMANYALLIESDIED:
text = "@SP_INGAME_MISSIONFAILED_TOOMANYALLIESDIED";
break;
case MISSIONFAILED_CHEWIE:
text = "@SP_INGAME_MISSIONFAILED_CHEWIE";
break;
case MISSIONFAILED_KYLE:
text = "@SP_INGAME_MISSIONFAILED_KYLE";
break;
case MISSIONFAILED_ROSH:
text = "@SP_INGAME_MISSIONFAILED_ROSH";
break;
case MISSIONFAILED_WEDGE:
text = "@SP_INGAME_MISSIONFAILED_WEDGE";
break;
case MISSIONFAILED_TURNED:
text = "@SP_INGAME_MISSIONFAILED_TURNED";
break;
default:
text = "@SP_INGAME_MISSIONFAILED_UNKNOWN";
break;
}
//done with the variable for this time so reset it.
statusTextIndex = -1;
gi.cvar_set("ui_missionfailed_text", text);
}
}
/*
=================
CG_DrawScoreboard
Draw the normal in-game scoreboard
return value is bool to NOT draw centerstring
=================
*/
qboolean CG_DrawScoreboard( void )
{
// don't draw anything if the menu is up
if ( cg_paused.integer )
{
return qfalse;
}
// Character is either dead, or a script has brought up the screen
if (((cg.predicted_player_state.pm_type == PM_DEAD) && (cg.missionStatusDeadTime < level.time))
|| (cg.missionStatusShow))
{
CG_MissionFailed();
return qtrue;
}
return qfalse;
}
void ScoreBoardReset(void)
{
}
//================================================================================

View File

@@ -0,0 +1,259 @@
// cg_servercmds.c -- text commands sent by the server
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
#include "cg_media.h"
#include "FxScheduler.h"
#include "cg_lights.h"
/*
================
CG_ParseServerinfo
This is called explicitly when the gamestate is first received,
and whenever the server updates any serverinfo flagged cvars
================
*/
void CG_ParseServerinfo( void ) {
const char *info;
char *mapname;
info = CG_ConfigString( CS_SERVERINFO );
cgs.dmflags = atoi( Info_ValueForKey( info, "dmflags" ) );
cgs.teamflags = atoi( Info_ValueForKey( info, "teamflags" ) );
cgs.timelimit = atoi( Info_ValueForKey( info, "timelimit" ) );
cgs.maxclients = 1;
mapname = Info_ValueForKey( info, "mapname" );
Com_sprintf( cgs.mapname, sizeof( cgs.mapname ), "maps/%s.bsp", mapname );
char *p = strrchr(mapname,'/');
strcpy( cgs.stripLevelName[0], p?p+1:mapname );
strupr( cgs.stripLevelName[0] );
for (int i=1; i<STRIPED_LEVELNAME_VARIATIONS; i++) // clear retry-array
{
cgs.stripLevelName[i][0]='\0';
}
// be careful with the []-numbers here. Currently I use 0,1,2 for replacements or substitution, and [3] for "INGAME"
// I know, if I'd known there was going to be this much messing about I'd have subroutinised it all and done it
// neater, but it kinda evolved... Feel free to bug me if you want to add to it... ? -Ste.
//
//FIXME: a better way to handle sound-matched strings from other levels (currently uses levelname+sound as key)
// additional String files needed for some levels...
//
// JKA...
if (!stricmp(cgs.stripLevelName[0],"YAVIN1B"))
{
strcpy( cgs.stripLevelName[1], "YAVIN1");
}
/* // JK2...
if (!stricmp(cgs.stripLevelName[0],"KEJIM_BASE") ||
!stricmp(cgs.stripLevelName[0],"KEJIM_POST")
)
{
strcpy( cgs.stripLevelName[1], "ARTUS_MINE" );
}
if (!stricmp(cgs.stripLevelName[0],"DOOM_DETENTION") ||
!stricmp(cgs.stripLevelName[0],"DOOM_SHIELDS")
)
{
strcpy( cgs.stripLevelName[1], "DOOM_COMM" );
}
if (!stricmp(cgs.stripLevelName[0],"DOOM_COMM"))
{
strcpy( cgs.stripLevelName[1], "CAIRN_BAY" );
}
if (!stricmp(cgs.stripLevelName[0],"NS_STARPAD"))
{
strcpy( cgs.stripLevelName[1], "ARTUS_TOPSIDE" ); // for dream sequence...
strcpy( cgs.stripLevelName[2], "BESPIN_UNDERCITY" ); // for dream sequence...
}
if (!stricmp(cgs.stripLevelName[0],"BESPIN_PLATFORM"))
{
strcpy( cgs.stripLevelName[1], "BESPIN_UNDERCITY" );
}
*/
}
/*
================
CG_ConfigStringModified
================
*/
void CG_RegisterClientModels (int entityNum);
extern void cgi_R_WorldEffectCommand( const char *command );
static void CG_ConfigStringModified( void ) {
const char *str;
int num;
num = atoi( CG_Argv( 1 ) );
// get the gamestate from the client system, which will have the
// new configstring already integrated
cgi_GetGameState( &cgs.gameState );
// look up the individual string that was modified
str = CG_ConfigString( num );
// do something with it if necessary
if ( num == CS_ITEMS ) {
int i;
for ( i = 1 ; i < bg_numItems ; i++ ) {
if ( str[ i ] == '1' )
{
if (bg_itemlist[i].classname)
{
CG_RegisterItemSounds( i );
CG_RegisterItemVisuals( i );
}
}
}
}
else if ( num == CS_MUSIC ) {
CG_StartMusic( qtrue );
} else if ( num == CS_SERVERINFO ) {
CG_ParseServerinfo();
} else if ( num >= CS_MODELS && num < CS_MODELS+MAX_MODELS ) {
cgs.model_draw[ num-CS_MODELS ] = cgi_R_RegisterModel( str );
// OutputDebugString(va("### CG_ConfigStringModified(): cgs.model_draw[%d] = \"%s\"\n",num-CS_MODELS,str));
// GHOUL2 Insert start
} else if ( num >= CS_CHARSKINS && num < CS_CHARSKINS+MAX_CHARSKINS ) {
cgs.skins[ num-CS_CHARSKINS ] = cgi_R_RegisterSkin( str );
// Ghoul2 Insert end
} else if ( num >= CS_SOUNDS && num < CS_SOUNDS+MAX_SOUNDS ) {
if ( str[0] != '*' ) {
cgs.sound_precache[ num-CS_SOUNDS] = cgi_S_RegisterSound( str );
}
}
else if ( num >= CS_EFFECTS && num < CS_EFFECTS + MAX_FX )
{
theFxScheduler.RegisterEffect( str );
}
else if ( num >= CS_PLAYERS && num < CS_PLAYERS+MAX_CLIENTS ) {
CG_NewClientinfo( num - CS_PLAYERS );
CG_RegisterClientModels( num - CS_PLAYERS );
}
else if ( num >= CS_LIGHT_STYLES && num < CS_LIGHT_STYLES + (MAX_LIGHT_STYLES*3))
{
CG_SetLightstyle(num - CS_LIGHT_STYLES);
}
else if ( num >= CS_WORLD_FX && num < CS_WORLD_FX + MAX_WORLD_FX )
{
cgi_R_WorldEffectCommand( str );
}
}
/*
=================
CG_ServerCommand
The string has been tokenized and can be retrieved with
Cmd_Argc() / Cmd_Argv()
=================
*/
static void CG_ServerCommand( void ) {
const char *cmd;
cmd = CG_Argv(0);
if ( !strcmp( cmd, "cp" ) ) {
CG_CenterPrint( CG_Argv(1), SCREEN_HEIGHT * 0.25 );
return;
}
if ( !strcmp( cmd, "cs" ) ) {
CG_ConfigStringModified();
return;
}
if ( !strcmp( cmd, "print" ) ) {
CG_Printf( "%s", CG_Argv(1) );
return;
}
if ( !strcmp( cmd, "chat" ) ) {
// cgi_S_StartLocalSound ( cgs.media.talkSound, CHAN_LOCAL_SOUND );
CG_Printf( "%s\n", CG_Argv(1) );
return;
}
// Scroll text
if ( !strcmp( cmd, "st" ) )
{
#ifdef _XBOX
if(cg.widescreen)
CG_ScrollText( CG_Argv(1), 720 - 16 );
else
#endif
CG_ScrollText( CG_Argv(1), SCREEN_WIDTH - 16 );
return;
}
// Cinematic text
if ( !strcmp( cmd, "ct" ) )
{
CG_CaptionText( CG_Argv(1), cgs.sound_precache[atoi(CG_Argv(2))], SCREEN_HEIGHT * 0.25 );
return;
}
// Text stop
if ( !strcmp( cmd, "cts" ) )
{
CG_CaptionTextStop();
return;
}
// Text to appear in center of screen with an LCARS frame around it.
if ( !strcmp( cmd, "lt" ) )
{
CG_Printf("CG_LCARSText() being called. Tell Ste\nString: \"%s\"\n",CG_Argv(1));
return;
}
// clientLevelShot is sent before taking a special screenshot for
// the menu system during development
if ( !strcmp( cmd, "clientLevelShot" ) ) {
cg.levelShot = qtrue;
return;
}
if ( !strcmp( cmd, "vmsg" ) ) {
#if 0
char snd[MAX_QPATH];
Com_sprintf(snd, sizeof(snd),
"sound/teamplay/vmsg/%s.wav", CG_Argv(1) );
cgi_S_StartSound (NULL, cg.snap->ps.clientNum, CHAN_AUTO,
cgi_S_RegisterSound (snd) );
#endif
return;
}
CG_Printf( "Unknown client game command: %s\n", cmd );
}
/*
====================
CG_ExecuteNewServerCommands
Execute all of the server commands that were received along
with this this snapshot.
====================
*/
void CG_ExecuteNewServerCommands( int latestSequence ) {
while ( cgs.serverCommandSequence < latestSequence ) {
if ( cgi_GetServerCommand( ++cgs.serverCommandSequence ) ) {
CG_ServerCommand();
}
}
}

407
code/cgame/cg_snapshot.cpp Normal file
View File

@@ -0,0 +1,407 @@
// cg_snapshot.c -- things that happen on snapshot transition,
// not necessarily every single frame
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
/*
==================
CG_ResetEntity
==================
*/
void CG_ResetEntity( centity_t *cent ) {
// if an event is set, assume it is new enough to use
// if the event had timed out, it would have been cleared
cent->previousEvent = 0;
// cent->trailTime = cg.snap->serverTime;
VectorCopy (cent->currentState.origin, cent->lerpOrigin);
VectorCopy (cent->currentState.angles, cent->lerpAngles);
if ( cent->currentState.eType == ET_PLAYER ) {
CG_ResetPlayerEntity( cent );
}
}
/*
===============
CG_TransitionEntity
cent->nextState is moved to cent->currentState and events are fired
===============
*/
void CG_TransitionEntity( centity_t *cent ) {
if (cent->nextState) {
cent->currentState = *cent->nextState;
}
cent->currentValid = qtrue;
// reset if the entity wasn't in the last frame or was teleported
if ( !cent->interpolate ) {
CG_ResetEntity( cent );
}
// clear the next state. if will be set by the next CG_SetNextSnap
cent->interpolate = qfalse;
if ( cent->currentState.number != 0 )
{
// check for events
CG_CheckEvents( cent );
}
}
/*
==================
CG_SetInitialSnapshot
This will only happen on the very first snapshot, or
on tourney restarts. All other times will use
CG_TransitionSnapshot instead.
==================
*/
void CG_SetInitialSnapshot( snapshot_t *snap ) {
int i;
centity_t *cent;
entityState_t *state;
cg.snap = snap;
// sort out solid entities
//CG_BuildSolidList();
CG_ExecuteNewServerCommands( snap->serverCommandSequence );
// set our local weapon selection pointer to
// what the server has indicated the current weapon is
CG_Respawn();
for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
state = &cg.snap->entities[ i ];
cent = &cg_entities[ state->number ];
cent->currentState = *state;
cent->interpolate = qfalse;
cent->currentValid = qtrue;
CG_ResetEntity( cent );
// check for events
CG_CheckEvents( cent );
}
}
/*
===================
CG_TransitionSnapshot
The transition point from snap to nextSnap has passed
===================
*/
void CG_TransitionSnapshot( void ) {
centity_t *cent;
snapshot_t *oldFrame;
int i;
if ( !cg.snap ) {
CG_Error( "CG_TransitionSnapshot: NULL cg.snap" );
}
if ( !cg.nextSnap ) {
CG_Error( "CG_TransitionSnapshot: NULL cg.nextSnap" );
}
// execute any server string commands before transitioning entities
CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence );
// clear the currentValid flag for all entities in the existing snapshot
for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
cent = &cg_entities[ cg.snap->entities[ i ].number ];
cent->currentValid = qfalse;
}
// move nextSnap to snap and do the transitions
oldFrame = cg.snap;
cg.snap = cg.nextSnap;
// sort out solid entities
//CG_BuildSolidList();
for ( i = 0 ; i < cg.snap->numEntities ; i++ )
{
if ( 1 ) //cg.snap->entities[ i ].number != 0 ) // I guess the player adds his/her events elsewhere, so doing this also gives us double events for the player!
{
cent = &cg_entities[ cg.snap->entities[ i ].number ];
CG_TransitionEntity( cent );
}
}
cg.nextSnap = NULL;
// check for playerstate transition events
if ( oldFrame ) {
// if we are not doing client side movement prediction for any
// reason, then the client events and view changes will be issued now
//if ( cg_timescale.value >= 1.0f )
{
CG_TransitionPlayerState( &cg.snap->ps, &oldFrame->ps );
}
}
}
/*
===============
CG_SetEntityNextState
Determine if the entity can be interpolated between the states
present in cg.snap and cg,nextSnap
===============
*/
void CG_SetEntityNextState( centity_t *cent, entityState_t *state ) {
cent->nextState = state;
// since we can't interpolate ghoul2 stuff from one frame to another, I'm just going to copy the ghoul2 info directly into the current state now
// CGhoul2Info *currentModel = &state->ghoul2[1];
// cent->gent->ghoul2 = state->ghoul2;
// CGhoul2Info *newModel = &cent->gent->ghoul2[1];
// if this frame is a teleport, or the entity wasn't in the
// previous frame, don't interpolate
if ( !cent->currentValid || ( ( cent->currentState.eFlags ^ state->eFlags ) & EF_TELEPORT_BIT ) ) {
cent->interpolate = qfalse;
} else {
cent->interpolate = qtrue;
}
}
/*
===================
CG_SetNextSnap
A new snapshot has just been read in from the client system.
===================
*/
void CG_SetNextSnap( snapshot_t *snap ) {
int num;
entityState_t *es;
centity_t *cent;
cg.nextSnap = snap;
// check for extrapolation errors
for ( num = 0 ; num < snap->numEntities ; num++ ) {
es = &snap->entities[num];
cent = &cg_entities[ es->number ];
CG_SetEntityNextState( cent, es );
}
// if the next frame is a teleport for the playerstate,
if ( cg.snap && ( ( snap->ps.eFlags ^ cg.snap->ps.eFlags ) & EF_TELEPORT_BIT ) ) {
cg.nextFrameTeleport = qtrue;
} else {
cg.nextFrameTeleport = qfalse;
}
}
/*
========================
CG_ReadNextSnapshot
This is the only place new snapshots are requested
This may increment cg.processedSnapshotNum multiple
times if the client system fails to return a
valid snapshot.
========================
*/
snapshot_t *CG_ReadNextSnapshot( void ) {
qboolean r;
snapshot_t *dest;
while ( cg.processedSnapshotNum < cg.latestSnapshotNum ) {
// decide which of the two slots to load it into
if ( cg.snap == &cg.activeSnapshots[0] ) {
dest = &cg.activeSnapshots[1];
} else {
dest = &cg.activeSnapshots[0];
}
// try to read the snapshot from the client system
cg.processedSnapshotNum++;
r = cgi_GetSnapshot( cg.processedSnapshotNum, dest );
// if it succeeded, return
if ( r ) {
return dest;
}
// a GetSnapshot will return failure if the snapshot
// never arrived, or is so old that its entities
// have been shoved off the end of the circular
// buffer in the client system.
// record as a dropped packet
// CG_AddLagometerSnapshotInfo( NULL );
// If there are additional snapshots, continue trying to
// read them.
}
// nothing left to read
return NULL;
}
/*
=================
CG_RestartLevel
A tournement restart will clear everything, but doesn't
require a reload of all the media
=================
*/
extern void CG_LinkCentsToGents(void);
static void CG_RestartLevel( void ) {
int snapshotNum;
int r;
snapshotNum = cg.processedSnapshotNum;
memset( cg_entities, 0, sizeof( cg_entities ) );
CG_Init_CG();
CG_LinkCentsToGents();
CG_InitLocalEntities();
CG_InitMarkPolys();
// regrab the first snapshot of the restart
cg.processedSnapshotNum = snapshotNum;
r = cgi_GetSnapshot( cg.processedSnapshotNum, &cg.activeSnapshots[0] );
if ( !r ) {
CG_Error( "cgi_GetSnapshot failed on restart" );
}
CG_SetInitialSnapshot( &cg.activeSnapshots[0] );
cg.time = cg.snap->serverTime;
}
/*
============
CG_ProcessSnapshots
We are trying to set up a renderable view, so determine
what the simulated time is, and try to get snapshots
both before and after that time if available.
If we don't have a valid cg.snap after exiting this function,
then a 3D game view cannot be rendered. This should only happen
right after the initial connection. After cg.snap has been valid
once, it will never turn invalid.
Even if cg.snap is valid, cg.nextSnap may not be, if the snapshot
hasn't arrived yet (it becomes an extrapolating situation instead
of an interpolating one)
============
*/
void CG_ProcessSnapshots( void ) {
snapshot_t *snap;
int n;
qboolean newSnapshots;
// see what the latest snapshot the client system has is
cgi_GetCurrentSnapshotNumber( &n, &cg.latestSnapshotTime );
if ( n != cg.latestSnapshotNum ) {
if ( n < cg.latestSnapshotNum ) {
// this should never happen
CG_Error( "CG_ProcessSnapshots: n < cg.latestSnapshotNum" );
}
cg.latestSnapshotNum = n;
newSnapshots = qtrue;
} else {
newSnapshots = qfalse;
}
// If we have yet to receive a snapshot, check for it.
// Once we have gotten the first snapshot, cg.snap will
// always have valid data for the rest of the game
if ( !cg.snap ) {
snap = CG_ReadNextSnapshot();
if ( !snap ) {
// we can't continue until we get a snapshot
return;
}
// set our weapon selection to what
// the playerstate is currently using
CG_SetInitialSnapshot( snap );
}
// loop until we either have a valid nextSnap with a serverTime
// greater than cg.time to interpolate towards, or we run
// out of available snapshots
do {
// if we don't have a nextframe, try to read a new one in
if ( !cg.nextSnap ) {
snap = CG_ReadNextSnapshot();
// if we still don't have a nextframe, we will just have to
// extrapolate
if ( !snap ) {
break;
}
CG_SetNextSnap( snap );
// if time went backwards, we have a level restart
if ( cg.nextSnap->serverTime < cg.snap->serverTime ) {
// restart the level
CG_RestartLevel();
continue; // we might also get a nextsnap
}
}
// if our time is < nextFrame's, we have a nice interpolating state
if ( cg.time < cg.nextSnap->serverTime ) {
break;
}
// we have passed the transition from nextFrame to frame
CG_TransitionSnapshot();
} while ( 1 );
if ( cg.snap->serverTime > cg.time )
{
cg.time=cg.snap->serverTime;
#if _DEBUG
Com_Printf("CG_ProcessSnapshots: cg.snap->serverTime > cg.time");
#endif
}
if ( cg.nextSnap != NULL && cg.nextSnap->serverTime <= cg.time )
{
cg.time=cg.nextSnap->serverTime-1;
#if _DEBUG
Com_Printf("CG_ProcessSnapshots: cg.nextSnap->serverTime <= cg.time");
#endif
}
// assert our valid conditions upon exiting
if ( cg.snap == NULL ) {
CG_Error( "CG_ProcessSnapshots: cg.snap == NULL" );
}
if ( cg.snap->serverTime > cg.time ) {
CG_Error( "CG_ProcessSnapshots: cg.snap->serverTime > cg.time" );
}
if ( cg.nextSnap != NULL && cg.nextSnap->serverTime <= cg.time ) {
CG_Error( "CG_ProcessSnapshots: cg.nextSnap->serverTime <= cg.time" );
}
}

646
code/cgame/cg_syscalls.cpp Normal file
View File

@@ -0,0 +1,646 @@
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
//#include "cg_local.h"
// this file is only included when building a dll
#ifdef _IMMERSION
#include "../ff/ff.h"
#else
///////////////////// this is a bit kludgy, but it only gives access to one
// enum table because of the #define. May get changed.
#define CGAME_ONLY
#include "../client/fffx.h"
//
/////////////////////
#endif // _IMMERSION
//prototypes
extern void CG_PreInit();
static int (*syscall)( int arg, ... ) = (int (*)( int, ...))-1;
#ifdef _XBOX
void cg_dllEntry( int (*syscallptr)( int arg,... ) ) {
#else
void dllEntry( int (*syscallptr)( int arg,... ) ) {
#endif
syscall = syscallptr;
CG_PreInit();
}
inline int PASSFLOAT( float x ) {
float floatTemp;
floatTemp = x;
return *(int *)&floatTemp;
}
void cgi_Printf( const char *fmt ) {
syscall( CG_PRINT, fmt );
}
void cgi_Error( const char *fmt ) {
syscall( CG_ERROR, fmt );
}
int cgi_Milliseconds( void ) {
return syscall( CG_MILLISECONDS );
}
void cgi_Cvar_Register( vmCvar_t *vmCvar, const char *varName, const char *defaultValue, int flags ) {
syscall( CG_CVAR_REGISTER, vmCvar, varName, defaultValue, flags );
}
void cgi_Cvar_Update( vmCvar_t *vmCvar ) {
syscall( CG_CVAR_UPDATE, vmCvar );
}
void cgi_Cvar_Set( const char *var_name, const char *value ) {
syscall( CG_CVAR_SET, var_name, value );
}
int cgi_Argc( void ) {
return syscall( CG_ARGC );
}
void cgi_Argv( int n, char *buffer, int bufferLength ) {
syscall( CG_ARGV, n, buffer, bufferLength );
}
void cgi_Args( char *buffer, int bufferLength ) {
syscall( CG_ARGS, buffer, bufferLength );
}
int cgi_FS_FOpenFile( const char *qpath, fileHandle_t *f, fsMode_t mode ) {
return syscall( CG_FS_FOPENFILE, qpath, f, mode );
}
int cgi_FS_Read( void *buffer, int len, fileHandle_t f ) {
return syscall( CG_FS_READ, buffer, len, f );
}
int cgi_FS_Write( const void *buffer, int len, fileHandle_t f ) {
return syscall( CG_FS_WRITE, buffer, len, f );
}
void cgi_FS_FCloseFile( fileHandle_t f ) {
syscall( CG_FS_FCLOSEFILE, f );
}
void cgi_SendConsoleCommand( const char *text ) {
syscall( CG_SENDCONSOLECOMMAND, text );
}
void cgi_AddCommand( const char *cmdName ) {
syscall( CG_ADDCOMMAND, cmdName );
}
void cgi_SendClientCommand( const char *s ) {
syscall( CG_SENDCLIENTCOMMAND, s );
}
void cgi_UpdateScreen( void ) {
syscall( CG_UPDATESCREEN );
}
//RMG BEGIN
void cgi_RMG_Init(int terrainID, const char *terrainInfo)
{
syscall( CG_RMG_INIT, terrainID, terrainInfo);
}
int cgi_CM_RegisterTerrain(const char *terrainInfo)
{
return syscall( CG_CM_REGISTER_TERRAIN, terrainInfo);
}
void cgi_RE_InitRendererTerrain( const char *terrainInfo )
{
syscall(CG_RE_INIT_RENDERER_TERRAIN, terrainInfo);
}
//RMG END
void cgi_CM_LoadMap( const char *mapname, qboolean subBSP ) {
syscall( CG_CM_LOADMAP, mapname, subBSP );
}
int cgi_CM_NumInlineModels( void ) {
return syscall( CG_CM_NUMINLINEMODELS );
}
clipHandle_t cgi_CM_InlineModel( int index ) {
return syscall( CG_CM_INLINEMODEL, index );
}
clipHandle_t cgi_CM_TempBoxModel( const vec3_t mins, const vec3_t maxs ) {//, const int contents ) {
return syscall( CG_CM_TEMPBOXMODEL, mins, maxs );//, contents );
}
int cgi_CM_PointContents( const vec3_t p, clipHandle_t model ) {
return syscall( CG_CM_POINTCONTENTS, p, model );
}
int cgi_CM_TransformedPointContents( const vec3_t p, clipHandle_t model, const vec3_t origin, const vec3_t angles ) {
return syscall( CG_CM_TRANSFORMEDPOINTCONTENTS, p, model, origin, angles );
}
void cgi_CM_BoxTrace( trace_t *results, const vec3_t start, const vec3_t end,
const vec3_t mins, const vec3_t maxs,
clipHandle_t model, int brushmask ) {
syscall( CG_CM_BOXTRACE, results, start, end, mins, maxs, model, brushmask );
}
void cgi_CM_TransformedBoxTrace( trace_t *results, const vec3_t start, const vec3_t end,
const vec3_t mins, const vec3_t maxs,
clipHandle_t model, int brushmask,
const vec3_t origin, const vec3_t angles ) {
syscall( CG_CM_TRANSFORMEDBOXTRACE, results, start, end, mins, maxs, model, brushmask, origin, angles );
}
int cgi_CM_MarkFragments( int numPoints, const vec3_t *points,
const vec3_t projection,
int maxPoints, vec3_t pointBuffer,
int maxFragments, markFragment_t *fragmentBuffer ) {
return syscall( CG_CM_MARKFRAGMENTS, numPoints, points, projection, maxPoints, pointBuffer, maxFragments, fragmentBuffer );
}
void cgi_CM_SnapPVS(vec3_t origin,byte *buffer)
{
syscall(CG_CM_SNAPPVS,origin,buffer);
}
void cgi_S_StopSounds( void )
{
syscall( CG_S_STOPSOUNDS);
}
void cgi_S_StartSound( const vec3_t origin, int entityNum, int entchannel, sfxHandle_t sfx ) {
syscall( CG_S_STARTSOUND, origin, entityNum, entchannel, sfx );
}
void cgi_AS_ParseSets( void ) {
syscall( CG_AS_PARSESETS );
}
void cgi_AS_AddPrecacheEntry( const char *name ) {
syscall( CG_AS_ADDENTRY, name );
}
void cgi_S_UpdateAmbientSet( const char *name, vec3_t origin ) {
syscall( CG_S_UPDATEAMBIENTSET, name, origin );
}
int cgi_S_AddLocalSet( const char *name, vec3_t listener_origin, vec3_t origin, int entID, int time ) {
return syscall( CG_S_ADDLOCALSET, name, listener_origin, origin, entID, time );
}
sfxHandle_t cgi_AS_GetBModelSound( const char *name, int stage ) {
return syscall( CG_AS_GETBMODELSOUND, name, stage );
}
void cgi_S_StartLocalSound( sfxHandle_t sfx, int channelNum ) {
syscall( CG_S_STARTLOCALSOUND, sfx, channelNum );
}
void cgi_S_ClearLoopingSounds( void ) {
syscall( CG_S_CLEARLOOPINGSOUNDS );
}
void cgi_S_AddLoopingSound( int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx, soundChannel_t chan ) {
syscall( CG_S_ADDLOOPINGSOUND, entityNum, origin, velocity, sfx, chan );
}
void cgi_S_UpdateEntityPosition( int entityNum, const vec3_t origin ) {
syscall( CG_S_UPDATEENTITYPOSITION, entityNum, origin );
}
void cgi_S_Respatialize( int entityNum, const vec3_t origin, vec3_t axis[3], qboolean inwater ) {
syscall( CG_S_RESPATIALIZE, entityNum, origin, axis, inwater );
}
sfxHandle_t cgi_S_RegisterSound( const char *sample ) {
return syscall( CG_S_REGISTERSOUND, sample );
}
void cgi_S_StartBackgroundTrack( const char *intro, const char *loop, qboolean bForceStart ) {
syscall( CG_S_STARTBACKGROUNDTRACK, intro, loop, bForceStart );
}
float cgi_S_GetSampleLength( sfxHandle_t sfx ) {
return syscall( CG_S_GETSAMPLELENGTH, sfx);
}
#ifdef _IMMERSION
void cgi_FF_Start( ffHandle_t ff, int clientNum ){
syscall( CG_FF_START, ff, clientNum );
}
void cgi_FF_Stop( ffHandle_t ff, int clientNum ){
syscall( CG_FF_STOP, ff, clientNum );
}
void cgi_FF_StopAll( void ){
syscall( CG_FF_STOPALL );
}
void cgi_FF_Shake( int intensity, int duration ){
syscall( CG_FF_SHAKE, intensity, duration );
}
ffHandle_t cgi_FF_Register( const char *name, int channel ){
return syscall( CG_FF_REGISTER, name, channel );
}
void cgi_FF_AddLoopingForce( ffHandle_t handle, int entNum ){
syscall( CG_FF_ADDLOOPINGFORCE, handle, entNum );
}
#else
void cgi_FF_StartFX( int iFX ){
syscall( CG_FF_STARTFX, iFX );
}
void cgi_FF_EnsureFX( int iFX ){
syscall( CG_FF_ENSUREFX, iFX );
}
void cgi_FF_StopFX( int iFX ){
syscall( CG_FF_STOPFX, iFX );
}
void cgi_FF_StopAllFX( void ){
syscall( CG_FF_STOPALLFX );
}
#endif // _IMMERSION
#ifdef _XBOX
void cgi_FF_Xbox_Shake( float intensity, int duration ){
syscall( CG_FF_XBOX_SHAKE, PASSFLOAT(intensity), duration );
}
void cgi_FF_Xbox_Damage( int damage, float xpos ) {
syscall( CG_FF_XBOX_DAMAGE, damage, PASSFLOAT(xpos) );
}
#endif
void cgi_R_LoadWorldMap( const char *mapname ) {
syscall( CG_R_LOADWORLDMAP, mapname );
}
qhandle_t cgi_R_RegisterModel( const char *name ) {
return syscall( CG_R_REGISTERMODEL, name );
}
qhandle_t cgi_R_RegisterSkin( const char *name ) {
return syscall( CG_R_REGISTERSKIN, name );
}
qhandle_t cgi_R_RegisterShader( const char *name ) {
qhandle_t hShader = syscall( CG_R_REGISTERSHADER, name );
assert (hShader);
return hShader;
}
qhandle_t cgi_R_RegisterShaderNoMip( const char *name ) {
return syscall( CG_R_REGISTERSHADERNOMIP, name );
}
qhandle_t cgi_R_RegisterFont( const char *name ) {
return syscall( CG_R_REGISTERFONT, name );
}
int cgi_R_Font_StrLenPixels(const char *text, const int iFontIndex, const float scale /*= 1.0f*/) {
return syscall( CG_R_FONTSTRLENPIXELS, text, iFontIndex, PASSFLOAT(scale) ) ;
}
int cgi_R_Font_StrLenChars(const char *text) {
return syscall( CG_R_FONTSTRLENCHARS, text ) ;
}
int cgi_R_Font_HeightPixels(const int iFontIndex, const float scale /*= 1.0f*/) {
return syscall( CG_R_FONTHEIGHTPIXELS, iFontIndex, PASSFLOAT(scale) );
}
qboolean cgi_Language_IsAsian( void )
{
return syscall( CG_LANGUAGE_ISASIAN );
}
qboolean cgi_Language_UsesSpaces(void)
{
return syscall( CG_LANGUAGE_USESSPACES );
}
unsigned int cgi_AnyLanguage_ReadCharFromString( const char *psText, int *piAdvanceCount, qboolean *pbIsTrailingPunctuation /* = NULL */ )
{
return syscall( CG_ANYLANGUAGE_READFROMSTRING, psText, piAdvanceCount, pbIsTrailingPunctuation );
}
void cgi_R_Font_DrawString(int ox, int oy, const char *text, const float *rgba, const int setIndex, int iMaxPixelWidth, const float scale /*= 1.0f*/) {
syscall (CG_R_FONTDRAWSTRING, ox, oy, text, rgba, setIndex, iMaxPixelWidth, PASSFLOAT(scale) );
}
//set some properties for the draw layer for my refractive effect (here primarily for mod authors) -rww
void cgi_R_SetRefractProp(float alpha, float stretch, qboolean prepost, qboolean negate)
{
syscall(CG_R_SETREFRACTIONPROP, PASSFLOAT(alpha), PASSFLOAT(stretch), prepost, negate);
}
void cgi_R_ClearScene( void ) {
syscall( CG_R_CLEARSCENE );
}
void cgi_R_AddRefEntityToScene( const refEntity_t *re ) {
syscall( CG_R_ADDREFENTITYTOSCENE, re );
}
qboolean cgi_R_inPVS( vec3_t p1, vec3_t p2 )
{
return syscall( CG_R_INPVS, p1, p2 );
}
void cgi_R_GetLighting( const vec3_t origin, vec3_t ambientLight, vec3_t directedLight, vec3_t ligthDir ) {
syscall( CG_R_GETLIGHTING, origin, ambientLight, directedLight, ligthDir );
}
void cgi_R_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts ) {
syscall( CG_R_ADDPOLYTOSCENE, hShader, numVerts, verts );
}
void cgi_R_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ) {
syscall( CG_R_ADDLIGHTTOSCENE, org, PASSFLOAT(intensity), PASSFLOAT(r), PASSFLOAT(g), PASSFLOAT(b) );
}
void cgi_R_RenderScene( const refdef_t *fd ) {
syscall( CG_R_RENDERSCENE, fd );
}
void cgi_R_SetColor( const float *rgba ) {
syscall( CG_R_SETCOLOR, rgba );
}
void cgi_R_DrawStretchPic( float x, float y, float w, float h,
float s1, float t1, float s2, float t2, qhandle_t hShader ) {
syscall( CG_R_DRAWSTRETCHPIC, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h), PASSFLOAT(s1), PASSFLOAT(t1), PASSFLOAT(s2), PASSFLOAT(t2), hShader );
}
//void cgi_R_DrawScreenShot( float x, float y, float w, float h){
// syscall( CG_R_DRAWSCREENSHOT, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h) );
//}
void cgi_R_ModelBounds( qhandle_t model, vec3_t mins, vec3_t maxs ) {
syscall( CG_R_MODELBOUNDS, model, mins, maxs );
}
void cgi_R_LerpTag( orientation_t *tag, qhandle_t mod, int startFrame, int endFrame,
float frac, const char *tagName ) {
syscall( CG_R_LERPTAG, tag, mod, startFrame, endFrame, PASSFLOAT(frac), tagName );
}
void cgi_R_DrawRotatePic( float x, float y, float w, float h,
float s1, float t1, float s2, float t2,float a, qhandle_t hShader )
{
syscall( CG_R_DRAWROTATEPIC, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h), PASSFLOAT(s1), PASSFLOAT(t1), PASSFLOAT(s2), PASSFLOAT(t2), PASSFLOAT(a), hShader );
}
void cgi_R_DrawRotatePic2( float x, float y, float w, float h,
float s1, float t1, float s2, float t2,float a, qhandle_t hShader )
{
syscall( CG_R_DRAWROTATEPIC2, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h), PASSFLOAT(s1), PASSFLOAT(t1), PASSFLOAT(s2), PASSFLOAT(t2), PASSFLOAT(a), hShader );
}
//linear fogging, with settable range -rww
void cgi_R_SetRangeFog(float range)
{
syscall(CG_R_SETRANGEFOG, PASSFLOAT(range));
}
void cgi_R_LAGoggles( void )
{
syscall( CG_R_LA_GOGGLES );
}
void cgi_R_Scissor( float x, float y, float w, float h)
{
syscall( CG_R_SCISSOR, PASSFLOAT(x), PASSFLOAT(y), PASSFLOAT(w), PASSFLOAT(h));
}
void cgi_GetGlconfig( glconfig_t *glconfig ) {
syscall( CG_GETGLCONFIG, glconfig );
}
void cgi_GetGameState( gameState_t *gamestate ) {
syscall( CG_GETGAMESTATE, gamestate );
}
void cgi_GetCurrentSnapshotNumber( int *snapshotNumber, int *serverTime ) {
syscall( CG_GETCURRENTSNAPSHOTNUMBER, snapshotNumber, serverTime );
}
qboolean cgi_GetSnapshot( int snapshotNumber, snapshot_t *snapshot ) {
return syscall( CG_GETSNAPSHOT, snapshotNumber, snapshot );
}
qboolean cgi_GetDefaultState(int entityIndex, entityState_t *state )
{
return syscall( CG_GETDEFAULTSTATE, entityIndex, state );
}
qboolean cgi_GetServerCommand( int serverCommandNumber ) {
return syscall( CG_GETSERVERCOMMAND, serverCommandNumber );
}
int cgi_GetCurrentCmdNumber( void ) {
return syscall( CG_GETCURRENTCMDNUMBER );
}
qboolean cgi_GetUserCmd( int cmdNumber, usercmd_t *ucmd ) {
return syscall( CG_GETUSERCMD, cmdNumber, ucmd );
}
void cgi_SetUserCmdValue( int stateValue, float sensitivityScale, float mPitchOverride, float mYawOverride ) {
syscall( CG_SETUSERCMDVALUE, stateValue, PASSFLOAT(sensitivityScale), PASSFLOAT(mPitchOverride), PASSFLOAT(mYawOverride) );
}
void cgi_SetUserCmdAngles( float pitchOverride, float yawOverride, float rollOverride ) {
syscall( CG_SETUSERCMDANGLES, PASSFLOAT(pitchOverride), PASSFLOAT(yawOverride), PASSFLOAT(rollOverride) );
}
/*
Ghoul2 Insert Start
*/
// CG Specific API calls
void trap_G2_SetGhoul2ModelIndexes(CGhoul2Info_v &ghoul2, qhandle_t *modelList, qhandle_t *skinList)
{
syscall( CG_G2_SETMODELS, &ghoul2, modelList, skinList);
}
/*
Ghoul2 Insert End
*/
void trap_Com_SetOrgAngles(vec3_t org,vec3_t angles)
{
syscall(COM_SETORGANGLES,org,angles);
}
void trap_R_GetLightStyle(int style, color4ub_t color)
{
syscall(CG_R_GET_LIGHT_STYLE, style, color);
}
void trap_R_SetLightStyle(int style, int color)
{
syscall(CG_R_SET_LIGHT_STYLE, style, color);
}
void cgi_R_GetBModelVerts(int bmodelIndex, vec3_t *verts, vec3_t normal )
{
syscall( CG_R_GET_BMODEL_VERTS, bmodelIndex, verts, normal );
}
void cgi_R_WorldEffectCommand( const char *command )
{
syscall( CG_R_WORLD_EFFECT_COMMAND, command );
}
// this returns a handle. arg0 is the name in the format "idlogo.roq", set arg1 to NULL, alteredstates to qfalse (do not alter gamestate)
int trap_CIN_PlayCinematic( const char *arg0, int xpos, int ypos, int width, int height, int bits, const char *psAudioFile /* = NULL */) {
return syscall(CG_CIN_PLAYCINEMATIC, arg0, xpos, ypos, width, height, bits, psAudioFile);
}
// stops playing the cinematic and ends it. should always return FMV_EOF
// cinematics must be stopped in reverse order of when they are started
e_status trap_CIN_StopCinematic(int handle) {
return (e_status) syscall(CG_CIN_STOPCINEMATIC, handle);
}
// will run a frame of the cinematic but will not draw it. Will return FMV_EOF if the end of the cinematic has been reached.
e_status trap_CIN_RunCinematic (int handle) {
return (e_status) syscall(CG_CIN_RUNCINEMATIC, handle);
}
// draws the current frame
void trap_CIN_DrawCinematic (int handle) {
syscall(CG_CIN_DRAWCINEMATIC, handle);
}
// allows you to resize the animation dynamically
void trap_CIN_SetExtents (int handle, int x, int y, int w, int h) {
syscall(CG_CIN_SETEXTENTS, handle, x, y, w, h);
}
void *cgi_Z_Malloc( int size, int tag )
{
return (void *)syscall(CG_Z_MALLOC,size,tag);
}
void cgi_Z_Free( void *ptr )
{
syscall(CG_Z_FREE,ptr);
}
void cgi_UI_SetActive_Menu(char *name)
{
syscall(CG_UI_SETACTIVE_MENU,name);
}
void cgi_UI_Menu_OpenByName(char *buf)
{
syscall(CG_UI_MENU_OPENBYNAME,buf);
}
void cgi_UI_Menu_Reset(void)
{
syscall(CG_UI_MENU_RESET);
}
void cgi_UI_Menu_New(char *buf)
{
syscall(CG_UI_MENU_NEW,buf);
}
void cgi_UI_Parse_Int(int *value)
{
syscall(CG_UI_PARSE_INT,value);
}
void cgi_UI_Parse_String(char *buf)
{
syscall(CG_UI_PARSE_STRING,buf);
}
void cgi_UI_Parse_Float(float *value)
{
syscall(CG_UI_PARSE_FLOAT,value);
}
int cgi_UI_StartParseSession(char *menuFile,char **buf)
{
return(int) syscall(CG_UI_STARTPARSESESSION,menuFile,buf);
}
void cgi_UI_EndParseSession(char *buf)
{
syscall(CG_UI_ENDPARSESESSION,buf);
}
void cgi_UI_ParseExt(char **token)
{
syscall(CG_UI_PARSEEXT,token);
}
void cgi_UI_MenuCloseAll(void)
{
syscall(CG_UI_MENUCLOSE_ALL);
}
void cgi_UI_MenuPaintAll(void)
{
syscall(CG_UI_MENUPAINT_ALL);
}
void cgi_UI_String_Init(void)
{
syscall(CG_UI_STRING_INIT);
}
int cgi_UI_GetMenuInfo(char *menuFile,int *x,int *y,int *w,int *h)
{
return(int) syscall(CG_UI_GETMENUINFO,menuFile,x,y,w,h);
}
int cgi_UI_GetMenuItemInfo(const char *menuFile,const char *itemName, int *x,int *y,int *w,int *h,vec4_t color,qhandle_t *background)
{
return(int) syscall(CG_UI_GETITEMINFO,menuFile,itemName,x,y,w,h,color,background);
}
int cgi_UI_GetItemText(char *menuFile,char *itemName, char* text)
{
return(int) syscall(CG_UI_GETITEMTEXT,menuFile,itemName,text);
}
int cgi_SP_GetStringTextString(const char *text, char *buffer, int bufferLength)
{
return syscall( CG_SP_GETSTRINGTEXTSTRING, text, buffer, bufferLength );
}
int cgi_EndGame(void)
{
return syscall( CG_SENDCONSOLECOMMAND, "cam_disable; disconnect\n" );//; cinematic outcast
}

786
code/cgame/cg_text.cpp Normal file
View File

@@ -0,0 +1,786 @@
// cg_text.c --
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
#include "cg_media.h"
//int precacheWav_i; // Current high index of precacheWav array
//precacheWav_t precacheWav[MAX_PRECACHEWAV];
//int precacheText_i; // Current high index of precacheText array
//precacheText_t precacheText[MAX_PRECACHETEXT];
#define TEXT_SCREEN_WIDTH_FRACTION_CUTSCENE 0.85
#define TEXT_SCREEN_WIDTH_FRACTION_INGAME 0.85
#define TEXT_SCREEN_HEIGHT_INGAME 0.15f
#define TEXT_CUTSCENE_Y_BOOST 18
#define NUDGE_PERCENTAGE 0.90
extern vec4_t textcolor_caption;
extern vec4_t textcolor_center;
extern vec4_t textcolor_scroll;
// display text in a supplied box, start at top left and going down by however many pixels I feel like internally,
// return value is NULL if all fitted, else char * of next char to continue from that didn't fit.
//
// (coords are in the usual 640x480 virtual space)...
//
// ( if you get the same char * returned as what you passed in, then none of it fitted at all (box too small) )
//
// this is execrable, and should NOT have had to've been done now, but...
//
float gfAdvanceHack = 0.0f; // MUST default to this
int giLinesOutput; // hack-city after release, only used by one function
//
const char *CG_DisplayBoxedText(int iBoxX, int iBoxY, int iBoxWidth, int iBoxHeight,
const char *psText, int iFontHandle, float fScale,
const vec4_t v4Color)
{
giLinesOutput = 0;
cgi_R_SetColor( v4Color );
// Setup a reasonable vertical spacing (taiwanese & japanese need 1.5 fontheight, so use that for all)...
//
const int iFontHeight = cgi_R_Font_HeightPixels(iFontHandle, fScale);
const int iFontHeightAdvance = (int) ( ((gfAdvanceHack == 0.0f) ? 1.5f : gfAdvanceHack) * (float) iFontHeight);
int iYpos = iBoxY; // start print pos
// this could probably be simplified now, but it was converted from something else I didn't originally write,
// and it works anyway so wtf...
//
const char *psCurrentTextReadPos = psText;
const char *psReadPosAtLineStart = psCurrentTextReadPos;
const char *psBestLineBreakSrcPos = psCurrentTextReadPos;
const char *psLastGood_s; // needed if we get a full screen of chars with no punctuation or space (see usage notes)
while( *psCurrentTextReadPos && (iYpos + iFontHeight < (iBoxY + iBoxHeight)) )
{
char sLineForDisplay[2048]; // ott
// construct a line...
//
psCurrentTextReadPos = psReadPosAtLineStart;
sLineForDisplay[0] = '\0';
while ( *psCurrentTextReadPos )
{
psLastGood_s = psCurrentTextReadPos;
// read letter...
//
qboolean bIsTrailingPunctuation;
int iAdvanceCount;
unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(psCurrentTextReadPos, &iAdvanceCount, &bIsTrailingPunctuation);
psCurrentTextReadPos += iAdvanceCount;
// concat onto string so far...
//
if (uiLetter == 32 && sLineForDisplay[0] == '\0')
{
psReadPosAtLineStart++;
continue; // unless it's a space at the start of a line, in which case ignore it.
}
if (uiLetter > 255)
{
Q_strcat(sLineForDisplay, sizeof(sLineForDisplay),va("%c%c",uiLetter >> 8, uiLetter & 0xFF));
}
else
{
Q_strcat(sLineForDisplay, sizeof(sLineForDisplay),va("%c",uiLetter & 0xFF));
}
if (uiLetter == '\n')
{
// explicit new line...
//
sLineForDisplay[ strlen(sLineForDisplay)-1 ] = '\0'; // kill the CR
psReadPosAtLineStart = psCurrentTextReadPos;
psBestLineBreakSrcPos = psCurrentTextReadPos;
break; // print this line
}
else
if ( cgi_R_Font_StrLenPixels(sLineForDisplay, iFontHandle, fScale) >= iBoxWidth )
{
// reached screen edge, so cap off string at bytepos after last good position...
//
if (uiLetter > 255 && bIsTrailingPunctuation && !cgi_Language_UsesSpaces())
{
// Special case, don't consider line breaking if you're on an asian punctuation char of
// a language that doesn't use spaces...
//
}
else
{
if (psBestLineBreakSrcPos == psReadPosAtLineStart)
{
// aarrrggh!!!!! we'll only get here is someone has fed in a (probably) garbage string,
// since it doesn't have a single space or punctuation mark right the way across one line
// of the screen. So far, this has only happened in testing when I hardwired a taiwanese
// string into this function while the game was running in english (which should NEVER happen
// normally). On the other hand I suppose it'psCurrentTextReadPos entirely possible that some taiwanese string
// might have no punctuation at all, so...
//
psBestLineBreakSrcPos = psLastGood_s; // force a break after last good letter
}
sLineForDisplay[ psBestLineBreakSrcPos - psReadPosAtLineStart ] = '\0';
psReadPosAtLineStart = psCurrentTextReadPos = psBestLineBreakSrcPos;
break; // print this line
}
}
// record last-good linebreak pos... (ie if we've just concat'd a punctuation point (western or asian) or space)
//
if (bIsTrailingPunctuation || uiLetter == ' ' || (uiLetter > 255 && !cgi_Language_UsesSpaces()))
{
psBestLineBreakSrcPos = psCurrentTextReadPos;
}
}
// ... and print it...
//
cgi_R_Font_DrawString(iBoxX, iYpos, sLineForDisplay, v4Color, iFontHandle, -1, fScale);
iYpos += iFontHeightAdvance;
giLinesOutput++;
// and echo to console in dev mode...
//
if ( cg_developer.integer )
{
// Com_Printf( "%psCurrentTextReadPos\n", sLineForDisplay );
}
}
return psReadPosAtLineStart;
}
/*
===============================================================================
CAPTION TEXT
===============================================================================
*/
void CG_CaptionTextStop(void)
{
cg.captionTextTime = 0;
}
// try and get the correct StripEd text (with retry) for a given reference...
//
// returns 0 if failed, else strlen...
//
static int cg_SP_GetStringTextStringWithRetry( LPCSTR psReference, char *psDest, int iSizeofDest)
{
int iReturn;
if (psReference[0] == '#')
{
// then we know the striped package name is already built in, so do NOT try prepending anything else...
//
return cgi_SP_GetStringTextString( va("%s",psReference+1), psDest, iSizeofDest );
}
for (int i=0; i<STRIPED_LEVELNAME_VARIATIONS; i++)
{
if (cgs.stripLevelName[i][0]) // entry present?
{
iReturn = cgi_SP_GetStringTextString( va("%s_%s",cgs.stripLevelName[i],psReference), psDest, iSizeofDest );
if (iReturn)
{
return iReturn;
}
}
}
return 0;
}
// slightly confusingly, the char arg for this function is an audio filename of the form "path/path/filename",
// the "filename" part of which should be the same as the StripEd reference we're looking for in the current
// level's string package...
//
void CG_CaptionText( const char *str, int sound, int y )
{
const char *s, *holds;
int i;
int holdTime;
char text[8192]={0};
const float fFontScale = cgi_Language_IsAsian() ? 0.8f : 1.0f;
holds = strrchr(str,'/');
if (!holds)
{
#ifndef FINAL_BUILD
Com_Printf("WARNING: CG_CaptionText given audio filename with no '/':'%s'\n",str);
#endif
return;
}
i = cg_SP_GetStringTextStringWithRetry( holds+1, text, sizeof(text) );
//ensure we found a match
if (!i)
{
#ifndef FINAL_BUILD
// we only care about some sound dirs...
if (!strnicmp(str,"sound/chars/",12)) // whichever language it is, it'll be pathed as english at this point
{
Com_Printf("WARNING: CG_CaptionText given invalid text key :'%s'\n",str);
}
else
{
// anything else is probably stuff we don't care about. It certainly shouldn't be speech, anyway
}
#endif
return;
}
const int fontHeight = (int) ((cgi_Language_IsAsian() ? 1.4f : 1.0f) * (float) cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, fFontScale)); // taiwanese & japanese need 1.5 fontheight spacing
int lineWidth;
cg.captionTextTime = cg.time;
if (in_camera) {
cg.captionTextY = SCREEN_HEIGHT - (client_camera.bar_height_dest/2)- TEXT_CUTSCENE_Y_BOOST; // ths is now a centre'd Y, not a start Y
#ifdef _XBOX
if(cg.widescreen)
lineWidth = 720 * TEXT_SCREEN_WIDTH_FRACTION_CUTSCENE ;
else
#endif
lineWidth = SCREEN_WIDTH * TEXT_SCREEN_WIDTH_FRACTION_CUTSCENE ;
} else { //get above the hud
cg.captionTextY = (int) (TEXT_SCREEN_HEIGHT_INGAME * ((float)SCREEN_HEIGHT - (float)fontHeight * 1.5f)); // do NOT move this, it has to fit in between the weapon HUD and the datapad update.
#ifdef _XBOX
if(cg.widescreen)
lineWidth = 720 * TEXT_SCREEN_WIDTH_FRACTION_INGAME ;
else
#endif
lineWidth = SCREEN_WIDTH * TEXT_SCREEN_WIDTH_FRACTION_INGAME ;
}
cg.captionTextCurrentLine = 0;
// count the number of lines for centering
cg.scrollTextLines = 1;
memset (cg.captionText, 0, sizeof(cg.captionText));
// Break into individual lines
i = 0; // this could be completely removed and replace by "cg.scrollTextLines-1", but wtf?
s=(const char*)&text;
// tai...
// s="賽卓哥爾博士已經安全了,我也把所有發現報告給「商店」。很不幸地,瑞士警局有些白癡發現了一些狀況,準備在機場逮捕亞歷西‧納克瑞得。他偽裝成外交使節,穿過了層層防備。現在他握有人質,並且威脅要散播病毒。根據最新的報告,納克瑞得以及他的黨羽已經完全佔據了機場。我受命來追捕納克瑞得以及救出所有人質。這並不容易。";
// kor...
// s="Wp:澗顫歜檜棻 詩萼. 斜菟檜 蜓フ渠煎 啻陛 澀ブ雖 晦渠ビ啊棻.澗顫歜檜棻 詩萼. 斜菟檜 蜓フ渠煎 啻陛 澀ブ雖 晦渠ビ啊棻.";
holds = s;
int iPlayingTimeMS = cgi_S_GetSampleLength(sound);
int iLengthInChars = strlen(s);//cgi_R_Font_StrLenChars(s); // strlen is also good for MBCS in this instance, since it's for timing
if (iLengthInChars == 0)
{
iLengthInChars = 1;
}
cg.captionLetterTime = iPlayingTimeMS / iLengthInChars;
const char *psBestLineBreakSrcPos = s;
const char *psLastGood_s; // needed if we get a full screen of chars with no punctuation or space (see usage notes)
while( *s )
{
psLastGood_s = s;
// read letter...
//
qboolean bIsTrailingPunctuation;
int iAdvanceCount;
unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(s, &iAdvanceCount, &bIsTrailingPunctuation);
s += iAdvanceCount;
// concat onto string so far...
//
if (uiLetter == 32 && cg.captionText[i][0] == '\0')
{
holds++;
continue; // unless it's a space at the start of a line, in which case ignore it.
}
if (uiLetter > 255)
{
Q_strcat(cg.captionText[i],sizeof(cg.captionText[i]),va("%c%c",uiLetter >> 8, uiLetter & 0xFF));
}
else
{
Q_strcat(cg.captionText[i],sizeof(cg.captionText[i]),va("%c",uiLetter & 0xFF));
}
if (uiLetter == '\n')
{
// explicit new line...
//
cg.captionText[i][ strlen(cg.captionText[i])-1 ] = '\0'; // kill the CR
i++;
holds = s;
psBestLineBreakSrcPos = s;
cg.scrollTextLines++;
}
else
if ( cgi_R_Font_StrLenPixels(cg.captionText[i], cgs.media.qhFontMedium, fFontScale) >= lineWidth)
{
// reached screen edge, so cap off string at bytepos after last good position...
//
if (uiLetter > 255 && bIsTrailingPunctuation && !cgi_Language_UsesSpaces())
{
// Special case, don't consider line breaking if you're on an asian punctuation char of
// a language that doesn't use spaces...
//
}
else
{
if (psBestLineBreakSrcPos == holds)
{
// aarrrggh!!!!! we'll only get here is someone has fed in a (probably) garbage string,
// since it doesn't have a single space or punctuation mark right the way across one line
// of the screen. So far, this has only happened in testing when I hardwired a taiwanese
// string into this function while the game was running in english (which should NEVER happen
// normally). On the other hand I suppose it's entirely possible that some taiwanese string
// might have no punctuation at all, so...
//
psBestLineBreakSrcPos = psLastGood_s; // force a break after last good letter
}
cg.captionText[i][ psBestLineBreakSrcPos - holds ] = '\0';
holds = s = psBestLineBreakSrcPos;
i++;
cg.scrollTextLines++;
}
}
// record last-good linebreak pos... (ie if we've just concat'd a punctuation point (western or asian) or space)
//
if (bIsTrailingPunctuation || uiLetter == ' ' || (uiLetter > 255 && !cgi_Language_UsesSpaces()))
{
psBestLineBreakSrcPos = s;
}
}
// calc the length of time to hold each 2 lines of text on the screen.... presumably this works?
//
holdTime = strlen(cg.captionText[0]);
if (cg.scrollTextLines > 1)
{
holdTime += strlen(cg.captionText[1]); // strlen is also good for MBCS in this instance, since it's for timing
}
cg.captionNextTextTime = cg.time + (/*JLF nudge it forward*/NUDGE_PERCENTAGE * holdTime * cg.captionLetterTime);
cg.scrollTextTime = 0; // No scrolling during captions
//Echo to console in dev mode
if ( cg_developer.integer )
{
Com_Printf( "%s\n", cg.captionText[0] ); // ste: was [i], but surely sentence 0 is more useful than last?
}
}
void CG_DrawCaptionText(void)
{
int i;
int x, y, w;
int holdTime;
if ( !cg.captionTextTime )
{
return;
}
const float fFontScale = cgi_Language_IsAsian() ? 0.8f : 1.0f;
if (cg_skippingcin.integer != 0)
{
cg.captionTextTime = 0;
return;
}
if ( cg.captionNextTextTime < cg.time )
{
cg.captionTextCurrentLine += 2;
if (cg.captionTextCurrentLine >= cg.scrollTextLines)
{
cg.captionTextTime = 0;
return;
}
else
{
holdTime = strlen(cg.captionText[cg.captionTextCurrentLine]);
if (cg.scrollTextLines >= cg.captionTextCurrentLine)
{
// ( strlen is also good for MBCS in this instance, since it's for timing -ste)
//
holdTime += strlen(cg.captionText[cg.captionTextCurrentLine + 1]);
}
cg.captionNextTextTime = cg.time + (holdTime * cg.captionLetterTime);//50);
}
}
// Give a color if one wasn't given
if((textcolor_caption[0] == 0) && (textcolor_caption[1] == 0) &&
(textcolor_caption[2] == 0) && (textcolor_caption[3] == 0))
{
Vector4Copy( colorTable[CT_WHITE], textcolor_caption );
}
cgi_R_SetColor(textcolor_caption);
// Set Y of the first line (varies if only printing one line of text)
// (this all works, please don't mess with it)
const int fontHeight = (int) ((cgi_Language_IsAsian() ? 1.4f : 1.0f) * (float) cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, fFontScale));
const bool bPrinting2Lines = !!(cg.captionText[ cg.captionTextCurrentLine+1 ][0]);
y = cg.captionTextY - ( (float)fontHeight * (bPrinting2Lines ? 1 : 0.5f)); // captionTextY was a centered Y pos, not a top one
y -= cgi_Language_IsAsian() ? 0 : 4;
for (i= cg.captionTextCurrentLine;i< cg.captionTextCurrentLine + 2;++i)
{
w = cgi_R_Font_StrLenPixels(cg.captionText[i], cgs.media.qhFontMedium, fFontScale);
if (w)
{
x = (SCREEN_WIDTH-w) / 2;
cgi_R_Font_DrawString(x, y, cg.captionText[i], textcolor_caption, cgs.media.qhFontMedium, -1, fFontScale);
y += fontHeight;
}
}
cgi_R_SetColor( NULL );
}
/*
===============================================================================
SCROLL TEXT
===============================================================================
CG_ScrollText - split text up into seperate lines
'str' arg is StripEd string reference, eg "CREDITS_RAVEN"
*/
int giScrollTextPixelWidth = SCREEN_WIDTH;
void CG_ScrollText( const char *str, int iPixelWidth )
{
const char *s,*holds;
int i;//, len;//, numChars;
giScrollTextPixelWidth = iPixelWidth;
// first, ask the strlen of the final string...
//
i = cgi_SP_GetStringTextString( str, NULL, 0 );
//ensure we found a match
if (!i)
{
#ifndef FINAL_BUILD
Com_Printf("WARNING: CG_ScrollText given invalid text key :'%s'\n",str);
#endif
return;
}
//
// malloc space to hold it...
//
char *psText = (char *) cgi_Z_Malloc( i+1, TAG_TEMP_WORKSPACE );
//
// now get the string...
//
i = cgi_SP_GetStringTextString( str, psText, i+1 );
//ensure we found a match
if (!i)
{
assert(0); // should never get here now, but wtf?
cgi_Z_Free(psText);
#ifndef FINAL_BUILD
Com_Printf("WARNING: CG_ScrollText given invalid text key :'%s'\n",str);
#endif
return;
}
cg.scrollTextTime = cg.time;
cg.printTextY = SCREEN_HEIGHT;
cg.scrollTextLines = 1;
s = psText;
i = 0;
holds = s;
const char *psBestLineBreakSrcPos = s;
const char *psLastGood_s; // needed if we get a full screen of chars with no punctuation or space (see usage notes)
while( *s )
{
psLastGood_s = s;
// read letter...
//
qboolean bIsTrailingPunctuation;
int iAdvanceCount;
unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(s, &iAdvanceCount, &bIsTrailingPunctuation);
s += iAdvanceCount;
// concat onto string so far...
//
if (uiLetter == 32 && cg.printText[i][0] == '\0')
{
holds++;
continue; // unless it's a space at the start of a line, in which case ignore it.
}
if (uiLetter > 255)
{
Q_strcat(cg.printText[i],sizeof(cg.printText[i]),va("%c%c",uiLetter >> 8, uiLetter & 0xFF));
}
else
{
Q_strcat(cg.printText[i],sizeof(cg.printText[i]),va("%c",uiLetter & 0xFF));
}
// record last-good linebreak pos... (ie if we've just concat'd a punctuation point (western or asian) or space)
//
if (bIsTrailingPunctuation || uiLetter == ' ')
{
psBestLineBreakSrcPos = s;
}
if (uiLetter == '\n')
{
// explicit new line...
//
cg.printText[i][ strlen(cg.printText[i])-1 ] = '\0'; // kill the CR
i++;
assert (i < (sizeof(cg.printText)/sizeof(cg.printText[0])) );
if (i >= (sizeof(cg.printText)/sizeof(cg.printText[0])) )
{
break;
}
holds = s;
cg.scrollTextLines++;
}
else
if ( cgi_R_Font_StrLenPixels(cg.printText[i], cgs.media.qhFontMedium, 1.0f) >= iPixelWidth)
{
// reached screen edge, so cap off string at bytepos after last good position...
//
if (psBestLineBreakSrcPos == holds)
{
// aarrrggh!!!!! we'll only get here is someone has fed in a (probably) garbage string,
// since it doesn't have a single space or punctuation mark right the way across one line
// of the screen. So far, this has only happened in testing when I hardwired a taiwanese
// string into this function while the game was running in english (which should NEVER happen
// normally). On the other hand I suppose it's entirely possible that some taiwanese string
// might have no punctuation at all, so...
//
psBestLineBreakSrcPos = psLastGood_s; // force a break after last good letter
}
cg.printText[i][ psBestLineBreakSrcPos - holds ] = '\0';
holds = s = psBestLineBreakSrcPos;
i++;
assert (i < (sizeof(cg.printText)/sizeof(cg.printText[0])) );
cg.scrollTextLines++;
}
}
cg.captionTextTime = 0; // No captions during scrolling
cgi_Z_Free(psText);
}
// draws using [textcolor_scroll]...
//
#define SCROLL_LPM (1/50.0) // 1 line per 50 ms
void CG_DrawScrollText(void)
{
char *start;
int i;
int x,y;
const int fontHeight = (int) (1.5f * (float) cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, 1.0f)); // taiwanese & japanese need 1.5 fontheight spacing
if ( !cg.scrollTextTime )
{
return;
}
cgi_R_SetColor( textcolor_scroll );
y = cg.printTextY - (cg.time - cg.scrollTextTime) * SCROLL_LPM;
// cgi_R_Font_DrawString(320, 200, va("Scrolltext printing @ %d",y), colorTable[CT_LTGOLD1], cgs.media.qhFontMedium, -1, 1.0f);
// See if text has finished scrolling off screen
if ((y + cg.scrollTextLines * fontHeight) < 1)
{
cg.scrollTextTime = 0;
return;
}
for (i=0;i<cg.scrollTextLines;++i)
{
// Is this line off top of screen?
if ((y + ((i +1) * fontHeight)) < 1)
{
y += fontHeight;
continue;
}
// or past bottom of screen?
else if (y > SCREEN_HEIGHT)
{
break;
}
start = cg.printText[i];
// w = cgi_R_Font_StrLenPixels(cg.printText[i], cgs.media.qhFontMedium, 1.0f);
// if (w)
{
x = (SCREEN_WIDTH - giScrollTextPixelWidth) / 2;
cgi_R_Font_DrawString(x,y, cg.printText[i], textcolor_scroll, cgs.media.qhFontMedium, -1, 1.0f);
y += fontHeight;
}
}
cgi_R_SetColor( NULL );
}
/*
===============================================================================
CENTER PRINTING
===============================================================================
*/
/*
==============
CG_CenterPrint
Called for important messages that should stay in the center of the screen
for a few moments
==============
*/
void CG_CenterPrint( const char *str, int y) {
char *s;
// Find text to match the str given
if (*str == '@')
{
int i;
i = cgi_SP_GetStringTextString( str+1, cg.centerPrint, sizeof(cg.centerPrint) );
if (!i)
{
Com_Printf (S_COLOR_RED"CG_CenterPrint: cannot find reference '%s' in StringPackage!\n",str);
Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) );
}
}
else
{
Q_strncpyz( cg.centerPrint, str, sizeof(cg.centerPrint) );
}
cg.centerPrintTime = cg.time;
cg.centerPrintY = y;
// count the number of lines for centering
cg.centerPrintLines = 1;
s = cg.centerPrint;
while( *s ) {
if (*s == '\n')
cg.centerPrintLines++;
s++;
}
}
/*
===================
CG_DrawCenterString
===================
*/
void CG_DrawCenterString( void )
{
char *start;
int l;
int x, y, w;
float *color;
if ( !cg.centerPrintTime ) {
return;
}
color = CG_FadeColor( cg.centerPrintTime, 1000 * 3 );
if ( !color ) {
return;
}
if((textcolor_center[0] == 0) && (textcolor_center[1] == 0) &&
(textcolor_center[2] == 0) && (textcolor_center[3] == 0))
{
Vector4Copy( colorTable[CT_WHITE], textcolor_center );
}
start = cg.centerPrint;
const int fontHeight = cgi_R_Font_HeightPixels(cgs.media.qhFontMedium, 1.0f);
y = cg.centerPrintY - (cg.centerPrintLines * fontHeight) / 2;
while ( 1 ) {
char linebuffer[1024];
// this is kind of unpleasant when dealing with MBCS, but...
//
const char *psString = start;
int iOutIndex = 0;
for ( l = 0; l < sizeof(linebuffer)-1; l++ ) {
int iAdvanceCount;
unsigned int uiLetter = cgi_AnyLanguage_ReadCharFromString(psString, &iAdvanceCount);
psString += iAdvanceCount;
if (!uiLetter || uiLetter == '\n'){
break;
}
if (uiLetter > 255)
{
linebuffer[iOutIndex++] = uiLetter >> 8;
linebuffer[iOutIndex++] = uiLetter & 0xFF;
}
else
{
linebuffer[iOutIndex++] = uiLetter & 0xFF;
}
}
linebuffer[iOutIndex++] = '\0';
w = cgi_R_Font_StrLenPixels(linebuffer, cgs.media.qhFontMedium, 1.0f);
x = ( SCREEN_WIDTH - w ) / 2;
cgi_R_Font_DrawString(x,y,linebuffer, textcolor_center, cgs.media.qhFontMedium, -1, 1.0f);
y += fontHeight;
while ( *start && ( *start != '\n' ) ) {
start++;
}
if ( !*start ) {
break;
}
start++;
}
}

2249
code/cgame/cg_view.cpp Normal file

File diff suppressed because it is too large Load Diff

3164
code/cgame/cg_weapons.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
// this line must stay at top so the whole PCH thing works...
#include "cg_headers.h"
// I am a dummy header file that takes the place of the PCH files in SoF2. This is done so that we can easily
// transport fx files amongst CHC, SoF2, and the effects editor...( If you are looking at this and have a better
// idea, just let me know what the better solution is. )
// The files can't be shared directly through Source Safe since the projects reside in different SS databases.
//
// - JD

View File

@@ -0,0 +1,82 @@
#ifndef __strip_objectives_h
#define __strip_objectives_h
/********************************************************************************
the objectives... duh...
********************************************************************************/
#define PACKAGE_OBJECTIVES 0x08
enum
{
OBJECTIVES_KEJIM_POST_OBJ1 = 0x0800,
OBJECTIVES_KEJIM_POST_OBJ2 = 0x0801,
OBJECTIVES_KEJIM_BASE_OBJ1 = 0x0802,
OBJECTIVES_KEJIM_BASE_OBJ2 = 0x0803,
OBJECTIVES_KEJIM_BASE_OBJ3 = 0x0804,
OBJECTIVES_ARTUS_MINE_OBJ1 = 0x0805,
OBJECTIVES_ARTUS_MINE_OBJ2 = 0x0806,
OBJECTIVES_ARTUS_MINE_OBJ3 = 0x0807,
OBJECTIVES_ARTUS_DETENTION_OBJ1 = 0x0808,
OBJECTIVES_ARTUS_DETENTION_OBJ2 = 0x0809,
OBJECTIVES_ARTUS_TOPSIDE_OBJ1 = 0x080a,
OBJECTIVES_ARTUS_TOPSIDE_OBJ2 = 0x080b,
OBJECTIVES_YAVIN_TEMPLE_OBJ1 = 0x080c,
OBJECTIVES_YAVIN_TRIAL_OBJ1 = 0x080d,
OBJECTIVES_YAVIN_TRIAL_OBJ2 = 0x080e,
OBJECTIVES_NS_STREETS_OBJ1 = 0x080f,
OBJECTIVES_NS_STREETS_OBJ2 = 0x0810,
OBJECTIVES_NS_STREETS_OBJ3 = 0x0811,
OBJECTIVES_NS_HIDEOUT_OBJ1 = 0x0812,
OBJECTIVES_NS_HIDEOUT_OBJ2 = 0x0813,
OBJECTIVES_NS_STARPAD_OBJ1 = 0x0814,
OBJECTIVES_NS_STARPAD_OBJ2 = 0x0815,
OBJECTIVES_NS_STARPAD_OBJ3 = 0x0816,
OBJECTIVES_NS_STARPAD_OBJ4 = 0x0817,
OBJECTIVES_NS_STARPAD_OBJ5 = 0x0818,
OBJECTIVES_BESPIN_UNDERCITY_OBJ1 = 0x0819,
OBJECTIVES_BESPIN_UNDERCITY_OBJ2 = 0x081a,
OBJECTIVES_BESPIN_STREETS_OBJ1 = 0x081b,
OBJECTIVES_BESPIN_STREETS_OBJ2 = 0x081c,
OBJECTIVES_BESPIN_PLATFORM_OBJ1 = 0x081d,
OBJECTIVES_BESPIN_PLATFORM_OBJ2 = 0x081e,
OBJECTIVES_CAIRN_BAY_OBJ1 = 0x081f,
OBJECTIVES_CAIRN_BAY_OBJ2 = 0x0820,
OBJECTIVES_CAIRN_ASSEMBLY_OBJ1 = 0x0821,
OBJECTIVES_CAIRN_REACTOR_OBJ1 = 0x0822,
OBJECTIVES_CAIRN_REACTOR_OBJ2 = 0x0823,
OBJECTIVES_CAIRN_DOCK1_OBJ1 = 0x0824,
OBJECTIVES_CAIRN_DOCK1_OBJ2 = 0x0825,
OBJECTIVES_DOOM_COMM_OBJ1 = 0x0826,
OBJECTIVES_DOOM_COMM_OBJ2 = 0x0827,
OBJECTIVES_DOOM_COMM_OBJ3 = 0x0828,
OBJECTIVES_DOOM_DETENTION_OBJ1 = 0x0829,
OBJECTIVES_DOOM_DETENTION_OBJ2 = 0x082a,
OBJECTIVES_DOOM_SHIELDS_OBJ1 = 0x082b,
OBJECTIVES_DOOM_SHIELDS_OBJ2 = 0x082c,
OBJECTIVES_YAVIN_SWAMP_OBJ1 = 0x082d,
OBJECTIVES_YAVIN_SWAMP_OBJ2 = 0x082e,
OBJECTIVES_YAVIN_CANYON_OBJ1 = 0x082f,
OBJECTIVES_YAVIN_CANYON_OBJ2 = 0x0830,
OBJECTIVES_YAVIN_COURTYARD_OBJ1 = 0x0831,
OBJECTIVES_YAVIN_COURTYARD_OBJ2 = 0x0832,
OBJECTIVES_YAVIN_FINAL_OBJ1 = 0x0833,
OBJECTIVES_KEJIM_POST_OBJ3 = 0x0834,
OBJECTIVES_KEJIM_POST_OBJ4 = 0x0835,
OBJECTIVES_KEJIM_POST_OBJ5 = 0x0836,
OBJECTIVES_ARTUS_DETENTION_OBJ3 = 0x0837,
OBJECTIVES_DOOM_COMM_OBJ4 = 0x0838,
OBJECTIVES_DOOM_SHIELDS_OBJ3 = 0x0839,
OBJECTIVES_DEMO_OBJ1 = 0x083a,
OBJECTIVES_DEMO_OBJ2 = 0x083b,
OBJECTIVES_DEMO_OBJ3 = 0x083c,
OBJECTIVES_DEMO_OBJ4 = 0x083d
};
#endif // __strip_objectives_h

Some files were not shown because too many files have changed in this diff Show More