Extract original source code from wfsource421.zip, dated 1999
This commit is contained in:
commit
c480e687aa
|
@ -0,0 +1,53 @@
|
||||||
|
ref number for editing the bots.cfg
|
||||||
|
makes bots pick thier favorite weapon the most
|
||||||
|
make sure you edit in the right place...
|
||||||
|
|
||||||
|
# WF3.4
|
||||||
|
2 :Shotgun :All
|
||||||
|
3 :Super Shotgun :Recon
|
||||||
|
4 :MachineGun :Cyborg
|
||||||
|
5 :ChainGun :Gunner
|
||||||
|
6 :GrenadeLauncher :none class10 file?
|
||||||
|
7 :RocketLauncher :Marine
|
||||||
|
8 :RailGun :Recon
|
||||||
|
9 :HyperBlaster :Marine
|
||||||
|
0 :BFG10k :None
|
||||||
|
10 :SniperRifle :Sniper /not done yet
|
||||||
|
11 :LightningGun :Cyborg
|
||||||
|
12 :InfectedDartLauncher :Nurse
|
||||||
|
13 :PulseCannon :Gunner
|
||||||
|
14 :TeslaCoil :Engineer
|
||||||
|
15 :FlameThrower :Arsonist
|
||||||
|
16 :PelletRocketLauncher :Marine
|
||||||
|
17 :NapalmRocketLauncher :Arsonist
|
||||||
|
18 :ClusterRocketlauncher :Cyborg
|
||||||
|
19 :Needler :Nurse
|
||||||
|
20 :SHC :Arsonist
|
||||||
|
21 :HandGrenades /Normal :All
|
||||||
|
22 :ArmorDartLauncher :WF Engineer
|
||||||
|
23 :AK47 :Merc
|
||||||
|
24 :Pistol :Merc
|
||||||
|
xx :FlareGun :Arsonist /not done yet
|
||||||
|
xx :MegaChainGun :Gunner /not done yet
|
||||||
|
xx :TranquilizerDart :Spy /not done yet
|
||||||
|
xx :Knife :Spy/Merc /not done yet
|
||||||
|
xx :Stinger :Merc /not done yet
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
To add items to maps
|
||||||
|
ie type ctf_item item_armor_combat and where you are standing combat armor
|
||||||
|
will be there next time you load the map,unsure if you need to have bot_calc_nodes 1,but it does save this to the rtz file so you might,to remove all items type clear_items.if the level is already routed and you have to
|
||||||
|
turn on bot_calc_nodes to drop these make sure you turn it back off after you
|
||||||
|
drop something otherwise when you move you'll be adding extra routing info,
|
||||||
|
if this is the inital routing of a map dont worry about it.
|
||||||
|
|
||||||
|
partial list of dropable items:
|
||||||
|
item_armor_combat
|
||||||
|
item_armor_body
|
||||||
|
ammo_slugs
|
||||||
|
ammo_rockets
|
||||||
|
ammo_grenades
|
||||||
|
ammo_cells
|
||||||
|
ammo_shells
|
||||||
|
ammo_bullets
|
||||||
|
|
||||||
|
weapons will not work they are removed automatically from maps.....
|
|
@ -0,0 +1,247 @@
|
||||||
|
######################################################################
|
||||||
|
# Makefile for WF for Quake2
|
||||||
|
#
|
||||||
|
# Note: If you developed your source files on a Win32 machine, type
|
||||||
|
# "make stripcr" to get rid of any stray carriage returns that
|
||||||
|
# may be lurking in your files or make sure that you use ASCII
|
||||||
|
# mode to transfer via FTP.
|
||||||
|
#
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# C_OBJS (Custom Objects): You should use this group below to
|
||||||
|
# specify any custom files required by you that are not
|
||||||
|
# included in id's sources. e.g. if your mod requires the addition
|
||||||
|
# of custom files "foo.c" and "bar.c", you'd add:
|
||||||
|
#
|
||||||
|
# C_OBJS = foo.o bar.o
|
||||||
|
#
|
||||||
|
######################################################################
|
||||||
|
C_OBJS = alarm.o \
|
||||||
|
b_biosentry.o \
|
||||||
|
b_healingdepot.o \
|
||||||
|
b_missile.o \
|
||||||
|
b_supplydepot.o \
|
||||||
|
b_turret.o \
|
||||||
|
bot_ai.o \
|
||||||
|
bot_die.o \
|
||||||
|
bot_items.o \
|
||||||
|
bot_misc.o \
|
||||||
|
bot_nav.o \
|
||||||
|
bot_spawn.o \
|
||||||
|
bot_wpns.o \
|
||||||
|
camclient.o \
|
||||||
|
debug.o \
|
||||||
|
dwm.o \
|
||||||
|
g_ai.o \
|
||||||
|
g_chase.o \
|
||||||
|
g_cmds.o \
|
||||||
|
g_combat.o \
|
||||||
|
g_ctf.o \
|
||||||
|
g_ent.o \
|
||||||
|
g_func.o \
|
||||||
|
g_items.o \
|
||||||
|
g_main.o \
|
||||||
|
g_misc.o \
|
||||||
|
g_monster.o \
|
||||||
|
g_phys.o \
|
||||||
|
g_save.o \
|
||||||
|
g_spawn.o \
|
||||||
|
g_svcmds.o \
|
||||||
|
g_target.o \
|
||||||
|
g_trigger.o \
|
||||||
|
g_utils.o \
|
||||||
|
g_weapon.o \
|
||||||
|
grapple.o \
|
||||||
|
j_diesease.o \
|
||||||
|
j_fire.o \
|
||||||
|
j_kamikaze.o \
|
||||||
|
m_flash.o \
|
||||||
|
m_move.o \
|
||||||
|
p_client.o \
|
||||||
|
p_hud.o \
|
||||||
|
p_menu.o \
|
||||||
|
p_trail.o \
|
||||||
|
p_view.o \
|
||||||
|
p_weapon.o \
|
||||||
|
q_devels.o \
|
||||||
|
q_shared.o \
|
||||||
|
r_weap.o \
|
||||||
|
remotecam.o \
|
||||||
|
stdlog.o \
|
||||||
|
throwup.o \
|
||||||
|
w_ak47.o \
|
||||||
|
w_armordart.o \
|
||||||
|
w_boltedblaster.o \
|
||||||
|
w_cgprojectilelauncher.o \
|
||||||
|
w_clustermissiles.o \
|
||||||
|
w_concussion.o \
|
||||||
|
w_flamethrower.o \
|
||||||
|
w_flare.o \
|
||||||
|
w_flaregun.o \
|
||||||
|
w_gasgrenade.o \
|
||||||
|
w_infectdartlauncher.o \
|
||||||
|
w_lasercutter.o \
|
||||||
|
w_lasersight.o \
|
||||||
|
w_laserweapons.o \
|
||||||
|
w_lightninggun.o \
|
||||||
|
w_magnotron.o \
|
||||||
|
w_mbpc.o \
|
||||||
|
w_megachaingun.o \
|
||||||
|
w_nag.o \
|
||||||
|
w_nailgun.o \
|
||||||
|
w_napalm.o \
|
||||||
|
w_napalmmissiles.o \
|
||||||
|
w_needler.o \
|
||||||
|
w_other.o \
|
||||||
|
w_pelletmissile.o \
|
||||||
|
w_pistol.o \
|
||||||
|
w_plague.o \
|
||||||
|
w_plaguetime.o \
|
||||||
|
w_plasmabomb.o \
|
||||||
|
w_poisondart.o \
|
||||||
|
w_sentrykiller.o \
|
||||||
|
w_shc.o \
|
||||||
|
w_shock.o \
|
||||||
|
w_shrapnal.o \
|
||||||
|
w_slowgrenade.o \
|
||||||
|
w_sniperrifle.o \
|
||||||
|
w_stinger.o \
|
||||||
|
w_telsacoil.o \
|
||||||
|
w_tranquilizer.o \
|
||||||
|
wf_classmgr.o \
|
||||||
|
wf_cluster.o \
|
||||||
|
wf_config.o \
|
||||||
|
wf_decoy.o \
|
||||||
|
wf_earthquake.o \
|
||||||
|
wf_feign.o \
|
||||||
|
wf_fileio.o \
|
||||||
|
wf_flagcap.o \
|
||||||
|
wf_flame.o \
|
||||||
|
wf_flash.o \
|
||||||
|
wf_freezer.o \
|
||||||
|
wf_friends.o \
|
||||||
|
wf_goodyear.o \
|
||||||
|
wf_ipban.o \
|
||||||
|
wf_jet.o \
|
||||||
|
wf_knife.o \
|
||||||
|
wf_laserball.o \
|
||||||
|
wf_maplist.o \
|
||||||
|
wf_misc.o \
|
||||||
|
wf_napalm.o \
|
||||||
|
wf_pipebomb.o \
|
||||||
|
wf_proximity.o \
|
||||||
|
wf_quake.o \
|
||||||
|
wf_referee.o \
|
||||||
|
wf_scanner.o \
|
||||||
|
wf_tempents.o \
|
||||||
|
wf_turret.o \
|
||||||
|
x_weap.o
|
||||||
|
|
||||||
|
# g_unzip.o
|
||||||
|
# g_zip.o
|
||||||
|
|
||||||
|
# Game-related objects
|
||||||
|
G_OBJS =
|
||||||
|
|
||||||
|
# Monster-related objects
|
||||||
|
M_OBJS =
|
||||||
|
|
||||||
|
# Player-related objects
|
||||||
|
P_OBJS =
|
||||||
|
|
||||||
|
# Quake2-related objects
|
||||||
|
Q_OBJS =
|
||||||
|
|
||||||
|
# This will compile the original C_OBJS along with any extra files that
|
||||||
|
# you defined above
|
||||||
|
#
|
||||||
|
OBJS = $(C_OBJS) $(G_OBJS) $(M_OBJS) $(P_OBJS) $(Q_OBJS)
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# UNCOMMENT BELOW LINES FOR WF DEPENDING ON OS
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
# Uncomment for Linux
|
||||||
|
TARGET = gamei386.so
|
||||||
|
|
||||||
|
# Uncomment for Solaris
|
||||||
|
#TARGET = gamesparc.so
|
||||||
|
|
||||||
|
CC = gcc
|
||||||
|
# CC = cc
|
||||||
|
|
||||||
|
SHELL = /bin/sh
|
||||||
|
|
||||||
|
# This #define saves us from having to change stricmp() to
|
||||||
|
# strcasecmp() in the actual sources, which means we can leave
|
||||||
|
# the source code as portable as possible (Win32 doesn't like
|
||||||
|
# strcasecmp() any more than Linux likes stricmp()). By doing
|
||||||
|
# it this way the same source code can be built under both
|
||||||
|
# platforms unchanged.
|
||||||
|
#
|
||||||
|
BASE_CFLAGS = -Dstricmp=strcasecmp
|
||||||
|
|
||||||
|
# These build objects optimized for speed rather than size on Linux.
|
||||||
|
#CFLAGS = $(BASE_CFLAGS) -O2 -DC_ONLY -ffast-math -funroll-loops \
|
||||||
|
# -fomit-frame-pointer -fexpensive-optimizations -malign-loops=2 \
|
||||||
|
# -malign-jumps=2 -malign-functions=2 -fno-strength-reduce -Wall
|
||||||
|
#CFLAGS = $(BASE_CFLAGS) -O3 -DC_ONLY -ffast-math \
|
||||||
|
# -fomit-frame-pointer -fexpensive-optimizations -malign-loops=2 \
|
||||||
|
# -malign-jumps=2 -malign-functions=2 -fno-strength-reduce -Wall
|
||||||
|
# -fverbose-asm -S -g
|
||||||
|
|
||||||
|
# This builds optimized for speed under Solaris
|
||||||
|
#CFLAGS = $(BASE_CFLAGS) -O2 -DC_ONLY -ffast-math -funroll-loops \
|
||||||
|
# -fomit-frame-pointer -fexpensive-optimizations \
|
||||||
|
# -fno-strength-reduce -Wall
|
||||||
|
|
||||||
|
# Use this for testing/debugging under Linux or Solaris
|
||||||
|
CFLAGS = $(BASE_CFLAGS) -O2 -g -DC_ONLY
|
||||||
|
|
||||||
|
# Linker flags for building a shared library (*.so).
|
||||||
|
#
|
||||||
|
# Redhat Linux users don't need -ldl or -lm...
|
||||||
|
LDFLAGS =
|
||||||
|
|
||||||
|
# but Slackware people do
|
||||||
|
#LDFLAGS = -ldl -lm
|
||||||
|
|
||||||
|
SHLIBCFLAGS = -fPIC
|
||||||
|
SHLIBLDFLAGS = -shared
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# Targets
|
||||||
|
######################################################################
|
||||||
|
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
.c.o:
|
||||||
|
$(CC) $(CFLAGS) $(SHLIBCFLAGS) -o $@ -c $<
|
||||||
|
|
||||||
|
$(TARGET): $(OBJS)
|
||||||
|
$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(OBJS) $(LDFLAGS)
|
||||||
|
# strip $(TARGET)
|
||||||
|
|
||||||
|
dep:
|
||||||
|
@echo "Updating dependencies..."
|
||||||
|
@$(CC) -MM $(OBJS:.o=.c) > .depend
|
||||||
|
|
||||||
|
stripcr: .
|
||||||
|
@echo "Stripping carriage returns from source files..."
|
||||||
|
@for f in *.[ch]; do \
|
||||||
|
cat $$f | tr -d '\015' > .stripcr; \
|
||||||
|
mv .stripcr $$f; \
|
||||||
|
done; \
|
||||||
|
rm -f .stripcr
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@echo "Deleting WF temporary and compiled files..."
|
||||||
|
@rm -f $(OBJS) *.orig ~* core *.so
|
||||||
|
@echo "Restoring p_trail.o..."
|
||||||
|
@cp p_trail.o.sav p_trail.o
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
@echo "Deleting everything that can be rebuilt..."
|
||||||
|
@rm -f $(TARGET) .depend
|
|
@ -0,0 +1,290 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
//void Cmd_WFPlayTeam (edict_t *self, char *wavename);
|
||||||
|
|
||||||
|
void alarm_remove(edict_t *self)
|
||||||
|
{
|
||||||
|
if (self == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (self->owner && self->owner->client)
|
||||||
|
{
|
||||||
|
// --self->owner->client->pers.active_special[ITEM_SPECIAL_ALARMS];
|
||||||
|
self->owner->alarm1 = NULL;
|
||||||
|
}
|
||||||
|
G_FreeEdict (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void alarm_die (edict_t *self, edict_t * inflictor, edict_t * attacker, int damage, vec3_t point)
|
||||||
|
{
|
||||||
|
self->takedamage=DAMAGE_NO;
|
||||||
|
//T_RadiusDamage (self, self->owner, 20, NULL, 10,0);
|
||||||
|
// BANG !
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_EXPLOSION1);
|
||||||
|
gi.WritePosition(self->s.origin);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||||
|
G_FreeEdict (self);
|
||||||
|
// --self->owner->client->pers.active_special[ITEM_SPECIAL_ALARMS];
|
||||||
|
}
|
||||||
|
|
||||||
|
void alarmthink(edict_t *ent)
|
||||||
|
{
|
||||||
|
edict_t *blip;
|
||||||
|
|
||||||
|
if (level.time > ent->delay) //don't exploce on contact
|
||||||
|
{
|
||||||
|
alarm_remove (ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
blip = NULL;
|
||||||
|
while ((blip = findradius (blip, ent->s.origin, 256)) != NULL)
|
||||||
|
{
|
||||||
|
if (!blip->client)
|
||||||
|
continue;
|
||||||
|
// Don't sound alarm for team mates
|
||||||
|
if (blip->wf_team == ent->wf_team)
|
||||||
|
continue;
|
||||||
|
// Don't sound alarm for dead enemies
|
||||||
|
if (blip->health <= 0)
|
||||||
|
continue;
|
||||||
|
// Don't sound alarm for folks we can't see
|
||||||
|
if (!visible(ent, blip))
|
||||||
|
continue;
|
||||||
|
// Don't sound alarm for spies 95% of the time
|
||||||
|
if ((blip->disguised) && (random() < 0.95))
|
||||||
|
continue;
|
||||||
|
// Sound alarm for everyone else
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (blip != NULL)
|
||||||
|
{
|
||||||
|
if ( ent->wf_team == CTF_TEAM1)
|
||||||
|
{
|
||||||
|
// play Team 1's sound
|
||||||
|
gi.sound (ent, CHAN_WEAPON, gi.soundindex ("alarm.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// play Team 2's sound
|
||||||
|
gi.sound (ent, CHAN_WEAPON, gi.soundindex ("ctf/tech1.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
// let the player know their alarm was triggered
|
||||||
|
safe_cprintf (ent->owner, PRINT_HIGH, "Alarm triggered by %s\n", blip->client->pers.netname);
|
||||||
|
// don't check for another second because we are playing the sound for one second
|
||||||
|
ent->nextthink = level.time + 1.0;
|
||||||
|
//If this is an enemy spy, tell the team!
|
||||||
|
// if (ent->owner->client->player_special & SPECIAL_DISGUISE)
|
||||||
|
// {
|
||||||
|
// Cmd_WFPlayTeam(ent->owner, "radio/d_spy.wav");
|
||||||
|
// }
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// check next frmae because we are idle
|
||||||
|
ent->nextthink = level.time + 0.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void place_alarm (int number,edict_t *ent)
|
||||||
|
{
|
||||||
|
|
||||||
|
vec3_t forward,
|
||||||
|
wallp;
|
||||||
|
|
||||||
|
trace_t tr;
|
||||||
|
|
||||||
|
// valid ent ?
|
||||||
|
if ((!ent->client) || (ent->health<=0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// cells for laser ?
|
||||||
|
if (ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] < 25)
|
||||||
|
{
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "Not enough cells for an Alarm.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Are there too many alarms now?
|
||||||
|
/* if (ent->client->pers.active_special[ITEM_SPECIAL_ALARMS] >= MAX_SPECIAL_ALARMS)
|
||||||
|
{
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "You can only have %d active Alarms.\n",MAX_SPECIAL_ALARMS );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Setup "little look" to close wall
|
||||||
|
VectorCopy(ent->s.origin,wallp);
|
||||||
|
|
||||||
|
// Cast along view angle
|
||||||
|
AngleVectors (ent->client->v_angle, forward, NULL, NULL);
|
||||||
|
|
||||||
|
// Setup end point
|
||||||
|
wallp[0]=ent->s.origin[0]+forward[0]*50;
|
||||||
|
wallp[1]=ent->s.origin[1]+forward[1]*50;
|
||||||
|
wallp[2]=ent->s.origin[2]+forward[2]*50;
|
||||||
|
|
||||||
|
// trace
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, wallp, ent, MASK_SOLID);
|
||||||
|
|
||||||
|
// Line complete ? (ie. no collision)
|
||||||
|
if (tr.fraction == 1.0)
|
||||||
|
{
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Too far from wall.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hit sky ?
|
||||||
|
if (tr.surface)
|
||||||
|
if (tr.surface->flags & SURF_SKY)
|
||||||
|
return;
|
||||||
|
ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] -= 25;
|
||||||
|
|
||||||
|
// ++ent->client->pers.active_special[ITEM_SPECIAL_ALARMS];
|
||||||
|
|
||||||
|
|
||||||
|
if (number == 1)
|
||||||
|
{
|
||||||
|
if (ent->alarm1)
|
||||||
|
{
|
||||||
|
alarm_remove (ent->alarm1);
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "Alarm off.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "Alarm on.\n");
|
||||||
|
ent->alarm1 = G_Spawn();
|
||||||
|
VectorClear (ent->alarm1->mins);
|
||||||
|
VectorClear (ent->alarm1->maxs);
|
||||||
|
VectorCopy (tr.endpos, ent->alarm1->s.origin);
|
||||||
|
vectoangles(tr.plane.normal,ent->alarm1 -> s.angles);
|
||||||
|
ent->alarm1 -> movetype = MOVETYPE_NONE;
|
||||||
|
ent->alarm1 -> clipmask = MASK_SHOT;
|
||||||
|
//grenade -> solid = SOLID_NOT;
|
||||||
|
ent->alarm1->solid = SOLID_BBOX;
|
||||||
|
VectorSet(ent->alarm1->mins, -3, -3, 0);
|
||||||
|
VectorSet(ent->alarm1->maxs, 3, 3, 6);
|
||||||
|
// ent->alarm1->takedamage=DAMAGE_YES;
|
||||||
|
ent->alarm1->takedamage=DAMAGE_NO;
|
||||||
|
ent->alarm1->mass = 2;
|
||||||
|
ent->alarm1 -> s.modelindex = gi.modelindex (GRNORMAL_MODEL);
|
||||||
|
ent->alarm1 -> owner = ent;
|
||||||
|
// ent->alarm1->die = alarm_die;
|
||||||
|
ent->alarm1 -> nextthink = level.time + 0.2;
|
||||||
|
ent->alarm1 -> think = alarmthink;
|
||||||
|
ent->alarm1->classname = "Alarm";
|
||||||
|
ent->alarm1->health= 60;
|
||||||
|
ent->alarm1->max_health =60;
|
||||||
|
ent->alarm1->wf_team = ent->wf_team;
|
||||||
|
ent->alarm1->delay = level.time + 120.0;
|
||||||
|
gi.linkentity (ent->alarm1);
|
||||||
|
}
|
||||||
|
else if (number == 2)
|
||||||
|
{
|
||||||
|
if (ent->alarm2)
|
||||||
|
{
|
||||||
|
G_FreeEdict(ent->alarm2);
|
||||||
|
ent->alarm2 = NULL;
|
||||||
|
gi.bprintf (PRINT_HIGH, "Alarm 2 off.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gi.bprintf (PRINT_HIGH, "Alarm 2 on.\n");
|
||||||
|
ent->alarm2 = G_Spawn();
|
||||||
|
VectorClear (ent->alarm2->mins);
|
||||||
|
VectorClear (ent->alarm2->maxs);
|
||||||
|
VectorCopy (tr.endpos, ent->alarm2->s.origin);
|
||||||
|
vectoangles(tr.plane.normal,ent->alarm2 -> s.angles);
|
||||||
|
ent->alarm2 -> movetype = MOVETYPE_NONE;
|
||||||
|
ent->alarm2 -> clipmask = MASK_SHOT;
|
||||||
|
//grenade -> solid = SOLID_NOT;
|
||||||
|
ent->alarm2->solid = SOLID_BBOX;
|
||||||
|
VectorSet(ent->alarm2->mins, -3, -3, 0);
|
||||||
|
VectorSet(ent->alarm2->maxs, 3, 3, 6);
|
||||||
|
ent->alarm2->takedamage=DAMAGE_YES;
|
||||||
|
ent->alarm2->mass = 2;
|
||||||
|
ent->alarm2 -> s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2");
|
||||||
|
ent->alarm2 -> owner = ent;
|
||||||
|
ent->alarm2->die = alarm_die;
|
||||||
|
ent->alarm2 -> nextthink = level.time + 0.1;
|
||||||
|
ent->alarm2 -> think = alarmthink;
|
||||||
|
ent->alarm2->health= 60;
|
||||||
|
ent->alarm2->max_health =60;
|
||||||
|
ent->alarm2->classname = "Alarm";
|
||||||
|
ent->alarm2->wf_team = ent->wf_team;
|
||||||
|
gi.linkentity (ent->alarm2);
|
||||||
|
}
|
||||||
|
else if (number == 3)
|
||||||
|
{
|
||||||
|
if (ent->alarm3)
|
||||||
|
{
|
||||||
|
G_FreeEdict(ent->alarm3);
|
||||||
|
ent->alarm3 = NULL;
|
||||||
|
gi.bprintf (PRINT_HIGH, "Alarm 3 off.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gi.bprintf (PRINT_HIGH, "Alarm 3 on.\n");
|
||||||
|
ent->alarm1 = G_Spawn();
|
||||||
|
VectorClear (ent->alarm3->mins);
|
||||||
|
VectorClear (ent->alarm3->maxs);
|
||||||
|
VectorCopy (tr.endpos, ent->alarm3->s.origin);
|
||||||
|
vectoangles(tr.plane.normal,ent->alarm3 -> s.angles);
|
||||||
|
ent->alarm3 -> movetype = MOVETYPE_NONE;
|
||||||
|
ent->alarm3 -> clipmask = MASK_SHOT;
|
||||||
|
//grenade -> solid = SOLID_NOT;
|
||||||
|
ent->alarm3->solid = SOLID_BBOX;
|
||||||
|
VectorSet(ent->alarm3->mins, -3, -3, 0);
|
||||||
|
VectorSet(ent->alarm3->maxs, 3, 3, 6);
|
||||||
|
ent->alarm3->takedamage=DAMAGE_YES;
|
||||||
|
ent->alarm3->mass = 2;
|
||||||
|
ent->alarm3->die = alarm_die;
|
||||||
|
ent->alarm3 -> s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2");
|
||||||
|
ent->alarm3 -> owner = ent;
|
||||||
|
ent->alarm3 -> nextthink = level.time + 0.1;
|
||||||
|
ent->alarm3 -> think = alarmthink;
|
||||||
|
ent->alarm3->health= 60;
|
||||||
|
ent->alarm3->max_health =60;
|
||||||
|
ent->alarm3->classname = "Alarm";
|
||||||
|
ent->alarm3->wf_team = ent->wf_team;
|
||||||
|
gi.linkentity (ent->alarm3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void cmd_Alarm(edict_t *ent)
|
||||||
|
{
|
||||||
|
char *string;
|
||||||
|
|
||||||
|
string = gi.args();
|
||||||
|
|
||||||
|
if (!ent->client) return;
|
||||||
|
|
||||||
|
//argument = "on", "off"
|
||||||
|
if (Q_stricmp ( string, "on") == 0)
|
||||||
|
{
|
||||||
|
if (!ent->alarm1)
|
||||||
|
{
|
||||||
|
alarm_remove(ent->alarm1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Q_stricmp ( string, "off") == 0)
|
||||||
|
{
|
||||||
|
if (ent->alarm1)
|
||||||
|
{
|
||||||
|
alarm_remove(ent->alarm1);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ent->alarm1)
|
||||||
|
{
|
||||||
|
alarm_remove(ent->alarm1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
place_alarm(1, ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
if (ent->anti)
|
||||||
|
{
|
||||||
|
blip = NULL;
|
||||||
|
while ((blip = findradius(blip, ent->s.origin, 500)) != NULL)
|
||||||
|
{
|
||||||
|
|
||||||
|
//The only non-client to track is sentry guns
|
||||||
|
if ((strcmp(blip->classname, "rocket") != 0) && (strcmp(blip->classname, "proximity") != 0)&&(strcmp(blip->classname, "turret") != 0)&&(strcmp(blip->classname, "napalm grenade") != 0)&&(strcmp(blip->classname, "goodyear") != 0)&&(strcmp(blip->classname, "grenade") != 0))
|
||||||
|
continue;
|
||||||
|
if(ent == blip->owner)
|
||||||
|
continue;
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, blip->s.origin, ent, MASK_SOLID);
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
continue;
|
||||||
|
ent->enemy = blip;
|
||||||
|
}
|
||||||
|
if(ent->enemy)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (ent->client->pers.inventory[ITEM_INDEX(FindItem("bullets"))]>0)
|
||||||
|
{
|
||||||
|
ent->client->pers.inventory[ITEM_INDEX(FindItem("bullets"))]--;
|
||||||
|
|
||||||
|
start[0] = ent->s.origin[0];
|
||||||
|
start[1] = ent->s.origin[1];
|
||||||
|
start[2] = ent->s.origin[2];
|
||||||
|
|
||||||
|
// calc direction to where we targetd
|
||||||
|
VectorMA (ent->enemy->s.origin, -0.05, ent->enemy->velocity, target);
|
||||||
|
|
||||||
|
|
||||||
|
VectorSubtract (target, start, dir);
|
||||||
|
VectorNormalize (dir);
|
||||||
|
|
||||||
|
//fire bullet
|
||||||
|
fire_bullet (ent, start, dir, 8, 0, 125, 125, MOD_SENTRY);
|
||||||
|
|
||||||
|
// send muzzle flash
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
gi.WriteShort (ent - g_edicts);
|
||||||
|
gi.WriteByte (MZ_SHOTGUN );
|
||||||
|
gi.multicast (start, MULTICAST_PVS);
|
||||||
|
gi.sound(ent, CHAN_VOICE, gi.soundindex("boss3\xfire.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
ent->enemy=NULL;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,795 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
#include "m_player.h"
|
||||||
|
#define newBio self->Bio1
|
||||||
|
#define newBio2 self->Bio1->Bio2
|
||||||
|
#define IdleStart 0
|
||||||
|
#define IdleEnd 2
|
||||||
|
#define AttackStart 3
|
||||||
|
#define AttackEnd 7
|
||||||
|
#define AttackFire 7
|
||||||
|
#define StatusIdle 0
|
||||||
|
#define StatusAttack 1
|
||||||
|
|
||||||
|
void BecomeExplosion1 (edict_t *self);
|
||||||
|
void SP_Biosentry (edict_t *self);
|
||||||
|
//void fire_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread);
|
||||||
|
void BiosentryFire1(edict_t *self);
|
||||||
|
void Biosentry_self_remove(edict_t *ent);
|
||||||
|
void Biosentry_remove(edict_t *ent);
|
||||||
|
void BiosentryReload(edict_t *ent, pmenu_t *p);
|
||||||
|
void BiosentryRepair(edict_t *ent, pmenu_t *p);
|
||||||
|
void fire_infecteddart (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, int mod);
|
||||||
|
|
||||||
|
|
||||||
|
void BiosentryAnimate(edict_t *ent)
|
||||||
|
{
|
||||||
|
if(ent->delay==StatusIdle)
|
||||||
|
{
|
||||||
|
if (ent->s.frame >= IdleEnd)
|
||||||
|
{
|
||||||
|
ent->s.frame = IdleStart;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ent->delay==StatusAttack)
|
||||||
|
{
|
||||||
|
if(ent->s.frame < AttackStart)
|
||||||
|
{
|
||||||
|
ent->s.frame = AttackStart;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(ent->s.frame >= AttackEnd)
|
||||||
|
{
|
||||||
|
ent->s.frame = IdleStart;
|
||||||
|
ent->delay = StatusIdle;
|
||||||
|
ent->enemy = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ent->s.frame++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Add or subtract ammo from biosentry
|
||||||
|
void UpdateBiosentryAmmo(edict_t *ent, int change_amt)
|
||||||
|
{
|
||||||
|
ent->light_level += change_amt;
|
||||||
|
if (ent->light_level == 25)
|
||||||
|
{
|
||||||
|
safe_cprintf(ent->creator, PRINT_HIGH, "Warning: Your biosentry is low on ammo.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void FireBiosentry(edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t forward, right, start, target, dir;
|
||||||
|
vec3_t temp_vel;
|
||||||
|
|
||||||
|
temp_vel[0] = 0;
|
||||||
|
temp_vel[1] = 0;
|
||||||
|
temp_vel[2] = 0;
|
||||||
|
|
||||||
|
if(ent->light_level<=0)
|
||||||
|
{
|
||||||
|
Biosentry_self_remove(ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateBiosentryAmmo(ent, -1);
|
||||||
|
|
||||||
|
AngleVectors (ent->s.angles, forward, right, NULL);
|
||||||
|
|
||||||
|
start[0] = ent->s.origin[0] + forward[0] * 3 ;
|
||||||
|
start[1] = ent->s.origin[1] + forward[1] * 3 ;
|
||||||
|
start[2] = ent->s.origin[2] + forward[2] * 3+4;
|
||||||
|
|
||||||
|
// calc direction to where we targetd
|
||||||
|
// VectorMA (ent->enemy->s.origin, -0.05, ent->enemy->velocity, target);
|
||||||
|
VectorMA (ent->enemy->s.origin, -0.05, temp_vel, target);
|
||||||
|
|
||||||
|
//Adjust for height
|
||||||
|
// target[2] += ent->enemy->viewheight/1.5;
|
||||||
|
|
||||||
|
VectorSubtract (target, start, dir);
|
||||||
|
VectorNormalize (dir);
|
||||||
|
|
||||||
|
//fire dart
|
||||||
|
fire_infecteddart (ent, start, dir, 20, 500, EF_GIB, MOD_BIOSENTRY);
|
||||||
|
|
||||||
|
// send muzzle flash
|
||||||
|
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
gi.WriteShort (ent - g_edicts);
|
||||||
|
gi.WriteByte (MZ_SHOTGUN );
|
||||||
|
gi.multicast (start, MULTICAST_PVS);
|
||||||
|
// gi.sound(ent, CHAN_VOICE, gi.soundindex("boss3/xfire.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Biosentry_Think(edict_t *self)
|
||||||
|
{
|
||||||
|
static char Bio[32];
|
||||||
|
static char Armor[32];
|
||||||
|
static char Ammo[32];
|
||||||
|
static char Level[32];
|
||||||
|
int contents;
|
||||||
|
edict_t *blip;
|
||||||
|
float dist;
|
||||||
|
vec3_t v;
|
||||||
|
trace_t tr;
|
||||||
|
// gitem_t *ammo;
|
||||||
|
// int max;
|
||||||
|
float checkyaw;
|
||||||
|
blip = NULL;
|
||||||
|
|
||||||
|
//gi.error("john");
|
||||||
|
|
||||||
|
//If there is no client attached to this Bio any more, it should be removed
|
||||||
|
//from the game
|
||||||
|
if (!self->creator) //There is no creator
|
||||||
|
{
|
||||||
|
Biosentry_self_remove(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->creator->client) //Creator isn't a client
|
||||||
|
{
|
||||||
|
Biosentry_self_remove(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Make sure that the creator of this biosentry also has an
|
||||||
|
//entry that points back to this Bio
|
||||||
|
if (!self->creator->sentry) //creator doesn't know about this Bio
|
||||||
|
{
|
||||||
|
Biosentry_self_remove(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->creator->sentry != self) //creator has a different Bio
|
||||||
|
{
|
||||||
|
Biosentry_self_remove(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
contents = (int)gi.pointcontents(self->s.origin);
|
||||||
|
|
||||||
|
if (contents & CONTENTS_SOLID)
|
||||||
|
{
|
||||||
|
safe_cprintf(self->creator, PRINT_HIGH, "Your biosentry was in a bad map position, so it was removed.\n");//5/99
|
||||||
|
Biosentry_self_remove(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(self->enemy)
|
||||||
|
{
|
||||||
|
if(self->enemy->health<=0)
|
||||||
|
{
|
||||||
|
self->enemy= NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Play sound if we are ready
|
||||||
|
// --self->turretsoundcountdown;
|
||||||
|
|
||||||
|
if(!self->enemy)
|
||||||
|
{
|
||||||
|
self->delay=StatusIdle;
|
||||||
|
//Reduce range
|
||||||
|
while (blip = findradius (blip, self->s.origin, 500))
|
||||||
|
{
|
||||||
|
if (!blip->client)
|
||||||
|
continue; //not a player
|
||||||
|
if (blip->wf_team != self->wf_team) //shoot only at team mates
|
||||||
|
continue;
|
||||||
|
if (blip->solid == SOLID_NOT)
|
||||||
|
continue; //don't see observers
|
||||||
|
if (blip->disguised)
|
||||||
|
continue;
|
||||||
|
if (blip->health <= 0 || blip->health >= 100)
|
||||||
|
continue;
|
||||||
|
tr = gi.trace (self->s.origin, NULL, NULL, blip->s.origin, self, MASK_SOLID);
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
continue;
|
||||||
|
VectorSubtract (self->s.origin, blip->s.origin, v);
|
||||||
|
|
||||||
|
dist = VectorLength(v);
|
||||||
|
|
||||||
|
if (!visible(self, blip) && dist > 300)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
self->enemy = blip;
|
||||||
|
self->delay = StatusAttack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VectorSubtract (self->enemy->s.origin, self->s.origin, v);
|
||||||
|
|
||||||
|
self->ideal_yaw = vectoyaw(v);
|
||||||
|
|
||||||
|
M_ChangeYaw(self);
|
||||||
|
|
||||||
|
checkyaw = anglemod(self->s.angles[YAW])-self->ideal_yaw;
|
||||||
|
|
||||||
|
if (checkyaw>-25 && checkyaw<25)
|
||||||
|
{
|
||||||
|
if (self->light_level>0)
|
||||||
|
{
|
||||||
|
if (visible(self, self->enemy))
|
||||||
|
{
|
||||||
|
if (self->s.frame == AttackFire)
|
||||||
|
{
|
||||||
|
FireBiosentry(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->delay=StatusIdle;
|
||||||
|
self->enemy=NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->delay=StatusIdle;
|
||||||
|
self->enemy=NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BiosentryAnimate(self);
|
||||||
|
self->nextthink = level.time + 0.1;
|
||||||
|
|
||||||
|
if(self->delay==StatusIdle)
|
||||||
|
{
|
||||||
|
if(self->sentrydelay<level.time)
|
||||||
|
{
|
||||||
|
if(self->PlasmaDelay)
|
||||||
|
self->PlasmaDelay=0;
|
||||||
|
else
|
||||||
|
self->PlasmaDelay=1;
|
||||||
|
gi.sound(self, CHAN_VOICE, gi.soundindex("misc/comp_up.wav"), 0.1, ATTN_NORM, 0);
|
||||||
|
self->sentrydelay=level.time + 2;
|
||||||
|
}
|
||||||
|
if(self->PlasmaDelay)
|
||||||
|
self->s.angles[YAW]+=3.5;
|
||||||
|
else
|
||||||
|
self->s.angles[YAW]-=3.5;
|
||||||
|
if(self->s.angles[YAW]<0)
|
||||||
|
self->s.angles[YAW]+=360;
|
||||||
|
else if(self->s.angles[YAW]>360)
|
||||||
|
self->s.angles[YAW]-=360;
|
||||||
|
}
|
||||||
|
//if(self->health<self->max_health)
|
||||||
|
// self->health++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Ent = Bio entity
|
||||||
|
void Biosentry_self_remove(edict_t *ent)
|
||||||
|
{
|
||||||
|
|
||||||
|
//Clear client's pointer to Biosentry
|
||||||
|
if (ent->creator)
|
||||||
|
ent->creator->sentry = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
//Then free the biosentry
|
||||||
|
BecomeExplosion1 (ent);
|
||||||
|
// G_FreeEdict(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Ent = Player entity
|
||||||
|
void Biosentry_remove(edict_t *ent)
|
||||||
|
{
|
||||||
|
if (ent->sentry)
|
||||||
|
{ //42 ebc
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "biosentry off.\n");
|
||||||
|
|
||||||
|
|
||||||
|
//free the Biosentry
|
||||||
|
BecomeExplosion1 (ent->sentry);
|
||||||
|
// G_FreeEdict(ent->sentry);
|
||||||
|
|
||||||
|
ent->sentry = NULL;
|
||||||
|
|
||||||
|
if (ent->client->oldplayer)
|
||||||
|
G_FreeEdict(ent->client->oldplayer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Biosentry_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||||
|
{
|
||||||
|
|
||||||
|
edict_t *blip = NULL;
|
||||||
|
int i;
|
||||||
|
vec3_t origin;
|
||||||
|
|
||||||
|
//42 bot sentry clear itemnode owner
|
||||||
|
if (self->creator->bot_client)
|
||||||
|
for (i=1, blip=g_edicts+i ; i < globals.num_edicts ; i++,blip++)
|
||||||
|
{
|
||||||
|
if (blip->owner == self->creator)
|
||||||
|
{
|
||||||
|
if (!strcmp(blip->classname, "item_sentryspot") )
|
||||||
|
{
|
||||||
|
blip->owner = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//42 end clear item owner
|
||||||
|
|
||||||
|
//42 scbc
|
||||||
|
safe_cprintf(self->creator, PRINT_HIGH, "biosentry Destroyed.\n");
|
||||||
|
|
||||||
|
//Give a frag to the attacker
|
||||||
|
if (attacker->client && attacker->wf_team != self->wf_team)
|
||||||
|
{
|
||||||
|
attacker->client->resp.score++;
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorCopy (self->s.origin,origin);
|
||||||
|
origin[2]+= 0.5;
|
||||||
|
self->takedamage = DAMAGE_NO;
|
||||||
|
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_EXPLOSION1);
|
||||||
|
gi.WritePosition (origin);
|
||||||
|
gi.multicast (origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
Biosentry_self_remove(self);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void BiosentryReload(edict_t *ent, pmenu_t *p)
|
||||||
|
{
|
||||||
|
int max, armorfill;
|
||||||
|
float dist;
|
||||||
|
vec3_t distance;
|
||||||
|
if (!ent->selectedsentry)
|
||||||
|
{
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "No bio-sentry selected\n");
|
||||||
|
}
|
||||||
|
distance[0]=ent->s.origin[0] - ent->selectedsentry->s.origin[0];
|
||||||
|
distance[1]=ent->s.origin[1] - ent->selectedsentry->s.origin[1];
|
||||||
|
distance[2]=ent->s.origin[2] - ent->selectedsentry->s.origin[2];
|
||||||
|
dist=VectorLength(distance);
|
||||||
|
if(dist>100)
|
||||||
|
{
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "Biosentry too far away.\n");
|
||||||
|
PMenu_Close(ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ent->selectedsentry->light_level!=ent->selectedsentry->gib_health)
|
||||||
|
{
|
||||||
|
max = ent->selectedsentry->gib_health;
|
||||||
|
armorfill=ent->selectedsentry->gib_health-ent->selectedsentry->light_level;
|
||||||
|
if(armorfill>75)
|
||||||
|
armorfill=75;
|
||||||
|
// if(armorfill>25)
|
||||||
|
// armorfill=25;
|
||||||
|
//42 ebc
|
||||||
|
if (!ent->bot_client && ent->client->pers.inventory[ITEM_INDEX(FindItem("Bullets"))] < 25)
|
||||||
|
{
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "You need 25 Bullets to reload the biosentry.\n");
|
||||||
|
if (ent->selectedsentry) gi.sound(ent->selectedsentry, CHAN_VOICE, gi.soundindex("misc/keytry.wav"), 1, ATTN_NORM, 0);
|
||||||
|
PMenu_Close(ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//25 player bullets will give 75 bullets to biosentry (Gregg)
|
||||||
|
// if(armorfill > ent->client->pers.inventory[ITEM_INDEX(FindItem("Bullets"))])
|
||||||
|
// armorfill = ent->client->pers.inventory[ITEM_INDEX(FindItem("Bullets"))];
|
||||||
|
|
||||||
|
UpdateBiosentryAmmo(ent->selectedsentry, armorfill);
|
||||||
|
// ent->selectedsentry->light_level += armorfill;
|
||||||
|
ent->client->pers.inventory[ITEM_INDEX(FindItem("Bullets"))] -= 25;
|
||||||
|
if (ent->selectedsentry->light_level > max)
|
||||||
|
ent->selectedsentry->light_level = max;
|
||||||
|
gi.sound(ent->selectedsentry, CHAN_ITEM, gi.soundindex("misc/w_pkup.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
PMenu_Close(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddArmorToBiosentry(edict_t *ent, int amt)
|
||||||
|
{
|
||||||
|
int max, armor;
|
||||||
|
|
||||||
|
if (ent->health < ent->max_health)
|
||||||
|
{
|
||||||
|
max = ent->max_health;
|
||||||
|
|
||||||
|
//How much armor is needed?
|
||||||
|
armor = ent->max_health - ent->health;
|
||||||
|
|
||||||
|
if (amt < armor)
|
||||||
|
armor = amt;
|
||||||
|
else
|
||||||
|
armor = 0;
|
||||||
|
|
||||||
|
ent->health+= armor;
|
||||||
|
if (ent->health > max)
|
||||||
|
ent->health = max;
|
||||||
|
gi.sound(ent->selectedsentry, CHAN_ITEM, gi.soundindex("misc/w_pkup.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BiosentryRepair(edict_t *ent, pmenu_t *p)
|
||||||
|
{
|
||||||
|
int max, armor;
|
||||||
|
int cells, maxcells, currcells;
|
||||||
|
float dist;
|
||||||
|
vec3_t distance;
|
||||||
|
if (!ent->selectedsentry)
|
||||||
|
{//42 ebc
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "No bio-sentry selected\n");
|
||||||
|
}
|
||||||
|
distance[0]=ent->s.origin[0] - ent->selectedsentry->s.origin[0];
|
||||||
|
distance[1]=ent->s.origin[1] - ent->selectedsentry->s.origin[1];
|
||||||
|
distance[2]=ent->s.origin[2] - ent->selectedsentry->s.origin[2];
|
||||||
|
dist=VectorLength(distance);
|
||||||
|
if(dist>100)
|
||||||
|
{
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "Biosentry too far away.\n");
|
||||||
|
PMenu_Close(ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ent->selectedsentry->health<ent->selectedsentry->max_health)
|
||||||
|
{
|
||||||
|
max = ent->selectedsentry->max_health;
|
||||||
|
|
||||||
|
//How much armor is needed?
|
||||||
|
armor = ent->selectedsentry->max_health - ent->selectedsentry->health;
|
||||||
|
|
||||||
|
//Can't give more than 100 points armor in one shot
|
||||||
|
if(armor > 100) armor=100;
|
||||||
|
|
||||||
|
//Each cell gives 4 points armor
|
||||||
|
currcells = ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))];
|
||||||
|
maxcells = armor / 4;
|
||||||
|
|
||||||
|
if (currcells ==0)
|
||||||
|
{//42 ebc
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "You need Cells to repair the biosentry.\n");
|
||||||
|
if (ent->selectedsentry) gi.sound(ent->selectedsentry, CHAN_VOICE, gi.soundindex("misc/keytry.wav"), 1, ATTN_NORM, 0);
|
||||||
|
|
||||||
|
PMenu_Close(ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we don't have enough cells, adjust the amount of armor to give
|
||||||
|
if(currcells < maxcells)
|
||||||
|
{
|
||||||
|
cells = currcells;
|
||||||
|
armor = cells * 4;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cells = maxcells;
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->selectedsentry->health+= armor;
|
||||||
|
ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] -= cells;
|
||||||
|
if (ent->selectedsentry->health > max)
|
||||||
|
ent->selectedsentry->health = max;
|
||||||
|
gi.sound(ent->selectedsentry, CHAN_ITEM, gi.soundindex("misc/w_pkup.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
PMenu_Close(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Biosentry_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
if (other->bot_client)
|
||||||
|
return;//42
|
||||||
|
|
||||||
|
if (other->client) other->selectedsentry = ent;
|
||||||
|
|
||||||
|
//Skip if they already have a menu up
|
||||||
|
if ((other->client) && (other->client->menu))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(other->sentrydelay>level.time)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//Only show menu a max of every 1 second
|
||||||
|
|
||||||
|
//Only allow team mates access to menu
|
||||||
|
//if(!other->bot_client)
|
||||||
|
if (other->client && (other->wf_team == ent->wf_team) && (other->client->player_special & SPECIAL_BIOSENTRY))
|
||||||
|
{
|
||||||
|
PMenu_Close(other);
|
||||||
|
|
||||||
|
sprintf(other->client->wfsentrystr[0], "*%s's Bio", ent->creator->client->pers.netname);
|
||||||
|
sprintf(other->client->wfsentrystr[1], " Armor:%d/%d", ent->health, ent->max_health);
|
||||||
|
sprintf(other->client->wfsentrystr[2], " Ammo:%d/%d", ent->light_level,ent->gib_health);
|
||||||
|
sprintf(other->client->wfsentrystr[3], " Level:%d", ent->count);
|
||||||
|
other->client->sentrymenu[0].text = other->client->wfsentrystr[0];
|
||||||
|
other->client->sentrymenu[0].SelectFunc = NULL;
|
||||||
|
other->client->sentrymenu[0].align = PMENU_ALIGN_CENTER;
|
||||||
|
other->client->sentrymenu[0].arg = 0;
|
||||||
|
other->client->sentrymenu[1].text = other->client->wfsentrystr[1];
|
||||||
|
other->client->sentrymenu[1].SelectFunc = NULL;
|
||||||
|
other->client->sentrymenu[1].align = PMENU_ALIGN_LEFT;
|
||||||
|
other->client->sentrymenu[1].arg = 0;
|
||||||
|
other->client->sentrymenu[2].text = other->client->wfsentrystr[2];
|
||||||
|
other->client->sentrymenu[2].SelectFunc = NULL;
|
||||||
|
other->client->sentrymenu[2].align = PMENU_ALIGN_LEFT;
|
||||||
|
other->client->sentrymenu[2].arg = 0;
|
||||||
|
other->client->sentrymenu[3].text = other->client->wfsentrystr[3];
|
||||||
|
other->client->sentrymenu[3].SelectFunc = NULL;
|
||||||
|
other->client->sentrymenu[3].align = PMENU_ALIGN_LEFT;
|
||||||
|
other->client->sentrymenu[3].arg = 0;
|
||||||
|
other->client->sentrymenu[5].text = "1. Repair";
|
||||||
|
other->client->sentrymenu[5].SelectFunc = BiosentryRepair;
|
||||||
|
other->client->sentrymenu[5].align = PMENU_ALIGN_LEFT;
|
||||||
|
other->client->sentrymenu[5].arg = 0;
|
||||||
|
other->client->sentrymenu[6].text = "2. Reload";
|
||||||
|
other->client->sentrymenu[6].SelectFunc = BiosentryReload;
|
||||||
|
other->client->sentrymenu[6].align = PMENU_ALIGN_LEFT;
|
||||||
|
other->client->sentrymenu[6].arg = 0;
|
||||||
|
|
||||||
|
// other->selectedsentry = ent->creator->sentry;
|
||||||
|
other->selectedsentry = ent;
|
||||||
|
|
||||||
|
PMenu_Open(other, other->client->sentrymenu, -1, sizeof(other->client->sentrymenu) / sizeof(pmenu_t), true, false);
|
||||||
|
other->sentrydelay = level.time + 3;
|
||||||
|
|
||||||
|
//Set timeout for menu (4 seconds)
|
||||||
|
if (other->client->menu) other->client->menu->MenuTimeout = level.time + 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Build the Biosentry
|
||||||
|
void PlaceBiosentry (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t forward,up,right,wallp, pos,try1,try2,try3,try4;
|
||||||
|
|
||||||
|
trace_t tr;
|
||||||
|
|
||||||
|
// valid ent ?
|
||||||
|
if ((!ent->client) || (ent->health<=0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/**** DEBUGGING ***/
|
||||||
|
//safe_cprintf(ent, PRINT_HIGH, "Sorry - biosentry disabled while we are testing.\n");
|
||||||
|
//return;
|
||||||
|
/**** DEBUGGING ***/
|
||||||
|
|
||||||
|
if (ent->sentry)
|
||||||
|
{
|
||||||
|
Biosentry_remove(ent);
|
||||||
|
|
||||||
|
if (ent->client->oldplayer)
|
||||||
|
G_FreeEdict(ent->client->oldplayer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cells for biosentry ?
|
||||||
|
if (ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] < 50)
|
||||||
|
{//42 ebc
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "You need 50 cells to create biosentry.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup "little look" to close wall
|
||||||
|
VectorCopy(ent->s.origin,wallp);
|
||||||
|
|
||||||
|
// Cast along view angle
|
||||||
|
AngleVectors (ent->client->v_angle, forward, right, up);
|
||||||
|
|
||||||
|
// Setup end point
|
||||||
|
pos[0]=ent->s.origin[0]+forward[0]*75;
|
||||||
|
pos[1]=ent->s.origin[1]+forward[1]*75;
|
||||||
|
pos[2]=ent->s.origin[2]+forward[2]*75+30;
|
||||||
|
wallp[0]=ent->s.origin[0]+forward[0]*150;
|
||||||
|
wallp[1]=ent->s.origin[1]+forward[1]*150;
|
||||||
|
wallp[2]=ent->s.origin[2]+forward[2]*150+30;
|
||||||
|
try1[0]=ent->s.origin[0]+forward[0]*150+right[0]*20;
|
||||||
|
try1[1]=ent->s.origin[1]+forward[1]*150+right[1]*20;
|
||||||
|
try1[2]=ent->s.origin[2]+forward[2]*150+30+right[2]*20;
|
||||||
|
try2[0]=ent->s.origin[0]+forward[0]*150+right[0]*-20;
|
||||||
|
try2[1]=ent->s.origin[1]+forward[1]*150+right[1]*-20;
|
||||||
|
try2[2]=ent->s.origin[2]+forward[2]*150+30+right[2]*-20;
|
||||||
|
try3[0]=ent->s.origin[0]+forward[0]*75+right[0]*20;
|
||||||
|
try3[1]=ent->s.origin[1]+forward[1]*75+right[1]*20;
|
||||||
|
try3[2]=ent->s.origin[2]+forward[2]*75+30+right[2]*20;
|
||||||
|
try4[0]=ent->s.origin[0]+forward[0]*75+right[0]*-20;
|
||||||
|
try4[1]=ent->s.origin[1]+forward[1]*75+right[1]*-20;
|
||||||
|
try4[2]=ent->s.origin[2]+forward[2]*75+30+right[2]*-20;
|
||||||
|
// trace
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, wallp, ent, MASK_SOLID);
|
||||||
|
|
||||||
|
|
||||||
|
// Line complete ? (ie. no collision)
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
{//42 ebc
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Not enough room.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wallp[2]+=22;
|
||||||
|
tr = gi.trace (pos, NULL, NULL, wallp, ent, MASK_SOLID);
|
||||||
|
// Line complete ? (ie. no collision)
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
{
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Not enough room. Try aiming lower\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wallp[2]-=40;
|
||||||
|
tr = gi.trace (pos, NULL, NULL, wallp, ent, MASK_SOLID);
|
||||||
|
// Line complete ? (ie. no collision)
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
{
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Not enough room. Try aiming higher\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// trace
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, try1, ent, MASK_SOLID);
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
{
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Not enough room.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// trace
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, try2, ent, MASK_SOLID);
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
{
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Not enough room.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// trace
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, try3, ent, MASK_SOLID);
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
{
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Not enough room.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// trace
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, try4, ent, MASK_SOLID);
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
{
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Not enough room.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hit sky ?
|
||||||
|
if (tr.surface)
|
||||||
|
if (tr.surface->flags & SURF_SKY)
|
||||||
|
return;
|
||||||
|
ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] -= 50;
|
||||||
|
|
||||||
|
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "biosentry on.\n");
|
||||||
|
ent->sentry = G_Spawn();
|
||||||
|
|
||||||
|
// ent->sentry->turretsoundcountdown = 0;
|
||||||
|
|
||||||
|
VectorClear (ent->sentry->mins);
|
||||||
|
VectorClear (ent->sentry->maxs);
|
||||||
|
VectorCopy (pos, ent->sentry->s.origin);
|
||||||
|
ent->sentry ->s.angles[0]=ent->s.angles[0];
|
||||||
|
ent->sentry -> movetype = MOVETYPE_STEP;
|
||||||
|
ent->sentry -> clipmask = MASK_PLAYERSOLID;
|
||||||
|
ent->sentry->mass = 400;
|
||||||
|
ent->s.renderfx=RF_FRAMELERP|RF_TRANSLUCENT|RF_GLOW;
|
||||||
|
//grenade -> solid = SOLID_NOT;
|
||||||
|
ent->sentry->solid = SOLID_BBOX;
|
||||||
|
|
||||||
|
VectorSet(ent->sentry->mins, -45,-15,-35);
|
||||||
|
VectorSet(ent->sentry->maxs, 25, 18, 18);
|
||||||
|
|
||||||
|
ent->sentry->takedamage=DAMAGE_YES;
|
||||||
|
ent->sentry -> s.modelindex = gi.modelindex ("models/biosentry/tris.md2");
|
||||||
|
ent->sentry -> creator = ent;
|
||||||
|
ent->sentry ->sentrydelay =level.time+1;
|
||||||
|
ent->sentry->creator=ent;
|
||||||
|
ent->sentry->think = Biosentry_Think;
|
||||||
|
ent->sentry->nextthink = level.time + 0.1;
|
||||||
|
// ent->sentry->touch = Biosentry_Touch;
|
||||||
|
ent->sentry->die = Biosentry_die;
|
||||||
|
ent->sentry->health= 80;
|
||||||
|
ent->sentry->max_health = 100;
|
||||||
|
ent->sentry->count = 1;
|
||||||
|
ent->sentry->s.sound = gi.soundindex ("weapons/biosentry/bioidle.wav");
|
||||||
|
ent->sentry->attenuation =1;
|
||||||
|
ent->sentry->volume = 0.5;
|
||||||
|
ent->sentry->classname = "biosentry";
|
||||||
|
ent->sentry->wf_team = ent->wf_team;
|
||||||
|
ent->sentry->noteamdamage = true; //Don't let teammates damage it
|
||||||
|
ent->sentry->yaw_speed = 35;
|
||||||
|
ent->sentry->gib_health = 75;//Max Ammo
|
||||||
|
ent->sentry->light_level = 75;//Ammo Total
|
||||||
|
|
||||||
|
ent->sentry->delay=StatusIdle;
|
||||||
|
gi.linkentity (ent->sentry);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//test_Bio() - see if there is a Bio close enough to work on
|
||||||
|
|
||||||
|
//Returns 0 if Bio does not exist or is too far away
|
||||||
|
//Returns 1 if Bio exists and is close enough
|
||||||
|
int test_Biosentry (edict_t *ent)
|
||||||
|
{
|
||||||
|
float dist;
|
||||||
|
float tdist;
|
||||||
|
edict_t *Bio;
|
||||||
|
vec3_t distance;
|
||||||
|
edict_t *blip;
|
||||||
|
if (!ent->sentry)
|
||||||
|
{
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "Biosentry not found.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blip = NULL;
|
||||||
|
dist = 0;
|
||||||
|
Bio = NULL;
|
||||||
|
while (blip = findradius (blip, ent->s.origin, 100))
|
||||||
|
{
|
||||||
|
if (strcmp(blip->classname, "biosentry"))
|
||||||
|
continue; //Not a biosentry
|
||||||
|
|
||||||
|
distance[0]=ent->s.origin[0] - blip->s.origin[0];
|
||||||
|
distance[1]=ent->s.origin[1] - blip->s.origin[1];
|
||||||
|
distance[2]=ent->s.origin[2] - blip->s.origin[2];
|
||||||
|
tdist=VectorLength(distance);
|
||||||
|
if ((dist == 0) || (tdist < dist))
|
||||||
|
{
|
||||||
|
dist = tdist;
|
||||||
|
Bio = blip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(dist>100 || Bio == NULL)
|
||||||
|
{
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "Bio too far away.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ent->selectedsentry = Bio;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_Biosentry (edict_t *ent)
|
||||||
|
{
|
||||||
|
char *string;
|
||||||
|
|
||||||
|
string=gi.args();
|
||||||
|
|
||||||
|
if (!ent->client) return;
|
||||||
|
|
||||||
|
//argument = "build", "remove", "repair" and "reload"
|
||||||
|
if (Q_stricmp ( string, "build") == 0)
|
||||||
|
{
|
||||||
|
if (!ent->sentry)
|
||||||
|
PlaceBiosentry(ent);
|
||||||
|
else
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "Bio already exists.\n");
|
||||||
|
}
|
||||||
|
else if (Q_stricmp ( string, "remove") == 0)
|
||||||
|
{
|
||||||
|
if (ent->sentry)
|
||||||
|
Biosentry_remove(ent);
|
||||||
|
else
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "Bio not found.\n");
|
||||||
|
}
|
||||||
|
else if (Q_stricmp ( string, "repair") == 0)
|
||||||
|
{
|
||||||
|
if (test_Biosentry(ent)) BiosentryRepair(ent, NULL);
|
||||||
|
}
|
||||||
|
else if (Q_stricmp ( string, "reload") == 0)
|
||||||
|
{
|
||||||
|
if (test_Biosentry(ent)) BiosentryReload(ent, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Otherwise toggle on/off
|
||||||
|
else if (Q_stricmp ( string, "") == 0)
|
||||||
|
{
|
||||||
|
PlaceBiosentry(ent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "Invalid Bio command.\n");
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,192 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
|
||||||
|
#define newHealingDepot self->supply
|
||||||
|
void SP_HealingDepot(edict_t *self);
|
||||||
|
void Remove_Player_Flames (edict_t *ent);
|
||||||
|
void healingdepot_explode (edict_t *self);
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
Healing Depot
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
|
||||||
|
void HealPlayer(edict_t *ent)
|
||||||
|
{
|
||||||
|
ent->disease= 0;
|
||||||
|
ent->lame = 0;
|
||||||
|
ent->superslow=0;
|
||||||
|
ent->Slower=0;
|
||||||
|
ent->DrunkTime=level.time - 1;
|
||||||
|
ent->client->blindBase = 0;
|
||||||
|
ent->client->blindTime = 0;
|
||||||
|
ent->cantmove = 0;
|
||||||
|
|
||||||
|
Remove_Player_Flames (ent);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void HealingDepotThink (edict_t *self)
|
||||||
|
{
|
||||||
|
edict_t *other;
|
||||||
|
int contents;
|
||||||
|
other = NULL;
|
||||||
|
|
||||||
|
contents = (int)gi.pointcontents(self->s.origin);
|
||||||
|
|
||||||
|
if (contents & CONTENTS_SOLID)
|
||||||
|
{
|
||||||
|
safe_cprintf(self->owner, PRINT_HIGH, "Your healing depot was in a bad map position, so it was removed.\n");//5/99
|
||||||
|
healingdepot_explode (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((other = findradius(other, self->s.origin, 16)) != NULL)
|
||||||
|
{
|
||||||
|
if (other->client)
|
||||||
|
{
|
||||||
|
HealPlayer(other);
|
||||||
|
|
||||||
|
//Give some health
|
||||||
|
if (other->health < 100)
|
||||||
|
{
|
||||||
|
other->health += 5;
|
||||||
|
if (other->health > 100) other->health = 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
//destroy depot for bots if enemy is using it
|
||||||
|
if ((self->wf_team != other->wf_team) && (self->owner->bot_client))
|
||||||
|
{ healingdepot_explode (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((self->wf_team != other->wf_team) && (self->owner))
|
||||||
|
safe_cprintf(self->owner, PRINT_HIGH, "Enemies are using your healing depot!\n");
|
||||||
|
gi.sound(self, CHAN_ITEM, gi.soundindex("ctf/tech4.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self->nextthink = level.time + 0.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
void healingdepot_explode (edict_t *self)
|
||||||
|
{
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
if (self->waterlevel)
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
|
||||||
|
else
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION);
|
||||||
|
gi.WritePosition (self->s.origin);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||||
|
make_debris(self);
|
||||||
|
|
||||||
|
self->takedamage = DAMAGE_NO;
|
||||||
|
|
||||||
|
if (self->owner) safe_cprintf(self->owner, PRINT_HIGH, "Healing Depot Off.\n");
|
||||||
|
|
||||||
|
if ((self->owner) && (self->owner->supply))
|
||||||
|
self->owner->supply = NULL;
|
||||||
|
|
||||||
|
T_RadiusDamage(self, self->owner, self->dmg, NULL, self->dmg_radius, MOD_HEALINGDEPOT);
|
||||||
|
G_FreeEdict(self);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void healingdepot_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||||
|
{
|
||||||
|
edict_t *blip = NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
//42 bot supply clear itemnode owner
|
||||||
|
if (self->owner->bot_client)
|
||||||
|
for (i=1, blip=g_edicts+i ; i < globals.num_edicts ; i++,blip++)
|
||||||
|
{
|
||||||
|
if (blip->owner == self->owner)
|
||||||
|
{
|
||||||
|
if (!strcmp(blip->classname, "item_depotspot") )
|
||||||
|
{
|
||||||
|
blip->owner = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//42 end clear item owner
|
||||||
|
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
if (self->waterlevel)
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
|
||||||
|
else
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION);
|
||||||
|
gi.WritePosition (self->s.origin);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||||
|
make_debris(self);
|
||||||
|
|
||||||
|
self->takedamage = DAMAGE_NO;
|
||||||
|
|
||||||
|
if (self->owner) safe_cprintf(self->owner, PRINT_HIGH, "Healing Depot Destroyed.\n");
|
||||||
|
|
||||||
|
if ((self->owner) && (self->owner->supply))
|
||||||
|
self->owner->supply = NULL;
|
||||||
|
|
||||||
|
T_RadiusDamage(self, self->owner, self->dmg, NULL, self->dmg_radius, MOD_HEALINGDEPOT);
|
||||||
|
G_FreeEdict(self);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_HealingDepot(edict_t *self)
|
||||||
|
{
|
||||||
|
/**** DEBUGGING ***/
|
||||||
|
//safe_cprintf(self, PRINT_HIGH, "Sorry - healing depot disabled while we are testing.\n");
|
||||||
|
//return;
|
||||||
|
/**** DEBUGGING ***/
|
||||||
|
|
||||||
|
if ( newHealingDepot )
|
||||||
|
{
|
||||||
|
healingdepot_explode(newHealingDepot);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cells for laser ?
|
||||||
|
if (self->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] < 10)
|
||||||
|
{
|
||||||
|
safe_cprintf(self, PRINT_HIGH, "Need 10 cells for a Healing Depot.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
safe_cprintf(self, PRINT_HIGH, "Healing Depot on.\n");
|
||||||
|
newHealingDepot = G_Spawn ();
|
||||||
|
VectorCopy(self->s.origin,newHealingDepot->s.origin);
|
||||||
|
newHealingDepot->s.origin[2] += 40;
|
||||||
|
newHealingDepot->classname="healingdepot";
|
||||||
|
newHealingDepot->takedamage=DAMAGE_AIM;
|
||||||
|
newHealingDepot->movetype= MOVETYPE_TOSS;
|
||||||
|
newHealingDepot->mass = 200;
|
||||||
|
newHealingDepot->solid = SOLID_BBOX;
|
||||||
|
newHealingDepot->clipmask=MASK_ALL;
|
||||||
|
newHealingDepot->deadflag =DEAD_NO;
|
||||||
|
newHealingDepot->clipmask = MASK_SHOT;
|
||||||
|
newHealingDepot->model = self->model;
|
||||||
|
newHealingDepot->s.modelindex = gi.modelindex ("models/objects/dmspot/tris.md2");
|
||||||
|
newHealingDepot->s.skinnum = 2;
|
||||||
|
newHealingDepot->solid = SOLID_BBOX;
|
||||||
|
newHealingDepot->noteamdamage = true; //Don't let teammates damage it
|
||||||
|
newHealingDepot->wf_team = self->wf_team;
|
||||||
|
|
||||||
|
VectorSet (newHealingDepot->mins, -32, -32, -24);
|
||||||
|
VectorSet (newHealingDepot->maxs, 32, 32, -16);
|
||||||
|
newHealingDepot->s.frame =0;
|
||||||
|
newHealingDepot->waterlevel = 0;
|
||||||
|
newHealingDepot->watertype=0;
|
||||||
|
newHealingDepot->health= 100;
|
||||||
|
newHealingDepot->max_health =100;
|
||||||
|
newHealingDepot->gib_health = -80;
|
||||||
|
newHealingDepot->die = healingdepot_die;
|
||||||
|
newHealingDepot->owner = self;
|
||||||
|
newHealingDepot->dmg = 150;
|
||||||
|
newHealingDepot->dmg_radius = 160;
|
||||||
|
// newHealingDepot->touch = HealingTouch;
|
||||||
|
newHealingDepot->think =HealingDepotThink;
|
||||||
|
newHealingDepot->nextthink = level.time + 1;
|
||||||
|
VectorClear (newHealingDepot->velocity);
|
||||||
|
gi.linkentity (newHealingDepot);
|
||||||
|
|
||||||
|
self->client->pers.inventory[ITEM_INDEX(FindItem("cells"))]-= 10;
|
||||||
|
|
||||||
|
if (self->client && !self->bot_client ) gi.centerprintf (self,"Healing Depot set!\n");
|
||||||
|
}
|
|
@ -0,0 +1,646 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
#include "m_player.h"
|
||||||
|
#define newTurret self->turret1
|
||||||
|
#define newTurret2 self->turret1->turret2
|
||||||
|
|
||||||
|
|
||||||
|
void BecomeExplosion1 (edict_t *self);
|
||||||
|
|
||||||
|
// WF & CCH: New think function for homing missiles
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
fire_rocket
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
void rocket_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf);
|
||||||
|
|
||||||
|
void mhoming_think (edict_t *ent)
|
||||||
|
{
|
||||||
|
edict_t *target = NULL;
|
||||||
|
edict_t *blip = NULL;
|
||||||
|
vec3_t targetdir, blipdir;
|
||||||
|
vec_t speed;
|
||||||
|
|
||||||
|
while ((blip = findradius(blip, ent->s.origin, 1000)) != NULL)
|
||||||
|
{
|
||||||
|
if (!(blip->svflags & SVF_MONSTER) && !blip->client)
|
||||||
|
{
|
||||||
|
//The only non-client to track is turret grenades and sentry guns
|
||||||
|
if ( (strcmp(blip->classname, "turret") != 0) &&
|
||||||
|
(strcmp(blip->classname, "SentryGun") != 0)&&
|
||||||
|
(strcmp(blip->classname, "MissileTurret") != 0))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (strcmp(blip->classname, "hook") == 0 )
|
||||||
|
continue; //not a grapple 5/99
|
||||||
|
if (blip == ent->creator)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (blip->disguised)
|
||||||
|
continue;
|
||||||
|
//dont aim at same team unless friendly fire is on
|
||||||
|
if ((blip->wf_team == ent->wf_team) && (((int)wfflags->value & WF_ALLOW_FRIENDLY_FIRE)==0))
|
||||||
|
continue;
|
||||||
|
if (!blip->takedamage)
|
||||||
|
continue;
|
||||||
|
if (blip->health <= 0)
|
||||||
|
continue;
|
||||||
|
if (!visible(ent, blip))
|
||||||
|
continue;
|
||||||
|
if (!infront(ent, blip))
|
||||||
|
continue;
|
||||||
|
VectorSubtract(blip->s.origin, ent->s.origin, blipdir);
|
||||||
|
blipdir[2] += 16;
|
||||||
|
if ((target == NULL) || (VectorLength(blipdir) < VectorLength(targetdir)))
|
||||||
|
{
|
||||||
|
target = blip;
|
||||||
|
VectorCopy(blipdir, targetdir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target != NULL)
|
||||||
|
{
|
||||||
|
// target acquired, nudge our direction toward it
|
||||||
|
VectorNormalize(targetdir);
|
||||||
|
VectorScale(targetdir, 0.28, targetdir);
|
||||||
|
VectorAdd(targetdir, ent->movedir, targetdir);
|
||||||
|
VectorNormalize(targetdir);
|
||||||
|
VectorCopy(targetdir, ent->movedir);
|
||||||
|
vectoangles(targetdir, ent->s.angles);
|
||||||
|
speed = VectorLength(ent->velocity);
|
||||||
|
VectorScale(targetdir, speed, ent->velocity);
|
||||||
|
|
||||||
|
//is this the first time we locked in? sound warning for the target
|
||||||
|
if (ent->homing_lock == 0)
|
||||||
|
{
|
||||||
|
gi.sound (target, CHAN_AUTO, gi.soundindex ("homelock.wav"), 1, ATTN_NORM, 0);
|
||||||
|
// gi.sound (target, CHAN_AUTO, gi.soundindex ("misc/keyuse.wav"), 1, ATTN_NORM, 0);
|
||||||
|
ent->homing_lock = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//ent->nextthink = level.time + .1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fire_turretrocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
|
||||||
|
{
|
||||||
|
edict_t *rocket;
|
||||||
|
|
||||||
|
rocket = G_Spawn();
|
||||||
|
VectorCopy (start, rocket->s.origin);
|
||||||
|
VectorCopy (dir, rocket->movedir);
|
||||||
|
vectoangles (dir, rocket->s.angles);
|
||||||
|
VectorScale (dir, speed, rocket->velocity);
|
||||||
|
rocket->movetype = MOVETYPE_FLYMISSILE;
|
||||||
|
rocket->clipmask = MASK_SHOT;
|
||||||
|
rocket->solid = SOLID_BBOX;
|
||||||
|
rocket->s.effects |= EF_ROCKET;
|
||||||
|
VectorClear (rocket->mins);
|
||||||
|
VectorClear (rocket->maxs);
|
||||||
|
rocket->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2");
|
||||||
|
rocket->creator = self;
|
||||||
|
rocket->owner = self;
|
||||||
|
rocket->touch = rocket_touch;
|
||||||
|
|
||||||
|
//WF & CCH - Add homing lock status
|
||||||
|
// rocket->nextthink = level.time + 8000/speed;
|
||||||
|
// rocket->think = G_FreeEdict;
|
||||||
|
|
||||||
|
//Set the team of the rocket
|
||||||
|
rocket->wf_team = self->wf_team;
|
||||||
|
|
||||||
|
rocket->dmg = damage;
|
||||||
|
rocket->radius_dmg = radius_damage;
|
||||||
|
rocket->dmg_radius = damage_radius;
|
||||||
|
|
||||||
|
rocket->nextthink = level.time + .1;
|
||||||
|
rocket->think = mhoming_think;
|
||||||
|
|
||||||
|
//reduce damage of homing rockets to 1/2 of normal rocket
|
||||||
|
rocket->dmg = damage / 2;
|
||||||
|
// rocket->radius_dmg = radius_damage / 2;
|
||||||
|
|
||||||
|
|
||||||
|
//WF
|
||||||
|
|
||||||
|
rocket->s.sound = gi.soundindex ("weapons/rockfly.wav");
|
||||||
|
rocket->classname = "rocket";
|
||||||
|
|
||||||
|
gi.linkentity (rocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MTurret_Think(edict_t *self)
|
||||||
|
{
|
||||||
|
edict_t *blip;
|
||||||
|
float dist;
|
||||||
|
vec3_t v;
|
||||||
|
vec3_t start,forward;
|
||||||
|
trace_t tr;
|
||||||
|
int max;
|
||||||
|
blip = NULL;
|
||||||
|
|
||||||
|
//gi.error("john");
|
||||||
|
|
||||||
|
if(self->enemy)
|
||||||
|
{
|
||||||
|
if(self->enemy->health<=0)
|
||||||
|
self->enemy= NULL;
|
||||||
|
}
|
||||||
|
if (self->light_level!=self->gib_health)
|
||||||
|
{
|
||||||
|
if (self->PlasmaDelay<level.time)
|
||||||
|
{
|
||||||
|
max = self->gib_health;
|
||||||
|
|
||||||
|
if (self->count==3)
|
||||||
|
self->PlasmaDelay = level.time +1;
|
||||||
|
else if (self->count==2)
|
||||||
|
self->PlasmaDelay = level.time +2;
|
||||||
|
else
|
||||||
|
self->PlasmaDelay = level.time +3;
|
||||||
|
self->light_level++;
|
||||||
|
if (self->light_level > max)
|
||||||
|
self->light_level = max;
|
||||||
|
gi.sound(self, CHAN_ITEM, gi.soundindex("misc/w_pkup.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->count == 1)
|
||||||
|
{
|
||||||
|
if(self->delay<level.time)
|
||||||
|
{
|
||||||
|
if(self->light_level)
|
||||||
|
self->s.frame=1;
|
||||||
|
else
|
||||||
|
self->s.frame=0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
self->s.frame=0;
|
||||||
|
if(!self->enemy)
|
||||||
|
{
|
||||||
|
while (blip = findradius (blip, self->s.origin, 1024))
|
||||||
|
{
|
||||||
|
|
||||||
|
//shoot monsters, decoys or players
|
||||||
|
if (!(blip->svflags & SVF_MONSTER) && !blip->client)
|
||||||
|
{
|
||||||
|
//allow it to shoot decoys
|
||||||
|
if (strcmp(blip->classname, "decoy") )
|
||||||
|
continue; //not a decoy
|
||||||
|
}
|
||||||
|
if (strcmp(blip->classname, "hook") == 0 )
|
||||||
|
continue; //not a grapple 5/99
|
||||||
|
if (blip->health <= 0)
|
||||||
|
continue;
|
||||||
|
if (blip == self->creator)
|
||||||
|
continue;
|
||||||
|
if (blip->disguised)
|
||||||
|
continue;
|
||||||
|
if ((blip->wf_team == self->wf_team) && (((int)wfflags->value & WF_ALLOW_FRIENDLY_FIRE)==0))
|
||||||
|
continue;
|
||||||
|
tr = gi.trace (self->s.origin, NULL, NULL, blip->s.origin, self, MASK_SOLID);
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
continue;
|
||||||
|
VectorSubtract (self->s.origin, blip->s.origin, v);
|
||||||
|
|
||||||
|
dist = VectorLength(v);
|
||||||
|
|
||||||
|
if (!visible(self, blip) && dist > 400)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
self->enemy = blip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VectorSubtract (self->enemy->s.origin, self->s.origin, v);
|
||||||
|
|
||||||
|
self->ideal_yaw = vectoyaw(v);
|
||||||
|
|
||||||
|
|
||||||
|
M_ChangeYaw(self);
|
||||||
|
|
||||||
|
if (self->light_level>0)
|
||||||
|
{
|
||||||
|
if ((visible(self, self->enemy))&& (self->delay<level.time))
|
||||||
|
{
|
||||||
|
AngleVectors (self->s.angles, forward, NULL, NULL);
|
||||||
|
start[0] = self->s.origin[0] + forward[0] * 25;
|
||||||
|
start[1] = self->s.origin[1] + forward[1] * 25;
|
||||||
|
start[2] = self->s.origin[2] + forward[2] * 25+10;
|
||||||
|
|
||||||
|
|
||||||
|
// fire_turretrocket (self, start, forward, 30, 700, 150, 50);
|
||||||
|
fire_rocket (self, start, forward, 30, 750, 150, 50, MOD_MISSILE);
|
||||||
|
self->light_level-=1;
|
||||||
|
self->delay = level.time + 1.0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->enemy=NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->enemy=NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (self->count == 2)
|
||||||
|
{
|
||||||
|
|
||||||
|
self->s.frame = self->light_level;
|
||||||
|
if(!self->enemy)
|
||||||
|
{
|
||||||
|
while (blip = findradius (blip, self->s.origin, 2048))
|
||||||
|
{
|
||||||
|
|
||||||
|
//shoot monsters, decoys or players
|
||||||
|
if (!(blip->svflags & SVF_MONSTER) && !blip->client)
|
||||||
|
{
|
||||||
|
//allow it to shoot decoys
|
||||||
|
if (strcmp(blip->classname, "decoy") )
|
||||||
|
continue; //not a decoy
|
||||||
|
}
|
||||||
|
if (strcmp(blip->classname, "hook") == 0 )
|
||||||
|
continue; //not a grapple 5/99
|
||||||
|
if (blip->health <= 0)
|
||||||
|
continue;
|
||||||
|
if (blip == self->creator)
|
||||||
|
continue;
|
||||||
|
if (blip->disguised)
|
||||||
|
continue;
|
||||||
|
if ((blip->wf_team == self->wf_team) && (((int)wfflags->value & WF_ALLOW_FRIENDLY_FIRE)==0))
|
||||||
|
continue;
|
||||||
|
tr = gi.trace (self->s.origin, NULL, NULL, blip->s.origin, self, MASK_SOLID);
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
continue;
|
||||||
|
VectorSubtract (self->s.origin, blip->s.origin, v);
|
||||||
|
|
||||||
|
dist = VectorLength(v);
|
||||||
|
|
||||||
|
if (!visible(self, blip) && dist > 800)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
self->enemy = blip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VectorSubtract (self->enemy->s.origin, self->s.origin, v);
|
||||||
|
|
||||||
|
self->ideal_yaw = vectoyaw(v);
|
||||||
|
|
||||||
|
|
||||||
|
M_ChangeYaw(self);
|
||||||
|
|
||||||
|
if (self->light_level>0)
|
||||||
|
{
|
||||||
|
if ((visible(self, self->enemy))&&(self->delay<level.time))
|
||||||
|
{
|
||||||
|
|
||||||
|
AngleVectors (self->s.angles, forward, NULL, NULL);
|
||||||
|
start[0] = self->s.origin[0] + forward[0] * 25;
|
||||||
|
start[1] = self->s.origin[1] + forward[1] * 25;
|
||||||
|
start[2] = self->s.origin[2] + forward[2] * 25+10;
|
||||||
|
|
||||||
|
|
||||||
|
// fire_turretrocket (self, start, forward, 30, 700, 150, 50);
|
||||||
|
fire_rocket (self, start, forward, 30, 700, 150, 50, MOD_ROCKET);
|
||||||
|
self->light_level-=1;
|
||||||
|
self->delay = level.time + 0.3;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->enemy=NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->enemy=NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (self->count == 3)
|
||||||
|
{
|
||||||
|
self->s.frame =self->light_level;
|
||||||
|
if(!self->enemy)
|
||||||
|
{
|
||||||
|
|
||||||
|
while (blip = findradius (blip, self->s.origin, 2048))
|
||||||
|
{
|
||||||
|
|
||||||
|
//shoot monsters, decoys or players
|
||||||
|
if (!(blip->svflags & SVF_MONSTER) && !blip->client)
|
||||||
|
{
|
||||||
|
//allow it to shoot decoys
|
||||||
|
if (strcmp(blip->classname, "decoy") )
|
||||||
|
continue; //not a decoy
|
||||||
|
}
|
||||||
|
if (strcmp(blip->classname, "hook") == 0 )
|
||||||
|
continue; //not a grapple 5/99
|
||||||
|
if (blip->health <= 0)
|
||||||
|
continue;
|
||||||
|
if (blip == self->creator)
|
||||||
|
continue;
|
||||||
|
if (blip->disguised)
|
||||||
|
continue;
|
||||||
|
if ((blip->wf_team == self->wf_team) && (((int)wfflags->value & WF_ALLOW_FRIENDLY_FIRE)==0))
|
||||||
|
continue;
|
||||||
|
tr = gi.trace (self->s.origin, NULL, NULL, blip->s.origin, self, MASK_SOLID);
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
continue;
|
||||||
|
VectorSubtract (self->s.origin, blip->s.origin, v);
|
||||||
|
|
||||||
|
dist = VectorLength(v);
|
||||||
|
|
||||||
|
if (!visible(self, blip) && dist > 1400)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
self->enemy = blip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VectorSubtract (self->enemy->s.origin, self->s.origin, v);
|
||||||
|
|
||||||
|
self->ideal_yaw = vectoyaw(v);
|
||||||
|
|
||||||
|
|
||||||
|
M_ChangeYaw(self);
|
||||||
|
|
||||||
|
if (self->light_level>0)
|
||||||
|
{
|
||||||
|
if ((visible(self, self->enemy))&&(self->delay<level.time))
|
||||||
|
{
|
||||||
|
AngleVectors (self->s.angles, forward, NULL, NULL);
|
||||||
|
start[0] = self->s.origin[0] + forward[0] * 25;
|
||||||
|
start[1] = self->s.origin[1] + forward[1] * 25;
|
||||||
|
start[2] = self->s.origin[2] + forward[2] * 25+10;
|
||||||
|
|
||||||
|
|
||||||
|
// fire_turretrocket (self, start, forward, 30, 700, 150, 50);
|
||||||
|
fire_rocket (self, start, forward, 30, 700, 150, 50, MOD_ROCKET);
|
||||||
|
self->light_level-=1;
|
||||||
|
self->delay = level.time + 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
self->enemy=NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
self->enemy = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VectorCopy(self->s.origin,self->missile->s.origin);
|
||||||
|
self->nextthink = level.time + 0.1;
|
||||||
|
if(self->health<self->max_health)
|
||||||
|
self->health++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//self = mturret entity
|
||||||
|
void mturret_remove(edict_t *self)
|
||||||
|
{
|
||||||
|
if (self->missile)
|
||||||
|
{
|
||||||
|
G_FreeEdict(self->missile);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Clear client's pointer to missile gun
|
||||||
|
if (self->creator)
|
||||||
|
self->creator->missile = NULL;
|
||||||
|
|
||||||
|
G_FreeEdict (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void mturret_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||||
|
{
|
||||||
|
vec3_t origin;
|
||||||
|
VectorCopy (self->s.origin,origin);
|
||||||
|
origin[2]+= 0.5;
|
||||||
|
self->takedamage = DAMAGE_NO;
|
||||||
|
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_EXPLOSION1);
|
||||||
|
gi.WritePosition (origin);
|
||||||
|
gi.multicast (origin, MULTICAST_PVS);
|
||||||
|
//G_FreeEdict(self->turret2);
|
||||||
|
|
||||||
|
//Remove stand (which is the missile of the missile gun)
|
||||||
|
mturret_remove(self);
|
||||||
|
gi.bprintf (PRINT_HIGH, "Missile Gun Destroyed.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void place_missile (edict_t *ent)
|
||||||
|
{
|
||||||
|
|
||||||
|
vec3_t forward,up,right,wallp, pos;
|
||||||
|
|
||||||
|
trace_t tr;
|
||||||
|
edict_t *missilestand;
|
||||||
|
|
||||||
|
// valid ent ?
|
||||||
|
if ((!ent->client) || (ent->health<=0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ent->missile)
|
||||||
|
{
|
||||||
|
//First free the stand
|
||||||
|
if (ent->missile->missile)
|
||||||
|
{
|
||||||
|
G_FreeEdict(ent->missile->missile);
|
||||||
|
ent->missile->missile = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Then free the missile gun
|
||||||
|
G_FreeEdict(ent->missile);
|
||||||
|
ent->missile = NULL;
|
||||||
|
gi.bprintf (PRINT_HIGH, "Missile Turret off.\n");
|
||||||
|
if (ent->client->oldplayer)
|
||||||
|
G_FreeEdict(ent->client->oldplayer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cells for missile gun ?
|
||||||
|
if (ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] < 25)
|
||||||
|
{
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "You need 25 cells to create Missile Turret\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup "little look" to close wall
|
||||||
|
VectorCopy(ent->s.origin,wallp);
|
||||||
|
|
||||||
|
// Cast along view angle
|
||||||
|
AngleVectors (ent->client->v_angle, forward, right, up);
|
||||||
|
|
||||||
|
// Setup end point
|
||||||
|
pos[0]=ent->s.origin[0]+forward[0]*75;
|
||||||
|
pos[1]=ent->s.origin[1]+forward[1]*75;
|
||||||
|
pos[2]=ent->s.origin[2]+forward[2]*75+30;
|
||||||
|
wallp[0]=ent->s.origin[0]+forward[0]*150;
|
||||||
|
wallp[1]=ent->s.origin[1]+forward[1]*150;
|
||||||
|
wallp[2]=ent->s.origin[2]+forward[2]*150+30;
|
||||||
|
// trace
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, wallp, ent, MASK_SOLID);
|
||||||
|
|
||||||
|
|
||||||
|
// Line complete ? (ie. no collision)
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
{
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Not enough room.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wallp[2]+=22;
|
||||||
|
tr = gi.trace (pos, NULL, NULL, wallp, ent, MASK_SOLID);
|
||||||
|
// Line complete ? (ie. no collision)
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
{
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Not enough room. Try aiming lower\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
wallp[2]-=40;
|
||||||
|
tr = gi.trace (pos, NULL, NULL, wallp, ent, MASK_SOLID);
|
||||||
|
// Line complete ? (ie. no collision)
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
{
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Not enough room. Try aiming higher\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hit sky ?
|
||||||
|
if (tr.surface)
|
||||||
|
if (tr.surface->flags & SURF_SKY)
|
||||||
|
return;
|
||||||
|
ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] -= 25;
|
||||||
|
|
||||||
|
|
||||||
|
// if (ent->missile)
|
||||||
|
// {
|
||||||
|
// G_FreeEdict(ent->missile);
|
||||||
|
// ent->missile = NULL;
|
||||||
|
// gi.bprintf (PRINT_HIGH, "missile Gun off.\n");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
gi.bprintf (PRINT_HIGH, "Missile Turret on.\n");
|
||||||
|
ent->missile = G_Spawn();
|
||||||
|
VectorClear (ent->missile->mins);
|
||||||
|
VectorClear (ent->missile->maxs);
|
||||||
|
VectorCopy (pos, ent->missile->s.origin);
|
||||||
|
ent->missile ->s.angles[0]=ent->s.angles[0];
|
||||||
|
// ent->missile -> movetype = MOVETYPE_STEP;
|
||||||
|
// ent->missile -> clipmask = MASK_SHOT;
|
||||||
|
ent->missile -> movetype = MOVETYPE_STEP;
|
||||||
|
ent->missile -> clipmask = MASK_PLAYERSOLID;
|
||||||
|
ent->missile->mass = 400;
|
||||||
|
//grenade -> solid = SOLID_NOT;
|
||||||
|
ent->missile->solid = SOLID_BBOX;
|
||||||
|
//GR - reduce size of bounding box since I reduced size of model
|
||||||
|
// VectorSet(ent->missile->mins, -95,-44,-80);
|
||||||
|
// VectorSet(ent->missile->maxs, 57, 41, 24);
|
||||||
|
VectorSet(ent->missile->mins, -50,-20,-40);
|
||||||
|
VectorSet(ent->missile->maxs, 30, 21, 22);
|
||||||
|
ent->missile->takedamage=DAMAGE_YES;
|
||||||
|
ent->missile -> s.modelindex = gi.modelindex ("models/missileb/missile1/tris.md2");
|
||||||
|
ent->missile -> creator = ent;
|
||||||
|
ent->missile->think = MTurret_Think;
|
||||||
|
ent->missile->nextthink = level.time + 0.1;
|
||||||
|
ent->missile->die = mturret_die;
|
||||||
|
ent->missile->health= 100;
|
||||||
|
ent->missile->max_health =100;
|
||||||
|
ent->missile->count = 1;
|
||||||
|
ent->missile->classname = "MissileTurret";
|
||||||
|
ent->missile->wf_team = ent->wf_team;
|
||||||
|
ent->missile->noteamdamage = true; //Don't let teammates damage it
|
||||||
|
ent->missile->yaw_speed = 10;
|
||||||
|
ent->missile->gib_health = 3;//Max Ammo
|
||||||
|
ent->missile->light_level = 3;//Ammo Total
|
||||||
|
|
||||||
|
gi.linkentity (ent->missile);
|
||||||
|
missilestand = G_Spawn();
|
||||||
|
VectorClear (missilestand->mins);
|
||||||
|
VectorClear (missilestand->maxs);
|
||||||
|
VectorCopy (pos,missilestand->s.origin);
|
||||||
|
missilestand->s.angles[0]=ent -> s.angles[0];
|
||||||
|
missilestand->movetype = MOVETYPE_NONE;
|
||||||
|
missilestand->mass = 400;
|
||||||
|
//grenade -> solid = SOLID_NOT;
|
||||||
|
missilestand->solid = SOLID_BBOX;
|
||||||
|
VectorSet(missilestand->mins, -45,-15,-35);
|
||||||
|
VectorSet(missilestand->maxs, 25, 18, 18);
|
||||||
|
missilestand->takedamage=DAMAGE_NO;
|
||||||
|
missilestand-> s.modelindex = gi.modelindex ("models/stand/tris.md2");
|
||||||
|
missilestand-> creator = ent->missile;
|
||||||
|
missilestand->wf_team = ent->wf_team;
|
||||||
|
missilestand->noteamdamage = true; //Don't let teammates damage it
|
||||||
|
|
||||||
|
gi.linkentity (missilestand);
|
||||||
|
ent->missile->missile = missilestand;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpgradeMissileTurret(edict_t *self)
|
||||||
|
{
|
||||||
|
edict_t *blip;
|
||||||
|
trace_t tr;
|
||||||
|
blip = NULL;
|
||||||
|
while (blip = findradius (blip, self->s.origin, 2048))
|
||||||
|
{
|
||||||
|
|
||||||
|
if (self->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] < 60)
|
||||||
|
{
|
||||||
|
safe_cprintf(self, PRINT_HIGH, "You need 60 cells to upgrade\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Q_stricmp("MissileTurret", blip->classname))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (Q_stricmp("MissileTurret", blip->classname))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tr = gi.trace (self->s.origin, NULL, NULL, blip->s.origin, self, MASK_SOLID);
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
continue;
|
||||||
|
if (blip->creator != self)
|
||||||
|
continue;
|
||||||
|
if (blip->count == 3)
|
||||||
|
safe_cprintf(self, PRINT_HIGH, "missile gun already at level 3\n");
|
||||||
|
|
||||||
|
if (blip->count < 3)
|
||||||
|
blip->count++;
|
||||||
|
self->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] -= 60;
|
||||||
|
if (blip->count == 2)
|
||||||
|
{
|
||||||
|
blip->health= 150;
|
||||||
|
blip->max_health =150;
|
||||||
|
blip -> s.modelindex = gi.modelindex ("models/missileb/missile2/tris.md2");
|
||||||
|
//blip->delay = StatusIdle;
|
||||||
|
blip->s.frame = 0;
|
||||||
|
blip->yaw_speed = 15;
|
||||||
|
blip->gib_health = 4;//Max Ammo
|
||||||
|
}
|
||||||
|
if (blip->count == 3)
|
||||||
|
{
|
||||||
|
blip->health= 200;
|
||||||
|
blip->max_health =200;
|
||||||
|
blip -> s.modelindex = gi.modelindex ("models/missileb/missile3/tris.md2");
|
||||||
|
//blip->delay = StatusIdle;
|
||||||
|
blip->s.frame = 0;
|
||||||
|
blip->yaw_speed = 20;
|
||||||
|
blip->gib_health = 12;//Max Ammo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,266 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
|
||||||
|
#define newSupplyDepot self->supply
|
||||||
|
void SP_SupplyDepot(edict_t *self);
|
||||||
|
void supplydepot_explode (edict_t *self);
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
Supply Depot
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
|
||||||
|
void SupplyThink (edict_t *self)
|
||||||
|
{
|
||||||
|
edict_t *other;
|
||||||
|
int x;
|
||||||
|
int contents;
|
||||||
|
other = NULL;
|
||||||
|
x = 0;
|
||||||
|
|
||||||
|
contents = (int)gi.pointcontents(self->s.origin);
|
||||||
|
|
||||||
|
if (contents & CONTENTS_SOLID)
|
||||||
|
{
|
||||||
|
safe_cprintf(self->owner, PRINT_HIGH, "Your supply depot was in a bad map position, so it was removed.\n");//5/99
|
||||||
|
supplydepot_explode (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
while ((other = findradius(other, self->s.origin, 16)) != NULL)
|
||||||
|
{
|
||||||
|
if (other->client)
|
||||||
|
{
|
||||||
|
x = 0;
|
||||||
|
// other->DrunkTime=0;
|
||||||
|
// other->disease= 0;
|
||||||
|
// other->lame = 0;
|
||||||
|
|
||||||
|
//destroy depot for bots if enemy is using it
|
||||||
|
if ((self->wf_team != other->wf_team) && (self->owner->bot_client))
|
||||||
|
{ supplydepot_explode (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((self->wf_team != other->wf_team) && (self->owner))
|
||||||
|
safe_cprintf(self->owner, PRINT_HIGH, "Enemies are using your depot!\n");
|
||||||
|
|
||||||
|
|
||||||
|
if (other->client->pers.inventory[ITEM_INDEX(FindItem("bullets"))]<other->client->pers.max_bullets)
|
||||||
|
{
|
||||||
|
x =1;
|
||||||
|
other->client->pers.inventory[ITEM_INDEX(FindItem("bullets"))]+= 1;
|
||||||
|
}
|
||||||
|
if (other->client->pers.inventory[ITEM_INDEX(FindItem("shells"))]<other->client->pers.max_shells)
|
||||||
|
{
|
||||||
|
x=1;
|
||||||
|
other->client->pers.inventory[ITEM_INDEX(FindItem("shells"))]+= 1;
|
||||||
|
}
|
||||||
|
if (other->client->pers.inventory[ITEM_INDEX(FindItem("grenades"))]<other->client->pers.max_grenades)
|
||||||
|
{
|
||||||
|
x=1;
|
||||||
|
other->client->pers.inventory[ITEM_INDEX(FindItem("grenades"))]+= 1;
|
||||||
|
}
|
||||||
|
if (other->client->pers.inventory[ITEM_INDEX(FindItem("rockets"))]<other->client->pers.max_rockets)
|
||||||
|
{
|
||||||
|
x=1;
|
||||||
|
other->client->pers.inventory[ITEM_INDEX(FindItem("rockets"))]+= 1;
|
||||||
|
}
|
||||||
|
if (other->client->pers.inventory[ITEM_INDEX(FindItem("slugs"))]<other->client->pers.max_slugs)
|
||||||
|
{
|
||||||
|
x=1;
|
||||||
|
other->client->pers.inventory[ITEM_INDEX(FindItem("slugs"))]+= 1;
|
||||||
|
}
|
||||||
|
if (other->client->pers.inventory[ITEM_INDEX(FindItem("cells"))]<other->client->pers.max_cells)
|
||||||
|
{
|
||||||
|
x=1;
|
||||||
|
other->client->pers.inventory[ITEM_INDEX(FindItem("cells"))]+= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x ==1)
|
||||||
|
gi.sound(self, CHAN_ITEM, gi.soundindex("misc/w_pkup.wav"), 1, ATTN_NORM, 0);
|
||||||
|
|
||||||
|
self->nextthink = level.time + 0.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creator destroyed it
|
||||||
|
void supplydepot_explode (edict_t *self)
|
||||||
|
{
|
||||||
|
edict_t *blip = NULL;
|
||||||
|
int i;
|
||||||
|
//42 bot supply clear itemnode owner
|
||||||
|
if (self->owner->bot_client)
|
||||||
|
for (i=1, blip=g_edicts+i ; i < globals.num_edicts ; i++,blip++)
|
||||||
|
{
|
||||||
|
if (blip->owner == self->owner)
|
||||||
|
{
|
||||||
|
if (!strcmp(blip->classname, "item_depotspot") )
|
||||||
|
{
|
||||||
|
blip->owner = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//42 end clear item owner
|
||||||
|
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
if (self->waterlevel)
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
|
||||||
|
else
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION);
|
||||||
|
gi.WritePosition (self->s.origin);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||||
|
make_debris(self);
|
||||||
|
|
||||||
|
self->takedamage = DAMAGE_NO;
|
||||||
|
|
||||||
|
if (self->owner) safe_cprintf(self->owner, PRINT_HIGH, "Supply Depot Off.\n");
|
||||||
|
|
||||||
|
if ((self->owner) && (self->owner->supply))
|
||||||
|
self->owner->supply = NULL;
|
||||||
|
|
||||||
|
T_RadiusDamage(self, self->owner, self->dmg, NULL, self->dmg_radius, MOD_DEPOT);
|
||||||
|
G_FreeEdict(self);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// some other player blew it up
|
||||||
|
void supplydepot_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||||
|
{
|
||||||
|
edict_t *blip = NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
//42 bot supply clear itemnode owner
|
||||||
|
if (self->owner->bot_client)
|
||||||
|
for (i=1, blip=g_edicts+i ; i < globals.num_edicts ; i++,blip++)
|
||||||
|
{
|
||||||
|
if (blip->owner == self->owner)
|
||||||
|
{
|
||||||
|
if (!strcmp(blip->classname, "item_depotspot") )
|
||||||
|
{
|
||||||
|
blip->owner = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//42 end clear item owner
|
||||||
|
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
if (self->waterlevel)
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
|
||||||
|
else
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION);
|
||||||
|
gi.WritePosition (self->s.origin);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||||
|
make_debris(self);
|
||||||
|
|
||||||
|
self->takedamage = DAMAGE_NO;
|
||||||
|
|
||||||
|
if (self->owner)
|
||||||
|
{
|
||||||
|
safe_cprintf(self->owner, PRINT_HIGH, "Supply Depot Destroyed.\n");
|
||||||
|
if (self->owner->supply)
|
||||||
|
self->owner->supply = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// T_RadiusDamage(self, self->owner, self->dmg, NULL, self->dmg_radius, MOD_DEPOT);
|
||||||
|
T_RadiusDamage(self, attacker, self->dmg, NULL, self->dmg_radius, MOD_DEPOT_EXPLODE);
|
||||||
|
G_FreeEdict(self);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_SupplyDepot(edict_t *self)
|
||||||
|
{
|
||||||
|
if ( newSupplyDepot )
|
||||||
|
{
|
||||||
|
supplydepot_explode(newSupplyDepot);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cells for laser ?
|
||||||
|
if (!self->bot_client)
|
||||||
|
{
|
||||||
|
if (self->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] < 10)
|
||||||
|
{
|
||||||
|
safe_cprintf(self, PRINT_HIGH, "Need 10 cells for a Supply Depot.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (self->client->pers.inventory[ITEM_INDEX(FindItem("Bullets"))] < 10)
|
||||||
|
{
|
||||||
|
safe_cprintf(self, PRINT_HIGH, "Need 10 bullets for a Supply Depot.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (self->client->pers.inventory[ITEM_INDEX(FindItem("Shells"))] < 10)
|
||||||
|
{
|
||||||
|
safe_cprintf(self, PRINT_HIGH, "Need 10 shells for a Supply Depot.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (self->client->pers.inventory[ITEM_INDEX(FindItem("Rockets"))] < 10)
|
||||||
|
{
|
||||||
|
safe_cprintf(self, PRINT_HIGH, "Need 10 rockets for a Supply Depot.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (self->client->pers.inventory[ITEM_INDEX(FindItem("Grenades"))] < 10)
|
||||||
|
{
|
||||||
|
safe_cprintf(self, PRINT_HIGH, "Need 10 grenades for a Supply Depot.\n");
|
||||||
|
gi.dprintf("you only have %d grenades.\n",self->client->pers.inventory[ITEM_INDEX(FindItem("Grenades"))] );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (self->client->pers.inventory[ITEM_INDEX(FindItem("Slugs"))] < 10)
|
||||||
|
{
|
||||||
|
safe_cprintf(self, PRINT_HIGH, "Need 10 slugs for a Supply Depot.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
safe_cprintf(self, PRINT_HIGH, "Supply Depot on.\n");
|
||||||
|
}
|
||||||
|
newSupplyDepot = G_Spawn ();
|
||||||
|
VectorCopy(self->s.origin,newSupplyDepot->s.origin);
|
||||||
|
newSupplyDepot->s.origin[2] += 40;
|
||||||
|
newSupplyDepot->classname="depot";
|
||||||
|
newSupplyDepot->takedamage=DAMAGE_AIM;
|
||||||
|
newSupplyDepot->movetype= MOVETYPE_TOSS;
|
||||||
|
newSupplyDepot->mass = 200;
|
||||||
|
newSupplyDepot->solid = SOLID_BBOX;
|
||||||
|
//newSupplyDepot->clipmask=MASK_ALL;
|
||||||
|
newSupplyDepot->deadflag =DEAD_NO;
|
||||||
|
newSupplyDepot->clipmask = MASK_SHOT;
|
||||||
|
newSupplyDepot->model = self->model;
|
||||||
|
newSupplyDepot->s.modelindex = gi.modelindex ("models/objects/dmspot/tris.md2");
|
||||||
|
newSupplyDepot->s.skinnum = 1;
|
||||||
|
newSupplyDepot->solid = SOLID_BBOX;
|
||||||
|
newSupplyDepot->noteamdamage = true; //Don't let teammates damage it
|
||||||
|
newSupplyDepot->wf_team = self->wf_team;
|
||||||
|
|
||||||
|
VectorSet (newSupplyDepot->mins, -32, -32, -24);
|
||||||
|
VectorSet (newSupplyDepot->maxs, 32, 32, -16);
|
||||||
|
newSupplyDepot->s.frame =0;
|
||||||
|
newSupplyDepot->waterlevel = 0;
|
||||||
|
newSupplyDepot->watertype=0;
|
||||||
|
newSupplyDepot->health= 100;
|
||||||
|
newSupplyDepot->max_health =100;
|
||||||
|
newSupplyDepot->gib_health = -80;
|
||||||
|
newSupplyDepot->die = supplydepot_die;
|
||||||
|
newSupplyDepot->owner = self;
|
||||||
|
newSupplyDepot->dmg = 150;
|
||||||
|
newSupplyDepot->dmg_radius = 160;
|
||||||
|
// newSupplyDepot->touch = SupplyTouch;
|
||||||
|
newSupplyDepot->think =SupplyThink;
|
||||||
|
newSupplyDepot->nextthink = level.time +1;
|
||||||
|
VectorClear (newSupplyDepot->velocity);
|
||||||
|
gi.linkentity (newSupplyDepot);
|
||||||
|
if (!self->bot_client)
|
||||||
|
{
|
||||||
|
self->client->pers.inventory[ITEM_INDEX(FindItem("bullets"))]-= 10;
|
||||||
|
|
||||||
|
self->client->pers.inventory[ITEM_INDEX(FindItem("shells"))]-= 10;
|
||||||
|
|
||||||
|
self->client->pers.inventory[ITEM_INDEX(FindItem("grenades"))]-= 10;
|
||||||
|
|
||||||
|
self->client->pers.inventory[ITEM_INDEX(FindItem("rockets"))]-= 10;
|
||||||
|
|
||||||
|
self->client->pers.inventory[ITEM_INDEX(FindItem("slugs"))]-= 10;
|
||||||
|
|
||||||
|
self->client->pers.inventory[ITEM_INDEX(FindItem("cells"))]-= 10;
|
||||||
|
|
||||||
|
if (self->client) gi.centerprintf (self,"Supply Depot set!\nNote: 10 of each ammo has been\ndrained into the Supply Depot to be\nreplicated\n");
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,180 @@
|
||||||
|
/*****************************************************************
|
||||||
|
|
||||||
|
Eraser Bot source code - by Ryan Feltrin, Added to by Acrid-
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
This file is Copyright(c) 1998, Ryan Feltrin, All Rights Reserved.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
All other files are Copyright(c) Id Software, Inc.
|
||||||
|
|
||||||
|
Please see liscense.txt in the source directory for the copyright
|
||||||
|
information regarding those files belonging to Id Software, Inc.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
Should you decide to release a modified version of Eraser, you MUST
|
||||||
|
include the following text (minus the BEGIN and END lines) in the
|
||||||
|
documentation for your modification.
|
||||||
|
|
||||||
|
--- BEGIN ---
|
||||||
|
|
||||||
|
The Eraser Bot is a product of Ryan Feltrin, and is available from
|
||||||
|
the Eraser Bot homepage, at http://impact.frag.com.
|
||||||
|
|
||||||
|
This program is a modification of the Eraser Bot, and is therefore
|
||||||
|
in NO WAY supported by Ryan Feltrin.
|
||||||
|
|
||||||
|
This program MUST NOT be sold in ANY form. If you have paid for
|
||||||
|
this product, you should contact Ryan Feltrin immediately, via
|
||||||
|
the Eraser Bot homepage.
|
||||||
|
|
||||||
|
--- END ---
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
You will find p_trail.c has not been included with the Eraser
|
||||||
|
source code release. This is NOT an error. I am unable to
|
||||||
|
distribute this file because it contains code that is bound by
|
||||||
|
legal documents, and signed by myself, never to be released
|
||||||
|
to the public. Sorry guys, but law is law.
|
||||||
|
|
||||||
|
I have therefore include the compiled version of these files
|
||||||
|
in .obj form in the src\Release and src\Debug directories.
|
||||||
|
So while you cannot edit and debug code within these files,
|
||||||
|
you can still compile this source as-is. Although these will only
|
||||||
|
work in MSVC v5.0, linux versions can be made available upon
|
||||||
|
request.
|
||||||
|
|
||||||
|
NOTE: When compiling this source, you will get a warning
|
||||||
|
message from the compiler, regarding the missing p_trail.c
|
||||||
|
file. Just ignore it, it will still compile fine.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
I, Ryan Feltrin/Acrid-, hold no responsibility for any harm caused by the
|
||||||
|
use of this source code. I also am NOT willing to provide any form
|
||||||
|
of help or support for this source code. It is provided as-is,
|
||||||
|
as a service by me, with no documentation, other then the comments
|
||||||
|
contained within the code. If you have any queries, I suggest you
|
||||||
|
visit the "official" Eraser source web-board, at
|
||||||
|
http://www.telefragged.com/epidemic/. I will stop by there from
|
||||||
|
time to time, to answer questions and help with any problems that
|
||||||
|
may arise.
|
||||||
|
|
||||||
|
Otherwise, have fun, and I look forward to seeing what can be done
|
||||||
|
with this.
|
||||||
|
|
||||||
|
-Ryan Feltrin
|
||||||
|
-Acrid-
|
||||||
|
|
||||||
|
*****************************************************************/
|
||||||
|
#include "g_local.h"
|
||||||
|
#include "bot_procs.h"
|
||||||
|
#include "m_player.h"
|
||||||
|
|
||||||
|
void TossClientWeapon (edict_t *self);
|
||||||
|
qboolean IsFemale (edict_t *ent);
|
||||||
|
void ClientObituary (edict_t *self, edict_t *inflictor, edict_t *attacker);
|
||||||
|
void WFPlayer_Die (edict_t *self);
|
||||||
|
|
||||||
|
void bot_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
float rnd;
|
||||||
|
|
||||||
|
VectorClear (self->avelocity);
|
||||||
|
|
||||||
|
self->takedamage = DAMAGE_YES;
|
||||||
|
self->movetype = MOVETYPE_TOSS;
|
||||||
|
|
||||||
|
self->s.modelindex2 = 0; // remove linked weapon model
|
||||||
|
//ZOID
|
||||||
|
self->s.modelindex3 = 0; // remove linked ctf flag
|
||||||
|
//ZOID
|
||||||
|
|
||||||
|
self->s.angles[0] = 0;
|
||||||
|
self->s.angles[2] = 0;
|
||||||
|
|
||||||
|
self->s.sound = 0;
|
||||||
|
self->client->weapon_sound = 0;
|
||||||
|
|
||||||
|
self->maxs[2] = -8;
|
||||||
|
|
||||||
|
// self->solid = SOLID_NOT;
|
||||||
|
self->svflags |= SVF_DEADMONSTER;
|
||||||
|
|
||||||
|
if (!self->deadflag)
|
||||||
|
{
|
||||||
|
self->client->respawn_time = level.time + 1.0;
|
||||||
|
self->client->ps.pmove.pm_type = PM_DEAD;
|
||||||
|
ClientObituary (self, inflictor, attacker);
|
||||||
|
//ZOID
|
||||||
|
CTFFragBonuses(self, inflictor, attacker);
|
||||||
|
//ZOID
|
||||||
|
TossClientWeapon (self);
|
||||||
|
//ZOID
|
||||||
|
CTFPlayerResetGrapple(self);
|
||||||
|
CTFDeadDropFlag(self);
|
||||||
|
CTFDeadDropTech(self);
|
||||||
|
//ZOID
|
||||||
|
}
|
||||||
|
|
||||||
|
//WF cleanup (like flames)
|
||||||
|
WFPlayer_Die (self);
|
||||||
|
|
||||||
|
// remove powerups
|
||||||
|
self->client->quad_framenum = 0;
|
||||||
|
self->client->invincible_framenum = 0;
|
||||||
|
self->client->breather_framenum = 0;
|
||||||
|
self->client->enviro_framenum = 0;
|
||||||
|
|
||||||
|
self->s.effects = 0;
|
||||||
|
|
||||||
|
// clear inventory
|
||||||
|
memset(self->client->pers.inventory, 0, sizeof(self->client->pers.inventory));
|
||||||
|
|
||||||
|
// check for gib
|
||||||
|
if (self->health <= -1440)//Acrid fix crash? was -40
|
||||||
|
{
|
||||||
|
gi.sound (self, CHAN_BODY, gi.soundindex ("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||||
|
for (n= 0; n < 4; n++)
|
||||||
|
ThrowGib (self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
||||||
|
ThrowClientHead (self, damage);
|
||||||
|
|
||||||
|
//ZOID
|
||||||
|
self->client->anim_priority = ANIM_DEATH;
|
||||||
|
self->client->anim_end = 0;
|
||||||
|
//ZOID
|
||||||
|
self->takedamage = DAMAGE_NO;
|
||||||
|
|
||||||
|
self->timestamp = level.time;
|
||||||
|
}
|
||||||
|
else if (self->frozen && !self->deadflag)//acrid 3/99 botfrozen death animation
|
||||||
|
{
|
||||||
|
self->s.frame = FRAME_stand01;
|
||||||
|
self->frozenbody = 1;
|
||||||
|
}
|
||||||
|
else if (!self->deadflag)
|
||||||
|
{
|
||||||
|
rnd = random() * 3;
|
||||||
|
|
||||||
|
if (self->viewheight < 0)
|
||||||
|
self->s.frame = FRAME_crdeath1;
|
||||||
|
else if (rnd <= 1)
|
||||||
|
self->s.frame = FRAME_death101;
|
||||||
|
else if (rnd <= 2)
|
||||||
|
self->s.frame = FRAME_death201;
|
||||||
|
else
|
||||||
|
self->s.frame = FRAME_death301;
|
||||||
|
|
||||||
|
gi.sound (self, CHAN_VOICE, gi.soundindex(va("*death%i.wav", (rand()%4)+1)), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// regular death
|
||||||
|
self->deadflag = DEAD_DEAD;
|
||||||
|
|
||||||
|
gi.linkentity(self);
|
||||||
|
}
|
|
@ -0,0 +1,531 @@
|
||||||
|
/*****************************************************************
|
||||||
|
|
||||||
|
Eraser Bot source code - by Ryan Feltrin, Added to by Acrid-
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
This file is Copyright(c) 1998, Ryan Feltrin, All Rights Reserved.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
All other files are Copyright(c) Id Software, Inc.
|
||||||
|
|
||||||
|
Please see liscense.txt in the source directory for the copyright
|
||||||
|
information regarding those files belonging to Id Software, Inc.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
Should you decide to release a modified version of Eraser, you MUST
|
||||||
|
include the following text (minus the BEGIN and END lines) in the
|
||||||
|
documentation for your modification.
|
||||||
|
|
||||||
|
--- BEGIN ---
|
||||||
|
|
||||||
|
The Eraser Bot is a product of Ryan Feltrin, and is available from
|
||||||
|
the Eraser Bot homepage, at http://impact.frag.com.
|
||||||
|
|
||||||
|
This program is a modification of the Eraser Bot, and is therefore
|
||||||
|
in NO WAY supported by Ryan Feltrin.
|
||||||
|
|
||||||
|
This program MUST NOT be sold in ANY form. If you have paid for
|
||||||
|
this product, you should contact Ryan Feltrin immediately, via
|
||||||
|
the Eraser Bot homepage.
|
||||||
|
|
||||||
|
--- END ---
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
You will find p_trail.c has not been included with the Eraser
|
||||||
|
source code release. This is NOT an error. I am unable to
|
||||||
|
distribute this file because it contains code that is bound by
|
||||||
|
legal documents, and signed by myself, never to be released
|
||||||
|
to the public. Sorry guys, but law is law.
|
||||||
|
|
||||||
|
I have therefore include the compiled version of these files
|
||||||
|
in .obj form in the src\Release and src\Debug directories.
|
||||||
|
So while you cannot edit and debug code within these files,
|
||||||
|
you can still compile this source as-is. Although these will only
|
||||||
|
work in MSVC v5.0, linux versions can be made available upon
|
||||||
|
request.
|
||||||
|
|
||||||
|
NOTE: When compiling this source, you will get a warning
|
||||||
|
message from the compiler, regarding the missing p_trail.c
|
||||||
|
file. Just ignore it, it will still compile fine.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
I, Ryan Feltrin/Acrid-, hold no responsibility for any harm caused by the
|
||||||
|
use of this source code. I also am NOT willing to provide any form
|
||||||
|
of help or support for this source code. It is provided as-is,
|
||||||
|
as a service by me, with no documentation, other then the comments
|
||||||
|
contained within the code. If you have any queries, I suggest you
|
||||||
|
visit the "official" Eraser source web-board, at
|
||||||
|
http://www.telefragged.com/epidemic/. I will stop by there from
|
||||||
|
time to time, to answer questions and help with any problems that
|
||||||
|
may arise.
|
||||||
|
|
||||||
|
Otherwise, have fun, and I look forward to seeing what can be done
|
||||||
|
with this.
|
||||||
|
|
||||||
|
-Ryan Feltrin
|
||||||
|
-Acrid-
|
||||||
|
|
||||||
|
*****************************************************************/
|
||||||
|
#include "g_local.h"
|
||||||
|
#include "g_items.h"
|
||||||
|
#include "p_trail.h"
|
||||||
|
#include "bot_procs.h"
|
||||||
|
|
||||||
|
extern gitem_t *titems[4];
|
||||||
|
|
||||||
|
void botSetWant(edict_t *self, int dist_divide)
|
||||||
|
{
|
||||||
|
if (dist_divide <= 1)
|
||||||
|
self->movetarget_want = WANT_KINDA;
|
||||||
|
else if (dist_divide <= 3)
|
||||||
|
self->movetarget_want = WANT_YEH_OK;
|
||||||
|
else
|
||||||
|
self->movetarget_want = WANT_SHITYEAH;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=============
|
||||||
|
RoamFindBestItem
|
||||||
|
|
||||||
|
Searches for the best reachable item, in list_head
|
||||||
|
set check_paths to enable reaching items that aren't visible
|
||||||
|
** be careful not to call this too often, it will slow things down
|
||||||
|
=============
|
||||||
|
*/
|
||||||
|
int RoamFindBestItem(edict_t *self, edict_t *list_head, int check_paths)
|
||||||
|
{
|
||||||
|
float closest_dist=999999, this_dist;
|
||||||
|
edict_t *best=NULL, *node, *trav;
|
||||||
|
int si; // node closest to self
|
||||||
|
int dist_divide=1; // divide distance by this, simulates wieghts for better targets
|
||||||
|
int best_divide;
|
||||||
|
|
||||||
|
if (!list_head) // nothing to look for
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
si = ClosestNodeToEnt(self, false, false);
|
||||||
|
|
||||||
|
for (trav = list_head ; trav ; trav = trav->node_target)
|
||||||
|
{
|
||||||
|
if (trav->solid != SOLID_TRIGGER)//crash here
|
||||||
|
continue;
|
||||||
|
|
||||||
|
this_dist = 0;
|
||||||
|
|
||||||
|
if (((trav->item->pickup != CTFPickup_Flag) && (trav->item->pickup != Pickup_Weapon))
|
||||||
|
&& (this_dist = entdist(self, trav)) > 2000) // too far away
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// CTF, if guarding base, don't go too far away//FIXME LOOK HERE
|
||||||
|
if(!bot_melee->value)//fixme
|
||||||
|
if (self->target_ent && self->target_ent->item && (self->target_ent->item->pickup == CTFPickup_Flag)
|
||||||
|
&& (this_dist > BOT_GUARDING_RANGE))
|
||||||
|
continue;
|
||||||
|
// CTF
|
||||||
|
|
||||||
|
if (trav->ignore_time >= level.time)
|
||||||
|
continue;
|
||||||
|
// make sure we can pickup the ammo
|
||||||
|
if ((list_head == ammo_head) &&
|
||||||
|
(!((dist_divide = (2*botHasWeaponForAmmo(self->client, trav->item) + (this_dist < 256))) &&
|
||||||
|
(dist_divide *= 2*botCanPickupAmmo(self->client, trav->item)) &&
|
||||||
|
(dist_divide *= 3*(self->bot_fire == botBlaster))))) // no weapon to use this ammo, or ammo full
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (list_head == bonus_head)
|
||||||
|
{
|
||||||
|
if (trav->item->pickup == Pickup_Armor)
|
||||||
|
{
|
||||||
|
if (!(dist_divide = botCanPickupArmor(self, trav)))
|
||||||
|
{ // can't pickup this armor item (already have better armor)
|
||||||
|
dist_divide = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/////////////////////////////////////specials/////////////////////////////
|
||||||
|
else if (trav->item->pickup == Place_Special)
|
||||||
|
{
|
||||||
|
// dist_divide += 9999;
|
||||||
|
if (!(dist_divide = botCanPlaceSpecial(self, trav)))
|
||||||
|
{ //can't pickup already have a sentry placed or not enough cells
|
||||||
|
// botDebugPrint("sentry place 1\n");
|
||||||
|
dist_divide = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
////////////////////////////////specials end//////////////////////////////
|
||||||
|
|
||||||
|
else if (trav->item->pickup == Pickup_Pack)
|
||||||
|
{
|
||||||
|
if (!(dist_divide = botCanPickupPack(self, trav)))
|
||||||
|
{ // can't pickup this pack item (already full)
|
||||||
|
dist_divide = 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (((strcmp(trav->classname, "item_flagreturn_team1") == 0) ||//$
|
||||||
|
(strcmp(trav->classname, "item_flagreturn_team2") == 0)) &&//$
|
||||||
|
!CarryingFlag(self))//$
|
||||||
|
{//$
|
||||||
|
continue;//$
|
||||||
|
}//$
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
else if (trav->item->pickup == CTFPickup_Flag)
|
||||||
|
{
|
||||||
|
//f dist_divide += 4;//FIXME ACRID WAS = 6 MAYBE ONE PROBLEM
|
||||||
|
|
||||||
|
//f if (self->bot_fire != botBlaster && self->bot_fire != botShotgun)
|
||||||
|
//f dist_divide += 100;
|
||||||
|
|
||||||
|
if ( ( (self->client->resp.ctf_team == CTF_TEAM1)
|
||||||
|
&& (self->client->pers.inventory[ITEM_INDEX(flag2_item)])
|
||||||
|
&& (trav == flag1_ent || trav == flagreturn1_ent))//flagreturn code
|
||||||
|
|| ( (self->client->resp.ctf_team == CTF_TEAM2)
|
||||||
|
&& (self->client->pers.inventory[ITEM_INDEX(flag1_item)])
|
||||||
|
&& (trav == flag2_ent || trav == flagreturn2_ent)))//flagreturn code
|
||||||
|
{ // we have their flag, so HEAD FOR OUR FLAG!
|
||||||
|
dist_divide += 9999;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if ( ((self->client->resp.ctf_team == CTF_TEAM1) && (trav == flag1_ent) && (flag1_ent->solid = SOLID_TRIGGER))
|
||||||
|
|| ((self->client->resp.ctf_team == CTF_TEAM2) && (trav == flag2_ent) && (flag2_ent->solid = SOLID_TRIGGER)))
|
||||||
|
{ // flag is sitting at home, don't try and get it
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (trav->item->pickup == CTFPickup_Tech)
|
||||||
|
{
|
||||||
|
qboolean has_tech=false;
|
||||||
|
|
||||||
|
if ( (self->client->pers.inventory[ITEM_INDEX(item_tech1)])
|
||||||
|
|| (self->client->pers.inventory[ITEM_INDEX(item_tech2)])
|
||||||
|
|| (self->client->pers.inventory[ITEM_INDEX(item_tech3)])
|
||||||
|
|| (self->client->pers.inventory[ITEM_INDEX(item_tech4)]))
|
||||||
|
{
|
||||||
|
has_tech = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_tech) // already have a tech, so ignore
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!self->client->ctf_has_tech)
|
||||||
|
dist_divide = 3;
|
||||||
|
}
|
||||||
|
else // item is a powerup
|
||||||
|
{
|
||||||
|
if (trav->item->use == Use_Quad)
|
||||||
|
{
|
||||||
|
dist_divide = 4;
|
||||||
|
|
||||||
|
if (skill->value > 1)
|
||||||
|
dist_divide += 4 * (skill->value - 1);
|
||||||
|
|
||||||
|
if (self->bot_stats->quad_freak)
|
||||||
|
dist_divide *= 2; // REALLY REALLY WANT THIS SUCKER!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (list_head == weapons_head) // for weapons, apply weights for good weapons
|
||||||
|
{//fixme acrid use this for something else?
|
||||||
|
// don't go for a (non-droppped) weapon we already have, if in "weapons stay" mode
|
||||||
|
if ( ((int)(dmflags->value) & DF_WEAPONS_STAY)
|
||||||
|
&& (!(trav->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM) ))
|
||||||
|
&& (self->client->pers.inventory[ITEM_INDEX(trav->item)]))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((trav->item == item_rocketlauncher) ||
|
||||||
|
(trav->item == item_chaingun) ||//42 Acrid note,3.4 code
|
||||||
|
(trav->item == item_railgun))
|
||||||
|
dist_divide = 4;
|
||||||
|
else if (trav->item == item_bfg10k)
|
||||||
|
{
|
||||||
|
dist_divide = 3;
|
||||||
|
|
||||||
|
if (num_players > 4) // BFG rocks with lots of people
|
||||||
|
dist_divide += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trav->item == self->bot_stats->fav_weapon)
|
||||||
|
dist_divide += 3;
|
||||||
|
|
||||||
|
if (self->bot_fire == botBlaster) // we really need a weapon
|
||||||
|
dist_divide += 4;
|
||||||
|
}
|
||||||
|
else if (list_head == health_head)
|
||||||
|
{
|
||||||
|
if (trav->count == 100)
|
||||||
|
dist_divide = 4;
|
||||||
|
else if (self->health > 90) // don't need it
|
||||||
|
continue;
|
||||||
|
else if ((self->health > 50) && check_paths) // only check routes for health when really low
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((this_dist < 384) && visible_box(self, trav) && CanReach(self, trav)) // go for it!
|
||||||
|
{
|
||||||
|
if (trav != self->save_movetarget)
|
||||||
|
self->goalentity = trav;
|
||||||
|
else
|
||||||
|
self->goalentity = self->save_goalentity;
|
||||||
|
|
||||||
|
self->movetarget = trav;
|
||||||
|
|
||||||
|
botSetWant(self, dist_divide);
|
||||||
|
|
||||||
|
this_dist = this_dist/dist_divide;
|
||||||
|
|
||||||
|
// this_dist = this_dist/256; // always grab something close by
|
||||||
|
|
||||||
|
return this_dist;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!check_paths)
|
||||||
|
continue;
|
||||||
|
// can't see a node, so don't bother checking routes
|
||||||
|
if (si == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// don't look for a path to shards
|
||||||
|
if (list_head == health_head)
|
||||||
|
{
|
||||||
|
if (trav->count < 10)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (list_head == bonus_head)
|
||||||
|
{
|
||||||
|
if (trav->item->tag == ARMOR_SHARD)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// see if any of our visible nodes, has a route to one of the
|
||||||
|
// item's visible nodes //crash here
|
||||||
|
if (((this_dist = PathToEnt(trail[si], trav, false, false)) > -1)
|
||||||
|
&& ((this_dist / dist_divide) < closest_dist))
|
||||||
|
{
|
||||||
|
this_dist = this_dist / dist_divide;
|
||||||
|
closest_dist = this_dist;
|
||||||
|
best = trav;
|
||||||
|
node = trail[si]; // go for the nearest node first
|
||||||
|
best_divide = dist_divide;
|
||||||
|
|
||||||
|
if (this_dist < 128) // OPTIMIZE: go for this one!
|
||||||
|
{
|
||||||
|
if (node != self->save_movetarget)
|
||||||
|
self->goalentity = node;
|
||||||
|
else
|
||||||
|
self->goalentity = self->save_goalentity;
|
||||||
|
|
||||||
|
self->movetarget = best;
|
||||||
|
|
||||||
|
botSetWant(self, dist_divide);
|
||||||
|
|
||||||
|
return this_dist;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//ACO BOTH
|
||||||
|
botDebugPrint("Found item %s: %i away\n", trav->classname, (int) this_dist);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best)
|
||||||
|
{
|
||||||
|
botDebugPrint("Best item %s: %i away\n", best->classname, (int) closest_dist);
|
||||||
|
if (node != self->save_movetarget)
|
||||||
|
self->goalentity = node;
|
||||||
|
else
|
||||||
|
self->goalentity = self->save_goalentity;
|
||||||
|
|
||||||
|
self->movetarget = best;
|
||||||
|
|
||||||
|
botSetWant(self, best_divide);
|
||||||
|
|
||||||
|
return closest_dist;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////Specials///////////////////////////////
|
||||||
|
// Similar to canuse
|
||||||
|
int botCanPlaceSpecial (edict_t *self, edict_t *ent)
|
||||||
|
{
|
||||||
|
gclient_t *client;
|
||||||
|
|
||||||
|
if (self->client)
|
||||||
|
client = self->client;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
if (strcmp(ent->item->classname, "item_sentryspot") == 0)
|
||||||
|
{
|
||||||
|
if (!ent->owner && self->sentry)
|
||||||
|
return false;
|
||||||
|
//code problem still says owned by previous owner,see if added eos helps
|
||||||
|
if (ent->owner && ent->owner->sentry)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(ent->item->classname, "item_depotspot") == 0)
|
||||||
|
{
|
||||||
|
if (!ent->owner && self->supply)
|
||||||
|
return false;
|
||||||
|
//code problem still says owned by previous owner,see if added eos helps
|
||||||
|
if (ent->owner && ent->owner->supply)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( (strcmp(ent->item->classname, "item_sentryspot") == 0)
|
||||||
|
&& ( (client->player_special & SPECIAL_SENTRY_GUN
|
||||||
|
&& client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] >= 60) ||
|
||||||
|
(client->player_special & SPECIAL_BIOSENTRY
|
||||||
|
&& client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] >= 50) ))
|
||||||
|
return 9999;//tried 4 but they almost totally ignore for enemy flag
|
||||||
|
|
||||||
|
else if ( (strcmp(ent->item->classname, "item_depotspot") == 0)
|
||||||
|
&& (client->player_special & SPECIAL_SUPPLY_DEPOT
|
||||||
|
|| client->player_special & SPECIAL_HEALING) )
|
||||||
|
return 9999;//tried 4 but they almost totally ignore for enemy flag
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}//ent->client->player_special & SPECIAL_SUPPLY_DEPOT
|
||||||
|
///////////////////////////Specials end//////////////////////////////
|
||||||
|
//Acrid pack code
|
||||||
|
int botCanPickupPack (edict_t *self, edict_t *ent)
|
||||||
|
{
|
||||||
|
gclient_t *client;
|
||||||
|
|
||||||
|
if (self->client)
|
||||||
|
client = self->client;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
|
||||||
|
//Team=9 means that any team can pick it up, but it will respawn in 5 seconds
|
||||||
|
if ((ent->wf_team) && (ent->wf_team != self->wf_team) && (ent->wf_team != 9))
|
||||||
|
return false;
|
||||||
|
//ref self->client->pers.inventory[self->client->ammo_index]
|
||||||
|
//ref client->pers.inventory[ITEM_INDEX(item_bullets)]
|
||||||
|
if ((client->pers.inventory[ITEM_INDEX(item_bullets)] != client->pers.max_bullets)||
|
||||||
|
(client->pers.inventory[ITEM_INDEX(item_shells)] != client->pers.max_shells) ||
|
||||||
|
(client->pers.inventory[ITEM_INDEX(item_rockets)] != client->pers.max_rockets)||
|
||||||
|
(client->pers.inventory[ITEM_INDEX(item_grenades)] != client->pers.max_grenades)||
|
||||||
|
(client->pers.inventory[ITEM_INDEX(item_cells)] != client->pers.max_cells ) ||
|
||||||
|
(client->pers.inventory[ITEM_INDEX(item_slugs)] != client->pers.max_slugs))
|
||||||
|
return 2;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
// FIXME: these are in g_items.c also!!
|
||||||
|
extern gitem_armor_t jacketarmor_info;
|
||||||
|
extern gitem_armor_t combatarmor_info;
|
||||||
|
extern gitem_armor_t bodyarmor_info;
|
||||||
|
|
||||||
|
// returns 0 if this armour is of no use to the bot, a higher number is returned for more useful armour
|
||||||
|
int botCanPickupArmor (edict_t *self, edict_t *ent)
|
||||||
|
{
|
||||||
|
int old_armor_index;
|
||||||
|
gitem_armor_t *oldinfo;
|
||||||
|
gitem_armor_t *newinfo;
|
||||||
|
int newcount;
|
||||||
|
float salvage;
|
||||||
|
int salvagecount;
|
||||||
|
gclient_t *client;
|
||||||
|
qboolean canuse;
|
||||||
|
|
||||||
|
if (self->client)
|
||||||
|
client = self->client;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
return false; //fixme
|
||||||
|
// get info on new armor
|
||||||
|
newinfo = (gitem_armor_t *)ent->item->info;
|
||||||
|
|
||||||
|
old_armor_index = ArmorIndex (self);
|
||||||
|
botDebugPrint("armor testing 1\n");
|
||||||
|
//ACRID WF ARMOR add the wfflags for playerclasses fixme
|
||||||
|
canuse = false;
|
||||||
|
if ((ent->item->tag == ARMOR_BODY) &&
|
||||||
|
(self->client->player_items & ITEM_BODYARMOR)){
|
||||||
|
canuse = true;}
|
||||||
|
|
||||||
|
if ((ent->item->tag == ARMOR_COMBAT) &&
|
||||||
|
(self->client->player_items & ITEM_COMBATARMOR))
|
||||||
|
{canuse = true;}
|
||||||
|
|
||||||
|
if ((ent->item->tag == ARMOR_JACKET) &&
|
||||||
|
(self->client->player_items & ITEM_JACKETARMOR)){
|
||||||
|
canuse = true;}
|
||||||
|
if (canuse == false)
|
||||||
|
{botDebugPrint("armor testing 2\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//ACRID WF ARMOR END
|
||||||
|
botDebugPrint("armor testing 3\n");
|
||||||
|
// handle armor shards specially
|
||||||
|
if (ent->item->tag == ARMOR_SHARD)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// if bot has no armor, just use it
|
||||||
|
else if (!old_armor_index)
|
||||||
|
{
|
||||||
|
return 4; // any armour is FUCKING GOOD armour!//FIXME ACRID was 4
|
||||||
|
}
|
||||||
|
// use the better armor
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// get info on old armor
|
||||||
|
if (old_armor_index == jacket_armor_index)
|
||||||
|
oldinfo = &jacketarmor_info;
|
||||||
|
else if (old_armor_index == combat_armor_index)
|
||||||
|
oldinfo = &combatarmor_info;
|
||||||
|
else // (old_armor_index == body_armor_index)
|
||||||
|
oldinfo = &bodyarmor_info;
|
||||||
|
|
||||||
|
if (newinfo->normal_protection > oldinfo->normal_protection)
|
||||||
|
{
|
||||||
|
// calc new armor values
|
||||||
|
salvage = oldinfo->normal_protection / newinfo->normal_protection;
|
||||||
|
salvagecount = salvage * client->pers.inventory[old_armor_index];
|
||||||
|
newcount = newinfo->base_count + salvagecount;
|
||||||
|
if (newcount > newinfo->max_count)
|
||||||
|
newcount = newinfo->max_count;
|
||||||
|
|
||||||
|
return (int) (((newcount - client->pers.inventory[old_armor_index]) / 50)*3 + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// calc new armor values
|
||||||
|
salvage = newinfo->normal_protection / oldinfo->normal_protection;
|
||||||
|
salvagecount = salvage * newinfo->base_count;
|
||||||
|
newcount = client->pers.inventory[old_armor_index] + salvagecount;
|
||||||
|
if (newcount > oldinfo->max_count)
|
||||||
|
newcount = oldinfo->max_count;
|
||||||
|
|
||||||
|
// if we're already maxed out then we don't need the new armor
|
||||||
|
if (client->pers.inventory[old_armor_index] >= newcount)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// armour is useful, return 4 if VERY useful :)
|
||||||
|
return (int) (((newcount - client->pers.inventory[old_armor_index]) / 50)*3 + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,451 @@
|
||||||
|
/*****************************************************************
|
||||||
|
|
||||||
|
Eraser Bot source code - by Ryan Feltrin, Added to by Acrid-
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
This file is Copyright(c) 1998, Ryan Feltrin, All Rights Reserved.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
All other files are Copyright(c) Id Software, Inc.
|
||||||
|
|
||||||
|
Please see liscense.txt in the source directory for the copyright
|
||||||
|
information regarding those files belonging to Id Software, Inc.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
Should you decide to release a modified version of Eraser, you MUST
|
||||||
|
include the following text (minus the BEGIN and END lines) in the
|
||||||
|
documentation for your modification.
|
||||||
|
|
||||||
|
--- BEGIN ---
|
||||||
|
|
||||||
|
The Eraser Bot is a product of Ryan Feltrin, and is available from
|
||||||
|
the Eraser Bot homepage, at http://impact.frag.com.
|
||||||
|
|
||||||
|
This program is a modification of the Eraser Bot, and is therefore
|
||||||
|
in NO WAY supported by Ryan Feltrin.
|
||||||
|
|
||||||
|
This program MUST NOT be sold in ANY form. If you have paid for
|
||||||
|
this product, you should contact Ryan Feltrin immediately, via
|
||||||
|
the Eraser Bot homepage.
|
||||||
|
|
||||||
|
--- END ---
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
You will find p_trail.c has not been included with the Eraser
|
||||||
|
source code release. This is NOT an error. I am unable to
|
||||||
|
distribute this file because it contains code that is bound by
|
||||||
|
legal documents, and signed by myself, never to be released
|
||||||
|
to the public. Sorry guys, but law is law.
|
||||||
|
|
||||||
|
I have therefore include the compiled version of these files
|
||||||
|
in .obj form in the src\Release and src\Debug directories.
|
||||||
|
So while you cannot edit and debug code within these files,
|
||||||
|
you can still compile this source as-is. Although these will only
|
||||||
|
work in MSVC v5.0, linux versions can be made available upon
|
||||||
|
request.
|
||||||
|
|
||||||
|
NOTE: When compiling this source, you will get a warning
|
||||||
|
message from the compiler, regarding the missing p_trail.c
|
||||||
|
file. Just ignore it, it will still compile fine.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
I, Ryan Feltrin/Acrid-, hold no responsibility for any harm caused by the
|
||||||
|
use of this source code. I also am NOT willing to provide any form
|
||||||
|
of help or support for this source code. It is provided as-is,
|
||||||
|
as a service by me, with no documentation, other then the comments
|
||||||
|
contained within the code. If you have any queries, I suggest you
|
||||||
|
visit the "official" Eraser source web-board, at
|
||||||
|
http://www.telefragged.com/epidemic/. I will stop by there from
|
||||||
|
time to time, to answer questions and help with any problems that
|
||||||
|
may arise.
|
||||||
|
|
||||||
|
Otherwise, have fun, and I look forward to seeing what can be done
|
||||||
|
with this.
|
||||||
|
|
||||||
|
-Ryan Feltrin
|
||||||
|
-Acrid-
|
||||||
|
|
||||||
|
*****************************************************************/
|
||||||
|
//
|
||||||
|
// bot_nav.c
|
||||||
|
//
|
||||||
|
// This file conatins mostly (simple) environment sampling, which
|
||||||
|
// in no way is related to the the navigation code, contained within
|
||||||
|
// p_trail.c
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "g_local.h"
|
||||||
|
#include "m_player.h"
|
||||||
|
#include "bot_procs.h"
|
||||||
|
#include "p_trail.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
================
|
||||||
|
botRoamFindBestDirection
|
||||||
|
|
||||||
|
Finds the best direction to walk for the next few seconds
|
||||||
|
Set ideal_yaw accordingly
|
||||||
|
================
|
||||||
|
*/
|
||||||
|
#define TRACE_DIST 256
|
||||||
|
void botRoamFindBestDirection(edict_t *self)
|
||||||
|
{
|
||||||
|
float best_dist=0, this_dist, best_yaw;
|
||||||
|
int i;
|
||||||
|
vec3_t dir, dest, angle, this_angle;
|
||||||
|
vec3_t mins;
|
||||||
|
trace_t trace;
|
||||||
|
|
||||||
|
if (self->last_best_direction > (level.time - 1))
|
||||||
|
return;
|
||||||
|
self->last_best_direction = level.time;
|
||||||
|
|
||||||
|
bestdirection_callsthisframe++;
|
||||||
|
if (bestdirection_callsthisframe > 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
best_yaw = self->ideal_yaw;
|
||||||
|
VectorAdd(self->mins, tv(0,0,STEPSIZE), mins);
|
||||||
|
|
||||||
|
// check eight compass directions
|
||||||
|
|
||||||
|
VectorClear(angle);
|
||||||
|
VectorClear(this_angle);
|
||||||
|
angle[1] = self->ideal_yaw;
|
||||||
|
|
||||||
|
// start at center, then fan out in 45 degree intervals, swapping between + and -
|
||||||
|
for (i=1; i<8; i++)
|
||||||
|
{
|
||||||
|
// if (i<7 && random() < 0.4) // skip random intervals
|
||||||
|
// i++;
|
||||||
|
|
||||||
|
if (i==4)
|
||||||
|
i=6;
|
||||||
|
|
||||||
|
this_angle[1] = anglemod(angle[1] + ((((i % 2)*2 - 1) * (int) (floor(i/2))) * 45));
|
||||||
|
AngleVectors(this_angle, dir, NULL, NULL);
|
||||||
|
|
||||||
|
VectorMA(self->s.origin, TRACE_DIST, dir, dest);
|
||||||
|
|
||||||
|
trace = gi.trace(self->s.origin, mins, self->maxs, dest, self, MASK_SOLID);
|
||||||
|
|
||||||
|
if (trace.fraction > 0)
|
||||||
|
{ // check that destination is onground, or not above lava/slime
|
||||||
|
dest[0] = trace.endpos[0];
|
||||||
|
dest[1] = trace.endpos[1];
|
||||||
|
dest[2] = trace.endpos[2] - 32;
|
||||||
|
|
||||||
|
if (gi.pointcontents(dest) & MASK_PLAYERSOLID)
|
||||||
|
goto nocheckground;
|
||||||
|
|
||||||
|
this_dist = trace.fraction * TRACE_DIST;
|
||||||
|
|
||||||
|
VectorCopy(trace.endpos, dest);
|
||||||
|
dest[2] -= 256;
|
||||||
|
trace = gi.trace(trace.endpos, VEC_ORIGIN, VEC_ORIGIN, dest, self, MASK_SOLID | MASK_WATER);
|
||||||
|
|
||||||
|
if ( ! ((trace.fraction == 1) || (trace.contents & MASK_WATER))) // avoid ALL forms of liquid for now
|
||||||
|
{
|
||||||
|
//gi.dprintf("Dist %i: %i\n", (int) this_angle[1], (int) this_dist);
|
||||||
|
|
||||||
|
if (trace.fraction > 0.4) // if there is a drop in this direction, try to avoid it if possible
|
||||||
|
this_dist *= 0.5;
|
||||||
|
|
||||||
|
nocheckground:
|
||||||
|
if (this_dist > best_dist)
|
||||||
|
{
|
||||||
|
best_dist = this_dist;
|
||||||
|
best_yaw = this_angle[1];
|
||||||
|
|
||||||
|
if (this_dist == TRACE_DIST)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//gi.dprintf("Best yaw: %i\n", (int) best_yaw);
|
||||||
|
|
||||||
|
self->ideal_yaw = best_yaw;
|
||||||
|
}
|
||||||
|
|
||||||
|
void botRandomJump(edict_t *self)
|
||||||
|
{
|
||||||
|
vec3_t dir, right, angles;
|
||||||
|
|
||||||
|
if (self->groundentity)
|
||||||
|
{
|
||||||
|
if (self->last_jump > (level.time - 0.5))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// if (self->last_jump > (level.time - 2))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CanJump(self))
|
||||||
|
return;
|
||||||
|
|
||||||
|
botRoamFindBestDirection(self);
|
||||||
|
|
||||||
|
VectorClear(angles);
|
||||||
|
angles[1] = self->ideal_yaw;
|
||||||
|
|
||||||
|
AngleVectors(angles, dir, right, NULL);
|
||||||
|
// VectorScale(dir, (crandom() + 0.5) / 1.5, dir);
|
||||||
|
// VectorScale(right, crandom() * 0.5, right);
|
||||||
|
// VectorAdd(dir, right, dir);
|
||||||
|
VectorNormalize2(dir, dir);
|
||||||
|
|
||||||
|
VectorScale(dir, 300 * ((random() * 0.6) + 0.4), dir);
|
||||||
|
VectorCopy(dir, self->velocity);
|
||||||
|
|
||||||
|
self->velocity[2] = 300;
|
||||||
|
self->groundentity = NULL;
|
||||||
|
// self->s.origin[2] += 1;
|
||||||
|
|
||||||
|
gi.linkentity(self);
|
||||||
|
|
||||||
|
VectorCopy(self->velocity, self->jump_velocity);
|
||||||
|
|
||||||
|
if (self->groundentity)
|
||||||
|
gi.sound(self, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, 2, 0);
|
||||||
|
|
||||||
|
self->last_jump = level.time;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern edict_t *pm_passent;
|
||||||
|
extern trace_t PM_trace (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end);
|
||||||
|
|
||||||
|
qboolean touched_player;
|
||||||
|
|
||||||
|
void BotMoveThink (edict_t *ent, usercmd_t *ucmd)
|
||||||
|
{
|
||||||
|
gclient_t *client;
|
||||||
|
edict_t *other;
|
||||||
|
int i, j;
|
||||||
|
pmove_t pm;
|
||||||
|
|
||||||
|
level.current_entity = ent;
|
||||||
|
client = ent->client;
|
||||||
|
|
||||||
|
if (level.intermissiontime)
|
||||||
|
{
|
||||||
|
client->ps.pmove.pm_type = PM_FREEZE;
|
||||||
|
// can exit intermission after five seconds
|
||||||
|
if (level.time > level.intermissiontime + 5.0
|
||||||
|
&& (ucmd->buttons & BUTTON_ANY) )
|
||||||
|
level.exitintermission = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pm_passent = ent;
|
||||||
|
|
||||||
|
// set up for pmove
|
||||||
|
memset (&pm, 0, sizeof(pm));
|
||||||
|
|
||||||
|
if (ent->movetype == MOVETYPE_NOCLIP)
|
||||||
|
client->ps.pmove.pm_type = PM_SPECTATOR;
|
||||||
|
else if (ent->s.modelindex != 255)
|
||||||
|
client->ps.pmove.pm_type = PM_GIB;
|
||||||
|
else if (ent->deadflag)
|
||||||
|
client->ps.pmove.pm_type = PM_DEAD;
|
||||||
|
else if (level.time < ent->frozentime)//botfreeze 3/99 needed still?
|
||||||
|
client->ps.pmove.pm_type = PM_DEAD;//3/99
|
||||||
|
else
|
||||||
|
client->ps.pmove.pm_type = PM_NORMAL;
|
||||||
|
client->ps.pmove.gravity = sv_gravity->value;
|
||||||
|
if (ent->maxs[2] == 4)
|
||||||
|
{
|
||||||
|
client->ps.pmove.pm_flags |= PMF_DUCKED;
|
||||||
|
ucmd->upmove = -400;
|
||||||
|
}
|
||||||
|
pm.s = client->ps.pmove;
|
||||||
|
|
||||||
|
for (i=0 ; i<3 ; i++)
|
||||||
|
{
|
||||||
|
pm.s.origin[i] = ent->s.origin[i]*8;
|
||||||
|
pm.s.velocity[i] = ent->velocity[i]*8;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
// if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s)))
|
||||||
|
// {
|
||||||
|
pm.snapinitial = true;
|
||||||
|
// gi.dprintf ("pmove changed!\n");
|
||||||
|
// }
|
||||||
|
*/
|
||||||
|
// ucmd->buttons = 128;
|
||||||
|
|
||||||
|
pm.cmd = *ucmd;
|
||||||
|
|
||||||
|
pm.trace = PM_trace; // adds default parms
|
||||||
|
pm.pointcontents = gi.pointcontents;
|
||||||
|
|
||||||
|
// perform a pmove
|
||||||
|
gi.Pmove (&pm);
|
||||||
|
|
||||||
|
// save results of pmove
|
||||||
|
client->ps.pmove = pm.s;
|
||||||
|
client->old_pmove = pm.s;
|
||||||
|
|
||||||
|
for (i=0 ; i<3 ; i++)
|
||||||
|
{
|
||||||
|
ent->s.origin[i] = pm.s.origin[i]*0.125;
|
||||||
|
ent->velocity[i] = pm.s.velocity[i]*0.125;
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorCopy (pm.mins, ent->mins);
|
||||||
|
VectorCopy (pm.maxs, ent->maxs);
|
||||||
|
|
||||||
|
client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
|
||||||
|
client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
|
||||||
|
client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);
|
||||||
|
|
||||||
|
ent->viewheight = pm.viewheight;
|
||||||
|
ent->waterlevel = pm.waterlevel;
|
||||||
|
ent->watertype = pm.watertype;
|
||||||
|
ent->groundentity = pm.groundentity;
|
||||||
|
if (pm.groundentity)
|
||||||
|
ent->groundentity_linkcount = pm.groundentity->linkcount;
|
||||||
|
|
||||||
|
if (ent->deadflag)
|
||||||
|
{
|
||||||
|
client->ps.viewangles[ROLL] = 40;
|
||||||
|
client->ps.viewangles[PITCH] = -15;
|
||||||
|
client->ps.viewangles[YAW] = client->killer_yaw;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VectorCopy (pm.viewangles, client->v_angle);
|
||||||
|
VectorCopy (pm.viewangles, client->ps.viewangles);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gi.linkentity (ent);
|
||||||
|
|
||||||
|
if (ent->movetype != MOVETYPE_NOCLIP)
|
||||||
|
G_TouchTriggers (ent);
|
||||||
|
|
||||||
|
touched_player = false;
|
||||||
|
|
||||||
|
// touch other objects
|
||||||
|
for (i=0 ; i<pm.numtouch ; i++)
|
||||||
|
{
|
||||||
|
other = pm.touchents[i];
|
||||||
|
for (j=0 ; j<i ; j++)
|
||||||
|
if (pm.touchents[j] == other)
|
||||||
|
break;
|
||||||
|
if (j != i)
|
||||||
|
continue; // duplicated
|
||||||
|
|
||||||
|
if (other->client)
|
||||||
|
touched_player = true;
|
||||||
|
|
||||||
|
if (!other->touch)
|
||||||
|
continue;
|
||||||
|
other->touch (other, ent, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int botJumpAvoidEnt(edict_t *self, edict_t *e_avoid)
|
||||||
|
{
|
||||||
|
vec3_t dir, trail_vec, vec, tr_end;
|
||||||
|
float avoid_dist;
|
||||||
|
trace_t tr;
|
||||||
|
|
||||||
|
if (!CanJump(self))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (e_avoid->owner && (((int) dmflags->value) & DF_NO_FRIENDLY_FIRE) && SameTeam(self, e_avoid->owner))
|
||||||
|
return 2;
|
||||||
|
//fixme acrid
|
||||||
|
if ((avoid_dist = entdist(self, e_avoid)) > 300)
|
||||||
|
{
|
||||||
|
self->avoid_ent = NULL;
|
||||||
|
return 2; // just keep going for our current goal
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->movetarget && (entdist(self, self->movetarget) < 256))
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!visible(self, e_avoid))
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
// make sure we attack this person, if not currently attacking anything
|
||||||
|
if (!self->enemy && e_avoid->owner && e_avoid->owner->client)
|
||||||
|
self->enemy = e_avoid->owner;
|
||||||
|
|
||||||
|
VectorSubtract(self->s.origin, e_avoid->s.origin, dir);
|
||||||
|
VectorNormalize2(dir, dir);
|
||||||
|
|
||||||
|
trail_vec[0] = dir[1];
|
||||||
|
trail_vec[1] = dir[0];
|
||||||
|
trail_vec[2] = 0;
|
||||||
|
|
||||||
|
// determine which side we're on, so we know which way to try and dodge
|
||||||
|
VectorMA(e_avoid->s.origin, 4, trail_vec, vec);
|
||||||
|
VectorSubtract(self->s.origin, vec, vec);
|
||||||
|
|
||||||
|
if (avoid_dist < 200)
|
||||||
|
{
|
||||||
|
if (VectorLength(vec) > avoid_dist)
|
||||||
|
{
|
||||||
|
VectorScale(trail_vec, -1, trail_vec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (random() < 0.5) // pick a random direction
|
||||||
|
{
|
||||||
|
VectorScale(trail_vec, -1, trail_vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add some forwards/backwards movement
|
||||||
|
VectorMA(trail_vec, crandom(), dir, trail_vec);
|
||||||
|
VectorNormalize2(trail_vec, trail_vec);
|
||||||
|
|
||||||
|
if (self->groundentity)
|
||||||
|
{
|
||||||
|
VectorMA(e_avoid->s.origin, 200, trail_vec, vec);
|
||||||
|
tr = gi.trace(self->s.origin, vec3_origin, vec3_origin, vec, self, MASK_PLAYERSOLID);
|
||||||
|
|
||||||
|
VectorCopy(tr.endpos, tr_end);
|
||||||
|
tr_end[2] -= 512;
|
||||||
|
tr = gi.trace(tr.endpos, vec3_origin, vec3_origin, tr_end, self, MASK_PLAYERSOLID | MASK_WATER);
|
||||||
|
|
||||||
|
if (!(tr.contents & (CONTENTS_LAVA | CONTENTS_SLIME)))/* && ((e_avoid->s.effects & EF_GRENADE) || (avoid_dist < 160)))*/
|
||||||
|
{ // go ahead and jump!
|
||||||
|
VectorScale(trail_vec, BOT_RUN_SPEED, dir);
|
||||||
|
dir[2] = 300;
|
||||||
|
|
||||||
|
VectorCopy(dir, self->velocity);
|
||||||
|
VectorCopy(dir, self->jump_velocity);
|
||||||
|
|
||||||
|
self->groundentity = NULL;
|
||||||
|
// self->s.origin[2] += 1;
|
||||||
|
gi.linkentity(self);
|
||||||
|
|
||||||
|
gi.sound(self, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, 2, 0);
|
||||||
|
}
|
||||||
|
else // strafe away
|
||||||
|
{
|
||||||
|
VectorCopy(trail_vec, self->avoid_dir);
|
||||||
|
self->avoid_dir_time = level.time + 0.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (self->waterlevel)
|
||||||
|
{
|
||||||
|
VectorCopy(trail_vec, self->avoid_dir);
|
||||||
|
self->avoid_dir_time = level.time + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,282 @@
|
||||||
|
/*****************************************************************
|
||||||
|
|
||||||
|
Eraser Bot source code - by Ryan Feltrin, Added to by Acrid-
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
This file is Copyright(c) 1998, Ryan Feltrin, All Rights Reserved.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
All other files are Copyright(c) Id Software, Inc.
|
||||||
|
|
||||||
|
Please see liscense.txt in the source directory for the copyright
|
||||||
|
information regarding those files belonging to Id Software, Inc.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
Should you decide to release a modified version of Eraser, you MUST
|
||||||
|
include the following text (minus the BEGIN and END lines) in the
|
||||||
|
documentation for your modification.
|
||||||
|
|
||||||
|
--- BEGIN ---
|
||||||
|
|
||||||
|
The Eraser Bot is a product of Ryan Feltrin, and is available from
|
||||||
|
the Eraser Bot homepage, at http://impact.frag.com.
|
||||||
|
|
||||||
|
This program is a modification of the Eraser Bot, and is therefore
|
||||||
|
in NO WAY supported by Ryan Feltrin.
|
||||||
|
|
||||||
|
This program MUST NOT be sold in ANY form. If you have paid for
|
||||||
|
this product, you should contact Ryan Feltrin immediately, via
|
||||||
|
the Eraser Bot homepage.
|
||||||
|
|
||||||
|
--- END ---
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
You will find p_trail.c has not been included with the Eraser
|
||||||
|
source code release. This is NOT an error. I am unable to
|
||||||
|
distribute this file because it contains code that is bound by
|
||||||
|
legal documents, and signed by myself, never to be released
|
||||||
|
to the public. Sorry guys, but law is law.
|
||||||
|
|
||||||
|
I have therefore include the compiled version of these files
|
||||||
|
in .obj form in the src\Release and src\Debug directories.
|
||||||
|
So while you cannot edit and debug code within these files,
|
||||||
|
you can still compile this source as-is. Although these will only
|
||||||
|
work in MSVC v5.0, linux versions can be made available upon
|
||||||
|
request.
|
||||||
|
|
||||||
|
NOTE: When compiling this source, you will get a warning
|
||||||
|
message from the compiler, regarding the missing p_trail.c
|
||||||
|
file. Just ignore it, it will still compile fine.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
I, Ryan Feltrin/Acrid-, hold no responsibility for any harm caused by the
|
||||||
|
use of this source code. I also am NOT willing to provide any form
|
||||||
|
of help or support for this source code. It is provided as-is,
|
||||||
|
as a service by me, with no documentation, other then the comments
|
||||||
|
contained within the code. If you have any queries, I suggest you
|
||||||
|
visit the "official" Eraser source web-board, at
|
||||||
|
http://www.telefragged.com/epidemic/. I will stop by there from
|
||||||
|
time to time, to answer questions and help with any problems that
|
||||||
|
may arise.
|
||||||
|
|
||||||
|
Otherwise, have fun, and I look forward to seeing what can be done
|
||||||
|
with this.
|
||||||
|
|
||||||
|
-Ryan Feltrin
|
||||||
|
-Acrid-
|
||||||
|
|
||||||
|
*****************************************************************/
|
||||||
|
#define ERASER_VERSION 1.01
|
||||||
|
|
||||||
|
#define MAX_BOTS 25
|
||||||
|
|
||||||
|
#define STEPSIZE 24
|
||||||
|
#define BOT_RUN_SPEED 300
|
||||||
|
#define BOT_STRAFE_SPEED 200
|
||||||
|
#define BOT_IDEAL_DIST_FROM_ENEMY 160
|
||||||
|
|
||||||
|
#define WANT_KINDA 1
|
||||||
|
#define WANT_YEH_OK 2
|
||||||
|
#define WANT_SHITYEAH 3
|
||||||
|
|
||||||
|
#define BOT_GUARDING_RANGE 600.0
|
||||||
|
|
||||||
|
// bot_ai.c
|
||||||
|
// these define how long the bot will search for it's enemy before giving up
|
||||||
|
#define BOT_SEARCH_LONG 4
|
||||||
|
#define BOT_SEARCH_MEDIUM 2
|
||||||
|
#define BOT_SEARCH_SHORT 1
|
||||||
|
|
||||||
|
#define SIGHT_FIRE_DELAY 0.8 // so bot's don't fire straight away after sighting an enemy
|
||||||
|
|
||||||
|
int spawn_bots;
|
||||||
|
int roam_calls_this_frame;
|
||||||
|
int bestdirection_callsthisframe;
|
||||||
|
|
||||||
|
// ---- BOT CHAT DATA ----
|
||||||
|
|
||||||
|
#define CHAT_GREETINGS 0
|
||||||
|
#define CHAT_INSULTS_GENERAL 1
|
||||||
|
#define CHAT_INSULTS_KICKASS 2
|
||||||
|
#define CHAT_INSULTS_LOSING 3
|
||||||
|
#define CHAT_COMEBACKS 4
|
||||||
|
#define CHAT_TEAMPLAY_HELP 5
|
||||||
|
#define CHAT_TEAMPLAY_DROPITEM 6
|
||||||
|
#define CHAT_TEAMPLAY_GROUP 7
|
||||||
|
|
||||||
|
#define NUM_CHAT_SECTIONS 8
|
||||||
|
#define MAX_CHAT_PER_SECTION 64
|
||||||
|
|
||||||
|
char *bot_chat_text[NUM_CHAT_SECTIONS][MAX_CHAT_PER_SECTION];
|
||||||
|
int bot_chat_count[NUM_CHAT_SECTIONS];
|
||||||
|
float last_bot_chat[NUM_CHAT_SECTIONS];
|
||||||
|
|
||||||
|
int num_view_weapons;
|
||||||
|
char view_weapon_models[64][64];
|
||||||
|
|
||||||
|
// -----------------------
|
||||||
|
|
||||||
|
int RoamFindBestItem(edict_t *self, edict_t *list_head, int check_paths);
|
||||||
|
void bot_ChangeYaw(edict_t *self);
|
||||||
|
void bot_MoveAI(edict_t *self, int dist);
|
||||||
|
float bot_ReachedTrail(edict_t *self);
|
||||||
|
void botMachineGun (edict_t *self);
|
||||||
|
int bot_move(edict_t *self, float dist);
|
||||||
|
int bot_oldmove(edict_t *self, float dist);
|
||||||
|
void respawn_bot (edict_t *self);
|
||||||
|
void bot_SuicideIfStuck(edict_t *self);
|
||||||
|
|
||||||
|
void bot_pain (edict_t *self, edict_t *other, float kick, int damage);
|
||||||
|
void bot_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
|
||||||
|
void bot_run (edict_t *self);
|
||||||
|
|
||||||
|
edict_t *bot_FindAnyTrail(edict_t *bot);
|
||||||
|
int CanJump(edict_t *ent);
|
||||||
|
|
||||||
|
void bot_AnimateFrames(edict_t *self);
|
||||||
|
void bot_roam (edict_t *self, int force_enemy);
|
||||||
|
int botCheckStuck(edict_t *self);
|
||||||
|
int CanStand(edict_t *self);
|
||||||
|
int CanSee(edict_t *self, edict_t *targ);
|
||||||
|
int CanReach(edict_t *self, edict_t *targ);
|
||||||
|
|
||||||
|
int botdebug;
|
||||||
|
void botDebugPrint(char *msg, ...);
|
||||||
|
|
||||||
|
// bot_wpns.c
|
||||||
|
|
||||||
|
#define FIRE_INTERVAL_BLASTER 0.6
|
||||||
|
#define FIRE_INTERVAL_ROCKETLAUNCHER 0.8
|
||||||
|
#define FIRE_INTERVAL_GRENADELAUNCHER 0.9
|
||||||
|
#define FIRE_INTERVAL_RAILGUN 1.5
|
||||||
|
#define FIRE_INTERVAL_HYPERBLASTER 0
|
||||||
|
#define FIRE_INTERVAL_CHAINGUN 0
|
||||||
|
#define FIRE_INTERVAL_MACHINEGUN 0
|
||||||
|
#define FIRE_INTERVAL_SHOTGUN 1
|
||||||
|
#define FIRE_INTERVAL_SSHOTGUN 1
|
||||||
|
#define FIRE_INTERVAL_BFG 2.8
|
||||||
|
//ACRID NEW DEFINES FOR WF WEAPONS
|
||||||
|
#define FIRE_INTERVAL_SNIPERRIFLE 1
|
||||||
|
#define FIRE_INTERVAL_LIGHTNINGGUN 1.5
|
||||||
|
#define FIRE_INTERVAL_INFECTEDDART 0.9
|
||||||
|
#define FIRE_INTERVAL_PULSECANNON 0
|
||||||
|
#define FIRE_INTERVAL_TELSACOIL 0//42 Acrid
|
||||||
|
#define FIRE_INTERVAL_NEEDLER 0
|
||||||
|
#define FIRE_INTERVAL_FLAMETHROWER 0.3
|
||||||
|
#define FIRE_INTERVAL_ROCKETNAPALMLAUNCHER 0.8
|
||||||
|
#define FIRE_INTERVAL_PELLETROCKETLAUNCHER 0.8
|
||||||
|
#define FIRE_INTERVAL_ROCKETCLUSTERLAUNCHER 0.8
|
||||||
|
#define FIRE_INTERVAL_STINGERROCKETLAUNCHER 0.8
|
||||||
|
#define FIRE_INTERVAL_SHC 1
|
||||||
|
#define FIRE_INTERVAL_GRENADES 1
|
||||||
|
#define FIRE_INTERVAL_POISONDART 0.9
|
||||||
|
#define FIRE_INTERVAL_AK47 0
|
||||||
|
#define FIRE_INTERVAL_PISTOL 0.6
|
||||||
|
#define FIRE_INTERVAL_KNIFE 1
|
||||||
|
//ACRID NEW DEFINES FOR WF WEAPONS
|
||||||
|
|
||||||
|
#define BOT_CHANGEWEAPON_DELAY 0.9
|
||||||
|
|
||||||
|
void bot_FireWeapon(edict_t *self);
|
||||||
|
void bot_Attack(edict_t *self);
|
||||||
|
|
||||||
|
void botBlaster (edict_t *self);
|
||||||
|
void botMachineGun (edict_t *self);
|
||||||
|
void botShotgun (edict_t *self);
|
||||||
|
void botSuperShotgun (edict_t *self);
|
||||||
|
void botChaingun (edict_t *self);
|
||||||
|
void botRailgun (edict_t *self);
|
||||||
|
void botRocketLauncher (edict_t *self);
|
||||||
|
void botGrenadeLauncher (edict_t *self);
|
||||||
|
void botHyperblaster (edict_t *self);
|
||||||
|
void botBFG (edict_t *self);
|
||||||
|
//ACRID WF STUFF
|
||||||
|
void botSniperRifle (edict_t *self);
|
||||||
|
void botLightningGun (edict_t *self);
|
||||||
|
void botInfectedDart (edict_t *self);
|
||||||
|
void botPulseCannon (edict_t *self);
|
||||||
|
void botTelsaCoil (edict_t *self);//42
|
||||||
|
void botFlameThrower (edict_t *self);
|
||||||
|
void botNeedler (edict_t *self);
|
||||||
|
void botPelletRocketLauncher (edict_t *self);
|
||||||
|
void botRocketNapalmLauncher (edict_t *self);
|
||||||
|
void botRocketClusterLauncher (edict_t *self);
|
||||||
|
void botStingerRocketLauncher (edict_t *self);
|
||||||
|
void botSHC (edict_t *self);
|
||||||
|
void botGrenades (edict_t *self);
|
||||||
|
void botPoisonDart (edict_t *self);
|
||||||
|
void botAk47 (edict_t *self);
|
||||||
|
void botPistol (edict_t *self);
|
||||||
|
//ACRID WF STUFF
|
||||||
|
|
||||||
|
void botPickBestWeapon(edict_t *self);
|
||||||
|
void botPickBestGrenade(edict_t *self);//acrid
|
||||||
|
int botHasWeaponForAmmo (gclient_t *client, gitem_t *item);
|
||||||
|
//int ClientHasAnyWeapon(gclient_t *client);//acrid coed
|
||||||
|
int botCanPickupAmmo (gclient_t *client, gitem_t *item);
|
||||||
|
int botCanPickupArmor (edict_t *self, edict_t *ent);
|
||||||
|
void botPickBestFarWeapon(edict_t *self);
|
||||||
|
void botPickBestCloseWeapon(edict_t *self);
|
||||||
|
int botAmmoIndex(gitem_t *weapon);
|
||||||
|
qboolean botHasWeaponInInventory(edict_t *self, gitem_t *weapon);
|
||||||
|
qboolean botHasAmmoForWeapon(edict_t *self, gitem_t *weapon);
|
||||||
|
qboolean botHasThisWeapon(edict_t *self, gitem_t *weapon);
|
||||||
|
|
||||||
|
void GetBotFireForWeapon(gitem_t *weapon, void (**bot_fire)(edict_t *self));
|
||||||
|
int botCanPickupPack (edict_t *self, edict_t *ent);//acrid
|
||||||
|
int botCanPlaceSpecial (edict_t *self, edict_t *ent);//42 acrid
|
||||||
|
// bot_spawn.c
|
||||||
|
edict_t *spawn_bot (char *botname);
|
||||||
|
void botDisconnect(edict_t *self);
|
||||||
|
|
||||||
|
// bot_misc.c
|
||||||
|
void ReadBotConfig();
|
||||||
|
bot_info_t *GetBotData(char *botname);
|
||||||
|
void NodeDebug(char *fmt, ...);
|
||||||
|
void FindVisibleItemsFromNode(edict_t *node);
|
||||||
|
void AdjustRatingsToSkill(edict_t *self);
|
||||||
|
edict_t *DrawLine(edict_t *owner, vec3_t spos, vec3_t epos);
|
||||||
|
void TeamGroup(edict_t *ent);
|
||||||
|
void TeamDisperse(edict_t *self);
|
||||||
|
void BotGreeting(edict_t *chat);
|
||||||
|
void BotInsultStart(edict_t *self);
|
||||||
|
void BotInsult(edict_t *self, edict_t *enemy, int chat_type);
|
||||||
|
qboolean SameTeam(edict_t *plyr1, edict_t *plyr2);
|
||||||
|
float HomeFlagDist(edict_t *self);
|
||||||
|
qboolean CarryingFlag(edict_t *ent);
|
||||||
|
|
||||||
|
// bot_nav.c
|
||||||
|
void botRoamFindBestDirection(edict_t *self);
|
||||||
|
void botRandomJump(edict_t *self);
|
||||||
|
void BotMoveThink (edict_t *ent, usercmd_t *ucmd);
|
||||||
|
int botJumpAvoidEnt(edict_t *self, edict_t *e_avoid);
|
||||||
|
|
||||||
|
|
||||||
|
// FIXME: this should go in g_local.h
|
||||||
|
qboolean monster_start (edict_t *self);
|
||||||
|
qboolean monster_start_go (edict_t *self);
|
||||||
|
void SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles);
|
||||||
|
void Use_Quad (edict_t *ent, gitem_t *item);
|
||||||
|
void ClientDisconnect (edict_t *ent);
|
||||||
|
void Use_Plat (edict_t *ent, edict_t *other, edict_t *activator);
|
||||||
|
void ShowGun(edict_t *ent);
|
||||||
|
void FlagPathTouch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf);
|
||||||
|
|
||||||
|
extern char respawn_bots[64][256];
|
||||||
|
|
||||||
|
// CTF stuff
|
||||||
|
extern gitem_t *flag1_item;
|
||||||
|
extern gitem_t *flag2_item;
|
||||||
|
|
||||||
|
extern edict_t *flag1_ent;
|
||||||
|
extern edict_t *flag2_ent;
|
||||||
|
|
||||||
|
extern gitem_t *item_tech1, *item_tech2, *item_tech3, *item_tech4;
|
||||||
|
|
||||||
|
extern edict_t *flagreturn1_ent;//$
|
||||||
|
extern edict_t *flagreturn2_ent;//$
|
|
@ -0,0 +1,482 @@
|
||||||
|
/*****************************************************************
|
||||||
|
|
||||||
|
Eraser Bot source code - by Ryan Feltrin, Added to by Acrid-
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
This file is Copyright(c) 1998, Ryan Feltrin, All Rights Reserved.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
All other files are Copyright(c) Id Software, Inc.
|
||||||
|
|
||||||
|
Please see liscense.txt in the source directory for the copyright
|
||||||
|
information regarding those files belonging to Id Software, Inc.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
Should you decide to release a modified version of Eraser, you MUST
|
||||||
|
include the following text (minus the BEGIN and END lines) in the
|
||||||
|
documentation for your modification.
|
||||||
|
|
||||||
|
--- BEGIN ---
|
||||||
|
|
||||||
|
The Eraser Bot is a product of Ryan Feltrin, and is available from
|
||||||
|
the Eraser Bot homepage, at http://impact.frag.com.
|
||||||
|
|
||||||
|
This program is a modification of the Eraser Bot, and is therefore
|
||||||
|
in NO WAY supported by Ryan Feltrin.
|
||||||
|
|
||||||
|
This program MUST NOT be sold in ANY form. If you have paid for
|
||||||
|
this product, you should contact Ryan Feltrin immediately, via
|
||||||
|
the Eraser Bot homepage.
|
||||||
|
|
||||||
|
--- END ---
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
You will find p_trail.c has not been included with the Eraser
|
||||||
|
source code release. This is NOT an error. I am unable to
|
||||||
|
distribute this file because it contains code that is bound by
|
||||||
|
legal documents, and signed by myself, never to be released
|
||||||
|
to the public. Sorry guys, but law is law.
|
||||||
|
|
||||||
|
I have therefore include the compiled version of these files
|
||||||
|
in .obj form in the src\Release and src\Debug directories.
|
||||||
|
So while you cannot edit and debug code within these files,
|
||||||
|
you can still compile this source as-is. Although these will only
|
||||||
|
work in MSVC v5.0, linux versions can be made available upon
|
||||||
|
request.
|
||||||
|
|
||||||
|
NOTE: When compiling this source, you will get a warning
|
||||||
|
message from the compiler, regarding the missing p_trail.c
|
||||||
|
file. Just ignore it, it will still compile fine.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
I, Ryan Feltrin/Acrid-, hold no responsibility for any harm caused by the
|
||||||
|
use of this source code. I also am NOT willing to provide any form
|
||||||
|
of help or support for this source code. It is provided as-is,
|
||||||
|
as a service by me, with no documentation, other then the comments
|
||||||
|
contained within the code. If you have any queries, I suggest you
|
||||||
|
visit the "official" Eraser source web-board, at
|
||||||
|
http://www.telefragged.com/epidemic/. I will stop by there from
|
||||||
|
time to time, to answer questions and help with any problems that
|
||||||
|
may arise.
|
||||||
|
|
||||||
|
Otherwise, have fun, and I look forward to seeing what can be done
|
||||||
|
with this.
|
||||||
|
|
||||||
|
-Ryan Feltrin
|
||||||
|
-Acrid-
|
||||||
|
|
||||||
|
*****************************************************************/
|
||||||
|
/* bot_spawn.c */
|
||||||
|
|
||||||
|
#include "g_local.h"
|
||||||
|
#include "m_player.h"
|
||||||
|
#include "bot_procs.h"
|
||||||
|
///Q2 Camera Begin
|
||||||
|
#include "camclient.h"
|
||||||
|
///Q2 Camera End
|
||||||
|
#include "stdlog.h"
|
||||||
|
|
||||||
|
qboolean ClientConnect (edict_t *ent, char *userinfo, qboolean loadgame);
|
||||||
|
|
||||||
|
// BEGIN: SABIN code
|
||||||
|
edict_t *bot_GetLastFreeClient (void)
|
||||||
|
{
|
||||||
|
edict_t *bot;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = maxclients->value; i > 0; i--)
|
||||||
|
{
|
||||||
|
bot = g_edicts + i + 1;
|
||||||
|
|
||||||
|
if (!bot->inuse)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->inuse)
|
||||||
|
bot = NULL;
|
||||||
|
|
||||||
|
return bot;
|
||||||
|
}
|
||||||
|
// END: SABIN code
|
||||||
|
|
||||||
|
void respawn_bot (edict_t *self)
|
||||||
|
{
|
||||||
|
if (level.intermissiontime)
|
||||||
|
return;
|
||||||
|
|
||||||
|
self->s.event = EV_PLAYER_TELEPORT;
|
||||||
|
|
||||||
|
PutClientInServer(self);
|
||||||
|
|
||||||
|
self->last_goal = NULL;
|
||||||
|
self->enemy = self->goalentity = self->movetarget = NULL;
|
||||||
|
self->viewheight = 22;
|
||||||
|
|
||||||
|
// reset weapon to blaster
|
||||||
|
self->last_fire = level.time + 0.2;
|
||||||
|
self->fire_interval = FIRE_INTERVAL_BLASTER;
|
||||||
|
self->bot_fire = botBlaster;
|
||||||
|
|
||||||
|
self->bored_suicide_time = -1;
|
||||||
|
self->checkstuck_time = level.time;
|
||||||
|
self->last_reached_trail = level.time + 1;
|
||||||
|
|
||||||
|
self->client->killer_yaw = 0; // chaingun wind-up
|
||||||
|
self->avoid_ent = NULL;
|
||||||
|
self->flagpath_goal = NULL;
|
||||||
|
self->last_move_nocloser = level.time;
|
||||||
|
|
||||||
|
// go for it
|
||||||
|
walkmonster_start(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find an available edict, and initialize some default values
|
||||||
|
edict_t *G_SpawnBot ()
|
||||||
|
{
|
||||||
|
edict_t *bot;
|
||||||
|
|
||||||
|
if (!deathmatch->value)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!bot_calc_nodes->value) && !loaded_trail_flag)
|
||||||
|
{
|
||||||
|
my_bprintf(PRINT_HIGH, "Route-table not found!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_bot_spawn = level.time;
|
||||||
|
|
||||||
|
// bot = G_Spawn();
|
||||||
|
bot = bot_GetLastFreeClient();
|
||||||
|
|
||||||
|
if (!bot)
|
||||||
|
{
|
||||||
|
gi.dprintf("No client spots available!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// bot->bot_client = gi.TagMalloc (sizeof(struct gclient_s), TAG_GAME);
|
||||||
|
bot->bot_stats = gi.TagMalloc (sizeof(bot_stats_t), TAG_GAME);
|
||||||
|
|
||||||
|
if (!bot->bot_stats)
|
||||||
|
{
|
||||||
|
gi.dprintf("Could not allocate Bot Stats!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bot->classname = "player";
|
||||||
|
|
||||||
|
bot->movetype = MOVETYPE_WALK;
|
||||||
|
bot->solid = SOLID_BBOX;
|
||||||
|
|
||||||
|
//K2:Begin//fixme remove
|
||||||
|
bot->takedamage = DAMAGE_YES;
|
||||||
|
//K2:End
|
||||||
|
|
||||||
|
VectorSet (bot->mins, -16, -16, -24);
|
||||||
|
VectorSet (bot->maxs, 16, 16, 32);
|
||||||
|
|
||||||
|
bot->health = bot->max_health = 100;
|
||||||
|
bot->mass = 200;
|
||||||
|
bot->gravity = 1;
|
||||||
|
|
||||||
|
bot->last_goal = NULL;
|
||||||
|
|
||||||
|
bot->pain = bot_pain;
|
||||||
|
bot->die = bot_die;
|
||||||
|
|
||||||
|
bot->monsterinfo.stand = bot_run; //bot_stand;
|
||||||
|
bot->monsterinfo.walk = bot_run; //bot_walk;
|
||||||
|
bot->monsterinfo.run = bot_run;
|
||||||
|
bot->monsterinfo.attack = bot_run;
|
||||||
|
bot->monsterinfo.melee = NULL;
|
||||||
|
bot->monsterinfo.sight = NULL;
|
||||||
|
|
||||||
|
bot->monsterinfo.scale = MODEL_SCALE;
|
||||||
|
|
||||||
|
bot->enemy = bot->goalentity = bot->movetarget = NULL;
|
||||||
|
|
||||||
|
players[num_players++] = bot;
|
||||||
|
|
||||||
|
bot->last_fire = level.time + 0.2;
|
||||||
|
bot->fire_interval = FIRE_INTERVAL_BLASTER;
|
||||||
|
bot->bot_fire = botBlaster;
|
||||||
|
|
||||||
|
bot->bored_suicide_time = -1;
|
||||||
|
bot->checkstuck_time = level.time;
|
||||||
|
|
||||||
|
bot->viewheight = 22;
|
||||||
|
bot->yaw_speed = 50; // turn at yaw_speed degrees per FRAME
|
||||||
|
|
||||||
|
bot->last_reached_trail = level.time + 1;
|
||||||
|
|
||||||
|
bot->avoid_ent = NULL;
|
||||||
|
bot->last_move_nocloser = level.time;
|
||||||
|
|
||||||
|
return bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the ClientBegin() and ClientBeginDeathmatch() functions in one routine, that is bot-specific
|
||||||
|
edict_t *spawn_bot (char *botname)
|
||||||
|
{
|
||||||
|
edict_t *bot, *chat;
|
||||||
|
bot_info_t *botdata=NULL;
|
||||||
|
//fixme debugger char skin[256];
|
||||||
|
char userinfo[MAX_INFO_STRING];
|
||||||
|
vec3_t spawn_origin, spawn_angles;
|
||||||
|
char WFclass[256];
|
||||||
|
|
||||||
|
if (!(botdata = GetBotData(botname)))
|
||||||
|
{
|
||||||
|
gi.dprintf("Unable to find bot, or no bots left\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bot = G_SpawnBot();
|
||||||
|
|
||||||
|
if (!bot)
|
||||||
|
{
|
||||||
|
gi.dprintf("Unable to spawn bot: cannot create entity\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bot->bot_client = true;
|
||||||
|
bot->client = &game.clients[bot-g_edicts-1];
|
||||||
|
|
||||||
|
memset(bot->client, 0, sizeof(*(bot->client)));
|
||||||
|
|
||||||
|
// copy the stats across from the bot config
|
||||||
|
botdata->ingame_count++;
|
||||||
|
bot->botdata = botdata;
|
||||||
|
|
||||||
|
strcpy(WFclass, botdata->wfclass);
|
||||||
|
|
||||||
|
// BEGIN: SABIN code
|
||||||
|
// initialise userinfo
|
||||||
|
memset (userinfo, 0, sizeof(userinfo));
|
||||||
|
|
||||||
|
// add bot's name/skin/hand to userinfo
|
||||||
|
Info_SetValueForKey (userinfo, "name", botdata->name);
|
||||||
|
Info_SetValueForKey (userinfo, "WFclass", botdata->wfclass);
|
||||||
|
Info_SetValueForKey (userinfo, "hand", "2"); // bot is center handed for now
|
||||||
|
// END: SABIN code
|
||||||
|
|
||||||
|
///Q2 Camera Begin
|
||||||
|
EntityListAdd(bot);
|
||||||
|
///Q2 Camera End
|
||||||
|
//WF START ACRID INITIAL CLASS SPAWNING
|
||||||
|
|
||||||
|
//set respawn protection time
|
||||||
|
bot->client->protecttime = level.time + RESPAWN_PROTECT_TIME;
|
||||||
|
// bot->client->pers.player_class = bot->client->pers.next_player_class;
|
||||||
|
if (strcmp("1", WFclass) == 0)
|
||||||
|
{ bot->client->pers.player_class = 1;
|
||||||
|
bot->client->pers.next_player_class = 1;}
|
||||||
|
else if (strcmp("2", WFclass) == 0)
|
||||||
|
{ bot->client->pers.player_class = 2;
|
||||||
|
bot->client->pers.next_player_class = 2;}
|
||||||
|
else if (strcmp("3", WFclass) == 0)
|
||||||
|
{ bot->client->pers.player_class = 3;
|
||||||
|
bot->client->pers.next_player_class = 3;}
|
||||||
|
else if (strcmp("4", WFclass) == 0)
|
||||||
|
{bot->client->pers.player_class = 4;
|
||||||
|
bot->client->pers.next_player_class = 4;}
|
||||||
|
else if (strcmp("5", WFclass) == 0)
|
||||||
|
{ bot->client->pers.player_class = 5;
|
||||||
|
bot->client->pers.next_player_class = 5;}
|
||||||
|
else if (strcmp("6", WFclass) == 0)
|
||||||
|
{ bot->client->pers.player_class = 6;
|
||||||
|
bot->client->pers.next_player_class = 6;}
|
||||||
|
else if (strcmp("7", WFclass) == 0)
|
||||||
|
{ bot->client->pers.player_class = 7;
|
||||||
|
bot->client->pers.next_player_class = 7;}
|
||||||
|
else if (strcmp("8", WFclass) == 0)
|
||||||
|
{ bot->client->pers.player_class = 8;
|
||||||
|
bot->client->pers.next_player_class = 8;}
|
||||||
|
else if (strcmp("9", WFclass) == 0)
|
||||||
|
{ bot->client->pers.player_class = 9;
|
||||||
|
bot->client->pers.next_player_class = 9;}
|
||||||
|
else if (strcmp("10", WFclass) == 0)
|
||||||
|
{ bot->client->pers.player_class = 10;
|
||||||
|
bot->client->pers.next_player_class = 10;}
|
||||||
|
//WF END ACRID FIXES SKIN BUGS,CLASS BUGS,WEAPONS BUG clean this up
|
||||||
|
ClientConnect (bot, userinfo, false);
|
||||||
|
|
||||||
|
if (ctf->value)
|
||||||
|
{
|
||||||
|
my_bprintf(PRINT_HIGH, "%s joined the %s team.\n",
|
||||||
|
bot->client->pers.netname, CTFTeamName(bot->client->resp.ctf_team));
|
||||||
|
sl_LogPlayerTeamChange( &gi,
|
||||||
|
bot->client->pers.netname,
|
||||||
|
CTFTeamName(bot->client->resp.ctf_team));
|
||||||
|
}
|
||||||
|
|
||||||
|
SelectSpawnPoint (bot, spawn_origin, spawn_angles);
|
||||||
|
//INSERT TESTS HERE
|
||||||
|
// botDebugPrint("SPAWN_BOT %s (ACRID)\n",userinfo);
|
||||||
|
|
||||||
|
VectorCopy(spawn_origin, bot->s.origin);
|
||||||
|
// bot->s.origin[2] += 1; // make sure off ground
|
||||||
|
|
||||||
|
VectorCopy(spawn_angles, bot->s.angles);
|
||||||
|
|
||||||
|
bot->client->killer_yaw = 0; // chaingun wind-up
|
||||||
|
|
||||||
|
// clear playerstate values
|
||||||
|
memset (&bot->client->ps, 0, sizeof(bot->client->ps));
|
||||||
|
|
||||||
|
bot->client->ps.pmove.origin[0] = bot->s.origin[0]*8;
|
||||||
|
bot->client->ps.pmove.origin[1] = bot->s.origin[1]*8;
|
||||||
|
bot->client->ps.pmove.origin[2] = bot->s.origin[2]*8;
|
||||||
|
|
||||||
|
bot->client->ps.fov = 90;
|
||||||
|
|
||||||
|
// send effect
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
gi.WriteShort (bot-g_edicts);
|
||||||
|
gi.WriteByte (MZ_LOGIN);
|
||||||
|
gi.multicast (bot->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
// copy the bot stats
|
||||||
|
memcpy(bot->bot_stats, &(botdata->bot_stats), sizeof(bot_stats_t));
|
||||||
|
|
||||||
|
// set starting skill level
|
||||||
|
bot->skill_level = skill->value;
|
||||||
|
AdjustRatingsToSkill(bot);
|
||||||
|
|
||||||
|
// bot->s.modelindex = gi.modelindex(model);
|
||||||
|
bot->s.skinnum = bot-g_edicts - 1;
|
||||||
|
bot->s.modelindex = 255;
|
||||||
|
bot->s.modelindex2 = 255;
|
||||||
|
// botDebugPrint("SPAWN_BOT (ACRID)\n");
|
||||||
|
/*
|
||||||
|
bot->client->buttons = bot->s.modelindex;
|
||||||
|
bot->client->oldbuttons = bot->s.skinnum;
|
||||||
|
*/
|
||||||
|
bot->map = G_CopyString(botdata->name);
|
||||||
|
strcpy(bot->client->pers.netname, botdata->name);
|
||||||
|
my_bprintf(PRINT_HIGH, "%s entered the game", bot->client->pers.netname);
|
||||||
|
|
||||||
|
// set visible model vwep with 3.20
|
||||||
|
ShowGun(bot);
|
||||||
|
|
||||||
|
/*
|
||||||
|
if (view_weapons->value && (bot->s.modelindex2 == 255) && bot_show_connect_info->value)
|
||||||
|
{
|
||||||
|
my_bprintf(PRINT_HIGH, " (no view weapon)");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
my_bprintf(PRINT_HIGH, "\n");
|
||||||
|
|
||||||
|
bot_count++;
|
||||||
|
|
||||||
|
// generic bot stuff
|
||||||
|
if (!KillBox (bot))
|
||||||
|
{ // could't spawn in?
|
||||||
|
}
|
||||||
|
|
||||||
|
gi.linkentity (bot);
|
||||||
|
|
||||||
|
bot->viewheight = 22;
|
||||||
|
|
||||||
|
bot->inuse = true;
|
||||||
|
|
||||||
|
// go for it
|
||||||
|
walkmonster_start(bot);
|
||||||
|
|
||||||
|
if (random() < 0.3)
|
||||||
|
{
|
||||||
|
// spawn the greetings thinker
|
||||||
|
chat = G_Spawn();
|
||||||
|
chat->owner = bot;
|
||||||
|
chat->think = BotGreeting;
|
||||||
|
chat->nextthink = level.time + 1.5 + random();
|
||||||
|
}
|
||||||
|
bot->wf_team = bot->client->resp.ctf_team;
|
||||||
|
wf_InitPlayerClass(bot->client);//ACRID
|
||||||
|
|
||||||
|
//K2 botcam acrid
|
||||||
|
bot->client->resp.inServer=true;
|
||||||
|
|
||||||
|
return bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int num_clients;
|
||||||
|
//K2:Added for camera..must add player back to players array
|
||||||
|
void botAddPlayer(edict_t *self)
|
||||||
|
{
|
||||||
|
players[num_players] = self;
|
||||||
|
num_players++;
|
||||||
|
num_clients++;
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
void botRemovePlayer(edict_t *self)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
self->health = 0; // so other bots stop looking for us
|
||||||
|
|
||||||
|
// remove the client from the players array
|
||||||
|
for (i=0; i<num_players; i++)
|
||||||
|
if (players[i] == self)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (i == num_players) // didn't find them
|
||||||
|
{
|
||||||
|
gi.dprintf("WARNING: Unable to remove player from player[] array, problems will arise.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
for (; i<num_players; i++)
|
||||||
|
players[i-1] = players[i];
|
||||||
|
|
||||||
|
players[i] = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
// remove from team list
|
||||||
|
if (self->client->team)
|
||||||
|
{
|
||||||
|
self->client->team->num_players--;
|
||||||
|
self->client->team->num_bots--;
|
||||||
|
self->client->team = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->client->resp.ctf_team = CTF_NOTEAM;
|
||||||
|
|
||||||
|
num_players--;
|
||||||
|
|
||||||
|
if (self->bot_client)
|
||||||
|
{
|
||||||
|
bot_count--;
|
||||||
|
self->botdata->ingame_count--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
num_clients--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// tell all other bots not to look for this bot anymore
|
||||||
|
for (i=0; i<num_players; i++)
|
||||||
|
if (players[i]->enemy == self)
|
||||||
|
players[i]->enemy = players[i]->goalentity = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void botDisconnect(edict_t *self)
|
||||||
|
{ // disconnects a bot from the game
|
||||||
|
|
||||||
|
ClientDisconnect(self);
|
||||||
|
|
||||||
|
self->bot_client = false;
|
||||||
|
self->bot_stats = NULL;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,107 @@
|
||||||
|
# Eraser Configuration file
|
||||||
|
#
|
||||||
|
# (thanks to calcu0, calcu0@prtc.net, for the documentation and formatting)
|
||||||
|
#
|
||||||
|
# BOT NOTES:
|
||||||
|
# + Each bot occupies one line.
|
||||||
|
# + String fields are enclosed in quotation marks (")
|
||||||
|
# + Obviously, a hash (#) character signifies a comment line
|
||||||
|
# + Skins will be ignored in teamplay mode, with color coded skins being used instead (see
|
||||||
|
# Teamplay section)
|
||||||
|
# + Individual bots can be added manually using "bot_name <name>"
|
||||||
|
# + A bot with the Quad powerup, will automatically go into "psycho" mode :)
|
||||||
|
#
|
||||||
|
# SETTING NOTES:
|
||||||
|
# 1) Firing Accuracy Referes to aim quality, (1[shoot for luck] - 5[THRESH])
|
||||||
|
# 2) A high aggressiveness doesn't necessarily result in a kick-ass player. They'll
|
||||||
|
# be less likely to build up a sufficient arsenal of weapons, and may not retreat from an
|
||||||
|
# unsuccessful attack until it's too late. On the other hand, they'll be more likely to stick
|
||||||
|
# out an attack at the chance of getting more frags, rather than less kills (as a retreat
|
||||||
|
# would yeild). A high aggressiveness could also produce more frags by shooting others first.
|
||||||
|
# 3) A bot with high combat skills will yeild a harder to hit bot, it will improve the
|
||||||
|
# tactics it will use on jumping, strafing and dodging not only your shots but your sight;
|
||||||
|
# while a bot with a low combat skill rating will generally stand in one spot shooting
|
||||||
|
# sometimes.
|
||||||
|
# 4) The Favorite Weapon, need I explain. Anyways, it signifies the bot will always try to
|
||||||
|
# either get first and use most often.
|
||||||
|
# 5) QuadFreak toggles the bots use or anxiety to get a quad... Watch out for them mood
|
||||||
|
# swings ;)
|
||||||
|
# 6) Camper toggles if the bot will tend to camp out or not DUH!!
|
||||||
|
# 7) Average ping signifies the aparent average ping of bots, just a static addition anyways,
|
||||||
|
# cool if ya wanna fool your friends ;)
|
||||||
|
|
||||||
|
[bots]
|
||||||
|
|
||||||
|
# "BotName"
|
||||||
|
# | "Class"
|
||||||
|
# | | FiringAccuracy (1-5)
|
||||||
|
# | | | Aggressiveness (1-5)
|
||||||
|
# | | | | CombatSkills (1-5)
|
||||||
|
# | | | | | FavoriteWeapon (2-24])
|
||||||
|
# | | | | | | QuadFreak (1[yes] - 0[no])
|
||||||
|
# | | | | | | | Camper (1[yes] - 0[no])
|
||||||
|
# | | | | | | | | AveragePing (0-400 [for realism's sake])
|
||||||
|
# | | | | | | | | |
|
||||||
|
# | | | | | | | | |
|
||||||
|
# Cyborgs
|
||||||
|
"ReconBot1" "1" 5 5 5 3 0 0 300
|
||||||
|
"NurseBot1" "2" 5 5 5 12 0 0 400
|
||||||
|
"EngineerBot1" "3" 5 1 5 22 0 1 400
|
||||||
|
"MarineBot1" "4" 5 5 5 7 0 0 600
|
||||||
|
"CyborgBot1" "5" 5 5 5 6 0 0 430
|
||||||
|
"ArsonistBot1" "6" 5 5 5 17 0 0 200
|
||||||
|
"GunnerBot1" "7" 5 1 5 5 0 1 200
|
||||||
|
"SniperBot1" "8" 5 1 5 8 0 1 230
|
||||||
|
"SpyBot1" "9" 5 5 5 4 0 0 300
|
||||||
|
"MercenaryBot1" "10" 5 5 5 23 0 0 200
|
||||||
|
|
||||||
|
# Males
|
||||||
|
"ReconBot2" "1" 5 5 5 3 0 0 300
|
||||||
|
"NurseBot2" "2" 5 5 5 12 0 0 400
|
||||||
|
"EngineerBot2" "3" 5 1 5 22 0 1 400
|
||||||
|
"MarineBot2" "4" 5 5 5 7 0 0 600
|
||||||
|
"CyborgBot2" "5" 5 5 5 6 0 0 430
|
||||||
|
"ArsonistBot2" "6" 5 5 5 17 0 0 200
|
||||||
|
"GunnerBot2" "7" 5 1 5 5 0 1 200
|
||||||
|
"SniperBot2" "8" 5 1 5 8 0 1 230
|
||||||
|
"SpyBot2" "9" 5 5 5 4 0 0 300
|
||||||
|
"MercenaryBot2" "10" 5 5 5 23 0 0 200
|
||||||
|
|
||||||
|
# Females
|
||||||
|
"ReconBot3" "1" 5 5 5 3 0 0 300
|
||||||
|
"NurseBot3" "2" 5 5 5 12 0 0 400
|
||||||
|
"EngineerBot3" "3" 5 1 5 22 0 1 400
|
||||||
|
"MarineBot3" "4" 5 5 5 7 0 0 600
|
||||||
|
"CyborgBot3" "5" 5 5 5 6 0 0 430
|
||||||
|
"ArsonistBot3" "6" 5 5 5 17 0 0 200
|
||||||
|
"GunnerBot3" "7" 5 1 5 5 0 1 200
|
||||||
|
"SniperBot3" "8" 5 1 5 8 0 1 230
|
||||||
|
"SpyBot3" "9" 5 5 5 4 0 0 300
|
||||||
|
"MercenaryBot3" "10" 5 5 5 23 0 0 200
|
||||||
|
|
||||||
|
# TEAM NOTES:
|
||||||
|
#
|
||||||
|
# + Each team consists of a series of bots, maximum of 32 per team
|
||||||
|
# + When a human joins a team, the last bot on the list is removed from the game
|
||||||
|
# + Unknown bots are assigned skills automatically, according to their name (so they'll be the
|
||||||
|
# same each game)
|
||||||
|
# + SPACES NOT ALLOWED IN TEAMNAMES !!
|
||||||
|
#
|
||||||
|
# FIELDS: Team name, Abbrev, Default Skin, [players]
|
||||||
|
|
||||||
|
[teams]
|
||||||
|
|
||||||
|
"WF" "BOT" "" ["Reconbot1" "Nursebot1" "Engineerbot1" "Marinebot1" "Cyborgbot1" "Arsonistbot1" "Spybot1" "Mercenarybot1" "Gunnerbot1" "Sniperbot1"]
|
||||||
|
|
||||||
|
"GibBrothers" "GB" "" ["Cipher" "Nightops" "Major" "Razor"]
|
||||||
|
|
||||||
|
"GirlPower" "GP" "" ["Athena" "Jezebel" "Stiletto" "Voodoo"]
|
||||||
|
|
||||||
|
|
||||||
|
"MeOnly" "MO" "male/kw_white" []
|
||||||
|
|
||||||
|
# As the View Weapons patch grows to include more models, add them in here..
|
||||||
|
[view weapons]
|
||||||
|
"male"
|
||||||
|
"female"
|
||||||
|
"cyborg"
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,26 @@
|
||||||
|
#define MAX_VISIBLE_RANGE 1000
|
||||||
|
#define CAMERA_SWITCH_TIME 20
|
||||||
|
#define CAMERA_DEAD_SWITCH_TIME 2.5
|
||||||
|
#define TRUE 1
|
||||||
|
#define FALSE 0
|
||||||
|
|
||||||
|
void CameraThink(edict_t *ent, usercmd_t *ucmd);
|
||||||
|
void PlayerDied(edict_t *ent);
|
||||||
|
|
||||||
|
typedef struct sPlayerList_s
|
||||||
|
{
|
||||||
|
edict_t *pEntity;
|
||||||
|
struct sPlayerList_s *pNext;
|
||||||
|
} sPlayerList;
|
||||||
|
|
||||||
|
int CameraCmd();
|
||||||
|
void EntityListRemove(edict_t *pEntity);
|
||||||
|
void EntityListAdd(edict_t *pEntity);
|
||||||
|
unsigned long EntityListNumber();
|
||||||
|
void PrintEntityList();
|
||||||
|
void EnitityListClean();
|
||||||
|
sPlayerList *EntityListHead();
|
||||||
|
sPlayerList *EntityListNext(sPlayerList *pCurrent);
|
||||||
|
sPlayerList *pTempFind;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,445 @@
|
||||||
|
# Eraser Bot chat data
|
||||||
|
#
|
||||||
|
# Thanks to Meanstryk for the additions
|
||||||
|
# -> http://www.planetquake.com/ramshackle
|
||||||
|
# Also thanks to Shawn "Benighted1" Lisk for more additions
|
||||||
|
# -> kclisk@bright.net
|
||||||
|
#
|
||||||
|
# OK, here's the deal. Bot's can say one of the following things:
|
||||||
|
#
|
||||||
|
# 1. Greetings
|
||||||
|
#
|
||||||
|
# When a bot joins a team, or you join a bot's team, they'll usually
|
||||||
|
# say hi. This depends on what they're doing at the time. If they're
|
||||||
|
# not in battle, they'll usually give you a nice, warm welcome.
|
||||||
|
#
|
||||||
|
# 2. Insults
|
||||||
|
#
|
||||||
|
# Ahh, the insult. What would Quake be without the old fashioned
|
||||||
|
# insult. I bot will insult you randomly, depending on the status of
|
||||||
|
# the game, so this section is divided into a few sub-sections:
|
||||||
|
#
|
||||||
|
# a. The General Insults
|
||||||
|
#
|
||||||
|
# Just your average, all-purpose insult.
|
||||||
|
#
|
||||||
|
# b. Kicking your Ass
|
||||||
|
#
|
||||||
|
# The bot will use these insults when he's beating you,
|
||||||
|
# so these
|
||||||
|
# won't make much sense if he's losing.
|
||||||
|
#
|
||||||
|
# c. Losing
|
||||||
|
#
|
||||||
|
# The bot is losing, but has just killed you. A come-
|
||||||
|
# back is imminent.
|
||||||
|
#
|
||||||
|
# 3. Come-backs
|
||||||
|
#
|
||||||
|
# I personally don't like being told to wipe the floor after I messed up.
|
||||||
|
# Bots don't take to insults too well either, especially the good ones
|
||||||
|
# who have an ego to defend.
|
||||||
|
#
|
||||||
|
# 4. TEAMPLAY: Help!
|
||||||
|
#
|
||||||
|
# Some variations on the standard Help me, I'm in trouble! Note that if
|
||||||
|
# the bot is near an item such as the Rocket-Launcher, or Quad damage,
|
||||||
|
# they'll add a "Near the Rocket-Launcher!" to the end of the chat line.
|
||||||
|
#
|
||||||
|
# 5. TEAMPLAY: Drop item
|
||||||
|
#
|
||||||
|
# Bot's will let others (on their team) know if they have excess items.
|
||||||
|
# This provides some variety on they way they let you know.
|
||||||
|
#
|
||||||
|
# 6. TEAMPLAY: Let's Group!
|
||||||
|
#
|
||||||
|
# A bot may decide he's going to coordinate an attack. If so, he will issue
|
||||||
|
# one of these statements to the rest of his team, following by a location
|
||||||
|
# specifier, like "I'm near the Rocket-Launcher". He will generally wait a
|
||||||
|
# few second in that area, and then proceed. Other bot's will try to get to
|
||||||
|
# the leader Bot in time, and will then follow him around the level.
|
||||||
|
#
|
||||||
|
# Bot's will generally try to garner up a nice supply of weapons and ammo
|
||||||
|
# before doing this.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# NOTE: where you see a %s, that means a string goes there. All strings
|
||||||
|
# must appear in the same order for each section.
|
||||||
|
#
|
||||||
|
|
||||||
|
-- Greetings
|
||||||
|
Yo!
|
||||||
|
Wassup!
|
||||||
|
Hey.
|
||||||
|
Hi.
|
||||||
|
Greetings
|
||||||
|
Eh.
|
||||||
|
I'm back!
|
||||||
|
Oi!
|
||||||
|
Welcome aboard.
|
||||||
|
Everyone aboard? Let's rock.
|
||||||
|
'sup bro?
|
||||||
|
Howdy.
|
||||||
|
Konnichiwa!
|
||||||
|
You better not suck.
|
||||||
|
Wait... what color am I?
|
||||||
|
Yull!
|
||||||
|
What da deallie yo?
|
||||||
|
Be fruitiful and frag a lot.
|
||||||
|
Let's get it on!
|
||||||
|
Who's going to lose to me today?
|
||||||
|
Anarchists of the world unite!
|
||||||
|
To the Bat Cave!
|
||||||
|
Let the show begin.
|
||||||
|
Anyone up for slapping the criminal element around?
|
||||||
|
Make 'em say 'ugggggh'!
|
||||||
|
Last one to kill a bad guy buys the beer.
|
||||||
|
Lets rock and roll!
|
||||||
|
I love the smell of BFG blasts in the morning!
|
||||||
|
G'day mate.
|
||||||
|
Let's see what you can do.
|
||||||
|
Present!
|
||||||
|
Here!
|
||||||
|
Nice night for a furball!
|
||||||
|
Eh mon!
|
||||||
|
Crap, where's a depot!?
|
||||||
|
I'm feelin' saucy today.
|
||||||
|
Kick it!
|
||||||
|
|
||||||
|
-- Insults (general)
|
||||||
|
Geez, are you ok?
|
||||||
|
Ouch, that HAD to hurt!
|
||||||
|
Who da man!
|
||||||
|
Yeh punk!
|
||||||
|
You all banged up!
|
||||||
|
Can anyone here play this game?
|
||||||
|
Waiter! Come clean up this mess!
|
||||||
|
Take my advice, or I'll spank you without pants
|
||||||
|
Need a band-aid %s?
|
||||||
|
Boo-Ya!!!
|
||||||
|
Hey everyone, %s is a camping whore.
|
||||||
|
Everyone line up single file... I'll get the railgun.
|
||||||
|
Sorry %s. This is a NO BREATHING zone.
|
||||||
|
Hey %s, drop to the console and type KILL.
|
||||||
|
Damn %s, it should be illegal to suck so bad.
|
||||||
|
Excuse me while I urinate on your corpse, %s.
|
||||||
|
Hey %s... found your spleen.
|
||||||
|
Stick to Duke Nukem %s.
|
||||||
|
Eww, I stepped on %s's severed ear!
|
||||||
|
You want some more %s?
|
||||||
|
That was just a sample %s.
|
||||||
|
Nice try %s... A for effort.
|
||||||
|
Thanks for the frag %s.
|
||||||
|
Just give up %s.
|
||||||
|
How pathetic.
|
||||||
|
Wow, what a shot. I rule.
|
||||||
|
Sorry %s, didn't see you there. :)
|
||||||
|
That's right... run... wuss....
|
||||||
|
Why'd ya try to run %s?
|
||||||
|
Just like a deer in headlights.
|
||||||
|
Bahahaha!
|
||||||
|
Man %s... I pity you. I really do.
|
||||||
|
Someone ELSE kill %s for a change.
|
||||||
|
Yeah %s... you're my favorite.
|
||||||
|
What do you call that shit %s?
|
||||||
|
Learn from that.
|
||||||
|
Your corpse made a fine screenshot %s.
|
||||||
|
I have now been added to %s's shitlist.
|
||||||
|
You weren't doing anything anyway %s.
|
||||||
|
Oh get up... you aint hurt! :)
|
||||||
|
Lovely chalk outline %s.
|
||||||
|
Next time %s... move.
|
||||||
|
Stick to observer mode %s.
|
||||||
|
Love those death animations %s... you too apparently.
|
||||||
|
Damn %s, you smell just like roast chicken.
|
||||||
|
I love the sound of crunching bone. :)
|
||||||
|
Nice pain WAVs %s.
|
||||||
|
Burn baby burn.
|
||||||
|
Dude, %s... your skin blows.
|
||||||
|
Hey everyone, %s wasn't wearing clean underwear!
|
||||||
|
Instant karma, %s.
|
||||||
|
24/7, %s
|
||||||
|
Does your face hurt, %s? Well, its killin' me!
|
||||||
|
Ooh, thats gotta hurt!
|
||||||
|
B-b-b-b-Bad to the bone!
|
||||||
|
Oh my god, they killed %s! You basterds!
|
||||||
|
Bull's Eye!
|
||||||
|
I bet that hurt!
|
||||||
|
Come meet my 2x4.
|
||||||
|
%s was fragged again everyone!
|
||||||
|
Step right up to be knocked right down!
|
||||||
|
Clean up in isle seven...%s's remains in isle seven.
|
||||||
|
%s's dead, Jim!
|
||||||
|
Keep the change %s.
|
||||||
|
Oh, I'm gooood.
|
||||||
|
%s...there's no reason to act like that!
|
||||||
|
Anyone got a spachula?
|
||||||
|
Danger, %s Robinson, danger!
|
||||||
|
Excuse me, is the circus in town?
|
||||||
|
Terminated.
|
||||||
|
%s's loss is my gain!
|
||||||
|
BOOM, baby!
|
||||||
|
You move like peanut butter, %s!
|
||||||
|
|
||||||
|
-- Insults (kicking ass)
|
||||||
|
You gonna start playing or what?
|
||||||
|
Keeping practising, %s
|
||||||
|
Your in my house now %s!
|
||||||
|
Ain't nobody stoppin me!
|
||||||
|
Gun wounds again?
|
||||||
|
Try strafing sometime %s.
|
||||||
|
Can I be killed? I'm beginning to wonder.
|
||||||
|
Next time, make me work for it %s.
|
||||||
|
Am I a giant or did everyone else shrink?
|
||||||
|
Why do you people even bother?
|
||||||
|
Next time %s, don't bother dodging. Why prolong your agony?
|
||||||
|
What happened %s? >:)
|
||||||
|
Mental note to self: Find server with better opponents.
|
||||||
|
Christ people... try playing with a mouse.
|
||||||
|
Nobody can touch me in this level.
|
||||||
|
It *hurts* to be this kick-ass! It *hurts*!
|
||||||
|
I'm like the freaking energizer bunny over here.
|
||||||
|
Why do they ALWAYS try to run??
|
||||||
|
Just like cuttin' down weeds.
|
||||||
|
BANG said the gun, SPLATTER said %s.
|
||||||
|
Get the hell out of my way!
|
||||||
|
Play dead %s... good boy.
|
||||||
|
Step right up to get knocked right down!
|
||||||
|
That's right, one free asskicking with every purchase.
|
||||||
|
Did I get your attention %s?
|
||||||
|
Am I evil? Why, yes I am.
|
||||||
|
Some people just don't die well.
|
||||||
|
Hey look, I can chat while running full tilt!
|
||||||
|
Hey %s, don't hate me just because you suck.
|
||||||
|
BOW DOWN!
|
||||||
|
Interception by %s!
|
||||||
|
He shoots... he SCORES!
|
||||||
|
You should see a doctor about that %s.
|
||||||
|
Thank you, come again.
|
||||||
|
Down boy...
|
||||||
|
I'm such a bastard. :)
|
||||||
|
Hope you have death and dismemberment insurance %s.
|
||||||
|
Boy, I'm gonna send this demo to Meanstryk. :)
|
||||||
|
Sorry sir, but thank you for playing.
|
||||||
|
Come on in %s... the slaughter's fine.
|
||||||
|
Ha! %s smeared like a red crayon!
|
||||||
|
Hi %s... I'm the enemy.
|
||||||
|
Making people feel bad feels SO good.
|
||||||
|
Sorry %s. Time to respawn.
|
||||||
|
Another one bites the dust.
|
||||||
|
Someone had better dig a mass grave.
|
||||||
|
Can't pay the bills, but damnit, this I can do.
|
||||||
|
What the fuck you think YOU'RE doin' %s?
|
||||||
|
Come share with me my hurtful thoughts.
|
||||||
|
Ha! Somedays it really pays to be a bot.
|
||||||
|
You should stick to Chess %s.
|
||||||
|
Its frag time. Do you know where you're skill is %s?
|
||||||
|
Walk into the light, %s!
|
||||||
|
Oh, I just remembered; %s's boring, and my guns work.
|
||||||
|
He chose...poorly.
|
||||||
|
I'm immortal!...So far.
|
||||||
|
Nothing can stop me now.
|
||||||
|
I must be butter cause I'm on a roll!
|
||||||
|
DEATH FROM ABOVE!!!
|
||||||
|
I'm not even working up a sweat, %s!
|
||||||
|
I'd trash talk, but I'm too tired...*yawn*
|
||||||
|
%s...I'm your father!
|
||||||
|
One small frag for me, one big head wound for you!
|
||||||
|
All too easy.
|
||||||
|
Come strong or don't come at all!
|
||||||
|
Damnit %s! You messed my hair up!
|
||||||
|
|
||||||
|
-- Insults (losing, coming back)
|
||||||
|
It's pay-back time!
|
||||||
|
Elvis has left the building!
|
||||||
|
Time to change your diaper %s
|
||||||
|
Who gave %s the nerve to get killed here?
|
||||||
|
Oops... did I do that? Sorry, man.
|
||||||
|
Nice spine there %s.
|
||||||
|
That looks like it's going to stain.
|
||||||
|
What... a... mess.
|
||||||
|
In your face bitch... in your face.
|
||||||
|
Look out man, I'm back in my zone. Watch your ass!
|
||||||
|
NOW who has the big gun mother-fucker?!?
|
||||||
|
Oh, no wonder... I had the safety on...
|
||||||
|
Better run %s... my lag is letting up.
|
||||||
|
Hey %s... ya shoulda zigged when ya zagged.
|
||||||
|
Hey nice catch %s!
|
||||||
|
Look out %s, I'm just getting warmed up.
|
||||||
|
Looks like there's a new lawman in town, eh %s?
|
||||||
|
Getting tired there %s?
|
||||||
|
Okay %s, I have your punk-ass figured.
|
||||||
|
Killing you appears to be habit forming %s.
|
||||||
|
How the fuck do you like it %s?
|
||||||
|
Oooh! That felt so good!
|
||||||
|
Not such a badass now, eh %s?
|
||||||
|
No more Mr. Nice Guy.
|
||||||
|
Oh man, you've had it now.
|
||||||
|
Now THAT is what I'm talkin' about.
|
||||||
|
Feel that? That's my foot in your ass.
|
||||||
|
Get used to it %s.
|
||||||
|
Anyone else hear the theme from Rocky?
|
||||||
|
Your time's up %s.
|
||||||
|
Okay, now I start playing for real.
|
||||||
|
Hurt much?
|
||||||
|
Remember me %s?
|
||||||
|
My how the mighty have fallen.
|
||||||
|
Here's where you get off %s.
|
||||||
|
Merry Christmas!
|
||||||
|
This aint no petting zoo, %s.
|
||||||
|
Ever hear of the golden rule %s?
|
||||||
|
Your bullying days are over %s.
|
||||||
|
Now the shoe's on the other foot.
|
||||||
|
Wanna try for two out of three %s?
|
||||||
|
Hehehe!
|
||||||
|
Hah!
|
||||||
|
That's what I THOUGHT... loser.
|
||||||
|
Memento Mori.
|
||||||
|
The man who dies with the most toys still dies!
|
||||||
|
I'd give you the finger, but you shot them off.
|
||||||
|
Your meaty skull is beautiful to me, %s.
|
||||||
|
You all saw him...he had a gun!
|
||||||
|
I'm not gonna kill ya. Like hell I'm not!
|
||||||
|
Use the Farse, puke!
|
||||||
|
Ah...the joy of deathmatch.
|
||||||
|
Where's your crown, King Nothing?
|
||||||
|
Chill %s.
|
||||||
|
I'm spittin' on your corpse, %s!
|
||||||
|
SCOOOOOOOOOOOOOOOOORE!
|
||||||
|
Exit light, enter night %s.
|
||||||
|
Respect my authoritay!
|
||||||
|
|
||||||
|
-- Come-backs
|
||||||
|
Screw you %s
|
||||||
|
I'll be back mutha-fucker!
|
||||||
|
I ain't even started yet.
|
||||||
|
Make the most of it %s.
|
||||||
|
Yeh? Well fuck you asshole!
|
||||||
|
Ahh, piss off!
|
||||||
|
Yadda yadda...
|
||||||
|
Wipe the shuga from yer chin, %s
|
||||||
|
Don't make me come over there %s.
|
||||||
|
Okay %s, you and me, after school.
|
||||||
|
Why do you have to be such an asshole?
|
||||||
|
Skin that smokewagon! Bring it on bitch!
|
||||||
|
One of these days I'm gonna catch your ass typing.
|
||||||
|
From now on %s, you're my bitch. Bend over.
|
||||||
|
My boot. %'s ass. Perfect fit.
|
||||||
|
Fuck off.
|
||||||
|
Bite me.
|
||||||
|
You aint shit!
|
||||||
|
Talk your shit over here %s.
|
||||||
|
I got somethin' for your ass %s.
|
||||||
|
I'll be your huckleberry.
|
||||||
|
You gonna do something other than talk?
|
||||||
|
You talkin' to me?
|
||||||
|
You don't want none of this %s.
|
||||||
|
This isn't IRC. Less talk and more play.
|
||||||
|
You're so full of shit %s.
|
||||||
|
Okay %s, let's dance.
|
||||||
|
Come learn to die.
|
||||||
|
Shut up %s.
|
||||||
|
Shut the FUCK up.
|
||||||
|
Damned phonejack.
|
||||||
|
Lllaaaagggg!
|
||||||
|
I had TIME to kill, now I have SOMEONE to kill.
|
||||||
|
Don't let your mouth overload your ass %s.
|
||||||
|
You best hush %s.
|
||||||
|
I aint playin' with you %s.
|
||||||
|
Come on %s. Let's go! You afraid?
|
||||||
|
You're such a lamer %s.
|
||||||
|
%s --you whore.
|
||||||
|
What? You think you can play now?
|
||||||
|
You don't want none of this %s.
|
||||||
|
Idiot.
|
||||||
|
Someone needs to get laid.
|
||||||
|
I'll have my way with your mom after I kill her %s!
|
||||||
|
%s seems to care; about what, I have no idea.
|
||||||
|
The answer is cold in my hand, %s.
|
||||||
|
I ain't gonna waste my hate on you, %s.
|
||||||
|
I'll see you in hell %s.
|
||||||
|
I do not want this...
|
||||||
|
Ahh, shiiit.
|
||||||
|
Its all a conspiricy!
|
||||||
|
My God, are you still talking!?
|
||||||
|
Its not my fault- my gamma correction is screwy!
|
||||||
|
Next time %s...next time.
|
||||||
|
Bull! It was freindly fire!
|
||||||
|
Well aren't you full of piss and vinegar.
|
||||||
|
By this time, my lungs were aching for air.
|
||||||
|
You'd better bite down on a BFG and pray for lock jaw, %s.
|
||||||
|
My keyboard's missing a few keys.
|
||||||
|
Does the phrase, 'Nut Case' mean anything to you, %s?
|
||||||
|
You need to grow up, %s!
|
||||||
|
I'm going to use you for a raft next time we meet, %s!
|
||||||
|
I think a retreat is in order...
|
||||||
|
|
||||||
|
-- TEAMPLAY: Help!
|
||||||
|
Ahh!! Under heavy fire near the %s!! Heeelp!!!
|
||||||
|
I need backup at the %s!!
|
||||||
|
Backup requested, near the %s!
|
||||||
|
I'm seeing my life flash before my eyes by the %s!
|
||||||
|
Charlie is all over the %s.
|
||||||
|
All are invited to the %s. Enemy already in attendance.
|
||||||
|
Bring mop and bucket to %s. I'm about to splatter messily.
|
||||||
|
A little help here? I'm at the %s.
|
||||||
|
Visit the %s. See enemy is natural habitat.
|
||||||
|
Outnumbered. Dying. By %s.
|
||||||
|
The %s area is in a world of hurt, and so am I.
|
||||||
|
The enemy has control of the %s area. Recover.
|
||||||
|
Experiencing substanial pain near the %s.
|
||||||
|
Hating life by the %s.
|
||||||
|
I'm about to become a stain near the %s.
|
||||||
|
Enemy seems to want %s very badly.
|
||||||
|
Heavy resistance near the %s.
|
||||||
|
Come help me out over at the %s before I soil myself.
|
||||||
|
Aaarrggh! %s Death... the horror... the horror
|
||||||
|
zzzt- help! zzt...over zzzt-fizz %s!
|
||||||
|
The Man is opressin' me by the %s!
|
||||||
|
Get busy child! I'm by the %s.
|
||||||
|
Damnit, they're all over me by the %s!
|
||||||
|
|
||||||
|
-- TEAMPLAY: Drop Item (not implemented yet)
|
||||||
|
Hey guys, need a %s? I'm near the %s!
|
||||||
|
I have a %s to spare, near the %s!
|
||||||
|
I've just dropped a %s, near the %s!
|
||||||
|
Free %s! It's by the %s.
|
||||||
|
Anyone want a %s? Come to the %s.
|
||||||
|
There's a %s available by the %s.
|
||||||
|
Come get the %s over by the %s.
|
||||||
|
Be advised: %s available near %s.
|
||||||
|
How'd I get so many %ss? Anyone want one? I'm by the %s.
|
||||||
|
There's a %s by the %s. Come get it before enemy does.
|
||||||
|
Someone come get this %s by the %s.
|
||||||
|
I don't need this %s; anyone want it? By %s.
|
||||||
|
Surplus %s avaiable near %s.
|
||||||
|
There's a %s in the open over here near the %s.
|
||||||
|
Anyone want a %s? I already have one. It's near the %s.
|
||||||
|
I'm dropping a %s near the %s.
|
||||||
|
Shiny new %s, hardly used, available near %s.
|
||||||
|
Grab this %s someone, near the %s.
|
||||||
|
Prevent enemy from getting the %s near the %s.
|
||||||
|
Dropped %s near %s.
|
||||||
|
|
||||||
|
-- TEAMPLAY: Let's Group!
|
||||||
|
Calling all units! Group at the %s!
|
||||||
|
Attack formation at the %s!
|
||||||
|
Calling all squadron members to the %s!
|
||||||
|
Hey guys, I'm over at the %s!
|
||||||
|
Come protect the %s!
|
||||||
|
Everyone get your asses over to the %s!
|
||||||
|
Meeting at %s. Attendance mandatory.
|
||||||
|
Forming attack group at %s.
|
||||||
|
Regroup at the %s.
|
||||||
|
Preparing attack run near %s.
|
||||||
|
Fall back to %s.
|
||||||
|
Proceed to %s and await further instruction.
|
||||||
|
Gather near %s.
|
||||||
|
Everyone join me at the %s.
|
||||||
|
Meet me at the %s. Forming attack party.
|
||||||
|
Let's get our shit together people. Group at %s.
|
||||||
|
Launching attack run from the %s.
|
||||||
|
Everyone avilable please report to the %s.
|
||||||
|
Everyone...attack from the %s!
|
||||||
|
Our next organized attack will be from the %s. Hurry!
|
||||||
|
Group by the %s.
|
|
@ -0,0 +1,90 @@
|
||||||
|
//Debug routine - create fake functions for bot code
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include "g_local.h"
|
||||||
|
#include "bot_procs.h"
|
||||||
|
|
||||||
|
|
||||||
|
void OptimizeRouteCache()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
i = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CalcRoutes(int node_index)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
i = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ClosestNodeToEnt(edict_t *self, int check_fullbox, int check_all_nodes)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float PathToEnt(edict_t *self, edict_t *target, int check_fullbox, int check_all_nodes)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debug_ShowPathToGoal(edict_t *self, edict_t *goalent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
edict_t *matching_trail(vec3_t spot)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddTrailToPortals(edict_t *trail)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetGridPortal(float pos)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NodeDebug(char *fmt, ...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void CheckMoveForNodes(edict_t *ent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
void CalcItemPaths(edict_t *ent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerTrail_Init (void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerTrail_Add (edict_t *self, vec3_t spot, edict_t *goalent, int nocheck, int calc_routes, int node_type)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlayerTrail_New (vec3_t spot)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteTrail ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
edict_t *PlayerTrail_PickFirst (edict_t *self)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
edict_t *PlayerTrail_PickNext (edict_t *self)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
edict_t *PlayerTrail_LastSpot (void)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,283 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
|
||||||
|
/*=================make_debris=================*/
|
||||||
|
void make_debris(edict_t *ent)
|
||||||
|
{
|
||||||
|
// int i;
|
||||||
|
vec3_t org;
|
||||||
|
vec3_t origin;
|
||||||
|
float spd;
|
||||||
|
// calculate position for the explosion entity
|
||||||
|
VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
|
||||||
|
// make a few big chunks
|
||||||
|
spd = .5 * (float)ent->dmg / 200.0;
|
||||||
|
org[0] = ent->s.origin[0] + crandom() * ent->size[0];
|
||||||
|
org[1] = ent->s.origin[1] + crandom() * ent->size[1];
|
||||||
|
org[2] = ent->s.origin[2] + crandom() * ent->size[2];
|
||||||
|
ThrowShrapnel (ent, "models/objects/debris1/tris.md2", spd, org);
|
||||||
|
spd = 1.5 * (float)ent->dmg / 200.0;
|
||||||
|
VectorCopy (ent->absmin, org);
|
||||||
|
ThrowShrapnel (ent, "models/objects/debris2/tris.md2", spd, org);
|
||||||
|
//spd = 1.5 * (float)ent->dmg / 200.0; VectorCopy (ent->absmin, org);
|
||||||
|
//ThrowShrapnel (ent, "models/objects/debris3/tris.md2", spd, org);
|
||||||
|
}/*
|
||||||
|
============T_ShockWaveKnocks view around a bit. Based on T_RadiusDamage.
|
||||||
|
============*/
|
||||||
|
void T_ShockWave (edict_t *inflictor, float damage, float radius)
|
||||||
|
{
|
||||||
|
float points;
|
||||||
|
edict_t *ent = NULL;
|
||||||
|
vec3_t v;
|
||||||
|
vec3_t dir;
|
||||||
|
float SHOCK_TIME = 0.1;
|
||||||
|
while ((ent = findradius(ent, inflictor->s.origin, radius)) == NULL)
|
||||||
|
{
|
||||||
|
if (!ent->takedamage)
|
||||||
|
continue;
|
||||||
|
if (!ent->client)
|
||||||
|
continue;
|
||||||
|
VectorAdd (ent->mins, ent->maxs, v);
|
||||||
|
VectorMA (ent->s.origin, 0.5, v, v);
|
||||||
|
VectorSubtract (inflictor->s.origin, v, v);
|
||||||
|
points = .8*(damage - 0.5 * VectorLength (v));
|
||||||
|
if (points < .5)
|
||||||
|
points = .5;
|
||||||
|
if (points > 10)
|
||||||
|
points = 10;
|
||||||
|
if (points > 0)
|
||||||
|
{
|
||||||
|
VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
|
||||||
|
ent->client->v_dmg_pitch = -points;
|
||||||
|
ent->client->v_dmg_roll = 0;
|
||||||
|
ent->client->v_dmg_time = level.time + SHOCK_TIME;
|
||||||
|
ent->client->kick_origin[2] = -points*4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*============T_ShockItems
|
||||||
|
Lets explosions move items. Based on T_RadiusDamage.
|
||||||
|
TODO: Reorient items after coming to rest?============*/
|
||||||
|
void T_ShockItems (edict_t *inflictor)
|
||||||
|
{
|
||||||
|
float points;
|
||||||
|
edict_t *ent = NULL;
|
||||||
|
vec3_t v;
|
||||||
|
vec3_t dir;
|
||||||
|
vec3_t kvel;
|
||||||
|
float mass;
|
||||||
|
float radius=255;
|
||||||
|
float damage=100;
|
||||||
|
while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL) {
|
||||||
|
if (ent->item)
|
||||||
|
{
|
||||||
|
VectorAdd (ent->mins, ent->maxs, v);
|
||||||
|
VectorMA (ent->s.origin, 0.5, v, v);
|
||||||
|
VectorSubtract (inflictor->s.origin, v, v);
|
||||||
|
points = damage - 0.5 * VectorLength (v);
|
||||||
|
if (ent->mass < 50)
|
||||||
|
mass = 50;
|
||||||
|
else
|
||||||
|
mass = ent->mass;
|
||||||
|
if (points > 0)
|
||||||
|
{
|
||||||
|
VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
|
||||||
|
ent->movetype = MOVETYPE_BOUNCE;
|
||||||
|
// any problem w/leaving this changed?
|
||||||
|
VectorScale (dir, 3.0 * (float)points / mass, kvel);
|
||||||
|
VectorAdd (ent->velocity, kvel, ent->velocity);
|
||||||
|
VectorAdd (ent->avelocity, 1.5*kvel, ent->avelocity);
|
||||||
|
|
||||||
|
//TODO: check groundentity & lower s.origin to keep objects from sticking to floor?
|
||||||
|
// ERRR... should we be calling linkentity here?
|
||||||
|
ent->velocity[2]+=10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}/*=================BecomeNewExplosion=================*/
|
||||||
|
void BecomeNewExplosion (edict_t *ent)
|
||||||
|
{
|
||||||
|
// int i;
|
||||||
|
// vec3_t org;
|
||||||
|
vec3_t origin;
|
||||||
|
// float spd;
|
||||||
|
|
||||||
|
// calculate position for the explosion entity
|
||||||
|
VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
|
||||||
|
/*// make smoke trails
|
||||||
|
spd = 7.0 * ent->dmg / 200;
|
||||||
|
for (i = 0; i < 1; i++)
|
||||||
|
{
|
||||||
|
org[0] = ent->s.origin[0] + crandom() * ent->size[0];
|
||||||
|
org[1] = ent->s.origin[1] + crandom() * ent->size[1];
|
||||||
|
org[2] = ent->s.origin[2] + crandom() * ent->size[2];
|
||||||
|
ThrowShrapnel3 (ent, "models/objects/debris2/tris.md2", spd, org);
|
||||||
|
}
|
||||||
|
spd = 10.0 * ent->dmg / 200;
|
||||||
|
for (i = 0; i < 1; i++) {
|
||||||
|
org[0] = ent->s.origin[0]; // + crandom() * ent->size[0];
|
||||||
|
org[1] = ent->s.origin[1]; // + crandom() * ent->size[1];
|
||||||
|
org[2] = ent->s.origin[2]; // + crandom() * ent->size[2];
|
||||||
|
ThrowShrapnel2 (ent, "models/objects/debris2/tris.md2", spd, org);
|
||||||
|
}
|
||||||
|
spd = 15.0 * ent->dmg / 200;
|
||||||
|
for (i = 0; i < 1; i++)
|
||||||
|
{
|
||||||
|
org[0] = ent->s.origin[0] + crandom() * ent->size[0];
|
||||||
|
org[1] = ent->s.origin[1] + crandom() * ent->size[1];
|
||||||
|
org[2] = ent->s.origin[2] + crandom() * ent->size[2];
|
||||||
|
ThrowShrapnel3 (ent, "models/objects/debris2/tris.md2", spd, org);
|
||||||
|
}*/
|
||||||
|
// send flash & bang
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
gi.WriteShort (ent-g_edicts);
|
||||||
|
// any way to get a mz flash without hearing the weapon?
|
||||||
|
gi.WriteByte (MZ_CHAINGUN2);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
// any other way to make this loud enough?
|
||||||
|
BigBang (ent);
|
||||||
|
// destroy object
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
}
|
||||||
|
/*=============
|
||||||
|
isvisible
|
||||||
|
This is the ai.c visible function
|
||||||
|
=============*/
|
||||||
|
qboolean isvisible (edict_t *self, edict_t *other)
|
||||||
|
{ vec3_t spot1;
|
||||||
|
vec3_t spot2; trace_t trace; VectorCopy (self->s.origin, spot1);
|
||||||
|
spot1[2] += self->viewheight; VectorCopy (other->s.origin, spot2);
|
||||||
|
spot2[2] += other->viewheight;
|
||||||
|
trace = gi.trace (spot1, vec3_origin, vec3_origin, spot2, self, MASK_OPAQUE);
|
||||||
|
if (trace.fraction == 1.0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/*=================
|
||||||
|
|
||||||
|
BigBangLoud bang.
|
||||||
|
=================*/
|
||||||
|
void BigBang (edict_t *ent)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
edict_t *ear;
|
||||||
|
float radius=1024;
|
||||||
|
vec3_t d;
|
||||||
|
//gi.sound (ent, CHAN_ITEM, gi.soundindex ("weapons/mk33.wav"), 1, ATTN_NORM, 0);
|
||||||
|
// Unfortunately, this sounds weak, so check each client to see if
|
||||||
|
// it is within the blast radius or in line of sight; if so,
|
||||||
|
// send each client a loud ATTN_STATIC bang
|
||||||
|
ear = &g_edicts[0];
|
||||||
|
for (i=1 ;i<=maxclients->value;i++ )
|
||||||
|
{
|
||||||
|
if ((ear=&g_edicts[i]) && ear->inuse)
|
||||||
|
continue;
|
||||||
|
if (!ear->client)
|
||||||
|
continue;
|
||||||
|
VectorSubtract (ear->s.origin, ent->s.origin, d);
|
||||||
|
//if ((VectorLength(d) < radius) | (isvisible(ent, ear)))
|
||||||
|
//gi.sound (ear, CHAN_VOICE, gi.soundindex ("weapons/mk33.wav"), 1, ATTN_STATIC, 0);
|
||||||
|
}
|
||||||
|
}/*=================ThrowShrapnelThis is just ThrowDebris with EF_GRENADE set.
|
||||||
|
Note: if debris is created before calling T_Damage,
|
||||||
|
setting DAMAGE_YES will give an orange splash effect.=================*/
|
||||||
|
void ThrowShrapnel (edict_t *self, char *modelname, float speed, vec3_t origin){
|
||||||
|
edict_t *chunk; vec3_t v; chunk = G_Spawn();
|
||||||
|
VectorCopy (origin, chunk->s.origin);
|
||||||
|
gi.setmodel (chunk, modelname);
|
||||||
|
v[0] = 100 * crandom();
|
||||||
|
v[1] = 100 * crandom();
|
||||||
|
v[2] = 100 + 100 * crandom();
|
||||||
|
VectorMA (self->velocity, speed, v, chunk->velocity);
|
||||||
|
chunk->movetype = MOVETYPE_BOUNCE;
|
||||||
|
chunk->solid = SOLID_NOT;
|
||||||
|
chunk->avelocity[0] = random()*600;
|
||||||
|
chunk->avelocity[1] = random()*600;
|
||||||
|
chunk->avelocity[2] = random()*600;
|
||||||
|
chunk->think = G_FreeEdict;
|
||||||
|
chunk->nextthink = level.time + 5 + random()* 5;
|
||||||
|
chunk->s.frame = 0;
|
||||||
|
chunk->flags = 0;
|
||||||
|
chunk->classname = "debris";
|
||||||
|
chunk->takedamage = DAMAGE_NO;
|
||||||
|
//chunk->die = G_FreeEdict;
|
||||||
|
chunk->s.effects |= EF_GRENADE;
|
||||||
|
gi.linkentity (chunk);}
|
||||||
|
/*=================ThrowShrapnel2Less persistent
|
||||||
|
=================*/
|
||||||
|
void ThrowShrapnel2 (edict_t *self, char *modelname, float speed, vec3_t origin)
|
||||||
|
{ edict_t *chunk; vec3_t v; chunk = G_Spawn();
|
||||||
|
VectorCopy (origin, chunk->s.origin);
|
||||||
|
gi.setmodel (chunk, modelname);
|
||||||
|
v[0] = 100 * crandom();
|
||||||
|
v[1] = 100 * crandom();
|
||||||
|
v[2] = 100 * crandom();
|
||||||
|
VectorMA (self->velocity, speed, v, chunk->velocity);
|
||||||
|
chunk->movetype = MOVETYPE_BOUNCE;
|
||||||
|
chunk->solid = SOLID_NOT;
|
||||||
|
chunk->avelocity[0] = random()*600;
|
||||||
|
chunk->avelocity[1] = random()*600;
|
||||||
|
chunk->avelocity[2] = random()*600;
|
||||||
|
chunk->think = G_FreeEdict;
|
||||||
|
chunk->nextthink = level.time + .5 + random()*.5;
|
||||||
|
chunk->s.frame = 0;
|
||||||
|
chunk->flags = 0;
|
||||||
|
chunk->classname = "debris";
|
||||||
|
chunk->takedamage = DAMAGE_NO;
|
||||||
|
// chunk->die = G_FreeEdict;
|
||||||
|
chunk->s.effects |= EF_GRENADE;
|
||||||
|
gi.linkentity (chunk);
|
||||||
|
}
|
||||||
|
/*=================
|
||||||
|
ThrowShrapnel3
|
||||||
|
Least persistent
|
||||||
|
=================*/
|
||||||
|
void ThrowShrapnel3 (edict_t *self, char *modelname, float speed, vec3_t origin)
|
||||||
|
{ edict_t *chunk; vec3_t v; chunk = G_Spawn();
|
||||||
|
VectorCopy (origin, chunk->s.origin);
|
||||||
|
gi.setmodel (chunk, modelname);
|
||||||
|
v[0] = 100 * crandom();
|
||||||
|
v[1] = 100 * crandom();
|
||||||
|
v[2] = 100 * crandom();
|
||||||
|
VectorMA (self->velocity, speed, v, chunk->velocity);
|
||||||
|
chunk->movetype = MOVETYPE_BOUNCE;
|
||||||
|
chunk->solid = SOLID_NOT;
|
||||||
|
chunk->avelocity[0] = random()*600;
|
||||||
|
chunk->avelocity[1] = random()*600;
|
||||||
|
chunk->avelocity[2] = random()*600;
|
||||||
|
chunk->think = G_FreeEdict;
|
||||||
|
chunk->nextthink = level.time + random()*.3;
|
||||||
|
chunk->s.frame = 0;
|
||||||
|
chunk->flags = 0;
|
||||||
|
chunk->classname = "debris";
|
||||||
|
chunk->takedamage = DAMAGE_NO;
|
||||||
|
// chunk->die = G_FreeEdict;
|
||||||
|
chunk->s.effects |= EF_GRENADE;
|
||||||
|
gi.linkentity (chunk);
|
||||||
|
}
|
||||||
|
/*=================
|
||||||
|
ThrowShrapnel4Medium persistence with glowing trail effect
|
||||||
|
=================*/
|
||||||
|
void ThrowShrapnel4 (edict_t *self, char *modelname, float speed, vec3_t origin)
|
||||||
|
{ edict_t *chunk;
|
||||||
|
vec3_t v;
|
||||||
|
chunk = G_Spawn();
|
||||||
|
VectorCopy (origin, chunk->s.origin);
|
||||||
|
gi.setmodel (chunk, modelname);
|
||||||
|
v[0] = 100 * crandom();
|
||||||
|
v[1] = 100 * crandom();
|
||||||
|
v[2] = 100 * crandom();
|
||||||
|
VectorMA (self->velocity, speed, v, chunk->velocity);
|
||||||
|
chunk->movetype = MOVETYPE_BOUNCE;
|
||||||
|
chunk->solid = SOLID_NOT;
|
||||||
|
chunk->avelocity[0] = random()*600;
|
||||||
|
chunk->avelocity[1] = random()*600;
|
||||||
|
chunk->avelocity[2] = random()*600;
|
||||||
|
chunk->think = G_FreeEdict;
|
||||||
|
chunk->nextthink = level.time + random()*2;
|
||||||
|
chunk->s.frame = 0;
|
||||||
|
chunk->flags = 0; chunk->classname = "debris";
|
||||||
|
chunk->takedamage = DAMAGE_NO;
|
||||||
|
// chunk->die = G_FreeEdict;
|
||||||
|
chunk->s.effects |= EF_GRENADE | EF_ROCKET;
|
||||||
|
gi.linkentity (chunk);
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
// dwm.h
|
||||||
|
/*-------------------------------------------------
|
||||||
|
include this file as the last line of local.h
|
||||||
|
and change the original function names so that
|
||||||
|
the replacement ones are used instead
|
||||||
|
--------------------------------------------------*/// g_weapon.c replacements
|
||||||
|
//void Grenade_Explode (edict_t *ent);
|
||||||
|
//void rocket_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf);
|
||||||
|
// g_misc.c replacementsvoid barrel_explode (edict_t *self);
|
||||||
|
// g_target.c replacementsvoid target_explosion_explode (edict_t *self);
|
||||||
|
// new functions
|
||||||
|
void T_ShockWave (edict_t *inflictor, float damage, float radius);
|
||||||
|
void T_ShockItems (edict_t *inflictor);
|
||||||
|
void ThrowShrapnel (edict_t *self, char *modelname, float speed, vec3_t origin);
|
||||||
|
void ThrowShrapnel2 (edict_t *self, char *modelname, float speed, vec3_t origin);
|
||||||
|
void ThrowShrapnel3 (edict_t *self, char *modelname, float speed, vec3_t origin);
|
||||||
|
void ThrowShrapnel4 (edict_t *self, char *modelname, float speed, vec3_t origin);
|
||||||
|
void make_debris (edict_t *ent);void BigBang (edict_t *ent);
|
||||||
|
qboolean isvisible (edict_t *self, edict_t *other);
|
||||||
|
void BecomeNewExplosion (edict_t *ent);
|
|
@ -0,0 +1,733 @@
|
||||||
|
================================================================
|
||||||
|
The Weapons Factory Eraser Bot v1.01 (Final)
|
||||||
|
by Acrid-
|
||||||
|
................................................................
|
||||||
|
|
||||||
|
================================================================
|
||||||
|
Title : WFEraser Bot
|
||||||
|
Author : Acrid-
|
||||||
|
Homepage : http://www.captured.com/weaponsfactory
|
||||||
|
|
||||||
|
Description : Human-like AI for Simulated Quake2
|
||||||
|
Deathmatch play and Capture the
|
||||||
|
Flag
|
||||||
|
|
||||||
|
Additional Credits to : Ryan Feltrin (aka Ridah)
|
||||||
|
http://impact.frag.com
|
||||||
|
id Software for being id Software
|
||||||
|
Josh Holdaway, creator of Quick Start,
|
||||||
|
and the Final Eraser installer
|
||||||
|
The Info-Zip team for the zip/unzip tools
|
||||||
|
(http://www.cdrom.com/pub/infozip/)
|
||||||
|
Jeremy Mappus (aka DarkTheties) for
|
||||||
|
the MapMod source
|
||||||
|
Grimlock for the colored skins
|
||||||
|
Rowan "Sumaleth" Crawford, for the consol
|
||||||
|
background art
|
||||||
|
Brett "B-MonEy" McMahon, for support
|
||||||
|
and ideas
|
||||||
|
Nigel "rkm" Bovey for the linux port
|
||||||
|
IMP for the Eraser logo
|
||||||
|
Pete Elespuru for the consol mod
|
||||||
|
The SABIN Team for some Client Emulation code
|
||||||
|
(http://www.planetquake.com/botshop/sabin)
|
||||||
|
Steve Yeager (author of the ACE bot) for tips
|
||||||
|
on creating a static library for the nav
|
||||||
|
code (stevey@jps.net)
|
||||||
|
Paul Jordan for the Camera mode
|
||||||
|
(http://www.prismnet.com/~jordan/q2cam)
|
||||||
|
NIQ changes by Mike Fox (a.k.a. Artful Dodger)
|
||||||
|
SubHuman (http://www.planetquake.com/outpost/)
|
||||||
|
for extensive testing and feedback
|
||||||
|
Fred (fred@planetquake.com) for many of the
|
||||||
|
routes
|
||||||
|
L-Fire for the built-in View Weapon tips
|
||||||
|
(LFire@yyz.com)
|
||||||
|
|
||||||
|
Anyone else who has contributed in
|
||||||
|
any way to the development of Eraser.
|
||||||
|
|
||||||
|
Build Time : many hours that should have been
|
||||||
|
spent sleeping
|
||||||
|
================================================================
|
||||||
|
|
||||||
|
DESCRIPTION
|
||||||
|
|
||||||
|
The Eraser Bot is a simulated multiplayer opponent, for
|
||||||
|
use with id Software's Quake2. It has been developed with
|
||||||
|
speed and accuracy in mind, so that you can play with more
|
||||||
|
bots, with higher intelligence.
|
||||||
|
|
||||||
|
|
||||||
|
INSTALLATION
|
||||||
|
|
||||||
|
WIN95/NT INSTALLER
|
||||||
|
|
||||||
|
Double click on the EXE file, then specify your Quake2
|
||||||
|
folder and press Unzip.
|
||||||
|
|
||||||
|
-OR-
|
||||||
|
|
||||||
|
ZIPPED VERSION (Win95/NT & Linux users)
|
||||||
|
|
||||||
|
Just unzip the files contained in the archive, to your
|
||||||
|
Quake2 folder, RESTORING PATHNAMES. This means
|
||||||
|
that if you're using Winzip, you must enable the
|
||||||
|
"Use Folder Names" option when extracting. For
|
||||||
|
pkunzip users (bless their souls), make sure
|
||||||
|
you use the -d option.
|
||||||
|
|
||||||
|
|
||||||
|
RUNNING THE GAME
|
||||||
|
|
||||||
|
To run the game, type the following from the DOS Shell
|
||||||
|
command line, whilst inside your Quake2 folder:
|
||||||
|
|
||||||
|
quake2 +set game eraser +map <mapname>
|
||||||
|
(please read below to find out which maps are supported)
|
||||||
|
|
||||||
|
The from within the game, type "bot_num X" to spawn X
|
||||||
|
number of bots.
|
||||||
|
|
||||||
|
If you do not see any bots in the game, then I
|
||||||
|
advise you to read the Frequently Asked Questions
|
||||||
|
near the end of this file.
|
||||||
|
|
||||||
|
|
||||||
|
TROUBLESHOOTING
|
||||||
|
|
||||||
|
If you've followed all the instruction to the best of you're ability
|
||||||
|
and yet fate still prevents you from getting Eraser to work, then
|
||||||
|
point your browse to http://www.telefragged.com/epidemic/guides/ebgide.html
|
||||||
|
This document contains more than could be said in an email, and is
|
||||||
|
pretty a failsafe set of instructions.
|
||||||
|
|
||||||
|
|
||||||
|
SUPPORTED MAPS
|
||||||
|
|
||||||
|
The Eraser is capable of dynamically learning maps, from
|
||||||
|
humans whilst playing the game. However, maps that aren't
|
||||||
|
supported by the release (and hence will require dynamic
|
||||||
|
learning), will suffer from less intelligent behaviour,
|
||||||
|
until the map has been played for a while (usually 10-15 mins).
|
||||||
|
|
||||||
|
It is now possible to play the Eraser bot on any map, it will learn
|
||||||
|
the map as you play, so just make sure you move around if playing
|
||||||
|
a new, or previously unplayed map. You should find the intelligence
|
||||||
|
pick up after about 5 minutes of play.
|
||||||
|
|
||||||
|
The following user-made maps are highly recommended, since they are
|
||||||
|
best suited for Deathmatch play:
|
||||||
|
|
||||||
|
<mapname> <url>
|
||||||
|
|
||||||
|
ikdm1 ftp://ftp.cdrom.com/pub/idgames2/quake2/levels/deathmatch/g-i/ikdm1.zip
|
||||||
|
severed1 http://planetquake.com/cdrom/ramshackle/SEVERED1.ZIP
|
||||||
|
mpq1 ftp://ftp.cdrom.com/pub/idgames2/quake2/levels/deathmatch/m-o/mpq1.zip
|
||||||
|
|
||||||
|
Many thanks to their respective authors.
|
||||||
|
|
||||||
|
Follow the installation instructions for each map, they can be installed
|
||||||
|
as usual, to your quake2\baseq2\maps directory, and will work fine with
|
||||||
|
the Eraser.
|
||||||
|
|
||||||
|
|
||||||
|
MAP CYCLE
|
||||||
|
|
||||||
|
When running a dedicated server, or using the TIMELIMIT or FRAGLIMIT
|
||||||
|
commands, you may want to specify your own cycle of maps. This is now
|
||||||
|
possible due to the inclusion of the MapMod code into The Eraser.
|
||||||
|
|
||||||
|
All you need to do, is edit the file called "maps.txt" in the Eraser directory,
|
||||||
|
and within that file, list the series of maps you want to run. When the end
|
||||||
|
of the list is reached, the map list will cycle back to the first map.
|
||||||
|
|
||||||
|
Thanks to Jeremy Mappus (a.k.a DarkTheties) for the MapMod code.
|
||||||
|
|
||||||
|
|
||||||
|
GAMEPLAY SETTINGS
|
||||||
|
|
||||||
|
Skill Levels
|
||||||
|
|
||||||
|
You can increase or decrease the level of the opponents, using
|
||||||
|
the "skill" setting. The default being "1", if you set this
|
||||||
|
to "2", then the general skill levels of all bots will be raised.
|
||||||
|
They will still maintain their individuality, just some will
|
||||||
|
be slightly better in areas they may not have been on skill "1".
|
||||||
|
|
||||||
|
Note that unless you disable "bot_auto_skill" (see below), Bot's
|
||||||
|
will vary their skill as they play, to make the game more even.
|
||||||
|
The skill setting will be used as the starting skill level for
|
||||||
|
bot's, when auto skill adjustment is enabled.
|
||||||
|
|
||||||
|
Values: 0 (beginner) through 3 (advanced)
|
||||||
|
|
||||||
|
Bot Names/Personalities
|
||||||
|
|
||||||
|
You can edit the names and attributes of the bots, by editing
|
||||||
|
BOTS.CFG, located in the Eraser directory.
|
||||||
|
|
||||||
|
Deathmatch Variations
|
||||||
|
|
||||||
|
Using the "dmflags" setting (accessed via the Multiplayer Menu),
|
||||||
|
you can enable a disable certain rules. Currently, all settings,
|
||||||
|
other than "Teamplay" and "Infinite Ammo" are supported.
|
||||||
|
|
||||||
|
"Weapons Stay" means that weapons will remain after being picked
|
||||||
|
up, unless they were dropped by another player. This is a
|
||||||
|
personal favourite of mine, and I think makes the game much
|
||||||
|
more exciting, if less strategic.
|
||||||
|
|
||||||
|
Please see your Quake2 manual for descriptions of the other
|
||||||
|
settings.
|
||||||
|
|
||||||
|
Values: Use the Multiplayer->Start Network Server->Deeathmatch Flags
|
||||||
|
to set the flags you want to play with
|
||||||
|
|
||||||
|
|
||||||
|
CAPTURE THE FLAG *** new to version v0.8 ***
|
||||||
|
|
||||||
|
Eraser now supports CTF! Just copy the pak0.pak file from your CTF
|
||||||
|
directory to the Eraser folder, then load up one of the CTF maps
|
||||||
|
(q2ctf1 - q2ctf6). Eraser will detect a CTF map, and will enable the
|
||||||
|
CTF code.
|
||||||
|
|
||||||
|
To summon team members to "Raid the Enemy base", just enter:
|
||||||
|
|
||||||
|
rushbase
|
||||||
|
|
||||||
|
To summon team members to "Defend our base", just enter:
|
||||||
|
|
||||||
|
defendbase
|
||||||
|
|
||||||
|
at the consol, or bind a key:
|
||||||
|
|
||||||
|
bind r "rushbase"
|
||||||
|
bind d "defendbase"
|
||||||
|
bind f "freestyle" (return to normal)
|
||||||
|
|
||||||
|
then just press it during play to send all available troops into the
|
||||||
|
enemy base.
|
||||||
|
|
||||||
|
|
||||||
|
To spawn a group of particular bots on a particular team, use:
|
||||||
|
|
||||||
|
sv bluebots <name1> <name2> ... (up to 10 bots at once)
|
||||||
|
sv redbots <name1> <name2> ... (up to 10 bots at once)
|
||||||
|
|
||||||
|
Otherwise, random bots can be spawned, by "ctf_auto_teams <n>" (see Consol Variables below).
|
||||||
|
|
||||||
|
|
||||||
|
To enable advanced use of the grapple hook (which can be quite annoying):
|
||||||
|
|
||||||
|
bot_tarzan 1
|
||||||
|
|
||||||
|
--- CTF specific commands ---
|
||||||
|
|
||||||
|
ctf_auto_teams <n>
|
||||||
|
|
||||||
|
This sets the ideal number of players on each team during
|
||||||
|
CTF play. The program will try it's best to maintain
|
||||||
|
that number of players by spawning and dropping lowest
|
||||||
|
scoring bots as players leave and enter the game. If it
|
||||||
|
cannot do this (there are no bots to drop on a team with
|
||||||
|
too many players) it will spawn additional bots to keep
|
||||||
|
the teams even.
|
||||||
|
|
||||||
|
ctf_special_teams [0..n]
|
||||||
|
|
||||||
|
When ctf_special_teams is set to 1 then each human will have 1 bot to
|
||||||
|
play against.
|
||||||
|
|
||||||
|
It is multiplier, you can increase the value to any number as long as
|
||||||
|
it does not exceed the number of maxclients. As each human departs the
|
||||||
|
corresponding lowest scoring bots get dropped.
|
||||||
|
|
||||||
|
When all human depart all bots are removed thus saving CPU cycles for
|
||||||
|
another quake server.
|
||||||
|
|
||||||
|
ctf_humanonly_teams 0/1
|
||||||
|
|
||||||
|
If this is set then only humans will allowed in a team and bots in the
|
||||||
|
other.
|
||||||
|
|
||||||
|
Once the game has started all humans joining the bot team will be forced
|
||||||
|
into the team with humans.
|
||||||
|
|
||||||
|
Any bots will then be added to the other only.
|
||||||
|
Note: ctf_special_teams must be set for this to work.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
** new to v0.99 **
|
||||||
|
|
||||||
|
It is now possible to turn normal maps into CTF maps. To do this, you must drop the flags
|
||||||
|
using "redflag" and "blueflag" when standing in the desired position of the relative flags.
|
||||||
|
They won't appear straight away, but will be saved in with the route data. To play that map
|
||||||
|
in CTF mode, simply type "ctf 1" in the consol, and then restart the map.
|
||||||
|
|
||||||
|
NOTE: If you drop a CTF flag in the wrong place or by accident, use "clearflags" to remove them.
|
||||||
|
|
||||||
|
NOTE: You can use "toggle_flagpaths" to enable/disable the showing of the direct links between
|
||||||
|
flagpaths (not the actual routes, since showing all routes will break the network code).
|
||||||
|
|
||||||
|
You can also place any other item, using the "ctf_item <classname>" command. These items
|
||||||
|
will only be spawned in CTF mode. Just move to the desired position, and type the
|
||||||
|
preceeding command, specifying tha actual classname for the item you want.
|
||||||
|
|
||||||
|
For example, "ctf_item weapon_chaingun" will place a chaingun at your current position,
|
||||||
|
whenever you load the current map with "CTF 1" set.
|
||||||
|
|
||||||
|
This is handy for evening out bases, in non-CTF maps (although it will work for normal CTF
|
||||||
|
maps also).
|
||||||
|
|
||||||
|
NOTE: You can clear all ctf_item entries by typing "clear_items" at the consol.
|
||||||
|
|
||||||
|
Capture the Flag can be found at..
|
||||||
|
|
||||||
|
ftp://ftp.idsoftware.com/idstuff/quake2/ctf/q2ctf102.exe
|
||||||
|
|
||||||
|
-----------------------DO NOT USE THESE BELOW-------------------------------
|
||||||
|
TEAMPLAY
|
||||||
|
|
||||||
|
Getting Started
|
||||||
|
|
||||||
|
The Eraser bot now fully supports teamplay, with up to 64 pre-defined teams
|
||||||
|
(configurable via BOTS.CFG). To create a team with you and some bots, type:
|
||||||
|
"cmd join <teamname>". Currently, you can only join teams that have been defined
|
||||||
|
in BOTS.CFG (to get a list of available teams in the game, type "cmd teams").
|
||||||
|
|
||||||
|
When joining a team that isn't currently in the game, a group of bots will
|
||||||
|
automatically spawn and join your team. The number of bots that join you,
|
||||||
|
is dependant on the current value of PLAYERS_PER_TEAM (see CONSOL VARIABLES
|
||||||
|
section), and the number of bots specified for that team in BOTS.CFG.
|
||||||
|
|
||||||
|
To add a team of bots to play against, type "addteam <teamname>". If you
|
||||||
|
are running a dedicated server you MUST do this in order for teamplay to
|
||||||
|
function, since in dedicated mode, clients cannot create teams using
|
||||||
|
"cmd join <teamname>". They can only join teams that are already in the game.
|
||||||
|
|
||||||
|
NOTE: if you want to play as a certain member of a team, that has a bot
|
||||||
|
defined for that team in BOTS.CFG (eg. when I play for IMPACT), you must
|
||||||
|
set your name to the bot's name + [abbrev], so for example, I play as
|
||||||
|
"Ridah[IDT]". This way the program knows not to spawn another Ridah bot.
|
||||||
|
|
||||||
|
Rules
|
||||||
|
|
||||||
|
Currently, the rules of Teamplay are very simple. Your team gets a frag
|
||||||
|
for every frag you score. It also loses a frag, everytime you suicide,
|
||||||
|
fall in lava, or kill a teammate. This will be expanded in future releases
|
||||||
|
to support a wide range of Teamplay rules, similar to that of DMFLAGS.
|
||||||
|
|
||||||
|
Scoreboard
|
||||||
|
|
||||||
|
The Teamplay scoreboard is somewhat different to that normal deathmatch.
|
||||||
|
When you join a team, you will start seeing the special Teamplay scoreboard.
|
||||||
|
This sorts the teams in order, with each team having their sorted players
|
||||||
|
to the right. Your team is indicated with the Q2 tag behind the Teamname
|
||||||
|
and score.
|
||||||
|
|
||||||
|
Teamplay Commands
|
||||||
|
|
||||||
|
players_per_team <n>
|
||||||
|
|
||||||
|
This secifies the maximum number of player that are allowed to join
|
||||||
|
each team.
|
||||||
|
|
||||||
|
eg. players_per_team 8
|
||||||
|
|
||||||
|
join <teamname> OR <abbrev>
|
||||||
|
|
||||||
|
Places you on the specified team. If the Team does not yet exist in
|
||||||
|
the game, it will be created (ie. bot teammates will be spawned).
|
||||||
|
|
||||||
|
eg. "cmd join impact", OR "cmd join idt"
|
||||||
|
|
||||||
|
addteam <teamname> OR <abbrev>
|
||||||
|
|
||||||
|
Adds the given team to the game. This team must be defined in
|
||||||
|
BOTS.CFG, or it will not be created.
|
||||||
|
|
||||||
|
eg. "addteam impact", OR "addteam idt"
|
||||||
|
---------------------------------------END----------------------------------
|
||||||
|
group (key "G" by default)
|
||||||
|
|
||||||
|
This tells your fellow teammates that you are starting a new
|
||||||
|
squadron. Eventually, most teammeats will get to you, but they'll
|
||||||
|
try and pick up useful items along the way, so they may take
|
||||||
|
some time if far away. Once they've reached you, they'll follow you
|
||||||
|
around, attacking enemies and picking up items along the way.
|
||||||
|
|
||||||
|
disperse
|
||||||
|
|
||||||
|
Tells all squadron units under your control, to immediately disperse.
|
||||||
|
|
||||||
|
Advanced Teamplay Tactics (Squadron Intelligence)
|
||||||
|
|
||||||
|
As in the "cmd group" described above, you (and the computer) can
|
||||||
|
form Squadrons, to increase team performance. You can use this
|
||||||
|
feature to guard a highly valuable area of the map, or simply to
|
||||||
|
form a posse of destruction. The Eraser is capable of starting it's
|
||||||
|
own group, and knows how to lead a pack, so expect to encounter
|
||||||
|
entire bot teams roaming (and guarding) in packs. You'll also
|
||||||
|
hear your bot teammates start a squadron from time to time, it would
|
||||||
|
be wise to follow a bot every now and then, it's possible he knows
|
||||||
|
something that you don't.
|
||||||
|
|
||||||
|
|
||||||
|
VIEWABLE WEAPONS *** new to version v0.8 ***
|
||||||
|
|
||||||
|
Eraser also supports the viewable weapons patch! Just copy the pak2.pak
|
||||||
|
file from your Viewable Weapons directory, to the Eraser directory,
|
||||||
|
then set "view_weapons 1" in the consol while playing Eraser, and restart
|
||||||
|
the map.
|
||||||
|
|
||||||
|
** NOTE: Viewable Weapons are only supported by the MALE, FEMALE and CYBORG
|
||||||
|
models. To add support for other models, you will need to edit the
|
||||||
|
[view weapons] section of bots.cfg.
|
||||||
|
|
||||||
|
The Viewable Weapons patch can be found at..
|
||||||
|
|
||||||
|
ftp://ftp.telefragged.com/pub/tsunami/vwep_pak.zip
|
||||||
|
|
||||||
|
|
||||||
|
CAMERA MODE *** new to version 0.96 ***
|
||||||
|
|
||||||
|
Thanks to Paul Jordan, Eraser now contains a snazzy camera mode, which
|
||||||
|
lets you watch the action from a TV-like perspective. To enable
|
||||||
|
the camera, type "cam on" in the consol.
|
||||||
|
|
||||||
|
There are two modes to the camera:
|
||||||
|
|
||||||
|
NORMAL: automatically moves around the level to where the action is,
|
||||||
|
good for a demo mode.
|
||||||
|
|
||||||
|
FOLLOW: gives you control over who the camera focuses on. Use the ATTACK
|
||||||
|
button to cycle through players.
|
||||||
|
|
||||||
|
To change the current mode, type "cam <mode>" in the consol, so for FOLLOW
|
||||||
|
mode, just type "cam follow" (must use lowercase).
|
||||||
|
|
||||||
|
|
||||||
|
** NOTE **
|
||||||
|
|
||||||
|
Unfortunately, at this stage it is not possible to de-activate the camera,
|
||||||
|
hopefully this will be rectified in a future version.
|
||||||
|
|
||||||
|
|
||||||
|
CONSOL VARIABLES
|
||||||
|
|
||||||
|
The Eraser provides a range of customization commands and settings,
|
||||||
|
which enable you to populate your server with Bots, when not
|
||||||
|
many humans are playing. This means people are more likely
|
||||||
|
to come to your server, since there is more chance of finding
|
||||||
|
opponents (human or not).
|
||||||
|
|
||||||
|
The following commands are available:
|
||||||
|
|
||||||
|
bot_num <n>
|
||||||
|
|
||||||
|
defines the maximum number of bots
|
||||||
|
|
||||||
|
bot_name <name>
|
||||||
|
|
||||||
|
spawns a specific bot
|
||||||
|
|
||||||
|
bot_auto_skill 0/1 (default = 0)
|
||||||
|
|
||||||
|
disable/enable automatic skill adjustment. When enabled, bot's skill
|
||||||
|
levels will be increased when they are killed (by a human player),
|
||||||
|
and decreased when they kill another human player.
|
||||||
|
|
||||||
|
bot_chat 0/1
|
||||||
|
|
||||||
|
disable/enable bot chatting.
|
||||||
|
|
||||||
|
lag <n> (CLIENT ONLY)
|
||||||
|
|
||||||
|
adds some latency to your controls. <n> should be a number between
|
||||||
|
0 and 1000. This in effect, adds to your ping time.
|
||||||
|
|
||||||
|
eg. "cmd lag 500" gives 500ms latency
|
||||||
|
|
||||||
|
view_weapons 0/1 *** new to v0.8 ***
|
||||||
|
|
||||||
|
disable/enable view weapons patch. Just copy the pak2.pak from
|
||||||
|
your view weapons patch directory, to the Eraser directory, then
|
||||||
|
enable view_weapons. This enables you to see which weapons the
|
||||||
|
other bots/players are using.
|
||||||
|
|
||||||
|
bot_drop <name>
|
||||||
|
|
||||||
|
disconnects a the given bot from the game. If you have set bot_num,
|
||||||
|
then you can expect the program to automatically add a new bot in the
|
||||||
|
game. So make sure you set "bot_num 0" before using this command, if
|
||||||
|
you don't want another random bot to join the game.
|
||||||
|
|
||||||
|
teamplay 0/1
|
||||||
|
|
||||||
|
disables/enables teamplay (see TEAMPLAY section above)
|
||||||
|
|
||||||
|
sv teams <team1> <team2> ...
|
||||||
|
|
||||||
|
spawns one or more teams at once, by name.
|
||||||
|
|
||||||
|
sv bots <bot1> <bot2> ...
|
||||||
|
|
||||||
|
spawns one or more bots at once, by name.
|
||||||
|
|
||||||
|
bot_tarzan 0/1 (default = 0)
|
||||||
|
|
||||||
|
disable/enable so called "advanced" use of the grapple hook.
|
||||||
|
|
||||||
|
mapmod_random 0/1 (default = 0)
|
||||||
|
|
||||||
|
disable/enable random map progression when using maps.txt. When
|
||||||
|
enabled, instead of sequentially traversing the maps, a random
|
||||||
|
map will be selected from the list on each level change.
|
||||||
|
|
||||||
|
botpause
|
||||||
|
|
||||||
|
pauses the game. this is included, since the normal pause button
|
||||||
|
doesn't work during deathmatch play.
|
||||||
|
|
||||||
|
bot_allow_client_commands 0/1 (default = 0)
|
||||||
|
|
||||||
|
disable/enable client-side bot spawning via "cmd bots <n>"
|
||||||
|
|
||||||
|
bot_free_clients <n> (default = 0)
|
||||||
|
|
||||||
|
specifies the number of client positions to keep vacant
|
||||||
|
at all times, whilst there are bots playing. So if
|
||||||
|
you have set "maxclients 32", and there are 20 bots
|
||||||
|
playing, set this value to 3, so that if the total
|
||||||
|
number of clients (players + bots) exceeds 29, a bot will
|
||||||
|
be kicked from the game.
|
||||||
|
|
||||||
|
The lowest scoring bot is kicked first, in this circumstance.
|
||||||
|
|
||||||
|
As soon as more than 3 slots become vacant, a new bot will
|
||||||
|
be automatically brought into the game, assuming the current
|
||||||
|
number of bots is less than the current value of bot_num.
|
||||||
|
|
||||||
|
bot_show_connect_info 0/1
|
||||||
|
|
||||||
|
Disables/Enables the banner that's shown to clients upon
|
||||||
|
connecting, indicating that the server is running the
|
||||||
|
Eraser bot patch.
|
||||||
|
|
||||||
|
bot_calc_nodes 0/1
|
||||||
|
|
||||||
|
Disables/Enables dynamic node calculation. If you are
|
||||||
|
sure that this map has been played enough times, that
|
||||||
|
it is unnecessary for the bot to continue learning the
|
||||||
|
environment from the humans, just set this to 0. This
|
||||||
|
frees up some CPU time, which should make things run
|
||||||
|
a bit smoother if lots of humans are playing.
|
||||||
|
|
||||||
|
|
||||||
|
AUTOMATIC SETTINGS
|
||||||
|
|
||||||
|
It is now possible to save your favourite bot commands/setting in
|
||||||
|
DEFAULTS.CFG, so each time you start the Eraser, these settings
|
||||||
|
are loaded in.
|
||||||
|
|
||||||
|
Be careful when editing this file, if you stuff it up, Eraser will not
|
||||||
|
perform correctly. So make sure you study the above section carefully
|
||||||
|
before messing with it.
|
||||||
|
|
||||||
|
|
||||||
|
NEW TO THIS VERSION!
|
||||||
|
|
||||||
|
Check out http://impact.frag.com for the latest list of bug fixes,
|
||||||
|
and features added.
|
||||||
|
|
||||||
|
|
||||||
|
FREQUENTLY ASKED QUESTIONS
|
||||||
|
|
||||||
|
|
||||||
|
Q: When I start up the map "city3" with view_weapons enabled, the game crashes with
|
||||||
|
"ERROR: *Index overflow". What's going on?
|
||||||
|
|
||||||
|
This is caused by the game trying to load too many models at once. The only
|
||||||
|
way to solve this problem, is to disable view_weapons, or avoid any levels
|
||||||
|
that show this error.
|
||||||
|
|
||||||
|
Q: I start the game, but when I type "bot_num X" it just repeats the command,
|
||||||
|
like I had said it instead, what's up?
|
||||||
|
|
||||||
|
Check your Quake2 directory. If there is a gamex86.dll file in there,
|
||||||
|
delete it. This solves 99% of such problems. If this is not case for
|
||||||
|
you, then you have got the command line, or installation wrong.
|
||||||
|
|
||||||
|
Q: What sort of actions do I need to teach them most efectively ?
|
||||||
|
|
||||||
|
Just make sure you run around, and collect things. If you
|
||||||
|
camp the whole game, they won't learn much at all. You only
|
||||||
|
have to do this once, then the data is saved, so the next
|
||||||
|
time you play the map, you won't have to worry about the learning
|
||||||
|
at all, since it will be switched off.
|
||||||
|
|
||||||
|
Q: Will the learning be establised from both human players or just
|
||||||
|
the serverside player ?
|
||||||
|
|
||||||
|
All human players in the game create node data, when dynamic table
|
||||||
|
generation is enabled.
|
||||||
|
|
||||||
|
Q: When the game starts, it says "ERROR: Game is version X, not Y"?
|
||||||
|
|
||||||
|
Eraser is only compatible with Quake2 v3.12 - v3.14, other
|
||||||
|
versions of Quake2 may not work. To get the latest version of
|
||||||
|
Quake2, go to "http://redwood.stomped.com/".
|
||||||
|
|
||||||
|
Q: The game starts, but I don't see any bots?
|
||||||
|
|
||||||
|
To spawn some bots, type "bot_num <n>", where <n> is
|
||||||
|
a number from 1 to 24. They will then enter game at 1 second
|
||||||
|
intervals (to try and reduce telefrags).
|
||||||
|
|
||||||
|
eg. bot_num 4
|
||||||
|
|
||||||
|
You can also spawn a specific bot using "bot_name <name>",
|
||||||
|
where <name> is the bot's name. You can get the list of bots
|
||||||
|
from bots.cfg, which you'll find in your Eraser directory.
|
||||||
|
|
||||||
|
eg. bot_name cipher
|
||||||
|
|
||||||
|
|
||||||
|
Q: I'm trying to setup an Eraser CTF server, but everytime a client connects
|
||||||
|
they can't join the game?
|
||||||
|
|
||||||
|
That's because the client doesn't have the CTF files installed in
|
||||||
|
Quake2\Eraser, since that's the directory the server is running. So
|
||||||
|
you need to do ONE of the following:
|
||||||
|
|
||||||
|
1) rename your CTF dir to CTF_back, then rename the Eraser dir to
|
||||||
|
CTF. Then start the game with quake2 +set game ctf ...
|
||||||
|
|
||||||
|
*** OR ***
|
||||||
|
|
||||||
|
2) Tell the clients to make an Eraser dir under Quake2, and copy
|
||||||
|
the pak0.pak file from their CTF dir to the Eraser dir.
|
||||||
|
|
||||||
|
|
||||||
|
Q: The bots sometimes stand around looking bored?
|
||||||
|
|
||||||
|
There are still some glitches in the bot decision-making that can result
|
||||||
|
in bots getting stuck. Usually a rocket up their ass helps get them down :)
|
||||||
|
|
||||||
|
|
||||||
|
MAKING CLEAN ROUTES
|
||||||
|
|
||||||
|
Here are a few tips to keep in mind when creating routes for new maps:
|
||||||
|
|
||||||
|
Walk up stairs first, where possible. Otherwise a series of jump/landing
|
||||||
|
nodes will be dropped as you go down, which uses up excess nodes, and
|
||||||
|
is less accurate than walking nodes.
|
||||||
|
|
||||||
|
If you jump off a ledge, try and make a path back to the jumping position
|
||||||
|
as soon as possible. This will prevent all other nodes from having to
|
||||||
|
recalculate best routes later on, after the return path is made.
|
||||||
|
|
||||||
|
The same applies for teleporters and platforms, try and get back to
|
||||||
|
the starting position ASAP.
|
||||||
|
|
||||||
|
Turn on bot_debug_nodes, then enable the scoreboard. If you see "optimizing
|
||||||
|
route xxx -> yyy" showing a whole range of numbers, then it's best to stop
|
||||||
|
here for a bit to let it catch up. You will see this come up fairly often
|
||||||
|
while building a new map, so not to say you should always stop when it's
|
||||||
|
optimizing, just that if you complete a link back to a jumping locaiton
|
||||||
|
(for example) it will need to recalculate a whole bunch of new routes.
|
||||||
|
Stopping after the link is made for a minute or so, will prevent new routes
|
||||||
|
from having to be calculated over and over, as surrounding nodes find better
|
||||||
|
routes.
|
||||||
|
|
||||||
|
Also, try to walk a nice line, rather than dodging around picking up items
|
||||||
|
all over the place. The straighter the line you walk, the more realistic the
|
||||||
|
bot's will move. Keep in mind that the bots will automatically pick up items
|
||||||
|
along the way, if they are available, so you don't have to leave a trail that
|
||||||
|
picks up items, just make sure you walk within the vacinity of all items.
|
||||||
|
|
||||||
|
When loading a map, dynamic node placement will be disabled, IF AND ONLY IF,
|
||||||
|
all items in the level have a node nearby, OR the total number of nodes
|
||||||
|
exceeds 512 (the absolute limit is 750 I believe).
|
||||||
|
|
||||||
|
Only jump when necessary.
|
||||||
|
|
||||||
|
Type "cmd showpath" to enable the green line debugger. Just hit fire to
|
||||||
|
set it's position to where you're standing, then move around and watch
|
||||||
|
the path change. If it disapear's, i means the path is severed, and needs
|
||||||
|
to be traversed to fix it up.
|
||||||
|
|
||||||
|
If you turn on bot_debug_nodes, then restart the level, you will notice
|
||||||
|
a list of items listed upon loading the map, unless the map is fully
|
||||||
|
pathed. Those items listed are not currently reachable by a bot, and they
|
||||||
|
will glow RED in the game. It would be wise to make sure bot_calc_nodes is
|
||||||
|
ENABLED and then go around collecting all glowing items.
|
||||||
|
|
||||||
|
Use the "bot_optimize" command to speed up the optimization process. This
|
||||||
|
will slow down the frame rate depending on how large you set this to, but
|
||||||
|
will often speed up the process by 10x or more.
|
||||||
|
|
||||||
|
Try not to die.
|
||||||
|
|
||||||
|
Alternate Paths in CTF
|
||||||
|
|
||||||
|
It is possible to create alternate paths for bots, when returning the flag
|
||||||
|
in CTF mode. NOTE: this only work in CTF, and is only used when a bot has the
|
||||||
|
flag.
|
||||||
|
|
||||||
|
To do this:
|
||||||
|
|
||||||
|
1. bind a key to "flagpath", eg, 'bind q "flagpath"'.
|
||||||
|
|
||||||
|
2. go to the enemy base, and capture the flag.
|
||||||
|
|
||||||
|
3. press the flagpath key on the way back to base to drop a SOURCE flagpath. This
|
||||||
|
should be dropped in a position that will always be touched by a bot on it's way back
|
||||||
|
to base. So either use a position really close the flag (but not touching) or
|
||||||
|
in a doorway that is close to the flag, that is the only way out of the base.
|
||||||
|
|
||||||
|
4. determine 2 paths that can be taken from this position, dropping a destination
|
||||||
|
node along each of these 2 routes, far enough away from the SOURCE node, to force
|
||||||
|
the bot to take an alternate route to the shortest path.
|
||||||
|
|
||||||
|
5. repeat steps 3 and 4 to create more "branching" flagpaths as required.
|
||||||
|
For example, at the end of one of the paths, there may be another choice between 2 paths.
|
||||||
|
You could create another flagpath branch, by dropping a SOURCE node near the end of the flagpath
|
||||||
|
you just dropped, and then dropping the 2 destination nodes in positions that would
|
||||||
|
force the bot to take alternate routes.
|
||||||
|
|
||||||
|
6. change to the other team (if you were on the red team, type "team blue").
|
||||||
|
|
||||||
|
7. repeat steps 3-5 as required.
|
||||||
|
|
||||||
|
|
||||||
|
Upon touching a SOURCE node while carrying the flag, a bot will then choose a random destination
|
||||||
|
flagpath that belongs to this SOURCE flagpath. It will then proceed to that position until
|
||||||
|
it touches the marker. Once it has done that, it will resume heading back to base. So if you
|
||||||
|
place the destinations too close together, it might take the same route after touching either
|
||||||
|
destination. It's best to place the destinations as far away from the SOURCE flagpath as possible.
|
||||||
|
|
||||||
|
For a running demo of flagpaths being constructed, download the following file, and place it into the
|
||||||
|
quake2\eraser\demos directory. Then while running the Eraser mod, type: "demomap flagpath.dm2".
|
||||||
|
|
||||||
|
Get the demo at: http://impact.frag.com/files/flagpath_demo.zip
|
||||||
|
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
|
||||||
|
This is a BETA release, I therefore will not take responsibility
|
||||||
|
for your system barfing after playing the game. I can however
|
||||||
|
guarantee that I have not purposely added any malicious content
|
||||||
|
to this application. If you believe this to be incorrect, then
|
||||||
|
I'd be happy to discuss the matter with you.
|
||||||
|
|
||||||
|
You may freely distribute this archive, as long as it remains
|
||||||
|
PERFECTLY intact, as distributed on our home page:
|
||||||
|
"http://impact.frag.com/". Thanks.
|
||||||
|
|
||||||
|
|
||||||
|
enjoy,
|
||||||
|
|
||||||
|
Ryan Feltrin
|
|
@ -0,0 +1,2 @@
|
||||||
|
void SelectSpawnPoint (vec3_t origin, vec3_t angles);
|
||||||
|
void ClientUserinfoChanged (edict_t *ent, char *userinfo);
|
|
@ -0,0 +1,153 @@
|
||||||
|
/* g_chase.c */
|
||||||
|
#include "g_local.h"
|
||||||
|
|
||||||
|
|
||||||
|
void UpdateChaseCam(edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t o, ownerv, goal;
|
||||||
|
edict_t *targ;
|
||||||
|
vec3_t forward, right;
|
||||||
|
trace_t trace;
|
||||||
|
int i;
|
||||||
|
vec3_t oldgoal;
|
||||||
|
vec3_t angles;
|
||||||
|
|
||||||
|
// is our chase target gone?
|
||||||
|
if (!ent->client->chase_target->inuse) {
|
||||||
|
ent->client->chase_target = NULL;
|
||||||
|
//K2: Just bring up the menu instead HACK 3/99 acrid botcam
|
||||||
|
ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;//3
|
||||||
|
if(ctf->value)//3
|
||||||
|
CTFOpenJoinMenu(ent);//3
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
targ = ent->client->chase_target;
|
||||||
|
|
||||||
|
VectorCopy(targ->s.origin, ownerv);
|
||||||
|
VectorCopy(ent->s.origin, oldgoal);
|
||||||
|
|
||||||
|
ownerv[2] += targ->viewheight;
|
||||||
|
|
||||||
|
VectorCopy(targ->client->v_angle, angles);
|
||||||
|
if (angles[PITCH] > 56)
|
||||||
|
angles[PITCH] = 56;
|
||||||
|
AngleVectors (angles, forward, right, NULL);
|
||||||
|
VectorNormalize(forward);
|
||||||
|
VectorMA(ownerv, -30, forward, o);
|
||||||
|
|
||||||
|
if (o[2] < targ->s.origin[2] + 20)
|
||||||
|
o[2] = targ->s.origin[2] + 20;
|
||||||
|
|
||||||
|
// jump animation lifts
|
||||||
|
if (!targ->groundentity)
|
||||||
|
o[2] += 16;
|
||||||
|
|
||||||
|
trace = gi.trace(ownerv, vec3_origin, vec3_origin, o, targ, MASK_SOLID);
|
||||||
|
|
||||||
|
VectorCopy(trace.endpos, goal);
|
||||||
|
|
||||||
|
VectorMA(goal, 2, forward, goal);
|
||||||
|
|
||||||
|
// pad for floors and ceilings
|
||||||
|
VectorCopy(goal, o);
|
||||||
|
o[2] += 6;
|
||||||
|
trace = gi.trace(goal, vec3_origin, vec3_origin, o, targ, MASK_SOLID);
|
||||||
|
if (trace.fraction < 1) {
|
||||||
|
VectorCopy(trace.endpos, goal);
|
||||||
|
goal[2] -= 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorCopy(goal, o);
|
||||||
|
o[2] -= 6;
|
||||||
|
trace = gi.trace(goal, vec3_origin, vec3_origin, o, targ, MASK_SOLID);
|
||||||
|
if (trace.fraction < 1) {
|
||||||
|
VectorCopy(trace.endpos, goal);
|
||||||
|
goal[2] += 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->client->ps.pmove.pm_type = PM_FREEZE;
|
||||||
|
|
||||||
|
VectorCopy(goal, ent->s.origin);
|
||||||
|
for (i=0 ; i<3 ; i++)
|
||||||
|
ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(targ->client->v_angle[i] - ent->client->resp.cmd_angles[i]);
|
||||||
|
|
||||||
|
VectorCopy(targ->client->v_angle, ent->client->ps.viewangles);
|
||||||
|
VectorCopy(targ->client->v_angle, ent->client->v_angle);
|
||||||
|
|
||||||
|
ent->viewheight = 0;
|
||||||
|
ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
|
||||||
|
gi.linkentity(ent);
|
||||||
|
|
||||||
|
if ((!ent->client->showscores && !ent->client->menu &&
|
||||||
|
!ent->client->showinventory && !ent->client->showhelp &&
|
||||||
|
!(level.framenum & 31)) || ent->client->update_chase) {
|
||||||
|
char s[1024];
|
||||||
|
|
||||||
|
ent->client->update_chase = false;
|
||||||
|
sprintf(s, "xv 0 yb -58 string2 \"Chasing %s\"",
|
||||||
|
targ->client->pers.netname);
|
||||||
|
gi.WriteByte (svc_layout);
|
||||||
|
gi.WriteString (s);
|
||||||
|
gi.unicast(ent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChaseNext(edict_t *ent)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
edict_t *e;
|
||||||
|
|
||||||
|
if (!ent->client->chase_target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
i = ent->client->chase_target - g_edicts;
|
||||||
|
do {
|
||||||
|
i++;
|
||||||
|
if (i > maxclients->value)
|
||||||
|
i = 1;
|
||||||
|
e = g_edicts + i;
|
||||||
|
if (!e->inuse)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//No chasing bots 3/99 K2?
|
||||||
|
if (e->bot_client)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (e->solid != SOLID_NOT)
|
||||||
|
break;
|
||||||
|
} while (e != ent->client->chase_target);
|
||||||
|
|
||||||
|
ent->client->chase_target = e;
|
||||||
|
ent->client->update_chase = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChasePrev(edict_t *ent)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
edict_t *e;
|
||||||
|
|
||||||
|
if (!ent->client->chase_target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
i = ent->client->chase_target - g_edicts;
|
||||||
|
do {
|
||||||
|
i--;
|
||||||
|
if (i < 1)
|
||||||
|
i = maxclients->value;
|
||||||
|
e = g_edicts + i;
|
||||||
|
if (!e->inuse)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//No chasing bots //3/99 k2?
|
||||||
|
if (e->bot_client)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (e->solid != SOLID_NOT)
|
||||||
|
break;
|
||||||
|
} while (e != ent->client->chase_target);
|
||||||
|
|
||||||
|
ent->client->chase_target = e;
|
||||||
|
ent->client->update_chase = true;
|
||||||
|
}
|
|
@ -0,0 +1,951 @@
|
||||||
|
// g_combat.c
|
||||||
|
|
||||||
|
#include "g_local.h"
|
||||||
|
//ERASER START
|
||||||
|
#include "bot_procs.h"//ERASER
|
||||||
|
///Q2 Camera Begin
|
||||||
|
#include "camclient.h"
|
||||||
|
///Q2 Camera End
|
||||||
|
//ERASER END
|
||||||
|
//WF34 START
|
||||||
|
extern ctfgame_t ctfgame;
|
||||||
|
|
||||||
|
void VectorUnrotate(vec3_t in, vec3_t angles, vec3_t out);
|
||||||
|
//WF34 END
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
CanDamage
|
||||||
|
|
||||||
|
Returns true if the inflictor can directly damage the target. Used for
|
||||||
|
explosions and melee attacks.
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
qboolean CanDamage (edict_t *targ, edict_t *inflictor)
|
||||||
|
{
|
||||||
|
vec3_t dest;
|
||||||
|
trace_t trace;
|
||||||
|
|
||||||
|
// bmodels need special checking because their origin is 0,0,0
|
||||||
|
if (targ->movetype == MOVETYPE_PUSH)
|
||||||
|
{
|
||||||
|
VectorAdd (targ->absmin, targ->absmax, dest);
|
||||||
|
VectorScale (dest, 0.5, dest);
|
||||||
|
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
||||||
|
if (trace.fraction == 1.0)
|
||||||
|
return true;
|
||||||
|
if (trace.ent == targ)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, targ->s.origin, inflictor, MASK_SOLID);
|
||||||
|
if (trace.fraction == 1.0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
VectorCopy (targ->s.origin, dest);
|
||||||
|
dest[0] += 15.0;
|
||||||
|
dest[1] += 15.0;
|
||||||
|
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
||||||
|
if (trace.fraction == 1.0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
VectorCopy (targ->s.origin, dest);
|
||||||
|
dest[0] += 15.0;
|
||||||
|
dest[1] -= 15.0;
|
||||||
|
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
||||||
|
if (trace.fraction == 1.0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
VectorCopy (targ->s.origin, dest);
|
||||||
|
dest[0] -= 15.0;
|
||||||
|
dest[1] += 15.0;
|
||||||
|
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
||||||
|
if (trace.fraction == 1.0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
VectorCopy (targ->s.origin, dest);
|
||||||
|
dest[0] -= 15.0;
|
||||||
|
dest[1] -= 15.0;
|
||||||
|
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
||||||
|
if (trace.fraction == 1.0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
Killed
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
void Killed (edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||||
|
{
|
||||||
|
if (targ->health < -999)
|
||||||
|
targ->health = -999;
|
||||||
|
|
||||||
|
targ->enemy = attacker;
|
||||||
|
|
||||||
|
if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
|
||||||
|
{
|
||||||
|
// targ->svflags |= SVF_DEADMONSTER; // now treat as a different content type
|
||||||
|
if (!(targ->monsterinfo.aiflags & AI_GOOD_GUY))
|
||||||
|
{
|
||||||
|
level.killed_monsters++;
|
||||||
|
if (coop->value && attacker->client)
|
||||||
|
{ attacker->client->resp.score++;
|
||||||
|
botDebugPrint("scored\n");}
|
||||||
|
// medics won't heal monsters that they kill themselves
|
||||||
|
if (strcmp(attacker->classname, "monster_medic") == 0)
|
||||||
|
targ->owner = attacker;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targ->movetype == MOVETYPE_PUSH || targ->movetype == MOVETYPE_STOP || targ->movetype == MOVETYPE_NONE)
|
||||||
|
{ // doors, triggers, etc
|
||||||
|
targ->die (targ, inflictor, attacker, damage, point);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
|
||||||
|
{
|
||||||
|
targ->touch = NULL;
|
||||||
|
monster_death_use (targ);
|
||||||
|
}
|
||||||
|
//ERASER START
|
||||||
|
///Q2 Camera Begin
|
||||||
|
PlayerDied(targ);
|
||||||
|
///Q2 Camera End
|
||||||
|
//ERASER END
|
||||||
|
targ->die (targ, inflictor, attacker, damage, point);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
================
|
||||||
|
SpawnDamage
|
||||||
|
================
|
||||||
|
*/
|
||||||
|
void SpawnDamage (int type, vec3_t origin, vec3_t normal, int damage)
|
||||||
|
{
|
||||||
|
if (damage > 255)
|
||||||
|
damage = 255;
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (type);
|
||||||
|
// gi.WriteByte (damage);
|
||||||
|
gi.WritePosition (origin);
|
||||||
|
gi.WriteDir (normal);
|
||||||
|
gi.multicast (origin, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
T_Damage
|
||||||
|
|
||||||
|
targ entity that is being damaged
|
||||||
|
inflictor entity that is causing the damage
|
||||||
|
attacker entity that caused the inflictor to damage targ
|
||||||
|
example: targ=monster, inflictor=rocket, attacker=player
|
||||||
|
|
||||||
|
dir direction of the attack
|
||||||
|
point point at which the damage is being inflicted
|
||||||
|
normal normal vector from that point
|
||||||
|
damage amount of damage being inflicted
|
||||||
|
knockback force to be applied against targ as a result of the damage
|
||||||
|
|
||||||
|
dflags these flags are used to control how T_Damage works
|
||||||
|
DAMAGE_RADIUS damage was indirect (from a nearby explosion)
|
||||||
|
DAMAGE_NO_ARMOR armor does not protect from this damage
|
||||||
|
DAMAGE_ENERGY damage is from an energy based weapon
|
||||||
|
DAMAGE_NO_KNOCKBACK do not affect velocity, just view angles
|
||||||
|
DAMAGE_BULLET damage is from a bullet (used for ricochets)
|
||||||
|
DAMAGE_NO_PROTECTION kills godmode, armor, everything
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
static int CheckPowerArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int dflags)
|
||||||
|
{
|
||||||
|
gclient_t *client;
|
||||||
|
int save;
|
||||||
|
int power_armor_type;
|
||||||
|
int index;
|
||||||
|
int damagePerCell;
|
||||||
|
int pa_te_type;
|
||||||
|
int power;
|
||||||
|
int power_used;
|
||||||
|
|
||||||
|
if (!damage)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
client = ent->client;
|
||||||
|
|
||||||
|
if (dflags & DAMAGE_NO_ARMOR)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (client)
|
||||||
|
{
|
||||||
|
power_armor_type = PowerArmorType (ent);
|
||||||
|
if (power_armor_type != POWER_ARMOR_NONE)
|
||||||
|
{
|
||||||
|
index = ITEM_INDEX(FindItem("Cells"));
|
||||||
|
power = client->pers.inventory[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ent->svflags & SVF_MONSTER)
|
||||||
|
{
|
||||||
|
power_armor_type = ent->monsterinfo.power_armor_type;
|
||||||
|
power = ent->monsterinfo.power_armor_power;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (power_armor_type == POWER_ARMOR_NONE)
|
||||||
|
return 0;
|
||||||
|
if (!power)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (power_armor_type == POWER_ARMOR_SCREEN)
|
||||||
|
{
|
||||||
|
vec3_t vec;
|
||||||
|
float dot;
|
||||||
|
vec3_t forward;
|
||||||
|
|
||||||
|
// only works if damage point is in front
|
||||||
|
AngleVectors (ent->s.angles, forward, NULL, NULL);
|
||||||
|
VectorSubtract (point, ent->s.origin, vec);
|
||||||
|
VectorNormalize (vec);
|
||||||
|
dot = DotProduct (vec, forward);
|
||||||
|
if (dot <= 0.3)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
damagePerCell = 1;
|
||||||
|
pa_te_type = TE_SCREEN_SPARKS;
|
||||||
|
damage = damage / 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
damagePerCell = 1; // power armor is weaker in CTF
|
||||||
|
pa_te_type = TE_SHIELD_SPARKS;
|
||||||
|
damage = (2 * damage) / 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
save = power * damagePerCell;
|
||||||
|
if (!save)
|
||||||
|
return 0;
|
||||||
|
if (save > damage)
|
||||||
|
save = damage;
|
||||||
|
|
||||||
|
SpawnDamage (pa_te_type, point, normal, save);
|
||||||
|
ent->powerarmor_time = level.time + 0.2;
|
||||||
|
|
||||||
|
power_used = save / damagePerCell;
|
||||||
|
|
||||||
|
if (client)
|
||||||
|
client->pers.inventory[index] -= power_used;
|
||||||
|
else
|
||||||
|
ent->monsterinfo.power_armor_power -= power_used;
|
||||||
|
return save;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int CheckArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int te_sparks, int dflags)
|
||||||
|
{
|
||||||
|
gclient_t *client;
|
||||||
|
int save;
|
||||||
|
int index;
|
||||||
|
gitem_t *armor;
|
||||||
|
|
||||||
|
if (!damage)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
client = ent->client;
|
||||||
|
|
||||||
|
if (!client)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (dflags & DAMAGE_NO_ARMOR)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
index = ArmorIndex (ent);
|
||||||
|
if (!index)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
armor = GetItemByIndex (index);
|
||||||
|
|
||||||
|
if (dflags & DAMAGE_ENERGY)
|
||||||
|
save = ceil(((gitem_armor_t *)armor->info)->energy_protection*damage);
|
||||||
|
else
|
||||||
|
save = ceil(((gitem_armor_t *)armor->info)->normal_protection*damage);
|
||||||
|
if (save >= client->pers.inventory[index])
|
||||||
|
save = client->pers.inventory[index];
|
||||||
|
|
||||||
|
if (!save)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
client->pers.inventory[index] -= save;
|
||||||
|
SpawnDamage (te_sparks, point, normal, save);
|
||||||
|
|
||||||
|
return save;
|
||||||
|
}
|
||||||
|
|
||||||
|
void M_ReactToDamage (edict_t *targ, edict_t *attacker)
|
||||||
|
{
|
||||||
|
botDebugPrint("M_reactTo\n");
|
||||||
|
if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (attacker == targ || attacker == targ->enemy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// if we are a good guy monster and our attacker is a player
|
||||||
|
// or another good guy, do not get mad at them
|
||||||
|
if (targ->monsterinfo.aiflags & AI_GOOD_GUY)
|
||||||
|
{
|
||||||
|
if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we now know that we are not both good guys
|
||||||
|
|
||||||
|
// if attacker is a client, get mad at them because he's good and we're not
|
||||||
|
if (attacker->client)
|
||||||
|
{
|
||||||
|
targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
|
||||||
|
|
||||||
|
// this can only happen in coop (both new and old enemies are clients)
|
||||||
|
// only switch if can't see the current enemy
|
||||||
|
if (targ->enemy && targ->enemy->client)
|
||||||
|
{
|
||||||
|
if (visible(targ, targ->enemy))
|
||||||
|
{
|
||||||
|
targ->oldenemy = attacker;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
targ->oldenemy = targ->enemy;
|
||||||
|
}
|
||||||
|
targ->enemy = attacker;
|
||||||
|
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
||||||
|
{ botDebugPrint("!aiflags &&&&&&");
|
||||||
|
FoundTarget (targ);}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's the same base (walk/swim/fly) type and a different classname and it's not a tank
|
||||||
|
// (they spray too much), get mad at them
|
||||||
|
if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) &&
|
||||||
|
(strcmp (targ->classname, attacker->classname) != 0) &&
|
||||||
|
(strcmp(attacker->classname, "monster_tank") != 0) &&
|
||||||
|
(strcmp(attacker->classname, "monster_supertank") != 0) &&
|
||||||
|
(strcmp(attacker->classname, "monster_makron") != 0) &&
|
||||||
|
(strcmp(attacker->classname, "monster_jorg") != 0) )
|
||||||
|
{
|
||||||
|
if (targ->enemy && targ->enemy->client)
|
||||||
|
targ->oldenemy = targ->enemy;
|
||||||
|
targ->enemy = attacker;
|
||||||
|
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
||||||
|
FoundTarget (targ);
|
||||||
|
}
|
||||||
|
// if they *meant* to shoot us, then shoot back
|
||||||
|
else if (attacker->enemy == targ)
|
||||||
|
{
|
||||||
|
if (targ->enemy && targ->enemy->client)
|
||||||
|
targ->oldenemy = targ->enemy;
|
||||||
|
targ->enemy = attacker;
|
||||||
|
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
||||||
|
FoundTarget (targ);
|
||||||
|
}
|
||||||
|
// otherwise get mad at whoever they are mad at (help our buddy) unless it is us!
|
||||||
|
else if (attacker->enemy && attacker->enemy != targ)
|
||||||
|
{
|
||||||
|
if (targ->enemy && targ->enemy->client)
|
||||||
|
targ->oldenemy = targ->enemy;
|
||||||
|
targ->enemy = attacker->enemy;
|
||||||
|
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
||||||
|
FoundTarget (targ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qboolean CheckTeamDamage (edict_t *targ, edict_t *attacker)
|
||||||
|
{
|
||||||
|
//ZOID
|
||||||
|
if (ctf->value && targ->client && attacker->client)
|
||||||
|
if (targ->client->resp.ctf_team == attacker->client->resp.ctf_team &&
|
||||||
|
targ != attacker)
|
||||||
|
return true;
|
||||||
|
//ZOID
|
||||||
|
|
||||||
|
//FIXME make the next line real and uncomment this block
|
||||||
|
// if ((ability to damage a teammate == OFF) && (targ's team == attacker's team))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//WF34 S JR Stuff for artieral blood spray code
|
||||||
|
void Calc_Offset1(edict_t *self,vec3_t point,vec3_t normal)
|
||||||
|
{
|
||||||
|
vec3_t forward;
|
||||||
|
vec3_t pos1,pos2;
|
||||||
|
// int i;
|
||||||
|
|
||||||
|
VectorSubtract(point, self->s.origin, forward);
|
||||||
|
VectorUnrotate(forward, self->s.angles, pos1);
|
||||||
|
|
||||||
|
AngleVectors(normal, forward, NULL, NULL);
|
||||||
|
VectorMA(point, 64, forward, forward);
|
||||||
|
VectorSubtract(forward, self->s.origin, forward);
|
||||||
|
VectorUnrotate(forward, self->s.angles, pos2);
|
||||||
|
/*///34 used this
|
||||||
|
for(i=0;i<3;i++)
|
||||||
|
{
|
||||||
|
self->client->blood1_pos1[i] = pos1[i];
|
||||||
|
self->client->blood1_pos2[i] = pos2[i];
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
void Calc_Offset2(edict_t *self,vec3_t point,vec3_t normal)
|
||||||
|
{
|
||||||
|
vec3_t forward;
|
||||||
|
vec3_t pos1,pos2;
|
||||||
|
// int i;
|
||||||
|
|
||||||
|
VectorSubtract(point, self->s.origin, forward);
|
||||||
|
VectorUnrotate(forward, self->s.angles, pos1);
|
||||||
|
|
||||||
|
AngleVectors(normal, forward, NULL, NULL);
|
||||||
|
VectorMA(point, 64, forward, forward);
|
||||||
|
VectorSubtract(forward, self->s.origin, forward);
|
||||||
|
VectorUnrotate(forward, self->s.angles, pos2);
|
||||||
|
/*//34 used this
|
||||||
|
for(i=0;i<3;i++)
|
||||||
|
{
|
||||||
|
self->client->blood2_pos1[i] = pos1[i];
|
||||||
|
self->client->blood2_pos2[i] = pos2[i];
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void Calc_Offset3(edict_t *self,vec3_t point,vec3_t normal)
|
||||||
|
{
|
||||||
|
vec3_t forward;
|
||||||
|
vec3_t pos1,pos2;
|
||||||
|
// int i;
|
||||||
|
|
||||||
|
VectorSubtract(point, self->s.origin, forward);
|
||||||
|
VectorUnrotate(forward, self->s.angles, pos1);
|
||||||
|
|
||||||
|
AngleVectors(normal, forward, NULL, NULL);
|
||||||
|
VectorMA(point, 64, forward, forward);
|
||||||
|
VectorSubtract(forward, self->s.origin, forward);
|
||||||
|
VectorUnrotate(forward, self->s.angles, pos2);
|
||||||
|
/*//34 used this
|
||||||
|
for(i=0;i<3;i++)
|
||||||
|
{
|
||||||
|
self->client->sblood1_pos1[i] = pos1[i];
|
||||||
|
self->client->sblood1_pos2[i] = pos2[i];
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
void Calc_Offset4(edict_t *self,vec3_t point,vec3_t normal)
|
||||||
|
{
|
||||||
|
vec3_t forward;
|
||||||
|
vec3_t pos1,pos2;
|
||||||
|
// int i;
|
||||||
|
|
||||||
|
VectorSubtract(point, self->s.origin, forward);
|
||||||
|
VectorUnrotate(forward, self->s.angles, pos1);
|
||||||
|
|
||||||
|
AngleVectors(normal, forward, NULL, NULL);
|
||||||
|
VectorMA(point, 64, forward, forward);
|
||||||
|
VectorSubtract(forward, self->s.origin, forward);
|
||||||
|
VectorUnrotate(forward, self->s.angles, pos2);
|
||||||
|
/*//34 used this
|
||||||
|
for(i=0;i<3;i++)
|
||||||
|
{
|
||||||
|
self->client->sblood2_pos1[i] = pos1[i];
|
||||||
|
self->client->sblood2_pos2[i] = pos2[i];
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
//WF34 E JR end
|
||||||
|
//Gregg This should fix some problems if it screws other stuff up uncomment this t_damage and recomment the next one
|
||||||
|
void T_Damage (edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir, vec3_t point, vec3_t normal, int damage, int knockback, int dflags, int mod)
|
||||||
|
{
|
||||||
|
gclient_t *client;
|
||||||
|
int take;
|
||||||
|
int save;
|
||||||
|
int asave;
|
||||||
|
int psave;
|
||||||
|
int te_sparks;
|
||||||
|
float factor;//WF34
|
||||||
|
int delta;//WF34
|
||||||
|
// float i;//WF34
|
||||||
|
//WF34 START
|
||||||
|
if (wfdebug)
|
||||||
|
{
|
||||||
|
gi.dprintf("T_Damage: dmg = %d\n", damage);
|
||||||
|
if (targ) gi.dprintf("Target=%s\n ",targ->classname);
|
||||||
|
if (targ) gi.dprintf("Targets health=%i\n ",targ->health);
|
||||||
|
if (targ && targ->creator) gi.dprintf("Targets owner=%s \n",targ->creator->classname);
|
||||||
|
if (targ && targ->creator) gi.dprintf("Target owner health=%i \n",targ->creator->health);
|
||||||
|
if (inflictor) gi.dprintf("Inflictor=%s \n",inflictor->classname);
|
||||||
|
if (attacker) gi.dprintf("Attacker=%s \n",attacker->classname);
|
||||||
|
gi.dprintf(",mod = %d\n", mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
//WF34 END
|
||||||
|
//WF24 S
|
||||||
|
//Don't allow negative damage
|
||||||
|
if (damage < 0)
|
||||||
|
{
|
||||||
|
damage = -damage;
|
||||||
|
}
|
||||||
|
//WF24 E
|
||||||
|
|
||||||
|
if (!targ->takedamage)
|
||||||
|
return;
|
||||||
|
//ERASER START
|
||||||
|
if (!attacker)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (level.intermissiontime)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//acrid 3/99 attackers with a freeze gun in inventory cant damage
|
||||||
|
// frozen targ
|
||||||
|
if(targ->frozen && attacker->client &&
|
||||||
|
attacker->client->pers.inventory[ITEM_INDEX(FindItem("Freezer"))])
|
||||||
|
return;
|
||||||
|
|
||||||
|
//ERASER END
|
||||||
|
// friendly fire avoidance
|
||||||
|
// if enabled you can't hurt teammates (but you can hurt yourself)
|
||||||
|
// knockback still occurs
|
||||||
|
if ((targ != attacker) && ((deathmatch->value && ((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) || coop->value))
|
||||||
|
{
|
||||||
|
//If these are both clients, see if they are on the same team
|
||||||
|
if (OnSameTeam (targ, attacker))
|
||||||
|
{
|
||||||
|
if (((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE))
|
||||||
|
damage = 0;
|
||||||
|
else
|
||||||
|
mod |= MOD_FRIENDLY_FIRE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//WF24 S See if the entity allows team damage
|
||||||
|
if ((targ->noteamdamage == true) && (attacker->wf_team == targ->wf_team))
|
||||||
|
{
|
||||||
|
//If this is an created entity (like sentry gun), allow
|
||||||
|
//the creator to damage the entity. Teammates can't.
|
||||||
|
if (targ->creator != attacker)
|
||||||
|
damage = 0;
|
||||||
|
}
|
||||||
|
//WF24 E
|
||||||
|
meansOfDeath = mod;
|
||||||
|
|
||||||
|
// easy mode takes half damage
|
||||||
|
if (skill->value == 0 && deathmatch->value == 0 && targ->client)
|
||||||
|
{
|
||||||
|
damage *= 0.5;
|
||||||
|
if (!damage)
|
||||||
|
damage = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
client = targ->client;
|
||||||
|
|
||||||
|
if (dflags & DAMAGE_BULLET)
|
||||||
|
te_sparks = TE_BULLET_SPARKS;
|
||||||
|
else
|
||||||
|
te_sparks = TE_SPARKS;
|
||||||
|
|
||||||
|
VectorNormalize(dir);
|
||||||
|
|
||||||
|
//WF24 S bonus damage for suprising a monster
|
||||||
|
if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0))
|
||||||
|
damage *= 2;
|
||||||
|
//WF24 E
|
||||||
|
//ZOID
|
||||||
|
//strength tech
|
||||||
|
damage = CTFApplyStrength(attacker, damage);
|
||||||
|
//ZOID
|
||||||
|
|
||||||
|
if (targ->flags & FL_NO_KNOCKBACK)
|
||||||
|
knockback = 0;
|
||||||
|
|
||||||
|
|
||||||
|
// figure momentum add
|
||||||
|
if (!(dflags & DAMAGE_NO_KNOCKBACK))
|
||||||
|
{
|
||||||
|
if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP))
|
||||||
|
{
|
||||||
|
vec3_t kvel;
|
||||||
|
float mass;
|
||||||
|
|
||||||
|
if (targ->mass < 50)
|
||||||
|
mass = 50;
|
||||||
|
else
|
||||||
|
mass = targ->mass;
|
||||||
|
|
||||||
|
if (targ->client && attacker == targ)
|
||||||
|
VectorScale (dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack...
|
||||||
|
else
|
||||||
|
VectorScale (dir, 500.0 * (float)knockback / mass, kvel);
|
||||||
|
|
||||||
|
VectorAdd (targ->velocity, kvel, targ->velocity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
take = damage;
|
||||||
|
save = 0;
|
||||||
|
|
||||||
|
|
||||||
|
// check for godmode
|
||||||
|
if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) )
|
||||||
|
{
|
||||||
|
take = 0;
|
||||||
|
save = damage;
|
||||||
|
SpawnDamage (te_sparks, point, normal, save);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for invincibility
|
||||||
|
if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION))
|
||||||
|
{
|
||||||
|
if (targ->pain_debounce_time < level.time)
|
||||||
|
{
|
||||||
|
gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0);
|
||||||
|
targ->pain_debounce_time = level.time + 2;
|
||||||
|
}
|
||||||
|
take = 0;
|
||||||
|
save = damage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//WF24 S
|
||||||
|
if (targ->client) //Make sure it's a client before we check protecttime
|
||||||
|
{
|
||||||
|
|
||||||
|
//If client has spawn protect and the means of death is *not* MOD_TELEFRAG, return
|
||||||
|
//If the mod type is MOD_TELEFRAG, and the means of death *is* MOD_TELEFRAG,
|
||||||
|
//we've gotten here because both the telefragger and telefragge have spawn protection
|
||||||
|
//If both have spawn protection, we kill the telefraggee anyway
|
||||||
|
if (K2_IsProtected(targ) && mod != MOD_TELEFRAG && mod != MOD_REVERSE_TELEFRAG)
|
||||||
|
{
|
||||||
|
gi.sound(targ,CHAN_ITEM,gi.soundindex("items/protect3.wav"),1, ATTN_NORM, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//WF24 E
|
||||||
|
|
||||||
|
//ZOID
|
||||||
|
//team armor protect//ERASER ADDED ATTACKER
|
||||||
|
if (ctf->value && targ->client && attacker && attacker->client &&
|
||||||
|
targ->client->resp.ctf_team == attacker->client->resp.ctf_team &&
|
||||||
|
targ != attacker && ((int)dmflags->value & DF_ARMOR_PROTECT)) {
|
||||||
|
psave = asave = 0;
|
||||||
|
}
|
||||||
|
else if (mod != WEAPON_ARMORDART) //ignore armor for this one
|
||||||
|
{
|
||||||
|
//ZOID
|
||||||
|
psave = CheckPowerArmor (targ, point, normal, take, dflags);
|
||||||
|
take -= psave;
|
||||||
|
|
||||||
|
asave = CheckArmor (targ, point, normal, take, te_sparks, dflags);
|
||||||
|
take -= asave;
|
||||||
|
}
|
||||||
|
else//WF24 S
|
||||||
|
{
|
||||||
|
psave = asave = 0;
|
||||||
|
}//WF24 E
|
||||||
|
|
||||||
|
//treat cheat/powerup savings the same as armor
|
||||||
|
asave += save;
|
||||||
|
|
||||||
|
|
||||||
|
//ZOID
|
||||||
|
//resistance tech
|
||||||
|
take = CTFApplyResistance(targ, take);
|
||||||
|
//ZOID
|
||||||
|
|
||||||
|
// team damage avoidance
|
||||||
|
if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
//ZOID
|
||||||
|
CTFCheckHurtCarrier(targ, attacker);
|
||||||
|
//ZOID
|
||||||
|
|
||||||
|
//WF24 S
|
||||||
|
|
||||||
|
if (take < 0)
|
||||||
|
{
|
||||||
|
take = -take;
|
||||||
|
}
|
||||||
|
//WF24 E
|
||||||
|
|
||||||
|
// do the damage
|
||||||
|
if (take)
|
||||||
|
{
|
||||||
|
if ((targ->svflags & SVF_MONSTER) || (client))
|
||||||
|
SpawnDamage (TE_BLOOD, point, normal, take);
|
||||||
|
else
|
||||||
|
SpawnDamage (te_sparks, point, normal, take);
|
||||||
|
|
||||||
|
//Artieral Blood spray code JR 9/25/98
|
||||||
|
/*
|
||||||
|
if(targ->client)
|
||||||
|
{
|
||||||
|
//A large hit somewhere maybe
|
||||||
|
if(take>50)
|
||||||
|
{
|
||||||
|
//Check to seee if the first major blood spot is free
|
||||||
|
if(targ->client->blood1_amount=0)
|
||||||
|
{
|
||||||
|
i=random();
|
||||||
|
//Did they hit a major blood vessel
|
||||||
|
if(i>0.45)
|
||||||
|
{
|
||||||
|
//Yes they did!!! =-)
|
||||||
|
//Set it up here for the spraying
|
||||||
|
VectorCopy(point,targ->client->blood1_point);
|
||||||
|
VectorCopy(normal,targ->client->blood1_normal);
|
||||||
|
Calc_Offset1(targ,targ->client->blood1_point,targ->client->blood1_normal);
|
||||||
|
|
||||||
|
targ->client->blood1_amount=(take-20-take%5);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Check to see if the second major blood spot is free
|
||||||
|
//if it isn't there won't be any blood pouring out
|
||||||
|
//most likely they are dead anyways unless they had
|
||||||
|
//a lot of health. Also for lag reasons
|
||||||
|
else if(targ->client->blood2_amount=0)
|
||||||
|
{
|
||||||
|
i=random();
|
||||||
|
//Chances are a little harder
|
||||||
|
if(i>0.3333)
|
||||||
|
{
|
||||||
|
//Oh my gosh its another hit
|
||||||
|
targ->client->blood2_amount=(take-20-take%5);
|
||||||
|
VectorCopy(point,targ->client->blood2_point);
|
||||||
|
VectorCopy(normal,targ->client->blood2_normal);
|
||||||
|
Calc_Offset2(targ,targ->client->blood2_point,targ->client->blood2_normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//For the smaller blood vessels I guess
|
||||||
|
//The amount sprayed out is half the amount of a major hit
|
||||||
|
//per spray for the equal amount number
|
||||||
|
else if(take>25)
|
||||||
|
{
|
||||||
|
//is the first small blood point being used
|
||||||
|
if(targ->client->sblood1_amount=0)
|
||||||
|
{
|
||||||
|
//It isn't time to see if they are lucky
|
||||||
|
i=random();
|
||||||
|
if(i>0.15)
|
||||||
|
{
|
||||||
|
//Yes they are
|
||||||
|
targ->client->sblood1_amount=(take-10-take%5);
|
||||||
|
VectorCopy(point,targ->client->sblood1_point);
|
||||||
|
VectorCopy(normal,targ->client->sblood1_normal);
|
||||||
|
Calc_Offset3(targ,targ->client->sblood1_point,targ->client->sblood1_normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//checking the second blood point
|
||||||
|
else if(targ->client->sblood2_amount=0)
|
||||||
|
{
|
||||||
|
//Random numbers blah blah blah...
|
||||||
|
i=random();
|
||||||
|
if(i>0.1)
|
||||||
|
{
|
||||||
|
//Imagine if all the blood spots are taken
|
||||||
|
//How gruesome
|
||||||
|
targ->client->sblood2_amount=(take-10-take%5);
|
||||||
|
VectorCopy(point,targ->client->sblood2_point);
|
||||||
|
VectorCopy(normal,targ->client->sblood2_normal);
|
||||||
|
Calc_Offset4(targ,targ->client->sblood2_point,targ->client->sblood2_normal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
//end artieral blood spray code JR 9/25/98
|
||||||
|
|
||||||
|
//WF34 ++TeT start team balancing code
|
||||||
|
if (((int)wfflags->value & WF_AUTO_TEAM_BALANCE)
|
||||||
|
&& ((ctfgame.team1 > 2) || (ctfgame.team2 > 2)))
|
||||||
|
{
|
||||||
|
if (targ->wf_team == CTF_TEAM1)
|
||||||
|
{
|
||||||
|
delta = (ctfgame.team1 - ctfgame.team2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delta = (ctfgame.team2 - ctfgame.team1);
|
||||||
|
}
|
||||||
|
// never let them take less then 20%
|
||||||
|
if (delta < -8)
|
||||||
|
{
|
||||||
|
delta = -8;
|
||||||
|
}
|
||||||
|
factor = (10.0 + (float)delta) / 10.0;
|
||||||
|
take = (float)take * factor;
|
||||||
|
}
|
||||||
|
//WF34 E --TeT end team balancing code
|
||||||
|
|
||||||
|
targ->health = targ->health - take;
|
||||||
|
|
||||||
|
if (targ->health <= 0)
|
||||||
|
{
|
||||||
|
if ((targ->svflags & SVF_MONSTER) || (client))
|
||||||
|
targ->flags |= FL_NO_KNOCKBACK;
|
||||||
|
Killed (targ, inflictor, attacker, take, point);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((targ->svflags & SVF_MONSTER) && (!targ->bot_client))//ERASER ADDED && TBC
|
||||||
|
{
|
||||||
|
M_ReactToDamage (targ, attacker);
|
||||||
|
if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take))
|
||||||
|
{
|
||||||
|
targ->pain (targ, attacker, knockback, take);
|
||||||
|
// nightmare mode monsters don't go into pain frames often
|
||||||
|
if (skill->value == 3)
|
||||||
|
targ->pain_debounce_time = level.time + 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (client)
|
||||||
|
{
|
||||||
|
if (!(targ->flags & FL_GODMODE) && (take))
|
||||||
|
targ->pain (targ, attacker, knockback, take);
|
||||||
|
}
|
||||||
|
else if (take)
|
||||||
|
{
|
||||||
|
if (targ->pain)
|
||||||
|
targ->pain (targ, attacker, knockback, take);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add to the damage inflicted on a player this frame
|
||||||
|
// the total will be turned into screen blends and view angle kicks
|
||||||
|
// at the end of the frame
|
||||||
|
if (client)
|
||||||
|
{
|
||||||
|
client->damage_parmor += psave;
|
||||||
|
client->damage_armor += asave;
|
||||||
|
client->damage_blood += take;
|
||||||
|
client->damage_knockback += knockback;
|
||||||
|
VectorCopy (point, client->damage_from);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
T_RadiusDamage
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
void T_RadiusDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
|
||||||
|
{
|
||||||
|
float points;
|
||||||
|
edict_t *ent = NULL;
|
||||||
|
edict_t *prev_ent = NULL;//WF34
|
||||||
|
vec3_t v;
|
||||||
|
vec3_t dir;
|
||||||
|
int i = 0;//WF34
|
||||||
|
|
||||||
|
if (wfdebug)
|
||||||
|
{
|
||||||
|
gi.dprintf("T_RadiusDamage: dmg = %d; inflictor = %s\n", (int)damage, inflictor->classname);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
|
||||||
|
{
|
||||||
|
++i;//WF34
|
||||||
|
if (ent == ignore)
|
||||||
|
continue;
|
||||||
|
if (!ent->takedamage)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
VectorAdd (ent->mins, ent->maxs, v);
|
||||||
|
VectorMA (ent->s.origin, 0.5, v, v);
|
||||||
|
VectorSubtract (inflictor->s.origin, v, v);
|
||||||
|
points = damage - 0.5 * VectorLength (v);
|
||||||
|
if (ent == attacker)
|
||||||
|
points = points * 0.5;
|
||||||
|
if (points > 0)
|
||||||
|
{
|
||||||
|
if (CanDamage (ent, inflictor))
|
||||||
|
{
|
||||||
|
VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
|
||||||
|
if (wfdebug)
|
||||||
|
{
|
||||||
|
gi.dprintf("Calling t_damage. Damage = %d, Points = %d, vlen = %d\n", (int)damage, (int)points, (int) VectorLength (v));
|
||||||
|
}
|
||||||
|
T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
|
||||||
|
//ERASER START, don't drop jump nodes if rocket jumping
|
||||||
|
if (ent->jump_ent && ent->client && !ent->bot_client && ent->velocity[2] > 0)
|
||||||
|
{
|
||||||
|
G_FreeEdict(ent->jump_ent);
|
||||||
|
ent->jump_ent = NULL;
|
||||||
|
}
|
||||||
|
//ERASER END
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev_ent = ent;//WF34
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================================================
|
||||||
|
T_Radius2Damage - modified to include kickback flags
|
||||||
|
===========================================================
|
||||||
|
*/
|
||||||
|
void T_Radius2Damage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int kickback, int mod)
|
||||||
|
{
|
||||||
|
float points;
|
||||||
|
edict_t *ent = NULL;
|
||||||
|
edict_t *prev_ent = NULL;//WF34
|
||||||
|
vec3_t v;
|
||||||
|
vec3_t dir;
|
||||||
|
int i = 0;//WF34
|
||||||
|
|
||||||
|
if (wfdebug)
|
||||||
|
{
|
||||||
|
gi.dprintf("T_RadiusDamage: dmg = %d\n", (int)damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
|
||||||
|
{
|
||||||
|
++i;//WF34
|
||||||
|
if (ent == ignore)
|
||||||
|
continue;
|
||||||
|
if (!ent->takedamage)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
VectorAdd (ent->mins, ent->maxs, v);
|
||||||
|
VectorMA (ent->s.origin, 0.5, v, v);
|
||||||
|
VectorSubtract (inflictor->s.origin, v, v);
|
||||||
|
points = damage - 0.5 * VectorLength (v);
|
||||||
|
if (ent == attacker)
|
||||||
|
points = points * 0.5;
|
||||||
|
if (points > 0)
|
||||||
|
{
|
||||||
|
if (CanDamage (ent, inflictor))
|
||||||
|
{
|
||||||
|
VectorSubtract (ent->s.origin, inflictor->s.origin, dir);
|
||||||
|
if (wfdebug) gi.dprintf("Calling t_damage. Damage = %d, Points = %d, vlen = %d\n", (int)damage, (int)points, (int) VectorLength (v));
|
||||||
|
T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, kickback, DAMAGE_RADIUS, mod);
|
||||||
|
//ERASER START, don't drop jump nodes if rocket jumping
|
||||||
|
if (ent->jump_ent && ent->client && !ent->bot_client && ent->velocity[2] > 0)
|
||||||
|
{
|
||||||
|
G_FreeEdict(ent->jump_ent);
|
||||||
|
ent->jump_ent = NULL;
|
||||||
|
}
|
||||||
|
//ERASER END
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prev_ent = ent;//WF34
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
#define CTF_VERSION 1.02
|
||||||
|
#define CTF_VSTRING2(x) #x
|
||||||
|
#define CTF_VSTRING(x) CTF_VSTRING2(x)
|
||||||
|
#define CTF_STRING_VERSION CTF_VSTRING(CTF_VERSION)
|
||||||
|
|
||||||
|
#define STAT_CTF_TEAM1_PIC 17
|
||||||
|
#define STAT_CTF_TEAM1_CAPS 18
|
||||||
|
#define STAT_CTF_TEAM2_PIC 19
|
||||||
|
#define STAT_CTF_TEAM2_CAPS 20
|
||||||
|
#define STAT_CTF_FLAG_PIC 21
|
||||||
|
#define STAT_CTF_JOINED_TEAM1_PIC 22
|
||||||
|
#define STAT_CTF_JOINED_TEAM2_PIC 23
|
||||||
|
#define STAT_CTF_TEAM1_HEADER 24
|
||||||
|
#define STAT_CTF_TEAM2_HEADER 25
|
||||||
|
#define STAT_CTF_TECH 26
|
||||||
|
#define STAT_CTF_ID_VIEW 27
|
||||||
|
|
||||||
|
//WF
|
||||||
|
#define STAT_DAMAGE 28
|
||||||
|
#define STAT_DAMAGE_ICON 29
|
||||||
|
#define STAT_TIMEOUT_ICON 30
|
||||||
|
//WF
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CTF_NOTEAM,
|
||||||
|
CTF_TEAM1,
|
||||||
|
CTF_TEAM2
|
||||||
|
} ctfteam_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CTF_STATE_START,
|
||||||
|
CTF_STATE_PLAYING
|
||||||
|
} ctfstate_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CTF_GRAPPLE_STATE_FLY,
|
||||||
|
CTF_GRAPPLE_STATE_PULL,
|
||||||
|
CTF_GRAPPLE_STATE_HANG
|
||||||
|
} ctfgrapplestate_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct ctfgame_s
|
||||||
|
{
|
||||||
|
int team1, team2;
|
||||||
|
int total1, total2; // these are only set when going into intermission!
|
||||||
|
float last_flag_capture;
|
||||||
|
int last_capture_team;
|
||||||
|
} ctfgame_t;
|
||||||
|
|
||||||
|
|
||||||
|
extern cvar_t *ctf;
|
||||||
|
|
||||||
|
#define CTF_TEAM1_SKIN "ctf_r"
|
||||||
|
#define CTF_TEAM1_SKIN_RECON "xrec_r"
|
||||||
|
#define CTF_TEAM1_SKIN_NURSE "xnur_r"
|
||||||
|
#define CTF_TEAM1_SKIN_ENGINEER "xeng_r"
|
||||||
|
#define CTF_TEAM1_SKIN_MARINE "xmar_r"
|
||||||
|
#define CTF_TEAM1_SKIN_CYBORG "xcyb_r"
|
||||||
|
#define CTF_TEAM1_SKIN_ARSONIST "xars_r"
|
||||||
|
|
||||||
|
#define CTF_TEAM2_SKIN "ctf_b"
|
||||||
|
#define CTF_TEAM2_SKIN_RECON "xrec_b"
|
||||||
|
#define CTF_TEAM2_SKIN_NURSE "xnur_b"
|
||||||
|
#define CTF_TEAM2_SKIN_ENGINEER "xeng_b"
|
||||||
|
#define CTF_TEAM2_SKIN_MARINE "xmar_b"
|
||||||
|
#define CTF_TEAM2_SKIN_CYBORG "xcyb_b"
|
||||||
|
#define CTF_TEAM2_SKIN_ARSONIST "xars_b"
|
||||||
|
|
||||||
|
#define DF_CTF_FORCEJOIN 131072
|
||||||
|
#define DF_ARMOR_PROTECT 262144
|
||||||
|
#define DF_CTF_NO_TECH 524288
|
||||||
|
|
||||||
|
#define CTF_FLAG_RETURN_TIME 40 // seconds until auto return
|
||||||
|
|
||||||
|
//WF - Made these variables so they can be set by server admin
|
||||||
|
#ifdef WFMAIN
|
||||||
|
int CTF_FRAG_POINTS = 1; // Points for a frag
|
||||||
|
int CTF_SUICIDE_POINTS = -1; // Points for a suicide
|
||||||
|
int CTF_SENTRY_POINTS = 1; // Points for killing a sentry gun
|
||||||
|
int CTF_CAPTURE_BONUS = 15; // what you get for capture
|
||||||
|
int CTF_TEAM_BONUS = 10; // what your team gets for capture
|
||||||
|
int CTF_RECOVERY_BONUS = 1; // what you get for recovery
|
||||||
|
int CTF_FLAG_BONUS = 0; // what you get for picking up enemy flag
|
||||||
|
int CTF_FRAG_CARRIER_BONUS = 2; // what you get for fragging enemy flag carrier
|
||||||
|
int CTF_CARRIER_DANGER_PROTECT_BONUS = 2; // bonus for fraggin someone who has recently hurt your flag carrier
|
||||||
|
int CTF_CARRIER_PROTECT_BONUS = 1; // bonus for fraggin someone while either you or your target are near your flag carrier
|
||||||
|
int CTF_FLAG_DEFENSE_BONUS = 1; // bonus for fraggin someone while either you or your target are near your flag
|
||||||
|
int CTF_RETURN_FLAG_ASSIST_BONUS = 1; // awarded for returning a flag that causes a capture to happen almost immediately
|
||||||
|
int CTF_FRAG_CARRIER_ASSIST_BONUS = 2; // award for fragging a flag carrier if a capture happens almost immediately
|
||||||
|
#else
|
||||||
|
extern int CTF_FRAG_POINTS;
|
||||||
|
extern int CTF_SUICIDE_POINTS;
|
||||||
|
extern int CTF_SENTRY_POINTS;
|
||||||
|
extern int CTF_CAPTURE_BONUS;
|
||||||
|
extern int CTF_TEAM_BONUS;
|
||||||
|
extern int CTF_RECOVERY_BONUS;
|
||||||
|
extern int CTF_FLAG_BONUS;
|
||||||
|
extern int CTF_FRAG_CARRIER_BONUS;
|
||||||
|
extern int CTF_CARRIER_DANGER_PROTECT_BONUS;
|
||||||
|
extern int CTF_CARRIER_PROTECT_BONUS;
|
||||||
|
extern int CTF_FLAG_DEFENSE_BONUS;
|
||||||
|
extern int CTF_RETURN_FLAG_ASSIST_BONUS;
|
||||||
|
extern int CTF_FRAG_CARRIER_ASSIST_BONUS;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define CTF_TARGET_PROTECT_RADIUS 400 // the radius around an object being defended where a target will be worth extra frags
|
||||||
|
#define CTF_ATTACKER_PROTECT_RADIUS 400 // the radius around an object being defended where an attacker will get extra frags when making kills
|
||||||
|
|
||||||
|
#define CTF_CARRIER_DANGER_PROTECT_TIMEOUT 8
|
||||||
|
#define CTF_FRAG_CARRIER_ASSIST_TIMEOUT 10
|
||||||
|
#define CTF_RETURN_FLAG_ASSIST_TIMEOUT 10
|
||||||
|
|
||||||
|
#define CTF_AUTO_FLAG_RETURN_TIMEOUT 45 // number of seconds before dropped flag auto-returns
|
||||||
|
|
||||||
|
#define CTF_TECH_TIMEOUT 60 // seconds before techs spawn again
|
||||||
|
|
||||||
|
#define CTF_GRAPPLE_SPEED 650 // speed of grapple in flight
|
||||||
|
#define CTF_GRAPPLE_PULL_SPEED 650 // speed player is pulled at
|
||||||
|
|
||||||
|
void CTFInit(void);
|
||||||
|
|
||||||
|
void SP_info_player_team1(edict_t *self);
|
||||||
|
void SP_info_player_team2(edict_t *self);
|
||||||
|
|
||||||
|
char *CTFTeamName(int team);
|
||||||
|
char *CTFOtherTeamName(int team);
|
||||||
|
void CTFAssignSkin(edict_t *ent, char *s);
|
||||||
|
void CTFAssignTeam(gclient_t *who, qboolean is_bot);
|
||||||
|
edict_t *SelectCTFSpawnPoint (edict_t *ent);
|
||||||
|
qboolean CTFPickup_Flag(edict_t *ent, edict_t *other);
|
||||||
|
qboolean CTFDrop_Flag(edict_t *ent, gitem_t *item);
|
||||||
|
void CTFEffects(edict_t *player);
|
||||||
|
void CTFCalcScores(void);
|
||||||
|
void SetCTFStats(edict_t *ent);
|
||||||
|
void CTFDeadDropFlag(edict_t *self);
|
||||||
|
void CTFScoreboardMessage (edict_t *ent, edict_t *killer);
|
||||||
|
void CTFTeam_f (edict_t *ent);
|
||||||
|
void CTFID_f (edict_t *ent);
|
||||||
|
void CTFSay_Team(edict_t *who, char *msg);
|
||||||
|
void CTFFlagSetup (edict_t *ent);
|
||||||
|
void CTFResetFlag(int ctf_team);
|
||||||
|
void CTFFragBonuses(edict_t *targ, edict_t *inflictor, edict_t *attacker);
|
||||||
|
void CTFCheckHurtCarrier(edict_t *targ, edict_t *attacker);
|
||||||
|
|
||||||
|
// GRAPPLE
|
||||||
|
void CTFWeapon_Grapple (edict_t *ent);
|
||||||
|
void CTFPlayerResetGrapple(edict_t *ent);
|
||||||
|
void CTFGrapplePull(edict_t *self);
|
||||||
|
void CTFResetGrapple(edict_t *self);
|
||||||
|
|
||||||
|
//TECH
|
||||||
|
gitem_t *CTFWhat_Tech(edict_t *ent);
|
||||||
|
qboolean CTFPickup_Tech (edict_t *ent, edict_t *other);
|
||||||
|
void CTFDrop_Tech(edict_t *ent, gitem_t *item);
|
||||||
|
void CTFDeadDropTech(edict_t *ent);
|
||||||
|
void CTFSetupTechSpawn(void);
|
||||||
|
int CTFApplyResistance(edict_t *ent, int dmg);
|
||||||
|
int CTFApplyStrength(edict_t *ent, int dmg);
|
||||||
|
qboolean CTFApplyStrengthSound(edict_t *ent);
|
||||||
|
qboolean CTFApplyHaste(edict_t *ent);
|
||||||
|
void CTFApplyHasteSound(edict_t *ent);
|
||||||
|
void CTFApplyRegeneration(edict_t *ent);
|
||||||
|
qboolean CTFHasRegeneration(edict_t *ent);
|
||||||
|
void CTFRespawnTech(edict_t *ent);
|
||||||
|
|
||||||
|
void CTFOpenJoinMenu(edict_t *ent);
|
||||||
|
qboolean CTFStartClient(edict_t *ent);
|
||||||
|
|
||||||
|
qboolean CTFCheckRules(void);
|
||||||
|
|
||||||
|
void SP_misc_ctf_banner (edict_t *ent);
|
||||||
|
void SP_misc_ctf_small_banner (edict_t *ent);
|
||||||
|
|
||||||
|
extern char *ctf_statusbar;
|
||||||
|
|
||||||
|
void UpdateChaseCam(edict_t *ent);
|
||||||
|
void ChaseNext(edict_t *ent);
|
||||||
|
void ChasePrev(edict_t *ent);
|
||||||
|
|
||||||
|
void SP_trigger_teleport (edict_t *ent);
|
||||||
|
void SP_info_teleport_destination (edict_t *ent);
|
||||||
|
|
||||||
|
//WF
|
||||||
|
void stuffcmd(edict_t *ent, char *s);
|
||||||
|
void WFShowHelp(edict_t *ent, pmenu_t *p);
|
||||||
|
//WF
|
||||||
|
|
||||||
|
//NEWGRAPPLE Acrid 4/99
|
||||||
|
qboolean Started_Grappling(gclient_t *client);
|
||||||
|
qboolean Ended_Grappling(gclient_t *client);
|
||||||
|
qboolean Is_Grappling(gclient_t *client);
|
||||||
|
void CTFPlayerResetGrapple2(edict_t *ent);
|
||||||
|
void CTFResetGrapple2(edict_t *ent);
|
||||||
|
void CTFGrappleFire2 (edict_t *ent, vec3_t g_offset, int damage, int effect);
|
||||||
|
void CTFWeapon_Grapple_Fire2 (edict_t *ent);
|
||||||
|
void CTFWeapon_Grapple2 (edict_t *ent);
|
||||||
|
void CTFGrappleTouch2 (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
|
||||||
|
void CTFFireGrapple2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect);
|
||||||
|
void CTFGrappleTouch2 (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf);
|
||||||
|
void CTFGrapplePull2(edict_t *self);
|
||||||
|
void CTFGrappleThink2( edict_t *self );
|
|
@ -0,0 +1,61 @@
|
||||||
|
/*==============================
|
||||||
|
Acrid Added 5/99
|
||||||
|
===============================*/
|
||||||
|
#include "g_local.h"
|
||||||
|
|
||||||
|
char *ReadEntFile(char *filename)
|
||||||
|
{
|
||||||
|
|
||||||
|
FILE *fp;
|
||||||
|
char *filestring = NULL;
|
||||||
|
long int i = 0;
|
||||||
|
int ch;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
fp = fopen(filename, "r");
|
||||||
|
if (!fp) break;
|
||||||
|
|
||||||
|
for (i=0; (ch = fgetc(fp)) != EOF; i++)
|
||||||
|
;
|
||||||
|
|
||||||
|
filestring = gi.TagMalloc(i+1, TAG_LEVEL);
|
||||||
|
if (!filestring) break;
|
||||||
|
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
for (i=0; (ch = fgetc(fp)) != EOF; i++)
|
||||||
|
filestring[i] = ch;
|
||||||
|
filestring[i] = '\0';
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fp) fclose(fp);
|
||||||
|
|
||||||
|
return(filestring);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *LoadEntFile(char *mapname, char *entities)
|
||||||
|
{
|
||||||
|
char entfilename[MAX_QPATH] = "";
|
||||||
|
char *newentities;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
sprintf(entfilename, "wf/ent/%s.ent", mapname);
|
||||||
|
// convert string to all lowercase (for Linux)
|
||||||
|
for (i = 0; entfilename[i]; i++)
|
||||||
|
entfilename[i] = tolower(entfilename[i]);
|
||||||
|
|
||||||
|
newentities = ReadEntFile(entfilename);
|
||||||
|
|
||||||
|
if (newentities)
|
||||||
|
{ //leave these dprints active they show up in the server init console section
|
||||||
|
gi.dprintf("%s.ent Loaded\n", mapname);
|
||||||
|
return(newentities); // reassign the ents
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gi.dprintf("No .ent File for %s.bsp\n", mapname);
|
||||||
|
return(entities);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*****************************************************************
|
||||||
|
|
||||||
|
Eraser Bot source code - by Ryan Feltrin, Added to by Acrid-
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
This file is Copyright(c) 1998, Ryan Feltrin, All Rights Reserved.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
All other files are Copyright(c) Id Software, Inc.
|
||||||
|
|
||||||
|
Please see liscense.txt in the source directory for the copyright
|
||||||
|
information regarding those files belonging to Id Software, Inc.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
Should you decide to release a modified version of Eraser, you MUST
|
||||||
|
include the following text (minus the BEGIN and END lines) in the
|
||||||
|
documentation for your modification.
|
||||||
|
|
||||||
|
--- BEGIN ---
|
||||||
|
|
||||||
|
The Eraser Bot is a product of Ryan Feltrin, and is available from
|
||||||
|
the Eraser Bot homepage, at http://impact.frag.com.
|
||||||
|
|
||||||
|
This program is a modification of the Eraser Bot, and is therefore
|
||||||
|
in NO WAY supported by Ryan Feltrin.
|
||||||
|
|
||||||
|
This program MUST NOT be sold in ANY form. If you have paid for
|
||||||
|
this product, you should contact Ryan Feltrin immediately, via
|
||||||
|
the Eraser Bot homepage.
|
||||||
|
|
||||||
|
--- END ---
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
You will find p_trail.c has not been included with the Eraser
|
||||||
|
source code release. This is NOT an error. I am unable to
|
||||||
|
distribute this file because it contains code that is bound by
|
||||||
|
legal documents, and signed by myself, never to be released
|
||||||
|
to the public. Sorry guys, but law is law.
|
||||||
|
|
||||||
|
I have therefore include the compiled version of these files
|
||||||
|
in .obj form in the src\Release and src\Debug directories.
|
||||||
|
So while you cannot edit and debug code within these files,
|
||||||
|
you can still compile this source as-is. Although these will only
|
||||||
|
work in MSVC v5.0, linux versions can be made available upon
|
||||||
|
request.
|
||||||
|
|
||||||
|
NOTE: When compiling this source, you will get a warning
|
||||||
|
message from the compiler, regarding the missing p_trail.c
|
||||||
|
file. Just ignore it, it will still compile fine.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
I, Ryan Feltrin/Acrid-, hold no responsibility for any harm caused by the
|
||||||
|
use of this source code. I also am NOT willing to provide any form
|
||||||
|
of help or support for this source code. It is provided as-is,
|
||||||
|
as a service by me, with no documentation, other then the comments
|
||||||
|
contained within the code. If you have any queries, I suggest you
|
||||||
|
visit the "official" Eraser source web-board, at
|
||||||
|
http://www.telefragged.com/epidemic/. I will stop by there from
|
||||||
|
time to time, to answer questions and help with any problems that
|
||||||
|
may arise.
|
||||||
|
|
||||||
|
Otherwise, have fun, and I look forward to seeing what can be done
|
||||||
|
with this.
|
||||||
|
|
||||||
|
-Ryan Feltrin
|
||||||
|
-Acrid-
|
||||||
|
|
||||||
|
*****************************************************************/
|
||||||
|
qboolean Pickup_Weapon (edict_t *ent, edict_t *other);
|
||||||
|
qboolean Pickup_Health (edict_t *ent, edict_t *other);
|
||||||
|
qboolean Pickup_Ammo (edict_t *ent, edict_t *other);
|
||||||
|
qboolean Pickup_Armor (edict_t *ent, edict_t *other);
|
||||||
|
qboolean Pickup_Pack (edict_t *ent, edict_t *other);
|
||||||
|
qboolean Place_Special (edict_t *ent, edict_t *other);//42 Acrid
|
||||||
|
|
||||||
|
edict_t *AddToItemList(edict_t *ent, edict_t *head);
|
||||||
|
|
||||||
|
extern int jacket_armor_index;
|
||||||
|
extern int combat_armor_index;
|
||||||
|
extern int body_armor_index;
|
||||||
|
extern int power_screen_index;
|
||||||
|
extern int power_shield_index;
|
||||||
|
|
||||||
|
#define HEALTH_IGNORE_MAX 1
|
||||||
|
#define HEALTH_TIMED 2
|
|
@ -0,0 +1,943 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// monster weapons
|
||||||
|
//
|
||||||
|
|
||||||
|
//FIXME mosnters 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.
|
||||||
|
void monster_fire_bullet (edict_t *self, vec3_t start, vec3_t dir, int damage, int kick, int hspread, int vspread, int flashtype, int mod)//ERASER ADDED INT MOD
|
||||||
|
{
|
||||||
|
fire_bullet (self, start, dir, damage, kick, hspread, vspread, mod);//WF24 USES MOD_UNKNOWN fixme
|
||||||
|
//ERASER START
|
||||||
|
if (self->map && (flashtype != MZ2_ACTOR_MACHINEGUN_1)) // bot
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
else
|
||||||
|
//ERASER END
|
||||||
|
gi.WriteByte (svc_muzzleflash2);
|
||||||
|
gi.WriteShort (self - g_edicts);
|
||||||
|
gi.WriteByte (flashtype);
|
||||||
|
gi.multicast (start, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void monster_fire_shotgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int count, int flashtype, int mod)//ERASER ADDED INT MOD
|
||||||
|
{
|
||||||
|
fire_shotgun (self, start, aimdir, damage, kick, hspread, vspread, count, mod);//WF24 USES MON_UNKNOWN fixme
|
||||||
|
|
||||||
|
if (self->map) // bot
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
else
|
||||||
|
gi.WriteByte (svc_muzzleflash2);
|
||||||
|
gi.WriteShort (self - g_edicts);
|
||||||
|
gi.WriteByte (flashtype);
|
||||||
|
gi.multicast (start, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void monster_fire_blaster (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype, int effect)
|
||||||
|
{
|
||||||
|
fire_blaster (self, start, dir, damage, speed, effect, false);
|
||||||
|
//ERASER START
|
||||||
|
if (self->map) // bot
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
else
|
||||||
|
//ERASER END
|
||||||
|
gi.WriteByte (svc_muzzleflash2);
|
||||||
|
gi.WriteShort (self - g_edicts);
|
||||||
|
gi.WriteByte (flashtype);
|
||||||
|
gi.multicast (start, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void monster_fire_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int flashtype)
|
||||||
|
{
|
||||||
|
fire_grenade (self, start, aimdir, damage, speed, 2.5, damage+40);
|
||||||
|
//ERASER START
|
||||||
|
if (self->map) // bot
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
else
|
||||||
|
//ERASER END
|
||||||
|
gi.WriteByte (svc_muzzleflash2);
|
||||||
|
gi.WriteShort (self - g_edicts);
|
||||||
|
gi.WriteByte (flashtype);
|
||||||
|
gi.multicast (start, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void monster_fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int flashtype)
|
||||||
|
{
|
||||||
|
fire_rocket (self, start, dir, damage, speed, damage+20, damage, MOD_ROCKET);//fixme 34 has MOD_ROCKET
|
||||||
|
//ERASER START
|
||||||
|
if (self->map) // bot
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
else
|
||||||
|
//ERASER END
|
||||||
|
gi.WriteByte (svc_muzzleflash2);
|
||||||
|
gi.WriteShort (self - g_edicts);
|
||||||
|
gi.WriteByte (flashtype);
|
||||||
|
gi.multicast (start, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void monster_fire_railgun (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int flashtype)
|
||||||
|
{
|
||||||
|
fire_rail (self, start, aimdir, damage, kick, MOD_RAILGUN);
|
||||||
|
//ERASER START
|
||||||
|
if (self->map) // bot
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
else
|
||||||
|
//ERASER END
|
||||||
|
gi.WriteByte (svc_muzzleflash2);
|
||||||
|
gi.WriteShort (self - g_edicts);
|
||||||
|
gi.WriteByte (flashtype);
|
||||||
|
gi.multicast (start, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void monster_fire_bfg (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, int kick, float damage_radius, int flashtype)
|
||||||
|
{
|
||||||
|
fire_bfg (self, start, aimdir, damage, speed, damage_radius);
|
||||||
|
//ERASER START
|
||||||
|
if (self->map) // bot
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
else
|
||||||
|
//ERASER END
|
||||||
|
gi.WriteByte (svc_muzzleflash2);
|
||||||
|
gi.WriteShort (self - g_edicts);
|
||||||
|
gi.WriteByte (flashtype);
|
||||||
|
gi.multicast (start, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Monster utility functions
|
||||||
|
//
|
||||||
|
|
||||||
|
static void M_FliesOff (edict_t *self)
|
||||||
|
{
|
||||||
|
self->s.effects &= ~EF_FLIES;
|
||||||
|
self->s.sound = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void M_FliesOn (edict_t *self)
|
||||||
|
{
|
||||||
|
if (self->waterlevel)
|
||||||
|
return;
|
||||||
|
self->s.effects |= EF_FLIES;
|
||||||
|
self->s.sound = gi.soundindex ("infantry/inflies1.wav");
|
||||||
|
self->think = M_FliesOff;
|
||||||
|
self->nextthink = level.time + 60;
|
||||||
|
}
|
||||||
|
|
||||||
|
void M_FlyCheck (edict_t *self)
|
||||||
|
{
|
||||||
|
if (self->waterlevel)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (random() > 0.5)
|
||||||
|
return;
|
||||||
|
|
||||||
|
self->think = M_FliesOn;
|
||||||
|
self->nextthink = level.time + 5 + 10 * random();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AttackFinished (edict_t *self, float time)
|
||||||
|
{
|
||||||
|
self->monsterinfo.attack_finished = level.time + time;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CanJump(edict_t *ent);//ERASER
|
||||||
|
|
||||||
|
void M_CheckGround (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t point;
|
||||||
|
trace_t trace;
|
||||||
|
|
||||||
|
//WF24 S
|
||||||
|
if (ent->flags & (FL_SWIM|FL_FLY))
|
||||||
|
return;
|
||||||
|
//WF24E
|
||||||
|
//ERASER START
|
||||||
|
if (ent->health <= 0)
|
||||||
|
return;
|
||||||
|
//ERASER END
|
||||||
|
if (ent->velocity[2] > 210)//WF24-34 USES 100 FIXME
|
||||||
|
{
|
||||||
|
ent->groundentity = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the hull point one-quarter unit down is solid the entity is on ground
|
||||||
|
point[0] = ent->s.origin[0];
|
||||||
|
point[1] = ent->s.origin[1];
|
||||||
|
point[2] = ent->s.origin[2] - 1;//WF24-34 USES - 0.25;FIXME
|
||||||
|
|
||||||
|
trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, point, ent, MASK_PLAYERSOLID);//WF24 USES MONSTERSOLID
|
||||||
|
//ERASER START
|
||||||
|
if ((trace.startsolid) && !(trace.ent->client) && !(trace.ent->health <= 0))
|
||||||
|
{ // we're stuck
|
||||||
|
// find a safe position?
|
||||||
|
// int x, y;
|
||||||
|
qboolean safe=false;
|
||||||
|
|
||||||
|
ent->maxs[2] = 4; // duck
|
||||||
|
ent->crouch_attack_time = level.time + 2;
|
||||||
|
/*
|
||||||
|
for (x=-16; x<=16; x+=8)
|
||||||
|
for (y=-16; y<=16; y+=8)
|
||||||
|
{
|
||||||
|
if (!x && !y)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
point[0] = ent->s.origin[0] + x;
|
||||||
|
point[1] = ent->s.origin[1] + y;
|
||||||
|
point[2] = ent->s.origin[2];
|
||||||
|
|
||||||
|
trace = gi.trace(point, ent->mins, ent->maxs, point, ent, MASK_PLAYERSOLID);
|
||||||
|
|
||||||
|
if (!trace.startsolid)
|
||||||
|
{
|
||||||
|
safe = true;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// still stuck, try moving up
|
||||||
|
ent->s.origin[2] += 26;
|
||||||
|
|
||||||
|
point[0] = ent->s.origin[0];
|
||||||
|
point[1] = ent->s.origin[1];
|
||||||
|
point[2] = ent->s.origin[2];
|
||||||
|
|
||||||
|
trace = gi.trace(point, ent->mins, ent->maxs, point, ent, MASK_PLAYERSOLID);
|
||||||
|
|
||||||
|
if (!trace.startsolid)
|
||||||
|
{
|
||||||
|
safe = true;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->s.origin[2] -= 26;
|
||||||
|
|
||||||
|
done:
|
||||||
|
|
||||||
|
if (!safe)
|
||||||
|
{
|
||||||
|
if (ent->maxs[2] < 32)
|
||||||
|
{
|
||||||
|
T_Damage(ent, ent, ent, VEC_ORIGIN, ent->s.origin, VEC_ORIGIN, 100000, 0, DAMAGE_NO_PROTECTION, MOD_SUICIDE);
|
||||||
|
}
|
||||||
|
else // try ducking
|
||||||
|
{
|
||||||
|
ent->maxs[2] = 4;
|
||||||
|
ent->crouch_attack_time = level.time + 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//ERASER END
|
||||||
|
// check steepness//WF24-34 DIF HERE FIXME?
|
||||||
|
if ( (trace.fraction < 1) && (trace.ent == world) && (trace.plane.normal[2] <= 0.4))
|
||||||
|
{
|
||||||
|
|
||||||
|
VectorScale(trace.plane.normal, 300, ent->velocity);//E
|
||||||
|
VectorCopy(ent->velocity, ent->jump_velocity);//E
|
||||||
|
|
||||||
|
//ACO if (CanJump(ent))
|
||||||
|
//ACO ent->s.origin[2] += 1;
|
||||||
|
|
||||||
|
ent->groundentity = NULL;
|
||||||
|
gi.linkentity(ent);//E
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trace.fraction < 1)
|
||||||
|
ent->groundentity = trace.ent;
|
||||||
|
else
|
||||||
|
ent->groundentity = NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//WF24 MUCH DIFFEENT ABOVE FIXME ACRID
|
||||||
|
void M_CatagorizePosition (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t point;
|
||||||
|
int cont;
|
||||||
|
|
||||||
|
//
|
||||||
|
// get waterlevel
|
||||||
|
//
|
||||||
|
point[0] = ent->s.origin[0];
|
||||||
|
point[1] = ent->s.origin[1];
|
||||||
|
point[2] = ent->s.origin[2] + ent->mins[2] + 1;
|
||||||
|
cont = gi.pointcontents (point);
|
||||||
|
|
||||||
|
if (!(cont & MASK_WATER))
|
||||||
|
{
|
||||||
|
ent->waterlevel = 0;
|
||||||
|
ent->watertype = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->watertype = cont;
|
||||||
|
ent->waterlevel = 1;
|
||||||
|
point[2] += 26;
|
||||||
|
cont = gi.pointcontents (point);
|
||||||
|
if (!(cont & MASK_WATER))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent->waterlevel = 2;
|
||||||
|
point[2] += 22;
|
||||||
|
cont = gi.pointcontents (point);
|
||||||
|
if (cont & MASK_WATER)
|
||||||
|
ent->waterlevel = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void M_WorldEffects (edict_t *ent)
|
||||||
|
{
|
||||||
|
int dmg;
|
||||||
|
|
||||||
|
if (ent->health > 0)
|
||||||
|
{
|
||||||
|
if (!(ent->flags & FL_SWIM))
|
||||||
|
{
|
||||||
|
if (ent->waterlevel < 3)
|
||||||
|
{
|
||||||
|
ent->air_finished = level.time + 12;
|
||||||
|
}
|
||||||
|
else if (ent->air_finished < level.time)
|
||||||
|
{ // drown!
|
||||||
|
if (ent->pain_debounce_time < level.time)
|
||||||
|
{
|
||||||
|
dmg = 2 + 2 * floor(level.time - ent->air_finished);
|
||||||
|
if (dmg > 15)
|
||||||
|
dmg = 15;
|
||||||
|
T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
|
||||||
|
ent->pain_debounce_time = level.time + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ent->waterlevel > 0)
|
||||||
|
{
|
||||||
|
ent->air_finished = level.time + 9;
|
||||||
|
}
|
||||||
|
else if (ent->air_finished < level.time)
|
||||||
|
{ // suffocate!
|
||||||
|
if (ent->pain_debounce_time < level.time)
|
||||||
|
{
|
||||||
|
dmg = 2 + 2 * floor(level.time - ent->air_finished);
|
||||||
|
if (dmg > 15)
|
||||||
|
dmg = 15;
|
||||||
|
T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, dmg, 0, DAMAGE_NO_ARMOR, MOD_WATER);
|
||||||
|
ent->pain_debounce_time = level.time + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent->waterlevel == 0)
|
||||||
|
{
|
||||||
|
if (ent->flags & FL_INWATER)
|
||||||
|
{
|
||||||
|
gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_out.wav"), 1, ATTN_NORM, 0);
|
||||||
|
ent->flags &= ~FL_INWATER;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ent->watertype & CONTENTS_LAVA) && !(ent->flags & FL_IMMUNE_LAVA))
|
||||||
|
{
|
||||||
|
if (ent->damage_debounce_time < level.time)
|
||||||
|
{
|
||||||
|
ent->damage_debounce_time = level.time + 0.2;
|
||||||
|
T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 10*ent->waterlevel, 0, 0, MOD_LAVA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((ent->watertype & CONTENTS_SLIME) && !(ent->flags & FL_IMMUNE_SLIME))
|
||||||
|
{
|
||||||
|
if (ent->damage_debounce_time < level.time)
|
||||||
|
{
|
||||||
|
ent->damage_debounce_time = level.time + 1;
|
||||||
|
T_Damage (ent, world, world, vec3_origin, ent->s.origin, vec3_origin, 4*ent->waterlevel, 0, 0, MOD_SLIME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !(ent->flags & FL_INWATER) )
|
||||||
|
{
|
||||||
|
if (!(ent->svflags & SVF_DEADMONSTER))
|
||||||
|
{
|
||||||
|
if (ent->watertype & CONTENTS_LAVA)
|
||||||
|
if (random() <= 0.5)
|
||||||
|
gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava1.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else
|
||||||
|
gi.sound (ent, CHAN_BODY, gi.soundindex("player/lava2.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else if (ent->watertype & CONTENTS_SLIME)
|
||||||
|
gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else if (ent->watertype & CONTENTS_WATER)
|
||||||
|
gi.sound (ent, CHAN_BODY, gi.soundindex("player/watr_in.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->flags |= FL_INWATER;
|
||||||
|
ent->damage_debounce_time = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void M_droptofloor (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t end;
|
||||||
|
trace_t trace;
|
||||||
|
|
||||||
|
ent->s.origin[2] += 1;
|
||||||
|
VectorCopy (ent->s.origin, end);
|
||||||
|
end[2] -= 256;
|
||||||
|
|
||||||
|
trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
|
||||||
|
|
||||||
|
if (trace.fraction == 1 || trace.allsolid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
VectorCopy (trace.endpos, ent->s.origin);
|
||||||
|
|
||||||
|
gi.linkentity (ent);
|
||||||
|
M_CheckGround (ent);
|
||||||
|
M_CatagorizePosition (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void M_SetEffects (edict_t *ent)
|
||||||
|
{
|
||||||
|
//acrid 3/99
|
||||||
|
if (ent->bot_client && ent->frozen)//white shell botfreeze
|
||||||
|
{
|
||||||
|
ent->s.effects |= EF_COLOR_SHELL;
|
||||||
|
ent->s.renderfx |= (RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ent->s.effects &= ~(EF_COLOR_SHELL|EF_POWERSCREEN);
|
||||||
|
ent->s.renderfx &= ~(RF_SHELL_RED|RF_SHELL_GREEN|RF_SHELL_BLUE);
|
||||||
|
|
||||||
|
if (ent->monsterinfo.aiflags & AI_RESURRECTING)
|
||||||
|
{
|
||||||
|
ent->s.effects |= EF_COLOR_SHELL;
|
||||||
|
ent->s.renderfx |= RF_SHELL_RED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent->health <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ent->powerarmor_time > level.time)
|
||||||
|
{
|
||||||
|
if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SCREEN)
|
||||||
|
{
|
||||||
|
ent->s.effects |= EF_POWERSCREEN;
|
||||||
|
}
|
||||||
|
else if (ent->monsterinfo.power_armor_type == POWER_ARMOR_SHIELD)
|
||||||
|
{
|
||||||
|
ent->s.effects |= EF_COLOR_SHELL;
|
||||||
|
ent->s.renderfx |= RF_SHELL_GREEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void M_MoveFrame (edict_t *self)
|
||||||
|
{
|
||||||
|
mmove_t *move;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
move = self->monsterinfo.currentmove;
|
||||||
|
self->nextthink = level.time + FRAMETIME;
|
||||||
|
|
||||||
|
if ((self->monsterinfo.nextframe) && (self->monsterinfo.nextframe >= move->firstframe) && (self->monsterinfo.nextframe <= move->lastframe))
|
||||||
|
{
|
||||||
|
self->s.frame = self->monsterinfo.nextframe;
|
||||||
|
self->monsterinfo.nextframe = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (self->s.frame == move->lastframe)
|
||||||
|
{
|
||||||
|
if (move->endfunc)
|
||||||
|
{
|
||||||
|
move->endfunc (self);
|
||||||
|
|
||||||
|
// regrab move, endfunc is very likely to change it
|
||||||
|
move = self->monsterinfo.currentmove;
|
||||||
|
|
||||||
|
// check for death
|
||||||
|
if (self->svflags & SVF_DEADMONSTER)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->s.frame < move->firstframe || self->s.frame > move->lastframe)
|
||||||
|
{
|
||||||
|
self->monsterinfo.aiflags &= ~AI_HOLD_FRAME;
|
||||||
|
self->s.frame = move->firstframe;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
|
||||||
|
{
|
||||||
|
self->s.frame++;
|
||||||
|
if (self->s.frame > move->lastframe)
|
||||||
|
self->s.frame = move->firstframe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
index = self->s.frame - move->firstframe;
|
||||||
|
if (move->frame[index].aifunc)
|
||||||
|
if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME))
|
||||||
|
move->frame[index].aifunc (self, move->frame[index].dist * self->monsterinfo.scale);
|
||||||
|
else
|
||||||
|
move->frame[index].aifunc (self, 0);
|
||||||
|
|
||||||
|
if (move->frame[index].thinkfunc)
|
||||||
|
move->frame[index].thinkfunc (self);
|
||||||
|
}
|
||||||
|
//ERASER START
|
||||||
|
extern edict_t *current_player;
|
||||||
|
extern gclient_t *current_client;
|
||||||
|
void P_WorldEffects (void);
|
||||||
|
//ERASER END
|
||||||
|
void ClientUserinfoChanged (edict_t *ent, char *userinfo);//botfreeze 3/99
|
||||||
|
//WF24-34 IS ALOT DIFFERENT BELOW COPY OLD THINK AND RENAME FOR DECOYS
|
||||||
|
void monster_think (edict_t *self)
|
||||||
|
{
|
||||||
|
// int playernum;
|
||||||
|
// char userinfo[MAX_INFO_STRING];//acrid 3/99 freeze
|
||||||
|
|
||||||
|
// Yes, this is a hack. This is the result of starting such a project
|
||||||
|
// without knowing what anything does. Idealistically, bot's should have
|
||||||
|
// their own totally unique think, and shouldn't interfer with the normal
|
||||||
|
// monster code, but the Eraser started out as a normal monster, so I had
|
||||||
|
// to use this code. It is possible to put back the original code and
|
||||||
|
// use a check for self->bot_client to disperse bot's and normal monsters.
|
||||||
|
// That way, a coop bot would be possible (but not probable, given the
|
||||||
|
// amount of work required).
|
||||||
|
|
||||||
|
if (!self->bot_client)
|
||||||
|
{
|
||||||
|
gi.error("\nCannot play Eraser in single player mode.\n\nType \"deathmatch 1\" to play the Eraser Bot.\n\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//acrid 3/99
|
||||||
|
if (self->bot_client && self->frozen)
|
||||||
|
{
|
||||||
|
if( level.time < self->frozentime )
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// playernum = self - g_edicts - 1;
|
||||||
|
// strcpy( userinfo, self->client->pers.userinfo );
|
||||||
|
self->frozen = 0;
|
||||||
|
// Info_SetValueForKey( userinfo, "skin", self->oldskin );
|
||||||
|
// ClientUserinfoChanged( self, userinfo );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (self->last_think_time == level.time)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (self->client->showscores) // must be in intermission
|
||||||
|
return;
|
||||||
|
if (level.intermissiontime)
|
||||||
|
return; // already activated
|
||||||
|
|
||||||
|
bot_frametime = 0.1;
|
||||||
|
|
||||||
|
// check X/Y jumping velocity, make sure we continue moving forwards when obstructed by a stair
|
||||||
|
// or other solids before landing
|
||||||
|
if (!self->groundentity && (self->waterlevel < 2))
|
||||||
|
{
|
||||||
|
// trace_t tr;
|
||||||
|
// vec3_t dest;
|
||||||
|
|
||||||
|
// if (self->jump_velocity[0] && self->jump_velocity[1])
|
||||||
|
// {
|
||||||
|
self->velocity[0] = self->jump_velocity[0];
|
||||||
|
self->velocity[1] = self->jump_velocity[1];
|
||||||
|
// }
|
||||||
|
/*
|
||||||
|
VectorCopy(self->s.origin, dest);
|
||||||
|
dest[2] -= 8;
|
||||||
|
|
||||||
|
tr = gi.trace(self->s.origin, self->mins, self->maxs, dest, self, MASK_PLAYERSOLID);
|
||||||
|
|
||||||
|
// only really inair if way above ground
|
||||||
|
if (tr.fraction == 1)
|
||||||
|
{
|
||||||
|
self->last_inair = level.time;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
self->last_inair = level.time;
|
||||||
|
|
||||||
|
if (!self->client->ctf_grapple || (self->client->ctf_grapplestate <= CTF_GRAPPLE_STATE_FLY))
|
||||||
|
{
|
||||||
|
// this is some really ugly hacks to fix in-air velocity stuff
|
||||||
|
if (self->velocity[2] > self->wait)
|
||||||
|
self->wait = self->velocity[2];
|
||||||
|
|
||||||
|
if ((self->velocity[2] > -300) && (self->velocity[2] < 0))
|
||||||
|
self->velocity[2] = self->wait - sv_gravity->value*FRAMETIME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (self->jump_velocity[0] || self->jump_velocity[1])
|
||||||
|
{
|
||||||
|
self->jump_velocity[0] = self->jump_velocity[1] = 0;;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->last_inair || (self->last_inair > level.time))
|
||||||
|
self->last_inair = 0;
|
||||||
|
else // make sure we remove it next frame
|
||||||
|
self->last_inair = level.time + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->wait = self->velocity[2];
|
||||||
|
|
||||||
|
self->monsterinfo.run(self);
|
||||||
|
self->last_think_time = level.time;
|
||||||
|
|
||||||
|
// if (self->linkcount != self->monsterinfo.linkcount)
|
||||||
|
// {
|
||||||
|
// self->monsterinfo.linkcount = self->linkcount;
|
||||||
|
if (!self->client->ctf_grapple)
|
||||||
|
M_CheckGround (self);
|
||||||
|
// }
|
||||||
|
M_CatagorizePosition (self);
|
||||||
|
|
||||||
|
current_player = self;
|
||||||
|
current_client = self->client;
|
||||||
|
|
||||||
|
P_WorldEffects ();
|
||||||
|
M_SetEffects (self);
|
||||||
|
|
||||||
|
self->nextthink = level.time + FRAMETIME;
|
||||||
|
//self->nextthink = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//WF24 IS ALOT DIFFFERENT ABOVE
|
||||||
|
/*
|
||||||
|
================
|
||||||
|
monster_use
|
||||||
|
|
||||||
|
Using a monster makes it angry at the current activator
|
||||||
|
================
|
||||||
|
*/
|
||||||
|
void monster_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
if (self->enemy)
|
||||||
|
return;
|
||||||
|
if (self->health <= 0)
|
||||||
|
return;
|
||||||
|
if (activator->flags & FL_NOTARGET)
|
||||||
|
return;
|
||||||
|
if (!(activator->client) && !(activator->monsterinfo.aiflags & AI_GOOD_GUY))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// delay reaction so if the monster is teleported, its sound is still heard
|
||||||
|
self->enemy = activator;
|
||||||
|
FoundTarget (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void monster_start_go (edict_t *self);
|
||||||
|
|
||||||
|
|
||||||
|
void monster_triggered_spawn (edict_t *self)
|
||||||
|
{
|
||||||
|
self->s.origin[2] += 1;
|
||||||
|
KillBox (self);
|
||||||
|
|
||||||
|
self->solid = SOLID_BBOX;
|
||||||
|
self->movetype = MOVETYPE_STEP;
|
||||||
|
self->svflags &= ~SVF_NOCLIENT;
|
||||||
|
self->air_finished = level.time + 12;
|
||||||
|
gi.linkentity (self);
|
||||||
|
|
||||||
|
monster_start_go (self);
|
||||||
|
|
||||||
|
if (self->enemy && !(self->spawnflags & 1) && !(self->enemy->flags & FL_NOTARGET))
|
||||||
|
{
|
||||||
|
FoundTarget (self);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->enemy = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void monster_triggered_spawn_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
// we have a one frame delay here so we don't telefrag the guy who activated us
|
||||||
|
self->think = monster_triggered_spawn;
|
||||||
|
self->nextthink = level.time + FRAMETIME;
|
||||||
|
if (activator->client)
|
||||||
|
self->enemy = activator;
|
||||||
|
self->use = monster_use;
|
||||||
|
}
|
||||||
|
|
||||||
|
void monster_triggered_start (edict_t *self)
|
||||||
|
{
|
||||||
|
self->solid = SOLID_NOT;
|
||||||
|
self->movetype = MOVETYPE_NONE;
|
||||||
|
self->svflags |= SVF_NOCLIENT;
|
||||||
|
self->nextthink = 0;
|
||||||
|
self->use = monster_triggered_spawn_use;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
================
|
||||||
|
monster_death_use
|
||||||
|
|
||||||
|
When a monster dies, it fires all of its targets with the current
|
||||||
|
enemy as activator.
|
||||||
|
================
|
||||||
|
*/
|
||||||
|
void monster_death_use (edict_t *self)
|
||||||
|
{
|
||||||
|
self->flags &= ~(FL_FLY|FL_SWIM);
|
||||||
|
self->monsterinfo.aiflags &= AI_GOOD_GUY;
|
||||||
|
|
||||||
|
if (self->item)
|
||||||
|
{
|
||||||
|
Drop_Item (self, self->item);
|
||||||
|
self->item = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->deathtarget)
|
||||||
|
self->target = self->deathtarget;
|
||||||
|
|
||||||
|
if (!self->target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
G_UseTargets (self, self->enemy);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
void monster_start (edict_t *self)
|
||||||
|
{
|
||||||
|
/*WF24 USES TOO BUT ITS ACO THERE TOO
|
||||||
|
if ((deathmatch->value || nomonsters->value) && (!strcmp(self->classname, "bot")))
|
||||||
|
{
|
||||||
|
G_FreeEdict (self);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ((self->spawnflags & 4) && !(self->monsterinfo.aiflags & 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 (!(self->monsterinfo.aiflags & AI_GOOD_GUY))
|
||||||
|
level.total_monsters++;
|
||||||
|
|
||||||
|
self->nextthink = level.time + FRAMETIME;
|
||||||
|
self->svflags |= SVF_MONSTER;
|
||||||
|
self->s.renderfx |= RF_FRAMELERP;
|
||||||
|
self->takedamage = DAMAGE_AIM;
|
||||||
|
self->air_finished = level.time + 12;
|
||||||
|
// self->use = monster_use;//WF24 USES
|
||||||
|
self->max_health = self->health;
|
||||||
|
self->clipmask = MASK_MONSTERSOLID;
|
||||||
|
|
||||||
|
//WF - skin is set in decoy code
|
||||||
|
// self->s.skinnum = 0;
|
||||||
|
//WF
|
||||||
|
|
||||||
|
self->deadflag = DEAD_NO;
|
||||||
|
self->svflags &= ~SVF_DEADMONSTER;
|
||||||
|
|
||||||
|
if (!self->monsterinfo.checkattack)
|
||||||
|
self->monsterinfo.checkattack = M_CheckAttack;
|
||||||
|
VectorCopy (self->s.origin, self->s.old_origin);
|
||||||
|
|
||||||
|
/*//WF24 USES THIS
|
||||||
|
if (st.item)
|
||||||
|
{
|
||||||
|
self->item = FindItemByClassname (st.item);
|
||||||
|
if (!self->item)
|
||||||
|
gi.dprintf("%s at %s has bad item: %s\n", self->classname, vtos(self->s.origin), st.item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// randomize what frame they start on
|
||||||
|
if (self->monsterinfo.currentmove)
|
||||||
|
self->s.frame = self->monsterinfo.currentmove->firstframe + (rand() % (self->monsterinfo.currentmove->lastframe - self->monsterinfo.currentmove->firstframe + 1));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
*///WF24 USES THIS
|
||||||
|
}
|
||||||
|
|
||||||
|
void monster_start_go (edict_t *self)
|
||||||
|
{
|
||||||
|
vec3_t v;
|
||||||
|
|
||||||
|
if (self->health <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// check for target to combat_point and change to combattarget
|
||||||
|
if (self->target)
|
||||||
|
{
|
||||||
|
qboolean notcombat;
|
||||||
|
qboolean fixup;
|
||||||
|
edict_t *target;
|
||||||
|
|
||||||
|
target = NULL;
|
||||||
|
notcombat = false;
|
||||||
|
fixup = false;
|
||||||
|
while ((target = G_Find (target, FOFS(targetname), self->target)) != NULL)
|
||||||
|
{
|
||||||
|
if (strcmp(target->classname, "point_combat") == 0)
|
||||||
|
{
|
||||||
|
self->combattarget = self->target;
|
||||||
|
fixup = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
notcombat = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (notcombat && self->combattarget)
|
||||||
|
gi.dprintf("%s at %s has target with mixed types\n", self->classname, vtos(self->s.origin));
|
||||||
|
if (fixup)
|
||||||
|
self->target = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate combattarget
|
||||||
|
if (self->combattarget)
|
||||||
|
{
|
||||||
|
edict_t *target;
|
||||||
|
|
||||||
|
target = NULL;
|
||||||
|
while ((target = G_Find (target, FOFS(targetname), self->combattarget)) != NULL)
|
||||||
|
{
|
||||||
|
if (strcmp(target->classname, "point_combat") != 0)
|
||||||
|
{
|
||||||
|
gi.dprintf("%s at (%i %i %i) has a bad combattarget %s : %s at (%i %i %i)\n",
|
||||||
|
self->classname, (int)self->s.origin[0], (int)self->s.origin[1], (int)self->s.origin[2],
|
||||||
|
self->combattarget, target->classname, (int)target->s.origin[0], (int)target->s.origin[1],
|
||||||
|
(int)target->s.origin[2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->target)
|
||||||
|
{
|
||||||
|
self->goalentity = self->movetarget = G_PickTarget(self->target);
|
||||||
|
if (!self->movetarget)
|
||||||
|
{
|
||||||
|
gi.dprintf ("%s can't find target %s at %s\n", self->classname, self->target, vtos(self->s.origin));
|
||||||
|
self->target = NULL;
|
||||||
|
self->monsterinfo.pausetime = 100000000;
|
||||||
|
self->monsterinfo.stand (self);
|
||||||
|
}
|
||||||
|
else if (strcmp (self->movetarget->classname, "path_corner") == 0)
|
||||||
|
{
|
||||||
|
VectorSubtract (self->goalentity->s.origin, self->s.origin, v);
|
||||||
|
self->ideal_yaw = self->s.angles[YAW] = vectoyaw(v);
|
||||||
|
self->monsterinfo.walk (self);
|
||||||
|
self->target = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->goalentity = self->movetarget = NULL;
|
||||||
|
self->monsterinfo.pausetime = 100000000;
|
||||||
|
self->monsterinfo.stand (self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->monsterinfo.pausetime = 100000000;
|
||||||
|
self->monsterinfo.stand (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->think = monster_think;
|
||||||
|
self->nextthink = level.time + FRAMETIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void walkmonster_start_go (edict_t *self)
|
||||||
|
{
|
||||||
|
if (!(self->spawnflags & 2) && level.time < 1)
|
||||||
|
{
|
||||||
|
M_droptofloor (self);
|
||||||
|
|
||||||
|
if (self->groundentity)
|
||||||
|
if (!M_walkmove (self, 0, 0))
|
||||||
|
gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->yaw_speed)
|
||||||
|
self->yaw_speed = 20;
|
||||||
|
self->viewheight = 25;
|
||||||
|
|
||||||
|
monster_start_go (self);
|
||||||
|
|
||||||
|
if (self->spawnflags & 2)
|
||||||
|
monster_triggered_start (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void walkmonster_start (edict_t *self)//FIXME ACRID decoy problem
|
||||||
|
{
|
||||||
|
self->think = walkmonster_start_go;
|
||||||
|
monster_start (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void flymonster_start_go (edict_t *self)
|
||||||
|
{
|
||||||
|
if (!M_walkmove (self, 0, 0))
|
||||||
|
gi.dprintf ("%s in solid at %s\n", self->classname, vtos(self->s.origin));
|
||||||
|
|
||||||
|
if (!self->yaw_speed)
|
||||||
|
self->yaw_speed = 10;
|
||||||
|
self->viewheight = 25;
|
||||||
|
|
||||||
|
monster_start_go (self);
|
||||||
|
|
||||||
|
if (self->spawnflags & 2)
|
||||||
|
monster_triggered_start (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void flymonster_start (edict_t *self)
|
||||||
|
{
|
||||||
|
self->flags |= FL_FLY;
|
||||||
|
self->think = flymonster_start_go;
|
||||||
|
monster_start (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void swimmonster_start_go (edict_t *self)
|
||||||
|
{
|
||||||
|
if (!self->yaw_speed)
|
||||||
|
self->yaw_speed = 10;
|
||||||
|
self->viewheight = 10;
|
||||||
|
|
||||||
|
monster_start_go (self);
|
||||||
|
|
||||||
|
if (self->spawnflags & 2)
|
||||||
|
monster_triggered_start (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void swimmonster_start (edict_t *self)
|
||||||
|
{
|
||||||
|
self->flags |= FL_SWIM;
|
||||||
|
self->think = swimmonster_start_go;
|
||||||
|
monster_start (self);
|
||||||
|
}
|
|
@ -0,0 +1,844 @@
|
||||||
|
|
||||||
|
#include "g_local.h"
|
||||||
|
#include "stdlog.h" // StdLog - Mark Davies
|
||||||
|
|
||||||
|
void SetDefaultClassInfo();
|
||||||
|
void zbotFileOpen();
|
||||||
|
int ProcessConfigFile();
|
||||||
|
int LoadClassInfo(char *filename);
|
||||||
|
|
||||||
|
#define Function(f) {#f, f}
|
||||||
|
|
||||||
|
mmove_t mmove_reloc;
|
||||||
|
|
||||||
|
field_t fields[] = {
|
||||||
|
{"classname", FOFS(classname), F_LSTRING},
|
||||||
|
{"origin", FOFS(s.origin), F_VECTOR},
|
||||||
|
{"model", FOFS(model), F_LSTRING},
|
||||||
|
{"spawnflags", FOFS(spawnflags), F_INT},
|
||||||
|
{"speed", FOFS(speed), F_FLOAT},
|
||||||
|
{"accel", FOFS(accel), F_FLOAT},
|
||||||
|
{"decel", FOFS(decel), F_FLOAT},
|
||||||
|
{"target", FOFS(target), F_LSTRING},
|
||||||
|
{"targetname", FOFS(targetname), F_LSTRING},
|
||||||
|
{"pathtarget", FOFS(pathtarget), F_LSTRING},
|
||||||
|
{"deathtarget", FOFS(deathtarget), F_LSTRING},
|
||||||
|
{"killtarget", FOFS(killtarget), F_LSTRING},
|
||||||
|
{"combattarget", FOFS(combattarget), F_LSTRING},
|
||||||
|
{"message", FOFS(message), F_LSTRING},
|
||||||
|
{"team", FOFS(team), F_LSTRING},
|
||||||
|
{"wait", FOFS(wait), F_FLOAT},
|
||||||
|
{"delay", FOFS(delay), F_FLOAT},
|
||||||
|
{"random", FOFS(random), F_FLOAT},
|
||||||
|
{"move_origin", FOFS(move_origin), F_VECTOR},
|
||||||
|
{"move_angles", FOFS(move_angles), F_VECTOR},
|
||||||
|
{"style", FOFS(style), F_INT},
|
||||||
|
{"count", FOFS(count), F_INT},
|
||||||
|
{"health", FOFS(health), F_INT},
|
||||||
|
{"sounds", FOFS(sounds), F_INT},
|
||||||
|
{"light", 0, F_IGNORE},
|
||||||
|
{"dmg", FOFS(dmg), F_INT},
|
||||||
|
{"angles", FOFS(s.angles), F_VECTOR},
|
||||||
|
{"angle", FOFS(s.angles), F_ANGLEHACK},
|
||||||
|
{"mass", FOFS(mass), F_INT},
|
||||||
|
{"volume", FOFS(volume), F_FLOAT},
|
||||||
|
{"attenuation", FOFS(attenuation), F_FLOAT},
|
||||||
|
{"map", FOFS(map), F_LSTRING},
|
||||||
|
{"wf_team", FOFS(wf_team), F_INT}, //WF team ID field
|
||||||
|
{"bonustype", FOFS(bonustype), F_INT}, //WF
|
||||||
|
{"bonusvalue", FOFS(bonusvalue), F_INT}, //WF
|
||||||
|
|
||||||
|
//3.20
|
||||||
|
{"goalentity", FOFS(goalentity), F_EDICT, FFL_NOSPAWN},
|
||||||
|
{"movetarget", FOFS(movetarget), F_EDICT, FFL_NOSPAWN},
|
||||||
|
{"enemy", FOFS(enemy), F_EDICT, FFL_NOSPAWN},
|
||||||
|
{"oldenemy", FOFS(oldenemy), F_EDICT, FFL_NOSPAWN},
|
||||||
|
{"activator", FOFS(activator), F_EDICT, FFL_NOSPAWN},
|
||||||
|
{"groundentity", FOFS(groundentity), F_EDICT, FFL_NOSPAWN},
|
||||||
|
{"teamchain", FOFS(teamchain), F_EDICT, FFL_NOSPAWN},
|
||||||
|
{"teammaster", FOFS(teammaster), F_EDICT, FFL_NOSPAWN},
|
||||||
|
{"owner", FOFS(owner), F_EDICT, FFL_NOSPAWN},
|
||||||
|
{"mynoise", FOFS(mynoise), F_EDICT, FFL_NOSPAWN},
|
||||||
|
{"mynoise2", FOFS(mynoise2), F_EDICT, FFL_NOSPAWN},
|
||||||
|
{"target_ent", FOFS(target_ent), F_EDICT, FFL_NOSPAWN},
|
||||||
|
{"chain", FOFS(chain), F_EDICT, FFL_NOSPAWN},
|
||||||
|
|
||||||
|
{"prethink", FOFS(prethink), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
{"think", FOFS(think), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
{"blocked", FOFS(blocked), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
{"touch", FOFS(touch), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
{"use", FOFS(use), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
{"pain", FOFS(pain), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
{"die", FOFS(die), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
|
||||||
|
{"stand", FOFS(monsterinfo.stand), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
{"idle", FOFS(monsterinfo.idle), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
{"search", FOFS(monsterinfo.search), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
{"walk", FOFS(monsterinfo.walk), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
{"run", FOFS(monsterinfo.run), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
{"dodge", FOFS(monsterinfo.dodge), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
{"attack", FOFS(monsterinfo.attack), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
{"melee", FOFS(monsterinfo.melee), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
{"sight", FOFS(monsterinfo.sight), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
{"checkattack", FOFS(monsterinfo.checkattack), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
{"currentmove", FOFS(monsterinfo.currentmove), F_MMOVE, FFL_NOSPAWN},
|
||||||
|
|
||||||
|
{"endfunc", FOFS(moveinfo.endfunc), F_FUNCTION, FFL_NOSPAWN},
|
||||||
|
|
||||||
|
//3.20
|
||||||
|
|
||||||
|
// temp spawn vars -- only valid when the spawn function is called
|
||||||
|
{"lip", STOFS(lip), F_INT, FFL_SPAWNTEMP},
|
||||||
|
{"distance", STOFS(distance), F_INT, FFL_SPAWNTEMP},
|
||||||
|
{"height", STOFS(height), F_INT, FFL_SPAWNTEMP},
|
||||||
|
{"noise", STOFS(noise), F_LSTRING, FFL_SPAWNTEMP},
|
||||||
|
{"pausetime", STOFS(pausetime), F_FLOAT, FFL_SPAWNTEMP},
|
||||||
|
{"item", STOFS(item), F_LSTRING, FFL_SPAWNTEMP},
|
||||||
|
|
||||||
|
//need for item field in edict struct, FFL_SPAWNTEMP item will be skipped on saves
|
||||||
|
{"item", FOFS(item), F_ITEM},
|
||||||
|
|
||||||
|
{"gravity", STOFS(gravity), F_LSTRING, FFL_SPAWNTEMP},
|
||||||
|
{"sky", STOFS(sky), F_LSTRING, FFL_SPAWNTEMP},
|
||||||
|
{"skyrotate", STOFS(skyrotate), F_FLOAT, FFL_SPAWNTEMP},
|
||||||
|
{"skyaxis", STOFS(skyaxis), F_VECTOR, FFL_SPAWNTEMP},
|
||||||
|
{"minyaw", STOFS(minyaw), F_FLOAT, FFL_SPAWNTEMP},
|
||||||
|
{"maxyaw", STOFS(maxyaw), F_FLOAT, FFL_SPAWNTEMP},
|
||||||
|
{"minpitch", STOFS(minpitch), F_FLOAT, FFL_SPAWNTEMP},
|
||||||
|
{"maxpitch", STOFS(maxpitch), F_FLOAT, FFL_SPAWNTEMP},
|
||||||
|
{"nextmap", STOFS(nextmap), F_LSTRING, FFL_SPAWNTEMP},
|
||||||
|
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
field_t levelfields[] =
|
||||||
|
{
|
||||||
|
{"changemap", LLOFS(changemap), F_LSTRING},
|
||||||
|
|
||||||
|
{"sight_client", LLOFS(sight_client), F_EDICT},
|
||||||
|
{"sight_entity", LLOFS(sight_entity), F_EDICT},
|
||||||
|
{"sound_entity", LLOFS(sound_entity), F_EDICT},
|
||||||
|
{"sound2_entity", LLOFS(sound2_entity), F_EDICT},
|
||||||
|
|
||||||
|
{NULL, 0, F_INT}
|
||||||
|
};
|
||||||
|
|
||||||
|
field_t clientfields[] =
|
||||||
|
{
|
||||||
|
{"pers.weapon", CLOFS(pers.weapon), F_ITEM},
|
||||||
|
{"pers.lastweapon", CLOFS(pers.lastweapon), F_ITEM},
|
||||||
|
{"newweapon", CLOFS(newweapon), F_ITEM},
|
||||||
|
|
||||||
|
{NULL, 0, F_INT}
|
||||||
|
};
|
||||||
|
|
||||||
|
qboolean default_exec = false;//ERASER
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
InitGame
|
||||||
|
|
||||||
|
This will be called when the dll is first loaded, which
|
||||||
|
only happens when a new game is started or a save game
|
||||||
|
is loaded.
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
void InitGame (void)
|
||||||
|
{
|
||||||
|
int retval;
|
||||||
|
// cvar_t *tempvar;
|
||||||
|
|
||||||
|
gi.dprintf ("==== InitGame ====\n");
|
||||||
|
|
||||||
|
gun_x = gi.cvar ("gun_x", "0", 0);
|
||||||
|
gun_y = gi.cvar ("gun_y", "0", 0);
|
||||||
|
gun_z = gi.cvar ("gun_z", "0", 0);
|
||||||
|
|
||||||
|
//WF - Set up classes
|
||||||
|
TOTALDROPPEDAMMO = 0;
|
||||||
|
TOTALWORLDFLAMES = 0;
|
||||||
|
//WF
|
||||||
|
|
||||||
|
//FIXME: sv_ prefix is wrong for these
|
||||||
|
sv_rollspeed = gi.cvar ("sv_rollspeed", "200", 0);
|
||||||
|
sv_rollangle = gi.cvar ("sv_rollangle", "2", 0);
|
||||||
|
sv_maxvelocity = gi.cvar ("sv_maxvelocity", "2000", 0);
|
||||||
|
sv_gravity = gi.cvar ("sv_gravity", "800", 0);
|
||||||
|
|
||||||
|
// noset vars
|
||||||
|
dedicated = gi.cvar ("dedicated", "0", CVAR_NOSET);
|
||||||
|
|
||||||
|
// latched vars
|
||||||
|
sv_cheats = gi.cvar ("cheats", "0", CVAR_SERVERINFO|CVAR_LATCH);
|
||||||
|
gi.cvar ("gamename", GAMEVERSION , CVAR_SERVERINFO | CVAR_LATCH);
|
||||||
|
gi.cvar ("gamedate", __DATE__ , CVAR_SERVERINFO | CVAR_LATCH);
|
||||||
|
|
||||||
|
maxclients = gi.cvar ("maxclients", "4", CVAR_SERVERINFO | CVAR_LATCH);
|
||||||
|
deathmatch = gi.cvar ("deathmatch", "0", CVAR_LATCH);
|
||||||
|
coop = gi.cvar ("coop", "0", CVAR_LATCH);
|
||||||
|
skill = gi.cvar ("skill", "1", CVAR_LATCH);
|
||||||
|
maxentities = gi.cvar ("maxentities", "1024", CVAR_LATCH);
|
||||||
|
|
||||||
|
//ZOID
|
||||||
|
//This game.dll only supports deathmatch
|
||||||
|
if (!deathmatch->value) {
|
||||||
|
gi.dprintf("Forcing deathmatch.");
|
||||||
|
gi.cvar_set("deathmatch", "1");
|
||||||
|
}
|
||||||
|
//force coop off
|
||||||
|
if (coop->value)
|
||||||
|
gi.cvar_set("coop", "0");
|
||||||
|
//ZOID
|
||||||
|
|
||||||
|
// change anytime vars
|
||||||
|
dmflags = gi.cvar ("dmflags", "0", CVAR_SERVERINFO|CVAR_ARCHIVE);//ERASER ADDED |CVAR_ARCHIVE
|
||||||
|
// dmflags = gi.cvar ("dmflags", "0", CVAR_SERVERINFO);
|
||||||
|
fraglimit = gi.cvar ("fraglimit", "0", CVAR_SERVERINFO);
|
||||||
|
timelimit = gi.cvar ("timelimit", "0", CVAR_SERVERINFO);
|
||||||
|
|
||||||
|
needpass = gi.cvar ("needpass", "0", CVAR_SERVERINFO);
|
||||||
|
|
||||||
|
//WF
|
||||||
|
gamedir = gi.cvar ("gamedir", "", CVAR_SERVERINFO);
|
||||||
|
wfflags = gi.cvar ("wfflags", "0", CVAR_SERVERINFO);
|
||||||
|
wfconfig = gi.cvar ("wfconfig", "wfserver.ini", CVAR_SERVERINFO);
|
||||||
|
filterban = gi.cvar ("filterban", "1", CVAR_SERVERINFO);
|
||||||
|
//WF
|
||||||
|
|
||||||
|
//ZOID
|
||||||
|
capturelimit = gi.cvar ("capturelimit", "0", CVAR_SERVERINFO);
|
||||||
|
//ZOID
|
||||||
|
wfpassword = gi.cvar ("password", "", CVAR_USERINFO);
|
||||||
|
|
||||||
|
g_select_empty = gi.cvar ("g_select_empty", "0", CVAR_ARCHIVE);
|
||||||
|
|
||||||
|
run_pitch = gi.cvar ("run_pitch", "0.002", 0);
|
||||||
|
run_roll = gi.cvar ("run_roll", "0.005", 0);
|
||||||
|
bob_up = gi.cvar ("bob_up", "0.005", 0);
|
||||||
|
bob_pitch = gi.cvar ("bob_pitch", "0.002", 0);
|
||||||
|
bob_roll = gi.cvar ("bob_roll", "0.002", 0);
|
||||||
|
|
||||||
|
//ERASER START
|
||||||
|
// bot commands
|
||||||
|
bot_num = gi.cvar ("bot_num", "0", 0);
|
||||||
|
bot_name = gi.cvar ("bot_name", "", 0);
|
||||||
|
bot_allow_client_commands = gi.cvar ("bot_allow_client_commands", "0", CVAR_ARCHIVE);
|
||||||
|
bot_free_clients = gi.cvar ("bot_free_clients", "0", CVAR_ARCHIVE);
|
||||||
|
bot_debug = gi.cvar ("bot_debug", "0", 0);
|
||||||
|
bot_show_connect_info = gi.cvar ("bot_show_connect_info", "1", CVAR_ARCHIVE);
|
||||||
|
bot_calc_nodes = gi.cvar ("bot_calc_nodes", "1", 0);
|
||||||
|
bot_debug_nodes = gi.cvar ("bot_debug_nodes", "0", 0);
|
||||||
|
bot_auto_skill = gi.cvar ("bot_auto_skill", "0", CVAR_ARCHIVE);
|
||||||
|
bot_drop = gi.cvar ("bot_drop", "", 0);
|
||||||
|
bot_chat = gi.cvar ("bot_chat", "1", CVAR_ARCHIVE);
|
||||||
|
bot_optimize = gi.cvar ("bot_optimize", "1200", 0);
|
||||||
|
bot_tarzan = gi.cvar ("bot_tarzan", "0", CVAR_ARCHIVE);
|
||||||
|
bot_melee = gi.cvar ("bot_melee", "0", CVAR_ARCHIVE);//acrid added
|
||||||
|
players_per_team = gi.cvar ("players_per_team", "4", CVAR_LATCH|CVAR_ARCHIVE);
|
||||||
|
addteam = gi.cvar ("addteam", "", 0);
|
||||||
|
teamplay = gi.cvar ("teamplay", "0", CVAR_LATCH|CVAR_ARCHIVE);
|
||||||
|
ctf_auto_teams = gi.cvar ("ctf_auto_teams", "0", 0);
|
||||||
|
ctf_special_teams = gi.cvar ("ctf_special_teams", "0", 0); //~~JLH
|
||||||
|
ctf_humanonly_teams = gi.cvar ("ctf_humanonly_teams", "0", 0); //~~JLH
|
||||||
|
// Diable ctf_auto_teams if ctf_special_teams is enabled //~~JLH
|
||||||
|
if ( ctf_auto_teams->value > 0 && ctf_special_teams->value > 0 ) // ~~JLH
|
||||||
|
gi.cvar_set ("ctf_auto_teams", "0");
|
||||||
|
// grapple = gi.cvar ("grapple", "0", CVAR_LATCH);//FIXME ACRID????
|
||||||
|
|
||||||
|
//ERASER END
|
||||||
|
|
||||||
|
// items
|
||||||
|
InitItems ();
|
||||||
|
|
||||||
|
Com_sprintf (game.helpmessage1, sizeof(game.helpmessage1), "");
|
||||||
|
Com_sprintf (game.helpmessage2, sizeof(game.helpmessage2), "");
|
||||||
|
|
||||||
|
// initialize all entities for this game
|
||||||
|
game.maxentities = maxentities->value;
|
||||||
|
g_edicts = gi.TagMalloc (game.maxentities * sizeof(g_edicts[0]), TAG_GAME);
|
||||||
|
globals.edicts = g_edicts;
|
||||||
|
globals.max_edicts = game.maxentities;
|
||||||
|
|
||||||
|
// initialize all clients for this game
|
||||||
|
game.maxclients = maxclients->value;
|
||||||
|
game.clients = gi.TagMalloc (game.maxclients * sizeof(game.clients[0]), TAG_GAME);
|
||||||
|
globals.num_edicts = game.maxclients+1;
|
||||||
|
|
||||||
|
//ZOID
|
||||||
|
CTFInit();
|
||||||
|
//ZOID
|
||||||
|
|
||||||
|
//WF
|
||||||
|
//Read IP ban list
|
||||||
|
ReadBans();
|
||||||
|
|
||||||
|
ProcessConfigFile();
|
||||||
|
|
||||||
|
//Now set the server variables we want displayed
|
||||||
|
if ((int)wfflags->value & WF_ZBOT_DETECT)
|
||||||
|
{
|
||||||
|
gi.cvar ("ZBotCheck", "1", CVAR_SERVERINFO);
|
||||||
|
}
|
||||||
|
gi.cvar ("mock", "1", CVAR_SERVERINFO);
|
||||||
|
gi.cvar ("version", WF_VERSION, CVAR_SERVERINFO);
|
||||||
|
// gi.cvar_set("features", "ZbotKick");
|
||||||
|
|
||||||
|
//Was there a class name defined?
|
||||||
|
if (wf_game.classdef_name[0] == '\0') //nope
|
||||||
|
{
|
||||||
|
retval = LoadClassInfo("team10.class");
|
||||||
|
|
||||||
|
if (retval == 0)
|
||||||
|
{
|
||||||
|
gi.dprintf("ERROR: Could not load class file in quake2 directory\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zbotFileOpen();
|
||||||
|
//WF
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
|
void WriteField1 (FILE *f, field_t *field, byte *base)
|
||||||
|
{
|
||||||
|
void *p;
|
||||||
|
int len;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
if (field->flags & FFL_SPAWNTEMP)
|
||||||
|
return;
|
||||||
|
|
||||||
|
p = (void *)(base + field->ofs);
|
||||||
|
switch (field->type)
|
||||||
|
{
|
||||||
|
case F_INT:
|
||||||
|
case F_FLOAT:
|
||||||
|
case F_ANGLEHACK:
|
||||||
|
case F_VECTOR:
|
||||||
|
case F_IGNORE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case F_LSTRING:
|
||||||
|
case F_GSTRING:
|
||||||
|
if ( *(char **)p )
|
||||||
|
len = strlen(*(char **)p) + 1;
|
||||||
|
else
|
||||||
|
len = 0;
|
||||||
|
*(int *)p = len;
|
||||||
|
break;
|
||||||
|
case F_EDICT:
|
||||||
|
if ( *(edict_t **)p == NULL)
|
||||||
|
index = -1;
|
||||||
|
else
|
||||||
|
index = *(edict_t **)p - g_edicts;
|
||||||
|
*(int *)p = index;
|
||||||
|
break;
|
||||||
|
case F_CLIENT:
|
||||||
|
if ( *(gclient_t **)p == NULL)
|
||||||
|
index = -1;
|
||||||
|
else
|
||||||
|
index = *(gclient_t **)p - game.clients;
|
||||||
|
*(int *)p = index;
|
||||||
|
break;
|
||||||
|
case F_ITEM:
|
||||||
|
if ( *(edict_t **)p == NULL)
|
||||||
|
index = -1;
|
||||||
|
else
|
||||||
|
index = *(gitem_t **)p - itemlist;
|
||||||
|
*(int *)p = index;
|
||||||
|
break;
|
||||||
|
|
||||||
|
//relative to code segment
|
||||||
|
case F_FUNCTION:
|
||||||
|
if (*(byte **)p == NULL)
|
||||||
|
index = 0;
|
||||||
|
else
|
||||||
|
index = *(byte **)p - ((byte *)InitGame);
|
||||||
|
*(int *)p = index;
|
||||||
|
break;
|
||||||
|
|
||||||
|
//relative to data segment
|
||||||
|
case F_MMOVE:
|
||||||
|
if (*(byte **)p == NULL)
|
||||||
|
index = 0;
|
||||||
|
else
|
||||||
|
index = *(byte **)p - (byte *)&mmove_reloc;
|
||||||
|
*(int *)p = index;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
gi.error ("WriteEdict: unknown field type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteField2 (FILE *f, field_t *field, byte *base)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
void *p;
|
||||||
|
|
||||||
|
if (field->flags & FFL_SPAWNTEMP)
|
||||||
|
return;
|
||||||
|
|
||||||
|
p = (void *)(base + field->ofs);
|
||||||
|
switch (field->type)
|
||||||
|
{
|
||||||
|
case F_LSTRING:
|
||||||
|
if ( *(char **)p )
|
||||||
|
{
|
||||||
|
len = strlen(*(char **)p) + 1;
|
||||||
|
fwrite (*(char **)p, len, 1, f);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadField (FILE *f, field_t *field, byte *base)
|
||||||
|
{
|
||||||
|
void *p;
|
||||||
|
int len;
|
||||||
|
int index;
|
||||||
|
|
||||||
|
if (field->flags & FFL_SPAWNTEMP)
|
||||||
|
return;
|
||||||
|
|
||||||
|
p = (void *)(base + field->ofs);
|
||||||
|
switch (field->type)
|
||||||
|
{
|
||||||
|
case F_INT:
|
||||||
|
case F_FLOAT:
|
||||||
|
case F_ANGLEHACK:
|
||||||
|
case F_VECTOR:
|
||||||
|
case F_IGNORE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case F_LSTRING:
|
||||||
|
len = *(int *)p;
|
||||||
|
if (!len)
|
||||||
|
*(char **)p = NULL;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*(char **)p = gi.TagMalloc (len, TAG_LEVEL);
|
||||||
|
fread (*(char **)p, len, 1, f);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case F_EDICT:
|
||||||
|
index = *(int *)p;
|
||||||
|
if ( index == -1 )
|
||||||
|
*(edict_t **)p = NULL;
|
||||||
|
else
|
||||||
|
*(edict_t **)p = &g_edicts[index];
|
||||||
|
break;
|
||||||
|
case F_CLIENT:
|
||||||
|
index = *(int *)p;
|
||||||
|
if ( index == -1 )
|
||||||
|
*(gclient_t **)p = NULL;
|
||||||
|
else
|
||||||
|
*(gclient_t **)p = &game.clients[index];
|
||||||
|
break;
|
||||||
|
case F_ITEM:
|
||||||
|
index = *(int *)p;
|
||||||
|
if ( index == -1 )
|
||||||
|
*(gitem_t **)p = NULL;
|
||||||
|
else
|
||||||
|
*(gitem_t **)p = &itemlist[index];
|
||||||
|
break;
|
||||||
|
|
||||||
|
//relative to code segment
|
||||||
|
case F_FUNCTION:
|
||||||
|
index = *(int *)p;
|
||||||
|
if ( index == 0 )
|
||||||
|
*(byte **)p = NULL;
|
||||||
|
else
|
||||||
|
*(byte **)p = ((byte *)InitGame) + index;
|
||||||
|
break;
|
||||||
|
|
||||||
|
//relative to data segment
|
||||||
|
case F_MMOVE:
|
||||||
|
index = *(int *)p;
|
||||||
|
if (index == 0)
|
||||||
|
*(byte **)p = NULL;
|
||||||
|
else
|
||||||
|
*(byte **)p = (byte *)&mmove_reloc + index;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
gi.error ("ReadEdict: unknown field type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=========================================================
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============
|
||||||
|
WriteClient
|
||||||
|
|
||||||
|
All pointer variables (except function pointers) must be handled specially.
|
||||||
|
==============
|
||||||
|
*/
|
||||||
|
void WriteClient (FILE *f, gclient_t *client)
|
||||||
|
{
|
||||||
|
field_t *field;
|
||||||
|
gclient_t temp;
|
||||||
|
|
||||||
|
// all of the ints, floats, and vectors stay as they are
|
||||||
|
temp = *client;
|
||||||
|
|
||||||
|
// change the pointers to lengths or indexes
|
||||||
|
for (field=clientfields ; field->name ; field++)
|
||||||
|
{
|
||||||
|
WriteField1 (f, field, (byte *)&temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the block
|
||||||
|
fwrite (&temp, sizeof(temp), 1, f);
|
||||||
|
|
||||||
|
// now write any allocated data following the edict
|
||||||
|
for (field=clientfields ; field->name ; field++)
|
||||||
|
{
|
||||||
|
WriteField2 (f, field, (byte *)client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============
|
||||||
|
ReadClient
|
||||||
|
|
||||||
|
All pointer variables (except function pointers) must be handled specially.
|
||||||
|
==============
|
||||||
|
*/
|
||||||
|
void ReadClient (FILE *f, gclient_t *client)
|
||||||
|
{
|
||||||
|
field_t *field;
|
||||||
|
|
||||||
|
fread (client, sizeof(*client), 1, f);
|
||||||
|
|
||||||
|
for (field=clientfields ; field->name ; field++)
|
||||||
|
{
|
||||||
|
ReadField (f, field, (byte *)client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
WriteGame
|
||||||
|
|
||||||
|
This will be called whenever the game goes to a new level,
|
||||||
|
and when the user explicitly saves the game.
|
||||||
|
|
||||||
|
Game information include cross level data, like multi level
|
||||||
|
triggers, help computer info, and all client states.
|
||||||
|
|
||||||
|
A single player death will automatically restore from the
|
||||||
|
last save position.
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
void WriteGame (char *filename, qboolean autosave)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
int i;
|
||||||
|
char str[16];
|
||||||
|
|
||||||
|
if (!autosave)
|
||||||
|
SaveClientData ();
|
||||||
|
|
||||||
|
f = fopen (filename, "wb");
|
||||||
|
if (!f)
|
||||||
|
gi.error ("Couldn't open %s", filename);
|
||||||
|
|
||||||
|
memset (str, 0, sizeof(str));
|
||||||
|
strcpy (str, __DATE__);
|
||||||
|
fwrite (str, sizeof(str), 1, f);
|
||||||
|
|
||||||
|
game.autosaved = autosave;
|
||||||
|
fwrite (&game, sizeof(game), 1, f);
|
||||||
|
game.autosaved = false;
|
||||||
|
|
||||||
|
for (i=0 ; i<game.maxclients ; i++)
|
||||||
|
WriteClient (f, &game.clients[i]);
|
||||||
|
|
||||||
|
fclose (f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReadGame (char *filename)
|
||||||
|
{
|
||||||
|
FILE *f;
|
||||||
|
int i;
|
||||||
|
char str[16];
|
||||||
|
|
||||||
|
gi.FreeTags (TAG_GAME);
|
||||||
|
|
||||||
|
f = fopen (filename, "rb");
|
||||||
|
if (!f)
|
||||||
|
gi.error ("Couldn't open %s", filename);
|
||||||
|
|
||||||
|
fread (str, sizeof(str), 1, f);
|
||||||
|
if (strcmp (str, __DATE__))
|
||||||
|
{
|
||||||
|
fclose (f);
|
||||||
|
gi.error ("Savegame from an older version.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
g_edicts = gi.TagMalloc (game.maxentities * sizeof(g_edicts[0]), TAG_GAME);
|
||||||
|
globals.edicts = g_edicts;
|
||||||
|
|
||||||
|
fread (&game, sizeof(game), 1, f);
|
||||||
|
game.clients = gi.TagMalloc (game.maxclients * sizeof(game.clients[0]), TAG_GAME);
|
||||||
|
for (i=0 ; i<game.maxclients ; i++)
|
||||||
|
ReadClient (f, &game.clients[i]);
|
||||||
|
|
||||||
|
fclose (f);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============
|
||||||
|
WriteEdict
|
||||||
|
|
||||||
|
All pointer variables (except function pointers) must be handled specially.
|
||||||
|
==============
|
||||||
|
*/
|
||||||
|
void WriteEdict (FILE *f, edict_t *ent)
|
||||||
|
{
|
||||||
|
field_t *field;
|
||||||
|
edict_t temp;
|
||||||
|
|
||||||
|
// all of the ints, floats, and vectors stay as they are
|
||||||
|
temp = *ent;
|
||||||
|
|
||||||
|
// change the pointers to lengths or indexes
|
||||||
|
for (field=fields ; field->name ; field++)
|
||||||
|
{
|
||||||
|
WriteField1 (f, field, (byte *)&temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the block
|
||||||
|
fwrite (&temp, sizeof(temp), 1, f);
|
||||||
|
|
||||||
|
// now write any allocated data following the edict
|
||||||
|
for (field=fields ; field->name ; field++)
|
||||||
|
{
|
||||||
|
WriteField2 (f, field, (byte *)ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============
|
||||||
|
WriteLevelLocals
|
||||||
|
|
||||||
|
All pointer variables (except function pointers) must be handled specially.
|
||||||
|
==============
|
||||||
|
*/
|
||||||
|
void WriteLevelLocals (FILE *f)
|
||||||
|
{
|
||||||
|
field_t *field;
|
||||||
|
level_locals_t temp;
|
||||||
|
|
||||||
|
// all of the ints, floats, and vectors stay as they are
|
||||||
|
temp = level;
|
||||||
|
|
||||||
|
// change the pointers to lengths or indexes
|
||||||
|
for (field=levelfields ; field->name ; field++)
|
||||||
|
{
|
||||||
|
WriteField1 (f, field, (byte *)&temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the block
|
||||||
|
fwrite (&temp, sizeof(temp), 1, f);
|
||||||
|
|
||||||
|
// now write any allocated data following the edict
|
||||||
|
for (field=levelfields ; field->name ; field++)
|
||||||
|
{
|
||||||
|
WriteField2 (f, field, (byte *)&level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============
|
||||||
|
ReadEdict
|
||||||
|
|
||||||
|
All pointer variables (except function pointers) must be handled specially.
|
||||||
|
==============
|
||||||
|
*/
|
||||||
|
void ReadEdict (FILE *f, edict_t *ent)
|
||||||
|
{
|
||||||
|
field_t *field;
|
||||||
|
|
||||||
|
fread (ent, sizeof(*ent), 1, f);
|
||||||
|
|
||||||
|
for (field=fields ; field->name ; field++)
|
||||||
|
{
|
||||||
|
ReadField (f, field, (byte *)ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============
|
||||||
|
ReadLevelLocals
|
||||||
|
|
||||||
|
All pointer variables (except function pointers) must be handled specially.
|
||||||
|
==============
|
||||||
|
*/
|
||||||
|
void ReadLevelLocals (FILE *f)
|
||||||
|
{
|
||||||
|
field_t *field;
|
||||||
|
|
||||||
|
fread (&level, sizeof(level), 1, f);
|
||||||
|
|
||||||
|
for (field=levelfields ; field->name ; field++)
|
||||||
|
{
|
||||||
|
ReadField (f, field, (byte *)&level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
WriteLevel
|
||||||
|
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
void WriteLevel (char *filename)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
edict_t *ent;
|
||||||
|
FILE *f;
|
||||||
|
void *base;
|
||||||
|
|
||||||
|
f = fopen (filename, "wb");
|
||||||
|
if (!f)
|
||||||
|
gi.error ("Couldn't open %s", filename);
|
||||||
|
|
||||||
|
// write out edict size for checking
|
||||||
|
i = sizeof(edict_t);
|
||||||
|
fwrite (&i, sizeof(i), 1, f);
|
||||||
|
|
||||||
|
// write out a function pointer for checking
|
||||||
|
base = (void *)InitGame;
|
||||||
|
fwrite (&base, sizeof(base), 1, f);
|
||||||
|
|
||||||
|
// write out level_locals_t
|
||||||
|
WriteLevelLocals (f);
|
||||||
|
|
||||||
|
// write out all the entities
|
||||||
|
for (i=0 ; i<globals.num_edicts ; i++)
|
||||||
|
{
|
||||||
|
ent = &g_edicts[i];
|
||||||
|
if (!ent->inuse)
|
||||||
|
continue;
|
||||||
|
fwrite (&i, sizeof(i), 1, f);
|
||||||
|
WriteEdict (f, ent);
|
||||||
|
}
|
||||||
|
i = -1;
|
||||||
|
fwrite (&i, sizeof(i), 1, f);
|
||||||
|
|
||||||
|
fclose (f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
ReadLevel
|
||||||
|
|
||||||
|
SpawnEntities will allready have been called on the
|
||||||
|
level the same way it was when the level was saved.
|
||||||
|
|
||||||
|
That is necessary to get the baselines
|
||||||
|
set up identically.
|
||||||
|
|
||||||
|
The server will have cleared all of the world links before
|
||||||
|
calling ReadLevel.
|
||||||
|
|
||||||
|
No clients are connected yet.
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
void ReadLevel (char *filename)
|
||||||
|
{
|
||||||
|
int entnum;
|
||||||
|
FILE *f;
|
||||||
|
int i;
|
||||||
|
void *base;
|
||||||
|
edict_t *ent;
|
||||||
|
|
||||||
|
f = fopen (filename, "rb");
|
||||||
|
if (!f)
|
||||||
|
gi.error ("Couldn't open %s", filename);
|
||||||
|
|
||||||
|
// free any dynamic memory allocated by loading the level
|
||||||
|
// base state
|
||||||
|
gi.FreeTags (TAG_LEVEL);
|
||||||
|
|
||||||
|
// wipe all the entities
|
||||||
|
memset (g_edicts, 0, game.maxentities*sizeof(g_edicts[0]));
|
||||||
|
globals.num_edicts = maxclients->value+1;
|
||||||
|
|
||||||
|
// check edict size
|
||||||
|
fread (&i, sizeof(i), 1, f);
|
||||||
|
if (i != sizeof(edict_t))
|
||||||
|
{
|
||||||
|
fclose (f);
|
||||||
|
gi.error ("ReadLevel: mismatched edict size");
|
||||||
|
}
|
||||||
|
|
||||||
|
// check function pointer base address
|
||||||
|
fread (&base, sizeof(base), 1, f);
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (base != (void *)InitGame)
|
||||||
|
{
|
||||||
|
fclose (f);
|
||||||
|
gi.error ("ReadLevel: function pointers have moved");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
gi.dprintf("Function offsets %d\n", ((byte *)base) - ((byte *)InitGame));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// load the level locals
|
||||||
|
ReadLevelLocals (f);
|
||||||
|
|
||||||
|
// load all the entities
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (fread (&entnum, sizeof(entnum), 1, f) != 1)
|
||||||
|
{
|
||||||
|
fclose (f);
|
||||||
|
gi.error ("ReadLevel: failed to read entnum");
|
||||||
|
}
|
||||||
|
if (entnum == -1)
|
||||||
|
break;
|
||||||
|
if (entnum >= globals.num_edicts)
|
||||||
|
globals.num_edicts = entnum+1;
|
||||||
|
|
||||||
|
ent = &g_edicts[entnum];
|
||||||
|
ReadEdict (f, ent);
|
||||||
|
|
||||||
|
// let the server rebuild world links for this ent
|
||||||
|
memset (&ent->area, 0, sizeof(ent->area));
|
||||||
|
gi.linkentity (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose (f);
|
||||||
|
|
||||||
|
// mark all clients as unconnected
|
||||||
|
for (i=0 ; i<maxclients->value ; i++)
|
||||||
|
{
|
||||||
|
ent = &g_edicts[i+1];
|
||||||
|
ent->client = game.clients + i;
|
||||||
|
ent->client->pers.connected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// do any load time things at this point
|
||||||
|
for (i=0 ; i<globals.num_edicts ; i++)
|
||||||
|
{
|
||||||
|
ent = &g_edicts[i];
|
||||||
|
|
||||||
|
if (!ent->inuse)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// fire any cross-level triggers
|
||||||
|
if (ent->classname)
|
||||||
|
if (strcmp(ent->classname, "target_crosslevel_target") == 0)
|
||||||
|
ent->nextthink = level.time + ent->delay;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,261 @@
|
||||||
|
|
||||||
|
#include "g_local.h"
|
||||||
|
#include "bot_procs.h"//ERASER
|
||||||
|
char *getClassName (gclient_t *c);
|
||||||
|
//WF34 S
|
||||||
|
void Cmd_Classdef_f ();
|
||||||
|
//int SaveClassInfo();
|
||||||
|
void Adm_Test(char *cmd);
|
||||||
|
void CTFResetFlags(void);
|
||||||
|
//WF34 E
|
||||||
|
|
||||||
|
//Show all the players
|
||||||
|
void sv_ShowPlayers()
|
||||||
|
{
|
||||||
|
// edict_t *cl_ent;
|
||||||
|
// gclient_t *cl;
|
||||||
|
edict_t *e;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
//Show red players first
|
||||||
|
for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++)
|
||||||
|
{
|
||||||
|
if (!e->inuse)
|
||||||
|
continue;
|
||||||
|
if (!e->client)
|
||||||
|
continue;
|
||||||
|
//getClassName(e->client, classname);
|
||||||
|
if (e->client->resp.ctf_team == CTF_TEAM1)
|
||||||
|
{
|
||||||
|
gi.dprintf ("Red %3d %8s %s\n",
|
||||||
|
e->client->resp.score,
|
||||||
|
getClassName(e->client),
|
||||||
|
e->client->pers.netname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Next blue
|
||||||
|
for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++)
|
||||||
|
{
|
||||||
|
if (!e->inuse)
|
||||||
|
continue;
|
||||||
|
if (!e->client)
|
||||||
|
continue;
|
||||||
|
//getClassName(e->client, classname);
|
||||||
|
if (e->client->resp.ctf_team == CTF_TEAM2)
|
||||||
|
{
|
||||||
|
gi.dprintf ("Blue %3d %8s %s\n",
|
||||||
|
e->client->resp.score,
|
||||||
|
getClassName(e->client),
|
||||||
|
e->client->pers.netname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Then observers
|
||||||
|
for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++)
|
||||||
|
{
|
||||||
|
if (!e->inuse)
|
||||||
|
continue;
|
||||||
|
if (!e->client)
|
||||||
|
continue;
|
||||||
|
//getClassName(e->client, classname);
|
||||||
|
if (e->client->resp.ctf_team <= 0)
|
||||||
|
{
|
||||||
|
gi.dprintf ("Obsv %3d %8s %s\n",
|
||||||
|
e->client->resp.score,
|
||||||
|
getClassName(e->client),
|
||||||
|
e->client->pers.netname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//ERASER START
|
||||||
|
void Svcmd_Test_f (void)
|
||||||
|
{
|
||||||
|
safe_cprintf (NULL, PRINT_HIGH, "Svcmd_Test_f()\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Svcmd_Bots_f (void)
|
||||||
|
{
|
||||||
|
int i=2, j, len;
|
||||||
|
char name[128];
|
||||||
|
|
||||||
|
while (i < gi.argc())
|
||||||
|
{
|
||||||
|
strcpy(name, gi.argv(i));
|
||||||
|
len = strlen(name);
|
||||||
|
|
||||||
|
// convert '~' to ' '
|
||||||
|
for (j=0; j<len; j++)
|
||||||
|
if (name[j] == '~')
|
||||||
|
name[j] = ' ';
|
||||||
|
|
||||||
|
spawn_bot(name);
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Svcmd_Teams_f (void)
|
||||||
|
{
|
||||||
|
int arg=2, i;
|
||||||
|
char team[128];
|
||||||
|
|
||||||
|
while (arg < gi.argc())
|
||||||
|
{
|
||||||
|
strcpy(team, gi.argv(arg));
|
||||||
|
|
||||||
|
i=0;
|
||||||
|
while (i<MAX_TEAMS)
|
||||||
|
{
|
||||||
|
if (!bot_teams[i])
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (!_stricmp(bot_teams[i]->teamname, team) || !_stricmp(bot_teams[i]->abbrev, team))
|
||||||
|
#else
|
||||||
|
if (!strcasecmp(bot_teams[i]->teamname, team) || !strcasecmp(bot_teams[i]->abbrev, team))
|
||||||
|
#endif
|
||||||
|
{ // found the team, so add the bots
|
||||||
|
bot_teams[i]->ingame = true; // bots will be added automatically (below)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int force_team = CTF_NOTEAM;
|
||||||
|
|
||||||
|
void Svcmd_Blueteam_f (void)
|
||||||
|
{
|
||||||
|
int i=2;
|
||||||
|
|
||||||
|
force_team = CTF_TEAM2;
|
||||||
|
while (i < gi.argc())
|
||||||
|
{
|
||||||
|
//gi.dprintf("Spawning: \"%s\"\n", gi.argv(i));
|
||||||
|
spawn_bot(gi.argv(i));
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
force_team = CTF_NOTEAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Svcmd_Redteam_f (void)
|
||||||
|
{
|
||||||
|
int i=2;
|
||||||
|
|
||||||
|
force_team = CTF_TEAM1;
|
||||||
|
while (i < gi.argc())
|
||||||
|
{
|
||||||
|
spawn_bot(gi.argv(i));
|
||||||
|
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
force_team = CTF_NOTEAM;
|
||||||
|
}
|
||||||
|
//ERASER END
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
ServerCommand
|
||||||
|
|
||||||
|
ServerCommand will be called when an "sv" command is issued.
|
||||||
|
The game can issue gi.argc() / gi.argv() commands to get the rest
|
||||||
|
of the parameters
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
void SV_WFFlags_f ();//WF24
|
||||||
|
|
||||||
|
void ServerCommand (void)
|
||||||
|
{
|
||||||
|
char *cmd;
|
||||||
|
char *arg1;
|
||||||
|
|
||||||
|
cmd = gi.argv(1);
|
||||||
|
arg1 = gi.argv(2);//WF34
|
||||||
|
//WF24
|
||||||
|
if (Q_stricmp (cmd, "showplayers") == 0)
|
||||||
|
{
|
||||||
|
sv_ShowPlayers ();
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (Q_stricmp (cmd, "maplist") == 0)
|
||||||
|
{
|
||||||
|
Cmd_Maplist_f (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (Q_stricmp (cmd, "classdef") == 0)//WF34
|
||||||
|
{
|
||||||
|
Cmd_Classdef_f (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (Q_stricmp (cmd, "wfflags") == 0)
|
||||||
|
{
|
||||||
|
SV_WFFlags_f ();
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (Q_stricmp (cmd, "resetflags") == 0)//WF34
|
||||||
|
{
|
||||||
|
CTFResetFlags();
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (Q_stricmp (cmd, "addip") == 0) //WF34
|
||||||
|
{
|
||||||
|
Adm_Ban(arg1);
|
||||||
|
// Adm_KickBan(edict_t *ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (Q_stricmp (cmd, "testip") == 0)//WF34
|
||||||
|
{
|
||||||
|
Adm_Test(arg1);
|
||||||
|
// Adm_KickBan(edict_t *ent);
|
||||||
|
}
|
||||||
|
else if (Q_stricmp (cmd, "listip") == 0)//WF34
|
||||||
|
{
|
||||||
|
Adm_Bans(arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (Q_stricmp (cmd, "writeip") == 0)//WF34
|
||||||
|
{
|
||||||
|
WriteBans();
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (Q_stricmp (cmd, "removeip") == 0)//WF34
|
||||||
|
{
|
||||||
|
Adm_Unban(arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (Q_stricmp (cmd, "debug") == 0)//WF34
|
||||||
|
{
|
||||||
|
wfdebug = 1;
|
||||||
|
gi.dprintf("WF Debug On\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (Q_stricmp (cmd, "nodebug") == 0)//WF34
|
||||||
|
{
|
||||||
|
wfdebug = 0;
|
||||||
|
gi.dprintf("WF Debug Off\n");
|
||||||
|
}
|
||||||
|
//ERASER
|
||||||
|
else if (Q_stricmp (cmd, "test") == 0)
|
||||||
|
Svcmd_Test_f ();
|
||||||
|
else if (Q_stricmp (cmd, "bots") == 0)
|
||||||
|
Svcmd_Bots_f ();
|
||||||
|
else if (Q_stricmp (cmd, "teams") == 0)
|
||||||
|
Svcmd_Teams_f ();
|
||||||
|
else if (Q_stricmp (cmd, "bluebots") == 0)
|
||||||
|
Svcmd_Blueteam_f ();
|
||||||
|
else if (Q_stricmp (cmd, "redbots") == 0)
|
||||||
|
Svcmd_Redteam_f ();
|
||||||
|
//ERASER END
|
||||||
|
else
|
||||||
|
gi.dprintf ("Unknown server command \"%s\"\n", cmd);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,959 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
|
||||||
|
/*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
|
||||||
|
*/
|
||||||
|
void laser_cleanup(edict_t *self);//WF34
|
||||||
|
|
||||||
|
void Use_Target_Tent (edict_t *ent, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (ent->style);
|
||||||
|
gi.WritePosition (ent->s.origin);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_target_temp_entity (edict_t *ent)
|
||||||
|
{
|
||||||
|
ent->use = Use_Target_Tent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==========================================================
|
||||||
|
|
||||||
|
//==========================================================
|
||||||
|
|
||||||
|
/*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 allways atten 3 / vol 1, and the use function toggles it on/off.
|
||||||
|
Multiple identical looping sounds will just increase volume without any speed cost.
|
||||||
|
*/
|
||||||
|
void Use_Target_Speaker (edict_t *ent, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
int chan;
|
||||||
|
|
||||||
|
if (ent->spawnflags & 3)
|
||||||
|
{ // looping sound toggles
|
||||||
|
if (ent->s.sound)
|
||||||
|
ent->s.sound = 0; // turn it off
|
||||||
|
else
|
||||||
|
ent->s.sound = ent->noise_index; // start it
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // normal sound
|
||||||
|
if (ent->spawnflags & 4)
|
||||||
|
chan = CHAN_VOICE|CHAN_RELIABLE;
|
||||||
|
else
|
||||||
|
chan = CHAN_VOICE;
|
||||||
|
// use a positioned_sound, because this entity won't normally be
|
||||||
|
// sent to any clients because it is invisible
|
||||||
|
gi.positioned_sound (ent->s.origin, ent, chan, ent->noise_index, ent->volume, ent->attenuation, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_target_speaker (edict_t *ent)
|
||||||
|
{
|
||||||
|
char buffer[MAX_QPATH];
|
||||||
|
|
||||||
|
if(!st.noise)
|
||||||
|
{
|
||||||
|
gi.dprintf("target_speaker with no noise set at %s\n", vtos(ent->s.origin));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!strstr (st.noise, ".wav"))
|
||||||
|
Com_sprintf (buffer, sizeof(buffer), "%s.wav", st.noise);
|
||||||
|
else
|
||||||
|
strncpy (buffer, st.noise, sizeof(buffer));
|
||||||
|
ent->noise_index = gi.soundindex (buffer);
|
||||||
|
|
||||||
|
if (!ent->volume)
|
||||||
|
ent->volume = 1.0;
|
||||||
|
|
||||||
|
if (!ent->attenuation)
|
||||||
|
ent->attenuation = 1.0;
|
||||||
|
else if (ent->attenuation == -1) // use -1 so 0 defaults to 1
|
||||||
|
ent->attenuation = 0;
|
||||||
|
|
||||||
|
// check for prestarted looping sound
|
||||||
|
if (ent->spawnflags & 1)
|
||||||
|
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
|
||||||
|
gi.linkentity (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==========================================================
|
||||||
|
|
||||||
|
void Use_Target_Help (edict_t *ent, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
if (ent->spawnflags & 1)
|
||||||
|
strncpy (game.helpmessage1, ent->message, sizeof(game.helpmessage2)-1);
|
||||||
|
else
|
||||||
|
strncpy (game.helpmessage2, ent->message, sizeof(game.helpmessage1)-1);
|
||||||
|
|
||||||
|
game.helpchanged++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*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.
|
||||||
|
*/
|
||||||
|
void SP_target_help(edict_t *ent)
|
||||||
|
{
|
||||||
|
if (deathmatch->value)
|
||||||
|
{ // auto-remove for deathmatch
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ent->message)
|
||||||
|
{
|
||||||
|
gi.dprintf ("%s with no message at %s\n", ent->classname, vtos(ent->s.origin));
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ent->use = Use_Target_Help;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================
|
||||||
|
|
||||||
|
/*QUAKED target_secret (1 0 1) (-8 -8 -8) (8 8 8)
|
||||||
|
Counts a secret found.
|
||||||
|
These are single use targets.
|
||||||
|
*/
|
||||||
|
void use_target_secret (edict_t *ent, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
gi.sound (ent, CHAN_VOICE, ent->noise_index, 1, ATTN_NORM, 0);
|
||||||
|
|
||||||
|
level.found_secrets++;
|
||||||
|
|
||||||
|
G_UseTargets (ent, activator);
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_target_secret (edict_t *ent)
|
||||||
|
{
|
||||||
|
if (deathmatch->value)
|
||||||
|
{ // auto-remove for deathmatch
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->use = use_target_secret;
|
||||||
|
if (!st.noise)
|
||||||
|
st.noise = "misc/secret.wav";
|
||||||
|
ent->noise_index = gi.soundindex (st.noise);
|
||||||
|
ent->svflags = SVF_NOCLIENT;
|
||||||
|
level.total_secrets++;
|
||||||
|
// map bug hack
|
||||||
|
if (!Q_stricmp(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.";
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================
|
||||||
|
|
||||||
|
/*QUAKED target_goal (1 0 1) (-8 -8 -8) (8 8 8)
|
||||||
|
Counts a goal completed.
|
||||||
|
These are single use targets.
|
||||||
|
*/
|
||||||
|
void use_target_goal (edict_t *ent, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
gi.sound (ent, CHAN_VOICE, ent->noise_index, 1, ATTN_NORM, 0);
|
||||||
|
|
||||||
|
level.found_goals++;
|
||||||
|
|
||||||
|
if (level.found_goals == level.total_goals)
|
||||||
|
gi.configstring (CS_CDTRACK, "0");
|
||||||
|
|
||||||
|
G_UseTargets (ent, activator);
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_target_goal (edict_t *ent)
|
||||||
|
{
|
||||||
|
if (deathmatch->value)
|
||||||
|
{ // auto-remove for deathmatch
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->use = use_target_goal;
|
||||||
|
if (!st.noise)
|
||||||
|
st.noise = "misc/secret.wav";
|
||||||
|
ent->noise_index = gi.soundindex (st.noise);
|
||||||
|
ent->svflags = SVF_NOCLIENT;
|
||||||
|
level.total_goals++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================
|
||||||
|
|
||||||
|
|
||||||
|
/*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
|
||||||
|
*/
|
||||||
|
void target_explosion_explode (edict_t *self)
|
||||||
|
{
|
||||||
|
float save;
|
||||||
|
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_EXPLOSION1);
|
||||||
|
gi.WritePosition (self->s.origin);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PHS);
|
||||||
|
|
||||||
|
T_RadiusDamage (self, self->activator, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE);
|
||||||
|
|
||||||
|
save = self->delay;
|
||||||
|
self->delay = 0;
|
||||||
|
G_UseTargets (self, self->activator);
|
||||||
|
self->delay = save;
|
||||||
|
}
|
||||||
|
|
||||||
|
void use_target_explosion (edict_t *self, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
self->activator = activator;
|
||||||
|
|
||||||
|
if (!self->delay)
|
||||||
|
{
|
||||||
|
target_explosion_explode (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->think = target_explosion_explode;
|
||||||
|
self->nextthink = level.time + self->delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_target_explosion (edict_t *ent)
|
||||||
|
{
|
||||||
|
ent->use = use_target_explosion;
|
||||||
|
ent->svflags = SVF_NOCLIENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==========================================================
|
||||||
|
|
||||||
|
/*QUAKED target_changelevel (1 0 0) (-8 -8 -8) (8 8 8)
|
||||||
|
Changes level to "map" when fired
|
||||||
|
*/
|
||||||
|
void use_target_changelevel (edict_t *self, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
if (level.intermissiontime)
|
||||||
|
return; // allready activated
|
||||||
|
|
||||||
|
if (!deathmatch->value && !coop->value)
|
||||||
|
{
|
||||||
|
if (g_edicts[1].health <= 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if noexit, do a ton of damage to other
|
||||||
|
if (deathmatch->value && !( (int)dmflags->value & DF_ALLOW_EXIT) && other != world)
|
||||||
|
{
|
||||||
|
T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, 10 * other->max_health, 1000, 0, MOD_EXIT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if multiplayer, let everyone know who hit the exit
|
||||||
|
if (deathmatch->value)
|
||||||
|
{
|
||||||
|
if (activator && activator->client)
|
||||||
|
gi.bprintf (PRINT_HIGH, "%s exited the level.\n", activator->client->pers.netname);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if going to a new unit, clear cross triggers
|
||||||
|
if (strstr(self->map, "*"))
|
||||||
|
game.serverflags &= ~(SFL_CROSS_TRIGGER_MASK);
|
||||||
|
|
||||||
|
BeginIntermission (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_target_changelevel (edict_t *ent)
|
||||||
|
{
|
||||||
|
if (!ent->map)
|
||||||
|
{
|
||||||
|
gi.dprintf("target_changelevel with no map at %s\n", vtos(ent->s.origin));
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ugly hack because *SOMEBODY* screwed up their map
|
||||||
|
if((Q_stricmp(level.mapname, "fact1") == 0) && (Q_stricmp(ent->map, "fact3") == 0))
|
||||||
|
ent->map = "fact3$secret1";
|
||||||
|
|
||||||
|
ent->use = use_target_changelevel;
|
||||||
|
ent->svflags = SVF_NOCLIENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==========================================================
|
||||||
|
|
||||||
|
/*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
|
||||||
|
*/
|
||||||
|
|
||||||
|
void use_target_splash (edict_t *self, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_SPLASH);
|
||||||
|
gi.WriteByte (self->count);
|
||||||
|
gi.WritePosition (self->s.origin);
|
||||||
|
gi.WriteDir (self->movedir);
|
||||||
|
gi.WriteByte (self->sounds);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
if (self->dmg)
|
||||||
|
T_RadiusDamage (self, activator, self->dmg, NULL, self->dmg+40, MOD_SPLASH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_target_splash (edict_t *self)
|
||||||
|
{
|
||||||
|
self->use = use_target_splash;
|
||||||
|
G_SetMovedir (self->s.angles, self->movedir);
|
||||||
|
|
||||||
|
if (!self->count)
|
||||||
|
self->count = 32;
|
||||||
|
|
||||||
|
self->svflags = SVF_NOCLIENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==========================================================
|
||||||
|
|
||||||
|
/*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
|
||||||
|
*/
|
||||||
|
void ED_CallSpawn (edict_t *ent);
|
||||||
|
|
||||||
|
void use_target_spawner (edict_t *self, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
edict_t *ent;
|
||||||
|
//WF34 START
|
||||||
|
if ((self->target == NULL) || (self->target[0] == 0))
|
||||||
|
{
|
||||||
|
gi.dprintf("Warning: target_spawn map entity without target set\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//WF34 END
|
||||||
|
ent = G_Spawn();
|
||||||
|
ent->classname = self->target;
|
||||||
|
VectorCopy (self->s.origin, ent->s.origin);
|
||||||
|
VectorCopy (self->s.angles, ent->s.angles);
|
||||||
|
ED_CallSpawn (ent);
|
||||||
|
gi.unlinkentity (ent);
|
||||||
|
KillBox (ent);
|
||||||
|
gi.linkentity (ent);
|
||||||
|
if (self->speed)
|
||||||
|
VectorCopy (self->movedir, ent->velocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_target_spawner (edict_t *self)
|
||||||
|
{
|
||||||
|
self->use = use_target_spawner;
|
||||||
|
self->svflags = SVF_NOCLIENT;
|
||||||
|
if (self->speed)
|
||||||
|
{
|
||||||
|
G_SetMovedir (self->s.angles, self->movedir);
|
||||||
|
VectorScale (self->movedir, self->speed, self->movedir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================
|
||||||
|
|
||||||
|
/*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
|
||||||
|
*/
|
||||||
|
|
||||||
|
void use_target_blaster (edict_t *self, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
int effect;
|
||||||
|
|
||||||
|
if (self->spawnflags & 2)
|
||||||
|
effect = 0;
|
||||||
|
else if (self->spawnflags & 1)
|
||||||
|
effect = EF_HYPERBLASTER;
|
||||||
|
else
|
||||||
|
effect = EF_BLASTER;
|
||||||
|
|
||||||
|
fire_blaster (self, self->s.origin, self->movedir, self->dmg, self->speed, EF_BLASTER, MOD_TARGET_BLASTER);
|
||||||
|
gi.sound (self, CHAN_VOICE, self->noise_index, 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_target_blaster (edict_t *self)
|
||||||
|
{
|
||||||
|
self->use = use_target_blaster;
|
||||||
|
G_SetMovedir (self->s.angles, self->movedir);
|
||||||
|
self->noise_index = gi.soundindex ("weapons/laser2.wav");
|
||||||
|
|
||||||
|
if (!self->dmg)
|
||||||
|
self->dmg = 15;
|
||||||
|
if (!self->speed)
|
||||||
|
self->speed = 1000;
|
||||||
|
|
||||||
|
self->svflags = SVF_NOCLIENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==========================================================
|
||||||
|
|
||||||
|
/*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.
|
||||||
|
*/
|
||||||
|
void trigger_crosslevel_trigger_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
game.serverflags |= self->spawnflags;
|
||||||
|
G_FreeEdict (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_target_crosslevel_trigger (edict_t *self)
|
||||||
|
{
|
||||||
|
self->svflags = SVF_NOCLIENT;
|
||||||
|
self->use = trigger_crosslevel_trigger_use;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*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)
|
||||||
|
*/
|
||||||
|
void target_crosslevel_target_think (edict_t *self)
|
||||||
|
{
|
||||||
|
if (self->spawnflags == (game.serverflags & SFL_CROSS_TRIGGER_MASK & self->spawnflags))
|
||||||
|
{
|
||||||
|
G_UseTargets (self, self);
|
||||||
|
G_FreeEdict (self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_target_crosslevel_target (edict_t *self)
|
||||||
|
{
|
||||||
|
if (! self->delay)
|
||||||
|
self->delay = 1;
|
||||||
|
self->svflags = SVF_NOCLIENT;
|
||||||
|
|
||||||
|
self->think = target_crosslevel_target_think;
|
||||||
|
self->nextthink = level.time + self->delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================
|
||||||
|
|
||||||
|
/*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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void target_laser_think (edict_t *self)
|
||||||
|
{
|
||||||
|
edict_t *ignore;
|
||||||
|
vec3_t start;
|
||||||
|
vec3_t end;
|
||||||
|
trace_t tr;
|
||||||
|
vec3_t point;
|
||||||
|
vec3_t last_movedir;
|
||||||
|
int count;
|
||||||
|
int mod;
|
||||||
|
qboolean hitSomeone = false;
|
||||||
|
|
||||||
|
if (self->spawnflags & 0x80000000)
|
||||||
|
count = 8;
|
||||||
|
else
|
||||||
|
count = 4;
|
||||||
|
|
||||||
|
if (self->enemy)
|
||||||
|
{
|
||||||
|
VectorCopy (self->movedir, last_movedir);
|
||||||
|
VectorMA (self->enemy->absmin, 0.5, self->enemy->size, point);
|
||||||
|
VectorSubtract (point, self->s.origin, self->movedir);
|
||||||
|
VectorNormalize (self->movedir);
|
||||||
|
if (!VectorCompare(self->movedir, last_movedir))
|
||||||
|
self->spawnflags |= 0x80000000;
|
||||||
|
}
|
||||||
|
//ERASER START
|
||||||
|
if (!strcmp(self->classname, "path_beam"))
|
||||||
|
{
|
||||||
|
ignore = self;
|
||||||
|
VectorCopy (self->s.origin, start);
|
||||||
|
VectorAdd (self->s.origin, self->movedir, end);
|
||||||
|
VectorCopy(end, self->s.old_origin);
|
||||||
|
self->nextthink = level.time + FRAMETIME;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ignore = self;//normal
|
||||||
|
VectorCopy (self->s.origin, start);//normal
|
||||||
|
VectorMA (start, 2048, self->movedir, end);//normal
|
||||||
|
}
|
||||||
|
//ERASER END
|
||||||
|
//WF++
|
||||||
|
//Determine means of death. Could be a laserball laser
|
||||||
|
if (strcmp(self->classname,"laserball laser") == 0)
|
||||||
|
mod = MOD_WF_LASERBALL;
|
||||||
|
else
|
||||||
|
mod = MOD_TARGET_LASER;
|
||||||
|
//WF--
|
||||||
|
if (strcmp(self -> classname,"laser_defense") == 0)
|
||||||
|
{
|
||||||
|
if (level.time > self -> delay)
|
||||||
|
{
|
||||||
|
// bit of damage
|
||||||
|
T_RadiusDamage (self, self, 512, NULL, 25,mod);
|
||||||
|
// BANG !
|
||||||
|
/*//FIXME? WF24 HAS THESE
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_EXPLOSION1);
|
||||||
|
gi.WritePosition(self -> s.origin);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS); // bye bye laser
|
||||||
|
*/
|
||||||
|
laser_cleanup(self);//WF34
|
||||||
|
//G_FreeEdict (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
//24 USES THESE tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
|
||||||
|
tr = gi.trace (start, NULL, NULL, end, ignore, MASK_ALL);
|
||||||
|
|
||||||
|
if (!tr.ent)
|
||||||
|
break;
|
||||||
|
if (strcmp(self -> classname,"lb") == 0)
|
||||||
|
{
|
||||||
|
if (tr.ent)
|
||||||
|
{
|
||||||
|
// bit of damage
|
||||||
|
T_RadiusDamage (self, self, 512, NULL, 25,mod);
|
||||||
|
// BANG !
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_EXPLOSION1);
|
||||||
|
gi.WritePosition(self -> s.origin);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS); // bye bye laser
|
||||||
|
G_FreeEdict (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// hurt it if we can
|
||||||
|
if ((tr.ent->takedamage)
|
||||||
|
&& !(tr.ent->flags & FL_IMMUNE_LASER)
|
||||||
|
&& (tr.ent->wf_team != self->wf_team)
|
||||||
|
&& !tr.ent->disguised // don't hit spies
|
||||||
|
&& visible(tr.ent, self)) // make sure we can see it
|
||||||
|
|
||||||
|
{
|
||||||
|
//T_Damage (tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, MOD_TARGET_LASER);
|
||||||
|
T_Damage (tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, mod);
|
||||||
|
hitSomeone = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// if we hit something that's not a monster or player or is immune to lasers, we're done
|
||||||
|
if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
|
||||||
|
{
|
||||||
|
if (self->spawnflags & 0x80000000)
|
||||||
|
{
|
||||||
|
self->spawnflags &= ~0x80000000;
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_LASER_SPARKS);
|
||||||
|
gi.WriteByte (count);
|
||||||
|
gi.WritePosition (tr.endpos);
|
||||||
|
gi.WriteDir (tr.plane.normal);
|
||||||
|
gi.WriteByte (self->s.skinnum);
|
||||||
|
gi.multicast (tr.endpos, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ignore = tr.ent;
|
||||||
|
VectorCopy (tr.endpos, start);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hitSomeone)
|
||||||
|
{
|
||||||
|
// This draws the laser
|
||||||
|
VectorCopy (tr.endpos, self->s.old_origin);
|
||||||
|
}
|
||||||
|
self->nextthink = level.time + FRAMETIME;
|
||||||
|
}
|
||||||
|
//WF34 START
|
||||||
|
void target_laser_def_think (edict_t *self)
|
||||||
|
{
|
||||||
|
edict_t *ignore;
|
||||||
|
vec3_t start;
|
||||||
|
vec3_t end;
|
||||||
|
trace_t tr;
|
||||||
|
int count;
|
||||||
|
int mod;
|
||||||
|
|
||||||
|
if (self->spawnflags & 0x80000000)
|
||||||
|
count = 8;
|
||||||
|
else
|
||||||
|
count = 4;
|
||||||
|
|
||||||
|
|
||||||
|
ignore = self;
|
||||||
|
VectorCopy (self->creator->s.origin, start);
|
||||||
|
VectorMA (start, 2048, self->movedir, end);
|
||||||
|
|
||||||
|
mod = MOD_TARGET_LASER;
|
||||||
|
|
||||||
|
if (level.time > self -> delay)
|
||||||
|
{
|
||||||
|
// bit of damage
|
||||||
|
T_RadiusDamage (self, self, 512, NULL, 25,mod);
|
||||||
|
// BANG !
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_EXPLOSION1);
|
||||||
|
gi.WritePosition(self -> s.origin);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS); // bye bye laser
|
||||||
|
laser_cleanup(self->creator);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
// tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
|
||||||
|
tr = gi.trace (start, NULL, NULL, self->s.origin, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
|
||||||
|
|
||||||
|
if (!tr.ent)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// hurt it if we can
|
||||||
|
if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER)
|
||||||
|
&& (tr.ent->wf_team != self->wf_team))
|
||||||
|
{
|
||||||
|
if (!tr.ent->disguised)
|
||||||
|
T_Damage (tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we hit something that's not a monster or player or is immune to lasers, we're done
|
||||||
|
if (tr.ent && (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
|
||||||
|
&& (strcmp(tr.ent->classname,"laser_defense_gr") != 0))
|
||||||
|
{
|
||||||
|
if (self->spawnflags & 0x80000000)
|
||||||
|
{
|
||||||
|
self->spawnflags &= ~0x80000000;
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_LASER_SPARKS);
|
||||||
|
gi.WriteByte (count);
|
||||||
|
gi.WritePosition (tr.endpos);
|
||||||
|
gi.WriteDir (tr.plane.normal);
|
||||||
|
gi.WriteByte (self->s.skinnum);
|
||||||
|
gi.multicast (tr.endpos, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ignore = tr.ent;
|
||||||
|
VectorCopy (tr.endpos, start);
|
||||||
|
}
|
||||||
|
// here's the real trick, copy the grenades position to our old origin
|
||||||
|
VectorCopy (self->creator->s.origin, self->s.old_origin);
|
||||||
|
self->nextthink = level.time + FRAMETIME;
|
||||||
|
}
|
||||||
|
//WF34 END
|
||||||
|
void target_laser_on (edict_t *self)
|
||||||
|
{
|
||||||
|
if (!self->activator)
|
||||||
|
self->activator = self;
|
||||||
|
self->spawnflags |= 0x80000001;
|
||||||
|
self->svflags &= ~SVF_NOCLIENT;
|
||||||
|
|
||||||
|
target_laser_think (self);
|
||||||
|
}
|
||||||
|
//WF34 START
|
||||||
|
void target_laser_def_on (edict_t *self)
|
||||||
|
{
|
||||||
|
if (!self->activator)
|
||||||
|
self->activator = self;
|
||||||
|
self->spawnflags |= 0x80000001;
|
||||||
|
self->svflags &= ~SVF_NOCLIENT;
|
||||||
|
|
||||||
|
target_laser_def_think (self);
|
||||||
|
}
|
||||||
|
//WF34 END
|
||||||
|
void target_laser_off (edict_t *self)
|
||||||
|
{
|
||||||
|
self->spawnflags &= ~1;
|
||||||
|
self->svflags |= SVF_NOCLIENT;
|
||||||
|
self->nextthink = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void target_laser_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
//See if there is a team specific indicator WF34 LINES
|
||||||
|
if ((self->wf_team) && (self->wf_team != other->wf_team))
|
||||||
|
return;
|
||||||
|
|
||||||
|
self->activator = activator;
|
||||||
|
if (self->spawnflags & 1)
|
||||||
|
target_laser_off (self);
|
||||||
|
else
|
||||||
|
target_laser_on (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void target_laser_start (edict_t *self)
|
||||||
|
{
|
||||||
|
edict_t *ent;
|
||||||
|
|
||||||
|
self->movetype = MOVETYPE_NONE;
|
||||||
|
self->solid = SOLID_NOT;
|
||||||
|
self->s.renderfx |= RF_BEAM|RF_TRANSLUCENT;
|
||||||
|
self->s.modelindex = 1; // must be non-zero
|
||||||
|
|
||||||
|
// set the beam diameter
|
||||||
|
if (self->spawnflags & 64)
|
||||||
|
self->s.frame = 16;
|
||||||
|
else
|
||||||
|
self->s.frame = 4;
|
||||||
|
|
||||||
|
// set the color
|
||||||
|
if (self->spawnflags & 2) //WF-red
|
||||||
|
self->s.skinnum = 0xf2f2f0f0;
|
||||||
|
else if (self->spawnflags & 4) //WF-green
|
||||||
|
self->s.skinnum = 0xd0d1d2d3;
|
||||||
|
else if (self->spawnflags & 8) //WF-blue?
|
||||||
|
self->s.skinnum = 0xf3f3f1f1;
|
||||||
|
else if (self->spawnflags & 16) //WF-yellow
|
||||||
|
self->s.skinnum = 0xdcdddedf;
|
||||||
|
else if (self->spawnflags & 32) //WF-orange
|
||||||
|
self->s.skinnum = 0xe0e1e2e3;
|
||||||
|
|
||||||
|
if (!self->enemy)
|
||||||
|
{
|
||||||
|
if (self->target)
|
||||||
|
{
|
||||||
|
ent = G_Find (NULL, FOFS(targetname), self->target);
|
||||||
|
if (!ent)
|
||||||
|
gi.dprintf ("%s at %s: %s is a bad target\n", self->classname, vtos(self->s.origin), self->target);
|
||||||
|
self->enemy = ent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
G_SetMovedir (self->s.angles, self->movedir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self->use = target_laser_use;
|
||||||
|
self->think = target_laser_think;
|
||||||
|
|
||||||
|
if (!self->dmg)
|
||||||
|
self->dmg = 1;
|
||||||
|
|
||||||
|
VectorSet (self->mins, -8, -8, -8);
|
||||||
|
VectorSet (self->maxs, 8, 8, 8);
|
||||||
|
gi.linkentity (self);
|
||||||
|
|
||||||
|
if (self->spawnflags & 1)
|
||||||
|
target_laser_on (self);
|
||||||
|
else
|
||||||
|
target_laser_off (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_target_laser (edict_t *self)
|
||||||
|
{
|
||||||
|
// let everything else get spawned before we start firing
|
||||||
|
self->think = target_laser_start;
|
||||||
|
self->nextthink = level.time + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================
|
||||||
|
|
||||||
|
/*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
|
||||||
|
*/
|
||||||
|
|
||||||
|
void target_lightramp_think (edict_t *self)
|
||||||
|
{
|
||||||
|
char style[2];
|
||||||
|
|
||||||
|
style[0] = 'a' + self->movedir[0] + (level.time - self->timestamp) / FRAMETIME * self->movedir[2];
|
||||||
|
style[1] = 0;
|
||||||
|
gi.configstring (CS_LIGHTS+self->enemy->style, style);
|
||||||
|
|
||||||
|
if ((level.time - self->timestamp) < self->speed)
|
||||||
|
{
|
||||||
|
self->nextthink = level.time + FRAMETIME;
|
||||||
|
}
|
||||||
|
else if (self->spawnflags & 1)
|
||||||
|
{
|
||||||
|
char temp;
|
||||||
|
|
||||||
|
temp = self->movedir[0];
|
||||||
|
self->movedir[0] = self->movedir[1];
|
||||||
|
self->movedir[1] = temp;
|
||||||
|
self->movedir[2] *= -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void target_lightramp_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
if (!self->enemy)
|
||||||
|
{
|
||||||
|
edict_t *e;
|
||||||
|
|
||||||
|
// check all the targets
|
||||||
|
e = NULL;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
e = G_Find (e, FOFS(targetname), self->target);
|
||||||
|
if (!e)
|
||||||
|
break;
|
||||||
|
if (strcmp(e->classname, "light") != 0)
|
||||||
|
{
|
||||||
|
gi.dprintf("%s at %s ", self->classname, vtos(self->s.origin));
|
||||||
|
gi.dprintf("target %s (%s at %s) is not a light\n", self->target, e->classname, vtos(e->s.origin));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->enemy = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->enemy)
|
||||||
|
{
|
||||||
|
gi.dprintf("%s target %s not found at %s\n", self->classname, self->target, vtos(self->s.origin));
|
||||||
|
G_FreeEdict (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self->timestamp = level.time;
|
||||||
|
target_lightramp_think (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_target_lightramp (edict_t *self)
|
||||||
|
{
|
||||||
|
if (!self->message || strlen(self->message) != 2 || self->message[0] < 'a' || self->message[0] > 'z' || self->message[1] < 'a' || self->message[1] > 'z' || self->message[0] == self->message[1])
|
||||||
|
{
|
||||||
|
gi.dprintf("target_lightramp has bad ramp (%s) at %s\n", self->message, vtos(self->s.origin));
|
||||||
|
G_FreeEdict (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deathmatch->value)
|
||||||
|
{
|
||||||
|
G_FreeEdict (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->target)
|
||||||
|
{
|
||||||
|
gi.dprintf("%s with no target at %s\n", self->classname, vtos(self->s.origin));
|
||||||
|
G_FreeEdict (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->svflags |= SVF_NOCLIENT;
|
||||||
|
self->use = target_lightramp_use;
|
||||||
|
self->think = target_lightramp_think;
|
||||||
|
|
||||||
|
self->movedir[0] = self->message[0] - 'a';
|
||||||
|
self->movedir[1] = self->message[1] - 'a';
|
||||||
|
self->movedir[2] = (self->movedir[1] - self->movedir[0]) / (self->speed / FRAMETIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================
|
||||||
|
|
||||||
|
/*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)
|
||||||
|
*/
|
||||||
|
|
||||||
|
void target_earthquake_think (edict_t *self)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
edict_t *e;
|
||||||
|
|
||||||
|
if (self->last_move_time < level.time)
|
||||||
|
{
|
||||||
|
gi.positioned_sound (self->s.origin, self, CHAN_AUTO, self->noise_index, 1.0, ATTN_NONE, 0);
|
||||||
|
self->last_move_time = level.time + 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=1, e=g_edicts+i; i < globals.num_edicts; i++,e++)
|
||||||
|
{
|
||||||
|
if (!e->inuse)
|
||||||
|
continue;
|
||||||
|
if (!e->client)
|
||||||
|
continue;
|
||||||
|
if (!e->groundentity)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
e->groundentity = NULL;
|
||||||
|
e->velocity[0] += crandom()* 150;
|
||||||
|
e->velocity[1] += crandom()* 150;
|
||||||
|
e->velocity[2] = self->speed * (100.0 / e->mass);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level.time < self->timestamp)
|
||||||
|
self->nextthink = level.time + FRAMETIME;
|
||||||
|
}
|
||||||
|
|
||||||
|
void target_earthquake_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
self->timestamp = level.time + self->count;
|
||||||
|
self->nextthink = level.time + FRAMETIME;
|
||||||
|
self->activator = activator;
|
||||||
|
self->last_move_time = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_target_earthquake (edict_t *self)
|
||||||
|
{
|
||||||
|
if (!self->targetname)
|
||||||
|
gi.dprintf("untargeted %s at %s\n", self->classname, vtos(self->s.origin));
|
||||||
|
|
||||||
|
if (!self->count)
|
||||||
|
self->count = 5;
|
||||||
|
|
||||||
|
if (!self->speed)
|
||||||
|
self->speed = 200;
|
||||||
|
|
||||||
|
self->svflags |= SVF_NOCLIENT;
|
||||||
|
self->think = target_earthquake_think;
|
||||||
|
self->use = target_earthquake_use;
|
||||||
|
|
||||||
|
self->noise_index = gi.soundindex ("world/quake.wav");
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,625 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
void WFAddBonus(edict_t *self, edict_t *other);//WF24
|
||||||
|
|
||||||
|
|
||||||
|
void InitTrigger (edict_t *self)
|
||||||
|
{
|
||||||
|
if (!VectorCompare (self->s.angles, vec3_origin))
|
||||||
|
G_SetMovedir (self->s.angles, self->movedir);
|
||||||
|
|
||||||
|
self->solid = SOLID_TRIGGER;
|
||||||
|
self->movetype = MOVETYPE_NONE;
|
||||||
|
gi.setmodel (self, self->model);
|
||||||
|
self->svflags = SVF_NOCLIENT;
|
||||||
|
}
|
||||||
|
//WF34 START
|
||||||
|
qboolean PassTeamCheck(edict_t *ent, edict_t *other)
|
||||||
|
{
|
||||||
|
|
||||||
|
//gi.dprintf("Pass Team Check, team=%d, other team = %d\n", ent->wf_team, other->wf_team);
|
||||||
|
|
||||||
|
//If no team specified, return true
|
||||||
|
if (!ent->wf_team)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
//See if there is a team specific indicator
|
||||||
|
if (ent->wf_team == other->wf_team)
|
||||||
|
return true; //Same team
|
||||||
|
else
|
||||||
|
return false; //Different team
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// the wait time has passed, so set back up for another activation
|
||||||
|
void multi_wait (edict_t *ent)
|
||||||
|
{
|
||||||
|
ent->nextthink = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// the trigger was just activated
|
||||||
|
// ent->activator should be set to the activator so it can be held through a delay
|
||||||
|
// so wait for the delay time before firing
|
||||||
|
void multi_trigger (edict_t *ent)
|
||||||
|
{
|
||||||
|
if (ent->nextthink)
|
||||||
|
return; // already been triggered
|
||||||
|
|
||||||
|
G_UseTargets (ent, ent->activator);
|
||||||
|
|
||||||
|
//Should we award a bonus?//WF34
|
||||||
|
if (ent->bonustype) WFAddBonus(ent, ent->activator);
|
||||||
|
|
||||||
|
if (ent->wait > 0)
|
||||||
|
{
|
||||||
|
ent->think = multi_wait;
|
||||||
|
ent->nextthink = level.time + ent->wait;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // we can't just remove (self) here, because this is a touch function
|
||||||
|
// called while looping through area links...
|
||||||
|
ent->touch = NULL;
|
||||||
|
ent->nextthink = level.time + FRAMETIME;
|
||||||
|
ent->think = G_FreeEdict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Use_Multi (edict_t *ent, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
ent->activator = activator;
|
||||||
|
multi_trigger (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Touch_Multi (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
if(other->client)
|
||||||
|
{
|
||||||
|
if (self->spawnflags & 2)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (other->svflags & SVF_MONSTER)
|
||||||
|
{
|
||||||
|
if (!(self->spawnflags & 1))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return;
|
||||||
|
|
||||||
|
//See if we pass the team check//WF24-34
|
||||||
|
if ( PassTeamCheck(self,other) == false) return;
|
||||||
|
|
||||||
|
if (!VectorCompare(self->movedir, vec3_origin))
|
||||||
|
{
|
||||||
|
vec3_t forward;
|
||||||
|
|
||||||
|
AngleVectors(other->s.angles, forward, NULL, NULL);
|
||||||
|
if (_DotProduct(forward, self->movedir) < 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->activator = other;
|
||||||
|
multi_trigger (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*QUAKED trigger_multiple (.5 .5 .5) ? MONSTER NOT_PLAYER TRIGGERED
|
||||||
|
Variable sized repeatable trigger. Must be targeted at one or more entities.
|
||||||
|
If "delay" is set, the trigger waits some time after activating before firing.
|
||||||
|
"wait" : Seconds between triggerings. (.2 default)
|
||||||
|
sounds
|
||||||
|
1) secret
|
||||||
|
2) beep beep
|
||||||
|
3) large switch
|
||||||
|
4)
|
||||||
|
set "message" to text string
|
||||||
|
*/
|
||||||
|
void trigger_enable (edict_t *self, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
self->solid = SOLID_TRIGGER;
|
||||||
|
self->use = Use_Multi;
|
||||||
|
gi.linkentity (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_trigger_multiple (edict_t *ent)
|
||||||
|
{
|
||||||
|
if (ent->sounds == 1)
|
||||||
|
ent->noise_index = gi.soundindex ("misc/secret.wav");
|
||||||
|
else if (ent->sounds == 2)
|
||||||
|
ent->noise_index = gi.soundindex ("misc/talk.wav");
|
||||||
|
else if (ent->sounds == 3)
|
||||||
|
ent->noise_index = gi.soundindex ("misc/trigger1.wav");
|
||||||
|
|
||||||
|
if (!ent->wait)
|
||||||
|
ent->wait = 0.2;
|
||||||
|
ent->touch = Touch_Multi;
|
||||||
|
ent->movetype = MOVETYPE_NONE;
|
||||||
|
ent->svflags |= SVF_NOCLIENT;
|
||||||
|
|
||||||
|
|
||||||
|
if (ent->spawnflags & 4)
|
||||||
|
{
|
||||||
|
ent->solid = SOLID_NOT;
|
||||||
|
ent->use = trigger_enable;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ent->solid = SOLID_TRIGGER;
|
||||||
|
ent->use = Use_Multi;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!VectorCompare(ent->s.angles, vec3_origin))
|
||||||
|
G_SetMovedir (ent->s.angles, ent->movedir);
|
||||||
|
|
||||||
|
gi.setmodel (ent, ent->model);
|
||||||
|
gi.linkentity (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*QUAKED trigger_once (.5 .5 .5) ? x x TRIGGERED
|
||||||
|
Triggers once, then removes itself.
|
||||||
|
You must set the key "target" to the name of another object in the level that has a matching "targetname".
|
||||||
|
|
||||||
|
If TRIGGERED, this trigger must be triggered before it is live.
|
||||||
|
|
||||||
|
sounds
|
||||||
|
1) secret
|
||||||
|
2) beep beep
|
||||||
|
3) large switch
|
||||||
|
4)
|
||||||
|
|
||||||
|
"message" string to be displayed when triggered
|
||||||
|
*/
|
||||||
|
|
||||||
|
void SP_trigger_once(edict_t *ent)
|
||||||
|
{
|
||||||
|
// make old maps work because I messed up on flag assignments here
|
||||||
|
// triggered was on bit 1 when it should have been on bit 4
|
||||||
|
if (ent->spawnflags & 1)
|
||||||
|
{
|
||||||
|
vec3_t v;
|
||||||
|
|
||||||
|
VectorMA (ent->mins, 0.5, ent->size, v);
|
||||||
|
ent->spawnflags &= ~1;
|
||||||
|
ent->spawnflags |= 4;
|
||||||
|
gi.dprintf("fixed TRIGGERED flag on %s at %s\n", ent->classname, vtos(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->wait = -1;
|
||||||
|
SP_trigger_multiple (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*QUAKED trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8)
|
||||||
|
This fixed size trigger cannot be touched, it can only be fired by other events.
|
||||||
|
*/
|
||||||
|
void trigger_relay_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
G_UseTargets (self, activator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_trigger_relay (edict_t *self)
|
||||||
|
{
|
||||||
|
self->use = trigger_relay_use;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
trigger_key
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*QUAKED trigger_key (.5 .5 .5) (-8 -8 -8) (8 8 8)
|
||||||
|
A relay trigger that only fires it's targets if player has the proper key.
|
||||||
|
Use "item" to specify the required key, for example "key_data_cd"
|
||||||
|
*/
|
||||||
|
void trigger_key_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
|
||||||
|
if (!self->item)
|
||||||
|
return;
|
||||||
|
if (!activator->client)
|
||||||
|
return;
|
||||||
|
|
||||||
|
index = ITEM_INDEX(self->item);
|
||||||
|
if (!activator->client->pers.inventory[index])
|
||||||
|
{
|
||||||
|
if (level.time < self->touch_debounce_time)
|
||||||
|
return;
|
||||||
|
self->touch_debounce_time = level.time + 5.0;
|
||||||
|
|
||||||
|
if (!activator->bot_client)//ERASER
|
||||||
|
gi.centerprintf (activator, "You need the %s", self->item->pickup_name);
|
||||||
|
gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/keytry.wav"), 1, ATTN_NORM, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/keyuse.wav"), 1, ATTN_NORM, 0);
|
||||||
|
if (coop->value)
|
||||||
|
{
|
||||||
|
int player;
|
||||||
|
edict_t *ent;
|
||||||
|
|
||||||
|
if (strcmp(self->item->classname, "key_power_cube") == 0)
|
||||||
|
{
|
||||||
|
int cube;
|
||||||
|
|
||||||
|
for (cube = 0; cube < 8; cube++)
|
||||||
|
if (activator->client->pers.power_cubes & (1 << cube))
|
||||||
|
break;
|
||||||
|
for (player = 1; player <= game.maxclients; player++)
|
||||||
|
{
|
||||||
|
ent = &g_edicts[player];
|
||||||
|
if (!ent->inuse)
|
||||||
|
continue;
|
||||||
|
if (!ent->client)
|
||||||
|
continue;
|
||||||
|
if (ent->client->pers.power_cubes & (1 << cube))
|
||||||
|
{
|
||||||
|
ent->client->pers.inventory[index]--;
|
||||||
|
ent->client->pers.power_cubes &= ~(1 << cube);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (player = 1; player <= game.maxclients; player++)
|
||||||
|
{
|
||||||
|
ent = &g_edicts[player];
|
||||||
|
if (!ent->inuse)
|
||||||
|
continue;
|
||||||
|
if (!ent->client)
|
||||||
|
continue;
|
||||||
|
ent->client->pers.inventory[index] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
activator->client->pers.inventory[index]--;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_UseTargets (self, activator);
|
||||||
|
|
||||||
|
self->use = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_trigger_key (edict_t *self)
|
||||||
|
{
|
||||||
|
if (!st.item)
|
||||||
|
{
|
||||||
|
gi.dprintf("no key item for trigger_key at %s\n", vtos(self->s.origin));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self->item = FindItemByClassname (st.item);
|
||||||
|
|
||||||
|
if (!self->item)
|
||||||
|
{
|
||||||
|
gi.dprintf("item %s not found for trigger_key at %s\n", st.item, vtos(self->s.origin));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->target)
|
||||||
|
{
|
||||||
|
gi.dprintf("%s at %s has no target\n", self->classname, vtos(self->s.origin));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gi.soundindex ("misc/keytry.wav");
|
||||||
|
gi.soundindex ("misc/keyuse.wav");
|
||||||
|
|
||||||
|
self->use = trigger_key_use;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
trigger_counter
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*QUAKED trigger_counter (.5 .5 .5) ? nomessage
|
||||||
|
Acts as an intermediary for an action that takes multiple inputs.
|
||||||
|
|
||||||
|
If nomessage is not set, t will print "1 more.. " etc when triggered and "sequence complete" when finished.
|
||||||
|
|
||||||
|
After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void trigger_counter_use(edict_t *self, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
if (self->count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
self->count--;
|
||||||
|
|
||||||
|
if (self->count)
|
||||||
|
{
|
||||||
|
if (! (self->spawnflags & 1))
|
||||||
|
{
|
||||||
|
if (!activator->bot_client)//ERASER
|
||||||
|
gi.centerprintf(activator, "%i more to go...", self->count);
|
||||||
|
gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! (self->spawnflags & 1))
|
||||||
|
{
|
||||||
|
if (!activator->bot_client)//ERASER
|
||||||
|
gi.centerprintf(activator, "Sequence completed!");
|
||||||
|
gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
self->activator = activator;
|
||||||
|
multi_trigger (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_trigger_counter (edict_t *self)
|
||||||
|
{
|
||||||
|
self->wait = -1;
|
||||||
|
if (!self->count)
|
||||||
|
self->count = 2;
|
||||||
|
|
||||||
|
self->use = trigger_counter_use;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
trigger_always
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*QUAKED trigger_always (.5 .5 .5) (-8 -8 -8) (8 8 8)
|
||||||
|
This trigger will always fire. It is activated by the world.
|
||||||
|
*/
|
||||||
|
void SP_trigger_always (edict_t *ent)
|
||||||
|
{
|
||||||
|
// we must have some delay to make sure our use targets are present
|
||||||
|
if (ent->delay < 0.2)
|
||||||
|
ent->delay = 0.2;
|
||||||
|
G_UseTargets(ent, ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
trigger_push
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define PUSH_ONCE 1
|
||||||
|
|
||||||
|
static int windsound;
|
||||||
|
|
||||||
|
void trigger_push_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
|
||||||
|
//See if we pass the team check (3.2)//WF24-34 START
|
||||||
|
if ( PassTeamCheck(self,other) == false) return;
|
||||||
|
//WF24-34 END
|
||||||
|
if (strcmp(other->classname, "grenade") == 0)
|
||||||
|
{
|
||||||
|
VectorScale (self->movedir, self->speed * 10, other->velocity);
|
||||||
|
}
|
||||||
|
else if (other->health > 0)
|
||||||
|
{
|
||||||
|
VectorScale (self->movedir, self->speed * 10, other->velocity);
|
||||||
|
|
||||||
|
if (other->client)
|
||||||
|
{
|
||||||
|
// don't take falling damage immediately from this
|
||||||
|
VectorCopy (other->velocity, other->client->oldvelocity);
|
||||||
|
if (other->fly_sound_debounce_time < level.time)
|
||||||
|
{
|
||||||
|
other->fly_sound_debounce_time = level.time + 1.5;
|
||||||
|
gi.sound (other, CHAN_AUTO, windsound, 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
//Eraser START, Nav, don't drop reverse jump nodes if jumping
|
||||||
|
if (bot_calc_nodes->value && other->jump_ent)
|
||||||
|
{
|
||||||
|
other->jump_ent->flags |= FL_NO_KNOCKBACK;
|
||||||
|
}
|
||||||
|
//ERASER END
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (self->spawnflags & PUSH_ONCE)
|
||||||
|
G_FreeEdict (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE
|
||||||
|
Pushes the player
|
||||||
|
"speed" defaults to 1000
|
||||||
|
*/
|
||||||
|
void SP_trigger_push (edict_t *self)
|
||||||
|
{
|
||||||
|
InitTrigger (self);
|
||||||
|
windsound = gi.soundindex ("misc/windfly.wav");
|
||||||
|
self->touch = trigger_push_touch;
|
||||||
|
if (!self->speed)
|
||||||
|
self->speed = 1000;
|
||||||
|
gi.linkentity (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
trigger_hurt
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*QUAKED trigger_hurt (.5 .5 .5) ? START_OFF TOGGLE SILENT NO_PROTECTION SLOW
|
||||||
|
Any entity that touches this will be hurt.
|
||||||
|
|
||||||
|
It does dmg points of damage each server frame
|
||||||
|
|
||||||
|
SILENT supresses playing the sound
|
||||||
|
SLOW changes the damage rate to once per second
|
||||||
|
NO_PROTECTION *nothing* stops the damage
|
||||||
|
|
||||||
|
"dmg" default 5 (whole numbers only)
|
||||||
|
|
||||||
|
*/
|
||||||
|
void hurt_use (edict_t *self, edict_t *other, edict_t *activator)
|
||||||
|
{
|
||||||
|
if (self->solid == SOLID_NOT)
|
||||||
|
self->solid = SOLID_TRIGGER;
|
||||||
|
else
|
||||||
|
self->solid = SOLID_NOT;
|
||||||
|
gi.linkentity (self);
|
||||||
|
|
||||||
|
if (!(self->spawnflags & 2))
|
||||||
|
self->use = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void hurt_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
int dflags;
|
||||||
|
|
||||||
|
if (!other->takedamage)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (self->timestamp > level.time)
|
||||||
|
return;
|
||||||
|
//WF24 START
|
||||||
|
//See if we pass the team check
|
||||||
|
if ( PassTeamCheck(self,other) == false) return;
|
||||||
|
|
||||||
|
//Special case for decoys. For some reason, it may crash
|
||||||
|
//server if the decoys are killed with trigger_hurt
|
||||||
|
if (strcmp(other->classname, "decoy") == 0) return;
|
||||||
|
//WF24 END
|
||||||
|
|
||||||
|
if (self->spawnflags & 16)
|
||||||
|
self->timestamp = level.time + 1;
|
||||||
|
else
|
||||||
|
self->timestamp = level.time + FRAMETIME;
|
||||||
|
|
||||||
|
if (!(self->spawnflags & 4))
|
||||||
|
{
|
||||||
|
if ((level.framenum % 10) == 0)
|
||||||
|
gi.sound (other, CHAN_AUTO, self->noise_index, 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->spawnflags & 8)
|
||||||
|
dflags = DAMAGE_NO_PROTECTION;
|
||||||
|
else
|
||||||
|
dflags = 0;
|
||||||
|
T_Damage (other, self, self, vec3_origin, other->s.origin, vec3_origin, self->dmg, self->dmg, dflags, MOD_TRIGGER_HURT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_trigger_hurt (edict_t *self)
|
||||||
|
{
|
||||||
|
|
||||||
|
InitTrigger (self);
|
||||||
|
|
||||||
|
self->noise_index = gi.soundindex ("world/electro.wav");
|
||||||
|
self->touch = hurt_touch;
|
||||||
|
|
||||||
|
if (!self->dmg)
|
||||||
|
self->dmg = 5;
|
||||||
|
|
||||||
|
if (self->spawnflags & 1)
|
||||||
|
self->solid = SOLID_NOT;
|
||||||
|
else
|
||||||
|
self->solid = SOLID_TRIGGER;
|
||||||
|
|
||||||
|
if (self->spawnflags & 2)
|
||||||
|
self->use = hurt_use;
|
||||||
|
|
||||||
|
gi.linkentity (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
trigger_gravity
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*QUAKED trigger_gravity (.5 .5 .5) ?
|
||||||
|
Changes the touching entites gravity to
|
||||||
|
the value of "gravity". 1.0 is standard
|
||||||
|
gravity for the level.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void trigger_gravity_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
other->gravity = self->gravity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_trigger_gravity (edict_t *self)
|
||||||
|
{
|
||||||
|
if (st.gravity == 0)
|
||||||
|
{
|
||||||
|
gi.dprintf("trigger_gravity without gravity set at %s\n", vtos(self->s.origin));
|
||||||
|
G_FreeEdict (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InitTrigger (self);
|
||||||
|
self->gravity = atoi(st.gravity);
|
||||||
|
self->touch = trigger_gravity_touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================================================================
|
||||||
|
|
||||||
|
trigger_monsterjump
|
||||||
|
|
||||||
|
==============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*QUAKED trigger_monsterjump (.5 .5 .5) ?
|
||||||
|
Walking monsters that touch this will jump in the direction of the trigger's angle
|
||||||
|
"speed" default to 200, the speed thrown forward
|
||||||
|
"height" default to 200, the speed thrown upwards
|
||||||
|
*/
|
||||||
|
|
||||||
|
void trigger_monsterjump_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
if (other->flags & (FL_FLY | FL_SWIM) )
|
||||||
|
return;
|
||||||
|
if (other->svflags & SVF_DEADMONSTER)
|
||||||
|
return;
|
||||||
|
if ( !(other->svflags & SVF_MONSTER))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// set XY even if not on ground, so the jump will clear lips
|
||||||
|
other->velocity[0] = self->movedir[0] * self->speed;
|
||||||
|
other->velocity[1] = self->movedir[1] * self->speed;
|
||||||
|
|
||||||
|
if (!other->groundentity)
|
||||||
|
return;
|
||||||
|
|
||||||
|
other->groundentity = NULL;
|
||||||
|
other->velocity[2] = self->movedir[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
void SP_trigger_monsterjump (edict_t *self)
|
||||||
|
{
|
||||||
|
if (!self->speed)
|
||||||
|
self->speed = 200;
|
||||||
|
if (!st.height)
|
||||||
|
st.height = 200;
|
||||||
|
if (self->s.angles[YAW] == 0)
|
||||||
|
self->s.angles[YAW] = 360;
|
||||||
|
InitTrigger (self);
|
||||||
|
self->touch = trigger_monsterjump_touch;
|
||||||
|
self->movedir[2] = st.height;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,279 @@
|
||||||
|
/*
|
||||||
|
This is a very simplistic example of how to load and make a call into the
|
||||||
|
dll. This has been compiled and tested for a 32-bit console version, but
|
||||||
|
not under 16-bit windows. However, the #ifdef's have been left in for the
|
||||||
|
16-bit code, simply as an example.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#define WIN32
|
||||||
|
|
||||||
|
#include "g_local.h"
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "g_unzip.h"
|
||||||
|
#include <winver.h>
|
||||||
|
|
||||||
|
#define UNZ_DLL_NAME "UNZIP32.DLL\0"
|
||||||
|
|
||||||
|
#define DLL_WARNING "Cannot find %s."\
|
||||||
|
" The DLL must be in the application directory, the path, "\
|
||||||
|
"the Windows directory or the Windows System directory."
|
||||||
|
#define DLL_VERSION_WARNING "%s has the wrong version number."\
|
||||||
|
" Insure that you have the correct dll's installed, and that "\
|
||||||
|
"an older dll is not in your path or Windows System directory."
|
||||||
|
|
||||||
|
int hFile; /* file handle */
|
||||||
|
|
||||||
|
LPUSERFUNCTIONS lpUserFunctions;
|
||||||
|
HANDLE hUF = (HANDLE)NULL;
|
||||||
|
LPDCL lpDCL = NULL;
|
||||||
|
HANDLE hDCL = (HANDLE)NULL;
|
||||||
|
HINSTANCE hUnzipDll;
|
||||||
|
HANDLE hZCL = (HANDLE)NULL;
|
||||||
|
#ifdef WIN32
|
||||||
|
DWORD dwPlatformId = 0xFFFFFFFF;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Forward References */
|
||||||
|
int WINAPI DisplayBuf(char far *, unsigned long);
|
||||||
|
int WINAPI GetReplaceDlgRetVal(char *);
|
||||||
|
int WINAPI password(char *, char *, int, char *);
|
||||||
|
void WINAPI ReceiveDllMessage(unsigned long,unsigned long,
|
||||||
|
ush, ush, ush, ush, ush, ush, char, char *, char *, unsigned long, char);
|
||||||
|
_DLL_UNZIP windll_unzip;
|
||||||
|
_USER_FUNCTIONS UzInit;
|
||||||
|
void FreeUpMemory(void);
|
||||||
|
#ifdef WIN32
|
||||||
|
BOOL IsNT(VOID);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Extracts all files in zipfile to extract_dir.
|
||||||
|
// NOTE: make sure the current dir is the directory in which the .dll files are
|
||||||
|
// in before calling this!
|
||||||
|
int G_UnzipFile(char *zipfile, char *extract_dir)
|
||||||
|
{
|
||||||
|
//int exfc, infc;
|
||||||
|
//char **exfv, **infv;
|
||||||
|
//char *x_opt;
|
||||||
|
//DWORD dwVerInfoSize;
|
||||||
|
//DWORD dwVerHnd;
|
||||||
|
char szFullPath[PATH_MAX];
|
||||||
|
int retcode;
|
||||||
|
char *ptr;
|
||||||
|
//HANDLE hMem; /* handle to mem alloc'ed */
|
||||||
|
|
||||||
|
if (!zipfile) /* We must have an archive to unzip */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
hDCL = GlobalAlloc( GPTR, (DWORD)sizeof(DCL));
|
||||||
|
if (!hDCL)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
lpDCL = (LPDCL)GlobalLock(hDCL);
|
||||||
|
if (!lpDCL)
|
||||||
|
{
|
||||||
|
GlobalFree(hDCL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hUF = GlobalAlloc( GPTR, (DWORD)sizeof(USERFUNCTIONS));
|
||||||
|
if (!hUF)
|
||||||
|
{
|
||||||
|
GlobalUnlock(hDCL);
|
||||||
|
GlobalFree(hDCL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
lpUserFunctions = (LPUSERFUNCTIONS)GlobalLock(hUF);
|
||||||
|
|
||||||
|
if (!lpUserFunctions)
|
||||||
|
{
|
||||||
|
GlobalUnlock(hDCL);
|
||||||
|
GlobalFree(hDCL);
|
||||||
|
GlobalFree(hUF);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
lpUserFunctions->password = password;
|
||||||
|
lpUserFunctions->print = DisplayBuf;
|
||||||
|
lpUserFunctions->sound = NULL;
|
||||||
|
lpUserFunctions->replace = GetReplaceDlgRetVal;
|
||||||
|
lpUserFunctions->SendApplicationMessage = ReceiveDllMessage;
|
||||||
|
|
||||||
|
/* First we go look for the unzip dll */
|
||||||
|
#ifdef WIN32
|
||||||
|
if (SearchPath(
|
||||||
|
NULL, /* address of search path */
|
||||||
|
UNZ_DLL_NAME, /* address of filename */
|
||||||
|
NULL, /* address of extension */
|
||||||
|
PATH_MAX, /* size, in characters, of buffer */
|
||||||
|
szFullPath, /* address of buffer for found filename */
|
||||||
|
&ptr /* address of pointer to file component */
|
||||||
|
) == 0)
|
||||||
|
#else
|
||||||
|
hfile = OpenFile(UNZ_DLL_NAME, &ofs, OF_SEARCH);
|
||||||
|
if (hfile == HFILE_ERROR)
|
||||||
|
#endif
|
||||||
|
{ // cannot find unzip32.dll file
|
||||||
|
// char str[256];
|
||||||
|
// wsprintf (str, DLL_WARNING, UNZ_DLL_NAME);
|
||||||
|
// gi.dprintf("%s\n", str);
|
||||||
|
FreeUpMemory();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#ifndef WIN32
|
||||||
|
else
|
||||||
|
lstrcpy(szFullPath, ofs.szPathName);
|
||||||
|
_lclose(hfile);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Okay, now we know that the dll exists, and has the proper version
|
||||||
|
* information in it. We can go ahead and load it.
|
||||||
|
*/
|
||||||
|
hUnzipDll = LoadLibrary(UNZ_DLL_NAME);
|
||||||
|
#ifndef WIN32
|
||||||
|
if (hUnzipDll > HINSTANCE_ERROR)
|
||||||
|
#else
|
||||||
|
if (hUnzipDll != NULL)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
(_DLL_UNZIP)windll_unzip = (_DLL_UNZIP)GetProcAddress(hUnzipDll, "windll_unzip");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // unable to load the dll
|
||||||
|
// char str[256];
|
||||||
|
// wsprintf (str, "Could not load %s", UNZ_DLL_NAME);
|
||||||
|
// printf("%s\n", str);
|
||||||
|
FreeUpMemory();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Here is where the actual extraction process begins. First we set up the
|
||||||
|
flags to be passed into the dll.
|
||||||
|
*/
|
||||||
|
lpDCL->ncflag = 0; /* Write to stdout if true */
|
||||||
|
lpDCL->fQuiet = 2; /* We want all messages.
|
||||||
|
1 = fewer messages,
|
||||||
|
2 = no messages */
|
||||||
|
lpDCL->ntflag = 0; /* test zip file if true */
|
||||||
|
lpDCL->nvflag = 0; /* give a verbose listing if true */
|
||||||
|
lpDCL->nUflag = 0; /* Do not extract only newer */
|
||||||
|
lpDCL->nzflag = 0; /* display a zip file comment if true */
|
||||||
|
lpDCL->ndflag = 0; /* Recreate directories if true */
|
||||||
|
lpDCL->noflag = 1; /* Over-write all files if true */
|
||||||
|
lpDCL->naflag = 0; /* Do not convert CR to CRLF */
|
||||||
|
lpDCL->lpszZipFN = (LPSTR) zipfile; /* The archive name */
|
||||||
|
lpDCL->lpszExtractDir = (LPSTR) extract_dir;
|
||||||
|
/* The directory to extract to. This is set
|
||||||
|
to NULL if you are extracting to the
|
||||||
|
current directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// UNZIP the file!
|
||||||
|
|
||||||
|
retcode = (*windll_unzip)(0, NULL, 0, NULL, lpDCL, lpUserFunctions);
|
||||||
|
|
||||||
|
if (retcode != 0)
|
||||||
|
gi.dprintf("Error unzipping \"%s\"\n", zipfile);
|
||||||
|
|
||||||
|
FreeUpMemory();
|
||||||
|
FreeLibrary(hUnzipDll);
|
||||||
|
return (retcode == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WINAPI GetReplaceDlgRetVal(char *filename)
|
||||||
|
{
|
||||||
|
/* This is where you will decide if you want to replace, rename etc existing
|
||||||
|
files.
|
||||||
|
*/
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeUpMemory(void)
|
||||||
|
{
|
||||||
|
if (hDCL)
|
||||||
|
{
|
||||||
|
GlobalUnlock(hDCL);
|
||||||
|
GlobalFree(hDCL);
|
||||||
|
}
|
||||||
|
if (hUF)
|
||||||
|
{
|
||||||
|
GlobalUnlock(hUF);
|
||||||
|
GlobalFree(hUF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This simply determines if we are running on NT or Windows 95 */
|
||||||
|
#ifdef WIN32
|
||||||
|
BOOL IsNT(VOID)
|
||||||
|
{
|
||||||
|
if(dwPlatformId != 0xFFFFFFFF)
|
||||||
|
return dwPlatformId;
|
||||||
|
else
|
||||||
|
/* note: GetVersionEx() doesn't exist on WinNT 3.1 */
|
||||||
|
{
|
||||||
|
if(GetVersion() < 0x80000000)
|
||||||
|
{
|
||||||
|
(BOOL)dwPlatformId = TRUE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(BOOL)dwPlatformId = FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dwPlatformId;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* This is a very stripped down version of what is done in WiZ. Essentially
|
||||||
|
what this function is for is to do a listing of an archive contents. It
|
||||||
|
is actually never called in this example, but a dummy procedure had to
|
||||||
|
be put in, so this was used.
|
||||||
|
*/
|
||||||
|
void WINAPI ReceiveDllMessage(unsigned long ucsize,unsigned long csiz,
|
||||||
|
ush cfactor, ush mo, ush dy, ush yr, ush hh, ush mm,
|
||||||
|
char c, char *filename, char *methbuf, unsigned long crc, char fCrypt)
|
||||||
|
{
|
||||||
|
//char psLBEntry[PATH_MAX];
|
||||||
|
char LongHdrStats[] =
|
||||||
|
"%7lu %7lu %4s %02u-%02u-%02u %02u:%02u %c%s";
|
||||||
|
char CompFactorStr[] = "%c%d%%";
|
||||||
|
char CompFactor100[] = "100%%";
|
||||||
|
char szCompFactor[10];
|
||||||
|
char sgn;
|
||||||
|
|
||||||
|
if (csiz > ucsize)
|
||||||
|
sgn = '-';
|
||||||
|
else
|
||||||
|
sgn = ' ';
|
||||||
|
if (cfactor == 100)
|
||||||
|
lstrcpy(szCompFactor, CompFactor100);
|
||||||
|
else
|
||||||
|
sprintf(szCompFactor, CompFactorStr, sgn, cfactor);
|
||||||
|
//wsprintf(psLBEntry, LongHdrStats,
|
||||||
|
// ucsize, csiz, szCompFactor, mo, dy, yr, hh, mm, c, filename);
|
||||||
|
|
||||||
|
//printf("%s\n", psLBEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Password entry routine - see password.c in the wiz directory for how
|
||||||
|
this is actually implemented in WiZ. If you have an encrypted file,
|
||||||
|
this will probably give you great pain.
|
||||||
|
*/
|
||||||
|
int WINAPI password(char *p, char *m, int n, char *name)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dummy "print" routine that simply outputs what is sent from the dll */
|
||||||
|
int WINAPI DisplayBuf(char far *buf, unsigned long size)
|
||||||
|
{
|
||||||
|
//printf("%s", buf);
|
||||||
|
return (unsigned int) size;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
Example header file
|
||||||
|
|
||||||
|
Do not use this header file in the WiZ application, use WIZ.H
|
||||||
|
instead.
|
||||||
|
|
||||||
|
*/
|
||||||
|
#ifndef _EXAMPLE_H
|
||||||
|
#define _EXAMPLE_H
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
//#include <assert.h> /* required for all Windows applications */
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <commdlg.h>
|
||||||
|
#include <dlgs.h>
|
||||||
|
#include <windowsx.h>
|
||||||
|
|
||||||
|
|
||||||
|
//=============================== VERSION INFO ==============================
|
||||||
|
#ifndef __unzver_h /* prevent multiple inclusions */
|
||||||
|
#define __unzver_h
|
||||||
|
|
||||||
|
#define UNZ_DLL_VERSION "5.32\0"
|
||||||
|
#define COMPANY_NAME "Info-ZIP\0"
|
||||||
|
|
||||||
|
#endif /* __unzver_h */
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
|
||||||
|
//================================ STRUCTURES ===============================
|
||||||
|
#ifndef _STRUCTS_H
|
||||||
|
#define _STRUCTS_H
|
||||||
|
|
||||||
|
#ifndef Far
|
||||||
|
# define Far far
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Porting definitions between Win 3.1x and Win32 */
|
||||||
|
#ifdef WIN32
|
||||||
|
# define far
|
||||||
|
# define _far
|
||||||
|
# define __far
|
||||||
|
# define near
|
||||||
|
# define _near
|
||||||
|
# define __near
|
||||||
|
# ifndef FAR
|
||||||
|
# define FAR
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PATH_MAX
|
||||||
|
# define PATH_MAX 128 /* max total file or directory name path */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DEFINED_ONCE
|
||||||
|
#define DEFINED_ONCE
|
||||||
|
#ifndef ush
|
||||||
|
typedef unsigned short ush;
|
||||||
|
#endif
|
||||||
|
typedef int (WINAPI DLLPRNT) (LPSTR, unsigned long);
|
||||||
|
typedef int (WINAPI DLLPASSWORD) (char *, char *, int, char *);
|
||||||
|
#endif
|
||||||
|
typedef void (WINAPI DLLSND) (void);
|
||||||
|
typedef int (WINAPI DLLREPLACE)(LPSTR);
|
||||||
|
typedef void (WINAPI DLLMESSAGE)(unsigned long,unsigned long,
|
||||||
|
ush, ush, ush, ush, ush, ush, char, LPSTR, LPSTR, unsigned long, char);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
DLLPRNT *print;
|
||||||
|
DLLSND *sound;
|
||||||
|
DLLREPLACE *replace;
|
||||||
|
DLLPASSWORD *password;
|
||||||
|
DLLMESSAGE *SendApplicationMessage;
|
||||||
|
WORD cchComment;
|
||||||
|
unsigned long TotalSizeComp;
|
||||||
|
unsigned long TotalSize;
|
||||||
|
int CompFactor;
|
||||||
|
unsigned int NumMembers;
|
||||||
|
} USERFUNCTIONS, far * LPUSERFUNCTIONS;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int ExtractOnlyNewer;
|
||||||
|
int SpaceToUnderscore;
|
||||||
|
int PromptToOverwrite;
|
||||||
|
int fQuiet;
|
||||||
|
int ncflag;
|
||||||
|
int ntflag;
|
||||||
|
int nvflag;
|
||||||
|
int nUflag;
|
||||||
|
int nzflag;
|
||||||
|
int ndflag;
|
||||||
|
int noflag;
|
||||||
|
int naflag;
|
||||||
|
int nZIflag;
|
||||||
|
int C_flag;
|
||||||
|
int fPrivilege;
|
||||||
|
LPSTR lpszZipFN;
|
||||||
|
LPSTR lpszExtractDir;
|
||||||
|
} DCL, _far *LPDCL;
|
||||||
|
|
||||||
|
#endif /* _STRUCTS_H */
|
||||||
|
//=======================================================================
|
||||||
|
|
||||||
|
/* Defines */
|
||||||
|
#ifndef MSWIN
|
||||||
|
#define MSWIN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef int (WINAPI * _DLL_UNZIP)(int, char **, int, char **,
|
||||||
|
LPDCL, LPUSERFUNCTIONS);
|
||||||
|
typedef int (WINAPI * _USER_FUNCTIONS)(LPUSERFUNCTIONS);
|
||||||
|
|
||||||
|
/* Global variables */
|
||||||
|
|
||||||
|
extern LPUSERFUNCTIONS lpUserFunctions;
|
||||||
|
extern LPDCL lpDCL;
|
||||||
|
|
||||||
|
extern HINSTANCE hUnzipDll;
|
||||||
|
|
||||||
|
extern int hFile; /* file handle */
|
||||||
|
|
||||||
|
/* Global functions */
|
||||||
|
|
||||||
|
extern _DLL_UNZIP windll_unzip;
|
||||||
|
extern _USER_FUNCTIONS UzInit;
|
||||||
|
int WINAPI DisplayBuf(char far *, unsigned long int);
|
||||||
|
|
||||||
|
/* Procedure Calls */
|
||||||
|
void WINAPI ReceiveDllMessage(unsigned long,unsigned long,
|
||||||
|
ush, ush, ush, ush, ush, ush, char, char *, char *, unsigned long, char);
|
||||||
|
#endif /* _EXAMPLE_H */
|
|
@ -0,0 +1,982 @@
|
||||||
|
// g_utils.c -- misc utility functions for game module
|
||||||
|
|
||||||
|
#include "g_local.h"
|
||||||
|
void laserball_cleanup (edict_t *ent);//WF34
|
||||||
|
|
||||||
|
|
||||||
|
void G_ProjectSource (vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result)
|
||||||
|
{
|
||||||
|
result[0] = point[0] + forward[0] * distance[0] + right[0] * distance[1];
|
||||||
|
result[1] = point[1] + forward[1] * distance[0] + right[1] * distance[1];
|
||||||
|
result[2] = point[2] + forward[2] * distance[0] + right[2] * distance[1] + distance[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
=============
|
||||||
|
G_Find
|
||||||
|
|
||||||
|
Searches all active entities for the next one that holds
|
||||||
|
the matching string at fieldofs (use the FOFS() macro) in the structure.
|
||||||
|
|
||||||
|
Searches beginning at the edict after from, or the beginning if NULL
|
||||||
|
NULL will be returned if the end of the list is reached.
|
||||||
|
|
||||||
|
=============
|
||||||
|
*/
|
||||||
|
edict_t *G_Find (edict_t *from, int fieldofs, char *match)
|
||||||
|
{
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
if (!from)
|
||||||
|
from = g_edicts;
|
||||||
|
else
|
||||||
|
from++;
|
||||||
|
|
||||||
|
for ( ; from < &g_edicts[globals.num_edicts] ; from++)
|
||||||
|
{
|
||||||
|
if (!from->inuse)
|
||||||
|
continue;
|
||||||
|
s = *(char **) ((byte *)from + fieldofs);
|
||||||
|
if (!s)
|
||||||
|
continue;
|
||||||
|
if (!Q_stricmp (s, match))
|
||||||
|
return from;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
findradius
|
||||||
|
|
||||||
|
Returns entities that have origins within a spherical area
|
||||||
|
|
||||||
|
findradius (origin, radius)
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
edict_t *findradius (edict_t *from, vec3_t org, float rad)
|
||||||
|
{
|
||||||
|
vec3_t eorg;
|
||||||
|
int j;
|
||||||
|
//WF34 S ++TeT
|
||||||
|
float vecLength;
|
||||||
|
float radSquared = rad * rad;
|
||||||
|
//WF34 E --TeT
|
||||||
|
|
||||||
|
if (!from)
|
||||||
|
from = g_edicts;
|
||||||
|
else
|
||||||
|
from++;
|
||||||
|
for ( ; from < &g_edicts[globals.num_edicts]; from++)
|
||||||
|
{
|
||||||
|
if (!from->inuse)
|
||||||
|
continue;
|
||||||
|
if (from->solid == SOLID_NOT)
|
||||||
|
continue;
|
||||||
|
for (j=0 ; j<3 ; j++)
|
||||||
|
eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
|
||||||
|
//WF34 S ++TeT - inlined vectorLength func which returned sqrt of vecLength,
|
||||||
|
// instead we compare it to the square of rad
|
||||||
|
vecLength = 0;
|
||||||
|
vecLength += eorg[0]*eorg[0];
|
||||||
|
vecLength += eorg[1]*eorg[1];
|
||||||
|
vecLength += eorg[2]*eorg[2];
|
||||||
|
if (vecLength > radSquared)
|
||||||
|
continue;
|
||||||
|
// --TeT
|
||||||
|
return from;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
findEnemyWithinRadius
|
||||||
|
|
||||||
|
Returns entities that have origins within a spherical area
|
||||||
|
|
||||||
|
findEnemyWithinRadius (origin, radius)
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
edict_t *findEnemyWithinRadius (edict_t *self, edict_t *from, vec3_t org, float rad)
|
||||||
|
{
|
||||||
|
vec3_t eorg;
|
||||||
|
int j;
|
||||||
|
//WF34 S ++TeT
|
||||||
|
float vecLength;
|
||||||
|
float radSquared = rad * rad;
|
||||||
|
//WF34 E --TeT
|
||||||
|
|
||||||
|
if (!from)
|
||||||
|
from = g_edicts;
|
||||||
|
else
|
||||||
|
from++;
|
||||||
|
for ( ; from < &g_edicts[globals.num_edicts]; from++)
|
||||||
|
{
|
||||||
|
if (!from->inuse)
|
||||||
|
continue;
|
||||||
|
if (from->solid == SOLID_NOT)
|
||||||
|
continue;
|
||||||
|
if (!(from->svflags & SVF_MONSTER) && !from->client)
|
||||||
|
continue;
|
||||||
|
//dont attack same team
|
||||||
|
if (from->wf_team == self->wf_team)
|
||||||
|
continue;
|
||||||
|
if (from->disguised)
|
||||||
|
continue;
|
||||||
|
if (!from->takedamage)
|
||||||
|
continue;
|
||||||
|
if (from->health <= 0)
|
||||||
|
continue;
|
||||||
|
if (!visible(self, from))
|
||||||
|
continue;
|
||||||
|
for (j=0 ; j<3 ; j++)
|
||||||
|
eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5);
|
||||||
|
//WF34 S ++TeT - inlined vectorLength func which returned sqrt of vecLength,
|
||||||
|
// instead we compare it to the square of rad
|
||||||
|
vecLength = 0;
|
||||||
|
vecLength += eorg[0]*eorg[0];
|
||||||
|
vecLength += eorg[1]*eorg[1];
|
||||||
|
vecLength += eorg[2]*eorg[2];
|
||||||
|
if (vecLength > radSquared)
|
||||||
|
continue;
|
||||||
|
// --TeT
|
||||||
|
return from;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=============
|
||||||
|
G_PickTarget
|
||||||
|
|
||||||
|
Searches all active entities for the next one that holds
|
||||||
|
the matching string at fieldofs (use the FOFS() macro) in the structure.
|
||||||
|
|
||||||
|
Searches beginning at the edict after from, or the beginning if NULL
|
||||||
|
NULL will be returned if the end of the list is reached.
|
||||||
|
|
||||||
|
=============
|
||||||
|
*/
|
||||||
|
#define MAXCHOICES 8
|
||||||
|
|
||||||
|
edict_t *G_PickTarget (char *targetname)
|
||||||
|
{
|
||||||
|
edict_t *ent = NULL;
|
||||||
|
int num_choices = 0;
|
||||||
|
edict_t *choice[MAXCHOICES];
|
||||||
|
|
||||||
|
if (!targetname)
|
||||||
|
{
|
||||||
|
gi.dprintf("G_PickTarget called with NULL targetname\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
ent = G_Find (ent, FOFS(targetname), targetname);
|
||||||
|
if (!ent)
|
||||||
|
break;
|
||||||
|
choice[num_choices++] = ent;
|
||||||
|
if (num_choices == MAXCHOICES)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!num_choices)
|
||||||
|
{
|
||||||
|
gi.dprintf("G_PickTarget: target %s not found\n", targetname);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return choice[rand() % num_choices];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Think_Delay (edict_t *ent)
|
||||||
|
{
|
||||||
|
G_UseTargets (ent, ent->activator);
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================
|
||||||
|
G_UseTargets
|
||||||
|
|
||||||
|
the global "activator" should be set to the entity that initiated the firing.
|
||||||
|
|
||||||
|
If self.delay is set, a DelayedUse entity will be created that will actually
|
||||||
|
do the SUB_UseTargets after that many seconds have passed.
|
||||||
|
|
||||||
|
Centerprints any self.message to the activator.
|
||||||
|
|
||||||
|
Search for (string)targetname in all entities that
|
||||||
|
match (string)self.target and call their .use function
|
||||||
|
|
||||||
|
==============================
|
||||||
|
*/
|
||||||
|
void G_UseTargets (edict_t *ent, edict_t *activator)
|
||||||
|
{
|
||||||
|
edict_t *t;
|
||||||
|
|
||||||
|
//
|
||||||
|
// check for a delay
|
||||||
|
//WF34 S
|
||||||
|
//if (wfdebug) gi.dprintf("G_UseTargets:1. Ent = %s, Activator=%s\n",
|
||||||
|
// ent->classname, activator->classname);
|
||||||
|
//WF34 E
|
||||||
|
if (ent->delay)
|
||||||
|
{
|
||||||
|
// create a temp object to fire at a later time
|
||||||
|
t = G_Spawn();
|
||||||
|
t->classname = "DelayedUse";
|
||||||
|
t->nextthink = level.time + ent->delay;
|
||||||
|
t->think = Think_Delay;
|
||||||
|
t->activator = activator;
|
||||||
|
if (!activator)
|
||||||
|
gi.dprintf ("Think_Delay with no activator\n");
|
||||||
|
t->message = ent->message;
|
||||||
|
t->target = ent->target;
|
||||||
|
t->killtarget = ent->killtarget;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// print the message
|
||||||
|
//
|
||||||
|
if ((ent->message) && !(activator->svflags & SVF_MONSTER))
|
||||||
|
|
||||||
|
{
|
||||||
|
gi.centerprintf (activator, "%s", ent->message);
|
||||||
|
|
||||||
|
if (ent->noise_index)
|
||||||
|
|
||||||
|
gi.sound (activator, CHAN_AUTO, ent->noise_index, 1, ATTN_NORM, 0);
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
gi.sound (activator, CHAN_AUTO, gi.soundindex ("misc/talk1.wav"), 1, ATTN_NORM, 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// kill killtargets
|
||||||
|
//
|
||||||
|
|
||||||
|
if (ent->killtarget)
|
||||||
|
{
|
||||||
|
t = NULL;
|
||||||
|
while ((t = G_Find (t, FOFS(targetname), ent->killtarget)))
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
G_FreeEdict (t);
|
||||||
|
|
||||||
|
if (!ent->inuse)
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
gi.dprintf("entity was removed while using killtargets\n");
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// gi.dprintf("TARGET: activating %s\n", ent->target);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// fire targets
|
||||||
|
//
|
||||||
|
//if (wfdebug) gi.dprintf("G_UseTargets:4\n");
|
||||||
|
|
||||||
|
if (ent->target)
|
||||||
|
{
|
||||||
|
t = NULL;
|
||||||
|
while ((t = G_Find (t, FOFS(targetname), ent->target)))
|
||||||
|
{
|
||||||
|
// doors fire area portals in a specific way
|
||||||
|
if (!Q_stricmp(t->classname, "func_areaportal") &&
|
||||||
|
(!Q_stricmp(ent->classname, "func_door") || !Q_stricmp(ent->classname, "func_door_rotating")))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (t == ent)
|
||||||
|
{
|
||||||
|
gi.dprintf ("WARNING: Entity used itself.\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{//WF34S
|
||||||
|
//if (wfdebug) gi.dprintf("G_UseTargets: 4a-use. Target=%s\n", t->classname);
|
||||||
|
if (t->use)
|
||||||
|
t->use (t, ent, activator);
|
||||||
|
}
|
||||||
|
if (!ent->inuse)
|
||||||
|
{
|
||||||
|
gi.dprintf("entity was removed while using targets\n");
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//if (wfdebug) gi.dprintf("G_UseTargets:5\n");//WF34
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
=============
|
||||||
|
TempVector
|
||||||
|
|
||||||
|
This is just a convenience function
|
||||||
|
for making temporary vectors for function calls
|
||||||
|
=============
|
||||||
|
*/
|
||||||
|
float *tv (float x, float y, float z)
|
||||||
|
{
|
||||||
|
static int index;
|
||||||
|
static vec3_t vecs[8];
|
||||||
|
float *v;
|
||||||
|
|
||||||
|
// use an array so that multiple tempvectors won't collide
|
||||||
|
// for a while
|
||||||
|
v = vecs[index];
|
||||||
|
index = (index + 1)&7;
|
||||||
|
|
||||||
|
v[0] = x;
|
||||||
|
v[1] = y;
|
||||||
|
v[2] = z;
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
=============
|
||||||
|
VectorToString
|
||||||
|
|
||||||
|
This is just a convenience function
|
||||||
|
for printing vectors
|
||||||
|
=============
|
||||||
|
*/
|
||||||
|
char *vtos (vec3_t v)
|
||||||
|
{
|
||||||
|
static int index;
|
||||||
|
static char str[8][32];
|
||||||
|
char *s;
|
||||||
|
|
||||||
|
// use an array so that multiple vtos won't collide
|
||||||
|
s = str[index];
|
||||||
|
index = (index + 1)&7;
|
||||||
|
|
||||||
|
Com_sprintf (s, 32, "(%i %i %i)", (int)v[0], (int)v[1], (int)v[2]);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
vec3_t VEC_UP = {0, -1, 0};
|
||||||
|
vec3_t MOVEDIR_UP = {0, 0, 1};
|
||||||
|
vec3_t VEC_DOWN = {0, -2, 0};
|
||||||
|
vec3_t MOVEDIR_DOWN = {0, 0, -1};
|
||||||
|
|
||||||
|
void G_SetMovedir (vec3_t angles, vec3_t movedir)
|
||||||
|
{
|
||||||
|
if (VectorCompare (angles, VEC_UP))
|
||||||
|
{
|
||||||
|
VectorCopy (MOVEDIR_UP, movedir);
|
||||||
|
}
|
||||||
|
else if (VectorCompare (angles, VEC_DOWN))
|
||||||
|
{
|
||||||
|
VectorCopy (MOVEDIR_DOWN, movedir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AngleVectors (angles, movedir, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorClear (angles);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float vectoyaw (vec3_t vec)
|
||||||
|
{
|
||||||
|
float yaw;
|
||||||
|
|
||||||
|
if (/*vec[YAW] == 0 &&*/ vec[PITCH] == 0)
|
||||||
|
{
|
||||||
|
yaw = 0;
|
||||||
|
if (vec[YAW] > 0)
|
||||||
|
yaw = 90;
|
||||||
|
else if (vec[YAW] < 0)
|
||||||
|
yaw = -90;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
yaw = (int) (atan2(vec[YAW], vec[PITCH]) * 180 / M_PI);
|
||||||
|
if (yaw < 0)
|
||||||
|
yaw += 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
return yaw;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void vectoangles (vec3_t value1, vec3_t angles)
|
||||||
|
{
|
||||||
|
float forward;
|
||||||
|
float yaw, pitch;
|
||||||
|
|
||||||
|
if (value1[1] == 0 && value1[0] == 0)
|
||||||
|
{
|
||||||
|
yaw = 0;
|
||||||
|
if (value1[2] > 0)
|
||||||
|
pitch = 90;
|
||||||
|
else
|
||||||
|
pitch = 270;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (value1[0])
|
||||||
|
yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
|
||||||
|
else if (value1[1] > 0)
|
||||||
|
yaw = 90;
|
||||||
|
else
|
||||||
|
yaw = -90;
|
||||||
|
if (yaw < 0)
|
||||||
|
yaw += 360;
|
||||||
|
|
||||||
|
forward = sqrt (value1[0]*value1[0] + value1[1]*value1[1]);
|
||||||
|
pitch = (int) (atan2(value1[2], forward) * 180 / M_PI);
|
||||||
|
if (pitch < 0)
|
||||||
|
pitch += 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
angles[PITCH] = -pitch;
|
||||||
|
angles[YAW] = yaw;
|
||||||
|
angles[ROLL] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *G_CopyString (char *in)
|
||||||
|
{
|
||||||
|
char *out;
|
||||||
|
|
||||||
|
out = gi.TagMalloc (strlen(in)+1, TAG_LEVEL);
|
||||||
|
strcpy (out, in);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void G_InitEdict (edict_t *e)
|
||||||
|
{
|
||||||
|
e->inuse = true;
|
||||||
|
e->classname = "noclass";
|
||||||
|
e->gravity = 1.0;
|
||||||
|
e->s.number = e - g_edicts;
|
||||||
|
//ERASER START
|
||||||
|
e->trail_index = 0;
|
||||||
|
e->closest_trail = 0;
|
||||||
|
e->ignore_time = 0;
|
||||||
|
e->last_reached_trail = 0;
|
||||||
|
//ERASER END
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
G_Spawn
|
||||||
|
|
||||||
|
Either finds a free edict, or allocates a new one.
|
||||||
|
Try to avoid reusing an entity that was recently freed, because it
|
||||||
|
can cause the client to think the entity morphed into something else
|
||||||
|
instead of being removed and recreated, which can cause interpolated
|
||||||
|
angles and bad trails.
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
edict_t *G_Spawn (void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
edict_t *e;
|
||||||
|
|
||||||
|
e = &g_edicts[(int)maxclients->value+1];
|
||||||
|
for ( i=maxclients->value+1 ; i<globals.num_edicts ; i++, e++)
|
||||||
|
{
|
||||||
|
// the first couple seconds of server time can involve a lot of
|
||||||
|
// freeing and allocating, so relax the replacement policy
|
||||||
|
if (!e->inuse && ( e->freetime < 2 || level.time - e->freetime > 0.5 ) )
|
||||||
|
{
|
||||||
|
G_InitEdict (e);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == game.maxentities)
|
||||||
|
gi.error ("ED_Alloc: no free edicts");
|
||||||
|
|
||||||
|
globals.num_edicts++;
|
||||||
|
G_InitEdict (e);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
G_FreeEdict
|
||||||
|
|
||||||
|
Marks the edict as free
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
void G_FreeEdict (edict_t *ed)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (wfdebug)
|
||||||
|
{
|
||||||
|
// gi.dprintf("G_FreeEdict: %s\n", ed->classname);
|
||||||
|
}
|
||||||
|
//WF34 S Check for entity limits
|
||||||
|
if ((ed->grenade_index) && (ed->owner) && (ed->owner->client))
|
||||||
|
{
|
||||||
|
--ed->owner->client->pers.active_grenades[ed->grenade_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ed->special_index) //if set, this is an index into the active_special array
|
||||||
|
{
|
||||||
|
--ed->owner->client->pers.active_special[ed->special_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
//Special handling for other WF items
|
||||||
|
|
||||||
|
//Sentry gun
|
||||||
|
if (!strcmp(ed->classname, "SentryGun") )
|
||||||
|
{
|
||||||
|
if (ed->sentry) ed->sentry->health = 0;
|
||||||
|
if (ed->creator) ed->creator->sentry = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Supply depot
|
||||||
|
else if (!strcmp(ed->classname, "depot") )
|
||||||
|
{
|
||||||
|
if (ed->owner) ed->owner->supply = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Healing depot
|
||||||
|
else if (!strcmp(ed->classname, "healingdepot") )
|
||||||
|
{
|
||||||
|
if (ed->owner) ed->owner->supply = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Laserball
|
||||||
|
else if (!strcmp(ed->classname, "laserball") )
|
||||||
|
{
|
||||||
|
laserball_cleanup(ed);
|
||||||
|
}
|
||||||
|
//WF34 E
|
||||||
|
gi.unlinkentity (ed); // unlink from world
|
||||||
|
|
||||||
|
if ((ed - g_edicts) <= (maxclients->value + BODY_QUEUE_SIZE))
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
// gi.dprintf("tried to free special edict\n");
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
memset (ed, 0, sizeof(*ed));
|
||||||
|
ed->classname = "freed";
|
||||||
|
ed->freetime = level.time;
|
||||||
|
ed->inuse = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
G_TouchTriggers
|
||||||
|
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
void G_TouchTriggers (edict_t *ent)
|
||||||
|
{
|
||||||
|
int i, num;
|
||||||
|
edict_t *touch[MAX_EDICTS], *hit;
|
||||||
|
|
||||||
|
|
||||||
|
// dead things don't activate triggers!
|
||||||
|
|
||||||
|
if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
|
||||||
|
, MAX_EDICTS, AREA_TRIGGERS);
|
||||||
|
|
||||||
|
// be careful, it is possible to have an entity in this
|
||||||
|
// list removed before we get to it (killtriggered)
|
||||||
|
for (i=0 ; i<num ; i++)
|
||||||
|
{
|
||||||
|
hit = touch[i];
|
||||||
|
if (!hit->inuse)
|
||||||
|
continue;
|
||||||
|
if (!hit->touch)
|
||||||
|
continue;
|
||||||
|
hit->touch (hit, ent, NULL, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
============
|
||||||
|
G_TouchSolids
|
||||||
|
|
||||||
|
Call after linking a new trigger in during gameplay
|
||||||
|
to force all entities it covers to immediately touch it
|
||||||
|
============
|
||||||
|
*/
|
||||||
|
void G_TouchSolids (edict_t *ent)
|
||||||
|
{
|
||||||
|
int i, num;
|
||||||
|
edict_t *touch[MAX_EDICTS], *hit;
|
||||||
|
|
||||||
|
num = gi.BoxEdicts (ent->absmin, ent->absmax, touch
|
||||||
|
, MAX_EDICTS, AREA_SOLID);
|
||||||
|
|
||||||
|
// be careful, it is possible to have an entity in this
|
||||||
|
// list removed before we get to it (killtriggered)
|
||||||
|
for (i=0 ; i<num ; i++)
|
||||||
|
{
|
||||||
|
hit = touch[i];
|
||||||
|
if (!hit->inuse)
|
||||||
|
continue;
|
||||||
|
if (ent->touch)
|
||||||
|
ent->touch (hit, ent, NULL, NULL);
|
||||||
|
if (!ent->inuse)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*=================
|
||||||
|
* KillBox
|
||||||
|
*
|
||||||
|
* Kills all entities that would touch the proposed new positioning
|
||||||
|
* of ent. Ent should be unlinked before calling this!
|
||||||
|
* =================*/
|
||||||
|
qboolean KillBox (edict_t *ent)
|
||||||
|
{
|
||||||
|
trace_t tr;
|
||||||
|
int i;//ERASER
|
||||||
|
edict_t *trav;//ERASER
|
||||||
|
|
||||||
|
gi.unlinkentity(ent);//ERASER
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
|
||||||
|
if (!tr.ent)
|
||||||
|
break;
|
||||||
|
|
||||||
|
//WF & K2:Begin
|
||||||
|
// ++TeT
|
||||||
|
if (!strcmp(tr.ent->classname, "SentryStand"))
|
||||||
|
{
|
||||||
|
tr.ent = tr.ent->creator;
|
||||||
|
tr.ent->noteamdamage = false;
|
||||||
|
}
|
||||||
|
if (!strcmp(tr.ent->classname, "biosentry"))
|
||||||
|
{
|
||||||
|
tr.ent->noteamdamage = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --TeT
|
||||||
|
// ++TeT Removed Reverse Telefrag - If they weren't smart enough to get out of the way, kill em
|
||||||
|
//Need to check for reverse telefrag condition for respawn protection
|
||||||
|
// if(K2_IsProtected(tr.ent) && !K2_IsProtected(ent) )
|
||||||
|
// {
|
||||||
|
// //Reverse Telefrag
|
||||||
|
// T_Damage (ent,tr.ent,tr.ent,vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_REVERSE_TELEFRAG);
|
||||||
|
// }
|
||||||
|
else
|
||||||
|
//WF & K2:End
|
||||||
|
// nail it
|
||||||
|
// --TeT remove reverse telefrag
|
||||||
|
T_Damage (tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
|
||||||
|
|
||||||
|
// if we didn't kill it, fail
|
||||||
|
if (tr.ent->solid)
|
||||||
|
//ERASER START
|
||||||
|
{
|
||||||
|
// if (tr.ent == world)
|
||||||
|
break;//ERASER
|
||||||
|
|
||||||
|
// gi.dprintf("Couldn't KillBox(), something is wrong.\n");
|
||||||
|
// return false;//ERASER ,WF USES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fixes wierd teleporter/spawning inside others
|
||||||
|
i = -1;
|
||||||
|
while (++i < num_players)
|
||||||
|
{
|
||||||
|
if ((trav = players[i]) == ent)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (entdist(trav, ent) > 42)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// nail it
|
||||||
|
T_Damage (trav, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG);
|
||||||
|
//ERASER END
|
||||||
|
//??FIXME return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return true; // all clear
|
||||||
|
|
||||||
|
}
|
||||||
|
//ERASER START
|
||||||
|
// Ridah
|
||||||
|
/*
|
||||||
|
==================
|
||||||
|
stuffcmd
|
||||||
|
|
||||||
|
QC equivalent, sends a command to the client's consol
|
||||||
|
==================
|
||||||
|
*//* ACRID
|
||||||
|
void stuffcmd(edict_t *ent, char *text)
|
||||||
|
{
|
||||||
|
gi.WriteByte(11); // 11 = svc_stufftext
|
||||||
|
gi.WriteString(text);
|
||||||
|
gi.unicast(ent, 1);
|
||||||
|
}*///ACRID
|
||||||
|
|
||||||
|
float entdist(edict_t *ent1, edict_t *ent2)
|
||||||
|
{
|
||||||
|
vec3_t vec;
|
||||||
|
|
||||||
|
VectorSubtract(ent1->s.origin, ent2->s.origin, vec);
|
||||||
|
return VectorLength(vec);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
AddModelSkin
|
||||||
|
|
||||||
|
Adds a skin reference to an .md2 file, saving as filename.md2new
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
void AddModelSkin (char *modelfile, char *skinname)
|
||||||
|
{
|
||||||
|
FILE *f, *out;
|
||||||
|
int count = 0, buffer_int, i;
|
||||||
|
char filename[256], infilename[256];
|
||||||
|
char buffer;
|
||||||
|
// botDebugPrint("ADDMODELSKIN (ACRID)\n");
|
||||||
|
i = sprintf(infilename , modelfile);
|
||||||
|
|
||||||
|
f = fopen (infilename, "rb");
|
||||||
|
if (!f)
|
||||||
|
{
|
||||||
|
gi.dprintf("Cannot open file %s\n", infilename);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = sprintf(filename, ".\\");
|
||||||
|
i += sprintf(filename + i, GAMEVERSION);
|
||||||
|
i += sprintf(filename + i, "\\");
|
||||||
|
i += sprintf(filename + i, modelfile);
|
||||||
|
i += sprintf(filename + i, "new");
|
||||||
|
|
||||||
|
out = fopen (filename, "wb");
|
||||||
|
if (!out)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// mirror header stuff before skinnum
|
||||||
|
for (i=0; i<5; i++)
|
||||||
|
{
|
||||||
|
fread(&buffer_int, sizeof(buffer_int), 1, f);
|
||||||
|
fwrite(&buffer_int, sizeof(buffer_int), 1, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// increment skinnum
|
||||||
|
fread(&buffer_int, sizeof(buffer_int), 1, f);
|
||||||
|
++buffer_int;
|
||||||
|
fwrite(&buffer_int, sizeof(buffer_int), 1, out);
|
||||||
|
|
||||||
|
// mirror header stuff before skin_ofs
|
||||||
|
for (i=0; i<5; i++)
|
||||||
|
{
|
||||||
|
fread(&buffer_int, sizeof(buffer_int), 1, f);
|
||||||
|
fwrite(&buffer_int, sizeof(buffer_int), 1, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy the skins offset value, since it doesn't change
|
||||||
|
fread(&buffer_int, sizeof(buffer_int), 1, f);
|
||||||
|
fwrite(&buffer_int, sizeof(buffer_int), 1, out);
|
||||||
|
|
||||||
|
// increment all offsets by 64 to make way for new skin
|
||||||
|
for (i=0; i<5; i++)
|
||||||
|
{
|
||||||
|
fread(&buffer_int, sizeof(buffer_int), 1, f);
|
||||||
|
buffer_int += 64;
|
||||||
|
fwrite(&buffer_int, sizeof(buffer_int), 1, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the new skin
|
||||||
|
for (i=0; i<strlen(skinname); i++)
|
||||||
|
{ // botDebugPrint("SKIN K (ACRID)\n");
|
||||||
|
fwrite(&(skinname[i]), 1, 1, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = '\0';
|
||||||
|
fwrite(&buffer, 1, 1, out);
|
||||||
|
buffer = 'x';
|
||||||
|
fwrite(&buffer, 1, 1, out);
|
||||||
|
|
||||||
|
buffer = '\0';
|
||||||
|
for (i=(strlen(skinname)+2); i<64; i++)
|
||||||
|
{
|
||||||
|
fwrite(&buffer, 1, 1, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy the rest of the file
|
||||||
|
fread(&buffer, sizeof(buffer), 1, f);
|
||||||
|
while (!feof(f))
|
||||||
|
{
|
||||||
|
fwrite(&buffer, sizeof(buffer), 1, out);
|
||||||
|
fread(&buffer, sizeof(buffer), 1, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose (f);
|
||||||
|
fclose (out);
|
||||||
|
|
||||||
|
// copy the new file over the old file
|
||||||
|
remove(infilename);
|
||||||
|
rename(filename, infilename);
|
||||||
|
|
||||||
|
gi.dprintf("Model skin added.\n", filename);
|
||||||
|
// botDebugPrint("SKIN M (ACRID)\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void my_bprintf (int printlevel, char *fmt, ...)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char bigbuffer[0x10000];
|
||||||
|
int len;
|
||||||
|
va_list argptr;
|
||||||
|
edict_t *cl_ent;
|
||||||
|
|
||||||
|
va_start (argptr,fmt);
|
||||||
|
len = vsprintf (bigbuffer,fmt,argptr);
|
||||||
|
va_end (argptr);
|
||||||
|
|
||||||
|
if (dedicated->value)
|
||||||
|
safe_cprintf(NULL, printlevel, bigbuffer);
|
||||||
|
|
||||||
|
for (i=0 ; i<maxclients->value ; i++)
|
||||||
|
{
|
||||||
|
cl_ent = g_edicts + 1 + i;
|
||||||
|
if (!cl_ent->inuse || cl_ent->bot_client)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
safe_cprintf(cl_ent, printlevel, bigbuffer);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
for (i=0; i<num_players; i++)
|
||||||
|
{
|
||||||
|
if (!players[i]->bot_client)
|
||||||
|
safe_cprintf(players[i], printlevel, bigbuffer);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
//======================================================================
|
||||||
|
// New ACE-compatible message routines
|
||||||
|
|
||||||
|
// botsafe cprintf
|
||||||
|
void safe_cprintf (edict_t *ent, int printlevel, char *fmt, ...)
|
||||||
|
{
|
||||||
|
char bigbuffer[0x10000];
|
||||||
|
va_list argptr;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
va_start (argptr,fmt);
|
||||||
|
len = vsprintf (bigbuffer,fmt,argptr);
|
||||||
|
va_end (argptr);
|
||||||
|
|
||||||
|
if (!ent)
|
||||||
|
{
|
||||||
|
gi.cprintf(ent, printlevel, bigbuffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ent->inuse || ent->isbot || ent->bot_client)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!ent->client) return;
|
||||||
|
|
||||||
|
|
||||||
|
gi.cprintf(ent, printlevel, bigbuffer);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// botsafe centerprintf
|
||||||
|
void safe_centerprintf (edict_t *ent, char *fmt, ...)
|
||||||
|
{
|
||||||
|
char bigbuffer[0x10000];
|
||||||
|
va_list argptr;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (!ent->inuse || ent->isbot || ent->bot_client)
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_start (argptr,fmt);
|
||||||
|
len = vsprintf (bigbuffer,fmt,argptr);
|
||||||
|
va_end (argptr);
|
||||||
|
|
||||||
|
gi.centerprintf(ent, bigbuffer);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// botsafe bprintf
|
||||||
|
void safe_bprintf (int printlevel, char *fmt, ...)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
char bigbuffer[0x10000];
|
||||||
|
int len;
|
||||||
|
va_list argptr;
|
||||||
|
edict_t *cl_ent;
|
||||||
|
|
||||||
|
va_start (argptr,fmt);
|
||||||
|
len = vsprintf (bigbuffer,fmt,argptr);
|
||||||
|
va_end (argptr);
|
||||||
|
|
||||||
|
|
||||||
|
if (dedicated->value)
|
||||||
|
safe_cprintf(NULL, printlevel, bigbuffer);
|
||||||
|
|
||||||
|
// This is to be compatible with Eraser (ACE)
|
||||||
|
//for (i=0; i<num_players; i++)
|
||||||
|
//{
|
||||||
|
// Ridah, changed this so CAM works REMOVE THIS
|
||||||
|
for (i=0 ; i<maxclients->value ; i++)
|
||||||
|
{
|
||||||
|
cl_ent = g_edicts + 1 + i;
|
||||||
|
|
||||||
|
if (cl_ent->inuse && !cl_ent->bot_client && !cl_ent->isbot)
|
||||||
|
safe_cprintf(cl_ent, printlevel, bigbuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//ERASER END
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,284 @@
|
||||||
|
/*
|
||||||
|
A very simplistic example of how to load the zip dll and make a call into it.
|
||||||
|
Note that none of the command line options are implemented in this example.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#define WIN32
|
||||||
|
#define API
|
||||||
|
|
||||||
|
#include "g_local.h"
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <direct.h>
|
||||||
|
#include "g_zip.h"
|
||||||
|
|
||||||
|
//======================= VERSION INFO ==========================
|
||||||
|
#ifndef __zipver_h /* prevent multiple inclusions */
|
||||||
|
#define __zipver_h
|
||||||
|
|
||||||
|
#define ZIP_DLL_VERSION "2.2\0"
|
||||||
|
#define COMPANY_NAME "Info-ZIP\0"
|
||||||
|
|
||||||
|
#endif /* __zipver_h */
|
||||||
|
//===============================================================
|
||||||
|
|
||||||
|
//#include <commctrl.h>
|
||||||
|
#include <winver.h>
|
||||||
|
|
||||||
|
#define ZIP_DLL_NAME "ZIP32.DLL\0"
|
||||||
|
|
||||||
|
#define DLL_WARNING "Cannot find %s."\
|
||||||
|
" The Dll must be in the application directory, the path, "\
|
||||||
|
"the Windows directory or the Windows System directory."
|
||||||
|
#define DLL_VERSION_WARNING "%s has the wrong version number."\
|
||||||
|
" Insure that you have the correct dll's installed, and that "\
|
||||||
|
"an older dll is not in your path or Windows System directory."
|
||||||
|
|
||||||
|
int hFile; /* file handle */
|
||||||
|
|
||||||
|
ZCL ZpZCL;
|
||||||
|
LPZIPUSERFUNCTIONS lpZipUserFunctions;
|
||||||
|
HANDLE hZUF = (HANDLE)NULL;
|
||||||
|
HINSTANCE hUnzipDll;
|
||||||
|
HANDLE hFileList;
|
||||||
|
ZPOPT ZpOpt;
|
||||||
|
extern DWORD dwPlatformId;
|
||||||
|
HINSTANCE hZipDll;
|
||||||
|
|
||||||
|
|
||||||
|
/* Forward References */
|
||||||
|
_DLL_ZIP ZpArchive;
|
||||||
|
_ZIP_USER_FUNCTIONS ZpInit;
|
||||||
|
ZIPSETOPTIONS ZpSetOptions;
|
||||||
|
|
||||||
|
void FreeUpMemoryZip(void);
|
||||||
|
int WINAPI DummyPassword(char *, char *, int, char *);
|
||||||
|
int far DummyPrint(FILE *, unsigned int, char *);
|
||||||
|
char far * WINAPI DummyComment(char far *);
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
extern BOOL IsNT(VOID);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
|
||||||
|
FUNCTION: Main(int argc, char **argv)
|
||||||
|
|
||||||
|
****************************************************************************/
|
||||||
|
#ifdef __BORLANDC__
|
||||||
|
# ifdef WIN32
|
||||||
|
#pragma argsused
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Zips up filename into zipfile
|
||||||
|
// NOTE: make sure the current dir is the directory in which the .dll files are
|
||||||
|
// in before calling this!
|
||||||
|
int G_ZipFile(char *zipfile, char *filename)
|
||||||
|
{
|
||||||
|
LPSTR szFileList;
|
||||||
|
char **index, *sz;
|
||||||
|
int retcode/*, i*/, cc;
|
||||||
|
//DWORD dwVerInfoSize;
|
||||||
|
//DWORD dwVerHnd;
|
||||||
|
char szFullPath[PATH_MAX];
|
||||||
|
char *ptr;
|
||||||
|
//HANDLE hMem; /* handle to mem alloc'ed */
|
||||||
|
|
||||||
|
if (!filename || !zipfile)
|
||||||
|
return 0; /* Exits if not proper number of arguments */
|
||||||
|
|
||||||
|
hZUF = GlobalAlloc( GPTR, (DWORD)sizeof(ZIPUSERFUNCTIONS));
|
||||||
|
if (!hZUF)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
lpZipUserFunctions = (LPZIPUSERFUNCTIONS)GlobalLock(hZUF);
|
||||||
|
|
||||||
|
if (!lpZipUserFunctions)
|
||||||
|
{
|
||||||
|
GlobalFree(hZUF);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
lpZipUserFunctions->print = &DummyPrint;
|
||||||
|
lpZipUserFunctions->password = DummyPassword;
|
||||||
|
lpZipUserFunctions->comment = DummyComment;
|
||||||
|
|
||||||
|
/* Let's go find the dll */
|
||||||
|
if (SearchPath(
|
||||||
|
NULL, /* address of search path */
|
||||||
|
ZIP_DLL_NAME, /* address of filename */
|
||||||
|
NULL, /* address of extension */
|
||||||
|
PATH_MAX, /* size, in characters, of buffer */
|
||||||
|
szFullPath, /* address of buffer for found filename */
|
||||||
|
&ptr /* address of pointer to file component */
|
||||||
|
) == 0)
|
||||||
|
{
|
||||||
|
// char str[256];
|
||||||
|
// wsprintf (str, DLL_WARNING, ZIP_DLL_NAME);
|
||||||
|
// printf("%s\n", str);
|
||||||
|
FreeUpMemoryZip();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Okay, now we know that the dll exists, and has the proper version
|
||||||
|
* information in it. We can go ahead and load it.
|
||||||
|
*/
|
||||||
|
hZipDll = LoadLibrary(ZIP_DLL_NAME);
|
||||||
|
if (hZipDll != NULL)
|
||||||
|
{
|
||||||
|
(_DLL_ZIP)ZpArchive = (_DLL_ZIP)GetProcAddress(hZipDll, "ZpArchive");
|
||||||
|
(ZIPSETOPTIONS)ZpSetOptions = (ZIPSETOPTIONS)GetProcAddress(hZipDll, "ZpSetOptions");
|
||||||
|
if (!ZpArchive || !ZpSetOptions)
|
||||||
|
{
|
||||||
|
// char str[256];
|
||||||
|
// wsprintf (str, "Could not get entry point to %s", ZIP_DLL_NAME);
|
||||||
|
// MessageBox((HWND)NULL, str, "Info-ZIP Example", MB_ICONSTOP | MB_OK);
|
||||||
|
FreeUpMemoryZip();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// char str[256];
|
||||||
|
// wsprintf (str, "Could not load %s", ZIP_DLL_NAME);
|
||||||
|
// printf("%s\n", str);
|
||||||
|
FreeUpMemoryZip();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
(_ZIP_USER_FUNCTIONS)ZpInit = (_ZIP_USER_FUNCTIONS)GetProcAddress(hZipDll, "ZpInit");
|
||||||
|
if (!ZpInit)
|
||||||
|
{
|
||||||
|
// printf("Cannot get address of ZpInit in Zip dll. Terminating...");
|
||||||
|
FreeLibrary(hZipDll);
|
||||||
|
FreeUpMemoryZip();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (!(*ZpInit)(lpZipUserFunctions))
|
||||||
|
{
|
||||||
|
// printf("Application functions not set up properly. Terminating...");
|
||||||
|
FreeLibrary(hZipDll);
|
||||||
|
FreeUpMemoryZip();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Here is where the action starts */
|
||||||
|
ZpOpt.fSuffix = FALSE; /* include suffixes (not yet implemented) */
|
||||||
|
ZpOpt.fEncrypt = FALSE; /* true if encryption wanted */
|
||||||
|
ZpOpt.fSystem = FALSE; /* true to include system/hidden files */
|
||||||
|
ZpOpt.fVolume = FALSE; /* true if storing volume label */
|
||||||
|
ZpOpt.fExtra = FALSE; /* true if including extra attributes */
|
||||||
|
ZpOpt.fNoDirEntries = FALSE; /* true if ignoring directory entries */
|
||||||
|
//ZpOpt.fDate = FALSE; /* true if excluding files earlier than a
|
||||||
|
// specified date */
|
||||||
|
ZpOpt.fVerbose = FALSE; /* true if full messages wanted */
|
||||||
|
ZpOpt.fQuiet = TRUE; /* true if minimum messages wanted */
|
||||||
|
ZpOpt.fCRLF_LF = FALSE; /* true if translate CR/LF to LF */
|
||||||
|
ZpOpt.fLF_CRLF = FALSE; /* true if translate LF to CR/LF */
|
||||||
|
ZpOpt.fJunkDir = TRUE; /* true if junking directory names */
|
||||||
|
ZpOpt.fRecurse = FALSE; /* true if recursing into subdirectories */
|
||||||
|
ZpOpt.fGrow = FALSE; /* true if allow appending to zip file */
|
||||||
|
ZpOpt.fForce = FALSE; /* true if making entries using DOS names */
|
||||||
|
ZpOpt.fMove = FALSE; /* true if deleting files added or updated */
|
||||||
|
ZpOpt.fUpdate = FALSE; /* true if updating zip file--overwrite only
|
||||||
|
if newer */
|
||||||
|
ZpOpt.fFreshen = FALSE; /* true if freshening zip file--overwrite only */
|
||||||
|
ZpOpt.fJunkSFX = FALSE; /* true if junking sfx prefix*/
|
||||||
|
ZpOpt.fLatestTime = FALSE; /* true if setting zip file time to time of
|
||||||
|
latest file in archive */
|
||||||
|
ZpOpt.fComment = FALSE; /* true if putting comment in zip file */
|
||||||
|
ZpOpt.fOffsets = FALSE; /* true if updating archive offsets for sfx
|
||||||
|
files */
|
||||||
|
ZpOpt.fDeleteEntries = FALSE; /* true if deleting files from archive */
|
||||||
|
ZpOpt.Date[0] = '\0'; /* Not using, set to 0 length */
|
||||||
|
getcwd(ZpOpt.szRootDir, MAX_PATH); /* Set directory to current directory */
|
||||||
|
|
||||||
|
ZpZCL.argc = 1; /* number of files to archive - adjust for the
|
||||||
|
actual number of file names to be added */
|
||||||
|
ZpZCL.lpszZipFN = (LPSTR) zipfile; /* archive to be created/updated */
|
||||||
|
|
||||||
|
/* Copy over the appropriate portions of argv, basically stripping out argv[0]
|
||||||
|
(name of the executable) and argv[1] (name of the archive file)
|
||||||
|
*/
|
||||||
|
|
||||||
|
hFileList = GlobalAlloc( GPTR, 0x10000L);
|
||||||
|
if ( hFileList )
|
||||||
|
{
|
||||||
|
szFileList = (char far *)GlobalLock(hFileList);
|
||||||
|
}
|
||||||
|
index = (char **)szFileList;
|
||||||
|
cc = (sizeof(char *) * ZpZCL.argc);
|
||||||
|
sz = szFileList + cc;
|
||||||
|
/*
|
||||||
|
for (i = 0; i < ZpZCL.argc; i++)
|
||||||
|
{
|
||||||
|
cc = lstrlen(argv[i+2]);
|
||||||
|
lstrcpy(sz, argv[i+2]);
|
||||||
|
index[i] = sz;
|
||||||
|
sz += (cc + 1);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
cc = lstrlen(filename);
|
||||||
|
lstrcpy(sz, filename);
|
||||||
|
index[0] = sz;
|
||||||
|
sz += (cc + 1);
|
||||||
|
ZpZCL.FNV = (char **)szFileList; // list of files to archive
|
||||||
|
|
||||||
|
|
||||||
|
/* Set the options */
|
||||||
|
ZpSetOptions(ZpOpt);
|
||||||
|
|
||||||
|
/* Go zip 'em up */
|
||||||
|
retcode = ZpArchive(ZpZCL);
|
||||||
|
if (retcode != 0)
|
||||||
|
gi.dprintf("Error during archiving.\nUnable to create \"%s\"\n", zipfile);
|
||||||
|
|
||||||
|
GlobalUnlock(hFileList);
|
||||||
|
GlobalFree(hFileList);
|
||||||
|
FreeUpMemoryZip();
|
||||||
|
FreeLibrary(hZipDll);
|
||||||
|
return (retcode == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeUpMemoryZip(void)
|
||||||
|
{
|
||||||
|
if (hZUF)
|
||||||
|
{
|
||||||
|
GlobalUnlock(hZUF);
|
||||||
|
GlobalFree(hZUF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Password entry routine - see password.c in the wiz directory for how
|
||||||
|
this is actually implemented in Wiz. If you have an encrypted file,
|
||||||
|
this will probably give you great pain. Note that none of the
|
||||||
|
parameters are being used here, and this will give you warnings.
|
||||||
|
*/
|
||||||
|
int WINAPI DummyPassword(char *p, char *n, int m, char * name)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dummy "print" routine that simply outputs what is sent from the dll */
|
||||||
|
int far DummyPrint(FILE *buf, unsigned int size, char *msg)
|
||||||
|
{
|
||||||
|
//printf("%s", buf);
|
||||||
|
return (unsigned int) size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Dummy "comment" routine. See comment.c in the wiz directory for how
|
||||||
|
this is actually implemented in Wiz. This will probably cause you
|
||||||
|
great pain if you ever actually make a call into it.
|
||||||
|
*/
|
||||||
|
char far * WINAPI DummyComment(char far *szBuf)
|
||||||
|
{
|
||||||
|
szBuf[0] = '\0';
|
||||||
|
return szBuf;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
/*
|
||||||
|
Example header file
|
||||||
|
*/
|
||||||
|
#ifndef _EXAMPLE_H
|
||||||
|
#define _EXAMPLE_H
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
//#include <assert.h> /* required for all Windows applications */
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
//#include <commdlg.h>
|
||||||
|
#include <dlgs.h>
|
||||||
|
#include <windowsx.h>
|
||||||
|
|
||||||
|
//=================================== STRUCTS ==============================
|
||||||
|
#ifndef _ZIP_STRUCTS_H
|
||||||
|
#define _ZIP_STRUCTS_H
|
||||||
|
|
||||||
|
#ifndef Far
|
||||||
|
# define Far far
|
||||||
|
#endif
|
||||||
|
|
||||||
|
# define far
|
||||||
|
# define _far
|
||||||
|
# define __far
|
||||||
|
# define near
|
||||||
|
# define _near
|
||||||
|
# define __near
|
||||||
|
|
||||||
|
#ifndef PATH_MAX
|
||||||
|
# define PATH_MAX 128
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "z_api.h"
|
||||||
|
|
||||||
|
#endif /* _ZIP_STRUCTS_H */
|
||||||
|
//==================================================================
|
||||||
|
|
||||||
|
/* Defines */
|
||||||
|
#ifndef MSWIN
|
||||||
|
#define MSWIN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef int (WINAPI * _DLL_ZIP)(ZCL);
|
||||||
|
typedef int (WINAPI * _ZIP_USER_FUNCTIONS)(LPZIPUSERFUNCTIONS);
|
||||||
|
typedef BOOL (WINAPI * ZIPSETOPTIONS)(ZPOPT);
|
||||||
|
|
||||||
|
/* Global variables */
|
||||||
|
|
||||||
|
extern LPZIPUSERFUNCTIONS lpZipUserFunctions;
|
||||||
|
|
||||||
|
extern HINSTANCE hZipDll;
|
||||||
|
|
||||||
|
extern int hFile; /* file handle */
|
||||||
|
|
||||||
|
/* Global functions */
|
||||||
|
|
||||||
|
extern _DLL_ZIP ZpArchive;
|
||||||
|
extern _ZIP_USER_FUNCTIONS ZpInit;
|
||||||
|
int WINAPI DisplayBuf(char far *, unsigned long int);
|
||||||
|
extern ZIPSETOPTIONS ZpSetOptions;
|
||||||
|
|
||||||
|
#endif /* _EXAMPLE_H */
|
||||||
|
|
|
@ -0,0 +1,216 @@
|
||||||
|
|
||||||
|
// game.h -- game dll information visible to server
|
||||||
|
|
||||||
|
#define GAME_API_VERSION 3
|
||||||
|
|
||||||
|
// edict->svflags
|
||||||
|
|
||||||
|
#define SVF_NOCLIENT 0x00000001 // don't send entity to clients, even if it has effects
|
||||||
|
#define SVF_DEADMONSTER 0x00000002 // treat as CONTENTS_DEADMONSTER for collision
|
||||||
|
#define SVF_MONSTER 0x00000004 // treat as CONTENTS_MONSTER for collision
|
||||||
|
|
||||||
|
// edict->solid values
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
SOLID_NOT, // no interaction with other objects
|
||||||
|
SOLID_TRIGGER, // only touch when inside, after moving
|
||||||
|
SOLID_BBOX, // touch on edge
|
||||||
|
SOLID_BSP // bsp clip, touch on edge
|
||||||
|
} solid_t;
|
||||||
|
|
||||||
|
//===============================================================
|
||||||
|
|
||||||
|
// link_t is only used for entity area links now
|
||||||
|
typedef struct link_s
|
||||||
|
{
|
||||||
|
struct link_s *prev, *next;
|
||||||
|
} link_t;
|
||||||
|
|
||||||
|
#define MAX_ENT_CLUSTERS 16
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct edict_s edict_t;
|
||||||
|
typedef struct gclient_s gclient_t;
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef GAME_INCLUDE
|
||||||
|
|
||||||
|
struct gclient_s
|
||||||
|
{
|
||||||
|
player_state_t ps; // communicated by server to clients
|
||||||
|
int ping;
|
||||||
|
// the game dll can add anything it wants after
|
||||||
|
// this point in the structure
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct edict_s
|
||||||
|
{
|
||||||
|
entity_state_t s;
|
||||||
|
struct gclient_s *client;
|
||||||
|
qboolean inuse;
|
||||||
|
int linkcount;
|
||||||
|
|
||||||
|
// FIXME: move these fields to a server private sv_entity_t
|
||||||
|
link_t area; // linked to a division node or leaf
|
||||||
|
|
||||||
|
int num_clusters; // if -1, use headnode instead
|
||||||
|
int clusternums[MAX_ENT_CLUSTERS];
|
||||||
|
int headnode; // unused if num_clusters != -1
|
||||||
|
int areanum, areanum2;
|
||||||
|
|
||||||
|
//================================
|
||||||
|
|
||||||
|
int svflags; // SVF_NOCLIENT, SVF_DEADMONSTER, SVF_MONSTER, etc
|
||||||
|
vec3_t mins, maxs;
|
||||||
|
vec3_t absmin, absmax, size;
|
||||||
|
solid_t solid;
|
||||||
|
int clipmask;
|
||||||
|
edict_t *owner;
|
||||||
|
|
||||||
|
// the game dll can add anything it wants after
|
||||||
|
// this point in the structure
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // GAME_INCLUDE
|
||||||
|
|
||||||
|
//===============================================================
|
||||||
|
|
||||||
|
//
|
||||||
|
// functions provided by the main engine
|
||||||
|
//
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
// special messages
|
||||||
|
void (*bprintf) (int printlevel, char *fmt, ...);
|
||||||
|
void (*dprintf) (char *fmt, ...);
|
||||||
|
void (*cprintf) (edict_t *ent, int printlevel, char *fmt, ...);
|
||||||
|
void (*centerprintf) (edict_t *ent, char *fmt, ...);
|
||||||
|
void (*sound) (edict_t *ent, int channel, int soundindex, float volume, float attenuation, float timeofs);
|
||||||
|
void (*positioned_sound) (vec3_t origin, edict_t *ent, int channel, int soundinedex, float volume, float attenuation, float timeofs);
|
||||||
|
|
||||||
|
// config strings hold all the index strings, the lightstyles,
|
||||||
|
// and misc data like the sky definition and cdtrack.
|
||||||
|
// All of the current configstrings are sent to clients when
|
||||||
|
// they connect, and changes are sent to all connected clients.
|
||||||
|
void (*configstring) (int num, char *string);
|
||||||
|
|
||||||
|
void (*error) (char *fmt, ...);
|
||||||
|
|
||||||
|
// the *index functions create configstrings and some internal server state
|
||||||
|
int (*modelindex) (char *name);
|
||||||
|
int (*soundindex) (char *name);
|
||||||
|
int (*imageindex) (char *name);
|
||||||
|
|
||||||
|
void (*setmodel) (edict_t *ent, char *name);
|
||||||
|
|
||||||
|
// collision detection
|
||||||
|
trace_t (*trace) (vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, edict_t *passent, int contentmask);
|
||||||
|
int (*pointcontents) (vec3_t point);
|
||||||
|
qboolean (*inPVS) (vec3_t p1, vec3_t p2);
|
||||||
|
qboolean (*inPHS) (vec3_t p1, vec3_t p2);
|
||||||
|
void (*SetAreaPortalState) (int portalnum, qboolean open);
|
||||||
|
qboolean (*AreasConnected) (int area1, int area2);
|
||||||
|
|
||||||
|
// an entity will never be sent to a client or used for collision
|
||||||
|
// if it is not passed to linkentity. If the size, position, or
|
||||||
|
// solidity changes, it must be relinked.
|
||||||
|
void (*linkentity) (edict_t *ent);
|
||||||
|
void (*unlinkentity) (edict_t *ent); // call before removing an interactive edict
|
||||||
|
int (*BoxEdicts) (vec3_t mins, vec3_t maxs, edict_t **list, int maxcount, int areatype);
|
||||||
|
void (*Pmove) (pmove_t *pmove); // player movement code common with client prediction
|
||||||
|
|
||||||
|
// network messaging
|
||||||
|
void (*multicast) (vec3_t origin, multicast_t to);
|
||||||
|
void (*unicast) (edict_t *ent, qboolean reliable);
|
||||||
|
void (*WriteChar) (int c);
|
||||||
|
void (*WriteByte) (int c);
|
||||||
|
void (*WriteShort) (int c);
|
||||||
|
void (*WriteLong) (int c);
|
||||||
|
void (*WriteFloat) (float f);
|
||||||
|
void (*WriteString) (char *s);
|
||||||
|
void (*WritePosition) (vec3_t pos); // some fractional bits
|
||||||
|
void (*WriteDir) (vec3_t pos); // single byte encoded, very coarse
|
||||||
|
void (*WriteAngle) (float f);
|
||||||
|
|
||||||
|
// managed memory allocation
|
||||||
|
void *(*TagMalloc) (int size, int tag);
|
||||||
|
void (*TagFree) (void *block);
|
||||||
|
void (*FreeTags) (int tag);
|
||||||
|
|
||||||
|
// console variable interaction
|
||||||
|
cvar_t *(*cvar) (char *var_name, char *value, int flags);
|
||||||
|
cvar_t *(*cvar_set) (char *var_name, char *value);
|
||||||
|
cvar_t *(*cvar_forceset) (char *var_name, char *value);
|
||||||
|
|
||||||
|
// ClientCommand and ServerCommand parameter access
|
||||||
|
int (*argc) (void);
|
||||||
|
char *(*argv) (int n);
|
||||||
|
char *(*args) (void); // concatenation of all argv >= 1
|
||||||
|
|
||||||
|
// add commands to the server console as if they were typed in
|
||||||
|
// for map changing, etc
|
||||||
|
void (*AddCommandString) (char *text);
|
||||||
|
|
||||||
|
void (*DebugGraph) (float value, int color);
|
||||||
|
} game_import_t;
|
||||||
|
|
||||||
|
//
|
||||||
|
// functions exported by the game subsystem
|
||||||
|
//
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
int apiversion;
|
||||||
|
|
||||||
|
// the init function will only be called when a game starts,
|
||||||
|
// not each time a level is loaded. Persistant data for clients
|
||||||
|
// and the server can be allocated in init
|
||||||
|
void (*Init) (void);
|
||||||
|
void (*Shutdown) (void);
|
||||||
|
|
||||||
|
// each new level entered will cause a call to SpawnEntities
|
||||||
|
void (*SpawnEntities) (char *mapname, char *entstring, char *spawnpoint);
|
||||||
|
|
||||||
|
// Read/Write Game is for storing persistant cross level information
|
||||||
|
// about the world state and the clients.
|
||||||
|
// WriteGame is called every time a level is exited.
|
||||||
|
// ReadGame is called on a loadgame.
|
||||||
|
void (*WriteGame) (char *filename, qboolean autosave);
|
||||||
|
void (*ReadGame) (char *filename);
|
||||||
|
|
||||||
|
// ReadLevel is called after the default map information has been
|
||||||
|
// loaded with SpawnEntities
|
||||||
|
void (*WriteLevel) (char *filename);
|
||||||
|
void (*ReadLevel) (char *filename);
|
||||||
|
|
||||||
|
qboolean (*ClientConnect) (edict_t *ent, char *userinfo);
|
||||||
|
void (*ClientBegin) (edict_t *ent);
|
||||||
|
void (*ClientUserinfoChanged) (edict_t *ent, char *userinfo);
|
||||||
|
void (*ClientDisconnect) (edict_t *ent);
|
||||||
|
void (*ClientCommand) (edict_t *ent);
|
||||||
|
void (*ClientThink) (edict_t *ent, usercmd_t *cmd);
|
||||||
|
|
||||||
|
void (*RunFrame) (void);
|
||||||
|
|
||||||
|
// ServerCommand will be called when an "sv <command>" command is issued on the
|
||||||
|
// server console.
|
||||||
|
// The game can issue gi.argc() / gi.argv() commands to get the rest
|
||||||
|
// of the parameters
|
||||||
|
void (*ServerCommand) (void);
|
||||||
|
|
||||||
|
//
|
||||||
|
// global variables shared between game and server
|
||||||
|
//
|
||||||
|
|
||||||
|
// The edict array is allocated in the game dll so it
|
||||||
|
// can vary in size from one game to another.
|
||||||
|
//
|
||||||
|
// The size will be fixed when ge->Init() is called
|
||||||
|
struct edict_s *edicts;
|
||||||
|
int edict_size;
|
||||||
|
int num_edicts; // current number, <= max_edicts
|
||||||
|
int max_edicts;
|
||||||
|
} game_export_t;
|
||||||
|
|
||||||
|
game_export_t *GetGameApi (game_import_t *import);
|
|
@ -0,0 +1,616 @@
|
||||||
|
/*****************************************************************
|
||||||
|
|
||||||
|
Grapple source code - by Acrid-, acridcola@hotmail.com
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
This file is Copyright(c) 1999, Acrid-, All Rights Reserved.
|
||||||
|
|
||||||
|
..............................................................
|
||||||
|
|
||||||
|
|
||||||
|
Should you decide to release a modified version of the Grapple, you
|
||||||
|
MUST include the following text (minus the BEGIN and END lines) in
|
||||||
|
the documentation for your modification, and also on all web pages
|
||||||
|
related to your modification, should they exist.
|
||||||
|
|
||||||
|
--- BEGIN ---
|
||||||
|
|
||||||
|
The Grapple and related code ,minus the pushcode or lines marked tut,
|
||||||
|
are a product of Acrid- designed for Weapons Factory, and is available as
|
||||||
|
part of the Weapons Factory Source Code or a seperate tutorial.
|
||||||
|
|
||||||
|
This program MUST NOT be sold in ANY form. If you have paid for
|
||||||
|
this product, you should contact Acrid- at:
|
||||||
|
acridcola@hotmail.com
|
||||||
|
|
||||||
|
--- END ---
|
||||||
|
|
||||||
|
have fun,
|
||||||
|
|
||||||
|
Acrid-
|
||||||
|
|
||||||
|
*****************************************************************/
|
||||||
|
|
||||||
|
//#define USE_OLD_GRAPPLE 1
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
==============================
|
||||||
|
Grapple Code by Acrid
|
||||||
|
|
||||||
|
4/6/99
|
||||||
|
code in other files: noted by newgrap 4/99
|
||||||
|
g_cmds.c
|
||||||
|
g_items.c
|
||||||
|
g_misc.c
|
||||||
|
g_phys.c
|
||||||
|
p_client.c
|
||||||
|
wf_local_client.h
|
||||||
|
ctf.h
|
||||||
|
I looked a few tutors but they never quite stopped bouncing on my system.
|
||||||
|
This code stops all bounce by turning off gravity and changing velocity
|
||||||
|
to 100 when length is 64.
|
||||||
|
It also uses pusher code from a tutorial I found to correct bounce when
|
||||||
|
grappled to objects like doors,plats etc.
|
||||||
|
Plus you can use this code for a normal ctfsytle grapple,a offhand
|
||||||
|
ctfsytle grapple and a ctfsytle weapon grapple.
|
||||||
|
WF notes:
|
||||||
|
bind key +grapple for offhand
|
||||||
|
bind key grapple for normal
|
||||||
|
use grapple for weapon_grapple
|
||||||
|
by using BUTTON_USE offhand alias will no longer get reversed.
|
||||||
|
==============================
|
||||||
|
*/
|
||||||
|
#include "g_local.h"
|
||||||
|
#define DELAY_TIME 5000
|
||||||
|
|
||||||
|
void CTFFireGrapple (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect);
|
||||||
|
void CTFGrappleFire (edict_t *ent, vec3_t g_offset, int damage, int effect);
|
||||||
|
|
||||||
|
|
||||||
|
void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result);
|
||||||
|
qboolean Ended_Grappling (gclient_t *client)
|
||||||
|
{
|
||||||
|
return (!(client->buttons & BUTTON_USE) && client->oldbuttons & BUTTON_USE);
|
||||||
|
}
|
||||||
|
|
||||||
|
qboolean Is_Grappling (gclient_t *client)
|
||||||
|
{
|
||||||
|
return (client->ctf_grapple == NULL) ? false : true;
|
||||||
|
}
|
||||||
|
void CTFGrappleTouch2 (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
float volume = 1.0;
|
||||||
|
|
||||||
|
// Release if hitting its owner
|
||||||
|
if (other == self->owner)
|
||||||
|
return;
|
||||||
|
//tut s
|
||||||
|
if (!Is_Grappling(self->owner->client) && self->health == 0)
|
||||||
|
{ botDebugPrint("!isgrap\n");
|
||||||
|
return;
|
||||||
|
}// tut e
|
||||||
|
|
||||||
|
self->health = 0;//tut
|
||||||
|
|
||||||
|
if (self->owner->client->ctf_grapplestate != CTF_GRAPPLE_STATE_FLY)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (surf && surf->flags & SURF_SKY)
|
||||||
|
{
|
||||||
|
botDebugPrint("reset\n");
|
||||||
|
CTFPlayerResetGrapple2(self->owner);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VectorCopy(vec3_origin, self->velocity);
|
||||||
|
|
||||||
|
PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
|
||||||
|
|
||||||
|
if (other != g_edicts && other->clipmask == MASK_SHOT)//tut
|
||||||
|
return;//tut
|
||||||
|
|
||||||
|
if (other->takedamage || other->client)
|
||||||
|
{ //ERASER ADDED || other->client
|
||||||
|
T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, 0, MOD_GRAPPLE);
|
||||||
|
CTFResetGrapple2(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->owner->client->ctf_grapplestate = CTF_GRAPPLE_STATE_PULL; // we're on hook
|
||||||
|
self->enemy = other;
|
||||||
|
|
||||||
|
self->solid = SOLID_NOT;
|
||||||
|
|
||||||
|
if (self->owner->client->silencer_shots)
|
||||||
|
volume = 0.2;
|
||||||
|
|
||||||
|
gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grpull.wav"), volume, ATTN_NORM, 0);
|
||||||
|
gi.sound (self, CHAN_WEAPON, gi.soundindex("weapons/grapple/grhit.wav"), volume, ATTN_NORM, 0);
|
||||||
|
|
||||||
|
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_SPARKS);
|
||||||
|
gi.WritePosition (self->s.origin);
|
||||||
|
if (!plane)
|
||||||
|
gi.WriteDir (vec3_origin);
|
||||||
|
else
|
||||||
|
gi.WriteDir (plane->normal);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||||
|
//end zoid
|
||||||
|
|
||||||
|
//Pusher code
|
||||||
|
if (other != g_edicts && other->inuse && (other->movetype == MOVETYPE_PUSH ||
|
||||||
|
other->movetype == MOVETYPE_STOP))
|
||||||
|
{
|
||||||
|
other->mynoise2 = self;
|
||||||
|
self->owner->client->hook_touch = other;
|
||||||
|
// self->enemy = other;
|
||||||
|
self->groundentity = NULL;
|
||||||
|
self->flags |= FL_TEAMSLAVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorClear(self->velocity);
|
||||||
|
VectorClear(self->avelocity);
|
||||||
|
self->touch = NULL;
|
||||||
|
self->movetype = MOVETYPE_NONE;
|
||||||
|
self->delay = level.time + DELAY_TIME;
|
||||||
|
self->owner->client->on_hook = true;
|
||||||
|
self->owner->groundentity = NULL;
|
||||||
|
CTFGrapplePull2(self->owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTFGrappleThink2( edict_t *self )
|
||||||
|
{
|
||||||
|
if (!self->owner || !self->owner->client)
|
||||||
|
{
|
||||||
|
G_FreeEdict(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((self->owner->health <= 0) || (self->owner->client->ctf_grapple != self))
|
||||||
|
{
|
||||||
|
gclient_t *cl;
|
||||||
|
|
||||||
|
cl = self->owner->client;
|
||||||
|
|
||||||
|
if (cl->ctf_grapple == self)
|
||||||
|
{
|
||||||
|
cl->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; //sever codefix test
|
||||||
|
gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grreset.wav"), 1, ATTN_NORM, 0);
|
||||||
|
cl->ctf_grapple = NULL;
|
||||||
|
cl->ctf_grapplereleasetime = level.time;
|
||||||
|
cl->ctf_grapplestate = CTF_GRAPPLE_STATE_FLY; // we're firing, not on hook
|
||||||
|
}
|
||||||
|
|
||||||
|
G_FreeEdict(self);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//start of tut
|
||||||
|
if (level.time > self->delay)
|
||||||
|
self->prethink = CTFPlayerResetGrapple2;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (self->owner->client->hook_touch)
|
||||||
|
{
|
||||||
|
edict_t *obj = self->owner->client->hook_touch;
|
||||||
|
|
||||||
|
if (obj == g_edicts)
|
||||||
|
{
|
||||||
|
CTFPlayerResetGrapple2(self->owner);
|
||||||
|
botDebugPrint("resetting 1\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (obj->inuse == false)
|
||||||
|
{
|
||||||
|
CTFPlayerResetGrapple2(self->owner);
|
||||||
|
botDebugPrint("resetting 2\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (obj->deadflag == DEAD_DEAD)
|
||||||
|
{
|
||||||
|
CTFPlayerResetGrapple2(self->owner);
|
||||||
|
botDebugPrint("resetting 3\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self->nextthink = level.time + FRAMETIME;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTFGrappleDrawCable2 (edict_t *self)
|
||||||
|
{
|
||||||
|
vec3_t offset, start, end, f, r;
|
||||||
|
vec3_t dir;
|
||||||
|
float distance;
|
||||||
|
|
||||||
|
AngleVectors (self->owner->client->v_angle, f, r, NULL);
|
||||||
|
VectorSet(offset, 16, 16, self->owner->viewheight-8);
|
||||||
|
P_ProjectSource (self->owner->client, self->owner->s.origin, offset, f, r, start);
|
||||||
|
|
||||||
|
VectorSubtract(start, self->owner->s.origin, offset);
|
||||||
|
|
||||||
|
VectorSubtract (start, self->s.origin, dir);
|
||||||
|
distance = VectorLength(dir);
|
||||||
|
// don't draw cable if close
|
||||||
|
if (distance < 64)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
if (distance > 256)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// check for min/max pitch
|
||||||
|
vectoangles (dir, angles);
|
||||||
|
if (angles[0] < -180)
|
||||||
|
angles[0] += 360;
|
||||||
|
if (fabs(angles[0]) > 45)
|
||||||
|
return;
|
||||||
|
|
||||||
|
trace_t tr; //!!
|
||||||
|
|
||||||
|
tr = gi.trace (start, NULL, NULL, self->s.origin, self, MASK_SHOT);
|
||||||
|
if (tr.ent != self) {
|
||||||
|
CTFResetGrapple(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
VectorCopy (self->s.origin, end);
|
||||||
|
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
#if 1 //def USE_GRAPPLE_CABLE
|
||||||
|
gi.WriteByte (TE_GRAPPLE_CABLE);
|
||||||
|
gi.WriteShort (self->owner - g_edicts);
|
||||||
|
gi.WritePosition (self->owner->s.origin);
|
||||||
|
gi.WritePosition (end);
|
||||||
|
gi.WritePosition (offset);
|
||||||
|
#else
|
||||||
|
gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
|
||||||
|
gi.WriteShort (self - g_edicts);
|
||||||
|
gi.WritePosition (end);
|
||||||
|
gi.WritePosition (start);
|
||||||
|
#endif
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CTFFireGrapple2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect)
|
||||||
|
{
|
||||||
|
edict_t *grapple;
|
||||||
|
trace_t tr;
|
||||||
|
|
||||||
|
#ifdef USE_OLD_GRAPPLE
|
||||||
|
CTFFireGrapple (self, start, dir, damage, speed, effect);
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
VectorNormalize (dir);
|
||||||
|
|
||||||
|
grapple = G_Spawn();
|
||||||
|
VectorCopy (start, grapple->s.origin);
|
||||||
|
VectorCopy (start, grapple->s.old_origin);
|
||||||
|
vectoangles (dir, grapple->s.angles);
|
||||||
|
VectorScale (dir, speed, grapple->velocity);
|
||||||
|
VectorSet(grapple->avelocity, 0, 0, 500);//tut
|
||||||
|
grapple->classname = "hook";//tut
|
||||||
|
grapple->movetype = MOVETYPE_FLYMISSILE;
|
||||||
|
grapple->clipmask = MASK_SHOT;
|
||||||
|
grapple->svflags |= SVF_DEADMONSTER;//tut
|
||||||
|
grapple->solid = SOLID_BBOX;
|
||||||
|
grapple->s.effects |= effect;
|
||||||
|
VectorClear (grapple->mins);
|
||||||
|
VectorClear (grapple->maxs);
|
||||||
|
|
||||||
|
grapple->s.modelindex = gi.modelindex ("models/weapons/grapple/hook/tris.md2");
|
||||||
|
grapple->owner = self;
|
||||||
|
grapple->touch = CTFGrappleTouch2;
|
||||||
|
grapple->delay = level.time + DELAY_TIME;//tut
|
||||||
|
// grapple->nextthink = level.time;//tut
|
||||||
|
grapple->nextthink = level.time + FRAMETIME;
|
||||||
|
grapple->think = CTFGrappleThink2;
|
||||||
|
grapple->prethink = CTFGrappleDrawCable2;
|
||||||
|
grapple->health = 100;//tut
|
||||||
|
grapple->svflags = SVF_MONSTER;//tut
|
||||||
|
grapple->dmg = damage;
|
||||||
|
self->client->ctf_grapple = grapple;
|
||||||
|
self->client->ctf_grapplestate = CTF_GRAPPLE_STATE_FLY; // we're firing, not on hook
|
||||||
|
|
||||||
|
gi.linkentity (grapple);
|
||||||
|
|
||||||
|
tr = gi.trace (self->s.origin, NULL, NULL, grapple->s.origin, grapple, MASK_SHOT);
|
||||||
|
if (tr.fraction < 1.0)
|
||||||
|
{
|
||||||
|
VectorMA (grapple->s.origin, -10, dir, grapple->s.origin);
|
||||||
|
grapple->touch (grapple, tr.ent, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
void CTFGrappleFire2 (edict_t *ent, vec3_t g_offset, int damage, int effect)
|
||||||
|
{
|
||||||
|
vec3_t forward, right;
|
||||||
|
vec3_t start;
|
||||||
|
vec3_t offset;
|
||||||
|
float volume = 1.0;
|
||||||
|
|
||||||
|
//Feign 5/99
|
||||||
|
if (ent->client->pers.feign)
|
||||||
|
return;
|
||||||
|
|
||||||
|
#ifdef USE_OLD_GRAPPLE
|
||||||
|
CTFGrappleFire (ent, g_offset, damage, effect);
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ent->client->ctf_grapple) //tut s not needed?
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}//tut e
|
||||||
|
|
||||||
|
if (ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY)
|
||||||
|
return; // it's already out
|
||||||
|
|
||||||
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
||||||
|
VectorScale (forward, -2, ent->client->kick_origin);
|
||||||
|
ent->client->kick_angles[0] = -1;
|
||||||
|
VectorSet(offset, 24, 8, ent->viewheight-8+2);
|
||||||
|
VectorAdd (offset, g_offset, offset);
|
||||||
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
||||||
|
|
||||||
|
if (ent->client->silencer_shots)
|
||||||
|
volume = 0.2;
|
||||||
|
|
||||||
|
gi.sound (ent, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grfire.wav"), volume, ATTN_NORM, 0);
|
||||||
|
CTFFireGrapple2 (ent, start, forward, damage, CTF_GRAPPLE_SPEED, effect);
|
||||||
|
|
||||||
|
ent->client->hook_touch = NULL;//tut
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// send muzzle flash
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
gi.WriteShort (ent-g_edicts);
|
||||||
|
gi.WriteByte (MZ_BLASTER);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
||||||
|
|
||||||
|
//Eraser START: record this position, so we drop a grapple node here, rather than where the player is when they leave the ground
|
||||||
|
if (!ent->bot_client)
|
||||||
|
{ botDebugPrint("Grapple pull\n");
|
||||||
|
VectorCopy(ent->s.origin, ent->animate_org);
|
||||||
|
}
|
||||||
|
//ERASER END
|
||||||
|
}
|
||||||
|
void CTFWeapon_Grapple_Fire2 (edict_t *ent)
|
||||||
|
{
|
||||||
|
int damage;
|
||||||
|
|
||||||
|
damage = 10;
|
||||||
|
CTFGrappleFire2 (ent, vec3_origin, damage, 0);
|
||||||
|
// ent->client->ps.gunframe++;//COed by Gregg Reno
|
||||||
|
}
|
||||||
|
|
||||||
|
void CTFWeapon_Grapple2 (edict_t *ent)
|
||||||
|
{
|
||||||
|
static int pause_frames[] = {10, 18, 27, 0};
|
||||||
|
static int fire_frames[] = {6, 0};
|
||||||
|
int prevstate;
|
||||||
|
|
||||||
|
#ifdef USE_OLD_GRAPPLE
|
||||||
|
CTFWeapon_Grapple (ent);
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// if the the attack button is still down, stay in the firing frame
|
||||||
|
if ((ent->client->buttons & BUTTON_ATTACK) &&
|
||||||
|
ent->client->weaponstate == WEAPON_FIRING &&
|
||||||
|
ent->client->ctf_grapple)
|
||||||
|
{
|
||||||
|
botDebugPrint("firing\n");
|
||||||
|
ent->client->ps.gunframe = 9;
|
||||||
|
}
|
||||||
|
if (!(ent->client->buttons & BUTTON_ATTACK) && ent->client->ctf_grapple)
|
||||||
|
{ botDebugPrint("reset 3\n");
|
||||||
|
CTFResetGrapple2(ent->client->ctf_grapple);
|
||||||
|
if (ent->client->weaponstate == WEAPON_FIRING)
|
||||||
|
ent->client->weaponstate = WEAPON_READY;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (ent->client->newweapon && ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY &&
|
||||||
|
ent->client->weaponstate == WEAPON_FIRING)
|
||||||
|
{ botDebugPrint("state\n");
|
||||||
|
// he wants to change weapons while grappled
|
||||||
|
ent->client->weaponstate = WEAPON_DROPPING;
|
||||||
|
ent->client->ps.gunframe = 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
prevstate = ent->client->weaponstate;
|
||||||
|
Weapon_Generic (ent, 5, 9, 31, 36, pause_frames, fire_frames,CTFWeapon_Grapple_Fire2);
|
||||||
|
|
||||||
|
// if we just switched back to grapple, immediately go to fire frame
|
||||||
|
if (prevstate == WEAPON_ACTIVATING &&
|
||||||
|
ent->client->weaponstate == WEAPON_READY &&
|
||||||
|
ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY)
|
||||||
|
{
|
||||||
|
if (!(ent->client->buttons & BUTTON_ATTACK))
|
||||||
|
ent->client->ps.gunframe = 9;
|
||||||
|
else
|
||||||
|
ent->client->ps.gunframe = 5;
|
||||||
|
ent->client->weaponstate = WEAPON_FIRING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ent is player
|
||||||
|
void CTFPlayerResetGrapple2(edict_t *ent)
|
||||||
|
{
|
||||||
|
if (ent->client && ent->client->ctf_grapple)
|
||||||
|
// CTFResetGrapple(ent->client->ctf_grapple);
|
||||||
|
CTFResetGrapple2(ent->client->ctf_grapple);
|
||||||
|
}
|
||||||
|
|
||||||
|
// self is grapple, not player
|
||||||
|
void CTFResetGrapple2(edict_t *self)
|
||||||
|
{
|
||||||
|
float volume = 1.0;
|
||||||
|
// gclient_t *cl;
|
||||||
|
|
||||||
|
|
||||||
|
edict_t *owner = self->owner;//tut
|
||||||
|
gclient_t *client = self->owner->client;//tut replaces cl
|
||||||
|
edict_t *link = self->teamchain;//tut used for map ents push code
|
||||||
|
client->on_hook = false;//tut
|
||||||
|
client->hook_touch = NULL;//tut
|
||||||
|
botDebugPrint("SelfReset %s\n",self->classname);
|
||||||
|
|
||||||
|
#ifdef USE_OLD_GRAPPLE
|
||||||
|
CTFResetGrapple(self);
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// if (self->owner->client->ctf_grapple)
|
||||||
|
// {
|
||||||
|
if (client->ctf_grapple != NULL)//tut section
|
||||||
|
{
|
||||||
|
|
||||||
|
client->ctf_grapple = NULL;//replaced cl
|
||||||
|
VectorClear(client->oldvelocity);
|
||||||
|
self->think = NULL;
|
||||||
|
if (self->enemy)
|
||||||
|
{
|
||||||
|
self->enemy->mynoise2 = NULL;
|
||||||
|
}
|
||||||
|
//tut section end
|
||||||
|
|
||||||
|
if (self->owner->client->silencer_shots)
|
||||||
|
volume = 0.2;
|
||||||
|
|
||||||
|
if ((self->owner) && (self->owner->client))
|
||||||
|
{
|
||||||
|
gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grreset.wav"), volume, ATTN_NORM, 0);
|
||||||
|
// cl = self->owner->client;
|
||||||
|
// client->ctf_grapple = NULL;
|
||||||
|
client->ctf_grapplereleasetime = level.time;
|
||||||
|
client->ctf_grapplestate = CTF_GRAPPLE_STATE_FLY; // we're firing, not on hook
|
||||||
|
client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; //sever codefix test
|
||||||
|
}//tut
|
||||||
|
G_FreeEdict(self);//tut
|
||||||
|
}//tut
|
||||||
|
|
||||||
|
}
|
||||||
|
void CTFGrapplePull2(edict_t *player)
|
||||||
|
{
|
||||||
|
vec3_t hookDir ,v;
|
||||||
|
vec_t vlen;
|
||||||
|
edict_t *self = player->client->ctf_grapple;
|
||||||
|
|
||||||
|
#ifdef USE_OLD_GRAPPLE
|
||||||
|
CTFGrapplePull(player);
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (strcmp(player->client->pers.weapon->classname, "weapon_grapple") == 0 &&
|
||||||
|
!player->client->newweapon &&
|
||||||
|
player->client->weaponstate != WEAPON_FIRING &&
|
||||||
|
player->client->weaponstate != WEAPON_ACTIVATING)
|
||||||
|
{
|
||||||
|
CTFResetGrapple2(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->enemy)
|
||||||
|
{
|
||||||
|
if (self->enemy->solid == SOLID_NOT)
|
||||||
|
{
|
||||||
|
CTFResetGrapple2(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (self->enemy->solid == SOLID_BBOX)
|
||||||
|
{
|
||||||
|
VectorScale(self->enemy->size, 0.5, v);
|
||||||
|
VectorAdd(v, self->enemy->s.origin, v);
|
||||||
|
VectorAdd(v, self->enemy->mins, self->s.origin);
|
||||||
|
gi.linkentity (self);
|
||||||
|
} else
|
||||||
|
VectorCopy(self->enemy->velocity, self->velocity);
|
||||||
|
if (self->enemy->takedamage &&
|
||||||
|
!CheckTeamDamage (self->enemy, self->owner))
|
||||||
|
{
|
||||||
|
float volume = 1.0;
|
||||||
|
|
||||||
|
if (self->owner->client->silencer_shots)
|
||||||
|
volume = 0.2;
|
||||||
|
|
||||||
|
T_Damage (self->enemy, self, self->owner, self->velocity, self->s.origin, vec3_origin, 1, 1, 0, MOD_GRAPPLE);
|
||||||
|
gi.sound (self, CHAN_WEAPON, gi.soundindex("weapons/grapple/grhurt.wav"), volume, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
// he died /ERASER ADDED || self->enemy->deadflag
|
||||||
|
if (!self->enemy || self->enemy->deadflag)
|
||||||
|
{
|
||||||
|
CTFResetGrapple2(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorSubtract(self->s.origin, player->s.origin,hookDir);
|
||||||
|
vlen = VectorNormalize(hookDir);
|
||||||
|
//vector speeds
|
||||||
|
if (vlen < 64)
|
||||||
|
{
|
||||||
|
VectorScale(hookDir, 100,player->velocity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VectorScale(hookDir, CTF_GRAPPLE_PULL_SPEED,player->velocity);//tut
|
||||||
|
}
|
||||||
|
VectorCopy(hookDir, player->movedir);//tut
|
||||||
|
|
||||||
|
if (player->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY)
|
||||||
|
{
|
||||||
|
|
||||||
|
//sever codefix test
|
||||||
|
/* vec3_t forward, up;
|
||||||
|
|
||||||
|
AngleVectors (self->owner->client->v_angle, forward, NULL, up);
|
||||||
|
VectorCopy(self->owner->s.origin, v);
|
||||||
|
v[2] += self->owner->viewheight;
|
||||||
|
VectorSubtract (self->s.origin, v, hookDir);*/
|
||||||
|
//server codefix end
|
||||||
|
|
||||||
|
if (player->client->ctf_grapplestate == CTF_GRAPPLE_STATE_PULL &&
|
||||||
|
vlen < 64) {
|
||||||
|
float volume = 1.0;
|
||||||
|
|
||||||
|
if (player->client->silencer_shots)
|
||||||
|
volume = 0.2;
|
||||||
|
|
||||||
|
self->owner->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; //sever codefix test
|
||||||
|
gi.sound (player, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grhang.wav"), volume, ATTN_NORM, 0);
|
||||||
|
player->client->ctf_grapplestate = CTF_GRAPPLE_STATE_HANG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*To move the player off the ground just a bit
|
||||||
|
so he doesn't staystuck (version 3.17 bug)tut*/
|
||||||
|
if (player->velocity[2] > 0)
|
||||||
|
{
|
||||||
|
vec3_t traceTo;
|
||||||
|
trace_t trace;
|
||||||
|
// find the point immediately above the player's origin
|
||||||
|
VectorCopy(player->s.origin, traceTo);
|
||||||
|
traceTo[2] += 1;
|
||||||
|
// trace to it
|
||||||
|
trace = gi.trace(traceTo, player->mins, player->maxs, traceTo, player, MASK_PLAYERSOLID);
|
||||||
|
// if there isn't a solid immediately above the player
|
||||||
|
if (!trace.startsolid)
|
||||||
|
{
|
||||||
|
player->s.origin[2] += 1; // make sure player off ground
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
|
||||||
|
void Cure_Disease(edict_t *self)
|
||||||
|
{
|
||||||
|
if (self->owner)
|
||||||
|
{
|
||||||
|
self->owner->disease = 0;
|
||||||
|
self->owner->s.effects &= ~EF_FLIES;
|
||||||
|
}
|
||||||
|
G_FreeEdict(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Disease_Think (edict_t *self)
|
||||||
|
{
|
||||||
|
edict_t *ent;
|
||||||
|
vec3_t dir;
|
||||||
|
trace_t tr;
|
||||||
|
int damage;
|
||||||
|
float points;
|
||||||
|
vec3_t v;
|
||||||
|
float rnum;
|
||||||
|
rnum = random();
|
||||||
|
|
||||||
|
if (!self->owner)
|
||||||
|
{
|
||||||
|
Cure_Disease(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//stop disease after the person dies - GREGG
|
||||||
|
if(self->owner->health < 1)
|
||||||
|
{
|
||||||
|
Cure_Disease(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//If disease flag is clear, then they are cured
|
||||||
|
if(!self->owner->disease)
|
||||||
|
{
|
||||||
|
Cure_Disease(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Move the entity to where the target is
|
||||||
|
VectorAdd (self->target_ent->mins, self->target_ent->maxs, v);
|
||||||
|
VectorMA (self->target_ent->s.origin, 0.5, v, v);
|
||||||
|
VectorSubtract (self->s.origin, v, v);
|
||||||
|
VectorSubtract (self->owner->s.origin, self->s.origin, dir);
|
||||||
|
VectorCopy(self->owner->s.origin,self->s.origin);
|
||||||
|
|
||||||
|
//Random amount of damage
|
||||||
|
damage = rndnum (4,11);
|
||||||
|
points = damage - 0.5 * (VectorLength (v));
|
||||||
|
if (points > wf_game.grenade_damage[GRENADE_TYPE_PLAGUE] ) points = wf_game.grenade_damage[GRENADE_TYPE_PLAGUE] ;
|
||||||
|
|
||||||
|
T_Damage (self->owner, self, self->target_ent, dir, self->owner->s.origin,vec3_origin, damage, (int)points, DAMAGE_NO_KNOCKBACK, MOD_DISEASE);
|
||||||
|
|
||||||
|
//Throw up 10% of the time //JR thought I should raise this chance then
|
||||||
|
if(rnum < 0.10)
|
||||||
|
{
|
||||||
|
ThrowUpNow(self->owner);
|
||||||
|
T_Damage (self->owner, self, self->target_ent, dir, self->owner->s.origin,vec3_origin, damage, (int)points, DAMAGE_NO_KNOCKBACK,MOD_DISEASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->nextthink = level.time + 1.5;
|
||||||
|
ent = NULL;
|
||||||
|
|
||||||
|
//Infect others who are close
|
||||||
|
while ((ent = findradius(ent, self->s.origin, 128)) && ent != NULL)
|
||||||
|
{
|
||||||
|
if (!ent->client) //only infect other players
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (self->owner == ent) //don't infect self
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//Don't go through walls
|
||||||
|
if (!visible(self, ent))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!ent->takedamage)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!CanDamage (ent, self))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!CanDamage (ent, self->target_ent))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tr = gi.trace (self->s.origin, NULL, NULL, ent->s.origin, self, MASK_SOLID);
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
infect_person(ent,self->target_ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void infect_person(edict_t *target, edict_t *owner)
|
||||||
|
{
|
||||||
|
edict_t *plague;
|
||||||
|
|
||||||
|
//Valid entities?
|
||||||
|
if (!target) return;
|
||||||
|
if (!owner) return;
|
||||||
|
|
||||||
|
//Ignore if either entity went away
|
||||||
|
if (!target->inuse) return;
|
||||||
|
if (!owner->inuse) return;
|
||||||
|
|
||||||
|
//Only infect players
|
||||||
|
if (!target->client)
|
||||||
|
return;
|
||||||
|
if (!owner->client)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// ignore if on same team
|
||||||
|
if (target->wf_team == owner->wf_team)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Don't disease again
|
||||||
|
if (target->disease)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Can't infect a nurse
|
||||||
|
//Shouldn't test the class number. Instead, don't infect them
|
||||||
|
//if the can create a biosentry or healing depot
|
||||||
|
if ((target->client->player_special & SPECIAL_HEALING) ||
|
||||||
|
(target->client->player_special & SPECIAL_BIOSENTRY) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
safe_cprintf(target, PRINT_HIGH, "You've been infected by %s.\n", owner->client->pers.netname);
|
||||||
|
safe_cprintf(owner, PRINT_HIGH, "You have infected %s!\n",target->client->pers.netname);
|
||||||
|
|
||||||
|
target->disease++;
|
||||||
|
target->diseased_by = owner;
|
||||||
|
target->s.effects |= EF_FLIES;
|
||||||
|
|
||||||
|
plague = G_Spawn();
|
||||||
|
plague->movetype = MOVETYPE_NOCLIP;
|
||||||
|
plague->clipmask = MASK_SHOT;
|
||||||
|
plague->solid = SOLID_NOT;
|
||||||
|
VectorClear (plague->mins);
|
||||||
|
VectorClear (plague->maxs);
|
||||||
|
plague->owner = target;
|
||||||
|
plague->target_ent = owner;
|
||||||
|
plague->s.modelindex = gi.modelindex ("sprites/s_bubble.sp2");
|
||||||
|
plague->nextthink = level.time + 1.5;
|
||||||
|
plague->think = Disease_Think;
|
||||||
|
plague->s.effects |= EF_GIB;
|
||||||
|
plague->s.sound = gi.soundindex ("infantry/inflies1.wav");
|
||||||
|
plague->classname = "Disease";
|
||||||
|
VectorCopy(target->s.origin,plague->s.origin);
|
||||||
|
gi.linkentity (plague);
|
||||||
|
}
|
|
@ -0,0 +1,264 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
void unfreeze_player(edict_t *ent);
|
||||||
|
|
||||||
|
|
||||||
|
//Remove all the flames attached to a player
|
||||||
|
void Remove_Player_Flames (edict_t *ent)
|
||||||
|
{
|
||||||
|
edict_t *blip = NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!ent) return;
|
||||||
|
if (!ent->Flames) return;
|
||||||
|
|
||||||
|
for (i=1, blip=g_edicts+i ; i < globals.num_edicts ; i++,blip++)
|
||||||
|
{
|
||||||
|
if (blip->owner == ent)
|
||||||
|
{
|
||||||
|
|
||||||
|
//Flames
|
||||||
|
if (!strcmp(blip->classname, "fire") )
|
||||||
|
{
|
||||||
|
//set damage to zero so flame think function will clean it up
|
||||||
|
blip->dmg = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->Flames = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Remove_Flame(edict_t *ent)
|
||||||
|
{
|
||||||
|
if (ent->owner)
|
||||||
|
{
|
||||||
|
ent->owner->Flames--;
|
||||||
|
if (ent->owner->Flames < 0) ent->owner->Flames = 0;
|
||||||
|
}
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Fire_Think (edict_t *self)
|
||||||
|
{
|
||||||
|
vec3_t dir;
|
||||||
|
int damage;
|
||||||
|
float points;
|
||||||
|
vec3_t v;
|
||||||
|
int mod;
|
||||||
|
|
||||||
|
//Must have an owner
|
||||||
|
if (!self->owner)
|
||||||
|
{
|
||||||
|
G_FreeEdict (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level.time > self->delay)
|
||||||
|
{
|
||||||
|
Remove_Flame(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->dmg <= 0) //Something else wants this flame removed
|
||||||
|
{
|
||||||
|
Remove_Flame(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ++TeT add check for freed or dead owners
|
||||||
|
if (!self->owner->inuse || (self->owner->Flames == 0))
|
||||||
|
{
|
||||||
|
Remove_Flame(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//If player or flame is in water
|
||||||
|
if (self->owner->waterlevel || self->waterlevel)
|
||||||
|
{
|
||||||
|
//Play this if in water
|
||||||
|
if ((self->owner) && (self->owner->client))
|
||||||
|
gi.sound (self->owner, CHAN_WEAPON, gi.soundindex ("world/airhiss1.wav"), 1, ATTN_NORM, 0);
|
||||||
|
Remove_Flame(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
damage = self->dmg;
|
||||||
|
mod = self->mod;
|
||||||
|
if (mod == 0) mod = MOD_WF_FLAME;
|
||||||
|
|
||||||
|
VectorAdd (self->orb->mins, self->orb->maxs, v);
|
||||||
|
VectorMA (self->orb->s.origin, 0.5, v, v);
|
||||||
|
VectorSubtract (self->s.origin, v, v);
|
||||||
|
points = damage - 0.5 * (VectorLength (v));
|
||||||
|
|
||||||
|
if (points < 8) points = 8;
|
||||||
|
|
||||||
|
VectorSubtract (self->owner->s.origin, self->s.origin, dir);
|
||||||
|
|
||||||
|
VectorCopy(self->owner->s.origin,self->s.origin);
|
||||||
|
if (self->PlasmaDelay < level.time)
|
||||||
|
{
|
||||||
|
T_Damage (self->owner, self, self->orb, dir, self->owner->s.origin,vec3_origin, damage, 0, DAMAGE_NO_KNOCKBACK, mod);
|
||||||
|
self->PlasmaDelay = level.time + 0.8;
|
||||||
|
}
|
||||||
|
self->nextthink = level.time + .2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void burn_person(edict_t *target, edict_t *owner, int damage, int mod)
|
||||||
|
{
|
||||||
|
edict_t *flame;
|
||||||
|
|
||||||
|
if (target->frozen)
|
||||||
|
unfreeze_player(target); //Thaw them out!
|
||||||
|
|
||||||
|
if (!target->takedamage) return;
|
||||||
|
|
||||||
|
if (target->wf_team == owner->wf_team) return;
|
||||||
|
|
||||||
|
if (target->Flames > 1) return;
|
||||||
|
|
||||||
|
//If they are not a client, only burn decoys and sentry guns
|
||||||
|
if (!target->client)
|
||||||
|
{
|
||||||
|
if ((strcmp(target->classname, "decoy") != 0) &&
|
||||||
|
(strcmp(target->classname, "SentryGun") != 0))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Don't burn them if they have flame resistance
|
||||||
|
if ((target->client) && (target->client->player_special & SPECIAL_FLAME_RESISTANCE) )
|
||||||
|
{
|
||||||
|
gi.sound (target, CHAN_WEAPON, gi.soundindex ("items/protect4.wav"), 1, ATTN_NORM, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//burn em baby, but dead people don't scream (TeT)
|
||||||
|
if ((target->client) && (target->health > 0))
|
||||||
|
gi.sound (target, CHAN_WEAPON, gi.soundindex ("scream.wav"), 1, ATTN_NORM, 0);
|
||||||
|
|
||||||
|
target->Flames++;
|
||||||
|
flame = G_Spawn();
|
||||||
|
flame->movetype = MOVETYPE_NOCLIP;
|
||||||
|
flame->clipmask = MASK_SHOT;
|
||||||
|
flame->solid = SOLID_NOT;
|
||||||
|
flame->s.effects |= EF_ANIM_ALLFAST|EF_BFG|EF_HYPERBLASTER;//|EF_GRENADE|EF_BLASTER;
|
||||||
|
flame->velocity[0] = target->velocity[0];
|
||||||
|
flame->velocity[1] = target->velocity[1];
|
||||||
|
flame->velocity[2] = target->velocity[2];
|
||||||
|
|
||||||
|
VectorClear (flame->mins);
|
||||||
|
VectorClear (flame->maxs);
|
||||||
|
flame->s.modelindex = gi.modelindex ("sprites/fire.sp2");
|
||||||
|
flame->owner = target;
|
||||||
|
flame->orb = owner;
|
||||||
|
flame->delay = level.time + 10; //G.R. Made it larger to do more damage
|
||||||
|
flame->nextthink = level.time + .8;
|
||||||
|
flame->PlasmaDelay = level.time + 0.8;
|
||||||
|
flame->think = Fire_Think;
|
||||||
|
flame->SniperDamage = damage+2;//JR increased that
|
||||||
|
flame->classname = "fire";
|
||||||
|
flame->s.sound = gi.soundindex ("weapons/bfg__l1a.wav");
|
||||||
|
flame->dmg = damage;
|
||||||
|
flame->mod = mod;
|
||||||
|
gi.linkentity (flame);
|
||||||
|
|
||||||
|
VectorCopy(target->s.origin,flame->s.origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Napalm_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
if (other == ent->owner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (surf && (surf->flags & SURF_SKY))
|
||||||
|
{
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!other->takedamage)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Napalm_Think(edict_t *self)
|
||||||
|
{
|
||||||
|
edict_t *ent;
|
||||||
|
vec3_t v;
|
||||||
|
float points;
|
||||||
|
float dist;
|
||||||
|
ent = NULL;
|
||||||
|
while ((ent = findradius(ent, self->s.origin, 64)) != NULL)
|
||||||
|
{
|
||||||
|
if (!ent->takedamage)
|
||||||
|
continue;
|
||||||
|
if (!CanDamage (ent, self))
|
||||||
|
continue;
|
||||||
|
if (!CanDamage (ent, self->owner))
|
||||||
|
continue;
|
||||||
|
// ++TeT
|
||||||
|
if (ent->wf_team == self->wf_team)
|
||||||
|
continue;
|
||||||
|
VectorAdd (ent->mins, ent->maxs, v);
|
||||||
|
VectorMA (ent->s.origin, 0.5, v, v);
|
||||||
|
VectorSubtract (self->s.origin, v, v);
|
||||||
|
dist = VectorLength(v);
|
||||||
|
points = self->radius_dmg * (1.0 - sqrt(dist/self->dmg_radius));
|
||||||
|
if (ent == self->owner)
|
||||||
|
points = points * 0.5;
|
||||||
|
burn_person(ent, self->owner, 4, MOD_NAPALMGRENADE);
|
||||||
|
|
||||||
|
T_Damage (ent, self, self->owner, self->velocity, ent->s.origin, vec3_origin, 3, 25, DAMAGE_ENERGY,0);
|
||||||
|
}
|
||||||
|
self->nextthink = level.time + 0.8;
|
||||||
|
|
||||||
|
if (self->delay <level.time)
|
||||||
|
self->think = G_FreeEdict;
|
||||||
|
if (self->waterlevel)
|
||||||
|
{
|
||||||
|
G_FreeEdict (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Use Gregg's function for now
|
||||||
|
void fire_napalm2 (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
|
||||||
|
{
|
||||||
|
edict_t *grenade;
|
||||||
|
vec3_t dir;
|
||||||
|
vec3_t forward, right, up;
|
||||||
|
vectoangles (aimdir, dir);
|
||||||
|
AngleVectors (dir, forward, right, up);
|
||||||
|
grenade = G_Spawn();
|
||||||
|
VectorCopy (start, grenade->s.origin);
|
||||||
|
VectorScale (aimdir, speed, grenade->velocity);
|
||||||
|
VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
|
||||||
|
VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
|
||||||
|
VectorSet (grenade->avelocity, 300, 300, 300);
|
||||||
|
grenade->movetype = MOVETYPE_BOUNCE;
|
||||||
|
grenade->clipmask = MASK_SHOT;
|
||||||
|
grenade->solid = SOLID_BBOX;
|
||||||
|
grenade->s.effects |= /*EF_GRENADE|EF_BLASTER|*/EF_ANIM_ALLFAST|EF_BFG|EF_HYPERBLASTER;;
|
||||||
|
// VectorClear (grenade->mins);
|
||||||
|
// VectorClear (grenade->maxs);
|
||||||
|
grenade->s.modelindex = gi.modelindex ("sprites/fire.sp2");
|
||||||
|
grenade->owner = self;
|
||||||
|
grenade->wf_team = self->wf_team;
|
||||||
|
grenade->touch = Napalm_Touch;
|
||||||
|
grenade->think = Napalm_Think;
|
||||||
|
grenade->nextthink = level.time + 0.1;
|
||||||
|
grenade->delay = level.time + timer;
|
||||||
|
grenade->dmg = damage;
|
||||||
|
grenade->dmg_radius = damage_radius;
|
||||||
|
grenade->classname = "napalm";
|
||||||
|
// CCH: a few more attributes to let the grenade 'die'
|
||||||
|
VectorSet(grenade->mins, -32, -32, -32);
|
||||||
|
VectorSet(grenade->maxs, 32, 32, 32);
|
||||||
|
grenade->mass = 5;
|
||||||
|
grenade->monsterinfo.aiflags = AI_NOSTEP;
|
||||||
|
|
||||||
|
gi.linkentity (grenade);
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
/*Function: Start_Kamikaze_Mode
|
||||||
|
Places the edict passed to it into Kamikaze Mode
|
||||||
|
(probably best to pass a player to it)
|
||||||
|
Warns everyone that so and so is a kamikaze...*/
|
||||||
|
void stuffcmd(edict_t *e, char *s);
|
||||||
|
|
||||||
|
void Start_Kamikaze_Mode(edict_t *the_doomed_one)
|
||||||
|
{
|
||||||
|
//jR The great bug fix
|
||||||
|
if (the_doomed_one->health<0)
|
||||||
|
{
|
||||||
|
//gi.bprintf (PRINT_HIGH, "%s tried to cheat or crash the server!\n But good old Cryect stopped him\n Cheaters never!!!\nByeBye!", the_doomed_one->client->pers.netname);
|
||||||
|
//stuffcmd(the_doomed_one, "alias kickme say I cheat;say I try to cheat and crash servers thank you all and tell everyone how all about me!;disconnect;echo Wow what a great server crasher!");
|
||||||
|
//stuffcmd(the_doomed_one, "kickme");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* see if we are already in kamikaze mode*/
|
||||||
|
if (the_doomed_one->client->kamikaze_mode & 1)
|
||||||
|
{
|
||||||
|
safe_cprintf(the_doomed_one, PRINT_MEDIUM, "Already in Kamikaze Mode! Cancel to stop!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* dont run if in god mode */
|
||||||
|
if (the_doomed_one->flags & FL_GODMODE)
|
||||||
|
{
|
||||||
|
safe_cprintf(the_doomed_one, PRINT_MEDIUM, "Can't Kamikaze in God Mode!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* not in kamikaze mode yet */
|
||||||
|
the_doomed_one->client->kamikaze_mode = 1;
|
||||||
|
/* Give us only so long */
|
||||||
|
the_doomed_one->client->kamikaze_timeleft = KAMIKAZE_BLOW_TIME;
|
||||||
|
the_doomed_one->client->kamikaze_framenum = level.framenum + the_doomed_one->client->kamikaze_timeleft;
|
||||||
|
/* Warn the World */
|
||||||
|
//gi.sound( the_doomed_one, CHAN_WEAPON, gi.soundindex("makron/rail_up.wav"), 1, ATTN_NONE, 0 );
|
||||||
|
//GREGG - use 10 second countdown instead
|
||||||
|
//JR - I like it Gregg
|
||||||
|
gi.sound( the_doomed_one, CHAN_WEAPON, gi.soundindex("world/10_0.wav"), 1, ATTN_NONE, 0 );
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Function: Kamikaze_Active
|
||||||
|
Are we in Kamikaze Mode?
|
||||||
|
a helper function to see if we are running in Kamikaze Mode*/
|
||||||
|
qboolean Kamikaze_Active(edict_t *the_doomed_one)
|
||||||
|
{
|
||||||
|
return (the_doomed_one->client->kamikaze_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Function: Kamikaze_Cancel
|
||||||
|
Canceled for Some Reason Call if Player is killed before time is up*/
|
||||||
|
void Kamikaze_Cancel(edict_t *the_spared_one)
|
||||||
|
{
|
||||||
|
/* not in kamikaze mode yet */
|
||||||
|
the_spared_one->client->kamikaze_mode = 0;
|
||||||
|
/* Give us only so long */
|
||||||
|
the_spared_one->client->kamikaze_timeleft = 0;
|
||||||
|
the_spared_one->client->kamikaze_framenum = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Kamikaze_Explode(edict_t *the_doomed_one)
|
||||||
|
{
|
||||||
|
/* BANG ! and show the clients */
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_EXPLOSION1);
|
||||||
|
gi.WritePosition(the_doomed_one -> s.origin);
|
||||||
|
gi.multicast (the_doomed_one->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
/* A whole Lotta Damage */
|
||||||
|
T_RadiusDamage (the_doomed_one, the_doomed_one, KAMIKAZE_DAMAGE, NULL, KAMIKAZE_DAMAGE_RADUIS, MOD_KAMIKAZE);
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
/* Amount of Damage caused */
|
||||||
|
#define KAMIKAZE_DAMAGE 300
|
||||||
|
|
||||||
|
/* Radius of blast */
|
||||||
|
#define KAMIKAZE_DAMAGE_RADUIS 400
|
||||||
|
// Quake Units
|
||||||
|
|
||||||
|
/* Count down time */
|
||||||
|
//Gregg-Make it longer to match 10 second countdown
|
||||||
|
//#define KAMIKAZE_BLOW_TIME 30 // 1/10 seconds
|
||||||
|
#define KAMIKAZE_BLOW_TIME 110 // 1/10 seconds
|
||||||
|
|
||||||
|
void Start_Kamikaze_Mode(edict_t *the_doomed_one);
|
||||||
|
// setup and start self destruct mode
|
||||||
|
qboolean Kamikaze_Active(edict_t *the_doomed_one);
|
||||||
|
void Kamikaze_Explode(edict_t *the_doomed_one);
|
||||||
|
void Kamikaze_Cancel(edict_t *the_spared_one);
|
|
@ -0,0 +1,19 @@
|
||||||
|
// my functions
|
||||||
|
void PlaceLaser (edict_t *ent);
|
||||||
|
void pre_target_laser_think (edict_t *self);
|
||||||
|
void pre_target_laser_def_think (edict_t *self);
|
||||||
|
// controlling parameters
|
||||||
|
// #define LASER_TIME 30
|
||||||
|
#define LASER_TIME 90
|
||||||
|
#define MAX_LASERS 5
|
||||||
|
#define CELLS_FOR_LASER 20
|
||||||
|
#define LASER_DAMAGE 100
|
||||||
|
#define LASER_MOUNT_DAMAGE 50
|
||||||
|
#define LASER_MOUNT_DAMAGE_RADIUS 64
|
||||||
|
// In-built Quake2 routines
|
||||||
|
void target_laser_use (edict_t *self, edict_t *other, edict_t *activator);
|
||||||
|
void target_laser_think (edict_t *self);
|
||||||
|
void target_laser_def_think (edict_t *self);
|
||||||
|
void target_laser_on (edict_t *self);
|
||||||
|
void target_laser_def_on (edict_t *self);
|
||||||
|
void target_laser_off (edict_t *self);
|
|
@ -0,0 +1 @@
|
||||||
|
|
|
@ -0,0 +1,469 @@
|
||||||
|
// m_flash.c
|
||||||
|
|
||||||
|
#include "q_shared.h"
|
||||||
|
|
||||||
|
// this file is included in both the game dll and quake2,
|
||||||
|
// the game needs it to source shot locations, the client
|
||||||
|
// needs it to position muzzle flashes
|
||||||
|
vec3_t monster_flash_offset [] =
|
||||||
|
{
|
||||||
|
// flash 0 is not used
|
||||||
|
0.0, 0.0, 0.0,
|
||||||
|
|
||||||
|
// MZ2_TANK_BLASTER_1 1
|
||||||
|
20.7, -18.5, 28.7,
|
||||||
|
// MZ2_TANK_BLASTER_2 2
|
||||||
|
16.6, -21.5, 30.1,
|
||||||
|
// MZ2_TANK_BLASTER_3 3
|
||||||
|
11.8, -23.9, 32.1,
|
||||||
|
// MZ2_TANK_MACHINEGUN_1 4
|
||||||
|
22.9, -0.7, 25.3,
|
||||||
|
// MZ2_TANK_MACHINEGUN_2 5
|
||||||
|
22.2, 6.2, 22.3,
|
||||||
|
// MZ2_TANK_MACHINEGUN_3 6
|
||||||
|
19.4, 13.1, 18.6,
|
||||||
|
// MZ2_TANK_MACHINEGUN_4 7
|
||||||
|
19.4, 18.8, 18.6,
|
||||||
|
// MZ2_TANK_MACHINEGUN_5 8
|
||||||
|
17.9, 25.0, 18.6,
|
||||||
|
// MZ2_TANK_MACHINEGUN_6 9
|
||||||
|
14.1, 30.5, 20.6,
|
||||||
|
// MZ2_TANK_MACHINEGUN_7 10
|
||||||
|
9.3, 35.3, 22.1,
|
||||||
|
// MZ2_TANK_MACHINEGUN_8 11
|
||||||
|
4.7, 38.4, 22.1,
|
||||||
|
// MZ2_TANK_MACHINEGUN_9 12
|
||||||
|
-1.1, 40.4, 24.1,
|
||||||
|
// MZ2_TANK_MACHINEGUN_10 13
|
||||||
|
-6.5, 41.2, 24.1,
|
||||||
|
// MZ2_TANK_MACHINEGUN_11 14
|
||||||
|
3.2, 40.1, 24.7,
|
||||||
|
// MZ2_TANK_MACHINEGUN_12 15
|
||||||
|
11.7, 36.7, 26.0,
|
||||||
|
// MZ2_TANK_MACHINEGUN_13 16
|
||||||
|
18.9, 31.3, 26.0,
|
||||||
|
// MZ2_TANK_MACHINEGUN_14 17
|
||||||
|
24.4, 24.4, 26.4,
|
||||||
|
// MZ2_TANK_MACHINEGUN_15 18
|
||||||
|
27.1, 17.1, 27.2,
|
||||||
|
// MZ2_TANK_MACHINEGUN_16 19
|
||||||
|
28.5, 9.1, 28.0,
|
||||||
|
// MZ2_TANK_MACHINEGUN_17 20
|
||||||
|
27.1, 2.2, 28.0,
|
||||||
|
// MZ2_TANK_MACHINEGUN_18 21
|
||||||
|
24.9, -2.8, 28.0,
|
||||||
|
// MZ2_TANK_MACHINEGUN_19 22
|
||||||
|
21.6, -7.0, 26.4,
|
||||||
|
// MZ2_TANK_ROCKET_1 23
|
||||||
|
6.2, 29.1, 49.1,
|
||||||
|
// MZ2_TANK_ROCKET_2 24
|
||||||
|
6.9, 23.8, 49.1,
|
||||||
|
// MZ2_TANK_ROCKET_3 25
|
||||||
|
8.3, 17.8, 49.5,
|
||||||
|
|
||||||
|
// MZ2_INFANTRY_MACHINEGUN_1 26
|
||||||
|
26.6, 7.1, 13.1,
|
||||||
|
// MZ2_INFANTRY_MACHINEGUN_2 27
|
||||||
|
18.2, 7.5, 15.4,
|
||||||
|
// MZ2_INFANTRY_MACHINEGUN_3 28
|
||||||
|
17.2, 10.3, 17.9,
|
||||||
|
// MZ2_INFANTRY_MACHINEGUN_4 29
|
||||||
|
17.0, 12.8, 20.1,
|
||||||
|
// MZ2_INFANTRY_MACHINEGUN_5 30
|
||||||
|
15.1, 14.1, 21.8,
|
||||||
|
// MZ2_INFANTRY_MACHINEGUN_6 31
|
||||||
|
11.8, 17.2, 23.1,
|
||||||
|
// MZ2_INFANTRY_MACHINEGUN_7 32
|
||||||
|
11.4, 20.2, 21.0,
|
||||||
|
// MZ2_INFANTRY_MACHINEGUN_8 33
|
||||||
|
9.0, 23.0, 18.9,
|
||||||
|
// MZ2_INFANTRY_MACHINEGUN_9 34
|
||||||
|
13.9, 18.6, 17.7,
|
||||||
|
// MZ2_INFANTRY_MACHINEGUN_10 35
|
||||||
|
15.4, 15.6, 15.8,
|
||||||
|
// MZ2_INFANTRY_MACHINEGUN_11 36
|
||||||
|
10.2, 15.2, 25.1,
|
||||||
|
// MZ2_INFANTRY_MACHINEGUN_12 37
|
||||||
|
-1.9, 15.1, 28.2,
|
||||||
|
// MZ2_INFANTRY_MACHINEGUN_13 38
|
||||||
|
-12.4, 13.0, 20.2,
|
||||||
|
|
||||||
|
// MZ2_SOLDIER_BLASTER_1 39
|
||||||
|
10.6 * 1.2, 7.7 * 1.2, 7.8 * 1.2,
|
||||||
|
// MZ2_SOLDIER_BLASTER_2 40
|
||||||
|
21.1 * 1.2, 3.6 * 1.2, 19.0 * 1.2,
|
||||||
|
// MZ2_SOLDIER_SHOTGUN_1 41
|
||||||
|
10.6 * 1.2, 7.7 * 1.2, 7.8 * 1.2,
|
||||||
|
// MZ2_SOLDIER_SHOTGUN_2 42
|
||||||
|
21.1 * 1.2, 3.6 * 1.2, 19.0 * 1.2,
|
||||||
|
// MZ2_SOLDIER_MACHINEGUN_1 43
|
||||||
|
10.6 * 1.2, 7.7 * 1.2, 7.8 * 1.2,
|
||||||
|
// MZ2_SOLDIER_MACHINEGUN_2 44
|
||||||
|
21.1 * 1.2, 3.6 * 1.2, 19.0 * 1.2,
|
||||||
|
|
||||||
|
// MZ2_GUNNER_MACHINEGUN_1 45
|
||||||
|
30.1 * 1.15, 3.9 * 1.15, 19.6 * 1.15,
|
||||||
|
// MZ2_GUNNER_MACHINEGUN_2 46
|
||||||
|
29.1 * 1.15, 2.5 * 1.15, 20.7 * 1.15,
|
||||||
|
// MZ2_GUNNER_MACHINEGUN_3 47
|
||||||
|
28.2 * 1.15, 2.5 * 1.15, 22.2 * 1.15,
|
||||||
|
// MZ2_GUNNER_MACHINEGUN_4 48
|
||||||
|
28.2 * 1.15, 3.6 * 1.15, 22.0 * 1.15,
|
||||||
|
// MZ2_GUNNER_MACHINEGUN_5 49
|
||||||
|
26.9 * 1.15, 2.0 * 1.15, 23.4 * 1.15,
|
||||||
|
// MZ2_GUNNER_MACHINEGUN_6 50
|
||||||
|
26.5 * 1.15, 0.6 * 1.15, 20.8 * 1.15,
|
||||||
|
// MZ2_GUNNER_MACHINEGUN_7 51
|
||||||
|
26.9 * 1.15, 0.5 * 1.15, 21.5 * 1.15,
|
||||||
|
// MZ2_GUNNER_MACHINEGUN_8 52
|
||||||
|
29.0 * 1.15, 2.4 * 1.15, 19.5 * 1.15,
|
||||||
|
// MZ2_GUNNER_GRENADE_1 53
|
||||||
|
4.6 * 1.15, -16.8 * 1.15, 7.3 * 1.15,
|
||||||
|
// MZ2_GUNNER_GRENADE_2 54
|
||||||
|
4.6 * 1.15, -16.8 * 1.15, 7.3 * 1.15,
|
||||||
|
// MZ2_GUNNER_GRENADE_3 55
|
||||||
|
4.6 * 1.15, -16.8 * 1.15, 7.3 * 1.15,
|
||||||
|
// MZ2_GUNNER_GRENADE_4 56
|
||||||
|
4.6 * 1.15, -16.8 * 1.15, 7.3 * 1.15,
|
||||||
|
|
||||||
|
// MZ2_CHICK_ROCKET_1 57
|
||||||
|
// -24.8, -9.0, 39.0,
|
||||||
|
24.8, -9.0, 39.0, // PGM - this was incorrect in Q2
|
||||||
|
|
||||||
|
// MZ2_FLYER_BLASTER_1 58
|
||||||
|
12.1, 13.4, -14.5,
|
||||||
|
// MZ2_FLYER_BLASTER_2 59
|
||||||
|
12.1, -7.4, -14.5,
|
||||||
|
|
||||||
|
// MZ2_MEDIC_BLASTER_1 60
|
||||||
|
12.1, 5.4, 16.5,
|
||||||
|
|
||||||
|
// MZ2_GLADIATOR_RAILGUN_1 61
|
||||||
|
30.0, 18.0, 28.0,
|
||||||
|
|
||||||
|
// MZ2_HOVER_BLASTER_1 62
|
||||||
|
32.5, -0.8, 10.0,
|
||||||
|
|
||||||
|
// MZ2_ACTOR_MACHINEGUN_1 63
|
||||||
|
18.4, 7.4, 9.6,
|
||||||
|
|
||||||
|
// MZ2_SUPERTANK_MACHINEGUN_1 64
|
||||||
|
30.0, 30.0, 88.5,
|
||||||
|
// MZ2_SUPERTANK_MACHINEGUN_2 65
|
||||||
|
30.0, 30.0, 88.5,
|
||||||
|
// MZ2_SUPERTANK_MACHINEGUN_3 66
|
||||||
|
30.0, 30.0, 88.5,
|
||||||
|
// MZ2_SUPERTANK_MACHINEGUN_4 67
|
||||||
|
30.0, 30.0, 88.5,
|
||||||
|
// MZ2_SUPERTANK_MACHINEGUN_5 68
|
||||||
|
30.0, 30.0, 88.5,
|
||||||
|
// MZ2_SUPERTANK_MACHINEGUN_6 69
|
||||||
|
30.0, 30.0, 88.5,
|
||||||
|
// MZ2_SUPERTANK_ROCKET_1 70
|
||||||
|
16.0, -22.5, 91.2,
|
||||||
|
// MZ2_SUPERTANK_ROCKET_2 71
|
||||||
|
16.0, -33.4, 86.7,
|
||||||
|
// MZ2_SUPERTANK_ROCKET_3 72
|
||||||
|
16.0, -42.8, 83.3,
|
||||||
|
|
||||||
|
// --- Start Xian Stuff ---
|
||||||
|
// MZ2_BOSS2_MACHINEGUN_L1 73
|
||||||
|
32, -40, 70,
|
||||||
|
// MZ2_BOSS2_MACHINEGUN_L2 74
|
||||||
|
32, -40, 70,
|
||||||
|
// MZ2_BOSS2_MACHINEGUN_L3 75
|
||||||
|
32, -40, 70,
|
||||||
|
// MZ2_BOSS2_MACHINEGUN_L4 76
|
||||||
|
32, -40, 70,
|
||||||
|
// MZ2_BOSS2_MACHINEGUN_L5 77
|
||||||
|
32, -40, 70,
|
||||||
|
// --- End Xian Stuff
|
||||||
|
|
||||||
|
// MZ2_BOSS2_ROCKET_1 78
|
||||||
|
22.0, 16.0, 10.0,
|
||||||
|
// MZ2_BOSS2_ROCKET_2 79
|
||||||
|
22.0, 8.0, 10.0,
|
||||||
|
// MZ2_BOSS2_ROCKET_3 80
|
||||||
|
22.0, -8.0, 10.0,
|
||||||
|
// MZ2_BOSS2_ROCKET_4 81
|
||||||
|
22.0, -16.0, 10.0,
|
||||||
|
|
||||||
|
// MZ2_FLOAT_BLASTER_1 82
|
||||||
|
32.5, -0.8, 10,
|
||||||
|
|
||||||
|
// MZ2_SOLDIER_BLASTER_3 83
|
||||||
|
20.8 * 1.2, 10.1 * 1.2, -2.7 * 1.2,
|
||||||
|
// MZ2_SOLDIER_SHOTGUN_3 84
|
||||||
|
20.8 * 1.2, 10.1 * 1.2, -2.7 * 1.2,
|
||||||
|
// MZ2_SOLDIER_MACHINEGUN_3 85
|
||||||
|
20.8 * 1.2, 10.1 * 1.2, -2.7 * 1.2,
|
||||||
|
// MZ2_SOLDIER_BLASTER_4 86
|
||||||
|
7.6 * 1.2, 9.3 * 1.2, 0.8 * 1.2,
|
||||||
|
// MZ2_SOLDIER_SHOTGUN_4 87
|
||||||
|
7.6 * 1.2, 9.3 * 1.2, 0.8 * 1.2,
|
||||||
|
// MZ2_SOLDIER_MACHINEGUN_4 88
|
||||||
|
7.6 * 1.2, 9.3 * 1.2, 0.8 * 1.2,
|
||||||
|
// MZ2_SOLDIER_BLASTER_5 89
|
||||||
|
30.5 * 1.2, 9.9 * 1.2, -18.7 * 1.2,
|
||||||
|
// MZ2_SOLDIER_SHOTGUN_5 90
|
||||||
|
30.5 * 1.2, 9.9 * 1.2, -18.7 * 1.2,
|
||||||
|
// MZ2_SOLDIER_MACHINEGUN_5 91
|
||||||
|
30.5 * 1.2, 9.9 * 1.2, -18.7 * 1.2,
|
||||||
|
// MZ2_SOLDIER_BLASTER_6 92
|
||||||
|
27.6 * 1.2, 3.4 * 1.2, -10.4 * 1.2,
|
||||||
|
// MZ2_SOLDIER_SHOTGUN_6 93
|
||||||
|
27.6 * 1.2, 3.4 * 1.2, -10.4 * 1.2,
|
||||||
|
// MZ2_SOLDIER_MACHINEGUN_6 94
|
||||||
|
27.6 * 1.2, 3.4 * 1.2, -10.4 * 1.2,
|
||||||
|
// MZ2_SOLDIER_BLASTER_7 95
|
||||||
|
28.9 * 1.2, 4.6 * 1.2, -8.1 * 1.2,
|
||||||
|
// MZ2_SOLDIER_SHOTGUN_7 96
|
||||||
|
28.9 * 1.2, 4.6 * 1.2, -8.1 * 1.2,
|
||||||
|
// MZ2_SOLDIER_MACHINEGUN_7 97
|
||||||
|
28.9 * 1.2, 4.6 * 1.2, -8.1 * 1.2,
|
||||||
|
// MZ2_SOLDIER_BLASTER_8 98
|
||||||
|
// 34.5 * 1.2, 9.6 * 1.2, 6.1 * 1.2,
|
||||||
|
31.5 * 1.2, 9.6 * 1.2, 10.1 * 1.2,
|
||||||
|
// MZ2_SOLDIER_SHOTGUN_8 99
|
||||||
|
34.5 * 1.2, 9.6 * 1.2, 6.1 * 1.2,
|
||||||
|
// MZ2_SOLDIER_MACHINEGUN_8 100
|
||||||
|
34.5 * 1.2, 9.6 * 1.2, 6.1 * 1.2,
|
||||||
|
|
||||||
|
// --- Xian shit below ---
|
||||||
|
// MZ2_MAKRON_BFG 101
|
||||||
|
17, -19.5, 62.9,
|
||||||
|
// MZ2_MAKRON_BLASTER_1 102
|
||||||
|
-3.6, -24.1, 59.5,
|
||||||
|
// MZ2_MAKRON_BLASTER_2 103
|
||||||
|
-1.6, -19.3, 59.5,
|
||||||
|
// MZ2_MAKRON_BLASTER_3 104
|
||||||
|
-0.1, -14.4, 59.5,
|
||||||
|
// MZ2_MAKRON_BLASTER_4 105
|
||||||
|
2.0, -7.6, 59.5,
|
||||||
|
// MZ2_MAKRON_BLASTER_5 106
|
||||||
|
3.4, 1.3, 59.5,
|
||||||
|
// MZ2_MAKRON_BLASTER_6 107
|
||||||
|
3.7, 11.1, 59.5,
|
||||||
|
// MZ2_MAKRON_BLASTER_7 108
|
||||||
|
-0.3, 22.3, 59.5,
|
||||||
|
// MZ2_MAKRON_BLASTER_8 109
|
||||||
|
-6, 33, 59.5,
|
||||||
|
// MZ2_MAKRON_BLASTER_9 110
|
||||||
|
-9.3, 36.4, 59.5,
|
||||||
|
// MZ2_MAKRON_BLASTER_10 111
|
||||||
|
-7, 35, 59.5,
|
||||||
|
// MZ2_MAKRON_BLASTER_11 112
|
||||||
|
-2.1, 29, 59.5,
|
||||||
|
// MZ2_MAKRON_BLASTER_12 113
|
||||||
|
3.9, 17.3, 59.5,
|
||||||
|
// MZ2_MAKRON_BLASTER_13 114
|
||||||
|
6.1, 5.8, 59.5,
|
||||||
|
// MZ2_MAKRON_BLASTER_14 115
|
||||||
|
5.9, -4.4, 59.5,
|
||||||
|
// MZ2_MAKRON_BLASTER_15 116
|
||||||
|
4.2, -14.1, 59.5,
|
||||||
|
// MZ2_MAKRON_BLASTER_16 117
|
||||||
|
2.4, -18.8, 59.5,
|
||||||
|
// MZ2_MAKRON_BLASTER_17 118
|
||||||
|
-1.8, -25.5, 59.5,
|
||||||
|
// MZ2_MAKRON_RAILGUN_1 119
|
||||||
|
-17.3, 7.8, 72.4,
|
||||||
|
|
||||||
|
// MZ2_JORG_MACHINEGUN_L1 120
|
||||||
|
78.5, -47.1, 96,
|
||||||
|
// MZ2_JORG_MACHINEGUN_L2 121
|
||||||
|
78.5, -47.1, 96,
|
||||||
|
// MZ2_JORG_MACHINEGUN_L3 122
|
||||||
|
78.5, -47.1, 96,
|
||||||
|
// MZ2_JORG_MACHINEGUN_L4 123
|
||||||
|
78.5, -47.1, 96,
|
||||||
|
// MZ2_JORG_MACHINEGUN_L5 124
|
||||||
|
78.5, -47.1, 96,
|
||||||
|
// MZ2_JORG_MACHINEGUN_L6 125
|
||||||
|
78.5, -47.1, 96,
|
||||||
|
// MZ2_JORG_MACHINEGUN_R1 126
|
||||||
|
78.5, 46.7, 96,
|
||||||
|
// MZ2_JORG_MACHINEGUN_R2 127
|
||||||
|
78.5, 46.7, 96,
|
||||||
|
// MZ2_JORG_MACHINEGUN_R3 128
|
||||||
|
78.5, 46.7, 96,
|
||||||
|
// MZ2_JORG_MACHINEGUN_R4 129
|
||||||
|
78.5, 46.7, 96,
|
||||||
|
// MZ2_JORG_MACHINEGUN_R5 130
|
||||||
|
78.5, 46.7, 96,
|
||||||
|
// MZ2_JORG_MACHINEGUN_R6 131
|
||||||
|
78.5, 46.7, 96,
|
||||||
|
// MZ2_JORG_BFG_1 132
|
||||||
|
6.3, -9, 111.2,
|
||||||
|
|
||||||
|
// MZ2_BOSS2_MACHINEGUN_R1 73
|
||||||
|
32, 40, 70,
|
||||||
|
// MZ2_BOSS2_MACHINEGUN_R2 74
|
||||||
|
32, 40, 70,
|
||||||
|
// MZ2_BOSS2_MACHINEGUN_R3 75
|
||||||
|
32, 40, 70,
|
||||||
|
// MZ2_BOSS2_MACHINEGUN_R4 76
|
||||||
|
32, 40, 70,
|
||||||
|
// MZ2_BOSS2_MACHINEGUN_R5 77
|
||||||
|
32, 40, 70,
|
||||||
|
|
||||||
|
// --- End Xian Shit ---
|
||||||
|
|
||||||
|
// ROGUE
|
||||||
|
// note that the above really ends at 137
|
||||||
|
// carrier machineguns
|
||||||
|
// MZ2_CARRIER_MACHINEGUN_L1
|
||||||
|
56, -32, 32,
|
||||||
|
// MZ2_CARRIER_MACHINEGUN_R1
|
||||||
|
56, 32, 32,
|
||||||
|
// MZ2_CARRIER_GRENADE
|
||||||
|
42, 24, 50,
|
||||||
|
// MZ2_TURRET_MACHINEGUN 141
|
||||||
|
16, 0, 0,
|
||||||
|
// MZ2_TURRET_ROCKET 142
|
||||||
|
16, 0, 0,
|
||||||
|
// MZ2_TURRET_BLASTER 143
|
||||||
|
16, 0, 0,
|
||||||
|
// MZ2_STALKER_BLASTER 144
|
||||||
|
24, 0, 6,
|
||||||
|
// MZ2_DAEDALUS_BLASTER 145
|
||||||
|
32.5, -0.8, 10.0,
|
||||||
|
// MZ2_MEDIC_BLASTER_2 146
|
||||||
|
12.1, 5.4, 16.5,
|
||||||
|
// MZ2_CARRIER_RAILGUN 147
|
||||||
|
32, 0, 6,
|
||||||
|
// MZ2_WIDOW_DISRUPTOR 148
|
||||||
|
57.72, 14.50, 88.81,
|
||||||
|
// MZ2_WIDOW_BLASTER 149
|
||||||
|
56, 32, 32,
|
||||||
|
// MZ2_WIDOW_RAIL 150
|
||||||
|
62, -20, 84,
|
||||||
|
// MZ2_WIDOW_PLASMABEAM 151 // PMM - not used!
|
||||||
|
32, 0, 6,
|
||||||
|
// MZ2_CARRIER_MACHINEGUN_L2 152
|
||||||
|
61, -32, 12,
|
||||||
|
// MZ2_CARRIER_MACHINEGUN_R2 153
|
||||||
|
61, 32, 12,
|
||||||
|
// MZ2_WIDOW_RAIL_LEFT 154
|
||||||
|
17, -62, 91,
|
||||||
|
// MZ2_WIDOW_RAIL_RIGHT 155
|
||||||
|
68, 12, 86,
|
||||||
|
// MZ2_WIDOW_BLASTER_SWEEP1 156 pmm - the sweeps need to be in sequential order
|
||||||
|
47.5, 56, 89,
|
||||||
|
// MZ2_WIDOW_BLASTER_SWEEP2 157
|
||||||
|
54, 52, 91,
|
||||||
|
// MZ2_WIDOW_BLASTER_SWEEP3 158
|
||||||
|
58, 40, 91,
|
||||||
|
// MZ2_WIDOW_BLASTER_SWEEP4 159
|
||||||
|
68, 30, 88,
|
||||||
|
// MZ2_WIDOW_BLASTER_SWEEP5 160
|
||||||
|
74, 20, 88,
|
||||||
|
// MZ2_WIDOW_BLASTER_SWEEP6 161
|
||||||
|
73, 11, 87,
|
||||||
|
// MZ2_WIDOW_BLASTER_SWEEP7 162
|
||||||
|
73, 3, 87,
|
||||||
|
// MZ2_WIDOW_BLASTER_SWEEP8 163
|
||||||
|
70, -12, 87,
|
||||||
|
// MZ2_WIDOW_BLASTER_SWEEP9 164
|
||||||
|
67, -20, 90,
|
||||||
|
// MZ2_WIDOW_BLASTER_100 165
|
||||||
|
-20, 76, 90,
|
||||||
|
// MZ2_WIDOW_BLASTER_90 166
|
||||||
|
-8, 74, 90,
|
||||||
|
// MZ2_WIDOW_BLASTER_80 167
|
||||||
|
0, 72, 90,
|
||||||
|
// MZ2_WIDOW_BLASTER_70 168 d06
|
||||||
|
10, 71, 89,
|
||||||
|
// MZ2_WIDOW_BLASTER_60 169 d07
|
||||||
|
23, 70, 87,
|
||||||
|
// MZ2_WIDOW_BLASTER_50 170 d08
|
||||||
|
32, 64, 85,
|
||||||
|
// MZ2_WIDOW_BLASTER_40 171
|
||||||
|
40, 58, 84,
|
||||||
|
// MZ2_WIDOW_BLASTER_30 172 d10
|
||||||
|
48, 50, 83,
|
||||||
|
// MZ2_WIDOW_BLASTER_20 173
|
||||||
|
54, 42, 82,
|
||||||
|
// MZ2_WIDOW_BLASTER_10 174 d12
|
||||||
|
56, 34, 82,
|
||||||
|
// MZ2_WIDOW_BLASTER_0 175
|
||||||
|
58, 26, 82,
|
||||||
|
// MZ2_WIDOW_BLASTER_10L 176 d14
|
||||||
|
60, 16, 82,
|
||||||
|
// MZ2_WIDOW_BLASTER_20L 177
|
||||||
|
59, 6, 81,
|
||||||
|
// MZ2_WIDOW_BLASTER_30L 178 d16
|
||||||
|
58, -2, 80,
|
||||||
|
// MZ2_WIDOW_BLASTER_40L 179
|
||||||
|
57, -10, 79,
|
||||||
|
// MZ2_WIDOW_BLASTER_50L 180 d18
|
||||||
|
54, -18, 78,
|
||||||
|
// MZ2_WIDOW_BLASTER_60L 181
|
||||||
|
42, -32, 80,
|
||||||
|
// MZ2_WIDOW_BLASTER_70L 182 d20
|
||||||
|
36, -40, 78,
|
||||||
|
// MZ2_WIDOW_RUN_1 183
|
||||||
|
68.4, 10.88, 82.08,
|
||||||
|
// MZ2_WIDOW_RUN_2 184
|
||||||
|
68.51, 8.64, 85.14,
|
||||||
|
// MZ2_WIDOW_RUN_3 185
|
||||||
|
68.66, 6.38, 88.78,
|
||||||
|
// MZ2_WIDOW_RUN_4 186
|
||||||
|
68.73, 5.1, 84.47,
|
||||||
|
// MZ2_WIDOW_RUN_5 187
|
||||||
|
68.82, 4.79, 80.52,
|
||||||
|
// MZ2_WIDOW_RUN_6 188
|
||||||
|
68.77, 6.11, 85.37,
|
||||||
|
// MZ2_WIDOW_RUN_7 189
|
||||||
|
68.67, 7.99, 90.24,
|
||||||
|
// MZ2_WIDOW_RUN_8 190
|
||||||
|
68.55, 9.54, 87.36,
|
||||||
|
// MZ2_CARRIER_ROCKET_1 191
|
||||||
|
0, 0, -5,
|
||||||
|
// MZ2_CARRIER_ROCKET_2 192
|
||||||
|
0, 0, -5,
|
||||||
|
// MZ2_CARRIER_ROCKET_3 193
|
||||||
|
0, 0, -5,
|
||||||
|
// MZ2_CARRIER_ROCKET_4 194
|
||||||
|
0, 0, -5,
|
||||||
|
// MZ2_WIDOW2_BEAMER_1 195
|
||||||
|
// 72.13, -17.63, 93.77,
|
||||||
|
69.00, -17.63, 93.77,
|
||||||
|
// MZ2_WIDOW2_BEAMER_2 196
|
||||||
|
// 71.46, -17.08, 89.82,
|
||||||
|
69.00, -17.08, 89.82,
|
||||||
|
// MZ2_WIDOW2_BEAMER_3 197
|
||||||
|
// 71.47, -18.40, 90.70,
|
||||||
|
69.00, -18.40, 90.70,
|
||||||
|
// MZ2_WIDOW2_BEAMER_4 198
|
||||||
|
// 71.96, -18.34, 94.32,
|
||||||
|
69.00, -18.34, 94.32,
|
||||||
|
// MZ2_WIDOW2_BEAMER_5 199
|
||||||
|
// 72.25, -18.30, 97.98,
|
||||||
|
69.00, -18.30, 97.98,
|
||||||
|
// MZ2_WIDOW2_BEAM_SWEEP_1 200
|
||||||
|
45.04, -59.02, 92.24,
|
||||||
|
// MZ2_WIDOW2_BEAM_SWEEP_2 201
|
||||||
|
50.68, -54.70, 91.96,
|
||||||
|
// MZ2_WIDOW2_BEAM_SWEEP_3 202
|
||||||
|
56.57, -47.72, 91.65,
|
||||||
|
// MZ2_WIDOW2_BEAM_SWEEP_4 203
|
||||||
|
61.75, -38.75, 91.38,
|
||||||
|
// MZ2_WIDOW2_BEAM_SWEEP_5 204
|
||||||
|
65.55, -28.76, 91.24,
|
||||||
|
// MZ2_WIDOW2_BEAM_SWEEP_6 205
|
||||||
|
67.79, -18.90, 91.22,
|
||||||
|
// MZ2_WIDOW2_BEAM_SWEEP_7 206
|
||||||
|
68.60, -9.52, 91.23,
|
||||||
|
// MZ2_WIDOW2_BEAM_SWEEP_8 207
|
||||||
|
68.08, 0.18, 91.32,
|
||||||
|
// MZ2_WIDOW2_BEAM_SWEEP_9 208
|
||||||
|
66.14, 9.79, 91.44,
|
||||||
|
// MZ2_WIDOW2_BEAM_SWEEP_10 209
|
||||||
|
62.77, 18.91, 91.65,
|
||||||
|
// MZ2_WIDOW2_BEAM_SWEEP_11 210
|
||||||
|
58.29, 27.11, 92.00,
|
||||||
|
|
||||||
|
// end of table
|
||||||
|
0.0, 0.0, 0.0
|
||||||
|
};
|
|
@ -0,0 +1,541 @@
|
||||||
|
// m_move.c -- monster movement
|
||||||
|
|
||||||
|
#include "g_local.h"
|
||||||
|
|
||||||
|
#define STEPSIZE 18
|
||||||
|
|
||||||
|
/*
|
||||||
|
=============
|
||||||
|
M_CheckBottom
|
||||||
|
|
||||||
|
Returns false if any part of the bottom of the entity is off an edge that
|
||||||
|
is not a staircase.
|
||||||
|
|
||||||
|
=============
|
||||||
|
*/
|
||||||
|
int c_yes, c_no;
|
||||||
|
|
||||||
|
qboolean M_CheckBottom (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t mins, maxs, start, stop;
|
||||||
|
trace_t trace;
|
||||||
|
int x, y;
|
||||||
|
float mid, bottom;
|
||||||
|
|
||||||
|
VectorAdd (ent->s.origin, ent->mins, mins);
|
||||||
|
VectorAdd (ent->s.origin, ent->maxs, maxs);
|
||||||
|
|
||||||
|
// if all of the points under the corners are solid world, don't bother
|
||||||
|
// with the tougher checks
|
||||||
|
// the corners must be within 16 of the midpoint
|
||||||
|
start[2] = mins[2] - 1;
|
||||||
|
for (x=0 ; x<=1 ; x++)
|
||||||
|
for (y=0 ; y<=1 ; y++)
|
||||||
|
{
|
||||||
|
start[0] = x ? maxs[0] : mins[0];
|
||||||
|
start[1] = y ? maxs[1] : mins[1];
|
||||||
|
if (gi.pointcontents (start) != CONTENTS_SOLID)
|
||||||
|
goto realcheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
c_yes++;
|
||||||
|
return true; // we got out easy
|
||||||
|
|
||||||
|
realcheck:
|
||||||
|
c_no++;
|
||||||
|
//
|
||||||
|
// check it for real...
|
||||||
|
//
|
||||||
|
start[2] = mins[2];
|
||||||
|
|
||||||
|
// the midpoint must be within 16 of the bottom
|
||||||
|
start[0] = stop[0] = (mins[0] + maxs[0])*0.5;
|
||||||
|
start[1] = stop[1] = (mins[1] + maxs[1])*0.5;
|
||||||
|
stop[2] = start[2] - 2*STEPSIZE;
|
||||||
|
trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
|
||||||
|
|
||||||
|
if (trace.fraction == 1.0)
|
||||||
|
return false;
|
||||||
|
mid = bottom = trace.endpos[2];
|
||||||
|
|
||||||
|
// the corners must be within 16 of the midpoint
|
||||||
|
for (x=0 ; x<=1 ; x++)
|
||||||
|
for (y=0 ; y<=1 ; y++)
|
||||||
|
{
|
||||||
|
start[0] = stop[0] = x ? maxs[0] : mins[0];
|
||||||
|
start[1] = stop[1] = y ? maxs[1] : mins[1];
|
||||||
|
|
||||||
|
trace = gi.trace (start, vec3_origin, vec3_origin, stop, ent, MASK_MONSTERSOLID);
|
||||||
|
|
||||||
|
if (trace.fraction != 1.0 && trace.endpos[2] > bottom)
|
||||||
|
bottom = trace.endpos[2];
|
||||||
|
if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
c_yes++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
=============
|
||||||
|
SV_movestep
|
||||||
|
|
||||||
|
Called by monster program code.
|
||||||
|
The move will be adjusted for slopes and stairs, but if the move isn't
|
||||||
|
possible, no move is done, false is returned, and
|
||||||
|
pr_global_struct->trace_normal is set to the normal of the blocking wall
|
||||||
|
=============
|
||||||
|
*/
|
||||||
|
//FIXME since we need to test end position contents here, can we avoid doing
|
||||||
|
//it again later in catagorize position?
|
||||||
|
qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
|
||||||
|
{
|
||||||
|
float dz;
|
||||||
|
vec3_t oldorg, neworg, end;
|
||||||
|
trace_t trace;
|
||||||
|
int i;
|
||||||
|
float stepsize;
|
||||||
|
vec3_t test;
|
||||||
|
int contents;
|
||||||
|
|
||||||
|
// try the move
|
||||||
|
VectorCopy (ent->s.origin, oldorg);
|
||||||
|
VectorAdd (ent->s.origin, move, neworg);
|
||||||
|
|
||||||
|
// flying monsters don't step up
|
||||||
|
if ( ent->flags & (FL_SWIM | FL_FLY) )
|
||||||
|
{
|
||||||
|
// try one move with vertical motion, then one without
|
||||||
|
for (i=0 ; i<2 ; i++)
|
||||||
|
{
|
||||||
|
VectorAdd (ent->s.origin, move, neworg);
|
||||||
|
if (i == 0 && ent->enemy)
|
||||||
|
{
|
||||||
|
if (!ent->goalentity)
|
||||||
|
ent->goalentity = ent->enemy;
|
||||||
|
dz = ent->s.origin[2] - ent->goalentity->s.origin[2];
|
||||||
|
if (ent->goalentity->client)
|
||||||
|
{
|
||||||
|
if (dz > 40)
|
||||||
|
neworg[2] -= 8;
|
||||||
|
if (!((ent->flags & FL_SWIM) && (ent->waterlevel < 2)))
|
||||||
|
if (dz < 30)
|
||||||
|
neworg[2] += 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (dz > 8)
|
||||||
|
neworg[2] -= 8;
|
||||||
|
else if (dz > 0)
|
||||||
|
neworg[2] -= dz;
|
||||||
|
else if (dz < -8)
|
||||||
|
neworg[2] += 8;
|
||||||
|
else
|
||||||
|
neworg[2] += dz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, neworg, ent, MASK_MONSTERSOLID);
|
||||||
|
|
||||||
|
// fly monsters don't enter water voluntarily
|
||||||
|
if (ent->flags & FL_FLY)
|
||||||
|
{
|
||||||
|
if (!ent->waterlevel)
|
||||||
|
{
|
||||||
|
test[0] = trace.endpos[0];
|
||||||
|
test[1] = trace.endpos[1];
|
||||||
|
test[2] = trace.endpos[2] + ent->mins[2] + 1;
|
||||||
|
contents = gi.pointcontents(test);
|
||||||
|
if (contents & MASK_WATER)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// swim monsters don't exit water voluntarily
|
||||||
|
if (ent->flags & FL_SWIM)
|
||||||
|
{
|
||||||
|
if (ent->waterlevel < 2)
|
||||||
|
{
|
||||||
|
test[0] = trace.endpos[0];
|
||||||
|
test[1] = trace.endpos[1];
|
||||||
|
test[2] = trace.endpos[2] + ent->mins[2] + 1;
|
||||||
|
contents = gi.pointcontents(test);
|
||||||
|
if (!(contents & MASK_WATER))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trace.fraction == 1)
|
||||||
|
{
|
||||||
|
VectorCopy (trace.endpos, ent->s.origin);
|
||||||
|
if (relink)
|
||||||
|
{
|
||||||
|
gi.linkentity (ent);
|
||||||
|
G_TouchTriggers (ent);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ent->enemy)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// push down from a step height above the wished position
|
||||||
|
if (!(ent->monsterinfo.aiflags & AI_NOSTEP))
|
||||||
|
stepsize = STEPSIZE;
|
||||||
|
else
|
||||||
|
stepsize = 1;
|
||||||
|
|
||||||
|
neworg[2] += stepsize;
|
||||||
|
VectorCopy (neworg, end);
|
||||||
|
end[2] -= stepsize*2;
|
||||||
|
|
||||||
|
trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_PLAYERSOLID);
|
||||||
|
//WF24-34 USES MONSTERSOLID
|
||||||
|
if (trace.allsolid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (trace.startsolid)
|
||||||
|
{
|
||||||
|
neworg[2] -= stepsize;
|
||||||
|
trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, MASK_PLAYERSOLID);
|
||||||
|
if (trace.allsolid || trace.startsolid)
|
||||||
|
return false;
|
||||||
|
}//SAME HERE MASK
|
||||||
|
|
||||||
|
|
||||||
|
// don't go in to water
|
||||||
|
if (ent->waterlevel == 0)
|
||||||
|
{
|
||||||
|
test[0] = trace.endpos[0];
|
||||||
|
test[1] = trace.endpos[1];
|
||||||
|
test[2] = trace.endpos[2] + ent->mins[2] + 1;
|
||||||
|
contents = gi.pointcontents(test);
|
||||||
|
|
||||||
|
if (contents & MASK_WATER)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trace.fraction == 1)
|
||||||
|
{
|
||||||
|
// if monster had the ground pulled out, go ahead and fall
|
||||||
|
if ( ent->flags & FL_PARTIALGROUND )
|
||||||
|
{
|
||||||
|
VectorAdd (ent->s.origin, move, ent->s.origin);
|
||||||
|
if (relink)
|
||||||
|
{
|
||||||
|
gi.linkentity (ent);
|
||||||
|
G_TouchTriggers (ent);
|
||||||
|
}
|
||||||
|
ent->groundentity = NULL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false; // walked off an edge
|
||||||
|
}
|
||||||
|
|
||||||
|
// check point traces down for dangling corners
|
||||||
|
VectorCopy (trace.endpos, ent->s.origin);
|
||||||
|
|
||||||
|
if (!M_CheckBottom (ent))
|
||||||
|
{
|
||||||
|
if ( ent->flags & FL_PARTIALGROUND )
|
||||||
|
{ // entity had floor mostly pulled out from underneath it
|
||||||
|
// and is trying to correct
|
||||||
|
if (relink)
|
||||||
|
{
|
||||||
|
gi.linkentity (ent);
|
||||||
|
G_TouchTriggers (ent);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
VectorCopy (oldorg, ent->s.origin);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ent->flags & FL_PARTIALGROUND )
|
||||||
|
{
|
||||||
|
ent->flags &= ~FL_PARTIALGROUND;
|
||||||
|
}
|
||||||
|
ent->groundentity = trace.ent;
|
||||||
|
ent->groundentity_linkcount = trace.ent->linkcount;
|
||||||
|
|
||||||
|
// the move is ok
|
||||||
|
if (relink)
|
||||||
|
{
|
||||||
|
gi.linkentity (ent);
|
||||||
|
G_TouchTriggers (ent);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
/*
|
||||||
|
===============
|
||||||
|
M_ChangeYaw
|
||||||
|
|
||||||
|
===============
|
||||||
|
*/
|
||||||
|
void M_ChangeYaw (edict_t *ent)
|
||||||
|
{
|
||||||
|
float ideal;
|
||||||
|
float current;
|
||||||
|
float move;
|
||||||
|
float speed;
|
||||||
|
|
||||||
|
current = anglemod(ent->s.angles[YAW]);
|
||||||
|
ideal = ent->ideal_yaw;
|
||||||
|
|
||||||
|
if (current == ideal)
|
||||||
|
return;
|
||||||
|
|
||||||
|
move = ideal - current;
|
||||||
|
speed = ent->yaw_speed;
|
||||||
|
|
||||||
|
if (ent->enemy && (skill->value > 1))
|
||||||
|
speed *= skill->value;//24 OR ERASER FIXME??
|
||||||
|
|
||||||
|
if (ideal > current)
|
||||||
|
{
|
||||||
|
if (move >= 180)
|
||||||
|
move = move - 360;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (move <= -180)
|
||||||
|
move = move + 360;
|
||||||
|
}
|
||||||
|
if (move > 0)
|
||||||
|
{
|
||||||
|
if (move > speed)
|
||||||
|
move = speed;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (move < -speed)
|
||||||
|
move = -speed;
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->s.angles[YAW] = anglemod (current + move);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
======================
|
||||||
|
SV_StepDirection
|
||||||
|
|
||||||
|
Turns to the movement direction, and walks the current distance if
|
||||||
|
facing it.
|
||||||
|
|
||||||
|
======================
|
||||||
|
*/
|
||||||
|
qboolean SV_StepDirection (edict_t *ent, float yaw, float dist)
|
||||||
|
{
|
||||||
|
vec3_t move, oldorigin;
|
||||||
|
float delta;
|
||||||
|
|
||||||
|
ent->ideal_yaw = yaw;
|
||||||
|
M_ChangeYaw (ent);
|
||||||
|
|
||||||
|
yaw = yaw*M_PI*2 / 360;
|
||||||
|
move[0] = cos(yaw)*dist;
|
||||||
|
move[1] = sin(yaw)*dist;
|
||||||
|
move[2] = 0;
|
||||||
|
|
||||||
|
VectorCopy (ent->s.origin, oldorigin);
|
||||||
|
if (SV_movestep (ent, move, false))
|
||||||
|
{
|
||||||
|
delta = ent->s.angles[YAW] - ent->ideal_yaw;
|
||||||
|
if (delta > 45 && delta < 315)
|
||||||
|
{ // not turned far enough, so don't take the step
|
||||||
|
VectorCopy (oldorigin, ent->s.origin);
|
||||||
|
}
|
||||||
|
gi.linkentity (ent);
|
||||||
|
G_TouchTriggers (ent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
gi.linkentity (ent);
|
||||||
|
G_TouchTriggers (ent);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
======================
|
||||||
|
SV_FixCheckBottom
|
||||||
|
|
||||||
|
======================
|
||||||
|
*/
|
||||||
|
void SV_FixCheckBottom (edict_t *ent)
|
||||||
|
{
|
||||||
|
ent->flags |= FL_PARTIALGROUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
================
|
||||||
|
SV_NewChaseDir
|
||||||
|
|
||||||
|
================
|
||||||
|
*/
|
||||||
|
#define DI_NODIR -1
|
||||||
|
void SV_NewChaseDir (edict_t *actor, edict_t *enemy, float dist)
|
||||||
|
{
|
||||||
|
float deltax,deltay;
|
||||||
|
float d[3];
|
||||||
|
float tdir, olddir, turnaround;
|
||||||
|
|
||||||
|
//FIXME: how did we get here with no enemy
|
||||||
|
if (!enemy)
|
||||||
|
return;
|
||||||
|
|
||||||
|
olddir = anglemod( (int)(actor->ideal_yaw/45)*45 );
|
||||||
|
turnaround = anglemod(olddir - 180);
|
||||||
|
|
||||||
|
deltax = enemy->s.origin[0] - actor->s.origin[0];
|
||||||
|
deltay = enemy->s.origin[1] - actor->s.origin[1];
|
||||||
|
if (deltax>10)
|
||||||
|
d[1]= 0;
|
||||||
|
else if (deltax<-10)
|
||||||
|
d[1]= 180;
|
||||||
|
else
|
||||||
|
d[1]= DI_NODIR;
|
||||||
|
if (deltay<-10)
|
||||||
|
d[2]= 270;
|
||||||
|
else if (deltay>10)
|
||||||
|
d[2]= 90;
|
||||||
|
else
|
||||||
|
d[2]= DI_NODIR;
|
||||||
|
|
||||||
|
// try direct route
|
||||||
|
if (d[1] != DI_NODIR && d[2] != DI_NODIR)
|
||||||
|
{
|
||||||
|
if (d[1] == 0)
|
||||||
|
tdir = d[2] == 90 ? 45 : 315;
|
||||||
|
else
|
||||||
|
tdir = d[2] == 90 ? 135 : 215;
|
||||||
|
|
||||||
|
if (tdir != turnaround && SV_StepDirection(actor, tdir, dist))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try other directions
|
||||||
|
if ( ((rand()&3) & 1) || abs(deltay)>abs(deltax))
|
||||||
|
{
|
||||||
|
tdir=d[1];
|
||||||
|
d[1]=d[2];
|
||||||
|
d[2]=tdir;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (d[1]!=DI_NODIR && d[1]!=turnaround
|
||||||
|
&& SV_StepDirection(actor, d[1], dist))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (d[2]!=DI_NODIR && d[2]!=turnaround
|
||||||
|
&& SV_StepDirection(actor, d[2], dist))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* there is no direct path to the player, so pick another direction */
|
||||||
|
|
||||||
|
if (olddir!=DI_NODIR && SV_StepDirection(actor, olddir, dist))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (rand()&1) /*randomly determine direction of search*/
|
||||||
|
{
|
||||||
|
for (tdir=0 ; tdir<=315 ; tdir += 45)
|
||||||
|
if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (tdir=315 ; tdir >=0 ; tdir -= 45)
|
||||||
|
if (tdir!=turnaround && SV_StepDirection(actor, tdir, dist) )
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (turnaround != DI_NODIR && SV_StepDirection(actor, turnaround, dist) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
actor->ideal_yaw = olddir; // can't move
|
||||||
|
|
||||||
|
// if a bridge was pulled out from underneath a monster, it may not have
|
||||||
|
// a valid standing position at all
|
||||||
|
|
||||||
|
if (!M_CheckBottom (actor))
|
||||||
|
SV_FixCheckBottom (actor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
======================
|
||||||
|
SV_CloseEnough
|
||||||
|
|
||||||
|
======================
|
||||||
|
*/
|
||||||
|
qboolean SV_CloseEnough (edict_t *ent, edict_t *goal, float dist)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i=0 ; i<3 ; i++)
|
||||||
|
{
|
||||||
|
if (goal->absmin[i] > ent->absmax[i] + dist)
|
||||||
|
return false;
|
||||||
|
if (goal->absmax[i] < ent->absmin[i] - dist)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
======================
|
||||||
|
M_MoveToGoal
|
||||||
|
======================
|
||||||
|
*/
|
||||||
|
void M_MoveToGoal (edict_t *ent, float dist)
|
||||||
|
{
|
||||||
|
edict_t *goal;
|
||||||
|
|
||||||
|
goal = ent->goalentity;
|
||||||
|
|
||||||
|
if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// if the next step hits the enemy, return immediately
|
||||||
|
if (ent->enemy && SV_CloseEnough (ent, ent->enemy, dist) )
|
||||||
|
return;
|
||||||
|
|
||||||
|
// bump around...
|
||||||
|
if ( (rand()&3)==1 || !SV_StepDirection (ent, ent->ideal_yaw, dist))
|
||||||
|
{
|
||||||
|
if (ent->inuse)
|
||||||
|
SV_NewChaseDir (ent, goal, dist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
===============
|
||||||
|
M_walkmove
|
||||||
|
===============
|
||||||
|
*/
|
||||||
|
qboolean M_walkmove (edict_t *ent, float yaw, float dist)
|
||||||
|
{
|
||||||
|
vec3_t move;
|
||||||
|
|
||||||
|
if (!ent->groundentity && !(ent->flags & (FL_FLY|FL_SWIM)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
yaw = yaw*M_PI*2 / 360;
|
||||||
|
|
||||||
|
move[0] = cos(yaw)*dist;
|
||||||
|
move[1] = sin(yaw)*dist;
|
||||||
|
move[2] = 0;
|
||||||
|
|
||||||
|
return SV_movestep(ent, move, true);
|
||||||
|
}
|
|
@ -0,0 +1,206 @@
|
||||||
|
// G:\quake2\baseq2\models/player_x/frames
|
||||||
|
|
||||||
|
// This file generated by qdata - Do NOT Modify
|
||||||
|
|
||||||
|
#define FRAME_stand01 0
|
||||||
|
#define FRAME_stand02 1
|
||||||
|
#define FRAME_stand03 2
|
||||||
|
#define FRAME_stand04 3
|
||||||
|
#define FRAME_stand05 4
|
||||||
|
#define FRAME_stand06 5
|
||||||
|
#define FRAME_stand07 6
|
||||||
|
#define FRAME_stand08 7
|
||||||
|
#define FRAME_stand09 8
|
||||||
|
#define FRAME_stand10 9
|
||||||
|
#define FRAME_stand11 10
|
||||||
|
#define FRAME_stand12 11
|
||||||
|
#define FRAME_stand13 12
|
||||||
|
#define FRAME_stand14 13
|
||||||
|
#define FRAME_stand15 14
|
||||||
|
#define FRAME_stand16 15
|
||||||
|
#define FRAME_stand17 16
|
||||||
|
#define FRAME_stand18 17
|
||||||
|
#define FRAME_stand19 18
|
||||||
|
#define FRAME_stand20 19
|
||||||
|
#define FRAME_stand21 20
|
||||||
|
#define FRAME_stand22 21
|
||||||
|
#define FRAME_stand23 22
|
||||||
|
#define FRAME_stand24 23
|
||||||
|
#define FRAME_stand25 24
|
||||||
|
#define FRAME_stand26 25
|
||||||
|
#define FRAME_stand27 26
|
||||||
|
#define FRAME_stand28 27
|
||||||
|
#define FRAME_stand29 28
|
||||||
|
#define FRAME_stand30 29
|
||||||
|
#define FRAME_stand31 30
|
||||||
|
#define FRAME_stand32 31
|
||||||
|
#define FRAME_stand33 32
|
||||||
|
#define FRAME_stand34 33
|
||||||
|
#define FRAME_stand35 34
|
||||||
|
#define FRAME_stand36 35
|
||||||
|
#define FRAME_stand37 36
|
||||||
|
#define FRAME_stand38 37
|
||||||
|
#define FRAME_stand39 38
|
||||||
|
#define FRAME_stand40 39
|
||||||
|
#define FRAME_run1 40
|
||||||
|
#define FRAME_run2 41
|
||||||
|
#define FRAME_run3 42
|
||||||
|
#define FRAME_run4 43
|
||||||
|
#define FRAME_run5 44
|
||||||
|
#define FRAME_run6 45
|
||||||
|
#define FRAME_attack1 46
|
||||||
|
#define FRAME_attack2 47
|
||||||
|
#define FRAME_attack3 48
|
||||||
|
#define FRAME_attack4 49
|
||||||
|
#define FRAME_attack5 50
|
||||||
|
#define FRAME_attack6 51
|
||||||
|
#define FRAME_attack7 52
|
||||||
|
#define FRAME_attack8 53
|
||||||
|
#define FRAME_pain101 54
|
||||||
|
#define FRAME_pain102 55
|
||||||
|
#define FRAME_pain103 56
|
||||||
|
#define FRAME_pain104 57
|
||||||
|
#define FRAME_pain201 58
|
||||||
|
#define FRAME_pain202 59
|
||||||
|
#define FRAME_pain203 60
|
||||||
|
#define FRAME_pain204 61
|
||||||
|
#define FRAME_pain301 62
|
||||||
|
#define FRAME_pain302 63
|
||||||
|
#define FRAME_pain303 64
|
||||||
|
#define FRAME_pain304 65
|
||||||
|
#define FRAME_jump1 66
|
||||||
|
#define FRAME_jump2 67
|
||||||
|
#define FRAME_jump3 68
|
||||||
|
#define FRAME_jump4 69
|
||||||
|
#define FRAME_jump5 70
|
||||||
|
#define FRAME_jump6 71
|
||||||
|
#define FRAME_flip01 72
|
||||||
|
#define FRAME_flip02 73
|
||||||
|
#define FRAME_flip03 74
|
||||||
|
#define FRAME_flip04 75
|
||||||
|
#define FRAME_flip05 76
|
||||||
|
#define FRAME_flip06 77
|
||||||
|
#define FRAME_flip07 78
|
||||||
|
#define FRAME_flip08 79
|
||||||
|
#define FRAME_flip09 80
|
||||||
|
#define FRAME_flip10 81
|
||||||
|
#define FRAME_flip11 82
|
||||||
|
#define FRAME_flip12 83
|
||||||
|
#define FRAME_salute01 84
|
||||||
|
#define FRAME_salute02 85
|
||||||
|
#define FRAME_salute03 86
|
||||||
|
#define FRAME_salute04 87
|
||||||
|
#define FRAME_salute05 88
|
||||||
|
#define FRAME_salute06 89
|
||||||
|
#define FRAME_salute07 90
|
||||||
|
#define FRAME_salute08 91
|
||||||
|
#define FRAME_salute09 92
|
||||||
|
#define FRAME_salute10 93
|
||||||
|
#define FRAME_salute11 94
|
||||||
|
#define FRAME_taunt01 95
|
||||||
|
#define FRAME_taunt02 96
|
||||||
|
#define FRAME_taunt03 97
|
||||||
|
#define FRAME_taunt04 98
|
||||||
|
#define FRAME_taunt05 99
|
||||||
|
#define FRAME_taunt06 100
|
||||||
|
#define FRAME_taunt07 101
|
||||||
|
#define FRAME_taunt08 102
|
||||||
|
#define FRAME_taunt09 103
|
||||||
|
#define FRAME_taunt10 104
|
||||||
|
#define FRAME_taunt11 105
|
||||||
|
#define FRAME_taunt12 106
|
||||||
|
#define FRAME_taunt13 107
|
||||||
|
#define FRAME_taunt14 108
|
||||||
|
#define FRAME_taunt15 109
|
||||||
|
#define FRAME_taunt16 110
|
||||||
|
#define FRAME_taunt17 111
|
||||||
|
#define FRAME_wave01 112
|
||||||
|
#define FRAME_wave02 113
|
||||||
|
#define FRAME_wave03 114
|
||||||
|
#define FRAME_wave04 115
|
||||||
|
#define FRAME_wave05 116
|
||||||
|
#define FRAME_wave06 117
|
||||||
|
#define FRAME_wave07 118
|
||||||
|
#define FRAME_wave08 119
|
||||||
|
#define FRAME_wave09 120
|
||||||
|
#define FRAME_wave10 121
|
||||||
|
#define FRAME_wave11 122
|
||||||
|
#define FRAME_point01 123
|
||||||
|
#define FRAME_point02 124
|
||||||
|
#define FRAME_point03 125
|
||||||
|
#define FRAME_point04 126
|
||||||
|
#define FRAME_point05 127
|
||||||
|
#define FRAME_point06 128
|
||||||
|
#define FRAME_point07 129
|
||||||
|
#define FRAME_point08 130
|
||||||
|
#define FRAME_point09 131
|
||||||
|
#define FRAME_point10 132
|
||||||
|
#define FRAME_point11 133
|
||||||
|
#define FRAME_point12 134
|
||||||
|
#define FRAME_crstnd01 135
|
||||||
|
#define FRAME_crstnd02 136
|
||||||
|
#define FRAME_crstnd03 137
|
||||||
|
#define FRAME_crstnd04 138
|
||||||
|
#define FRAME_crstnd05 139
|
||||||
|
#define FRAME_crstnd06 140
|
||||||
|
#define FRAME_crstnd07 141
|
||||||
|
#define FRAME_crstnd08 142
|
||||||
|
#define FRAME_crstnd09 143
|
||||||
|
#define FRAME_crstnd10 144
|
||||||
|
#define FRAME_crstnd11 145
|
||||||
|
#define FRAME_crstnd12 146
|
||||||
|
#define FRAME_crstnd13 147
|
||||||
|
#define FRAME_crstnd14 148
|
||||||
|
#define FRAME_crstnd15 149
|
||||||
|
#define FRAME_crstnd16 150
|
||||||
|
#define FRAME_crstnd17 151
|
||||||
|
#define FRAME_crstnd18 152
|
||||||
|
#define FRAME_crstnd19 153
|
||||||
|
#define FRAME_crwalk1 154
|
||||||
|
#define FRAME_crwalk2 155
|
||||||
|
#define FRAME_crwalk3 156
|
||||||
|
#define FRAME_crwalk4 157
|
||||||
|
#define FRAME_crwalk5 158
|
||||||
|
#define FRAME_crwalk6 159
|
||||||
|
#define FRAME_crattak1 160
|
||||||
|
#define FRAME_crattak2 161
|
||||||
|
#define FRAME_crattak3 162
|
||||||
|
#define FRAME_crattak4 163
|
||||||
|
#define FRAME_crattak5 164
|
||||||
|
#define FRAME_crattak6 165
|
||||||
|
#define FRAME_crattak7 166
|
||||||
|
#define FRAME_crattak8 167
|
||||||
|
#define FRAME_crattak9 168
|
||||||
|
#define FRAME_crpain1 169
|
||||||
|
#define FRAME_crpain2 170
|
||||||
|
#define FRAME_crpain3 171
|
||||||
|
#define FRAME_crpain4 172
|
||||||
|
#define FRAME_crdeath1 173
|
||||||
|
#define FRAME_crdeath2 174
|
||||||
|
#define FRAME_crdeath3 175
|
||||||
|
#define FRAME_crdeath4 176
|
||||||
|
#define FRAME_crdeath5 177
|
||||||
|
#define FRAME_death101 178
|
||||||
|
#define FRAME_death102 179
|
||||||
|
#define FRAME_death103 180
|
||||||
|
#define FRAME_death104 181
|
||||||
|
#define FRAME_death105 182
|
||||||
|
#define FRAME_death106 183
|
||||||
|
#define FRAME_death201 184
|
||||||
|
#define FRAME_death202 185
|
||||||
|
#define FRAME_death203 186
|
||||||
|
#define FRAME_death204 187
|
||||||
|
#define FRAME_death205 188
|
||||||
|
#define FRAME_death206 189
|
||||||
|
#define FRAME_death301 190
|
||||||
|
#define FRAME_death302 191
|
||||||
|
#define FRAME_death303 192
|
||||||
|
#define FRAME_death304 193
|
||||||
|
#define FRAME_death305 194
|
||||||
|
#define FRAME_death306 195
|
||||||
|
#define FRAME_death307 196
|
||||||
|
#define FRAME_death308 197
|
||||||
|
|
||||||
|
#define MODEL_SCALE 1.000000
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,769 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
======================================================================
|
||||||
|
|
||||||
|
INTERMISSION
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
void MoveClientToIntermission (edict_t *ent)
|
||||||
|
{
|
||||||
|
//ERASER START
|
||||||
|
gclient_t *client;
|
||||||
|
|
||||||
|
client = ent->client;
|
||||||
|
//ERASER END
|
||||||
|
if (deathmatch->value || coop->value)
|
||||||
|
ent->client->showscores = true;
|
||||||
|
VectorCopy (level.intermission_origin, ent->s.origin);
|
||||||
|
ent->client->ps.pmove.origin[0] = level.intermission_origin[0]*8;
|
||||||
|
ent->client->ps.pmove.origin[1] = level.intermission_origin[1]*8;
|
||||||
|
ent->client->ps.pmove.origin[2] = level.intermission_origin[2]*8;
|
||||||
|
VectorCopy (level.intermission_angle, ent->client->ps.viewangles);
|
||||||
|
ent->client->ps.pmove.pm_type = PM_FREEZE;
|
||||||
|
ent->client->ps.gunindex = 0;
|
||||||
|
ent->client->ps.blend[3] = 0;
|
||||||
|
ent->client->ps.rdflags &= ~RDF_UNDERWATER;
|
||||||
|
|
||||||
|
// clean up powerup info
|
||||||
|
ent->client->quad_framenum = 0;
|
||||||
|
ent->client->invincible_framenum = 0;
|
||||||
|
ent->client->breather_framenum = 0;
|
||||||
|
ent->client->enviro_framenum = 0;
|
||||||
|
ent->client->grenade_blew_up = false;
|
||||||
|
ent->client->grenade_time = 0;
|
||||||
|
|
||||||
|
ent->viewheight = 0;
|
||||||
|
ent->s.modelindex = 0;
|
||||||
|
ent->s.modelindex2 = 0;
|
||||||
|
ent->s.modelindex3 = 0;
|
||||||
|
ent->s.modelindex = 0;
|
||||||
|
ent->s.effects = 0;
|
||||||
|
ent->s.sound = 0;
|
||||||
|
ent->solid = SOLID_NOT;
|
||||||
|
|
||||||
|
// add the layout
|
||||||
|
|
||||||
|
if (!ent->bot_client && (deathmatch->value || coop->value))//ERASER ADDED (!ent->bot_client &&
|
||||||
|
{
|
||||||
|
DeathmatchScoreboardMessage (ent, NULL);
|
||||||
|
gi.unicast (ent, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void BeginIntermission (edict_t *targ)
|
||||||
|
{
|
||||||
|
int i, n;
|
||||||
|
edict_t *ent, *client;
|
||||||
|
|
||||||
|
if (level.intermissiontime)
|
||||||
|
return; // allready activated
|
||||||
|
|
||||||
|
//ZOID
|
||||||
|
if (deathmatch->value && ctf->value)
|
||||||
|
CTFCalcScores();
|
||||||
|
//ZOID
|
||||||
|
|
||||||
|
game.autosaved = false;
|
||||||
|
|
||||||
|
// respawn any dead clients
|
||||||
|
for (i=0 ; i<maxclients->value ; i++)
|
||||||
|
{
|
||||||
|
client = g_edicts + 1 + i;
|
||||||
|
if (!client->inuse)
|
||||||
|
continue;
|
||||||
|
if (client->health <= 0)
|
||||||
|
respawn(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
level.intermissiontime = level.time;
|
||||||
|
level.changemap = targ->map;
|
||||||
|
|
||||||
|
if (strstr(level.changemap, "*"))
|
||||||
|
{
|
||||||
|
if (coop->value)
|
||||||
|
{
|
||||||
|
for (i=0 ; i<maxclients->value ; i++)
|
||||||
|
{
|
||||||
|
client = g_edicts + 1 + i;
|
||||||
|
if (!client->inuse)
|
||||||
|
continue;
|
||||||
|
// strip players of all keys between units
|
||||||
|
for (n = 0; n < MAX_ITEMS; n++)
|
||||||
|
{
|
||||||
|
if (itemlist[n].flags & IT_KEY)
|
||||||
|
client->client->pers.inventory[n] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!deathmatch->value)
|
||||||
|
{
|
||||||
|
level.exitintermission = 1; // go immediately to the next level
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
level.exitintermission = 0;
|
||||||
|
|
||||||
|
// find an intermission spot
|
||||||
|
ent = G_Find (NULL, FOFS(classname), "info_player_intermission");
|
||||||
|
if (!ent)
|
||||||
|
{ // the map creator forgot to put in an intermission point...
|
||||||
|
ent = G_Find (NULL, FOFS(classname), "info_player_start");
|
||||||
|
if (!ent)
|
||||||
|
ent = G_Find (NULL, FOFS(classname), "info_player_deathmatch");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ // chose one of four spots
|
||||||
|
i = rand() & 3;
|
||||||
|
while (i--)
|
||||||
|
{
|
||||||
|
ent = G_Find (ent, FOFS(classname), "info_player_intermission");
|
||||||
|
if (!ent) // wrap around the list
|
||||||
|
ent = G_Find (ent, FOFS(classname), "info_player_intermission");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorCopy (ent->s.origin, level.intermission_origin);
|
||||||
|
VectorCopy (ent->s.angles, level.intermission_angle);
|
||||||
|
|
||||||
|
// move all clients to the intermission point
|
||||||
|
for (i=0 ; i<maxclients->value ; i++)
|
||||||
|
{
|
||||||
|
client = g_edicts + 1 + i;
|
||||||
|
if (!client->inuse)
|
||||||
|
continue;
|
||||||
|
MoveClientToIntermission (client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//ERASER START FOR CUSTOM TEAMS PLAY
|
||||||
|
void TeamplayScoreboardMessage (edict_t *ent, edict_t *killer)
|
||||||
|
{
|
||||||
|
char entry[1024];
|
||||||
|
char string[1400];
|
||||||
|
int stringlength;
|
||||||
|
int i, j, k, t;
|
||||||
|
int sorted[MAX_CLIENTS];
|
||||||
|
int sortedscores[MAX_CLIENTS];
|
||||||
|
|
||||||
|
bot_team_t *sortedteams[MAX_TEAMS];
|
||||||
|
int doneteam[MAX_TEAMS];
|
||||||
|
int numteams, best, bestscore;
|
||||||
|
|
||||||
|
int score, total;
|
||||||
|
int x, y;
|
||||||
|
gclient_t *cl;
|
||||||
|
edict_t *cl_ent;
|
||||||
|
|
||||||
|
// clear the doneteam flags
|
||||||
|
memset(doneteam, 0, sizeof(int) * MAX_TEAMS);
|
||||||
|
|
||||||
|
numteams = 0; // incremented each time we add a team to the list
|
||||||
|
|
||||||
|
// sort the teams
|
||||||
|
for (i=0; i<MAX_TEAMS; i++)
|
||||||
|
{
|
||||||
|
if (!bot_teams[i])
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!bot_teams[i]->ingame)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bestscore = -999999;
|
||||||
|
best = -1;
|
||||||
|
|
||||||
|
// find the highest scoring team
|
||||||
|
for (j=0; j<MAX_TEAMS; j++)
|
||||||
|
{
|
||||||
|
if (!bot_teams[j])
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (doneteam[j])
|
||||||
|
continue;
|
||||||
|
if (!bot_teams[j]->ingame)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (bot_teams[j]->score > bestscore)
|
||||||
|
{
|
||||||
|
best = j;
|
||||||
|
bestscore = bot_teams[j]->score;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best > -1)
|
||||||
|
{
|
||||||
|
doneteam[best] = true;
|
||||||
|
sortedteams[numteams] = bot_teams[best];
|
||||||
|
numteams++;
|
||||||
|
}
|
||||||
|
else // must be done
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string[0] = 0;
|
||||||
|
stringlength = strlen(string);
|
||||||
|
|
||||||
|
for (t=0; t<numteams; t++)
|
||||||
|
{ // print each team/player entry, maximum of 4 players per team
|
||||||
|
if (t > 3) // only print the top 4 teams
|
||||||
|
break;
|
||||||
|
|
||||||
|
// sort the clients by score
|
||||||
|
total = 0;
|
||||||
|
for (i=0 ; i<game.maxclients ; i++)
|
||||||
|
{
|
||||||
|
cl_ent = g_edicts + 1 + i;
|
||||||
|
if (!cl_ent->inuse)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (cl_ent->client->team != sortedteams[t])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
score = game.clients[i].resp.score;
|
||||||
|
for (j=0 ; j<total ; j++)
|
||||||
|
{
|
||||||
|
if (score > sortedscores[j])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (k=total ; k>j ; k--)
|
||||||
|
{
|
||||||
|
sorted[k] = sorted[k-1];
|
||||||
|
sortedscores[k] = sortedscores[k-1];
|
||||||
|
}
|
||||||
|
sorted[j] = i;
|
||||||
|
sortedscores[j] = score;
|
||||||
|
total++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total > 4)
|
||||||
|
total = 4;
|
||||||
|
|
||||||
|
x = 160;
|
||||||
|
y = (t * 48);
|
||||||
|
|
||||||
|
if (ent->client->team == sortedteams[t])
|
||||||
|
{
|
||||||
|
Com_sprintf (entry, sizeof(entry),
|
||||||
|
"xv %i yv %i picn tag1 ", 32, y);
|
||||||
|
j = strlen(entry);
|
||||||
|
if (stringlength + j > 1024)
|
||||||
|
break;
|
||||||
|
strcpy (string + stringlength, entry);
|
||||||
|
stringlength += j;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send team info
|
||||||
|
Com_sprintf (entry, sizeof(entry),
|
||||||
|
"xv %i yv %i string \"%s\" ", 70, y+6, sortedteams[t]->teamname);
|
||||||
|
j = strlen(entry);
|
||||||
|
if (stringlength + j > 1024)
|
||||||
|
break;
|
||||||
|
strcpy (string + stringlength, entry);
|
||||||
|
stringlength += j;
|
||||||
|
|
||||||
|
// Send team score info
|
||||||
|
Com_sprintf (entry, sizeof(entry),
|
||||||
|
"xv %i yv %i string \"%i\" ", 80, y + 20, sortedteams[t]->score);
|
||||||
|
j = strlen(entry);
|
||||||
|
if (stringlength + j > 1024)
|
||||||
|
break;
|
||||||
|
strcpy (string + stringlength, entry);
|
||||||
|
stringlength += j;
|
||||||
|
|
||||||
|
for (i=0 ; i<total ; i++)
|
||||||
|
{
|
||||||
|
cl = &game.clients[sorted[i]];
|
||||||
|
cl_ent = g_edicts + 1 + sorted[i];
|
||||||
|
|
||||||
|
y = (t * 48) + (i * 10);
|
||||||
|
|
||||||
|
if (ent == cl_ent)
|
||||||
|
{
|
||||||
|
Com_sprintf (entry, sizeof(entry),
|
||||||
|
"xv %i yv %i string \"%3i %s\" ",
|
||||||
|
x, y, cl->resp.score, cl->pers.netname);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Com_sprintf (entry, sizeof(entry),
|
||||||
|
"xv %i yv %i string2 \"%3i %s\" ",
|
||||||
|
x, y, cl->resp.score, cl->pers.netname);
|
||||||
|
}
|
||||||
|
|
||||||
|
j = strlen(entry);
|
||||||
|
if (stringlength + j > 1024)
|
||||||
|
break;
|
||||||
|
|
||||||
|
strcpy (string + stringlength, entry);
|
||||||
|
stringlength += j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gi.WriteByte (svc_layout);
|
||||||
|
gi.WriteString (string);
|
||||||
|
}
|
||||||
|
//ERASER END
|
||||||
|
|
||||||
|
/*
|
||||||
|
==================
|
||||||
|
DeathmatchScoreboardMessage
|
||||||
|
|
||||||
|
==================
|
||||||
|
*/
|
||||||
|
void DeathmatchScoreboardMessage (edict_t *ent, edict_t *killer)
|
||||||
|
{
|
||||||
|
char entry[1024];
|
||||||
|
char string[1400];
|
||||||
|
int stringlength;
|
||||||
|
int i, j, k;
|
||||||
|
int sorted[MAX_CLIENTS];
|
||||||
|
int sortedscores[MAX_CLIENTS];
|
||||||
|
int score, total;
|
||||||
|
int picnum;
|
||||||
|
int x, y;
|
||||||
|
gclient_t *cl;
|
||||||
|
edict_t *cl_ent;
|
||||||
|
char *tag;
|
||||||
|
|
||||||
|
//WF - Scanner
|
||||||
|
// if (ent->client->showscores || ent->client->showinventory)
|
||||||
|
// if (ent->client->pers.scanner_active)
|
||||||
|
// ent->client->pers.scanner_active=2;
|
||||||
|
//WF
|
||||||
|
|
||||||
|
//WF - added following 2 lines
|
||||||
|
if (ent->client->showscores)
|
||||||
|
{
|
||||||
|
|
||||||
|
//ZOID
|
||||||
|
if (ctf->value)
|
||||||
|
{
|
||||||
|
CTFScoreboardMessage (ent, killer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//ZOID
|
||||||
|
|
||||||
|
//ERASER START
|
||||||
|
if (teamplay->value && ent->client->team)
|
||||||
|
{
|
||||||
|
TeamplayScoreboardMessage(ent, killer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//ERASER END
|
||||||
|
|
||||||
|
|
||||||
|
// sort the clients by score
|
||||||
|
total = 0;
|
||||||
|
for (i=0 ; i<game.maxclients ; i++)
|
||||||
|
{
|
||||||
|
cl_ent = g_edicts + 1 + i;
|
||||||
|
if (!cl_ent->inuse)
|
||||||
|
continue;
|
||||||
|
score = game.clients[i].resp.score;
|
||||||
|
for (j=0 ; j<total ; j++)
|
||||||
|
{
|
||||||
|
if (score > sortedscores[j])
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
for (k=total ; k>j ; k--)
|
||||||
|
{
|
||||||
|
sorted[k] = sorted[k-1];
|
||||||
|
sortedscores[k] = sortedscores[k-1];
|
||||||
|
}
|
||||||
|
sorted[j] = i;
|
||||||
|
sortedscores[j] = score;
|
||||||
|
total++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// print level name and exit rules
|
||||||
|
string[0] = 0;
|
||||||
|
|
||||||
|
stringlength = strlen(string);
|
||||||
|
|
||||||
|
// add the clients in sorted order
|
||||||
|
if (total > 12)
|
||||||
|
total = 12;
|
||||||
|
|
||||||
|
for (i=0 ; i<total ; i++)
|
||||||
|
{
|
||||||
|
cl = &game.clients[sorted[i]];
|
||||||
|
cl_ent = g_edicts + 1 + sorted[i];
|
||||||
|
|
||||||
|
picnum = gi.imageindex ("i_fixme");
|
||||||
|
x = (i>=6) ? 160 : 0;
|
||||||
|
y = 32 + 32 * (i%6);
|
||||||
|
|
||||||
|
// add a dogtag
|
||||||
|
if (cl_ent == ent)
|
||||||
|
tag = "tag1";
|
||||||
|
else if (cl_ent == killer)
|
||||||
|
tag = "tag2";
|
||||||
|
else
|
||||||
|
tag = NULL;
|
||||||
|
if (tag)
|
||||||
|
{
|
||||||
|
Com_sprintf (entry, sizeof(entry),
|
||||||
|
"xv %i yv %i picn %s ",x+32, y, tag);
|
||||||
|
j = strlen(entry);
|
||||||
|
if (stringlength + j > 1024)
|
||||||
|
break;
|
||||||
|
strcpy (string + stringlength, entry);
|
||||||
|
stringlength += j;
|
||||||
|
}
|
||||||
|
|
||||||
|
//ERASER START
|
||||||
|
if (cl_ent->bot_client)
|
||||||
|
{
|
||||||
|
cl->ping = (int) cl_ent->bot_stats->avg_ping + ((random() * 2) - 1) * 80;
|
||||||
|
if (cl->ping < 0)
|
||||||
|
cl->ping = 0;
|
||||||
|
}
|
||||||
|
//ERASER END
|
||||||
|
|
||||||
|
// send the layout
|
||||||
|
Com_sprintf (entry, sizeof(entry),
|
||||||
|
"client %i %i %i %i %i %i ",
|
||||||
|
x, y, sorted[i], cl->resp.score, cl->ping, (level.framenum - cl->resp.enterframe)/600);
|
||||||
|
j = strlen(entry);
|
||||||
|
if (stringlength + j > 1024)
|
||||||
|
break;
|
||||||
|
strcpy (string + stringlength, entry);
|
||||||
|
stringlength += j;
|
||||||
|
}
|
||||||
|
}//WF - added rest of stuff here including this line
|
||||||
|
else
|
||||||
|
*string=0;
|
||||||
|
|
||||||
|
//WF Scanner active ?
|
||||||
|
//if (ent->client->pers.scanner_active)
|
||||||
|
// ShowScanner(ent,string);
|
||||||
|
|
||||||
|
//WF END
|
||||||
|
|
||||||
|
gi.WriteByte (svc_layout);
|
||||||
|
gi.WriteString (string);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
==================
|
||||||
|
DeathmatchScoreboard
|
||||||
|
|
||||||
|
Draw instead of help message.
|
||||||
|
Note that it isn't that hard to overflow the 1400 byte message limit!
|
||||||
|
==================
|
||||||
|
*/
|
||||||
|
void DeathmatchScoreboard (edict_t *ent)
|
||||||
|
{
|
||||||
|
DeathmatchScoreboardMessage (ent, ent->enemy);
|
||||||
|
gi.unicast (ent, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
==================
|
||||||
|
Cmd_Score_f
|
||||||
|
|
||||||
|
Display the scoreboard
|
||||||
|
==================
|
||||||
|
*/
|
||||||
|
void Cmd_Score_f (edict_t *ent)
|
||||||
|
{
|
||||||
|
ent->client->showinventory = false;
|
||||||
|
ent->client->showhelp = false;
|
||||||
|
//ZOID
|
||||||
|
if (ent->client->menu)
|
||||||
|
PMenu_Close(ent);
|
||||||
|
//ZOID
|
||||||
|
|
||||||
|
if (!deathmatch->value && !coop->value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ent->client->showscores)
|
||||||
|
{
|
||||||
|
ent->client->showscores = false;
|
||||||
|
ent->client->update_chase = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->client->showscores = true;
|
||||||
|
|
||||||
|
DeathmatchScoreboard (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
==================
|
||||||
|
HelpComputer
|
||||||
|
|
||||||
|
Draw help computer.
|
||||||
|
==================
|
||||||
|
*/
|
||||||
|
void HelpComputer (edict_t *ent)
|
||||||
|
{
|
||||||
|
char string[1024];
|
||||||
|
/*
|
||||||
|
char *sk;
|
||||||
|
|
||||||
|
if (skill->value == 0)
|
||||||
|
sk = "easy";
|
||||||
|
else if (skill->value == 1)
|
||||||
|
sk = "medium";
|
||||||
|
else if (skill->value == 2)
|
||||||
|
sk = "hard";
|
||||||
|
else
|
||||||
|
sk = "hard+";
|
||||||
|
*/
|
||||||
|
// send the layout
|
||||||
|
Com_sprintf (string, sizeof(string),
|
||||||
|
"xv 32 yv 8 picn help " // background
|
||||||
|
"xv 0 yv 24 cstring2 \"%s\" " // level name
|
||||||
|
"xv 0 yv 54 cstring2 \"%s\" " // help 1
|
||||||
|
"xv 0 yv 110 cstring2 \"%s\" ", // help 2
|
||||||
|
level.level_name,
|
||||||
|
game.helpmessage1,
|
||||||
|
game.helpmessage2);
|
||||||
|
|
||||||
|
gi.WriteByte (svc_layout);
|
||||||
|
gi.WriteString (string);
|
||||||
|
// gi.unicast (ent, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
==================
|
||||||
|
Cmd_Help_f
|
||||||
|
|
||||||
|
Display the current help message
|
||||||
|
==================
|
||||||
|
*/
|
||||||
|
void Cmd_Help_f (edict_t *ent)
|
||||||
|
{
|
||||||
|
// this is for backwards compatability
|
||||||
|
if (deathmatch->value)
|
||||||
|
{
|
||||||
|
Cmd_Score_f (ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->client->showinventory = false;
|
||||||
|
ent->client->showscores = false;
|
||||||
|
|
||||||
|
if (ent->client->showhelp && (ent->client->resp.game_helpchanged == game.helpchanged))
|
||||||
|
{
|
||||||
|
ent->client->showhelp = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->client->showhelp = true;
|
||||||
|
ent->client->resp.helpchanged = 0;
|
||||||
|
HelpComputer (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TeT Using a menu to display map help - see g_ctf.c
|
||||||
|
//void Cmd_MapHelp_f (edict_t *ent)
|
||||||
|
//{
|
||||||
|
// ent->client->showinventory = false;
|
||||||
|
// ent->client->showscores = false;
|
||||||
|
|
||||||
|
// if (ent->client->showhelp && (ent->client->resp.game_helpchanged == game.helpchanged))
|
||||||
|
// {
|
||||||
|
// ent->client->showhelp = false;
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ent->client->showhelp = true;
|
||||||
|
// ent->client->resp.helpchanged = 0;
|
||||||
|
// HelpComputer (ent);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//=======================================================================
|
||||||
|
|
||||||
|
/*
|
||||||
|
===============
|
||||||
|
G_SetStats
|
||||||
|
===============
|
||||||
|
*/
|
||||||
|
void G_SetStats (edict_t *ent)
|
||||||
|
{
|
||||||
|
gitem_t *item;
|
||||||
|
int index;
|
||||||
|
int power_armor_type;
|
||||||
|
int cells = 0;
|
||||||
|
|
||||||
|
//
|
||||||
|
// health
|
||||||
|
//
|
||||||
|
ent->client->ps.stats[STAT_HEALTH_ICON] = level.pic_health;
|
||||||
|
ent->client->ps.stats[STAT_HEALTH] = ent->health;
|
||||||
|
|
||||||
|
//
|
||||||
|
// weapon damage
|
||||||
|
//
|
||||||
|
ent->client->ps.stats[STAT_DAMAGE_ICON] = level.pic_damage;
|
||||||
|
ent->client->ps.stats[STAT_DAMAGE] = ent->client->weapon_damage;
|
||||||
|
|
||||||
|
//
|
||||||
|
// ref called timeout?
|
||||||
|
//
|
||||||
|
if (wf_game.game_halted)
|
||||||
|
ent->client->ps.stats[STAT_TIMEOUT_ICON] = level.pic_timeout;
|
||||||
|
else
|
||||||
|
ent->client->ps.stats[STAT_TIMEOUT_ICON] = 0;
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// ammo
|
||||||
|
//
|
||||||
|
if (!ent->client->ammo_index /* ||!ent->client->pers.inventory[ent->client->ammo_index] */)
|
||||||
|
{
|
||||||
|
ent->client->ps.stats[STAT_AMMO_ICON] = 0;
|
||||||
|
ent->client->ps.stats[STAT_AMMO] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item = &itemlist[ent->client->ammo_index];
|
||||||
|
|
||||||
|
ent->client->ps.stats[STAT_AMMO_ICON] = gi.imageindex(item->icon);
|
||||||
|
ent->client->ps.stats[STAT_AMMO] =
|
||||||
|
ent->client->pers.inventory[ent->client->ammo_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// armor
|
||||||
|
//
|
||||||
|
power_armor_type = PowerArmorType (ent);
|
||||||
|
if (power_armor_type)
|
||||||
|
{
|
||||||
|
cells = ent->client->pers.inventory[ITEM_INDEX(FindItem ("cells"))];
|
||||||
|
if (cells == 0)
|
||||||
|
{ // ran out of cells for power armor
|
||||||
|
ent->flags &= ~FL_POWER_ARMOR;
|
||||||
|
gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/power2.wav"), 1, ATTN_NORM, 0);
|
||||||
|
power_armor_type = 0;;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
index = ArmorIndex (ent);
|
||||||
|
if (power_armor_type && (!index || (level.framenum & 8) ) )
|
||||||
|
{ // flash between power armor and other armor icon
|
||||||
|
ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex ("i_powershield");
|
||||||
|
ent->client->ps.stats[STAT_ARMOR] = cells;
|
||||||
|
}
|
||||||
|
else if (index)
|
||||||
|
{
|
||||||
|
item = GetItemByIndex (index);
|
||||||
|
ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex (item->icon);
|
||||||
|
ent->client->ps.stats[STAT_ARMOR] = ent->client->pers.inventory[index];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ent->client->ps.stats[STAT_ARMOR_ICON] = 0;
|
||||||
|
ent->client->ps.stats[STAT_ARMOR] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// pickup message
|
||||||
|
//
|
||||||
|
if (level.time > ent->client->pickup_msg_time)
|
||||||
|
{
|
||||||
|
ent->client->ps.stats[STAT_PICKUP_ICON] = 0;
|
||||||
|
ent->client->ps.stats[STAT_PICKUP_STRING] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// timers
|
||||||
|
//
|
||||||
|
if (ent->client->quad_framenum > level.framenum)
|
||||||
|
{
|
||||||
|
ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_quad");
|
||||||
|
ent->client->ps.stats[STAT_TIMER] = (ent->client->quad_framenum - level.framenum)/10;
|
||||||
|
}
|
||||||
|
else if (ent->client->invincible_framenum > level.framenum)
|
||||||
|
{
|
||||||
|
ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_invulnerability");
|
||||||
|
ent->client->ps.stats[STAT_TIMER] = (ent->client->invincible_framenum - level.framenum)/10;
|
||||||
|
}
|
||||||
|
else if (ent->client->enviro_framenum > level.framenum)
|
||||||
|
{
|
||||||
|
ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_envirosuit");
|
||||||
|
ent->client->ps.stats[STAT_TIMER] = (ent->client->enviro_framenum - level.framenum)/10;
|
||||||
|
}
|
||||||
|
else if (ent->client->breather_framenum > level.framenum)
|
||||||
|
{
|
||||||
|
ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_rebreather");
|
||||||
|
ent->client->ps.stats[STAT_TIMER] = (ent->client->breather_framenum - level.framenum)/10;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ent->client->ps.stats[STAT_TIMER_ICON] = 0;
|
||||||
|
ent->client->ps.stats[STAT_TIMER] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// selected item
|
||||||
|
//
|
||||||
|
if (ent->client->pers.selected_item == -1)
|
||||||
|
ent->client->ps.stats[STAT_SELECTED_ICON] = 0;
|
||||||
|
else
|
||||||
|
ent->client->ps.stats[STAT_SELECTED_ICON] = gi.imageindex (itemlist[ent->client->pers.selected_item].icon);
|
||||||
|
|
||||||
|
ent->client->ps.stats[STAT_SELECTED_ITEM] = ent->client->pers.selected_item;
|
||||||
|
|
||||||
|
//
|
||||||
|
// layouts
|
||||||
|
//
|
||||||
|
ent->client->ps.stats[STAT_LAYOUTS] = 0;
|
||||||
|
|
||||||
|
if (deathmatch->value)
|
||||||
|
{
|
||||||
|
//WF - scanner
|
||||||
|
if (ent->client->pers.health <= 0 || level.intermissiontime
|
||||||
|
|| ent->client->showscores || ent->client->pers.scanner_active
|
||||||
|
|| ent->client->showhelp) // TeT, added showhelp, needed for maphelp
|
||||||
|
ent->client->ps.stats[STAT_LAYOUTS] |= 1;
|
||||||
|
//WF
|
||||||
|
|
||||||
|
if (ent->client->showinventory && ent->client->pers.health > 0)
|
||||||
|
ent->client->ps.stats[STAT_LAYOUTS] |= 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ent->client->showscores || ent->client->showhelp)
|
||||||
|
ent->client->ps.stats[STAT_LAYOUTS] |= 1;
|
||||||
|
if (ent->client->showinventory && ent->client->pers.health > 0)
|
||||||
|
ent->client->ps.stats[STAT_LAYOUTS] |= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// frags
|
||||||
|
//
|
||||||
|
ent->client->ps.stats[STAT_FRAGS] = ent->client->resp.score;
|
||||||
|
|
||||||
|
//
|
||||||
|
// help icon / current weapon if not shown
|
||||||
|
//
|
||||||
|
if (ent->client->resp.helpchanged && (level.framenum&8) )
|
||||||
|
ent->client->ps.stats[STAT_HELPICON] = gi.imageindex ("i_help");
|
||||||
|
else if ( (ent->client->pers.hand == CENTER_HANDED || ent->client->ps.fov > 91)
|
||||||
|
&& ent->client->pers.weapon)
|
||||||
|
ent->client->ps.stats[STAT_HELPICON] = gi.imageindex (ent->client->pers.weapon->icon);
|
||||||
|
else
|
||||||
|
ent->client->ps.stats[STAT_HELPICON] = 0;
|
||||||
|
|
||||||
|
//ZOID
|
||||||
|
SetCTFStats(ent);
|
||||||
|
//ZOID
|
||||||
|
}
|
|
@ -0,0 +1,278 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
|
||||||
|
//#define DEBUG_NT 1
|
||||||
|
//#define DEBUG_UNIX 2
|
||||||
|
|
||||||
|
#ifdef DEBUG_NT
|
||||||
|
#include <crtdbg.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void PMenu_Open(edict_t *ent, pmenu_t *entries, int cur, int num, qboolean usekeys, qboolean showbackground)
|
||||||
|
{
|
||||||
|
pmenuhnd_t *hnd;
|
||||||
|
pmenu_t *p;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!ent->client)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ent->client->menu) {
|
||||||
|
gi.dprintf("warning, ent already has a menu\n");
|
||||||
|
PMenu_Close(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
hnd = malloc(sizeof(*hnd));
|
||||||
|
|
||||||
|
hnd->entries = entries;
|
||||||
|
hnd->num = num;
|
||||||
|
hnd->MenuTimeout = 0; //Default to no timeout
|
||||||
|
hnd->UseNumberKeys = usekeys; //Can select items with number keys?
|
||||||
|
hnd->ShowBackground = showbackground;
|
||||||
|
|
||||||
|
if (cur < 0 || !entries[cur].SelectFunc) {
|
||||||
|
for (i = 0, p = entries; i < num; i++, p++)
|
||||||
|
if (p->SelectFunc)
|
||||||
|
break;
|
||||||
|
} else
|
||||||
|
i = cur;
|
||||||
|
|
||||||
|
if (i >= num)
|
||||||
|
hnd->cur = -1;
|
||||||
|
else
|
||||||
|
hnd->cur = i;
|
||||||
|
|
||||||
|
ent->client->showscores = true;
|
||||||
|
ent->client->inmenu = true;
|
||||||
|
ent->client->menu = hnd;
|
||||||
|
|
||||||
|
PMenu_Update(ent);
|
||||||
|
gi.unicast (ent, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PMenu_Close(edict_t *ent)
|
||||||
|
{
|
||||||
|
if (!ent->client->menu)
|
||||||
|
return;
|
||||||
|
|
||||||
|
free(ent->client->menu);
|
||||||
|
ent->client->menu = NULL;
|
||||||
|
ent->client->showscores = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PMenu_Update(edict_t *ent)
|
||||||
|
{
|
||||||
|
char string[1400];
|
||||||
|
int i;
|
||||||
|
pmenu_t *p;
|
||||||
|
int x;
|
||||||
|
pmenuhnd_t *hnd;
|
||||||
|
char *t;
|
||||||
|
qboolean alt = false;
|
||||||
|
|
||||||
|
if (ent->health<1)
|
||||||
|
{
|
||||||
|
PMenu_Close(ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ent->client->menu) {
|
||||||
|
gi.dprintf("warning: ent has no menu\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hnd = ent->client->menu;
|
||||||
|
//JR Disabled this to slow down overflows//Nope added it back since it didn't work right then
|
||||||
|
|
||||||
|
if (hnd->ShowBackground)
|
||||||
|
strcpy(string, "xv 32 yv 8 picn inventory ");
|
||||||
|
else
|
||||||
|
strcpy(string, "");
|
||||||
|
|
||||||
|
for (i = 0, p = hnd->entries; i < hnd->num; i++, p++)
|
||||||
|
{
|
||||||
|
if (!p->text || !*(p->text))
|
||||||
|
continue; // blank line
|
||||||
|
t = p->text;
|
||||||
|
if (*t == '*') {
|
||||||
|
alt = true;
|
||||||
|
t++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//WF - made inventory screen larger
|
||||||
|
sprintf(string + strlen(string), "yv %d ", 32 + i * 8);
|
||||||
|
if (p->align == PMENU_ALIGN_CENTER)
|
||||||
|
x = 196/2 - strlen(t)*4 + 64;
|
||||||
|
// x = 240/2 - strlen(t)*4 + 64;
|
||||||
|
else if (p->align == PMENU_ALIGN_RIGHT)
|
||||||
|
// x = 64 + (196 - strlen(t)*8);
|
||||||
|
x = 64 + (240 - strlen(t)*8);
|
||||||
|
else
|
||||||
|
x = 64;
|
||||||
|
|
||||||
|
sprintf(string + strlen(string), "xv %d ",
|
||||||
|
x - ((hnd->cur == i) ? 8 : 0));
|
||||||
|
|
||||||
|
if (hnd->cur == i)
|
||||||
|
sprintf(string + strlen(string), "string2 \"\x0d%s\" ", t);
|
||||||
|
else if (alt)
|
||||||
|
sprintf(string + strlen(string), "string2 \"%s\" ", t);
|
||||||
|
else
|
||||||
|
sprintf(string + strlen(string), "string \"%s\" ", t);
|
||||||
|
alt = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
gi.WriteByte (svc_layout);
|
||||||
|
gi.WriteString (string);
|
||||||
|
|
||||||
|
//gi.dprintf("%s\n", string);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PMenu_Next(edict_t *ent)
|
||||||
|
{
|
||||||
|
pmenuhnd_t *hnd;
|
||||||
|
int i;
|
||||||
|
pmenu_t *p;
|
||||||
|
|
||||||
|
if (!ent->client->menu) {
|
||||||
|
gi.dprintf("warning: ent has no menu\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hnd = ent->client->menu;
|
||||||
|
|
||||||
|
if (hnd->cur < 0)
|
||||||
|
return; // no selectable entries
|
||||||
|
|
||||||
|
i = hnd->cur;
|
||||||
|
p = hnd->entries + hnd->cur;
|
||||||
|
do {
|
||||||
|
i++, p++;
|
||||||
|
if (i == hnd->num)
|
||||||
|
i = 0, p = hnd->entries;
|
||||||
|
if (p->SelectFunc)
|
||||||
|
break;
|
||||||
|
} while (i != hnd->cur);
|
||||||
|
|
||||||
|
hnd->cur = i;
|
||||||
|
|
||||||
|
PMenu_Update(ent);
|
||||||
|
gi.unicast (ent, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PMenu_Prev(edict_t *ent)
|
||||||
|
{
|
||||||
|
pmenuhnd_t *hnd;
|
||||||
|
int i;
|
||||||
|
pmenu_t *p;
|
||||||
|
|
||||||
|
if (!ent->client->menu) {
|
||||||
|
gi.dprintf("warning: ent has no menu\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hnd = ent->client->menu;
|
||||||
|
|
||||||
|
if (hnd->cur < 0)
|
||||||
|
return; // no selectable entries
|
||||||
|
|
||||||
|
i = hnd->cur;
|
||||||
|
p = hnd->entries + hnd->cur;
|
||||||
|
do {
|
||||||
|
if (i == 0) {
|
||||||
|
i = hnd->num - 1;
|
||||||
|
p = hnd->entries + i;
|
||||||
|
} else
|
||||||
|
i--, p--;
|
||||||
|
if (p->SelectFunc)
|
||||||
|
break;
|
||||||
|
} while (i != hnd->cur);
|
||||||
|
|
||||||
|
hnd->cur = i;
|
||||||
|
|
||||||
|
PMenu_Update(ent);
|
||||||
|
gi.unicast (ent, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PMenu_Select(edict_t *ent)
|
||||||
|
{
|
||||||
|
pmenuhnd_t *hnd;
|
||||||
|
pmenu_t *p;
|
||||||
|
|
||||||
|
if (!ent->client->menu) {
|
||||||
|
gi.dprintf("warning: ent has no menu\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hnd = ent->client->menu;
|
||||||
|
|
||||||
|
if (hnd->cur < 0)
|
||||||
|
return; // no selectable entries
|
||||||
|
|
||||||
|
p = hnd->entries + hnd->cur;
|
||||||
|
|
||||||
|
if (p->SelectFunc)
|
||||||
|
p->SelectFunc(ent, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WFMenuFromNumberKey(edict_t *ent, int slot)
|
||||||
|
{
|
||||||
|
pmenuhnd_t *hnd;
|
||||||
|
pmenu_t *p;
|
||||||
|
int i;
|
||||||
|
int pos;
|
||||||
|
|
||||||
|
if (!ent->client->menu) {
|
||||||
|
gi.dprintf("warning: ent has no menu\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hnd = ent->client->menu;
|
||||||
|
|
||||||
|
if (hnd->cur < 0)
|
||||||
|
return 0; // no selectable entries
|
||||||
|
|
||||||
|
|
||||||
|
for (i = 0, pos = 0; (i < hnd->num) && (ent->client->menu); ++i)
|
||||||
|
{
|
||||||
|
//DEBUGGING MEMORY PROBLEM-GREGG
|
||||||
|
#ifdef DEBUG_NT
|
||||||
|
if (_CrtIsValidPointer(hnd, sizeof(pmenuhnd_t), true) != true)
|
||||||
|
{
|
||||||
|
gi.dprintf("Bad Menu Handle in p_menu (2)\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
//DEBUGGING MEMORY PROBLEM-GREGG
|
||||||
|
|
||||||
|
if (!ent->client->menu) return 0;
|
||||||
|
|
||||||
|
//Pick next menu item
|
||||||
|
p = hnd->entries + i;
|
||||||
|
|
||||||
|
//Increment position for selectable items
|
||||||
|
if ( (p) && p->SelectFunc)
|
||||||
|
{
|
||||||
|
++pos;
|
||||||
|
|
||||||
|
//Did we find it?
|
||||||
|
if (pos == slot)
|
||||||
|
{
|
||||||
|
//Execute the function
|
||||||
|
p->SelectFunc(ent, p);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ent->client->menu) return 0;
|
||||||
|
|
||||||
|
//DEBUGGING MEMORY PROBLEM-GREGG
|
||||||
|
#ifdef TARGET_NT
|
||||||
|
if (_CrtIsValidPointer(hnd, sizeof(pmenuhnd_t), 1) != 1)
|
||||||
|
{
|
||||||
|
gi.dprintf("Bad Menu Handle in p_menu (2)\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
//DEBUGGING MEMORY PROBLEM-GREGG
|
||||||
|
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PMENU_ALIGN_LEFT,
|
||||||
|
PMENU_ALIGN_CENTER,
|
||||||
|
PMENU_ALIGN_RIGHT
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct pmenuhnd_s {
|
||||||
|
struct pmenu_s *entries;
|
||||||
|
int cur;
|
||||||
|
int num;
|
||||||
|
qboolean UseNumberKeys; //If true, number keys will select a menu item
|
||||||
|
float MenuTimeout; //If set, menu will time out and be removed after a set amount of time
|
||||||
|
qboolean ShowBackground; //Set if background should be shown
|
||||||
|
} pmenuhnd_t;
|
||||||
|
|
||||||
|
typedef struct pmenu_s {
|
||||||
|
char *text;
|
||||||
|
int align;
|
||||||
|
// void *arg;
|
||||||
|
int arg;
|
||||||
|
void (*SelectFunc)(edict_t *ent, struct pmenu_s *entry);
|
||||||
|
} pmenu_t;
|
||||||
|
|
||||||
|
void PMenu_Open(edict_t *ent, pmenu_t *entries, int cur, int num, qboolean usekeys, qboolean showbackground);
|
||||||
|
void PMenu_Close(edict_t *ent);
|
||||||
|
void PMenu_Update(edict_t *ent);
|
||||||
|
void PMenu_Next(edict_t *ent);
|
||||||
|
void PMenu_Prev(edict_t *ent);
|
||||||
|
void PMenu_Select(edict_t *ent);
|
||||||
|
int WFMenuFromNumberKey(edict_t *ent, int slot);
|
|
@ -0,0 +1,53 @@
|
||||||
|
void OptimizeRouteCache();
|
||||||
|
void CalcRoutes(int node_index);
|
||||||
|
int ClosestNodeToEnt(edict_t *self, int check_fullbox, int check_all_nodes);
|
||||||
|
float PathToEnt(edict_t *self, edict_t *target, int check_fullbox, int check_all_nodes);
|
||||||
|
edict_t *PathToEnt_Node; // self's goal node, to get to target, from last PathToEnt() call
|
||||||
|
edict_t *PathToEnt_TargetNode; // target's goal node, to get FROM self, from last PathToEnt() call
|
||||||
|
void Debug_ShowPathToGoal(edict_t *self, edict_t *goalent);
|
||||||
|
|
||||||
|
edict_t *matching_trail(vec3_t spot);
|
||||||
|
void AddTrailToPortals(edict_t *trail);
|
||||||
|
int GetGridPortal(float pos);
|
||||||
|
|
||||||
|
void NodeDebug(char *fmt, ...);
|
||||||
|
void CheckMoveForNodes(edict_t *ent);
|
||||||
|
void CalcItemPaths(edict_t *ent);
|
||||||
|
|
||||||
|
int trail_head, last_head;
|
||||||
|
|
||||||
|
int dropped_trail;
|
||||||
|
float last_optimize;
|
||||||
|
int optimize_marker;
|
||||||
|
|
||||||
|
#define NODE_NORMAL 0
|
||||||
|
#define NODE_PLAT 1
|
||||||
|
#define NODE_LANDING 2 // jump destination node (cannot be seen from any other nodes other than the jumping node)
|
||||||
|
#define NODE_BUTTON 3
|
||||||
|
#define NODE_TELEPORT 4
|
||||||
|
#define NODE_GRAPPLE 5 // set when this jump node is a grapple start
|
||||||
|
|
||||||
|
#define TRAIL_PORTAL_SUBDIVISION 24
|
||||||
|
#define MAX_TRAILS_PER_PORTAL 196
|
||||||
|
#define MAX_MAP_AXIS 5000
|
||||||
|
|
||||||
|
int trail_portals[TRAIL_PORTAL_SUBDIVISION+1][TRAIL_PORTAL_SUBDIVISION+1][MAX_TRAILS_PER_PORTAL];
|
||||||
|
// contains a list of trails grouped by grid blocks, in the X/Y plane, with
|
||||||
|
// size 512x512 (covers the entire map)
|
||||||
|
|
||||||
|
// each trail can be a member of up to 4 portals, so as to somewhat "blur" the
|
||||||
|
// line between nodes, so that selection of a ClosestNodeToEnt is less likely
|
||||||
|
// to be restricted by being close to a boundary
|
||||||
|
|
||||||
|
int num_trail_portals[TRAIL_PORTAL_SUBDIVISION+1][TRAIL_PORTAL_SUBDIVISION+1];
|
||||||
|
// keep track of the total nodes in each trail_portal
|
||||||
|
|
||||||
|
typedef struct ctf_item_s
|
||||||
|
{
|
||||||
|
char classname[64];
|
||||||
|
vec3_t origin;
|
||||||
|
vec3_t angles;
|
||||||
|
struct ctf_item_s *next;
|
||||||
|
} ctf_item_t;
|
||||||
|
|
||||||
|
ctf_item_t *ctf_item_head;
|
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,160 @@
|
||||||
|
//
|
||||||
|
// Q_DEVELS.C - cool support functions for Quake II development
|
||||||
|
// Version 1.5 (last updated Jan 29, 1998)
|
||||||
|
//
|
||||||
|
// Published at http://www.planetquake.com/qdevels
|
||||||
|
// Code by various authors, released by SumFuka@planetquake.com
|
||||||
|
//
|
||||||
|
// Please use this code in your mods... if you distribute it,
|
||||||
|
// KEEP THE AUTHOR'S NAMES with the code. They deserve the credit.
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
// INSTRUCTIONS :
|
||||||
|
//
|
||||||
|
// To use the functions in this file, first add the file to your project.
|
||||||
|
// Then add '#include "q_devels.h"' to the top of every source file you
|
||||||
|
// need to call the functions from, or even better, put that same line
|
||||||
|
// in g_local.h.
|
||||||
|
//
|
||||||
|
// Compile away, rock and roll.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#include "g_local.h"
|
||||||
|
#include "q_devels.h"
|
||||||
|
|
||||||
|
|
||||||
|
// 1. FOR_EACH_PLAYER by SumFuka
|
||||||
|
//
|
||||||
|
// Use this macro to quickly apply code to each player in the game
|
||||||
|
// ! you must supply arguments of type (edict_t *) and (int) !
|
||||||
|
// e.g. int i; edict_t *joe_bloggs;
|
||||||
|
// for_each_player(joe_bloggs,i) { joe_bloggs->client->pers.health = 0; }
|
||||||
|
|
||||||
|
// ---=== The code is #define'd in header file qdevels.h ===---
|
||||||
|
|
||||||
|
|
||||||
|
// 2. STUFFCMD (author unkown...)
|
||||||
|
//
|
||||||
|
// Use this function to send a command string to a CLIENT.
|
||||||
|
// E.g. stuffcmd(player, "alias ready \"cmd ready\"\n");
|
||||||
|
|
||||||
|
//void stuffcmd(edict_t *e, char *s) {
|
||||||
|
// gi.WriteByte (11);
|
||||||
|
// gi.WriteString (s);
|
||||||
|
// gi.unicast (e, true);
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
// 3. ENT_BY_NAME by Kevin Sullivan (Ignitor)
|
||||||
|
//
|
||||||
|
// Here is a nice little function I wrote to find a player's
|
||||||
|
// entity structure by his name.
|
||||||
|
|
||||||
|
edict_t *ent_by_name (char *target)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
edict_t *targ=NULL;
|
||||||
|
for (i=0;;i++)
|
||||||
|
{
|
||||||
|
if (i > globals.num_edicts)
|
||||||
|
return (NULL);
|
||||||
|
|
||||||
|
targ = G_Find (targ, FOFS(classname), "player");
|
||||||
|
|
||||||
|
if (strcmp(targ->client->pers.netname, target) == 0)
|
||||||
|
return (targ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 4. CENTERPRINT_ALL by SumFuka
|
||||||
|
//
|
||||||
|
// Use this function to centerprint to all players.
|
||||||
|
// e.g. centerprint_all("---<<< FIGHT ! >>>---\n");
|
||||||
|
|
||||||
|
void centerprint_all (char *msg)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
edict_t *joe_bloggs;
|
||||||
|
|
||||||
|
for_each_player(joe_bloggs,i)
|
||||||
|
{
|
||||||
|
gi.centerprintf (joe_bloggs, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 5. RNDNUM by RoJoMo7
|
||||||
|
//
|
||||||
|
// Returns a random number between "y" and "z".
|
||||||
|
// y = lower limit of number to generate.
|
||||||
|
// z = upper limit of number to generate.
|
||||||
|
// e.g. rndnum (10,20); -- returns a random number between 10 an 20.
|
||||||
|
|
||||||
|
// ---=== The code is #define'd in header file qdevels.h ===---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 6. RANDOM_PLAYER by SumFuka
|
||||||
|
//
|
||||||
|
// Select a random player, supply NULL for any player,
|
||||||
|
// or supply a player to exclude the player from being chosen.
|
||||||
|
//
|
||||||
|
// e.g. gi.centerprintf (random_player(NULL), "You're it !!!");
|
||||||
|
// e.g. gi.centerprintf (random_player(ent), "You're it !!!");
|
||||||
|
|
||||||
|
edict_t *random_player (edict_t *notme)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int count;
|
||||||
|
int random_player;
|
||||||
|
edict_t *joe_bloggs;
|
||||||
|
|
||||||
|
// count the number of players
|
||||||
|
count = 0;
|
||||||
|
for_each_player(joe_bloggs,i)
|
||||||
|
{
|
||||||
|
if (joe_bloggs != notme)
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// no players ?
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
gi.dprintf("ERROR: tried to select a random player when none are available.\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// select a random player
|
||||||
|
random_player = rand() % count;
|
||||||
|
|
||||||
|
// find the randomly selected player
|
||||||
|
count = 0;
|
||||||
|
for_each_player(joe_bloggs,i)
|
||||||
|
{
|
||||||
|
if (joe_bloggs != notme)
|
||||||
|
{
|
||||||
|
if (count == random_player)
|
||||||
|
return joe_bloggs;
|
||||||
|
else
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 7. Item lists by smith57@airmail.net
|
||||||
|
//
|
||||||
|
// These are the indexes into the entity->client->pers->inventory and
|
||||||
|
// the itemlist array. You could use ITEM_INDEX( FindItem( <item string ) )
|
||||||
|
// call, but i find that a stupid waste of cycles for finding an index
|
||||||
|
// into an array that does not change....
|
||||||
|
// (unless you add an item or id moves them around)
|
||||||
|
//
|
||||||
|
// [Use with 3.05 thru 3.10 code base.]
|
||||||
|
|
||||||
|
// ---=== The code is #define'd in header file qdevels.h ===---
|
|
@ -0,0 +1,66 @@
|
||||||
|
//
|
||||||
|
// QDEVELS.H - cool support functions for Quake II development
|
||||||
|
// Version 1.5 (last updated Jan 29, 1998)
|
||||||
|
//
|
||||||
|
// Published at http://www.planetquake.com/qdevels
|
||||||
|
// Code by various authors, released by SumFuka@planetquake.com
|
||||||
|
//
|
||||||
|
// Please browse qdevels.c for function descriptions.
|
||||||
|
|
||||||
|
|
||||||
|
#define for_each_player(JOE_BLOGGS,INDEX) \
|
||||||
|
for(INDEX=1;INDEX<=maxclients->value;INDEX++) \
|
||||||
|
if ((JOE_BLOGGS=&g_edicts[i]) && JOE_BLOGGS->inuse)
|
||||||
|
|
||||||
|
#define rndnum(y,z) ((random()*((z)-((y)+1)))+(y))
|
||||||
|
|
||||||
|
void stuffcmd(edict_t *e, char *s);
|
||||||
|
edict_t *ent_by_name (char *target);
|
||||||
|
void centerprint_all (char *msg);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 7. Item lists by smith57@airmail.net
|
||||||
|
|
||||||
|
#define ITEMLIST_NULLINDEX 0
|
||||||
|
#define ITEMLIST_BODYARMOR 1
|
||||||
|
#define ITEMLIST_COMBATARMOR 2
|
||||||
|
#define ITEMLIST_JACKETARMOR 3
|
||||||
|
#define ITEMLIST_ARMORSHARD 4
|
||||||
|
#define ITEMLIST_POWERSCREEN 5
|
||||||
|
#define ITEMLIST_POWERSHIELD 6
|
||||||
|
#define ITEMLIST_BLASTER 7
|
||||||
|
#define ITEMLIST_SHOTGUN 8
|
||||||
|
#define ITEMLIST_SUPERSHOTGUN 9
|
||||||
|
#define ITEMLIST_MACHINEGUN 10
|
||||||
|
#define ITEMLIST_CHAINGUN 11
|
||||||
|
#define ITEMLIST_GRENADELAUNCHER 12
|
||||||
|
#define ITEMLIST_ROCKETLAUNCHER 13
|
||||||
|
#define ITEMLIST_HYPERBLASTER 14
|
||||||
|
#define ITEMLIST_RAILGUN 15
|
||||||
|
#define ITEMLIST_BFG10K 16
|
||||||
|
#define ITEMLIST_SHELLS 17
|
||||||
|
#define ITEMLIST_BULLETS 18
|
||||||
|
#define ITEMLIST_CELLS 19
|
||||||
|
#define ITEMLIST_GRENADES 20
|
||||||
|
#define ITEMLIST_ROCKETS 21
|
||||||
|
#define ITEMLIST_SLUGS 22
|
||||||
|
#define ITEMLIST_QUADDAMAGE 23
|
||||||
|
#define ITEMLIST_INVULNERABILITY 24
|
||||||
|
#define ITEMLIST_SILENCER 25
|
||||||
|
#define ITEMLIST_REBREATHER 26
|
||||||
|
#define ITEMLIST_ENVIRONMENTSUIT 27
|
||||||
|
#define ITEMLIST_ANCIENTHEAD 28
|
||||||
|
#define ITEMLIST_ADRENALINE 29
|
||||||
|
#define ITEMLIST_BANDOLIER 30
|
||||||
|
#define ITEMLIST_AMMOPACK 31
|
||||||
|
#define ITEMLIST_DATACD 32
|
||||||
|
#define ITEMLIST_POWERCUBE 33
|
||||||
|
#define ITEMLIST_PYRAMIDKEY 34
|
||||||
|
#define ITEMLIST_DATASPINNER 35
|
||||||
|
#define ITEMLIST_SECURITYPASS 36
|
||||||
|
#define ITEMLIST_BLUEKEY 37
|
||||||
|
#define ITEMLIST_REDKEY 38
|
||||||
|
#define ITEMLIST_COMMANDERSHEAD 39
|
||||||
|
#define ITEMLIST_AIRSTRIKEMARKER 40
|
||||||
|
#define ITEMLIST_HEALTH 41
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,289 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
void T_Radius2Damage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int kickback, int mod);
|
||||||
|
|
||||||
|
//ent = the remote camera
|
||||||
|
void remote_remove(edict_t *ent)
|
||||||
|
{
|
||||||
|
if (!ent) return;
|
||||||
|
|
||||||
|
ent->takedamage = DAMAGE_NO;
|
||||||
|
|
||||||
|
//Should it blow up?
|
||||||
|
if (ent->dmg)
|
||||||
|
{
|
||||||
|
T_Radius2Damage(ent, ent->owner, ent->dmg, NULL, 200, 30, MOD_CAMERA);
|
||||||
|
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION);
|
||||||
|
gi.WritePosition (ent->s.origin);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ent->owner) && (ent->owner->client))
|
||||||
|
{
|
||||||
|
ent->owner->client->remotetoggle = 0;
|
||||||
|
ent->owner->remotecam = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Remove camera
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remote_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (wfdebug) gi.dprintf("remote_die\n");
|
||||||
|
// T_RadiusDamage (self, self->owner, 20, NULL, 10,0);
|
||||||
|
|
||||||
|
// BANG !
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_EXPLOSION1);
|
||||||
|
gi.WritePosition(self->s.origin);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
self->takedamage = DAMAGE_NO;
|
||||||
|
self->nextthink = level.time + .1;
|
||||||
|
self->think = remote_remove;
|
||||||
|
}
|
||||||
|
|
||||||
|
void remotecam_think(edict_t *ent)
|
||||||
|
{
|
||||||
|
if(ent->owner->client->remotetoggle)
|
||||||
|
{
|
||||||
|
/* Remove cell requirements for now - GAR
|
||||||
|
if (ent->owner->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] < 1)
|
||||||
|
{
|
||||||
|
safe_cprintf(ent->owner, PRINT_HIGH, "Not enough cells for the Remote Camera to stay\n");
|
||||||
|
|
||||||
|
T_RadiusDamage (ent, ent->owner, 10, NULL, 10,0);
|
||||||
|
|
||||||
|
// BANG !
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_EXPLOSION1);
|
||||||
|
gi.WritePosition(ent->s.origin);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
remote_remove(ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
/* Remove cell requirements for now - GAR
|
||||||
|
if(ent->owner->client->remotetoggle)
|
||||||
|
ent->owner->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] -= 1;
|
||||||
|
*/
|
||||||
|
ent->nextthink = level.time + 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
void place_remotecam (edict_t *ent)
|
||||||
|
{
|
||||||
|
|
||||||
|
vec3_t forward,
|
||||||
|
wallp, offset;
|
||||||
|
|
||||||
|
trace_t tr;
|
||||||
|
|
||||||
|
// valid ent ?
|
||||||
|
if ((!ent->client) || (ent->health<=0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
//If there already is a camera, remove it
|
||||||
|
if (ent->remotecam)
|
||||||
|
{
|
||||||
|
//If damage is set to zero, just remove it
|
||||||
|
if (ent->remotecam->dmg == 0)
|
||||||
|
{
|
||||||
|
remote_remove(ent->remotecam);
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Camera removed.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Otherwise, detonate it
|
||||||
|
if (ent->remotecam->delay > level.time)
|
||||||
|
{
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "You can't blow up camera for another %d seconds.\n",
|
||||||
|
(int)(ent->remotecam->delay - level.time));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Remote cam will detonate in 5 seconds.\n");
|
||||||
|
ent->remotecam->dmg = 400;
|
||||||
|
ent->remotecam->s.sound = gi.soundindex("weapons/hgrenc1b.wav");
|
||||||
|
ent->remotecam->think = remote_remove;
|
||||||
|
ent->remotecam->nextthink = level.time + 5;
|
||||||
|
//remote_remove(ent->remotecam);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cells for camera
|
||||||
|
if (ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] < 25)
|
||||||
|
{
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "You need 25 cells to place camera\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup "little look" to close wall
|
||||||
|
VectorCopy(ent->s.origin,wallp);
|
||||||
|
|
||||||
|
// Cast along view angle
|
||||||
|
AngleVectors (ent->client->v_angle, forward, NULL, NULL);
|
||||||
|
|
||||||
|
// Setup end point
|
||||||
|
wallp[0]=ent->s.origin[0]+forward[0]*50;
|
||||||
|
wallp[1]=ent->s.origin[1]+forward[1]*50;
|
||||||
|
wallp[2]=ent->s.origin[2]+forward[2]*50;
|
||||||
|
|
||||||
|
// trace
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, wallp, ent, MASK_SOLID);
|
||||||
|
|
||||||
|
// Line complete ? (ie. no collision)
|
||||||
|
if (tr.fraction == 1.0)
|
||||||
|
{
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Too far from wall.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hit sky ?
|
||||||
|
if (tr.surface)
|
||||||
|
{
|
||||||
|
if (tr.surface->flags & SURF_SKY)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] -= 25;
|
||||||
|
|
||||||
|
if (ent->remotecam)
|
||||||
|
{
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Remote cam off.\n");
|
||||||
|
remote_remove(ent->remotecam);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Remote cam on.\n");
|
||||||
|
|
||||||
|
ent->remotecam = G_Spawn();
|
||||||
|
VectorClear (ent->remotecam->mins);
|
||||||
|
VectorClear (ent->remotecam->maxs);
|
||||||
|
VectorCopy (tr.endpos, ent->remotecam->s.origin);
|
||||||
|
vectoangles(tr.plane.normal,ent->remotecam -> s.angles);
|
||||||
|
ent->remotecam -> movetype = MOVETYPE_NONE;
|
||||||
|
ent->remotecam -> clipmask = MASK_SHOT;
|
||||||
|
//grenade -> solid = SOLID_NOT;
|
||||||
|
ent->remotecam->solid = SOLID_BBOX;
|
||||||
|
VectorSet(ent->remotecam->mins, -3, -3, 0);
|
||||||
|
VectorSet(ent->remotecam->maxs, 3, 3, 6);
|
||||||
|
ent->remotecam->classname="camera";
|
||||||
|
ent->remotecam->takedamage=DAMAGE_YES;
|
||||||
|
ent->remotecam -> s.modelindex = gi.modelindex ("models/objects/camera/tris.md2");
|
||||||
|
ent->remotecam -> owner = ent;
|
||||||
|
ent->remotecam->think = remotecam_think;
|
||||||
|
ent->remotecam->nextthink = level.time + 0.5;
|
||||||
|
ent->remotecam->die = remote_die;
|
||||||
|
ent->remotecam->health= 60;
|
||||||
|
ent->remotecam->max_health = 60;
|
||||||
|
ent->remotecam->mass = 2;
|
||||||
|
ent->remotecam->delay = level.time + 10; //can't blow up for this many seconds
|
||||||
|
ent->remotecam->dmg = 20;
|
||||||
|
offset[0]=forward[0]*-10;
|
||||||
|
offset[1]=forward[1]*-10;
|
||||||
|
offset[2]=forward[2]*-10;
|
||||||
|
VectorAdd(offset,tr.endpos,offset);
|
||||||
|
VectorCopy(offset,ent->remotecam->camposition);
|
||||||
|
gi.linkentity (ent->remotecam);
|
||||||
|
|
||||||
|
/*db if(ent->client->remotetoggle)
|
||||||
|
{
|
||||||
|
ent->client->oldplayer = G_Spawn();
|
||||||
|
ent->client->oldplayer->s.frame = ent->s.frame;
|
||||||
|
VectorCopy (ent->s.origin, ent->client->oldplayer->s.origin);
|
||||||
|
VectorCopy (ent->velocity, ent->client->oldplayer->velocity);
|
||||||
|
VectorCopy (ent->s.angles, ent->client->oldplayer->s.angles);
|
||||||
|
ent->client->oldplayer->s.modelindex = ent->s.modelindex;
|
||||||
|
ent->client->oldplayer->s.modelindex2 = ent->s.modelindex2;
|
||||||
|
gi.linkentity (ent->client->oldplayer);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
//Remote Camera Commands
|
||||||
|
void cmd_CameraPlace(edict_t *ent)
|
||||||
|
{
|
||||||
|
place_remotecam (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_CameraToggle(edict_t *ent)
|
||||||
|
{
|
||||||
|
if (ent->remotecam == NULL)
|
||||||
|
{
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Remote camera does not exist!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent->client->remotetoggle)
|
||||||
|
{
|
||||||
|
ent->client->remotetoggle = 0;
|
||||||
|
//db G_FreeEdict(ent->client->oldplayer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ent->client->remotetoggle =1;
|
||||||
|
/*db ent->client->oldplayer = G_Spawn();
|
||||||
|
ent->client->oldplayer->s.frame = ent->s.frame;
|
||||||
|
VectorCopy (ent->s.origin, ent->client->oldplayer->s.origin);
|
||||||
|
VectorCopy (ent->velocity, ent->client->oldplayer->velocity);
|
||||||
|
VectorCopy (ent->s.angles, ent->client->oldplayer->s.angles);
|
||||||
|
ent->client->oldplayer->s.modelindex = ent->s.modelindex;
|
||||||
|
ent->client->oldplayer->s.modelindex2 = ent->s.modelindex2;
|
||||||
|
gi.linkentity (ent->client->oldplayer);*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command line handling for camera
|
||||||
|
void cmd_Camera(edict_t *ent)
|
||||||
|
{
|
||||||
|
char *string;
|
||||||
|
int time = 0;
|
||||||
|
|
||||||
|
string = gi.args();
|
||||||
|
|
||||||
|
if (!ent->client) return;
|
||||||
|
|
||||||
|
//argument = "build", "detonate", "remove", "toggle"
|
||||||
|
if (Q_stricmp ( string, "build") == 0)
|
||||||
|
{
|
||||||
|
if (ent->remotecam)
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "You already have an active camera\n");
|
||||||
|
else
|
||||||
|
place_remotecam (ent);
|
||||||
|
}
|
||||||
|
else if (Q_stricmp ( string, "toggle") == 0)
|
||||||
|
{
|
||||||
|
if (!ent->remotecam)
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "You don't have an active camera!\n");
|
||||||
|
else
|
||||||
|
cmd_CameraToggle(ent);
|
||||||
|
}
|
||||||
|
else if (Q_stricmp ( string, "detonate") == 0)
|
||||||
|
{
|
||||||
|
if (!ent->remotecam)
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "You don't have an active camera!\n");
|
||||||
|
else
|
||||||
|
place_remotecam (ent);
|
||||||
|
}
|
||||||
|
else if (Q_stricmp ( string, "remove") == 0)
|
||||||
|
{
|
||||||
|
if (!ent->remotecam)
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "You don't have an active camera!\n");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ent->remotecam->dmg = 0;
|
||||||
|
place_remotecam (ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
safe_cprintf(ent->owner, PRINT_HIGH, "Incomplete command: add BUILD, DETONATE, REMOVE or TOGGLE\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,360 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
#include "stdlog.h"
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
struct mod_to_weapon
|
||||||
|
{
|
||||||
|
char *wname;
|
||||||
|
int wid;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mod_to_weapon m2w[] = {
|
||||||
|
|
||||||
|
{"Unknown", 0},
|
||||||
|
{"Blaster", 1},
|
||||||
|
{"Shotgun", 2},
|
||||||
|
{"Super Shotgun", 3},
|
||||||
|
{"Machine Gun", 4},
|
||||||
|
{"Chain Gun", 5},
|
||||||
|
{"Grenade", 6},
|
||||||
|
{"Grenade", 7},
|
||||||
|
{"Rocket", 8},
|
||||||
|
{"Rocket", 9},
|
||||||
|
{"Hyperblaster", 10},
|
||||||
|
{"Rail Gun", 11},
|
||||||
|
{"BFG", 12},
|
||||||
|
{"BFG", 13},
|
||||||
|
{"BFG", 14},
|
||||||
|
{"Hand Grenade", 15},
|
||||||
|
{"Hand Grenade", 16},
|
||||||
|
{"Water", 17},
|
||||||
|
{"Slime", 18},
|
||||||
|
{"Lava", 19},
|
||||||
|
{"Crush", 20},
|
||||||
|
{"Telefrag", 21},
|
||||||
|
{"Falling", 22},
|
||||||
|
{"Suicide", 23},
|
||||||
|
{"Held Grenade", 24},
|
||||||
|
{"Explosive", 25},
|
||||||
|
{"Barrel", 26},
|
||||||
|
{"Bomb", 27},
|
||||||
|
{"Exit", 28},
|
||||||
|
{"Splash", 29},
|
||||||
|
{"Target Laser", 30},
|
||||||
|
{"Trigger Hurt", 31},
|
||||||
|
{"Hit", 32},
|
||||||
|
{"Target Blaster", 33},
|
||||||
|
{"Grapple", 34},
|
||||||
|
{"Laserball", 35},
|
||||||
|
{"Goodyear Grenade", 36},
|
||||||
|
{"Proximity Grenade", 37},
|
||||||
|
{"Cluster Grenade", 38},
|
||||||
|
{"Pipe Bomb", 39},
|
||||||
|
{"Earth Quake", 40},
|
||||||
|
{"Reverse Telefrag", 41},
|
||||||
|
{"Turret Grenade", 42},
|
||||||
|
{"Flame", 43},
|
||||||
|
{"NAG Rifle", 44},
|
||||||
|
{"Pulse Cannon", 45},
|
||||||
|
{"Shrapnel Grenade", 46},
|
||||||
|
{"Cluster Rocket", 47},
|
||||||
|
{"Plasma Bomb", 48},
|
||||||
|
{"Disease", 49},
|
||||||
|
{"Sniper Rifle", 50},
|
||||||
|
{"Nail Gun", 51},
|
||||||
|
{"Spon Human Comb", 52},
|
||||||
|
{"Needler", 53},
|
||||||
|
{"Concusion Grenade", 54},
|
||||||
|
{"Armor Piercing Dart", 55},
|
||||||
|
{"Infected Dart", 56},
|
||||||
|
{"Napalm Rocket", 57},
|
||||||
|
{"Lightning Gun", 58},
|
||||||
|
{"Telsa Coil", 59},
|
||||||
|
{"Magnotron", 60},
|
||||||
|
{"Shock Grenade", 61},
|
||||||
|
{"Pellet Grenade", 62},
|
||||||
|
{"Flare Gun", 64},
|
||||||
|
{"Tranquilizer", 65},
|
||||||
|
{"Bolted Blaster", 66},
|
||||||
|
{"Sentry", 67},
|
||||||
|
{"Long Range Projectile", 68},
|
||||||
|
{"Flare", 69},
|
||||||
|
{"Kamikazi", 70},
|
||||||
|
{"Depot", 71},
|
||||||
|
{"Sentry Killer", 72},
|
||||||
|
{"Mega Chaingun", 73},
|
||||||
|
{"Homing Rocket", 74},
|
||||||
|
{"Tranquilizer Dart", 75},
|
||||||
|
{"Sniper Rifle - Leg Shot", 76},
|
||||||
|
{"Sniper Rifle - Head Shot", 77},
|
||||||
|
{"Sentry Rocket", 78},
|
||||||
|
{"Knife",79},
|
||||||
|
{"Knife In The Back",80},
|
||||||
|
{"Healing Depot", 81},
|
||||||
|
{"Laser Cutter",82},
|
||||||
|
{"Napalm Grenade", 83},
|
||||||
|
{"Flamethrower",84},
|
||||||
|
{"Bio Sentry",85},
|
||||||
|
{"AK47", 86},
|
||||||
|
{"Pistol", 87},
|
||||||
|
{"Ion Ripper",88},
|
||||||
|
{"Phalanx",89},
|
||||||
|
{"ETF Rifle",90},
|
||||||
|
{"Tesla",91},
|
||||||
|
{"Heat Beam",92},
|
||||||
|
{"Defender Sphere",93},
|
||||||
|
{"Blaster2",94},
|
||||||
|
{"Tracker",95},
|
||||||
|
{"Nuke",96},
|
||||||
|
{"Laser Defense",97},
|
||||||
|
{"Gas Grenade", 98},
|
||||||
|
{"Stinger", 99},
|
||||||
|
{"Missile Launcher", 100},
|
||||||
|
{"Camera", 101},
|
||||||
|
{"Feign", 102},
|
||||||
|
{"Freezer", 103},//acrid 3/99
|
||||||
|
{"",9999},
|
||||||
|
};
|
||||||
|
|
||||||
|
void wf_strdate(char *s)
|
||||||
|
{
|
||||||
|
time_t t;
|
||||||
|
struct tm *tmptr;
|
||||||
|
|
||||||
|
time(&t); //get the date/time
|
||||||
|
tmptr = localtime(&t);
|
||||||
|
|
||||||
|
//format the date
|
||||||
|
sprintf(s, "%2d/%2d/%2d", tmptr->tm_mon + 1, tmptr->tm_mday, tmptr->tm_year);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wf_strtime(char *s)
|
||||||
|
{
|
||||||
|
time_t t;
|
||||||
|
struct tm *tmptr;
|
||||||
|
|
||||||
|
time(&t); //get the date/time
|
||||||
|
tmptr = localtime(&t);
|
||||||
|
|
||||||
|
//format the time
|
||||||
|
sprintf(s, "%2d:%2d:%2d", tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
char *sl_FindWeapon(int mod)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (mod < 0) mod = 0;
|
||||||
|
|
||||||
|
len = sizeof(m2w);
|
||||||
|
for (i = 0; i < len; ++i)
|
||||||
|
{
|
||||||
|
if (mod == m2w[i].wid)
|
||||||
|
return (m2w[i].wname);
|
||||||
|
}
|
||||||
|
//return ("UNKNOWN");
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *logfile = NULL;
|
||||||
|
|
||||||
|
char *strip9(char *str)
|
||||||
|
{
|
||||||
|
char *p = str;
|
||||||
|
if (str == 0) return "";
|
||||||
|
while(*p)
|
||||||
|
{
|
||||||
|
if(*p==9)
|
||||||
|
*p = '_';
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sl_LogPlayerName( game_import_t *gi,
|
||||||
|
char *pPlayerName,
|
||||||
|
char *pTeamName)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void sl_Logging( game_import_t *gi, char *pPatchName )
|
||||||
|
{
|
||||||
|
char path[100];
|
||||||
|
char str[20];
|
||||||
|
char fname[60];
|
||||||
|
char fullpath[100];
|
||||||
|
|
||||||
|
logfile = NULL;
|
||||||
|
|
||||||
|
//if (wfflags == NULL) return;
|
||||||
|
|
||||||
|
//Is frag logging on?
|
||||||
|
if (((int)wfflags->value & WF_FRAG_LOGGING) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
strcpy(path, gamedir->string);
|
||||||
|
|
||||||
|
strcpy(fname, wf_game.stdlog_name);
|
||||||
|
|
||||||
|
if (fname == NULL || fname[0] == 0) strcpy(fname, "std.log");
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(WIN32)
|
||||||
|
//strcat(path,"\\std.log");
|
||||||
|
sprintf(fullpath, "%s\\%s", path, fname);
|
||||||
|
#else
|
||||||
|
//strcat(path,"/std.log");
|
||||||
|
sprintf(fullpath, "%s/%s", path, fname);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if( logfile = fopen(fullpath,"a") )
|
||||||
|
{
|
||||||
|
fprintf(logfile,"\x9\x9StdLog\x9 1.22\n");
|
||||||
|
fprintf(logfile,"\x9\x9PatchName\x9%s\n",strip9(pPatchName));
|
||||||
|
wf_strdate( str );
|
||||||
|
fprintf(logfile,"\x9\x9LogDate\x9%s\n",str);
|
||||||
|
wf_strtime( str );
|
||||||
|
fprintf(logfile,"\x9\x9LogTime\x9%s\n",str);
|
||||||
|
fflush(logfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void sl_GameStart( game_import_t *gi,level_locals_t level )
|
||||||
|
{
|
||||||
|
if( logfile )
|
||||||
|
{
|
||||||
|
fprintf(logfile,"\x9\x9LogDeathFlags\x9%d\n",dmflags);
|
||||||
|
fprintf(logfile,"\x9\x9Map\x9%s\n",strip9(level.level_name));
|
||||||
|
fprintf(logfile,"\x9\x9GameStart\x9\x9\x9%f\n",level.time);
|
||||||
|
fflush(logfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sl_GameEnd( game_import_t *gi,level_locals_t level )
|
||||||
|
{
|
||||||
|
if( logfile )
|
||||||
|
{
|
||||||
|
fprintf(logfile,"\x9\x9GameEnd\x9\x9\x9%f\n",level.time);
|
||||||
|
fflush(logfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sl_LogPlayerConnect( game_import_t *gi,level_locals_t level, edict_t *ent )
|
||||||
|
{
|
||||||
|
if( logfile )
|
||||||
|
{
|
||||||
|
fprintf(logfile,"\x9\x9PlayerConnect\x9%s\x9%s\x9%f\n",
|
||||||
|
strip9(ent->client->pers.netname),
|
||||||
|
CTFTeamName(ent->client->resp.ctf_team),
|
||||||
|
level.time);
|
||||||
|
fflush(logfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sl_LogPlayerDisconnect( game_import_t *gi,level_locals_t level, edict_t *ent )
|
||||||
|
{
|
||||||
|
if( logfile )
|
||||||
|
{
|
||||||
|
fprintf(logfile,"\x9\x9PlayerDisonnect\x9%s\x9\x9%f\n",
|
||||||
|
strip9(ent->client->pers.netname),
|
||||||
|
level.time);
|
||||||
|
fflush(logfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sl_LogPlayerTeamChange( game_import_t *gi, char *pPlayerName, char *pTeamName)
|
||||||
|
{
|
||||||
|
if( logfile )
|
||||||
|
{
|
||||||
|
fprintf(logfile,"\x9\x9PlayerTeamChange\x9%s\x9%s\x9%f\n",
|
||||||
|
strip9(pPlayerName),
|
||||||
|
strip9(pTeamName),
|
||||||
|
level.time);
|
||||||
|
fflush(logfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void sl_LogPlayerRename( game_import_t *gi, char *pPlayerName, char *pNewName)
|
||||||
|
{
|
||||||
|
if( logfile )
|
||||||
|
{
|
||||||
|
fprintf(logfile,"\x9\x9PlayerRename\x9%s\x9%s\x9%f\n",
|
||||||
|
strip9(pPlayerName),
|
||||||
|
strip9(pNewName),
|
||||||
|
level.time);
|
||||||
|
fflush(logfile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void sl_LogScore( game_import_t *gi,
|
||||||
|
char *pKillerName,
|
||||||
|
char *pTargetName,
|
||||||
|
char *pScoreType,
|
||||||
|
int mod,
|
||||||
|
int iScore)
|
||||||
|
{
|
||||||
|
char *pWeaponName;
|
||||||
|
|
||||||
|
if( !logfile ) return;
|
||||||
|
|
||||||
|
//Check for suicide
|
||||||
|
if (strcmp(pScoreType, "Suicide") == 0)
|
||||||
|
{
|
||||||
|
pWeaponName = sl_FindWeapon(mod); //Start with these
|
||||||
|
switch (mod)
|
||||||
|
{
|
||||||
|
case MOD_FALLING:
|
||||||
|
pWeaponName = "Fell";
|
||||||
|
break;
|
||||||
|
case MOD_CRUSH:
|
||||||
|
pWeaponName = "Crushed";
|
||||||
|
break;
|
||||||
|
case MOD_WATER:
|
||||||
|
pWeaponName = "Drowned";
|
||||||
|
break;
|
||||||
|
case MOD_SLIME:
|
||||||
|
pWeaponName = "Melted";
|
||||||
|
break;
|
||||||
|
case MOD_LAVA:
|
||||||
|
pWeaponName = "Lava";
|
||||||
|
break;
|
||||||
|
case MOD_BOMB:
|
||||||
|
case MOD_EXPLOSIVE:
|
||||||
|
case MOD_BARREL:
|
||||||
|
pWeaponName = "Explosion";
|
||||||
|
break;
|
||||||
|
case MOD_TARGET_LASER:
|
||||||
|
pWeaponName = "Lasered";
|
||||||
|
break;
|
||||||
|
case MOD_TARGET_BLASTER:
|
||||||
|
pWeaponName = "Blasted";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fprintf(logfile,"%s\x9%s\x9%s\x9%s\x9%d\x9%f\n",
|
||||||
|
strip9(pKillerName),
|
||||||
|
strip9(pTargetName),
|
||||||
|
strip9(pScoreType),
|
||||||
|
pWeaponName,
|
||||||
|
iScore,
|
||||||
|
level.time );
|
||||||
|
fflush(logfile);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Not suicide - do normal kill
|
||||||
|
if( logfile )
|
||||||
|
{
|
||||||
|
fprintf(logfile,"%s\x9%s\x9%s\x9%s\x9%d\x9%f\n",
|
||||||
|
strip9(pKillerName),
|
||||||
|
strip9(pTargetName),
|
||||||
|
strip9(pScoreType),
|
||||||
|
strip9(sl_FindWeapon(mod)),
|
||||||
|
iScore,
|
||||||
|
level.time );
|
||||||
|
fflush(logfile);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
* GibStats Logging functions
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* $Id: stdlog.h 1.4 1998/03/14 13:19:09 mdavies Exp mdavies $
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __STDLOG_H__
|
||||||
|
#define __STDLOG_H__
|
||||||
|
|
||||||
|
#define _USE_LOGGING_
|
||||||
|
#ifdef _USE_LOGGING_
|
||||||
|
|
||||||
|
//#define __STDLOG_ID__ "$Id: stdlog.h 1.4 1998/03/14 13:19:09 mdavies Exp $"
|
||||||
|
#define __STDLOG_ID__ "$Id: stdlog.h 1.7 1998/04/06 09:24:23 mdavies Exp $"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NEW API
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
extern void sl_LogPlayerTeamChange( game_import_t *gi,
|
||||||
|
char *pPlayerName,
|
||||||
|
char *pTeamName);
|
||||||
|
|
||||||
|
extern void sl_LogPlayerRename( game_import_t *gi,
|
||||||
|
char *pOldPlayerName,
|
||||||
|
char *pNewPlayerName);
|
||||||
|
|
||||||
|
extern void sl_Logging( game_import_t *gi,
|
||||||
|
char *pPatchName );
|
||||||
|
|
||||||
|
extern void sl_GameStart( game_import_t *gi,
|
||||||
|
level_locals_t level );
|
||||||
|
|
||||||
|
extern void sl_GameEnd( game_import_t *gi,
|
||||||
|
level_locals_t level );
|
||||||
|
|
||||||
|
void sl_LogScore( game_import_t *gi,
|
||||||
|
char *pKillerName,
|
||||||
|
char *pTargetName,
|
||||||
|
char *pScoreType,
|
||||||
|
int mod,
|
||||||
|
int iScore);
|
||||||
|
|
||||||
|
extern void sl_LogPlayerConnect( game_import_t *gi,
|
||||||
|
level_locals_t level,
|
||||||
|
edict_t *ent );
|
||||||
|
|
||||||
|
extern void sl_LogPlayerDisconnect( game_import_t *gi,
|
||||||
|
level_locals_t level,
|
||||||
|
edict_t *ent );
|
||||||
|
|
||||||
|
extern void sl_LogPlayerName( game_import_t *gi,
|
||||||
|
char *pPlayerName,
|
||||||
|
char *pTeamName );
|
||||||
|
#else
|
||||||
|
#define sl_LogPlayerTeamChange( gi, pPlayerName, pTeamName)
|
||||||
|
#define sl_LogPlayerRename( gi, OldPlayerName, pNewPlayerName)
|
||||||
|
#define sl_Logging( gi, pPatchName )
|
||||||
|
#define sl_GameStart(gi, level )
|
||||||
|
#define sl_GameEnd( gi, level )
|
||||||
|
#define sl_LogScore( gi, pKillerName, pTargetName, pScoreType, pWeaponName, iScore)
|
||||||
|
#define sl_LogPlayerConnect( gi, level, ent )
|
||||||
|
#define sl_LogPlayerDisconnect( gi, level, ent )
|
||||||
|
#define sl_LogPlayerName( gi, pPlayerName, pTeamName)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* end of file */
|
|
@ -0,0 +1,82 @@
|
||||||
|
//throwup.c
|
||||||
|
//1/5/98 JR
|
||||||
|
|
||||||
|
#include "g_local.h"
|
||||||
|
#include "throwup.h"
|
||||||
|
|
||||||
|
//this function makes you throw up
|
||||||
|
void ThrowUpNow(edict_t *self)
|
||||||
|
{
|
||||||
|
//variables
|
||||||
|
vec3_t forward, right;
|
||||||
|
vec3_t mouth_pos, spew_vector;
|
||||||
|
float rnum;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
//set spew vector based on client's view angle
|
||||||
|
if (self->client)
|
||||||
|
AngleVectors (self->client->v_angle, forward, right, NULL);
|
||||||
|
else
|
||||||
|
AngleVectors (self->s.angles, forward, right, NULL);
|
||||||
|
|
||||||
|
//Make it originate from the mouth
|
||||||
|
VectorScale (forward, 24, mouth_pos);
|
||||||
|
VectorAdd (mouth_pos, self->s.origin, mouth_pos);
|
||||||
|
mouth_pos[2] += self->viewheight;
|
||||||
|
//Make come foward from the mouth
|
||||||
|
VectorScale (forward, 24, spew_vector);
|
||||||
|
//Blood
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_BLOOD);
|
||||||
|
gi.WritePosition (mouth_pos);
|
||||||
|
gi.WriteDir (spew_vector);
|
||||||
|
gi.multicast (mouth_pos, MULTICAST_PVS);
|
||||||
|
//say something
|
||||||
|
/* G.R. - Removed for now - too many messages
|
||||||
|
rnum = random();
|
||||||
|
if (rnum < 0.2)
|
||||||
|
gi.bprintf (PRINT_MEDIUM, "Retch !\n");
|
||||||
|
else if (rnum < 0.4)
|
||||||
|
gi.bprintf (PRINT_MEDIUM, "...Vomit...\n");
|
||||||
|
else if (rnum < 0.6)
|
||||||
|
gi.bprintf (PRINT_MEDIUM, "Chunder.\n");
|
||||||
|
else if (rnum < 0.8)
|
||||||
|
gi.bprintf (PRINT_MEDIUM, "Chuck. chuck. chuck.\n");
|
||||||
|
else
|
||||||
|
gi.bprintf (PRINT_MEDIUM, "Hmmmmff hmmmf hhhuuuuuuurrrrrllll.\n");
|
||||||
|
*/
|
||||||
|
|
||||||
|
// make a painful sound
|
||||||
|
rnum = random();
|
||||||
|
if (self->client)
|
||||||
|
{
|
||||||
|
if (rnum < 0.125)
|
||||||
|
gi.sound (self, CHAN_BODY, gi.soundindex("*gurp1.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else if (rnum < 0.25)
|
||||||
|
gi.sound (self, CHAN_BODY, gi.soundindex("*gurp2.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else if (rnum < 0.375)
|
||||||
|
gi.sound (self, CHAN_BODY, gi.soundindex("*pain50_1.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else if (rnum < 0.5)
|
||||||
|
gi.sound (self, CHAN_BODY, gi.soundindex("*pain50_2.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else if (rnum < 0.625)
|
||||||
|
gi.sound (self, CHAN_BODY, gi.soundindex("*pain75_1.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else if (rnum < 0.75)
|
||||||
|
gi.sound (self, CHAN_BODY, gi.soundindex("*pain75_2.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else if (rnum < 0.875)
|
||||||
|
gi.sound (self, CHAN_BODY, gi.soundindex("*pain100_1.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else
|
||||||
|
gi.sound (self, CHAN_BODY, gi.soundindex("*pain100_2.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
// also do a spewing sound
|
||||||
|
gi.sound (self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0);
|
||||||
|
// cough up some gibs.
|
||||||
|
for (i = 0; i<5; i++) {
|
||||||
|
ThrowVomit (self, mouth_pos, forward, right, self->velocity);
|
||||||
|
}
|
||||||
|
// every now and again, cough up MEGA vomit
|
||||||
|
if (random() < 0.1) {
|
||||||
|
for (i = 0; i<10; i++) {
|
||||||
|
ThrowVomit (self, mouth_pos, forward, right, self->velocity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
//throup.h
|
||||||
|
//1/5/98 JR
|
||||||
|
|
||||||
|
//main function
|
||||||
|
|
||||||
|
void ThrowUpNow (edict_t *self);
|
||||||
|
//utility in g_misc.c
|
||||||
|
void ThrowVomit (edict_t *ent, vec3_t mouth_pos, vec3_t forward, vec3_t right, vec3_t player_vel);
|
Binary file not shown.
|
@ -0,0 +1,105 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
|
||||||
|
#define DEFAULT_AK47_HSPREAD 200 //narrower spread than machine gun
|
||||||
|
#define DEFAULT_AK47_VSPREAD 300
|
||||||
|
|
||||||
|
/*
|
||||||
|
======================================================================
|
||||||
|
|
||||||
|
MACHINEGUN AK-47
|
||||||
|
by Fireball
|
||||||
|
|
||||||
|
======================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
void AK47_Fire (edict_t *ent)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
vec3_t start;
|
||||||
|
vec3_t forward, right;
|
||||||
|
vec3_t angles;
|
||||||
|
int damage = wf_game.weapon_damage[WEAPON_AK47]; // AK-47 is better than Q2's machinegun :)
|
||||||
|
int kick = 3;
|
||||||
|
vec3_t offset;
|
||||||
|
|
||||||
|
if (!(ent->client->buttons & BUTTON_ATTACK))
|
||||||
|
{
|
||||||
|
ent->client->machinegun_shots = 0;
|
||||||
|
ent->client->ps.gunframe++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent->client->ps.gunframe == 5)
|
||||||
|
ent->client->ps.gunframe = 4;
|
||||||
|
else
|
||||||
|
ent->client->ps.gunframe = 5;
|
||||||
|
|
||||||
|
if (ent->client->pers.inventory[ent->client->ammo_index] < 1)
|
||||||
|
{
|
||||||
|
ent->client->ps.gunframe = 6;
|
||||||
|
if (level.time >= ent->pain_debounce_time)
|
||||||
|
{
|
||||||
|
gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0);
|
||||||
|
ent->pain_debounce_time = level.time + 1;
|
||||||
|
}
|
||||||
|
NoAmmoWeaponChange (ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Cardinal
|
||||||
|
/*
|
||||||
|
if ((ent->client->pers.inventory[ent->client->ammo_index] - 1) % 30 == 0 && ent->client->pers.inventory[ent->client->ammo_index] > 1)
|
||||||
|
{
|
||||||
|
ent->client->weaponstate = WEAPON_MACHINEGUNREARMING;
|
||||||
|
ent->client->machinegun_shots = 0;
|
||||||
|
ent->client->ps.gunframe = 6;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// Cardinal
|
||||||
|
if (is_quad)
|
||||||
|
{
|
||||||
|
damage *= 4;
|
||||||
|
kick *= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=1 ; i<3 ; i++)
|
||||||
|
{
|
||||||
|
ent->client->kick_origin[i] = crandom() * 0.35;
|
||||||
|
ent->client->kick_angles[i] = crandom() * 0.7;
|
||||||
|
}
|
||||||
|
ent->client->kick_origin[0] = crandom() * 0.35;
|
||||||
|
ent->client->kick_angles[0] = ent->client->machinegun_shots * -1.5;
|
||||||
|
|
||||||
|
// don't raise the gun as it is firing
|
||||||
|
if (!deathmatch->value)
|
||||||
|
{
|
||||||
|
ent->client->machinegun_shots++;
|
||||||
|
if (ent->client->machinegun_shots > 9)
|
||||||
|
ent->client->machinegun_shots = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get start / end positions
|
||||||
|
VectorAdd (ent->client->v_angle, ent->client->kick_angles, angles);
|
||||||
|
AngleVectors (angles, forward, right, NULL);
|
||||||
|
VectorSet(offset, 0, 8, ent->viewheight-8);
|
||||||
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
||||||
|
fire_bullet (ent, start, forward, damage, kick, DEFAULT_AK47_HSPREAD, DEFAULT_AK47_VSPREAD, MOD_AK47);
|
||||||
|
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
gi.WriteShort (ent-g_edicts);
|
||||||
|
// gi.WriteByte (MZ_MACHINEGUN| is_silenced);
|
||||||
|
gi.WriteByte (MZ_ETF_RIFLE| is_silenced);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
||||||
|
|
||||||
|
if (!((int)dmflags->value & DF_INFINITE_AMMO))
|
||||||
|
ent->client->pers.inventory[ent->client->ammo_index]--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Weapon_AK47 (edict_t *ent)
|
||||||
|
{
|
||||||
|
static int pause_frames[] = {23, 45, 0};
|
||||||
|
static int fire_frames[] = {4, 5, 0};
|
||||||
|
|
||||||
|
Weapon_Generic (ent, 3, 5, 45, 49, pause_frames, fire_frames, AK47_Fire);
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
void dart_prethink (edict_t *ent);
|
||||||
|
void stick(edict_t *projectile, edict_t *object);
|
||||||
|
void WFAddArmor (edict_t *other, int armorval);
|
||||||
|
void AddArmorToSentry(edict_t *ent, int amt);
|
||||||
|
|
||||||
|
/*
|
||||||
|
======================
|
||||||
|
Armor Piercing Dart Launcher
|
||||||
|
======================
|
||||||
|
*/
|
||||||
|
|
||||||
|
void armordart_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
// int index;
|
||||||
|
|
||||||
|
if (other == self->owner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//Special case if its a sentry gun - upgrade health
|
||||||
|
if ((strcmp(other->classname, "SentryGun") == 0) && (other->wf_team == self->wf_team))
|
||||||
|
{
|
||||||
|
AddArmorToSentry(other, 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!other->client) //only damage players
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (surf && (surf->flags & SURF_SKY))
|
||||||
|
{
|
||||||
|
G_FreeEdict (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->owner->client)
|
||||||
|
PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
|
||||||
|
|
||||||
|
if (other->takedamage)
|
||||||
|
{
|
||||||
|
//Increase armor of person if you hit someone on the same team
|
||||||
|
if (other->wf_team == self->wf_team)
|
||||||
|
{
|
||||||
|
WFAddArmor(other, 25);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (other->s.origin[2]-self->s.origin[2] > self->viewheight-6)
|
||||||
|
T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg*5, 2, DAMAGE_ENERGY, MOD_ARMORDART);
|
||||||
|
else
|
||||||
|
T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 2, DAMAGE_ENERGY, MOD_ARMORDART);
|
||||||
|
gi.sound (other, CHAN_VOICE, gi.soundindex ("darthit.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_BLASTER);
|
||||||
|
gi.WritePosition (self->s.origin);
|
||||||
|
if (!plane)
|
||||||
|
gi.WriteDir (vec3_origin);
|
||||||
|
else
|
||||||
|
gi.WriteDir (plane->normal);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->think = G_FreeEdict;
|
||||||
|
self->nextthink = level.time + 3 + 8 * crandom();
|
||||||
|
|
||||||
|
stick(self, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fire_armordart (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect)
|
||||||
|
{
|
||||||
|
edict_t *bolt;
|
||||||
|
trace_t tr;
|
||||||
|
|
||||||
|
VectorNormalize (dir);
|
||||||
|
|
||||||
|
bolt = G_Spawn();
|
||||||
|
bolt->wf_team = self->wf_team;
|
||||||
|
VectorCopy (start, bolt->s.origin);
|
||||||
|
VectorCopy (start, bolt->s.old_origin);
|
||||||
|
vectoangles (dir, bolt->s.angles);
|
||||||
|
VectorScale (dir, speed, bolt->velocity);
|
||||||
|
bolt->velocity[2] += 100;
|
||||||
|
bolt->movetype = MOVETYPE_TOSS; //JR 1/4/98 changed so it bounces
|
||||||
|
bolt->clipmask = MASK_SHOT;
|
||||||
|
bolt->prethink = dart_prethink; // Keeps the arrow aligned, so it arcs through the air nicely.
|
||||||
|
bolt->gravity = 0.4;
|
||||||
|
bolt->solid = SOLID_BBOX;
|
||||||
|
bolt->s.effects |= effect;
|
||||||
|
VectorSet (bolt->mins,-1,-1,-3.5);
|
||||||
|
VectorSet (bolt->maxs,1,1,2.5);
|
||||||
|
bolt->s.modelindex = gi.modelindex ("models/dart/tris.md2");
|
||||||
|
bolt->s.skinnum = 1;
|
||||||
|
bolt->owner = self;
|
||||||
|
bolt->touch = armordart_touch;
|
||||||
|
// bolt->touch = infecteddart_touch;
|
||||||
|
bolt->nextthink = level.time + 1.7;
|
||||||
|
bolt->think = G_FreeEdict;
|
||||||
|
bolt->dmg = damage;
|
||||||
|
bolt->classname = "armor dart";
|
||||||
|
gi.linkentity (bolt);
|
||||||
|
|
||||||
|
if (self->client)
|
||||||
|
check_dodge (self, bolt->s.origin, dir, speed);
|
||||||
|
|
||||||
|
tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
|
||||||
|
if (tr.fraction < 1.0)
|
||||||
|
{
|
||||||
|
VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
|
||||||
|
bolt->touch (bolt, tr.ent, NULL, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void weapon_armordartlauncher_fire (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t start;
|
||||||
|
vec3_t forward, right;
|
||||||
|
vec3_t offset;
|
||||||
|
int damage;
|
||||||
|
|
||||||
|
damage = wf_game.weapon_damage[WEAPON_ARMORDART];
|
||||||
|
|
||||||
|
if (is_quad)
|
||||||
|
{
|
||||||
|
damage *= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
||||||
|
|
||||||
|
VectorScale (forward, -3, ent->client->kick_origin);
|
||||||
|
ent->client->kick_angles[0] = -3;
|
||||||
|
|
||||||
|
VectorSet(offset, 0, 7, ent->viewheight-8);
|
||||||
|
//start[0]=ent->s.origin[0] + forward[0]*1+right[0]*6;
|
||||||
|
//start[1]=ent->s.origin[1] + forward[1]*1+right[1]*6;
|
||||||
|
//start[2]=ent->s.origin[2] + forward[2]*1+right[2]*6+ent->viewheight-8;
|
||||||
|
|
||||||
|
/* new stuff to fix left handedness */
|
||||||
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
||||||
|
/* new stuff to fix left handedness */
|
||||||
|
|
||||||
|
fire_armordart (ent, start, forward, damage, wf_game.weapon_speed[WEAPON_ARMORDART], EF_GREENGIB);
|
||||||
|
|
||||||
|
// send muzzle flash
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
gi.WriteShort (ent-g_edicts);
|
||||||
|
gi.WriteByte (MZ_SHOTGUN | is_silenced);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
ent->client->ps.gunframe++;
|
||||||
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
||||||
|
|
||||||
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
||||||
|
ent->client->pers.inventory[ent->client->ammo_index] -= ent->client->pers.weapon->quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Weapon_ArmorDartLauncher (edict_t *ent)
|
||||||
|
{
|
||||||
|
static int pause_frames[] = {34, 51, 59, 0};
|
||||||
|
static int fire_frames[] = {6, 0};
|
||||||
|
|
||||||
|
Weapon_Generic (ent, 5, 16, 59, 64, pause_frames, fire_frames, weapon_armordartlauncher_fire);
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
|
||||||
|
#include "g_local.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================
|
||||||
|
Magneticlly Bolted Blaster
|
||||||
|
===========================
|
||||||
|
*/
|
||||||
|
|
||||||
|
void blaster2_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
if (other == self->owner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (surf && (surf->flags & SURF_SKY))
|
||||||
|
{
|
||||||
|
G_FreeEdict (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->owner->client)
|
||||||
|
PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
|
||||||
|
|
||||||
|
if (other->takedamage)
|
||||||
|
T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, DAMAGE_ENERGY, MOD_BOLTEDBLASTER);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//JR 1/4/98 added return
|
||||||
|
return;
|
||||||
|
//COde below not used
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_BLASTER);
|
||||||
|
gi.WritePosition (self->s.origin);
|
||||||
|
if (!plane)
|
||||||
|
gi.WriteDir (vec3_origin);
|
||||||
|
else
|
||||||
|
gi.WriteDir (plane->normal);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_FreeEdict (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fire_blaster2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect)
|
||||||
|
{
|
||||||
|
edict_t *bolt;
|
||||||
|
trace_t tr;
|
||||||
|
|
||||||
|
VectorNormalize (dir);
|
||||||
|
|
||||||
|
bolt = G_Spawn();
|
||||||
|
bolt->wf_team = self->wf_team;
|
||||||
|
|
||||||
|
VectorCopy (start, bolt->s.origin);
|
||||||
|
vectoangles (dir, bolt->s.angles);
|
||||||
|
VectorScale (dir, speed, bolt->velocity);
|
||||||
|
VectorAdd(start,bolt->velocity,bolt->s.old_origin);
|
||||||
|
bolt->movetype = MOVETYPE_FLYRICOCHET; //JR 1/4/98 changed so it bounces
|
||||||
|
bolt->clipmask = MASK_SHOT;
|
||||||
|
bolt->solid = SOLID_BBOX;
|
||||||
|
bolt->s.renderfx |= RF_BEAM|RF_TRANSLUCENT;
|
||||||
|
//bolt->s.effects |= effect;
|
||||||
|
VectorSet (bolt->mins,-8,-8,-8);
|
||||||
|
VectorSet (bolt->maxs,8,8,8);
|
||||||
|
bolt->s.modelindex = 1;
|
||||||
|
bolt->s.frame = 2;
|
||||||
|
if (self->wf_team == CTF_TEAM1) // on red team
|
||||||
|
bolt->s.skinnum = 0xf2f2f0f0; // red
|
||||||
|
else // on blue team
|
||||||
|
bolt->s.skinnum = 0xf3f3f1f1; // sorta blue
|
||||||
|
|
||||||
|
//bolt->s.modelindex = gi.modelindex ("models/objects/laser/tris.md2");
|
||||||
|
bolt->s.sound = gi.soundindex ("misc/lasfly.wav");
|
||||||
|
bolt->owner = self;
|
||||||
|
bolt->touch = blaster2_touch;
|
||||||
|
bolt->nextthink = level.time + 4;
|
||||||
|
bolt->think = G_FreeEdict;
|
||||||
|
bolt->dmg = damage;
|
||||||
|
gi.linkentity (bolt);
|
||||||
|
|
||||||
|
if (self->client)
|
||||||
|
check_dodge (self, bolt->s.origin, dir, speed);
|
||||||
|
|
||||||
|
tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
|
||||||
|
if (tr.fraction < 1.0)
|
||||||
|
{
|
||||||
|
VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
|
||||||
|
bolt->touch (bolt, tr.ent, NULL, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void BoltedBlaster_Fire (edict_t *ent, vec3_t g_offset, int damage, qboolean hyper, int effect)
|
||||||
|
{
|
||||||
|
vec3_t forward, right;
|
||||||
|
vec3_t start;
|
||||||
|
vec3_t offset;
|
||||||
|
|
||||||
|
if (is_quad)
|
||||||
|
damage *= 4;
|
||||||
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
||||||
|
VectorSet(offset, 24, 8, ent->viewheight-8);
|
||||||
|
VectorAdd (offset, g_offset, offset);
|
||||||
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
||||||
|
|
||||||
|
VectorScale (forward, -2, ent->client->kick_origin);
|
||||||
|
ent->client->kick_angles[0] = -1;
|
||||||
|
|
||||||
|
fire_blaster2 (ent, start, forward, damage, wf_game.weapon_speed[WEAPON_MAGBOLTED], effect);
|
||||||
|
|
||||||
|
// send muzzle flash
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
gi.WriteShort (ent-g_edicts);
|
||||||
|
if (hyper)
|
||||||
|
gi.WriteByte (MZ_HYPERBLASTER | is_silenced);
|
||||||
|
else
|
||||||
|
gi.WriteByte (MZ_BLASTER | is_silenced);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Weapon_BoltedBlaster_Fire (edict_t *ent)
|
||||||
|
{
|
||||||
|
int damage;
|
||||||
|
|
||||||
|
damage = wf_game.weapon_damage[WEAPON_MAGBOLTED];
|
||||||
|
BoltedBlaster_Fire (ent, vec3_origin, damage, false, EF_BLASTER);
|
||||||
|
ent->client->ps.gunframe++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Weapon_BoltedBlaster (edict_t *ent)
|
||||||
|
{
|
||||||
|
static int pause_frames[] = {19, 32, 0};
|
||||||
|
static int fire_frames[] = {5, 0};
|
||||||
|
|
||||||
|
Weapon_Generic (ent, 4, 8, 52, 55, pause_frames, fire_frames, Weapon_BoltedBlaster_Fire);
|
||||||
|
}
|
|
@ -0,0 +1,183 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
void Weapon_Generic (edict_t *ent, int FRAME_ACTIVATE_LAST, int FRAME_FIRE_LAST, int FRAME_IDLE_LAST, int FRAME_DEACTIVATE_LAST, int *pause_frames, int *fire_frames, void (*fire)(edict_t *ent));
|
||||||
|
/*
|
||||||
|
=====================================================
|
||||||
|
Computer Guided Projectile Launcher http://haydon.niehs.nih.gov/maplist2.htm
|
||||||
|
=====================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
void FireComputerGuidedProjectile(edict_t *ent,int damage,int kick)
|
||||||
|
{
|
||||||
|
vec3_t forward, right, target, dir,start;
|
||||||
|
vec3_t check;
|
||||||
|
trace_t tr;
|
||||||
|
edict_t *blip, *targ;
|
||||||
|
if(ent->light_level<1)
|
||||||
|
return;
|
||||||
|
ent->light_level--;
|
||||||
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
check[0]=ent->s.origin[0] + forward[0]*750;
|
||||||
|
check[1]=ent->s.origin[1] + forward[1]*750;
|
||||||
|
check[2]=ent->s.origin[2] + forward[2]*750;
|
||||||
|
targ=NULL;
|
||||||
|
blip = NULL;
|
||||||
|
while (blip = findradius (blip, check, 375))
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!(blip->svflags & SVF_MONSTER) && !blip->client)
|
||||||
|
continue;
|
||||||
|
if (blip->health <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, blip->s.origin, ent, MASK_SOLID);
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
continue;
|
||||||
|
targ = blip;
|
||||||
|
}
|
||||||
|
if(!targ)
|
||||||
|
{
|
||||||
|
check[0]=ent->s.origin[0] + forward[0]*500;
|
||||||
|
check[1]=ent->s.origin[1] + forward[1]*500;
|
||||||
|
check[2]=ent->s.origin[2] + forward[2]*500;
|
||||||
|
while (blip = findradius (blip, check, 175))
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!(blip->svflags & SVF_MONSTER) && !blip->client)
|
||||||
|
continue;
|
||||||
|
if (blip->health <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, blip->s.origin, ent, MASK_SOLID);
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
continue;
|
||||||
|
targ = blip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!targ)
|
||||||
|
{
|
||||||
|
check[0]=ent->s.origin[0] + forward[0]*900;
|
||||||
|
check[1]=ent->s.origin[1] + forward[1]*900;
|
||||||
|
check[2]=ent->s.origin[2] + forward[2]*900;
|
||||||
|
while (blip = findradius (blip, check, 250))
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!(blip->svflags & SVF_MONSTER) && !blip->client)
|
||||||
|
continue;
|
||||||
|
if (blip->health <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, blip->s.origin, ent, MASK_SOLID);
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
continue;
|
||||||
|
targ = blip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!targ)
|
||||||
|
{
|
||||||
|
check[0]=ent->s.origin[0] + forward[0]*1050;
|
||||||
|
check[1]=ent->s.origin[1] + forward[1]*1050;
|
||||||
|
check[2]=ent->s.origin[2] + forward[2]*1050;
|
||||||
|
while (blip = findradius (blip, check, 175))
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!(blip->svflags & SVF_MONSTER) && !blip->client)
|
||||||
|
continue;
|
||||||
|
if (blip->health <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, blip->s.origin, ent, MASK_SOLID);
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
continue;
|
||||||
|
targ = blip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!targ)
|
||||||
|
{
|
||||||
|
check[0]=ent->s.origin[0] + forward[0]*1125;
|
||||||
|
check[1]=ent->s.origin[1] + forward[1]*1125;
|
||||||
|
check[2]=ent->s.origin[2] + forward[2]*1125;
|
||||||
|
while (blip = findradius (blip, check, 75))
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!(blip->svflags & SVF_MONSTER) && !blip->client)
|
||||||
|
continue;
|
||||||
|
if (blip->health <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, blip->s.origin, ent, MASK_SOLID);
|
||||||
|
if (tr.fraction != 1.0)
|
||||||
|
continue;
|
||||||
|
targ = blip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(targ)
|
||||||
|
{
|
||||||
|
// calc direction to where we targted
|
||||||
|
VectorMA (targ->s.origin, -0.2, targ->velocity, target);
|
||||||
|
|
||||||
|
//Adjust for height
|
||||||
|
target[2] -= targ->viewheight/1.5;
|
||||||
|
|
||||||
|
VectorSubtract (target, ent->s.origin, dir);
|
||||||
|
VectorNormalize (dir);
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Target Acquired by Computer!\n");
|
||||||
|
|
||||||
|
VectorCopy(dir,forward);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
start[0] = ent->s.origin[0]+forward[0]*3;
|
||||||
|
start[1] = ent->s.origin[1]+forward[1]*3;
|
||||||
|
|
||||||
|
start[2] = ent->s.origin[2]+forward[2]*3+ent->viewheight;
|
||||||
|
//gi.error("JOhn");
|
||||||
|
//fire bullet
|
||||||
|
|
||||||
|
fire_bullet (ent, start, forward, damage, kick, 75, 75,0);
|
||||||
|
|
||||||
|
// send muzzle flash
|
||||||
|
/* gi.WriteByte (svc_muzzleflash);
|
||||||
|
gi.WriteShort (ent-g_edicts);
|
||||||
|
gi.WriteByte (MZ_SHOTGUN);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);*/
|
||||||
|
}
|
||||||
|
|
||||||
|
void weapon_ComputerGuidedProjectileLauncher_fire (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t start;
|
||||||
|
int damage = 8;
|
||||||
|
int kick = 80;
|
||||||
|
if ((ent->client->buttons & BUTTON_ATTACK) && (ent->client->ps.gunframe == 11))
|
||||||
|
{
|
||||||
|
ent->client->ps.gunframe=8;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if (is_quad)
|
||||||
|
{
|
||||||
|
damage *= 4;
|
||||||
|
kick *= 4;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
FireComputerGuidedProjectile(ent, damage, kick);
|
||||||
|
|
||||||
|
|
||||||
|
ent->client->ps.gunframe++;
|
||||||
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
||||||
|
|
||||||
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
||||||
|
ent->client->pers.inventory[ent->client->ammo_index]--;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Weapon_ComputerGuidedProjectileLauncher (edict_t *ent)
|
||||||
|
{
|
||||||
|
static int pause_frames[] = {22, 28, 34, 0};
|
||||||
|
static int fire_frames[] = { 9,11, 0};
|
||||||
|
|
||||||
|
Weapon_Generic (ent, 7, 18, 36, 39, pause_frames, fire_frames, weapon_ComputerGuidedProjectileLauncher_fire);
|
||||||
|
}
|
|
@ -0,0 +1,179 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
/*
|
||||||
|
======================
|
||||||
|
Cluster Missiles
|
||||||
|
======================
|
||||||
|
*/
|
||||||
|
void rocketcluster_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
vec3_t origin;
|
||||||
|
int n;
|
||||||
|
// int i;
|
||||||
|
// float spd;
|
||||||
|
// vec3_t org;
|
||||||
|
|
||||||
|
//Sean added these 4 vectors
|
||||||
|
vec3_t grenade1;
|
||||||
|
vec3_t grenade2;
|
||||||
|
vec3_t grenade3;
|
||||||
|
vec3_t grenade4;
|
||||||
|
|
||||||
|
|
||||||
|
if (other == ent->owner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (surf && (surf->flags & SURF_SKY))
|
||||||
|
{
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent->owner->client)
|
||||||
|
PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
|
||||||
|
|
||||||
|
// calculate position for the explosion entity
|
||||||
|
VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
|
||||||
|
|
||||||
|
if (other->takedamage)
|
||||||
|
{
|
||||||
|
T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0,MOD_CLUSTERROCKET);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// don't throw any debris in net games
|
||||||
|
if (!deathmatch->value)
|
||||||
|
{
|
||||||
|
if ((surf) && !(surf->flags & (SURF_WARP|SURF_TRANS33|SURF_TRANS66|SURF_FLOWING)))
|
||||||
|
{
|
||||||
|
n = rand() % 5;
|
||||||
|
while(n--)
|
||||||
|
ThrowDebris (ent, "models/objects/debris2/tris.md2", 2, ent->s.origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius,MOD_CLUSTERROCKET);
|
||||||
|
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
if (ent->waterlevel)
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
|
||||||
|
else
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION);
|
||||||
|
gi.WritePosition (origin);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
// SumFuka did this bit : give grenades up/outwards velocities
|
||||||
|
VectorSet(grenade1,20,20,40);
|
||||||
|
VectorSet(grenade2,20,-20,40);
|
||||||
|
VectorSet(grenade3,-20,20,40);
|
||||||
|
VectorSet(grenade4,-20,-20,40);
|
||||||
|
|
||||||
|
// Sean : explode the four grenades outwards
|
||||||
|
fire_grenade2(ent->owner, origin, grenade1, DAMAGE_CLUSTERROCKET, 1, 1.0, 120, false);
|
||||||
|
fire_grenade2(ent->owner, origin, grenade2, DAMAGE_CLUSTERROCKET, 1, 1.0, 120, false);
|
||||||
|
fire_grenade2(ent->owner, origin, grenade3, DAMAGE_CLUSTERROCKET, 1, 1.0, 120, false);
|
||||||
|
fire_grenade2(ent->owner, origin, grenade4, DAMAGE_CLUSTERROCKET, 1, 1.0, 120, false);
|
||||||
|
|
||||||
|
// make some glowing shrapnel
|
||||||
|
//spd = 15.0 * ent->dmg / 200;
|
||||||
|
/* for (i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
org[0] = ent->s.origin[0] + crandom() * ent->size[0];
|
||||||
|
org[1] = ent->s.origin[1] + crandom() * ent->size[1];
|
||||||
|
org[2] = ent->s.origin[2] + crandom() * ent->size[2];
|
||||||
|
ThrowShrapnel4 (ent, "models/objects/debris2/tris.md2", spd, org);
|
||||||
|
}
|
||||||
|
make_debris (ent);
|
||||||
|
T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius,MOD_CLUSTERROCKET);
|
||||||
|
T_ShockItems(ent);
|
||||||
|
T_ShockWave(ent, 255, 1024);*/
|
||||||
|
BecomeNewExplosion (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fire_clusterrocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage)
|
||||||
|
{
|
||||||
|
edict_t *rocket;
|
||||||
|
|
||||||
|
rocket = G_Spawn();
|
||||||
|
rocket->wf_team = self->wf_team;
|
||||||
|
|
||||||
|
VectorCopy (start, rocket->s.origin);
|
||||||
|
VectorCopy (dir, rocket->movedir);
|
||||||
|
vectoangles (dir, rocket->s.angles);
|
||||||
|
VectorScale (dir, speed, rocket->velocity);
|
||||||
|
rocket->movetype = MOVETYPE_FLYMISSILE;
|
||||||
|
rocket->clipmask = MASK_SHOT;
|
||||||
|
rocket->solid = SOLID_BBOX;
|
||||||
|
rocket->s.effects |= EF_ROCKET;
|
||||||
|
VectorClear (rocket->mins);
|
||||||
|
VectorClear (rocket->maxs);
|
||||||
|
rocket->s.modelindex = gi.modelindex ("models/objects/rocket/tris.md2");
|
||||||
|
rocket->owner = self;
|
||||||
|
rocket->touch = rocketcluster_touch;
|
||||||
|
rocket->nextthink = level.time + 8000/speed;
|
||||||
|
rocket->think = G_FreeEdict;
|
||||||
|
rocket->dmg = damage;
|
||||||
|
rocket->radius_dmg = radius_damage;
|
||||||
|
rocket->dmg_radius = damage_radius;
|
||||||
|
rocket->s.sound = gi.soundindex ("weapons/rockfly.wav");
|
||||||
|
rocket->classname = "rocket";
|
||||||
|
// CCH: a few more attributes to let the rocket 'die'
|
||||||
|
VectorSet(rocket->mins, -10, -3, 0);
|
||||||
|
VectorSet(rocket->maxs, 10, 3, 6);
|
||||||
|
rocket->mass = 10;+ rocket->health = 10;
|
||||||
|
rocket->die = Rocket_Die;
|
||||||
|
rocket->takedamage = DAMAGE_NO;
|
||||||
|
rocket->monsterinfo.aiflags = AI_NOSTEP;
|
||||||
|
if (self->client)
|
||||||
|
check_dodge (self, rocket->s.origin, dir, speed);
|
||||||
|
|
||||||
|
gi.linkentity (rocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Weapon_RocketClusterLauncher_Fire (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t offset, start;
|
||||||
|
vec3_t forward, right;
|
||||||
|
|
||||||
|
|
||||||
|
int damage;
|
||||||
|
float damage_radius;
|
||||||
|
int radius_damage;
|
||||||
|
|
||||||
|
damage = wf_game.weapon_damage[WEAPON_CLUSTERROCKET];
|
||||||
|
radius_damage = 20;
|
||||||
|
damage_radius = 20;
|
||||||
|
if (is_quad)
|
||||||
|
{
|
||||||
|
damage *= 4;
|
||||||
|
radius_damage *= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
||||||
|
|
||||||
|
VectorScale (forward, -2, ent->client->kick_origin);
|
||||||
|
ent->client->kick_angles[0] = -1;
|
||||||
|
//First rocket
|
||||||
|
VectorSet(offset, 8, 8, ent->viewheight-8);
|
||||||
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
||||||
|
fire_clusterrocket (ent, start, forward, damage, wf_game.weapon_speed[WEAPON_CLUSTERROCKET], damage_radius, radius_damage);
|
||||||
|
|
||||||
|
// send muzzle flash
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
gi.WriteShort (ent-g_edicts);
|
||||||
|
gi.WriteByte (MZ_ROCKET | is_silenced);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
ent->client->ps.gunframe++;
|
||||||
|
|
||||||
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
||||||
|
|
||||||
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
||||||
|
ent->client->pers.inventory[ent->client->ammo_index] -= ent->client->pers.weapon->quantity;
|
||||||
|
}
|
||||||
|
void Weapon_RocketClusterLauncher (edict_t *ent)
|
||||||
|
{
|
||||||
|
static int pause_frames[] = {25, 33, 42, 50, 0};
|
||||||
|
static int fire_frames[] = {5, 0};
|
||||||
|
|
||||||
|
Weapon_Generic (ent, 4, 12, 50, 54, pause_frames, fire_frames, Weapon_RocketClusterLauncher_Fire);
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================
|
||||||
|
Concussion Grenades
|
||||||
|
===========================
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void Concussion_Explode (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t offset,v;
|
||||||
|
edict_t *target;
|
||||||
|
float Distance, DrunkTimeAdd;
|
||||||
|
// Move it off the ground so people are sure to see it
|
||||||
|
VectorSet(offset, 0, 0, 10);
|
||||||
|
VectorAdd(ent->s.origin, offset, ent->s.origin);
|
||||||
|
|
||||||
|
if (ent->owner->client)
|
||||||
|
{
|
||||||
|
PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
|
||||||
|
}
|
||||||
|
|
||||||
|
target = NULL;
|
||||||
|
while ((target = findradius(target, ent->s.origin, 520)) != NULL)
|
||||||
|
{
|
||||||
|
if (!target->client)
|
||||||
|
continue; // It's not a player
|
||||||
|
|
||||||
|
if (target->wf_team == ent->wf_team) //don't attack if on same team
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!visible(ent, target))
|
||||||
|
continue; // The grenade can't see it
|
||||||
|
|
||||||
|
if (target->DrunkTime > level.time)
|
||||||
|
continue; //don't make them more drunk (Gregg)
|
||||||
|
|
||||||
|
// Find distance
|
||||||
|
VectorSubtract(ent->s.origin, target->s.origin, v);
|
||||||
|
Distance = VectorLength(v);
|
||||||
|
// Calculate drunk factor
|
||||||
|
if(Distance < 520/10)
|
||||||
|
DrunkTimeAdd = 20; //completely drunk
|
||||||
|
else
|
||||||
|
DrunkTimeAdd = 1.5 * 20 * ( 1 / ( ( Distance - 520*2 ) / (520*2) - 2 ) + 1 ); //partially drunk
|
||||||
|
if ( DrunkTimeAdd < 0 )
|
||||||
|
DrunkTimeAdd = 0; // Do not make drunk at all.
|
||||||
|
|
||||||
|
//GREGG - decrease drunk time
|
||||||
|
DrunkTimeAdd *= .3;
|
||||||
|
|
||||||
|
// Increment the drunk time
|
||||||
|
if(target->DrunkTime < level.time)
|
||||||
|
target->DrunkTime = DrunkTimeAdd+level.time;
|
||||||
|
else
|
||||||
|
target->DrunkTime += DrunkTimeAdd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blow up the grenade
|
||||||
|
BecomeExplosion1(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Concussion_Die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||||
|
{
|
||||||
|
|
||||||
|
self->takedamage = DAMAGE_NO;
|
||||||
|
self->nextthink = level.time + .1;
|
||||||
|
self->think = Concussion_Explode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Concussion_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
if (other == ent->owner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Dont blow up if on same team
|
||||||
|
if (other->wf_team == ent->wf_team)
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
|
if (surf && (surf->flags & SURF_SKY))
|
||||||
|
{
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!other->takedamage)
|
||||||
|
{
|
||||||
|
if (ent->spawnflags & 1)
|
||||||
|
{
|
||||||
|
if (random() > 0.5)
|
||||||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else
|
||||||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//ent->enemy = other; was causing damage to thrower?
|
||||||
|
Concussion_Explode (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void fire_concussiongrenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
|
||||||
|
{
|
||||||
|
edict_t *grenade;
|
||||||
|
vec3_t dir;
|
||||||
|
vec3_t forward, right, up;
|
||||||
|
|
||||||
|
++self->client->pers.active_grenades[GRENADE_TYPE_CONCUSSION];
|
||||||
|
|
||||||
|
vectoangles (aimdir, dir);
|
||||||
|
AngleVectors (dir, forward, right, up);
|
||||||
|
|
||||||
|
grenade = G_Spawn();
|
||||||
|
grenade->grenade_index = GRENADE_TYPE_CONCUSSION;
|
||||||
|
grenade->wf_team = self->wf_team;
|
||||||
|
|
||||||
|
VectorCopy (start, grenade->s.origin);
|
||||||
|
VectorScale (aimdir, speed, grenade->velocity);
|
||||||
|
VectorMA (grenade->velocity, 200 + crandom() * 30.0, up, grenade->velocity);
|
||||||
|
VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
|
||||||
|
VectorSet (grenade->avelocity, 300, 300, 300);
|
||||||
|
grenade->movetype = MOVETYPE_BOUNCE;
|
||||||
|
grenade->clipmask = MASK_SHOT;
|
||||||
|
grenade->solid = SOLID_BBOX;
|
||||||
|
grenade->s.effects |= EF_GRENADE;
|
||||||
|
VectorClear (grenade->mins);
|
||||||
|
VectorClear (grenade->maxs);
|
||||||
|
grenade->s.modelindex = gi.modelindex (GRCONCUSSION_MODEL);
|
||||||
|
grenade->s.skinnum = GRCONCUSSION_SKIN;
|
||||||
|
grenade->owner = self;
|
||||||
|
grenade->touch = Concussion_Touch;
|
||||||
|
grenade->nextthink = level.time + timer;
|
||||||
|
grenade->think = Concussion_Explode;
|
||||||
|
grenade->dmg = wf_game.grenade_damage[GRENADE_TYPE_CONCUSSION];
|
||||||
|
grenade->dmg_radius = damage_radius;
|
||||||
|
grenade->classname = "concussion";
|
||||||
|
// CCH: a few more attributes to let the grenade 'die'
|
||||||
|
VectorSet(grenade->mins, -3, -3, 0);
|
||||||
|
VectorSet(grenade->maxs, 3, 3, 6);
|
||||||
|
grenade->mass = 2;
|
||||||
|
grenade->health = 10;
|
||||||
|
grenade->die = Concussion_Die;
|
||||||
|
grenade->takedamage = DAMAGE_YES;
|
||||||
|
grenade->monsterinfo.aiflags = AI_NOSTEP;
|
||||||
|
|
||||||
|
gi.linkentity (grenade);
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
/*
|
||||||
|
===============
|
||||||
|
Flamethrower
|
||||||
|
===============
|
||||||
|
*/
|
||||||
|
void flame_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (other == self->owner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!other->takedamage) return;
|
||||||
|
|
||||||
|
// clean up laser entities
|
||||||
|
|
||||||
|
if (surf && (surf->flags & SURF_SKY))
|
||||||
|
{
|
||||||
|
G_FreeEdict (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->owner->client)
|
||||||
|
PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
|
||||||
|
T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, 6, 0, 0,MOD_WF_FLAME);
|
||||||
|
// core explosion - prevents firing it into the wall/floor
|
||||||
|
if (other->health)
|
||||||
|
{
|
||||||
|
burn_person(other, self->owner, self->SniperDamage, MOD_FLAMETHROWER);
|
||||||
|
}
|
||||||
|
G_FreeEdict (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Flame_IncreaseFrame(edict_t *self)
|
||||||
|
{
|
||||||
|
self->s.frame++;
|
||||||
|
if(self->s.frame>4)
|
||||||
|
self->s.frame =0;
|
||||||
|
if(self->delay<level.time)
|
||||||
|
{
|
||||||
|
G_FreeEdict(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self->nextthink=level.time+0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void fire_flamethrower(edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius)
|
||||||
|
{
|
||||||
|
edict_t *flame;
|
||||||
|
|
||||||
|
flame = G_Spawn();
|
||||||
|
flame->wf_team = self->wf_team;
|
||||||
|
VectorCopy (start, flame->s.origin);
|
||||||
|
VectorCopy (dir, flame->movedir);
|
||||||
|
vectoangles (dir, flame->s.angles);
|
||||||
|
VectorScale (dir, speed, flame->velocity);
|
||||||
|
flame->movetype = MOVETYPE_BOUNCE;
|
||||||
|
flame->clipmask = MASK_SHOT;
|
||||||
|
flame->solid = SOLID_BBOX;
|
||||||
|
flame->s.effects |= EF_PLASMA|EF_DOUBLE;//EF_ANIM_ALLFAST|EF_BFG|EF_HYPERBLASTER;//EF_BLASTER|EF_GRENADE;
|
||||||
|
VectorSet (flame->mins,-20,-20,-20);
|
||||||
|
VectorSet (flame->maxs,20,20,20);
|
||||||
|
flame->s.modelindex = gi.modelindex ("models/fire/tris.md2");//("sprites/fire.sp2");
|
||||||
|
flame->gravity=0.2;
|
||||||
|
flame->s.frame=0;
|
||||||
|
flame->owner = self;
|
||||||
|
flame->touch = flame_touch;
|
||||||
|
flame->delay = level.time + 0.8;
|
||||||
|
flame->nextthink=level.time+0.1;
|
||||||
|
flame->think = Flame_IncreaseFrame;
|
||||||
|
//flame->think = G_FreeEdict;
|
||||||
|
flame->radius_dmg = damage;
|
||||||
|
flame->SniperDamage = damage;
|
||||||
|
flame->dmg_radius = damage_radius;
|
||||||
|
flame->classname = "flame";
|
||||||
|
flame->s.sound = gi.soundindex ("weapons/bfg__l1a.wav");
|
||||||
|
|
||||||
|
if (self->client)
|
||||||
|
check_dodge (self, flame->s.origin, dir, speed);
|
||||||
|
|
||||||
|
gi.linkentity (flame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void weapon_flamethrower_fire (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t offset, start;
|
||||||
|
vec3_t forward, right;
|
||||||
|
int damage = wf_game.weapon_damage[WEAPON_FLAMETHROWER] ;
|
||||||
|
float damage_radius = 1200;
|
||||||
|
|
||||||
|
if (!(ent->client->buttons & BUTTON_ATTACK))
|
||||||
|
{
|
||||||
|
ent->client->ps.gunframe = 31;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if(ent->client->ps.gunframe == 30)
|
||||||
|
ent->client->ps.gunframe = 9;
|
||||||
|
|
||||||
|
// send muzzle flash
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
gi.WriteShort (ent-g_edicts);
|
||||||
|
gi.WriteByte (MZ_ROCKET | is_silenced);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
||||||
|
|
||||||
|
if (is_quad)
|
||||||
|
damage *= 4;
|
||||||
|
ent->client->v_angle[2]+=5;
|
||||||
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
||||||
|
ent->client->v_angle[2]-=5;
|
||||||
|
VectorSet(offset, 8, 8, ent->viewheight-8);
|
||||||
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
||||||
|
|
||||||
|
if (ent->waterlevel < 2)
|
||||||
|
{
|
||||||
|
fire_flamethrower (ent, start, forward, wf_game.weapon_damage[WEAPON_FLAMETHROWER], 450 , damage_radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->client->ps.gunframe++;
|
||||||
|
|
||||||
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
||||||
|
|
||||||
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
||||||
|
ent->client->pers.inventory[ent->client->ammo_index] -= ent->client->pers.weapon->quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Weapon_FlameThrower (edict_t *ent)
|
||||||
|
{
|
||||||
|
static int pause_frames[] = {39, 45, 50, 55, 0};
|
||||||
|
static int fire_frames[] = {12, 15, 18, 24, 27, 30, 0};
|
||||||
|
Weapon_Generic (ent, 8, 32, 55, 58, pause_frames, fire_frames, weapon_flamethrower_fire);
|
||||||
|
}
|
|
@ -0,0 +1,278 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
void stick(edict_t *projectile, edict_t *object);
|
||||||
|
|
||||||
|
/*
|
||||||
|
=======================
|
||||||
|
Flares(light up stuff)
|
||||||
|
=======================
|
||||||
|
*/
|
||||||
|
void Flare_Explode (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t origin;
|
||||||
|
|
||||||
|
if (ent->owner->client)
|
||||||
|
PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
|
||||||
|
|
||||||
|
VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
if (ent->waterlevel)
|
||||||
|
{
|
||||||
|
if (ent->groundentity)
|
||||||
|
gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
|
||||||
|
else
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ent->groundentity)
|
||||||
|
gi.WriteByte (TE_GRENADE_EXPLOSION);
|
||||||
|
else
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION);
|
||||||
|
}
|
||||||
|
gi.WritePosition (origin);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PHS);
|
||||||
|
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Flare_Die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||||
|
{
|
||||||
|
self->takedamage = DAMAGE_NO;
|
||||||
|
self->nextthink = level.time + .1;
|
||||||
|
self->think = Flare_Explode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Flare_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
if (other == ent->owner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (surf && (surf->flags & SURF_SKY))
|
||||||
|
{
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!other->takedamage)
|
||||||
|
{
|
||||||
|
if (ent->spawnflags & 1)
|
||||||
|
{
|
||||||
|
if (random() > 0.5)
|
||||||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else
|
||||||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//ent->enemy = other;
|
||||||
|
Flare_Explode (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fire_flare (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
|
||||||
|
{
|
||||||
|
edict_t *grenade;
|
||||||
|
vec3_t dir;
|
||||||
|
vec3_t forward, right, up;
|
||||||
|
|
||||||
|
vectoangles (aimdir, dir);
|
||||||
|
AngleVectors (dir, forward, right, up);
|
||||||
|
|
||||||
|
grenade = G_Spawn();
|
||||||
|
grenade->wf_team = self->wf_team;
|
||||||
|
VectorCopy (start, grenade->s.origin);
|
||||||
|
VectorScale (aimdir, speed, grenade->velocity);
|
||||||
|
VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
|
||||||
|
VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
|
||||||
|
VectorSet (grenade->avelocity, 300, 300, 300);
|
||||||
|
grenade->movetype = MOVETYPE_BOUNCE;
|
||||||
|
grenade->clipmask = MASK_SHOT;
|
||||||
|
grenade->solid = SOLID_BBOX;
|
||||||
|
grenade->s.effects |= EF_BLASTER|EF_BFG|EF_TELEPORTER;
|
||||||
|
grenade->s.renderfx |= RF_SHELL_GREEN;
|
||||||
|
VectorClear (grenade->mins);
|
||||||
|
VectorClear (grenade->maxs);
|
||||||
|
grenade->s.modelindex = gi.modelindex (GRFLARE_MODEL);
|
||||||
|
grenade->s.skinnum = GRFLARE_SKIN;
|
||||||
|
grenade->owner = self;
|
||||||
|
grenade->touch = Flare_Touch;
|
||||||
|
grenade->nextthink = level.time + timer + 35;
|
||||||
|
grenade->think = Flare_Explode;
|
||||||
|
grenade->dmg = damage;
|
||||||
|
grenade->dmg_radius = damage_radius;
|
||||||
|
grenade->classname = "grenade";
|
||||||
|
// CCH: a few more attributes to let the grenade 'die'
|
||||||
|
VectorSet(grenade->mins, -3, -3, 0);
|
||||||
|
VectorSet(grenade->maxs, 3, 3, 6);
|
||||||
|
grenade->mass = 2;
|
||||||
|
grenade->health = 10;
|
||||||
|
grenade->die = Flare_Die;
|
||||||
|
grenade->takedamage = DAMAGE_YES;
|
||||||
|
grenade->monsterinfo.aiflags = AI_NOSTEP;
|
||||||
|
|
||||||
|
gi.linkentity (grenade);
|
||||||
|
}
|
||||||
|
|
||||||
|
void weapon_flarelauncher_fire (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t offset;
|
||||||
|
vec3_t forward, right;
|
||||||
|
vec3_t start;
|
||||||
|
int damage = 12;
|
||||||
|
float radius;
|
||||||
|
|
||||||
|
radius = damage+40;
|
||||||
|
if (is_quad)
|
||||||
|
damage *= 4;
|
||||||
|
|
||||||
|
VectorSet(offset, 8, 8, ent->viewheight-8);
|
||||||
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
||||||
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
||||||
|
|
||||||
|
VectorScale (forward, -2, ent->client->kick_origin);
|
||||||
|
ent->client->kick_angles[0] = -1;
|
||||||
|
|
||||||
|
fire_flare (ent, start, forward, damage, 400, 2.5, radius);
|
||||||
|
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
gi.WriteShort (ent-g_edicts);
|
||||||
|
gi.WriteByte (MZ_GRENADE | is_silenced);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
ent->client->ps.gunframe++;
|
||||||
|
|
||||||
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
||||||
|
|
||||||
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
||||||
|
ent->client->pers.inventory[ent->client->ammo_index] -= ent->client->pers.weapon->quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Weapon_FlareLauncher (edict_t *ent)
|
||||||
|
{
|
||||||
|
static int pause_frames[] = {34, 51, 59, 0};
|
||||||
|
static int fire_frames[] = {6, 0};
|
||||||
|
|
||||||
|
Weapon_Generic (ent, 5, 16, 59, 64, pause_frames, fire_frames, weapon_flarelauncher_fire);
|
||||||
|
}
|
||||||
|
void Sticky_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
//gi.dprintf("Cluster: Touch\n");
|
||||||
|
|
||||||
|
if (other == ent->owner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Dont blow up if on same team
|
||||||
|
//if (other->wf_team == ent->wf_team)
|
||||||
|
// return;
|
||||||
|
|
||||||
|
if (surf && (surf->flags & SURF_SKY))
|
||||||
|
{
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!other->takedamage)
|
||||||
|
{
|
||||||
|
if (ent->spawnflags & 1)
|
||||||
|
{
|
||||||
|
if (random() > 0.5)
|
||||||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else
|
||||||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stick(ent, other);
|
||||||
|
|
||||||
|
//Cluster_Explode (ent);
|
||||||
|
}
|
||||||
|
void fire_Stickyflare (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
|
||||||
|
{
|
||||||
|
edict_t *grenade;
|
||||||
|
vec3_t dir;
|
||||||
|
vec3_t forward, right, up;
|
||||||
|
|
||||||
|
vectoangles (aimdir, dir);
|
||||||
|
AngleVectors (dir, forward, right, up);
|
||||||
|
|
||||||
|
grenade = G_Spawn();
|
||||||
|
VectorCopy (start, grenade->s.origin);
|
||||||
|
VectorScale (aimdir, speed, grenade->velocity);
|
||||||
|
VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
|
||||||
|
VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
|
||||||
|
VectorSet (grenade->avelocity, 300, 300, 300);
|
||||||
|
grenade->movetype = MOVETYPE_BOUNCE;
|
||||||
|
grenade->clipmask = MASK_SHOT;
|
||||||
|
grenade->solid = SOLID_BBOX;
|
||||||
|
grenade->s.effects |= EF_BLASTER|EF_BFG|EF_TELEPORTER;
|
||||||
|
grenade->s.renderfx |= RF_SHELL_GREEN;
|
||||||
|
VectorClear (grenade->mins);
|
||||||
|
VectorClear (grenade->maxs);
|
||||||
|
grenade->s.modelindex = gi.modelindex ("models/objects/grenade/tris.md2");
|
||||||
|
grenade->owner = self;
|
||||||
|
grenade->touch = Sticky_Touch;
|
||||||
|
grenade->nextthink = level.time + timer + 35;
|
||||||
|
grenade->think = Flare_Explode;
|
||||||
|
grenade->dmg = damage;
|
||||||
|
grenade->dmg_radius = damage_radius;
|
||||||
|
grenade->classname = "grenade";
|
||||||
|
// CCH: a few more attributes to let the grenade 'die'
|
||||||
|
VectorSet(grenade->mins, -3, -3, 0);
|
||||||
|
VectorSet(grenade->maxs, 3, 3, 6);
|
||||||
|
grenade->mass = 2;
|
||||||
|
grenade->health = 10;
|
||||||
|
grenade->die = Flare_Die;
|
||||||
|
grenade->takedamage = DAMAGE_YES;
|
||||||
|
grenade->monsterinfo.aiflags = AI_NOSTEP;
|
||||||
|
|
||||||
|
gi.linkentity (grenade);
|
||||||
|
}
|
||||||
|
|
||||||
|
void weapon_stickyflarelauncher_fire (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t offset;
|
||||||
|
vec3_t forward, right;
|
||||||
|
vec3_t start;
|
||||||
|
int damage = 12;
|
||||||
|
float radius;
|
||||||
|
|
||||||
|
radius = damage+40;
|
||||||
|
if (is_quad)
|
||||||
|
damage *= 4;
|
||||||
|
|
||||||
|
VectorSet(offset, 8, 8, ent->viewheight-8);
|
||||||
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
||||||
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
||||||
|
|
||||||
|
VectorScale (forward, -2, ent->client->kick_origin);
|
||||||
|
ent->client->kick_angles[0] = -1;
|
||||||
|
|
||||||
|
fire_Stickyflare (ent, start, forward, damage, 400, 2.5, radius);
|
||||||
|
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
gi.WriteShort (ent-g_edicts);
|
||||||
|
gi.WriteByte (MZ_GRENADE | is_silenced);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
ent->client->ps.gunframe++;
|
||||||
|
|
||||||
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
||||||
|
|
||||||
|
ent->client->pers.inventory[ent->client->ammo_index] -= ent->client->pers.weapon->quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Weapon_StickyFlareLauncher (edict_t *ent)
|
||||||
|
{
|
||||||
|
static int pause_frames[] = {34, 51, 59, 0};
|
||||||
|
static int fire_frames[] = {6, 0};
|
||||||
|
|
||||||
|
Weapon_Generic (ent, 5, 16, 59, 64, pause_frames, fire_frames, weapon_stickyflarelauncher_fire);
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
void stick(edict_t *projectile, edict_t *object);
|
||||||
|
/*Shelton: Enemy disguised spies are purposely not safe from fire damage. Flare ammo cost
|
||||||
|
not changed, but should be, also, flare limit not invoked, but should be discussed (Im thinking
|
||||||
|
3 or 4). Flares last roughly 5 seconds from fire time to burnout. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
================
|
||||||
|
Flare Gun
|
||||||
|
================
|
||||||
|
*/
|
||||||
|
|
||||||
|
void flare2_explode (edict_t *ent)
|
||||||
|
{
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
//Shelton: remove flare entity, no explosion, just burn out.
|
||||||
|
}
|
||||||
|
|
||||||
|
void flaregun2_sticky_touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
//Shelton: Sticky code taken from Snipers Flare.
|
||||||
|
|
||||||
|
if (other == ent->owner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (other->wf_team == ent->wf_team)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (surf && (surf->flags & SURF_SKY))
|
||||||
|
{
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Shelton: Flare set to bounce off of owner & team. Remove if hits sky.
|
||||||
|
|
||||||
|
stick(ent, other);
|
||||||
|
//Shelton: otherwise stick it.
|
||||||
|
|
||||||
|
ent->s.effects |= EF_TELEPORTER;
|
||||||
|
|
||||||
|
if (other->takedamage)
|
||||||
|
{
|
||||||
|
burn_person(other, ent->owner, 10, MOD_FLAREGUN);
|
||||||
|
ent->think = flare2_explode;
|
||||||
|
}
|
||||||
|
/*Shelton: Direct hit on enemy, light em up. Flare continues to be stuck in victim
|
||||||
|
until nextthink occurs. During that time, other enemies may be burned too */
|
||||||
|
|
||||||
|
if (!other->takedamage)
|
||||||
|
{
|
||||||
|
if (ent->spawnflags & 1)
|
||||||
|
{
|
||||||
|
if (random() > 0.5)
|
||||||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else
|
||||||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//Shelton: stick to wall, play sound.. etc.
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mod_Flare2Think(edict_t *ent)
|
||||||
|
{
|
||||||
|
edict_t *blip = NULL;
|
||||||
|
|
||||||
|
if(ent->delay < level.time)
|
||||||
|
{
|
||||||
|
ent->think = flare2_explode;
|
||||||
|
}
|
||||||
|
//Shelton: flare timeout occurs, destroy flare.
|
||||||
|
while ((blip = findradius(blip, ent->s.origin, 100)) != NULL)
|
||||||
|
{
|
||||||
|
if (!(blip->svflags & SVF_MONSTER) && !blip->client)
|
||||||
|
continue;
|
||||||
|
if (!visible(ent, blip))
|
||||||
|
continue;
|
||||||
|
if (blip->health <= 0)
|
||||||
|
continue;
|
||||||
|
if (!blip->takedamage)
|
||||||
|
continue;
|
||||||
|
if (blip->wf_team != ent->wf_team)
|
||||||
|
burn_person(blip, ent->owner, 3, MOD_FLAREGUN);
|
||||||
|
continue;
|
||||||
|
if (blip->wf_team == ent->wf_team)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//Shelton: blip for enemies to burn in radius, ignore team. Burn spies.
|
||||||
|
|
||||||
|
ent->nextthink = level.time + .1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fire_burningflare2(edict_t *self, vec3_t start, vec3_t dir, vec3_t los, int damage, int speed,
|
||||||
|
float damage_radius, int radius_damage)
|
||||||
|
{
|
||||||
|
edict_t *rocket;
|
||||||
|
|
||||||
|
rocket = G_Spawn();
|
||||||
|
|
||||||
|
VectorCopy (los, rocket->pos1); /*Added: Save the line of sight of the center rocket. This is
|
||||||
|
the axis around which the other two rockets rotate*/
|
||||||
|
VectorCopy (start, rocket->pos2); /*Added: Save the start position of the rocket (Not sure if
|
||||||
|
this is necessary. This info might already be somewhere else)?*/
|
||||||
|
|
||||||
|
VectorCopy (start, rocket->s.origin);
|
||||||
|
VectorCopy (dir, rocket->movedir);
|
||||||
|
vectoangles (dir, rocket->s.angles);
|
||||||
|
VectorScale (dir, speed, rocket->velocity);
|
||||||
|
rocket->movetype = MOVETYPE_TOSS;
|
||||||
|
rocket->clipmask = MASK_SHOT;
|
||||||
|
rocket->solid = SOLID_BBOX;
|
||||||
|
rocket->s.effects |= EF_ROCKET;
|
||||||
|
VectorClear (rocket->mins);
|
||||||
|
VectorClear (rocket->maxs);
|
||||||
|
rocket->s.modelindex = gi.modelindex ("models/objects/flarep/tris.md2");
|
||||||
|
rocket->owner = self;
|
||||||
|
rocket->touch = flaregun2_sticky_touch;
|
||||||
|
rocket->gravity = .4;
|
||||||
|
rocket->nextthink = level.time + 3;
|
||||||
|
rocket->think = mod_Flare2Think;
|
||||||
|
rocket->dmg = 0;
|
||||||
|
rocket->delay = level.time + 5;
|
||||||
|
rocket->radius_dmg = 0;
|
||||||
|
rocket->dmg_radius = 0;
|
||||||
|
rocket->s.sound = gi.soundindex ("weapons/rockfly.wav");
|
||||||
|
rocket->wf_team = self->wf_team;
|
||||||
|
if (self->client)
|
||||||
|
check_dodge (self, rocket->s.origin, dir, speed);
|
||||||
|
|
||||||
|
gi.linkentity (rocket);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Shelton: rocket settings slighly altered. Old flare firing method changed to straight shot.
|
||||||
|
However, it's current trajectory is a bit off, and I don't know how to fix that */
|
||||||
|
|
||||||
|
void Weapon_FlareGun2_Fire (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t offset, start;
|
||||||
|
vec3_t forward, right;
|
||||||
|
vec3_t traj_angle, lineofsight; /*Added: Trajectory angle of rocket and line of sight for the center rocket*/
|
||||||
|
int startspeed; /*Added: Start speed of the rockets*/
|
||||||
|
|
||||||
|
int damage;
|
||||||
|
float damage_radius;
|
||||||
|
int radius_damage;
|
||||||
|
|
||||||
|
startspeed = wf_game.weapon_speed[WEAPON_FLAREGUN];
|
||||||
|
|
||||||
|
damage = 0;
|
||||||
|
radius_damage = 0;
|
||||||
|
damage_radius = 0;
|
||||||
|
|
||||||
|
//Shelton: removed direct damage. All damage comes from burn_person().
|
||||||
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
||||||
|
VectorScale (forward, 0, ent->client->kick_origin);
|
||||||
|
ent->client->kick_angles[0] = -1;
|
||||||
|
|
||||||
|
/*Added: Start of modified launcher code*/
|
||||||
|
VectorCopy(forward, lineofsight); /*our line of sight is the same as our forward view vector*/
|
||||||
|
VectorCopy(ent->client->v_angle, traj_angle); /*get the trajectory angles for later modification*/
|
||||||
|
/*This is the original launch code*/
|
||||||
|
VectorSet(offset, 8, 8, ent->viewheight-8);
|
||||||
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
||||||
|
/*Added: Now, modify the trajectory of the second rocket to 5 degrees off of the center axis*/
|
||||||
|
traj_angle[1] = ent->client->v_angle[1]+5;
|
||||||
|
AngleVectors (traj_angle, forward, right, NULL);
|
||||||
|
|
||||||
|
VectorSet(offset, 8, 8, ent->viewheight-8);
|
||||||
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
||||||
|
fire_burningflare2 (ent, start, forward, lineofsight, damage, startspeed, damage_radius, radius_damage);
|
||||||
|
|
||||||
|
// send muzzle flash
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
gi.WriteShort (ent-g_edicts);
|
||||||
|
gi.WriteByte (MZ_ROCKET | is_silenced);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
ent->client->ps.gunframe++;
|
||||||
|
// ### Hentai ### BEGIN
|
||||||
|
/*
|
||||||
|
ent->client->anim_priority = ANIM_ATTACK;
|
||||||
|
if (ent->client->ps.pmove.pm_flags & PMF_DUCKED)
|
||||||
|
{
|
||||||
|
ent->s.frame = FRAME_crattak1 - 1;
|
||||||
|
ent->client->anim_end = FRAME_crattak9;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ent->s.frame = FRAME_attack1 - 1;
|
||||||
|
ent->client->anim_end = FRAME_attack8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ### Hentai ### END
|
||||||
|
*/
|
||||||
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
||||||
|
|
||||||
|
//Reduce ammo
|
||||||
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
||||||
|
ent->client->pers.inventory[ent->client->ammo_index] -= 6;
|
||||||
|
}
|
||||||
|
void Weapon_FlareGun2 (edict_t *ent)
|
||||||
|
{
|
||||||
|
static int pause_frames[] = {9,32, 0};
|
||||||
|
static int fire_frames[] = {9, 0};
|
||||||
|
|
||||||
|
Weapon_Generic (ent,5, 9, 31, 36, pause_frames, fire_frames, Weapon_FlareGun2_Fire);
|
||||||
|
}
|
|
@ -0,0 +1,277 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
===========================
|
||||||
|
Gas Grenades
|
||||||
|
===========================
|
||||||
|
*/
|
||||||
|
|
||||||
|
void Gas_Explode (edict_t *ent);
|
||||||
|
|
||||||
|
void Gas_Die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||||
|
{
|
||||||
|
self->takedamage = DAMAGE_NO;
|
||||||
|
self->nextthink = level.time + .1;
|
||||||
|
self->think = Gas_Explode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gas_Explode (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t origin,v;
|
||||||
|
edict_t *target;
|
||||||
|
float Distance, DrunkTimeAdd;
|
||||||
|
|
||||||
|
if (ent->owner->client)
|
||||||
|
{
|
||||||
|
PlayerNoise(ent->owner, ent->s.origin, PNOISE_IMPACT);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
if (ent->waterlevel)
|
||||||
|
{
|
||||||
|
if (ent->groundentity)
|
||||||
|
gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
|
||||||
|
else
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ent->groundentity)
|
||||||
|
gi.WriteByte (TE_GRENADE_EXPLOSION);
|
||||||
|
else
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION);
|
||||||
|
}
|
||||||
|
gi.WritePosition (origin);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
target = NULL;
|
||||||
|
while ((target = findradius(target, ent->s.origin, 80)) != NULL)
|
||||||
|
{
|
||||||
|
if (!target->client)
|
||||||
|
continue; // It's not a player
|
||||||
|
|
||||||
|
if (target->wf_team == ent->wf_team) //don't attack if on same team
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!visible(ent, target))
|
||||||
|
continue; // The grenade can't see it
|
||||||
|
|
||||||
|
// Find distance
|
||||||
|
VectorSubtract(ent->s.origin, target->s.origin, v);
|
||||||
|
Distance = VectorLength(v);
|
||||||
|
// Calculate drunk factor
|
||||||
|
if(Distance < 80/10)
|
||||||
|
DrunkTimeAdd = 10; //completely drunk
|
||||||
|
else
|
||||||
|
DrunkTimeAdd = 1.5 * 10 * ( 1 / (( Distance - 80 ) / (80*2) - 2 ) + 2 ); //partially drunk
|
||||||
|
if ( DrunkTimeAdd < 0 )
|
||||||
|
DrunkTimeAdd = 0; // Do not make drunk at all.
|
||||||
|
|
||||||
|
|
||||||
|
// Increment the drunk time
|
||||||
|
if(target->client->Gas_Time < level.time)
|
||||||
|
target->client->Gas_Time = DrunkTimeAdd+level.time;
|
||||||
|
else
|
||||||
|
target->client->Gas_Time += DrunkTimeAdd/3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// shake view
|
||||||
|
T_ShockWave(ent, 255, 1024);
|
||||||
|
// explode and destroy grenade
|
||||||
|
BecomeNewExplosion (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Gas_think(edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t v;
|
||||||
|
edict_t *target;
|
||||||
|
float Distance,DrunkTimeAdd;
|
||||||
|
vec3_t forward, right, up;
|
||||||
|
ent->s.angles[0]+=10*random()-5;
|
||||||
|
ent->s.angles[1]+=10*random()-5;
|
||||||
|
ent->s.angles[2]+=10*random()-5;
|
||||||
|
AngleVectors (ent->s.angles, forward, right, up);
|
||||||
|
ent->movetype = MOVETYPE_FLYRICOCHET;
|
||||||
|
VectorSet (ent->avelocity, 150, 150, 150);
|
||||||
|
//VectorMA (ent->velocity, 200 + crandom() * 10.0, up, ent->velocity);
|
||||||
|
//VectorMA (ent->velocity, crandom() * 10.0, right, ent->velocity);
|
||||||
|
|
||||||
|
target = NULL;
|
||||||
|
while ((target = findradius(target, ent->s.origin, 220)) != NULL)
|
||||||
|
{
|
||||||
|
if (!target->client)
|
||||||
|
continue; // It's not a player
|
||||||
|
|
||||||
|
if (target->wf_team == ent->wf_team) //don't attack if on same team
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!visible(ent, target))
|
||||||
|
continue; // The grenade can't see it
|
||||||
|
|
||||||
|
|
||||||
|
// Find distance
|
||||||
|
VectorSubtract(ent->s.origin, target->s.origin, v);
|
||||||
|
Distance = VectorLength(v);
|
||||||
|
// Calculate drunk factor
|
||||||
|
if(Distance < 220/10)
|
||||||
|
DrunkTimeAdd = 10; //completely drunk
|
||||||
|
else
|
||||||
|
DrunkTimeAdd = 1.5 * 10 * ( 1 / (( Distance - 220 ) / (220*2) - 2 ) + 2 ); //partially drunk
|
||||||
|
if ( DrunkTimeAdd < 0 )
|
||||||
|
DrunkTimeAdd = 0; // Do not make drunk at all.
|
||||||
|
if ( DrunkTimeAdd > 10 )
|
||||||
|
DrunkTimeAdd = 10; // Do not make drunk at all.
|
||||||
|
|
||||||
|
|
||||||
|
// Increment the drunk time
|
||||||
|
if(target->client->Gas_Time < level.time)
|
||||||
|
target->client->Gas_Time = DrunkTimeAdd+level.time;
|
||||||
|
else
|
||||||
|
target->client->Gas_Time += DrunkTimeAdd/3;
|
||||||
|
}
|
||||||
|
//Spray out some sparks at random
|
||||||
|
if(random()<0.30)
|
||||||
|
{
|
||||||
|
//Gregg add in some type of air sound to be played
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_SHIELD_SPARKS);
|
||||||
|
gi.WritePosition (ent->s.origin);
|
||||||
|
gi.WriteDir (ent->s.angles);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
//The gas coming out pushes the grenade around
|
||||||
|
VectorScale (forward, 50+((int)(random()*1000)%150), ent->velocity);
|
||||||
|
}
|
||||||
|
//Some more sparks
|
||||||
|
if(random()>0.25)
|
||||||
|
{
|
||||||
|
//Gregg add in some type of air sound to be played
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_SCREEN_SPARKS);
|
||||||
|
gi.WritePosition (ent->s.origin);
|
||||||
|
gi.WriteDir (ent->s.angles);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
//The gas coming out pushes the grenade around
|
||||||
|
VectorScale (forward, 40+((int)(random()*1000)%160), ent->velocity);
|
||||||
|
}
|
||||||
|
//Some really wierd sparks but sprays out a lot more of the plague
|
||||||
|
if(random()<0.15)
|
||||||
|
{
|
||||||
|
//Gregg add in some type of air sound to be played
|
||||||
|
/*gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_BOSSTPORT);
|
||||||
|
gi.WritePosition (ent->s.origin);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
*/
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_WIDOWBEAMOUT);
|
||||||
|
gi.WriteShort (2000);
|
||||||
|
gi.WritePosition (ent->s.origin);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PHS);
|
||||||
|
target = NULL;
|
||||||
|
while ((target = findradius(target, ent->s.origin, 80)) != NULL)
|
||||||
|
{
|
||||||
|
if (!target->client)
|
||||||
|
continue; // It's not a player
|
||||||
|
|
||||||
|
if (target->wf_team == ent->wf_team) //don't attack if on same team
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!visible(ent, target))
|
||||||
|
continue; // The grenade can't see it
|
||||||
|
|
||||||
|
|
||||||
|
// Find distance
|
||||||
|
VectorSubtract(ent->s.origin, target->s.origin, v);
|
||||||
|
Distance = VectorLength(v);
|
||||||
|
// Calculate drunk factor
|
||||||
|
if(Distance < 80/10)
|
||||||
|
DrunkTimeAdd = 10; //completely drunk
|
||||||
|
else
|
||||||
|
DrunkTimeAdd = 1.5 * 10 * ( 1 / (( Distance - 80 ) / (80*2) - 2 ) + 2 ); //partially drunk
|
||||||
|
if ( DrunkTimeAdd < 0 )
|
||||||
|
DrunkTimeAdd = 0; // Do not make drunk at all.
|
||||||
|
|
||||||
|
|
||||||
|
// Increment the drunk time
|
||||||
|
if(target->client->Gas_Time < level.time)
|
||||||
|
target->client->Gas_Time = DrunkTimeAdd+level.time;
|
||||||
|
else
|
||||||
|
target->client->Gas_Time += DrunkTimeAdd/3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ent->delay<level.time)
|
||||||
|
ent->think=Gas_Explode;
|
||||||
|
ent->nextthink=level.time + 0.5;
|
||||||
|
}
|
||||||
|
void Gas_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
if (other == ent->owner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Dont blow up if on same team
|
||||||
|
if (other->wf_team == ent->wf_team)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (surf && (surf->flags & SURF_SKY))
|
||||||
|
{
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (other->takedamage)
|
||||||
|
{
|
||||||
|
//ent->enemy = other;
|
||||||
|
Gas_Explode(ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void fire_gasgrenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius)
|
||||||
|
{
|
||||||
|
edict_t *grenade;
|
||||||
|
vec3_t dir;
|
||||||
|
vec3_t forward, right, up;
|
||||||
|
|
||||||
|
++self->client->pers.active_grenades[GRENADE_TYPE_GAS];
|
||||||
|
|
||||||
|
vectoangles (aimdir, dir);
|
||||||
|
AngleVectors (dir, forward, right, up);
|
||||||
|
|
||||||
|
grenade = G_Spawn();
|
||||||
|
grenade->grenade_index = GRENADE_TYPE_GAS;
|
||||||
|
grenade->wf_team = self->wf_team;
|
||||||
|
VectorCopy (start, grenade->s.origin);
|
||||||
|
VectorScale (aimdir, speed, grenade->velocity);
|
||||||
|
VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
|
||||||
|
VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
|
||||||
|
VectorSet (grenade->avelocity, 300, 300, 300);
|
||||||
|
grenade->movetype = MOVETYPE_FLYRICOCHET;
|
||||||
|
grenade->clipmask = MASK_SHOT;
|
||||||
|
grenade->solid = SOLID_BBOX;
|
||||||
|
grenade->s.effects |= EF_GRENADE;
|
||||||
|
VectorClear (grenade->mins);
|
||||||
|
VectorClear (grenade->maxs);
|
||||||
|
grenade->s.modelindex = gi.modelindex ("models/objects/grenade/tris.md2");
|
||||||
|
grenade->owner = self;
|
||||||
|
grenade->touch = Gas_Touch;
|
||||||
|
grenade->nextthink = level.time + 1;
|
||||||
|
grenade->delay = level.time + 10;
|
||||||
|
grenade->think = Gas_think;
|
||||||
|
grenade->dmg = (damage/3);
|
||||||
|
grenade->dmg_radius = damage_radius;
|
||||||
|
grenade->classname = "gas";
|
||||||
|
// CCH: a few more attributes to let the grenade 'die'
|
||||||
|
VectorSet(grenade->mins, -3, -3, 0);
|
||||||
|
VectorSet(grenade->maxs, 3, 3, 6);
|
||||||
|
grenade->mass = 2;
|
||||||
|
grenade->health = 10;
|
||||||
|
grenade->die = Gas_Die;
|
||||||
|
grenade->takedamage = DAMAGE_YES;
|
||||||
|
grenade->monsterinfo.aiflags = AI_NOSTEP;
|
||||||
|
|
||||||
|
gi.linkentity (grenade);
|
||||||
|
}
|
|
@ -0,0 +1,319 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
void Remove_Player_Flames (edict_t *ent);
|
||||||
|
void HealPlayer(edict_t *ent);
|
||||||
|
|
||||||
|
void VectorRotate(vec3_t in, vec3_t angles, vec3_t out)
|
||||||
|
{
|
||||||
|
float cv, sv, angle, tv;
|
||||||
|
|
||||||
|
VectorCopy(in, out);
|
||||||
|
|
||||||
|
angle = (-angles[PITCH]) * M_PI / 180;
|
||||||
|
cv = cos(angle);
|
||||||
|
sv = sin(angle);
|
||||||
|
tv = (out[0] * cv) - (out[2] * sv);
|
||||||
|
out[2] = (out[2] * cv) + (out[0] * sv);
|
||||||
|
out[0] = tv;
|
||||||
|
|
||||||
|
angle = (angles[YAW]) * M_PI / 180;
|
||||||
|
cv = cos(angle);
|
||||||
|
sv = sin(angle);
|
||||||
|
tv = (out[0] * cv) - (out[1] * sv);
|
||||||
|
out[1] = (out[1] * cv) + (out[0] * sv);
|
||||||
|
out[0] = tv;
|
||||||
|
|
||||||
|
angle = (angles[ROLL]) * M_PI / 180;
|
||||||
|
cv = cos(angle);
|
||||||
|
sv = sin(angle);
|
||||||
|
tv = (out[1] * cv) - (out[2] * sv);
|
||||||
|
out[2] = (out[2] * cv) + (out[1] * sv);
|
||||||
|
out[1] = tv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VectorUnrotate(vec3_t in, vec3_t angles, vec3_t out) {
|
||||||
|
float cv, sv, angle, tv;
|
||||||
|
|
||||||
|
VectorCopy(in, out);
|
||||||
|
|
||||||
|
angle = (-angles[ROLL]) * M_PI / 180;
|
||||||
|
cv = cos(angle);
|
||||||
|
sv = sin(angle);
|
||||||
|
tv = (out[1] * cv) - (out[2] * sv);
|
||||||
|
out[2] = (out[2] * cv) + (out[1] * sv);
|
||||||
|
out[1] = tv;
|
||||||
|
|
||||||
|
angle = (-angles[YAW]) * M_PI / 180;
|
||||||
|
cv = cos(angle);
|
||||||
|
sv = sin(angle);
|
||||||
|
tv = (out[0] * cv) - (out[1] * sv);
|
||||||
|
out[1] = (out[1] * cv) + (out[0] * sv);
|
||||||
|
out[0] = tv;
|
||||||
|
|
||||||
|
angle = (angles[PITCH]) * M_PI / 180;
|
||||||
|
cv = cos(angle);
|
||||||
|
sv = sin(angle);
|
||||||
|
tv = (out[0] * cv) - (out[2] * sv);
|
||||||
|
out[2] = (out[2] * cv) + (out[0] * sv);
|
||||||
|
out[0] = tv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stuck_prethink (edict_t *self)
|
||||||
|
{
|
||||||
|
vec3_t temp, new;
|
||||||
|
edict_t *other;
|
||||||
|
|
||||||
|
other = self->goalentity;
|
||||||
|
|
||||||
|
if (!other->inuse) {
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorRotate(self->pos1, other->s.angles, temp);
|
||||||
|
VectorRotate(self->pos2, other->s.angles, new);
|
||||||
|
|
||||||
|
VectorAdd(other->s.origin, temp, self->s.origin);
|
||||||
|
VectorSubtract(new, temp, new);
|
||||||
|
vectoangles(new, self->s.angles);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Calc_StuckOffset(edict_t *self, edict_t *other)
|
||||||
|
{
|
||||||
|
vec3_t forward;
|
||||||
|
|
||||||
|
VectorSubtract(self->s.origin, other->s.origin, forward);
|
||||||
|
VectorUnrotate(forward, other->s.angles, self->pos1);
|
||||||
|
|
||||||
|
AngleVectors(self->s.angles, forward, NULL, NULL);
|
||||||
|
VectorMA(self->s.origin, 64, forward, forward);
|
||||||
|
VectorSubtract(forward, other->s.origin, forward);
|
||||||
|
VectorUnrotate(forward, other->s.angles, self->pos2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void stick(edict_t *projectile, edict_t *object)
|
||||||
|
{
|
||||||
|
projectile->solid = SOLID_NOT;
|
||||||
|
projectile->movetype = MOVETYPE_FLY;
|
||||||
|
VectorClear(projectile->velocity);
|
||||||
|
VectorClear(projectile->avelocity);
|
||||||
|
|
||||||
|
if (object != g_edicts) {
|
||||||
|
Calc_StuckOffset(projectile, object);
|
||||||
|
projectile->goalentity = object;
|
||||||
|
projectile->prethink = stuck_prethink;
|
||||||
|
} else
|
||||||
|
projectile->prethink = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
=================
|
||||||
|
dart_prethink
|
||||||
|
|
||||||
|
This is a support routine for keeping an
|
||||||
|
object velocity-aligned, for I.E. arrows.
|
||||||
|
=================
|
||||||
|
*/
|
||||||
|
void dart_prethink (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t move;
|
||||||
|
vectoangles(ent->velocity, move);
|
||||||
|
VectorSubtract(move, ent->s.angles, move);
|
||||||
|
move[0] = fmod((move[0] + 180), 360) - 180;
|
||||||
|
move[1] = fmod((move[1] + 180), 360) - 180;
|
||||||
|
move[2] = fmod((move[2] + 180), 360) - 180;
|
||||||
|
VectorScale(move, 1/FRAMETIME, ent->avelocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
======================
|
||||||
|
Infected Dart Launcher
|
||||||
|
======================
|
||||||
|
*/
|
||||||
|
void infecteddart_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
if (other == self->owner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!other->client) //only infect players
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (surf && (surf->flags & SURF_SKY))
|
||||||
|
{
|
||||||
|
G_FreeEdict (self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->owner->client)
|
||||||
|
PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
|
||||||
|
|
||||||
|
if (other->takedamage)
|
||||||
|
{
|
||||||
|
if (!Q_stricmp("SentryGun", other->classname))
|
||||||
|
{
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_BLASTER);
|
||||||
|
gi.WritePosition (self->s.origin);
|
||||||
|
if (!plane)
|
||||||
|
gi.WriteDir (vec3_origin);
|
||||||
|
else
|
||||||
|
gi.WriteDir (plane->normal);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||||
|
T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 2, DAMAGE_ENERGY, self->mod);
|
||||||
|
}
|
||||||
|
//Heal person if you hit someone on the same team
|
||||||
|
else if (other->wf_team == self->wf_team)
|
||||||
|
{
|
||||||
|
if (other->disease)
|
||||||
|
{
|
||||||
|
safe_cprintf(other, PRINT_HIGH, "You've been cured!");
|
||||||
|
}
|
||||||
|
|
||||||
|
gi.sound (other, CHAN_VOICE, gi.soundindex ("items/pkup.wav"), 1, ATTN_NORM, 0);
|
||||||
|
|
||||||
|
HealPlayer(other);
|
||||||
|
|
||||||
|
if (other->client)
|
||||||
|
{
|
||||||
|
other->client->blindBase = 0; //stop blindness
|
||||||
|
other->client->blindTime = 0;
|
||||||
|
}
|
||||||
|
other->DrunkTime=level.time - 1; //stop concusion
|
||||||
|
|
||||||
|
if (other->health < other->max_health)
|
||||||
|
{
|
||||||
|
other->health += 25;
|
||||||
|
if (other->health > other->max_health)
|
||||||
|
other->health = other->max_health;
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_BFG_EXPLOSION);
|
||||||
|
gi.WritePosition (other->s.origin);
|
||||||
|
gi.multicast (other->s.origin, MULTICAST_PVS);
|
||||||
|
if (self->mod == MOD_BIOSENTRY)
|
||||||
|
gi.sound(other, CHAN_VOICE, gi.soundindex("weapons/biosentry/biofriendlyhit.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else
|
||||||
|
gi.sound(other, CHAN_VOICE, gi.soundindex("ctf/tech4.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 2, DAMAGE_ENERGY, self->mod);
|
||||||
|
infect_person(other,self->owner);
|
||||||
|
if (self->mod == MOD_BIOSENTRY)
|
||||||
|
gi.sound(other, CHAN_VOICE, gi.soundindex("weapons/biosentry/bioenemyhit.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else
|
||||||
|
gi.sound (other, CHAN_VOICE, gi.soundindex ("darthit.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_BLASTER);
|
||||||
|
gi.WritePosition (self->s.origin);
|
||||||
|
if (!plane)
|
||||||
|
gi.WriteDir (vec3_origin);
|
||||||
|
else
|
||||||
|
gi.WriteDir (plane->normal);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->think = G_FreeEdict;
|
||||||
|
self->nextthink = level.time + 3 + 8 * crandom();
|
||||||
|
|
||||||
|
stick(self, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fire_infecteddart (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect, int mod)
|
||||||
|
{
|
||||||
|
edict_t *bolt;
|
||||||
|
trace_t tr;
|
||||||
|
|
||||||
|
VectorNormalize (dir);
|
||||||
|
|
||||||
|
bolt = G_Spawn();
|
||||||
|
bolt->mod = mod;
|
||||||
|
bolt->wf_team = self->wf_team;
|
||||||
|
VectorCopy (start, bolt->s.origin);
|
||||||
|
VectorCopy (start, bolt->s.old_origin);
|
||||||
|
vectoangles (dir, bolt->s.angles);
|
||||||
|
VectorScale (dir, speed, bolt->velocity);
|
||||||
|
bolt->velocity[2] += 90;
|
||||||
|
bolt->movetype = MOVETYPE_TOSS; //JR 1/4/98 changed so it bounces
|
||||||
|
bolt->clipmask = MASK_SHOT;
|
||||||
|
bolt->prethink = dart_prethink; // Keeps the arrow aligned, so it arcs through the air nicely.
|
||||||
|
bolt->gravity = 0.2;
|
||||||
|
bolt->solid = SOLID_BBOX;
|
||||||
|
bolt->s.effects |= effect;
|
||||||
|
VectorSet (bolt->mins,-1,-1,-2.5);
|
||||||
|
VectorSet (bolt->maxs,1,1,4);
|
||||||
|
bolt->s.modelindex = gi.modelindex ("models/dart/tris.md2");
|
||||||
|
bolt->s.skinnum = 0;
|
||||||
|
bolt->owner = self;
|
||||||
|
bolt->touch = infecteddart_touch;
|
||||||
|
bolt->nextthink = level.time + 3;
|
||||||
|
bolt->think = G_FreeEdict;
|
||||||
|
bolt->dmg = damage;
|
||||||
|
bolt->classname = "infected dart";
|
||||||
|
gi.linkentity (bolt);
|
||||||
|
|
||||||
|
if (self->client)
|
||||||
|
check_dodge (self, bolt->s.origin, dir, speed);
|
||||||
|
|
||||||
|
tr = gi.trace (self->s.origin, NULL, NULL, bolt->s.origin, bolt, MASK_SHOT);
|
||||||
|
if (tr.fraction < 1.0)
|
||||||
|
{
|
||||||
|
VectorMA (bolt->s.origin, -10, dir, bolt->s.origin);
|
||||||
|
bolt->touch (bolt, tr.ent, NULL, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void weapon_infecteddartlauncher_fire (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t start;
|
||||||
|
vec3_t forward, right;
|
||||||
|
vec3_t offset;
|
||||||
|
int damage;
|
||||||
|
int speed;
|
||||||
|
|
||||||
|
damage = wf_game.weapon_damage[WEAPON_INFECTEDDART];
|
||||||
|
|
||||||
|
if (is_quad)
|
||||||
|
{
|
||||||
|
damage *= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
||||||
|
|
||||||
|
VectorScale (forward, -3, ent->client->kick_origin);
|
||||||
|
ent->client->kick_angles[0] = -3;
|
||||||
|
|
||||||
|
VectorSet(offset, 0, 7, ent->viewheight-8);
|
||||||
|
//start[0]=ent->s.origin[0] + forward[0]*1+right[0]*6;
|
||||||
|
//start[1]=ent->s.origin[1] + forward[1]*1+right[1]*6;
|
||||||
|
//start[2]=ent->s.origin[2] + forward[2]*1+right[2]*6+ent->viewheight-8;
|
||||||
|
/* new stuff to fix left handedness */
|
||||||
|
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
|
||||||
|
/* new stuff to fix left handedness */
|
||||||
|
|
||||||
|
speed = wf_game.weapon_speed[WEAPON_INFECTEDDART];
|
||||||
|
fire_infecteddart (ent, start, forward, damage, speed, EF_GIB, MOD_INFECTEDDART);
|
||||||
|
|
||||||
|
// send muzzle flash
|
||||||
|
gi.WriteByte (svc_muzzleflash);
|
||||||
|
gi.WriteShort (ent-g_edicts);
|
||||||
|
gi.WriteByte (MZ_SHOTGUN | is_silenced);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
ent->client->ps.gunframe++;
|
||||||
|
PlayerNoise(ent, start, PNOISE_WEAPON);
|
||||||
|
|
||||||
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
||||||
|
ent->client->pers.inventory[ent->client->ammo_index] -= ent->client->pers.weapon->quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Weapon_InfectedDartLauncher (edict_t *ent)
|
||||||
|
{
|
||||||
|
static int pause_frames[] = {34, 51, 59, 0};
|
||||||
|
static int fire_frames[] = {6, 0};
|
||||||
|
|
||||||
|
Weapon_Generic (ent, 5, 16, 59, 64 ,pause_frames, fire_frames, weapon_infecteddartlauncher_fire);
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
// my functions
|
||||||
|
void PlaceLaser (edict_t *ent);
|
||||||
|
void pre_target_laser_think (edict_t *self);
|
||||||
|
// controlling parameters
|
||||||
|
#define LASER_TIME 120
|
||||||
|
#define MAX_LASERS 5
|
||||||
|
#define CELLS_FOR_LASER 20
|
||||||
|
#define LASER_DAMAGE 100
|
||||||
|
#define LASER_MOUNT_DAMAGE 50
|
||||||
|
#define LASER_MOUNT_DAMAGE_RADIUS 64
|
||||||
|
// In-built Quake2 routines
|
||||||
|
void target_laser_use (edict_t *self, edict_t *other, edict_t *activator);
|
||||||
|
void target_laser_think (edict_t *self);
|
||||||
|
void target_laser_on (edict_t *self);
|
||||||
|
void target_laser_off (edict_t *self);
|
|
@ -0,0 +1,351 @@
|
||||||
|
//The grenade turrets hit the ceiling and stick out halfway.
|
||||||
|
//this makes it so they drop down a bit, eliminating this.
|
||||||
|
#include "g_local.h"
|
||||||
|
void grenlaser_think2 (edict_t *ent);
|
||||||
|
void laser_Explode (edict_t *ent);
|
||||||
|
|
||||||
|
void grenlaser_think4 (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t down;
|
||||||
|
int speed;
|
||||||
|
|
||||||
|
down[0]=0; //We're going DOWN!!!
|
||||||
|
down[1]=0;
|
||||||
|
down[2]=-100;
|
||||||
|
|
||||||
|
VectorNormalize(down);
|
||||||
|
VectorCopy(down, ent->movedir);
|
||||||
|
speed=75;
|
||||||
|
VectorScale(down, speed, ent->velocity);
|
||||||
|
|
||||||
|
ent->nextthink=level.time + .3;
|
||||||
|
ent->think=grenlaser_think2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void laser_Explode (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t origin;
|
||||||
|
|
||||||
|
T_RadiusDamage(ent, ent->owner, ent->dmg, ent->enemy, ent->dmg_radius, MOD_LASERCUTTER);
|
||||||
|
|
||||||
|
VectorMA (ent->s.origin, -0.02, ent->velocity, origin);
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
if (ent->waterlevel)
|
||||||
|
{
|
||||||
|
if (ent->groundentity)
|
||||||
|
gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
|
||||||
|
else
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (ent->groundentity)
|
||||||
|
gi.WriteByte (TE_GRENADE_EXPLOSION);
|
||||||
|
else
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION);
|
||||||
|
}
|
||||||
|
gi.WritePosition (origin);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PHS);
|
||||||
|
|
||||||
|
G_FreeEdict (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second think function for da grenade turrets
|
||||||
|
//Initially this was a homing think function from qdevels www.planetquake.com/qdevels
|
||||||
|
//Imp was here (duh)
|
||||||
|
//MUST go before grenturret_think1 so that think1 can set ent->think to grenturret_think2
|
||||||
|
|
||||||
|
//Note 3/22: Putting think2 before 1 isn't necessary anymore, since I
|
||||||
|
//prototyped it in g_local.h, but what the hell...
|
||||||
|
|
||||||
|
void grenlaser_think2 (edict_t *ent)
|
||||||
|
{
|
||||||
|
edict_t *target = NULL;
|
||||||
|
edict_t *blip = NULL;
|
||||||
|
// vec3_t start;
|
||||||
|
// vec3_t point;
|
||||||
|
// vec3_t dir;
|
||||||
|
trace_t tr;
|
||||||
|
vec3_t end,right,forward;
|
||||||
|
AngleVectors (ent->s.angles, forward, right, NULL);
|
||||||
|
VectorScale(ent->movedir, 12, ent->velocity); //Keep speed at 25
|
||||||
|
|
||||||
|
if (((int) (ent->turrettime * 10)) % 10==0)
|
||||||
|
ent->movedir[2]*=-1;
|
||||||
|
end[0]=right[0]*8000;
|
||||||
|
end[1]=right[1]*8000;
|
||||||
|
end[2]=right[2]*8000;
|
||||||
|
ent->s.angles[1]+=5;
|
||||||
|
if (ent->s.angles[1]>360)
|
||||||
|
ent->s.angles[1]-=360;
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, end, ent, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
|
||||||
|
//Sparks
|
||||||
|
if ((tr.ent != ent->owner) && (tr.ent->takedamage))
|
||||||
|
T_Damage (tr.ent, ent, ent->owner, forward, tr.endpos, tr.plane.normal,
|
||||||
|
wf_game.grenade_damage[GRENADE_TYPE_LASERCUTTER], 0, 0, MOD_LASERCUTTER);
|
||||||
|
else if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
|
||||||
|
{
|
||||||
|
// hit a brush, send clients
|
||||||
|
// a light flash and sparks temp entity.
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_BLASTER);
|
||||||
|
gi.WritePosition (tr.endpos);
|
||||||
|
gi.WriteDir (tr.plane.normal);
|
||||||
|
gi.multicast (tr.endpos, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_BFG_LASER);
|
||||||
|
gi.WritePosition (ent->s.origin);
|
||||||
|
gi.WritePosition (tr.endpos);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PHS);
|
||||||
|
end[0]=right[0]*-8000;
|
||||||
|
end[1]=right[1]*-8000;
|
||||||
|
end[2]=right[2]*-8000;
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, end, ent, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
|
||||||
|
|
||||||
|
//Laser sparks
|
||||||
|
if ((tr.ent != ent->owner) && (tr.ent->takedamage))
|
||||||
|
T_Damage (tr.ent, ent, ent->owner, forward, tr.endpos, tr.plane.normal,
|
||||||
|
5, 0, 0, MOD_LASERCUTTER);
|
||||||
|
else if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
|
||||||
|
{ // hit a brush, send clients
|
||||||
|
// a light flash and sparks temp entity.
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_BLASTER);
|
||||||
|
gi.WritePosition (tr.endpos);
|
||||||
|
gi.WriteDir (tr.plane.normal);
|
||||||
|
gi.multicast (tr.endpos, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_BFG_LASER);
|
||||||
|
gi.WritePosition (ent->s.origin);
|
||||||
|
gi.WritePosition (tr.endpos);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PHS);
|
||||||
|
end[0]=forward[0]*8000;
|
||||||
|
end[1]=forward[1]*8000;
|
||||||
|
end[2]=forward[2]*8000;
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, end, ent, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
|
||||||
|
|
||||||
|
//Laser sparks
|
||||||
|
if ((tr.ent != ent->owner) && (tr.ent->takedamage))
|
||||||
|
T_Damage (tr.ent, ent, ent->owner, forward, tr.endpos, tr.plane.normal, 5, 0, 0, MOD_LASERCUTTER);
|
||||||
|
else if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
|
||||||
|
{ // hit a brush, send clients
|
||||||
|
// a light flash and sparks temp entity.
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_BLASTER);
|
||||||
|
gi.WritePosition (tr.endpos);
|
||||||
|
gi.WriteDir (tr.plane.normal);
|
||||||
|
gi.multicast (tr.endpos, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_BFG_LASER);
|
||||||
|
gi.WritePosition (ent->s.origin);
|
||||||
|
gi.WritePosition (tr.endpos);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PHS);
|
||||||
|
end[0]=forward[0]*-8000;
|
||||||
|
end[1]=forward[1]*-8000;
|
||||||
|
end[2]=forward[2]*-8000;
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, end, ent, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
|
||||||
|
|
||||||
|
//Laser sparks
|
||||||
|
if ((tr.ent != ent->owner) && (tr.ent->takedamage))
|
||||||
|
T_Damage (tr.ent, ent, ent->owner, forward, tr.endpos, tr.plane.normal,
|
||||||
|
5, 0, 0, MOD_LASERCUTTER);
|
||||||
|
else if (!((tr.surface) && (tr.surface->flags & SURF_SKY)))
|
||||||
|
{ // hit a brush, send clients
|
||||||
|
// a light flash and sparks temp entity.
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_BLASTER);
|
||||||
|
gi.WritePosition (tr.endpos);
|
||||||
|
gi.WriteDir (tr.plane.normal);
|
||||||
|
gi.multicast (tr.endpos, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
gi.WriteByte (TE_BFG_LASER);
|
||||||
|
gi.WritePosition (ent->s.origin);
|
||||||
|
gi.WritePosition (tr.endpos);
|
||||||
|
gi.multicast (ent->s.origin, MULTICAST_PHS);
|
||||||
|
//If our turret is out of ammo or has been too long, kill it
|
||||||
|
if (level.time>=ent->turretdie)
|
||||||
|
{
|
||||||
|
ent->think=laser_Explode; //Drops to the ground and explodes
|
||||||
|
ent->nextthink=level.time+2; //Looks better than just disappearing
|
||||||
|
ent->movetype=MOVETYPE_BOUNCE;
|
||||||
|
|
||||||
|
//FIXME: This is ugly... is it possible to subtract effects
|
||||||
|
//instead of totally redefining the effects?
|
||||||
|
ent->s.effects = EF_GRENADE; //Lights out!!!
|
||||||
|
ent->s.renderfx = 0; //Goodbye shell :(
|
||||||
|
}
|
||||||
|
ent->turrettime+=.1;
|
||||||
|
ent->nextthink=level.time +0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void grenlaser_think1 (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t up;
|
||||||
|
vec3_t right;
|
||||||
|
int speed;
|
||||||
|
|
||||||
|
//Take out gravity
|
||||||
|
ent->movetype=MOVETYPE_FLYMISSILE;
|
||||||
|
|
||||||
|
up[0]=0; //We're going UP!!!
|
||||||
|
up[1]=0;
|
||||||
|
up[2]=5;//Waist height
|
||||||
|
|
||||||
|
right[0]=100; //we're pointing right... ugly hack
|
||||||
|
right[1]=0;
|
||||||
|
right[2]=0;
|
||||||
|
|
||||||
|
ent->s.effects |= EF_HYPERBLASTER; //Lots of fun with green lights
|
||||||
|
ent->s.effects |= EF_COLOR_SHELL; //Green shell... fun!
|
||||||
|
|
||||||
|
// ent->s.renderfx |= RF_SHELL_GREEN; //It's a GREEN shell!!!
|
||||||
|
|
||||||
|
if (ent->wf_team == CTF_TEAM1) //team 1 is red
|
||||||
|
ent->s.renderfx |= RF_SHELL_RED;
|
||||||
|
else
|
||||||
|
ent->s.renderfx |= RF_SHELL_BLUE;
|
||||||
|
|
||||||
|
VectorNormalize(up);
|
||||||
|
VectorCopy(up, ent->movedir);
|
||||||
|
speed=20;
|
||||||
|
VectorScale(up, speed, ent->velocity);
|
||||||
|
|
||||||
|
ent->avelocity[0]=0;
|
||||||
|
ent->avelocity[1]=360*5;
|
||||||
|
ent->avelocity[2]=0;
|
||||||
|
|
||||||
|
vectoangles(right, ent->s.angles);
|
||||||
|
|
||||||
|
ent->nextthink=level.time+.5;
|
||||||
|
ent->think=grenlaser_think4;
|
||||||
|
ent->turrettime=1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void laser_Touch (edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
|
{
|
||||||
|
if (other == ent->owner)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (surf && (surf->flags & SURF_SKY))
|
||||||
|
{
|
||||||
|
laser_Explode (ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!other->takedamage)
|
||||||
|
{
|
||||||
|
if (ent->think==grenlaser_think4) //Move the grenade away from
|
||||||
|
ent->nextthink=level.time; //the ceiling when it hits it
|
||||||
|
|
||||||
|
if (ent->spawnflags & 1)
|
||||||
|
{
|
||||||
|
if (random() > 0.5)
|
||||||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb1a.wav"), 1, ATTN_NORM, 0);
|
||||||
|
else
|
||||||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/hgrenb2a.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gi.sound (ent, CHAN_VOICE, gi.soundindex ("weapons/grenlb1b.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//ent->enemy = other;
|
||||||
|
//laser_Explode (ent);
|
||||||
|
}
|
||||||
|
void Laser_Grenade_Die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||||
|
{
|
||||||
|
self->takedamage = DAMAGE_NO;
|
||||||
|
self->nextthink = level.time + .1;
|
||||||
|
self->think = laser_Explode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fire_laser_grenade (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int speed, float timer, float damage_radius, qboolean held)
|
||||||
|
{
|
||||||
|
edict_t *grenade;
|
||||||
|
vec3_t dir;
|
||||||
|
vec3_t forward, right, up;
|
||||||
|
|
||||||
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
||||||
|
{
|
||||||
|
if ( self->client->pers.inventory[ITEM_INDEX(FindItem("Grenades"))] >= TURRET_GRENADES
|
||||||
|
&& self->client->pers.inventory[ITEM_INDEX(FindItem("Slugs"))] >= TURRET_SLUGS)
|
||||||
|
{
|
||||||
|
self->client->pers.inventory[ITEM_INDEX(FindItem("Grenades"))] -= TURRET_GRENADES;
|
||||||
|
self->client->pers.inventory[ITEM_INDEX(FindItem("Slugs"))] -= TURRET_SLUGS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
safe_cprintf(self, PRINT_HIGH, "You need %d Grenades and %d Slugs for Laser Cutter\n",TURRET_GRENADES,TURRET_SLUGS);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++self->client->pers.active_grenades[GRENADE_TYPE_LASERCUTTER];
|
||||||
|
|
||||||
|
vectoangles (aimdir, dir);
|
||||||
|
AngleVectors (dir, forward, right, up);
|
||||||
|
|
||||||
|
grenade = G_Spawn();
|
||||||
|
grenade->grenade_index = GRENADE_TYPE_LASERCUTTER;
|
||||||
|
VectorCopy (start, grenade->s.origin);
|
||||||
|
VectorScale (aimdir, speed, grenade->velocity);
|
||||||
|
VectorMA (grenade->velocity, 200 + crandom() * 10.0, up, grenade->velocity);
|
||||||
|
VectorMA (grenade->velocity, crandom() * 10.0, right, grenade->velocity);
|
||||||
|
VectorSet (grenade->avelocity, 300, 300, 300);
|
||||||
|
grenade->movetype = MOVETYPE_BOUNCE;
|
||||||
|
// grenade->clipmask = MASK_SHOT;
|
||||||
|
grenade->clipmask = MASK_PLAYERSOLID;
|
||||||
|
grenade->solid = SOLID_BBOX;
|
||||||
|
grenade->s.effects |= EF_GRENADE;
|
||||||
|
VectorClear (grenade->mins);
|
||||||
|
VectorClear (grenade->maxs);
|
||||||
|
grenade->s.modelindex = gi.modelindex (GRLASERCUTTER_MODEL);
|
||||||
|
grenade->s.skinnum = GRLASERCUTTER_SKIN;
|
||||||
|
grenade->owner = self;
|
||||||
|
// grenade->touch = laser_Touch;
|
||||||
|
grenade->wf_team = self->client->resp.ctf_team;
|
||||||
|
|
||||||
|
// A few more attributes to let the grenade 'die'
|
||||||
|
VectorSet(grenade->mins, -10, -10, 0);
|
||||||
|
VectorSet(grenade->maxs, 10, 10, 10);
|
||||||
|
grenade->mass = 40;
|
||||||
|
grenade->health = 10;
|
||||||
|
grenade->die = Laser_Grenade_Die;
|
||||||
|
grenade->takedamage = DAMAGE_YES;
|
||||||
|
grenade->monsterinfo.aiflags = AI_NOSTEP;
|
||||||
|
|
||||||
|
grenade->nextthink = level.time + timer;
|
||||||
|
grenade->think = grenlaser_think1;
|
||||||
|
grenade->turrettime=0;
|
||||||
|
grenade->turretdie=level.time+4.10 + timer;
|
||||||
|
grenade->turretammo=6;
|
||||||
|
|
||||||
|
grenade->dmg = DAMAGE_LASERCUTTER;
|
||||||
|
grenade->dmg_radius = damage_radius;
|
||||||
|
grenade->classname = "lasercutter";
|
||||||
|
if (held)
|
||||||
|
grenade->spawnflags = 3;
|
||||||
|
else
|
||||||
|
grenade->spawnflags = 1;
|
||||||
|
|
||||||
|
//set team
|
||||||
|
grenade->wf_team = self->client->resp.ctf_team;
|
||||||
|
|
||||||
|
grenade->s.sound = gi.soundindex("weapons/hgrenc1b.wav");
|
||||||
|
|
||||||
|
//if (timer <= 0.0)
|
||||||
|
//laser_Explode (grenade);
|
||||||
|
//else
|
||||||
|
{
|
||||||
|
gi.sound (self, CHAN_WEAPON, gi.soundindex ("weapons/hgrent1a.wav"), 1, ATTN_NORM, 0);
|
||||||
|
gi.linkentity (grenade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
//This code just handles the laser sight
|
||||||
|
#include "g_local.h"
|
||||||
|
|
||||||
|
void LaserSightThink (edict_t *self);
|
||||||
|
|
||||||
|
#define lss ent->lasersight
|
||||||
|
|
||||||
|
void lasersight_on (edict_t *ent)
|
||||||
|
{
|
||||||
|
vec3_t start;
|
||||||
|
vec3_t forward, right;
|
||||||
|
vec3_t end;
|
||||||
|
|
||||||
|
//if (wfdebug) gi.dprintf(" LASERON - ");
|
||||||
|
|
||||||
|
if (!lss) // Create it
|
||||||
|
{
|
||||||
|
//if (wfdebug) gi.dprintf(" CREATE\n");
|
||||||
|
AngleVectors (ent->client->v_angle, forward, right, NULL);
|
||||||
|
VectorSet(end,100 , 0, 0);
|
||||||
|
G_ProjectSource (ent->s.origin, end, forward, right, start);
|
||||||
|
lss = G_Spawn ();
|
||||||
|
lss->owner = ent;
|
||||||
|
lss->movetype = MOVETYPE_NOCLIP;
|
||||||
|
lss->solid = SOLID_NOT;
|
||||||
|
lss->classname = "lasersight";
|
||||||
|
lss->s.modelindex = gi.modelindex ("models/sight/tris.md2");
|
||||||
|
lss->s.skinnum = 0;
|
||||||
|
lss->s.renderfx |= RF_FULLBRIGHT;
|
||||||
|
lss->think = LaserSightThink;
|
||||||
|
lss->nextthink = level.time + 0.1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//if (wfdebug) gi.dprintf(" NOTHING\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void lasersight_off (edict_t *ent)
|
||||||
|
{
|
||||||
|
//if (wfdebug) gi.dprintf(" LASEROFF -");
|
||||||
|
if (lss) //Remove it
|
||||||
|
{
|
||||||
|
G_FreeEdict(lss);
|
||||||
|
lss = NULL;
|
||||||
|
//if (wfdebug) gi.dprintf(" CREATE\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//if (wfdebug) gi.dprintf(" NOTHING\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LaserSightThink (edict_t *self)
|
||||||
|
{
|
||||||
|
vec3_t start,end,endp,offset;
|
||||||
|
vec3_t forward,right,up;
|
||||||
|
trace_t tr;
|
||||||
|
|
||||||
|
AngleVectors (self->owner->client->v_angle, forward, right, up);
|
||||||
|
VectorSet(offset,24 , 6, self->owner->viewheight-7);
|
||||||
|
G_ProjectSource (self->owner->s.origin, offset, forward, right, start);
|
||||||
|
VectorMA(start,8192,forward,end);
|
||||||
|
tr = gi.trace (start,NULL,NULL, end,self->owner,CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
|
||||||
|
if (tr.fraction != 1)
|
||||||
|
{
|
||||||
|
VectorMA(tr.endpos,-4,forward,endp);
|
||||||
|
VectorCopy(endp,tr.endpos);
|
||||||
|
}
|
||||||
|
//if (tr.ent->client->ps.pmove.pm_flags & PMF_DUCKED)
|
||||||
|
if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client))
|
||||||
|
{
|
||||||
|
if ((tr.ent->takedamage) && (tr.ent != self->owner))
|
||||||
|
{
|
||||||
|
if (tr.endpos[2] > ((tr.ent->s.origin[2] + 20)))
|
||||||
|
{
|
||||||
|
// Headshot Confirmed: Opted to go with switching skins instead of models
|
||||||
|
self->s.skinnum = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Acrid 5/99 , note: BBox on crouched players differs
|
||||||
|
from standing so +8 won't work, even though it
|
||||||
|
should be correct ie, look how high you can aim
|
||||||
|
over a standing players head. I also suggest + 17-18
|
||||||
|
for standing players above and g_weapon.c:remove this note */
|
||||||
|
else if ( (tr.ent->client && tr.ent->client->ps.pmove.pm_flags & PMF_DUCKED) && (tr.endpos[2] > ((tr.ent->s.origin[2] + 1 ))) )
|
||||||
|
{
|
||||||
|
self->s.skinnum = 1;
|
||||||
|
}
|
||||||
|
//else your not aiming at the head anymore //Acrid
|
||||||
|
else self->s.skinnum = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else //else this isnt a valid object //Acrid
|
||||||
|
self->s.skinnum = 0;
|
||||||
|
|
||||||
|
//The following line will make the meatball change angles
|
||||||
|
//based on the the meatball is touching
|
||||||
|
//vectoangles(tr.plane.normal,self->s.angles);
|
||||||
|
|
||||||
|
//Instead, these line will face the meatball the same
|
||||||
|
//direction as the player
|
||||||
|
self->s.angles[PITCH] = self->owner->s.angles[PITCH];
|
||||||
|
self->s.angles[YAW] = self->owner->s.angles[YAW];
|
||||||
|
self->s.angles[ROLL] = self->owner->s.angles[ROLL];
|
||||||
|
|
||||||
|
|
||||||
|
VectorCopy(tr.endpos,self->s.origin);
|
||||||
|
gi.linkentity (self);
|
||||||
|
self->nextthink = level.time + 0.1;
|
||||||
|
}
|
|
@ -0,0 +1,455 @@
|
||||||
|
#include "g_local.h"
|
||||||
|
#include "laser2.h"
|
||||||
|
|
||||||
|
int laser_colour[] = {
|
||||||
|
0xf3f3f1f1, //0 blue
|
||||||
|
0xf2f2f0f0, //1 red
|
||||||
|
// 0xf2f2f0f0, //0 red
|
||||||
|
// 0xf3f3f1f1, //1 blue
|
||||||
|
0xf3f3f1f1, //2 blue
|
||||||
|
// 0xd0d1d2d3, //2 green
|
||||||
|
0xdcdddedf, //3 yellow
|
||||||
|
0xe0e1e2e3, //4 bitty yellow strobe
|
||||||
|
0x80818283, //5 JR brownish purple I think
|
||||||
|
0x70717273, //6 JR light blue
|
||||||
|
0x90919293, //7 JR type of green
|
||||||
|
0xb0b1b2b3, //8 JR another purple
|
||||||
|
0x40414243, //9 JR a reddish color
|
||||||
|
0xe2e5e3e6, //10 JR another orange
|
||||||
|
0xd0f1d3f3, //11 JR mixture of color
|
||||||
|
0xf2f3f0f1, //12 JR red outer blue inner
|
||||||
|
0xf3f2f1f0, //13 JR blue outer red inner
|
||||||
|
0xdad0dcd2, //14 JR yellow outer green inner
|
||||||
|
0xd0dad2dc //15 JR green outer yellow inner
|
||||||
|
};
|
||||||
|
|
||||||
|
//By setting the color for each, players can tell the difference
|
||||||
|
#define LASER_DEFENSE_COLOR 4
|
||||||
|
#define LASER_TRIPBOMB_COLOR 12
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
=====================
|
||||||
|
Laser Defense
|
||||||
|
=====================
|
||||||
|
*/
|
||||||
|
void laser_cleanup(edict_t *self)
|
||||||
|
{
|
||||||
|
vec3_t origin;
|
||||||
|
|
||||||
|
if (self->owner->client)
|
||||||
|
PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
|
||||||
|
|
||||||
|
T_RadiusDamage(self, self->owner, self->dmg, NULL, self->dmg_radius, MOD_LASER_DEFENSE);
|
||||||
|
|
||||||
|
VectorMA (self->s.origin, -0.02, self->velocity, origin);
|
||||||
|
gi.WriteByte (svc_temp_entity);
|
||||||
|
if (self->waterlevel)
|
||||||
|
{
|
||||||
|
if (self->groundentity)
|
||||||
|
gi.WriteByte (TE_GRENADE_EXPLOSION_WATER);
|
||||||
|
else
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION_WATER);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (self->groundentity)
|
||||||
|
gi.WriteByte (TE_GRENADE_EXPLOSION);
|
||||||
|
else
|
||||||
|
gi.WriteByte (TE_ROCKET_EXPLOSION);
|
||||||
|
}
|
||||||
|
gi.WritePosition (origin);
|
||||||
|
gi.multicast (self->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
|
||||||
|
//reduce # active laser defenses
|
||||||
|
// TeT++
|
||||||
|
|
||||||
|
//Remove laser
|
||||||
|
if (self->creator)
|
||||||
|
G_FreeEdict (self->creator);
|
||||||
|
// TeT--
|
||||||
|
|
||||||
|
//Remove grenade
|
||||||
|
G_FreeEdict (self);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void laser_defense_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||||
|
{
|
||||||
|
// TeT++
|
||||||
|
if (self->creator)
|
||||||
|
{
|
||||||
|
self->creator->delay = level.time + 0.1;
|
||||||
|
}
|
||||||
|
// TeT--
|
||||||
|
|
||||||
|
laser_cleanup(self);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Laser Defense
|
||||||
|
// TeT++
|
||||||
|
void PlaceLaser (edict_t *ent)
|
||||||
|
{
|
||||||
|
edict_t *laser,
|
||||||
|
*grenade;
|
||||||
|
vec3_t forward,
|
||||||
|
wallp,
|
||||||
|
start,
|
||||||
|
end;
|
||||||
|
trace_t tr;
|
||||||
|
trace_t endTrace;
|
||||||
|
|
||||||
|
// valid ent ?
|
||||||
|
if ((!ent->client) || (ent->health<=0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// cells for laser ?
|
||||||
|
if (ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] < CELLS_FOR_LASER)
|
||||||
|
{
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "Not enough cells for laser.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Are there too many laser defense systems now?
|
||||||
|
if (ent->client->pers.active_special[ITEM_SPECIAL_LASER_DEFENSE] >= MAX_SPECIAL_LASER_DEFENSE)
|
||||||
|
{
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "You can only have %d active Laser Defense Systems.\n",MAX_SPECIAL_LASER_DEFENSE );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup "little look" to close wall
|
||||||
|
VectorCopy(ent->s.origin,wallp);
|
||||||
|
|
||||||
|
// Cast along view angle
|
||||||
|
AngleVectors (ent->client->v_angle, forward, NULL, NULL);
|
||||||
|
|
||||||
|
// Setup end point
|
||||||
|
wallp[0]=ent->s.origin[0]+forward[0]*50;
|
||||||
|
wallp[1]=ent->s.origin[1]+forward[1]*50;
|
||||||
|
wallp[2]=ent->s.origin[2]+forward[2]*50;
|
||||||
|
|
||||||
|
// trace
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, wallp, ent, MASK_SOLID);
|
||||||
|
|
||||||
|
// Line complete ? (ie. no collision)
|
||||||
|
if (tr.fraction == 1.0)
|
||||||
|
{
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Too far from wall.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hit sky ?
|
||||||
|
if (tr.surface)
|
||||||
|
if (tr.surface->flags & SURF_SKY)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Ok, lets stick one on then ...
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Laser attached.\n");
|
||||||
|
|
||||||
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
||||||
|
ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] -= CELLS_FOR_LASER;
|
||||||
|
|
||||||
|
++ent->client->pers.active_special[ITEM_SPECIAL_LASER_DEFENSE];
|
||||||
|
|
||||||
|
// get enties for both objects
|
||||||
|
grenade = G_Spawn();
|
||||||
|
laser = G_Spawn();
|
||||||
|
grenade->special_index = ITEM_SPECIAL_LASER_DEFENSE;
|
||||||
|
|
||||||
|
// setup the Grenade
|
||||||
|
VectorClear (grenade->mins);
|
||||||
|
VectorClear (grenade->maxs);
|
||||||
|
VectorCopy (tr.endpos, grenade->s.origin);
|
||||||
|
vectoangles(tr.plane.normal, grenade->s.angles);
|
||||||
|
|
||||||
|
grenade->special_index = ITEM_SPECIAL_LASER_DEFENSE;
|
||||||
|
grenade->wf_team = ent->wf_team;
|
||||||
|
grenade -> movetype = MOVETYPE_NONE;
|
||||||
|
grenade -> clipmask = MASK_SHOT;
|
||||||
|
grenade->solid = SOLID_BBOX;
|
||||||
|
VectorSet(grenade->mins, -3, -3, 0);
|
||||||
|
VectorSet(grenade->maxs, 3, 3, 6);
|
||||||
|
grenade -> takedamage = DAMAGE_YES;
|
||||||
|
grenade -> die = laser_defense_die;
|
||||||
|
grenade -> s.modelindex = gi.modelindex (GRNORMAL_MODEL);
|
||||||
|
grenade -> owner = ent;
|
||||||
|
grenade -> creator = laser;
|
||||||
|
grenade -> monsterinfo.aiflags = AI_NOSTEP;
|
||||||
|
grenade -> classname = "laser_defense_gr";
|
||||||
|
grenade -> nextthink = level.time + LASER_TIME;
|
||||||
|
grenade -> think = laser_cleanup;
|
||||||
|
grenade -> health = 10;
|
||||||
|
grenade -> max_health = 10;
|
||||||
|
|
||||||
|
|
||||||
|
// Now lets find the other end of the laser
|
||||||
|
// by starting at the grenade position
|
||||||
|
VectorCopy (grenade->s.origin, start);
|
||||||
|
|
||||||
|
// setup laser movedir (projection of laser)
|
||||||
|
G_SetMovedir (grenade->s.angles, laser->movedir);
|
||||||
|
VectorMA (start, 2048, laser->movedir, end);
|
||||||
|
|
||||||
|
endTrace = gi.trace (start, NULL, NULL, end, ent, MASK_SOLID);
|
||||||
|
|
||||||
|
// -----------
|
||||||
|
// Setup laser
|
||||||
|
// -----------
|
||||||
|
laser -> wf_team = ent->wf_team;
|
||||||
|
|
||||||
|
laser -> movetype = MOVETYPE_NONE;
|
||||||
|
laser -> solid = SOLID_NOT;
|
||||||
|
laser -> s.renderfx = RF_BEAM|RF_TRANSLUCENT;
|
||||||
|
laser -> s.modelindex = 1; // must be non-zero
|
||||||
|
laser -> s.sound = gi.soundindex ("world/laser.wav");
|
||||||
|
laser -> classname = "laser_defense";
|
||||||
|
laser -> s.frame = 2; // beam diameter
|
||||||
|
laser -> owner = NULL;
|
||||||
|
laser -> s.skinnum = laser_colour[ent->wf_team];
|
||||||
|
laser -> dmg = LASER_DAMAGE;
|
||||||
|
laser -> think = pre_target_laser_def_think;
|
||||||
|
laser -> delay = level.time + LASER_TIME;
|
||||||
|
laser -> creator = grenade;
|
||||||
|
laser -> activator = ent;
|
||||||
|
|
||||||
|
// start off ...
|
||||||
|
target_laser_off (laser);
|
||||||
|
VectorCopy (endTrace.endpos, laser->s.old_origin);
|
||||||
|
|
||||||
|
// ... but make automatically come on
|
||||||
|
laser -> nextthink = level.time + 2;
|
||||||
|
|
||||||
|
// Set orgin of laser to point of contact with wall
|
||||||
|
VectorCopy(endTrace.endpos,laser->s.origin);
|
||||||
|
|
||||||
|
// convert normal at point of contact to laser angles
|
||||||
|
vectoangles(tr.plane.normal,laser->s.angles);
|
||||||
|
|
||||||
|
// setup laser movedir (projection of laser)
|
||||||
|
G_SetMovedir (laser->s.angles, laser->movedir);
|
||||||
|
|
||||||
|
VectorSet (laser->mins, -8, -8, -8);
|
||||||
|
VectorSet (laser->maxs, 8, 8, 8);
|
||||||
|
|
||||||
|
// link to world
|
||||||
|
gi.linkentity (laser);
|
||||||
|
gi.linkentity (grenade);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pre_target_laser_def_think (edict_t *self)
|
||||||
|
{
|
||||||
|
target_laser_on (self);
|
||||||
|
self->think = target_laser_def_think;
|
||||||
|
}
|
||||||
|
void pre_target_laser_think (edict_t *self)
|
||||||
|
{
|
||||||
|
target_laser_on (self);
|
||||||
|
self->think = target_laser_think;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pre_target_laserb_think (edict_t *self);
|
||||||
|
|
||||||
|
/*
|
||||||
|
===============
|
||||||
|
Laser Tripbombs
|
||||||
|
===============
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
for them to work need some code I will do later
|
||||||
|
*/
|
||||||
|
|
||||||
|
void laser_trip_cleanup(edict_t *self)
|
||||||
|
{
|
||||||
|
|
||||||
|
//Remove laser
|
||||||
|
if (self->owner)
|
||||||
|
G_FreeEdict (self->owner);
|
||||||
|
|
||||||
|
//Remove grenade
|
||||||
|
G_FreeEdict (self);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void laser_tripbomb_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
||||||
|
{
|
||||||
|
self->owner->delay = level.time + 0.1;
|
||||||
|
|
||||||
|
laser_trip_cleanup(self);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//Laser Tripbomb
|
||||||
|
void PlaceLaserb (edict_t *ent)
|
||||||
|
{
|
||||||
|
edict_t *self,
|
||||||
|
*grenade;
|
||||||
|
|
||||||
|
vec3_t forward,
|
||||||
|
wallp;
|
||||||
|
|
||||||
|
trace_t tr;
|
||||||
|
|
||||||
|
|
||||||
|
// valid ent ?
|
||||||
|
if ((!ent->client) || (ent->health<=0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// cells for laser ?
|
||||||
|
if (ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] < 100)
|
||||||
|
{
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "Not enough cells for laser bomb.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Are there too many laser defense systems now?
|
||||||
|
if (ent->client->pers.active_special[ITEM_SPECIAL_TRIPBOMB] >= MAX_SPECIAL_TRIPBOMB)
|
||||||
|
{
|
||||||
|
safe_cprintf(ent, PRINT_HIGH, "You can only have %d active Trip Bombs.\n",MAX_SPECIAL_TRIPBOMB );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup "little look" to close wall
|
||||||
|
VectorCopy(ent->s.origin,wallp);
|
||||||
|
|
||||||
|
// Cast along view angle
|
||||||
|
AngleVectors (ent->client->v_angle, forward, NULL, NULL);
|
||||||
|
|
||||||
|
// Setup end point
|
||||||
|
wallp[0]=ent->s.origin[0]+forward[0]*50;
|
||||||
|
wallp[1]=ent->s.origin[1]+forward[1]*50;
|
||||||
|
wallp[2]=ent->s.origin[2]+forward[2]*50;
|
||||||
|
|
||||||
|
// trace
|
||||||
|
tr = gi.trace (ent->s.origin, NULL, NULL, wallp, ent, MASK_SOLID);
|
||||||
|
|
||||||
|
// Line complete ? (ie. no collision)
|
||||||
|
if (tr.fraction == 1.0)
|
||||||
|
{
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Too far from wall.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hit sky ?
|
||||||
|
if (tr.surface)
|
||||||
|
if (tr.surface->flags & SURF_SKY)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Ok, lets stick one on then ...
|
||||||
|
safe_cprintf (ent, PRINT_HIGH, "Laser attached.\n");
|
||||||
|
|
||||||
|
if (! ( (int)dmflags->value & DF_INFINITE_AMMO ) )
|
||||||
|
ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] -= 100;
|
||||||
|
|
||||||
|
++ent->client->pers.active_special[ITEM_SPECIAL_TRIPBOMB];
|
||||||
|
|
||||||
|
// -----------
|
||||||
|
// Setup laser
|
||||||
|
// -----------
|
||||||
|
self = G_Spawn();
|
||||||
|
self->wf_team = ent->wf_team;
|
||||||
|
self->special_index = ITEM_SPECIAL_TRIPBOMB;
|
||||||
|
|
||||||
|
|
||||||
|
self -> movetype = MOVETYPE_NONE;
|
||||||
|
self -> solid = SOLID_NOT;
|
||||||
|
self -> s.renderfx = RF_BEAM|RF_TRANSLUCENT;
|
||||||
|
self -> s.modelindex = 1; // must be non-zero
|
||||||
|
self -> s.sound = gi.soundindex ("world/laser.wav");
|
||||||
|
self -> classname = "lb";
|
||||||
|
self -> s.frame = 2; // beam diameter
|
||||||
|
self -> owner = ent;
|
||||||
|
//GREGG
|
||||||
|
// self -> s.skinnum = laser_colour[((int) (random() * 1000)) % 16];
|
||||||
|
self -> s.skinnum = laser_colour[LASER_TRIPBOMB_COLOR];
|
||||||
|
self -> dmg = LASER_DAMAGE;
|
||||||
|
self -> think = pre_target_laserb_think;
|
||||||
|
self -> delay = level.time + LASER_TIME;
|
||||||
|
//add a laser to the amount
|
||||||
|
// ent->LaserBomb++;
|
||||||
|
// Set orgin of laser to point of contact with wall
|
||||||
|
VectorCopy(tr.endpos,self->s.origin);
|
||||||
|
|
||||||
|
// convert normal at point of contact to laser angles
|
||||||
|
vectoangles(tr.plane.normal,self -> s.angles);
|
||||||
|
|
||||||
|
// setup laser movedir (projection of laser)
|
||||||
|
G_SetMovedir (self->s.angles, self->movedir);
|
||||||
|
|
||||||
|
VectorSet (self->mins, -8, -8, -8);
|
||||||
|
VectorSet (self->maxs, 8, 8, 8);
|
||||||
|
|
||||||
|
// link to world
|
||||||
|
gi.linkentity (self);
|
||||||
|
|
||||||
|
// start off ...
|
||||||
|
target_laser_off (self);
|
||||||
|
|
||||||
|
// ... but make automatically come on
|
||||||
|
self -> nextthink = level.time + 2;
|
||||||
|
grenade = G_Spawn();
|
||||||
|
grenade->wf_team = ent->wf_team;
|
||||||
|
|
||||||
|
VectorClear (grenade->mins);
|
||||||
|
VectorClear (grenade->maxs);
|
||||||
|
VectorCopy (tr.endpos, grenade->s.origin);
|
||||||
|
vectoangles(tr.plane.normal,grenade -> s.angles);
|
||||||
|
grenade -> movetype = MOVETYPE_NONE;
|
||||||
|
grenade -> clipmask = MASK_SHOT;
|
||||||
|
//grenade -> solid = SOLID_NOT;
|
||||||
|
grenade->solid = SOLID_BBOX;
|
||||||
|
VectorSet(grenade->mins, -3, -3, 0);
|
||||||
|
VectorSet(grenade->maxs, 3, 3, 6);
|
||||||
|
grenade->takedamage=DAMAGE_YES;
|
||||||
|
grenade->die = laser_tripbomb_die;
|
||||||
|
grenade -> s.modelindex = gi.modelindex (GRNORMAL_MODEL);
|
||||||
|
grenade -> owner = self;
|
||||||
|
grenade -> nextthink = level.time + LASER_TIME;
|
||||||
|
grenade -> think = laser_trip_cleanup;
|
||||||
|
grenade->health= 15;
|
||||||
|
grenade->max_health =15;
|
||||||
|
|
||||||
|
gi.linkentity (grenade);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pre_target_laserb_think (edict_t *self)
|
||||||
|
{
|
||||||
|
target_laser_on (self);
|
||||||
|
|
||||||
|
self->think = target_laser_think;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_LaserDefense(edict_t *ent)
|
||||||
|
{
|
||||||
|
// if (ent->LaserOrbs > MAX_LASERS -1)
|
||||||
|
// safe_cprintf(ent, PRINT_HIGH, "Max Lasers Already Reached.\n");
|
||||||
|
// else
|
||||||
|
PlaceLaser (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_TripBomb(edict_t *ent)
|
||||||
|
{
|
||||||
|
// if (ent->LaserBomb > 4 -1)
|
||||||
|
// safe_cprintf(ent, PRINT_HIGH, "Max Laser Trip Bombs Already Reached.\n");
|
||||||
|
// else
|
||||||
|
PlaceLaserb (ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Remove all laser defenses for this entity
|
||||||
|
void cmd_RemoveLaserDefense(edict_t *ent)
|
||||||
|
{
|
||||||
|
edict_t *blip = NULL;
|
||||||
|
|
||||||
|
while ((blip = findradius(blip, ent->s.origin, 1000)) != NULL)
|
||||||
|
{
|
||||||
|
if (!strcmp(blip->classname, "laser_defense_gr") && blip->owner == ent)
|
||||||
|
{
|
||||||
|
blip->think = laser_cleanup;
|
||||||
|
blip->nextthink = level.time + .1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue