diff options
Diffstat (limited to 'src/uqm/process.c')
-rw-r--r-- | src/uqm/process.c | 1108 |
1 files changed, 1108 insertions, 0 deletions
diff --git a/src/uqm/process.c b/src/uqm/process.c new file mode 100644 index 0000000..142c58e --- /dev/null +++ b/src/uqm/process.c @@ -0,0 +1,1108 @@ +//Copyright Paul Reiche, Fred Ford. 1992-2002 + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "process.h" + +#include "races.h" +#include "collide.h" +#include "options.h" +#include "settings.h" +#include "setup.h" +#include "sounds.h" +#include "hyper.h" +#include "element.h" +#include "battle.h" +#include "weapon.h" +#include "libs/graphics/drawable.h" +#include "libs/graphics/drawcmd.h" +#include "libs/graphics/gfx_common.h" +#include "libs/log.h" +#include "libs/misc.h" + + +//#define DEBUG_PROCESS + +COUNT DisplayFreeList; +PRIMITIVE DisplayArray[MAX_DISPLAY_PRIMS]; +extern POINT SpaceOrg; + +SIZE zoom_out = 1 << ZOOM_SHIFT; +static SIZE opt_max_zoom_out; + +#if 0 +static inline void +CALC_ZOOM_STUFF (COUNT* idx, COUNT* sc) +{ + int i, z; + + z = 1 << ZOOM_SHIFT; + for (i = 0; (z <<= 1) <= zoom_out; i++) + ; + *idx = i; + *sc = ((1 << i) << (ZOOM_SHIFT + 8)) / zoom_out; +} +#else +static inline void +CALC_ZOOM_STUFF (COUNT* idx, COUNT* sc) +{ + int i; + + if (zoom_out < 2 << ZOOM_SHIFT) + i = 0; + else if (zoom_out < 4 << ZOOM_SHIFT) + i = 1; + else + i = 2; + *idx = i; + *sc = (1 << (i + ZOOM_SHIFT + 8)) / zoom_out; +} +#endif + +HELEMENT +AllocElement (void) +{ + HELEMENT hElement; + + hElement = AllocLink (&disp_q); + if (hElement) + { + ELEMENT *ElementPtr; + + LockElement (hElement, &ElementPtr); + memset (ElementPtr, 0, sizeof (*ElementPtr)); + ElementPtr->PrimIndex = AllocDisplayPrim (); + if (ElementPtr->PrimIndex == END_OF_LIST) + { + log_add (log_Error, "AllocElement: Out of display prims!"); + explode (); + } + SetPrimType (&DisplayArray[ElementPtr->PrimIndex], NO_PRIM); + UnlockElement (hElement); + } + + return (hElement); +} + +void +FreeElement (HELEMENT hElement) +{ + if (hElement) + { + ELEMENT *ElementPtr; + + LockElement (hElement, &ElementPtr); + FreeDisplayPrim (ElementPtr->PrimIndex); + UnlockElement (hElement); + + FreeLink (&disp_q, hElement); + } +} + +void +SetUpElement (ELEMENT *ElementPtr) +{ + ElementPtr->next = ElementPtr->current; + if (CollidingElement (ElementPtr)) + { + InitIntersectStartPoint (ElementPtr); + InitIntersectEndPoint (ElementPtr); + InitIntersectFrame (ElementPtr); + } +} + +static void +PreProcess (ELEMENT *ElementPtr) +{ + ELEMENT_FLAGS state_flags; + + if (ElementPtr->life_span == 0) + { + if (ElementPtr->pParent) /* untarget this dead element */ + Untarget (ElementPtr); + + ElementPtr->state_flags |= DISAPPEARING; + if (ElementPtr->death_func) + (*ElementPtr->death_func) (ElementPtr); + } + + state_flags = ElementPtr->state_flags; + if (!(state_flags & DISAPPEARING)) + { + if (state_flags & APPEARING) + { + SetUpElement (ElementPtr); + + if (state_flags & PLAYER_SHIP) + state_flags &= ~APPEARING; /* want to preprocess ship */ + } + + if (ElementPtr->preprocess_func && !(state_flags & APPEARING)) + { + (*ElementPtr->preprocess_func) (ElementPtr); + + state_flags = ElementPtr->state_flags; + if ((state_flags & CHANGING) && CollidingElement (ElementPtr)) + InitIntersectFrame (ElementPtr); + } + + if (!(state_flags & IGNORE_VELOCITY)) + { + SIZE delta_x, delta_y; + + GetNextVelocityComponents (&ElementPtr->velocity, + &delta_x, &delta_y, 1); + if (delta_x != 0 || delta_y != 0) + { + state_flags |= CHANGING; + ElementPtr->next.location.x += delta_x; + ElementPtr->next.location.y += delta_y; + } + } + + if (CollidingElement (ElementPtr)) + InitIntersectEndPoint (ElementPtr); + + if (state_flags & FINITE_LIFE) + --ElementPtr->life_span; + } + + ElementPtr->state_flags = (state_flags & ~(POST_PROCESS | COLLISION)) + | PRE_PROCESS; +} + +static void +PostProcess (ELEMENT *ElementPtr) +{ + if (ElementPtr->postprocess_func) + (*ElementPtr->postprocess_func) (ElementPtr); + ElementPtr->current = ElementPtr->next; + + if (CollidingElement (ElementPtr)) + { + InitIntersectStartPoint (ElementPtr); + InitIntersectEndPoint (ElementPtr); + } + + ElementPtr->state_flags = (ElementPtr->state_flags + & ~(PRE_PROCESS | CHANGING | APPEARING)) + | POST_PROCESS; +} + +static COUNT +CalcReduction (SIZE dx, SIZE dy) +{ + COUNT next_reduction; + +#ifdef KDEBUG + log_add (log_Debug, "CalcReduction:"); +#endif + + if (optMeleeScale == TFB_SCALE_STEP) + { + SIZE sdx, sdy; + + if (LOBYTE (GLOBAL (CurrentActivity)) > IN_ENCOUNTER) + return (0); + + sdx = dx; + sdy = dy; + for (next_reduction = MAX_VIS_REDUCTION; + (dx <<= REDUCTION_SHIFT) <= TRANSITION_WIDTH + && (dy <<= REDUCTION_SHIFT) <= TRANSITION_HEIGHT + && next_reduction > 0; + next_reduction -= REDUCTION_SHIFT) + ; + + /* check for "real" zoom in */ + if (next_reduction < zoom_out + && zoom_out <= MAX_VIS_REDUCTION) + { +#define HYSTERESIS_X DISPLAY_TO_WORLD(24) +#define HYSTERESIS_Y DISPLAY_TO_WORLD(20) + if (((sdx + HYSTERESIS_X) + << (MAX_VIS_REDUCTION - next_reduction)) > TRANSITION_WIDTH + || ((sdy + HYSTERESIS_Y) + << (MAX_VIS_REDUCTION - next_reduction)) > TRANSITION_HEIGHT) + /* if we don't zoom in, we want to stay at next+1 */ + next_reduction += REDUCTION_SHIFT; + } + + if (next_reduction == 0 + && LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE) + next_reduction += REDUCTION_SHIFT; + } + else + { + if (LOBYTE (GLOBAL (CurrentActivity)) > IN_ENCOUNTER) + return (1 << ZOOM_SHIFT); + + dx = (dx * MAX_ZOOM_OUT) / (LOG_SPACE_WIDTH >> 2); + if (dx < (1 << ZOOM_SHIFT)) + dx = 1 << ZOOM_SHIFT; + else if (dx > MAX_ZOOM_OUT) + dx = MAX_ZOOM_OUT; + + dy = (dy * MAX_ZOOM_OUT) / (LOG_SPACE_HEIGHT >> 2); + if (dy < (1 << ZOOM_SHIFT)) + dy = 1 << ZOOM_SHIFT; + else if (dy > MAX_ZOOM_OUT) + dy = MAX_ZOOM_OUT; + + if (dy > dx) + next_reduction = dy; + else + next_reduction = dx; + + if (next_reduction < (2 << ZOOM_SHIFT) + && LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE) + next_reduction = (2 << ZOOM_SHIFT); + } + +#ifdef KDEBUG + log_add (log_Debug, "CalcReduction: exit"); +#endif + + return (next_reduction); +} + +static VIEW_STATE +CalcView (POINT *pNewScrollPt, SIZE next_reduction, + SIZE *pdx, SIZE *pdy, COUNT ships_alive) +{ + SIZE dx, dy; + VIEW_STATE view_state; + +#ifdef KDEBUG + log_add (log_Debug, "CalcView:"); +#endif + dx = ((COORD)(LOG_SPACE_WIDTH >> 1) - pNewScrollPt->x); + dy = ((COORD)(LOG_SPACE_HEIGHT >> 1) - pNewScrollPt->y); + dx = WRAP_DELTA_X (dx); + dy = WRAP_DELTA_Y (dy); + if (ships_alive == 1) + { +#define ORG_JUMP_X ((SIZE)DISPLAY_ALIGN(LOG_SPACE_WIDTH / 75)) +#define ORG_JUMP_Y ((SIZE)DISPLAY_ALIGN(LOG_SPACE_HEIGHT / 75)) + if (dx > ORG_JUMP_X) + dx = ORG_JUMP_X; + else if (dx < -ORG_JUMP_X) + dx = -ORG_JUMP_X; + if (dy > ORG_JUMP_Y) + dy = ORG_JUMP_Y; + else if (dy < -ORG_JUMP_Y) + dy = -ORG_JUMP_Y; + } + + if ((dx || dy) && inHQSpace ()) + MoveSIS (&dx, &dy); + + if (zoom_out == next_reduction) + view_state = dx == 0 && dy == 0 && !inHQSpace () + ? VIEW_STABLE : VIEW_SCROLL; + else + { + if (optMeleeScale == TFB_SCALE_STEP) + { + SpaceOrg.x = (COORD)(LOG_SPACE_WIDTH >> 1) + - (LOG_SPACE_WIDTH >> ((MAX_REDUCTION + 1) + - next_reduction)); + SpaceOrg.y = (COORD)(LOG_SPACE_HEIGHT >> 1) + - (LOG_SPACE_HEIGHT >> ((MAX_REDUCTION + 1) + - next_reduction)); + } + else + { +#define ZOOM_JUMP ((1 << ZOOM_SHIFT) >> 3) + if (ships_alive == 1 + && zoom_out > next_reduction + && zoom_out <= MAX_ZOOM_OUT + && zoom_out - next_reduction > ZOOM_JUMP) + next_reduction = zoom_out - ZOOM_JUMP; + + // Always align the origin on a whole pixel to reduce the + // amount of object positioning jitter + SpaceOrg.x = DISPLAY_ALIGN((int)(LOG_SPACE_WIDTH >> 1) - + (LOG_SPACE_WIDTH * next_reduction / (MAX_ZOOM_OUT << 2))); + SpaceOrg.y = DISPLAY_ALIGN((int)(LOG_SPACE_HEIGHT >> 1) - + (LOG_SPACE_HEIGHT * next_reduction / (MAX_ZOOM_OUT << 2))); + } + zoom_out = next_reduction; + view_state = VIEW_CHANGE; + } + + if (LOBYTE (GLOBAL (CurrentActivity)) <= IN_HYPERSPACE) + MoveGalaxy (view_state, dx, dy); + + *pdx = dx; + *pdy = dy; + +#ifdef KDEBUG + log_add (log_Debug, "CalcView: exit"); +#endif + return (view_state); +} + + +static ELEMENT_FLAGS +ProcessCollisions (HELEMENT hSuccElement, ELEMENT *ElementPtr, + TIME_VALUE min_time, ELEMENT_FLAGS process_flags) +{ + HELEMENT hTestElement; + + while ((hTestElement = hSuccElement) != 0) + { + ELEMENT *TestElementPtr; + + LockElement (hTestElement, &TestElementPtr); + if (!(TestElementPtr->state_flags & process_flags)) + PreProcess (TestElementPtr); + hSuccElement = GetSuccElement (TestElementPtr); + + if (TestElementPtr == ElementPtr) + { + UnlockElement (hTestElement); + continue; + } + + if (CollisionPossible (TestElementPtr, ElementPtr)) + { + ELEMENT_FLAGS state_flags, test_state_flags; + TIME_VALUE time_val; + + state_flags = ElementPtr->state_flags; + test_state_flags = TestElementPtr->state_flags; + if (((state_flags | test_state_flags) & FINITE_LIFE) + && (((state_flags & APPEARING) + && ElementPtr->life_span > 1) + || ((test_state_flags & APPEARING) + && TestElementPtr->life_span > 1))) + time_val = 0; + else + { + while ((time_val = DrawablesIntersect (&ElementPtr->IntersectControl, + &TestElementPtr->IntersectControl, min_time)) == 1 + && !((state_flags | test_state_flags) & FINITE_LIFE)) + { +#ifdef DEBUG_PROCESS + log_add (log_Debug, "BAD NEWS 0x%x <--> 0x%x", ElementPtr, + TestElementPtr); +#endif /* DEBUG_PROCESS */ + if (state_flags & COLLISION) + { + InitIntersectEndPoint (TestElementPtr); + TestElementPtr->IntersectControl.IntersectStamp.origin = + TestElementPtr->IntersectControl.EndPoint; + time_val = DrawablesIntersect (&ElementPtr->IntersectControl, + &TestElementPtr->IntersectControl, 1); + InitIntersectStartPoint (TestElementPtr); + } + + if (time_val == 1) + { + FRAME CurFrame, NextFrame, + TestCurFrame, TestNextFrame; + + CurFrame = ElementPtr->current.image.frame; + NextFrame = ElementPtr->next.image.frame; + TestCurFrame = TestElementPtr->current.image.frame; + TestNextFrame = TestElementPtr->next.image.frame; + if (NextFrame == CurFrame + && TestNextFrame == TestCurFrame) + { + if (test_state_flags & APPEARING) + { + do_damage (TestElementPtr, TestElementPtr->hit_points); + if (TestElementPtr->pParent) /* untarget this dead element */ + Untarget (TestElementPtr); + + TestElementPtr->state_flags |= (COLLISION | DISAPPEARING); + if (TestElementPtr->death_func) + (*TestElementPtr->death_func) (TestElementPtr); + } + if (state_flags & APPEARING) + { + do_damage (ElementPtr, ElementPtr->hit_points); + if (ElementPtr->pParent) /* untarget this dead element */ + Untarget (ElementPtr); + + ElementPtr->state_flags |= (COLLISION | DISAPPEARING); + if (ElementPtr->death_func) + (*ElementPtr->death_func) (ElementPtr); + + UnlockElement (hTestElement); + return (COLLISION); + } + + time_val = 0; + } + else + { + if (GetFrameIndex (CurFrame) != + GetFrameIndex (NextFrame)) + ElementPtr->next.image.frame = + SetEquFrameIndex (NextFrame, + CurFrame); + else if (NextFrame != CurFrame) + { + ElementPtr->next.image = + ElementPtr->current.image; + if (ElementPtr->life_span > NORMAL_LIFE) + ElementPtr->life_span = NORMAL_LIFE; + } + + if (GetFrameIndex (TestCurFrame) != + GetFrameIndex (TestNextFrame)) + TestElementPtr->next.image.frame = + SetEquFrameIndex (TestNextFrame, + TestCurFrame); + else if (TestNextFrame != TestCurFrame) + { + TestElementPtr->next.image = + TestElementPtr->current.image; + if (TestElementPtr->life_span > NORMAL_LIFE) + TestElementPtr->life_span = NORMAL_LIFE; + } + + InitIntersectStartPoint (ElementPtr); + InitIntersectEndPoint (ElementPtr); + InitIntersectFrame (ElementPtr); + if (state_flags & PLAYER_SHIP) + { + STARSHIP *StarShipPtr; + + GetElementStarShip (ElementPtr, &StarShipPtr); + StarShipPtr->ShipFacing = + GetFrameIndex ( + ElementPtr->next.image.frame); + } + + InitIntersectStartPoint (TestElementPtr); + InitIntersectEndPoint (TestElementPtr); + InitIntersectFrame (TestElementPtr); + if (test_state_flags & PLAYER_SHIP) + { + STARSHIP *StarShipPtr; + + GetElementStarShip (TestElementPtr, &StarShipPtr); + StarShipPtr->ShipFacing = + GetFrameIndex ( + TestElementPtr->next.image.frame); + } + } + } + + if (time_val == 0) + { + InitIntersectEndPoint (ElementPtr); + InitIntersectEndPoint (TestElementPtr); + + break; + } + } + } + + if (time_val > 0) + { + POINT SavePt, TestSavePt; + +#ifdef DEBUG_PROCESS + log_add (log_Debug, "0x%x <--> 0x%x at %u", ElementPtr, + TestElementPtr, time_val); +#endif /* DEBUG_PROCESS */ + SavePt = ElementPtr->IntersectControl.EndPoint; + TestSavePt = TestElementPtr->IntersectControl.EndPoint; + InitIntersectEndPoint (ElementPtr); + InitIntersectEndPoint (TestElementPtr); + if (time_val == 1 + || (((state_flags & COLLISION) + || !ProcessCollisions (hSuccElement, ElementPtr, + time_val - 1, process_flags)) + && ((test_state_flags & COLLISION) + || !ProcessCollisions ( + !(TestElementPtr->state_flags & APPEARING) ? + GetSuccElement (ElementPtr) : + GetHeadElement (), TestElementPtr, + time_val - 1, process_flags)))) + { + state_flags = ElementPtr->state_flags; + test_state_flags = TestElementPtr->state_flags; + +#ifdef DEBUG_PROCESS + log_add (log_Debug, "PROCESSING 0x%x <--> 0x%x at %u", + ElementPtr, TestElementPtr, time_val); +#endif /* DEBUG_PROCESS */ + if (test_state_flags & PLAYER_SHIP) + { + (*TestElementPtr->collision_func) ( + TestElementPtr, &TestSavePt, + ElementPtr, &SavePt + ); + (*ElementPtr->collision_func) ( + ElementPtr, &SavePt, + TestElementPtr, &TestSavePt + ); + } + else + { + (*ElementPtr->collision_func) ( + ElementPtr, &SavePt, + TestElementPtr, &TestSavePt + ); + (*TestElementPtr->collision_func) ( + TestElementPtr, &TestSavePt, + ElementPtr, &SavePt + ); + } + + if (TestElementPtr->state_flags & COLLISION) + { + if (!(test_state_flags & COLLISION)) + { + TestElementPtr->IntersectControl.IntersectStamp.origin = + TestSavePt; + TestElementPtr->next.location.x = + DISPLAY_TO_WORLD (TestSavePt.x); + TestElementPtr->next.location.y = + DISPLAY_TO_WORLD (TestSavePt.y); + InitIntersectEndPoint (TestElementPtr); + } + } + + if (ElementPtr->state_flags & COLLISION) + { + if (!(state_flags & COLLISION)) + { + ElementPtr->IntersectControl.IntersectStamp.origin = + SavePt; + ElementPtr->next.location.x = + DISPLAY_TO_WORLD (SavePt.x); + ElementPtr->next.location.y = + DISPLAY_TO_WORLD (SavePt.y); + InitIntersectEndPoint (ElementPtr); + + if (!(state_flags & FINITE_LIFE) && + !(test_state_flags & FINITE_LIFE)) + { + collide (ElementPtr, TestElementPtr); + + ProcessCollisions (GetHeadElement (), ElementPtr, + MAX_TIME_VALUE, process_flags); + ProcessCollisions (GetHeadElement (), TestElementPtr, + MAX_TIME_VALUE, process_flags); + } + } + UnlockElement (hTestElement); + return (COLLISION); + } + + if (!CollidingElement (ElementPtr)) + { + ElementPtr->state_flags |= COLLISION; + UnlockElement (hTestElement); + return (COLLISION); + } + } + } + } + + UnlockElement (hTestElement); + } + + return (ElementPtr->state_flags & COLLISION); +} + +static VIEW_STATE +PreProcessQueue (SIZE *pscroll_x, SIZE *pscroll_y) +{ + SIZE min_reduction, max_reduction; + COUNT sides_active; + POINT Origin; + HELEMENT hElement; + COUNT ships_alive; + +#ifdef KDEBUG + log_add (log_Debug, "PreProcess:"); +#endif + sides_active = (battle_counter[0] ? 1 : 0) + + (battle_counter[1] ? 1 : 0); + + if (optMeleeScale == TFB_SCALE_STEP) + min_reduction = max_reduction = MAX_VIS_REDUCTION + 1; + else + min_reduction = max_reduction = MAX_ZOOM_OUT + (1 << ZOOM_SHIFT); + + Origin.x = (COORD)(LOG_SPACE_WIDTH >> 1); + Origin.y = (COORD)(LOG_SPACE_HEIGHT >> 1); + + hElement = GetHeadElement (); + ships_alive = 0; + while (hElement != 0) + { + ELEMENT *ElementPtr; + HELEMENT hNextElement; + + LockElement (hElement, &ElementPtr); + + if (!(ElementPtr->state_flags & PRE_PROCESS)) + PreProcess (ElementPtr); + hNextElement = GetSuccElement (ElementPtr); + + if (CollidingElement (ElementPtr) + && !(ElementPtr->state_flags & COLLISION)) + ProcessCollisions (hNextElement, ElementPtr, + MAX_TIME_VALUE, PRE_PROCESS); + + if (ElementPtr->state_flags & PLAYER_SHIP) + { + SIZE dx, dy; + + ships_alive++; + if (max_reduction > opt_max_zoom_out + && min_reduction > opt_max_zoom_out) + { + Origin.x = DISPLAY_ALIGN (ElementPtr->next.location.x); + Origin.y = DISPLAY_ALIGN (ElementPtr->next.location.y); + } + + dx = DISPLAY_ALIGN (ElementPtr->next.location.x) - Origin.x; + dx = WRAP_DELTA_X (dx); + dy = DISPLAY_ALIGN (ElementPtr->next.location.y) - Origin.y; + dy = WRAP_DELTA_Y (dy); + + if (sides_active <= 2 || ElementPtr->playerNr == 0) + { + Origin.x = DISPLAY_ALIGN (Origin.x + (dx >> 1)); + Origin.y = DISPLAY_ALIGN (Origin.y + (dy >> 1)); + + if (dx < 0) + dx = -dx; + if (dy < 0) + dy = -dy; + max_reduction = CalcReduction (dx, dy); + } + else if (max_reduction > opt_max_zoom_out + && min_reduction <= opt_max_zoom_out) + { + Origin.x = DISPLAY_ALIGN (Origin.x + (dx >> 1)); + Origin.y = DISPLAY_ALIGN (Origin.y + (dy >> 1)); + + if (dx < 0) + dx = -dx; + if (dy < 0) + dy = -dy; + min_reduction = CalcReduction (dx, dy); + } + else + { + SIZE reduction; + + if (dx < 0) + dx = -dx; + if (dy < 0) + dy = -dy; + reduction = CalcReduction (dx << 1, dy << 1); + + if (min_reduction > opt_max_zoom_out + || reduction < min_reduction) + min_reduction = reduction; + } +// log_add (log_Debug, "dx = %d dy = %d min_red = %d max_red = %d", +// dx, dy, min_reduction, max_reduction); + } + + UnlockElement (hElement); + hElement = hNextElement; + } + + if ((min_reduction > opt_max_zoom_out || min_reduction <= max_reduction) + && (min_reduction = max_reduction) > opt_max_zoom_out + && (min_reduction = zoom_out) > opt_max_zoom_out) + { + if (optMeleeScale == TFB_SCALE_STEP) + min_reduction = 0; + else + min_reduction = 1 << ZOOM_SHIFT; + } + +#ifdef KDEBUG + log_add (log_Debug, "PreProcess: exit"); +#endif + return (CalcView (&Origin, min_reduction, pscroll_x, pscroll_y, ships_alive)); +} + +void +InsertPrim (PRIM_LINKS *pLinks, COUNT primIndex, COUNT iPI) +{ + COUNT Link; + PRIM_LINKS PL; + + if (iPI == END_OF_LIST) + { + Link = GetSuccLink (*pLinks); /* get tail */ + if (Link == END_OF_LIST) + *pLinks = MakeLinks (primIndex, primIndex); + else + *pLinks = MakeLinks (GetPredLink (*pLinks), primIndex); + } + else + { + PL = GetPrimLinks (&DisplayArray[iPI]); + if (iPI != GetPredLink (*pLinks)) /* if not the head */ + Link = GetPredLink (PL); + else + { + Link = END_OF_LIST; + *pLinks = MakeLinks (primIndex, GetSuccLink (*pLinks)); + } + SetPrimLinks (&DisplayArray[iPI], primIndex, GetSuccLink (PL)); + } + + if (Link != END_OF_LIST) + { + PL = GetPrimLinks (&DisplayArray[Link]); + SetPrimLinks (&DisplayArray[Link], GetPredLink (PL), primIndex); + } + SetPrimLinks (&DisplayArray[primIndex], Link, iPI); +} + +PRIM_LINKS DisplayLinks; + +static inline COORD +CalcDisplayCoord (COORD c, COORD orgc, SIZE reduction) +{ + if (optMeleeScale == TFB_SCALE_STEP) + { /* old fixed-step zoom style */ + return (c - orgc) >> reduction; + } + else + { /* new continuous zoom style */ + return ((c - orgc) << ZOOM_SHIFT) / reduction; + } +} + +static void +PostProcessQueue (VIEW_STATE view_state, SIZE scroll_x, + SIZE scroll_y) +{ + POINT delta; + SIZE reduction; + HELEMENT hElement; + +#ifdef KDEBUG + log_add (log_Debug, "PostProcess:"); +#endif + if (optMeleeScale == TFB_SCALE_STEP) + reduction = zoom_out + ONE_SHIFT; + else + reduction = zoom_out << ONE_SHIFT; + + hElement = GetHeadElement (); + while (hElement != 0) + { + ELEMENT_FLAGS state_flags; + ELEMENT *ElementPtr; + HELEMENT hNextElement; + + LockElement (hElement, &ElementPtr); + + state_flags = ElementPtr->state_flags; + if (state_flags & PRE_PROCESS) + { + if (!(state_flags & COLLISION)) + ElementPtr->state_flags &= ~DEFY_PHYSICS; + else + ElementPtr->state_flags &= ~COLLISION; + + if (state_flags & POST_PROCESS) + { + delta.x = 0; + delta.y = 0; + } + else + { + delta.x = scroll_x; + delta.y = scroll_y; + } + } + else + { + HELEMENT hPostElement; + + hPostElement = hElement; + do + { + ELEMENT *PostElementPtr; + + LockElement (hPostElement, &PostElementPtr); + if (!(PostElementPtr->state_flags & PRE_PROCESS)) + PreProcess (PostElementPtr); + hNextElement = GetSuccElement (PostElementPtr); + + if (CollidingElement (PostElementPtr) + && !(PostElementPtr->state_flags & COLLISION)) + ProcessCollisions (GetHeadElement (), PostElementPtr, + MAX_TIME_VALUE, PRE_PROCESS | POST_PROCESS); + UnlockElement (hPostElement); + hPostElement = hNextElement; + } while (hPostElement != 0); + + scroll_x = 0; + scroll_y = 0; + delta.x = 0; + delta.y = 0; + /* because these are newly added elements that are + * already in adjusted coordinates */ + state_flags = ElementPtr->state_flags; + } + + if (state_flags & DISAPPEARING) + { + hNextElement = GetSuccElement (ElementPtr); + UnlockElement (hElement); + RemoveElement (hElement); + FreeElement (hElement); + } + else + { + GRAPHICS_PRIM ObjType; + + ObjType = GetPrimType (&DisplayArray[ElementPtr->PrimIndex]); + if (view_state != VIEW_STABLE + || (state_flags & (APPEARING | CHANGING))) + { + POINT next; + + if (ObjType == LINE_PRIM) + { + SIZE dx, dy; + + dx = ElementPtr->next.location.x + - ElementPtr->current.location.x; + dy = ElementPtr->next.location.y + - ElementPtr->current.location.y; + + next.x = WRAP_X (ElementPtr->current.location.x + delta.x); + next.y = WRAP_Y (ElementPtr->current.location.y + delta.y); + DisplayArray[ElementPtr->PrimIndex].Object.Line.first.x = + CalcDisplayCoord (next.x, SpaceOrg.x, reduction); + DisplayArray[ElementPtr->PrimIndex].Object.Line.first.y = + CalcDisplayCoord (next.y, SpaceOrg.y, reduction); + + next.x += dx; + next.y += dy; + DisplayArray[ElementPtr->PrimIndex].Object.Line.second.x = + CalcDisplayCoord (next.x, SpaceOrg.x, reduction); + DisplayArray[ElementPtr->PrimIndex].Object.Line.second.y = + CalcDisplayCoord (next.y, SpaceOrg.y, reduction); + } + else + { + next.x = WRAP_X (ElementPtr->next.location.x + delta.x); + next.y = WRAP_Y (ElementPtr->next.location.y + delta.y); + DisplayArray[ElementPtr->PrimIndex].Object.Point.x = + CalcDisplayCoord (next.x, SpaceOrg.x, reduction); + DisplayArray[ElementPtr->PrimIndex].Object.Point.y = + CalcDisplayCoord (next.y, SpaceOrg.y, reduction); + + if (ObjType == STAMP_PRIM || ObjType == STAMPFILL_PRIM) + { + if (view_state == VIEW_CHANGE + || (state_flags & (APPEARING | CHANGING))) + { + COUNT index, scale = GSCALE_IDENTITY; + + if (optMeleeScale == TFB_SCALE_STEP) + index = zoom_out; + else + CALC_ZOOM_STUFF (&index, &scale); + + ElementPtr->next.image.frame = SetEquFrameIndex ( + ElementPtr->next.image.farray[index], + ElementPtr->next.image.frame); + + if (optMeleeScale == TFB_SCALE_TRILINEAR && + index < 2 && scale != GSCALE_IDENTITY) + { + // enqueues drawcommand to assign next + // (smaller) zoom level image as mipmap, + // needed for trilinear scaling + + FRAME frame = ElementPtr->next.image.frame; + FRAME mmframe = SetEquFrameIndex ( + ElementPtr->next.image.farray[ + index + 1], frame); + + // TODO: This is currently hacky, this code + // really should not dereference FRAME. + // Perhaps make mipmap part of STAMP prim? + if (frame && mmframe) + { + HOT_SPOT mmhs = GetFrameHot (mmframe); + TFB_DrawScreen_SetMipmap (frame->image, + mmframe->image, mmhs.x, mmhs.y); + } + } + } + DisplayArray[ElementPtr->PrimIndex].Object.Stamp.frame = + ElementPtr->next.image.frame; + } + } + + ElementPtr->next.location = next; + } + + PostProcess (ElementPtr); + + if (ObjType < NUM_PRIMS) + InsertPrim (&DisplayLinks, ElementPtr->PrimIndex, END_OF_LIST); + + hNextElement = GetSuccElement (ElementPtr); + UnlockElement (hElement); + } + + hElement = hNextElement; + } +#ifdef KDEBUG + log_add (log_Debug, "PostProcess: exit"); +#endif +} + +void +InitDisplayList (void) +{ + COUNT i; + + if (optMeleeScale == TFB_SCALE_STEP) + { + zoom_out = MAX_VIS_REDUCTION + 1; + opt_max_zoom_out = MAX_VIS_REDUCTION; + } + else + { + zoom_out = MAX_ZOOM_OUT + (1 << ZOOM_SHIFT); + opt_max_zoom_out = MAX_ZOOM_OUT; + } + + ReinitQueue (&disp_q); + + for (i = 0; i < MAX_DISPLAY_PRIMS; ++i) + SetPrimLinks (&DisplayArray[i], END_OF_LIST, i + 1); + SetPrimLinks (&DisplayArray[i - 1], END_OF_LIST, END_OF_LIST); + DisplayFreeList = 0; + DisplayLinks = MakeLinks (END_OF_LIST, END_OF_LIST); +} + +UWORD nth_frame = 0; + +void +RedrawQueue (BOOLEAN clear) +{ + SIZE scroll_x, scroll_y; + VIEW_STATE view_state; + + SetContext (StatusContext); + + view_state = PreProcessQueue (&scroll_x, &scroll_y); + PostProcessQueue (view_state, scroll_x, scroll_y); + + if (optStereoSFX) + UpdateSoundPositions (); + + SetContext (SpaceContext); + if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE + || !(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))) + { + BYTE skip_frames; + + skip_frames = HIBYTE (nth_frame); + if (skip_frames != (BYTE)~0 + && (skip_frames == 0 || (--nth_frame & 0x00FF) == 0)) + { + nth_frame += skip_frames; + if (clear) + ClearDrawable (); // this is for BATCH_BUILD_PAGE effect, but not scaled by SetGraphicScale + + if (optMeleeScale != TFB_SCALE_STEP) + { + COUNT index, scale; + + CALC_ZOOM_STUFF (&index, &scale); + SetGraphicScale (scale); + } + + DrawBatch (DisplayArray, DisplayLinks, 0);//BATCH_BUILD_PAGE); + SetGraphicScale (0); + } + + FlushSounds (); + } + else + { // sfx queue needs to be flushed when aborting + ProcessSound ((SOUND)~0, NULL); + FlushSounds (); + } + + DisplayLinks = MakeLinks (END_OF_LIST, END_OF_LIST); +} + +// Set the hTarget field to 0 for all elements in the display list that +// have hTarget set to ElementPtr. +void +Untarget (ELEMENT *ElementPtr) +{ + HELEMENT hElement, hNextElement; + + for (hElement = GetHeadElement (); hElement; hElement = hNextElement) + { + HELEMENT hTarget; + ELEMENT *ListPtr; + + LockElement (hElement, &ListPtr); + hNextElement = GetSuccElement (ListPtr); + + hTarget = ListPtr->hTarget; + if (hTarget) + { + ELEMENT *TargetElementPtr; + + LockElement (hTarget, &TargetElementPtr); + if (TargetElementPtr == ElementPtr) + ListPtr->hTarget = 0; + UnlockElement (hTarget); + } + + UnlockElement (hElement); + } +} + +void +RemoveElement (HLINK hLink) +{ + if (optStereoSFX) + { + ELEMENT *ElementPtr; + + LockElement (hLink, &ElementPtr); + if (ElementPtr != NULL) + RemoveSoundsForObject(ElementPtr); + UnlockElement (hLink); + } + RemoveQueue (&disp_q, hLink); +} + + |