DEADSOFTWARE

faster particles awakening
authorKetmar Dark <ketmar@ketmar.no-ip.org>
Fri, 1 Sep 2017 15:54:36 +0000 (18:54 +0300)
committerKetmar Dark <ketmar@ketmar.no-ip.org>
Fri, 1 Sep 2017 19:17:53 +0000 (22:17 +0300)
src/game/g_gfx.pas
src/game/g_map.pas
src/game/g_panel.pas

index 7903e13b28ca2a8d139e29b42ab64c4c3f937911..b7e5ff73c2f21188708b7dec90b91b43b3789494 100644 (file)
@@ -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
index 5c3a8a17ef5ef457e970dc450bc6ef9e03c4a321..ff6e7570c3b35863ce5ad0dd26127907ed12d8ce 100644 (file)
@@ -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;
index 634e21bfca8bd5ca3672e461352666a32f7cadec..685c245ffe075590491851c94d6e95699296e8c1 100644 (file)
@@ -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]);