Add the source of 3zb2 version 0.97d.

This is the latest version of 3zb2, released on 1999/11/30. This code is
licensed through the Quake II SDL license.
This commit is contained in:
Yamagi Burmeister 2017-11-05 12:14:21 +01:00
parent ae948ffbfe
commit b84f610e23
37 changed files with 43120 additions and 0 deletions

299
src/bot.c Normal file
View file

@ -0,0 +1,299 @@
#include "g_local.h"
#include "m_player.h"
#include "bot.h"
void droptofloor (edict_t *ent);
edict_t *bot_team_flag1;
edict_t *bot_team_flag2;
void SetBotFlag1(edict_t *ent) //チーム1の旗
{
bot_team_flag1 = ent;
}
void SetBotFlag2(edict_t *ent) //チーム2の旗
{
bot_team_flag2 = ent;
}
edict_t *GetBotFlag1() //チーム1の旗
{
return bot_team_flag1;
}
edict_t *GetBotFlag2() //チーム2の旗
{
return bot_team_flag2;
}
qboolean ChkTFlg( void )
{
if(bot_team_flag1 != NULL
&& bot_team_flag2 != NULL) return true;
else return false;
}
void SpawnItem2 (edict_t *ent, gitem_t *item)
{
// PrecacheItem (item);
ent->item = item;
ent->nextthink = level.time ; // items start after other solids
ent->think = droptofloor;
ent->s.effects = 0;
ent->s.renderfx = RF_GLOW;
// if (ent->model)
// gi.modelindex (ent->model);
droptofloor (ent);
ent->s.modelindex = 0;
ent->nextthink = level.time + 100 * FRAMETIME; // items start after other solids
ent->think = G_FreeEdict;
}
//=====================================
//
// BOT用可視判定
//
qboolean Bot_trace (edict_t *ent,edict_t *other)
{
trace_t rs_trace;
vec3_t ttx;
vec3_t tty;
VectorCopy (ent->s.origin,ttx);
VectorCopy (other->s.origin,tty);
if(ent->maxs[2] >=32)
{
if(tty[2] > ttx[2] ) tty[2] += 16;
// else if(ttx[2] > tty[2] > 100 ) tty[2] += 32;
ttx[2] += 30;
}
else
{
ttx[2] -= 12;
}
rs_trace = gi.trace (ttx, NULL, NULL, tty ,ent, CONTENTS_SOLID | CONTENTS_WINDOW | CONTENTS_LAVA | CONTENTS_SLIME /*| CONTENTS_TRANSLUCENT*/);
if(rs_trace.fraction == 1.0 && !rs_trace.allsolid && !rs_trace.startsolid) return true;
if( ent->maxs[2] < 32 ) return false;
if(other->classname[6] == 'F'
|| other->classname[0] == 'w')
{}
else if(other->classname[0]=='i')
{
if(other->classname[5]=='q'
|| other->classname[5]=='f'
|| other->classname[5]=='t'
|| other->classname[5]=='i'
|| other->classname[5]=='h'
|| other->classname[5]=='a'){}
else return false;
}
else return false;
if(rs_trace.ent != NULL)
{
if(rs_trace.ent->classname[0] == 'f'
&& rs_trace.ent->classname[5] == 'd'
&& rs_trace.ent->targetname == NULL) return true;
}
if(ent->s.origin[2] < other->s.origin[2]
|| ent->s.origin[2]-24 > other->s.origin[2]) return false;
ttx[2] -= 36;
rs_trace = gi.trace (ttx, NULL, NULL, other->s.origin ,ent, CONTENTS_SOLID | CONTENTS_WINDOW | CONTENTS_LAVA | CONTENTS_SLIME /*|CONTENTS_TRANSLUCENT*/);
if(rs_trace.fraction == 1.0 && !rs_trace.allsolid && !rs_trace.startsolid) return true;
return false;
}
qboolean Bot_traceX (edict_t *ent,edict_t *other)
{
trace_t rs_trace;
vec3_t ttx,tty;
VectorCopy (ent->s.origin,ttx);
VectorCopy (other->s.origin,tty);
ttx[2] += 16;
tty[2] += 16;
rs_trace = gi.trace (ttx, NULL, NULL, tty ,ent, CONTENTS_SOLID | CONTENTS_WINDOW | CONTENTS_LAVA | CONTENTS_SLIME);
if(rs_trace.fraction == 1.0 ) return true;
return false;
}
qboolean Bot_traceY (edict_t *ent,edict_t *other)
{
trace_t rs_trace;
vec3_t ttx,tty;
VectorCopy (ent->s.origin,ttx);
VectorCopy (other->s.origin,tty);
if(ent->maxs[2] >=32) ttx[2] += 24;
else ttx[2] -= 12;
tty[2] += 16;
rs_trace = gi.trace (ttx, NULL, NULL, tty ,ent, CONTENTS_SOLID | CONTENTS_WINDOW | CONTENTS_LAVA | CONTENTS_SLIME);
if(rs_trace.fraction == 1.0 ) return true;
return false;
}
//
// BOT用可視判定 2
//
qboolean Bot_trace2 (edict_t *ent,vec3_t ttz)
{
trace_t rs_trace;
vec3_t ttx;
VectorCopy (ent->s.origin,ttx);
if(ent->maxs[2] >=32) ttx[2] += 24;
else ttx[2] -= 12;
rs_trace = gi.trace (ttx, NULL, NULL, ttz ,ent, CONTENTS_SOLID | CONTENTS_LAVA | CONTENTS_SLIME /*| CONTENTS_TRANSLUCENT*/);
if(rs_trace.fraction != 1.0 ) return false;
return true;
}
//
// BOT用可視判定 3
//
qboolean Bot_traceS (edict_t *ent,edict_t *other)
{
trace_t rs_trace;
vec3_t start,end;
int mycont;
VectorCopy(ent->s.origin,start);
VectorCopy(other->s.origin,end);
start[2] += ent->viewheight - 8;
end[2] += other->viewheight - 8;
if(Bot[ent->client->zc.botindex].param[BOP_NOSTHRWATER]) goto WATERMODE;
rs_trace = gi.trace (start, NULL, NULL, end ,ent,CONTENTS_SOLID | CONTENTS_WINDOW | CONTENTS_LAVA | CONTENTS_SLIME);
if(rs_trace.fraction != 1.0 ) return false;
return true;
WATERMODE:
mycont = gi.pointcontents(start);
if((mycont & CONTENTS_WATER) && !other->waterlevel)
{
rs_trace = gi.trace (end, NULL, NULL, start ,ent,CONTENTS_SOLID | CONTENTS_WINDOW | CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER);
if(rs_trace.surface)
{
if(rs_trace.surface->flags & SURF_WARP) return false;
}
rs_trace = gi.trace (start, NULL, NULL, end ,ent,CONTENTS_SOLID | CONTENTS_WINDOW | CONTENTS_LAVA | CONTENTS_SLIME);
if(rs_trace.fraction != 1.0 ) return false;
return true;
}
else if((mycont & CONTENTS_WATER) && other->waterlevel)
{
VectorCopy(other->s.origin,end);
end[2] -= 16;
rs_trace = gi.trace (start, NULL, NULL, end ,ent,CONTENTS_SOLID | CONTENTS_WINDOW );
if(rs_trace.fraction != 1.0 ) return false;
return true;
}
if(other->waterlevel)
{
VectorCopy(other->s.origin,end);
end[2] += 32;
rs_trace = gi.trace (start, NULL, NULL, end ,ent,CONTENTS_SOLID | CONTENTS_WINDOW | CONTENTS_WATER);
if(rs_trace.surface)
{
if(rs_trace.surface->flags & SURF_WARP) return false;
}
// if(rs_trace.fraction != 1.0 ) return false;
// return true;
}
rs_trace = gi.trace (start, NULL, NULL, end ,ent,CONTENTS_SOLID | CONTENTS_WINDOW | CONTENTS_LAVA | CONTENTS_SLIME);
if(rs_trace.fraction != 1.0 ) return false;
return true;
}
//
// VEC値からyawを得る
//
float Get_yaw (vec3_t vec)
{
vec3_t out;
double yaw;
VectorCopy(vec,out);
out[2] = 0;
VectorNormalize2 (out, out);
yaw = acos((double) out[0]);
// yaw = (float) yaw;
yaw = yaw / M_PI * 180;
if(asin((double) out[1]) < 0 ) yaw *= -1;
return (float)yaw;
}
float Get_pitch (vec3_t vec)
{
vec3_t out;
float pitch;
VectorNormalize2 (vec, out);
pitch = acos((double) out[2]);
// yaw = (float) yaw;
pitch = ((float)pitch) / M_PI * 180;
// if(asin((double) out[0]) < 0 ) pitch *= -1;
pitch -= 90;
if(pitch < -180) pitch += 360;
return pitch;
}
//
// VEC値とyaw値の角度差を得る
//
float Get_vec_yaw (vec3_t vec,float yaw)
{
float vecsyaw;
vecsyaw = Get_yaw (vec);
if(vecsyaw > yaw) vecsyaw -= yaw;
else vecsyaw = yaw - vecsyaw;
if(vecsyaw >180 ) vecsyaw = 360 - vecsyaw;
return vecsyaw;
}
//yaw に対するvecの相対
float Get_vec_yaw2 (vec3_t vec,float yaw)
{
float vecsyaw;
vecsyaw = Get_yaw (vec);
vecsyaw -= yaw;
if(vecsyaw >180 ) vecsyaw -= 360;
else if(vecsyaw <-180 ) vecsyaw += 360;
return vecsyaw;
}

429
src/bot.h Normal file
View file

@ -0,0 +1,429 @@
#ifndef BOTHEAD
#define BOTHEAD
#include "g_local.h"
//general func
void player_die (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
//bot spawn & remove
qboolean SpawnBot(int i);
void Bot_LevelChange();
void Load_BotInfo();
void Bot_SpawnCall();
void RemoveBot();
void SpawnBotReserving();
//weapon
void Weapon_Blaster (edict_t *ent);
void Weapon_Shotgun (edict_t *ent);
void Weapon_SuperShotgun (edict_t *ent);
void Weapon_Machinegun (edict_t *ent);
void Weapon_Chaingun (edict_t *ent);
void Weapon_HyperBlaster (edict_t *ent);
void Weapon_RocketLauncher (edict_t *ent);
void Weapon_Grenade (edict_t *ent);
void Weapon_GrenadeLauncher (edict_t *ent);
void Weapon_Railgun (edict_t *ent);
void Weapon_BFG (edict_t *ent);
void CTFWeapon_Grapple (edict_t *ent);
// RAFAEL
void Weapon_Ionripper (edict_t *ent);
void Weapon_Phalanx (edict_t *ent);
void Weapon_Trap (edict_t *ent);
// wideuse
qboolean Bot_trace (edict_t *ent,edict_t *other);
qboolean Bot_trace2 (edict_t *ent,vec3_t ttz);
float Get_yaw (vec3_t vec); //
float Get_pitch (vec3_t vec); //
float Get_vec_yaw (vec3_t vec,float yaw);
void ShowGun(edict_t *ent);
void SpawnItem3 (edict_t *ent, gitem_t *item);
int Bot_moveT ( edict_t *ent,float ryaw,vec3_t pos,float dist,float *bottom);
void Set_BotAnim(edict_t *ent,int anim,int frame,int end);
void plat_go_up (edict_t *ent);
int Get_KindWeapon(gitem_t *it);
qboolean TargetJump(edict_t *ent,vec3_t tpos);
qboolean Bot_traceS (edict_t *ent,edict_t *other);
qboolean Bot_Fall(edict_t *ent,vec3_t pos,float dist);
void SelectSpawnPoint (edict_t *ent, vec3_t origin, vec3_t angles);
void ClientUserinfoChanged (edict_t *ent, char *userinfo);
void CopyToBodyQue (edict_t *ent);
//route util
qboolean TraceX (edict_t *ent,vec3_t p2);
void Move_LastRouteIndex();
void Get_RouteOrigin(int index,vec3_t pos);
//Bot Func
void ZigockJoinMenu(edict_t *ent);
qboolean ZigockStartClient(edict_t *ent);
void Cmd_AirStrike(edict_t *ent);
void BotEndServerFrame (edict_t *ent);
void SpawnItem2 (edict_t *ent, gitem_t *item);
void Get_WaterState(edict_t *ent);
void Bot_Think (edict_t *self);
void PutBotInServer (edict_t *ent);
void SpawnBotReserving2(int *red,int *blue);
//Combat AI
void Combat_Level0(edict_t *ent,int foundedenemy,int enewep,float aim,float distance,int skill);
void Combat_LevelX(edict_t *ent,int foundedenemy,int enewep,float aim,float distance,int skill);
void UsePrimaryWeapon(edict_t *ent);
//Explotion Index
void UpdateExplIndex(edict_t* ent);
//flag
qboolean ZIGDrop_Flag(edict_t *ent, gitem_t *item);
//p_view.c
void BotEndServerFrame (edict_t *ent);
//Bot AI routine
void Bots_Move_NORM (edict_t *ent); //normal AI
//spawn
void SetBotFlag1(edict_t *ent); //チーム1の旗
void SetBotFlag2(edict_t *ent); //チーム2の旗
void CTFSetupNavSpawn(); //ナビの設置
//ctf
void CTFJobAssign (void); //job assign
//VWep
// ### Hentai ### BEGIN
//void ShowGun(edict_t *ent);
// ### Hentai ### END
//----------------------------------------------------------------
//moving speed
#define MOVE_SPD_WALK 20
#define MOVE_SPD_RUN 32
#define MOVE_SPD_DUCK 10
#define MOVE_SPD_WATER 16
#define MOVE_SPD_JUMP 32
#define VEL_BOT_JUMP 340//341 //jump vel
//#define VEL_BOT_ROCJ 500 //roc jump
#define VEL_BOT_WJUMP 341//150 //waterjump vel
#define VEL_BOT_LADRUP 200 //ladderup vel
#define VEL_BOT_WLADRUP 200 //0 //water ladderup gain
//classes
#define CLS_NONE 0 //normal
#define CLS_ALPHA 1 //sniper
#define CLS_BETA 2
#define CLS_GAMMA 3
#define CLS_DELTA 4
#define CLS_EPSILON 5
#define CLS_ZETA 6
#define CLS_ETA 7
// function's state P
#define PSTATE_TOP 0
#define PSTATE_BOTTOM 1
#define PSTATE_UP 2
#define PSTATE_DOWN 3
#define PDOOR_TOGGLE 32
// height
#define TOP_LIMIT 52
#define TOP_LIMIT_WATER 100
#define BOTTOM_LIMIT -52
#define BOTTOM_LIMIT_WATER -8190
#define BOTTOM_LIMITM -300
//waterstate
#define WAS_NONE 0
#define WAS_FLOAT 1
#define WAS_IN 2
//route
//chaining pod state
#define GRS_NORMAL 0
#define GRS_ONROTATE 1
#define GRS_TELEPORT 2
#define GRS_ITEMS 3
#define GRS_ONPLAT 4
#define GRS_ONTRAIN 5
#define GRS_ONDOOR 6
#define GRS_PUSHBUTTON 7
#define GRS_GRAPSHOT 20
#define GRS_GRAPHOOK 21
#define GRS_GRAPRELEASE 22
#define GRS_REDFLAG -10
#define GRS_BLUEFLAG -11
#define POD_LOCKFRAME 15 //20
#define POD_RELEFRAME 20 //25
#define MAX_SEARCH 12 //max search count/FRAMETIME
#define MAX_DOORSEARCH 10
//trace param
#define TRP_NOT 0 //don't trace
#define TRP_NORMAL 1 //trace normal
#define TRP_ANGLEKEEP 2 //trace and keep angle
#define TRP_MOVEKEEP 3 //angle and move vec keep but move
#define TRP_ALLKEEP 4 //don't move
// bot spawning status
#define BOT_SPAWNNOT 0
#define BOT_SPRESERVED 1
#define BOT_SPAWNED 2
#define BOT_NEXTLEVEL 3
//combat
#define AIMING_POSGAP 5
#define AIMING_ANGLEGAP_S 0.75 //shot gun
#define AIMING_ANGLEGAP_M 0.35 //machine gun
//team play state
#define TMS_NONE 0
#define TMS_LEADER 1
#define TMS_FOLLOWER 2
//ctf state
#define CTFS_NONE 0
#define CTFS_CARRIER 1
#define CTFS_ROAMER 2
#define CTFS_OFFENCER 3
#define CTFS_DEFENDER 4
#define CTFS_SUPPORTER 5
#define FOR_FLAG1 1
#define FOR_FLAG2 2
//fire----------------------------------------------------------
#define FIRE_SLIDEMODE 0x00000001 //slide with route
#define FIRE_PRESTAYFIRE 0x00000002 //X pre don't move fire
#define FIRE_STAYFIRE 0x00000004 //X don't move
#define FIRE_CHIKEN 0x00000008 //X chiken fire
#define FIRE_RUSH 0x00000010 //X rush
#define FIRE_JUMPNRUSH 0x00000020 //
#define FIRE_ESTIMATE 0x00000040 //X estimate 予測
#define FIRE_SCATTER 0x00000080 //scatter バラ撒き
#define FIRE_RUNNIN 0x00000100 //run & shot(normal)
#define FIRE_JUMPROC 0x00000200 //X ジャンプふぁいあ
#define FIRE_REFUGE 0x00001000 //X 避難
#define FIRE_EXPAVOID 0x00002000 //X 爆発よけ
#define FIRE_QUADUSE 0x00004000 //X Quad時の連射武器選択
#define FIRE_AVOIDINV 0x00008000 //X 相手がペンタの時逃げる
#define FIRE_BFG 0x00010000 //X 普通にBFGを撃つ
#define FIRE_SHIFT_R 0x00020000 //X 右スライド
#define FIRE_SHIFT_L 0x00040000 //X 左スライド
#define FIRE_SHIFT (FIRE_SHIFT_R | FIRE_SHIFT_L)//X 右スライド
#define FIRE_REFLECT 0x00080000 // 壁に反射させる
#define FIRE_IGNORE 0x10000000 //無視して逃げる
// means of death
#define MOD_SNIPERAIL 50 //SNIPE RAIL
#define MOD_LOCMISSILE 51 //LOCKON MISSILE
#define MOD_BFG100K 52 //
#define MOD_AIRSTRIKE 70 //AIRSTRIKE
//----------------------------------------------------------------
//general status list
#define STS_IDLE 0x00000000 //normal running
#define STS_THINK 0x00000001 //stand and analise
#define STS_LADDERUP 0x00000002 //crimb the ladder
#define STS_ROCJ 0x00000004 //rocket jumping
#define STS_TURBOJ 0x00000008 //turbo jump
#define STS_WATERJ 0x00000010 //turbo jump
#define STS_SJMASK (STS_ROCJ | STS_TURBOJ | STS_WATERJ) //special jump mask
#define STS_SJMASKEXW (STS_ROCJ | STS_TURBOJ ) //special jump mask ex. water
#define STS_TALKING 0x00000200 //talking
#define STS_ESC_WXPL 0x00000400 //escape from explode
#define STS_MOVE_WPOINT 0x00000800 //moving waiting point
//#define STS_W_EXPL 0x00001000 //wait for end of explode
//wait
#define STS_W_DONT 0x00001000 //don't wait door or plat
#define STS_W_DOOROPEN 0x00002000 //wait for door open or down to bottom
#define STS_W_ONPLAT 0x00004000 //wait for plat or door reach to da top
#define STS_W_ONDOORUP 0x00008000 //wait for door reach to da top
#define STS_W_ONDOORDWN 0x00010000 //wait for door reach to da bottom
#define STS_W_ONTRAIN 0x00020000 //wait for plat or door reach to da top
#define STS_W_COMETRAIN 0x00040000 //wait for train come
#define STS_W_COMEPLAT 0x00080000 //wait for plat come
#define STS_WAITS (STS_W_DONT | STS_W_DOOROPEN | STS_W_COMEPLAT | STS_W_ONPLAT | STS_W_ONDOORUP | STS_W_ONDOORDWN | STS_W_ONTRAIN)
#define STS_WAITSMASK (STS_W_DOOROPEN | STS_W_ONPLAT | STS_W_ONDOORUP | STS_W_COMEPLAT | STS_W_ONDOORDWN | STS_W_ONTRAIN)
#define STS_WAITSMASK2 (STS_W_ONDOORDWN |STS_W_ONDOORUP | STS_W_ONPLAT | STS_W_ONTRAIN)
#define STS_WAITSMASKCOM (STS_W_DOOROPEN | STS_W_ONPLAT | STS_W_ONDOORUP | STS_W_ONDOORDWN | STS_W_ONTRAIN)
//----------------------------------------------------------------
//general status list
#define CTS_ENEM_NSEE 0x00000001 //have enemy but can't see
#define CTS_AGRBATTLE 0x00000002 //aglessive battle
#define CTS_ESCBATTLE 0x00000004 //escaping battle(item want)
#define CTS_HIPBATTLE 0x00000008 //high position battle(camp)
//shoot
#define CTS_PREAIMING 0x00000010 //prepare for snipe or lockon
#define CTS_AIMING 0x00000020 //aimning for snipe or lockon
#define CTS_GRENADE 0x00000040 //hand grenade mode
#define CTS_JUMPSHOT 0x00000080 //jump shot
#define CTS_COMBS (CTS_AGRBATTLE | CTS_ESCBATTLE | CTS_HIPBATTLE | CTS_ENEM_NSEE)
//----------------------------------------------------------------
//route struct
#define MAXNODES 10000 //5000 added 5000 pods
#define MAXLINKPOD 6 //don't modify this
#define CTF_FLAG1_FLAG 0x0000
#define CTF_FLAG2_FLAG 0x8000
typedef struct
{
vec3_t Pt; //target point
union
{
vec3_t Tcourner; //target courner(train and grap-shot only)
unsigned short linkpod[MAXLINKPOD]; //(GRS_NORMAL,GRS_ITEMS only 0 = do not select pod)
};
edict_t *ent; //target ent
short index; //index num
short state; //targetstate
} route_t;
//----------------------------------------------------------------
//bot info struct
#define MAXBOTS 64
#define MAXBOP 16
// bot params
#define BOP_WALK 0 //flags
#define BOP_AIM 1 //aiming
#define BOP_PICKUP 2 //frq PICKUP
#define BOP_OFFENCE 3 //chiken fire etc.
#define BOP_COMBATSKILL 4 //combat skill
#define BOP_ROCJ 5 //rocket jump
#define BOP_REACTION 6 //reaction skill exp. frq SEARCH ENEMY
#define BOP_VRANGE 7 //V-View of RANGE 縦
#define BOP_HRANGE 8 //H-View of Range 横
#define BOP_PRIWEP 9 //primary weapon
#define BOP_SECWEP 10 //secondary weapon
#define BOP_DODGE 11 //dodge
#define BOP_ESTIMATE 12 //estimate
#define BOP_NOISECHK 13 //noisecheck
#define BOP_NOSTHRWATER 14 //can't see through water
#define BOP_TEAMWORK 15 //teamwork
typedef struct
{
char netname[21]; //netname
char model[21]; //model
char skin[21]; //skin
int spflg; //spawned flag 0-not 1-waiting 2-spawned
int team; //team NO. 0-noteam 1-RED 2-BLUE
int arena; //if arena is on
unsigned char param[MAXBOP]; //Params
} botinfo_t;
//----------------------------------------------------------------
//message section name
#define MESS_DEATHMATCH "[MessDeathMatch]"
#define MESS_CHAIN_DM "[MessChainDM]"
#define MESS_CTF "[MessCTF]"
#define MESS_CHAIN_CTF "[MessChainCTF]"
//----------------------------------------------------------------
//bot list section name
#define BOTLIST_SECTION_DM "[BotList]"
#define BOTLIST_SECTION_TM "[BotListTM]"
//----------------------------------------------------------------
#define MAX_BOTSKILL 10
#define FALLCHK_LOOPMAX 30
//laser Index
#define MAX_LASERINDEX 30
extern edict_t* LaserIndex[MAX_LASERINDEX];
//Explotion Index
#define MAX_EXPLINDEX 12
extern edict_t* ExplIndex[MAX_EXPLINDEX];
//
extern int cumsindex;
extern int targetindex; //debugtarget
extern int ListedBotCount; //bot count of list
extern int SpawnWaitingBots;
extern char ClientMessage[MAX_STRING_CHARS];
extern botinfo_t Bot[MAXBOTS];
extern route_t Route[MAXNODES];
extern int CurrentIndex;
extern float JumpMax;
extern int botskill;
extern int trace_priority;
extern int FFlg[MAX_BOTSKILL];
extern int ListedBots;
//for avoid abnormal frame error
extern int skullindex;
extern int headindex;
extern gitem_t *zflag_item;
extern edict_t *zflag_ent;
extern int zigflag_spawn;
//item index
extern int mpindex[MPI_INDEX];
//PON-CTF
extern edict_t *bot_team_flag1;
extern edict_t *bot_team_flag2;
//PON-CTF
//pre searched items
extern gitem_t *Fdi_GRAPPLE;
extern gitem_t *Fdi_BLASTER;
extern gitem_t *Fdi_SHOTGUN;
extern gitem_t *Fdi_SUPERSHOTGUN;
extern gitem_t *Fdi_MACHINEGUN;
extern gitem_t *Fdi_CHAINGUN;
extern gitem_t *Fdi_GRENADES;
extern gitem_t *Fdi_GRENADELAUNCHER;
extern gitem_t *Fdi_ROCKETLAUNCHER;
extern gitem_t *Fdi_HYPERBLASTER;
extern gitem_t *Fdi_RAILGUN;
extern gitem_t *Fdi_BFG;
extern gitem_t *Fdi_PHALANX;
extern gitem_t *Fdi_BOOMER;
extern gitem_t *Fdi_TRAP;
extern gitem_t *Fdi_SHELLS;
extern gitem_t *Fdi_BULLETS;
extern gitem_t *Fdi_CELLS;
extern gitem_t *Fdi_ROCKETS;
extern gitem_t *Fdi_SLUGS;
extern gitem_t *Fdi_MAGSLUGS;
extern float ctfjob_update;
#endif

1822
src/bot_fire.c Normal file

File diff suppressed because it is too large Load diff

1079
src/bot_func.c Normal file

File diff suppressed because it is too large Load diff

5903
src/bot_za.c Normal file

File diff suppressed because it is too large Load diff

98
src/botstr.h Normal file
View file

@ -0,0 +1,98 @@
#ifndef BOTSTRUCT
#define BOTSTRUCT
//Zigock client info
#define ALEAT_MAX 10
typedef struct zgcl_s
{
int zclass; //class no.
int botindex; //botlist's index NO.
// true client—p zoom ƒtƒ‰ƒO
int aiming; //0-not 1-aiming 2-firing zoomingflag
float distance; //zoomÌFOVl
float olddistance; //Œzooming FOVl
qboolean autozoom; //autozoom
qboolean lockon; //lockon flag false-not true-locking
// bot—p
int zcstate; //status
int zccmbstt; //combat status
//duck
float n_duckedtime; //non ducked time
//targets
edict_t *first_target; //enemy uses LockOntarget(for client)
float targetlock; //target locking time
short firstinterval; //enemy search count
edict_t *second_target; //kindof items
short secondinterval; //item pickup call count
//waiting
vec3_t movtarget_pt; //moving target waiting point
edict_t *waitin_obj; //for waiting sequence complete
//basical moving
float moveyaw; //true moving yaw
//combat
int total_bomb; //total put bomb
float gren_time; //grenade time
//contents
// int front_contents;
int ground_contents;
float ground_slope;
//count (inc only)
int tmpcount;
//moving hist
float nextcheck; //checking time
vec3_t pold_origin; //old origin
vec3_t pold_angles; //old angles
//target object shot
qboolean objshot;
edict_t *sighten; //sighting enemy to me info from entity sight
edict_t *locked; //locking enemy to me info from lockon missile
//waterstate
int waterstate;
//route
qboolean route_trace;
int routeindex; //routing index
float rt_locktime;
float rt_releasetime;
qboolean havetarget; //target on/off
int targetindex;
//battle
edict_t *last_target; //old enemy
vec3_t last_pos; //old origin
int battlemode; //mode
int battlecount; //temporary count
int battlesubcnt; //subcount
int battleduckcnt; //duck
float fbattlecount; //float temoporary count
vec3_t vtemp; //temporary vec
int foundedenemy; //foundedenemy
char secwep_selected;//secondweapon selected
vec3_t aimedpos; //shottenpoint
qboolean trapped; //trapflag
//team
short tmplstate; //teamplay state
short ctfstate; //ctf state
edict_t *followmate; //follow
float matelock; //team mate locking time
} zgcl_t;
#endif

155
src/g_chase.c Normal file
View file

@ -0,0 +1,155 @@
#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->client->resp.spectator) {
edict_t *old = ent->client->chase_target;
ChaseNext(ent);
if (ent->client->chase_target == old) {
ent->client->chase_target = NULL;
ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
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;
}
if (targ->deadflag)
ent->client->ps.pmove.pm_type = PM_DEAD;
else
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]);
if (targ->deadflag) {
ent->client->ps.viewangles[ROLL] = 40;
ent->client->ps.viewangles[PITCH] = -15;
ent->client->ps.viewangles[YAW] = targ->client->killer_yaw;
} else {
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);
}
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;
if (!e->client->resp.spectator)
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;
if (!e->client->resp.spectator)
break;
} while (e != ent->client->chase_target);
ent->client->chase_target = e;
ent->client->update_chase = true;
}
void GetChaseTarget(edict_t *ent)
{
int i;
edict_t *other;
for (i = 1; i <= maxclients->value; i++) {
other = g_edicts + i;
if (other->inuse && !other->client->resp.spectator) {
ent->client->chase_target = other;
ent->client->update_chase = true;
UpdateChaseCam(ent);
return;
}
}
gi.centerprintf(ent, "No other players to chase.");
}

1168
src/g_cmds.c Normal file

File diff suppressed because it is too large Load diff

542
src/g_combat.c Normal file
View file

@ -0,0 +1,542 @@
// g_combat.c
#include "g_local.h"
#include "bot.h"
/*
============
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++;
// 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);
}
*/
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(Fdi_CELLS/*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 = 2;
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;
}
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;
}
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;
if (!targ->takedamage)
return;
if(mod == MOD_CRUSH)
{
//bot's state change
if((targ->svflags & SVF_MONSTER) && targ->client)
{
if((targ->client->zc.waitin_obj == inflictor && targ->client->zc.zcstate)
|| targ->groundentity == inflictor)
{
// gi.bprintf(PRINT_HIGH,"MOOOOOOOOOOOOOOOOOOOO\n");
targ->client->zc.zcstate |= STS_W_DONT;
}
}
}
// 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 (OnSameTeam (targ, attacker))
{
if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE)
damage = 0;
else
mod |= MOD_FRIENDLY_FIRE;
}
else if(targ->client && !(targ->svflags & SVF_MONSTER))
{
if(attacker->client) targ->client->zc.first_target = attacker;
}
}
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);
// bonus damage for suprising a monster
if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0))
damage *= 2;
//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/protect3.wav"), 1, ATTN_NORM, 0);
// 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;
}
//ZOID
//team armor protect
if (ctf->value && targ->client && attacker->client &&
targ->client->resp.ctf_team == attacker->client->resp.ctf_team &&
targ != attacker && ((int)dmflags->value & DF_ARMOR_PROTECT)) {
psave = asave = 0;
} else {
//ZOID
psave = CheckPowerArmor (targ, point, normal, take, dflags);
take -= psave;
asave = CheckArmor (targ, point, normal, take, te_sparks, dflags);
take -= asave;
}
//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
// do the damage
if (take)
{
if ((targ->svflags & SVF_MONSTER) || (client))
{
SpawnDamage (TE_BLOOD, point, normal, take);
if(client && (targ->svflags & SVF_MONSTER) && attacker)
{
if(client->zc.battlemode & FIRE_CHIKEN) client->zc.battlemode &= ~FIRE_CHIKEN;
if(mod == MOD_RAILGUN
|| mod == MOD_BFG_LASER
|| mod == MOD_ROCKET
|| mod == MOD_BLASTER
|| mod == MOD_RIPPER
|| mod == MOD_HYPERBLASTER
|| mod == MOD_PHALANX)
{
if(attacker->client
&& (9 * random() < Bot[client->zc.botindex].param[BOP_REACTION])
&& !client->zc.first_target)
{
if(!OnSameTeam (targ, attacker)) client->zc.first_target = attacker;
}
}
}
}
else
SpawnDamage (te_sparks, point, normal, take);
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 (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;
vec3_t v;
vec3_t dir;
while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
{
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);
T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);
}
}
}
}

3227
src/g_ctf.c Normal file

File diff suppressed because it is too large Load diff

136
src/g_ctf.h Normal file
View file

@ -0,0 +1,136 @@
#ifndef _CTF
#define _CTF
#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
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;
extern cvar_t *ctf;
#define CTF_TEAM1_SKIN "ctf_r"
#define CTF_TEAM2_SKIN "ctf_b"
#define DF_CTF_FORCEJOIN 131072
#define DF_ARMOR_PROTECT 262144
#define DF_CTF_NO_TECH 524288
#define CTF_CAPTURE_BONUS 15 // what you get for capture
#define CTF_TEAM_BONUS 10 // what your team gets for capture
#define CTF_RECOVERY_BONUS 1 // what you get for recovery
#define CTF_FLAG_BONUS 0 // what you get for picking up enemy flag
#define CTF_FRAG_CARRIER_BONUS 2 // what you get for fragging enemy flag carrier
#define CTF_FLAG_RETURN_TIME 40 // seconds until auto return
#define CTF_CARRIER_DANGER_PROTECT_BONUS 2 // bonus for fraggin someone who has recently hurt your flag carrier
#define CTF_CARRIER_PROTECT_BONUS 1 // bonus for fraggin someone while either you or your target are near your flag carrier
#define CTF_FLAG_DEFENSE_BONUS 1 // bonus for fraggin someone while either you or your target are near your flag
#define CTF_RETURN_FLAG_ASSIST_BONUS 1 // awarded for returning a flag that causes a capture to happen almost immediately
#define CTF_FRAG_CARRIER_ASSIST_BONUS 2 // award for fragging a flag carrier if a capture happens almost immediately
#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 30 // 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);
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);
#endif

2397
src/g_func.c Normal file

File diff suppressed because it is too large Load diff

3265
src/g_items.c Normal file

File diff suppressed because it is too large Load diff

1257
src/g_local.h Normal file

File diff suppressed because it is too large Load diff

626
src/g_main.c Normal file
View file

@ -0,0 +1,626 @@
#include "g_local.h"
#include "bot.h"
game_locals_t game;
level_locals_t level;
game_import_t gi;
game_export_t globals;
spawn_temp_t st;
int sm_meat_index;
int snd_fry;
int meansOfDeath;
edict_t *g_edicts;
cvar_t *deathmatch;
cvar_t *coop;
cvar_t *dmflags;
cvar_t *skill;
cvar_t *fraglimit;
cvar_t *timelimit;
cvar_t *filterban;
//ZOID
cvar_t *capturelimit;
//ZOID
cvar_t *password;
cvar_t *spectator_password;
cvar_t *maxclients;
cvar_t *maxspectators;
cvar_t *maxentities;
cvar_t *g_select_empty;
cvar_t *dedicated;
cvar_t *sv_maxvelocity;
cvar_t *sv_gravity;
cvar_t *sv_rollspeed;
cvar_t *sv_rollangle;
cvar_t *gun_x;
cvar_t *gun_y;
cvar_t *gun_z;
cvar_t *run_pitch;
cvar_t *run_roll;
cvar_t *bob_up;
cvar_t *bob_pitch;
cvar_t *bob_roll;
cvar_t *sv_cheats;
//ponpoko
cvar_t *gamepath;
cvar_t *chedit;
cvar_t *vwep;
cvar_t *maplist;
cvar_t *botlist;
cvar_t *autospawn;
cvar_t *zigmode;
float spawncycle;
float ctfjob_update;
//ponpoko
void SpawnEntities (char *mapname, char *entities, char *spawnpoint);
void ClientThink (edict_t *ent, usercmd_t *cmd);
qboolean ClientConnect (edict_t *ent, char *userinfo, qboolean loadgame);
void ClientUserinfoChanged (edict_t *ent, char *userinfo);
void ClientDisconnect (edict_t *ent);
void ClientBegin (edict_t *ent, qboolean loadgame);
void ClientCommand (edict_t *ent);
void RunEntity (edict_t *ent);
void WriteGame (char *filename);
void ReadGame (char *filename);
void WriteLevel (char *filename);
void ReadLevel (char *filename);
void InitGame (void);
void G_RunFrame (void);
void SetBotFlag1(edict_t *ent); //チーム1の旗
void SetBotFlag2(edict_t *ent); //チーム2の旗
//===================================================================
/*
=================
GetGameAPI
Returns a pointer to the structure with all entry points
and global variables
=================
*/
void ShutdownGame (void)
{
gi.dprintf ("==== ShutdownGame ====\n");
// Bot_LevelChange();
gi.FreeTags (TAG_LEVEL);
gi.FreeTags (TAG_GAME);
SetBotFlag1(NULL);
SetBotFlag2(NULL);
}
//void Dummy (void) {};
game_export_t *GetGameAPI (game_import_t *import)
{
gi = *import;
globals.apiversion = GAME_API_VERSION;
globals.Init = InitGame;
globals.Shutdown = ShutdownGame;
globals.SpawnEntities = SpawnEntities;
globals.WriteGame = WriteGame;
globals.ReadGame = ReadGame;
globals.WriteLevel = WriteLevel;
globals.ReadLevel = ReadLevel;
globals.ClientThink = ClientThink;
globals.ClientConnect = ClientConnect;
globals.ClientUserinfoChanged = ClientUserinfoChanged;
globals.ClientDisconnect = ClientDisconnect;
globals.ClientBegin = ClientBegin;
globals.ClientCommand = ClientCommand;
globals.RunFrame = G_RunFrame;
globals.ServerCommand = ServerCommand;
globals.edict_size = sizeof(edict_t);
return &globals;
}
#ifndef GAME_HARD_LINKED
// this is only here so the functions in q_shared.c and q_shwin.c can link
void Sys_Error (char *error, ...)
{
va_list argptr;
char text[1024];
va_start (argptr, error);
vsprintf (text, error, argptr);
va_end (argptr);
gi.error (ERR_FATAL, "%s", text);
}
void Com_Printf (char *msg, ...)
{
va_list argptr;
char text[1024];
va_start (argptr, msg);
vsprintf (text, msg, argptr);
va_end (argptr);
gi.dprintf ("%s", text);
}
#endif
//======================================================================
/*
=================
ClientEndServerFrames
=================
*/
void ClientEndServerFrames (void)
{
int i;
edict_t *ent;
// calc the player views now that all pushing
// and damage has been added
for (i=0 ; i<maxclients->value ; i++)
{
ent = g_edicts + 1 + i;
if (!ent->inuse || !ent->client)
continue;
if(!(ent->svflags & SVF_MONSTER))
ClientEndServerFrame (ent);
}
}
/*
=================
GetNextMap
get next map's file name
=================
*/
void Get_NextMap()
{
FILE *fp;
qboolean firstflag = false;
char Buff[MAX_QPATH];
char top[MAX_QPATH];
char nextmap[MAX_QPATH];
int i;
if(!maplist->string) return;
sprintf(Buff,".\\%s\\3ZBMAPS.LST",gamepath->string);
fp = fopen(Buff,"r");
if(fp == NULL) return;
//search section
while(1)
{
if(fgets( Buff, sizeof(Buff), fp ) == NULL) goto NONEXTMAP;
if(Buff[0] != '[') continue;
i = 0;
while(1)
{
if(Buff[i] == ']') Buff[i] = 0;
if(Buff[i] == 0) break;
if(++i >= sizeof(Buff))
{
Buff[i - 1] = 0;
break;
}
}
//compare map section name
if(Q_stricmp (&Buff[1], maplist->string) == 0) break;
}
//search current mapname
while(1)
{
if(fgets( Buff, sizeof(Buff), fp ) == NULL) goto NONEXTMAP;
if(Buff[0] == '[')
{
if( firstflag )
{
strcpy(nextmap,top);
goto SETNEXTMAP;
}
else goto NONEXTMAP;
}
if(Buff[0] == '\n') continue;
sscanf(Buff,"%s",nextmap);
if(!firstflag)
{
firstflag = true;
strcpy(top,nextmap);
}
if(Q_stricmp (level.mapname, nextmap) == 0) break;
}
//search nextmap
while(1)
{
if(fgets( Buff, sizeof(Buff), fp ) == NULL)
{
if( firstflag )
{
strcpy(nextmap,top);
goto SETNEXTMAP;
}
else goto NONEXTMAP;
}
if(Buff[0] == '[')
{
if( firstflag )
{
strcpy(nextmap,top);
goto SETNEXTMAP;
}
else goto NONEXTMAP;
}
if(Buff[0] == '\n') continue;
sscanf(Buff,"%s",nextmap);
break;
}
SETNEXTMAP:
strcpy(level.nextmap,nextmap);
NONEXTMAP:
fclose(fp);
}
/*
=================
EndDMLevel
The timelimit or fraglimit has been exceeded
=================
*/
void EndDMLevel (void)
{
edict_t *ent;
Get_NextMap();
// stay on same level flag
if ((int)dmflags->value & DF_SAME_LEVEL)
{
ent = G_Spawn ();
ent->classname = "target_changelevel";
ent->map = level.mapname;
}
else if (level.nextmap)
{ // go to a specific map
ent = G_Spawn ();
ent->classname = "target_changelevel";
ent->map = level.nextmap;
}
else
{ // search for a changeleve
ent = G_Find (NULL, FOFS(classname), "target_changelevel");
if (!ent)
{ // the map designer didn't include a changelevel,
// so create a fake ent that goes back to the same level
ent = G_Spawn ();
ent->classname = "target_changelevel";
ent->map = level.mapname;
}
}
BeginIntermission (ent);
//PONKO
Bot_LevelChange();
//PONKO
}
/*
=================
CheckNeedPass
=================
*/
void CheckNeedPass (void)
{
int need;
// if password or spectator_password has changed, update needpass
// as needed
if (password->modified || spectator_password->modified)
{
password->modified = spectator_password->modified = false;
need = 0;
if (*password->string && Q_stricmp(password->string, "none"))
need |= 1;
if (*spectator_password->string && Q_stricmp(spectator_password->string, "none"))
need |= 2;
gi.cvar_set("needpass", va("%d", need));
}
}
/*
=================
CheckDMRules
=================
*/
void CheckDMRules (void)
{
int i;
gclient_t *cl;
if (level.intermissiontime)
return;
if (!deathmatch->value)
return;
if (timelimit->value)
{
if (level.time >= timelimit->value*60)
{
gi.bprintf (PRINT_HIGH, "Timelimit hit.\n");
EndDMLevel ();
return;
}
}
if (fraglimit->value)
{
//ZOID
if (ctf->value) {
if (CTFCheckRules()) {
EndDMLevel ();
}
}
//ZOID
for (i=0 ; i<maxclients->value ; i++)
{
cl = game.clients + i;
if (!g_edicts[i+1].inuse)
continue;
if (cl->resp.score >= fraglimit->value)
{
gi.bprintf (PRINT_HIGH, "Fraglimit hit.\n");
EndDMLevel ();
return;
}
}
}
}
/*
=============
ExitLevel
=============
*/
void ExitLevel (void)
{
int i;
edict_t *ent;
char command [256];
Com_sprintf (command, sizeof(command), "gamemap \"%s\"\n", level.changemap);
gi.AddCommandString (command);
level.changemap = NULL;
level.exitintermission = 0;
level.intermissiontime = 0;
ClientEndServerFrames ();
// clear some things before going to next level
for (i=0 ; i<maxclients->value ; i++)
{
ent = g_edicts + 1 + i;
if (!ent->inuse)
continue;
if (ent->health > ent->client->pers.max_health)
ent->health = ent->client->pers.max_health;
}
SetBotFlag1(NULL);
SetBotFlag2(NULL);
//ZOID
CTFInit();
//ZOID
}
/*
================
G_RunFrame
Advances the world by 0.1 seconds
================
*/
void G_InitEdict (edict_t *e);
void G_RunFrame (void)
{
int i,j;
static int ofs;
static float next_fragadd = 0;
edict_t *ent;
vec3_t v,vv;
qboolean haveflag;
gitem_t *item;
level.framenum++;
level.time = level.framenum*FRAMETIME;
// choose a client for monsters to target this frame
// AI_SetSightClient ();
// exit intermissions
if (level.exitintermission)
{
ExitLevel ();
return;
}
//
// Bot Spawning
//
if(SpawnWaitingBots && !level.intermissiontime)
{
if(spawncycle < level.time)
{
Bot_SpawnCall();
spawncycle = level.time + FRAMETIME * 10 + 0.01 * SpawnWaitingBots;
}
}
else
{
if(spawncycle < level.time) spawncycle = level.time + FRAMETIME * 10;
}
//
// treat each object in turn
// even the world gets a chance to think
//
haveflag = false;
ent = &g_edicts[0];
for (i=0 ; i<globals.num_edicts ; i++, ent++)
{
if (!ent->inuse)
continue;
level.current_entity = ent;
VectorCopy (ent->s.origin, ent->s.old_origin);
// if the ground entity moved, make sure we are still on it
if ((ent->groundentity) && (ent->groundentity->linkcount != ent->groundentity_linkcount))
{
ent->groundentity = NULL;
if ( !(ent->flags & (FL_SWIM|FL_FLY)) && (ent->svflags & SVF_MONSTER) )
{
M_CheckGround (ent);
}
}
//ctf job assign
if(ctf->value)
{
if(ctfjob_update < level.time)
{
//gi.bprintf(PRINT_HIGH,"Assigned!!!\n");
CTFJobAssign();
ctfjob_update = level.time + FRAMETIME * 2;
}
}
//////////旗のスコアチェック
if(zigmode->value == 1)
{
if(next_fragadd < level.time)
{
if(i > 0 && i <= maxclients->value && g_edicts[i].client)
{
if(g_edicts[i].client->pers.inventory[ITEM_INDEX(zflag_item)])
{
zflag_ent = NULL;
haveflag = true;
gi.sound(ent, CHAN_VOICE, gi.soundindex("misc/secret.wav"), 1, ATTN_NORM, 0);
if (!((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS)))
g_edicts[i].client->resp.score += 1;
else
{
//旗を持ってるとフラッグを足す
for ( j = 1 ; j <= maxclients->value ; j++)
{
if(g_edicts[j].inuse)
{
if(OnSameTeam(&g_edicts[i],&g_edicts[j]))
g_edicts[j].client->resp.score += 1;
}
}
}
}
}
if(zflag_ent != NULL)
{
if(!zflag_ent->inuse)
{
// item = FindItem("Zig Flag");
SelectSpawnPoint (ent, v, vv);
// VectorCopy (v, ent->s.origin);
if(ZIGDrop_Flag(ent,zflag_item))
{
VectorCopy (v, zflag_ent->s.origin);
}
}
}
}
}
/////////////
if (i > 0 && i <= maxclients->value && !(ent->svflags & SVF_MONSTER))
{
ClientBeginServerFrame (ent);
continue;
}
G_RunEntity (ent);
}
if(next_fragadd < level.time)
{
if(zflag_ent == NULL && !haveflag && !ctf->value
&& zigmode->value == 1 && zigflag_spawn == 2)
{
SelectSpawnPoint (ent, v, vv);
//VectorCopy (v, ent->s.origin);
if(ZIGDrop_Flag(ent,zflag_item))
{
VectorCopy (v, zflag_ent->s.origin);
}
}
next_fragadd = level.time + FRAMETIME * 100;
}
// see if it is time to end a deathmatch
CheckDMRules ();
// see if needpass needs updated
CheckNeedPass ();
// build the playerstate_t structures for all players
ClientEndServerFrames ();
}

2060
src/g_misc.c Normal file

File diff suppressed because it is too large Load diff

795
src/g_monster.c Normal file
View file

@ -0,0 +1,795 @@
#include "g_local.h"
#include "bot.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)
{
fire_bullet (self, start, dir, damage, kick, hspread, vspread, MOD_UNKNOWN);
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)
{
fire_shotgun (self, start, aimdir, damage, kick, hspread, vspread, count, MOD_UNKNOWN);
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);
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);
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);
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);
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);
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)
{
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;
}
*/
/*
void M_CheckGround (edict_t *ent)
{
vec3_t point;
trace_t trace;
if (ent->flags & (FL_SWIM|FL_FLY))
return;
if (ent->velocity[2] > 100)
{
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] - 0.25;
if(!deathmatch->value) trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, point, ent, MASK_MONSTERSOLID);
else trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, point, ent, MASK_PLAYERSOLID);
// check steepness
if ( trace.plane.normal[2] < 0.7 && !trace.startsolid)
{
ent->groundentity = NULL;
return;
}
ent->groundentity = trace.ent;
ent->groundentity_linkcount = trace.ent->linkcount;
if(ent->client) ent->client->zc.ground_contents = trace.contents;
// if (!trace.startsolid && !trace.allsolid)
// VectorCopy (trace.endpos, ent->s.origin);
if (!trace.startsolid && !trace.allsolid)
{
VectorCopy (trace.endpos, ent->s.origin);
ent->groundentity = trace.ent;
ent->groundentity_linkcount = trace.ent->linkcount;
ent->velocity[2] = 0;
return;
}
}
*/
void M_CheckGround (edict_t *ent)
{
vec3_t point,stp,v1,v2;
trace_t trace,tracep;
if (ent->flags & (FL_SWIM|FL_FLY))
return;
if(ent->client)
{
ent->client->zc.ground_slope = 1.0;
/* if( ent->client->ctf_grapple && ent->client->ctf_grapplestate == CTF_GRAPPLE_STATE_PULL)
{
if(ent->velocity[2] > 0)
{
ent->groundentity = NULL;
return;
}
}*/
}
//ent->groundentity = NULL;
if (ent->velocity[2] > 100)
{
ent->groundentity = NULL;
//gi.bprintf(PRINT_HIGH,"ogeeX\n");
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] - 0.25;
trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, point, ent,MASK_BOTSOLID/*CONTENTS_SOLID|CONTENTS_WINDOW|CONTENTS_MONSTER*/);
//MASK_BOTSOLID /*MASK_PLAYERSOLID*/);
// check steepness
if ( trace.fraction == 1.0/*trace.plane.normal[2] < 0.7*/
&& (!trace.startsolid && !trace.allsolid))
{
ent->groundentity = NULL;
// ent->groundentity_linkcount = trace.ent->linkcount;
//gi.bprintf(PRINT_HIGH,"NULLKUN\n");
return;
}
if(/*trace.ent &&*/ (/*trace.startsolid ||*/ trace.allsolid))
{
if(1/*trace.ent->classname[0] == 'f' && trace.ent->classname[5] == 'r'*/)
{
VectorSet(v1,-16,-16,-24);
VectorSet(v2,16,16,4);
VectorCopy(ent->s.origin,stp);
// gi.bprintf(PRINT_HIGH,"ogeeY\n");
stp[2] += 24;
tracep = gi.trace (stp, v1, v2, point, ent, MASK_BOTSOLID /*MASK_PLAYERSOLID*/);
if(tracep.ent && !tracep.allsolid /*&& !tracep.startsolid*/)
{
if (tracep.ent->classname[0] == 'f' /*&& tracep.ent->classname[5] == 'r'*/)
{
VectorCopy(tracep.endpos,ent->s.origin);
// gi.bprintf(PRINT_HIGH,"ogee done\n");
ent->groundentity = tracep.ent;
/*if(tracep.ent->classname[5] == 'r')*/ ent->groundentity_linkcount = tracep.ent->linkcount;
//ent->velocity[2] = 0;
gi.linkentity(ent);
return;
}
}
}
}
// ent->groundentity = trace.ent;
// ent->groundentity_linkcount = trace.ent->linkcount;
// if (!trace.startsolid && !trace.allsolid)
// VectorCopy (trace.endpos, ent->s.origin);
if (/*!trace.startsolid &&*/ !trace.allsolid)
{
if(ent->client)
{
ent->client->zc.ground_contents = trace.contents;
ent->client->zc.ground_slope = trace.plane.normal[2];
}
VectorCopy (trace.endpos, ent->s.origin);
ent->groundentity = trace.ent;
ent->groundentity_linkcount = trace.ent->linkcount;
// ent->velocity[2] = 0;
// VectorCopy(trace.endpos,ent->s.origin);
}
// else gi.bprintf(PRINT_HIGH,"mopmop! %x %f\n",trace.contents,ent->velocity[2]);
gi.linkentity(ent);
}
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->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)
{
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);
}
void monster_think (edict_t *self)
{
M_MoveFrame (self);
if (self->linkcount != self->monsterinfo.linkcount)
{
self->monsterinfo.linkcount = self->linkcount;
M_CheckGround (self);
}
M_CatagorizePosition (self);
M_WorldEffects (self);
M_SetEffects (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);
}
//============================================================================
qboolean monster_start (edict_t *self)
{
if (deathmatch->value )
{
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;
self->max_health = self->health;
self->clipmask = MASK_MONSTERSOLID;
self->s.skinnum = 0;
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);
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;
}
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)
{
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);
}
*/

1042
src/g_phys.c Normal file

File diff suppressed because it is too large Load diff

736
src/g_save.c Normal file
View file

@ -0,0 +1,736 @@
#include "g_local.h"
#include "bot.h"
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},
//arena
{"arena", FOFS(arena),F_INT},
//end arena
// 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},
{"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}
};
// -------- just for savegames ----------
// all pointer fields should be listed here, or savegames
// won't work properly (they will crash and burn).
// this wasn't just tacked on to the fields array, because
// these don't need names, we wouldn't want map fields using
// some of these, and if one were accidentally present twice
// it would double swizzle (fuck) the pointer.
field_t savefields[] =
{
{"", FOFS(classname), F_LSTRING},
{"", FOFS(target), F_LSTRING},
{"", FOFS(targetname), F_LSTRING},
{"", FOFS(killtarget), F_LSTRING},
{"", FOFS(team), F_LSTRING},
{"", FOFS(pathtarget), F_LSTRING},
{"", FOFS(deathtarget), F_LSTRING},
{"", FOFS(combattarget), F_LSTRING},
{"", FOFS(model), F_LSTRING},
{"", FOFS(map), F_LSTRING},
{"", FOFS(message), F_LSTRING},
{"", FOFS(client), F_CLIENT},
{"", FOFS(item), F_ITEM},
{"", FOFS(goalentity), F_EDICT},
{"", FOFS(movetarget), F_EDICT},
{"", FOFS(enemy), F_EDICT},
{"", FOFS(oldenemy), F_EDICT},
{"", FOFS(activator), F_EDICT},
{"", FOFS(groundentity), F_EDICT},
{"", FOFS(teamchain), F_EDICT},
{"", FOFS(teammaster), F_EDICT},
{"", FOFS(owner), F_EDICT},
{"", FOFS(mynoise), F_EDICT},
{"", FOFS(mynoise2), F_EDICT},
{"", FOFS(target_ent), F_EDICT},
{"", FOFS(chain), F_EDICT},
{NULL, 0, F_INT}
};
field_t levelfields[] =
{
{"", LLOFS(changemap), F_LSTRING},
{"", LLOFS(sight_client), F_EDICT},
{"", LLOFS(sight_entity), F_EDICT},
{"", LLOFS(sound_entity), F_EDICT},
{"", LLOFS(sound2_entity), F_EDICT},
{NULL, 0, F_INT}
};
field_t clientfields[] =
{
{"", CLOFS(pers.weapon), F_ITEM},
{"", CLOFS(pers.lastweapon), F_ITEM},
{"", CLOFS(newweapon), F_ITEM},
{NULL, 0, F_INT}
};
/*
============
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 SetBotFlag1(edict_t *ent); //チーム1の旗
void SetBotFlag2(edict_t *ent); //チーム2の旗
void InitGame (void)
{
gi.dprintf ("==== InitGame ====\n");
bot_team_flag1 = NULL;
bot_team_flag2 = NULL;
// SetBotFlag1(NULL);
// SetBotFlag2(NULL);
gun_x = gi.cvar ("gun_x", "0", 0);
gun_y = gi.cvar ("gun_y", "0", 0);
gun_z = gi.cvar ("gun_z", "0", 0);
//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);
//maplist value
maplist = gi.cvar ("maplist", "default", CVAR_SERVERINFO | CVAR_LATCH);
//autospawn
autospawn = gi.cvar ("autospawn", "0", CVAR_SERVERINFO | CVAR_LATCH);
//chain edit flag
chedit = gi.cvar ("chedit", "0", CVAR_LATCH);
//vwep support
vwep = gi.cvar ("vwep", "1", CVAR_LATCH);
//game mode
zigmode = gi.cvar ("zigmode", "0", CVAR_SERVERINFO| CVAR_LATCH);
//ZOID
//This game.dll only supports deathmatch
if (!deathmatch->value) {
gi.dprintf("Forcing deathmatch.\n");
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);
fraglimit = gi.cvar ("fraglimit", "0", CVAR_SERVERINFO);
timelimit = gi.cvar ("timelimit", "0", CVAR_SERVERINFO);
//ZOID
capturelimit = gi.cvar ("capturelimit", "0", CVAR_SERVERINFO);
//ZOID
password = gi.cvar ("password", "", CVAR_USERINFO);
spectator_password = gi.cvar ("spectator_password", "", CVAR_USERINFO);
maxspectators = gi.cvar ("maxspectators", "4", CVAR_SERVERINFO);
g_select_empty = gi.cvar ("g_select_empty", "0", CVAR_ARCHIVE);
filterban = gi.cvar ("filterban", "1", 0);
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);
// 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
Load_BotInfo(); //コンフィグ読み込み3ZBConfig.cfg
}
//=========================================================
void WriteField1 (FILE *f, field_t *field, byte *base)
{
void *p;
int len;
int index;
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;
default:
gi.error ("WriteEdict: unknown field type");
}
}
void WriteField2 (FILE *f, field_t *field, byte *base)
{
int len;
void *p;
p = (void *)(base + field->ofs);
switch (field->type)
{
case F_LSTRING:
case F_GSTRING:
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;
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_GSTRING:
len = *(int *)p;
if (!len)
*(char **)p = NULL;
else
{
*(char **)p = gi.TagMalloc (len, TAG_GAME);
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;
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=savefields ; 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=savefields ; 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=savefields ; 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 already 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);
if (base != (void *)InitGame)
{
fclose (f);
gi.error ("ReadLevel: function pointers have moved");
}
// 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;
}
}

1474
src/g_spawn.c Normal file

File diff suppressed because it is too large Load diff

456
src/g_svcmds.c Normal file
View file

@ -0,0 +1,456 @@
#include "g_local.h"
#include "bot.h"
/*
==============================================================================
PACKET FILTERING
You can add or remove addresses from the filter list with:
addip <ip>
removeip <ip>
The ip address is specified in dot format, and any unspecified digits will match any value, so you can specify an entire class C network with "addip 192.246.40".
Removeip will only remove an address specified exactly the same way. You cannot addip a subnet, then removeip a single host.
listip
Prints the current list of filters.
writeip
Dumps "addip <ip>" commands to listip.cfg so it can be execed at a later date. The filter lists are not saved and restored by default, because I beleive it would cause too much confusion.
filterban <0 or 1>
If 1 (the default), then ip addresses matching the current list will be prohibited from entering the game. This is the default setting.
If 0, then only addresses matching the list will be allowed. This lets you easily set up a private game, or a game that only allows players from your local network.
==============================================================================
*/
typedef struct
{
unsigned mask;
unsigned compare;
} ipfilter_t;
#define MAX_IPFILTERS 1024
ipfilter_t ipfilters[MAX_IPFILTERS];
int numipfilters;
/*
=================
StringToFilter
=================
*/
static qboolean StringToFilter (char *s, ipfilter_t *f)
{
char num[128];
int i, j;
byte b[4];
byte m[4];
for (i=0 ; i<4 ; i++)
{
b[i] = 0;
m[i] = 0;
}
for (i=0 ; i<4 ; i++)
{
if (*s < '0' || *s > '9')
{
gi.cprintf(NULL, PRINT_HIGH, "Bad filter address: %s\n", s);
return false;
}
j = 0;
while (*s >= '0' && *s <= '9')
{
num[j++] = *s++;
}
num[j] = 0;
b[i] = atoi(num);
if (b[i] != 0)
m[i] = 255;
if (!*s)
break;
s++;
}
f->mask = *(unsigned *)m;
f->compare = *(unsigned *)b;
return true;
}
/*
=================
SV_FilterPacket
=================
*/
qboolean SV_FilterPacket (char *from)
{
int i;
unsigned in;
byte m[4];
char *p;
i = 0;
p = from;
while (*p && i < 4) {
m[i] = 0;
while (*p >= '0' && *p <= '9') {
m[i] = m[i]*10 + (*p - '0');
p++;
}
if (!*p || *p == ':')
break;
i++, p++;
}
in = *(unsigned *)m;
for (i=0 ; i<numipfilters ; i++)
if ( (in & ipfilters[i].mask) == ipfilters[i].compare)
return (int)filterban->value;
return (int)!filterban->value;
}
/*
=================
SV_AddIP_f
=================
*/
void SVCmd_AddIP_f (void)
{
int i;
if (gi.argc() < 3) {
gi.cprintf(NULL, PRINT_HIGH, "Usage: addip <ip-mask>\n");
return;
}
for (i=0 ; i<numipfilters ; i++)
if (ipfilters[i].compare == 0xffffffff)
break; // free spot
if (i == numipfilters)
{
if (numipfilters == MAX_IPFILTERS)
{
gi.cprintf (NULL, PRINT_HIGH, "IP filter list is full\n");
return;
}
numipfilters++;
}
if (!StringToFilter (gi.argv(2), &ipfilters[i]))
ipfilters[i].compare = 0xffffffff;
}
/*
=================
SV_RemoveIP_f
=================
*/
void SVCmd_RemoveIP_f (void)
{
ipfilter_t f;
int i, j;
if (gi.argc() < 3) {
gi.cprintf(NULL, PRINT_HIGH, "Usage: sv removeip <ip-mask>\n");
return;
}
if (!StringToFilter (gi.argv(2), &f))
return;
for (i=0 ; i<numipfilters ; i++)
if (ipfilters[i].mask == f.mask
&& ipfilters[i].compare == f.compare)
{
for (j=i+1 ; j<numipfilters ; j++)
ipfilters[j-1] = ipfilters[j];
numipfilters--;
gi.cprintf (NULL, PRINT_HIGH, "Removed.\n");
return;
}
gi.cprintf (NULL, PRINT_HIGH, "Didn't find %s.\n", gi.argv(2));
}
/*
=================
SV_ListIP_f
=================
*/
void SVCmd_ListIP_f (void)
{
int i;
byte b[4];
gi.cprintf (NULL, PRINT_HIGH, "Filter list:\n");
for (i=0 ; i<numipfilters ; i++)
{
*(unsigned *)b = ipfilters[i].compare;
gi.cprintf (NULL, PRINT_HIGH, "%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]);
}
}
/*
=================
SV_WriteIP_f
=================
*/
void SVCmd_WriteIP_f (void)
{
FILE *f;
char name[MAX_OSPATH];
byte b[4];
int i;
cvar_t *game;
game = gi.cvar("game", "", 0);
if (!*game->string)
sprintf (name, "%s/listip.cfg", GAMEVERSION);
else
sprintf (name, "%s/listip.cfg", game->string);
gi.cprintf (NULL, PRINT_HIGH, "Writing %s.\n", name);
f = fopen (name, "wb");
if (!f)
{
gi.cprintf (NULL, PRINT_HIGH, "Couldn't open %s\n", name);
return;
}
fprintf(f, "set filterban %d\n", (int)filterban->value);
for (i=0 ; i<numipfilters ; i++)
{
*(unsigned *)b = ipfilters[i].compare;
fprintf (f, "sv addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]);
}
fclose (f);
}
//ルート修正
//ノーマルポッドは全て切り捨て
void Move_LastRouteIndex()
{
int i;
for(i = CurrentIndex - 1 ; i >= 0;i--)
{
if(Route[i].state) break;
else if(!Route[i].index) break;
}
if(!CurrentIndex || !Route[i].index) CurrentIndex = i;
else CurrentIndex = i + 1;
if(CurrentIndex < MAXNODES)
{
memset(&Route[CurrentIndex],0,sizeof(route_t));
if(CurrentIndex > 0) Route[CurrentIndex].index = Route[CurrentIndex - 1].index + 1;
}
}
//分岐付きに変換処理
void RouteTreepointSet()
{
int i;
for(i = 0;i < CurrentIndex;i++)
{
if(Route[i].state == GRS_NORMAL)
{
}
}
}
void Svcmd_Test_f (void)
{
gi.cprintf (NULL, PRINT_HIGH, "Svcmd_Test_f()\n");
}
//chainファイルのセーブ
void SaveChain()
{
char name[256];
FILE *fpout;
unsigned int size;
if(!chedit->value)
{
gi.cprintf (NULL, PRINT_HIGH, "Not a chaining mode.\n");
return;
}
//とりあえずCTFだめ
if(ctf->value) sprintf(name,".\\%s\\chctf\\%s.chf",gamepath->string,level.mapname);
else sprintf(name,".\\%s\\chdtm\\%s.chn",gamepath->string,level.mapname);
fpout = fopen(name,"wb");
if(fpout == NULL) gi.cprintf(NULL,PRINT_HIGH,"Can't open %s\n",name);
else
{
if(!ctf->value) fwrite("3ZBRGDTM",sizeof(char),8,fpout);
else fwrite("3ZBRGCTF",sizeof(char),8,fpout);
fwrite(&CurrentIndex,sizeof(int),1,fpout);
size = (unsigned int)CurrentIndex * sizeof(route_t);
fwrite(Route,size,1,fpout);
gi.cprintf (NULL, PRINT_HIGH,"%s Saving done.\n",name);
fclose(fpout);
}
}
//Spawn Command
void SpawnCommand(int i)
{
int j;
if(chedit->value){ gi.cprintf(NULL,PRINT_HIGH,"Can't spawn.");return;}
if(i <= 0) {gi.cprintf(NULL,PRINT_HIGH,"Specify num of bots.");return;}
for(j = 0;j < i;j++)
{
SpawnBotReserving();
}
}
//Random Spawn Command
void RandomSpawnCommand(int i)
{
int j,k,red = 0,blue = 0;
edict_t *e;
if(chedit->value){ gi.cprintf(NULL,PRINT_HIGH,"Can't spawn.");return;}
if(i <= 0) {gi.cprintf(NULL,PRINT_HIGH,"Specify num of bots.");return;}
//count current teams
for ( k = 1 ; k <= maxclients->value ; k++)
{
e = &g_edicts[k];
if(e->inuse && e->client)
{
if(e->client->resp.ctf_team == CTF_TEAM1) red++;
else if(e->client->resp.ctf_team == CTF_TEAM2) blue++;
}
}
for(j = 0;j < i;j++)
{
SpawnBotReserving2(&red,&blue);
//gi.cprintf(NULL,PRINT_HIGH,"R B %i %i\n",red,blue);
}
}
//Remove Command
void RemoveCommand(int i)
{
int j;
if(i <= 0) i = 1;//gi.cprintf(NULL,PRINT_HIGH,"Specify num of bots.");
for(j = 0;j < i;j++)
{
RemoveBot();
}
}
//Debug Spawn Command
void DebugSpawnCommand(int i)
{
if(!chedit->value) {gi.cprintf(NULL,PRINT_HIGH,"Can't debug.");return;}
if(targetindex) {gi.cprintf(NULL,PRINT_HIGH,"Now debugging.");return;}
if(i < 1) i = 1;
targetindex = i;
SpawnBotReserving();
}
/*
=================
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 ServerCommand (void)
{
char *cmd;
cmd = gi.argv(1);
if (Q_stricmp (cmd, "test") == 0)
Svcmd_Test_f ();
else if (Q_stricmp (cmd, "savechain") == 0)
SaveChain ();
else if (Q_stricmp (cmd, "spb") == 0)
{
if(gi.argc() <= 1) SpawnCommand(1);
else SpawnCommand (atoi(gi.argv(2)));
}
else if (Q_stricmp (cmd, "rspb") == 0)
{
if(gi.argc() <= 1) RandomSpawnCommand(1);
else RandomSpawnCommand (atoi(gi.argv(2)));
}
else if (Q_stricmp (cmd, "rmb") == 0)
{
if(gi.argc() <= 1) RemoveCommand(1);
else RemoveCommand (atoi(gi.argv(2)));
}
else if (Q_stricmp (cmd, "dsp") == 0)
{
if(gi.argc() <= 1) DebugSpawnCommand(1);
else DebugSpawnCommand (atoi(gi.argv(2)));
}
else if (Q_stricmp (cmd, "addip") == 0)
SVCmd_AddIP_f ();
else if (Q_stricmp (cmd, "removeip") == 0)
SVCmd_RemoveIP_f ();
else if (Q_stricmp (cmd, "listip") == 0)
SVCmd_ListIP_f ();
else if (Q_stricmp (cmd, "writeip") == 0)
SVCmd_WriteIP_f ();
else
gi.cprintf (NULL, PRINT_HIGH, "Unknown server command \"%s\"\n", cmd);
}

881
src/g_target.c Normal file
View file

@ -0,0 +1,881 @@
#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 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 always atten 3 / vol 1, and the use function toggles it on/off.
Multiple identical looping sounds will just increase volume without any speed cost.
*/
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 (!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; // already 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((stricmp(level.mapname, "fact1") == 0) && (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;
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;
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;
}
ignore = self;
VectorCopy (self->s.origin, start);
VectorMA (start, 2048, self->movedir, end);
while(1)
{
tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);
VectorCopy (tr.endpos,self->moveinfo.end_origin);
if (!tr.ent)
break;
// hurt it if we can
if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER))
T_Damage (tr.ent, self, self->activator, self->movedir, tr.endpos, vec3_origin, self->dmg, 1, DAMAGE_ENERGY, MOD_TARGET_LASER);
// 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);
}
VectorCopy (tr.endpos, self->s.old_origin);
self->nextthink = level.time + FRAMETIME;
}
void target_laser_on (edict_t *self)
{
if (!self->activator)
self->activator = self;
self->spawnflags |= 0x80000001;
self->svflags &= ~SVF_NOCLIENT;
target_laser_think (self);
}
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)
{
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)
self->s.skinnum = 0xf2f2f0f0;
else if (self->spawnflags & 4)
self->s.skinnum = 0xd0d1d2d3;
else if (self->spawnflags & 8)
self->s.skinnum = 0xf3f3f1f1;
else if (self->spawnflags & 16)
self->s.skinnum = 0xdcdddedf;
else if (self->spawnflags & 32)
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;
}
//==========================================================
// RAFAEL 15-APR-98
/*QUAKED target_mal_laser (1 0 0) (-4 -4 -4) (4 4 4) START_ON RED GREEN BLUE YELLOW ORANGE FAT
Mal's laser
*/
void target_mal_laser_on (edict_t *self)
{
if (!self->activator)
self->activator = self;
self->spawnflags |= 0x80000001;
self->svflags &= ~SVF_NOCLIENT;
// target_laser_think (self);
self->nextthink = level.time + self->wait + self->delay;
}
void target_mal_laser_off (edict_t *self)
{
self->spawnflags &= ~1;
self->svflags |= SVF_NOCLIENT;
self->nextthink = 0;
}
void target_mal_laser_use (edict_t *self, edict_t *other, edict_t *activator)
{
self->activator = activator;
if (self->spawnflags & 1)
target_mal_laser_off (self);
else
target_mal_laser_on (self);
}
void mal_laser_think (edict_t *self)
{
target_laser_think (self);
self->nextthink = level.time + self->wait + 0.1;
self->spawnflags |= 0x80000000;
}
void SP_target_mal_laser (edict_t *self)
{
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)
self->s.skinnum = 0xf2f2f0f0;
else if (self->spawnflags & 4)
self->s.skinnum = 0xd0d1d2d3;
else if (self->spawnflags & 8)
self->s.skinnum = 0xf3f3f1f1;
else if (self->spawnflags & 16)
self->s.skinnum = 0xdcdddedf;
else if (self->spawnflags & 32)
self->s.skinnum = 0xe0e1e2e3;
G_SetMovedir (self->s.angles, self->movedir);
if (!self->delay)
self->delay = 0.1;
if (!self->wait)
self->wait = 0.1;
if (!self->dmg)
self->dmg = 5;
VectorSet (self->mins, -8, -8, -8);
VectorSet (self->maxs, 8, 8, 8);
self->nextthink = level.time + self->delay;
self->think = mal_laser_think;
self->use = target_mal_laser_use;
gi.linkentity (self);
if (self->spawnflags & 1)
target_mal_laser_on (self);
else
target_mal_laser_off (self);
}
// END 15-APR-98
//==========================================================
/*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");
}

692
src/g_trigger.c Normal file
View file

@ -0,0 +1,692 @@
#include "g_local.h"
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;
}
// 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);
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;
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->svflags & SVF_MONSTER))
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) && !(self->svflags & SVF_MONSTER))
{
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) && !(self->svflags & SVF_MONSTER))
{
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
==============================================================================
*/
#if 0
#define PUSH_ONCE 1
static int windsound;
void trigger_push_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
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);
}
}
}
if (self->spawnflags & PUSH_ONCE)
G_FreeEdict (self);
}
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);
}
#endif
// RAFAEL
#define PUSH_ONCE 1
static int windsound;
void trigger_push_touch (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
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);
}
}
}
if (self->spawnflags & PUSH_ONCE)
G_FreeEdict (self);
}
/*QUAKED trigger_push (.5 .5 .5) ? PUSH_ONCE
Pushes the player
"speed" defaults to 1000
*/
void trigger_push_active (edict_t *self);
void trigger_effect (edict_t *self)
{
vec3_t origin;
vec3_t size;
int i;
VectorScale (self->size, 0.5, size);
VectorAdd (self->absmin, size, origin);
for (i=0; i<10; i++)
{
origin[2] += (self->speed * 0.01) * (i + random());
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_TUNNEL_SPARKS);
gi.WriteByte (1);
gi.WritePosition (origin);
gi.WriteDir (vec3_origin);
gi.WriteByte (0x74 + (rand()&7));
gi.multicast (self->s.origin, MULTICAST_PVS);
}
}
void trigger_push_inactive (edict_t *self)
{
if (self->delay > level.time)
{
self->nextthink = level.time + 0.1;
}
else
{
self->touch = trigger_push_touch;
self->think = trigger_push_active;
self->nextthink = level.time + 0.1;
self->delay = self->nextthink + self->wait;
}
}
void trigger_push_active (edict_t *self)
{
if (self->delay > level.time)
{
self->nextthink = level.time + 0.1;
trigger_effect (self);
}
else
{
self->touch = NULL;
self->think = trigger_push_inactive;
self->nextthink = level.time + 0.1;
self->delay = self->nextthink + self->wait;
}
}
void SP_trigger_push (edict_t *self)
{
InitTrigger (self);
windsound = gi.soundindex ("misc/windfly.wav");
self->touch = trigger_push_touch;
if (self->spawnflags & 2)
{
if (!self->wait)
self->wait = 10;
self->think = trigger_push_active;
self->nextthink = level.time + 0.1;
self->delay = self->nextthink + self->wait;
}
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;
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->client) return;
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;
}

591
src/g_utils.c Normal file
View file

@ -0,0 +1,591 @@
// g_utils.c -- misc utility functions for game module
#include "g_local.h"
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;
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);
if (VectorLength(eorg) > rad)
continue;
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
//
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;
}
}
}
//
// fire targets
//
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
{
if (t->use)
t->use (t, ent, activator);
}
if (!ent->inuse)
{
gi.dprintf("entity was removed while using targets\n");
return;
}
}
}
}
/*
=============
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;
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
{
yaw = (int) (atan2(value1[1], value1[0]) * 180 / M_PI);
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;
}
/*
=================
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)
{
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);
if(ent->client) if(ent->client->zc.second_target == hit)
ent->client->zc.second_target = NULL;
}
}
/*
void G_TouchTriggers (edict_t *ent)
{
int i, num;
//pon edict_t *touch[MAX_EDICTS], *hit;
edict_t *e;
if(ent->classname[0] != 'p') return;
// dead things don't activate triggers!
if ((ent->client || (ent->svflags & SVF_MONSTER)) && (ent->health <= 0))
return;
//pon
e = &g_edicts[(int)maxclients->value+1];
for ( i=maxclients->value+1 ; i<globals.num_edicts ; i++, e++)
{
if(!e->inuse) continue;
if(!e->touch) continue;
if(e->solid != SOLID_TRIGGER) continue;
if(e->absmax[0] < ent->absmin[0]) continue;
if(ent->absmax[0] < e->absmin[0]) continue;
if(e->absmax[1] < ent->absmin[1]) continue;
if(ent->absmax[1] < e->absmin[1]) continue;
if(e->absmax[2] < ent->absmin[2]) continue;
if(ent->absmax[2] < e->absmin[2]) continue;
e->touch (e, ent, NULL, NULL);
if(ent->client) if(ent->client->zc.second_target == e)
ent->client->zc.second_target = NULL;
}
return;
//pon
// 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;
}
}
/*
==============================================================================
Kill box
==============================================================================
*/
/*
=================
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;
while (1)
{
tr = gi.trace (ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID);
if (!tr.ent)
break;
// nail it
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)
return false;
}
return true; // all clear
}

1670
src/g_weapon.c Normal file

File diff suppressed because it is too large Load diff

220
src/game.h Normal file
View file

@ -0,0 +1,220 @@
#ifndef GAMEHEAD
#define GAMEHEAD
// 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
typedef 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
} gclient_t;
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, ...);
// new names can only be added during spawning
// existing names can be looked up at any time
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 coneole command parameter checking
int (*argc) (void);
char *(*argv) (int n);
char *(*args) (void);
// 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);
void (*ReadGame) (char *filename);
// ReadLevel is called after the default map information has been
// loaded with SpawnEntities, so any stored client spawn spots will
// be used when the clients reconnect.
void (*WriteLevel) (char *filename);
void (*ReadLevel) (char *filename);
qboolean (*ClientConnect) (edict_t *ent, char *userinfo, qboolean loadgame);
void (*ClientBegin) (edict_t *ent, qboolean loadgame);
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);
#endif

80
src/m_move.c Normal file
View file

@ -0,0 +1,80 @@
// 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_PLAYERSOLID /*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_PLAYERSOLID /*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;
}

205
src/m_player.h Normal file
View file

@ -0,0 +1,205 @@
// 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

2402
src/p_client.c Normal file

File diff suppressed because it is too large Load diff

585
src/p_hud.c Normal file
View file

@ -0,0 +1,585 @@
#include "g_local.h"
#include "bot.h"
/*
======================================================================
INTERMISSION
======================================================================
*/
void MoveClientToIntermission (edict_t *ent)
{
if(!(ent->svflags & SVF_MONSTER))
{
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;
}
VectorCopy (level.intermission_origin, ent->s.origin);
// 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;
// RAFAEL
ent->client->quadfire_framenum = 0;
// RAFAEL
ent->client->trap_blew_up = false;
ent->client->trap_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 (deathmatch->value && !(ent->svflags & SVF_MONSTER))
{
DeathmatchScoreboardMessage (ent, NULL);
gi.unicast (ent, true);
}
}
void SetLVChanged ( int i );
void DelBots2(int i);
int GetNumbots ( void );
int GetLVChanged ( void );
void BeginIntermission (edict_t *targ)
{
int i;
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 on same unit, return immediately
if (!deathmatch->value && (targ->map && targ->map[0] != '*') )
{ // go immediately to the next level
level.exitintermission = 1;
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);
}
}
/*
==================
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;
//ZOID
if (ctf->value) {
CTFScoreboardMessage (ent, killer);
return;
}
//ZOID
// 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;
}
// 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;
}
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;
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 202 yv 12 string2 \"%s\" " // skill
"xv 0 yv 24 cstring2 \"%s\" " // level name
"xv 0 yv 54 cstring2 \"%s\" " // help 1
"xv 0 yv 110 cstring2 \"%s\" " // help 2
"xv 50 yv 164 string2 \" kills goals secrets\" "
"xv 50 yv 172 string2 \"%3i/%3i %i/%i %i/%i\" ",
sk,
level.level_name,
game.helpmessage1,
game.helpmessage2,
level.killed_monsters, level.total_monsters,
level.found_goals, level.total_goals,
level.found_secrets, level.total_secrets);
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);
}
//=======================================================================
/*
===============
G_SetStats
===============
*/
void G_SetStats (edict_t *ent)
{
gitem_t *item;
int index, cells;
int power_armor_type;
//
// health
//
ent->client->ps.stats[STAT_HEALTH_ICON] = level.pic_health;
ent->client->ps.stats[STAT_HEALTH] = ent->health;
//
// 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(Fdi_CELLS/*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;
}
// RAFAEL
else if (ent->client->quadfire_framenum > level.framenum)
{
// note to self
// need to change imageindex
ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_quadfire");
ent->client->ps.stats[STAT_TIMER] = (ent->client->quadfire_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)
{
if (ent->client->pers.health <= 0 || level.intermissiontime
|| ent->client->showscores)
ent->client->ps.stats[STAT_LAYOUTS] |= 1;
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;
//ponpoko
if(ent->client->zc.aiming == 1)
{
ent->client->ps.stats[STAT_SIGHT_PIC] = gi.imageindex ("zsight");
}
else if(ent->client->zc.aiming == 3)
{
if(ent->client->zc.lockon) ent->client->ps.stats[STAT_SIGHT_PIC] = gi.imageindex ("zsight_l1");
else ent->client->ps.stats[STAT_SIGHT_PIC] = gi.imageindex ("zsight_l0");
}
else ent->client->ps.stats[STAT_SIGHT_PIC] = 0;//gi.imageindex ("i_help"/*"zsight"*/);;
//ponpoko
//ZOID
SetCTFStats(ent);
//ZOID
}
/*
===============
G_CheckChaseStats
===============
*/
void G_CheckChaseStats (edict_t *ent)
{
int i;
gclient_t *cl;
for (i = 1; i <= maxclients->value; i++) {
cl = g_edicts[i].client;
if (!g_edicts[i].inuse || cl->chase_target != ent)
continue;
memcpy(cl->ps.stats, ent->client->ps.stats, sizeof(cl->ps.stats));
G_SetSpectatorStats(g_edicts + i);
}
}
/*
===============
G_SetSpectatorStats
===============
*/
void G_SetSpectatorStats (edict_t *ent)
{
gclient_t *cl = ent->client;
if (!cl->chase_target)
G_SetStats (ent);
cl->ps.stats[STAT_SPECTATOR] = 1;
// layouts are independant in spectator
cl->ps.stats[STAT_LAYOUTS] = 0;
if (cl->pers.health <= 0 || level.intermissiontime || cl->showscores)
cl->ps.stats[STAT_LAYOUTS] |= 1;
if (cl->showinventory && cl->pers.health > 0)
cl->ps.stats[STAT_LAYOUTS] |= 2;
if (cl->chase_target && cl->chase_target->inuse)
cl->ps.stats[STAT_CHASE] = CS_PLAYERSKINS +
(cl->chase_target - g_edicts) - 1;
else
cl->ps.stats[STAT_CHASE] = 0;
}

196
src/p_menu.c Normal file
View file

@ -0,0 +1,196 @@
#include "g_local.h"
void PMenu_Open(edict_t *ent, pmenu_t *entries, int cur, int num)
{
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;
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;
if(!(ent->svflags & SVF_MONSTER))
{
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->client->menu) {
gi.dprintf("warning: ent has no menu\n");
return;
}
hnd = ent->client->menu;
strcpy(string, "xv 32 yv 8 picn inventory ");
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++;
}
sprintf(string + strlen(string), "yv %d ", 32 + i * 8);
if (p->align == PMENU_ALIGN_CENTER)
x = 196/2 - strlen(t)*4 + 64;
else if (p->align == PMENU_ALIGN_RIGHT)
x = 64 + (196 - 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);
}
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;
if(!(ent->svflags & SVF_MONSTER))
{
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;
if(!(ent->svflags & SVF_MONSTER))
{
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);
}

26
src/p_menu.h Normal file
View file

@ -0,0 +1,26 @@
enum {
PMENU_ALIGN_LEFT,
PMENU_ALIGN_CENTER,
PMENU_ALIGN_RIGHT
};
typedef struct pmenuhnd_s {
struct pmenu_s *entries;
int cur;
int num;
} pmenuhnd_t;
typedef struct pmenu_s {
char *text;
int align;
void *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);
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);

127
src/p_trail.c Normal file
View file

@ -0,0 +1,127 @@
#include "g_local.h"
/*
==============================================================================
PLAYER TRAIL
==============================================================================
This is a circular list containing the a list of points of where
the player has been recently. It is used by monsters for pursuit.
.origin the spot
.owner forward link
.aiment backward link
*/
#define TRAIL_LENGTH 8
edict_t *trail[TRAIL_LENGTH];
int trail_head;
qboolean trail_active = false;
#define NEXT(n) (((n) + 1) & (TRAIL_LENGTH - 1))
#define PREV(n) (((n) - 1) & (TRAIL_LENGTH - 1))
void PlayerTrail_Init (void)
{
int n;
if (deathmatch->value /* FIXME || coop */)
return;
for (n = 0; n < TRAIL_LENGTH; n++)
{
trail[n] = G_Spawn();
trail[n]->classname = "player_trail";
}
trail_head = 0;
trail_active = true;
}
void PlayerTrail_Add (vec3_t spot)
{
vec3_t temp;
if (!trail_active)
return;
VectorCopy (spot, trail[trail_head]->s.origin);
trail[trail_head]->timestamp = level.time;
VectorSubtract (spot, trail[PREV(trail_head)]->s.origin, temp);
trail[trail_head]->s.angles[1] = vectoyaw (temp);
trail_head = NEXT(trail_head);
}
void PlayerTrail_New (vec3_t spot)
{
if (!trail_active)
return;
PlayerTrail_Init ();
PlayerTrail_Add (spot);
}
edict_t *PlayerTrail_PickFirst (edict_t *self)
{
int marker;
int n;
if (!trail_active)
return NULL;
for (marker = trail_head, n = TRAIL_LENGTH; n; n--)
{
if(trail[marker]->timestamp <= self->monsterinfo.trail_time)
marker = NEXT(marker);
else
break;
}
if (visible(self, trail[marker]))
{
return trail[marker];
}
if (visible(self, trail[PREV(marker)]))
{
return trail[PREV(marker)];
}
return trail[marker];
}
edict_t *PlayerTrail_PickNext (edict_t *self)
{
int marker;
int n;
if (!trail_active)
return NULL;
for (marker = trail_head, n = TRAIL_LENGTH; n; n--)
{
if(trail[marker]->timestamp <= self->monsterinfo.trail_time)
marker = NEXT(marker);
else
break;
}
return trail[marker];
}
edict_t *PlayerTrail_LastSpot (void)
{
return trail[trail_head];
}

1552
src/p_view.c Normal file

File diff suppressed because it is too large Load diff

2368
src/p_weapon.c Normal file

File diff suppressed because it is too large Load diff

1400
src/q_shared.c Normal file

File diff suppressed because it is too large Load diff

1159
src/q_shared.h Normal file

File diff suppressed because it is too large Load diff