/*
* 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.game;
import com.googlecode.gwtquake.shared.common.Constants;
import com.googlecode.gwtquake.shared.common.Globals;
import com.googlecode.gwtquake.shared.game.adapters.EntityThinkAdapter;
import com.googlecode.gwtquake.shared.game.adapters.EntityUseAdapter;
import com.googlecode.gwtquake.shared.server.ServerGame;
import com.googlecode.gwtquake.shared.server.ServerInit;
import com.googlecode.gwtquake.shared.server.ServerSend;
import com.googlecode.gwtquake.shared.server.World;
import com.googlecode.gwtquake.shared.util.Lib;
import com.googlecode.gwtquake.shared.util.Math3D;
public class GameTarget {
public static void SP_target_temp_entity(Entity ent) {
ent.use = Use_Target_Tent;
}
public static void SP_target_speaker(Entity ent) {
//char buffer[MAX_QPATH];
String buffer;
if (GameBase.st.noise == null) {
ServerGame.PF_dprintf("target_speaker with no noise set at "
+ Lib.vtos(ent.s.origin) + "\n");
return;
}
if (GameBase.st.noise.indexOf(".wav") < 0)
buffer = "" + GameBase.st.noise + ".wav";
else
buffer = GameBase.st.noise;
ent.noise_index = ServerInit.SV_SoundIndex(buffer);
if (ent.volume == 0)
ent.volume = 1.0f;
if (ent.attenuation == 0)
ent.attenuation = 1.0f;
else if (ent.attenuation == -1) // use -1 so 0 defaults to 1
ent.attenuation = 0;
// check for prestarted looping sound
if ((ent.spawnflags & 1) != 0)
ent.s.sound = ent.noise_index;
ent.use = Use_Target_Speaker;
// must link the entity so we get areas and clusters so
// the server can determine who to send updates to
World.SV_LinkEdict(ent);
}
/**
* QUAKED target_help (1 0 1) (-16 -16 -24) (16 16 24) help1 When fired, the
* "message" key becomes the current personal computer string, and the
* message light will be set on all clients status bars.
*/
public static void SP_target_help(Entity ent) {
if (GameBase.deathmatch.value != 0) { // auto-remove for deathmatch
GameUtil.G_FreeEdict(ent);
return;
}
if (ent.message == null) {
ServerGame.PF_dprintf(ent.classname + " with no message at "
+ Lib.vtos(ent.s.origin) + "\n");
GameUtil.G_FreeEdict(ent);
return;
}
ent.use = Use_Target_Help;
}
public static void SP_target_secret(Entity ent) {
if (GameBase.deathmatch.value != 0) { // auto-remove for deathmatch
GameUtil.G_FreeEdict(ent);
return;
}
ent.use = use_target_secret;
if (GameBase.st.noise == null)
GameBase.st.noise = "misc/secret.wav";
ent.noise_index = ServerInit.SV_SoundIndex(GameBase.st.noise);
ent.svflags = Constants.SVF_NOCLIENT;
GameBase.level.total_secrets++;
// map bug hack
if (0 == Lib.Q_stricmp(GameBase.level.mapname, "mine3")
&& ent.s.origin[0] == 280 && ent.s.origin[1] == -2048
&& ent.s.origin[2] == -624)
ent.message = "You have found a secret area.";
}
public static void SP_target_goal(Entity ent) {
if (GameBase.deathmatch.value != 0) { // auto-remove for deathmatch
GameUtil.G_FreeEdict(ent);
return;
}
ent.use = use_target_goal;
if (GameBase.st.noise == null)
GameBase.st.noise = "misc/secret.wav";
ent.noise_index = ServerInit.SV_SoundIndex(GameBase.st.noise);
ent.svflags = Constants.SVF_NOCLIENT;
GameBase.level.total_goals++;
}
public static void SP_target_explosion(Entity ent) {
ent.use = use_target_explosion;
ent.svflags = Constants.SVF_NOCLIENT;
}
public static void SP_target_changelevel(Entity ent) {
if (ent.map == null) {
ServerGame.PF_dprintf("target_changelevel with no map at "
+ Lib.vtos(ent.s.origin) + "\n");
GameUtil.G_FreeEdict(ent);
return;
}
// ugly hack because *SOMEBODY* screwed up their map
if ((Lib.Q_stricmp(GameBase.level.mapname, "fact1") == 0)
&& (Lib.Q_stricmp(ent.map, "fact3") == 0))
ent.map = "fact3$secret1";
ent.use = use_target_changelevel;
ent.svflags = Constants.SVF_NOCLIENT;
}
public static void SP_target_splash(Entity self) {
self.use = use_target_splash;
GameBase.G_SetMovedir(self.s.angles, self.movedir);
if (0 == self.count)
self.count = 32;
self.svflags = Constants.SVF_NOCLIENT;
}
public static void SP_target_spawner(Entity self) {
self.use = use_target_spawner;
self.svflags = Constants.SVF_NOCLIENT;
if (self.speed != 0) {
GameBase.G_SetMovedir(self.s.angles, self.movedir);
Math3D.VectorScale(self.movedir, self.speed, self.movedir);
}
}
public static void SP_target_blaster(Entity self) {
self.use = use_target_blaster;
GameBase.G_SetMovedir(self.s.angles, self.movedir);
self.noise_index = ServerInit.SV_SoundIndex("weapons/laser2.wav");
if (0 == self.dmg)
self.dmg = 15;
if (0 == self.speed)
self.speed = 1000;
self.svflags = Constants.SVF_NOCLIENT;
}
public static void SP_target_crosslevel_trigger(Entity self) {
self.svflags = Constants.SVF_NOCLIENT;
self.use = trigger_crosslevel_trigger_use;
}
public static void SP_target_crosslevel_target(Entity self) {
if (0 == self.delay)
self.delay = 1;
self.svflags = Constants.SVF_NOCLIENT;
self.think = target_crosslevel_target_think;
self.nextthink = GameBase.level.time + self.delay;
}
public static void target_laser_on(Entity self) {
if (null == self.activator)
self.activator = self;
self.spawnflags |= 0x80000001;
self.svflags &= ~Constants.SVF_NOCLIENT;
target_laser_think.think(self);
}
public static void target_laser_off(Entity self) {
self.spawnflags &= ~1;
self.svflags |= Constants.SVF_NOCLIENT;
self.nextthink = 0;
}
public static void SP_target_laser(Entity self) {
// let everything else get spawned before we start firing
self.think = target_laser_start;
self.nextthink = GameBase.level.time + 1;
}
public static void SP_target_lightramp(Entity self) {
if (self.message == null || self.message.length() != 2
|| self.message.charAt(0) < 'a' || self.message.charAt(0) > 'z'
|| self.message.charAt(1) < 'a' || self.message.charAt(1) > 'z'
|| self.message.charAt(0) == self.message.charAt(1)) {
ServerGame.PF_dprintf("target_lightramp has bad ramp ("
+ self.message + ") at " + Lib.vtos(self.s.origin) + "\n");
GameUtil.G_FreeEdict(self);
return;
}
if (GameBase.deathmatch.value != 0) {
GameUtil.G_FreeEdict(self);
return;
}
if (self.target == null) {
ServerGame.PF_dprintf(self.classname + " with no target at "
+ Lib.vtos(self.s.origin) + "\n");
GameUtil.G_FreeEdict(self);
return;
}
self.svflags |= Constants.SVF_NOCLIENT;
self.use = target_lightramp_use;
self.think = target_lightramp_think;
self.movedir[0] = self.message.charAt(0) - 'a';
self.movedir[1] = self.message.charAt(1) - 'a';
self.movedir[2] = (self.movedir[1] - self.movedir[0])
/ (self.speed / Constants.FRAMETIME);
}
public static void SP_target_earthquake(Entity self) {
if (null == self.targetname)
ServerGame.PF_dprintf("untargeted " + self.classname + " at "
+ Lib.vtos(self.s.origin) + "\n");
if (0 == self.count)
self.count = 5;
if (0 == self.speed)
self.speed = 200;
self.svflags |= Constants.SVF_NOCLIENT;
self.think = target_earthquake_think;
self.use = target_earthquake_use;
self.noise_index = ServerInit.SV_SoundIndex("world/quake.wav");
}
/**
* QUAKED target_temp_entity (1 0 0) (-8 -8 -8) (8 8 8) Fire an origin based
* temp entity event to the clients. "style" type byte
*/
public static EntityUseAdapter Use_Target_Tent = new EntityUseAdapter() {
public String getID() { return "Use_Target_Tent"; }
public void use(Entity ent, Entity other, Entity activator) {
ServerGame.PF_WriteByte(Constants.svc_temp_entity);
ServerGame.PF_WriteByte(ent.style);
ServerGame.PF_WritePos(ent.s.origin);
ServerSend.SV_Multicast(ent.s.origin, Constants.MULTICAST_PVS);
}
};
/**
* QUAKED target_speaker (1 0 0) (-8 -8 -8) (8 8 8) looped-on looped-off
* reliable "noise" wav file to play "attenuation" -1 = none, send to whole
* level 1 = normal fighting sounds 2 = idle sound level 3 = ambient sound
* level "volume" 0.0 to 1.0
*
* Normal sounds play each time the target is used. The reliable flag can be
* set for crucial voiceovers.
*
* Looped sounds are always atten 3 / vol 1, and the use function toggles it
* on/off. Multiple identical looping sounds will just increase volume
* without any speed cost.
*/
public static EntityUseAdapter Use_Target_Speaker = new EntityUseAdapter() {
public String getID() { return "Use_Target_Speaker"; }
public void use(Entity ent, Entity other, Entity activator) {
int chan;
if ((ent.spawnflags & 3) != 0) { // looping sound toggles
if (ent.s.sound != 0)
ent.s.sound = 0; // turn it off
else
ent.s.sound = ent.noise_index; // start it
} else { // normal sound
if ((ent.spawnflags & 4) != 0)
chan = Constants.CHAN_VOICE | Constants.CHAN_RELIABLE;
else
chan = Constants.CHAN_VOICE;
// use a positioned_sound, because this entity won't normally be
// sent to any clients because it is invisible
ServerSend.SV_StartSound(ent.s.origin, ent, chan, ent.noise_index, ent.volume,
ent.attenuation, (float) 0);
}
}
};
public static EntityUseAdapter Use_Target_Help = new EntityUseAdapter() {
public String getID() { return "Use_Target_Help"; }
public void use(Entity ent, Entity other, Entity activator) {
if ((ent.spawnflags & 1) != 0)
GameBase.game.helpmessage1 = ent.message;
else
GameBase.game.helpmessage2 = ent.message;
GameBase.game.helpchanged++;
}
};
/**
* QUAKED target_secret (1 0 1) (-8 -8 -8) (8 8 8) Counts a secret found.
* These are single use targets.
*/
static EntityUseAdapter use_target_secret = new EntityUseAdapter() {
public String getID() { return "use_target_secret"; }
public void use(Entity ent, Entity other, Entity activator) {
ServerGame.PF_StartSound(ent, Constants.CHAN_VOICE, ent.noise_index, (float) 1, (float) Constants.ATTN_NORM,
(float) 0);
GameBase.level.found_secrets++;
GameUtil.G_UseTargets(ent, activator);
GameUtil.G_FreeEdict(ent);
}
};
/**
* QUAKED target_goal (1 0 1) (-8 -8 -8) (8 8 8) Counts a goal completed.
* These are single use targets.
*/
static EntityUseAdapter use_target_goal = new EntityUseAdapter() {
public String getID() { return "use_target_goal"; }
public void use(Entity ent, Entity other, Entity activator) {
ServerGame.PF_StartSound(ent, Constants.CHAN_VOICE, ent.noise_index, (float) 1, (float) Constants.ATTN_NORM,
(float) 0);
GameBase.level.found_goals++;
if (GameBase.level.found_goals == GameBase.level.total_goals)
ServerGame.PF_Configstring(Constants.CS_CDTRACK, "0");
GameUtil.G_UseTargets(ent, activator);
GameUtil.G_FreeEdict(ent);
}
};
/**
* QUAKED target_explosion (1 0 0) (-8 -8 -8) (8 8 8) Spawns an explosion
* temporary entity when used.
*
* "delay" wait this long before going off "dmg" how much radius damage
* should be done, defaults to 0
*/
static EntityThinkAdapter target_explosion_explode = new EntityThinkAdapter() {
public String getID() { return "target_explosion_explode"; }
public boolean think(Entity self) {
float save;
ServerGame.PF_WriteByte(Constants.svc_temp_entity);
ServerGame.PF_WriteByte(Constants.TE_EXPLOSION1);
ServerGame.PF_WritePos(self.s.origin);
ServerSend.SV_Multicast(self.s.origin, Constants.MULTICAST_PHS);
GameCombat.T_RadiusDamage(self, self.activator, self.dmg, null,
self.dmg + 40, Constants.MOD_EXPLOSIVE);
save = self.delay;
self.delay = 0;
GameUtil.G_UseTargets(self, self.activator);
self.delay = save;
return true;
}
};
static EntityUseAdapter use_target_explosion = new EntityUseAdapter() {
public String getID() { return "use_target_explosion"; }
public void use(Entity self, Entity other, Entity activator) {
self.activator = activator;
if (0 == self.delay) {
target_explosion_explode.think(self);
return;
}
self.think = target_explosion_explode;
self.nextthink = GameBase.level.time + self.delay;
}
};
/**
* QUAKED target_changelevel (1 0 0) (-8 -8 -8) (8 8 8) Changes level to
* "map" when fired
*/
static EntityUseAdapter use_target_changelevel = new EntityUseAdapter() {
public String getID() { return "use_target_changelevel"; }
public void use(Entity self, Entity other, Entity activator) {
if (GameBase.level.intermissiontime != 0)
return; // already activated
if (0 == GameBase.deathmatch.value && 0 == GameBase.coop.value) {
if (GameBase.g_edicts[1].health <= 0)
return;
}
// if noexit, do a ton of damage to other
if (GameBase.deathmatch.value != 0
&& 0 == ((int) GameBase.dmflags.value & Constants.DF_ALLOW_EXIT)
&& other != GameBase.g_edicts[0] /* world */
) {
GameCombat.T_Damage(other, self, self, Globals.vec3_origin,
other.s.origin, Globals.vec3_origin,
10 * other.max_health, 1000, 0, Constants.MOD_EXIT);
return;
}
// if multiplayer, let everyone know who hit the exit
if (GameBase.deathmatch.value != 0) {
if (activator != null && activator.client != null)
ServerSend.SV_BroadcastPrintf(Constants.PRINT_HIGH, activator.client.pers.netname
+ " exited the level.\n");
}
// if going to a new unit, clear cross triggers
if (self.map.indexOf('*') > -1)
GameBase.game.serverflags &= ~(Constants.SFL_CROSS_TRIGGER_MASK);
PlayerHud.BeginIntermission(self);
}
};
/**
* QUAKED target_splash (1 0 0) (-8 -8 -8) (8 8 8) Creates a particle splash
* effect when used.
*
* Set "sounds" to one of the following: 1) sparks 2) blue water 3) brown
* water 4) slime 5) lava 6) blood
*
* "count" how many pixels in the splash "dmg" if set, does a radius damage
* at this location when it splashes useful for lava/sparks
*/
static EntityUseAdapter use_target_splash = new EntityUseAdapter() {
public String getID() { return "use_target_splash"; }
public void use(Entity self, Entity other, Entity activator) {
ServerGame.PF_WriteByte(Constants.svc_temp_entity);
ServerGame.PF_WriteByte(Constants.TE_SPLASH);
ServerGame.PF_WriteByte(self.count);
ServerGame.PF_WritePos(self.s.origin);
ServerGame.PF_WriteDir(self.movedir);
ServerGame.PF_WriteByte(self.sounds);
ServerSend.SV_Multicast(self.s.origin, Constants.MULTICAST_PVS);
if (self.dmg != 0)
GameCombat.T_RadiusDamage(self, activator, self.dmg, null,
self.dmg + 40, Constants.MOD_SPLASH);
}
};
/**
* QUAKED target_spawner (1 0 0) (-8 -8 -8) (8 8 8) Set target to the type
* of entity you want spawned. Useful for spawning monsters and gibs in the
* factory levels.
*
* For monsters: Set direction to the facing you want it to have.
*
* For gibs: Set direction if you want it moving and speed how fast it
* should be moving otherwise it will just be dropped
*/
static EntityUseAdapter use_target_spawner = new EntityUseAdapter() {
public String getID() { return "use_target_spawner"; }
public void use(Entity self, Entity other, Entity activator) {
Entity ent;
ent = GameUtil.G_Spawn();
ent.classname = self.target;
Math3D.VectorCopy(self.s.origin, ent.s.origin);
Math3D.VectorCopy(self.s.angles, ent.s.angles);
GameSpawn.ED_CallSpawn(ent);
World.SV_UnlinkEdict(ent);
GameUtil.KillBox(ent);
World.SV_LinkEdict(ent);
if (self.speed != 0)
Math3D.VectorCopy(self.movedir, ent.velocity);
}
};
/**
* QUAKED target_blaster (1 0 0) (-8 -8 -8) (8 8 8) NOTRAIL NOEFFECTS Fires
* a blaster bolt in the set direction when triggered.
*
* dmg default is 15 speed default is 1000
*/
public static EntityUseAdapter use_target_blaster = new EntityUseAdapter() {
public String getID() { return "use_target_blaster"; }
public void use(Entity self, Entity other, Entity activator) {
int effect;
if ((self.spawnflags & 2) != 0)
effect = 0;
else if ((self.spawnflags & 1) != 0)
effect = Constants.EF_HYPERBLASTER;
else
effect = Constants.EF_BLASTER;
GameWeapon.fire_blaster(self, self.s.origin, self.movedir, self.dmg,
(int) self.speed, Constants.EF_BLASTER,
Constants.MOD_TARGET_BLASTER != 0
/* true */
);
ServerGame.PF_StartSound(self, Constants.CHAN_VOICE, self.noise_index, (float) 1, (float) Constants.ATTN_NORM,
(float) 0);
}
};
/**
* QUAKED target_crosslevel_trigger (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1
* trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8 Once this
* trigger is touched/used, any trigger_crosslevel_target with the same
* trigger number is automatically used when a level is started within the
* same unit. It is OK to check multiple triggers. Message, delay, target,
* and killtarget also work.
*/
public static EntityUseAdapter trigger_crosslevel_trigger_use = new EntityUseAdapter() {
public String getID() { return "trigger_crosslevel_trigger_use"; }
public void use(Entity self, Entity other, Entity activator) {
GameBase.game.serverflags |= self.spawnflags;
GameUtil.G_FreeEdict(self);
}
};
/**
* QUAKED target_crosslevel_target (.5 .5 .5) (-8 -8 -8) (8 8 8) trigger1
* trigger2 trigger3 trigger4 trigger5 trigger6 trigger7 trigger8 Triggered
* by a trigger_crosslevel elsewhere within a unit. If multiple triggers are
* checked, all must be true. Delay, target and killtarget also work.
*
* "delay" delay before using targets if the trigger has been activated
* (default 1)
*/
static EntityThinkAdapter target_crosslevel_target_think = new EntityThinkAdapter() {
public String getID() { return "target_crosslevel_target_think"; }
public boolean think(Entity self) {
if (self.spawnflags == (GameBase.game.serverflags
& Constants.SFL_CROSS_TRIGGER_MASK & self.spawnflags)) {
GameUtil.G_UseTargets(self, self);
GameUtil.G_FreeEdict(self);
}
return true;
}
};
/**
* QUAKED target_laser (0 .5 .8) (-8 -8 -8) (8 8 8) START_ON RED GREEN BLUE
* YELLOW ORANGE FAT When triggered, fires a laser. You can either set a
* target or a direction.
*/
public static EntityThinkAdapter target_laser_think = new EntityThinkAdapter() {
public String getID() { return "target_laser_think"; }
public boolean think(Entity self) {
Entity ignore;
float[] start = { 0, 0, 0 };
float[] end = { 0, 0, 0 };
Trace tr;
float[] point = { 0, 0, 0 };
float[] last_movedir = { 0, 0, 0 };
int count;
if ((self.spawnflags & 0x80000000) != 0)
count = 8;
else
count = 4;
if (self.enemy != null) {
Math3D.VectorCopy(self.movedir, last_movedir);
Math3D.VectorMA(self.enemy.absmin, 0.5f, self.enemy.size, point);
Math3D.VectorSubtract(point, self.s.origin, self.movedir);
Math3D.VectorNormalize(self.movedir);
if (!Math3D.VectorEquals(self.movedir, last_movedir))
self.spawnflags |= 0x80000000;
}
ignore = self;
Math3D.VectorCopy(self.s.origin, start);
Math3D.VectorMA(start, 2048, self.movedir, end);
while (true) {
tr = World.SV_Trace(start, null, null, end, ignore, Constants.CONTENTS_SOLID | Constants.CONTENTS_MONSTER
| Constants.CONTENTS_DEADMONSTER);
if (tr.ent == null)
break;
// hurt it if we can
if ((tr.ent.takedamage != 0)
&& 0 == (tr.ent.flags & Constants.FL_IMMUNE_LASER))
GameCombat.T_Damage(tr.ent, self, self.activator,
self.movedir, tr.endpos, Globals.vec3_origin,
self.dmg, 1, Constants.DAMAGE_ENERGY,
Constants.MOD_TARGET_LASER);
// if we hit something that's not a monster or player or is
// immune to lasers, we're done
if (0 == (tr.ent.svflags & Constants.SVF_MONSTER)
&& (null == tr.ent.client)) {
if ((self.spawnflags & 0x80000000) != 0) {
self.spawnflags &= ~0x80000000;
ServerGame.PF_WriteByte(Constants.svc_temp_entity);
ServerGame.PF_WriteByte(Constants.TE_LASER_SPARKS);
ServerGame.PF_WriteByte(count);
ServerGame.PF_WritePos(tr.endpos);
ServerGame.PF_WriteDir(tr.plane.normal);
ServerGame.PF_WriteByte(self.s.skinnum);
ServerSend.SV_Multicast(tr.endpos, Constants.MULTICAST_PVS);
}
break;
}
ignore = tr.ent;
Math3D.VectorCopy(tr.endpos, start);
}
Math3D.VectorCopy(tr.endpos, self.s.old_origin);
self.nextthink = GameBase.level.time + Constants.FRAMETIME;
return true;
}
};
public static EntityUseAdapter target_laser_use = new EntityUseAdapter() {
public String getID() { return "target_laser_use"; }
public void use(Entity self, Entity other, Entity activator) {
self.activator = activator;
if ((self.spawnflags & 1) != 0)
target_laser_off(self);
else
target_laser_on(self);
}
};
static EntityThinkAdapter target_laser_start = new EntityThinkAdapter() {
public String getID() { return "target_laser_start"; }
public boolean think(Entity self) {
self.movetype = Constants.MOVETYPE_NONE;
self.solid = Constants.SOLID_NOT;
self.s.renderfx |= Constants.RF_BEAM | Constants.RF_TRANSLUCENT;
self.s.modelindex = 1; // must be non-zero
// set the beam diameter
if ((self.spawnflags & 64) != 0)
self.s.frame = 16;
else
self.s.frame = 4;
// set the color
if ((self.spawnflags & 2) != 0)
self.s.skinnum = 0xf2f2f0f0;
else if ((self.spawnflags & 4) != 0)
self.s.skinnum = 0xd0d1d2d3;
else if ((self.spawnflags & 8) != 0)
self.s.skinnum = 0xf3f3f1f1;
else if ((self.spawnflags & 16) != 0)
self.s.skinnum = 0xdcdddedf;
else if ((self.spawnflags & 32) != 0)
self.s.skinnum = 0xe0e1e2e3;
if (null == self.enemy) {
if (self.target != null) {
EntityIterator edit = GameBase.G_Find(null, GameBase.findByTarget,
self.target);
if (edit == null)
ServerGame.PF_dprintf(self.classname + " at "
+ Lib.vtos(self.s.origin) + ": " + self.target
+ " is a bad target\n");
self.enemy = edit.o;
} else {
GameBase.G_SetMovedir(self.s.angles, self.movedir);
}
}
self.use = target_laser_use;
self.think = target_laser_think;
if (0 == self.dmg)
self.dmg = 1;
Math3D.VectorSet(self.mins, -8, -8, -8);
Math3D.VectorSet(self.maxs, 8, 8, 8);
World.SV_LinkEdict(self);
if ((self.spawnflags & 1) != 0)
target_laser_on(self);
else
target_laser_off(self);
return true;
}
};
/**
* QUAKED target_lightramp (0 .5 .8) (-8 -8 -8) (8 8 8) TOGGLE speed How
* many seconds the ramping will take message two letters; starting
* lightlevel and ending lightlevel
*/
static EntityThinkAdapter target_lightramp_think = new EntityThinkAdapter() {
public String getID() { return "target_lightramp_think"; }
public boolean think(Entity self) {
char tmp[] = {(char) ('a' + (int) (self.movedir[0] + (GameBase.level.time - self.timestamp)
/ Constants.FRAMETIME * self.movedir[2]))};
ServerGame.PF_Configstring(Constants.CS_LIGHTS + self.enemy.style, new String(tmp));
if ((GameBase.level.time - self.timestamp) < self.speed) {
self.nextthink = GameBase.level.time + Constants.FRAMETIME;
} else if ((self.spawnflags & 1) != 0) {
char temp;
temp = (char) self.movedir[0];
self.movedir[0] = self.movedir[1];
self.movedir[1] = temp;
self.movedir[2] *= -1;
}
return true;
}
};
static EntityUseAdapter target_lightramp_use = new EntityUseAdapter() {
public String getID() { return "target_lightramp_use"; }
public void use(Entity self, Entity other, Entity activator) {
if (self.enemy == null) {
Entity e;
// check all the targets
e = null;
EntityIterator es = null;
while (true) {
es = GameBase
.G_Find(es, GameBase.findByTarget, self.target);
if (es == null)
break;
e = es.o;
if (Lib.strcmp(e.classname, "light") != 0) {
ServerGame.PF_dprintf(self.classname + " at "
+ Lib.vtos(self.s.origin));
ServerGame.PF_dprintf("target " + self.target + " ("
+ e.classname + " at " + Lib.vtos(e.s.origin)
+ ") is not a light\n");
} else {
self.enemy = e;
}
}
if (null == self.enemy) {
ServerGame.PF_dprintf(self.classname + " target "
+ self.target + " not found at "
+ Lib.vtos(self.s.origin) + "\n");
GameUtil.G_FreeEdict(self);
return;
}
}
self.timestamp = GameBase.level.time;
target_lightramp_think.think(self);
}
};
/**
* QUAKED target_earthquake (1 0 0) (-8 -8 -8) (8 8 8) When triggered, this
* initiates a level-wide earthquake. All players and monsters are affected.
* "speed" severity of the quake (default:200) "count" duration of the quake
* (default:5)
*/
static EntityThinkAdapter target_earthquake_think = new EntityThinkAdapter() {
public String getID() { return "target_earthquake_think"; }
public boolean think(Entity self) {
int i;
Entity e;
if (self.last_move_time < GameBase.level.time) {
ServerSend.SV_StartSound(self.s.origin, self, Constants.CHAN_AUTO, self.noise_index, 1.0f,
(float) Constants.ATTN_NONE, (float) 0);
self.last_move_time = GameBase.level.time + 0.5f;
}
for (i = 1; i < GameBase.num_edicts; i++) {
e = GameBase.g_edicts[i];
if (!e.inuse)
continue;
if (null == e.client)
continue;
if (null == e.groundentity)
continue;
e.groundentity = null;
e.velocity[0] += Lib.crandom() * 150;
e.velocity[1] += Lib.crandom() * 150;
e.velocity[2] = self.speed * (100.0f / e.mass);
}
if (GameBase.level.time < self.timestamp)
self.nextthink = GameBase.level.time + Constants.FRAMETIME;
return true;
}
};
static EntityUseAdapter target_earthquake_use = new EntityUseAdapter() {
public String getID() { return "target_earthquake_use"; }
public void use(Entity self, Entity other, Entity activator) {
self.timestamp = GameBase.level.time + self.count;
self.nextthink = GameBase.level.time + Constants.FRAMETIME;
self.activator = activator;
self.last_move_time = 0;
}
};
}