/*
* 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 java.util.*;
import com.googlecode.gwtquake.shared.client.ClientMonsterMethods;
import com.googlecode.gwtquake.shared.common.CM;
import com.googlecode.gwtquake.shared.common.Com;
import com.googlecode.gwtquake.shared.common.Constants;
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.ServerSend;
import com.googlecode.gwtquake.shared.server.World;
import com.googlecode.gwtquake.shared.util.*;
public class Monster {
// FIXME monsters should call these with a totally accurate direction
// and we can mess it up based on skill. Spread should be for normal
// and we can tighten or loosen based on skill. We could muck with
// the damages too, but I'm not sure that's such a good idea.
public static void monster_fire_bullet(Entity self, float[] start,
float[] dir, int damage, int kick, int hspread, int vspread,
int flashtype) {
GameWeapon.fire_bullet(self, start, dir, damage, kick, hspread, vspread,
Constants.MOD_UNKNOWN);
ServerGame.PF_WriteByte(Constants.svc_muzzleflash2);
ServerGame.PF_WriteShort(self.index);
ServerGame.PF_WriteByte(flashtype);
ServerSend.SV_Multicast(start, Constants.MULTICAST_PVS);
}
/** The Moster fires the shotgun. */
public static void monster_fire_shotgun(Entity self, float[] start,
float[] aimdir, int damage, int kick, int hspread, int vspread,
int count, int flashtype) {
GameWeapon.fire_shotgun(self, start, aimdir, damage, kick, hspread, vspread,
count, Constants.MOD_UNKNOWN);
ServerGame.PF_WriteByte(Constants.svc_muzzleflash2);
ServerGame.PF_WriteShort(self.index);
ServerGame.PF_WriteByte(flashtype);
ServerSend.SV_Multicast(start, Constants.MULTICAST_PVS);
}
/** The Moster fires the blaster. */
public static void monster_fire_blaster(Entity self, float[] start,
float[] dir, int damage, int speed, int flashtype, int effect) {
GameWeapon.fire_blaster(self, start, dir, damage, speed, effect, false);
ServerGame.PF_WriteByte(Constants.svc_muzzleflash2);
ServerGame.PF_WriteShort(self.index);
ServerGame.PF_WriteByte(flashtype);
ServerSend.SV_Multicast(start, Constants.MULTICAST_PVS);
}
/** The Moster fires the grenade. */
public static void monster_fire_grenade(Entity self, float[] start,
float[] aimdir, int damage, int speed, int flashtype) {
GameWeapon
.fire_grenade(self, start, aimdir, damage, speed, 2.5f,
damage + 40);
ServerGame.PF_WriteByte(Constants.svc_muzzleflash2);
ServerGame.PF_WriteShort(self.index);
ServerGame.PF_WriteByte(flashtype);
ServerSend.SV_Multicast(start, Constants.MULTICAST_PVS);
}
/** The Moster fires the rocket. */
public static void monster_fire_rocket(Entity self, float[] start,
float[] dir, int damage, int speed, int flashtype) {
GameWeapon.fire_rocket(self, start, dir, damage, speed, damage + 20, damage);
ServerGame.PF_WriteByte(Constants.svc_muzzleflash2);
ServerGame.PF_WriteShort(self.index);
ServerGame.PF_WriteByte(flashtype);
ServerSend.SV_Multicast(start, Constants.MULTICAST_PVS);
}
/** The Moster fires the railgun. */
public static void monster_fire_railgun(Entity self, float[] start,
float[] aimdir, int damage, int kick, int flashtype) {
GameWeapon.fire_rail(self, start, aimdir, damage, kick);
ServerGame.PF_WriteByte(Constants.svc_muzzleflash2);
ServerGame.PF_WriteShort(self.index);
ServerGame.PF_WriteByte(flashtype);
ServerSend.SV_Multicast(start, Constants.MULTICAST_PVS);
}
/** The Moster fires the bfg. */
public static void monster_fire_bfg(Entity self, float[] start,
float[] aimdir, int damage, int speed, int kick,
float damage_radius, int flashtype) {
GameWeapon.fire_bfg(self, start, aimdir, damage, speed, damage_radius);
ServerGame.PF_WriteByte(Constants.svc_muzzleflash2);
ServerGame.PF_WriteShort(self.index);
ServerGame.PF_WriteByte(flashtype);
ServerSend.SV_Multicast(start, Constants.MULTICAST_PVS);
}
/*
* ================ monster_death_use
*
* When a monster dies, it fires all of its targets with the current enemy
* as activator. ================
*/
public static void monster_death_use(Entity self) {
self.flags &= ~(Constants.FL_FLY | Constants.FL_SWIM);
self.monsterinfo.aiflags &= Constants.AI_GOOD_GUY;
if (self.item != null) {
GameItems.Drop_Item(self, self.item);
self.item = null;
}
if (self.deathtarget != null)
self.target = self.deathtarget;
if (self.target == null)
return;
GameUtil.G_UseTargets(self, self.enemy);
}
// ============================================================================
public static boolean monster_start(Entity self) {
if (GameBase.deathmatch.value != 0) {
GameUtil.G_FreeEdict(self);
return false;
}
if ((self.spawnflags & 4) != 0
&& 0 == (self.monsterinfo.aiflags & Constants.AI_GOOD_GUY)) {
self.spawnflags &= ~4;
self.spawnflags |= 1;
// gi.dprintf("fixed spawnflags on %s at %s\n", self.classname,
// vtos(self.s.origin));
}
if (0 == (self.monsterinfo.aiflags & Constants.AI_GOOD_GUY))
GameBase.level.total_monsters++;
self.nextthink = GameBase.level.time + Constants.FRAMETIME;
self.svflags |= Constants.SVF_MONSTER;
self.s.renderfx |= Constants.RF_FRAMELERP;
self.takedamage = Constants.DAMAGE_AIM;
self.air_finished = GameBase.level.time + 12;
self.use = GameUtil.monster_use;
self.max_health = self.health;
self.clipmask = Constants.MASK_MONSTERSOLID;
self.s.skinnum = 0;
self.deadflag = Constants.DEAD_NO;
self.svflags &= ~Constants.SVF_DEADMONSTER;
if (null == self.monsterinfo.checkattack)
self.monsterinfo.checkattack = GameUtil.M_CheckAttack;
Math3D.VectorCopy(self.s.origin, self.s.old_origin);
if (GameBase.st.item != null && GameBase.st.item.length() > 0) {
self.item = GameItems.FindItemByClassname(GameBase.st.item);
if (self.item == null)
ServerGame.PF_dprintf("monster_start:" + self.classname + " at "
+ Lib.vtos(self.s.origin) + " has bad item: "
+ GameBase.st.item + "\n");
}
// randomize what frame they start on
if (self.monsterinfo.currentmove != null)
self.s.frame = self.monsterinfo.currentmove.firstframe
+ (Lib.rand() % (self.monsterinfo.currentmove.lastframe
- self.monsterinfo.currentmove.firstframe + 1));
return true;
}
public static void monster_start_go(Entity self) {
float[] v = { 0, 0, 0 };
if (self.health <= 0)
return;
// check for target to combat_point and change to combattarget
if (self.target != null) {
boolean notcombat;
boolean fixup;
Entity target = null;
notcombat = false;
fixup = false;
/*
* if (true) { Com.Printf("all entities:\n");
*
* for (int n = 0; n < Game.globals.num_edicts; n++) { edict_t ent =
* GameBase.g_edicts[n]; Com.Printf( "|%4i | %25s
* |%8.2f|%8.2f|%8.2f||%8.2f|%8.2f|%8.2f||%8.2f|%8.2f|%8.2f|\n", new
* Vargs().add(n).add(ent.classname).
* add(ent.s.origin[0]).add(ent.s.origin[1]).add(ent.s.origin[2])
* .add(ent.mins[0]).add(ent.mins[1]).add(ent.mins[2])
* .add(ent.maxs[0]).add(ent.maxs[1]).add(ent.maxs[2])); }
* sleep(10); }
*/
EntityIterator edit = null;
while ((edit = GameBase.G_Find(edit, GameBase.findByTarget,
self.target)) != null) {
target = edit.o;
if (Lib.strcmp(target.classname, "point_combat") == 0) {
self.combattarget = self.target;
fixup = true;
} else {
notcombat = true;
}
}
if (notcombat && self.combattarget != null)
ServerGame.PF_dprintf(self.classname + " at "
+ Lib.vtos(self.s.origin)
+ " has target with mixed types\n");
if (fixup)
self.target = null;
}
// validate combattarget
if (self.combattarget != null) {
Entity target = null;
EntityIterator edit = null;
while ((edit = GameBase.G_Find(edit, GameBase.findByTarget,
self.combattarget)) != null) {
target = edit.o;
if (Lib.strcmp(target.classname, "point_combat") != 0) {
ServerGame.PF_dprintf(self.classname + " at "
+ Lib.vtos(self.s.origin)
+ " has bad combattarget " + self.combattarget
+ " : " + target.classname + " at "
+ Lib.vtos(target.s.origin));
}
}
}
if (self.target != null) {
self.goalentity = self.movetarget = GameBase
.G_PickTarget(self.target);
if (null == self.movetarget) {
ServerGame.PF_dprintf(self.classname + " can't find target "
+ self.target + " at "
+ Lib.vtos(self.s.origin) + "\n");
self.target = null;
self.monsterinfo.pausetime = 100000000;
self.monsterinfo.stand.think(self);
} else if (Lib.strcmp(self.movetarget.classname, "path_corner") == 0) {
Math3D.VectorSubtract(self.goalentity.s.origin, self.s.origin,
v);
self.ideal_yaw = self.s.angles[Constants.YAW] = Math3D
.vectoyaw(v);
self.monsterinfo.walk.think(self);
self.target = null;
} else {
self.goalentity = self.movetarget = null;
self.monsterinfo.pausetime = 100000000;
self.monsterinfo.stand.think(self);
}
} else {
self.monsterinfo.pausetime = 100000000;
self.monsterinfo.stand.think(self);
}
self.think = Monster.monster_think;
self.nextthink = GameBase.level.time + Constants.FRAMETIME;
}
public static EntityThinkAdapter monster_think = new EntityThinkAdapter() {
public String getID() { return "monster_think";}
public boolean think(Entity self) {
ClientMonsterMethods.M_MoveFrame(self);
if (self.linkcount != self.monsterinfo.linkcount) {
self.monsterinfo.linkcount = self.linkcount;
ClientMonsterMethods.M_CheckGround(self);
}
ClientMonsterMethods.M_CatagorizePosition(self);
ClientMonsterMethods.M_WorldEffects(self);
ClientMonsterMethods.M_SetEffects(self);
return true;
}
};
public static EntityThinkAdapter monster_triggered_spawn = new EntityThinkAdapter() {
public String getID() { return "monster_trigger_spawn";}
public boolean think(Entity self) {
self.s.origin[2] += 1;
GameUtil.KillBox(self);
self.solid = Constants.SOLID_BBOX;
self.movetype = Constants.MOVETYPE_STEP;
self.svflags &= ~Constants.SVF_NOCLIENT;
self.air_finished = GameBase.level.time + 12;
World.SV_LinkEdict(self);
Monster.monster_start_go(self);
if (self.enemy != null && 0 == (self.spawnflags & 1)
&& 0 == (self.enemy.flags & Constants.FL_NOTARGET)) {
GameUtil.FoundTarget(self);
} else {
self.enemy = null;
}
return true;
}
};
// we have a one frame delay here so we don't telefrag the guy who activated
// us
public static EntityUseAdapter monster_triggered_spawn_use = new EntityUseAdapter() {
public String getID() { return "monster_trigger_spawn_use";}
public void use(Entity self, Entity other, Entity activator) {
self.think = monster_triggered_spawn;
self.nextthink = GameBase.level.time + Constants.FRAMETIME;
if (activator.client != null)
self.enemy = activator;
self.use = GameUtil.monster_use;
}
};
public static EntityThinkAdapter monster_triggered_start = new EntityThinkAdapter() {
public String getID() { return "monster_triggered_start";}
public boolean think(Entity self) {
if (self.index == 312)
Com.Printf("monster_triggered_start\n");
self.solid = Constants.SOLID_NOT;
self.movetype = Constants.MOVETYPE_NONE;
self.svflags |= Constants.SVF_NOCLIENT;
self.nextthink = 0;
self.use = monster_triggered_spawn_use;
return true;
}
};
}