Extract original source code from wfsource421.zip, dated 1999

This commit is contained in:
mv 2024-09-02 14:03:07 +03:00
commit c480e687aa
165 changed files with 88828 additions and 0 deletions

53
FAVORITE.txt Normal file
View File

@ -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.....

247
Makefile Normal file
View File

@ -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

290
alarm.c Normal file
View File

@ -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);
}
}
}

47
b_antiweapon.h Normal file
View File

@ -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;
}
}

795
b_biosentry.c Normal file
View File

@ -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");
}

192
b_healingdepot.c Normal file
View File

@ -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");
}

646
b_missile.c Normal file
View File

@ -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
}
}
}

266
b_supplydepot.c Normal file
View File

@ -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");
}
}

1455
b_turret.c Normal file

File diff suppressed because it is too large Load Diff

2882
bot_ai.c Normal file

File diff suppressed because it is too large Load Diff

180
bot_die.c Normal file
View File

@ -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);
}

531
bot_items.c Normal file
View File

@ -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;
}

1423
bot_misc.c Normal file

File diff suppressed because it is too large Load Diff

451
bot_nav.c Normal file
View File

@ -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;
}

282
bot_procs.h Normal file
View File

@ -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;//$

482
bot_spawn.c Normal file
View File

@ -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;
}

3704
bot_wpns.c Normal file

File diff suppressed because it is too large Load Diff

107
bots.cfg Normal file
View File

@ -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"

1076
camclient.c Normal file

File diff suppressed because it is too large Load Diff

26
camclient.h Normal file
View File

@ -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;

445
chat.txt Normal file
View File

@ -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.

90
debug.c Normal file
View File

@ -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;
}
*/

3241
docker4xwf.ent Normal file

File diff suppressed because it is too large Load Diff

283
dwm.c Normal file
View File

@ -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);
}

20
dwm.h Normal file
View File

@ -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);

733
eraser_readme.txt Normal file
View File

@ -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

2
extra.h Normal file
View File

@ -0,0 +1,2 @@
void SelectSpawnPoint (vec3_t origin, vec3_t angles);
void ClientUserinfoChanged (edict_t *ent, char *userinfo);

1212
g_ai.c Normal file

File diff suppressed because it is too large Load Diff

153
g_chase.c Normal file
View File

@ -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;
}

2953
g_cmds.c Normal file

File diff suppressed because it is too large Load Diff

951
g_combat.c Normal file
View File

@ -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
}
}

5769
g_ctf.c Normal file

File diff suppressed because it is too large Load Diff

202
g_ctf.h Normal file
View File

@ -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 );

61
g_ent.c Normal file
View File

@ -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);
}
}

2388
g_func.c Normal file

File diff suppressed because it is too large Load Diff

4103
g_items.c Normal file

File diff suppressed because it is too large Load Diff

90
g_items.h Normal file
View File

@ -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

1618
g_local.h Normal file

File diff suppressed because it is too large Load Diff

1331
g_main.c Normal file

File diff suppressed because it is too large Load Diff

2023
g_misc.c Normal file

File diff suppressed because it is too large Load Diff

943
g_monster.c Normal file
View File

@ -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);
}

1086
g_phys.c Normal file

File diff suppressed because it is too large Load Diff

844
g_save.c Normal file
View File

@ -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;
}
}

1264
g_spawn.c Normal file

File diff suppressed because it is too large Load Diff

261
g_svcmds.c Normal file
View File

@ -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);
}

959
g_target.c Normal file
View File

@ -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");
}

625
g_trigger.c Normal file
View File

@ -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;
}

279
g_unzip.c Normal file
View File

@ -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;
}

132
g_unzip.h Normal file
View File

@ -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 */

982
g_utils.c Normal file
View File

@ -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

1483
g_weapon.c Normal file

File diff suppressed because it is too large Load Diff

284
g_zip.c Normal file
View File

@ -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;
}

64
g_zip.h Normal file
View File

@ -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 */

2
game.def Normal file
View File

@ -0,0 +1,2 @@
EXPORTS
GetGameAPI

216
game.h Normal file
View File

@ -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);

616
grapple.c Normal file
View File

@ -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
}
}
}

158
j_diesease.c Normal file
View File

@ -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);
}

264
j_fire.c Normal file
View File

@ -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);
}

76
j_kamikaze.c Normal file
View File

@ -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);
}

17
kamikaze.h Normal file
View File

@ -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);

19
laser2.h Normal file
View File

@ -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);

1
launcher.cfg Normal file
View File

@ -0,0 +1 @@

469
m_flash.c Normal file
View File

@ -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
};

541
m_move.c Normal file
View File

@ -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);
}

206
m_player.h Normal file
View File

@ -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

3624
p_client.c Normal file

File diff suppressed because it is too large Load Diff

769
p_hud.c Normal file
View File

@ -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
}

278
p_menu.c Normal file
View File

@ -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;
}

31
p_menu.h Normal file
View File

@ -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);

53
p_trail.h Normal file
View File

@ -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;

BIN
p_trail.o.sav Normal file

Binary file not shown.

BIN
p_trail.obj Normal file

Binary file not shown.

1423
p_view.c Normal file

File diff suppressed because it is too large Load Diff

2161
p_weapon.c Normal file

File diff suppressed because it is too large Load Diff

160
q_devels.c Normal file
View File

@ -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 ===---

66
q_devels.h Normal file
View File

@ -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

1400
q_shared.c Normal file

File diff suppressed because it is too large Load Diff

1269
q_shared.h Normal file

File diff suppressed because it is too large Load Diff

2246
r_weap.c Normal file

File diff suppressed because it is too large Load Diff

289
remotecam.c Normal file
View File

@ -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");
}
}

360
stdlog.c Normal file
View File

@ -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);
}
}

71
stdlog.h Normal file
View File

@ -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 */

82
throwup.c Normal file
View 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);
}
}
}

8
throwup.h Normal file
View File

@ -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);

BIN
unzip32.dll Normal file

Binary file not shown.

105
w_ak47.c Normal file
View File

@ -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);
}

168
w_armordart.c Normal file
View File

@ -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);
}

139
w_boltedblaster.c Normal file
View File

@ -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);
}

183
w_cgprojectilelauncher.c Normal file
View File

@ -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);
}

179
w_clustermissiles.c Normal file
View File

@ -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);
}

155
w_concussion.c Normal file
View File

@ -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);
}

134
w_flamethrower.c Normal file
View File

@ -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);
}

278
w_flare.c Normal file
View File

@ -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);
}

212
w_flaregun.c Normal file
View File

@ -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);
}

277
w_gasgrenade.c Normal file
View File

@ -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);
}

319
w_infectdartlauncher.c Normal file
View File

@ -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);
}

15
w_laser.h Normal file
View File

@ -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);

351
w_lasercutter.c Normal file
View File

@ -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);
}
}

111
w_lasersight.c Normal file
View File

@ -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;
}

455
w_laserweapons.c Normal file
View File

@ -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