mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2024-11-10 07:12:07 +00:00
game: sync game logic from xatrix
This commit is contained in:
parent
0956f965f7
commit
5b7696b789
30 changed files with 1846 additions and 24440 deletions
37
Makefile
37
Makefile
|
@ -1466,12 +1466,12 @@ XATRIX_OBJS_ = \
|
||||||
src/common/shared/rand.o \
|
src/common/shared/rand.o \
|
||||||
src/common/shared/shared.o \
|
src/common/shared/shared.o \
|
||||||
src/game/dm/tag.o \
|
src/game/dm/tag.o \
|
||||||
src/xatrix/g_ai.o \
|
src/game/g_ai.o \
|
||||||
src/xatrix/g_chase.o \
|
src/game/g_chase.o \
|
||||||
src/xatrix/g_cmds.o \
|
src/game/g_cmds.o \
|
||||||
src/xatrix/g_combat.o \
|
src/game/g_combat.o \
|
||||||
src/xatrix/g_func.o \
|
src/game/g_func.o \
|
||||||
src/xatrix/g_items.o \
|
src/game/g_items.o \
|
||||||
src/game/g_main.o \
|
src/game/g_main.o \
|
||||||
src/game/g_misc.o \
|
src/game/g_misc.o \
|
||||||
src/game/g_monster.o \
|
src/game/g_monster.o \
|
||||||
|
@ -1481,15 +1481,15 @@ XATRIX_OBJS_ = \
|
||||||
src/game/g_newtarg.o \
|
src/game/g_newtarg.o \
|
||||||
src/game/g_newtrig.o \
|
src/game/g_newtrig.o \
|
||||||
src/game/g_newweap.o \
|
src/game/g_newweap.o \
|
||||||
src/xatrix/g_phys.o \
|
src/game/g_phys.o \
|
||||||
src/xatrix/g_spawn.o \
|
src/game/g_spawn.o \
|
||||||
src/game/g_sphere.o \
|
src/game/g_sphere.o \
|
||||||
src/xatrix/g_svcmds.o \
|
src/game/g_svcmds.o \
|
||||||
src/xatrix/g_target.o \
|
src/game/g_target.o \
|
||||||
src/xatrix/g_trigger.o \
|
src/game/g_trigger.o \
|
||||||
src/xatrix/g_turret.o \
|
src/game/g_turret.o \
|
||||||
src/game/g_utils.o \
|
src/game/g_utils.o \
|
||||||
src/xatrix/g_weapon.o \
|
src/game/g_weapon.o \
|
||||||
src/game/monster/berserker/berserker.o \
|
src/game/monster/berserker/berserker.o \
|
||||||
src/game/monster/boss2/boss2.o \
|
src/game/monster/boss2/boss2.o \
|
||||||
src/game/monster/boss3/boss3.o \
|
src/game/monster/boss3/boss3.o \
|
||||||
|
@ -1497,6 +1497,7 @@ XATRIX_OBJS_ = \
|
||||||
src/game/monster/boss3/boss32.o \
|
src/game/monster/boss3/boss32.o \
|
||||||
src/game/monster/boss5/boss5.o \
|
src/game/monster/boss5/boss5.o \
|
||||||
src/game/monster/brain/brain.o \
|
src/game/monster/brain/brain.o \
|
||||||
|
src/game/monster/carrier/carrier.o \
|
||||||
src/game/monster/chick/chick.o \
|
src/game/monster/chick/chick.o \
|
||||||
src/game/monster/fixbot/fixbot.o \
|
src/game/monster/fixbot/fixbot.o \
|
||||||
src/game/monster/flipper/flipper.o \
|
src/game/monster/flipper/flipper.o \
|
||||||
|
@ -1514,8 +1515,12 @@ XATRIX_OBJS_ = \
|
||||||
src/game/monster/mutant/mutant.o \
|
src/game/monster/mutant/mutant.o \
|
||||||
src/game/monster/parasite/parasite.o \
|
src/game/monster/parasite/parasite.o \
|
||||||
src/game/monster/soldier/soldier.o \
|
src/game/monster/soldier/soldier.o \
|
||||||
|
src/game/monster/stalker/stalker.o \
|
||||||
src/game/monster/supertank/supertank.o \
|
src/game/monster/supertank/supertank.o \
|
||||||
src/game/monster/tank/tank.o \
|
src/game/monster/tank/tank.o \
|
||||||
|
src/game/monster/turret/turret.o \
|
||||||
|
src/game/monster/widow/widow2.o \
|
||||||
|
src/game/monster/widow/widow.o \
|
||||||
src/game/player/client.o \
|
src/game/player/client.o \
|
||||||
src/game/player/hud.o \
|
src/game/player/hud.o \
|
||||||
src/game/player/trail.o \
|
src/game/player/trail.o \
|
||||||
|
@ -1591,7 +1596,7 @@ ROGUE_OBJS_ = \
|
||||||
src/common/shared/shared.o \
|
src/common/shared/shared.o \
|
||||||
src/rogue/g_ai.o \
|
src/rogue/g_ai.o \
|
||||||
src/rogue/g_chase.o \
|
src/rogue/g_chase.o \
|
||||||
src/rogue/g_cmds.o \
|
src/game/g_cmds.o \
|
||||||
src/rogue/g_combat.o \
|
src/rogue/g_combat.o \
|
||||||
src/rogue/g_func.o \
|
src/rogue/g_func.o \
|
||||||
src/rogue/g_items.o \
|
src/rogue/g_items.o \
|
||||||
|
@ -1604,7 +1609,7 @@ ROGUE_OBJS_ = \
|
||||||
src/game/g_newtarg.o \
|
src/game/g_newtarg.o \
|
||||||
src/game/g_newtrig.o \
|
src/game/g_newtrig.o \
|
||||||
src/game/g_newweap.o \
|
src/game/g_newweap.o \
|
||||||
src/rogue/g_phys.o \
|
src/game/g_phys.o \
|
||||||
src/rogue/g_spawn.o \
|
src/rogue/g_spawn.o \
|
||||||
src/game/g_sphere.o \
|
src/game/g_sphere.o \
|
||||||
src/rogue/g_svcmds.o \
|
src/rogue/g_svcmds.o \
|
||||||
|
@ -1642,7 +1647,7 @@ ROGUE_OBJS_ = \
|
||||||
src/game/monster/turret/turret.o \
|
src/game/monster/turret/turret.o \
|
||||||
src/game/monster/widow/widow.o \
|
src/game/monster/widow/widow.o \
|
||||||
src/game/monster/widow/widow2.o \
|
src/game/monster/widow/widow2.o \
|
||||||
src/rogue/player/client.o \
|
src/game/player/client.o \
|
||||||
src/rogue/player/hud.o \
|
src/rogue/player/hud.o \
|
||||||
src/rogue/player/trail.o \
|
src/rogue/player/trail.o \
|
||||||
src/rogue/player/view.o \
|
src/rogue/player/view.o \
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||||
|
* Copyright (c) ZeniMax Media Inc.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||||
|
* Copyright (c) ZeniMax Media Inc.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -228,7 +229,7 @@ Cmd_Give_f(edict_t *ent)
|
||||||
|
|
||||||
if ((deathmatch->value || coop->value) && !sv_cheats->value)
|
if ((deathmatch->value || coop->value) && !sv_cheats->value)
|
||||||
{
|
{
|
||||||
gi.cprintf( ent, PRINT_HIGH,
|
gi.cprintf(ent, PRINT_HIGH,
|
||||||
"You must run the server with '+set cheats 1' to enable this command.\n");
|
"You must run the server with '+set cheats 1' to enable this command.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -362,6 +363,11 @@ Cmd_Give_f(edict_t *ent)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (it->flags & IT_NOT_GIVEABLE)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (it->flags & (IT_ARMOR | IT_WEAPON | IT_AMMO))
|
if (it->flags & (IT_ARMOR | IT_WEAPON | IT_AMMO))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
@ -393,6 +399,12 @@ Cmd_Give_f(edict_t *ent)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (it->flags & IT_NOT_GIVEABLE)
|
||||||
|
{
|
||||||
|
gi.dprintf("item cannot be given\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
index = ITEM_INDEX(it);
|
index = ITEM_INDEX(it);
|
||||||
|
|
||||||
if (it->flags & IT_AMMO)
|
if (it->flags & IT_AMMO)
|
||||||
|
@ -411,6 +423,13 @@ Cmd_Give_f(edict_t *ent)
|
||||||
it_ent = G_Spawn();
|
it_ent = G_Spawn();
|
||||||
it_ent->classname = it->classname;
|
it_ent->classname = it->classname;
|
||||||
SpawnItem(it_ent, it);
|
SpawnItem(it_ent, it);
|
||||||
|
|
||||||
|
/* since some items don't actually spawn when you say to .. */
|
||||||
|
if (!it_ent->inuse)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Touch_Item(it_ent, ent, NULL, NULL);
|
Touch_Item(it_ent, ent, NULL, NULL);
|
||||||
|
|
||||||
if (it_ent->inuse)
|
if (it_ent->inuse)
|
||||||
|
@ -435,7 +454,7 @@ Cmd_God_f(edict_t *ent)
|
||||||
|
|
||||||
if ((deathmatch->value || coop->value) && !sv_cheats->value)
|
if ((deathmatch->value || coop->value) && !sv_cheats->value)
|
||||||
{
|
{
|
||||||
gi.cprintf( ent, PRINT_HIGH,
|
gi.cprintf(ent, PRINT_HIGH,
|
||||||
"You must run the server with '+set cheats 1' to enable this command.\n");
|
"You must run the server with '+set cheats 1' to enable this command.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -469,7 +488,7 @@ Cmd_Notarget_f(edict_t *ent)
|
||||||
|
|
||||||
if ((deathmatch->value || coop->value) && !sv_cheats->value)
|
if ((deathmatch->value || coop->value) && !sv_cheats->value)
|
||||||
{
|
{
|
||||||
gi.cprintf( ent, PRINT_HIGH,
|
gi.cprintf(ent, PRINT_HIGH,
|
||||||
"You must run the server with '+set cheats 1' to enable this command.\n");
|
"You must run the server with '+set cheats 1' to enable this command.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -503,7 +522,7 @@ Cmd_Noclip_f(edict_t *ent)
|
||||||
|
|
||||||
if ((deathmatch->value || coop->value) && !sv_cheats->value)
|
if ((deathmatch->value || coop->value) && !sv_cheats->value)
|
||||||
{
|
{
|
||||||
gi.cprintf( ent, PRINT_HIGH,
|
gi.cprintf(ent, PRINT_HIGH,
|
||||||
"You must run the server with '+set cheats 1' to enable this command.\n");
|
"You must run the server with '+set cheats 1' to enable this command.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -556,8 +575,33 @@ Cmd_Use_f(edict_t *ent)
|
||||||
|
|
||||||
if (!ent->client->pers.inventory[index])
|
if (!ent->client->pers.inventory[index])
|
||||||
{
|
{
|
||||||
gi.cprintf(ent, PRINT_HIGH, "Out of item: %s\n", s);
|
if (strcmp(it->pickup_name, "HyperBlaster") == 0)
|
||||||
return;
|
{
|
||||||
|
it = FindItem("Ionripper");
|
||||||
|
index = ITEM_INDEX(it);
|
||||||
|
|
||||||
|
if (!ent->client->pers.inventory[index])
|
||||||
|
{
|
||||||
|
gi.cprintf(ent, PRINT_HIGH, "Out of item: %s\n", s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(it->pickup_name, "Railgun") == 0)
|
||||||
|
{
|
||||||
|
it = FindItem("Phalanx");
|
||||||
|
index = ITEM_INDEX(it);
|
||||||
|
|
||||||
|
if (!ent->client->pers.inventory[index])
|
||||||
|
{
|
||||||
|
gi.cprintf(ent, PRINT_HIGH, "Out of item: %s\n", s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gi.cprintf(ent, PRINT_HIGH, "Out of item: %s\n", s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
it->use(ent, it);
|
it->use(ent, it);
|
||||||
|
@ -597,8 +641,33 @@ Cmd_Drop_f(edict_t *ent)
|
||||||
|
|
||||||
if (!ent->client->pers.inventory[index])
|
if (!ent->client->pers.inventory[index])
|
||||||
{
|
{
|
||||||
gi.cprintf(ent, PRINT_HIGH, "Out of item: %s\n", s);
|
if (strcmp(it->pickup_name, "HyperBlaster") == 0)
|
||||||
return;
|
{
|
||||||
|
it = FindItem("Ionripper");
|
||||||
|
index = ITEM_INDEX(it);
|
||||||
|
|
||||||
|
if (!ent->client->pers.inventory[index])
|
||||||
|
{
|
||||||
|
gi.cprintf(ent, PRINT_HIGH, "Out of item: %s\n", s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp(it->pickup_name, "Railgun") == 0)
|
||||||
|
{
|
||||||
|
it = FindItem("Phalanx");
|
||||||
|
index = ITEM_INDEX(it);
|
||||||
|
|
||||||
|
if (!ent->client->pers.inventory[index])
|
||||||
|
{
|
||||||
|
gi.cprintf(ent, PRINT_HIGH, "Out of item: %s\n", s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gi.cprintf(ent, PRINT_HIGH, "Out of item: %s\n", s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
it->drop(ent, it);
|
it->drop(ent, it);
|
||||||
|
@ -742,7 +811,8 @@ Cmd_WeapPrev_f(edict_t *ent)
|
||||||
/* scan for the next valid one */
|
/* scan for the next valid one */
|
||||||
for (i = 1; i <= MAX_ITEMS; i++)
|
for (i = 1; i <= MAX_ITEMS; i++)
|
||||||
{
|
{
|
||||||
index = (selected_weapon + i) % MAX_ITEMS;
|
/* prevent scrolling through ALL weapons */
|
||||||
|
index = (selected_weapon + MAX_ITEMS - i) % MAX_ITEMS;
|
||||||
|
|
||||||
if (!cl->pers.inventory[index])
|
if (!cl->pers.inventory[index])
|
||||||
{
|
{
|
||||||
|
@ -763,9 +833,10 @@ Cmd_WeapPrev_f(edict_t *ent)
|
||||||
|
|
||||||
it->use(ent, it);
|
it->use(ent, it);
|
||||||
|
|
||||||
if (cl->pers.weapon == it)
|
/* prevent scrolling through ALL weapons */
|
||||||
|
if (cl->newweapon == it)
|
||||||
{
|
{
|
||||||
return; /* successful */
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -795,7 +866,8 @@ Cmd_WeapNext_f(edict_t *ent)
|
||||||
/* scan for the next valid one */
|
/* scan for the next valid one */
|
||||||
for (i = 1; i <= MAX_ITEMS; i++)
|
for (i = 1; i <= MAX_ITEMS; i++)
|
||||||
{
|
{
|
||||||
index = (selected_weapon + MAX_ITEMS - i) % MAX_ITEMS;
|
/* prevent scrolling through ALL weapons */
|
||||||
|
index = (selected_weapon + i) % MAX_ITEMS;
|
||||||
|
|
||||||
if (!cl->pers.inventory[index])
|
if (!cl->pers.inventory[index])
|
||||||
{
|
{
|
||||||
|
@ -816,9 +888,10 @@ Cmd_WeapNext_f(edict_t *ent)
|
||||||
|
|
||||||
it->use(ent, it);
|
it->use(ent, it);
|
||||||
|
|
||||||
if (cl->pers.weapon == it)
|
/* prevent scrolling through ALL weapons */
|
||||||
|
if (cl->newweapon == it)
|
||||||
{
|
{
|
||||||
return; /* successful */
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -910,6 +983,19 @@ Cmd_Kill_f(edict_t *ent)
|
||||||
ent->flags &= ~FL_GODMODE;
|
ent->flags &= ~FL_GODMODE;
|
||||||
ent->health = 0;
|
ent->health = 0;
|
||||||
meansOfDeath = MOD_SUICIDE;
|
meansOfDeath = MOD_SUICIDE;
|
||||||
|
|
||||||
|
/* make sure no trackers are still hurting us. */
|
||||||
|
if (ent->client->tracker_pain_framenum)
|
||||||
|
{
|
||||||
|
RemoveAttackingPainDaemons(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent->client->owned_sphere)
|
||||||
|
{
|
||||||
|
G_FreeEdict(ent->client->owned_sphere);
|
||||||
|
ent->client->owned_sphere = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
player_die(ent, ent, ent, 100000, vec3_origin);
|
player_die(ent, ent, ent, 100000, vec3_origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1224,6 +1310,30 @@ Cmd_Say_f(edict_t *ent, qboolean team, qboolean arg0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Cmd_Ent_Count_f(edict_t *ent)
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
edict_t *e;
|
||||||
|
|
||||||
|
if (!ent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = 0;
|
||||||
|
|
||||||
|
for (e = g_edicts; e < &g_edicts[globals.num_edicts]; e++)
|
||||||
|
{
|
||||||
|
if (e->inuse)
|
||||||
|
{
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gi.dprintf("%d entites active\n", x);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Cmd_PlayerList_f(edict_t *ent)
|
Cmd_PlayerList_f(edict_t *ent)
|
||||||
{
|
{
|
||||||
|
@ -1242,6 +1352,8 @@ Cmd_PlayerList_f(edict_t *ent)
|
||||||
|
|
||||||
for (i = 0, e2 = g_edicts + 1; i < maxclients->value; i++, e2++)
|
for (i = 0, e2 = g_edicts + 1; i < maxclients->value; i++, e2++)
|
||||||
{
|
{
|
||||||
|
int text_len;
|
||||||
|
|
||||||
if (!e2->inuse)
|
if (!e2->inuse)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
@ -1255,9 +1367,11 @@ Cmd_PlayerList_f(edict_t *ent)
|
||||||
e2->client->pers.netname,
|
e2->client->pers.netname,
|
||||||
e2->client->resp.spectator ? " (spectator)" : "");
|
e2->client->resp.spectator ? " (spectator)" : "");
|
||||||
|
|
||||||
if (strlen(text) + strlen(st) > sizeof(text) - 50)
|
text_len = strlen(text);
|
||||||
|
|
||||||
|
if ((text_len + strlen(st)) > (sizeof(text) - 50))
|
||||||
{
|
{
|
||||||
strcpy(text + strlen(text), "And more...\n");
|
snprintf(text + text_len, sizeof(text) - text_len, "And more...\n");
|
||||||
gi.cprintf(ent, PRINT_HIGH, "%s", text);
|
gi.cprintf(ent, PRINT_HIGH, "%s", text);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1962,6 +2076,14 @@ ClientCommand(edict_t *ent)
|
||||||
{
|
{
|
||||||
Cmd_PlayerList_f(ent);
|
Cmd_PlayerList_f(ent);
|
||||||
}
|
}
|
||||||
|
else if (Q_stricmp(cmd, "entcount") == 0)
|
||||||
|
{
|
||||||
|
Cmd_Ent_Count_f(ent);
|
||||||
|
}
|
||||||
|
else if (Q_stricmp(cmd, "disguise") == 0)
|
||||||
|
{
|
||||||
|
ent->flags |= FL_DISGUISED;
|
||||||
|
}
|
||||||
else if (Q_stricmp(cmd, "teleport") == 0)
|
else if (Q_stricmp(cmd, "teleport") == 0)
|
||||||
{
|
{
|
||||||
Cmd_Teleport_f(ent);
|
Cmd_Teleport_f(ent);
|
||||||
|
|
|
@ -200,7 +200,7 @@ SpawnDamage(int type, vec3_t origin, vec3_t normal)
|
||||||
* targ entity that is being damaged
|
* targ entity that is being damaged
|
||||||
* inflictor entity that is causing the damage
|
* inflictor entity that is causing the damage
|
||||||
* attacker entity that caused the inflictor to damage targ
|
* attacker entity that caused the inflictor to damage targ
|
||||||
* example: targ=monster, inflictor=rocket, attacker=player
|
* example: targ=monster, inflictor=rocket, attacker=player
|
||||||
*
|
*
|
||||||
* dir direction of the attack
|
* dir direction of the attack
|
||||||
* point point at which the damage is being inflicted
|
* point point at which the damage is being inflicted
|
||||||
|
@ -217,8 +217,8 @@ SpawnDamage(int type, vec3_t origin, vec3_t normal)
|
||||||
* DAMAGE_NO_PROTECTION kills godmode, armor, everything
|
* DAMAGE_NO_PROTECTION kills godmode, armor, everything
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
CheckPowerArmor(edict_t *ent, vec3_t point, vec3_t normal, int damage,
|
CheckPowerArmor(edict_t *ent, vec3_t point, vec3_t normal,
|
||||||
int dflags)
|
int damage, int dflags)
|
||||||
{
|
{
|
||||||
gclient_t *client;
|
gclient_t *client;
|
||||||
int save;
|
int save;
|
||||||
|
@ -441,7 +441,7 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker)
|
||||||
targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
|
targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
|
||||||
|
|
||||||
/* this can only happen in coop (both new and old
|
/* this can only happen in coop (both new and old
|
||||||
enemies are clients) only switch if can't see
|
enemies are clients) only switch if can't see
|
||||||
the current enemy */
|
the current enemy */
|
||||||
if (targ->enemy && targ->enemy->client)
|
if (targ->enemy && targ->enemy->client)
|
||||||
{
|
{
|
||||||
|
@ -533,9 +533,9 @@ apply_knockback(edict_t *targ, vec3_t dir, float knockback, float scale)
|
||||||
|
|
||||||
mass = (targ->mass < 50) ? 50.0f : (float)targ->mass;
|
mass = (targ->mass < 50) ? 50.0f : (float)targ->mass;
|
||||||
|
|
||||||
VectorNormalize2 (dir, kvel);
|
VectorNormalize2(dir, kvel);
|
||||||
VectorScale (kvel, scale * (knockback / mass), kvel);
|
VectorScale(kvel, scale * (knockback / mass), kvel);
|
||||||
VectorAdd (targ->velocity, kvel, targ->velocity);
|
VectorAdd(targ->velocity, kvel, targ->velocity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -564,7 +564,7 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
|
||||||
can't hurt teammates (but you can hurt
|
can't hurt teammates (but you can hurt
|
||||||
yourself) knockback still occurs */
|
yourself) knockback still occurs */
|
||||||
if ((targ != attacker) && ((deathmatch->value &&
|
if ((targ != attacker) && ((deathmatch->value &&
|
||||||
((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) ||
|
((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) ||
|
||||||
coop->value))
|
coop->value))
|
||||||
{
|
{
|
||||||
if (OnSameTeam(targ, attacker))
|
if (OnSameTeam(targ, attacker))
|
||||||
|
@ -699,7 +699,7 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
|
||||||
{
|
{
|
||||||
M_ReactToDamage(targ, attacker);
|
M_ReactToDamage(targ, attacker);
|
||||||
|
|
||||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take))
|
if (!(targ->monsterinfo.aiflags & (AI_DUCKED|AI_IGNORE_PAIN)) && (take))
|
||||||
{
|
{
|
||||||
targ->pain(targ, attacker, knockback, take);
|
targ->pain(targ, attacker, knockback, take);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||||
|
* Copyright (c) ZeniMax Media Inc.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -176,8 +177,10 @@ Move_Calc(edict_t *ent, vec3_t dest, void (*func)(edict_t *))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Support routines for angular movement (changes in angle using avelocity) */
|
/*
|
||||||
|
* Support routines for angular movement
|
||||||
|
* (changes in angle using avelocity)
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
AngleMove_Done(edict_t *ent)
|
AngleMove_Done(edict_t *ent)
|
||||||
{
|
{
|
||||||
|
@ -289,6 +292,11 @@ AngleMove_Calc(edict_t *ent, void (*func)(edict_t *))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The team has completed a frame of movement, so
|
||||||
|
* change the speed for the next frame
|
||||||
|
*/
|
||||||
|
|
||||||
#define AccelerationDistance(target, rate) (target * ((target / rate) + 1) / 2)
|
#define AccelerationDistance(target, rate) (target * ((target / rate) + 1) / 2)
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -691,7 +699,6 @@ plat_spawn_inside_trigger(edict_t *ent)
|
||||||
|
|
||||||
tmin[0] = ent->mins[0] + 25;
|
tmin[0] = ent->mins[0] + 25;
|
||||||
tmin[1] = ent->mins[1] + 25;
|
tmin[1] = ent->mins[1] + 25;
|
||||||
// tmin[2] = ent->mins[2];
|
|
||||||
|
|
||||||
tmax[0] = ent->maxs[0] - 25;
|
tmax[0] = ent->maxs[0] - 25;
|
||||||
tmax[1] = ent->maxs[1] - 25;
|
tmax[1] = ent->maxs[1] - 25;
|
||||||
|
@ -1648,8 +1655,9 @@ door_blocked(edict_t *self, edict_t *other)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if a door has a negative wait, it would never come back if blocked,
|
/* if a door has a negative wait, it would never
|
||||||
so let it just squash the object to death real fast */
|
come back if blocked, so let it just squash the
|
||||||
|
object to death real fast */
|
||||||
if (self->moveinfo.wait >= 0)
|
if (self->moveinfo.wait >= 0)
|
||||||
{
|
{
|
||||||
if (self->moveinfo.state == STATE_DOWN)
|
if (self->moveinfo.state == STATE_DOWN)
|
||||||
|
@ -2452,8 +2460,8 @@ SP_func_train(edict_t *self)
|
||||||
|
|
||||||
if (self->target)
|
if (self->target)
|
||||||
{
|
{
|
||||||
/* start trains on the second frame, to make sure
|
/* start trains on the second frame, to make
|
||||||
their targets have had a chance to spawn */
|
* sure their targets have had a chance to spawn */
|
||||||
self->nextthink = level.time + FRAMETIME;
|
self->nextthink = level.time + FRAMETIME;
|
||||||
self->think = func_train_find;
|
self->think = func_train_find;
|
||||||
}
|
}
|
||||||
|
@ -3010,3 +3018,231 @@ SP_func_killbox(edict_t *ent)
|
||||||
ent->use = use_killbox;
|
ent->use = use_killbox;
|
||||||
ent->svflags = SVF_NOCLIENT;
|
ent->svflags = SVF_NOCLIENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QUAKED rotating_light (0 .5 .8) (-8 -8 -8) (8 8 8) START_OFF ALARM
|
||||||
|
* "health" if set, the light may be killed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define START_OFF 1
|
||||||
|
|
||||||
|
void
|
||||||
|
rotating_light_alarm(edict_t *self)
|
||||||
|
{
|
||||||
|
if (!self)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->spawnflags & START_OFF)
|
||||||
|
{
|
||||||
|
self->think = NULL;
|
||||||
|
self->nextthink = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gi.sound(self, CHAN_NO_PHS_ADD + CHAN_VOICE,
|
||||||
|
self->moveinfo.sound_start, 1,
|
||||||
|
ATTN_STATIC, 0);
|
||||||
|
self->nextthink = level.time + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rotating_light_killed(edict_t *self, edict_t *inflictor /* unused */,
|
||||||
|
edict_t *attacker /* unused */, int damage /* unused */,
|
||||||
|
vec3_t point /* unused */)
|
||||||
|
{
|
||||||
|
if (!self)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gi.WriteByte(svc_temp_entity);
|
||||||
|
gi.WriteByte(TE_WELDING_SPARKS);
|
||||||
|
gi.WriteByte(30);
|
||||||
|
gi.WritePosition(self->s.origin);
|
||||||
|
gi.WriteDir(vec3_origin);
|
||||||
|
gi.WriteByte(0xe0 + (rand() & 7));
|
||||||
|
gi.multicast(self->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
self->s.effects &= ~EF_SPINNINGLIGHTS;
|
||||||
|
self->use = NULL;
|
||||||
|
|
||||||
|
self->think = G_FreeEdict;
|
||||||
|
self->nextthink = level.time + 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
rotating_light_use(edict_t *self, edict_t *other /* unused */,
|
||||||
|
edict_t *activator /* unused */)
|
||||||
|
{
|
||||||
|
if (!self)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->spawnflags & START_OFF)
|
||||||
|
{
|
||||||
|
self->spawnflags &= ~START_OFF;
|
||||||
|
self->s.effects |= EF_SPINNINGLIGHTS;
|
||||||
|
|
||||||
|
if (self->spawnflags & 2)
|
||||||
|
{
|
||||||
|
self->think = rotating_light_alarm;
|
||||||
|
self->nextthink = level.time + 0.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->spawnflags |= START_OFF;
|
||||||
|
self->s.effects &= ~EF_SPINNINGLIGHTS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SP_rotating_light(edict_t *self)
|
||||||
|
{
|
||||||
|
if (!self)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->movetype = MOVETYPE_STOP;
|
||||||
|
self->solid = SOLID_BBOX;
|
||||||
|
|
||||||
|
self->s.modelindex = gi.modelindex("models/objects/light/tris.md2");
|
||||||
|
|
||||||
|
self->s.frame = 0;
|
||||||
|
|
||||||
|
self->use = rotating_light_use;
|
||||||
|
|
||||||
|
if (self->spawnflags & START_OFF)
|
||||||
|
{
|
||||||
|
self->s.effects &= ~EF_SPINNINGLIGHTS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->s.effects |= EF_SPINNINGLIGHTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->speed)
|
||||||
|
{
|
||||||
|
self->speed = 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->health)
|
||||||
|
{
|
||||||
|
self->health = 10;
|
||||||
|
self->max_health = self->health;
|
||||||
|
self->die = rotating_light_killed;
|
||||||
|
self->takedamage = DAMAGE_YES;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->max_health = self->health;
|
||||||
|
self->die = rotating_light_killed;
|
||||||
|
self->takedamage = DAMAGE_YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->spawnflags & 2)
|
||||||
|
{
|
||||||
|
self->moveinfo.sound_start = gi.soundindex("misc/alarm.wav");
|
||||||
|
}
|
||||||
|
|
||||||
|
gi.linkentity(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QUAKED func_object_repair (1 .5 0) (-8 -8 -8) (8 8 8)
|
||||||
|
* object to be repaired.
|
||||||
|
* The default delay is 1 second
|
||||||
|
* "delay" the delay in seconds for spark to occur
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
object_repair_fx(edict_t *ent)
|
||||||
|
{
|
||||||
|
if (!ent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->nextthink = level.time + ent->delay;
|
||||||
|
|
||||||
|
if (ent->health <= 100)
|
||||||
|
{
|
||||||
|
ent->health++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gi.WriteByte(svc_temp_entity);
|
||||||
|
gi.WriteByte(TE_WELDING_SPARKS);
|
||||||
|
gi.WriteByte(10);
|
||||||
|
gi.WritePosition(ent->s.origin);
|
||||||
|
gi.WriteDir(vec3_origin);
|
||||||
|
gi.WriteByte(0xe0 + (rand() & 7));
|
||||||
|
gi.multicast(ent->s.origin, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
object_repair_dead(edict_t *ent)
|
||||||
|
{
|
||||||
|
if (!ent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_UseTargets(ent, ent);
|
||||||
|
ent->nextthink = level.time + 0.1;
|
||||||
|
ent->think = object_repair_fx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
object_repair_sparks(edict_t *ent)
|
||||||
|
{
|
||||||
|
if (!ent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent->health < 0)
|
||||||
|
{
|
||||||
|
ent->nextthink = level.time + 0.1;
|
||||||
|
ent->think = object_repair_dead;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->nextthink = level.time + ent->delay;
|
||||||
|
|
||||||
|
gi.WriteByte(svc_temp_entity);
|
||||||
|
gi.WriteByte(TE_WELDING_SPARKS);
|
||||||
|
gi.WriteByte(10);
|
||||||
|
gi.WritePosition(ent->s.origin);
|
||||||
|
gi.WriteDir(vec3_origin);
|
||||||
|
gi.WriteByte(0xe0 + (rand() & 7));
|
||||||
|
gi.multicast(ent->s.origin, MULTICAST_PVS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SP_object_repair(edict_t *ent)
|
||||||
|
{
|
||||||
|
if (!ent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->movetype = MOVETYPE_NONE;
|
||||||
|
ent->solid = SOLID_BBOX;
|
||||||
|
ent->classname = "object_repair";
|
||||||
|
ent->think = object_repair_sparks;
|
||||||
|
ent->nextthink = level.time + 1.0;
|
||||||
|
ent->health = 100;
|
||||||
|
|
||||||
|
if (!ent->delay)
|
||||||
|
{
|
||||||
|
ent->delay = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
gi.linkentity(ent);
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||||
|
* Copyright (c) ZeniMax Media Inc.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -46,6 +47,10 @@ void Weapon_GrenadeLauncher(edict_t *ent);
|
||||||
void Weapon_Railgun(edict_t *ent);
|
void Weapon_Railgun(edict_t *ent);
|
||||||
void Weapon_BFG(edict_t *ent);
|
void Weapon_BFG(edict_t *ent);
|
||||||
|
|
||||||
|
void Weapon_Ionripper(edict_t *ent);
|
||||||
|
void Weapon_Phalanx(edict_t *ent);
|
||||||
|
void Weapon_Trap(edict_t *ent);
|
||||||
|
|
||||||
static gitem_armor_t jacketarmor_info = {25, 50, .30, .00, ARMOR_JACKET};
|
static gitem_armor_t jacketarmor_info = {25, 50, .30, .00, ARMOR_JACKET};
|
||||||
static gitem_armor_t combatarmor_info = {50, 100, .60, .30, ARMOR_COMBAT};
|
static gitem_armor_t combatarmor_info = {50, 100, .60, .30, ARMOR_COMBAT};
|
||||||
static gitem_armor_t bodyarmor_info = {100, 200, .80, .60, ARMOR_BODY};
|
static gitem_armor_t bodyarmor_info = {100, 200, .80, .60, ARMOR_BODY};
|
||||||
|
@ -60,6 +65,7 @@ void Use_Quad(edict_t *ent, gitem_t *item);
|
||||||
void Use_QuadFire(edict_t *ent, gitem_t *item);
|
void Use_QuadFire(edict_t *ent, gitem_t *item);
|
||||||
|
|
||||||
static int quad_drop_timeout_hack;
|
static int quad_drop_timeout_hack;
|
||||||
|
static int quad_fire_drop_timeout_hack;
|
||||||
|
|
||||||
/* ====================================================================== */
|
/* ====================================================================== */
|
||||||
|
|
||||||
|
@ -226,6 +232,11 @@ Pickup_Powerup(edict_t *ent, edict_t *other)
|
||||||
void
|
void
|
||||||
Drop_General(edict_t *ent, gitem_t *item)
|
Drop_General(edict_t *ent, gitem_t *item)
|
||||||
{
|
{
|
||||||
|
if (!ent || !item)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Drop_Item(ent, item);
|
Drop_Item(ent, item);
|
||||||
ent->client->pers.inventory[ITEM_INDEX(item)]--;
|
ent->client->pers.inventory[ITEM_INDEX(item)]--;
|
||||||
ValidateSelectedItem(ent);
|
ValidateSelectedItem(ent);
|
||||||
|
@ -308,6 +319,11 @@ Pickup_Bandolier(edict_t *ent, edict_t *other)
|
||||||
other->client->pers.max_slugs = 75;
|
other->client->pers.max_slugs = 75;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (other->client->pers.max_magslug < 75)
|
||||||
|
{
|
||||||
|
other->client->pers.max_magslug = 75;
|
||||||
|
}
|
||||||
|
|
||||||
item = FindItem("Bullets");
|
item = FindItem("Bullets");
|
||||||
|
|
||||||
if (item)
|
if (item)
|
||||||
|
@ -387,6 +403,11 @@ Pickup_Pack(edict_t *ent, edict_t *other)
|
||||||
other->client->pers.max_slugs = 100;
|
other->client->pers.max_slugs = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (other->client->pers.max_magslug < 100)
|
||||||
|
{
|
||||||
|
other->client->pers.max_magslug = 100;
|
||||||
|
}
|
||||||
|
|
||||||
item = FindItem("Bullets");
|
item = FindItem("Bullets");
|
||||||
|
|
||||||
if (item)
|
if (item)
|
||||||
|
@ -477,6 +498,21 @@ Pickup_Pack(edict_t *ent, edict_t *other)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
item = FindItem("Mag Slug");
|
||||||
|
|
||||||
|
if (item)
|
||||||
|
{
|
||||||
|
index = ITEM_INDEX(item);
|
||||||
|
other->client->pers.inventory[index] += item->quantity;
|
||||||
|
|
||||||
|
if (other->client->pers.inventory[index] >
|
||||||
|
other->client->pers.max_magslug)
|
||||||
|
{
|
||||||
|
other->client->pers.inventory[index] =
|
||||||
|
other->client->pers.max_magslug;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value))
|
if (!(ent->spawnflags & DROPPED_ITEM) && (deathmatch->value))
|
||||||
{
|
{
|
||||||
SetRespawn(ent, ent->item->quantity);
|
SetRespawn(ent, ent->item->quantity);
|
||||||
|
@ -522,6 +558,43 @@ Use_Quad(edict_t *ent, gitem_t *item)
|
||||||
gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage.wav"), 1, ATTN_NORM, 0);
|
gi.sound(ent, CHAN_ITEM, gi.soundindex("items/damage.wav"), 1, ATTN_NORM, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ===================================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
Use_QuadFire(edict_t *ent, gitem_t *item)
|
||||||
|
{
|
||||||
|
int timeout;
|
||||||
|
|
||||||
|
if (!ent || !item)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->client->pers.inventory[ITEM_INDEX(item)]--;
|
||||||
|
ValidateSelectedItem(ent);
|
||||||
|
|
||||||
|
if (quad_fire_drop_timeout_hack)
|
||||||
|
{
|
||||||
|
timeout = quad_fire_drop_timeout_hack;
|
||||||
|
quad_fire_drop_timeout_hack = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
timeout = 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent->client->quadfire_framenum > level.framenum)
|
||||||
|
{
|
||||||
|
ent->client->quadfire_framenum += timeout;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ent->client->quadfire_framenum = level.framenum + timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
gi.sound(ent, CHAN_ITEM, gi.soundindex("items/quadfire1.wav"), 1, ATTN_NORM, 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* ====================================================================== */
|
/* ====================================================================== */
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -691,6 +764,14 @@ Add_Ammo(edict_t *ent, gitem_t *item, int count)
|
||||||
{
|
{
|
||||||
max = ent->client->pers.max_slugs;
|
max = ent->client->pers.max_slugs;
|
||||||
}
|
}
|
||||||
|
else if (item->tag == AMMO_MAGSLUG)
|
||||||
|
{
|
||||||
|
max = ent->client->pers.max_magslug;
|
||||||
|
}
|
||||||
|
else if (item->tag == AMMO_TRAP)
|
||||||
|
{
|
||||||
|
max = ent->client->pers.max_trap;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -941,12 +1022,16 @@ Pickup_Armor(edict_t *ent, edict_t *other)
|
||||||
other->client->pers.inventory[old_armor_index] += 2;
|
other->client->pers.inventory[old_armor_index] += 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!old_armor_index) /* if player has no armor, just use it */
|
|
||||||
|
/* if player has no armor, just use it */
|
||||||
|
else if (!old_armor_index)
|
||||||
{
|
{
|
||||||
other->client->pers.inventory[ITEM_INDEX(ent->item)] =
|
other->client->pers.inventory[ITEM_INDEX(ent->item)] =
|
||||||
newinfo->base_count;
|
newinfo->base_count;
|
||||||
}
|
}
|
||||||
else /* use the better armor */
|
|
||||||
|
/* use the better armor */
|
||||||
|
else
|
||||||
{
|
{
|
||||||
/* get info on old armor */
|
/* get info on old armor */
|
||||||
if (old_armor_index == jacket_armor_index)
|
if (old_armor_index == jacket_armor_index)
|
||||||
|
@ -957,7 +1042,7 @@ Pickup_Armor(edict_t *ent, edict_t *other)
|
||||||
{
|
{
|
||||||
oldinfo = &combatarmor_info;
|
oldinfo = &combatarmor_info;
|
||||||
}
|
}
|
||||||
else
|
else /* (old_armor_index == body_armor_index) */
|
||||||
{
|
{
|
||||||
oldinfo = &bodyarmor_info;
|
oldinfo = &bodyarmor_info;
|
||||||
}
|
}
|
||||||
|
@ -1162,9 +1247,9 @@ Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane /* unused */, csurface_
|
||||||
|
|
||||||
/* show icon and name on status bar */
|
/* show icon and name on status bar */
|
||||||
other->client->ps.stats[STAT_PICKUP_ICON] =
|
other->client->ps.stats[STAT_PICKUP_ICON] =
|
||||||
gi.imageindex( ent->item->icon);
|
gi.imageindex(ent->item->icon);
|
||||||
other->client->ps.stats[STAT_PICKUP_STRING] =
|
other->client->ps.stats[STAT_PICKUP_STRING] =
|
||||||
CS_ITEMS + ITEM_INDEX( ent->item);
|
CS_ITEMS + ITEM_INDEX(ent->item);
|
||||||
other->client->pickup_msg_time = level.time + 3.0;
|
other->client->pickup_msg_time = level.time + 3.0;
|
||||||
|
|
||||||
/* change selected item */
|
/* change selected item */
|
||||||
|
@ -1172,7 +1257,7 @@ Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane /* unused */, csurface_
|
||||||
{
|
{
|
||||||
other->client->pers.selected_item =
|
other->client->pers.selected_item =
|
||||||
other->client->ps.stats[STAT_SELECTED_ITEM] =
|
other->client->ps.stats[STAT_SELECTED_ITEM] =
|
||||||
ITEM_INDEX( ent->item);
|
ITEM_INDEX(ent->item);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ent->item->pickup == Pickup_Health)
|
if (ent->item->pickup == Pickup_Health)
|
||||||
|
@ -1210,14 +1295,21 @@ Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane /* unused */, csurface_
|
||||||
{
|
{
|
||||||
if ((((int)dmflags->value & DF_INSTANT_ITEMS) &&
|
if ((((int)dmflags->value & DF_INSTANT_ITEMS) &&
|
||||||
(ent->item->flags & IT_INSTANT_USE)) ||
|
(ent->item->flags & IT_INSTANT_USE)) ||
|
||||||
((ent->item->use == Use_Quad) &&
|
(((ent->item->use == Use_Quad) || (ent->item->use == Use_QuadFire)) &&
|
||||||
(ent->spawnflags & DROPPED_PLAYER_ITEM)))
|
(ent->spawnflags & DROPPED_PLAYER_ITEM)))
|
||||||
{
|
{
|
||||||
if ((ent->item->use == Use_Quad) &&
|
if (ent->spawnflags & DROPPED_PLAYER_ITEM)
|
||||||
(ent->spawnflags & DROPPED_PLAYER_ITEM))
|
|
||||||
{
|
{
|
||||||
quad_drop_timeout_hack =
|
if (ent->item->use == Use_Quad)
|
||||||
(ent->nextthink - level.time) / FRAMETIME;
|
{
|
||||||
|
quad_drop_timeout_hack =
|
||||||
|
(ent->nextthink - level.time) / FRAMETIME;
|
||||||
|
}
|
||||||
|
else if (ent->item->use == Use_QuadFire)
|
||||||
|
{
|
||||||
|
quad_fire_drop_timeout_hack =
|
||||||
|
(ent->nextthink - level.time) / FRAMETIME;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ent->item->use)
|
if (ent->item->use)
|
||||||
|
@ -1423,10 +1515,19 @@ droptofloor(edict_t *ent)
|
||||||
|
|
||||||
if (tr.startsolid)
|
if (tr.startsolid)
|
||||||
{
|
{
|
||||||
gi.dprintf("droptofloor: %s startsolid at %s\n", ent->classname,
|
if (strcmp(ent->classname, "foodcube") == 0)
|
||||||
vtos(ent->s.origin));
|
{
|
||||||
G_FreeEdict(ent);
|
VectorCopy(ent->s.origin, tr.endpos);
|
||||||
return;
|
ent->velocity[2] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gi.dprintf("droptofloor: %s startsolid at %s\n",
|
||||||
|
ent->classname,
|
||||||
|
vtos(ent->s.origin));
|
||||||
|
G_FreeEdict(ent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VectorCopy(tr.endpos, ent->s.origin);
|
VectorCopy(tr.endpos, ent->s.origin);
|
||||||
|
@ -1672,7 +1773,10 @@ static const gitem_t gameitemlist[] = {
|
||||||
NULL
|
NULL
|
||||||
}, /* leave index 0 alone */
|
}, /* leave index 0 alone */
|
||||||
|
|
||||||
/* QUAKED item_armor_body (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
|
||||||
|
/*
|
||||||
|
* QUAKED item_armor_body (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"item_armor_body",
|
"item_armor_body",
|
||||||
Pickup_Armor,
|
Pickup_Armor,
|
||||||
|
@ -1694,7 +1798,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED item_armor_combat (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED item_armor_combat (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"item_armor_combat",
|
"item_armor_combat",
|
||||||
Pickup_Armor,
|
Pickup_Armor,
|
||||||
|
@ -1716,7 +1822,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED item_armor_jacket (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED item_armor_jacket (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"item_armor_jacket",
|
"item_armor_jacket",
|
||||||
Pickup_Armor,
|
Pickup_Armor,
|
||||||
|
@ -1738,7 +1846,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED item_armor_shard (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED item_armor_shard (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"item_armor_shard",
|
"item_armor_shard",
|
||||||
Pickup_Armor,
|
Pickup_Armor,
|
||||||
|
@ -1760,7 +1870,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED item_power_screen (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED item_power_screen (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"item_power_screen",
|
"item_power_screen",
|
||||||
Pickup_PowerArmor,
|
Pickup_PowerArmor,
|
||||||
|
@ -1782,7 +1894,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED item_power_shield (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED item_power_shield (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"item_power_shield",
|
"item_power_shield",
|
||||||
Pickup_PowerArmor,
|
Pickup_PowerArmor,
|
||||||
|
@ -1804,8 +1918,10 @@ static const gitem_t gameitemlist[] = {
|
||||||
"misc/power2.wav misc/power1.wav"
|
"misc/power2.wav misc/power1.wav"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* weapon_blaster (.3 .3 1) (-16 -16 -16) (16 16 16)
|
/*
|
||||||
always owned, never in the world */
|
* weapon_blaster (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
* always owned, never in the world
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"weapon_blaster",
|
"weapon_blaster",
|
||||||
NULL,
|
NULL,
|
||||||
|
@ -1827,7 +1943,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
"weapons/blastf1a.wav misc/lasfly.wav"
|
"weapons/blastf1a.wav misc/lasfly.wav"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED weapon_shotgun (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED weapon_shotgun (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"weapon_shotgun",
|
"weapon_shotgun",
|
||||||
Pickup_Weapon,
|
Pickup_Weapon,
|
||||||
|
@ -1849,7 +1967,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
"weapons/shotgf1b.wav weapons/shotgr1b.wav"
|
"weapons/shotgf1b.wav weapons/shotgr1b.wav"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED weapon_supershotgun (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED weapon_supershotgun (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"weapon_supershotgun",
|
"weapon_supershotgun",
|
||||||
Pickup_Weapon,
|
Pickup_Weapon,
|
||||||
|
@ -1871,7 +1991,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
"weapons/sshotf1b.wav"
|
"weapons/sshotf1b.wav"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED weapon_machinegun (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED weapon_machinegun (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"weapon_machinegun",
|
"weapon_machinegun",
|
||||||
Pickup_Weapon,
|
Pickup_Weapon,
|
||||||
|
@ -1890,10 +2012,13 @@ static const gitem_t gameitemlist[] = {
|
||||||
WEAP_MACHINEGUN,
|
WEAP_MACHINEGUN,
|
||||||
NULL,
|
NULL,
|
||||||
0,
|
0,
|
||||||
|
|
||||||
"weapons/machgf1b.wav weapons/machgf2b.wav weapons/machgf3b.wav weapons/machgf4b.wav weapons/machgf5b.wav"
|
"weapons/machgf1b.wav weapons/machgf2b.wav weapons/machgf3b.wav weapons/machgf4b.wav weapons/machgf5b.wav"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED weapon_chaingun (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED weapon_chaingun (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"weapon_chaingun",
|
"weapon_chaingun",
|
||||||
Pickup_Weapon,
|
Pickup_Weapon,
|
||||||
|
@ -1912,10 +2037,13 @@ static const gitem_t gameitemlist[] = {
|
||||||
WEAP_CHAINGUN,
|
WEAP_CHAINGUN,
|
||||||
NULL,
|
NULL,
|
||||||
0,
|
0,
|
||||||
|
|
||||||
"weapons/chngnu1a.wav weapons/chngnl1a.wav weapons/machgf3b.wav` weapons/chngnd1a.wav"
|
"weapons/chngnu1a.wav weapons/chngnl1a.wav weapons/machgf3b.wav` weapons/chngnd1a.wav"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED ammo_grenades (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED ammo_grenades (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"ammo_grenades",
|
"ammo_grenades",
|
||||||
Pickup_Ammo,
|
Pickup_Ammo,
|
||||||
|
@ -1934,10 +2062,38 @@ static const gitem_t gameitemlist[] = {
|
||||||
WEAP_GRENADES,
|
WEAP_GRENADES,
|
||||||
NULL,
|
NULL,
|
||||||
AMMO_GRENADES,
|
AMMO_GRENADES,
|
||||||
|
|
||||||
"weapons/hgrent1a.wav weapons/hgrena1b.wav weapons/hgrenc1b.wav weapons/hgrenb1a.wav weapons/hgrenb2a.wav "
|
"weapons/hgrent1a.wav weapons/hgrena1b.wav weapons/hgrenc1b.wav weapons/hgrenb1a.wav weapons/hgrenb2a.wav "
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED weapon_grenadelauncher (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED ammo_trap (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
"ammo_trap",
|
||||||
|
Pickup_Ammo,
|
||||||
|
Use_Weapon,
|
||||||
|
Drop_Ammo,
|
||||||
|
Weapon_Trap,
|
||||||
|
"misc/am_pkup.wav",
|
||||||
|
"models/weapons/g_trap/tris.md2", EF_ROTATE,
|
||||||
|
"models/weapons/v_trap/tris.md2",
|
||||||
|
"a_trap",
|
||||||
|
"Trap",
|
||||||
|
3,
|
||||||
|
1,
|
||||||
|
"trap",
|
||||||
|
IT_AMMO | IT_WEAPON,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
AMMO_TRAP,
|
||||||
|
|
||||||
|
"weapons/trapcock.wav weapons/traploop.wav weapons/trapsuck.wav weapons/trapdown.wav"
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QUAKED weapon_grenadelauncher (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"weapon_grenadelauncher",
|
"weapon_grenadelauncher",
|
||||||
Pickup_Weapon,
|
Pickup_Weapon,
|
||||||
|
@ -1956,10 +2112,13 @@ static const gitem_t gameitemlist[] = {
|
||||||
WEAP_GRENADELAUNCHER,
|
WEAP_GRENADELAUNCHER,
|
||||||
NULL,
|
NULL,
|
||||||
0,
|
0,
|
||||||
|
|
||||||
"models/objects/grenade/tris.md2 weapons/grenlf1a.wav weapons/grenlr1b.wav weapons/grenlb1b.wav"
|
"models/objects/grenade/tris.md2 weapons/grenlf1a.wav weapons/grenlr1b.wav weapons/grenlb1b.wav"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED weapon_rocketlauncher (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED weapon_rocketlauncher (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"weapon_rocketlauncher",
|
"weapon_rocketlauncher",
|
||||||
Pickup_Weapon,
|
Pickup_Weapon,
|
||||||
|
@ -1978,14 +2137,17 @@ static const gitem_t gameitemlist[] = {
|
||||||
WEAP_ROCKETLAUNCHER,
|
WEAP_ROCKETLAUNCHER,
|
||||||
NULL,
|
NULL,
|
||||||
0,
|
0,
|
||||||
|
|
||||||
"models/objects/rocket/tris.md2 weapons/rockfly.wav weapons/rocklf1a.wav weapons/rocklr1b.wav models/objects/debris2/tris.md2"
|
"models/objects/rocket/tris.md2 weapons/rockfly.wav weapons/rocklf1a.wav weapons/rocklr1b.wav models/objects/debris2/tris.md2"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED weapon_hyperblaster (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED weapon_hyperblaster (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"weapon_hyperblaster",
|
"weapon_hyperblaster",
|
||||||
Pickup_Weapon,
|
Pickup_Weapon,
|
||||||
Use_Weapon,
|
Use_Weapon2,
|
||||||
Drop_Weapon,
|
Drop_Weapon,
|
||||||
Weapon_HyperBlaster,
|
Weapon_HyperBlaster,
|
||||||
"misc/w_pkup.wav",
|
"misc/w_pkup.wav",
|
||||||
|
@ -2000,14 +2162,41 @@ static const gitem_t gameitemlist[] = {
|
||||||
WEAP_HYPERBLASTER,
|
WEAP_HYPERBLASTER,
|
||||||
NULL,
|
NULL,
|
||||||
0,
|
0,
|
||||||
|
|
||||||
"weapons/hyprbu1a.wav weapons/hyprbl1a.wav weapons/hyprbf1a.wav weapons/hyprbd1a.wav misc/lasfly.wav"
|
"weapons/hyprbu1a.wav weapons/hyprbl1a.wav weapons/hyprbf1a.wav weapons/hyprbd1a.wav misc/lasfly.wav"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED weapon_railgun (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED weapon_boomer (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
"weapon_boomer",
|
||||||
|
Pickup_Weapon,
|
||||||
|
Use_Weapon,
|
||||||
|
Drop_Weapon,
|
||||||
|
Weapon_Ionripper,
|
||||||
|
"misc/w_pkup.wav",
|
||||||
|
"models/weapons/g_boom/tris.md2", EF_ROTATE,
|
||||||
|
"models/weapons/v_boomer/tris.md2",
|
||||||
|
"w_ripper",
|
||||||
|
"Ionripper",
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
"Cells",
|
||||||
|
IT_WEAPON,
|
||||||
|
WEAP_BOOMER,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
"weapons/rg_hum.wav weapons/rippfire.wav"
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QUAKED weapon_railgun (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"weapon_railgun",
|
"weapon_railgun",
|
||||||
Pickup_Weapon,
|
Pickup_Weapon,
|
||||||
Use_Weapon,
|
Use_Weapon2,
|
||||||
Drop_Weapon,
|
Drop_Weapon,
|
||||||
Weapon_Railgun,
|
Weapon_Railgun,
|
||||||
"misc/w_pkup.wav",
|
"misc/w_pkup.wav",
|
||||||
|
@ -2025,7 +2214,34 @@ static const gitem_t gameitemlist[] = {
|
||||||
"weapons/rg_hum.wav"
|
"weapons/rg_hum.wav"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED weapon_bfg (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED weapon_phalanx (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
|
|
||||||
|
{
|
||||||
|
"weapon_phalanx",
|
||||||
|
Pickup_Weapon,
|
||||||
|
Use_Weapon,
|
||||||
|
Drop_Weapon,
|
||||||
|
Weapon_Phalanx,
|
||||||
|
"misc/w_pkup.wav",
|
||||||
|
"models/weapons/g_shotx/tris.md2", EF_ROTATE,
|
||||||
|
"models/weapons/v_shotx/tris.md2",
|
||||||
|
"w_phallanx",
|
||||||
|
"Phalanx",
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
"Mag Slug",
|
||||||
|
IT_WEAPON,
|
||||||
|
WEAP_PHALANX,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
"weapons/plasshot.wav"
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QUAKED weapon_bfg (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"weapon_bfg",
|
"weapon_bfg",
|
||||||
Pickup_Weapon,
|
Pickup_Weapon,
|
||||||
|
@ -2044,10 +2260,13 @@ static const gitem_t gameitemlist[] = {
|
||||||
WEAP_BFG,
|
WEAP_BFG,
|
||||||
NULL,
|
NULL,
|
||||||
0,
|
0,
|
||||||
|
|
||||||
"sprites/s_bfg1.sp2 sprites/s_bfg2.sp2 sprites/s_bfg3.sp2 weapons/bfg__f1y.wav weapons/bfg__l1a.wav weapons/bfg__x1b.wav weapons/bfg_hum.wav"
|
"sprites/s_bfg1.sp2 sprites/s_bfg2.sp2 sprites/s_bfg3.sp2 weapons/bfg__f1y.wav weapons/bfg__l1a.wav weapons/bfg__x1b.wav weapons/bfg_hum.wav"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED ammo_shells (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED ammo_shells (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"ammo_shells",
|
"ammo_shells",
|
||||||
Pickup_Ammo,
|
Pickup_Ammo,
|
||||||
|
@ -2069,7 +2288,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED ammo_bullets (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED ammo_bullets (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"ammo_bullets",
|
"ammo_bullets",
|
||||||
Pickup_Ammo,
|
Pickup_Ammo,
|
||||||
|
@ -2091,7 +2312,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED ammo_cells (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED ammo_cells (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"ammo_cells",
|
"ammo_cells",
|
||||||
Pickup_Ammo,
|
Pickup_Ammo,
|
||||||
|
@ -2113,7 +2336,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED ammo_rockets (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED ammo_rockets (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"ammo_rockets",
|
"ammo_rockets",
|
||||||
Pickup_Ammo,
|
Pickup_Ammo,
|
||||||
|
@ -2135,7 +2360,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED ammo_slugs (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED ammo_slugs (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"ammo_slugs",
|
"ammo_slugs",
|
||||||
Pickup_Ammo,
|
Pickup_Ammo,
|
||||||
|
@ -2157,7 +2384,33 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED item_quad (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED ammo_magslug (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
"ammo_magslug",
|
||||||
|
Pickup_Ammo,
|
||||||
|
NULL,
|
||||||
|
Drop_Ammo,
|
||||||
|
NULL,
|
||||||
|
"misc/am_pkup.wav",
|
||||||
|
"models/objects/ammo/tris.md2", 0,
|
||||||
|
NULL,
|
||||||
|
"a_mslugs",
|
||||||
|
"Mag Slug",
|
||||||
|
3,
|
||||||
|
10,
|
||||||
|
NULL,
|
||||||
|
IT_AMMO,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
AMMO_MAGSLUG,
|
||||||
|
""
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QUAKED item_quad (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"item_quad",
|
"item_quad",
|
||||||
Pickup_Powerup,
|
Pickup_Powerup,
|
||||||
|
@ -2179,7 +2432,34 @@ static const gitem_t gameitemlist[] = {
|
||||||
"items/damage.wav items/damage2.wav items/damage3.wav"
|
"items/damage.wav items/damage2.wav items/damage3.wav"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED item_invulnerability (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED item_quadfire (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
"item_quadfire",
|
||||||
|
Pickup_Powerup,
|
||||||
|
Use_QuadFire,
|
||||||
|
Drop_General,
|
||||||
|
NULL,
|
||||||
|
"items/pkup.wav",
|
||||||
|
"models/items/quadfire/tris.md2", EF_ROTATE,
|
||||||
|
NULL,
|
||||||
|
"p_quadfire",
|
||||||
|
|
||||||
|
"DualFire Damage",
|
||||||
|
2,
|
||||||
|
60,
|
||||||
|
NULL,
|
||||||
|
IT_POWERUP | IT_INSTANT_USE,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
"items/quadfire1.wav items/quadfire2.wav items/quadfire3.wav"
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QUAKED item_invulnerability (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"item_invulnerability",
|
"item_invulnerability",
|
||||||
Pickup_Powerup,
|
Pickup_Powerup,
|
||||||
|
@ -2201,7 +2481,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
"items/protect.wav items/protect2.wav items/protect4.wav"
|
"items/protect.wav items/protect2.wav items/protect4.wav"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED item_silencer (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED item_silencer (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"item_silencer",
|
"item_silencer",
|
||||||
Pickup_Powerup,
|
Pickup_Powerup,
|
||||||
|
@ -2223,7 +2505,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED item_breather (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED item_breather (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"item_breather",
|
"item_breather",
|
||||||
Pickup_Powerup,
|
Pickup_Powerup,
|
||||||
|
@ -2245,7 +2529,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
"items/airout.wav"
|
"items/airout.wav"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED item_enviro (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED item_enviro (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"item_enviro",
|
"item_enviro",
|
||||||
Pickup_Powerup,
|
Pickup_Powerup,
|
||||||
|
@ -2267,8 +2553,10 @@ static const gitem_t gameitemlist[] = {
|
||||||
"items/airout.wav"
|
"items/airout.wav"
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED item_ancient_head (.3 .3 1) (-16 -16 -16) (16 16 16)
|
/*
|
||||||
Special item that gives +2 to maximum health */
|
* QUAKED item_ancient_head (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
* Special item that gives +2 to maximum health
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"item_ancient_head",
|
"item_ancient_head",
|
||||||
Pickup_AncientHead,
|
Pickup_AncientHead,
|
||||||
|
@ -2290,8 +2578,10 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED item_adrenaline (.3 .3 1) (-16 -16 -16) (16 16 16)
|
/*
|
||||||
gives +1 to maximum health */
|
* QUAKED item_adrenaline (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
* gives +1 to maximum health
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"item_adrenaline",
|
"item_adrenaline",
|
||||||
Pickup_Adrenaline,
|
Pickup_Adrenaline,
|
||||||
|
@ -2313,7 +2603,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED item_bandolier (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED item_bandolier (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"item_bandolier",
|
"item_bandolier",
|
||||||
Pickup_Bandolier,
|
Pickup_Bandolier,
|
||||||
|
@ -2335,7 +2627,9 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED item_pack (.3 .3 1) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED item_pack (.3 .3 1) (-16 -16 -16) (16 16 16)
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"item_pack",
|
"item_pack",
|
||||||
Pickup_Pack,
|
Pickup_Pack,
|
||||||
|
@ -2357,8 +2651,10 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED key_data_cd (0 .5 .8) (-16 -16 -16) (16 16 16)
|
/*
|
||||||
key for computer centers */
|
* QUAKED key_data_cd (0 .5 .8) (-16 -16 -16) (16 16 16)
|
||||||
|
* key for computer centers
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"key_data_cd",
|
"key_data_cd",
|
||||||
Pickup_Key,
|
Pickup_Key,
|
||||||
|
@ -2380,8 +2676,10 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED key_power_cube (0 .5 .8) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN NO_TOUCH
|
/*
|
||||||
warehouse circuits */
|
* QUAKED key_power_cube (0 .5 .8) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN NO_TOUCH
|
||||||
|
* warehouse circuits
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"key_power_cube",
|
"key_power_cube",
|
||||||
Pickup_Key,
|
Pickup_Key,
|
||||||
|
@ -2403,8 +2701,10 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED key_pyramid (0 .5 .8) (-16 -16 -16) (16 16 16)
|
/*
|
||||||
key for the entrance of jail3 */
|
* QUAKED key_pyramid (0 .5 .8) (-16 -16 -16) (16 16 16)
|
||||||
|
* key for the entrance of jail3
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"key_pyramid",
|
"key_pyramid",
|
||||||
Pickup_Key,
|
Pickup_Key,
|
||||||
|
@ -2426,8 +2726,10 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED key_data_spinner (0 .5 .8) (-16 -16 -16) (16 16 16)
|
/*
|
||||||
key for the city computer */
|
* QUAKED key_data_spinner (0 .5 .8) (-16 -16 -16) (16 16 16)
|
||||||
|
* key for the city computer
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"key_data_spinner",
|
"key_data_spinner",
|
||||||
Pickup_Key,
|
Pickup_Key,
|
||||||
|
@ -2449,8 +2751,10 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED key_pass (0 .5 .8) (-16 -16 -16) (16 16 16)
|
/*
|
||||||
security pass for the security level */
|
* QUAKED key_pass (0 .5 .8) (-16 -16 -16) (16 16 16)
|
||||||
|
* security pass for the security level
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"key_pass",
|
"key_pass",
|
||||||
Pickup_Key,
|
Pickup_Key,
|
||||||
|
@ -2472,8 +2776,10 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED key_blue_key (0 .5 .8) (-16 -16 -16) (16 16 16)
|
/*
|
||||||
normal door key - blue */
|
* QUAKED key_blue_key (0 .5 .8) (-16 -16 -16) (16 16 16)
|
||||||
|
* normal door key - blue
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"key_blue_key",
|
"key_blue_key",
|
||||||
Pickup_Key,
|
Pickup_Key,
|
||||||
|
@ -2495,8 +2801,10 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED key_red_key (0 .5 .8) (-16 -16 -16) (16 16 16)
|
/*
|
||||||
normal door key - red */
|
* QUAKED key_red_key (0 .5 .8) (-16 -16 -16) (16 16 16)
|
||||||
|
* normal door key - red
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"key_red_key",
|
"key_red_key",
|
||||||
Pickup_Key,
|
Pickup_Key,
|
||||||
|
@ -2518,8 +2826,36 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED key_commander_head (0 .5 .8) (-16 -16 -16) (16 16 16)
|
|
||||||
tank commander's head */
|
/*
|
||||||
|
* QUAKED key_green_key (0 .5 .8) (-16 -16 -16) (16 16 16)
|
||||||
|
* normal door key - blue
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
"key_green_key",
|
||||||
|
Pickup_Key,
|
||||||
|
NULL,
|
||||||
|
Drop_General,
|
||||||
|
NULL,
|
||||||
|
"items/pkup.wav",
|
||||||
|
"models/items/keys/green_key/tris.md2", EF_ROTATE,
|
||||||
|
NULL,
|
||||||
|
"k_green",
|
||||||
|
"Green Key",
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
IT_STAY_COOP | IT_KEY,
|
||||||
|
0,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
""
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QUAKED key_commander_head (0 .5 .8) (-16 -16 -16) (16 16 16)
|
||||||
|
* tank commander's head
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"key_commander_head",
|
"key_commander_head",
|
||||||
Pickup_Key,
|
Pickup_Key,
|
||||||
|
@ -2541,7 +2877,10 @@ static const gitem_t gameitemlist[] = {
|
||||||
""
|
""
|
||||||
},
|
},
|
||||||
|
|
||||||
/* QUAKED key_airstrike_target (0 .5 .8) (-16 -16 -16) (16 16 16) */
|
/*
|
||||||
|
* QUAKED key_airstrike_target (0 .5 .8) (-16 -16 -16) (16 16 16)
|
||||||
|
* tank commander's head
|
||||||
|
*/
|
||||||
{
|
{
|
||||||
"key_airstrike_target",
|
"key_airstrike_target",
|
||||||
Pickup_Key,
|
Pickup_Key,
|
||||||
|
@ -2581,6 +2920,7 @@ static const gitem_t gameitemlist[] = {
|
||||||
0,
|
0,
|
||||||
NULL,
|
NULL,
|
||||||
0,
|
0,
|
||||||
|
|
||||||
"items/s_health.wav items/n_health.wav items/l_health.wav items/m_health.wav"
|
"items/s_health.wav items/n_health.wav items/l_health.wav items/m_health.wav"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -2614,7 +2954,7 @@ SP_item_health(edict_t *self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* QUAKED item_health_small (.3 .3 1) (-16 -16 -16) (16 16 16)
|
* QUAKED item_health_small (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
SP_item_health_small(edict_t *self)
|
SP_item_health_small(edict_t *self)
|
||||||
|
@ -2638,7 +2978,7 @@ SP_item_health_small(edict_t *self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* QUAKED item_health_large (.3 .3 1) (-16 -16 -16) (16 16 16)
|
* QUAKED item_health_large (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
SP_item_health_large(edict_t *self)
|
SP_item_health_large(edict_t *self)
|
||||||
|
@ -2661,7 +3001,7 @@ SP_item_health_large(edict_t *self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* QUAKED item_health_mega (.3 .3 1) (-16 -16 -16) (16 16 16)
|
* QUAKED item_health_mega (.3 .3 1) (-16 -16 -16) (16 16 16) TRIGGER_SPAWN
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
SP_item_health_mega(edict_t *self)
|
SP_item_health_mega(edict_t *self)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||||
|
* Copyright (c) ZeniMax Media Inc.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -24,31 +25,40 @@
|
||||||
* =======================================================================
|
* =======================================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "header/local.h"
|
#include "header/local.h"
|
||||||
|
|
||||||
#define STOP_EPSILON 0.1
|
#define STOP_EPSILON 0.1
|
||||||
#define MAX_CLIP_PLANES 5
|
#define MAX_CLIP_PLANES 5
|
||||||
#define STOPSPEED 100
|
|
||||||
#define FRICTION 6
|
#define FRICTION 6
|
||||||
#define WATERFRICTION 1
|
#define WATERFRICTION 1
|
||||||
|
|
||||||
|
void SV_Physics_NewToss(edict_t *ent);
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
edict_t *ent;
|
||||||
|
vec3_t origin;
|
||||||
|
vec3_t angles;
|
||||||
|
} pushed_t;
|
||||||
|
|
||||||
|
static pushed_t pushed[MAX_EDICTS], *pushed_p;
|
||||||
|
static edict_t *obstacle;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pushmove objects do not obey gravity, and do not interact
|
* pushmove objects do not obey gravity, and do not interact with each other or
|
||||||
* with each other or trigger fields, but block normal movement
|
* trigger fields, but block normal movement and push normal objects when they move.
|
||||||
* and push normal objects when they move.
|
|
||||||
*
|
*
|
||||||
* onground is set for toss objects when they come to a complete
|
* onground is set for toss objects when they come to a complete rest. it is set for
|
||||||
* rest. It is set for steping or walking objects.
|
* steping or walking objects
|
||||||
*
|
*
|
||||||
* doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
|
* - doors, plats, etc are SOLID_BSP, and MOVETYPE_PUSH
|
||||||
* bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
|
* - bonus items are SOLID_TRIGGER touch, and MOVETYPE_TOSS
|
||||||
* corpses are SOLID_NOT and MOVETYPE_TOSS
|
* - corpses are SOLID_NOT and MOVETYPE_TOSS
|
||||||
* crates are SOLID_BBOX and MOVETYPE_TOSS
|
* - crates are SOLID_BBOX and MOVETYPE_TOSS
|
||||||
* walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
|
* - walking monsters are SOLID_SLIDEBOX and MOVETYPE_STEP
|
||||||
* flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
|
* - flying/floating monsters are SOLID_SLIDEBOX and MOVETYPE_FLY
|
||||||
|
* - solid_edge items only clip against bsp models.
|
||||||
*
|
*
|
||||||
* solid_edge items only clip against bsp models.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
edict_t *
|
edict_t *
|
||||||
|
@ -167,8 +177,9 @@ SV_Impact(edict_t *e1, trace_t *trace)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Slide off of the impacting object
|
* Slide off of the impacting object
|
||||||
* returns the blocked flags (1 = floor,
|
* returns the blocked flags:
|
||||||
* 2 = step / wall)
|
* 1 = floor
|
||||||
|
* 2 = step / wall
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce)
|
ClipVelocity(vec3_t in, vec3_t normal, vec3_t out, float overbounce)
|
||||||
|
@ -378,7 +389,15 @@ SV_AddGravity(edict_t *ent)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ent->velocity[2] -= ent->gravity * sv_gravity->value * FRAMETIME;
|
if (ent->gravityVector[2] > 0)
|
||||||
|
{
|
||||||
|
VectorMA(ent->velocity, ent->gravity * sv_gravity->value * FRAMETIME,
|
||||||
|
ent->gravityVector, ent->velocity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ent->velocity[2] -= ent->gravity * sv_gravity->value * FRAMETIME;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -398,6 +417,11 @@ RealBoundingBox(edict_t *ent, vec3_t mins, vec3_t maxs)
|
||||||
vec3_t p[8];
|
vec3_t p[8];
|
||||||
int i, j, k, j2, k4;
|
int i, j, k, j2, k4;
|
||||||
|
|
||||||
|
if (!ent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (k = 0; k < 2; k++)
|
for (k = 0; k < 2; k++)
|
||||||
{
|
{
|
||||||
k4 = k * 4;
|
k4 = k * 4;
|
||||||
|
@ -481,7 +505,7 @@ RealBoundingBox(edict_t *ent, vec3_t mins, vec3_t maxs)
|
||||||
maxs[0] = p[i][0];
|
maxs[0] = p[i][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (maxs[1] < p[i][1])
|
if (maxs[1] < p[i][1])
|
||||||
{
|
{
|
||||||
maxs[1] = p[i][1];
|
maxs[1] = p[i][1];
|
||||||
}
|
}
|
||||||
|
@ -569,6 +593,8 @@ retry:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ent->gravity = 1.0;
|
||||||
|
|
||||||
if (ent->inuse)
|
if (ent->inuse)
|
||||||
{
|
{
|
||||||
G_TouchTriggers(ent);
|
G_TouchTriggers(ent);
|
||||||
|
@ -577,16 +603,6 @@ retry:
|
||||||
return trace;
|
return trace;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
edict_t *ent;
|
|
||||||
vec3_t origin;
|
|
||||||
vec3_t angles;
|
|
||||||
} pushed_t;
|
|
||||||
|
|
||||||
static pushed_t pushed[MAX_EDICTS], *pushed_p;
|
|
||||||
static edict_t *obstacle;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Objects need to be moved back on a failed push,
|
* Objects need to be moved back on a failed push,
|
||||||
* otherwise riders would continue to slide.
|
* otherwise riders would continue to slide.
|
||||||
|
@ -641,7 +657,7 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove)
|
||||||
|
|
||||||
/* Create a real bounding box for
|
/* Create a real bounding box for
|
||||||
rotating brush models. */
|
rotating brush models. */
|
||||||
RealBoundingBox(pusher,realmins,realmaxs);
|
RealBoundingBox(pusher, realmins, realmaxs);
|
||||||
|
|
||||||
/* see if any solid entities
|
/* see if any solid entities
|
||||||
are inside the final position */
|
are inside the final position */
|
||||||
|
@ -706,7 +722,23 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove)
|
||||||
VectorSubtract(check->s.origin, pusher->s.origin, org);
|
VectorSubtract(check->s.origin, pusher->s.origin, org);
|
||||||
org2[0] = DotProduct(org, forward);
|
org2[0] = DotProduct(org, forward);
|
||||||
org2[1] = -DotProduct(org, right);
|
org2[1] = -DotProduct(org, right);
|
||||||
org2[2] = DotProduct(org, up);
|
|
||||||
|
/* Quirk for blocking Elevators when
|
||||||
|
running under amd64. This is most
|
||||||
|
likey caused by a too high float
|
||||||
|
precision. -_- */
|
||||||
|
if (((pusher->s.number == 285) &&
|
||||||
|
(Q_strcasecmp(level.mapname, "xcompnd2") == 0)) ||
|
||||||
|
((pusher->s.number == 520) &&
|
||||||
|
(Q_strcasecmp(level.mapname, "xsewer2") == 0)))
|
||||||
|
{
|
||||||
|
org2[2] = DotProduct(org, up) + 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
org2[2] = DotProduct(org, up);
|
||||||
|
}
|
||||||
|
|
||||||
VectorSubtract(org2, org, move2);
|
VectorSubtract(org2, org, move2);
|
||||||
VectorAdd(check->s.origin, move2, check->s.origin);
|
VectorAdd(check->s.origin, move2, check->s.origin);
|
||||||
|
|
||||||
|
@ -719,9 +751,11 @@ SV_Push(edict_t *pusher, vec3_t move, vec3_t amove)
|
||||||
block = SV_TestEntityPosition(check);
|
block = SV_TestEntityPosition(check);
|
||||||
|
|
||||||
if (!block)
|
if (!block)
|
||||||
|
{
|
||||||
{ /* pushed ok */
|
/* pushed ok */
|
||||||
gi.linkentity(check);
|
gi.linkentity(check);
|
||||||
|
|
||||||
|
/* impact? */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -808,7 +842,7 @@ SV_Physics_Pusher(edict_t *ent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pushed_p > &pushed[MAX_EDICTS -1 ])
|
if (pushed_p > &pushed[MAX_EDICTS - 1])
|
||||||
{
|
{
|
||||||
gi.error("pushed_p > &pushed[MAX_EDICTS - 1], memory corrupted");
|
gi.error("pushed_p > &pushed[MAX_EDICTS - 1], memory corrupted");
|
||||||
}
|
}
|
||||||
|
@ -838,7 +872,11 @@ SV_Physics_Pusher(edict_t *ent)
|
||||||
/* the move succeeded, so call all think functions */
|
/* the move succeeded, so call all think functions */
|
||||||
for (part = ent; part; part = part->teamchain)
|
for (part = ent; part; part = part->teamchain)
|
||||||
{
|
{
|
||||||
SV_RunThink(part);
|
/* prevent entities that are on trains that have gone away from thinking! */
|
||||||
|
if (part->inuse)
|
||||||
|
{
|
||||||
|
SV_RunThink(part);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -938,7 +976,7 @@ SV_Physics_Toss(edict_t *ent)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if onground, return without moving */
|
/* if onground, return without moving */
|
||||||
if (ent->groundentity)
|
if (ent->groundentity && (ent->gravity > 0.0))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -949,7 +987,8 @@ SV_Physics_Toss(edict_t *ent)
|
||||||
|
|
||||||
/* add gravity */
|
/* add gravity */
|
||||||
if ((ent->movetype != MOVETYPE_FLY) &&
|
if ((ent->movetype != MOVETYPE_FLY) &&
|
||||||
(ent->movetype != MOVETYPE_FLYMISSILE))
|
(ent->movetype != MOVETYPE_FLYMISSILE)
|
||||||
|
&& (ent->movetype != MOVETYPE_WALLBOUNCE))
|
||||||
{
|
{
|
||||||
SV_AddGravity(ent);
|
SV_AddGravity(ent);
|
||||||
}
|
}
|
||||||
|
@ -968,7 +1007,11 @@ SV_Physics_Toss(edict_t *ent)
|
||||||
|
|
||||||
if (trace.fraction < 1)
|
if (trace.fraction < 1)
|
||||||
{
|
{
|
||||||
if (ent->movetype == MOVETYPE_BOUNCE)
|
if (ent->movetype == MOVETYPE_WALLBOUNCE)
|
||||||
|
{
|
||||||
|
backoff = 2.0;
|
||||||
|
}
|
||||||
|
else if (ent->movetype == MOVETYPE_BOUNCE)
|
||||||
{
|
{
|
||||||
backoff = 1.5;
|
backoff = 1.5;
|
||||||
}
|
}
|
||||||
|
@ -979,8 +1022,14 @@ SV_Physics_Toss(edict_t *ent)
|
||||||
|
|
||||||
ClipVelocity(ent->velocity, trace.plane.normal, ent->velocity, backoff);
|
ClipVelocity(ent->velocity, trace.plane.normal, ent->velocity, backoff);
|
||||||
|
|
||||||
|
if (ent->movetype == MOVETYPE_WALLBOUNCE)
|
||||||
|
{
|
||||||
|
vectoangles(ent->velocity, ent->s.angles);
|
||||||
|
}
|
||||||
|
|
||||||
/* stop if on ground */
|
/* stop if on ground */
|
||||||
if (trace.plane.normal[2] > 0.7)
|
if ((trace.plane.normal[2] > 0.7) &&
|
||||||
|
(ent->movetype != MOVETYPE_WALLBOUNCE))
|
||||||
{
|
{
|
||||||
if ((ent->velocity[2] < 60) || (ent->movetype != MOVETYPE_BOUNCE))
|
if ((ent->velocity[2] < 60) || (ent->movetype != MOVETYPE_BOUNCE))
|
||||||
{
|
{
|
||||||
|
@ -1054,7 +1103,7 @@ SV_AddRotationalFriction(edict_t *ent)
|
||||||
}
|
}
|
||||||
|
|
||||||
VectorMA(ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles);
|
VectorMA(ent->s.angles, FRAMETIME, ent->avelocity, ent->s.angles);
|
||||||
adjustment = FRAMETIME * STOPSPEED * FRICTION;
|
adjustment = FRAMETIME * sv_stopspeed->value * FRICTION;
|
||||||
|
|
||||||
for (n = 0; n < 3; n++)
|
for (n = 0; n < 3; n++)
|
||||||
{
|
{
|
||||||
|
@ -1122,8 +1171,8 @@ SV_Physics_Step(edict_t *ent)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add gravity except:
|
/* add gravity except:
|
||||||
flying monsters
|
- flying monsters
|
||||||
swimming monsters who are in the water */
|
- swimming monsters who are in the water */
|
||||||
if (!wasonground)
|
if (!wasonground)
|
||||||
{
|
{
|
||||||
if (!(ent->flags & FL_FLY))
|
if (!(ent->flags & FL_FLY))
|
||||||
|
@ -1147,7 +1196,7 @@ SV_Physics_Step(edict_t *ent)
|
||||||
if ((ent->flags & FL_FLY) && (ent->velocity[2] != 0))
|
if ((ent->flags & FL_FLY) && (ent->velocity[2] != 0))
|
||||||
{
|
{
|
||||||
speed = fabs(ent->velocity[2]);
|
speed = fabs(ent->velocity[2]);
|
||||||
control = speed < STOPSPEED ? STOPSPEED : speed;
|
control = speed < sv_stopspeed->value ? sv_stopspeed->value : speed;
|
||||||
friction = FRICTION / 3;
|
friction = FRICTION / 3;
|
||||||
newspeed = speed - (FRAMETIME * control * friction);
|
newspeed = speed - (FRAMETIME * control * friction);
|
||||||
|
|
||||||
|
@ -1164,7 +1213,7 @@ SV_Physics_Step(edict_t *ent)
|
||||||
if ((ent->flags & FL_SWIM) && (ent->velocity[2] != 0))
|
if ((ent->flags & FL_SWIM) && (ent->velocity[2] != 0))
|
||||||
{
|
{
|
||||||
speed = fabs(ent->velocity[2]);
|
speed = fabs(ent->velocity[2]);
|
||||||
control = speed < STOPSPEED ? STOPSPEED : speed;
|
control = speed < sv_stopspeed->value ? sv_stopspeed->value : speed;
|
||||||
newspeed = speed - (FRAMETIME * control * WATERFRICTION * ent->waterlevel);
|
newspeed = speed - (FRAMETIME * control * WATERFRICTION * ent->waterlevel);
|
||||||
|
|
||||||
if (newspeed < 0)
|
if (newspeed < 0)
|
||||||
|
@ -1191,7 +1240,7 @@ SV_Physics_Step(edict_t *ent)
|
||||||
{
|
{
|
||||||
friction = FRICTION;
|
friction = FRICTION;
|
||||||
|
|
||||||
control = speed < STOPSPEED ? STOPSPEED : speed;
|
control = speed < sv_stopspeed->value ? sv_stopspeed->value : speed;
|
||||||
newspeed = speed - FRAMETIME * control * friction;
|
newspeed = speed - FRAMETIME * control * friction;
|
||||||
|
|
||||||
if (newspeed < 0)
|
if (newspeed < 0)
|
||||||
|
@ -1235,6 +1284,7 @@ SV_Physics_Step(edict_t *ent)
|
||||||
}
|
}
|
||||||
|
|
||||||
gi.linkentity(ent);
|
gi.linkentity(ent);
|
||||||
|
ent->gravity = 1.0;
|
||||||
G_TouchTriggers(ent);
|
G_TouchTriggers(ent);
|
||||||
|
|
||||||
if (!ent->inuse)
|
if (!ent->inuse)
|
||||||
|
@ -1254,6 +1304,11 @@ SV_Physics_Step(edict_t *ent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!ent->inuse) /* g_touchtrigger free problem */
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* regular thinking */
|
/* regular thinking */
|
||||||
SV_RunThink(ent);
|
SV_RunThink(ent);
|
||||||
}
|
}
|
||||||
|
@ -1263,11 +1318,25 @@ SV_Physics_Step(edict_t *ent)
|
||||||
void
|
void
|
||||||
G_RunEntity(edict_t *ent)
|
G_RunEntity(edict_t *ent)
|
||||||
{
|
{
|
||||||
|
trace_t trace;
|
||||||
|
vec3_t previous_origin;
|
||||||
|
qboolean saved_origin;
|
||||||
|
|
||||||
if (!ent)
|
if (!ent)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ent->movetype == MOVETYPE_STEP)
|
||||||
|
{
|
||||||
|
VectorCopy(ent->s.origin, previous_origin);
|
||||||
|
saved_origin = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
saved_origin = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (ent->prethink)
|
if (ent->prethink)
|
||||||
{
|
{
|
||||||
ent->prethink(ent);
|
ent->prethink(ent);
|
||||||
|
@ -1292,9 +1361,166 @@ G_RunEntity(edict_t *ent)
|
||||||
case MOVETYPE_BOUNCE:
|
case MOVETYPE_BOUNCE:
|
||||||
case MOVETYPE_FLY:
|
case MOVETYPE_FLY:
|
||||||
case MOVETYPE_FLYMISSILE:
|
case MOVETYPE_FLYMISSILE:
|
||||||
|
case MOVETYPE_WALLBOUNCE:
|
||||||
SV_Physics_Toss(ent);
|
SV_Physics_Toss(ent);
|
||||||
break;
|
break;
|
||||||
|
case MOVETYPE_NEWTOSS:
|
||||||
|
SV_Physics_NewToss(ent);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
gi.error("SV_Physics: bad movetype %i", (int)ent->movetype);
|
gi.error("SV_Physics: bad movetype %i", (int)ent->movetype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* if we moved, check and fix origin if needed */
|
||||||
|
/* also check inuse since entities are very often freed while thinking */
|
||||||
|
if (saved_origin && ent->inuse && !VectorCompare(ent->s.origin, previous_origin))
|
||||||
|
{
|
||||||
|
trace = gi.trace(ent->s.origin, ent->mins, ent->maxs,
|
||||||
|
previous_origin, ent, MASK_MONSTERSOLID);
|
||||||
|
|
||||||
|
if (trace.allsolid || trace.startsolid)
|
||||||
|
{
|
||||||
|
VectorCopy(previous_origin, ent->s.origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Toss, bounce, and fly movement. When on ground and
|
||||||
|
* no velocity, do nothing. With velocity, slide.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
SV_Physics_NewToss(edict_t *ent)
|
||||||
|
{
|
||||||
|
trace_t trace;
|
||||||
|
vec3_t move;
|
||||||
|
edict_t *slave;
|
||||||
|
qboolean wasinwater;
|
||||||
|
qboolean isinwater;
|
||||||
|
float speed, newspeed;
|
||||||
|
vec3_t old_origin;
|
||||||
|
|
||||||
|
if (!ent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* regular thinking */
|
||||||
|
SV_RunThink(ent);
|
||||||
|
|
||||||
|
/* if not a team captain, so movement will be handled elsewhere */
|
||||||
|
if (ent->flags & FL_TEAMSLAVE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find out what we're sitting on. */
|
||||||
|
VectorCopy(ent->s.origin, move);
|
||||||
|
move[2] -= 0.25;
|
||||||
|
trace = gi.trace(ent->s.origin, ent->mins, ent->maxs,
|
||||||
|
move, ent, ent->clipmask);
|
||||||
|
|
||||||
|
if (ent->groundentity && ent->groundentity->inuse)
|
||||||
|
{
|
||||||
|
ent->groundentity = trace.ent;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ent->groundentity = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we're sitting on something flat and have no velocity of our own, return. */
|
||||||
|
if (ent->groundentity && (trace.plane.normal[2] == 1.0) &&
|
||||||
|
!ent->velocity[0] && !ent->velocity[1] && !ent->velocity[2])
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store the old origin */
|
||||||
|
VectorCopy(ent->s.origin, old_origin);
|
||||||
|
|
||||||
|
SV_CheckVelocity(ent);
|
||||||
|
|
||||||
|
/* add gravity */
|
||||||
|
SV_AddGravity(ent);
|
||||||
|
|
||||||
|
if (ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2])
|
||||||
|
{
|
||||||
|
SV_AddRotationalFriction(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add friction */
|
||||||
|
speed = VectorLength(ent->velocity);
|
||||||
|
|
||||||
|
if (ent->waterlevel) /* friction for water movement */
|
||||||
|
{
|
||||||
|
newspeed = speed - (WATERFRICTION * 6 * ent->waterlevel);
|
||||||
|
|
||||||
|
if (newspeed < 0)
|
||||||
|
{
|
||||||
|
newspeed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
newspeed /= speed;
|
||||||
|
VectorScale(ent->velocity, newspeed, ent->velocity);
|
||||||
|
}
|
||||||
|
else if (!ent->groundentity) /* friction for air movement */
|
||||||
|
{
|
||||||
|
newspeed = speed - ((FRICTION));
|
||||||
|
|
||||||
|
if (newspeed < 0)
|
||||||
|
{
|
||||||
|
newspeed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
newspeed /= speed;
|
||||||
|
VectorScale(ent->velocity, newspeed, ent->velocity);
|
||||||
|
}
|
||||||
|
else /* use ground friction */
|
||||||
|
{
|
||||||
|
newspeed = speed - (FRICTION * 6);
|
||||||
|
|
||||||
|
if (newspeed < 0)
|
||||||
|
{
|
||||||
|
newspeed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
newspeed /= speed;
|
||||||
|
VectorScale(ent->velocity, newspeed, ent->velocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
SV_FlyMove(ent, FRAMETIME, ent->clipmask);
|
||||||
|
gi.linkentity(ent);
|
||||||
|
|
||||||
|
G_TouchTriggers(ent);
|
||||||
|
|
||||||
|
/* check for water transition */
|
||||||
|
wasinwater = (ent->watertype & MASK_WATER);
|
||||||
|
ent->watertype = gi.pointcontents(ent->s.origin);
|
||||||
|
isinwater = ent->watertype & MASK_WATER;
|
||||||
|
|
||||||
|
if (isinwater)
|
||||||
|
{
|
||||||
|
ent->waterlevel = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ent->waterlevel = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wasinwater && isinwater)
|
||||||
|
{
|
||||||
|
gi.positioned_sound(old_origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
|
||||||
|
}
|
||||||
|
else if (wasinwater && !isinwater)
|
||||||
|
{
|
||||||
|
gi.positioned_sound(ent->s.origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* move teamslaves */
|
||||||
|
for (slave = ent->teamchain; slave; slave = slave->teamchain)
|
||||||
|
{
|
||||||
|
VectorCopy(ent->s.origin, slave->s.origin);
|
||||||
|
gi.linkentity(slave);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,6 +155,23 @@ void SP_turret_breach(edict_t *self);
|
||||||
void SP_turret_base(edict_t *self);
|
void SP_turret_base(edict_t *self);
|
||||||
void SP_turret_driver(edict_t *self);
|
void SP_turret_driver(edict_t *self);
|
||||||
|
|
||||||
|
void SP_monster_soldier_hypergun(edict_t *self);
|
||||||
|
void SP_monster_soldier_lasergun(edict_t *self);
|
||||||
|
void SP_monster_soldier_ripper(edict_t *self);
|
||||||
|
void SP_monster_fixbot(edict_t *self);
|
||||||
|
void SP_monster_gekk(edict_t *self);
|
||||||
|
void SP_monster_chick_heat(edict_t *self);
|
||||||
|
void SP_monster_gladb(edict_t *self);
|
||||||
|
void SP_monster_boss5(edict_t *self);
|
||||||
|
void SP_rotating_light(edict_t *self);
|
||||||
|
void SP_object_repair(edict_t *self);
|
||||||
|
void SP_misc_crashviper(edict_t *ent);
|
||||||
|
void SP_misc_viper_missile(edict_t *self);
|
||||||
|
void SP_misc_amb4(edict_t *ent);
|
||||||
|
void SP_target_mal_laser(edict_t *ent);
|
||||||
|
void SP_misc_transport(edict_t *ent);
|
||||||
|
|
||||||
|
void SP_misc_nuke(edict_t *ent);
|
||||||
void SP_func_plat2(edict_t *ent);
|
void SP_func_plat2(edict_t *ent);
|
||||||
void SP_func_door_secret2(edict_t *ent);
|
void SP_func_door_secret2(edict_t *ent);
|
||||||
void SP_func_force_wall(edict_t *ent);
|
void SP_func_force_wall(edict_t *ent);
|
||||||
|
@ -222,6 +239,9 @@ static spawn_t spawns[] = {
|
||||||
{"func_explosive", SP_func_explosive},
|
{"func_explosive", SP_func_explosive},
|
||||||
{"func_killbox", SP_func_killbox},
|
{"func_killbox", SP_func_killbox},
|
||||||
|
|
||||||
|
{"func_object_repair", SP_object_repair},
|
||||||
|
{"rotating_light", SP_rotating_light},
|
||||||
|
|
||||||
{"trigger_always", SP_trigger_always},
|
{"trigger_always", SP_trigger_always},
|
||||||
{"trigger_once", SP_trigger_once},
|
{"trigger_once", SP_trigger_once},
|
||||||
{"trigger_multiple", SP_trigger_multiple},
|
{"trigger_multiple", SP_trigger_multiple},
|
||||||
|
@ -251,6 +271,7 @@ static spawn_t spawns[] = {
|
||||||
{"target_earthquake", SP_target_earthquake},
|
{"target_earthquake", SP_target_earthquake},
|
||||||
{"target_character", SP_target_character},
|
{"target_character", SP_target_character},
|
||||||
{"target_string", SP_target_string},
|
{"target_string", SP_target_string},
|
||||||
|
{"target_mal_laser", SP_target_mal_laser},
|
||||||
|
|
||||||
{"worldspawn", SP_worldspawn},
|
{"worldspawn", SP_worldspawn},
|
||||||
{"viewthing", SP_viewthing},
|
{"viewthing", SP_viewthing},
|
||||||
|
@ -282,6 +303,11 @@ static spawn_t spawns[] = {
|
||||||
{"misc_eastertank", SP_misc_eastertank},
|
{"misc_eastertank", SP_misc_eastertank},
|
||||||
{"misc_easterchick", SP_misc_easterchick},
|
{"misc_easterchick", SP_misc_easterchick},
|
||||||
{"misc_easterchick2", SP_misc_easterchick2},
|
{"misc_easterchick2", SP_misc_easterchick2},
|
||||||
|
{"misc_crashviper", SP_misc_crashviper},
|
||||||
|
{"misc_viper_missile", SP_misc_viper_missile},
|
||||||
|
{"misc_amb4", SP_misc_amb4},
|
||||||
|
{"misc_transport", SP_misc_transport},
|
||||||
|
{"misc_nuke", SP_misc_nuke},
|
||||||
|
|
||||||
{"monster_berserk", SP_monster_berserk},
|
{"monster_berserk", SP_monster_berserk},
|
||||||
{"monster_gladiator", SP_monster_gladiator},
|
{"monster_gladiator", SP_monster_gladiator},
|
||||||
|
@ -306,8 +332,15 @@ static spawn_t spawns[] = {
|
||||||
{"monster_boss3_stand", SP_monster_boss3_stand},
|
{"monster_boss3_stand", SP_monster_boss3_stand},
|
||||||
{"monster_makron", SP_monster_makron},
|
{"monster_makron", SP_monster_makron},
|
||||||
{"monster_jorg", SP_monster_jorg},
|
{"monster_jorg", SP_monster_jorg},
|
||||||
|
|
||||||
{"monster_commander_body", SP_monster_commander_body},
|
{"monster_commander_body", SP_monster_commander_body},
|
||||||
|
{"monster_soldier_hypergun", SP_monster_soldier_hypergun},
|
||||||
|
{"monster_soldier_lasergun", SP_monster_soldier_lasergun},
|
||||||
|
{"monster_soldier_ripper", SP_monster_soldier_ripper},
|
||||||
|
{"monster_fixbot", SP_monster_fixbot},
|
||||||
|
{"monster_gekk", SP_monster_gekk},
|
||||||
|
{"monster_chick_heat", SP_monster_chick_heat},
|
||||||
|
{"monster_gladb", SP_monster_gladb},
|
||||||
|
{"monster_boss5", SP_monster_boss5},
|
||||||
|
|
||||||
{"turret_breach", SP_turret_breach},
|
{"turret_breach", SP_turret_breach},
|
||||||
{"turret_base", SP_turret_base},
|
{"turret_base", SP_turret_base},
|
||||||
|
@ -316,6 +349,35 @@ static spawn_t spawns[] = {
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
qboolean Spawn_CheckCoop_MapHacks (edict_t *ent)
|
||||||
|
{
|
||||||
|
if(!coop->value || !ent)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!Q_stricmp(level.mapname, "xsewer1"))
|
||||||
|
{
|
||||||
|
if(ent->classname && !Q_stricmp(ent->classname, "trigger_relay") && ent->target && !Q_stricmp(ent->target, "t3") && ent->targetname && !Q_stricmp(ent->targetname, "t2"))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(ent->classname && !Q_stricmp(ent->classname, "func_button") && ent->target && !Q_stricmp(ent->target, "t16") && ent->model && !Q_stricmp(ent->model, "*71"))
|
||||||
|
{
|
||||||
|
ent->message = "Overflow valve maintenance\nhatch A opened.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ent->classname && !Q_stricmp(ent->classname, "trigger_once") && ent->model && !Q_stricmp(ent->model, "*3"))
|
||||||
|
{
|
||||||
|
ent->message = "Overflow valve maintenance\nhatch B opened.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Finds the spawn function for
|
* Finds the spawn function for
|
||||||
* the entity and calls it
|
* the entity and calls it
|
||||||
|
@ -479,7 +541,7 @@ ED_ParseField(const char *key, const char *value, edict_t *ent)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parses an edict out of the given string,
|
* Parses an edict out of the given string,
|
||||||
* returning the new position ed should be
|
* returning the new position. ed should be
|
||||||
* a properly initialized empty edict.
|
* a properly initialized empty edict.
|
||||||
*/
|
*/
|
||||||
char *
|
char *
|
||||||
|
@ -818,13 +880,14 @@ SpawnEntities(const char *mapname, char *entities, const char *spawnpoint)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (((skill->value == SKILL_EASY) &&
|
if (Spawn_CheckCoop_MapHacks(ent) || (
|
||||||
|
((skill->value == SKILL_EASY) &&
|
||||||
(ent->spawnflags & SPAWNFLAG_NOT_EASY)) ||
|
(ent->spawnflags & SPAWNFLAG_NOT_EASY)) ||
|
||||||
((skill->value == SKILL_MEDIUM) &&
|
((skill->value == SKILL_MEDIUM) &&
|
||||||
(ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) ||
|
(ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) ||
|
||||||
(((skill->value == SKILL_HARD) ||
|
(((skill->value == SKILL_HARD) ||
|
||||||
(skill->value == SKILL_HARDPLUS)) &&
|
(skill->value == SKILL_HARDPLUS)) &&
|
||||||
(ent->spawnflags & SPAWNFLAG_NOT_HARD))
|
(ent->spawnflags & SPAWNFLAG_NOT_HARD)))
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
G_FreeEdict(ent);
|
G_FreeEdict(ent);
|
||||||
|
@ -1137,6 +1200,9 @@ SP_worldspawn(edict_t *ent)
|
||||||
gi.modelindex("#w_plasma.md2");
|
gi.modelindex("#w_plasma.md2");
|
||||||
gi.modelindex("#w_plauncher.md2");
|
gi.modelindex("#w_plauncher.md2");
|
||||||
gi.modelindex("#w_chainfist.md2");
|
gi.modelindex("#w_chainfist.md2");
|
||||||
|
|
||||||
|
gi.modelindex("#w_phalanx.md2");
|
||||||
|
gi.modelindex("#w_ripper.md2");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------- */
|
/* ------------------- */
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||||
|
* Copyright (c) ZeniMax Media Inc.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -130,6 +131,7 @@ StringToFilter(char *s, ipfilter_t *f)
|
||||||
s++;
|
s++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* PVS NOTE: maybe use memcpy here instead? */
|
||||||
f->mask = *(unsigned *)m;
|
f->mask = *(unsigned *)m;
|
||||||
f->compare = *(unsigned *)b;
|
f->compare = *(unsigned *)b;
|
||||||
|
|
||||||
|
@ -170,6 +172,7 @@ SV_FilterPacket(char *from)
|
||||||
i++, p++;
|
i++, p++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* PVS NOTE: maybe use memcpy instead? */
|
||||||
in = *(unsigned *)m;
|
in = *(unsigned *)m;
|
||||||
|
|
||||||
for (i = 0; i < numipfilters; i++)
|
for (i = 0; i < numipfilters; i++)
|
||||||
|
@ -265,6 +268,7 @@ SVCmd_ListIP_f(void)
|
||||||
|
|
||||||
for (i = 0; i < numipfilters; i++)
|
for (i = 0; i < numipfilters; i++)
|
||||||
{
|
{
|
||||||
|
/* PVS NOTE: maybe use memcpy instead? */
|
||||||
*(unsigned *)b = ipfilters[i].compare;
|
*(unsigned *)b = ipfilters[i].compare;
|
||||||
gi.cprintf(NULL, PRINT_HIGH, "%3i.%3i.%3i.%3i\n", b[0],
|
gi.cprintf(NULL, PRINT_HIGH, "%3i.%3i.%3i.%3i\n", b[0],
|
||||||
b[1], b[2], b[3]);
|
b[1], b[2], b[3]);
|
||||||
|
@ -305,6 +309,7 @@ SVCmd_WriteIP_f(void)
|
||||||
|
|
||||||
for (i = 0; i < numipfilters; i++)
|
for (i = 0; i < numipfilters; i++)
|
||||||
{
|
{
|
||||||
|
/* PVS NOTE: maybe use memcpy instead? */
|
||||||
*(unsigned *)b = ipfilters[i].compare;
|
*(unsigned *)b = ipfilters[i].compare;
|
||||||
fprintf(f, "sv addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]);
|
fprintf(f, "sv addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||||
|
* Copyright (c) ZeniMax Media Inc.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -415,7 +416,7 @@ SP_target_explosion(edict_t *ent)
|
||||||
void
|
void
|
||||||
use_target_changelevel(edict_t *self, edict_t *other, edict_t *activator)
|
use_target_changelevel(edict_t *self, edict_t *other, edict_t *activator)
|
||||||
{
|
{
|
||||||
if (!self || !other)
|
if (!self || !other || !activator)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -479,7 +480,7 @@ SP_target_changelevel(edict_t *ent)
|
||||||
|
|
||||||
/* Mapquirk for secret exists in fact1 and fact3 */
|
/* Mapquirk for secret exists in fact1 and fact3 */
|
||||||
if ((Q_stricmp(level.mapname, "fact1") == 0) &&
|
if ((Q_stricmp(level.mapname, "fact1") == 0) &&
|
||||||
(Q_stricmp(ent->map, "fact3") == 0))
|
(Q_stricmp(ent->map, "fact3") == 0))
|
||||||
{
|
{
|
||||||
ent->map = "fact3$secret1";
|
ent->map = "fact3$secret1";
|
||||||
}
|
}
|
||||||
|
@ -1002,6 +1003,155 @@ SP_target_laser(edict_t *self)
|
||||||
self->nextthink = level.time + 1;
|
self->nextthink = level.time + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!self->activator)
|
||||||
|
{
|
||||||
|
self->activator = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->spawnflags |= 0x80000001;
|
||||||
|
self->svflags &= ~SVF_NOCLIENT;
|
||||||
|
self->nextthink = level.time + self->wait + self->delay;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
target_mal_laser_off(edict_t *self)
|
||||||
|
{
|
||||||
|
if (!self)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self->spawnflags &= ~1;
|
||||||
|
self->svflags |= SVF_NOCLIENT;
|
||||||
|
self->nextthink = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
target_mal_laser_use(edict_t *self, edict_t *other /* unused */, edict_t *activator)
|
||||||
|
{
|
||||||
|
if (!self || !activator)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (!self)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
target_laser_think(self);
|
||||||
|
self->nextthink = level.time + self->wait + 0.1;
|
||||||
|
self->spawnflags |= 0x80000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SP_target_mal_laser(edict_t *self)
|
||||||
|
{
|
||||||
|
if (!self)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* ========================================================== */
|
/* ========================================================== */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1154,8 +1304,13 @@ target_earthquake_think(edict_t *self)
|
||||||
|
|
||||||
if (self->last_move_time < level.time)
|
if (self->last_move_time < level.time)
|
||||||
{
|
{
|
||||||
gi.positioned_sound(self->s.origin, self, CHAN_AUTO,
|
gi.positioned_sound(self->s.origin,
|
||||||
self->noise_index, 1.0, ATTN_NONE, 0);
|
self,
|
||||||
|
CHAN_AUTO,
|
||||||
|
self->noise_index,
|
||||||
|
1.0,
|
||||||
|
ATTN_NONE,
|
||||||
|
0);
|
||||||
self->last_move_time = level.time + 0.5;
|
self->last_move_time = level.time + 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||||
|
* Copyright (c) ZeniMax Media Inc.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -28,6 +29,8 @@
|
||||||
|
|
||||||
#define PUSH_ONCE 1
|
#define PUSH_ONCE 1
|
||||||
|
|
||||||
|
void trigger_push_active(edict_t *self);
|
||||||
|
|
||||||
static int windsound;
|
static int windsound;
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -184,7 +187,6 @@ trigger_enable(edict_t *self, edict_t *other /* unused */,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
self->solid = SOLID_TRIGGER;
|
self->solid = SOLID_TRIGGER;
|
||||||
self->use = Use_Multi;
|
self->use = Use_Multi;
|
||||||
gi.linkentity(self);
|
gi.linkentity(self);
|
||||||
|
@ -208,7 +210,7 @@ SP_trigger_multiple(edict_t *ent)
|
||||||
}
|
}
|
||||||
else if (ent->sounds == 3)
|
else if (ent->sounds == 3)
|
||||||
{
|
{
|
||||||
ent->noise_index = gi.soundindex ("misc/trigger1.wav");
|
ent->noise_index = gi.soundindex("misc/trigger1.wav");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ent->wait)
|
if (!ent->wait)
|
||||||
|
@ -591,6 +593,77 @@ trigger_push_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
||||||
*
|
*
|
||||||
* "speed" defaults to 1000
|
* "speed" defaults to 1000
|
||||||
*/
|
*/
|
||||||
|
void
|
||||||
|
trigger_effect(edict_t *self)
|
||||||
|
{
|
||||||
|
vec3_t origin;
|
||||||
|
vec3_t size;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!self)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
void
|
||||||
SP_trigger_push(edict_t *self)
|
SP_trigger_push(edict_t *self)
|
||||||
{
|
{
|
||||||
|
@ -603,6 +676,18 @@ SP_trigger_push(edict_t *self)
|
||||||
windsound = gi.soundindex("misc/windfly.wav");
|
windsound = gi.soundindex("misc/windfly.wav");
|
||||||
self->touch = trigger_push_touch;
|
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)
|
if (!self->speed)
|
||||||
{
|
{
|
||||||
self->speed = 1000;
|
self->speed = 1000;
|
||||||
|
@ -623,9 +708,10 @@ SP_trigger_push(edict_t *self)
|
||||||
*
|
*
|
||||||
* "dmg" default 5 (whole numbers only)
|
* "dmg" default 5 (whole numbers only)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void
|
void
|
||||||
hurt_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
hurt_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
||||||
csurface_t *surf /* unused */)
|
csurface_t *surf /* unused */)
|
||||||
{
|
{
|
||||||
int dflags;
|
int dflags;
|
||||||
|
|
||||||
|
@ -634,7 +720,6 @@ hurt_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!other->takedamage)
|
if (!other->takedamage)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
@ -755,7 +840,6 @@ SP_trigger_hurt(edict_t *self)
|
||||||
* the value of "gravity". 1.0 is standard
|
* the value of "gravity". 1.0 is standard
|
||||||
* gravity for the level.
|
* gravity for the level.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void
|
void
|
||||||
trigger_gravity_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
trigger_gravity_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
||||||
csurface_t *surf /* unused */)
|
csurface_t *surf /* unused */)
|
||||||
|
@ -796,7 +880,6 @@ SP_trigger_gravity(edict_t *self)
|
||||||
* "speed" default to 200, the speed thrown forward
|
* "speed" default to 200, the speed thrown forward
|
||||||
* "height" default to 200, the speed thrown upwards
|
* "height" default to 200, the speed thrown upwards
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void
|
void
|
||||||
trigger_monsterjump_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
trigger_monsterjump_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
||||||
csurface_t *surf /* unused */)
|
csurface_t *surf /* unused */)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
* Copyright (C) 1997-2001 Id Software, Inc.
|
||||||
|
* Copyright (c) ZeniMax Media Inc.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -260,7 +261,7 @@ turret_breach_think(edict_t *self)
|
||||||
ent->avelocity[1] = self->avelocity[1];
|
ent->avelocity[1] = self->avelocity[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if we have adriver, adjust his velocities */
|
/* if we have a driver, adjust his velocities */
|
||||||
if (self->owner)
|
if (self->owner)
|
||||||
{
|
{
|
||||||
float angle;
|
float angle;
|
||||||
|
@ -387,7 +388,6 @@ SP_turret_breach(edict_t *self)
|
||||||
* This portion of the turret changes yaw only.
|
* This portion of the turret changes yaw only.
|
||||||
* MUST be teamed with a turret_breach.
|
* MUST be teamed with a turret_breach.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void
|
void
|
||||||
SP_turret_base(edict_t *self)
|
SP_turret_base(edict_t *self)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1526,7 +1526,16 @@ plasma_touch(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf)
|
||||||
ent->dmg_radius, MOD_PHALANX);
|
ent->dmg_radius, MOD_PHALANX);
|
||||||
|
|
||||||
gi.WriteByte(svc_temp_entity);
|
gi.WriteByte(svc_temp_entity);
|
||||||
gi.WriteByte(TE_PLASMA_EXPLOSION);
|
|
||||||
|
if (ent->waterlevel)
|
||||||
|
{
|
||||||
|
gi.WriteByte(TE_ROCKET_EXPLOSION_WATER);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gi.WriteByte(TE_PLASMA_EXPLOSION);
|
||||||
|
}
|
||||||
|
|
||||||
gi.WritePosition(origin);
|
gi.WritePosition(origin);
|
||||||
gi.multicast(ent->s.origin, MULTICAST_PVS);
|
gi.multicast(ent->s.origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@
|
||||||
#include "../header/local.h"
|
#include "../header/local.h"
|
||||||
#include "../monster/misc/player.h"
|
#include "../monster/misc/player.h"
|
||||||
|
|
||||||
|
edict_t *pm_passent;
|
||||||
|
|
||||||
void ClientUserinfoChanged(edict_t *ent, char *userinfo);
|
void ClientUserinfoChanged(edict_t *ent, char *userinfo);
|
||||||
void SP_misc_teleporter_dest(edict_t *ent);
|
void SP_misc_teleporter_dest(edict_t *ent);
|
||||||
void Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf);
|
void Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf);
|
||||||
|
@ -295,6 +297,7 @@ SP_CreateUnnamedSpawn(edict_t *self)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32)
|
* QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32)
|
||||||
|
*
|
||||||
* The normal starting point for a level.
|
* The normal starting point for a level.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
|
@ -305,7 +308,7 @@ SP_info_player_start(edict_t *self)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call function to hack unnamed spawn points */
|
/* Call function to hack unnamed spawn points */
|
||||||
self->think = SP_CreateUnnamedSpawn;
|
self->think = SP_CreateUnnamedSpawn;
|
||||||
self->nextthink = level.time + FRAMETIME;
|
self->nextthink = level.time + FRAMETIME;
|
||||||
|
|
||||||
|
@ -327,6 +330,7 @@ SP_info_player_start(edict_t *self)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32)
|
* QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32)
|
||||||
|
*
|
||||||
* potential spawning position for deathmatch games
|
* potential spawning position for deathmatch games
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
|
@ -389,8 +393,30 @@ SP_info_player_coop(edict_t *self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QUAKED info_player_coop_lava (1 0 1) (-16 -16 -24) (16 16 32)
|
||||||
|
*
|
||||||
|
* potential spawning position for coop games on rmine2 where lava level
|
||||||
|
* needs to be checked
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
SP_info_player_coop_lava(edict_t *self)
|
||||||
|
{
|
||||||
|
if (!self)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!coop->value)
|
||||||
|
{
|
||||||
|
G_FreeEdict(self);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32)
|
* QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32)
|
||||||
|
*
|
||||||
* The deathmatch intermission point will be at one of these
|
* The deathmatch intermission point will be at one of these
|
||||||
* Use 'angles' instead of 'angle', so you can set pitch or
|
* Use 'angles' instead of 'angle', so you can set pitch or
|
||||||
* roll as well as yaw. 'pitch yaw roll'
|
* roll as well as yaw. 'pitch yaw roll'
|
||||||
|
@ -487,7 +513,7 @@ ClientObituary(edict_t *self, edict_t *inflictor /* unused */,
|
||||||
char *message2;
|
char *message2;
|
||||||
qboolean ff;
|
qboolean ff;
|
||||||
|
|
||||||
if (!self || !inflictor)
|
if (!self || !attacker || !inflictor)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -546,6 +572,8 @@ ClientObituary(edict_t *self, edict_t *inflictor /* unused */,
|
||||||
case MOD_BRAINTENTACLE:
|
case MOD_BRAINTENTACLE:
|
||||||
message = "that's gotta hurt";
|
message = "that's gotta hurt";
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attacker == self)
|
if (attacker == self)
|
||||||
|
@ -591,6 +619,21 @@ ClientObituary(edict_t *self, edict_t *inflictor /* unused */,
|
||||||
case MOD_BFG_BLAST:
|
case MOD_BFG_BLAST:
|
||||||
message = "should have used a smaller gun";
|
message = "should have used a smaller gun";
|
||||||
break;
|
break;
|
||||||
|
case MOD_DOPPLE_EXPLODE:
|
||||||
|
|
||||||
|
if (IsNeutral(self))
|
||||||
|
{
|
||||||
|
message = "got caught in it's own trap";
|
||||||
|
}
|
||||||
|
else if (IsFemale(self))
|
||||||
|
{
|
||||||
|
message = "got caught in her own trap";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
message = "got caught in his own trap";
|
||||||
|
}
|
||||||
|
|
||||||
case MOD_TRAP:
|
case MOD_TRAP:
|
||||||
message = "sucked into his own trap";
|
message = "sucked into his own trap";
|
||||||
break;
|
break;
|
||||||
|
@ -712,6 +755,63 @@ ClientObituary(edict_t *self, edict_t *inflictor /* unused */,
|
||||||
case MOD_TRAP:
|
case MOD_TRAP:
|
||||||
message = "caught in trap by";
|
message = "caught in trap by";
|
||||||
break;
|
break;
|
||||||
|
case MOD_CHAINFIST:
|
||||||
|
message = "was shredded by";
|
||||||
|
message2 = "'s ripsaw";
|
||||||
|
break;
|
||||||
|
case MOD_DISINTEGRATOR:
|
||||||
|
message = "lost his grip courtesy of";
|
||||||
|
message2 = "'s disintegrator";
|
||||||
|
break;
|
||||||
|
case MOD_ETF_RIFLE:
|
||||||
|
message = "was perforated by";
|
||||||
|
break;
|
||||||
|
case MOD_HEATBEAM:
|
||||||
|
message = "was scorched by";
|
||||||
|
message2 = "'s plasma beam";
|
||||||
|
break;
|
||||||
|
case MOD_TESLA:
|
||||||
|
message = "was enlightened by";
|
||||||
|
message2 = "'s tesla mine";
|
||||||
|
break;
|
||||||
|
case MOD_PROX:
|
||||||
|
message = "got too close to";
|
||||||
|
message2 = "'s proximity mine";
|
||||||
|
break;
|
||||||
|
case MOD_NUKE:
|
||||||
|
message = "was nuked by";
|
||||||
|
message2 = "'s antimatter bomb";
|
||||||
|
break;
|
||||||
|
case MOD_VENGEANCE_SPHERE:
|
||||||
|
message = "was purged by";
|
||||||
|
message2 = "'s vengeance sphere";
|
||||||
|
break;
|
||||||
|
case MOD_DEFENDER_SPHERE:
|
||||||
|
message = "had a blast with";
|
||||||
|
message2 = "'s defender sphere";
|
||||||
|
break;
|
||||||
|
case MOD_HUNTER_SPHERE:
|
||||||
|
message = "was killed like a dog by";
|
||||||
|
message2 = "'s hunter sphere";
|
||||||
|
break;
|
||||||
|
case MOD_TRACKER:
|
||||||
|
message = "was annihilated by";
|
||||||
|
message2 = "'s disruptor";
|
||||||
|
break;
|
||||||
|
case MOD_DOPPLE_EXPLODE:
|
||||||
|
message = "was blown up by";
|
||||||
|
message2 = "'s doppleganger";
|
||||||
|
break;
|
||||||
|
case MOD_DOPPLE_VENGEANCE:
|
||||||
|
message = "was purged by";
|
||||||
|
message2 = "'s doppleganger";
|
||||||
|
break;
|
||||||
|
case MOD_DOPPLE_HUNTER:
|
||||||
|
message = "was hunted down by";
|
||||||
|
message2 = "'s doppleganger";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message)
|
if (message)
|
||||||
|
@ -721,6 +821,23 @@ ClientObituary(edict_t *self, edict_t *inflictor /* unused */,
|
||||||
message, attacker->client->pers.netname,
|
message, attacker->client->pers.netname,
|
||||||
message2);
|
message2);
|
||||||
|
|
||||||
|
if (gamerules && gamerules->value)
|
||||||
|
{
|
||||||
|
if (DMGame.Score)
|
||||||
|
{
|
||||||
|
if (ff)
|
||||||
|
{
|
||||||
|
DMGame.Score(attacker, self, -1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DMGame.Score(attacker, self, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (deathmatch->value)
|
if (deathmatch->value)
|
||||||
{
|
{
|
||||||
if (ff)
|
if (ff)
|
||||||
|
@ -742,7 +859,19 @@ ClientObituary(edict_t *self, edict_t *inflictor /* unused */,
|
||||||
|
|
||||||
if (deathmatch->value)
|
if (deathmatch->value)
|
||||||
{
|
{
|
||||||
self->client->resp.score--;
|
if (gamerules && gamerules->value)
|
||||||
|
{
|
||||||
|
if (DMGame.Score)
|
||||||
|
{
|
||||||
|
DMGame.Score(self, self, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self->client->resp.score--;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -870,7 +999,15 @@ LookAtKiller(edict_t *self, edict_t *inflictor, edict_t *attacker)
|
||||||
|
|
||||||
if (dir[0])
|
if (dir[0])
|
||||||
{
|
{
|
||||||
self->client->killer_yaw = 180 / M_PI *atan2(dir[1], dir[0]);
|
self->client->killer_yaw = 180 / M_PI * atan2(dir[1], dir[0]);
|
||||||
|
}
|
||||||
|
else if (dir[1] > 0)
|
||||||
|
{
|
||||||
|
self->client->killer_yaw = 90;
|
||||||
|
}
|
||||||
|
else if (dir[1] < 0)
|
||||||
|
{
|
||||||
|
self->client->killer_yaw = 270;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -947,6 +1084,14 @@ player_die(edict_t *self, edict_t *inflictor, edict_t *attacker,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (gamerules && gamerules->value) /* if we're in a dm game, alert the game */
|
||||||
|
{
|
||||||
|
if (DMGame.PlayerDeath)
|
||||||
|
{
|
||||||
|
DMGame.PlayerDeath(self, inflictor, attacker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* remove powerups */
|
/* remove powerups */
|
||||||
self->client->quad_framenum = 0;
|
self->client->quad_framenum = 0;
|
||||||
self->client->invincible_framenum = 0;
|
self->client->invincible_framenum = 0;
|
||||||
|
@ -955,18 +1100,59 @@ player_die(edict_t *self, edict_t *inflictor, edict_t *attacker,
|
||||||
self->flags &= ~FL_POWER_ARMOR;
|
self->flags &= ~FL_POWER_ARMOR;
|
||||||
|
|
||||||
self->client->quadfire_framenum = 0;
|
self->client->quadfire_framenum = 0;
|
||||||
|
self->client->double_framenum = 0;
|
||||||
|
|
||||||
|
if (self->client->owned_sphere)
|
||||||
|
{
|
||||||
|
edict_t *sphere;
|
||||||
|
|
||||||
|
sphere = self->client->owned_sphere;
|
||||||
|
sphere->die(sphere, self, self, 0, vec3_origin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we've been killed by the tracker, GIB! */
|
||||||
|
if ((meansOfDeath & ~MOD_FRIENDLY_FIRE) == MOD_TRACKER)
|
||||||
|
{
|
||||||
|
self->health = -100;
|
||||||
|
damage = 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make sure no trackers are still hurting us. */
|
||||||
|
if (self->client->tracker_pain_framenum)
|
||||||
|
{
|
||||||
|
RemoveAttackingPainDaemons(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we got obliterated by the nuke, don't gib */
|
||||||
|
if ((self->health < -80) && (meansOfDeath == MOD_NUKE))
|
||||||
|
{
|
||||||
|
self->flags |= FL_NOGIB;
|
||||||
|
}
|
||||||
|
|
||||||
if (self->health < -40)
|
if (self->health < -40)
|
||||||
{
|
{
|
||||||
/* gib (sound is played at end of server frame) */
|
/* don't toss gibs if we got vaped by the nuke */
|
||||||
self->sounds = gi.soundindex("misc/udeath.wav");
|
if (!(self->flags & FL_NOGIB))
|
||||||
|
|
||||||
for (n = 0; n < 4; n++)
|
|
||||||
{
|
{
|
||||||
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2",
|
/* gib (sound is played at end of server frame) */
|
||||||
damage, GIB_ORGANIC);
|
self->sounds = gi.soundindex("misc/udeath.wav");
|
||||||
|
|
||||||
|
/* more meaty gibs for your dollar! */
|
||||||
|
if ((deathmatch->value) && (self->health < -80))
|
||||||
|
{
|
||||||
|
for (n = 0; n < 4; n++)
|
||||||
|
{
|
||||||
|
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (n = 0; n < 4; n++)
|
||||||
|
{
|
||||||
|
ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", damage, GIB_ORGANIC);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self->flags &= ~FL_NOGIB;
|
||||||
ThrowClientHead(self, damage);
|
ThrowClientHead(self, damage);
|
||||||
|
|
||||||
self->takedamage = DAMAGE_NO;
|
self->takedamage = DAMAGE_NO;
|
||||||
|
@ -1058,6 +1244,10 @@ InitClientPersistant(gclient_t *client)
|
||||||
|
|
||||||
client->pers.max_magslug = 50;
|
client->pers.max_magslug = 50;
|
||||||
client->pers.max_trap = 5;
|
client->pers.max_trap = 5;
|
||||||
|
client->pers.max_prox = 50;
|
||||||
|
client->pers.max_tesla = 50;
|
||||||
|
client->pers.max_flechettes = 200;
|
||||||
|
client->pers.max_rounds = 100;
|
||||||
|
|
||||||
client->pers.connected = true;
|
client->pers.connected = true;
|
||||||
}
|
}
|
||||||
|
@ -1143,7 +1333,7 @@ PlayersRangeFromSpot(edict_t *spot)
|
||||||
|
|
||||||
if (!spot)
|
if (!spot)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bestplayerdistance = 9999999;
|
bestplayerdistance = 9999999;
|
||||||
|
@ -1296,6 +1486,112 @@ SelectDeathmatchSpawnPoint(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
edict_t *
|
||||||
|
SelectLavaCoopSpawnPoint(edict_t *ent)
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
edict_t *spot = NULL;
|
||||||
|
float lavatop;
|
||||||
|
edict_t *lava;
|
||||||
|
edict_t *pointWithLeastLava;
|
||||||
|
float lowest;
|
||||||
|
edict_t *spawnPoints[64];
|
||||||
|
vec3_t center;
|
||||||
|
int numPoints;
|
||||||
|
edict_t *highestlava;
|
||||||
|
|
||||||
|
if (!ent)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
lavatop = -99999;
|
||||||
|
highestlava = NULL;
|
||||||
|
|
||||||
|
lava = NULL;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
lava = G_Find(lava, FOFS(classname), "func_door");
|
||||||
|
|
||||||
|
if (!lava)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorAdd(lava->absmax, lava->absmin, center);
|
||||||
|
VectorScale(center, 0.5, center);
|
||||||
|
|
||||||
|
if (lava->spawnflags & 2 && (gi.pointcontents(center) & MASK_WATER))
|
||||||
|
{
|
||||||
|
if (lava->absmax[2] > lavatop)
|
||||||
|
{
|
||||||
|
lavatop = lava->absmax[2];
|
||||||
|
highestlava = lava;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we didn't find ANY lava, then return NULL */
|
||||||
|
if (!highestlava)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* find the top of the lava and include a small margin of error (plus bbox size) */
|
||||||
|
lavatop = highestlava->absmax[2] + 64;
|
||||||
|
|
||||||
|
/* find all the lava spawn points and store them in spawnPoints[] */
|
||||||
|
spot = NULL;
|
||||||
|
numPoints = 0;
|
||||||
|
|
||||||
|
while ((spot = (G_Find(spot, FOFS(classname), "info_player_coop_lava"))))
|
||||||
|
{
|
||||||
|
if (numPoints == 64)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
spawnPoints[numPoints++] = spot;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numPoints < 1)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* walk up the sorted list and return the lowest, open, non-lava spawn point */
|
||||||
|
spot = NULL;
|
||||||
|
lowest = 999999;
|
||||||
|
pointWithLeastLava = NULL;
|
||||||
|
|
||||||
|
for (index = 0; index < numPoints; index++)
|
||||||
|
{
|
||||||
|
if (spawnPoints[index]->s.origin[2] < lavatop)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PlayersRangeFromSpot(spawnPoints[index]) > 32)
|
||||||
|
{
|
||||||
|
if (spawnPoints[index]->s.origin[2] < lowest)
|
||||||
|
{
|
||||||
|
/* save the last point */
|
||||||
|
pointWithLeastLava = spawnPoints[index];
|
||||||
|
lowest = spawnPoints[index]->s.origin[2];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* well, we may telefrag someone, but oh well... */
|
||||||
|
if (pointWithLeastLava)
|
||||||
|
{
|
||||||
|
return pointWithLeastLava;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
edict_t *
|
edict_t *
|
||||||
SelectCoopSpawnPoint(edict_t *ent)
|
SelectCoopSpawnPoint(edict_t *ent)
|
||||||
{
|
{
|
||||||
|
@ -1308,6 +1604,11 @@ SelectCoopSpawnPoint(edict_t *ent)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Q_stricmp(level.mapname, "rmine2p") || !Q_stricmp(level.mapname, "rmine2"))
|
||||||
|
{
|
||||||
|
return SelectLavaCoopSpawnPoint(ent);
|
||||||
|
}
|
||||||
|
|
||||||
index = ent->client - game.clients;
|
index = ent->client - game.clients;
|
||||||
|
|
||||||
/* player 0 starts in normal player spawn point */
|
/* player 0 starts in normal player spawn point */
|
||||||
|
@ -1718,11 +2019,17 @@ PutClientInServer(edict_t *ent)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* find a spawn point do it before setting
|
/* find a spawn point do it before setting
|
||||||
health back up, so farthest ranging
|
health back up, so farthest ranging
|
||||||
doesn't count this client */
|
doesn't count this client */
|
||||||
SelectSpawnPoint(ent, spawn_origin, spawn_angles);
|
if (gamerules && gamerules->value && DMGame.SelectSpawnPoint)
|
||||||
|
{
|
||||||
|
DMGame.SelectSpawnPoint(ent, spawn_origin, spawn_angles);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SelectSpawnPoint(ent, spawn_origin, spawn_angles);
|
||||||
|
}
|
||||||
|
|
||||||
index = ent - g_edicts - 1;
|
index = ent - g_edicts - 1;
|
||||||
client = ent->client;
|
client = ent->client;
|
||||||
|
@ -1823,7 +2130,15 @@ PutClientInServer(edict_t *ent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model);
|
if (client->pers.weapon)
|
||||||
|
{
|
||||||
|
client->ps.gunindex = gi.modelindex(client->pers.weapon->view_model);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client->ps.gunindex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* clear entity state values */
|
/* clear entity state values */
|
||||||
ent->s.effects = 0;
|
ent->s.effects = 0;
|
||||||
|
@ -1878,6 +2193,22 @@ PutClientInServer(edict_t *ent)
|
||||||
|
|
||||||
gi.linkentity(ent);
|
gi.linkentity(ent);
|
||||||
|
|
||||||
|
/* my tribute to cash's level-specific hacks. I hope
|
||||||
|
* live up to his trailblazing cheese. */
|
||||||
|
if (Q_stricmp(level.mapname, "rboss") == 0)
|
||||||
|
{
|
||||||
|
/* if you get on to rboss in single player or coop, ensure
|
||||||
|
the player has the nuke key. (not in DM) */
|
||||||
|
if (!(deathmatch->value))
|
||||||
|
{
|
||||||
|
gitem_t *item;
|
||||||
|
|
||||||
|
item = FindItem("Antimatter Bomb");
|
||||||
|
client->pers.selected_item = ITEM_INDEX(item);
|
||||||
|
client->pers.inventory[client->pers.selected_item] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* force the current weapon up */
|
/* force the current weapon up */
|
||||||
client->newweapon = client->pers.weapon;
|
client->newweapon = client->pers.weapon;
|
||||||
ChangeWeapon(ent);
|
ChangeWeapon(ent);
|
||||||
|
@ -1897,9 +2228,13 @@ ClientBeginDeathmatch(edict_t *ent)
|
||||||
}
|
}
|
||||||
|
|
||||||
G_InitEdict(ent);
|
G_InitEdict(ent);
|
||||||
|
|
||||||
InitClientResp(ent->client);
|
InitClientResp(ent->client);
|
||||||
|
|
||||||
|
if (gamerules && gamerules->value && DMGame.ClientBegin)
|
||||||
|
{
|
||||||
|
DMGame.ClientBegin(ent);
|
||||||
|
}
|
||||||
|
|
||||||
/* locate ent at a spawn point */
|
/* locate ent at a spawn point */
|
||||||
PutClientInServer(ent);
|
PutClientInServer(ent);
|
||||||
|
|
||||||
|
@ -2192,6 +2527,30 @@ ClientDisconnect(edict_t *ent)
|
||||||
|
|
||||||
gi.bprintf(PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname);
|
gi.bprintf(PRINT_HIGH, "%s disconnected\n", ent->client->pers.netname);
|
||||||
|
|
||||||
|
/* make sure no trackers are still hurting us. */
|
||||||
|
if (ent->client->tracker_pain_framenum)
|
||||||
|
{
|
||||||
|
RemoveAttackingPainDaemons(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent->client->owned_sphere)
|
||||||
|
{
|
||||||
|
if (ent->client->owned_sphere->inuse)
|
||||||
|
{
|
||||||
|
G_FreeEdict(ent->client->owned_sphere);
|
||||||
|
}
|
||||||
|
|
||||||
|
ent->client->owned_sphere = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gamerules && gamerules->value)
|
||||||
|
{
|
||||||
|
if (DMGame.PlayerDisconnect)
|
||||||
|
{
|
||||||
|
DMGame.PlayerDisconnect(ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* send effect */
|
/* send effect */
|
||||||
gi.WriteByte(svc_muzzleflash);
|
gi.WriteByte(svc_muzzleflash);
|
||||||
gi.WriteShort(ent - g_edicts);
|
gi.WriteShort(ent - g_edicts);
|
||||||
|
@ -2211,8 +2570,6 @@ ClientDisconnect(edict_t *ent)
|
||||||
|
|
||||||
/* ============================================================== */
|
/* ============================================================== */
|
||||||
|
|
||||||
static edict_t *pm_passent;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pmove doesn't need to know
|
* pmove doesn't need to know
|
||||||
* about passent and contentmask
|
* about passent and contentmask
|
||||||
|
@ -2329,7 +2686,7 @@ ClientThink(edict_t *ent, usercmd_t *ucmd)
|
||||||
client->ps.pmove.pm_type = PM_NORMAL;
|
client->ps.pmove.pm_type = PM_NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
client->ps.pmove.gravity = sv_gravity->value;
|
client->ps.pmove.gravity = sv_gravity->value * ent->gravity;
|
||||||
pm.s = client->ps.pmove;
|
pm.s = client->ps.pmove;
|
||||||
|
|
||||||
for (i = 0; i < 3; i++)
|
for (i = 0; i < 3; i++)
|
||||||
|
@ -2379,7 +2736,15 @@ ClientThink(edict_t *ent, usercmd_t *ucmd)
|
||||||
PlayerNoise(ent, ent->s.origin, PNOISE_SELF);
|
PlayerNoise(ent, ent->s.origin, PNOISE_SELF);
|
||||||
}
|
}
|
||||||
|
|
||||||
ent->viewheight = pm.viewheight;
|
if (ent->flags & FL_SAM_RAIMI)
|
||||||
|
{
|
||||||
|
ent->viewheight = 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ent->viewheight = pm.viewheight;
|
||||||
|
}
|
||||||
|
|
||||||
ent->waterlevel = pm.waterlevel;
|
ent->waterlevel = pm.waterlevel;
|
||||||
ent->watertype = pm.watertype;
|
ent->watertype = pm.watertype;
|
||||||
ent->groundentity = pm.groundentity;
|
ent->groundentity = pm.groundentity;
|
||||||
|
@ -2403,6 +2768,8 @@ ClientThink(edict_t *ent, usercmd_t *ucmd)
|
||||||
|
|
||||||
gi.linkentity(ent);
|
gi.linkentity(ent);
|
||||||
|
|
||||||
|
ent->gravity = 1.0;
|
||||||
|
|
||||||
if (ent->movetype != MOVETYPE_NOCLIP)
|
if (ent->movetype != MOVETYPE_NOCLIP)
|
||||||
{
|
{
|
||||||
G_TouchTriggers(ent);
|
G_TouchTriggers(ent);
|
||||||
|
@ -2581,3 +2948,35 @@ ClientBeginServerFrame(edict_t *ent)
|
||||||
|
|
||||||
client->latched_buttons = 0;
|
client->latched_buttons = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is called to clean up the pain daemons that
|
||||||
|
* the disruptor attaches to clients to damage them.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
RemoveAttackingPainDaemons(edict_t *self)
|
||||||
|
{
|
||||||
|
edict_t *tracker;
|
||||||
|
|
||||||
|
if (!self)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker = G_Find(NULL, FOFS(classname), "pain daemon");
|
||||||
|
|
||||||
|
while (tracker)
|
||||||
|
{
|
||||||
|
if (tracker->enemy == self)
|
||||||
|
{
|
||||||
|
G_FreeEdict(tracker);
|
||||||
|
}
|
||||||
|
|
||||||
|
tracker = G_Find(tracker, FOFS(classname), "pain daemon");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->client)
|
||||||
|
{
|
||||||
|
self->client->tracker_pain_framenum = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
1944
src/rogue/g_cmds.c
1944
src/rogue/g_cmds.c
File diff suppressed because it is too large
Load diff
1445
src/rogue/g_phys.c
1445
src/rogue/g_phys.c
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1336
src/xatrix/g_ai.c
1336
src/xatrix/g_ai.c
File diff suppressed because it is too large
Load diff
|
@ -1,250 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) ZeniMax Media Inc.
|
|
||||||
* Licensed under the GNU General Public License 2.0.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* =======================================================================
|
|
||||||
*
|
|
||||||
* Chase cam. Only used in multiplayer mode.
|
|
||||||
*
|
|
||||||
* =======================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "header/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 angles;
|
|
||||||
|
|
||||||
if (!ent)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
if (!ent)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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.");
|
|
||||||
}
|
|
1927
src/xatrix/g_cmds.c
1927
src/xatrix/g_cmds.c
File diff suppressed because it is too large
Load diff
|
@ -1,964 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
|
||||||
* Copyright (c) ZeniMax Media Inc.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
||||||
* 02111-1307, USA.
|
|
||||||
*
|
|
||||||
* =======================================================================
|
|
||||||
*
|
|
||||||
* Combat code like damage, death and so on.
|
|
||||||
*
|
|
||||||
* =======================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "header/local.h"
|
|
||||||
|
|
||||||
void M_SetEffects(edict_t *self);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* clean up heal targets for medic
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
cleanupHealTarget(edict_t *ent)
|
|
||||||
{
|
|
||||||
if (!ent)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ent->monsterinfo.healer = NULL;
|
|
||||||
ent->takedamage = DAMAGE_YES;
|
|
||||||
ent->monsterinfo.aiflags &= ~AI_RESURRECTING;
|
|
||||||
M_SetEffects(ent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
if (!targ || !inflictor)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker,
|
|
||||||
int damage, vec3_t point)
|
|
||||||
{
|
|
||||||
if (!targ || !inflictor || !attacker)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
targ->enemy = attacker;
|
|
||||||
|
|
||||||
if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
|
|
||||||
{
|
|
||||||
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 (attacker->classname && 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SpawnDamage(int type, vec3_t origin, vec3_t normal)
|
|
||||||
{
|
|
||||||
gi.WriteByte(svc_temp_entity);
|
|
||||||
gi.WriteByte(type);
|
|
||||||
gi.WritePosition(origin);
|
|
||||||
gi.WriteDir(normal);
|
|
||||||
gi.multicast(origin, MULTICAST_PVS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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
|
|
||||||
* ============
|
|
||||||
*/
|
|
||||||
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 = 0;
|
|
||||||
int damagePerCell;
|
|
||||||
int pa_te_type;
|
|
||||||
int power = 0;
|
|
||||||
int power_used;
|
|
||||||
|
|
||||||
if (!ent)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!damage)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
index = 0;
|
|
||||||
|
|
||||||
client = ent->client;
|
|
||||||
|
|
||||||
if (dflags & DAMAGE_NO_ARMOR)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (client)
|
|
||||||
{
|
|
||||||
power_armor_type = PowerArmorType(ent);
|
|
||||||
|
|
||||||
if (power_armor_type != POWER_ARMOR_NONE)
|
|
||||||
{
|
|
||||||
index = ITEM_INDEX(FindItem("Cells"));
|
|
||||||
power = client->pers.inventory[index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (ent->svflags & SVF_MONSTER)
|
|
||||||
{
|
|
||||||
power_armor_type = ent->monsterinfo.power_armor_type;
|
|
||||||
power = ent->monsterinfo.power_armor_power;
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (!ent)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
return save;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
M_ReactToDamage(edict_t *targ, edict_t *attacker)
|
|
||||||
{
|
|
||||||
if (!targ || !attacker)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targ->health <= 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((attacker == targ) || (attacker == targ->enemy))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if we are a good guy monster and our attacker is a
|
|
||||||
player or another good guy, do not get mad at them */
|
|
||||||
if (targ->monsterinfo.aiflags & AI_GOOD_GUY)
|
|
||||||
{
|
|
||||||
if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if attacker is a client, get mad at
|
|
||||||
them because he's good and we're not */
|
|
||||||
if (attacker->client)
|
|
||||||
{
|
|
||||||
targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
|
|
||||||
|
|
||||||
/* this can only happen in coop (both new and old
|
|
||||||
enemies are clients) only switch if can't see the
|
|
||||||
current enemy */
|
|
||||||
if (targ->enemy && targ->enemy->client)
|
|
||||||
{
|
|
||||||
if (visible(targ, targ->enemy))
|
|
||||||
{
|
|
||||||
targ->oldenemy = attacker;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
targ->oldenemy = targ->enemy;
|
|
||||||
}
|
|
||||||
|
|
||||||
targ->enemy = attacker;
|
|
||||||
|
|
||||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
|
||||||
{
|
|
||||||
FoundTarget(targ);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* it's the same base (walk/swim/fly) type and
|
|
||||||
a different classname and it's not a tank
|
|
||||||
(they spray too much), get mad at them */
|
|
||||||
if (((targ->flags & (FL_FLY | FL_SWIM)) ==
|
|
||||||
(attacker->flags & (FL_FLY | FL_SWIM))) &&
|
|
||||||
(strcmp(targ->classname, attacker->classname) != 0) &&
|
|
||||||
(strcmp(attacker->classname, "monster_tank") != 0) &&
|
|
||||||
(strcmp(attacker->classname, "monster_supertank") != 0) &&
|
|
||||||
(strcmp(attacker->classname, "monster_makron") != 0) &&
|
|
||||||
(strcmp(attacker->classname, "monster_jorg") != 0))
|
|
||||||
{
|
|
||||||
if (targ->enemy && targ->enemy->client)
|
|
||||||
{
|
|
||||||
targ->oldenemy = targ->enemy;
|
|
||||||
}
|
|
||||||
|
|
||||||
targ->enemy = attacker;
|
|
||||||
|
|
||||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
|
||||||
{
|
|
||||||
FoundTarget(targ);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* if they *meant* to shoot us, then shoot back */
|
|
||||||
else if (attacker->enemy == targ)
|
|
||||||
{
|
|
||||||
if (targ->enemy && targ->enemy->client)
|
|
||||||
{
|
|
||||||
targ->oldenemy = targ->enemy;
|
|
||||||
}
|
|
||||||
|
|
||||||
targ->enemy = attacker;
|
|
||||||
|
|
||||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
|
||||||
{
|
|
||||||
FoundTarget(targ);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* otherwise get mad at whoever they are mad at (help our buddy) unless it is us! */
|
|
||||||
else if (attacker->enemy)
|
|
||||||
{
|
|
||||||
if (targ->enemy && targ->enemy->client)
|
|
||||||
{
|
|
||||||
targ->oldenemy = targ->enemy;
|
|
||||||
}
|
|
||||||
|
|
||||||
targ->enemy = attacker->enemy;
|
|
||||||
|
|
||||||
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
|
|
||||||
{
|
|
||||||
FoundTarget(targ);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
apply_knockback(edict_t *targ, vec3_t dir, float knockback, float scale)
|
|
||||||
{
|
|
||||||
vec3_t kvel;
|
|
||||||
float mass;
|
|
||||||
|
|
||||||
if (!knockback)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mass = (targ->mass < 50) ? 50.0f : (float)targ->mass;
|
|
||||||
|
|
||||||
VectorNormalize2(dir, kvel);
|
|
||||||
VectorScale(kvel, scale * (knockback / mass), kvel);
|
|
||||||
VectorAdd(targ->velocity, kvel, targ->velocity);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 || !inflictor || !attacker)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!targ->takedamage)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
meansOfDeath = mod;
|
|
||||||
|
|
||||||
/* easy mode takes half damage */
|
|
||||||
if ((skill->value == SKILL_EASY) && (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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* bonus damage for suprising a monster */
|
|
||||||
if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) &&
|
|
||||||
(attacker->client) && (!targ->enemy) && (targ->health > 0))
|
|
||||||
{
|
|
||||||
damage *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targ->flags & FL_NO_KNOCKBACK)
|
|
||||||
{
|
|
||||||
knockback = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* figure momentum add */
|
|
||||||
if (!(dflags & DAMAGE_NO_KNOCKBACK) &&
|
|
||||||
(targ->movetype != MOVETYPE_NONE) &&
|
|
||||||
(targ->movetype != MOVETYPE_BOUNCE) &&
|
|
||||||
(targ->movetype != MOVETYPE_PUSH) &&
|
|
||||||
(targ->movetype != MOVETYPE_STOP))
|
|
||||||
{
|
|
||||||
apply_knockback (targ, dir, knockback,
|
|
||||||
((client && attacker == targ) ? 1600.0f : 500.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check for invincibility */
|
|
||||||
if ((client && (client->invincible_framenum > level.framenum)) &&
|
|
||||||
!(dflags & DAMAGE_NO_PROTECTION) && (mod != MOD_TRAP))
|
|
||||||
{
|
|
||||||
if (targ->pain_debounce_time < level.time)
|
|
||||||
{
|
|
||||||
gi.sound(targ, CHAN_ITEM, gi.soundindex(
|
|
||||||
"items/protect4.wav"), 1, ATTN_NORM, 0);
|
|
||||||
targ->pain_debounce_time = level.time + 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
take = 0;
|
|
||||||
save = damage;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
/* do the damage */
|
|
||||||
if (take)
|
|
||||||
{
|
|
||||||
if ((targ->svflags & SVF_MONSTER) || (client))
|
|
||||||
{
|
|
||||||
if (strcmp(targ->classname, "monster_gekk") == 0)
|
|
||||||
{
|
|
||||||
SpawnDamage(TE_GREENBLOOD, point, normal);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SpawnDamage(TE_BLOOD, point, normal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SpawnDamage(te_sparks, point, normal);
|
|
||||||
}
|
|
||||||
|
|
||||||
targ->health = targ->health - take;
|
|
||||||
|
|
||||||
if (targ->health <= 0)
|
|
||||||
{
|
|
||||||
if ((targ->svflags & SVF_MONSTER) || (client))
|
|
||||||
{
|
|
||||||
targ->flags |= FL_NO_KNOCKBACK;
|
|
||||||
}
|
|
||||||
|
|
||||||
Killed(targ, inflictor, attacker, take, point);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targ->svflags & SVF_MONSTER)
|
|
||||||
{
|
|
||||||
M_ReactToDamage(targ, attacker);
|
|
||||||
|
|
||||||
if (!(targ->monsterinfo.aiflags & (AI_DUCKED|AI_IGNORE_PAIN)) && (take))
|
|
||||||
{
|
|
||||||
targ->pain(targ, attacker, knockback, take);
|
|
||||||
|
|
||||||
/* nightmare mode monsters don't go into pain frames often */
|
|
||||||
if (skill->value == SKILL_HARDPLUS)
|
|
||||||
{
|
|
||||||
targ->pain_debounce_time = level.time + 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (client)
|
|
||||||
{
|
|
||||||
if (!(targ->flags & FL_GODMODE) && (take))
|
|
||||||
{
|
|
||||||
targ->pain(targ, attacker, knockback, take);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (take)
|
|
||||||
{
|
|
||||||
if (targ->pain)
|
|
||||||
{
|
|
||||||
targ->pain(targ, attacker, knockback, take);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add to the damage inflicted on a player this frame
|
|
||||||
the total will be turned into screen blends and view angle kicks
|
|
||||||
at the end of the frame */
|
|
||||||
if (client)
|
|
||||||
{
|
|
||||||
client->damage_parmor += psave;
|
|
||||||
client->damage_armor += asave;
|
|
||||||
client->damage_blood += take;
|
|
||||||
client->damage_knockback += knockback;
|
|
||||||
VectorCopy(point, client->damage_from);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
if (!inflictor || !attacker)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
T_RadiusNukeDamage(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;
|
|
||||||
float len;
|
|
||||||
float killzone, killzone2;
|
|
||||||
trace_t tr;
|
|
||||||
float dist;
|
|
||||||
|
|
||||||
killzone = radius;
|
|
||||||
killzone2 = radius * 2.0;
|
|
||||||
|
|
||||||
if (!inflictor || !attacker || !ignore)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((ent = findradius(ent, inflictor->s.origin, killzone2)) != NULL)
|
|
||||||
{
|
|
||||||
/* ignore nobody */
|
|
||||||
if (ent == ignore)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ent->takedamage)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ent->inuse)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(ent->client || (ent->svflags & SVF_MONSTER) ||
|
|
||||||
(ent->svflags & SVF_DAMAGEABLE)))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
VectorAdd(ent->mins, ent->maxs, v);
|
|
||||||
VectorMA(ent->s.origin, 0.5, v, v);
|
|
||||||
VectorSubtract(inflictor->s.origin, v, v);
|
|
||||||
len = VectorLength(v);
|
|
||||||
|
|
||||||
if (len <= killzone)
|
|
||||||
{
|
|
||||||
if (ent->client)
|
|
||||||
{
|
|
||||||
ent->flags |= FL_NOGIB;
|
|
||||||
}
|
|
||||||
|
|
||||||
points = 10000;
|
|
||||||
}
|
|
||||||
else if (len <= killzone2)
|
|
||||||
{
|
|
||||||
points = (damage / killzone) * (killzone2 - len);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
points = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (points > 0)
|
|
||||||
{
|
|
||||||
if (ent->client)
|
|
||||||
{
|
|
||||||
ent->client->nuke_framenum = level.framenum + 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* skip the worldspawn */
|
|
||||||
ent = g_edicts + 1;
|
|
||||||
|
|
||||||
/* cycle through players */
|
|
||||||
while (ent)
|
|
||||||
{
|
|
||||||
if ((ent->client) &&
|
|
||||||
(ent->client->nuke_framenum != level.framenum + 20) && (ent->inuse))
|
|
||||||
{
|
|
||||||
tr = gi.trace(inflictor->s.origin, NULL, NULL, ent->s.origin,
|
|
||||||
inflictor, MASK_SOLID);
|
|
||||||
|
|
||||||
if (tr.fraction == 1.0)
|
|
||||||
{
|
|
||||||
ent->client->nuke_framenum = level.framenum + 20;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dist = realrange(ent, inflictor);
|
|
||||||
|
|
||||||
if (dist < 2048)
|
|
||||||
{
|
|
||||||
ent->client->nuke_framenum = max(ent->client->nuke_framenum,
|
|
||||||
level.framenum + 15);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ent->client->nuke_framenum = max(ent->client->nuke_framenum,
|
|
||||||
level.framenum + 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ent++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ent = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Like T_RadiusDamage, but ignores
|
|
||||||
* anything with classname=ignoreClass
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
T_RadiusClassDamage(edict_t *inflictor, edict_t *attacker, float damage,
|
|
||||||
char *ignoreClass, float radius, int mod)
|
|
||||||
{
|
|
||||||
float points;
|
|
||||||
edict_t *ent = NULL;
|
|
||||||
vec3_t v;
|
|
||||||
vec3_t dir;
|
|
||||||
|
|
||||||
if (!inflictor || !attacker || !ignoreClass)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
|
|
||||||
{
|
|
||||||
if (ent->classname && !strcmp(ent->classname, ignoreClass))
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
3218
src/xatrix/g_func.c
3218
src/xatrix/g_func.c
File diff suppressed because it is too large
Load diff
3049
src/xatrix/g_items.c
3049
src/xatrix/g_items.c
File diff suppressed because it is too large
Load diff
1319
src/xatrix/g_phys.c
1319
src/xatrix/g_phys.c
File diff suppressed because it is too large
Load diff
1132
src/xatrix/g_spawn.c
1132
src/xatrix/g_spawn.c
File diff suppressed because it is too large
Load diff
|
@ -1,356 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
|
||||||
* Copyright (c) ZeniMax Media Inc.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
||||||
* 02111-1307, USA.
|
|
||||||
*
|
|
||||||
* =======================================================================
|
|
||||||
*
|
|
||||||
* Game side of server CMDs. At this time only the ipfilter.
|
|
||||||
*
|
|
||||||
* =======================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "header/local.h"
|
|
||||||
|
|
||||||
#define MAX_IPFILTERS 1024
|
|
||||||
|
|
||||||
void
|
|
||||||
Svcmd_Test_f(void)
|
|
||||||
{
|
|
||||||
gi.cprintf(NULL, PRINT_HIGH, "Svcmd_Test_f()\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ==============================================================================
|
|
||||||
*
|
|
||||||
* 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 belive 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;
|
|
||||||
|
|
||||||
ipfilter_t ipfilters[MAX_IPFILTERS];
|
|
||||||
int numipfilters;
|
|
||||||
|
|
||||||
qboolean
|
|
||||||
StringToFilter(char *s, ipfilter_t *f)
|
|
||||||
{
|
|
||||||
char num[128];
|
|
||||||
int i, j;
|
|
||||||
byte b[4];
|
|
||||||
byte m[4];
|
|
||||||
|
|
||||||
if (!s || !f)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PVS NOTE: maybe use memcpy here instead? */
|
|
||||||
f->mask = *(unsigned *)m;
|
|
||||||
f->compare = *(unsigned *)b;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
qboolean
|
|
||||||
SV_FilterPacket(char *from)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
unsigned in;
|
|
||||||
byte m[4];
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
if (!from)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PVS NOTE: maybe use memcpy instead? */
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SVCmd_ListIP_f(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
byte b[4];
|
|
||||||
|
|
||||||
gi.cprintf(NULL, PRINT_HIGH, "Filter list:\n");
|
|
||||||
|
|
||||||
for (i = 0; i < numipfilters; i++)
|
|
||||||
{
|
|
||||||
/* PVS NOTE: maybe use memcpy instead? */
|
|
||||||
*(unsigned *)b = ipfilters[i].compare;
|
|
||||||
gi.cprintf(NULL, PRINT_HIGH, "%3i.%3i.%3i.%3i\n",
|
|
||||||
b[0], b[1], b[2], b[3]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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++)
|
|
||||||
{
|
|
||||||
/* PVS NOTE: maybe use memcpy instead? */
|
|
||||||
*(unsigned *)b = ipfilters[i].compare;
|
|
||||||
fprintf(f, "sv addip %i.%i.%i.%i\n", b[0], b[1], b[2], b[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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, "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);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,925 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
|
||||||
* Copyright (c) ZeniMax Media Inc.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
||||||
* 02111-1307, USA.
|
|
||||||
*
|
|
||||||
* =======================================================================
|
|
||||||
*
|
|
||||||
* Trigger.
|
|
||||||
*
|
|
||||||
* =======================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "header/local.h"
|
|
||||||
|
|
||||||
#define PUSH_ONCE 1
|
|
||||||
|
|
||||||
void trigger_push_active(edict_t *self);
|
|
||||||
|
|
||||||
static int windsound;
|
|
||||||
|
|
||||||
void
|
|
||||||
InitTrigger(edict_t *self)
|
|
||||||
{
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (!ent)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 /* unused */, edict_t *activator)
|
|
||||||
{
|
|
||||||
if (!ent || !activator)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ent->activator = activator;
|
|
||||||
multi_trigger(ent);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Touch_Multi(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
|
||||||
csurface_t *surf /* unused */)
|
|
||||||
{
|
|
||||||
if (!self || !other)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 /* unused */,
|
|
||||||
edict_t *activator /* unused */)
|
|
||||||
{
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->solid = SOLID_TRIGGER;
|
|
||||||
self->use = Use_Multi;
|
|
||||||
gi.linkentity(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SP_trigger_multiple(edict_t *ent)
|
|
||||||
{
|
|
||||||
if (!ent)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
*
|
|
||||||
* "message" string to be displayed when triggered
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
SP_trigger_once(edict_t *ent)
|
|
||||||
{
|
|
||||||
if (!ent)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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 /* unused */,
|
|
||||||
edict_t *activator /* may be NULL */)
|
|
||||||
{
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
G_UseTargets(self, activator);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SP_trigger_relay(edict_t *self)
|
|
||||||
{
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->use = trigger_relay_use;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 /* unused */, edict_t *activator)
|
|
||||||
{
|
|
||||||
int index;
|
|
||||||
|
|
||||||
if (!self || !activator)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
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 (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* QUAKED trigger_counter (.5 .5 .5) ? nomessage
|
|
||||||
* Acts as an intermediary for an action that takes multiple inputs.
|
|
||||||
*
|
|
||||||
* If nomessage is not set, it 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 /* unused */, edict_t *activator)
|
|
||||||
{
|
|
||||||
if (!self || !activator)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self->count == 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->count--;
|
|
||||||
|
|
||||||
if (self->count)
|
|
||||||
{
|
|
||||||
if (!(self->spawnflags & 1))
|
|
||||||
{
|
|
||||||
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))
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->wait = -1;
|
|
||||||
|
|
||||||
if (!self->count)
|
|
||||||
{
|
|
||||||
self->count = 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->use = trigger_counter_use;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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)
|
|
||||||
{
|
|
||||||
if (!ent)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
trigger_push_touch(edict_t *self, edict_t *other, cplane_t *plane /* unused */,
|
|
||||||
csurface_t *surf /* unused */)
|
|
||||||
{
|
|
||||||
if (!self || !other)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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_effect(edict_t *self)
|
|
||||||
{
|
|
||||||
vec3_t origin;
|
|
||||||
vec3_t size;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 /* unused */, edict_t *activator /* unused */)
|
|
||||||
{
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 /* unused */,
|
|
||||||
csurface_t *surf /* unused */)
|
|
||||||
{
|
|
||||||
int dflags;
|
|
||||||
|
|
||||||
if (!self || !other)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 /* unused */, csurface_t *surf /* unused */)
|
|
||||||
{
|
|
||||||
if (!self || !other)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
other->gravity = self->gravity;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SP_trigger_gravity(edict_t *self)
|
|
||||||
{
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = (int)strtol(st.gravity, (char **)NULL, 10);
|
|
||||||
self->touch = trigger_gravity_touch;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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 /* unused */, csurface_t *surf /* unused */)
|
|
||||||
{
|
|
||||||
if (!self || !other)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -1,609 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 1997-2001 Id Software, Inc.
|
|
||||||
* Copyright (c) ZeniMax Media Inc.
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*
|
|
||||||
* See the GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
||||||
* 02111-1307, USA.
|
|
||||||
*
|
|
||||||
* =======================================================================
|
|
||||||
*
|
|
||||||
* Turrets aka big cannons with a driver.
|
|
||||||
*
|
|
||||||
* =======================================================================
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "header/local.h"
|
|
||||||
|
|
||||||
void infantry_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point);
|
|
||||||
void infantry_stand(edict_t *self);
|
|
||||||
void monster_use(edict_t *self, edict_t *other, edict_t *activator);
|
|
||||||
|
|
||||||
qboolean FindTarget(edict_t *self);
|
|
||||||
|
|
||||||
void
|
|
||||||
AnglesNormalize(vec3_t vec)
|
|
||||||
{
|
|
||||||
while (vec[0] > 360)
|
|
||||||
{
|
|
||||||
vec[0] -= 360;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (vec[0] < 0)
|
|
||||||
{
|
|
||||||
vec[0] += 360;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (vec[1] > 360)
|
|
||||||
{
|
|
||||||
vec[1] -= 360;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (vec[1] < 0)
|
|
||||||
{
|
|
||||||
vec[1] += 360;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float
|
|
||||||
SnapToEights(float x)
|
|
||||||
{
|
|
||||||
x *= 8.0;
|
|
||||||
|
|
||||||
if (x > 0.0)
|
|
||||||
{
|
|
||||||
x += 0.5;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
x -= 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0.125 * (int)x;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
turret_blocked(edict_t *self, edict_t *other)
|
|
||||||
{
|
|
||||||
edict_t *attacker;
|
|
||||||
|
|
||||||
if (!self || !other)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (other->takedamage)
|
|
||||||
{
|
|
||||||
if (self->teammaster->owner)
|
|
||||||
{
|
|
||||||
attacker = self->teammaster->owner;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
attacker = self->teammaster;
|
|
||||||
}
|
|
||||||
|
|
||||||
T_Damage(other, self, attacker, vec3_origin, other->s.origin,
|
|
||||||
vec3_origin, self->teammaster->dmg, 10, 0, MOD_CRUSH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* QUAKED turret_breach (0 0 0) ?
|
|
||||||
* This portion of the turret can change both pitch and yaw.
|
|
||||||
* The model should be made with a flat pitch.
|
|
||||||
* It (and the associated base) need to be oriented towards 0.
|
|
||||||
* Use "angle" to set the starting angle.
|
|
||||||
*
|
|
||||||
* "speed" default 50
|
|
||||||
* "dmg" default 10
|
|
||||||
* "angle" point this forward
|
|
||||||
* "target" point this at an info_notnull at the muzzle tip
|
|
||||||
* "minpitch" min acceptable pitch angle : default -30
|
|
||||||
* "maxpitch" max acceptable pitch angle : default 30
|
|
||||||
* "minyaw" min acceptable yaw angle : default 0
|
|
||||||
* "maxyaw" max acceptable yaw angle : default 360
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
turret_breach_fire(edict_t *self)
|
|
||||||
{
|
|
||||||
vec3_t f, r, u;
|
|
||||||
vec3_t start;
|
|
||||||
int damage;
|
|
||||||
int speed;
|
|
||||||
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AngleVectors(self->s.angles, f, r, u);
|
|
||||||
VectorMA(self->s.origin, self->move_origin[0], f, start);
|
|
||||||
VectorMA(start, self->move_origin[1], r, start);
|
|
||||||
VectorMA(start, self->move_origin[2], u, start);
|
|
||||||
|
|
||||||
damage = 100 + random() * 50;
|
|
||||||
speed = 550 + 50 * skill->value;
|
|
||||||
fire_rocket(self->teammaster->owner, start, f, damage, speed, 150, damage);
|
|
||||||
gi.positioned_sound(start, self, CHAN_WEAPON,
|
|
||||||
gi.soundindex("weapons/rocklf1a.wav"), 1, ATTN_NORM, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
turret_breach_think(edict_t *self)
|
|
||||||
{
|
|
||||||
edict_t *ent;
|
|
||||||
vec3_t current_angles;
|
|
||||||
vec3_t delta;
|
|
||||||
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
VectorCopy(self->s.angles, current_angles);
|
|
||||||
AnglesNormalize(current_angles);
|
|
||||||
|
|
||||||
AnglesNormalize(self->move_angles);
|
|
||||||
|
|
||||||
if (self->move_angles[PITCH] > 180)
|
|
||||||
{
|
|
||||||
self->move_angles[PITCH] -= 360;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* clamp angles to mins & maxs */
|
|
||||||
if (self->move_angles[PITCH] > self->pos1[PITCH])
|
|
||||||
{
|
|
||||||
self->move_angles[PITCH] = self->pos1[PITCH];
|
|
||||||
}
|
|
||||||
else if (self->move_angles[PITCH] < self->pos2[PITCH])
|
|
||||||
{
|
|
||||||
self->move_angles[PITCH] = self->pos2[PITCH];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((self->move_angles[YAW] < self->pos1[YAW]) ||
|
|
||||||
(self->move_angles[YAW] > self->pos2[YAW]))
|
|
||||||
{
|
|
||||||
float dmin, dmax;
|
|
||||||
|
|
||||||
dmin = fabs(self->pos1[YAW] - self->move_angles[YAW]);
|
|
||||||
|
|
||||||
if (dmin < -180)
|
|
||||||
{
|
|
||||||
dmin += 360;
|
|
||||||
}
|
|
||||||
else if (dmin > 180)
|
|
||||||
{
|
|
||||||
dmin -= 360;
|
|
||||||
}
|
|
||||||
|
|
||||||
dmax = fabs(self->pos2[YAW] - self->move_angles[YAW]);
|
|
||||||
|
|
||||||
if (dmax < -180)
|
|
||||||
{
|
|
||||||
dmax += 360;
|
|
||||||
}
|
|
||||||
else if (dmax > 180)
|
|
||||||
{
|
|
||||||
dmax -= 360;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fabs(dmin) < fabs(dmax))
|
|
||||||
{
|
|
||||||
self->move_angles[YAW] = self->pos1[YAW];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self->move_angles[YAW] = self->pos2[YAW];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VectorSubtract(self->move_angles, current_angles, delta);
|
|
||||||
|
|
||||||
if (delta[0] < -180)
|
|
||||||
{
|
|
||||||
delta[0] += 360;
|
|
||||||
}
|
|
||||||
else if (delta[0] > 180)
|
|
||||||
{
|
|
||||||
delta[0] -= 360;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (delta[1] < -180)
|
|
||||||
{
|
|
||||||
delta[1] += 360;
|
|
||||||
}
|
|
||||||
else if (delta[1] > 180)
|
|
||||||
{
|
|
||||||
delta[1] -= 360;
|
|
||||||
}
|
|
||||||
|
|
||||||
delta[2] = 0;
|
|
||||||
|
|
||||||
if (delta[0] > self->speed * FRAMETIME)
|
|
||||||
{
|
|
||||||
delta[0] = self->speed * FRAMETIME;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (delta[0] < -1 * self->speed * FRAMETIME)
|
|
||||||
{
|
|
||||||
delta[0] = -1 * self->speed * FRAMETIME;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (delta[1] > self->speed * FRAMETIME)
|
|
||||||
{
|
|
||||||
delta[1] = self->speed * FRAMETIME;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (delta[1] < -1 * self->speed * FRAMETIME)
|
|
||||||
{
|
|
||||||
delta[1] = -1 * self->speed * FRAMETIME;
|
|
||||||
}
|
|
||||||
|
|
||||||
VectorScale(delta, 1.0 / FRAMETIME, self->avelocity);
|
|
||||||
|
|
||||||
self->nextthink = level.time + FRAMETIME;
|
|
||||||
|
|
||||||
for (ent = self->teammaster; ent; ent = ent->teamchain)
|
|
||||||
{
|
|
||||||
ent->avelocity[1] = self->avelocity[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if we have a driver, adjust his velocities */
|
|
||||||
if (self->owner)
|
|
||||||
{
|
|
||||||
float angle;
|
|
||||||
float target_z;
|
|
||||||
float diff;
|
|
||||||
vec3_t target;
|
|
||||||
vec3_t dir;
|
|
||||||
|
|
||||||
/* angular is easy, just copy ours */
|
|
||||||
self->owner->avelocity[0] = self->avelocity[0];
|
|
||||||
self->owner->avelocity[1] = self->avelocity[1];
|
|
||||||
|
|
||||||
/* x & y */
|
|
||||||
angle = self->s.angles[1] + self->owner->move_origin[1];
|
|
||||||
angle *= (M_PI * 2 / 360);
|
|
||||||
target[0] = SnapToEights(self->s.origin[0] + cos(
|
|
||||||
angle) * self->owner->move_origin[0]);
|
|
||||||
target[1] = SnapToEights(self->s.origin[1] + sin(
|
|
||||||
angle) * self->owner->move_origin[0]);
|
|
||||||
target[2] = self->owner->s.origin[2];
|
|
||||||
|
|
||||||
VectorSubtract(target, self->owner->s.origin, dir);
|
|
||||||
self->owner->velocity[0] = dir[0] * 1.0 / FRAMETIME;
|
|
||||||
self->owner->velocity[1] = dir[1] * 1.0 / FRAMETIME;
|
|
||||||
|
|
||||||
/* z */
|
|
||||||
angle = self->s.angles[PITCH] * (M_PI * 2 / 360);
|
|
||||||
target_z = SnapToEights(
|
|
||||||
self->s.origin[2] + self->owner->move_origin[0] * tan(
|
|
||||||
angle) + self->owner->move_origin[2]);
|
|
||||||
|
|
||||||
diff = target_z - self->owner->s.origin[2];
|
|
||||||
self->owner->velocity[2] = diff * 1.0 / FRAMETIME;
|
|
||||||
|
|
||||||
if (self->spawnflags & 65536)
|
|
||||||
{
|
|
||||||
turret_breach_fire(self);
|
|
||||||
self->spawnflags &= ~65536;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
turret_breach_finish_init(edict_t *self)
|
|
||||||
{
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get and save info for muzzle location */
|
|
||||||
if (!self->target)
|
|
||||||
{
|
|
||||||
gi.dprintf("%s at %s needs a target\n", self->classname,
|
|
||||||
vtos(self->s.origin));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self->target_ent = G_PickTarget(self->target);
|
|
||||||
VectorSubtract(self->target_ent->s.origin, self->s.origin,
|
|
||||||
self->move_origin);
|
|
||||||
G_FreeEdict(self->target_ent);
|
|
||||||
}
|
|
||||||
|
|
||||||
self->teammaster->dmg = self->dmg;
|
|
||||||
self->think = turret_breach_think;
|
|
||||||
self->think(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SP_turret_breach(edict_t *self)
|
|
||||||
{
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->solid = SOLID_BSP;
|
|
||||||
self->movetype = MOVETYPE_PUSH;
|
|
||||||
gi.setmodel(self, self->model);
|
|
||||||
|
|
||||||
if (!self->speed)
|
|
||||||
{
|
|
||||||
self->speed = 50;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!self->dmg)
|
|
||||||
{
|
|
||||||
self->dmg = 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!st.minpitch)
|
|
||||||
{
|
|
||||||
st.minpitch = -30;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!st.maxpitch)
|
|
||||||
{
|
|
||||||
st.maxpitch = 30;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!st.maxyaw)
|
|
||||||
{
|
|
||||||
st.maxyaw = 360;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->pos1[PITCH] = -1 * st.minpitch;
|
|
||||||
self->pos1[YAW] = st.minyaw;
|
|
||||||
self->pos2[PITCH] = -1 * st.maxpitch;
|
|
||||||
self->pos2[YAW] = st.maxyaw;
|
|
||||||
|
|
||||||
self->ideal_yaw = self->s.angles[YAW];
|
|
||||||
self->move_angles[YAW] = self->ideal_yaw;
|
|
||||||
|
|
||||||
self->blocked = turret_blocked;
|
|
||||||
|
|
||||||
self->think = turret_breach_finish_init;
|
|
||||||
self->nextthink = level.time + FRAMETIME;
|
|
||||||
gi.linkentity(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* QUAKED turret_base (0 0 0) ?
|
|
||||||
* This portion of the turret changes yaw only.
|
|
||||||
* MUST be teamed with a turret_breach.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
SP_turret_base(edict_t *self)
|
|
||||||
{
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->solid = SOLID_BSP;
|
|
||||||
self->movetype = MOVETYPE_PUSH;
|
|
||||||
gi.setmodel(self, self->model);
|
|
||||||
self->blocked = turret_blocked;
|
|
||||||
gi.linkentity(self);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* QUAKED turret_driver (1 .5 0) (-16 -16 -24) (16 16 32)
|
|
||||||
* Must NOT be on the team with the rest of the turret parts.
|
|
||||||
* Instead it must target the turret_breach.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
turret_driver_die(edict_t *self, edict_t *inflictor, edict_t *attacker,
|
|
||||||
int damage, vec3_t point)
|
|
||||||
{
|
|
||||||
edict_t *ent;
|
|
||||||
|
|
||||||
if (!self || !inflictor || !attacker)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* level the gun */
|
|
||||||
self->target_ent->move_angles[0] = 0;
|
|
||||||
|
|
||||||
/* remove the driver from the end of them team chain */
|
|
||||||
for (ent = self->target_ent->teammaster;
|
|
||||||
ent->teamchain != self;
|
|
||||||
ent = ent->teamchain)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ent->teamchain = NULL;
|
|
||||||
self->teammaster = NULL;
|
|
||||||
self->flags &= ~FL_TEAMSLAVE;
|
|
||||||
|
|
||||||
self->target_ent->owner = NULL;
|
|
||||||
self->target_ent->teammaster->owner = NULL;
|
|
||||||
|
|
||||||
infantry_die(self, inflictor, attacker, damage, point);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
turret_driver_think(edict_t *self)
|
|
||||||
{
|
|
||||||
vec3_t target;
|
|
||||||
vec3_t dir;
|
|
||||||
float reaction_time;
|
|
||||||
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->nextthink = level.time + FRAMETIME;
|
|
||||||
|
|
||||||
if (self->enemy && (!self->enemy->inuse || (self->enemy->health <= 0)))
|
|
||||||
{
|
|
||||||
self->enemy = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!self->enemy)
|
|
||||||
{
|
|
||||||
if (!FindTarget(self))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->monsterinfo.trail_time = level.time;
|
|
||||||
self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (visible(self, self->enemy))
|
|
||||||
{
|
|
||||||
if (self->monsterinfo.aiflags & AI_LOST_SIGHT)
|
|
||||||
{
|
|
||||||
self->monsterinfo.trail_time = level.time;
|
|
||||||
self->monsterinfo.aiflags &= ~AI_LOST_SIGHT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
self->monsterinfo.aiflags |= AI_LOST_SIGHT;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* let the turret know where we want it to aim */
|
|
||||||
VectorCopy(self->enemy->s.origin, target);
|
|
||||||
target[2] += self->enemy->viewheight;
|
|
||||||
VectorSubtract(target, self->target_ent->s.origin, dir);
|
|
||||||
vectoangles(dir, self->target_ent->move_angles);
|
|
||||||
|
|
||||||
/* decide if we should shoot */
|
|
||||||
if (level.time < self->monsterinfo.attack_finished)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
reaction_time = (3 - skill->value) * 1.0;
|
|
||||||
|
|
||||||
if ((level.time - self->monsterinfo.trail_time) < reaction_time)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->monsterinfo.attack_finished = level.time + reaction_time + 1.0;
|
|
||||||
self->target_ent->spawnflags |= 65536;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
turret_driver_link(edict_t *self)
|
|
||||||
{
|
|
||||||
vec3_t vec;
|
|
||||||
edict_t *ent;
|
|
||||||
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->think = turret_driver_think;
|
|
||||||
self->nextthink = level.time + FRAMETIME;
|
|
||||||
|
|
||||||
self->target_ent = G_PickTarget(self->target);
|
|
||||||
self->target_ent->owner = self;
|
|
||||||
self->target_ent->teammaster->owner = self;
|
|
||||||
VectorCopy(self->target_ent->s.angles, self->s.angles);
|
|
||||||
|
|
||||||
vec[0] = self->target_ent->s.origin[0] - self->s.origin[0];
|
|
||||||
vec[1] = self->target_ent->s.origin[1] - self->s.origin[1];
|
|
||||||
vec[2] = 0;
|
|
||||||
self->move_origin[0] = VectorLength(vec);
|
|
||||||
|
|
||||||
VectorSubtract(self->s.origin, self->target_ent->s.origin, vec);
|
|
||||||
vectoangles(vec, vec);
|
|
||||||
AnglesNormalize(vec);
|
|
||||||
self->move_origin[1] = vec[1];
|
|
||||||
|
|
||||||
self->move_origin[2] = self->s.origin[2] - self->target_ent->s.origin[2];
|
|
||||||
|
|
||||||
/* add the driver to the end of them team chain */
|
|
||||||
for (ent = self->target_ent->teammaster;
|
|
||||||
ent->teamchain;
|
|
||||||
ent = ent->teamchain)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ent->teamchain = self;
|
|
||||||
self->teammaster = self->target_ent->teammaster;
|
|
||||||
self->flags |= FL_TEAMSLAVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SP_turret_driver(edict_t *self)
|
|
||||||
{
|
|
||||||
if (!self)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deathmatch->value)
|
|
||||||
{
|
|
||||||
G_FreeEdict(self);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->movetype = MOVETYPE_PUSH;
|
|
||||||
self->solid = SOLID_BBOX;
|
|
||||||
self->s.modelindex = gi.modelindex("models/monsters/infantry/tris.md2");
|
|
||||||
VectorSet(self->mins, -16, -16, -24);
|
|
||||||
VectorSet(self->maxs, 16, 16, 32);
|
|
||||||
|
|
||||||
self->health = 100;
|
|
||||||
self->gib_health = 0;
|
|
||||||
self->mass = 200;
|
|
||||||
self->viewheight = 24;
|
|
||||||
|
|
||||||
self->die = turret_driver_die;
|
|
||||||
self->monsterinfo.stand = infantry_stand;
|
|
||||||
|
|
||||||
self->flags |= FL_NO_KNOCKBACK;
|
|
||||||
|
|
||||||
level.total_monsters++;
|
|
||||||
|
|
||||||
self->svflags |= SVF_MONSTER;
|
|
||||||
self->s.renderfx |= RF_FRAMELERP;
|
|
||||||
self->takedamage = DAMAGE_AIM;
|
|
||||||
self->use = monster_use;
|
|
||||||
self->clipmask = MASK_MONSTERSOLID;
|
|
||||||
VectorCopy(self->s.origin, self->s.old_origin);
|
|
||||||
self->monsterinfo.aiflags |= AI_STAND_GROUND | AI_DUCKED;
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self->think = turret_driver_link;
|
|
||||||
self->nextthink = level.time + FRAMETIME;
|
|
||||||
|
|
||||||
gi.linkentity(self);
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue