/*
Copyright (C) 1997-2001 Id Software, Inc.
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.
*/
/* Modifications
Copyright 2003-2004 Bytonic Software
Copyright 2010 Google Inc.
*/
package com.googlecode.gwtquake.shared.client;
import com.googlecode.gwtquake.shared.common.AsyncCallback;
import com.googlecode.gwtquake.shared.common.Buffer;
import com.googlecode.gwtquake.shared.common.Buffers;
import com.googlecode.gwtquake.shared.common.Com;
import com.googlecode.gwtquake.shared.common.Constants;
import com.googlecode.gwtquake.shared.common.Globals;
import com.googlecode.gwtquake.shared.game.PlayerState;
import com.googlecode.gwtquake.shared.render.Model;
import com.googlecode.gwtquake.shared.sound.Sfx;
import com.googlecode.gwtquake.shared.sound.Sound;
import com.googlecode.gwtquake.shared.util.Lib;
import com.googlecode.gwtquake.shared.util.Math3D;
/**
* CL_tent
*/
public class ClientTent {
static class ExplosionData {
int type;
EntityType ent = new EntityType();
int frames;
float light;
float[] lightcolor = new float[3];
float start;
int baseframe;
void clear() {
lightcolor[0] = lightcolor[1] = lightcolor[2] = light = start = type = frames = baseframe = 0;
ent.clear();
}
}
static final int MAX_EXPLOSIONS = 32;
static ExplosionData[] cl_explosions = new ExplosionData[MAX_EXPLOSIONS];
static final int MAX_BEAMS = 32;
static beam_t[] cl_beams = new beam_t[MAX_BEAMS];
// PMM - added this for player-linked beams. Currently only used by the
// plasma beam
static beam_t[] cl_playerbeams = new beam_t[MAX_BEAMS];
static final int MAX_LASERS = 32;
static laser_t[] cl_lasers = new laser_t[MAX_LASERS];
// ROGUE
static final int MAX_SUSTAINS = 32;
static ClientSustain[] cl_sustains = new ClientSustain[MAX_SUSTAINS];
static class beam_t {
int entity;
int dest_entity;
Model model;
int endtime;
float[] offset = new float[3];
float[] start = new float[3];
float[] end = new float[3];
void clear() {
offset[0] = offset[1] = offset[2] = start[0] = start[1] = start[2] = end[0] = end[1] = end[2] = entity = dest_entity = endtime = 0;
model = null;
}
}
static {
for (int i = 0; i < cl_explosions.length; i++)
cl_explosions[i] = new ExplosionData();
}
static {
for (int i = 0; i < cl_beams.length; i++)
cl_beams[i] = new beam_t();
for (int i = 0; i < cl_playerbeams.length; i++)
cl_playerbeams[i] = new beam_t();
}
static class laser_t {
EntityType ent = new EntityType();
int endtime;
void clear() {
endtime = 0;
ent.clear();
}
}
static {
for (int i = 0; i < cl_lasers.length; i++)
cl_lasers[i] = new laser_t();
}
static {
for (int i = 0; i < cl_sustains.length; i++)
cl_sustains[i] = new ClientSustain();
}
static final int ex_free = 0;
static final int ex_explosion = 1;
static final int ex_misc = 2;
static final int ex_flash = 3;
static final int ex_mflash = 4;
static final int ex_poly = 5;
static final int ex_poly2 = 6;
// ROGUE
// all are references;
static Sfx cl_sfx_ric1;
static Sfx cl_sfx_ric2;
static Sfx cl_sfx_ric3;
static Sfx cl_sfx_lashit;
static Sfx cl_sfx_spark5;
static Sfx cl_sfx_spark6;
static Sfx cl_sfx_spark7;
static Sfx cl_sfx_railg;
static Sfx cl_sfx_rockexp;
static Sfx cl_sfx_grenexp;
static Sfx cl_sfx_watrexp;
// RAFAEL
static Sfx cl_sfx_plasexp;
static Sfx cl_sfx_footsteps[] = new Sfx[4];
static Model cl_mod_explode;
static Model cl_mod_smoke;
static Model cl_mod_flash;
static Model cl_mod_parasite_segment;
static Model cl_mod_grapple_cable;
static Model cl_mod_parasite_tip;
static Model cl_mod_explo4;
static Model cl_mod_bfg_explo;
static Model cl_mod_powerscreen;
// RAFAEL
static Model cl_mod_plasmaexplo;
// ROGUE
static Sfx cl_sfx_lightning;
static Sfx cl_sfx_disrexp;
static Model cl_mod_lightning;
static Model cl_mod_heatbeam;
static Model cl_mod_monster_heatbeam;
static Model cl_mod_explo4_big;
// ROGUE
/*
* ================= CL_RegisterTEntSounds =================
*/
static void RegisterTEntSounds() {
int i;
String name;
// PMM - version stuff
// Com_Printf ("%s\n", ROGUE_VERSION_STRING);
// PMM
cl_sfx_ric1 = Sound.RegisterSound("world/ric1.wav");
cl_sfx_ric2 = Sound.RegisterSound("world/ric2.wav");
cl_sfx_ric3 = Sound.RegisterSound("world/ric3.wav");
cl_sfx_lashit = Sound.RegisterSound("weapons/lashit.wav");
cl_sfx_spark5 = Sound.RegisterSound("world/spark5.wav");
cl_sfx_spark6 = Sound.RegisterSound("world/spark6.wav");
cl_sfx_spark7 = Sound.RegisterSound("world/spark7.wav");
cl_sfx_railg = Sound.RegisterSound("weapons/railgf1a.wav");
cl_sfx_rockexp = Sound.RegisterSound("weapons/rocklx1a.wav");
cl_sfx_grenexp = Sound.RegisterSound("weapons/grenlx1a.wav");
cl_sfx_watrexp = Sound.RegisterSound("weapons/xpld_wat.wav");
// RAFAEL
// cl_sfx_plasexp = S.RegisterSound ("weapons/plasexpl.wav");
Sound.RegisterSound("player/land1.wav");
Sound.RegisterSound("player/fall2.wav");
Sound.RegisterSound("player/fall1.wav");
for (i = 0; i < 4; i++) {
//Com_sprintf (name, sizeof(name), "player/step%i.wav", i+1);
name = "player/step" + (i + 1) + ".wav";
cl_sfx_footsteps[i] = Sound.RegisterSound(name);
}
// PGM
cl_sfx_lightning = Sound.RegisterSound("weapons/tesla.wav");
cl_sfx_disrexp = Sound.RegisterSound("weapons/disrupthit.wav");
// version stuff
// sprintf (name, "weapons/sound%d.wav", ROGUE_VERSION_ID);
// if (name[0] == 'w')
// name[0] = 'W';
// PGM
}
/*
* ================= CL_RegisterTEntModels =================
*/
private abstract static class LoadCallback implements AsyncCallback<Model> {
public void onFailure(Throwable e) {
System.err.println("Unable to load model: " + e.getMessage());
}
}
static void RegisterTEntModels() {
// TODO(jgw): There's gotta be a simpler way to do this.
Globals.re.RegisterModel("models/objects/explode/tris.md2", new LoadCallback() {
public void onSuccess(Model response) {
cl_mod_explode = response;
}
});
Globals.re.RegisterModel("models/objects/smoke/tris.md2", new LoadCallback() {
public void onSuccess(Model response) {
cl_mod_smoke = response;
}
});
Globals.re.RegisterModel("models/objects/flash/tris.md2", new LoadCallback() {
public void onSuccess(Model response) {
cl_mod_flash = response;
}
});
Globals.re.RegisterModel("models/monsters/parasite/segment/tris.md2", new LoadCallback() {
public void onSuccess(Model response) {
cl_mod_parasite_segment = response;
}
});
Globals.re.RegisterModel("models/ctf/segment/tris.md2", new LoadCallback() {
public void onSuccess(Model response) {
cl_mod_grapple_cable = response;
}
});
Globals.re.RegisterModel("models/monsters/parasite/tip/tris.md2", new LoadCallback() {
public void onSuccess(Model response) {
cl_mod_parasite_tip = response;
}
});
Globals.re.RegisterModel("models/objects/r_explode/tris.md2", new LoadCallback() {
public void onSuccess(Model response) {
cl_mod_explo4 = response;
}
});
Globals.re.RegisterModel("sprites/s_bfg2.sp2", new LoadCallback() {
public void onSuccess(Model response) {
cl_mod_bfg_explo = response;
}
});
Globals.re.RegisterModel("models/items/armor/effect/tris.md2", new LoadCallback() {
public void onSuccess(Model response) {
cl_mod_powerscreen = response;
}
});
Globals.re.RegisterModel("models/objects/r_explode2/tris.md2", new LoadCallback() {
public void onSuccess(Model response) {
cl_mod_explo4_big = response;
}
});
Globals.re.RegisterModel("models/proj/lightning/tris.md2", new LoadCallback() {
public void onSuccess(Model response) {
cl_mod_lightning = response;
}
});
Globals.re.RegisterModel("models/proj/beam/tris.md2", new LoadCallback() {
public void onSuccess(Model response) {
cl_mod_heatbeam = response;
}
});
Globals.re.RegisterModel("models/proj/widowbeam/tris.md2", new LoadCallback() {
public void onSuccess(Model response) {
cl_mod_monster_heatbeam = response;
}
});
Globals.re.RegisterModel("models/objects/laser/tris.md2", null);
Globals.re.RegisterModel("models/objects/grenade2/tris.md2", null);
Globals.re.RegisterModel("models/weapons/v_machn/tris.md2", null);
Globals.re.RegisterModel("models/weapons/v_handgr/tris.md2", null);
Globals.re.RegisterModel("models/weapons/v_shotg2/tris.md2", null);
Globals.re.RegisterModel("models/objects/gibs/bone/tris.md2", null);
Globals.re.RegisterModel("models/objects/gibs/sm_meat/tris.md2", null);
Globals.re.RegisterModel("models/objects/gibs/bone2/tris.md2", null);
// RAFAEL
// re.RegisterModel ("models/objects/blaser/tris.md2");
Globals.re.RegisterPic("w_machinegun");
Globals.re.RegisterPic("a_bullets");
Globals.re.RegisterPic("i_health");
Globals.re.RegisterPic("a_grenades");
}
/*
* ================= CL_ClearTEnts =================
*/
static void ClearTEnts() {
// memset (cl_beams, 0, sizeof(cl_beams));
for (int i = 0; i < cl_beams.length; i++)
cl_beams[i].clear();
// memset (cl_explosions, 0, sizeof(cl_explosions));
for (int i = 0; i < cl_explosions.length; i++)
cl_explosions[i].clear();
// memset (cl_lasers, 0, sizeof(cl_lasers));
for (int i = 0; i < cl_lasers.length; i++)
cl_lasers[i].clear();
//
// ROGUE
// memset (cl_playerbeams, 0, sizeof(cl_playerbeams));
for (int i = 0; i < cl_playerbeams.length; i++)
cl_playerbeams[i].clear();
// memset (cl_sustains, 0, sizeof(cl_sustains));
for (int i = 0; i < cl_sustains.length; i++)
cl_sustains[i].clear();
// ROGUE
}
/*
* ================= CL_AllocExplosion =================
*/
static ExplosionData AllocExplosion() {
int i;
int time;
int index;
for (i = 0; i < MAX_EXPLOSIONS; i++) {
if (cl_explosions[i].type == ex_free) {
//memset (&cl_explosions[i], 0, sizeof (cl_explosions[i]));
cl_explosions[i].clear();
return cl_explosions[i];
}
}
// find the oldest explosion
time = Globals.cl.time;
index = 0;
for (i = 0; i < MAX_EXPLOSIONS; i++)
if (cl_explosions[i].start < time) {
time = (int) cl_explosions[i].start;
index = i;
}
//memset (&cl_explosions[index], 0, sizeof (cl_explosions[index]));
cl_explosions[index].clear();
return cl_explosions[index];
}
/*
* ================= CL_SmokeAndFlash =================
*/
static void SmokeAndFlash(float[] origin) {
ExplosionData ex;
ex = AllocExplosion();
Math3D.VectorCopy(origin, ex.ent.origin);
ex.type = ex_misc;
ex.frames = 4;
ex.ent.flags = Constants.RF_TRANSLUCENT;
ex.start = Globals.cl.frame.servertime - 100;
ex.ent.model = cl_mod_smoke;
ex = AllocExplosion();
Math3D.VectorCopy(origin, ex.ent.origin);
ex.type = ex_flash;
ex.ent.flags = Constants.RF_FULLBRIGHT;
ex.frames = 2;
ex.start = Globals.cl.frame.servertime - 100;
ex.ent.model = cl_mod_flash;
}
/*
* =================
* CL_ParseBeam
* =================
*/
static int ParseBeam(Model model) {
int ent;
float[] start = new float[3];
float[] end = new float[3];
beam_t[] b;
int i;
ent = Globals.net_message.getShort();
Buffers.getPos(Globals.net_message, start);
Buffers.getPos(Globals.net_message, end);
// override any beam with the same entity
b = cl_beams;
for (i = 0; i < MAX_BEAMS; i++)
if (b[i].entity == ent) {
b[i].entity = ent;
b[i].model = model;
b[i].endtime = Globals.cl.time + 200;
Math3D.VectorCopy(start, b[i].start);
Math3D.VectorCopy(end, b[i].end);
Math3D.VectorClear(b[i].offset);
return ent;
}
// find a free beam
b = cl_beams;
for (i = 0; i < MAX_BEAMS; i++) {
if (b[i].model == null || b[i].endtime < Globals.cl.time) {
b[i].entity = ent;
b[i].model = model;
b[i].endtime = Globals.cl.time + 200;
Math3D.VectorCopy(start, b[i].start);
Math3D.VectorCopy(end, b[i].end);
Math3D.VectorClear(b[i].offset);
return ent;
}
}
Com.Printf("beam list overflow!\n");
return ent;
}
/*
* ================= CL_ParseBeam2 =================
*/
static int ParseBeam2(Model model) {
int ent;
float[] start = new float[3];
float[] end = new float[3];
float[] offset = new float[3];
beam_t[] b;
int i;
ent = Globals.net_message.getShort();
Buffers.getPos(Globals.net_message, start);
Buffers.getPos(Globals.net_message, end);
Buffers.getPos(Globals.net_message, offset);
// Com_Printf ("end- %f %f %f\n", end[0], end[1], end[2]);
// override any beam with the same entity
b = cl_beams;
for (i = 0; i < MAX_BEAMS; i++)
if (b[i].entity == ent) {
b[i].entity = ent;
b[i].model = model;
b[i].endtime = Globals.cl.time + 200;
Math3D.VectorCopy(start, b[i].start);
Math3D.VectorCopy(end, b[i].end);
Math3D.VectorCopy(offset, b[i].offset);
return ent;
}
// find a free beam
b = cl_beams;
for (i = 0; i < MAX_BEAMS; i++) {
if (b[i].model == null || b[i].endtime < Globals.cl.time) {
b[i].entity = ent;
b[i].model = model;
b[i].endtime = Globals.cl.time + 200;
Math3D.VectorCopy(start, b[i].start);
Math3D.VectorCopy(end, b[i].end);
Math3D.VectorCopy(offset, b[i].offset);
return ent;
}
}
Com.Printf("beam list overflow!\n");
return ent;
}
// ROGUE
/*
* ================= CL_ParsePlayerBeam - adds to the cl_playerbeam array
* instead of the cl_beams array =================
*/
static int ParsePlayerBeam(Model model) {
int ent;
float[] start = new float[3];
float[] end = new float[3];
float[] offset = new float[3];
beam_t[] b;
int i;
ent = Globals.net_message.getShort();
Buffers.getPos(Globals.net_message, start);
Buffers.getPos(Globals.net_message, end);
// PMM - network optimization
if (model == cl_mod_heatbeam)
Math3D.VectorSet(offset, 2, 7, -3);
else if (model == cl_mod_monster_heatbeam) {
model = cl_mod_heatbeam;
Math3D.VectorSet(offset, 0, 0, 0);
} else
Buffers.getPos(Globals.net_message, offset);
// Com_Printf ("end- %f %f %f\n", end[0], end[1], end[2]);
// override any beam with the same entity
// PMM - For player beams, we only want one per player (entity) so..
b = cl_playerbeams;
for (i = 0; i < MAX_BEAMS; i++) {
if (b[i].entity == ent) {
b[i].entity = ent;
b[i].model = model;
b[i].endtime = Globals.cl.time + 200;
Math3D.VectorCopy(start, b[i].start);
Math3D.VectorCopy(end, b[i].end);
Math3D.VectorCopy(offset, b[i].offset);
return ent;
}
}
// find a free beam
b = cl_playerbeams;
for (i = 0; i < MAX_BEAMS; i++) {
if (b[i].model == null || b[i].endtime < Globals.cl.time) {
b[i].entity = ent;
b[i].model = model;
b[i].endtime = Globals.cl.time + 100; // PMM - this needs to be
// 100 to prevent multiple
// heatbeams
Math3D.VectorCopy(start, b[i].start);
Math3D.VectorCopy(end, b[i].end);
Math3D.VectorCopy(offset, b[i].offset);
return ent;
}
}
Com.Printf("beam list overflow!\n");
return ent;
}
// rogue
// stack variable
private static final float[] start = new float[3];
private static final float[] end = new float[3];
/*
* ================= CL_ParseLightning =================
*/
static int ParseLightning(Model model) {
int srcEnt, destEnt;
beam_t[] b;
int i;
srcEnt = Globals.net_message.getShort();
destEnt = Globals.net_message.getShort();
Buffers.getPos(Globals.net_message, start);
Buffers.getPos(Globals.net_message, end);
// override any beam with the same source AND destination entities
b = cl_beams;
for (i = 0; i < MAX_BEAMS; i++)
if (b[i].entity == srcEnt && b[i].dest_entity == destEnt) {
// Com_Printf("%d: OVERRIDE %d . %d\n", cl.time, srcEnt,
// destEnt);
b[i].entity = srcEnt;
b[i].dest_entity = destEnt;
b[i].model = model;
b[i].endtime = Globals.cl.time + 200;
Math3D.VectorCopy(start, b[i].start);
Math3D.VectorCopy(end, b[i].end);
Math3D.VectorClear(b[i].offset);
return srcEnt;
}
// find a free beam
b = cl_beams;
for (i = 0; i < MAX_BEAMS; i++) {
if (b[i].model == null || b[i].endtime < Globals.cl.time) {
// Com_Printf("%d: NORMAL %d . %d\n", cl.time, srcEnt, destEnt);
b[i].entity = srcEnt;
b[i].dest_entity = destEnt;
b[i].model = model;
b[i].endtime = Globals.cl.time + 200;
Math3D.VectorCopy(start, b[i].start);
Math3D.VectorCopy(end, b[i].end);
Math3D.VectorClear(b[i].offset);
return srcEnt;
}
}
Com.Printf("beam list overflow!\n");
return srcEnt;
}
// stack variable
// start, end
/*
* ================= CL_ParseLaser =================
*/
static void ParseLaser(int colors) {
laser_t[] l;
int i;
Buffers.getPos(Globals.net_message, start);
Buffers.getPos(Globals.net_message, end);
l = cl_lasers;
for (i = 0; i < MAX_LASERS; i++) {
if (l[i].endtime < Globals.cl.time) {
l[i].ent.flags = Constants.RF_TRANSLUCENT | Constants.RF_BEAM;
Math3D.VectorCopy(start, l[i].ent.origin);
Math3D.VectorCopy(end, l[i].ent.oldorigin);
l[i].ent.alpha = 0.30f;
l[i].ent.skinnum = (colors >> ((Lib.rand() % 4) * 8)) & 0xff;
l[i].ent.model = null;
l[i].ent.frame = 4;
l[i].endtime = Globals.cl.time + 100;
return;
}
}
}
// stack variable
private static final float[] pos = new float[3];
private static final float[] dir = new float[3];
// =============
// ROGUE
static void ParseSteam() {
int id, i;
int r;
int cnt;
int color;
int magnitude;
ClientSustain[] s;
ClientSustain free_sustain;
id = Globals.net_message.getShort(); // an id of -1 is an instant
// effect
if (id != -1) // sustains
{
// Com_Printf ("Sustain effect id %d\n", id);
free_sustain = null;
s = cl_sustains;
for (i = 0; i < MAX_SUSTAINS; i++) {
if (s[i].id == 0) {
free_sustain = s[i];
break;
}
}
if (free_sustain != null) {
s[i].id = id;
s[i].count = Buffers.readUnsignedByte(Globals.net_message);
Buffers.getPos(Globals.net_message, s[i].org);
ClientTent.ReadDir(Globals.net_message, s[i].dir);
r = Buffers.readUnsignedByte(Globals.net_message);
s[i].color = r & 0xff;
s[i].magnitude = Globals.net_message.getShort();
s[i].endtime = Globals.cl.time
+ Globals.net_message.getInt();
s[i].think = new ClientSustain.ThinkAdapter() {
void think(ClientSustain self) {
ClientNewFx.ParticleSteamEffect2(self);
}
};
s[i].thinkinterval = 100;
s[i].nextthink = Globals.cl.time;
} else {
// Com_Printf ("No free sustains!\n");
// FIXME - read the stuff anyway
cnt = Buffers.readUnsignedByte(Globals.net_message);
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
r = Buffers.readUnsignedByte(Globals.net_message);
magnitude = Globals.net_message.getShort();
magnitude = Globals.net_message.getInt(); // really
// interval
}
} else // instant
{
cnt = Buffers.readUnsignedByte(Globals.net_message);
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
r = Buffers.readUnsignedByte(Globals.net_message);
magnitude = Globals.net_message.getShort();
color = r & 0xff;
ClientNewFx.ParticleSteamEffect(pos, dir, color, cnt, magnitude);
// S_StartSound (pos, 0, 0, cl_sfx_lashit, 1, ATTN_NORM, 0);
}
}
// stack variable
// pos
static void ParseWidow() {
int id, i;
ClientSustain[] s;
ClientSustain free_sustain;
id = Globals.net_message.getShort();
free_sustain = null;
s = cl_sustains;
for (i = 0; i < MAX_SUSTAINS; i++) {
if (s[i].id == 0) {
free_sustain = s[i];
break;
}
}
if (free_sustain != null) {
s[i].id = id;
Buffers.getPos(Globals.net_message, s[i].org);
s[i].endtime = Globals.cl.time + 2100;
s[i].think = new ClientSustain.ThinkAdapter() {
void think(ClientSustain self) {
ClientNewFx.Widowbeamout(self);
}
};
s[i].thinkinterval = 1;
s[i].nextthink = Globals.cl.time;
} else // no free sustains
{
// FIXME - read the stuff anyway
Buffers.getPos(Globals.net_message, pos);
}
}
// stack variable
// pos
static void ParseNuke() {
int i;
ClientSustain[] s;
ClientSustain free_sustain;
free_sustain = null;
s = cl_sustains;
for (i = 0; i < MAX_SUSTAINS; i++) {
if (s[i].id == 0) {
free_sustain = s[i];
break;
}
}
if (free_sustain != null) {
s[i].id = 21000;
Buffers.getPos(Globals.net_message, s[i].org);
s[i].endtime = Globals.cl.time + 1000;
s[i].think = new ClientSustain.ThinkAdapter() {
void think(ClientSustain self) {
ClientNewFx.Nukeblast(self);
}
};
s[i].thinkinterval = 1;
s[i].nextthink = Globals.cl.time;
} else // no free sustains
{
// FIXME - read the stuff anyway
Buffers.getPos(Globals.net_message, pos);
}
}
// ROGUE
// =============
/*
* ================= CL_ParseTEnt =================
*/
static int[] splash_color = { 0x00, 0xe0, 0xb0, 0x50, 0xd0, 0xe0, 0xe8 };
// stack variable
// pos, dir
private static final float[] pos2 = {0, 0, 0};
static void ParseTEnt() {
int type;
ExplosionData ex;
int cnt;
int color;
int r;
int ent;
int magnitude;
type = Buffers.readUnsignedByte(Globals.net_message);
switch (type) {
case Constants.TE_BLOOD: // bullet hitting flesh
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
ClientEffects.ParticleEffect(pos, dir, 0xe8, 60);
break;
case Constants.TE_GUNSHOT: // bullet hitting wall
case Constants.TE_SPARKS:
case Constants.TE_BULLET_SPARKS:
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
if (type == Constants.TE_GUNSHOT)
ClientEffects.ParticleEffect(pos, dir, 0, 40);
else
ClientEffects.ParticleEffect(pos, dir, 0xe0, 6);
if (type != Constants.TE_SPARKS) {
SmokeAndFlash(pos);
// impact sound
cnt = Lib.rand() & 15;
if (cnt == 1)
Sound.StartSound(pos, 0, 0, cl_sfx_ric1, 1, Constants.ATTN_NORM,
0);
else if (cnt == 2)
Sound.StartSound(pos, 0, 0, cl_sfx_ric2, 1, Constants.ATTN_NORM,
0);
else if (cnt == 3)
Sound.StartSound(pos, 0, 0, cl_sfx_ric3, 1, Constants.ATTN_NORM,
0);
}
break;
case Constants.TE_SCREEN_SPARKS:
case Constants.TE_SHIELD_SPARKS:
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
if (type == Constants.TE_SCREEN_SPARKS)
ClientEffects.ParticleEffect(pos, dir, 0xd0, 40);
else
ClientEffects.ParticleEffect(pos, dir, 0xb0, 40);
//FIXME : replace or remove this sound
Sound.StartSound(pos, 0, 0, cl_sfx_lashit, 1, Constants.ATTN_NORM, 0);
break;
case Constants.TE_SHOTGUN: // bullet hitting wall
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
ClientEffects.ParticleEffect(pos, dir, 0, 20);
SmokeAndFlash(pos);
break;
case Constants.TE_SPLASH: // bullet hitting water
cnt = Buffers.readUnsignedByte(Globals.net_message);
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
r = Buffers.readUnsignedByte(Globals.net_message);
if (r > 6)
color = 0x00;
else
color = splash_color[r];
ClientEffects.ParticleEffect(pos, dir, color, cnt);
if (r == Constants.SPLASH_SPARKS) {
r = Lib.rand() & 3;
if (r == 0)
Sound.StartSound(pos, 0, 0, cl_sfx_spark5, 1,
Constants.ATTN_STATIC, 0);
else if (r == 1)
Sound.StartSound(pos, 0, 0, cl_sfx_spark6, 1,
Constants.ATTN_STATIC, 0);
else
Sound.StartSound(pos, 0, 0, cl_sfx_spark7, 1,
Constants.ATTN_STATIC, 0);
}
break;
case Constants.TE_LASER_SPARKS:
cnt = Buffers.readUnsignedByte(Globals.net_message);
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
color = Buffers.readUnsignedByte(Globals.net_message);
ClientEffects.ParticleEffect2(pos, dir, color, cnt);
break;
// RAFAEL
case Constants.TE_BLUEHYPERBLASTER:
Buffers.getPos(Globals.net_message, pos);
Buffers.getPos(Globals.net_message, dir);
ClientEffects.BlasterParticles(pos, dir);
break;
case Constants.TE_BLASTER: // blaster hitting wall
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
ClientEffects.BlasterParticles(pos, dir);
ex = AllocExplosion();
Math3D.VectorCopy(pos, ex.ent.origin);
ex.ent.angles[0] = (float) (Math.acos(dir[2]) / Math.PI * 180);
// PMM - fixed to correct for pitch of 0
if (dir[0] != 0.0f)
ex.ent.angles[1] = (float) (Math.atan2(dir[1], dir[0])
/ Math.PI * 180);
else if (dir[1] > 0)
ex.ent.angles[1] = 90;
else if (dir[1] < 0)
ex.ent.angles[1] = 270;
else
ex.ent.angles[1] = 0;
ex.type = ex_misc;
ex.ent.flags = Constants.RF_FULLBRIGHT | Constants.RF_TRANSLUCENT;
ex.start = Globals.cl.frame.servertime - 100;
ex.light = 150;
ex.lightcolor[0] = 1;
ex.lightcolor[1] = 1;
ex.ent.model = cl_mod_explode;
ex.frames = 4;
Sound.StartSound(pos, 0, 0, cl_sfx_lashit, 1, Constants.ATTN_NORM, 0);
break;
case Constants.TE_RAILTRAIL: // railgun effect
Buffers.getPos(Globals.net_message, pos);
Buffers.getPos(Globals.net_message, pos2);
ClientEffects.RailTrail(pos, pos2);
Sound.StartSound(pos2, 0, 0, cl_sfx_railg, 1, Constants.ATTN_NORM, 0);
break;
case Constants.TE_EXPLOSION2:
case Constants.TE_GRENADE_EXPLOSION:
case Constants.TE_GRENADE_EXPLOSION_WATER:
Buffers.getPos(Globals.net_message, pos);
ex = AllocExplosion();
Math3D.VectorCopy(pos, ex.ent.origin);
ex.type = ex_poly;
ex.ent.flags = Constants.RF_FULLBRIGHT;
ex.start = Globals.cl.frame.servertime - 100;
ex.light = 350;
ex.lightcolor[0] = 1.0f;
ex.lightcolor[1] = 0.5f;
ex.lightcolor[2] = 0.5f;
ex.ent.model = cl_mod_explo4;
ex.frames = 19;
ex.baseframe = 30;
ex.ent.angles[1] = Lib.rand() % 360;
ClientEffects.ExplosionParticles(pos);
if (type == Constants.TE_GRENADE_EXPLOSION_WATER)
Sound
.StartSound(pos, 0, 0, cl_sfx_watrexp, 1,
Constants.ATTN_NORM, 0);
else
Sound
.StartSound(pos, 0, 0, cl_sfx_grenexp, 1,
Constants.ATTN_NORM, 0);
break;
// RAFAEL
case Constants.TE_PLASMA_EXPLOSION:
Buffers.getPos(Globals.net_message, pos);
ex = AllocExplosion();
Math3D.VectorCopy(pos, ex.ent.origin);
ex.type = ex_poly;
ex.ent.flags = Constants.RF_FULLBRIGHT;
ex.start = Globals.cl.frame.servertime - 100;
ex.light = 350;
ex.lightcolor[0] = 1.0f;
ex.lightcolor[1] = 0.5f;
ex.lightcolor[2] = 0.5f;
ex.ent.angles[1] = Lib.rand() % 360;
ex.ent.model = cl_mod_explo4;
if (Globals.rnd.nextFloat() < 0.5)
ex.baseframe = 15;
ex.frames = 15;
ClientEffects.ExplosionParticles(pos);
Sound.StartSound(pos, 0, 0, cl_sfx_rockexp, 1, Constants.ATTN_NORM, 0);
break;
case Constants.TE_EXPLOSION1:
case Constants.TE_EXPLOSION1_BIG: // PMM
case Constants.TE_ROCKET_EXPLOSION:
case Constants.TE_ROCKET_EXPLOSION_WATER:
case Constants.TE_EXPLOSION1_NP: // PMM
Buffers.getPos(Globals.net_message, pos);
ex = AllocExplosion();
Math3D.VectorCopy(pos, ex.ent.origin);
ex.type = ex_poly;
ex.ent.flags = Constants.RF_FULLBRIGHT;
ex.start = Globals.cl.frame.servertime - 100;
ex.light = 350;
ex.lightcolor[0] = 1.0f;
ex.lightcolor[1] = 0.5f;
ex.lightcolor[2] = 0.5f;
ex.ent.angles[1] = Lib.rand() % 360;
if (type != Constants.TE_EXPLOSION1_BIG) // PMM
ex.ent.model = cl_mod_explo4; // PMM
else
ex.ent.model = cl_mod_explo4_big;
if (Globals.rnd.nextFloat() < 0.5)
ex.baseframe = 15;
ex.frames = 15;
if ((type != Constants.TE_EXPLOSION1_BIG)
&& (type != Constants.TE_EXPLOSION1_NP)) // PMM
ClientEffects.ExplosionParticles(pos); // PMM
if (type == Constants.TE_ROCKET_EXPLOSION_WATER)
Sound
.StartSound(pos, 0, 0, cl_sfx_watrexp, 1,
Constants.ATTN_NORM, 0);
else
Sound
.StartSound(pos, 0, 0, cl_sfx_rockexp, 1,
Constants.ATTN_NORM, 0);
break;
case Constants.TE_BFG_EXPLOSION:
Buffers.getPos(Globals.net_message, pos);
ex = AllocExplosion();
Math3D.VectorCopy(pos, ex.ent.origin);
ex.type = ex_poly;
ex.ent.flags = Constants.RF_FULLBRIGHT;
ex.start = Globals.cl.frame.servertime - 100;
ex.light = 350;
ex.lightcolor[0] = 0.0f;
ex.lightcolor[1] = 1.0f;
ex.lightcolor[2] = 0.0f;
ex.ent.model = cl_mod_bfg_explo;
ex.ent.flags |= Constants.RF_TRANSLUCENT;
ex.ent.alpha = 0.30f;
ex.frames = 4;
break;
case Constants.TE_BFG_BIGEXPLOSION:
Buffers.getPos(Globals.net_message, pos);
ClientEffects.BFGExplosionParticles(pos);
break;
case Constants.TE_BFG_LASER:
ParseLaser(0xd0d1d2d3);
break;
case Constants.TE_BUBBLETRAIL:
Buffers.getPos(Globals.net_message, pos);
Buffers.getPos(Globals.net_message, pos2);
ClientEffects.BubbleTrail(pos, pos2);
break;
case Constants.TE_PARASITE_ATTACK:
case Constants.TE_MEDIC_CABLE_ATTACK:
ent = ParseBeam(cl_mod_parasite_segment);
break;
case Constants.TE_BOSSTPORT: // boss teleporting to station
Buffers.getPos(Globals.net_message, pos);
ClientEffects.BigTeleportParticles(pos);
Sound.StartSound(pos, 0, 0, Sound.RegisterSound("misc/bigtele.wav"), 1,
Constants.ATTN_NONE, 0);
break;
case Constants.TE_GRAPPLE_CABLE:
ent = ParseBeam2(cl_mod_grapple_cable);
break;
// RAFAEL
case Constants.TE_WELDING_SPARKS:
cnt = Buffers.readUnsignedByte(Globals.net_message);
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
color = Buffers.readUnsignedByte(Globals.net_message);
ClientEffects.ParticleEffect2(pos, dir, color, cnt);
ex = AllocExplosion();
Math3D.VectorCopy(pos, ex.ent.origin);
ex.type = ex_flash;
// note to self
// we need a better no draw flag
ex.ent.flags = Constants.RF_BEAM;
ex.start = Globals.cl.frame.servertime - 0.1f;
ex.light = 100 + (Lib.rand() % 75);
ex.lightcolor[0] = 1.0f;
ex.lightcolor[1] = 1.0f;
ex.lightcolor[2] = 0.3f;
ex.ent.model = cl_mod_flash;
ex.frames = 2;
break;
case Constants.TE_GREENBLOOD:
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
ClientEffects.ParticleEffect2(pos, dir, 0xdf, 30);
break;
// RAFAEL
case Constants.TE_TUNNEL_SPARKS:
cnt = Buffers.readUnsignedByte(Globals.net_message);
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
color = Buffers.readUnsignedByte(Globals.net_message);
ClientEffects.ParticleEffect3(pos, dir, color, cnt);
break;
// =============
// PGM
// PMM -following code integrated for flechette (different color)
case Constants.TE_BLASTER2: // green blaster hitting wall
case Constants.TE_FLECHETTE: // flechette
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
// PMM
if (type == Constants.TE_BLASTER2)
ClientNewFx.BlasterParticles2(pos, dir, 0xd0);
else
ClientNewFx.BlasterParticles2(pos, dir, 0x6f); // 75
ex = AllocExplosion();
Math3D.VectorCopy(pos, ex.ent.origin);
ex.ent.angles[0] = (float) (Math.acos(dir[2]) / Math.PI * 180);
// PMM - fixed to correct for pitch of 0
if (dir[0] != 0.0f)
ex.ent.angles[1] = (float) (Math.atan2(dir[1], dir[0])
/ Math.PI * 180);
else if (dir[1] > 0)
ex.ent.angles[1] = 90;
else if (dir[1] < 0)
ex.ent.angles[1] = 270;
else
ex.ent.angles[1] = 0;
ex.type = ex_misc;
ex.ent.flags = Constants.RF_FULLBRIGHT | Constants.RF_TRANSLUCENT;
// PMM
if (type == Constants.TE_BLASTER2)
ex.ent.skinnum = 1;
else
// flechette
ex.ent.skinnum = 2;
ex.start = Globals.cl.frame.servertime - 100;
ex.light = 150;
// PMM
if (type == Constants.TE_BLASTER2)
ex.lightcolor[1] = 1;
else // flechette
{
ex.lightcolor[0] = 0.19f;
ex.lightcolor[1] = 0.41f;
ex.lightcolor[2] = 0.75f;
}
ex.ent.model = cl_mod_explode;
ex.frames = 4;
Sound.StartSound(pos, 0, 0, cl_sfx_lashit, 1, Constants.ATTN_NORM, 0);
break;
case Constants.TE_LIGHTNING:
ent = ParseLightning(cl_mod_lightning);
Sound.StartSound(null, ent, Constants.CHAN_WEAPON, cl_sfx_lightning, 1,
Constants.ATTN_NORM, 0);
break;
case Constants.TE_DEBUGTRAIL:
Buffers.getPos(Globals.net_message, pos);
Buffers.getPos(Globals.net_message, pos2);
ClientNewFx.DebugTrail(pos, pos2);
break;
case Constants.TE_PLAIN_EXPLOSION:
Buffers.getPos(Globals.net_message, pos);
ex = AllocExplosion();
Math3D.VectorCopy(pos, ex.ent.origin);
ex.type = ex_poly;
ex.ent.flags = Constants.RF_FULLBRIGHT;
ex.start = Globals.cl.frame.servertime - 100;
ex.light = 350;
ex.lightcolor[0] = 1.0f;
ex.lightcolor[1] = 0.5f;
ex.lightcolor[2] = 0.5f;
ex.ent.angles[1] = Lib.rand() % 360;
ex.ent.model = cl_mod_explo4;
if (Globals.rnd.nextFloat() < 0.5)
ex.baseframe = 15;
ex.frames = 15;
if (type == Constants.TE_ROCKET_EXPLOSION_WATER)
Sound
.StartSound(pos, 0, 0, cl_sfx_watrexp, 1,
Constants.ATTN_NORM, 0);
else
Sound
.StartSound(pos, 0, 0, cl_sfx_rockexp, 1,
Constants.ATTN_NORM, 0);
break;
case Constants.TE_FLASHLIGHT:
Buffers.getPos(Globals.net_message, pos);
ent = Globals.net_message.getShort();
ClientNewFx.flashlight(ent, pos);
break;
case Constants.TE_FORCEWALL:
Buffers.getPos(Globals.net_message, pos);
Buffers.getPos(Globals.net_message, pos2);
color = Buffers.readUnsignedByte(Globals.net_message);
ClientNewFx.ForceWall(pos, pos2, color);
break;
case Constants.TE_HEATBEAM:
ent = ParsePlayerBeam(cl_mod_heatbeam);
break;
case Constants.TE_MONSTER_HEATBEAM:
ent = ParsePlayerBeam(cl_mod_monster_heatbeam);
break;
case Constants.TE_HEATBEAM_SPARKS:
// cnt = MSG.ReadByte (net_message);
cnt = 50;
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
// r = MSG.ReadByte (net_message);
// magnitude = MSG.ReadShort (net_message);
r = 8;
magnitude = 60;
color = r & 0xff;
ClientNewFx.ParticleSteamEffect(pos, dir, color, cnt, magnitude);
Sound.StartSound(pos, 0, 0, cl_sfx_lashit, 1, Constants.ATTN_NORM, 0);
break;
case Constants.TE_HEATBEAM_STEAM:
// cnt = MSG.ReadByte (net_message);
cnt = 20;
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
// r = MSG.ReadByte (net_message);
// magnitude = MSG.ReadShort (net_message);
// color = r & 0xff;
color = 0xe0;
magnitude = 60;
ClientNewFx.ParticleSteamEffect(pos, dir, color, cnt, magnitude);
Sound.StartSound(pos, 0, 0, cl_sfx_lashit, 1, Constants.ATTN_NORM, 0);
break;
case Constants.TE_STEAM:
ParseSteam();
break;
case Constants.TE_BUBBLETRAIL2:
// cnt = MSG.ReadByte (net_message);
cnt = 8;
Buffers.getPos(Globals.net_message, pos);
Buffers.getPos(Globals.net_message, pos2);
ClientNewFx.BubbleTrail2(pos, pos2, cnt);
Sound.StartSound(pos, 0, 0, cl_sfx_lashit, 1, Constants.ATTN_NORM, 0);
break;
case Constants.TE_MOREBLOOD:
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
ClientEffects.ParticleEffect(pos, dir, 0xe8, 250);
break;
case Constants.TE_CHAINFIST_SMOKE:
dir[0] = 0;
dir[1] = 0;
dir[2] = 1;
Buffers.getPos(Globals.net_message, pos);
ClientNewFx.ParticleSmokeEffect(pos, dir, 0, 20, 20);
break;
case Constants.TE_ELECTRIC_SPARKS:
Buffers.getPos(Globals.net_message, pos);
ClientTent.ReadDir(Globals.net_message, dir);
// CL_ParticleEffect (pos, dir, 109, 40);
ClientEffects.ParticleEffect(pos, dir, 0x75, 40);
//FIXME : replace or remove this sound
Sound.StartSound(pos, 0, 0, cl_sfx_lashit, 1, Constants.ATTN_NORM, 0);
break;
case Constants.TE_TRACKER_EXPLOSION:
Buffers.getPos(Globals.net_message, pos);
ClientNewFx.colorFlash(pos, 0, 150, -1, -1, -1);
ClientNewFx.ColorExplosionParticles(pos, 0, 1);
Sound.StartSound(pos, 0, 0, cl_sfx_disrexp, 1, Constants.ATTN_NORM, 0);
break;
case Constants.TE_TELEPORT_EFFECT:
case Constants.TE_DBALL_GOAL:
Buffers.getPos(Globals.net_message, pos);
ClientEffects.TeleportParticles(pos);
break;
case Constants.TE_WIDOWBEAMOUT:
ParseWidow();
break;
case Constants.TE_NUKEBLAST:
ParseNuke();
break;
case Constants.TE_WIDOWSPLASH:
Buffers.getPos(Globals.net_message, pos);
ClientNewFx.WidowSplash(pos);
break;
// PGM
// ==============
default:
Com.Error(Constants.ERR_DROP, "CL_ParseTEnt: bad type");
}
}
// stack variable
// dist, org
private static final EntityType ent = new EntityType();
/*
* ================= CL_AddBeams =================
*/
static void AddBeams() {
int i, j;
beam_t[] b;
float d;
float yaw, pitch;
float forward;
float len, steps;
float model_length;
// update beams
b = cl_beams;
for (i = 0; i < MAX_BEAMS; i++) {
if (b[i].model == null || b[i].endtime < Globals.cl.time)
continue;
// if coming from the player, update the start position
if (b[i].entity == Globals.cl.playernum + 1) // entity 0 is the
// world
{
Math3D.VectorCopy(Globals.cl.refdef.vieworg, b[i].start);
b[i].start[2] -= 22; // adjust for view height
}
Math3D.VectorAdd(b[i].start, b[i].offset, org);
// calculate pitch and yaw
Math3D.VectorSubtract(b[i].end, org, dist);
if (dist[1] == 0 && dist[0] == 0) {
yaw = 0;
if (dist[2] > 0)
pitch = 90;
else
pitch = 270;
} else {
// PMM - fixed to correct for pitch of 0
if (dist[0] != 0.0f)
yaw = (float) (Math.atan2(dist[1], dist[0]) * 180 / Math.PI);
else if (dist[1] > 0)
yaw = 90;
else
yaw = 270;
if (yaw < 0)
yaw += 360;
forward = (float) Math.sqrt(dist[0] * dist[0] + dist[1]
* dist[1]);
pitch = (float) (Math.atan2(dist[2], forward) * -180.0 / Math.PI);
if (pitch < 0)
pitch += 360.0;
}
// add new entities for the beams
d = Math3D.VectorNormalize(dist);
//memset (&ent, 0, sizeof(ent));
ent.clear();
if (b[i].model == cl_mod_lightning) {
model_length = 35.0f;
d -= 20.0; // correction so it doesn't end in middle of tesla
} else {
model_length = 30.0f;
}
steps = (float) Math.ceil(d / model_length);
len = (d - model_length) / (steps - 1);
// PMM - special case for lightning model .. if the real length is
// shorter than the model,
// flip it around & draw it from the end to the start. This prevents
// the model from going
// through the tesla mine (instead it goes through the target)
if ((b[i].model == cl_mod_lightning) && (d <= model_length)) {
// Com_Printf ("special case\n");
Math3D.VectorCopy(b[i].end, ent.origin);
// offset to push beam outside of tesla model (negative because
// dist is from end to start
// for this beam)
// for (j=0 ; j<3 ; j++)
// ent.origin[j] -= dist[j]*10.0;
ent.model = b[i].model;
ent.flags = Constants.RF_FULLBRIGHT;
ent.angles[0] = pitch;
ent.angles[1] = yaw;
ent.angles[2] = Lib.rand() % 360;
Video.AddEntity(ent);
return;
}
while (d > 0) {
Math3D.VectorCopy(org, ent.origin);
ent.model = b[i].model;
if (b[i].model == cl_mod_lightning) {
ent.flags = Constants.RF_FULLBRIGHT;
ent.angles[0] = -pitch;
ent.angles[1] = yaw + 180.0f;
ent.angles[2] = Lib.rand() % 360;
} else {
ent.angles[0] = pitch;
ent.angles[1] = yaw;
ent.angles[2] = Lib.rand() % 360;
}
// Com_Printf("B: %d . %d\n", b[i].entity, b[i].dest_entity);
Video.AddEntity(ent);
for (j = 0; j < 3; j++)
org[j] += dist[j] * len;
d -= model_length;
}
}
}
//extern cvar_t *hand;
// stack variable
private static final float[] dist = new float[3];
private static final float[] org = new float[3];
private static final float[] f = new float[3];
private static final float[] u = new float[3];
private static final float[] r = new float[3];
/*
* ================= ROGUE - draw player locked beams CL_AddPlayerBeams
* =================
*/
static void AddPlayerBeams() {
float d;
//entity_t ent = new entity_t();
float yaw, pitch;
float forward;
float len, steps;
int framenum = 0;
float model_length;
float hand_multiplier;
FrameData oldframe;
PlayerState ps, ops;
// PMM
if (Globals.hand != null) {
if (Globals.hand.value == 2)
hand_multiplier = 0;
else if (Globals.hand.value == 1)
hand_multiplier = -1;
else
hand_multiplier = 1;
} else {
hand_multiplier = 1;
}
// PMM
// update beams
beam_t[] b = cl_playerbeams;
for (int i = 0; i < MAX_BEAMS; i++) {
if (b[i].model == null || b[i].endtime < Globals.cl.time)
continue;
if (cl_mod_heatbeam != null && (b[i].model == cl_mod_heatbeam)) {
// if coming from the player, update the start position
if (b[i].entity == Globals.cl.playernum + 1) // entity 0 is the
// world
{
// set up gun position
// code straight out of CL_AddViewWeapon
ps = Globals.cl.frame.playerstate;
int j = (Globals.cl.frame.serverframe - 1)
& Constants.UPDATE_MASK;
oldframe = Globals.cl.frames[j];
if (oldframe.serverframe != Globals.cl.frame.serverframe - 1
|| !oldframe.valid)
oldframe = Globals.cl.frame; // previous frame was
// dropped or involid
ops = oldframe.playerstate;
for (j = 0; j < 3; j++) {
b[i].start[j] = Globals.cl.refdef.vieworg[j]
+ ops.gunoffset[j] + Globals.cl.lerpfrac
* (ps.gunoffset[j] - ops.gunoffset[j]);
}
Math3D.VectorMA(b[i].start,
(hand_multiplier * b[i].offset[0]),
Globals.cl.v_right, org);
Math3D.VectorMA(org, b[i].offset[1], Globals.cl.v_forward,
org);
Math3D.VectorMA(org, b[i].offset[2], Globals.cl.v_up, org);
if ((Globals.hand != null) && (Globals.hand.value == 2)) {
Math3D.VectorMA(org, -1, Globals.cl.v_up, org);
}
// FIXME - take these out when final
Math3D.VectorCopy(Globals.cl.v_right, r);
Math3D.VectorCopy(Globals.cl.v_forward, f);
Math3D.VectorCopy(Globals.cl.v_up, u);
} else
Math3D.VectorCopy(b[i].start, org);
} else {
// if coming from the player, update the start position
if (b[i].entity == Globals.cl.playernum + 1) // entity 0 is the
// world
{
Math3D.VectorCopy(Globals.cl.refdef.vieworg, b[i].start);
b[i].start[2] -= 22; // adjust for view height
}
Math3D.VectorAdd(b[i].start, b[i].offset, org);
}
// calculate pitch and yaw
Math3D.VectorSubtract(b[i].end, org, dist);
// PMM
if (cl_mod_heatbeam != null && (b[i].model == cl_mod_heatbeam)
&& (b[i].entity == Globals.cl.playernum + 1)) {
len = Math3D.VectorLength(dist);
Math3D.VectorScale(f, len, dist);
Math3D.VectorMA(dist, (hand_multiplier * b[i].offset[0]), r,
dist);
Math3D.VectorMA(dist, b[i].offset[1], f, dist);
Math3D.VectorMA(dist, b[i].offset[2], u, dist);
if ((Globals.hand != null) && (Globals.hand.value == 2)) {
Math3D.VectorMA(org, -1, Globals.cl.v_up, org);
}
}
// PMM
if (dist[1] == 0 && dist[0] == 0) {
yaw = 0;
if (dist[2] > 0)
pitch = 90;
else
pitch = 270;
} else {
// PMM - fixed to correct for pitch of 0
if (dist[0] != 0.0f)
yaw = (float) (Math.atan2(dist[1], dist[0]) * 180 / Math.PI);
else if (dist[1] > 0)
yaw = 90;
else
yaw = 270;
if (yaw < 0)
yaw += 360;
forward = (float) Math.sqrt(dist[0] * dist[0] + dist[1]
* dist[1]);
pitch = (float) (Math.atan2(dist[2], forward) * -180.0 / Math.PI);
if (pitch < 0)
pitch += 360.0;
}
if (cl_mod_heatbeam != null && (b[i].model == cl_mod_heatbeam)) {
if (b[i].entity != Globals.cl.playernum + 1) {
framenum = 2;
// Com_Printf ("Third person\n");
ent.angles[0] = -pitch;
ent.angles[1] = yaw + 180.0f;
ent.angles[2] = 0;
// Com_Printf ("%f %f - %f %f %f\n", -pitch, yaw+180.0,
// b[i].offset[0], b[i].offset[1], b[i].offset[2]);
Math3D.AngleVectors(ent.angles, f, r, u);
// if it's a non-origin offset, it's a player, so use the
// hardcoded player offset
if (!Math3D.VectorEquals(b[i].offset, Globals.vec3_origin)) {
Math3D.VectorMA(org, -(b[i].offset[0]) + 1, r, org);
Math3D.VectorMA(org, -(b[i].offset[1]), f, org);
Math3D.VectorMA(org, -(b[i].offset[2]) - 10, u, org);
} else {
// if it's a monster, do the particle effect
ClientNewFx.MonsterPlasma_Shell(b[i].start);
}
} else {
framenum = 1;
}
}
// if it's the heatbeam, draw the particle effect
if ((cl_mod_heatbeam != null && (b[i].model == cl_mod_heatbeam) && (b[i].entity == Globals.cl.playernum + 1))) {
ClientNewFx.Heatbeam(org, dist);
}
// add new entities for the beams
d = Math3D.VectorNormalize(dist);
//memset (&ent, 0, sizeof(ent));
ent.clear();
if (b[i].model == cl_mod_heatbeam) {
model_length = 32.0f;
} else if (b[i].model == cl_mod_lightning) {
model_length = 35.0f;
d -= 20.0; // correction so it doesn't end in middle of tesla
} else {
model_length = 30.0f;
}
steps = (float) Math.ceil(d / model_length);
len = (d - model_length) / (steps - 1);
// PMM - special case for lightning model .. if the real length is
// shorter than the model,
// flip it around & draw it from the end to the start. This prevents
// the model from going
// through the tesla mine (instead it goes through the target)
if ((b[i].model == cl_mod_lightning) && (d <= model_length)) {
// Com_Printf ("special case\n");
Math3D.VectorCopy(b[i].end, ent.origin);
// offset to push beam outside of tesla model (negative because
// dist is from end to start
// for this beam)
// for (j=0 ; j<3 ; j++)
// ent.origin[j] -= dist[j]*10.0;
ent.model = b[i].model;
ent.flags = Constants.RF_FULLBRIGHT;
ent.angles[0] = pitch;
ent.angles[1] = yaw;
ent.angles[2] = Lib.rand() % 360;
Video.AddEntity(ent);
return;
}
while (d > 0) {
Math3D.VectorCopy(org, ent.origin);
ent.model = b[i].model;
if (cl_mod_heatbeam != null && (b[i].model == cl_mod_heatbeam)) {
// ent.flags = RF_FULLBRIGHT|RF_TRANSLUCENT;
// ent.alpha = 0.3;
ent.flags = Constants.RF_FULLBRIGHT;
ent.angles[0] = -pitch;
ent.angles[1] = yaw + 180.0f;
ent.angles[2] = (Globals.cl.time) % 360;
// ent.angles[2] = rand()%360;
ent.frame = framenum;
} else if (b[i].model == cl_mod_lightning) {
ent.flags = Constants.RF_FULLBRIGHT;
ent.angles[0] = -pitch;
ent.angles[1] = yaw + 180.0f;
ent.angles[2] = Lib.rand() % 360;
} else {
ent.angles[0] = pitch;
ent.angles[1] = yaw;
ent.angles[2] = Lib.rand() % 360;
}
// Com_Printf("B: %d . %d\n", b[i].entity, b[i].dest_entity);
Video.AddEntity(ent);
for (int j = 0; j < 3; j++)
org[j] += dist[j] * len;
d -= model_length;
}
}
}
/*
* ================= CL_AddExplosions =================
*/
static void AddExplosions() {
EntityType ent;
int i;
ExplosionData[] ex;
float frac;
int f;
//memset (&ent, 0, sizeof(ent)); Pointer!
ent = null;
ex = cl_explosions;
for (i = 0; i < MAX_EXPLOSIONS; i++) {
if (ex[i].type == ex_free)
continue;
frac = (Globals.cl.time - ex[i].start) / 100.0f;
f = (int) Math.floor(frac);
ent = ex[i].ent;
switch (ex[i].type) {
case ex_mflash:
if (f >= ex[i].frames - 1)
ex[i].type = ex_free;
break;
case ex_misc:
if (f >= ex[i].frames - 1) {
ex[i].type = ex_free;
break;
}
ent.alpha = 1.0f - frac / (ex[i].frames - 1);
break;
case ex_flash:
if (f >= 1) {
ex[i].type = ex_free;
break;
}
ent.alpha = 1.0f;
break;
case ex_poly:
if (f >= ex[i].frames - 1) {
ex[i].type = ex_free;
break;
}
ent.alpha = (16.0f - (float) f) / 16.0f;
if (f < 10) {
ent.skinnum = (f >> 1);
if (ent.skinnum < 0)
ent.skinnum = 0;
} else {
ent.flags |= Constants.RF_TRANSLUCENT;
if (f < 13)
ent.skinnum = 5;
else
ent.skinnum = 6;
}
break;
case ex_poly2:
if (f >= ex[i].frames - 1) {
ex[i].type = ex_free;
break;
}
ent.alpha = (5.0f - (float) f) / 5.0f;
ent.skinnum = 0;
ent.flags |= Constants.RF_TRANSLUCENT;
break;
}
if (ex[i].type == ex_free)
continue;
if (ex[i].light != 0.0f) {
Video.AddLight(ent.origin, ex[i].light * ent.alpha,
ex[i].lightcolor[0], ex[i].lightcolor[1],
ex[i].lightcolor[2]);
}
Math3D.VectorCopy(ent.origin, ent.oldorigin);
if (f < 0)
f = 0;
ent.frame = ex[i].baseframe + f + 1;
ent.oldframe = ex[i].baseframe + f;
ent.backlerp = 1.0f - Globals.cl.lerpfrac;
Video.AddEntity(ent);
}
}
/*
* ================= CL_AddLasers =================
*/
static void AddLasers() {
laser_t[] l;
int i;
l = cl_lasers;
for (i = 0; i < MAX_LASERS; i++) {
if (l[i].endtime >= Globals.cl.time)
Video.AddEntity(l[i].ent);
}
}
/* PMM - CL_Sustains */
static void ProcessSustain() {
ClientSustain[] s;
int i;
s = cl_sustains;
for (i = 0; i < MAX_SUSTAINS; i++) {
if (s[i].id != 0)
if ((s[i].endtime >= Globals.cl.time)
&& (Globals.cl.time >= s[i].nextthink)) {
s[i].think.think(s[i]);
} else if (s[i].endtime < Globals.cl.time)
s[i].id = 0;
}
}
/*
* ================= CL_AddTEnts =================
*/
static void AddTEnts() {
AddBeams();
// PMM - draw plasma beams
AddPlayerBeams();
AddExplosions();
AddLasers();
// PMM - set up sustain
ProcessSustain();
}
//should be ok.
public static void ReadDir(Buffer sb, float[] dir) {
int b;
b = Buffers.readUnsignedByte(sb);
if (b >= Constants.NUMVERTEXNORMALS)
Com.Error(Constants.ERR_DROP, "MSF_ReadDir: out of range");
Math3D.VectorCopy(Globals.bytedirs[b], dir);
}
}