/* Copyright (C) 1999-2007 id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant 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 GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "plugin.h" #include "entity.h" #include "entity_entitymodel.h" #include "light.h" int g_entityId = 1; // internal static void Entity_FreeEpairs( entity_t *e ); static void SetKeyValue( epair_t *&e, const char *key, const char *value ); static void DeleteKey( epair_t *&e, const char *key ); static const char *ValueForKey( epair_t *&e, const char *key ); static void Entity_OnKeyValueChanged( entity_t *e, const char* key, const char* value ); // constructor entity_t *Entity_Alloc(){ entity_t *e; e = (entity_t*)malloc( sizeof( *e ) ); e->entityId = g_entityId++; VectorSet( e->origin, 0, 0, 0 ); VectorSet( e->color, 1, 1, 1 ); e->redoId = 0; e->undoId = 0; e->next = e->prev = NULL; e->brushes.onext = e->brushes.oprev = &e->brushes; e->epairs = NULL; e->eclass = NULL; e->model.pRender = NULL; e->model.pSelect = NULL; e->model.pEdit = NULL; return e; } // destructor void Entity_Free( entity_t *e ){ while ( e->brushes.onext != &e->brushes ) Brush_Free( e->brushes.onext, true ); if ( e->next ) { e->next->prev = e->prev; e->prev->next = e->next; } Entity_FreeEpairs( e ); if ( e->model.pRender ) { e->model.pRender->DecRef(); e->model.pRender = NULL; } if ( e->model.pSelect ) { e->model.pSelect->DecRef(); e->model.pSelect = NULL; } if ( e->model.pEdit ) { e->model.pEdit->DecRef(); e->model.pEdit = NULL; } free( e ); } // construct from entity entity_t *Entity_Clone( entity_t *e ){ entity_t *n; epair_t *ep; n = Entity_Alloc(); n->eclass = e->eclass; for ( ep = e->epairs ; ep ; ep = ep->next ) SetKeyValue( n, ep->key, ep->value ); // copy some misc stuff as well VectorCopy( e->origin, n->origin ); // VectorCopy( e->vRotation, n->vRotation ); // VectorCopy( e->vScale, n->vScale ); // n->bDirty = true; return n; } const char *ValueForKey( epair_t *&e, const char *key ){ epair_t *ep; for ( ep = e ; ep ; ep = ep->next ) { if ( !strcmp( ep->key, key ) ) { return ep->value; } } return ""; } const char *ValueForKey( entity_t *ent, const char *key ){ return ValueForKey( ent->epairs, key ); } void SetKeyValue( epair_t *&e, const char *key, const char *value ){ epair_t *ep; for ( ep = e ; ep ; ep = ep->next ) { if ( !strcmp( ep->key, key ) ) { free( ep->value ); ep->value = (char*)malloc( strlen( value ) + 1 ); strcpy( ep->value, value ); return; } } ep = (epair_t*)malloc( sizeof( *ep ) ); ep->next = e; e = ep; ep->key = (char*)malloc( strlen( key ) + 1 ); strcpy( ep->key, key ); ep->value = (char*)malloc( strlen( value ) + 1 ); strcpy( ep->value, value ); } void SetKeyValue( entity_t *ent, const char *key, const char *value ){ if ( ent == NULL ) { Sys_FPrintf( SYS_ERR, "ERROR: SetKeyValue: NULL entity \n" ); return; } if ( !key || !key[0] ) { Sys_FPrintf( SYS_ERR, "ERROR: SetKeyValue: NULL or zero-length key\n" ); return; } SetKeyValue( ent->epairs, key, value ); /*! \todo TODO broadcast this through a clean messaging API ;-) */ Entity_OnKeyValueChanged( ent, key, value ); } void DeleteKey( epair_t *&e, const char *key ){ epair_t **ep, *next; ep = &e; while ( *ep ) { next = *ep; if ( !strcmp( next->key, key ) ) { *ep = next->next; free( next->key ); free( next->value ); free( next ); return; } ep = &next->next; } } void DeleteKey( entity_t *ent, const char *key ){ DeleteKey( ent->epairs, key ); Entity_OnKeyValueChanged( ent, key, "" ); } float FloatForKey( entity_t *ent, const char *key ){ const char *k; k = ValueForKey( ent, key ); return (float) atof( k ); } int IntForKey( entity_t *ent, const char *key ){ const char *k; k = ValueForKey( ent, key ); return atoi( k ); } void GetVectorForKey( entity_t *ent, const char *key, vec3_t vec ){ const char *k; k = ValueForKey( ent, key ); sscanf( k, "%f %f %f", &vec[0], &vec[1], &vec[2] ); } /* =============== Entity_FreeEpairs Frees the entity epairs. =============== */ void Entity_FreeEpairs( entity_t *e ){ epair_t *ep, *next; for ( ep = e->epairs; ep; ep = next ) { next = ep->next; free( ep->key ); free( ep->value ); free( ep ); } e->epairs = NULL; } void Entity_AddToList( entity_t *e, entity_t *elist ){ if ( e->next || e->prev ) { Error( "Entity_AddToList: already linked" ); } //e->next = elist->next; //elist->next->prev = e; //elist->next = e; //e->prev = elist; e->next = elist; e->prev = elist->prev; elist->prev->next = e; elist->prev = e; } 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; } void Entity_LinkBrush( entity_t *e, brush_t *b ){ if ( b->oprev || b->onext ) { Error( "Entity_LinkBrush: Already linked" ); } b->owner = e; // b->onext = e->brushes.onext; // b->oprev = &e->brushes; // e->brushes.onext->oprev = b; // e->brushes.onext = b; /* SPoG - changed to add brushes to end of list instead of start - so this can be used by map loader. This could concievably cause a problem if someone is traversing e->brushes while calling this function. So don't. */ b->onext = &e->brushes; b->oprev = e->brushes.oprev; e->brushes.oprev->onext = b; e->brushes.oprev = b; } void Entity_UnlinkBrush( brush_t *b ){ 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; } // for undo int Entity_MemorySize( entity_t *e ){ epair_t *ep; int size = 0; for ( ep = e->epairs; ep; ep = ep->next ) { size += strlen( ep->key ); size += strlen( ep->value ); size += sizeof( epair_t ); } size += sizeof( entity_t ); return size; } epair_t* Entity_AllocateEpair( const char *key, const char *value ){ epair_t *ep = (epair_t*)malloc( sizeof( *ep ) ); ep->key = (char*)malloc( strlen( key ) + 1 ); strcpy( ep->key, key ); ep->value = (char*)malloc( strlen( value ) + 1 ); strcpy( ep->value, value ); ep->next = NULL; return ep; } epair_t** Entity_GetKeyValList( entity_t *e ){ return &e->epairs; } void Entity_SetKeyValList( entity_t *e, epair_t* ep ){ if ( e->epairs ) { Sys_Printf( "Warning : pe->epairs != NULL in Entity_SetKeyValList, will not set\n" ); } else { e->epairs = ep; for ( epair_t *pe_ep = e->epairs; pe_ep; pe_ep = pe_ep->next ) Entity_OnKeyValueChanged( e, pe_ep->key, pe_ep->value ); } } /*! \todo FIXME TTimo this is meant to raise messages instead of calling the IEdit directly */ static void Entity_OnKeyValueChanged( entity_t *e, const char *key, const char* value ){ if ( strcmp( key,"classname" ) == 0 ) { e->eclass = Eclass_ForName( value, false ); Entity_UpdateClass( e, value ); if ( strcmp( value,"light" ) == 0 ) { for ( epair_t* ep = e->epairs; ep != NULL; ep = ep->next ) Light_OnKeyValueChanged( e, ep->key, ep->value ); } if ( e->model.pEdit ) { for ( epair_t* ep = e->epairs; ep != NULL; ep = ep->next ) e->model.pEdit->OnKeyValueChanged( e, ep->key, ep->value ); } } else if ( Entity_IsLight( e ) ) { Light_OnKeyValueChanged( e, key, value ); } else if ( e->model.pEdit ) { e->model.pEdit->OnKeyValueChanged( e, key, value ); } // update brush mins/maxs for legacy culling system if ( e->model.pRender && e->brushes.onext != &e->brushes ) { Brush_Build( e->brushes.onext, true, true, false, true ); } }