1 /*
2 ** intermission.cpp
3 ** Framework for intermissions (text screens, slideshows, etc)
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 2010 Christoph Oelckers
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 ** notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 ** notice, this list of conditions and the following disclaimer in the
17 ** documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 ** derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34
35 #include "doomtype.h"
36 #include "doomstat.h"
37 #include "d_event.h"
38 #include "w_wad.h"
39 #include "gi.h"
40 #include "v_video.h"
41 #include "v_palette.h"
42 #include "d_main.h"
43 #include "gstrings.h"
44 #include "intermission/intermission.h"
45 #include "actor.h"
46 #include "d_player.h"
47 #include "r_state.h"
48 #include "r_data/r_translate.h"
49 #include "c_bind.h"
50 #include "g_level.h"
51 #include "p_conversation.h"
52 #include "menu/menu.h"
53 #include "d_net.h"
54
55 FIntermissionDescriptorList IntermissionDescriptors;
56
57 IMPLEMENT_CLASS(DIntermissionScreen)
58 IMPLEMENT_CLASS(DIntermissionScreenFader)
59 IMPLEMENT_CLASS(DIntermissionScreenText)
60 IMPLEMENT_CLASS(DIntermissionScreenCast)
61 IMPLEMENT_CLASS(DIntermissionScreenScroller)
62 IMPLEMENT_POINTY_CLASS(DIntermissionController)
63 DECLARE_POINTER(mScreen)
64 END_POINTERS
65
66 extern int NoWipe;
67
68 //==========================================================================
69 //
70 //
71 //
72 //==========================================================================
73
Init(FIntermissionAction * desc,bool first)74 void DIntermissionScreen::Init(FIntermissionAction *desc, bool first)
75 {
76 int lumpnum;
77
78 if (desc->mCdTrack == 0 || !S_ChangeCDMusic (desc->mCdTrack, desc->mCdId))
79 {
80 if (desc->mMusic.IsEmpty())
81 {
82 // only start the default music if this is the first action in an intermission
83 if (first) S_ChangeMusic (gameinfo.finaleMusic, gameinfo.finaleOrder, desc->mMusicLooping);
84 }
85 else
86 {
87 S_ChangeMusic (desc->mMusic, desc->mMusicOrder, desc->mMusicLooping);
88 }
89 }
90 mDuration = desc->mDuration;
91
92 const char *texname = desc->mBackground;
93 if (*texname == '@')
94 {
95 char *pp;
96 unsigned int v = strtoul(texname+1, &pp, 10) - 1;
97 if (*pp == 0 && v < gameinfo.finalePages.Size())
98 {
99 texname = gameinfo.finalePages[v].GetChars();
100 }
101 else if (gameinfo.finalePages.Size() > 0)
102 {
103 texname = gameinfo.finalePages[0].GetChars();
104 }
105 else
106 {
107 texname = gameinfo.TitlePage.GetChars();
108 }
109 }
110 else if (*texname == '$')
111 {
112 texname = GStrings[texname+1];
113 }
114 if (texname[0] != 0)
115 {
116 mBackground = TexMan.CheckForTexture(texname, FTexture::TEX_MiscPatch);
117 mFlatfill = desc->mFlatfill;
118 }
119 S_Sound (CHAN_VOICE | CHAN_UI, desc->mSound, 1.0f, ATTN_NONE);
120 if (desc->mPalette.IsNotEmpty() && (lumpnum = Wads.CheckNumForFullName(desc->mPalette, true)) > 0)
121 {
122 PalEntry *palette;
123 const BYTE *orgpal;
124 FMemLump lump;
125 int i;
126
127 lump = Wads.ReadLump (lumpnum);
128 orgpal = (BYTE *)lump.GetMem();
129 palette = screen->GetPalette ();
130 for (i = 256; i > 0; i--, orgpal += 3)
131 {
132 *palette++ = PalEntry (orgpal[0], orgpal[1], orgpal[2]);
133 }
134 screen->UpdatePalette ();
135 mPaletteChanged = true;
136 NoWipe = 1;
137 M_EnableMenu(false);
138 }
139 mOverlays.Resize(desc->mOverlays.Size());
140 for (unsigned i=0; i < mOverlays.Size(); i++)
141 {
142 mOverlays[i].x = desc->mOverlays[i].x;
143 mOverlays[i].y = desc->mOverlays[i].y;
144 mOverlays[i].mCondition = desc->mOverlays[i].mCondition;
145 mOverlays[i].mPic = TexMan.CheckForTexture(desc->mOverlays[i].mName, FTexture::TEX_MiscPatch);
146 }
147 mTicker = 0;
148 }
149
150
Responder(event_t * ev)151 int DIntermissionScreen::Responder (event_t *ev)
152 {
153 if (ev->type == EV_KeyDown)
154 {
155 return -1;
156 }
157 return 0;
158 }
159
Ticker()160 int DIntermissionScreen::Ticker ()
161 {
162 if (++mTicker >= mDuration && mDuration > 0) return -1;
163 return 0;
164 }
165
CheckOverlay(int i)166 bool DIntermissionScreen::CheckOverlay(int i)
167 {
168 if (mOverlays[i].mCondition == NAME_Multiplayer && !multiplayer) return false;
169 else if (mOverlays[i].mCondition != NAME_None)
170 {
171 if (multiplayer || players[0].mo == NULL) return false;
172 const PClass *cls = PClass::FindClass(mOverlays[i].mCondition);
173 if (cls == NULL) return false;
174 if (!players[0].mo->IsKindOf(cls)) return false;
175 }
176 return true;
177 }
178
Drawer()179 void DIntermissionScreen::Drawer ()
180 {
181 if (mBackground.isValid())
182 {
183 if (!mFlatfill)
184 {
185 screen->DrawTexture (TexMan[mBackground], 0, 0, DTA_Fullscreen, true, TAG_DONE);
186 }
187 else
188 {
189 screen->FlatFill (0,0, SCREENWIDTH, SCREENHEIGHT, TexMan[mBackground]);
190 }
191 }
192 else
193 {
194 screen->Clear (0, 0, SCREENWIDTH, SCREENHEIGHT, 0, 0);
195 }
196 for (unsigned i=0; i < mOverlays.Size(); i++)
197 {
198 if (CheckOverlay(i))
199 screen->DrawTexture (TexMan[mOverlays[i].mPic], mOverlays[i].x, mOverlays[i].y, DTA_320x200, true, TAG_DONE);
200 }
201 if (!mFlatfill) screen->FillBorder (NULL);
202 }
203
Destroy()204 void DIntermissionScreen::Destroy()
205 {
206 if (mPaletteChanged)
207 {
208 PalEntry *palette;
209 int i;
210
211 palette = screen->GetPalette ();
212 for (i = 0; i < 256; ++i)
213 {
214 palette[i] = GPalette.BaseColors[i];
215 }
216 screen->UpdatePalette ();
217 NoWipe = 5;
218 mPaletteChanged = false;
219 M_EnableMenu(true);
220 }
221 S_StopSound(CHAN_VOICE);
222 Super::Destroy();
223 }
224
225 //==========================================================================
226 //
227 //
228 //
229 //==========================================================================
230
Init(FIntermissionAction * desc,bool first)231 void DIntermissionScreenFader::Init(FIntermissionAction *desc, bool first)
232 {
233 Super::Init(desc, first);
234 mType = static_cast<FIntermissionActionFader*>(desc)->mFadeType;
235 }
236
237 //===========================================================================
238 //
239 // FadePic
240 //
241 //===========================================================================
242
Responder(event_t * ev)243 int DIntermissionScreenFader::Responder (event_t *ev)
244 {
245 if (ev->type == EV_KeyDown)
246 {
247 V_SetBlend(0,0,0,0);
248 return -1;
249 }
250 return Super::Responder(ev);
251 }
252
Ticker()253 int DIntermissionScreenFader::Ticker ()
254 {
255 if (mFlatfill || !mBackground.isValid()) return -1;
256 return Super::Ticker();
257 }
258
Drawer()259 void DIntermissionScreenFader::Drawer ()
260 {
261 if (!mFlatfill && mBackground.isValid())
262 {
263 double factor = clamp(double(mTicker) / mDuration, 0., 1.);
264 if (mType == FADE_In) factor = 1.0 - factor;
265 int color = MAKEARGB(xs_RoundToInt(factor*255), 0,0,0);
266
267 if (screen->Begin2D(false))
268 {
269 screen->DrawTexture (TexMan[mBackground], 0, 0, DTA_Fullscreen, true, DTA_ColorOverlay, color, TAG_DONE);
270 for (unsigned i=0; i < mOverlays.Size(); i++)
271 {
272 if (CheckOverlay(i))
273 screen->DrawTexture (TexMan[mOverlays[i].mPic], mOverlays[i].x, mOverlays[i].y, DTA_320x200, true, DTA_ColorOverlay, color, TAG_DONE);
274 }
275 screen->FillBorder (NULL);
276 }
277 else
278 {
279 V_SetBlend (0,0,0,int(256*factor));
280 Super::Drawer();
281 }
282 }
283 }
284
285 //==========================================================================
286 //
287 //
288 //
289 //==========================================================================
290
Init(FIntermissionAction * desc,bool first)291 void DIntermissionScreenText::Init(FIntermissionAction *desc, bool first)
292 {
293 Super::Init(desc, first);
294 mText = static_cast<FIntermissionActionTextscreen*>(desc)->mText;
295 if (mText[0] == '$') mText = GStrings(&mText[1]);
296 mTextSpeed = static_cast<FIntermissionActionTextscreen*>(desc)->mTextSpeed;
297 mTextX = static_cast<FIntermissionActionTextscreen*>(desc)->mTextX;
298 if (mTextX < 0) mTextX =gameinfo.TextScreenX;
299 mTextY = static_cast<FIntermissionActionTextscreen*>(desc)->mTextY;
300 if (mTextY < 0) mTextY =gameinfo.TextScreenY;
301 mTextLen = (int)strlen(mText);
302 mTextDelay = static_cast<FIntermissionActionTextscreen*>(desc)->mTextDelay;
303 mTextColor = static_cast<FIntermissionActionTextscreen*>(desc)->mTextColor;
304 // For text screens, the duration only counts when the text is complete.
305 if (mDuration > 0) mDuration += mTextDelay + mTextSpeed * mTextLen;
306 }
307
Responder(event_t * ev)308 int DIntermissionScreenText::Responder (event_t *ev)
309 {
310 if (ev->type == EV_KeyDown)
311 {
312 if (mTicker < mTextDelay + (mTextLen * mTextSpeed))
313 {
314 mTicker = mTextDelay + (mTextLen * mTextSpeed);
315 return 1;
316 }
317 }
318 return Super::Responder(ev);
319 }
320
Drawer()321 void DIntermissionScreenText::Drawer ()
322 {
323 Super::Drawer();
324 if (mTicker >= mTextDelay)
325 {
326 FTexture *pic;
327 int w;
328 size_t count;
329 int c;
330 const FRemapTable *range;
331 const char *ch = mText;
332 const int kerning = SmallFont->GetDefaultKerning();
333
334 // Count number of rows in this text. Since it does not word-wrap, we just count
335 // line feed characters.
336 int numrows;
337
338 for (numrows = 1, c = 0; ch[c] != '\0'; ++c)
339 {
340 numrows += (ch[c] == '\n');
341 }
342
343 int rowheight = SmallFont->GetHeight() * CleanYfac;
344 int rowpadding = (gameinfo.gametype & (GAME_DoomStrifeChex) ? 3 : -1) * CleanYfac;
345
346 int cx = (mTextX - 160)*CleanXfac + screen->GetWidth() / 2;
347 int cy = (mTextY - 100)*CleanYfac + screen->GetHeight() / 2;
348 int startx = cx;
349
350 // Does this text fall off the end of the screen? If so, try to eliminate some margins first.
351 while (rowpadding > 0 && cy + numrows * (rowheight + rowpadding) - rowpadding > screen->GetHeight())
352 {
353 rowpadding--;
354 }
355 // If it's still off the bottom, try to center it vertically.
356 if (cy + numrows * (rowheight + rowpadding) - rowpadding > screen->GetHeight())
357 {
358 cy = (screen->GetHeight() - (numrows * (rowheight + rowpadding) - rowpadding)) / 2;
359 // If it's off the top now, you're screwed. It's too tall to fit.
360 if (cy < 0)
361 {
362 cy = 0;
363 }
364 }
365 rowheight += rowpadding;
366
367 // draw some of the text onto the screen
368 count = (mTicker - mTextDelay) / mTextSpeed;
369 range = SmallFont->GetColorTranslation (mTextColor);
370
371 for ( ; count > 0 ; count-- )
372 {
373 c = *ch++;
374 if (!c)
375 break;
376 if (c == '\n')
377 {
378 cx = startx;
379 cy += rowheight;
380 continue;
381 }
382
383 pic = SmallFont->GetChar (c, &w);
384 w += kerning;
385 w *= CleanXfac;
386 if (cx + w > SCREENWIDTH)
387 continue;
388 if (pic != NULL)
389 {
390 screen->DrawTexture (pic,
391 cx,
392 cy,
393 DTA_Translation, range,
394 DTA_CleanNoMove, true,
395 TAG_DONE);
396 }
397 cx += w;
398 }
399 }
400 }
401
402 //==========================================================================
403 //
404 //
405 //
406 //==========================================================================
407
Init(FIntermissionAction * desc,bool first)408 void DIntermissionScreenCast::Init(FIntermissionAction *desc, bool first)
409 {
410 Super::Init(desc, first);
411 mName = static_cast<FIntermissionActionCast*>(desc)->mName;
412 mClass = PClass::FindClass(static_cast<FIntermissionActionCast*>(desc)->mCastClass);
413 if (mClass != NULL) mDefaults = GetDefaultByType(mClass);
414 else
415 {
416 mDefaults = NULL;
417 caststate = NULL;
418 return;
419 }
420
421 mCastSounds.Resize(static_cast<FIntermissionActionCast*>(desc)->mCastSounds.Size());
422 for (unsigned i=0; i < mCastSounds.Size(); i++)
423 {
424 mCastSounds[i].mSequence = static_cast<FIntermissionActionCast*>(desc)->mCastSounds[i].mSequence;
425 mCastSounds[i].mIndex = static_cast<FIntermissionActionCast*>(desc)->mCastSounds[i].mIndex;
426 mCastSounds[i].mSound = static_cast<FIntermissionActionCast*>(desc)->mCastSounds[i].mSound;
427 }
428 caststate = mDefaults->SeeState;
429 if (mClass->IsDescendantOf(RUNTIME_CLASS(APlayerPawn)))
430 {
431 advplayerstate = mDefaults->MissileState;
432 casttranslation = translationtables[TRANSLATION_Players][consoleplayer];
433 }
434 else
435 {
436 advplayerstate = NULL;
437 casttranslation = NULL;
438 if (mDefaults->Translation != 0)
439 {
440 casttranslation = translationtables[GetTranslationType(mDefaults->Translation)]
441 [GetTranslationIndex(mDefaults->Translation)];
442 }
443 }
444 castdeath = false;
445 castframes = 0;
446 castonmelee = 0;
447 castattacking = false;
448 if (mDefaults->SeeSound)
449 {
450 S_Sound (CHAN_VOICE | CHAN_UI, mDefaults->SeeSound, 1, ATTN_NONE);
451 }
452 }
453
Responder(event_t * ev)454 int DIntermissionScreenCast::Responder (event_t *ev)
455 {
456 if (ev->type != EV_KeyDown) return 0;
457
458 if (castdeath)
459 return 1; // already in dying frames
460
461 castdeath = true;
462
463 if (mClass != NULL)
464 {
465 FName label[] = {NAME_Death, NAME_Cast};
466 caststate = mClass->ActorInfo->FindState(2, label);
467 if (caststate == NULL) return -1;
468
469 casttics = caststate->GetTics();
470 castframes = 0;
471 castattacking = false;
472
473 if (mClass->IsDescendantOf(RUNTIME_CLASS(APlayerPawn)))
474 {
475 int snd = S_FindSkinnedSound(players[consoleplayer].mo, "*death");
476 if (snd != 0) S_Sound (CHAN_VOICE | CHAN_UI, snd, 1, ATTN_NONE);
477 }
478 else if (mDefaults->DeathSound)
479 {
480 S_Sound (CHAN_VOICE | CHAN_UI, mDefaults->DeathSound, 1, ATTN_NONE);
481 }
482 }
483 return true;
484 }
485
PlayAttackSound()486 void DIntermissionScreenCast::PlayAttackSound()
487 {
488 // sound hacks....
489 if (caststate != NULL && castattacking)
490 {
491 for (unsigned i = 0; i < mCastSounds.Size(); i++)
492 {
493 if ((!!mCastSounds[i].mSequence) == (basestate != mDefaults->MissileState) &&
494 (caststate == basestate + mCastSounds[i].mIndex))
495 {
496 S_StopAllChannels ();
497 S_Sound (CHAN_WEAPON | CHAN_UI, mCastSounds[i].mSound, 1, ATTN_NONE);
498 return;
499 }
500 }
501 }
502
503 }
504
Ticker()505 int DIntermissionScreenCast::Ticker ()
506 {
507 Super::Ticker();
508
509 if (--casttics > 0 && caststate != NULL)
510 return 0; // not time to change state yet
511
512 if (caststate == NULL || caststate->GetTics() == -1 || caststate->GetNextState() == NULL ||
513 (caststate->GetNextState() == caststate && castdeath))
514 {
515 return -1;
516 }
517 else
518 {
519 // just advance to next state in animation
520 if (caststate == advplayerstate)
521 goto stopattack; // Oh, gross hack!
522
523 caststate = caststate->GetNextState();
524
525 PlayAttackSound();
526 castframes++;
527 }
528
529 if (castframes == 12 && !castdeath)
530 {
531 // go into attack frame
532 castattacking = true;
533 if (!mClass->IsDescendantOf(RUNTIME_CLASS(APlayerPawn)))
534 {
535 if (castonmelee)
536 basestate = caststate = mDefaults->MeleeState;
537 else
538 basestate = caststate = mDefaults->MissileState;
539 castonmelee ^= 1;
540 if (caststate == NULL)
541 {
542 if (castonmelee)
543 basestate = caststate = mDefaults->MeleeState;
544 else
545 basestate = caststate = mDefaults->MissileState;
546 }
547 }
548 else
549 {
550 // The players use the melee state differently so it can't be used here
551 basestate = caststate = mDefaults->MissileState;
552 }
553 PlayAttackSound();
554 }
555
556 if (castattacking)
557 {
558 if (castframes == 24 || caststate == mDefaults->SeeState )
559 {
560 stopattack:
561 castattacking = false;
562 castframes = 0;
563 caststate = mDefaults->SeeState;
564 }
565 }
566
567 casttics = caststate->GetTics();
568 if (casttics == -1)
569 casttics = 15;
570 return 0;
571 }
572
Drawer()573 void DIntermissionScreenCast::Drawer ()
574 {
575 spriteframe_t* sprframe;
576 FTexture* pic;
577
578 Super::Drawer();
579
580 const char *name = mName;
581 if (name != NULL)
582 {
583 if (*name == '$') name = GStrings(name+1);
584 screen->DrawText (SmallFont, CR_UNTRANSLATED,
585 (SCREENWIDTH - SmallFont->StringWidth (name) * CleanXfac)/2,
586 (SCREENHEIGHT * 180) / 200,
587 name,
588 DTA_CleanNoMove, true, TAG_DONE);
589 }
590
591 // draw the current frame in the middle of the screen
592 if (caststate != NULL)
593 {
594 double castscalex = FIXED2DBL(mDefaults->scaleX);
595 double castscaley = FIXED2DBL(mDefaults->scaleY);
596
597 int castsprite = caststate->sprite;
598
599 if (!(mDefaults->flags4 & MF4_NOSKIN) &&
600 mDefaults->SpawnState != NULL && caststate->sprite == mDefaults->SpawnState->sprite &&
601 mClass->IsDescendantOf(RUNTIME_CLASS(APlayerPawn)) &&
602 skins != NULL)
603 {
604 // Only use the skin sprite if this class has not been removed from the
605 // PlayerClasses list.
606 for (unsigned i = 0; i < PlayerClasses.Size(); ++i)
607 {
608 if (PlayerClasses[i].Type == mClass)
609 {
610 FPlayerSkin *skin = &skins[players[consoleplayer].userinfo.GetSkin()];
611 castsprite = skin->sprite;
612
613 if (!(mDefaults->flags4 & MF4_NOSKIN))
614 {
615 castscaley = FIXED2DBL(skin->ScaleY);
616 castscalex = FIXED2DBL(skin->ScaleX);
617 }
618
619 }
620 }
621 }
622
623 sprframe = &SpriteFrames[sprites[castsprite].spriteframes + caststate->GetFrame()];
624 pic = TexMan(sprframe->Texture[0]);
625
626 screen->DrawTexture (pic, 160, 170,
627 DTA_320x200, true,
628 DTA_FlipX, sprframe->Flip & 1,
629 DTA_DestHeightF, pic->GetScaledHeightDouble() * castscaley,
630 DTA_DestWidthF, pic->GetScaledWidthDouble() * castscalex,
631 DTA_RenderStyle, mDefaults->RenderStyle,
632 DTA_Alpha, mDefaults->alpha,
633 DTA_Translation, casttranslation,
634 TAG_DONE);
635 }
636 }
637
638 //==========================================================================
639 //
640 //
641 //
642 //==========================================================================
643
Init(FIntermissionAction * desc,bool first)644 void DIntermissionScreenScroller::Init(FIntermissionAction *desc, bool first)
645 {
646 Super::Init(desc, first);
647 mFirstPic = mBackground;
648 mSecondPic = TexMan.CheckForTexture(static_cast<FIntermissionActionScroller*>(desc)->mSecondPic, FTexture::TEX_MiscPatch);
649 mScrollDelay = static_cast<FIntermissionActionScroller*>(desc)->mScrollDelay;
650 mScrollTime = static_cast<FIntermissionActionScroller*>(desc)->mScrollTime;
651 mScrollDir = static_cast<FIntermissionActionScroller*>(desc)->mScrollDir;
652 }
653
Responder(event_t * ev)654 int DIntermissionScreenScroller::Responder (event_t *ev)
655 {
656 int res = Super::Responder(ev);
657 if (res == -1)
658 {
659 mBackground = mSecondPic;
660 mTicker = mScrollDelay + mScrollTime;
661 }
662 return res;
663 }
664
Drawer()665 void DIntermissionScreenScroller::Drawer ()
666 {
667 FTexture *tex = TexMan[mFirstPic];
668 FTexture *tex2 = TexMan[mSecondPic];
669 if (mTicker >= mScrollDelay && mTicker < mScrollDelay + mScrollTime && tex != NULL && tex2 != NULL)
670 {
671
672 int fwidth = tex->GetScaledWidth();
673 int fheight = tex->GetScaledHeight();
674
675 double xpos1 = 0, ypos1 = 0, xpos2 = 0, ypos2 = 0;
676
677 switch (mScrollDir)
678 {
679 case SCROLL_Up:
680 ypos1 = double(mTicker - mScrollDelay) * fheight / mScrollTime;
681 ypos2 = ypos1 - fheight;
682 break;
683
684 case SCROLL_Down:
685 ypos1 = -double(mTicker - mScrollDelay) * fheight / mScrollTime;
686 ypos2 = ypos1 + fheight;
687 break;
688
689 case SCROLL_Left:
690 default:
691 xpos1 = double(mTicker - mScrollDelay) * fwidth / mScrollTime;
692 xpos2 = xpos1 - fwidth;
693 break;
694
695 case SCROLL_Right:
696 xpos1 = -double(mTicker - mScrollDelay) * fwidth / mScrollTime;
697 xpos2 = xpos1 + fwidth;
698 break;
699 }
700
701 screen->DrawTexture (tex, xpos1, ypos1,
702 DTA_VirtualWidth, fwidth,
703 DTA_VirtualHeight, fheight,
704 DTA_Masked, false,
705 TAG_DONE);
706 screen->DrawTexture (tex2, xpos2, ypos2,
707 DTA_VirtualWidth, fwidth,
708 DTA_VirtualHeight, fheight,
709 DTA_Masked, false,
710 TAG_DONE);
711
712 screen->FillBorder (NULL);
713 mBackground = mSecondPic;
714 }
715 else
716 {
717 Super::Drawer();
718 }
719 }
720
721
722 //==========================================================================
723 //
724 //
725 //
726 //==========================================================================
727
728 DIntermissionController *DIntermissionController::CurrentIntermission;
729
DIntermissionController(FIntermissionDescriptor * Desc,bool DeleteDesc,BYTE state)730 DIntermissionController::DIntermissionController(FIntermissionDescriptor *Desc, bool DeleteDesc, BYTE state)
731 {
732 mDesc = Desc;
733 mDeleteDesc = DeleteDesc;
734 mIndex = 0;
735 mAdvance = false;
736 mSentAdvance = false;
737 mScreen = NULL;
738 mFirst = true;
739 mGameState = state;
740
741 // If the intermission finishes straight away then cancel the wipe.
742 if(!NextPage())
743 wipegamestate = GS_FINALE;
744 }
745
NextPage()746 bool DIntermissionController::NextPage ()
747 {
748 FTextureID bg;
749 bool fill = false;
750
751 if (mIndex == (int)mDesc->mActions.Size() && mDesc->mLink == NAME_None)
752 {
753 // last page
754 return false;
755 }
756
757 if (mScreen != NULL)
758 {
759 bg = mScreen->GetBackground(&fill);
760 mScreen->Destroy();
761 }
762 again:
763 while ((unsigned)mIndex < mDesc->mActions.Size())
764 {
765 FIntermissionAction *action = mDesc->mActions[mIndex++];
766 if (action->mClass == WIPER_ID)
767 {
768 wipegamestate = static_cast<FIntermissionActionWiper*>(action)->mWipeType;
769 }
770 else if (action->mClass == TITLE_ID)
771 {
772 Destroy();
773 D_StartTitle ();
774 return false;
775 }
776 else
777 {
778 // create page here
779 mScreen = (DIntermissionScreen*)action->mClass->CreateNew();
780 mScreen->SetBackground(bg, fill); // copy last screen's background before initializing
781 mScreen->Init(action, mFirst);
782 mFirst = false;
783 return true;
784 }
785 }
786 if (mDesc->mLink != NAME_None)
787 {
788 FIntermissionDescriptor **pDesc = IntermissionDescriptors.CheckKey(mDesc->mLink);
789 if (pDesc != NULL)
790 {
791 if (mDeleteDesc) delete mDesc;
792 mDeleteDesc = false;
793 mIndex = 0;
794 mDesc = *pDesc;
795 goto again;
796 }
797 }
798 return false;
799 }
800
Responder(event_t * ev)801 bool DIntermissionController::Responder (event_t *ev)
802 {
803 if (mScreen != NULL)
804 {
805 if (!mScreen->mPaletteChanged && ev->type == EV_KeyDown)
806 {
807 const char *cmd = Bindings.GetBind (ev->data1);
808
809 if (cmd != NULL &&
810 (!stricmp(cmd, "toggleconsole") ||
811 !stricmp(cmd, "screenshot")))
812 {
813 return false;
814 }
815 }
816
817 if (mScreen->mTicker < 2) return false; // prevent some leftover events from auto-advancing
818 int res = mScreen->Responder(ev);
819 if (res == -1 && !mSentAdvance)
820 {
821 Net_WriteByte(DEM_ADVANCEINTER);
822 mSentAdvance = true;
823 }
824 return !!res;
825 }
826 return false;
827 }
828
Ticker()829 void DIntermissionController::Ticker ()
830 {
831 if (mAdvance)
832 {
833 mSentAdvance = false;
834 }
835 if (mScreen != NULL)
836 {
837 mAdvance |= (mScreen->Ticker() == -1);
838 }
839 if (mAdvance)
840 {
841 mAdvance = false;
842 if (!NextPage())
843 {
844 switch (mGameState)
845 {
846 case FSTATE_InLevel:
847 if (level.cdtrack == 0 || !S_ChangeCDMusic (level.cdtrack, level.cdid))
848 S_ChangeMusic (level.Music, level.musicorder);
849 gamestate = GS_LEVEL;
850 wipegamestate = GS_LEVEL;
851 P_ResumeConversation ();
852 viewactive = true;
853 Destroy();
854 break;
855
856 case FSTATE_ChangingLevel:
857 gameaction = ga_worlddone;
858 Destroy();
859 break;
860
861 default:
862 break;
863 }
864 }
865 }
866 }
867
Drawer()868 void DIntermissionController::Drawer ()
869 {
870 if (mScreen != NULL)
871 {
872 mScreen->Drawer();
873 }
874 }
875
Destroy()876 void DIntermissionController::Destroy ()
877 {
878 Super::Destroy();
879 if (mScreen != NULL) mScreen->Destroy();
880 if (mDeleteDesc) delete mDesc;
881 mDesc = NULL;
882 if (CurrentIntermission == this) CurrentIntermission = NULL;
883 }
884
885
886 //==========================================================================
887 //
888 // starts a new intermission
889 //
890 //==========================================================================
891
F_StartIntermission(FIntermissionDescriptor * desc,bool deleteme,BYTE state)892 void F_StartIntermission(FIntermissionDescriptor *desc, bool deleteme, BYTE state)
893 {
894 if (DIntermissionController::CurrentIntermission != NULL)
895 {
896 DIntermissionController::CurrentIntermission->Destroy();
897 }
898 V_SetBlend (0,0,0,0);
899 S_StopAllChannels ();
900 gameaction = ga_nothing;
901 gamestate = GS_FINALE;
902 if (state == FSTATE_InLevel) wipegamestate = GS_FINALE; // don't wipe when within a level.
903 viewactive = false;
904 automapactive = false;
905 DIntermissionController::CurrentIntermission = new DIntermissionController(desc, deleteme, state);
906 GC::WriteBarrier(DIntermissionController::CurrentIntermission);
907 }
908
909
910 //==========================================================================
911 //
912 // starts a new intermission
913 //
914 //==========================================================================
915
F_StartIntermission(FName seq,BYTE state)916 void F_StartIntermission(FName seq, BYTE state)
917 {
918 FIntermissionDescriptor **pdesc = IntermissionDescriptors.CheckKey(seq);
919 if (pdesc != NULL)
920 {
921 F_StartIntermission(*pdesc, false, state);
922 }
923 }
924
925
926 //==========================================================================
927 //
928 // Called by main loop.
929 //
930 //==========================================================================
931
F_Responder(event_t * ev)932 bool F_Responder (event_t* ev)
933 {
934 if (DIntermissionController::CurrentIntermission != NULL)
935 {
936 return DIntermissionController::CurrentIntermission->Responder(ev);
937 }
938 return false;
939 }
940
941 //==========================================================================
942 //
943 // Called by main loop.
944 //
945 //==========================================================================
946
F_Ticker()947 void F_Ticker ()
948 {
949 if (DIntermissionController::CurrentIntermission != NULL)
950 {
951 DIntermissionController::CurrentIntermission->Ticker();
952 }
953 }
954
955 //==========================================================================
956 //
957 // Called by main loop.
958 //
959 //==========================================================================
960
F_Drawer()961 void F_Drawer ()
962 {
963 if (DIntermissionController::CurrentIntermission != NULL)
964 {
965 DIntermissionController::CurrentIntermission->Drawer();
966 }
967 }
968
969
970 //==========================================================================
971 //
972 // Called by main loop.
973 //
974 //==========================================================================
975
F_EndFinale()976 void F_EndFinale ()
977 {
978 if (DIntermissionController::CurrentIntermission != NULL)
979 {
980 DIntermissionController::CurrentIntermission->Destroy();
981 DIntermissionController::CurrentIntermission = NULL;
982 }
983 }
984
985 //==========================================================================
986 //
987 // Called by net loop.
988 //
989 //==========================================================================
990
F_AdvanceIntermission()991 void F_AdvanceIntermission()
992 {
993 if (DIntermissionController::CurrentIntermission != NULL)
994 {
995 DIntermissionController::CurrentIntermission->mAdvance = true;
996 }
997 }
998
999