diff --git a/gzdoom.vcproj b/gzdoom.vcproj index 3f706099..91660078 100644 --- a/gzdoom.vcproj +++ b/gzdoom.vcproj @@ -1791,6 +1791,10 @@ RelativePath=".\src\win32\boing8.ico" > + + @@ -2976,6 +2980,10 @@ /> + + diff --git a/src/b_func.cpp b/src/b_func.cpp index 5970c15f..3e72ff43 100644 --- a/src/b_func.cpp +++ b/src/b_func.cpp @@ -25,98 +25,87 @@ static FRandom pr_botdofire ("BotDoFire"); -//Used with Reachable(). -static AActor *looker; -static AActor *rtarget; -static bool reachable; -static fixed_t last_z; -static sector_t *last_s; -static fixed_t estimated_dist; -static bool PTR_Reachable (intercept_t *in) +//Checks TRUE reachability from +//one looker to another. First mobj (looker) is looker. +bool FCajunMaster::Reachable (AActor *looker, AActor *rtarget) { - fixed_t hitx, hity; - fixed_t frac; - line_t *line; - AActor *thing; - fixed_t dist; - sector_t *s; + if (looker == rtarget) + return false; - frac = in->frac - FixedDiv (4*FRACUNIT, MAX_TRAVERSE_DIST); - dist = FixedMul (frac, MAX_TRAVERSE_DIST); + if ((rtarget->Sector->ceilingplane.ZatPoint (rtarget->x, rtarget->y) - + rtarget->Sector->floorplane.ZatPoint (rtarget->x, rtarget->y)) + < looker->height) //Where rtarget is, looker can't be. + return false; - hitx = trace.x + FixedMul (looker->momx, frac); - hity = trace.y + FixedMul (looker->momy, frac); + sector_t *last_s = looker->Sector; + fixed_t last_z = last_s->floorplane.ZatPoint (looker->x, looker->y); + fixed_t estimated_dist = P_AproxDistance (looker->x - rtarget->x, looker->y - rtarget->y); + bool reachable = true; - if (in->isaline) + FPathTraverse it(looker->x+looker->momx, looker->y+looker->momy, rtarget->x, rtarget->y, PT_ADDLINES|PT_ADDTHINGS); + intercept_t *in; + while ((in = it.Next())) { - line = in->d.line; + fixed_t hitx, hity; + fixed_t frac; + line_t *line; + AActor *thing; + fixed_t dist; + sector_t *s; - if (!(line->flags & ML_TWOSIDED) || (line->flags & (ML_BLOCKING|ML_BLOCKEVERYTHING|ML_BLOCK_PLAYERS))) - { - return (reachable = false); //Cannot continue. - } - else - { - //Determine if going to use backsector/frontsector. - s = (line->backsector == last_s) ? line->frontsector : line->backsector; - fixed_t ceilingheight = s->ceilingplane.ZatPoint (hitx, hity); - fixed_t floorheight = s->floorplane.ZatPoint (hitx, hity); + frac = in->frac - FixedDiv (4*FRACUNIT, MAX_TRAVERSE_DIST); + dist = FixedMul (frac, MAX_TRAVERSE_DIST); - if (!bglobal.IsDangerous (s) && //Any nukage/lava? - (floorheight <= (last_z+MAXMOVEHEIGHT) - && ((ceilingheight == floorheight && line->special) - || (ceilingheight - floorheight) >= looker->height))) //Does it fit? + hitx = it.Trace().x + FixedMul (looker->momx, frac); + hity = it.Trace().y + FixedMul (looker->momy, frac); + + if (in->isaline) + { + line = in->d.line; + + if (!(line->flags & ML_TWOSIDED) || (line->flags & (ML_BLOCKING|ML_BLOCKEVERYTHING|ML_BLOCK_PLAYERS))) { - last_z = floorheight; - last_s = s; - return true; + return false; //Cannot continue. } else { - return (reachable = false); + //Determine if going to use backsector/frontsector. + s = (line->backsector == last_s) ? line->frontsector : line->backsector; + fixed_t ceilingheight = s->ceilingplane.ZatPoint (hitx, hity); + fixed_t floorheight = s->floorplane.ZatPoint (hitx, hity); + + if (!bglobal.IsDangerous (s) && //Any nukage/lava? + (floorheight <= (last_z+MAXMOVEHEIGHT) + && ((ceilingheight == floorheight && line->special) + || (ceilingheight - floorheight) >= looker->height))) //Does it fit? + { + last_z = floorheight; + last_s = s; + continue; + } + else + { + return false; + } } } + + if (dist > estimated_dist) + { + return true; + } + + thing = in->d.thing; + if (thing == looker) //Can't reach self in this case. + continue; + if (thing == rtarget && (rtarget->Sector->floorplane.ZatPoint (rtarget->x, rtarget->y) <= (last_z+MAXMOVEHEIGHT))) + { + return true; + } + + reachable = false; } - - if (dist > estimated_dist) - { - reachable = true; - return false; //Don't need to continue. - } - - thing = in->d.thing; - if (thing == looker) //Can't reach self in this case. - return true; - if (thing == rtarget && (rtarget->Sector->floorplane.ZatPoint (rtarget->x, rtarget->y) <= (last_z+MAXMOVEHEIGHT))) - { - reachable = true; - return false; - } - - reachable = false; - return true; -} - -//Checks TRUE reachability from -//one actor to another. First mobj (actor) is looker. -bool FCajunMaster::Reachable (AActor *actor, AActor *target) -{ - if (actor == target) - return false; - - if ((target->Sector->ceilingplane.ZatPoint (target->x, target->y) - - target->Sector->floorplane.ZatPoint (target->x, target->y)) - < actor->height) //Where target is, looker can't be. - return false; - - looker = actor; - rtarget = target; - last_s = actor->Sector; - last_z = last_s->floorplane.ZatPoint (actor->x, actor->y); - reachable = true; - estimated_dist = P_AproxDistance (actor->x - target->x, actor->y - target->y); - P_PathTraverse (actor->x+actor->momx, actor->y+actor->momy, target->x, target->y, PT_ADDLINES|PT_ADDTHINGS, PTR_Reachable); return reachable; } diff --git a/src/g_shared/sbarinfo.h b/src/g_shared/sbarinfo.h index 7d38c324..510da22b 100644 --- a/src/g_shared/sbarinfo.h +++ b/src/g_shared/sbarinfo.h @@ -40,6 +40,7 @@ #include "v_collection.h" #define NUMHUDS 9 +#define NUMPOPUPS 3 class FBarTexture; class FScanner; @@ -47,6 +48,35 @@ class FScanner; struct SBarInfoCommand; //we need to be able to use this before it is defined. struct MugShotState; +//Popups! +enum PopupTransition +{ + TRANSITION_NONE, + TRANSITION_SLIDEINBOTTOM, +}; + +struct Popup +{ + PopupTransition transition; + bool opened; + bool moving; + int height; + int width; + int speed; + int x; + int y; + + Popup(); + void init(); + void tick(); + void open(); + void close(); + bool isDoneMoving(); + int getXOffset(); + int getYOffset(); +}; + +//SBarInfo struct SBarInfoBlock { TArray commands; @@ -82,6 +112,7 @@ struct SBarInfo { TArray Images; SBarInfoBlock huds[NUMHUDS]; + Popup popups[NUMPOPUPS]; bool automapbar; bool interpolateHealth; bool interpolateArmor; @@ -276,6 +307,7 @@ enum //Key words SBARINFO_LOWERHEALTHCAP, SBARINFO_STATUSBAR, SBARINFO_MUGSHOT, + SBARINFO_CREATEPOPUP, }; enum //Bar types @@ -340,7 +372,7 @@ public: void ShowPop(int popnum); void SetMugShotState(const char* stateName, bool waitTillDone=false); private: - void doCommands(SBarInfoBlock &block); + void doCommands(SBarInfoBlock &block, int xOffset=0, int yOffset=0); void DrawGraphic(FTexture* texture, int x, int y, int flags=0); void DrawString(const char* str, int x, int y, EColorRange translation, int spacing=0); void DrawNumber(int num, int len, int x, int y, EColorRange translation, int spacing=0, bool fillzeros=false); @@ -366,6 +398,7 @@ private: int mugshotHealth; int chainWiggle; int artiflash; + int pendingPopup; int currentPopup; unsigned int invBarOffset; FBarShader shader_horz_normal; diff --git a/src/g_shared/sbarinfo_display.cpp b/src/g_shared/sbarinfo_display.cpp index 77cb7f7b..21a9da55 100644 --- a/src/g_shared/sbarinfo_display.cpp +++ b/src/g_shared/sbarinfo_display.cpp @@ -267,6 +267,8 @@ DSBarInfo::DSBarInfo () : DBaseStatusBar (SBarInfoScript->height), chainWiggle = 0; artiflash = 4; currentState = NULL; + currentPopup = POP_None; + pendingPopup = POP_None; } DSBarInfo::~DSBarInfo () @@ -320,11 +322,11 @@ void DSBarInfo::Draw (EHudState state) if(currentPopup != POP_None) { if(currentPopup == POP_Log) - doCommands(SBarInfoScript->huds[STBAR_POPUPLOG]); + doCommands(SBarInfoScript->huds[STBAR_POPUPLOG], SBarInfoScript->popups[currentPopup].getXOffset(), SBarInfoScript->popups[currentPopup].getYOffset()); else if(currentPopup == POP_Keys) - doCommands(SBarInfoScript->huds[STBAR_POPUPKEYS]); + doCommands(SBarInfoScript->huds[STBAR_POPUPKEYS], SBarInfoScript->popups[currentPopup].getXOffset(), SBarInfoScript->popups[currentPopup].getYOffset()); else if(currentPopup == POP_Status) - doCommands(SBarInfoScript->huds[STBAR_POPUPSTATUS]); + doCommands(SBarInfoScript->huds[STBAR_POPUPSTATUS], SBarInfoScript->popups[currentPopup].getXOffset(), SBarInfoScript->popups[currentPopup].getYOffset()); } } @@ -410,6 +412,16 @@ void DSBarInfo::Tick () rampageTimer = 0; } mugshotHealth = CPlayer->health; + if(currentPopup != POP_None) + { + SBarInfoScript->popups[currentPopup].tick(); + if(SBarInfoScript->popups[currentPopup].opened == false && SBarInfoScript->popups[currentPopup].isDoneMoving()) + { + currentPopup = pendingPopup; + if(currentPopup != POP_None) + SBarInfoScript->popups[currentPopup].open(); + } + } } void DSBarInfo::ReceivedWeapon (AWeapon *weapon) @@ -426,9 +438,20 @@ void DSBarInfo::ShowPop(int popnum) { DBaseStatusBar::ShowPop(popnum); if(popnum != currentPopup) - currentPopup = popnum; + { + pendingPopup = popnum; + } else - currentPopup = POP_None; + pendingPopup = POP_None; + if(currentPopup != POP_None) + SBarInfoScript->popups[currentPopup].close(); + else + { + currentPopup = pendingPopup; + pendingPopup = POP_None; + if(currentPopup != POP_None) + SBarInfoScript->popups[currentPopup].open(); + } } //Public so it can be called by ACS @@ -447,7 +470,7 @@ void DSBarInfo::SetMugShotState(const char* stateName, bool waitTillDone) } } -void DSBarInfo::doCommands(SBarInfoBlock &block) +void DSBarInfo::doCommands(SBarInfoBlock &block, int xOffset, int yOffset) { //prepare ammo counts AAmmo *ammo1, *ammo2; @@ -467,6 +490,8 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) for(unsigned int i = 0;i < block.commands.Size();i++) { SBarInfoCommand& cmd = block.commands[i]; + cmd.x += xOffset; + cmd.y += yOffset; switch(cmd.type) //read and execute all the commands { case SBARINFO_DRAWSWITCHABLEIMAGE: //draw the alt image if we don't have the item else this is like a normal drawimage @@ -712,7 +737,7 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) } else if((cmd.flags & DRAWSELECTEDINVENTORY_ALTERNATEONEMPTY)) { - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); } break; case SBARINFO_DRAWINVENTORYBAR: @@ -1063,7 +1088,7 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) ((cmd.flags & GAMETYPE_COOPERATIVE) && multiplayer && !deathmatch) || ((cmd.flags & GAMETYPE_TEAMGAME) && teamplay)) { - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); } break; case SBARINFO_PLAYERCLASS: @@ -1072,14 +1097,14 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) int spawnClass = CPlayer->cls->ClassIndex; if(cmd.special == spawnClass || cmd.special2 == spawnClass || cmd.special3 == spawnClass) { - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); } break; } case SBARINFO_ASPECTRATIO: if(CheckRatio(screen->GetWidth(), screen->GetHeight()) == cmd.value) { - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); } break; case SBARINFO_ISSELECTED: @@ -1090,16 +1115,16 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) if(weapon2 != NULL) { if((cmd.flags & SBARINFOEVENT_NOT) && (weapon1 != CPlayer->ReadyWeapon->GetSpecies() && weapon2 != CPlayer->ReadyWeapon->GetSpecies())) - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); else if(!(cmd.flags & SBARINFOEVENT_NOT) && (weapon1 == CPlayer->ReadyWeapon->GetSpecies() || weapon2 == CPlayer->ReadyWeapon->GetSpecies())) - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); } else { if(!(cmd.flags & SBARINFOEVENT_NOT) && weapon1 == CPlayer->ReadyWeapon->GetSpecies()) - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); else if((cmd.flags & SBARINFOEVENT_NOT) && weapon1 != CPlayer->ReadyWeapon->GetSpecies()) - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); } } break; @@ -1115,7 +1140,7 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) bool usesammo2 = (AmmoType2 != NULL); if(!(cmd.flags & SBARINFOEVENT_NOT) && !usesammo1 && !usesammo2) //if the weapon doesn't use ammo don't go though the trouble. { - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); break; } //Or means only 1 ammo type needs to match and means both need to match. @@ -1126,11 +1151,11 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) if(((cmd.flags & SBARINFOEVENT_OR) && (match1 || match2)) || ((cmd.flags & SBARINFOEVENT_AND) && (match1 && match2))) { if(!(cmd.flags & SBARINFOEVENT_NOT)) - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); } else if(cmd.flags & SBARINFOEVENT_NOT) { - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); } } else //Every thing here could probably be one long if statement but then it would be more confusing. @@ -1138,11 +1163,11 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) if((usesammo1 && (AmmoType1 == IfAmmo1)) || (usesammo2 && (AmmoType2 == IfAmmo1))) { if(!(cmd.flags & SBARINFOEVENT_NOT)) - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); } else if(cmd.flags & SBARINFOEVENT_NOT) { - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); } } } @@ -1154,24 +1179,26 @@ void DSBarInfo::doCommands(SBarInfoBlock &block) if(cmd.flags & SBARINFOEVENT_AND) { if((item1 != NULL && item2 != NULL) && !(cmd.flags & SBARINFOEVENT_NOT)) - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); else if((item1 == NULL || item2 == NULL) && (cmd.flags & SBARINFOEVENT_NOT)) - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); } else if(cmd.flags & SBARINFOEVENT_OR) { if((item1 != NULL || item2 != NULL) && !(cmd.flags & SBARINFOEVENT_NOT)) - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); else if((item1 == NULL && item2 == NULL) && (cmd.flags & SBARINFOEVENT_NOT)) - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); } else if((item1 != NULL) && !(cmd.flags & SBARINFOEVENT_NOT)) - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); else if((item1 == NULL) && (cmd.flags & SBARINFOEVENT_NOT)) - doCommands(cmd.subBlock); + doCommands(cmd.subBlock, xOffset, yOffset); break; } } + cmd.x -= xOffset; + cmd.y -= yOffset; } } @@ -1191,7 +1218,8 @@ void DSBarInfo::DrawGraphic(FTexture* texture, int x, int y, int flags) y += ST_Y; int w = texture->GetScaledWidth(); int h = texture->GetScaledHeight() + y; - screen->VirtualToRealCoordsInt(x, y, w, h, 320, 200, true); + if(Scaled) + screen->VirtualToRealCoordsInt(x, y, w, h, 320, 200, true); h -= y; if((flags & DRAWIMAGE_TRANSLATABLE)) { @@ -1238,7 +1266,8 @@ void DSBarInfo::DrawString(const char* str, int x, int y, EColorRange translatio int ry = y + ST_Y; int rw = character->GetScaledWidth(); int rh = character->GetScaledHeight(); - screen->VirtualToRealCoordsInt(rx, ry, rw, rh, 320, 200, true); + if(Scaled) + screen->VirtualToRealCoordsInt(rx, ry, rw, rh, 320, 200, true); screen->DrawTexture(character, rx, ry, DTA_DestWidth, rw, DTA_DestHeight, rh, @@ -1297,7 +1326,8 @@ void DSBarInfo::DrawFace(FString &defaultFace, int accuracy, bool xdth, bool ani y += ST_Y; int w = face->GetScaledWidth(); int h = face->GetScaledHeight(); - screen->VirtualToRealCoordsInt(x, y, w, h, 320, 200, true); + if(Scaled) + screen->VirtualToRealCoordsInt(x, y, w, h, 320, 200, true); screen->DrawTexture(face, x, y, DTA_DestWidth, w, DTA_DestHeight, h, @@ -1492,10 +1522,10 @@ void DSBarInfo::DrawGem(FTexture* chain, FTexture* gem, int value, int x, int y, int offset = (int) (((double) (chainWidth-padleft-padright)/100)*value); if(chain != NULL) { - DrawImage(chain, x+(offset%chainsize), y); + DrawGraphic(chain, x+(offset%chainsize), y); } if(gem != NULL) - DrawImage(gem, x+padleft+offset, y, translate ? getTranslation() : NULL); + DrawGraphic(gem, x+padleft+offset, y, translate ? DRAWIMAGE_TRANSLATABLE : 0); } FRemapTable* DSBarInfo::getTranslation() diff --git a/src/g_shared/sbarinfo_parser.cpp b/src/g_shared/sbarinfo_parser.cpp index fb87786c..fb683e6b 100644 --- a/src/g_shared/sbarinfo_parser.cpp +++ b/src/g_shared/sbarinfo_parser.cpp @@ -60,6 +60,7 @@ static const char *SBarInfoTopLevel[] = "lowerhealthcap", "statusbar", "mugshot", + "createpopup", NULL }; @@ -305,6 +306,43 @@ void SBarInfo::ParseSBarInfo(int lump) MugShotStates.Push(state); break; } + case SBARINFO_CREATEPOPUP: + { + int pop = 0; + sc.MustGetToken(TK_Identifier); + if(sc.Compare("keys")) + pop = 0; + else if(sc.Compare("log")) + pop = 1; + else if(sc.Compare("status")) + pop = 2; + else + sc.ScriptError("Unkown popup: '%s'", sc.String); + Popup &popup = popups[pop]; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + popup.width = sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + popup.height = sc.Number; + sc.MustGetToken(','); + if(!sc.CheckToken(TK_None)) + { + sc.MustGetToken(TK_Identifier); + if(sc.Compare("slideinbottom")) + { + popup.transition = TRANSITION_SLIDEINBOTTOM; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + popup.speed = sc.Number; + } + else + sc.ScriptError("Unkown transition type: '%s'", sc.String); + } + popup.init(); + sc.MustGetToken(';'); + break; + } } } } @@ -553,9 +591,11 @@ void SBarInfo::ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block) sc.MustGetToken(';'); break; case SBARINFO_DRAWMUGSHOT: - sc.MustGetToken(TK_StringConst); - cmd.setString(sc, sc.String, 0, 3, true); - sc.MustGetToken(','); + if(sc.CheckToken(TK_StringConst)) + { + cmd.setString(sc, sc.String, 0, 3, true); + sc.MustGetToken(','); + } sc.MustGetToken(TK_IntConst); //accuracy if(sc.Number < 1 || sc.Number > 9) sc.ScriptError("Expected a number between 1 and 9, got %d instead.", sc.Number); @@ -1237,6 +1277,88 @@ const MugShotState *FindMugShotState(FString state) return NULL; } +//Popup +Popup::Popup() +{ + transition = TRANSITION_NONE; + height = 320; + width = 200; + speed = 0; + x = 320; + y = 200; + opened = false; + moving = false; +} + +void Popup::init() +{ + x = width; + y = height; + if(transition == TRANSITION_SLIDEINBOTTOM) + { + x = 0; + } +} + +void Popup::tick() +{ + if(transition == TRANSITION_SLIDEINBOTTOM) + { + if(moving) + { + if(opened) + y -= clamp(height + (y - height), 1, speed); + else + y += clamp(height - y, 1, speed); + } + if(y != 0 && y != height) + moving = true; + else + moving = false; + } + else + { + moving = false; + if(opened) + { + y = 0; + x = 0; + } + else + { + y = height; + x = width; + } + } +} + +bool Popup::isDoneMoving() +{ + return !moving; +} + +int Popup::getXOffset() +{ + return x; +} + +int Popup::getYOffset() +{ + return y; +} + +void Popup::open() +{ + opened = true; + moving = true; +} + +void Popup::close() +{ + opened = false; + moving = true; +} + //Used to allow replacements of states int FindMugShotStateIndex(FName state) { diff --git a/src/gameconfigfile.cpp b/src/gameconfigfile.cpp index 7db13ed7..e8ad2b91 100644 --- a/src/gameconfigfile.cpp +++ b/src/gameconfigfile.cpp @@ -70,6 +70,8 @@ EXTERN_CVAR (Bool, snd_pitched) EXTERN_CVAR (Color, am_wallcolor) EXTERN_CVAR (Color, am_fdwallcolor) EXTERN_CVAR (Color, am_cdwallcolor) +EXTERN_CVAR (Float, spc_amp) +EXTERN_CVAR (Bool, snd_midiprecache) FString WeaponSection; @@ -297,14 +299,15 @@ void FGameConfigFile::DoGlobalSetup () } if (last < 206) { // spc_amp is now a float, not an int. - FBaseCVar *amp = FindCVar ("spc_amp", NULL); - if (amp != NULL) + if (spc_amp > 16) { - UCVarValue val = amp->GetGenericRep(CVAR_Float); - val.Float /= 16.f; - amp->SetGenericRep(val, CVAR_Float); + spc_amp = spc_amp / 16.f; } } + if (last < 207) + { // Now that snd_midiprecache works again, you probably don't want it on. + snd_midiprecache = false; + } } } } diff --git a/src/m_bbox.cpp b/src/m_bbox.cpp index 3a938ab1..d13f790e 100644 --- a/src/m_bbox.cpp +++ b/src/m_bbox.cpp @@ -40,12 +40,59 @@ void FBoundingBox::AddToBox (fixed_t x, fixed_t y) m_Box[BOXTOP] = y; } +//========================================================================== +// +// FBoundingBox :: BoxOnLineSide +// +// Considers the line to be infinite +// Returns side 0 or 1, -1 if box crosses the line. +// +//========================================================================== + int FBoundingBox::BoxOnLineSide (const line_t *ld) const { - return P_BoxOnLineSide(m_Box, ld); + int p1; + int p2; + + switch (ld->slopetype) + { + case ST_HORIZONTAL: + p1 = m_Box[BOXTOP] > ld->v1->y; + p2 = m_Box[BOXBOTTOM] > ld->v1->y; + if (ld->dx < 0) + { + p1 ^= 1; + p2 ^= 1; + } + break; + + case ST_VERTICAL: + p1 = m_Box[BOXRIGHT] < ld->v1->x; + p2 = m_Box[BOXLEFT] < ld->v1->x; + if (ld->dy < 0) + { + p1 ^= 1; + p2 ^= 1; + } + break; + + case ST_POSITIVE: + p1 = P_PointOnLineSide (m_Box[BOXLEFT], m_Box[BOXTOP], ld); + p2 = P_PointOnLineSide (m_Box[BOXRIGHT], m_Box[BOXBOTTOM], ld); + break; + + case ST_NEGATIVE: + default: // Just to assure GCC that p1 and p2 really do get initialized + p1 = P_PointOnLineSide (m_Box[BOXRIGHT], m_Box[BOXTOP], ld); + p2 = P_PointOnLineSide (m_Box[BOXLEFT], m_Box[BOXBOTTOM], ld); + break; + } + + return (p1 == p2) ? p1 : -1; } + diff --git a/src/mus2midi.h b/src/mus2midi.h index e36da678..79b41007 100644 --- a/src/mus2midi.h +++ b/src/mus2midi.h @@ -71,6 +71,7 @@ typedef struct WORD NumSecondaryChans; WORD NumInstruments; WORD Pad; + // WORD UsedInstruments[NumInstruments]; } MUSHeader; bool ProduceMIDI (const BYTE *musBuf, TArray &outFile); diff --git a/src/oplsynth/music_opl_mididevice.cpp b/src/oplsynth/music_opl_mididevice.cpp index 10b8ff00..bfab365c 100644 --- a/src/oplsynth/music_opl_mididevice.cpp +++ b/src/oplsynth/music_opl_mididevice.cpp @@ -261,9 +261,9 @@ void OPLMIDIDevice::Stop() int OPLMIDIDevice::StreamOutSync(MIDIHDR *header) { - Serialize(); + ChipAccess.Enter(); StreamOut(header); - Unserialize(); + ChipAccess.Leave(); return 0; } diff --git a/src/oplsynth/opl_mus_player.cpp b/src/oplsynth/opl_mus_player.cpp index f8201e03..2e0c5e90 100644 --- a/src/oplsynth/opl_mus_player.cpp +++ b/src/oplsynth/opl_mus_player.cpp @@ -28,59 +28,22 @@ OPLmusicBlock::OPLmusicBlock() TwoChips = !opl_onechip; Looping = false; io = NULL; -#ifdef _WIN32 - InitializeCriticalSection (&ChipAccess); -#else - ChipAccess = SDL_CreateMutex (); - if (ChipAccess == NULL) - { - return; - } -#endif io = new OPLio; } OPLmusicBlock::~OPLmusicBlock() { BlockForStats = NULL; -#ifdef _WIN32 - DeleteCriticalSection (&ChipAccess); -#else - if (ChipAccess != NULL) - { - SDL_DestroyMutex (ChipAccess); - ChipAccess = NULL; - } -#endif delete io; } -void OPLmusicBlock::Serialize() -{ -#ifdef _WIN32 - EnterCriticalSection (&ChipAccess); -#else - if (SDL_mutexP (ChipAccess) != 0) - return; -#endif -} - -void OPLmusicBlock::Unserialize() -{ -#ifdef _WIN32 - LeaveCriticalSection (&ChipAccess); -#else - SDL_mutexV (ChipAccess); -#endif -} - void OPLmusicBlock::ResetChips () { TwoChips = !opl_onechip; - Serialize(); + ChipAccess.Enter(); io->OPLdeinit (); io->OPLinit (TwoChips + 1); - Unserialize(); + ChipAccess.Leave(); } void OPLmusicBlock::Restart() @@ -237,7 +200,7 @@ bool OPLmusicBlock::ServiceStream (void *buff, int numbytes) samples1 = samples; memset(buff, 0, numbytes); - Serialize(); + ChipAccess.Enter(); while (numsamples > 0) { double ticky = NextTickIn; @@ -294,7 +257,7 @@ bool OPLmusicBlock::ServiceStream (void *buff, int numbytes) } } } - Unserialize(); + ChipAccess.Leave(); return res; } diff --git a/src/oplsynth/opl_mus_player.h b/src/oplsynth/opl_mus_player.h index d20e0a96..0540ecb1 100644 --- a/src/oplsynth/opl_mus_player.h +++ b/src/oplsynth/opl_mus_player.h @@ -1,11 +1,4 @@ -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -#define USE_WINDOWS_DWORD -#else -#include -#endif - +#include "critsec.h" #include "muslib.h" #include "files.h" @@ -23,19 +16,12 @@ public: protected: virtual int PlayTick() = 0; - void Serialize(); - void Unserialize(); - double NextTickIn; double SamplesPerTick; bool TwoChips; bool Looping; -#ifdef _WIN32 - CRITICAL_SECTION ChipAccess; -#else - SDL_mutex *ChipAccess; -#endif + FCriticalSection ChipAccess; }; class OPLmusicFile : public OPLmusicBlock diff --git a/src/p_local.h b/src/p_local.h index be538e24..f097f7c3 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -61,7 +61,6 @@ #define BASETHRESHOLD 100 - // // P_PSPR // @@ -161,6 +160,7 @@ typedef struct { fixed_t frac; // along trace line bool isaline; + bool done; union { AActor *thing; line_t *line; @@ -216,7 +216,6 @@ inline void P_MakeDivline (const line_t *li, divline_t *dl) } fixed_t P_InterceptVector (const divline_t *v2, const divline_t *v1); -int P_BoxOnLineSide (const fixed_t *tmbox, const line_t *ld); struct FLineOpening { @@ -256,10 +255,7 @@ public: class FBlockThingsIterator { - typedef TArray BTChecked; - - static TDeletingArray< BTChecked* > FreeBTChecked; - + static TArray CheckArray; int minx, maxx; int miny, maxy; @@ -267,21 +263,24 @@ class FBlockThingsIterator int curx, cury; bool dontfreecheck; - BTChecked *checkarray; + int checkindex; FBlockNode *block; - BTChecked *GetCheckArray(); - void FreeCheckArray(); void StartBlock(int x, int y); + // The following 3 functions are only for use in the path traverser + // and therefore declared private. + static int GetCheckIndex(); + static void SetCheckIndex(int newvalue); + FBlockThingsIterator(int x, int y, int checkindex); + + friend class FPathTraverse; + public: - FBlockThingsIterator(int minx, int miny, int maxx, int maxy, TArray *check = NULL); + FBlockThingsIterator(int minx, int miny, int maxx, int maxy); FBlockThingsIterator(const FBoundingBox &box); - ~FBlockThingsIterator() - { - if (!dontfreecheck) FreeCheckArray(); - } + ~FBlockThingsIterator(); AActor *Next(); void Reset() { StartBlock(minx, miny); } }; @@ -294,20 +293,31 @@ public: AActor *Next(); }; +class FPathTraverse +{ + static TArray intercepts; + + divline_t trace; + unsigned int intercept_index; + unsigned int intercept_count; + fixed_t maxfrac; + unsigned int count; + + void AddLineIntercepts(int bx, int by); + void AddThingIntercepts(int bx, int by, int checkindex); +public: + + intercept_t *Next(); + + FPathTraverse(fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags); + ~FPathTraverse(); + const divline_t &Trace() const { return trace; } +}; + + #define PT_ADDLINES 1 #define PT_ADDTHINGS 2 -extern divline_t trace; - -bool -P_PathTraverse -( fixed_t x1, - fixed_t y1, - fixed_t x2, - fixed_t y2, - int flags, - bool (*trav) (intercept_t *)); - AActor *P_BlockmapSearch (AActor *origin, int distance, AActor *(*func)(AActor *, int)); AActor *P_RoughMonsterSearch (AActor *mo, int distance); @@ -386,9 +396,7 @@ bool P_CheckMissileSpawn (AActor *missile); void P_PlaySpawnSound(AActor *missile, AActor *spawner); // [RH] Position the chasecam -void P_AimCamera (AActor *t1); -extern fixed_t CameraX, CameraY, CameraZ; -extern sector_t *CameraSector; +void P_AimCamera (AActor *t1, fixed_t &x, fixed_t &y, fixed_t &z, sector_t *&sec); // [RH] Means of death void P_RadiusAttack (AActor *spot, AActor *source, int damage, int distance, FName damageType, bool hurtSelf, bool dodamage=true); @@ -434,9 +442,7 @@ extern FBlockNode** blocklinks; // for thing chains // P_INTER // void P_TouchSpecialThing (AActor *special, AActor *toucher); - void P_DamageMobj (AActor *target, AActor *inflictor, AActor *source, int damage, FName mod, int flags=0); - bool P_GiveBody (AActor *actor, int num); void P_PoisonPlayer (player_t *player, AActor *poisoner, AActor *source, int poison); void P_PoisonDamage (player_t *player, AActor *source, int damage, bool playPainSound); diff --git a/src/p_map.cpp b/src/p_map.cpp index a313b650..4e40df89 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -60,16 +60,13 @@ #define WATER_SINK_SPEED (FRACUNIT/2) #define WATER_JUMP_SPEED (FRACUNIT*7/2) -#define USE_PUZZLE_ITEM_SPECIAL 129 - - CVAR (Bool, cl_bloodsplats, true, CVAR_ARCHIVE) CVAR (Int, sv_smartaim, 0, CVAR_ARCHIVE|CVAR_SERVERINFO) static void CheckForPushSpecial (line_t *line, int side, AActor *mobj); static void SpawnShootDecal (AActor *t1, const FTraceResults &trace); static void SpawnDeepSplash (AActor *t1, const FTraceResults &trace, AActor *puff, - fixed_t vx, fixed_t vy, fixed_t vz); + fixed_t vx, fixed_t vy, fixed_t vz, fixed_t shootz); static FRandom pr_tracebleed ("TraceBleed"); static FRandom pr_checkthing ("CheckThing"); @@ -1714,18 +1711,28 @@ bool P_TryMove (AActor *thing, fixed_t x, fixed_t y, // SLIDE MOVE // Allows the player to slide along any angled walls. // -fixed_t bestslidefrac; -fixed_t secondslidefrac; +struct FSlide +{ + fixed_t bestslidefrac; + fixed_t secondslidefrac; -line_t* bestslideline; -line_t* secondslideline; + line_t* bestslideline; + line_t* secondslideline; -AActor* slidemo; + AActor* slidemo; -fixed_t tmxmove; -fixed_t tmymove; + fixed_t tmxmove; + fixed_t tmymove; + + void HitSlideLine(line_t *ld); + void SlideTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy); + void SlideMove (AActor *mo, fixed_t tryx, fixed_t tryy, int numsteps); + + // The bouncing code uses the same data structure + bool BounceTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy); + bool BounceWall (AActor *mo); +}; -extern bool onground; // @@ -1734,7 +1741,7 @@ extern bool onground; // so that the next move will slide along the wall. // If the floor is icy, then you can bounce off a wall. // phares // -void P_HitSlideLine (line_t* ld) +void FSlide::HitSlideLine (line_t* ld) { int side; @@ -1870,79 +1877,89 @@ void P_HitSlideLine (line_t* ld) // // PTR_SlideTraverse // -bool PTR_SlideTraverse (intercept_t* in) +void FSlide::SlideTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy) { - line_t* li; - - if (!in->isaline) - I_Error ("PTR_SlideTraverse: not a line?"); - - li = in->d.line; - - if ( !(li->flags & ML_TWOSIDED) ) + FPathTraverse it(startx, starty, endx, endy, PT_ADDLINES); + intercept_t *in; + + while ((in = it.Next())) { - if (P_PointOnLineSide (slidemo->x, slidemo->y, li)) + line_t* li; + + if (!in->isaline) { - // don't hit the back side - return true; + // should never happen + Printf ("PTR_SlideTraverse: not a line?"); + continue; } - goto isblocking; - } - if (li->flags & (ML_BLOCKING|ML_BLOCKEVERYTHING)) - { - goto isblocking; - } - if (li->flags & ML_BLOCK_PLAYERS && slidemo->player != NULL) - { - goto isblocking; - } - if (li->flags & ML_BLOCKMONSTERS && !(slidemo->flags3 & MF3_NOBLOCKMONST)) - { - goto isblocking; - } - - FLineOpening open; - // set openrange, opentop, openbottom - P_LineOpening (open, slidemo, li, trace.x + FixedMul (trace.dx, in->frac), - trace.y + FixedMul (trace.dy, in->frac)); - - if (open.range < slidemo->height) - goto isblocking; // doesn't fit - - if (open.top - slidemo->z < slidemo->height) - goto isblocking; // mobj is too high - - if (open.bottom - slidemo->z > slidemo->MaxStepHeight) - { - goto isblocking; // too big a step up - } - else if (slidemo->z < open.bottom) - { // [RH] Check to make sure there's nothing in the way for the step up - fixed_t savedz = slidemo->z; - slidemo->z = open.bottom; - bool good = P_TestMobjZ (slidemo); - slidemo->z = savedz; - if (!good) + + li = in->d.line; + + if ( !(li->flags & ML_TWOSIDED) || !li->backsector ) + { + if (P_PointOnLineSide (slidemo->x, slidemo->y, li)) + { + // don't hit the back side + continue; + } + goto isblocking; + } + if (li->flags & (ML_BLOCKING|ML_BLOCKEVERYTHING)) + { + goto isblocking; + } + if (li->flags & ML_BLOCK_PLAYERS && slidemo->player != NULL) + { + goto isblocking; + } + if (li->flags & ML_BLOCKMONSTERS && !(slidemo->flags3 & MF3_NOBLOCKMONST)) { goto isblocking; } - } - // this line doesn't block movement - return true; + FLineOpening open; + // set openrange, opentop, openbottom + P_LineOpening (open, slidemo, li, it.Trace().x + FixedMul (it.Trace().dx, in->frac), + it.Trace().y + FixedMul (it.Trace().dy, in->frac)); - // the line does block movement, - // see if it is closer than best so far - isblocking: - if (in->frac < bestslidefrac) - { - secondslidefrac = bestslidefrac; - secondslideline = bestslideline; - bestslidefrac = in->frac; - bestslideline = li; + if (open.range < slidemo->height) + goto isblocking; // doesn't fit + + if (open.top - slidemo->z < slidemo->height) + goto isblocking; // mobj is too high + + if (open.bottom - slidemo->z > slidemo->MaxStepHeight) + { + goto isblocking; // too big a step up + } + else if (slidemo->z < open.bottom) + { // [RH] Check to make sure there's nothing in the way for the step up + fixed_t savedz = slidemo->z; + slidemo->z = open.bottom; + bool good = P_TestMobjZ (slidemo); + slidemo->z = savedz; + if (!good) + { + goto isblocking; + } + } + + // this line doesn't block movement + continue; + + // the line does block movement, + // see if it is closer than best so far + isblocking: + if (in->frac < bestslidefrac) + { + secondslidefrac = bestslidefrac; + secondslideline = bestslideline; + bestslidefrac = in->frac; + bestslideline = li; + } + + return; // stop } - - return false; // stop } @@ -1956,7 +1973,7 @@ bool PTR_SlideTraverse (intercept_t* in) // // This is a kludgy mess. // -void P_SlideMove (AActor *mo, fixed_t tryx, fixed_t tryy, int numsteps) +void FSlide::SlideMove (AActor *mo, fixed_t tryx, fixed_t tryy, int numsteps) { fixed_t leadx, leady; fixed_t trailx, traily; @@ -1965,8 +1982,8 @@ void P_SlideMove (AActor *mo, fixed_t tryx, fixed_t tryy, int numsteps) const secplane_t * walkplane; int hitcount; - slidemo = mo; hitcount = 3; + slidemo = mo; if (mo->player && mo->reactiontime > 0) return; // player coming right out of a teleporter. @@ -2000,10 +2017,10 @@ void P_SlideMove (AActor *mo, fixed_t tryx, fixed_t tryy, int numsteps) bestslidefrac = FRACUNIT+1; - P_PathTraverse (leadx, leady, leadx+tryx, leady+tryy, PT_ADDLINES, PTR_SlideTraverse); - P_PathTraverse (trailx, leady, trailx+tryx, leady+tryy, PT_ADDLINES, PTR_SlideTraverse); - P_PathTraverse (leadx, traily, leadx+tryx, traily+tryy, PT_ADDLINES, PTR_SlideTraverse); - + SlideTraverse (leadx, leady, leadx+tryx, leady+tryy); + SlideTraverse (trailx, leady, trailx+tryx, leady+tryy); + SlideTraverse (leadx, traily, leadx+tryx, traily+tryy); + // move up to the wall if (bestslidefrac == FRACUNIT+1) { @@ -2043,7 +2060,7 @@ void P_SlideMove (AActor *mo, fixed_t tryx, fixed_t tryy, int numsteps) tryx = tmxmove = FixedMul (tryx, bestslidefrac); tryy = tmymove = FixedMul (tryy, bestslidefrac); - P_HitSlideLine (bestslideline); // clip the moves + HitSlideLine (bestslideline); // clip the moves mo->momx = tmxmove * numsteps; mo->momy = tmymove * numsteps; @@ -2066,6 +2083,12 @@ void P_SlideMove (AActor *mo, fixed_t tryx, fixed_t tryy, int numsteps) } } +void P_SlideMove (AActor *mo, fixed_t tryx, fixed_t tryy, int numsteps) +{ + FSlide slide; + slide.SlideMove(mo, tryx, tryy, numsteps); +} + //============================================================================ // // P_CheckSlopeWalk @@ -2204,51 +2227,62 @@ const secplane_t * P_CheckSlopeWalk (AActor *actor, fixed_t &xmove, fixed_t &ymo // //============================================================================ -bool PTR_BounceTraverse (intercept_t *in) +bool FSlide::BounceTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy) { - line_t *li; + FPathTraverse it(startx, starty, endx, endy, PT_ADDLINES); + intercept_t *in; - if (!in->isaline) - I_Error ("PTR_BounceTraverse: not a line?"); - - li = in->d.line; - assert(((size_t)li - (size_t)lines) % sizeof(line_t) == 0); - if (li->flags & ML_BLOCKEVERYTHING) + while ((in = it.Next())) { - goto bounceblocking; + + line_t *li; + + if (!in->isaline) + { + Printf ("PTR_BounceTraverse: not a line?"); + continue; + } + + li = in->d.line; + assert(((size_t)li - (size_t)lines) % sizeof(line_t) == 0); + if (li->flags & ML_BLOCKEVERYTHING) + { + goto bounceblocking; + } + if (!(li->flags&ML_TWOSIDED) || !li->backsector) + { + if (P_PointOnLineSide (slidemo->x, slidemo->y, li)) + continue; // don't hit the back side + goto bounceblocking; + } + + FLineOpening open; + + P_LineOpening (open, slidemo, li, it.Trace().x + FixedMul (it.Trace().dx, in->frac), + it.Trace().y + FixedMul (it.Trace().dy, in->frac)); // set openrange, opentop, openbottom + if (open.range < slidemo->height) + goto bounceblocking; // doesn't fit + + if (open.top - slidemo->z < slidemo->height) + goto bounceblocking; // mobj is too high + + if (open.bottom > slidemo->z) + goto bounceblocking; // mobj is too low + + continue; // this line doesn't block movement + + // the line does block movement, see if it is closer than best so far + bounceblocking: + if (in->frac < bestslidefrac) + { + secondslidefrac = bestslidefrac; + secondslideline = bestslideline; + bestslidefrac = in->frac; + bestslideline = li; + } + return false; // stop } - if (!(li->flags&ML_TWOSIDED)) - { - if (P_PointOnLineSide (slidemo->x, slidemo->y, li)) - return true; // don't hit the back side - goto bounceblocking; - } - - FLineOpening open; - - P_LineOpening (open, slidemo, li, trace.x + FixedMul (trace.dx, in->frac), - trace.y + FixedMul (trace.dy, in->frac)); // set openrange, opentop, openbottom - if (open.range < slidemo->height) - goto bounceblocking; // doesn't fit - - if (open.top - slidemo->z < slidemo->height) - goto bounceblocking; // mobj is too high - - if (open.bottom > slidemo->z) - goto bounceblocking; // mobj is too low - - return true; // this line doesn't block movement - -// the line does block movement, see if it is closer than best so far -bounceblocking: - if (in->frac < bestslidefrac) - { - secondslidefrac = bestslidefrac; - secondslideline = bestslideline; - bestslidefrac = in->frac; - bestslideline = li; - } - return false; // stop + return true; } //============================================================================ @@ -2257,7 +2291,7 @@ bounceblocking: // //============================================================================ -bool P_BounceWall (AActor *mo) +bool FSlide::BounceWall (AActor *mo) { fixed_t leadx, leady; int side; @@ -2292,8 +2326,7 @@ bool P_BounceWall (AActor *mo) } bestslidefrac = FRACUNIT+1; bestslideline = mo->BlockingLine; - if (P_PathTraverse(leadx, leady, leadx+mo->momx, leady+mo->momy, - PT_ADDLINES, PTR_BounceTraverse) && mo->BlockingLine == NULL) + if (BounceTraverse(leadx, leady, leadx+mo->momx, leady+mo->momy) && mo->BlockingLine == NULL) { // Could not find a wall, so bounce off the floor/ceiling instead. fixed_t floordist = mo->z - mo->floorz; fixed_t ceildist = mo->ceilingz - mo->z; @@ -2340,15 +2373,11 @@ bool P_BounceWall (AActor *mo) movelen = P_AproxDistance (mo->momx, mo->momy); movelen = (movelen * 192) >> 8; // friction - fixed_t box[4]; - box[BOXTOP] = mo->y + mo->radius; - box[BOXBOTTOM] = mo->y - mo->radius; - box[BOXLEFT] = mo->x - mo->radius; - box[BOXRIGHT] = mo->x + mo->radius; - if (P_BoxOnLineSide (box, line) == -1) + FBoundingBox box(mo->x, mo->y, mo->radius); + if (box.BoxOnLineSide (line) == -1) { mo->SetOrigin (mo->x + FixedMul(mo->radius, - finecosine[deltaangle]), mo->y + FixedMul(mo->radius, finesine[deltaangle]), mo->z);; + finecosine[deltaangle]), mo->y + FixedMul(mo->radius, finesine[deltaangle]), mo->z); } if (movelen < FRACUNIT) { @@ -2359,6 +2388,13 @@ bool P_BounceWall (AActor *mo) return true; } +bool P_BounceWall (AActor *mo) +{ + FSlide slide; + return slide.BounceWall(mo); +} + + //============================================================================ // @@ -2366,14 +2402,13 @@ bool P_BounceWall (AActor *mo) // //============================================================================ AActor* linetarget; // who got hit (or NULL) -AActor* shootthing; -fixed_t shootz; // Height if not aiming up or down -fixed_t attackrange; -fixed_t aimpitch; struct aim_t { - + fixed_t aimpitch; + fixed_t attackrange; + fixed_t shootz; // Height if not aiming up or down + AActor* shootthing; fixed_t toppitch, bottompitch; AActor * thing_friend, * thing_other; @@ -2385,6 +2420,9 @@ struct aim_t bool crossedffloors; + void AimTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy); + bool AimTraverse3DFloors(const divline_t &trace, intercept_t * in); + }; aim_t aim; @@ -2394,7 +2432,7 @@ aim_t aim; // AimTraverse3DFloors // //============================================================================ -bool P_AimTraverse3DFloors(intercept_t * in) +bool aim_t::AimTraverse3DFloors(const divline_t &trace, intercept_t * in) { sector_t * nextsector; secplane_t * nexttopplane, * nextbottomplane; @@ -2426,7 +2464,7 @@ bool P_AimTraverse3DFloors(intercept_t * in) for(unsigned k=0;ke->XFloor.ffloors.Size();k++) { - aim.crossedffloors=true; + crossedffloors=true; rover=s->e->XFloor.ffloors[k]; if(!(rover->flags & FF_SOLID) || !(rover->flags & FF_EXISTS)) continue; @@ -2438,26 +2476,26 @@ bool P_AimTraverse3DFloors(intercept_t * in) highpitch = -(int)R_PointToAngle2 (0, shootz, dist, ff_top); lowpitch = -(int)R_PointToAngle2 (0, shootz, dist, ff_bottom); - if (highpitch<=aim.toppitch) + if (highpitch<=toppitch) { // blocks completely - if (lowpitch>=aim.bottompitch) return false; + if (lowpitch>=bottompitch) return false; // blocks upper edge of view - if (lowpitch>aim.toppitch) + if (lowpitch>toppitch) { - aim.toppitch=lowpitch; + toppitch=lowpitch; if (frontflag!=i-1) { nexttopplane=rover->bottom.plane; } } } - else if (lowpitch>=aim.bottompitch) + else if (lowpitch>=bottompitch) { // blocks lower edge of view - if (highpitchtop.plane; @@ -2468,28 +2506,28 @@ bool P_AimTraverse3DFloors(intercept_t * in) if (frontflag==i-1) { - if (s==aim.lastsector) + if (s==lastsector) { // upper slope intersects with this 3d-floor - if (rover->bottom.plane==aim.lastceilingplane && lowpitch > aim.toppitch) + if (rover->bottom.plane==lastceilingplane && lowpitch > toppitch) { - aim.toppitch=lowpitch; + toppitch=lowpitch; } // lower slope intersects with this 3d-floor - if (rover->top.plane==aim.lastfloorplane && highpitch < aim.bottompitch) + if (rover->top.plane==lastfloorplane && highpitch < bottompitch) { - aim.bottompitch=highpitch; + bottompitch=highpitch; } } } - if (aim.toppitch >= aim.bottompitch) return false; // stop + if (toppitch >= bottompitch) return false; // stop } } } - aim.lastsector=nextsector; - aim.lastceilingplane=nexttopplane; - aim.lastfloorplane=nextbottomplane; + lastsector=nextsector; + lastceilingplane=nexttopplane; + lastfloorplane=nextbottomplane; return true; } @@ -2500,170 +2538,173 @@ bool P_AimTraverse3DFloors(intercept_t * in) // //============================================================================ -bool PTR_AimTraverse (intercept_t* in) +void aim_t::AimTraverse (fixed_t startx, fixed_t starty, fixed_t endx, fixed_t endy) { - fixed_t & toppitch=aim.toppitch; - fixed_t & bottompitch=aim.bottompitch; + FPathTraverse it(startx, starty, endx, endy, PT_ADDLINES|PT_ADDTHINGS); + intercept_t *in; - line_t* li; - AActor* th; - fixed_t pitch; - fixed_t thingtoppitch; - fixed_t thingbottompitch; - fixed_t dist; - int thingpitch; - - if (in->isaline) + while ((in = it.Next())) { - li = in->d.line; + line_t* li; + AActor* th; + fixed_t pitch; + fixed_t thingtoppitch; + fixed_t thingbottompitch; + fixed_t dist; + int thingpitch; - if ( !(li->flags & ML_TWOSIDED) || (li->flags & ML_BLOCKEVERYTHING) ) - return false; // stop + if (in->isaline) + { + li = in->d.line; - // Crosses a two sided line. - // A two sided line will restrict the possible target ranges. - FLineOpening open; - P_LineOpening (open, NULL, li, trace.x + FixedMul (trace.dx, in->frac), - trace.y + FixedMul (trace.dy, in->frac)); + if ( !(li->flags & ML_TWOSIDED) || (li->flags & ML_BLOCKEVERYTHING) ) + return; // stop - if (open.bottom >= open.top) - return false; // stop + // Crosses a two sided line. + // A two sided line will restrict the possible target ranges. + FLineOpening open; + P_LineOpening (open, NULL, li, it.Trace().x + FixedMul (it.Trace().dx, in->frac), + it.Trace().y + FixedMul (it.Trace().dy, in->frac)); + if (open.bottom >= open.top) + return; // stop + + dist = FixedMul (attackrange, in->frac); + + pitch = -(int)R_PointToAngle2 (0, shootz, dist, open.bottom); + if (pitch < bottompitch) + bottompitch = pitch; + + pitch = -(int)R_PointToAngle2 (0, shootz, dist, open.top); + if (pitch > toppitch) + toppitch = pitch; + + if (toppitch >= bottompitch) + return; // stop + + if (!AimTraverse3DFloors(it.Trace(), in)) return; + continue; + } + + // shoot a thing + th = in->d.thing; + if (th == shootthing) + continue; // can't shoot self + + if (!(th->flags&MF_SHOOTABLE)) + continue; // corpse or something + + // check for physical attacks on a ghost + if ((th->flags3 & MF3_GHOST) && + shootthing->player && // [RH] Be sure shootthing is a player + shootthing->player->ReadyWeapon && + (shootthing->player->ReadyWeapon->flags2 & MF2_THRUGHOST)) + { + continue; + } + dist = FixedMul (attackrange, in->frac); - pitch = -(int)R_PointToAngle2 (0, shootz, dist, open.bottom); - if (pitch < bottompitch) - bottompitch = pitch; + // we must do one last check whether the trace has crossed a 3D floor + if (lastsector==th->Sector && th->Sector->e->XFloor.ffloors.Size()) + { + if (lastceilingplane) + { + fixed_t ff_top=lastceilingplane->ZatPoint(th->x, th->y); + fixed_t pitch = -(int)R_PointToAngle2 (0, shootz, dist, ff_top); + // upper slope intersects with this 3d-floor + if (pitch > toppitch) + { + toppitch=pitch; + } + } + if (lastfloorplane) + { + fixed_t ff_bottom=lastfloorplane->ZatPoint(th->x, th->y); + fixed_t pitch = -(int)R_PointToAngle2 (0, shootz, dist, ff_bottom); + // lower slope intersects with this 3d-floor + if (pitch < bottompitch) + { + bottompitch=pitch; + } + } + } - pitch = -(int)R_PointToAngle2 (0, shootz, dist, open.top); - if (pitch > toppitch) - toppitch = pitch; + // check angles to see if the thing can be aimed at - if (toppitch >= bottompitch) - return false; // stop - - return P_AimTraverse3DFloors(in); - } + thingtoppitch = -(int)R_PointToAngle2 (0, shootz, dist, th->z + th->height); - // shoot a thing - th = in->d.thing; - if (th == shootthing) - return true; // can't shoot self + if (thingtoppitch > bottompitch) + continue; // shot over the thing - if (!(th->flags&MF_SHOOTABLE)) - return true; // corpse or something + thingbottompitch = -(int)R_PointToAngle2 (0, shootz, dist, th->z); - // check for physical attacks on a ghost - if ((th->flags3 & MF3_GHOST) && - shootthing->player && // [RH] Be sure shootthing is a player - shootthing->player->ReadyWeapon && - (shootthing->player->ReadyWeapon->flags2 & MF2_THRUGHOST)) - { - return true; - } + if (thingbottompitch < toppitch) + continue; // shot under the thing - dist = FixedMul (attackrange, in->frac); - - // we must do one last check whether the trace has crossed a 3D floor - if (aim.lastsector==th->Sector && th->Sector->e->XFloor.ffloors.Size()) - { - if (aim.lastceilingplane) + if (crossedffloors) { - fixed_t ff_top=aim.lastceilingplane->ZatPoint(th->x, th->y); - fixed_t pitch = -(int)R_PointToAngle2 (0, shootz, dist, ff_top); - // upper slope intersects with this 3d-floor - if (pitch > toppitch) + // if 3D floors were in the way do an extra visibility check for safety + if (!P_CheckSight(shootthing, th, 1)) { - toppitch=pitch; + // the thing can't be seen so we can safely exclude its range from our aiming field + if (thingtoppitchtoppitch) toppitch=thingbottompitch; + } + else if (thingbottompitch>bottompitch) + { + if (thingtoppitchZatPoint(th->x, th->y); - fixed_t pitch = -(int)R_PointToAngle2 (0, shootz, dist, ff_bottom); - // lower slope intersects with this 3d-floor - if (pitch < bottompitch) - { - bottompitch=pitch; - } - } - } + // this thing can be hit! + if (thingtoppitch < toppitch) + thingtoppitch = toppitch; - // check angles to see if the thing can be aimed at - - thingtoppitch = -(int)R_PointToAngle2 (0, shootz, dist, th->z + th->height); - - if (thingtoppitch > bottompitch) - return true; // shot over the thing - - thingbottompitch = -(int)R_PointToAngle2 (0, shootz, dist, th->z); - - if (thingbottompitch < toppitch) - return true; // shot under the thing + if (thingbottompitch > bottompitch) + thingbottompitch = bottompitch; - if (aim.crossedffloors) - { - // if 3D floors were in the way do an extra visibility check for safety - if (!P_CheckSight(shootthing, th, 1)) - { - // the thing can't be seen so we can safely exclude its range from our aiming field - if (thingtoppitchtoppitch) toppitch=thingbottompitch; - } - else if (thingbottompitch>bottompitch) - { - if (thingtoppitch bottompitch) - thingbottompitch = bottompitch; - - thingpitch = thingtoppitch/2 + thingbottompitch/2; - - if (sv_smartaim && !aim.notsmart) - { - // try to be a little smarter about what to aim at! - // In particular avoid autoaiming at friends amd barrels. - if (th->IsFriend(shootthing)) + if (sv_smartaim && !notsmart) { - if (sv_smartaim < 2) + // try to be a little smarter about what to aim at! + // In particular avoid autoaiming at friends amd barrels. + if (th->IsFriend(shootthing)) { - // friends don't aim at friends (except players), at least not first - aim.thing_friend=th; - aim.pitch_friend=thingpitch; + if (sv_smartaim < 2) + { + // friends don't aim at friends (except players), at least not first + thing_friend=th; + pitch_friend=thingpitch; + } } - } - else if (!(th->flags3&MF3_ISMONSTER) ) - { - if (sv_smartaim < 3) + else if (!(th->flags3&MF3_ISMONSTER) ) { - // don't autoaim at barrels and other shootable stuff unless no monsters have been found - aim.thing_other=th; - aim.pitch_other=thingpitch; + if (sv_smartaim < 3) + { + // don't autoaim at barrels and other shootable stuff unless no monsters have been found + thing_other=th; + pitch_other=thingpitch; + } + } + else + { + linetarget=th; + aimpitch=thingpitch; + return; } } else { linetarget=th; aimpitch=thingpitch; - return false; + return; } } - else - { - linetarget=th; - aimpitch=thingpitch; - return false; - } - return true; } //============================================================================ @@ -2675,20 +2716,21 @@ fixed_t P_AimLineAttack (AActor *t1, angle_t angle, fixed_t distance, fixed_t vr { fixed_t x2; fixed_t y2; + aim_t aim; angle >>= ANGLETOFINESHIFT; - shootthing = t1; + aim.shootthing = t1; x2 = t1->x + (distance>>FRACBITS)*finecosine[angle]; y2 = t1->y + (distance>>FRACBITS)*finesine[angle]; - shootz = t1->z + (t1->height>>1) - t1->floorclip; + aim.shootz = t1->z + (t1->height>>1) - t1->floorclip; if (t1->player != NULL) { - shootz += FixedMul (t1->player->mo->AttackZOffset, t1->player->crouchfactor); + aim.shootz += FixedMul (t1->player->mo->AttackZOffset, t1->player->crouchfactor); } else { - shootz += 8*FRACUNIT; + aim.shootz += 8*FRACUNIT; } // can't shoot outside view angles @@ -2711,14 +2753,14 @@ fixed_t P_AimLineAttack (AActor *t1, angle_t angle, fixed_t distance, fixed_t vr aim.bottompitch = t1->pitch + vrange; aim.notsmart = forcenosmart; - attackrange = distance; + aim.attackrange = distance; linetarget = NULL; // for smart aiming aim.thing_friend=aim.thing_other=NULL; // Information for tracking crossed 3D floors - aimpitch=t1->pitch; + aim.aimpitch=t1->pitch; aim.crossedffloors=t1->Sector->e->XFloor.ffloors.Size()!=0; aim.lastsector=t1->Sector; aim.lastfloorplane=aim.lastceilingplane=NULL; @@ -2735,22 +2777,22 @@ fixed_t P_AimLineAttack (AActor *t1, angle_t angle, fixed_t distance, fixed_t vr if (bottomz<=t1->z) aim.lastfloorplane=rover->top.plane; } - P_PathTraverse (t1->x, t1->y, x2, y2, PT_ADDLINES|PT_ADDTHINGS, PTR_AimTraverse); + aim.AimTraverse (t1->x, t1->y, x2, y2); if (!linetarget) { if (aim.thing_other) { linetarget=aim.thing_other; - aimpitch=aim.pitch_other; + aim.aimpitch=aim.pitch_other; } else if (aim.thing_friend) { linetarget=aim.thing_friend; - aimpitch=aim.pitch_friend; + aim.aimpitch=aim.pitch_friend; } } - return linetarget ? aimpitch : t1->pitch; + return linetarget ? aim.aimpitch : t1->pitch; } @@ -2826,8 +2868,6 @@ AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, { shootz += 8*FRACUNIT; } - attackrange = distance; - aimpitch = pitch; hitGhosts = (t1->player != NULL && t1->player->ReadyWeapon != NULL && @@ -2977,7 +3017,7 @@ AActor *P_LineAttack (AActor *t1, angle_t angle, fixed_t distance, puff = P_SpawnPuff (pufftype, hitx, hity, hitz, angle - ANG180, 2, flags|PF_HITTHING|PF_TEMPORARY); killPuff = true; } - SpawnDeepSplash (t1, trace, puff, vx, vy, vz); + SpawnDeepSplash (t1, trace, puff, vx, vy, vz, shootz); } } if (killPuff && puff != NULL) @@ -3165,6 +3205,7 @@ void P_RailAttack (AActor *source, int damage, int offset, int color1, int color fixed_t x1, y1; FVector3 start, end; FTraceResults trace; + fixed_t shootz; pitch = (angle_t)(-source->pitch) >> ANGLETOFINESHIFT; angle = source->angle >> ANGLETOFINESHIFT; @@ -3243,7 +3284,7 @@ void P_RailAttack (AActor *source, int damage, int offset, int color1, int color AActor *thepuff = Spawn ("BulletPuff", 0, 0, 0, ALLOW_REPLACE); if (thepuff != NULL) { - SpawnDeepSplash (source, trace, thepuff, vx, vy, vz); + SpawnDeepSplash (source, trace, thepuff, vx, vy, vz, shootz); thepuff->Destroy (); } } @@ -3286,10 +3327,8 @@ void P_RailAttack (AActor *source, int damage, int offset, int color1, int color // CVAR (Float, chase_height, -8.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) CVAR (Float, chase_dist, 90.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) -fixed_t CameraX, CameraY, CameraZ; -sector_t *CameraSector; -void P_AimCamera (AActor *t1) +void P_AimCamera (AActor *t1, fixed_t &CameraX, fixed_t &CameraY, fixed_t &CameraZ, sector_t *&CameraSector) { fixed_t distance = (fixed_t)(chase_dist * FRACUNIT); angle_t angle = (t1->angle - ANG180) >> ANGLETOFINESHIFT; @@ -3326,123 +3365,130 @@ void P_AimCamera (AActor *t1) // // USE LINES // -AActor *usething; -bool foundline; -bool PTR_UseTraverse (intercept_t *in) +bool P_UseTraverse(AActor *usething, fixed_t endx, fixed_t endy, bool &foundline) { - // [RH] Check for things to talk with or use a puzzle item on - if (!in->isaline) - { - if (usething==in->d.thing) return true; - // Check thing + FPathTraverse it(usething->x, usething->y, endx, endy, PT_ADDLINES|PT_ADDTHINGS); + intercept_t *in; - // Check for puzzle item use or USESPECIAL flag - if (in->d.thing->flags5 & MF5_USESPECIAL || in->d.thing->special == USE_PUZZLE_ITEM_SPECIAL) - { - if (LineSpecials[in->d.thing->special] (NULL, usething, false, - in->d.thing->args[0], in->d.thing->args[1], in->d.thing->args[2], - in->d.thing->args[3], in->d.thing->args[4])) - return false; - } - // Dead things can't talk. - if (in->d.thing->health <= 0) - { - return true; - } - // Fighting things don't talk either. - if (in->d.thing->flags4 & MF4_INCOMBAT) - { - return true; - } - if (in->d.thing->Conversation != NULL) - { - // Give the NPC a chance to play a brief animation - in->d.thing->ConversationAnimation (0); - P_StartConversation (in->d.thing, usething, true, true); - return false; - } - return true; - } - - FLineOpening open; - // [RH] The range passed to P_PathTraverse was doubled so that it could - // find things up to 128 units away (for Strife), but it should still reject - // lines further than 64 units away. - if (in->frac > FRACUNIT/2) + while ((in = it.Next())) { - // don't pass usething here. It will not do what might be expected! - P_LineOpening (open, NULL, in->d.line, trace.x + FixedMul (trace.dx, in->frac), - trace.y + FixedMul (trace.dy, in->frac)); - return open.range>0; - } - - if (in->d.line->special == 0 || (GET_SPAC(in->d.line->flags) != SPAC_USETHROUGH && - GET_SPAC(in->d.line->flags) != SPAC_USE)) - { -blocked: - if (in->d.line->flags & ML_BLOCKEVERYTHING) + // [RH] Check for things to talk with or use a puzzle item on + if (!in->isaline) { - open.range = 0; + if (usething==in->d.thing) continue; + // Check thing + + // Check for puzzle item use or USESPECIAL flag + if (in->d.thing->flags5 & MF5_USESPECIAL || in->d.thing->special == UsePuzzleItem) + { + if (LineSpecials[in->d.thing->special] (NULL, usething, false, + in->d.thing->args[0], in->d.thing->args[1], in->d.thing->args[2], + in->d.thing->args[3], in->d.thing->args[4])) + return true; + } + // Dead things can't talk. + if (in->d.thing->health <= 0) + { + continue; + } + // Fighting things don't talk either. + if (in->d.thing->flags4 & MF4_INCOMBAT) + { + continue; + } + if (in->d.thing->Conversation != NULL) + { + // Give the NPC a chance to play a brief animation + in->d.thing->ConversationAnimation (0); + P_StartConversation (in->d.thing, usething, true, true); + return true; + } + continue; + } + + FLineOpening open; + // [RH] The range passed to P_PathTraverse was doubled so that it could + // find things up to 128 units away (for Strife), but it should still reject + // lines further than 64 units away. + if (in->frac > FRACUNIT/2) + { + // don't pass usething here. It will not do what might be expected! + P_LineOpening (open, NULL, in->d.line, it.Trace().x + FixedMul (it.Trace().dx, in->frac), + it.Trace().y + FixedMul (it.Trace().dy, in->frac)); + if (open.range <= 0) return false; + else continue; + } + if (in->d.line->special == 0 || (GET_SPAC(in->d.line->flags) != SPAC_USETHROUGH && + GET_SPAC(in->d.line->flags) != SPAC_USE)) + { + blocked: + if (in->d.line->flags & ML_BLOCKEVERYTHING) + { + open.range = 0; + } + else + { + P_LineOpening (open, NULL, in->d.line, it.Trace().x + FixedMul (it.Trace().dx, in->frac), + it.Trace().y + FixedMul (it.Trace().dy, in->frac)); + } + if (open.range <= 0 || + (in->d.line->special != 0 && (i_compatflags & COMPATF_USEBLOCKING))) + { + // [RH] Give sector a chance to intercept the use + + sector_t * sec; + + sec = usething->Sector; + + if (sec->SecActTarget && sec->SecActTarget->TriggerAction (usething, SECSPAC_Use)) + { + return true; + } + + sec = P_PointOnLineSide(usething->x, usething->y, in->d.line) == 0? + in->d.line->frontsector : in->d.line->backsector; + + if (sec != NULL && sec->SecActTarget && + sec->SecActTarget->TriggerAction (usething, SECSPAC_UseWall)) + { + return true; + } + + if (usething->player) + { + S_Sound (usething, CHAN_VOICE, "*usefail", 1, ATTN_IDLE); + } + return true; // can't use through a wall + } + foundline = true; + continue; // not a special line, but keep checking + } + + if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1) + // [RH] continue traversal for two-sided lines + //return in->d.line->backsector != NULL; // don't use back side + goto blocked; // do a proper check for back sides of triggers + + P_ActivateLine (in->d.line, usething, 0, SPAC_USE); + + //WAS can't use more than one special line in a row + //jff 3/21/98 NOW multiple use allowed with enabling line flag + //[RH] And now I've changed it again. If the line is of type + // SPAC_USE, then it eats the use. Everything else passes + // it through, including SPAC_USETHROUGH. + if (i_compatflags & COMPATF_USEBLOCKING) + { + if (GET_SPAC(in->d.line->flags) == SPAC_USETHROUGH) continue; + else return true; } else { - P_LineOpening (open, NULL, in->d.line, trace.x + FixedMul (trace.dx, in->frac), - trace.y + FixedMul (trace.dy, in->frac)); + if (GET_SPAC(in->d.line->flags) != SPAC_USE) continue; + else return true; } - if (open.range <= 0 || - (in->d.line->special != 0 && (i_compatflags & COMPATF_USEBLOCKING))) - { - // [RH] Give sector a chance to intercept the use - - sector_t * sec; - - sec = usething->Sector; - - if (sec->SecActTarget && sec->SecActTarget->TriggerAction (usething, SECSPAC_Use)) - { - return false; - } - - sec = P_PointOnLineSide(usething->x, usething->y, in->d.line) == 0? - in->d.line->frontsector : in->d.line->backsector; - - if (sec != NULL && sec->SecActTarget && - sec->SecActTarget->TriggerAction (usething, SECSPAC_UseWall)) - { - return false; - } - - if (usething->player) - { - S_Sound (usething, CHAN_VOICE, "*usefail", 1, ATTN_IDLE); - } - return false; // can't use through a wall - } - foundline = true; - return true; // not a special line, but keep checking - } - - if (P_PointOnLineSide (usething->x, usething->y, in->d.line) == 1) - // [RH] continue traversal for two-sided lines - //return in->d.line->backsector != NULL; // don't use back side - goto blocked; // do a proper check for back sides of triggers - - P_ActivateLine (in->d.line, usething, 0, SPAC_USE); - - //WAS can't use more than one special line in a row - //jff 3/21/98 NOW multiple use allowed with enabling line flag - //[RH] And now I've changed it again. If the line is of type - // SPAC_USE, then it eats the use. Everything else passes - // it through, including SPAC_USETHROUGH. - if (i_compatflags & COMPATF_USEBLOCKING) - { - return GET_SPAC(in->d.line->flags) == SPAC_USETHROUGH; - } - else - { - return GET_SPAC(in->d.line->flags) != SPAC_USE; } + return false; } // Returns false if a "oof" sound should be made because of a blocking @@ -3455,19 +3501,27 @@ blocked: // by Lee Killough // -bool PTR_NoWayTraverse (intercept_t *in) +bool P_NoWayTraverse (AActor *usething, fixed_t endx, fixed_t endy) { - line_t *ld = in->d.line; - FLineOpening open; + FPathTraverse it(usething->x, usething->y, endx, endy, PT_ADDLINES); + intercept_t *in; - // [GrafZahl] de-obfuscated. Was I the only one who was unable to makes sense out of - // this convoluted mess? - if (ld->special) return true; - if (ld->flags&(ML_BLOCKING|ML_BLOCKEVERYTHING|ML_BLOCK_PLAYERS)) return false; - P_LineOpening(open, NULL, ld, trace.x+FixedMul(trace.dx, in->frac),trace.y+FixedMul(trace.dy, in->frac)); - return open.range >0 && - open.bottom <= usething->z + usething->MaxStepHeight && - open.top >= usething->z + usething->height; + while ((in = it.Next())) + { + line_t *ld = in->d.line; + FLineOpening open; + + // [GrafZahl] de-obfuscated. Was I the only one who was unable to makes sense out of + // this convoluted mess? + if (ld->special) continue; + if (ld->flags&(ML_BLOCKING|ML_BLOCKEVERYTHING|ML_BLOCK_PLAYERS)) return true; + P_LineOpening(open, NULL, ld, it.Trace().x+FixedMul(it.Trace().dx, in->frac), + it.Trace().y+FixedMul(it.Trace().dy, in->frac)); + if (open.range <= 0 || + open.bottom > usething->z + usething->MaxStepHeight || + open.top < usething->z + usething->height) return true; + } + return false; } /* @@ -3482,16 +3536,14 @@ bool PTR_NoWayTraverse (intercept_t *in) void P_UseLines (player_t *player) { angle_t angle; - fixed_t x1, y1, x2, y2; + fixed_t x2, y2; + bool foundline; - usething = player->mo; foundline = false; angle = player->mo->angle >> ANGLETOFINESHIFT; - x1 = player->mo->x; - y1 = player->mo->y; - x2 = x1 + (USERANGE>>FRACBITS)*finecosine[angle]*2; - y2 = y1 + (USERANGE>>FRACBITS)*finesine[angle]*2; + x2 = player->mo->x + (USERANGE>>FRACBITS)*finecosine[angle]*2; + y2 = player->mo->y + (USERANGE>>FRACBITS)*finesine[angle]*2; // old code: // @@ -3499,80 +3551,19 @@ void P_UseLines (player_t *player) // // This added test makes the "oof" sound work on 2s lines -- killough: - if (P_PathTraverse (x1, y1, x2, y2, PT_ADDLINES|PT_ADDTHINGS, PTR_UseTraverse)) + if (!P_UseTraverse (player->mo, x2, y2, foundline)) { // [RH] Give sector a chance to eat the use - sector_t *sec = usething->Sector; + sector_t *sec = player->mo->Sector; int spac = SECSPAC_Use; - if (foundline) - spac |= SECSPAC_UseWall; - if ((!sec->SecActTarget || - !sec->SecActTarget->TriggerAction (usething, spac)) && - !P_PathTraverse (x1, y1, x2, y2, PT_ADDLINES, PTR_NoWayTraverse)) + if (foundline) spac |= SECSPAC_UseWall; + if ((!sec->SecActTarget || !sec->SecActTarget->TriggerAction (player->mo, spac)) && + P_NoWayTraverse (player->mo, x2, y2)) { - S_Sound (usething, CHAN_VOICE, "*usefail", 1, ATTN_IDLE); + S_Sound (player->mo, CHAN_VOICE, "*usefail", 1, ATTN_IDLE); } } } -//========================================================================== -// -// PTR_PuzzleItemTraverse -// -//========================================================================== - -static AActor *PuzzleItemUser; -static int PuzzleItemType; -static bool PuzzleActivated; - -bool PTR_PuzzleItemTraverse (intercept_t *in) -{ - AActor *mobj; - FLineOpening open; - - if (in->isaline) - { // Check line - if (in->d.line->special != USE_PUZZLE_ITEM_SPECIAL) - { - P_LineOpening (open, NULL, in->d.line, trace.x + FixedMul (trace.dx, in->frac), - trace.y + FixedMul (trace.dy, in->frac)); - if (open.range <= 0) - { - return false; // can't use through a wall - } - return true; // Continue searching - } - if (P_PointOnLineSide (PuzzleItemUser->x, PuzzleItemUser->y, - in->d.line) == 1) - { // Don't use back sides - return false; - } - if (PuzzleItemType != in->d.line->args[0]) - { // Item type doesn't match - return false; - } - P_StartScript (PuzzleItemUser, in->d.line, in->d.line->args[1], NULL, 0, - in->d.line->args[2], in->d.line->args[3], in->d.line->args[4], true, false); - in->d.line->special = 0; - PuzzleActivated = true; - return false; // Stop searching - } - // Check thing - mobj = in->d.thing; - if (mobj->special != USE_PUZZLE_ITEM_SPECIAL) - { // Wrong special - return true; - } - if (PuzzleItemType != mobj->args[0]) - { // Item type doesn't match - return true; - } - P_StartScript (PuzzleItemUser, NULL, mobj->args[1], NULL, 0, - mobj->args[2], mobj->args[3], mobj->args[4], true, false); - mobj->special = 0; - PuzzleActivated = true; - return false; // Stop searching -} - //========================================================================== // // P_UsePuzzleItem @@ -3581,36 +3572,72 @@ bool PTR_PuzzleItemTraverse (intercept_t *in) // //========================================================================== -bool P_UsePuzzleItem (AActor *actor, int itemType) +bool P_UsePuzzleItem (AActor *PuzzleItemUser, int PuzzleItemType) { int angle; fixed_t x1, y1, x2, y2; - PuzzleItemType = itemType; - PuzzleItemUser = actor; - PuzzleActivated = false; - angle = actor->angle>>ANGLETOFINESHIFT; - x1 = actor->x; - y1 = actor->y; + angle = PuzzleItemUser->angle>>ANGLETOFINESHIFT; + x1 = PuzzleItemUser->x; + y1 = PuzzleItemUser->y; x2 = x1+(USERANGE>>FRACBITS)*finecosine[angle]; y2 = y1+(USERANGE>>FRACBITS)*finesine[angle]; - P_PathTraverse (x1, y1, x2, y2, PT_ADDLINES|PT_ADDTHINGS, - PTR_PuzzleItemTraverse); - return PuzzleActivated; + + FPathTraverse it(x1, y1, x2, y2, PT_ADDLINES|PT_ADDTHINGS); + intercept_t *in; + + while ((in = it.Next())) + { + AActor *mobj; + FLineOpening open; + + if (in->isaline) + { // Check line + if (in->d.line->special != UsePuzzleItem) + { + P_LineOpening (open, NULL, in->d.line, it.Trace().x + FixedMul (it.Trace().dx, in->frac), + it.Trace().y + FixedMul (it.Trace().dy, in->frac)); + if (open.range <= 0) + { + return false; // can't use through a wall + } + continue; + } + if (P_PointOnLineSide (PuzzleItemUser->x, PuzzleItemUser->y, in->d.line) == 1) + { // Don't use back sides + return false; + } + if (PuzzleItemType != in->d.line->args[0]) + { // Item type doesn't match + return false; + } + P_StartScript (PuzzleItemUser, in->d.line, in->d.line->args[1], NULL, 0, + in->d.line->args[2], in->d.line->args[3], in->d.line->args[4], true, false); + in->d.line->special = 0; + return true; + } + // Check thing + mobj = in->d.thing; + if (mobj->special != UsePuzzleItem) + { // Wrong special + continue; + } + if (PuzzleItemType != mobj->args[0]) + { // Item type doesn't match + continue; + } + P_StartScript (PuzzleItemUser, NULL, mobj->args[1], NULL, 0, + mobj->args[2], mobj->args[3], mobj->args[4], true, false); + mobj->special = 0; + return true; + } + return false; } // // RADIUS ATTACK // -//============================================================================= -// -// PIT_RadiusAttack -// -// "bombsource" is the creature that caused the explosion at "bombspot". -// [RH] Now it knows about vertical distances and can thrust things vertically. -//============================================================================= - // [RH] Damage scale to apply to thing that shot the missile. static float selfthrustscale; @@ -4737,7 +4764,7 @@ void SpawnShootDecal (AActor *t1, const FTraceResults &trace) } static void SpawnDeepSplash (AActor *t1, const FTraceResults &trace, AActor *puff, - fixed_t vx, fixed_t vy, fixed_t vz) + fixed_t vx, fixed_t vy, fixed_t vz, fixed_t shootz) { if (!trace.CrossedWater->heightsec) return; diff --git a/src/p_maputl.cpp b/src/p_maputl.cpp index d4a73488..81c7ba02 100644 --- a/src/p_maputl.cpp +++ b/src/p_maputl.cpp @@ -59,57 +59,6 @@ fixed_t P_AproxDistance (fixed_t dx, fixed_t dy) return (dx < dy) ? dx+dy-(dx>>1) : dx+dy-(dy>>1); } -//========================================================================== -// -// P_BoxOnLineSide -// -// Considers the line to be infinite -// Returns side 0 or 1, -1 if box crosses the line. -// -//========================================================================== - -int P_BoxOnLineSide (const fixed_t *tmbox, const line_t *ld) -{ - int p1; - int p2; - - switch (ld->slopetype) - { - case ST_HORIZONTAL: - p1 = tmbox[BOXTOP] > ld->v1->y; - p2 = tmbox[BOXBOTTOM] > ld->v1->y; - if (ld->dx < 0) - { - p1 ^= 1; - p2 ^= 1; - } - break; - - case ST_VERTICAL: - p1 = tmbox[BOXRIGHT] < ld->v1->x; - p2 = tmbox[BOXLEFT] < ld->v1->x; - if (ld->dy < 0) - { - p1 ^= 1; - p2 ^= 1; - } - break; - - case ST_POSITIVE: - p1 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXTOP], ld); - p2 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld); - break; - - case ST_NEGATIVE: - default: // Just to assure GCC that p1 and p2 really do get initialized - p1 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXTOP], ld); - p2 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld); - break; - } - - return (p1 == p2) ? p1 : -1; -} - //========================================================================== // // P_InterceptVector @@ -270,6 +219,7 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef, // THING POSITION SETTING // +//========================================================================== // // P_UnsetThingPosition // Unlinks a thing from block map and sectors. @@ -277,6 +227,8 @@ void P_LineOpening (FLineOpening &open, AActor *actor, const line_t *linedef, // lookups maintaining lists of things inside // these structures need to be updated. // +//========================================================================== + void AActor::UnlinkFromWorld () { sector_list = NULL; @@ -332,11 +284,14 @@ void AActor::UnlinkFromWorld () } +//========================================================================== // // P_SetThingPosition // Links a thing into both a block and a subsector based on it's x y. // Sets thing->sector properly // +//========================================================================== + void AActor::LinkToWorld (bool buggy) { // link into subsector @@ -440,12 +395,15 @@ void AActor::LinkToWorld (sector_t *sec) } } +//========================================================================== // // [RH] LinkToWorldForMapThing // // Emulate buggy PointOnLineSide and fix actors that lie on // lines to compensate for some IWAD maps. // +//========================================================================== + static int R_PointOnSideSlow (fixed_t x, fixed_t y, node_t *node) { // [RH] This might have been faster than two multiplies and an @@ -549,7 +507,6 @@ sector_t *AActor::LinkToWorldForMapThing () } } -#if 1 // Not inside the line's bounding box if (x + radius <= ldef->bbox[BOXLEFT] || x - radius >= ldef->bbox[BOXRIGHT] @@ -593,39 +550,6 @@ sector_t *AActor::LinkToWorldForMapThing () y += FixedMul(distance, finesine[finean]); return P_PointInSector (x, y); } -#else - if (DMulScale32 (y - ldef->v1->y, ldef->dx, ldef->v1->x - x, ldef->dy) == 0) - { - // It touches the infinite line; now make sure it touches the linedef - SQWORD num, den; - - den = (SQWORD)ldef->dx*ldef->dx + (SQWORD)ldef->dy*ldef->dy; - if (den != 0) - { - num = (SQWORD)(x-ldef->v1->x)*ldef->dx+(SQWORD)(y-ldef->v1->y)*ldef->dy; - if (num >= 0 && num <= den) - { - DPrintf ("%s at (%d,%d) lies directly on %s line %d\n", - this->GetClass()->TypeName.GetChars(), x>>FRACBITS, y>>FRACBITS, - ldef->dx == 0? "vertical" : ldef->dy == 0? "horizontal" : "diagonal", - ldef-lines); - angle_t finean = R_PointToAngle2 (0, 0, ldef->dx, ldef->dy); - if (ldef->backsector != NULL && ldef->backsector == ssec->sector) - { - finean += ANGLE_90; - } - else - { - finean -= ANGLE_90; - } - finean >>= ANGLETOFINESHIFT; - x += finecosine[finean]) >> 2; - y += finesine[finean]) >> 2; - break; - } - } - } -#endif } } } @@ -815,34 +739,20 @@ line_t *FBlockLinesIterator::Next() //=========================================================================== // -// FBlockThingsIterator :: GetCheckArray +// FBlockThingsIterator :: CheckArray // //=========================================================================== -TDeletingArray< FBlockThingsIterator::BTChecked* > FBlockThingsIterator::FreeBTChecked; +TArray FBlockThingsIterator::CheckArray(32); -FBlockThingsIterator::BTChecked *FBlockThingsIterator::GetCheckArray() +int FBlockThingsIterator::GetCheckIndex() { - dontfreecheck = false; - if (FreeBTChecked.Size() != 0) - { - BTChecked *ret; - FreeBTChecked.Pop(ret); - ret->Clear(); - return ret; - } - return new BTChecked(); + return CheckArray.Size(); } -//=========================================================================== -// -// FBlockThingsIterator :: FreeCheckArray -// -//=========================================================================== - -void FBlockThingsIterator::FreeCheckArray() +void FBlockThingsIterator::SetCheckIndex(int newvalue) { - FreeBTChecked.Push(checkarray); + CheckArray.Resize(newvalue); } //=========================================================================== @@ -851,17 +761,19 @@ void FBlockThingsIterator::FreeCheckArray() // //=========================================================================== -FBlockThingsIterator::FBlockThingsIterator(int _minx, int _miny, int _maxx, int _maxy, TArray *Check) +FBlockThingsIterator::FBlockThingsIterator(int x, int y, int check) { - if (Check != NULL) - { - checkarray = Check; - dontfreecheck = true; - } - else - { - checkarray = GetCheckArray(); - } + checkindex = check; + dontfreecheck = true; + minx = maxx = x; + miny = maxy = y; + Reset(); +} + +FBlockThingsIterator::FBlockThingsIterator(int _minx, int _miny, int _maxx, int _maxy) +{ + checkindex = CheckArray.Size(); + dontfreecheck = false; minx = _minx; maxx = _maxx; miny = _miny; @@ -871,7 +783,8 @@ FBlockThingsIterator::FBlockThingsIterator(int _minx, int _miny, int _maxx, int FBlockThingsIterator::FBlockThingsIterator(const FBoundingBox &box) { - checkarray = GetCheckArray(); + checkindex = CheckArray.Size(); + dontfreecheck = false; maxy = (box.Top() - bmaporgy) >> MAPBLOCKSHIFT; miny = (box.Bottom() - bmaporgy) >> MAPBLOCKSHIFT; maxx = (box.Right() - bmaporgx) >> MAPBLOCKSHIFT; @@ -879,6 +792,17 @@ FBlockThingsIterator::FBlockThingsIterator(const FBoundingBox &box) Reset(); } +//=========================================================================== +// +// FBlockThingsIterator :: FreeCheckArray +// +//=========================================================================== + +FBlockThingsIterator::~FBlockThingsIterator() +{ + if (!dontfreecheck) CheckArray.Resize(checkindex); +} + //=========================================================================== // // FBlockThingsIterator :: StartBlock @@ -917,16 +841,16 @@ AActor *FBlockThingsIterator::Next() block = block->NextActor; // Don't recheck things that were already checked - for (i = (int)checkarray->Size() - 1; i >= 0; --i) + for (i = (int)CheckArray.Size() - 1; i >= checkindex; --i) { - if ((*checkarray)[i] == me) + if (CheckArray[i] == me) { break; } } - if (i < 0) + if (i < checkindex) { - checkarray->Push (me); + CheckArray.Push (me); return me; } } @@ -974,16 +898,18 @@ AActor *FRadiusThingsIterator::Next() } +//=========================================================================== // -// INTERCEPT ROUTINES +// FPathTraverse :: Intercepts // -TArray intercepts (128); +//=========================================================================== -divline_t trace; -int ptflags; +TArray FPathTraverse::intercepts(128); + +//=========================================================================== // -// PIT_AddLineIntercepts. +// FPathTraverse :: AddLineIntercepts. // Looks for lines in the given block // that intercept the given trace // to add to the intercepts list. @@ -991,7 +917,9 @@ int ptflags; // A line is crossed if its endpoints // are on opposite sides of the trace. // -void P_AddLineIntercepts(int bx, int by) +//=========================================================================== + +void FPathTraverse::AddLineIntercepts(int bx, int by) { FBlockLinesIterator it(bx, by, bx, by, true); line_t *ld; @@ -1030,18 +958,22 @@ void P_AddLineIntercepts(int bx, int by) newintercept.frac = frac; newintercept.isaline = true; + newintercept.done = false; newintercept.d.line = ld; intercepts.Push (newintercept); } } +//=========================================================================== // -// PIT_AddThingIntercepts +// FPathTraverse :: AddThingIntercepts // -void P_AddThingIntercepts (int bx, int by, TArray &checkbt) +//=========================================================================== + +void FPathTraverse::AddThingIntercepts (int bx, int by, int checkindex) { - FBlockThingsIterator it(bx, by, bx, by, &checkbt); + FBlockThingsIterator it(bx, by, checkindex); AActor *thing; while ((thing = it.Next())) @@ -1106,6 +1038,7 @@ void P_AddThingIntercepts (int bx, int by, TArray &checkbt) intercept_t newintercept; newintercept.frac = frac; newintercept.isaline = false; + newintercept.done = false; newintercept.d.thing = thing; intercepts.Push (newintercept); continue; @@ -1120,6 +1053,7 @@ void P_AddThingIntercepts (int bx, int by, TArray &checkbt) intercept_t newintercept; newintercept.frac = 0; newintercept.isaline = false; + newintercept.done = false; newintercept.d.thing = thing; intercepts.Push (newintercept); } @@ -1127,60 +1061,41 @@ void P_AddThingIntercepts (int bx, int by, TArray &checkbt) } +//=========================================================================== // -// P_TraverseIntercepts -// Returns true if the traverser function returns true -// for all lines. +// FPathTraverse :: Next // -bool P_TraverseIntercepts (traverser_t func, fixed_t maxfrac) +//=========================================================================== + +intercept_t *FPathTraverse::Next() { - unsigned int count; - fixed_t dist; - unsigned int scanpos; - intercept_t *scan; intercept_t *in = NULL; - count = intercepts.Size (); - - while (count--) + fixed_t dist = FIXED_MAX; + for (unsigned scanpos = intercept_index; scanpos < intercepts.Size (); scanpos++) { - dist = FIXED_MAX; - for (scanpos = 0; scanpos < intercepts.Size (); scanpos++) + intercept_t *scan = &intercepts[scanpos]; + if (scan->frac < dist && !scan->done) { - scan = &intercepts[scanpos]; - if (scan->frac < dist) - { - dist = scan->frac; - in = scan; - } + dist = scan->frac; + in = scan; } - - if (dist > maxfrac || in == NULL) - return true; // checked everything in range - - if (!func (in)) - return false; // don't bother going farther - - in->frac = FIXED_MAX; } - - return true; // everything was traversed + + if (dist > maxfrac || in == NULL) return NULL; // checked everything in range + in->done = true; + return in; } - - - +//=========================================================================== // -// P_PathTraverse +// FPathTraverse // Traces a line from x1,y1 to x2,y2, -// calling the traverser function for each. -// Returns true if the traverser function returns true -// for all lines. // -bool P_PathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, bool (*trav) (intercept_t *)) -{ - static TArray pathbt; +//=========================================================================== +FPathTraverse::FPathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags) +{ fixed_t xt1; fixed_t yt1; fixed_t xt2; @@ -1203,8 +1118,7 @@ bool P_PathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, int count; validcount++; - intercepts.Clear (); - pathbt.Clear (); + intercept_index = intercepts.Size(); if ( ((x1-bmaporgx)&(MAPBLOCKSIZE-1)) == 0) x1 += FRACUNIT; // don't side exactly on a line @@ -1295,16 +1209,18 @@ bool P_PathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, mapx = xt1; mapy = yt1; + // we want to use one list of checked actors for the entire operation + int BTI_CheckIndex = FBlockThingsIterator::GetCheckIndex(); for (count = 0 ; count < 100 ; count++) { if (flags & PT_ADDLINES) { - P_AddLineIntercepts(mapx, mapy); + AddLineIntercepts(mapx, mapy); } if (flags & PT_ADDTHINGS) { - P_AddThingIntercepts(mapx, mapy, pathbt); + AddThingIntercepts(mapx, mapy, BTI_CheckIndex); } if (mapx == xt2 && mapy == yt2) @@ -1336,14 +1252,14 @@ bool P_PathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, // be checked. if (flags & PT_ADDLINES) { - P_AddLineIntercepts(mapx + mapxstep, mapy); - P_AddLineIntercepts(mapx, mapy + mapystep); + AddLineIntercepts(mapx + mapxstep, mapy); + AddLineIntercepts(mapx, mapy + mapystep); } if (flags & PT_ADDTHINGS) { - P_AddThingIntercepts(mapx + mapxstep, mapy, pathbt); - P_AddThingIntercepts(mapx, mapy + mapystep, pathbt); + AddThingIntercepts(mapx + mapxstep, mapy, BTI_CheckIndex); + AddThingIntercepts(mapx, mapy + mapystep, BTI_CheckIndex); } xintercept += xstep; yintercept += ystep; @@ -1352,10 +1268,16 @@ bool P_PathTraverse (fixed_t x1, fixed_t y1, fixed_t x2, fixed_t y2, int flags, break; } } - // go through the sorted list - return P_TraverseIntercepts ( trav, FRACUNIT ); + FBlockThingsIterator::SetCheckIndex(BTI_CheckIndex); + maxfrac = FRACUNIT; } +FPathTraverse::~FPathTraverse() +{ + intercepts.Resize(intercept_index); +} + + //=========================================================================== // // P_RoughMonsterSearch diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 117eef13..06094b3b 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -80,7 +80,6 @@ static void PlayerLandedOnThing (AActor *mo, AActor *onmobj); extern cycle_t BotSupportCycles; extern cycle_t BotWTG; -extern fixed_t attackrange; EXTERN_CVAR (Bool, r_drawfuzz); EXTERN_CVAR (Int, cl_rockettrails) diff --git a/src/p_sight.cpp b/src/p_sight.cpp index 4f41bfe5..23960c7f 100644 --- a/src/p_sight.cpp +++ b/src/p_sight.cpp @@ -43,8 +43,7 @@ static int sightcounts[6]; static cycle_t SightCycles; static cycle_t MaxSightCycles; -// we might as well use the global intercepts array instead of creating a new one -extern TArray intercepts; +static TArray intercepts (128); class SightCheck { diff --git a/src/p_trace.cpp b/src/p_trace.cpp index 609307ee..30d21670 100644 --- a/src/p_trace.cpp +++ b/src/p_trace.cpp @@ -36,70 +36,73 @@ #include "p_local.h" #include "i_system.h" -static fixed_t StartZ; -static fixed_t Vx, Vy, Vz; -static DWORD ActorMask, WallMask; -static AActor *IgnoreThis; -static FTraceResults *Results; -static sector_t *CurSector; -static fixed_t MaxDist; -static fixed_t EnterDist; -static bool (*TraceCallback)(FTraceResults &res); -static DWORD TraceFlags; +struct FTraceInfo +{ + fixed_t StartX, StartY, StartZ; + fixed_t Vx, Vy, Vz; + DWORD ActorMask, WallMask; + AActor *IgnoreThis; + FTraceResults *Results; + sector_t *CurSector; + fixed_t MaxDist; + fixed_t EnterDist; + bool (*TraceCallback)(FTraceResults &res); + DWORD TraceFlags; -// These are required for 3D-floor checking -// to create a fake sector with a floor -// or ceiling plane coming from a 3D-floor -static sector_t DummySector[2]; -static int sectorsel; + // These are required for 3D-floor checking + // to create a fake sector with a floor + // or ceiling plane coming from a 3D-floor + sector_t DummySector[2]; + int sectorsel; + + bool TraceTraverse (int ptflags); + bool CheckSectorPlane (const sector_t *sector, bool checkFloor); +}; -static bool PTR_TraceIterator (intercept_t *); -static bool CheckSectorPlane (const sector_t *sector, bool checkFloor, divline_t & trace); static bool EditTraceResult (DWORD flags, FTraceResults &res); + bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, fixed_t vx, fixed_t vy, fixed_t vz, fixed_t maxDist, DWORD actorMask, DWORD wallMask, AActor *ignore, FTraceResults &res, DWORD flags, bool (*callback)(FTraceResults &res)) { - // Under extreme circumstances ADecal::DoTrace can call this from inside a linespecial call! - static bool recursion; - if (recursion) return false; - recursion=true; - int ptflags; + FTraceInfo inf; ptflags = actorMask ? PT_ADDLINES|PT_ADDTHINGS : PT_ADDLINES; - StartZ = z; - Vx = vx; - Vy = vy; - Vz = vz; - ActorMask = actorMask; - WallMask = wallMask; - IgnoreThis = ignore; - CurSector = sector; - MaxDist = maxDist; - EnterDist = 0; - TraceCallback = callback; - TraceFlags = flags; + inf.StartX = x; + inf.StartY = y; + inf.StartZ = z; + inf.Vx = vx; + inf.Vy = vy; + inf.Vz = vz; + inf.ActorMask = actorMask; + inf.WallMask = wallMask; + inf.IgnoreThis = ignore; + inf.CurSector = sector; + inf.MaxDist = maxDist; + inf.EnterDist = 0; + inf.TraceCallback = callback; + inf.TraceFlags = flags; res.CrossedWater = NULL; - Results = &res; + inf.Results = &res; res.HitType = TRACE_HitNone; // Do a 3D floor check in the starting sector res.ffloor=NULL; - sectorsel=0; + inf.sectorsel=0; TDeletingArray &ff = sector->e->XFloor.ffloors; if (ff.Size()) { - memcpy(&DummySector[0],sector,sizeof(sector_t)); - CurSector=sector=&DummySector[0]; - sectorsel=1; + memcpy(&inf.DummySector[0],sector,sizeof(sector_t)); + inf.CurSector=sector=&inf.DummySector[0]; + inf.sectorsel=1; fixed_t bf = sector->floorplane.ZatPoint (x, y); fixed_t bc = sector->ceilingplane.ZatPoint (x, y); @@ -141,11 +144,11 @@ bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, if (xd>SQWORD(32767)*FRACUNIT) { - maxDist=FixedDiv(FIXED_MAX-x,vx); + maxDist = inf.MaxDist=FixedDiv(FIXED_MAX-x,vx); } else if (xd<-SQWORD(32767)*FRACUNIT) { - maxDist=FixedDiv(FIXED_MIN-x,vx); + maxDist = inf.MaxDist=FixedDiv(FIXED_MIN-x,vx); } @@ -153,40 +156,34 @@ bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, if (yd>SQWORD(32767)*FRACUNIT) { - maxDist=FixedDiv(FIXED_MAX-y,vy); + maxDist = inf.MaxDist=FixedDiv(FIXED_MAX-y,vy); } else if (yd<-SQWORD(32767)*FRACUNIT) { - maxDist=FixedDiv(FIXED_MIN-y,vy); + maxDist = inf.MaxDist=FixedDiv(FIXED_MIN-y,vy); } // recalculate the trace's end points for robustness - if (P_PathTraverse (x, y, x + FixedMul (vx, maxDist), y + FixedMul (vy, maxDist), - ptflags, PTR_TraceIterator)) + if (inf.TraceTraverse (ptflags)) { // check for intersection with floor/ceiling - res.Sector = CurSector; - divline_t trace; + res.Sector = inf.CurSector; - trace.x=x; - trace.y=y; - - if (CheckSectorPlane (CurSector, true, trace)) + if (inf.CheckSectorPlane (inf.CurSector, true)) { res.HitType = TRACE_HitFloor; if (res.CrossedWater == NULL && - CurSector->heightsec != NULL && - CurSector->heightsec->floorplane.ZatPoint (res.X, res.Y) >= res.Z) + inf.CurSector->heightsec != NULL && + inf.CurSector->heightsec->floorplane.ZatPoint (res.X, res.Y) >= res.Z) { - res.CrossedWater = CurSector; + res.CrossedWater = inf.CurSector; } } - else if (CheckSectorPlane (CurSector, false, trace)) + else if (inf.CheckSectorPlane (inf.CurSector, false)) { res.HitType = TRACE_HitCeiling; } } - recursion=false; if (res.HitType != TRACE_HitNone) { if (flags) @@ -210,339 +207,343 @@ bool Trace (fixed_t x, fixed_t y, fixed_t z, sector_t *sector, } } -static bool PTR_TraceIterator (intercept_t *in) +bool FTraceInfo::TraceTraverse (int ptflags) { - fixed_t hitx, hity, hitz; - fixed_t dist; + FPathTraverse it(StartX, StartY, StartX + FixedMul (Vx, MaxDist), StartY + FixedMul (Vy, MaxDist), ptflags); + intercept_t *in; - if (in->isaline) + while ((in = it.Next())) { - int lineside; - sector_t *entersector; + fixed_t hitx, hity, hitz; + fixed_t dist; - dist = FixedMul (MaxDist, in->frac); - hitx = trace.x + FixedMul (Vx, dist); - hity = trace.y + FixedMul (Vy, dist); - hitz = StartZ + FixedMul (Vz, dist); - - fixed_t ff, fc, bf = 0, bc = 0; - - // CurSector may be a copy so we must compare the sector number, not the index! - if (in->d.line->frontsector->sectornum == CurSector->sectornum) + if (in->isaline) { - lineside = 0; - } - else if (in->d.line->backsector && in->d.line->backsector->sectornum == CurSector->sectornum) - { - lineside = 1; - } - else - { // Dammit. Why does Doom have to allow non-closed sectors? - if (in->d.line->backsector == NULL) + int lineside; + sector_t *entersector; + + dist = FixedMul (MaxDist, in->frac); + hitx = StartX + FixedMul (Vx, dist); + hity = StartY + FixedMul (Vy, dist); + hitz = StartZ + FixedMul (Vz, dist); + + fixed_t ff, fc, bf = 0, bc = 0; + + // CurSector may be a copy so we must compare the sector number, not the index! + if (in->d.line->frontsector->sectornum == CurSector->sectornum) { lineside = 0; - CurSector = in->d.line->frontsector; + } + else if (in->d.line->backsector && in->d.line->backsector->sectornum == CurSector->sectornum) + { + lineside = 1; } else - { - lineside = P_PointOnLineSide (trace.x, trace.y, in->d.line); - CurSector = lineside ? in->d.line->backsector : in->d.line->frontsector; - } - } - - if (!(in->d.line->flags & ML_TWOSIDED)) - { - entersector = NULL; - } - else - { - entersector = (lineside == 0) ? in->d.line->backsector : in->d.line->frontsector; - - // For backwards compatibility: Ignore lines with the same sector on both sides. - // This is the way Doom.exe did it and some WADs (e.g. Alien Vendetta MAP15 need it. - if (i_compatflags & COMPATF_TRACE && in->d.line->backsector == in->d.line->frontsector) - { - return true; - } - } - - ff = CurSector->floorplane.ZatPoint (hitx, hity); - fc = CurSector->ceilingplane.ZatPoint (hitx, hity); - - if (entersector != NULL) - { - bf = entersector->floorplane.ZatPoint (hitx, hity); - bc = entersector->ceilingplane.ZatPoint (hitx, hity); - } - - if (Results->CrossedWater == NULL && - CurSector->heightsec && - !(CurSector->MoreFlags & SECF_IGNOREHEIGHTSEC) && - //CurSector->heightsec->waterzone && - hitz <= CurSector->heightsec->floorplane.ZatPoint (hitx, hity)) - { - // hit crossed a water plane - Results->CrossedWater = CurSector; - } - - if (hitz <= ff) - { // hit floor in front of wall - Results->HitType = TRACE_HitFloor; - } - else if (hitz >= fc) - { // hit ceiling in front of wall - Results->HitType = TRACE_HitCeiling; - } - else if (entersector == NULL || - hitz <= bf || hitz >= bc || - in->d.line->flags & WallMask) - { // hit the wall - - Results->HitType = TRACE_HitWall; - Results->Tier = - entersector == NULL ? TIER_Middle : - hitz <= bf ? TIER_Lower : - hitz >= bc ? TIER_Upper : TIER_Middle; - if (TraceFlags & TRACE_Impact) - { - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_IMPACT); - } - } - else - { // made it past the wall - // check for 3D floors first - if (entersector->e->XFloor.ffloors.Size()) - { - memcpy(&DummySector[sectorsel],entersector,sizeof(sector_t)); - entersector=&DummySector[sectorsel]; - sectorsel^=1; - - for(unsigned int i=0;ie->XFloor.ffloors.Size();i++) + { // Dammit. Why does Doom have to allow non-closed sectors? + if (in->d.line->backsector == NULL) { - F3DFloor * rover=entersector->e->XFloor.ffloors[i]; - - if (rover->flags&FF_SOLID && rover->flags&FF_EXISTS) - { - fixed_t ff_bottom=rover->bottom.plane->ZatPoint(hitx, hity); - fixed_t ff_top=rover->top.plane->ZatPoint(hitx, hity); - - // clip to the part of the sector we are in - if (hitz>ff_top) - { - // above - if (bffloorplane=*rover->top.plane; - entersector->floorpic=*rover->top.texture; - bf=ff_top; - } - } - else if (hitzff_bottom) - { - entersector->ceilingplane=*rover->bottom.plane; - entersector->ceilingpic=*rover->bottom.texture; - bc=ff_bottom; - } - } - else - { - //hit the edge - equivalent to hitting the wall - Results->HitType = TRACE_HitWall; - Results->Tier = TIER_FFloor; - Results->ffloor = rover; - if ((TraceFlags & TRACE_Impact) && in->d.line->special) - { - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_IMPACT); - } - goto cont; - } - } - } - } - - - - Results->HitType = TRACE_HitNone; - if (TraceFlags & TRACE_PCross) - { - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_PCROSS); - } - if (TraceFlags & TRACE_Impact) - { // This is incorrect for "impact", but Hexen did this, so - // we need to as well, for compatibility - P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_IMPACT); - } - } -cont: - - if (Results->HitType != TRACE_HitNone) - { - // We hit something, so figure out where exactly - Results->Sector = CurSector; - - if (Results->HitType != TRACE_HitWall && - !CheckSectorPlane (CurSector, Results->HitType == TRACE_HitFloor, trace)) - { // trace is parallel to the plane (or right on it) - if (entersector == NULL) - { - Results->HitType = TRACE_HitWall; - Results->Tier = TIER_Middle; + lineside = 0; + CurSector = in->d.line->frontsector; } else { - if (hitz <= bf || hitz >= bc) + lineside = P_PointOnLineSide (StartX, StartY, in->d.line); + CurSector = lineside ? in->d.line->backsector : in->d.line->frontsector; + } + } + + if (!(in->d.line->flags & ML_TWOSIDED)) + { + entersector = NULL; + } + else + { + entersector = (lineside == 0) ? in->d.line->backsector : in->d.line->frontsector; + + // For backwards compatibility: Ignore lines with the same sector on both sides. + // This is the way Doom.exe did it and some WADs (e.g. Alien Vendetta MAP15 need it. + if (i_compatflags & COMPATF_TRACE && in->d.line->backsector == in->d.line->frontsector) + { + continue; + } + } + + ff = CurSector->floorplane.ZatPoint (hitx, hity); + fc = CurSector->ceilingplane.ZatPoint (hitx, hity); + + if (entersector != NULL) + { + bf = entersector->floorplane.ZatPoint (hitx, hity); + bc = entersector->ceilingplane.ZatPoint (hitx, hity); + } + + if (Results->CrossedWater == NULL && + CurSector->heightsec && + !(CurSector->MoreFlags & SECF_IGNOREHEIGHTSEC) && + //CurSector->heightsec->waterzone && + hitz <= CurSector->heightsec->floorplane.ZatPoint (hitx, hity)) + { + // hit crossed a water plane + Results->CrossedWater = CurSector; + } + + if (hitz <= ff) + { // hit floor in front of wall + Results->HitType = TRACE_HitFloor; + } + else if (hitz >= fc) + { // hit ceiling in front of wall + Results->HitType = TRACE_HitCeiling; + } + else if (entersector == NULL || + hitz <= bf || hitz >= bc || + in->d.line->flags & WallMask) + { // hit the wall + Results->HitType = TRACE_HitWall; + Results->Tier = + entersector == NULL ? TIER_Middle : + hitz <= bf ? TIER_Lower : + hitz >= bc ? TIER_Upper : TIER_Middle; + if (TraceFlags & TRACE_Impact) + { + P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_IMPACT); + } + } + else + { // made it past the wall + // check for 3D floors first + if (entersector->e->XFloor.ffloors.Size()) + { + memcpy(&DummySector[sectorsel],entersector,sizeof(sector_t)); + entersector=&DummySector[sectorsel]; + sectorsel^=1; + + for(unsigned int i=0;ie->XFloor.ffloors.Size();i++) + { + F3DFloor * rover=entersector->e->XFloor.ffloors[i]; + + if (rover->flags&FF_SOLID && rover->flags&FF_EXISTS) + { + fixed_t ff_bottom=rover->bottom.plane->ZatPoint(hitx, hity); + fixed_t ff_top=rover->top.plane->ZatPoint(hitx, hity); + + // clip to the part of the sector we are in + if (hitz>ff_top) + { + // above + if (bffloorplane=*rover->top.plane; + entersector->floorpic=*rover->top.texture; + bf=ff_top; + } + } + else if (hitzff_bottom) + { + entersector->ceilingplane=*rover->bottom.plane; + entersector->ceilingpic=*rover->bottom.texture; + bc=ff_bottom; + } + } + else + { + //hit the edge - equivalent to hitting the wall + Results->HitType = TRACE_HitWall; + Results->Tier = TIER_FFloor; + Results->ffloor = rover; + if ((TraceFlags & TRACE_Impact) && in->d.line->special) + { + P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_IMPACT); + } + goto cont; + } + } + } + } + + + + Results->HitType = TRACE_HitNone; + if (TraceFlags & TRACE_PCross) + { + P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_PCROSS); + } + if (TraceFlags & TRACE_Impact) + { // This is incorrect for "impact", but Hexen did this, so + // we need to as well, for compatibility + P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_IMPACT); + } + } +cont: + + if (Results->HitType != TRACE_HitNone) + { + // We hit something, so figure out where exactly + Results->Sector = CurSector; + + if (Results->HitType != TRACE_HitWall && + !CheckSectorPlane (CurSector, Results->HitType == TRACE_HitFloor)) + { // trace is parallel to the plane (or right on it) + if (entersector == NULL) { Results->HitType = TRACE_HitWall; - Results->Tier = - hitz <= bf ? TIER_Lower : - hitz >= bc ? TIER_Upper : TIER_Middle; + Results->Tier = TIER_Middle; } else { - Results->HitType = TRACE_HitNone; + if (hitz <= bf || hitz >= bc) + { + Results->HitType = TRACE_HitWall; + Results->Tier = + hitz <= bf ? TIER_Lower : + hitz >= bc ? TIER_Upper : TIER_Middle; + } + else + { + Results->HitType = TRACE_HitNone; + } + } + if (Results->HitType == TRACE_HitWall && TraceFlags & TRACE_Impact) + { + P_ActivateLine (in->d.line, IgnoreThis, lineside, SPAC_IMPACT); } } - if (Results->HitType == TRACE_HitWall && TraceFlags & TRACE_Impact) + + if (Results->HitType == TRACE_HitWall) { - P_ActivateLine(in->d.line, IgnoreThis, lineside, SPAC_IMPACT); + Results->X = hitx; + Results->Y = hity; + Results->Z = hitz; + Results->Distance = dist; + Results->Fraction = in->frac; + Results->Line = in->d.line; + Results->Side = lineside; } } - if (Results->HitType == TRACE_HitWall) + if (Results->HitType == TRACE_HitNone) { - Results->X = hitx; - Results->Y = hity; - Results->Z = hitz; - Results->Distance = dist; - Results->Fraction = in->frac; - Results->Line = in->d.line; - Results->Side = lineside; + CurSector = entersector; + EnterDist = dist; + continue; + } + + if (TraceCallback != NULL) + { + if (!TraceCallback (*Results)) return false; + } + else + { + return false; } } - if (Results->HitType == TRACE_HitNone) + // Encountered an actor + if (!(in->d.thing->flags & ActorMask) || + in->d.thing == IgnoreThis) { - CurSector = entersector; - EnterDist = dist; - return true; + continue; } - if (TraceCallback != NULL) - { - return TraceCallback (*Results); - } - else - { - return false; - } - } - - // Encountered an actor - if (!(in->d.thing->flags & ActorMask) || - in->d.thing == IgnoreThis) - { - return true; - } - - dist = FixedMul (MaxDist, in->frac); - hitx = trace.x + FixedMul (Vx, dist); - hity = trace.y + FixedMul (Vy, dist); - hitz = StartZ + FixedMul (Vz, dist); - - if (hitz > in->d.thing->z + in->d.thing->height) - { // trace enters above actor - if (Vz >= 0) return true; // Going up: can't hit - - // Does it hit the top of the actor? - dist = FixedDiv(in->d.thing->z + in->d.thing->height - StartZ, Vz); - - if (dist > MaxDist) return true; - in->frac = FixedDiv(dist, MaxDist); - - hitx = trace.x + FixedMul (Vx, dist); - hity = trace.y + FixedMul (Vy, dist); + dist = FixedMul (MaxDist, in->frac); + hitx = StartX + FixedMul (Vx, dist); + hity = StartY + FixedMul (Vy, dist); hitz = StartZ + FixedMul (Vz, dist); - // calculated coordinate is outside the actor's bounding box - if (abs(hitx - in->d.thing->x) > in->d.thing->radius || - abs(hity - in->d.thing->y) > in->d.thing->radius) return true; - } - else if (hitz < in->d.thing->z) - { // trace enters below actor - if (Vz <= 0) return true; // Going down: can't hit - - // Does it hit the bottom of the actor? - dist = FixedDiv(in->d.thing->z - StartZ, Vz); - if (dist > MaxDist) return true; - in->frac = FixedDiv(dist, MaxDist); + if (hitz > in->d.thing->z + in->d.thing->height) + { // trace enters above actor + if (Vz >= 0) continue; // Going up: can't hit + + // Does it hit the top of the actor? + dist = FixedDiv(in->d.thing->z + in->d.thing->height - StartZ, Vz); - hitx = trace.x + FixedMul (Vx, dist); - hity = trace.y + FixedMul (Vy, dist); - hitz = StartZ + FixedMul (Vz, dist); + if (dist > MaxDist) continue; + in->frac = FixedDiv(dist, MaxDist); - // calculated coordinate is outside the actor's bounding box - if (abs(hitx - in->d.thing->x) > in->d.thing->radius || - abs(hity - in->d.thing->y) > in->d.thing->radius) return true; - } + hitx = StartX + FixedMul (Vx, dist); + hity = StartY + FixedMul (Vy, dist); + hitz = StartZ + FixedMul (Vz, dist); - // check for extrafloors first - if (CurSector->e->XFloor.ffloors.Size()) - { - fixed_t ff_floor=CurSector->floorplane.ZatPoint(hitx, hity); - fixed_t ff_ceiling=CurSector->ceilingplane.ZatPoint(hitx, hity); - - if (hitz>ff_ceiling) // actor is hit above the current ceiling - { - Results->HitType=TRACE_HitCeiling; + // calculated coordinate is outside the actor's bounding box + if (abs(hitx - in->d.thing->x) > in->d.thing->radius || + abs(hity - in->d.thing->y) > in->d.thing->radius) continue; } - else if (hitzHitType=TRACE_HitFloor; - } - else goto cont1; + else if (hitz < in->d.thing->z) + { // trace enters below actor + if (Vz <= 0) continue; // Going down: can't hit + + // Does it hit the bottom of the actor? + dist = FixedDiv(in->d.thing->z - StartZ, Vz); + if (dist > MaxDist) continue; + in->frac = FixedDiv(dist, MaxDist); - // the trace hit a 3D-floor before the thing. - // Calculate an intersection and abort. - Results->Sector = CurSector; - if (!CheckSectorPlane(CurSector, Results->HitType==TRACE_HitFloor, trace)) - { - Results->HitType=TRACE_HitNone; - } - if (TraceCallback != NULL) - { - return TraceCallback (*Results); - } - else - { - return false; - } - } + hitx = StartX + FixedMul (Vx, dist); + hity = StartY + FixedMul (Vy, dist); + hitz = StartZ + FixedMul (Vz, dist); + // calculated coordinate is outside the actor's bounding box + if (abs(hitx - in->d.thing->x) > in->d.thing->radius || + abs(hity - in->d.thing->y) > in->d.thing->radius) continue; + } + // check for extrafloors first + if (CurSector->e->XFloor.ffloors.Size()) + { + fixed_t ff_floor=CurSector->floorplane.ZatPoint(hitx, hity); + fixed_t ff_ceiling=CurSector->ceilingplane.ZatPoint(hitx, hity); + + if (hitz>ff_ceiling) // actor is hit above the current ceiling + { + Results->HitType=TRACE_HitCeiling; + } + else if (hitzHitType=TRACE_HitFloor; + } + else goto cont1; + + // the trace hit a 3D-floor before the thing. + // Calculate an intersection and abort. + Results->Sector = CurSector; + if (!CheckSectorPlane(CurSector, Results->HitType==TRACE_HitFloor)) + { + Results->HitType=TRACE_HitNone; + } + if (TraceCallback != NULL) + { + return TraceCallback (*Results); + } + else + { + return false; + } + } cont1: - Results->HitType = TRACE_HitActor; - Results->X = hitx; - Results->Y = hity; - Results->Z = hitz; - Results->Distance = dist; - Results->Fraction = in->frac; - Results->Actor = in->d.thing; + Results->HitType = TRACE_HitActor; + Results->X = hitx; + Results->Y = hity; + Results->Z = hitz; + Results->Distance = dist; + Results->Fraction = in->frac; + Results->Actor = in->d.thing; - if (TraceCallback != NULL) - { - return TraceCallback (*Results); - } - else - { - return false; + if (TraceCallback != NULL) + { + if (!TraceCallback (*Results)) return false; + } + else + { + return false; + } } + return true; } -static bool CheckSectorPlane (const sector_t *sector, bool checkFloor, divline_t & trace) +bool FTraceInfo::CheckSectorPlane (const sector_t *sector, bool checkFloor) { secplane_t plane; @@ -559,16 +560,16 @@ static bool CheckSectorPlane (const sector_t *sector, bool checkFloor, divline_t if (den != 0) { - fixed_t num = TMulScale16 (plane.a, trace.x, - plane.b, trace.y, + fixed_t num = TMulScale16 (plane.a, StartX, + plane.b, StartY, plane.c, StartZ) + plane.d; fixed_t hitdist = FixedDiv (-num, den); if (hitdist > EnterDist && hitdist < MaxDist) { - Results->X = trace.x + FixedMul (Vx, hitdist); - Results->Y = trace.y + FixedMul (Vy, hitdist); + Results->X = StartX + FixedMul (Vx, hitdist); + Results->Y = StartY + FixedMul (Vy, hitdist); Results->Z = StartZ + FixedMul (Vz, hitdist); Results->Distance = hitdist; Results->Fraction = FixedDiv (hitdist, MaxDist); diff --git a/src/r_main.cpp b/src/r_main.cpp index 0e0859fc..74e82eb5 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -1134,11 +1134,7 @@ void R_SetupFrame (AActor *actor) camera->sprite != 0) // Sprite 0 is always TNT1 { // [RH] Use chasecam view - P_AimCamera (camera); - iview->nviewx = CameraX; - iview->nviewy = CameraY; - iview->nviewz = CameraZ; - viewsector = CameraSector; + P_AimCamera (camera, iview->nviewx, iview->nviewy, iview->nviewz, viewsector); r_showviewer = true; } else diff --git a/src/sdl/critsec.h b/src/sdl/critsec.h new file mode 100644 index 00000000..daaf30f7 --- /dev/null +++ b/src/sdl/critsec.h @@ -0,0 +1,48 @@ +// Wraps an SDL mutex object. (A critical section is a Windows synchronization +// object similar to a mutex but optimized for access by threads belonging to +// only one process, hence the class name.) + +#ifndef CRITSEC_H +#define CRITSEC_H + +#include "SDL.h" +#include "SDL_thread.h" +#include "i_system.h" + +class FCriticalSection +{ +public: + FCriticalSection() + { + CritSec = SDL_CreateMutex(); + if (CritSec == NULL) + { + I_FatalError("Failed to create a critical section mutex."); + } + } + ~FCriticalSection() + { + if (CritSec != NULL) + { + SDL_DestroyMutex(CritSec); + } + } + void Enter() + { + if (SDL_mutexP(CritSec) != 0) + { + I_FatalError("Failed entering a critical section."); + } + } + void Leave() + { + if (SDL_mutexV(CritSec) != 0) + { + I_FatalError("Failed to leave a critical section."); + } + } +private: + SDL_mutex *CritSec; +}; + +#endif diff --git a/src/sound/i_musicinterns.h b/src/sound/i_musicinterns.h index 58e31d6e..e807839e 100644 --- a/src/sound/i_musicinterns.h +++ b/src/sound/i_musicinterns.h @@ -112,6 +112,7 @@ public: virtual bool FakeVolume() = 0; virtual bool Pause(bool paused) = 0; virtual bool NeedThreadedCallback() = 0; + virtual void PrecacheInstruments(const BYTE *instruments, int count); }; // WinMM implementation of a MIDI output device ----------------------------- @@ -137,6 +138,7 @@ public: bool FakeVolume(); bool NeedThreadedCallback(); bool Pause(bool paused); + void PrecacheInstruments(const BYTE *instruments, int count); protected: static void CALLBACK CallbackFunc(HMIDIOUT, UINT, DWORD_PTR, DWORD, DWORD); @@ -236,6 +238,7 @@ protected: virtual void DoInitialSetup() = 0; virtual void DoRestart() = 0; virtual bool CheckDone() = 0; + virtual void Precache() = 0; virtual DWORD *MakeEvents(DWORD *events, DWORD *max_event_p, DWORD max_time) = 0; enum @@ -294,6 +297,7 @@ protected: void DoInitialSetup(); void DoRestart(); bool CheckDone(); + void Precache(); DWORD *MakeEvents(DWORD *events, DWORD *max_events_p, DWORD max_time); MUSHeader *MusHeader; @@ -319,6 +323,7 @@ protected: void DoInitialSetup(); void DoRestart(); bool CheckDone(); + void Precache(); DWORD *MakeEvents(DWORD *events, DWORD *max_events_p, DWORD max_time); void AdvanceTracks(DWORD time); diff --git a/src/sound/music_midi_base.cpp b/src/sound/music_midi_base.cpp index e85919a7..d7bc8f1f 100644 --- a/src/sound/music_midi_base.cpp +++ b/src/sound/music_midi_base.cpp @@ -12,8 +12,6 @@ static bool nummididevicesset; #ifdef _WIN32 UINT mididevice; -CVAR (Bool, snd_midiprecache, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); - CUSTOM_CVAR (Int, snd_mididevice, -1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) { UINT oldmididev = mididevice; @@ -155,8 +153,9 @@ CCMD (snd_listmididevices) MIDIOUTCAPS caps; MMRESULT res; - PrintMidiDevice (-2, "TiMidity++", 0, 0); - PrintMidiDevice (-1, "FMOD", 0, 0); + PrintMidiDevice (-3, "Emulated OPL FM Synth", MOD_FMSYNTH, 0); + PrintMidiDevice (-2, "TiMidity++", 0, MOD_WAVETABLE | MOD_SWSYNTH); + PrintMidiDevice (-1, "FMOD", 0, MOD_WAVETABLE | MOD_SWSYNTH); if (nummididevices != 0) { for (id = 0; id < nummididevices; ++id) @@ -206,6 +205,7 @@ void I_BuildMIDIMenuList (struct value_t **outValues, float *numValues) CCMD (snd_listmididevices) { + Printf("%s-3. Emulated OPL FM Synth\n", -3 == snd_mididevice ? TEXTCOLOR_BOLD : ""); Printf("%s-2. TiMidity++\n", -2 == snd_mididevice ? TEXTCOLOR_BOLD : ""); Printf("%s-1. FMOD\n", -1 == snd_mididevice ? TEXTCOLOR_BOLD : ""); } diff --git a/src/sound/music_midi_midiout.cpp b/src/sound/music_midi_midiout.cpp index 99b918f5..11c41f27 100644 --- a/src/sound/music_midi_midiout.cpp +++ b/src/sound/music_midi_midiout.cpp @@ -754,6 +754,96 @@ void MIDISong2::SetTempo(int new_tempo) } } +//========================================================================== +// +// MIDISong2 :: Precache +// +// Scans each track for program change events on normal channels and note on +// events on channel 10. Does not care about bank selects, since they're +// unlikely to appear in a song aimed at Doom. +// +//========================================================================== + +void MIDISong2::Precache() +{ + int i, j; + + // This array keeps track of instruments that are used. The first 128 + // entries are for melodic instruments. The second 128 are for + // percussion. + BYTE found_instruments[256] = { 0, }; + + DoRestart(); + for (i = 0; i < NumTracks; ++i) + { + TrackInfo *track = &Tracks[i]; + BYTE running_status = 0; + BYTE ev, data1, data2, command, channel; + int len; + + while (track->TrackP < track->MaxTrackP) + { + ev = track->TrackBegin[track->TrackP++]; + command = ev & 0xF0; + + if (command == MIDI_SYSEX || command == MIDI_SYSEXEND) + { + len = track->ReadVarLen(); + track->TrackP += len; + } + else if (command == MIDI_META) + { + track->TrackP++; + len = track->ReadVarLen(); + track->TrackP += len; + } + else if ((command & 0xF0) == 0xF0) + { + track->TrackP += CommonLengths[ev & 0xF]; + } + else + { + if ((ev & 0x80) == 0) + { // Use running status. + data1 = ev; + ev = running_status; + } + else + { // Store new running status. + running_status = ev; + data1 = track->TrackBegin[track->TrackP++]; + } + command = ev & 0x70; + channel = ev & 0x0F; + if (EventLengths[command >> 4] == 2) + { + data2 = track->TrackBegin[track->TrackP++]; + } + if (channel != 9 && command == (MIDI_PRGMCHANGE & 0x70)) + { + found_instruments[data1 & 127] = 1; + } + else if (channel == 9 && command == (MIDI_NOTEON & 0x70) && data2 != 0) + { + found_instruments[data1 | 128] = 1; + } + track->ReadVarLen(); // Skip delay. + } + } + } + DoRestart(); + + // Now pack everything into a contiguous region for the PrecacheInstruments call(). + for (i = j = 0; i < 256; ++i) + { + if (found_instruments[i]) + { + found_instruments[j++] = i; + } + } + MIDI->PrecacheInstruments(found_instruments, j); +} + //========================================================================== // // MIDISong2 :: GetOPLDumper diff --git a/src/sound/music_midistream.cpp b/src/sound/music_midistream.cpp index db78a7ad..c57ddfad 100644 --- a/src/sound/music_midistream.cpp +++ b/src/sound/music_midistream.cpp @@ -221,6 +221,7 @@ void MIDIStreamer::Play(bool looping) } CheckCaps(); + Precache(); // Set time division and tempo. if (0 != MIDI->SetTimeDiv(Division) || @@ -301,7 +302,8 @@ void MIDIStreamer::Play(bool looping) // MIDIStreamer :: Pause // // "Pauses" the song by setting it to zero volume and filling subsequent -// buffers with NOPs until the song is unpaused. +// buffers with NOPs until the song is unpaused. A MIDI device that +// supports real pauses will return true from its Pause() method. // //========================================================================== @@ -711,3 +713,22 @@ MIDIDevice::MIDIDevice() MIDIDevice::~MIDIDevice() { } + +//========================================================================== +// +// MIDIDevice :: PrecacheInstruments +// +// The MIDIStreamer calls this method between device open and the first +// buffered stream with a list of instruments known to be used by the song. +// If the device can benefit from preloading the instruments, it can do so +// now. +// +// For each entry, bit 7 set indicates that the instrument is percussion and +// the lower 7 bits contain the note number to use on MIDI channel 10, +// otherwise it is melodic and the lower 7 bits are the program number. +// +//========================================================================== + +void MIDIDevice::PrecacheInstruments(const BYTE *instruments, int count) +{ +} diff --git a/src/sound/music_mus_midiout.cpp b/src/sound/music_mus_midiout.cpp index f7b875cf..651f154b 100644 --- a/src/sound/music_mus_midiout.cpp +++ b/src/sound/music_mus_midiout.cpp @@ -179,6 +179,34 @@ bool MUSSong2::CheckDone() return MusP >= MaxMusP; } +//========================================================================== +// +// MUSSong2 :: Precache +// +// MUS songs contain information in their header for exactly this purpose. +// +//========================================================================== + +void MUSSong2::Precache() +{ + BYTE *work = (BYTE *)alloca(MusHeader->NumInstruments); + const WORD *used = (WORD *)MusHeader + sizeof(MUSHeader) / 2; + int i, j; + + for (i = j = 0; i < MusHeader->NumInstruments; ++i) + { + if (used[i] < 128) + { + work[j++] = (BYTE)used[i]; + } + else if (used[i] >= 135 && used[i] <= 181) + { // Percussions are 100-based, not 128-based, eh? + work[j++] = (used[i] - 100) | 0x80; + } + } + MIDI->PrecacheInstruments(&work[0], j); +} + //========================================================================== // // MUSSong2 :: MakeEvents diff --git a/src/sound/music_win_mididevice.cpp b/src/sound/music_win_mididevice.cpp index 56080ec3..aa137a26 100644 --- a/src/sound/music_win_mididevice.cpp +++ b/src/sound/music_win_mididevice.cpp @@ -55,6 +55,8 @@ // PUBLIC DATA DEFINITIONS ------------------------------------------------- +CVAR (Bool, snd_midiprecache, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG); + // CODE -------------------------------------------------------------------- //========================================================================== @@ -203,6 +205,61 @@ void WinMIDIDevice::Stop() } } +//========================================================================== +// +// WinMIDIDevice :: PrecacheInstruments +// +// For each entry, bit 7 set indicates that the instrument is percussion and +// the lower 7 bits contain the note number to use on MIDI channel 10, +// otherwise it is melodic and the lower 7 bits are the program number. +// +// My old GUS PnP needed the instruments to be preloaded, or it would miss +// some notes the first time through the song. I doubt any modern +// hardware has this problem, but since I'd already written the code for +// ZDoom 1.22 and below, I'm resurrecting it now for completeness, since I'm +// using preloading for the internal Timidity. +// +//========================================================================== + +void WinMIDIDevice::PrecacheInstruments(const BYTE *instruments, int count) +{ + // Setting snd_midiprecache to false disables this precaching, since it + // does involve sleeping for more than a miniscule amount of time. + if (!snd_midiprecache) + { + return; + } + for (int i = 0, chan = 0; i < count; ++i) + { + if (instruments[i] & 0x80) + { // Percussion + midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_NOTEON | 9 | ((instruments[i] & 0x7f) << 8) | (1 << 16)); + } + else + { // Melodic + midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_PRGMCHANGE | chan | (instruments[i] << 8)); + midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_NOTEON | chan | (60 << 8) | (1 << 16)); + if (++chan == 9) + { // Skip the percussion channel + chan = 10; + } + } + // Once we've got an instrument playing on each melodic channel, sleep to give + // the driver time to load the instruments. Also do this for the final batch + // of instruments. + if (chan == 16 || i == count - 1) + { + Sleep(250); + for (chan = 15; chan-- != 0; ) + { + // Turn all notes off + midiOutShortMsg((HMIDIOUT)MidiOut, MIDI_CTRLCHANGE | chan | (123 << 8)); + } + // And now chan is back at 0, ready to start the cycle over. + } + } +} + //========================================================================== // // WinMIDIDevice :: Pause diff --git a/src/svnrevision.h b/src/svnrevision.h index 8c980983..be0ef286 100644 --- a/src/svnrevision.h +++ b/src/svnrevision.h @@ -3,5 +3,5 @@ // This file was automatically generated by the // updaterevision tool. Do not edit by hand. -#define ZD_SVN_REVISION_STRING "894" -#define ZD_SVN_REVISION_NUMBER 894 +#define ZD_SVN_REVISION_STRING "898" +#define ZD_SVN_REVISION_NUMBER 898 diff --git a/src/version.h b/src/version.h index 5aa1e017..846f8b85 100644 --- a/src/version.h +++ b/src/version.h @@ -56,12 +56,12 @@ // Version identifier for network games. // Bump it every time you do a release unless you're certain you // didn't change anything that will affect sync. -#define NETGAMEVERSION 215 +#define NETGAMEVERSION 216 // Version stored in the ini's [LastRun] section. // Bump it if you made some configuration change that you want to // be able to migrate in FGameConfigFile::DoGlobalSetup(). -#define LASTRUNVERSION "206" +#define LASTRUNVERSION "207" // Protocol version used in demos. // Bump it if you change existing DEM_ commands or add new ones. diff --git a/src/win32/critsec.h b/src/win32/critsec.h new file mode 100644 index 00000000..05d54f15 --- /dev/null +++ b/src/win32/critsec.h @@ -0,0 +1,42 @@ +// Wraps a Windows critical section object. + +#ifndef CRITSEC_H +#define CRITSEC_H + +#ifndef _WINNT_ +#define WIN32_LEAN_AND_MEAN +#include +#define USE_WINDOWS_DWORD +#endif + +class FCriticalSection +{ +public: + FCriticalSection() + { + InitializeCriticalSection(&CritSec); + } + ~FCriticalSection() + { + DeleteCriticalSection(&CritSec); + } + void Enter() + { + EnterCriticalSection(&CritSec); + } + void Leave() + { + LeaveCriticalSection(&CritSec); + } +#if 0 + // SDL has no equivalent functionality, so better not use it on Windows. + bool TryEnter() + { + return TryEnterCriticalSection(&CritSec) != 0; + } +#endif +private: + CRITICAL_SECTION CritSec; +}; + +#endif