/* Copyright (C) 1997-2001 Id Software, Inc. Copyright (C) 2000-2002 Mr. Hyde and Mad Dog This program 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. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "g_local.h" #define MAX_MIRRORS 16 edict_t *g_mirror[MAX_MIRRORS]; #define SF_REFLECT_OFF 1 #define SF_REFLECT_TOGGLE 2 void ReflectExplosion (int type, vec3_t origin) { int m; edict_t *mirror; vec3_t org; if(!level.num_reflectors) return; for(m=0; minuse) continue; if(mirror->spawnflags & SF_REFLECT_OFF) continue; // Don't reflect explosions (other than BFG) from floor or ceiling, // 'cuz there's no way to do it right if((mirror->style <= 1) && (type != TE_BFG_EXPLOSION) && (type != TE_BFG_BIGEXPLOSION)) continue; VectorCopy(origin,org); switch(mirror->style) { case 0: org[2] = 2*mirror->absmax[2] - origin[2] + mirror->moveinfo.distance - 2; break; case 1: org[2] = 2*mirror->absmin[2] - origin[2] - mirror->moveinfo.distance + 2; break; case 2: org[0] = 2*mirror->absmin[0] - origin[0] + mirror->moveinfo.distance + 2; break; case 3: org[0] = 2*mirror->absmax[0] - origin[0] - mirror->moveinfo.distance - 2; break; case 4: org[1] = 2*mirror->absmin[1] - origin[1] + mirror->moveinfo.distance + 2; break; case 5: org[1] = 2*mirror->absmax[1] - origin[1] - mirror->moveinfo.distance - 2; break; } if(org[0] < mirror->absmin[0]) continue; if(org[0] > mirror->absmax[0]) continue; if(org[1] < mirror->absmin[1]) continue; if(org[1] > mirror->absmax[1]) continue; if(org[2] < mirror->absmin[2]) continue; if(org[2] > mirror->absmax[2]) continue; gi.WriteByte (svc_temp_entity); gi.WriteByte (type); gi.WritePosition (org); gi.multicast (org, MULTICAST_PVS); } } void ReflectTrail (int type, vec3_t start, vec3_t end) { int m; edict_t *mirror; vec3_t p1, p2; if(!level.num_reflectors) return; for(m=0; minuse) continue; if(mirror->spawnflags & SF_REFLECT_OFF) continue; VectorCopy(start,p1); VectorCopy(end, p2); switch(mirror->style) { case 0: p1[2] = 2*mirror->absmax[2] - start[2] + mirror->moveinfo.distance + 2; p2[2] = 2*mirror->absmax[2] - end[2] + mirror->moveinfo.distance + 2; break; case 1: p1[2] = 2*mirror->absmin[2] - start[2] - mirror->moveinfo.distance - 2; p2[2] = 2*mirror->absmin[2] - end[2] - mirror->moveinfo.distance - 2; break; case 2: p1[0] = 2*mirror->absmin[0] - start[0] + mirror->moveinfo.distance + 2; p2[0] = 2*mirror->absmin[0] - end[0] + mirror->moveinfo.distance + 2; break; case 3: p1[0] = 2*mirror->absmax[0] - start[0] - mirror->moveinfo.distance - 2; p2[0] = 2*mirror->absmax[0] - end[0] - mirror->moveinfo.distance - 2; break; case 4: p1[1] = 2*mirror->absmin[1] - start[1] + mirror->moveinfo.distance + 2; p2[1] = 2*mirror->absmin[1] - end[1] + mirror->moveinfo.distance + 2; break; case 5: p1[1] = 2*mirror->absmax[1] - start[1] - mirror->moveinfo.distance - 2; p2[1] = 2*mirror->absmax[1] - end[1] - mirror->moveinfo.distance - 2; break; } if(p1[0] < mirror->absmin[0]) continue; if(p1[0] > mirror->absmax[0]) continue; if(p1[1] < mirror->absmin[1]) continue; if(p1[1] > mirror->absmax[1]) continue; if(p1[2] < mirror->absmin[2]) continue; if(p1[2] > mirror->absmax[2]) continue; // If p1 is within func_reflect, we assume p2 is also. If map is constructed // properly this should always be true. gi.WriteByte (svc_temp_entity); gi.WriteByte (type); gi.WritePosition (p1); gi.WritePosition (p2); gi.multicast (p1, MULTICAST_PVS); } } void ReflectSteam (vec3_t origin,vec3_t movedir,int count,int sounds,int speed, int wait, int nextid) { int m; edict_t *mirror; vec3_t org, dir; if(!level.num_reflectors) return; for(m=0; minuse) continue; if(mirror->spawnflags & SF_REFLECT_OFF) continue; VectorCopy(origin,org); VectorCopy(movedir,dir); switch(mirror->style) { case 0: org[2] = 2*mirror->absmax[2] - origin[2] + mirror->moveinfo.distance + 2; dir[2] = -dir[2]; break; case 1: org[2] = 2*mirror->absmin[2] - origin[2] - mirror->moveinfo.distance - 2; dir[2] = -dir[2]; break; case 2: org[0] = 2*mirror->absmin[0] - origin[0] + mirror->moveinfo.distance + 2; dir[0] = -dir[0]; break; case 3: org[0] = 2*mirror->absmax[0] - origin[0] - mirror->moveinfo.distance - 2; dir[0] = -dir[0]; break; case 4: org[1] = 2*mirror->absmin[1] - origin[1] + mirror->moveinfo.distance + 2; dir[1] = -dir[1]; break; case 5: org[1] = 2*mirror->absmax[1] - origin[1] - mirror->moveinfo.distance - 2; dir[1] = -dir[1]; break; } if(org[0] < mirror->absmin[0]) continue; if(org[0] > mirror->absmax[0]) continue; if(org[1] < mirror->absmin[1]) continue; if(org[1] > mirror->absmax[1]) continue; if(org[2] < mirror->absmin[2]) continue; if(org[2] > mirror->absmax[2]) continue; // If p1 is within func_reflect, we assume p2 is also. If map is constructed // properly this should always be true. gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_STEAM); gi.WriteShort (nextid); gi.WriteByte (count); gi.WritePosition (org); gi.WriteDir (dir); gi.WriteByte (sounds&0xff); gi.WriteShort (speed); gi.WriteLong (wait); gi.multicast (org, MULTICAST_PVS); } } void ReflectSparks (int type,vec3_t origin,vec3_t movedir) { int m; edict_t *mirror; vec3_t org, dir; if(!level.num_reflectors) return; for(m=0; minuse) continue; if(mirror->spawnflags & SF_REFLECT_OFF) continue; // Don't reflect in floor or ceiling because sparks are affected by // gravity if(mirror->style <= 1) continue; VectorCopy(origin,org); VectorCopy(movedir,dir); switch(mirror->style) { case 0: org[2] = 2*mirror->absmax[2] - origin[2] + mirror->moveinfo.distance + 2; dir[2] = -dir[2]; break; case 1: org[2] = 2*mirror->absmin[2] - origin[2] - mirror->moveinfo.distance - 2; dir[2] = -dir[2]; break; case 2: org[0] = 2*mirror->absmin[0] - origin[0] + mirror->moveinfo.distance + 2; dir[0] = -dir[0]; break; case 3: org[0] = 2*mirror->absmax[0] - origin[0] - mirror->moveinfo.distance - 2; dir[0] = -dir[0]; break; case 4: org[1] = 2*mirror->absmin[1] - origin[1] + mirror->moveinfo.distance + 2; dir[1] = -dir[1]; break; case 5: org[1] = 2*mirror->absmax[1] - origin[1] - mirror->moveinfo.distance - 2; dir[1] = -dir[1]; break; } if(org[0] < mirror->absmin[0]) continue; if(org[0] > mirror->absmax[0]) continue; if(org[1] < mirror->absmin[1]) continue; if(org[1] > mirror->absmax[1]) continue; if(org[2] < mirror->absmin[2]) continue; if(org[2] > mirror->absmax[2]) continue; gi.WriteByte(svc_temp_entity); gi.WriteByte(type); gi.WritePosition(org); if(type != TE_CHAINFIST_SMOKE) gi.WriteDir(dir); gi.multicast(org, MULTICAST_PVS); } } void DeleteReflection (edict_t *ent, int index) { edict_t *r; if(index < 0) { int i; for(i=0; i<6; i++) { r = ent->reflection[i]; if(r) { if(r->client) gi.TagFree(r->client); memset (r, 0, sizeof(*r)); r->classname = "freed"; r->freetime = level.time; r->inuse = false; } ent->reflection[i] = NULL; } } else { r = ent->reflection[index]; if(r) { if(r->client) gi.TagFree(r->client); memset (r, 0, sizeof(*r)); r->classname = "freed"; r->freetime = level.time; r->inuse = false; ent->reflection[index] = NULL; } } } void AddReflection (edict_t *ent) { gclient_t *cl; edict_t *mirror; float roll; int i, m; qboolean is_reflected; vec3_t forward; vec3_t org; for(i=0; i<6; i++) { is_reflected = false; for(m=0; minuse) continue; if(mirror->spawnflags & SF_REFLECT_OFF) continue; if(mirror->style != i) continue; VectorCopy(ent->s.origin,org); switch(i) { case 0: org[2] = 2*mirror->absmax[2] - ent->s.origin[2] - mirror->moveinfo.distance - 2; break; case 1: org[2] = 2*mirror->absmin[2] - ent->s.origin[2] + mirror->moveinfo.distance + 2; break; case 2: org[0] = 2*mirror->absmin[0] - ent->s.origin[0] + mirror->moveinfo.distance + 2; break; case 3: org[0] = 2*mirror->absmax[0] - ent->s.origin[0] - mirror->moveinfo.distance - 2; break; case 4: org[1] = 2*mirror->absmin[1] - ent->s.origin[1] + mirror->moveinfo.distance + 2; break; case 5: org[1] = 2*mirror->absmax[1] - ent->s.origin[1] - mirror->moveinfo.distance - 2; break; } if(org[0] < mirror->absmin[0]) continue; if(org[0] > mirror->absmax[0]) continue; if(org[1] < mirror->absmin[1]) continue; if(org[1] > mirror->absmax[1]) continue; if(org[2] < mirror->absmin[2]) continue; if(org[2] > mirror->absmax[2]) continue; is_reflected = true; } if(is_reflected) { if (!ent->reflection[i]) { ent->reflection[i] = G_Spawn(); if(ent->s.effects & EF_ROTATE) { ent->s.effects &= ~EF_ROTATE; gi.linkentity(ent); } ent->reflection[i]->movetype = MOVETYPE_NONE; ent->reflection[i]->solid = SOLID_NOT; ent->reflection[i]->classname = "reflection"; ent->reflection[i]->flags = FL_REFLECT; ent->reflection[i]->takedamage = DAMAGE_NO; } if (ent->client && !ent->reflection[i]->client) { cl = (gclient_t *)gi.TagMalloc(sizeof(gclient_t), TAG_LEVEL); ent->reflection[i]->client = cl; } if (ent->client && ent->reflection[i]->client) { // Lazarus: Hmm.. this crashes when loading saved game. // Not sure what use pers is anyhow? // ent->reflection[i]->client->pers = ent->client->pers; ent->reflection[i]->s = ent->s; } ent->reflection[i]->s.number = ent->reflection[i] - g_edicts; ent->reflection[i]->s.modelindex = ent->s.modelindex; ent->reflection[i]->s.modelindex2 = ent->s.modelindex2; ent->reflection[i]->s.modelindex3 = ent->s.modelindex3; ent->reflection[i]->s.modelindex4 = ent->s.modelindex4; #ifdef KMQUAKE2_ENGINE_MOD ent->reflection[i]->s.modelindex5 = ent->s.modelindex5; ent->reflection[i]->s.modelindex6 = ent->s.modelindex6; ent->reflection[i]->s.alpha = ent->s.alpha; #endif ent->reflection[i]->s.skinnum = ent->s.skinnum; ent->reflection[i]->s.frame = ent->s.frame; ent->reflection[i]->s.effects = ent->s.effects; ent->reflection[i]->s.renderfx = ent->s.renderfx; ent->reflection[i]->s.renderfx &= ~RF_IR_VISIBLE; #ifdef KMQUAKE2_ENGINE_MOD // Don't flip if left handed player model if ( !(ent->s.modelindex == (MAX_MODELS-1) && hand->value == 1) ) ent->reflection[i]->s.renderfx |= RF_MIRRORMODEL; // Knightmare added- flip reflected models #endif VectorCopy (ent->s.angles, ent->reflection[i]->s.angles); switch(i) { case 0: case 1: ent->reflection[i]->s.angles[0]+=180; ent->reflection[i]->s.angles[1]+=180; ent->reflection[i]->s.angles[2]=360-ent->reflection[i]->s.angles[2]; break; case 2: case 3: AngleVectors(ent->reflection[i]->s.angles,forward,NULL,NULL); roll = ent->reflection[i]->s.angles[2]; forward[0] = -forward[0]; vectoangles(forward,ent->reflection[i]->s.angles); ent->reflection[i]->s.angles[2] = 360-roll; break; case 4: case 5: AngleVectors(ent->reflection[i]->s.angles,forward,NULL,NULL); roll = ent->reflection[i]->s.angles[2]; forward[1] = -forward[1]; vectoangles(forward,ent->reflection[i]->s.angles); ent->reflection[i]->s.angles[2] = 360-roll; } VectorCopy (org, ent->reflection[i]->s.origin); if(ent->s.renderfx & RF_BEAM) { vec3_t delta; VectorSubtract(ent->reflection[i]->s.origin,ent->s.origin,delta); VectorAdd(ent->s.old_origin,delta,ent->reflection[i]->s.old_origin); } else VectorCopy(ent->reflection[i]->s.origin, ent->reflection[i]->s.old_origin); gi.linkentity (ent->reflection[i]); } else if (ent->reflection[i]) { DeleteReflection(ent,i); } } } void use_func_reflect (edict_t *self, edict_t *other, edict_t *activator) { if (self->spawnflags & SF_REFLECT_OFF) { // was off, turn it on self->spawnflags &= ~SF_REFLECT_OFF; } else { // was on, turn it off self->spawnflags |= SF_REFLECT_OFF; } if (!(self->spawnflags & SF_REFLECT_TOGGLE)) self->use = NULL; } void SP_func_reflect (edict_t *self) { if(level.num_reflectors >= MAX_MIRRORS) { gi.dprintf("Max number of func_reflect's (%d) exceeded.\n",MAX_MIRRORS); G_FreeEdict(self); return; } self->class_id = ENTITY_FUNC_REFLECT; gi.setmodel (self, self->model); self->svflags = SVF_NOCLIENT; g_mirror[level.num_reflectors] = self; if(!st.lip) st.lip = 2; self->moveinfo.distance = st.lip; self->use = use_func_reflect; level.num_reflectors++; }