mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-11-19 10:51:26 +00:00
bf4e17703d
- Changed the definition of several typedef'd structs so that they are properly named. - Limited DEHSUPP lump lookup to search zdoom.pk3 only. It will no longer be possible to load DEHSUPP lumps from user WADs. - Brought back the text-based DEHSUPP parser and changed it to be able to reference states by label. Also changed label names of DoomUnusedStates and added proper labels to all states that were previously forced to be the first state of an actor so that the old (limited) method could access them. This was done to address the following bug: - Fixed: The player's death states calling A_PlayerSkinCheck should not be part of the state set that is accessible by Dehacked. These will produce error messages when mapped to non-players. SVN r1512 (trunk)
2021 lines
47 KiB
C++
2021 lines
47 KiB
C++
// Emacs style mode select -*- C++ -*-
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// $Id:$
|
|
//
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
//
|
|
// This source is available for distribution and/or modification
|
|
// only under the terms of the DOOM Source Code License as
|
|
// published by id Software. All rights reserved.
|
|
//
|
|
// The source is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
|
|
// for more details.
|
|
//
|
|
// $Log:$
|
|
//
|
|
// DESCRIPTION:
|
|
// Intermission screens.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Enhancements by Graf Zahl
|
|
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
|
|
#include "m_random.h"
|
|
#include "m_swap.h"
|
|
#include "i_system.h"
|
|
#include "w_wad.h"
|
|
#include "g_game.h"
|
|
#include "g_level.h"
|
|
#include "r_local.h"
|
|
#include "s_sound.h"
|
|
#include "doomstat.h"
|
|
#include "v_video.h"
|
|
#include "i_video.h"
|
|
#include "wi_stuff.h"
|
|
#include "c_console.h"
|
|
#include "hu_stuff.h"
|
|
#include "v_palette.h"
|
|
#include "s_sndseq.h"
|
|
#include "sc_man.h"
|
|
#include "v_text.h"
|
|
#include "gi.h"
|
|
#include "r_translate.h"
|
|
#include "templates.h"
|
|
|
|
// States for the intermission
|
|
typedef enum
|
|
{
|
|
NoState = -1,
|
|
StatCount,
|
|
ShowNextLoc,
|
|
LeavingIntermission
|
|
} stateenum_t;
|
|
|
|
CVAR (Bool, wi_percents, true, CVAR_ARCHIVE)
|
|
CVAR (Bool, wi_showtotaltime, true, CVAR_ARCHIVE)
|
|
CVAR (Bool, wi_noautostartmap, false, CVAR_ARCHIVE)
|
|
|
|
|
|
void WI_loadData ();
|
|
void WI_unloadData ();
|
|
|
|
#define NEXTSTAGE (gameinfo.gametype & GAME_DoomChex ? "weapons/rocklx" : "doors/dr1_clos")
|
|
#define PASTSTATS (gameinfo.gametype & GAME_DoomChex ? "weapons/shotgr" : "plats/pt1_stop")
|
|
|
|
// GLOBAL LOCATIONS
|
|
#define WI_TITLEY 2
|
|
#define WI_SPACINGY 33
|
|
|
|
// SINGPLE-PLAYER STUFF
|
|
#define SP_STATSX 50
|
|
#define SP_STATSY 50
|
|
|
|
#define SP_TIMEX 8
|
|
#define SP_TIMEY (200-32)
|
|
|
|
|
|
// NET GAME STUFF
|
|
#define NG_STATSY 50
|
|
#define NG_STATSX (32 + star->GetWidth()/2 + 32*!dofrags)
|
|
|
|
#define NG_SPACINGX 64
|
|
|
|
|
|
// DEATHMATCH STUFF
|
|
#define DM_MATRIXX 42
|
|
#define DM_MATRIXY 68
|
|
|
|
#define DM_SPACINGX 40
|
|
|
|
#define DM_TOTALSX 269
|
|
|
|
#define DM_KILLERSX 10
|
|
#define DM_KILLERSY 100
|
|
#define DM_VICTIMSX 5
|
|
#define DM_VICTIMSY 50
|
|
|
|
// These animation variables, structures, etc. are used for the
|
|
// DOOM/Ultimate DOOM intermission screen animations. This is
|
|
// totally different from any sprite or texture/flat animations
|
|
typedef enum
|
|
{
|
|
ANIM_ALWAYS, // determined by patch entry
|
|
ANIM_PIC, // continuous
|
|
|
|
// condition bitflags
|
|
ANIM_IFVISITED=8,
|
|
ANIM_IFNOTVISITED=16,
|
|
ANIM_IFENTERING=32,
|
|
ANIM_IFNOTENTERING=64,
|
|
ANIM_IFLEAVING=128,
|
|
ANIM_IFNOTLEAVING=256,
|
|
ANIM_IFTRAVELLING=512,
|
|
ANIM_IFNOTTRAVELLING=1024,
|
|
|
|
ANIM_TYPE=7,
|
|
ANIM_CONDITION=~7,
|
|
|
|
} animenum_t;
|
|
|
|
struct yahpt_t
|
|
{
|
|
int x, y;
|
|
};
|
|
|
|
struct lnode_t
|
|
{
|
|
int x; // x/y coordinate pair structure
|
|
int y;
|
|
char level[9];
|
|
} ;
|
|
|
|
|
|
#define FACEBACKOFS 4
|
|
|
|
|
|
//
|
|
// Animation.
|
|
// There is another anim_t used in p_spec.
|
|
// (which is why I have renamed this one!)
|
|
//
|
|
|
|
#define MAX_ANIMATION_FRAMES 20
|
|
struct in_anim_t
|
|
{
|
|
int type; // Made an int so I can use '|'
|
|
int period; // period in tics between animations
|
|
int nanims; // number of animation frames
|
|
yahpt_t loc; // location of animation
|
|
int data; // ALWAYS: n/a, RANDOM: period deviation (<256)
|
|
FTexture * p[MAX_ANIMATION_FRAMES]; // actual graphics for frames of animations
|
|
|
|
// following must be initialized to zero before use!
|
|
int nexttic; // next value of bcnt (used in conjunction with period)
|
|
int ctr; // next frame number to animate
|
|
int state; // used by RANDOM and LEVEL when animating
|
|
|
|
char levelname[9];
|
|
char levelname2[9];
|
|
};
|
|
|
|
static TArray<lnode_t> lnodes;
|
|
static TArray<in_anim_t> anims;
|
|
|
|
|
|
//
|
|
// GENERAL DATA
|
|
//
|
|
|
|
//
|
|
// Locally used stuff.
|
|
//
|
|
|
|
|
|
// States for single-player
|
|
#define SP_KILLS 0
|
|
#define SP_ITEMS 2
|
|
#define SP_SECRET 4
|
|
#define SP_FRAGS 6
|
|
#define SP_TIME 8
|
|
#define SP_PAR ST_TIME
|
|
|
|
#define SP_PAUSE 1
|
|
|
|
#define SHOWNEXTLOCDELAY 4 // in seconds
|
|
|
|
static int acceleratestage; // used to accelerate or skip a stage
|
|
static int me; // wbs->pnum
|
|
static stateenum_t state; // specifies current state
|
|
static wbstartstruct_t *wbs; // contains information passed into intermission
|
|
static wbplayerstruct_t*plrs; // wbs->plyr[]
|
|
static int cnt; // used for general timing
|
|
static int bcnt; // used for timing of background animation
|
|
static int cnt_kills[MAXPLAYERS];
|
|
static int cnt_items[MAXPLAYERS];
|
|
static int cnt_secret[MAXPLAYERS];
|
|
static int cnt_time;
|
|
static int cnt_total_time;
|
|
static int cnt_par;
|
|
static int cnt_pause;
|
|
static bool noautostartmap;
|
|
|
|
//
|
|
// GRAPHICS
|
|
//
|
|
|
|
static TArray<FTexture *> yah; // You Are Here graphic
|
|
static FTexture* splat; // splat
|
|
static FTexture* finished; // "Finished!" graphics
|
|
static FTexture* entering; // "Entering" graphic
|
|
static FTexture* sp_secret; // "secret"
|
|
static FTexture* kills; // "Kills", "Scrt", "Items", "Frags"
|
|
static FTexture* secret;
|
|
static FTexture* items;
|
|
static FTexture* frags;
|
|
static FTexture* timepic; // Time sucks.
|
|
static FTexture* par;
|
|
static FTexture* sucks;
|
|
static FTexture* killers; // "killers", "victims"
|
|
static FTexture* victims;
|
|
static FTexture* total; // "Total", your face, your dead face
|
|
static FTexture* star;
|
|
static FTexture* bstar;
|
|
static FTexture* p; // Player graphic
|
|
static FTexture* lnames[2]; // Name graphics of each level (centered)
|
|
|
|
// [RH] Info to dynamically generate the level name graphics
|
|
static FString lnametexts[2];
|
|
|
|
static FTexture *background;
|
|
|
|
//
|
|
// CODE
|
|
//
|
|
|
|
// ====================================================================
|
|
//
|
|
// Background script commands
|
|
//
|
|
// ====================================================================
|
|
|
|
static const char *WI_Cmd[]={
|
|
"Background",
|
|
"Splat",
|
|
"Pointer",
|
|
"Spots",
|
|
|
|
"IfEntering",
|
|
"IfNotEntering",
|
|
"IfVisited",
|
|
"IfNotVisited",
|
|
"IfLeaving",
|
|
"IfNotLeaving",
|
|
"IfTravelling",
|
|
"IfNotTravelling",
|
|
|
|
"Animation",
|
|
"Pic",
|
|
|
|
"NoAutostartMap",
|
|
|
|
NULL
|
|
};
|
|
|
|
//====================================================================
|
|
//
|
|
// Loads the background - either from a single texture
|
|
// or an intermission lump.
|
|
// Unfortunately the texture manager is incapable of recognizing text
|
|
// files so if you use a script you have to prefix its name by '$' in
|
|
// MAPINFO.
|
|
//
|
|
//====================================================================
|
|
static bool IsExMy(const char * name)
|
|
{
|
|
// Only check for the first 3 episodes. They are the only ones with default intermission scripts.
|
|
// Level names can be upper- and lower case so use tolower to check!
|
|
return (tolower(name[0])=='e' && name[1]>='1' && name[1]<='3' && tolower(name[2])=='m');
|
|
}
|
|
|
|
void WI_LoadBackground(bool isenterpic)
|
|
{
|
|
const char * lumpname = NULL;
|
|
char buffer[10];
|
|
in_anim_t an;
|
|
lnode_t pt;
|
|
FTextureID texture;
|
|
|
|
bcnt=0;
|
|
|
|
texture.SetInvalid();
|
|
if (isenterpic)
|
|
{
|
|
level_info_t * li = FindLevelInfo(wbs->next);
|
|
if (li != NULL) lumpname = li->EnterPic;
|
|
}
|
|
else
|
|
{
|
|
lumpname = level.info->ExitPic;
|
|
}
|
|
|
|
// Try to get a default if nothing specified
|
|
if (lumpname == NULL || lumpname[0]==0)
|
|
{
|
|
lumpname = NULL;
|
|
switch(gameinfo.gametype)
|
|
{
|
|
case GAME_Chex:
|
|
case GAME_Doom:
|
|
if (!(gameinfo.flags & GI_MAPxx))
|
|
{
|
|
const char *level = isenterpic ? wbs->next : wbs->current;
|
|
if (IsExMy(level))
|
|
{
|
|
mysnprintf(buffer, countof(buffer), "$IN_EPI%c", level[1]);
|
|
lumpname = buffer;
|
|
}
|
|
}
|
|
if (!lumpname)
|
|
{
|
|
if (isenterpic)
|
|
{
|
|
// One special case needs to be handled here!
|
|
// If going from E1-E3 to E4 the default should be used, not the exit pic.
|
|
|
|
// Not if the exit pic is user defined!
|
|
if (level.info->ExitPic.IsNotEmpty()) return;
|
|
|
|
// E1-E3 need special treatment when playing Doom 1.
|
|
if (!(gameinfo.flags & GI_MAPxx))
|
|
{
|
|
// not if the last level is not from the first 3 episodes
|
|
if (!IsExMy(wbs->current)) return;
|
|
|
|
// not if the next level is one of the first 3 episodes
|
|
if (IsExMy(wbs->next)) return;
|
|
}
|
|
}
|
|
lumpname = "INTERPIC";
|
|
}
|
|
break;
|
|
|
|
case GAME_Heretic:
|
|
if (isenterpic)
|
|
{
|
|
if (IsExMy(wbs->next))
|
|
{
|
|
mysnprintf(buffer, countof(buffer), "$IN_HTC%c", wbs->next[1]);
|
|
lumpname = buffer;
|
|
}
|
|
}
|
|
if (!lumpname)
|
|
{
|
|
if (isenterpic) return;
|
|
lumpname = "FLOOR16";
|
|
}
|
|
break;
|
|
|
|
case GAME_Hexen:
|
|
if (isenterpic) return;
|
|
lumpname = "INTERPIC";
|
|
break;
|
|
|
|
case GAME_Strife:
|
|
default:
|
|
// Strife doesn't have an intermission pic so choose something neutral.
|
|
if (isenterpic) return;
|
|
lumpname = gameinfo.borderFlat;
|
|
break;
|
|
}
|
|
}
|
|
if (lumpname == NULL)
|
|
{
|
|
// shouldn't happen!
|
|
background = NULL;
|
|
return;
|
|
}
|
|
|
|
lnodes.Clear();
|
|
anims.Clear();
|
|
yah.Clear();
|
|
splat = NULL;
|
|
|
|
// a name with a starting '$' indicates an intermission script
|
|
if (*lumpname!='$')
|
|
{
|
|
texture = TexMan.CheckForTexture(lumpname, FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny);
|
|
}
|
|
else
|
|
{
|
|
int lumpnum=Wads.CheckNumForFullName(lumpname+1, true);
|
|
if (lumpnum>=0)
|
|
{
|
|
FScanner sc(lumpnum);
|
|
while (sc.GetString())
|
|
{
|
|
memset(&an,0,sizeof(an));
|
|
int caseval = sc.MustMatchString(WI_Cmd);
|
|
switch(caseval)
|
|
{
|
|
case 0: // Background
|
|
sc.MustGetString();
|
|
texture = TexMan.CheckForTexture(sc.String, FTexture::TEX_MiscPatch, FTextureManager::TEXMAN_TryAny);
|
|
break;
|
|
|
|
case 1: // Splat
|
|
sc.MustGetString();
|
|
splat = TexMan[sc.String];
|
|
break;
|
|
|
|
case 2: // Pointers
|
|
while (sc.GetString() && !sc.Crossed)
|
|
{
|
|
yah.Push(TexMan[sc.String]);
|
|
}
|
|
if (sc.Crossed)
|
|
sc.UnGet();
|
|
break;
|
|
|
|
case 3: // Spots
|
|
sc.MustGetStringName("{");
|
|
while (!sc.CheckString("}"))
|
|
{
|
|
sc.MustGetString();
|
|
strncpy(pt.level, sc.String,8);
|
|
pt.level[8] = 0;
|
|
sc.MustGetNumber();
|
|
pt.x = sc.Number;
|
|
sc.MustGetNumber();
|
|
pt.y = sc.Number;
|
|
lnodes.Push(pt);
|
|
}
|
|
break;
|
|
|
|
case 4: // IfEntering
|
|
an.type = ANIM_IFENTERING;
|
|
goto readanimation;
|
|
|
|
case 5: // IfEntering
|
|
an.type = ANIM_IFNOTENTERING;
|
|
goto readanimation;
|
|
|
|
case 6: // IfVisited
|
|
an.type = ANIM_IFVISITED;
|
|
goto readanimation;
|
|
|
|
case 7: // IfNotVisited
|
|
an.type = ANIM_IFNOTVISITED;
|
|
goto readanimation;
|
|
|
|
case 8: // IfLeaving
|
|
an.type = ANIM_IFLEAVING;
|
|
goto readanimation;
|
|
|
|
case 9: // IfNotLeaving
|
|
an.type = ANIM_IFNOTLEAVING;
|
|
goto readanimation;
|
|
|
|
case 10: // IfTravelling
|
|
an.type = ANIM_IFTRAVELLING;
|
|
sc.MustGetString();
|
|
strncpy(an.levelname2, sc.String, 8);
|
|
an.levelname2[8] = 0;
|
|
goto readanimation;
|
|
|
|
case 11: // IfNotTravelling
|
|
an.type = ANIM_IFTRAVELLING;
|
|
sc.MustGetString();
|
|
strncpy(an.levelname2, sc.String, 8);
|
|
an.levelname2[8] = 0;
|
|
goto readanimation;
|
|
|
|
case 14: // NoAutostartMap
|
|
noautostartmap = true;
|
|
break;
|
|
|
|
readanimation:
|
|
sc.MustGetString();
|
|
strncpy(an.levelname, sc.String, 8);
|
|
an.levelname[8] = 0;
|
|
sc.MustGetString();
|
|
caseval=sc.MustMatchString(WI_Cmd);
|
|
|
|
default:
|
|
switch (caseval)
|
|
{
|
|
case 12: // Animation
|
|
an.type |= ANIM_ALWAYS;
|
|
sc.MustGetNumber();
|
|
an.loc.x = sc.Number;
|
|
sc.MustGetNumber();
|
|
an.loc.y = sc.Number;
|
|
sc.MustGetNumber();
|
|
an.period = sc.Number;
|
|
an.nexttic = 1 + (M_Random() % an.period);
|
|
if (sc.GetString())
|
|
{
|
|
if (sc.Compare("ONCE"))
|
|
{
|
|
an.data = 1;
|
|
}
|
|
else
|
|
{
|
|
sc.UnGet();
|
|
}
|
|
}
|
|
if (!sc.CheckString("{"))
|
|
{
|
|
sc.MustGetString();
|
|
an.p[an.nanims++] = TexMan[sc.String];
|
|
}
|
|
else
|
|
{
|
|
while (!sc.CheckString("}"))
|
|
{
|
|
sc.MustGetString();
|
|
if (an.nanims<MAX_ANIMATION_FRAMES)
|
|
an.p[an.nanims++] = TexMan[sc.String];
|
|
}
|
|
}
|
|
an.ctr = -1;
|
|
anims.Push(an);
|
|
break;
|
|
|
|
case 13: // Pic
|
|
an.type |= ANIM_PIC;
|
|
sc.MustGetNumber();
|
|
an.loc.x = sc.Number;
|
|
sc.MustGetNumber();
|
|
an.loc.y = sc.Number;
|
|
sc.MustGetString();
|
|
an.p[0] = TexMan[sc.String];
|
|
anims.Push(an);
|
|
break;
|
|
|
|
default:
|
|
sc.ScriptError("Unknown token %s in intermission script", sc.String);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Printf("Intermission script %s not found!\n", lumpname+1);
|
|
texture = TexMan.GetTexture("INTERPIC", FTexture::TEX_MiscPatch);
|
|
}
|
|
}
|
|
background=TexMan[texture];
|
|
}
|
|
|
|
//====================================================================
|
|
//
|
|
// made this more generic and configurable through a script
|
|
// Removed all the ugly special case handling for different game modes
|
|
//
|
|
//====================================================================
|
|
|
|
void WI_updateAnimatedBack()
|
|
{
|
|
unsigned int i;
|
|
|
|
for(i=0;i<anims.Size();i++)
|
|
{
|
|
in_anim_t * a = &anims[i];
|
|
switch (a->type & ANIM_TYPE)
|
|
{
|
|
case ANIM_ALWAYS:
|
|
if (bcnt >= a->nexttic)
|
|
{
|
|
if (++a->ctr >= a->nanims)
|
|
{
|
|
if (a->data==0) a->ctr = 0;
|
|
else a->ctr--;
|
|
}
|
|
a->nexttic = bcnt + a->period;
|
|
}
|
|
break;
|
|
|
|
case ANIM_PIC:
|
|
a->ctr = 0;
|
|
break;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
//====================================================================
|
|
//
|
|
// Draws the background including all animations
|
|
//
|
|
//====================================================================
|
|
|
|
void WI_drawBackground()
|
|
{
|
|
unsigned int i;
|
|
int animwidth=320; // For a flat fill or clear background scale animations to 320x200
|
|
int animheight=200;
|
|
|
|
if (background)
|
|
{
|
|
// background
|
|
if (background->UseType == FTexture::TEX_MiscPatch)
|
|
{
|
|
// scale all animations below to fit the size of the base pic
|
|
// The base pic is always scaled to fit the screen so this allows
|
|
// placing the animations precisely where they belong on the base pic
|
|
animwidth = background->GetWidth();
|
|
animheight = background->GetHeight();
|
|
screen->FillBorder (NULL);
|
|
screen->DrawTexture(background, 0, 0, DTA_VirtualWidth, animwidth,
|
|
DTA_VirtualHeight, animheight, TAG_DONE);
|
|
}
|
|
else
|
|
{
|
|
screen->FlatFill(0, 0, SCREENWIDTH, SCREENHEIGHT, background);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
screen->Clear(0,0, SCREENWIDTH, SCREENHEIGHT, 0, 0);
|
|
}
|
|
|
|
for(i=0;i<anims.Size();i++)
|
|
{
|
|
in_anim_t * a = &anims[i];
|
|
level_info_t * li;
|
|
|
|
switch (a->type & ANIM_CONDITION)
|
|
{
|
|
case ANIM_IFVISITED:
|
|
li = FindLevelInfo(a->levelname);
|
|
if (li == NULL || !(li->flags & LEVEL_VISITED)) continue;
|
|
break;
|
|
|
|
case ANIM_IFNOTVISITED:
|
|
li = FindLevelInfo(a->levelname);
|
|
if (li == NULL || (li->flags & LEVEL_VISITED)) continue;
|
|
break;
|
|
|
|
// StatCount means 'leaving' - everything else means 'entering'!
|
|
case ANIM_IFENTERING:
|
|
if (state == StatCount || strnicmp(a->levelname, wbs->next, 8)) continue;
|
|
break;
|
|
|
|
case ANIM_IFNOTENTERING:
|
|
if (state != StatCount && !strnicmp(a->levelname, wbs->next, 8)) continue;
|
|
break;
|
|
|
|
case ANIM_IFLEAVING:
|
|
if (state != StatCount || strnicmp(a->levelname, wbs->current, 8)) continue;
|
|
break;
|
|
|
|
case ANIM_IFNOTLEAVING:
|
|
if (state == StatCount && !strnicmp(a->levelname, wbs->current, 8)) continue;
|
|
break;
|
|
|
|
case ANIM_IFTRAVELLING:
|
|
if (strnicmp(a->levelname2, wbs->current, 8) || strnicmp(a->levelname, wbs->next, 8)) continue;
|
|
break;
|
|
|
|
case ANIM_IFNOTTRAVELLING:
|
|
if (!strnicmp(a->levelname2, wbs->current, 8) && !strnicmp(a->levelname, wbs->next, 8)) continue;
|
|
break;
|
|
}
|
|
if (a->ctr >= 0)
|
|
screen->DrawTexture(a->p[a->ctr], a->loc.x, a->loc.y,
|
|
DTA_VirtualWidth, animwidth, DTA_VirtualHeight, animheight, TAG_DONE);
|
|
}
|
|
}
|
|
|
|
|
|
//====================================================================
|
|
//
|
|
// Draws a single character with a shadow
|
|
//
|
|
//====================================================================
|
|
|
|
static int WI_DrawCharPatch (FFont *font, int charcode, int x, int y, EColorRange translation=CR_UNTRANSLATED, bool nomove=false)
|
|
{
|
|
int width;
|
|
screen->DrawTexture(font->GetChar(charcode, &width), x, y,
|
|
nomove ? DTA_CleanNoMove : DTA_Clean, true,
|
|
DTA_ShadowAlpha, (gameinfo.gametype & GAME_DoomChex) ? 0 : FRACUNIT/2,
|
|
DTA_Translation, font->GetColorTranslation(translation),
|
|
TAG_DONE);
|
|
return x - width;
|
|
}
|
|
|
|
|
|
//====================================================================
|
|
//
|
|
// Draws a level name with the big font
|
|
//
|
|
// x is no longer passed as a parameter because the text is now broken into several lines
|
|
// if it is too long
|
|
//
|
|
//====================================================================
|
|
|
|
int WI_DrawName(int y, const char *levelname)
|
|
{
|
|
int i;
|
|
size_t l;
|
|
const char *p;
|
|
int h = 0;
|
|
int lumph;
|
|
|
|
lumph = BigFont->GetHeight() * CleanYfac;
|
|
|
|
p = levelname;
|
|
if (!p) return 0;
|
|
l = strlen(p);
|
|
if (!l) return 0;
|
|
|
|
FBrokenLines *lines = V_BreakLines(BigFont, screen->GetWidth() / CleanXfac, p);
|
|
|
|
if (lines)
|
|
{
|
|
for (i = 0; lines[i].Width >= 0; i++)
|
|
{
|
|
screen->DrawText(BigFont, CR_UNTRANSLATED, (SCREENWIDTH - lines[i].Width * CleanXfac) / 2, y + h,
|
|
lines[i].Text, DTA_CleanNoMove, true, TAG_DONE);
|
|
h += lumph;
|
|
}
|
|
V_FreeBrokenLines(lines);
|
|
}
|
|
return h + lumph/4;
|
|
}
|
|
|
|
|
|
//====================================================================
|
|
//
|
|
// Draws "<Levelname> Finished!"
|
|
//
|
|
// Either uses the specified patch or the big font
|
|
// A level name patch can be specified for all games now, not just Doom.
|
|
//
|
|
//====================================================================
|
|
int WI_drawLF ()
|
|
{
|
|
int y = WI_TITLEY * CleanYfac;
|
|
int midx = screen->GetWidth() / 2;
|
|
|
|
FTexture *tex = wbs->LName0;
|
|
|
|
// draw <LevelName>
|
|
if (tex)
|
|
{
|
|
screen->DrawTexture(tex, midx - tex->GetWidth()*CleanXfac/2, y, DTA_CleanNoMove, true, TAG_DONE);
|
|
y += (tex->GetHeight() + BigFont->GetHeight()/4) * CleanYfac;
|
|
}
|
|
else
|
|
{
|
|
y += WI_DrawName(y, lnametexts[0]);
|
|
}
|
|
|
|
// draw "Finished!"
|
|
FFont *font = gameinfo.gametype & GAME_Raven ? SmallFont : BigFont;
|
|
if (y < (NG_STATSY - font->GetHeight()*3/4) * CleanYfac)
|
|
{
|
|
// don't draw 'finished' if the level name is too high!
|
|
if (gameinfo.gametype & GAME_DoomChex)
|
|
{
|
|
screen->DrawTexture(finished, midx - finished->GetWidth()*CleanXfac/2, y, DTA_CleanNoMove, true, TAG_DONE);
|
|
return y + finished->GetHeight() * CleanYfac;
|
|
}
|
|
else
|
|
{
|
|
screen->DrawText(font, CR_WHITE,
|
|
midx - font->StringWidth("finished")*CleanXfac/2, y - 4*CleanYfac, "finished",
|
|
DTA_CleanNoMove, true, TAG_DONE);
|
|
return y + font->GetHeight() * CleanYfac;
|
|
}
|
|
}
|
|
return y;
|
|
}
|
|
|
|
|
|
//====================================================================
|
|
//
|
|
// Draws "Entering <LevelName>"
|
|
//
|
|
// Either uses the specified patch or the big font
|
|
// A level name patch can be specified for all games now, not just Doom.
|
|
//
|
|
//====================================================================
|
|
void WI_drawEL ()
|
|
{
|
|
int y = WI_TITLEY * CleanYfac;
|
|
FFont *font = gameinfo.gametype & GAME_Raven ? SmallFont : BigFont;
|
|
|
|
// draw "entering"
|
|
// be careful with the added height so that it works for oversized 'entering' patches!
|
|
if (gameinfo.gametype & GAME_DoomChex)
|
|
{
|
|
screen->DrawTexture(entering, (SCREENWIDTH - entering->GetWidth() * CleanXfac) / 2, y, DTA_CleanNoMove, true, TAG_DONE);
|
|
y += (entering->GetHeight() + font->GetHeight()/4) * CleanYfac;
|
|
}
|
|
else
|
|
{
|
|
screen->DrawText(font, CR_WHITE,
|
|
(SCREENWIDTH - font->StringWidth("now entering:") * CleanXfac) / 2, y,
|
|
"now entering:", DTA_CleanNoMove, true, TAG_DONE);
|
|
y += font->GetHeight()*5*CleanYfac/4;
|
|
}
|
|
|
|
// draw <LevelName>
|
|
FTexture *tex = wbs->LName1;
|
|
if (tex)
|
|
{
|
|
screen->DrawTexture(tex, (SCREENWIDTH - tex->GetWidth() * CleanXfac) / 2, y, DTA_CleanNoMove, true, TAG_DONE);
|
|
}
|
|
else
|
|
{
|
|
WI_DrawName(y, lnametexts[1]);
|
|
}
|
|
}
|
|
|
|
|
|
//====================================================================
|
|
//
|
|
// Draws the splats and the 'You are here' arrows
|
|
//
|
|
//====================================================================
|
|
|
|
int WI_MapToIndex (const char *map)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < lnodes.Size(); i++)
|
|
{
|
|
if (!strnicmp (lnodes[i].level, map, 8))
|
|
break;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
//====================================================================
|
|
//
|
|
// Draws the splats and the 'You are here' arrows
|
|
//
|
|
//====================================================================
|
|
|
|
void WI_drawOnLnode( int n, FTexture * c[] ,int numc)
|
|
{
|
|
int i;
|
|
for(i=0;i<numc;i++)
|
|
{
|
|
int left;
|
|
int top;
|
|
int right;
|
|
int bottom;
|
|
|
|
|
|
right = c[i]->GetWidth();
|
|
bottom = c[i]->GetHeight();
|
|
left = lnodes[n].x - c[i]->LeftOffset;
|
|
top = lnodes[n].y - c[i]->TopOffset;
|
|
right += left;
|
|
bottom += top;
|
|
|
|
if (left >= 0 && right < 320 && top >= 0 && bottom < 200)
|
|
{
|
|
screen->DrawTexture (c[i], lnodes[n].x, lnodes[n].y, DTA_320x200, true, TAG_DONE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//====================================================================
|
|
//
|
|
// Draws a number.
|
|
// If digits > 0, then use that many digits minimum,
|
|
// otherwise only use as many as necessary.
|
|
// x is the right edge of the number.
|
|
// Returns new x position, that is, the left edge of the number.
|
|
//
|
|
//====================================================================
|
|
int WI_drawNum (FFont *font, int x, int y, int n, int digits, bool leadingzeros=true, EColorRange translation=CR_UNTRANSLATED)
|
|
{
|
|
int fontwidth = font->GetCharWidth('3');
|
|
char text[8];
|
|
int len;
|
|
char *text_p;
|
|
bool nomove = font != IntermissionFont;
|
|
|
|
if (nomove)
|
|
{
|
|
fontwidth *= CleanXfac;
|
|
}
|
|
if (leadingzeros)
|
|
{
|
|
len = mysnprintf (text, countof(text), "%0*d", digits, n);
|
|
}
|
|
else
|
|
{
|
|
len = mysnprintf (text, countof(text), "%d", n);
|
|
}
|
|
text_p = text + MIN<int>(len, countof(text)-1);
|
|
|
|
while (--text_p >= text)
|
|
{
|
|
// Digits are centered in a box the width of the '3' character.
|
|
// Other characters (specifically, '-') are right-aligned in their cell.
|
|
if (*text_p >= '0' && *text_p <= '9')
|
|
{
|
|
x -= fontwidth;
|
|
WI_DrawCharPatch(font, *text_p, x + (fontwidth - font->GetCharWidth(*text_p)) / 2, y, translation, nomove);
|
|
}
|
|
else
|
|
{
|
|
WI_DrawCharPatch(font, *text_p, x - font->GetCharWidth(*text_p), y, translation, nomove);
|
|
x -= fontwidth;
|
|
}
|
|
}
|
|
if (len < digits)
|
|
{
|
|
x -= fontwidth * (digits - len);
|
|
}
|
|
return x;
|
|
}
|
|
|
|
//====================================================================
|
|
//
|
|
//
|
|
//
|
|
//====================================================================
|
|
|
|
void WI_drawPercent (FFont *font, int x, int y, int p, int b, bool show_total=true, EColorRange color=CR_UNTRANSLATED)
|
|
{
|
|
if (p < 0)
|
|
return;
|
|
|
|
if (wi_percents)
|
|
{
|
|
if (font != IntermissionFont)
|
|
{
|
|
x -= font->GetCharWidth('%') * CleanXfac;
|
|
}
|
|
else
|
|
{
|
|
x -= font->GetCharWidth('%');
|
|
}
|
|
screen->DrawText(font, color, x, y, "%", font != IntermissionFont ? DTA_CleanNoMove : DTA_Clean, true, TAG_DONE);
|
|
if (font != IntermissionFont)
|
|
{
|
|
x -= 2*CleanXfac;
|
|
}
|
|
WI_drawNum(font, x, y, b == 0 ? 100 : p * 100 / b, -1, false, color);
|
|
}
|
|
else
|
|
{
|
|
if (show_total)
|
|
{
|
|
x = WI_drawNum(font, x, y, b, 2, false);
|
|
x -= font->GetCharWidth('/');
|
|
screen->DrawText (IntermissionFont, color, x, y, "/",
|
|
DTA_Clean, true, TAG_DONE);
|
|
}
|
|
WI_drawNum (font, x, y, p, -1, false, color);
|
|
}
|
|
}
|
|
|
|
//====================================================================
|
|
//
|
|
// Display level completion time and par, or "sucks" message if overflow.
|
|
//
|
|
//====================================================================
|
|
void WI_drawTime (int x, int y, int t, bool no_sucks=false)
|
|
{
|
|
bool sucky;
|
|
|
|
if (t<0)
|
|
return;
|
|
|
|
sucky = !no_sucks && t >= wbs->sucktime * 60 * 60 && wbs->sucktime > 0;
|
|
|
|
if (sucky)
|
|
{ // "sucks"
|
|
if (sucks != NULL)
|
|
{
|
|
screen->DrawTexture (sucks, x - sucks->GetWidth(), y - IntermissionFont->GetHeight() - 2,
|
|
DTA_Clean, true, TAG_DONE);
|
|
}
|
|
else
|
|
{
|
|
screen->DrawText (BigFont, CR_UNTRANSLATED, x - BigFont->StringWidth("SUCKS"), y - IntermissionFont->GetHeight() - 2,
|
|
"SUCKS", DTA_Clean, true, TAG_DONE);
|
|
}
|
|
}
|
|
|
|
int hours = t / 3600;
|
|
t -= hours * 3600;
|
|
int minutes = t / 60;
|
|
t -= minutes * 60;
|
|
int seconds = t;
|
|
|
|
// Why were these offsets hard coded? Half the WADs with custom patches
|
|
// I tested screwed up miserably in this function!
|
|
int num_spacing = IntermissionFont->GetCharWidth('3');
|
|
int colon_spacing = IntermissionFont->GetCharWidth(':');
|
|
|
|
x = WI_drawNum (IntermissionFont, x, y, seconds, 2) - 1;
|
|
WI_DrawCharPatch (IntermissionFont, ':', x -= colon_spacing, y);
|
|
x = WI_drawNum (IntermissionFont, x, y, minutes, 2, hours!=0);
|
|
if (hours)
|
|
{
|
|
WI_DrawCharPatch (IntermissionFont, ':', x -= colon_spacing, y);
|
|
WI_drawNum (IntermissionFont, x, y, hours, 2);
|
|
}
|
|
}
|
|
|
|
void WI_End ()
|
|
{
|
|
state = LeavingIntermission;
|
|
WI_unloadData ();
|
|
|
|
//Added by mc
|
|
if (deathmatch)
|
|
{
|
|
bglobal.RemoveAllBots (consoleplayer != Net_Arbitrator);
|
|
}
|
|
}
|
|
|
|
void WI_initNoState ()
|
|
{
|
|
state = NoState;
|
|
acceleratestage = 0;
|
|
cnt = 10;
|
|
}
|
|
|
|
void WI_updateNoState ()
|
|
{
|
|
WI_updateAnimatedBack();
|
|
|
|
|
|
if (!wi_noautostartmap && !noautostartmap) cnt--;
|
|
if (acceleratestage) cnt=0;
|
|
|
|
if (cnt==0)
|
|
{
|
|
WI_End();
|
|
G_WorldDone();
|
|
}
|
|
}
|
|
|
|
static bool snl_pointeron = false;
|
|
|
|
void WI_initShowNextLoc ()
|
|
{
|
|
if (wbs->next_ep == -1)
|
|
{
|
|
// Last map in episode - there is no next location!
|
|
WI_End();
|
|
G_WorldDone();
|
|
return;
|
|
}
|
|
|
|
state = ShowNextLoc;
|
|
acceleratestage = 0;
|
|
cnt = SHOWNEXTLOCDELAY * TICRATE;
|
|
WI_LoadBackground(true);
|
|
}
|
|
|
|
void WI_updateShowNextLoc ()
|
|
{
|
|
WI_updateAnimatedBack();
|
|
|
|
if (!--cnt || acceleratestage)
|
|
WI_initNoState();
|
|
else
|
|
snl_pointeron = (cnt & 31) < 20;
|
|
}
|
|
|
|
void WI_drawShowNextLoc(void)
|
|
{
|
|
unsigned int i;
|
|
|
|
WI_drawBackground();
|
|
|
|
if (splat)
|
|
{
|
|
for (i=0 ; i<lnodes.Size() ; i++)
|
|
{
|
|
level_info_t * li = FindLevelInfo (lnodes[i].level);
|
|
if (li && li->flags & LEVEL_VISITED) WI_drawOnLnode(i, &splat,1); // draw a splat on taken cities.
|
|
}
|
|
}
|
|
|
|
// draw flashing ptr
|
|
if (snl_pointeron && yah.Size())
|
|
{
|
|
unsigned int v = WI_MapToIndex (wbs->next);
|
|
// Draw only if it points to a valid level on the current screen!
|
|
if (v<lnodes.Size()) WI_drawOnLnode (v, &yah[0], yah.Size());
|
|
}
|
|
|
|
// draws which level you are entering..
|
|
WI_drawEL ();
|
|
|
|
}
|
|
|
|
void WI_drawNoState ()
|
|
{
|
|
snl_pointeron = true;
|
|
WI_drawShowNextLoc();
|
|
}
|
|
|
|
int WI_fragSum (int playernum)
|
|
{
|
|
int i;
|
|
int frags = 0;
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (playeringame[i]
|
|
&& i!=playernum)
|
|
{
|
|
frags += plrs[playernum].frags[i];
|
|
}
|
|
}
|
|
|
|
// JDC hack - negative frags.
|
|
frags -= plrs[playernum].frags[playernum];
|
|
|
|
return frags;
|
|
}
|
|
|
|
static int dm_state;
|
|
static int dm_frags[MAXPLAYERS][MAXPLAYERS];
|
|
static int dm_totals[MAXPLAYERS];
|
|
|
|
void WI_initDeathmatchStats (void)
|
|
{
|
|
|
|
int i, j;
|
|
|
|
state = StatCount;
|
|
acceleratestage = 0;
|
|
dm_state = 1;
|
|
|
|
cnt_pause = TICRATE;
|
|
|
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
|
{
|
|
if (playeringame[i])
|
|
{
|
|
for (j=0 ; j<MAXPLAYERS ; j++)
|
|
if (playeringame[j])
|
|
dm_frags[i][j] = 0;
|
|
|
|
dm_totals[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WI_updateDeathmatchStats ()
|
|
{
|
|
/*
|
|
int i, j;
|
|
bool stillticking;
|
|
*/
|
|
|
|
WI_updateAnimatedBack();
|
|
|
|
if (acceleratestage && dm_state != 4)
|
|
{
|
|
/*
|
|
acceleratestage = 0;
|
|
|
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
|
{
|
|
if (playeringame[i])
|
|
{
|
|
for (j=0 ; j<MAXPLAYERS ; j++)
|
|
if (playeringame[j])
|
|
dm_frags[i][j] = plrs[i].frags[j];
|
|
|
|
dm_totals[i] = WI_fragSum(i);
|
|
}
|
|
}
|
|
|
|
S_Sound (CHAN_VOICE | CHAN_UI, NEXTSTAGE, 1, ATTN_NONE);
|
|
*/
|
|
dm_state = 4;
|
|
}
|
|
|
|
|
|
if (dm_state == 2)
|
|
{
|
|
/*
|
|
if (!(bcnt&3))
|
|
S_Sound (CHAN_VOICE | CHAN_UI, "weapons/pistol", 1, ATTN_NONE);
|
|
|
|
stillticking = false;
|
|
|
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
|
{
|
|
if (playeringame[i])
|
|
{
|
|
for (j=0 ; j<MAXPLAYERS ; j++)
|
|
{
|
|
if (playeringame[j]
|
|
&& dm_frags[i][j] != plrs[i].frags[j])
|
|
{
|
|
if (plrs[i].frags[j] < 0)
|
|
dm_frags[i][j]--;
|
|
else
|
|
dm_frags[i][j]++;
|
|
|
|
if (dm_frags[i][j] > 99)
|
|
dm_frags[i][j] = 99;
|
|
|
|
if (dm_frags[i][j] < -99)
|
|
dm_frags[i][j] = -99;
|
|
|
|
stillticking = true;
|
|
}
|
|
}
|
|
dm_totals[i] = WI_fragSum(i);
|
|
|
|
if (dm_totals[i] > 99)
|
|
dm_totals[i] = 99;
|
|
|
|
if (dm_totals[i] < -99)
|
|
dm_totals[i] = -99;
|
|
}
|
|
|
|
}
|
|
if (!stillticking)
|
|
{
|
|
S_Sound (CHAN_VOICE | CHAN_UI, NEXTSTAGE, 1, ATTN_NONE);
|
|
dm_state++;
|
|
}
|
|
*/
|
|
dm_state = 3;
|
|
}
|
|
else if (dm_state == 4)
|
|
{
|
|
if (acceleratestage)
|
|
{
|
|
S_Sound (CHAN_VOICE | CHAN_UI, "players/male/gibbed", 1, ATTN_NONE);
|
|
WI_initShowNextLoc();
|
|
}
|
|
}
|
|
else if (dm_state & 1)
|
|
{
|
|
if (!--cnt_pause)
|
|
{
|
|
dm_state++;
|
|
cnt_pause = TICRATE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void WI_drawDeathmatchStats ()
|
|
{
|
|
|
|
// draw animated background
|
|
WI_drawBackground();
|
|
WI_drawLF();
|
|
|
|
// [RH] Draw heads-up scores display
|
|
HU_DrawScores (&players[me]);
|
|
|
|
/*
|
|
int i;
|
|
int j;
|
|
int x;
|
|
int y;
|
|
int w;
|
|
|
|
int lh; // line height
|
|
|
|
lh = WI_SPACINGY;
|
|
|
|
// draw stat titles (top line)
|
|
V_DrawPatchClean(DM_TOTALSX-LittleShort(total->width)/2,
|
|
DM_MATRIXY-WI_SPACINGY+10,
|
|
&FB,
|
|
total);
|
|
|
|
V_DrawPatchClean(DM_KILLERSX, DM_KILLERSY, &FB, killers);
|
|
V_DrawPatchClean(DM_VICTIMSX, DM_VICTIMSY, &FB, victims);
|
|
|
|
// draw P?
|
|
x = DM_MATRIXX + DM_SPACINGX;
|
|
y = DM_MATRIXY;
|
|
|
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
|
{
|
|
if (playeringame[i])
|
|
{
|
|
V_DrawPatchClean(x-LittleShort(p[i]->width)/2,
|
|
DM_MATRIXY - WI_SPACINGY,
|
|
&FB,
|
|
p[i]);
|
|
|
|
V_DrawPatchClean(DM_MATRIXX-LittleShort(p[i]->width)/2,
|
|
y,
|
|
&FB,
|
|
p[i]);
|
|
|
|
if (i == me)
|
|
{
|
|
V_DrawPatchClean(x-LittleShort(p[i]->width)/2,
|
|
DM_MATRIXY - WI_SPACINGY,
|
|
&FB,
|
|
bstar);
|
|
|
|
V_DrawPatchClean(DM_MATRIXX-LittleShort(p[i]->width)/2,
|
|
y,
|
|
&FB,
|
|
star);
|
|
}
|
|
}
|
|
x += DM_SPACINGX;
|
|
y += WI_SPACINGY;
|
|
}
|
|
|
|
// draw stats
|
|
y = DM_MATRIXY+10;
|
|
w = LittleShort(num[0]->width);
|
|
|
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
|
{
|
|
x = DM_MATRIXX + DM_SPACINGX;
|
|
|
|
if (playeringame[i])
|
|
{
|
|
for (j=0 ; j<MAXPLAYERS ; j++)
|
|
{
|
|
if (playeringame[j])
|
|
WI_drawNum(x+w, y, dm_frags[i][j], 2);
|
|
|
|
x += DM_SPACINGX;
|
|
}
|
|
WI_drawNum(DM_TOTALSX+w, y, dm_totals[i], 2);
|
|
}
|
|
y += WI_SPACINGY;
|
|
}
|
|
*/
|
|
}
|
|
|
|
static int cnt_frags[MAXPLAYERS];
|
|
static int dofrags;
|
|
static int ng_state;
|
|
|
|
void WI_initNetgameStats ()
|
|
{
|
|
|
|
int i;
|
|
|
|
state = StatCount;
|
|
acceleratestage = 0;
|
|
ng_state = 1;
|
|
|
|
cnt_pause = TICRATE;
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (!playeringame[i])
|
|
continue;
|
|
|
|
cnt_kills[i] = cnt_items[i] = cnt_secret[i] = cnt_frags[i] = 0;
|
|
|
|
dofrags += WI_fragSum (i);
|
|
}
|
|
|
|
dofrags = !!dofrags;
|
|
}
|
|
|
|
void WI_updateNetgameStats ()
|
|
{
|
|
|
|
int i;
|
|
int fsum;
|
|
bool stillticking;
|
|
|
|
WI_updateAnimatedBack ();
|
|
|
|
if (acceleratestage && ng_state != 10)
|
|
{
|
|
acceleratestage = 0;
|
|
|
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
|
{
|
|
if (!playeringame[i])
|
|
continue;
|
|
|
|
cnt_kills[i] = plrs[i].skills;
|
|
cnt_items[i] = plrs[i].sitems;
|
|
cnt_secret[i] = plrs[i].ssecret;
|
|
|
|
if (dofrags)
|
|
cnt_frags[i] = WI_fragSum (i);
|
|
}
|
|
S_Sound (CHAN_VOICE | CHAN_UI, NEXTSTAGE, 1, ATTN_NONE);
|
|
ng_state = 10;
|
|
}
|
|
|
|
if (ng_state == 2)
|
|
{
|
|
if (!(bcnt&3))
|
|
S_Sound (CHAN_VOICE | CHAN_UI, "weapons/pistol", 1, ATTN_NONE);
|
|
|
|
stillticking = false;
|
|
|
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
|
{
|
|
if (!playeringame[i])
|
|
continue;
|
|
|
|
cnt_kills[i] += 2;
|
|
|
|
if (cnt_kills[i] > plrs[i].skills)
|
|
cnt_kills[i] = plrs[i].skills;
|
|
else
|
|
stillticking = true;
|
|
}
|
|
|
|
if (!stillticking)
|
|
{
|
|
S_Sound (CHAN_VOICE | CHAN_UI, NEXTSTAGE, 1, ATTN_NONE);
|
|
ng_state++;
|
|
}
|
|
}
|
|
else if (ng_state == 4)
|
|
{
|
|
if (!(bcnt&3))
|
|
S_Sound (CHAN_VOICE | CHAN_UI, "weapons/pistol", 1, ATTN_NONE);
|
|
|
|
stillticking = false;
|
|
|
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
|
{
|
|
if (!playeringame[i])
|
|
continue;
|
|
|
|
cnt_items[i] += 2;
|
|
if (cnt_items[i] > plrs[i].sitems)
|
|
cnt_items[i] = plrs[i].sitems;
|
|
else
|
|
stillticking = true;
|
|
}
|
|
if (!stillticking)
|
|
{
|
|
S_Sound (CHAN_VOICE | CHAN_UI, NEXTSTAGE, 1, ATTN_NONE);
|
|
ng_state++;
|
|
}
|
|
}
|
|
else if (ng_state == 6)
|
|
{
|
|
if (!(bcnt&3))
|
|
S_Sound (CHAN_VOICE | CHAN_UI, "weapons/pistol", 1, ATTN_NONE);
|
|
|
|
stillticking = false;
|
|
|
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
|
{
|
|
if (!playeringame[i])
|
|
continue;
|
|
|
|
cnt_secret[i] += 2;
|
|
|
|
if (cnt_secret[i] > plrs[i].ssecret)
|
|
cnt_secret[i] = plrs[i].ssecret;
|
|
else
|
|
stillticking = true;
|
|
}
|
|
|
|
if (!stillticking)
|
|
{
|
|
S_Sound (CHAN_VOICE | CHAN_UI, NEXTSTAGE, 1, ATTN_NONE);
|
|
ng_state += 1 + 2*!dofrags;
|
|
}
|
|
}
|
|
else if (ng_state == 8)
|
|
{
|
|
if (!(bcnt&3))
|
|
S_Sound (CHAN_VOICE | CHAN_UI, "weapons/pistol", 1, ATTN_NONE);
|
|
|
|
stillticking = false;
|
|
|
|
for (i=0 ; i<MAXPLAYERS ; i++)
|
|
{
|
|
if (!playeringame[i])
|
|
continue;
|
|
|
|
cnt_frags[i] += 1;
|
|
|
|
if (cnt_frags[i] >= (fsum = WI_fragSum(i)))
|
|
cnt_frags[i] = fsum;
|
|
else
|
|
stillticking = true;
|
|
}
|
|
|
|
if (!stillticking)
|
|
{
|
|
S_Sound (CHAN_VOICE | CHAN_UI, "player/male/death1", 1, ATTN_NONE);
|
|
ng_state++;
|
|
}
|
|
}
|
|
else if (ng_state == 10)
|
|
{
|
|
if (acceleratestage)
|
|
{
|
|
S_Sound (CHAN_VOICE | CHAN_UI, PASTSTATS, 1, ATTN_NONE);
|
|
WI_initShowNextLoc();
|
|
}
|
|
}
|
|
else if (ng_state & 1)
|
|
{
|
|
if (!--cnt_pause)
|
|
{
|
|
ng_state++;
|
|
cnt_pause = TICRATE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WI_drawNetgameStats ()
|
|
{
|
|
int i, x, y, height;
|
|
int maxnamewidth, maxscorewidth;
|
|
int pwidth = IntermissionFont->GetCharWidth('%');
|
|
int icon_x, name_x, kills_x, bonus_x, secret_x;
|
|
int bonus_len, secret_len;
|
|
int missed_kills, missed_items, missed_secrets;
|
|
EColorRange color;
|
|
const char *bonus_label;
|
|
|
|
// draw animated background
|
|
WI_drawBackground();
|
|
|
|
y = WI_drawLF();
|
|
|
|
HU_GetPlayerWidths(maxnamewidth, maxscorewidth);
|
|
height = SmallFont->GetHeight() * CleanYfac;
|
|
y += 16*CleanYfac;
|
|
|
|
bonus_label = (gameinfo.gametype & GAME_Raven) ? "BONUS" : "ITEMS";
|
|
icon_x = (SmallFont->StringWidth("COLOR") + 8) * CleanXfac;
|
|
name_x = icon_x + maxscorewidth * CleanXfac;
|
|
kills_x = name_x + (maxnamewidth + SmallFont->StringWidth("XXXXX") + 8) * CleanXfac;
|
|
bonus_x = kills_x + ((bonus_len = SmallFont->StringWidth(bonus_label)) + 8) * CleanXfac;
|
|
secret_x = bonus_x + ((secret_len = SmallFont->StringWidth("SECRET")) + 8) * CleanXfac;
|
|
|
|
x = (SCREENWIDTH - secret_x) >> 1;
|
|
icon_x += x;
|
|
name_x += x;
|
|
kills_x += x;
|
|
bonus_x += x;
|
|
secret_x += x;
|
|
|
|
color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED;
|
|
|
|
screen->DrawText(SmallFont, color, x, y, "COLOR", DTA_CleanNoMove, true, TAG_DONE);
|
|
screen->DrawText(SmallFont, color, name_x, y, "NAME", DTA_CleanNoMove, true, TAG_DONE);
|
|
screen->DrawText(SmallFont, color, kills_x - SmallFont->StringWidth("KILLS")*CleanXfac, y, "KILLS", DTA_CleanNoMove, true, TAG_DONE);
|
|
screen->DrawText(SmallFont, color, bonus_x - bonus_len*CleanXfac, y, bonus_label, DTA_CleanNoMove, true, TAG_DONE);
|
|
screen->DrawText(SmallFont, color, secret_x - secret_len*CleanXfac, y, "SECRET", DTA_CleanNoMove, true, TAG_DONE);
|
|
y += height + 6 * CleanYfac;
|
|
|
|
missed_kills = wbs->maxkills;
|
|
missed_items = wbs->maxitems;
|
|
missed_secrets = wbs->maxsecret;
|
|
|
|
// Draw lines for each player
|
|
for (i = 0; i < MAXPLAYERS; ++i)
|
|
{
|
|
player_t *player;
|
|
|
|
if (!playeringame[i])
|
|
continue;
|
|
|
|
player = &players[i];
|
|
HU_DrawColorBar(x, y, height, i);
|
|
color = (EColorRange)HU_GetRowColor(player, i == consoleplayer);
|
|
if (player->mo->ScoreIcon.isValid())
|
|
{
|
|
FTexture *pic = TexMan[player->mo->ScoreIcon];
|
|
screen->DrawTexture(pic, icon_x, y, DTA_CleanNoMove, true, TAG_DONE);
|
|
}
|
|
screen->DrawText(SmallFont, color, name_x, y, player->userinfo.netname, DTA_CleanNoMove, true, TAG_DONE);
|
|
WI_drawPercent(SmallFont, kills_x, y, cnt_kills[i], wbs->maxkills, false, color);
|
|
missed_kills -= cnt_kills[i];
|
|
if (ng_state >= 4)
|
|
{
|
|
WI_drawPercent(SmallFont, bonus_x, y, cnt_items[i], wbs->maxitems, false, color);
|
|
missed_items -= cnt_items[i];
|
|
if (ng_state >= 6)
|
|
{
|
|
WI_drawPercent(SmallFont, secret_x, y, cnt_secret[i], wbs->maxsecret, false, color);
|
|
missed_secrets -= cnt_secret[i];
|
|
}
|
|
}
|
|
y += height + CleanYfac;
|
|
}
|
|
|
|
// Draw "MISSED" line
|
|
y += 5 * CleanYfac;
|
|
screen->DrawText(SmallFont, CR_DARKGRAY, name_x, y, "MISSED", DTA_CleanNoMove, true, TAG_DONE);
|
|
WI_drawPercent(SmallFont, kills_x, y, missed_kills, wbs->maxkills, false, CR_DARKGRAY);
|
|
if (ng_state >= 4)
|
|
{
|
|
WI_drawPercent(SmallFont, bonus_x, y, missed_items, wbs->maxitems, false, CR_DARKGRAY);
|
|
if (ng_state >= 6)
|
|
{
|
|
WI_drawPercent(SmallFont, secret_x, y, missed_secrets, wbs->maxsecret, false, CR_DARKGRAY);
|
|
}
|
|
}
|
|
|
|
// Draw "TOTAL" line
|
|
y += height + 5 * CleanYfac;
|
|
color = (gameinfo.gametype & GAME_Raven) ? CR_GREEN : CR_UNTRANSLATED;
|
|
screen->DrawText(SmallFont, color, name_x, y, "TOTAL", DTA_CleanNoMove, true, TAG_DONE);
|
|
WI_drawNum(SmallFont, kills_x, y, wbs->maxkills, 0, false, color);
|
|
if (ng_state >= 4)
|
|
{
|
|
WI_drawNum(SmallFont, bonus_x, y, wbs->maxitems, 0, false, color);
|
|
if (ng_state >= 6)
|
|
{
|
|
WI_drawNum(SmallFont, secret_x, y, wbs->maxsecret, 0, false, color);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int sp_state;
|
|
|
|
void WI_initStats ()
|
|
{
|
|
state = StatCount;
|
|
acceleratestage = 0;
|
|
sp_state = 1;
|
|
cnt_kills[0] = cnt_items[0] = cnt_secret[0] = -1;
|
|
cnt_time = cnt_par = -1;
|
|
cnt_pause = TICRATE;
|
|
|
|
cnt_total_time = -1;
|
|
}
|
|
|
|
void WI_updateStats ()
|
|
{
|
|
WI_updateAnimatedBack ();
|
|
|
|
if ((!(gameinfo.gametype & GAME_DoomChex) || acceleratestage)
|
|
&& sp_state != 10)
|
|
{
|
|
if (acceleratestage)
|
|
{
|
|
acceleratestage = 0;
|
|
sp_state = 10;
|
|
S_Sound (CHAN_VOICE | CHAN_UI, NEXTSTAGE, 1, ATTN_NONE);
|
|
}
|
|
cnt_kills[0] = plrs[me].skills;
|
|
cnt_items[0] = plrs[me].sitems;
|
|
cnt_secret[0] = plrs[me].ssecret;
|
|
cnt_time = plrs[me].stime / TICRATE;
|
|
cnt_par = wbs->partime / TICRATE;
|
|
cnt_total_time = wbs->totaltime / TICRATE;
|
|
}
|
|
|
|
if (sp_state == 2)
|
|
{
|
|
if (gameinfo.gametype & GAME_DoomChex)
|
|
{
|
|
cnt_kills[0] += 2;
|
|
|
|
if (!(bcnt&3))
|
|
S_Sound (CHAN_VOICE | CHAN_UI, "weapons/pistol", 1, ATTN_NONE);
|
|
}
|
|
if (cnt_kills[0] >= plrs[me].skills)
|
|
{
|
|
cnt_kills[0] = plrs[me].skills;
|
|
S_Sound (CHAN_VOICE | CHAN_UI, NEXTSTAGE, 1, ATTN_NONE);
|
|
sp_state++;
|
|
}
|
|
}
|
|
else if (sp_state == 4)
|
|
{
|
|
if (gameinfo.gametype & GAME_DoomChex)
|
|
{
|
|
cnt_items[0] += 2;
|
|
|
|
if (!(bcnt&3))
|
|
S_Sound (CHAN_VOICE | CHAN_UI, "weapons/pistol", 1, ATTN_NONE);
|
|
}
|
|
if (cnt_items[0] >= plrs[me].sitems)
|
|
{
|
|
cnt_items[0] = plrs[me].sitems;
|
|
S_Sound (CHAN_VOICE | CHAN_UI, NEXTSTAGE, 1, ATTN_NONE);
|
|
sp_state++;
|
|
}
|
|
}
|
|
else if (sp_state == 6)
|
|
{
|
|
if (gameinfo.gametype & GAME_DoomChex)
|
|
{
|
|
cnt_secret[0] += 2;
|
|
|
|
if (!(bcnt&3))
|
|
S_Sound (CHAN_VOICE | CHAN_UI, "weapons/pistol", 1, ATTN_NONE);
|
|
}
|
|
if (cnt_secret[0] >= plrs[me].ssecret)
|
|
{
|
|
cnt_secret[0] = plrs[me].ssecret;
|
|
S_Sound (CHAN_VOICE | CHAN_UI, NEXTSTAGE, 1, ATTN_NONE);
|
|
sp_state++;
|
|
}
|
|
}
|
|
else if (sp_state == 8)
|
|
{
|
|
if (gameinfo.gametype & GAME_DoomChex)
|
|
{
|
|
if (!(bcnt&3))
|
|
S_Sound (CHAN_VOICE | CHAN_UI, "weapons/pistol", 1, ATTN_NONE);
|
|
|
|
cnt_time += 3;
|
|
cnt_par += 3;
|
|
cnt_total_time += 3;
|
|
}
|
|
|
|
if (cnt_time >= plrs[me].stime / TICRATE)
|
|
cnt_time = plrs[me].stime / TICRATE;
|
|
|
|
if (cnt_total_time >= wbs->totaltime / TICRATE)
|
|
cnt_total_time = wbs->totaltime / TICRATE;
|
|
|
|
if (cnt_par >= wbs->partime / TICRATE)
|
|
{
|
|
cnt_par = wbs->partime / TICRATE;
|
|
|
|
if (cnt_time >= plrs[me].stime / TICRATE)
|
|
{
|
|
cnt_total_time = wbs->totaltime / TICRATE;
|
|
S_Sound (CHAN_VOICE | CHAN_UI, NEXTSTAGE, 1, ATTN_NONE);
|
|
sp_state++;
|
|
}
|
|
}
|
|
}
|
|
else if (sp_state == 10)
|
|
{
|
|
if (acceleratestage)
|
|
{
|
|
S_Sound (CHAN_VOICE | CHAN_UI, PASTSTATS, 1, ATTN_NONE);
|
|
WI_initShowNextLoc();
|
|
}
|
|
}
|
|
else if (sp_state & 1)
|
|
{
|
|
if (!--cnt_pause)
|
|
{
|
|
sp_state++;
|
|
cnt_pause = TICRATE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WI_drawStats (void)
|
|
{
|
|
// line height
|
|
int lh;
|
|
|
|
lh = IntermissionFont->GetHeight() * 3 / 2;
|
|
|
|
// draw animated background
|
|
WI_drawBackground();
|
|
|
|
WI_drawLF();
|
|
|
|
if (gameinfo.gametype & GAME_DoomChex)
|
|
{
|
|
screen->DrawTexture (kills, SP_STATSX, SP_STATSY, DTA_Clean, true, TAG_DONE);
|
|
WI_drawPercent (IntermissionFont, 320 - SP_STATSX, SP_STATSY, cnt_kills[0], wbs->maxkills);
|
|
|
|
screen->DrawTexture (items, SP_STATSX, SP_STATSY+lh, DTA_Clean, true, TAG_DONE);
|
|
WI_drawPercent (IntermissionFont, 320 - SP_STATSX, SP_STATSY+lh, cnt_items[0], wbs->maxitems);
|
|
|
|
screen->DrawTexture (sp_secret, SP_STATSX, SP_STATSY+2*lh, DTA_Clean, true, TAG_DONE);
|
|
WI_drawPercent (IntermissionFont, 320 - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0], wbs->maxsecret);
|
|
|
|
screen->DrawTexture (timepic, SP_TIMEX, SP_TIMEY, DTA_Clean, true, TAG_DONE);
|
|
WI_drawTime (160 - SP_TIMEX, SP_TIMEY, cnt_time);
|
|
if (wi_showtotaltime)
|
|
{
|
|
WI_drawTime (160 - SP_TIMEX, SP_TIMEY + lh, cnt_total_time, true); // no 'sucks' for total time ever!
|
|
}
|
|
|
|
if (wbs->partime)
|
|
{
|
|
screen->DrawTexture (par, 160 + SP_TIMEX, SP_TIMEY, DTA_Clean, true, TAG_DONE);
|
|
WI_drawTime (320 - SP_TIMEX, SP_TIMEY, cnt_par);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 65, "KILLS", DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
|
|
screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 90, "ITEMS", DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
|
|
screen->DrawText (BigFont, CR_UNTRANSLATED, 50, 115, "SECRETS", DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
|
|
|
|
int countpos = gameinfo.gametype==GAME_Strife? 285:270;
|
|
if (sp_state >= 2)
|
|
{
|
|
WI_drawPercent (IntermissionFont, countpos, 65, cnt_kills[0], wbs->maxkills);
|
|
}
|
|
if (sp_state >= 4)
|
|
{
|
|
WI_drawPercent (IntermissionFont, countpos, 90, cnt_items[0], wbs->maxitems);
|
|
}
|
|
if (sp_state >= 6)
|
|
{
|
|
WI_drawPercent (IntermissionFont, countpos, 115, cnt_secret[0], wbs->maxsecret);
|
|
}
|
|
if (sp_state >= 8)
|
|
{
|
|
screen->DrawText (BigFont, CR_UNTRANSLATED, 85, 160, "TIME",
|
|
DTA_Clean, true, DTA_Shadow, true, TAG_DONE);
|
|
WI_drawTime (249, 160, cnt_time);
|
|
if (wi_showtotaltime)
|
|
{
|
|
WI_drawTime (249, 180, cnt_total_time);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ====================================================================
|
|
// WI_checkForAccelerate
|
|
// Purpose: See if the player has hit either the attack or use key
|
|
// or mouse button. If so we set acceleratestage to 1 and
|
|
// all those display routines above jump right to the end.
|
|
// Args: none
|
|
// Returns: void
|
|
//
|
|
// ====================================================================
|
|
void WI_checkForAccelerate(void)
|
|
{
|
|
int i;
|
|
player_t *player;
|
|
|
|
// check for button presses to skip delays
|
|
for (i = 0, player = players; i < MAXPLAYERS; i++, player++)
|
|
{
|
|
if (playeringame[i])
|
|
{
|
|
if ((player->cmd.ucmd.buttons ^ player->oldbuttons) &&
|
|
((players[i].cmd.ucmd.buttons & players[i].oldbuttons)
|
|
== players[i].oldbuttons) && !player->isbot)
|
|
{
|
|
acceleratestage = 1;
|
|
}
|
|
player->oldbuttons = player->cmd.ucmd.buttons;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ====================================================================
|
|
// WI_Ticker
|
|
// Purpose: Do various updates every gametic, for stats, animation,
|
|
// checking that intermission music is running, etc.
|
|
// Args: none
|
|
// Returns: void
|
|
//
|
|
// ====================================================================
|
|
void WI_Ticker(void)
|
|
{
|
|
// counter for general background animation
|
|
bcnt++;
|
|
|
|
if (bcnt == 1)
|
|
{
|
|
// intermission music - use the defaults if none specified
|
|
if (level.info->InterMusic.IsNotEmpty())
|
|
S_ChangeMusic(level.info->InterMusic, level.info->intermusicorder);
|
|
else
|
|
S_ChangeMusic (gameinfo.intermissionMusic.GetChars());
|
|
|
|
}
|
|
|
|
WI_checkForAccelerate();
|
|
|
|
switch (state)
|
|
{
|
|
case StatCount:
|
|
if (deathmatch) WI_updateDeathmatchStats();
|
|
else if (multiplayer) WI_updateNetgameStats();
|
|
else WI_updateStats();
|
|
break;
|
|
|
|
case ShowNextLoc:
|
|
WI_updateShowNextLoc();
|
|
break;
|
|
|
|
case NoState:
|
|
WI_updateNoState();
|
|
break;
|
|
|
|
case LeavingIntermission:
|
|
// Hush, GCC.
|
|
break;
|
|
}
|
|
}
|
|
|
|
void WI_loadData(void)
|
|
{
|
|
if (gameinfo.gametype & GAME_DoomChex)
|
|
{
|
|
finished = TexMan["WIF"]; // "finished"
|
|
entering = TexMan["WIENTER"]; // "entering"
|
|
kills = TexMan["WIOSTK"]; // "kills"
|
|
secret = TexMan["WIOSTS"]; // "scrt"
|
|
sp_secret = TexMan["WISCRT2"]; // "secret"
|
|
items = TexMan["WIOSTI"]; // "items"
|
|
frags = TexMan["WIFRGS"]; // "frgs"
|
|
timepic = TexMan["WITIME"]; // "time"
|
|
sucks = TexMan["WISUCKS"]; // "sucks"
|
|
par = TexMan["WIPAR"]; // "par"
|
|
killers = TexMan["WIKILRS"]; // "killers" (vertical]
|
|
victims = TexMan["WIVCTMS"]; // "victims" (horiz]
|
|
total = TexMan["WIMSTT"]; // "total"
|
|
star = TexMan["STFST01"]; // your face
|
|
bstar = TexMan["STFDEAD0"]; // dead face
|
|
p = TexMan["STPBANY"];
|
|
}
|
|
else if (gameinfo.gametype & GAME_Raven)
|
|
{
|
|
if (gameinfo.gametype == GAME_Heretic)
|
|
{
|
|
star = TexMan["FACEA0"];
|
|
bstar = TexMan["FACEB0"];
|
|
}
|
|
else
|
|
{
|
|
int dummywidth;
|
|
star = BigFont->GetChar('*', &dummywidth); // just a dummy to avoid an error if it is being used
|
|
bstar = star;
|
|
}
|
|
}
|
|
else // Strife needs some handling, too!
|
|
{
|
|
int dummywidth;
|
|
star = BigFont->GetChar('*', &dummywidth); // just a dummy to avoid an error if it is being used
|
|
bstar = star;
|
|
}
|
|
|
|
// Use the local level structure which can be overridden by hubs
|
|
lnametexts[0] = level.LevelName;
|
|
|
|
level_info_t *li = FindLevelInfo(wbs->next);
|
|
if (li) lnametexts[1] = li->LookupLevelName();
|
|
else lnametexts[1] = "";
|
|
|
|
WI_LoadBackground(false);
|
|
}
|
|
|
|
void WI_unloadData ()
|
|
{
|
|
// [RH] The texture data gets unloaded at pre-map time, so there's nothing to do here
|
|
return;
|
|
}
|
|
|
|
void WI_Drawer (void)
|
|
{
|
|
switch (state)
|
|
{
|
|
case StatCount:
|
|
if (deathmatch)
|
|
WI_drawDeathmatchStats();
|
|
else if (multiplayer)
|
|
WI_drawNetgameStats();
|
|
else
|
|
WI_drawStats();
|
|
break;
|
|
|
|
case ShowNextLoc:
|
|
WI_drawShowNextLoc();
|
|
break;
|
|
|
|
case LeavingIntermission:
|
|
break;
|
|
|
|
default:
|
|
WI_drawNoState();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void WI_initVariables (wbstartstruct_t *wbstartstruct)
|
|
{
|
|
wbs = wbstartstruct;
|
|
acceleratestage = 0;
|
|
cnt = bcnt = 0;
|
|
me = wbs->pnum;
|
|
plrs = wbs->plyr;
|
|
}
|
|
|
|
void WI_Start (wbstartstruct_t *wbstartstruct)
|
|
{
|
|
noautostartmap = false;
|
|
V_SetBlend (0,0,0,0);
|
|
WI_initVariables (wbstartstruct);
|
|
WI_loadData ();
|
|
if (deathmatch)
|
|
WI_initDeathmatchStats();
|
|
else if (multiplayer)
|
|
WI_initNetgameStats();
|
|
else
|
|
WI_initStats();
|
|
S_StopAllChannels ();
|
|
SN_StopAllSequences ();
|
|
}
|