12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451 |
- #include <string.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <assert.h>
- #include <io.h>
- #include <fcntl.h>
- #include <malloc.h>
- #include "engine.h"
- #include "multi.h"
- #include "typedefs.h"
- #include "misc.h"
- #include "gameutil.h"
- #include "globals.h"
- #include "names.h"
- #include "key.h"
- #include "timer.h"
- #include "textio.h"
- #include "inifile.h"
- #include "screen.h"
- #include "view.h"
- #include "db.h"
- #include "debug4g.h"
- #include "error.h"
- #include "fire.h"
- #include "sectorfx.h"
- #include "helix.h"
- #include "getopt.h"
- #include "levels.h"
- #include "sound.h"
- #include "controls.h"
- #include "actor.h"
- #include "player.h"
- #include "resource.h"
- #include "protect.h"
- #include "triggers.h"
- #include "weapon.h"
- #include "options.h"
- #include "tile.h"
- #include "gui.h"
- #include "seq.h"
- #include "trig.h"
- #include "mirrors.h"
- #include "warp.h"
- #include "credits.h"
- #include "sfx.h"
- #include <memcheck.h>
- #define kAttrTitle (kColorRed * 16 + kColorYellow)
- EHF prevErrorHandler;
- static BOOL gForceMap = FALSE;
- short gPacketIndex = 0;
- BOOL ready2send = FALSE;
- static char playerreadyflag[kMaxPlayers];
- #define kNetFifoSize 256
- INPUT gNetInput[kMaxPlayers]; // input received from net players
- INPUT gFifoInput[kNetFifoSize][kMaxPlayers];
- int gNetFifoClock;
- int gNetFifoHead, gNetFifoTail;
- ulong gFifoSendCRC[kNetFifoSize], gFifoReceiveCRC[kNetFifoSize];
- int gSendCRCHead, gReceiveCRCHead, gCRCTail;
- BOOL bOutOfSync = FALSE;
- /***********************************************************************
- * Function prototypes
- **********************************************************************/
- enum {
- // kTweakDecel = 0,
- // kTweakSideDecel,
- kTweakFrontAccel,
- kTweakSideAccel,
- kTweakBackAccel,
- kTweakFrontSpeed1,
- kTweakFrontSpeed2,
- kTweakSideSpeed1,
- kTweakSideSpeed2,
- kTweakBackSpeed1,
- kTweakBackSpeed2,
- kTweakPace1,
- kTweakPace2,
- kMaxTweaks
- };
- struct TWEAKS
- {
- int x;
- int y;
- int *pData;
- int nDefault;
- } tweakSet[ kMaxTweaks ] =
- {
- // {2, 2, &gPosture[0].decel, 0},
- // {2, 20, &gPosture[0].sideDecel, 0},
- {2, 38, &gPosture[0].frontAccel, 0},
- {2, 56, &gPosture[0].sideAccel, 0},
- {2, 74, &gPosture[0].backAccel, 0},
- {2, 92, &gPosture[0].frontSpeed[0], 0},
- {2, 110, &gPosture[0].frontSpeed[1], 0},
- {2, 128, &gPosture[0].sideSpeed[0], 0},
- {2, 146, &gPosture[0].sideSpeed[1], 0},
- {2, 164, &gPosture[0].backSpeed[0], 0},
- {90, 2, &gPosture[0].backSpeed[1], 0},
- {90, 20, &gPosture[0].pace[0], 0},
- {90, 38, &gPosture[0].pace[1], 0},
- };
- void TweakProfile( char *title )
- {
- int i;
- EditNumber *enTweaks[kMaxTweaks];
- // create the dialog
- Window dialog(0, 0, 319, 199, title);
- // these will automatically be destroyed when the dialog is destroyed
- for (i = 0; i < kMaxTweaks; i++)
- {
- tweakSet[i].nDefault = *tweakSet[i].pData; // initialize the defaults
- enTweaks[i] = new EditNumber(tweakSet[i].x, tweakSet[i].y, 80, 16, tweakSet[i].nDefault);
- dialog.Insert(enTweaks[i]);
- }
- ShowModal(&dialog);
- for (i = 0; i < kMaxTweaks; i++)
- {
- if ( dialog.endState != mrOk)
- *tweakSet[i].pData = tweakSet[i].nDefault; // restore the defaults
- else
- *tweakSet[i].pData = enTweaks[i]->value;
- }
- }
- void PreloadCache( void )
- {
- long i;
- dprintf("Preload floor and ceiling tiles\n");
- for (i = 0; i < numsectors; i++)
- {
- tilePreloadTile(sector[i].floorpicnum);
- tilePreloadTile(sector[i].ceilingpicnum);
- if ( sector[i].ceilingstat & kSectorParallax )
- {
- for (int j = 1; j < gSkyCount; j++)
- tilePreloadTile(sector[i].ceilingpicnum + j);
- }
- }
- dprintf("Preload wall tiles\n");
- for (i = 0; i < numwalls; i++)
- {
- tilePreloadTile(wall[i].picnum);
- if (wall[i].overpicnum >= 0)
- tilePreloadTile(wall[i].overpicnum);
- }
- dprintf("Preload sprite tiles\n");
- for (i = 0; i < kMaxSprites; i++)
- {
- if (sprite[i].statnum < kMaxStatus)
- tilePreloadTile(sprite[i].picnum);
- }
- }
- void prepareboard( void )
- {
- if ( !gForceMap )
- levelGetName(gEpisodeId, gMapId);
- dbLoadMap(gLevelName, &startposx, &startposy, &startposz, &startang, &startsectnum);
- srand(gMapCRC);
- levelLoadDef();
- scrLoadPLUs();
- for ( int i = 0; i < kMaxPlayers; i++ )
- {
- gStartZone[i].x = startposx;
- gStartZone[i].y = startposy;
- gStartZone[i].z = sector[startsectnum].floorz;
- gStartZone[i].sector = startsectnum;
- gStartZone[i].angle = startang;
- }
- // make sure the player starts out on the floor
- startposz = sector[startsectnum].floorz;
- seqKillAll(); // clear the current sequence list
- InitSectorFX();
- InitPlayerStartZones();
- actInit();
- // initialize all the tag buckets
- dprintf("evInit()\n");
- evInit();
- gFrameClock = gGameClock = 0;
- // initialize external trigger masks for sprites, sectors, and walls
- dprintf("trInit()\n");
- trInit();
- PreloadCache();
- //Map starts out blank; you map out what you see in 3D mode
- automapping = 1;
- for (i = 0; i < numplayers; i++)
- playerInit( i );
- InitMirrors();
- // start the music for the level
- sndPlaySong(gLevelSong, TRUE);
- gNetFifoClock = gFrameClock = gGameClock = 0;
- gNetFifoHead = gNetFifoTail = 0;
- gSendCRCHead = gReceiveCRCHead = gCRCTail = 0;
- }
- BYTE cheatAmmo[] = { KEY_Q, KEY_S, KEY_A, KEY_M, KEY_M, KEY_O };
- BYTE cheatRate[] = { KEY_Q, KEY_S, KEY_R, KEY_A, KEY_T, KEY_E };
- BYTE cheatGoto[] = { KEY_Q, KEY_S, KEY_G, KEY_O, KEY_T, KEY_O };
- BYTE cheatJump[] = { KEY_Q, KEY_S, KEY_J, KEY_U, KEY_M, KEY_P };
- BYTE cheatClip[] = { KEY_Q, KEY_S, KEY_C, KEY_L, KEY_I, KEY_P };
- BYTE cheatKeys[] = { KEY_Q, KEY_S, KEY_K, KEY_E, KEY_Y, KEY_S };
- BYTE cheatMaps[] = { KEY_Q, KEY_S, KEY_M, KEY_A, KEY_P, KEY_S };
- BYTE cheatHurt[] = { KEY_Q, KEY_S, KEY_O, KEY_U, KEY_C, KEY_H };
- BYTE cheatExplode[] = { KEY_Q, KEY_S, KEY_B, KEY_O, KEY_O, KEY_M };
- BYTE cheatBurn[] = { KEY_Q, KEY_S, KEY_B, KEY_U, KEY_R, KEY_N };
- BYTE cheatHigh[] = { KEY_Q, KEY_S, KEY_H, KEY_I, KEY_G, KEY_H };
- BYTE cheatInviso[] = { KEY_Q, KEY_S, KEY_O, KEY_N, KEY_E, KEY_R, KEY_I, KEY_N, KEY_G };
- BYTE cheatBeast[] = { KEY_Q, KEY_S, KEY_L, KEY_U, KEY_S, KEY_T };
- BYTE cheatHealth[] = { KEY_Q, KEY_S, KEY_L, KEY_A, KEY_M, KEY_E };
- BYTE cheatSeer[] = { KEY_Q, KEY_S, KEY_I, KEY_C, KEY_U };
- BYTE cheatFrag[] = { KEY_Q, KEY_S, KEY_F, KEY_R, KEY_A, KEY_G };
- BYTE cheatGod[] = { KEY_Q, KEY_S, KEY_G, KEY_O, KEY_D };
- BYTE cheatSight[] = { KEY_Q, KEY_S, KEY_O, KEY_S, KEY_C , KEY_A , KEY_R };
- BYTE cheatAim[] = { KEY_Q, KEY_S, KEY_A, KEY_I, KEY_M };
- void LocalKeys( void )
- {
- char buffer[80];
- BYTE key;
- BYTE alt = keystatus[KEY_LALT] | keystatus[KEY_RALT];
- while ( (key = keyGet()) != 0 )
- {
- switch( key )
- {
- case KEY_F1: // help/info screens
- TweakProfile( "Player Profile" );
- break;
- case KEY_F2: // save game
- break;
- case KEY_F3: // load game
- break;
- case KEY_F4: // volume
- break;
- case KEY_F5: // detail
- break;
- case KEY_F6: // quicksave
- break;
- case KEY_F7: // end game
- break;
- case KEY_F8: // messages
- break;
- case KEY_F10: // quit game
- break;
- case KEY_F11: // gamma
- if (gGamma == gGammaLevels - 1)
- gGamma = 0;
- else
- gGamma = ClipHigh(gGamma + 1, gGammaLevels - 1);
- BloodINI.PutKeyInt("View", "Gamma", gGamma);
- scrSetGamma(gGamma);
- sprintf(buffer, "Gamma correction level %i", gGamma);
- viewSetMessage(buffer);
- break;
- case KEY_F12:
- // setup gViewIndex for different play and power-up modes
- if ( gNetGame && gNetPlayers > 1 )
- {
- if ( powerupCheck(gMe, kItemCrystalBall - kItemBase) )
- {
- gViewIndex = connectpoint2[gViewIndex];
- if (gViewIndex == -1)
- gViewIndex = connecthead;
- }
- else
- {
- if (gNetMode == kNetModeTeams)
- {
- gViewIndex = connectpoint2[gViewIndex];
- if (gViewIndex == -1)
- gViewIndex = connecthead;
- while ( gPlayer[gViewIndex].teamID != gMe->teamID )
- {
- gViewIndex = connectpoint2[gViewIndex];
- if (gViewIndex == -1)
- gViewIndex = connecthead;
- }
- gView = &gPlayer[gViewIndex];
- }
- }
- }
- else
- {
- gViewIndex = connectpoint2[gViewIndex];
- if (gViewIndex == -1)
- gViewIndex = connecthead;
- gView = &gPlayer[gViewIndex];
- }
- break;
- case KEY_MINUS:
- if ( keystatus[KEY_G] )
- {
- gGamma = ClipLow(gGamma - 1, 0);
- BloodINI.PutKeyInt("View", "Gamma", gGamma);
- scrSetGamma(gGamma);
- sprintf(buffer, "Gamma correction level %i", gGamma);
- viewSetMessage(buffer);
- }
- else if ( keystatus[KEY_D] )
- {
- visibility = ClipHigh(visibility + 16, 4096);
- sprintf(buffer, "Depth cueing level %i", visibility);
- viewSetMessage(buffer);
- }
- else if ( keystatus[KEY_S] )
- {
- gSpring = ClipLow(gSpring - 1, 1);
- BloodINI.PutKeyInt("Player", "SpringCoefficient", gSpring);
- gSpringPhaseInc = (int)(kAngle360 / (2 * 3.141592654 * (double)gSpring));
- sprintf(buffer, "Spring coefficient = %i", gSpring);
- viewSetMessage(buffer);
- }
- else if (gViewMode == kView3D)
- {
- viewResizeView(gViewSize + 1);
- BloodINI.PutKeyInt("View", "Size", gViewSize);
- }
- break;
- case KEY_PLUS:
- if ( keystatus[KEY_G] )
- {
- gGamma = ClipHigh(gGamma + 1, gGammaLevels - 1);
- BloodINI.PutKeyInt("View", "Gamma", gGamma);
- scrSetGamma(gGamma);
- sprintf(buffer, "Gamma correction level %i", gGamma);
- viewSetMessage(buffer);
- }
- else if ( keystatus[KEY_D] )
- {
- visibility = ClipLow(visibility - 16, 128);
- sprintf(buffer, "Depth cueing level %i", visibility);
- viewSetMessage(buffer);
- }
- else if ( keystatus[KEY_S])
- {
- gSpring++;
- BloodINI.PutKeyInt("Player", "SpringCoefficient", gSpring);
- gSpringPhaseInc = (int)(kAngle360 / (2 * 3.141592654 * (double)gSpring));
- sprintf(buffer, "Spring coefficient = %i", gSpring);
- viewSetMessage(buffer);
- }
- else if (gViewMode == kView3D)
- {
- viewResizeView(gViewSize - 1);
- BloodINI.PutKeyInt("View", "Size", gViewSize);
- }
- break;
- case KEY_P:
- parallaxtype = (char)IncRotate(parallaxtype, 3);
- break;
- case KEY_V:
- if ( alt )
- {
- if ( gViewPos > 0 )
- gViewPos = (VIEWPOS)((gViewPos & 7) + 1);
- sprintf(buffer, "gViewPos = %i", gViewPos);
- viewSetMessage(buffer);
- }
- else
- {
- if ( gViewPos > 0 )
- gViewPos = kViewPosCenter;
- else
- gViewPos = kViewPosBack;
- }
- break;
- case KEY_PRINTSCREEN:
- screencapture("captxxxx.pcx");
- break;
- default:
- break;
- }
- }
- if (keystatus[KEY_MINUS] && (gViewMode == kView2D || gViewMode == kView2DIcon))
- {
- gZoom = ClipLow(gZoom - (gZoom >> 6), 64);
- }
- if (keystatus[KEY_PLUS] && (gViewMode == kView2D || gViewMode == kView2DIcon))
- {
- gZoom = ClipHigh(gZoom + (gZoom >> 6), 4096);
- }
- int nDelirium = powerupCheck(gView, kItemShroomDelirium - kItemBase);
- if ( nDelirium )
- {
- static int timer = 0;
- timer += kFrameTicks;
- int maxTilt = kAngle30;
- int maxTurn = kAngle30;
- int maxPitch = 20;
- // this constant determine when the effect starts to wear off
- #define kWaneTime 512
- if (nDelirium < kWaneTime)
- {
- int scale = (nDelirium << 16) / kWaneTime;
- maxTilt = mulscale16(maxTilt, scale);
- maxTurn = mulscale16(maxTurn, scale);
- maxPitch = mulscale16(maxPitch, scale);
- }
- gScreenTilt = mulscale30(Sin(timer * 2) / 2 + Sin(timer * 3) / 2, maxTilt);
- deliriumTurn = mulscale30(Sin(timer * 3) / 2 + Sin(timer * 4) / 2, maxTurn);
- deliriumPitch = mulscale30(Sin(timer * 4) / 2 + Sin(timer * 5) / 2, maxPitch);
- }
- else
- {
- // this code needs a different calculation,
- // and should probably be partially placed in ctrlGetInput
- if ( keystatus[KEY_LBRACE] )
- gScreenTilt += kFrameTicks * 4;
- if ( keystatus[KEY_RBRACE] )
- gScreenTilt -= kFrameTicks * 4;
- // put gScreenTilt in the range -kAngle180 to kAngle180
- gScreenTilt = ((gScreenTilt + kAngle180) & kAngleMask) - kAngle180;
- // pull screen to vertical
- if ( gScreenTilt > 0 )
- gScreenTilt = ClipLow(gScreenTilt - kFrameTicks * 2, 0);
- else if ( gScreenTilt < 0 )
- gScreenTilt = ClipHigh(gScreenTilt + kFrameTicks * 2, 0);
- }
- if ( keyCompareStream( cheatRate, LENGTH(cheatRate)) )
- {
- keyPokeStream(0);
- gShowFrameRate = !gShowFrameRate;
- }
- if ( keyCompareStream( cheatFrag, LENGTH(cheatFrag)) )
- {
- keyPokeStream(0);
- gShowFrags = !gShowFrags;
- }
- if ( keyCompareStream( cheatAim, LENGTH(cheatAim)) )
- {
- keyPokeStream(0);
- gAimReticle = !gAimReticle;
- }
- if ( gNetGame == FALSE )
- {
- if ( keyCompareStream( cheatAmmo, LENGTH(cheatAmmo)) )
- {
- keyPokeStream(0);
- for (int i = 0; i < kWeaponMax; i++)
- {
- gMe->hasWeapon[i] = TRUE;
- //gMe->weaponState[i] = 0;
- }
- for (i = 0; i < kAmmoMax; i++)
- {
- gMe->ammoCount[i] = gAmmoInfo[i].max;
- }
- viewSetMessage("Full armory granted!");
- }
- if ( keyCompareStream( cheatKeys, LENGTH(cheatKeys)) )
- {
- keyPokeStream(0);
- for (int i = 1; i <= 6; i++)
- gMe->hasKey[i] = (short)(kPicKey1 + i - 1);
- viewSetMessage("Full keychain granted!");
- }
- if ( keyCompareStream( cheatMaps, LENGTH(cheatMaps)) )
- {
- keyPokeStream(0);
- gFullMap = !gFullMap;
- if ( gFullMap )
- viewSetMessage("Full atlas granted");
- else
- viewSetMessage("Full atlas revoked");
- }
- if ( keyCompareStream( cheatClip, LENGTH(cheatClip)) )
- {
- keyPokeStream(0);
- gNoClip = !gNoClip;
- if ( gNoClip )
- viewSetMessage("Non-clipped mode enabled");
- else
- viewSetMessage("Non-clipped mode disabled");
- }
- if ( keyCompareStream( cheatGoto, LENGTH(cheatGoto)) )
- {
- keyPokeStream(0);
- long x = GetNumberBox("X=", gMe->sprite->x, gMe->sprite->x);
- long y = GetNumberBox("Y=", gMe->sprite->y, gMe->sprite->y);
- short nSector;
- updatesector(x, y, &nSector);
- if (nSector != -1) {
- changespritesect((short)gMe->nSprite, nSector);
- gMe->sprite->x = x;
- gMe->sprite->y = y;
- gMe->sprite->z = sector[nSector].floorz;
- gMe->sprite->zvel = 0;
- }
- }
- if ( keyCompareStream( cheatJump, LENGTH(cheatJump)) )
- {
- keyPokeStream(0);
- long newEpisodeId = GetNumberBox("Episode (1..3)=", gEpisodeId, 1);
- long newMapId = GetNumberBox("Map (1..15)=", gMapId, 1);
- if ((newEpisodeId != 0) && (newMapId != 0)) {
- gEpisodeId = newEpisodeId;
- gMapId = newMapId;
- gForceMap = FALSE;
- ready2send = FALSE;
- prepareboard();
- ready2send = TRUE;
- }
- viewResizeView(gViewSize);
- }
- if ( keyCompareStream( cheatHurt, LENGTH(cheatHurt)) )
- {
- keyPokeStream(0);
- actDamageSprite(gMe->nSprite, gMe->nSprite, kDamagePummel, 100 << 4);
- viewSetMessage("Ouch!");
- }
- if ( keyCompareStream( cheatExplode, LENGTH(cheatExplode)) )
- {
- keyPokeStream(0);
- actDamageSprite(gMe->nSprite, gMe->nSprite, kDamageExplode, 100 << 4);
- viewSetMessage("Boom.");
- }
- if ( keyCompareStream( cheatBurn, LENGTH(cheatBurn)) )
- {
- keyPokeStream(0);
- actAddBurnTime( gMe->nSprite, gMe->xsprite, kMaxBurnTime );
- viewSetMessage("Crispy!");
- }
- if ( keyCompareStream( cheatHigh, LENGTH(cheatHigh)) )
- {
- keyPokeStream(0);
- if ( powerupCheck( gMe, kItemShroomDelirium - kItemBase ) )
- powerupDeactivate( gMe, kItemShroomDelirium - kItemBase );
- else
- powerupActivate( gMe, kItemShroomDelirium - kItemBase );
- viewSetMessage("Cosmic, man!");
- }
- if ( keyCompareStream( cheatGod, LENGTH(cheatGod)) )
- {
- keyPokeStream(0);
- playerSetGodMode( gMe, !gMe->godMode );
- if (gMe->godMode)
- viewSetMessage("Entering Broussard mode!");
- else
- viewSetMessage("Back to normal mode!");
- }
- if ( keyCompareStream( cheatInviso, LENGTH(cheatInviso)) )
- {
- keyPokeStream(0);
- if ( powerupCheck( gMe, kItemLtdInvisibility - kItemBase ) )
- {
- viewSetMessage("Deactivating the One Ring");
- powerupDeactivate( gMe, kItemLtdInvisibility - kItemBase );
- }
- else
- {
- viewSetMessage("Activating the One Ring");
- powerupActivate( gMe, kItemLtdInvisibility - kItemBase );
- }
- }
- if ( keyCompareStream( cheatSeer, LENGTH(cheatSeer)) )
- {
- keyPokeStream(0);
- if ( powerupCheck( gMe, kItemCrystalBall - kItemBase ) )
- {
- viewSetMessage("Deactivating the Crystal Ball");
- powerupDeactivate( gMe, kItemCrystalBall - kItemBase );
- }
- else
- {
- viewSetMessage("Activating the Crystal Ball");
- powerupActivate( gMe, kItemCrystalBall - kItemBase );
- }
- }
- if ( keyCompareStream( cheatBeast, LENGTH(cheatBeast)) )
- {
- keyPokeStream(0);
- if (gMe->lifemode == kModeBeast)
- playerSetRace( gMe, kModeHuman, gMe->sprite->type );
- else
- playerSetRace( gMe, kModeBeast, gMe->sprite->type );
- }
- if ( keyCompareStream( cheatHealth, LENGTH(cheatHealth)) )
- {
- keyPokeStream(0);
- gMe->xsprite->health = 200 << 4;
- viewSetMessage("Full health granted, you maggot!");
- }
- if ( keyCompareStream( cheatSight, LENGTH(cheatSight)) )
- {
- keyPokeStream(0);
- if ( powerupCheck( gMe, kItemRoseGlasses - kItemBase ) )
- {
- viewSetMessage("Deactivating Oscar Sight");
- powerupDeactivate( gMe, kItemRoseGlasses - kItemBase );
- }
- else
- {
- viewSetMessage("Activating Oscar Sight");
- powerupActivate( gMe, kItemRoseGlasses - kItemBase );
- }
- }
- }
- if (*control[kAutomapToggle])
- {
- *control[kAutomapToggle] = 0;
- if (gViewMode == kView3D)
- {
- gViewMode = kView2D;
- }
- else if (gViewMode == kView2D)
- {
- gViewMode = kView2DIcon;
- }
- else
- {
- gViewMode = kView3D;
- viewResizeView(gViewSize);
- }
- }
- if (gNetPlayers == 1) //Single player only keys
- {
- if (keystatus[KEY_INSERT]) //Insert - Insert player
- {
- keystatus[KEY_INSERT] = 0;
- if (numplayers < kMaxPlayers)
- {
- connectpoint2[numplayers-1] = numplayers;
- connectpoint2[numplayers] = -1;
- gViewIndex = myconnectindex = numplayers++;
- gView = gMe = &gPlayer[myconnectindex];
- playerInit( myconnectindex );
- }
- }
- if (keystatus[KEY_DELETE]) //Delete - Delete player
- {
- keystatus[KEY_DELETE] = 0;
- if (numplayers > 1)
- {
- // actDeletePlayer( myconnectindex ); // should probably add something like this!
- numplayers--;
- connectpoint2[numplayers-1] = -1;
- seqKill( SS_SPRITE, gPlayer[numplayers].sprite->extra );
- actPostSprite( gPlayer[numplayers].nSprite, kStatFree );
- gPlayer[numplayers].sprite = NULL;
- if (myconnectindex >= numplayers) myconnectindex = 0;
- gMe = &gPlayer[myconnectindex];
- if (gViewIndex >= numplayers) gViewIndex = 0;
- gView = &gPlayer[gViewIndex];
- }
- }
- if (keystatus[KEY_SCROLLLOCK]) //Scroll Lock
- {
- keystatus[KEY_SCROLLLOCK] = 0;
- myconnectindex = connectpoint2[myconnectindex];
- if (myconnectindex < 0) myconnectindex = connecthead;
- gViewIndex = myconnectindex;
- gView = gMe = &gPlayer[myconnectindex];
- }
- }
- }
- ulong CalcGameChecksum( void )
- {
- ulong value = 0;
- value = Random(65536);
- // dprintf("Frame #%d: randCount1 = %d, randCount2 = %d, Random() = %4x\n",
- // gFrame, randCount1, randCount2, value);
- //value ^= CRC32(gPlayer, sizeof(gPlayer));
- for ( int i = connecthead; i >= 0; i = connectpoint2[i] )
- value ^= CRC32(gPlayer[i].sprite, sizeof(SPRITE));
- return value;
- }
- /*
- void CheckMasterSwitch( void )
- {
- int i, j;
- i = connecthead; j = connectpoint2[i];
- while (j >= 0)
- {
- if (gPlayer[j].keyFlags.master)
- {
- connectpoint2[i] = connectpoint2[j];
- connectpoint2[j] = connecthead;
- connecthead = (short)j;
- gGameClock = gFrameClock;
- if (connecthead == myconnectindex)
- viewSetMessage("Master");
- else
- viewSetMessage("Slave");
- gPlayer[j].keyFlags.master = 0;
- return;
- }
- i = j; j = connectpoint2[i];
- }
- }
- */
- /*******************************************************************************
- FUNCTION: ProcessFrame()
- DESCRIPTION: Main game processing function
- *******************************************************************************/
- void ProcessFrame()
- {
- for ( int i = connecthead; i >= 0; i = connectpoint2[i] )
- {
- gPlayerInput[i].syncFlags = gFifoInput[gNetFifoHead][i].syncFlags;
- gPlayerInput[i].buttonFlags = gFifoInput[gNetFifoHead][i].buttonFlags;
- // keys and weapon selection are non-destructive
- gPlayerInput[i].keyFlags.byte |= gFifoInput[gNetFifoHead][i].keyFlags.byte;
- if (gFifoInput[gNetFifoHead][i].newWeapon != 0)
- gPlayerInput[i].newWeapon = gFifoInput[gNetFifoHead][i].newWeapon;
- gPlayerInput[i].forward = gFifoInput[gNetFifoHead][i].forward;
- gPlayerInput[i].strafe = gFifoInput[gNetFifoHead][i].strafe;
- gPlayerInput[i].turn = gFifoInput[gNetFifoHead][i].turn;
- gPlayerInput[i].ticks = gFifoInput[gNetFifoHead][i].ticks;
- }
- gNetFifoHead = IncRotate(gNetFifoHead, kNetFifoSize);
- gFifoSendCRC[gSendCRCHead] = CalcGameChecksum();
- gSendCRCHead = IncRotate(gSendCRCHead, kNetFifoSize);
- for (i = connecthead; i >= 0; i = connectpoint2[i])
- {
- if ( gPlayerInput[i].keyFlags.restart )
- {
- gPlayerInput[i].keyFlags.restart = 0;
- gForceMap = TRUE;
- gPaused = FALSE;
- ready2send = FALSE;
- prepareboard();
- ready2send = TRUE;
- }
- if ( gPlayerInput[i].keyFlags.pause )
- {
- gPlayerInput[i].keyFlags.pause = 0;
- gPaused = !gPaused;
- }
- }
- if (gPaused)
- return;
- for (i = connecthead; i >= 0; i = connectpoint2[i])
- viewBackupPlayerLoc(i);
- trProcessBusy(); // process busy triggers
- evProcess(gFrameClock); // process event queue
- seqProcess(kFrameTicks);
- for (i = connecthead; i >= 0; i = connectpoint2[i])
- {
- powerupProcess(&gPlayer[i]); // process the power-ups
- playerMove(&gPlayer[i], &gPlayerInput[i]); // Move player
- }
- actProcessSprites(); // Actor code: projectiles, etc.
- actPostProcess(); // clean up deleted sprites or sprites changing status
- sndProcess();
- sfxUpdate3DSounds();
- gFrame++;
- gFrameClock += kFrameTicks;
- }
- void UpdateNetFifo( void )
- {
- for ( int i = connecthead; i >= 0; i = connectpoint2[i] )
- {
- gFifoInput[gNetFifoTail][i] = gNetInput[i];
- }
- gNetFifoTail = IncRotate(gNetFifoTail, kNetFifoSize);
- // CheckMasterSwitch();
- }
- char packet[576];
- inline char GetPacketByte(char * &p)
- { return *p++; }
- inline PutPacketByte(char * &p, int b)
- { *p++ = (char)b; }
- inline short GetPacketShort(char * &p)
- {
- short w = *(short *)p;
- p += 2;
- return w;
- }
- inline PutPacketShort(char * &p, int w)
- {
- *(short *)p = (short)w;
- p += 2;
- }
- inline long GetPacketLong(char * &p)
- {
- long l = *(long *)p;
- p += 4;
- return l;
- }
- inline PutPacketLong(char * &p, int l)
- {
- *(long *)p = (long)l;
- p += 4;
- }
- int GetPackets( void )
- {
- short nPlayer, length;
- long i, j;
- char *p;
- int n = 0;
- int nMoveCount = 0;
- while ( (length = getpacket(&nPlayer, packet)) > 0)
- {
- n++;
- p = packet;
- switch( GetPacketByte(p) )
- {
- case 0: // receive master sync buffer
- j = GetPacketShort(p);
- if (j != gPacketIndex++)
- ThrowError("Missing master packet", ES_ERROR);
- for (i = connecthead; i >= 0; i = connectpoint2[i])
- {
- SYNCFLAGS syncFlags;
- syncFlags.byte = GetPacketByte(p);
- gNetInput[i].syncFlags.byte = syncFlags.byte;
- if (syncFlags.buttonChange)
- gNetInput[i].buttonFlags.byte = GetPacketByte(p);
- if (syncFlags.keyChange)
- gNetInput[i].keyFlags.byte = GetPacketByte(p);
- if (syncFlags.weaponChange)
- gNetInput[i].newWeapon = GetPacketByte(p);
- if (syncFlags.forwardChange)
- gNetInput[i].forward = GetPacketByte(p);
- if (syncFlags.turnChange)
- gNetInput[i].turn = GetPacketShort(p);
- if (syncFlags.strafeChange)
- gNetInput[i].strafe = GetPacketByte(p);
- gNetInput[i].ticks = GetPacketByte(p);
- }
- while ( p < &packet[length] )
- {
- gFifoReceiveCRC[gReceiveCRCHead] = GetPacketLong(p);
- gReceiveCRCHead = IncRotate(gReceiveCRCHead, kNetFifoSize);
- }
- if ( gReceiveCRCHead != gCRCTail && gSendCRCHead != gCRCTail )
- {
- bOutOfSync = FALSE;
- do
- {
- if ( gFifoReceiveCRC[gCRCTail] != gFifoSendCRC[gCRCTail] )
- {
- dprintf("SYNC: gFifoReceiveCRC[%d] = %8x, gFifoSendCRC[%d] = %8x\n",
- gCRCTail, gFifoReceiveCRC[gCRCTail], gCRCTail, gFifoSendCRC[gCRCTail]);
- bOutOfSync = TRUE;
- }
- gCRCTail = IncRotate(gCRCTail, kNetFifoSize);
- }
- while ( gReceiveCRCHead != gCRCTail && gSendCRCHead != gCRCTail );
- }
- UpdateNetFifo();
- nMoveCount++;
- continue;
- case 1: // receive slave sync buffer
- SYNCFLAGS syncFlags;
- syncFlags.byte = GetPacketByte(p);
- gNetInput[nPlayer].syncFlags.byte = syncFlags.byte;
- if (syncFlags.buttonChange)
- gNetInput[nPlayer].buttonFlags.byte = GetPacketByte(p);
- if (syncFlags.keyChange)
- gNetInput[nPlayer].keyFlags.byte = GetPacketByte(p);
- if (syncFlags.weaponChange)
- gNetInput[nPlayer].newWeapon = GetPacketByte(p);
- if (syncFlags.forwardChange)
- gNetInput[nPlayer].forward = GetPacketByte(p);
- if (syncFlags.turnChange)
- gNetInput[nPlayer].turn = GetPacketShort(p);
- if (syncFlags.strafeChange)
- gNetInput[nPlayer].strafe = GetPacketByte(p);
- gNetInput[nPlayer].ticks = GetPacketByte(p);
- continue;
- case 250:
- playerreadyflag[nPlayer] = GetPacketByte(p);
- // if the message came from the master, acknowledge it
- if ( nPlayer == connecthead && playerreadyflag[connecthead] == 2 )
- sendpacket(connecthead, packet, 2);
- break;
- case 255: //[255] (logout)
- keystatus[1] = 1;
- break;
- }
- break;
- }
- // try to prevent slave and master from getting into phase lock-step
- if ( myconnectindex != connecthead && nMoveCount != 1 )
- {
- if (nMoveCount == 2)
- dprintf("%4d: Received 2 packets from master\n", gPacketIndex);
- // if (nMoveCount & 2)
- // gNetFifoClock += kFrameTicks / 2;
- // else
- // gNetFifoClock -= kFrameTicks / 2;
- }
- return n;
- }
- void GetInput( void )
- {
- if ( gNetPlayers > 1 )
- GetPackets();
- // this doesn't currently do anything but Ken might add to this later
- if (getoutputcirclesize() >= 16)
- return;
- ctrlGetInput();
- char *p = packet;
- // single player or master
- if (gNetPlayers == 1 || myconnectindex == connecthead)
- {
- // copy in local input
- gNetInput[myconnectindex].syncFlags.byte = gInput.syncFlags.byte;
- if (gInput.syncFlags.buttonChange)
- gNetInput[myconnectindex].buttonFlags.byte = gInput.buttonFlags.byte;
- if (gInput.syncFlags.keyChange)
- gNetInput[myconnectindex].keyFlags.byte = gInput.keyFlags.byte;
- if (gInput.syncFlags.weaponChange)
- gNetInput[myconnectindex].newWeapon = gInput.newWeapon;
- if (gInput.syncFlags.forwardChange)
- gNetInput[myconnectindex].forward = gInput.forward;
- if (gInput.syncFlags.turnChange)
- gNetInput[myconnectindex].turn = gInput.turn;
- if (gInput.syncFlags.strafeChange)
- gNetInput[myconnectindex].strafe = gInput.strafe;
- gNetInput[myconnectindex].ticks = gInput.ticks;
- if ( gNetPlayers > 1 )
- {
- // build master packet
- PutPacketByte(p, 0);
- PutPacketShort(p, gPacketIndex++);
- for (int i = connecthead; i >= 0; i = connectpoint2[i])
- {
- PutPacketByte(p, gNetInput[i].syncFlags.byte);
- if (gNetInput[i].syncFlags.buttonChange)
- PutPacketByte(p, gNetInput[i].buttonFlags.byte);
- if (gNetInput[i].syncFlags.keyChange)
- PutPacketByte(p, gNetInput[i].keyFlags.byte);
- if (gNetInput[i].syncFlags.weaponChange)
- PutPacketByte(p, gNetInput[i].newWeapon);
- if (gNetInput[i].syncFlags.forwardChange)
- PutPacketByte(p, gNetInput[i].forward);
- if (gNetInput[i].syncFlags.turnChange)
- PutPacketShort(p, gNetInput[i].turn);
- if (gNetInput[i].syncFlags.strafeChange)
- PutPacketByte(p, gNetInput[i].strafe);
- PutPacketByte(p, gNetInput[i].ticks);
- gNetInput[i].syncFlags.byte = 0;
- }
- while ( gCRCTail != gSendCRCHead )
- {
- PutPacketLong(p, gFifoSendCRC[gCRCTail]);
- gCRCTail = IncRotate(gCRCTail, kNetFifoSize); // master increment SEND tail
- }
- // send packets to slaves
- for (i = connectpoint2[connecthead]; i >= 0; i = connectpoint2[i])
- sendpacket((short)i, packet, (short)(p - packet));
- }
- UpdateNetFifo();
- }
- else //I am a SLAVE
- {
- // build slave packet
- PutPacketByte(p, 1);
- PutPacketByte(p, gInput.syncFlags.byte);
- if (gInput.syncFlags.buttonChange)
- PutPacketByte(p, gInput.buttonFlags.byte);
- if (gInput.syncFlags.keyChange)
- PutPacketByte(p, gInput.keyFlags.byte);
- if (gInput.syncFlags.weaponChange)
- PutPacketByte(p, gInput.newWeapon);
- if (gInput.syncFlags.forwardChange)
- PutPacketByte(p, gInput.forward);
- if (gInput.syncFlags.turnChange)
- PutPacketShort(p, gInput.turn);
- if (gInput.syncFlags.strafeChange)
- PutPacketByte(p, gInput.strafe);
- PutPacketByte(p, gInput.ticks);
- // send packet to master
- sendpacket((short)connecthead, packet, (short)(p - packet));
- }
- }
- void faketimerhandler( void )
- {
- if ( gGameClock >= gNetFifoClock && ready2send )
- {
- gNetFifoClock += kFrameTicks;
- GetInput();
- }
- }
- BOOL WaitForAllPlayers( void )
- {
- long i, j, oldtotalclock;
- if (numplayers < 2) return TRUE;
- // if I'm the Master
- if (myconnectindex == connecthead)
- {
- for (j = 1; j <= 2; j++)
- {
- for (i = connectpoint2[connecthead]; i >= 0; i = connectpoint2[i])
- playerreadyflag[i] = 0;
- oldtotalclock = gGameClock - 8;
- do
- {
- if (keystatus[1]) return FALSE;
- if ( gNetPlayers > 1 )
- GetPackets();
- if (gGameClock >= oldtotalclock + 8)
- {
- oldtotalclock = gGameClock;
- char *p = packet;
- PutPacketByte(p, 250);
- PutPacketByte(p, j);
- for(i = connectpoint2[connecthead]; i >= 0; i = connectpoint2[i])
- if (playerreadyflag[i] != j)
- sendpacket((short)i, packet, 2);
- }
- for(i = connectpoint2[connecthead]; i >= 0; i = connectpoint2[i])
- if (playerreadyflag[i] != j) break;
- } while (i >= 0);
- }
- }
- else
- {
- playerreadyflag[connecthead] = 0;
- while (playerreadyflag[connecthead] != 2)
- {
- if (keystatus[1]) return FALSE;
- if ( gNetPlayers > 1 )
- GetPackets();
- if (playerreadyflag[connecthead] == 1)
- {
- playerreadyflag[connecthead] = 0;
- sendpacket(connecthead, packet, 2);
- }
- }
- }
- return TRUE;
- }
- ErrorResult GameErrorHandler( const Error& error )
- {
- // shut down the game engine
- if ( gNetGame )
- {
- sendlogoff();
- uninitmultiplayers();
- }
- sndTerm();
- timerRemove();
- ctrlTerm();
- UnlockClockStrobe();
- uninitengine();
- setvmode(gOldDisplayMode);
- // chain to the default error handler
- return prevErrorHandler(error);
- };
- enum
- {
- kSwitchNet,
- kSwitchBloodBath,
- kSwitchTeams,
- };
- SWITCH switches[] =
- {
- { "net", kSwitchNet, TRUE },
- { "bloodbath", kSwitchBloodBath, FALSE },
- { "teams", kSwitchBloodBath, FALSE },
- { NULL, 0, FALSE },
- };
- /***********************************************************************
- * Process command line arguments
- **********************************************************************/
- void ParseOptions( void )
- {
- char buffer[256];
- int r;
- while ( (r = GetOptions(switches)) != GO_EOF )
- {
- switch (r)
- {
- case GO_INVALID:
- sprintf(buffer, "Invalid argument: %s", OptArgument);
- ThrowError(buffer, ES_ERROR);
- case kSwitchNet:
- gNetGame = TRUE;
- dprintf("Net parameter is %s\n", OptArgument);
- break;
- case kSwitchBloodBath:
- // NET PARAMETER MUST COME FIRST: Can you add a CheckParm?
- gNetMode = kNetModeBloodBath;
- dprintf("Bloodbath network mode\n");
- break;
- case kSwitchTeams:
- // NET PARAMETER MUST COME FIRST: Can you add a CheckParm?
- gNetMode = kNetModeTeams;
- dprintf("Team network mode\n");
- break;
- case GO_FULL:
- strcpy(gLevelName, OptArgument);
- gForceMap = TRUE;
- break;
- }
- }
- }
- /***********************************************************************
- * main()
- *
- * It all starts here. Have a Bloody day.
- *
- **********************************************************************/
- void main( /* int argc, char *argv[] */ )
- {
- char title[256];
- gOldDisplayMode = getvmode();
- sprintf(title, "BLOOD ALPHA BUILD [%s] -- DO NOT DISTRIBUTE", gBuildDate);
- tioInit();
- tioCenterString(0, 0, tioScreenCols - 1, title, kAttrTitle);
- tioCenterString(tioScreenRows - 1, 0, tioScreenCols - 1,
- "Copyright (c) 1994, 1995 Q Studios Corporation", kAttrTitle);
- tioWindow(1, 0, tioScreenRows - 2, tioScreenCols);
- if ( _grow_handles(kRequiredFiles) < kRequiredFiles )
- {
- tioPrint("Increase FILES=## value in CONFIG.SYS.");
- ThrowError("Not enough file handles available", ES_ERROR);
- }
- ParseOptions();
- tioPrint("Loading INI file");
- optLoadINI();
- tioPrint("Initializing heap and resource system");
- Resource::heap = new QHeap(dpmiDetermineMaxRealAlloc());
- tioPrint("Initializing resource archive");
- gSysRes.Init("BLOOD.RFF", "*.*");
- gGuiRes.Init("GUI.RFF", NULL);
- CheckDemoDate(gSysRes);
- // install our error handler
- prevErrorHandler = errSetHandler(GameErrorHandler);
- tioPrint("Initializing 3D engine");
- InitEngine();
- tioPrint("Creating standard color lookups");
- scrCreateStdColors();
- tioPrint("Loading tiles");
- if (tileInit() == 0)
- ThrowError("ART files not found", ES_ERROR);
- powerupInit(); // calc zoom table for power-up animations
- tioPrint("Loading cosine table");
- trigInit(gSysRes);
- tioPrint("Initializing screen");
- scrInit();
- tioPrint("Initializing view subsystem");
- viewInit();
- tioPrint("Initializing dynamic fire");
- FireInit();
- tioPrint("Initializing weapon animations");
- WeaponInit();
- tioPrint("Installing keyboard handler");
- keyInstall();
- tioPrint("Initializing network users");
- initmultiplayers(0, 0, 0);
- gNetPlayers = numplayers;
- gMe = gView = &gPlayer[myconnectindex];
- gViewIndex = myconnectindex;
- // this must be done before initializing timer, because mouse interrupt hooks timer vector
- tioPrint("Loading control setup");
- ctrlInit();
- tioPrint("Installing timer");
- LockClockStrobe();
- timerRegisterClient(ClockStrobe, kTimerRate);
- timerInstall();
- tioPrint("Initializing sound system");
- sndInit();
- sfxInit();
- tioPrint("Waiting for network players");
- WaitForAllPlayers();
- scrSetGameMode();
- scrSetGamma(gGamma);
- // if ( !gForceMap )
- // credCredits();
- viewResizeView(gViewSize);
- prepareboard();
- gNetFifoClock = gFrameClock = gGameClock = 0;
- gNetFifoHead = gNetFifoTail = 0;
- gSendCRCHead = gReceiveCRCHead = gCRCTail = 0;
- ready2send = TRUE;
- //This is the whole game loop!
- while ( keystatus[KEY_ESC] == 0 && !syncstate )
- {
- LocalKeys();
- if ( gNetPlayers > 1 )
- GetPackets();
- faketimerhandler();
- while ( gNetFifoHead != gNetFifoTail )
- ProcessFrame();
- if ( !gPaused )
- gInterpolate = (gGameClock + kFrameTicks - gFrameClock) * (0x10000 / kFrameTicks);
- viewDrawScreen();
- if (bOutOfSync)
- viewSetMessage("Out of Sync!");
- if ( gEndLevelFlag )
- {
- // fade out the current music
- sndFadeSong(4000);
- if ( gEndLevelFlag < 0 )
- {
- // end episode or something special like that
- }
- else
- gMapId = gEndLevelFlag;
- gEndLevelFlag = 0;
- gForceMap = FALSE;
- ready2send = FALSE;
- prepareboard();
- ready2send = TRUE;
- viewResizeView(gViewSize);
- }
- }
- ready2send = FALSE;
- setvmode(gOldDisplayMode);
- sndTerm();
- dprintf("Removing timer\n");
- timerRemove();
- ctrlTerm();
- UnlockClockStrobe();
- if ( gNetGame )
- {
- sendlogoff();
- uninitmultiplayers();
- }
- dprintf("uninitengine()\n");
- uninitengine();
- BloodINI.Save();
- if (syncstate)
- printf("A packet was lost! (syncstate)\n");
- dprintf("All subsystems shut down. Processing exit functions\n");
- errSetHandler(prevErrorHandler);
- }
|