From 679461cd7908bd408cf384840ef1c7af78e5c597 Mon Sep 17 00:00:00 2001 From: Ketmar Dark Date: Fri, 1 Sep 2017 18:54:36 +0300 Subject: [PATCH] faster particles awakening --- src/game/g_gfx.pas | 288 +++++++++++++++++++++---------------------- src/game/g_map.pas | 20 ++- src/game/g_panel.pas | 19 ++- 3 files changed, 161 insertions(+), 166 deletions(-) diff --git a/src/game/g_gfx.pas b/src/game/g_gfx.pas index 7903e13..b7e5ff7 100644 --- a/src/game/g_gfx.pas +++ b/src/game/g_gfx.pas @@ -54,10 +54,10 @@ function g_GFX_GetMax(): Integer; procedure g_GFX_OnceAnim(X, Y: Integer; Anim: TAnimation; AnimType: Byte = 0); -procedure g_Mark(x, y, Width, Height: Integer; t: Byte; st: Boolean=true); +procedure g_Mark (x, y, Width, Height: Integer; t: Byte; st: Boolean=true); -procedure g_GFX_Update(); -procedure g_GFX_Draw(); +procedure g_GFX_Update (); +procedure g_GFX_Draw (); var @@ -103,6 +103,9 @@ type procedure thinkerBubble (); procedure thinkerWater (); + function isSleeping (): Boolean; inline; + procedure awake (); inline; + function alive (): Boolean; inline; procedure die (); inline; procedure think (); inline; @@ -128,14 +131,113 @@ var OnceAnims: array of TOnceAnim; MaxParticles: Integer; CurrentParticle: Integer; + // awakeMap has one bit for each map grid cell; on g_Mark, + // corresponding bits will be set, and in `think()` all particles + // in marked cells will be awaken + awakeMap: packed array of LongWord = nil; + awakeMapH: Integer = -1; + awakeMapW: Integer = -1; + awakeMinX, awakeMinY: Integer; + + +// ////////////////////////////////////////////////////////////////////////// // +// HACK! using mapgrid +procedure awmClear (); inline; +begin + if (awakeMapW > 0) then FillDWord(awakeMap[0], Length(awakeMap), 0); +end; + + +procedure awmSetup (); +begin + assert(mapGrid <> nil); + awakeMapW := (mapGrid.gridWidth+mapGrid.tileSize-1) div mapGrid.tileSize; + awakeMapW := (awakeMapW+31) div 32; // LongWord has 32 bits ;-) + awakeMapH := (mapGrid.gridHeight+mapGrid.tileSize-1) div mapGrid.tileSize; + 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]); + {$ENDIF} + awmClear(); +end; + + +function awmIsSet (x, y: Integer): Boolean; inline; +begin + 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 DEFINED(D2F_DEBUG)} + assert(y*awakeMapW+x div 32 < Length(awakeMap)); + {$ENDIF} + result := ((awakeMap[y*awakeMapW+x div 32] and (LongWord(1) shl (x mod 32))) <> 0); + end + else + begin + result := false; + end; +end; + + +procedure awmSet (x, y: Integer); inline; +var + v: PLongWord; +begin + 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 DEFINED(D2F_DEBUG)} + assert(y*awakeMapW+x div 32 < Length(awakeMap)); + {$ENDIF} + v := @awakeMap[y*awakeMapW+x div 32]; + v^ := v^ or (LongWord(1) shl (x mod 32)); + end; +end; // ////////////////////////////////////////////////////////////////////////// // function TParticle.alive (): Boolean; inline; begin result := (State <> STATE_FREE); end; procedure TParticle.die (); inline; begin State := STATE_FREE; end; +function TParticle.isSleeping (): Boolean; inline; +begin + result := alive and (onGround or (not justSticked and (State = STATE_STICK))); +end; + +procedure TParticle.awake (); inline; +begin + if {alive and} (onGround or (not justSticked and (State = STATE_STICK))) then + begin + // wakeup this particle + { + if (part.ParticleType = PARTICLE_SPARK) then + begin + e_LogWritefln('waking up particle of type %s; justSticked=%s; onGround=%s; VelY=%s; AccelY=%s', [part.ParticleType, part.justSticked, part.onGround, part.VelY, part.AccelY]); + end; + } + justSticked := true; // so sticked state will be re-evaluated + if onGround then + begin + if (VelY = 0) then VelY := 0.1; + if (AccelY = 0) then AccelY := 0.5; + end; + onGround := false; // so onground state will be re-evaluated + awaken := true; + end; +end; + + procedure TParticle.think (); inline; begin + // awake sleeping particle, if necessary + if isSleeping then + begin + if awmIsSet(X, Y) then awake(); + end; case ParticleType of PARTICLE_BLOOD: thinkerBlood(); PARTICLE_SPARK: thinkerSpark(); @@ -196,86 +298,32 @@ begin end; +// st: set mark +// t: mark type +// currently unused procedure g_Mark(x, y, Width, Height: Integer; t: Byte; st: Boolean=true); -{$IF not DEFINED(HAS_COLLIDE_BITMAP)} var - part: PParticle; - f: Integer; + cx, ex, ey: Integer; + ts: Integer; begin - for f := 0 to High(Particles) do + 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 - part := @Particles[f]; - if part.alive and (part.onGround or (not part.justSticked and (part.State = STATE_STICK))) and - (part.X >= x-2) and (part.Y >= y-2) and (part.X < x+Width+4) and (part.Y < y+Height+4) then + cx := x; + while (cx < ex) do begin - // wakup this particle - { - if (part.ParticleType = PARTICLE_SPARK) then - begin - e_LogWritefln('waking up particle of type %s; justSticked=%s; onGround=%s; VelY=%s; AccelY=%s', [part.ParticleType, part.justSticked, part.onGround, part.VelY, part.AccelY]); - end; - } - part.justSticked := true; // so sticked state will be re-evaluated - if part.onGround then - begin - if (part.VelY = 0) then part.VelY := 0.1; - if (part.AccelY = 0) then part.AccelY := 0.5; - end; - part.onGround := false; // so onground state will be re-evaluated - part.awaken := true; + awmSet(cx, y); + Inc(cx, ts); end; + Inc(y, ts); end; end; -{$ELSE} -var - yy, y2, xx, x2: Integer; -begin - if x < 0 then - begin - Width := Width + x; - x := 0; - end; - - if Width < 0 then - Exit; - - if y < 0 then - begin - Height := Height + y; - y := 0; - end; - - if Height < 0 then - Exit; - - if x > gMapInfo.Width then - Exit; - if y > gMapInfo.Height then - Exit; - - y2 := y + Height - 1; - if y2 > gMapInfo.Height then - y2 := gMapInfo.Height; - - x2 := x + Width - 1; - if x2 > gMapInfo.Width then - x2 := gMapInfo.Width; - - if st then - begin // Óñòàíîâèòü ïðèçíàê - for yy := y to y2 do - for xx := x to x2 do - gCollideMap[yy][xx] := gCollideMap[yy][xx] or t; - end - else - begin // Óáðàòü ïðèçíàê - t := not t; - for yy := y to y2 do - for xx := x to x2 do - gCollideMap[yy][xx] := gCollideMap[yy][xx] and t; - end; -end; -{$ENDIF} {$IF DEFINED(HAS_COLLIDE_BITMAP)} @@ -283,75 +331,9 @@ procedure CreateCollideMap(); var a: Integer; begin - g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False); - SetLength(gCollideMap, gMapInfo.Height+1); - for a := 0 to High(gCollideMap) do - SetLength(gCollideMap[a], gMapInfo.Width+1); - - if gWater <> nil then - begin - g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 2/6', 0, True); - for a := 0 to High(gWater) do - with gWater[a] do - g_Mark(X, Y, Width, Height, MARK_WATER, True); - end; - - if gAcid1 <> nil then - begin - g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 3/6', 0, True); - for a := 0 to High(gAcid1) do - with gAcid1[a] do - g_Mark(X, Y, Width, Height, MARK_ACID, True); - end; - - if gAcid2 <> nil then - begin - g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 4/6', 0, True); - for a := 0 to High(gAcid2) do - with gAcid2[a] do - g_Mark(X, Y, Width, Height, MARK_ACID, True); - end; - - if gLifts <> nil then - begin - g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 5/6', 0, True); - for a := 0 to High(gLifts) do - with gLifts[a] do - begin - g_Mark(X, Y, Width, Height, MARK_LIFT, False); - - if LiftType = 0 then - g_Mark(X, Y, Width, Height, MARK_LIFTUP, True) - else if LiftType = 1 then - g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True) - else if LiftType = 2 then - g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True) - else if LiftType = 3 then - g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True) - end; - end; - - if gWalls <> nil then - begin - g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 6/6', 0, True); - for a := 0 to High(gWalls) do - begin - if gWalls[a].Door then - begin - // Çàêðûòàÿ äâåðü: - if gWalls[a].Enabled then - with gWalls[a] do - g_Mark(X, Y, Width, Height, MARK_DOOR, True) - else // Îòêðûòàÿ äâåðü: - if gWalls[a].Enabled then - with gWalls[a] do - g_Mark(X, Y, Width, Height, MARK_DOOR, False); - end - else // Ñòåíà - with gWalls[a] do - g_Mark(X, Y, Width, Height, MARK_WALL, True); - end; - end; + //g_Game_SetLoadingText(_lc[I_LOAD_COLLIDE_MAP]+' 1/6', 0, False); + //SetLength(gCollideMap, gMapInfo.Height+1); + //for a := 0 to High(gCollideMap) do SetLength(gCollideMap[a], gMapInfo.Width+1); end; {$ENDIF} @@ -359,6 +341,7 @@ end; procedure g_GFX_Init(); begin //CreateCollideMap(); + awmSetup(); {$IFDEF HEADLESS} gpart_dbg_enabled := False; {$ENDIF} @@ -374,13 +357,16 @@ begin for a := 0 to High(Particles) do Particles[a].die(); CurrentParticle := 0; - if OnceAnims <> nil then + if (OnceAnims <> nil) then begin - for a := 0 to High(OnceAnims) do - OnceAnims[a].Animation.Free(); - + for a := 0 to High(OnceAnims) do OnceAnims[a].Animation.Free(); OnceAnims := nil; end; + + awakeMap := nil; + // why not? + awakeMapH := -1; + awakeMapW := -1; end; @@ -1664,7 +1650,8 @@ var len: Integer; begin if not gpart_dbg_enabled then exit; - if Particles <> nil then + + if (Particles <> nil) then begin w := gMapInfo.Width; h := gMapInfo.Height; @@ -1687,6 +1674,9 @@ begin end; // for end; // Particles <> nil + // clear awake map + awmClear(); + if OnceAnims <> nil then begin for a := 0 to High(OnceAnims) do diff --git a/src/game/g_map.pas b/src/game/g_map.pas index 5c3a8a1..ff6e757 100644 --- a/src/game/g_map.pas +++ b/src/game/g_map.pas @@ -2804,7 +2804,7 @@ var begin pan := gWalls[ID]; pan.Enabled := True; - g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, True); + g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, true); mapGrid.proxyEnabled[pan.proxyId] := true; //if (pan.proxyId >= 0) then mapGrid.proxyEnabled[pan.proxyId] := true @@ -2823,7 +2823,7 @@ var begin pan := gWalls[ID]; pan.Enabled := False; - g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, False); + g_Mark(pan.X, pan.Y, pan.Width, pan.Height, MARK_DOOR, false); mapGrid.proxyEnabled[pan.proxyId] := false; //if (pan.proxyId >= 0) then begin mapGrid.removeBody(pan.proxyId); pan.proxyId := -1; end; @@ -2872,17 +2872,15 @@ begin begin LiftType := t; - g_Mark(X, Y, Width, Height, MARK_LIFT, False); + g_Mark(X, Y, Width, Height, MARK_LIFT, false); //TODO: make separate lift tags, and change tag here - if LiftType = 0 then - g_Mark(X, Y, Width, Height, MARK_LIFTUP, True) - else if LiftType = 1 then - g_Mark(X, Y, Width, Height, MARK_LIFTDOWN, True) - else if LiftType = 2 then - g_Mark(X, Y, Width, Height, MARK_LIFTLEFT, True) - else if LiftType = 3 then - g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT, True); + case LiftType of + 0: g_Mark(X, Y, Width, Height, MARK_LIFTUP); + 1: g_Mark(X, Y, Width, Height, MARK_LIFTDOWN); + 2: g_Mark(X, Y, Width, Height, MARK_LIFTLEFT); + 3: g_Mark(X, Y, Width, Height, MARK_LIFTRIGHT); + end; if g_Game_IsServer and g_Game_IsNet then MH_SEND_PanelState(PanelType, ID); end; diff --git a/src/game/g_panel.pas b/src/game/g_panel.pas index 634e21b..685c245 100644 --- a/src/game/g_panel.pas +++ b/src/game/g_panel.pas @@ -120,7 +120,7 @@ type implementation uses - SysUtils, g_basic, g_map, g_game, e_graphics, + SysUtils, g_basic, g_map, g_game, g_gfx, e_graphics, g_console, g_language, g_monsters, g_player, e_log, GL; const @@ -501,18 +501,25 @@ begin monMoveListUsed := 0; nx := X+mMovingSpeed.X; ny := Y+mMovingSpeed.Y; + // move monsters on lifts g_Mons_ForEachAt(X, Y-1, Width, 1, monMove); + // push monsters g_Mons_ForEachAt(nx, ny, Width, Height, monPush); + // move and push players for f := 0 to High(gPlayers) do plrMove(gPlayers[f]); + // reverse moving direction, if necessary if (mMovingSpeed.X < 0) and (nx <= mMovingStart.X) then mMovingSpeed.X := -mMovingSpeed.X else if (mMovingSpeed.X > 0) and (nx >= mMovingEnd.X) then mMovingSpeed.X := -mMovingSpeed.X; if (mMovingSpeed.Y < 0) and (ny <= mMovingStart.Y) then mMovingSpeed.Y := -mMovingSpeed.Y else if (mMovingSpeed.Y > 0) and (ny >= mMovingEnd.Y) then mMovingSpeed.Y := -mMovingSpeed.Y; - //!!!g_Mark(X, Y, Width, Height, MARK_FREE); + // awake particles + g_Mark(X, Y, Width, Height, MARK_WALL, false); X := nx; Y := ny; - //!!!g_Mark(X, Y, Width, Height, MARK_WALL); + g_Mark(nx, ny, Width, Height, MARK_WALL); + // fix grid if (proxyId >= 0) then mapGrid.moveBody(proxyId, nx, ny); + // notify moved monsters about their movement for f := 0 to monMoveListUsed-1 do monMoveList[f].positionChanged(); end; end; @@ -695,7 +702,7 @@ procedure TPanel.LoadState(var Mem: TBinMemoryReader); var sig: DWORD; anim: Boolean; - ox, oy: Integer; + //ox, oy: Integer; begin if (Mem = nil) then exit; //if not SaveIt then exit; @@ -713,8 +720,8 @@ begin // Íîìåð òåêóùåé òåêñòóðû: Mem.ReadInt(FCurTexture); // Êîîðäû - ox := FX; - oy := FY; + //ox := FX; + //oy := FY; Mem.ReadInt(FX); Mem.ReadInt(FY); //e_LogWritefln('panel %s(%s): old=(%s,%s); new=(%s,%s); delta=(%s,%s)', [arrIdx, proxyId, ox, oy, FX, FY, FX-ox, FY-oy]); -- 2.29.2