X-Git-Url: https://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_gfx.pas;h=ff7089b46f31177abfc89971a3f66a28b8838e36;hb=c16bfd4a6ac97b3ea5b3300a0e6eed93d44ace87;hp=c365e662fe81db704f64ea09438007fcbd7bcc46;hpb=be528d3dadda758afda02a5dbf7b89e05277a7a6;p=d2df-sdl.git diff --git a/src/game/g_gfx.pas b/src/game/g_gfx.pas index c365e66..ff7089b 100644 --- a/src/game/g_gfx.pas +++ b/src/game/g_gfx.pas @@ -14,6 +14,8 @@ * along with this program. If not, see . *) {$INCLUDE ../shared/a_modes.inc} +{.$DEFINE D2F_DEBUG_FALL_MPLAT} +{/$DEFINE D2F_DEBUG_PART_AWAKE} unit g_gfx; interface @@ -69,6 +71,10 @@ var gpart_dbg_phys_enabled: Boolean = true; +//WARNING: only for Holmes! +function awmIsSetHolmes (x, y: Integer): Boolean; inline; + + implementation uses @@ -121,6 +127,8 @@ type procedure freeze (); inline; // remove velocities and acceleration procedure sleep (); inline; // switch to sleep mode + function checkAirStreams (): Boolean; // `true`: affected by air stream + function alive (): Boolean; inline; procedure die (); inline; procedure think (); inline; @@ -146,12 +154,50 @@ var awakeMapW: Integer = -1; awakeMinX, awakeMinY: Integer; awakeDirty: Boolean = false; + {$IF DEFINED(D2F_DEBUG_PART_AWAKE)} + awakeMapHlm: packed array of LongWord = nil; + {$ENDIF} + + +// ////////////////////////////////////////////////////////////////////////// // +function awmIsSetHolmes (x, y: Integer): Boolean; inline; +begin +{$IF DEFINED(D2F_DEBUG_PART_AWAKE)} + if (Length(awakeMapHlm) = 0) then begin result := false; exit; end; + x := (x-awakeMinX) div mapGrid.tileSize; + y := (y-awakeMinY) div mapGrid.tileSize; + if (x >= 0) and (y >= 0) and (x div 32 < awakeMapW) and (y < awakeMapH) then + begin + if (y*awakeMapW+x div 32 < Length(awakeMapHlm)) then + begin + result := ((awakeMapHlm[y*awakeMapW+x div 32] and (LongWord(1) shl (x mod 32))) <> 0); + end + else + begin + result := false; + end; + end + else + begin + result := false; + end; +{$ELSE} + result := false; +{$ENDIF} +end; // ////////////////////////////////////////////////////////////////////////// // // HACK! using mapgrid procedure awmClear (); inline; begin + {$IF DEFINED(D2F_DEBUG_PART_AWAKE)} + if (Length(awakeMap) > 0) then + begin + if (Length(awakeMapHlm) <> Length(awakeMap)) then SetLength(awakeMapHlm, Length(awakeMap)); + Move(awakeMap[0], awakeMapHlm[0], Length(awakeMap)*sizeof(awakeMap[0])); + end; + {$ENDIF} if awakeDirty and (awakeMapW > 0) then begin FillDWord(awakeMap[0], Length(awakeMap), 0); @@ -169,9 +215,13 @@ begin awakeMinX := mapGrid.gridX0; awakeMinY := mapGrid.gridY0; SetLength(awakeMap, awakeMapW*awakeMapH); - {$IF DEFINED(D2F_DEBUG)} - e_LogWritefln('particle awake map: %sx%s (for grid of size %sx%s)', [awakeMapW, awakeMapH, mapGrid.gridWidth, mapGrid.gridHeight]); + {$IF DEFINED(D2F_DEBUG_PART_AWAKE)} + SetLength(awakeMapHlm, awakeMapW*awakeMapH); + FillDWord(awakeMapHlm[0], Length(awakeMapHlm), 0); {$ENDIF} + //{$IF DEFINED(D2F_DEBUG)} + e_LogWritefln('particle awake map: %sx%s (for grid of size %sx%s)', [awakeMapW, awakeMapH, mapGrid.gridWidth, mapGrid.gridHeight]); + //{$ENDIF} awakeDirty := true; awmClear(); end; @@ -213,6 +263,55 @@ begin end; +// ////////////////////////////////////////////////////////////////////////// // +// st: set mark +// t: mark type +// currently unused +procedure g_Mark (x, y, Width, Height: Integer; t: Byte; st: Boolean=true); +const Extrude = 1; +var + dx, dy, ex, ey: Integer; + v: PLongWord; +begin + if (not gpart_dbg_enabled) or (not gpart_dbg_phys_enabled) then exit; + if (awakeMapW < 1) or (awakeMapH < 1) then exit; + + if (Width < 1) or (Height < 1) then exit; + + // make some border, so we'll hit particles around the panel + ex := x+Width+Extrude-1-awakeMinX; + ey := y+Height+Extrude-1-awakeMinY; + x := (x-Extrude)-awakeMinX; + y := (y-Extrude)-awakeMinY; + + x := x div mapGrid.tileSize; + y := y div mapGrid.tileSize; + ex := ex div mapGrid.tileSize; + ey := ey div mapGrid.tileSize; + + // has something to do? + if (ex < 0) or (ey < 0) or (x >= awakeMapW*32) or (y >= awakeMapH) then exit; + if (x < 0) then x := 0; + if (y < 0) then y := 0; + if (ex >= awakeMapW*32) then ex := awakeMapW*32-1; + if (ey >= awakeMapH) then ey := awakeMapH; + + awakeDirty := true; + for dy := y to ey do + begin + for dx := x to ex do + begin + {$IF DEFINED(D2F_DEBUG)} + assert((dx >= 0) and (dy >= 0) and (dx div 32 < awakeMapW) and (dy < awakeMapH)); + assert(dy*awakeMapW+dx div 32 < Length(awakeMap)); + {$ENDIF} + v := @awakeMap[dy*awakeMapW+dx div 32]; + v^ := v^ or (LongWord(1) shl (dx mod 32)); + end; + end; +end; + + // ////////////////////////////////////////////////////////////////////////// // function TParticle.alive (): Boolean; inline; begin result := (state <> TPartState.Free); end; procedure TParticle.die (); inline; begin state := TPartState.Free; end; @@ -228,11 +327,50 @@ begin end; +// `true`: affected by air stream +function TParticle.checkAirStreams (): Boolean; +var + pan: TPanel; +begin + pan := g_Map_PanelAtPoint(x, y, GridTagLift); + result := (pan <> nil); + if result then + begin + if ((pan.PanelType and PANEL_LIFTUP) <> 0) then + begin + if (velY > -4-Random(3)) then velY -= 0.8; + if (abs(velX) > 0.1) then velX -= velX/10.0; + velX += (Random-Random)*0.2; + accelY := 0.15; + end + else if ((pan.PanelType and PANEL_LIFTLEFT) <> 0) then + begin + if (velX > -8-Random(3)) then velX -= 0.8; + accelY := 0.15; + end + else if ((pan.PanelType and PANEL_LIFTRIGHT) <> 0) then + begin + if (velX < 8+Random(3)) then velX += 0.8; + accelY := 0.15; + end + else + begin + result := false; + end; + // awake + if result and (state = TPartState.Sleeping) then state := TPartState.Normal; + end; +end; + + // switch to sleep mode procedure TParticle.sleep (); inline; begin - state := TPartState.Sleeping; - freeze(); + if not checkAirStreams() then + begin + state := TPartState.Sleeping; + freeze(); + end; end; @@ -317,21 +455,54 @@ end; procedure TParticle.think (); inline; + procedure awake (); + begin + if (state = TPartState.Stuck) then + begin + //writeln('awaking particle at (', x, ',', y, ')'); + if (stickDX = 0) then + begin + state := TPartState.Normal; // stuck to a ceiling + end + else + begin + // stuck to a wall, check if wall is still there + if (wallEndY <> Unknown) then + begin + wallEndY := Unknown; + if (g_Map_PanelAtPoint(x+stickDX, y, GridTagObstacle) = nil) then + begin + // a wall was moved out, start falling + state := TPartState.Normal; + if (velY = 0) then velY := 0.1; + if (accelY = 0) then accelY := 0.5; + end; + end; + end; + end + else + begin + state := TPartState.Normal; + if (velY = 0) then velY := 0.1; + if (accelY = 0) then accelY := 0.5; + end; + floorY := Unknown; + ceilingY := Unknown; + end; + begin // awake sleeping particle, if necessary if awakeDirty then begin + if awmIsSet(x, y) then awake(); + { case state of TPartState.Sleeping, TPartState.Stuck: - if awmIsSet(x, y) then - begin - state := TPartState.Normal; - floorY := Unknown; - ceilingY := Unknown; - if (velY = 0) then velY := 0.1; - if (accelY = 0) then accelY := 0.5; - end; + if awmIsSet(x, y) then awake(); + else + if (env = TEnvType.EWall) and awmIsSet(x, y) then awake(); end; + } end; case particleType of TPartType.Blood, TPartType.Water: thinkerBloodAndWater(); @@ -380,41 +551,12 @@ procedure TParticle.thinkerBloodAndWater (); TPartType.Water: result := (Random(30) = 15); else raise Exception.Create('internal error in particle engine: drip'); end; - if result then begin velY := 0.5; accelY := 0.15; end; - end; - - // `true`: affected by air stream - function checkAirStreams (): Boolean; - var - pan: TPanel; - begin - pan := g_Map_PanelAtPoint(x, y, GridTagLift); - result := (pan <> nil); if result then begin - if ((pan.PanelType and PANEL_LIFTUP) <> 0) then - begin - if (velY > -4-Random(3)) then velY -= 0.8; - if (abs(velX) > 0.1) then velX -= velX/10.0; - velX += (Random-Random)*0.2; - accelY := 0.15; - end - else if ((pan.PanelType and PANEL_LIFTLEFT) <> 0) then - begin - if (velX > -8-Random(3)) then velX -= 0.8; - accelY := 0.15; - end - else if ((pan.PanelType and PANEL_LIFTRIGHT) <> 0) then - begin - if (velX < 8+Random(3)) then velX += 0.8; - accelY := 0.15; - end - else - begin - result := false; - end; - // awake - if result and (state = TPartState.Sleeping) then state := TPartState.Normal; + velY := 0.5; + accelY := 0.15; + // if we're falling from ceiling, switch to normal mode + if (state = TPartState.Stuck) and (stickDX = 0) then state := TPartState.Normal; end; end; @@ -429,7 +571,7 @@ procedure TParticle.thinkerBloodAndWater (); procedure applyGravity (inLiquid: Boolean); begin state := TPartState.Normal; - if (inLiquid) then + if inLiquid then begin velY := 0.5; accelY := 0.15; @@ -442,19 +584,23 @@ procedure TParticle.thinkerBloodAndWater (); end; label - _done; + _done, _gravityagain, _stuckagain; var pan: TPanel; dX, dY: SmallInt; ex, ey: Integer; checkEnv: Boolean; + floorJustTraced: Boolean; + {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)} + oldFloorY: Integer; + {$ENDIF} begin if not gpart_dbg_phys_enabled then goto _done; if gAdvBlood then begin - // still check for air streams when sleeping - if (state = TPartState.Sleeping) then begin checkAirStreams(); goto _done; end; // so blood will dissolve + // still check for air streams when sleeping (no) + if (state = TPartState.Sleeping) then begin {checkAirStreams();} goto _done; end; // so blood will dissolve // process stuck particles if (state = TPartState.Stuck) then @@ -463,7 +609,7 @@ begin if (stickDX = 0) then begin // yeah, stuck to a ceiling - assert(ceilingY <> Unknown); + if (ceilingY = Unknown) then findCeiling(); // dropped from a ceiling? if (y > ceilingY) then begin @@ -481,7 +627,14 @@ begin else begin // stuck to a wall - assert(wallEndY <> Unknown); + if (wallEndY = Unknown) then + begin + // this can happen if mplat was moved out; find new `wallEndY` + findFloor(true); // force trace, just in case + if (floorType = TFloorType.LiquidOut) then env := TEnvType.ELiquid else env := TEnvType.EAir; + mapGrid.traceOrthoRayWhileIn(ex, wallEndY, x+stickDX, y, x+stickDX, floorY+1, (GridTagWall or GridTagDoor or GridTagStep)); + end; + _stuckagain: // floor transition? if (wallEndY <= floorY) and (y >= floorY) then begin @@ -489,8 +642,15 @@ begin case floorType of TFloorType.Wall: // hit the ground begin - sleep(); - goto _done; // nothing to do anymore + // check if our ground wasn't moved since the last scan + findFloor(true); // force trace + if (y = floorY) then + begin + sleep(); + goto _done; // nothing to do anymore + end; + // otherwise, do it again + goto _stuckagain; end; TFloorType.LiquidIn: // entering the liquid begin @@ -525,17 +685,28 @@ begin dX := round(velX); dY := round(velY); + if (state = TPartState.Normal) then checkAirStreams(); + // gravity, if not stuck if (state <> TPartState.Stuck) and (abs(velX) < 0.1) and (abs(velY) < 0.1) then begin - if (floorY = Unknown) then findFloor(); + floorJustTraced := (floorY = Unknown); + if floorJustTraced then findFloor(); + _gravityagain: // floor transition? if (y = floorY) then begin case floorType of TFloorType.Wall: // hit the ground begin - // nothing to do + // check if our ground wasn't moved since the last scan + if not floorJustTraced then + begin + findFloor(true); // force trace + if (floorType = TFloorType.LiquidOut) then env := TEnvType.ELiquid else env := TEnvType.EAir; + if (y <> floorY) then goto _gravityagain; + end; + // otherwise, nothing to do end; TFloorType.LiquidIn: // entering the liquid begin @@ -608,7 +779,8 @@ begin while (dY > 0) do begin // falling down - if (floorY = Unknown) then findFloor(); // need to do this anyway + floorJustTraced := (floorY = Unknown); + if floorJustTraced then findFloor(); if (floorType = TFloorType.LiquidOut) then env := TEnvType.ELiquid else env := TEnvType.EAir; y += dY; //e_LogWritefln('floorY=%s; newy=%s; dY=%s; floorType=%s', [floorY, y, dY, floorType]); @@ -621,6 +793,22 @@ begin case floorType of TFloorType.Wall: // hit the ground begin + // check if our ground wasn't moved since the last scan + if not floorJustTraced then + begin + {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)} + oldFloorY := floorY; + {$ENDIF} + findFloor(true); // force trace + {$IF DEFINED(D2F_DEBUG_FALL_MPLAT)} + if (floorY <> oldFloorY) then + begin + e_LogWritefln('force rescanning vpart at (%s,%s); oldFloorY=%s; floorY=%s', [x, y, oldFloorY, floorY]); + end; + {$ENDIF} + if (floorType = TFloorType.LiquidOut) then env := TEnvType.ELiquid else env := TEnvType.EAir; + if (y <> floorY) then continue; + end; // environment didn't changed hitAFloor(); break; // done with vertical movement @@ -849,19 +1037,19 @@ begin // check for level bounds if (x < g_Map_MinX) or (y < g_Map_MinY) or (x > g_Map_MaxX) or (y > g_Map_MaxY) then continue; - // in what environment we are starting in? - pan := g_Map_PanelAtPoint(x, y, (GridTagObstacle or GridTagLiquid)); - if (pan <> nil) then + // this hack will allow water spawned in water to fly out + // it can happen when player fell from a huge height (see "DOOM2D.WAD:\MAP03", for example) + if (fVelY >= 0) then begin - // either in a wall, or in a liquid - //if ((pan.tag and GridTagObstacle) <> 0) then continue; // don't spawn in walls - //env := TEnvType.ELiquid; - continue; + // in what environment we are starting in? + pan := g_Map_PanelAtPoint(x, y, (GridTagObstacle or GridTagLiquid)); end else begin - env := TEnvType.EAir; + pan := g_Map_PanelAtPoint(x, y, GridTagObstacle); end; + if (pan <> nil) then continue; + env := TEnvType.EAir; // color case color of @@ -1331,37 +1519,6 @@ begin end; -// ////////////////////////////////////////////////////////////////////////// // -// st: set mark -// t: mark type -// currently unused -procedure g_Mark (x, y, Width, Height: Integer; t: Byte; st: Boolean=true); -var - cx, ex, ey: Integer; - ts: Integer; -begin - if not gpart_dbg_enabled then exit; - - if (Width < 1) or (Height < 1) then exit; - // make some border, so we'll hit particles lying around the panel - x -= 1; Width += 2; - y -= 1; Height += 2; - ex := x+Width; - ey := y+Height; - ts := mapGrid.tileSize; - while (y < ey) do - begin - cx := x; - while (cx < ex) do - begin - awmSet(cx, y); - Inc(cx, ts); - end; - Inc(y, ts); - end; -end; - - // ////////////////////////////////////////////////////////////////////////// // procedure g_GFX_Init (); begin @@ -1460,6 +1617,7 @@ end; procedure g_GFX_Draw (); var a, len: Integer; + scaled: Boolean; begin if not gpart_dbg_enabled then exit; @@ -1473,12 +1631,15 @@ begin glBegin(GL_POINTS); + scaled := (g_dbg_scale <> 1.0); + len := High(Particles); for a := 0 to len do begin with Particles[a] do begin - if alive and (x >= sX) and (y >= sY) and (x <= sX+sWidth) and (sY <= sY+sHeight) then + if not alive then continue; + if scaled or ((x >= sX) and (y >= sY) and (x <= sX+sWidth) and (sY <= sY+sHeight)) then begin glColor4ub(red, green, blue, alpha); glVertex2f(x+0.37, y+0.37);