/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see .
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "tools/edit_gui_common.h"
#include "qe3.h"
#include "../../renderer/tr_local.h"
#include "../../renderer/model_local.h" // for idRenderModelMD5
int g_entityId = 1;
#define CURVE_TAG "curve_"
extern void Brush_Resize(brush_t *b, idVec3 vMin, idVec3 vMax);
int GetNumKeys(entity_t *ent)
{
// int iCount = 0;
// for (epair_t* ep=ent->epairs ; ep ; ep=ep->next)
// {
// iCount++;
// }
int iCount = ent->epairs.GetNumKeyVals();
return iCount;
}
const char *GetKeyString(entity_t *ent, int iIndex)
{
// for (epair_t* ep=ent->epairs ; ep ; ep=ep->next)
// {
// if (!iIndex--)
// return ep->key;
// }
//
// assert(0);
// return NULL;
if ( iIndex < GetNumKeys(ent) )
{
return ent->epairs.GetKeyVal(iIndex)->GetKey().c_str();
}
assert(0);
return NULL;
}
/*
=======================================================================================================================
=======================================================================================================================
*/
const char *ValueForKey(entity_t *ent, const char *key) {
return ent->epairs.GetString(key);
}
/*
=======================================================================================================================
=======================================================================================================================
*/
void TrackMD3Angles(entity_t *e, const char *key, const char *value) {
if ( idStr::Icmp(key, "angle") != 0 ) {
return;
}
if ((e->eclass->fixedsize && e->eclass->nShowFlags & ECLASS_MISCMODEL) || EntityHasModel(e)) {
float a = FloatForKey(e, "angle");
float b = atof(value);
if (a != b) {
idVec3 vAngle;
vAngle[0] = vAngle[1] = 0;
vAngle[2] = -a;
Brush_Rotate(e->brushes.onext, vAngle, e->origin, true);
vAngle[2] = b;
Brush_Rotate(e->brushes.onext, vAngle, e->origin, true);
}
}
}
/*
=======================================================================================================================
=======================================================================================================================
*/
void SetKeyValue(entity_t *ent, const char *key, const char *value, bool trackAngles) {
if (ent == NULL) {
return;
}
if (!key || !key[0]) {
return;
}
if (trackAngles) {
TrackMD3Angles(ent, key, value);
}
ent->epairs.Set(key, value);
GetVectorForKey(ent, "origin", ent->origin);
// update sound in case this key was relevent
Entity_UpdateSoundEmitter( ent );
}
/*
=======================================================================================================================
=======================================================================================================================
*/
void SetKeyVec3(entity_t *ent, const char *key, idVec3 v) {
if (ent == NULL) {
return;
}
if (!key || !key[0]) {
return;
}
idStr str;
sprintf(str, "%g %g %g", v.x, v.y, v.z);
ent->epairs.Set(key, str);
GetVectorForKey(ent, "origin", ent->origin);
}
/*
=======================================================================================================================
=======================================================================================================================
*/
void SetKeyMat3(entity_t *ent, const char *key, idMat3 m) {
if (ent == NULL) {
return;
}
if (!key || !key[0]) {
return;
}
idStr str;
sprintf(str, "%g %g %g %g %g %g %g %g %g",m[0][0],m[0][1],m[0][2],m[1][0],m[1][1],m[1][2],m[2][0],m[2][1],m[2][2]);
ent->epairs.Set(key, str);
GetVectorForKey(ent, "origin", ent->origin);
}
/*
=======================================================================================================================
=======================================================================================================================
*/
void DeleteKey(entity_t *ent, const char *key) {
ent->epairs.Delete(key);
if (stricmp(key, "rotation") == 0) {
ent->rotation.Identity();
}
}
/*
=======================================================================================================================
=======================================================================================================================
*/
float FloatForKey(entity_t *ent, const char *key) {
const char *k;
k = ValueForKey(ent, key);
if (k && *k) {
return atof(k);
}
return 0.0;
}
/*
=======================================================================================================================
=======================================================================================================================
*/
int IntForKey(entity_t *ent, const char *key) {
const char *k;
k = ValueForKey(ent, key);
return atoi(k);
}
/*
=======================================================================================================================
=======================================================================================================================
*/
bool GetVectorForKey(entity_t *ent, const char *key, idVec3 &vec) {
const char *k;
k = ValueForKey(ent, key);
if (k && strlen(k) > 0) {
sscanf(k, "%f %f %f", &vec[0], &vec[1], &vec[2]);
return true;
}
else {
vec[0] = vec[1] = vec[2] = 0;
}
return false;
}
/*
=======================================================================================================================
=======================================================================================================================
*/
bool GetVector4ForKey(entity_t *ent, const char *key, idVec4 &vec) {
const char *k;
k = ValueForKey(ent, key);
if (k && strlen(k) > 0) {
sscanf(k, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]);
return true;
}
else {
vec[0] = vec[1] = vec[2] = vec[3] = 0;
}
return false;
}
/*
=======================================================================================================================
=======================================================================================================================
*/
bool GetFloatForKey(entity_t *ent, const char *key, float *f) {
const char *k;
k = ValueForKey(ent, key);
if (k && strlen(k) > 0) {
*f = atof(k);
return true;
}
*f = 0;
return false;
}
/*
=======================================================================================================================
=======================================================================================================================
*/
bool GetMatrixForKey(entity_t *ent, const char *key, idMat3 &mat) {
const char *k;
k = ValueForKey(ent, key);
if (k && strlen(k) > 0) {
sscanf
(
k,
"%f %f %f %f %f %f %f %f %f ",
&mat[0][0],
&mat[0][1],
&mat[0][2],
&mat[1][0],
&mat[1][1],
&mat[1][2],
&mat[2][0],
&mat[2][1],
&mat[2][2]
);
return true;
}
else {
mat.Identity();
}
return false;
}
/*
=======================================================================================================================
Entity_FreeEpairs Frees the entity epairs.
=======================================================================================================================
*/
void Entity_FreeEpairs(entity_t *e) {
e->epairs.Clear();
}
/*
=======================================================================================================================
Entity_AddToList
=======================================================================================================================
*/
void Entity_AddToList(entity_t *e, entity_t *list) {
if (e->next || e->prev) {
Error("Entity_AddToList: already linked");
}
e->next = list->next;
list->next->prev = e;
list->next = e;
e->prev = list;
}
/*
=======================================================================================================================
Entity_RemoveFromList
=======================================================================================================================
*/
void Entity_RemoveFromList(entity_t *e) {
if ( !e->next || !e->prev ) {
Error("Entity_RemoveFromList: not linked");
}
e->next->prev = e->prev;
e->prev->next = e->next;
e->next = e->prev = NULL;
}
/*
=======================================================================================================================
Entity_Free Frees the entity and any brushes is has. The entity is removed from the global entities list.
=======================================================================================================================
*/
void Entity_Free( entity_t *e ) {
while ( e->brushes.onext != &e->brushes ) {
Brush_Free(e->brushes.onext);
}
if ( e->next ) {
e->next->prev = e->prev;
e->prev->next = e->next;
}
Entity_FreeEpairs( e );
delete e;
}
/*
=======================================================================================================================
Entity_MemorySize
=======================================================================================================================
*/
int Entity_MemorySize( entity_t *e )
{
brush_t *b;
int size;
size = sizeof( entity_t ) + e->epairs.Size();
for( b = e->brushes.onext; b != &e->brushes; b = b->onext )
{
size += Brush_MemorySize( b );
}
return( size );
}
/*
=======================================================================================================================
ParseEpair
=======================================================================================================================
*/
struct EpairFixup {
const char *name;
int type;
};
const EpairFixup FloatFixups[] = {
{ "origin", 1 },
{ "rotation", 2 },
{ "_color", 1 },
{ "falloff", 0 },
{ "light", 0 },
{ "light_target", 1 },
{ "light_up", 1 },
{ "light_right", 1 },
{ "light_start", 1 },
{ "light_center", 1 },
{ "light_end", 1 },
{ "light_radius", 1 },
{ "light_origin", 1 }
};
const int FixupCount = sizeof(FloatFixups) / sizeof(EpairFixup);
void FixFloats(idDict *dict) {
int count = dict->GetNumKeyVals();
for (int i = 0; i < count; i++) {
const idKeyValue *kv = dict->GetKeyVal(i);
for (int j = 0; j < FixupCount; j++) {
if (kv->GetKey().Icmp(FloatFixups[j].name) == 0) {
idStr val;
if (FloatFixups[j].type == 1) {
idVec3 v;
sscanf(kv->GetValue().c_str(), "%f %f %f", &v.x, &v.y, &v.z);
sprintf(val, "%g %g %g", v.x, v.y, v.z);
} else if (FloatFixups[j].type == 2) {
idMat3 mat;
sscanf(kv->GetValue().c_str(), "%f %f %f %f %f %f %f %f %f ",&mat[0][0],&mat[0][1],&mat[0][2],&mat[1][0],&mat[1][1],&mat[1][2],&mat[2][0],&mat[2][1],&mat[2][2]);
sprintf(val, "%g %g %g %g %g %g %g %g %g",mat[0][0],mat[0][1],mat[0][2],mat[1][0],mat[1][1],mat[1][2],mat[2][0],mat[2][1],mat[2][2]);
} else {
float f = atof(kv->GetValue().c_str());
sprintf(val, "%g", f);
}
dict->Set(kv->GetKey(), val);
break;
}
}
}
}
void ParseEpair(idDict *dict) {
idStr key = token;
GetToken(false);
idStr val = token;
if (key.Length() > 0) {
dict->Set(key, val);
}
}
/*
=======================================================================================================================
=======================================================================================================================
*/
bool EntityHasModel(entity_t *ent) {
if (ent) {
const char *model = ValueForKey(ent, "model");
const char *name = ValueForKey(ent, "name");
if (model && *model) {
if ( idStr::Icmp(model, name) ) {
return true;
}
}
}
return false;
}
/*
=======================================================================================================================
=======================================================================================================================
*/
entity_t *Entity_New() {
entity_t *ent = new entity_t;
ent->prev = ent->next = NULL;
ent->brushes.prev = ent->brushes.next = NULL;
ent->brushes.oprev = ent->brushes.onext = NULL;
ent->brushes.owner = NULL;
ent->undoId = 0;
ent->redoId = 0;
ent->entityId = g_entityId++;
ent->origin.Zero();
ent->eclass = NULL;
ent->md3Class = NULL;
ent->lightOrigin.Zero();
ent->lightRotation.Identity();
ent->trackLightOrigin = false;
ent->rotation.Identity();
ent->lightDef = -1;
ent->modelDef = -1;
ent->soundEmitter = NULL;
ent->curve = NULL;
return ent;
}
void Entity_UpdateCurveData( entity_t *ent ) {
if ( ent == NULL || ent->curve == NULL ) {
return;
}
const idKeyValue *kv = ent->epairs.MatchPrefix( CURVE_TAG );
if ( kv == NULL ) {
if ( ent->curve ) {
delete ent->curve;
ent->curve = NULL;
if ( g_qeglobals.d_select_mode == sel_editpoint ) {
g_qeglobals.d_select_mode = sel_brush;
}
}
return;
}
int c = ent->curve->GetNumValues();
idStr str = va( "%i ( ", c );
idVec3 v;
for ( int i = 0; i < c; i++ ) {
v = ent->curve->GetValue( i );
str += " ";
str += v.ToString();
str += " ";
}
str += " )";
ent->epairs.Set( kv->GetKey(), str );
}
idCurve *Entity_MakeCurve( entity_t *ent ) {
const idKeyValue *kv = ent->epairs.MatchPrefix( CURVE_TAG );
if ( kv ) {
idStr str = kv->GetKey().Right( kv->GetKey().Length() - strlen( CURVE_TAG ) );
if ( str.Icmp( "CatmullRomSpline" ) == 0 ) {
return new idCurve_CatmullRomSpline();
} else if ( str.Icmp( "Nurbs" ) == 0 ) {
return new idCurve_NURBS();
}
}
return NULL;
}
void Entity_SetCurveData( entity_t *ent ) {
ent->curve = Entity_MakeCurve( ent );
const idKeyValue *kv = ent->epairs.MatchPrefix( CURVE_TAG );
if ( kv && ent->curve ) {
idLexer lex;
lex.LoadMemory( kv->GetValue(), kv->GetValue().Length(), "_curve" );
int numPoints = lex.ParseInt();
if ( numPoints > 0 ) {
float *fp = new float[numPoints * 3];
lex.Parse1DMatrix( numPoints * 3, fp );
int time = 0;
for ( int i = 0; i < numPoints * 3; i += 3 ) {
idVec3 v;
v.x = fp[i];
v.y = fp[i+1];
v.z = fp[i+2];
ent->curve->AddValue( time, v );
time += 100;
}
delete []fp;
}
}
}
entity_t *Entity_PostParse(entity_t *ent, brush_t *pList) {
bool has_brushes;
eclass_t *e;
brush_t *b;
idVec3 mins, maxs, zero;
idBounds bo;
zero.Zero();
Entity_SetCurveData( ent );
if (ent->brushes.onext == &ent->brushes) {
has_brushes = false;
}
else {
has_brushes = true;
}
bool needsOrigin = !GetVectorForKey(ent, "origin", ent->origin);
const char *pModel = ValueForKey(ent, "model");
const char *cp = ValueForKey(ent, "classname");
if (strlen(cp)) {
e = Eclass_ForName(cp, has_brushes);
} else {
const char *cp2 = ValueForKey(ent, "name");
if (strlen(cp2)) {
char buff[1024];
strcpy(buff, cp2);
int len = strlen(buff);
while ((isdigit(buff[len-1]) || buff[len-1] == '_') && len > 0) {
buff[len-1] = '\0';
len--;
}
e = Eclass_ForName(buff, has_brushes);
SetKeyValue(ent, "classname", buff, false);
} else {
e = Eclass_ForName("", has_brushes);
}
}
idStr str;
if (e->defArgs.GetString("model", "", str) && e->entityModel == NULL) {
e->entityModel = gameEdit->ANIM_GetModelFromEntityDef( &e->defArgs );
}
ent->eclass = e;
bool hasModel = EntityHasModel(ent);
if (hasModel) {
ent->eclass->defArgs.GetString("model", "", str);
if (str.Length()) {
hasModel = false;
ent->epairs.Delete("model");
}
}
if (e->nShowFlags & ECLASS_WORLDSPAWN) {
ent->origin.Zero();
needsOrigin = false;
ent->epairs.Delete( "model" );
} else if (e->nShowFlags & ECLASS_LIGHT) {
if (GetVectorForKey(ent, "light_origin", ent->lightOrigin)) {
GetMatrixForKey(ent, "light_rotation", ent->lightRotation);
ent->trackLightOrigin = true;
} else if (hasModel) {
SetKeyValue(ent, "light_origin", ValueForKey(ent, "origin"));
ent->lightOrigin = ent->origin;
if (GetMatrixForKey(ent, "rotation", ent->lightRotation)) {
SetKeyValue(ent, "light_rotation", ValueForKey(ent, "rotation"));
}
ent->trackLightOrigin = true;
}
} else if ( e->nShowFlags & ECLASS_ENV ) {
// need to create an origin from the bones here
idVec3 org;
idAngles ang;
bo.Clear();
bool hasBody = false;
const idKeyValue *arg = ent->epairs.MatchPrefix( "body ", NULL );
while ( arg ) {
sscanf( arg->GetValue(), "%f %f %f %f %f %f", &org.x, &org.y, &org.z, &ang.pitch, &ang.yaw, &ang.roll );
bo.AddPoint( org );
arg = ent->epairs.MatchPrefix( "body ", arg );
hasBody = true;
}
if (hasBody) {
ent->origin = bo.GetCenter();
}
}
if (e->fixedsize || hasModel) { // fixed size entity
if (ent->brushes.onext != &ent->brushes) {
for (b = ent->brushes.onext; b != &ent->brushes; b = b->onext) {
b->entityModel = true;
}
}
if (hasModel) {
// model entity
idRenderModel *modelHandle = renderModelManager->FindModel( pModel );
if ( dynamic_cast( modelHandle ) || dynamic_cast( modelHandle ) ) {
bo.Zero();
bo.ExpandSelf( 12.0f );
} else {
bo = modelHandle->Bounds( NULL );
}
VectorCopy(bo[0], mins);
VectorCopy(bo[1], maxs);
for (int i = 0; i < 3; i++) {
if (mins[i] == maxs[i]) {
mins[i]--;
maxs[i]++;
}
}
VectorAdd(mins, ent->origin, mins);
VectorAdd(maxs, ent->origin, maxs);
b = Brush_Create(mins, maxs, &e->texdef);
b->modelHandle = modelHandle;
float yaw = 0;
bool convertAngles = GetFloatForKey(ent, "angle", &yaw);
extern void Brush_Rotate(brush_t *b, idMat3 matrix, idVec3 origin, bool bBuild);
extern void Brush_Rotate(brush_t *b, idVec3 rot, idVec3 origin, bool bBuild);
if (convertAngles) {
idVec3 rot(0, 0, yaw);
Brush_Rotate(b, rot, ent->origin, false);
}
if (GetMatrixForKey(ent, "rotation", ent->rotation)) {
idBounds bo2;
bo2.FromTransformedBounds(bo, ent->origin, ent->rotation);
b->owner = ent;
Brush_Resize(b, bo2[0], bo2[1]);
}
Entity_LinkBrush(ent, b);
}
if (!hasModel || (ent->eclass->nShowFlags & ECLASS_LIGHT && hasModel)) {
// create a custom brush
if (ent->trackLightOrigin) {
mins = e->mins + ent->lightOrigin;
maxs = e->maxs + ent->lightOrigin;
} else {
mins = e->mins + ent->origin;
maxs = e->maxs + ent->origin;
}
b = Brush_Create(mins, maxs, &e->texdef);
GetMatrixForKey(ent, "rotation", ent->rotation);
Entity_LinkBrush(ent, b);
b->trackLightOrigin = ent->trackLightOrigin;
if ( e->texdef.name == NULL ) {
brushprimit_texdef_t bp;
texdef_t td;
td.SetName( ent->eclass->defMaterial );
Brush_SetTexture( b, &td, &bp, false );
}
}
} else { // brush entity
if (ent->brushes.next == &ent->brushes) {
printf("Warning: Brush entity with no brushes\n");
}
if (!needsOrigin) {
idStr cn = ValueForKey(ent, "classname");
idStr name = ValueForKey(ent, "name");
idStr model = ValueForKey(ent, "model");
if (cn.Icmp("func_static") == 0) {
if (name.Icmp(model) == 0) {
needsOrigin = true;
}
}
}
if (needsOrigin) {
idVec3 entmins, entmaxs, mid;
int i;
char text[32];
entmins[0] = entmins[1] = entmins[2] = 999999;
entmaxs[0] = entmaxs[1] = entmaxs[2] = -999999;
// add in the origin
for (b = ent->brushes.onext; b != &ent->brushes; b = b->onext) {
Brush_Build(b, true, false, false);
for (i = 0; i < 3; i++) {
if (b->mins[i] < entmins[i]) {
entmins[i] = b->mins[i];
}
if (b->maxs[i] > entmaxs[i]) {
entmaxs[i] = b->maxs[i];
}
}
}
for (i = 0; i < 3; i++) {
ent->origin[i] = (entmins[i] + ((entmaxs[i] - entmins[i]) / 2));
}
sprintf(text, "%i %i %i", (int)ent->origin[0], (int)ent->origin[1], (int)ent->origin[2]);
SetKeyValue(ent, "origin", text);
}
if (!(e->nShowFlags & ECLASS_WORLDSPAWN)) {
if (e->defArgs.FindKey("model") == NULL && (pModel == NULL || (pModel && strlen(pModel) == 0))) {
SetKeyValue(ent, "model", ValueForKey(ent, "name"));
}
}
else {
DeleteKey(ent, "origin");
}
}
// add all the brushes to the main list
if (pList) {
for (b = ent->brushes.onext; b != &ent->brushes; b = b->onext) {
b->next = pList->next;
pList->next->prev = b;
b->prev = pList;
pList->next = b;
}
}
FixFloats(&ent->epairs);
return ent;
}
/*
=======================================================================================================================
Entity_Parse If onlypairs is set, the classname info will not be looked up, and the entity will not be added to the
global list. Used for parsing the project.
=======================================================================================================================
*/
entity_t *Entity_Parse(bool onlypairs, brush_t *pList) {
entity_t *ent;
if (!GetToken(true)) {
return NULL;
}
if (strcmp(token, "{")) {
Error("ParseEntity: { not found");
}
ent = Entity_New();
ent->brushes.onext = ent->brushes.oprev = &ent->brushes;
ent->origin.Zero();
do {
if (!GetToken(true)) {
Warning("ParseEntity: EOF without closing brace");
return NULL;
}
if (!strcmp(token, "}")) {
break;
}
if (!strcmp(token, "{")) {
GetVectorForKey(ent, "origin", ent->origin);
brush_t *b = Brush_Parse(ent->origin);
if (b != NULL) {
b->owner = ent;
// add to the end of the entity chain
b->onext = &ent->brushes;
b->oprev = ent->brushes.oprev;
ent->brushes.oprev->onext = b;
ent->brushes.oprev = b;
}
else {
break;
}
}
else {
ParseEpair(&ent->epairs);
}
} while (1);
if (onlypairs) {
return ent;
}
return Entity_PostParse(ent, pList);
}
/*
=======================================================================================================================
=======================================================================================================================
*/
void VectorMidpoint(idVec3 va, idVec3 vb, idVec3 &out) {
for (int i = 0; i < 3; i++) {
out[i] = va[i] + ((vb[i] - va[i]) / 2);
}
}
/*
=======================================================================================================================
Entity_Write
=======================================================================================================================
*/
void Entity_Write(entity_t *e, FILE *f, bool use_region) {
brush_t *b;
idVec3 origin;
char text[128];
int count;
// if none of the entities brushes are in the region, don't write the entity at all
if (use_region) {
// in region mode, save the camera position as playerstart
if (!strcmp(ValueForKey(e, "classname"), "info_player_start")) {
fprintf(f, "{\n");
fprintf(f, "\"classname\" \"info_player_start\"\n");
fprintf
(
f,
"\"origin\" \"%i %i %i\"\n",
(int)g_pParentWnd->GetCamera()->Camera().origin[0],
(int)g_pParentWnd->GetCamera()->Camera().origin[1],
(int)g_pParentWnd->GetCamera()->Camera().origin[2]
);
fprintf(f, "\"angle\" \"%i\"\n", (int)g_pParentWnd->GetCamera()->Camera().angles[YAW]);
fprintf(f, "}\n");
return;
}
for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
if (!Map_IsBrushFiltered(b)) {
break; // got one
}
}
if (b == &e->brushes) {
return; // nothing visible
}
}
if (e->eclass->nShowFlags & ECLASS_PLUGINENTITY) {
// NOTE: the whole brush placement / origin stuff is a mess
VectorCopy(e->origin, origin);
sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
SetKeyValue(e, "origin", text);
}
// if fixedsize, calculate a new origin based on the current brush position
else if (e->eclass->fixedsize || EntityHasModel(e)) {
if (!GetVectorForKey(e, "origin", origin)) {
VectorSubtract(e->brushes.onext->mins, e->eclass->mins, origin);
sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
SetKeyValue(e, "origin", text);
}
}
fprintf(f, "{\n");
count = e->epairs.GetNumKeyVals();
for (int j = 0; j < count; j++) {
fprintf(f, "\"%s\" \"%s\"\n", e->epairs.GetKeyVal(j)->GetKey().c_str(), e->epairs.GetKeyVal(j)->GetValue().c_str());
}
if (!EntityHasModel(e)) {
count = 0;
for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
if (e->eclass->fixedsize && !b->entityModel) {
continue;
}
if (!use_region || !Map_IsBrushFiltered(b)) {
fprintf(f, "// brush %i\n", count);
count++;
Brush_Write( b, f, e->origin, ( g_PrefsDlg.m_bNewMapFormat != FALSE ) );
}
}
}
fprintf(f, "}\n");
}
/*
=======================================================================================================================
=======================================================================================================================
*/
bool IsBrushSelected(brush_t *bSel) {
for (brush_t * b = selected_brushes.next; b != NULL && b != &selected_brushes; b = b->next) {
if (b == bSel) {
return true;
}
}
return false;
}
//
// =======================================================================================================================
// Entity_WriteSelected
// =======================================================================================================================
//
void Entity_WriteSelected(entity_t *e, FILE *f) {
brush_t *b;
idVec3 origin;
char text[128];
int count;
for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
if (IsBrushSelected(b)) {
break; // got one
}
}
if (b == &e->brushes) {
return; // nothing selected
}
// if fixedsize, calculate a new origin based on the current brush position
if (e->eclass->fixedsize || EntityHasModel(e)) {
if (!GetVectorForKey(e, "origin", origin)) {
VectorSubtract(e->brushes.onext->mins, e->eclass->mins, origin);
sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
SetKeyValue(e, "origin", text);
}
}
fprintf(f, "{\n");
count = e->epairs.GetNumKeyVals();
for (int j = 0; j < count; j++) {
fprintf(f, "\"%s\" \"%s\"\n", e->epairs.GetKeyVal(j)->GetKey().c_str(), e->epairs.GetKeyVal(j)->GetValue().c_str());
}
if (!EntityHasModel(e)) {
count = 0;
for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
if (e->eclass->fixedsize && !b->entityModel) {
continue;
}
if (IsBrushSelected(b)) {
fprintf(f, "// brush %i\n", count);
count++;
Brush_Write( b, f, e->origin, ( g_PrefsDlg.m_bNewMapFormat != FALSE ) );
}
}
}
fprintf(f, "}\n");
}
//
// =======================================================================================================================
// Entity_WriteSelected to a CMemFile
// =======================================================================================================================
//
void Entity_WriteSelected(entity_t *e, CMemFile *pMemFile) {
brush_t *b;
idVec3 origin;
char text[128];
int count;
for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
if (IsBrushSelected(b)) {
break; // got one
}
}
if (b == &e->brushes) {
return; // nothing selected
}
// if fixedsize, calculate a new origin based on the current brush position
if (e->eclass->fixedsize || EntityHasModel(e)) {
if (!GetVectorForKey(e, "origin", origin)) {
VectorSubtract(e->brushes.onext->mins, e->eclass->mins, origin);
sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
SetKeyValue(e, "origin", text);
}
}
MemFile_fprintf(pMemFile, "{\n");
count = e->epairs.GetNumKeyVals();
for (int j = 0; j < count; j++) {
MemFile_fprintf(pMemFile, "\"%s\" \"%s\"\n", e->epairs.GetKeyVal(j)->GetKey().c_str(), e->epairs.GetKeyVal(j)->GetValue().c_str());
}
if (!EntityHasModel(e)) {
count = 0;
for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
if (e->eclass->fixedsize && !b->entityModel) {
continue;
}
if (IsBrushSelected(b)) {
MemFile_fprintf(pMemFile, "// brush %i\n", count);
count++;
Brush_Write( b, pMemFile, e->origin, ( g_PrefsDlg.m_bNewMapFormat != FALSE ) );
}
}
}
MemFile_fprintf(pMemFile, "}\n");
}
/*
=======================================================================================================================
=======================================================================================================================
*/
void Entity_SetName(entity_t *e, const char *name) {
CString oldName = ValueForKey(e, "name");
CString oldModel = ValueForKey(e, "model");
SetKeyValue(e, "name", name);
if (oldName == oldModel) {
SetKeyValue(e, "model", name);
}
}
extern bool Entity_NameIsUnique(const char *name);
/*
=======================================================================================================================
=======================================================================================================================
*/
void Entity_Name(entity_t *e, bool force) {
const char *name = ValueForKey(e, "name");
if (!force && name && name[0]) {
return;
}
if (name && name[0] && Entity_NameIsUnique(name)) {
return;
}
bool setModel = false;
if (name[0]) {
const char *model = ValueForKey(e, "model");
if (model[0]) {
if ( idStr::Icmp(model, name) == 0 ) {
setModel = true;
}
}
}
const char *eclass = ValueForKey(e, "classname");
if (eclass && eclass[0]) {
idStr str = cvarSystem->GetCVarString( "radiant_nameprefix" );
int id = Map_GetUniqueEntityID(str, eclass);
if (str.Length()) {
SetKeyValue(e, "name", va("%s_%s_%i", str.c_str(), eclass, id));
} else {
SetKeyValue(e, "name", va("%s_%i", eclass, id));
}
if (setModel) {
if (str.Length()) {
SetKeyValue(e, "model", va("%s_%s_%i", str.c_str(), eclass, id));
} else {
SetKeyValue(e, "model", va("%s_%i", eclass, id));
}
}
}
}
/*
=======================================================================================================================
Entity_Create Creates a new entity out of the selected_brushes list. If the entity class is fixed size, the brushes
are only used to find a midpoint. Otherwise, the brushes have their ownership transfered to the new entity.
=======================================================================================================================
*/
entity_t *Entity_Create(eclass_t *c, bool forceFixed) {
entity_t *e;
brush_t *b;
idVec3 mins, maxs, origin;
char text[32];
texdef_t td;
brushprimit_texdef_t bp;
// check to make sure the brushes are ok
for (b = selected_brushes.next; b != &selected_brushes; b = b->next) {
if (b->owner != world_entity) {
Sys_Status("Entity NOT created, brushes not all from world\n");
Sys_Beep();
return NULL;
}
}
idStr str;
if (c->defArgs.GetString("model", "", str) && c->entityModel == NULL) {
c->entityModel = gameEdit->ANIM_GetModelFromEntityDef( &c->defArgs );
}
// create it
e = Entity_New();
e->brushes.onext = e->brushes.oprev = &e->brushes;
e->eclass = c;
e->epairs.Copy(c->args);
SetKeyValue(e, "classname", c->name);
Entity_Name(e, false);
// add the entity to the entity list
Entity_AddToList(e, &entities);
if (c->fixedsize) {
//
// just use the selection for positioning b = selected_brushes.next; for (i=0 ;
// i<3 ; i++) { e->origin[i] = b->mins[i] - c->mins[i]; }
//
Select_GetMid(e->origin);
VectorCopy(e->origin, origin);
// create a custom brush
VectorAdd(c->mins, e->origin, mins);
VectorAdd(c->maxs, e->origin, maxs);
b = Brush_Create(mins, maxs, &c->texdef);
Entity_LinkBrush(e, b);
if (c->defMaterial.Length()) {
td.SetName(c->defMaterial);
Brush_SetTexture(b, &td, &bp, false);
}
// delete the current selection
Select_Delete();
// select the new brush
b->next = b->prev = &selected_brushes;
selected_brushes.next = selected_brushes.prev = b;
Brush_Build(b);
}
else {
Select_GetMid(origin);
// change the selected brushes over to the new entity
for (b = selected_brushes.next; b != &selected_brushes; b = b->next) {
Entity_UnlinkBrush(b);
Entity_LinkBrush(e, b);
Brush_Build(b); // so the key brush gets a name
if (c->defMaterial.Length()) {
td.SetName(c->defMaterial);
Brush_SetTexture(b, &td, &bp, false);
}
}
//for (int i = 0; i < 3; i++) {
// origin[i] = vMin[i] + vMax[i] * 0.5;
//}
if (!forceFixed) {
SetKeyValue(e, "model", ValueForKey(e, "name"));
}
}
sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
SetKeyValue(e, "origin", text);
VectorCopy(origin, e->origin);
Sys_UpdateWindows(W_ALL);
return e;
}
void Brush_MakeDirty(brush_t *b) {
for (face_t *f = b->brush_faces; f; f = f->next) {
f->dirty = true;
}
}
/*
=======================================================================================================================
Entity_LinkBrush
=======================================================================================================================
*/
void Entity_LinkBrush(entity_t *e, brush_t *b) {
if (b->oprev || b->onext) {
Error("Entity_LinkBrush: Allready linked");
}
Brush_MakeDirty(b);
b->owner = e;
b->onext = e->brushes.onext;
b->oprev = &e->brushes;
e->brushes.onext->oprev = b;
e->brushes.onext = b;
}
/*
=======================================================================================================================
Entity_UnlinkBrush
=======================================================================================================================
*/
void Entity_UnlinkBrush(brush_t *b) {
// if (!b->owner || !b->onext || !b->oprev)
if (!b->onext || !b->oprev) {
Error("Entity_UnlinkBrush: Not currently linked");
}
b->onext->oprev = b->oprev;
b->oprev->onext = b->onext;
b->onext = b->oprev = NULL;
b->owner = NULL;
}
/*
=======================================================================================================================
Entity_Clone
=======================================================================================================================
*/
entity_t *Entity_Clone(entity_t *e) {
entity_t *n;
n = Entity_New();
n->brushes.onext = n->brushes.oprev = &n->brushes;
n->eclass = e->eclass;
n->rotation = e->rotation;
n->origin = e->origin;
// add the entity to the entity list
Entity_AddToList(n, &entities);
n->epairs = e->epairs;
return n;
}
/*
=======================================================================================================================
=======================================================================================================================
*/
int GetUniqueTargetId(int iHint) {
int iMin, iMax, i;
BOOL fFound;
entity_t *pe;
fFound = FALSE;
pe = entities.next;
iMin = 0;
iMax = 0;
for (; pe != NULL && pe != &entities; pe = pe->next) {
i = IntForKey(pe, "target");
if (i) {
iMin = Min(i, iMin);
iMax = Max(i, iMax);
if (i == iHint) {
fFound = TRUE;
}
}
}
if (fFound) {
return iMax + 1;
}
else {
return iHint;
}
}
/*
=======================================================================================================================
=======================================================================================================================
*/
entity_t *FindEntity(const char *pszKey, const char *pszValue) {
entity_t *pe;
pe = entities.next;
for (; pe != NULL && pe != &entities; pe = pe->next) {
if (!strcmp(ValueForKey(pe, pszKey), pszValue)) {
return pe;
}
}
return NULL;
}
/*
=======================================================================================================================
=======================================================================================================================
*/
entity_t *FindEntityInt(const char *pszKey, int iValue) {
entity_t *pe;
pe = entities.next;
for (; pe != NULL && pe != &entities; pe = pe->next) {
if (IntForKey(pe, pszKey) == iValue) {
return pe;
}
}
return NULL;
}
/*
====================
Entity_UpdateSoundEmitter
Deletes the soundEmitter if the entity should not emit a sound due
to it not having one, being filtered away, or the sound mode being turned off.
Creates or updates the soundEmitter if needed
====================
*/
void Entity_UpdateSoundEmitter( entity_t *ent ) {
bool playing = false;
// if an entity doesn't have any brushes at all, don't do anything
// if the brush isn't displayed (filtered or culled), don't do anything
if ( g_pParentWnd->GetCamera()->GetSoundMode()
&& ent->brushes.onext != &ent->brushes && !FilterBrush(ent->brushes.onext) ) {
// check for sounds
const char *v = ValueForKey( ent, "s_shader" );
if ( v[0] ) {
refSound_t sound;
gameEdit->ParseSpawnArgsToRefSound( &ent->epairs, &sound );
if ( !sound.waitfortrigger ) { // waitfortrigger will not start playing immediately
if ( !ent->soundEmitter ) {
ent->soundEmitter = g_qeglobals.sw->AllocSoundEmitter();
}
playing = true;
ent->soundEmitter->UpdateEmitter( ent->origin, 0, &sound.parms );
// always play on a single channel, so updates always override
ent->soundEmitter->StartSound( sound.shader, SCHANNEL_ONE );
}
}
}
// delete the soundEmitter if not used
if ( !playing && ent->soundEmitter ) {
ent->soundEmitter->Free( true );
ent->soundEmitter = NULL;
}
}