Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

p_hud.c (12529B)


      1 /*
      2 Copyright (C) 1997-2001 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 #include "g_local.h"
     21 
     22 
     23 
     24 /*
     25 ======================================================================
     26 
     27 INTERMISSION
     28 
     29 ======================================================================
     30 */
     31 
     32 void MoveClientToIntermission (edict_t *ent)
     33 {
     34 	if (deathmatch->value || coop->value)
     35 		ent->client->showscores = true;
     36 	VectorCopy (level.intermission_origin, ent->s.origin);
     37 	ent->client->ps.pmove.origin[0] = level.intermission_origin[0]*8;
     38 	ent->client->ps.pmove.origin[1] = level.intermission_origin[1]*8;
     39 	ent->client->ps.pmove.origin[2] = level.intermission_origin[2]*8;
     40 	VectorCopy (level.intermission_angle, ent->client->ps.viewangles);
     41 	ent->client->ps.pmove.pm_type = PM_FREEZE;
     42 	ent->client->ps.gunindex = 0;
     43 	ent->client->ps.blend[3] = 0;
     44 	ent->client->ps.rdflags &= ~RDF_UNDERWATER;
     45 
     46 	// clean up powerup info
     47 	ent->client->quad_framenum = 0;
     48 	ent->client->invincible_framenum = 0;
     49 	ent->client->breather_framenum = 0;
     50 	ent->client->enviro_framenum = 0;
     51 	ent->client->grenade_blew_up = false;
     52 	ent->client->grenade_time = 0;
     53 
     54 	ent->viewheight = 0;
     55 	ent->s.modelindex = 0;
     56 	ent->s.modelindex2 = 0;
     57 	ent->s.modelindex3 = 0;
     58 	ent->s.modelindex = 0;
     59 	ent->s.effects = 0;
     60 	ent->s.sound = 0;
     61 	ent->solid = SOLID_NOT;
     62 
     63 	// add the layout
     64 
     65 	if (deathmatch->value || coop->value)
     66 	{
     67 		DeathmatchScoreboardMessage (ent, NULL);
     68 		gi.unicast (ent, true);
     69 	}
     70 
     71 }
     72 
     73 void BeginIntermission (edict_t *targ)
     74 {
     75 	int		i, n;
     76 	edict_t	*ent, *client;
     77 
     78 	if (level.intermissiontime)
     79 		return;		// allready activated
     80 
     81 //ZOID
     82 	if (deathmatch->value && ctf->value)
     83 		CTFCalcScores();
     84 //ZOID
     85 
     86 	game.autosaved = false;
     87 
     88 	// respawn any dead clients
     89 	for (i=0 ; i<maxclients->value ; i++)
     90 	{
     91 		client = g_edicts + 1 + i;
     92 		if (!client->inuse)
     93 			continue;
     94 		if (client->health <= 0)
     95 			respawn(client);
     96 	}
     97 
     98 	level.intermissiontime = level.time;
     99 	level.changemap = targ->map;
    100 
    101 	if (strstr(level.changemap, "*"))
    102 	{
    103 		if (coop->value)
    104 		{
    105 			for (i=0 ; i<maxclients->value ; i++)
    106 			{
    107 				client = g_edicts + 1 + i;
    108 				if (!client->inuse)
    109 					continue;
    110 				// strip players of all keys between units
    111 				for (n = 0; n < MAX_ITEMS; n++)
    112 				{
    113 					if (itemlist[n].flags & IT_KEY)
    114 						client->client->pers.inventory[n] = 0;
    115 				}
    116 			}
    117 		}
    118 	}
    119 	else
    120 	{
    121 		if (!deathmatch->value)
    122 		{
    123 			level.exitintermission = 1;		// go immediately to the next level
    124 			return;
    125 		}
    126 	}
    127 
    128 	level.exitintermission = 0;
    129 
    130 	// find an intermission spot
    131 	ent = G_Find (NULL, FOFS(classname), "info_player_intermission");
    132 	if (!ent)
    133 	{	// the map creator forgot to put in an intermission point...
    134 		ent = G_Find (NULL, FOFS(classname), "info_player_start");
    135 		if (!ent)
    136 			ent = G_Find (NULL, FOFS(classname), "info_player_deathmatch");
    137 	}
    138 	else
    139 	{	// chose one of four spots
    140 		i = rand() & 3;
    141 		while (i--)
    142 		{
    143 			ent = G_Find (ent, FOFS(classname), "info_player_intermission");
    144 			if (!ent)	// wrap around the list
    145 				ent = G_Find (ent, FOFS(classname), "info_player_intermission");
    146 		}
    147 	}
    148 
    149 	VectorCopy (ent->s.origin, level.intermission_origin);
    150 	VectorCopy (ent->s.angles, level.intermission_angle);
    151 
    152 	// move all clients to the intermission point
    153 	for (i=0 ; i<maxclients->value ; i++)
    154 	{
    155 		client = g_edicts + 1 + i;
    156 		if (!client->inuse)
    157 			continue;
    158 		MoveClientToIntermission (client);
    159 	}
    160 }
    161 
    162 
    163 /*
    164 ==================
    165 DeathmatchScoreboardMessage
    166 
    167 ==================
    168 */
    169 void DeathmatchScoreboardMessage (edict_t *ent, edict_t *killer)
    170 {
    171 	char	entry[1024];
    172 	char	string[1400];
    173 	int		stringlength;
    174 	int		i, j, k;
    175 	int		sorted[MAX_CLIENTS];
    176 	int		sortedscores[MAX_CLIENTS];
    177 	int		score, total;
    178 	int		picnum;
    179 	int		x, y;
    180 	gclient_t	*cl;
    181 	edict_t		*cl_ent;
    182 	char	*tag;
    183 
    184 //ZOID
    185 	if (ctf->value) {
    186 		CTFScoreboardMessage (ent, killer);
    187 		return;
    188 	}
    189 //ZOID
    190 
    191 	// sort the clients by score
    192 	total = 0;
    193 	for (i=0 ; i<game.maxclients ; i++)
    194 	{
    195 		cl_ent = g_edicts + 1 + i;
    196 		if (!cl_ent->inuse)
    197 			continue;
    198 		score = game.clients[i].resp.score;
    199 		for (j=0 ; j<total ; j++)
    200 		{
    201 			if (score > sortedscores[j])
    202 				break;
    203 		}
    204 		for (k=total ; k>j ; k--)
    205 		{
    206 			sorted[k] = sorted[k-1];
    207 			sortedscores[k] = sortedscores[k-1];
    208 		}
    209 		sorted[j] = i;
    210 		sortedscores[j] = score;
    211 		total++;
    212 	}
    213 
    214 	// print level name and exit rules
    215 	string[0] = 0;
    216 
    217 	stringlength = strlen(string);
    218 
    219 	// add the clients in sorted order
    220 	if (total > 12)
    221 		total = 12;
    222 
    223 	for (i=0 ; i<total ; i++)
    224 	{
    225 		cl = &game.clients[sorted[i]];
    226 		cl_ent = g_edicts + 1 + sorted[i];
    227 
    228 		picnum = gi.imageindex ("i_fixme");
    229 		x = (i>=6) ? 160 : 0;
    230 		y = 32 + 32 * (i%6);
    231 
    232 		// add a dogtag
    233 		if (cl_ent == ent)
    234 			tag = "tag1";
    235 		else if (cl_ent == killer)
    236 			tag = "tag2";
    237 		else
    238 			tag = NULL;
    239 		if (tag)
    240 		{
    241 			Com_sprintf (entry, sizeof(entry),
    242 				"xv %i yv %i picn %s ",x+32, y, tag);
    243 			j = strlen(entry);
    244 			if (stringlength + j > 1024)
    245 				break;
    246 			strcpy (string + stringlength, entry);
    247 			stringlength += j;
    248 		}
    249 
    250 		// send the layout
    251 		Com_sprintf (entry, sizeof(entry),
    252 			"client %i %i %i %i %i %i ",
    253 			x, y, sorted[i], cl->resp.score, cl->ping, (level.framenum - cl->resp.enterframe)/600);
    254 		j = strlen(entry);
    255 		if (stringlength + j > 1024)
    256 			break;
    257 		strcpy (string + stringlength, entry);
    258 		stringlength += j;
    259 	}
    260 
    261 	gi.WriteByte (svc_layout);
    262 	gi.WriteString (string);
    263 }
    264 
    265 
    266 /*
    267 ==================
    268 DeathmatchScoreboard
    269 
    270 Draw instead of help message.
    271 Note that it isn't that hard to overflow the 1400 byte message limit!
    272 ==================
    273 */
    274 void DeathmatchScoreboard (edict_t *ent)
    275 {
    276 	DeathmatchScoreboardMessage (ent, ent->enemy);
    277 	gi.unicast (ent, true);
    278 }
    279 
    280 
    281 /*
    282 ==================
    283 Cmd_Score_f
    284 
    285 Display the scoreboard
    286 ==================
    287 */
    288 void Cmd_Score_f (edict_t *ent)
    289 {
    290 	ent->client->showinventory = false;
    291 	ent->client->showhelp = false;
    292 //ZOID
    293 	if (ent->client->menu)
    294 		PMenu_Close(ent);
    295 //ZOID
    296 
    297 	if (!deathmatch->value && !coop->value)
    298 		return;
    299 
    300 	if (ent->client->showscores)
    301 	{
    302 		ent->client->showscores = false;
    303 		ent->client->update_chase = true;
    304 		return;
    305 	}
    306 
    307 	ent->client->showscores = true;
    308 
    309 	DeathmatchScoreboard (ent);
    310 }
    311 
    312 
    313 /*
    314 ==================
    315 HelpComputer
    316 
    317 Draw help computer.
    318 ==================
    319 */
    320 void HelpComputer (edict_t *ent)
    321 {
    322 	char	string[1024];
    323 	char	*sk;
    324 
    325 	if (skill->value == 0)
    326 		sk = "easy";
    327 	else if (skill->value == 1)
    328 		sk = "medium";
    329 	else if (skill->value == 2)
    330 		sk = "hard";
    331 	else
    332 		sk = "hard+";
    333 
    334 	// send the layout
    335 	Com_sprintf (string, sizeof(string),
    336 		"xv 32 yv 8 picn help "			// background
    337 		"xv 202 yv 12 string2 \"%s\" "		// skill
    338 		"xv 0 yv 24 cstring2 \"%s\" "		// level name
    339 		"xv 0 yv 54 cstring2 \"%s\" "		// help 1
    340 		"xv 0 yv 110 cstring2 \"%s\" "		// help 2
    341 		"xv 50 yv 164 string2 \" kills     goals    secrets\" "
    342 		"xv 50 yv 172 string2 \"%3i/%3i     %i/%i       %i/%i\" ", 
    343 		sk,
    344 		level.level_name,
    345 		game.helpmessage1,
    346 		game.helpmessage2,
    347 		level.killed_monsters, level.total_monsters, 
    348 		level.found_goals, level.total_goals,
    349 		level.found_secrets, level.total_secrets);
    350 
    351 	gi.WriteByte (svc_layout);
    352 	gi.WriteString (string);
    353 	gi.unicast (ent, true);
    354 }
    355 
    356 
    357 /*
    358 ==================
    359 Cmd_Help_f
    360 
    361 Display the current help message
    362 ==================
    363 */
    364 void Cmd_Help_f (edict_t *ent)
    365 {
    366 	// this is for backwards compatability
    367 	if (deathmatch->value)
    368 	{
    369 		Cmd_Score_f (ent);
    370 		return;
    371 	}
    372 
    373 	ent->client->showinventory = false;
    374 	ent->client->showscores = false;
    375 
    376 	if (ent->client->showhelp && (ent->client->resp.game_helpchanged == game.helpchanged))
    377 	{
    378 		ent->client->showhelp = false;
    379 		return;
    380 	}
    381 
    382 	ent->client->showhelp = true;
    383 	ent->client->resp.helpchanged = 0;
    384 	HelpComputer (ent);
    385 }
    386 
    387 
    388 //=======================================================================
    389 
    390 /*
    391 ===============
    392 G_SetStats
    393 ===============
    394 */
    395 void G_SetStats (edict_t *ent)
    396 {
    397 	gitem_t		*item;
    398 	int			index, cells;
    399 	int			power_armor_type;
    400 
    401 	//
    402 	// health
    403 	//
    404 	ent->client->ps.stats[STAT_HEALTH_ICON] = level.pic_health;
    405 	ent->client->ps.stats[STAT_HEALTH] = ent->health;
    406 
    407 	//
    408 	// ammo
    409 	//
    410 	if (!ent->client->ammo_index /* || !ent->client->pers.inventory[ent->client->ammo_index] */)
    411 	{
    412 		ent->client->ps.stats[STAT_AMMO_ICON] = 0;
    413 		ent->client->ps.stats[STAT_AMMO] = 0;
    414 	}
    415 	else
    416 	{
    417 		item = &itemlist[ent->client->ammo_index];
    418 		ent->client->ps.stats[STAT_AMMO_ICON] = gi.imageindex (item->icon);
    419 		ent->client->ps.stats[STAT_AMMO] = ent->client->pers.inventory[ent->client->ammo_index];
    420 	}
    421 	
    422 	//
    423 	// armor
    424 	//
    425 	power_armor_type = PowerArmorType (ent);
    426 	if (power_armor_type)
    427 	{
    428 		cells = ent->client->pers.inventory[ITEM_INDEX(FindItem ("cells"))];
    429 		if (cells == 0)
    430 		{	// ran out of cells for power armor
    431 			ent->flags &= ~FL_POWER_ARMOR;
    432 			gi.sound(ent, CHAN_ITEM, gi.soundindex("misc/power2.wav"), 1, ATTN_NORM, 0);
    433 			power_armor_type = 0;;
    434 		}
    435 	}
    436 
    437 	index = ArmorIndex (ent);
    438 	if (power_armor_type && (!index || (level.framenum & 8) ) )
    439 	{	// flash between power armor and other armor icon
    440 		ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex ("i_powershield");
    441 		ent->client->ps.stats[STAT_ARMOR] = cells;
    442 	}
    443 	else if (index)
    444 	{
    445 		item = GetItemByIndex (index);
    446 		ent->client->ps.stats[STAT_ARMOR_ICON] = gi.imageindex (item->icon);
    447 		ent->client->ps.stats[STAT_ARMOR] = ent->client->pers.inventory[index];
    448 	}
    449 	else
    450 	{
    451 		ent->client->ps.stats[STAT_ARMOR_ICON] = 0;
    452 		ent->client->ps.stats[STAT_ARMOR] = 0;
    453 	}
    454 
    455 	//
    456 	// pickup message
    457 	//
    458 	if (level.time > ent->client->pickup_msg_time)
    459 	{
    460 		ent->client->ps.stats[STAT_PICKUP_ICON] = 0;
    461 		ent->client->ps.stats[STAT_PICKUP_STRING] = 0;
    462 	}
    463 
    464 	//
    465 	// timers
    466 	//
    467 	if (ent->client->quad_framenum > level.framenum)
    468 	{
    469 		ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_quad");
    470 		ent->client->ps.stats[STAT_TIMER] = (ent->client->quad_framenum - level.framenum)/10;
    471 	}
    472 	else if (ent->client->invincible_framenum > level.framenum)
    473 	{
    474 		ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_invulnerability");
    475 		ent->client->ps.stats[STAT_TIMER] = (ent->client->invincible_framenum - level.framenum)/10;
    476 	}
    477 	else if (ent->client->enviro_framenum > level.framenum)
    478 	{
    479 		ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_envirosuit");
    480 		ent->client->ps.stats[STAT_TIMER] = (ent->client->enviro_framenum - level.framenum)/10;
    481 	}
    482 	else if (ent->client->breather_framenum > level.framenum)
    483 	{
    484 		ent->client->ps.stats[STAT_TIMER_ICON] = gi.imageindex ("p_rebreather");
    485 		ent->client->ps.stats[STAT_TIMER] = (ent->client->breather_framenum - level.framenum)/10;
    486 	}
    487 	else
    488 	{
    489 		ent->client->ps.stats[STAT_TIMER_ICON] = 0;
    490 		ent->client->ps.stats[STAT_TIMER] = 0;
    491 	}
    492 
    493 	//
    494 	// selected item
    495 	//
    496 	if (ent->client->pers.selected_item == -1)
    497 		ent->client->ps.stats[STAT_SELECTED_ICON] = 0;
    498 	else
    499 		ent->client->ps.stats[STAT_SELECTED_ICON] = gi.imageindex (itemlist[ent->client->pers.selected_item].icon);
    500 
    501 	ent->client->ps.stats[STAT_SELECTED_ITEM] = ent->client->pers.selected_item;
    502 
    503 	//
    504 	// layouts
    505 	//
    506 	ent->client->ps.stats[STAT_LAYOUTS] = 0;
    507 
    508 	if (deathmatch->value)
    509 	{
    510 		if (ent->client->pers.health <= 0 || level.intermissiontime
    511 			|| ent->client->showscores)
    512 			ent->client->ps.stats[STAT_LAYOUTS] |= 1;
    513 		if (ent->client->showinventory && ent->client->pers.health > 0)
    514 			ent->client->ps.stats[STAT_LAYOUTS] |= 2;
    515 	}
    516 	else
    517 	{
    518 		if (ent->client->showscores || ent->client->showhelp)
    519 			ent->client->ps.stats[STAT_LAYOUTS] |= 1;
    520 		if (ent->client->showinventory && ent->client->pers.health > 0)
    521 			ent->client->ps.stats[STAT_LAYOUTS] |= 2;
    522 	}
    523 
    524 	//
    525 	// frags
    526 	//
    527 	ent->client->ps.stats[STAT_FRAGS] = ent->client->resp.score;
    528 
    529 	//
    530 	// help icon / current weapon if not shown
    531 	//
    532 	if (ent->client->resp.helpchanged && (level.framenum&8) )
    533 		ent->client->ps.stats[STAT_HELPICON] = gi.imageindex ("i_help");
    534 	else if ( (ent->client->pers.hand == CENTER_HANDED || ent->client->ps.fov > 91)
    535 		&& ent->client->pers.weapon)
    536 		ent->client->ps.stats[STAT_HELPICON] = gi.imageindex (ent->client->pers.weapon->icon);
    537 	else
    538 		ent->client->ps.stats[STAT_HELPICON] = 0;
    539 
    540 //ZOID
    541 	SetCTFStats(ent);
    542 //ZOID
    543 }
    544