mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-30 08:00:51 +00:00
470 lines
8.7 KiB
Objective-C
470 lines
8.7 KiB
Objective-C
#include "QF/dstring.h"
|
|
#include "QF/script.h"
|
|
#include "QF/sys.h"
|
|
#include "QF/va.h"
|
|
|
|
#include "Entity.h"
|
|
#include "EntityClass.h"
|
|
#include "TexturePalette.h"
|
|
#include "SetBrush.h"
|
|
#include "Map.h"
|
|
#include "CameraView.h"
|
|
|
|
#define THING Entity
|
|
#include "THING+NSArray.m"
|
|
|
|
@implementation Entity
|
|
|
|
vec3_t bad_mins = {-8, -8, -8};
|
|
vec3_t bad_maxs = {8, 8, 8};
|
|
|
|
- (id) createFixedBrush: (vec3_t)org
|
|
{
|
|
vec3_t emins, emaxs;
|
|
float *v, *v2, *color;
|
|
id new;
|
|
texturedef_t td;
|
|
|
|
// get class
|
|
new = [entity_classes_i classForName: [self valueForQKey: "classname"]];
|
|
if (new) {
|
|
v = [new mins];
|
|
v2 = [new maxs];
|
|
} else {
|
|
v = bad_mins;
|
|
v2 = bad_maxs;
|
|
}
|
|
|
|
color = [new drawColor];
|
|
|
|
modifiable = NO;
|
|
memset (&td, 0, sizeof (td));
|
|
strcpy (td.texture, "entity");
|
|
|
|
VectorAdd (org, v, emins);
|
|
VectorAdd (org, v2, emaxs);
|
|
new = [[SetBrush alloc] initOwner: self mins: emins maxs: emaxs texture: &td];
|
|
[new setEntityColor: color];
|
|
|
|
[self addObject: new];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (id) copyWithZone: (NSZone *) zone
|
|
{
|
|
id new, nb;
|
|
epair_t *e;
|
|
int i, c;
|
|
|
|
new = [[Entity allocWithZone: zone] init];
|
|
[new setModifiable: modifiable];
|
|
|
|
for (e = epairs; e; e = e->next) {
|
|
// don't copy target and targetname fields
|
|
if (strncmp (e->key, "target", 6))
|
|
[new setKey: e->key toValue: e->value];
|
|
}
|
|
|
|
c = [self count];
|
|
for (i = 0; i < c; i++) {
|
|
nb = [[self objectAtIndex: i] copy];
|
|
[nb setParent: new];
|
|
[new addObject: nb];
|
|
}
|
|
|
|
return new;
|
|
}
|
|
|
|
- (Entity *) initClass: (const char *)classname
|
|
{
|
|
id new;
|
|
esize_t esize;
|
|
vec3_t min, max;
|
|
int org[3];
|
|
float *v;
|
|
|
|
self = [super init];
|
|
array = [[NSMutableArray alloc] init];
|
|
|
|
modifiable = YES;
|
|
|
|
[self setKey: "classname" toValue: classname];
|
|
|
|
// get class
|
|
new = [entity_classes_i classForName: [self valueForQKey: "classname"]];
|
|
if (!new)
|
|
esize = esize_model;
|
|
else
|
|
esize = [new esize];
|
|
|
|
// create a brush if needed
|
|
if (esize == esize_fixed) {
|
|
v = [new mins];
|
|
[[map_i selectedBrush] getMins: min maxs: max];
|
|
VectorSubtract (min, v, min);
|
|
VectorCopy (min, org);
|
|
|
|
// convert to integer
|
|
[self setKey: "origin" toValue: va ("%i %i %i", org[0], org[1], org[2])];
|
|
|
|
[self createFixedBrush: min];
|
|
} else {
|
|
modifiable = YES;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (oneway void) dealloc
|
|
{
|
|
epair_t *e, *n;
|
|
|
|
for (e = epairs; e; e = n) {
|
|
n = e->next;
|
|
free (e->key);
|
|
free (e->value);
|
|
free (e);
|
|
}
|
|
[array release];
|
|
[super dealloc];
|
|
}
|
|
|
|
- (BOOL) modifiable
|
|
{
|
|
return modifiable;
|
|
}
|
|
|
|
- (void) setModifiable: (BOOL)m
|
|
{
|
|
modifiable = m;
|
|
return;
|
|
}
|
|
|
|
- (void) removeObject: (id)o
|
|
{
|
|
[super removeObject: o];
|
|
if ([self count])
|
|
return;
|
|
|
|
// the entity is empty, so remove the entire thing
|
|
if (self == [map_i objectAtIndex: 0]) // unless it's the world...
|
|
return;
|
|
|
|
[map_i removeObject: self];
|
|
[self release];
|
|
}
|
|
|
|
- (const char *) valueForQKey: (const char *)k
|
|
{
|
|
epair_t *e;
|
|
|
|
for (e = epairs; e; e = e->next) {
|
|
if (!strcmp (k, e->key))
|
|
return e->value;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
- (void) getVector: (vec3_t)v
|
|
forKey: (const char *)k
|
|
{
|
|
const char *c;
|
|
|
|
c = [self valueForQKey: k];
|
|
v[0] = v[1] = v[2] = 0;
|
|
|
|
sscanf (c, "%f %f %f", &v[0], &v[1], &v[2]);
|
|
}
|
|
|
|
- (id) print
|
|
{
|
|
epair_t *e;
|
|
|
|
for (e = epairs; e; e = e->next)
|
|
printf ("%20s : %20s\n", e->key, e->value);
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) setKey: (const char *)k
|
|
toValue: (const char *)v
|
|
{
|
|
epair_t *e;
|
|
|
|
while (*k && *k <= ' ')
|
|
k++;
|
|
|
|
// don't set NULL values
|
|
if (!*k)
|
|
return;
|
|
|
|
for (e = epairs; e; e = e->next) {
|
|
if (!strcmp (k, e->key)) {
|
|
if (e->value == v)
|
|
return;
|
|
free (e->value);
|
|
e->value = strdup (v);
|
|
return;
|
|
}
|
|
}
|
|
|
|
e = malloc (sizeof (epair_t));
|
|
|
|
e->key = strdup (k);
|
|
e->value = strdup (v);
|
|
e->next = epairs;
|
|
epairs = e;
|
|
}
|
|
|
|
- (int) numPairs
|
|
{
|
|
int i;
|
|
epair_t *e;
|
|
|
|
i = 0;
|
|
for (e = epairs; e; e = e->next)
|
|
i++;
|
|
|
|
return i;
|
|
}
|
|
|
|
- (epair_t *) epairs
|
|
{
|
|
return epairs;
|
|
}
|
|
|
|
- (void) removeKeyPair: (char *)key
|
|
{
|
|
epair_t *e, *e2;
|
|
|
|
if (!epairs)
|
|
return;
|
|
|
|
e = epairs;
|
|
if (!strcmp (e->key, key)) {
|
|
epairs = e->next;
|
|
free (e);
|
|
return;
|
|
}
|
|
|
|
for ( ; e; e = e->next) {
|
|
if (e->next && !strcmp (e->next->key, key)) {
|
|
e2 = e->next;
|
|
e->next = e2->next;
|
|
free (e2);
|
|
return;
|
|
}
|
|
}
|
|
|
|
printf ("WARNING: removeKeyPair: %s not found\n", key);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
=============
|
|
targetname
|
|
|
|
If the entity does not have a "targetname" key, a unique one is generated
|
|
=============
|
|
*/
|
|
- (const char *) targetname
|
|
{
|
|
const char *t;
|
|
int i, count;
|
|
id ent;
|
|
int tval, maxt;
|
|
|
|
t = [self valueForQKey: "targetname"];
|
|
if (t && t[0])
|
|
return t;
|
|
|
|
// make a unique name of the form t<number>
|
|
count = [map_i count];
|
|
maxt = 0;
|
|
for (i = 1; i < count; i++) {
|
|
ent = [map_i objectAtIndex: i];
|
|
t = [ent valueForQKey: "targetname"];
|
|
if (!t || t[0] != 't')
|
|
continue;
|
|
tval = atoi (t + 1);
|
|
if (tval > maxt)
|
|
maxt = tval;
|
|
}
|
|
|
|
[self setKey: "targetname" toValue: va ("t%i", maxt + 1)];
|
|
|
|
return [self valueForQKey: "targetname"];
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
FILE METHODS
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
int nument;
|
|
|
|
- (Entity *) initFromScript: (script_t *)script
|
|
{
|
|
char *key;
|
|
id eclass, brush;
|
|
const char *spawn;
|
|
vec3_t emins, emaxs;
|
|
vec3_t org;
|
|
texturedef_t td;
|
|
esize_t esize;
|
|
int i, c;
|
|
float *color;
|
|
|
|
self = [super init];
|
|
array = [[NSMutableArray alloc] init];
|
|
|
|
if (!Script_GetToken (script, true)) {
|
|
[self dealloc];
|
|
return nil;
|
|
}
|
|
|
|
if (strcmp (Script_Token (script), "{"))
|
|
Sys_Error ("initFromScript: { not found");
|
|
|
|
do {
|
|
if (!Script_GetToken (script, true))
|
|
break;
|
|
if (!strcmp (Script_Token (script), "}"))
|
|
break;
|
|
if (!strcmp (Script_Token (script), "{")) {
|
|
// read a brush
|
|
brush = [[SetBrush alloc] initFromScript: script owner: self];
|
|
[self addObject: brush];
|
|
} else {
|
|
// read a key / value pair
|
|
key = strdup (Script_Token (script));
|
|
Script_GetToken (script, false);
|
|
[self setKey: key toValue: Script_Token (script)];
|
|
free (key);
|
|
}
|
|
} while (1);
|
|
|
|
nument++;
|
|
|
|
// get class
|
|
spawn = [self valueForQKey: "classname"];
|
|
eclass = [entity_classes_i classForName: spawn];
|
|
|
|
esize = [eclass esize];
|
|
|
|
[self getVector: org forKey: "origin"];
|
|
|
|
if ([self count] && esize != esize_model) {
|
|
printf ("WARNING:Entity with brushes and wrong model type\n");
|
|
[self removeAllObjects];
|
|
}
|
|
|
|
if (![self count] && esize == esize_model) {
|
|
printf ("WARNING:Entity with no brushes and esize_model: %s\n",
|
|
[self valueForQKey: "classname"]);
|
|
[texturepalette_i getTextureDef: &td];
|
|
for (i = 0; i < 3; i++) {
|
|
emins[i] = org[i] - 8;
|
|
emaxs[i] = org[i] + 8;
|
|
}
|
|
brush =
|
|
[[SetBrush alloc] initOwner: self mins: emins maxs: emaxs texture: &td];
|
|
[self addObject: brush];
|
|
}
|
|
|
|
// create a brush if needed
|
|
if (esize == esize_fixed)
|
|
[self createFixedBrush: org];
|
|
else
|
|
modifiable = YES;
|
|
|
|
// set all the brush colors
|
|
color = [eclass drawColor];
|
|
|
|
c = [self count];
|
|
for (i = 0; i < c; i++) {
|
|
brush = [self objectAtIndex: i];
|
|
[brush setEntityColor: color];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) writeToFILE: (FILE *)f
|
|
region: (BOOL)reg;
|
|
{
|
|
epair_t *e;
|
|
int ang;
|
|
unsigned int i;
|
|
id new;
|
|
vec3_t mins, maxs;
|
|
int org[3];
|
|
const vec_t *v;
|
|
char *oldang = 0;
|
|
|
|
if (reg) {
|
|
if (!strcmp ([self valueForQKey: "classname"], "info_player_start")) {
|
|
// move the playerstart temporarily to the camera position
|
|
oldang = strdup ([self valueForQKey: "angle"]);
|
|
ang = (int) ([cameraview_i yawAngle] * 180 / M_PI);
|
|
[self setKey: "angle" toValue: va ("%i", ang)];
|
|
} else if (self != [map_i objectAtIndex: 0]
|
|
&& [[self objectAtIndex: 0] regioned]) {
|
|
return; // skip the entire entity definition
|
|
}
|
|
}
|
|
|
|
fprintf (f, "{\n");
|
|
|
|
// set an origin epair
|
|
if (!modifiable) {
|
|
[[self objectAtIndex: 0] getMins: mins maxs: maxs];
|
|
if (oldang) {
|
|
[cameraview_i getOrigin: mins];
|
|
mins[0] -= 16;
|
|
mins[1] -= 16;
|
|
mins[2] -= 48;
|
|
}
|
|
new = [entity_classes_i classForName:
|
|
[self valueForQKey: "classname"]];
|
|
if (new)
|
|
v = [new mins];
|
|
else
|
|
v = vec3_origin;
|
|
|
|
VectorSubtract (mins, v, org);
|
|
[self setKey: "origin"
|
|
toValue: va ("%i %i %i", org[0], org[1], org[2])];
|
|
}
|
|
|
|
for (e = epairs; e; e = e->next)
|
|
fprintf (f, "\"%s\"\t\"%s\"\n", e->key, e->value);
|
|
|
|
// fixed size entities don't save out brushes
|
|
if (modifiable) {
|
|
for (i = 0; i < [self count]; i++)
|
|
[[self objectAtIndex: i] writeToFILE: f region: reg];
|
|
}
|
|
|
|
fprintf (f, "}\n");
|
|
|
|
if (oldang) {
|
|
[self setKey: "angle" toValue: oldang];
|
|
free (oldang);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
INTERACTION
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
@end
|