//------------------------------------------------------------------------- /* Copyright (C) 2010-2019 EDuke32 developers and contributors Copyright (C) 2019 sirlemonhead, Nuke.YKT This file is part of PCExhumed. PCExhumed is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //------------------------------------------------------------------------- #include "ns.h" #include "compat.h" #include "keyboard.h" #include "control.h" #include "init.h" #include "runlist.h" #include "switch.h" #include "object.h" #include "aistuff.h" #include "player.h" #include "mummy.h" #include "move.h" #include "ra.h" #include "view.h" #include "runlist.h" #include "engine.h" #include "sound.h" #include "exhumed.h" #include "config.h" #include "items.h" #include "light.h" #include "map.h" #include "menu.h" #include "lighting.h" #include "anims.h" #include "ps_input.h" #include "util.h" #include #include BEGIN_PS_NS enum { kTagRamses = 61, }; ClockTicks ototalclock = 0; int initx, inity, initz; short inita, initsect; short nCurChunkNum = 0; short nBodyGunSprite[50]; int movefifoend; int movefifopos; short nCurBodyGunNum; short SectSoundSect[kMaxSectors] = { 0 }; short SectSound[kMaxSectors] = { 0 }; short SectFlag[kMaxSectors] = { 0 }; int SectDepth[kMaxSectors] = { 0 }; int SectAbove[kMaxSectors] = { 0 }; short SectDamage[kMaxSectors] = { 0 }; short SectSpeed[kMaxSectors] = { 0 }; int SectBelow[kMaxSectors] = { 0 }; uint8_t bIsVersion6 = kTrue; #if 0 // definitions for map version 6 structures #pragma pack(1) // 37 bytes struct Sector_6 { uint16_t wallptr, wallnum; short ceilingpicnum, floorpicnum; short ceilingheinum, floorheinum; int ceilingz, floorz; int8_t ceilingshade, floorshade; uint8_t ceilingxpanning, floorxpanning; uint8_t ceilingypanning, floorypanning; uint8_t ceilingstat, floorstat; uint8_t ceilingpal, floorpal; uint8_t visibility; short lotag, hitag, extra; }; struct Wall_6 { int x, y; short point2, nextsector, nextwall; short picnum, overpicnum; int8_t shade; uint8_t pal; short cstat; uint8_t xrepeat, yrepeat, xpanning, ypanning; short lotag, hitag, extra; }; // 43 bytes struct Sprite_6 { int x, y, z; short cstat; int8_t shade; uint8_t pal, clipdist; uint8_t xrepeat, yrepeat; int8_t xoffset, yoffset; short picnum, ang, xvel, yvel, zvel, owner; short sectnum, statnum; short lotag, hitag, extra; }; #pragma pack() static Sector_6 sector_6[1024]; static Wall_6 wall_6[8192]; static Sprite_6 sprite_6[4096]; #endif uint8_t LoadLevel(int nMap) { char fileName_1[80]; char fileName_2[32]; initspritelists(); // nMap = 1; // sprintf(fileName_2, "queen"); sprintf(fileName_2, "lev%d", nMap); // sprintf(fileName_2, "sentry"); // sprintf(fileName_2, "bjd"); // sprintf(fileName_2, "door"); // sprintf(fileName_2, "ceil"); // sprintf(fileName_2, "scarab"); // sprintf(fileName_2, "guns"); // sprintf(fileName_2, "wasp"); // sprintf(fileName_2, "items"); fileName_1[0] = '\0'; strcat(fileName_1, fileName_2); strcat(fileName_1, ".map"); // init stuff { StopAllSounds(); nCreaturesLeft = 0; nFreeze = 0; nSpiritSprite = -1; InitLion(); InitRexs(); InitSets(); InitQueens(); InitRoachs(); InitWasps(); InitRats(); InitBullets(); InitWeapons(); InitGrenades(); InitAnims(); InitSnakes(); InitFishes(); InitLights(); InitMap(); InitBubbles(); InitObjects(); InitLava(); InitPushBlocks(); InitAnubis(); InitSpider(); InitMummy(); InitScorp(); InitPlayer(); InitItems(); InitInput(); if (nMap == kMap20) { InitEnergyTile(); } } if (nMap > 15) { nSwitchSound = 35; nStoneSound = 23; nElevSound = 51; nStopSound = 35; } else { nSwitchSound = 33; nStoneSound = 23; nElevSound = 23; nStopSound = 66; } if (nMap < 0) { return kFalse; } vec3_t startPos; int status = engineLoadBoard(fileName_1, 0, &startPos, &inita, &initsect); if (status == -2) status = engineLoadBoardV5V6(fileName_1, 0, &startPos, &inita, &initsect); initx = startPos.x; inity = startPos.y; initz = startPos.z; #ifdef YAX_ENABLE yax_update(1); #endif int i; for (i = 0; i < kMaxPlayers; i++) { PlayerList[i].nSprite = -1; } psky_t* pSky = tileSetupSky(0); pSky->tileofs[0] = 0; pSky->tileofs[1] = 0; pSky->tileofs[2] = 0; pSky->tileofs[3] = 0; pSky->yoffs = 256; pSky->lognumtiles = 2; pSky->horizfrac = 65536; pSky->yscale = 65536; parallaxtype = 2; g_visibility = 2048; flash = 0; precache(); LoadObjects(); levelnum = nMap; return kTrue; } void ResetEngine() { SetOverscan(BASEPAL); EraseScreen(-1); resettiming(); totalclock = 0; ototalclock = totalclock; localclock = (int)totalclock; numframes = 0; } void InstallEngine() { // initgroupfile("stuff.dat"); TileFiles.LoadArtSet("tiles%03d.art"); // TEMP //nScreenWidth *= 2; //nScreenHeight *= 2; bHiRes = kTrue; // TEMP if (engineInit()) { wm_msgbox("Fatal Engine Initialization Error", "There was a problem initializing the engine: %s\n\nThe application will now close.", engineerrstr); //TODO: //G_Cleanup(); ERRprintf("G_Startup: There was a problem initializing the engine: %s\n", engineerrstr); exit(6); } if (videoSetGameMode(gSetup.fullscreen, gSetup.xdim, gSetup.ydim, gSetup.bpp, 0) < 0) { initprintf("Failure setting video mode %dx%dx%d %s! Trying next mode...\n", gSetup.xdim, gSetup.ydim, gSetup.bpp, gSetup.fullscreen ? "fullscreen" : "windowed"); int resIdx = 0; for (int i=0; i < validmodecnt; i++) { if (validmode[i].xdim == gSetup.xdim && validmode[i].ydim == gSetup.ydim) { resIdx = i; break; } } int const savedIdx = resIdx; int bpp = gSetup.bpp; while (videoSetGameMode(0, validmode[resIdx].xdim, validmode[resIdx].ydim, bpp, 0) < 0) { initprintf("Failure setting video mode %dx%dx%d windowed! Trying next mode...\n", validmode[resIdx].xdim, validmode[resIdx].ydim, bpp); if (++resIdx == validmodecnt) { if (bpp == 8) I_Error("Fatal error: unable to set any video mode!"); resIdx = savedIdx; bpp = 8; } } gSetup.xdim = validmode[resIdx].xdim; gSetup.ydim = validmode[resIdx].ydim; gSetup.bpp = bpp; } LoadPaletteLookups(); enginecompatibility_mode = ENGINECOMPATIBILITY_19950829; } void RemoveEngine() { engineUnInit(); } void SetBelow(short nCurSector, short nBelowSector) { SectBelow[nCurSector] = nBelowSector; } void SetAbove(short nCurSector, short nAboveSector) { SectAbove[nCurSector] = nAboveSector; } void SnapSectors(short nSectorA, short nSectorB, short b) { // edx - nSectorA // eax - nSectorB short nWallA = sector[nSectorA].wallptr; short nWallB = sector[nSectorB].wallptr; short num1 = sector[nSectorA].wallnum; short num2 = sector[nSectorB].wallnum; int nCount = 0; while (num1 > nCount) { short dx = nWallB; int esi = 0x7FFFFFF; int edi = esi; int x = wall[nWallA].x; int y = wall[nWallA].y; int var_14 = 0; int nCount2 = 0; while (nCount2 < num2) { int eax = x - wall[dx].x; int ebx = y - wall[dx].y; if (eax < 0) { eax = -eax; } int var_38 = eax; if (ebx < 0) { ebx = -ebx; } int var_3C = ebx; var_38 += var_3C; eax = esi; if (eax < 0) { eax = -eax; } var_3C = eax; eax = edi; // int var_34 = edi; if (eax < 0) { eax = -eax; } int var_34 = eax; var_34 += var_3C; if (var_38 < var_34) { esi = x - wall[dx].x; edi = y - wall[dx].y; var_14 = dx; } dx++; nCount2++; } dragpoint(var_14, wall[var_14].x + esi, wall[var_14].y + edi, 0); nCount++; nWallA++; } if (b) { sector[nSectorB].ceilingz = sector[nSectorA].floorz; } if (SectFlag[nSectorA] & 0x1000) { SnapBobs(nSectorA, nSectorB); } } void InitSectFlag() { for (int i = 0; i < kMaxSectors; i++) { SectSoundSect[i] = -1; SectSound[i] = -1; SectAbove[i] = -1; SectBelow[i] = -1; SectDepth[i] = 0; SectFlag[i] = 0; SectSpeed[i] = 0; SectDamage[i] = 0; } } void ProcessSpriteTag(short nSprite, short lotag, short hitag) { int nChannel = runlist_AllocChannel(hitag % 1000); // int ebp = nChannel; int nHitag2 = hitag / 1000; int nLotag2 = lotag / 1000; if (nLotag2 == 0) { nLotag2 = 1; } // this value can change in the below code but we also need to retain the original hitag value int nVal = hitag; if (lotag >= 900 && lotag <= 949) { ProcessTrailSprite(nSprite, lotag, hitag); return; } // handle tags 6 to 60 switch (lotag) { case 8: // M-60 ammo belt { nVal = 3 * (hitag / 3); // fall through to 6,7 etc fallthrough__; } case 6: case 7: case 9: case 10: case 11: case 15: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 24: case 26: case 28: case 29: case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: case 39: case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: case 48: case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: case 58: case 60: { sprite[nSprite].hitag = nVal; changespritestat(nSprite, lotag + 900); sprite[nSprite].cstat &= 0xFEFE; BuildItemAnim(nSprite); return; } case 12: // berry twig { sprite[nSprite].hitag = 40; changespritestat(nSprite, lotag + 900); sprite[nSprite].cstat &= 0xFEFE; BuildItemAnim(nSprite); return; } case 13: // blood bowl { sprite[nSprite].hitag = 160; changespritestat(nSprite, lotag + 900); sprite[nSprite].cstat &= 0xFEFE; BuildItemAnim(nSprite); return; } case 14: // venom bowl { sprite[nSprite].hitag = -200; changespritestat(nSprite, lotag + 900); sprite[nSprite].cstat &= 0xFEFE; BuildItemAnim(nSprite); return; } case 16: // reserved mydeletesprite(nSprite); return; case 25: case 59: { // extra life or checkpoint scarab. Delete for multiplayer if (nNetPlayerCount != 0) { mydeletesprite(nSprite); return; } else { sprite[nSprite].hitag = nVal; changespritestat(nSprite, lotag + 900); sprite[nSprite].cstat &= 0xFEFE; BuildItemAnim(nSprite); return; } } case 27: { sprite[nSprite].hitag = 1; changespritestat(nSprite, 9 + 900); sprite[nSprite].cstat &= 0xFEFE; BuildItemAnim(nSprite); return; } case 38: // raw energy { nVal++; nVal--; // CHECKME ?? sprite[nSprite].hitag = nVal; changespritestat(nSprite, lotag + 900); sprite[nSprite].cstat &= 0xFEFE; BuildItemAnim(nSprite); return; } } int v6 = lotag % 1000; if (!bNoCreatures || v6 < 100 || v6 > 118) { if (v6 > 999) { mydeletesprite(nSprite); return; } switch (v6) { case 999: { AddFlicker(sprite[nSprite].sectnum, nLotag2); break; } case 998: { AddGlow(sprite[nSprite].sectnum, nLotag2); break; } case 118: // Anubis with drum { if (bNoCreatures) { mydeletesprite(nSprite); return; } BuildAnubis(nSprite, 0, 0, 0, 0, 0, 1); return; } case 117: { if (bNoCreatures) { mydeletesprite(nSprite); return; } BuildWasp(nSprite, 0, 0, 0, 0, 0); return; } case 116: { BuildRat(nSprite, 0, 0, 0, 0, -1); return; } case 115: // Rat (eating) { BuildRat(nSprite, 0, 0, 0, 0, 0); return; } case 113: { BuildQueen(nSprite, 0, 0, 0, 0, 0, nChannel); return; } case 112: { BuildScorp(nSprite, 0, 0, 0, 0, 0, nChannel); return; } case 111: { if (bNoCreatures) { mydeletesprite(nSprite); return; } BuildSet(nSprite, 0, 0, 0, 0, 0, nChannel); return; } case 108: { if (bNoCreatures) { mydeletesprite(nSprite); return; } BuildLava(nSprite, 0, 0, 0, 0, 0, nChannel); return; } case 107: { if (bNoCreatures) { mydeletesprite(nSprite); return; } BuildRex(nSprite, 0, 0, 0, 0, 0, nChannel); return; } case 106: { if (bNoCreatures) { mydeletesprite(nSprite); return; } BuildFish(nSprite, 0, 0, 0, 0, 0); return; } case 105: { if (bNoCreatures) { mydeletesprite(nSprite); return; } BuildSpider(nSprite, 0, 0, 0, 0, 0); return; } case 104: { if (bNoCreatures) { mydeletesprite(nSprite); return; } BuildRoach(1, nSprite, 0, 0, 0, 0, 0); return; } case 103: { if (bNoCreatures) { mydeletesprite(nSprite); return; } BuildRoach(0, nSprite, 0, 0, 0, 0, 0); return; } case 102: { if (bNoCreatures) { mydeletesprite(nSprite); return; } BuildLion(nSprite, 0, 0, 0, 0, 0); return; } case 101: { if (bNoCreatures) { mydeletesprite(nSprite); return; } BuildMummy(nSprite, 0, 0, 0, 0, 0); return; } case 100: { if (bNoCreatures) { mydeletesprite(nSprite); return; } BuildAnubis(nSprite, 0, 0, 0, 0, 0, 0); return; } case 99: // underwater type 2 { short nSector = sprite[nSprite].sectnum; SetAbove(nSector, hitag); SectFlag[nSector] |= kSectUnderwater; mydeletesprite(nSprite); return; } case 98: { short nSector = sprite[nSprite].sectnum; SetBelow(nSector, hitag); SnapSectors(nSector, hitag, 1); mydeletesprite(nSprite); return; } case 97: { AddSectorBob(sprite[nSprite].sectnum, hitag, 1); mydeletesprite(nSprite); return; } case 96: // Lava sector { hitag /= 4; // hitag is damage level? if (hitag == 0) { hitag = 1; } short nSector = sprite[nSprite].sectnum; SectDamage[nSector] = hitag; SectFlag[nSector] |= kSectLava; mydeletesprite(nSprite); return; } case 95: { AddSectorBob(sprite[nSprite].sectnum, hitag, 0); mydeletesprite(nSprite); return; } case 94: // water { short nSector = sprite[nSprite].sectnum; SectDepth[nSector] = hitag << 8; mydeletesprite(nSprite); return; } case 93: { BuildBubbleMachine(nSprite); return; } case 90: { BuildObject(nSprite, 3, hitag); return; } case 79: case 89: { short nSector = sprite[nSprite].sectnum; SectSpeed[nSector] = nLotag2; SectFlag[nSector] |= sprite[nSprite].ang; mydeletesprite(nSprite); return; } case 88: { AddFlow(nSprite, nLotag2, 0); mydeletesprite(nSprite); return; } case 80: // underwater { short nSector = sprite[nSprite].sectnum; SectFlag[nSector] |= kSectUnderwater; mydeletesprite(nSprite); return; } case 78: { AddFlow(nSprite, nLotag2, 1); short nSector = sprite[nSprite].sectnum; SectFlag[nSector] |= 0x8000; mydeletesprite(nSprite); return; } case 77: { int nArrow = BuildArrow(nSprite, nLotag2); runlist_AddRunRec(sRunChannels[nChannel].a, nArrow); return; } case 76: // Explosion Trigger (Exploding Fire Cauldron) { BuildObject(nSprite, 0, hitag); return; } case 75: // Explosion Target (Cauldrons, fireballs and grenades will destroy nearby 75 sprites) { BuildObject(nSprite, 1, hitag); return; } case 71: { int nFireball = BuildFireBall(nSprite, hitag, nLotag2); runlist_AddRunRec(sRunChannels[nChannel].a, nFireball); return; } case 70: { BuildDrip(nSprite); return; } case 63: { changespritestat(nSprite, 405); sprite[nSprite].cstat = 0x8000; return; } case 62: { nNetStartSprite[nNetStartSprites] = nSprite; sprite[nSprite].cstat = 0x8000; nNetStartSprites++; return; } case kTagRamses: // Ramses head { nSpiritSprite = nSprite; sprite[nSprite].cstat |= 0x8000; return; } default: // TODO - checkme! { mydeletesprite(nSprite); return; } } } mydeletesprite(nSprite); } void ExamineSprites() { nNetStartSprites = 0; nCurStartSprite = 0; for (int nSprite = 0; nSprite < kMaxSprites; nSprite++) { int nStatus = sprite[nSprite].statnum; if (!nStatus) { short lotag = sprite[nSprite].lotag; short hitag = sprite[nSprite].hitag; if ((nStatus < kMaxStatus) && lotag) { sprite[nSprite].lotag = 0; sprite[nSprite].hitag = 0; ProcessSpriteTag(nSprite, lotag, hitag); } else { changespritestat(nSprite, 0); } } } if (nNetPlayerCount) { int nSprite = insertsprite(initsect, 0); sprite[nSprite].x = initx; sprite[nSprite].y = inity; sprite[nSprite].z = initz; sprite[nSprite].cstat = 0x8000; nNetStartSprite[nNetStartSprites] = nSprite; nNetStartSprites++; } } void LoadObjects() { runlist_InitRun(); runlist_InitChan(); InitLink(); InitPoint(); InitSlide(); InitSwitch(); InitElev(); InitWallFace(); InitTimeSlot(); InitSectFlag(); for (int nSector = 0; nSector < numsectors; nSector++) { short hitag = sector[nSector].hitag; short lotag = sector[nSector].lotag; sector[nSector].hitag = 0; sector[nSector].lotag = 0; sector[nSector].extra = -1; if (hitag || lotag) { sector[nSector].lotag = runlist_HeadRun() + 1; sector[nSector].hitag = lotag; runlist_ProcessSectorTag(nSector, lotag, hitag); } } for (int nWall = 0; nWall < numwalls; nWall++) { wall[nWall].extra = -1; short lotag = wall[nWall].lotag; short hitag = wall[nWall].hitag; wall[nWall].lotag = 0; if (hitag || lotag) { wall[nWall].lotag = runlist_HeadRun() + 1; runlist_ProcessWallTag(nWall, lotag, hitag); } } ExamineSprites(); PostProcess(); InitRa(); InitChunks(); for (int nSprite = 0; nSprite < kMaxSprites; nSprite++) { runlist_ChangeChannel(nSprite, 0); runlist_ReadyChannel(nSprite); } nCamerax = initx; nCameray = inity; nCameraz = initz; } int myloadconfig() { FILE *fp = fopen("psa.ini", "rb"); if (fp == NULL) { gFXVolume = 200; nGamma = 2; gMusicVolume = 200; bFullScreen = 0; mysetbrightness((uint8_t)nGamma); lMouseSens = 8; return -1; } fread(&gFXVolume, sizeof(gFXVolume), 1, fp); fread(&gMusicVolume, sizeof(gMusicVolume), 1, fp); fread(&screensize, sizeof(screensize), 1, fp); fread(&bFullScreen, sizeof(bFullScreen), 1, fp); fread(&nGamma, sizeof(nGamma), 1, fp); fread(&lMouseSens, sizeof(lMouseSens), 1, fp); if (screensize < 0 || screensize > 15) { screensize = 0; } fclose(fp); if (gFXVolume > 255) { gFXVolume = 125; } if (gMusicVolume > 255) { gMusicVolume = 125; } return 1; } int mysaveconfig() { FILE *fp = fopen("psa.ini", "wb"); if (fp == NULL) { return -1; } fwrite(&gFXVolume, sizeof(gFXVolume), 1, fp); fwrite(&gMusicVolume, sizeof(gMusicVolume), 1, fp); short nSize = screensize; fwrite(&nSize, sizeof(nSize), 1, fp); fwrite(&bFullScreen, sizeof(bFullScreen), 1, fp); fwrite(&nGamma, sizeof(nGamma), 1, fp); fwrite(&lMouseSens, sizeof(lMouseSens), 1, fp); fclose(fp); return 1; } END_PS_NS