diff --git a/CMakeLists.txt b/CMakeLists.txt index 5730a0bff..05442ef84 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -414,6 +414,7 @@ add_subdirectory( source/duke3d ) add_subdirectory( source/blood ) add_subdirectory( source/rr ) add_subdirectory( source/sw ) +add_subdirectory( source/exhumed ) if( NOT CMAKE_CROSSCOMPILING ) export(TARGETS ${CROSS_EXPORTS} FILE "${CMAKE_BINARY_DIR}/ImportExecutables.cmake" ) diff --git a/README.md b/README.md new file mode 100644 index 000000000..87278772a --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# PCExhumed +A port of the PC version of Exhumed based on EDuke32 + +## Installing +1. Extract PCExhumed to a new directory +2. Copy the following files from the PC retail version of Exhumed or PowerSlave (Exhumed preferred). Beta, demo or pre-release versions not supported. + + STUFF.DAT + DEMO.VCR + BOOK.MOV + +CD audio tracks are not yet working. +The intro movie is not yet working with audio. +Demo playback is not yet working. + +3. Launch PCExhumed + +## Building PCExhumed +See: https://wiki.eduke32.com/wiki/Main_Page + +## Acknowledgments: + See AUTHORS.md \ No newline at end of file diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 199b77ef3..09f3c7d2e 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -226,15 +226,15 @@ find_package( FluidSynth ) # SSE only matters on 32-bit targets. We check compiler flags to know if we can do it. if( CMAKE_SIZEOF_VOID_P MATCHES "4" AND NOT CMAKE_OSX_ARCHITECTURES MATCHES ppc ) - CHECK_CXX_COMPILER_FLAG( "-msse2 -mfpmath=sse" CAN_DO_MFPMATH ) - CHECK_CXX_COMPILER_FLAG( -arch:SSE2 CAN_DO_ARCHSSE2 ) - if( CAN_DO_MFPMATH ) - set( SSE1_ENABLE "-msse -mfpmath=sse" ) - set( SSE2_ENABLE "-msse2 -mfpmath=sse" ) - elseif( CAN_DO_ARCHSSE2 ) - set( SSE1_ENABLE -arch:SSE ) - set( SSE2_ENABLE -arch:SSE2 ) - endif() + CHECK_CXX_COMPILER_FLAG( "-msse2 -mfpmath=sse" CAN_DO_MFPMATH ) + CHECK_CXX_COMPILER_FLAG( -arch:SSE2 CAN_DO_ARCHSSE2 ) + if( CAN_DO_MFPMATH ) + set( SSE1_ENABLE "-msse -mfpmath=sse" ) + set( SSE2_ENABLE "-msse2 -mfpmath=sse" ) + elseif( CAN_DO_ARCHSSE2 ) + set( SSE1_ENABLE -arch:SSE ) + set( SSE2_ENABLE -arch:SSE2 ) + endif() endif() if( X64 ) @@ -859,7 +859,7 @@ if( UNIX ) endif() endif() -target_link_libraries( ${PROJECT_NAME} ${PROJECT_LIBRARIES} gdtoa lzma duke3d blood rr sw zmusic ) +target_link_libraries( ${PROJECT_NAME} ${PROJECT_LIBRARIES} gdtoa lzma duke3d blood rr sw exhumed zmusic ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} @@ -993,6 +993,8 @@ source_group("Code\\Menu" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/commo source_group("Code\\Rendering" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/rendering/.+") source_group("Code\\Rendering\\GL_Load" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/rendering/gl_load/.+") source_group("Code\\Rendering\\GL\\System" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/rendering/gl/system.+") +source_group("Platform" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/platform/.+") +source_group("Platform\\Win32" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/platform/win32/.+") source_group("Utility\\Smackerdec" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/smackerdec/.+") source_group("Utility\\Smackerdec\\Headers" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/libsmackerdec/include/.+") source_group("Utility\\Smackerdec\\Sources" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/libsmackerdec/src/.+") diff --git a/source/build/include/baselayer.h b/source/build/include/baselayer.h index cc8cc4d57..2fda1fa88 100644 --- a/source/build/include/baselayer.h +++ b/source/build/include/baselayer.h @@ -125,7 +125,7 @@ struct GameInterface virtual void DrawMenuCaption(const DVector2& origin, const char* text) {} virtual bool SaveGame(FSaveGameNode*) { return false; } virtual bool LoadGame(FSaveGameNode*) { return false; } - virtual void DoPrintMessage(int prio, const char*) = 0; + virtual void DoPrintMessage(int prio, const char*) {} void PrintMessage(int prio, const char*fmt, ...) { va_list ap; diff --git a/source/build/src/clip.cpp b/source/build/src/clip.cpp index a4b417ca1..a25dd0c9d 100644 --- a/source/build/src/clip.cpp +++ b/source/build/src/clip.cpp @@ -766,10 +766,8 @@ static FORCE_INLINE void clipmove_tweak_pos(const vec3_t *pos, int32_t gx, int32 { int32_t daz; - if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829) - return; - - if (rintersect(pos->x, pos->y, 0, gx, gy, 0, x1, y1, x2, y2, daxptr, dayptr, &daz) == -1) + if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829 || + rintersect(pos->x, pos->y, 0, gx, gy, 0, x1, y1, x2, y2, daxptr, dayptr, &daz) == -1) { *daxptr = pos->x; *dayptr = pos->y; @@ -785,7 +783,7 @@ int32_t getceilzofslope_old(int32_t sectnum, int32_t dax, int32_t day) dx = wall[wall[j].point2].x-wall[j].x; dy = wall[wall[j].point2].y-wall[j].y; i = (ksqrtasm_old(dx*dx+dy*dy)); if (i == 0) return(sector[sectnum].ceilingz); - i = divscale20(sector[sectnum].ceilingheinum,i); + i = divscale15(sector[sectnum].ceilingheinum,i); dx *= i; dy *= i; return(sector[sectnum].ceilingz+dmulscale23(dx,day-wall[j].y,-dy,dax-wall[j].x)); } @@ -799,7 +797,7 @@ int32_t getflorzofslope_old(int32_t sectnum, int32_t dax, int32_t day) dx = wall[wall[j].point2].x-wall[j].x; dy = wall[wall[j].point2].y-wall[j].y; i = (ksqrtasm_old(dx*dx+dy*dy)); if (i == 0) return sector[sectnum].floorz; - i = divscale20(sector[sectnum].floorheinum,i); + i = divscale15(sector[sectnum].floorheinum,i); dx *= i; dy *= i; return(sector[sectnum].floorz+dmulscale23(dx,day-wall[j].y,-dy,dax-wall[j].x)); } @@ -1442,7 +1440,7 @@ int32_t clipmove(vec3_t * const pos, int16_t * const sectnum, int32_t xvect, int if (templl > 0) { int64_t const templl2 = compat_maybe_truncate_to_int32((int64_t)(goal.x-vec.x)*clipr.x + (int64_t)(goal.y-vec.y)*clipr.y); - int32_t const i = enginecompatibility_mode == ENGINECOMPATIBILITY_19950829 || ((llabs(templl2)>>11) < templl) ? + int32_t const i = (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829 || (llabs(templl2)>>11) < templl) ? divscale64(templl2, templl, 20) : 0; goal = { mulscale20(clipr.x, i)+vec.x, mulscale20(clipr.y, i)+vec.y }; diff --git a/source/build/src/engine.cpp b/source/build/src/engine.cpp index c0e39bc6b..9c8b3f2c8 100644 --- a/source/build/src/engine.cpp +++ b/source/build/src/engine.cpp @@ -10428,7 +10428,7 @@ int32_t cansee_old(int32_t xs, int32_t ys, int32_t zs, int16_t sectnums, int32_t int32_t cansee(int32_t x1, int32_t y1, int32_t z1, int16_t sect1, int32_t x2, int32_t y2, int32_t z2, int16_t sect2) { if (enginecompatibility_mode == ENGINECOMPATIBILITY_19950829) - return cansee_old(x1, y1, z2, sect1, x2, y2, z2, sect2); + return cansee_old(x1, y1, z1, sect1, x2, y2, z2, sect2); int32_t dacnt, danum; const int32_t x21 = x2-x1, y21 = y2-y1, z21 = z2-z1; diff --git a/source/common/console/c_buttons.h b/source/common/console/c_buttons.h index 4f53282f9..7a3cd2773 100644 --- a/source/common/console/c_buttons.h +++ b/source/common/console/c_buttons.h @@ -56,12 +56,14 @@ enum GameFunction_t gamefunc_Shrink_Screen, gamefunc_Enlarge_Screen, gamefunc_Center_View, + gamefunc_Look_Straight = gamefunc_Center_View, gamefunc_Holster_Weapon, gamefunc_Show_Opponents_Weapon, gamefunc_Map_Follow_Mode, gamefunc_See_Coop_View, gamefunc_See_Co_Op_View = gamefunc_See_Coop_View, gamefunc_Mouse_Aiming, + gamefunc_Mouseview = gamefunc_Mouse_Aiming, gamefunc_Toggle_Crosshair, gamefunc_Steroids, gamefunc_Quick_Kick, @@ -83,10 +85,14 @@ enum GameFunction_t gamefunc_Flash_Bomb, gamefunc_Caltrops, + gamefunc_Zoom_In, // Map controls should not pollute the global button namespace. + gamefunc_Zoom_Out, + NUMGAMEFUNCTIONS }; + // Actions struct FButtonStatus { diff --git a/source/common/console/c_console.cpp b/source/common/console/c_console.cpp index 024cc2d27..237b64ec0 100644 --- a/source/common/console/c_console.cpp +++ b/source/common/console/c_console.cpp @@ -1051,7 +1051,7 @@ void C_Ticker() lasttic = consoletic; NotifyStrings.Tick(); - if (GUICapture & 1) + if (ConsoleState == c_down) { D_ProcessEvents(); } diff --git a/source/common/gamecontrol.cpp b/source/common/gamecontrol.cpp index 273958146..77e4e8258 100644 --- a/source/common/gamecontrol.cpp +++ b/source/common/gamecontrol.cpp @@ -265,6 +265,10 @@ namespace ShadowWarrior { ::GameInterface* CreateInterface(); } +namespace Powerslave +{ + ::GameInterface* CreateInterface(); +} void CheckFrontend(int flags) { @@ -280,6 +284,10 @@ void CheckFrontend(int flags) { gi = ShadowWarrior::CreateInterface(); } + else if (flags & GAMEFLAG_PSEXHUMED) + { + gi = Powerslave::CreateInterface(); + } else { gi = Duke::CreateInterface(); @@ -639,12 +647,19 @@ FStringCVar* const CombatMacros[] = { &combatmacro0, &combatmacro1, &combatmacro void CONFIG_ReadCombatMacros() { FScanner sc; - sc.Open("engine/combatmacros.txt"); - for (auto s : CombatMacros) + try { - sc.MustGetToken(TK_StringConst); - if (strlen(*s) == 0) - *s = sc.String; + sc.Open("engine/combatmacros.txt"); + for (auto s : CombatMacros) + { + sc.MustGetToken(TK_StringConst); + if (strlen(*s) == 0) + *s = sc.String; + } + } + catch (const std::runtime_error &) + { + // We do not want this to error out. Just ignore if it fails. } } diff --git a/source/common/gamecontrol.h b/source/common/gamecontrol.h index 5d3eb9c79..eeb724511 100644 --- a/source/common/gamecontrol.h +++ b/source/common/gamecontrol.h @@ -105,7 +105,10 @@ enum GAMEFLAG_RRRA = 0x00000200, GAMEFLAG_BLOOD = 0x00000400, GAMEFLAG_SW = 0x00000800, - GAMEFLAG_STANDALONE = 0x00001000, + GAMEFLAG_POWERSLAVE = 0x00001000, + GAMEFLAG_EXHUMED = 0x00002000, + GAMEFLAG_PSEXHUMED = 0x00003000, // the two games really are the same, except for the name and the publisher. + GAMEFLAG_STANDALONE = 0x00004000, GAMEFLAGMASK = 0x00000FFF, // flags allowed from grpinfo }; diff --git a/source/common/inputstate.h b/source/common/inputstate.h index d43b4818e..0178f2f24 100644 --- a/source/common/inputstate.h +++ b/source/common/inputstate.h @@ -192,7 +192,7 @@ public: void ClearKeysDown(void) { - KB_LastScan = 0; + ClearLastScanCode(); ClearAllKeyStatus(); } diff --git a/source/common/menu/menu.cpp b/source/common/menu/menu.cpp index 7d01f7e5f..3fe09c1c3 100644 --- a/source/common/menu/menu.cpp +++ b/source/common/menu/menu.cpp @@ -59,6 +59,7 @@ void RegisterDukeMenus(); void RegisterRedneckMenus(); void RegisterBloodMenus(); void RegisterSWMenus(); +void RegisterPSMenus(); void RegisterLoadsaveMenus(); void RegisterOptionMenus(); void RegisterJoystickMenus(); @@ -387,8 +388,6 @@ void M_StartControlPanel (bool makeSound) BackbuttonTime = 0; BackbuttonAlpha = 0; DrawBackground = -1; - DMenu::MenuTime = -1; - M_Ticker(); // This needs to be called once here to make sure that the menu actually has ticked before it gets drawn for the first time. } void Menu_Open(int playerid) @@ -412,6 +411,8 @@ void M_ActivateMenu(DMenu *menu) transition.StartTransition(DMenu::CurrentMenu, menu, MA_Advance); } DMenu::CurrentMenu = menu; + DMenu::MenuTime = -1; + M_Ticker(); // This needs to be called once here to make sure that the menu actually has ticked before it gets drawn for the first time. } //============================================================================= @@ -428,7 +429,7 @@ bool M_SetMenu(FName menu, int param, FName caller) GameStartupInfo.Episode = GameStartupInfo.Skill = 0; menu = NAME_StartGame; #endif - if (DrawBackground == -1) + if (DrawBackground == -1) { if (menu == NAME_MainMenu) DrawBackground = 1; else DrawBackground = 0; @@ -473,6 +474,7 @@ bool M_SetMenu(FName menu, int param, FName caller) { case NAME_StartGame: M_ClearMenus(); // must be done before starting the level. + if (caller == NAME_MainMenu) GameStartupInfo.Episode = param; STAT_StartNewGame(gVolumeNames[GameStartupInfo.Episode], GameStartupInfo.Skill); gi->StartGame(GameStartupInfo); return false; @@ -837,7 +839,7 @@ void M_Ticker (void) if (DMenu::MenuTime & 3) return; if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off) { - D_ProcessEvents(); // The main loop is blocked when the menu is open and cannot dispatch the events. + if (DMenu::MenuTime != 0) D_ProcessEvents(); // The main loop is blocked when the menu is open and cannot dispatch the events. if (transition.previous) transition.previous->Ticker(); if (DMenu::CurrentMenu == nullptr) return; // In case one of the sub-screens has closed the menu. DMenu::CurrentMenu->Ticker(); @@ -894,6 +896,7 @@ void M_Drawer (void) } if (!going) { + assert(DMenu::CurrentMenu); DMenu::CurrentMenu->origin = { 0,0 }; // else if (DrawBackground) Menu_DrawBackground(origin); DMenu::CurrentMenu->Drawer(); @@ -925,8 +928,8 @@ void M_ClearMenus (bool final) menuactive = MENU_Off; if (!final) { - mouseGrabInput(true); - gi->MenuClosed(); + mouseGrabInput(true); + gi->MenuClosed(); } } @@ -979,6 +982,7 @@ void M_Init (void) RegisterRedneckMenus(); RegisterBloodMenus(); RegisterSWMenus(); + RegisterPSMenus(); RegisterLoadsaveMenus(); RegisterOptionMenus(); RegisterJoystickMenus(); diff --git a/source/common/menu/menudef.cpp b/source/common/menu/menudef.cpp index 29e421df7..7ff338c9d 100644 --- a/source/common/menu/menudef.cpp +++ b/source/common/menu/menudef.cpp @@ -152,6 +152,7 @@ static const gamefilter games[] = { { "RedneckRides", GAMEFLAG_RRRA}, { "Blood", GAMEFLAG_BLOOD}, { "ShadowWarrior", GAMEFLAG_SW}, + { "Exhumed", GAMEFLAG_POWERSLAVE|GAMEFLAG_EXHUMED}, }; // for other parts that need to filter by game name. diff --git a/source/common/ns.h b/source/common/ns.h index 48e8230e9..8ad103b2d 100644 --- a/source/common/ns.h +++ b/source/common/ns.h @@ -14,6 +14,9 @@ #define BEGIN_SW_NS namespace ShadowWarrior { #define END_SW_NS } +#define BEGIN_PS_NS namespace Powerslave { +#define END_PS_NS } + #else #define BEGIN_DUKE_NS @@ -28,5 +31,8 @@ #define BEGIN_SW_NS #define END_SW_NS +#define BEGIN_PS_NS +#define END_PS_NS + #endif diff --git a/source/common/searchpaths.cpp b/source/common/searchpaths.cpp index f1546f0eb..ef2301292 100644 --- a/source/common/searchpaths.cpp +++ b/source/common/searchpaths.cpp @@ -664,6 +664,8 @@ static TArray ParseGrpInfo(const char *fn, FileReader &fr, TMap EChanFlags; diff --git a/source/common/sound/backend/oalsound.cpp b/source/common/sound/backend/oalsound.cpp index 327129335..4461a71b4 100644 --- a/source/common/sound/backend/oalsound.cpp +++ b/source/common/sound/backend/oalsound.cpp @@ -1702,7 +1702,9 @@ void OpenALSoundRenderer::StopChannel(FISoundChannel *chan) ALuint source = GET_PTRID(chan->SysChannel); // Release first, so it can be properly marked as evicted if it's being killed + chan->ChanFlags |= CHANF_ENDED; soundEngine->ChannelEnded(chan); + assert(!(chan->ChanFlags & CHANF_ENDED)); ALint state = AL_INITIAL; alGetSourcei(source, AL_SOURCE_STATE, &state); @@ -1726,7 +1728,9 @@ void OpenALSoundRenderer::ForceStopChannel(FISoundChannel *chan) ALuint source = GET_PTRID(chan->SysChannel); if(!source) return; + chan->ChanFlags |= CHANF_ENDED; soundEngine->ChannelEnded(chan); + assert(!(chan->ChanFlags & CHANF_ENDED)); FreeSource(source); } diff --git a/source/common/sound/s_sound.cpp b/source/common/sound/s_sound.cpp index 16b153287..e7a88100d 100644 --- a/source/common/sound/s_sound.cpp +++ b/source/common/sound/s_sound.cpp @@ -63,7 +63,7 @@ int sfx_empty = -1; // //========================================================================== -void SoundEngine::Init(TArray &curve) +void SoundEngine::Init(TArray &curve, int factor) { StopAllChannels(); // Free all channels for use. @@ -72,6 +72,7 @@ void SoundEngine::Init(TArray &curve) ReturnChannel(Channels); } S_SoundCurve = std::move(curve); + SndCurveFactor = (uint8_t)factor; } //========================================================================== @@ -605,6 +606,7 @@ FSoundChan *SoundEngine::StartSound(int type, const void *source, } if (chan != NULL) { + assert(!(chan->ChanFlags & CHANF_FORGETTABLE)); chan->SoundID = sound_id; chan->OrgID = FSoundID(org_id); chan->EntChannel = channel; @@ -1332,7 +1334,7 @@ float SoundEngine::GetRolloff(const FRolloffInfo* rolloff, float distance) if (rolloff->RolloffType == ROLLOFF_Custom && S_SoundCurve.Size() > 0) { - return S_SoundCurve[int(S_SoundCurve.Size() * (1.f - volume))] / 127.f; + return S_SoundCurve[int(S_SoundCurve.Size() * (1.f - volume))] / (float)SndCurveFactor; } return (powf(10.f, volume) - 1.f) / 9.f; } @@ -1347,7 +1349,7 @@ void SoundEngine::ChannelEnded(FISoundChannel *ichan) { FSoundChan *schan = static_cast(ichan); bool evicted; - + schan->ChanFlags &= ~CHANF_ENDED; if (schan != NULL) { // If the sound was stopped with GSnd->StopSound(), then we know @@ -1764,7 +1766,7 @@ FString SoundEngine::NoiseDebug() int ch = 0; FStringf out("*** SOUND DEBUG INFO ***\nListener: %3.2f %2.3f %2.3f\n" - "x y z vol dist chan pri flags aud pos name\n", listener.X, listener.Y, listener.Z); + "x y z vol dist chan pri flags aud pos name\n", listener.X, listener.Y, listener.Z); if (Channels == nullptr) { @@ -1782,16 +1784,17 @@ FString SoundEngine::NoiseDebug() CalcPosVel(chan, &origin, nullptr); out.AppendFormat(TEXTCOLOR_GOLD "%5.0f | %5.0f | %5.0f | %5.0f ", origin.X, origin.Z, origin.Y, (origin - listener).Length()); } - out.AppendFormat("%-.2g %-4d %-4d %s3%sZ%sU%sM%sN%sA%sL%sE%sV" TEXTCOLOR_GOLD " %-5.4f %-4u %d: %s\n", chan->Volume, chan->EntChannel, chan->Priority, - (chan->ChanFlags & CHANF_IS3D) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHANF_LISTENERZ) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHANF_UI) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHANF_MAYBE_LOCAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHANF_NOPAUSE) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHANF_AREA) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHANF_LOOP) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHANF_EVICTED) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, - (chan->ChanFlags & CHANF_VIRTUAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_BLACK, + out.AppendFormat("%-.2g %-4d %-4d %sF%s3%sZ%sU%sM%sN%sA%sL%sE%sV" TEXTCOLOR_GOLD " %-5.4f %-4u %d: %s\n", chan->Volume, chan->EntChannel, chan->Priority, + (chan->ChanFlags & CHANF_FORGETTABLE) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED, + (chan->ChanFlags & CHANF_IS3D) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED, + (chan->ChanFlags & CHANF_LISTENERZ) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED, + (chan->ChanFlags & CHANF_UI) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED, + (chan->ChanFlags & CHANF_MAYBE_LOCAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED, + (chan->ChanFlags & CHANF_NOPAUSE) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED, + (chan->ChanFlags & CHANF_AREA) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED, + (chan->ChanFlags & CHANF_LOOP) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED, + (chan->ChanFlags & CHANF_EVICTED) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED, + (chan->ChanFlags & CHANF_VIRTUAL) ? TEXTCOLOR_GREEN : TEXTCOLOR_DARKRED, GSnd->GetAudibility(chan), GSnd->GetPosition(chan), ((int)chan->OrgID)-1, S_sfx[chan->SoundID].name.GetChars()); ch++; } diff --git a/source/common/sound/s_soundinternal.h b/source/common/sound/s_soundinternal.h index 7d9a29d78..08cfff2c5 100644 --- a/source/common/sound/s_soundinternal.h +++ b/source/common/sound/s_soundinternal.h @@ -217,6 +217,8 @@ enum // This cannot be remain as this, but for now it has to suffice. SOURCE_Ambient, // Sound is coming from a blood ambient definition. SOURCE_Unattached, // Sound is not attached to any particular emitter. SOURCE_Player, // SW player sound (player in SW maintains its own position separately from the sprite so needs to be special.) + SOURCE_Swirly, // Special stuff for Exhumed. (local sound with custom panning) + SOURCE_EXBoss, // Another special case for Exhumed. }; @@ -234,6 +236,7 @@ class SoundEngine { protected: bool SoundPaused = false; // whether sound is paused + uint8_t SndCurveFactor = 127; int RestartEvictionsAt = 0; // do not restart evicted channels before this time SoundListener listener{}; @@ -284,7 +287,7 @@ public: // Sets channels, SFX and music volume, // allocates channel buffer, sets S_sfx lookup. // - void Init(TArray &sndcurve); + void Init(TArray &sndcurve, int factor = 127); void InitData(); void Clear(); void Shutdown(); diff --git a/source/common/textures/buildtiles.cpp b/source/common/textures/buildtiles.cpp index d061d84b1..fded6c4ad 100644 --- a/source/common/textures/buildtiles.cpp +++ b/source/common/textures/buildtiles.cpp @@ -379,9 +379,10 @@ FTexture* BuildTiles::ValidateCustomTile(int tilenum, int type) else if (type == FTexture::Restorable) { // This is for modifying an existing tile. - // It only gets used for the crosshair and two specific effects: + // It only gets used for the crosshair and a few specific effects: // A) the fire in Blood. // B) the pin display in Redneck Rampage's bowling lanes. + // C) Exhumed's menu plus one special effect tile. // All of these effects should probably be redone without actual texture hacking... if (tile->GetWidth() == 0 || tile->GetHeight() == 0) return nullptr; // The base must have a size for this to work. // todo: invalidate hardware textures for tile. diff --git a/source/common/textures/textures.h b/source/common/textures/textures.h index 22e54b4c6..469ebaeed 100644 --- a/source/common/textures/textures.h +++ b/source/common/textures/textures.h @@ -588,7 +588,7 @@ inline uint8_t* tileData(int num) return tex->GetWritableBuffer(); } -// Some hacks to allow accessing the no lpnger existing arrays as if they still were arrays to avoid changing hundreds of lines of code. +// Some hacks to allow accessing the no longer existing arrays as if they still were arrays to avoid changing hundreds of lines of code. struct TileSiz { const vec2_16_t &operator[](size_t index) diff --git a/source/common/utility/printf.h b/source/common/utility/printf.h index e7d5d4b59..b2511ec8f 100644 --- a/source/common/utility/printf.h +++ b/source/common/utility/printf.h @@ -44,6 +44,7 @@ void I_FatalError(const char* fmt, ...) ATTRIBUTE((format(printf, 1, 2))); // lots of potential for merge conflicts. int PrintString (int iprintlevel, const char *outline); +int VPrintf(int printlevel, const char* format, va_list parms); int Printf (int printlevel, const char *format, ...) ATTRIBUTE((format(printf,2,3))); int Printf (const char *format, ...) ATTRIBUTE((format(printf,1,2))); int DPrintf (int level, const char *format, ...) ATTRIBUTE((format(printf,2,3))); diff --git a/source/common/version.h b/source/common/version.h index 0c8cbbd13..9c83451ec 100644 --- a/source/common/version.h +++ b/source/common/version.h @@ -64,16 +64,19 @@ const char *GetVersionString(); #define SAVESIG_BLD GAMENAME ".Blood" #define SAVESIG_RR GAMENAME ".Redneck" #define SAVESIG_SW GAMENAME ".ShadowWarrior" +#define SAVESIG_PS GAMENAME ".Exhumed" #define MINSAVEVER_DN3D 1 #define MINSAVEVER_BLD 1 #define MINSAVEVER_RR 1 #define MINSAVEVER_SW 1 +#define MINSAVEVER_PS 1 #define SAVEVER_DN3D 1 #define SAVEVER_BLD 1 #define SAVEVER_RR 1 #define SAVEVER_SW 1 +#define SAVEVER_PS 1 #if defined(__APPLE__) || defined(_WIN32) #define GAME_DIR GAMENAME diff --git a/source/exhumed/CMakeLists.txt b/source/exhumed/CMakeLists.txt new file mode 100644 index 000000000..a20ec1157 --- /dev/null +++ b/source/exhumed/CMakeLists.txt @@ -0,0 +1,114 @@ +cmake_minimum_required( VERSION 3.1.0 ) + +if (MSVC) + set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /J" ) +endif() + +include_directories( "${CMAKE_CURRENT_SOURCE_DIR}/../../build/include" ) + +if (WIN32) + include_directories( "${ZLIB_INCLUDE_DIR}" "${ZMUSIC_INCLUDE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/../../platform/windows/include" "${CMAKE_CURRENT_SOURCE_DIR}/../../platform/windows/include/vpx" "${CMAKE_CURRENT_SOURCE_DIR}/../../platform/windows/include/sdl2" ) +else () +include_directories( "${ZLIB_INCLUDE_DIR}" "${ZMUSIC_INCLUDE_DIR}" "${BZIP2_INCLUDE_DIR}" "${LZMA_INCLUDE_DIR}" "${JPEG_INCLUDE_DIR}" "${GDTOA_INCLUDE_DIR}") +endif() + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../build/include + ${CMAKE_CURRENT_SOURCE_DIR}/../libsmackerdec/include + ${CMAKE_CURRENT_SOURCE_DIR}/../thirdparty/include + ${CMAKE_CURRENT_SOURCE_DIR}/../common + ${CMAKE_CURRENT_SOURCE_DIR}/../common/utility + ${CMAKE_CURRENT_SOURCE_DIR}/../common/console + ${CMAKE_CURRENT_SOURCE_DIR}/../common/textures + ${CMAKE_CURRENT_SOURCE_DIR}/../common/fonts + ${CMAKE_CURRENT_SOURCE_DIR}/../common/2d + ${CMAKE_CURRENT_SOURCE_DIR}/../common/music + ${CMAKE_CURRENT_SOURCE_DIR}/../common/input + ${CMAKE_CURRENT_SOURCE_DIR}/../platform ) + + + +#set( NOT_COMPILED_SOURCE_FILES +# src/gamestructures.cpp +#) + +#set_source_files_properties( ${NOT_COMPILED_SOURCE_FILES} PROPERTIES HEADER_FILE_ONLY TRUE ) + +set( PCH_SOURCES + src/aistuff.cpp + src/anims.cpp + src/anubis.cpp + src/bubbles.cpp + src/bullet.cpp + src/cd.cpp + src/enginesubs.cpp + src/exhumed.cpp + src/fish.cpp + src/grenade.cpp + src/gun.cpp + src/init.cpp + src/input.cpp + src/items.cpp + src/lavadude.cpp + src/light.cpp + src/lighting.cpp + src/lion.cpp + src/main.cpp + src/map.cpp + src/menu.cpp + src/mono.cpp + src/move.cpp + src/movie.cpp + src/mummy.cpp + src/network.cpp + src/object.cpp + src/osdcmds.cpp + src/paul.cpp + src/player.cpp + src/queen.cpp + src/ra.cpp + src/random.cpp + src/rat.cpp + src/record.cpp + src/rex.cpp + src/roach.cpp + src/runlist.cpp + src/save.cpp + src/scorp.cpp + src/sequence.cpp + src/serial.cpp + src/set.cpp + src/snake.cpp + src/sound.cpp + src/spider.cpp + src/status.cpp + src/stream.cpp + src/switch.cpp + src/text2.cpp + src/timer.cpp + src/trigdat.cpp + src/view.cpp + src/wasp.cpp + src/d_menu.cpp +) + +if( MSVC ) + enable_precompiled_headers( ../g_pch.h PCH_SOURCES ) + # The original Build code was written with unsigned chars and unfortunately they still haven't been eliminated entirely. + # All other code should stay with signed chars. What a mess... :( + #set_source_files_properties( ${PCH_SOURCES} PROPERTIES COMPILE_FLAGS "/J" ) +else() + # Temporary solution for compilers other than MSVC + set_source_files_properties( ${PCH_SOURCES} PROPERTIES COMPILE_FLAGS "-include g_pch.h" ) +endif() + +file( GLOB HEADER_FILES + src/*.h + ) +add_library( exhumed STATIC + ${HEADER_FILES} + ${PCH_SOURCES} + #${NOT_COMPILED_SOURCE_FILES} + ) + + diff --git a/source/exhumed/src/aistuff.cpp b/source/exhumed/src/aistuff.cpp new file mode 100644 index 000000000..410ebd55a --- /dev/null +++ b/source/exhumed/src/aistuff.cpp @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------- +/* +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 "aistuff.h" + +BEGIN_PS_NS + + +extern int localclock; + +int TimeSlot[KMaxTimeSlots]; + + +void InitTimeSlot() +{ + for (int i = 0; i < KMaxTimeSlots; i++) { + TimeSlot[i] = 0; + } +} + +int GrabTimeSlot(int UNUSED(nVal)) +{ + return -1; + + // BJD - below code found in an early Powerslave release. Doesn't seem to do anything and is missing in later releases. +#if 0 + int ebx = -1; + int esi; + + for (int i = 0; i < nVal; i++) + { + int nSlot = (localclock + i) & 0xF; + + if (ebx >= 0) + { + if (esi <= TimeSlot[nSlot]) { + continue; + } + } + + esi = TimeSlot[nSlot]; + ebx = i; + } + + esi = localclock; + + int edx = ebx; + + while (edx < 16) + { + TimeSlot[(edx + esi) & 0xF]++; + edx += nVal; + } +#endif +} +END_PS_NS diff --git a/source/exhumed/src/aistuff.h b/source/exhumed/src/aistuff.h new file mode 100644 index 000000000..c5365b31c --- /dev/null +++ b/source/exhumed/src/aistuff.h @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __aistuff_h__ +#define __aistuff_h__ + +#include "compat.h" +#include "anubis.h" +#include "bubbles.h" +#include "mummy.h" +#include "rex.h" +#include "roach.h" +#include "scorp.h" +#include "spider.h" +#include "lion.h" +#include "set.h" +#include "queen.h" +#include "wasp.h" +#include "rat.h" +#include "gun.h" +#include "grenade.h" +#include "snake.h" +#include "fish.h" +#include "lavadude.h" +#include "bullet.h" + +BEGIN_PS_NS + +#define KMaxTimeSlots 16 + +void InitTimeSlot(); +int GrabTimeSlot(int nVal); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/anims.cpp b/source/exhumed/src/anims.cpp new file mode 100644 index 000000000..554e00844 --- /dev/null +++ b/source/exhumed/src/anims.cpp @@ -0,0 +1,348 @@ +//------------------------------------------------------------------------- +/* +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 "engine.h" +#include "anims.h" +#include "sequence.h" +#include "runlist.h" +#include "exhumed.h" +#include "sound.h" +#include "random.h" +#include "init.h" +#include + +BEGIN_PS_NS + +#define kMaxAnims 400 + +short nMagicSeq = -1; +short nPreMagicSeq = -1; +short nSavePointSeq = -1; +short nAnimsFree = 0; + +short AnimRunRec[kMaxAnims]; +short AnimsFree[kMaxAnims]; +Anim AnimList[kMaxAnims]; +uint8_t AnimFlags[kMaxAnims]; + +static SavegameHelper sgh("anims", + SV(nMagicSeq), + SV(nPreMagicSeq), + SV(nSavePointSeq), + SV(nAnimsFree), + SA(AnimRunRec), + SA(AnimsFree), + SA(AnimList), + SA(AnimFlags), + nullptr); + +void InitAnims() +{ + for (int i = 0; i < kMaxAnims; i++) { + AnimsFree[i] = i; + } + + nAnimsFree = kMaxAnims; + + nMagicSeq = SeqOffsets[kSeqItems] + 21; + nPreMagicSeq = SeqOffsets[kSeqMagic2]; + nSavePointSeq = SeqOffsets[kSeqItems] + 12; +} + +void DestroyAnim(int nAnim) +{ + short nSprite = AnimList[nAnim].nSprite; + + if (nSprite >= 0) + { + StopSpriteSound(nSprite); + runlist_SubRunRec(AnimRunRec[nAnim]); + runlist_DoSubRunRec(sprite[nSprite].extra); + runlist_FreeRun(sprite[nSprite].lotag - 1); + } + + AnimsFree[nAnimsFree] = nAnim; + nAnimsFree++; +} + +int BuildAnim(int nSprite, int val, int val2, int x, int y, int z, int nSector, int nRepeat, int nFlag) +{ + if (!nAnimsFree) { + return -1; + } + + nAnimsFree--; + + short nAnim = AnimsFree[nAnimsFree]; + + if (nSprite == -1) { + nSprite = insertsprite(nSector, 500); + } + + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + sprite[nSprite].cstat = 0; + + if (nFlag & 4) + { + sprite[nSprite].pal = 4; + sprite[nSprite].shade = -64; + } + else + { + sprite[nSprite].pal = 0; + sprite[nSprite].shade = -12; + } + + sprite[nSprite].clipdist = 10; + sprite[nSprite].xrepeat = nRepeat; + sprite[nSprite].yrepeat = nRepeat; + sprite[nSprite].picnum = 1; + sprite[nSprite].ang = 0; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + + // CHECKME - where is hitag set otherwise? + if (sprite[nSprite].statnum < 900) { + sprite[nSprite].hitag = -1; + } + + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].owner = -1; + sprite[nSprite].extra = runlist_AddRunRec(sprite[nSprite].lotag - 1, nAnim | 0x100000); + + AnimRunRec[nAnim] = runlist_AddRunRec(NewRun, nAnim | 0x100000); + AnimList[nAnim].nSprite = nSprite; + AnimFlags[nAnim] = nFlag; + AnimList[nAnim].field_2 = 0; + AnimList[nAnim].nSeq = SeqOffsets[val] + val2; + AnimList[nAnim].field_4 = 256; + + if (nFlag & 0x80) { + sprite[nSprite].cstat |= 0x2; // set transluscence + } + + return nAnim; +} + +short GetAnimSprite(short nAnim) +{ + return AnimList[nAnim].nSprite; +} + +void FuncAnim(int a, int, int nRun) +{ + short nAnim = RunData[nRun].nVal; + assert(nAnim >= 0 && nAnim < kMaxAnims); + + short nSprite = AnimList[nAnim].nSprite; + short nSeq = AnimList[nAnim].nSeq; + + assert(nSprite != -1); + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + case 0x20000: + { + short var_1C = AnimList[nAnim].field_2; + + if (!(sprite[nSprite].cstat & 0x8000)) + { + seq_MoveSequence(nSprite, nSeq, var_1C); + } + + if (sprite[nSprite].statnum == kStatIgnited) + { + short nSpriteB = sprite[nSprite].hitag; + if (nSpriteB > -1) + { + sprite[nSprite].x = sprite[nSpriteB].x; + sprite[nSprite].y = sprite[nSpriteB].y; + sprite[nSprite].z = sprite[nSpriteB].z; + + if (sprite[nSpriteB].sectnum != sprite[nSprite].sectnum) + { + if (sprite[nSpriteB].sectnum < 0 || sprite[nSpriteB].sectnum >= kMaxSectors) + { + DestroyAnim(nAnim); + mydeletesprite(nSprite); + return; + } + else + { + mychangespritesect(nSprite, sprite[nSpriteB].sectnum); + } + } + + if (!var_1C) + { + if (sprite[nSpriteB].cstat != 0x8000) + { + short hitag2 = sprite[nSpriteB].hitag; + sprite[nSpriteB].hitag--; + + if (hitag2 >= 15) + { + runlist_DamageEnemy(nSpriteB, -1, (sprite[nSpriteB].hitag - 14) * 2); + + if (sprite[nSpriteB].shade < 100) + { + sprite[nSpriteB].pal = 0; + sprite[nSpriteB].shade++; + } + + if (!(sprite[nSpriteB].cstat & 101)) + { + DestroyAnim(nAnim); + mydeletesprite(nSprite); + return; + } + } + else + { + sprite[nSpriteB].hitag = 1; + DestroyAnim(nAnim); + mydeletesprite(nSprite); + } + } + else + { + sprite[nSpriteB].hitag = 1; + DestroyAnim(nAnim); + mydeletesprite(nSprite); + } + } + } + } + + AnimList[nAnim].field_2++; + if (AnimList[nAnim].field_2 >= SeqSize[nSeq]) + { + if (AnimFlags[nAnim] & 0x10) + { + AnimList[nAnim].field_2 = 0; + } + else if (nSeq == nPreMagicSeq) + { + AnimList[nAnim].field_2 = 0; + AnimList[nAnim].nSeq = nMagicSeq; + short nAnimSprite = AnimList[nAnim].nSprite; + AnimFlags[nAnim] |= 0x10; + sprite[nAnimSprite].cstat |= 2; + } + else if (nSeq == nSavePointSeq) + { + AnimList[nAnim].field_2 = 0; + AnimList[nAnim].nSeq++; + AnimFlags[nAnim] |= 0x10; + } + else + { + DestroyAnim(nAnim); + mydeletesprite(nSprite); + } + return; + } + + return; + } + + case 0x90000: + { + seq_PlotSequence(a & 0xFFFF, nSeq, AnimList[nAnim].field_2, 0x101); + tsprite[a & 0xFFFF].owner = -1; + return; + } + + case 0xA0000: + { + return; + } + + default: + { + Printf("unknown msg %x for anim\n", a & 0x7F0000); + return; + } + } +} + +void BuildExplosion(short nSprite) +{ + short nSector = sprite[nSprite].sectnum; + + int edx = 36; + + if (SectFlag[nSector] & kSectUnderwater) + { + edx = 75; + } + else if (sprite[nSprite].z == sector[nSector].floorz) + { + edx = 34; + } + + BuildAnim(-1, edx, 0, sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, sprite[nSprite].sectnum, sprite[nSprite].xrepeat, 4); +} + +int BuildSplash(int nSprite, int nSector) +{ + int nRepeat, nSound; + + if (sprite[nSprite].statnum != 200) + { + nRepeat = sprite[nSprite].xrepeat + (RandomWord() % sprite[nSprite].xrepeat); + nSound = kSound0; + } + else + { + nRepeat = 20; + nSound = kSound1; + } + + int bIsLava = SectFlag[nSector] & kSectLava; + + int edx, nFlag; + + if (bIsLava) + { + edx = 43; + nFlag = 4; + } + else + { + edx = 35; + nFlag = 0; + } + + int nAnim = BuildAnim(-1, edx, 0, sprite[nSprite].x, sprite[nSprite].y, sector[nSector].floorz, nSector, nRepeat, nFlag); + + if (!bIsLava) + { + D3PlayFX(StaticSound[nSound] | 0xa00, AnimList[nAnim].nSprite); + } + + return AnimList[nAnim].nSprite; +} +END_PS_NS diff --git a/source/exhumed/src/anims.h b/source/exhumed/src/anims.h new file mode 100644 index 000000000..43b80e5e1 --- /dev/null +++ b/source/exhumed/src/anims.h @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __anims_h__ +#define __anims_h__ + +#include "compat.h" + +BEGIN_PS_NS + +struct Anim +{ + short nSeq; + short field_2; + short field_4; + short nSprite; +}; + +extern Anim AnimList[]; +extern uint8_t AnimFlags[]; + +void InitAnims(); +void DestroyAnim(int nAnim); +int BuildAnim(int nSprite, int val, int val2, int x, int y, int z, int nSector, int nRepeat, int nFlag); +short GetAnimSprite(short nAnim); + +void FuncAnim(int, int, int); +void BuildExplosion(short nSprite); +int BuildSplash(int nSprite, int nSector); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/anubis.cpp b/source/exhumed/src/anubis.cpp new file mode 100644 index 000000000..ff8e37593 --- /dev/null +++ b/source/exhumed/src/anubis.cpp @@ -0,0 +1,502 @@ +//------------------------------------------------------------------------- +/* +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 "exhumed.h" +#include "anubis.h" +#include "engine.h" +#include "runlist.h" +#include "sequence.h" +#include "move.h" +#include "bullet.h" +#include "random.h" +#include "items.h" +#include "object.h" +#include "sound.h" +#include "trigdat.h" +#include + +BEGIN_PS_NS + +#define kMaxAnubis 80 + +struct Anubis +{ + short nHealth; + short nFrame; + short nAction; + short nSprite; + short nTarget; + short f; + short g; + short h; +}; + +Anubis AnubisList[kMaxAnubis]; + +short AnubisSprite = -1; +short AnubisCount = -1; + +static actionSeq ActionSeq[] = { + { 0, 0 }, + { 8, 0 }, + { 16, 0 }, + { 24, 0 }, + { 32, 0 }, + { -1, 0 }, + { 46, 1 }, + { 46, 1 }, + { 47, 1 }, + { 49, 1 }, + { 49, 1 }, + { 40, 1 }, + { 42, 1 }, + { 41, 1 }, + { 43, 1 }, +}; + +short nAnubisDrum = 0; + +static SavegameHelper sgh("anubis", + SA(AnubisList), + SV(AnubisSprite), + SV(AnubisCount), + SV(nAnubisDrum), + nullptr); + + +void InitAnubis() +{ + AnubisCount = kMaxAnubis; + AnubisSprite = 1; + nAnubisDrum = 1; +} + +int BuildAnubis(int nSprite, int x, int y, int z, int nSector, int nAngle, uint8_t bIsDrummer) +{ + AnubisCount--; + short nAnubis = AnubisCount; + + if (nAnubis < 0) { + return -1; + } + + if (nSprite == -1) + { + nSprite = insertsprite(nSector, 101); + } + else + { + changespritestat(nSprite, 101); + + x = sprite[nSprite].x; + y = sprite[nSprite].y; + z = sector[sprite[nSprite].sectnum].floorz; + nAngle = sprite[nSprite].ang; + } + + assert(nSprite >=0 && nSprite < kMaxSprites); + + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + sprite[nSprite].cstat = 0x101; + sprite[nSprite].xoffset = 0; + sprite[nSprite].shade = -12; + sprite[nSprite].yoffset = 0; + sprite[nSprite].picnum = 1; + sprite[nSprite].pal = sector[sprite[nSprite].sectnum].ceilingpal; + sprite[nSprite].clipdist = 60; + sprite[nSprite].ang = nAngle; + sprite[nSprite].xrepeat = 40; + sprite[nSprite].yrepeat = 40; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].hitag = 0; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].extra = -1; + +// GrabTimeSlot(3); + + if (bIsDrummer) + { + AnubisList[nAnubis].nAction = nAnubisDrum + 6; + nAnubisDrum++; + + if (nAnubisDrum >= 5) { + nAnubisDrum = 0; + } + } + else + { + AnubisList[nAnubis].nAction = 0; + } + + AnubisList[nAnubis].nHealth = 540; + AnubisList[nAnubis].nFrame = 0; + AnubisList[nAnubis].nSprite = nSprite; + AnubisList[nAnubis].nTarget = -1; + AnubisList[nAnubis].g = 0; + + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nAnubis | 0x90000); + + runlist_AddRunRec(NewRun, nAnubis | 0x90000); + nCreaturesLeft++; + + return nAnubis | 0x90000; +} + +void FuncAnubis(int a, int nDamage, int nRun) +{ + short nAnubis = RunData[nRun].nVal; + int var_14 = 0; + + assert(nAnubis >= 0 && nAnubis < kMaxAnubis); + + short nSprite = AnubisList[nAnubis].nSprite; + short nAction = AnubisList[nAnubis].nAction; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + case 0x20000: + { + if (nAction < 11) { + Gravity(nSprite); + } + + short nSeq = SeqOffsets[kSeqAnubis] + ActionSeq[nAction].a; + + seq_MoveSequence(nSprite, nSeq, AnubisList[nAnubis].nFrame); + + sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, AnubisList[nAnubis].nFrame); + + AnubisList[nAnubis].nFrame++; + if (AnubisList[nAnubis].nFrame >= SeqSize[nSeq]) + { + AnubisList[nAnubis].nFrame = 0; + var_14 = 1; + } + + short nTarget = AnubisList[nAnubis].nTarget; + + short nFrame = SeqBase[nSeq] + AnubisList[nAnubis].nFrame; + short nFlag = FrameFlag[nFrame]; + + int c = 0; + + if (nAction > 0 && nAction < 11) { + c = MoveCreatureWithCaution(nSprite); + } + + switch (nAction) + { + case 0: + { + if ((nAnubis & 0x1F) == (totalmoves & 0x1F)) + { + if (nTarget < 0) { + nTarget = FindPlayer(nSprite, 100); + } + + if (nTarget >= 0) + { + D3PlayFX(StaticSound[kSound8], nSprite); + AnubisList[nAnubis].nAction = 1; + AnubisList[nAnubis].nFrame = 0; + AnubisList[nAnubis].nTarget = nTarget; + + sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 2; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 2; + } + } + return; + } + case 1: + { + if ((nAnubis & 0x1F) == (totalmoves & 0x1F)) + { + PlotCourseToSprite(nSprite, nTarget); + + sprite[nSprite].xvel = Cos(sprite[nSprite].ang & 0xFFF8) >> 2; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang & 0xFFF8) >> 2; + } + + switch (c & 0xC000) + { + case 0xC000: + { + if ((c & 0x3FFF) == nTarget) + { + int nAng = getangle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y); + int nAngDiff = AngleDiff(sprite[nSprite].ang, nAng); + + if (nAngDiff < 64) + { + AnubisList[nAnubis].nAction = 2; + AnubisList[nAnubis].nFrame = 0; + } + break; // only break if condition met + } + // else we fall through to 0x8000 + fallthrough__; + } + case 0x8000: + { + sprite[nSprite].ang = (sprite[nSprite].ang + 256) & kAngleMask; + sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 2; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 2; + break; + } + + default: + { + if (AnubisList[nAnubis].g) + { + AnubisList[nAnubis].g--; + } + else + { + AnubisList[nAnubis].g = 60; + + if (cansee(sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z - GetSpriteHeight(nSprite), sprite[nSprite].sectnum, + sprite[nTarget].x, sprite[nTarget].y, sprite[nTarget].z - GetSpriteHeight(nTarget), sprite[nTarget].sectnum)) + { + AnubisList[nAnubis].nAction = 3; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].ang = GetMyAngle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y); + AnubisList[nAnubis].nFrame = 0; + } + } + break; + } + } + break; + } + case 2: + { + if (nTarget == -1) + { + AnubisList[nAnubis].nAction = 0; + AnubisList[nAnubis].g = 50; + } + else + { + if (PlotCourseToSprite(nSprite, nTarget) >= 768) + { + AnubisList[nAnubis].nAction = 1; + } + else + { + if (nFlag & 0x80) + { + runlist_DamageEnemy(nTarget, nSprite, 7); + } + } + } + + break; + } + case 3: + { + if (var_14) + { + AnubisList[nAnubis].nAction = 1; + + sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 2; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 2; + AnubisList[nAnubis].nFrame = 0; + } + else + { + // loc_25718: + if (nFlag & 0x80) + { + BuildBullet(nSprite, 8, 0, 0, -1, sprite[nSprite].ang, nTarget + 10000, 1); + } + } + + return; + } + case 4: + case 5: + { + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + + if (var_14) + { + AnubisList[nAnubis].nAction = 1; + AnubisList[nAnubis].nFrame = 0; + } + return; + } + case 6: + case 7: + case 8: + case 9: + case 10: + { + if (var_14) + { + AnubisList[nAnubis].nAction = (RandomSize(3) % 5) + 6; + AnubisList[nAnubis].nFrame = 0; + } + return; + } + case 11: + case 12: + { + if (var_14) + { + AnubisList[nAnubis].nAction = nAction + 2; + AnubisList[nAnubis].nFrame = 0; + + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } + return; + } + case 13: + case 14: + { + sprite[nSprite].cstat &= 0xFEFE; + return; + } + + default: + return; + } + + // loc_2564C: + if (nAction && nTarget != -1) + { + if (!(sprite[nTarget].cstat & 0x101)) + { + AnubisList[nAnubis].nAction = 0; + AnubisList[nAnubis].nFrame = 0; + AnubisList[nAnubis].g = 100; + AnubisList[nAnubis].nTarget = -1; + + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } + } + + return; + } + + case 0x90000: + { + seq_PlotSequence(a & 0xFFFF, SeqOffsets[kSeqAnubis] + ActionSeq[nAction].a, AnubisList[nAnubis].nFrame, ActionSeq[nAction].b); + break; + } + + case 0xA0000: // fall through to next case + { + if (nAction >= 11) { + return; + } + + nDamage = runlist_CheckRadialDamage(nSprite); + fallthrough__; + } + case 0x80000: + { + if (nDamage) + { + if (AnubisList[nAnubis].nHealth <= 0) + return; + + AnubisList[nAnubis].nHealth -= nDamage; + + if (AnubisList[nAnubis].nHealth > 0) + { + short nTarget = a & 0xFFFF; + + // loc_258D6: + if (nTarget < 0) { + return; + } + + if (sprite[nTarget].statnum == 100 || sprite[nTarget].statnum < 199) + { + if (!RandomSize(5)) { + AnubisList[nAnubis].nTarget = nTarget; + } + } + + if (RandomSize(1)) + { + if (nAction >= 6 && nAction <= 10) + { + int nThisSprite = insertsprite(sprite[nSprite].sectnum, 98); + + sprite[nThisSprite].x = sprite[nSprite].x; + sprite[nThisSprite].y = sprite[nSprite].y; + sprite[nThisSprite].z = sector[sprite[nThisSprite].sectnum].floorz; + sprite[nThisSprite].xrepeat = 40; + sprite[nThisSprite].yrepeat = 40; + sprite[nThisSprite].shade = -64; + + BuildObject(nThisSprite, 2, 0); + } + + AnubisList[nAnubis].nAction = 4; + AnubisList[nAnubis].nFrame = 0; + } + else + { + // loc_259B5: + D3PlayFX(StaticSound[kSound39], nSprite); + } + } + else + { + // he ded. + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].z = sector[sprite[nSprite].sectnum].floorz; + sprite[nSprite].cstat &= 0xFEFE; + + AnubisList[nAnubis].nHealth = 0; + + nCreaturesLeft--; + + if (nAction < 11) + { + DropMagic(nSprite); + AnubisList[nAnubis].nAction = (nMessage == 0xA0000) + 11; + AnubisList[nAnubis].nFrame = 0; + } + } + } + + return; + } + default: + { + Printf("unknown msg %d for Anubis\n", a & 0x7F0000); + return; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/anubis.h b/source/exhumed/src/anubis.h new file mode 100644 index 000000000..208b9e2bb --- /dev/null +++ b/source/exhumed/src/anubis.h @@ -0,0 +1,33 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __anubis_h__ +#define __anubis_h__ + +#include "compat.h" + +BEGIN_PS_NS + +void InitAnubis(); +int BuildAnubis(int nSprite, int x, int y, int z, int nSector, int nAngle, uint8_t bIsDrummer); + +void FuncAnubis(int a, int b, int c); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/bubbles.cpp b/source/exhumed/src/bubbles.cpp new file mode 100644 index 000000000..bc5713d01 --- /dev/null +++ b/source/exhumed/src/bubbles.cpp @@ -0,0 +1,252 @@ +//------------------------------------------------------------------------- +/* +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 "bubbles.h" +#include "runlist.h" +#include "exhumed.h" +#include "random.h" +#include "engine.h" +#include "sequence.h" +#include "move.h" +#include "init.h" +#include "runlist.h" +#include "init.h" +#include "anims.h" +#include + +BEGIN_PS_NS + + +#define kMaxBubbles 200 +#define kMaxMachines 125 + +struct Bubble +{ + short _0; + short _2; + short nSprite; + short _6; +}; + +struct machine +{ + short _0; + short nSprite; + short _4; +}; + +short BubbleCount = 0; + +short nFreeCount; +short nMachineCount; + +uint8_t nBubblesFree[kMaxBubbles]; +machine Machine[kMaxMachines]; +Bubble BubbleList[kMaxBubbles]; + +static SavegameHelper sgh("bubbles", + SV(BubbleCount), + SV(nFreeCount), + SV(nMachineCount), + SA(nBubblesFree), + SA(Machine), + SA(BubbleList), + nullptr); + +void InitBubbles() +{ + BubbleCount = 0; + nMachineCount = 0; + + for (int i = 0; i < kMaxBubbles; i++) { + nBubblesFree[i] = i; + } + + nFreeCount = kMaxBubbles; +} + +void DestroyBubble(short nBubble) +{ + short nSprite = BubbleList[nBubble].nSprite; + + runlist_DoSubRunRec(sprite[nSprite].lotag - 1); + runlist_DoSubRunRec(sprite[nSprite].owner); + runlist_SubRunRec(BubbleList[nBubble]._6); + + mydeletesprite(nSprite); + + nBubblesFree[nFreeCount] = nBubble; + + nFreeCount++; +} + +short GetBubbleSprite(short nBubble) +{ + return BubbleList[nBubble].nSprite; +} + +int BuildBubble(int x, int y, int z, short nSector) +{ + int nSize = RandomSize(3); + if (nSize > 4) { + nSize -= 4; + } + + if (nFreeCount <= 0) { + return -1; + } + + nFreeCount--; + + uint8_t nBubble = nBubblesFree[nFreeCount]; + + int nSprite = insertsprite(nSector, 402); + assert(nSprite >= 0 && nSprite < kMaxSprites); + + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + sprite[nSprite].cstat = 0; + sprite[nSprite].shade = -32; + sprite[nSprite].pal = 0; + sprite[nSprite].clipdist = 5; + sprite[nSprite].xrepeat = 40; + sprite[nSprite].yrepeat = 40; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].picnum = 1; + sprite[nSprite].ang = inita; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = -1200; + sprite[nSprite].hitag = -1; + sprite[nSprite].extra = -1; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + +// GrabTimeSlot(3); + + BubbleList[nBubble].nSprite = nSprite; + BubbleList[nBubble]._0 = 0; + BubbleList[nBubble]._2 = SeqOffsets[kSeqBubble] + nSize; + + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nBubble | 0x140000); + + BubbleList[nBubble]._6 = runlist_AddRunRec(NewRun, nBubble | 0x140000); + return nBubble | 0x140000; +} + +void FuncBubble(int a, int UNUSED(b), int nRun) +{ + short nBubble = RunData[nRun].nVal; + assert(nBubble >= 0 && nBubble < kMaxBubbles); + + short nSprite = BubbleList[nBubble].nSprite; + short dx = BubbleList[nBubble]._2; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + case 0x20000: + { + seq_MoveSequence(nSprite, dx, BubbleList[nBubble]._0); + + BubbleList[nBubble]._0++; + + if (BubbleList[nBubble]._0 >= SeqSize[dx]) { + BubbleList[nBubble]._0 = 0; + } + + sprite[nSprite].z += sprite[nSprite].zvel; + + short nSector = sprite[nSprite].sectnum; + + if (sprite[nSprite].z <= sector[nSector].ceilingz) + { + short nSectAbove = SectAbove[nSector]; + + if (sprite[nSprite].hitag > -1 && nSectAbove != -1) { + BuildAnim(-1, 70, 0, sprite[nSprite].x, sprite[nSprite].y, sector[nSectAbove].floorz, nSectAbove, 64, 0); + } + + DestroyBubble(nBubble); + } + + return; + } + + case 0x90000: + { + seq_PlotSequence(a & 0xFFFF, dx, BubbleList[nBubble]._0, 1); + tsprite[a & 0xFFFF].owner = -1; + return; + } + + case 0x80000: + case 0xA0000: + return; + + default: + Printf("unknown msg %d for Bubble\n", nMessage); + return; + } +} + +void DoBubbleMachines() +{ + for (int i = 0; i < nMachineCount; i++) + { + Machine[i]._0--; + + if (Machine[i]._0 <= 0) + { + Machine[i]._0 = (RandomWord() % Machine[i]._4) + 30; + + int nSprite = Machine[i].nSprite; + BuildBubble(sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, sprite[nSprite].sectnum); + } + } +} + +void BuildBubbleMachine(int nSprite) +{ + if (nMachineCount >= kMaxMachines) { + I_Error("too many bubble machines in level %d\n", levelnew); + } + + Machine[nMachineCount]._4 = 75; + Machine[nMachineCount].nSprite = nSprite; + Machine[nMachineCount]._0 = Machine[nMachineCount]._4; + nMachineCount++; + + sprite[nSprite].cstat = 0x8000u; +} + +void DoBubbles(int nPlayer) +{ + int x, y, z; + short nSector; + + WheresMyMouth(nPlayer, &x, &y, &z, &nSector); + + int nBubble = BuildBubble(x, y, z, nSector); + int nSprite = GetBubbleSprite(nBubble); + + sprite[nSprite].hitag = nPlayer; +} +END_PS_NS diff --git a/source/exhumed/src/bubbles.h b/source/exhumed/src/bubbles.h new file mode 100644 index 000000000..af609ab39 --- /dev/null +++ b/source/exhumed/src/bubbles.h @@ -0,0 +1,34 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __bubbles_h__ +#define __bubbles_h__ + +BEGIN_PS_NS + +void InitBubbles(); + +void BuildBubbleMachine(int nSprite); +void DoBubbleMachines(); + +void DoBubbles(int nPlayer); +void FuncBubble(int, int, int); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/bullet.cpp b/source/exhumed/src/bullet.cpp new file mode 100644 index 000000000..6aabdb4eb --- /dev/null +++ b/source/exhumed/src/bullet.cpp @@ -0,0 +1,869 @@ +//------------------------------------------------------------------------- +/* +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 "engine.h" +#include "bullet.h" +#include "runlist.h" +#include "anims.h" +#include "sequence.h" +#include "exhumed.h" +#include "sound.h" +#include "init.h" +#include "move.h" +#include "player.h" +#include "trigdat.h" +#include "random.h" +#include "gun.h" +#include "names.h" +#include "lighting.h" +#include +#include +#ifndef __WATCOMC__ +//#include +#else +//#include +#include +#endif + +BEGIN_PS_NS + +#define kMaxBullets 500 + +short BulletFree[kMaxBullets]; + +// 32 bytes +struct Bullet +{ + short nSeq; // 0 + short field_2; // 2 + short nSprite; // 4 + short field_6; + short field_8; + short nType; + short field_C; + short field_E; + uint16_t field_10; + uint8_t field_12; + uint8_t field_13; + int x; + int y; + int z; +}; + +Bullet BulletList[kMaxBullets]; +short nBulletEnemy[kMaxBullets]; +int nBulletsFree; +int lasthitz, lasthitx, lasthity; +short lasthitsect, lasthitsprite, lasthitwall; + +int nBulletCount = 0; +short nRadialBullet = 0; + +static SavegameHelper sgh("bullet", + SV(BulletFree), + SA(BulletList), + SA(nBulletEnemy), + SV(nBulletsFree), + SV(lasthitz), + SV(lasthitx), + SV(lasthity), + SV(lasthitsect), + SV(lasthitsprite), + SV(lasthitwall), + SV(nBulletCount), + SV(nRadialBullet), + nullptr); + + +bulletInfo BulletInfo[] = { + { 25, 1, 20, -1, -1, 13, 0, 0, -1, 0 }, + { 25, -1, 65000, -1, 31, 73, 0, 0, -1, 0 }, + { 15, -1, 60000, -1, 31, 73, 0, 0, -1, 0 }, + { 5, 15, 2000, -1, 14, 38, 4, 5, 3, 0 }, + { 250, 100, 2000, -1, 33, 34, 4, 20, -1, 0 }, + { 200, -1, 2000, -1, 20, 23, 4, 10, -1, 0 }, + { 200, -1, 60000, 68, 68, -1, -1, 0, -1, 0 }, + { 300, 1, 0, -1, -1, -1, 0, 50, -1, 0 }, + { 18, -1, 2000, -1, 18, 29, 4, 0, -1, 0 }, + { 20, -1, 2000, 37, 11, 30, 4, 0, -1, 0 }, + { 25, -1, 3000, -1, 44, 36, 4, 15, 90, 0 }, + { 30, -1, 1000, -1, 52, 53, 4, 20, 48, 0 }, + { 20, -1, 3500, -1, 54, 55, 4, 30, -1, 0 }, + { 10, -1, 5000, -1, 57, 76, 4, 0, -1, 0 }, + { 40, -1, 1500, -1, 63, 38, 4, 10, 40, 0 }, + { 20, -1, 2000, -1, 60, 12, 0, 0, -1, 0 }, + { 5, -1, 60000, -1, 31, 76, 0, 0, -1, 0 } +}; + + +void InitBullets() +{ + nBulletCount = 0; + + for (int i = 0; i < kMaxBullets; i++) { + BulletFree[i] = i; + } + + nBulletsFree = kMaxBullets; + + memset(nBulletEnemy, -1, sizeof(nBulletEnemy)); +} + +short GrabBullet() +{ + nBulletsFree--; + return BulletFree[nBulletsFree]; +} + +void DestroyBullet(short nBullet) +{ + short nSprite = BulletList[nBullet].nSprite; + + runlist_DoSubRunRec(BulletList[nBullet].field_6); + runlist_DoSubRunRec(sprite[nSprite].lotag - 1); + runlist_SubRunRec(BulletList[nBullet].field_8); + + StopSpriteSound(nSprite); + + mydeletesprite(nSprite); + + BulletFree[nBulletsFree] = nBullet; + nBulletsFree++; +} + +void IgniteSprite(int nSprite) +{ + sprite[nSprite].hitag += 2; + + int nAnim = BuildAnim(-1, 38, 0, sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, sprite[nSprite].sectnum, 40, 20);//276); + short nAnimSprite = GetAnimSprite(nAnim); + + sprite[nAnimSprite].hitag = nSprite; + changespritestat(nAnimSprite, kStatIgnited); + + short yRepeat = (tilesiz[sprite[nAnimSprite].picnum].y * 32) / nFlameHeight; + if (yRepeat < 1) + yRepeat = 1; + + sprite[nAnimSprite].yrepeat = (uint8_t)yRepeat; +} + +void BulletHitsSprite(Bullet *pBullet, short nBulletSprite, short nHitSprite, int x, int y, int z, int nSector) +{ + assert(nSector >= 0 && nSector < kMaxSectors); + + bulletInfo *pBulletInfo = &BulletInfo[pBullet->nType]; + + short nStat = sprite[nHitSprite].statnum; + + switch (pBullet->nType) + { + case 3: + { + if (nStat > 107 || nStat == 98) { + return; + } + + sprite[nHitSprite].hitag++; + + if (sprite[nHitSprite].hitag == 15) { + IgniteSprite(nHitSprite); + } + + if (!RandomSize(2)) { + BuildAnim(-1, pBulletInfo->field_C, 0, x, y, z, nSector, 40, pBulletInfo->nFlags); + } + + return; + } + case 14: + { + if (nStat > 107 || nStat == 98) { + return; + } + // else - fall through to below cases + fallthrough__; + } + case 1: + case 2: + case 8: + case 9: + case 12: + case 13: + case 15: + case 16: + { + // loc_29E59 + if (!nStat || nStat > 98) { + break; + } + + short nSprite = pBullet->nSprite; + spritetype *pSprite = &sprite[nSprite]; + spritetype *pHitSprite = &sprite[nHitSprite]; + + if (nStat != 98) + { + int xVel = pHitSprite->xvel; + int yVel = pHitSprite->yvel; + + pHitSprite->xvel = Cos(pSprite->ang) >> 2; + pHitSprite->yvel = Sin(pSprite->ang) >> 2; + + MoveCreature(nHitSprite); + + pHitSprite->xvel = xVel; + pHitSprite->yvel = yVel; + } + else + { + short nAngle = pSprite->ang - (RandomSize(9) - 256); + + pHitSprite->xvel = Cos(nAngle) << 1; + pHitSprite->yvel = Sin(nAngle) << 1; + pHitSprite->zvel = (-(RandomSize(3) + 1)) << 8; + } + + break; + } + + default: + break; + } + + // BHS_switchBreak: + short nDamage = pBulletInfo->nDamage; + + if (pBullet->field_13 > 1) { + nDamage *= 2; + } + + runlist_DamageEnemy(nHitSprite, nBulletSprite, nDamage); + + if (nStat <= 90 || nStat >= 199) + { + BuildAnim(-1, pBulletInfo->field_C, 0, x, y, z, nSector, 40, pBulletInfo->nFlags); + return; + } + + switch (nStat) + { + case 97: + break; + case 0: + case 98: + case 102: + case 141: + case 152: + BuildAnim(-1, 12, 0, x, y, z, nSector, 40, 0); + break; + default: + BuildAnim(-1, 39, 0, x, y, z, nSector, 40, 0); + if (pBullet->nType > 2) + { + BuildAnim(-1, pBulletInfo->field_C, 0, x, y, z, nSector, 40, pBulletInfo->nFlags); + } + break; + } +} + + +void BackUpBullet(int *x, int *y, short nAngle) +{ + *x -= Cos(nAngle) >> 11; + *y -= Sin(nAngle) >> 11; +} + +int MoveBullet(short nBullet) +{ + short hitsect = -1; + short hitwall = -1; + short hitsprite = -1; + + Bullet *pBullet = &BulletList[nBullet]; + short nType = pBullet->nType; + bulletInfo *pBulletInfo = &BulletInfo[nType]; + + short nSprite = BulletList[nBullet].nSprite; + spritetype *pSprite = &sprite[nSprite]; + + int x = pSprite->x; + int y = pSprite->y; + int z = pSprite->z; // ebx + short nSectFlag = SectFlag[pSprite->sectnum]; + + int x2, y2, z2; + + int nVal; + + if (pBullet->field_10 < 30000) + { + short nEnemySprite = nBulletEnemy[nBullet]; + if (nEnemySprite > -1) + { + if (!(sprite[nEnemySprite].cstat & 0x101)) + nBulletEnemy[nBullet] = -1; + else + { + nVal = AngleChase(nSprite, nEnemySprite, pBullet->field_10, 0, 16); + goto MOVEEND; + } + } + if (nType == 3) + { + if (pBullet->field_E < 8) + { + pSprite->xrepeat -= 1; + pSprite->yrepeat += 8; + + pBullet->z -= 200; + + if (pSprite->shade < 90) { + pSprite->shade += 35; + } + + if (pBullet->field_E == 3) + { + pBullet->nSeq = 45; + pBullet->field_2 = 0; + pSprite->xrepeat = 40; + pSprite->yrepeat = 40; + pSprite->shade = 0; + pSprite->z += 512; + } + } + else + { + pSprite->xrepeat += 4; + pSprite->yrepeat += 4; + } + } + + // loc_2A1DD + nVal = movesprite(nSprite, pBullet->x, pBullet->y, pBullet->z, pSprite->clipdist >> 1, pSprite->clipdist >> 1, CLIPMASK1); + +MOVEEND: + if (nVal) + { + x2 = pSprite->x; + y2 = pSprite->y; + z2 = pSprite->z; + hitsect = pSprite->sectnum; + + if (nVal & 0x30000) + { + hitwall = nVal & 0x3FFF; + goto HITWALL; + } + else + { + switch (nVal & 0xc000) + { + case 0x8000: + hitwall = nVal & 0x3FFF; + goto HITWALL; + case 0xc000: + hitsprite = nVal & 0x3FFF; + goto HITSPRITE; + } + } + } + + // sprite[nSprite].sectnum may have changed since we set nSectFlag ? + short nFlagVal = nSectFlag ^ SectFlag[pSprite->sectnum]; + if (nFlagVal & kSectUnderwater) + { + DestroyBullet(nBullet); + nVal = 1; + } + + if (nVal == 0 && nType != 15 && nType != 3) + { + AddFlash(sprite[nSprite].sectnum, sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, 0); + + if (sprite[nSprite].pal != 5) { + sprite[nSprite].pal = 1; + } + } + } + else + { + nVal = 1; + + if (nBulletEnemy[nBullet] > -1) + { + hitsprite = nBulletEnemy[nBullet]; + x2 = sprite[hitsprite].x; + y2 = sprite[hitsprite].y; + z2 = sprite[hitsprite].z - (GetSpriteHeight(hitsprite) >> 1); + hitsect = sprite[hitsprite].sectnum; + } + else + { + vec3_t startPos = { x, y, z }; + hitdata_t hitData; + int dz; + if (bVanilla) + dz = -Sin(pBullet->field_C) * 8; + else + dz = -pBullet->field_C * 512; + hitscan(&startPos, pSprite->sectnum, Cos(pSprite->ang), Sin(pSprite->ang), dz, &hitData, CLIPMASK1); + x2 = hitData.pos.x; + y2 = hitData.pos.y; + z2 = hitData.pos.z; + hitsprite = hitData.sprite; + hitsect = hitData.sect; + hitwall = hitData.wall; + } + + lasthitx = x2; + lasthity = y2; + lasthitz = z2; + lasthitsect = hitsect; + lasthitwall = hitwall; + lasthitsprite = hitsprite; + + if (hitsprite > -1) + { +HITSPRITE: + if (pSprite->pal == 5 && sprite[hitsprite].statnum == 100) + { + short nPlayer = GetPlayerFromSprite(hitsprite); + if (!PlayerList[nPlayer].bIsMummified) + { + PlayerList[nPlayer].bIsMummified = kTrue; + SetNewWeapon(nPlayer, kWeaponMummified); + } + } + else + { +// assert(hitsect <= kMaxSectors); + + BulletHitsSprite(pBullet, pSprite->owner, hitsprite, x2, y2, z2, hitsect); + } + } + else if (hitwall > -1) + { +HITWALL: + if (wall[hitwall].picnum == kEnergy1) + { + short nSector = wall[hitwall].nextsector; + if (nSector > -1) + { + short nDamage = BulletInfo[pBullet->nType].nDamage; + if (pBullet->field_13 > 1) { + nDamage *= 2; + } + + short nNormal = GetWallNormal(hitwall) & kAngleMask; + + runlist_DamageEnemy(sector[nSector].extra, nNormal, nDamage); + } + } + } + + // loc_2A4F5:? + if (hitsprite < 0 && hitwall < 0) + { + if ((SectBelow[hitsect] >= 0 && (SectFlag[SectBelow[hitsect]] & kSectUnderwater)) || SectDepth[hitsect]) + { + pSprite->x = x2; + pSprite->y = y2; + pSprite->z = z2; + BuildSplash(nSprite, hitsect); + } + else + { + BuildAnim(-1, pBulletInfo->field_C, 0, x2, y2, z2, hitsect, 40, pBulletInfo->nFlags); + } + } + else if (hitwall >= 0) + { + BackUpBullet(&x2, &y2, pSprite->ang); + + if (nType != 3 || RandomSize(2) == 0) + { + int zOffset = RandomSize(8) << 3; + + if (!RandomBit()) { + zOffset = -zOffset; + } + + // draws bullet puff on walls when they're shot + BuildAnim(-1, pBulletInfo->field_C, 0, x2, y2, z2 + zOffset, hitsect, 40, pBulletInfo->nFlags); + } + } + else + { + pSprite->x = x2; + pSprite->y = y2; + pSprite->z = z2; + + mychangespritesect(nSprite, hitsect); + } + + // loc_2A639: + if (BulletInfo[nType].field_10) + { + nRadialBullet = nType; + + runlist_RadialDamageEnemy(nSprite, pBulletInfo->nDamage, pBulletInfo->field_10); + + nRadialBullet = -1; + + AddFlash(pSprite->sectnum, pSprite->x, pSprite->y, pSprite->z, 128); + } + + DestroyBullet(nBullet); + } + + return nVal; +} + +void SetBulletEnemy(short nBullet, short nEnemy) +{ + if (nBullet >= 0) { + nBulletEnemy[nBullet] = nEnemy; + } +} + +int BuildBullet(short nSprite, int nType, int UNUSED(ebx), int UNUSED(ecx), int val1, int nAngle, int val2, int val3) +{ + Bullet sBullet; + bulletInfo *pBulletInfo = &BulletInfo[nType]; + + if (pBulletInfo->field_4 > 30000) + { + if (val2 >= 10000) + { + val2 -= 10000; + + short nTargetSprite = val2; + spritetype *pTargetSprite = &sprite[nTargetSprite]; + +// assert(sprite[nTargetSprite].sectnum <= kMaxSectors); + + if (pTargetSprite->cstat & 0x101) + { + sBullet.nType = nType; + sBullet.field_13 = val3; + + sBullet.nSprite = insertsprite(sprite[nSprite].sectnum, 200); + sprite[sBullet.nSprite].ang = nAngle; + + int nHeight = GetSpriteHeight(nTargetSprite); + + assert(sprite[nTargetSprite].sectnum >= 0 && sprite[nTargetSprite].sectnum < kMaxSectors); + + BulletHitsSprite(&sBullet, nSprite, nTargetSprite, pTargetSprite->x, pTargetSprite->y, pTargetSprite->z - (nHeight >> 1), pTargetSprite->sectnum); + mydeletesprite(sBullet.nSprite); + return -1; + } + else + { + val2 = 0; + } + } + } + + if (!nBulletsFree) { + return -1; + } + + short nSector; + + if (sprite[nSprite].statnum == 100) + { + nSector = nPlayerViewSect[GetPlayerFromSprite(nSprite)]; + } + else + { + nSector = sprite[nSprite].sectnum; + } + + short nBulletSprite = insertsprite(nSector, 200); + int nHeight = GetSpriteHeight(nSprite); + nHeight = nHeight - (nHeight >> 2); + + if (val1 == -1) { + val1 = -nHeight; + } + + sprite[nBulletSprite].x = sprite[nSprite].x; + sprite[nBulletSprite].y = sprite[nSprite].y; + sprite[nBulletSprite].z = sprite[nSprite].z; + + // why is this done here??? + assert(nBulletSprite >= 0 && nBulletSprite < kMaxSprites); + + short nBullet = GrabBullet(); + Bullet *pBullet = &BulletList[nBullet]; + + nBulletEnemy[nBullet] = -1; + + sprite[nBulletSprite].cstat = 0; + sprite[nBulletSprite].shade = -64; + + if (pBulletInfo->nFlags & 4) { + sprite[nBulletSprite].pal = 4; + } + else { + sprite[nBulletSprite].pal = 0; + } + + sprite[nBulletSprite].clipdist = 25; + + short nRepeat = pBulletInfo->xyRepeat; + if (nRepeat < 0) { + nRepeat = 30; + } + + sprite[nBulletSprite].xrepeat = nRepeat; + sprite[nBulletSprite].yrepeat = nRepeat; + sprite[nBulletSprite].xoffset = 0; + sprite[nBulletSprite].yoffset = 0; + sprite[nBulletSprite].ang = nAngle; + sprite[nBulletSprite].xvel = 0; + sprite[nBulletSprite].yvel = 0; + sprite[nBulletSprite].zvel = 0; + sprite[nBulletSprite].owner = nSprite; + sprite[nBulletSprite].lotag = runlist_HeadRun() + 1; + sprite[nBulletSprite].extra = -1; + sprite[nBulletSprite].hitag = 0; + +// GrabTimeSlot(3); + + pBullet->field_10 = 0; + pBullet->field_E = pBulletInfo->field_2; + pBullet->field_2 = 0; + + short nSeq; + + if (pBulletInfo->field_8 != -1) + { + pBullet->field_12 = 0; + nSeq = pBulletInfo->field_8; + } + else + { + pBullet->field_12 = 1; + nSeq = pBulletInfo->nSeq; + } + + pBullet->nSeq = nSeq; + + sprite[nBulletSprite].picnum = seq_GetSeqPicnum(nSeq, 0, 0); + + if (nSeq == kSeqBullet) { + sprite[nBulletSprite].cstat |= 0x8000; + } + + pBullet->field_C = val2; + pBullet->nType = nType; + pBullet->nSprite = nBulletSprite; + pBullet->field_6 = runlist_AddRunRec(sprite[nBulletSprite].lotag - 1, nBullet | 0xB0000); + pBullet->field_8 = runlist_AddRunRec(NewRun, nBullet | 0xB0000); + pBullet->field_13 = val3; + sprite[nBulletSprite].z += val1; + + int var_18; + + nSector = sprite[nBulletSprite].sectnum; + + while (sprite[nBulletSprite].z < sector[nSector].ceilingz) + { + if (SectAbove[nSector] == -1) + { + sprite[nBulletSprite].z = sector[nSector].ceilingz; + break; + } + + nSector = SectAbove[nSector]; + mychangespritesect(nBulletSprite, nSector); + } + + if (val2 < 10000) + { + var_18 = ((-Sin(val2)) * pBulletInfo->field_4) >> 11; + } + else + { + val2 -= 10000; + + short nTargetSprite = val2; + + if ((unsigned int)pBulletInfo->field_4 > 30000) + { + nBulletEnemy[nBullet] = nTargetSprite; + } + else + { + nHeight = GetSpriteHeight(nTargetSprite); + + if (sprite[nTargetSprite].statnum == 100) + { + nHeight -= nHeight >> 2; + } + else + { + nHeight -= nHeight >> 1; + } + + int var_20 = sprite[nTargetSprite].z - nHeight; + + int x, y; + + if (nSprite != -1 && sprite[nSprite].statnum != 100) + { + x = sprite[nTargetSprite].x; + y = sprite[nTargetSprite].y; + + if (sprite[nTargetSprite].statnum != 100) + { + x += (sprite[nTargetSprite].xvel * 20) >> 6; + y += (sprite[nTargetSprite].yvel * 20) >> 6; + } + else + { + int nPlayer = GetPlayerFromSprite(nTargetSprite); + if (nPlayer > -1) + { + x += nPlayerDX[nPlayer] * 15; + y += nPlayerDY[nPlayer] * 15; + } + } + + x -= sprite[nBulletSprite].x; + y -= sprite[nBulletSprite].y; + + nAngle = GetMyAngle(x, y); + sprite[nSprite].ang = nAngle; + } + else + { + // loc_2ABA3: + x = sprite[nTargetSprite].x - sprite[nBulletSprite].x; + y = sprite[nTargetSprite].y - sprite[nBulletSprite].y; + } + + int nSqrt = lsqrt(y*y + x*x); + if ((unsigned int)nSqrt > 0) + { + var_18 = ((var_20 - sprite[nBulletSprite].z) * pBulletInfo->field_4) / nSqrt; + } + else + { + var_18 = 0; + } + } + } + + pBullet->z = 0; + pBullet->x = (sprite[nSprite].clipdist << 2) * Cos(nAngle); + pBullet->y = (sprite[nSprite].clipdist << 2) * Sin(nAngle); + nBulletEnemy[nBullet] = -1; + + if (MoveBullet(nBullet)) + { + nBulletSprite = -1; + } + else + { + pBullet->field_10 = pBulletInfo->field_4; + pBullet->x = (Cos(nAngle) >> 3) * pBulletInfo->field_4; + pBullet->y = (Sin(nAngle) >> 3) * pBulletInfo->field_4; + pBullet->z = var_18 >> 3; + } + + return nBulletSprite | (nBullet << 16); +} + +void FuncBullet(int a, int UNUSED(b), int nRun) +{ + short nBullet = RunData[nRun].nVal; + assert(nBullet >= 0 && nBullet < kMaxBullets); + + short nSeq = SeqOffsets[BulletList[nBullet].nSeq]; + short nSprite = BulletList[nBullet].nSprite; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + case 0x20000: + { + short nFlag = FrameFlag[SeqBase[nSeq] + BulletList[nBullet].field_2]; + + seq_MoveSequence(nSprite, nSeq, BulletList[nBullet].field_2); + + if (nFlag & 0x80) + { + BuildAnim(-1, 45, 0, sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, sprite[nSprite].sectnum, sprite[nSprite].xrepeat, 0); + } + + BulletList[nBullet].field_2++; + if (BulletList[nBullet].field_2 >= SeqSize[nSeq]) + { + if (!BulletList[nBullet].field_12) + { + BulletList[nBullet].nSeq = BulletInfo[BulletList[nBullet].nType].nSeq; + BulletList[nBullet].field_12++; + } + + BulletList[nBullet].field_2 = 0; + } + + if (BulletList[nBullet].field_E != -1 && --BulletList[nBullet].field_E == 0) + { + DestroyBullet(nBullet); + } + else + { + MoveBullet(nBullet); + } + break; + } + + case 0x90000: + { + short nSprite2 = a & 0xFFFF; + tsprite[nSprite2].statnum = 1000; + + if (BulletList[nBullet].nType == 15) + { + seq_PlotArrowSequence(nSprite2, nSeq, BulletList[nBullet].field_2); + } + else + { + seq_PlotSequence(nSprite2, nSeq, BulletList[nBullet].field_2, 0); + tsprite[nSprite2].owner = -1; + } + break; + } + + case 0xA0000: + break; + + default: + { + Printf("unknown msg %x for bullet\n", a & 0x7F0000); + return; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/bullet.h b/source/exhumed/src/bullet.h new file mode 100644 index 000000000..c51ea97a6 --- /dev/null +++ b/source/exhumed/src/bullet.h @@ -0,0 +1,59 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __bullet_h__ +#define __bullet_h__ + +BEGIN_PS_NS + +// 32 bytes +struct bulletInfo +{ + short nDamage; // 0 + short field_2; // 2 + int field_4; // 4 + short field_8; // 8 + short nSeq; // 10 + short field_C; // 12 + short nFlags; + short field_10; // damage radius? + short xyRepeat; + char pad[12]; +}; + +extern bulletInfo BulletInfo[]; + +extern short nRadialBullet; +extern short lasthitsect; +extern int lasthitz; +extern int lasthitx; +extern int lasthity; + +void InitBullets(); +short GrabBullet(); +void DestroyBullet(short nRun); +int MoveBullet(short nBullet); +void SetBulletEnemy(short nBullet, short nEnemy); +int BuildBullet(short nSprite, int nType, int ebx, int ecx, int val1, int nAngle, int val2, int val3); +void IgniteSprite(int nSprite); +void FuncBullet(int, int, int); +void BackUpBullet(int *x, int *y, short nAngle); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/cd.cpp b/source/exhumed/src/cd.cpp new file mode 100644 index 000000000..44375b96b --- /dev/null +++ b/source/exhumed/src/cd.cpp @@ -0,0 +1,102 @@ +//------------------------------------------------------------------------- +/* +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 "build.h" +#include "compat.h" +#include "baselayer.h" +#include "cd.h" +#include "sound.h" +#include "exhumed.h" +#include +#include +#include "z_music.h" + +BEGIN_PS_NS + +extern short word_9AC30; + +int nLastVolumeSet = 0; + +/* TODO + +Currently playing music must keep playing on return to map screen or exit from training level + +*/ + +bool playCDtrack(int nTrack, bool bLoop) +{ + if (nTrack < 2) { + return false; + } + + StopCD(); + + char filename[128]; + + // try ogg vorbis now + sprintf(filename, "exhumed%02d.ogg", nTrack); + Mus_Play(nullptr, filename, true); + return true; +} + +void StartfadeCDaudio() +{ +} + +int StepFadeCDaudio() +{ + if (!CDplaying()) { + return 0; + } + Mus_Stop(); + return 1; +} + +bool CDplaying() +{ + return Mus_IsPlaying(); +} + +void StopCD() +{ + Mus_Stop(); +} + +void FadeSong() +{ +} + +int fadecdaudio() +{ + StartfadeCDaudio(); + + while (1) + { + if (!StepFadeCDaudio()) { + return 1; + } + else { + WaitTicks(1); + } + } + + return 1; +} + + +END_PS_NS diff --git a/source/exhumed/src/cd.h b/source/exhumed/src/cd.h new file mode 100644 index 000000000..811730e35 --- /dev/null +++ b/source/exhumed/src/cd.h @@ -0,0 +1,31 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __cd_h__ +#define __cd_h__ + +BEGIN_PS_NS +bool playCDtrack(int nTrack, bool bLoop); +void StartfadeCDaudio(); +int StepFadeCDaudio(); +bool CDplaying(); +void StopCD(); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/d_menu.cpp b/source/exhumed/src/d_menu.cpp new file mode 100644 index 000000000..5b750a4be --- /dev/null +++ b/source/exhumed/src/d_menu.cpp @@ -0,0 +1,233 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 2016 EDuke32 developers and contributors +Copyright (C) 2019 Christoph Oelckers + +This 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" // Must come before everything else! +#include "build.h" +#include "osd.h" +#include "osd.h" +#include "exhumed.h" +#include "engine.h" +#include "sound.h" +#include "names.h" +#include "version.h" + + +#include "menu/menu.h" + +#include "../../glbackend/glbackend.h" + + +BEGIN_PS_NS + +int handle1; + + +int MenuExitCondition; +int MenuStartCondition; + +int menu_Menu(int nVal) +{ + MenuStartCondition = nVal; + MenuExitCondition = -2; + M_StartControlPanel(false); + M_SetMenu(NAME_MainMenu); + while (M_Active()) + { + auto nLogoTile = EXHUMED ? kExhumedLogo : kPowerslaveLogo; + int dword_9AB5F = ((int)totalclock / 16) & 3; + + videoClearScreen(blackcol); + + overwritesprite(160, 100, kSkullHead, 32, 3, kPalNormal); + overwritesprite(161, 130, kSkullJaw, 32, 3, kPalNormal); + + overwritesprite(160, 40, nLogoTile, 32, 3, kPalNormal); + + // draw the fire urn/lamp thingies + overwritesprite(50, 150, kTile3512 + dword_9AB5F, 32, 3, kPalNormal); + overwritesprite(270, 150, kTile3512 + ((dword_9AB5F + 2) & 3), 32, 3, kPalNormal); + + HandleAsync(); + videoNextPage(); + + } + return MenuExitCondition; +} + + +//---------------------------------------------------------------------------- +// +// Implements the native looking menu used for the main menu +// and the episode/skill selection screens, i.e. the parts +// that need to look authentic +// +//---------------------------------------------------------------------------- +void menu_DoPlasma(); +int zoomsize = 0; + +class PSMainMenu : public DListMenu +{ + + void Init(DMenu* parent, FListMenuDescriptor* desc) override + { + DListMenu::Init(parent, desc); + PlayLocalSound(StaticSound[kSound31], 0); + } + + void Ticker() + { + // handle the menu zoom-in + if (zoomsize < 0x10000) + { + zoomsize += 4096; + if (zoomsize >= 0x10000) { + zoomsize = 0x10000; + } + } + } + + void PreDraw() override + { + menu_DoPlasma(); + } +}; + + +//---------------------------------------------------------------------------- +// +// Menu related game interface functions +// +//---------------------------------------------------------------------------- + +void GameInterface::DrawNativeMenuText(int fontnum, int state, double xpos, double ypos, float fontscale, const char* text, int flags) +{ + int tilenum = (int)strtoll(text, nullptr, 0); + double y = ypos - tilesiz[tilenum].y / 2; + + int8_t shade; + + if (state == NIT_SelectedState) + { // currently selected menu item + shade = Sin((int)totalclock << 4) >> 9; + } + else if (state == NIT_ActiveState) { + shade = 0; + } + else { + shade = 25; + } + + picanm[tilenum].xofs = 0; + picanm[tilenum].yofs = 0; + rotatesprite(160 << 16, int((y + tilesiz[tilenum].y) *65536), zoomsize, 0, tilenum, shade, 0, 2, 0, 0, xdim, ydim); + + // tilesizx is 51 + // tilesizy is 33 + + if (state == NIT_SelectedState) + { + overwritesprite(62, short(ypos - 12), kMenuCursorTile, 0, 2, kPalNormal); + overwritesprite(62 + 146, short(ypos - 12), kMenuCursorTile, 0, 10, kPalNormal); + } +} + + +void GameInterface::MenuOpened() +{ + GrabPalette(); + zoomsize = 0; + StopAllSounds(); + StopLocalSound(); +} + +void GameInterface::MenuSound(EMenuSounds snd) +{ + switch (snd) + { + case CursorSound: + PlayLocalSound(StaticSound[kSound35], 0); + break; + + case AdvanceSound: + case BackSound: + PlayLocalSound(StaticSound[kSound33], 0); + break; + + default: + return; + } +} + +void GameInterface::MenuClosed() +{ + +} + + +void GameInterface::StartGame(FGameStartup& gs) +{ + MenuExitCondition = gs.Episode; // Gross hack. The main loop needs to be redone for better handling. +} + +FSavegameInfo GameInterface::GetSaveSig() +{ + return { SAVESIG_PS, MINSAVEVER_PS, SAVEVER_PS }; +} + +void GameInterface::DrawCenteredTextScreen(const DVector2& origin, const char* text, int position, bool bg) +{ + if (text) + { + int height = 11; + + auto lines = FString(text).MakeUpper().Split("\n"); + int y = position - (height * lines.Size() / 2); + for (auto& l : lines) + { + int width = MyGetStringWidth(l); + myprintext(int(origin.X) + 160 - width / 2, int(origin.Y) + y, l, 0); + y += height; + } + } +} + +void GameInterface::DrawMenuCaption(const DVector2& origin, const char* text) +{ + DrawCenteredTextScreen(origin, text, 10, false); +} + + + +END_PS_NS + +//---------------------------------------------------------------------------- +// +// Class registration +// +//---------------------------------------------------------------------------- + + +static TMenuClassDescriptor _mm("Exhumed.MainMenu"); + +void RegisterPSMenus() +{ + menuClasses.Push(&_mm); +} diff --git a/source/exhumed/src/engine.h b/source/exhumed/src/engine.h new file mode 100644 index 000000000..1c3d804b0 --- /dev/null +++ b/source/exhumed/src/engine.h @@ -0,0 +1,67 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __engine_h__ +#define __engine_h__ + +#include "compat.h" +#include "build.h" +#include "pragmas.h" +#include "typedefs.h" +#include "trigdat.h" + +BEGIN_PS_NS + +#define kMaxTiles 6144 +#define kMaxSprites 4096 +#define kMaxSectors 1024 +#define kMaxWalls 8192 +#define kMaxTiles 6144 +#define kMaxVoxels 4096 + +enum +{ + kStatIgnited = 404 +}; + +#define kMaxSpritesOnscreen 1024 + +#define kMaxPalookups 256 +#define kMaxStatus 1024 +//#define MAXPSKYTILES 256 + +inline int Sin(int angle) +{ + return sintable[angle & kAngleMask]; +} + +inline int Cos(int angle) +{ + return sintable[(angle + 512) & kAngleMask]; +} + +int movesprite(short spritenum, int dx, int dy, int dz, int ceildist, int flordist, unsigned int clipmask); +void overwritesprite(int thex, int they, short tilenum, signed char shade, char stat, char dapalnum); +void precache(); +void resettiming(); +void printext(int x, int y, const char* buffer, short tilenum, char invisiblecol); +void kensetpalette(unsigned char *vgapal); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/enginesubs.cpp b/source/exhumed/src/enginesubs.cpp new file mode 100644 index 000000000..121aa8b1b --- /dev/null +++ b/source/exhumed/src/enginesubs.cpp @@ -0,0 +1,179 @@ +//------------------------------------------------------------------------- +/* +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 "engine.h" + +//#include +//#include +#include +#include "gamecvars.h" + +// static int globhiz, globloz, globhihit, globlohit; + +BEGIN_PS_NS + + +void overwritesprite(int thex, int they, short tilenum, signed char shade, char stat, char dapalnum) +{ +#if 0 + rotatesprite(thex << 16, they << 16, 0x10000, (short)((flags & 8) << 7), tilenum, shade, dapalnum, + (char)(((flags & 1 ^ 1) << 4) + (flags & 2) + ((flags & 4) >> 2) + ((flags & 16) >> 2) ^ ((flags & 8) >> 1)), + windowx1, windowy1, windowx2, windowy2); +#endif + // no animation + uint8_t animbak = picanm[tilenum].sf; + picanm[tilenum].sf = 0; + int offx = 0, offy = 0; + if (stat & 1) + { + offx -= tilesiz[tilenum].x>>1; + if (stat & 8) + offx += picanm[tilenum].xofs; + else + offx -= picanm[tilenum].xofs; + offy -= (tilesiz[tilenum].y>>1)+picanm[tilenum].yofs; + } + if (stat&8) + offx += tilesiz[tilenum].x; + if (stat&16) + offy += tilesiz[tilenum].y; + thex += offx; + they += offy; + rotatesprite(thex << 16, they << 16, 65536L, (stat & 8) << 7, tilenum, shade, dapalnum, + 16 + (stat & 2) + ((stat & 4) >> 2) + (((stat & 16) >> 2) ^ ((stat & 8) >> 1)), + windowxy1.x, windowxy1.y, windowxy2.x, windowxy2.y); + picanm[tilenum].sf = animbak; +} + +void permanentwritesprite(int thex, int they, short tilenum, signed char shade, int cx1, int cy1, int cx2, int cy2, char dapalnum) +{ + rotatesprite(thex << 16, they << 16, 65536L, 0, tilenum, shade, dapalnum, 8 + 16, cx1, cy1, cx2, cy2); +} + +void resettiming() +{ + numframes = 0L; + totalclock = 0L; +// TODO totalclocklock = 0L; +} + +void kensetpalette(unsigned char *vgapal) +{ + //setbrightness(0, (char*)vgapal, 4 | 2); + // TODO + Bmemcpy(palette, vgapal, 768); + for (auto &i : palette) + i <<= 2; + videoSetPalette(0, 0, /*4 | */2); +#if 0 + char vesapal[1024]; + + for(int i = 0; i < 256; i++) + { + vesapal[i*4+0] = vgapal[i*3+2]; + vesapal[i*4+1] = vgapal[i*3+1]; + vesapal[i*4+2] = vgapal[i*3+0]; + vesapal[i*4+3] = 0; + } +#ifndef __WATCOMC__ + (0L, 256L, vesapal); +#endif + +#endif +} + +static int32_t xdim_to_320_16(int32_t x) +{ + const int32_t screenwidth = scale(240<<16, xdim, ydim); + return scale(x, screenwidth, xdim) + (160<<16) - (screenwidth>>1); +} + +static int32_t ydim_to_200_16(int32_t y) +{ + y = scale(y, 200<<16, ydim); + return divscale16(y - (200<<15), rotatesprite_yxaspect) - rotatesprite_y_offset + (200<<15); +} + +static int32_t xdim_from_320_16(int32_t x) +{ + const int32_t screenwidth = scale(240<<16, xdim, ydim); + return scale(x + (screenwidth>>1) - (160<<16), xdim, screenwidth); +} + +static int32_t ydim_from_200_16(int32_t y) +{ + y = mulscale16(y + rotatesprite_y_offset - (200<<15), rotatesprite_yxaspect) + (200<<15); + return scale(y, ydim, 200<<16); +} + +void printext(int x, int y, const char *buffer, short tilenum, char UNUSED(invisiblecol)) +{ + int i; + unsigned char ch; +// const int32_t screenwidth = scale(240<<16, xdim, ydim); + + x = xdim_to_320_16(x); + y = ydim_to_200_16(y); + + for (i = 0; buffer[i] != 0; i++) + { + ch = (unsigned char)buffer[i]; + rotatesprite(x - ((ch & 15) << (3+16)), y - ((ch >> 4) << (3+16)), 65536L, 0, tilenum, 0, 0, 2 + 8 + 16 + 128, xdim_from_320_16(x), ydim_from_200_16(y), + xdim_from_320_16(x + (8<<16))-1, ydim_from_200_16(y + (8<<16))-1); + x += (8<<16); + } +} + +void doTileLoad(int i) +{ + tileLoad(i); + +#ifdef USE_OPENGL + if (r_precache) PrecacheHardwareTextures(i); +#endif + +} + +void precache() +{ + int i; + + for (i = 0; i < numsectors; i++) + { + short j = sector[i].ceilingpicnum; + doTileLoad(j); + j = sector[i].floorpicnum; + doTileLoad(j); + } + + for (i = 0; i < numwalls; i++) + { + short j = wall[i].picnum; + doTileLoad(j); + } + + for (i = 0; i < kMaxSprites; i++) + { + if (sprite[i].statnum < kMaxStatus) + { + short j = sprite[i].picnum; + doTileLoad(j); + } + } +} +END_PS_NS diff --git a/source/exhumed/src/exhumed.cpp b/source/exhumed/src/exhumed.cpp new file mode 100644 index 000000000..f75af005a --- /dev/null +++ b/source/exhumed/src/exhumed.cpp @@ -0,0 +1,3519 @@ +//------------------------------------------------------------------------- +/* +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 "baselayer.h" +#include "renderlayer.h" +#include "typedefs.h" +#include "common.h" +#include "engine.h" +#include "exhumed.h" +#include "osdcmds.h" +#include "map.h" +#include "sequence.h" +#include "movie.h" +#include "names.h" +#include "menu.h" +#include "player.h" +#include "network.h" +#include "ps_input.h" +#include "sound.h" +#include "cd.h" +#include "view.h" +#include "status.h" +#include "init.h" +#include "ra.h" +#include "version.h" +#include "timer.h" +#include "runlist.h" +#include "anubis.h" +#include "spider.h" +#include "mummy.h" +#include "fish.h" +#include "lion.h" +#include "light.h" +#include "move.h" +#include "lavadude.h" +#include "rex.h" +#include "set.h" +#include "queen.h" +#include "roach.h" +#include "wasp.h" +#include "scorp.h" +#include "rat.h" +#include "serial.h" +#include "network.h" +#include "random.h" +#include "items.h" +#include "trigdat.h" +#include "record.h" +#include "lighting.h" +#include "mapinfo.h" +#include +#include // for printf +#include +#include +#include +#include +#include +#include "gamecvars.h" +#include "savegamehelp.h" +#include "c_dispatch.h" + +BEGIN_PS_NS + + + extern const char* s_buildRev; + extern const char* s_buildTimestamp; + + +void FinishLevel(); + +int htimer = 0; + +/* these are XORed in the original game executable then XORed back to normal when the game first starts. Here they are normally */ +const char *gString[] = +{ + "CINEMAS", + "THE ANCIENT EGYPTIAN CITY", + "OF KARNAK HAS BEEN SEIZED", + "BY UNKNOWN POWERS, AND GREAT", + "TURMOIL IS SPREADING INTO", + "NEIGHBORING LANDS, POSING", + "A GRAVE THREAT TO PLANET", + "EARTH. MILITANT FORCES FROM", + "ALL PARTS OF THE GLOBE HAVE", + "ENTERED THE KARNAK VALLEY,", + "BUT NONE HAVE RETURNED. THE", + "ONLY KNOWN INFORMATION", + "REGARDING THIS CRISIS CAME", + "FROM A DYING KARNAK VILLAGER", + "WHO MANAGED TO WANDER OUT OF", + "THE VALLEY TO SAFETY.", + "'THEY'VE STOLEN THE GREAT", + "KING'S MUMMY...', MURMURED", + "THE DYING VILLAGER, BUT THE", + "VILLAGER DIED BEFORE HE", + "COULD SAY MORE. WITH NO", + "OTHER OPTIONS, WORLD", + "LEADERS HAVE CHOSEN TO DROP", + "YOU INTO THE VALLEY VIA", + "HELICOPTER IN AN ATTEMPT", + "TO FIND AND DESTROY THE", + "THREATENING FORCES AND", + "RESOLVE THE MYSTERY THAT", + "HAS ENGULFED THIS ONCE", + "PEACEFUL LAND. FLYING AT", + "HIGH ALTITUDE TO AVOID", + "BEING SHOT DOWN LIKE OTHERS", + "BEFORE YOU, YOUR COPTER", + "MYSTERIOUSLY EXPLODES IN THE", + "AIR AS YOU BARELY ESCAPE,", + "WITH NO POSSIBLE CONTACT", + "WITH THE OUTSIDE WORLD.", + "SCARED AS HELL, YOU DESCEND", + "INTO THE HEART OF KARNAK...", + "HOME TO THE CELEBRATED", + "BURIAL CRYPT OF THE GREAT", + "KING RAMSES.", + "END", + "AN EVIL FORCE KNOWN AS THE", + "KILMAAT HAS BESIEGED THE", + "SANCTITY OF MY PALACE AND", + "IS PULLING AT THE VERY", + "TENDRILS OF MY EXISTENCE.", + "THESE FORCES INTEND TO", + "ENSLAVE ME BY REANIMATING", + "MY PRESERVED CORPSE. I HAVE", + "PROTECTED MY CORPSE WITH A", + "GENETIC KEY. IF YOU ARE", + "UNSUCCESSFUL I CANNOT", + "PROTECT CIVILIZATION, AND", + "CHAOS WILL PREVAIL. I AM", + "BEING TORN BETWEEN WORLDS", + "AND THIS INSIDIOUS", + "EXPERIMENT MUST BE STOPPED.", + "END", + "I HAVE HIDDEN A MYSTICAL", + "GAUNTLET AT EL KAB THAT WILL", + "CHANNEL MY ENERGY THROUGH", + "YOUR HANDS. FIND THE", + "GAUNTLET AND CROSS THE ASWAN", + "HIGH DAM TO DEFEAT THE EVIL", + "BEAST SET.", + "END", + "SET WAS A FORMIDABLE FOE.", + "NO MORTAL HAS EVEN CONQUERED", + "THEIR OWN FEAR, MUCH LESS", + "ENTERED MORTAL BATTLE AND", + "TAKEN HIS LIFE.", + "END", + "YOU'VE MADE IT HALFWAY TOWARD", + "FULLFILLING YOUR DESTINY.", + "THE KILMAAT ARE GROWING", + "RESTLESS WITH YOUR PROGRESS.", + "SEEK OUT A TEMPLE IN THIS", + "CITADEL WHERE I WILL PROVIDE", + "MORE INFORMATION", + "END", + "THE KILMAAT RACE HAS", + "CONTINUED THEIR MONSTEROUS", + "ANIMAL-HUMAN EXPERIMENTS IN", + "AN EFFORT TO SOLVE THE KEY OF", + "ANIMATING MY CORPSE. THE", + "VICTORY DEFEATING SET DIDN'T", + "SLOW YOU DOWN AS MUCH AS", + "THEY HAD PLANNED. THEY ARE", + "ACTIVELY ROBBING A SLAVE", + "GIRL OF HER LIFE TO CREATE", + "ANOTHER MONSTEROUS", + "ABOMINATION, COMBINING HUMAN", + "AND INSECT INTENT ON SLAYING", + "YOU. PREPARE YOURSELF FOR", + "BATTLE AS SHE WILL BE WAITING", + "FOR YOU AT THE LUXOR TEMPLE. ", + "END", + "YOU'VE DONE WELL TO DEFEAT", + "SELKIS. YOU HAVE DISTRACTED", + "THE KILMAAT WITH YOUR", + "PRESENCE AND THEY HAVE", + "TEMPORARILY ABANDONED", + "ANIMATION OF MY CORPSE.", + "THE ALIEN QUEEN KILMAATIKHAN", + "HAS A PERSONAL VENDETTA", + "AGAINST YOU. ARROGANCE IS", + "HER WEAKNESS, AND IF YOU CAN", + "DEFEAT KILMAATIKHAN, THE", + "BATTLE WILL BE WON.", + "END", + "THE KILMAAT HAVE BEEN", + "DESTROYED. UNFORTUNATELY,", + "YOUR RECKLESSNESS HAS", + "DESTROYED THE EARTH AND ALL", + "OF ITS INHABITANTS. ALL THAT", + "REMAINS IS A SMOLDERING HUNK", + "OF ROCK.", + "END", + "THE KILMAAT HAVE BEEN", + "DEFEATED AND YOU SINGLE", + "HANDEDLY SAVED THE EARTH", + "FROM DESTRUCTION.", + " ", + " ", + " ", + "YOUR BRAVERY AND HEROISM", + "ARE LEGENDARY.", + "END", + "ITEMS", + "LIFE BLOOD", + "LIFE", + "VENOM", + "YOU'RE LOSING YOUR GRIP", + "FULL LIFE", + "INVINCIBILITY", + "INVISIBILITY", + "TORCH", + "SOBEK MASK", + "INCREASED WEAPON POWER!", + "THE MAP!", + "AN EXTRA LIFE!", + ".357 MAGNUM!", + "GRENADE", + "M-60", + "FLAME THROWER!", + "COBRA STAFF!", + "THE EYE OF RAH GAUNTLET!", + "SPEED LOADER", + "AMMO", + "FUEL", + "COBRA!", + "RAW ENERGY", + "POWER KEY", + "TIME KEY", + "WAR KEY", + "EARTH KEY", + "MAGIC", + "LOCATION PRESERVED", + "COPYRIGHT", + "LOBOTOMY SOFTWARE, INC.", + "3D ENGINE BY 3D REALMS", + "", + "LASTLEVEL", + "INCOMING MESSAGE", + "", + "OUR LATEST SCANS SHOW", + "THAT THE ALIEN CRAFT IS", + "POWERING UP, APPARENTLY", + "IN AN EFFORT TO LEAVE.", + "THE BAD NEWS IS THAT THEY", + "SEEM TO HAVE LEFT A DEVICE", + "BEHIND, AND ALL EVIDENCE", + "SAYS ITS GOING TO BLOW A", + "BIG HOLE IN OUR FINE PLANET.", + "A SQUAD IS TRYING TO DISMANTLE", + "IT RIGHT NOW, BUT NO LUCK SO", + "FAR, AND TIME IS RUNNING OUT.", + "", + "GET ABOARD THAT CRAFT NOW", + "BEFORE IT LEAVES, THEN FIND", + "AND SHOOT ALL THE ENERGY", + "TOWERS TO GAIN ACCESS TO THE", + "CONTROL ROOM. THERE YOU NEED TO", + "TAKE OUT THE CONTROL PANELS AND", + "THE CENTRAL POWER SOURCE. THIS", + "IS THE BIG ONE BUDDY, BEST OF", + "LUCK... FOR ALL OF US.", + "", + "", + "CREDITS", + "EXHUMED", + "", + "EXECUTIVE PRODUCERS", + " ", + "BRIAN MCNEELY", + "PAUL LANGE", + "", + "GAME CONCEPT", + " ", + "PAUL LANGE", + "", + "GAME DESIGN", + " ", + "BRIAN MCNEELY", + "", + "ADDITIONAL DESIGN", + " ", + "PAUL KNUTZEN", + "PAUL LANGE", + "JOHN VAN DEUSEN", + "KURT PFEIFER", + "DOMINICK MEISSNER", + "DANE EMERSON", + "", + "GAME PROGRAMMING", + " ", + "KURT PFEIFER", + "JOHN YUILL", + "", + "ADDITIONAL PROGRAMMING", + " ", + "PAUL HAUGERUD", + "", + "ADDITIONAL TECHNICAL SUPPORT", + " ", + "JOHN YUILL", + "PAUL HAUGERUD", + "JEFF BLAZIER", + "", + "LEVEL DESIGN", + " ", + "PAUL KNUTZEN", + "", + "ADDITIONAL LEVELS", + " ", + "BRIAN MCNEELY", + "", + "MONSTERS AND WEAPONS ", + " ", + "JOHN VAN DEUSEN", + "", + "ARTISTS", + " ", + "BRIAN MCNEELY", + "PAUL KNUTZEN", + "JOHN VAN DEUSEN", + "TROY JACOBSON", + "KEVIN CHUNG", + "ERIC KLOKSTAD", + "RICHARD NICHOLS", + "JOE KRESOJA", + "JASON WIGGIN", + "", + "MUSIC AND SOUND EFFECTS", + " ", + "SCOTT BRANSTON", + "", + "PRODUCT TESTING", + " ", + "DOMINICK MEISSNER", + "TOM KRISTENSEN", + "JASON WIGGIN", + "MARK COATES", + "", + "INSTRUCTION MANUAL", + " ", + "TOM KRISTENSEN", + "", + "SPECIAL THANKS", + " ", + "JACQUI LYONS", + "MARJACQ MICRO, LTD.", + "MIKE BROWN", + "IAN MATHIAS", + "CHERYL LUSCHEI", + "3D REALMS", + "KENNETH SILVERMAN", + "GREG MALONE", + "MILES DESIGN", + "REDMOND AM/PM MINI MART", + "7-11 DOUBLE GULP", + "", + "THANKS FOR PLAYING", + "", + "THE END", + "", + "GUESS YOURE STUCK HERE", + "UNTIL THE SONG ENDS", + "", + "MAYBE THIS IS A GOOD", + "TIME TO THINK ABOUT ALL", + "THE THINGS YOU CAN DO", + "AFTER THE MUSIC IS OVER.", + "", + "OR YOU COULD JUST STARE", + "AT THIS SCREEN", + "", + "AND WATCH THESE MESSAGES", + "GO BY...", + "", + "...AND WONDER JUST HOW LONG", + "WE WILL DRAG THIS OUT...", + "", + "AND BELIEVE ME, WE CAN DRAG", + "IT OUT FOR QUITE A WHILE.", + "", + "SHOULD BE OVER SOON...", + "", + "ANY MOMENT NOW...", + "", + " ", + "", + "SEE YA", + "", + "END", + "PASSWORDS", + "HOLLY", + "KIMBERLY", + "LOBOCOP", + "LOBODEITY", + "LOBOLITE", + "LOBOPICK", + "LOBOSLIP", + "LOBOSNAKE", + "LOBOSPHERE", + "LOBOSWAG", + "LOBOXY", + "", + "PASSINFO", + "", + "HI SWEETIE, I LOVE YOU", + "", + "", + "SNAKE CAM ENABLED", + "FLASHES TOGGLED", + "FULL MAP", + "", + "", + "", + "", + "", + "EOF", + "", +}; + + +////////// + +enum gametokens +{ + T_INCLUDE = 0, + T_INTERFACE = 0, + T_LOADGRP = 1, + T_MODE = 1, + T_CACHESIZE = 2, + T_ALLOW = 2, + T_NOAUTOLOAD, + T_INCLUDEDEFAULT, + T_MUSIC, + T_SOUND, + T_FILE, + T_CUTSCENE, + T_ANIMSOUNDS, + T_NOFLOORPALRANGE, + T_ID, + T_MINPITCH, + T_MAXPITCH, + T_PRIORITY, + T_TYPE, + T_DISTANCE, + T_VOLUME, + T_DELAY, + T_RENAMEFILE, + T_GLOBALGAMEFLAGS, + T_ASPECT, + T_FORCEFILTER, + T_FORCENOFILTER, + T_TEXTUREFILTER, + T_NEWGAMECHOICES, + T_CHOICE, + T_NAME, + T_LOCKED, + T_HIDDEN, + T_USERCONTENT, +}; + +int exhumed_globalflags; + +static int parsedefinitions_game(scriptfile *, int); + +static void parsedefinitions_game_include(const char *fileName, scriptfile *pScript, const char *cmdtokptr, int const firstPass) +{ + scriptfile *included = scriptfile_fromfile(fileName); + + if (!included) + { + if (!Bstrcasecmp(cmdtokptr,"null") || pScript == NULL) // this is a bit overboard to prevent unused parameter warnings + { + // initprintf("Warning: Failed including %s as module\n", fn); + } +/* + else + { + initprintf("Warning: Failed including %s on line %s:%d\n", + fn, script->filename,scriptfile_getlinum(script,cmdtokptr)); + } +*/ + } + else + { + parsedefinitions_game(included, firstPass); + scriptfile_close(included); + } +} + +static int parsedefinitions_game(scriptfile *pScript, int firstPass) +{ + int token; + char *pToken; + + static const tokenlist tokens[] = + { + { "include", T_INCLUDE }, + { "#include", T_INCLUDE }, + { "includedefault", T_INCLUDEDEFAULT }, + { "#includedefault", T_INCLUDEDEFAULT }, + { "loadgrp", T_LOADGRP }, + { "cachesize", T_CACHESIZE }, + { "noautoload", T_NOAUTOLOAD }, + { "renamefile", T_RENAMEFILE }, + { "globalgameflags", T_GLOBALGAMEFLAGS }, + }; + + do + { + token = getatoken(pScript, tokens, ARRAY_SIZE(tokens)); + pToken = pScript->ltextptr; + + switch (token) + { + case T_LOADGRP: + { + char *fileName; + + if (!scriptfile_getstring(pScript,&fileName) && firstPass) + { + fileSystem.AddAdditionalFile(fileName); + } + } + break; + case T_CACHESIZE: + { + int32_t cacheSize; + + if (scriptfile_getnumber(pScript, &cacheSize) || !firstPass) + break; + } + break; + case T_INCLUDE: + { + char *fileName; + + if (!scriptfile_getstring(pScript, &fileName)) + parsedefinitions_game_include(fileName, pScript, pToken, firstPass); + + break; + } + case T_INCLUDEDEFAULT: + { + parsedefinitions_game_include(G_DefaultDefFile(), pScript, pToken, firstPass); + break; + } + case T_NOAUTOLOAD: + break; + case T_GLOBALGAMEFLAGS: scriptfile_getnumber(pScript, &exhumed_globalflags); break; + case T_EOF: return 0; + default: break; + } + } + while (1); + + return 0; +} + +int loaddefinitions_game(const char *fileName, int32_t firstPass) +{ + scriptfile *pScript = scriptfile_fromfile(fileName); + + if (pScript) + parsedefinitions_game(pScript, firstPass); + + for (auto &m : *userConfig.AddDefs) + parsedefinitions_game_include(m, NULL, "null", firstPass); + + if (pScript) + scriptfile_close(pScript); + + scriptfile_clearsymbols(); + + return 0; +} + +//////// + +const uint32_t kSpiritX = 106; +const uint32_t kSpiritY = 97; + +short cPupData[300]; +//int worktile[97 * 106] = { 0 }; +uint8_t *Worktile; +const uint32_t WorktileSize = kSpiritX * 2 * kSpiritY * 2; +int lHeadStartClock; +short *pPupData; +int lNextStateChange; +int nPixels; +int nHeadTimeStart; +short curx[97 * 106]; +short cury[97 * 106]; +int8_t destvelx[97 * 106]; +int8_t destvely[97 * 106]; +uint8_t pixelval[97 * 106]; +int8_t origy[97 * 106]; +int8_t origx[97 * 106]; +int8_t velx[97 * 106]; +int8_t vely[97 * 106]; +short nMouthTile; + +short nPupData = 0; + +short word_964E8 = 0; +short word_964EA = 0; +short word_964EC = 10; + +short nSpiritRepeatX; +short nSpiritRepeatY; +short nPixelsToShow; + +void CopyTileToBitmap(short nSrcTile, short nDestTile, int xPos, int yPos); +void DoTitle(); + +// void TestSaveLoad(); +void EraseScreen(int nVal); +void LoadStatus(); +int FindGString(const char *str); +void MySetView(int x1, int y1, int x2, int y2); +void mysetbrightness(char al); +void FadeIn(); + +char sHollyStr[40]; + +short nFontFirstChar; +short nBackgroundPic; +short nShadowPic; + +short nCreaturesLeft = 0; + +short nFreeze; +short bFullScreen; + +short nSnakeCam = -1; + +short nBestLevel; +short nSpiritSprite; + +short nLocalSpr; +short levelnew = 1; + +int nNetPlayerCount = 0; + +short nClockVal; +short fps; +short nRedTicks; +short lastlevel; +volatile short bInMove; +short nAlarmTicks; +short nButtonColor; +short nEnergyChan; + + +short bSerialPlay = kFalse; +short bModemPlay = kFalse; +int lCountDown = 0; +short nEnergyTowers = 0; + + +short nHeadStage; + +short nCfgNetPlayers = 0; +FILE *vcrfp = NULL; +short nTalkTime = 0; + +short forcelevel = -1; + +int lLocalButtons = 0; +int lLocalCodes = 0; + +short bHiRes = kFalse; +short bCoordinates = kFalse; + +int nNetTime = -1; + +short nCodeMin = 0; +short nCodeMax = 0; +short nCodeIndex = 0; + +short levelnum = -1; +//short nScreenWidth = 320; +//short nScreenHeight = 200; +int moveframes; +int flash; +int localclock; +int totalmoves; + +short nCurBodyNum = 0; + +short nBodyTotal = 0; + +short textpages; +short lastfps; + +short nMapMode = 0; +short bNoCreatures = kFalse; + +short nTotalPlayers = 1; +// TODO: Rename this (or make it static) so it doesn't conflict with library function +//short socket = 0; + +short nFirstPassword = 0; +short nFirstPassInfo = 0; +short nPasswordCount = 0; + +short screensize; + +short bSnakeCam = kFalse; +short bRecord = kFalse; +short bPlayback = kFalse; +short bPause = kFalse; +short bInDemo = kFalse; +short bSlipMode = kFalse; +short bDoFlashes = kTrue; +short bHolly = kFalse; + +short nItemTextIndex; + +short scan_char = 0; + +int nStartLevel; +int nTimeLimit; + +int bVanilla = 0; + +short wConsoleNode; // TODO - move me into network file + +ClockTicks tclocks, tclocks2; + +void DebugOut(const char *fmt, ...) +{ +#ifdef _DEBUG + va_list args; + va_start(args, fmt); + VPrintf(PRINT_HIGH, fmt, args); +#endif +} + +void ShutDown(void) +{ + StopCD(); + if (bSerialPlay) + { + if (bModemPlay) { + HangUp(); + } + UnInitSerial(); + } + + RemoveEngine(); + UnInitNet(); + //UnInitFX(); +} + +void I_Error(const char *fmt, ...) +{ + + char buf[256]; + +#ifdef __WATCOMC__ + setvmode(3); +#endif + + initprintf("bailed to dos\n"); + + va_list args; + va_start(args, fmt); + + vsprintf(buf, fmt, args); + + va_end(args); + + throw std::runtime_error(buf); +} + +void faketimerhandler() +{ + if ((totalclock < ototalclock + 1) || bInMove) + return; + ototalclock = ototalclock + 1; + + if (!((int)ototalclock&3) && moveframes < 4) + moveframes++; + + PlayerInterruptKeys(); +} + +void timerhandler() +{ + UpdateSounds(); + scan_char++; + if (scan_char == kTimerTicks) + { + scan_char = 0; + lastfps = fps; + fps = 0; + } + + if (!bInMove) { + OSD_DispatchQueued(); + } +} + +void HandleAsync() +{ + handleevents(); +} + +int MyGetStringWidth(const char *str) +{ + int nLen = strlen(str); + + int nWidth = 0; + + for (int i = 0; i < nLen; i++) + { + int nPic = seq_GetSeqPicnum(kSeqFont2, 0, str[i] - 32); + nWidth += tilesiz[nPic].x + 1; + } + + return nWidth; +} + +void UpdateScreenSize() +{ + int xsize = xdim - scale(screensize*16, xdim, 320); + int ysize = scale(ydim, xsize, xdim); + int y1 = ((ydim >> 1) - (ysize >> 1)); + + MySetView( + (xdim >> 1) - (xsize >> 1), + y1, + (xdim >> 1) - (xsize >> 1) + xsize - 1, + (y1 + ysize - 1)); + + RefreshStatus(); +} + +void ResetPassword() +{ + nCodeMin = nFirstPassword; + nCodeIndex = 0; + + nCodeMax = (nFirstPassword + nPasswordCount) - 1; +} + +void DoPassword(int nPassword) +{ + if (nNetPlayerCount) { + return; + } + + const char *str = gString[nFirstPassInfo + nPassword]; + + if (str[0] != '\0') { + StatusMessage(750, str); + } + + switch (nPassword) + { + case 0: + { + if (!nNetPlayerCount) { + bHolly = kTrue; + } + break; + } + + case 2: // LOBOCOP + { + lLocalCodes |= 0x20; + break; + } + + case 3: // LOBODEITY + { + lLocalCodes |= 0x40; + break; + } + + case 4: + { + if (bDoFlashes == kFalse) { + bDoFlashes = kTrue; + } + else { + bDoFlashes = kFalse; + } + break; + } + + case 5: + { + lLocalCodes |= 0x80; + break; + } + + case 6: + { + if (!nNetPlayerCount) + { + if (bSlipMode == kFalse) + { + bSlipMode = kTrue; + StatusMessage(300, "Slip mode %s", "ON"); + } + else { + bSlipMode = kFalse; + StatusMessage(300, "Slip mode %s", "OFF"); + } + } + break; + } + + case 7: + { + if (!nNetPlayerCount) + { + if (bSnakeCam == kFalse) { + bSnakeCam = kTrue; + } + else { + bSnakeCam = kFalse; + } + } + break; + } + + case 8: + { + GrabMap(); + bShowTowers = kTrue; + break; + } + + case 9: + { + lLocalCodes |= 0x100; // LOBOSWAG? + break; + } + + case 10: + { + if (bCoordinates == kFalse) { + bCoordinates = kTrue; + } + else { + bCoordinates = kFalse; + } + break; + } + + default: + return; + } +} + +void mysetbrightness(char nBrightness) +{ + g_visibility = 2048 - (nBrightness << 9); +} + +void CheckKeys() +{ + if (buttonMap.ButtonDown(gamefunc_Enlarge_Screen)) + { + if (screensize == 0) + { + if (!bFullScreen) + { + bFullScreen = kTrue; + UnMaskStatus(); + } + } + else + { + screensize--; + if (screensize < 0) { + screensize = 0; + } + + UpdateScreenSize(); + } + buttonMap.ClearButton(gamefunc_Enlarge_Screen); + } + + if (buttonMap.ButtonDown(gamefunc_Shrink_Screen)) + { + if (bFullScreen) + { + bFullScreen = kFalse; + } + else + { + if ((screensize + 1) < 15) + screensize++; + } + + UpdateScreenSize(); + buttonMap.ClearButton(gamefunc_Shrink_Screen); + } + + // go to 3rd person view? + if (buttonMap.ButtonDown(gamefunc_Third_Person_View)) + { + if (!nFreeze) + { + if (bCamera) { + bCamera = kFalse; + } + else { + bCamera = kTrue; + } + + if (bCamera) + GrabPalette(); + } + buttonMap.ClearButton(gamefunc_Third_Person_View); + return; + } + + if (inputState.GetKeyStatus(sc_Pause)) + { + if (!nNetPlayerCount) + { + if (bPause) + { + ototalclock = totalclock = tclocks; + bPause = kFalse; + } + else + { + bPause = kTrue; + // NoClip(); + // int nLen = MyGetStringWidth("PAUSED"); + // myprintext((320 - nLen) / 2, 100, "PAUSED", 0); + // Clip(); + // videoNextPage(); + } + inputState.ClearKeyStatus(sc_Pause); + } + return; + } + + // Handle cheat codes + if (!bInDemo && inputState.keyBufferWaiting()) + { + char ch = inputState.keyGetChar(); + + if (bHolly) + { + if (ch) + { + size_t nStringLen = strlen(sHollyStr); + + if (ch == asc_Enter) + { + char *pToken = strtok(sHollyStr, " "); + + if (nStringLen == 0) // bjd - added this check. watcom allows passing NULL to strcmp so the below checks will all fail OK on DOS but will cause a crash on Windows + { + bHolly = kFalse; + StatusMessage(1, " "); + } + else if (!strcmp(pToken, "GOTO")) + { + // move player to X, Y coordinates + int nSprite = PlayerList[0].nSprite; + + pToken = strtok(NULL, ","); + sprite[nSprite].x = atoi(pToken); + pToken = strtok(NULL, ","); + sprite[nSprite].y = atoi(pToken); + + setsprite(nSprite, &sprite[nSprite].pos); + sprite[nSprite].z = sector[sprite[nSprite].sectnum].floorz; + } + else if (!strcmp(pToken, "LEVEL")) + { + pToken = strtok(NULL, " "); + levelnew = atoi(pToken); + } + else if (!strcmp(pToken, "DOORS")) + { + for (int i = 0; i < kMaxChannels; i++) + { + // CHECKME - does this toggle? + if (sRunChannels[i].c == 0) { + runlist_ChangeChannel(i, 1); + } + else { + runlist_ChangeChannel(i, 0); + } + } + } + else if (!strcmp(pToken, "EXIT")) + { + FinishLevel(); + } + else if (!strcmp(pToken, "CREATURE")) + { + // i = nNetPlayerCount; + if (!nNetPlayerCount) + { + pToken = strtok(NULL, " "); + switch (atoi(pToken)) + { + // TODO - enums? + case 0: + BuildAnubis(-1, initx, inity, sector[initsect].floorz, initsect, inita, kFalse); + break; + case 1: + BuildSpider(-1, initx, inity, sector[initsect].floorz, initsect, inita); + break; + case 2: + BuildMummy(-1, initx, inity, sector[initsect].floorz, initsect, inita); + break; + case 3: + BuildFish(-1, initx, inity, initz + eyelevel[nLocalPlayer], initsect, inita); + break; + case 4: + BuildLion(-1, initx, inity, sector[initsect].floorz, initsect, inita); + break; + case 5: + BuildLava(-1, initx, inity, sector[initsect].floorz, initsect, inita, nNetPlayerCount); + break; + case 6: + BuildRex(-1, initx, inity, sector[initsect].floorz, initsect, inita, nNetPlayerCount); + break; + case 7: + BuildSet(-1, initx, inity, sector[initsect].floorz, initsect, inita, nNetPlayerCount); + break; + case 8: + BuildQueen(-1, initx, inity, sector[initsect].floorz, initsect, inita, nNetPlayerCount); + break; + case 9: + BuildRoach(0, -1, initx, inity, sector[initsect].floorz, initsect, inita); + break; + case 10: + BuildRoach(1, -1, initx, inity, sector[initsect].floorz, initsect, inita); + break; + case 11: + BuildWasp(-1, initx, inity, sector[initsect].floorz - 25600, initsect, inita); + break; + case 12: + BuildScorp(-1, initx, inity, sector[initsect].floorz, initsect, inita, nNetPlayerCount); + break; + case 13: + BuildRat(-1, initx, inity, sector[initsect].floorz, initsect, inita); + break; + default: + break; + } + } + } + else + { + if (nStringLen == 0) + { + bHolly = kFalse; + StatusMessage(1, " "); + } + else + { + for (int i = 0; i < nPasswordCount; ++i) + { + if (!strcmp(sHollyStr, gString[i + nFirstPassword])) + { + DoPassword(i); + break; + } + } + } + } + sHollyStr[0] = '\0'; + } + else if (ch == asc_BackSpace) + { + if (nStringLen != 0) { + sHollyStr[nStringLen - 1] = '\0'; + } + } + else if (nStringLen < (sizeof(sHollyStr) - 1)) // do we have room to add a char and null terminator? + { + sHollyStr[nStringLen] = toupper(ch); + sHollyStr[nStringLen + 1] = '\0'; + } + } + else + { + inputState.keyGetChar(); //??? + } + } + + if (isalpha(ch)) + { + ch = toupper(ch); + + int ecx = nCodeMin; + + int ebx = nCodeMin; + int edx = nCodeMin - 1; + + while (ebx <= nCodeMax) + { + if (ch == gString[ecx][nCodeIndex]) + { + nCodeMin = ebx; + nCodeIndex++; + + if (gString[ecx][nCodeIndex] == 0) + { + ebx -= nFirstPassword; + + DoPassword(ebx); + ResetPassword(); + } + + break; + } + else if (gString[ecx][nCodeIndex] < ch) + { + nCodeMin = ebx + 1; + } + else if (gString[ecx][nCodeIndex] > ch) + { + nCodeMax = edx; + } + + ecx++; + edx++; + ebx++; + } + + if (nCodeMin > nCodeMax) { + ResetPassword(); + } + } + } +} + +void DoCredits() +{ + NoClip(); + + playCDtrack(19, false); + + int var_20 = 0; + + if (videoGetRenderMode() == REND_CLASSIC) + FadeOut(0); + + int nCreditsIndex = FindGString("CREDITS"); + + while (strcmp(gString[nCreditsIndex], "END") != 0) + { + EraseScreen(overscanindex); + + int nStart = nCreditsIndex; + + // skip blanks + while (strlen(gString[nCreditsIndex]) != 0) { + nCreditsIndex++; + } + + int y = 100 - ((10 * (nCreditsIndex - nStart - 1)) / 2); + + for (int i = nStart; i < nCreditsIndex; i++) + { + int nWidth = MyGetStringWidth(gString[i]); + myprintext((320 - nWidth) / 2, y, gString[i], 0); + y += 10; + } + + videoNextPage(); + + nCreditsIndex++; + + if (videoGetRenderMode() == REND_CLASSIC) + FadeIn(); + + int nDuration = (int)totalclock + 600; + + while ((int)totalclock <= nDuration) + { + handleevents(); + if(inputState.GetKeyStatus(sc_F12)) + { + var_20++; + + inputState.ClearKeyStatus(sc_F12); + + if (var_20 > 5) { + return; + } + } + } + + if (videoGetRenderMode() == REND_CLASSIC) + FadeOut(0); + } + + while (CDplaying()) + { + inputState.keyGetChar(); + } + + Clip(); +} + +void FinishLevel() +{ + if (levelnum > nBestLevel) { + nBestLevel = levelnum - 1; + } + + levelnew = levelnum + 1; + + StopAllSounds(); + + bCamera = kFalse; + nMapMode = 0; + + if (levelnum != kMap20) + { + EraseScreen(4); + PlayLocalSound(StaticSound[59], 0, true); + videoNextPage(); + WaitTicks(12); + WaitVBL(); + RefreshBackground(); + RefreshStatus(); + DrawView(65536); + videoNextPage(); + } + + FadeOut(1); + EraseScreen(overscanindex); + + if (levelnum == 0) + { + nPlayerLives[0] = 0; + levelnew = 100; + } + else + { + DoAfterCinemaScene(levelnum); + if (levelnum == kMap20) + { + DoCredits(); + nPlayerLives[0] = 0; + } + } +} + +EDUKE32_STATIC_ASSERT(sizeof(demo_header) == 75); +EDUKE32_STATIC_ASSERT(sizeof(demo_input) == 36); + + +void WritePlaybackInputs() +{ + fwrite(&moveframes, sizeof(moveframes), 1, vcrfp); + fwrite(&sPlayerInput[nLocalPlayer], sizeof(PlayerInput), 1, vcrfp); +} + +uint8_t ReadPlaybackInputs() +{ + demo_input input; + if (fread(&input, 1, sizeof(input), vcrfp)) + { + moveframes = input.moveframes; + sPlayerInput[nLocalPlayer].xVel = input.xVel; + sPlayerInput[nLocalPlayer].yVel = input.yVel; + sPlayerInput[nLocalPlayer].nAngle = fix16_from_int(input.nAngle<<2); + sPlayerInput[nLocalPlayer].buttons = input.buttons; + sPlayerInput[nLocalPlayer].nTarget = input.nTarget; + sPlayerInput[nLocalPlayer].horizon = fix16_from_int(input.horizon); + sPlayerInput[nLocalPlayer].nItem = input.nItem; + sPlayerInput[nLocalPlayer].h = input.h; + sPlayerInput[nLocalPlayer].i = input.i; + + besttarget = sPlayerInput[nLocalPlayer].nTarget; + Ra[nLocalPlayer].nTarget = besttarget; + return kTrue; + } + else + { + fclose(vcrfp); + vcrfp = NULL; + bPlayback = kFalse; + return kFalse; + } +} + +void SetHiRes() +{ + //nScreenWidth = 640; + //nScreenHeight = 400; + bHiRes = kTrue; +} + +void DoClockBeep() +{ + for (int i = headspritestat[407]; i != -1; i = nextspritestat[i]) { + PlayFX2(StaticSound[kSound74], i); + } +} + +void DoRedAlert(int nVal) +{ + if (nVal) + { + nAlarmTicks = 69; + nRedTicks = 30; + } + + for (int i = headspritestat[405]; i != -1; i = nextspritestat[i]) + { + if (nVal) + { + PlayFXAtXYZ(StaticSound[kSoundAlarm], sprite[i].x, sprite[i].y, sprite[i].z, sprite[i].sectnum); + AddFlash(sprite[i].sectnum, sprite[i].x, sprite[i].y, sprite[i].z, 192); + } + } +} + +void LockEnergyTiles() +{ + // old loadtilelockmode = 1; + tileLoad(kTile3603); + tileLoad(kEnergy1); + tileLoad(kEnergy2); + // old loadtilelockmode = 0; +} + +void DrawClock() +{ + int ebp = 49; + + auto pixels = TileFiles.tileMakeWritable(kTile3603); + + memset(pixels, -1, 4096); + + if (lCountDown / 30 != nClockVal) + { + nClockVal = lCountDown / 30; + DoClockBeep(); + } + + int nVal = nClockVal; + + while (nVal) + { + int v2 = nVal & 0xF; + int yPos = 32 - tilesiz[v2 + kTile3606].y / 2; + + CopyTileToBitmap(v2 + kTile3606, kTile3603, ebp - tilesiz[v2 + kTile3606].x / 2, yPos); + + ebp -= 15; + + nVal /= 16; + } + + DoEnergyTile(); +} + +void M32RunScript(const char* s) { UNREFERENCED_PARAMETER(s); } +void app_crashhandler(void) +{ + ShutDown(); +} + +void G_Polymer_UnInit(void) { } + +static inline int32_t calc_smoothratio(ClockTicks totalclk, ClockTicks ototalclk) +{ + // if (!((ud.show_help == 0 && (!g_netServer && ud.multimode < 2) && ((g_player[myconnectindex].ps->gm & MODE_MENU) == 0)) || + // (g_netServer || ud.multimode > 1) || + // ud.recstat == 2) || + // ud.pause_on) + // { + // return 65536; + // } + if (bRecord || bPlayback || nFreeze != 0 || bCamera || bPause) + return 65536; + int32_t rfreq = (refreshfreq != -1 ? refreshfreq : 60); + uint64_t elapsedFrames = tabledivide64(((uint64_t) (totalclk - ototalclk).toScale16()) * rfreq, 65536*120); +#if 0 + //POGO: additional debug info for testing purposes + OSD_Printf("Elapsed frames: %" PRIu64 ", smoothratio: %" PRIu64 "\n", elapsedFrames, tabledivide64(65536*elapsedFrames*30, rfreq)); +#endif + return clamp(tabledivide64(65536*elapsedFrames*30, rfreq), 0, 65536); +} + +#define COLOR_RED redcol +#define COLOR_WHITE whitecol + +#define LOW_FPS ((videoGetRenderMode() == REND_CLASSIC) ? 35 : 50) +#define SLOW_FRAME_TIME 20 + +#if defined GEKKO +# define FPS_YOFFSET 16 +#else +# define FPS_YOFFSET 0 +#endif + +#define FPS_COLOR(x) ((x) ? COLOR_RED : COLOR_WHITE) + +FString GameInterface::statFPS() +{ + FString out; + static int32_t frameCount; + static double cumulativeFrameDelay; + static double lastFrameTime; + static float lastFPS; // , minFPS = std::numeric_limits::max(), maxFPS; + //static double minGameUpdate = std::numeric_limits::max(), maxGameUpdate; + + double frameTime = timerGetHiTicks(); + double frameDelay = frameTime - lastFrameTime; + cumulativeFrameDelay += frameDelay; + + if (frameDelay >= 0) + { + out.Format("%.1f ms, %5.1f fps", frameDelay, lastFPS); + + if (cumulativeFrameDelay >= 1000.0) + { + lastFPS = 1000.f * frameCount / cumulativeFrameDelay; + // g_frameRate = Blrintf(lastFPS); + frameCount = 0; + cumulativeFrameDelay = 0.0; + } + frameCount++; + } + lastFrameTime = frameTime; + return out; +} + +static void GameDisplay(void) +{ + // End Section B + + SetView1(); + faketimerhandler(); + + if (levelnum == kMap20) + { + LockEnergyTiles(); + DoEnergyTile(); + DrawClock(); + } + + auto smoothRatio = calc_smoothratio(totalclock, tclocks); + + DrawView(smoothRatio); + + if (bPause) + { + int nLen = MyGetStringWidth("PAUSED"); + myprintext((320 - nLen) / 2, 100, "PAUSED", 0); + } + + videoNextPage(); +} + +static void GameMove(void) +{ + FixPalette(); + + if (levelnum == kMap20) + { + if (lCountDown <= 0) + { + for (int i = 0; i < nTotalPlayers; i++) { + nPlayerLives[i] = 0; + } + + DoFailedFinalScene(); + levelnew = 100; + + return; + } + // Pink section + lCountDown--; + DrawClock(); + + if (nRedTicks) + { + nRedTicks--; + + if (nRedTicks <= 0) { + DoRedAlert(0); + } + } + + nAlarmTicks--; + nButtonColor--; + + if (nAlarmTicks <= 0) { + DoRedAlert(1); + } + } + + // YELLOW SECTION + MoveThings(); + + obobangle = bobangle; + + if (totalvel[nLocalPlayer] == 0) + { + bobangle = 0; + } + else + { + bobangle += 56; + bobangle &= kAngleMask; + } + + UpdateCreepySounds(); + + // loc_120E9: + totalmoves++; + moveframes--; +} + +#if defined(_WIN32) && defined(DEBUGGINGAIDS) +// See FILENAME_CASE_CHECK in cache1d.c +static int32_t check_filename_casing(void) +{ + return 1; +} +#endif + +int32_t r_maxfpsoffset = 0; + +void PatchDemoStrings() +{ + if (!ISDEMOVER) + return; + + if (EXHUMED) { + gString[60] = "PICK UP A COPY OF EXHUMED"; + } + else { + gString[60] = "PICK UP A COPY OF POWERSLAVE"; + } + + gString[61] = "TODAY TO CONTINUE THE ADVENTURE!"; + gString[62] = "MORE LEVELS, NASTIER CREATURES"; + gString[63] = "AND THE EVIL DOINGS OF THE"; + gString[64] = "KILMAAT AWAIT YOU IN THE FULL"; + gString[65] = "VERSION OF THE GAME."; + gString[66] = "TWENTY LEVELS, PLUS 12 NETWORK"; + gString[67] = "PLAY LEVELS CAN BE YOURS!"; + gString[68] = "END"; +} + +void ExitGame() +{ + if (bRecord) { + fclose(vcrfp); + } + + if (bSerialPlay) + { + if (nNetPlayerCount != 0) { + bSendBye = kTrue; + UpdateSerialInputs(); + } + } + else + { + if (nNetPlayerCount != 0) { + SendGoodbye(); + } + } + + ShutDown(); + throw ExitEvent(0); +} + +static int32_t nonsharedtimer; + +void CheckCommandLine(int argc, char const* const* argv, int &doTitle) +{ + // Check for any command line arguments + for (int i = 1; i < argc; i++) + { + const char* pChar = argv[i]; + + if (*pChar == '/') + { + pChar++; + //strlwr(pChar); + + if (Bstrcasecmp(pChar, "nocreatures") == 0) { + bNoCreatures = kTrue; + } + else if (Bstrcasecmp(pChar, "record") == 0) + { + if (!bPlayback) + { + vcrfp = fopen("data.vcr", "wb+"); + if (vcrfp != NULL) { + bRecord = kTrue; + } + else { + DebugOut("Can't open data file for recording\n"); + } + } + } + else if (Bstrcasecmp(pChar, "playback") == 0) + { + if (!bRecord) + { + vcrfp = fopen("data.vcr", "rb"); + if (vcrfp != NULL) { + bPlayback = kTrue; + doTitle = kFalse; + } + else { + DebugOut("Can't open data file 'data.vcr' for reading\n"); + } + } + } + else if (Bstrncasecmp(pChar, "null", 4) == 0) + { + pChar += 4; + + bSerialPlay = kTrue; + nNetPlayerCount = 1; + nTotalPlayers = 2; + + doTitle = kFalse; + + char ch = *pChar; + + // bjd - unused/unfished code in original .exe? + switch (ch - 1) + { + default: + break; + + case 0: + case 1: + case 2: + case 3: + break; + } + + if (forcelevel < 0) + { + forcelevel = levelnew; + } + } + else if (Bstrcasecmp(pChar, "modem") == 0) + { + bModemPlay = kTrue; + bSerialPlay = kTrue; + nNetPlayerCount = 1; + nTotalPlayers = 2; + + doTitle = kFalse; + + if (forcelevel < 0) { + forcelevel = levelnew; + } + } + else if (Bstrcasecmp(pChar, "network") == 0) + { + nNetPlayerCount = -1; + forcelevel = levelnew; + bModemPlay = kFalse; + bSerialPlay = kFalse; + + doTitle = kFalse; + } + else + { + char c = tolower(*pChar); + + switch (c) + { + case 'h': + SetHiRes(); + break; +#if 0 + case 's': + socket = atoi(pChar + 1); + break; +#endif + case 't': + nNetTime = atoi(pChar + 1); + if (nNetTime < 0) { + nNetTime = 0; + } + else { + nNetTime = nNetTime * 1800; + } + break; + case 'c': + { + break; + } + default: + { + if (isdigit(c)) + { + levelnew = atoi(pChar); + forcelevel = levelnew; + + doTitle = kFalse; + + initprintf("Jumping to level %d...\n", levelnew); + } + break; + } + } + } + } + } +} + + +int GameInterface::app_main() +{ + C_DoCommand("stat sounddebug"); + int i; + //int esi = 1; + //int edi = esi; + int doTitle = kTrue; // REVERT kTrue; + int stopTitle = kFalse; + levelnew = 1; + + // Create the global level table. Parts of the engine need it, even though the game itself does not. + for (int i = 0; i <= 32; i++) + { + auto mi = &mapList[i]; + mi->fileName.Format("LEV%d.MAP", i); + mi->labelName.Format("LEV%d", i); + mi->name.Format("$TXT_EX_MAP%02d", i); + + int nTrack = i; + if (nTrack != 0) nTrack--; + mi->cdSongId = (nTrack % 8) + 11; + } + + // REVERT - change back to kTrue +// short bDoTitle = kFalse; + + wConsoleNode = 0; + + int nMenu = 0; // TEMP + + + if (nNetPlayerCount && forcelevel == -1) { + forcelevel = 1; + } + +#if defined(RENDERTYPEWIN) && defined(USE_OPENGL) + if (forcegl) initprintf("GL driver blacklist disabled.\n"); +#endif + + + PatchDemoStrings(); + // loc_115F5: + nItemTextIndex = FindGString("ITEMS"); + nFirstPassword = FindGString("PASSWORDS"); + nFirstPassInfo = FindGString("PASSINFO"); + + // count the number of passwords available + for (nPasswordCount = 0; strlen(gString[nFirstPassword+nPasswordCount]) != 0; nPasswordCount++) + { + } + + ResetPassword(); + + // GetCurPal(NULL); + + initprintf("Initializing OSD...\n"); + + registerosdcommands(); + + if (nNetPlayerCount == -1) + { + nNetPlayerCount = nCfgNetPlayers - 1; + nTotalPlayers += nNetPlayerCount; + } + + // loc_116A5: + +#if 0 + if (nNetPlayerCount) + { + InitInput(); + forcelevel = nStartLevel; + nNetTime = 1800 * nTimeLimit; + + if (nNetTime == 0) { + nNetTime = -1; + } + + int nWaitTicks = 0; + + if (!bSerialPlay) + { + if (InitNet(socket, nTotalPlayers)) + { + DebugOut("Found network players!\n"); + nWaitTicks = 30; + } + else + { + AbortNetworkPlay(); + DebugOut("Network play aborted\n"); + initprintf("Network play aborted\n"); + nWaitTicks = 60; + } + + WaitTicks(nWaitTicks); + } + } +#endif + + // temp - moving InstallEngine(); before FadeOut as we use nextpage() in FadeOut + InstallEngine(); + + const char *defsfile = G_DefFile(); + uint32_t stime = timerGetTicks(); + if (!loaddefinitionsfile(defsfile)) + { + uint32_t etime = timerGetTicks(); + initprintf("Definitions file \"%s\" loaded in %d ms.\n", defsfile, etime-stime); + } + loaddefinitions_game(defsfile, FALSE); + + + if (enginePostInit()) + ShutDown(); + + // loc_11745: +// FadeOut(0); +// InstallEngine(); + //KB_Startup(); + InitView(); + myloadconfig(); + InitFX(); + seq_LoadSequences(); + InitStatus(); + InitTimer(); + + for (i = 0; i < kMaxPlayers; i++) { + nPlayerLives[i] = kDefaultLives; + } + + nBestLevel = 0; + + UpdateScreenSize(); + + EraseScreen(overscanindex); + ResetEngine(); + EraseScreen(overscanindex); + + ResetView(); + GrabPalette(); + paletteSetColorTable(curbasepal, basepaltable[BASEPAL]); + + if (bSerialPlay && !InitSerial()) { + I_Error("Unable to connect"); + } + + if (doTitle) + { + while (!stopTitle) + { + DoTitle(); + stopTitle = kTrue; + } + } + // loc_11811: + if (forcelevel > -1) + { + levelnew = forcelevel; + goto STARTGAME1; + } + // no forcelevel specified... + if (bPlayback) + { + goto STARTGAME1; + } +MENU: + SavePosition = -1; + nMenu = menu_Menu(0); + switch (nMenu) + { + case -1: + goto MENU; + case 0: + goto EXITGAME; + case 3: + forcelevel = 0; + goto STARTGAME2; + case 6: + goto GAMELOOP; + case 9: + vcrfp = fopen("demo.vcr", "rb"); + if (vcrfp == NULL) { + goto MENU; + } + + InitRandom(); + bInDemo = kTrue; + bPlayback = kTrue; + + inputState.keyFlushChars(); + inputState.ClearAllKeyStatus(); + break; + } +STARTGAME1: + levelnew = 1; + levelnum = 1; + if (!nNetPlayerCount) { + FadeOut(0); + } +STARTGAME2: + + bCamera = kFalse; + ClearCinemaSeen(); + PlayerCount = 0; + lastlevel = -1; + + for (i = 0; i < nTotalPlayers; i++) + { + int nPlayer = GrabPlayer(); + if (nPlayer < 0) { + I_Error("Can't create local player\n"); + } + + InitPlayerInventory(nPlayer); + + if (i == wConsoleNode) { + PlayerList[nPlayer].someNetVal = -3; + } + else { + PlayerList[nPlayer].someNetVal = -4; + } + } + + nNetMoves = 0; + + if (bPlayback) + { + menu_GameLoad2(vcrfp, true); + levelnew = GameStats.nMap; + levelnum = GameStats.nMap; + forcelevel = GameStats.nMap; + } + + if (forcelevel > -1) + { + // YELLOW SECTION + levelnew = forcelevel; + UpdateInputs(); + forcelevel = -1; + + if (bRecord && !bInDemo) { + menu_GameSave2(vcrfp); + } + goto LOOP3; + } + + // PINK SECTION + UpdateInputs(); + nNetMoves = 1; + + if (nMenu == 2) + { + levelnew = 1; + levelnum = 1; + levelnew = menu_GameLoad(SavePosition); + lastlevel = -1; + } + + if (bRecord && !bInDemo) { + menu_GameSave2(vcrfp); + } + + nBestLevel = levelnew - 1; +LOOP1: + + if (nPlayerLives[nLocalPlayer] <= 0) { + goto MENU; + } + if (levelnew > 99) { + goto EXITGAME; + } + if (!bInDemo && levelnew > nBestLevel && levelnew != 0 && levelnew <= kMap20 && SavePosition > -1) { + menu_GameSave(SavePosition); + } +LOOP2: + if (!nNetPlayerCount && !bPlayback && levelnew > 0 && levelnew <= kMap20) { + levelnew = showmap(levelnum, levelnew, nBestLevel); + } + + if (levelnew > nBestLevel) { + nBestLevel = levelnew; + } +LOOP3: + while (levelnew != -1) + { + // BLUE + if (CDplaying()) { + fadecdaudio(); + } + + if (levelnew == kMap20) + { + lCountDown = 81000; + nAlarmTicks = 30; + nRedTicks = 0; + nClockVal = 0; + nEnergyTowers = 0; + } + + if (!LoadLevel(levelnew)) { + // TODO "Can't load level %d...\n", nMap; + goto EXITGAME; + } + levelnew = -1; + } + if (nNetPlayerCount == 0 && lastlevel == levelnum) + { + RestoreSavePoint(nLocalPlayer, &initx, &inity, &initz, &initsect, &inita); + } + + lastlevel = levelnum; + + for (i = 0; i < nTotalPlayers; i++) + { + SetSavePoint(i, initx, inity, initz, initsect, inita); + RestartPlayer(i); + InitPlayerKeys(i); + } + + UpdateScreenSize(); + fps = 0; + lastfps = 0; + InitStatus(); + ResetView(); + ResetEngine(); + totalmoves = 0; + GrabPalette(); + ResetMoveFifo(); + moveframes = 0; + bInMove = kFalse; + tclocks = totalclock; + nPlayerDAng = 0; + lPlayerXVel = 0; + lPlayerYVel = 0; + movefifopos = movefifoend; + + RefreshStatus(); + + if (bSerialPlay) { + ClearSerialInbuf(); + } + + //int edi = totalclock; + tclocks2 = totalclock; + // Game Loop +GAMELOOP: + while (1) + { + if (levelnew >= 0) + { + goto LOOP1; + } + + HandleAsync(); + OSD_DispatchQueued(); + + // Section B + if (!CDplaying() && !nFreeze && !nNetPlayerCount) + { + int nTrack = levelnum; + if (nTrack != 0) { + nTrack--; + } + + playCDtrack((nTrack % 8) + 11, true); + } + +// TODO CONTROL_GetButtonInput(); + CheckKeys(); + + if (bRecord || bPlayback) + { + bInMove = kTrue; + + moveframes = ((int)totalclock - (int)tclocks2) / 4; + + if (moveframes > 4) + moveframes = 4; + + if (moveframes != 0) + tclocks2 = totalclock; + + if (bPlayback) + { + // YELLOW + if (((bInDemo && inputState.keyBufferWaiting()) || !ReadPlaybackInputs()) && inputState.keyGetChar()) + { + inputState.keyFlushChars(); + inputState.ClearAllKeyStatus(); + + bPlayback = kFalse; + bInDemo = kFalse; + + if (vcrfp) { + fclose(vcrfp); + } + + goto MENU; + } + } + else if (bRecord || moveframes) + { + GetLocalInput(); + + sPlayerInput[nLocalPlayer].xVel = lPlayerXVel; + sPlayerInput[nLocalPlayer].yVel = lPlayerYVel; + sPlayerInput[nLocalPlayer].buttons = lLocalButtons | lLocalCodes; + sPlayerInput[nLocalPlayer].nAngle = nPlayerDAng; + sPlayerInput[nLocalPlayer].nTarget = besttarget; + + Ra[nLocalPlayer].nTarget = besttarget; + + lLocalCodes = 0; + nPlayerDAng = 0; + + sPlayerInput[nLocalPlayer].horizon = nVertPan[nLocalPlayer]; + } + + // loc_11F72: + if (bRecord && !bInDemo) { + WritePlaybackInputs(); + } + + if (nNetPlayerCount) + { + if (moveframes) + { + UpdateInputs(); + moveframes = nNetMoveFrames; + } + } + else + { + // loc_11FBC: + while (bPause) + { + ClearAllKeys(); + if (WaitAnyKey(-1) != sc_Pause) + { + bPause = kFalse; + } + } + } + + // loc_11FEE: + tclocks += moveframes * 4; + while (moveframes && levelnew < 0) + { + GameMove(); + // if (nNetTime > 0) + // { + // nNetTime--; + // + // if (!nNetTime) { + // nFreeze = 3; + // } + // } + // else if (nNetTime == 0) + // { + // if (buttonMap.ButtonDown(gamefunc_Open)) + // { + // buttonMap.ClearButton(gamefunc_Open); + // goto MENU2; + // } + // } + } + + bInMove = kFalse; + + // END YELLOW SECTION + + // loc_12149: + if (bInDemo || bPlayback) + { + while (tclocks > totalclock) { HandleAsync(); } + tclocks = totalclock; + } + + if (G_FPSLimit()) + { + GameDisplay(); + } + } + else + { + static bool frameJustDrawn; + bInMove = kTrue; + if (!bPause && totalclock >= tclocks + 4) + { + do + { + if (!frameJustDrawn) + break; + + frameJustDrawn = false; + + GetLocalInput(); + + sPlayerInput[nLocalPlayer].xVel = lPlayerXVel; + sPlayerInput[nLocalPlayer].yVel = lPlayerYVel; + sPlayerInput[nLocalPlayer].buttons = lLocalButtons | lLocalCodes; + sPlayerInput[nLocalPlayer].nAngle = nPlayerDAng; + sPlayerInput[nLocalPlayer].nTarget = besttarget; + + Ra[nLocalPlayer].nTarget = besttarget; + + lLocalCodes = 0; + nPlayerDAng = 0; + + sPlayerInput[nLocalPlayer].horizon = nVertPan[nLocalPlayer]; + + do + { + // timerUpdate(); + tclocks += 4; + GameMove(); + // timerUpdate(); + } while (levelnew < 0 && totalclock >= tclocks + 4); + } while (0); + } + bInMove = kFalse; + + faketimerhandler(); + + if (G_FPSLimit()) + { + GameDisplay(); + frameJustDrawn = true; + } + } + if (!bInDemo) + { + if (inputState.GetKeyStatus(sc_Escape)) + { + inputState.ClearKeyStatus(sc_Escape); +// MENU2: + bInMove = kTrue; + nMenu = menu_Menu(1); + + switch (nMenu) + { + case 0: + goto EXITGAME; + + case 1: + goto STARTGAME1; + + case 2: + levelnum = levelnew = menu_GameLoad(SavePosition); + lastlevel = -1; + nBestLevel = levelnew - 1; + goto LOOP2; + + case 3: // When selecting 'Training' from ingame menu when already playing a level + if (levelnum == 0 || !Query(2, 4, "Quit current game?", "Y/N", 'Y', 13, 'N', 27)) + { + levelnew = 0; + levelnum = 0; + + goto STARTGAME2; + } + case 6: + goto GAMELOOP; + } + + totalclock = ototalclock = tclocks; + bInMove = kFalse; + RefreshStatus(); + } + else if (buttonMap.ButtonDown(gamefunc_Map)) // e.g. TAB (to show 2D map) + { + buttonMap.ClearButton(gamefunc_Map); + + if (!nFreeze) { + nMapMode = (nMapMode+1)%3; + } + } + + if (nMapMode != 0) + { + int const timerOffset = ((int) totalclock - nonsharedtimer); + nonsharedtimer += timerOffset; + + if (buttonMap.ButtonDown(gamefunc_Zoom_In)) + lMapZoom += mulscale6(timerOffset, max(lMapZoom, 256)); + + if (buttonMap.ButtonDown(gamefunc_Zoom_Out)) + lMapZoom -= mulscale6(timerOffset, max(lMapZoom, 256)); + + lMapZoom = clamp(lMapZoom, 48, 2048); + } + + if (PlayerList[nLocalPlayer].nHealth > 0) + { + if (buttonMap.ButtonDown(gamefunc_Inventory_Left)) + { + SetPrevItem(nLocalPlayer); + buttonMap.ClearButton(gamefunc_Inventory_Left); + } + if (buttonMap.ButtonDown(gamefunc_Inventory_Right)) + { + SetNextItem(nLocalPlayer); + buttonMap.ClearButton(gamefunc_Inventory_Right); + } + if (buttonMap.ButtonDown(gamefunc_Inventory)) + { + UseCurItem(nLocalPlayer); + buttonMap.ClearButton(gamefunc_Inventory); + } + } + else { + SetAirFrame(); + } + } + if (record_mode == 3 && movefifopos == movefifoend) { + levelnew = 0; + } + fps++; + } +EXITGAME: + + ExitGame(); + return 0; +} + +void mychangespritesect(int nSprite, int nSector) +{ + DoKenTest(); + changespritesect(nSprite, nSector); + DoKenTest(); +} + +void mydeletesprite(int nSprite) +{ + if (nSprite < 0 || nSprite > kMaxSprites) { + I_Error("bad sprite value %d handed to mydeletesprite", nSprite); + } + + deletesprite(nSprite); + + if (nSprite == besttarget) { + besttarget = -1; + } +} + + +void KeyFn1() +{ + menu_DoPlasma(); + overwritesprite(160, 100, kSkullHead, 0, 3, kPalNormal); + overwritesprite(161, 130, kSkullJaw, 0, 3, kPalNormal); + videoNextPage(); +} + +void DoGameOverScene() +{ + FadeOut(0); + ClearAllKeys(); + + if (LoadCinemaPalette(16) < 0) { + return; + } + + SetOverscan(ANIMPAL); + NoClip(); + overwritesprite(0, 0, kTile3591, 0, 2, kPalNormal); + Clip(); + videoNextPage(); + CinemaFadeIn(); + PlayGameOverSound(); + WaitAnyKey(3); + FadeOut(0); + SetOverscan(BASEPAL); +} + +// TODO - missing some values? +short word_10010[] = {6, 25, 43, 50, 68, 78, 101, 111, 134, 158, 173, 230, 6000}; + +void DoTitle() +{ + short theArray[13]; + memcpy(theArray, word_10010, sizeof(word_10010)); + + videoSetViewableArea(0, 0, xdim - 1, ydim - 1); + + if (videoGetRenderMode() == REND_CLASSIC) + BlackOut(); + + overwritesprite(0, 0, EXHUMED ? kTileBMGLogo : kTilePIELogo, 0, 2, kPalNormal); + videoNextPage(); + + if (videoGetRenderMode() == REND_CLASSIC) + FadeIn(); + + ClearAllKeys(); + + WaitAnyKey(2); + + if (videoGetRenderMode() == REND_CLASSIC) + FadeOut(0); + + SetOverscan(BASEPAL); + + int nScreenTile = seq_GetSeqPicnum(kSeqScreens, 0, 0); + + overwritesprite(0, 0, nScreenTile, 0, 2, kPalNormal); + videoNextPage(); + + if (videoGetRenderMode() == REND_CLASSIC) + FadeIn(); + PlayLogoSound(); + + WaitAnyKey(2); + + if (videoGetRenderMode() == REND_CLASSIC) + FadeOut(0); + ClearAllKeys(); + + PlayMovie("book.mov"); + + if (videoGetRenderMode() == REND_CLASSIC) + FadeOut(0); + + SetOverscan(BASEPAL); + GrabPalette(); + + PlayLocalSound(StaticSound[59], 0, true); + + EraseScreen(4); + + playCDtrack(19, true); + + videoNextPage(); + FadeIn(); + WaitVBL(); + + int String_Copyright = FindGString("COPYRIGHT"); + + const char *a = gString[String_Copyright]; + const char *b = gString[String_Copyright + 1]; + + menu_DoPlasma(); + + int nTile = kSkullHead; + + overwritesprite(160, 100, kSkullHead, 0, 3, kPalNormal); + overwritesprite(161, 130, kSkullJaw, 0, 3, kPalNormal); + videoNextPage(); + + WaitNoKey(2, KeyFn1); + + if (time(0) & 0xF) { + PlayGameOverSound(); + } + else { + PlayLocalSound(StaticSound[61], 0); + } + + int nStartTime = (int)totalclock; + int nCount = 0; + int var_18 = (int)totalclock; + int var_4 = 0; + + int esi = 130; + + var_18 += theArray[0]; + + inputState.ClearAllKeyStatus(); + while (LocalSoundPlaying()) + { + HandleAsync(); + if (inputState.CheckAllInput()) break; + + menu_DoPlasma(); + overwritesprite(160, 100, nTile, 0, 3, kPalNormal); + + int nStringWidth = MyGetStringWidth(a); + + int y = 200 - 24; + myprintext((320 / 2 - nStringWidth / 2), y, a, 0); + + nStringWidth = MyGetStringWidth(b); + + y = 200 - 16; + myprintext((320 / 2 - nStringWidth / 2), y, b, 0); + + if ((int)totalclock > var_18) + { + nCount++; + + if (nCount > 12) break; + var_18 = nStartTime + theArray[nCount]; + + var_4 = var_4 == 0; + } + + short nTile = kSkullJaw; + + if (var_4) + { + if (esi >= 135) { + nTile = kTile3583; + } + else { + esi += 5; + } + } + else if (esi <= 130) + { + esi = 130; + } + else + { + esi -= 2; + } + + y = 0; + + if (nTile == kTile3583) + { + y = 131; + } + else + { + y = esi; + + if (y > 135) { + y = 135; + } + } + + overwritesprite(161, y, nTile, 0, 3, kPalNormal); + videoNextPage(); + } + + WaitNoKey(1, KeyFn1); + + videoSetViewableArea(nViewLeft, nViewTop, nViewRight, nViewBottom); +} + +void CopyTileToBitmap(short nSrcTile, short nDestTile, int xPos, int yPos) +{ + int nOffs = tilesiz[nDestTile].y * xPos; + + auto pixels = TileFiles.tileMakeWritable(nDestTile); + uint8_t *pDest = pixels + nOffs + yPos; + uint8_t *pDestB = pDest; + + tileLoad(nSrcTile); + + int destYSize = tilesiz[nDestTile].y; + int srcYSize = tilesiz[nSrcTile].y; + + const uint8_t *pSrc = tilePtr(nSrcTile); + + for (int x = 0; x < tilesiz[nSrcTile].x; x++) + { + pDest += destYSize; + + for (int y = 0; y < srcYSize; y++) + { + uint8_t val = *pSrc; + if (val != 0xFF) { + *pDestB = val; + } + + pDestB++; + pSrc++; + } + + // reset pDestB + pDestB = pDest; + } + + tileInvalidate(nDestTile, -1, -1); +} + +int CopyCharToBitmap(char nChar, int nTile, int xPos, int yPos) +{ + if (nChar == ' ') { + return 4; + } + + nChar = toupper(nChar); + int nFontTile = seq_GetSeqPicnum(kSeqFont2, 0, nChar - 32) + 102; + CopyTileToBitmap(nFontTile, nTile, xPos, yPos); + + return tilesiz[nFontTile].x + 1; +} + +// Note: strings passed should be uppercase +int myprintext(int x, int y, const char *str, int shade) +{ + if (y < -15 || y >= 200) + return x; + + const char *c = str; + + while (*c != '\0') + { + int nTile = seq_GetSeqPicnum(kSeqFont2, 0, (*c) - 32); + overwritesprite(x, y, nTile, shade, 2, kPalNormal); + + int tileWidth = tilesiz[nTile].x; + + x += tileWidth + 1; + c++; + } + + return x; +} + +void EraseScreen(int nVal) +{ + if (nVal == -1) { + nVal = overscanindex; + } + + videoClearScreen(nVal); +#if 0 + for (int i = 0; i < numpages; i++) + { + videoClearScreen(nVal); + videoNextPage(); + } +#endif +} + +int Query(short nLines, short nKeys, ...) +{ + short i; + + char strings[20][80]; + char keys[20]; + + va_list args; + + short bPrevClip = bClip; + NoClip(); + + short nWidth = 0; + + va_start(args, nKeys); + + for (i = 0; i < nLines; i++) + { + char *str = va_arg(args, char*); + strcpy(strings[i], str); + Bstrupr(strings[i]); + + int strWidth = MyGetStringWidth(strings[i]); + + if (strWidth > nWidth) { + nWidth = strWidth; + } + } + + for (i = 0; i < nKeys; i++) { + keys[i] = (char)va_arg(args, int); + } + + va_end(args); + + int nHeight = (nLines + 1) * 10; + int y1 = (200 - nHeight) / 2; + int yB = y1; + + // add some padding to left and right sides of text in the box + nWidth += 30; + + int x1 = (320 - nWidth) / 2; + int x2 = x1 + nWidth; + + // if (bHiRes) + // { + // x1 *= 2; + // y1 *= 2; + // nHeight *= 2; + // x2 *= 2; + // } + + y1 = scale(y1, ydim, 200); + nHeight = scale(nHeight, ydim, 200); + x1 = scale(x1-160, ydim*4/3, 320)+xdim/2; + x2 = scale(x2-160, ydim*4/3, 320)+xdim/2; + + // draw background box that the text sits in + for (i = 0; i < nHeight; i++) { + renderDrawLine(x1 << 12, (y1 + i) << 12, x2 << 12, (y1 + i) << 12, overscanindex); + } + + y1 = yB + 5; + + // draw each line of text + for (i = 0; i < nLines; i++) { + myprintext((320 - MyGetStringWidth(strings[i])) / 2, (y1 + i*10) , strings[i], 0); + } + + videoNextPage(); + + if (bPrevClip) { + Clip(); + } + + i = 0; + + if (nKeys) + { + inputState.keyFlushChars(); + + while (1) + { + HandleAsync(); + + char key = toupper(inputState.keyGetChar()); + + for (i = 0; i < nKeys; i++) + { + if (keys[i] == 0 || key == keys[i]) + { + RefreshStatus(); + ClearAllKeys(); + return i; + } + } + } + } + + RefreshStatus(); + ClearAllKeys(); + + return i; +} + +void InitSpiritHead() +{ + char filename[20]; + + nPixels = 0; + + nSpiritRepeatX = sprite[nSpiritSprite].xrepeat; + nSpiritRepeatY = sprite[nSpiritSprite].yrepeat; + + tileLoad(kTileRamsesNormal); // Ramses Normal Head + + for (int i = 0; i < kMaxSprites; i++) + { + if (sprite[i].statnum) + { + sprite[i].cstat |= 0x8000; + } + } + + auto pTile = tilePtr(kTileRamsesNormal); // Ramses Normal Head + auto pGold = tilePtr(kTileRamsesGold); + for (int x = 0; x < 97; x++) + { + for (int y = 0; y < 106; y++) + { + if (*pTile != 255) + { + pixelval[nPixels] = *(pGold + x * 106 + y); + origx[nPixels] = x - 48; + origy[nPixels] = y - 53; + curx[nPixels] = 0; + cury[nPixels] = 0; + vely[nPixels] = 0; + velx[nPixels] = 0; + + destvelx[nPixels] = RandomSize(2) + 1; + + if (curx[nPixels] > 0) { + destvelx[nPixels] = -destvelx[nPixels]; + } + + destvely[nPixels] = RandomSize(2) + 1; + + if (cury[nPixels] > 0) { + destvely[nPixels] = -destvely[nPixels]; + } + + nPixels++; + } + + pTile++; + } + } + + + sprite[nSpiritSprite].yrepeat = 140; + sprite[nSpiritSprite].xrepeat = 140; + sprite[nSpiritSprite].picnum = kTileRamsesWorkTile; + + nHeadStage = 0; + + // work tile is twice as big as the normal head size + Worktile = TileFiles.tileCreate(kTileRamsesWorkTile, kSpiritX * 2, kSpiritY * 2); + + sprite[nSpiritSprite].cstat &= 0x7FFF; + + nHeadTimeStart = (int)totalclock; + + memset(Worktile, -1, WorktileSize); + tileInvalidate(kTileRamsesWorkTile, -1, -1); + + nPixelsToShow = 0; + + fadecdaudio(); + + int nTrack; + + if (levelnum == 1) + { + nTrack = 3; + } + else + { + nTrack = 7; + } + + bSubTitles = playCDtrack(nTrack, false) == 0; + + StartSwirlies(); + + sprintf(filename, "LEV%d.PUP", levelnum); + lNextStateChange = (int)totalclock; + lHeadStartClock = (int)totalclock; + + auto headfd = fileSystem.OpenFileReader(filename, 512); // 512?? + if (!headfd.isOpen()) + { + memset(cPupData, 0, sizeof(cPupData)); + } + else + { + nPupData = headfd.Read(cPupData, sizeof(cPupData)); + pPupData = cPupData; + } + nMouthTile = 0; + nTalkTime = 1; +} + +void DimSector(short nSector) +{ + short startwall = sector[nSector].wallptr; + short nWalls = sector[nSector].wallnum; + + for (int i = 0; i < nWalls; i++) + { + if (wall[startwall+i].shade < 40) { + wall[startwall+i].shade++; + } + } + + if (sector[nSector].floorshade < 40) { + sector[nSector].floorshade++; + } + + if (sector[nSector].ceilingshade < 40) { + sector[nSector].ceilingshade++; + } +} + +void CopyHeadToWorkTile(short nTile) +{ + const uint8_t* pSrc = tilePtr(nTile); + uint8_t *pDest = &Worktile[212 * 49 + 53]; + + for (int i = 0; i < 97; i++) + { + memcpy(pDest, pSrc, 106); + + pDest += 212; + pSrc += 106; + } +} + +int DoSpiritHead() +{ + static short word_964E6 = 0; + + nVertPan[0] += (nDestVertPan[0] - nVertPan[0]) / 4; + + tileInvalidate(kTileRamsesWorkTile, -1, -1); + + if (nHeadStage < 2) + { + memset(Worktile, -1, WorktileSize); + } + + if (nHeadStage < 2 || nHeadStage != 5) + { + nPixelsToShow = ((int)totalclock - nHeadTimeStart) * 15; + + if (nPixelsToShow > nPixels) { + nPixelsToShow = nPixels; + } + + if (nHeadStage < 3) + { + UpdateSwirlies(); + + if (sprite[nSpiritSprite].shade > -127) { + sprite[nSpiritSprite].shade--; + } + + word_964E6--; + if (word_964E6 < 0) + { + DimSector(sprite[nSpiritSprite].sectnum); + word_964E6 = 5; + } + + if (!nHeadStage) + { + if (((int)totalclock - nHeadTimeStart) > 480) + { + nHeadStage = 1; + nHeadTimeStart = (int)totalclock + 480; + } + +// int ecx = 0; + + // loc_1362C + for (int i = 0; i < nPixelsToShow; i++) + { + if (destvely[i] >= 0) + { + vely[i]++; + + if (vely[i] >= destvely[i]) + { + destvely[i] = -(RandomSize(2) + 1); + } + } + else + { + vely[i]--; + + if (vely[i] <= destvely[i]) + { + destvely[i] = RandomSize(2) + 1; + } + } + + // loc_13541 + if (destvelx[i] >= 0) + { + velx[i]++; + + if (velx[i] >= destvelx[i]) + { + destvelx[i] = -(RandomSize(2) + 1); + } + } + else + { + velx[i]--; + + if (velx[i] <= destvelx[i]) + { + destvelx[i] = RandomSize(2) + 1; + } + } + + // loc_13593 + int esi = vely[i] + (cury[i] >> 8); + + if (esi < 106) + { + if (esi < -105) + { + vely[i] = 0; + esi = 0; + } + } + else + { + vely[i] = 0; + esi = 0; + } + + // loc_135C6 + int ebx = velx[i] + (curx[i] >> 8); + + if (ebx < 97) + { + if (ebx < -96) + { + velx[i] = 0; + ebx = 0; + } + } + else + { + velx[i] = 0; + ebx = 0; + } + + // loc_135F9 + curx[i] = ebx * 256; + cury[i] = esi * 256; + + //ecx += 2; +// ecx++; + + esi += (ebx + 97) * 212; + + Worktile[106 + esi] = pixelval[i]; + } + + return 1; + } + else + { + // loc_13679: + if (nHeadStage != 1) { + return 1; + } + + uint8_t nXRepeat = sprite[nSpiritSprite].xrepeat; + if (nXRepeat > nSpiritRepeatX) + { + sprite[nSpiritSprite].xrepeat -= 2; + + nXRepeat = sprite[nSpiritSprite].xrepeat; + if (nXRepeat < nSpiritRepeatX) + { + sprite[nSpiritSprite].xrepeat = nSpiritRepeatX; + } + } + + uint8_t nYRepeat = sprite[nSpiritSprite].yrepeat; + if (nYRepeat > nSpiritRepeatY) + { + sprite[nSpiritSprite].yrepeat -= 2; + + nYRepeat = sprite[nSpiritSprite].yrepeat; + if (nYRepeat < nSpiritRepeatY) + { + sprite[nSpiritSprite].yrepeat = nSpiritRepeatY; + } + } + + // loc_13705 + int esi = 0; +// int edx = 0; + + // loc_137E7: + for (int i = 0; i < nPixels; i++) + { + int eax = (origx[i] << 8) - curx[i]; + int ecx = eax; + + if (eax) + { + if (eax < 0) { + eax = -eax; + } + + if (eax < 8) + { + curx[i] = origx[i] << 8; + ecx = 0; + } + else { + ecx >>= 3; + } + } + else + { + ecx >>= 3; + } + + // loc_1374B + int var_1C = (origy[i] << 8) - cury[i]; + int ebp = var_1C; + + if (var_1C) + { + eax = ebp; + + if (eax < 0) { + eax = -eax; + } + + if (eax < 8) + { + cury[i] = origy[i] << 8; + var_1C = 0; + } + else + { + var_1C >>= 3; + } + } + else + { + var_1C >>= 3; + } + + if (var_1C || ecx) + { + curx[i] += ecx; + cury[i] += var_1C; + + esi++; + } + + ecx = (((curx[i] >> 8) + 97) * 212) + (cury[i] >> 8); + +// edx++; + + Worktile[106 + ecx] = pixelval[i]; + } + + if (((int)totalclock - lHeadStartClock) > 600) { + CopyHeadToWorkTile(kTileRamsesGold); + } + + int eax = ((nPixels << 4) - nPixels) / 16; + + if (esi < eax) + { + SoundBigEntrance(); + AddGlow(sprite[nSpiritSprite].sectnum, 20); + AddFlash( + sprite[nSpiritSprite].sectnum, + sprite[nSpiritSprite].x, + sprite[nSpiritSprite].y, + sprite[nSpiritSprite].z, + 128); + + nHeadStage = 3; + TintPalette(255, 255, 255); + CopyHeadToWorkTile(kTileRamsesNormal); + } + + return 1; + } + } + else + { + // loc_138A7 + FixPalette(); + + if (!nPalDiff) + { + nFreeze = 2; + nHeadStage++; + } + + return 0; + } + } + else + { + if (lNextStateChange <= (int)totalclock) + { + if (nPupData) + { + short nPupVal = *pPupData; + pPupData++; + nPupData -= 2; + + if (nPupData > 0) + { + lNextStateChange = (nPupVal + lHeadStartClock) - 10; + nTalkTime = !nTalkTime; + } + else + { + nTalkTime = 0; + nPupData = 0; + } + } + else if (!bSubTitles) + { + if (!CDplaying()) + { + levelnew = levelnum + 1; + fadecdaudio(); + } + } + } + + word_964E8--; + if (word_964E8 <= 0) + { + word_964EA = RandomBit() * 2; + word_964E8 = RandomSize(5) + 4; + } + + int ebx = 592; + word_964EC--; + + if (word_964EC < 3) + { + ebx = 593; + if (word_964EC <= 0) { + word_964EC = RandomSize(6) + 4; + } + } + + ebx += word_964EA; + + // TODO - fixme. How big is worktile? + uint8_t *pDest = &Worktile[10441]; + const uint8_t* pSrc = tilePtr(ebx); + + for (int i = 0; i < 97; i++) + { + memcpy(pDest, pSrc, 106); + + pDest += 212; + pSrc += 106; + } + + if (nTalkTime) + { + if (nMouthTile < 2) { + nMouthTile++; + } + } + else if (nMouthTile != 0) + { + nMouthTile--; + } + + if (nMouthTile) + { + short nTileSizeX = tilesiz[nMouthTile + 598].x; + short nTileSizeY = tilesiz[nMouthTile + 598].y; + + // TODO - checkme. near loc_133AA +// uint8_t *pDest = (uint8_t*)worktile; +// pDest += (212 * (97 - nTileSizeX / 2)) + (159 - nTileSizeY); + + uint8_t *pDest = &Worktile[212 * (97 - nTileSizeX / 2)] + (159 - nTileSizeY); + const uint8_t *pSrc = tilePtr(nMouthTile + 598); + + while (nTileSizeX > 0) + { + memcpy(pDest, pSrc, nTileSizeY); + + nTileSizeX--; + pDest += 212; + pSrc += nTileSizeY; + } + } + + return 1; + } + + // TEMP FIXME - temporary return value. what to return here? 1? + + return 0; +} + +bool GameInterface::CanSave() +{ + return !bRecord && !bPlayback && !bPause && !bInDemo && nTotalPlayers == 1; +} + + +::GameInterface* CreateInterface() +{ + return new GameInterface; +} + +//short lastlevel; +//short forcelevel = -1; +//int lLocalCodes = 0; +//int flash; +//short textpages; + +// This is only the static global data. +static SavegameHelper sgh("exhumed", + SA(cPupData), + SV(nPupData), + SV(nPixels), + SA(curx), + SA(cury), + SA(destvelx), + SA(destvely), + SA(pixelval), + SA(origy), + SA(origx), + SA(velx), + SA(vely), + SV(nMouthTile), + SV(nSpiritSprite), + SV(word_964E8), + SV(word_964EA), + SV(word_964EC), + SV(nSpiritRepeatX), + SV(nSpiritRepeatY), + SV(nPixelsToShow), + SV(nCreaturesLeft), // todo: also maintain a total counter. + SV(nFreeze), + SV(nSnakeCam), + SV(nLocalSpr), + SV(levelnew), + SV(nClockVal), // kTile3603 + SV(nRedTicks), + SV(nAlarmTicks), + SV(nButtonColor), + SV(nEnergyChan), + SV(lCountDown), + SV(nEnergyTowers), + SV(nHeadStage), + SV(nTalkTime), + SV(levelnum), + SV(moveframes), + SV(totalmoves), + SV(nCurBodyNum), + SV(nBodyTotal), + SV(bSnakeCam), + SV(bSlipMode), + SV(lHeadStartClock), + SV(lNextStateChange), + SV(nHeadTimeStart), + SV(localclock), + SV(tclocks), + SV(tclocks2), + SV(totalclock), + nullptr); + + +void SaveTextureState() +{ + auto fw = WriteSavegameChunk("texture"); + int pupOffset = pPupData? int(pPupData - cPupData) : -1; + + // There is really no good way to restore these tiles, so it's probably best to save them as well, so that they can be reloaded with the exact state they were left in + fw->Write(&pupOffset, 4); + uint8_t loaded = !!Worktile; + fw->Write(&loaded, 1); + if (Worktile) fw->Write(Worktile, WorktileSize); + auto pixels = TileFiles.tileMakeWritable(kTile3603); + fw->Write(pixels, tilesiz[kTile3603].x * tilesiz[kTile3603].y); + pixels = TileFiles.tileMakeWritable(kEnergy1); + fw->Write(pixels, tilesiz[kEnergy1].x * tilesiz[kEnergy1].y); + pixels = TileFiles.tileMakeWritable(kEnergy2); + fw->Write(pixels, tilesiz[kEnergy2].x * tilesiz[kEnergy2].y); + +} + +void LoadTextureState() +{ + auto fr = ReadSavegameChunk("texture"); + int pofs; + fr.Read(&pofs, 4); + pPupData = pofs == -1 ? nullptr : cPupData + pofs; + uint8_t loaded; + fr.Read(&loaded, 1); + if (loaded) + { + Worktile = TileFiles.tileCreate(kTileRamsesWorkTile, kSpiritX * 2, kSpiritY * 2); + fr.Read(Worktile, WorktileSize); + } + auto pixels = TileFiles.tileMakeWritable(kTile3603); + fr.Read(pixels, tilesiz[kTile3603].x * tilesiz[kTile3603].y); + pixels = TileFiles.tileMakeWritable(kEnergy1); + fr.Read(pixels, tilesiz[kEnergy1].x * tilesiz[kEnergy1].y); + pixels = TileFiles.tileMakeWritable(kEnergy2); + fr.Read(pixels, tilesiz[kEnergy2].x * tilesiz[kEnergy2].y); + TileFiles.InvalidateTile(kTileRamsesWorkTile); + TileFiles.InvalidateTile(kTile3603); + TileFiles.InvalidateTile(kEnergy1); + TileFiles.InvalidateTile(kEnergy2); +} + + +END_PS_NS diff --git a/source/exhumed/src/exhumed.h b/source/exhumed/src/exhumed.h new file mode 100644 index 000000000..a6b5bdd40 --- /dev/null +++ b/source/exhumed/src/exhumed.h @@ -0,0 +1,300 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __exhumed_h__ +#define __exhumed_h__ + +#include "compat.h" +#include "baselayer.h" +#include "v_text.h" +#include "printf.h" +#include "gamecvars.h" +#include "m_argv.h" +#include "gamecontrol.h" +#include "c_buttons.h" +#include +#include "tarray.h" +#include "save.h" +#include "zstring.h" +#include "filesystem/filesystem.h" + +BEGIN_PS_NS + +#define kTimerTicks 120 + +#ifdef __WATCOMC__ +void handleevents(); +#endif + +enum basepal_t { + BASEPAL = 0, + ANIMPAL, + BASEPALCOUNT +}; + +#pragma pack(push, 1) +struct demo_header +{ + uint8_t nMap; + int16_t nWeapons; + int16_t nCurrentWeapon; + int16_t clip; + int16_t items; + + int16_t nHealth; + int16_t field_2; + int16_t nAction; + int16_t nSprite; + int16_t bIsMummified; + int16_t someNetVal; + int16_t invincibility; + int16_t nAir; + int16_t nSeq; + int16_t nMaskAmount; + uint16_t keys; + int16_t nMagic; + uint8_t item[8]; + int16_t nAmmo[7]; // TODO - kMaxWeapons? + int16_t pad[2]; + int16_t nCurrentWeapon2; + int16_t field_3FOUR; + int16_t bIsFiring; + int16_t field_38; + int16_t field_3A; + int16_t field_3C; + int16_t nRun; + + int16_t nLives; +}; + +struct demo_input +{ + int32_t moveframes; + + int32_t xVel; + int32_t yVel; + int16_t nAngle; + uint16_t buttons; + int16_t nTarget; + uint8_t horizon; + int8_t nItem; + int32_t h; + uint8_t i; + uint8_t pad[11]; +}; +#pragma pack(pop) + +void ExitGame(); +void ShutDown(void); +void DebugOut(const char *fmt, ...); +int ExhumedMain(int argc, char *argv[]); + +void FinishLevel(); + +void SetHiRes(); + +void BlackOut(); + +void DoGameOverScene(); + +int Query(short n, short l, ...); + +extern unsigned char curpal[]; + +void TintPalette(int a, int b, int c); +//void MySetPalette(unsigned char *palette); +//void GetCurPal(unsigned char *palette); + +void EraseScreen(int eax); + +void RestorePalette(); + +int FindGString(const char *str); + +void WaitTicks(int nTicks); + +void FadeIn(); +void FadeOut(int bFadeMusic); + +int myprintext(int x, int y, const char *str, int shade); +int MyGetStringWidth(const char *str); + +void mychangespritesect(int nSprite, int nSector); +void mydeletesprite(int nSprite); + +void GrabPalette(); + +void mysetbrightness(char nBrightness); + +void StartFadeIn(); +int DoFadeIn(); + +void InitSpiritHead(); + +int CopyCharToBitmap(char nChar, int nTile, int xPos, int yPos); + +// TODO - relocate +void StatusMessage(int messageTime, const char *fmt, ...); + +int DoSpiritHead(); + +void UpdateScreenSize(); + +void HandleAsync(); + +extern int32_t g_commandSetup; +extern int32_t g_noSetup; + +extern char sHollyStr[]; + +extern int localclock; + +extern int moveframes; + +extern short bSerialPlay; + +extern int nNetPlayerCount; + +extern int htimer; + +extern int nNetTime; + +extern short nTotalPlayers; + +extern short nFontFirstChar; +extern short nBackgroundPic; +extern short nShadowPic; + +extern short nCreaturesLeft; + +extern int lLocalButtons; + +extern short nEnergyTowers; + +extern short nEnergyChan; + +extern short nSpiritSprite; + +extern short bInDemo; + +extern short nFreeze; + +extern short nCurBodyNum; +extern short nBodyTotal; + +extern short bSnakeCam; + +extern short levelnum; +//extern short nScreenWidth; +//extern short nScreenHeight; + +extern short nMapMode; + +extern short nButtonColor; + +extern short nHeadStage; + +extern short lastfps; + +extern int flash; + +extern short bNoCreatures; + +extern short nLocalSpr; +extern short levelnew; + +extern short textpages; + +extern short nSnakeCam; + +extern short bHiRes; +extern short bCoordinates; +extern short bFullScreen; + +extern short bHolly; + +extern short screensize; + +extern int totalmoves; + +extern int lCountDown; + +extern short bSlipMode; + +extern short nItemTextIndex; +extern const char* gString[]; +extern const char* gPSDemoString[]; +extern const char* gEXDemoString[]; + +extern int bVanilla; + +#define POWERSLAVE (g_gameType & GAMEFLAG_POWERSLAVE) +#define EXHUMED (g_gameType & GAMEFLAG_EXHUMED) +#define ISDEMOVER (g_gameType & GAMEFLAG_SHAREWARE) + +extern double g_frameDelay; + +static inline double calcFrameDelay(int const maxFPS) { return maxFPS > 0 ? (timerGetFreqU64()/(double)maxFPS) : 0.0; } + +enum { + kPalNormal = 0, + kPalNoDim, + kPalTorch, + kPalNoTorch, + kPalBrite, + kPalRedBrite, + kPalGreenBrite, + kPalNormal2, + kPalNoDim2, + kPalTorch2, + kPalNoTorch2, + kPalBrite2 +}; + +extern char g_modDir[BMAX_PATH]; + +extern int loaddefinitions_game(const char* fn, int32_t preload); +void G_LoadGroupsInDir(const char* dirname); +void G_DoAutoload(const char* dirname); + +struct GameInterface : ::GameInterface +{ + int app_main() override; + bool validate_hud(int) override { return true; } + void set_hud_layout(int size) override {} + void set_hud_scale(int size) override {} + void DrawNativeMenuText(int fontnum, int state, double xpos, double ypos, float fontscale, const char* text, int flags) override; + void MenuOpened() override; + void MenuSound(EMenuSounds snd) override; + void MenuClosed() override; + void StartGame(FGameStartup& gs) override; + FSavegameInfo GetSaveSig() override; + void DrawCenteredTextScreen(const DVector2& origin, const char* text, int position, bool bg) override; + void DrawMenuCaption(const DVector2& origin, const char* text) override; + bool LoadGame(FSaveGameNode* sv) override; + bool SaveGame(FSaveGameNode* sv) override; + bool CanSave() override; + + FString statFPS() override; + //GameStats getStats() override; +}; + + +END_PS_NS + +#endif diff --git a/source/exhumed/src/fish.cpp b/source/exhumed/src/fish.cpp new file mode 100644 index 000000000..b770abaeb --- /dev/null +++ b/source/exhumed/src/fish.cpp @@ -0,0 +1,601 @@ +//------------------------------------------------------------------------- +/* +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 "fish.h" +#include "anims.h" +#include "engine.h" +#include "sequence.h" +#include "random.h" +#include "runlist.h" +#include "exhumed.h" +#include "move.h" +#include "trigdat.h" +#include "init.h" +#include "sound.h" +#include + +BEGIN_PS_NS + +#define kMaxFishes 128 +#define kMaxChunks 128 + +short FishSprite = -1; +short FishCount = 0; + +static actionSeq ActionSeq[] = { + {8, 0}, + {8, 0}, + {0, 0}, + {24, 0}, + {8, 0}, + {32, 1}, + {33, 1}, + {34, 1}, + {35, 1}, + {39, 1} +}; + +short nChunksFree; + +int nFreeChunk[kMaxChunks] = { 0 }; + +struct Fish +{ + short nHealth; + short field_2; + short nAction; + short nSprite; + short nTarget; + short field_A; + short field_C; + short field_E; +}; + +struct Chunk +{ + short nSprite; + short field_2; + short field_4; + short field_6; +}; + +Fish FishList[kMaxFishes]; +Chunk FishChunk[kMaxChunks]; + +static SavegameHelper sgh("fish", + SV(FishSprite), + SV(FishCount), + SV(nChunksFree), + SA(nFreeChunk), + SA(FishList), + SA(FishChunk), + nullptr); + + +void InitFishes() +{ + FishCount = 0; + FishSprite = 1; + nChunksFree = kMaxChunks; + + for (int i = 0; i < kMaxChunks; i++) { + nFreeChunk[i] = i; + } +} + +int BuildFishLimb(short nFish, short edx) +{ + if (nChunksFree <= 0) { + return -1; + } + + short nSprite = FishList[nFish].nSprite; + + nChunksFree--; + + int nFree = nFreeChunk[nChunksFree]; + + int nSprite2 = insertsprite(sprite[nSprite].sectnum, 99); + assert(nSprite2 >= 0 && nSprite2 < kMaxSprites); + + FishChunk[nFree].nSprite = nSprite2; + FishChunk[nFree].field_4 = edx + 40; + FishChunk[nFree].field_2 = RandomSize(3) % SeqSize[SeqOffsets[kSeqFish] + edx + 40]; + + sprite[nSprite2].x = sprite[nSprite].x; + sprite[nSprite2].y = sprite[nSprite].y; + sprite[nSprite2].z = sprite[nSprite].z; + sprite[nSprite2].cstat = 0; + sprite[nSprite2].shade = -12; + sprite[nSprite2].pal = 0; + sprite[nSprite2].xvel = (RandomSize(5) - 16) << 8; + sprite[nSprite2].yvel = (RandomSize(5) - 16) << 8; + sprite[nSprite2].xrepeat = 64; + sprite[nSprite2].yrepeat = 64; + sprite[nSprite2].xoffset = 0; + sprite[nSprite2].yoffset = 0; + sprite[nSprite2].zvel = (-(RandomByte() + 512)) * 2; + + // not sure what's going on here... return value doesn't seem to be used + seq_GetSeqPicnum(kSeqFish, FishChunk[nFree].field_4, 0); + + sprite[nSprite2].picnum = edx; + sprite[nSprite2].lotag = runlist_HeadRun() + 1; + sprite[nSprite2].clipdist = 0; + +// GrabTimeSlot(3); + + sprite[nSprite2].extra = -1; + sprite[nSprite2].owner = runlist_AddRunRec(sprite[nSprite2].lotag - 1, nFree | 0x200000); + sprite[nSprite2].hitag = runlist_AddRunRec(NewRun, nFree | 0x200000); + + return nFree | 0x200000; +} + +void BuildBlood(int x, int y, int z, short nSector) +{ + BuildAnim(-1, kSeqFish, 36, x, y, z, nSector, 75, 128); +} + +void FuncFishLimb(int a, int UNUSED(nDamage), int nRun) +{ + short nFish = RunData[nRun].nVal; + short nSprite = FishChunk[nFish].nSprite; + + assert(nSprite >= 0 && nSprite < kMaxSprites); + + int nSeq = SeqOffsets[kSeqFish] + FishChunk[nFish].field_4; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + case 0x20000: + { + sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, FishChunk[nFish].field_2); + + Gravity(nSprite); + + FishChunk[nFish].field_2++; + + if (FishChunk[nFish].field_2 >= SeqSize[nSeq]) + { + FishChunk[nFish].field_2 = 0; + if (RandomBit()) { + BuildBlood(sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, sprite[nSprite].sectnum); + } + } + + int FloorZ = sector[sprite[nSprite].sectnum].floorz; + + if (FloorZ <= sprite[nSprite].z) + { + sprite[nSprite].z += 256; + + if ((sprite[nSprite].z - FloorZ) > 25600) + { + sprite[nSprite].zvel = 0; + runlist_DoSubRunRec(sprite[nSprite].owner); + runlist_FreeRun(sprite[nSprite].lotag - 1); + runlist_SubRunRec(sprite[nSprite].hitag); + mydeletesprite(nSprite); + } + else if ((sprite[nSprite].z - FloorZ) > 0) + { + sprite[nSprite].zvel = 1024; + } + + return; + } + else + { + if (movesprite(nSprite, sprite[nSprite].xvel << 8, sprite[nSprite].yvel << 8, sprite[nSprite].zvel, 2560, -2560, CLIPMASK1)) + { + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + } + } + + return; + } + + case 0x90000: + { + seq_PlotSequence(a & 0xFFFF, nSeq, FishChunk[nFish].field_2, 1); + return; + } + } +} + +int BuildFish(int nSprite, int x, int y, int z, int nSector, int nAngle) +{ + short nFish = FishCount; + FishCount++; + + if (nFish >= kMaxFishes) { + return -1; + } + + if (nSprite == -1) + { + nSprite = insertsprite(nSector, 103); + } + else + { + x = sprite[nSprite].x; + y = sprite[nSprite].y; + z = sprite[nSprite].z; + nAngle = sprite[nSprite].ang; + changespritestat(nSprite, 103); + } + + assert(nSprite >= 0 && nSprite < kMaxSprites); + + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + sprite[nSprite].cstat = 0x101; + sprite[nSprite].shade = -12; + sprite[nSprite].clipdist = 80; + sprite[nSprite].xrepeat = 40; + sprite[nSprite].yrepeat = 40; + sprite[nSprite].pal = sector[sprite[nSprite].sectnum].ceilingpal; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].picnum = seq_GetSeqPicnum(kSeqFish, ActionSeq[0].a, 0); + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].ang = nAngle; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].hitag = 0; + sprite[nSprite].extra = -1; + +// GrabTimeSlot(3); + + FishList[nFish].nAction = 0; + FishList[nFish].nHealth = 200; + FishList[nFish].nSprite = nSprite; + FishList[nFish].nTarget = -1; + FishList[nFish].field_C = 60; + FishList[nFish].field_2 = 0; + + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nFish | 0x120000); + FishList[nFish].field_E = runlist_AddRunRec(NewRun, nFish | 0x120000); + + nCreaturesLeft++; + + return nFish | 0x120000; +} + +void IdleFish(short nFish, short edx) +{ + short nSprite = FishList[nFish].nSprite; + + sprite[nSprite].ang += (256 - RandomSize(9)) + 1024; + sprite[nSprite].ang &= kAngleMask; + + sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 8; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 8; + + FishList[nFish].nAction = 0; + FishList[nFish].field_2 = 0; + + sprite[nSprite].zvel = RandomSize(9); + + if (!edx) + { + if (RandomBit()) { + sprite[nSprite].zvel = -sprite[nSprite].zvel; + } + } + else if (edx < 0) + { + sprite[nSprite].zvel = -sprite[nSprite].zvel; + } +} + +void DestroyFish(short nFish) +{ + short nSprite = FishList[nFish].nSprite; + + runlist_DoSubRunRec(sprite[nSprite].owner); + runlist_FreeRun(sprite[nSprite].lotag - 1); + runlist_SubRunRec(FishList[nFish].field_E); + mydeletesprite(nSprite); +} + +void FuncFish(int a, int nDamage, int nRun) +{ + short nFish = RunData[nRun].nVal; + assert(nFish >= 0 && nFish < kMaxFishes); + + short nSprite = FishList[nFish].nSprite; + short nAction = FishList[nFish].nAction; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + default: + { + Printf("unknown msg %d for Fish\n", a & 0x7F0000); + return; + } + + case 0x90000: + { + seq_PlotSequence(a & 0xFFFF, SeqOffsets[kSeqFish] + ActionSeq[nAction].a, FishList[nFish].field_2, ActionSeq[nAction].b); + tsprite[a & 0xFFFF].owner = -1; + return; + } + + case 0xA0000: + { + if (FishList[nFish].nHealth <= 0) { + return; + } + else + { + nDamage = runlist_CheckRadialDamage(nSprite); + if (!nDamage) { + return; + } + + FishList[nFish].field_C = 10; + } + // fall through + fallthrough__; + } + case 0x80000: + { + if (!nDamage) { + return; + } + + FishList[nFish].nHealth -= nDamage; + if (FishList[nFish].nHealth <= 0) + { + FishList[nFish].nHealth = 0; + nCreaturesLeft--; + + sprite[nSprite].cstat &= 0xFEFE; + + if (nMessage == 0x80000) + { + for (int i = 0; i < 3; i++) + { + BuildFishLimb(nFish, i); + } + + PlayFXAtXYZ(StaticSound[kSound40], sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, sprite[nSprite].sectnum); + DestroyFish(nFish); + } + else + { + FishList[nFish].nAction = 9; + FishList[nFish].field_2 = 0; + } + + return; + } + else + { + short nTarget = a & 0xFFFF; + if (nTarget >= 0 && sprite[nTarget].statnum < 199) + { + FishList[nFish].nTarget = nTarget; + } + + FishList[nFish].nAction = 4; + FishList[nFish].field_2 = 0; + FishList[nFish].field_C += 10; + } + + return; + } + + case 0x20000: + { + if (!(SectFlag[sprite[nSprite].sectnum] & kSectUnderwater)) + { + Gravity(nSprite); + } + + short nSeq = SeqOffsets[kSeqFish] + ActionSeq[nAction].a; + + sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, FishList[nFish].field_2); + + seq_MoveSequence(nSprite, nSeq, FishList[nFish].field_2); + + FishList[nFish].field_2++; + if (FishList[nFish].field_2 >= SeqSize[nSeq]) { + FishList[nFish].field_2 = 0; + } + + short nTarget = FishList[nFish].nTarget; + + switch (nAction) + { + default: + return; + + case 0: + { + FishList[nFish].field_C--; + if (FishList[nFish].field_C <= 0) + { + nTarget = FindPlayer(nSprite, 60); + if (nTarget >= 0) + { + FishList[nFish].nTarget = nTarget; + FishList[nFish].nAction = 2; + FishList[nFish].field_2 = 0; + + int nAngle = GetMyAngle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].z - sprite[nSprite].z); + sprite[nSprite].zvel = Sin(nAngle) >> 5; + + FishList[nFish].field_C = RandomSize(6) + 90; + } + else + { + IdleFish(nFish, 0); + } + } + + break; + } + + case 1: + return; + + case 2: + case 3: + { + FishList[nFish].field_C--; + if (FishList[nFish].field_C <= 0) + { + IdleFish(nFish, 0); + return; + } + else + { + PlotCourseToSprite(nSprite, nTarget); + int nHeight = GetSpriteHeight(nSprite) >> 1; + + int z = sprite[nTarget].z - sprite[nSprite].z; + + if (z < 0) { + z = -z; + } + + if (z <= nHeight) + { + sprite[nSprite].xvel = (Sin(sprite[nSprite].ang + 512) >> 5) - (Sin(sprite[nSprite].ang + 512) >> 7); + sprite[nSprite].yvel = (Sin(sprite[nSprite].ang) >> 5) - (Sin(sprite[nSprite].ang) >> 7); + } + else + { + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + } + + sprite[nSprite].zvel = (sprite[nTarget].z - sprite[nSprite].z) >> 3; + } + break; + } + + case 4: + { + if (FishList[nFish].field_2 == 0) + { + IdleFish(nFish, 0); + } + return; + } + + case 8: + { + return; + } + + case 9: + { + if (FishList[nFish].field_2 == 0) + { + DestroyFish(nFish); + } + return; + } + } + + int x = sprite[nSprite].x; + int y = sprite[nSprite].y; + int z = sprite[nSprite].z; + short nSector = sprite[nSprite].sectnum; + + // loc_2EF54 + int nVal = movesprite(nSprite, sprite[nSprite].xvel << 13, sprite[nSprite].yvel << 13, sprite[nSprite].zvel << 2, 0, 0, CLIPMASK0); + + if (!(SectFlag[sprite[nSprite].sectnum] & kSectUnderwater)) + { + mychangespritesect(nSprite, nSector); + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + + IdleFish(nFish, 0); + return; + } + else + { + if (nAction >= 5) { + return; + } + + if (!nVal) + { + if (nAction == 3) + { + FishList[nFish].nAction = 2; + FishList[nFish].field_2 = 0; + } + return; + } + + if ((nVal & 0x30000) == 0) + { + if ((nVal & 0xC000) == 0x8000) + { + IdleFish(nFish, 0); + } + else if ((nVal & 0xC000) == 0xC000) + { + if (sprite[nVal & 0x3FFF].statnum == 100) + { + FishList[nFish].nTarget = nVal & 0x3FFF; + sprite[nSprite].ang = GetMyAngle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y); + + if (nAction != 3) + { + FishList[nFish].nAction = 3; + FishList[nFish].field_2 = 0; + } + + if (!FishList[nFish].field_2) + { + runlist_DamageEnemy(nTarget, nSprite, 2); + } + } + } + } + else if (nVal & 0x20000) + { + IdleFish(nFish, -1); + } + else + { + IdleFish(nFish, 1); + } + } + + return; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/fish.h b/source/exhumed/src/fish.h new file mode 100644 index 000000000..6a3d2bc95 --- /dev/null +++ b/source/exhumed/src/fish.h @@ -0,0 +1,32 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __fish_h__ +#define __fish_h__ + +BEGIN_PS_NS + +void InitFishes(); +int BuildFish(int nSprite, int x, int y, int z, int nSector, int nAngle); + +void FuncFish(int, int, int); +void FuncFishLimb(int a, int b, int c); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/grenade.cpp b/source/exhumed/src/grenade.cpp new file mode 100644 index 000000000..7e908a921 --- /dev/null +++ b/source/exhumed/src/grenade.cpp @@ -0,0 +1,446 @@ +//------------------------------------------------------------------------- +/* +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 "grenade.h" +#include "engine.h" +#include "player.h" +#include "runlist.h" +#include "exhumed.h" +#include "sound.h" +#include "move.h" +#include "init.h" +#include "bullet.h" +#include "gun.h" +#include "anims.h" +#include "lighting.h" +#include "sequence.h" +#include "random.h" +#include + +BEGIN_PS_NS + +int nGrenadeCount = 0; +int nGrenadesFree; + +short GrenadeFree[kMaxGrenades]; + +struct Grenade +{ + short field_0; + short field_2; + short nSprite; + short field_6; + short field_8; + short field_A; + short field_C; + short field_E; + int field_10; + int x; + int y; +}; + +Grenade GrenadeList[kMaxGrenades]; + +static SavegameHelper sgh("grenade", + SV(nGrenadeCount), + SV(nGrenadesFree), + SA(GrenadeFree), + SA(GrenadeList), + nullptr); + + +void InitGrenades() +{ + nGrenadeCount = 0; + + for (int i = 0; i < kMaxGrenades; i++) { + GrenadeFree[i] = i; + } + + nGrenadesFree = kMaxGrenades; +} + +short GrabGrenade() +{ + return GrenadeFree[--nGrenadesFree]; +} + +void DestroyGrenade(short nGrenade) +{ + runlist_DoSubRunRec(GrenadeList[nGrenade].field_6); + runlist_SubRunRec(GrenadeList[nGrenade].field_8); + runlist_DoSubRunRec(sprite[GrenadeList[nGrenade].nSprite].lotag - 1); + + mydeletesprite(GrenadeList[nGrenade].nSprite); + GrenadeFree[nGrenadesFree] = nGrenade; + + nGrenadesFree++; +} + +void BounceGrenade(short nGrenade, short nAngle) +{ + GrenadeList[nGrenade].field_10 >>= 1; + + GrenadeList[nGrenade].x = (Sin(nAngle + 512) >> 5) * GrenadeList[nGrenade].field_10; + GrenadeList[nGrenade].y = (Sin(nAngle) >> 5) * GrenadeList[nGrenade].field_10; + + D3PlayFX(StaticSound[kSound3], GrenadeList[nGrenade].nSprite); +} + +int ThrowGrenade(short nPlayer, int UNUSED(edx), int UNUSED(ebx), int ecx, int push1) +{ + if (nPlayerGrenade[nPlayer] < 0) + return -1; + + short nGrenade = nPlayerGrenade[nPlayer]; + + short nGrenadeSprite = GrenadeList[nGrenade].nSprite; + short nPlayerSprite = PlayerList[nPlayer].nSprite; + + short nAngle = sprite[nPlayerSprite].ang; + + mychangespritesect(nGrenadeSprite, nPlayerViewSect[nPlayer]); + + sprite[nGrenadeSprite].x = sprite[nPlayerSprite].x; + sprite[nGrenadeSprite].y = sprite[nPlayerSprite].y; + sprite[nGrenadeSprite].z = sprite[nPlayerSprite].z; + + if (nAngle < 0) { + nAngle = sprite[nPlayerSprite].ang; + } + + sprite[nGrenadeSprite].cstat &= 0x7FFF; + sprite[nGrenadeSprite].ang = nAngle; + + if (push1 >= -3000) + { + int nVel = totalvel[nPlayer] << 5; + + GrenadeList[nGrenade].field_10 = ((90 - GrenadeList[nGrenade].field_E) * (90 - GrenadeList[nGrenade].field_E)) + nVel; + sprite[nGrenadeSprite].zvel = (-64 * push1) - 4352; + + int nMov = movesprite(nGrenadeSprite, Sin(nAngle + 512) * (sprite[nPlayerSprite].clipdist << 3), Sin(nAngle) * (sprite[nPlayerSprite].clipdist << 3), ecx, 0, 0, CLIPMASK1); + if (nMov & 0x8000) + { + nAngle = GetWallNormal(nMov & 0x3FFF); + BounceGrenade(nGrenade, nAngle); + } + } + else + { + GrenadeList[nGrenade].field_10 = 0; + sprite[nGrenadeSprite].zvel = sprite[nPlayerSprite].zvel; + } + + GrenadeList[nGrenade].x = Sin(nAngle + 512) >> 4; + GrenadeList[nGrenade].x *= GrenadeList[nGrenade].field_10; + + GrenadeList[nGrenade].y = Sin(nAngle) >> 4; + GrenadeList[nGrenade].y *= GrenadeList[nGrenade].field_10; + + nPlayerGrenade[nPlayer] = -1; + + return nGrenadeSprite; +} + +int BuildGrenade(int nPlayer) +{ + if (nGrenadesFree == 0) + return -1; + + int nGrenade = GrabGrenade(); + + int nSprite = insertsprite(nPlayerViewSect[nPlayer], 201); + assert(nSprite >= 0 && nSprite < kMaxSprites); + + int nPlayerSprite = PlayerList[nPlayer].nSprite; + + sprite[nSprite].x = sprite[nPlayerSprite].x; + sprite[nSprite].y = sprite[nPlayerSprite].y; + sprite[nSprite].z = sprite[nPlayerSprite].z - 3840; + sprite[nSprite].shade = -64; + sprite[nSprite].xrepeat = 20; + sprite[nSprite].yrepeat = 20; + sprite[nSprite].cstat = 0x8000u; + sprite[nSprite].picnum = 1; + sprite[nSprite].pal = 0; + sprite[nSprite].clipdist = 30; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].ang = sprite[nPlayerSprite].ang; + sprite[nSprite].yvel = 0; + sprite[nSprite].owner = nPlayerSprite; + sprite[nSprite].xvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].hitag = 0; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].extra = -1; + +// GrabTimeSlot(3); + + GrenadeList[nGrenade].field_E = 90; + GrenadeList[nGrenade].field_2 = 0; + GrenadeList[nGrenade].field_0 = 16; + GrenadeList[nGrenade].field_10 = -1; + GrenadeList[nGrenade].nSprite = nSprite; + GrenadeList[nGrenade].field_A = 0; + GrenadeList[nGrenade].field_C = 0; + GrenadeList[nGrenade].field_6 = runlist_AddRunRec(sprite[nSprite].lotag - 1, nGrenade | 0x0F0000); + GrenadeList[nGrenade].field_8 = runlist_AddRunRec(NewRun, nGrenade | 0x0F0000); + + nGrenadePlayer[nGrenade] = nPlayer; + nPlayerGrenade[nPlayer] = nGrenade; + + return nSprite; +} + +void ExplodeGrenade(short nGrenade) +{ + int var_28, var_20; + + short nPlayer = nGrenadePlayer[nGrenade]; + int nGrenadeSprite = GrenadeList[nGrenade].nSprite; + short nGrenadeSect = sprite[nGrenadeSprite].sectnum; + + GrenadeList[nGrenade].field_C = 1; + + if (SectFlag[nGrenadeSect] & kSectUnderwater) + { + var_28 = 75; + var_20 = 60; + } + else + { + if (sprite[nGrenadeSprite].z < sector[nGrenadeSect].floorz) + { + var_20 = 200; + var_28 = 36; + +// TODO MonoOut("GRENPOW\n"); + } + else + { + var_28 = 34; + var_20 = 150; + +// TODO MonoOut("GRENBOOM\n"); + } + } + + if (GrenadeList[nGrenade].field_10 < 0) + { + short nPlayerSprite = PlayerList[nPlayer].nSprite; + short nAngle = sprite[nPlayerSprite].ang; + + sprite[nGrenadeSprite].z = sprite[nPlayerSprite].z; + sprite[nGrenadeSprite].x = (Sin(nAngle + 512) >> 5) + sprite[nPlayerSprite].x; + sprite[nGrenadeSprite].y = (Sin(nAngle) >> 5) + sprite[nPlayerSprite].y; + + changespritesect(nGrenadeSprite, sprite[nPlayerSprite].sectnum); + + if (!PlayerList[nPlayer].invincibility) { + PlayerList[nPlayer].nHealth = 1; + } + } + + short nDamage = BulletInfo[kWeaponGrenade].nDamage; + + if (nPlayerDouble[nPlayer] > 0) { + nDamage *= 2; + } + + runlist_RadialDamageEnemy(nGrenadeSprite, nDamage, BulletInfo[kWeaponGrenade].field_10); + + BuildAnim(-1, var_28, 0, sprite[nGrenadeSprite].x, sprite[nGrenadeSprite].y, sprite[nGrenadeSprite].z, sprite[nGrenadeSprite].sectnum, var_20, 4); + AddFlash(sprite[nGrenadeSprite].sectnum, sprite[nGrenadeSprite].x, sprite[nGrenadeSprite].y, sprite[nGrenadeSprite].z, 128); + + nGrenadePlayer[nGrenade] = -1; + DestroyGrenade(nGrenade); +} + +void FuncGrenade(int a, int UNUSED(nDamage), int nRun) +{ + short nGrenade = RunData[nRun].nVal; + assert(nGrenade >= 0 && nGrenade < kMaxGrenades); + + short nGrenadeSprite = GrenadeList[nGrenade].nSprite; + short nSeq; + + if (GrenadeList[nGrenade].field_C) + { + nSeq = SeqOffsets[kSeqGrenBoom]; + } + else + { + nSeq = SeqOffsets[kSeqGrenRoll] + GrenadeList[nGrenade].field_A; + } + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + case 0x90000: + { + seq_PlotSequence(a & 0xFFFF, nSeq, GrenadeList[nGrenade].field_2 >> 8, 1); + break; + } + + default: + { + Printf("unknown msg %d for bullet\n", a & 0x7F0000); // TODO - change 'bullet' to 'grenade' ? + return; + } + + case 0x20000: + { + seq_MoveSequence(nGrenadeSprite, nSeq, GrenadeList[nGrenade].field_2 >> 8); + sprite[nGrenadeSprite].picnum = seq_GetSeqPicnum2(nSeq, GrenadeList[nGrenade].field_2 >> 8); + + GrenadeList[nGrenade].field_E--; + if (!GrenadeList[nGrenade].field_E) + { + short nPlayer = nGrenadePlayer[nGrenade]; + + if (GrenadeList[nGrenade].field_10 < 0) + { + PlayerList[nPlayer].field_3A = 0; + PlayerList[nPlayer].field_3FOUR = 0; + + if (PlayerList[nPlayer].nAmmo[kWeaponGrenade]) + { + PlayerList[nPlayer].bIsFiring = kFalse; + } + else + { + SelectNewWeapon(nPlayer); + + PlayerList[nPlayer].nCurrentWeapon = PlayerList[nPlayer].field_38; + PlayerList[nPlayer].field_38 = -1; + } + } + + ExplodeGrenade(nGrenade); + return; + } + else + { + if (GrenadeList[nGrenade].field_10 < 0) { + return; + } + + int ebp = (GrenadeList[nGrenade].field_2 + GrenadeList[nGrenade].field_0) >> 8; + + GrenadeList[nGrenade].field_2 += GrenadeList[nGrenade].field_0; + + if (ebp < 0) + { + GrenadeList[nGrenade].field_2 += SeqSize[nSeq] << 8; + } + else + { + if (ebp >= SeqSize[nSeq]) + { + if (GrenadeList[nGrenade].field_C) + { + DestroyGrenade(nGrenade); + return; + } + else + { + GrenadeList[nGrenade].field_2 = GrenadeList[nGrenade].field_C; + } + } + } + + if (GrenadeList[nGrenade].field_C) { + return; + } + + int zVel = sprite[nGrenadeSprite].zvel; + + Gravity(nGrenadeSprite); + int nMov = movesprite(nGrenadeSprite, GrenadeList[nGrenade].x, GrenadeList[nGrenade].y, sprite[nGrenadeSprite].zvel, sprite[nGrenadeSprite].clipdist >> 1, sprite[nGrenadeSprite].clipdist >> 1, CLIPMASK1); + + if (!nMov) + return; + + if (nMov & 0x20000) + { + if (zVel) + { + if (SectDamage[sprite[nGrenadeSprite].sectnum] > 0) + { + ExplodeGrenade(nGrenade); + return; + } + + GrenadeList[nGrenade].field_0 = (uint8_t)totalmoves; // limit to 8bits? + + D3PlayFX(StaticSound[kSound3], nGrenadeSprite); + + sprite[nGrenadeSprite].zvel = -(zVel >> 1); + + if (sprite[nGrenadeSprite].zvel > -1280) + { + D3PlayFX(StaticSound[kSound5], nGrenadeSprite); + GrenadeList[nGrenade].field_0 = 0; + GrenadeList[nGrenade].field_2 = 0; + sprite[nGrenadeSprite].zvel = 0; + GrenadeList[nGrenade].field_A = 1; + } + } + + GrenadeList[nGrenade].field_0 = 255 - (RandomByte() * 2); + GrenadeList[nGrenade].x -= (GrenadeList[nGrenade].x >> 4); + GrenadeList[nGrenade].y -= (GrenadeList[nGrenade].y >> 4); + } + + // loc_2CF60: + if ((nMov & 0xC000) >= 0x8000) + { + if ((nMov & 0xC000) <= 0x8000) + { + BounceGrenade(nGrenade, GetWallNormal(nMov & 0x3FFF)); + } + else if ((nMov & 0xC000) == 0xC000) + { + BounceGrenade(nGrenade, sprite[nMov & 0x3FFF].ang); + } + } + + GrenadeList[nGrenade].field_2 = 0; + return; + } + + break; + } + + case 0xA0000: + { + if (nGrenadeSprite != nRadialSpr && !GrenadeList[nGrenade].field_C) + { + if (runlist_CheckRadialDamage(nGrenadeSprite) > 280) + { + GrenadeList[nGrenade].field_E = RandomSize(4) + 1; + } + } + break; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/grenade.h b/source/exhumed/src/grenade.h new file mode 100644 index 000000000..a7d783cef --- /dev/null +++ b/source/exhumed/src/grenade.h @@ -0,0 +1,34 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __grenade_h__ +#define __grenade_h__ + +BEGIN_PS_NS + +#define kMaxGrenades 50 + +void InitGrenades(); +int BuildGrenade(int nPlayer); +void DestroyGrenade(short nGrenade); +int ThrowGrenade(short nPlayer, int edx, int ebx, int ecx, int push1); +void FuncGrenade(int, int, int); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/gun.cpp b/source/exhumed/src/gun.cpp new file mode 100644 index 000000000..78584be58 --- /dev/null +++ b/source/exhumed/src/gun.cpp @@ -0,0 +1,1128 @@ +//------------------------------------------------------------------------- +/* +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 "gun.h" +#include "engine.h" +#include "init.h" +#include "player.h" +#include "exhumed.h" +#include "view.h" +#include "move.h" +#include "status.h" +#include "bubbles.h" +#include "typedefs.h" +#include "sound.h" +#include "ra.h" +#include "snake.h" +#include "grenade.h" +#include "lighting.h" +#include "light.h" +#include "ps_input.h" +#include "util.h" +#include "anims.h" +#include "runlist.h" +#include "bullet.h" +#include "trigdat.h" +#include "object.h" +#include +#include + +BEGIN_PS_NS + +/* +struct Weapon +{ + short nSeq; + short b[12]; // seq offsets? + short nAmmoType; + short c; + short d; + short bFireUnderwater; +}; +*/ + +Weapon WeaponInfo[] = { + { kSeqSword, { 0, 1, 3, 7, -1, 2, 4, 5, 6, 8, 9, 10 }, 0, 0, 0, kTrue }, + { kSeqPistol, { 0, 3, 2, 4, -1, 1, 0, 0, 0, 0, 0, 0 }, 1, 0, 1, kFalse }, + { kSeqM60, { 0, 5, 6, 16, -1, 21, 0, 0, 0, 0, 0, 0 }, 2, 0, 1, kFalse }, + { kSeqFlamer, { 0, 2, 5, 5, 6, 1, 0, 0, 0, 0, 0, 0 }, 3, 4, 1, kFalse }, + { kSeqGrenade, { 0, 2, 3, 4, -1, 1, 0, 0, 0, 0, 0, 0 }, 4, 0, 1, kTrue }, + { kSeqCobra, { 0, 1, 2, 2, -1, 4, 0, 0, 0, 0, 0, 0 }, 5, 0, 1, kTrue }, + { kSeqRavolt, { 0, 1, 2, 3, -1, 4, 0, 0, 0, 0, 0, 0 }, 6, 0, 1, kTrue }, + { kSeqRothands,{ 0, 1, 2, -1, -1, -1, 0, 0, 0, 0, 0, 0 }, 7, 0, 0, kTrue }, + { kSeqDead, { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 1, 0, kFalse }, + { kSeqDeadEx, { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 1, 0, kFalse }, + { kSeqDeadBrn, { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0, 1, 0, kFalse } +}; + +short nTemperature[kMaxPlayers]; +short nMinAmmo[] = { 0, 24, 51, 50, 1, 0, 0 }; +short word_96E26 = 0; + +static SavegameHelper sgh("gun", + SA(nTemperature), + SV(word_96E26), + nullptr); + + +void RestoreMinAmmo(short nPlayer) +{ + for (int i = 0; i < kMaxWeapons; i++) + { + if (i == kWeaponGrenade) { + continue; + } + + if ((1 << i) & nPlayerWeapons[nPlayer]) + { + if (nMinAmmo[i] > PlayerList[nPlayer].nAmmo[i]) { + PlayerList[nPlayer].nAmmo[i] = nMinAmmo[i]; + } + } + } + + CheckClip(nPlayer); +} + +void FillWeapons(short nPlayer) +{ + nPlayerWeapons[nPlayer] = 0xFFFF; // turn on all bits + + for (int i = 0; i < kMaxWeapons; i++) + { + if (WeaponInfo[i].d) { + PlayerList[nPlayer].nAmmo[i] = 99; + } + } + + CheckClip(nPlayer); + + if (nPlayer == nLocalPlayer) + { + short nWeapon = PlayerList[nPlayer].nCurrentWeapon; + SetCounter(PlayerList[nPlayer].nAmmo[nWeapon]); + } +} + +void ResetPlayerWeapons(short nPlayer) +{ + for (int i = 0; i < kMaxWeapons; i++) + { + PlayerList[nPlayer].nAmmo[i] = 0; + } + + PlayerList[nPlayer].nCurrentWeapon = 0; + PlayerList[nPlayer].field_3A = 0; + PlayerList[nPlayer].field_3FOUR = 0; + + nPlayerGrenade[nPlayer] = -1; + nPlayerWeapons[nPlayer] = 0x1; // turn on bit 1 only +} + +void InitWeapons() +{ + memset(nPlayerGrenade, 0, sizeof(nPlayerGrenade)); + memset(nGrenadePlayer, 0, sizeof(nGrenadePlayer)); +} + +void SetNewWeapon(short nPlayer, short nWeapon) +{ + if (nWeapon == kWeaponMummified) + { + PlayerList[nPlayer].field_3C = PlayerList[nPlayer].nCurrentWeapon; + PlayerList[nPlayer].bIsFiring = kFalse; + PlayerList[nPlayer].field_3A = 5; + SetPlayerMummified(nPlayer, kTrue); + + PlayerList[nPlayer].field_3FOUR = 0; + } + else + { + if (nWeapon < 0) + { + nPlayerOldWeapon[nPlayer] = PlayerList[nPlayer].nCurrentWeapon; + } + else if (nWeapon != kWeaponGrenade || PlayerList[nPlayer].nAmmo[kWeaponGrenade] > 0) + { + short nCurrentWeapon = PlayerList[nPlayer].nCurrentWeapon; + + if (nCurrentWeapon != kWeaponMummified) + { + if (PlayerList[nPlayer].bIsFiring || nWeapon == nCurrentWeapon) { + return; + } + } + else + { + PlayerList[nPlayer].nCurrentWeapon = nWeapon; + PlayerList[nPlayer].field_3FOUR = 0; + } + } + else { + return; + } + } + + PlayerList[nPlayer].field_38 = nWeapon; + + if (nPlayer == nLocalPlayer) + { + int nCounter; + + if (nWeapon >= kWeaponSword && nWeapon <= kWeaponRing) { + nCounter = PlayerList[nPlayer].nAmmo[nWeapon]; + } + else { + nCounter = 0; + } + + SetCounterImmediate(nCounter); + } +} + +void SetNewWeaponImmediate(short nPlayer, short nWeapon) +{ + SetNewWeapon(nPlayer, nWeapon); + + PlayerList[nPlayer].nCurrentWeapon = nWeapon; + PlayerList[nPlayer].field_38 = -1; + PlayerList[nPlayer].field_3FOUR = 0; + PlayerList[nPlayer].field_3A = 0; +} + +void SetNewWeaponIfBetter(short nPlayer, short nWeapon) +{ + if (nWeapon > PlayerList[nPlayer].nCurrentWeapon) { + SetNewWeapon(nPlayer, nWeapon); + } +} + +void SelectNewWeapon(short nPlayer) +{ + int nWeapon = kWeaponRing; // start at the highest weapon number + + uint16_t di = nPlayerWeapons[nPlayer]; + uint16_t dx = 0x40; // bit 7 turned on + + while (dx) + { + if (di & dx) + { + // we have this weapon + if (!WeaponInfo[nWeapon].d || PlayerList[nPlayer].nAmmo[WeaponInfo[nWeapon].nAmmoType]) + break; + } + + nWeapon--; + dx >>= 1; + } + + if (nWeapon < 0) + nWeapon = kWeaponSword; + + PlayerList[nPlayer].bIsFiring = kFalse; + + SetNewWeapon(nPlayer, nWeapon); +} + +void StopFiringWeapon(short nPlayer) +{ + PlayerList[nPlayer].bIsFiring = kFalse; +} + +void FireWeapon(short nPlayer) +{ + if (!PlayerList[nPlayer].bIsFiring) { + PlayerList[nPlayer].bIsFiring = kTrue; + } +} + +void SetWeaponStatus(short nPlayer) +{ + if (nPlayer != nLocalPlayer) + return; + + short nWeapon = PlayerList[nPlayer].nCurrentWeapon; + + if (nWeapon < 0) + { + nCounterBullet = -1; + SetCounterImmediate(0); + } + else + { + nCounterBullet = WeaponInfo[nWeapon].nAmmoType; + SetCounterImmediate(PlayerList[nPlayer].nAmmo[nCounterBullet]); + } +} + +uint8_t WeaponCanFire(short nPlayer) +{ + short nWeapon = PlayerList[nPlayer].nCurrentWeapon; + short nSector = nPlayerViewSect[nPlayer]; + + if (!(SectFlag[nSector] & kSectUnderwater) || WeaponInfo[nWeapon].bFireUnderwater) + { + short nAmmoType = WeaponInfo[nWeapon].nAmmoType; + + if (WeaponInfo[nWeapon].d <= PlayerList[nPlayer].nAmmo[nAmmoType]) { + return kTrue; + } + } + + return kFalse; +} + +// UNUSED +void ResetSwordSeqs() +{ + WeaponInfo[kWeaponSword].b[2] = 3; + WeaponInfo[kWeaponSword].b[3] = 7; +} + +int CheckCloseRange(short nPlayer, int *x, int *y, int *z, short *nSector) +{ + short hitSect, hitWall, hitSprite; + int hitX, hitY, hitZ; + + short nSprite = PlayerList[nPlayer].nSprite; + + int xVect = Sin(sprite[nSprite].ang + 512); + int yVect = Sin(sprite[nSprite].ang); + + vec3_t startPos = { *x, *y, *z }; + hitdata_t hitData; + hitscan(&startPos, *nSector, xVect, yVect, 0, &hitData, CLIPMASK1); + hitX = hitData.pos.x; + hitY = hitData.pos.y; + hitZ = hitData.pos.z; + hitSprite = hitData.sprite; + hitSect = hitData.sect; + hitWall = hitData.wall; + + int ecx = sintable[150] >> 3; + + if (ksqrt((hitX - *x) * (hitX - *x) + (hitY - *y) * (hitY - *y)) >= ecx) + return 0; + + *x = hitX; + *y = hitY; + *z = hitZ; + *nSector = hitSect; + + if (hitSprite > -1) { + return hitSprite | 0xC000; + } + if (hitWall > -1) { + return hitWall | 0x8000; + } + + return 0; +} + +void CheckClip(short nPlayer) +{ + if (nPlayerClip[nPlayer] <= 0) + { + nPlayerClip[nPlayer] = PlayerList[nPlayer].nAmmo[kWeaponM60]; + + if (nPlayerClip[nPlayer] > 99) { + nPlayerClip[nPlayer] = 99; + } + } +} + +void MoveWeapons(short nPlayer) +{ + static int dword_96E22 = 0; + + short nSectFlag = SectFlag[nPlayerViewSect[nPlayer]]; + + if ((nSectFlag & kSectUnderwater) && (totalmoves & 1)) { + return; + } + + nPilotLightFrame++; + + if (nPilotLightFrame >= nPilotLightCount) + nPilotLightFrame = 0; + + if (!PlayerList[nPlayer].bIsFiring || (nSectFlag & kSectUnderwater)) + nTemperature[nPlayer] = 0; + + short nPlayerSprite = PlayerList[nPlayer].nSprite; + short nWeapon = PlayerList[nPlayer].nCurrentWeapon; + + if (nWeapon < -1) + { + if (PlayerList[nPlayer].field_38 != -1) + { + PlayerList[nPlayer].nCurrentWeapon = PlayerList[nPlayer].field_38; + PlayerList[nPlayer].field_3A = 0; + PlayerList[nPlayer].field_3FOUR = 0; + PlayerList[nPlayer].field_38 = -1; + } + + return; + } + + // loc_26ACC + short eax = PlayerList[nPlayer].field_3A; + short nSeq = WeaponInfo[nWeapon].nSeq; + + short var_3C = WeaponInfo[nWeapon].b[eax] + SeqOffsets[nSeq]; + + int var_1C = (nPlayerDouble[nPlayer] > 0) + 1; + + frames = var_1C - 1; + + for (frames = var_1C; frames > 0; frames--) + { + seq_MoveSequence(nPlayerSprite, var_3C, PlayerList[nPlayer].field_3FOUR); + + PlayerList[nPlayer].field_3FOUR++; + + dword_96E22++; + if (dword_96E22 >= 15) { + dword_96E22 = 0; + } + + if (PlayerList[nPlayer].field_3FOUR >= SeqSize[var_3C]) + { + if (PlayerList[nPlayer].field_38 == -1) + { + switch (PlayerList[nPlayer].field_3A) + { + default: + break; + + case 0: + { + PlayerList[nPlayer].field_3A = 1; + SetWeaponStatus(nPlayer); + break; + } + case 1: + { + if (PlayerList[nPlayer].bIsFiring) + { + if (!WeaponCanFire(nPlayer)) + { + if (!dword_96E22) { + D3PlayFX(StaticSound[4], PlayerList[nPlayer].nSprite); + } + } + else + { + if (nWeapon == kWeaponRing) + { + if (Ra[nPlayer].nTarget == -1) + break; + + Ra[nPlayer].field_0 = 0; + Ra[nPlayer].field_2 = 0; + Ra[nPlayer].field_C = 1; + } + + PlayerList[nPlayer].field_3A = 2; + + if (nWeapon == 0) + break; + + if (nWeapon == kWeaponGrenade) + { + BuildGrenade(nPlayer); + AddAmmo(nPlayer, 4, -1); + } + else if (nWeapon == kWeaponStaff) + { + ShootStaff(nPlayer); + } + } + } + break; + } + + case 2: + case 6: + case 7: + case 8: + { + if (nWeapon == kWeaponPistol && nPistolClip[nPlayer] <= 0) + { + PlayerList[nPlayer].field_3A = 3; + PlayerList[nPlayer].field_3FOUR = 0; + + nPistolClip[nPlayer] = Min(6, PlayerList[nPlayer].nAmmo[kWeaponPistol]); + break; + } + else if (nWeapon == kWeaponGrenade) + { + if (!PlayerList[nPlayer].bIsFiring) + { + PlayerList[nPlayer].field_3A = 3; + break; + } + else + { + PlayerList[nPlayer].field_3FOUR = SeqSize[var_3C] - 1; + continue; + } + } + else if (nWeapon == kWeaponMummified) + { + PlayerList[nPlayer].field_3A = 0; + PlayerList[nPlayer].nCurrentWeapon = PlayerList[nPlayer].field_3C; + + nWeapon = PlayerList[nPlayer].nCurrentWeapon; + + SetPlayerMummified(nPlayer, kFalse); + break; + } + else + { + // loc_26D88: + if (PlayerList[nPlayer].bIsFiring && WeaponCanFire(nPlayer)) + { + if (nWeapon != kWeaponM60 && nWeapon != kWeaponPistol) { + PlayerList[nPlayer].field_3A = 3; + } + } + else + { + if (WeaponInfo[nWeapon].b[4] == -1) + { + PlayerList[nPlayer].field_3A = 1; + } + else + { + if (nWeapon == kWeaponFlamer && (nSectFlag & kSectUnderwater)) + { + PlayerList[nPlayer].field_3A = 1; + } + else + { + PlayerList[nPlayer].field_3A = 4; + } + } + } + + break; + } + } + + case 3: + case 9: + case 10: + case 11: + { + if (nWeapon == kWeaponMummified) + { + PlayerList[nPlayer].nCurrentWeapon = PlayerList[nPlayer].field_3C; + + nWeapon = PlayerList[nPlayer].nCurrentWeapon; + + PlayerList[nPlayer].field_3A = 0; + break; + } + else if (nWeapon == kWeaponRing) + { + if (!WeaponInfo[nWeapon].d || PlayerList[nPlayer].nAmmo[WeaponInfo[nWeapon].nAmmoType]) + { + if (!PlayerList[nPlayer].bIsFiring) { + PlayerList[nPlayer].field_3A = 1; + } + else { + break; + } + } + else + { + SelectNewWeapon(nPlayer); + } + + Ra[nPlayer].field_C = 0; + break; + } + else if (nWeapon == kWeaponM60) + { + CheckClip(nPlayer); + PlayerList[nPlayer].field_3A = 1; + break; + } + else if (nWeapon == kWeaponGrenade) + { + if (!WeaponInfo[nWeapon].d || PlayerList[nPlayer].nAmmo[WeaponInfo[nWeapon].nAmmoType]) + { + PlayerList[nPlayer].field_3A = 0; + break; + } + else + { + SelectNewWeapon(nPlayer); + PlayerList[nPlayer].field_3A = 5; + + PlayerList[nPlayer].field_3FOUR = SeqSize[WeaponInfo[kWeaponGrenade].b[0] + SeqOffsets[nSeq]] - 1; // CHECKME + goto loc_flag; // FIXME + } + } + else + { + if (PlayerList[nPlayer].bIsFiring && WeaponCanFire(nPlayer)) { + PlayerList[nPlayer].field_3A = 2; + break; + } + + if (WeaponInfo[nWeapon].b[4] == -1) + { + PlayerList[nPlayer].field_3A = 1; + break; + } + + if (nWeapon == kWeaponFlamer && (nSectFlag & kSectUnderwater)) + { + PlayerList[nPlayer].field_3A = 1; + } + else + { + PlayerList[nPlayer].field_3A = 4; + } + } + break; + } + + case 4: + { + PlayerList[nPlayer].field_3A = 1; + break; + } + + case 5: + { + PlayerList[nPlayer].nCurrentWeapon = PlayerList[nPlayer].field_38; + + nWeapon = PlayerList[nPlayer].nCurrentWeapon; + + PlayerList[nPlayer].field_3A = 0; + PlayerList[nPlayer].field_38 = -1; + + SetWeaponStatus(nPlayer); + break; + } + } + + // loc_26FC5 + var_3C = SeqOffsets[WeaponInfo[nWeapon].nSeq] + WeaponInfo[nWeapon].b[PlayerList[nPlayer].field_3A]; + PlayerList[nPlayer].field_3FOUR = 0; + } + else + { + if (PlayerList[nPlayer].field_3A == 5) + { + PlayerList[nPlayer].nCurrentWeapon = PlayerList[nPlayer].field_38; + + nWeapon = PlayerList[nPlayer].nCurrentWeapon; + + PlayerList[nPlayer].field_38 = -1; + PlayerList[nPlayer].field_3A = 0; + } + else + { + PlayerList[nPlayer].field_3A = 5; + } + + PlayerList[nPlayer].field_3FOUR = 0; + continue; + } + } // end of if (PlayerList[nPlayer].field_34 >= SeqSize[var_3C]) + +loc_flag: + + // loc_27001 + short nFrameFlag = seq_GetFrameFlag(var_3C, PlayerList[nPlayer].field_3FOUR); + + if (((!(nSectFlag & kSectUnderwater)) || nWeapon == kWeaponRing) && (nFrameFlag & 4)) + { + BuildFlash(nPlayer, sprite[nPlayerSprite].sectnum, 512); + AddFlash( + sprite[nPlayerSprite].sectnum, + sprite[nPlayerSprite].x, + sprite[nPlayerSprite].y, + sprite[nPlayerSprite].z, + 0); + } + + if (nFrameFlag & 0x80) + { + int nAction = PlayerList[nPlayer].nAction; + + int var_38 = 1; + + if (nAction < 10 || nAction > 12) { + var_38 = 0; + } + + if (nPlayer == nLocalPlayer) { + obobangle = bobangle = 512; + } + + if (nWeapon == kWeaponFlamer && (!(nSectFlag & kSectUnderwater))) + { + nTemperature[nPlayer]++; + + if (nTemperature[nPlayer] > 50) + { + nTemperature[nPlayer] = 0; + PlayerList[nPlayer].field_3A = 4; + PlayerList[nPlayer].field_3FOUR = 0; + } + } + + short nAmmoType = WeaponInfo[nWeapon].nAmmoType; + short nAngle = sprite[nPlayerSprite].ang; + int theX = sprite[nPlayerSprite].x; + int theY = sprite[nPlayerSprite].y; + int theZ = sprite[nPlayerSprite].z; + + int ebp = Sin(nAngle + 512) * (sprite[nPlayerSprite].clipdist << 3); + int ebx = Sin(nAngle) * (sprite[nPlayerSprite].clipdist << 3); + + if (WeaponInfo[nWeapon].c) + { + int ecx; + + int theVal = (totalmoves + 101) & (WeaponInfo[nWeapon].c - 1); + if (theVal & 1) + ecx = -theVal; + else + ecx = theVal; + + int var_44 = (nAngle + 512) & kAngleMask; + ebp += ((Sin(var_44 + 512) >> 11) * ecx); + ebx += (Sin(var_44) >> 11) * ecx; + } + + int nHeight = (-GetSpriteHeight(nPlayerSprite)) >> 1; + + if (nAction < 6) + { + nHeight -= 1792; + } + else + { + if (!var_38) + { + nHeight += 1024; + } + else { + nHeight -= 2560; + } + } + + short nSectorB = sprite[nPlayerSprite].sectnum; + + switch (nWeapon) + { + // loc_27266: + case kWeaponSword: + { + nHeight += (92 - fix16_to_int(sPlayerInput[nPlayer].horizon)) << 6; + + theZ += nHeight; + + int var_28; + + if (PlayerList[nPlayer].field_3A == 2) { + var_28 = 6; + } + else { + var_28 = 9; + } + + int cRange = CheckCloseRange(nPlayer, &theX, &theY, &theZ, &nSectorB); + + if (cRange) + { + short nDamage = BulletInfo[kWeaponSword].nDamage; + + if (nPlayerDouble[nPlayer]) { + nDamage *= 2; + } + + if ((cRange & 0xC000) >= 0x8000) + { + if ((cRange & 0xC000) == 0x8000) // hit wall + { + // loc_2730E: + var_28 += 2; + } + else if ((cRange & 0xC000) == 0xC000) // hit sprite + { + short nSprite2 = cRange & 0x3FFF; + + if (sprite[nSprite2].cstat & 0x50) + { + var_28 += 2; + } + else if (sprite[nSprite2].statnum > 90 && sprite[nSprite2].statnum <= 199) + { + runlist_DamageEnemy(nSprite2, nPlayerSprite, nDamage); + + if (sprite[nSprite2].statnum < 102) { + var_28++; + } + else if (sprite[nSprite2].statnum == 102) + { + // loc_27370: + BuildAnim(-1, 12, 0, theX, theY, theZ, nSectorB, 30, 0); + } + else if (sprite[nSprite2].statnum == kStatExplodeTrigger) { + var_28 += 2; + } + else { + var_28++; + } + } + else + { + // loc_27370: + BuildAnim(-1, 12, 0, theX, theY, theZ, nSectorB, 30, 0); + } + } + } + } + + // loc_27399: + PlayerList[nPlayer].field_3A = var_28; + PlayerList[nPlayer].field_3FOUR = 0; + break; + } + case kWeaponFlamer: + { + if (nSectFlag & kSectUnderwater) + { + DoBubbles(nPlayer); + PlayerList[nPlayer].field_3A = 1; + PlayerList[nPlayer].field_3FOUR = 0; + StopSpriteSound(nPlayerSprite); + break; + } + else + { + if (var_38) { + nHeight += 768; + } + else { + nHeight -= 2560; + } + + // fall through to case 1 (kWeaponPistol) + fallthrough__; + } + } + + case kWeaponM60: + { + if (nWeapon == kWeaponM60) { // hack(?) to do fallthrough from kWeapon3 into kWeaponPistol without doing the nQuake[] change + nQuake[nPlayer] = 128; + } + // fall through + fallthrough__; + } + case kWeaponPistol: + { + int var_50 = (fix16_to_int(sPlayerInput[nPlayer].horizon) - 92) << 2; + nHeight -= var_50; + + if (sPlayerInput[nPlayer].nTarget >= 0) + { + assert(sprite[sPlayerInput[nPlayer].nTarget].sectnum < kMaxSectors); + var_50 = sPlayerInput[nPlayer].nTarget + 10000; + } + + BuildBullet(nPlayerSprite, nAmmoType, ebp, ebx, nHeight, nAngle, var_50, var_1C); + break; + } + + case kWeaponGrenade: + { + ThrowGrenade(nPlayer, ebp, ebx, nHeight - 2560, fix16_to_int(sPlayerInput[nPlayer].horizon) - 92); + break; + } + case kWeaponStaff: + { + BuildSnake(nPlayer, nHeight); + nQuake[nPlayer] = 512; + + nXDamage[nPlayer] -= Sin(sprite[nPlayerSprite].ang + 512) << 9; + nYDamage[nPlayer] -= Sin(sprite[nPlayerSprite].ang) << 9; + break; + } + case kWeaponRing: + break; + + case kWeaponMummified: + { + short nDamage = BulletInfo[kWeaponMummified].nDamage; + if (nPlayerDouble[nPlayer]) { + nDamage *= 2; + } + + runlist_RadialDamageEnemy(nPlayerSprite, nDamage, BulletInfo[kWeaponMummified].field_10); + break; + } + } + + // end of switch, loc_2753E: + if (nWeapon < kWeaponMummified) + { + if (nWeapon != kWeaponGrenade) + { + short nAmmo = -WeaponInfo[nWeapon].d; // negative + + if (nAmmo) { + AddAmmo(nPlayer, nAmmoType, nAmmo); + } + + if (nWeapon == kWeaponM60) { + nPlayerClip[nPlayer] -= WeaponInfo[nWeapon].d; + } + else if (nWeapon == kWeaponPistol) { + nPistolClip[nPlayer]--; + } + } + + if (!WeaponInfo[nWeapon].d || PlayerList[nPlayer].nAmmo[WeaponInfo[nWeapon].nAmmoType]) + { + if (nWeapon == kWeaponM60 && nPlayerClip[nPlayer] <= 0) + { + PlayerList[nPlayer].field_3A = 3; + PlayerList[nPlayer].field_3FOUR = 0; + // goto loc_27609: + } + } + else if (nWeapon != kWeaponGrenade) + { + SelectNewWeapon(nPlayer); + // go to loc_27609: + } + } + } + } +} + +void DrawWeapons(int smooth) +{ + if (bCamera) { + return; + } + + short nWeapon = PlayerList[nLocalPlayer].nCurrentWeapon; + if (nWeapon < -1) { + return; + } + + short var_34 = PlayerList[nLocalPlayer].field_3A; + + short var_30 = SeqOffsets[WeaponInfo[nWeapon].nSeq]; + + short var_28 = var_30 + WeaponInfo[nWeapon].b[var_34]; + + int8_t nShade = sector[initsect].ceilingshade; + + int nDouble = nPlayerDouble[nLocalPlayer]; + int nPal = kPalNormal; + + if (nDouble) + { + if (word_96E26) { + nPal = kPalRedBrite; + } + + word_96E26 = word_96E26 == 0; + } + + nPal = RemapPLU(nPal); + + int nVal = totalvel[nLocalPlayer] >> 1; + + // CHECKME - not & 0x7FF? + int nBobAngle = angle_interpolate16(obobangle, bobangle, smooth); + int yOffset = (nVal * (sintable[nBobAngle & 0x3FF] >> 8)) >> 9; + + int xOffset = 0; + + if (var_34 == 1) + { + xOffset = ((Sin(nBobAngle + 512) >> 8) * nVal) >> 8; + } + else + { + xOffset = 0; + obobangle = bobangle = 512; + } + + if (nWeapon == 3 && var_34 == 1) { + seq_DrawPilotLightSeq(xOffset, yOffset); + } + + if (nWeapon < 0) { + nShade = sprite[PlayerList[nLocalPlayer].nSprite].shade; + } + + seq_DrawGunSequence(var_28, PlayerList[nLocalPlayer].field_3FOUR, xOffset, yOffset, nShade, nPal); + + if (nWeapon != kWeaponM60) + return; + + switch (var_34) + { + default: + return; + + case 0: + { + int nClip = nPlayerClip[nLocalPlayer]; + + if (nClip <= 0) + return; + + int nSeqOffset; + + if (nClip <= 3) + { + nSeqOffset = var_30 + 1; + } + else if (nClip <= 6) + { + nSeqOffset = var_30 + 2; + } + else if (nClip <= 25) + { + nSeqOffset = var_30 + 3; + } + else if (nClip > 25) + { + nSeqOffset = var_30 + 4; + } + + seq_DrawGunSequence(nSeqOffset, PlayerList[nLocalPlayer].field_3FOUR, xOffset, yOffset, nShade, nPal); + return; + } + case 1: + { + int nClip = nPlayerClip[nLocalPlayer]; + + short edx = (nClip % 3) * 4; + + if (nClip <= 0) { + return; + } + + seq_DrawGunSequence(var_30 + 8, edx, xOffset, yOffset, nShade, nPal); + + if (nClip <= 3) { + return; + } + + seq_DrawGunSequence(var_30 + 9, edx, xOffset, yOffset, nShade, nPal); + + if (nClip <= 6) { + return; + } + + seq_DrawGunSequence(var_30 + 10, edx, xOffset, yOffset, nShade, nPal); + + if (nClip <= 25) { + return; + } + + seq_DrawGunSequence(var_30 + 11, edx, xOffset, yOffset, nShade, nPal); + return; + } + case 2: + { + int nClip = nPlayerClip[nLocalPlayer]; + + short dx = PlayerList[nLocalPlayer].field_3FOUR; + + if (nClip <= 0) { + return; + } + + seq_DrawGunSequence(var_30 + 8, dx, xOffset, yOffset, nShade, nPal); + + if (nClip <= 3) { + return; + } + + seq_DrawGunSequence(var_30 + 9, dx, xOffset, yOffset, nShade, nPal); + + if (nClip <= 6) { + return; + } + + seq_DrawGunSequence(var_30 + 10, dx, xOffset, yOffset, nShade, nPal); + + if (nClip <= 25) { + return; + } + + seq_DrawGunSequence(var_30 + 11, dx, xOffset, yOffset, nShade, nPal); + return; + } + + case 3: + case 4: + return; + + case 5: + { + int nClip = nPlayerClip[nLocalPlayer]; + + short ax = PlayerList[nLocalPlayer].field_3FOUR; + + if (nClip <= 0) { + return; + } + + int nSeqOffset; + + if (nClip <= 3) + { + nSeqOffset = var_30 + 20; + } + else if (nClip <= 6) + { + nSeqOffset = var_30 + 19; + } + else if (nClip <= 25) + { + nSeqOffset = var_30 + 18; + } + else + { + nSeqOffset = var_30 + 17; + } + + seq_DrawGunSequence(nSeqOffset, ax, xOffset, yOffset, nShade, nPal); + return; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/gun.h b/source/exhumed/src/gun.h new file mode 100644 index 000000000..4058ae687 --- /dev/null +++ b/source/exhumed/src/gun.h @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __gun_h__ +#define __gun_h__ + +#include "compat.h" +#include "sequence.h" + +BEGIN_PS_NS + +#define kMaxWeapons 7 + +enum +{ + kWeaponSword = 0, + kWeaponPistol, + kWeaponM60, + kWeaponFlamer, + kWeaponGrenade, + kWeaponStaff, + kWeaponRing, + kWeaponMummified +}; + +struct Weapon +{ + short nSeq; + short b[12]; // seq offsets? + short nAmmoType; + short c; + short d; // default or min ammo? or ammo used per 'shot' ? + short bFireUnderwater; +// short pad[15]; +}; + +extern Weapon WeaponInfo[]; +extern short nTemperature[]; + +void RestoreMinAmmo(short nPlayer); +void FillWeapons(short nPlayer); +void ResetPlayerWeapons(short nPlayer); +void InitWeapons(); +void SetNewWeapon(short nPlayer, short nWeapon); +void SetNewWeaponImmediate(short nPlayer, short nWeapon); +void SetNewWeaponIfBetter(short nPlayer, short nWeapon); +void SelectNewWeapon(short nPlayer); +void StopFiringWeapon(short nPlayer); +void FireWeapon(short nPlayer); +void CheckClip(short nPlayer); +void MoveWeapons(short nPlayer); +void DrawWeapons(int smooth); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/init.cpp b/source/exhumed/src/init.cpp new file mode 100644 index 000000000..78ec790f7 --- /dev/null +++ b/source/exhumed/src/init.cpp @@ -0,0 +1,952 @@ +//------------------------------------------------------------------------- +/* +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 "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 "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 "mapinfo.h" +#include "gamecontrol.h" +#include "rendering/v_video.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; + + + + +uint8_t LoadLevel(int nMap) +{ + initspritelists(); + + currentLevel = &mapList[nMap]; + + // 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(currentLevel->fileName, 0, &startPos, &inita, &initsect); + if (status == -2) + status = engineLoadBoardV5V6(currentLevel->fileName, 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()) + { + G_FatalEngineError(); + } + + LoadPaletteLookups(); + V_Init2(); + + 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() +{ + + return 1; +} + +int mysaveconfig() +{ + return 1; +} + +static SavegameHelper sgh("init", + SV(initx), + SV(inity), + SV(initz), + SV(inita), + SV(initsect), + SV(nCurChunkNum), + SA(nBodyGunSprite), + SV(nCurBodyGunNum), + SA(SectSoundSect), + SA(SectSound), + SA(SectFlag), + SA(SectDepth), + SA(SectAbove), + SA(SectDamage), + SA(SectSpeed), + SA(SectBelow), + nullptr); + + +END_PS_NS diff --git a/source/exhumed/src/init.h b/source/exhumed/src/init.h new file mode 100644 index 000000000..33b4847fb --- /dev/null +++ b/source/exhumed/src/init.h @@ -0,0 +1,69 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __init_h__ +#define __init_h__ + +#include "compat.h" + +BEGIN_PS_NS + +#define kMap20 20 + +enum { + kSectUnderwater = 0x2000, + kSectLava = 0x4000, +}; + +extern ClockTicks ototalclock; + +extern int initx; +extern int inity; +extern int initz; +extern short inita; +extern short initsect; + +extern short nCurChunkNum; +extern short nBodyGunSprite[50]; +extern int movefifoend; +extern int movefifopos; +extern short nCurBodyGunNum; + +void SnapSectors(short nSectorA, short nSectorB, short b); + +extern short SectSound[]; +extern short SectDamage[]; +extern short SectSpeed[]; +extern int SectBelow[]; +extern short SectFlag[]; +extern int SectDepth[]; +extern short SectSoundSect[]; +extern int SectAbove[]; + +uint8_t LoadLevel(int nMap); +void InstallEngine(); +void ResetEngine(); +void RemoveEngine(); +void LoadObjects(); + +int myloadconfig(); +int mysaveconfig(); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/input.cpp b/source/exhumed/src/input.cpp new file mode 100644 index 000000000..a499ecbad --- /dev/null +++ b/source/exhumed/src/input.cpp @@ -0,0 +1,218 @@ +//------------------------------------------------------------------------- +/* +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 "ps_input.h" +#include "engine.h" +#include "exhumed.h" +#include "player.h" +#include "serial.h" +#include "network.h" +#include + +BEGIN_PS_NS + +int nNetMoves = 0; + +short nInputStack = 0; + +short bStackNode[kMaxPlayers]; + +short nTypeStack[kMaxPlayers]; +PlayerInput sPlayerInput[kMaxPlayers]; + +int *pStackPtr; + +// (nInputStack * 32) - 11; + +void PushInput(PlayerInput *pInput, int edx) +{ + if (!bStackNode[edx]) + { +// memcpy(sInputStack[nInputStack], pInput, + } +} + +int PopInput() +{ + if (!nInputStack) + return -1; + + nInputStack--; + + // TEMP + return 0; +} + +void InitInput() +{ + memset(nTypeStack, 0, sizeof(nTypeStack)); + nInputStack = 0; + memset(bStackNode, 0, sizeof(bStackNode)); + +// pStackPtr = &sInputStack; +} + +void ClearSpaceBar(short nPlayer) +{ + sPlayerInput[nPlayer].buttons &= 0x0FB; + buttonMap.ClearButton(gamefunc_Open); +} + +void GetLocalInput() +{ + int i; + for (i = 6; i >= 0; i--) + { + if (buttonMap.ButtonDown(gamefunc_Weapon_1+i)) + break; + } + i++; + + if (PlayerList[nLocalPlayer].nHealth) + { + lLocalButtons = (buttonMap.ButtonDown(gamefunc_Crouch) << 4) | (buttonMap.ButtonDown(gamefunc_Fire) << 3) + | (buttonMap.ButtonDown(gamefunc_Jump)<<0) | (i<<13); + } + else + { + lLocalButtons = 0; + } + + lLocalButtons |= buttonMap.ButtonDown(gamefunc_Open) << 2; + +// TODO ExecRecord(&sPlayerInput[nLocalPlayer], sizeof(PlayerInput)); +} + +void BackupInput() +{ + +} + +void SendInput() +{ + +} + +void LogoffPlayer(int nPlayer) +{ + if (nPlayer == nLocalPlayer) + return; + + if (PlayerList[nPlayer].someNetVal == -1) + return; + + memset(&sPlayerInput[nPlayer], 0, sizeof(sPlayerInput)); + + sprite[nDoppleSprite[nPlayer]].cstat = 0x8000u; + sprite[nPlayerFloorSprite[nPlayer]].cstat = 0x8000u; + sprite[PlayerList[nPlayer].nSprite].cstat = 0x8000u; + + PlayerList[nPlayer].someNetVal = -1; + + StatusMessage(150, "Player %d has left the game", nPlayer); + +// TODO ClearPlayerInput(&sPlayerInput[nPlayer]); + nNetPlayerCount--; +} + +void UpdateInputs() +{ + nNetMoveFrames = moveframes; + + if (nNetPlayerCount) + { + if (bSerialPlay) { + UpdateSerialInputs(); + } + else { + UpdateNetInputs(); + } + + nNetMoves++; + + if (!nNetMoves) { + nNetMoves++; + } + } +} + +/* +ClearSpaceBar_ + GetLocalInput_ + GetModemInput_ + BackupInput_ + SendInput_ + SendToUnAckd_ + LogoffPlayer_ + UpdateInputs_ + faketimerhandler_ +*/ + +void ClearAllKeys() +{ + inputState.ClearAllKeyStatus(); + inputState.keyFlushChars(); +} + +void WaitNoKey(int nSecs, void (*pFunc) (void)) +{ + int nTotalTime = (kTimerTicks * nSecs) + (int)totalclock; + + while (nTotalTime > (int)totalclock) + { + HandleAsync(); + if (pFunc) { + pFunc(); + } + } +} + +int WaitAnyKey(int nSecs) +{ + int nTotalTime = (int)totalclock + (kTimerTicks * nSecs); + + while (1) + { + HandleAsync(); + if (nTotalTime <= (int)totalclock || nSecs == -1) { + return -1; + } + if (inputState.CheckAllInput()) return 1; + } +} + + +/* + Name: _nLocalPlayer + Name: _nNetPlayerCount + Name: _nModemPlayer + Name: _nNetMoves + Name: _lLocalButtons + Name: _lLocalCodes + Name: _nInputStack + Name: _bSyncNet + Name: _lStartupTime + Name: _bStackNode + Name: _sSync + Name: _nTypeStack + Name: _sPlayerInput + Name: _pStackPtr + Name: _sInputStack + + */ + END_PS_NS diff --git a/source/exhumed/src/items.cpp b/source/exhumed/src/items.cpp new file mode 100644 index 000000000..10bf9615e --- /dev/null +++ b/source/exhumed/src/items.cpp @@ -0,0 +1,506 @@ +//------------------------------------------------------------------------- +/* +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 "items.h" +#include "anims.h" +#include "player.h" +#include "exhumed.h" +#include "lighting.h" +#include "sound.h" +#include "status.h" +#include "engine.h" +#include "random.h" +#include "init.h" +#include "ps_input.h" +#include "object.h" + +BEGIN_PS_NS + +struct AnimInfo +{ + short a; + short repeat; +}; + +AnimInfo nItemAnimInfo[] = { + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { 6, 64 }, + { -1, 48 }, + { 0, 64 }, + { 1, 64 }, + { -1, 32 }, + { 4, 64 }, + { 5, 64 }, + { 16, 64 }, + { 10, 64 }, + { -1, 32 }, + { 8, 64 }, + { 9, 64 }, + { -1, 40 }, + { -1, 32 }, + { 7, 64 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { 14, 64 }, + { 15, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { 17, 48 }, + { 18, 48 }, + { 19, 48 }, + { 20, 48 }, + { 24, 64 }, + { 21, 64 }, + { 23, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { 11, 30 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 }, + { -1, 32 } +}; + +short nItemMagic[] = { 500, 1000, 100, 500, 400, 200, 700, 0 }; + +/* + +short something +short x/y repeat + +*/ + +short nRegenerates; +short nFirstRegenerate; +short nMagicCount; + +static SavegameHelper sgh("items", + SV(nRegenerates), + SV(nFirstRegenerate), + SV(nMagicCount), + nullptr); + + +void BuildItemAnim(short nSprite) +{ + int nItem = sprite[nSprite].statnum - 906; + + if (nItemAnimInfo[nItem].a >= 0) + { + int nAnim = BuildAnim(nSprite, 41, nItemAnimInfo[nItem].a, sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, sprite[nSprite].sectnum, nItemAnimInfo[nItem].repeat, 20); + int nAnimSprite = GetAnimSprite(nAnim); + + if (nItem == 44) { + sprite[nAnimSprite].cstat |= 2; + } + + changespritestat(nAnimSprite, sprite[nSprite].statnum); + + sprite[nAnimSprite].owner = nAnim; + sprite[nAnimSprite].hitag = sprite[nSprite].hitag; + } + else + { + sprite[nSprite].owner = -1; + sprite[nSprite].yrepeat = nItemAnimInfo[nItem].repeat; + sprite[nSprite].xrepeat = nItemAnimInfo[nItem].repeat; + } +} + +void DestroyItemAnim(short nSprite) +{ + short nAnim = sprite[nSprite].owner; + + if (nAnim >= 0) { + DestroyAnim(nAnim); + } +} + +void ItemFlash() +{ + TintPalette(16, 16, 16); +} + +void FillItems(short nPlayer) +{ + for (int i = 0; i < 6; i++) + { + PlayerList[nPlayer].items[i] = 5; + } + + PlayerList[nPlayer].nMagic = 1000; + + if (nPlayer == nLocalPlayer) + { + ItemFlash(); + SetMagicFrame(); + } + + if (nPlayerItem[nPlayer] == -1) { + SetPlayerItem(nPlayer, 0); + } +} + +void UseEye(short nPlayer) +{ + if (nPlayerInvisible[nPlayer] >= 0) { + nPlayerInvisible[nPlayer] = 900; + } + + int nSprite = PlayerList[nPlayer].nSprite; + + sprite[nSprite].cstat |= 0x8000; + + if (nPlayerFloorSprite[nPlayer] >= 0) { + sprite[nSprite].cstat |= 0x8000; + } + + if (nPlayer == nLocalPlayer) + { + ItemFlash(); + D3PlayFX(StaticSound[kSound31], nSprite); + } +} + +void UseMask(short nPlayer) +{ + PlayerList[nPlayer].nMaskAmount = 1350; + PlayerList[nPlayer].nAir = 100; + + if (nPlayer == nLocalPlayer) + { + SetAirFrame(); + D3PlayFX(StaticSound[kSound31], PlayerList[nPlayer].nSprite); + } +} + +void UseTorch(short nPlayer) +{ + if (!nPlayerTorch[nPlayer]) { + SetTorch(nPlayer, 1); + } + + nPlayerTorch[nPlayer] = 900; +} + +void UseHeart(short nPlayer) +{ + if (PlayerList[nPlayer].nHealth < kMaxHealth) { + PlayerList[nPlayer].nHealth = kMaxHealth; + } + + if (nPlayer == nLocalPlayer) + { + ItemFlash(); + SetHealthFrame(1); + D3PlayFX(StaticSound[kSound31], PlayerList[nPlayer].nSprite); + } +} + +// invincibility +void UseScarab(short nPlayer) +{ + if (PlayerList[nPlayer].invincibility < 900) { + PlayerList[nPlayer].invincibility = 900; + } + + if (nPlayer == nLocalPlayer) + { + ItemFlash(); + D3PlayFX(StaticSound[kSound31], PlayerList[nPlayer].nSprite); + } +} + +// faster firing +void UseHand(short nPlayer) +{ + nPlayerDouble[nPlayer] = 1350; + + if (nPlayer == nLocalPlayer) + { + ItemFlash(); + D3PlayFX(StaticSound[kSound31], PlayerList[nPlayer].nSprite); + } +} + +void UseItem(short nPlayer, short nItem) +{ + switch (nItem) + { + case 0: + UseHeart(nPlayer); + break; + case 1: + UseScarab(nPlayer); + break; + case 2: + UseTorch(nPlayer); + break; + case 3: + UseHand(nPlayer); + break; + case 4: + UseEye(nPlayer); + break; + case 5: + UseMask(nPlayer); + break; + default: + break; + } + + PlayerList[nPlayer].items[nItem]--; + int nItemCount = PlayerList[nPlayer].items[nItem]; + + int nMagic = nItemMagic[nItem]; + + if (nPlayer == nLocalPlayer) + { + BuildStatusAnim(156 + (nItemCount * 2), 0); + } + + if (!nItemCount) + { + for (nItem = 0; nItem < 6; nItem++) + { + if (PlayerList[nPlayer].items[nItem] > 0) { + break; + } + } + + if (nItem == 6) { + nItem = -1; + } + } + + PlayerList[nPlayer].nMagic -= nMagic; + SetPlayerItem(nPlayer, nItem); + + if (nPlayer == nLocalPlayer) { + SetMagicFrame(); + } +} + +void UseCurItem(short nPlayer) +{ + int nItem = nPlayerItem[nPlayer]; + + if (nItem >= 0) + { + if (PlayerList[nPlayer].items[nItem] > 0) + { + if (nItemMagic[nItem] <= PlayerList[nPlayer].nMagic) + { + sPlayerInput[nPlayer].nItem = nItem; + } + } + } +} + +// TODO - bool return type? +int GrabItem(short nPlayer, short nItem) +{ + if (PlayerList[nPlayer].items[nItem] >= 5) { + return 0; + } + + PlayerList[nPlayer].items[nItem]++; + + if (nPlayerItem[nPlayer] < 0 || nItem == nPlayerItem[nPlayer]) { + SetPlayerItem(nPlayer, nItem); + } + + return 1; +} + +void DropMagic(short nSprite) +{ + if (lFinaleStart) { + return; + } + + nMagicCount--; + + if (nMagicCount <= 0) + { + int nAnim = BuildAnim( + -1, + 64, + 0, + sprite[nSprite].x, + sprite[nSprite].y, + sprite[nSprite].z, + sprite[nSprite].sectnum, + 48, + 4); + + int nAnimSprite = GetAnimSprite(nAnim); + + sprite[nAnimSprite].owner = nAnim; + + AddFlash(sprite[nAnimSprite].sectnum, sprite[nAnimSprite].x, sprite[nAnimSprite].y, sprite[nAnimSprite].z, 128); + changespritestat(nAnimSprite, 950); + + nMagicCount = RandomSize(2); + } +} + +void InitItems() +{ + nRegenerates = 0; + nFirstRegenerate = -1; + nMagicCount = 0; +} + +void StartRegenerate(short nSprite) +{ + spritetype *pSprite = &sprite[nSprite]; + + int edi = -1; + + int nReg = nFirstRegenerate; + + int i = 0; + +// for (int i = 0; i < nRegenerates; i++) + while (1) + { + if (i >= nRegenerates) + { + // ?? CHECKME + pSprite->xvel = pSprite->xrepeat; + pSprite->zvel = pSprite->shade; + pSprite->yvel = pSprite->pal; + break; + } + else + { + if (nReg != nSprite) + { + edi = nReg; + nReg = sprite[nReg].ang; + i++; + continue; + } + else + { + if (edi == -1) + { + nFirstRegenerate = pSprite->ang; + } + else + { + sprite[edi].ang = sprite[nSprite].ang; + } + + nRegenerates--; + } + } + } + + pSprite->extra = 1350; + pSprite->ang = nFirstRegenerate; + + if (levelnum <= kMap20) + { + pSprite->ang /= 5; + } + + pSprite->cstat = 0x8000; + pSprite->xrepeat = 1; + pSprite->yrepeat = 1; + pSprite->pal = 1; + + nRegenerates++; + nFirstRegenerate = nSprite; +} + +void DoRegenerates() +{ + int nSprite = nFirstRegenerate; + + for (int i = nRegenerates; i > 0; i--, nSprite = sprite[nSprite].ang) + { + if (sprite[nSprite].extra > 0) + { + sprite[nSprite].extra--; + + if (sprite[nSprite].extra <= 0) + { + BuildAnim(-1, 38, 0, sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, sprite[nSprite].sectnum, 64, 4); + D3PlayFX(StaticSound[kSoundTorchOn], i); + } + else { + continue; + } + } + else + { + if (sprite[nSprite].xrepeat < sprite[nSprite].xvel) + { + sprite[nSprite].xrepeat += 2; + sprite[nSprite].yrepeat += 2; + continue; + } + } + + sprite[nSprite].zvel = 0; + sprite[nSprite].yrepeat = sprite[nSprite].xvel; + sprite[nSprite].xrepeat = sprite[nSprite].xvel; + sprite[nSprite].pal = sprite[nSprite].yvel; + sprite[nSprite].yvel = sprite[nSprite].zvel; // setting to 0 + sprite[nSprite].xvel = sprite[nSprite].zvel; // setting to 0 + nRegenerates--; + + if (sprite[nSprite].statnum == kStatExplodeTrigger) { + sprite[nSprite].cstat = 0x101; + } + else { + sprite[nSprite].cstat = 0; + } + + if (nRegenerates == 0) { + nFirstRegenerate = -1; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/items.h b/source/exhumed/src/items.h new file mode 100644 index 000000000..961d6479d --- /dev/null +++ b/source/exhumed/src/items.h @@ -0,0 +1,56 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __items_h__ +#define __items_h__ + +BEGIN_PS_NS + +enum +{ + kItemHeart = 0, + kItemInvincibility, + kItemTorch, + kItemDoubleDamage, + kItemInvisibility, + kItemMask, +}; + +extern short nItemMagic[]; + +void BuildItemAnim(short nSprite); +void DestroyItemAnim(short nSprite); +void ItemFlash(); +void FillItems(short nPlayer); +void UseEye(short nPlayer); +void UseMask(short nPlayer); +void UseTorch(short nPlayer); +void UseHeart(short nPlayer); +void UseScarab(short nPlayer); +void UseHand(short nPlayer); +void UseItem(short nPlayer, short nItem); +void UseCurItem(short nPlayer); +int GrabItem(short nPlayer, short nItem); +void DropMagic(short nSprite); +void InitItems(); +void StartRegenerate(short nSprite); +void DoRegenerates(); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/lavadude.cpp b/source/exhumed/src/lavadude.cpp new file mode 100644 index 000000000..25b8ae2fc --- /dev/null +++ b/source/exhumed/src/lavadude.cpp @@ -0,0 +1,526 @@ +//------------------------------------------------------------------------- +/* +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 "engine.h" +#include "lavadude.h" +#include "random.h" +#include "runlist.h" +#include "sequence.h" +#include "exhumed.h" +#include "move.h" +#include "trigdat.h" +#include "move.h" +#include "bullet.h" +#include "sound.h" +#include + +BEGIN_PS_NS + +#define kMaxLavas 20 + +struct Lava +{ + short nSprite; + short field_2; + short nAction; + short nTarget; + short nHealth; + short field_10; + short field_12; +}; + +Lava LavaList[kMaxLavas]; + +short LavaCount = 0; +short LavaSprite = -1; + +static actionSeq ActionSeq[] = { + {0, 1}, + {0, 1}, + {1, 0}, + {10, 0}, + {19, 0}, + {28, 1}, + {29, 1}, + {33, 0}, + {42, 1} +}; + +static SavegameHelper sgh("lavadude", + SA(LavaList), + SV(LavaCount), + SV(LavaSprite), + nullptr); + + +// done +void InitLava() +{ + LavaCount = 0; + LavaSprite = 1; +} + +int BuildLavaLimb(int nSprite, int edx, int ebx) +{ + short nSector = sprite[nSprite].sectnum; + + int nLimbSprite = insertsprite(nSector, 118); + assert(nLimbSprite >= 0 && nLimbSprite < kMaxSprites); + + sprite[nLimbSprite].x = sprite[nSprite].x; + sprite[nLimbSprite].y = sprite[nSprite].y; + sprite[nLimbSprite].z = sprite[nSprite].z - RandomLong() % ebx; + sprite[nLimbSprite].cstat = 0; + sprite[nLimbSprite].shade = -127; + sprite[nLimbSprite].pal = 1; + sprite[nLimbSprite].xvel = (RandomSize(5) - 16) << 8; + sprite[nLimbSprite].yvel = (RandomSize(5) - 16) << 8; + sprite[nLimbSprite].zvel = 2560 - (RandomSize(5) << 8); + sprite[nLimbSprite].yoffset = 0; + sprite[nLimbSprite].xoffset = 0; + sprite[nLimbSprite].xrepeat = 90; + sprite[nLimbSprite].yrepeat = 90; + sprite[nLimbSprite].picnum = (edx & 3) % 3; + sprite[nLimbSprite].hitag = 0; + sprite[nLimbSprite].lotag = runlist_HeadRun() + 1; + sprite[nLimbSprite].clipdist = 0; + +// GrabTimeSlot(3); + + sprite[nLimbSprite].extra = -1; + sprite[nLimbSprite].owner = runlist_AddRunRec(sprite[nLimbSprite].lotag - 1, nLimbSprite | 0x160000); + sprite[nLimbSprite].hitag = runlist_AddRunRec(NewRun, nLimbSprite | 0x160000); + + return nLimbSprite; +} + +void FuncLavaLimb(int eax, int UNUSED(nDamage), int nRun) +{ + short nSprite = RunData[nRun].nVal; + assert(nSprite >= 0 && nSprite < kMaxSprites); + + int nMessage = eax & 0x7F0000; + + switch (nMessage) + { + case 0x20000: + { + sprite[nSprite].shade += 3; + + int nRet = movesprite(nSprite, sprite[nSprite].xvel << 12, sprite[nSprite].yvel << 12, sprite[nSprite].zvel, 2560, -2560, CLIPMASK1); + + if (nRet || sprite[nSprite].shade > 100) + { + sprite[nSprite].zvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + + runlist_DoSubRunRec(sprite[nSprite].owner); + runlist_FreeRun(sprite[nSprite].lotag - 1); + runlist_SubRunRec(sprite[nSprite].hitag); + + mydeletesprite(nSprite); + } + break; + } + + case 0x90000: + { + seq_PlotSequence(eax, (SeqOffsets[kSeqLavag] + 30) + sprite[nSprite].picnum, 0, 1); + break; + } + + default: + return; + } +} + +int BuildLava(short nSprite, int x, int y, int UNUSED(z), short nSector, short nAngle, int lastArg) +{ + short nLava = LavaCount; + LavaCount++; + + if (nLava >= kMaxLavas) { + return -1; + } + + if (nSprite == -1) + { + nSprite = insertsprite(nSector, 118); + } + else + { + nSector = sprite[nSprite].sectnum; + nAngle = sprite[nSprite].ang; + x = sprite[nSprite].x; + y = sprite[nSprite].y; + + changespritestat(nSprite, 118); + } + + assert(nSprite >= 0 && nSprite < kMaxSprites); + + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = sector[nSector].floorz; + sprite[nSprite].cstat = 0x8000u; + sprite[nSprite].xrepeat = 200; + sprite[nSprite].yrepeat = 200; + sprite[nSprite].shade = -12; + sprite[nSprite].pal = 0; + sprite[nSprite].clipdist = 127; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].picnum = seq_GetSeqPicnum(kSeqLavag, ActionSeq[3].a, 0); + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].ang = nAngle; + sprite[nSprite].hitag = 0; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + +// GrabTimeSlot(3); + + sprite[nSprite].extra = -1; + + LavaList[nLava].nAction = 0; + LavaList[nLava].nHealth = 4000; + LavaList[nLava].nSprite = nSprite; + LavaList[nLava].nTarget = -1; + LavaList[nLava].field_12 = lastArg; + LavaList[nLava].field_10 = 0; + + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nLava | 0x150000); + LavaList[nLava].field_2 = runlist_AddRunRec(NewRun, nLava | 0x150000); + + nCreaturesLeft++; + + return nLava | 0x150000; +} + +void FuncLava(int a, int nDamage, int nRun) +{ + short nLava = RunData[nRun].nVal; + assert(nLava >= 0 && nLava < kMaxLavas); + + short nAction = LavaList[nLava].nAction; + + short nSeq = ActionSeq[nAction].a + SeqOffsets[kSeqLavag]; + + short nSprite = LavaList[nLava].nSprite; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + default: + { + Printf("unknown msg %d for Lava\n", a & 0x7F0000); + return; + } + + case 0x90000: + { + seq_PlotSequence(a & 0xFFFF, nSeq, LavaList[nLava].field_10, ActionSeq[nAction].b); + tsprite[a & 0xFFFF].owner = -1; + return; + } + + case 0xA0000: + { + return; + } + + case 0x80000: + { + if (!nDamage) { + return; + } + + LavaList[nLava].nHealth -= nDamage; + + if (LavaList[nLava].nHealth <= 0) + { + LavaList[nLava].nHealth = 0; + LavaList[nLava].nAction = 5; + LavaList[nLava].field_10 = 0; + + nCreaturesLeft--; + + sprite[nSprite].cstat &= 0xFEFE; + } + else + { + short nTarget = a & 0xFFFF; + + if (nTarget >= 0) + { + if (sprite[nTarget].statnum < 199) + { + LavaList[nLava].nTarget = nTarget; + } + } + + if (nAction == 3) + { + if (!RandomSize(2)) + { + LavaList[nLava].nAction = 4; + LavaList[nLava].field_10 = 0; + sprite[nSprite].cstat = 0; + } + } + + BuildLavaLimb(nSprite, totalmoves, 0xFA00); + } + + return; + } + + case 0x20000: + { + sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, LavaList[nLava].field_10); + int var_38 = LavaList[nLava].field_10; + + short nFlag = FrameFlag[SeqBase[nSeq] + var_38]; + + int var_1C; + + if (nAction) + { + seq_MoveSequence(nSprite, nSeq, var_38); + + LavaList[nLava].field_10++; + if (LavaList[nLava].field_10 >= SeqSize[nSeq]) + { + var_1C = 1; + LavaList[nLava].field_10 = 0; + } + else + { + var_1C = 0; + } + } + + short nTarget = LavaList[nLava].nTarget; + + if (nTarget >= 0 && nAction < 4) + { + if (!(sprite[nTarget].cstat & 0x101) || sprite[nTarget].sectnum >= 1024) + { + nTarget = -1; + LavaList[nLava].nTarget = -1; + } + } + + switch (nAction) + { + case 0: + { + if ((nLava & 0x1F) == (totalmoves & 0x1F)) + { + if (nTarget < 0) + { + nTarget = FindPlayer(nSprite, 76800); + } + + PlotCourseToSprite(nSprite, nTarget); + + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512); + sprite[nSprite].yvel = Sin(sprite[nSprite].ang); + + if (nTarget >= 0 && !RandomSize(1)) + { + LavaList[nLava].nTarget = nTarget; + LavaList[nLava].nAction = 2; + sprite[nSprite].cstat = 0x101; + LavaList[nLava].field_10 = 0; + break; + } + } + + int x = sprite[nSprite].x; + int y = sprite[nSprite].y; + int z = sprite[nSprite].z; + short nSector = sprite[nSprite].sectnum; + + int nVal = movesprite(nSprite, sprite[nSprite].xvel << 8, sprite[nSprite].yvel << 8, 0, 0, 0, CLIPMASK0); + + if (nSector != sprite[nSprite].sectnum) + { + changespritesect(nSprite, nSector); + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + + sprite[nSprite].ang = (sprite[nSprite].ang + ((RandomWord() & 0x3FF) + 1024)) & 0x7FF; + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512); + sprite[nSprite].yvel = Sin(sprite[nSprite].ang); + break; + } + + if (!nVal) { + break; + } + + if ((nVal & 0x0C000) == 0x8000) + { + sprite[nSprite].ang = (sprite[nSprite].ang + ((RandomWord() & 0x3FF) + 1024)) & 0x7FF; + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512); + sprite[nSprite].yvel = Sin(sprite[nSprite].ang); + break; + } + else if ((nVal & 0x0C000) == 0x0C000) + { + if ((nVal & 0x3FFF) == nTarget) + { + int nAng = getangle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y); + if (AngleDiff(sprite[nSprite].ang, nAng) < 64) + { + LavaList[nLava].nAction = 2; + sprite[nSprite].cstat = 0x101; + LavaList[nLava].field_10 = 0; + break; + } + } + } + + break; + } + + case 1: + case 6: + { + break; + } + + case 2: + { + if (var_1C) + { + LavaList[nLava].nAction = 3; + LavaList[nLava].field_10 = 0; + + PlotCourseToSprite(nSprite, nTarget); + + sprite[nSprite].cstat |= 0x101; + } + + break; + } + + case 3: + { + if ((nFlag & 0x80) && nTarget > -1) + { + int nHeight = GetSpriteHeight(nSprite); + GetUpAngle(nSprite, 0x0FFFF0600, nTarget, (-(nHeight >> 1))); + + BuildBullet(nSprite, 10, Sin(sprite[nSprite].ang + 512) << 8, Sin(sprite[nSprite].ang) << 8, -1, sprite[nSprite].ang, nTarget + 10000, 1); + } + else if (var_1C) + { + PlotCourseToSprite(nSprite, nTarget); + LavaList[nLava].nAction = 7; + LavaList[nLava].field_10 = 0; + } + + break; + } + + case 4: + { + if (var_1C) + { + LavaList[nLava].nAction = 7; + sprite[nSprite].cstat &= 0xFEFE; + } + + break; + } + + case 5: + { + if (nFlag & 0x40) + { + int nLimbSprite = BuildLavaLimb(nSprite, LavaList[nLava].field_10, 0xFA00u); + D3PlayFX(StaticSound[kSound26], nLimbSprite); + } + + if (LavaList[nLava].field_10) + { + if (nFlag & 0x80) + { + int ecx = 0; + do + { + BuildLavaLimb(nSprite, ecx, 0xFA00u); + ecx++; + } + while (ecx < 20); + runlist_ChangeChannel(LavaList[nLava].field_12, 1); + } + } + else + { + int ecx = 0; + + do + { + BuildLavaLimb(nSprite, ecx, 256); + ecx++; + } + while (ecx < 30); + + runlist_DoSubRunRec(sprite[nSprite].owner); + runlist_FreeRun(sprite[nSprite].lotag - 1); + runlist_SubRunRec(LavaList[nLava].field_2); + mydeletesprite(nSprite); + } + + break; + } + + case 7: + { + if (var_1C) + { + LavaList[nLava].nAction = 8; + LavaList[nLava].field_10 = 0; + } + break; + } + + case 8: + { + if (var_1C) + { + LavaList[nLava].nAction = 0; + LavaList[nLava].field_10 = 0; + sprite[nSprite].cstat = 0x8000; + } + break; + } + } + + // loc_31521: + sprite[nSprite].pal = 1; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/lavadude.h b/source/exhumed/src/lavadude.h new file mode 100644 index 000000000..8be004d66 --- /dev/null +++ b/source/exhumed/src/lavadude.h @@ -0,0 +1,32 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __lavadude_h__ +#define __lavadude_h__ + +BEGIN_PS_NS + +void InitLava(); +int BuildLava(short nSprite, int x, int y, int z, short nSector, short nAngle, int lastArg); +int BuildLavaLimb(int nSprite, int edx, int ebx); +void FuncLavaLimb(int, int, int); +void FuncLava(int, int, int); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/light.cpp b/source/exhumed/src/light.cpp new file mode 100644 index 000000000..4d82b7d03 --- /dev/null +++ b/source/exhumed/src/light.cpp @@ -0,0 +1,750 @@ +//------------------------------------------------------------------------- +/* +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 "light.h" +#include "engine.h" +#include "exhumed.h" +#include "view.h" +#include "cd.h" +#include "lighting.h" +#include +#include +#include + +BEGIN_PS_NS + +#define kMaxGrads 12 + +const char *GradList[kMaxGrads] = { + "normal.rmp", + "nodim.rmp", + "torch.rmp", + "notorch.rmp", + "brite.rmp", + "redbrite.rmp", + "grnbrite.rmp", + "normal.rmp", + "nodim.rmp", + "torch.rmp", + "notorch.rmp", + "brite.rmp" +}; + +int rtint = 0; +int gtint = 0; +int btint = 0; +//char *origpalookup[kMaxPalookups]; +//unsigned char curpal[768]; +//unsigned char kenpal[768]; +palette_t *fadedestpal; +palette_t *fadecurpal; +short nPalDelay; +short nPalDiff; +short overscanindex; +int bGreenPal = 0; + +// keep a local copy of the palette that would have been sent to the VGA display adapter +uint8_t vgaPalette[768]; + + +void MyLoadPalette() +{ + //int hFile = kopen4load("PALETTE.DAT", 1); + //if (hFile == -1) + //{ + // initprintf("Error reading palette 'PALETTE.DAT'\n"); + // return; + //} + // + //kread(hFile, kenpal, sizeof(kenpal)); + //kclose(hFile); + videoSetPalette(0, BASEPAL, 0); + SetOverscan(BASEPAL); +} + +int LoadPaletteLookups() +{ + uint8_t buffer[256*64]; + numshades = 64; + + for (int i = 0; i < kMaxGrads; i++) + { + auto hFile = fileSystem.OpenFileReader(GradList[i], 1); + if (!hFile.isOpen()) + { + initprintf("Error reading palette lookup '%s'\n", GradList[i]); + return 0; + } + + hFile.Read(buffer, 256*64); + // TODO: dumb hack + if (palookup[i]) + ALIGNED_FREE_AND_NULL(palookup[i]); + paletteSetLookupTable(i, buffer); + + // origpalookup[i] = palookup[i]; + bGreenPal = 0; + +#ifdef USE_OPENGL + /* + // Very rough approximation... + palookupfogfactor[kPalNormal] = 1.f; + palookupfogfactor[kPalNoDim] = 0.f; + palookupfogfactor[kPalTorch] = 0.36f; + palookupfogfactor[kPalNoTorch] = 0.15f; + palookupfogfactor[kPalBrite] = 2.f; + palookupfogfactor[kPalRedBrite] = 0.36f; + palookupfog[kPalRedBrite] = { 248, 32, 0, 0 }; + hicsetpalettetint(kPalRedBrite, 255, 255, 255, 248, 32, 0, 0); + palookupfog[kPalGreenBrite] = { 248, 32, 0, 0 }; + palookupfogfactor[kPalGreenBrite] = 0.f; + hicsetpalettetint(kPalGreenBrite, 100, 200, 100, 0, 0, 0, 0); + palookupfogfactor[kPalNormal2] = 1.f; + palookupfogfactor[kPalNoDim2] = 0.f; + palookupfogfactor[kPalTorch2] = 0.36f; + palookupfogfactor[kPalNoTorch2] = 0.15f; + palookupfogfactor[kPalBrite2] = 2.f; + */ +#endif + + } + + return 1; +} + +void SetGreenPal() +{ + bGreenPal = 1; + // for (int i = 0; i < kMaxGrads; i++) + // { + // palookup[i] = palookup[6]; + // } + // + // palookup[5] = origpalookup[5]; +} + +void RestoreGreenPal() +{ + bGreenPal = 0; + // for (int i = 0; i < kMaxGrads; i++) + // { + // palookup[i] = origpalookup[i]; + // } +} + +int HavePLURemap() +{ + return bGreenPal || bTorch; +} + +uint8_t RemapPLU(uint8_t pal) +{ + if (bGreenPal) + { + if (pal != kPalRedBrite) + pal = kPalGreenBrite; + } + else if (bTorch) + { + switch (pal) + { + case kPalTorch: + pal = kPalNoTorch; + break; + case kPalNoTorch: + pal = kPalTorch; + break; + case kPalTorch2: + pal = kPalNoTorch2; + break; + case kPalNoTorch2: + pal = kPalTorch2; + break; + } + } + return pal; +} + +void WaitVBL() +{ +#ifdef __WATCOMC__ + while (!(inp(0x3da) & 8)); +#endif +} + +//void MySetPalette(unsigned char *palette) +//{ +// WaitVBL(); +// +// // TODO +// kensetpalette(palette); +// +// memcpy(vgaPalette, palette, sizeof(vgaPalette)); +//} + +//void GetCurPal(unsigned char *palette) +//{ +// if (!palette) { +// memcpy(curpal, vgaPalette, sizeof(curpal)); +// } +// else { +// memcpy(palette, vgaPalette, sizeof(curpal)); +// } +//} + +void GrabPalette() +{ + SetOverscan(BASEPAL); + + videoSetPalette(0, BASEPAL, 2+8); + + nPalDiff = 0; + nPalDelay = 0; + + btint = 0; + gtint = 0; + rtint = 0; +#ifdef USE_OPENGL + videoTintBlood(0, 0, 0); +#endif +} + +void BlackOut() +{ + for (int i = 0; i < 256; i++) + { + curpalettefaded[i].r = 0; + curpalettefaded[i].g = 0; + curpalettefaded[i].b = 0; + } + //videoUpdatePalette(0, 256); + g_lastpalettesum = -1; +#ifdef USE_OPENGL + videoTintBlood(0, 0, 0); +#endif +} + +void RestorePalette() +{ + videoSetPalette(0, BASEPAL, 2+8); +#ifdef USE_OPENGL + videoTintBlood(0, 0, 0); +#endif +} + +void WaitTicks(int nTicks) +{ + if (htimer) + { + nTicks += (int)totalclock; + while (nTicks > (int)totalclock) { HandleAsync(); } + } + else + { + while (nTicks > 0) { + nTicks--; + WaitVBL(); + } + } +} + +// unused +void DoFadeToRed() +{ +#ifdef USE_OPENGL + if (videoGetRenderMode() >= REND_POLYMOST) + { + videoTintBlood(-255, -255, -255); + videoNextPage(); + return; + } +#endif + for (int i = 0; i < 256; i++) + { + if (curpalettefaded[i].g > 0) + { + curpalettefaded[i].g -= 4; + if (curpalettefaded[i].g < 0) + curpalettefaded[i].g = 0; + } + + if (curpalettefaded[i].b > 0) + { + curpalettefaded[i].b -= 4; + if (curpalettefaded[i].b < 0) + curpalettefaded[i].b = 0; + } + } + + //videoUpdatePalette(0, 256); + g_lastpalettesum = -1; +} + +void FadeToWhite() +{ + int ebx = 0; + +#ifdef USE_OPENGL + if (videoGetRenderMode() >= REND_POLYMOST) + { + videoTintBlood(255, 255, 255); + videoNextPage(); + return; + } +#endif + + for (int i = 0; i < 64; i++) + { + palette_t *pPal = curpalettefaded; + + for (int j = 0; j < 256; j++) + { + if (pPal->r < 255) + { + pPal->r += 4; + if (pPal->r > 255) + pPal->r = 255; + ebx++; + } + if (pPal->g < 255) + { + pPal->g += 4; + if (pPal->g > 255) + pPal->g = 255; + ebx++; + } + if (pPal->b < 255) + { + pPal->b += 4; + if (pPal->b > 255) + pPal->b = 255; + ebx++; + } + pPal++; + } + + //videoUpdatePalette(0, 256); + g_lastpalettesum = -1; + WaitTicks(2); + + // need to page flip in each iteration of the loop for non DOS version + videoNextPage(); + + if (!ebx) { + return; + } + } +} + +void FadeOut(int bFadeMusic) +{ + if (bFadeMusic) { + StartfadeCDaudio(); + } + + +#ifdef USE_OPENGL + if (videoGetRenderMode() >= REND_POLYMOST) + { + videoTintBlood(-255, -255, -255); + videoNextPage(); + } + else +#endif + for (int i = 64; i > 0; i--) + { + int v4 = 0; + palette_t *pPal = curpalettefaded; + + for (int j = 0; j < 256; j++) + { + if (pPal->r > 0) + { + pPal->r -= 4; + if (pPal->r < 0) + pPal->r = 0; + v4++; + } + if (pPal->g > 0) + { + pPal->g -= 4; + if (pPal->g < 0) + pPal->g = 0; + v4++; + } + if (pPal->b > 0) + { + pPal->b -= 4; + if (pPal->b < 0) + pPal->b = 0; + v4++; + } + pPal++; + } + + //videoUpdatePalette(0, 256); + g_lastpalettesum = -1; + WaitTicks(2); + + // need to page flip in each iteration of the loop for non DOS version + videoNextPage(); + + if (v4 == 0) { + break; + } + + if (bFadeMusic) { + StepFadeCDaudio(); + } + } + + if (bFadeMusic) { + while (StepFadeCDaudio() != 0) {} + } + + EraseScreen(overscanindex); +} + +void StartFadeIn() +{ + //fadedestpal = curpalette; + //fadecurpal = curpal; +} + +int DoFadeIn() +{ +#ifdef USE_OPENGL + if (videoGetRenderMode() >= REND_POLYMOST) + { + paletteSetColorTable(curbasepal, basepaltable[BASEPAL]); + videoSetPalette(0, curbasepal, 2+8); + videoNextPage(); + return 0; + } +#endif + int v2 = 0; + + for (int i = 0; i < 256; i++) + { + if (curpalettefaded[i].r != curpalette[i].r) + { + v2++; + int diff = curpalette[i].r - curpalettefaded[i].r; + if (klabs(diff) < 4) + curpalettefaded[i].r = curpalette[i].r; + else + curpalettefaded[i].r += 4 * ksgn(diff); + } + if (curpalettefaded[i].g != curpalette[i].g) + { + v2++; + int diff = curpalette[i].g - curpalettefaded[i].g; + if (klabs(diff) < 4) + curpalettefaded[i].g = curpalette[i].g; + else + curpalettefaded[i].g += 4 * ksgn(diff); + } + if (curpalettefaded[i].b != curpalette[i].b) + { + v2++; + int diff = curpalette[i].b - curpalettefaded[i].b; + if (klabs(diff) < 4) + curpalettefaded[i].b = curpalette[i].b; + else + curpalettefaded[i].b += 4 * ksgn(diff); + } + } + + //videoUpdatePalette(0, 256); + g_lastpalettesum = -1; + + return v2; +} + +void FadeIn() +{ +#ifdef USE_OPENGL + if (videoGetRenderMode() >= REND_POLYMOST) + { + videoSetPalette(0, BASEPAL, 2+8); + videoNextPage(); + return; + } +#endif + StartFadeIn(); + + int val; + + do + { + val = DoFadeIn(); + WaitTicks(2); + + // need to page flip in each iteration of the loop for non DOS version + videoNextPage(); + } while (val); +} + +void FixPalette() +{ + if (!nPalDiff) { + return; + } + + if (nPalDelay > 0) + { + nPalDelay--; + return; + } + + nPalDelay = 5; + +#ifdef USE_OPENGL + if (videoGetRenderMode() == REND_CLASSIC) +#endif + for (int i = 0; i < 256; i++) + { + short nVal; + + nVal = curpalettefaded[i].r - curpalette[i].r; + if (nVal > 0) + { + if (nVal > 20) + { + curpalettefaded[i].r -= 20; + } + else + { + curpalettefaded[i].r = curpalette[i].r; + } + } + + nVal = curpalettefaded[i].g - curpalette[i].g; + if (nVal > 0) + { + if (nVal > 20) + { + curpalettefaded[i].g -= 20; + } + else + { + curpalettefaded[i].g = curpalette[i].g; + } + } + + nVal = curpalettefaded[i].b - curpalette[i].b; + if (nVal > 0) + { + if (nVal > 20) + { + curpalettefaded[i].b -= 20; + } + else + { + curpalettefaded[i].b = curpalette[i].b; + } + } + } + + nPalDiff -= 20; + gtint -= 20; + rtint -= 20; + btint -= 20; + + if (gtint < 0) { + gtint = 0; + } + + if (rtint < 0) { + rtint = 0; + } + + if (btint < 0) { + btint = 0; + } + + if (nPalDiff < 0) { + nPalDiff = 0; + } + +#ifdef USE_OPENGL + if (videoGetRenderMode() >= REND_POLYMOST) videoTintBlood(rtint, gtint, btint); + else +#endif + { + + //videoUpdatePalette(0, 256); + g_lastpalettesum = -1; + } +} + +void TintPalette(int r, int g, int b) +{ + palette_t *pPal = curpalettefaded; + + if (bCamera) { + return; + } + + // range limit R between 20 and 255 if positive + if (r > 255) + { + r = 255; + } + else + { + if (r && r < 20) { + r = 20; + } + } + + // range limit G between 20 and 255 if positive + if (g > 255) + { + g = 255; + } + else + { + if (g && g < 20) { + g = 20; + } + } + + // range limit B between 20 and 255 if positive + if (b > 255) + { + b = 255; + } + else + { + if (b && b < 20) { + b = 20; + } + } + + // loc_17EFA + if (g && gtint > 32) { + return; + } + + gtint += g; + + if (r && rtint > 256) { + return; + } + + rtint += r; + + btint += b; + + // do not modify r, g or b variables from this point on + int nVal; + + // loc_17F49 + if (klabs(r) > klabs(g)) { + nVal = klabs(r); + } + else { + nVal = klabs(g); + } + + if (nVal < klabs(b)) { + nVal = klabs(b); + } + + nPalDiff += nVal; + +#ifdef USE_OPENGL + if (videoGetRenderMode() >= REND_POLYMOST) videoTintBlood(rtint, gtint, btint); + else +#endif + for (int i = 0; i < 256; i++) + { + nVal = pPal->r + r; + if (nVal > 255) { + nVal = 255; + } + pPal->r = nVal; + + nVal = pPal->g + g; + if (nVal > 255) { + nVal = 255; + } + pPal->g = nVal; + + nVal = pPal->b + b; + if (nVal > 255) { + nVal = 255; + } + pPal->b = nVal; + + pPal++; + } + + nPalDelay = 0; +} + +void DoOverscanSet(short someval) +{ +#ifdef __WATCOMC__ + union REGS regs; + + regs.h.al = 1; + regs.h.ah = 0x10; + regs.h.ch = someval; + + int386(0x10, ®s, ®s); + +#endif +} + +// unused +void SetWhiteOverscan() +{ + +} + +void SetOverscan(int id) +{ + if (basepaltable[id] == NULL) + return; + uint8_t *palette = basepaltable[id]; + int edi = 1000; + overscanindex = 0; + + for (int i = 0; i < 256; i++) + { + int ebx = 0; + + for (int j = 0; j < 3; j++) + { + uint8_t cl = *palette; + palette++; + ebx += cl; + } + + if (ebx < edi) + { + edi = ebx; + overscanindex = i; + } + } + + DoOverscanSet(overscanindex); +} +END_PS_NS diff --git a/source/exhumed/src/light.h b/source/exhumed/src/light.h new file mode 100644 index 000000000..5c65fae0e --- /dev/null +++ b/source/exhumed/src/light.h @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __light_h__ +#define __light_h__ + +#include "compat.h" + +BEGIN_PS_NS + +void MyLoadPalette(); +int LoadPaletteLookups(); +void WaitVBL(); +void SetGreenPal(); +void RestoreGreenPal(); +void FixPalette(); +void FadeToWhite(); +int HavePLURemap(); +uint8_t RemapPLU(uint8_t pal); + +extern void DoOverscanSet(short someval); +void SetOverscan(int id); + +//extern unsigned char kenpal[]; +extern short overscanindex; + +extern char *origpalookup[]; + +extern short nPalDiff; + +END_PS_NS + +#endif diff --git a/source/exhumed/src/lighting.cpp b/source/exhumed/src/lighting.cpp new file mode 100644 index 000000000..49d2b4260 --- /dev/null +++ b/source/exhumed/src/lighting.cpp @@ -0,0 +1,804 @@ +//------------------------------------------------------------------------- +/* +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 "typedefs.h" +#include "lighting.h" +#include "player.h" +#include "engine.h" +#include "exhumed.h" +#include "sound.h" +#include "light.h" +#include "random.h" +#include +#include + +BEGIN_PS_NS + +#define kMaxFlashes 2000 +#define kMaxFlickerMask 25 +#define kMaxGlows 50 +#define kMaxFlickers 100 +#define kMaxFlows 375 + +struct Flash +{ + char field_0; + short field_1; + int8_t shade; +}; + +struct Glow +{ + short field_0; + short field_2; + short nSector; + short field_6; +}; + +struct Flicker +{ + short field_0; + short nSector; + unsigned int field_4; +}; + +struct Flow +{ + short field_0; + short field_2; + int field_4; + int field_8; + int field_C; + int field_10; + int field_14; + int field_18; +}; + +Flash sFlash[kMaxFlashes]; + +Glow sGlow[kMaxGlows]; +short nNextFlash[kMaxFlashes]; +Flicker sFlicker[kMaxFlickers]; +short nFreeFlash[kMaxFlashes]; +Flow sFlowInfo[kMaxFlows]; +int flickermask[kMaxFlickerMask]; + +short bTorch = 0; +short nFirstFlash = -1; +short nLastFlash = -1; +short nFlashDepth = 2; +short nFlashes; +short nFlowCount; +short nFlickerCount; +short nGlowCount; + +int bDoFlicks = 0; +int bDoGlows = 0; + + +static SavegameHelper sgh("lightning", + SA(sFlash), + SA(sGlow), + SA(nNextFlash), + SA(sFlicker), + SA(nFreeFlash), + SA(sFlowInfo), + SA(flickermask), + SV(bTorch), + SV(nFirstFlash), + SV(nLastFlash), + SV(nFlashDepth), + SV(nFlashes), + SV(nFlowCount), + SV(nFlickerCount), + SV(nGlowCount), + SV(bDoFlicks), + SV(bDoGlows), + nullptr); + + +// done +int GrabFlash() +{ + if (nFlashes >= kMaxFlashes) { + return -1; + } + + short nFlash = nFreeFlash[nFlashes]; + nNextFlash[nFlash] = -1; + + nFlashes++; + + if (nLastFlash <= -1) + { + nFirstFlash = nFlash; + } + else + { + nNextFlash[nLastFlash] = nFlash; + } + + nLastFlash = nFlash; + + return nLastFlash; +} + +void InitLights() +{ + int i; + nFlickerCount = 0; + + for (i = 0; i < kMaxFlickerMask; i++) { + flickermask[i] = RandomSize(0x1F) * 2; + } + + nGlowCount = 0; + nFlowCount = 0; + nFlashes = 0; + bDoFlicks = kFalse; + bDoGlows = kFalse; + + for (i = 0; i < kMaxFlashes; i++) { + nFreeFlash[i] = i; + } + + nFirstFlash = -1; + nLastFlash = -1; +} + +void AddFlash(short nSector, int x, int y, int z, int val) +{ + assert(nSector >= 0 && nSector < kMaxSectors); + + int var_28 = 0; + unsigned int var_1C = val >> 8; + + if (var_1C >= nFlashDepth) { + return; + } + + unsigned int var_20 = val & 0x80; + unsigned int var_18 = val & 0x40; + + val = ((var_1C + 1) << 8) | char(val); + + int var_14 = 0; + + short startwall = sector[nSector].wallptr; + short endwall = sector[nSector].wallptr + sector[nSector].wallnum; + + for (int i = startwall; i < endwall; i++) + { + short wall2 = wall[i].point2; + + int xAverage = (wall[i].x + wall[wall2].x) / 2; + int yAverage = (wall[i].y + wall[wall2].y) / 2; + + sectortype *pNextSector = NULL; + if (wall[i].nextsector > -1) { + pNextSector = §or[wall[i].nextsector]; + } + + int ebx = -255; + + if (!var_18) + { + int x2 = x - xAverage; + if (x2 < 0) { + x2 = -x2; + } + + ebx = x2; + + int y2 = y - yAverage; + if (y2 < 0) { + y2 = -y2; + } + + ebx = ((y2 + ebx) >> 4) - 255; + } + + if (ebx < 0) + { + var_14++; + var_28 += ebx; + + if (wall[i].pal < 5) + { + if (!pNextSector || pNextSector->floorz < sector[nSector].floorz) + { + short nFlash = GrabFlash(); + if (nFlash < 0) { + return; + } + + sFlash[nFlash].field_0 = var_20 | 2; + sFlash[nFlash].shade = wall[i].shade; + sFlash[nFlash].field_1 = i; + + wall[i].pal += 7; + + ebx += wall[i].shade; + int eax = ebx; + + if (ebx < -127) { + eax = -127; + } + + wall[i].shade = eax; + + if (!var_1C && !wall[i].overpicnum && pNextSector) + { + AddFlash(wall[i].nextsector, x, y, z, val); + } + } + } + } + } + + if (var_14 && sector[nSector].floorpal < 4) + { + short nFlash = GrabFlash(); + if (nFlash < 0) { + return; + } + + sFlash[nFlash].field_0 = var_20 | 1; + sFlash[nFlash].field_1 = nSector; + sFlash[nFlash].shade = sector[nSector].floorshade; + + sector[nSector].floorpal += 7; + + int edx = sector[nSector].floorshade + var_28; + int eax = edx; + + if (edx < -127) { + eax = -127; + } + + sector[nSector].floorshade = eax; + + if (!(sector[nSector].ceilingstat & 1)) + { + if (sector[nSector].ceilingpal < 4) + { + short nFlash2 = GrabFlash(); + if (nFlash2 >= 0) + { + sFlash[nFlash2].field_0 = var_20 | 3; + sFlash[nFlash2].field_1 = nSector; + sFlash[nFlash2].shade = sector[nSector].ceilingshade; + + sector[nSector].ceilingpal += 7; + + int edx = sector[nSector].ceilingshade + var_28; + int eax = edx; + + if (edx < -127) { + eax = -127; + } + + sector[nSector].ceilingshade = eax; + } + } + } + + for (short nSprite = headspritesect[nSector]; nSprite >= 0; nSprite = nextspritesect[nSprite]) + { + if (sprite[nSprite].pal < 4) + { + short nFlash3 = GrabFlash(); + if (nFlash3 >= 0) + { + sFlash[nFlash3].field_0 = var_20 | 4; + sFlash[nFlash3].shade = sprite[nSprite].shade; + sFlash[nFlash3].field_1 = nSprite; + + sprite[nSprite].pal += 7; + + int eax = -255; + + if (!var_18) + { + int xDiff = x - sprite[nSprite].x; + if (xDiff < 0) { + xDiff = -xDiff; + } + + int yDiff = y - sprite[nSprite].y; + if (yDiff < 0) { + yDiff = -yDiff; + } + + eax = ((xDiff + yDiff) >> 4) - 255; + } + + if (eax < 0) + { + short shade = sprite[nSprite].shade + eax; + if (shade < -127) { + shade = -127; + } + + sprite[nSprite].shade = shade; + } + } + } + } + } +} + +void UndoFlashes() +{ + if (!nFlashes) { + return; + } + + int var_24 = 0; // CHECKME - Watcom error "initializer for variable var_24 may not execute + + int edi = -1; + + for (short nFlash = nFirstFlash; nFlash >= 0; nFlash = nNextFlash[nFlash]) + { + assert(nFlash < 2000 && nFlash >= 0); + + uint8_t var_28 = sFlash[nFlash].field_0 & 0x3F; + short nIndex = sFlash[nFlash].field_1; + + if (sFlash[nFlash].field_0 & 0x80) + { + int var_20 = var_28 - 1; + assert(var_20 >= 0); + + int8_t *pShade = NULL; + + switch (var_20) + { + case 0: + { + assert(nIndex >= 0 && nIndex < kMaxSectors); + + pShade = §or[nIndex].floorshade; + break; + } + + case 1: + { + assert(nIndex >= 0 && nIndex < kMaxWalls); + + pShade = &wall[nIndex].shade; + break; + } + + case 2: + { + assert(nIndex >= 0 && nIndex < kMaxSectors); + + pShade = §or[nIndex].ceilingshade; + break; + } + + case 3: + { + assert(nIndex >= 0 && nIndex < kMaxSprites); + + if (sprite[nIndex].pal >= 7) + { + pShade = &sprite[nIndex].shade; + } + else { + goto loc_1868A; + } + + break; + } + + default: + break; + } + + assert(pShade != NULL); + + short var_2C = (*pShade) + 6; + int var_30 = sFlash[nFlash].shade; + + if (var_2C < var_30) + { + *pShade = var_2C; + edi = nFlash; + continue; + } + } + + // loc_185FE + var_24 = var_28 - 1; // CHECKME - Watcom error "initializer for variable var_24 may not execute + assert(var_24 >= 0); + + switch (var_24) + { + default: + break; + + case 0: + { + sector[nIndex].floorpal -= 7; + sector[nIndex].floorshade = sFlash[nFlash].shade; + break; + } + + case 1: + { + wall[nIndex].pal -= 7; + wall[nIndex].shade = sFlash[nFlash].shade; + break; + } + + case 2: + { + sector[nIndex].ceilingpal -= 7; + sector[nIndex].ceilingshade = sFlash[nFlash].shade; + break; + } + + case 3: + { + if (sprite[nIndex].pal >= 7) + { + sprite[nIndex].pal -= 7; + sprite[nIndex].shade = sFlash[nFlash].shade; + } + + break; + } + } + +loc_1868A: + + nFlashes--; + assert(nFlashes >= 0); + + nFreeFlash[nFlashes] = nFlash; + + if (edi != -1) + { + nNextFlash[edi] = nNextFlash[nFlash]; + } + + if (nFlash == nFirstFlash) + { + nFirstFlash = nNextFlash[nFirstFlash]; + } + + if (nFlash == nLastFlash) + { + nLastFlash = edi; + } + } +} + +void AddGlow(short nSector, int nVal) +{ + if (nGlowCount >= kMaxGlows) { + return; + } + + sGlow[nGlowCount].field_6 = nVal; + sGlow[nGlowCount].nSector = nSector; + sGlow[nGlowCount].field_0 = -1; + sGlow[nGlowCount].field_2 = 0; + + nGlowCount++; +} + +// ok +void AddFlicker(short nSector, int nVal) +{ + if (nFlickerCount >= kMaxFlickers) { + return; + } + + sFlicker[nFlickerCount].field_0 = nVal; + sFlicker[nFlickerCount].nSector = nSector; + + if (nVal >= 25) { + nVal = 24; + } + + sFlicker[nFlickerCount].field_4 = flickermask[nVal]; + + nFlickerCount++; +} + +void DoGlows() +{ + bDoGlows++; + + if (bDoGlows < 3) { + return; + } + + bDoGlows = 0; + + for (int i = 0; i < nGlowCount; i++) + { + sGlow[i].field_2++; + + short nSector = sGlow[i].nSector; + short nShade = sGlow[i].field_0; + + if (sGlow[i].field_2 >= sGlow[i].field_6) + { + sGlow[i].field_2 = 0; + sGlow[i].field_0 = -sGlow[i].field_0; + } + + sector[nSector].ceilingshade += nShade; + sector[nSector].floorshade += nShade; + + int startwall = sector[nSector].wallptr; + int endwall = startwall + sector[nSector].wallnum - 1; + + for (int nWall = startwall; nWall <= endwall; nWall++) + { + wall[nWall].shade += nShade; + + // CHECKME - ASM has edx decreasing here. why? + } + } +} + +void DoFlickers() +{ + bDoFlicks ^= 1; + if (!bDoFlicks) { + return; + } + + for (int i = 0; i < nFlickerCount; i++) + { + short nSector = sFlicker[i].nSector; + + unsigned int eax = (sFlicker[i].field_4 & 1); + unsigned int edx = (sFlicker[i].field_4 & 1) << 31; + unsigned int ebp = sFlicker[i].field_4 >> 1; + + ebp |= edx; + edx = ebp & 1; + + sFlicker[i].field_4 = ebp; + + if (edx ^ eax) + { + short shade; + + if (eax) + { + shade = sFlicker[i].field_0; + } + else + { + shade = -sFlicker[i].field_0; + } + + sector[nSector].ceilingshade += shade; + sector[nSector].floorshade += shade; + + int startwall = sector[nSector].wallptr; + int endwall = startwall + sector[nSector].wallnum - 1; + + for (int nWall = endwall; nWall >= startwall; nWall--) + { + wall[nWall].shade += shade; + + // CHECKME - ASM has edx decreasing here. why? + } + } + } +} + +// nWall can also be passed in here via nSprite parameter - TODO - rename nSprite parameter :) +void AddFlow(int nSprite, int a, int b) +{ + if (nFlowCount >= kMaxFlows) + return; + + short nFlow = nFlowCount; + nFlowCount++; + + short var_18; + + if (b < 2) + { + var_18 = sprite[nSprite].sectnum; + short nPic = sector[var_18].floorpicnum; + short nAngle = sprite[nSprite].ang; + + sFlowInfo[nFlow].field_14 = (tilesiz[nPic].x << 14) - 1; + sFlowInfo[nFlow].field_18 = (tilesiz[nPic].y << 14) - 1; + sFlowInfo[nFlow].field_C = -Cos(nAngle) * a; + sFlowInfo[nFlow].field_10 = Sin(nAngle) * a; + } + else + { + short nAngle; + + if (b == 2) { + nAngle = 512; + } + else { + nAngle = 1536; + } + + var_18 = nSprite; + short nPic = wall[var_18].picnum; + + sFlowInfo[nFlow].field_14 = (tilesiz[nPic].x * wall[var_18].xrepeat) << 8; + sFlowInfo[nFlow].field_18 = (tilesiz[nPic].y * wall[var_18].yrepeat) << 8; + sFlowInfo[nFlow].field_C = -Cos(nAngle) * a; + sFlowInfo[nFlow].field_10 = Sin(nAngle) * a; + } + + sFlowInfo[nFlow].field_8 = 0; + sFlowInfo[nFlow].field_4 = 0; + sFlowInfo[nFlow].field_0 = var_18; + sFlowInfo[nFlow].field_2 = b; +} + +void DoFlows() +{ + for (int i = 0; i < nFlowCount; i++) + { + sFlowInfo[i].field_4 += sFlowInfo[i].field_C; + sFlowInfo[i].field_8 += sFlowInfo[i].field_10; + + switch (sFlowInfo[i].field_2) + { + case 0: + { + sFlowInfo[i].field_4 &= sFlowInfo[i].field_14; + sFlowInfo[i].field_8 &= sFlowInfo[i].field_18; + + short nSector = sFlowInfo[i].field_0; + sector[nSector].floorxpanning = sFlowInfo[i].field_4 >> 14; + sector[nSector].floorypanning = sFlowInfo[i].field_8 >> 14; + break; + } + + case 1: + { + short nSector = sFlowInfo[i].field_0; + + sector[nSector].ceilingxpanning = sFlowInfo[i].field_4 >> 14; + sector[nSector].ceilingypanning = sFlowInfo[i].field_8 >> 14; + + sFlowInfo[i].field_4 &= sFlowInfo[i].field_14; + sFlowInfo[i].field_8 &= sFlowInfo[i].field_18; + break; + } + + case 2: + { + short nWall = sFlowInfo[i].field_0; + + wall[nWall].xpanning = sFlowInfo[i].field_4 >> 14; + wall[nWall].ypanning = sFlowInfo[i].field_8 >> 14; + + if (sFlowInfo[i].field_4 < 0) + { + sFlowInfo[i].field_4 += sFlowInfo[i].field_14; + } + + if (sFlowInfo[i].field_8 < 0) + { + sFlowInfo[i].field_8 += sFlowInfo[i].field_18; + } + + break; + } + + case 3: + { + short nWall = sFlowInfo[i].field_0; + + wall[nWall].xpanning = sFlowInfo[i].field_4 >> 14; + wall[nWall].ypanning = sFlowInfo[i].field_8 >> 14; + + if (sFlowInfo[i].field_4 >= sFlowInfo[i].field_14) + { + sFlowInfo[i].field_4 -= sFlowInfo[i].field_14; + } + + if (sFlowInfo[i].field_8 >= sFlowInfo[i].field_18) + { + sFlowInfo[i].field_8 -= sFlowInfo[i].field_18; + } + + break; + } + } + } +} + +void DoLights() +{ + DoFlickers(); + DoGlows(); + DoFlows(); +} + +void SetTorch(int nPlayer, int bTorchOnOff) +{ + char buf[40]; + + if (bTorchOnOff == bTorch) { + return; + } + + if (nPlayer != nLocalPlayer) { + return; + } + + // char *pTempPal = origpalookup[kPalTorch]; + // palookup[kPalTorch] = palookup[kPalNoTorch]; + // palookup[kPalNoTorch] = pTempPal; + // + // pTempPal = origpalookup[kPalTorch]; + // origpalookup[kPalTorch] = origpalookup[kPalNoTorch]; + // origpalookup[kPalNoTorch] = pTempPal; + // + // pTempPal = origpalookup[kPalTorch2]; + // origpalookup[kPalTorch2] = origpalookup[kPalNoTorch2]; + // origpalookup[kPalNoTorch2] = pTempPal; + // + // pTempPal = palookup[kPalTorch2]; + // palookup[kPalNoTorch2] = palookup[kPalTorch2]; + // palookup[kPalTorch2] = pTempPal; + + if (bTorchOnOff == 2) { + bTorch = !bTorch; + } + else { + bTorch = bTorchOnOff; + } + + if (bTorch) { + PlayLocalSound(kSoundTorchOn, 0); + } + + strcpy(buf, "TORCH IS "); + + if (bTorch) { + strcat(buf, "LIT"); + } + else { + strcat(buf, "OUT"); + } + + StatusMessage(150, buf); +} + +void BuildFlash(short nPlayer, short UNUSED(nSector), int nVal) +{ + if (nPlayer == nLocalPlayer) + { + flash = nVal; + flash = -nVal; // ??? + } +} + +END_PS_NS diff --git a/source/exhumed/src/lighting.h b/source/exhumed/src/lighting.h new file mode 100644 index 000000000..fdd146486 --- /dev/null +++ b/source/exhumed/src/lighting.h @@ -0,0 +1,40 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __lighting_h__ +#define __lighting_h__ + +BEGIN_PS_NS + +extern short nFlashDepth; + +void InitLights(); +void AddFlash(short nSector, int x, int y, int z, int val); +void SetTorch(int nPlayer, int bTorchOnOff); +void UndoFlashes(); +void DoLights(); +void AddFlow(int nSprite, int a, int b); +void BuildFlash(short nPlayer, short nSector, int nVal); +void AddGlow(short nSector, int nVal); +void AddFlicker(short nSector, int nVal); + +extern short bTorch; + +END_PS_NS + +#endif \ No newline at end of file diff --git a/source/exhumed/src/lion.cpp b/source/exhumed/src/lion.cpp new file mode 100644 index 000000000..8737c0f2f --- /dev/null +++ b/source/exhumed/src/lion.cpp @@ -0,0 +1,598 @@ +//------------------------------------------------------------------------- +/* +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 "lion.h" +#include "engine.h" +#include "runlist.h" +#include "exhumed.h" +#include "sequence.h" +#include "move.h" +#include "sound.h" +#include "random.h" +#include "trigdat.h" +#include "items.h" +#include + +BEGIN_PS_NS + +#define kMaxLions 40 + +short LionCount = -1; + +short MoveHook[kMaxLions]; + +static actionSeq ActionSeq[] = {{54, 1}, {18, 1}, {0, 0}, {10, 0}, {44, 0}, {18, 0}, {26, 0}, {34, 0}, {8, 1}, {9, 1}, {52, 1}, {53, 1}}; + +struct Lion +{ + short nHealth; + short _b; + short nAction; + short nSprite; + short nTarget; + short _f; + short _g; + short _h; +}; + +Lion LionList[kMaxLions]; + +static SavegameHelper sgh("lion", + SV(LionCount), + SA(MoveHook), + SA(LionList), + nullptr); + + +void InitLion() +{ + LionCount = kMaxLions; +} + +int BuildLion(short nSprite, int x, int y, int z, short nSector, short nAngle) +{ + LionCount--; + short nLion = LionCount; + + if (LionCount < 0) { + return -1; + } + + if (nSprite == -1) + { + nSprite = insertsprite(nSector, 104); + } + else + { + changespritestat(nSprite, 104); + x = sprite[nSprite].x; + y = sprite[nSprite].y; + z = sector[sprite[nSprite].sectnum].floorz; + nAngle = sprite[nSprite].ang; + + } + + assert(nSprite >= 0 && nSprite < kMaxSprites); + + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + sprite[nSprite].cstat = 0x101; + sprite[nSprite].clipdist = 60; + sprite[nSprite].shade = -12; + sprite[nSprite].xrepeat = 40; + sprite[nSprite].yrepeat = 40; + sprite[nSprite].picnum = 1; + sprite[nSprite].pal = sector[sprite[nSprite].sectnum].ceilingpal; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].ang = nAngle; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].hitag = 0; + sprite[nSprite].extra = -1; + +// GrabTimeSlot(3); + + LionList[nLion].nAction = 0; + LionList[nLion].nHealth = 500; + LionList[nLion]._b = 0; + LionList[nLion].nSprite = nSprite; + LionList[nLion].nTarget = -1; + LionList[nLion]._g = 0; + LionList[nLion]._f = nLion; + + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nLion | 0x130000); + + MoveHook[nLion] = runlist_AddRunRec(NewRun, nLion | 0x130000); + + nCreaturesLeft++; + + return nLion | 0x130000; +} + +void FuncLion(int a, int nDamage, int nRun) +{ + int var_18 = 0; + + short nLion = RunData[nRun].nVal; + assert(nLion >= 0 && nLion < kMaxLions); + + short nSprite = LionList[nLion].nSprite; + short nAction = LionList[nLion].nAction; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + default: + { + Printf("unknown msg %d for Lion\n", nMessage); + return; + } + + case 0x90000: + { + seq_PlotSequence(a, SeqOffsets[kSeqLion] + ActionSeq[nAction].a, LionList[nLion]._b, ActionSeq[nAction].b); + return; + } + + case 0xA0000: + { + nDamage = runlist_CheckRadialDamage(nSprite); + // now fall through to 0x80000 + fallthrough__; + } + case 0x80000: + { + if (nDamage && LionList[nLion].nHealth > 0) + { + LionList[nLion].nHealth -= nDamage; + if (LionList[nLion].nHealth <= 0) + { + // R.I.P. + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + LionList[nLion].nHealth = 0; + sprite[nSprite].cstat &= 0xFEFE; + + nCreaturesLeft--; + + if (nAction < 10) + { + DropMagic(nSprite); + + if (nMessage == 0xA0000) { + LionList[nLion].nAction = 11; + } + else + { + LionList[nLion].nAction = 10; + } + + LionList[nLion]._b = 0; + return; + } + } + else + { + short nTarget = a & 0xFFFF; + + if (nTarget > -1) + { + if (sprite[nTarget].statnum < 199) { + LionList[nLion].nTarget = nTarget; + } + + if (nAction != 6) + { + if (RandomSize(8) <= (LionList[nLion].nHealth >> 2)) + { + LionList[nLion].nAction = 4; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } + else if (RandomSize(1)) + { + PlotCourseToSprite(nSprite, nTarget); + LionList[nLion].nAction = 5; + LionList[nLion]._g = RandomSize(3); + + sprite[nSprite].ang = (sprite[nSprite].ang - (RandomSize(1) << 8)) + (RandomSize(1) << 8); + } + else + { + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + LionList[nLion].nAction = 8; + sprite[nSprite].cstat &= 0xFEFE; + } + + LionList[nLion]._b = 0; + } + } + } + } + return; + } + + case 0x20000: + { + if (nAction != 7) { + Gravity(nSprite); + } + + short nSeq = SeqOffsets[kSeqLion] + ActionSeq[nAction].a; + + sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, LionList[nLion]._b); + + seq_MoveSequence(nSprite, nSeq, LionList[nLion]._b); + + LionList[nLion]._b++; + if (LionList[nLion]._b >= SeqSize[nSeq]) + { + LionList[nLion]._b = 0; + var_18 = 1; + } + + short nFlag = FrameFlag[SeqBase[nSeq] + LionList[nLion]._b]; + short nTarget = LionList[nLion].nTarget; + + int nVal = MoveCreatureWithCaution(nSprite); + + switch (nAction) + { + default: + return; + + case 0: + case 1: + { + if ((LionList[nLion]._f & 31) == (totalmoves & 31)) + { + if (nTarget < 0) + { + nTarget = FindPlayer(nSprite, 40); + if (nTarget >= 0) + { + D3PlayFX(StaticSound[kSound24], nSprite); + LionList[nLion].nAction = 2; + LionList[nLion]._b = 0; + + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512) >> 1; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 1; + LionList[nLion].nTarget = nTarget; + return; + } + } + } + + if (nAction) + { + LionList[nLion]._g--; + if (LionList[nLion]._g <= 0) + { + if (RandomBit()) + { + sprite[nSprite].ang = RandomWord() & kAngleMask; + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512) >> 1; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 1; + } + else + { + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } + + LionList[nLion]._g = 100; + } + } + + return; + } + + case 2: + { + if ((totalmoves & 0x1F) == (LionList[nLion]._f & 0x1F)) + { + PlotCourseToSprite(nSprite, nTarget); + + short nAng = sprite[nSprite].ang & 0xFFF8; + + if (sprite[nSprite].cstat & 0x8000) + { + sprite[nSprite].xvel = Sin(nAng + 512) * 2; + sprite[nSprite].yvel = Sin(nAng) * 2; + } + else + { + sprite[nSprite].xvel = Sin(nAng + 512) >> 1; + sprite[nSprite].yvel = Sin(nAng) >> 1; + } + } + + if ((nVal & 0xC000) < 0x8000) + { + break; + } + else if ((nVal & 0xC000) == 0x8000) + { + // loc_378FA: + sprite[nSprite].ang = (sprite[nSprite].ang + 256) & kAngleMask; + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512) >> 1; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 1; + break; + } + else if ((nVal & 0xC000) == 0xC000) + { + if ((nVal & 0x3FFF) == nTarget) + { + if (sprite[nSprite].cstat & 0x8000) + { + LionList[nLion].nAction = 9; + sprite[nSprite].cstat &= 0x7FFF; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } + else + { + int nAng = getangle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y); + + if (AngleDiff(sprite[nSprite].ang, nAng) < 64) + { + LionList[nLion].nAction = 3; + } + } + + LionList[nLion]._b = 0; + break; + } + else + { + // loc_378FA: + sprite[nSprite].ang = (sprite[nSprite].ang + 256) & kAngleMask; + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512) >> 1; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 1; + break; + } + } + + break; + } + + case 3: + { + if (nTarget == -1) + { + LionList[nLion].nAction = 1; + LionList[nLion]._g = 50; + } + else + { + if (PlotCourseToSprite(nSprite, nTarget) >= 768) + { + LionList[nLion].nAction = 2; + } + else if (nFlag & 0x80) + { + runlist_DamageEnemy(nTarget, nSprite, 10); + } + } + + break; + } + + case 4: + { + if (var_18) + { + LionList[nLion].nAction = 2; + LionList[nLion]._b = 0; + } + + if (nVal & 0x20000) + { + sprite[nSprite].xvel >>= 1; + sprite[nSprite].yvel >>= 1; + } + + return; + } + + case 5: + { + LionList[nLion]._g--; + if (LionList[nLion]._g <= 0) + { + sprite[nSprite].zvel = -4000; + LionList[nLion]._g = 0; + + int x = sprite[nSprite].x; + int y = sprite[nSprite].y; + int z = sprite[nSprite].z - (GetSpriteHeight(nSprite) >> 1); + + int var_40 = 0x7FFFFFFF; + + short var_28 = sprite[nSprite].ang; + + short nAng = (sprite[nSprite].ang - 512) & kAngleMask; + + for (int i = 0; i < 5; i++) + { + short hitwall; + int hitx, hity; + vec3_t startPos = { x, y, z }; + hitdata_t hitData; + hitscan(&startPos, sprite[nSprite].sectnum, Sin(nAng + 512), Sin(nAng), 0, &hitData, CLIPMASK1); + hitx = hitData.pos.x; + hity = hitData.pos.y; + hitwall = hitData.wall; + + if (hitwall > -1) + { + int ebx = klabs(hitx - x); + int eax = klabs(hity - y); + + ebx += eax; + + if (ebx < var_40) + { + var_40 = ebx; + var_28 = nAng; + } + } + + nAng += 256; + nAng &= kAngleMask; + } + + sprite[nSprite].ang = var_28; + + LionList[nLion].nAction = 6; + sprite[nSprite].xvel = (Sin(sprite[nSprite].ang + 512)) - (Sin(sprite[nSprite].ang + 512) >> 3); + sprite[nSprite].yvel = (Sin(sprite[nSprite].ang)) - (Sin(sprite[nSprite].ang) >> 3); + D3PlayFX(StaticSound[kSound24], nSprite); + } + + return; + } + + case 6: + { + if (nVal & 0x30000) + { + LionList[nLion].nAction = 2; + LionList[nLion]._b = 0; + return; + } + + if ((nVal & 0xC000) == 0x8000) + { + LionList[nLion].nAction = 7; + sprite[nSprite].ang = (GetWallNormal(nVal & 0x3FFF) + 1024) & kAngleMask; + LionList[nLion]._g = RandomSize(4); + return; + } + else if ((nVal & 0xC000) == 0xC000) + { + if ((nVal & 0x3FFF) == nTarget) + { + int nAng = getangle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y); + if (AngleDiff(sprite[nSprite].ang, nAng) < 64) + { + LionList[nLion].nAction = 3; + LionList[nLion]._b = 0; + } + } + else + { + // loc_378FA: + sprite[nSprite].ang = (sprite[nSprite].ang + 256) & kAngleMask; + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512) >> 1; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 1; + break; + } + } + + return; + } + + case 7: + { + LionList[nLion]._g--; + + if (LionList[nLion]._g <= 0) + { + LionList[nLion]._g = 0; + if (nTarget > -1) + { + PlotCourseToSprite(nSprite, nTarget); + } + else + { + sprite[nSprite].ang = (RandomSize(9) + (sprite[nSprite].ang + 768)) & kAngleMask; + } + + sprite[nSprite].zvel = -1000; + + LionList[nLion].nAction = 6; + sprite[nSprite].xvel = (Sin(sprite[nSprite].ang + 512)) - (Sin(sprite[nSprite].ang + 512) >> 3); + sprite[nSprite].yvel = (Sin(sprite[nSprite].ang)) - (Sin(sprite[nSprite].ang) >> 3); + D3PlayFX(StaticSound[kSound24], nSprite); + } + + return; + } + + case 8: + { + if (var_18) + { + LionList[nLion].nAction = 2; + LionList[nLion]._b = 0; + sprite[nSprite].cstat |= 0x8000; + } + return; + } + + case 9: + { + if (var_18) + { + LionList[nLion]._b = 0; + LionList[nLion].nAction = 2; + sprite[nSprite].cstat |= 0x101; + } + return; + } + + case 10: + case 11: + { + if (var_18) + { + runlist_SubRunRec(sprite[nSprite].owner); + runlist_SubRunRec(MoveHook[nLion]); + sprite[nSprite].cstat = 0x8000; + } + return; + } + } + + // loc_379AD: ? + if (nAction != 1 && nTarget != -1) + { + if (!(sprite[nTarget].cstat & 0x101)) + { + LionList[nLion].nAction = 1; + LionList[nLion]._b = 0; + LionList[nLion]._g = 100; + LionList[nLion].nTarget = -1; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } + } + + return; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/lion.h b/source/exhumed/src/lion.h new file mode 100644 index 000000000..b074cab75 --- /dev/null +++ b/source/exhumed/src/lion.h @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __lion_h__ +#define __lion_h__ + +BEGIN_PS_NS + +void InitLion(); +int BuildLion(short nSprite, int x, int y, int z, short nSector, short nAngle); +void FuncLion(int, int, int); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/main.cpp b/source/exhumed/src/main.cpp new file mode 100644 index 000000000..14d78bdc9 --- /dev/null +++ b/source/exhumed/src/main.cpp @@ -0,0 +1,17 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- diff --git a/source/exhumed/src/map.cpp b/source/exhumed/src/map.cpp new file mode 100644 index 000000000..d98ecd04d --- /dev/null +++ b/source/exhumed/src/map.cpp @@ -0,0 +1,631 @@ +//------------------------------------------------------------------------- +/* +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 "typedefs.h" +#include +#include "player.h" +#include "init.h" +#include "engine.h" +#include "exhumed.h" +#include "view.h" + +BEGIN_PS_NS + + +short bShowTowers = kFalse;int ldMapZoom; +int lMapZoom; + +void MarkSectorSeen(short nSector); + + +void InitMap() +{ + memset(show2dsector, 0, sizeof(show2dsector)); + memset(show2dwall, 0, sizeof(show2dwall)); + memset(show2dsprite, 0, sizeof(show2dsprite)); + + ldMapZoom = 64; + lMapZoom = 1000; +} + +void GrabMap() +{ + for (int i = 0; i < numsectors; i++) { + MarkSectorSeen(i); + } +} + +void MarkSectorSeen(short nSector) +{ + if (!((1 << (nSector & 7)) & show2dsector[nSector >> 3])) + { + show2dsector[nSector >> 3] |= 1 << (nSector & 7); + + short startwall = sector[nSector].wallptr; + short nWalls = sector[nSector].wallnum; + short endwall = startwall + nWalls; + + while (startwall <= endwall) + { + show2dwall[startwall >> 3] = (1 << (startwall & 7)) | show2dwall[startwall >> 3]; + startwall++; + } + } +} + +void drawoverheadmap(int cposx, int cposy, int czoom, short cang) +{ +#ifndef __WATCOMC__ // FIXME - Won't compile on Watcom + int xvect = sintable[(2048 - cang) & 2047] * czoom; + int yvect = sintable[(1536 - cang) & 2047] * czoom; + int xvect2 = mulscale(xvect, yxaspect, 16); + int yvect2 = mulscale(yvect, yxaspect, 16); + + // draw player position arrow + renderDrawLine(xdim << 11, (ydim << 11) - 20480, xdim << 11, (ydim << 11) + 20480, 24); + renderDrawLine((xdim << 11) - 20480, ydim << 11, xdim << 11, (ydim << 11) - 20480, 24); + renderDrawLine((xdim << 11) + 20480, ydim << 11, xdim << 11, (ydim << 11) - 20480, 24); + + short nPlayerSprite = PlayerList[nLocalPlayer].nSprite; + + int nPlayerZ = sprite[nPlayerSprite].z; + + for (int nSector = 0; nSector < numsectors; nSector++) + { + short startwall = sector[nSector].wallptr; + short nWalls = sector[nSector].wallnum; + short endwall = startwall + nWalls - 1; + + int nCeilZ = sector[nSector].ceilingz; + int nFloorZ = sector[nSector].floorz; + + int nZVal = nFloorZ - nPlayerZ; + if (nZVal < 0) { + nZVal = -nZVal; + } + + int var_10 = nZVal >> 13; + if (var_10 > 12) { + var_10 = 12; + } + + var_10 = 111 - var_10; + + // int startwallB = startwall; + + for (int nWall = startwall; nWall <= endwall; nWall++) + { + short nextwall = wall[nWall].nextwall; + + if (nextwall >= 0) + { + if (show2dwall[nWall >> 3] & (1 << (nWall & 7))) + { + if (nextwall <= nWall || (show2dwall[nextwall >> 3] & (1 << (nextwall & 7))) <= 0) + { + if (nCeilZ != sector[wall[nWall].nextsector].ceilingz || + nFloorZ != sector[wall[nWall].nextsector].floorz || + ((wall[nextwall].cstat | wall[nWall].cstat) & 0x30)) + { + int ox = wall[nWall].x - cposx; + int oy = wall[nWall].y - cposy; + + int x1 = mulscale(ox, xvect, 16) - mulscale(oy, yvect, 16); + int y1 = mulscale(oy, xvect2, 16) + mulscale(ox, yvect2, 16); + + int nWall2 = wall[nWall].point2; + ox = wall[nWall2].x - cposx; + oy = wall[nWall2].y - cposy; + int x2 = mulscale(ox, xvect, 16) - mulscale(oy, yvect, 16); + int y2 = mulscale(oy, xvect2, 16) + mulscale(ox, yvect2, 16); + + renderDrawLine(x1 + (xdim << 11), y1 + (ydim << 11), x2 + (xdim << 11), y2 + (ydim << 11), var_10); + + /* + drawline256( + ((unsigned __int64)(v4 * (signed __int64)v12) >> 16) + - ((unsigned __int64)(v5 * (signed __int64)v13) >> 16) + + (xdim << 11), + ((unsigned __int64)(v42 * (signed __int64)v12) >> 16) + + ((unsigned __int64)(v43 * (signed __int64)v13) >> 16) + + (ydim << 11), + (build_xdim << 11) + + ((unsigned __int64)(v4 * (signed __int64)(*v14 - v31)) >> 16) + - ((unsigned __int64)(v5 * (signed __int64)(v14[1] - v30)) >> 16), + ydim << 11) + + ((unsigned __int64)(v43 * (signed __int64)(v14[1] - v30)) >> 16) + + ((unsigned __int64)(v42 * (signed __int64)(*v14 - v31)) >> 16), + v48); + */ + } + } + } + } + } + } + +// int var_4C = 0; +// int var_48 = 0; + + for (int nSector = 0; nSector < numsectors; nSector++) + { + int startwall = sector[nSector].wallptr; + int nWalls = sector[nSector].wallnum; + int endwall = startwall + nWalls - 1; + + int nFloorZ = sector[nSector].floorz; + + int nVal = nFloorZ - nPlayerZ; + if (nVal < 0) { + nVal = -nVal; + } + + int var_14 = nVal >> 13; + + if (var_14 <= 15) + { + var_14 = 111 - var_14; + + for (int nWall = startwall; nWall <= endwall; nWall++) + { + if (wall[nWall].nextwall < 0) + { + if (show2dwall[nWall >> 3] & (1 << (nWall & 7))) + { + if (tilesiz[wall[nWall].picnum].x && tilesiz[wall[nWall].picnum].y) + { + int ox = wall[nWall].x - cposx; + int oy = wall[nWall].y - cposy; + int x1 = mulscale(ox, xvect, 16) - mulscale(oy, yvect, 16); + int y1 = mulscale(oy, xvect2, 16) + mulscale(ox, yvect2, 16); + + int nWall2 = wall[nWall].point2; + ox = wall[nWall2].x - cposx; + oy = wall[nWall2].y - cposy; + int x2 = mulscale(ox, xvect, 16) - mulscale(oy, yvect, 16); + int y2 = mulscale(oy, xvect2, 16) + mulscale(ox, yvect2, 16); + + renderDrawLine(x1 + (xdim << 11), y1 + (ydim << 11), x2 + (xdim << 11), y2 + (ydim << 11), 24); + +/* + + v19 = *v17 - v31; + v20 = v17[1] - v30; + v21 = &wall[8 * *((_WORD *)v17 + 4)]; + + build_drawline256( + (build_xdim << 11) + + ((unsigned __int64)(v4 * (signed __int64)v19) >> 16) + - ((unsigned __int64)(v5 * (signed __int64)v20) >> 16), + (build_ydim << 11) + + ((unsigned __int64)(v42 * (signed __int64)v19) >> 16) + + ((unsigned __int64)(v43 * (signed __int64)v20) >> 16), + (build_xdim << 11) + + ((unsigned __int64)(v4 * (signed __int64)(*v21 - v31)) >> 16) + - ((unsigned __int64)(v5 * (signed __int64)(v21[1] - v30)) >> 16), + (build_ydim << 11) + + ((unsigned __int64)(v42 * (signed __int64)(*v21 - v31)) >> 16) + + ((unsigned __int64)(v43 * (signed __int64)(v21[1] - v30)) >> 16), + v46); +*/ + } + } + } + } + + if (bShowTowers) + { + for (int nSprite = headspritestat[406]; nSprite != -1; nSprite = nextspritestat[nSprite]) + { + int ox = sprite[nSprite].x - cposx; // var_64 + int oy = sprite[nSprite].y - cposx; // var_68 + + // int var_58 = mulscale(var_64, xvect, 16) - mulscale(var_68, yvect, 16); + int x1 = mulscale(ox, xvect, 16) - mulscale(oy, yvect, 16); + int y1 = mulscale(oy, xvect2, 16) + mulscale(ox, yvect2, 16); + + //int var_58 = mulscale(var_64, xvect, 16) - mulscale(var_68, yvect, 16); + //int esi = mulscale(var_68, xvect2, 16) + mulscale(var_65, yvect2, 16) + + //v25 = ((unsigned __int64)(v4 * (signed __int64)ox) >> 16) + // - ((unsigned __int64)(v5 * (signed __int64)oy) >> 16); + + //v26 = ((unsigned __int64)(v42 * (signed __int64)ox) >> 16) + // + ((unsigned __int64)(v43 * (signed __int64)oy) >> 16); + + //v27 = v26 + 2048; + //v28 = v26 + 2048 + (ydim << 11); + //v26 -= 2048; + + // v25 is x1 + // v26 is y1 + // v27 is y1 + 2048 + // v28 is y1 + 2048 + (ydim << 1); + + renderDrawLine( + x1 - 2048 + (xdim << 11), + y1 - 2048 + (ydim << 11), + x1 - 2048 + (xdim << 11), + y1 + 2048 + (ydim << 1), + 170); + + renderDrawLine( + x1 + (xdim << 11), + y1 + (ydim << 11), + x1 + (xdim << 11), + y1 + 2048 + (ydim << 11), + 170); + + renderDrawLine( + x1 + 2048 + (xdim << 11), + y1 + (ydim << 11), + x1 + 2048 + (xdim << 11), + y1 + 2048 + (ydim << 11), + 170); + } + } + } + } +#endif +} + +#ifdef _MSC_VER +#pragma warning(disable:4101) // this function produces a little bit too much noise +#endif + +static void G_DrawOverheadMap(int32_t cposx, int32_t cposy, int32_t czoom, int16_t cang) +{ + int32_t i, j, k, x1, y1, x2=0, y2=0, ox, oy; + int32_t z1, z2, startwall, endwall; + int32_t xvect, yvect, xvect2, yvect2; + char col; + uwallptr_t wal, wal2; + + int32_t tmpydim = (xdim*5)/8; + + renderSetAspect(65536, divscale16(tmpydim*320, xdim*200)); + + xvect = sintable[(-cang)&2047] * czoom; + yvect = sintable[(1536-cang)&2047] * czoom; + xvect2 = mulscale16(xvect, yxaspect); + yvect2 = mulscale16(yvect, yxaspect); + + //renderDisableFog(); + + // draw player position arrow + renderDrawLine(xdim << 11, (ydim << 11) - 20480, xdim << 11, (ydim << 11) + 20480, 24); + renderDrawLine((xdim << 11) - 20480, ydim << 11, xdim << 11, (ydim << 11) - 20480, 24); + renderDrawLine((xdim << 11) + 20480, ydim << 11, xdim << 11, (ydim << 11) - 20480, 24); + + short nPlayerSprite = PlayerList[nLocalPlayer].nSprite; + + int nPlayerZ = sprite[nPlayerSprite].z; + + //Draw red lines + for (i=numsectors-1; i>=0; i--) + { + if (!(show2dsector[i>>3]&pow2char[i&7])) continue; + + startwall = sector[i].wallptr; + endwall = sector[i].wallptr + sector[i].wallnum; + + z1 = sector[i].ceilingz; + z2 = sector[i].floorz; + + for (j=startwall, wal=(uwallptr_t)&wall[startwall]; jnextwall; + if (k < 0) continue; + + if (sector[wal->nextsector].ceilingz == z1 && sector[wal->nextsector].floorz == z2) + if (((wal->cstat|wall[wal->nextwall].cstat)&(16+32)) == 0) continue; + + if (nMapMode == 2) + col = 111; + else + col = 111 - min(klabs(z2 - nPlayerZ) >> 13, 12); + + ox = wal->x-cposx; + oy = wal->y-cposy; + x1 = dmulscale16(ox, xvect, -oy, yvect)+(xdim<<11); + y1 = dmulscale16(oy, xvect2, ox, yvect2)+(ydim<<11); + + wal2 = (uwallptr_t)&wall[wal->point2]; + ox = wal2->x-cposx; + oy = wal2->y-cposy; + x2 = dmulscale16(ox, xvect, -oy, yvect)+(xdim<<11); + y2 = dmulscale16(oy, xvect2, ox, yvect2)+(ydim<<11); + + renderDrawLine(x1, y1, x2, y2, col); + } + } + +#if 0 + renderEnableFog(); + + //Draw sprites + k = PlayerList[nLocalPlayer].nSprite; + if (!FURY) for (i=numsectors-1; i>=0; i--) + { + if (!(show2dsector[i>>3]&pow2char[i&7])) continue; + for (j=headspritesect[i]; j>=0; j=nextspritesect[j]) + { + spr = &sprite[j]; + + if (j == k || (spr->cstat&0x8000) || spr->cstat == 257 || spr->xrepeat == 0) continue; + + col = editorcolors[6]; //cyan + if (spr->cstat&1) col = editorcolors[5]; //magenta + + sprx = spr->x; + spry = spr->y; + + if ((spr->cstat&257) != 0) switch (spr->cstat&48) + { + case 0: + // break; + + ox = sprx-cposx; + oy = spry-cposy; + x1 = dmulscale16(ox, xvect, -oy, yvect); + y1 = dmulscale16(oy, xvect2, ox, yvect2); + + ox = (sintable[(spr->ang+512)&2047]>>7); + oy = (sintable[(spr->ang)&2047]>>7); + x2 = dmulscale16(ox, xvect, -oy, yvect); + y2 = dmulscale16(oy, xvect, ox, yvect); + + x3 = mulscale16(x2, yxaspect); + y3 = mulscale16(y2, yxaspect); + + renderDrawLine(x1-x2+(xdim<<11), y1-y3+(ydim<<11), + x1+x2+(xdim<<11), y1+y3+(ydim<<11), col); + renderDrawLine(x1-y2+(xdim<<11), y1+x3+(ydim<<11), + x1+x2+(xdim<<11), y1+y3+(ydim<<11), col); + renderDrawLine(x1+y2+(xdim<<11), y1-x3+(ydim<<11), + x1+x2+(xdim<<11), y1+y3+(ydim<<11), col); + break; + + case 16: + if (spr->picnum == LASERLINE) + { + x1 = sprx; + y1 = spry; + tilenum = spr->picnum; + xoff = picanm[tilenum].xofs + spr->xoffset; + if ((spr->cstat&4) > 0) xoff = -xoff; + k = spr->ang; + l = spr->xrepeat; + dax = sintable[k&2047]*l; + day = sintable[(k+1536)&2047]*l; + l = tilesiz[tilenum].x; + k = (l>>1)+xoff; + x1 -= mulscale16(dax, k); + x2 = x1+mulscale16(dax, l); + y1 -= mulscale16(day, k); + y2 = y1+mulscale16(day, l); + + ox = x1-cposx; + oy = y1-cposy; + x1 = dmulscale16(ox, xvect, -oy, yvect); + y1 = dmulscale16(oy, xvect2, ox, yvect2); + + ox = x2-cposx; + oy = y2-cposy; + x2 = dmulscale16(ox, xvect, -oy, yvect); + y2 = dmulscale16(oy, xvect2, ox, yvect2); + + renderDrawLine(x1+(xdim<<11), y1+(ydim<<11), + x2+(xdim<<11), y2+(ydim<<11), col); + } + + break; + + case 32: + tilenum = spr->picnum; + xoff = picanm[tilenum].xofs + spr->xoffset; + yoff = picanm[tilenum].yofs + spr->yoffset; + if ((spr->cstat&4) > 0) xoff = -xoff; + if ((spr->cstat&8) > 0) yoff = -yoff; + + k = spr->ang; + cosang = sintable[(k+512)&2047]; + sinang = sintable[k&2047]; + xspan = tilesiz[tilenum].x; + xrepeat = spr->xrepeat; + yspan = tilesiz[tilenum].y; + yrepeat = spr->yrepeat; + + dax = ((xspan>>1)+xoff)*xrepeat; + day = ((yspan>>1)+yoff)*yrepeat; + x1 = sprx + dmulscale16(sinang, dax, cosang, day); + y1 = spry + dmulscale16(sinang, day, -cosang, dax); + l = xspan*xrepeat; + x2 = x1 - mulscale16(sinang, l); + y2 = y1 + mulscale16(cosang, l); + l = yspan*yrepeat; + k = -mulscale16(cosang, l); + x3 = x2+k; + x4 = x1+k; + k = -mulscale16(sinang, l); + y3 = y2+k; + y4 = y1+k; + + ox = x1-cposx; + oy = y1-cposy; + x1 = dmulscale16(ox, xvect, -oy, yvect); + y1 = dmulscale16(oy, xvect2, ox, yvect2); + + ox = x2-cposx; + oy = y2-cposy; + x2 = dmulscale16(ox, xvect, -oy, yvect); + y2 = dmulscale16(oy, xvect2, ox, yvect2); + + ox = x3-cposx; + oy = y3-cposy; + x3 = dmulscale16(ox, xvect, -oy, yvect); + y3 = dmulscale16(oy, xvect2, ox, yvect2); + + ox = x4-cposx; + oy = y4-cposy; + x4 = dmulscale16(ox, xvect, -oy, yvect); + y4 = dmulscale16(oy, xvect2, ox, yvect2); + + renderDrawLine(x1+(xdim<<11), y1+(ydim<<11), + x2+(xdim<<11), y2+(ydim<<11), col); + + renderDrawLine(x2+(xdim<<11), y2+(ydim<<11), + x3+(xdim<<11), y3+(ydim<<11), col); + + renderDrawLine(x3+(xdim<<11), y3+(ydim<<11), + x4+(xdim<<11), y4+(ydim<<11), col); + + renderDrawLine(x4+(xdim<<11), y4+(ydim<<11), + x1+(xdim<<11), y1+(ydim<<11), col); + + break; + } + } + } + + renderDisableFog(); +#endif + + //Draw white lines + for (i=numsectors-1; i>=0; i--) + { + if (!(show2dsector[i>>3]&pow2char[i&7])) continue; + + startwall = sector[i].wallptr; + endwall = sector[i].wallptr + sector[i].wallnum; + z2 = sector[i].floorz; + + if (nMapMode == 2) + { + col = 111; + } + else + { + col = klabs(z2 - nPlayerZ) >> 13; + if (col > 15) + continue; + col = 111 - col; + } + + k = -1; + for (j=startwall, wal=(uwallptr_t)&wall[startwall]; jnextwall >= 0) continue; + + if (tilesiz[wal->picnum].x == 0) continue; + if (tilesiz[wal->picnum].y == 0) continue; + + if (j == k) + { + x1 = x2; + y1 = y2; + } + else + { + ox = wal->x-cposx; + oy = wal->y-cposy; + x1 = dmulscale16(ox, xvect, -oy, yvect)+(xdim<<11); + y1 = dmulscale16(oy, xvect2, ox, yvect2)+(ydim<<11); + } + + k = wal->point2; + wal2 = (uwallptr_t)&wall[k]; + ox = wal2->x-cposx; + oy = wal2->y-cposy; + x2 = dmulscale16(ox, xvect, -oy, yvect)+(xdim<<11); + y2 = dmulscale16(oy, xvect2, ox, yvect2)+(ydim<<11); + + renderDrawLine(x1, y1, x2, y2, col); + } + } + + //renderEnableFog(); + + videoSetCorrectedAspect(); + +#if 0 + for (TRAVERSE_CONNECT(p)) + { + if (ud.scrollmode && p == screenpeek) continue; + + auto const pPlayer = g_player[p].ps; + auto const pSprite = (uspriteptr_t)&sprite[pPlayer->i]; + + ox = pSprite->x - cposx; + oy = pSprite->y - cposy; + daang = (pSprite->ang - cang) & 2047; + if (p == screenpeek) + { + ox = 0; + oy = 0; + daang = 0; + } + x1 = mulscale16(ox, xvect) - mulscale16(oy, yvect); + y1 = mulscale16(oy, xvect2) + mulscale16(ox, yvect2); + + if (p == screenpeek || GTFLAGS(GAMETYPE_OTHERPLAYERSINMAP)) + { + if (pSprite->xvel > 16 && pPlayer->on_ground) + i = APLAYERTOP+(((int32_t) totalclock>>4)&3); + else + i = APLAYERTOP; + + i = VM_OnEventWithReturn(EVENT_DISPLAYOVERHEADMAPPLAYER, pPlayer->i, p, i); + + if (i < 0) + continue; + + j = klabs(pPlayer->truefz - pPlayer->pos.z) >> 8; + j = mulscale16(czoom * (pSprite->yrepeat + j), yxaspect); + + if (j < 22000) j = 22000; + else if (j > (65536<<1)) j = (65536<<1); + + rotatesprite_win((x1<<4)+(xdim<<15), (y1<<4)+(ydim<<15), j, daang, i, pSprite->shade, + P_GetOverheadPal(pPlayer), 0); + } + } +#endif +} + +void UpdateMap() +{ + if (sector[initsect].ceilingpal != 3 || (nPlayerTorch[nLocalPlayer] != 0)) { + MarkSectorSeen(initsect); + } +} + +void DrawMap() +{ + if (!nFreeze && nMapMode) { + //drawoverheadmap(initx, inity, lMapZoom, inita); + if (nMapMode == 2) + { + videoClearViewableArea(blackcol); + RefreshBackground(); + renderDrawMapView(initx, inity, lMapZoom, inita); + } + G_DrawOverheadMap(initx, inity, lMapZoom, inita); + } +} +END_PS_NS diff --git a/source/exhumed/src/map.h b/source/exhumed/src/map.h new file mode 100644 index 000000000..359dca0ca --- /dev/null +++ b/source/exhumed/src/map.h @@ -0,0 +1,37 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __map_h__ +#define __map_h__ + +#include "compat.h" + +BEGIN_PS_NS + +extern short bShowTowers; +extern int ldMapZoom; +extern int lMapZoom; + +void InitMap(); +void GrabMap(); +void UpdateMap(); +void DrawMap(); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/menu.cpp b/source/exhumed/src/menu.cpp new file mode 100644 index 000000000..ebd6204a1 --- /dev/null +++ b/source/exhumed/src/menu.cpp @@ -0,0 +1,2047 @@ +//------------------------------------------------------------------------- +/* +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 "build.h" +#include "exhumed.h" +#include "typedefs.h" +#include "player.h" +#include "sequence.h" +#include "menu.h" +#include "names.h" +#include "engine.h" +#include "keyboard.h" +#include "status.h" +#include "random.h" +#include "sound.h" +#include "names.h" +#include "init.h" +#include "ps_input.h" +#include "gun.h" +#include "view.h" +#include "object.h" +#include "light.h" +#include "cd.h" +#include "menu/menu.h" +#include + +#include + +#ifdef __WATCOMC__ +#include +#endif + +BEGIN_PS_NS + + +#define kSaveFileName "savgamea.sav" +#define kMaxSaveSlots 5 +#define kMaxSaveSlotChars 25 + +GameStat GameStats; + +short nCinemaSeen[30]; + +// this might be static within the DoPlasma function? +uint8_t * PlasmaBuffer; + +uint8_t energytile[66 * 66] = {0}; + +uint8_t cinemapal[768]; +short nLeft[50] = {0}; +int line; + +short SavePosition = -1; + +uint8_t *cur; +uint8_t *dest; + +unsigned int nSmokeBottom; +unsigned int nSmokeRight; +unsigned int nSmokeTop; +unsigned int nSmokeLeft; + +unsigned int nRandom = 0x41C6167E; +int dword_9AB57 = 0x1F; +short word_9AB5B = 0; + +int keytimer = 0; + +int plasma_A[5] = {0}; +int plasma_B[5] = {0}; +int plasma_C[5] = {0}; + +short nMenuKeys[] = { sc_N, sc_L, sc_M, sc_V, sc_Q, sc_None }; // select a menu item using the keys. 'N' for New Gane, 'V' for voume etc. 'M' picks Training for some reason... + + +void menu_ResetKeyTimer(); + +enum { + kMenuNewGame = 0, + kMenuLoadGame, + kMenuTraining, + kMenuVolume, + kMenuQuitGame, + kMenuMaxItems +}; + + +void ClearCinemaSeen() +{ + memset(nCinemaSeen, 0, sizeof(nCinemaSeen)); +} + +unsigned int menu_RandomBit2() +{ + unsigned int result = nRandom & 1; + + if ( --dword_9AB57 > 0 ) + { + nRandom = (result << 31) | (nRandom >> 1); + } + else + { + dword_9AB57 = 31; + nRandom ^= nRandom >> 4; + } + return result; +} + +int menu_RandomLong2() +{ + int randLong = 0; + + for (int i = 0; i < 32; i++) + { + int val = menu_RandomBit2(); + randLong *= 2; + randLong |= val; + } + + return randLong; +} + +void InitEnergyTile() +{ + memset(energytile, 96, sizeof(energytile)); +} + +void DoEnergyTile() +{ + nButtonColor += nButtonColor < 0 ? 8 : 0; + + auto energy1 = TileFiles.tileMakeWritable(kEnergy1); + auto energy2 = TileFiles.tileMakeWritable(kEnergy2); + uint8_t *ptr1 = energy1 + 1984; + uint8_t *ptr2 = energy2 + 2048; + + short nColor = nButtonColor + 161; + + int i, j; + + for (i = 0; i < 32; i++) + { + memset(ptr1, nColor, 64); + memset(ptr2, nColor, 64); + + ptr1 -= 64; + ptr2 += 64; + + nColor++; + + if (nColor >= 168) { + nColor = 160; + } + } + + tileInvalidate(kEnergy1, -1, -1); + + if (nSmokeSparks) + { + uint8_t *c = &energytile[67]; // skip a line + uint8_t *ptrW = energy2; + + for (i = 0; i < 64; i++) + { + for (j = 0; j < 64; j++) + { + uint8_t val = *c; + + if (val != 96) + { + if (val > 158) { + *ptrW = val - 1; + } + else { + *ptrW = 96; + } + } + else + { + if (menu_RandomBit2()) { + *ptrW = *c; + } + else + { + uint8_t al = *(c + 1); + uint8_t ah = *(c - 1); + + if (al <= ah) { + al = ah; + } + + uint8_t cl = al; + + al = *(c - 66); + if (cl <= al) { + cl = al; + } + + al = *(c + 66); + if (cl <= al) { + cl = al; + } + + al = *(c + 66); + if (cl <= al) { + cl = al; + } + + al = *(c + 66); + if (cl <= al) { + cl = al; + } + + al = *(c - 65); + if (cl <= al) { + cl = al; + } + + al = *(c - 67); + if (cl > al) { + al = cl; + } + + cl = al; + + if (al <= 159) { + *ptrW = 96; + } + else + { + if (!menu_RandomBit2()) { + cl--; + } + + *ptrW = cl; + } + } + } + + c++; + ptrW++; + } + + c += 2; + } + + c = &energytile[67]; + ptrW = energy2; + + // copy back to energytile[] + for (i = 0; i < 64; i++) + { + memcpy(c, ptrW, 64); + c += 66; + ptrW += 64; + } + + ptrW = energy2; + + // kEnergy2 is 64 x 64 + for (i = 0; i < 4096; i++) + { + if (ptrW[i] == 96) { + ptrW[i] = 255; // -1? + } + } + + word_9AB5B--; + if (word_9AB5B <= 0) + { + int randSize = (RandomSize(5) & 0x1F) + 16; + int randSize2 = (RandomSize(5) & 0x1F) + 16; + + int val = randSize << 5; + val += randSize; + val *= 2; + val += randSize2; + + assert(val < 4356); + + energytile[val] = 175; + word_9AB5B = 1; + } + tileInvalidate(kEnergy2, -1, -1); + } +} + +int nPlasmaTile = kTile4092; +int nLogoTile; + +#define kPlasmaWidth 320 +#define kPlasmaHeight 80 + +int nextPlasmaTic; + +void menu_DoPlasma() +{ + int ptile = nPlasmaTile; + if (totalclock >= nextPlasmaTic || !PlasmaBuffer) + { + nextPlasmaTic = (int)totalclock + 4; + + if (!nLogoTile) + nLogoTile = EXHUMED ? kExhumedLogo : kPowerslaveLogo; + + if (!PlasmaBuffer) + { + auto pixels = TileFiles.tileCreate(kTile4092, kPlasmaWidth, kPlasmaHeight); + memset(pixels, 96, kPlasmaWidth * kPlasmaHeight); + + PlasmaBuffer = TileFiles.tileCreate(kTile4093, kPlasmaWidth, kPlasmaHeight); + memset(PlasmaBuffer, 96, kPlasmaWidth * kPlasmaHeight); + + nSmokeLeft = 160 - tilesiz[nLogoTile].x / 2; + nSmokeRight = nSmokeLeft + tilesiz[nLogoTile].x; + + nSmokeTop = 40 - tilesiz[nLogoTile].y / 2; + nSmokeBottom = nSmokeTop + tilesiz[nLogoTile].y - 1; + + //uint32_t t = time(0) << 16; + //uint32_t t2 = time(0) | t; + nRandom = timerGetTicksU64(); + + for (int i = 0; i < 5; i++) + { + int logoWidth = tilesiz[nLogoTile].x; + plasma_C[i] = (nSmokeLeft + rand() % logoWidth) << 16; + plasma_B[i] = (menu_RandomLong2() % 327680) + 0x10000; + + if (menu_RandomBit2()) { + plasma_B[i] = -plasma_B[i]; + } + + plasma_A[i] = menu_RandomBit2(); + } + } + + videoClearScreen(overscanindex); + + + uint8_t* plasmapix = const_cast(tilePtr(nPlasmaTile)); + uint8_t* r_ebx = plasmapix + 81; + const uint8_t* r_edx = tilePtr(nPlasmaTile ^ 1) + 81; // flip between value of 4092 and 4093 with xor + + for (int x = 0; x < kPlasmaWidth - 2; x++) + // for (int x = 1; x < 318; x++) + { + // for (int y = 1; y < 79; y++) + for (int y = 0; y < kPlasmaHeight - 2; y++) + { + uint8_t al = *r_edx; + + if (al != 96) + { + if (al > 158) { + *r_ebx = al - 1; + } + else { + *r_ebx = 96; + } + } + else + { + if (menu_RandomBit2()) { + *r_ebx = *r_edx; + } + else + { + uint8_t al = *(r_edx + 1); + uint8_t cl = *(r_edx - 1); + + if (al <= cl) { + al = cl; + } + + cl = al; + al = *(r_edx - 80); + if (cl <= al) { + cl = al; + } + + al = *(r_edx + 80); + if (cl <= al) { + cl = al; + } + + al = *(r_edx + 80); + if (cl <= al) { + cl = al; + } + + al = *(r_edx + 80); + if (cl <= al) { + cl = al; + } + + al = *(r_edx - 79); + if (cl > al) { + al = cl; + } + + cl = *(r_edx - 81); + if (al <= cl) { + al = cl; + } + + cl = al; + + if (al <= 159) { + *r_ebx = 96; + } + else + { + if (!menu_RandomBit2()) { + cl--; + } + + *r_ebx = cl; + } + } + } + + // before restarting inner loop + r_edx++; + r_ebx++; + } + + // before restarting outer loop + r_edx += 2; + r_ebx += 2; + } + + auto logopix = tilePtr(nLogoTile); + + for (int j = 0; j < 5; j++) + { + int pB = plasma_B[j]; + int pC = plasma_C[j]; + int badOffset = (pC >> 16) < nSmokeLeft || (pC >> 16) >= nSmokeRight; + + const uint8_t* ptr3 = (logopix + ((pC >> 16) - nSmokeLeft)* tilesiz[nLogoTile].y); + + plasma_C[j] += plasma_B[j]; + + if ((pB > 0 && (plasma_C[j] >> 16) >= nSmokeRight) || (pB < 0 && (plasma_C[j] >> 16) <= nSmokeLeft)) + { + int esi = plasma_A[j]; + plasma_B[j] = -plasma_B[j]; + plasma_A[j] = esi == 0; + } + + if (badOffset) + continue; + + unsigned int nSmokeOffset = 0; + + if (plasma_A[j]) + { + nSmokeOffset = nSmokeTop; + + while (nSmokeOffset < nSmokeBottom) + { + uint8_t al = *ptr3; + if (al != 255 && al != 96) { + break; + } + + nSmokeOffset++; + ptr3++; + } + } + else + { + nSmokeOffset = nSmokeBottom; + + ptr3 += tilesiz[nLogoTile].y - 1; + + while (nSmokeOffset > nSmokeTop) + { + uint8_t al = *ptr3; + if (al != 255 && al != 96) { + break; + } + + nSmokeOffset--; + ptr3--; + } + } + + uint8_t* v28 = plasmapix + (80 * (plasma_C[j] >> 16)); + v28[nSmokeOffset] = 175; + } + + tileInvalidate(nPlasmaTile, -1, -1); + + // flip between tile 4092 and 4093 + if (nPlasmaTile == kTile4092) { + nPlasmaTile = kTile4093; + } + else if (nPlasmaTile == kTile4093) { + nPlasmaTile = kTile4092; + } + } + overwritesprite(0, 0, ptile, 0, 2, kPalNormal); + overwritesprite(160, 40, nLogoTile, 0, 3, kPalNormal); + + // draw the fire urn/lamp thingies + int dword_9AB5F = ((int)totalclock/16) & 3; + + overwritesprite(50, 150, kTile3512 + dword_9AB5F, 0, 3, kPalNormal); + overwritesprite(270, 150, kTile3512 + ((dword_9AB5F + 2) & 3), 0, 3, kPalNormal); +} + + +int8_t MapLevelOffsets[] = { 0, 50, 10, 20, 0, 45, -20, 20, 5, 0, -10, 10, 30, -20, 0, 20, 0, 0, 0, 0 }; + +struct TILEFRAMEDEF +{ + short nTile; + short xOffs; + short yOffs; +}; + +// 22 bytes +struct MapNamePlaque +{ + short xPos; + short yPos; + TILEFRAMEDEF tiles[2]; + TILEFRAMEDEF text; +}; + +MapNamePlaque mapNamePlaques[] = { + { 100, 170, kTile3376, 0, 0, kTile3377, 0, 0, kTile3411, 18, 6 }, + { 230, 10, kTile3378, 0, 0, kTile3379, 0, 0, kTile3414, 18, 6 }, // DENDUR (level 2) + { 180, 125, kTile3380, 0, 0, kTile3381, 0, 0, kTile3417, 18, 6 }, // Kalabash + { 10, 95, kTile3382, 0, 0, kTile3383, 0, 0, kTile3420, 18, 6 }, + { 210, 160, kTile3384, 0, 0, kTile3385, 0, 0, kTile3423, 18, 6 }, + { 10, 110, kTile3371, 0, 0, kTile3386, 0, 0, kTile3426, 18, 6 }, + { 10, 50, kTile3387, 0, 0, kTile3388, 0, 0, kTile3429, 18, 6 }, + { 140, 0, kTile3389, 0, 0, kTile3390, 0, 0, kTile3432, 18, 6 }, + { 30, 20, kTile3391, 0, 0, kTile3392, 0, 0, kTile3435, 18, 6 }, + { 200, 150, kTile3409, 0, 0, kTile3410, 0, 0, kTile3418, 20, 4 }, + { 145, 170, kTile3393, 0, 0, kTile3394, 0, 0, kTile3438, 18, 6 }, + { 80, 80, kTile3395, 0, 0, kTile3396, 0, 0, kTile3441, 18, 6 }, + { 15, 0, kTile3397, 0, 0, kTile3398, 0, 0, kTile3444, 18, 5 }, + { 220, 35, kTile3399, 0, 0, kTile3400, 0, 0, kTile3447, 18, 6 }, + { 190, 40, kTile3401, 0, 0, kTile3402, 0, 0, kTile3450, 18, 6 }, + { 20, 130, kTile3403, 0, 0, kTile3404, 0, 0, kTile3453, 19, 6 }, + { 220, 160, kTile3405, 0, 0, kTile3406, 0, 0, kTile3456, 18, 6 }, + { 20, 10, kTile3407, 0, 0, kTile3408, 0, 0, kTile3459, 18, 6 }, + { 200, 10, kTile3412, 0, 0, kTile3413, 0, 0, kTile3419, 18, 5 }, + { 20, 10, kTile3415, 0, 0, kTile3416, 0, 0, kTile3421, 19, 4 } +}; + +// 3 different types of fire, each with 4 frames +TILEFRAMEDEF FireTiles[3][4] = { + {{ kTile3484,0,3 },{ kTile3485,0,0 },{ kTile3486,0,3 },{ kTile3487,0,0 }}, + {{ kTile3488,1,0 },{ kTile3489,1,0 },{ kTile3490,0,1 },{ kTile3491,1,1 }}, + {{ kTile3492,1,2 },{ kTile3493,1,0 },{ kTile3494,1,2 },{ kTile3495,1,0 }} +}; + +struct Fire +{ + short nFireType; + short xPos; + short yPos; +}; + +// 20 bytes +struct MapFire +{ + short nFires; + Fire fires[3]; +}; + +/* + level 1 - 3 fires + level 2 - 3 fires + level 3 - 1 fire + +*/ + +MapFire MapLevelFires[] = { + 3, {{0, 107, 95}, {1, 58, 140}, {2, 28, 38}}, + 3, {{2, 240, 0}, {0, 237, 32}, {1, 200, 30}}, + 2, {{2, 250, 57}, {0, 250, 43}, {2, 200, 70}}, + 2, {{1, 82, 59}, {2, 84, 16}, {0, 10, 95}}, + 2, {{2, 237, 50}, {1, 215, 42}, {1, 210, 50}}, + 3, {{0, 40, 7}, {1, 75, 6}, {2, 100, 10}}, + 3, {{0, 58, 61}, {1, 85, 80}, {2, 111, 63}}, + 3, {{0, 260, 65}, {1, 228, 0}, {2, 259, 15}}, + 2, {{0, 81, 38}, {2, 58, 38}, {2, 30, 20}}, + 3, {{0, 259, 49}, {1, 248, 76}, {2, 290, 65}}, + 3, {{2, 227, 66}, {0, 224, 98}, {1, 277, 30}}, + 2, {{0, 100, 10}, {2, 48, 76}, {2, 80, 80}}, + 3, {{0, 17, 2}, {1, 29, 49}, {2, 53, 28}}, + 3, {{0, 266, 42}, {1, 283, 99}, {2, 243, 108}}, + 2, {{0, 238, 19}, {2, 240, 92}, {2, 190, 40}}, + 2, {{0, 27, 0}, {1, 70, 40}, {0, 20, 130}}, + 3, {{0, 275, 65}, {1, 235, 8}, {2, 274, 6}}, + 3, {{0, 75, 45}, {1, 152, 105}, {2, 24, 68}}, + 3, {{0, 290, 25}, {1, 225, 63}, {2, 260, 110}}, + 0, {{1, 20, 10}, {1, 20, 10}, {1, 20, 10}} +}; + +int menu_DrawTheMap(int nLevel, int nLevelNew, int nLevelBest) +{ + int i; + int x = 0; + int var_2C = 0; + int nIdleSeconds = 0; + int bFadeDone = kFalse; + + int startTime = (int)totalclock; + + ClearAllKeys(); + UnMaskStatus(); + videoSetViewableArea(0, 0, xdim - 1, ydim - 1); + + // 0-offset the level numbers + nLevel--; + nLevelNew--; + nLevelBest--; + + if (nLevel >= kMap20) { // max single player levels + return -1; + } + + if (nLevelNew >= kMap20) { + return -1; + } + + if (nLevel < 0) { + nLevel = 0; + } + + if (nLevelNew < 0) { + nLevelNew = nLevel; + } + + int curYPos = MapLevelOffsets[nLevel] + (200 * (nLevel / 2)); + int destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); + + if (curYPos < destYPos) { + var_2C = 2; + } + + if (curYPos > destYPos) { + var_2C = -2; + } + + int runtimer = (int)totalclock; + + // User has 12 seconds to do something on the map screen before loading the current level + while (nIdleSeconds < 12) + { + HandleAsync(); + + if (((int)totalclock - startTime) / kTimerTicks) + { + nIdleSeconds++; + startTime = (int)totalclock; + } + + int moveTimer = (int)totalclock; + + int tileY = curYPos; + + // Draw the background screens + for (i = 0; i < 10; i++) + { + overwritesprite(x, tileY, kTile3353 + i, 0, 2, kPalNormal); + tileY -= 200; + } + + // for each level - drawing the 'level completed' on-fire smoke markers + for (i = 0; i < kMap20; i++) + { + int screenY = (i >> 1) * -200; + + if (nLevelBest >= i) // check if the player has finished this level + { + for (int j = 0; j < MapLevelFires[i].nFires; j++) + { + int nFireFrame = (((int)totalclock >> 4) & 3); + assert(nFireFrame >= 0 && nFireFrame < 4); + + int nFireType = MapLevelFires[i].fires[j].nFireType; + assert(nFireType >= 0 && nFireType < 3); + + int nTile = FireTiles[nFireType][nFireFrame].nTile; + int smokeX = MapLevelFires[i].fires[j].xPos + FireTiles[nFireType][nFireFrame].xOffs; + int smokeY = MapLevelFires[i].fires[j].yPos + FireTiles[nFireType][nFireFrame].yOffs + curYPos + screenY; + + overwritesprite(smokeX, smokeY, nTile, 0, 2, kPalNormal); + } + } + + int t = ((((int)totalclock & 16) >> 4)); + + int nTile = mapNamePlaques[i].tiles[t].nTile; + + int nameX = mapNamePlaques[i].xPos + mapNamePlaques[i].tiles[t].xOffs; + int nameY = mapNamePlaques[i].yPos + mapNamePlaques[i].tiles[t].yOffs + curYPos + screenY; + + // Draw level name plaque + overwritesprite(nameX, nameY, nTile, 0, 2, kPalNormal); + + int8_t shade = 96; + + if (nLevelNew == i) + { + shade = (Sin(16 * (int)totalclock) + 31) >> 8; + } + else if (nLevelBest >= i) + { + shade = 31; + } + + int textY = mapNamePlaques[i].yPos + mapNamePlaques[i].text.yOffs + curYPos + screenY; + int textX = mapNamePlaques[i].xPos + mapNamePlaques[i].text.xOffs; + nTile = mapNamePlaques[i].text.nTile; + + // draw the text, alternating between red and black + overwritesprite(textX, textY, nTile, shade, 2, kPalNormal); + } + + videoNextPage(); + if (!bFadeDone) + { + bFadeDone = kTrue; + FadeIn(); + moveTimer = (int)totalclock; + } + + if (curYPos == destYPos) + { + if (inputState.GetKeyStatus(sc_UpArrow)) + { + inputState.ClearKeyStatus(sc_UpArrow); + + if (nLevelNew <= nLevelBest) + { + nLevelNew++; + assert(nLevelNew < 20); + + destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); + + if (curYPos <= destYPos) { + var_2C = 2; + } + else { + var_2C = -2; + } + + nIdleSeconds = 0; + } + } + + if (inputState.GetKeyStatus(sc_DownArrow)) + { + inputState.ClearKeyStatus(sc_DownArrow); + + if (nLevelNew > 0) + { + nLevelNew--; + assert(nLevelNew >= 0); + + destYPos = MapLevelOffsets[nLevelNew] + (200 * (nLevelNew / 2)); + + if (curYPos <= destYPos) { + var_2C = 2; + } + else { + var_2C = -2; + } + + nIdleSeconds = 0; + } + } + + if (inputState.CheckAllInput()) + { + return nLevelNew + 1; + } + } + else + { + // scroll the map every couple of ms + if (totalclock - runtimer >= (kTimerTicks / 32)) { + curYPos += var_2C; + runtimer = (int)totalclock; + } + + //curYPos += var_2C * (((int)totalclock - moveTimer) / 2); + + if (inputState.CheckAllInput()) + { + if (var_2C < 8) { + var_2C *= 2; + } + + } + + if (curYPos > destYPos&& var_2C > 0) { + curYPos = destYPos; + } + + if (curYPos < destYPos && var_2C < 0) { + curYPos = destYPos; + } + + nIdleSeconds = 0; + } + } + + MySetView(nViewLeft, nViewTop, nViewRight, nViewBottom); + return nLevelNew + 1; +} + +void menu_AdjustVolume() +{ + int nOption = 1; + int var_8 = 0; + + while (1) + { + HandleAsync(); + + menu_DoPlasma(); + + overwritesprite(80, 50, kMenuMusicTile, (Sin((int)totalclock << 4) >> 9) * (nOption == 0), 2, kPalNormal); + overwritesprite(55, 75, kMenuBlankTitleTile, 0, 2, kPalNormal); + + /* + seq_DrawGunSequence( + SeqOffsets[kSeqSlider], // eax + gMusicVolume % 3, // pick one of 3 frames? + (gMusicVolume >> 1) - 93, // ebx. must be x??? + -22, + 0, + 0);*/ + + overwritesprite(80, 110, kMenuSoundFxTile, (Sin((int)totalclock << 4) >> 9) * (nOption == 1), 2, kPalNormal); + overwritesprite(55, 135, kMenuBlankTitleTile, 0, 2, kPalNormal); + + seq_DrawGunSequence( + SeqOffsets[kSeqSlider], + snd_fxvolume % 3, + (snd_fxvolume / 2) - 93, + 38, + 0, + 0); + + int y = (60 * nOption) + 38; + + overwritesprite(60, y, kMenuCursorTile, 0, 2, kPalNormal); + overwritesprite(206, y, kMenuCursorTile, 0, 10, kPalNormal); + + videoNextPage(); + + if (inputState.CheckAllInput()) + { + PlayLocalSound(StaticSound[kSound33], 0); + return; + } + +#if 0 + if (I_MenuUp()) + { + I_MenuUpClear(); + if (nOption > 0) + { + nOption--; + PlayLocalSound(StaticSound[kSound35], 0); + } + } + + if (I_MenuDown()) + { + I_MenuDownClear(); + if (nOption < 1) + { + nOption++; + PlayLocalSound(StaticSound[kSound35], 0); + } + } + + if ((int)totalclock <= var_8) { + continue; + } + + var_8 = (int)totalclock + 5; + + if (I_MenuLeft()) + { + I_MenuLeftClear(); + } + + if (I_MenuRight()) + { + I_MenuRightClear(); + } +#endif + + if (GetLocalSound() != 23) { + continue; + } + else { + StopLocalSound(); + } + } +} + +int menu_NewGameMenu() +{ +#if 0 + const char endMark = 0xF; + char nameList[5][25]; + int nNameLen = sizeof(nameList); + + int nNameOffset = 0; // char index into slot name string + + //int nPages = numpages; + + int arg_3E = tilesiz[kMenuBlankTitleTile].x - 10; + + int nSlot = 0; + + FILE *fp = fopen(kSaveFileName, "rb"); + if (fp == NULL) + { + memset(nameList, 0, nNameLen); + memset(&GameStats, 0, sizeof(GameStat)); + + fp = fopen(kSaveFileName, "wb+"); + if (fp != NULL) + { + fwrite(nameList, nNameLen, 1, fp); + fwrite(&GameStats, 75, 1, fp); //fwrite(&GameStats, 75, 5, fp); // CHECKME! the size + fwrite(&endMark, sizeof(endMark), 1, fp); + + fclose(fp); + } + } + else + { + int nRead = fread(nameList, 1, nNameLen, fp); + if (nRead != nNameLen) + { + memset(nameList, 0, nNameLen); + } + + fclose(fp); + } + + // while (1) + { + ClearAllKeys(); + + while (1) + { + HandleAsync(); + menu_DoPlasma(); + + int y = (tilesiz[kMenuBlankTitleTile].y - (tilesiz[kMenuBlankTitleTile].y / 2) / 2) + 65; + rotatesprite(160 << 16, y << 16, 0x10000, 0, kMenuNewGameTile, 0, 0, 2, 0, 0, xdim, ydim); + + int edi = 0; + + int arg_4A = 90; + int arg_4E = 98; + + // Loop #3 + for (int i = 0; i < 5; i++) + { + int8_t shade = ((Sin((int)totalclock << 4) >> 9) * (i == nSlot)) + ((i != nSlot) * 31); + + overwritesprite(55, arg_4A, kMenuBlankTitleTile, shade, 2, kPalNormal); + myprintext(63, arg_4E, nameList[i], 0); + + arg_4E += 22; + arg_4A += 22; + + edi++; + } + + edi = nSlot * 22; + + // draw selection markers + overwritesprite(35, edi + 78, kMenuCursorTile, 0, 2, kPalNormal); + overwritesprite(233, edi + 78, kMenuCursorTile, 0, 10, kPalNormal); + videoNextPage(); + + //nPages--; + //if (nPages > 0) { + // continue; + //} + + if (I_EscapeTrigger()) + { + PlayLocalSound(StaticSound[kSound33], 0); + I_EscapeTriggerClear(); + return -1; + } + + if (I_MenuUp()) + { + I_MenuUpClear(); + PlayLocalSound(StaticSound[kSound35], 0); + if (nSlot <= 0) { + nSlot = 4; + } + else { + nSlot--; + } + + ClearAllKeys(); + continue; + } + + if (I_MenuDown()) + { + I_MenuDownClear(); + PlayLocalSound(StaticSound[kSound35], 0); + if (nSlot >= 4) { + nSlot = 0; + } + else { + nSlot++; + } + + inputState.ClearAllKeyStatus(); + continue; + } + + if (I_AdvanceTrigger() || inputState.keyBufferWaiting()) + { + break; + } + } + } + + PlayLocalSound(StaticSound[kSound33], 0); + if (!inputState.keyBufferWaiting()) { + inputState.ClearAllKeyStatus(); + } + + char *pName = nameList[nSlot]; + int nNameLength = strlen(pName); + + memset(pName, 0, nNameLength); + + menu_DoPlasma(); + overwritesprite(55, (nSlot * 22) + 90, kMenuBlankTitleTile, 0, 2, kPalNormal); + + int arg_5A = 90; + int arg_52 = 98; + + for (int i = 0; i < 5; i++) + { + overwritesprite(55, arg_5A, kMenuBlankTitleTile, (i != nSlot) * 31, 2, kPalNormal); + myprintext(63, arg_52, nameList[i], 0); + + arg_52 += 22; + arg_5A += 22; + } + + int x = 35; + int y = (nSlot * 22) + 78; + + while (1) + { + HandleAsync(); + + overwritesprite(x, y, kMenuCursorTile, 0, 2, kPalNormal); + overwritesprite(233, y, kMenuCursorTile, 0, 10, kPalNormal); + videoNextPage(); + + char ch = 0; + +check_keys: + if (inputState.keyBufferWaiting()) + { + HandleAsync(); + + ch = inputState.keyGetChar(); + if (!ch) + { + inputState.keyGetChar(); //??? + goto check_keys; + } + + // handle key input + if (ch == asc_Enter) + { + // loc_39ACA: + nameList[nSlot][nNameOffset] = 0; + + PlayLocalSound(StaticSound[kSound33], 0); + inputState.ClearKeyStatus(sc_Return); + + if (nameList[nSlot][0] == 0) { + return -1; + } + + if (nNameLength) // does the save slot already exist? + { + menu_DoPlasma(); + if (Query(2, 4, "Overwrite existing game?", "Y/N", 'Y', 13, 'N', 27) >= 2) { + return -1; + } + } + + FILE *fp = fopen(kSaveFileName, "rb+"); + if (fp == NULL) { + return -1; + } + + memset(&GameStats, 0, sizeof(GameStat)); + GameStats.nWeapons = 1; + GameStats.nMap = 1; + + fwrite(nameList, sizeof(nameList), 1, fp); + fseek(fp, sizeof(nameList), SEEK_SET); + fseek(fp, nSlot * sizeof(GameStat), SEEK_CUR); + fwrite(&GameStats, sizeof(GameStat), 1, fp); + fclose(fp); + return nSlot; + } + else + { + // Enter wasn't pressed + PlayLocalSound(4, 0); // ?? + + if (ch == asc_BackSpace) + { + nameList[nSlot][nNameOffset] = 0; + + if (nNameOffset > 0) { + nNameOffset--; + } + + nameList[nSlot][nNameOffset] = 0; + } + else if (ch == asc_Escape) + { + PlayLocalSound(StaticSound[kSound33], 0); + inputState.ClearAllKeyStatus(); + inputState.keyFlushChars(); + return -1; + } + else + { + // check if a slot name is being typed + if ((ch >= '0' && ch <= '9') + || (ch >= 'A' && ch <= 'Z') + || (ch >= 'a' && ch <= 'z') + || (ch == ' ')) + { + ch = toupper(ch); + if (nNameOffset < 24) // n chars per slot name + { + nameList[nSlot][nNameOffset] = ch; + nNameOffset++; + nameList[nSlot][nNameOffset] = '\0'; // null terminate in the new offset + + int nLen = MyGetStringWidth(nameList[nSlot]); + if (nLen > arg_3E) + { + nNameOffset--; + nameList[nSlot][nNameOffset] = '\0'; + } + } + } + } + } + } + + // loc_399FD: + menu_DoPlasma(); + + int arg_5E = ((int)totalclock / 30) & 1; + + int y = 90; + int arg_42 = 98; + + for (int i = 0; i < 5; i++) + { + overwritesprite(55, y, kMenuBlankTitleTile, (i != nSlot) * 31, 2, kPalNormal); + int nTextWidth = myprintext(63, arg_42, nameList[i], 0); + + // flash a full-stop to show the current typing position + if (arg_5E != 0 && nSlot == i) + { + myprintext(nTextWidth, arg_42, ".", 0); + } + + arg_42 += 22; + y += 22; + } + } +#endif + return 0; +} + +int menu_LoadGameMenu() +{ +#if 0 + char nameList[5][25]; + + int nSlot = 0; + + FILE *fp = fopen(kSaveFileName, "rb"); + if (fp == NULL) + { + memset(nameList, 0, sizeof(nameList)); + } + else + { + fread(nameList, sizeof(nameList), 1, fp); + fclose(fp); + } + + +#endif + return 0; +} + +void menu_GameLoad2(FILE *fp, bool bIsDemo) +{ + if (bIsDemo) + { + demo_header header; + fread(&header, 1, sizeof(demo_header), fp); + + GameStats.nMap = header.nMap; + GameStats.nWeapons = header.nWeapons; + GameStats.nCurrentWeapon = header.nCurrentWeapon; + GameStats.clip = header.clip; + GameStats.items = header.items; + GameStats.player.nHealth = header.nHealth; + GameStats.player.field_2 = header.field_2; + GameStats.player.nAction = header.nAction; + GameStats.player.nSprite = header.nSprite; + GameStats.player.bIsMummified = header.bIsMummified; + GameStats.player.someNetVal = header.someNetVal; + GameStats.player.invincibility = header.invincibility; + GameStats.player.nAir = header.nAir; + GameStats.player.nSeq = header.nSeq; + GameStats.player.nMaskAmount = header.nMaskAmount; + GameStats.player.keys = header.keys; + GameStats.player.nMagic = header.nMagic; + Bmemcpy(GameStats.player.items, header.item, sizeof(header.item)); + Bmemcpy(GameStats.player.nAmmo, header.nAmmo, sizeof(header.nAmmo)); + Bmemcpy(GameStats.player.pad, header.pad, sizeof(header.pad)); + GameStats.player.nCurrentWeapon = header.nCurrentWeapon2; + GameStats.player.field_3FOUR = header.field_3FOUR; + GameStats.player.bIsFiring = header.bIsFiring; + GameStats.player.field_38 = header.field_38; + GameStats.player.field_3A = header.field_3A; + GameStats.player.field_3C = header.field_3C; + GameStats.player.nRun = header.nRun; + GameStats.nLives = header.nLives; + } + else + fread(&GameStats, sizeof(GameStats), 1, fp); + + nPlayerWeapons[nLocalPlayer] = GameStats.nWeapons; + + PlayerList[nLocalPlayer].nCurrentWeapon = GameStats.nCurrentWeapon; + nPlayerClip[nLocalPlayer] = GameStats.clip; + + int nPistolBullets = PlayerList[nLocalPlayer].nAmmo[kWeaponPistol]; + if (nPistolBullets >= 6) { + nPistolBullets = 6; + } + + nPistolClip[nLocalPlayer] = nPistolBullets; + + memcpy(&PlayerList[nLocalPlayer], &GameStats.player, sizeof(Player)); + + nPlayerItem[nLocalPlayer] = GameStats.items; + nPlayerLives[nLocalPlayer] = GameStats.nLives; + + SetPlayerItem(nLocalPlayer, nPlayerItem[nLocalPlayer]); + CheckClip(nLocalPlayer); +} + +short menu_GameLoad(int nSlot) +{ + memset(&GameStats, 0, sizeof(GameStats)); + + FILE *fp = fopen(kSaveFileName, "rb"); + if (fp == NULL) { + return 0; + } + + fseek(fp, 125, SEEK_SET); + fseek(fp, nSlot * sizeof(GameStats), SEEK_CUR); + + menu_GameLoad2(fp); + fclose(fp); + + return GameStats.nMap; +} + +void menu_GameSave2(FILE *fp) +{ + memset(&GameStats, 0, sizeof(GameStats)); + + GameStats.nMap = (uint8_t)levelnew; + GameStats.nWeapons = nPlayerWeapons[nLocalPlayer]; + GameStats.nCurrentWeapon = PlayerList[nLocalPlayer].nCurrentWeapon; + GameStats.clip = nPlayerClip[nLocalPlayer]; + GameStats.items = nPlayerItem[nLocalPlayer]; + GameStats.nLives = nPlayerLives[nLocalPlayer]; + + memcpy(&GameStats.player, &PlayerList[nLocalPlayer], sizeof(GameStats.player)); + + fwrite(&GameStats, sizeof(GameStats), 1, fp); +} + +void menu_GameSave(int nSaveSlot) +{ + if (nSaveSlot < 0) { + return; + } + + FILE *fp = fopen(kSaveFileName, "rb+"); + if (fp != NULL) + { + fseek(fp, 125, SEEK_SET); // skip save slot names + fseek(fp, sizeof(GameStat) * nSaveSlot, SEEK_CUR); + menu_GameSave2(fp); + fclose(fp); + } +} + +#define kMaxCinemaPals 16 +const char *cinpalfname[kMaxCinemaPals] = { + "3454.pal", + "3452.pal", + "3449.pal", + "3445.pal", + "set.pal", + "3448.pal", + "3446.pal", + "hsc1.pal", + "2972.pal", + "2973.pal", + "2974.pal", + "2975.pal", + "2976.pal", + "heli.pal", + "2978.pal", + "terror.pal" +}; + +int linecount; +int nextclock; +short nHeight; +short nCrawlY; +short cinematile; + + +// TODO - moveme +int LoadCinemaPalette(int nPal) +{ + nPal--; + + if (nPal < 0 || nPal >= kMaxCinemaPals) { + return -2; + } + + // original code strcpy'd into a buffer first... + + auto hFile = fileSystem.OpenFileReader(cinpalfname[nPal], 0); + if (!hFile.isOpen()) { + return -2; + } + + hFile.Read(cinemapal, sizeof(cinemapal)); + + for (auto &c : cinemapal) + c <<= 2; + + return nPal; +} + +//int IncrementCinemaFadeIn() +//{ +// dest = cinemapal; +// cur = curpal; +// +// int ebx = 0; +// +// for (int i = 0; i < 768; i++) +// { +// ebx++; +// +// if (*cur < *dest) +// { +// (*cur)++; +// } +// else if (*cur == *dest) +// { +// ebx--; +// } +// else +// { +// (*cur)--; +// } +// +// cur++; +// dest++; +// } +// +// MySetPalette(curpal); +// return ebx; +//} + +void CinemaFadeIn() +{ + BlackOut(); + + paletteSetColorTable(ANIMPAL, cinemapal); + videoSetPalette(0, ANIMPAL, 2+8); + +#ifdef USE_OPENGL + if (videoGetRenderMode() >= REND_POLYMOST) + { + videoNextPage(); + return; + } +#endif + + int val; + + do + { + val = DoFadeIn(); + WaitTicks(2); + + // need to page flip in each iteration of the loop for non DOS version + videoNextPage(); + + } while (val > 0); +} + +void ComputeCinemaText(int nLine) +{ + linecount = 0; + + while (1) + { + if (!strcmp(gString[linecount + nLine], "END")) { + break; + } + + int nWidth = MyGetStringWidth(gString[linecount + nLine]); + nLeft[linecount] = 160 - nWidth / 2; + + linecount++; + } + + nCrawlY = 199; + nHeight = linecount * 10; + + ClearAllKeys(); +} + +void ReadyCinemaText(uint16_t nVal) +{ + line = FindGString("CINEMAS"); + if (line < 0) { + return; + } + + while (nVal) + { + while (strcmp(gString[line], "END")) { + line++; + } + + line++; + nVal--; + } + + ComputeCinemaText(line); +} + +uint8_t AdvanceCinemaText() +{ + int tmp = nHeight + nCrawlY > 0; + + if (tmp || CDplaying()) + { + nextclock = (int)totalclock + 14; + + if (tmp > 0) + { + short y = nCrawlY; + int edi = 0; + + while (edi < linecount && y <= 199) + { + if (y >= -10) { + myprintext(nLeft[edi], y, gString[line + edi], 0); + } + + edi++; + y += 10; + } + + nCrawlY--; + } + + while (1) + { + HandleAsync(); + + if (inputState.CheckAllInput()) + { + break; + } + + if (CDplaying()) + { + if (nextclock <= (int)totalclock) { + return kTrue; + } + } + else + { + return kTrue; + } + } + } + + return kFalse; +} + +void DoCinemaText(short nVal) +{ + ReadyCinemaText(nVal); + + while (1) + { + overwritesprite(0, 0, cinematile, 0, 2, kPalNormal); + + uint8_t bContinue = AdvanceCinemaText(); + + WaitVBL(); + videoNextPage(); + + // TEMP + int time = (int)totalclock + 4; + while ((int)totalclock < time) { + HandleAsync(); + } + + if (!bContinue) { + return; + } + } +} + +void GoToTheCinema(int nVal) +{ + UnMaskStatus(); + + switch (nVal - 1) + { + default: + return; + + case 0: + { + LoadCinemaPalette(1); + cinematile = 3454; + break; + } + + case 1: + { + LoadCinemaPalette(2); + cinematile = 3452; + break; + } + + case 2: + { + LoadCinemaPalette(3); + cinematile = 3449; + break; + } + + case 3: + { + LoadCinemaPalette(4); + cinematile = 3445; + break; + } + + case 4: + { + LoadCinemaPalette(5); + cinematile = 3451; + break; + } + + case 5: + { + LoadCinemaPalette(6); + cinematile = 3448; + break; + } + + case 6: + { + LoadCinemaPalette(7); + cinematile = 3446; + break; + } + } + +#if 0 + if (ISDEMOVER) { + //??? + if (tilesiz[cinematile].x * tilesiz[cinematile].y == 0) + TileFiles.tileCreate(cinematile, 320, 200); + } +#endif + + FadeOut(kFalse); + StopAllSounds(); + NoClip(); + + overwritesprite(0, 0, kMovieTile, 100, 2, kPalNormal); + videoNextPage(); + +// int386(16, (const union REGS *)&val, (union REGS *)&val) + + overwritesprite(0, 0, cinematile, 0, 2, kPalNormal); + videoNextPage(); + + CinemaFadeIn(); + ClearAllKeys(); + + int ebx = -1; + int edx = -1; + + switch (nVal - 1) + { + default: + WaitAnyKey(10); + break; + + case 0: + ebx = 4; + edx = ebx; + break; + + case 1: + ebx = 0; + break; + + case 2: + ebx = 2; + edx = ebx; + break; + + case 3: + ebx = 7; + break; + + case 4: + ebx = 3; + edx = ebx; + break; + + case 5: + ebx = 8; + edx = ebx; + break; + + case 6: + ebx = 6; + edx = ebx; + break; + } + + if (ebx != -1) + { + if (edx != -1) + { + if (CDplaying()) { + fadecdaudio(); + } + + playCDtrack(edx + 2, false); + } + + DoCinemaText(ebx); + } + + FadeOut(kTrue); + + overwritesprite(0, 0, kMovieTile, 100, 2, kPalNormal); + videoNextPage(); + + GrabPalette(); + Clip(); + + // quit the game if we've finished level 4 and displayed the advert text + if (ISDEMOVER && nVal == 3) { + ExitGame(); + } +} + + +short nBeforeScene[] = { 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }; + + +void CheckBeforeScene(int nLevel) +{ + if (nLevel == kMap20) + { + DoLastLevelCinema(); + return; + } + + short nScene = nBeforeScene[nLevel]; + + if (nScene) + { + if (!nCinemaSeen[nScene]) + { + GoToTheCinema(nScene); + nCinemaSeen[nScene] = 1; + } + } +} + +int showmap(short nLevel, short nLevelNew, short nLevelBest) +{ + FadeOut(0); + EraseScreen(overscanindex); + GrabPalette(); + BlackOut(); + + if (nLevelNew != 11) { + CheckBeforeScene(nLevelNew); + } + + int selectedLevel = menu_DrawTheMap(nLevel, nLevelNew, nLevelBest); + if (selectedLevel == 11) { + CheckBeforeScene(selectedLevel); + } + + return selectedLevel; +} + +void DoAfterCinemaScene(int nLevel) +{ + short nAfterScene[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 7, 0, 0, 0, 0, 6 }; + + if (nAfterScene[nLevel]) { + GoToTheCinema(nAfterScene[nLevel]); + } +} + +void DoFailedFinalScene() +{ + videoSetViewableArea(0, 0, xdim - 1, ydim - 1); + + if (CDplaying()) { + fadecdaudio(); + } + + playCDtrack(9, false); + FadeToWhite(); + + GoToTheCinema(4); +} + +int FindGString(const char *str) +{ + int i = 0; + + while (1) + { + if (!strcmp(gString[i], str)) + return i + 1; + + if (!strcmp(gString[i], "EOF")) + break; + + i++; + } + + return -1; +} + +uint8_t CheckForEscape() +{ + if (!inputState.keyBufferWaiting() || (inputState.keyGetChar() != 27)) { + return kFalse; + } + + return kTrue; +} + +void DoStatic(int a, int b) +{ + RandomLong(); // nothing done with the result of this? + + auto pixels = TileFiles.tileMakeWritable(kTileLoboLaptop); + + int v2 = 160 - a / 2; + int v4 = 81 - b / 2; + + int var_18 = v2 + a; + int v5 = v4 + b; + + auto pTile = (pixels + (200 * v2)) + v4; + + tileInvalidate(kTileLoboLaptop, -1, -1); + + while (v2 < var_18) + { + uint8_t *pStart = pTile; + pTile += 200; + + int v7 = v4; + + while (v7 < v5) + { + *pStart = RandomBit() * 16; + + v7++; + pStart++; + } + v2++; + } + + tileInvalidate(kTileLoboLaptop, 0, 0); + overwritesprite(0, 0, kTileLoboLaptop, 0, 2, kPalNormal); + videoNextPage(); +} + +void DoLastLevelCinema() +{ + FadeOut(0); + UnMaskStatus(); + + videoSetViewableArea(0, 0, xdim - 1, ydim - 1); + + EraseScreen(-1); + RestorePalette(); + + int nString = FindGString("LASTLEVEL"); + + PlayLocalSound(StaticSound[kSound75], 0); + + auto pixels = TileFiles.tileMakeWritable(kTileLoboLaptop); + // uh, what? + //memcpy((void*)waloff[kTileLoboLaptop], (void*)waloff[kTileLoboLaptop], tilesiz[kTileLoboLaptop].x * tilesiz[kTileLoboLaptop].y); + + int var_24 = 16; + int var_28 = 12; + + int nEndTime = (int)totalclock + 240; + + while (inputState.keyBufferWaiting()) { + inputState.keyGetChar(); + } + + while (nEndTime > (int)totalclock) + { + HandleAsync(); + + if (var_24 >= 116) + { + if (var_28 < 192) + var_28 += 20; + } + else + { + var_24 += 20; + } + + DoStatic(var_28, var_24); + + // WaitVBL(); + int time = (int)totalclock + 4; + while ((int)totalclock < time) { + HandleAsync(); + } + } + + // loc_3AD75 + + do + { + LABEL_11: + + HandleAsync(); + + if (strlen(gString[nString]) == 0) + break; + + int esi = nString; + + while (strlen(gString[esi]) != 0) + esi++; + + int ebp = esi; + + ebp -= nString; + ebp <<= 2; + ebp = 81 - ebp; + + int var_1C = esi - nString; + + // loc_3ADD7 + while (1) + { + HandleAsync(); + + if (strlen(gString[nString]) == 0) + break; + + int xPos = 70; + + const char *nChar = gString[nString]; + + nString++; + + TileFiles.tileMakeWritable(kTileLoboLaptop); + while (*nChar) + { + HandleAsync(); + + if (*nChar != ' ') { + PlayLocalSound(StaticSound[kSound71], 0); + } + + xPos += CopyCharToBitmap(*nChar, kTileLoboLaptop, xPos, ebp); + nChar++; + + overwritesprite(0, 0, kTileLoboLaptop, 0, 2, kPalNormal); + videoNextPage(); + + // WaitVBL(); + int time = (int)totalclock + 4; + while ((int)totalclock < time) { + HandleAsync(); + } + + if (CheckForEscape()) + goto LABEL_28; + } + + ebp += 8; + } + + nString++; + + inputState.keyFlushChars(); + inputState.ClearAllKeyStatus(); + + int v11 = (kTimerTicks * (var_1C + 2)) + (int)totalclock; + + do + { + HandleAsync(); + + if (v11 <= (int)totalclock) + goto LABEL_11; + } while (!inputState.keyBufferWaiting()); + } + while (inputState.keyGetChar() != 27); + +LABEL_28: + PlayLocalSound(StaticSound[kSound75], 0); + + nEndTime = (int)totalclock + 240; + + while (nEndTime > (int)totalclock) + { + HandleAsync(); + + DoStatic(var_28, var_24); + + // WaitVBL(); + int time = (int)totalclock + 4; + while ((int)totalclock < time) { + HandleAsync(); + } + + if (var_28 > 20) { + var_28 -= 20; + continue; + } + + if (var_24 > 20) { + var_24 -= 20; + continue; + } + + break; + } + + EraseScreen(-1); + tileLoad(kTileLoboLaptop); + FadeOut(0); + MySetView(nViewLeft, nViewTop, nViewRight, nViewBottom); + MaskStatus(); +} + +static SavegameHelper sgh("menu", + SA(nCinemaSeen), + SA(energytile), + SV(nButtonColor), + SV(word_9AB5B), + nullptr); + +END_PS_NS diff --git a/source/exhumed/src/menu.h b/source/exhumed/src/menu.h new file mode 100644 index 000000000..fe13184f1 --- /dev/null +++ b/source/exhumed/src/menu.h @@ -0,0 +1,81 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __menu_h__ +#define __menu_h__ + +#include "player.h" +#include "typedefs.h" +#include + +BEGIN_PS_NS + +#pragma pack(1) +// should be 75 bytes +struct GameStat +{ + uint8_t nMap; + short nWeapons; + short nCurrentWeapon; + short clip; + short items; + + Player player; + + short nLives; +}; +#pragma pack() + +extern GameStat GameStats; + +extern unsigned char cinemapal[]; + +extern short SavePosition; + +int showmap(short nLevel, short nLevelNew, short nLevelBest); + +void ClearCinemaSeen(); +void menu_DoPlasma(); +int menu_Menu(int val); +void menu_AdjustVolume(); +short menu_GameLoad(int nSlot); +void menu_GameLoad2(FILE *fp, bool bIsDemo = false); +void menu_GameSave2(FILE *fp); +void menu_GameSave(int nSaveSlot); + +int menu_DrawTheMap(int nLevel, int param_B, int param_C); + +void DoEnergyTile(); + +int LoadCinemaPalette(int nPal); + +void CinemaFadeIn(); + +void ReadyCinemaText(uint16_t nVal); +uint8_t AdvanceCinemaText(); + +void DoFailedFinalScene(); + +void DoLastLevelCinema(); +void DoAfterCinemaScene(int nLevel); + +void InitEnergyTile(); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/mono.cpp b/source/exhumed/src/mono.cpp new file mode 100644 index 000000000..77e7bf72b --- /dev/null +++ b/source/exhumed/src/mono.cpp @@ -0,0 +1,74 @@ +//------------------------------------------------------------------------- +/* +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" +/* +Name: MonoClear_ +address = 0001:0001CF5A +module index = 24 +kind: (code) +Name: MonoInUse_ +address = 0001:0001CF8A +module index = 24 +kind: (code) +Name: MonoOpen_ +address = 0001:0001CF8A +module index = 24 +kind: (code) +Name: MonoClose_ +address = 0001:0001CF8D +module index = 24 +kind: (code) +Name: MonoOut_ +address = 0001:0001CFAA +module index = 24 +kind: (code) +Name: CACopy_ +address = 0001:0001D1C0 +module index = 24 +kind: (static pubdef) (code) +Name: CAFill_ +address = 0001:0001D1CD +module index = 24 +kind: (static pubdef) (code) +Name: MonoQuery_ +address = 0001:0001D1E6 +module index = 24 +kind: (code) +Name: _rowCur +address = 0003:000073D8 +module index = 24 +kind: (data) +Name: _colCur +address = 0003:000073DC +module index = 24 +kind: (data) +Name: _fMonoOpen +address = 0003:000073E0 +module index = 24 +kind: (data) + + +*/ +#include "mono.h" + +BEGIN_PS_NS + +int rowCur = 0; +int colCur = 0; + +END_PS_NS diff --git a/source/exhumed/src/mono.h b/source/exhumed/src/mono.h new file mode 100644 index 000000000..3efb676d0 --- /dev/null +++ b/source/exhumed/src/mono.h @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __mono_h__ +#define __mono_h__ + + + +#endif diff --git a/source/exhumed/src/move.cpp b/source/exhumed/src/move.cpp new file mode 100644 index 000000000..27c556e78 --- /dev/null +++ b/source/exhumed/src/move.cpp @@ -0,0 +1,1516 @@ +//------------------------------------------------------------------------- +/* +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 "engine.h" +#include "exhumed.h" +#include "move.h" +#include "init.h" +#include "lighting.h" +#include "bubbles.h" +#include "object.h" +#include "player.h" +#include "view.h" +#include "status.h" +#include "runlist.h" +#include "items.h" +#include "sound.h" +#include "trigdat.h" +#include "anims.h" +#include "random.h" +#include "bullet.h" +#include +#include +#ifndef __WATCOMC__ +//#include +#else +#include +//#include +#endif + +BEGIN_PS_NS + +short NearSector[kMaxSectors] = { 0 }; + +short nPushBlocks; + +// TODO - moveme? +short overridesect; +short NearCount = -1; + +short nBodySprite[50]; + +int hihit, sprceiling, sprfloor, lohit; + +#define kMaxPushBlocks 100 +#define kMaxChunks 75 + +// think this belongs in init.c? +BlockInfo sBlockInfo[kMaxPushBlocks]; + +short nChunkSprite[kMaxChunks]; + + +static SavegameHelper sgh("move", + SV(nPushBlocks), + SV(overridesect), + SV(NearCount), + SV(hihit), + SV(sprceiling), + SV(sprfloor), + SV(lohit), + SA(nBodySprite), + SA(NearSector), + SA(sBlockInfo), + SA(nChunkSprite), + nullptr); + + +signed int lsqrt(int a1) +{ + int v1; // edx@1 + int v2; // ebx@1 + signed int result; // eax@1 + + v1 = a1; + v2 = a1 - 0x40000000; + result = 0; + if (v2 >= 0) + { + result = 0x8000; + v1 = v2; + } + if (v1 - ((result << 15) + 0x10000000) >= 0) + { + v1 -= (result << 15) + 0x10000000; + result += 0x4000; + } + if (v1 - ((result << 14) + 0x4000000) >= 0) + { + v1 -= (result << 14) + 0x4000000; + result += 0x2000; + } + if (v1 - ((result << 13) + 0x1000000) >= 0) + { + v1 -= (result << 13) + 0x1000000; + result += 4096; + } + if (v1 - ((result << 12) + 0x400000) >= 0) + { + v1 -= (result << 12) + 0x400000; + result += 2048; + } + if (v1 - ((result << 11) + 0x100000) >= 0) + { + v1 -= (result << 11) + 0x100000; + result += 1024; + } + if (v1 - ((result << 10) + 0x40000) >= 0) + { + v1 -= (result << 10) + 0x40000; + result += 512; + } + if (v1 - ((result << 9) + 0x10000) >= 0) + { + v1 -= (result << 9) + 0x10000; + result += 256; + } + if (v1 - ((result << 8) + 0x4000) >= 0) + { + v1 -= (result << 8) + 0x4000; + result += 128; + } + if (v1 - ((result << 7) + 4096) >= 0) + { + v1 -= (result << 7) + 4096; + result += 64; + } + if (v1 - ((result << 6) + 1024) >= 0) + { + v1 -= (result << 6) + 1024; + result += 32; + } + if (v1 - (32 * result + 256) >= 0) + { + v1 -= 32 * result + 256; + result += 16; + } + if (v1 - (16 * result + 64) >= 0) + { + v1 -= 16 * result + 64; + result += 8; + } + if (v1 - (8 * result + 16) >= 0) + { + v1 -= 8 * result + 16; + result += 4; + } + if (v1 - (4 * result + 4) >= 0) + { + v1 -= 4 * result + 4; + result += 2; + } + if (v1 - (2 * result + 1) >= 0) + ++result; + return result; +} + +void MoveThings() +{ + UndoFlashes(); + DoLights(); + + if (nFreeze) + { + if (nFreeze == 1 || nFreeze == 2) { + DoSpiritHead(); + } + } + else + { + runlist_ExecObjects(); + runlist_CleanRunRecs(); + } + + MoveStatus(); + DoBubbleMachines(); + DoDrips(); + DoMovingSects(); + DoRegenerates(); + + if (levelnum == kMap20) + { + DoFinale(); + if (lCountDown < 1800 && nDronePitch < 2400 && !lFinaleStart) + { + nDronePitch += 64; + BendAmbientSound(); + } + } +} + +void ResetMoveFifo() +{ + localclock = (int)totalclock; + movefifoend = 0; + movefifopos = 0; +} + +// not used +void clipwall() +{ + +} + +void BuildNear(int x, int y, int walldist, int nSector) +{ + NearSector[0] = nSector; + NearCount = 1; + + int i = 0; + + while (i < NearCount) + { + short nSector = NearSector[i]; + + short nWall = sector[nSector].wallptr; + short nWallCount = sector[nSector].wallnum; + + while (1) + { + nWallCount--; + if (nWallCount < 0) + { + i++; + break; + } + + short nNextSector = wall[nWall].nextsector; + + if (nNextSector >= 0) + { + int j = 0; + for (; j < NearCount; j++) + { + // loc_14F4D: + if (nNextSector == NearSector[j]) + break; + } + + if (j >= NearCount) + { + vec2_t pos = { x, y }; + if (clipinsidebox(&pos, nWall, walldist)) + { + NearSector[NearCount] = wall[nWall].nextsector; + NearCount++; + } + } + } + + nWall++; + } + } +} + +int BelowNear(short nSprite) +{ + short nSector = sprite[nSprite].sectnum; + int z = sprite[nSprite].z; + + int var_24, z2; + + if ((lohit & 0xC000) == 0xC000) + { + var_24 = lohit & 0xC000; + z2 = sprite[lohit & 0x3FFF].z; + } + else + { + var_24 = 0x20000; + z2 = sector[nSector].floorz + SectDepth[nSector]; + + if (NearCount > 0) + { + short edx; + + for (int i = 0; i < NearCount; i++) + { + int nSect2 = NearSector[i]; + + while (nSect2 >= 0) + { + edx = nSect2; + nSect2 = SectBelow[nSect2]; + } + + int ecx = sector[edx].floorz + SectDepth[edx]; + int eax = ecx - z; + + if (eax < 0 && eax >= -5120) + { + z2 = ecx; + nSector = edx; + } + } + } + } + + if (z2 < sprite[nSprite].z) + { + sprite[nSprite].z = z2; + overridesect = nSector; + sprite[nSprite].zvel = 0; + + bTouchFloor = kTrue; + + return var_24; + } + else + { + return 0; + } +} + +int movespritez(short nSprite, int z, int height, int UNUSED(flordist), int clipdist) +{ + spritetype* pSprite = &sprite[nSprite]; + short nSector = pSprite->sectnum; + assert(nSector >= 0 && nSector < kMaxSectors); + + overridesect = nSector; + short edi = nSector; + + // backup cstat + uint16_t cstat = pSprite->cstat; + + pSprite->cstat &= ~CSTAT_SPRITE_BLOCK; + + int nRet = 0; + + short nSectFlags = SectFlag[nSector]; + + if (nSectFlags & kSectUnderwater) { + z >>= 1; + } + + int spriteZ = pSprite->z; + int floorZ = sector[nSector].floorz; + + int ebp = spriteZ + z; + int eax = sector[nSector].ceilingz + (height >> 1); + + if ((nSectFlags & kSectUnderwater) && ebp < eax) { + ebp = eax; + } + + // loc_151E7: + while (ebp > sector[pSprite->sectnum].floorz && SectBelow[pSprite->sectnum] >= 0) + { + edi = SectBelow[pSprite->sectnum]; + + mychangespritesect(nSprite, edi); + } + + if (edi != nSector) + { + pSprite->z = ebp; + + if (SectFlag[edi] & kSectUnderwater) + { + if (nSprite == PlayerList[nLocalPlayer].nSprite) { + D3PlayFX(StaticSound[kSound2], nSprite); + } + + if (pSprite->statnum <= 107) { + pSprite->hitag = 0; + } + } + } + else + { + while ((ebp < sector[pSprite->sectnum].ceilingz) && (SectAbove[pSprite->sectnum] >= 0)) + { + edi = SectAbove[pSprite->sectnum]; + + mychangespritesect(nSprite, edi); + } + } + + // This function will keep the player from falling off cliffs when you're too close to the edge. + // This function finds the highest and lowest z coordinates that your clipping BOX can get to. + getzrange_old(pSprite->x, pSprite->y, pSprite->z - 256, pSprite->sectnum, + &sprceiling, &hihit, &sprfloor, &lohit, 128, CLIPMASK0); + + int mySprfloor = sprfloor; + + if ((lohit & 0xC000) != 0xC000) { + mySprfloor += SectDepth[pSprite->sectnum]; + } + + if (ebp > mySprfloor) + { + if (z > 0) + { + bTouchFloor = kTrue; + + if ((lohit & 0xC000) == 0xC000) + { + // Path A + short nFloorSprite = lohit & 0x3FFF; + + if (pSprite->statnum == 100 && sprite[nFloorSprite].statnum != 0 && sprite[nFloorSprite].statnum < 100) + { + short nDamage = (z >> 9); + if (nDamage) + { + runlist_DamageEnemy(nFloorSprite, nSprite, nDamage << 1); + } + + pSprite->zvel = -z; + } + else + { + if (sprite[nFloorSprite].statnum == 0 || sprite[nFloorSprite].statnum > 199) + { + nRet |= 0x20000; + } + else + { + nRet |= lohit; + } + + pSprite->zvel = 0; + } + } + else + { + // Path B + if (SectBelow[pSprite->sectnum] == -1) + { + nRet |= 0x20000; + + short nSectDamage = SectDamage[pSprite->sectnum]; + + if (nSectDamage != 0) + { + if (pSprite->hitag < 15) + { + IgniteSprite(nSprite); + pSprite->hitag = 20; + } + nSectDamage >>= 2; + nSectDamage = nSectDamage - (nSectDamage>>2); + if (nSectDamage) { + runlist_DamageEnemy(nSprite, -1, nSectDamage); + } + } + + pSprite->zvel = 0; + } + } + } + + // loc_1543B: + ebp = mySprfloor; + pSprite->z = mySprfloor; + } + else + { + if ((ebp - height) < sprceiling && ((hihit & 0xC000) == 0xC000 || SectAbove[pSprite->sectnum] == -1)) + { + ebp = sprceiling + height; + nRet |= 0x10000; + } + } + + if (spriteZ <= floorZ && ebp > floorZ) + { + if ((SectDepth[nSector] != 0) || (edi != nSector && (SectFlag[edi] & kSectUnderwater))) + { + assert(nSector >= 0 && nSector < kMaxSectors); + BuildSplash(nSprite, nSector); + } + } + + pSprite->cstat = cstat; // restore cstat + pSprite->z = ebp; + + if (pSprite->statnum == 100) + { + BuildNear(pSprite->x, pSprite->y, clipdist + (clipdist / 2), pSprite->sectnum); + nRet |= BelowNear(nSprite); + } + + return nRet; +} + +int GetSpriteHeight(int nSprite) +{ + return tilesiz[sprite[nSprite].picnum].y * sprite[nSprite].yrepeat * 4; +} + +// TODO - where is ceildist used? +int movesprite(short nSprite, int dx, int dy, int dz, int UNUSED(ceildist), int flordist, unsigned int clipmask) +{ + spritetype *pSprite = &sprite[nSprite]; + bTouchFloor = kFalse; + + int x = pSprite->x; + int y = pSprite->y; + int z = pSprite->z; + + int nSpriteHeight = GetSpriteHeight(nSprite); + + int nClipDist = (int8_t)pSprite->clipdist << 2; + + short nSector = pSprite->sectnum; + assert(nSector >= 0 && nSector < kMaxSectors); + + int floorZ = sector[nSector].floorz; + + int nRet = 0; + + if ((SectFlag[nSector] & kSectUnderwater) || (floorZ < z)) + { + dx >>= 1; + dy >>= 1; + } + + nRet |= movespritez(nSprite, dz, nSpriteHeight, flordist, nClipDist); + + nSector = pSprite->sectnum; // modified in movespritez so re-grab this variable + + if (pSprite->statnum == 100) + { + short nPlayer = GetPlayerFromSprite(nSprite); + + int varA = 0; + int varB = 0; + + CheckSectorFloor(overridesect, pSprite->z, &varB, &varA); + + if (varB || varA) + { + nXDamage[nPlayer] = varB; + nYDamage[nPlayer] = varA; + } + + dx += nXDamage[nPlayer]; + dy += nYDamage[nPlayer]; + } + else + { + CheckSectorFloor(overridesect, pSprite->z, &dx, &dy); + } + + nRet |= (uint16_t)clipmove_old(&pSprite->x, &pSprite->y, &pSprite->z, &nSector, dx, dy, nClipDist, nSpriteHeight, flordist, clipmask); + + if ((nSector != pSprite->sectnum) && nSector >= 0) + { + if (nRet & 0x20000) { + dz = 0; + } + + if ((sector[nSector].floorz - z) < (dz + flordist)) + { + pSprite->x = x; + pSprite->y = y; + } + else + { + mychangespritesect(nSprite, nSector); + + if (pSprite->pal < 5 && !pSprite->hitag) + { + pSprite->pal = sector[pSprite->sectnum].ceilingpal; + } + } + } + + return nRet; +} + +// OK +void Gravity(short nSprite) +{ + short nSector = sprite[nSprite].sectnum; + + if (SectFlag[nSector] & kSectUnderwater) + { + if (sprite[nSprite].statnum != 100) + { + if (sprite[nSprite].zvel <= 1024) + { + if (sprite[nSprite].zvel < 2048) { + sprite[nSprite].zvel += 512; + } + } + else + { + sprite[nSprite].zvel -= 64; + } + } + else + { + if (sprite[nSprite].zvel > 0) + { + sprite[nSprite].zvel -= 64; + if (sprite[nSprite].zvel < 0) { + sprite[nSprite].zvel = 0; + } + } + else if (sprite[nSprite].zvel < 0) + { + sprite[nSprite].zvel += 64; + if (sprite[nSprite].zvel > 0) { + sprite[nSprite].zvel = 0; + } + } + } + } + else + { + sprite[nSprite].zvel += 512; + if (sprite[nSprite].zvel > 16384) { + sprite[nSprite].zvel = 16384; + } + } +} + +int MoveCreature(short nSprite) +{ + return movesprite(nSprite, sprite[nSprite].xvel << 8, sprite[nSprite].yvel << 8, sprite[nSprite].zvel, 15360, -5120, CLIPMASK0); +} + +int MoveCreatureWithCaution(int nSprite) +{ + int x = sprite[nSprite].x; + int y = sprite[nSprite].y; + int z = sprite[nSprite].z; + short nSectorPre = sprite[nSprite].sectnum; + + int ecx = MoveCreature(nSprite); + + short nSector = sprite[nSprite].sectnum; + + if (nSector != nSectorPre) + { + int zDiff = sector[nSectorPre].floorz - sector[nSector].floorz; + if (zDiff < 0) { + zDiff = -zDiff; + } + + if (zDiff > 15360 || (SectFlag[nSector] & kSectUnderwater) || (SectBelow[nSector] > -1 && SectFlag[SectBelow[nSector]]) || SectDamage[nSector]) + { + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + + mychangespritesect(nSprite, nSectorPre); + + sprite[nSprite].ang = (sprite[nSprite].ang + 256) & kAngleMask; + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512) >> 2; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 2; + return 0; + } + } + + return ecx; +} + +int GetAngleToSprite(int nSprite1, int nSprite2) +{ + if (nSprite1 < 0 || nSprite2 < 0) + return -1; + + return GetMyAngle(sprite[nSprite2].x - sprite[nSprite1].x, sprite[nSprite2].y - sprite[nSprite1].y); +} + +int PlotCourseToSprite(int nSprite1, int nSprite2) +{ + if (nSprite1 < 0 || nSprite2 < 0) + return -1; + + int x = sprite[nSprite2].x - sprite[nSprite1].x; + int y = sprite[nSprite2].y - sprite[nSprite1].y; + + sprite[nSprite1].ang = GetMyAngle(x, y); + + return ksqrt(y * y + x * x); +} + +int FindPlayer(int nSprite, int nVal) +{ + int var_18 = 0; + if (nSprite >= 0) + var_18 = 1; + + if (nSprite < 0) + nSprite = -nSprite; + + if (nVal < 0) + nVal = 100; + + int x = sprite[nSprite].x; + int y = sprite[nSprite].y; + short nSector = sprite[nSprite].sectnum; + + int z = sprite[nSprite].z - GetSpriteHeight(nSprite); + + nVal <<= 8; + + short nPlayerSprite; + int i = 0; + + while (1) + { + if (i >= nTotalPlayers) + return -1; + + nPlayerSprite = PlayerList[i].nSprite; + + if ((sprite[nPlayerSprite].cstat & 0x101) && (!(sprite[nPlayerSprite].cstat & 0x8000))) + { + int v9 = sprite[nPlayerSprite].x - x; + if (v9 < 0) { + v9 = -v9; + } + + int v10 = sprite[nPlayerSprite].y - y; + + if (v9 < nVal) + { + if (v10 < 0) { + v10 = -v10; + } + + if (v10 < nVal && cansee(sprite[nPlayerSprite].x, sprite[nPlayerSprite].y, sprite[nPlayerSprite].z - 7680, sprite[nPlayerSprite].sectnum, x, y, z, nSector)) + { + break; + } + } + } + + i++; + } + + if (var_18) { + PlotCourseToSprite(nSprite, nPlayerSprite); + } + + return nPlayerSprite; +} + +void CheckSectorFloor(short nSector, int z, int *a, int *b) +{ + short nSpeed = SectSpeed[nSector]; + + if (!nSpeed) { + return; + } + + short nFlag = SectFlag[nSector]; + short nAng = nFlag & kAngleMask; + + if (z >= sector[nSector].floorz) + { + *a += (Sin(nAng + 512) << 3) * nSpeed; + *b += (sintable[nAng] << 3) * nSpeed; + } + else if (nFlag & 0x800) + { + *a += (Sin(nAng + 512) << 4) * nSpeed; + *b += (sintable[nAng] << 4) * nSpeed; + } +} + +int GetUpAngle(short nSprite1, int nVal, short nSprite2, int ecx) +{ + int x = sprite[nSprite2].x - sprite[nSprite1].x; + int y = sprite[nSprite2].y - sprite[nSprite1].y; + + int ebx = (sprite[nSprite2].z + ecx) - (sprite[nSprite1].z + nVal); + int edx = (sprite[nSprite2].z + ecx) - (sprite[nSprite1].z + nVal); + + ebx >>= 4; + edx >>= 8; + + ebx = -ebx; + + ebx -= edx; + + int nSqrt = lsqrt(x * x + y * y); + + return GetMyAngle(nSqrt, ebx); +} + +void InitPushBlocks() +{ + nPushBlocks = 0; +} + +int GrabPushBlock() +{ + if (nPushBlocks >= kMaxPushBlocks) { + return -1; + } + + return nPushBlocks++; +} + +void CreatePushBlock(int nSector) +{ + int nBlock = GrabPushBlock(); + int i; + + int startwall = sector[nSector].wallptr; + int nWalls = sector[nSector].wallnum; + + int ecx = 0; + int ebx = 0; + + for (i = 0; i < nWalls; i++) + { + ecx += wall[startwall + i].x; + ebx += wall[startwall + i].y; + } + + int avgx = ecx / nWalls; + int avgy = ebx / nWalls; + + sBlockInfo[nBlock].x = avgx; + sBlockInfo[nBlock].y = avgy; + + int nSprite = insertsprite(nSector, 0); + + sBlockInfo[nBlock].nSprite = nSprite; + + sprite[nSprite].x = avgx; + sprite[nSprite].y = avgy; + sprite[nSprite].z = sector[nSector].floorz - 256; + sprite[nSprite].cstat = 0x8000; + + int var_28 = 0; + + for (i = 0; i < nWalls; i++) + { + int x = avgx - wall[startwall + i].x; + int y = avgy - wall[startwall + i].y; + + int nSqrt = ksqrt(x * x + y * y); + if (nSqrt > var_28) { + var_28 = nSqrt; + } + } + + sBlockInfo[nBlock].field_8 = var_28; + + sprite[nSprite].clipdist = (var_28 & 0xFF) << 2; + sector[nSector].extra = nBlock; +} + +void MoveSector(short nSector, int nAngle, int *nXVel, int *nYVel) +{ + int i; + + if (nSector == -1) { + return; + } + + int nXVect, nYVect; + + if (nAngle < 0) + { + nXVect = *nXVel; + nYVect = *nYVel; + nAngle = GetMyAngle(nXVect, nYVect); + } + else + { + nXVect = Sin(nAngle + 512) << 6; + nYVect = Sin(nAngle) << 6; + } + + short nBlock = sector[nSector].extra; + short nSectFlag = SectFlag[nSector]; + + sectortype *pSector = §or[nSector]; + int nFloorZ = sector[nSector].floorz; + int startwall = sector[nSector].wallptr; + int nWalls = sector[nSector].wallnum; + + walltype *pStartWall = &wall[startwall]; + short nNextSector = wall[startwall].nextsector; + + BlockInfo *pBlockInfo = &sBlockInfo[nBlock]; + + int x = sBlockInfo[nBlock].x; + int x_b = sBlockInfo[nBlock].x; + + int y = sBlockInfo[nBlock].y; + int y_b = sBlockInfo[nBlock].y; + + short nSectorB = nSector; + + int nZVal; + int z; + + int bUnderwater = nSectFlag & kSectUnderwater; + + if (nSectFlag & kSectUnderwater) + { + nZVal = sector[nSector].ceilingz; + z = sector[nNextSector].ceilingz + 256; + + sector[nSector].ceilingz = sector[nNextSector].ceilingz; + } + else + { + nZVal = sector[nSector].floorz; + z = sector[nNextSector].floorz - 256; + + sector[nSector].floorz = sector[nNextSector].floorz; + } + + clipmove_old((int32_t*)&x, (int32_t*)&y, (int32_t*)&z, &nSectorB, nXVect, nYVect, pBlockInfo->field_8, 0, 0, CLIPMASK1); + + int yvect = y - y_b; + int xvect = x - x_b; + + if (nSectorB != nNextSector && nSectorB != nSector) + { + yvect = 0; + xvect = 0; + } + else + { + if (!bUnderwater) + { + z = nZVal; + x = x_b; + y = y_b; + + clipmove_old((int32_t*)&x, (int32_t*)&y, (int32_t*)&z, &nSectorB, nXVect, nYVect, pBlockInfo->field_8, 0, 0, CLIPMASK1); + + int ebx = x; + int ecx = x_b; + int edx = y; + int eax = xvect; + int esi = y_b; + + if (eax < 0) { + eax = -eax; + } + + ebx -= ecx; + ecx = eax; + eax = ebx; + edx -= esi; + + if (eax < 0) { + eax = -eax; + } + + if (ecx > eax) + { + xvect = ebx; + } + + eax = yvect; + if (eax < 0) { + eax = -eax; + } + + ebx = eax; + eax = edx; + + if (eax < 0) { + eax = -eax; + } + + if (ebx > eax) { + yvect = edx; + } + } + } + + // GREEN + if (yvect || xvect) + { + for (i = headspritesect[nSector]; i != -1; i = nextspritesect[i]) + { + if (sprite[i].statnum < 99) + { + sprite[i].x += xvect; + sprite[i].y += yvect; + } + else + { + z = sprite[i].z; + + if ((nSectFlag & kSectUnderwater) || z != nZVal || sprite[i].cstat & 0x8000) + { + x = sprite[i].x; + y = sprite[i].y; + nSectorB = nSector; + + clipmove_old((int32_t*)&x, (int32_t*)&y, (int32_t*)&z, &nSectorB, -xvect, -yvect, 4 * sprite[i].clipdist, 0, 0, CLIPMASK0); + + if (nSectorB >= 0 && nSectorB < kMaxSectors && nSectorB != nSector) { + mychangespritesect(i, nSectorB); + } + } + } + } + + for (i = headspritesect[nNextSector]; i != -1; i = nextspritesect[i]) + { + if (sprite[i].statnum >= 99) + { + x = sprite[i].x; + y = sprite[i].y; + z = sprite[i].z; + nSectorB = nNextSector; + + clipmove_old((int32_t*)&x, (int32_t*)&y, (int32_t*)&z, &nSectorB, + -xvect - (Sin(nAngle + 512) * (4 * sprite[i].clipdist)), + -yvect - (Sin(nAngle) * (4 * sprite[i].clipdist)), + 4 * sprite[i].clipdist, 0, 0, CLIPMASK0); + + + if (nSectorB != nNextSector && (nSectorB == nSector || nNextSector == nSector)) + { + if (nSectorB != nSector || nFloorZ >= sprite[i].z) + { + if (nSectorB >= 0 && nSectorB < kMaxSectors) { + mychangespritesect(i, nSectorB); + } + } + else + { + movesprite(i, + (xvect << 14) + Sin(nAngle + 512) * sprite[i].clipdist, + (yvect << 14) + Sin(nAngle) * sprite[i].clipdist, + 0, 0, 0, CLIPMASK0); + } + } + } + } + + for (int i = 0; i < nWalls; i++) + { + dragpoint(startwall, xvect + pStartWall->x, yvect + pStartWall->y, 0); + pStartWall++; + startwall++; + } + + pBlockInfo->x += xvect; + pBlockInfo->y += yvect; + } + + // loc_163DD + xvect <<= 14; + yvect <<= 14; + + if (!(nSectFlag & kSectUnderwater)) + { + for (i = headspritesect[nSector]; i != -1; i = nextspritesect[i]) + { + if (sprite[i].statnum >= 99 && nZVal == sprite[i].z && !(sprite[i].cstat & 0x8000)) + { + nSectorB = nSector; + clipmove_old(&sprite[i].x, &sprite[i].y, &sprite[i].z, &nSectorB, xvect, yvect, 4 * sprite[i].clipdist, 5120, -5120, CLIPMASK0); + } + } + } + + if (nSectFlag & kSectUnderwater) { + pSector->ceilingz = nZVal; + } + else { + pSector->floorz = nZVal; + } + + *nXVel = xvect; + *nYVel = yvect; +} + +void SetQuake(short nSprite, int nVal) +{ + int x = sprite[nSprite].x; + int y = sprite[nSprite].y; + + for (int i = 0; i < nTotalPlayers; i++) + { + int nPlayerSprite = PlayerList[i].nSprite; + + int nSqrt = ksqrt(((sprite[nPlayerSprite].x - x) >> 8) * ((sprite[nPlayerSprite].x - x) >> 8) + ((sprite[nPlayerSprite].y - y) >> 8) + * ((sprite[nPlayerSprite].y - y) >> 8)); + + int eax = nVal * 256; + + if (nSqrt) + { + eax = eax / nSqrt; + + if (eax >= 256) + { + if (eax > 3840) { + eax = 3840; + } + } + else + { + eax = 0; + } + } + + if (eax > nQuake[i]) { + nQuake[i] = eax; + } + } +} + +int AngleChase(int nSprite, int nSprite2, int ebx, int ecx, int push1) +{ + int nClipType = sprite[nSprite].statnum != 107; + + /* bjd - need to handle cliptype to clipmask change that occured in later build engine version */ + if (nClipType == 1) { + nClipType = CLIPMASK1; + } + else { + nClipType = CLIPMASK0; + } + + short nAngle; + + if (nSprite2 < 0) + { + sprite[nSprite].zvel = 0; + nAngle = sprite[nSprite].ang; + } + else + { + int nHeight = tilesiz[sprite[nSprite2].picnum].y * sprite[nSprite2].yrepeat * 2; + + int nMyAngle = GetMyAngle(sprite[nSprite2].x - sprite[nSprite].x, sprite[nSprite2].y - sprite[nSprite].y); + + int nSqrt = ksqrt( + (sprite[nSprite2].y - sprite[nSprite].y) + * + (sprite[nSprite2].y - sprite[nSprite].y) + + + (sprite[nSprite2].x - sprite[nSprite].x) + * + (sprite[nSprite2].x - sprite[nSprite].x) + ); + + int var_18 = GetMyAngle(nSqrt, ((sprite[nSprite2].z - nHeight) - sprite[nSprite].z) >> 8); + +// int edx = nMyAngle; + + int nAngDelta = AngleDelta(sprite[nSprite].ang, nMyAngle, 1024); + int nAngDelta2 = nAngDelta; + + if (nAngDelta2 < 0) + nAngDelta2 = -nAngDelta2; + + if (nAngDelta2 > 63) + { + nAngDelta2 = nAngDelta; + nAngDelta2 >>= 6; + +// edx = ebx; + + if (nAngDelta2 < 0) + nAngDelta2 = -nAngDelta2; + + ebx /= nAngDelta2; + + if (ebx < 5) + ebx = 5; + } + + int nAngDeltaC = nAngDelta; + + if (nAngDeltaC < 0) + nAngDeltaC = -nAngDeltaC; + + if (nAngDeltaC > push1) + { + if (nAngDelta >= 0) + nAngDelta = push1; + else + nAngDelta = -push1; + } + + int nAngDeltaD = AngleDelta(sprite[nSprite].zvel, var_18, 24); + nAngle = (nAngDelta + sprite[nSprite].ang) & kAngleMask; + + // TODO - CHECKME int ebx = 24; + + sprite[nSprite].zvel = (sprite[nSprite].zvel + nAngDeltaD) & kAngleMask; + } + + sprite[nSprite].ang = nAngle; + + int eax = Sin(sprite[nSprite].zvel + 512); + int edx = (nAngle + 512) & kAngleMask; + + if (eax < 0) + eax = -eax; + + // rename this var. CHECKME + int x = ((sintable[edx] * ebx) >> 14) * eax; + + int ceildist = x >> 8; + + int y = ((Sin(nAngle) * ebx) >> 14) * eax; + + int nVal = y >> 8; + + int z = Sin(sprite[nSprite].zvel) * ksqrt((nVal * nVal) + (ceildist * ceildist)); + + return movesprite(nSprite, x >> 2, y >> 2, (z >> 13) + (Sin(ecx) >> 5), 0, 0, nClipType); +} + +int GetWallNormal(short nWall) +{ + nWall &= kMaxWalls-1; + + int nWall2 = wall[nWall].point2; + + int nAngle = GetMyAngle(wall[nWall2].x - wall[nWall].x, wall[nWall2].y - wall[nWall].y); + return (nAngle + 512) & kAngleMask; +} + +void WheresMyMouth(int nPlayer, int *x, int *y, int *z, short *sectnum) +{ + int nSprite = PlayerList[nPlayer].nSprite; + + *x = sprite[nSprite].x; + *y = sprite[nSprite].y; + + int height = GetSpriteHeight(nSprite) / 2; + + *z = sprite[nSprite].z - height; + *sectnum = sprite[nSprite].sectnum; + + clipmove_old((int32_t*)x, (int32_t*)y, (int32_t*)z, sectnum, + Sin(sprite[nSprite].ang + 512) << 7, + Sin(sprite[nSprite].ang) << 7, + 5120, 1280, 1280, CLIPMASK1); +} + +void InitChunks() +{ + nCurChunkNum = 0; + memset(nChunkSprite, -1, sizeof(nChunkSprite)); + memset(nBodyGunSprite, -1, sizeof(nBodyGunSprite)); + memset(nBodySprite, -1, sizeof(nBodySprite)); + nCurBodyNum = 0; + nCurBodyGunNum = 0; + nBodyTotal = 0; + nChunkTotal = 0; +} + +int GrabBodyGunSprite() +{ + int nSprite = nBodyGunSprite[nCurBodyGunNum]; + + if (nSprite == -1) + { + nSprite = insertsprite(0, 899); + nBodyGunSprite[nCurBodyGunNum] = nSprite; + + sprite[nSprite].lotag = -1; + sprite[nSprite].owner = -1; + } + else + { + int nAnim = sprite[nSprite].owner; + + if (nAnim != -1) { + DestroyAnim(nAnim); + } + + sprite[nSprite].lotag = -1; + sprite[nSprite].owner = -1; + } + + nCurBodyGunNum++; + if (nCurBodyGunNum >= 50) { // TODO - enum/define + nCurBodyGunNum = 0; + } + + sprite[nSprite].cstat = 0; + + return nSprite; +} + +int GrabBody() +{ + int nSprite; + + do + { + nSprite = nBodySprite[nCurBodyNum]; + + if (nSprite == -1) + { + nSprite = insertsprite(0, 899); + nBodySprite[nCurBodyNum] = nSprite; + sprite[nSprite].cstat = 0x8000; + } + + nCurBodyNum++; + if (nCurBodyNum >= 50) { + nCurBodyNum = 0; + } + } + while (sprite[nSprite].cstat & 0x101); + + if (nBodyTotal < 50) { + nBodyTotal++; + } + + sprite[nSprite].cstat = 0; + return nSprite; +} + +int GrabChunkSprite() +{ + int nSprite = nChunkSprite[nCurChunkNum]; + + if (nSprite == -1) + { + nSprite = insertsprite(0, 899); + nChunkSprite[nCurChunkNum] = nSprite; + } + else if (sprite[nSprite].statnum) + { +// TODO MonoOut("too many chunks being used at once!\n"); + return -1; + } + + changespritestat(nSprite, 899); + + nCurChunkNum++; + if (nCurChunkNum >= kMaxChunks) + nCurChunkNum = 0; + + if (nChunkTotal < kMaxChunks) + nChunkTotal++; + + sprite[nSprite].cstat = 0x80; + + return nSprite; +} + +int BuildCreatureChunk(int nVal, int nPic) +{ + int var_14; + + int nSprite = GrabChunkSprite(); + + if (nSprite == -1) { + return -1; + } + + if (nVal & 0x4000) + { + nVal &= 0x3FFF; + var_14 = 1; + } + else + { + var_14 = 0; + } + + nVal &= 0xFFFF; + + sprite[nSprite].x = sprite[nVal].x; + sprite[nSprite].y = sprite[nVal].y; + sprite[nSprite].z = sprite[nVal].z; + + mychangespritesect(nSprite, sprite[nVal].sectnum); + + sprite[nSprite].cstat = 0x80; + sprite[nSprite].shade = -12; + sprite[nSprite].pal = 0; + + sprite[nSprite].xvel = (RandomSize(5) - 16) << 7; + sprite[nSprite].yvel = (RandomSize(5) - 16) << 7; + sprite[nSprite].zvel = (-(RandomSize(8) + 512)) << 3; + + if (var_14) + { + sprite[nSprite].xvel *= 4; + sprite[nSprite].yvel *= 4; + sprite[nSprite].zvel *= 2; + } + + sprite[nSprite].xrepeat = 64; + sprite[nSprite].yrepeat = 64; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].picnum = nPic; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].clipdist = 40; + +// GrabTimeSlot(3); + + sprite[nSprite].extra = -1; + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nSprite | 0xD0000); + sprite[nSprite].hitag = runlist_AddRunRec(NewRun, nSprite | 0xD0000); + + return nSprite | 0xD0000; +} + +void FuncCreatureChunk(int a, int, int nRun) +{ + int nSprite = RunData[nRun].nVal; + assert(nSprite >= 0 && nSprite < kMaxSprites); + + int nMessage = a & 0x7F0000; + + if (nMessage != 0x20000) + return; + + Gravity(nSprite); + + int nSector = sprite[nSprite].sectnum; + sprite[nSprite].pal = sector[nSector].ceilingpal; + + int nVal = movesprite(nSprite, sprite[nSprite].xvel << 10, sprite[nSprite].yvel << 10, sprite[nSprite].zvel, 2560, -2560, CLIPMASK1); + + if (sprite[nSprite].z >= sector[nSector].floorz) + { + // re-grab this variable as it may have changed in movesprite(). Note the check above is against the value *before* movesprite so don't change it. + nSector = sprite[nSprite].sectnum; + + sprite[nSprite].zvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + sprite[nSprite].z = sector[nSector].floorz; + } + else + { + if (!nVal) + return; + + short nAngle; + + if (nVal & 0x20000) + { + sprite[nSprite].cstat = 0x8000; + } + else + { + if ((nVal & 0x3C000) == 0x10000) + { + sprite[nSprite].xvel >>= 1; + sprite[nSprite].yvel >>= 1; + sprite[nSprite].zvel = -sprite[nSprite].zvel; + return; + } + else if ((nVal & 0x3C000) == 0xC000) + { + nAngle = sprite[nVal & 0x3FFF].ang; + } + else if ((nVal & 0x3C000) == 0x8000) + { + nAngle = GetWallNormal(nVal & 0x3FFF); + } + else + { + return; + } + + // loc_16E0C + int nSqrt = lsqrt(((sprite[nSprite].yvel >> 10) * (sprite[nSprite].yvel >> 10) + + (sprite[nSprite].xvel >> 10) * (sprite[nSprite].xvel >> 10)) >> 8); + + sprite[nSprite].xvel = Sin(nAngle + 512) * (nSqrt >> 1); + sprite[nSprite].yvel = Sin(nAngle) * (nSqrt >> 1); + return; + } + } + + runlist_DoSubRunRec(sprite[nSprite].owner); + runlist_FreeRun(sprite[nSprite].lotag - 1); + runlist_SubRunRec(sprite[nSprite].hitag); + + changespritestat(nSprite, 0); + sprite[nSprite].hitag = 0; + sprite[nSprite].lotag = 0; +} + +short UpdateEnemy(short *nEnemy) +{ + if (*nEnemy >= 0) + { + if (!(sprite[*nEnemy].cstat & 0x101)) { + *nEnemy = -1; + } + } + + return *nEnemy; +} +END_PS_NS diff --git a/source/exhumed/src/move.h b/source/exhumed/src/move.h new file mode 100644 index 000000000..d6123ba01 --- /dev/null +++ b/source/exhumed/src/move.h @@ -0,0 +1,82 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __move_h__ +#define __move_h__ + +BEGIN_PS_NS + +// 16 bytes +struct BlockInfo +{ + int x; + int y; + int field_8; + short nSprite; +}; +extern BlockInfo sBlockInfo[]; + +extern int hihit; +extern short nChunkSprite[]; +extern short nBodySprite[]; + +signed int lsqrt(int a1); +void MoveThings(); +void ResetMoveFifo(); +void InitChunks(); +void InitPushBlocks(); +void Gravity(short nSprite); +short UpdateEnemy(short *nEnemy); +int MoveCreature(short nSprite); +int MoveCreatureWithCaution(int nSprite); +void WheresMyMouth(int nPlayer, int *x, int *y, int *z, short *sectnum); + +int GetSpriteHeight(int nSprite); + +int GrabBody(); + +int GrabBodyGunSprite(); +void CreatePushBlock(int nSector); + +void FuncCreatureChunk(int a, int, int nRun); + +int FindPlayer(int nSprite, int nVal); + +int BuildCreatureChunk(int nVal, int nPic); + +void BuildNear(int x, int y, int walldist, int nSector); +int BelowNear(short nSprite); + +int PlotCourseToSprite(int nSprite1, int nSprite2); + +void CheckSectorFloor(short nSector, int z, int *a, int *b); + +int GetAngleToSprite(int nSprite1, int nSprite2); + +int GetWallNormal(short nWall); + +int GetUpAngle(short nSprite1, int nVal, short nSprite2, int ecx); +void MoveSector(short nSector, int nAngle, int *nXVel, int *nYVel); + +int AngleChase(int nSprite, int nSprite2, int ebx, int ecx, int push1); + +void SetQuake(short nSprite, int nVal); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/movie.cpp b/source/exhumed/src/movie.cpp new file mode 100644 index 000000000..75e60a5e2 --- /dev/null +++ b/source/exhumed/src/movie.cpp @@ -0,0 +1,282 @@ +//------------------------------------------------------------------------- +/* +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 "engine.h" +#include "exhumed.h" +#include "names.h" +#include "movie.h" +#include "light.h" +#include +#include +#include "baselayer.h" +#include "typedefs.h" +#include "keyboard.h" +#include "sound.h" + +BEGIN_PS_NS + +void ServeSample(const char** ptr, uint32_t* length); + +enum { + kFramePalette = 0, + kFrameSound, + kFrameImage, + kFrameDone +}; + +#define kSampleRate 22050 +#define kSampleSize 2205 + +uint8_t bankbuf[kSampleRate]; +uint32_t bankptr = 0; +uint32_t banktail = 0; + +uint32_t lSoundBytesRead = 0; +uint32_t lSoundBytesUsed = 0; + +uint8_t lh[32] = { 0 }; + +static uint8_t* CurFrame = NULL; + +bool bServedSample = false; +palette_t moviepal[256]; + +int ReadFrame(FileReader &fp) +{ + static int nFrame = 0; + Printf("Reading frame %d...\n", nFrame); + nFrame++; + + uint8_t nType; + uint8_t var_1C; + int nSize; + uint16_t yOffset; + uint8_t xOffset; + uint8_t nPixels; + uint8_t palette[768]; + + while (1) + { + if (fp.Read(&nType, sizeof(nType)) == 0) { + return 0; + } + + fp.Read(&nSize, sizeof(nSize)); + + nType--; + if (nType > 3) { + continue; + } + + switch (nType) + { + case kFramePalette: + { + fp.Read(palette, sizeof(palette)); + fp.Read(&var_1C, sizeof(var_1C)); + + for (auto &c : palette) + c <<= 2; + + paletteSetColorTable(ANIMPAL, palette); + videoSetPalette(0, ANIMPAL, 2+8); + + memset(CurFrame, overscanindex, 4); //sizeof(CurFrame)); + continue; + } + case kFrameSound: + { + Printf("Reading sound block size %d...\n", nSize); + + if (lSoundBytesRead - lSoundBytesUsed >= kSampleRate) + { + DebugOut("SOUND BUF FULL!\n"); + fp.Seek(nSize, FileReader::SeekCur); + } + else + { + //mutex_lock(&mutex); + + int nRead = fp.Read((char*)bankbuf + bankptr, nSize); + + lSoundBytesRead += nRead; + bankptr += nSize; + + assert(nSize == nRead); + assert(bankptr <= kSampleRate); + + if (bankptr >= kSampleRate) { + bankptr -= kSampleRate; // loop back to start + } + + //mutex_unlock(&mutex); + } + + continue; + } + case kFrameImage: + { + Printf("Reading image block size %d...\n", nSize); + if (nSize == 0) { + continue; + } + + uint8_t *pFrame = CurFrame; + + int nRead = fp.Read(&yOffset, sizeof(yOffset)); + nSize -= nRead; + + pFrame += yOffset * 200; // row position + + while (nSize > 0) + { + fp.Read(&xOffset, sizeof(xOffset)); + fp.Read(&nPixels, sizeof(nPixels)); + nSize -= 2; + + pFrame += xOffset; + + if (nPixels) + { + int nRead = fp.Read(pFrame, nPixels); + pFrame += nRead; + nSize -= nRead; + } + } + + tileInvalidate(kMovieTile, -1, -1); + + break; + } + case kFrameDone: + { + return 1; + break; + } + } + } +} + +void ServeSample(const char** ptr, uint32_t* length) +{ + //mutex_lock(&mutex); + + *ptr = (char*)bankbuf + banktail; + *length = kSampleSize; + + banktail += kSampleSize; + if (banktail >= kSampleRate) { + banktail -= kSampleRate; // rotate back to start + } + + lSoundBytesUsed += kSampleSize; + bServedSample = true; + + //mutex_unlock(&mutex); +} + +void PlayMovie(const char* fileName) +{ + int bDoFade = kTrue; + int hFx = -1; + auto fp = fileSystem.OpenFileReader(fileName, 0); + if (!fp.isOpen()) + { + Printf("Unable to open %s\n", fileName); + return; + } + + tileLoad(kMovieTile); + CurFrame = TileFiles.tileMakeWritable(kMovieTile); + + + fp.Read(lh, sizeof(lh)); + + // sound stuff + bankptr = 0; + banktail = 0; + + // clear keys + inputState.keyFlushChars(); + inputState.ClearAllKeyStatus(); + + if (bDoFade) { + StartFadeIn(); + } + + int angle = 1536; + int z = 0; + + videoSetPalette(0, ANIMPAL, 2 + 8); + + // Read a frame in first + if (ReadFrame(fp)) + { + // start audio playback (fixme) +#if 0 + hFx = FX_StartDemandFeedPlayback(ServeSample, kSampleRate, 0, snd_fxvolume, snd_fxvolume, snd_fxvolume, FX_MUSIC_PRIORITY, fix16_one, -1); +#else + hFx = -1; +#endif + + while (!inputState.keyBufferWaiting()) + { + HandleAsync(); + + // audio is king for sync - if the backend doesn't need any more samples yet, + // don't process any more movie file data. + if (!bServedSample && hFx > 0) { + continue; + } + + bServedSample = false; + + if (z < 65536) { // Zoom - normal zoom is 65536. + z += 2048; + } + if (angle != 0) { + angle += 16; + if (angle == 2048) { + angle = 0; + } + } + + videoClearViewableArea(blackcol); + rotatesprite(160 << 16, 100 << 16, z, angle, kMovieTile, 0, 1, 2, 0, 0, xdim - 1, ydim - 1); + + if (bDoFade) { + bDoFade = DoFadeIn(); + } + + videoNextPage(); + + if (ReadFrame(fp) == 0) { + break; + } + } + } + + if (hFx > 0) { + //FX_StopSound(hFx); + } + + if (inputState.keyBufferWaiting()) { + inputState.keyGetChar(); + } +} +END_PS_NS diff --git a/source/exhumed/src/movie.h b/source/exhumed/src/movie.h new file mode 100644 index 000000000..cad9d61cd --- /dev/null +++ b/source/exhumed/src/movie.h @@ -0,0 +1,28 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __movie_h__ +#define __movie_h__ + +BEGIN_PS_NS + +void PlayMovie(const char *fileName); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/mummy.cpp b/source/exhumed/src/mummy.cpp new file mode 100644 index 000000000..d09bf9818 --- /dev/null +++ b/source/exhumed/src/mummy.cpp @@ -0,0 +1,530 @@ +//------------------------------------------------------------------------- +/* +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 "mummy.h" +#include "sequence.h" +#include "move.h" +#include "map.h" +#include "sound.h" +#include "exhumed.h" +#include "random.h" +#include "trigdat.h" +#include "bullet.h" +#include "items.h" +#include +#include "engine.h" + +BEGIN_PS_NS + +short nMummies = -1; + +struct Mummy +{ + short nHealth; + short B; + short nAction; + short nSprite; + short nTarget; + short F; + short G; + short H; +}; + +Mummy MummyList[kMaxMummies]; + +static actionSeq ActionSeq[] = { + {8, 0}, + {0, 0}, + {16, 0}, + {24, 0}, + {32, 1}, + {40, 1}, + {48, 1}, + {50, 0} +}; + + +static SavegameHelper sgh("mummy", + SV(nMummies), + SA(MummyList), + nullptr); + +// done +void InitMummy() +{ + nMummies = 0; +} + +// done +int BuildMummy(int nSprite, int x, int y, int z, int nSector, int nAngle) +{ + if (nMummies >= kMaxMummies) { + return -1; + } + + short nMummy = nMummies++; + + if (nSprite == -1) + { + nSprite = insertsprite(nSector, 102); + } + else + { + x = sprite[nSprite].x; + y = sprite[nSprite].y; + z = sprite[nSprite].z; + nAngle = sprite[nSprite].ang; + + changespritestat(nSprite, 102); + } + + assert(nSprite >= 0 && nSprite < kMaxSprites); + + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + sprite[nSprite].cstat = 0x101; + sprite[nSprite].shade = -12; + sprite[nSprite].clipdist = 32; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].xrepeat = 42; + sprite[nSprite].yrepeat = 42; + sprite[nSprite].pal = sector[sprite[nSprite].sectnum].ceilingpal; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].ang = nAngle; + sprite[nSprite].picnum = 1; + sprite[nSprite].hitag = 0; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].extra = -1; + +// GrabTimeSlot(3); + + MummyList[nMummy].nAction = 0; + MummyList[nMummy].nHealth = 640; + MummyList[nMummy].B = 0; + MummyList[nMummy].nSprite = nSprite; + MummyList[nMummy].nTarget = -1; + MummyList[nMummy].F = nMummy; + MummyList[nMummy].G = 0; + + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nMummy | 0xE0000); + + MummyList[nMummy].H = runlist_AddRunRec(NewRun, nMummy | 0xE0000); + + nCreaturesLeft++; + + return (nMummy | 0xE0000); +} + +// done +void CheckMummyRevive(short nMummy) +{ + short nSprite = MummyList[nMummy].nSprite; + + for (int i = 0; i < nMummies; i++) + { + if (i != nMummy) + { + short nSprite2 = MummyList[i].nSprite; + if (sprite[nSprite2].statnum != 102) { + continue; + } + + if (MummyList[i].nAction != 5) { + continue; + } + + int x = klabs(sprite[nSprite2].x - sprite[nSprite].x) >> 8; + int y = klabs(sprite[nSprite2].y - sprite[nSprite].y) >> 8; + + if (x <= 20 && y <= 20) + { + if (cansee(sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z - 8192, sprite[nSprite].sectnum, + sprite[nSprite2].x, sprite[nSprite2].y, sprite[nSprite2].z - 8192, sprite[nSprite2].sectnum)) + { + sprite[nSprite2].cstat = 0; + MummyList[i].nAction = 6; + MummyList[i].B = 0; + } + } + } + } +} + +void FuncMummy(int a, int nDamage, int nRun) +{ + short nMummy = RunData[nRun].nVal; + assert(nMummy >= 0 && nMummy < kMaxMummies); + + short nTarget = UpdateEnemy(&MummyList[nMummy].nTarget); + + short nSprite = MummyList[nMummy].nSprite; + short nAction = MummyList[nMummy].nAction; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + case 0x20000: + { + Gravity(nSprite); + + int nSeq = SeqOffsets[kSeqMummy] + ActionSeq[nAction].a; + + sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, MummyList[nMummy].B); + + short nFrame = SeqBase[nSeq] + MummyList[nMummy].B; + short nFrameFlag = FrameFlag[nFrame]; + + seq_MoveSequence(nSprite, nSeq, MummyList[nMummy].B); + + short ecx = 0; + + MummyList[nMummy].B++; + if (MummyList[nMummy].B >= SeqSize[nSeq]) + { + MummyList[nMummy].B = 0; + + ecx = 1; + } + + if (nTarget != -1 && nAction < 4) + { + if ((!sprite[nTarget].cstat) && nAction) + { + MummyList[nMummy].nAction = 0; + MummyList[nMummy].B = 0; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } + } + + int nMov = MoveCreatureWithCaution(nSprite); + + if (nAction > 7) + return; + + switch (nAction) + { + case 0: + { + if ((MummyList[nMummy].F & 0x1F) == (totalmoves & 0x1F)) + { + sprite[nSprite].cstat = 0x101; + + if (nTarget < 0) + { + int nTarget = FindPlayer(nSprite, 100); + if (nTarget >= 0) + { + D3PlayFX(StaticSound[kSound7], nSprite); + MummyList[nMummy].B = 0; + MummyList[nMummy].nTarget = nTarget; + MummyList[nMummy].nAction = 1; + MummyList[nMummy].G = 90; + + sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 2; + sprite[nSprite].yvel = sintable[sprite[nSprite].ang] >> 2; // NOTE no angle masking in original code + } + } + } + return; + } + + case 1: + { + if (MummyList[nMummy].G > 0) + { + MummyList[nMummy].G--; + } + + if ((MummyList[nMummy].F & 0x1F) == (totalmoves & 0x1F)) + { + sprite[nSprite].cstat = 0x101; + + PlotCourseToSprite(nSprite, nTarget); + + if (MummyList[nMummy].nAction == 1) + { + if (RandomBit()) + { + if (cansee(sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z - GetSpriteHeight(nSprite), sprite[nSprite].sectnum, + sprite[nTarget].x, sprite[nTarget].y, sprite[nTarget].z - GetSpriteHeight(nTarget), sprite[nTarget].sectnum)) + { + MummyList[nMummy].nAction = 3; + MummyList[nMummy].B = 0; + + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + return; + } + } + } + } + + // loc_2B5A8 + if (!MummyList[nMummy].B) + { + sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 1; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 1; + } + + if (sprite[nSprite].xvel || sprite[nSprite].yvel) + { + if (sprite[nSprite].xvel > 0) + { + sprite[nSprite].xvel -= 1024; + if (sprite[nSprite].xvel < 0) { + sprite[nSprite].xvel = 0; + } + } + else if (sprite[nSprite].xvel < 0) + { + sprite[nSprite].xvel += 1024; + if (sprite[nSprite].xvel > 0) { + sprite[nSprite].xvel = 0; + } + } + + if (sprite[nSprite].yvel > 0) + { + sprite[nSprite].yvel -= 1024; + if (sprite[nSprite].yvel < 0) { + sprite[nSprite].yvel = 0; + } + } + else if (sprite[nSprite].yvel < 0) + { + sprite[nSprite].yvel += 1024; + if (sprite[nSprite].yvel > 0) { + sprite[nSprite].yvel = 0; + } + } + } + + if (nMov) + { + switch (nMov & 0xC000) + { + case 0x8000: + { + sprite[nSprite].ang = (sprite[nSprite].ang + ((RandomWord() & 0x3FF) + 1024)) & kAngleMask; + sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 2; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 2; + return; + } + + case 0xC000: + { + if ((nMov & 0x3FFF) == nTarget) + { + int nAngle = getangle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y); + if (AngleDiff(sprite[nSprite].ang, nAngle) < 64) + { + MummyList[nMummy].nAction = 2; + MummyList[nMummy].B = 0; + + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } + } + return; + } + } + } + + break; + } + + case 2: + { + if (nTarget == -1) + { + MummyList[nMummy].nAction = 0; + MummyList[nMummy].B = 0; + } + else + { + if (PlotCourseToSprite(nSprite, nTarget) >= 1024) + { + MummyList[nMummy].nAction = 1; + MummyList[nMummy].B = 0; + } + else if (nFrameFlag & 0x80) + { + runlist_DamageEnemy(nTarget, nSprite, 5); + } + } + return; + } + + case 3: + { + if (ecx) + { + MummyList[nMummy].B = 0; + MummyList[nMummy].nAction = 0; + MummyList[nMummy].G = 100; + MummyList[nMummy].nTarget = -1; + return; + } + else if (nFrameFlag & 0x80) + { + SetQuake(nSprite, 100); + + // low 16 bits of returned var contains the sprite index, the high 16 the bullet number + int nBullet = BuildBullet(nSprite, 9, 0, 0, 0x0FFFFC400, sprite[nSprite].ang, nTarget + 10000, 1); + CheckMummyRevive(nMummy); + + if (nBullet > -1) + { + if (!RandomSize(3)) + { + // FIXME CHECKME - nBullet & 0xFFFF can be -1. Original code doesn't handle this?? + + SetBulletEnemy(nBullet >> 16, nTarget); // isolate the bullet number (shift off the sprite index) + sprite[nBullet & 0xFFFF].pal = 5; + } + } + } + return; + } + + case 4: + { + if (ecx) + { + MummyList[nMummy].B = 0; + MummyList[nMummy].nAction = 5; + } + return; + } + + case 5: + { + MummyList[nMummy].B = 0; + return; + } + + case 6: + { + if (ecx) + { + MummyList[nMummy].nAction = 0; + sprite[nSprite].cstat = 0x101; + MummyList[nMummy].nHealth = 300; + MummyList[nMummy].nTarget = -1; + nCreaturesLeft++; + } + return; + } + + case 7: + { + if (nMov & 0x20000) + { + sprite[nSprite].xvel >>= 1; + sprite[nSprite].yvel >>= 1; + } + + if (ecx) + { + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].cstat = 0x101; + + MummyList[nMummy].nAction = 0; + MummyList[nMummy].B = 0; + MummyList[nMummy].nTarget = -1; + } + + return; + } + } + + return; + } + + case 0x90000: + { + seq_PlotSequence(a & 0xFFFF, SeqOffsets[kSeqMummy] + ActionSeq[nAction].a, MummyList[nMummy].B, ActionSeq[nAction].b); + return; + } + + case 0xA0000: + { + if (MummyList[nMummy].nHealth <= 0) + return; + + nDamage = runlist_CheckRadialDamage(nSprite); + // fall through to 0x80000 + fallthrough__; + } + case 0x80000: + { + if (nDamage <= 0) + return; + + if (MummyList[nMummy].nHealth <= 0) { + return; + } + + MummyList[nMummy].nHealth -= nDamage; + + if (MummyList[nMummy].nHealth <= 0) + { + MummyList[nMummy].nHealth = 0; + sprite[nSprite].cstat &= 0xFEFE; + nCreaturesLeft--; + + DropMagic(nSprite); + + MummyList[nMummy].B = 0; + MummyList[nMummy].nAction = 4; + + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].z = sector[sprite[nSprite].sectnum].floorz; + } + else + { + if (!RandomSize(2)) + { + MummyList[nMummy].nAction = 7; + MummyList[nMummy].B = 0; + + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } + } + + return; + } + + default: + { + Printf("unknown msg %d for Mummy\n", a & 0x7F0000); + break; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/mummy.h b/source/exhumed/src/mummy.h new file mode 100644 index 000000000..ceb2d0ce9 --- /dev/null +++ b/source/exhumed/src/mummy.h @@ -0,0 +1,35 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __mummy_h__ +#define __mummy_h__ + +#include "runlist.h" + +BEGIN_PS_NS + +#define kMaxMummies 150 + +void InitMummy(); +int BuildMummy(int val, int x, int y, int z, int nSector, int angle); + +void FuncMummy(int nSector, int edx, int nRun); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/names.h b/source/exhumed/src/names.h new file mode 100644 index 000000000..08b1e1f8d --- /dev/null +++ b/source/exhumed/src/names.h @@ -0,0 +1,6167 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __names_h__ +#define __names_h__ + +#define kTile0 0 +#define kTile1 1 +#define kTile2 2 +#define kTile3 3 +#define kTile4 4 +#define kTile5 5 +#define kTile6 6 +#define kTile7 7 +#define kTile8 8 +#define kTile9 9 +#define kTile10 10 +#define kTile11 11 +#define kTile12 12 +#define kTile13 13 +#define kTile14 14 +#define kTile15 15 +#define kTile16 16 +#define kTile17 17 +#define kTile18 18 +#define kTile19 19 +#define kTile20 20 +#define kTile21 21 +#define kTile22 22 +#define kTile23 23 +#define kTile24 24 +#define kTile25 25 +#define kTile26 26 +#define kTile27 27 +#define kTile28 28 +#define kTile29 29 +#define kTile30 30 +#define kTile31 31 +#define kTile32 32 +#define kTile33 33 +#define kTile34 34 +#define kTile35 35 +#define kTile36 36 +#define kTile37 37 +#define kTile38 38 +#define kTile39 39 +#define kTile40 40 +#define kTile41 41 +#define kTile42 42 +#define kTile43 43 +#define kTile44 44 +#define kTile45 45 +#define kTile46 46 +#define kTile47 47 +#define kTile48 48 +#define kTile49 49 +#define kTile50 50 +#define kTile51 51 +#define kTile52 52 +#define kTile53 53 +#define kTile54 54 +#define kTile55 55 +#define kTile56 56 +#define kTile57 57 +#define kTile58 58 +#define kTile59 59 +#define kTile60 60 +#define kTile61 61 +#define kTile62 62 +#define kTile63 63 +#define kTile64 64 +#define kTile65 65 +#define kTile66 66 +#define kTile67 67 +#define kTile68 68 +#define kTile69 69 +#define kTile70 70 +#define kTile71 71 +#define kTile72 72 +#define kTile73 73 +#define kTile74 74 +#define kTile75 75 +#define kTile76 76 +#define kTile77 77 +#define kTile78 78 +#define kTile79 79 +#define kTile80 80 +#define kTile81 81 +#define kTile82 82 +#define kTile83 83 +#define kTile84 84 +#define kTile85 85 +#define kTile86 86 +#define kTile87 87 +#define kTile88 88 +#define kTile89 89 +#define kTile90 90 +#define kTile91 91 +#define kTile92 92 +#define kTile93 93 +#define kTile94 94 +#define kTile95 95 +#define kTile96 96 +#define kTile97 97 +#define kTile98 98 +#define kTile99 99 +#define kTile100 100 +#define kTile101 101 +#define kTile102 102 +#define kTile103 103 +#define kTile104 104 +#define kTile105 105 +#define kTile106 106 +#define kTile107 107 +#define kTile108 108 +#define kTile109 109 +#define kTile110 110 +#define kTile111 111 +#define kTile112 112 +#define kTile113 113 +#define kTile114 114 +#define kTile115 115 +#define kTile116 116 +#define kTile117 117 +#define kTile118 118 +#define kTile119 119 +#define kTile120 120 +#define kTile121 121 +#define kTile122 122 +#define kTile123 123 +#define kTile124 124 +#define kTile125 125 +#define kTile126 126 +#define kTile127 127 +#define kTile128 128 +#define kTile129 129 +#define kTile130 130 +#define kTile131 131 +#define kTile132 132 +#define kTile133 133 +#define kTile134 134 +#define kTile135 135 +#define kTile136 136 +#define kTile137 137 +#define kTile138 138 +#define kTile139 139 +#define kTile140 140 +#define kTile141 141 +#define kTile142 142 +#define kTile143 143 +#define kTile144 144 +#define kTile145 145 +#define kTile146 146 +#define kTile147 147 +#define kTile148 148 +#define kTile149 149 +#define kTile150 150 +#define kTile151 151 +#define kTile152 152 +#define kTile153 153 +#define kTile154 154 +#define kTile155 155 +#define kTile156 156 +#define kTile157 157 +#define kTile158 158 +#define kTile159 159 +#define kTile160 160 +#define kTile161 161 +#define kTile162 162 +#define kTile163 163 +#define kTile164 164 +#define kTile165 165 +#define kTile166 166 +#define kTile167 167 +#define kTile168 168 +#define kTile169 169 +#define kTile170 170 +#define kTile171 171 +#define kTile172 172 +#define kTile173 173 +#define kTile174 174 +#define kTile175 175 +#define kTile176 176 +#define kTile177 177 +#define kTile178 178 +#define kTile179 179 +#define kTile180 180 +#define kTile181 181 +#define kTile182 182 +#define kTile183 183 +#define kTile184 184 +#define kTile185 185 +#define kTile186 186 +#define kTile187 187 +#define kTile188 188 +#define kTile189 189 +#define kTile190 190 +#define kTile191 191 +#define kTile192 192 +#define kTile193 193 +#define kTile194 194 +#define kTile195 195 +#define kTile196 196 +#define kTile197 197 +#define kTile198 198 +#define kTile199 199 +#define kTile200 200 +#define kTile201 201 +#define kTile202 202 +#define kTile203 203 +#define kTile204 204 +#define kTile205 205 +#define kTile206 206 +#define kTile207 207 +#define kTile208 208 +#define kTile209 209 +#define kTile210 210 +#define kTile211 211 +#define kTile212 212 +#define kTile213 213 +#define kTile214 214 +#define kTile215 215 +#define kTile216 216 +#define kTile217 217 +#define kTile218 218 +#define kTile219 219 +#define kTile220 220 +#define kTile221 221 +#define kTile222 222 +#define kTile223 223 +#define kTile224 224 +#define kTile225 225 +#define kTile226 226 +#define kTile227 227 +#define kTile228 228 +#define kTile229 229 +#define kTile230 230 +#define kTile231 231 +#define kTile232 232 +#define kTile233 233 +#define kTile234 234 +#define kTile235 235 +#define kTile236 236 +#define kTile237 237 +#define kTile238 238 +#define kTile239 239 +#define kTile240 240 +#define kTile241 241 +#define kTile242 242 +#define kTile243 243 +#define kTile244 244 +#define kTile245 245 +#define kTile246 246 +#define kTile247 247 +#define kTile248 248 +#define kTile249 249 +#define kTile250 250 +#define kTile251 251 +#define kTile252 252 +#define kTile253 253 +#define kTile254 254 +#define kTile255 255 +#define kTile256 256 +#define kTile257 257 +#define kTile258 258 +#define kTile259 259 +#define kTile260 260 +#define kTile261 261 +#define kTile262 262 +#define kTile263 263 +#define kTile264 264 +#define kTile265 265 +#define kTile266 266 +#define kTile267 267 +#define kTile268 268 +#define kTile269 269 +#define kTile270 270 +#define kTile271 271 +#define kTile272 272 +#define kTile273 273 +#define kTile274 274 +#define kTile275 275 +#define kTile276 276 +#define kTile277 277 +#define kTile278 278 +#define kTile279 279 +#define kTile280 280 +#define kTile281 281 +#define kTile282 282 +#define kTile283 283 +#define kTile284 284 +#define kTile285 285 +#define kTile286 286 +#define kTile287 287 +#define kTile288 288 +#define kTile289 289 +#define kTile290 290 +#define kTile291 291 +#define kTile292 292 +#define kTile293 293 +#define kTile294 294 +#define kTile295 295 +#define kTile296 296 +#define kTile297 297 +#define kTile298 298 +#define kTile299 299 +#define kTile300 300 +#define kTile301 301 +#define kTile302 302 +#define kTile303 303 +#define kTile304 304 +#define kTile305 305 +#define kTile306 306 +#define kTile307 307 +#define kTile308 308 +#define kTile309 309 +#define kTile310 310 +#define kTile311 311 +#define kTile312 312 +#define kTile313 313 +#define kTile314 314 +#define kTile315 315 +#define kTile316 316 +#define kTile317 317 +#define kTile318 318 +#define kTile319 319 +#define kTile320 320 +#define kTile321 321 +#define kTile322 322 +#define kTile323 323 +#define kTile324 324 +#define kTile325 325 +#define kTile326 326 +#define kTile327 327 +#define kTile328 328 +#define kTile329 329 +#define kTile330 330 +#define kTile331 331 +#define kTile332 332 +#define kTile333 333 +#define kTile334 334 +#define kTile335 335 +#define kTile336 336 +#define kTile337 337 +#define kTile338 338 +#define kTile339 339 +#define kTile340 340 +#define kTile341 341 +#define kTile342 342 +#define kTile343 343 +#define kTile344 344 +#define kTile345 345 +#define kTile346 346 +#define kTile347 347 +#define kTile348 348 +#define kTile349 349 +#define kTile350 350 +#define kTile351 351 +#define kTile352 352 +#define kTile353 353 +#define kTile354 354 +#define kTile355 355 +#define kTile356 356 +#define kTile357 357 +#define kTile358 358 +#define kTile359 359 +#define kTile360 360 +#define kTile361 361 +#define kTile362 362 +#define kTile363 363 +#define kTile364 364 +#define kTile365 365 +#define kTile366 366 +#define kTile367 367 +#define kTile368 368 +#define kTile369 369 +#define kTile370 370 +#define kTile371 371 +#define kTile372 372 +#define kTile373 373 +#define kTile374 374 +#define kTile375 375 +#define kTile376 376 +#define kTile377 377 +#define kTile378 378 +#define kTile379 379 +#define kTile380 380 +#define kTile381 381 +#define kTile382 382 +#define kTile383 383 +#define kTile384 384 +#define kTile385 385 +#define kTile386 386 +#define kTile387 387 +#define kTile388 388 +#define kTile389 389 +#define kTile390 390 +#define kTile391 391 +#define kTile392 392 +#define kTile393 393 +#define kTile394 394 +#define kTile395 395 +#define kTile396 396 +#define kTile397 397 +#define kTile398 398 +#define kTile399 399 +#define kTile400 400 +#define kTile401 401 +#define kTile402 402 +#define kTile403 403 +#define kTile404 404 +#define kTile405 405 +#define kTile406 406 +#define kTile407 407 +#define kTile408 408 +#define kTile409 409 +#define kTile410 410 +#define kTile411 411 +#define kTile412 412 +#define kTile413 413 +#define kTile414 414 +#define kTile415 415 +#define kTile416 416 +#define kTile417 417 +#define kTile418 418 +#define kTile419 419 +#define kTile420 420 +#define kTile421 421 +#define kTile422 422 +#define kTile423 423 +#define kTile424 424 +#define kTile425 425 +#define kTile426 426 +#define kTile427 427 +#define kTile428 428 +#define kTile429 429 +#define kTile430 430 +#define kTile431 431 +#define kTile432 432 +#define kTile433 433 +#define kTile434 434 +#define kTile435 435 +#define kTile436 436 +#define kTile437 437 +#define kTile438 438 +#define kTile439 439 +#define kTile440 440 +#define kTile441 441 +#define kTile442 442 +#define kTile443 443 +#define kTile444 444 +#define kTile445 445 +#define kTile446 446 +#define kTile447 447 +#define kTile448 448 +#define kTile449 449 +#define kTile450 450 +#define kTile451 451 +#define kTile452 452 +#define kTile453 453 +#define kTile454 454 +#define kTile455 455 +#define kTile456 456 +#define kTile457 457 +#define kTile458 458 +#define kTile459 459 +#define kTile460 460 +#define kTile461 461 +#define kTile462 462 +#define kTile463 463 +#define kTile464 464 +#define kTile465 465 +#define kTile466 466 +#define kTile467 467 +#define kTile468 468 +#define kTile469 469 +#define kTile470 470 +#define kTile471 471 +#define kTile472 472 +#define kTile473 473 +#define kTile474 474 +#define kTile475 475 +#define kTile476 476 +#define kTile477 477 +#define kTile478 478 +#define kTile479 479 +#define kTile480 480 +#define kTile481 481 +#define kTile482 482 +#define kTile483 483 +#define kTile484 484 +#define kTile485 485 +#define kTile486 486 +#define kTile487 487 +#define kTile488 488 +#define kTile489 489 +#define kTile490 490 +#define kTile491 491 +#define kTile492 492 +#define kTile493 493 +#define kTile494 494 +#define kTile495 495 +#define kTile496 496 +#define kTile497 497 +#define kTile498 498 +#define kTile499 499 +#define kTile500 500 +#define kTile501 501 +#define kTile502 502 +#define kTile503 503 +#define kTile504 504 +#define kTile505 505 +#define kTile506 506 +#define kTile507 507 +#define kTile508 508 +#define kTile509 509 +#define kTile510 510 +#define kTile511 511 +#define kTile512 512 +#define kTile513 513 +#define kTile514 514 +#define kTile515 515 +#define kTile516 516 +#define kTile517 517 +#define kTile518 518 +#define kTile519 519 +#define kTile520 520 +#define kTile521 521 +#define kTile522 522 +#define kTile523 523 +#define kTile524 524 +#define kTile525 525 +#define kTile526 526 +#define kTile527 527 +#define kTile528 528 +#define kTile529 529 +#define kTile530 530 +#define kTile531 531 +#define kTile532 532 +#define kTile533 533 +#define kTile534 534 +#define kTile535 535 +#define kTile536 536 +#define kTile537 537 +#define kTile538 538 +#define kTile539 539 +#define kTile540 540 +#define kTile541 541 +#define kTile542 542 +#define kTile543 543 +#define kTile544 544 +#define kTile545 545 +#define kTile546 546 +#define kTile547 547 +#define kTile548 548 +#define kTile549 549 +#define kTile550 550 +#define kTile551 551 +#define kTile552 552 +#define kTile553 553 +#define kTile554 554 +#define kTile555 555 +#define kTile556 556 +#define kTile557 557 +#define kTile558 558 +#define kTile559 559 +#define kTile560 560 +#define kTile561 561 +#define kTile562 562 +#define kTile563 563 +#define kTile564 564 +#define kTile565 565 +#define kTile566 566 +#define kTile567 567 +#define kTile568 568 +#define kTile569 569 +#define kTile570 570 +#define kTile571 571 +#define kTile572 572 +#define kTile573 573 +#define kTile574 574 +#define kTile575 575 +#define kTile576 576 +#define kTile577 577 +#define kTile578 578 +#define kTile579 579 +#define kTile580 580 +#define kTile581 581 +#define kTile582 582 +#define kTile583 583 +#define kTile584 584 +#define kTile585 585 +#define kTile586 586 +#define kTile587 587 +#define kTile588 588 +#define kTile589 589 +#define kTileRamsesGold 590 +#define kTileRamsesWorkTile 591 +#define kTileRamsesNormal 592 +#define kTile593 593 +#define kTile594 594 +#define kTile595 595 +#define kTile596 596 +#define kTile597 597 +#define kTile598 598 +#define kTile599 599 +#define kTile600 600 +#define kTile601 601 +#define kTile602 602 +#define kTile603 603 +#define kTile604 604 +#define kTile605 605 +#define kTile606 606 +#define kTile607 607 +#define kTile608 608 +#define kTile609 609 +#define kTile610 610 +#define kTile611 611 +#define kTile612 612 +#define kTile613 613 +#define kTile614 614 +#define kTile615 615 +#define kTile616 616 +#define kTile617 617 +#define kTile618 618 +#define kTile619 619 +#define kTile620 620 +#define kTile621 621 +#define kTile622 622 +#define kTile623 623 +#define kTile624 624 +#define kTile625 625 +#define kTile626 626 +#define kTile627 627 +#define kTile628 628 +#define kTile629 629 +#define kTile630 630 +#define kTile631 631 +#define kTile632 632 +#define kTile633 633 +#define kTile634 634 +#define kTile635 635 +#define kTile636 636 +#define kTile637 637 +#define kTile638 638 +#define kTile639 639 +#define kTile640 640 +#define kTile641 641 +#define kTile642 642 +#define kTile643 643 +#define kTile644 644 +#define kTile645 645 +#define kTile646 646 +#define kTile647 647 +#define kTile648 648 +#define kTile649 649 +#define kTile650 650 +#define kTile651 651 +#define kTile652 652 +#define kTile653 653 +#define kTile654 654 +#define kTile655 655 +#define kTile656 656 +#define kTile657 657 +#define kTile658 658 +#define kTile659 659 +#define kTile660 660 +#define kTile661 661 +#define kTile662 662 +#define kTile663 663 +#define kTile664 664 +#define kTile665 665 +#define kTile666 666 +#define kTile667 667 +#define kTile668 668 +#define kTile669 669 +#define kTile670 670 +#define kTile671 671 +#define kTile672 672 +#define kTile673 673 +#define kTile674 674 +#define kTile675 675 +#define kTile676 676 +#define kTile677 677 +#define kTile678 678 +#define kTile679 679 +#define kTile680 680 +#define kTile681 681 +#define kTile682 682 +#define kTile683 683 +#define kTile684 684 +#define kTile685 685 +#define kTile686 686 +#define kTile687 687 +#define kTile688 688 +#define kTile689 689 +#define kTile690 690 +#define kTile691 691 +#define kTile692 692 +#define kTile693 693 +#define kTile694 694 +#define kTile695 695 +#define kTile696 696 +#define kTile697 697 +#define kTile698 698 +#define kTile699 699 +#define kTile700 700 +#define kTile701 701 +#define kTile702 702 +#define kTile703 703 +#define kTile704 704 +#define kTile705 705 +#define kTile706 706 +#define kTile707 707 +#define kTile708 708 +#define kTile709 709 +#define kTile710 710 +#define kTile711 711 +#define kTile712 712 +#define kTile713 713 +#define kTile714 714 +#define kTile715 715 +#define kTile716 716 +#define kTile717 717 +#define kTile718 718 +#define kTile719 719 +#define kTile720 720 +#define kTile721 721 +#define kTile722 722 +#define kTile723 723 +#define kTile724 724 +#define kTile725 725 +#define kTile726 726 +#define kTile727 727 +#define kTile728 728 +#define kTile729 729 +#define kTile730 730 +#define kTile731 731 +#define kTile732 732 +#define kTile733 733 +#define kTile734 734 +#define kTile735 735 +#define kTile736 736 +#define kTile737 737 +#define kTile738 738 +#define kTile739 739 +#define kTile740 740 +#define kTile741 741 +#define kTile742 742 +#define kTile743 743 +#define kTile744 744 +#define kTile745 745 +#define kTile746 746 +#define kTile747 747 +#define kTile748 748 +#define kTile749 749 +#define kTile750 750 +#define kTile751 751 +#define kTile752 752 +#define kTile753 753 +#define kTile754 754 +#define kTile755 755 +#define kTile756 756 +#define kTile757 757 +#define kTile758 758 +#define kTile759 759 +#define kTile760 760 +#define kTile761 761 +#define kTile762 762 +#define kTile763 763 +#define kMovieTile 764 +#define kTile765 765 +#define kTile766 766 +#define kTile767 767 +#define kTile768 768 +#define kTile769 769 +#define kTile770 770 +#define kTile771 771 +#define kTile772 772 +#define kTile773 773 +#define kTile774 774 +#define kTile775 775 +#define kTile776 776 +#define kTile777 777 +#define kTile778 778 +#define kTile779 779 +#define kTile780 780 +#define kTile781 781 +#define kTile782 782 +#define kTile783 783 +#define kTile784 784 +#define kTile785 785 +#define kTile786 786 +#define kTile787 787 +#define kTile788 788 +#define kTile789 789 +#define kTile790 790 +#define kTile791 791 +#define kTile792 792 +#define kTile793 793 +#define kTile794 794 +#define kTile795 795 +#define kTile796 796 +#define kTile797 797 +#define kTile798 798 +#define kTile799 799 +#define kTile800 800 +#define kTile801 801 +#define kTile802 802 +#define kTile803 803 +#define kTile804 804 +#define kTile805 805 +#define kTile806 806 +#define kTile807 807 +#define kTile808 808 +#define kTile809 809 +#define kTile810 810 +#define kTile811 811 +#define kTile812 812 +#define kTile813 813 +#define kTile814 814 +#define kTile815 815 +#define kTile816 816 +#define kTile817 817 +#define kTile818 818 +#define kTile819 819 +#define kTile820 820 +#define kTile821 821 +#define kTile822 822 +#define kTile823 823 +#define kTile824 824 +#define kTile825 825 +#define kTile826 826 +#define kTile827 827 +#define kTile828 828 +#define kTile829 829 +#define kTile830 830 +#define kTile831 831 +#define kTile832 832 +#define kTile833 833 +#define kTile834 834 +#define kTile835 835 +#define kTile836 836 +#define kTile837 837 +#define kTile838 838 +#define kTile839 839 +#define kTile840 840 +#define kTile841 841 +#define kTile842 842 +#define kTile843 843 +#define kTile844 844 +#define kTile845 845 +#define kTile846 846 +#define kTile847 847 +#define kTile848 848 +#define kTile849 849 +#define kTile850 850 +#define kTile851 851 +#define kTile852 852 +#define kTile853 853 +#define kTile854 854 +#define kTile855 855 +#define kTile856 856 +#define kTile857 857 +#define kTile858 858 +#define kTile859 859 +#define kTile860 860 +#define kTile861 861 +#define kTile862 862 +#define kTile863 863 +#define kTile864 864 +#define kTile865 865 +#define kTile866 866 +#define kTile867 867 +#define kTile868 868 +#define kTile869 869 +#define kTile870 870 +#define kTile871 871 +#define kTile872 872 +#define kTile873 873 +#define kTile874 874 +#define kTile875 875 +#define kTile876 876 +#define kTile877 877 +#define kTile878 878 +#define kTile879 879 +#define kTile880 880 +#define kTile881 881 +#define kTile882 882 +#define kTile883 883 +#define kTile884 884 +#define kTile885 885 +#define kTile886 886 +#define kTile887 887 +#define kTile888 888 +#define kTile889 889 +#define kTile890 890 +#define kTile891 891 +#define kTile892 892 +#define kTile893 893 +#define kTile894 894 +#define kTile895 895 +#define kTile896 896 +#define kTile897 897 +#define kTile898 898 +#define kTile899 899 +#define kTile900 900 +#define kTile901 901 +#define kTile902 902 +#define kTile903 903 +#define kTile904 904 +#define kTile905 905 +#define kTile906 906 +#define kTile907 907 +#define kTile908 908 +#define kTile909 909 +#define kTile910 910 +#define kTile911 911 +#define kTile912 912 +#define kTile913 913 +#define kTile914 914 +#define kTile915 915 +#define kTile916 916 +#define kTile917 917 +#define kTile918 918 +#define kTile919 919 +#define kTile920 920 +#define kTile921 921 +#define kTile922 922 +#define kTile923 923 +#define kTile924 924 +#define kTile925 925 +#define kTile926 926 +#define kTile927 927 +#define kTile928 928 +#define kTile929 929 +#define kTile930 930 +#define kTile931 931 +#define kTile932 932 +#define kTile933 933 +#define kTile934 934 +#define kTile935 935 +#define kTile936 936 +#define kTile937 937 +#define kTile938 938 +#define kTile939 939 +#define kTile940 940 +#define kTile941 941 +#define kTile942 942 +#define kTile943 943 +#define kTile944 944 +#define kTile945 945 +#define kTile946 946 +#define kTile947 947 +#define kTile948 948 +#define kTile949 949 +#define kTile950 950 +#define kTile951 951 +#define kTile952 952 +#define kTile953 953 +#define kTile954 954 +#define kTile955 955 +#define kTile956 956 +#define kTile957 957 +#define kTile958 958 +#define kTile959 959 +#define kTile960 960 +#define kTile961 961 +#define kTile962 962 +#define kTile963 963 +#define kTile964 964 +#define kTile965 965 +#define kTile966 966 +#define kTile967 967 +#define kTile968 968 +#define kTile969 969 +#define kTile970 970 +#define kTile971 971 +#define kTile972 972 +#define kTile973 973 +#define kTile974 974 +#define kTile975 975 +#define kTile976 976 +#define kTile977 977 +#define kTile978 978 +#define kTile979 979 +#define kTile980 980 +#define kTile981 981 +#define kTile982 982 +#define kTile983 983 +#define kTile984 984 +#define kTile985 985 +#define kTile986 986 +#define kTile987 987 +#define kTile988 988 +#define kTile989 989 +#define kTile990 990 +#define kTile991 991 +#define kTile992 992 +#define kTile993 993 +#define kTile994 994 +#define kTile995 995 +#define kTile996 996 +#define kTile997 997 +#define kTile998 998 +#define kTile999 999 +#define kTile1000 1000 +#define kTile1001 1001 +#define kTile1002 1002 +#define kTile1003 1003 +#define kTile1004 1004 +#define kTile1005 1005 +#define kTile1006 1006 +#define kTile1007 1007 +#define kTile1008 1008 +#define kTile1009 1009 +#define kTile1010 1010 +#define kTile1011 1011 +#define kTile1012 1012 +#define kTile1013 1013 +#define kTile1014 1014 +#define kTile1015 1015 +#define kTile1016 1016 +#define kTile1017 1017 +#define kTile1018 1018 +#define kTile1019 1019 +#define kTile1020 1020 +#define kTile1021 1021 +#define kTile1022 1022 +#define kTile1023 1023 +#define kTile1024 1024 +#define kTile1025 1025 +#define kTile1026 1026 +#define kTile1027 1027 +#define kTile1028 1028 +#define kTile1029 1029 +#define kTile1030 1030 +#define kTile1031 1031 +#define kTile1032 1032 +#define kTile1033 1033 +#define kTile1034 1034 +#define kTile1035 1035 +#define kTile1036 1036 +#define kTile1037 1037 +#define kTile1038 1038 +#define kTile1039 1039 +#define kTile1040 1040 +#define kTile1041 1041 +#define kTile1042 1042 +#define kTile1043 1043 +#define kTile1044 1044 +#define kTile1045 1045 +#define kTile1046 1046 +#define kTile1047 1047 +#define kTile1048 1048 +#define kTile1049 1049 +#define kTile1050 1050 +#define kTile1051 1051 +#define kTile1052 1052 +#define kTile1053 1053 +#define kTile1054 1054 +#define kTile1055 1055 +#define kTile1056 1056 +#define kTile1057 1057 +#define kTile1058 1058 +#define kTile1059 1059 +#define kTile1060 1060 +#define kTile1061 1061 +#define kTile1062 1062 +#define kTile1063 1063 +#define kTile1064 1064 +#define kTile1065 1065 +#define kTile1066 1066 +#define kTile1067 1067 +#define kTile1068 1068 +#define kTile1069 1069 +#define kTile1070 1070 +#define kTile1071 1071 +#define kTile1072 1072 +#define kTile1073 1073 +#define kTile1074 1074 +#define kTile1075 1075 +#define kTile1076 1076 +#define kTile1077 1077 +#define kTile1078 1078 +#define kTile1079 1079 +#define kTile1080 1080 +#define kTile1081 1081 +#define kTile1082 1082 +#define kTile1083 1083 +#define kTile1084 1084 +#define kTile1085 1085 +#define kTile1086 1086 +#define kTile1087 1087 +#define kTile1088 1088 +#define kTile1089 1089 +#define kTile1090 1090 +#define kTile1091 1091 +#define kTile1092 1092 +#define kTile1093 1093 +#define kTile1094 1094 +#define kTile1095 1095 +#define kTile1096 1096 +#define kTile1097 1097 +#define kTile1098 1098 +#define kTile1099 1099 +#define kTile1100 1100 +#define kTile1101 1101 +#define kTile1102 1102 +#define kTile1103 1103 +#define kTile1104 1104 +#define kTile1105 1105 +#define kTile1106 1106 +#define kTile1107 1107 +#define kTile1108 1108 +#define kTile1109 1109 +#define kTile1110 1110 +#define kTile1111 1111 +#define kTile1112 1112 +#define kTile1113 1113 +#define kTile1114 1114 +#define kTile1115 1115 +#define kTile1116 1116 +#define kTile1117 1117 +#define kTile1118 1118 +#define kTile1119 1119 +#define kTile1120 1120 +#define kTile1121 1121 +#define kTile1122 1122 +#define kTile1123 1123 +#define kTile1124 1124 +#define kTile1125 1125 +#define kTile1126 1126 +#define kTile1127 1127 +#define kTile1128 1128 +#define kTile1129 1129 +#define kTile1130 1130 +#define kTile1131 1131 +#define kTile1132 1132 +#define kTile1133 1133 +#define kTile1134 1134 +#define kTile1135 1135 +#define kTile1136 1136 +#define kTile1137 1137 +#define kTile1138 1138 +#define kTile1139 1139 +#define kTile1140 1140 +#define kTile1141 1141 +#define kTile1142 1142 +#define kTile1143 1143 +#define kTile1144 1144 +#define kTile1145 1145 +#define kTile1146 1146 +#define kTile1147 1147 +#define kTile1148 1148 +#define kTile1149 1149 +#define kTile1150 1150 +#define kTile1151 1151 +#define kTile1152 1152 +#define kTile1153 1153 +#define kTile1154 1154 +#define kTile1155 1155 +#define kTile1156 1156 +#define kTile1157 1157 +#define kTile1158 1158 +#define kTile1159 1159 +#define kTile1160 1160 +#define kTile1161 1161 +#define kTile1162 1162 +#define kTile1163 1163 +#define kTile1164 1164 +#define kTile1165 1165 +#define kTile1166 1166 +#define kTile1167 1167 +#define kTile1168 1168 +#define kTile1169 1169 +#define kTile1170 1170 +#define kTile1171 1171 +#define kTile1172 1172 +#define kTile1173 1173 +#define kTile1174 1174 +#define kTile1175 1175 +#define kTile1176 1176 +#define kTile1177 1177 +#define kTile1178 1178 +#define kTile1179 1179 +#define kTile1180 1180 +#define kTile1181 1181 +#define kTile1182 1182 +#define kTile1183 1183 +#define kTile1184 1184 +#define kTile1185 1185 +#define kTile1186 1186 +#define kTile1187 1187 +#define kTile1188 1188 +#define kTile1189 1189 +#define kTile1190 1190 +#define kTile1191 1191 +#define kTile1192 1192 +#define kTile1193 1193 +#define kTile1194 1194 +#define kTile1195 1195 +#define kTile1196 1196 +#define kTile1197 1197 +#define kTile1198 1198 +#define kTile1199 1199 +#define kTile1200 1200 +#define kTile1201 1201 +#define kTile1202 1202 +#define kTile1203 1203 +#define kTile1204 1204 +#define kTile1205 1205 +#define kTile1206 1206 +#define kTile1207 1207 +#define kTile1208 1208 +#define kTile1209 1209 +#define kTile1210 1210 +#define kTile1211 1211 +#define kTile1212 1212 +#define kTile1213 1213 +#define kTile1214 1214 +#define kTile1215 1215 +#define kTile1216 1216 +#define kTile1217 1217 +#define kTile1218 1218 +#define kTile1219 1219 +#define kTile1220 1220 +#define kTile1221 1221 +#define kTile1222 1222 +#define kTile1223 1223 +#define kTile1224 1224 +#define kTile1225 1225 +#define kTile1226 1226 +#define kTile1227 1227 +#define kTile1228 1228 +#define kTile1229 1229 +#define kTile1230 1230 +#define kTile1231 1231 +#define kTile1232 1232 +#define kTile1233 1233 +#define kTile1234 1234 +#define kTile1235 1235 +#define kTile1236 1236 +#define kTile1237 1237 +#define kTile1238 1238 +#define kTile1239 1239 +#define kTile1240 1240 +#define kTile1241 1241 +#define kTile1242 1242 +#define kTile1243 1243 +#define kTile1244 1244 +#define kTile1245 1245 +#define kTile1246 1246 +#define kTile1247 1247 +#define kTile1248 1248 +#define kTile1249 1249 +#define kTile1250 1250 +#define kTile1251 1251 +#define kTile1252 1252 +#define kTile1253 1253 +#define kTile1254 1254 +#define kTile1255 1255 +#define kTile1256 1256 +#define kTile1257 1257 +#define kTile1258 1258 +#define kTile1259 1259 +#define kTile1260 1260 +#define kTile1261 1261 +#define kTile1262 1262 +#define kTile1263 1263 +#define kTile1264 1264 +#define kTile1265 1265 +#define kTile1266 1266 +#define kTile1267 1267 +#define kTile1268 1268 +#define kTile1269 1269 +#define kTile1270 1270 +#define kTile1271 1271 +#define kTile1272 1272 +#define kTile1273 1273 +#define kTile1274 1274 +#define kTile1275 1275 +#define kTile1276 1276 +#define kTile1277 1277 +#define kTile1278 1278 +#define kTile1279 1279 +#define kTile1280 1280 +#define kTile1281 1281 +#define kTile1282 1282 +#define kTile1283 1283 +#define kTile1284 1284 +#define kTile1285 1285 +#define kTile1286 1286 +#define kTile1287 1287 +#define kTile1288 1288 +#define kTile1289 1289 +#define kTile1290 1290 +#define kTile1291 1291 +#define kTile1292 1292 +#define kTile1293 1293 +#define kTile1294 1294 +#define kTile1295 1295 +#define kTile1296 1296 +#define kTile1297 1297 +#define kTile1298 1298 +#define kTile1299 1299 +#define kTile1300 1300 +#define kTile1301 1301 +#define kTile1302 1302 +#define kTile1303 1303 +#define kTile1304 1304 +#define kTile1305 1305 +#define kTile1306 1306 +#define kTile1307 1307 +#define kTile1308 1308 +#define kTile1309 1309 +#define kTile1310 1310 +#define kTile1311 1311 +#define kTile1312 1312 +#define kTile1313 1313 +#define kTile1314 1314 +#define kTile1315 1315 +#define kTile1316 1316 +#define kTile1317 1317 +#define kTile1318 1318 +#define kTile1319 1319 +#define kTile1320 1320 +#define kTile1321 1321 +#define kTile1322 1322 +#define kTile1323 1323 +#define kTile1324 1324 +#define kTile1325 1325 +#define kTile1326 1326 +#define kTile1327 1327 +#define kTile1328 1328 +#define kTile1329 1329 +#define kTile1330 1330 +#define kTile1331 1331 +#define kTile1332 1332 +#define kTile1333 1333 +#define kTile1334 1334 +#define kTile1335 1335 +#define kTile1336 1336 +#define kTile1337 1337 +#define kTile1338 1338 +#define kTile1339 1339 +#define kTile1340 1340 +#define kTile1341 1341 +#define kTile1342 1342 +#define kTile1343 1343 +#define kTile1344 1344 +#define kTile1345 1345 +#define kTile1346 1346 +#define kTile1347 1347 +#define kTile1348 1348 +#define kTile1349 1349 +#define kTile1350 1350 +#define kTile1351 1351 +#define kTile1352 1352 +#define kTile1353 1353 +#define kTile1354 1354 +#define kTile1355 1355 +#define kTile1356 1356 +#define kTile1357 1357 +#define kTile1358 1358 +#define kTile1359 1359 +#define kTile1360 1360 +#define kTile1361 1361 +#define kTile1362 1362 +#define kTile1363 1363 +#define kTile1364 1364 +#define kTile1365 1365 +#define kTile1366 1366 +#define kTile1367 1367 +#define kTile1368 1368 +#define kTile1369 1369 +#define kTile1370 1370 +#define kTile1371 1371 +#define kTile1372 1372 +#define kTile1373 1373 +#define kTile1374 1374 +#define kTile1375 1375 +#define kTile1376 1376 +#define kTile1377 1377 +#define kTile1378 1378 +#define kTile1379 1379 +#define kTile1380 1380 +#define kTile1381 1381 +#define kTile1382 1382 +#define kTile1383 1383 +#define kTile1384 1384 +#define kTile1385 1385 +#define kTile1386 1386 +#define kTile1387 1387 +#define kTile1388 1388 +#define kTile1389 1389 +#define kTile1390 1390 +#define kTile1391 1391 +#define kTile1392 1392 +#define kTile1393 1393 +#define kTile1394 1394 +#define kTile1395 1395 +#define kTile1396 1396 +#define kTile1397 1397 +#define kTile1398 1398 +#define kTile1399 1399 +#define kTile1400 1400 +#define kTile1401 1401 +#define kTile1402 1402 +#define kTile1403 1403 +#define kTile1404 1404 +#define kTile1405 1405 +#define kTile1406 1406 +#define kTile1407 1407 +#define kTile1408 1408 +#define kTile1409 1409 +#define kTile1410 1410 +#define kTile1411 1411 +#define kTile1412 1412 +#define kTile1413 1413 +#define kTile1414 1414 +#define kTile1415 1415 +#define kTile1416 1416 +#define kTile1417 1417 +#define kTile1418 1418 +#define kTile1419 1419 +#define kTile1420 1420 +#define kTile1421 1421 +#define kTile1422 1422 +#define kTile1423 1423 +#define kTile1424 1424 +#define kTile1425 1425 +#define kTile1426 1426 +#define kTile1427 1427 +#define kTile1428 1428 +#define kTile1429 1429 +#define kTile1430 1430 +#define kTile1431 1431 +#define kTile1432 1432 +#define kTile1433 1433 +#define kTile1434 1434 +#define kTile1435 1435 +#define kTile1436 1436 +#define kTile1437 1437 +#define kTile1438 1438 +#define kTile1439 1439 +#define kTile1440 1440 +#define kTile1441 1441 +#define kTile1442 1442 +#define kTile1443 1443 +#define kTile1444 1444 +#define kTile1445 1445 +#define kTile1446 1446 +#define kTile1447 1447 +#define kTile1448 1448 +#define kTile1449 1449 +#define kTile1450 1450 +#define kTile1451 1451 +#define kTile1452 1452 +#define kTile1453 1453 +#define kTile1454 1454 +#define kTile1455 1455 +#define kTile1456 1456 +#define kTile1457 1457 +#define kTile1458 1458 +#define kTile1459 1459 +#define kTile1460 1460 +#define kTile1461 1461 +#define kTile1462 1462 +#define kTile1463 1463 +#define kTile1464 1464 +#define kTile1465 1465 +#define kTile1466 1466 +#define kTile1467 1467 +#define kTile1468 1468 +#define kTile1469 1469 +#define kTile1470 1470 +#define kTile1471 1471 +#define kTile1472 1472 +#define kTile1473 1473 +#define kTile1474 1474 +#define kTile1475 1475 +#define kTile1476 1476 +#define kTile1477 1477 +#define kTile1478 1478 +#define kTile1479 1479 +#define kTile1480 1480 +#define kTile1481 1481 +#define kTile1482 1482 +#define kTile1483 1483 +#define kTile1484 1484 +#define kTile1485 1485 +#define kTile1486 1486 +#define kTile1487 1487 +#define kTile1488 1488 +#define kTile1489 1489 +#define kTile1490 1490 +#define kTile1491 1491 +#define kTile1492 1492 +#define kTile1493 1493 +#define kTile1494 1494 +#define kTile1495 1495 +#define kTile1496 1496 +#define kTile1497 1497 +#define kTile1498 1498 +#define kTile1499 1499 +#define kTile1500 1500 +#define kTile1501 1501 +#define kTile1502 1502 +#define kTile1503 1503 +#define kTile1504 1504 +#define kTile1505 1505 +#define kTile1506 1506 +#define kTile1507 1507 +#define kTile1508 1508 +#define kTile1509 1509 +#define kTile1510 1510 +#define kTile1511 1511 +#define kTile1512 1512 +#define kTile1513 1513 +#define kTile1514 1514 +#define kTile1515 1515 +#define kTile1516 1516 +#define kTile1517 1517 +#define kTile1518 1518 +#define kTile1519 1519 +#define kTile1520 1520 +#define kTile1521 1521 +#define kTile1522 1522 +#define kTile1523 1523 +#define kTile1524 1524 +#define kTile1525 1525 +#define kTile1526 1526 +#define kTile1527 1527 +#define kTile1528 1528 +#define kTile1529 1529 +#define kTile1530 1530 +#define kTile1531 1531 +#define kTile1532 1532 +#define kTile1533 1533 +#define kTile1534 1534 +#define kTile1535 1535 +#define kTile1536 1536 +#define kTile1537 1537 +#define kTile1538 1538 +#define kTile1539 1539 +#define kTile1540 1540 +#define kTile1541 1541 +#define kTile1542 1542 +#define kTile1543 1543 +#define kTile1544 1544 +#define kTile1545 1545 +#define kTile1546 1546 +#define kTile1547 1547 +#define kTile1548 1548 +#define kTile1549 1549 +#define kTile1550 1550 +#define kTile1551 1551 +#define kTile1552 1552 +#define kTile1553 1553 +#define kTile1554 1554 +#define kTile1555 1555 +#define kTile1556 1556 +#define kTile1557 1557 +#define kTile1558 1558 +#define kTile1559 1559 +#define kTile1560 1560 +#define kTile1561 1561 +#define kTile1562 1562 +#define kTile1563 1563 +#define kTile1564 1564 +#define kTile1565 1565 +#define kTile1566 1566 +#define kTile1567 1567 +#define kTile1568 1568 +#define kTile1569 1569 +#define kTile1570 1570 +#define kTile1571 1571 +#define kTile1572 1572 +#define kTile1573 1573 +#define kTile1574 1574 +#define kTile1575 1575 +#define kTile1576 1576 +#define kTile1577 1577 +#define kTile1578 1578 +#define kTile1579 1579 +#define kTile1580 1580 +#define kTile1581 1581 +#define kTile1582 1582 +#define kTile1583 1583 +#define kTile1584 1584 +#define kTile1585 1585 +#define kTile1586 1586 +#define kTile1587 1587 +#define kTile1588 1588 +#define kTile1589 1589 +#define kTile1590 1590 +#define kTile1591 1591 +#define kTile1592 1592 +#define kTile1593 1593 +#define kTile1594 1594 +#define kTile1595 1595 +#define kTile1596 1596 +#define kTile1597 1597 +#define kTile1598 1598 +#define kTile1599 1599 +#define kTile1600 1600 +#define kTile1601 1601 +#define kTile1602 1602 +#define kTile1603 1603 +#define kTile1604 1604 +#define kTile1605 1605 +#define kTile1606 1606 +#define kTile1607 1607 +#define kTile1608 1608 +#define kTile1609 1609 +#define kTile1610 1610 +#define kTile1611 1611 +#define kTile1612 1612 +#define kTile1613 1613 +#define kTile1614 1614 +#define kTile1615 1615 +#define kTile1616 1616 +#define kTile1617 1617 +#define kTile1618 1618 +#define kTile1619 1619 +#define kTile1620 1620 +#define kTile1621 1621 +#define kTile1622 1622 +#define kTile1623 1623 +#define kTile1624 1624 +#define kTile1625 1625 +#define kTile1626 1626 +#define kTile1627 1627 +#define kTile1628 1628 +#define kTile1629 1629 +#define kTile1630 1630 +#define kTile1631 1631 +#define kTile1632 1632 +#define kTile1633 1633 +#define kTile1634 1634 +#define kTile1635 1635 +#define kTile1636 1636 +#define kTile1637 1637 +#define kTile1638 1638 +#define kTile1639 1639 +#define kTile1640 1640 +#define kTile1641 1641 +#define kTile1642 1642 +#define kTile1643 1643 +#define kTile1644 1644 +#define kTile1645 1645 +#define kTile1646 1646 +#define kTile1647 1647 +#define kTile1648 1648 +#define kTile1649 1649 +#define kTile1650 1650 +#define kTile1651 1651 +#define kTile1652 1652 +#define kTile1653 1653 +#define kTile1654 1654 +#define kTile1655 1655 +#define kTile1656 1656 +#define kTile1657 1657 +#define kTile1658 1658 +#define kTile1659 1659 +#define kTile1660 1660 +#define kTile1661 1661 +#define kTile1662 1662 +#define kTile1663 1663 +#define kTile1664 1664 +#define kTile1665 1665 +#define kTile1666 1666 +#define kTile1667 1667 +#define kTile1668 1668 +#define kTile1669 1669 +#define kTile1670 1670 +#define kTile1671 1671 +#define kTile1672 1672 +#define kTile1673 1673 +#define kTile1674 1674 +#define kTile1675 1675 +#define kTile1676 1676 +#define kTile1677 1677 +#define kTile1678 1678 +#define kTile1679 1679 +#define kTile1680 1680 +#define kTile1681 1681 +#define kTile1682 1682 +#define kTile1683 1683 +#define kTile1684 1684 +#define kTile1685 1685 +#define kTile1686 1686 +#define kTile1687 1687 +#define kTile1688 1688 +#define kTile1689 1689 +#define kTile1690 1690 +#define kTile1691 1691 +#define kTile1692 1692 +#define kTile1693 1693 +#define kTile1694 1694 +#define kTile1695 1695 +#define kTile1696 1696 +#define kTile1697 1697 +#define kTile1698 1698 +#define kTile1699 1699 +#define kTile1700 1700 +#define kTile1701 1701 +#define kTile1702 1702 +#define kTile1703 1703 +#define kTile1704 1704 +#define kTile1705 1705 +#define kTile1706 1706 +#define kTile1707 1707 +#define kTile1708 1708 +#define kTile1709 1709 +#define kTile1710 1710 +#define kTile1711 1711 +#define kTile1712 1712 +#define kTile1713 1713 +#define kTile1714 1714 +#define kTile1715 1715 +#define kTile1716 1716 +#define kTile1717 1717 +#define kTile1718 1718 +#define kTile1719 1719 +#define kTile1720 1720 +#define kTile1721 1721 +#define kTile1722 1722 +#define kTile1723 1723 +#define kTile1724 1724 +#define kTile1725 1725 +#define kTile1726 1726 +#define kTile1727 1727 +#define kTile1728 1728 +#define kTile1729 1729 +#define kTile1730 1730 +#define kTile1731 1731 +#define kTile1732 1732 +#define kTile1733 1733 +#define kTile1734 1734 +#define kTile1735 1735 +#define kTile1736 1736 +#define kTile1737 1737 +#define kTile1738 1738 +#define kTile1739 1739 +#define kTile1740 1740 +#define kTile1741 1741 +#define kTile1742 1742 +#define kTile1743 1743 +#define kTile1744 1744 +#define kTile1745 1745 +#define kTile1746 1746 +#define kTile1747 1747 +#define kTile1748 1748 +#define kTile1749 1749 +#define kTile1750 1750 +#define kTile1751 1751 +#define kTile1752 1752 +#define kTile1753 1753 +#define kTile1754 1754 +#define kTile1755 1755 +#define kTile1756 1756 +#define kTile1757 1757 +#define kTile1758 1758 +#define kTile1759 1759 +#define kTile1760 1760 +#define kTile1761 1761 +#define kTile1762 1762 +#define kTile1763 1763 +#define kTile1764 1764 +#define kTile1765 1765 +#define kTile1766 1766 +#define kTile1767 1767 +#define kTile1768 1768 +#define kTile1769 1769 +#define kTile1770 1770 +#define kTile1771 1771 +#define kTile1772 1772 +#define kTile1773 1773 +#define kTile1774 1774 +#define kTile1775 1775 +#define kTile1776 1776 +#define kTile1777 1777 +#define kTile1778 1778 +#define kTile1779 1779 +#define kTile1780 1780 +#define kTile1781 1781 +#define kTile1782 1782 +#define kTile1783 1783 +#define kTile1784 1784 +#define kTile1785 1785 +#define kTile1786 1786 +#define kTile1787 1787 +#define kTile1788 1788 +#define kTile1789 1789 +#define kTile1790 1790 +#define kTile1791 1791 +#define kTile1792 1792 +#define kTile1793 1793 +#define kTile1794 1794 +#define kTile1795 1795 +#define kTile1796 1796 +#define kTile1797 1797 +#define kTile1798 1798 +#define kTile1799 1799 +#define kTile1800 1800 +#define kTile1801 1801 +#define kTile1802 1802 +#define kTile1803 1803 +#define kTile1804 1804 +#define kTile1805 1805 +#define kTile1806 1806 +#define kTile1807 1807 +#define kTile1808 1808 +#define kTile1809 1809 +#define kTile1810 1810 +#define kTile1811 1811 +#define kTile1812 1812 +#define kTile1813 1813 +#define kTile1814 1814 +#define kTile1815 1815 +#define kTile1816 1816 +#define kTile1817 1817 +#define kTile1818 1818 +#define kTile1819 1819 +#define kTile1820 1820 +#define kTile1821 1821 +#define kTile1822 1822 +#define kTile1823 1823 +#define kTile1824 1824 +#define kTile1825 1825 +#define kTile1826 1826 +#define kTile1827 1827 +#define kTile1828 1828 +#define kTile1829 1829 +#define kTile1830 1830 +#define kTile1831 1831 +#define kTile1832 1832 +#define kTile1833 1833 +#define kTile1834 1834 +#define kTile1835 1835 +#define kTile1836 1836 +#define kTile1837 1837 +#define kTile1838 1838 +#define kTile1839 1839 +#define kTile1840 1840 +#define kTile1841 1841 +#define kTile1842 1842 +#define kTile1843 1843 +#define kTile1844 1844 +#define kTile1845 1845 +#define kTile1846 1846 +#define kTile1847 1847 +#define kTile1848 1848 +#define kTile1849 1849 +#define kTile1850 1850 +#define kTile1851 1851 +#define kTile1852 1852 +#define kTile1853 1853 +#define kTile1854 1854 +#define kTile1855 1855 +#define kTile1856 1856 +#define kTile1857 1857 +#define kTile1858 1858 +#define kTile1859 1859 +#define kTile1860 1860 +#define kTile1861 1861 +#define kTile1862 1862 +#define kTile1863 1863 +#define kTile1864 1864 +#define kTile1865 1865 +#define kTile1866 1866 +#define kTile1867 1867 +#define kTile1868 1868 +#define kTile1869 1869 +#define kTile1870 1870 +#define kTile1871 1871 +#define kTile1872 1872 +#define kTile1873 1873 +#define kTile1874 1874 +#define kTile1875 1875 +#define kTile1876 1876 +#define kTile1877 1877 +#define kTile1878 1878 +#define kTile1879 1879 +#define kTile1880 1880 +#define kTile1881 1881 +#define kTile1882 1882 +#define kTile1883 1883 +#define kTile1884 1884 +#define kTile1885 1885 +#define kTile1886 1886 +#define kTile1887 1887 +#define kTile1888 1888 +#define kTile1889 1889 +#define kTile1890 1890 +#define kTile1891 1891 +#define kTile1892 1892 +#define kTile1893 1893 +#define kTile1894 1894 +#define kTile1895 1895 +#define kTile1896 1896 +#define kTile1897 1897 +#define kTile1898 1898 +#define kTile1899 1899 +#define kTile1900 1900 +#define kTile1901 1901 +#define kTile1902 1902 +#define kTile1903 1903 +#define kTile1904 1904 +#define kTile1905 1905 +#define kTile1906 1906 +#define kTile1907 1907 +#define kTile1908 1908 +#define kTile1909 1909 +#define kTile1910 1910 +#define kTile1911 1911 +#define kTile1912 1912 +#define kTile1913 1913 +#define kTile1914 1914 +#define kTile1915 1915 +#define kTile1916 1916 +#define kTile1917 1917 +#define kTile1918 1918 +#define kTile1919 1919 +#define kTile1920 1920 +#define kTile1921 1921 +#define kTile1922 1922 +#define kTile1923 1923 +#define kTile1924 1924 +#define kTile1925 1925 +#define kTile1926 1926 +#define kTile1927 1927 +#define kTile1928 1928 +#define kTile1929 1929 +#define kTile1930 1930 +#define kTile1931 1931 +#define kTile1932 1932 +#define kTile1933 1933 +#define kTile1934 1934 +#define kTile1935 1935 +#define kTile1936 1936 +#define kTile1937 1937 +#define kTile1938 1938 +#define kTile1939 1939 +#define kTile1940 1940 +#define kTile1941 1941 +#define kTile1942 1942 +#define kTile1943 1943 +#define kTile1944 1944 +#define kTile1945 1945 +#define kTile1946 1946 +#define kTile1947 1947 +#define kTile1948 1948 +#define kTile1949 1949 +#define kTile1950 1950 +#define kTile1951 1951 +#define kTile1952 1952 +#define kTile1953 1953 +#define kTile1954 1954 +#define kTile1955 1955 +#define kTile1956 1956 +#define kTile1957 1957 +#define kTile1958 1958 +#define kTile1959 1959 +#define kTile1960 1960 +#define kTile1961 1961 +#define kTile1962 1962 +#define kTile1963 1963 +#define kTile1964 1964 +#define kTile1965 1965 +#define kTile1966 1966 +#define kTile1967 1967 +#define kTile1968 1968 +#define kTile1969 1969 +#define kTile1970 1970 +#define kTile1971 1971 +#define kTile1972 1972 +#define kTile1973 1973 +#define kTile1974 1974 +#define kTile1975 1975 +#define kTile1976 1976 +#define kTile1977 1977 +#define kTile1978 1978 +#define kTile1979 1979 +#define kTile1980 1980 +#define kTile1981 1981 +#define kTile1982 1982 +#define kTile1983 1983 +#define kTile1984 1984 +#define kTile1985 1985 +#define kTile1986 1986 +#define kTile1987 1987 +#define kTile1988 1988 +#define kTile1989 1989 +#define kTile1990 1990 +#define kTile1991 1991 +#define kTile1992 1992 +#define kTile1993 1993 +#define kTile1994 1994 +#define kTile1995 1995 +#define kTile1996 1996 +#define kTile1997 1997 +#define kTile1998 1998 +#define kTile1999 1999 +#define kTile2000 2000 +#define kTile2001 2001 +#define kTile2002 2002 +#define kTile2003 2003 +#define kTile2004 2004 +#define kTile2005 2005 +#define kTile2006 2006 +#define kTile2007 2007 +#define kTile2008 2008 +#define kTile2009 2009 +#define kTile2010 2010 +#define kTile2011 2011 +#define kTile2012 2012 +#define kTile2013 2013 +#define kTile2014 2014 +#define kTile2015 2015 +#define kTile2016 2016 +#define kTile2017 2017 +#define kTile2018 2018 +#define kTile2019 2019 +#define kTile2020 2020 +#define kTile2021 2021 +#define kTile2022 2022 +#define kTile2023 2023 +#define kTile2024 2024 +#define kTile2025 2025 +#define kTile2026 2026 +#define kTile2027 2027 +#define kTile2028 2028 +#define kTile2029 2029 +#define kTile2030 2030 +#define kTile2031 2031 +#define kTile2032 2032 +#define kTile2033 2033 +#define kTile2034 2034 +#define kTile2035 2035 +#define kTile2036 2036 +#define kTile2037 2037 +#define kTile2038 2038 +#define kTile2039 2039 +#define kTile2040 2040 +#define kTile2041 2041 +#define kTile2042 2042 +#define kTile2043 2043 +#define kTile2044 2044 +#define kTile2045 2045 +#define kTile2046 2046 +#define kTile2047 2047 +#define kTile2048 2048 +#define kTile2049 2049 +#define kTile2050 2050 +#define kTile2051 2051 +#define kTile2052 2052 +#define kTile2053 2053 +#define kTile2054 2054 +#define kTile2055 2055 +#define kTile2056 2056 +#define kTile2057 2057 +#define kTile2058 2058 +#define kTile2059 2059 +#define kTile2060 2060 +#define kTile2061 2061 +#define kTile2062 2062 +#define kTile2063 2063 +#define kTile2064 2064 +#define kTile2065 2065 +#define kTile2066 2066 +#define kTile2067 2067 +#define kTile2068 2068 +#define kTile2069 2069 +#define kTile2070 2070 +#define kTile2071 2071 +#define kTile2072 2072 +#define kTile2073 2073 +#define kTile2074 2074 +#define kTile2075 2075 +#define kTile2076 2076 +#define kTile2077 2077 +#define kTile2078 2078 +#define kTile2079 2079 +#define kTile2080 2080 +#define kTile2081 2081 +#define kTile2082 2082 +#define kTile2083 2083 +#define kTile2084 2084 +#define kTile2085 2085 +#define kTile2086 2086 +#define kTile2087 2087 +#define kTile2088 2088 +#define kTile2089 2089 +#define kTile2090 2090 +#define kTile2091 2091 +#define kTile2092 2092 +#define kTile2093 2093 +#define kTile2094 2094 +#define kTile2095 2095 +#define kTile2096 2096 +#define kTile2097 2097 +#define kTile2098 2098 +#define kTile2099 2099 +#define kTile2100 2100 +#define kTile2101 2101 +#define kTile2102 2102 +#define kTile2103 2103 +#define kTile2104 2104 +#define kTile2105 2105 +#define kTile2106 2106 +#define kTile2107 2107 +#define kTile2108 2108 +#define kTile2109 2109 +#define kTile2110 2110 +#define kTile2111 2111 +#define kTile2112 2112 +#define kTile2113 2113 +#define kTile2114 2114 +#define kTile2115 2115 +#define kTile2116 2116 +#define kTile2117 2117 +#define kTile2118 2118 +#define kTile2119 2119 +#define kTile2120 2120 +#define kTile2121 2121 +#define kTile2122 2122 +#define kTile2123 2123 +#define kTile2124 2124 +#define kTile2125 2125 +#define kTile2126 2126 +#define kTile2127 2127 +#define kTile2128 2128 +#define kTile2129 2129 +#define kTile2130 2130 +#define kTile2131 2131 +#define kTile2132 2132 +#define kTile2133 2133 +#define kTile2134 2134 +#define kTile2135 2135 +#define kTile2136 2136 +#define kTile2137 2137 +#define kTile2138 2138 +#define kTile2139 2139 +#define kTile2140 2140 +#define kTile2141 2141 +#define kTile2142 2142 +#define kTile2143 2143 +#define kTile2144 2144 +#define kTile2145 2145 +#define kTile2146 2146 +#define kTile2147 2147 +#define kTile2148 2148 +#define kTile2149 2149 +#define kTile2150 2150 +#define kTile2151 2151 +#define kTile2152 2152 +#define kTile2153 2153 +#define kTile2154 2154 +#define kTile2155 2155 +#define kTile2156 2156 +#define kTile2157 2157 +#define kTile2158 2158 +#define kTile2159 2159 +#define kTile2160 2160 +#define kTile2161 2161 +#define kTile2162 2162 +#define kTile2163 2163 +#define kTile2164 2164 +#define kTile2165 2165 +#define kTile2166 2166 +#define kTile2167 2167 +#define kTile2168 2168 +#define kTile2169 2169 +#define kTile2170 2170 +#define kTile2171 2171 +#define kTile2172 2172 +#define kTile2173 2173 +#define kTile2174 2174 +#define kTile2175 2175 +#define kTile2176 2176 +#define kTile2177 2177 +#define kTile2178 2178 +#define kTile2179 2179 +#define kTile2180 2180 +#define kTile2181 2181 +#define kTile2182 2182 +#define kTile2183 2183 +#define kTile2184 2184 +#define kTile2185 2185 +#define kTile2186 2186 +#define kTile2187 2187 +#define kTile2188 2188 +#define kTile2189 2189 +#define kTile2190 2190 +#define kTile2191 2191 +#define kTile2192 2192 +#define kTile2193 2193 +#define kTile2194 2194 +#define kTile2195 2195 +#define kTile2196 2196 +#define kTile2197 2197 +#define kTile2198 2198 +#define kTile2199 2199 +#define kTile2200 2200 +#define kTile2201 2201 +#define kTile2202 2202 +#define kTile2203 2203 +#define kTile2204 2204 +#define kTile2205 2205 +#define kTile2206 2206 +#define kTile2207 2207 +#define kTile2208 2208 +#define kTile2209 2209 +#define kTile2210 2210 +#define kTile2211 2211 +#define kTile2212 2212 +#define kTile2213 2213 +#define kTile2214 2214 +#define kTile2215 2215 +#define kTile2216 2216 +#define kTile2217 2217 +#define kTile2218 2218 +#define kTile2219 2219 +#define kTile2220 2220 +#define kTile2221 2221 +#define kTile2222 2222 +#define kTile2223 2223 +#define kTile2224 2224 +#define kTile2225 2225 +#define kTile2226 2226 +#define kTile2227 2227 +#define kTile2228 2228 +#define kTile2229 2229 +#define kTile2230 2230 +#define kTile2231 2231 +#define kTile2232 2232 +#define kTile2233 2233 +#define kTile2234 2234 +#define kTile2235 2235 +#define kTile2236 2236 +#define kTile2237 2237 +#define kTile2238 2238 +#define kTile2239 2239 +#define kTile2240 2240 +#define kTile2241 2241 +#define kTile2242 2242 +#define kTile2243 2243 +#define kTile2244 2244 +#define kTile2245 2245 +#define kTile2246 2246 +#define kTile2247 2247 +#define kTile2248 2248 +#define kTile2249 2249 +#define kTile2250 2250 +#define kTile2251 2251 +#define kTile2252 2252 +#define kTile2253 2253 +#define kTile2254 2254 +#define kTile2255 2255 +#define kTile2256 2256 +#define kTile2257 2257 +#define kTile2258 2258 +#define kTile2259 2259 +#define kTile2260 2260 +#define kTile2261 2261 +#define kTile2262 2262 +#define kTile2263 2263 +#define kTile2264 2264 +#define kTile2265 2265 +#define kTile2266 2266 +#define kTile2267 2267 +#define kTile2268 2268 +#define kTile2269 2269 +#define kTile2270 2270 +#define kTile2271 2271 +#define kTile2272 2272 +#define kTile2273 2273 +#define kTile2274 2274 +#define kTile2275 2275 +#define kTile2276 2276 +#define kTile2277 2277 +#define kTile2278 2278 +#define kTile2279 2279 +#define kTile2280 2280 +#define kTile2281 2281 +#define kTile2282 2282 +#define kTile2283 2283 +#define kTile2284 2284 +#define kTile2285 2285 +#define kTile2286 2286 +#define kTile2287 2287 +#define kTile2288 2288 +#define kTile2289 2289 +#define kTile2290 2290 +#define kTile2291 2291 +#define kTile2292 2292 +#define kTile2293 2293 +#define kTile2294 2294 +#define kTile2295 2295 +#define kTile2296 2296 +#define kTile2297 2297 +#define kTile2298 2298 +#define kTile2299 2299 +#define kTile2300 2300 +#define kTile2301 2301 +#define kTile2302 2302 +#define kTile2303 2303 +#define kTile2304 2304 +#define kTile2305 2305 +#define kTile2306 2306 +#define kTile2307 2307 +#define kTile2308 2308 +#define kTile2309 2309 +#define kTile2310 2310 +#define kTile2311 2311 +#define kTile2312 2312 +#define kTile2313 2313 +#define kTile2314 2314 +#define kTile2315 2315 +#define kTile2316 2316 +#define kTile2317 2317 +#define kTile2318 2318 +#define kTile2319 2319 +#define kTile2320 2320 +#define kTile2321 2321 +#define kTile2322 2322 +#define kTile2323 2323 +#define kTile2324 2324 +#define kTile2325 2325 +#define kTile2326 2326 +#define kTile2327 2327 +#define kTile2328 2328 +#define kTile2329 2329 +#define kTile2330 2330 +#define kTile2331 2331 +#define kTile2332 2332 +#define kTile2333 2333 +#define kTile2334 2334 +#define kTile2335 2335 +#define kTile2336 2336 +#define kTile2337 2337 +#define kTile2338 2338 +#define kTile2339 2339 +#define kTile2340 2340 +#define kTile2341 2341 +#define kTile2342 2342 +#define kTile2343 2343 +#define kTile2344 2344 +#define kTile2345 2345 +#define kTile2346 2346 +#define kTile2347 2347 +#define kTile2348 2348 +#define kTile2349 2349 +#define kTile2350 2350 +#define kTile2351 2351 +#define kTile2352 2352 +#define kTile2353 2353 +#define kTile2354 2354 +#define kTile2355 2355 +#define kTile2356 2356 +#define kTile2357 2357 +#define kTile2358 2358 +#define kTile2359 2359 +#define kTile2360 2360 +#define kTile2361 2361 +#define kTile2362 2362 +#define kTile2363 2363 +#define kTile2364 2364 +#define kTile2365 2365 +#define kTile2366 2366 +#define kTile2367 2367 +#define kTile2368 2368 +#define kTile2369 2369 +#define kTile2370 2370 +#define kTile2371 2371 +#define kTile2372 2372 +#define kTile2373 2373 +#define kTile2374 2374 +#define kTile2375 2375 +#define kTile2376 2376 +#define kTile2377 2377 +#define kTile2378 2378 +#define kTile2379 2379 +#define kTile2380 2380 +#define kTile2381 2381 +#define kTile2382 2382 +#define kTile2383 2383 +#define kTile2384 2384 +#define kTile2385 2385 +#define kTile2386 2386 +#define kTile2387 2387 +#define kTile2388 2388 +#define kTile2389 2389 +#define kTile2390 2390 +#define kTile2391 2391 +#define kTile2392 2392 +#define kTile2393 2393 +#define kTile2394 2394 +#define kTile2395 2395 +#define kTile2396 2396 +#define kTile2397 2397 +#define kTile2398 2398 +#define kTile2399 2399 +#define kTile2400 2400 +#define kTile2401 2401 +#define kTile2402 2402 +#define kTile2403 2403 +#define kTile2404 2404 +#define kTile2405 2405 +#define kTile2406 2406 +#define kTile2407 2407 +#define kTile2408 2408 +#define kTile2409 2409 +#define kTile2410 2410 +#define kTile2411 2411 +#define kTile2412 2412 +#define kTile2413 2413 +#define kTile2414 2414 +#define kTile2415 2415 +#define kTile2416 2416 +#define kTile2417 2417 +#define kTile2418 2418 +#define kTile2419 2419 +#define kTile2420 2420 +#define kTile2421 2421 +#define kTile2422 2422 +#define kTile2423 2423 +#define kTile2424 2424 +#define kTile2425 2425 +#define kTile2426 2426 +#define kTile2427 2427 +#define kTile2428 2428 +#define kTile2429 2429 +#define kTile2430 2430 +#define kTile2431 2431 +#define kTile2432 2432 +#define kTile2433 2433 +#define kTile2434 2434 +#define kTile2435 2435 +#define kTile2436 2436 +#define kTile2437 2437 +#define kTile2438 2438 +#define kTile2439 2439 +#define kTile2440 2440 +#define kTile2441 2441 +#define kTile2442 2442 +#define kTile2443 2443 +#define kTile2444 2444 +#define kTile2445 2445 +#define kTile2446 2446 +#define kTile2447 2447 +#define kTile2448 2448 +#define kTile2449 2449 +#define kTile2450 2450 +#define kTile2451 2451 +#define kTile2452 2452 +#define kTile2453 2453 +#define kTile2454 2454 +#define kTile2455 2455 +#define kTile2456 2456 +#define kTile2457 2457 +#define kTile2458 2458 +#define kTile2459 2459 +#define kTile2460 2460 +#define kTile2461 2461 +#define kTile2462 2462 +#define kTile2463 2463 +#define kTile2464 2464 +#define kTile2465 2465 +#define kTile2466 2466 +#define kTile2467 2467 +#define kTile2468 2468 +#define kTile2469 2469 +#define kTile2470 2470 +#define kTile2471 2471 +#define kTile2472 2472 +#define kTile2473 2473 +#define kTile2474 2474 +#define kTile2475 2475 +#define kTile2476 2476 +#define kTile2477 2477 +#define kTile2478 2478 +#define kTile2479 2479 +#define kTile2480 2480 +#define kTile2481 2481 +#define kTile2482 2482 +#define kTile2483 2483 +#define kTile2484 2484 +#define kTile2485 2485 +#define kTile2486 2486 +#define kTile2487 2487 +#define kTile2488 2488 +#define kTile2489 2489 +#define kTile2490 2490 +#define kTile2491 2491 +#define kTile2492 2492 +#define kTile2493 2493 +#define kTile2494 2494 +#define kTile2495 2495 +#define kTile2496 2496 +#define kTile2497 2497 +#define kTile2498 2498 +#define kTile2499 2499 +#define kTile2500 2500 +#define kTile2501 2501 +#define kTile2502 2502 +#define kTile2503 2503 +#define kTile2504 2504 +#define kTile2505 2505 +#define kTile2506 2506 +#define kTile2507 2507 +#define kTile2508 2508 +#define kTile2509 2509 +#define kTile2510 2510 +#define kTile2511 2511 +#define kTile2512 2512 +#define kTile2513 2513 +#define kTile2514 2514 +#define kTile2515 2515 +#define kTile2516 2516 +#define kTile2517 2517 +#define kTile2518 2518 +#define kTile2519 2519 +#define kTile2520 2520 +#define kTile2521 2521 +#define kTile2522 2522 +#define kTile2523 2523 +#define kTile2524 2524 +#define kTile2525 2525 +#define kTile2526 2526 +#define kTile2527 2527 +#define kTile2528 2528 +#define kTile2529 2529 +#define kTile2530 2530 +#define kTile2531 2531 +#define kTile2532 2532 +#define kTile2533 2533 +#define kTile2534 2534 +#define kTile2535 2535 +#define kTile2536 2536 +#define kTile2537 2537 +#define kTile2538 2538 +#define kTile2539 2539 +#define kTile2540 2540 +#define kTile2541 2541 +#define kTile2542 2542 +#define kTile2543 2543 +#define kTile2544 2544 +#define kTile2545 2545 +#define kTile2546 2546 +#define kTile2547 2547 +#define kTile2548 2548 +#define kTile2549 2549 +#define kTile2550 2550 +#define kTile2551 2551 +#define kTile2552 2552 +#define kTile2553 2553 +#define kTile2554 2554 +#define kTile2555 2555 +#define kTile2556 2556 +#define kTile2557 2557 +#define kTile2558 2558 +#define kTile2559 2559 +#define kTile2560 2560 +#define kTile2561 2561 +#define kTile2562 2562 +#define kTile2563 2563 +#define kTile2564 2564 +#define kTile2565 2565 +#define kTile2566 2566 +#define kTile2567 2567 +#define kTile2568 2568 +#define kTile2569 2569 +#define kTile2570 2570 +#define kTile2571 2571 +#define kTile2572 2572 +#define kTile2573 2573 +#define kTile2574 2574 +#define kTile2575 2575 +#define kTile2576 2576 +#define kTile2577 2577 +#define kTile2578 2578 +#define kTile2579 2579 +#define kTile2580 2580 +#define kTile2581 2581 +#define kTile2582 2582 +#define kTile2583 2583 +#define kTile2584 2584 +#define kTile2585 2585 +#define kTile2586 2586 +#define kTile2587 2587 +#define kTile2588 2588 +#define kTile2589 2589 +#define kTile2590 2590 +#define kTile2591 2591 +#define kTile2592 2592 +#define kTile2593 2593 +#define kTile2594 2594 +#define kTile2595 2595 +#define kTile2596 2596 +#define kTile2597 2597 +#define kTile2598 2598 +#define kTile2599 2599 +#define kTile2600 2600 +#define kTile2601 2601 +#define kTile2602 2602 +#define kTile2603 2603 +#define kTile2604 2604 +#define kTile2605 2605 +#define kTile2606 2606 +#define kTile2607 2607 +#define kTile2608 2608 +#define kTile2609 2609 +#define kTile2610 2610 +#define kTile2611 2611 +#define kTile2612 2612 +#define kTile2613 2613 +#define kTile2614 2614 +#define kTile2615 2615 +#define kTile2616 2616 +#define kTile2617 2617 +#define kTile2618 2618 +#define kTile2619 2619 +#define kTile2620 2620 +#define kTile2621 2621 +#define kTile2622 2622 +#define kTile2623 2623 +#define kTile2624 2624 +#define kTile2625 2625 +#define kTile2626 2626 +#define kTile2627 2627 +#define kTile2628 2628 +#define kTile2629 2629 +#define kTile2630 2630 +#define kTile2631 2631 +#define kTile2632 2632 +#define kTile2633 2633 +#define kTile2634 2634 +#define kTile2635 2635 +#define kTile2636 2636 +#define kTile2637 2637 +#define kTile2638 2638 +#define kTile2639 2639 +#define kTile2640 2640 +#define kTile2641 2641 +#define kTile2642 2642 +#define kTile2643 2643 +#define kTile2644 2644 +#define kTile2645 2645 +#define kTile2646 2646 +#define kTile2647 2647 +#define kTile2648 2648 +#define kTile2649 2649 +#define kTile2650 2650 +#define kTile2651 2651 +#define kTile2652 2652 +#define kTile2653 2653 +#define kTile2654 2654 +#define kTile2655 2655 +#define kTile2656 2656 +#define kTile2657 2657 +#define kTile2658 2658 +#define kTile2659 2659 +#define kTile2660 2660 +#define kTile2661 2661 +#define kTile2662 2662 +#define kTile2663 2663 +#define kTile2664 2664 +#define kTile2665 2665 +#define kTile2666 2666 +#define kTile2667 2667 +#define kTile2668 2668 +#define kTile2669 2669 +#define kTile2670 2670 +#define kTile2671 2671 +#define kTile2672 2672 +#define kTile2673 2673 +#define kTile2674 2674 +#define kTile2675 2675 +#define kTile2676 2676 +#define kTile2677 2677 +#define kTile2678 2678 +#define kTile2679 2679 +#define kTile2680 2680 +#define kTile2681 2681 +#define kTile2682 2682 +#define kTile2683 2683 +#define kTile2684 2684 +#define kTile2685 2685 +#define kTile2686 2686 +#define kTile2687 2687 +#define kTile2688 2688 +#define kTile2689 2689 +#define kTile2690 2690 +#define kTile2691 2691 +#define kTile2692 2692 +#define kTile2693 2693 +#define kTile2694 2694 +#define kTile2695 2695 +#define kTile2696 2696 +#define kTile2697 2697 +#define kTile2698 2698 +#define kTile2699 2699 +#define kTile2700 2700 +#define kTile2701 2701 +#define kTile2702 2702 +#define kTile2703 2703 +#define kTile2704 2704 +#define kTile2705 2705 +#define kTile2706 2706 +#define kTile2707 2707 +#define kTile2708 2708 +#define kTile2709 2709 +#define kTile2710 2710 +#define kTile2711 2711 +#define kTile2712 2712 +#define kTile2713 2713 +#define kTile2714 2714 +#define kTile2715 2715 +#define kTile2716 2716 +#define kTile2717 2717 +#define kTile2718 2718 +#define kTile2719 2719 +#define kTile2720 2720 +#define kTile2721 2721 +#define kTile2722 2722 +#define kTile2723 2723 +#define kTile2724 2724 +#define kTile2725 2725 +#define kTile2726 2726 +#define kTile2727 2727 +#define kTile2728 2728 +#define kTile2729 2729 +#define kTile2730 2730 +#define kTile2731 2731 +#define kTile2732 2732 +#define kTile2733 2733 +#define kTile2734 2734 +#define kTile2735 2735 +#define kTile2736 2736 +#define kTile2737 2737 +#define kTile2738 2738 +#define kTile2739 2739 +#define kTile2740 2740 +#define kTile2741 2741 +#define kTile2742 2742 +#define kTile2743 2743 +#define kTile2744 2744 +#define kTile2745 2745 +#define kTile2746 2746 +#define kTile2747 2747 +#define kTile2748 2748 +#define kTile2749 2749 +#define kTile2750 2750 +#define kTile2751 2751 +#define kTile2752 2752 +#define kTile2753 2753 +#define kTile2754 2754 +#define kTile2755 2755 +#define kTile2756 2756 +#define kTile2757 2757 +#define kTile2758 2758 +#define kTile2759 2759 +#define kTile2760 2760 +#define kTile2761 2761 +#define kTile2762 2762 +#define kTile2763 2763 +#define kTile2764 2764 +#define kTile2765 2765 +#define kTile2766 2766 +#define kTile2767 2767 +#define kTile2768 2768 +#define kTile2769 2769 +#define kTile2770 2770 +#define kTile2771 2771 +#define kTile2772 2772 +#define kTile2773 2773 +#define kTile2774 2774 +#define kTile2775 2775 +#define kTile2776 2776 +#define kTile2777 2777 +#define kTile2778 2778 +#define kTile2779 2779 +#define kTile2780 2780 +#define kTile2781 2781 +#define kTile2782 2782 +#define kTile2783 2783 +#define kTile2784 2784 +#define kTile2785 2785 +#define kTile2786 2786 +#define kTile2787 2787 +#define kTile2788 2788 +#define kTile2789 2789 +#define kTile2790 2790 +#define kTile2791 2791 +#define kTile2792 2792 +#define kTile2793 2793 +#define kTile2794 2794 +#define kTile2795 2795 +#define kTile2796 2796 +#define kTile2797 2797 +#define kTile2798 2798 +#define kTile2799 2799 +#define kTile2800 2800 +#define kTile2801 2801 +#define kTile2802 2802 +#define kTile2803 2803 +#define kTile2804 2804 +#define kTile2805 2805 +#define kTile2806 2806 +#define kTile2807 2807 +#define kTile2808 2808 +#define kTile2809 2809 +#define kTile2810 2810 +#define kTile2811 2811 +#define kTile2812 2812 +#define kTile2813 2813 +#define kTile2814 2814 +#define kTile2815 2815 +#define kTile2816 2816 +#define kTile2817 2817 +#define kTile2818 2818 +#define kTile2819 2819 +#define kTile2820 2820 +#define kTile2821 2821 +#define kTile2822 2822 +#define kTile2823 2823 +#define kTile2824 2824 +#define kTile2825 2825 +#define kTile2826 2826 +#define kTile2827 2827 +#define kTile2828 2828 +#define kTile2829 2829 +#define kTile2830 2830 +#define kTile2831 2831 +#define kTile2832 2832 +#define kTile2833 2833 +#define kTile2834 2834 +#define kTile2835 2835 +#define kTile2836 2836 +#define kTile2837 2837 +#define kTile2838 2838 +#define kTile2839 2839 +#define kTile2840 2840 +#define kTile2841 2841 +#define kTile2842 2842 +#define kTile2843 2843 +#define kTile2844 2844 +#define kTile2845 2845 +#define kTile2846 2846 +#define kTile2847 2847 +#define kTile2848 2848 +#define kTile2849 2849 +#define kTile2850 2850 +#define kTile2851 2851 +#define kTile2852 2852 +#define kTile2853 2853 +#define kTile2854 2854 +#define kTile2855 2855 +#define kTile2856 2856 +#define kTile2857 2857 +#define kTile2858 2858 +#define kTile2859 2859 +#define kTile2860 2860 +#define kTile2861 2861 +#define kTile2862 2862 +#define kTile2863 2863 +#define kTile2864 2864 +#define kTile2865 2865 +#define kTile2866 2866 +#define kTile2867 2867 +#define kTile2868 2868 +#define kTile2869 2869 +#define kTile2870 2870 +#define kTile2871 2871 +#define kTile2872 2872 +#define kTile2873 2873 +#define kTile2874 2874 +#define kTile2875 2875 +#define kTile2876 2876 +#define kTile2877 2877 +#define kTile2878 2878 +#define kTile2879 2879 +#define kTile2880 2880 +#define kTile2881 2881 +#define kTile2882 2882 +#define kTile2883 2883 +#define kTile2884 2884 +#define kTile2885 2885 +#define kTile2886 2886 +#define kTile2887 2887 +#define kTile2888 2888 +#define kTile2889 2889 +#define kTile2890 2890 +#define kTile2891 2891 +#define kTile2892 2892 +#define kTile2893 2893 +#define kTile2894 2894 +#define kTile2895 2895 +#define kTile2896 2896 +#define kTile2897 2897 +#define kTile2898 2898 +#define kTile2899 2899 +#define kTile2900 2900 +#define kTile2901 2901 +#define kTile2902 2902 +#define kTile2903 2903 +#define kTile2904 2904 +#define kTile2905 2905 +#define kTile2906 2906 +#define kTile2907 2907 +#define kTile2908 2908 +#define kTile2909 2909 +#define kTile2910 2910 +#define kTile2911 2911 +#define kTile2912 2912 +#define kTile2913 2913 +#define kTile2914 2914 +#define kTile2915 2915 +#define kTile2916 2916 +#define kTile2917 2917 +#define kTile2918 2918 +#define kTile2919 2919 +#define kTile2920 2920 +#define kTile2921 2921 +#define kTile2922 2922 +#define kTile2923 2923 +#define kTile2924 2924 +#define kTile2925 2925 +#define kTile2926 2926 +#define kTile2927 2927 +#define kTile2928 2928 +#define kTile2929 2929 +#define kTile2930 2930 +#define kTile2931 2931 +#define kTile2932 2932 +#define kTile2933 2933 +#define kTile2934 2934 +#define kTile2935 2935 +#define kTile2936 2936 +#define kTile2937 2937 +#define kTile2938 2938 +#define kTile2939 2939 +#define kTile2940 2940 +#define kTile2941 2941 +#define kTile2942 2942 +#define kTile2943 2943 +#define kTile2944 2944 +#define kTile2945 2945 +#define kTile2946 2946 +#define kTile2947 2947 +#define kTile2948 2948 +#define kTile2949 2949 +#define kTile2950 2950 +#define kTile2951 2951 +#define kTile2952 2952 +#define kTile2953 2953 +#define kTile2954 2954 +#define kTile2955 2955 +#define kTile2956 2956 +#define kTile2957 2957 +#define kTile2958 2958 +#define kTile2959 2959 +#define kTile2960 2960 +#define kTile2961 2961 +#define kTile2962 2962 +#define kTile2963 2963 +#define kTile2964 2964 +#define kTile2965 2965 +#define kTile2966 2966 +#define kTile2967 2967 +#define kTile2968 2968 +#define kTile2969 2969 +#define kTile2970 2970 +#define kTile2971 2971 +#define kTile2972 2972 +#define kTile2973 2973 +#define kTile2974 2974 +#define kTile2975 2975 +#define kTile2976 2976 +#define kTile2977 2977 +#define kTile2978 2978 +#define kTile2979 2979 +#define kTile2980 2980 +#define kTile2981 2981 +#define kTile2982 2982 +#define kTile2983 2983 +#define kTile2984 2984 +#define kTile2985 2985 +#define kTile2986 2986 +#define kTile2987 2987 +#define kTile2988 2988 +#define kTile2989 2989 +#define kTile2990 2990 +#define kTile2991 2991 +#define kTile2992 2992 +#define kTile2993 2993 +#define kTile2994 2994 +#define kTile2995 2995 +#define kTile2996 2996 +#define kTile2997 2997 +#define kTile2998 2998 +#define kTile2999 2999 +#define kTile3000 3000 +#define kTile3001 3001 +#define kTile3002 3002 +#define kTile3003 3003 +#define kTile3004 3004 +#define kTile3005 3005 +#define kTile3006 3006 +#define kTile3007 3007 +#define kTile3008 3008 +#define kTile3009 3009 +#define kTile3010 3010 +#define kTile3011 3011 +#define kTile3012 3012 +#define kTile3013 3013 +#define kTile3014 3014 +#define kTile3015 3015 +#define kTile3016 3016 +#define kTile3017 3017 +#define kTile3018 3018 +#define kTile3019 3019 +#define kTile3020 3020 +#define kTile3021 3021 +#define kTile3022 3022 +#define kTile3023 3023 +#define kTile3024 3024 +#define kTile3025 3025 +#define kTile3026 3026 +#define kTile3027 3027 +#define kTile3028 3028 +#define kTile3029 3029 +#define kTile3030 3030 +#define kTile3031 3031 +#define kTile3032 3032 +#define kTile3033 3033 +#define kTile3034 3034 +#define kTile3035 3035 +#define kTile3036 3036 +#define kTile3037 3037 +#define kTile3038 3038 +#define kTile3039 3039 +#define kTile3040 3040 +#define kTile3041 3041 +#define kTile3042 3042 +#define kTile3043 3043 +#define kTile3044 3044 +#define kTile3045 3045 +#define kTile3046 3046 +#define kTile3047 3047 +#define kTile3048 3048 +#define kTile3049 3049 +#define kTile3050 3050 +#define kTile3051 3051 +#define kTile3052 3052 +#define kTile3053 3053 +#define kTile3054 3054 +#define kTile3055 3055 +#define kTile3056 3056 +#define kTile3057 3057 +#define kTile3058 3058 +#define kTile3059 3059 +#define kTile3060 3060 +#define kTile3061 3061 +#define kTile3062 3062 +#define kTile3063 3063 +#define kTile3064 3064 +#define kTile3065 3065 +#define kTile3066 3066 +#define kTile3067 3067 +#define kTile3068 3068 +#define kTile3069 3069 +#define kTile3070 3070 +#define kTile3071 3071 +#define kTile3072 3072 +#define kTile3073 3073 +#define kTile3074 3074 +#define kTile3075 3075 +#define kTile3076 3076 +#define kTile3077 3077 +#define kTile3078 3078 +#define kTile3079 3079 +#define kTile3080 3080 +#define kTile3081 3081 +#define kTile3082 3082 +#define kTile3083 3083 +#define kTile3084 3084 +#define kTile3085 3085 +#define kTile3086 3086 +#define kTile3087 3087 +#define kTile3088 3088 +#define kTile3089 3089 +#define kTile3090 3090 +#define kTile3091 3091 +#define kTile3092 3092 +#define kTile3093 3093 +#define kTile3094 3094 +#define kTile3095 3095 +#define kTile3096 3096 +#define kTile3097 3097 +#define kTile3098 3098 +#define kTile3099 3099 +#define kTile3100 3100 +#define kTile3101 3101 +#define kTile3102 3102 +#define kTile3103 3103 +#define kTile3104 3104 +#define kTile3105 3105 +#define kTile3106 3106 +#define kTile3107 3107 +#define kTile3108 3108 +#define kTile3109 3109 +#define kTile3110 3110 +#define kTile3111 3111 +#define kTile3112 3112 +#define kTile3113 3113 +#define kTile3114 3114 +#define kTile3115 3115 +#define kTile3116 3116 +#define kTile3117 3117 +#define kTile3118 3118 +#define kTile3119 3119 +#define kTile3120 3120 +#define kTile3121 3121 +#define kTile3122 3122 +#define kTile3123 3123 +#define kTile3124 3124 +#define kTile3125 3125 +#define kTile3126 3126 +#define kTile3127 3127 +#define kTile3128 3128 +#define kTile3129 3129 +#define kTile3130 3130 +#define kTile3131 3131 +#define kTile3132 3132 +#define kTile3133 3133 +#define kTile3134 3134 +#define kTile3135 3135 +#define kTile3136 3136 +#define kTile3137 3137 +#define kTile3138 3138 +#define kTile3139 3139 +#define kTile3140 3140 +#define kTile3141 3141 +#define kTile3142 3142 +#define kTile3143 3143 +#define kTile3144 3144 +#define kTile3145 3145 +#define kTile3146 3146 +#define kTile3147 3147 +#define kTile3148 3148 +#define kTile3149 3149 +#define kTile3150 3150 +#define kTile3151 3151 +#define kTile3152 3152 +#define kTile3153 3153 +#define kTile3154 3154 +#define kTile3155 3155 +#define kTile3156 3156 +#define kTile3157 3157 +#define kTile3158 3158 +#define kTile3159 3159 +#define kTile3160 3160 +#define kTile3161 3161 +#define kTile3162 3162 +#define kTile3163 3163 +#define kTile3164 3164 +#define kTile3165 3165 +#define kTile3166 3166 +#define kTile3167 3167 +#define kTile3168 3168 +#define kTile3169 3169 +#define kTile3170 3170 +#define kTile3171 3171 +#define kTile3172 3172 +#define kTile3173 3173 +#define kTile3174 3174 +#define kTile3175 3175 +#define kTile3176 3176 +#define kTile3177 3177 +#define kTile3178 3178 +#define kTile3179 3179 +#define kTile3180 3180 +#define kTile3181 3181 +#define kTile3182 3182 +#define kTile3183 3183 +#define kTile3184 3184 +#define kTile3185 3185 +#define kTile3186 3186 +#define kTile3187 3187 +#define kTile3188 3188 +#define kTile3189 3189 +#define kTile3190 3190 +#define kTile3191 3191 +#define kTile3192 3192 +#define kTile3193 3193 +#define kTile3194 3194 +#define kTile3195 3195 +#define kTile3196 3196 +#define kTile3197 3197 +#define kTile3198 3198 +#define kTile3199 3199 +#define kTile3200 3200 +#define kTile3201 3201 +#define kTile3202 3202 +#define kTile3203 3203 +#define kTile3204 3204 +#define kTile3205 3205 +#define kTile3206 3206 +#define kTile3207 3207 +#define kTile3208 3208 +#define kTile3209 3209 +#define kTile3210 3210 +#define kTile3211 3211 +#define kTile3212 3212 +#define kTile3213 3213 +#define kTile3214 3214 +#define kTile3215 3215 +#define kTile3216 3216 +#define kTile3217 3217 +#define kTile3218 3218 +#define kTile3219 3219 +#define kTile3220 3220 +#define kTile3221 3221 +#define kTile3222 3222 +#define kTile3223 3223 +#define kTile3224 3224 +#define kTile3225 3225 +#define kTile3226 3226 +#define kTile3227 3227 +#define kTile3228 3228 +#define kTile3229 3229 +#define kTile3230 3230 +#define kTile3231 3231 +#define kTile3232 3232 +#define kTile3233 3233 +#define kTile3234 3234 +#define kTile3235 3235 +#define kTile3236 3236 +#define kTile3237 3237 +#define kTile3238 3238 +#define kTile3239 3239 +#define kTile3240 3240 +#define kTile3241 3241 +#define kTile3242 3242 +#define kTile3243 3243 +#define kTile3244 3244 +#define kTile3245 3245 +#define kTile3246 3246 +#define kTile3247 3247 +#define kTile3248 3248 +#define kTile3249 3249 +#define kTile3250 3250 +#define kTile3251 3251 +#define kTile3252 3252 +#define kTile3253 3253 +#define kTile3254 3254 +#define kTile3255 3255 +#define kTile3256 3256 +#define kTile3257 3257 +#define kTile3258 3258 +#define kTile3259 3259 +#define kTile3260 3260 +#define kTile3261 3261 +#define kTile3262 3262 +#define kTile3263 3263 +#define kTile3264 3264 +#define kTile3265 3265 +#define kTile3266 3266 +#define kTile3267 3267 +#define kTile3268 3268 +#define kTile3269 3269 +#define kTile3270 3270 +#define kTile3271 3271 +#define kTile3272 3272 +#define kTile3273 3273 +#define kTile3274 3274 +#define kTile3275 3275 +#define kTile3276 3276 +#define kTile3277 3277 +#define kTile3278 3278 +#define kTile3279 3279 +#define kTile3280 3280 +#define kTile3281 3281 +#define kTile3282 3282 +#define kTile3283 3283 +#define kTile3284 3284 +#define kTile3285 3285 +#define kTile3286 3286 +#define kTile3287 3287 +#define kTile3288 3288 +#define kTile3289 3289 +#define kTile3290 3290 +#define kTile3291 3291 +#define kTile3292 3292 +#define kTile3293 3293 +#define kTile3294 3294 +#define kTile3295 3295 +#define kTile3296 3296 +#define kTile3297 3297 +#define kTile3298 3298 +#define kTile3299 3299 +#define kTile3300 3300 +#define kTile3301 3301 +#define kTile3302 3302 +#define kTile3303 3303 +#define kTile3304 3304 +#define kTile3305 3305 +#define kTile3306 3306 +#define kTile3307 3307 +#define kTile3308 3308 +#define kTile3309 3309 +#define kTile3310 3310 +#define kTile3311 3311 +#define kTile3312 3312 +#define kTile3313 3313 +#define kTile3314 3314 +#define kTile3315 3315 +#define kTile3316 3316 +#define kTile3317 3317 +#define kTile3318 3318 +#define kTile3319 3319 +#define kTile3320 3320 +#define kTile3321 3321 +#define kTile3322 3322 +#define kTile3323 3323 +#define kTile3324 3324 +#define kTile3325 3325 +#define kTile3326 3326 +#define kTile3327 3327 +#define kTile3328 3328 +#define kTile3329 3329 +#define kTile3330 3330 +#define kTile3331 3331 +#define kTile3332 3332 +#define kTile3333 3333 +#define kTile3334 3334 +#define kTile3335 3335 +#define kTile3336 3336 +#define kTile3337 3337 +#define kTile3338 3338 +#define kTile3339 3339 +#define kTile3340 3340 +#define kTile3341 3341 +#define kTile3342 3342 +#define kTile3343 3343 +#define kTile3344 3344 +#define kTile3345 3345 +#define kTile3346 3346 +#define kTile3347 3347 +#define kTile3348 3348 +#define kTilePIELogo 3349 +#define kTile3350 3350 +#define kTile3351 3351 +#define kTile3352 3352 +#define kTile3353 3353 +#define kTile3354 3354 +#define kTile3355 3355 +#define kTile3356 3356 +#define kTile3357 3357 +#define kTile3358 3358 +#define kTile3359 3359 +#define kTile3360 3360 +#define kTile3361 3361 +#define kTile3362 3362 +#define kTile3363 3363 // sky +#define kTile3364 3364 // sky +#define kTile3365 3365 // sky +#define kTile3366 3366 // sky +#define kTile3367 3367 // sky +#define kTileBMGLogo 3368 +#define kTile3369 3369 +#define kTile3370 3370 +#define kTile3371 3371 +#define kTile3372 3372 +#define kTile3373 3373 +#define kTile3374 3374 +#define kTile3375 3375 +#define kTile3376 3376 +#define kTile3377 3377 +#define kTile3378 3378 +#define kTile3379 3379 +#define kTile3380 3380 +#define kTile3381 3381 +#define kTile3382 3382 +#define kTile3383 3383 +#define kTile3384 3384 +#define kTile3385 3385 +#define kTile3386 3386 +#define kTile3387 3387 +#define kTile3388 3388 +#define kTile3389 3389 +#define kTile3390 3390 +#define kTile3391 3391 +#define kTile3392 3392 +#define kTile3393 3393 +#define kTile3394 3394 +#define kTile3395 3395 +#define kTile3396 3396 +#define kTile3397 3397 +#define kTile3398 3398 +#define kTile3399 3399 +#define kTile3400 3400 +#define kTile3401 3401 +#define kTile3402 3402 +#define kTile3403 3403 +#define kTile3404 3404 +#define kTile3405 3405 +#define kTile3406 3406 +#define kTile3407 3407 +#define kTile3408 3408 +#define kTile3409 3409 +#define kTile3410 3410 +#define kTile3411 3411 +#define kTile3412 3412 +#define kTile3413 3413 +#define kTile3414 3414 +#define kTile3415 3415 +#define kTile3416 3416 +#define kTile3417 3417 +#define kTile3418 3418 +#define kTile3419 3419 +#define kTile3420 3420 +#define kTile3421 3421 +#define kTile3422 3422 +#define kTile3423 3423 +#define kTile3424 3424 +#define kTile3425 3425 +#define kTile3426 3426 +#define kTile3427 3427 +#define kTile3428 3428 +#define kTile3429 3429 +#define kTile3430 3430 +#define kTile3431 3431 +#define kTile3432 3432 +#define kTile3433 3433 +#define kTile3434 3434 +#define kTile3435 3435 +#define kTile3436 3436 +#define kSkullJaw 3437 +#define kTile3438 3438 +#define kTile3439 3439 +#define kTile3440 3440 +#define kTile3441 3441 +#define kPowerslaveLogo 3442 +#define kTile3443 3443 +#define kTile3444 3444 +#define kTile3445 3445 +#define kTile3446 3446 +#define kTile3447 3447 +#define kTile3448 3448 +#define kTile3449 3449 +#define kTile3450 3450 +#define kTile3451 3451 +#define kTile3452 3452 +#define kTile3453 3453 +#define kTile3454 3454 +#define kTile3455 3455 +#define kTile3456 3456 +#define kTile3457 3457 +#define kTile3458 3458 +#define kTile3459 3459 +#define kMenuNewGameTile 3460 +#define kMenuLoadGameTile 3461 +#define kTile3462 3462 +#define kTile3463 3463 +#define kTile3464 3464 +#define kMenuMusicTile 3465 +#define kMenuSoundFxTile 3466 +#define kTile3467 3467 +#define kMenuCursorTile 3468 +#define kMenuBlankTitleTile 3469 +#define kTile3470 3470 +#define kTile3471 3471 +#define kTile3472 3472 +#define kTile3473 3473 +#define kTile3474 3474 +#define kTile3475 3475 +#define kTile3476 3476 +#define kTile3477 3477 +#define kTile3478 3478 +#define kTile3479 3479 +#define kTile3480 3480 +#define kTile3481 3481 +#define kTile3482 3482 +#define kTile3483 3483 +#define kTile3484 3484 +#define kTile3485 3485 +#define kTile3486 3486 +#define kTile3487 3487 +#define kTile3488 3488 +#define kTile3489 3489 +#define kTile3490 3490 +#define kTile3491 3491 +#define kTile3492 3492 +#define kTile3493 3493 +#define kTile3494 3494 +#define kTile3495 3495 +#define kTile3496 3496 +#define kTile3497 3497 +#define kTile3498 3498 +#define kTile3499 3499 +#define kTile3500 3500 +#define kTile3501 3501 +#define kTile3502 3502 +#define kTile3503 3503 +#define kTile3504 3504 +#define kTile3505 3505 +#define kTile3506 3506 +#define kTile3507 3507 +#define kTile3508 3508 +#define kTile3509 3509 +#define kTile3510 3510 +#define kTile3511 3511 +#define kTile3512 3512 +#define kTile3513 3513 +#define kTile3514 3514 +#define kTile3515 3515 +#define kTile3516 3516 +#define kTile3517 3517 +#define kTile3518 3518 +#define kTile3519 3519 +#define kTile3520 3520 +#define kTile3521 3521 +#define kTile3522 3522 +#define kTile3523 3523 +#define kTile3524 3524 +#define kTile3525 3525 +#define kTile3526 3526 +#define kTile3527 3527 +#define kTile3528 3528 +#define kTile3529 3529 +#define kTile3530 3530 +#define kTile3531 3531 +#define kTile3532 3532 +#define kTile3533 3533 +#define kTile3534 3534 +#define kTile3535 3535 +#define kTile3536 3536 +#define kTile3537 3537 +#define kTile3538 3538 +#define kTile3539 3539 +#define kTile3540 3540 +#define kTile3541 3541 +#define kTile3542 3542 +#define kTile3543 3543 +#define kTile3544 3544 +#define kTile3545 3545 +#define kTile3546 3546 +#define kTile3547 3547 +#define kTile3548 3548 +#define kTile3549 3549 +#define kTile3550 3550 +#define kTile3551 3551 +#define kTile3552 3552 +#define kTile3553 3553 +#define kTile3554 3554 +#define kTile3555 3555 +#define kTile3556 3556 +#define kTile3557 3557 +#define kTile3558 3558 +#define kTile3559 3559 +#define kTile3560 3560 +#define kTile3561 3561 +#define kTile3562 3562 +#define kTile3563 3563 +#define kTile3564 3564 +#define kTile3565 3565 +#define kTile3566 3566 +#define kTile3567 3567 +#define kTile3568 3568 +#define kTile3569 3569 +#define kTile3570 3570 +#define kTile3571 3571 +#define kTile3572 3572 +#define kTile3573 3573 +#define kTile3574 3574 +#define kTile3575 3575 +#define kTile3576 3576 +#define kTile3577 3577 +#define kTile3578 3578 +#define kTile3579 3579 +#define kTile3580 3580 +#define kTile3581 3581 +#define kSkullHead 3582 +#define kTile3583 3583 +#define kTile3584 3584 +#define kTile3585 3585 +#define kTile3586 3586 +#define kTile3587 3587 +#define kTile3588 3588 +#define kTile3589 3589 +#define kTile3590 3590 +#define kTile3591 3591 +#define kExhumedLogo 3592 +#define kTile3593 3593 +#define kTile3594 3594 +#define kTile3595 3595 +#define kTile3596 3596 +#define kTile3597 3597 +#define kTile3598 3598 +#define kTile3599 3599 +#define kTile3600 3600 +#define kTile3601 3601 +#define kTile3602 3602 +#define kTile3603 3603 +#define kEnergy1 3604 +#define kEnergy2 3605 +#define kTile3606 3606 +#define kTile3607 3607 +#define kTile3608 3608 +#define kTile3609 3609 +#define kTile3610 3610 +#define kTile3611 3611 +#define kTile3612 3612 +#define kTile3613 3613 +#define kTile3614 3614 +#define kTile3615 3615 +#define kTile3616 3616 +#define kTile3617 3617 +#define kTile3618 3618 +#define kTile3619 3619 +#define kTile3620 3620 +#define kTile3621 3621 +#define kTile3622 3622 +#define kTileLoboLaptop 3623 +#define kTile3624 3624 +#define kTile3625 3625 +#define kTile3626 3626 +#define kTile3627 3627 +#define kTile3628 3628 +#define kTile3629 3629 +#define kTile3630 3630 +#define kTile3631 3631 +#define kTile3632 3632 +#define kTile3633 3633 +#define kTile3634 3634 +#define kTile3635 3635 +#define kTile3636 3636 +#define kTile3637 3637 +#define kTile3638 3638 +#define kTile3639 3639 +#define kTile3640 3640 +#define kTile3641 3641 +#define kTile3642 3642 +#define kTile3643 3643 +#define kTile3644 3644 +#define kTile3645 3645 +#define kTile3646 3646 +#define kTile3647 3647 +#define kTile3648 3648 +#define kTile3649 3649 +#define kTile3650 3650 +#define kTile3651 3651 +#define kTile3652 3652 +#define kTile3653 3653 +#define kTile3654 3654 +#define kTile3655 3655 +#define kTile3656 3656 +#define kTile3657 3657 +#define kTile3658 3658 +#define kTile3659 3659 +#define kTile3660 3660 +#define kTile3661 3661 +#define kTile3662 3662 +#define kTile3663 3663 +#define kTile3664 3664 +#define kTile3665 3665 +#define kTile3666 3666 +#define kTile3667 3667 +#define kTile3668 3668 +#define kTile3669 3669 +#define kTile3670 3670 +#define kTile3671 3671 +#define kTile3672 3672 +#define kTile3673 3673 +#define kTile3674 3674 +#define kTile3675 3675 +#define kTile3676 3676 +#define kTile3677 3677 +#define kTile3678 3678 +#define kTile3679 3679 +#define kTile3680 3680 +#define kTile3681 3681 +#define kTile3682 3682 +#define kTile3683 3683 +#define kTile3684 3684 +#define kTile3685 3685 +#define kTile3686 3686 +#define kTile3687 3687 +#define kTile3688 3688 +#define kTile3689 3689 +#define kTile3690 3690 +#define kTile3691 3691 +#define kTile3692 3692 +#define kTile3693 3693 +#define kTile3694 3694 +#define kTile3695 3695 +#define kTile3696 3696 +#define kTile3697 3697 +#define kTile3698 3698 +#define kTile3699 3699 +#define kTile3700 3700 +#define kTile3701 3701 +#define kTile3702 3702 +#define kTile3703 3703 +#define kTile3704 3704 +#define kTile3705 3705 +#define kTile3706 3706 +#define kTile3707 3707 +#define kTile3708 3708 +#define kTile3709 3709 +#define kTile3710 3710 +#define kTile3711 3711 +#define kTile3712 3712 +#define kTile3713 3713 +#define kTile3714 3714 +#define kTile3715 3715 +#define kTile3716 3716 +#define kTile3717 3717 +#define kTile3718 3718 +#define kTile3719 3719 +#define kTile3720 3720 +#define kTile3721 3721 +#define kTile3722 3722 +#define kTile3723 3723 +#define kTile3724 3724 +#define kTile3725 3725 +#define kTile3726 3726 +#define kTile3727 3727 +#define kTile3728 3728 +#define kTile3729 3729 +#define kTile3730 3730 +#define kTile3731 3731 +#define kTile3732 3732 +#define kTile3733 3733 +#define kTile3734 3734 +#define kTile3735 3735 +#define kTile3736 3736 +#define kTile3737 3737 +#define kTile3738 3738 +#define kTile3739 3739 +#define kTile3740 3740 +#define kTile3741 3741 +#define kTile3742 3742 +#define kTile3743 3743 +#define kTile3744 3744 +#define kTile3745 3745 +#define kTile3746 3746 +#define kTile3747 3747 +#define kTile3748 3748 +#define kTile3749 3749 +#define kTile3750 3750 +#define kTile3751 3751 +#define kTile3752 3752 +#define kTile3753 3753 +#define kTile3754 3754 +#define kTile3755 3755 +#define kTile3756 3756 +#define kTile3757 3757 +#define kTile3758 3758 +#define kTile3759 3759 +#define kTile3760 3760 +#define kTile3761 3761 +#define kTile3762 3762 +#define kTile3763 3763 +#define kTile3764 3764 +#define kTile3765 3765 +#define kTile3766 3766 +#define kTile3767 3767 +#define kTile3768 3768 +#define kTile3769 3769 +#define kTile3770 3770 +#define kTile3771 3771 +#define kTile3772 3772 +#define kTile3773 3773 +#define kTile3774 3774 +#define kTile3775 3775 +#define kTile3776 3776 +#define kTile3777 3777 +#define kTile3778 3778 +#define kTile3779 3779 +#define kTile3780 3780 +#define kTile3781 3781 +#define kTile3782 3782 +#define kTile3783 3783 +#define kTile3784 3784 +#define kTile3785 3785 +#define kTile3786 3786 +#define kTile3787 3787 +#define kTile3788 3788 +#define kTile3789 3789 +#define kTile3790 3790 +#define kTile3791 3791 +#define kTile3792 3792 +#define kTile3793 3793 +#define kTile3794 3794 +#define kTile3795 3795 +#define kTile3796 3796 +#define kTile3797 3797 +#define kTile3798 3798 +#define kTile3799 3799 +#define kTile3800 3800 +#define kTile3801 3801 +#define kTile3802 3802 +#define kTile3803 3803 +#define kTile3804 3804 +#define kTile3805 3805 +#define kTile3806 3806 +#define kTile3807 3807 +#define kTile3808 3808 +#define kTile3809 3809 +#define kTile3810 3810 +#define kTile3811 3811 +#define kTile3812 3812 +#define kTile3813 3813 +#define kTile3814 3814 +#define kTile3815 3815 +#define kTile3816 3816 +#define kTile3817 3817 +#define kTile3818 3818 +#define kTile3819 3819 +#define kTile3820 3820 +#define kTile3821 3821 +#define kTile3822 3822 +#define kTile3823 3823 +#define kTile3824 3824 +#define kTile3825 3825 +#define kTile3826 3826 +#define kTile3827 3827 +#define kTile3828 3828 +#define kTile3829 3829 +#define kTile3830 3830 +#define kTile3831 3831 +#define kTile3832 3832 +#define kTile3833 3833 +#define kTile3834 3834 +#define kTile3835 3835 +#define kTile3836 3836 +#define kTile3837 3837 +#define kTile3838 3838 +#define kTile3839 3839 +#define kTile3840 3840 +#define kTile3841 3841 +#define kTile3842 3842 +#define kTile3843 3843 +#define kTile3844 3844 +#define kTile3845 3845 +#define kTile3846 3846 +#define kTile3847 3847 +#define kTile3848 3848 +#define kTile3849 3849 +#define kTile3850 3850 +#define kTile3851 3851 +#define kTile3852 3852 +#define kTile3853 3853 +#define kTile3854 3854 +#define kTile3855 3855 +#define kTile3856 3856 +#define kTile3857 3857 +#define kTile3858 3858 +#define kTile3859 3859 +#define kTile3860 3860 +#define kTile3861 3861 +#define kTile3862 3862 +#define kTile3863 3863 +#define kTile3864 3864 +#define kTile3865 3865 +#define kTile3866 3866 +#define kTile3867 3867 +#define kTile3868 3868 +#define kTile3869 3869 +#define kTile3870 3870 +#define kTile3871 3871 +#define kTile3872 3872 +#define kTile3873 3873 +#define kTile3874 3874 +#define kTile3875 3875 +#define kTile3876 3876 +#define kTile3877 3877 +#define kTile3878 3878 +#define kTile3879 3879 +#define kTile3880 3880 +#define kTile3881 3881 +#define kTile3882 3882 +#define kTile3883 3883 +#define kTile3884 3884 +#define kTile3885 3885 +#define kTile3886 3886 +#define kTile3887 3887 +#define kTile3888 3888 +#define kTile3889 3889 +#define kTile3890 3890 +#define kTile3891 3891 +#define kTile3892 3892 +#define kTile3893 3893 +#define kTile3894 3894 +#define kTile3895 3895 +#define kTile3896 3896 +#define kTile3897 3897 +#define kTile3898 3898 +#define kTile3899 3899 +#define kTile3900 3900 +#define kTile3901 3901 +#define kTile3902 3902 +#define kTile3903 3903 +#define kTile3904 3904 +#define kTile3905 3905 +#define kTile3906 3906 +#define kTile3907 3907 +#define kTile3908 3908 +#define kTile3909 3909 +#define kTile3910 3910 +#define kTile3911 3911 +#define kTile3912 3912 +#define kTile3913 3913 +#define kTile3914 3914 +#define kTile3915 3915 +#define kTile3916 3916 +#define kTile3917 3917 +#define kTile3918 3918 +#define kTile3919 3919 +#define kTile3920 3920 +#define kTile3921 3921 +#define kTile3922 3922 +#define kTile3923 3923 +#define kTile3924 3924 +#define kTile3925 3925 +#define kTile3926 3926 +#define kTile3927 3927 +#define kTile3928 3928 +#define kTile3929 3929 +#define kTile3930 3930 +#define kTile3931 3931 +#define kTile3932 3932 +#define kTile3933 3933 +#define kTile3934 3934 +#define kTile3935 3935 +#define kTile3936 3936 +#define kTile3937 3937 +#define kTile3938 3938 +#define kTile3939 3939 +#define kTile3940 3940 +#define kTile3941 3941 +#define kTile3942 3942 +#define kTile3943 3943 +#define kTile3944 3944 +#define kTile3945 3945 +#define kTile3946 3946 +#define kTile3947 3947 +#define kTile3948 3948 +#define kTile3949 3949 +#define kTile3950 3950 +#define kTile3951 3951 +#define kTile3952 3952 +#define kTile3953 3953 +#define kTile3954 3954 +#define kTile3955 3955 +#define kTile3956 3956 +#define kTile3957 3957 +#define kTile3958 3958 +#define kTile3959 3959 +#define kTile3960 3960 +#define kTile3961 3961 +#define kTile3962 3962 +#define kTile3963 3963 +#define kTile3964 3964 +#define kTile3965 3965 +#define kTile3966 3966 +#define kTile3967 3967 +#define kTile3968 3968 +#define kTile3969 3969 +#define kTile3970 3970 +#define kTile3971 3971 +#define kTile3972 3972 +#define kTile3973 3973 +#define kTile3974 3974 +#define kTile3975 3975 +#define kTile3976 3976 +#define kTile3977 3977 +#define kTile3978 3978 +#define kTile3979 3979 +#define kTile3980 3980 +#define kTile3981 3981 +#define kTile3982 3982 +#define kTile3983 3983 +#define kTile3984 3984 +#define kTile3985 3985 +#define kTile3986 3986 +#define kTile3987 3987 +#define kTile3988 3988 +#define kTile3989 3989 +#define kTile3990 3990 +#define kTile3991 3991 +#define kTile3992 3992 +#define kTile3993 3993 +#define kTile3994 3994 +#define kTile3995 3995 +#define kTile3996 3996 +#define kTile3997 3997 +#define kTile3998 3998 +#define kTile3999 3999 +#define kTile4000 4000 +#define kTile4001 4001 +#define kTile4002 4002 +#define kTile4003 4003 +#define kTile4004 4004 +#define kTile4005 4005 +#define kTile4006 4006 +#define kTile4007 4007 +#define kTile4008 4008 +#define kTile4009 4009 +#define kTile4010 4010 +#define kTile4011 4011 +#define kTile4012 4012 +#define kTile4013 4013 +#define kTile4014 4014 +#define kTile4015 4015 +#define kTile4016 4016 +#define kTile4017 4017 +#define kTile4018 4018 +#define kTile4019 4019 +#define kTile4020 4020 +#define kTile4021 4021 +#define kTile4022 4022 +#define kTile4023 4023 +#define kTile4024 4024 +#define kTile4025 4025 +#define kTile4026 4026 +#define kTile4027 4027 +#define kTile4028 4028 +#define kTile4029 4029 +#define kTile4030 4030 +#define kTile4031 4031 +#define kTile4032 4032 +#define kTile4033 4033 +#define kTile4034 4034 +#define kTile4035 4035 +#define kTile4036 4036 +#define kTile4037 4037 +#define kTile4038 4038 +#define kTile4039 4039 +#define kTile4040 4040 +#define kTile4041 4041 +#define kTile4042 4042 +#define kTile4043 4043 +#define kTile4044 4044 +#define kTile4045 4045 +#define kTile4046 4046 +#define kTile4047 4047 +#define kTile4048 4048 +#define kTile4049 4049 +#define kTile4050 4050 +#define kTile4051 4051 +#define kTile4052 4052 +#define kTile4053 4053 +#define kTile4054 4054 +#define kTile4055 4055 +#define kTile4056 4056 +#define kTile4057 4057 +#define kTile4058 4058 +#define kTile4059 4059 +#define kTile4060 4060 +#define kTile4061 4061 +#define kTile4062 4062 +#define kTile4063 4063 +#define kTile4064 4064 +#define kTile4065 4065 +#define kTile4066 4066 +#define kTile4067 4067 +#define kTile4068 4068 +#define kTile4069 4069 +#define kTile4070 4070 +#define kTile4071 4071 +#define kTile4072 4072 +#define kTile4073 4073 +#define kTile4074 4074 +#define kTile4075 4075 +#define kTile4076 4076 +#define kTile4077 4077 +#define kTile4078 4078 +#define kTile4079 4079 +#define kTile4080 4080 +#define kTile4081 4081 +#define kTile4082 4082 +#define kTile4083 4083 +#define kTile4084 4084 +#define kTile4085 4085 +#define kTile4086 4086 +#define kTile4087 4087 +#define kTile4088 4088 +#define kTile4089 4089 +#define kTile4090 4090 +#define kTile4091 4091 +#define kTile4092 4092 +#define kTile4093 4093 +#define kTile4094 4094 +#define kTile4095 4095 +#define kTile4096 4096 +#define kTile4097 4097 +#define kTile4098 4098 +#define kTile4099 4099 +#define kTile4100 4100 +#define kTile4101 4101 +#define kTile4102 4102 +#define kTile4103 4103 +#define kTile4104 4104 +#define kTile4105 4105 +#define kTile4106 4106 +#define kTile4107 4107 +#define kTile4108 4108 +#define kTile4109 4109 +#define kTile4110 4110 +#define kTile4111 4111 +#define kTile4112 4112 +#define kTile4113 4113 +#define kTile4114 4114 +#define kTile4115 4115 +#define kTile4116 4116 +#define kTile4117 4117 +#define kTile4118 4118 +#define kTile4119 4119 +#define kTile4120 4120 +#define kTile4121 4121 +#define kTile4122 4122 +#define kTile4123 4123 +#define kTile4124 4124 +#define kTile4125 4125 +#define kTile4126 4126 +#define kTile4127 4127 +#define kTile4128 4128 +#define kTile4129 4129 +#define kTile4130 4130 +#define kTile4131 4131 +#define kTile4132 4132 +#define kTile4133 4133 +#define kTile4134 4134 +#define kTile4135 4135 +#define kTile4136 4136 +#define kTile4137 4137 +#define kTile4138 4138 +#define kTile4139 4139 +#define kTile4140 4140 +#define kTile4141 4141 +#define kTile4142 4142 +#define kTile4143 4143 +#define kTile4144 4144 +#define kTile4145 4145 +#define kTile4146 4146 +#define kTile4147 4147 +#define kTile4148 4148 +#define kTile4149 4149 +#define kTile4150 4150 +#define kTile4151 4151 +#define kTile4152 4152 +#define kTile4153 4153 +#define kTile4154 4154 +#define kTile4155 4155 +#define kTile4156 4156 +#define kTile4157 4157 +#define kTile4158 4158 +#define kTile4159 4159 +#define kTile4160 4160 +#define kTile4161 4161 +#define kTile4162 4162 +#define kTile4163 4163 +#define kTile4164 4164 +#define kTile4165 4165 +#define kTile4166 4166 +#define kTile4167 4167 +#define kTile4168 4168 +#define kTile4169 4169 +#define kTile4170 4170 +#define kTile4171 4171 +#define kTile4172 4172 +#define kTile4173 4173 +#define kTile4174 4174 +#define kTile4175 4175 +#define kTile4176 4176 +#define kTile4177 4177 +#define kTile4178 4178 +#define kTile4179 4179 +#define kTile4180 4180 +#define kTile4181 4181 +#define kTile4182 4182 +#define kTile4183 4183 +#define kTile4184 4184 +#define kTile4185 4185 +#define kTile4186 4186 +#define kTile4187 4187 +#define kTile4188 4188 +#define kTile4189 4189 +#define kTile4190 4190 +#define kTile4191 4191 +#define kTile4192 4192 +#define kTile4193 4193 +#define kTile4194 4194 +#define kTile4195 4195 +#define kTile4196 4196 +#define kTile4197 4197 +#define kTile4198 4198 +#define kTile4199 4199 +#define kTile4200 4200 +#define kTile4201 4201 +#define kTile4202 4202 +#define kTile4203 4203 +#define kTile4204 4204 +#define kTile4205 4205 +#define kTile4206 4206 +#define kTile4207 4207 +#define kTile4208 4208 +#define kTile4209 4209 +#define kTile4210 4210 +#define kTile4211 4211 +#define kTile4212 4212 +#define kTile4213 4213 +#define kTile4214 4214 +#define kTile4215 4215 +#define kTile4216 4216 +#define kTile4217 4217 +#define kTile4218 4218 +#define kTile4219 4219 +#define kTile4220 4220 +#define kTile4221 4221 +#define kTile4222 4222 +#define kTile4223 4223 +#define kTile4224 4224 +#define kTile4225 4225 +#define kTile4226 4226 +#define kTile4227 4227 +#define kTile4228 4228 +#define kTile4229 4229 +#define kTile4230 4230 +#define kTile4231 4231 +#define kTile4232 4232 +#define kTile4233 4233 +#define kTile4234 4234 +#define kTile4235 4235 +#define kTile4236 4236 +#define kTile4237 4237 +#define kTile4238 4238 +#define kTile4239 4239 +#define kTile4240 4240 +#define kTile4241 4241 +#define kTile4242 4242 +#define kTile4243 4243 +#define kTile4244 4244 +#define kTile4245 4245 +#define kTile4246 4246 +#define kTile4247 4247 +#define kTile4248 4248 +#define kTile4249 4249 +#define kTile4250 4250 +#define kTile4251 4251 +#define kTile4252 4252 +#define kTile4253 4253 +#define kTile4254 4254 +#define kTile4255 4255 +#define kTile4256 4256 +#define kTile4257 4257 +#define kTile4258 4258 +#define kTile4259 4259 +#define kTile4260 4260 +#define kTile4261 4261 +#define kTile4262 4262 +#define kTile4263 4263 +#define kTile4264 4264 +#define kTile4265 4265 +#define kTile4266 4266 +#define kTile4267 4267 +#define kTile4268 4268 +#define kTile4269 4269 +#define kTile4270 4270 +#define kTile4271 4271 +#define kTile4272 4272 +#define kTile4273 4273 +#define kTile4274 4274 +#define kTile4275 4275 +#define kTile4276 4276 +#define kTile4277 4277 +#define kTile4278 4278 +#define kTile4279 4279 +#define kTile4280 4280 +#define kTile4281 4281 +#define kTile4282 4282 +#define kTile4283 4283 +#define kTile4284 4284 +#define kTile4285 4285 +#define kTile4286 4286 +#define kTile4287 4287 +#define kTile4288 4288 +#define kTile4289 4289 +#define kTile4290 4290 +#define kTile4291 4291 +#define kTile4292 4292 +#define kTile4293 4293 +#define kTile4294 4294 +#define kTile4295 4295 +#define kTile4296 4296 +#define kTile4297 4297 +#define kTile4298 4298 +#define kTile4299 4299 +#define kTile4300 4300 +#define kTile4301 4301 +#define kTile4302 4302 +#define kTile4303 4303 +#define kTile4304 4304 +#define kTile4305 4305 +#define kTile4306 4306 +#define kTile4307 4307 +#define kTile4308 4308 +#define kTile4309 4309 +#define kTile4310 4310 +#define kTile4311 4311 +#define kTile4312 4312 +#define kTile4313 4313 +#define kTile4314 4314 +#define kTile4315 4315 +#define kTile4316 4316 +#define kTile4317 4317 +#define kTile4318 4318 +#define kTile4319 4319 +#define kTile4320 4320 +#define kTile4321 4321 +#define kTile4322 4322 +#define kTile4323 4323 +#define kTile4324 4324 +#define kTile4325 4325 +#define kTile4326 4326 +#define kTile4327 4327 +#define kTile4328 4328 +#define kTile4329 4329 +#define kTile4330 4330 +#define kTile4331 4331 +#define kTile4332 4332 +#define kTile4333 4333 +#define kTile4334 4334 +#define kTile4335 4335 +#define kTile4336 4336 +#define kTile4337 4337 +#define kTile4338 4338 +#define kTile4339 4339 +#define kTile4340 4340 +#define kTile4341 4341 +#define kTile4342 4342 +#define kTile4343 4343 +#define kTile4344 4344 +#define kTile4345 4345 +#define kTile4346 4346 +#define kTile4347 4347 +#define kTile4348 4348 +#define kTile4349 4349 +#define kTile4350 4350 +#define kTile4351 4351 +#define kTile4352 4352 +#define kTile4353 4353 +#define kTile4354 4354 +#define kTile4355 4355 +#define kTile4356 4356 +#define kTile4357 4357 +#define kTile4358 4358 +#define kTile4359 4359 +#define kTile4360 4360 +#define kTile4361 4361 +#define kTile4362 4362 +#define kTile4363 4363 +#define kTile4364 4364 +#define kTile4365 4365 +#define kTile4366 4366 +#define kTile4367 4367 +#define kTile4368 4368 +#define kTile4369 4369 +#define kTile4370 4370 +#define kTile4371 4371 +#define kTile4372 4372 +#define kTile4373 4373 +#define kTile4374 4374 +#define kTile4375 4375 +#define kTile4376 4376 +#define kTile4377 4377 +#define kTile4378 4378 +#define kTile4379 4379 +#define kTile4380 4380 +#define kTile4381 4381 +#define kTile4382 4382 +#define kTile4383 4383 +#define kTile4384 4384 +#define kTile4385 4385 +#define kTile4386 4386 +#define kTile4387 4387 +#define kTile4388 4388 +#define kTile4389 4389 +#define kTile4390 4390 +#define kTile4391 4391 +#define kTile4392 4392 +#define kTile4393 4393 +#define kTile4394 4394 +#define kTile4395 4395 +#define kTile4396 4396 +#define kTile4397 4397 +#define kTile4398 4398 +#define kTile4399 4399 +#define kTile4400 4400 +#define kTile4401 4401 +#define kTile4402 4402 +#define kTile4403 4403 +#define kTile4404 4404 +#define kTile4405 4405 +#define kTile4406 4406 +#define kTile4407 4407 +#define kTile4408 4408 +#define kTile4409 4409 +#define kTile4410 4410 +#define kTile4411 4411 +#define kTile4412 4412 +#define kTile4413 4413 +#define kTile4414 4414 +#define kTile4415 4415 +#define kTile4416 4416 +#define kTile4417 4417 +#define kTile4418 4418 +#define kTile4419 4419 +#define kTile4420 4420 +#define kTile4421 4421 +#define kTile4422 4422 +#define kTile4423 4423 +#define kTile4424 4424 +#define kTile4425 4425 +#define kTile4426 4426 +#define kTile4427 4427 +#define kTile4428 4428 +#define kTile4429 4429 +#define kTile4430 4430 +#define kTile4431 4431 +#define kTile4432 4432 +#define kTile4433 4433 +#define kTile4434 4434 +#define kTile4435 4435 +#define kTile4436 4436 +#define kTile4437 4437 +#define kTile4438 4438 +#define kTile4439 4439 +#define kTile4440 4440 +#define kTile4441 4441 +#define kTile4442 4442 +#define kTile4443 4443 +#define kTile4444 4444 +#define kTile4445 4445 +#define kTile4446 4446 +#define kTile4447 4447 +#define kTile4448 4448 +#define kTile4449 4449 +#define kTile4450 4450 +#define kTile4451 4451 +#define kTile4452 4452 +#define kTile4453 4453 +#define kTile4454 4454 +#define kTile4455 4455 +#define kTile4456 4456 +#define kTile4457 4457 +#define kTile4458 4458 +#define kTile4459 4459 +#define kTile4460 4460 +#define kTile4461 4461 +#define kTile4462 4462 +#define kTile4463 4463 +#define kTile4464 4464 +#define kTile4465 4465 +#define kTile4466 4466 +#define kTile4467 4467 +#define kTile4468 4468 +#define kTile4469 4469 +#define kTile4470 4470 +#define kTile4471 4471 +#define kTile4472 4472 +#define kTile4473 4473 +#define kTile4474 4474 +#define kTile4475 4475 +#define kTile4476 4476 +#define kTile4477 4477 +#define kTile4478 4478 +#define kTile4479 4479 +#define kTile4480 4480 +#define kTile4481 4481 +#define kTile4482 4482 +#define kTile4483 4483 +#define kTile4484 4484 +#define kTile4485 4485 +#define kTile4486 4486 +#define kTile4487 4487 +#define kTile4488 4488 +#define kTile4489 4489 +#define kTile4490 4490 +#define kTile4491 4491 +#define kTile4492 4492 +#define kTile4493 4493 +#define kTile4494 4494 +#define kTile4495 4495 +#define kTile4496 4496 +#define kTile4497 4497 +#define kTile4498 4498 +#define kTile4499 4499 +#define kTile4500 4500 +#define kTile4501 4501 +#define kTile4502 4502 +#define kTile4503 4503 +#define kTile4504 4504 +#define kTile4505 4505 +#define kTile4506 4506 +#define kTile4507 4507 +#define kTile4508 4508 +#define kTile4509 4509 +#define kTile4510 4510 +#define kTile4511 4511 +#define kTile4512 4512 +#define kTile4513 4513 +#define kTile4514 4514 +#define kTile4515 4515 +#define kTile4516 4516 +#define kTile4517 4517 +#define kTile4518 4518 +#define kTile4519 4519 +#define kTile4520 4520 +#define kTile4521 4521 +#define kTile4522 4522 +#define kTile4523 4523 +#define kTile4524 4524 +#define kTile4525 4525 +#define kTile4526 4526 +#define kTile4527 4527 +#define kTile4528 4528 +#define kTile4529 4529 +#define kTile4530 4530 +#define kTile4531 4531 +#define kTile4532 4532 +#define kTile4533 4533 +#define kTile4534 4534 +#define kTile4535 4535 +#define kTile4536 4536 +#define kTile4537 4537 +#define kTile4538 4538 +#define kTile4539 4539 +#define kTile4540 4540 +#define kTile4541 4541 +#define kTile4542 4542 +#define kTile4543 4543 +#define kTile4544 4544 +#define kTile4545 4545 +#define kTile4546 4546 +#define kTile4547 4547 +#define kTile4548 4548 +#define kTile4549 4549 +#define kTile4550 4550 +#define kTile4551 4551 +#define kTile4552 4552 +#define kTile4553 4553 +#define kTile4554 4554 +#define kTile4555 4555 +#define kTile4556 4556 +#define kTile4557 4557 +#define kTile4558 4558 +#define kTile4559 4559 +#define kTile4560 4560 +#define kTile4561 4561 +#define kTile4562 4562 +#define kTile4563 4563 +#define kTile4564 4564 +#define kTile4565 4565 +#define kTile4566 4566 +#define kTile4567 4567 +#define kTile4568 4568 +#define kTile4569 4569 +#define kTile4570 4570 +#define kTile4571 4571 +#define kTile4572 4572 +#define kTile4573 4573 +#define kTile4574 4574 +#define kTile4575 4575 +#define kTile4576 4576 +#define kTile4577 4577 +#define kTile4578 4578 +#define kTile4579 4579 +#define kTile4580 4580 +#define kTile4581 4581 +#define kTile4582 4582 +#define kTile4583 4583 +#define kTile4584 4584 +#define kTile4585 4585 +#define kTile4586 4586 +#define kTile4587 4587 +#define kTile4588 4588 +#define kTile4589 4589 +#define kTile4590 4590 +#define kTile4591 4591 +#define kTile4592 4592 +#define kTile4593 4593 +#define kTile4594 4594 +#define kTile4595 4595 +#define kTile4596 4596 +#define kTile4597 4597 +#define kTile4598 4598 +#define kTile4599 4599 +#define kTile4600 4600 +#define kTile4601 4601 +#define kTile4602 4602 +#define kTile4603 4603 +#define kTile4604 4604 +#define kTile4605 4605 +#define kTile4606 4606 +#define kTile4607 4607 +#define kTile4608 4608 +#define kTile4609 4609 +#define kTile4610 4610 +#define kTile4611 4611 +#define kTile4612 4612 +#define kTile4613 4613 +#define kTile4614 4614 +#define kTile4615 4615 +#define kTile4616 4616 +#define kTile4617 4617 +#define kTile4618 4618 +#define kTile4619 4619 +#define kTile4620 4620 +#define kTile4621 4621 +#define kTile4622 4622 +#define kTile4623 4623 +#define kTile4624 4624 +#define kTile4625 4625 +#define kTile4626 4626 +#define kTile4627 4627 +#define kTile4628 4628 +#define kTile4629 4629 +#define kTile4630 4630 +#define kTile4631 4631 +#define kTile4632 4632 +#define kTile4633 4633 +#define kTile4634 4634 +#define kTile4635 4635 +#define kTile4636 4636 +#define kTile4637 4637 +#define kTile4638 4638 +#define kTile4639 4639 +#define kTile4640 4640 +#define kTile4641 4641 +#define kTile4642 4642 +#define kTile4643 4643 +#define kTile4644 4644 +#define kTile4645 4645 +#define kTile4646 4646 +#define kTile4647 4647 +#define kTile4648 4648 +#define kTile4649 4649 +#define kTile4650 4650 +#define kTile4651 4651 +#define kTile4652 4652 +#define kTile4653 4653 +#define kTile4654 4654 +#define kTile4655 4655 +#define kTile4656 4656 +#define kTile4657 4657 +#define kTile4658 4658 +#define kTile4659 4659 +#define kTile4660 4660 +#define kTile4661 4661 +#define kTile4662 4662 +#define kTile4663 4663 +#define kTile4664 4664 +#define kTile4665 4665 +#define kTile4666 4666 +#define kTile4667 4667 +#define kTile4668 4668 +#define kTile4669 4669 +#define kTile4670 4670 +#define kTile4671 4671 +#define kTile4672 4672 +#define kTile4673 4673 +#define kTile4674 4674 +#define kTile4675 4675 +#define kTile4676 4676 +#define kTile4677 4677 +#define kTile4678 4678 +#define kTile4679 4679 +#define kTile4680 4680 +#define kTile4681 4681 +#define kTile4682 4682 +#define kTile4683 4683 +#define kTile4684 4684 +#define kTile4685 4685 +#define kTile4686 4686 +#define kTile4687 4687 +#define kTile4688 4688 +#define kTile4689 4689 +#define kTile4690 4690 +#define kTile4691 4691 +#define kTile4692 4692 +#define kTile4693 4693 +#define kTile4694 4694 +#define kTile4695 4695 +#define kTile4696 4696 +#define kTile4697 4697 +#define kTile4698 4698 +#define kTile4699 4699 +#define kTile4700 4700 +#define kTile4701 4701 +#define kTile4702 4702 +#define kTile4703 4703 +#define kTile4704 4704 +#define kTile4705 4705 +#define kTile4706 4706 +#define kTile4707 4707 +#define kTile4708 4708 +#define kTile4709 4709 +#define kTile4710 4710 +#define kTile4711 4711 +#define kTile4712 4712 +#define kTile4713 4713 +#define kTile4714 4714 +#define kTile4715 4715 +#define kTile4716 4716 +#define kTile4717 4717 +#define kTile4718 4718 +#define kTile4719 4719 +#define kTile4720 4720 +#define kTile4721 4721 +#define kTile4722 4722 +#define kTile4723 4723 +#define kTile4724 4724 +#define kTile4725 4725 +#define kTile4726 4726 +#define kTile4727 4727 +#define kTile4728 4728 +#define kTile4729 4729 +#define kTile4730 4730 +#define kTile4731 4731 +#define kTile4732 4732 +#define kTile4733 4733 +#define kTile4734 4734 +#define kTile4735 4735 +#define kTile4736 4736 +#define kTile4737 4737 +#define kTile4738 4738 +#define kTile4739 4739 +#define kTile4740 4740 +#define kTile4741 4741 +#define kTile4742 4742 +#define kTile4743 4743 +#define kTile4744 4744 +#define kTile4745 4745 +#define kTile4746 4746 +#define kTile4747 4747 +#define kTile4748 4748 +#define kTile4749 4749 +#define kTile4750 4750 +#define kTile4751 4751 +#define kTile4752 4752 +#define kTile4753 4753 +#define kTile4754 4754 +#define kTile4755 4755 +#define kTile4756 4756 +#define kTile4757 4757 +#define kTile4758 4758 +#define kTile4759 4759 +#define kTile4760 4760 +#define kTile4761 4761 +#define kTile4762 4762 +#define kTile4763 4763 +#define kTile4764 4764 +#define kTile4765 4765 +#define kTile4766 4766 +#define kTile4767 4767 +#define kTile4768 4768 +#define kTile4769 4769 +#define kTile4770 4770 +#define kTile4771 4771 +#define kTile4772 4772 +#define kTile4773 4773 +#define kTile4774 4774 +#define kTile4775 4775 +#define kTile4776 4776 +#define kTile4777 4777 +#define kTile4778 4778 +#define kTile4779 4779 +#define kTile4780 4780 +#define kTile4781 4781 +#define kTile4782 4782 +#define kTile4783 4783 +#define kTile4784 4784 +#define kTile4785 4785 +#define kTile4786 4786 +#define kTile4787 4787 +#define kTile4788 4788 +#define kTile4789 4789 +#define kTile4790 4790 +#define kTile4791 4791 +#define kTile4792 4792 +#define kTile4793 4793 +#define kTile4794 4794 +#define kTile4795 4795 +#define kTile4796 4796 +#define kTile4797 4797 +#define kTile4798 4798 +#define kTile4799 4799 +#define kTile4800 4800 +#define kTile4801 4801 +#define kTile4802 4802 +#define kTile4803 4803 +#define kTile4804 4804 +#define kTile4805 4805 +#define kTile4806 4806 +#define kTile4807 4807 +#define kTile4808 4808 +#define kTile4809 4809 +#define kTile4810 4810 +#define kTile4811 4811 +#define kTile4812 4812 +#define kTile4813 4813 +#define kTile4814 4814 +#define kTile4815 4815 +#define kTile4816 4816 +#define kTile4817 4817 +#define kTile4818 4818 +#define kTile4819 4819 +#define kTile4820 4820 +#define kTile4821 4821 +#define kTile4822 4822 +#define kTile4823 4823 +#define kTile4824 4824 +#define kTile4825 4825 +#define kTile4826 4826 +#define kTile4827 4827 +#define kTile4828 4828 +#define kTile4829 4829 +#define kTile4830 4830 +#define kTile4831 4831 +#define kTile4832 4832 +#define kTile4833 4833 +#define kTile4834 4834 +#define kTile4835 4835 +#define kTile4836 4836 +#define kTile4837 4837 +#define kTile4838 4838 +#define kTile4839 4839 +#define kTile4840 4840 +#define kTile4841 4841 +#define kTile4842 4842 +#define kTile4843 4843 +#define kTile4844 4844 +#define kTile4845 4845 +#define kTile4846 4846 +#define kTile4847 4847 +#define kTile4848 4848 +#define kTile4849 4849 +#define kTile4850 4850 +#define kTile4851 4851 +#define kTile4852 4852 +#define kTile4853 4853 +#define kTile4854 4854 +#define kTile4855 4855 +#define kTile4856 4856 +#define kTile4857 4857 +#define kTile4858 4858 +#define kTile4859 4859 +#define kTile4860 4860 +#define kTile4861 4861 +#define kTile4862 4862 +#define kTile4863 4863 +#define kTile4864 4864 +#define kTile4865 4865 +#define kTile4866 4866 +#define kTile4867 4867 +#define kTile4868 4868 +#define kTile4869 4869 +#define kTile4870 4870 +#define kTile4871 4871 +#define kTile4872 4872 +#define kTile4873 4873 +#define kTile4874 4874 +#define kTile4875 4875 +#define kTile4876 4876 +#define kTile4877 4877 +#define kTile4878 4878 +#define kTile4879 4879 +#define kTile4880 4880 +#define kTile4881 4881 +#define kTile4882 4882 +#define kTile4883 4883 +#define kTile4884 4884 +#define kTile4885 4885 +#define kTile4886 4886 +#define kTile4887 4887 +#define kTile4888 4888 +#define kTile4889 4889 +#define kTile4890 4890 +#define kTile4891 4891 +#define kTile4892 4892 +#define kTile4893 4893 +#define kTile4894 4894 +#define kTile4895 4895 +#define kTile4896 4896 +#define kTile4897 4897 +#define kTile4898 4898 +#define kTile4899 4899 +#define kTile4900 4900 +#define kTile4901 4901 +#define kTile4902 4902 +#define kTile4903 4903 +#define kTile4904 4904 +#define kTile4905 4905 +#define kTile4906 4906 +#define kTile4907 4907 +#define kTile4908 4908 +#define kTile4909 4909 +#define kTile4910 4910 +#define kTile4911 4911 +#define kTile4912 4912 +#define kTile4913 4913 +#define kTile4914 4914 +#define kTile4915 4915 +#define kTile4916 4916 +#define kTile4917 4917 +#define kTile4918 4918 +#define kTile4919 4919 +#define kTile4920 4920 +#define kTile4921 4921 +#define kTile4922 4922 +#define kTile4923 4923 +#define kTile4924 4924 +#define kTile4925 4925 +#define kTile4926 4926 +#define kTile4927 4927 +#define kTile4928 4928 +#define kTile4929 4929 +#define kTile4930 4930 +#define kTile4931 4931 +#define kTile4932 4932 +#define kTile4933 4933 +#define kTile4934 4934 +#define kTile4935 4935 +#define kTile4936 4936 +#define kTile4937 4937 +#define kTile4938 4938 +#define kTile4939 4939 +#define kTile4940 4940 +#define kTile4941 4941 +#define kTile4942 4942 +#define kTile4943 4943 +#define kTile4944 4944 +#define kTile4945 4945 +#define kTile4946 4946 +#define kTile4947 4947 +#define kTile4948 4948 +#define kTile4949 4949 +#define kTile4950 4950 +#define kTile4951 4951 +#define kTile4952 4952 +#define kTile4953 4953 +#define kTile4954 4954 +#define kTile4955 4955 +#define kTile4956 4956 +#define kTile4957 4957 +#define kTile4958 4958 +#define kTile4959 4959 +#define kTile4960 4960 +#define kTile4961 4961 +#define kTile4962 4962 +#define kTile4963 4963 +#define kTile4964 4964 +#define kTile4965 4965 +#define kTile4966 4966 +#define kTile4967 4967 +#define kTile4968 4968 +#define kTile4969 4969 +#define kTile4970 4970 +#define kTile4971 4971 +#define kTile4972 4972 +#define kTile4973 4973 +#define kTile4974 4974 +#define kTile4975 4975 +#define kTile4976 4976 +#define kTile4977 4977 +#define kTile4978 4978 +#define kTile4979 4979 +#define kTile4980 4980 +#define kTile4981 4981 +#define kTile4982 4982 +#define kTile4983 4983 +#define kTile4984 4984 +#define kTile4985 4985 +#define kTile4986 4986 +#define kTile4987 4987 +#define kTile4988 4988 +#define kTile4989 4989 +#define kTile4990 4990 +#define kTile4991 4991 +#define kTile4992 4992 +#define kTile4993 4993 +#define kTile4994 4994 +#define kTile4995 4995 +#define kTile4996 4996 +#define kTile4997 4997 +#define kTile4998 4998 +#define kTile4999 4999 +#define kTile5000 5000 +#define kTile5001 5001 +#define kTile5002 5002 +#define kTile5003 5003 +#define kTile5004 5004 +#define kTile5005 5005 +#define kTile5006 5006 +#define kTile5007 5007 +#define kTile5008 5008 +#define kTile5009 5009 +#define kTile5010 5010 +#define kTile5011 5011 +#define kTile5012 5012 +#define kTile5013 5013 +#define kTile5014 5014 +#define kTile5015 5015 +#define kTile5016 5016 +#define kTile5017 5017 +#define kTile5018 5018 +#define kTile5019 5019 +#define kTile5020 5020 +#define kTile5021 5021 +#define kTile5022 5022 +#define kTile5023 5023 +#define kTile5024 5024 +#define kTile5025 5025 +#define kTile5026 5026 +#define kTile5027 5027 +#define kTile5028 5028 +#define kTile5029 5029 +#define kTile5030 5030 +#define kTile5031 5031 +#define kTile5032 5032 +#define kTile5033 5033 +#define kTile5034 5034 +#define kTile5035 5035 +#define kTile5036 5036 +#define kTile5037 5037 +#define kTile5038 5038 +#define kTile5039 5039 +#define kTile5040 5040 +#define kTile5041 5041 +#define kTile5042 5042 +#define kTile5043 5043 +#define kTile5044 5044 +#define kTile5045 5045 +#define kTile5046 5046 +#define kTile5047 5047 +#define kTile5048 5048 +#define kTile5049 5049 +#define kTile5050 5050 +#define kTile5051 5051 +#define kTile5052 5052 +#define kTile5053 5053 +#define kTile5054 5054 +#define kTile5055 5055 +#define kTile5056 5056 +#define kTile5057 5057 +#define kTile5058 5058 +#define kTile5059 5059 +#define kTile5060 5060 +#define kTile5061 5061 +#define kTile5062 5062 +#define kTile5063 5063 +#define kTile5064 5064 +#define kTile5065 5065 +#define kTile5066 5066 +#define kTile5067 5067 +#define kTile5068 5068 +#define kTile5069 5069 +#define kTile5070 5070 +#define kTile5071 5071 +#define kTile5072 5072 +#define kTile5073 5073 +#define kTile5074 5074 +#define kTile5075 5075 +#define kTile5076 5076 +#define kTile5077 5077 +#define kTile5078 5078 +#define kTile5079 5079 +#define kTile5080 5080 +#define kTile5081 5081 +#define kTile5082 5082 +#define kTile5083 5083 +#define kTile5084 5084 +#define kTile5085 5085 +#define kTile5086 5086 +#define kTile5087 5087 +#define kTile5088 5088 +#define kTile5089 5089 +#define kTile5090 5090 +#define kTile5091 5091 +#define kTile5092 5092 +#define kTile5093 5093 +#define kTile5094 5094 +#define kTile5095 5095 +#define kTile5096 5096 +#define kTile5097 5097 +#define kTile5098 5098 +#define kTile5099 5099 +#define kTile5100 5100 +#define kTile5101 5101 +#define kTile5102 5102 +#define kTile5103 5103 +#define kTile5104 5104 +#define kTile5105 5105 +#define kTile5106 5106 +#define kTile5107 5107 +#define kTile5108 5108 +#define kTile5109 5109 +#define kTile5110 5110 +#define kTile5111 5111 +#define kTile5112 5112 +#define kTile5113 5113 +#define kTile5114 5114 +#define kTile5115 5115 +#define kTile5116 5116 +#define kTile5117 5117 +#define kTile5118 5118 +#define kTile5119 5119 +#define kTile5120 5120 +#define kTile5121 5121 +#define kTile5122 5122 +#define kTile5123 5123 +#define kTile5124 5124 +#define kTile5125 5125 +#define kTile5126 5126 +#define kTile5127 5127 +#define kTile5128 5128 +#define kTile5129 5129 +#define kTile5130 5130 +#define kTile5131 5131 +#define kTile5132 5132 +#define kTile5133 5133 +#define kTile5134 5134 +#define kTile5135 5135 +#define kTile5136 5136 +#define kTile5137 5137 +#define kTile5138 5138 +#define kTile5139 5139 +#define kTile5140 5140 +#define kTile5141 5141 +#define kTile5142 5142 +#define kTile5143 5143 +#define kTile5144 5144 +#define kTile5145 5145 +#define kTile5146 5146 +#define kTile5147 5147 +#define kTile5148 5148 +#define kTile5149 5149 +#define kTile5150 5150 +#define kTile5151 5151 +#define kTile5152 5152 +#define kTile5153 5153 +#define kTile5154 5154 +#define kTile5155 5155 +#define kTile5156 5156 +#define kTile5157 5157 +#define kTile5158 5158 +#define kTile5159 5159 +#define kTile5160 5160 +#define kTile5161 5161 +#define kTile5162 5162 +#define kTile5163 5163 +#define kTile5164 5164 +#define kTile5165 5165 +#define kTile5166 5166 +#define kTile5167 5167 +#define kTile5168 5168 +#define kTile5169 5169 +#define kTile5170 5170 +#define kTile5171 5171 +#define kTile5172 5172 +#define kTile5173 5173 +#define kTile5174 5174 +#define kTile5175 5175 +#define kTile5176 5176 +#define kTile5177 5177 +#define kTile5178 5178 +#define kTile5179 5179 +#define kTile5180 5180 +#define kTile5181 5181 +#define kTile5182 5182 +#define kTile5183 5183 +#define kTile5184 5184 +#define kTile5185 5185 +#define kTile5186 5186 +#define kTile5187 5187 +#define kTile5188 5188 +#define kTile5189 5189 +#define kTile5190 5190 +#define kTile5191 5191 +#define kTile5192 5192 +#define kTile5193 5193 +#define kTile5194 5194 +#define kTile5195 5195 +#define kTile5196 5196 +#define kTile5197 5197 +#define kTile5198 5198 +#define kTile5199 5199 +#define kTile5200 5200 +#define kTile5201 5201 +#define kTile5202 5202 +#define kTile5203 5203 +#define kTile5204 5204 +#define kTile5205 5205 +#define kTile5206 5206 +#define kTile5207 5207 +#define kTile5208 5208 +#define kTile5209 5209 +#define kTile5210 5210 +#define kTile5211 5211 +#define kTile5212 5212 +#define kTile5213 5213 +#define kTile5214 5214 +#define kTile5215 5215 +#define kTile5216 5216 +#define kTile5217 5217 +#define kTile5218 5218 +#define kTile5219 5219 +#define kTile5220 5220 +#define kTile5221 5221 +#define kTile5222 5222 +#define kTile5223 5223 +#define kTile5224 5224 +#define kTile5225 5225 +#define kTile5226 5226 +#define kTile5227 5227 +#define kTile5228 5228 +#define kTile5229 5229 +#define kTile5230 5230 +#define kTile5231 5231 +#define kTile5232 5232 +#define kTile5233 5233 +#define kTile5234 5234 +#define kTile5235 5235 +#define kTile5236 5236 +#define kTile5237 5237 +#define kTile5238 5238 +#define kTile5239 5239 +#define kTile5240 5240 +#define kTile5241 5241 +#define kTile5242 5242 +#define kTile5243 5243 +#define kTile5244 5244 +#define kTile5245 5245 +#define kTile5246 5246 +#define kTile5247 5247 +#define kTile5248 5248 +#define kTile5249 5249 +#define kTile5250 5250 +#define kTile5251 5251 +#define kTile5252 5252 +#define kTile5253 5253 +#define kTile5254 5254 +#define kTile5255 5255 +#define kTile5256 5256 +#define kTile5257 5257 +#define kTile5258 5258 +#define kTile5259 5259 +#define kTile5260 5260 +#define kTile5261 5261 +#define kTile5262 5262 +#define kTile5263 5263 +#define kTile5264 5264 +#define kTile5265 5265 +#define kTile5266 5266 +#define kTile5267 5267 +#define kTile5268 5268 +#define kTile5269 5269 +#define kTile5270 5270 +#define kTile5271 5271 +#define kTile5272 5272 +#define kTile5273 5273 +#define kTile5274 5274 +#define kTile5275 5275 +#define kTile5276 5276 +#define kTile5277 5277 +#define kTile5278 5278 +#define kTile5279 5279 +#define kTile5280 5280 +#define kTile5281 5281 +#define kTile5282 5282 +#define kTile5283 5283 +#define kTile5284 5284 +#define kTile5285 5285 +#define kTile5286 5286 +#define kTile5287 5287 +#define kTile5288 5288 +#define kTile5289 5289 +#define kTile5290 5290 +#define kTile5291 5291 +#define kTile5292 5292 +#define kTile5293 5293 +#define kTile5294 5294 +#define kTile5295 5295 +#define kTile5296 5296 +#define kTile5297 5297 +#define kTile5298 5298 +#define kTile5299 5299 +#define kTile5300 5300 +#define kTile5301 5301 +#define kTile5302 5302 +#define kTile5303 5303 +#define kTile5304 5304 +#define kTile5305 5305 +#define kTile5306 5306 +#define kTile5307 5307 +#define kTile5308 5308 +#define kTile5309 5309 +#define kTile5310 5310 +#define kTile5311 5311 +#define kTile5312 5312 +#define kTile5313 5313 +#define kTile5314 5314 +#define kTile5315 5315 +#define kTile5316 5316 +#define kTile5317 5317 +#define kTile5318 5318 +#define kTile5319 5319 +#define kTile5320 5320 +#define kTile5321 5321 +#define kTile5322 5322 +#define kTile5323 5323 +#define kTile5324 5324 +#define kTile5325 5325 +#define kTile5326 5326 +#define kTile5327 5327 +#define kTile5328 5328 +#define kTile5329 5329 +#define kTile5330 5330 +#define kTile5331 5331 +#define kTile5332 5332 +#define kTile5333 5333 +#define kTile5334 5334 +#define kTile5335 5335 +#define kTile5336 5336 +#define kTile5337 5337 +#define kTile5338 5338 +#define kTile5339 5339 +#define kTile5340 5340 +#define kTile5341 5341 +#define kTile5342 5342 +#define kTile5343 5343 +#define kTile5344 5344 +#define kTile5345 5345 +#define kTile5346 5346 +#define kTile5347 5347 +#define kTile5348 5348 +#define kTile5349 5349 +#define kTile5350 5350 +#define kTile5351 5351 +#define kTile5352 5352 +#define kTile5353 5353 +#define kTile5354 5354 +#define kTile5355 5355 +#define kTile5356 5356 +#define kTile5357 5357 +#define kTile5358 5358 +#define kTile5359 5359 +#define kTile5360 5360 +#define kTile5361 5361 +#define kTile5362 5362 +#define kTile5363 5363 +#define kTile5364 5364 +#define kTile5365 5365 +#define kTile5366 5366 +#define kTile5367 5367 +#define kTile5368 5368 +#define kTile5369 5369 +#define kTile5370 5370 +#define kTile5371 5371 +#define kTile5372 5372 +#define kTile5373 5373 +#define kTile5374 5374 +#define kTile5375 5375 +#define kTile5376 5376 +#define kTile5377 5377 +#define kTile5378 5378 +#define kTile5379 5379 +#define kTile5380 5380 +#define kTile5381 5381 +#define kTile5382 5382 +#define kTile5383 5383 +#define kTile5384 5384 +#define kTile5385 5385 +#define kTile5386 5386 +#define kTile5387 5387 +#define kTile5388 5388 +#define kTile5389 5389 +#define kTile5390 5390 +#define kTile5391 5391 +#define kTile5392 5392 +#define kTile5393 5393 +#define kTile5394 5394 +#define kTile5395 5395 +#define kTile5396 5396 +#define kTile5397 5397 +#define kTile5398 5398 +#define kTile5399 5399 +#define kTile5400 5400 +#define kTile5401 5401 +#define kTile5402 5402 +#define kTile5403 5403 +#define kTile5404 5404 +#define kTile5405 5405 +#define kTile5406 5406 +#define kTile5407 5407 +#define kTile5408 5408 +#define kTile5409 5409 +#define kTile5410 5410 +#define kTile5411 5411 +#define kTile5412 5412 +#define kTile5413 5413 +#define kTile5414 5414 +#define kTile5415 5415 +#define kTile5416 5416 +#define kTile5417 5417 +#define kTile5418 5418 +#define kTile5419 5419 +#define kTile5420 5420 +#define kTile5421 5421 +#define kTile5422 5422 +#define kTile5423 5423 +#define kTile5424 5424 +#define kTile5425 5425 +#define kTile5426 5426 +#define kTile5427 5427 +#define kTile5428 5428 +#define kTile5429 5429 +#define kTile5430 5430 +#define kTile5431 5431 +#define kTile5432 5432 +#define kTile5433 5433 +#define kTile5434 5434 +#define kTile5435 5435 +#define kTile5436 5436 +#define kTile5437 5437 +#define kTile5438 5438 +#define kTile5439 5439 +#define kTile5440 5440 +#define kTile5441 5441 +#define kTile5442 5442 +#define kTile5443 5443 +#define kTile5444 5444 +#define kTile5445 5445 +#define kTile5446 5446 +#define kTile5447 5447 +#define kTile5448 5448 +#define kTile5449 5449 +#define kTile5450 5450 +#define kTile5451 5451 +#define kTile5452 5452 +#define kTile5453 5453 +#define kTile5454 5454 +#define kTile5455 5455 +#define kTile5456 5456 +#define kTile5457 5457 +#define kTile5458 5458 +#define kTile5459 5459 +#define kTile5460 5460 +#define kTile5461 5461 +#define kTile5462 5462 +#define kTile5463 5463 +#define kTile5464 5464 +#define kTile5465 5465 +#define kTile5466 5466 +#define kTile5467 5467 +#define kTile5468 5468 +#define kTile5469 5469 +#define kTile5470 5470 +#define kTile5471 5471 +#define kTile5472 5472 +#define kTile5473 5473 +#define kTile5474 5474 +#define kTile5475 5475 +#define kTile5476 5476 +#define kTile5477 5477 +#define kTile5478 5478 +#define kTile5479 5479 +#define kTile5480 5480 +#define kTile5481 5481 +#define kTile5482 5482 +#define kTile5483 5483 +#define kTile5484 5484 +#define kTile5485 5485 +#define kTile5486 5486 +#define kTile5487 5487 +#define kTile5488 5488 +#define kTile5489 5489 +#define kTile5490 5490 +#define kTile5491 5491 +#define kTile5492 5492 +#define kTile5493 5493 +#define kTile5494 5494 +#define kTile5495 5495 +#define kTile5496 5496 +#define kTile5497 5497 +#define kTile5498 5498 +#define kTile5499 5499 +#define kTile5500 5500 +#define kTile5501 5501 +#define kTile5502 5502 +#define kTile5503 5503 +#define kTile5504 5504 +#define kTile5505 5505 +#define kTile5506 5506 +#define kTile5507 5507 +#define kTile5508 5508 +#define kTile5509 5509 +#define kTile5510 5510 +#define kTile5511 5511 +#define kTile5512 5512 +#define kTile5513 5513 +#define kTile5514 5514 +#define kTile5515 5515 +#define kTile5516 5516 +#define kTile5517 5517 +#define kTile5518 5518 +#define kTile5519 5519 +#define kTile5520 5520 +#define kTile5521 5521 +#define kTile5522 5522 +#define kTile5523 5523 +#define kTile5524 5524 +#define kTile5525 5525 +#define kTile5526 5526 +#define kTile5527 5527 +#define kTile5528 5528 +#define kTile5529 5529 +#define kTile5530 5530 +#define kTile5531 5531 +#define kTile5532 5532 +#define kTile5533 5533 +#define kTile5534 5534 +#define kTile5535 5535 +#define kTile5536 5536 +#define kTile5537 5537 +#define kTile5538 5538 +#define kTile5539 5539 +#define kTile5540 5540 +#define kTile5541 5541 +#define kTile5542 5542 +#define kTile5543 5543 +#define kTile5544 5544 +#define kTile5545 5545 +#define kTile5546 5546 +#define kTile5547 5547 +#define kTile5548 5548 +#define kTile5549 5549 +#define kTile5550 5550 +#define kTile5551 5551 +#define kTile5552 5552 +#define kTile5553 5553 +#define kTile5554 5554 +#define kTile5555 5555 +#define kTile5556 5556 +#define kTile5557 5557 +#define kTile5558 5558 +#define kTile5559 5559 +#define kTile5560 5560 +#define kTile5561 5561 +#define kTile5562 5562 +#define kTile5563 5563 +#define kTile5564 5564 +#define kTile5565 5565 +#define kTile5566 5566 +#define kTile5567 5567 +#define kTile5568 5568 +#define kTile5569 5569 +#define kTile5570 5570 +#define kTile5571 5571 +#define kTile5572 5572 +#define kTile5573 5573 +#define kTile5574 5574 +#define kTile5575 5575 +#define kTile5576 5576 +#define kTile5577 5577 +#define kTile5578 5578 +#define kTile5579 5579 +#define kTile5580 5580 +#define kTile5581 5581 +#define kTile5582 5582 +#define kTile5583 5583 +#define kTile5584 5584 +#define kTile5585 5585 +#define kTile5586 5586 +#define kTile5587 5587 +#define kTile5588 5588 +#define kTile5589 5589 +#define kTile5590 5590 +#define kTile5591 5591 +#define kTile5592 5592 +#define kTile5593 5593 +#define kTile5594 5594 +#define kTile5595 5595 +#define kTile5596 5596 +#define kTile5597 5597 +#define kTile5598 5598 +#define kTile5599 5599 +#define kTile5600 5600 +#define kTile5601 5601 +#define kTile5602 5602 +#define kTile5603 5603 +#define kTile5604 5604 +#define kTile5605 5605 +#define kTile5606 5606 +#define kTile5607 5607 +#define kTile5608 5608 +#define kTile5609 5609 +#define kTile5610 5610 +#define kTile5611 5611 +#define kTile5612 5612 +#define kTile5613 5613 +#define kTile5614 5614 +#define kTile5615 5615 +#define kTile5616 5616 +#define kTile5617 5617 +#define kTile5618 5618 +#define kTile5619 5619 +#define kTile5620 5620 +#define kTile5621 5621 +#define kTile5622 5622 +#define kTile5623 5623 +#define kTile5624 5624 +#define kTile5625 5625 +#define kTile5626 5626 +#define kTile5627 5627 +#define kTile5628 5628 +#define kTile5629 5629 +#define kTile5630 5630 +#define kTile5631 5631 +#define kTile5632 5632 +#define kTile5633 5633 +#define kTile5634 5634 +#define kTile5635 5635 +#define kTile5636 5636 +#define kTile5637 5637 +#define kTile5638 5638 +#define kTile5639 5639 +#define kTile5640 5640 +#define kTile5641 5641 +#define kTile5642 5642 +#define kTile5643 5643 +#define kTile5644 5644 +#define kTile5645 5645 +#define kTile5646 5646 +#define kTile5647 5647 +#define kTile5648 5648 +#define kTile5649 5649 +#define kTile5650 5650 +#define kTile5651 5651 +#define kTile5652 5652 +#define kTile5653 5653 +#define kTile5654 5654 +#define kTile5655 5655 +#define kTile5656 5656 +#define kTile5657 5657 +#define kTile5658 5658 +#define kTile5659 5659 +#define kTile5660 5660 +#define kTile5661 5661 +#define kTile5662 5662 +#define kTile5663 5663 +#define kTile5664 5664 +#define kTile5665 5665 +#define kTile5666 5666 +#define kTile5667 5667 +#define kTile5668 5668 +#define kTile5669 5669 +#define kTile5670 5670 +#define kTile5671 5671 +#define kTile5672 5672 +#define kTile5673 5673 +#define kTile5674 5674 +#define kTile5675 5675 +#define kTile5676 5676 +#define kTile5677 5677 +#define kTile5678 5678 +#define kTile5679 5679 +#define kTile5680 5680 +#define kTile5681 5681 +#define kTile5682 5682 +#define kTile5683 5683 +#define kTile5684 5684 +#define kTile5685 5685 +#define kTile5686 5686 +#define kTile5687 5687 +#define kTile5688 5688 +#define kTile5689 5689 +#define kTile5690 5690 +#define kTile5691 5691 +#define kTile5692 5692 +#define kTile5693 5693 +#define kTile5694 5694 +#define kTile5695 5695 +#define kTile5696 5696 +#define kTile5697 5697 +#define kTile5698 5698 +#define kTile5699 5699 +#define kTile5700 5700 +#define kTile5701 5701 +#define kTile5702 5702 +#define kTile5703 5703 +#define kTile5704 5704 +#define kTile5705 5705 +#define kTile5706 5706 +#define kTile5707 5707 +#define kTile5708 5708 +#define kTile5709 5709 +#define kTile5710 5710 +#define kTile5711 5711 +#define kTile5712 5712 +#define kTile5713 5713 +#define kTile5714 5714 +#define kTile5715 5715 +#define kTile5716 5716 +#define kTile5717 5717 +#define kTile5718 5718 +#define kTile5719 5719 +#define kTile5720 5720 +#define kTile5721 5721 +#define kTile5722 5722 +#define kTile5723 5723 +#define kTile5724 5724 +#define kTile5725 5725 +#define kTile5726 5726 +#define kTile5727 5727 +#define kTile5728 5728 +#define kTile5729 5729 +#define kTile5730 5730 +#define kTile5731 5731 +#define kTile5732 5732 +#define kTile5733 5733 +#define kTile5734 5734 +#define kTile5735 5735 +#define kTile5736 5736 +#define kTile5737 5737 +#define kTile5738 5738 +#define kTile5739 5739 +#define kTile5740 5740 +#define kTile5741 5741 +#define kTile5742 5742 +#define kTile5743 5743 +#define kTile5744 5744 +#define kTile5745 5745 +#define kTile5746 5746 +#define kTile5747 5747 +#define kTile5748 5748 +#define kTile5749 5749 +#define kTile5750 5750 +#define kTile5751 5751 +#define kTile5752 5752 +#define kTile5753 5753 +#define kTile5754 5754 +#define kTile5755 5755 +#define kTile5756 5756 +#define kTile5757 5757 +#define kTile5758 5758 +#define kTile5759 5759 +#define kTile5760 5760 +#define kTile5761 5761 +#define kTile5762 5762 +#define kTile5763 5763 +#define kTile5764 5764 +#define kTile5765 5765 +#define kTile5766 5766 +#define kTile5767 5767 +#define kTile5768 5768 +#define kTile5769 5769 +#define kTile5770 5770 +#define kTile5771 5771 +#define kTile5772 5772 +#define kTile5773 5773 +#define kTile5774 5774 +#define kTile5775 5775 +#define kTile5776 5776 +#define kTile5777 5777 +#define kTile5778 5778 +#define kTile5779 5779 +#define kTile5780 5780 +#define kTile5781 5781 +#define kTile5782 5782 +#define kTile5783 5783 +#define kTile5784 5784 +#define kTile5785 5785 +#define kTile5786 5786 +#define kTile5787 5787 +#define kTile5788 5788 +#define kTile5789 5789 +#define kTile5790 5790 +#define kTile5791 5791 +#define kTile5792 5792 +#define kTile5793 5793 +#define kTile5794 5794 +#define kTile5795 5795 +#define kTile5796 5796 +#define kTile5797 5797 +#define kTile5798 5798 +#define kTile5799 5799 +#define kTile5800 5800 +#define kTile5801 5801 +#define kTile5802 5802 +#define kTile5803 5803 +#define kTile5804 5804 +#define kTile5805 5805 +#define kTile5806 5806 +#define kTile5807 5807 +#define kTile5808 5808 +#define kTile5809 5809 +#define kTile5810 5810 +#define kTile5811 5811 +#define kTile5812 5812 +#define kTile5813 5813 +#define kTile5814 5814 +#define kTile5815 5815 +#define kTile5816 5816 +#define kTile5817 5817 +#define kTile5818 5818 +#define kTile5819 5819 +#define kTile5820 5820 +#define kTile5821 5821 +#define kTile5822 5822 +#define kTile5823 5823 +#define kTile5824 5824 +#define kTile5825 5825 +#define kTile5826 5826 +#define kTile5827 5827 +#define kTile5828 5828 +#define kTile5829 5829 +#define kTile5830 5830 +#define kTile5831 5831 +#define kTile5832 5832 +#define kTile5833 5833 +#define kTile5834 5834 +#define kTile5835 5835 +#define kTile5836 5836 +#define kTile5837 5837 +#define kTile5838 5838 +#define kTile5839 5839 +#define kTile5840 5840 +#define kTile5841 5841 +#define kTile5842 5842 +#define kTile5843 5843 +#define kTile5844 5844 +#define kTile5845 5845 +#define kTile5846 5846 +#define kTile5847 5847 +#define kTile5848 5848 +#define kTile5849 5849 +#define kTile5850 5850 +#define kTile5851 5851 +#define kTile5852 5852 +#define kTile5853 5853 +#define kTile5854 5854 +#define kTile5855 5855 +#define kTile5856 5856 +#define kTile5857 5857 +#define kTile5858 5858 +#define kTile5859 5859 +#define kTile5860 5860 +#define kTile5861 5861 +#define kTile5862 5862 +#define kTile5863 5863 +#define kTile5864 5864 +#define kTile5865 5865 +#define kTile5866 5866 +#define kTile5867 5867 +#define kTile5868 5868 +#define kTile5869 5869 +#define kTile5870 5870 +#define kTile5871 5871 +#define kTile5872 5872 +#define kTile5873 5873 +#define kTile5874 5874 +#define kTile5875 5875 +#define kTile5876 5876 +#define kTile5877 5877 +#define kTile5878 5878 +#define kTile5879 5879 +#define kTile5880 5880 +#define kTile5881 5881 +#define kTile5882 5882 +#define kTile5883 5883 +#define kTile5884 5884 +#define kTile5885 5885 +#define kTile5886 5886 +#define kTile5887 5887 +#define kTile5888 5888 +#define kTile5889 5889 +#define kTile5890 5890 +#define kTile5891 5891 +#define kTile5892 5892 +#define kTile5893 5893 +#define kTile5894 5894 +#define kTile5895 5895 +#define kTile5896 5896 +#define kTile5897 5897 +#define kTile5898 5898 +#define kTile5899 5899 +#define kTile5900 5900 +#define kTile5901 5901 +#define kTile5902 5902 +#define kTile5903 5903 +#define kTile5904 5904 +#define kTile5905 5905 +#define kTile5906 5906 +#define kTile5907 5907 +#define kTile5908 5908 +#define kTile5909 5909 +#define kTile5910 5910 +#define kTile5911 5911 +#define kTile5912 5912 +#define kTile5913 5913 +#define kTile5914 5914 +#define kTile5915 5915 +#define kTile5916 5916 +#define kTile5917 5917 +#define kTile5918 5918 +#define kTile5919 5919 +#define kTile5920 5920 +#define kTile5921 5921 +#define kTile5922 5922 +#define kTile5923 5923 +#define kTile5924 5924 +#define kTile5925 5925 +#define kTile5926 5926 +#define kTile5927 5927 +#define kTile5928 5928 +#define kTile5929 5929 +#define kTile5930 5930 +#define kTile5931 5931 +#define kTile5932 5932 +#define kTile5933 5933 +#define kTile5934 5934 +#define kTile5935 5935 +#define kTile5936 5936 +#define kTile5937 5937 +#define kTile5938 5938 +#define kTile5939 5939 +#define kTile5940 5940 +#define kTile5941 5941 +#define kTile5942 5942 +#define kTile5943 5943 +#define kTile5944 5944 +#define kTile5945 5945 +#define kTile5946 5946 +#define kTile5947 5947 +#define kTile5948 5948 +#define kTile5949 5949 +#define kTile5950 5950 +#define kTile5951 5951 +#define kTile5952 5952 +#define kTile5953 5953 +#define kTile5954 5954 +#define kTile5955 5955 +#define kTile5956 5956 +#define kTile5957 5957 +#define kTile5958 5958 +#define kTile5959 5959 +#define kTile5960 5960 +#define kTile5961 5961 +#define kTile5962 5962 +#define kTile5963 5963 +#define kTile5964 5964 +#define kTile5965 5965 +#define kTile5966 5966 +#define kTile5967 5967 +#define kTile5968 5968 +#define kTile5969 5969 +#define kTile5970 5970 +#define kTile5971 5971 +#define kTile5972 5972 +#define kTile5973 5973 +#define kTile5974 5974 +#define kTile5975 5975 +#define kTile5976 5976 +#define kTile5977 5977 +#define kTile5978 5978 +#define kTile5979 5979 +#define kTile5980 5980 +#define kTile5981 5981 +#define kTile5982 5982 +#define kTile5983 5983 +#define kTile5984 5984 +#define kTile5985 5985 +#define kTile5986 5986 +#define kTile5987 5987 +#define kTile5988 5988 +#define kTile5989 5989 +#define kTile5990 5990 +#define kTile5991 5991 +#define kTile5992 5992 +#define kTile5993 5993 +#define kTile5994 5994 +#define kTile5995 5995 +#define kTile5996 5996 +#define kTile5997 5997 +#define kTile5998 5998 +#define kTile5999 5999 +#define kTile6000 6000 +#define kTile6001 6001 +#define kTile6002 6002 +#define kTile6003 6003 +#define kTile6004 6004 +#define kTile6005 6005 +#define kTile6006 6006 +#define kTile6007 6007 +#define kTile6008 6008 +#define kTile6009 6009 +#define kTile6010 6010 +#define kTile6011 6011 +#define kTile6012 6012 +#define kTile6013 6013 +#define kTile6014 6014 +#define kTile6015 6015 +#define kTile6016 6016 +#define kTile6017 6017 +#define kTile6018 6018 +#define kTile6019 6019 +#define kTile6020 6020 +#define kTile6021 6021 +#define kTile6022 6022 +#define kTile6023 6023 +#define kTile6024 6024 +#define kTile6025 6025 +#define kTile6026 6026 +#define kTile6027 6027 +#define kTile6028 6028 +#define kTile6029 6029 +#define kTile6030 6030 +#define kTile6031 6031 +#define kTile6032 6032 +#define kTile6033 6033 +#define kTile6034 6034 +#define kTile6035 6035 +#define kTile6036 6036 +#define kTile6037 6037 +#define kTile6038 6038 +#define kTile6039 6039 +#define kTile6040 6040 +#define kTile6041 6041 +#define kTile6042 6042 +#define kTile6043 6043 +#define kTile6044 6044 +#define kTile6045 6045 +#define kTile6046 6046 +#define kTile6047 6047 +#define kTile6048 6048 +#define kTile6049 6049 +#define kTile6050 6050 +#define kTile6051 6051 +#define kTile6052 6052 +#define kTile6053 6053 +#define kTile6054 6054 +#define kTile6055 6055 +#define kTile6056 6056 +#define kTile6057 6057 +#define kTile6058 6058 +#define kTile6059 6059 +#define kTile6060 6060 +#define kTile6061 6061 +#define kTile6062 6062 +#define kTile6063 6063 +#define kTile6064 6064 +#define kTile6065 6065 +#define kTile6066 6066 +#define kTile6067 6067 +#define kTile6068 6068 +#define kTile6069 6069 +#define kTile6070 6070 +#define kTile6071 6071 +#define kTile6072 6072 +#define kTile6073 6073 +#define kTile6074 6074 +#define kTile6075 6075 +#define kTile6076 6076 +#define kTile6077 6077 +#define kTile6078 6078 +#define kTile6079 6079 +#define kTile6080 6080 +#define kTile6081 6081 +#define kTile6082 6082 +#define kTile6083 6083 +#define kTile6084 6084 +#define kTile6085 6085 +#define kTile6086 6086 +#define kTile6087 6087 +#define kTile6088 6088 +#define kTile6089 6089 +#define kTile6090 6090 +#define kTile6091 6091 +#define kTile6092 6092 +#define kTile6093 6093 +#define kTile6094 6094 +#define kTile6095 6095 +#define kTile6096 6096 +#define kTile6097 6097 +#define kTile6098 6098 +#define kTile6099 6099 +#define kTile6100 6100 +#define kTile6101 6101 +#define kTile6102 6102 +#define kTile6103 6103 +#define kTile6104 6104 +#define kTile6105 6105 +#define kTile6106 6106 +#define kTile6107 6107 +#define kTile6108 6108 +#define kTile6109 6109 +#define kTile6110 6110 +#define kTile6111 6111 +#define kTile6112 6112 +#define kTile6113 6113 +#define kTile6114 6114 +#define kTile6115 6115 +#define kTile6116 6116 +#define kTile6117 6117 +#define kTile6118 6118 +#define kTile6119 6119 +#define kTile6120 6120 +#define kTile6121 6121 +#define kTile6122 6122 +#define kTile6123 6123 +#define kTile6124 6124 +#define kTile6125 6125 +#define kTile6126 6126 +#define kTile6127 6127 +#define kTile6128 6128 +#define kTile6129 6129 +#define kTile6130 6130 +#define kTile6131 6131 +#define kTile6132 6132 +#define kTile6133 6133 +#define kTile6134 6134 +#define kTile6135 6135 +#define kTile6136 6136 +#define kTile6137 6137 +#define kTile6138 6138 +#define kTile6139 6139 +#define kTile6140 6140 +#define kTile6141 6141 +#define kTile6142 6142 +#define kTile6143 6143 + +#endif \ No newline at end of file diff --git a/source/exhumed/src/network.cpp b/source/exhumed/src/network.cpp new file mode 100644 index 000000000..f270158c5 --- /dev/null +++ b/source/exhumed/src/network.cpp @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------- +/* +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" +// this is net.c in the original code + +#include "typedefs.h" +#include "network.h" +#include "serial.h" +#include "ps_input.h" + +BEGIN_PS_NS + +short nNetMoveFrames = 0; + + +void SendGoodbye() +{ + bSendBye = kTrue; + UpdateInputs(); +} + +void UpdateNetInputs() +{ + +} +/* +int InitNet(short nSocket, int nPlayers) +{ + return 0; +} +*/ + +int InitSerial() +{ + return 1; +} + +void AbortNetworkPlay() +{ + +} + +void UnInitNet() +{ + +} +END_PS_NS diff --git a/source/exhumed/src/network.h b/source/exhumed/src/network.h new file mode 100644 index 000000000..18a39fa8f --- /dev/null +++ b/source/exhumed/src/network.h @@ -0,0 +1,35 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __network_h__ +#define __network_h__ + +BEGIN_PS_NS + +extern short nNetMoveFrames; + +void SendGoodbye(); +void UpdateNetInputs(); +int InitNet(short nSocket, int nPlayers); +int InitSerial(); +void AbortNetworkPlay(); +void UnInitNet(); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/object.cpp b/source/exhumed/src/object.cpp new file mode 100644 index 000000000..3844a5788 --- /dev/null +++ b/source/exhumed/src/object.cpp @@ -0,0 +1,2718 @@ +//------------------------------------------------------------------------- +/* +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 "engine.h" +#include "object.h" +#include "exhumed.h" +#include "move.h" +#include "random.h" +#include "view.h" +#include "sound.h" +#include "init.h" +#include "runlist.h" +#include "names.h" +#include "sequence.h" +#include "lighting.h" +#include "anims.h" +#include "items.h" +#include "player.h" +#include "trigdat.h" +#include "bullet.h" +#include +#include + +BEGIN_PS_NS + +#define kMaxBobs 200 +#define kMaxDrips 50 +#define kMaxMoveSects 50 +#define kMaxObjects 128 +#define kMaxWallFace 4096 +#define kMaxSlideData 128 +#define kMaxPoints 1024 +#define kMaxTraps 40 +#define kMaxTrails 20 +#define kMaxTrailPoints 100 + + +static short ObjectSeq[] = { + 46, -1, 72, -1 +}; + +static short ObjectStatnum[] = { + kStatExplodeTrigger, kStatExplodeTarget, 98, kStatDestructibleSprite +}; + +struct Trail +{ + short field_0; + short field_2; + short field_4; + short pad; +}; + +struct TrailPoint +{ + int x; + int y; +}; + +struct Bob +{ + short nSector; + char field_2; + char field_3; + int z; +}; + +struct Drip +{ + short nSprite; + short field_2; +}; + +// 56 bytes +struct Elev +{ + short field_0; + short nChannel; + short nSector; + int field_6; + int field_A; + short nCountZOffsets; // count of items in zOffsets + short nCurZOffset; + int zOffsets[8]; // different Z offsets + short field_32; + short nSprite; + short field_36; +}; + +// 16 bytes +struct MoveSect +{ + short nSector; + short nTrail; + short nTrailPoint; + short field_6; + short field_8; // nSector? + int field_10; + short field_14; // nChannel? +}; + +struct Object +{ + short field_0; + short nHealth; + short field_4; + short nSprite; + short field_8; + short field_10; + short field_12; +}; + +struct wallFace +{ + short nChannel; + short nWall; + short field_4; + short field_6[8]; +}; + +// TODO - rename +struct slideData2 +{ + short nChannel; + short field_2; + short field_4; + short field_6; + short field_8; + uint8_t field_A[6]; // pad? +}; + +struct slideData +{ + int field_0; + int field_4; + int field_8; + int field_C; + int field_10; + int field_14; + int field_18; + int field_1C; + int field_20; + int field_24; + int field_28; + int field_2C; + int field_30; + int field_34; + int field_38; + int field_3C; +}; + +struct Point +{ + short field_0; + short field_2; + short field_4; + short field_6; + short field_8; + short field_A; + short field_C; + short field_E; +}; + +struct Trap +{ + short field_0; + short nSprite; + short nType; + short field_6; + short field_8; + short field_A; + short field_C; + short field_E; +}; + +Trap sTrap[kMaxTraps]; + +Bob sBob[kMaxBobs]; +Trail sTrail[kMaxTrails]; +TrailPoint sTrailPoint[kMaxTrailPoints]; +Elev Elevator[kMaxElevs]; +Object ObjectList[kMaxObjects]; +MoveSect sMoveSect[kMaxMoveSects]; +slideData SlideData[kMaxSlideData]; +short sMoveDir[kMaxMoveSects]; +wallFace WallFace[kMaxWallFace]; +slideData2 SlideData2[kMaxSlideData]; +Point PointList[kMaxPoints]; + +short nTrapInterval[kMaxTraps]; + +short sBobID[kMaxBobs]; + +short PointCount; +short PointFree[kMaxPoints]; + +short SlideCount = 0; +short SlideFree[kMaxSlides]; + +char nTrailPointVal[kMaxTrailPoints]; +short nTrailPointPrev[kMaxTrailPoints]; +short nTrailPointNext[kMaxTrailPoints]; + +Drip sDrip[kMaxDrips]; + +short ElevCount = -1; + +short WallFaceCount = -1; + +int lFinaleStart; + +short nTrailPoints; + +short nEnergyBlocks; +short nMoveSects; +short nFinaleStage; +short nTrails; +short nTraps; +short nFinaleSpr; +short ObjectCount; +short nDrips; + +short nBobs = 0; +short nDronePitch = 0; +short nSmokeSparks = 0; + +// done +void InitObjects() +{ + ObjectCount = 0; + nTraps = 0; + nDrips = 0; + nBobs = 0; + nTrails = 0; + nTrailPoints = 0; + nMoveSects = 0; + + memset(sTrail, -1, sizeof(sTrail)); + + nEnergyBlocks = 0; + nDronePitch = 0; + nFinaleStage = 0; + lFinaleStart = 0; + nSmokeSparks = 0; +} + +// done +void InitElev() +{ + ElevCount = kMaxElevs; +} + +// done +int BuildWallSprite(int nSector) +{ + int nWall = sector[nSector].wallptr; + + int x = wall[nWall].x; + int y = wall[nWall].y; + + int x2 = wall[nWall + 1].x; + int y2 = wall[nWall + 1].y; + + int nSprite = insertsprite(nSector, 401); + + sprite[nSprite].x = (x + x2) / 2; + sprite[nSprite].y = (y + y2) / 2; + sprite[nSprite].z = (sector[nSector].floorz + sector[nSector].ceilingz) / 2; + sprite[nSprite].cstat = 0x8000; + + return nSprite; +} + +// done +short FindWallSprites(short nSector) +{ + int var_24 = 0x7FFFFFFF; + int ecx = 0x7FFFFFFF; + + int nWall = sector[nSector].wallptr; + int nWallCount = sector[nSector].wallnum; + + int esi = 0x80000002; + int edi = 0x80000002; + + int i; + + for (i = 0; i < nWallCount; i++) + { + if (wall[nWall].x < var_24) { + var_24 = wall[nWall].x; + } + + if (esi < wall[nWall].x) { + esi = wall[nWall].x; + } + + if (ecx > wall[nWall].y) { + ecx = wall[nWall].y; + } + + if (edi < wall[nWall].y) { + edi = wall[nWall].y; + } + + nWall++; + } + + ecx -= 5; + esi += 5; + edi += 5; + var_24 -= 5; + + int nSprite = -1; + + for (i = 0; i < kMaxSprites; i++) + { + if (sprite[i].lotag == 0) + { + if ((sprite[i].cstat & 0x50) == 80) + { + int var_28 = sprite[i].x; + int ebx = sprite[i].y; + + if ((var_28 >= var_24) && (esi >= var_28) && (ebx >= ecx) && (ebx <= edi)) + { + sprite[i].owner = nSprite; + nSprite = i; + } + } + } + } + + if (nSprite < 0) + { + nSprite = insertsprite(nSector, 401); + + sprite[nSprite].x = (var_24 + esi) / 2; + sprite[nSprite].y = (ecx + edi) / 2; + sprite[nSprite].z = sector[nSector].floorz; + sprite[nSprite].cstat = 0x8000; + sprite[nSprite].owner = -1; + sprite[nSprite].lotag = 0; + sprite[nSprite].hitag = 0; + } + + return nSprite; +} + +int BuildElevF(int nChannel, int nSector, int nWallSprite, int arg_4, int arg_5, int nCount, ...) +{ + assert(ElevCount > 0); + + if (ElevCount <= 0) { + return -1; + } + + ElevCount--; + + Elevator[ElevCount].field_0 = 2; + Elevator[ElevCount].field_6 = arg_4; + Elevator[ElevCount].field_32 = -1; + Elevator[ElevCount].field_A = arg_5; + Elevator[ElevCount].nChannel = nChannel; + Elevator[ElevCount].nSector = nSector; + Elevator[ElevCount].nCountZOffsets = 0; + Elevator[ElevCount].nCurZOffset = 0; + Elevator[ElevCount].field_36 = 0; + + if (nWallSprite < 0) { + nWallSprite = BuildWallSprite(nSector); + } + + Elevator[ElevCount].nSprite = nWallSprite; + + va_list zlist; + va_start(zlist, nCount); + + while (1) + { + if (Elevator[ElevCount].nCountZOffsets >= nCount) { + return ElevCount; + } + + int nVal = Elevator[ElevCount].nCountZOffsets; + + Elevator[ElevCount].nCountZOffsets++; + + Elevator[ElevCount].zOffsets[nVal] = va_arg(zlist, int); + } + va_end(zlist); + + return ElevCount; +} + +int BuildElevC(int arg1, int nChannel, int nSector, int nWallSprite, int arg5, int arg6, int nCount, ...) +{ + int edi = arg5; + + assert(ElevCount > 0); + if (ElevCount <= 0) { + return -1; + } + + ElevCount--; + + Elevator[ElevCount].field_0 = arg1; + + if (arg1 & 4) + { + edi = arg5 / 2; + } + + Elevator[ElevCount].field_6 = edi; + Elevator[ElevCount].nCountZOffsets = 0; + Elevator[ElevCount].field_36 = 0; + Elevator[ElevCount].nCurZOffset = 0; + Elevator[ElevCount].field_A = arg6; + Elevator[ElevCount].field_32 = -1; + Elevator[ElevCount].nChannel = nChannel; + Elevator[ElevCount].nSector = nSector; + + if (nWallSprite < 0) { + nWallSprite = BuildWallSprite(nSector); + } + + Elevator[ElevCount].nSprite = nWallSprite; + + va_list zlist; + va_start(zlist, nCount); + + while (1) + { + if (Elevator[ElevCount].nCountZOffsets >= nCount) { + return ElevCount; + } + + int nVal = Elevator[ElevCount].nCountZOffsets; + + Elevator[ElevCount].nCountZOffsets++; + + Elevator[ElevCount].zOffsets[nVal] = va_arg(zlist, int); + } + va_end(zlist); + + return ElevCount; +} + +// TODO - tidy me up +// RENAME param A - not always Z +// Confirmed 100% correct with original .exe +int LongSeek(int *pZVal, int a2, int a3, int a4) +{ + int v4; // edx@1 + int v5; // ebx@2 + + v4 = a2 - *pZVal; + + if (v4 < 0) + { + v5 = -a3; + if (v5 > v4) + v4 = v5; + (*pZVal) += v4; + } + + if (v4 > 0) + { + if (a4 < v4) + v4 = a4; + (*pZVal) += v4; + } + + return v4; +} + +// done +int CheckSectorSprites(short nSector, int nVal) +{ + int b = 0; + + if (nVal) + { + short nSprite = headspritesect[nSector]; + + int nZDiff = sector[nSector].floorz - sector[nSector].ceilingz; + + while (nSprite != -1) + { + if ((sprite[nSprite].cstat & 0x101) && (nZDiff < GetSpriteHeight(nSprite))) + { + if (nVal != 1) { + return 1; + } + + b = 1; + + runlist_DamageEnemy(nSprite, -1, 5); + + if (sprite[nSprite].statnum == 100 && PlayerList[GetPlayerFromSprite(nSprite)].nHealth <= 0) + { + PlayFXAtXYZ(StaticSound[kSoundJonFDie], + sprite[nSprite].x, + sprite[nSprite].y, + sprite[nSprite].z, + sprite[nSprite].sectnum | 0x4000); + } + } + nSprite = nextspritesect[nSprite]; + } + } + else + { + for (int i = headspritesect[nSector]; i != -1; i = nextspritesect[i]) + { + if (sprite[i].cstat & 0x101) { + return 1; + } + } + b = 0; + } + + return b; +} + +// done +void MoveSectorSprites(int nSector, int z) +{ + int nSprite = headspritesect[nSector]; + + while (nSprite != -1) + { + if (sprite[nSprite].statnum != 200) { + sprite[nSprite].z += z; + } + + nSprite = nextspritesect[nSprite]; + } +} + +void StartElevSound(short nSprite, int nVal) +{ + short nSound; + + if (nVal & 2) { + nSound = nElevSound; + } + else { + nSound = nStoneSound; + } + + D3PlayFX(StaticSound[nSound], nSprite); +} + +void FuncElev(int a, int UNUSED(b), int nRun) +{ + short nElev = RunData[nRun].nVal; + assert(nElev >= 0 && nElev < kMaxElevs); + + short nChannel = Elevator[nElev].nChannel; + short var_18 = Elevator[nElev].field_0; + + assert(nChannel >= 0 && nChannel < kMaxChannels); + + int nMessage = a & 0x7F0000; + + if (nMessage < 0x10000) { + return; + } + +// int var_24 = var_18 & 0x10; // floor based? + + switch (nMessage) + { + default: + { + return; + } + + case 0x10000: + { +// short ax = var_18 & 8; + short dx = sRunChannels[nChannel].c; + + int edi = 999; // FIXME CHECKME - this isn't default set to anything in the ASM that I can see - if ax is 0 and var_24 is 0, this will never be set to a known value otherwise! + + if (var_18 & 0x8) + { + if (dx) { + edi = 1; + } + else { + edi = 0; + } + } + else + { + // loc_20D48: + if (var_18 & 0x10) // was var_24 + { + if (Elevator[nElev].field_32 < 0) + { + Elevator[nElev].field_32 = runlist_AddRunRec(NewRun, RunData[nRun].nMoves); + StartElevSound(Elevator[nElev].nSprite, var_18); + + edi = 1; + } + } + else + { + if (dx < 0) { + edi = 0; + } + else + { + if (dx == Elevator[nElev].nCurZOffset || dx >= Elevator[nElev].nCountZOffsets) + { + Elevator[nElev].field_36 = dx; + edi = 1; + } + else + { + Elevator[nElev].nCurZOffset = sRunChannels[nChannel].c; + edi = 1; + } + } + } + } + + assert(edi != 999); + + // loc_20DF9: + if (edi) + { + if (Elevator[nElev].field_32 < 0) + { + Elevator[nElev].field_32 = runlist_AddRunRec(NewRun, RunData[nRun].nMoves); + + StartElevSound(Elevator[nElev].nSprite, var_18); + } + } + else + { + //loc_20E4E: + if (Elevator[nElev].field_32 >= 0) + { + runlist_SubRunRec(Elevator[nElev].field_32); + Elevator[nElev].field_32 = -1; + } + } + + return; + } + + case 0x20000: + { + short nSector = Elevator[nElev].nSector; + short di = Elevator[nElev].nSprite; + + int ebp = 0; // initialise to *something* + + if (var_18 & 0x2) + { + int nZOffset = Elevator[nElev].nCurZOffset; + int nZVal = Elevator[nElev].zOffsets[nZOffset]; + + short nSectorB = nSector; + + int nVal = LongSeek((int*)§or[nSector].floorz, nZVal, Elevator[nElev].field_6, Elevator[nElev].field_A); + ebp = nVal; + + if (!nVal) + { + if (var_18 & 0x10) + { + Elevator[nElev].nCurZOffset ^= 1; + StartElevSound(di, var_18); + } + else + { + StopSpriteSound(di); + runlist_SubRunRec(nRun); + Elevator[nElev].field_32 = -1; + runlist_ReadyChannel(nChannel); + + D3PlayFX(StaticSound[nStopSound], Elevator[nElev].nSprite); + } + } + else + { + assert(nSector == nSectorB); + MoveSectorSprites(nSector, nVal); + + if (nVal < 0 && CheckSectorSprites(nSector, 2)) + { + runlist_ChangeChannel(nChannel, sRunChannels[nChannel].c == 0); + return; + } + } + } + else + { + // loc_20FC3: + int ceilZ = sector[nSector].ceilingz; + sectortype *var_28 = §or[nSector]; + + int nZOffset = Elevator[nElev].nCurZOffset; + int zVal = Elevator[nElev].zOffsets[nZOffset]; + + int nVal = LongSeek(&ceilZ, zVal, Elevator[nElev].field_6, Elevator[nElev].field_A); + ebp = nVal; + + if (!nVal) + { + if (var_18 & 0x10) + { + Elevator[nElev].nCurZOffset ^= 1; + + StartElevSound(Elevator[nElev].nSprite, var_18); + } + else + { + runlist_SubRunRec(nRun); + Elevator[nElev].field_32 = -1; + StopSpriteSound(Elevator[nElev].nSprite); + D3PlayFX(StaticSound[nStopSound], Elevator[nElev].nSprite); + runlist_ReadyChannel(nChannel); + } + + return; + } + else if (nVal > 0) + { + if (ceilZ == zVal) + { + if (var_18 & 0x4) { + SetQuake(di, 30); + } + + PlayFXAtXYZ(StaticSound[kSound26], sprite[di].x, sprite[di].y, sprite[di].z, sprite[di].sectnum); + } + + if (var_18 & 0x4) + { + if (CheckSectorSprites(nSector, 1)) { + return; + } + } + else + { + if (CheckSectorSprites(nSector, 0)) + { + runlist_ChangeChannel(nChannel, sRunChannels[nChannel].c == 0); + return; + } + } + } + + var_28->ceilingz = ceilZ; + } + + // maybe this doesn't go here? + while (di != -1) + { + sprite[di].z += ebp; + di = sprite[di].owner; + } + + return; + } + } +} + +// done +void InitWallFace() +{ + WallFaceCount = kMaxWallFace; +} + +int BuildWallFace(short nChannel, short nWall, short nCount, ...) +{ + if (WallFaceCount <= 0) { + I_Error("Too many wall faces!\n"); + } + + WallFaceCount--; + + WallFace[WallFaceCount].field_4 = 0; + WallFace[WallFaceCount].nWall = nWall; + WallFace[WallFaceCount].nChannel = nChannel; + + if (nCount > 8) { + nCount = 8; + } + + va_list piclist; + va_start(piclist, nCount); + + while (WallFace[WallFaceCount].field_4 < nCount) + { + int i = WallFace[WallFaceCount].field_4; + WallFace[WallFaceCount].field_4++; + + WallFace[WallFaceCount].field_6[i] = (short)va_arg(piclist, int); + } + va_end(piclist); + + return WallFaceCount | 0x70000; +} + +void FuncWallFace(int a, int UNUSED(b), int nRun) +{ + int nWallFace = RunData[nRun].nVal; + assert(nWallFace >= 0 && nWallFace < kMaxWallFace); + + short nChannel = WallFace[nWallFace].nChannel; + + if ((a & 0x7F0000) != 0x10000) + return; + + short si = sRunChannels[nChannel].c; + + if ((si <= WallFace[nWallFace].field_4) && (si >= 0)) + { + wall[WallFace[nWallFace].nWall].picnum = WallFace[nWallFace].field_6[si]; + } +} + +// done +void InitPoint() +{ + PointCount = 0; + + for (int i = 0; i < kMaxPoints; i++) { + PointFree[i] = i; + } +} + +// done +int GrabPoint() +{ + return PointFree[PointCount++]; +} + +// done +void InitSlide() +{ + SlideCount = kMaxSlides; + + for (int i = 0; i < kMaxSlides; i++) { + SlideFree[i] = i; + } +} + +int IdentifySector(int nVal) +{ + for (int i = 0; i < numsectors; i++) + { + for (int j = 0; ; j++) + { + if (j < sector[i].wallnum) + { + int nWall = sector[i].wallptr; + if (nWall + j == nVal) + return i; + } + else { + break; + } + } + } + + return -1; +} + +int BuildSlide(int nChannel, int edx, int ebx, int ecx, int arg1, int arg2, int arg3) +{ + int var_1C = edx; + int ebp = ebx; + int nVal = ecx; + + if (SlideCount <= 0) { + I_Error("Too many slides!\n"); + return -1; + } + + SlideCount--; + + int nSlide = SlideCount; + + short nSector = IdentifySector(var_1C); + + SlideData2[nSlide].field_4 = -1; + SlideData2[nSlide].nChannel = nChannel; + SlideData2[nSlide].field_2 = -1; + + int nPoint = GrabPoint(); + + SlideData2[nSlide].field_2 = nPoint; + + PointList[nPoint].field_E = -1; + PointList[nPoint].field_0 = nSector; + + short startwall = sector[nSector].wallptr; + short endwall = startwall + sector[nSector].wallnum; + + for (int nWall = startwall; nWall < endwall; nWall++) + { + short ax = SlideData2[nSlide].field_2; + + if (ax >= 0) + { + while (ax >= 0) + { + if (wall[nWall].nextsector == PointList[ax].field_0) { + break; + } + + ax = PointList[ax].field_E; + } + } + else + { + if (wall[nWall].nextsector >= 0) + { + nPoint = GrabPoint(); + + PointList[nPoint].field_E = SlideData2[nSlide].field_2; + PointList[nPoint].field_0 = wall[nWall].nextsector; + + SlideData2[nSlide].field_2 = nPoint; + } + } + } + + SlideData[nSlide].field_0 = var_1C; + SlideData[nSlide].field_8 = arg1; + SlideData[nSlide].field_C = arg2; + SlideData[nSlide].field_10 = wall[var_1C].x; + SlideData[nSlide].field_14 = wall[var_1C].y; + + SlideData[nSlide].field_1C = wall[arg1].y; + SlideData[nSlide].field_18 = wall[arg1].x; + + SlideData[nSlide].field_24 = wall[ebp].y; + SlideData[nSlide].field_20 = wall[ebp].x; + + SlideData[nSlide].field_2C = wall[arg2].y; + SlideData[nSlide].field_28 = wall[arg2].x; + + SlideData[nSlide].field_34 = wall[nVal].y; + SlideData[nSlide].field_4 = ebp; + SlideData[nSlide].field_30 = wall[nVal].x; + SlideData[nSlide].field_38 = wall[arg3].x; + SlideData[nSlide].field_3C = wall[arg3].y; + + int nSprite = insertsprite(nSector, 899); + + SlideData2[nSlide].field_6 = nSprite; + sprite[nSprite].cstat = 0x8000; + sprite[nSprite].x = wall[var_1C].x; + sprite[nSprite].y = wall[var_1C].y; + sprite[nSprite].z = sector[nSector].floorz; + + SlideData2[nSlide].field_8 = 0; + + return nSlide | 0x80000; +} + +void FuncSlide(int a, int UNUSED(b), int nRun) +{ + int nSlide = RunData[nRun].nVal; + assert(nSlide >= 0 && nSlide < kMaxSlides); + + short nChannel = SlideData2[nSlide].nChannel; + + int nMessage = a & 0x7F0000; + + int ebp = 0; + + short cx = sRunChannels[nChannel].c; + + switch (nMessage) + { + case 0x10000: + { + if (SlideData2[nSlide].field_4 >= 0) + { + runlist_SubRunRec(SlideData2[nSlide].field_4); + SlideData2[nSlide].field_4 = -1; + } + + if (sRunChannels[nChannel].c && sRunChannels[nChannel].c != 1) { + return; + } + + SlideData2[nSlide].field_4 = runlist_AddRunRec(NewRun, RunData[nRun].nMoves); + + if (SlideData2[nSlide].field_8 != sRunChannels[nChannel].c) + { + D3PlayFX(StaticSound[kSound23], SlideData2[nSlide].field_6); + SlideData2[nSlide].field_8 = sRunChannels[nChannel].c; + } + + return; + } + + case 0x20000: + { + int clipmask = ebp + 1; // RENAME + + if (cx == 1) + { + short nWall = SlideData[nSlide].field_4; + int x = wall[nWall].x; + int y = wall[nWall].y; + + int nSeekA = LongSeek(&x, SlideData[nSlide].field_30, 20, 20); + int var_34 = nSeekA; + int var_20 = nSeekA; + + int nSeekB = LongSeek(&y, SlideData[nSlide].field_34, 20, 20); + int var_2C = nSeekB; + int var_24 = nSeekB; + + dragpoint(SlideData[nSlide].field_4, x, y, 0); + movesprite(SlideData2[nSlide].field_6, var_34 << 14, var_2C << 14, 0, 0, 0, CLIPMASK1); + + if (var_34 == 0) + { + if (var_2C == 0) + { + ebp = clipmask; + } + } + + nWall = SlideData[nSlide].field_0; + + y = wall[nWall].y + var_24; + x = wall[nWall].x + var_20; + + dragpoint(SlideData[nSlide].field_0, x, y, 0); + + nWall = SlideData[nSlide].field_C; + + x = wall[nWall].x; + y = wall[nWall].y; + + int nSeekC = LongSeek(&x, SlideData[nSlide].field_38, 20, 20); + int var_30 = nSeekC; + var_20 = nSeekC; + + int nSeekD = LongSeek(&y, SlideData[nSlide].field_3C, 20, 20); + int edi = nSeekD; + var_24 = nSeekD; + + dragpoint(SlideData[nSlide].field_C, x, y, 0); + + if (var_30 == 0 && edi == 0) { + ebp++; + } + + nWall = SlideData[nSlide].field_8; + + x = wall[nWall].x + var_20; + y = wall[nWall].y + var_24; + + dragpoint(SlideData[nSlide].field_8, x, y, 0); + } + else if (cx == 0) // right branch + { + short nWall = SlideData[nSlide].field_0; + int x = wall[nWall].x; + int y = wall[nWall].y; + + int nSeekA = LongSeek(&x, SlideData[nSlide].field_10, 20, 20); + int edi = nSeekA; + int var_1C = nSeekA; + + int nSeekB = LongSeek(&y, SlideData[nSlide].field_14, 20, 20); + int ecx = nSeekB; + int var_28 = nSeekB; + + dragpoint(SlideData[nSlide].field_0, x, y, 0); + + if (edi == 0 && ecx == 0) { + ebp = clipmask; + } + + nWall = SlideData[nSlide].field_4; + + y = wall[nWall].y + var_28; + x = wall[nWall].x + var_1C; + + dragpoint(SlideData[nSlide].field_4, x, y, 0); + + nWall = SlideData[nSlide].field_8; + + x = wall[nWall].x; + y = wall[nWall].y; + + int nSeekC = LongSeek(&x, SlideData[nSlide].field_18, 20, 20); + edi = nSeekC; + var_1C = nSeekC; + + int nSeekD = LongSeek(&y, SlideData[nSlide].field_1C, 20, 20); + ecx = nSeekD; + var_28 = nSeekD; + + dragpoint(SlideData[nSlide].field_8, x, y, 0); + + if (edi == 0 && ecx == 0) { + ebp++; + } + + nWall = SlideData[nSlide].field_C; + + y = wall[nWall].y + var_28; + x = wall[nWall].x + var_1C; + + dragpoint(SlideData[nSlide].field_C, x, y, 0); + } + + // loc_21A51: + if (ebp >= 2) + { + runlist_SubRunRec(SlideData2[nSlide].field_4); + + SlideData2[nSlide].field_4 = -1; + D3PlayFX(StaticSound[nStopSound], SlideData2[nSlide].field_6); + + runlist_ReadyChannel(nChannel); + } + + return; + } + } +} + +int BuildTrap(int nSprite, int edx, int ebx, int ecx) +{ + int var_14 = edx; + int var_18 = ebx; + int var_10 = ecx; + + if (nTraps >= kMaxTraps) { + I_Error("Too many traps!\n"); + } + + short nTrap = nTraps; + nTraps++; + + changespritestat(nSprite, 0); + + sprite[nSprite].cstat = 0x8000; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].extra = -1; + + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].hitag = runlist_AddRunRec(NewRun, nTrap | 0x1F0000); + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nTrap | 0x1F0000); + +// GrabTimeSlot(3); + + sTrap[nTrap].nSprite = nSprite; + sTrap[nTrap].nType = (var_14 == 0) + 14; + sTrap[nTrap].field_0 = -1; + + nTrapInterval[nTrap] = 64 - (2 * var_10); + if (nTrapInterval[nTrap] < 5) { + nTrapInterval[nTrap] = 5; + } + + sTrap[nTrap].field_C = 0; + sTrap[nTrap].field_A = 0; + + if (var_18 == -1) { + return nTrap | 0x1F0000; + } + + sTrap[nTrap].field_6 = -1; + sTrap[nTrap].field_8 = -1; + + short nSector = sprite[nSprite].sectnum; + short nWall = sector[nSector].wallptr; + + int i = 0; + + while (1) + { + if (sector[nSector].wallnum >= i) { + return nTrap | 0x1F0000; + } + + if (var_18 == wall[nWall].hitag) + { + if (sTrap[nTrap].field_6 != -1) + { + sTrap[nTrap].field_8 = nWall; + sTrap[nTrap].field_C = wall[nWall].picnum; + return nTrap | 0x1F0000; + } + else + { + sTrap[nTrap].field_6 = nWall; + sTrap[nTrap].field_A = wall[nWall].picnum; + } + } + + ecx++; + nWall++; + } +} + +void FuncTrap(int a, int UNUSED(b), int nRun) +{ + short nTrap = RunData[nRun].nVal; + short nSprite = sTrap[nTrap].nSprite; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + case 0x10000: + { + short nChannel = a & 0x3FFF; + + if (sRunChannels[nChannel].c > 0) + { + sTrap[nTrap].field_0 = 12; + } + else + { + sTrap[nTrap].field_0 = -1; + } + + return; + } + + case 0x20000: + { + if (sTrap[nTrap].field_0 >= 0) + { + sTrap[nTrap].field_0--; + if (sTrap[nTrap].field_0 > 10) { + return; + } + + short nType = sTrap[nTrap].nType; + + if (sTrap[nTrap].field_0 == 0) + { + sTrap[nTrap].field_0 = nTrapInterval[nTrap]; + + if (nType == 14) + { + short nWall = sTrap[nTrap].field_6; + if (nWall > -1) + { + wall[nWall].picnum = sTrap[nTrap].field_A; + } + + nWall = sTrap[nTrap].field_8; + if (nWall > -1) + { + wall[nWall].picnum = sTrap[nTrap].field_C; + } + } + } + else + { + // loc_21D92: + if (sTrap[nTrap].field_0 != 5) { + return; + } + + int nBullet = BuildBullet(nSprite, nType, 0, 0, 0, sprite[nSprite].ang, 0, 1); + if (nBullet > -1) + { + short nBulletSprite = nBullet & 0xFFFF; // isolate the sprite index (disregard top 16 bits) + assert(nBulletSprite >= 0); + + if (nType == 15) + { + sprite[nBulletSprite].ang = (sprite[nBulletSprite].ang - 512) & kAngleMask; + D3PlayFX(StaticSound[kSound32], nSprite); + } + else + { + sprite[nBulletSprite].clipdist = 50; + + short nWall = sTrap[nTrap].field_6; + if (nWall > -1) + { + wall[nWall].picnum = sTrap[nTrap].field_A + 1; + } + + nWall = sTrap[nTrap].field_8; + if (nWall > -1) + { + wall[nWall].picnum = sTrap[nTrap].field_C + 1; + } + + D3PlayFX(StaticSound[kSound36], nSprite); + } + } + } + } + + return; + } + + case 0x30000: + case 0x90000: + case 0x80000: + case 0xA0000: + return; + + default: + DebugOut("unknown msg %d for trap\n", a & 0x7F0000); + return; + } +} + +int BuildArrow(int nSprite, int nVal) +{ + return BuildTrap(nSprite, 0, -1, nVal); +} + +int BuildFireBall(int nSprite, int a, int b) +{ + return BuildTrap(nSprite, 1, a, b); +} + +int BuildSpark(int nSprite, int nVal) +{ + int var_14 = insertsprite(sprite[nSprite].sectnum, 0); + + if (var_14 < 0) { + return -1; + } + + assert(var_14 < kMaxSprites); + + sprite[var_14].x = sprite[nSprite].x; + sprite[var_14].y = sprite[nSprite].y; + sprite[var_14].cstat = 0; + sprite[var_14].shade = -127; + sprite[var_14].pal = 1; + sprite[var_14].xoffset = 0; + sprite[var_14].yoffset = 0; + sprite[var_14].xrepeat = 50; + sprite[var_14].yrepeat = 50; + + if (nVal >= 2) + { + sprite[var_14].picnum = kEnergy2; + nSmokeSparks++; + + if (nVal == 3) + { + sprite[var_14].xrepeat = 120; + sprite[var_14].yrepeat = 120; + } + else + { + sprite[var_14].xrepeat = sprite[nSprite].xrepeat + 15; + sprite[var_14].yrepeat = sprite[nSprite].xrepeat + 15; + } + } + else + { + int nAngle = (sprite[nSprite].ang + 256) - RandomSize(9); + + if (nVal) + { + sprite[var_14].xvel = Sin(nAngle + 512) >> 5; + sprite[var_14].yvel = Sin(nAngle) >> 5; + } + else + { + sprite[var_14].xvel = Sin(nAngle + 512) >> 6; + sprite[var_14].yvel = Sin(nAngle) >> 6; + } + + sprite[var_14].zvel = -(RandomSize(4) << 7); + sprite[var_14].picnum = nVal + 985; + } + + sprite[var_14].z = sprite[nSprite].z; + sprite[var_14].lotag = runlist_HeadRun() + 1; + sprite[var_14].clipdist = 1; + sprite[var_14].hitag = 0; + +// GrabTimeSlot(3); + + sprite[var_14].extra = -1; + sprite[var_14].owner = runlist_AddRunRec(sprite[var_14].lotag - 1, var_14 | 0x260000); + sprite[var_14].hitag = runlist_AddRunRec(NewRun, var_14 | 0x260000); + + return var_14; +} + +void FuncSpark(int a, int UNUSED(b), int nRun) +{ + int nSprite = RunData[nRun].nVal; + assert(nSprite >= 0 && nSprite < kMaxSprites); + + int nMessage = a & 0x7F0000; + + if (nMessage != 0x20000) { + return; + } + + sprite[nSprite].shade += 3; + sprite[nSprite].xrepeat -= 2; + + if (sprite[nSprite].xrepeat >= 4 && sprite[nSprite].shade <= 100) + { + sprite[nSprite].yrepeat -= 2; + + if (sprite[nSprite].picnum == kTile986 && (sprite[nSprite].xrepeat & 2)) + { + BuildSpark(nSprite, 2); + } + + if (sprite[nSprite].picnum >= kTile3000) { + return; + } + + sprite[nSprite].zvel += 128; + + int nMov = movesprite(nSprite, sprite[nSprite].xvel << 12, sprite[nSprite].yvel << 12, sprite[nSprite].zvel, 2560, -2560, CLIPMASK1); + if (!nMov) { + return; + } + + if (sprite[nSprite].zvel <= 0) { + return; + } + } + + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + + if (sprite[nSprite].picnum > kTile3000) { + nSmokeSparks--; + } + + runlist_DoSubRunRec(sprite[nSprite].owner); + runlist_FreeRun(sprite[nSprite].lotag - 1); + runlist_SubRunRec(sprite[nSprite].hitag); + mydeletesprite(nSprite); +} + +void DimLights() +{ + static short word_96786 = 0; + + word_96786 = word_96786 == 0; + if (word_96786 == 0) + return; + + for (int i = 0; i < numsectors; i++) + { + if (sector[i].ceilingshade < 100) + sector[i].ceilingshade++; + + if (sector[i].floorshade < 100) + sector[i].floorshade++; + + short startwall = sector[i].wallptr; + short endwall = startwall + sector[i].wallnum; + + for (int nWall = startwall; nWall < endwall; nWall++) + { + if (wall[nWall].shade < 100) + wall[nWall].shade++; + } + } +} + +void DoFinale() +{ + static int dword_96788 = 0; + static int dword_1542FC = 0; + + if (!lFinaleStart) + return; + + dword_96788++; + + if (dword_96788 < 90) + { + if (!(dword_96788 & 2)) + { + int nAng = RandomSize(11); + sprite[nFinaleSpr].ang = nAng; + BuildSpark(nFinaleSpr, 1); + } + + if (!RandomSize(2)) + { + PlayFX2(StaticSound[kSound78] | 0x2000, nFinaleSpr); + + for (int i = 0; i < nTotalPlayers; i++) { + nQuake[i] = 1280; + } + } + } + else + { + DimLights(); + + if (nDronePitch <= -2400) + { + if (nFinaleStage < 2) + { + if (nFinaleStage == 1) + { + StopLocalSound(); + PlayLocalSound(StaticSound[kSound76], 0); + dword_1542FC = (int)totalclock + 120; + ++nFinaleStage; + } + } + else if (nFinaleStage <= 2) + { + if ((int)totalclock >= dword_1542FC) + { + PlayLocalSound(StaticSound[kSound77], 0); + nFinaleStage++; + dword_1542FC = (int)totalclock + 360; + } + } + else if (nFinaleStage == 3 && (int)totalclock >= dword_1542FC) + { + FinishLevel(); + } + } + else + { + nDronePitch -= 128; + BendAmbientSound(); + nFinaleStage = 1; + } + } +} + +int BuildEnergyBlock(short nSector) +{ + short startwall = sector[nSector].wallptr; + + int x = 0; + int y = 0; + + for (int i = 0; i < sector[nSector].wallnum; i++) + { + x += wall[startwall + i].x; + y += wall[startwall + i].y; + + // wall[startwall + i].picnum = kTile3621; + wall[startwall + i].pal = 0; + wall[startwall + i].shade = 50; + } + + int xAvg = x / 2; + int yAvg = y / 2; + + int nSprite = insertsprite(nSector, 406); + + short nextsector = wall[startwall].nextsector; + + sprite[nSprite].x = xAvg; + sprite[nSprite].y = yAvg; + + sector[nSector].extra = nSprite; + +// GrabTimeSlot(3); + + sprite[nSprite].z = sector[nextsector].floorz; + + // CHECKME - name of this variable? + int nRepeat = (sprite[nSprite].z - sector[nSector].floorz) >> 8; + if (nRepeat > 255) { + nRepeat = 255; + } + + sprite[nSprite].xrepeat = nRepeat; + sprite[nSprite].cstat = 0x8000; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].extra = -1; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].hitag = 0; + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nSprite | 0x250000); + + nEnergyBlocks++; + + return nSprite | 0x250000; +} + +// TODO - tidy +void KillCreatures() +{ + signed int v0; + signed int v1; + int i; + + v0 = 99; + v1 = 99; + + while (1) + { + if (v0 != 100) + { + for (i = headspritestat[v1]; i != -1; i = nextspritestat[i]) + { + runlist_DamageEnemy(i, -1, 1600); + } + } + ++v0; + ++v1; + + if (v0 > 107) { + return; + } + } +} + +void ExplodeEnergyBlock(int nSprite) +{ + short nSector = sprite[nSprite].sectnum; + + short startwall = sector[nSector].wallptr; + short nWalls = sector[nSector].wallnum; + + int i; + + for (i = 0; i < nWalls; i++) + { + short nextwall = wall[startwall + i].nextwall; + + if (wall[nextwall].pal >= 4) { + wall[nextwall].pal = 7; + } + else { + wall[nextwall].pal = 0; + } + + wall[nextwall].shade = 50; + } + + if (sector[nSector].floorpal >= 4) { + sector[nSector].floorpal = 7; + } + else { + sector[nSector].floorpal = 0; + } + + sector[nSector].floorshade = 50; + sector[nSector].extra = -1; + sector[nSector].floorz = sprite[nSprite].z; + + sprite[nSprite].z = (sprite[nSprite].z + sector[nSector].floorz) / 2; + + BuildSpark(nSprite, 3); + + sprite[nSprite].cstat = 0; + sprite[nSprite].xrepeat = 100; + + PlayFX2(StaticSound[kSound78], nSprite); + + sprite[nSprite].xrepeat = 0; + + nEnergyTowers--; + + for (i = 0; i < 20; i++) + { + sprite[nSprite].ang = RandomSize(11); + + BuildSpark(nSprite, 1); + } + + TintPalette(64, 64, 64); + + if (nEnergyTowers == 1) + { + runlist_ChangeChannel(nEnergyChan, nEnergyTowers); + + StatusMessage(1000, "TAKE OUT THE CONTROL CENTER!"); + } + else if (nEnergyTowers != 0) + { + StatusMessage(500, "%d TOWERS REMAINING", nEnergyTowers); + } + else + { + nFinaleSpr = nSprite; + lFinaleStart = (int)totalclock; + + if (!lFinaleStart) { + lFinaleStart = lFinaleStart + 1; + } + + for (i = 0; i < numsectors; i++) + { + if (sector[i].ceilingpal == 1) { + sector[i].ceilingpal = 0; + } + + if (sector[i].floorpal == 1) { + sector[i].floorpal = 0; + } + + short startwall = sector[i].wallptr; + short endwall = startwall + sector[i].wallnum; + + for (int nWall = startwall; nWall < endwall; nWall++) + { + if (wall[nWall].pal == 1) { + wall[nWall].pal = 0; + } + } + } + + KillCreatures(); + } + + changespritestat(nSprite, 0); +} + +void FuncEnergyBlock(int a, int nDamage, int nRun) +{ + short nSprite = RunData[nRun].nVal; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + case 0x20000: + case 0x30000: + case 0x90000: + { + return; + } + + case 0xA0000: + { + short nSector = sprite[nSprite].sectnum; + + if (sector[nSector].extra == -1) { + return; + } + + int nFloorZ = sector[nSector].floorz; + + sector[nSector].floorz = sprite[nSprite].z; + sprite[nSprite].z -= 256; + + nDamage = runlist_CheckRadialDamage(nSprite); + + // restore previous values + sector[nSector].floorz = nFloorZ; + sprite[nSprite].z += 256; + + if (nDamage <= 0) { + return; + } + + // fall through to case 0x80000 + fallthrough__; + } + + case 0x80000: + { + nDamage >>= 2; + if (nDamage <= 0) { + return; + } + + if (nDamage < sprite[nSprite].xrepeat) + { + sprite[nSprite].xrepeat -= nDamage; + + int nSprite2 = insertsprite(lasthitsect, 0); + + sprite[nSprite2].ang = a & 0xFFFF; + sprite[nSprite2].x = lasthitx; + sprite[nSprite2].y = lasthity; + sprite[nSprite2].z = lasthitz; + + BuildSpark(nSprite2, 0); + mydeletesprite(nSprite2); + } + else + { + sprite[nSprite].xrepeat = 0; + + ExplodeEnergyBlock(nSprite); + } + + return; + } + } +} + +int BuildObject(short nSprite, int nOjectType, int nHitag) +{ + if (ObjectCount >= kMaxObjects) { + I_Error("Too many objects!\n"); + } + + short nObject = ObjectCount; + ObjectCount++; + + changespritestat(nSprite, ObjectStatnum[nOjectType]); + + // 0x7FFD to ensure set as blocking ('B' and 'H') sprite and also disable translucency and set not invisible + sprite[nSprite].cstat = (sprite[nSprite].cstat | 0x101) & 0x7FFD; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].extra = -1; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].hitag = 0; + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nObject | 0x170000); + +// GrabTimeSlot(3); + + if (sprite[nSprite].statnum == kStatDestructibleSprite) { + ObjectList[nObject].nHealth = 4; + } + else { + ObjectList[nObject].nHealth = 120; + } + + ObjectList[nObject].nSprite = nSprite; + ObjectList[nObject].field_4 = runlist_AddRunRec(NewRun, nObject | 0x170000); + + short nSeq = ObjectSeq[nOjectType]; + + if (nSeq > -1) + { + ObjectList[nObject].field_8 = SeqOffsets[nSeq]; + + if (!nOjectType) // if not Explosion Trigger (e.g. Exploding Fire Cauldron) + { + ObjectList[nObject].field_0 = RandomSize(4) % (SeqSize[ObjectList[nObject].field_8] - 1); + } + + int nSprite2 = insertsprite(sprite[nSprite].sectnum, 0); + ObjectList[nObject].field_10 = nSprite2; + + sprite[nSprite2].cstat = 0x8000; + sprite[nSprite2].x = sprite[nSprite].x; + sprite[nSprite2].y = sprite[nSprite].y; + sprite[nSprite2].z = sprite[nSprite].z; + } + else + { + ObjectList[nObject].field_0 = 0; + ObjectList[nObject].field_8 = -1; + + if (sprite[nSprite].statnum == kStatDestructibleSprite) { + ObjectList[nObject].field_10 = -1; + } + else { + ObjectList[nObject].field_10 = -nHitag; + } + } + + return nObject | 0x170000; +} + +void ExplodeScreen(short nSprite) +{ + sprite[nSprite].z -= GetSpriteHeight(nSprite) / 2; + + for (int i = 0; i < 30; i++) { + BuildSpark(nSprite, 0); + } + + sprite[nSprite].cstat = 0x8000; + PlayFX2(StaticSound[kSound78], nSprite); +} + +void FuncObject(int a, int b, int nRun) +{ + short nObject = RunData[nRun].nVal; + + short nSprite = ObjectList[nObject].nSprite; + short nStat = sprite[nSprite].statnum; + short bx = ObjectList[nObject].field_8; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + default: + { + DebugOut("unknown msg %d for Object\n", a & 0x7F0000); + return; + } + + case 0x30000: + return; + + case 0x80000: + { + if (nStat >= 150 || ObjectList[nObject].nHealth <= 0) { + return; + } + + if (nStat == 98) + { + D3PlayFX((StaticSound[kSound47] | 0x2000) | (RandomSize(2) << 9), nSprite); + return; + } + + ObjectList[nObject].nHealth -= (short)b; + if (ObjectList[nObject].nHealth > 0) { + return; + } + + if (nStat == kStatDestructibleSprite) + { + ExplodeScreen(nSprite); + } + else + { + ObjectList[nObject].nHealth = -(RandomSize(3) + 1); + } + + return; + } + + case 0x90000: + { + if (bx > -1) + { + seq_PlotSequence(a & 0xFFFF, bx, ObjectList[nObject].field_0, 1); + } + return; + } + + case 0xA0000: + { + if (ObjectList[nObject].nHealth > 0 && sprite[nSprite].cstat & 0x101 + && (nStat != kStatExplodeTarget + || sprite[nRadialSpr].statnum == 201 + || nRadialBullet != 3 && nRadialBullet > -1 + || sprite[nRadialSpr].statnum == kStatExplodeTrigger)) + { + int nDamage = runlist_CheckRadialDamage(nSprite); + if (nDamage <= 0) { + return; + } + + if (sprite[nSprite].statnum != kStat98) { + ObjectList[nObject].nHealth -= nDamage; + } + + if (sprite[nSprite].statnum == kStatExplodeTarget) + { + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + } + else if (sprite[nSprite].statnum != kStat98) + { + sprite[nSprite].xvel >>= 1; + sprite[nSprite].yvel >>= 1; + sprite[nSprite].zvel >>= 1; + } + + if (ObjectList[nObject].nHealth > 0) { + return; + } + + if (sprite[nSprite].statnum == kStatExplodeTarget) + { + ObjectList[nObject].nHealth = -1; + short ax = ObjectList[nObject].field_10; + + if (ax < 0 || ObjectList[ax].nHealth <= 0) { + return; + } + + ObjectList[ax].nHealth = -1; + } + else if (sprite[nSprite].statnum == kStatDestructibleSprite) + { + ObjectList[nObject].nHealth = 0; + + ExplodeScreen(nSprite); + } + else + { + ObjectList[nObject].nHealth = -(RandomSize(4) + 1); + } + } + + return; + } + + case 0x20000: + { + if (nStat == 97 || (!(sprite[nSprite].cstat & 0x101))) { + return; + } + + if (nStat != kStatExplodeTarget) { + Gravity(nSprite); + } + + // do animation + if (bx != -1) + { + ObjectList[nObject].field_0++; + if (ObjectList[nObject].field_0 >= SeqSize[bx]) { + ObjectList[nObject].field_0 = 0; + } + + sprite[nSprite].picnum = seq_GetSeqPicnum2(bx, ObjectList[nObject].field_0); + } + + if (ObjectList[nObject].nHealth >= 0) { + goto FUNCOBJECT_GOTO; + } + + ObjectList[nObject].nHealth++; + + if (ObjectList[nObject].nHealth) + { +FUNCOBJECT_GOTO: + if (nStat != kStatExplodeTarget) + { + int nMov = movesprite(nSprite, sprite[nSprite].xvel << 6, sprite[nSprite].yvel << 6, sprite[nSprite].zvel, 0, 0, CLIPMASK0); + + if (sprite[nSprite].statnum == kStatExplodeTrigger) { + sprite[nSprite].pal = 1; + } + + if (nMov & 0x20000) + { + sprite[nSprite].xvel -= sprite[nSprite].xvel >> 3; + sprite[nSprite].yvel -= sprite[nSprite].yvel >> 3; + } + + if (((nMov & 0xC000) > 0x8000) && ((nMov & 0xC000) == 0xC000)) + { + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + } + } + + return; + } + else + { + int var_18; + + // red branch + if ((nStat == kStatExplodeTarget) || (sprite[nSprite].z < sector[sprite[nSprite].sectnum].floorz)) + { + var_18 = 36; + } + else + { + var_18 = 34; + } + + AddFlash(sprite[nSprite].sectnum, sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, 128); + BuildAnim(-1, var_18, 0, sprite[nSprite].x, sprite[nSprite].y, sector[sprite[nSprite].sectnum].floorz, sprite[nSprite].sectnum, 240, 4); + +// int edi = nSprite | 0x4000; + + if (nStat == kStatExplodeTrigger) + { + for (int i = 4; i < 8; i++) { + BuildCreatureChunk(nSprite | 0x4000, seq_GetSeqPicnum(kSeqFirePot, (i >> 2) + 1, 0)); + } + + runlist_RadialDamageEnemy(nSprite, 200, 20); + } + else if (nStat == kStatExplodeTarget) + { + for (int i = 0; i < 8; i++) { + BuildCreatureChunk(nSprite | 0x4000, seq_GetSeqPicnum(kSeqFirePot, (i >> 1) + 3, 0)); + } + } + + if (levelnum <= 20 || nStat != kStatExplodeTrigger) + { + runlist_SubRunRec(sprite[nSprite].owner); + runlist_SubRunRec(ObjectList[nObject].field_4); + + mydeletesprite(nSprite); + return; + } + else + { + StartRegenerate(nSprite); + ObjectList[nObject].nHealth = 120; + + sprite[nSprite].x = sprite[ObjectList[nObject].field_10].x; + sprite[nSprite].y = sprite[ObjectList[nObject].field_10].y; + sprite[nSprite].z = sprite[ObjectList[nObject].field_10].z; + + mychangespritesect(nSprite, sprite[ObjectList[nObject].field_10].sectnum); + return; + } + } + } + } +} + +void BuildDrip(int nSprite) +{ + if (nDrips >= kMaxDrips) { + I_Error("Too many drips!\n"); + } + + sDrip[nDrips].nSprite = nSprite; + sDrip[nDrips].field_2 = RandomSize(8) + 90; + + nDrips++; + + sprite[nSprite].cstat = 0x8000u; +} + +void DoDrips() +{ + int i; + + for (i = 0; i < nDrips; i++) + { + sDrip[i].field_2--; + if (sDrip[i].field_2 <= 0) + { + short nSprite = sDrip[i].nSprite; + + short nSeqOffset = SeqOffsets[kSeqDrips]; + + if (!(SectFlag[sprite[nSprite].sectnum] & kSectLava)) { + nSeqOffset++; + } + + seq_MoveSequence(nSprite, nSeqOffset, RandomSize(2) % SeqSize[nSeqOffset]); + + sDrip[i].field_2 = RandomSize(8) + 90; + } + } + + for (i = 0; i < nBobs; i++) + { + sBob[i].field_2 += 4; + + int edx = Sin(sBob[i].field_2 << 3) >> 4; + short nSector = sBob[i].nSector; + + if (sBob[i].field_3) + { + sector[nSector].ceilingz = edx + sBob[i].z; + } + else + { + int nFloorZ = sector[nSector].floorz; + + sector[nSector].floorz = edx + sBob[i].z; + + MoveSectorSprites(nSector, sector[nSector].floorz - nFloorZ); + } + } +} + +void SnapBobs(short nSectorA, short nSectorB) +{ + int ecx = -1; + int ebx = ecx; +// int var_14 = nSector; +// int edi = edx; + + for (int i = 0; i < nBobs; i++) + { + int esi = sBob[i].nSector; + + if (esi != nSectorA) + { + if (nSectorB != esi) + continue; + + esi = ebx; + ecx = i; + } + else + { + esi = ecx; + ebx = i; + } + + if (esi != -1) { + break; + } + } + + if (ecx <= -1) { + return; + } + + if (ebx <= -1) { + return; + } + + sBob[ecx].field_2 = sBob[ebx].field_2; +} + +void AddSectorBob(int nSector, int nHitag, int bx) +{ + if (nBobs >= kMaxBobs) { + I_Error("Too many bobs!\n"); + } + + sBob[nBobs].field_3 = bx; + + int z; + + if (bx == 0) { + z = sector[nSector].floorz; + } + else { + z = sector[nSector].ceilingz; + } + + sBob[nBobs].z = z; + sBob[nBobs].field_2 = nHitag << 4; + sBobID[nBobs] = nHitag; + + sBob[nBobs].nSector = nSector; + + SectFlag[nSector] |= 0x0010; + + nBobs++; +} + +// Confirmed 100% correct with original .exe +int FindTrail(int nVal) +{ + for (int i = 0; i < nTrails; i++) + { + if (sTrail[i].field_2 == nVal) + return i; + } + + sTrail[nTrails].field_2 = nVal; + sTrail[nTrails].field_0 = -1; + + return nTrails++; +} + +// ok ? +void ProcessTrailSprite(int nSprite, int nLotag, int nHitag) +{ + if (nTrailPoints >= 100) { + I_Error("Too many trail point sprites (900-949)... increase MAX_TRAILPOINTS\n"); + } + + short nPoint = nTrailPoints; + nTrailPoints++; + + sTrailPoint[nPoint].x = sprite[nSprite].x; + sTrailPoint[nPoint].y = sprite[nSprite].y; + + int nTrail = FindTrail(nHitag); + + int var_14 = nLotag - 900; + + nTrailPointVal[nPoint] = var_14; + + int field0 = sTrail[nTrail].field_0; + + if (field0 == -1) + { + sTrail[nTrail].field_0 = nPoint; + sTrail[nTrail].field_4 = nPoint; + + nTrailPointNext[nPoint] = -1; + nTrailPointPrev[nPoint] = -1; + } + else + { + int ecx = -1; + + while (field0 != -1) + { + if (nTrailPointVal[field0] > var_14) + { + nTrailPointPrev[nPoint] = nTrailPointPrev[field0]; + nTrailPointPrev[field0] = nPoint; + nTrailPointNext[nPoint] = field0; + + if (field0 == sTrail[nTrail].field_0) { + sTrail[nTrail].field_0 = nPoint; + } + + break; + } + + ecx = field0; + field0 = nTrailPointNext[field0]; + } + + if (field0 == -1) + { + nTrailPointNext[ecx] = nPoint; + nTrailPointPrev[nPoint] = ecx; + nTrailPointNext[nPoint] = -1; + sTrail[nTrail].field_4 = nPoint; + } + } + + mydeletesprite(nSprite); +} + +// ok? +void AddMovingSector(int nSector, int edx, int ebx, int ecx) +{ + if (nMoveSects >= kMaxMoveSects) { + I_Error("Too many moving sectors\n"); + } + + CreatePushBlock(nSector); + + short nTrail = FindTrail(ebx); + + sMoveDir[nMoveSects] = 1; + + MoveSect *pMoveSect = &sMoveSect[nMoveSects]; + nMoveSects++; + + pMoveSect->nTrail = nTrail; + pMoveSect->nTrailPoint = -1; + pMoveSect->field_8 = -1; + pMoveSect->field_6 = ecx; + pMoveSect->field_10 = (edx / 1000) + 1; + pMoveSect->nSector = nSector; + + if (ecx & 8) + { + pMoveSect->field_14 = runlist_AllocChannel(ebx % 1000); + } + else + { + pMoveSect->field_14 = -1; + } + + sector[nSector].floorstat |= 0x40; +} + +void DoMovingSects() +{ + for (int i = 0; i < nMoveSects; i++) + { + if (sMoveSect[i].nSector == -1) { + continue; + } + + if (sMoveSect[i].field_14 != -1 && !sRunChannels[sMoveSect[i].field_14].c) { + continue; + } + + short nSector = sMoveSect[i].nSector; + short nBlock = sector[nSector].extra; + + BlockInfo *pBlockInfo = &sBlockInfo[nBlock]; + + if (sMoveSect[i].nTrailPoint == -1) + { + if (sMoveSect[i].field_6 & 0x20) + { + runlist_ChangeChannel(sMoveSect[i].field_14, 0); + } + + short ax; + + if (sMoveSect[i].field_6 & 0x10) + { + sMoveDir[i] = -sMoveDir[i]; + if (sMoveDir[i] > 0) + { + ax = sTrail[sMoveSect[i].nTrail].field_0; + } + else + { + ax = sTrail[sMoveSect[i].nTrail].field_4; + } + } + else + { + ax = sTrail[sMoveSect[i].nTrail].field_0; + } + + sMoveSect[i].nTrailPoint = ax; + } + + short nTrail = sMoveSect[i].nTrailPoint; +// TrailPoint *pTrail = &sTrailPoint[nTrail]; + + // loc_23872: + int nAngle = GetMyAngle(sTrailPoint[nTrail].x - pBlockInfo->x, sTrailPoint[nTrail].y - pBlockInfo->y); + + int nXVel = (Sin(nAngle + 512) << 4) * sMoveSect[i].field_10; + int nYVel = (Sin(nAngle) << 4) * sMoveSect[i].field_10; + + int ebx = (sTrailPoint[nTrail].x - pBlockInfo->x) << 14; + + int eax = nXVel; + if (eax < 0) { + eax = -eax; + } + + int edx = eax; + eax = ebx; + + int ecx = (sTrailPoint[nTrail].y - pBlockInfo->y) << 14; + + if (eax < 0) { + eax = -eax; + } + + // loc_238EC: + if (edx <= eax) + { + eax = nYVel; + if (eax < 0) { + eax = -eax; + } + + edx = eax; + eax = ecx; + + if (eax < 0) { + eax = -eax; + } + + if (edx > eax) + { + // loc_23908: + nYVel = ecx; + nXVel = ebx; + + if (sMoveDir[i] > 0) + { + sMoveSect[i].nTrailPoint = nTrailPointNext[sMoveSect[i].nTrailPoint]; + } + else + { + sMoveSect[i].nTrailPoint = nTrailPointPrev[sMoveSect[i].nTrailPoint]; + } + } + } + else + { + // repeat of code from loc_23908 + nYVel = ecx; + nXVel = ebx; + + if (sMoveDir[i] > 0) + { + sMoveSect[i].nTrailPoint = nTrailPointNext[sMoveSect[i].nTrailPoint]; + } + else + { + sMoveSect[i].nTrailPoint = nTrailPointPrev[sMoveSect[i].nTrailPoint]; + } + } + + // loc_2393A: + if (sMoveSect[i].field_8 != -1) + { + MoveSector(sMoveSect[i].field_8, -1, &nXVel, &nYVel); + } + + int var_2C = nXVel; + int var_30 = nYVel; + + MoveSector(nSector, -1, &nXVel, &nYVel); + + if (nXVel != var_2C || nYVel != var_30) + { + MoveSector(sMoveSect[i].field_8, -1, &var_2C, &var_30); + MoveSector(sMoveSect[i].field_8, -1, &nXVel, &nYVel); + } + } +} + +void PostProcess() +{ + int i, j; + + for (i = 0; i < nMoveSects; i++) + { + int nTrail = sMoveSect[i].nTrail; + sMoveSect[i].nTrailPoint = sTrail[nTrail].field_0; + + if (sMoveSect[i].field_6 & 0x40) { + runlist_ChangeChannel(sMoveSect[i].field_14, 1); + } + + short nSector = sMoveSect[i].nSector; + + if (SectFlag[nSector] & kSectUnderwater) + { + sector[nSector].ceilingstat |= 0x40; + sector[nSector].floorstat &= 0xBFFF; + + for (j = 0; j < nMoveSects; j++) + { + if (j != i && sMoveSect[i].nTrail == sMoveSect[j].nTrail) + { + sMoveSect[j].field_8 = sMoveSect[i].nSector; + + SnapSectors(sMoveSect[j].nSector, sMoveSect[i].nSector, 0); + sMoveSect[i].nSector = -1; + } + } + } + } + + for (i = 0; i < nBobs; i++) + { + if (sBob[i].field_3 == 0) + { + int bobID = sBobID[i]; + + for (j = 0; j < nBobs; j++) + { + if (j != i) + { + if (sBob[i].field_3 != 0 && sBobID[j] == bobID) { + SnapSectors(i, j, 0); + } + } + } + } + } + + if (levelnew != kMap20) + { + // esi is i + for (i = 0; i < numsectors; i++) + { + int var_20 = 30000; + + if (SectSpeed[i] && SectDepth[i] && !(SectFlag[i] & kSectLava)) + { + SectSoundSect[i] = i; + SectSound[i] = StaticSound[kSound43]; + } + else + { + // ebp and ecx are j + for (j = 0; j < numsectors; j++) + { + // loc_23CA6: + + if (i != j && SectSpeed[j] && !(SectFlag[i] & kSectLava)) + { + int xVal = wall[sector[i].wallptr].x - wall[sector[j].wallptr].x; + if (xVal < 0) { + xVal = -xVal; + } + + int yVal = wall[sector[i].wallptr].x - wall[sector[j].wallptr].x; + if (yVal < 0) { + yVal = -yVal; + } + + if (xVal < 15000 && yVal < 15000 && (xVal + yVal < var_20)) + { + var_20 = xVal + yVal; + SectSoundSect[i] = j; + SectSound[i] = StaticSound[kSound43]; + } + } + } + } + } + } + else // nMap == kMap20) + { + // int var_24 = 0; + int ebp = 0; + + for (i = 0; i < numsectors; i++) + { + SectSoundSect[i] = i; + SectSound[i] = StaticSound[kSound62]; + + int startwall = sector[ebp].wallptr; + int endwall = sector[ebp].wallptr + sector[ebp].wallnum; + + int nWall = startwall; + + while (nWall < endwall) + { + if (wall[nWall].picnum == kTile3603) + { + wall[nWall].pal = 1; + int nSprite = insertsprite(i, 407); + sprite[nSprite].cstat = 0x8000; + } + + nWall++; + } + } + + for (i = 0; i < kMaxSprites; i++) + { + if (sprite[i].statnum < kMaxStatus && sprite[i].picnum == kTile3603) + { + changespritestat(i, 407); + sprite[i].pal = 1; + } + } + } + + // esi is i + for (i = 0; i < ObjectCount; i++) + { + int nObjectSprite = ObjectList[i].nSprite; + + if (sprite[nObjectSprite].statnum == kStatExplodeTarget) + { + if (!ObjectList[i].field_10) { + ObjectList[i].field_10 = -1; + } + else + { + int edi = ObjectList[i].field_10; + ObjectList[i].field_10 = -1; + + // ecx, ebx is j + for (j = 0; j < ObjectCount; j++) + { + if (i != j && sprite[ObjectList[j].nSprite].statnum == kStatExplodeTarget && edi == ObjectList[j].field_10) + { + ObjectList[i].field_10 = j; + ObjectList[j].field_10 = i; + } + } + } + } + } +} + + +static SavegameHelper sgh("objects", + SA(sTrap), + SA(sBob), + SA(sTrail), + SA(sTrailPoint), + SA(Elevator), + SA(ObjectList), + SA(sMoveSect), + SA(SlideData), + SA(sMoveDir), + SA(WallFace), + SA(SlideData2), + SA(PointList), + SA(nTrapInterval), + SA(sBobID), + SA(PointFree), + SA(SlideFree), + SA(nTrailPointVal), + SA(nTrailPointPrev), + SA(nTrailPointNext), + SA(sDrip), + SV(PointCount), + SV(SlideCount), + SV(ElevCount), + SV(WallFaceCount), + SV(lFinaleStart), + SV(nTrailPoints), + SV(nEnergyBlocks), + SV(nMoveSects), + SV(nFinaleStage), + SV(nTrails), + SV(nTraps), + SV(nFinaleSpr), + SV(ObjectCount), + SV(nDrips), + SV(nBobs), + SV(nDronePitch), + SV(nSmokeSparks), + nullptr); + +END_PS_NS diff --git a/source/exhumed/src/object.h b/source/exhumed/src/object.h new file mode 100644 index 000000000..38d071eb9 --- /dev/null +++ b/source/exhumed/src/object.h @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __object_h__ +#define __object_h__ + +BEGIN_PS_NS + +#define kMaxPoints 1024 +#define kMaxSlides 128 +#define kMaxElevs 1024 + +enum kStatus +{ + kStatDestructibleSprite = 97, + kStat98, + kStatExplodeTrigger = 141, + kStatExplodeTarget = 152 +}; + +extern short nSmokeSparks; +extern short nDronePitch; +extern int lFinaleStart; +extern short nFinaleSpr; + +void InitObjects(); +void InitElev(); +void InitPoint(); +void InitSlide(); +void InitWallFace(); +void DoDrips(); +void DoMovingSects(); +void DoFinale(); +void PostProcess(); + +void FuncElev(int, int, int); +void FuncWallFace(int, int, int); +void FuncSlide(int, int, int); +void FuncObject(int, int, int); +void FuncTrap(int, int, int); +void FuncEnergyBlock(int, int, int); +void FuncSpark(int, int, int); + +void SnapBobs(short nSectorA, short nSectorB); + +short FindWallSprites(short nSector); + +void AddMovingSector(int nSector, int edx, int ebx, int ecx); + +int BuildWallSprite(int nSector); + +void ProcessTrailSprite(int nSprite, int nLotag, int nHitag); + +void AddSectorBob(int nSector, int nHitag, int bx); + +int BuildObject(short nSprite, int nOjectType, int nHitag); + +int BuildArrow(int nSprite, int nVal); + +int BuildFireBall(int nSprite, int a, int b); + +void BuildDrip(int nSprite); + +int BuildEnergyBlock(short nSector); + +int BuildElevC(int arg1, int nChannel, int nSector, int nWallSprite, int arg5, int arg6, int nCount, ...); +int BuildElevF(int nChannel, int nSector, int nWallSprite, int arg_4, int arg_5, int nCount, ...); + +int BuildWallFace(short nChannel, short nWall, short nCount, ...); + +int BuildSlide(int nChannel, int edx, int ebx, int ecx, int arg1, int arg2, int arg3); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/osdcmds.cpp b/source/exhumed/src/osdcmds.cpp new file mode 100644 index 000000000..e53348876 --- /dev/null +++ b/source/exhumed/src/osdcmds.cpp @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 2010 EDuke32 developers and contributors + +This file is part of EDuke32. + +EDuke32 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 "build.h" +#include "common.h" +#include "exhumed.h" +#include "osdcmds.h" +#include "view.h" + +BEGIN_PS_NS + + + +int32_t registerosdcommands(void) +{ + //if (VOLUMEONE) + // OSD_RegisterFunction("changelevel","changelevel : warps to the given level", osdcmd_changelevel); + //else + //{ + // OSD_RegisterFunction("changelevel","changelevel : warps to the given level", osdcmd_changelevel); + // OSD_RegisterFunction("map","map : loads the given user map", osdcmd_map); + // OSD_RegisterFunction("demo","demo : starts the given demo", osdcmd_demo); + //} + + //OSD_RegisterFunction("cmenu","cmenu <#>: jumps to menu", osdcmd_cmenu); + //OSD_RegisterFunction("crosshaircolor","crosshaircolor: changes the crosshair color", osdcmd_crosshaircolor); + + + //OSD_RegisterFunction("give","give : gives requested item", osdcmd_give); + //OSD_RegisterFunction("god","god: toggles god mode", osdcmd_god); + //OSD_RegisterFunction("activatecheat","activatecheat : activates a cheat code", osdcmd_activatecheat); + + //OSD_RegisterFunction("restartmap", "restartmap: restarts the current map", osdcmd_restartmap); + //OSD_RegisterFunction("restartsound","restartsound: reinitializes the sound system",osdcmd_restartsound); + + //OSD_RegisterFunction("spawn","spawn [palnum] [cstat] [ang] [x y z]: spawns a sprite with the given properties",osdcmd_spawn); + + return 0; +} + + +END_PS_NS diff --git a/source/exhumed/src/osdcmds.h b/source/exhumed/src/osdcmds.h new file mode 100644 index 000000000..b028441e1 --- /dev/null +++ b/source/exhumed/src/osdcmds.h @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 2010 EDuke32 developers and contributors + +This file is part of EDuke32. + +EDuke32 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. +*/ +//------------------------------------------------------------------------- + +#ifndef osdcmds_h_ +#define osdcmds_h_ + +BEGIN_PS_NS + + +int32_t registerosdcommands(void); +void onvideomodechange(int32_t newmode); +void GAME_onshowosd(int shown); +void GAME_clearbackground(int numcols, int numrows); + +//extern float r_ambientlight,r_ambientlightrecip; + +extern const char *const ConsoleButtons[]; + +//extern uint32_t cl_cheatmask; + +END_PS_NS + +#endif // osdcmds_h_ + diff --git a/source/exhumed/src/paul.cpp b/source/exhumed/src/paul.cpp new file mode 100644 index 000000000..99d198dd4 --- /dev/null +++ b/source/exhumed/src/paul.cpp @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------- +/* +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 "paul.h" diff --git a/source/exhumed/src/paul.h b/source/exhumed/src/paul.h new file mode 100644 index 000000000..c8a4c05d6 --- /dev/null +++ b/source/exhumed/src/paul.h @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __paul_h__ +#define __paul_h__ + + + +#endif diff --git a/source/exhumed/src/player.cpp b/source/exhumed/src/player.cpp new file mode 100644 index 000000000..f099d9026 --- /dev/null +++ b/source/exhumed/src/player.cpp @@ -0,0 +1,3168 @@ +//------------------------------------------------------------------------- +/* +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 "player.h" +#include "runlist.h" +#include "exhumed.h" +#include "names.h" +#include "gun.h" +#include "items.h" +#include "engine.h" +#include "move.h" +#include "sequence.h" +#include "lighting.h" +#include "view.h" +#include "bubbles.h" +#include "random.h" +#include "ra.h" +#include "ps_input.h" +#include "light.h" +#include "status.h" +#include "sound.h" +#include "init.h" +#include "move.h" +#include "trigdat.h" +#include "anims.h" +#include "grenade.h" +#include "menu.h" +#include "cd.h" +#include "map.h" +#include "sound.h" +#include "textures.h" +#include +#include +#include + +BEGIN_PS_NS + +struct PlayerSave +{ + int x; + int y; + int z; + short nSector; + short nAngle; +}; + +fix16_t lPlayerXVel = 0; +fix16_t lPlayerYVel = 0; +fix16_t nPlayerDAng = 0; +short obobangle = 0, bobangle = 0; +short bPlayerPan = 0; +short bLockPan = 0; +bool g_MyAimMode; + +static actionSeq ActionSeq[] = { + {18, 0}, {0, 0}, {9, 0}, {27, 0}, {63, 0}, + {72, 0}, {54, 0}, {45, 0}, {54, 0}, {81, 0}, + {90, 0}, {99, 0}, {108, 0}, {8, 0}, {0, 0}, + {139, 0}, {117, 1}, {119, 1}, {120, 1}, {121, 1}, + {122, 1} +}; + +static short nHeightTemplate[] = { 0, 0, 0, 0, 0, 0, 7, 7, 7, 9, 9, 9, 9, 0, 0, 0, 0, 0, 0, 0, 0 }; + +short nActionEyeLevel[] = { + -14080, -14080, -14080, -14080, -14080, -14080, -8320, + -8320, -8320, -8320, -8320, -8320, -8320, -14080, + -14080, -14080, -14080, -14080, -14080, -14080, -14080 +}; + +uint16_t nGunLotag[] = { 52, 53, 54, 55, 56, 57 }; +uint16_t nGunPicnum[] = { 57, 488, 490, 491, 878, 899, 3455 }; + +int16_t nItemText[] = { + -1, -1, -1, -1, -1, -1, 18, 20, 19, 13, -1, 10, 1, 0, 2, -1, 3, + -1, 4, 5, 9, 6, 7, 8, -1, 11, -1, 13, 12, 14, 15, -1, 16, 17, + -1, -1, -1, 21, 22, -1, -1, -1, -1, -1, -1, 23, 24, 25, 26, 27, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; + + +int nLocalPlayer = 0; + +short nBreathTimer[kMaxPlayers]; +short nPlayerSwear[kMaxPlayers]; +short nPlayerPushSect[kMaxPlayers]; +short nDeathType[kMaxPlayers]; +short nPlayerScore[kMaxPlayers]; +short nPlayerColor[kMaxPlayers]; +int nPlayerDY[kMaxPlayers]; +int nPlayerDX[kMaxPlayers]; +char playerNames[kMaxPlayers][11]; +short nPistolClip[kMaxPlayers]; +int nXDamage[kMaxPlayers]; +int nYDamage[kMaxPlayers]; +short nDoppleSprite[kMaxPlayers]; +short namelen[kMaxPlayers]; +short nPlayerOldWeapon[kMaxPlayers]; +short nPlayerClip[kMaxPlayers]; +short nPlayerPushSound[kMaxPlayers]; +short nTauntTimer[kMaxPlayers]; +short nPlayerTorch[kMaxPlayers]; +uint16_t nPlayerWeapons[kMaxPlayers]; // each set bit represents a weapon the player has +short nPlayerLives[kMaxPlayers]; +short nPlayerItem[kMaxPlayers]; +Player PlayerList[kMaxPlayers]; +short nPlayerInvisible[kMaxPlayers]; +short nPlayerDouble[kMaxPlayers]; +short nPlayerViewSect[kMaxPlayers]; +short nPlayerFloorSprite[kMaxPlayers]; +PlayerSave sPlayerSave[kMaxPlayers]; +int totalvel[kMaxPlayers] = { 0 }; +int16_t eyelevel[kMaxPlayers], oeyelevel[kMaxPlayers]; +short nNetStartSprite[kMaxPlayers] = { 0 }; + +short nStandHeight; + +short nPlayerGrenade[kMaxPlayers]; +short nGrenadePlayer[50]; + +short word_D282A[32]; + + +short PlayerCount; + +short nNetStartSprites; +short nCurStartSprite; + +/* +typedef struct +{ +fixed dx; +fixed dy; +fixed dz; +fixed dyaw; +fixed dpitch; +fixed droll; +} ControlInfo; +*/ + +void PlayerInterruptKeys() +{ + ControlInfo info; + memset(&info, 0, sizeof(ControlInfo)); // this is done within CONTROL_GetInput() anyway + CONTROL_GetInput(&info); + D_ProcessEvents(); + + if (PlayerList[nLocalPlayer].nHealth == 0) + { + lPlayerYVel = 0; + lPlayerXVel = 0; + nPlayerDAng = 0; + return; + } + + // JBF: Run key behaviour is selectable + int const playerRunning = G_CheckAutorun(buttonMap.ButtonDown(gamefunc_Run)); + int const turnAmount = playerRunning ? 12 : 8; + int const keyMove = playerRunning ? 12 : 6; + constexpr int const analogTurnAmount = 12; + constexpr int const analogExtent = 32767; // KEEPINSYNC sdlayer.cpp + int fvel = 0, svel = 0; + fix16_t q16avel = 0, q16horz = 0; + + if (buttonMap.ButtonDown(gamefunc_Strafe)) + { + static int strafeyaw; + + svel = -(info.mousex + strafeyaw) >> 6; + strafeyaw = (info.mousex + strafeyaw) % 64; + + svel -= info.dyaw * keyMove / analogExtent; + } + else + { + q16avel = fix16_div(fix16_from_int(info.mousex), F16(32)); + q16avel += fix16_from_int(info.dyaw) / analogExtent * (analogTurnAmount << 1); + } + + g_MyAimMode = in_mousemode || buttonMap.ButtonDown(gamefunc_Mouse_Aiming); + + if (g_MyAimMode) + q16horz = fix16_div(fix16_from_int(info.mousey), F16(64)); + else + fvel = -(info.mousey >> 6); + + if (!in_mouseflip) q16horz = -q16horz; + + q16horz -= fix16_from_int(info.dpitch) / analogExtent * analogTurnAmount; + svel -= info.dx * keyMove / analogExtent; + fvel -= info.dz * keyMove / analogExtent; + + if (buttonMap.ButtonDown(gamefunc_Strafe)) + { + if (buttonMap.ButtonDown(gamefunc_Turn_Left)) + svel -= -keyMove; + + if (buttonMap.ButtonDown(gamefunc_Turn_Right)) + svel -= keyMove; + } + else + { + static int turn = 0; + static int counter = 0; + // normal, non strafing movement + if (buttonMap.ButtonDown(gamefunc_Turn_Left)) + { + turn -= 2; + + if (turn < -turnAmount) + turn = -turnAmount; + } + else if (buttonMap.ButtonDown(gamefunc_Turn_Right)) + { + turn += 2; + + if (turn > turnAmount) + turn = turnAmount; + } + + if (turn < 0) + { + turn++; + if (turn > 0) + turn = 0; + } + + if (turn > 0) + { + turn--; + if (turn < 0) + turn = 0; + } + + //if ((counter++) % 4 == 0) // what was this for??? + q16avel += fix16_from_int(turn); + + } + + if (buttonMap.ButtonDown(gamefunc_Strafe_Left)) + svel += keyMove; + + if (buttonMap.ButtonDown(gamefunc_Strafe_Right)) + svel += -keyMove; + + if (buttonMap.ButtonDown(gamefunc_Move_Forward)) + fvel += keyMove; + + if (buttonMap.ButtonDown(gamefunc_Move_Backward)) + fvel += -keyMove; + + fvel = clamp(fvel, -12, 12); + svel = clamp(svel, -12, 12); + + nPlayerDAng += q16avel; + + inita &= kAngleMask; + + lPlayerXVel += fvel * Cos(inita) + svel * Sin(inita); + lPlayerYVel += fvel * Sin(inita) - svel * Cos(inita); + + lPlayerXVel -= (lPlayerXVel >> 5) + (lPlayerXVel >> 6); + lPlayerYVel -= (lPlayerYVel >> 5) + (lPlayerYVel >> 6); + + // A horiz diff of 128 equal 45 degrees, + // so we convert horiz to 1024 angle units + + float horizAngle = atan2f(nVertPan[nLocalPlayer] - F16(92), F16(128)) * (512.f / fPI) + fix16_to_float(q16horz); + nVertPan[nLocalPlayer] = fix16_clamp(F16(92) + Blrintf(F16(128) * tanf(horizAngle * (fPI / 512.f))), F16(0), F16(184)); + +} + +void RestoreSavePoint(int nPlayer, int *x, int *y, int *z, short *nSector, short *nAngle) +{ + *x = sPlayerSave[nPlayer].x; + *y = sPlayerSave[nPlayer].y; + *z = sPlayerSave[nPlayer].z; + *nSector = sPlayerSave[nPlayer].nSector; + *nAngle = sPlayerSave[nPlayer].nAngle; +} + +void SetSavePoint(int nPlayer, int x, int y, int z, short nSector, short nAngle) +{ + sPlayerSave[nPlayer].x = x; + sPlayerSave[nPlayer].y = y; + sPlayerSave[nPlayer].z = z; + sPlayerSave[nPlayer].nSector = nSector; + sPlayerSave[nPlayer].nAngle = nAngle; +} + +void feebtag(int x, int y, int z, int nSector, short *nSprite, int nVal2, int nVal3) +{ + *nSprite = -1; + + int startwall = sector[nSector].wallptr; + + int nWalls = sector[nSector].wallnum; + + int var_20 = nVal2 & 2; + int var_14 = nVal2 & 1; + + while (1) + { + if (nSector != -1) + { + short i = headspritesect[nSector]; + + while (i != -1) + { + short nNextSprite = nextspritesect[i]; + short nStat = sprite[i].statnum; + + if (nStat >= 900 && !(sprite[i].cstat & 0x8000)) + { + int xDiff = sprite[i].x - x; + int yDiff = sprite[i].y - y; + int zDiff = sprite[i].z - z; + + if (zDiff < 5120 && zDiff > -25600) + { + int theSqrt = ksqrt(xDiff * xDiff + yDiff * yDiff); + + if (theSqrt < nVal3 && (nStat != 950 && nStat != 949 || !(var_14 & 1)) && (nStat != 912 && nStat != 913 || !(var_20 & 2))) + { + nVal3 = theSqrt; + *nSprite = i; + } + } + } + + i = nNextSprite; + } + } + + nWalls--; + if (nWalls < -1) + return; + + nSector = wall[startwall].nextsector; + startwall++; + } +} + +void InitPlayer() +{ + for (int i = 0; i < kMaxPlayers; i++) { + PlayerList[i].nSprite = -1; + } +} + +void InitPlayerKeys(short nPlayer) +{ + PlayerList[nPlayer].keys = 0; +} + +void InitPlayerInventory(short nPlayer) +{ + memset(&PlayerList[nPlayer], 0, sizeof(Player)); + + nPlayerItem[nPlayer] = -1; + nPlayerSwear[nPlayer] = 4; + + ResetPlayerWeapons(nPlayer); + + nPlayerLives[nPlayer] = kDefaultLives; + + PlayerList[nPlayer].nSprite = -1; + PlayerList[nPlayer].nRun = -1; + + nPistolClip[nPlayer] = 6; + nPlayerClip[nPlayer] = 100; + + PlayerList[nPlayer].nCurrentWeapon = 0; + + if (nPlayer == nLocalPlayer) { + nMapMode = 0; + } + + nPlayerScore[nPlayer] = 0; + + auto pixels = tilePtr(kTile3571 + nPlayer); + + nPlayerColor[nPlayer] = pixels[tilesiz[nPlayer + kTile3571].x * tilesiz[nPlayer + kTile3571].y / 2]; +} + +// done +short GetPlayerFromSprite(short nSprite) +{ + return RunData[sprite[nSprite].owner].nVal; +} + +void RestartPlayer(short nPlayer) +{ + int nSprite = PlayerList[nPlayer].nSprite; + int nDopSprite = nDoppleSprite[nPlayer]; + + int floorspr; + + if (nSprite > -1) + { + runlist_DoSubRunRec(sprite[nSprite].owner); + runlist_FreeRun(sprite[nSprite].lotag - 1); + + changespritestat(nSprite, 0); + + PlayerList[nPlayer].nSprite = -1; + + int nFloorSprite = nPlayerFloorSprite[nPlayer]; + if (nFloorSprite > -1) { + mydeletesprite(nFloorSprite); + } + + if (nDopSprite > -1) + { + runlist_DoSubRunRec(sprite[nDopSprite].owner); + runlist_FreeRun(sprite[nDopSprite].lotag - 1); + mydeletesprite(nDopSprite); + } + } + + nSprite = GrabBody(); + + mychangespritesect(nSprite, sPlayerSave[nPlayer].nSector); + changespritestat(nSprite, 100); + + assert(nSprite >= 0 && nSprite < kMaxSprites); + + int nDSprite = insertsprite(sprite[nSprite].sectnum, 100); + nDoppleSprite[nPlayer] = nDSprite; + + assert(nDSprite >= 0 && nDSprite < kMaxSprites); + + if (nTotalPlayers > 1) + { + int nNStartSprite = nNetStartSprite[nCurStartSprite]; + nCurStartSprite++; + + if (nCurStartSprite >= nNetStartSprites) { + nCurStartSprite = 0; + } + + sprite[nSprite].x = sprite[nNStartSprite].x; + sprite[nSprite].y = sprite[nNStartSprite].y; + sprite[nSprite].z = sprite[nNStartSprite].z; + mychangespritesect(nSprite, sprite[nNStartSprite].sectnum); + PlayerList[nPlayer].q16angle = fix16_from_int(sprite[nNStartSprite].ang&kAngleMask); + sprite[nSprite].ang = fix16_to_int(PlayerList[nPlayer].q16angle); + + floorspr = insertsprite(sprite[nSprite].sectnum, 0); + assert(floorspr >= 0 && floorspr < kMaxSprites); + + sprite[floorspr].x = sprite[nSprite].x; + sprite[floorspr].y = sprite[nSprite].y; + sprite[floorspr].z = sprite[nSprite].z; + sprite[floorspr].yrepeat = 64; + sprite[floorspr].xrepeat = 64; + sprite[floorspr].cstat = 32; + sprite[floorspr].picnum = nPlayer + kTile3571; + } + else + { + sprite[nSprite].x = sPlayerSave[nPlayer].x; + sprite[nSprite].y = sPlayerSave[nPlayer].y; + sprite[nSprite].z = sector[sPlayerSave[nPlayer].nSector].floorz; + PlayerList[nPlayer].q16angle = fix16_from_int(sPlayerSave[nPlayer].nAngle&kAngleMask); + sprite[nSprite].ang = fix16_to_int(PlayerList[nPlayer].q16angle); + + floorspr = -1; + } + + PlayerList[nPlayer].opos = sprite[nSprite].pos; + PlayerList[nPlayer].q16oangle = PlayerList[nPlayer].q16angle; + + nPlayerFloorSprite[nPlayer] = floorspr; + + sprite[nSprite].cstat = 0x101; + sprite[nSprite].shade = -12; + sprite[nSprite].clipdist = 58; + sprite[nSprite].pal = 0; + sprite[nSprite].xrepeat = 40; + sprite[nSprite].yrepeat = 40; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].picnum = seq_GetSeqPicnum(kSeqJoe, 18, 0); + + int nHeight = GetSpriteHeight(nSprite); + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + + nStandHeight = nHeight; + + sprite[nSprite].hitag = 0; + sprite[nSprite].extra = -1; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + + sprite[nDSprite].x = sprite[nSprite].x; + sprite[nDSprite].y = sprite[nSprite].y; + sprite[nDSprite].z = sprite[nSprite].z; + sprite[nDSprite].xrepeat = sprite[nSprite].xrepeat; + sprite[nDSprite].yrepeat = sprite[nSprite].yrepeat; + sprite[nDSprite].xoffset = 0; + sprite[nDSprite].yoffset = 0; + sprite[nDSprite].shade = sprite[nSprite].shade; + sprite[nDSprite].ang = sprite[nSprite].ang; + sprite[nDSprite].cstat = sprite[nSprite].cstat; + + sprite[nDSprite].lotag = runlist_HeadRun() + 1; + + PlayerList[nPlayer].nAction = 0; + PlayerList[nPlayer].nHealth = 800; // TODO - define + + if (nNetPlayerCount) { + PlayerList[nPlayer].nHealth = 1600; // TODO - define + } + + PlayerList[nPlayer].field_2 = 0; + PlayerList[nPlayer].nSprite = nSprite; + PlayerList[nPlayer].bIsMummified = kFalse; + + if (PlayerList[nPlayer].invincibility >= 0) { + PlayerList[nPlayer].invincibility = 0; + } + + nPlayerTorch[nPlayer] = 0; + PlayerList[nPlayer].nMaskAmount = 0; + + SetTorch(nPlayer, 0); + + nPlayerInvisible[nPlayer] = 0; + + PlayerList[nPlayer].bIsFiring = 0; + PlayerList[nPlayer].field_3FOUR = 0; + nPlayerViewSect[nPlayer] = sPlayerSave[nPlayer].nSector; + PlayerList[nPlayer].field_3A = 0; + + nPlayerDouble[nPlayer] = 0; + + PlayerList[nPlayer].nSeq = kSeqJoe; + + nPlayerPushSound[nPlayer] = -1; + + PlayerList[nPlayer].field_38 = -1; + + if (PlayerList[nPlayer].nCurrentWeapon == 7) { + PlayerList[nPlayer].nCurrentWeapon = PlayerList[nPlayer].field_3C; + } + + PlayerList[nPlayer].field_3C = 0; + PlayerList[nPlayer].nAir = 100; + airpages = 0; + + if (levelnum <= kMap20) + { + RestoreMinAmmo(nPlayer); + } + else + { + ResetPlayerWeapons(nPlayer); + PlayerList[nPlayer].nMagic = 0; + } + + nPlayerGrenade[nPlayer] = -1; + oeyelevel[nPlayer] = eyelevel[nPlayer] = -14080; + dVertPan[nPlayer] = 0; + + nTemperature[nPlayer] = 0; + + nYDamage[nPlayer] = 0; + nXDamage[nPlayer] = 0; + + PlayerList[nPlayer].q16ohoriz = PlayerList[nPlayer].q16horiz = nVertPan[nPlayer] = F16(92); + nDestVertPan[nPlayer] = F16(92); + nBreathTimer[nPlayer] = 90; + + nTauntTimer[nPlayer] = RandomSize(3) + 3; + + sprite[nDSprite].owner = runlist_AddRunRec(sprite[nDSprite].lotag - 1, nPlayer | 0xA0000); + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nPlayer | 0xA0000); + + if (PlayerList[nPlayer].nRun < 0) { + PlayerList[nPlayer].nRun = runlist_AddRunRec(NewRun, nPlayer | 0xA0000); + } + + BuildRa(nPlayer); + + if (nPlayer == nLocalPlayer) + { + nLocalSpr = nSprite; + nPlayerDAng = 0; + + SetMagicFrame(); + RestoreGreenPal(); + + bPlayerPan = 0; + bLockPan = 0; + } + + sprintf(playerNames[nPlayer], "JOE%d", nPlayer); + namelen[nPlayer] = strlen(playerNames[nPlayer]); + + totalvel[nPlayer] = 0; + + memset(&sPlayerInput[nPlayer], 0, sizeof(PlayerInput)); + sPlayerInput[nPlayer].nItem = -1; + + nDeathType[nPlayer] = 0; + nQuake[nPlayer] = 0; + + if (nPlayer == nLocalPlayer) { + SetHealthFrame(0); + } +} + +// done +int GrabPlayer() +{ + if (PlayerCount >= kMaxPlayers) { + return -1; + } + + return PlayerCount++; +} + +// checked OK on 26/03/2019 +void StartDeathSeq(int nPlayer, int nVal) +{ + FreeRa(nPlayer); + + short nSprite = PlayerList[nPlayer].nSprite; + PlayerList[nPlayer].nHealth = 0; + + short nLotag = sector[sprite[nSprite].sectnum].lotag; + + if (nLotag > 0) { + runlist_SignalRun(nLotag - 1, nPlayer | 0x70000); + } + + if (nPlayerGrenade[nPlayer] >= 0) + { + ThrowGrenade(nPlayer, 0, 0, 0, -10000); + } + else + { + if (nNetPlayerCount) + { + int nWeapon = PlayerList[nPlayer].nCurrentWeapon; + + if (nWeapon > kWeaponSword && nWeapon <= kWeaponRing) + { + short nSector = sprite[nSprite].sectnum; + if (SectBelow[nSector] > -1) { + nSector = SectBelow[nSector]; + } + + int nGunSprite = GrabBodyGunSprite(); + changespritesect(nGunSprite, nSector); + + sprite[nGunSprite].x = sprite[nSprite].x; + sprite[nGunSprite].y = sprite[nSprite].y; + sprite[nGunSprite].z = sector[nSector].floorz - 512; + + changespritestat(nGunSprite, nGunLotag[nWeapon] + 900); + + sprite[nGunSprite].picnum = nGunPicnum[nWeapon]; + + BuildItemAnim(nGunSprite); + } + } + } + + StopFiringWeapon(nPlayer); + + PlayerList[nPlayer].q16ohoriz = PlayerList[nPlayer].q16horiz = nVertPan[nPlayer] = F16(92); + oeyelevel[nPlayer] = eyelevel[nPlayer] = -14080; + nPlayerInvisible[nPlayer] = 0; + dVertPan[nPlayer] = 15; + + sprite[nSprite].cstat &= 0x7FFF; + + SetNewWeaponImmediate(nPlayer, -2); + + if (SectDamage[sprite[nSprite].sectnum] <= 0) + { + nDeathType[nPlayer] = nVal; + } + else + { + nDeathType[nPlayer] = 2; + } + + nVal *= 2; + + if (nVal || !(SectFlag[sprite[nSprite].sectnum] & kSectUnderwater)) + { + PlayerList[nPlayer].nAction = nVal + 17; + } + else { + PlayerList[nPlayer].nAction = 16; + } + + PlayerList[nPlayer].field_2 = 0; + + sprite[nSprite].cstat &= 0xFEFE; + + if (nTotalPlayers == 1) + { + short nLives = nPlayerLives[nPlayer]; + + if (nLives > 0) { + BuildStatusAnim((3 * (nLives - 1)) + 7, 0); + } + + if (levelnum > 0) { // if not on the training level + nPlayerLives[nPlayer]--; + } + + if (nPlayerLives[nPlayer] < 0) { + nPlayerLives[nPlayer] = 0; + } + } + + totalvel[nPlayer] = 0; + + if (nPlayer == nLocalPlayer) { + RefreshStatus(); + } +} + +int AddAmmo(int nPlayer, int nWeapon, int nAmmoAmount) +{ + if (!nAmmoAmount) { + nAmmoAmount = 1; + } + + short nCurAmmo = PlayerList[nPlayer].nAmmo[nWeapon]; + + if (nCurAmmo >= 300 && nAmmoAmount > 0) { + return 0; + } + + nAmmoAmount = nCurAmmo + nAmmoAmount; + if (nAmmoAmount > 300) { + nAmmoAmount = 300; + } + + PlayerList[nPlayer].nAmmo[nWeapon] = nAmmoAmount; + + if (nPlayer == nLocalPlayer) + { + if (nWeapon == nCounterBullet) { + SetCounter(nAmmoAmount); + } + } + + if (nWeapon == 1) + { + if (!nPistolClip[nPlayer]) { + nPistolClip[nPlayer] = 6; + } + } + + return 1; +} + +void SetPlayerMummified(int nPlayer, int bIsMummified) +{ + int nSprite = PlayerList[nPlayer].nSprite; + + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + + PlayerList[nPlayer].bIsMummified = bIsMummified; + + if (bIsMummified) + { + PlayerList[nPlayer].nAction = 13; + PlayerList[nPlayer].nSeq = kSeqMummy; + } + else + { + PlayerList[nPlayer].nAction = 0; + PlayerList[nPlayer].nSeq = kSeqJoe; + } + + PlayerList[nPlayer].field_2 = 0; +} + +void ShootStaff(int nPlayer) +{ + PlayerList[nPlayer].nAction = 15; + PlayerList[nPlayer].field_2 = 0; + PlayerList[nPlayer].nSeq = kSeqJoe; +} + +void PlayAlert(const char *str) +{ + StatusMessage(300, str); + PlayLocalSound(StaticSound[kSound63], 0); +} + +void DoKenTest() +{ + int nPlayerSprite = PlayerList[0].nSprite; + if ((unsigned int)nPlayerSprite >= kMaxSprites) + { + return; + } + int nSector = sprite[nPlayerSprite].sectnum; + if ((unsigned int)nSector >= kMaxSectors) + { + initprintf("DoKenTest: (unsigned int)nSector >= kMaxSectors\n"); + return; + } + + for (int i = headspritesect[nSector]; ; i = nextspritesect[i]) + { + if (i == -1) { + return; + } + + if (nextspritesect[i] == i) { + I_Error("ERROR in Ken's linked list!\n"); + } + } +} + +void FuncPlayer(int pA, int nDamage, int nRun) +{ + int var_48 = 0; + int var_40; + + short nPlayer = RunData[nRun].nVal; + assert(nPlayer >= 0 && nPlayer < kMaxPlayers); + + if (PlayerList[nPlayer].someNetVal == -1) + return; + + short nPlayerSprite = PlayerList[nPlayer].nSprite; + + short nDopple = nDoppleSprite[nPlayer]; + + short nAction = PlayerList[nPlayer].nAction; + short nActionB = PlayerList[nPlayer].nAction; + + int nMessage = pA & 0x7F0000; + + short nSprite2; + + PlayerList[nPlayer].opos = sprite[nPlayerSprite].pos; + PlayerList[nPlayer].q16oangle = PlayerList[nPlayer].q16angle; + PlayerList[nPlayer].q16ohoriz = PlayerList[nPlayer].q16horiz; + oeyelevel[nPlayer] = eyelevel[nPlayer]; + + switch (nMessage) + { + case 0x90000: + { + seq_PlotSequence(pA & 0xFFFF, SeqOffsets[PlayerList[nPlayer].nSeq] + ActionSeq[nAction].a, PlayerList[nPlayer].field_2, ActionSeq[nAction].b); + return; + } + + case 0xA0000: + { + if (PlayerList[nPlayer].nHealth <= 0) { + return; + } + + nDamage = runlist_CheckRadialDamage(nPlayerSprite); + if (!nDamage) { + return; + } + + nSprite2 = nRadialOwner; + // fall through to case 0x80000 + fallthrough__; + } + + case 0x80000: + { + // Dunno how to do this otherwise... we fall through from above but don't want to do this check.. + if (nMessage != 0xA0000) + { + if (!nDamage) { + return; + } + + nSprite2 = pA & 0xFFFF; + } + + // ok continue case 0x80000 as normal, loc_1C57C + if (!PlayerList[nPlayer].nHealth) { + return; + } + + if (!PlayerList[nPlayer].invincibility) + { + PlayerList[nPlayer].nHealth -= nDamage; + if (nPlayer == nLocalPlayer) + { + TintPalette(nDamage, 0, 0); + SetHealthFrame(-1); + } + } + + if (PlayerList[nPlayer].nHealth > 0) + { + if (nDamage > 40 || (totalmoves & 0xF) < 2) + { + if (PlayerList[nPlayer].invincibility) { + return; + } + + if (SectFlag[sprite[nPlayerSprite].sectnum] & kSectUnderwater) + { + if (nAction != 12) + { + PlayerList[nPlayer].field_2 = 0; + PlayerList[nPlayer].nAction = 12; + return; + } + } + else + { + if (nAction != 4) + { + PlayerList[nPlayer].field_2 = 0; + PlayerList[nPlayer].nAction = 4; + + if (nSprite2 > -1) + { + nPlayerSwear[nPlayer]--; + if (nPlayerSwear[nPlayer] <= 0) + { + D3PlayFX(StaticSound[kSound52], nDopple); + nPlayerSwear[nPlayer] = RandomSize(3) + 4; + } + } + } + } + } + + return; + } + else + { + // player has died + if (nSprite2 > -1 && sprite[nSprite2].statnum == 100) + { + short nPlayer2 = GetPlayerFromSprite(nSprite2); + + if (nPlayer2 == nPlayer) // player caused their own death + { + nPlayerScore[nPlayer]--; + } + else + { + nPlayerScore[nPlayer]++; + } + } + else if (nSprite2 < 0) + { + nPlayerScore[nPlayer]--; + } + + if (nMessage == 0xA0000) + { + for (int i = 122; i <= 131; i++) + { + BuildCreatureChunk(nPlayerSprite, seq_GetSeqPicnum(kSeqJoe, i, 0)); + } + + StartDeathSeq(nPlayer, 1); + } + else + { + StartDeathSeq(nPlayer, 0); + } + } + + return; + } + + case 0x20000: + { + sprite[nPlayerSprite].xvel = sPlayerInput[nPlayer].xVel >> 14; + sprite[nPlayerSprite].yvel = sPlayerInput[nPlayer].yVel >> 14; + + if (sPlayerInput[nPlayer].nItem > -1) + { + UseItem(nPlayer, sPlayerInput[nPlayer].nItem); + sPlayerInput[nPlayer].nItem = -1; + } + + int var_EC = PlayerList[nPlayer].field_2; + + sprite[nPlayerSprite].picnum = seq_GetSeqPicnum(PlayerList[nPlayer].nSeq, ActionSeq[nHeightTemplate[nAction]].a, var_EC); + sprite[nDopple].picnum = sprite[nPlayerSprite].picnum; + + if (nPlayerTorch[nPlayer] > 0) + { + nPlayerTorch[nPlayer]--; + if (nPlayerTorch[nPlayer] == 0) + { + SetTorch(nPlayer, 0); + } + else + { + if (nPlayer != nLocalPlayer) + { + nFlashDepth = 5; + AddFlash(sprite[nPlayerSprite].sectnum, + sprite[nPlayerSprite].x, + sprite[nPlayerSprite].y, + sprite[nPlayerSprite].z, 0); + } + } + } + + if (nPlayerDouble[nPlayer] > 0) + { + nPlayerDouble[nPlayer]--; + if (nPlayerDouble[nPlayer] == 150 && nPlayer == nLocalPlayer) { + PlayAlert("WEAPON POWER IS ABOUT TO EXPIRE"); + } + } + + if (nPlayerInvisible[nPlayer] > 0) + { + nPlayerInvisible[nPlayer]--; + if (nPlayerInvisible[nPlayer] == 0) + { + sprite[nPlayerSprite].cstat &= 0x7FFF; // set visible + short nFloorSprite = nPlayerFloorSprite[nPlayerSprite]; + + if (nFloorSprite > -1) { + sprite[nFloorSprite].cstat &= 0x7FFF; // set visible + } + } + else if (nPlayerInvisible[nPlayer] == 150 && nPlayer == nLocalPlayer) + { + PlayAlert("INVISIBILITY IS ABOUT TO EXPIRE"); + } + } + + if (PlayerList[nPlayer].invincibility > 0) + { + PlayerList[nPlayer].invincibility--; + if (PlayerList[nPlayer].invincibility == 150 && nPlayer == nLocalPlayer) { + PlayAlert("INVINCIBILITY IS ABOUT TO EXPIRE"); + } + } + + if (nQuake[nPlayer] != 0) + { + nQuake[nPlayer] = -nQuake[nPlayer]; + if (nQuake[nPlayer] > 0) + { + nQuake[nPlayer] -= 512; + if (nQuake[nPlayer] < 0) + nQuake[nPlayer] = 0; + } + } + + // loc_1A494: + PlayerList[nPlayer].q16angle = (PlayerList[nPlayer].q16angle + sPlayerInput[nPlayer].nAngle) & 0x7FFFFFF; + PlayerList[nPlayer].q16horiz = sPlayerInput[nPlayer].horizon; + sprite[nPlayerSprite].ang = fix16_to_int(PlayerList[nPlayer].q16angle); + + // sprite[nPlayerSprite].zvel is modified within Gravity() + short zVel = sprite[nPlayerSprite].zvel; + + Gravity(nPlayerSprite); + + if (sprite[nPlayerSprite].zvel >= 6500 && zVel < 6500) + { + D3PlayFX(StaticSound[kSound17], 0); + } + + // loc_1A4E6 + short nSector = sprite[nPlayerSprite].sectnum; + short nSectFlag = SectFlag[nPlayerViewSect[nPlayer]]; + + int playerX = sprite[nPlayerSprite].x; + int playerY = sprite[nPlayerSprite].y; + + int x = (sPlayerInput[nPlayer].xVel * 4) >> 2; + int y = (sPlayerInput[nPlayer].yVel * 4) >> 2; + int z = (sprite[nPlayerSprite].zvel * 4) >> 2; + + if (sprite[nPlayerSprite].zvel > 8192) + sprite[nPlayerSprite].zvel = 8192; + + if (PlayerList[nPlayer].bIsMummified) + { + x /= 2; + y /= 2; + } + + int spr_x = sprite[nPlayerSprite].x; + int spr_y = sprite[nPlayerSprite].y; + int spr_z = sprite[nPlayerSprite].z; + int spr_sectnum = sprite[nPlayerSprite].sectnum; + + // TODO + // nSectFlag & kSectUnderwater; + + zVel = sprite[nPlayerSprite].zvel; + + int nMove = 0; // TEMP + + if (bSlipMode) + { + nMove = 0; + + sprite[nPlayerSprite].x += (x >> 14); + sprite[nPlayerSprite].y += (y >> 14); + + vec3_t pos = { sprite[nPlayerSprite].x, sprite[nPlayerSprite].y, sprite[nPlayerSprite].z }; + setsprite(nPlayerSprite, &pos); + + sprite[nPlayerSprite].z = sector[sprite[nPlayerSprite].sectnum].floorz; + } + else + { + nMove = movesprite(nPlayerSprite, x, y, z, 5120, -5120, CLIPMASK0); + + short var_54 = sprite[nPlayerSprite].sectnum; + + pushmove_old(&sprite[nPlayerSprite].x, &sprite[nPlayerSprite].y, &sprite[nPlayerSprite].z, &var_54, sprite[nPlayerSprite].clipdist << 2, 5120, -5120, CLIPMASK0); + if (var_54 != sprite[nPlayerSprite].sectnum) { + mychangespritesect(nPlayerSprite, var_54); + } + } + + // loc_1A6E4 + if (inside(sprite[nPlayerSprite].x, sprite[nPlayerSprite].y, sprite[nPlayerSprite].sectnum) != 1) + { + mychangespritesect(nPlayerSprite, spr_sectnum); + + sprite[nPlayerSprite].x = spr_x; + sprite[nPlayerSprite].y = spr_y; + + if (zVel < sprite[nPlayerSprite].zvel) { + sprite[nPlayerSprite].zvel = zVel; + } + } + +// int _bTouchFloor = bTouchFloor; + short bUnderwater = SectFlag[sprite[nPlayerSprite].sectnum] & kSectUnderwater; + + if (bUnderwater) + { + nXDamage[nPlayer] /= 2; + nYDamage[nPlayer] /= 2; + } + + // Trigger Ramses? + if ((SectFlag[sprite[nPlayerSprite].sectnum] & 0x8000) && bTouchFloor) + { + if (nTotalPlayers <= 1) + { + PlayerList[nPlayer].q16angle = fix16_from_int(GetAngleToSprite(nPlayerSprite, nSpiritSprite) & kAngleMask); + sprite[nPlayerSprite].ang = fix16_to_int(PlayerList[nPlayer].q16angle); + + lPlayerXVel = 0; + lPlayerYVel = 0; + + sprite[nPlayerSprite].xvel = 0; + sprite[nPlayerSprite].yvel = 0; + sprite[nPlayerSprite].zvel = 0; + + nPlayerDAng = 0; + + if (nFreeze < 1) + { + nFreeze = 1; + StopAllSounds(); + StopLocalSound(); + InitSpiritHead(); + + nDestVertPan[nPlayer] = F16(92); + + if (levelnum == 11) + { + nDestVertPan[nPlayer] += F16(46); + } + else + { + nDestVertPan[nPlayer] += F16(11); + } + } + } + else + { + FinishLevel(); + } + + return; + } + + if (nMove & 0x3C000) + { + if (bTouchFloor) + { + // Damage stuff.. + nXDamage[nPlayer] /= 2; + nYDamage[nPlayer] /= 2; + + if (nPlayer == nLocalPlayer) + { + short zVelB = zVel; + + if (zVelB < 0) { + zVelB = -zVelB; + } + + if (zVelB > 512 && !bLockPan) { + nDestVertPan[nPlayer] = F16(92); + } + } + + if (zVel >= 6500) + { + sprite[nPlayerSprite].xvel >>= 2; + sprite[nPlayerSprite].yvel >>= 2; + + runlist_DamageEnemy(nPlayerSprite, -1, ((zVel - 6500) >> 7) + 10); + + if (PlayerList[nPlayer].nHealth <= 0) + { + sprite[nPlayerSprite].xvel = 0; + sprite[nPlayerSprite].yvel = 0; + + StopSpriteSound(nPlayerSprite); + PlayFXAtXYZ(StaticSound[kSoundJonFDie], sprite[nPlayerSprite].x, sprite[nPlayerSprite].y, sprite[nPlayerSprite].z, sprite[nPlayerSprite].sectnum |= 0x4000); // CHECKME + } + else + { + D3PlayFX(StaticSound[kSound27] | 0x2000, nPlayerSprite); + } + } + } + + if (((nMove & 0xC000) == 0x4000) || ((nMove & 0xC000) == 0x8000)) + { + int bx = 0; + + if ((nMove & 0xC000) == 0x4000) + { + bx = nMove & 0x3FFF; + } + else if ((nMove & 0xC000) == 0x8000) + { + bx = wall[nMove & 0x3FFF].nextsector; + } + + if (bx >= 0) + { + int var_B4 = bx; + + if ((sector[bx].hitag == 45) && bTouchFloor) + { + int nNormal = GetWallNormal(nMove & 0x3FFF); + int nDiff = AngleDiff(nNormal, (sprite[nPlayerSprite].ang + 1024) & kAngleMask); + + if (nDiff < 0) { + nDiff = -nDiff; + } + + if (nDiff <= 256) + { + nPlayerPushSect[nPlayer] = bx; + + int var_F4 = sPlayerInput[nPlayer].xVel; + int var_F8 = sPlayerInput[nPlayer].yVel; + int nMyAngle = GetMyAngle(sPlayerInput[nPlayer].xVel, sPlayerInput[nPlayer].yVel); + + MoveSector(var_B4, nMyAngle, &var_F4, &var_F8); + + if (nPlayerPushSound[nPlayer] <= -1) + { + nPlayerPushSound[nPlayer] = 1; + short nBlock = sector[nPlayerPushSect[nPlayer]].extra; + int nBlockSprite = sBlockInfo[nBlock].nSprite; + + D3PlayFX(StaticSound[kSound23], nBlockSprite | 0x4000); + } + else + { + sprite[nPlayerSprite].x = spr_x; + sprite[nPlayerSprite].y = spr_y; + sprite[nPlayerSprite].z = spr_z; + + mychangespritesect(nPlayerSprite, spr_sectnum); + } + + movesprite(nPlayerSprite, var_F4, var_F8, z, 5120, -5120, CLIPMASK0); + goto loc_1AB8E; + } + } + } + } + } + + // loc_1AB46: + if (nPlayerPushSound[nPlayer] > -1) + { + if (nPlayerPushSect[nPlayer] > -1) + { + StopSpriteSound(sBlockInfo[sector[nPlayerPushSect[nPlayer]].extra].nSprite); + } + + nPlayerPushSound[nPlayer] = -1; + } + +loc_1AB8E: + if (!bPlayerPan && !bLockPan) + { + fix16_t nPanVal = fix16_from_int(spr_z - sprite[nPlayerSprite].z) / 32 + F16(92); + + if (nPanVal < F16(0)) { + nPanVal = F16(0); + } + else if (nPanVal > F16(183)) + { + nPanVal = F16(183); + } + + nDestVertPan[nPlayer] = nPanVal; + } + + playerX -= sprite[nPlayerSprite].x; + playerY -= sprite[nPlayerSprite].y; + + totalvel[nPlayer] = ksqrt((playerY * playerY) + (playerX * playerX)); + + int nViewSect = sprite[nPlayerSprite].sectnum; + + int EyeZ = eyelevel[nPlayer] + sprite[nPlayerSprite].z + nQuake[nPlayer]; + + while (1) + { + int nCeilZ = sector[nViewSect].ceilingz; + + if (EyeZ >= nCeilZ) + break; + + if (SectAbove[nViewSect] <= -1) + break; + + nViewSect = SectAbove[nViewSect]; + } + + // Do underwater sector check + if (bUnderwater) + { + if (nViewSect != sprite[nPlayerSprite].sectnum) + { + if ((nMove & 0xC000) == 0x8000) + { + int var_C4 = sprite[nPlayerSprite].x; + int var_D4 = sprite[nPlayerSprite].y; + int var_C8 = sprite[nPlayerSprite].z; + + mychangespritesect(nPlayerSprite, nViewSect); + + sprite[nPlayerSprite].x = spr_x; + sprite[nPlayerSprite].y = spr_y; + + int var_FC = sector[nViewSect].floorz + (-5120); + + sprite[nPlayerSprite].z = var_FC; + + if ((movesprite(nPlayerSprite, x, y, 0, 5120, 0, CLIPMASK0) & 0xC000) == 0x8000) + { + mychangespritesect(nPlayerSprite, sprite[nPlayerSprite].sectnum); + + sprite[nPlayerSprite].x = var_C4; + sprite[nPlayerSprite].y = var_D4; + sprite[nPlayerSprite].z = var_C8; + } + else + { + sprite[nPlayerSprite].z = var_FC - 256; + D3PlayFX(StaticSound[kSound42], nPlayerSprite); + } + } + } + } + + // loc_1ADAF + nPlayerViewSect[nPlayer] = nViewSect; + + nPlayerDX[nPlayer] = sprite[nPlayerSprite].x - spr_x; + nPlayerDY[nPlayer] = sprite[nPlayerSprite].y - spr_y; + + int var_5C = SectFlag[nViewSect] & kSectUnderwater; + + uint16_t buttons = sPlayerInput[nPlayer].buttons; + + if (buttons & 0x40) // LOBODEITY cheat + { + char strDeity[96]; // TODO - reduce in size? + + const char *strDMode = NULL; + + if (PlayerList[nPlayer].invincibility >= 0) + { + PlayerList[nPlayer].invincibility = -1; + strDMode = "ON"; + } + else + { + PlayerList[nPlayer].invincibility = 0; + strDMode = "OFF"; + } + + sPlayerInput[nPlayer].buttons &= 0xBF; + + sprintf(strDeity, "Deity mode %s for player", strDMode); + StatusMessage(150, strDeity); + } + else if (buttons & 0x20) // LOBOCOP cheat + { + FillWeapons(nPlayer); + StatusMessage(150, "All weapons loaded for player"); + } + else if (buttons & 0x80) // LOBOPICK cheat + { + PlayerList[nPlayer].keys = 0xFFFF; + StatusMessage(150, "All keys loaded for player"); + RefreshStatus(); + } + else if (buttons & 0x100) // LOBOSWAG cheat + { + FillItems(nPlayer); + StatusMessage(150, "All items loaded for player"); + } + + // loc_1AEF5: + if (PlayerList[nPlayer].nHealth > 0) + { + if (PlayerList[nPlayer].nMaskAmount > 0) + { + PlayerList[nPlayer].nMaskAmount--; + if (PlayerList[nPlayer].nMaskAmount == 150 && nPlayer == nLocalPlayer) { + PlayAlert("MASK IS ABOUT TO EXPIRE"); + } + } + + if (!PlayerList[nPlayer].invincibility) + { + // Handle air + nBreathTimer[nPlayer]--; + + if (nBreathTimer[nPlayer] <= 0) + { + nBreathTimer[nPlayer] = 90; + + // if underwater + if (var_5C) + { + airpages = 1; + if (PlayerList[nPlayer].nMaskAmount > 0) + { + if (nPlayer == nLocalPlayer) { + BuildStatusAnim(132, 0); + } + + D3PlayFX(StaticSound[kSound30], nPlayerSprite); + + PlayerList[nPlayer].nAir = 100; + } + else + { + PlayerList[nPlayer].nAir -= 25; + if (PlayerList[nPlayer].nAir > 0) + { + D3PlayFX(StaticSound[kSound25], nPlayerSprite); + } + else + { + PlayerList[nPlayer].nHealth += (PlayerList[nPlayer].nAir << 2); + if (PlayerList[nPlayer].nHealth <= 0) + { + PlayerList[nPlayer].nHealth = 0; + StartDeathSeq(nPlayer, 0); + } + + if (nPlayer == nLocalPlayer) + { + SetHealthFrame(-1); + } + + PlayerList[nPlayer].nAir = 0; + + if (PlayerList[nPlayer].nHealth < 300) + { + D3PlayFX(StaticSound[kSound79], nPlayerSprite); + } + else + { + D3PlayFX(StaticSound[kSound19], nPlayerSprite); + } + } + } + + DoBubbles(nPlayer); + SetAirFrame(); + } + else + { + if (nPlayer == nLocalPlayer) + { + BuildStatusAnim(132, 0); + } + + airpages = 0; + } + } + } + + // loc_1B0B9 + if (var_5C) // if underwater + { + if (nPlayerTorch[nPlayer] > 0) + { + nPlayerTorch[nPlayer] = 0; + SetTorch(nPlayer, 0); + } + } + else + { + int nTmpSectNum = sprite[nPlayerSprite].sectnum; + + if (totalvel[nPlayer] > 25 && sprite[nPlayerSprite].z > sector[nTmpSectNum].floorz) + { + if (SectDepth[nTmpSectNum] && !SectSpeed[nTmpSectNum] && !SectDamage[nTmpSectNum]) + { + D3PlayFX(StaticSound[kSound42], nPlayerSprite); + } + } + + // CHECKME - wrong place? + if (nSectFlag & kSectUnderwater) + { + if (PlayerList[nPlayer].nAir < 50) + { + D3PlayFX(StaticSound[kSound14], nPlayerSprite); + } + + nBreathTimer[nPlayer] = 1; + } + + airpages = 0; + + nBreathTimer[nPlayer]--; + if (nBreathTimer[nPlayer] <= 0) + { + nBreathTimer[nPlayer] = 90; + if (nPlayer == nLocalPlayer) + { + // animate lungs + BuildStatusAnim(132, 0); + } + } + + if (PlayerList[nPlayer].nAir < 100) + { + PlayerList[nPlayer].nAir = 100; + SetAirFrame(); + } + } + + // loc_1B1EB + if (nTotalPlayers > 1) + { + int nFloorSprite = nPlayerFloorSprite[nPlayer]; + + sprite[nFloorSprite].x = sprite[nPlayerSprite].x; + sprite[nFloorSprite].y = sprite[nPlayerSprite].y; + + if (sprite[nFloorSprite].sectnum != sprite[nPlayerSprite].sectnum) + { + mychangespritesect(nFloorSprite, sprite[nPlayerSprite].sectnum); + } + + sprite[nFloorSprite].z = sector[sprite[nPlayerSprite].sectnum].floorz; + } + + int var_30 = 0; + + if (PlayerList[nPlayer].nHealth >= 800) + { + var_30 = 2; + } + + if (PlayerList[nPlayer].nMagic >= 1000) + { + var_30 |= 1; + } + + // code to handle item pickup? + short nearTagSector, nearTagWall, nearTagSprite; + int nearHitDist; + + short nValB; + + // neartag finds the nearest sector, wall, and sprite which has its hitag and/or lotag set to a value. + neartag(sprite[nPlayerSprite].x, sprite[nPlayerSprite].y, sprite[nPlayerSprite].z, sprite[nPlayerSprite].sectnum, sprite[nPlayerSprite].ang, + &nearTagSector, &nearTagWall, &nearTagSprite, (int32_t*)&nearHitDist, 1024, 2, NULL); + + feebtag(sprite[nPlayerSprite].x, sprite[nPlayerSprite].y, sprite[nPlayerSprite].z, sprite[nPlayerSprite].sectnum, + &nValB, var_30, 768); + + // Item pickup code + if (nValB >= 0 && sprite[nValB].statnum >= 900) + { + int var_8C = 16; + int var_88 = 9; + + int var_70 = sprite[nValB].statnum - 900; + int var_44 = 0; + + // item lotags start at 6 (1-5 reserved?) so 0-offset them + int var_6C = var_70 - 6; + + if (var_6C <= 54) + { + switch (var_6C) + { +do_default: + default: + { + // loc_1B3C7 + + // CHECKME - is order of evaluation correct? + if (levelnum <= 20 || var_70 >= 25 && (var_70 <= 25 || var_70 == 50)) + { + DestroyItemAnim(nValB); + mydeletesprite(nValB); + } + else + { + StartRegenerate(nValB); + } +do_default_b: + // loc_1BA74 + if (nPlayer == nLocalPlayer) + { + if (nItemText[var_70] > -1 && nTotalPlayers == 1) + { + StatusMessage(400, gString[nItemTextIndex + nItemText[var_70]]); + } + + TintPalette(var_44*4, var_8C*4, 0); + + if (var_88 > -1) + { + PlayLocalSound(var_88, 0); + } + } + + break; + } + case 0: // Speed Loader + { + if (AddAmmo(nPlayer, 1, sprite[nValB].hitag)) + { + var_88 = StaticSound[kSound69]; + goto do_default; + } + + break; + } + case 1: // Fuel Canister + { + if (AddAmmo(nPlayer, 3, sprite[nValB].hitag)) + { + var_88 = StaticSound[kSound69]; + goto do_default; + } + break; + } + case 2: // M - 60 Ammo Belt + { + if (AddAmmo(nPlayer, 2, sprite[nValB].hitag)) + { + var_88 = StaticSound[kSound69]; + CheckClip(nPlayer); + goto do_default; + } + break; + } + case 3: // Grenade + case 21: + case 49: + { + if (AddAmmo(nPlayer, 4, 1)) + { + var_88 = StaticSound[kSound69]; + if (!(nPlayerWeapons[nPlayer] & 0x10)) + { + nPlayerWeapons[nPlayer] |= 0x10; + SetNewWeaponIfBetter(nPlayer, 4); + } + + if (var_70 == 55) + { + sprite[nValB].cstat = 0x8000; + DestroyItemAnim(nValB); + + // loc_1BA74: - repeated block, see in default case + if (nPlayer == nLocalPlayer) + { + if (nItemText[var_70] > -1 && nTotalPlayers == 1) + { + StatusMessage(400, gString[nItemTextIndex + nItemText[var_70]]); + } + + TintPalette(var_44*4, var_8C*4, 0); + + if (var_88 > -1) + { + PlayLocalSound(var_88, 0); + } + } + break; + } + else + { + goto do_default; + } + } + break; + } + + case 4: // Pickable item + case 9: // Pickable item + case 10: // Reserved + case 18: + case 25: + case 28: + case 29: + case 30: + case 33: + case 34: + case 35: + case 36: + case 37: + case 38: + case 45: + case 52: + { + goto do_default; + } + + case 5: // Map + { + GrabMap(); + goto do_default; + } + + case 6: // Berry Twig + { + if (sprite[nValB].hitag == 0) { + break; + } + + var_88 = 20; + int edx = 40; + + if (edx <= 0 || (!(var_30 & 2))) + { + if (!PlayerList[nPlayer].invincibility || edx > 0) + { + PlayerList[nPlayer].nHealth += edx; + if (PlayerList[nPlayer].nHealth > 800) + { + PlayerList[nPlayer].nHealth = 800; + } + else + { + if (PlayerList[nPlayer].nHealth < 0) + { + var_88 = -1; + StartDeathSeq(nPlayer, 0); + } + } + } + + if (nLocalPlayer == nPlayer) + { + SetHealthFrame(1); + } + + if (var_70 == 12) + { + sprite[nValB].hitag = 0; + sprite[nValB].picnum++; + + changespritestat(nValB, 0); + + // loc_1BA74: - repeated block, see in default case + if (nPlayer == nLocalPlayer) + { + if (nItemText[var_70] > -1 && nTotalPlayers == 1) + { + StatusMessage(400, gString[nItemTextIndex + nItemText[var_70]]); + } + + TintPalette(var_44*4, var_8C*4, 0); + + if (var_88 > -1) + { + PlayLocalSound(var_88, 0); + } + } + + break; + } + else + { + if (var_70 != 14) + { + var_88 = 21; + } + else + { + var_44 = var_8C; + var_88 = 22; + var_8C = 0; + } + + goto do_default; + } + } + + break; + } + + case 7: // Blood Bowl + { + int edx = 160; + + // Same code as case 6 now till break + if (edx <= 0 || (!(var_30 & 2))) + { + if (!PlayerList[nPlayer].invincibility || edx > 0) + { + PlayerList[nPlayer].nHealth += edx; + if (PlayerList[nPlayer].nHealth > 800) + { + PlayerList[nPlayer].nHealth = 800; + } + else + { + if (PlayerList[nPlayer].nHealth < 0) + { + var_88 = -1; + StartDeathSeq(nPlayer, 0); + } + } + } + + if (nLocalPlayer == nPlayer) + { + SetHealthFrame(1); + } + + if (var_70 == 12) + { + sprite[nValB].hitag = 0; + sprite[nValB].picnum++; + + changespritestat(nValB, 0); + + // loc_1BA74: - repeated block, see in default case + if (nPlayer == nLocalPlayer) + { + if (nItemText[var_70] > -1 && nTotalPlayers == 1) + { + StatusMessage(400, gString[nItemTextIndex + nItemText[var_70]]); + } + + TintPalette(var_44*4, var_8C*4, 0); + + if (var_88 > -1) + { + PlayLocalSound(var_88, 0); + } + } + + break; + } + else + { + if (var_70 != 14) + { + var_88 = 21; + } + else + { + var_44 = var_8C; + var_88 = 22; + var_8C = 0; + } + + goto do_default; + } + } + + break; + } + + case 8: // Cobra Venom Bowl + { + int edx = -200; + + // Same code as case 6 and 7 from now till break + if (edx <= 0 || (!(var_30 & 2))) + { + if (!PlayerList[nPlayer].invincibility || edx > 0) + { + PlayerList[nPlayer].nHealth += edx; + if (PlayerList[nPlayer].nHealth > 800) + { + PlayerList[nPlayer].nHealth = 800; + } + else + { + if (PlayerList[nPlayer].nHealth < 0) + { + var_88 = -1; + StartDeathSeq(nPlayer, 0); + } + } + } + + if (nLocalPlayer == nPlayer) + { + SetHealthFrame(1); + } + + if (var_70 == 12) + { + sprite[nValB].hitag = 0; + sprite[nValB].picnum++; + + changespritestat(nValB, 0); + + // loc_1BA74: - repeated block, see in default case + if (nPlayer == nLocalPlayer) + { + if (nItemText[var_70] > -1 && nTotalPlayers == 1) + { + StatusMessage(400, gString[nItemTextIndex + nItemText[var_70]]); + } + + TintPalette(var_44*4, var_8C*4, 0); + + if (var_88 > -1) + { + PlayLocalSound(var_88, 0); + } + } + + break; + } + else + { + if (var_70 != 14) + { + var_88 = 21; + } + else + { + var_44 = var_8C; + var_88 = 22; + var_8C = 0; + } + + goto do_default; + } + } + + break; + } + + case 11: // Bubble Nest + { + PlayerList[nPlayer].nAir += 10; + if (PlayerList[nPlayer].nAir > 100) { + PlayerList[nPlayer].nAir = 100; // TODO - constant + } + + SetAirFrame(); + + if (nBreathTimer[nPlayer] < 89) + { + D3PlayFX(StaticSound[kSound13], nPlayerSprite); + } + + nBreathTimer[nPlayer] = 90; + break; + } + + case 12: // Still Beating Heart + { + if (GrabItem(nPlayer, kItemHeart)) { + goto do_default; + } + + break; + } + + case 13: // Scarab amulet(Invicibility) + { + if (GrabItem(nPlayer, kItemInvincibility)) { + goto do_default; + } + + break; + } + + case 14: // Severed Slave Hand(double damage) + { + if (GrabItem(nPlayer, kItemDoubleDamage)) { + goto do_default; + } + + break; + } + + case 15: // Unseen eye(Invisibility) + { + if (GrabItem(nPlayer, kItemInvisibility)) { + goto do_default; + } + + break; + } + + case 16: // Torch + { + if (GrabItem(nPlayer, kItemTorch)) { + goto do_default; + } + + break; + } + + case 17: // Sobek Mask + { + if (GrabItem(nPlayer, kItemMask)) { + goto do_default; + } + + break; + } + + case 19: // Extra Life + { + var_88 = -1; + + if (nPlayerLives[nPlayer] >= kMaxPlayerLives) { + break; + } + + nPlayerLives[nPlayer]++; + + if (nPlayer == nLocalPlayer) { + BuildStatusAnim(146 + ((nPlayerLives[nPlayer] - 1) * 2), 0); + } + + var_8C = 32; + var_44 = 32; + goto do_default; + } + + // FIXME - lots of repeated code from here down!! + case 20: // sword pickup?? + { + var_40 = 0; + int ebx = 0; + + // loc_1B75D + int var_18 = 1 << var_40; + + short weapons = nPlayerWeapons[nPlayer]; + + if (weapons & var_18) + { + if (levelnum > 20) + { + AddAmmo(nPlayer, WeaponInfo[var_40].nAmmoType, ebx); + } + } + else + { + weapons = var_40; + + SetNewWeaponIfBetter(nPlayer, weapons); + + nPlayerWeapons[nPlayer] |= var_18; + + AddAmmo(nPlayer, WeaponInfo[weapons].nAmmoType, ebx); + + var_88 = StaticSound[kSound72]; + } + + if (var_40 == 2) { + CheckClip(nPlayer); + } + + if (var_70 <= 50) { + goto do_default; + } + + sprite[nValB].cstat = 0x8000; + DestroyItemAnim(nValB); +//// + // loc_1BA74: - repeated block, see in default case + if (nPlayer == nLocalPlayer) + { + if (nItemText[var_70] > -1 && nTotalPlayers == 1) + { + StatusMessage(400, gString[nItemTextIndex + nItemText[var_70]]); + } + + TintPalette(var_44*4, var_8C*4, 0); + + if (var_88 > -1) + { + PlayLocalSound(var_88, 0); + } + } + + break; +///// + } + + case 22: // .357 Magnum Revolver + case 46: + { + var_40 = 1; + int ebx = 6; + + // loc_1B75D + int var_18 = 1 << var_40; + + short weapons = nPlayerWeapons[nPlayer]; + + if (weapons & var_18) + { + if (levelnum > 20) + { + AddAmmo(nPlayer, WeaponInfo[var_40].nAmmoType, ebx); + } + } + else + { + weapons = var_40; + + SetNewWeaponIfBetter(nPlayer, weapons); + + nPlayerWeapons[nPlayer] |= var_18; + + AddAmmo(nPlayer, WeaponInfo[weapons].nAmmoType, ebx); + + var_88 = StaticSound[kSound72]; + } + + if (var_40 == 2) { + CheckClip(nPlayer); + } + + if (var_70 <= 50) { + goto do_default; + } + + sprite[nValB].cstat = 0x8000; + DestroyItemAnim(nValB); +//// + // loc_1BA74: - repeated block, see in default case + if (nPlayer == nLocalPlayer) + { + if (nItemText[var_70] > -1 && nTotalPlayers == 1) + { + StatusMessage(400, gString[nItemTextIndex + nItemText[var_70]]); + } + + TintPalette(var_44*4, var_8C*4, 0); + + if (var_88 > -1) + { + PlayLocalSound(var_88, 0); + } + } + + break; +///// + } + + case 23: // M - 60 Machine Gun + case 47: + { + var_40 = 2; + int ebx = 24; + + // loc_1B75D + int var_18 = 1 << var_40; + + short weapons = nPlayerWeapons[nPlayer]; + + if (weapons & var_18) + { + if (levelnum > 20) + { + AddAmmo(nPlayer, WeaponInfo[var_40].nAmmoType, ebx); + } + } + else + { + weapons = var_40; + + SetNewWeaponIfBetter(nPlayer, weapons); + + nPlayerWeapons[nPlayer] |= var_18; + + AddAmmo(nPlayer, WeaponInfo[weapons].nAmmoType, ebx); + + var_88 = StaticSound[kSound72]; + } + + if (var_40 == 2) { + CheckClip(nPlayer); + } + + if (var_70 <= 50) { + goto do_default; + } + + sprite[nValB].cstat = 0x8000; + DestroyItemAnim(nValB); +//// + // loc_1BA74: - repeated block, see in default case + if (nPlayer == nLocalPlayer) + { + if (nItemText[var_70] > -1 && nTotalPlayers == 1) + { + StatusMessage(400, gString[nItemTextIndex + nItemText[var_70]]); + } + + TintPalette(var_44*4, var_8C*4, 0); + + if (var_88 > -1) + { + PlayLocalSound(var_88, 0); + } + } + + break; +///// + } + + case 24: // Flame Thrower + case 48: + { + var_40 = 3; + int ebx = 100; + + // loc_1B75D + int var_18 = 1 << var_40; + + short weapons = nPlayerWeapons[nPlayer]; + + if (weapons & var_18) + { + if (levelnum > 20) + { + AddAmmo(nPlayer, WeaponInfo[var_40].nAmmoType, ebx); + } + } + else + { + weapons = var_40; + + SetNewWeaponIfBetter(nPlayer, weapons); + + nPlayerWeapons[nPlayer] |= var_18; + + AddAmmo(nPlayer, WeaponInfo[weapons].nAmmoType, ebx); + + var_88 = StaticSound[kSound72]; + } + + if (var_40 == 2) { + CheckClip(nPlayer); + } + + if (var_70 <= 50) { + goto do_default; + } + + sprite[nValB].cstat = 0x8000; + DestroyItemAnim(nValB); +//// + // loc_1BA74: - repeated block, see in default case + if (nPlayer == nLocalPlayer) + { + if (nItemText[var_70] > -1 && nTotalPlayers == 1) + { + StatusMessage(400, gString[nItemTextIndex + nItemText[var_70]]); + } + + TintPalette(var_44*4, var_8C*4, 0); + + if (var_88 > -1) + { + PlayLocalSound(var_88, 0); + } + } + + break; +///// + } + + case 26: // Cobra Staff + case 50: + { + var_40 = 5; + int ebx = 20; + + // loc_1B75D + int var_18 = 1 << var_40; + + short weapons = nPlayerWeapons[nPlayer]; + + if (weapons & var_18) + { + if (levelnum > 20) + { + AddAmmo(nPlayer, WeaponInfo[var_40].nAmmoType, ebx); + } + } + else + { + weapons = var_40; + + SetNewWeaponIfBetter(nPlayer, weapons); + + nPlayerWeapons[nPlayer] |= var_18; + + AddAmmo(nPlayer, WeaponInfo[weapons].nAmmoType, ebx); + + var_88 = StaticSound[kSound72]; + } + + if (var_40 == 2) { + CheckClip(nPlayer); + } + + if (var_70 <= 50) { + goto do_default; + } + + sprite[nValB].cstat = 0x8000; + DestroyItemAnim(nValB); +//// + // loc_1BA74: - repeated block, see in default case + if (nPlayer == nLocalPlayer) + { + if (nItemText[var_70] > -1 && nTotalPlayers == 1) + { + StatusMessage(400, gString[nItemTextIndex + nItemText[var_70]]); + } + + TintPalette(var_44*4, var_8C*4, 0); + + if (var_88 > -1) + { + PlayLocalSound(var_88, 0); + } + } + + break; +///// + } + + case 27: // Eye of Ra Gauntlet + case 51: + { + var_40 = 6; + int ebx = 2; + + // loc_1B75D + int var_18 = 1 << var_40; + + short weapons = nPlayerWeapons[nPlayer]; + + if (weapons & var_18) + { + if (levelnum > 20) + { + AddAmmo(nPlayer, WeaponInfo[var_40].nAmmoType, ebx); + } + } + else + { + weapons = var_40; + + SetNewWeaponIfBetter(nPlayer, weapons); + + nPlayerWeapons[nPlayer] |= var_18; + + AddAmmo(nPlayer, WeaponInfo[weapons].nAmmoType, ebx); + + var_88 = StaticSound[kSound72]; + } + + if (var_40 == 2) { + CheckClip(nPlayer); + } + + if (var_70 <= 50) { + goto do_default; + } + + sprite[nValB].cstat = 0x8000; + DestroyItemAnim(nValB); +//// + // loc_1BA74: - repeated block, see in default case + if (nPlayer == nLocalPlayer) + { + if (nItemText[var_70] > -1 && nTotalPlayers == 1) + { + StatusMessage(400, gString[nItemTextIndex + nItemText[var_70]]); + } + + TintPalette(var_44*4, var_8C*4, 0); + + if (var_88 > -1) + { + PlayLocalSound(var_88, 0); + } + } + + break; +///// + } + + case 31: // Cobra staff ammo + { + if (AddAmmo(nPlayer, 5, 1)) { + var_88 = StaticSound[kSound69]; + goto do_default; + } + + break; + } + + case 32: // Raw Energy + { + if (AddAmmo(nPlayer, 6, sprite[nValB].hitag)) { + var_88 = StaticSound[kSound69]; + goto do_default; + } + + break; + } + + // Lots of repeated code for door key handling + case 39: // Power key + { + int ecx = 4096; + + var_88 = -1; + + if (PlayerList[nPlayer].keys != ecx) + { + if (nPlayer == nLocalPlayer) { + BuildStatusAnim(36, 0); + } + + PlayerList[nPlayer].keys |= ecx; + + if (nTotalPlayers > 1) + { + goto do_default_b; + } + else + { + goto do_default; + } + } + + break; + } + case 40: // Time key + { + int ecx = 4096 << 1; + + var_88 = -1; + + if (PlayerList[nPlayer].keys != ecx) + { + if (nPlayer == nLocalPlayer) { + BuildStatusAnim(36 + 2, 0); + } + + PlayerList[nPlayer].keys |= ecx; + + if (nTotalPlayers > 1) + { + goto do_default_b; + } + else + { + goto do_default; + } + } + + break; + } + case 41: // War key + { + int ecx = 4096 << 2; + + var_88 = -1; + + if (PlayerList[nPlayer].keys != ecx) + { + if (nPlayer == nLocalPlayer) { + BuildStatusAnim(36 + 4, 0); + } + + PlayerList[nPlayer].keys |= ecx; + + if (nTotalPlayers > 1) + { + goto do_default_b; + } + else + { + goto do_default; + } + } + + break; + } + case 42: // Earth key + { + int ecx = 4096 << 3; + + var_88 = -1; + + if (PlayerList[nPlayer].keys != ecx) + { + if (nPlayer == nLocalPlayer) { + BuildStatusAnim(36 + 6, 0); + } + + PlayerList[nPlayer].keys |= ecx; + + if (nTotalPlayers > 1) + { + goto do_default_b; + } + else + { + goto do_default; + } + } + + break; + } + + case 43: // Magical Essence + case 44: // ? + { + if (PlayerList[nPlayer].nMagic >= 1000) { + break; + } + + var_88 = StaticSound[kSound67]; + + PlayerList[nPlayer].nMagic += 100; + if (PlayerList[nPlayer].nMagic >= 1000) { + PlayerList[nPlayer].nMagic = 1000; + } + + if (nLocalPlayer == nPlayer) + { + SetMagicFrame(); + } + + goto do_default; + } + + case 53: // Scarab (Checkpoint) + { + if (nLocalPlayer == nPlayer) + { + short nAnim = sprite[nValB].owner; + AnimList[nAnim].nSeq++; + AnimFlags[nAnim] &= 0xEF; + AnimList[nAnim].field_2 = 0; + + changespritestat(nValB, 899); + } + + SetSavePoint(nPlayer, sprite[nPlayerSprite].x, sprite[nPlayerSprite].y, sprite[nPlayerSprite].z, sprite[nPlayerSprite].sectnum, sprite[nPlayerSprite].ang); + break; + } + + case 54: // Golden Sarcophagus (End Level) + { + if (!bInDemo) { + FinishLevel(); + } + else { + inputState.keySetState(32, 1); + } + + DestroyItemAnim(nValB); + mydeletesprite(nValB); + break; + } + } + } + } + + // CORRECT ? // loc_1BAF9: + if (bTouchFloor) + { + if (sector[sprite[nPlayerSprite].sectnum].lotag > 0) + { + runlist_SignalRun(sector[sprite[nPlayerSprite].sectnum].lotag - 1, nPlayer | 0x50000); + } + } + + if (nSector != sprite[nPlayerSprite].sectnum) + { + if (sector[nSector].lotag > 0) + { + runlist_SignalRun(sector[nSector].lotag - 1, nPlayer | 0x70000); + } + + if (sector[sprite[nPlayerSprite].sectnum].lotag > 0) + { + runlist_SignalRun(sector[sprite[nPlayerSprite].sectnum].lotag - 1, nPlayer | 0x60000); + } + } + + if (!PlayerList[nPlayer].bIsMummified) + { + if (buttons & kButtonOpen) + { + ClearSpaceBar(nPlayer); + + if (nearTagWall >= 0 && wall[nearTagWall].lotag > 0) + { + runlist_SignalRun(wall[nearTagWall].lotag - 1, nPlayer | 0x40000); + } + + if (nearTagSector >= 0 && sector[nearTagSector].lotag > 0) + { + runlist_SignalRun(sector[nearTagSector].lotag - 1, nPlayer | 0x40000); + } + } + + // was int var_38 = buttons & 0x8 + if (buttons & kButtonFire) + { + FireWeapon(nPlayer); + } + else + { + StopFiringWeapon(nPlayer); + } + + // loc_1BC57: + + // CHECKME - are we finished with 'nSector' variable at this point? if so, maybe set it to sprite[nPlayerSprite].sectnum so we can make this code a bit neater. Don't assume sprite[nPlayerSprite].sectnum == nSector here!! + if (nStandHeight > (sector[sprite[nPlayerSprite].sectnum].floorz - sector[sprite[nPlayerSprite].sectnum].ceilingz)) { + var_48 = 1; + } + + // Jumping + if (buttons & kButtonJump) + { + if (bUnderwater) + { + sprite[nPlayerSprite].zvel = -2048; + nActionB = 10; + } + else if (bTouchFloor) + { + if (nAction < 6 || nAction > 8) + { + sprite[nPlayerSprite].zvel = -3584; + nActionB = 3; + } + } + + // goto loc_1BE70: + } + else if (buttons & kButtonCrouch) + { + if (bUnderwater) + { + sprite[nPlayerSprite].zvel = 2048; + nActionB = 10; + } + else + { + if (eyelevel[nPlayer] < -8320) { + eyelevel[nPlayer] += ((-8320 - eyelevel[nPlayer]) >> 1); + } + +loc_1BD2E: + if (totalvel[nPlayer] < 1) { + nActionB = 6; + } + else { + nActionB = 7; + } + } + + // goto loc_1BE70: + } + else + { + if (PlayerList[nPlayer].nHealth > 0) + { + int var_EC = nActionEyeLevel[nAction]; + eyelevel[nPlayer] += (var_EC - eyelevel[nPlayer]) >> 1; + + if (bUnderwater) + { + if (totalvel[nPlayer] <= 1) + nActionB = 9; + else + nActionB = 10; + } + else + { + // CHECKME - confirm branching in this area is OK + if (var_48) + { + goto loc_1BD2E; + } + else + { + if (totalvel[nPlayer] <= 1) { + nActionB = 0;//bUnderwater; // this is just setting to 0 + } + else if (totalvel[nPlayer] <= 30) { + nActionB = 2; + } + else + { + nActionB = 1; + } + } + } + } + // loc_1BE30 + if (buttons & kButtonFire) // was var_38 + { + if (bUnderwater) + { + nActionB = 11; + } + else + { + if (nActionB != 2 && nActionB != 1) + { + nActionB = 5; + } + } + } + } + + // loc_1BE70: + // Handle player pressing number keys to change weapon + uint8_t var_90 = (buttons >> 13) & 0xF; + + if (var_90) + { + var_90--; + + if (nPlayerWeapons[nPlayer] & (1 << var_90)) + { + SetNewWeapon(nPlayer, var_90); + } + } + } + else // player is mummified + { + if (buttons & kButtonFire) + { + FireWeapon(nPlayer); + } + + if (nAction != 15) + { + if (totalvel[nPlayer] <= 1) + { + nActionB = 13; + } + else + { + nActionB = 14; + } + } + } + + // loc_1BF09 + if (nActionB != nAction && nAction != 4) + { + nAction = nActionB; + PlayerList[nPlayer].nAction = nActionB; + PlayerList[nPlayer].field_2 = 0; + } + + if (nPlayer == nLocalPlayer) + { + // TODO - tidy / consolidate repeating blocks of code here? + if (buttonMap.ButtonDown(gamefunc_Look_Up)) + { + bLockPan = kFalse; + if (nVertPan[nPlayer] < F16(180)) { + nVertPan[nPlayer] += F16(4); + } + + bPlayerPan = kTrue; + nDestVertPan[nPlayer] = nVertPan[nPlayer]; + } + else if (buttonMap.ButtonDown(gamefunc_Look_Down)) + { + bLockPan = kFalse; + if (nVertPan[nPlayer] > F16(4)) { + nVertPan[nPlayer] -= F16(4); + } + + bPlayerPan = kTrue; + nDestVertPan[nPlayer] = nVertPan[nPlayer]; + } + else if (buttonMap.ButtonDown(gamefunc_Look_Straight)) + { + bLockPan = kFalse; + bPlayerPan = kFalse; + nVertPan[nPlayer] = F16(92); + nDestVertPan[nPlayer] = F16(92); + } + else if (buttonMap.ButtonDown(gamefunc_Aim_Up)) + { + bLockPan = kTrue; + if (nVertPan[nPlayer] < F16(180)) { + nVertPan[nPlayer] += F16(4); + } + + bPlayerPan = kTrue; + nDestVertPan[nPlayer] = nVertPan[nPlayer]; + } + else if (buttonMap.ButtonDown(gamefunc_Aim_Down)) + { + bLockPan = kTrue; + if (nVertPan[nPlayer] > F16(4)) { + nVertPan[nPlayer] -= F16(4); + } + + bPlayerPan = kTrue; + nDestVertPan[nPlayer] = nVertPan[nPlayer]; + } + + // loc_1C048: + if (totalvel[nPlayer] > 20) { + bPlayerPan = kFalse; + } + + if (g_MyAimMode) + bLockPan = kTrue; + + // loc_1C05E + fix16_t ecx = nDestVertPan[nPlayer] - nVertPan[nPlayer]; + + if (g_MyAimMode) + { + ecx = 0; + } + + if (ecx) + { + if (ecx / 4 == 0) + { + if (ecx >= 0) { + ecx = 1; + } + else + { + ecx = -1; + } + } + else + { + ecx /= 4; + + if (ecx > F16(4)) + { + ecx = F16(4); + } + else if (ecx < -F16(4)) + { + ecx = -F16(4); + } + } + + nVertPan[nPlayer] += ecx; + } + } + } + else // else, player's health is less than 0 + { + // loc_1C0E9 + if (buttons & kButtonOpen) + { + ClearSpaceBar(nPlayer); + + if (nAction >= 16) + { + if (nPlayer == nLocalPlayer) + { + StopAllSounds(); + StopLocalSound(); + GrabPalette(); + } + + PlayerList[nPlayer].nCurrentWeapon = nPlayerOldWeapon[nPlayer]; + + if (nPlayerLives[nPlayer] && nNetTime) + { + if (nAction != 20) + { + sprite[nPlayerSprite].picnum = seq_GetSeqPicnum(kSeqJoe, 120, 0); + sprite[nPlayerSprite].cstat = 0; + sprite[nPlayerSprite].z = sector[sprite[nPlayerSprite].sectnum].floorz; + } + + // will invalidate nPlayerSprite + RestartPlayer(nPlayer); + + nPlayerSprite = PlayerList[nPlayer].nSprite; + nDopple = nDoppleSprite[nPlayer]; + } + else + { + if (CDplaying()) { + fadecdaudio(); + } + + if (levelnum == 20) { + DoFailedFinalScene(); + } + else { + DoGameOverScene(); + } + + levelnew = 100; + } + } + } + } + + // loc_1C201: + if (nLocalPlayer == nPlayer) + { + nLocalEyeSect = nPlayerViewSect[nLocalPlayer]; + CheckAmbience(nLocalEyeSect); + } + + int var_AC = SeqOffsets[PlayerList[nPlayer].nSeq] + ActionSeq[nAction].a; + + seq_MoveSequence(nPlayerSprite, var_AC, PlayerList[nPlayer].field_2); + PlayerList[nPlayer].field_2++; + + if (PlayerList[nPlayer].field_2 >= SeqSize[var_AC]) + { + PlayerList[nPlayer].field_2 = 0; + + switch (PlayerList[nPlayer].nAction) + { + default: + break; + + case 3: + PlayerList[nPlayer].field_2 = SeqSize[var_AC] - 1; + break; + case 4: + PlayerList[nPlayer].nAction = 0; + break; + case 16: + PlayerList[nPlayer].field_2 = SeqSize[var_AC] - 1; + + if (sprite[nPlayerSprite].z < sector[sprite[nPlayerSprite].sectnum].floorz) { + sprite[nPlayerSprite].z += 256; + } + + if (!RandomSize(5)) + { + int mouthX, mouthY, mouthZ; + short mouthSect; + WheresMyMouth(nPlayer, &mouthX, &mouthY, &mouthZ, &mouthSect); + + BuildAnim(-1, 71, 0, mouthX, mouthY, sprite[nPlayerSprite].z + 3840, mouthSect, 75, 128); + } + break; + case 17: + PlayerList[nPlayer].nAction = 18; + break; + case 19: + sprite[nPlayerSprite].cstat |= 0x8000; + PlayerList[nPlayer].nAction = 20; + break; + } + } + + // loc_1C3B4: + if (nPlayer == nLocalPlayer) + { + initx = sprite[nPlayerSprite].x; + inity = sprite[nPlayerSprite].y; + initz = sprite[nPlayerSprite].z; + initsect = sprite[nPlayerSprite].sectnum; + inita = sprite[nPlayerSprite].ang; + } + + if (!PlayerList[nPlayer].nHealth) + { + nYDamage[nPlayer] = 0; + nXDamage[nPlayer] = 0; + + if (eyelevel[nPlayer] >= -2816) + { + eyelevel[nPlayer] = -2816; + dVertPan[nPlayer] = 0; + } + else + { + if (nVertPan[nPlayer] < F16(92)) + { + nVertPan[nPlayer] = F16(91); + eyelevel[nPlayer] -= (dVertPan[nPlayer] << 8); + } + else + { + nVertPan[nPlayer] += fix16_from_int(dVertPan[nPlayer]); + if (nVertPan[nPlayer] >= F16(200)) + { + nVertPan[nPlayer] = F16(199); + } + else if (nVertPan[nPlayer] <= F16(92)) + { + if (!(SectFlag[sprite[nPlayerSprite].sectnum] & kSectUnderwater)) + { + SetNewWeapon(nPlayer, nDeathType[nPlayer] + 8); + } + } + + dVertPan[nPlayer]--; + } + } + } + + // loc_1C4E1 + sprite[nDopple].x = sprite[nPlayerSprite].x; + sprite[nDopple].y = sprite[nPlayerSprite].y; + sprite[nDopple].z = sprite[nPlayerSprite].z; + + if (SectAbove[sprite[nPlayerSprite].sectnum] > -1) + { + sprite[nDopple].ang = sprite[nPlayerSprite].ang; + mychangespritesect(nDopple, SectAbove[sprite[nPlayerSprite].sectnum]); + sprite[nDopple].cstat = 0x101; + } + else + { + sprite[nDopple].cstat = 0x8000; + } + + MoveWeapons(nPlayer); + + return; + } + } +} + + +static SavegameHelper sgh("player", + SV(lPlayerXVel), + SV(lPlayerYVel), + SV(nPlayerDAng), + SV(obobangle), + SV(bobangle), + SV(bPlayerPan), + SV(bLockPan), + SV(g_MyAimMode), + SV(nStandHeight), + SV(PlayerCount), + SV(nNetStartSprites), + SV(nCurStartSprite), + SV(nLocalPlayer), + SA(nBreathTimer), + SA(nPlayerSwear), + SA(nPlayerPushSect), + SA(nDeathType), + SA(nPlayerScore), + SA(nPlayerColor), + SA(nPlayerDY), + SA(nPlayerDX), + SA(playerNames), + SA(nPistolClip), + SA(nXDamage), + SA(nYDamage), + SA(nDoppleSprite), + SA(namelen), + SA(nPlayerOldWeapon), + SA(nPlayerClip), + SA(nPlayerPushSound), + SA(nTauntTimer), + SA(nPlayerTorch), + SA(nPlayerWeapons), + SA(nPlayerLives), + SA(nPlayerItem), + SA(PlayerList), + SA(nPlayerInvisible), + SA(nPlayerDouble), + SA(nPlayerViewSect), + SA(nPlayerFloorSprite), + SA(sPlayerSave), + SA(totalvel), + SA(eyelevel), + SA(nNetStartSprite), + SA(nPlayerGrenade), + SA(nGrenadePlayer), + SA(word_D282A), + nullptr); + +END_PS_NS diff --git a/source/exhumed/src/player.h b/source/exhumed/src/player.h new file mode 100644 index 000000000..1ac79976a --- /dev/null +++ b/source/exhumed/src/player.h @@ -0,0 +1,130 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __player_h__ +#define __player_h__ + +#include "compat.h" + +BEGIN_PS_NS + +void PlayerInterruptKeys(); +void RestoreSavePoint(int nPlayer, int *x, int *y, int *z, short *nSector, short *nAngle); +void SetSavePoint(int nPlayer, int x, int y, int z, short nSector, short nAngle); +void InitPlayer(); +void InitPlayerKeys(short nPlayer); +void DoKenTest(); +int GrabPlayer(); +void InitPlayerInventory(short nPlayer); +void RestartPlayer(short nPlayer); + +void FuncPlayer(int nSector, int nSprite, int nRun); + +#define kMaxPlayers 8 +#define kDefaultLives 3 +#define kMaxPlayerLives 5 +#define kMaxHealth 800 + +extern int nLocalPlayer; + +extern int lPlayerXVel; +extern int lPlayerYVel; +extern fix16_t nPlayerDAng; + +struct Player +{ + short nHealth; + short field_2; + short nAction; + short nSprite; + short bIsMummified; + short someNetVal; + short invincibility; + short nAir; + short nSeq; + short nMaskAmount; + uint16_t keys; + short nMagic; + char items[8]; + short nAmmo[7]; // TODO - kMaxWeapons? + short pad[2]; + + short nCurrentWeapon; + short field_3FOUR; + short bIsFiring; + short field_38; + short field_3A; + short field_3C; + short nRun; + + fix16_t q16angle, q16oangle; + fix16_t q16horiz, q16ohoriz; + vec3_t opos; +}; + +extern short PlayerCount; + +extern short nPlayerTorch[]; + +extern short nPlayerLives[]; +extern short nPlayerItem[]; +extern Player PlayerList[]; +extern short nPlayerInvisible[]; +extern short nPlayerDouble[]; +extern short nPlayerViewSect[]; +extern short nPlayerFloorSprite[]; + +extern short nTauntTimer[]; + +extern short nDoppleSprite[]; + +extern uint16_t nPlayerWeapons[]; + +extern short nPlayerOldWeapon[]; +extern short nPlayerGrenade[kMaxPlayers]; +extern short nGrenadePlayer[50]; + +extern short nPistolClip[]; + +extern short nPlayerScore[]; + +extern short nPlayerClip[]; + +extern short obobangle, bobangle; + +extern int totalvel[]; +extern int16_t eyelevel[], oeyelevel[]; + +extern short nNetStartSprite[kMaxPlayers]; +extern short nNetStartSprites; +extern short nCurStartSprite; + +extern int nXDamage[kMaxPlayers]; +extern int nYDamage[kMaxPlayers]; + +extern int nPlayerDY[kMaxPlayers]; +extern int nPlayerDX[kMaxPlayers]; + +short GetPlayerFromSprite(short nSprite); +void SetPlayerMummified(int nPlayer, int bIsMummified); +int AddAmmo(int nPlayer, int nWeapon, int nAmmoAmount); +void ShootStaff(int nPlayer); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/ps_input.h b/source/exhumed/src/ps_input.h new file mode 100644 index 000000000..b27185990 --- /dev/null +++ b/source/exhumed/src/ps_input.h @@ -0,0 +1,70 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __input_h__ +#define __input_h__ + +#include "compat.h" + +BEGIN_PS_NS + +enum { + kButtonJump = 0x1, + kButtonOpen = 0x4, + kButtonFire = 0x8, + kButtonCrouch = 0x10, + kButtonCheatGuns = 0x20, + kButtonCheatGodMode = 0x40, + kButtonCheatKeys = 0x80, + kButtonCheatItems = 0x100, +}; + +// 32 bytes +struct PlayerInput // TODO consider adjusting this for demo compatibility +{ + int xVel; + int yVel; + // short nAngle; + fix16_t nAngle; + uint16_t buttons; + short nTarget; + // uint8_t horizon; + fix16_t horizon; + int8_t nItem; + int h; + char i; + char field_15[11]; +}; + +void InitInput(); +void ClearAllKeys(); +void WaitNoKey(int nSecs, void (*pFunc) (void)); +int WaitAnyKey(int nSecs); + +void UpdateInputs(); + +void ClearSpaceBar(short nPlayer); + +void GetLocalInput(); + +extern PlayerInput sPlayerInput[]; +extern int nNetMoves; + +END_PS_NS + +#endif diff --git a/source/exhumed/src/queen.cpp b/source/exhumed/src/queen.cpp new file mode 100644 index 000000000..d3a3b6777 --- /dev/null +++ b/source/exhumed/src/queen.cpp @@ -0,0 +1,1504 @@ +//------------------------------------------------------------------------- +/* +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 "exhumed.h" +#include "aistuff.h" +#include "engine.h" +#include "queen.h" +#include "move.h" +#include "sequence.h" +#include "runlist.h" +#include "random.h" +#include "wasp.h" +#include "trigdat.h" +#include "anims.h" +#include "player.h" +#include "sound.h" +#include "names.h" +#include + +BEGIN_PS_NS + +#define kMaxQueens 1 +#define kMaxEggs 10 +#define kMaxTails 7 + +short QueenCount = 0; + +static actionSeq ActionSeq[] = { + { 0, 0 }, + { 0, 0 }, + { 9, 0 }, + { 36, 0 }, + { 18, 0 }, + { 27, 0 }, + { 45, 0 }, + { 45, 0 }, + { 54, 1 }, + { 53, 1 }, + { 55, 1 } +}; + +static actionSeq HeadSeq[] = { + { 56, 1 }, + { 65, 0 }, + { 65, 0 }, + { 65, 0 }, + { 65, 0 }, + { 65, 0 }, + { 74, 0 }, + { 82, 0 }, + { 90, 0 } +}; + +static actionSeq EggSeq[] = { + { 19, 1 }, + { 18, 1 }, + { 0, 0 }, + { 9, 0 }, + { 23, 1 }, +}; + +int nQHead = 0; + +short nEggsFree; +short nHeadVel; +short nVelShift; + +short tailspr[kMaxTails]; +short nEggFree[kMaxEggs]; + +short QueenChan[kMaxQueens]; + + + +struct Queen +{ + short nHealth; + short field_2; + short nAction; + short nSprite; + short nTarget; + short field_A; + short field_C; + short pad; + short field_10; + short field_12; +}; + +struct Egg +{ + short nHealth; + short field_2; + short nAction; + short nSprite; + short field_8; + short nTarget; + short field_C; + short field_E; +}; + +struct Head +{ + short nHealth; + short field_2; + short nAction; + short nSprite; + short field_8; + short nTarget; + short field_C; + short field_E; +}; + +Egg QueenEgg[kMaxEggs]; +Queen QueenList[kMaxQueens]; +Head QueenHead; + +int MoveQX[25]; +int MoveQY[25]; +int MoveQZ[25]; +short MoveQS[25]; +short MoveQA[25]; + + + +static SavegameHelper sgh("queen", + SV(QueenCount), + SV(nQHead), + SV(nEggsFree), + SV(nHeadVel), + SV(nVelShift), + SV(QueenHead), + SA(tailspr), + SA(nEggFree), + SA(QueenChan), + SA(QueenEgg), + SA(QueenList), + SA(MoveQX), + SA(MoveQY), + SA(MoveQZ), + SA(MoveQS), + SA(MoveQA), + nullptr); + + +void InitQueens() +{ + QueenCount = 1; + + for (int i = 0; i < kMaxEggs; i++) + { + nEggFree[i] = i; + QueenEgg[i].field_8 = -1; + } + + nEggsFree = kMaxEggs; +} + +int GrabEgg() +{ + if (!nEggsFree) { + return -1; + } + + nEggsFree--; + return nEggFree[nEggsFree]; +} + +void BlowChunks(int nSprite) +{ + for (int i = 0; i < 4; i++) + { + BuildCreatureChunk(nSprite, seq_GetSeqPicnum(16, i + 41, 0)); + } +} + +void DestroyEgg(short nEgg) +{ + short nSprite = QueenEgg[nEgg].nSprite; + + if (QueenEgg[nEgg].nAction != 4) + { + BuildAnim(-1, 34, 0, sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, sprite[nSprite].sectnum, sprite[nSprite].xrepeat, 4); + } + else + { + for (int i = 0; i < 4; i++) + { + BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqQueenEgg, (i % 2) + 24, 0)); + } + } + + runlist_DoSubRunRec(sprite[nSprite].owner); + runlist_DoSubRunRec(sprite[nSprite].lotag - 1); + runlist_SubRunRec(QueenEgg[nEgg].field_8); + + QueenEgg[nEgg].field_8 = -1; + + mydeletesprite(nSprite); + + nEggFree[nEggsFree] = nEgg; + nEggsFree++; +} + +void DestroyAllEggs() +{ + for (int i = 0; i < kMaxEggs; i++) + { + if (QueenEgg[i].field_8 > -1) + { + DestroyEgg(i); + } + } +} + +void SetHeadVel(short nSprite) +{ + short nAngle = sprite[nSprite].ang; + + if (nVelShift >= 0) + { + sprite[nSprite].xvel = Cos(nAngle) >> (int8_t)(nVelShift); + sprite[nSprite].yvel = Sin(nAngle) >> (int8_t)(nVelShift); + } + else + { + sprite[nSprite].xvel = Cos(nAngle) << (int8_t)(-nVelShift); + sprite[nSprite].yvel = Sin(nAngle) << (int8_t)(-nVelShift); + } +} + +int QueenAngleChase(short nSprite, short nSprite2, int val1, int val2) +{ + short nAngle; + + spritetype *pSprite = &sprite[nSprite]; + if (nSprite2 < 0) + { + pSprite->zvel = 0; + nAngle = pSprite->ang; + } + else + { + spritetype *pSprite2 = &sprite[nSprite2]; + int nTileY = (tilesiz[pSprite2->picnum].y * pSprite2->yrepeat) * 2; + + int nMyAngle = GetMyAngle(pSprite2->x - pSprite->x, pSprite2->y - pSprite->y); + + int edx = ((pSprite2->z - nTileY) - pSprite->z) >> 8; + + int x = pSprite2->x - pSprite->x; + int y = pSprite2->y - pSprite->y; + + int nSqrt = ksqrt(x * x + y * y); + + int var_14 = GetMyAngle(nSqrt, edx); + + int nAngDelta = AngleDelta(pSprite->ang, nMyAngle, 1024); + + if (klabs(nAngDelta) > 127) + { + val1 /= klabs(nAngDelta>>7); + if (val1 < 256) + val1 = 256; + } + + if (klabs(nAngDelta) > val2) + { + if (nAngDelta < 0) + nAngDelta = -val2; + else + nAngDelta = val2; + } + + nAngle = (nAngDelta + sprite[nSprite].ang) & kAngleMask; + + pSprite->zvel = (AngleDelta(pSprite->zvel, var_14, 24) + pSprite->zvel) & kAngleMask; + } + + pSprite->ang = nAngle; + + int da = pSprite->zvel; + int x = klabs(Cos(da)); + + int v26 = x * ((val1 * Cos(nAngle)) >> 14); + int v27 = x * ((val1 * Sin(nAngle)) >> 14); + + int nSqrt = ksqrt(((v26 >> 8) * (v26 >> 8)) + ((v27 >> 8) * (v27 >> 8))) * Sin(da); + + return movesprite(nSprite, v26 >> 2, v27 >> 2, (Sin(bobangle) >> 5) + (nSqrt >> 13), 0, 0, CLIPMASK1); +} + +int DestroyTailPart() +{ + if (!QueenHead.field_E) { + return 0; + } + + short nSprite = tailspr[--QueenHead.field_E]; + + BlowChunks(nSprite); + BuildExplosion(nSprite); + + for (int i = 0; i < 5; i++) + { + short nHeight = GetSpriteHeight(nSprite); + BuildLavaLimb(nSprite, i, nHeight); + } + + mydeletesprite(nSprite); + return 1; +} + +void BuildTail() +{ + short nSprite = QueenHead.nSprite; + + int x = sprite[nSprite].x; + int y = sprite[nSprite].x; + int z = sprite[nSprite].x; + short nSector = sprite[nSprite].sectnum; + + int i; + + for (i = 0; i < kMaxTails; i++) + { + short nTailSprite = insertsprite(nSector, 121); + tailspr[i] = nTailSprite; + + if (nTailSprite < 0) { + I_Error("Can't create queen's tail!\n"); + } + + sprite[nTailSprite].lotag = runlist_HeadRun() + 1; + sprite[nTailSprite].owner = runlist_AddRunRec(sprite[nTailSprite].lotag - 1, (i + 1) | 0x1B0000); + sprite[nTailSprite].shade = -12; + sprite[nTailSprite].x = x; + sprite[nTailSprite].y = y; + sprite[nTailSprite].hitag = 0; + sprite[nTailSprite].cstat = 0; + sprite[nTailSprite].clipdist = 100; + sprite[nTailSprite].xrepeat = 80; + sprite[nTailSprite].yrepeat = 80; + sprite[nTailSprite].picnum = 1; + sprite[nTailSprite].pal = sector[sprite[nTailSprite].sectnum].ceilingpal; + sprite[nTailSprite].xoffset = 0; + sprite[nTailSprite].yoffset = 0; + sprite[nTailSprite].z = z; + sprite[nTailSprite].extra = -1; + } + + for (i = 0; i < 24 + 1; i++) + { + MoveQX[i] = x; + MoveQZ[i] = z; + MoveQY[i] = y; + assert(nSector >= 0 && nSector < kMaxSectors); + MoveQS[i] = nSector; + } + + nQHead = 0; + QueenHead.field_E = 7; +} + +int BuildQueenEgg(short nQueen, int nVal) +{ + int nEgg = GrabEgg(); + if (nEgg < 0) { + return -1; + } + + short nSprite = QueenList[nQueen].nSprite; + + int x = sprite[nSprite].x; + int y = sprite[nSprite].y; + short nSector = sprite[nSprite].sectnum; + int nFloorZ = sector[nSector].floorz; + short nAngle = sprite[nSprite].ang; + + int nSprite2 = insertsprite(nSector, 121); + assert(nSprite2 >= 0 && nSprite2 < kMaxSprites); + + sprite[nSprite2].x = x; + sprite[nSprite2].y = y; + sprite[nSprite2].z = nFloorZ; + sprite[nSprite2].pal = 0; + sprite[nSprite2].clipdist = 50; + sprite[nSprite2].xoffset = 0; + sprite[nSprite2].yoffset = 0; + sprite[nSprite2].shade = -12; + sprite[nSprite2].picnum = 1; + sprite[nSprite2].ang = (RandomSize(9) + (nAngle - 256)) & kAngleMask; + + if (!nVal) + { + sprite[nSprite2].xrepeat = 30; + sprite[nSprite2].yrepeat = 30; + sprite[nSprite2].xvel = Cos(sprite[nSprite2].ang); + sprite[nSprite2].yvel = Sin(sprite[nSprite2].ang); + sprite[nSprite2].zvel = -6000; + sprite[nSprite2].cstat = 0; + } + else + { + sprite[nSprite2].xrepeat = 60; + sprite[nSprite2].yrepeat = 60; + sprite[nSprite2].xvel = 0; + sprite[nSprite2].yvel = 0; + sprite[nSprite2].zvel = -2000; + sprite[nSprite2].cstat = 0x101; + } + + sprite[nSprite2].lotag = runlist_HeadRun() + 1; + sprite[nSprite2].extra = -1; + sprite[nSprite2].hitag = 0; + + GrabTimeSlot(3); + + QueenEgg[nEgg].nHealth = 200; + QueenEgg[nEgg].field_2 = 0; + QueenEgg[nEgg].nSprite = nSprite2; + QueenEgg[nEgg].field_E = nVal; + QueenEgg[nEgg].nTarget = QueenList[nQueen].nTarget; + + if (nVal) + { + nVal = 4; + QueenEgg[nEgg].field_C = 200; + } + + QueenEgg[nEgg].nAction = nVal; + + sprite[nSprite2].owner = runlist_AddRunRec(sprite[nSprite2].lotag - 1, nEgg | 0x1D0000); + QueenEgg[nEgg].field_8 = runlist_AddRunRec(NewRun, nEgg | 0x1D0000); + + return 0; +} + +void FuncQueenEgg(int a, int nDamage, int nRun) +{ + short nEgg = RunData[nRun].nVal; + + int var_14 = 0; + + Egg *pEgg = &QueenEgg[nEgg]; + short nSprite = pEgg->nSprite; + short nAction = pEgg->nAction; + + short nTarget; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + case 0x20000: + { + if (pEgg->nHealth <= 0) + { + DestroyEgg(nEgg); + return; + } + + if (nAction == 0 || nAction == 4) { + Gravity(nSprite); + } + + short nSeq = SeqOffsets[kSeqQueenEgg] + EggSeq[nAction].a; + + sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, pEgg->field_2); + + if (nAction != 4) + { + seq_MoveSequence(nSprite, nSeq, pEgg->field_2); + + pEgg->field_2++; + if (pEgg->field_2 >= SeqSize[nSeq]) + { + pEgg->field_2 = 0; + var_14 = 1; + } + + nTarget = UpdateEnemy(&pEgg->nTarget); + pEgg->nTarget = nTarget; + + if (nTarget >= 0 && (sprite[nTarget].cstat & 0x101) == 0) + { + pEgg->nTarget = -1; + pEgg->nAction = 0; + } + else + { + nTarget = FindPlayer(-nSprite, 1000); + pEgg->nTarget = nTarget; + } + } + + switch (nAction) + { + case 0: + { + int nMov = MoveCreature(nSprite); + if (!nMov) { + break; + } + + if (nMov & 0x20000) + { + if (!RandomSize(1)) + { + pEgg->nAction = 1; + pEgg->field_2 = 0; + } + else + { + DestroyEgg(nEgg); + } + } + else + { + short nAngle; + + switch (nMov & 0xC000) + { + default: + return; + case 0x8000: + nAngle = GetWallNormal(nMov & 0x3FFF); + break; + case 0xC000: + nAngle = sprite[nMov & 0x3FFF].ang; + break; + } + + sprite[nSprite].ang = nAngle; + sprite[nSprite].xvel = Cos(nAngle) >> 1; + sprite[nSprite].yvel = Sin(nAngle) >> 1; + } + + break; + } + + case 1: + { + if (var_14) + { + pEgg->nAction = 3; + sprite[nSprite].cstat = 0x101; + } + break; + } + + case 2: + case 3: + { + int nMov = QueenAngleChase(nSprite, nTarget, nHeadVel, 64); + + switch (nMov & 0xC000) + { + case 0xC000: + if (sprite[nMov & 0x3FFF].statnum != 121) + { + runlist_DamageEnemy(nMov & 0x3FFF, nSprite, 5); + } + fallthrough__; + case 0x8000: + sprite[nSprite].ang += (RandomSize(9) + 768); + sprite[nSprite].ang &= kAngleMask; + sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 3; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 3; + sprite[nSprite].zvel = -RandomSize(5); + break; + } + + return; + } + + case 4: + { + int nMov = MoveCreature(nSprite); + + if (nMov & 0x20000) + { + sprite[nSprite].zvel = -(sprite[nSprite].zvel - 256); + if (sprite[nSprite].zvel < -512) + { + sprite[nSprite].zvel = 0; + } + } + + pEgg->field_C--; + if (pEgg->field_C <= 0) + { + short nWaspSprite = BuildWasp(-2, sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, sprite[nSprite].sectnum, sprite[nSprite].ang); + sprite[nSprite].z = sprite[nWaspSprite].z; + + DestroyEgg(nEgg); + } + break; + } + } + break; + } + + case 0xA0000: + { + if (sprite[nRadialSpr].statnum != 121 && (sprite[nSprite].cstat & 0x101) != 0) + { + nDamage = runlist_CheckRadialDamage(nSprite); + + pEgg->nHealth -= nDamage; + } + break; + } + + case 0x80000: + { + if (nDamage != 0 && pEgg->nHealth > 0) + { + QueenEgg[nEgg].nHealth -= nDamage; + + if (QueenEgg[nEgg].nHealth <= 0) + DestroyEgg(nEgg); + } + break; + } + + case 0x90000: + { + seq_PlotSequence(a & 0xFFFF, SeqOffsets[kSeqQueenEgg] + EggSeq[nAction].a, pEgg->field_2, EggSeq[nAction].b); + break; + } + + default: + { + DebugOut("unknown msg %d for Queenhead\n", a & 0x7F0000); + break; + } + } +} + +int BuildQueenHead(short nQueen) +{ + short nSprite = QueenList[nQueen].nSprite; + + int x = sprite[nSprite].x; + int y = sprite[nSprite].y; + short nAngle = sprite[nSprite].ang; + short nSector = sprite[nSprite].sectnum; + int z = sector[nSector].floorz; + + int nSprite2 = insertsprite(nSector, 121); + assert(nSprite2 >= 0 && nSprite2 < kMaxSprites); + + sprite[nSprite2].x = x; + sprite[nSprite2].y = y; + sprite[nSprite2].z = z; + sprite[nSprite2].clipdist = 70; + sprite[nSprite2].xrepeat = 80; + sprite[nSprite2].yrepeat = 80; + sprite[nSprite2].cstat = 0; + sprite[nSprite2].picnum = 1; + sprite[nSprite2].shade = -12; + sprite[nSprite2].pal = 0; + sprite[nSprite2].xoffset = 0; + sprite[nSprite2].yoffset = 0; + sprite[nSprite2].ang = nAngle; + + nVelShift = 2; + SetHeadVel(nSprite2); + + sprite[nSprite2].zvel = -8192; + sprite[nSprite2].lotag = runlist_HeadRun() + 1; + sprite[nSprite2].hitag = 0; + sprite[nSprite2].extra = -1; + + GrabTimeSlot(3); + + QueenHead.nHealth = 800; + QueenHead.nAction = 0; + QueenHead.nTarget = QueenList[nQueen].nTarget; + QueenHead.field_2 = 0; + QueenHead.nSprite = nSprite2; + QueenHead.field_C = 0; + + sprite[nSprite2].owner = runlist_AddRunRec(sprite[nSprite2].lotag - 1, 0x1B0000); + + QueenHead.field_8 = runlist_AddRunRec(NewRun, 0x1B0000); + QueenHead.field_E = 0; + + return 0; +} + +void FuncQueenHead(int a, int nDamage, int nRun) +{ + short nHead = RunData[nRun].nVal; + + short nSprite = QueenHead.nSprite; + int nSector = sprite[nSprite].sectnum; + assert(nSector >= 0 && nSector < kMaxSectors); + + short nAction = QueenHead.nAction; + + short nTarget, nHd; + + int var_14 = 0; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + case 0x20000: + { + if (nAction == 0) { + Gravity(nSprite); + } + + short nSeq = SeqOffsets[kSeqQueen] + HeadSeq[QueenHead.nAction].a; + + seq_MoveSequence(nSprite, nSeq, QueenHead.field_2); + + sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, QueenHead.field_2); + + QueenHead.field_2++; + if (QueenHead.field_2 >= SeqSize[nSeq]) + { + QueenHead.field_2 = 0; + var_14 = 1; + } + + nTarget = QueenHead.nTarget; + + if (nTarget > -1) + { + if (!(sprite[nTarget].cstat & 0x101)) + { + nTarget = -1; + QueenHead.nTarget = nTarget; + } + } + else + { + nTarget = FindPlayer(nSprite, 1000); + QueenHead.nTarget = nTarget; + } + + switch (nAction) + { + case 0: + if (QueenHead.field_C > 0) + { + QueenHead.field_C--; + if (QueenHead.field_C == 0) + { + BuildTail(); + + QueenHead.nAction = 6; + nHeadVel = 800; + sprite[nSprite].cstat = 0x101; + } + else if (QueenHead.field_C < 60) + { + sprite[nSprite].shade--; + } + } + else + { + int nMov = MoveCreature(nSprite); + + // original BUG - this line doesn't exist in original code? + short nNewAng = sprite[nSprite].ang; + + switch (nMov & 0xFC000) + { + default: + return; + case 0xC000: + nNewAng = sprite[nMov & 0x3FFF].ang; + break; + case 0x8000: + nNewAng = GetWallNormal(nMov & 0x3FFF); + break; + case 0x20000: + sprite[nSprite].zvel = -(sprite[nSprite].zvel >> 1); + + if (sprite[nSprite].zvel > -256) + { + nVelShift = 100; + sprite[nSprite].zvel = 0; + } + break; + } + + // original BUG - var_18 isn't being set if the check above == 0x20000 ? + sprite[nSprite].ang = nNewAng; + nVelShift++; + + if (nVelShift < 5) + { + SetHeadVel(nSprite); + } + else + { + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + + if (sprite[nSprite].zvel == 0) + { + QueenHead.field_C = 120; + } + } + } + + break; + + case 6: + if (var_14) + { + QueenHead.nAction = 1; + QueenHead.field_2 = 0; + break; + } + fallthrough__; + + case 1: + if ((sprite[nTarget].z - 51200) > sprite[nSprite].z) + { + QueenHead.nAction = 4; + QueenHead.field_2 = 0; + } + else + { + sprite[nSprite].z -= 2048; + goto __MOVEQS; + } + break; + + case 4: + case 7: + case 8: + if (var_14) + { + int nRnd = RandomSize(2); + + if (nRnd == 0) + { + QueenHead.nAction = 4; + } + else if (nRnd == 1) + { + QueenHead.nAction = 7; + } + else + { + QueenHead.nAction = 8; + } + } + + if (nTarget > -1) + { + int nMov = QueenAngleChase(nSprite, nTarget, nHeadVel, 64); + + switch (nMov & 0xC000) + { + case 0x8000: + break; + case 0xC000: + if ((nMov & 0x3FFF) == nTarget) + { + runlist_DamageEnemy(nTarget, nSprite, 10); + D3PlayFX(StaticSound[kSoundQTail] | 0x2000, nSprite); + + sprite[nSprite].ang += RandomSize(9) + 768; + sprite[nSprite].ang &= kAngleMask; + + sprite[nSprite].zvel = (-20) - RandomSize(6); + + SetHeadVel(nSprite); + } + break; + } + } + + // switch break. MoveQS stuff? +__MOVEQS: + MoveQX[nQHead] = sprite[nSprite].x; + MoveQY[nQHead] = sprite[nSprite].y; + MoveQZ[nQHead] = sprite[nSprite].z; + assert(sprite[nSprite].sectnum >= 0 && sprite[nSprite].sectnum < kMaxSectors); + MoveQS[nQHead] = sprite[nSprite].sectnum; + MoveQA[nQHead] = sprite[nSprite].ang; + + nHd = nQHead; + + for (int i = 0; i < QueenHead.field_E; i++) + { + nHd -= 3; + if (nHd < 0) { + nHd += (24 + 1); // TODO - enum/define for these + //assert(nHd < 24 && nHd >= 0); + } + + int var_20 = MoveQS[nHd]; + short nTSprite = tailspr[i]; + + if (var_20 != sprite[nTSprite].sectnum) + { + assert(var_20 >= 0 && var_20 < kMaxSectors); + mychangespritesect(nTSprite, var_20); + } + + sprite[nTSprite].x = MoveQX[nHd]; + sprite[nTSprite].y = MoveQY[nHd]; + sprite[nTSprite].z = MoveQZ[nHd]; + sprite[nTSprite].ang = MoveQA[nHd]; + } + + nQHead++; + if (nQHead >= 25) + { + nQHead = 0; + } + + break; + + case 5: + QueenHead.field_C--; + if (QueenHead.field_C <= 0) + { + QueenHead.field_C = 3; + + if (QueenHead.field_E--) + { + if (QueenHead.field_E >= 15 || QueenHead.field_E < 10) + { + int x = sprite[nSprite].x; + int y = sprite[nSprite].y; + int z = sprite[nSprite].z; + short nSector = sprite[nSprite].sectnum; + int nAngle = RandomSize(11) & kAngleMask; + + sprite[nSprite].xrepeat = 127 - QueenHead.field_E; + sprite[nSprite].yrepeat = 127 - QueenHead.field_E; + + sprite[nSprite].cstat = 0x8000; + + // DEMO-TODO: in disassembly angle was used without masking and thus causing OOB issue. + // This behavior probably would be needed emulated for demo compatibility + // int dx = sintable[nAngle + 512] << 10; + int dx = Cos(nAngle) << 10; + int dy = Sin(nAngle) << 10; + int dz = (RandomSize(5) - RandomSize(5)) << 7; + + movesprite(nSprite, dx, dy, dz, 0, 0, CLIPMASK1); + + BlowChunks(nSprite); + BuildExplosion(nSprite); + + mychangespritesect(nSprite, nSector); + + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + + if (QueenHead.field_E < 10) { + for (int i = (10 - QueenHead.field_E) * 2; i > 0; i--) + { + BuildLavaLimb(nSprite, i, GetSpriteHeight(nSprite)); + } + } + } + } + else + { + BuildExplosion(nSprite); + + int i; + + for (i = 0; i < 10; i++) + { + BlowChunks(nSprite); + } + + for (i = 0; i < 20; i++) + { + BuildLavaLimb(nSprite, i, GetSpriteHeight(nSprite)); + } + + runlist_SubRunRec(sprite[nSprite].owner); + runlist_SubRunRec(QueenHead.field_8); + mydeletesprite(nSprite); + runlist_ChangeChannel(QueenChan[0], 1); + } + } + break; + } + break; + } + + case 0xA0000: + if (sprite[nRadialSpr].statnum != 121 && (sprite[nSprite].cstat & 0x101) != 0) + { + nDamage = runlist_CheckRadialDamage(nSprite); + if (!nDamage) + break; + } + else + break; + // fall through to case 0x80000 + fallthrough__; + + case 0x80000: + if (QueenHead.nHealth > 0 && nDamage != 0) + { + QueenHead.nHealth -= nDamage; + + if (!RandomSize(4)) + { + QueenHead.nTarget = a & 0xFFFF; + QueenHead.nAction = 7; + QueenHead.field_2 = 0; + } + + if (QueenHead.nHealth <= 0) + { + if (DestroyTailPart()) + { + QueenHead.nHealth = 200; + nHeadVel += 100; + } + else + { + QueenHead.nAction = 5; + QueenHead.field_2 = 0; + QueenHead.field_C = 0; + QueenHead.field_E = 80; + sprite[nSprite].cstat = 0; + } + } + } + break; + + case 0x90000: + { + short nSeq = SeqOffsets[kSeqQueen]; + + int edx; + + if (nHead == 0) + { + edx = HeadSeq[nAction].b; + nSeq += HeadSeq[nAction].a; + } + else + { + edx = 1; + nSeq += 73; + } + + seq_PlotSequence(a & 0xFFFF, nSeq, QueenHead.field_2, edx); + break; + } + + default: + { + DebugOut("unknown msg %d for Queenhead\n", a & 0x7F0000); + break; + } + } +} + +int BuildQueen(int nSprite, int x, int y, int z, int nSector, int nAngle, int nChannel) +{ + QueenCount--; + + short nQueen = QueenCount; + if (nQueen < 0) { + return -1; + } + + if (nSprite == -1) + { + nSprite = insertsprite(nSector, 121); + } + else + { + changespritestat(nSprite, 121); + x = sprite[nSprite].x; + y = sprite[nSprite].y; + z = sector[sprite[nSprite].sectnum].floorz; + nAngle = sprite[nSprite].ang; + } + + assert(nSprite >= 0 && nSprite < kMaxSprites); + + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + sprite[nSprite].cstat = 0x101; + sprite[nSprite].pal = 0; + sprite[nSprite].shade = -12; + sprite[nSprite].clipdist = 100; + sprite[nSprite].xrepeat = 80; + sprite[nSprite].yrepeat = 80; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].picnum = 1; + sprite[nSprite].ang = nAngle; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].extra = -1; + sprite[nSprite].hitag = 0; + + GrabTimeSlot(3); + + QueenList[nQueen].nAction = 0; + QueenList[nQueen].nHealth = 4000; + QueenList[nQueen].field_2 = 0; + QueenList[nQueen].nSprite = nSprite; + QueenList[nQueen].nTarget = -1; + QueenList[nQueen].field_A = 0; + QueenList[nQueen].field_10 = 5; + QueenList[nQueen].field_C = 0; + + QueenChan[nQueen] = nChannel; + + nHeadVel = 800; + + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nQueen | 0x1A0000); + + runlist_AddRunRec(NewRun, nQueen | 0x1A0000); + + nCreaturesLeft++; + + return nQueen | 0x1A0000; +} + +void SetQueenSpeed(short nSprite, int nSpeed) +{ + sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> (2 - nSpeed); + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> (2 - nSpeed); +} + +void FuncQueen(int a, int nDamage, int nRun) +{ + int var_18 = 0; + + short nQueen = RunData[nRun].nVal; + assert(nQueen >= 0 && nQueen < kMaxQueens); + + int nMessage = a & 0x7F0000; + + short nSprite = QueenList[nQueen].nSprite; + short nAction = QueenList[nQueen].nAction; + short si = QueenList[nQueen].field_A; + short nTarget = QueenList[nQueen].nTarget; + + switch (nMessage) + { + case 0x20000: + { + if (si < 3) { + Gravity(nSprite); + } + + short nSeq = SeqOffsets[kSeqQueen] + ActionSeq[nAction].a; + + sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, QueenList[nQueen].field_2); + + seq_MoveSequence(nSprite, nSeq, QueenList[nQueen].field_2); + + QueenList[nQueen].field_2++; + if (QueenList[nQueen].field_2 >= SeqSize[nSeq]) + { + QueenList[nQueen].field_2 = 0; + var_18 = 1; + } + + short nFlag = FrameFlag[SeqBase[nSeq] + QueenList[nQueen].field_2]; + + if (nTarget > -1) + { + if (nAction < 7) + { + if (!(sprite[nSprite].cstat & 0x101)) + { + nTarget = -1; + QueenList[nQueen].nTarget = -1; + QueenList[nQueen].nAction = 0; + } + } + } + switch (nAction) + { + case 0: + { + if (nTarget < 0) + { + nTarget = FindPlayer(nSprite, 60); + } + + if (nTarget >= 0) + { + QueenList[nQueen].nAction = QueenList[nQueen].field_A + 1; + QueenList[nQueen].field_2 = 0; + QueenList[nQueen].nTarget = nTarget; + QueenList[nQueen].field_C = RandomSize(7); + + SetQueenSpeed(nSprite, si); + } + break; + } + + case 6: + { + if (var_18) + { + BuildQueenEgg(nQueen, 1); + QueenList[nQueen].nAction = 3; + QueenList[nQueen].field_C = RandomSize(6) + 60; + } + + break; + } + + case 1: + case 2: + case 3: + { + QueenList[nQueen].field_C--; + + if ((nQueen & 0x1F) == (totalmoves & 0x1F)) + { + if (si < 2) + { + if (QueenList[nQueen].field_C <= 0) + { + QueenList[nQueen].field_2 = 0; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + QueenList[nQueen].nAction = si + 4; + QueenList[nQueen].field_C = RandomSize(6) + 30; + break; + } + else + { + if (QueenList[nQueen].field_10 < 5) + { + QueenList[nQueen].field_10++; + } + + // then to PLOTSPRITE + } + } + else + { + if (QueenList[nQueen].field_C <= 0) + { + if (nWaspCount < 100) + { + QueenList[nQueen].nAction = 6; + QueenList[nQueen].field_2 = 0; + break; + } + else + { + QueenList[nQueen].field_C = 30000; + // then to PLOTSPRITE + } + } + } + + // loc_35B4B + PlotCourseToSprite(nSprite, nTarget); + SetQueenSpeed(nSprite, si); + } + + int nMov = MoveCreatureWithCaution(nSprite); + + switch (nMov & 0xC000) + { + case 0xC000: + if ((si == 2) && ((nMov & 0x3FFF) == nTarget)) + { + runlist_DamageEnemy(nTarget, nSprite, 5); + break; + } + fallthrough__; + case 0x8000: + sprite[nSprite].ang += 256; + sprite[nSprite].ang &= kAngleMask; + + SetQueenSpeed(nSprite, si); + break; + } + + // loc_35BD2 + if (nAction && nTarget != -1) + { + if (!(sprite[nTarget].cstat & 0x101)) + { + QueenList[nQueen].nAction = 0; + QueenList[nQueen].field_2 = 0; + QueenList[nQueen].field_C = 100; + QueenList[nQueen].nTarget = -1; + + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } + } + + break; + } + + case 4: + case 5: + { + if (var_18 && QueenList[nQueen].field_10 <= 0) + { + QueenList[nQueen].nAction = 0; + QueenList[nQueen].field_C = 15; + } + else + { + if (nFlag & 0x80) + { + QueenList[nQueen].field_10--; + + PlotCourseToSprite(nSprite, nTarget); + + if (!si) + { + BuildBullet(nSprite, 12, 0, 0, -1, sprite[nSprite].ang, nTarget + 10000, 1); + } + else + { + BuildQueenEgg(nQueen, 0); + } + } + } + + break; + } + + case 7: + { + if (var_18) + { + QueenList[nQueen].nAction = 0; + QueenList[nQueen].field_2 = 0; + } + + break; + } + + case 8: + case 9: + { + if (var_18) + { + if (nAction == 9) + { + QueenList[nQueen].field_C--; + if (QueenList[nQueen].field_C <= 0) + { + sprite[nSprite].cstat = 0; + + for (int i = 0; i < 20; i++) + { + short nChunkSprite = BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqQueen, 57, 0)) & 0xFFFF; + + sprite[nChunkSprite].picnum = kTile3117 + (i % 3); + sprite[nChunkSprite].yrepeat = 100; + sprite[nChunkSprite].xrepeat = 100; + } + + short nChunkSprite = BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqQueen, 57, 0)); + + sprite[nChunkSprite].picnum = kTile3126; + sprite[nChunkSprite].yrepeat = 100; + sprite[nChunkSprite].xrepeat = 100; + PlayFXAtXYZ( + StaticSound[kSound40], + sprite[nSprite].x, + sprite[nSprite].y, + sprite[nSprite].z, + sprite[nSprite].sectnum); + BuildQueenHead(nQueen); + + QueenList[nQueen].nAction++; + } + } + else + QueenList[nQueen].nAction++; + } + + break; + } + + case 10: + { + sprite[nSprite].cstat &= 0xFEFE; + break; + } + } + break; + } + + case 0xA0000: + { + if (sprite[nRadialSpr].statnum != 121 && (sprite[nSprite].cstat & 0x101) != 0) + { + nDamage = runlist_CheckRadialDamage(nSprite); + + if (!nDamage) { + break; + } + } + else + break; + + fallthrough__; + } // fall through to case 0x80000 + + case 0x80000: + { + if (QueenList[nQueen].nHealth > 0) + { + QueenList[nQueen].nHealth -= nDamage; + + if (QueenList[nQueen].nHealth <= 0) + { + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + + QueenList[nQueen].field_A++; + + switch (QueenList[nQueen].field_A) + { + case 1: + QueenList[nQueen].nHealth = 4000; + QueenList[nQueen].nAction = 7; + + BuildAnim(-1, 36, 0, sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z - 7680, sprite[nSprite].sectnum, sprite[nSprite].xrepeat, 4); + break; + case 2: + QueenList[nQueen].nHealth = 4000; + QueenList[nQueen].nAction = 7; + + DestroyAllEggs(); + break; + case 3: + QueenList[nQueen].nAction = 8; + QueenList[nQueen].nHealth = 0; + QueenList[nQueen].field_C = 5; + + nCreaturesLeft--; + break; + } + + QueenList[nQueen].field_2 = 0; + } + else + { + if (si > 0 && !RandomSize(4)) + { + QueenList[nQueen].nAction = 7; + QueenList[nQueen].field_2 = 0; + } + } + } + break; + } + + case 0x90000: + { + seq_PlotSequence(a & 0xFFFF, SeqOffsets[kSeqQueen] + ActionSeq[nAction].a, QueenList[nQueen].field_2, ActionSeq[nAction].b); + break; + } + + default: + { + DebugOut("unknown msg %d for Queen\n", a & 0x7F0000); + break; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/queen.h b/source/exhumed/src/queen.h new file mode 100644 index 000000000..67c7359b3 --- /dev/null +++ b/source/exhumed/src/queen.h @@ -0,0 +1,34 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __queen_h__ +#define __queen_h__ + +BEGIN_PS_NS + +void InitQueens(); + +int BuildQueen(int nSprite, int x, int y, int z, int nSector, int nAngle, int nVal); + +void FuncQueenEgg(int, int, int); +void FuncQueenHead(int, int, int); +void FuncQueen(int, int, int); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/ra.cpp b/source/exhumed/src/ra.cpp new file mode 100644 index 000000000..94db53d77 --- /dev/null +++ b/source/exhumed/src/ra.cpp @@ -0,0 +1,302 @@ +//------------------------------------------------------------------------- +/* +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 "ra.h" +#include "runlist.h" +#include "engine.h" +#include "exhumed.h" +#include "player.h" +#include "move.h" +#include "sequence.h" +#include "ps_input.h" +#include "gun.h" +#include "bullet.h" +#include + +BEGIN_PS_NS + +/* bjd - the content of the ra.* files originally resided in gun.c I think... */ + +//#define kMaxRA 8 + +RA Ra[kMaxPlayers]; // one Ra for each player +short RaCount; + +static actionSeq ActionSeq[] = { + {2, 1}, {0, 0}, {1, 0}, {2, 0} +}; + +static SavegameHelper sgh("ra", + SA(Ra), + SV(RaCount), + nullptr); + + +void FreeRa(short nPlayer) +{ + int nRun = Ra[nPlayer].field_4; + int nSprite = Ra[nPlayer].nSprite; + + runlist_SubRunRec(nRun); + runlist_DoSubRunRec(sprite[nSprite].owner); + runlist_FreeRun(sprite[nSprite].lotag - 1); + + mydeletesprite(nSprite); +} + +int BuildRa(short nPlayer) +{ + short nPlayerSprite = PlayerList[nPlayer].nSprite; + + int nSprite = insertsprite(sprite[nPlayerSprite].sectnum, 203); + + sprite[nSprite].cstat = 0x8000; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].extra = -1; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].hitag = 0; + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nPlayer | 0x210000); + sprite[nSprite].pal = 1; + sprite[nSprite].xrepeat = 64; + sprite[nSprite].yrepeat = 64; + sprite[nSprite].x = sprite[nPlayerSprite].x; + sprite[nSprite].y = sprite[nPlayerSprite].y; + sprite[nSprite].z = sprite[nPlayerSprite].z; + +// GrabTimeSlot(3); + + Ra[nPlayer].nSprite = nSprite; + + Ra[nPlayer].field_4 = runlist_AddRunRec(NewRun, nPlayer | 0x210000); + Ra[nPlayer].nTarget = -1; + Ra[nPlayer].field_2 = 0; + Ra[nPlayer].field_0 = 0; + Ra[nPlayer].field_C = 0; + Ra[nPlayer].field_E = nPlayer; + + return nPlayer | 0x210000; +} + +void InitRa() +{ + RaCount = 0; + memset(Ra, 0, sizeof(RA) * kMaxPlayers); +} + +void MoveRaToEnemy(short nPlayer) +{ + short nTarget = Ra[nPlayer].nTarget; + short nSprite = Ra[nPlayer].nSprite; + short field_0 = Ra[nPlayer].field_0; + + if (nTarget != -1) + { + if (!(sprite[nTarget].cstat & 0x101) || sprite[nTarget].sectnum == MAXSECTORS) + { + Ra[nPlayer].nTarget = -1; + if (!field_0 || field_0 == 3) { + return; + } + + Ra[nPlayer].field_0 = 3; + Ra[nPlayer].field_2 = 0; + return; + } + else + { + if (sprite[nSprite].sectnum != sprite[nTarget].sectnum) { + mychangespritesect(nSprite, sprite[nTarget].sectnum); + } + } + } + else + { + if (field_0 == 1 || field_0 == 2) + { + Ra[nPlayer].field_0 = 3; + Ra[nPlayer].field_2 = 0; + return; + } + + if (field_0) { + return; + } + + sprite[nSprite].cstat = 0x8000; + nTarget = PlayerList[nPlayer].nSprite; + } + + sprite[nSprite].x = sprite[nTarget].x; + sprite[nSprite].y = sprite[nTarget].y; + sprite[nSprite].z = sprite[nTarget].z - GetSpriteHeight(nTarget); + + if (sprite[nSprite].sectnum != sprite[nTarget].sectnum) { + mychangespritesect(nSprite, sprite[nTarget].sectnum); + } +} + +void FuncRa(int a, int UNUSED(nDamage), int nRun) +{ + short nPlayer = RunData[nRun].nVal; + short nCurrentWeapon = PlayerList[nPlayer].nCurrentWeapon; + + int var_14 = 0; + + short edx = SeqOffsets[kSeqEyeHit] + ActionSeq[Ra[nPlayer].field_0].a; + short nSprite = Ra[nPlayer].nSprite; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + default: + { + Printf("unknown msg %d for Ra\n", a & 0x7F0000); + return; + } + + case 0x30000: + case 0xA0000: + return; + + case 0x20000: + { + Ra[nPlayer].nTarget = sPlayerInput[nPlayer].nTarget; + sprite[nSprite].picnum = seq_GetSeqPicnum2(edx, Ra[nPlayer].field_2); + + if (Ra[nPlayer].field_0) + { + seq_MoveSequence(nSprite, edx, Ra[nPlayer].field_2); + + Ra[nPlayer].field_2++; + if (Ra[nPlayer].field_2 >= SeqSize[edx]) + { + Ra[nPlayer].field_2 = 0; + var_14 = 1; + } + } + + switch (Ra[nPlayer].field_0) + { + case 0: + { + MoveRaToEnemy(nPlayer); + + if (!Ra[nPlayer].field_C || Ra[nPlayer].nTarget <= -1) + { + sprite[nSprite].cstat = 0x8000; + } + else + { + sprite[nSprite].cstat &= 0x7FFF; + Ra[nPlayer].field_0 = 1; + Ra[nPlayer].field_2 = 0; + } + + return; + } + + case 1: + { + if (!Ra[nPlayer].field_C) + { + Ra[nPlayer].field_0 = 3; + Ra[nPlayer].field_2 = 0; + } + else + { + if (var_14) { + Ra[nPlayer].field_0 = 2; + } + + MoveRaToEnemy(nPlayer); + } + + return; + } + + case 2: + { + MoveRaToEnemy(nPlayer); + + if (nCurrentWeapon != kWeaponRing) + { + Ra[nPlayer].field_0 = 3; + Ra[nPlayer].field_2 = 0; + } + else + { + if (Ra[nPlayer].field_2 || Ra[nPlayer].nTarget <= -1) + { + if (!var_14) { + return; + } + + Ra[nPlayer].field_0 = 3; + Ra[nPlayer].field_2 = 0; + } + else + { + if (PlayerList[nPlayer].nAmmo[kWeaponRing] > 0) + { + runlist_DamageEnemy(Ra[nPlayer].nTarget, PlayerList[Ra[nPlayer].field_E].nSprite, BulletInfo[kWeaponRing].nDamage); + AddAmmo(nPlayer, kWeaponRing, -WeaponInfo[kWeaponRing].d); + SetQuake(nSprite, 100); + } + else + { + Ra[nPlayer].field_0 = 3; + Ra[nPlayer].field_2 = 0; + SelectNewWeapon(nPlayer); + } + } + } + + return; + } + + case 3: + { + if (var_14) + { + sprite[nSprite].cstat |= 0x8000; + Ra[nPlayer].field_0 = 0; + Ra[nPlayer].field_2 = 0; + Ra[nPlayer].field_C = 0; + } + + return; + } + + default: + return; + } + } + + case 0x90000: + { + short nSprite2 = a & 0xFFFF; + seq_PlotSequence(nSprite2, edx, Ra[nPlayer].field_2, 1); + tsprite[nSprite2].owner = -1; + return; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/ra.h b/source/exhumed/src/ra.h new file mode 100644 index 000000000..4dbea3b6f --- /dev/null +++ b/source/exhumed/src/ra.h @@ -0,0 +1,46 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __ra_h__ +#define __ra_h__ + +BEGIN_PS_NS + +struct RA +{ + short field_0; + short field_2; + short field_4; + short nSprite; + short nTarget; + short field_A; + short field_C; + short field_E; +}; + +extern RA Ra[]; + +void FreeRa(short nPlayer); +int BuildRa(short nPlayer); +void InitRa(); +void MoveRaToEnemy(short nPlayer); +void FuncRa(int, int, int); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/random.cpp b/source/exhumed/src/random.cpp new file mode 100644 index 000000000..3f5a0ce2d --- /dev/null +++ b/source/exhumed/src/random.cpp @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------- +/* +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 "random.h" +#include "exhumed.h" + +BEGIN_PS_NS + +int randA = 0; +int randB = 0x11111111; +int randC = 0x1010101; + +static SavegameHelper sgh("rand", + SV(randA), + SV(randB), + SV(randC), + nullptr); + + +void InitRandom() +{ + randA = 0; + randB = 0x11111111; + randC = 0x1010101; +} + +// TODO - checkme +int RandomBit() +{ + randA = (randA >> 1) | (((randA ^ ((randA >> 1) ^ (randA >> 2) ^ (randA >> 31) ^ (randA >> 6) ^ (randA >> 4))) & 1) << 31); + randB = (randB >> 1) | ((((randB >> 2) ^ (randB >> 30)) & 1) << 30); + randC = (randC >> 1) | ((((randC >> 1) ^ (randC >> 28)) & 1) << 28); + return ((randA == 0) & randC | (randB & randA)) & 1; +} + +char RandomByte() +{ + char randByte = RandomBit() << 7; + randByte |= RandomBit() << 6; + randByte |= RandomBit() << 5; + randByte |= RandomBit() << 4; + randByte |= RandomBit() << 3; + randByte |= RandomBit() << 2; + randByte |= RandomBit() << 1; + randByte |= RandomBit(); + return randByte; +} + +uint16_t RandomWord() +{ + short randWord = RandomByte() << 8; + randWord |= RandomByte(); + return randWord; +} + +int RandomLong() +{ + int randLong = RandomWord() << 16; + randLong |= RandomWord(); + return randLong; +} + +int RandomSize(int nSize) +{ + int randSize = 0; + + while (nSize > 0) + { + randSize = randSize * 2 | RandomBit(); + nSize--; + } + + return randSize; +} +END_PS_NS diff --git a/source/exhumed/src/random.h b/source/exhumed/src/random.h new file mode 100644 index 000000000..cec5a7abf --- /dev/null +++ b/source/exhumed/src/random.h @@ -0,0 +1,34 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __random_h__ +#define __random_h__ +#include "compat.h" + +BEGIN_PS_NS + +void InitRandom(); +int RandomBit(); +char RandomByte(); +uint16_t RandomWord(); +int RandomLong(); +int RandomSize(int nSize); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/rat.cpp b/source/exhumed/src/rat.cpp new file mode 100644 index 000000000..9b31ec6b8 --- /dev/null +++ b/source/exhumed/src/rat.cpp @@ -0,0 +1,395 @@ +//------------------------------------------------------------------------- +/* +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 "engine.h" +#include "rat.h" +#include "sequence.h" +#include "runlist.h" +#include "random.h" +#include "view.h" +#include "init.h" +#include "exhumed.h" +#include "move.h" +#include + +BEGIN_PS_NS + +#define kMaxRats 50 + +short nMinChunk; +short nPlayerPic; +short nRatCount; +short nMaxChunk; + +struct Rat +{ + short a; + short nAction; + short nSprite; + short d; + short nTarget; + short f; + short g; +// short _pad; +}; + +Rat RatList[kMaxRats]; + +static actionSeq ActionSeq[] = {{0, 1}, {1, 0}, {1, 0}, {9, 1}, {0, 1}}; + + +static SavegameHelper sgh("rat", + SV(nMinChunk), + SV(nPlayerPic), + SV(nRatCount), + SV(nMaxChunk), + SA(RatList), + nullptr); + +void InitRats() +{ + nRatCount = 0; + nMinChunk = 9999; + nMaxChunk = -1; + + for (int i = 122; i <= 131; i++) + { + int nPic = seq_GetSeqPicnum(kSeqJoe, i, 0); + + if (nPic < nMinChunk) + nMinChunk = nPic; + + if (nPic > nMaxChunk) + nMaxChunk = nPic; + } + + nPlayerPic = seq_GetSeqPicnum(kSeqJoe, 120, 0); +} + +void SetRatVel(short nSprite) +{ + sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 2; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 2; +} + +int BuildRat(short nSprite, int x, int y, int z, short nSector, int nAngle) +{ + if (nRatCount >= kMaxRats) { + return -1; + } + + short nRat = nRatCount++; + + if (nSprite < 0) + { + nSprite = insertsprite(nSector, 108); + assert(nSprite >= 0 && nSprite < kMaxSprites); + + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + } + else + { + nAngle = sprite[nSprite].ang; + changespritestat(nSprite, 108); + } + + sprite[nSprite].cstat = 0x101; + sprite[nSprite].shade = -12; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].picnum = 1; + sprite[nSprite].pal = sector[sprite[nSprite].sectnum].ceilingpal; + sprite[nSprite].clipdist = 30; + sprite[nSprite].ang = nAngle; + sprite[nSprite].xrepeat = 50; + sprite[nSprite].yrepeat = 50; + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].hitag = 0; + sprite[nSprite].extra = -1; + + if (nAngle >= 0) { + RatList[nRat].nAction = 2; + } + else { + RatList[nRat].nAction = 4; + } + + RatList[nRat].a = 0; + RatList[nRat].nSprite = nSprite; + RatList[nRat].nTarget = -1; + RatList[nRat].f = RandomSize(5); + RatList[nRat].g = RandomSize(3); + + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nRat | 0x240000); + + RatList[nRat].d = runlist_AddRunRec(NewRun, nRat | 0x240000); + return 0; +} + +int FindFood(short nSprite) +{ + short nSector = sprite[nSprite].sectnum; + int x = sprite[nSprite].x; + int y = sprite[nSprite].y; + int z = sprite[nSprite].z; + + int z2 = (z + sector[nSector].ceilingz) / 2; + + if (nChunkTotal) + { + int nSprite2 = nChunkSprite[RandomSize(7) % nChunkTotal]; + if (nSprite2 != -1) + { + if (cansee(x, y, z2, nSector, sprite[nSprite2].x, sprite[nSprite2].y, sprite[nSprite2].z, sprite[nSprite2].sectnum)) { + return nSprite2; + } + } + } + + if (!nBodyTotal) { + return -1; + } + + int nSprite2 = nBodySprite[RandomSize(7) % nBodyTotal]; + if (nSprite2 != -1) + { + if (nPlayerPic == sprite[nSprite2].picnum) + { + if (cansee(x, y, z, nSector, sprite[nSprite2].x, sprite[nSprite2].y, sprite[nSprite2].z, sprite[nSprite2].sectnum)) { + return nSprite2; + } + } + } + + return -1; +} + +void FuncRat(int a, int nDamage, int nRun) +{ + short nRat = RunData[nRun].nVal; + short nSprite = RatList[nRat].nSprite; + short nAction = RatList[nRat].nAction; + + bool var_20 = false; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + default: + { + Printf("unknown msg %d for Rathead\n", nMessage); + return; + } + + case 0xA0000: + { + nDamage = runlist_CheckRadialDamage(nSprite); + // fall through to 0x80000 + fallthrough__; + } + case 0x80000: + { + if (nDamage) + { + sprite[nSprite].cstat = 0; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + RatList[nRat].nAction = 3; + RatList[nRat].a = 0; + } + return; + } + + case 0x90000: + { + seq_PlotSequence(a & 0xFFFF, SeqOffsets[kSeqRat] + ActionSeq[nAction].a, RatList[nRat].a, ActionSeq[nAction].b); + return; + } + + case 0x20000: + { + int nSeq = SeqOffsets[kSeqRat] + ActionSeq[nAction].a; + sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, RatList[nRat].a); + + seq_MoveSequence(nSprite, nSeq, RatList[nRat].a); + + RatList[nRat].a++; + if (RatList[nRat].a >= SeqSize[nSeq]) + { + var_20 = true; + RatList[nRat].a = 0; + } + + short nTarget = RatList[nRat].nTarget; + + Gravity(nSprite); + + switch (nAction) + { + default: + return; + + case 0: + { + RatList[nRat].f--; + if (RatList[nRat].f > 0) { + return; + } + + int xVal = klabs(sprite[nSprite].x - sprite[nTarget].x); + int yVal = klabs(sprite[nSprite].y - sprite[nTarget].y); + + if (xVal > 50 || yVal > 50) + { + RatList[nRat].nAction = 2; + RatList[nRat].a = 0; + RatList[nRat].nTarget = -1; + + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + return; + } + + RatList[nRat].a ^= 1; + RatList[nRat].f = RandomSize(5) + 4; + RatList[nRat].g--; + + if (RatList[nRat].g <= 0) + { + short nFoodSprite = FindFood(nSprite); + if (nFoodSprite == -1) { + return; + } + + RatList[nRat].nTarget = nFoodSprite; + + PlotCourseToSprite(nSprite, nFoodSprite); + SetRatVel(nSprite); + + RatList[nRat].nAction = 1; + RatList[nRat].g = 900; + RatList[nRat].a = 0; + } + + return; + } + case 1: + { + RatList[nRat].g--; + + if (RatList[nRat].g <= 0) + { + RatList[nRat].nAction = 2; + RatList[nRat].a = 0; + RatList[nRat].nTarget = -1; + + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } + + MoveCreature(nSprite); + + int xVal = klabs(sprite[nSprite].x - sprite[nTarget].x); + int yVal = klabs(sprite[nSprite].y - sprite[nTarget].y); + + if (xVal >= 50 || yVal >= 50) + { + RatList[nRat].f--; + if (RatList[nRat].f < 0) + { + PlotCourseToSprite(nSprite, nTarget); + SetRatVel(nSprite); + + RatList[nRat].f = 32; + } + + return; + } + + RatList[nRat].nAction = 0; + RatList[nRat].a = 0; + RatList[nRat].g = RandomSize(3); + + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + return; + } + case 2: + { + if (sprite[nSprite].xvel || sprite[nSprite].yvel || sprite[nSprite].zvel) { + MoveCreature(nSprite); + } + + RatList[nRat].f--; + if (RatList[nRat].f <= 0) + { + RatList[nRat].nTarget = FindFood(nSprite); + + if (RatList[nRat].nTarget <= -1) + { + RatList[nRat].f = RandomSize(6); + if (sprite[nSprite].xvel || sprite[nSprite].yvel) + { + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + return; + } + + sprite[nSprite].ang = RandomSize(11); + SetRatVel(nSprite); + return; + } + else + { + PlotCourseToSprite(nSprite, RatList[nRat].nTarget); + SetRatVel(nSprite); + RatList[nRat].nAction = 1; + RatList[nRat].g = 900; + RatList[nRat].a = 0; + return; + } + } + + return; + } + case 3: + { + if (var_20) + { + runlist_DoSubRunRec(sprite[nSprite].owner); + runlist_FreeRun(sprite[nSprite].lotag - 1); + runlist_SubRunRec(RatList[nRat].d); + + sprite[nSprite].cstat = 0x8000; + mydeletesprite(nSprite); + } + return; + } + } + + break; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/rat.h b/source/exhumed/src/rat.h new file mode 100644 index 000000000..6bf83c85a --- /dev/null +++ b/source/exhumed/src/rat.h @@ -0,0 +1,32 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __rat_h__ +#define __rat_h__ + +BEGIN_PS_NS + +void InitRats(); +void SetRatVel(short nSprite); +int BuildRat(short nSprite, int x, int y, int z, short nSector, int nAngle); +int FindFood(short nSprite); +void FuncRat(int a, int b, int nRun); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/record.cpp b/source/exhumed/src/record.cpp new file mode 100644 index 000000000..2aca7a446 --- /dev/null +++ b/source/exhumed/src/record.cpp @@ -0,0 +1,173 @@ +//------------------------------------------------------------------------- +/* +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 "record.h" +#include "typedefs.h" +#include "save.h" +#include +#include + +BEGIN_PS_NS + +short record_mode = 0; +int record_limit = -1; +int record_index = 16384; +uint8_t record_buffer[16384]; +FILE *record_file; + +struct RecordHeader +{ + char signature[4]; + short b; +}; + +RecordHeader record_head; + + +uint8_t GetRecord() +{ + if (record_index >= 16384) + { + record_index = 0; + + int nRead = fread(record_buffer, 1, 16384, record_file); + + if (nRead < 16384) { + record_limit = 16384; + } + else { + record_limit = -1; + } + } + + if (record_limit > 0) + { + if (record_limit <= record_index) + { + record_mode = 3; + } + } + + return record_buffer[record_index++]; +} + +void PutRecord(uint8_t record) +{ + uint8_t val_10 = record; + + if (record_index >= 16384) + { + record_index = 0; + + fwrite(record_buffer, 16384, 1, record_file); + } + + record_buffer[record_index++] = val_10; +} + +int OpenRecord(const char *filename, short *edx) +{ + record_file = fopen(filename, "rb"); + if (record_file) + { + if (!fread(&record_head, sizeof(record_head), 1, record_file)) { + return 0; + } + + if (memcmp(record_head.signature, "LOBO", 4) == 0) { + return 0; + } + + *edx = record_head.b; + + record_index = 16384; + record_limit = -1; + record_mode = 2; + + return 1; + } + else + { + record_file = fopen(filename, "wb"); + if (!record_file) { + return 0; + } + + strncpy(record_head.signature, "LOBO", 4); + record_head.b = *edx; + + if (!fwrite(&record_head, sizeof(record_head), 1, record_file)) { + return 0; + } + + record_index = 0; + record_limit = -1; + record_mode = 1; + + return 1; + } +} + +int ExecRecord(uint8_t *pRecord, int nSize) +{ + if (record_mode == 2) + { + for (int i = 0; i < nSize; i++) + { + pRecord[i] = GetRecord(); + } + } + else if (record_mode == 1) + { + for (int i = 0; i < nSize; i++) + { + PutRecord(pRecord[i]); + } + } + + if (record_mode == 3) { + return 0; + } + else { + return 1; + } +} + +int CloseRecord() +{ + if (record_mode == 1) + { + //loadgame(0); ??? + + if (record_index) + { + if (!fwrite(record_buffer, record_index, 1, record_file)) { + return 0; + } + } + } + else if (record_mode == 2 || record_mode == 3) + { + //loadgame(1); ??? + } + + fclose(record_file); + return 1; +} +END_PS_NS diff --git a/source/exhumed/src/record.h b/source/exhumed/src/record.h new file mode 100644 index 000000000..8e12f12ad --- /dev/null +++ b/source/exhumed/src/record.h @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __record_h__ +#define __record_h__ + +BEGIN_PS_NS + + +extern short record_mode; + + +END_PS_NS + +#endif diff --git a/source/exhumed/src/rex.cpp b/source/exhumed/src/rex.cpp new file mode 100644 index 000000000..63aa1e015 --- /dev/null +++ b/source/exhumed/src/rex.cpp @@ -0,0 +1,491 @@ +//------------------------------------------------------------------------- +/* +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 "rex.h" +#include "exhumed.h" +#include "engine.h" +#include "runlist.h" +#include "move.h" +#include "sequence.h" +#include "sound.h" +#include "random.h" +#include "trigdat.h" +#include "player.h" +#include "aistuff.h" +#include + +BEGIN_PS_NS + +#define kMaxRex 50 + +short RexCount = 0; +short RexChan[kMaxRex]; + +struct Rex +{ + short nHealth; + short field_2; + short nAction; + short nSprite; + short nTarget; + short field_A; +}; + +Rex RexList[kMaxRex]; + +static actionSeq ActionSeq[] = { + {29, 0}, + {0, 0}, + {0, 0}, + {37, 0}, + {9, 0}, + {18, 0}, + {27, 1}, + {28, 1} +}; + + +static SavegameHelper sgh("rex", + SV(RexCount), + SA(RexChan), + SA(RexList), + nullptr); + + +void InitRexs() +{ + RexCount = kMaxRex; +} + +int BuildRex(short nSprite, int x, int y, int z, short nSector, short nAngle, int nVal) +{ + RexCount--; + + int nRex = RexCount; + if (nRex < 0) { + return -1; + } + + if (nSprite == -1) + { + nSprite = insertsprite(nSector, 119); + } + else + { + changespritestat(nSprite, 119); + x = sprite[nSprite].x; + y = sprite[nSprite].y; + z = sector[sprite[nSprite].sectnum].floorz; + nAngle = sprite[nSprite].ang; + } + + assert(nSprite >= 0 && nSprite < kMaxSprites); + + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + sprite[nSprite].cstat = 0x101; + sprite[nSprite].clipdist = 80; + sprite[nSprite].shade = -12; + sprite[nSprite].xrepeat = 64; + sprite[nSprite].yrepeat = 64; + sprite[nSprite].picnum = 1; + sprite[nSprite].pal = sector[sprite[nSprite].sectnum].ceilingpal; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].ang = nAngle; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].extra = -1; + sprite[nSprite].hitag = 0; + + GrabTimeSlot(3); + + RexList[nRex].nAction = 0; + RexList[nRex].nHealth = 4000; + RexList[nRex].field_2 = 0; + RexList[nRex].nSprite = nSprite; + RexList[nRex].nTarget = -1; + RexList[nRex].field_A = 0; + RexChan[nRex] = nVal; + + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nRex | 0x180000); + + // this isn't stored anywhere... + runlist_AddRunRec(NewRun, nRex | 0x180000); + + nCreaturesLeft++; + + return nRex | 0x180000; +} + +void FuncRex(int a, int nDamage, int nRun) +{ + short nRex = RunData[nRun].nVal; + assert(nRex >= 0 && nRex < kMaxRex); + + int var_1C = 0; + + short nAction = RexList[nRex].nAction; + short nSprite = RexList[nRex].nSprite; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + default: + { + Printf("unknown msg %d for Rex\n", a & 0x7F0000); + return; + } + + case 0xA0000: + { + if (nAction == 5) + { + nDamage = runlist_CheckRadialDamage(nSprite); + } + // fall through to case 0x80000 + fallthrough__; + } + + case 0x80000: + { + if (nDamage) + { + short nTarget = a & 0xFFFF; + if (nTarget >= 0 && sprite[nTarget].statnum == 100) + { + RexList[nRex].nTarget = nTarget; + } + + if (RexList[nRex].nAction == 5 && RexList[nRex].nHealth > 0) + { + RexList[nRex].nHealth -= nDamage; + + if (RexList[nRex].nHealth <= 0) + { + sprite[nSprite].zvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + RexList[nRex].nHealth = 0; + sprite[nSprite].cstat &= 0xFEFE; + nCreaturesLeft--; + + if (nAction < 6) + { + RexList[nRex].nAction = 6; + RexList[nRex].field_2 = 0; + } + } + } + } + return; + } + + case 0x90000: + { + seq_PlotSequence(a & 0xFFFF, SeqOffsets[kSeqRex] + ActionSeq[nAction].a, RexList[nRex].field_2, ActionSeq[nAction].b); + return; + } + + case 0x20000: + { + Gravity(nSprite); + + int nSeq = SeqOffsets[kSeqRex] + ActionSeq[nAction].a; + + sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, RexList[nRex].field_2); + + int ecx; + + if (nAction != 2) { + ecx = 1; + } + else { + ecx = 2; + } + + // moves the mouth open and closed as it's idle? + while (--ecx != -1) + { + seq_MoveSequence(nSprite, nSeq, RexList[nRex].field_2); + + RexList[nRex].field_2++; + if (RexList[nRex].field_2 >= SeqSize[nSeq]) + { + RexList[nRex].field_2 = 0; + var_1C = 1; + } + } + + int nFlag = FrameFlag[SeqBase[nSeq] + RexList[nRex].field_2]; + + short nTarget = RexList[nRex].nTarget; + + switch (nAction) + { + default: + return; + + // OK + case 0: + { + if (!RexList[nRex].field_A) + { + if ((nRex & 0x1F) == (totalmoves & 0x1F)) + { + if (nTarget < 0) + { + short nAngle = sprite[nSprite].ang; // make backup of this variable + RexList[nRex].nTarget = FindPlayer(nSprite, 60); + sprite[nSprite].ang = nAngle; + } + else + { + RexList[nRex].field_A = 60; + } + } + } + else + { + RexList[nRex].field_A--; + if (RexList[nRex].field_A <= 0) + { + RexList[nRex].nAction = 1; + RexList[nRex].field_2 = 0; + + sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 2; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 2; + + D3PlayFX(StaticSound[kSound48], nSprite); + + RexList[nRex].field_A = 30; + } + } + + return; + } + + // OK + case 1: + { + if (RexList[nRex].field_A > 0) + { + RexList[nRex].field_A--; + } + + if ((nRex & 0x0F) == (totalmoves & 0x0F)) + { + if (!RandomSize(1)) + { + RexList[nRex].nAction = 5; + RexList[nRex].field_2 = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + return; + } + else + { + if (((PlotCourseToSprite(nSprite, nTarget) >> 8) >= 60) || RexList[nRex].field_A > 0) + { + sprite[nSprite].xvel = Sin((sprite[nSprite].ang & 0xFFF8) + 512) >> 2; + sprite[nSprite].yvel = Sin((sprite[nSprite].ang & 0xFFF8)) >> 2; + } + else + { + RexList[nRex].nAction = 2; + RexList[nRex].field_A = 240; + D3PlayFX(StaticSound[kSound48], nSprite); + RexList[nRex].field_2 = 0; + return; + } + } + } + + int nVal = MoveCreatureWithCaution(nSprite); + + switch ((nVal & 0xC000)) + { + case 0xc000: + if ((nVal & 0x3FFF) == nTarget) + { + PlotCourseToSprite(nSprite, nTarget); + RexList[nRex].nAction = 4; + RexList[nRex].field_2 = 0; + break; + } + fallthrough__; + case 0x8000: + sprite[nSprite].ang = (sprite[nSprite].ang + 256) & kAngleMask; + sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 2; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 2; + RexList[nRex].nAction = 1; + RexList[nRex].field_2 = 0; + nAction = 1; + break; + } + + break; + } + + case 2: + { + RexList[nRex].field_A--; + if (RexList[nRex].field_A > 0) + { + PlotCourseToSprite(nSprite, nTarget); + + sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 1; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 1; + + int nVal = MoveCreatureWithCaution(nSprite); + + switch(nVal & 0x0C000) + { + case 0x8000: + SetQuake(nSprite, 25); + RexList[nRex].field_A = 60; + + sprite[nSprite].ang = (sprite[nSprite].ang + 256) & kAngleMask; + sprite[nSprite].xvel = Cos(sprite[nSprite].ang) >> 2; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 2; + RexList[nRex].nAction = 1; + RexList[nRex].field_2 = 0; + nAction = 1; + break; + case 0xc000: + RexList[nRex].nAction = 3; + RexList[nRex].field_2 = 0; + + short nSprite2 = nVal & 0x3FFF; + + if (sprite[nSprite2].statnum && sprite[nSprite2].statnum < 107) + { + short nAngle = sprite[nSprite].ang; + + runlist_DamageEnemy(nSprite2, nSprite, 15); + + int ebx = Cos(nAngle) * 15; + int edx = Sin(nAngle) * 15; + + if (sprite[nSprite2].statnum == 100) + { + short nPlayer = GetPlayerFromSprite(nSprite2); + nXDamage[nPlayer] += (ebx << 4); + nYDamage[nPlayer] += (edx << 4); + sprite[nSprite2].zvel = -3584; + } + else + { + sprite[nSprite2].xvel += (ebx >> 3); + sprite[nSprite2].yvel += (edx >> 3); + sprite[nSprite2].zvel = -2880; + } + } + + RexList[nRex].field_A >>= 2; + return; + } + } + else + { + RexList[nRex].nAction = 1; + RexList[nRex].field_2 = 0; + RexList[nRex].field_A = 90; + } + + return; + } + + case 3: + { + if (var_1C) + { + RexList[nRex].nAction = 2; + } + return; + } + + case 4: + { + if (nTarget != -1) + { + if (PlotCourseToSprite(nSprite, nTarget) < 768) + { + if (nFlag & 0x80) + { + runlist_DamageEnemy(nTarget, nSprite, 15); + } + + break; + } + } + + RexList[nRex].nAction = 1; + break; + } + + case 5: + { + if (var_1C) + { + RexList[nRex].nAction = 1; + RexList[nRex].field_A = 15; + } + return; + } + + case 6: + { + if (var_1C) + { + RexList[nRex].nAction = 7; + RexList[nRex].field_2 = 0; + runlist_ChangeChannel(RexChan[nRex], 1); + } + return; + } + + case 7: + { + sprite[nSprite].cstat &= 0xFEFE; + return; + } + } + + // break-ed + if (nAction > 0) + { + if ((nTarget != -1) && (!(sprite[nTarget].cstat & 0x101))) + { + RexList[nRex].nAction = 0; + RexList[nRex].field_2 = 0; + RexList[nRex].field_A = 0; + RexList[nRex].nTarget = -1; + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + } + } + return; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/rex.h b/source/exhumed/src/rex.h new file mode 100644 index 000000000..4dfdb46c0 --- /dev/null +++ b/source/exhumed/src/rex.h @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __rex_h__ +#define __rex_h__ + +BEGIN_PS_NS + +void InitRexs(); +int BuildRex(short nSprite, int x, int y, int z, short nSector, short nAngle, int nVal); +void FuncRex(int, int, int); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/roach.cpp b/source/exhumed/src/roach.cpp new file mode 100644 index 000000000..399244677 --- /dev/null +++ b/source/exhumed/src/roach.cpp @@ -0,0 +1,421 @@ +//------------------------------------------------------------------------- +/* +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 "exhumed.h" +#include "engine.h" +#include "runlist.h" +#include "roach.h" +#include "typedefs.h" +#include "sequence.h" +#include "move.h" +#include "random.h" +#include "trigdat.h" +#include "bullet.h" +#include "items.h" +#include + +BEGIN_PS_NS + +#define kMaxRoach 100 + +int16_t RoachSprite = -1; +int16_t RoachCount = -1; + +static actionSeq ActionSeq[] = {{ 24, 0 }, { 0, 0 }, { 0, 0 }, { 16, 0 }, { 8, 0 }, { 32, 1 }, { 42, 1 }}; + +struct Roach +{ + short nHealth; + short field_2; + short nAction; + short nSprite; + short nTarget; + short field_A; + short field_C; + short field_E; +}; + +Roach RoachList[kMaxRoach]; + +static SavegameHelper sgh("roach", + SV(RoachSprite), + SV(RoachCount), + SA(RoachList), + nullptr); + + +/* Kilmaat Sentry */ + +void InitRoachs() +{ + RoachCount = kMaxRoach; + RoachSprite = 1; +} + +// TODO - make nType a bool? +int BuildRoach(int nType, int nSprite, int x, int y, int z, short nSector, int angle) +{ + RoachCount--; + if (RoachCount < 0) { + return -1; + } + + if (nSprite == -1) + { + nSprite = insertsprite(nSector, 105); + } + else + { + changespritestat(nSprite, 105); + x = sprite[nSprite].x; + y = sprite[nSprite].y; + z = sector[sprite[nSprite].sectnum].floorz; + angle = sprite[nSprite].ang; + } + + assert(nSprite >= 0 && nSprite < kMaxSprites); + + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + sprite[nSprite].cstat = 0x101; + sprite[nSprite].shade = -12; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].picnum = 1; + sprite[nSprite].pal = sector[sprite[nSprite].sectnum].ceilingpal; + sprite[nSprite].clipdist = 60; + sprite[nSprite].ang = angle; + sprite[nSprite].xrepeat = 40; + sprite[nSprite].yrepeat = 40; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].hitag = 0; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].extra = -1; + + // GrabTimeSlot(3); + + if (nType) + { + RoachList[RoachCount].nAction = 0; + } + else + { + RoachList[RoachCount].nAction = 1; + } + + RoachList[RoachCount].nSprite = nSprite; + RoachList[RoachCount].field_2 = 0; + RoachList[RoachCount].field_C = 0; + RoachList[RoachCount].nTarget = -1; + RoachList[RoachCount].nHealth = 600; + + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, RoachCount | 0x1C0000); + RoachList[RoachCount].field_A = runlist_AddRunRec(NewRun, RoachCount | 0x1C0000); + + nCreaturesLeft++; + + return RoachCount | 0x1C0000; +} + +void GoRoach(short nSprite) +{ + sprite[nSprite].xvel = (Sin(sprite[nSprite].ang + 512) >> 1) - (Sin(sprite[nSprite].ang + 512) >> 3); + sprite[nSprite].yvel = (Sin(sprite[nSprite].ang) >> 1) - (Sin(sprite[nSprite].ang) >> 3); +} + +void FuncRoach(int a, int nDamage, int nRun) +{ + short nRoach = RunData[nRun].nVal; + assert(nRoach >= 0 && nRoach < kMaxRoach); + + bool bVar_24 = false; + short nSprite = RoachList[nRoach].nSprite; + short nAction = RoachList[nRoach].nAction; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + default: + { + Printf("unknown msg %d for Roach\n", a & 0x7F0000); + return; + } + + case 0x90000: + { + seq_PlotSequence(a & 0xFFFF, ActionSeq[nAction].a + SeqOffsets[kSeqRoach], RoachList[nRoach].field_2, ActionSeq[nAction].b); + return; + } + + case 0xA0000: // fall through to next case + { + nDamage = runlist_CheckRadialDamage(nSprite); + fallthrough__; + } + case 0x80000: + { + if (nDamage) + { + if (RoachList[nRoach].nHealth <= 0) { + return; + } + + RoachList[nRoach].nHealth -= nDamage; + if (RoachList[nRoach].nHealth <= 0) + { + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + RoachList[nRoach].nHealth = 0; + sprite[nSprite].cstat &= 0xFEFE; + nCreaturesLeft++; // This seems to be incorrect in original exe? should be decrementing? + + if (nAction < 5) + { + DropMagic(nSprite); + RoachList[nRoach].nAction = 5; + RoachList[nRoach].field_2 = 0; + } + } + else + { + short nSprite2 = a & 0xFFFF; + if (nSprite2 >= 0) + { + if (sprite[nSprite2].statnum < 199) { + RoachList[nRoach].nTarget = nSprite2; + } + + if (nAction == 0) + { + RoachList[nRoach].nAction = 2; + GoRoach(nSprite); + RoachList[nRoach].field_2 = 0; + } + else + { + if (!RandomSize(4)) + { + RoachList[nRoach].nAction = 4; + RoachList[nRoach].field_2 = 0; + } + } + } + } + } + + return; + } + + case 0x20000: + { + Gravity(nSprite); + + int nSeq = SeqOffsets[kSeqRoach] + ActionSeq[RoachList[nRoach].nAction].a; + + sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, RoachList[nRoach].field_2); + seq_MoveSequence(nSprite, nSeq, RoachList[nRoach].field_2); + + RoachList[nRoach].field_2++; + if (RoachList[nRoach].field_2 >= SeqSize[nSeq]) + { + bVar_24 = true; + RoachList[nRoach].field_2 = 0; + } + + int nFlag = FrameFlag[SeqBase[nSeq] + RoachList[nRoach].field_2]; + short nTarget = RoachList[nRoach].nTarget; + + if (nAction > 5) { + return; + } + + switch (nAction) + { + case 0: + { + if (RoachList[nRoach].field_2 == 1) + { + RoachList[nRoach].field_C--; + if (RoachList[nRoach].field_C <= 0) + { + RoachList[nRoach].field_C = RandomSize(6); + } + else + { + RoachList[nRoach].field_2 = 0; + } + } + + if (((nRoach & 0xF) == (totalmoves & 0xF)) && nTarget < 0) + { + short nTarget = FindPlayer(nSprite, 50); + if (nTarget >= 0) + { + RoachList[nRoach].nAction = 2; + RoachList[nRoach].field_2 = 0; + RoachList[nRoach].nTarget = nTarget; + GoRoach(nSprite); + } + } + + return; + } + + case 1: + { + // parltly the same as case 0... + if (((nRoach & 0xF) == (totalmoves & 0xF)) && nTarget < 0) + { + short nTarget = FindPlayer(nSprite, 100); + if (nTarget >= 0) + { + RoachList[nRoach].nAction = 2; + RoachList[nRoach].field_2 = 0; + RoachList[nRoach].nTarget = nTarget; + GoRoach(nSprite); + } + } + + return; + } + + case 2: + { + if ((totalmoves & 0xF) == (nRoach & 0xF)) + { + PlotCourseToSprite(nSprite, nTarget); + GoRoach(nSprite); + } + + int nVal = MoveCreatureWithCaution(nSprite); + + if ((nVal & 0xC000) == 49152) + { + if ((nVal & 0x3FFF) == nTarget) + { + // repeated below + RoachList[nRoach].field_E = RandomSize(2) + 1; + RoachList[nRoach].nAction = 3; + + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].ang = GetMyAngle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y); + + RoachList[nRoach].field_2 = 0; + } + else + { + sprite[nSprite].ang = (sprite[nSprite].ang + 256) & kAngleMask; + GoRoach(nSprite); + } + } + else if ((nVal & 0xC000) == 32768) + { + sprite[nSprite].ang = (sprite[nSprite].ang + 256) & kAngleMask; + GoRoach(nSprite); + } + //else if ((nVal & 0xC000) < 32768) + else + { + if (RoachList[nRoach].field_C != 0) + { + RoachList[nRoach].field_C--; + } + else + { + // same as above + RoachList[nRoach].field_E = RandomSize(2) + 1; + RoachList[nRoach].nAction = 3; + + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].ang = GetMyAngle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y); + + RoachList[nRoach].field_2 = 0; + } + } + + if (nTarget != -1 && !(sprite[nTarget].cstat & 0x101)) + { + RoachList[nRoach].nAction = 1; + RoachList[nRoach].field_2 = 0; + RoachList[nRoach].field_C = 100; + RoachList[nRoach].nTarget = -1; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } + + return; + } + + case 3: + { + if (bVar_24) + { + RoachList[nRoach].field_E--; + if (RoachList[nRoach].field_E <= 0) + { + RoachList[nRoach].nAction = 2; + GoRoach(nSprite); + RoachList[nRoach].field_2 = 0; + RoachList[nRoach].field_C = RandomSize(7); + } + } + else + { + if (nFlag & 0x80) + { + BuildBullet(nSprite, 13, 0, 0, -1, sprite[nSprite].ang, nTarget + 10000, 1); + } + } + + return; + } + + case 4: + { + if (bVar_24) + { + RoachList[nRoach].nAction = 2; + RoachList[nRoach].field_2 = 0; + } + + return; + } + + case 5: + { + if (bVar_24) + { + sprite[nSprite].cstat = 0; + RoachList[nRoach].nAction = 6; + RoachList[nRoach].field_2 = 0; + } + + return; + } + } + } + } +} +END_PS_NS diff --git a/source/exhumed/src/roach.h b/source/exhumed/src/roach.h new file mode 100644 index 000000000..ffe71b155 --- /dev/null +++ b/source/exhumed/src/roach.h @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __roach_h__ +#define __roach_h__ + +BEGIN_PS_NS + +void InitRoachs(); +int BuildRoach(int nType, int nSprite, int x, int y, int z, short nSector, int angle); +void FuncRoach(int a, int nDamage, int nRun); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/runlist.cpp b/source/exhumed/src/runlist.cpp new file mode 100644 index 000000000..442d5170b --- /dev/null +++ b/source/exhumed/src/runlist.cpp @@ -0,0 +1,1865 @@ +//------------------------------------------------------------------------- +/* +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 "exhumed.h" +#include "engine.h" +#include "runlist.h" +#include "player.h" +#include "trigdat.h" +#include "move.h" +#include "random.h" +#include "mummy.h" +#include "fish.h" +#include "lion.h" +#include "rex.h" +#include "set.h" +#include "rat.h" +#include "wasp.h" +#include "anubis.h" +#include "snake.h" +#include "scorp.h" +#include "ra.h" +#include "spider.h" +#include "bullet.h" +#include "queen.h" +#include "roach.h" +#include "bubbles.h" +#include "lavadude.h" +#include "grenade.h" +#include "object.h" +#include "switch.h" +#include "anims.h" +#include "sound.h" +#include "init.h" +#include "lighting.h" +#include + +BEGIN_PS_NS + +//#define kFuncMax 0x260000 // the number 38 stored in the high word of an int +#define kFuncMax 39 +#define kMaxRunStack 200 + + +short RunCount = -1; +short nRadialSpr = -1; +short nStackCount = 0; +short word_966BE = 0; +short ChannelList = -1; +short ChannelLast = -1; + +int nRadialOwner; +int nDamageRadius; +int nRadialDamage; +short RunChain; +short NewRun; + +int sRunStack[kMaxRunStack]; +short RunFree[kMaxRuns]; +RunChannel sRunChannels[kMaxChannels]; + + +RunStruct RunData[kMaxRuns]; + +short word_96760 = 0; + +/* variables + Name: _sRunStack + Name: _RunFree + Name: _channel + Name: _RunData + Name: _nRadialOwner + Name: _nDamageRadius + Name: _nRadialDamage + Name: _RunCount + Name: _nRadialSpr + Name: _nStackCount +*/ + +AiFunc aiFunctions[kFuncMax] = { + FuncElev, + FuncSwReady, + FuncSwPause, + FuncSwStepOn, + FuncSwNotOnPause, + FuncSwPressSector, + FuncSwPressWall, + FuncWallFace, + FuncSlide, + FuncAnubis, + FuncPlayer, // 10 + FuncBullet, + FuncSpider, + FuncCreatureChunk, + FuncMummy, + FuncGrenade, + FuncAnim, + FuncSnake, + FuncFish, + FuncLion, + FuncBubble, // 20 + FuncLava, + FuncLavaLimb, + FuncObject, + FuncRex, + FuncSet, + FuncQueen, + FuncQueenHead, + FuncRoach, + FuncQueenEgg, + FuncWasp, // 30 + FuncTrap, + FuncFishLimb, + FuncRa, // 33 + FuncScorp, + FuncSoul, + FuncRat, + FuncEnergyBlock, + FuncSpark, +}; + + + +int runlist_GrabRun() +{ + assert(RunCount > 0 && RunCount <= kMaxRuns); + + RunCount--; + + return RunFree[RunCount]; +} + +int runlist_FreeRun(int nRun) +{ + assert(RunCount >= 0 && RunCount < kMaxRuns); + assert(nRun >= 0 && nRun < kMaxRuns); + + RunFree[RunCount] = nRun; + RunCount++; + + RunData[nRun]._6 = -1; + RunData[nRun].nMoves = -1; + RunData[nRun]._4 = RunData[nRun]._6; + + return 1; +} + +// done +int runlist_HeadRun() +{ + int nRun = runlist_GrabRun(); + + RunData[nRun]._4 = -1; + RunData[nRun]._6 = -1; + + return nRun; +} + +// sub 4 +void runlist_InitRun() +{ + int i; + + RunCount = kMaxRuns; + nStackCount = 0; + + for (i = 0; i < kMaxRuns; i++) + { + RunData[i].nMoves = -1; + RunData[i]._6 = -1; + RunFree[i] = i; + RunData[i]._4 = -1; + } + + int nRun = runlist_HeadRun(); + RunChain = nRun; + NewRun = nRun; + + for (i = 0; i < kMaxPlayers; i++) { + PlayerList[i].nRun = -1; + } + + nRadialSpr = -1; +} + +int runlist_UnlinkRun(int nRun) +{ + assert(nRun >= 0 && nRun < kMaxRuns); + + if (nRun == RunChain) + { + RunChain = RunData[nRun]._4; + return nRun; + } + + if (RunData[nRun]._6 >= 0) + { + RunData[RunData[nRun]._6]._4 = RunData[nRun]._4; + } + + if (RunData[nRun]._4 >= 0) + { + RunData[RunData[nRun]._4]._6 = RunData[nRun]._6; + } + + return nRun; +} + +// done ? +int runlist_InsertRun(int RunLst, int RunNum) +{ + assert(RunLst >= 0 && RunLst < kMaxRuns); + assert(RunNum >= 0 && RunNum < kMaxRuns); + + RunData[RunNum]._6 = RunLst; + int val = RunData[RunLst]._4; + RunData[RunNum]._4 = val; + + if (val >= 0) { + RunData[val]._6 = RunNum; + } + + RunData[RunLst]._4 = RunNum; + return RunNum; +} + +// done +int runlist_AddRunRec(int a, int b) +{ + int nRun = runlist_GrabRun(); + + RunData[nRun].nMoves = b; // TODO - split this into the two shorts? + + runlist_InsertRun(a, nRun); + return nRun; +} + +void runlist_DoSubRunRec(int RunPtr) +{ + assert(RunPtr >= 0 && RunPtr < kMaxRuns); + + runlist_UnlinkRun(RunPtr); + runlist_FreeRun(RunPtr); +} + +void runlist_CleanRunRecs() +{ + int nextPtr = RunChain; + + if (nextPtr >= 0) + { + assert(nextPtr < kMaxRuns); + nextPtr = RunData[nextPtr]._4; + } + + while (nextPtr >= 0) + { + int runPtr = nextPtr; + assert(runPtr < kMaxRuns); + + int val = RunData[runPtr].nRef; // >> 16; + nextPtr = RunData[runPtr]._4; + + if (val < 0) { + runlist_DoSubRunRec(runPtr); + } + } +} + +// done +void runlist_SubRunRec(int RunPtr) +{ + assert(RunPtr >= 0 && RunPtr < kMaxRuns); + + RunData[RunPtr].nMoves = -totalmoves; +} + +void runlist_SendMessageToRunRec(int nRun, int edx, int nDamage) +{ + int nFunc = RunData[nRun].nRef;// >> 16; + + if (nFunc < 0) { + return; + } + + assert(nFunc >= 0 && nFunc <= kFuncMax); + + if (nFunc > kFuncMax) { + return; + } + + assert(nFunc < kFuncMax); // REMOVE + + // do function pointer call here. + aiFunctions[nFunc](edx, nDamage, nRun); +} + +void runlist_ExplodeSignalRun() +{ + short nextPtr = RunChain; + + if (nextPtr >= 0) + { + assert(nextPtr < kMaxRuns); + nextPtr = RunData[nextPtr]._4; + } + + // LOOP + while (nextPtr >= 0) + { + int runPtr = nextPtr; + assert(runPtr < kMaxRuns); + + int val = RunData[runPtr].nMoves; + nextPtr = RunData[runPtr]._4; + + if (val >= 0) + { + runlist_SendMessageToRunRec(runPtr, 0xA0000, 0); + } + } +} + +void runlist_PushMoveRun(int eax) +{ + if (nStackCount < kMaxRunStack) + { + sRunStack[nStackCount] = eax; + nStackCount++; + } +} + +int runlist_PopMoveRun() +{ + if (nStackCount <= 0) { + I_Error("PopMoveRun() called inappropriately\n"); + } + + nStackCount--; + return sRunStack[nStackCount]; +} + +void runlist_SignalRun(int NxtPtr, int edx) +{ + if (NxtPtr == RunChain && word_966BE != 0) { + runlist_PushMoveRun(edx); + return; + } + + while (1) + { + word_966BE = 1; + + if (NxtPtr >= 0) + { + assert(NxtPtr < kMaxRuns); + NxtPtr = RunData[NxtPtr]._4; + } + + while (NxtPtr >= 0) + { + int RunPtr = NxtPtr; + + if (RunPtr >= 0) + { + assert(RunPtr < kMaxRuns); + int val = RunData[RunPtr].nMoves; + NxtPtr = RunData[RunPtr]._4; + + if (val >= 0) { + runlist_SendMessageToRunRec(RunPtr, edx, 0); + } + } + } + + word_966BE = 0; + if (nStackCount == 0) { + return; + } + + edx = runlist_PopMoveRun(); + NxtPtr = RunChain; + } +} + +void runlist_InitChan() +{ + ChannelList = -1; + ChannelLast = -1; + + for (int i = 0; i < kMaxChannels; i++) + { + sRunChannels[i].c = -1; + sRunChannels[i].a = runlist_HeadRun(); + sRunChannels[i].b = -1; + sRunChannels[i].d = 0; + } +} + +void runlist_ChangeChannel(int eax, short dx) +{ + if (sRunChannels[eax].b < 0) + { + short nChannel = ChannelList; + ChannelList = eax; + sRunChannels[eax].b = nChannel; + } + + sRunChannels[eax].c = dx; + sRunChannels[eax].d |= 2; +} + +void runlist_ReadyChannel(short eax) +{ + if (sRunChannels[eax].b < 0) + { + short nChannel = ChannelList; + ChannelList = eax; + sRunChannels[eax].b = nChannel; + } + + sRunChannels[eax].d |= 1; +} + +void runlist_ProcessChannels() +{ +#if 1 + short v0; + short v1; + int v5; + short b; + short d; + + do + { + v0 = -1; + v1 = -1; + + while (ChannelList >= 0) + { + b = sRunChannels[ChannelList].b; + d = sRunChannels[ChannelList].d; + //v3 = v2[3]; + //b = v2[1]; + + if (d & 2) + { + sRunChannels[ChannelList].d = d ^ 2; + //v2[3] = v3 ^ 2; + runlist_SignalRun(sRunChannels[ChannelList].a, ChannelList | 0x10000); + } + +// v4 = v3 & 1; + if (d & 1) + { + sRunChannels[ChannelList].d ^= 1; +// *((_BYTE *)v2 + offsetof(RunChannel, d)) ^= 1u; + runlist_SignalRun(sRunChannels[ChannelList].a, 0x30000); + } + + if (sRunChannels[ChannelList].d) + { + if (v1 == -1) + { + v1 = ChannelList; + v0 = ChannelList; + } + else + { + v5 = v0; + v0 = ChannelList; + sRunChannels[v5].b = ChannelList; + } + } + else + { + sRunChannels[ChannelList].b = -1; + } + ChannelList = b; + } + ChannelList = v1; + } while (v1 != -1); + +#else + int edi = -1; + int esi = edi; + + while (1) + { + short nChannel = ChannelList; + if (nChannel < 0) + { + ChannelList = esi; + if (esi != -1) + { + edi = -1; + esi = edi; + continue; + } + else { + return; + } + } + + short b = sRunChannels[nChannel].b; + short d = sRunChannels[nChannel].d; + + if (d & 2) + { + sRunChannels[nChannel].d = d ^ 2; + runlist_SignalRun(sRunChannels[nChannel].a, ChannelList | 0x10000); + } + + if (d & 1) + { + sRunChannels[nChannel].d = d ^ 1; + runlist_SignalRun(sRunChannels[nChannel].a, 0x30000); + } + + if (sRunChannels[nChannel].d == 0) + { + sRunChannels[ChannelList].b = -1; + } + else + { + if (esi == -1) + { + esi = ChannelList; + edi = esi; + } + else + { + sRunChannels[edi].b = ChannelList; + edi = ChannelList; + } + } + + ChannelList = b; + } +#endif +} + +int runlist_FindChannel(short ax) +{ + for (int i = 0; i < kMaxChannels; i++) + { + if (sRunChannels[i].c == -1) + { + sRunChannels[i].c = ax; + return i; + } + } + + return -1; +} + +int runlist_AllocChannel(int a) +{ + if (a) + { + for (int i = 0; i < kMaxChannels; i++) + { + if (sRunChannels[i].c == a) { + return i; + } + } + } + + return runlist_FindChannel(a); +} + +void runlist_ExecObjects() +{ + runlist_ProcessChannels(); + runlist_SignalRun(RunChain, 0x20000); +} + +void runlist_ProcessSectorTag(int nSector, int lotag, int hitag) +{ + int zListA[8]; + int zListB[8]; + + int _lotag = lotag; + int nChannel = runlist_AllocChannel(hitag % 1000); + assert(nChannel >= 0); // REMOVE + + int var_24 = (hitag / 1000) << 12; + int var_18 = lotag / 1000; + + if (!var_18) { + var_18 = 1; + } + + var_18 <<= 2; + + _lotag = (lotag % 1000); + int eax = _lotag - 1; + + switch (eax) + { + case 0: // Ceiling Doom door + { + /* + This function searches z-coordinates of neighboring sectors to find the + closest (next) ceiling starting at the given z-coordinate (thez). + */ + short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1); + assert(nextSector > -1); + + int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].ceilingz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwPress = BuildSwPressSector(nChannel, BuildLink(1, 1), nSector, var_24); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwPress); + + int nSwPause = BuildSwPause(nChannel, BuildLink(2, -1, 0), 60); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwPause); + return; + } + + case 1: // Floor Doom door + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].ceilingz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwPress = BuildSwPressSector(nChannel, BuildLink(1, 1), nSector, var_24); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwPress); + + int nSwPause = BuildSwPause(nChannel, BuildLink(2, -1, 0), 60); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwPause); + return; + } + + case 2: + case 3: + case 18: + case 19: + case 21: + case 28: + case 29: + case 45: + case 46: + case 59: + case 64: + case 65: + case 66: + case 68: + case 71: + case 72: + case 73: + case 75: + case 76: + case 77: + case 78: + case 80: + case 81: + case 82: + case 83: + case 84: + { + return; + } + + case 4: // Permanent floor raise + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz + 1, -1, -1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].ceilingz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + return; + } + + case 5: // Touchplate floor lower, single + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, -1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), 400, 400, 2, sector[nextSector].floorz, sector[nSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwStepOn(nChannel, BuildLink(2, 1, 1), nSector); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + + sector[nSector].floorz = sector[nextSector].floorz; + return; + } + + case 6: // Touchplate floor lower, multiple + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), nSector); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + + int nSwitch2 = BuildSwNotOnPause(nChannel, BuildLink(2, -1, 0), nSector, 8); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch2); + return; + } + + case 7: // Permanent floor lower + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + return; + } + + case 8: // Switch activated lift down + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 150); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 9: // Touchplate Floor Raise + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, -1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), nSector); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + + int nSwitch2 = BuildSwNotOnPause(nChannel, BuildLink(2, -1, 0), nSector, 8); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch2); + return; + } + + case 10: // Permanent floor raise + case 13: // Sector raise / lower + case 37: // Sector raise / lower + { + /* + fix for original behaviour - nextSector could be -1 the and game would do an invalid memory read + when getting the floorz for nextSector. Here, we assume 0 and only set the correct value if nextSector + is valid. + */ + int zVal = 0; + + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, -1); + if (nextSector >= 0) { + zVal = sector[nextSector].floorz; + } + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, zVal); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + return; + } + + case 11: // Switch activated lift up + { + /* + fix for original behaviour - nextSector could be -1 the and game would do an invalid memory read + when getting the floorz for nextSector. Here, we assume 0 and only set the correct value if nextSector + is valid. + */ + int zVal = 0; + + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, -1); + if (nextSector >= 0) { + zVal = sector[nextSector].floorz; + } + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, zVal); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 150); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 12: // Bobbing floor + { + /* + fix for original behaviour - nextSector could be -1 the and game would do an invalid memory read + when getting the floorz for nextSector. Here, we assume 0 and only set the correct value if nextSector + is valid. + */ + int zVal = 0; + + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + if (nextSector >= 0) { + zVal = sector[nextSector].floorz; + } + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, zVal); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwReady(nChannel, BuildLink(2, 1, 0)); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 14: // Sector raise/lower + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, -1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + return; + } + + case 15: // Stuttering noise (floor makes noise) + { + int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), 200, var_18 * 100, 2, sector[nSector].ceilingz, sector[nSector].floorz - 8); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), nSector); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + + int nSwitch2 = BuildSwReady(nChannel, BuildLink(2, -1, 0)); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch2); + return; + } + + case 16: // Reserved? + { + int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), 200, var_18 * 100, 2, sector[nSector].ceilingz, sector[nSector].floorz - 8); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwReady(nChannel, BuildLink(2, -1, 0)); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 17: // Raises floor AND lowers ceiling + { + int ebx = ((sector[nSector].floorz - sector[nSector].ceilingz) / 2) + sector[nSector].ceilingz; + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), 200, var_18 * 100, 2, sector[nSector].floorz, ebx); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int ebx2 = (((sector[nSector].floorz - sector[nSector].ceilingz) / 2) + sector[nSector].ceilingz) - 8; + + int nElev2 = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), 200, var_18 * 100, 2, sector[nSector].ceilingz, ebx2); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev2); + + int nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), nSector); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 20: // Touchplate + { + int nSwitch = BuildSwStepOn(nChannel, BuildLink(2, 1, 1), nSector); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 22: // Floor raise, Sychronize + { + /* + fix for original behaviour - nextSector could be -1 the and game would do an invalid memory read + when getting the floorz for nextSector. Here, we assume 0 and only set the correct value if nextSector + is valid. + */ + int zVal = 0; + + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + if (nextSector >= 0) { + zVal = sector[nextSector].floorz; + } + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), 32767, 200, 2, sector[nSector].floorz, zVal); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), var_18 * 60); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 23: // Ceiling door, channel trigger only + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1); + assert(nextSector > -1); + + int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].ceilingz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 60); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 24: + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 300); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 25: + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 450); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 26: + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 600); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 27: + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 900); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 30: // Touchplate + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), 0x7FFF, 0x7FFF, 2, sector[nSector].floorz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), nSector); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 31: + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), 0x7FFF, 0x7FFF, 2, sector[nSector].floorz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + return; + } + + case 32: // Ceiling Crusher + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1); + assert(nextSector > -1); + + int nElev = BuildElevC(20, nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nextSector].ceilingz, sector[nSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + return; + } + + case 33: // Triggerable Ceiling Crusher(Inactive) + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1); + assert(nextSector > -1); + + int nElev = BuildElevC(28, nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nextSector].ceilingz, sector[nSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + return; + } + + case 34: // Destructible sector + case 35: + { + nEnergyTowers++; + + int nEnergyBlock = BuildEnergyBlock(nSector); + + if (_lotag == 36) { + nFinaleSpr = nEnergyBlock; + } + + return; + } + + case 36: + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + return; + } + + case 38: // Touchplate + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), 0x7FFF, 0x7FFF, 2, sector[nSector].floorz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), nSector); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + + int nSwitch2 = BuildSwNotOnPause(nChannel, BuildLink(2, -1, 0), nSector, 8); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch2); + return; + } + + case 39: // Moving sector(follows waypoints) + { + AddMovingSector(nSector, lotag, hitag % 1000, 2); + return; + } + + case 40: // Moving sector(follows waypoints) + { + AddMovingSector(nSector, lotag, hitag % 1000, 18); + return; + } + + case 41: // Moving sector(follows waypoints) + { + AddMovingSector(nSector, lotag, hitag % 1000, 58); + return; + } + + case 42: // Moving sector(follows waypoints) + { + AddMovingSector(nSector, lotag, hitag % 1000, 122); + return; + } + + case 43: // Moving sector(follows waypoints) + { + AddMovingSector(nSector, lotag, hitag % 1000, 90); + return; + } + + case 44: // Pushbox sector + { + CreatePushBlock(nSector); + return; + } + + case 47: // Ceiling lower + { + /* + fix for original behaviour - nextSector could be -1 the and game would do an invalid memory read + when getting the floorz for nextSector. Here, we assume 0 and only set the correct value if nextSector + is valid. + */ + int zVal = 0; + + short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, 1); + if (nextSector >= 0) { + zVal = sector[nextSector].ceilingz; + } + + int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), 200, var_18 * 100, 2, sector[nSector].ceilingz, zVal); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + return; + } + + case 48: + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1); + assert(nextSector > -1); + + int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), 200, var_18 * 100, 2, sector[nSector].ceilingz, sector[nextSector].ceilingz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + return; + } + + case 49: // Floor lower / raise + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, 1, 1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), 0x7FFF, 200, 2, sector[nextSector].floorz, sector[nSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + return; + } + + case 50: + { + int edx = ((sector[nSector].floorz - sector[nSector].ceilingz) / 2) + sector[nSector].ceilingz; + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), 200, var_18 * 100, 2, sector[nSector].floorz, edx); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int eax = (((sector[nSector].floorz - sector[nSector].ceilingz) / 2) + sector[nSector].ceilingz) - 8; + + nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), 200, var_18 * 100, 2, sector[nSector].ceilingz, eax); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwReady(nChannel, BuildLink(2, 1, 0)); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 51: + { + int eax = ((sector[nSector].floorz - sector[nSector].ceilingz) / 2) + sector[nSector].ceilingz; + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, eax, sector[nSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + eax = ((sector[nSector].floorz - sector[nSector].ceilingz) / 2) + sector[nSector].ceilingz; + + nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, eax, sector[nSector].ceilingz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwPressSector(nChannel, BuildLink(1, 1), nSector, var_24); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + + nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 60); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 52: + { + int eax = ((sector[nSector].floorz - sector[nSector].ceilingz) / 2) + sector[nSector].ceilingz; + + int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, eax, sector[nSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + eax = ((sector[nSector].floorz - sector[nSector].ceilingz) / 2) + sector[nSector].ceilingz; + + nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, eax, sector[nSector].ceilingz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwPause(nChannel, BuildLink(2, -1, 0), 150); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 53: + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1); + assert(nextSector > -1); + + int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].ceilingz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwPressSector(nChannel, BuildLink(1, 1), nSector, var_24); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 54: + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1); + assert(nextSector > -1); + + int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].ceilingz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwPressSector(nChannel, BuildLink(1, 1), nSector, var_24); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 55: + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1); + assert(nextSector > -1); + + int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].ceilingz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + return; + } + + case 56: + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].ceilingz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + return; + } + + case 57: + { + int nSwitch = BuildSwPressSector(nChannel, BuildLink(1, 1), nSector, var_24); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + + // Fall through to case 62 + fallthrough__; + } + case 62: + { + if (_lotag == 63) { + nEnergyChan = nChannel; + } + + short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1); + assert(nextSector > -1); + + int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].ceilingz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + return; + } + + case 58: + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwStepOn(nChannel, BuildLink(1, 1), nSector); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + + int nSwitch2 = BuildSwNotOnPause(nChannel, BuildLink(1, 1), nSector, 60); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch2); + return; + } + + case 60: + { + zListB[0] = sector[nSector].floorz; + int var_1C = 1; + + while (1) + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, -1); + if (nextSector < 0 || var_1C >= 8) { + break; + } + + zListB[var_1C] = sector[nextSector].floorz; + + var_1C++; + } + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, var_1C, + zListB[0], zListB[1], zListB[2], zListB[3], zListB[4], zListB[5], zListB[6], zListB[7]); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + return; + } + + case 61: + { + zListA[0] = sector[nSector].floorz; + int var_20 = 1; + + while (1) + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + if (nextSector < 0 || var_20 >= 8) { + break; + } + + zListA[var_20] = sector[nextSector].floorz; + + var_20++; + } + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, var_20, + zListA[0], zListA[1], zListA[2], zListA[3], zListA[4], zListA[5], zListA[6], zListA[7]); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + return; + } + + case 63: + { + int nSwitch = BuildSwStepOn(nChannel, BuildLink(2, 0, 0), nSector); + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + + return; + } + + case 67: + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].floorz, 1, 1); + assert(nextSector > -1); + + int nElev = BuildElevF(nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, sector[nSector].floorz, sector[nextSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + return; + } + + case 69: + case 70: + { + short nextSector = nextsectorneighborz(nSector, sector[nSector].ceilingz, -1, -1); + assert(nextSector > -1); + + int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, (int)sector[nSector].floorz, (int)sector[nextSector].ceilingz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + int nSwitch = BuildSwPressSector(nChannel, BuildLink(1, 1), nSector, var_24); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + + int nSwitch2 = BuildSwPause(nChannel, BuildLink(2, -1, 0), 60); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch2); + + return; + } + + case 74: + { + int nElev = BuildElevC(0, nChannel, nSector, FindWallSprites(nSector), var_18 * 100, var_18 * 100, 2, (int)sector[nSector].ceilingz, (int)sector[nSector].floorz); + + runlist_AddRunRec(sRunChannels[nChannel].a, nElev); + + return; + } + + case 79: + { + SectFlag[nSector] |= 0x8000; + return; + } + } +} + +void runlist_ProcessWallTag(int nWall, short lotag, short hitag) +{ + int nChannel = runlist_AllocChannel(hitag % 1000); + assert(nChannel >= 0 && nChannel < kMaxChannels); + + int var_18 = 0; // TODO - FIXME CHECKME. This doesn't seem to be initialised in the ASM? + int var_28; + + int var_20 = 0; // TODO - FIXME CHECKME. This doesn't seem to be initialised in the ASM? + int var_34; + + int var_14 = 0; // TODO - FIXME CHECKME. This doesn't seem to be initialised in the ASM? + int var_24; + + int var_38 = 0; // TODO - FIXME CHECKME. This doesn't seem to be initialised in the ASM? + int var_2C; + + int ebp = 0; // TODO - FIXME CHECKME. This doesn't seem to be initialised in the ASM? + int var_30; + + int eax = lotag / 1000; + if (!eax) { + eax = 1; + } + + int nEffectTag = lotag % 1000; + // int edi = nEffectTag; + eax <<= 2; + + switch (nEffectTag) + { + default: + return; + + case 1: + { + int nWallFace = BuildWallFace(nChannel, nWall, 2, wall[nWall].picnum, wall[nWall].picnum + 1); + runlist_AddRunRec(sRunChannels[nChannel].a, nWallFace); + + int nSwitch = BuildSwPressWall(nChannel, BuildLink(2, nEffectTag, 0), nWall); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 6: + { + int nSwitch = BuildSwPressWall(nChannel, BuildLink(2, 1, 0), nWall); + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 7: // Regular switch + { + int nWallFace = BuildWallFace(nChannel, nWall, 2, wall[nWall].picnum, wall[nWall].picnum + 1); + runlist_AddRunRec(sRunChannels[nChannel].a, nWallFace); + + int nSwitch = BuildSwPressWall(nChannel, BuildLink(1, 1), nWall); + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 8: + { + int nWallFace = BuildWallFace(nChannel, nWall, 2, wall[nWall].picnum, wall[nWall].picnum + 1); + runlist_AddRunRec(sRunChannels[nChannel].a, nWallFace); + + int nSwitch = BuildSwPressWall(nChannel, BuildLink(2, -1, 0), nWall); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 9: // Invisible switch + { + int nSwitch = BuildSwPressWall(nChannel, BuildLink(2, 1, 1), nWall); + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 10: + { + int nSwitch = BuildSwPressWall(nChannel, BuildLink(2, -1, 0), nWall); + runlist_AddRunRec(sRunChannels[nChannel].a, nSwitch); + return; + } + + case 12: + { + short nStart = nWall; + + while (1) + { + nWall = wall[nWall].point2; + + if (nStart == nWall) { + break; + } + + var_28 = var_18; + var_18 = nWall; + } + + short nWall2 = wall[nStart].point2; + short nWall3 = wall[nWall2].point2; + short nWall4 = wall[nWall3].point2; + + int nSlide = BuildSlide(nChannel, nStart, var_18, var_28, nWall2, nWall3, nWall4); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSlide); + return; + } + + case 14: + { + short nStart = nWall; + + while (1) + { + nWall = wall[nWall].point2; + + if (nStart == nWall) { + break; + } + + var_34 = var_20; + var_20 = nWall; + } + + short nWall2 = wall[nStart].point2; + short nWall3 = wall[nWall2].point2; + short nWall4 = wall[nWall3].point2; + + int nSlide = BuildSlide(nChannel, nStart, var_20, var_34, nWall2, nWall3, nWall4); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSlide); + return; + } + + case 16: + { + short nStart = nWall; + + while (1) + { + nWall = wall[nWall].point2; + + if (nStart == nWall) { + break; + } + + var_24 = var_14; + var_14 = nWall; + } + + short nWall2 = wall[nStart].point2; + short nWall3 = wall[nWall2].point2; + short nWall4 = wall[nWall3].point2; + + int nSlide = BuildSlide(nChannel, nStart, var_14, var_24, nWall2, nWall3, nWall4); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSlide); + return; + } + + case 19: + { + short nStart = nWall; + + while (1) + { + nWall = wall[nWall].point2; + + if (nStart == nWall) { + break; + } + + var_2C = var_38; + var_38 = nWall; + } + + short nWall2 = wall[nStart].point2; + short nWall3 = wall[nWall2].point2; + short nWall4 = wall[nWall3].point2; + + int nSlide = BuildSlide(nChannel, nStart, var_38, var_2C, nWall2, nWall3, nWall4); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSlide); + return; + } + + case 20: + { + short nStart = nWall; + + while (1) + { + nWall = wall[nWall].point2; + + if (nStart == nWall) { + break; + } + + var_30 = ebp; + ebp = nWall; + } + + short nWall2 = wall[nStart].point2; + short nWall3 = wall[nWall2].point2; + short nWall4 = wall[nWall3].point2; + + int nSlide = BuildSlide(nChannel, nStart, ebp, var_30, nWall2, nWall3, nWall4); + + runlist_AddRunRec(sRunChannels[nChannel].a, nSlide); + return; + } + + case 24: + { + AddFlow(nWall, eax, 3); + return; + } + + case 25: + { + AddFlow(nWall, eax, 2); + return; + } + } +} + +int runlist_CheckRadialDamage(short nSprite) +{ + if (nSprite == nRadialSpr) { + return 0; + } + + if (!(sprite[nSprite].cstat & 0x101)) { + return 0; + } + + if (sprite[nSprite].statnum >= kMaxStatus || sprite[nRadialSpr].statnum >= kMaxStatus) { + return 0; + } + + if (sprite[nSprite].statnum != 100 && nSprite == nRadialOwner) { + return 0; + } + + int x = (sprite[nSprite].x - sprite[nRadialSpr].x) >> 8; + int y = (sprite[nSprite].y - sprite[nRadialSpr].y) >> 8; + int z = (sprite[nSprite].z - sprite[nRadialSpr].z) >> 12; + + if (x < 0) { + x = -x; + } + + if (x > nDamageRadius) { + return 0; + } + + if (y < 0) { + y = -y; + } + + if (y > nDamageRadius) { + return 0; + } + + if (z < 0) { + z = -z; + } + + if (z > nDamageRadius) { + return 0; + } + + int edi = 0; + + int nDist = ksqrt(x * x + y * y); + + if (nDist < nDamageRadius) + { + uint16_t nCStat = sprite[nSprite].cstat; + sprite[nSprite].cstat = 0x101; + + if (((kStatExplodeTarget - sprite[nSprite].statnum) <= 1) || + cansee(sprite[nRadialSpr].x, + sprite[nRadialSpr].y, + sprite[nRadialSpr].z - 512, + sprite[nRadialSpr].sectnum, + sprite[nSprite].x, + sprite[nSprite].y, + sprite[nSprite].z - 8192, + sprite[nSprite].sectnum)) + { + edi = (nRadialDamage * (nDamageRadius - nDist)) / nDamageRadius; + + if (edi < 0) { + edi = 0; + } + else if (edi > 20) + { + int nAngle = GetMyAngle(x, y); + sprite[nSprite].xvel += (short)((edi * Sin(nAngle + 512)) >> 3); + sprite[nSprite].yvel += (short)((edi * Sin(nAngle)) >> 3); + sprite[nSprite].zvel -= edi * 24; + + if (sprite[nSprite].zvel < -3584) { + sprite[nSprite].zvel = -3584; + } + } + } + + sprite[nSprite].cstat = nCStat; + } + + if (edi > 0x7FFF) { + edi = 0x7FFF; + } + + return edi; +} + +void runlist_RadialDamageEnemy(short nSprite, short nDamage, short nRadius) +{ + if (!nRadius) { + return; + } + + word_96760++; + + if (nRadialSpr == -1) + { + nRadialDamage = nDamage * 4; + nDamageRadius = nRadius; + nRadialSpr = nSprite; + nRadialOwner = sprite[nSprite].owner; + + runlist_ExplodeSignalRun(); + + nRadialSpr = -1; + word_96760--; + } +} + +void runlist_DamageEnemy(int nSprite, int nSprite2, short nDamage) +{ + if (sprite[nSprite].statnum >= kMaxStatus) { + return; + } + + short nRun = sprite[nSprite].owner; + if (nRun <= -1) { + return; + } + + short nPreCreaturesLeft = nCreaturesLeft; + + runlist_SendMessageToRunRec(nRun, (nSprite2 & 0xFFFF) | 0x80000, nDamage * 4); + + // is there now one less creature? (has one died) + if (nPreCreaturesLeft > nCreaturesLeft && nSprite2 > -1) + { + if (sprite[nSprite2].statnum != 100) { + return; + } + + short nPlayer = GetPlayerFromSprite(nSprite2); + nTauntTimer[nPlayer]--; + + if (nTauntTimer[nPlayer] <= 0) + { + // Do a taunt + int nPlayerSprite = PlayerList[nPlayer].nSprite; + int nSector = sprite[nPlayerSprite].sectnum; + + if (!(SectFlag[nSector] & kSectUnderwater)) + { + int ebx = 0x4000; + + if (nPlayer == nLocalPlayer) { + ebx = 0x6000; + } + + int nDopSprite = nDoppleSprite[nPlayer]; + D3PlayFX(StaticSound[kSoundTauntStart + (RandomSize(3) % 5)], nDopSprite | ebx); + } + + nTauntTimer[nPlayer] = RandomSize(3) + 3; + } + } +} + +static SavegameHelper sgh("runlist", + SV(RunCount), + SV(nRadialSpr), + SV(nStackCount), + SV(word_966BE), + SV(ChannelList), + SV(ChannelLast), + SV(nRadialOwner), + SV(nDamageRadius), + SV(nRadialDamage), + SV(RunChain), + SV(NewRun), + SA(sRunStack), + SA(RunFree), + SA(sRunChannels), + SA(RunData), + SV(word_96760), + nullptr); + +END_PS_NS diff --git a/source/exhumed/src/runlist.h b/source/exhumed/src/runlist.h new file mode 100644 index 000000000..543d7c9aa --- /dev/null +++ b/source/exhumed/src/runlist.h @@ -0,0 +1,86 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __runlist_h__ +#define __runlist_h__ + +#include "compat.h" + +BEGIN_PS_NS + + +#define kMaxRuns 25600 +#define kMaxChannels 4096 + +struct RunStruct +{ + union + { + int nMoves; + struct + { + short nVal; + short nRef; + }; + }; + + short _4; + short _6; +}; + +struct RunChannel +{ + short a; + short b; + short c; + short d; +}; + +typedef void(*AiFunc)(int, int, int nRun); + +extern RunStruct RunData[kMaxRuns]; +extern RunChannel sRunChannels[kMaxChannels]; +extern short NewRun; +extern int nRadialOwner; +extern short nRadialSpr; + +void runlist_InitRun(); + +int runlist_GrabRun(); +int runlist_FreeRun(int nRun); +int runlist_AddRunRec(int a, int b); +int runlist_HeadRun(); +void runlist_InitChan(); +void runlist_ChangeChannel(int eax, short dx); +void runlist_ReadyChannel(short eax); +void runlist_ProcessSectorTag(int nSector, int lotag, int hitag); +int runlist_AllocChannel(int a); +void runlist_DoSubRunRec(int RunPtr); +void runlist_SubRunRec(int RunPtr); +void runlist_ProcessWallTag(int nWall, short lotag, short hitag); +int runlist_CheckRadialDamage(short nSprite); +void runlist_RadialDamageEnemy(short nSprite, short nDamage, short nRadius); +void runlist_DamageEnemy(int nSprite, int nSprite2, short nDamage); +void runlist_SignalRun(int NxtPtr, int edx); + +void runlist_CleanRunRecs(); +void runlist_ExecObjects(); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/save.cpp b/source/exhumed/src/save.cpp new file mode 100644 index 000000000..9bb95088d --- /dev/null +++ b/source/exhumed/src/save.cpp @@ -0,0 +1,195 @@ +//------------------------------------------------------------------------- +/* +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 "save.h" +#include +#include +#include "init.h" +#include "music/z_music.h" +//#include +//#include +//#include +#include "engine.h" +#include "exhumed.h" +#include "mmulti.h" +#include "savegamehelp.h" +#include "sound.h" + +BEGIN_PS_NS + +extern int MenuExitCondition; +void SaveTextureState(); +void LoadTextureState(); + +static TArray sghelpers(TArray::NoInit); + +bool GameInterface::SaveGame(FSaveGameNode* sv) +{ + OpenSaveGameForWrite(sv->Filename); + // workaround until the level info here has been transitioned. + G_WriteSaveHeader(sv->SaveTitle); + + auto fw = WriteSavegameChunk("engine"); + fw->Write(&numsectors, sizeof(numsectors)); + fw->Write(sector, sizeof(sectortype) * numsectors); + fw->Write(&numwalls, sizeof(numwalls)); + fw->Write(wall, sizeof(walltype) * numwalls); + fw->Write(sprite, sizeof(spritetype) * kMaxSprites); + fw->Write(headspritesect, sizeof(headspritesect)); + fw->Write(prevspritesect, sizeof(prevspritesect)); + fw->Write(nextspritesect, sizeof(nextspritesect)); + fw->Write(headspritestat, sizeof(headspritestat)); + fw->Write(prevspritestat, sizeof(prevspritestat)); + fw->Write(nextspritestat, sizeof(nextspritestat)); + + fw->Write(&tailspritefree, sizeof(tailspritefree)); + fw->Write(&myconnectindex, sizeof(myconnectindex)); + fw->Write(&connecthead, sizeof(connecthead)); + fw->Write(connectpoint2, sizeof(connectpoint2)); + fw->Write(&numframes, sizeof(numframes)); + fw->Write(&randomseed, sizeof(randomseed)); + fw->Write(&numshades, sizeof(numshades)); + + fw->Write(&g_visibility, sizeof(g_visibility)); + fw->Write(¶llaxtype, sizeof(parallaxtype)); + fw->Write(¶llaxyoffs_override, sizeof(parallaxyoffs_override)); + fw->Write(¶llaxyscale_override, sizeof(parallaxyscale_override)); + fw->Write(&pskybits_override, sizeof(pskybits_override)); + + fw->Write(show2dwall, sizeof(show2dwall)); + fw->Write(show2dsprite, sizeof(show2dsprite)); + fw->Write(show2dsector, sizeof(show2dsector)); + + for (auto sgh : sghelpers) sgh->Save(); + SaveTextureState(); + FinishSavegameWrite(); + return 1; // CHECKME +} + +bool GameInterface::LoadGame(FSaveGameNode* sv) +{ + OpenSaveGameForRead(sv->Filename); + auto fr = ReadSavegameChunk("engine"); + if (fr.isOpen()) + { + fr.Read(&numsectors, sizeof(numsectors)); + fr.Read(sector, sizeof(sectortype) * numsectors); + fr.Read(&numwalls, sizeof(numwalls)); + fr.Read(wall, sizeof(walltype) * numwalls); + fr.Read(sprite, sizeof(spritetype) * kMaxSprites); + fr.Read(headspritesect, sizeof(headspritesect)); + fr.Read(prevspritesect, sizeof(prevspritesect)); + fr.Read(nextspritesect, sizeof(nextspritesect)); + fr.Read(headspritestat, sizeof(headspritestat)); + fr.Read(prevspritestat, sizeof(prevspritestat)); + fr.Read(nextspritestat, sizeof(nextspritestat)); + + fr.Read(&tailspritefree, sizeof(tailspritefree)); + fr.Read(&myconnectindex, sizeof(myconnectindex)); + fr.Read(&connecthead, sizeof(connecthead)); + fr.Read(connectpoint2, sizeof(connectpoint2)); + fr.Read(&numframes, sizeof(numframes)); + fr.Read(&randomseed, sizeof(randomseed)); + fr.Read(&numshades, sizeof(numshades)); + + fr.Read(&g_visibility, sizeof(g_visibility)); + fr.Read(¶llaxtype, sizeof(parallaxtype)); + fr.Read(¶llaxyoffs_override, sizeof(parallaxyoffs_override)); + fr.Read(¶llaxyscale_override, sizeof(parallaxyscale_override)); + fr.Read(&pskybits_override, sizeof(pskybits_override)); + + fr.Read(show2dwall, sizeof(show2dwall)); + fr.Read(show2dsprite, sizeof(show2dsprite)); + fr.Read(show2dsector, sizeof(show2dsector)); + fr.Close(); + } + + for (auto sgh : sghelpers) sgh->Load(); + LoadTextureState(); + FinishSavegameRead(); + + // reset the sky in case it hasn't been done yet. + 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; + ototalclock = totalclock; + MenuExitCondition = 6; + + if (levelnum > 15) + { + nSwitchSound = 35; + nStoneSound = 23; + nElevSound = 51; + nStopSound = 35; + } + else + { + nSwitchSound = 33; + nStoneSound = 23; + nElevSound = 23; + nStopSound = 66; + } + + Mus_ResumeSaved(); + return 1; // CHECKME +} + + +SavegameHelper::SavegameHelper(const char* name, ...) +{ + Name = name; + sghelpers.Push(this); + va_list ap; + va_start(ap, name); + for(;;) + { + void* addr = va_arg(ap, void*); + if (!addr) break; + size_t size = va_arg(ap, size_t); + Elements.Push(std::make_pair(addr, size)); + } +} + +void SavegameHelper::Load() +{ + auto fr = ReadSavegameChunk(Name); + for (auto& entry : Elements) + { + auto read = fr.Read(entry.first, entry.second); + if (read != entry.second) I_Error("Save game read error in %s", Name.GetChars()); + } +} +void SavegameHelper::Save() +{ + auto fw = WriteSavegameChunk(Name); + for (auto& entry : Elements) + { + auto write = fw->Write(entry.first, entry.second); + if (write != entry.second) I_Error("Save game write error in %s", Name.GetChars()); + } +} + +END_PS_NS diff --git a/source/exhumed/src/save.h b/source/exhumed/src/save.h new file mode 100644 index 000000000..d200e887f --- /dev/null +++ b/source/exhumed/src/save.h @@ -0,0 +1,44 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __save_h__ +#define __save_h__ +#include "zstring.h" + +BEGIN_PS_NS + +int savegame(int nSlot); +int loadgame(int nSlot); + + +struct SavegameHelper +{ + FString Name; + TArray> Elements; + SavegameHelper(const char* name, ...); + void Load(); + void Save(); +}; + +#define SV(v) &v, sizeof(v) +#define SA(a) &a, sizeof(a) + + +END_PS_NS + +#endif diff --git a/source/exhumed/src/scorp.cpp b/source/exhumed/src/scorp.cpp new file mode 100644 index 000000000..48a0cb7af --- /dev/null +++ b/source/exhumed/src/scorp.cpp @@ -0,0 +1,514 @@ +//------------------------------------------------------------------------- +/* +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 "engine.h" +#include "scorp.h" +#include "runlist.h" +#include "exhumed.h" +#include "move.h" +#include "sequence.h" +#include "sound.h" +#include "random.h" +#include "trigdat.h" +#include "bullet.h" +#include "spider.h" +#include + +BEGIN_PS_NS + +/* + Selkis Boss AI code +*/ + +short ScorpCount = -1; + +Scorpion scorpion[kMaxScorpions]; +short ScorpChan[kMaxScorpions]; + +static actionSeq ActionSeq[] = { + {0, 0}, + {8, 0}, + {29, 0}, + {19, 0}, + {45, 1}, + {46, 1}, + {47, 1}, + {48, 1}, + {50, 1}, + {53, 1} +}; + +static SavegameHelper sgh("scorp", + SV(ScorpCount), + SA(scorpion), + SA(ScorpChan), + nullptr); + +void InitScorp() +{ + ScorpCount = kMaxScorpions; +} + +int BuildScorp(short nSprite, int x, int y, int z, short nSector, short nAngle, int nChannel) +{ + ScorpCount--; + if (ScorpCount < 0) { + return -1; + } + + short nScorp = ScorpCount; + + if (nSprite == -1) + { + nSprite = insertsprite(nSector, 122); + } + else + { + changespritestat(nSprite, 122); + + x = sprite[nSprite].x; + y = sprite[nSprite].y; + z = sector[sprite[nSprite].sectnum].floorz; + nAngle = sprite[nSprite].ang; + } + + assert(nSprite >= 0 && nSprite < kMaxSprites); + + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + sprite[nSprite].cstat = 0x101; + sprite[nSprite].clipdist = 70; + sprite[nSprite].shade = -12; + sprite[nSprite].xrepeat = 80; + sprite[nSprite].yrepeat = 80; + sprite[nSprite].picnum = 1; + sprite[nSprite].pal = sector[sprite[nSprite].sectnum].ceilingpal; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].ang = nAngle; + sprite[nSprite].zvel = 0; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].extra = -1; + sprite[nSprite].hitag = 0; + +// GrabTimeSlot(3); + + scorpion[ScorpCount].nHealth = 20000; + scorpion[ScorpCount].nFrame = 0; + scorpion[ScorpCount].nAction = 0; + scorpion[ScorpCount].nSprite = nSprite; + scorpion[ScorpCount].nTarget = -1; + scorpion[ScorpCount].g = 0; + scorpion[ScorpCount].i = 1; + + ScorpChan[nScorp] = nChannel; + + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nScorp | 0x220000); + scorpion[nScorp].f = runlist_AddRunRec(NewRun, nScorp | 0x220000); + + nCreaturesLeft++; + + return nScorp | 0x220000; +} + +void FuncScorp(int a, int nDamage, int nRun) +{ + short nScorp = RunData[nRun].nVal; + assert(nScorp >= 0 && nScorp < kMaxScorpions); + + int edi = 0; + + short nSprite = scorpion[nScorp].nSprite; + short nAction = scorpion[nScorp].nAction; + + short nTarget = -1; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + default: + { + Printf("unknown msg %d for Scorp\n", a & 0x7F0000); + return; + } + + case 0x90000: + { + seq_PlotSequence(a & 0xFFFF, SeqOffsets[kSeqScorp] + ActionSeq[nAction].a, scorpion[nScorp].nFrame, ActionSeq[nAction].b); + return; + } + + case 0xA0000: + { + nDamage = runlist_CheckRadialDamage(nSprite); + if (!nDamage) { + return; + } + // else fall through to case 0x80000 + fallthrough__; + } + + case 0x80000: + { + if (scorpion[nScorp].nHealth <= 0) { + return; + } + + scorpion[nScorp].nHealth -= nDamage; + + if (scorpion[nScorp].nHealth <= 0) + { + scorpion[nScorp].nHealth = 0; + + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + + scorpion[nScorp].nAction = 4; + scorpion[nScorp].nFrame = 0; + + sprite[nSprite].cstat &= 0xFEFE; + + nCreaturesLeft--; + + scorpion[nScorp].g = 10; + return; + } + else + { + nTarget = a & 0xFFFF; + + if (nTarget >= 0) + { + if (sprite[nSprite].statnum == 100 || (sprite[nSprite].statnum < 199 && !RandomSize(5))) + { + scorpion[nScorp].nTarget = nTarget; + } + } + + if (!RandomSize(5)) + { + scorpion[nScorp].nAction = RandomSize(2) + 4; + scorpion[nScorp].nFrame = 0; + return; + } + + if (RandomSize(2)) { + return; + } + + D3PlayFX(StaticSound[kSound41], nSprite); + + goto FS_Pink_A; + } + } + + case 0x20000: + { + if (scorpion[nScorp].nHealth) { + Gravity(nSprite); + } + + int nSeq = SeqOffsets[kSeqScorp] + ActionSeq[nAction].a; + + sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, scorpion[nScorp].nFrame); + seq_MoveSequence(nSprite, nSeq, scorpion[nScorp].nFrame); + + scorpion[nScorp].nFrame++; + + if (scorpion[nScorp].nFrame >= SeqSize[nSeq]) + { + scorpion[nScorp].nFrame = 0; + edi = 1; + } + + int nFlag = FrameFlag[SeqBase[nSeq] + scorpion[nScorp].nFrame]; + nTarget = scorpion[nScorp].nTarget; + + switch (nAction) + { + default: + return; + + case 0: + { + if (scorpion[nScorp].g > 0) + { + scorpion[nScorp].g--; + return; + } + + if ((nScorp & 31) == (totalmoves & 31)) + { + if (nTarget < 0) + { + nTarget = FindPlayer(nSprite, 500); + + if (nTarget >= 0) + { + D3PlayFX(StaticSound[kSound41], nSprite); + + scorpion[nScorp].nFrame = 0; + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512); + sprite[nSprite].yvel = Sin(sprite[nSprite].ang); + + scorpion[nScorp].nAction = 1; + scorpion[nScorp].nTarget = nTarget; + } + } + } + + return; + } + + case 1: + { + scorpion[nScorp].i--; + + if (scorpion[nScorp].i <= 0) + { + scorpion[nScorp].i = RandomSize(5); + // GOTO FS_Pink_A: + goto FS_Pink_A; + } + else + { + int nMove = MoveCreatureWithCaution(nSprite); + if ((nMove & 0xC000) == 0xC000) + { + if (nTarget == (nMove & 0x3FFF)) + { + int nAngle = getangle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y); + if (AngleDiff(sprite[nSprite].ang, nAngle) < 64) + { + scorpion[nScorp].nAction = 2; + scorpion[nScorp].nFrame = 0; + } + + // GOTO FS_Red + goto FS_Red; + } + else + { +// GOTO FS_Pink_A + goto FS_Pink_A; + } + } + else if ((nMove & 0xC000) == 0x8000) + { + // GOTO FS_Pink_A + goto FS_Pink_A; + } + else + { + // GOTO FS_Pink_B + goto FS_Pink_B; + } + } + } + + case 2: + { + if (nTarget == -1) + { + scorpion[nScorp].nAction = 0; + scorpion[nScorp].g = 5; + } + else + { + if (PlotCourseToSprite(nSprite, nTarget) >= 768) + { + scorpion[nScorp].nAction = 1; + } + else if (nFlag & 0x80) + { + runlist_DamageEnemy(nTarget, nSprite, 7); + } + } + + // GOTO FS_Red + goto FS_Red; + } + + case 3: + { + if (edi) + { + scorpion[nScorp].h--; + if (scorpion[nScorp].h <= 0) + { + scorpion[nScorp].nAction = 1; + + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512); + sprite[nSprite].yvel = Sin(sprite[nSprite].ang); + + scorpion[nScorp].nFrame = 0; + return; + } + } + + if (!(nFlag & 0x80)) { + return; + } + + short nBulletSprite = BuildBullet(nSprite, 16, 0, 0, -1, sprite[nSprite].ang, nTarget + 10000, 1) & 0xFFFF; + if (nBulletSprite > -1) + { + PlotCourseToSprite(nBulletSprite, nTarget); + } + + return; + } + + case 4: + case 5: + case 6: + case 7: + { + if (!edi) { + return; + } + + if (scorpion[nScorp].nHealth > 0) + { + scorpion[nScorp].nAction = 1; + scorpion[nScorp].nFrame = 0; + scorpion[nScorp].g = 0; + return; + } + + scorpion[nScorp].g--; + if (scorpion[nScorp].g <= 0) + { + scorpion[nScorp].nAction = 8; + } + else + { + scorpion[nScorp].nAction = RandomBit() + 6; + } + + return; + } + + case 8: + { + if (edi) + { + scorpion[nScorp].nAction++; // set to 9 + scorpion[nScorp].nFrame = 0; + + runlist_ChangeChannel(ScorpChan[nScorp], 1); + return; + } + + int nSpider = BuildSpider(-1, sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, sprite[nSprite].sectnum, sprite[nSprite].ang); + if (nSpider != -1) + { + short nSpiderSprite = nSpider & 0xFFFF; + + sprite[nSpiderSprite].ang = RandomSize(11); + + int nRnd = RandomSize(5) + 1; + + sprite[nSpiderSprite].xvel = (Sin(sprite[nSpiderSprite].ang + 512) >> 8) * nRnd; + sprite[nSpiderSprite].yvel = (Sin(sprite[nSpiderSprite].ang) >> 8) * nRnd; + sprite[nSpiderSprite].zvel = (-(RandomSize(5) + 3)) << 8; + } + + return; + } + + case 9: + { + sprite[nSprite].cstat &= 0xFEFE; + + if (edi) + { + runlist_SubRunRec(scorpion[nScorp].f); + runlist_DoSubRunRec(sprite[nSprite].owner); + runlist_FreeRun(sprite[nSprite].lotag - 1); + + mydeletesprite(nSprite); + } + + return; + } + } + + break; + } + } + +FS_Pink_A: + PlotCourseToSprite(nSprite, nTarget); + sprite[nSprite].ang += RandomSize(7) - 63; + sprite[nSprite].ang &= kAngleMask; + + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512); + sprite[nSprite].yvel = Sin(sprite[nSprite].ang); + +FS_Pink_B: + if (scorpion[nScorp].g) + { + scorpion[nScorp].g--; + } + else + { + scorpion[nScorp].g = 45; + + if (cansee(sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z - GetSpriteHeight(nSprite), sprite[nSprite].sectnum, + sprite[nTarget].x, sprite[nTarget].y, sprite[nTarget].z - GetSpriteHeight(nTarget), sprite[nTarget].sectnum)) + { + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + sprite[nSprite].ang = GetMyAngle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y); + + scorpion[nScorp].h = RandomSize(2) + RandomSize(3); + + if (!scorpion[nScorp].h) { + scorpion[nScorp].g = RandomSize(5); + } + else + { + scorpion[nScorp].nAction = 3; + scorpion[nScorp].nFrame = 0; + } + } + } + +FS_Red: + if (!nAction || nTarget == -1) { + return; + } + + if (!(sprite[nTarget].cstat & 0x101)) + { + scorpion[nScorp].nAction = 0; + scorpion[nScorp].nFrame = 0; + scorpion[nScorp].g = 30; + scorpion[nScorp].nTarget = -1; + + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } +} +END_PS_NS diff --git a/source/exhumed/src/scorp.h b/source/exhumed/src/scorp.h new file mode 100644 index 000000000..11667afad --- /dev/null +++ b/source/exhumed/src/scorp.h @@ -0,0 +1,51 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __scorp_h__ +#define __scorp_h__ + +#include "compat.h" + +BEGIN_PS_NS + +/* + Selkis Boss AI code +*/ + +#define kMaxScorpions 5 + +struct Scorpion +{ + short nHealth; + short nFrame; + short nAction; + short nSprite; + short nTarget; + short f; + short g; + int8_t h; + int8_t i; +}; + +void InitScorp(); +int BuildScorp(short nSprite, int x, int y, int z, short nSector, short nAngle, int nChannel); +void FuncScorp(int, int, int); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/sequence.cpp b/source/exhumed/src/sequence.cpp new file mode 100644 index 000000000..b86f88934 --- /dev/null +++ b/source/exhumed/src/sequence.cpp @@ -0,0 +1,674 @@ +//------------------------------------------------------------------------- +/* +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 "typedefs.h" +#include "sequence.h" +#include "engine.h" +#include "exhumed.h" +#include "sound.h" +#include "player.h" +#include "trigdat.h" +#include "move.h" +#include "view.h" +#include "init.h" +#include "light.h" +#ifndef __WATCOMC__ +#include +#include // for printf +#else +#include +#include +#endif + +// TEMP +#include + +BEGIN_PS_NS + +#define kMaxSequences 4096 +#define kMaxSEQFiles 78 +#define kMaxSEQFrames 18000 +#define kMaxSEQChunks 21000 + +short sequences = 0; +short frames = 0; +short chunks = 0; +short nPilotLightFrame; +short nPilotLightCount; + +short nPilotLightBase; +short laststatustile; + +short nShadowWidth = 1; +short nFlameHeight = 1; + +short SeqBase[kMaxSequences]; +short SeqSize[kMaxSequences]; +short SeqFlag[kMaxSequences]; + +short FrameSound[kMaxSEQFrames]; +short FrameSize[kMaxSEQFrames]; +short FrameBase[kMaxSEQFrames]; +short FrameFlag[kMaxSEQFrames]; + +short ChunkYpos[kMaxSEQChunks]; +short ChunkXpos[kMaxSEQChunks]; +short ChunkPict[kMaxSEQChunks]; +short ChunkFlag[kMaxSEQChunks]; + + +const char *SeqNames[kMaxSEQFiles] = +{ + "rothands", + "sword", + "pistol", + "m_60", + "flamer", // 4 + "grenade", + "cobra", + "bonesaw", + "scramble", + "glove", + "mummy", // 10 + "skull", + "poof", + "kapow", + "fireball", + "bubble", + "spider", // 16 + "anubis", + "anuball", + "fish", + "snakehed", // 20? + "snakbody", + "wasp", + "cobrapow", + "scorp", + "joe", // 25 + "status", + "dead", + "deadex", + "anupoof", + "skulpoof", // 30 + "bullet", + "shadow", + "grenroll", + "grenboom", + "splash", + "grenpow", + "skulstrt", + "firepoof", + "bloodhit", + "lion", // 40 + "items", + "lavag", // 42 + "lsplash", + "lavashot", + "smokebal", + "firepot", + "rex", + "set", // 48 + "queen", + "roach", // 50 + "hawk", + "setghost", + "setgblow", + "bizztail", + "bizzpoof", + "queenegg", + "roacshot", + "backgrnd", + "screens", // 59 + "arrow", + "fonts", + "drips", + "firetrap", + "magic2", + "creepy", + "slider", // 66 + "ravolt", + "eyehit", + "font2", // 69 + "seebubbl", + "blood", + "drum", + "poof2", + "deadbrn", + "grenbubb", + "rochfire", + "rat" +}; + +short SeqOffsets[kMaxSEQFiles]; + + +int seq_ReadSequence(const char *seqName) +{ + int i; + char buffer[200]; + buffer[0] = '\0'; + + strcat(buffer, seqName); + strcat(buffer, ".seq"); + + auto hFile = fileSystem.OpenFileReader(buffer, 1); + if (!hFile.isOpen()) + { + initprintf("Unable to open '%s'!\n", buffer); + return 0; + } + + short tag; + hFile.Read(&tag, sizeof(tag)); + if (tag < 'HI' || tag > 'HI' && tag != 'SD') + { + initprintf("Unsupported sequence version!\n"); + return 0; + } + + short centerx, centery; // TODO - are global vars? + short nSeqs; + hFile.Read(¢erx, sizeof(centerx)); + hFile.Read(¢ery, sizeof(centery)); + hFile.Read(&nSeqs, sizeof(nSeqs)); + + if (nSeqs <= 0 || sequences + nSeqs >= kMaxSequences) + { + if (nSeqs < 0) + { + initprintf("Invalid sequence count!\n"); + return 0; + } + else { + I_Error("Not enough sequences available! Increase array!\n"); + } + } + + hFile.Read(&SeqBase[sequences], nSeqs * sizeof(SeqBase[0])); + hFile.Read(&SeqSize[sequences], nSeqs * sizeof(SeqSize[0])); + hFile.Read(&SeqFlag[sequences], nSeqs * sizeof(SeqFlag[0])); + + for (i = 0; i < nSeqs; i++) + { + SeqBase[sequences + i] += frames; + } + + short vdi = frames; + + int16_t nFrames; + hFile.Read(&nFrames, sizeof(nFrames)); + + if (nFrames <= 0 || frames + nFrames >= kMaxSEQFrames) + { + if (nFrames < 0 ) + { + initprintf("Invalid frame count!\n"); + return 0; + } + else { + I_Error("Not enough frames available! Increase FRAMEMAX!\n"); + } + } + + hFile.Read(&FrameBase[frames], nFrames * sizeof(FrameBase[0])); + hFile.Read(&FrameSize[frames], nFrames * sizeof(FrameSize[0])); + hFile.Read(&FrameFlag[frames], nFrames * sizeof(FrameFlag[0])); + memset(&FrameSound[frames], -1, nFrames * sizeof(FrameSound[0])); + + for (i = 0; i < nFrames; i++) + { + FrameBase[frames + i] += chunks; + } + + int16_t nChunks; + hFile.Read(&nChunks, sizeof(nChunks)); + + if (nChunks < 0 || chunks + nChunks >= kMaxSEQChunks) + { + if (nChunks < 0 ) + { + initprintf("Invalid chunk count!\n"); + return 0; + } + else { + I_Error("Not enough chunks available! Increase CHUNKMAX!\n"); + } + } + + hFile.Read(&ChunkXpos[chunks], nChunks * sizeof(ChunkXpos[0])); + hFile.Read(&ChunkYpos[chunks], nChunks * sizeof(ChunkYpos[0])); + hFile.Read(&ChunkPict[chunks], nChunks * sizeof(ChunkPict[0])); + hFile.Read(&ChunkFlag[chunks], nChunks * sizeof(ChunkFlag[0])); + + for (i = 0; i < nChunks; i++) + { + ChunkXpos[chunks + i] -= centerx; + ChunkYpos[chunks + i] -= centery; + } + + sequences += nSeqs; + FrameBase[frames + nFrames] = chunks + nChunks; + frames += nFrames; + SeqBase[sequences] = frames; + chunks += nChunks; + + if (tag == 'SD') + { + short var_20; + hFile.Read(&var_20, sizeof(var_20)); + + for (i = 0; i < var_20; i++) + { + hFile.Read(&buffer[i * 10], 8); + } + + short var_24; + hFile.Read(&var_24, sizeof(var_24)); + + for (i = 0; i < var_24; i++) + { + short var_28, var_2C; + hFile.Read(&var_28, sizeof(var_28)); + hFile.Read(&var_2C, sizeof(var_2C)); + + int hSound = LoadSound(&buffer[(var_2C&0x1FF)*10]); + + FrameSound[vdi + var_28] = hSound | (var_2C & 0xFE00); + } + } + + return nSeqs; +} + +int seq_GetFirstSeqPicnum(int nSeq) +{ + int i = SeqOffsets[nSeq]; + i = SeqBase[i]; + i = FrameBase[i]; + i = ChunkPict[i]; + + return i; +} + +void seq_LoadSequences() +{ + int i; + + for (i = 0; i < kMaxSEQFiles; i++) + { + SeqOffsets[i] = sequences; + + if (seq_ReadSequence(SeqNames[i]) == 0) { + initprintf("Error loading '%s'\n", SeqNames[i]); + } + } + + nShadowPic = seq_GetFirstSeqPicnum(kSeqShadow); + nShadowWidth = tilesiz[nShadowPic].x; + + nFlameHeight = tilesiz[seq_GetFirstSeqPicnum(kSeqFirePoof)].y; + + nBackgroundPic = seq_GetFirstSeqPicnum(kSeqBackgrnd); + + nPilotLightBase = SeqBase[SeqOffsets[kSeqFlamer] + 3]; + nPilotLightCount = SeqSize[SeqOffsets[kSeqFlamer] + 3]; + nPilotLightFrame = 0; + + nFontFirstChar = seq_GetFirstSeqPicnum(kSeqFont2); + + short nSize = SeqSize[SeqOffsets[kSeqFont2]]; + + for (i = 0; i < nSize; i++) + { + picanm[nFontFirstChar + i].xofs = 0; + picanm[nFontFirstChar + i].yofs = 0; + } +} + +void seq_DrawStatusSequence(short nSequence, uint16_t edx, short ebx) +{ + edx += SeqBase[nSequence]; + + short nFrameBase = FrameBase[edx]; + int16_t nFrameSize = FrameSize[edx]; + + int const nPal = RemapPLU(kPalNormal); + + while (1) + { + nFrameSize--; + if (nFrameSize < 0) + break; + + uint8_t nStat = 1; // (thex, they) is middle + + laststatusx = ChunkXpos[nFrameBase] + 160; + laststatusy = ChunkYpos[nFrameBase] + 100 + ebx; + + short chunkFlag = ChunkFlag[nFrameBase]; + + if (chunkFlag & 1) { + nStat = 0x9; // (thex, they) is middle, and x-flipped + } + + if (chunkFlag & 2) { + nStat |= 0x10; // y-flipped + } + + laststatustile = ChunkPict[nFrameBase]; + + if (bHiRes) { + nStat |= 0x2; // scale and clip to viewing window + } + + overwritesprite(laststatusx, laststatusy, laststatustile, 0, nStat, nPal); + nFrameBase++; + } +} + +short seq_GetFrameFlag(short val, short nFrame) +{ + return FrameFlag[SeqBase[val] + nFrame]; +} + +void seq_DrawPilotLightSeq(int xOffset, int yOffset) +{ + short nSect = nPlayerViewSect[nLocalPlayer]; + + if (!(SectFlag[nSect] & kSectUnderwater)) + { + short nFrame = nPilotLightBase + nPilotLightFrame; + short nFrameBase = FrameBase[nFrame]; + short nFrameSize = FrameSize[nFrame]; + + while (1) + { + nFrameSize--; + if (nFrameSize < 0) + return; + + short nTile = ChunkPict[nFrameBase]; + int x = ChunkXpos[nFrameBase] + (160 + xOffset); + int y = ChunkYpos[nFrameBase] + (100 + yOffset); + + rotatesprite(x << 16, y << 16, 0x10000, (-2 * fix16_to_int(nPlayerDAng)) & kAngleMask, nTile, -127, 1, 2, windowxy1.x, windowxy1.y, windowxy2.x, windowxy2.y); + nFrameBase++; + } + } +} + +/* + 6 parameters + + arg0 - shade? + +*/ + +int seq_DrawGunSequence(int nSeqOffset, short dx, int xOffs, int yOffs, int nShade, int nPal) +{ + short nFrame = SeqBase[nSeqOffset] + dx; + short nFrameBase = FrameBase[nFrame]; + short nFrameSize = FrameSize[nFrame]; + short frameFlag = FrameFlag[nFrame]; + + while (1) + { + nFrameSize--; + if (nFrameSize < 0) + break; + + uint8_t nStat = 3; + int x = ChunkXpos[nFrameBase] + 160; + int y = ChunkYpos[nFrameBase] + 100; + + if (ChunkFlag[nFrameBase] & 1) { + nStat = 11; + } + + if (ChunkFlag[nFrameBase] & 2) { + nStat |= 0x10; + } + + short nTile = ChunkPict[nFrameBase]; + + if (frameFlag & 4) { + nShade = -100; + } + + if (nPlayerInvisible[nLocalPlayer]) { + nStat |= 0x4; + } + + overwritesprite(x + xOffs, y + yOffs, nTile, nShade, nStat, nPal); + nFrameBase++; + } + + return frameFlag; +} + +int seq_GetFrameSound(int val, int edx) +{ + return FrameSound[SeqBase[val] + edx]; +} + +void seq_MoveSequence(short nSprite, short nSeq, short bx) +{ + assert(nSeq >= 0); // TEMP + + int nSound = FrameSound[SeqBase[nSeq] + bx]; + if (nSound == -1) { + return; + } + + if (nSprite > -1) { + D3PlayFX(nSound, nSprite); + } + else { + PlayLocalSound(nSound, 0); + } +} + +int seq_GetSeqPicnum2(short nSeq, short nFrame) +{ + short nBase = FrameBase[SeqBase[nSeq] + nFrame]; + return ChunkPict[nBase]; +} + +int seq_GetSeqPicnum(short nSeq, short edx, short ebx) +{ + edx += SeqOffsets[nSeq]; + ebx += SeqBase[edx]; + short c = FrameBase[ebx]; + + return ChunkPict[c]; +} + +int seq_PlotArrowSequence(short nSprite, short nSeq, int nVal) +{ + int nAngle = GetMyAngle(nCamerax - tsprite[nSprite].x, nCameray - tsprite[nSprite].y); + + int nSeqOffset = ((((tsprite[nSprite].ang + 512) - nAngle) + 128) & kAngleMask) >> 8; + + short nFrame = SeqBase[nSeqOffset + nSeq] + nVal; + + short nFrameBase = FrameBase[nFrame]; + short nFrameSize = FrameSize[nFrame]; + + uint8_t nShade = tsprite[nSprite].shade; + short nStat = tsprite[nSprite].cstat; + + nStat |= 0x80; + + if (nSeqOffset & 3) { + nStat |= 0x18; + } + else { + nStat &= 0x0E7; + } + + if (FrameFlag[nFrame] & 4) { + nShade -= 100; + } + + tsprite[nSprite].cstat = nStat; + tsprite[nSprite].shade = nShade; + tsprite[nSprite].statnum = nFrameSize; + + if (ChunkFlag[nFrameBase] & 1) + { + tsprite[nSprite].xoffset = ChunkXpos[nFrameBase]; + tsprite[nSprite].cstat |= 4; + } + else + { + tsprite[nSprite].xoffset = -ChunkXpos[nFrameBase]; + } + + tsprite[nSprite].yoffset = -ChunkYpos[nFrameBase]; + tsprite[nSprite].picnum = ChunkPict[nFrameBase]; + + return ChunkPict[nFrameBase]; +} + +int seq_PlotSequence(short nSprite, short edx, short nFrame, short ecx) +{ + int nAngle = GetMyAngle(nCamerax - tsprite[nSprite].x, nCameray - tsprite[nSprite].y); + + int val; + + if (ecx & 1) + { + val = 0; + } + else + { + val = (((tsprite[nSprite].ang - nAngle) + 128) & kAngleMask) >> 8; + } + + int eax = SeqBase[edx] + nFrame; + int edi = SeqBase[edx + val] + nFrame; + + short nBase = FrameBase[edi]; + short nSize = FrameSize[edi]; + + int8_t shade = tsprite[nSprite].shade; + + if (FrameFlag[eax] & 4) + { + shade -= 100; + } + + short nPict = ChunkPict[nBase]; + + if (ecx & 0x100) + { + edx = -3; + } + else + { + edx = 100; + } + + int esi = nSize + 1; + esi += edx; + + int var_14 = edx + 1; + short nOwner = tsprite[nSprite].owner; + + while (1) + { + esi--; + nSize--; + + if (esi < var_14) { + break; + } + + tsprite[spritesortcnt].x = tsprite[nSprite].x; + tsprite[spritesortcnt].y = tsprite[nSprite].y; + tsprite[spritesortcnt].z = tsprite[nSprite].z; + tsprite[spritesortcnt].shade = shade; + tsprite[spritesortcnt].pal = tsprite[nSprite].pal; + tsprite[spritesortcnt].xrepeat = tsprite[nSprite].xrepeat; + tsprite[spritesortcnt].yrepeat = tsprite[nSprite].yrepeat; + tsprite[spritesortcnt].ang = tsprite[nSprite].ang; + tsprite[spritesortcnt].owner = tsprite[nSprite].owner; + tsprite[spritesortcnt].sectnum = tsprite[nSprite].sectnum; + tsprite[spritesortcnt].cstat = tsprite[nSprite].cstat |= 0x80; + tsprite[spritesortcnt].statnum = esi; + + if (ChunkFlag[nBase] & 1) + { + tsprite[spritesortcnt].xoffset = ChunkXpos[nBase]; + tsprite[spritesortcnt].cstat |= 4; // x-flipped + } + else + { + tsprite[spritesortcnt].xoffset = -ChunkXpos[nBase]; + } + + tsprite[spritesortcnt].yoffset = -ChunkYpos[nBase]; + tsprite[spritesortcnt].picnum = ChunkPict[nBase]; + + spritesortcnt++; + nBase++; + } + + if (!(tsprite[nSprite].cstat & 0x101) || (sprite[nOwner].statnum == 100 && nNetPlayerCount)) + { + tsprite[nSprite].owner = -1; + } + else + { + short nSector = tsprite[nSprite].sectnum; + int nFloorZ = sector[nSector].floorz; + + if (nFloorZ <= eyelevel[nLocalPlayer] + initz) { + tsprite[nSprite].owner = -1; + } + else + { + tsprite[nSprite].picnum = nShadowPic; + + int edx = ((tilesiz[nPict].x << 5) / nShadowWidth) - ((nFloorZ - tsprite[nSprite].z) >> 10); + if (edx < 1) { + edx = 1; + } + + tsprite[nSprite].cstat = 0x22; // transluscence, floor sprite + tsprite[nSprite].z = videoGetRenderMode() >= REND_POLYMOST ? nFloorZ : nFloorZ + 1; + tsprite[nSprite].yrepeat = (uint8_t)edx; + tsprite[nSprite].xrepeat = (uint8_t)edx; + tsprite[nSprite].statnum = -3; + tsprite[nSprite].pal = 0; + } + } + + return nPict; +} + +static SavegameHelper sgh("sequence", + SV(nPilotLightFrame), + SV(nPilotLightCount), + SV(nPilotLightBase), + SV(laststatustile), + SV(nShadowWidth), + SV(nFlameHeight), + nullptr); + +END_PS_NS diff --git a/source/exhumed/src/sequence.h b/source/exhumed/src/sequence.h new file mode 100644 index 000000000..a6a306e16 --- /dev/null +++ b/source/exhumed/src/sequence.h @@ -0,0 +1,147 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __sequence_h__ +#define __sequence_h__ + +#include "compat.h" + +BEGIN_PS_NS + +enum { + kSeqRothands = 0, + kSeqSword, + kSeqPistol, + kSeqM60, + kSeqFlamer, + kSeqGrenade, + kSeqCobra, + kSeqBoneSaw, + kSeqScramble, + kSeqGlove, + kSeqMummy, + kSeqSkull, + kSeqPoof, + kSeqKapow, + kSeqFireball, + kSeqBubble, + kSeqSpider, + kSeqAnubis, + kSeqAnuBall, + kSeqFish, + kSeqSnakehed, + kSeqSnakBody, + kSeqWasp, + kSeqCobraPow, + kSeqScorp, + kSeqJoe, // player pic + kSeqStatus, + kSeqDead, + kSeqDeadEx, + kSeqAnuPoof, + kSeqSkulPoof, + kSeqBullet, + kSeqShadow, + kSeqGrenRoll, + kSeqGrenBoom, + kSeqSplash, + kSeqGrenPow, + kSeqSkulSrt, + kSeqFirePoof, + kSeqBloodHit, + kSeqLion, + kSeqItems, + kSeqLavag, + kSeqLsplash, + kSeqLavaShot, + kSeqSmokeBal, + kSeqFirePot, + kSeqRex, + kSeqSet, + kSeqQueen, + kSeqRoach, + kSeqHawk, + kSeqSetGhost, + kSeqSetGBlow, + kSeqBizzTail, + kSeqBizzPoof, + kSeqQueenEgg, + kSeqRoacShot, + kSeqBackgrnd, + kSeqScreens, + kSeqArrow, + kSeqFonts, + kSeqDrips, + kSeqFireTrap, + kSeqMagic2, + kSeqCreepy, + kSeqSlider, + kSeqRavolt, // 67 + kSeqEyeHit, + kSeqFont2, + kSeqSeeBubbl, + kSeqBlood, + kSeqDrum, + kSeqPoof2, + kSeqDeadBrn, // 74 + kSeqGrenBubb, + kSeqRochfire, + kSeqRat +}; + +struct actionSeq +{ + short a; + short b; +}; + +extern short frames; + +extern short SeqBase[]; +extern short SeqSize[]; +extern short SeqOffsets[]; + +extern short FrameFlag[]; + +extern short nShadowWidth; +extern short nFlameHeight; + +extern short nPilotLightFrame; +extern short nPilotLightCount; + +extern short laststatustile; + +extern int laststatusx; +extern int laststatusy; + +void seq_LoadSequences(); +int seq_GetFrameSound(int val, int edx); +void seq_MoveSequence(short nSprite, short nSeq, short bx); +int seq_GetSeqPicnum2(short nSeq, short nFrame); +int seq_GetSeqPicnum(short nSeq, short edx, short ebx); +void seq_DrawStatusSequence(short nSequence, uint16_t edx, short ebx); + +int seq_DrawGunSequence(int nSeqOffset, short dx, int xOffs, int yOffs, int nShade, int nPal); +short seq_GetFrameFlag(short val, short nFrame); +int seq_PlotSequence(short nSprite, short edx, short nFrame, short ecx); +int seq_PlotArrowSequence(short nSprite, short nSeq, int nVal); +void seq_DrawPilotLightSeq(int xOffset, int yOffset); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/serial.cpp b/source/exhumed/src/serial.cpp new file mode 100644 index 000000000..2cf20ee5a --- /dev/null +++ b/source/exhumed/src/serial.cpp @@ -0,0 +1,46 @@ +//------------------------------------------------------------------------- +/* +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 "typedefs.h" +#include "serial.h" + +BEGIN_PS_NS + +short bSendBye = kFalse; + + +void UpdateSerialInputs() +{ + +} + +void ClearSerialInbuf() +{ + +} + +void HangUp() +{ + +} + +void UnInitSerial() +{ + +} +END_PS_NS diff --git a/source/exhumed/src/serial.h b/source/exhumed/src/serial.h new file mode 100644 index 000000000..d86f9187b --- /dev/null +++ b/source/exhumed/src/serial.h @@ -0,0 +1,33 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __serial_h__ +#define __serial_h__ + +BEGIN_PS_NS + +extern short bSendBye; + +void UpdateSerialInputs(); +void ClearSerialInbuf(); +void HangUp(); +void UnInitSerial(); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/set.cpp b/source/exhumed/src/set.cpp new file mode 100644 index 000000000..05c8922a5 --- /dev/null +++ b/source/exhumed/src/set.cpp @@ -0,0 +1,697 @@ +//------------------------------------------------------------------------- +/* +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 "set.h" +#include "engine.h" +#include "exhumed.h" +#include "runlist.h" +#include "sequence.h" +#include "random.h" +#include "move.h" +#include "trigdat.h" +#include "bullet.h" +#include + +BEGIN_PS_NS + +#define kMaxSets 10 + +short SetCount = 0; + +static actionSeq ActionSeq[] = { + {0, 0}, + {77, 1}, + {78, 1}, + {0, 0}, + {9, 0}, + {63, 0}, + {45, 0}, + {18, 0}, + {27, 0}, + {36, 0}, + {72, 1}, + {74, 1} +}; + +struct Set +{ + short nHealth; + short field_2; + short nAction; + short nSprite; + short nTarget; + short field_A; + short field_C; + short field_D; + short field_E; +}; + +Set SetList[kMaxSets]; +short SetChan[kMaxSets]; + +static SavegameHelper sgh("set", + SV(SetCount), + SA(SetList), + SA(SetChan), + nullptr); + + +void InitSets() +{ + SetCount = kMaxSets; +} + +int BuildSet(short nSprite, int x, int y, int z, short nSector, short nAngle, int nVal) +{ + SetCount--; + + short nSet = SetCount; + if (nSet < 0) { + return -1; + } + + if (nSprite == -1) + { + nSprite = insertsprite(nSector, 120); + } + else + { + changespritestat(nSprite, 120); + x = sprite[nSprite].x; + y = sprite[nSprite].y; + z = sector[sprite[nSprite].sectnum].floorz; + nAngle = sprite[nSprite].ang; + } + + assert(nSprite >= 0 && nSprite < kMaxSprites); + + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + sprite[nSprite].cstat = 0x101; + sprite[nSprite].shade = -12; + sprite[nSprite].clipdist = 110; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].xrepeat = 87; + sprite[nSprite].yrepeat = 96; + sprite[nSprite].pal = sector[sprite[nSprite].sectnum].ceilingpal; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].ang = nAngle; + sprite[nSprite].picnum = 1; + sprite[nSprite].hitag = 0; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].extra = -1; + + // GrabTimeSlot(3); + + SetList[nSet].nAction = 1; + SetList[nSet].nHealth = 8000; + SetList[nSet].nSprite = nSprite; + SetList[nSet].field_2 = 0; + SetList[nSet].nTarget = -1; + SetList[nSet].field_A = 90; + SetList[nSet].field_C = 0; + SetList[nSet].field_D = 0; + + SetChan[nSet] = nVal; + + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nSet | 0x190000); + + // this isn't stored anywhere... + runlist_AddRunRec(NewRun, nSet | 0x190000); + + nCreaturesLeft++; + + return nSet | 0x190000; +} + +int BuildSoul(int nSet) +{ + int nSetSprite = SetList[nSet].nSprite; + int nSprite = insertsprite(sprite[nSetSprite].sectnum, 0); + + assert(nSprite >= 0 && nSprite < kMaxSprites); + + sprite[nSprite].cstat = 0x8000u; + sprite[nSprite].shade = -127; + sprite[nSprite].xrepeat = 1; + sprite[nSprite].yrepeat = 1; + sprite[nSprite].pal = 0; + sprite[nSprite].clipdist = 5; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].picnum = seq_GetSeqPicnum(kSeqSet, 75, 0); + sprite[nSprite].ang = RandomSize(11); + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 65280 - RandomSize(10); + sprite[nSprite].x = sprite[nSetSprite].x; + sprite[nSprite].y = sprite[nSetSprite].y; + + short nSector = sprite[nSprite].sectnum; + sprite[nSprite].z = (RandomSize(8) << 8) + 8192 + sector[nSector].ceilingz - GetSpriteHeight(nSprite); + + sprite[nSprite].hitag = nSet; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].extra = 0; + +// GrabTimeSlot(3); + + sprite[nSprite].owner = runlist_AddRunRec(NewRun, nSprite | 0x230000); + + return nSprite | 0x230000; +} + +void FuncSoul(int pA, int, int nRun) +{ + short nSoulSprite = RunData[nRun].nVal; + + int nMessage = pA & 0x7F0000; + + switch (nMessage) + { + case 0x20000: + { + seq_MoveSequence(nSoulSprite, SeqOffsets[kSeqSet] + 75, 0); + + if (sprite[nSoulSprite].xrepeat < 32) + { + sprite[nSoulSprite].xrepeat++; + sprite[nSoulSprite].yrepeat++; + } + + sprite[nSoulSprite].extra += (nSoulSprite & 0x0F) + 5; + sprite[nSoulSprite].extra &= 0x7FF; + + int ebx = (Sin(sprite[nSoulSprite].extra + 512) >> 7);// *Sin(sprite[nSoulSprite].ang); + + if (movesprite(nSoulSprite, Sin(sprite[nSoulSprite].ang + 512) * ebx, Sin(sprite[nSoulSprite].ang) * ebx, sprite[nSoulSprite].zvel, 5120, 0, CLIPMASK0) & 0x10000) + { + int nSet = sprite[nSoulSprite].hitag; + int nSetSprite = SetList[nSet].nSprite; + + sprite[nSoulSprite].cstat = 0; + sprite[nSoulSprite].yrepeat = 1; + sprite[nSoulSprite].xrepeat = 1; + sprite[nSoulSprite].x = sprite[nSetSprite].x; + sprite[nSoulSprite].y = sprite[nSetSprite].y; + sprite[nSoulSprite].z = sprite[nSetSprite].z - (GetSpriteHeight(nSetSprite) >> 1); + mychangespritesect(nSoulSprite, sprite[nSetSprite].sectnum); + return; + } + } + + case 0x80000: + case 0xA0000: + case 0x90000: + return; + + default: + Printf("unknown msg %d for Soul\n", nMessage); + } +} + +void FuncSet(int a, int nDamage, int nRun) +{ + int var_24 = 0; + + short nSet = RunData[nRun].nVal; + assert(nSet >= 0 && nSet < kMaxSets); + + short nSprite = SetList[nSet].nSprite; + short nAction = SetList[nSet].nAction; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + default: + { + Printf("unknown msg %d for Set\n", nMessage); + return; + } + + case 0xA0000: + { + if (nAction == 5) + { + nDamage = runlist_CheckRadialDamage(nSprite); + // fall through to case 0x80000 + } + fallthrough__; + } + + case 0x80000: + { + if (nDamage && SetList[nSet].nHealth > 0) + { + if (nAction != 1) + { + SetList[nSet].nHealth -= nDamage; + } + + if (SetList[nSet].nHealth <= 0) + { + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + SetList[nSet].nHealth = 0; + sprite[nSprite].cstat &= 0xFEFE; + + nCreaturesLeft--; + + if (nAction < 10) + { + SetList[nSet].field_2 = 0; + SetList[nSet].nAction = 10; + } + } + else if (nAction == 1) + { + SetList[nSet].nAction = 2; + SetList[nSet].field_2 = 0; + } + } + return; + } + + case 0x90000: + { + seq_PlotSequence(a, SeqOffsets[kSeqSet] + ActionSeq[nAction].a, SetList[nSet].field_2, ActionSeq[nAction].b); + return; + } + + case 0x20000: + { + Gravity(nSprite); + + short nSeq = SeqOffsets[kSeqSet] + ActionSeq[SetList[nSet].nAction].a; + sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, SetList[nSet].field_2); + seq_MoveSequence(nSprite, nSeq, SetList[nSet].field_2); + + if (nAction == 3) + { + if (SetList[nSet].field_D) { + SetList[nSet].field_2++; + } + } + + SetList[nSet].field_2++; + if (SetList[nSet].field_2 >= SeqSize[nSeq]) + { + SetList[nSet].field_2 = 0; + var_24 = 1; + } + + short nFlag = FrameFlag[SeqBase[nSeq] + SetList[nSet].field_2]; + short nTarget = SetList[nSet].nTarget; + + if (nTarget > -1 && nAction < 10) + { + if (!(sprite[nTarget].cstat & 0x101)) + { + SetList[nSet].nTarget = -1; + SetList[nSet].nAction = 0; + nTarget = -1; + SetList[nSet].field_2 = 0; + } + } + + int nVal = MoveCreature(nSprite); + int nSprite_b = nSprite; + + pushmove_old(&sprite[nSprite].x, &sprite[nSprite].y, &sprite[nSprite].z, &sprite[nSprite].sectnum, sprite[nSprite].clipdist << 2, 5120, -5120, CLIPMASK0); + + if (sprite[nSprite].zvel > 4000) + { + if (nVal & 0x20000) + { + SetQuake(nSprite_b, 100); + } + } + + switch (nAction) + { + default: + return; + + case 0: + { + if ((nSet & 0x1F) == (totalmoves & 0x1F)) + { + if (nTarget < 0) + { + nTarget = FindPlayer(nSprite, 1000); + } + + if (nTarget >= 0) + { + SetList[nSet].nAction = 3; + SetList[nSet].field_2 = 0; + SetList[nSet].nTarget = nTarget; + + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512) >> 1; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 1; + } + } + + return; + } + + case 1: + { + if (FindPlayer(nSprite, 1000) >= 0) + { + SetList[nSet].field_A--; + if (SetList[nSet].field_A <= 0) + { + SetList[nSet].nAction = 2; + SetList[nSet].field_2 = 0; + } + } + + return; + } + + case 2: + { + if (var_24) + { + SetList[nSet].nAction = 7; + SetList[nSet].field_C = 0; + SetList[nSet].field_2 = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + + SetList[nSet].nTarget = FindPlayer(nSprite, 1000); + } + return; + } + + case 3: + { + if (nTarget != -1) + { + if (nFlag & 0x10 && nVal != 0x20000) + { + SetQuake(nSprite, 100); + } + + int nCourse = PlotCourseToSprite(nSprite, nTarget); + + if ((nSet & 0x1F) == (totalmoves & 0x1F)) + { + int nRand = RandomSize(3); + + switch (nRand) + { + case 0: + case 2: + { + SetList[nSet].field_C = 0; + SetList[nSet].nAction = 7; + SetList[nSet].field_2 = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + return; + } + case 1: + { + PlotCourseToSprite(nSprite, nTarget); + + SetList[nSet].nAction = 6; + SetList[nSet].field_2 = 0; + SetList[nSet].field_E = 5; + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + return; + } + default: + { + if (nCourse <= 100) + { + SetList[nSet].field_D = 0; + } + else + { + SetList[nSet].field_D = 1; + } + break; + } + } + } + + // loc_338E2 + sprite[nSprite].xvel = Sin((sprite[nSprite].ang & 0xF8) + 512) >> 1; + sprite[nSprite].yvel = Sin((sprite[nSprite].ang & 0xF8)) >> 1; + + if (SetList[nSet].field_D) + { + sprite[nSprite].xvel *= 2; + sprite[nSprite].yvel *= 2; + } + + if ((nVal & 0xC000) < 0x8000) + { + break; + } + else if ((nVal & 0xC000) == 0x8000) + { + short nWall = nVal & 0x3FFF; + short nSector = wall[nWall].nextsector; + + if (nSector >= 0) + { + if ((sprite[nSprite].z - sector[nSector].floorz) < 55000) + { + if (sprite[nSprite].z > sector[nSector].ceilingz) + { + SetList[nSet].field_C = 1; + SetList[nSet].nAction = 7; + SetList[nSet].field_2 = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + return; + } + } + } + + sprite[nSprite].ang = (sprite[nSprite].ang + 256) & kAngleMask; + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512) >> 1; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> 1; + break; + } + else if ((nVal & 0xC000) == 0xC000) + { + if (nTarget == (nVal & 0x3FFF)) + { + int nAng = getangle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y); + if (AngleDiff(sprite[nSprite].ang, nAng) < 64) + { + SetList[nSet].nAction = 4; + SetList[nSet].field_2 = 0; + } + break; + } + else + { + SetList[nSet].field_C = 1; + SetList[nSet].nAction = 7; + SetList[nSet].field_2 = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + return; + } + } + + break; + } + else + { + SetList[nSet].nAction = 0; + SetList[nSet].field_2 = 0; + return; + } + } + + case 4: + { + if (nTarget == -1) + { + SetList[nSet].nAction = 0; + SetList[nSet].field_A = 50; + } + else + { + if (PlotCourseToSprite(nSprite, nTarget) >= 768) + { + SetList[nSet].nAction = 3; + } + else if (nFlag & 0x80) + { + runlist_DamageEnemy(nTarget, nSprite, 5); + } + } + + break; + } + + case 5: + { + if (var_24) + { + SetList[nSet].nAction = 0; + SetList[nSet].field_A = 15; + } + return; + } + + case 6: + { + if (nFlag & 0x80) + { + // low 16 bits of returned var contains the sprite index, the high 16 the bullet number + int nBullet = BuildBullet(nSprite, 11, 0, 0, -1, sprite[nSprite].ang, nTarget + 10000, 1); + SetBulletEnemy(nBullet >> 16, nTarget); // isolate the bullet number (shift off the sprite index) + + SetList[nSet].field_E--; + if (SetList[nSet].field_E <= 0 || !RandomBit()) + { + SetList[nSet].nAction = 0; + SetList[nSet].field_2 = 0; + } + } + return; + } + + case 7: + { + if (var_24) + { + if (SetList[nSet].field_C) + { + sprite[nSprite].zvel = -10000; + } + else + { + sprite[nSprite].zvel = -(PlotCourseToSprite(nSprite, nTarget)); + } + + SetList[nSet].nAction = 8; + SetList[nSet].field_2 = 0; + + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512); + sprite[nSprite].yvel = Sin(sprite[nSprite].ang); + } + return; + } + + case 8: + { + if (var_24) + { + SetList[nSet].field_2 = SeqSize[nSeq] - 1; + } + + if (nVal & 0x20000) + { + SetQuake(nSprite, 200); + SetList[nSet].nAction = 9; + SetList[nSet].field_2 = 0; + } + return; + } + + case 9: + { + sprite[nSprite].xvel >>= 1; + sprite[nSprite].yvel >>= 1; + + if (var_24) + { + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + + PlotCourseToSprite(nSprite, nTarget); + + SetList[nSet].nAction = 6; + SetList[nSet].field_2 = 0; + SetList[nSet].field_E = 5; + + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + } + return; + } + + case 10: + { + if (nFlag & 0x80) + { + sprite[nSprite].z -= GetSpriteHeight(nSprite); + BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqSet, 76, 0)); + sprite[nSprite].z += GetSpriteHeight(nSprite); + } + + if (var_24) + { + SetList[nSet].nAction = 11; + SetList[nSet].field_2 = 0; + + runlist_ChangeChannel(SetChan[nSet], 1); + + for (int i = 0; i < 20; i++) + { + BuildSoul(nSet); + } + } + return; + } + + case 11: + { + sprite[nSprite].cstat &= 0x0FEFE; + return; + } + } + + // loc_33AE3: ? + if (nAction) + { + if (nTarget != -1) + { + if (!(sprite[nTarget].cstat & 0x101)) + { + SetList[nSet].nAction = 0; + SetList[nSet].field_2 = 0; + SetList[nSet].field_A = 100; + SetList[nSet].nTarget = -1; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } + } + } + + return; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/set.h b/source/exhumed/src/set.h new file mode 100644 index 000000000..74d4ddd45 --- /dev/null +++ b/source/exhumed/src/set.h @@ -0,0 +1,31 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __set_h__ +#define __set_h__ + +BEGIN_PS_NS + +void InitSets(); +int BuildSet(short nSprite, int x, int y, int z, short nSector, short nAngle, int nVal); +void FuncSoul(int, int, int); +void FuncSet(int, int, int); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/snake.cpp b/source/exhumed/src/snake.cpp new file mode 100644 index 000000000..5b52fc982 --- /dev/null +++ b/source/exhumed/src/snake.cpp @@ -0,0 +1,434 @@ +//------------------------------------------------------------------------- +/* +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 "engine.h" +#include "exhumed.h" +#include "snake.h" +#include "status.h" +#include "player.h" +#include "runlist.h" +#include "sequence.h" +#include "bullet.h" +#include "ps_input.h" +#include "anims.h" +#include "lighting.h" +#include "sound.h" +#include "move.h" +#include "trigdat.h" +#include "gun.h" +#include +#include + +BEGIN_PS_NS + +#define kMaxSnakes 50 + +int nSnakeCount = 0; +int nSnakesFree; + +short SnakeFree[kMaxSnakes]; +short nPlayerSnake[kMaxPlayers]; + +Snake SnakeList[kMaxSnakes]; +short nSnakePlayer[kMaxSnakes]; + +static SavegameHelper sgh("snake", + SV(nSnakeCount), + SV(nSnakesFree), + SA(SnakeFree), + SA(nPlayerSnake), + SA(SnakeList), + SA(nSnakePlayer), + nullptr); + +void InitSnakes() +{ + nSnakeCount = 0; + + for (int i = 0; i < kMaxSnakes; i++) { + SnakeFree[i] = i; + } + + nSnakesFree = kMaxSnakes; + memset(nPlayerSnake, 0, sizeof(nPlayerSnake)); +} + +short GrabSnake() +{ + nSnakesFree--; + return SnakeFree[nSnakesFree]; +} + +void DestroySnake(int nSnake) +{ + short nRun = SnakeList[nSnake].nRun; + runlist_SubRunRec(nRun); + + for (int i = 0; i < kSnakeSprites; i++) + { + short nSprite = SnakeList[nSnake].nSprites[i]; + + runlist_DoSubRunRec(sprite[nSprite].lotag - 1); + runlist_DoSubRunRec(sprite[nSprite].owner); + + mydeletesprite(nSprite); + } + + SnakeFree[nSnakesFree] = nSnake; + nSnakesFree++; + + if (nSnake == nSnakeCam) + { + nSnakeCam = -1; + if (!bFullScreen) { + RefreshStatus(); + } + } +} + +void ExplodeSnakeSprite(int nSprite, short nPlayer) +{ + short nDamage = BulletInfo[kWeaponStaff].nDamage; + + if (nPlayerDouble[nPlayer] > 0) { + nDamage *= 2; + } + + // take a copy of this, to revert after call to runlist_RadialDamageEnemy() + short nOwner = sprite[nSprite].owner; + sprite[nSprite].owner = PlayerList[nPlayer].nSprite; + + runlist_RadialDamageEnemy(nSprite, nDamage, BulletInfo[kWeaponStaff].field_10); + + sprite[nSprite].owner = nOwner; + + BuildAnim(-1, 23, 0, sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, sprite[nSprite].sectnum, 40, 4); + + AddFlash(sprite[nSprite].sectnum, sprite[nSprite].x, sprite[nSprite].y, sprite[nSprite].z, 128); + + StopSpriteSound(nSprite); +} + +int BuildSnake(short nPlayer, short zVal) +{ + if (!nSnakesFree) + return -1; + + zVal -= 1280; + + short nPlayerSprite = PlayerList[nPlayer].nSprite; + short nViewSect = nPlayerViewSect[nPlayer]; + short nPic = seq_GetSeqPicnum(kSeqSnakBody, 0, 0); + + int x = sprite[nPlayerSprite].x; + int y = sprite[nPlayerSprite].y; + int z = (sprite[nPlayerSprite].z + zVal) - 2560; + short nAngle = sprite[nPlayerSprite].ang; + + short hitsect, hitsprite; + int hitx, hity, hitz; + + short nSprite; + + vec3_t pos = { x, y, z }; + hitdata_t hitData; + hitscan(&pos, sprite[nPlayerSprite].sectnum, Cos(nAngle), Sin(nAngle), 0, &hitData, CLIPMASK1); + + hitx = hitData.pos.x; + hity = hitData.pos.y; + hitz = hitData.pos.z; + hitsect = hitData.sect; + hitsprite = hitData.sprite; + + int nSqrt = ksqrt(((hity - y) * (hity - y)) + ((hitx - x) * (hitx - x))); + + if (nSqrt < (sintable[512] >> 4)) + { + BackUpBullet(&hitx, &hity, nAngle); + nSprite = insertsprite(hitsect, 202); + sprite[nSprite].x = hitx; + sprite[nSprite].y = hity; + sprite[nSprite].z = hitz; + + ExplodeSnakeSprite(nSprite, nPlayer); + mydeletesprite(nSprite); + return -1; + } + else + { + short nTarget; + + if (hitsprite >= 0 && sprite[hitsprite].statnum >= 90 && sprite[hitsprite].statnum <= 199) { + nTarget = hitsprite; + } + else { + nTarget = sPlayerInput[nPlayer].nTarget; + } + + short nSnake = GrabSnake(); + +// GrabTimeSlot(3); + + short var_24; + + for (int i = 0; i < kSnakeSprites; i++) + { + nSprite = insertsprite(nViewSect, 202); + assert(nSprite >= 0 && nSprite < kMaxSprites); + + sprite[nSprite].owner = nPlayerSprite; + sprite[nSprite].picnum = nPic; + + if (i == 0) + { + sprite[nSprite].x = sprite[nPlayerSprite].x; + sprite[nSprite].y = sprite[nPlayerSprite].y; + sprite[nSprite].z = sprite[nPlayerSprite].z + zVal; + sprite[nSprite].xrepeat = 32; + sprite[nSprite].yrepeat = 32; + nViewSect = sprite[nSprite].sectnum; + var_24 = nSprite; + } + else + { + sprite[nSprite].x = sprite[var_24].x; + sprite[nSprite].y = sprite[var_24].y; + sprite[nSprite].z = sprite[var_24].z; + sprite[nSprite].xrepeat = 40 - 3*i; + sprite[nSprite].yrepeat = 40 - 3*i; + } + + sprite[nSprite].clipdist = 10; + sprite[nSprite].cstat = 0; + sprite[nSprite].shade = -64; + sprite[nSprite].pal = 0; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].ang = sprite[nPlayerSprite].ang; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].hitag = 0; + sprite[nSprite].extra = -1; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + + SnakeList[nSnake].nSprites[i] = nSprite; + + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, ((nSnake << 8) | i) | 0x110000); + } + + SnakeList[nSnake].nRun = runlist_AddRunRec(NewRun, nSnake | 0x110000); + SnakeList[nSnake].c[1] = 2; + SnakeList[nSnake].c[5] = 5; + SnakeList[nSnake].c[2] = 4; + SnakeList[nSnake].c[3] = 6; + SnakeList[nSnake].c[4] = 7; + SnakeList[nSnake].c[6] = 6; + SnakeList[nSnake].c[7] = 7; + SnakeList[nSnake].nEnemy = nTarget; + SnakeList[nSnake].sC = 1200; + SnakeList[nSnake].sE = 0; + nSnakePlayer[nSnake] = nPlayer; + nPlayerSnake[nPlayer] = nSnake; + + if (bSnakeCam) + { + if (nSnakeCam < 0) { + nSnakeCam = nSnake; + } + } + + D3PlayFX(StaticSound[kSound6], var_24); + } + + return nSprite; +} + +int FindSnakeEnemy(short nSnake) +{ + short nPlayer = nSnakePlayer[nSnake]; + short nPlayerSprite = PlayerList[nPlayer].nSprite; + + short nSprite = SnakeList[nSnake].nSprites[0]; // CHECKME + + short nAngle = sprite[nSprite].ang; + short nSector = sprite[nSprite].sectnum; + + int esi = 2048; + + int nEnemy = -1; + + for (int i = headspritesect[nSector]; i >= 0; i = nextspritesect[i]) + { + if (sprite[i].statnum >= 90 && sprite[i].statnum < 150 && (sprite[i].cstat & 0x101)) + { + if (i != nPlayerSprite && !(sprite[i].cstat & 0x8000)) + { + int nAngle2 = (nAngle - GetAngleToSprite(nSprite, i)) & kAngleMask; + if (nAngle2 < esi) + { + nEnemy = i; + esi = nAngle2; + } + } + } + } + + if (nEnemy != -1) + { + SnakeList[nSnake].nEnemy = nEnemy; + } + else + { + SnakeList[nSnake].nEnemy--; + if (SnakeList[nSnake].nEnemy < -25) + { + nEnemy = nPlayerSprite; + SnakeList[nSnake].nEnemy = nPlayerSprite; + } + } + + return nEnemy; +} + +void FuncSnake(int a, int UNUSED(nDamage), int nRun) +{ + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + case 0x20000: + { + short nSnake = RunData[nRun].nVal; + assert(nSnake >= 0 && nSnake < kMaxSnakes); + + short nSprite = SnakeList[nSnake].nSprites[0]; + + seq_MoveSequence(nSprite, SeqOffsets[kSeqSnakehed], 0); + + short nEnemySprite = SnakeList[nSnake].nEnemy; + + int nMov; + int zVal; + + if (nEnemySprite < 0) + { +SEARCH_ENEMY: + nMov = movesprite(nSprite, + 600 * Cos(sprite[nSprite].ang), + 600 * Sin(sprite[nSprite].ang), + Sin(SnakeList[nSnake].sE) >> 5, + 0, 0, CLIPMASK1); + + FindSnakeEnemy(nSnake); + + zVal = 0; + } + else + { + if (!(sprite[nEnemySprite].cstat&0x101)) + { + SnakeList[nSnake].nEnemy = -1; + goto SEARCH_ENEMY; + } + + zVal = sprite[nSprite].z; + + nMov = AngleChase(nSprite, nEnemySprite, 1200, SnakeList[nSnake].sE, 32); + + zVal = sprite[nSprite].z - zVal; + } + + if (nMov) + { + short nPlayer = nSnakePlayer[nSnake]; + ExplodeSnakeSprite(SnakeList[nSnake].nSprites[0], nPlayer); + + nPlayerSnake[nPlayer] = -1; + nSnakePlayer[nSnake] = -1; + + DestroySnake(nSnake); + } + else + { + short nAngle = sprite[nSprite].ang; + int var_30 = -(64 * Cos(nAngle)); + int var_34 = -(64 * Sin(nAngle)); + + int var_20 = SnakeList[nSnake].sE; + + SnakeList[nSnake].sE = (SnakeList[nSnake].sE + 64) & 0x7FF; + + int var_28 = (nAngle + 512) & kAngleMask; + short nSector = sprite[nSprite].sectnum; + + int x = sprite[nSprite].x; + int y = sprite[nSprite].y; + int z = sprite[nSprite].z; + + for (int i = 7; i > 0; i--) + { + int nSprite2 = SnakeList[nSnake].nSprites[i]; + + sprite[nSprite2].ang = nAngle; + sprite[nSprite2].x = x; + sprite[nSprite2].y = y; + sprite[nSprite2].z = z; + + mychangespritesect(nSprite2, nSector); + + int eax = (Sin(var_20) * SnakeList[nSnake].c[i]) >> 9; + + movesprite(nSprite2, var_30 + var_30 * i + eax * Cos(var_28), var_30 + var_34 * i + eax * Sin(var_28), + -zVal*(i-1), 0, 0, CLIPMASK1); + + var_20 = (var_20 + 128) & kAngleMask; + } + } + break; + } + + case 0x90000: + { + short nSnake = RunData[nRun].nVal; + short nSprite = a & 0xFFFF; + + if ((nSnake & 0xFF) == 0) { + seq_PlotSequence(nSprite, SeqOffsets[kSeqSnakehed], 0, 0); + } + else { + seq_PlotSequence(nSprite, SeqOffsets[kSeqSnakBody], 0, 0); + } + + tsprite[nSprite].owner = -1; + break; + } + + case 0xA0000: + { + break; + } + + default: + { + Printf("unknown msg %x for bullet\n", a & 0x7F0000); + break; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/snake.h b/source/exhumed/src/snake.h new file mode 100644 index 000000000..33a84cffa --- /dev/null +++ b/source/exhumed/src/snake.h @@ -0,0 +1,60 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __snake_h__ +#define __snake_h__ + +BEGIN_PS_NS + +#define kSnakeSprites 8 // or rename to kSnakeParts? + +// 32bytes +struct Snake +{ + short nEnemy; // nRun + short nSprites[kSnakeSprites]; + + short sC; + short nRun; + + // array? + char c[8]; + /* + char c1; + char c2; + char c3; + char c4; + char c5; + char c6; + char c7; + char c8; + */ + + short sE; +}; + +extern Snake SnakeList[]; + +void InitSnakes(); +short GrabSnake(); +int BuildSnake(short nPlayer, short zVal); +void FuncSnake(int, int, int); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/sound.cpp b/source/exhumed/src/sound.cpp new file mode 100644 index 000000000..1044ea15b --- /dev/null +++ b/source/exhumed/src/sound.cpp @@ -0,0 +1,988 @@ +//------------------------------------------------------------------------- +/* +Copyright (C) 2010-2019 EDuke32 developers and contributors +Copyright (C) 2019 Nuke.YKT, sirlemonhead +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 "baselayer.h" +#include "build.h" +#include "engine.h" +#include "exhumed.h" +#include "sound.h" +#include "init.h" +#include "object.h" +#include "player.h" +#include "random.h" +#include "snake.h" +#include "trigdat.h" +#include "sequence.h" +#include "cd.h" +#include "sound/s_soundinternal.h" + +BEGIN_PS_NS + +const char *SoundFiles[kMaxSoundFiles] = +{ + "spl_big", + "spl_smal", + "bubble_l", + "grn_drop", + "p_click", + "grn_roll", + "cosprite", + "m_chant0", + "anu_icu", + "item_reg", + "item_spe", // 10 + "item_key", + "torch_on", // 12 + "jon_bnst", + "jon_gasp", + "jon_land", + "jon_gags", + "jon_fall", + "jon_drwn", + "jon_air1", + "jon_glp1", // 20 + "jon_bbwl", + "jon_pois", + "amb_ston", + "cat_icu", + "bubble_h", + "set_land", + "jon_hlnd", + "jon_laf2", + "spi_jump", + "jon_scub", // 30 + "item_use", + "tr_arrow", + "swi_foot", + "swi_ston", + "swi_wtr1", + "tr_fire", + "m_skull5", + "spi_atak", + "anu_hit", + "fishdies", // 40 + "scrp_icu", + "jon_wade", + "amb_watr", + "tele_1", + "wasp_stg", + "res", + "drum4", + "rex_icu", + "m_hits_u", + "q_tail", // 50 + "vatr_mov", + "jon_hit3", + "jon_t_2", // 53 + "jon_t_1", + "jon_t_5", + "jon_t_6", + "jon_t_8", + "jon_t_4", + "rasprit1", + "jon_fdie", // 60 + "wijaf1", + "ship_1", + "saw_on", + "ra_on", + "amb_ston", // 65 + "vatr_stp", // 66 + "mana1", + "mana2", + "ammo", + "pot_pc1", // 70? + "pot_pc2", + "weapon", + "alarm", + "tick1", + "scrp_zap", // 75 + "jon_t_3", + "jon_laf1", + "blasted", + "jon_air2" // 79 +}; + +short nAmbientChannel = -1; + +short nStopSound; +short nStoneSound; +short nSwitchSound; +short nLocalEyeSect; +short nElevSound; +short nCreepyTimer; + +bool looped[kMaxSounds]; + +struct ActiveSound +{ + short snd_sprite; + short snd_id; + short snd_volume; + short snd_angle; + short snd_ambientflag; + short snd_priority; + int snd_handle; + FSoundChan* snd_channel; + int snd_pitch; + int snd_time; + int snd_x; + int snd_y; + int snd_z; + short snd_sector; +}; + +ActiveSound sActiveSound[kMaxSounds]; +short StaticSound[kMaxSounds]; +int fakesources[] = { 0, 1, 2, 3 }; +int nLocalChan = 0; + +enum +{ + nSwirlyChan1 = 1, + nSwirlyChan2, + nSwirlyChan3, + nSwirlyChan4, +}; + +//========================================================================== +// +// +// +//========================================================================== + +class EXSoundEngine : public SoundEngine +{ + // client specific parts of the sound engine go in this class. + void CalcPosVel(int type, const void* source, const float pt[3], int channum, int chanflags, FSoundID chanSound, FVector3* pos, FVector3* vel, FSoundChan* chan) override; + TArray ReadSound(int lumpnum) override; + void ChannelEnded(FISoundChannel* chan) override + { + for (auto& inf : sActiveSound) + { + if (inf.snd_channel == chan) inf.snd_channel = nullptr; + } + SoundEngine::ChannelEnded(chan); + } + +public: + EXSoundEngine() + { + int eax = 260; + TArray disttable(256, 1); + + for (int i = 0; i < 256; i++) + { + if (eax > 65280) + { + disttable[i] = 0; + } + else + { + disttable[i] = 255 - eax >> 8; + + eax = (eax * eax) >> 8; + } + } + + S_Rolloff.RolloffType = ROLLOFF_Custom; + S_Rolloff.MinDistance = 0; + S_Rolloff.MaxDistance = 4096; // It's really this big + Init(disttable, 255); + } +}; + +//========================================================================== +// +// +// +//========================================================================== + +TArray EXSoundEngine::ReadSound(int lumpnum) +{ + auto wlump = fileSystem.OpenFileReader(lumpnum); + return wlump.Read(); +} + + +//========================================================================== +// +// +// +//========================================================================== + +int LoadSound(const char* name) +{ + FString nname(name, 8); + int sndid = soundEngine->FindSoundNoHash(nname.GetChars()); + if (sndid > 0) return sndid - 1; + + FStringf filename("%s.voc", nname.GetChars()); + auto lump = fileSystem.FindFile(filename); + if (lump > 0) + { + auto &S_sfx = soundEngine->GetSounds(); + S_sfx.Reserve(1); + int retval = S_sfx.Size() - 2; + auto check = fileSystem.GetFileData(lump); + if (check.Size() > 26 && check[26] == 6 && !memcmp("Creative Voice File", check.Data(), 19)) + { + // This game uses the actual loop point information in the sound data as its only means to check if a sound is looped. + looped[retval] = true; + } + auto& newsfx = S_sfx.Last(); + newsfx.Clear(); + newsfx.name = nname; + newsfx.lumpnum = lump; + newsfx.NearLimit = 6; + newsfx.bTentative = false; + soundEngine->CacheSound(retval + 1); + return retval; + } + else if (!ISDEMOVER) // demo tries to load sound files it doesn't have + { + Printf("Unable to open sound '%s'!\n", filename.GetChars()); + return 0; + } + +} +//========================================================================== +// +// +// +//========================================================================== + +void InitFX(void) +{ + if (soundEngine) return; // just in case. + soundEngine = new EXSoundEngine; + + auto& S_sfx = soundEngine->GetSounds(); + S_sfx.Resize(1); + S_sfx[0].Clear(); S_sfx[0].lumpnum = sfx_empty; + for (size_t i = 0; i < kMaxSoundFiles; i++) + { + StaticSound[i] = LoadSound(SoundFiles[i]); + } + soundEngine->HashSounds(); + + memset(sActiveSound, 255, sizeof(sActiveSound)); + for (int i = 0; i < kMaxSounds; i++) + { + sActiveSound[i].snd_channel = nullptr; + sActiveSound[i].snd_handle = -1; + } + + nCreepyTimer = kCreepyCount; +} + + +//========================================================================== +// +// +// +//========================================================================== + +void GetSpriteSoundPitch(short nSector, int* pVolume, int* pPitch, int nLocalSectFlags) +{ + if (nSector < 0) + return; + if ((SectFlag[nSector] ^ nLocalSectFlags) & kSectUnderwater) + { + *pVolume >>= 1; + *pPitch -= 1200; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void BendAmbientSound(void) +{ + if (nAmbientChannel < 0) + return; + ActiveSound* pASound = &sActiveSound[nAmbientChannel]; + if (pASound->snd_channel) + { + soundEngine->SetPitch(pASound->snd_channel, (nDronePitch + 11800) / 11025.f); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void PlayLocalSound(short nSound, short nRate, bool unattached) +{ + if (nSound < 0 || nSound >= kMaxSounds || !soundEngine->isValidSoundId(nSound + 1)) + { + initprintf("PlayLocalSound: Invalid sound nSound == %i, nRate == %i\n", nSound, nRate); + return; + } + int bLoop = looped[nSound]; + + if (nLocalChan == nAmbientChannel) + nAmbientChannel = -1; + + + ActiveSound* pASound = nullptr; + + if (!unattached) + { + pASound = &sActiveSound[nLocalChan]; + if (pASound->snd_channel != nullptr) + soundEngine->StopChannel(pASound->snd_channel); + } + + // There is exactly one occurence in the entire game which alters the pitch, and that's the laugh on the logo. + auto chan = soundEngine->StartSound(SOURCE_Unattached, nullptr, nullptr, CHAN_BODY, CHANF_OVERLAP, nSound + 1, 1.f, ATTN_NONE, nullptr); + + if (nRate && chan) + { + float ratefac = (11025 + nRate) / 11025.f; + soundEngine->SetPitch(chan, ratefac); + } + if (pASound) + { + pASound->snd_id = nSound; + pASound->snd_channel = chan; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +int LocalSoundPlaying(void) +{ + return sActiveSound[nLocalChan].snd_channel != nullptr; +} + +int GetLocalSound(void) +{ + if (LocalSoundPlaying() == -1) + return -1; + + return sActiveSound[nLocalChan].snd_id & 0x1ff; +} + +//========================================================================== +// +// +// +//========================================================================== + +void StopLocalSound(void) +{ + if (nLocalChan == nAmbientChannel) + nAmbientChannel = -1; + + if (LocalSoundPlaying()) + { + soundEngine->StopChannel(sActiveSound[nLocalChan].snd_channel); + sActiveSound[nLocalChan].snd_channel = nullptr; + } +} + +//========================================================================== +// +// +// +//========================================================================== +int nNextFreq; +int nSwirlyFrames; + +void StartSwirly(int nActiveSound) +{ + ActiveSound* pASound = &sActiveSound[nActiveSound]; + pASound->snd_angle = rand() & 0x7ff; + + short nPitch = nNextFreq - RandomSize(9); + pASound->snd_pitch = nPitch; + nNextFreq = 25000 - RandomSize(10) * 6; + if (nNextFreq > 32000) + nNextFreq = 32000; + + int nVolume = nSwirlyFrames + 1; + if (nVolume >= 220) + nVolume = 220; + + pASound->snd_volume = nVolume; + if (pASound->snd_channel) soundEngine->StopChannel(pASound->snd_channel); + + pASound->snd_channel = soundEngine->StartSound(SOURCE_Swirly, &fakesources[nActiveSound-1], nullptr, CHAN_BODY, 0, StaticSound[kSound67]+1, nVolume / 255.f, ATTN_NONE, nullptr, nPitch / 11025.f); +} + +//========================================================================== +// +// +// +//========================================================================== + +void StartSwirlies() +{ + StopAllSounds(); + + nNextFreq = 19000; + nSwirlyFrames = 0; + + for (int i = nSwirlyChan1; i <= nSwirlyChan4; i++) + StartSwirly(i); +} + +//========================================================================== +// +// +// +//========================================================================== + +void UpdateSwirlies() +{ + nSwirlyFrames++; + for (int i = nSwirlyChan1; i <= nSwirlyChan4; i++) + { + ActiveSound* pASound = &sActiveSound[i]; + + if (pASound->snd_channel == nullptr) + StartSwirly(i); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void SoundBigEntrance(void) +{ + StopAllSounds(); + ActiveSound* pASound = sActiveSound+1; + for (int i = 0; i < 4; i++, pASound++) + { + short nPitch = i * 512 - 1200; + pASound->snd_pitch = nPitch; + if (pASound->snd_channel) soundEngine->StopChannel(pASound->snd_channel); + pASound->snd_channel = soundEngine->StartSound(SOURCE_EXBoss, &fakesources[i], nullptr, CHAN_BODY, 0, StaticSound[kSoundTorchOn]+1, 200 / 255.f, ATTN_NONE, nullptr, nPitch / 11025.f); + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +void EXSoundEngine::CalcPosVel(int type, const void* source, const float pt[3], int channum, int chanflags, FSoundID chanSound, FVector3* pos, FVector3* vel, FSoundChan* chan) +{ + if (pos != nullptr) + { + vec3_t campos; + if (nSnakeCam > -1) + { + Snake* pSnake = &SnakeList[nSnakeCam]; + spritetype* pSnakeSprite = &sprite[pSnake->nSprites[0]]; + campos.x = pSnakeSprite->x; + campos.y = pSnakeSprite->y; + campos.z = pSnakeSprite->z; + } + else + { + campos = { initx, inity, initz }; + } + auto fcampos = GetSoundPos(&campos); + + if (type == SOURCE_Unattached) + { + pos->X = pt[0]; + pos->Y = pt[1]; + pos->Z = pt[2]; + } + // Do some angular magic. The original was just 2D panning which in a 3D sound field is not sufficient. + else if (type == SOURCE_Swirly) + { + int which = *(int*)source; + float phase = ((int)totalclock << (4 + which)) * (M_PI / 1024); + pos->X = fcampos.X + 256 * cos(phase); + pos->Y = fcampos.Y + 256 * sin(phase); + } + else if (type == SOURCE_EXBoss) + { + int which = *(int*)source; + *pos = fcampos; + // Should be positioned in 90° intervals. + switch (which) + { + default: + case 0: pos->X -= 256; break; + case 1: pos->Y -= 256; break; + case 2: pos->X += 256; break; + case 3: pos->Y += 256; break; + } + } + else if (type == SOURCE_Actor) + { + auto actor = (spritetype*)source; + assert(actor != nullptr); + if (actor != nullptr) + { + *pos = GetSoundPos(&actor->pos); + } + } + if ((chanflags & CHANF_LISTENERZ) && type != SOURCE_None) + { + pos->Y = fcampos.Z; + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +int GetDistFromDXDY(int dx, int dy) +{ + int nSqr = dx*dx+dy*dy; + return (nSqr>>3)-(nSqr>>5); +} + +void UpdateSounds() +{ + if (nFreeze) + return; + + int nLocalSectFlags = SectFlag[nPlayerViewSect[nLocalPlayer]]; + + vec3_t pos; + short ang; + if (nSnakeCam > -1) + { + Snake *pSnake = &SnakeList[nSnakeCam]; + spritetype *pSnakeSprite = &sprite[pSnake->nSprites[0]]; + pos = pSnakeSprite->pos; + ang = pSnakeSprite->ang; + } + else + { + pos = { initx, inity, initz }; + ang = inita; + } + auto fv = GetSoundPos(&pos); + SoundListener listener; + listener.angle = -(float)ang * pi::pi() / 1024; // Build uses a period of 2048. + listener.velocity.Zero(); + listener.position = GetSoundPos(&pos); + listener.underwater = false; + // This should probably use a real environment instead of the pitch hacking in S_PlaySound3D. + // listenactor->waterlevel == 3; + //assert(primaryLevel->Zones.Size() > listenactor->Sector->ZoneNumber); + listener.Environment = 0;// primaryLevel->Zones[listenactor->Sector->ZoneNumber].Environment; + listener.valid = true; + + + soundEngine->SetListener(listener); + soundEngine->UpdateSounds((int)totalclock); + ActiveSound* pASound = sActiveSound; + pASound++; + for (int i = 1; i < kMaxActiveSounds; i++, pASound++) + { + if (pASound->snd_channel != nullptr) + { + if (pASound->snd_channel->ChanFlags & CHANF_FORGETTABLE) + { + // If the channel has become invalid, remove the reference. + // ChannelEnded may be called late so waiting for it is problematic. + pASound->snd_channel = nullptr; + } + else + { + short nSoundSprite = pASound->snd_sprite; + int nPitch = pASound->snd_pitch; + short nSoundSect; + if (nSoundSprite >= 0) + { + if (nSoundSprite == nLocalSpr) + nSoundSect = nPlayerViewSect[nLocalPlayer]; + else + nSoundSect = sprite[nSoundSprite].sectnum; + } + else + nSoundSect = pASound->snd_sector; + + int nVolume = pASound->snd_volume; + GetSpriteSoundPitch(nSoundSect, &nVolume, &nPitch, nLocalSectFlags); + soundEngine->SetPitch(pASound->snd_channel, (11025 + nPitch) / 11025.f); + soundEngine->SetVolume(pASound->snd_channel, nVolume / 255.f); + } + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +int soundx, soundy, soundz; +short soundsect; + +short PlayFX2(unsigned short nSound, short nSprite) +{ + if ((nSound&0x1ff) >= kMaxSounds || !soundEngine->isValidSoundId((nSound & 0x1ff)+1)) + { + initprintf("PlayFX2: Invalid sound nSound == %i, nSprite == %i\n", nSound, nSprite); + return -1; + } + + int nLocalSectFlags = SectFlag[nPlayerViewSect[nLocalPlayer]]; + short v1c; + short vcx; + if (nSprite < 0) + { + vcx = 0; + v1c = 0; + } + else + { + v1c = nSprite&0x2000; + vcx = nSprite&0x4000; + nSprite &= 0xfff; + soundx = sprite[nSprite].x; + soundy = sprite[nSprite].y; + soundz = sprite[nSprite].z; + } + int dx, dy; + + dx = initx-soundx; + dy = inity-soundy; + + dx >>= 8; dy >>= 8; + + short nSoundAng; + if ((dx|dy) == 0) + nSoundAng = 0; + else + nSoundAng = AngleDelta(GetMyAngle(dx, dy), inita, 1024); + + int nDist = GetDistFromDXDY(dx, dy); + if (nDist >= 255) + { + if ((int16_t)nSound > -1) + StopSpriteSound(nSound); + return -1; + } + + int nVolume; + + if (!v1c) + { + nVolume = snd_fxvolume+10-(Sin(nDist<<1)>>6)-10; + if (nVolume <= 0) + { + if ((int16_t)nSound > -1) + StopSpriteSound(nSound); + return -1; + } + if (nVolume > 255) + nVolume = 255; + } + else + nVolume = snd_fxvolume; + + short vc = nSound & (~0x1ff); + short v4 = nSound & 0x2000; + short v8 = nSound & 0x1000; + short v14 = nSound & 0x4000; + short v10 = (nSound&0xe00)>>9; + int v2c = 0x7fffffff; + ActiveSound* v38 = NULL; + ActiveSound* v28 = NULL; + ActiveSound* vdi = NULL; + nSound &= 0x1ff; + + short priority; + + if (v8 || v14) + priority = 1000; + else if (nSprite != -1 && vcx) + priority = 2000; + else + priority = 0; + ActiveSound* pASound = sActiveSound; + pASound++; + for (int i = 1; i < kMaxActiveSounds; i++, pASound++) + { + if (pASound->snd_channel == nullptr) + { + vdi = pASound; + } + else if (priority >= pASound->snd_priority) + { + if (v2c > pASound->snd_time && pASound->snd_priority <= priority) + { + v28 = pASound; + v2c = pASound->snd_time; + } + if (!v8) + { + if (nSound == pASound->snd_id) + { + if (v4 == 0 && nSprite == pASound->snd_sprite) + return -1; + if (priority >= pASound->snd_priority) + v38 = pASound; + } + else if (nSprite == pASound->snd_sprite) + { + if (v4 || nSound != pASound->snd_id) + { + vdi = pASound; + break; + } + } + } + } + } + + if (!vdi) + { + if (v38) + vdi = v38; + else if (v28) + vdi = v28; + } + + if (vdi->snd_channel != nullptr) + { + soundEngine->StopChannel(vdi->snd_channel); + vdi->snd_channel = nullptr; + if (short(vdi - sActiveSound) == nAmbientChannel) // f_2c was never set to anything other than 0. + nAmbientChannel = -1; + } + + int nPitch; + if (v10) + nPitch = -(totalmoves&((1<snd_pitch = nPitch; + if (nSprite < 0) + { + vdi->snd_x = soundx; + vdi->snd_y = soundy; + vdi->snd_sector = soundsect; + } + GetSpriteSoundPitch(soundsect, &nVolume, &nPitch, nLocalSectFlags); + vdi->snd_volume = nVolume; + vdi->snd_angle = nSoundAng; + vdi->snd_sprite = nSprite; + vdi->snd_id = nSound; + vdi->snd_time = (int)totalclock; + vdi->snd_priority = priority; + vdi->snd_ambientflag = vc; + + int bLoop = looped[nSound]; + + if (nSprite) + { + vdi->snd_channel = soundEngine->StartSound(SOURCE_Actor, &sprite[nSprite], nullptr, CHAN_BODY, CHANF_OVERLAP, nSound+1, nVolume / 255.f, ATTN_NORM, nullptr, (11025 + nPitch) / 11025.f); + } + else + { + vec3_t v = { soundx, soundy, soundz }; + FVector3 vv = GetSoundPos(&v); + vdi->snd_channel = soundEngine->StartSound(SOURCE_Unattached, nullptr, &vv, CHAN_BODY, CHANF_OVERLAP, nSound+1, nVolume / 255.f, ATTN_NORM, nullptr, (11025 + nPitch) / 11025.f); + } + + if (v14) + nAmbientChannel = v14; + + // Nuke: added nSprite >= 0 check + if (nSprite != nLocalSpr && nSprite >= 0 && (sprite[nSprite].cstat&257)) + nCreepyTimer = kCreepyCount; + + return v14; + } + return -1; +} + +//========================================================================== +// +// +// +//========================================================================== + +short PlayFXAtXYZ(unsigned short ax, int x, int y, int z, int nSector) +{ + soundx = x; + soundy = y; + soundz = z; + soundsect = nSector&0x3fff; + short nSnd = PlayFX2(ax, -1); + if (nSnd > -1 && (nSector&0x4000)) + sActiveSound[nSnd].snd_priority = 2000; + return nSnd; +} + +//========================================================================== +// +// +// +//========================================================================== + +void CheckAmbience(short nSector) +{ + if (SectSound[nSector] != -1) + { + short nSector2 = SectSoundSect[nSector]; + walltype* pWall = &wall[sector[nSector2].wallptr]; + if (nAmbientChannel < 0) + { + PlayFXAtXYZ(SectSound[nSector] | 0x4000, pWall->x, pWall->y, sector[nSector2].floorz, nSector); + return; + } + ActiveSound* pASound = &sActiveSound[nAmbientChannel]; + if (nSector == nSector2) + { + spritetype* pSprite = &sprite[PlayerList[0].nSprite]; + pASound->snd_x = pSprite->x; + pASound->snd_y = pSprite->y; + pASound->snd_z = pSprite->z; + } + else + { + pASound->snd_x = pWall->x; + pASound->snd_y = pWall->y; + pASound->snd_z = sector[nSector2].floorz; + } + } + else if (nAmbientChannel != -1) + { + if (sActiveSound[nAmbientChannel].snd_channel) + soundEngine->StopChannel(sActiveSound[nAmbientChannel].snd_channel); + sActiveSound[nAmbientChannel].snd_channel = nullptr; + nAmbientChannel = -1; + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +void UpdateCreepySounds() +{ + if (levelnum == 20 || nFreeze) + return; + spritetype* pSprite = &sprite[PlayerList[nLocalPlayer].nSprite]; + nCreepyTimer--; + if (nCreepyTimer <= 0) + { + if (nCreaturesLeft > 0 && !(SectFlag[nPlayerViewSect[nLocalPlayer]] & 0x2000)) + { + int vsi = seq_GetFrameSound(SeqOffsets[kSeqCreepy], totalmoves % SeqSize[SeqOffsets[kSeqCreepy]]); + if (vsi >= 0 && (vsi & 0x1ff) < kMaxSounds) + { + int vdx = (totalmoves + 32) & 31; + if (totalmoves & 1) + vdx = -vdx; + int vax = (totalmoves + 32) & 63; + if (totalmoves & 2) + vax = -vax; + + PlayFXAtXYZ(vsi, pSprite->x + vdx, pSprite->y + vax, pSprite->z, pSprite->sectnum); + } + } + nCreepyTimer = kCreepyCount; + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +short D3PlayFX(unsigned short nSound, short nVal) +{ + return PlayFX2(nSound, nVal); +} + +void StopSpriteSound(short nSprite) +{ + for (int i = 0; i < kMaxActiveSounds; i++) + { + if (sActiveSound[i].snd_channel != nullptr && nSprite == sActiveSound[i].snd_sprite) + { + soundEngine->StopChannel(sActiveSound[i].snd_channel); + sActiveSound[i].snd_channel = nullptr; + return; + } + } +} + +void StopAllSounds(void) +{ + soundEngine->StopAllChannels(); + for (int i = 0; i < kMaxActiveSounds; i++) + { + sActiveSound[i].snd_channel = nullptr; + } + + nAmbientChannel = -1; +} + +//========================================================================== +// +// +// +//========================================================================== + +void PlayTitleSound(void) +{ + PlayLocalSound(StaticSound[kSound10], 0); +} + +void PlayLogoSound(void) +{ + PlayLocalSound(StaticSound[kSound28], 7000); +} + +void PlayGameOverSound(void) +{ + PlayLocalSound(StaticSound[kSound28], 0); +} + +END_PS_NS diff --git a/source/exhumed/src/sound.h b/source/exhumed/src/sound.h new file mode 100644 index 000000000..02276dcc2 --- /dev/null +++ b/source/exhumed/src/sound.h @@ -0,0 +1,153 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __sound_h__ +#define __sound_h__ + +BEGIN_PS_NS + + +#define kMaxSoundFiles 80 +#define kMaxSounds 200 +#define kMaxSoundNameLen 8 +#define kMaxActiveSounds 8 + +#define kCreepyCount 150 + +#define MUSIC_ID (-65536) + +enum { + kSound0 = 0, + kSound1, + kSound2, + kSound3, + kSound4, + kSound5, + kSound6, + kSound7, + kSound8, + kSound9, + kSound10, + kSound11, + kSoundTorchOn, + kSound13, + kSound14, + kSound15, + kSound16, + kSound17, + kSound18, + kSound19, + kSound20, + kSound21, + kSound22, + kSound23, + kSound24, + kSound25, + kSound26, + kSound27, + kSound28, + kSound29, + kSound30, + kSound31, + kSound32, + kSound33, + kSound34, + kSound35, + kSound36, + kSound38 = 38, + kSound39, + kSound40, + kSound41, + kSound42, + kSound43, + kSound47 = 47, + kSound48 = 48, + kSoundQTail = 50, + kSound52 = 52, + kSoundTauntStart = 53, + kSoundJonFDie = 60, + kSound61, + kSound62, + kSound63, + kSound64, + kSound65, + kSound66, + kSound67, + kSound68, + kSound69, + kSound70, + kSound71, + kSound72, + kSoundAlarm, + kSound74, + kSound75, + kSound76, + kSound77, + kSound78, + kSound79, +}; + +extern short gMusicVolume; +extern short gFXVolume; + +extern short nStopSound; +extern short nStoneSound; +extern short nSwitchSound; +extern short nLocalEyeSect; +extern short nElevSound; +extern short nCreepyTimer; + +extern short StaticSound[]; + + +void UpdateSounds(); +void UpdateCreepySounds(); + +void InitFX(); +void UnInitFX(); +void FadeSong(); +int fadecdaudio(); +int LocalSoundPlaying(); +void LoadFX(); +void StopAllSounds(); +int GetLocalSound(); +void UpdateLocalSound(); +void StopLocalSound(); +void PlayLocalSound(short nSound, short val, bool unattached = false); +int LoadSound(const char* sound); + +void BendAmbientSound(); +void CheckAmbience(short nSector); + +short PlayFX2(unsigned short nSound, short nSprite); +short PlayFXAtXYZ(unsigned short ax, int x, int y, int z, int nSector); +short D3PlayFX(unsigned short nSound, short nVal); +void StopSpriteSound(short nSprite); + +void StartSwirlies(); +void UpdateSwirlies(); + +void PlayLogoSound(void); +void PlayTitleSound(void); +void PlayGameOverSound(void); + +void SoundBigEntrance(void); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/spider.cpp b/source/exhumed/src/spider.cpp new file mode 100644 index 000000000..d31ee785c --- /dev/null +++ b/source/exhumed/src/spider.cpp @@ -0,0 +1,447 @@ +//------------------------------------------------------------------------- +/* +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 "exhumed.h" +#include "spider.h" +#include "engine.h" +#include "runlist.h" +#include "move.h" +#include "sequence.h" +#include "random.h" +#include "sound.h" +#include "trigdat.h" +#include + +BEGIN_PS_NS + +short SpiderSprite = -1; +short SpiderCount = 0; + +#define kMaxSpiders 100 + +struct Spider +{ + short nHealth; + short b; + short nAction; + short nSprite; + short nTarget; + short f; + short g; + short h; +}; + +Spider SpiderList[kMaxSpiders]; + +static actionSeq ActionSeq[] = { {16, 0}, {8, 0}, {32, 0}, {24, 0}, {0, 0}, {40, 1}, {41, 1} }; + +static SavegameHelper sgh("spider", + SV(SpiderSprite), + SV(SpiderCount), + SA(SpiderList), + nullptr); + +void InitSpider() +{ + SpiderCount = 0; + SpiderSprite = 1; +} + +int BuildSpider(int nSprite, int x, int y, int z, short nSector, int nAngle) +{ + int nSpider = SpiderCount++; + if (nSpider >= kMaxSpiders) { + return -1; + } + + if (nSprite == -1) + { + nSprite = insertsprite(nSector, 99); + } + else + { + changespritestat(nSprite, 99); + x = sprite[nSprite].x; + y = sprite[nSprite].y; + z = sprite[nSprite].z; + nAngle = sprite[nSprite].ang; + } + + assert(nSprite >= 0 && nSprite < kMaxSprites); + + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + sprite[nSprite].cstat = 0x101; + sprite[nSprite].shade = -12; + sprite[nSprite].clipdist = 15; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].xrepeat = 40; + sprite[nSprite].yrepeat = 40; + sprite[nSprite].pal = sector[sprite[nSprite].sectnum].ceilingpal; + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].ang = nAngle; + sprite[nSprite].picnum = 1; + sprite[nSprite].hitag = 0; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].extra = -1; + + // GrabTimeSlot(3); + + SpiderList[nSpider].nAction = 0; + SpiderList[nSpider].b = 0; + SpiderList[nSpider].nSprite = nSprite; + SpiderList[nSpider].nTarget = -1; + SpiderList[nSpider].nHealth = 160; + + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nSpider | 0xC0000); + + SpiderList[nSpider].h = runlist_AddRunRec(NewRun, nSpider | 0xC0000); + + nCreaturesLeft++; + + return nSpider | 0xC0000; +} + +void FuncSpider(int a, int nDamage, int nRun) +{ + short nSpider = RunData[nRun].nVal; + assert(nSpider >= 0 && nSpider < kMaxSpiders); + + int var_14; + + short nSprite = SpiderList[nSpider].nSprite; + short nAction = SpiderList[nSpider].nAction; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + case 0x20000: + { + var_14 = 6; + + if (SpiderList[nSpider].nHealth) + { + if (sprite[nSprite].cstat & 8) + { + sprite[nSprite].z = sector[sprite[nSprite].sectnum].ceilingz + GetSpriteHeight(nSprite); + } + else + { + Gravity(nSprite); + } + } + + int nSeq = SeqOffsets[kSeqSpider] + ActionSeq[nAction].a; + + sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, SpiderList[nSpider].b); + + seq_MoveSequence(nSprite, nSeq, SpiderList[nSpider].b); + + int nFrameFlag = FrameFlag[SeqBase[nSeq] + SpiderList[nSpider].b]; + + SpiderList[nSpider].b++; + if (SpiderList[nSpider].b >= SeqSize[nSeq]) { + SpiderList[nSpider].b = 0; + } + + short nTarget = SpiderList[nSpider].nTarget; + + if (nTarget <= -1 || sprite[nTarget].cstat & 0x101) + { + switch (nAction) + { + default: + return; + + case 0: + { + if ((nSpider & 0x1F) == (totalmoves & 0x1F)) + { + if (nTarget < 0) { + nTarget = FindPlayer(nSprite, 100); + } + + if (nTarget >= 0) + { + SpiderList[nSpider].nAction = 1; + SpiderList[nSpider].b = 0; + SpiderList[nSpider].nTarget = nTarget; + + sprite[nSprite].xvel = Cos(sprite[nSprite].ang); + sprite[nSprite].yvel = sintable[sprite[nSprite].ang]; // NOTE - not angle masking here in original code + return; + } + } + + break; + } + case 1: + { + if (nTarget >= 0) { + var_14++; + } + goto case_3; + break; + } + case 4: + { + if (!SpiderList[nSpider].b) + { + SpiderList[nSpider].b = 0; + SpiderList[nSpider].nAction = 1; + } + //break; // fall through + fallthrough__; + } + case 3: + { + case_3: + short nSector = sprite[nSprite].sectnum; + + if (sprite[nSprite].cstat & 8) + { + sprite[nSprite].zvel = 0; + sprite[nSprite].z = sector[nSector].ceilingz + (tilesiz[sprite[nSprite].picnum].y << 5); + + if (sector[nSector].ceilingstat & 1) + { + sprite[nSprite].cstat ^= 8; + sprite[nSprite].zvel = 1; + + SpiderList[nSpider].nAction = 3; + SpiderList[nSpider].b = 0; + } + } + + if ((totalmoves & 0x1F) == (nSpider & 0x1F)) + { + PlotCourseToSprite(nSprite, nTarget); + + if (RandomSize(3)) + { + sprite[nSprite].xvel = Cos(sprite[nSprite].ang); + sprite[nSprite].yvel = Sin(sprite[nSprite].ang); + } + else + { + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } + + if (SpiderList[nSpider].nAction == 1 && RandomBit()) + { + if (sprite[nSprite].cstat & 8) + { + sprite[nSprite].cstat ^= 8u; + sprite[nSprite].zvel = 1; + sprite[nSprite].z = sector[nSector].ceilingz + GetSpriteHeight(nSprite); + } + else + { + sprite[nSprite].zvel = -5120; + } + + SpiderList[nSpider].nAction = 3; + SpiderList[nSpider].b = 0; + + if (!RandomSize(3)) { + D3PlayFX(StaticSound[kSound29], nSprite); + } + } + } + break; + } + case 5: + { + if (!SpiderList[nSpider].b) + { + runlist_DoSubRunRec(sprite[nSprite].owner); + runlist_FreeRun(sprite[nSprite].lotag - 1); + runlist_SubRunRec(SpiderList[nSpider].h); + sprite[nSprite].cstat = 0x8000; + mydeletesprite(nSprite); + } + return; + } + case 2: + { + if (nTarget != -1) + { + if (nFrameFlag & 0x80) + { + runlist_DamageEnemy(nTarget, nSprite, 3); + D3PlayFX(StaticSound[kSound38], nSprite); + } + + if (PlotCourseToSprite(nSprite, nTarget) < 1024) { + return; + } + + SpiderList[nSpider].nAction = 1; + } + else + { + SpiderList[nSpider].nAction = 0; + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } + + SpiderList[nSpider].b = 0; + break; + } + } + } + else + { + SpiderList[nSpider].nTarget = -1; + SpiderList[nSpider].nAction = 0; + SpiderList[nSpider].b = 0; + + sprite[nSprite].xvel = 0; + sprite[nSprite].yvel = 0; + } + + int nMov = movesprite(nSprite, sprite[nSprite].xvel << var_14, sprite[nSprite].yvel << var_14, sprite[nSprite].zvel, 1280, -1280, CLIPMASK0); + + if (!nMov) + return; + + if (nMov & 0x10000 + && sprite[nSprite].zvel < 0 + && (hihit & 0xC000) != 0xC000 + && !((sector[sprite[nSprite].sectnum].ceilingstat) & 1)) + { + sprite[nSprite].cstat |= 8; + sprite[nSprite].z = GetSpriteHeight(nSprite) + sector[sprite[nSprite].sectnum].ceilingz; + sprite[nSprite].zvel = 0; + + SpiderList[nSpider].nAction = 1; + SpiderList[nSpider].b = 0; + return; + } + else + { + switch (nMov & 0xC000) + { + case 0x8000: + { + sprite[nSprite].ang = (sprite[nSprite].ang + 256) & 0x7EF; + sprite[nSprite].xvel = Cos(sprite[nSprite].ang); + sprite[nSprite].yvel = Sin(sprite[nSprite].ang); + return; + } + case 0xC000: + { + if ((nMov & 0x3FFF) == nTarget) + { + int nAng = getangle(sprite[nTarget].x - sprite[nSprite].x, sprite[nTarget].y - sprite[nSprite].y); + if (AngleDiff(sprite[nSprite].ang, nAng) < 64) + { + SpiderList[nSpider].nAction = 2; + SpiderList[nSpider].b = 0; + } + } + return; + } + default: + break; + } + + if (SpiderList[nSpider].nAction == 3) + { + SpiderList[nSpider].nAction = 1; + SpiderList[nSpider].b = 0; + } + return; + } + + return; + } + + case 0x90000: + { + seq_PlotSequence(a & 0xFFFF, SeqOffsets[kSeqSpider] + ActionSeq[nAction].a, SpiderList[nSpider].b, ActionSeq[nAction].b); + break; + } + + case 0xA0000: + { + if (SpiderList[nSpider].nHealth <= 0) + return; + + nDamage = runlist_CheckRadialDamage(nSprite); + // fall through + fallthrough__; + } + + case 0x80000: + { + if (!nDamage) + return; + + short nTarget = a & 0xFFFF; + + SpiderList[nSpider].nHealth -= nDamage; + if (SpiderList[nSpider].nHealth > 0) + { + /* + TODO - nTarget check was added, but should we return if it's invalid instead + or should code below (action set, b set) happen? + Other AI doesn't show consistency in this regard (see Scorpion code) + */ + if (nTarget > -1 && sprite[nTarget].statnum == 100) + { + SpiderList[nSpider].nTarget = nTarget; + } + + SpiderList[nSpider].nAction = 4; + SpiderList[nSpider].b = 0; + } + else + { + // creature is dead, make some chunks + SpiderList[nSpider].nHealth = 0; + SpiderList[nSpider].nAction = 5; + SpiderList[nSpider].b = 0; + + sprite[nSprite].cstat &= 0xFEFE; + + nCreaturesLeft--; + + for (int i = 0; i < 7; i++) + { + BuildCreatureChunk(nSprite, seq_GetSeqPicnum(kSeqSpider, i + 41, 0)); + } + } + + return; + } + + default: + { + Printf("unknown msg %d for Spider\n", a & 0x7F0000); + break; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/spider.h b/source/exhumed/src/spider.h new file mode 100644 index 000000000..681ceb549 --- /dev/null +++ b/source/exhumed/src/spider.h @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __spider_h__ +#define __spider_h__ + +BEGIN_PS_NS + +void InitSpider(); +int BuildSpider(int nSprite, int x, int y, int z, short nSector, int nAngle); +void FuncSpider(int a, int b, int nRun); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/status.cpp b/source/exhumed/src/status.cpp new file mode 100644 index 000000000..a4696afe2 --- /dev/null +++ b/source/exhumed/src/status.cpp @@ -0,0 +1,898 @@ +//------------------------------------------------------------------------- +/* +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 "engine.h" +#include "player.h" +#include "anims.h" +#include "status.h" +#include "exhumed.h" +#include "sequence.h" +#include "init.h" +#include "names.h" +#include "items.h" +#include "view.h" +#include "trigdat.h" +#include +#include +#include +#include +#include "typedefs.h" + +BEGIN_PS_NS + +short nMaskY; +static short nAnimsFree = 0; + +short statusmask[MAXXDIM]; + +short message_timer = 0; +char message_text[80]; +int magicperline; +int airperline; +int healthperline; +int nAirFrames; +int nCounter; +int nCounterDest; + +short nStatusSeqOffset; +short nItemFrames; + +int laststatusx; +int laststatusy; + +int16_t nItemSeq; +short nDigit[3]; + +short nMagicFrames; +short nHealthLevel; +short nItemFrame; +short nMeterRange; +short nMagicLevel; +short nHealthFrame; +short nMagicFrame; + +short statusx; +short statusy; +short nHealthFrames; + +short airframe; + +int16_t nFirstAnim; +int16_t nLastAnim; +short nItemAltSeq; + +short airpages = 0; + +short ammodelay = 3; + +short nCounterBullet = -1; + + +// 8 bytes +struct statusAnim +{ + int16_t s1; + int16_t s2; +// int16_t nPage; + int8_t nPrevAnim; + int8_t nNextAnim; +}; + +#define kMaxStatusAnims 50 + +statusAnim StatusAnim[kMaxStatusAnims]; +uint8_t StatusAnimsFree[kMaxStatusAnims]; +uint8_t StatusAnimFlags[kMaxStatusAnims]; + +short nItemSeqOffset[] = {91, 72, 76, 79, 68, 87, 83}; + +short word_9AD54[kMaxPlayers] = {0, 0, 0, 0, 0, 0, 0, 0}; +int dword_9AD64[kMaxPlayers] = {0, 0, 0, 0, 0, 0, 0, 0}; + +void SetCounterDigits(); +void SetItemSeq(); +void SetItemSeq2(int nSeqOffset); +void DestroyStatusAnim(short nAnim); + + +int BuildStatusAnim(int val, int nFlags) +{ + // destroy this anim if it already exists + for (int i = nFirstAnim; i >= 0; i = StatusAnim[i].nPrevAnim) + { + if (StatusAnim[i].s1 == val) { + DestroyStatusAnim(i); + break; + } + } + + if (nAnimsFree <= 0) { + return -1; + } + + nAnimsFree--; + + uint8_t nStatusAnim = StatusAnimsFree[nAnimsFree]; + + StatusAnim[nStatusAnim].nPrevAnim = -1; + StatusAnim[nStatusAnim].nNextAnim = nLastAnim; + + if (nLastAnim < 0) { + nFirstAnim = nStatusAnim; + } + else { + StatusAnim[nLastAnim].nPrevAnim = nStatusAnim; + } + + nLastAnim = nStatusAnim; + + StatusAnim[nStatusAnim].s1 = val; + StatusAnim[nStatusAnim].s2 = 0; + StatusAnimFlags[nStatusAnim] = nFlags; +// StatusAnim[nStatusAnim].nPage = numpages; + return nStatusAnim; +} + +void RefreshStatus() +{ + short nLives = nPlayerLives[nLocalPlayer]; + if (nLives < 0 || nLives > kMaxPlayerLives) { + I_Error("illegal value for nPlayerLives #%d\n", nLocalPlayer); + } + + // draws the red dots that indicate the lives amount + BuildStatusAnim(145 + (2 * nLives), 0); + + uint16_t nKeys = PlayerList[nLocalPlayer].keys; + + int val = 37; + + for (int i = 0; i < 4; i++) + { + if (nKeys & 0x1000) { + BuildStatusAnim(val, 0); + } + + nKeys >>= 1; + val += 2; + } + + SetPlayerItem(nLocalPlayer, nPlayerItem[nLocalPlayer]); + SetHealthFrame(0); + SetMagicFrame(); + SetAirFrame(); +} + +void InitStatus() +{ + nStatusSeqOffset = SeqOffsets[kSeqStatus]; + nHealthFrames = SeqSize[nStatusSeqOffset + 1]; + int nPicNum = seq_GetSeqPicnum(kSeqStatus, 1, 0); + nMagicFrames = SeqSize[nStatusSeqOffset + 129]; + nHealthFrame = 0; + nMagicFrame = 0; + nHealthLevel = 0; + nMagicLevel = 0; + nMeterRange = tilesiz[nPicNum].y; + magicperline = 1000 / nMeterRange; + healthperline = 800 / nMeterRange; + nAirFrames = SeqSize[nStatusSeqOffset + 133]; + airperline = 100 / nAirFrames; + nCounter = 0; + nCounterDest = 0; + + memset(nDigit, 0, sizeof(nDigit)); + + SetCounter(0); + SetHealthFrame(0); + SetMagicFrame(); + + for (int i = 0; i < kMaxStatusAnims; i++) { + StatusAnimsFree[i] = i; + } + + nLastAnim = -1; + nFirstAnim = -1; + nItemSeq = -1; + nAnimsFree = kMaxStatusAnims; + statusx = xdim - 320; + textpages = 0; + message_timer = 0; + statusy = ydim - 200; +} + +void MoveStatusAnims() +{ + for (int i = nFirstAnim; i >= 0; i = StatusAnim[i].nPrevAnim) + { + seq_MoveSequence(-1, nStatusSeqOffset + StatusAnim[i].s1, StatusAnim[i].s2); + + StatusAnim[i].s2++; + + short nSize = SeqSize[nStatusSeqOffset + StatusAnim[i].s1]; + + if (StatusAnim[i].s2 >= nSize) + { + if (StatusAnimFlags[i] & 0x10) { + StatusAnim[i].s2 = 0; + } + else { + StatusAnim[i].s2 = nSize - 1; // restart it + } + } + } +} + +void DestroyStatusAnim(short nAnim) +{ + int8_t nPrev = StatusAnim[nAnim].nPrevAnim; + int8_t nNext = StatusAnim[nAnim].nNextAnim; + + if (nNext >= 0) { + StatusAnim[nNext].nPrevAnim = nPrev; + } + + if (nPrev >= 0) { + StatusAnim[nPrev].nNextAnim = nNext; + } + + if (nAnim == nFirstAnim) { + nFirstAnim = nPrev; + } + + if (nAnim == nLastAnim) { + nLastAnim = nNext; + } + + StatusAnimsFree[nAnimsFree] = (uint8_t)nAnim; + nAnimsFree++; +} + +void DrawStatusAnims() +{ + for (int i = nFirstAnim; i >= 0; i = StatusAnim[i].nPrevAnim) + { + int nSequence = nStatusSeqOffset + StatusAnim[i].s1; + + seq_DrawStatusSequence(nSequence, StatusAnim[i].s2, 0); + +/* + if (StatusAnim[nAnim].s2 >= (SeqSize[nSequence] - 1)) + { + if (!(StatusAnimFlags[nAnim] & 0x10)) + { + StatusAnim[nAnim].nPage--; + if (StatusAnim[nAnim].nPage <= 0) { + DestroyStatusAnim(nAnim); + } + } + } +*/ + } +} + +void SetMagicFrame() +{ + nMagicLevel = (1000 - PlayerList[nLocalPlayer].nMagic) / magicperline; + + if (nMagicLevel >= nMeterRange) { + nMagicLevel = nMeterRange - 1; + } + + if (nMagicLevel < 0) { + nMagicLevel = 0; + } + + SetItemSeq(); +} + +void SetHealthFrame(short nVal) +{ + nHealthLevel = (800 - PlayerList[nLocalPlayer].nHealth) / healthperline; + + if (nHealthLevel >= nMeterRange ) { + nHealthLevel = nMeterRange - 1; + } + + if (nHealthLevel < 0) { + nHealthLevel = 0; + } + + if (nVal < 0) { + BuildStatusAnim(4, 0); + } +} + +void SetAirFrame() +{ + airframe = PlayerList[nLocalPlayer].nAir / airperline; + + if (airframe >= nAirFrames) + { + airframe = nAirFrames - 1; + } + else if (airframe < 0) + { + airframe = 0; + } +} + +void SetCounter(short nVal) +{ + if (nVal <= 999) + { + if (nVal < 0) { + nVal = 0; + } + } + else { + nVal = 999; + } + + nCounterDest = nVal; +} + +void SetCounterImmediate(short nVal) +{ + SetCounter(nVal); + nCounter = nCounterDest; + + SetCounterDigits(); +} + +void SetCounterDigits() +{ + nDigit[2] = 3 * (nCounter / 100 % 10); + nDigit[1] = 3 * (nCounter / 10 % 10); + nDigit[0] = 3 * (nCounter % 10); +} + +void SetItemSeq() +{ + short nItem = nPlayerItem[nLocalPlayer]; + if (nItem < 0) + { + nItemSeq = -1; + return; + } + + short nOffset = nItemSeqOffset[nItem]; + + SetItemSeq2(nOffset); +} + +void SetItemSeq2(int nSeqOffset) +{ + short nItem = nPlayerItem[nLocalPlayer]; + + if (nItemMagic[nItem] <= PlayerList[nLocalPlayer].nMagic) { + nItemAltSeq = 0; + } + else { + nItemAltSeq = 2; + } + + nItemFrame = 0; + nItemSeq = nSeqOffset + nItemAltSeq; + nItemFrames = SeqSize[nItemSeq + nStatusSeqOffset]; +} + +void SetPlayerItem(short nPlayer, short nItem) +{ + nPlayerItem[nPlayer] = nItem; + + if (nPlayer == nLocalPlayer) + { + SetItemSeq(); + if (nItem >= 0) { + BuildStatusAnim(156 + (2 * PlayerList[nLocalPlayer].items[nItem]), 0); + } + } +} + +void SetNextItem(int nPlayer) +{ + short nItem = nPlayerItem[nPlayer]; + + int i; + + for (i = 6; i > 0; i--) + { + nItem++; + if (nItem == 6) + nItem = 0; + + if (PlayerList[nPlayer].items[nItem] != 0) + break; + } + + if (i > 0) { + SetPlayerItem(nPlayer, nItem); + } +} + +void SetPrevItem(int nPlayer) +{ + if (nPlayerItem[nPlayer] == -1) + return; + + int nItem = nPlayerItem[nPlayer]; + + int i; + + for (i = 6; i > 0; i--) + { + nItem--; + if (nItem < 0) + nItem = 5; + + if (PlayerList[nPlayer].items[nItem] != 0) + break; + } + + if (i > 0) { + SetPlayerItem(nPlayer, nItem); + } +} + +void MoveStatus() +{ + if (nItemSeq >= 0) + { + nItemFrame++; + + if (nItemFrame >= nItemFrames) + { + if (nItemSeq == 67) { + SetItemSeq(); + } + else + { + nItemSeq -= nItemAltSeq; + + if (nItemAltSeq || totalmoves & 0x1F) + { + if (nItemSeq < 2) { + nItemAltSeq = 0; + } + } + else + { + nItemAltSeq = 1; + } + + nItemFrame = 0; + nItemSeq += nItemAltSeq; + nItemFrames = SeqSize[nStatusSeqOffset + nItemSeq]; + } + } + } + + if (message_timer) + { + message_timer -= 4; + if (message_timer <= 0) + { + if (screensize > 0) { + textpages = numpages; + } + + message_timer = 0; + } + } + + MoveStatusAnims(); + + if (nCounter == nCounterDest) + { + nCounter = nCounterDest; + ammodelay = 3; + return; + } + else + { + ammodelay--; + if (ammodelay > 0) { + return; + } + } + + int eax = nCounterDest - nCounter; + + if (eax <= 0) + { + if (eax >= -30) + { + for (int i = 0; i < 3; i++) + { + nDigit[i]--; + + if (nDigit[i] < 0) + { + nDigit[i] += 30; + } + + if (nDigit[i] < 27) { + break; + } + } + } + else + { + nCounter += (nCounterDest - nCounter) >> 1; + SetCounterDigits(); + return; + } + } + else + { + if (eax <= 30) + { + for (int i = 0; i < 3; i++) + { + nDigit[i]++; + + if (nDigit[i] <= 27) { + break; + } + + if (nDigit[i] >= 30) { + nDigit[i] -= 30; + } + } + } + else + { + nCounter += (nCounterDest - nCounter) >> 1; + SetCounterDigits(); + return; + } + } + + if (!(nDigit[0] % 3)) { + nCounter = nDigit[0] / 3 + 100 * (nDigit[2] / 3) + 10 * (nDigit[1] / 3); + } + + eax = nCounterDest - nCounter; + if (eax < 0) { + eax = -eax; + } + + ammodelay = 4 - (eax >> 1); + if (ammodelay < 1) { + ammodelay = 1; + } +} + +void UnMaskStatus() +{ +#if 0 + for (int i = 0; i < xdim; i++) { + startdmost[i] = ydim; + } +#endif +} + +void MaskStatus() +{ +#if 0 + for (int i = 0; i < xdim; i++) + { + short bx = startdmost[i]; + short cx = statusmask[i]; + + if (bx > cx) { + startdmost[i] = cx; + } + } +#endif +} + +void LoadStatus() +{ +#if 0 + int i; + short nSize; + short tmp; + short buffer[1024]; +// memset(buffer, 0, sizeof(buffer)); // bjd - added by me + + for (i = 0; i < xdim; i++) { + statusmask[i] = ydim; + } + + nMaskY = ydim; + + int hStatus = kopen4load("status.msk", 1); + if (!hStatus) { + return; + } + + kread(hStatus, &nSize, sizeof(nSize)); + + int nCount = nSize >> 1; + + kread(hStatus, &tmp, sizeof(tmp)); + kread(hStatus, buffer, nSize); + + kclose(hStatus); + + short *pStatusMask = statusmask; + + for (i = 0; i < nCount; i++) + { + int v8 = ydim - ((ydim * buffer[i]) / 200); + *pStatusMask++ = ydim - v8; + + if (bHiRes) { + *pStatusMask++ = ydim - v8; + } + + if (ydim - v8 < nMaskY) { + nMaskY = ydim - v8; + } + } +#endif +} + +void StatusMessage(int messageTime, const char *fmt, ...) +{ + message_timer = messageTime; + + va_list args; + va_start(args, fmt); + + vsprintf(message_text, fmt, args); + + if (screensize > 0) { + textpages = numpages; + } +} + +void DrawStatus() +{ + char numberBuf[10] = {0}; + char stringBuf[20] = {0}; + char coordBuf[50] = {0}; // not sure of the size for this? + + if (!bFullScreen && nNetTime) + { + // bjd - commenting out this check seems to fix the black status bar at 320x200 resolution +// if (bHiRes) { + NoClip(); +// } + + // draw the main bar itself + seq_DrawStatusSequence(nStatusSeqOffset, 0, 0); + + seq_DrawStatusSequence(nStatusSeqOffset + 128, 0, 0); + seq_DrawStatusSequence(nStatusSeqOffset + 127, 0, 0); + seq_DrawStatusSequence(nStatusSeqOffset + 1, nHealthFrame, nHealthLevel); + seq_DrawStatusSequence(nStatusSeqOffset + 129, nMagicFrame, nMagicLevel); + + nHealthFrame++; + if (nHealthFrame >= nHealthFrames) { + nHealthFrame = 0; + } + + nMagicFrame++; + if (nMagicFrame >= nMagicFrames) { + nMagicFrame = 0; + } + + seq_DrawStatusSequence(nStatusSeqOffset + 125, 0, 0); // draw ankh on health pool + seq_DrawStatusSequence(nStatusSeqOffset + 130, 0, 0); // draw health pool frame (top) + seq_DrawStatusSequence(nStatusSeqOffset + 131, 0, 0); // magic pool frame (bottom) + + if (nItemSeq >= 0) { + seq_DrawStatusSequence(nItemSeq + nStatusSeqOffset, nItemFrame, 0); + } + + // draws health level dots, animates breathing lungs and other things + DrawStatusAnims(); + + // draw the blue air level meter when underwater (but not responsible for animating the breathing lungs otherwise) + if (airpages) + { + seq_DrawStatusSequence(nStatusSeqOffset + 133, airframe, 0); + // airpages--; + } + + // draw compass + seq_DrawStatusSequence(nStatusSeqOffset + 35, ((inita + 128) & kAngleMask) >> 8, 0); + + if (bCoordinates) + { + sprintf(numberBuf, "%i", lastfps); + // char *cFPS = itoa(lastfps, numberBuf, 10); + printext(xdim - 20, nViewTop, numberBuf, kTile159, -1); + } + + // draw ammo count + seq_DrawStatusSequence(nStatusSeqOffset + 44, nDigit[2], 0); + seq_DrawStatusSequence(nStatusSeqOffset + 45, nDigit[1], 0); + seq_DrawStatusSequence(nStatusSeqOffset + 46, nDigit[0], 0); + + // bjd - commenting out this check seems to fix the black status bar at 320x200 resolution +// if (bHiRes) { + Clip(); +// } + } + + if (nNetPlayerCount) + { + NoClip(); + + int shade; + + if ((int)totalclock / kTimerTicks & 1) { + shade = -100; + } + else { + shade = 127; + } + + int nTile = kTile3593; + + int x = 320 / (nTotalPlayers + 1); + + for (int i = 0; i < nTotalPlayers; i++) + { + int nScore = nPlayerScore[i]; + if (word_9AD54[i] == nScore) + { + int v9 = dword_9AD64[i]; + if (v9 && v9 <= (int)totalclock) { + dword_9AD64[i] = 0; + } + } + else + { + word_9AD54[i] = nScore; + dword_9AD64[i] = (int)totalclock + 30; + } + + overwritesprite(x, 7, nTile, 0, 3, kPalNormal); + + if (i != nLocalPlayer) { + shade = -100; + } + + sprintf(stringBuf, "%d", nPlayerScore[i]); + int nStringLen = MyGetStringWidth(stringBuf); + + myprintext(x - (nStringLen / 2), 4, stringBuf, shade); + + x *= 2; + nTile++; + } + + if (nNetTime >= 0) + { + int y = nViewTop; + + if (nNetTime) + { + int v12 = (nNetTime + 29) / 30 % 60; + int v13 = (nNetTime + 29) / 1800; + nNetTime += 29; + + sprintf(stringBuf, "%d.%02d", v13, v12); + + if (bHiRes) { + y = nViewTop / 2; + } + + if (nViewTop <= 0) { + y += 20; + } + else { + y += 15; + } + + nNetTime -= 29; + } + else + { + y = 100; + strcpy(stringBuf, "GAME OVER"); + } + + int nLenString = MyGetStringWidth(stringBuf); + myprintext((320 - nLenString) / 2, y, stringBuf, 0); + } + + Clip(); + } + + if (bCoordinates) + { + int nSprite = PlayerList[nLocalPlayer].nSprite; + + int x = (nViewLeft + nViewRight) / 2; + + snprintf(coordBuf, 50, "X %d", (int)sprite[nSprite].x.cast()); + printext(x, nViewTop + 1, coordBuf, kTile159, 255); + + snprintf(coordBuf, 50, "Y %d", sprite[nSprite].y.cast()); + printext(x, nViewTop + 10, coordBuf, kTile159, 255); + } + + if (bHolly) + { + snprintf(message_text, 80, "HOLLY: %s", sHollyStr); + printext(0, 0, message_text, kTile159, 255); + } + else if (nSnakeCam < 0) + { + if (message_timer) { + printext(0, 0, message_text, kTile159, 255); + } + } + else + { + printext(0, 0, "S E R P E N T C A M", kTile159, 255); + } +} + +// I'm not sure this really needs to be saved. +static SavegameHelper sgh("status", + SV(nMaskY), + SV(nAnimsFree), + SV(message_timer), + SV(magicperline), + SV(airperline), + SV(healthperline), + SV(nAirFrames), + SV(nCounter), + SV(nCounterDest), + SV(nStatusSeqOffset), + SV(nItemFrames), + SV(laststatusx), + SV(laststatusy), + SV(nItemSeq), + SV(nMagicFrames), + SV(nHealthLevel), + SV(nItemFrame), + SV(nMeterRange), + SV(nMagicLevel), + SV(nHealthFrame), + SV(nMagicFrame), + SV(statusx), + SV(statusy), + SV(nHealthFrames), + SV(airframe), + SV(nFirstAnim), + SV(nLastAnim), + SV(nItemAltSeq), + SV(airpages), + SV(ammodelay), + SV(nCounterBullet), + SA(statusmask), + SA(message_text), + SA(nDigit), + SA(StatusAnim), + SA(StatusAnimsFree), + SA(StatusAnimFlags), + SA(nItemSeqOffset), + SA(word_9AD54), + SA(dword_9AD64), + nullptr); + + +END_PS_NS diff --git a/source/exhumed/src/status.h b/source/exhumed/src/status.h new file mode 100644 index 000000000..826c88a5e --- /dev/null +++ b/source/exhumed/src/status.h @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __status_h__ +#define __status_h__ + +BEGIN_PS_NS + + +extern short nMaskY; +extern short nCounterBullet; +extern short airpages; + +void RefreshStatus(); +void InitStatus(); +void UnMaskStatus(); +void MaskStatus(); +void LoadStatus(); +void SetPlayerItem(short nPlayer, short nItem); +void SetMagicFrame(); +void SetHealthFrame(short nVal); +void SetAirFrame(); + +void MoveStatus(); + +void DrawStatus(); + +int BuildStatusAnim(int val, int nFlags); + +void SetNextItem(int nPlayer); +void SetPrevItem(int nPlayer); + +void SetCounter(short nVal); +void SetCounterImmediate(short nVal); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/stream.cpp b/source/exhumed/src/stream.cpp new file mode 100644 index 000000000..3fe06d262 --- /dev/null +++ b/source/exhumed/src/stream.cpp @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------- +/* +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 "stream.h" diff --git a/source/exhumed/src/stream.h b/source/exhumed/src/stream.h new file mode 100644 index 000000000..3d73ba6ad --- /dev/null +++ b/source/exhumed/src/stream.h @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __stream_h__ +#define __stream_h__ + + + +#endif diff --git a/source/exhumed/src/switch.cpp b/source/exhumed/src/switch.cpp new file mode 100644 index 000000000..a720cab64 --- /dev/null +++ b/source/exhumed/src/switch.cpp @@ -0,0 +1,550 @@ +//------------------------------------------------------------------------- +/* +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 "switch.h" +#include "exhumed.h" +#include "runlist.h" +#include "engine.h" +#include "player.h" +#include "sound.h" +#include +#include + +BEGIN_PS_NS + +short LinkCount = -1; +short SwitchCount = -1; + +int8_t LinkMap[kMaxLinks][8]; + +struct Switch +{ + short field_0; + short field_2; + short nChannel; + short nLink; + short field_8; + short nSector; + short field_C; + short nWall; + short field_10; + uint16_t field_12; + short field_14; + char pad[10]; +}; + +Switch SwitchData[kMaxSwitches]; + +static SavegameHelper sgh("switch", + SV(LinkCount), + SV(SwitchCount), + SA(LinkMap), + SA(SwitchData), + nullptr); + +void InitLink() +{ + LinkCount = kMaxLinks; +} + +int BuildLink(int nCount, ...) +{ + if (LinkCount <= 0) { + return -1; + } + + va_list list; + va_start(list, nCount); + + LinkCount--; + + for (int i = 0; i < 8; i++) + { + int ebx; + + if (i >= nCount) + { + ebx = -1; + } + else + { + ebx = va_arg(list, int); + } + + LinkMap[LinkCount][i] = (int8_t)ebx; + } + va_end(list); + + return LinkCount; +} + +void InitSwitch() +{ + SwitchCount = kMaxSwitches; + memset(SwitchData, 0, sizeof(SwitchData)); +} + +int BuildSwReady(int nChannel, short nLink) +{ + if (SwitchCount <= 0 || nLink < 0) { + I_Error("Too many switch readys!\n"); + return -1; + } + + SwitchCount--; + SwitchData[SwitchCount].nChannel = nChannel; + SwitchData[SwitchCount].nLink = nLink; + + return SwitchCount | 0x10000; +} + +void FuncSwReady(int a, int, int nRun) +{ + short nSwitch = RunData[nRun].nVal; + assert(nSwitch >= 0 && nSwitch < kMaxSwitches); + + int nMessage = a & 0x7F0000; + + short nChannel = SwitchData[nSwitch].nChannel; + short nLink = SwitchData[nSwitch].nLink; + + switch (nMessage) + { + case 0x10000: + return; + + case 0x30000: + { + assert(sRunChannels[nChannel].c < 8); + int8_t nVal = LinkMap[nLink][sRunChannels[nChannel].c]; + if (nVal >= 0) { + runlist_ChangeChannel(nChannel, nVal); + } + + break; + } + + default: + return; + } +} + +int BuildSwPause(int nChannel, int nLink, int ebx) +{ + for (int i = kMaxSwitches - 1; i >= SwitchCount; i--) + { + if (SwitchData[i].nChannel == nChannel && SwitchData[i].field_2 != 0) { + return i | 0x20000; + } + } + + if (SwitchCount <= 0 || nLink < 0) { + I_Error("Too many switches!\n"); + return -1; + } + + SwitchCount--; + + SwitchData[SwitchCount].nChannel = nChannel; + SwitchData[SwitchCount].nLink = nLink; + SwitchData[SwitchCount].field_2 = ebx; + SwitchData[SwitchCount].field_8 = -1; + + return SwitchCount | 0x20000; +} + +void FuncSwPause(int a, int, int nRun) +{ + short nSwitch = RunData[nRun].nVal; + assert(nSwitch >= 0 && nSwitch < kMaxSwitches); + + int nMessage = a & 0x7F0000; + + short nChannel = SwitchData[nSwitch].nChannel; + short nLink = SwitchData[nSwitch].nLink; + + switch (nMessage) + { + default: + return; + + case 0x10000: + { + if (SwitchData[nSwitch].field_8 >= 0) + { + runlist_SubRunRec(SwitchData[nSwitch].field_8); + SwitchData[nSwitch].field_8 = -1; + } + + return; + } + + case 0x20000: + { + SwitchData[nSwitch].field_0--; + if (SwitchData[nSwitch].field_0 <= 0) + { + runlist_SubRunRec(SwitchData[nSwitch].field_8); + SwitchData[nSwitch].field_8 = -1; + + assert(sRunChannels[nChannel].c < 8); + assert(nLink < 1024); + + runlist_ChangeChannel(nChannel, LinkMap[nLink][sRunChannels[nChannel].c]); + } + + return; + } + + case 0x30000: + { + assert(sRunChannels[nChannel].c < 8); + + if (LinkMap[nLink][sRunChannels[nChannel].c] < 0) { + return; + } + + if (SwitchData[nSwitch].field_8 >= 0) { + return; + } + + SwitchData[nSwitch].field_8 = runlist_AddRunRec(NewRun, RunData[nRun].nMoves); + + int eax; + + if (SwitchData[nSwitch].field_2 <= 0) + { + eax = 100; + } + else + { + eax = SwitchData[nSwitch].field_2; + } + + SwitchData[nSwitch].field_0 = eax; + return; + } + } +} + +int BuildSwStepOn(int nChannel, int nLink, int nSector) +{ + if (SwitchCount <= 0 || nLink < 0 || nSector < 0) + I_Error("Too many switches!\n"); + + int nSwitch = --SwitchCount; + + SwitchData[nSwitch].nChannel = nChannel; + SwitchData[nSwitch].nLink = nLink; + SwitchData[nSwitch].nSector = nSector; + SwitchData[nSwitch].field_C = -1; + + return nSwitch | 0x30000; +} + +void FuncSwStepOn(int a, int, int nRun) +{ + short nSwitch = RunData[nRun].nVal; + assert(nSwitch >= 0 && nSwitch < kMaxSwitches); + + short nLink = SwitchData[nSwitch].nLink; + short nChannel = SwitchData[nSwitch].nChannel; + short nSector = SwitchData[nSwitch].nSector; + + assert(sRunChannels[nChannel].c < 8); + + int8_t var_14 = LinkMap[nLink][sRunChannels[nChannel].c]; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + default: + return; + + case 0x10000: + { + if (SwitchData[nSwitch].field_C >= 0) + { + runlist_SubRunRec(SwitchData[nSwitch].field_C); + SwitchData[nSwitch].field_C = -1; + } + + if (var_14 >= 0) + { + SwitchData[nSwitch].field_C = runlist_AddRunRec(sector[nSector].lotag - 1, RunData[nRun].nMoves); + } + + return; + } + + case 0x50000: + { + if (var_14 != sRunChannels[nChannel].c) + { + short nWall = sector[nSector].wallptr; + PlayFXAtXYZ(StaticSound[nSwitchSound], wall[nWall].x, wall[nWall].y, sector[nSector].floorz, nSector); + + assert(sRunChannels[nChannel].c < 8); + + runlist_ChangeChannel(nChannel, LinkMap[nLink][sRunChannels[nChannel].c]); + } + } + + return; + } + +} + +int BuildSwNotOnPause(int nChannel, int nLink, int nSector, int ecx) +{ + if (SwitchCount <= 0 || nLink < 0 || nSector < 0) + I_Error("Too many switches!\n"); + + int nSwitch = --SwitchCount; + + SwitchData[nSwitch].nChannel = nChannel; + SwitchData[nSwitch].nLink = nLink; + SwitchData[nSwitch].field_2 = ecx; + SwitchData[nSwitch].nSector = nSector; + SwitchData[nSwitch].field_8 = -1; + SwitchData[nSwitch].field_C = -1; + + return nSwitch | 0x40000; +} + +void FuncSwNotOnPause(int a, int, int nRun) +{ + short nSwitch = RunData[nRun].nVal; + assert(nSwitch >= 0 && nSwitch < kMaxSwitches); + + int nMessage = a & 0x7F0000; + + short nChannel = SwitchData[nSwitch].nChannel; + short nLink = SwitchData[nSwitch].nLink; + + switch (nMessage) + { + default: + return; + + case 0x10000: + { + if (SwitchData[nSwitch].field_C >= 0) + { + runlist_SubRunRec(SwitchData[nSwitch].field_C); + SwitchData[nSwitch].field_C = -1; + } + + if (SwitchData[nSwitch].field_8 >= 0) + { + runlist_SubRunRec(SwitchData[nSwitch].field_8); + SwitchData[nSwitch].field_8 = -1; + } + + return; + } + + case 0x20000: + { + SwitchData[nSwitch].field_0 -= 4; + if (SwitchData[nSwitch].field_0 <= 0) + { + assert(sRunChannels[nChannel].c < 8); + + runlist_ChangeChannel(nChannel, LinkMap[nLink][sRunChannels[nChannel].c]); + } + + return; + } + + case 0x30000: + { + assert(sRunChannels[nChannel].c < 8); + + if (LinkMap[nLink][sRunChannels[nChannel].c] >= 0) + { + if (SwitchData[nSwitch].field_8 < 0) + { + SwitchData[nSwitch].field_8 = runlist_AddRunRec(NewRun, RunData[nRun].nMoves); + + short nSector = SwitchData[nSwitch].nSector; + + SwitchData[nSwitch].field_0 = SwitchData[nSwitch].field_2; + SwitchData[nSwitch].field_C = runlist_AddRunRec(sector[nSector].lotag - 1, RunData[nRun].nMoves); + } + } + + return; + } + + case 0x50000: + { + SwitchData[nSwitch].field_0 = SwitchData[nSwitch].field_2; + return; + } + } +} + +int BuildSwPressSector(int nChannel, int nLink, int nSector, int ecx) +{ + if (SwitchCount <= 0 || nLink < 0 || nSector < 0) + I_Error("Too many switches!\n"); + + int nSwitch = --SwitchCount; + + SwitchData[nSwitch].nChannel = nChannel; + SwitchData[nSwitch].nLink = nLink; + SwitchData[nSwitch].nSector = nSector; + SwitchData[nSwitch].field_12 = ecx; + SwitchData[nSwitch].field_C = -1; + + return nSwitch | 0x50000; +} + +void FuncSwPressSector(int a, int, int nRun) +{ + short nSwitch = RunData[nRun].nVal; + assert(nSwitch >= 0 && nSwitch < kMaxSwitches); + + int nMessage = a & 0x7F0000; + + short nChannel = SwitchData[nSwitch].nChannel; + short nLink = SwitchData[nSwitch].nLink; + short nPlayer = a & 0xFFFF; + + switch (nMessage) + { + default: + return; + + case 0x10000: + { + if (SwitchData[nSwitch].field_C >= 0) + { + runlist_SubRunRec(SwitchData[nSwitch].field_C); + SwitchData[nSwitch].field_C = -1; + } + + assert(sRunChannels[nChannel].c < 8); + + if (LinkMap[nLink][sRunChannels[nChannel].c] < 0) { + return; + } + + short nSector = SwitchData[nSwitch].nSector; + + SwitchData[nSwitch].field_C = runlist_AddRunRec(sector[nSector].lotag - 1, RunData[nRun].nMoves); + return; + } + + case 0x40000: + { + if ((PlayerList[nPlayer].keys & SwitchData[nSwitch].field_12) == SwitchData[nSwitch].field_12) + { + runlist_ChangeChannel(nChannel, LinkMap[nLink][sRunChannels[nChannel].c]); + } + else + { + if (SwitchData[nSwitch].field_12) + { + short nSprite = PlayerList[nPlayer].nSprite; + PlayFXAtXYZ(StaticSound[nSwitchSound], sprite[nSprite].x, sprite[nSprite].y, 0, sprite[nSprite].sectnum); + + StatusMessage(300, "YOU NEED THE KEY FOR THIS DOOR"); + } + } + } + } +} + +int BuildSwPressWall(short nChannel, short nLink, short nWall) +{ + if (SwitchCount <= 0 || nLink < 0 || nWall < 0) { + I_Error("Too many switches!\n"); + } + + SwitchCount--; + + SwitchData[SwitchCount].nChannel = nChannel; + SwitchData[SwitchCount].nLink = nLink; + SwitchData[SwitchCount].nWall = nWall; + SwitchData[SwitchCount].field_10 = -1; + SwitchData[SwitchCount].field_14 = 0; + + return SwitchCount | 0x60000; +} + +void FuncSwPressWall(int a, int, int nRun) +{ + short nSwitch = RunData[nRun].nVal; + assert(nSwitch >= 0 && nSwitch < kMaxSwitches); + + short nChannel = SwitchData[nSwitch].nChannel; + short nLink = SwitchData[nSwitch].nLink; + + // TEMP +// assert(nLink < 1024); +// assert(nChannel < 8); + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + default: + return; + + case 0x30000: + { + if (SwitchData[nSwitch].field_10 >= 0) + { + runlist_SubRunRec(SwitchData[nSwitch].field_10); + SwitchData[nSwitch].field_10 = -1; + } + + if (LinkMap[nLink][sRunChannels[nChannel].c] >= 0) + { + short nWall = SwitchData[nSwitch].nWall; + SwitchData[nSwitch].field_10 = runlist_AddRunRec(wall[nWall].lotag - 1, RunData[nRun].nMoves); + } + + return; + } + + case 0x40000: + { + int8_t cx = LinkMap[nLink][sRunChannels[nChannel].c]; + + runlist_ChangeChannel(nChannel, LinkMap[nLink][sRunChannels[nChannel].c]); + + if (cx < 0 || LinkMap[nLink][cx] < 0) + { + runlist_SubRunRec(SwitchData[nSwitch].field_10); + SwitchData[nSwitch].field_10 = -1; + } + + short nWall = SwitchData[nSwitch].nWall; + short nSector = SwitchData[nSwitch].nSector; // CHECKME - where is this set?? + + PlayFXAtXYZ(StaticSound[nSwitchSound], wall[nWall].x, wall[nWall].y, 0, nSector); + + return; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/switch.h b/source/exhumed/src/switch.h new file mode 100644 index 000000000..7acb3cdfa --- /dev/null +++ b/source/exhumed/src/switch.h @@ -0,0 +1,48 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __switch_h__ +#define __switch_h__ + +BEGIN_PS_NS + +#define kMaxLinks 1024 +#define kMaxSwitches 1024 + +void InitLink(); +void InitSwitch(); + +void FuncSwReady(int, int, int); +void FuncSwPause(int, int, int); +void FuncSwStepOn(int, int, int); +void FuncSwNotOnPause(int, int, int); +void FuncSwPressSector(int, int, int); +void FuncSwPressWall(int, int, int); + +int BuildSwPause(int nChannel, int nLink, int ebx); +int BuildSwNotOnPause(int nChannel, int nLink, int nSector, int ecx); +int BuildLink(int nCount, ...); +int BuildSwPressSector(int nChannel, int nLink, int nSector, int ecx); +int BuildSwStepOn(int nChannel, int nLink, int nSector); +int BuildSwReady(int nChannel, short nLink); + +int BuildSwPressWall(short nChannel, short nLink, short nWall); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/text2.cpp b/source/exhumed/src/text2.cpp new file mode 100644 index 000000000..1d446a433 --- /dev/null +++ b/source/exhumed/src/text2.cpp @@ -0,0 +1,19 @@ +//------------------------------------------------------------------------- +/* +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 "text2.h" diff --git a/source/exhumed/src/text2.h b/source/exhumed/src/text2.h new file mode 100644 index 000000000..a655d66e9 --- /dev/null +++ b/source/exhumed/src/text2.h @@ -0,0 +1,24 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __text2_h__ +#define __text2_h__ + + + +#endif diff --git a/source/exhumed/src/timer.cpp b/source/exhumed/src/timer.cpp new file mode 100644 index 000000000..33a70e7ed --- /dev/null +++ b/source/exhumed/src/timer.cpp @@ -0,0 +1,36 @@ +//------------------------------------------------------------------------- +/* +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 "baselayer.h" +#include "timer.h" +#include "exhumed.h" + +BEGIN_PS_NS + +void timerhandler(); + +void InitTimer() +{ + htimer = 1; + + timerInit(kTimerTicks); + timerSetCallback(timerhandler); +} + +END_PS_NS diff --git a/source/exhumed/src/timer.h b/source/exhumed/src/timer.h new file mode 100644 index 000000000..11b838180 --- /dev/null +++ b/source/exhumed/src/timer.h @@ -0,0 +1,34 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __timer_h__ +#define __timer_h__ + +BEGIN_PS_NS + +#define kTimerTicks 120 + +void InitTimer(); + +#ifdef __WATCOMC__ +void uninittimer(); +#endif + +END_PS_NS + +#endif \ No newline at end of file diff --git a/source/exhumed/src/trigdat.cpp b/source/exhumed/src/trigdat.cpp new file mode 100644 index 000000000..c0cf59531 --- /dev/null +++ b/source/exhumed/src/trigdat.cpp @@ -0,0 +1,171 @@ +//------------------------------------------------------------------------- +/* +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 "trigdat.h" +#ifndef __WATCOMC__ +#include +#else +#include +#endif + +BEGIN_PS_NS + +short AngTable[] = {0,0,0,0,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,4,4,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7,7,7,7,7,8,8,8,8,8,8,9,9,9,9,9,9,10,10,10,10,10,10,10,11,11,11,11,11,11,12,12,12,12,12,12,13,13,13,13,13,13,14,14,14,14,14,14,14,15,15,15,15,15,15,16,16,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,19,19,19,19,19,19,20,20,20,20,20,20,20,21,21,21,21,21,21,22,22,22,22,22,22,23,23,23,23,23,23,24,24,24,24,24,24,24,25,25,25,25,25,25,26,26,26,26,26,26,27,27,27,27,27,27,27,28,28,28,28,28,28,29,29,29,29,29,29,30,30,30,30,30,30,30,31,31,31,31,31,31,32,32,32,32,32,32,33,33,33,33,33,33,33,34,34,34,34,34,34,35,35,35,35,35,35,35,36,36,36,36,36,36,37,37,37,37,37,37,38,38,38,38,38,38,38,39,39,39,39,39,39,40,40,40,40,40,40,41,41,41,41,41,41,41,42,42,42,42,42,42,43,43,43,43,43,43,43,44,44,44,44,44,44,45,45,45,45,45,45,46,46,46,46,46,46,46,47,47,47,47,47,47,48,48,48,48,48,48,48,49,49,49,49,49,49,50,50,50,50,50,50,51,51,51,51,51,51,51,52,52,52,52,52,52,53,53,53,53,53,53,53,54,54,54,54,54,54,55,55,55,55,55,55,55,56,56,56,56,56,56,57,57,57,57,57,57,57,58,58,58,58,58,58,59,59,59,59,59,59,59,60,60,60,60,60,60,61,61,61,61,61,61,61,62,62,62,62,62,62,63,63,63,63,63,63,63,64,64,64,64,64,64,65,65,65,65,65,65,65,66,66,66,66,66,66,67,67,67,67,67,67,67,68,68,68,68,68,68,69,69,69,69,69,69,69,70,70,70,70,70,70,70,71,71,71,71,71,71,72,72,72,72,72,72,72,73,73,73,73,73,73,74,74,74,74,74,74,74,75,75,75,75,75,75,75,76,76,76,76,76,76,77,77,77,77,77,77,77,78,78,78,78,78,78,78,79,79,79,79,79,79,80,80,80,80,80,80,80,81,81,81,81,81,81,81,82,82,82,82,82,82,83,83,83,83,83,83,83,84,84,84,84,84,84,84,85,85,85,85,85,85,86,86,86,86,86,86,86,87,87,87,87,87,87,87,88,88,88,88,88,88,88,89,89,89,89,89,89,90,90,90,90,90,90,90,91,91,91,91,91,91,91,92,92,92,92,92,92,92,93,93,93,93,93,93,93,94,94,94,94,94,94,94,95,95,95,95,95,95,96,96,96,96,96,96,96,97,97,97,97,97,97,97,98,98,98,98,98,98,98,99,99,99,99,99,99,99,100,100,100,100,100,100,100,101,101,101,101,101,101,101,102,102,102,102,102,102,102,103,103,103,103,103,103,103,104,104,104,104,104,104,104,105,105,105,105,105,105,105,106,106,106,106,106,106,106,107,107,107,107,107,107,107,108,108,108,108,108,108,108,109,109,109,109,109,109,109,110,110,110,110,110,110,110,111,111,111,111,111,111,111,112,112,112,112,112,112,112,113,113,113,113,113,113,113,114,114,114,114,114,114,114,115,115,115,115,115,115,115,116,116,116,116,116,116,116,117,117,117,117,117,117,117,117,118,118,118,118,118,118,118,119,119,119,119,119,119,119,120,120,120,120,120,120,120,121,121,121,121,121,121,121,122,122,122,122,122,122,122,122,123,123,123,123,123,123,123,124,124,124,124,124,124,124,125,125,125,125,125,125,125,125,126,126,126,126,126,126,126,127,127,127,127,127,127,127,128,128,128,128,128,128,128,128,129,129,129,129,129,129,129,130,130,130,130,130,130,130,131,131,131,131,131,131,131,131,132,132,132,132,132,132,132,133,133,133,133,133,133,133,133,134,134,134,134,134,134,134,135,135,135,135,135,135,135,135,136,136,136,136,136,136,136,137,137,137,137,137,137,137,137,138,138,138,138,138,138,138,139,139,139,139,139,139,139,139,140,140,140,140,140,140,140,141,141,141,141,141,141,141,141,142,142,142,142,142,142,142,142,143,143,143,143,143,143,143,144,144,144,144,144,144,144,144,145,145,145,145,145,145,145,145,146,146,146,146,146,146,146,146,147,147,147,147,147,147,147,148,148,148,148,148,148,148,148,149,149,149,149,149,149,149,149,150,150,150,150,150,150,150,150,151,151,151,151,151,151,151,151,152,152,152,152,152,152,152,153,153,153,153,153,153,153,153,154,154,154,154,154,154,154,154,155,155,155,155,155,155,155,155,156,156,156,156,156,156,156,156,157,157,157,157,157,157,157,157,158,158,158,158,158,158,158,158,159,159,159,159,159,159,159,159,160,160,160,160,160,160,160,160,161,161,161,161,161,161,161,161,162,162,162,162,162,162,162,162,162,163,163,163,163,163,163,163,163,164,164,164,164,164,164,164,164,165,165,165,165,165,165,165,165,166,166,166,166,166,166,166,166,167,167,167,167,167,167,167,167,167,168,168,168,168,168,168,168,168,169,169,169,169,169,169,169,169,170,170,170,170,170,170,170,170,170,171,171,171,171,171,171,171,171,172,172,172,172,172,172,172,172,173,173,173,173,173,173,173,173,173,174,174,174,174,174,174,174,174,175,175,175,175,175,175,175,175,175,176,176,176,176,176,176,176,176,177,177,177,177,177,177,177,177,177,178,178,178,178,178,178,178,178,178,179,179,179,179,179,179,179,179,180,180,180,180,180,180,180,180,180,181,181,181,181,181,181,181,181,181,182,182,182,182,182,182,182,182,183,183,183,183,183,183,183,183,183,184,184,184,184,184,184,184,184,184,185,185,185,185,185,185,185,185,185,186,186,186,186,186,186,186,186,186,187,187,187,187,187,187,187,187,188,188,188,188,188,188,188,188,188,189,189,189,189,189,189,189,189,189,190,190,190,190,190,190,190,190,190,191,191,191,191,191,191,191,191,191,192,192,192,192,192,192,192,192,192,192,193,193,193,193,193,193,193,193,193,194,194,194,194,194,194,194,194,194,195,195,195,195,195,195,195,195,195,196,196,196,196,196,196,196,196,196,197,197,197,197,197,197,197,197,197,197,198,198,198,198,198,198,198,198,198,199,199,199,199,199,199,199,199,199,200,200,200,200,200,200,200,200,200,200,201,201,201,201,201,201,201,201,201,202,202,202,202,202,202,202,202,202,202,203,203,203,203,203,203,203,203,203,204,204,204,204,204,204,204,204,204,204,205,205,205,205,205,205,205,205,205,206,206,206,206,206,206,206,206,206,206,207,207,207,207,207,207,207,207,207,207,208,208,208,208,208,208,208,208,208,209,209,209,209,209,209,209,209,209,209,210,210,210,210,210,210,210,210,210,210,211,211,211,211,211,211,211,211,211,211,212,212,212,212,212,212,212,212,212,212,213,213,213,213,213,213,213,213,213,213,214,214,214,214,214,214,214,214,214,214,215,215,215,215,215,215,215,215,215,215,216,216,216,216,216,216,216,216,216,216,217,217,217,217,217,217,217,217,217,217,218,218,218,218,218,218,218,218,218,218,219,219,219,219,219,219,219,219,219,219,219,220,220,220,220,220,220,220,220,220,220,221,221,221,221,221,221,221,221,221,221,222,222,222,222,222,222,222,222,222,222,222,223,223,223,223,223,223,223,223,223,223,224,224,224,224,224,224,224,224,224,224,224,225,225,225,225,225,225,225,225,225,225,226,226,226,226,226,226,226,226,226,226,226,227,227,227,227,227,227,227,227,227,227,228,228,228,228,228,228,228,228,228,228,228,229,229,229,229,229,229,229,229,229,229,229,230,230,230,230,230,230,230,230,230,230,230,231,231,231,231,231,231,231,231,231,231,231,232,232,232,232,232,232,232,232,232,232,232,233,233,233,233,233,233,233,233,233,233,233,234,234,234,234,234,234,234,234,234,234,234,235,235,235,235,235,235,235,235,235,235,235,236,236,236,236,236,236,236,236,236,236,236,237,237,237,237,237,237,237,237,237,237,237,238,238,238,238,238,238,238,238,238,238,238,238,239,239,239,239,239,239,239,239,239,239,239,240,240,240,240,240,240,240,240,240,240,240,240,241,241,241,241,241,241,241,241,241,241,241,242,242,242,242,242,242,242,242,242,242,242,242,243,243,243,243,243,243,243,243,243,243,243,244,244,244,244,244,244,244,244,244,244,244,244,245,245,245,245,245,245,245,245,245,245,245,245,246,246,246,246,246,246,246,246,246,246,246,246,247,247,247,247,247,247,247,247,247,247,247,248,248,248,248,248,248,248,248,248,248,248,248,249,249,249,249,249,249,249,249,249,249,249,249,249,250,250,250,250,250,250,250,250,250,250,250,250,251,251,251,251,251,251,251,251,251,251,251,251,252,252,252,252,252,252,252,252,252,252,252,252,253,253,253,253,253,253,253,253,253,253,253,253,254,254,254,254,254,254,254,254,254,254,254,254,254,255,255,255,255,255,255,255,255,255,255,255,255,256,256,256,256,256,256}; + + +int GetMyAngle(int x, int y) +{ + int ebx = -x; + int esi = y << 11; + int ecx = y; + int edx = y; + + if (ebx >= 0) + { + // left path + + edx = ebx << 11; + + if (y >= 0) + { + if (ebx == y) { + return 768; + } + else // loc_2F318: + { + if (y > ebx) + { + return (AngTable[(edx / y) & kAngleMask] + 512) & kAngleMask; + } + else + { + // loc_2F33C: + return ((512 - AngTable[(esi / ebx) & kAngleMask]) + 512) & kAngleMask; + } + } + } + else + { + // loc_2F35D: + ecx = -y; + + if (ebx == ecx) { + return 1280; + } + else if (ebx <= ecx) + { + return ((1024 - AngTable[(edx / ecx) & kAngleMask]) + 512) & kAngleMask; + } + else + { + edx = ecx << 11; + return (AngTable[(edx / ebx) & kAngleMask] + 1024) & kAngleMask; + } + } + } + else + { + if (edx >= 0) + { + ebx = -ebx; + + if (ebx == edx) { + return 256; + } + else if (ebx > edx) + { + return (AngTable[(esi / ebx) & kAngleMask] + 2048) & kAngleMask; + } + else + { + edx = ebx << 11; + return ((2048 - AngTable[(edx / ecx) & kAngleMask]) + 512) & kAngleMask; + } + } + else + { + ebx = -ebx; + ecx = -ecx; + + if (ebx == ecx) { + return 1792; + } + else if (ebx >= ecx) + { + edx = ecx << 11; + return ((1536 - AngTable[(edx / ebx) & kAngleMask]) + 512) & kAngleMask; + } + else + { + edx = ebx << 11; + return (AngTable[(edx / ecx) & kAngleMask] + 1536) & kAngleMask; + } + } + } +} + +// 100% done +int AngleDiff(short a, short b) +{ + int diff = (b - a) & kAngleMask; + + if (diff > 1024) { + diff = 2048 - diff; + } + return diff; +} + +// unused +int AnglePick(short a, short b) +{ + int nRet = b; + + if (AngleDiff(a, b) > 512) + { + nRet ^= 0x400; + } + + return nRet; +} + +int AngleDelta(int a, int b, int c) +{ + int diff = b - a; + + if (diff >= 0) + { + if (diff > 1024) { + diff = -(2048 - diff); + } + } + else if (diff < -1024) + { + diff += 2048; + } + + if (abs(diff) > c) + { + if (diff < 0) { + return -diff; + } + + diff = c; + } + return diff; +} +END_PS_NS diff --git a/source/exhumed/src/trigdat.h b/source/exhumed/src/trigdat.h new file mode 100644 index 000000000..72b09a31d --- /dev/null +++ b/source/exhumed/src/trigdat.h @@ -0,0 +1,32 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __trigdat_h__ +#define __trigdat_h__ + +BEGIN_PS_NS + +#define kAngleMask 0x7FF + +int GetMyAngle(int x, int y); +int AngleDiff(short a, short b); +int AngleDelta(int a, int b, int c); + +END_PS_NS + +#endif diff --git a/source/exhumed/src/typedefs.h b/source/exhumed/src/typedefs.h new file mode 100644 index 000000000..0c54c9c4d --- /dev/null +++ b/source/exhumed/src/typedefs.h @@ -0,0 +1,25 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __typedefs_h__ +#define __typedefs_h__ + +#define kTrue (0 == 0) +#define kFalse (0 != 0) + +#endif diff --git a/source/exhumed/src/util.h b/source/exhumed/src/util.h new file mode 100644 index 000000000..efafe308e --- /dev/null +++ b/source/exhumed/src/util.h @@ -0,0 +1,43 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __util_h__ +#define __util_h__ + +BEGIN_PS_NS + + +inline int Min(int a, int b) +{ + if (a < b) + return a; + else + return b; +} + +inline int Max(int a, int b) +{ + if (a < b) + return b; + else + return a; +} + +END_PS_NS + +#endif diff --git a/source/exhumed/src/view.cpp b/source/exhumed/src/view.cpp new file mode 100644 index 000000000..cc064af5c --- /dev/null +++ b/source/exhumed/src/view.cpp @@ -0,0 +1,684 @@ +//------------------------------------------------------------------------- +/* +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 "engine.h" +#include "names.h" +#include "view.h" +#include "status.h" +#include "exhumed.h" +#include "player.h" +#include "snake.h" +#include "gun.h" +#include "light.h" +#include "init.h" +#include "menu.h" +#include "cd.h" +#include "typedefs.h" +#include "map.h" +#include "move.h" +#include "sound.h" +#include "engine.h" +#include "trigdat.h" +#include "runlist.h" +#include + +BEGIN_PS_NS + +short bSubTitles = kTrue; + +int zbob; + +fix16_t nDestVertPan[kMaxPlayers] = { 0 }; +short dVertPan[kMaxPlayers]; +fix16_t nVertPan[kMaxPlayers]; +int nCamerax; +int nCameray; +int nCameraz; + +short bTouchFloor; + +short nQuake[kMaxPlayers] = { 0 }; + +short nChunkTotal = 0; + +fix16_t nCameraa; +fix16_t nCamerapan; +short nViewTop; +short bClip = kFalse; +short nViewBottom; +short nViewRight; +short besttarget; +short nViewLeft; +short bCamera = kFalse; + +short nViewy; + +int viewz; + +short enemy; + +short nEnemyPal = 0; + +#define MAXINTERPOLATIONS MAXSPRITES +int32_t g_interpolationCnt; +int32_t oldipos[MAXINTERPOLATIONS]; +int32_t* curipos[MAXINTERPOLATIONS]; +int32_t bakipos[MAXINTERPOLATIONS]; + +int viewSetInterpolation(int32_t *const posptr) +{ + if (g_interpolationCnt >= MAXINTERPOLATIONS) + return 1; + + for (bssize_t i = 0; i < g_interpolationCnt; ++i) + if (curipos[i] == posptr) + return 0; + + curipos[g_interpolationCnt] = posptr; + oldipos[g_interpolationCnt] = *posptr; + g_interpolationCnt++; + return 0; +} + +void viewStopInterpolation(const int32_t * const posptr) +{ + for (bssize_t i = 0; i < g_interpolationCnt; ++i) + if (curipos[i] == posptr) + { + g_interpolationCnt--; + oldipos[i] = oldipos[g_interpolationCnt]; + bakipos[i] = bakipos[g_interpolationCnt]; + curipos[i] = curipos[g_interpolationCnt]; + } +} + +void viewDoInterpolations(int smoothRatio) +{ + int32_t ndelta = 0; + + for (bssize_t i = 0, j = 0; i < g_interpolationCnt; ++i) + { + int32_t const odelta = ndelta; + bakipos[i] = *curipos[i]; + ndelta = (*curipos[i]) - oldipos[i]; + if (odelta != ndelta) + j = mulscale16(ndelta, smoothRatio); + *curipos[i] = oldipos[i] + j; + } +} + +void viewUpdateInterpolations(void) //Stick at beginning of G_DoMoveThings +{ + for (bssize_t i=g_interpolationCnt-1; i>=0; i--) oldipos[i] = *curipos[i]; +} + +void viewRestoreInterpolations(void) //Stick at end of drawscreen +{ + int32_t i=g_interpolationCnt-1; + + for (; i>=0; i--) *curipos[i] = bakipos[i]; +} + +void InitView() +{ + screensize = 0; +#ifdef USE_OPENGL + polymostcenterhoriz = 92; +#endif +} + +// NOTE - not to be confused with Ken's analyzesprites() +static void analyzesprites() +{ + short nPlayerSprite = PlayerList[nLocalPlayer].nSprite; + + int var_38 = 20; + int var_2C = 30000; + + spritetype *pPlayerSprite = &sprite[nPlayerSprite]; + + besttarget = -1; + + int x = pPlayerSprite->x; + int y = pPlayerSprite->y; + + int z = pPlayerSprite->z - (GetSpriteHeight(nPlayerSprite) / 2); + + short nSector = pPlayerSprite->sectnum; + + int nAngle = (2048 - pPlayerSprite->ang) & kAngleMask; + + int nTSprite; + tspritetype *pTSprite; + +// int var_20 = var_24; + + for (nTSprite = spritesortcnt-1, pTSprite = &tsprite[nTSprite]; nTSprite >= 0; nTSprite--, pTSprite--) + { + int nSprite = pTSprite->owner; + spritetype *pSprite = &sprite[nSprite]; + + if (pTSprite->sectnum >= 0) + { + sectortype *pSector = §or[pTSprite->sectnum]; + int nSectShade = (pSector->ceilingstat & 1) ? pSector->ceilingshade : pSector->floorshade; + int nShade = pTSprite->shade + nSectShade + 6; + pTSprite->shade = clamp(nShade, -128, 127); + } + + pTSprite->pal = RemapPLU(pTSprite->pal); + + if (pSprite->statnum > 0) + { + runlist_SignalRun(pSprite->lotag - 1, nTSprite | 0x90000); + + if ((pSprite->statnum < 150) && (pSprite->cstat & 0x101) && (nSprite != nPlayerSprite)) + { + int xval = pSprite->x - x; + int yval = pSprite->y - y; + + int vcos = Cos(nAngle); + int vsin = Sin(nAngle); + + + int edx = ((vcos * yval) + (xval * vsin)) >> 14; + + + int ebx = klabs(((vcos * xval) - (yval * vsin)) >> 14); + + if (!ebx) + continue; + + edx = (klabs(edx) * 32) / ebx; + if (ebx < 1000 && ebx < var_2C && edx < 10) + { + besttarget = nSprite; + var_38 = edx; + var_2C = ebx; + } + else if (ebx < 30000) + { + int t = var_38 - edx; + if (t > 3 || (ebx < var_2C && klabs(t) < 5)) + { + var_38 = edx; + var_2C = ebx; + besttarget = nSprite; + } + } + } + } + } + if (besttarget != -1) + { + spritetype *pTarget = &sprite[besttarget]; + + nCreepyTimer = kCreepyCount; + + if (!cansee(x, y, z, nSector, pTarget->x, pTarget->y, pTarget->z - GetSpriteHeight(besttarget), pTarget->sectnum)) + { + besttarget = -1; + } + } +} + +void ResetView() +{ + //videoSetGameMode(gSetup.fullscreen, gSetup.xdim, gSetup.ydim, gSetup.bpp, 0); + DoOverscanSet(overscanindex); + EraseScreen(overscanindex); + memcpy(curpalettefaded, curpalette, sizeof(curpalette)); + //videoUpdatePalette(0, 256); +#ifdef USE_OPENGL + videoTintBlood(0, 0, 0); +#endif + + LoadStatus(); +} + +void SetView1() +{ +} + +void FlushMessageLine() +{ + int tileX = tilesiz[nBackgroundPic].x; + int nTileOffset = 0; + + int xPos = 0; + + while (xPos < xdim) + { + overwritesprite(xPos, 0, nBackgroundPic + nTileOffset, -32, 0, kPalNormal); + + nTileOffset = nTileOffset == 0; + + xPos += tileX; + } +} + +void RefreshBackground() +{ + if (screensize <= 0) + return; + int nTileOffset = 0; + int tileX = tilesiz[nBackgroundPic].x; + int tileY = tilesiz[nBackgroundPic].y; + + videoSetViewableArea(0, 0, xdim - 1, ydim - 1); + + MaskStatus(); + + for (int y = 0; y < nViewTop; y += tileY) + { + nTileOffset = (y/tileY)&1; + for (int x = 0; x < xdim; x += tileX) + { + rotatesprite(x<<16, y<<16, 65536L, 0, nBackgroundPic + nTileOffset, -32, kPalNormal, 8 + 16 + 64, 0, 0, xdim-1, nViewTop-1); + nTileOffset ^= 1; + } + } + for (int y = (nViewTop/tileY)*tileY; y <= nViewBottom; y += tileY) + { + nTileOffset = (y/tileY)&1; + for (int x = 0; x < nViewLeft; x += tileX) + { + rotatesprite(x<<16, y<<16, 65536L, 0, nBackgroundPic + nTileOffset, -32, kPalNormal, 8 + 16 + 64, 0, nViewTop, nViewLeft-1, nViewBottom); + nTileOffset ^= 1; + } + } + for (int y = (nViewTop/tileY)*tileY; y <= nViewBottom; y += tileY) + { + nTileOffset = ((y/tileY)^((nViewRight+1)/tileX))&1; + for (int x = ((nViewRight+1)/tileX)*tileX; x < xdim; x += tileX) + { + rotatesprite(x<<16, y<<16, 65536L, 0, nBackgroundPic + nTileOffset, -32, kPalNormal, 8 + 16 + 64, nViewRight+1, nViewTop, xdim-1, nViewBottom); + nTileOffset ^= 1; + } + } + for (int y = ((nViewBottom+1)/tileY)*tileY; y < ydim; y += tileY) + { + nTileOffset = (y/tileY)&1; + for (int x = 0; x < xdim; x += tileX) + { + rotatesprite(x<<16, y<<16, 65536L, 0, nBackgroundPic + nTileOffset, -32, kPalNormal, 8 + 16 + 64, 0, nViewBottom+1, xdim-1, ydim-1); + nTileOffset ^= 1; + } + } + + videoSetViewableArea(nViewLeft, nViewTop, nViewRight, nViewBottom); +} + +void MySetView(int x1, int y1, int x2, int y2) +{ + if (!bFullScreen) { + MaskStatus(); + } + + nViewLeft = x1; + nViewTop = y1; + nViewRight = x2; + nViewBottom = y2; + + videoSetViewableArea(x1, y1, x2, y2); + + nViewy = y1; +} + +// unused function +void TestLava() +{ +} + +static inline int interpolate16(int a, int b, int smooth) +{ + return a + mulscale16(b - a, smooth); +} + +static inline fix16_t q16angle_interpolate16(fix16_t a, fix16_t b, int smooth) +{ + return a + mulscale16(((b+F16(1024)-a)&0x7FFFFFF)-F16(1024), smooth); +} + +void DrawView(int smoothRatio) +{ + int playerX; + int playerY; + int playerZ; + short nSector; + fix16_t nAngle; + fix16_t pan; + +#if 0 + if (bgpages <= 0) + { + if (textpages > 0) + { + textpages--; + FlushMessageLine(); + } + } + else + { + RefreshBackground(); + bgpages--; + } +#else + FlushMessageLine(); + RefreshBackground(); +#endif + + if (!bFullScreen) { + MaskStatus(); + } + + zbob = Sin(2 * bobangle) >> 3; + + int nPlayerSprite = PlayerList[nLocalPlayer].nSprite; + int nPlayerOldCstat = sprite[nPlayerSprite].cstat; + int nDoppleOldCstat = sprite[nDoppleSprite[nLocalPlayer]].cstat; + + if (nSnakeCam >= 0) + { + int nSprite = SnakeList[nSnakeCam].nSprites[0]; + + playerX = sprite[nSprite].x; + playerY = sprite[nSprite].y; + playerZ = sprite[nSprite].z; + nSector = sprite[nSprite].sectnum; + nAngle = fix16_from_int(sprite[nSprite].ang); + + SetGreenPal(); + UnMaskStatus(); + + enemy = SnakeList[nSnakeCam].nEnemy; + + if (enemy <= -1 || totalmoves & 1) + { + nEnemyPal = -1; + } + else + { + nEnemyPal = sprite[enemy].pal; + sprite[enemy].pal = 5; + } + } + else + { + playerX = interpolate16(PlayerList[nLocalPlayer].opos.x, sprite[nPlayerSprite].x, smoothRatio); + playerY = interpolate16(PlayerList[nLocalPlayer].opos.y, sprite[nPlayerSprite].y, smoothRatio); + playerZ = interpolate16(PlayerList[nLocalPlayer].opos.z, sprite[nPlayerSprite].z, smoothRatio) + + interpolate16(oeyelevel[nLocalPlayer], eyelevel[nLocalPlayer], smoothRatio); + nSector = nPlayerViewSect[nLocalPlayer]; + nAngle = q16angle_interpolate16(PlayerList[nLocalPlayer].q16oangle, PlayerList[nLocalPlayer].q16angle, smoothRatio); + + if (!bCamera) + { + sprite[nPlayerSprite].cstat |= CSTAT_SPRITE_INVISIBLE; + sprite[nDoppleSprite[nLocalPlayer]].cstat |= CSTAT_SPRITE_INVISIBLE; + } + } + + nCameraa = nAngle; + + if (!bCamera || nFreeze) + { + if (nSnakeCam >= 0) + { + pan = F16(92); + viewz = playerZ; + } + else + { + viewz = playerZ + nQuake[nLocalPlayer]; + int floorZ = sector[sprite[nPlayerSprite].sectnum].floorz; + + // pan = nVertPan[nLocalPlayer]; + pan = interpolate16(PlayerList[nLocalPlayer].q16ohoriz, PlayerList[nLocalPlayer].q16horiz, smoothRatio); + + if (viewz > floorZ) + viewz = floorZ; + + nCameraa += fix16_from_int((nQuake[nLocalPlayer] >> 7) % 31); + nCameraa &= 0x7FFFFFF; + } + } + else + { + clipmove_old((int32_t*)&playerX, (int32_t*)&playerY, (int32_t*)&playerZ, &nSector, + -2000 * Sin(inita + 512), + -2000 * Sin(inita), + 4, 0, 0, CLIPMASK1); + + pan = F16(92); + viewz = playerZ; + } + + nCamerax = playerX; + nCameray = playerY; + nCameraz = playerZ; + + int Z = sector[nSector].ceilingz + 256; + if (Z <= viewz) + { + Z = sector[nSector].floorz - 256; + + if (Z < viewz) + viewz = Z; + } + else { + viewz = Z; + } + + nCamerapan = pan; + + if (nFreeze == 2 || nFreeze == 1) + { + nSnakeCam = -1; + videoSetViewableArea(0, 0, xdim - 1, ydim - 1); + UnMaskStatus(); + } + + UpdateMap(); + + if (nFreeze != 3) + { + static uint8_t sectorFloorPal[MAXSECTORS]; + static uint8_t sectorCeilingPal[MAXSECTORS]; + static uint8_t wallPal[MAXWALLS]; + int const viewingRange = viewingrange; + int const vr = Blrintf(65536.f * tanf(r_fov * (fPI / 360.f))); + + if (r_usenewaspect) + { + newaspect_enable = 1; + videoSetCorrectedAspect(); + renderSetAspect(mulscale16(vr, viewingrange), yxaspect); + } + else + renderSetAspect(vr, yxaspect); + + if (HavePLURemap()) + { + for (int i = 0; i < numsectors; i++) + { + sectorFloorPal[i] = sector[i].floorpal; + sectorCeilingPal[i] = sector[i].ceilingpal; + sector[i].floorpal = RemapPLU(sectorFloorPal[i]); + sector[i].ceilingpal = RemapPLU(sectorCeilingPal[i]); + } + for (int i = 0; i < numwalls; i++) + { + wallPal[i] = wall[i].pal; + wall[i].pal = RemapPLU(wallPal[i]); + } + } + + renderDrawRoomsQ16(nCamerax, nCameray, viewz, nCameraa, nCamerapan, nSector); + analyzesprites(); + renderDrawMasks(); + + if (HavePLURemap()) + { + for (int i = 0; i < numsectors; i++) + { + sector[i].floorpal = sectorFloorPal[i]; + sector[i].ceilingpal = sectorCeilingPal[i]; + } + for (int i = 0; i < numwalls; i++) + { + wall[i].pal = wallPal[i]; + } + } + + if (r_usenewaspect) + { + newaspect_enable = 0; + renderSetAspect(viewingRange, tabledivide32_noinline(65536 * ydim * 8, xdim * 5)); + } + + if (nFreeze) + { + nSnakeCam = -1; + + if (nFreeze == 2) + { + if (nHeadStage == 4) + { + nHeadStage = 5; + + sprite[nPlayerSprite].cstat |= 0x8000; + + int ang2 = fix16_to_int(nCameraa) - sprite[nPlayerSprite].ang; + if (ang2 < 0) + ang2 = -ang2; + + if (ang2 > 10) + { + inita -= (ang2 >> 3); + return; + } + + if (bSubTitles) + { + if (levelnum == 1) + ReadyCinemaText(1); + else + ReadyCinemaText(5); + } + } + else + { + if ((bSubTitles && !AdvanceCinemaText()) || inputState.CheckAllInput()) + { + inputState.ClearAllInput(); + levelnew = levelnum + 1; + + if (CDplaying()) { + fadecdaudio(); + } + } + + videoSetViewableArea(nViewLeft, nViewTop, nViewRight, nViewBottom); + } + } + } + else + { + if (nSnakeCam < 0) + { + DrawWeapons(smoothRatio); + DrawMap(); + DrawStatus(); + } + else + { + RestoreGreenPal(); + if (nEnemyPal > -1) { + sprite[enemy].pal = nEnemyPal; + } + + DrawMap(); + + if (!bFullScreen) { + MaskStatus(); + } + } + } + } + else + { + videoClearScreen(overscanindex); + DrawStatus(); + } + + sprite[nPlayerSprite].cstat = nPlayerOldCstat; + sprite[nDoppleSprite[nLocalPlayer]].cstat = nDoppleOldCstat; + + flash = 0; +} + +void NoClip() +{ + videoSetViewableArea(0, 0, xdim - 1, ydim - 1); + + bClip = kFalse; +} + +void Clip() +{ + videoSetViewableArea(nViewLeft, nViewTop, nViewRight, nViewBottom); + if (!bFullScreen) { + MaskStatus(); + } + + bClip = kTrue; +} + + +static SavegameHelper sgh("view", + SV(nCamerax), + SV(nCameray), + SV(nCameraz), + SV(bTouchFloor), + SV(nChunkTotal), + SV(nCameraa), + SV(nCamerapan), + SV(nViewTop), + SV(bClip), + SV(nViewBottom), + SV(nViewRight), + SV(besttarget), + SV(nViewLeft), + SV(bCamera), + SV(nViewy), + SV(viewz), + SV(enemy), + SV(nEnemyPal), + SA(nDestVertPan), + SA(dVertPan), + SA(nVertPan), + SA(nQuake), + SV(g_interpolationCnt), + SA(oldipos), + SA(curipos), + SA(bakipos), + nullptr); + +END_PS_NS diff --git a/source/exhumed/src/view.h b/source/exhumed/src/view.h new file mode 100644 index 000000000..b583e8031 --- /dev/null +++ b/source/exhumed/src/view.h @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __view_h__ +#define __view_h__ + +#include "build.h" + +BEGIN_PS_NS + +extern short bSubTitles; +extern short nViewTop; +extern short bClip; +extern short nViewBottom; +extern short nViewRight; +extern short nViewLeft; +extern short besttarget; +extern short bCamera; + +void InitView(); +void SetView1(); +void RefreshBackground(); +void DrawView(int smoothRatio); +void MySetView(int x1, int y1, int x2, int y2); +void ResetView(); +void NoClip(); +void Clip(); + +int viewSetInterpolation(int32_t *const posptr); +void viewStopInterpolation(const int32_t * const posptr); +void viewDoInterpolations(int smoothRatio); +void viewUpdateInterpolations(void); +void viewRestoreInterpolations(void); + +extern fix16_t nDestVertPan[]; +extern short dVertPan[]; +extern fix16_t nVertPan[]; +extern short nQuake[]; + +extern int nCamerax; +extern int nCameray; +extern int nCameraz; + +extern short bTouchFloor; + +extern short nChunkTotal; + +extern int gFov; + +static inline int angle_interpolate16(int a, int b, int smooth) +{ + return a + mulscale16(((b+1024-a)&2047)-1024, smooth); +} + +END_PS_NS + +#endif diff --git a/source/exhumed/src/wasp.cpp b/source/exhumed/src/wasp.cpp new file mode 100644 index 000000000..9fe6efca4 --- /dev/null +++ b/source/exhumed/src/wasp.cpp @@ -0,0 +1,424 @@ +//------------------------------------------------------------------------- +/* +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 "wasp.h" +#include "engine.h" +#include "runlist.h" +#include "random.h" +#include "exhumed.h" +#include "sequence.h" +#include "init.h" +#include "move.h" +#include "anims.h" +#include "trigdat.h" +#include + +BEGIN_PS_NS + +#define kMaxWasps 100 + +static short nVelShift = 0; +short nWaspCount; + +struct Wasp +{ + short nHealth; + short field_2; + short nAction; + short nSprite; + short field_8; + short nTarget; + short field_C; + short field_E; + short field_10; + short field_12; + short field_14; + short field_16; +}; + +Wasp WaspList[kMaxWasps]; + +static actionSeq ActionSeq[] = {{0,0}, {0,0}, {9,0}, {18,0}, {27,1}, {28,1}, {29,1}}; + +static SavegameHelper sgh("wasp", + SV(nVelShift), + SV(nWaspCount), + SA(WaspList), + nullptr); + + +void InitWasps() +{ + nWaspCount = 0; +} + +void SetWaspVel(short nSprite) +{ + if (nVelShift < 0) + { + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512) << -nVelShift; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) << -nVelShift; + } + else + { + sprite[nSprite].xvel = Sin(sprite[nSprite].ang + 512) >> nVelShift; + sprite[nSprite].yvel = Sin(sprite[nSprite].ang) >> nVelShift; + } + +} + +int BuildWasp(short nSprite, int x, int y, int z, short nSector, short nAngle) +{ + if (nWaspCount >= kMaxWasps) { + return -1; + } + + short nWasp = nWaspCount; + nWaspCount++; + + uint8_t bEggWasp = kFalse; + if (nSprite == -2) { + bEggWasp = kTrue; + } + + if (nSprite < 0) + { + nSprite = insertsprite(nSector, 107); + assert(nSprite >= 0 && nSprite < kMaxSprites); + + sprite[nSprite].x = x; + sprite[nSprite].y = y; + sprite[nSprite].z = z; + } + else + { + nAngle = sprite[nSprite].ang; + changespritestat(nSprite, 107); + } + + sprite[nSprite].shade = -12; + sprite[nSprite].cstat = 0x101; + sprite[nSprite].pal = sector[sprite[nSprite].sectnum].ceilingpal; + sprite[nSprite].clipdist = 70; + + if (bEggWasp) + { + sprite[nSprite].yrepeat = 20; + sprite[nSprite].xrepeat = 20; + } + else + { + sprite[nSprite].xrepeat = 50; + sprite[nSprite].yrepeat = 50; + } + + sprite[nSprite].xoffset = 0; + sprite[nSprite].yoffset = 0; + sprite[nSprite].picnum = 1; + sprite[nSprite].ang = nAngle; + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + sprite[nSprite].zvel = 0; + sprite[nSprite].hitag = 0; + sprite[nSprite].lotag = runlist_HeadRun() + 1; + sprite[nSprite].extra = -1; + +// GrabTimeSlot(3); + + WaspList[nWasp].nAction = 0; + WaspList[nWasp].field_2 = 0; + WaspList[nWasp].nSprite = nSprite; + WaspList[nWasp].nTarget = -1; + WaspList[nWasp].nHealth = 800; + WaspList[nWasp].field_16 = 10; + + if (bEggWasp) + { + WaspList[nWasp].field_C = 60; + WaspList[nWasp].field_16 = (WaspList[nWasp].field_16 - 1) >> 1; + } + else + { + WaspList[nWasp].field_C = RandomSize(5); + } + + WaspList[nWasp].field_E = 0; + WaspList[nWasp].field_14 = 0; + WaspList[nWasp].field_12 = 0; + WaspList[nWasp].field_10 = RandomSize(7) + 127; + + sprite[nSprite].owner = runlist_AddRunRec(sprite[nSprite].lotag - 1, nWasp | 0x1E0000); + + WaspList[nWasp].field_8 = runlist_AddRunRec(NewRun, nWasp | 0x1E0000); + + nCreaturesLeft++; + return nSprite; +} + +void FuncWasp(int a, int nDamage, int nRun) +{ + short nWasp = RunData[nRun].nVal; + + short nTarget = -1; + + short nSprite = WaspList[nWasp].nSprite; + short nAction = WaspList[nWasp].nAction; + + int someval = 0; + + int nMessage = a & 0x7F0000; + + switch (nMessage) + { + case 0x90000: + { + seq_PlotSequence(a & 0xFFFF, SeqOffsets[kSeqWasp] + ActionSeq[nAction].a, WaspList[nWasp].field_2, ActionSeq[nAction].b); + return; + } + + case 0xA0000: + { + if (!(sprite[nSprite].cstat & 0x101)) + return; + + nDamage = runlist_CheckRadialDamage(nSprite); + // fall through to case 0x80000 + fallthrough__; + } + + case 0x80000: + { + if (!nDamage) { + return; + } + + if (WaspList[nWasp].nHealth > 0) + { + WaspList[nWasp].nHealth -= nDamage; + + if (WaspList[nWasp].nHealth > 0) + { + if (!RandomSize(4)) + { + WaspList[nWasp].nAction = 3; + WaspList[nWasp].field_2 = 0; + } + + WaspList[nWasp].nAction = 1; + sprite[nSprite].ang += RandomSize(9) + 768; + sprite[nSprite].ang &= kAngleMask; + + WaspList[nWasp].field_12 = 3000; + + sprite[nSprite].zvel = (-20) - RandomSize(6); + } + else + { + // Wasp is dead + WaspList[nWasp].nAction = 4; + WaspList[nWasp].field_2 = 0; + + nVelShift = 0; + + sprite[nSprite].cstat = 0; + + nCreaturesLeft--; + + sprite[nSprite].ang = (sprite[nSprite].ang + 1024) & kAngleMask; + + SetWaspVel(nSprite); + + sprite[nSprite].zvel = 512; + } + } + return; + } + + case 0x20000: + { + short nSeq = SeqOffsets[kSeqWasp] + ActionSeq[nAction].a; + + sprite[nSprite].picnum = seq_GetSeqPicnum2(nSeq, WaspList[nWasp].field_2); + + seq_MoveSequence(nSprite, nSeq, WaspList[nWasp].field_2); + + WaspList[nWasp].field_2++; + if (WaspList[nWasp].field_2 >= SeqSize[nSeq]) + { + WaspList[nWasp].field_2 = 0; + someval = 1; + } + + if (WaspList[nWasp].nHealth > 0) + { + nTarget = WaspList[nWasp].nTarget; + + if (nTarget > -1 && (!(sprite[nTarget].cstat & 0x101) || (SectFlag[sprite[nTarget].sectnum] & kSectUnderwater))) + { + // goto pink + WaspList[nWasp].nTarget = -1; + WaspList[nWasp].nAction = 0; + WaspList[nWasp].field_C = RandomSize(6); + return; + } + } + + switch (nAction) + { + case 0: + { + sprite[nSprite].zvel = Sin(WaspList[nWasp].field_E) >> 4; + + WaspList[nWasp].field_E += WaspList[nWasp].field_10; + WaspList[nWasp].field_E &= kAngleMask; + + MoveCreature(nSprite); + + if (nTarget >= 0) + { + WaspList[nWasp].field_C--; + if (WaspList[nWasp].field_C > 0) + { + PlotCourseToSprite(nSprite, nTarget); + } + else + { + WaspList[nWasp].nAction = 1; + sprite[nSprite].zvel = 0; + WaspList[nWasp].field_2 = 0; + WaspList[nWasp].field_12 = 1500; + WaspList[nWasp].field_C = RandomSize(5) + 60; + } + } + else + { + if ((nWasp & 0x1F) == (totalmoves & 0x1F)) { + WaspList[nWasp].nTarget = FindPlayer(nSprite, 60); + } + } + + return; + } + + case 1: + { + WaspList[nWasp].field_C--; + + if (WaspList[nWasp].field_C <= 0) + { + WaspList[nWasp].nAction = 0; + WaspList[nWasp].field_C = RandomSize(6); + return; + } + + int nChaseVal = AngleChase(nSprite, nTarget, WaspList[nWasp].field_12, 0, 16); + + switch (nChaseVal & 0xC000) + { + default: + return; + + case 0x8000: + { + return; + } + + case 0xC000: + { + short nSprite2 = (nChaseVal & 0x3FFF); + if (nSprite2 == nTarget) + { + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + runlist_DamageEnemy(nSprite2, nSprite, WaspList[nWasp].field_16); + WaspList[nWasp].nAction = 2; + WaspList[nWasp].field_2 = 0; + } + return; + } + } + + return; + } + + case 2: + case 3: + { + if (someval) + { + WaspList[nWasp].nAction = 1; + sprite[nSprite].ang = RandomSize(9) + 768; + sprite[nSprite].ang &= kAngleMask; + + WaspList[nWasp].field_12 = 3000; + + sprite[nSprite].zvel = (-20) - RandomSize(6); + } + return; + } + case 4: + { + int nMove = MoveCreature(nSprite) & 0x8000; + nMove |= 0xC000; + + if (nMove) + { + sprite[nSprite].yvel = 0; + WaspList[nWasp].nAction = 5; + sprite[nSprite].xvel = 0; + WaspList[nWasp].field_2 = 0; + sprite[nSprite].zvel = 1024; + } + + return; + } + case 5: + { + short nSector = sprite[nSprite].sectnum; + + sprite[nSprite].z += sprite[nSprite].zvel; + + if (sprite[nSprite].z >= sector[nSector].floorz) + { + if (SectBelow[nSector] > -1) + { + BuildSplash(nSprite, nSector); + sprite[nSprite].cstat |= 0x8000; + } + + sprite[nSprite].zvel = 0; + sprite[nSprite].yvel = 0; + sprite[nSprite].xvel = 0; + WaspList[nWasp].nAction = 6; + WaspList[nWasp].field_2 = 0; + runlist_SubRunRec(WaspList[nWasp].field_8); + } + + return; + } + default: + { + return; + } + } + + break; + } + } +} +END_PS_NS diff --git a/source/exhumed/src/wasp.h b/source/exhumed/src/wasp.h new file mode 100644 index 000000000..aa2a7be39 --- /dev/null +++ b/source/exhumed/src/wasp.h @@ -0,0 +1,32 @@ +//------------------------------------------------------------------------- +/* +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. +*/ +//------------------------------------------------------------------------- + +#ifndef __wasp_h__ +#define __wasp_h__ + +BEGIN_PS_NS + +extern short nWaspCount; + +void InitWasps(); +int BuildWasp(short nSprite, int x, int y, int z, short nSector, short nAngle); +void FuncWasp(int eax, int edx, int nRun); + +END_PS_NS + +#endif diff --git a/source/glbackend/glbackend.h b/source/glbackend/glbackend.h index 5c17afbe9..0598a2254 100644 --- a/source/glbackend/glbackend.h +++ b/source/glbackend/glbackend.h @@ -256,6 +256,15 @@ public: { renderState.IndexBuffer = vb; } + void ClearBufferState() + { + SetVertexBuffer(nullptr, 0, 0); + SetIndexBuffer(nullptr); + // Invalidate the pointers as well to make sure that if another buffer with the same address is used it actually gets bound. + LastVertexBuffer = (IVertexBuffer*)~intptr_t(0); + LastIndexBuffer = (IIndexBuffer*)~intptr_t(0); + } + const VSMatrix &GetMatrix(int num) { return matrices[num]; diff --git a/source/glbackend/hw_draw2d.cpp b/source/glbackend/hw_draw2d.cpp index c9ec925d0..c16b0dece 100644 --- a/source/glbackend/hw_draw2d.cpp +++ b/source/glbackend/hw_draw2d.cpp @@ -118,6 +118,7 @@ void GLInstance::Draw2D(F2DDrawer *drawer) } F2DVertexBuffer vb; vb.UploadData(&vertices[0], vertices.Size(), &indices[0], indices.Size()); + assert(vb.GetBufferObjects().first && vb.GetBufferObjects().second); SetVertexBuffer(vb.GetBufferObjects().first, 0, 0); SetIndexBuffer(vb.GetBufferObjects().second); SetFadeDisable(true); @@ -210,8 +211,7 @@ void GLInstance::Draw2D(F2DDrawer *drawer) //state.SetScissor(-1, -1, -1, -1); //state.SetRenderStyle(STYLE_Translucent); - SetVertexBuffer(nullptr, 0, 0); - SetIndexBuffer(nullptr); + ClearBufferState(); UseColorOnly(false); //state.EnableBrightmap(true); //state.SetTextureMode(TM_NORMAL); diff --git a/source/rr/src/config.h b/source/rr/src/config.h index eaf9ab298..b499d7bb0 100644 --- a/source/rr/src/config.h +++ b/source/rr/src/config.h @@ -32,8 +32,6 @@ BEGIN_RR_NS int32_t CONFIG_ReadSetup( void ); void CONFIG_GetSetupFilename( void ); -void CONFIG_SetDefaults(void); - END_RR_NS #endif diff --git a/source/sw/src/config.cpp b/source/sw/src/config.cpp index f681a1403..b4684f56a 100644 --- a/source/sw/src/config.cpp +++ b/source/sw/src/config.cpp @@ -52,15 +52,6 @@ BEGIN_SW_NS int32_t NumberPlayers,CommPort,PortSpeed,IrqNumber,UartAddress; -// -// Sound variables -// - - -int32_t UseMouse = 1, UseJoystick = 0; - - - /* =================== = diff --git a/source/sw/src/config.h b/source/sw/src/config.h index 73f934848..89f91ac51 100644 --- a/source/sw/src/config.h +++ b/source/sw/src/config.h @@ -66,7 +66,6 @@ extern char UserPath[MAXUSERLEVELPATHLENGTH]; #endif // controller externs -extern int32_t UseMouse, UseJoystick; extern int32_t EnableRudder; diff --git a/source/sw/src/game.cpp b/source/sw/src/game.cpp index ba0d95d79..c4674a82f 100644 --- a/source/sw/src/game.cpp +++ b/source/sw/src/game.cpp @@ -614,7 +614,7 @@ bool LoadLevel(const char *filename) { Printf("Level not found: %s", filename); return false; - } + } currentLevel = &mapList[Level]; STAT_NewLevel(currentLevel->labelName); return true; @@ -768,8 +768,8 @@ bool InitGame() while (initmultiplayerscycle()) { handleevents(); + } } - } #else numplayers = 1; myconnectindex = 0; connecthead = 0; connectpoint2[0] = -1; @@ -855,7 +855,7 @@ bool InitGame() LoadCustomInfoFromScript("engine/swcustom.txt"); // load the internal definitions. These also apply to the shareware version. if (!SW_SHAREWARE) { - LoadCustomInfoFromScript("swcustom.txt"); // Load user customisation information + LoadCustomInfoFromScript("swcustom.txt"); // Load user customisation information } if (!loaddefinitionsfile(G_DefFile())) buildputs("Definitions file loaded.\n"); @@ -950,10 +950,10 @@ void FindLevelInfo(char *map_name, short *level) { if (Bstrcasecmp(map_name, mapList[j].fileName.GetChars()) == 0) { - *level = j; - return; + *level = j; + return; + } } - } *level = 0; return; @@ -1392,7 +1392,7 @@ void NewLevel(void) PlayTheme(); MenuLevel(); STAT_Update(true); - } + } } FinishAnim = 0; } @@ -3021,7 +3021,7 @@ void PauseKey(PLAYERp pp) { if (ReloadPrompt) { - ReloadPrompt = FALSE; + ReloadPrompt = FALSE; /* } else @@ -3087,9 +3087,9 @@ void getinput(SW_PACKET *loc) if (cl_autoaim) SET(Player[myconnectindex].Flags, PF_AUTO_AIM); - else + else RESET(Player[myconnectindex].Flags, PF_AUTO_AIM); - } + } ControlInfo info; CONTROL_GetInput(&info); @@ -3396,6 +3396,7 @@ void getinput(SW_PACKET *loc) SET(loc->bits, which_weapon); } + inv_hotkey = 0; if (buttonMap.ButtonDown(gamefunc_Med_Kit)) inv_hotkey = INVENTORY_MEDKIT+1; diff --git a/source/sw/src/menus.cpp b/source/sw/src/menus.cpp index daa835ae1..034e2cf88 100644 --- a/source/sw/src/menus.cpp +++ b/source/sw/src/menus.cpp @@ -69,79 +69,79 @@ short TimeLimitTable[9] = {0,3,5,10,15,20,30,45,60}; SWBOOL MNU_StartNetGame(void) { - extern SWBOOL ExitLevel, ShortGameMode, DemoInitOnce, FirstTimeIntoGame; - extern short Level, Skill; - // CTW REMOVED - //extern int gTenActivated; - // CTW REMOVED END - int pnum; + extern SWBOOL ExitLevel, ShortGameMode, DemoInitOnce, FirstTimeIntoGame; + extern short Level, Skill; + // CTW REMOVED + //extern int gTenActivated; + // CTW REMOVED END + int pnum; - // always assumed that a demo is playing + // always assumed that a demo is playing - ready2send = 0; - // Skill can go negative here + ready2send = 0; + // Skill can go negative here Skill = gs.NetMonsters - 1; - Level = gs.NetLevel + 1; - DemoPlaying = FALSE; - ExitLevel = TRUE; - NewGame = TRUE; - // restart demo for multi-play mode - DemoInitOnce = FALSE; + Level = gs.NetLevel + 1; + DemoPlaying = FALSE; + ExitLevel = TRUE; + NewGame = TRUE; + // restart demo for multi-play mode + DemoInitOnce = FALSE; - // TENSW: return if a joiner - if (/* CTW REMOVED gTenActivated && */ !AutoNet && FirstTimeIntoGame) - return TRUE; + // TENSW: return if a joiner + if (/* CTW REMOVED gTenActivated && */ !AutoNet && FirstTimeIntoGame) + return TRUE; - // need to set gNet vars for self - // everone else gets a packet to set them - gNet.AutoAim = cl_autoaim; - gNet.SpawnMarkers = gs.NetSpawnMarkers; - gNet.HurtTeammate = gs.NetHurtTeammate; - gNet.Nuke = gs.NetNuke; + // need to set gNet vars for self + // everone else gets a packet to set them + gNet.AutoAim = cl_autoaim; + gNet.SpawnMarkers = gs.NetSpawnMarkers; + gNet.HurtTeammate = gs.NetHurtTeammate; + gNet.Nuke = gs.NetNuke; gNet.KillLimit = gs.NetKillLimit * 10; gNet.TimeLimit = TimeLimitTable[gs.NetTimeLimit] * 60 * 120; - if (ShortGameMode) - { - gNet.KillLimit /= 10; - gNet.TimeLimit /= 2; - } + if (ShortGameMode) + { + gNet.KillLimit /= 10; + gNet.TimeLimit /= 2; + } - gNet.TimeLimitClock = gNet.TimeLimit; - gNet.TeamPlay = gs.NetTeamPlay; + gNet.TimeLimitClock = gNet.TimeLimit; + gNet.TeamPlay = gs.NetTeamPlay; gNet.MultiGameType = gs.NetGameType + 1; - if (gNet.MultiGameType == MULTI_GAME_COMMBAT_NO_RESPAWN) - { - gNet.MultiGameType = MULTI_GAME_COMMBAT; - gNet.NoRespawn = TRUE; - } - else - { - gNet.NoRespawn = FALSE; - } + if (gNet.MultiGameType == MULTI_GAME_COMMBAT_NO_RESPAWN) + { + gNet.MultiGameType = MULTI_GAME_COMMBAT; + gNet.NoRespawn = TRUE; + } + else + { + gNet.NoRespawn = FALSE; + } - if (CommEnabled) - { - PACKET_NEW_GAME p; + if (CommEnabled) + { + PACKET_NEW_GAME p; - p.PacketType = PACKET_TYPE_NEW_GAME; - p.Level = Level; - p.Skill = Skill; - p.GameType = gs.NetGameType; - p.AutoAim = cl_autoaim; - p.HurtTeammate = gs.NetHurtTeammate; - p.TeamPlay = gs.NetTeamPlay; - p.SpawnMarkers = gs.NetSpawnMarkers; - p.KillLimit = gs.NetKillLimit; - p.TimeLimit = gs.NetTimeLimit; - p.Nuke = gs.NetNuke; + p.PacketType = PACKET_TYPE_NEW_GAME; + p.Level = Level; + p.Skill = Skill; + p.GameType = gs.NetGameType; + p.AutoAim = cl_autoaim; + p.HurtTeammate = gs.NetHurtTeammate; + p.TeamPlay = gs.NetTeamPlay; + p.SpawnMarkers = gs.NetSpawnMarkers; + p.KillLimit = gs.NetKillLimit; + p.TimeLimit = gs.NetTimeLimit; + p.Nuke = gs.NetNuke; - netbroadcastpacket((uint8_t*)(&p), sizeof(p)); // TENSW - } + netbroadcastpacket((uint8_t*)(&p), sizeof(p)); // TENSW + } - return TRUE; + return TRUE; } @@ -386,17 +386,17 @@ void MNU_DrawSmallString(short x, short y, const char *string, short shade, shor // intersections of these lines. static int faderamp[32] = { - // y=64-4x - 252,240,224,208,192,176, + // y=64-4x + 252,240,224,208,192,176, - // y=44.8-(16/20)x - 160,156,152,152,148, - 144,140,136,136,132, - 128,124,120,120,116, - 112,108,104,104,100, + // y=44.8-(16/20)x + 160,156,152,152,148, + 144,140,136,136,132, + 128,124,120,120,116, + 112,108,104,104,100, - // y=128-4x - 96,80,64,48,32,16 + // y=128-4x + 96,80,64,48,32,16 }; @@ -416,117 +416,117 @@ unsigned char palette_data[256][3]; // Global palette array ////////////////////////////////////////// void SetFadeAmt(PLAYERp pp, short damage, unsigned char startcolor) { - int palreg, usereg = 0, tmpreg1 = 0, tmpreg2 = 0; + int palreg, usereg = 0, tmpreg1 = 0, tmpreg2 = 0; short fadedamage = 0; - RGB_color color; + RGB_color color; //OSD_Printf("SetAmt: fadeamt = %d, startcolor = %d, pp = %d",pp->FadeAmt,startcolor,pp->StartColor); - if (abs(pp->FadeAmt) > 0 && startcolor == pp->StartColor) - return; + if (abs(pp->FadeAmt) > 0 && startcolor == pp->StartColor) + return; - // Don't ever over ride flash bomb - if (pp->StartColor == 1 && abs(pp->FadeAmt) > 0) - return; + // Don't ever over ride flash bomb + if (pp->StartColor == 1 && abs(pp->FadeAmt) > 0) + return; - // Reset the palette - if (pp == Player + screenpeek) - { + // Reset the palette + if (pp == Player + screenpeek) + { videoFadePalette(0, 0, 0, 0); - if (pp->FadeAmt <= 0) - GetPaletteFromVESA(&ppalette[screenpeek][0]); - } + if (pp->FadeAmt <= 0) + GetPaletteFromVESA(&ppalette[screenpeek][0]); + } - if (damage < -150 && damage > -1000) fadedamage = 150; - else if (damage < -1000) // Underwater + if (damage < -150 && damage > -1000) fadedamage = 150; + else if (damage < -1000) // Underwater fadedamage = abs(damage + 1000); - else - fadedamage = abs(damage); + else + fadedamage = abs(damage); - if (damage >= -5 && damage < 0) - fadedamage += 10; + if (damage >= -5 && damage < 0) + fadedamage += 10; - // Don't let red to TOO red - if (startcolor == COLOR_PAIN && fadedamage > 100) fadedamage = 100; + // Don't let red to TOO red + if (startcolor == COLOR_PAIN && fadedamage > 100) fadedamage = 100; - pp->FadeAmt = fadedamage / FADE_DAMAGE_FACTOR; + pp->FadeAmt = fadedamage / FADE_DAMAGE_FACTOR; - if (pp->FadeAmt <= 0) - { - pp->FadeAmt = 0; - return; - } + if (pp->FadeAmt <= 0) + { + pp->FadeAmt = 0; + return; + } - // It's a health item, just do a preset flash amount - if (damage > 0) - pp->FadeAmt = 3; + // It's a health item, just do a preset flash amount + if (damage > 0) + pp->FadeAmt = 3; - pp->StartColor = startcolor; + pp->StartColor = startcolor; - pp->FadeTics = 0; + pp->FadeTics = 0; - // Set player's palette to current game palette - GetPaletteFromVESA(pp->temp_pal); + // Set player's palette to current game palette + GetPaletteFromVESA(pp->temp_pal); - color.red = palette_data[pp->StartColor][0]; - color.green = palette_data[pp->StartColor][1]; - color.blue = palette_data[pp->StartColor][2]; + color.red = palette_data[pp->StartColor][0]; + color.green = palette_data[pp->StartColor][1]; + color.blue = palette_data[pp->StartColor][2]; - for (palreg = 0; palreg < 768; palreg++) - { - tmpreg1 = (int)(pp->temp_pal[palreg]) + ((2 * pp->FadeAmt) + 4); - tmpreg2 = (int)(pp->temp_pal[palreg]) - ((2 * pp->FadeAmt) + 4); - if (tmpreg1 > 255) - tmpreg1 = 255; - if (tmpreg2 < 0) - tmpreg2 = 0; + for (palreg = 0; palreg < 768; palreg++) + { + tmpreg1 = (int)(pp->temp_pal[palreg]) + ((2 * pp->FadeAmt) + 4); + tmpreg2 = (int)(pp->temp_pal[palreg]) - ((2 * pp->FadeAmt) + 4); + if (tmpreg1 > 255) + tmpreg1 = 255; + if (tmpreg2 < 0) + tmpreg2 = 0; - if (usereg == 0) - { - if (pp->temp_pal[palreg] < color.red) - { - if ((pp->temp_pal[palreg] = tmpreg1) > color.red) - pp->temp_pal[palreg] = color.red; - } - else if (pp->temp_pal[palreg] > color.red) - if ((pp->temp_pal[palreg] = tmpreg2) < color.red) - pp->temp_pal[palreg] = color.red; - } - else if (usereg == 1) - { - if (pp->temp_pal[palreg] < color.green) - { - if ((pp->temp_pal[palreg] = tmpreg1) > color.green) - pp->temp_pal[palreg] = color.green; - } - else if (pp->temp_pal[palreg] > color.green) - if ((pp->temp_pal[palreg] = tmpreg2) < color.green) - pp->temp_pal[palreg] = color.green; - } - else if (usereg == 2) - { - if (pp->temp_pal[palreg] < color.blue) - { - if ((pp->temp_pal[palreg] = tmpreg1) > color.blue) - pp->temp_pal[palreg] = color.blue; - } - else if (pp->temp_pal[palreg] > color.blue) - if ((pp->temp_pal[palreg] = tmpreg2) < color.blue) - pp->temp_pal[palreg] = color.blue; - } + if (usereg == 0) + { + if (pp->temp_pal[palreg] < color.red) + { + if ((pp->temp_pal[palreg] = tmpreg1) > color.red) + pp->temp_pal[palreg] = color.red; + } + else if (pp->temp_pal[palreg] > color.red) + if ((pp->temp_pal[palreg] = tmpreg2) < color.red) + pp->temp_pal[palreg] = color.red; + } + else if (usereg == 1) + { + if (pp->temp_pal[palreg] < color.green) + { + if ((pp->temp_pal[palreg] = tmpreg1) > color.green) + pp->temp_pal[palreg] = color.green; + } + else if (pp->temp_pal[palreg] > color.green) + if ((pp->temp_pal[palreg] = tmpreg2) < color.green) + pp->temp_pal[palreg] = color.green; + } + else if (usereg == 2) + { + if (pp->temp_pal[palreg] < color.blue) + { + if ((pp->temp_pal[palreg] = tmpreg1) > color.blue) + pp->temp_pal[palreg] = color.blue; + } + else if (pp->temp_pal[palreg] > color.blue) + if ((pp->temp_pal[palreg] = tmpreg2) < color.blue) + pp->temp_pal[palreg] = color.blue; + } - if (++usereg > 2) - usereg = 0; - } + if (++usereg > 2) + usereg = 0; + } - // Do initial palette set - if (pp == Player + screenpeek) - { - if (videoGetRenderMode() < REND_POLYMOST) set_pal(pp->temp_pal); + // Do initial palette set + if (pp == Player + screenpeek) + { + if (videoGetRenderMode() < REND_POLYMOST) set_pal(pp->temp_pal); else videoFadePalette(color.red, color.green, color.blue, faderamp[min(31, max(0, 32 - abs(pp->FadeAmt)))]); - if (damage < -1000) - pp->FadeAmt = 1000; // Don't call DoPaletteFlash for underwater stuff - } + if (damage < -1000) + pp->FadeAmt = 1000; // Don't call DoPaletteFlash for underwater stuff + } } ////////////////////////////////////////// @@ -535,98 +535,98 @@ void SetFadeAmt(PLAYERp pp, short damage, unsigned char startcolor) #define MAXFADETICS 5 void DoPaletteFlash(PLAYERp pp) { - int i, palreg, tmpreg1 = 0, tmpreg2 = 0; + int i, palreg, tmpreg1 = 0, tmpreg2 = 0; unsigned char* pal_ptr = &ppalette[screenpeek][0]; - if (pp->FadeAmt <= 1) - { - pp->FadeAmt = 0; - pp->StartColor = 0; - if (pp == Player + screenpeek) - { + if (pp->FadeAmt <= 1) + { + pp->FadeAmt = 0; + pp->StartColor = 0; + if (pp == Player + screenpeek) + { videoFadePalette(0, 0, 0, 0); - memcpy(pp->temp_pal, palette_data, sizeof(palette_data)); - DoPlayerDivePalette(pp); // Check Dive again - DoPlayerNightVisionPalette(pp); // Check Night Vision again - } + memcpy(pp->temp_pal, palette_data, sizeof(palette_data)); + DoPlayerDivePalette(pp); // Check Dive again + DoPlayerNightVisionPalette(pp); // Check Night Vision again + } - return; - } + return; + } - pp->FadeTics += synctics; // Add this frame's tic amount to - // counter + pp->FadeTics += synctics; // Add this frame's tic amount to + // counter - if (pp->FadeTics >= MAXFADETICS) - { - while (pp->FadeTics >= MAXFADETICS) - { - pp->FadeTics -= MAXFADETICS; + if (pp->FadeTics >= MAXFADETICS) + { + while (pp->FadeTics >= MAXFADETICS) + { + pp->FadeTics -= MAXFADETICS; - pp->FadeAmt--; // Decrement FadeAmt till it gets to - // 0 again. - } - } - else - return; // Return if they were not > - // MAXFADETICS + pp->FadeAmt--; // Decrement FadeAmt till it gets to + // 0 again. + } + } + else + return; // Return if they were not > + // MAXFADETICS - if (pp->FadeAmt > 32) - return; + if (pp->FadeAmt > 32) + return; - if (pp->FadeAmt <= 1) - { - pp->FadeAmt = 0; - pp->StartColor = 0; - if (pp == Player + screenpeek) - { + if (pp->FadeAmt <= 1) + { + pp->FadeAmt = 0; + pp->StartColor = 0; + if (pp == Player + screenpeek) + { videoFadePalette(0, 0, 0, 0); - memcpy(pp->temp_pal, palette_data, sizeof(palette_data)); - DoPlayerDivePalette(pp); // Check Dive again - DoPlayerNightVisionPalette(pp); // Check Night Vision again - } - return; - } - else - { - //CON_Message("gamavalues = %d, %d, %d",pp->temp_pal[pp->StartColor],pp->temp_pal[pp->StartColor+1],pp->temp_pal[pp->StartColor+2]); - for (palreg = 0; palreg < 768; palreg++) - { - tmpreg1 = (int)(pp->temp_pal[palreg]) + 2; - tmpreg2 = (int)(pp->temp_pal[palreg]) - 2; - if (tmpreg1 > 255) - tmpreg1 = 255; - if (tmpreg2 < 0) - tmpreg2 = 0; + memcpy(pp->temp_pal, palette_data, sizeof(palette_data)); + DoPlayerDivePalette(pp); // Check Dive again + DoPlayerNightVisionPalette(pp); // Check Night Vision again + } + return; + } + else + { + //CON_Message("gamavalues = %d, %d, %d",pp->temp_pal[pp->StartColor],pp->temp_pal[pp->StartColor+1],pp->temp_pal[pp->StartColor+2]); + for (palreg = 0; palreg < 768; palreg++) + { + tmpreg1 = (int)(pp->temp_pal[palreg]) + 2; + tmpreg2 = (int)(pp->temp_pal[palreg]) - 2; + if (tmpreg1 > 255) + tmpreg1 = 255; + if (tmpreg2 < 0) + tmpreg2 = 0; - if (pp->temp_pal[palreg] < pal_ptr[palreg]) - { - if ((pp->temp_pal[palreg] = tmpreg1) > pal_ptr[palreg]) - pp->temp_pal[palreg] = pal_ptr[palreg]; - } - else if (pp->temp_pal[palreg] > pal_ptr[palreg]) - if ((pp->temp_pal[palreg] = tmpreg2) < pal_ptr[palreg]) - pp->temp_pal[palreg] = pal_ptr[palreg]; + if (pp->temp_pal[palreg] < pal_ptr[palreg]) + { + if ((pp->temp_pal[palreg] = tmpreg1) > pal_ptr[palreg]) + pp->temp_pal[palreg] = pal_ptr[palreg]; + } + else if (pp->temp_pal[palreg] > pal_ptr[palreg]) + if ((pp->temp_pal[palreg] = tmpreg2) < pal_ptr[palreg]) + pp->temp_pal[palreg] = pal_ptr[palreg]; - } + } - // Only hard set the palette if this is currently the player's view - if (pp == Player + screenpeek) - { - if (videoGetRenderMode() < REND_POLYMOST) set_pal(pp->temp_pal); - else - { - videoFadePalette( - palette_data[pp->StartColor][0], - palette_data[pp->StartColor][1], - palette_data[pp->StartColor][2], + // Only hard set the palette if this is currently the player's view + if (pp == Player + screenpeek) + { + if (videoGetRenderMode() < REND_POLYMOST) set_pal(pp->temp_pal); + else + { + videoFadePalette( + palette_data[pp->StartColor][0], + palette_data[pp->StartColor][1], + palette_data[pp->StartColor][2], faderamp[min(31, max(0, 32 - abs(pp->FadeAmt)))] - ); - } - } + ); + } + } - } + } } diff --git a/source/sw/src/sounds.cpp b/source/sw/src/sounds.cpp index 0a30cece2..38a4f34d5 100644 --- a/source/sw/src/sounds.cpp +++ b/source/sw/src/sounds.cpp @@ -354,7 +354,7 @@ static void DoTimedSound(AmbientSound* amb) amb->curIndex += synctics; if (amb->curIndex >= amb->maxIndex) { - if (amb->sndChan == nullptr) + if (amb->sndChan == nullptr || (amb->sndChan->ChanFlags & CHANF_FORGETTABLE)) { // Check for special case ambient sounds. Since the sound is stopped and doesn't occupy a real channel at this time we can just swap out the sound ID before restarting it. int ambid = RandomizeAmbientSpecials(amb->vocIndex); diff --git a/wadsrc/static/engine/grpinfo.txt b/wadsrc/static/engine/grpinfo.txt index 23ac595db..6c768f07f 100644 --- a/wadsrc/static/engine/grpinfo.txt +++ b/wadsrc/static/engine/grpinfo.txt @@ -32,6 +32,11 @@ CRC SWWD_CRC 0xA9AAA7B7 SWTD_CRC 0xA1A65BE8 + POWERSLAVE_CRC 0x303CBD89 + EXHUMED_CRC 0xE3B172F1 + POWERSLAVE_DEMO_CRC 0x1D8C7645 + EXHUMED_DEMO_CRC 0x1A6E27FA + } @@ -496,4 +501,37 @@ grpinfo gamefilter "ShadowWarrior.TwinDragon" } - \ No newline at end of file + + +grpinfo +{ + name "Powerslave" + flags GAMEFLAG_POWERSLAVE + crc POWERSLAVE_CRC + size 27020745 + defname "exhumed.def" + gamefilter "Exhumed.Powerslave" +} + +grpinfo +{ + name "Exhumed" + flags GAMEFLAG_EXHUMED + crc EXHUMED_CRC + size 27108170 + defname "exhumed.def" + gamefilter "Exhumed.Exhumed" +} + +grpinfo +{ + name "Powerslave Demo" + flags GAMEFLAG_POWERSLAVE|GAMEFLAG_SHAREWARE + crc POWERSLAVE_DEMO_CRC + size 27020745 + defname "exhumed.def" + gamefilter "Exhumed.Powerslave" +} + + +// { "Exhumed Demo", EXHUMED_DEMO_CRC, 16481687, GAMEFLAG_EXHUMED | GAMEFLAG_DEMO, 0 }, diff --git a/wadsrc/static/engine/menudef.txt b/wadsrc/static/engine/menudef.txt index 11907640c..375d11280 100644 --- a/wadsrc/static/engine/menudef.txt +++ b/wadsrc/static/engine/menudef.txt @@ -78,6 +78,18 @@ LISTMENU "MainMenu" } NativeTextItem "$MNU_QUITGAME", "q", "QuitMenu" } + ifgame(Exhumed) + { + Position 160, 65 + centermenu + class "Exhumed.MainMenu" + linespacing 22 + NativeTextItem "3460", "n", "StartGame", 1 + NativeTextItem "3461", "l", "LoadGameMenu" + NativeTextItem "3462", "m", "StartGame", 3 + NativeTextItem "3463", "v", "OptionsMenu" + NativeTextItem "3464", "q", "QuitMenu" + } } //------------------------------------------------------------------------------------------- @@ -153,6 +165,18 @@ LISTMENU "IngameMenu" } NativeTextItem "$MNU_QUITGAME", "q", "QuitMenu" } + ifgame(Exhumed) + { + Position 160, 65 + centermenu + class "Exhumed.MainMenu" + linespacing 22 + NativeTextItem "3460", "n", "StartGame", 1 + NativeTextItem "3461", "l", "LoadGameMenu" + NativeTextItem "3462", "m", "StartGame", 3 + NativeTextItem "3463", "v", "OptionsMenu" + NativeTextItem "3464", "q", "QuitMenu" + } } //------------------------------------------------------------------------------------------- diff --git a/wadsrc/static/filter/exhumed/engine/defbinds.txt b/wadsrc/static/filter/exhumed/engine/defbinds.txt new file mode 100644 index 000000000..08ae25f57 --- /dev/null +++ b/wadsrc/static/filter/exhumed/engine/defbinds.txt @@ -0,0 +1,66 @@ +W "+Move_Forward" +KP8 "+Move_Forward" +S "+Move_Backward" +KP2 "+Move_Backward" +leftarrow "+Turn_Left" +KP4 "+Turn_Left" +rightarrow "+Turn_Right" +KP6 "+Turn_Right" +LAlt "+Strafe" +RAlt "+Strafe" +RCtrl "+Fire" +E "+Open" +LShift "+Run" +RShift "+Run" +Space "+Jump" +/ "+Jump" +LCtrl "+Crouch" +PgUp "+Look_Up" +KP9 "+Look_Up" +PgDn "+Look_Down" +KP3 "+Look_Down" +Ins "+Look_Left" +KP0 "+Look_Left" +Del "+Look_Right" +KP. "+Look_Right" +A "+Strafe_Left" +D "+Strafe_Right" +Home "+Aim_Up" +KP7 "+Aim_Up" +End "+Aim_Down" +KP1 "+Aim_Down" +1 "+Weapon_1" +2 "+Weapon_2" +3 "+Weapon_3" +4 "+Weapon_4" +5 "+Weapon_5" +6 "+Weapon_6" +7 "+Weapon_7" +Enter "+Inventory" +KP-Enter "+Inventory" +[ "+Inventory_Left" +] "+Inventory_Right" +Backspace "+Turn_Around" +T "+Send_Message" +Tab "+Map" +- "+Shrink_Screen" +KP- "+Shrink_Screen" += "+Enlarge_Screen" +KP+ "+Enlarge_Screen" +KP5 "+Center_View" +Scroll "+Holster_Weapon" +Y "+Show_Opponents_Weapon" +F "+Map_Follow_Mode" +K "+See_Coop_View" +' "+Next_Weapon" +; "+Previous_Weapon" +` "toggleconsole" +Capslock "+AutoRun" +X "+Last_Used_Weapon" +F6 "+Quick_Save" +F9 "+Quick_Load" +F7 "+Third_Person_View" +C "+Toggle_Crouch" +Mouse1 "+Fire" +Mouse2 "+Strafe" +Mouse3 "+Move_Forward" diff --git a/wadsrc/static/filter/exhumed/engine/leftbinds.txt b/wadsrc/static/filter/exhumed/engine/leftbinds.txt new file mode 100644 index 000000000..6a6fd5e57 --- /dev/null +++ b/wadsrc/static/filter/exhumed/engine/leftbinds.txt @@ -0,0 +1,64 @@ +uparrow "+Move_Forward" +KP8 "+Move_Forward" +downarrow "+Move_Backward" +KP2 "+Move_Backward" +leftarrow "+Turn_Left" +KP4 "+Turn_Left" +rightarrow "+Turn_Right" +KP6 "+Turn_Right" +LAlt "+Strafe" +RAlt "+Strafe" +LCtrl "+Fire" +RCtrl "+Fire" +Space "+Open" +LShift "+Run" +RShift "+Run" +A "+Jump" +KP- "+Jump" +Z "+Crouch" +PgUp "+Look_Up" +PgDn "+Look_Down" +Ins "+Look_Left" +Del "+Look_Right" +, "+Strafe_Left" +KP7 "+Strafe_Left" +. "+Strafe_Right" +KP9 "+Strafe_Right" +Home "+Aim_Up" +End "+Aim_Down" +1 "+Weapon_1" +2 "+Weapon_2" +3 "+Weapon_3" +4 "+Weapon_4" +5 "+Weapon_5" +6 "+Weapon_6" +7 "+Weapon_7" +Enter "+Inventory" +KP-Enter "+Inventory" +[ "+Inventory_Left" +] "+Inventory_Right" +Backspace "+Turn_Around" +T "+Send_Message" +Tab "+Map" +- "+Shrink_Screen" += "+Enlarge_Screen" +//Scroll "+Holster_Weapon" +//W "+Show_Opponents_Weapon" +F "+Map_Follow_Mode" +K "+See_Coop_View" +U "+Mouse_Aiming" +I "+Toggle_Crosshair" +//R "+Steroids" +//Q "+Quick_Kick" +' "+Next_Weapon" +; "+Previous_Weapon" +` "toggleconsole" +Capslock "+AutoRun" +F6 "+Quick_Save" +F9 "+Quick_Load" +F7 "+Third_Person_View" +Mouse1 "+Fire" +Mouse2 "+Open" +Mouse3 "+Run" +MWheelUp "+Previous_Weapon" +MWheelDown "+Next_Weapon" diff --git a/wadsrc/static/filter/exhumed/engine/origbinds.txt b/wadsrc/static/filter/exhumed/engine/origbinds.txt new file mode 100644 index 000000000..70f2f743d --- /dev/null +++ b/wadsrc/static/filter/exhumed/engine/origbinds.txt @@ -0,0 +1,69 @@ +uparrow "+Move_Forward" +KP8 "+Move_Forward" +downarrow "+Move_Backward" +KP2 "+Move_Backward" +leftarrow "+Turn_Left" +KP4 "+Turn_Left" +rightarrow "+Turn_Right" +KP6 "+Turn_Right" +LAlt "+Strafe" +RAlt "+Strafe" +LCtrl "+Fire" +RCtrl "+Fire" +Space "+Open" +LShift "+Run" +RShift "+Run" +A "+Jump" +/ "+Jump" +Z "+Crouch" +PgUp "+Look_Up" +KP9 "+Look_Up" +PgDn "+Look_Down" +KP3 "+Look_Down" +Ins "+Look_Left" +KP0 "+Look_Left" +Del "+Look_Right" +KP. "+Look_Right" +, "+Strafe_Left" +. "+Strafe_Right" +Home "+Aim_Up" +KP7 "+Aim_Up" +End "+Aim_Down" +KP1 "+Aim_Down" +1 "+Weapon_1" +2 "+Weapon_2" +3 "+Weapon_3" +4 "+Weapon_4" +5 "+Weapon_5" +6 "+Weapon_6" +7 "+Weapon_7" +Enter "+Inventory" +KP-Enter "+Inventory" +[ "+Inventory_Left" +] "+Inventory_Right" +Backspace "+Turn_Around" +T "+Send_Message" +Tab "+Map" +- "+Shrink_Screen" +KP- "+Shrink_Screen" += "+Enlarge_Screen" +KP+ "+Enlarge_Screen" +KP5 "+Center_View" +//Scroll "+Holster_Weapon" +//W "+Show_Opponents_Weapon" +F "+Map_Follow_Mode" +K "+See_Coop_View" +U "+Mouse_Aiming" +I "+Toggle_Crosshair" +//R "+Steroids" +//` "+Quick_Kick" +' "+Next_Weapon" +; "+Previous_Weapon" +` "toggleconsole" +Capslock "+AutoRun" +F6 "+Quick_Save" +F9 "+Quick_Load" +F7 "+Third_Person_View" +Mouse1 "+Fire" +Mouse2 "+Strafe" +Mouse3 "+Move_Forward"