X-Git-Url: http://deadsoftware.ru/gitweb?a=blobdiff_plain;f=src%2Fgame%2Fg_panel.pas;h=7651b48a17326b5ea5efd06b208f55de3e2229f5;hb=e3862af724594e026b2eda52e284d21fc580aa71;hp=d104593da3eeb95a875366263c08c5c78b2554f7;hpb=66d9ad247935e99fe7161849b71ed952cbb85508;p=d2df-sdl.git diff --git a/src/game/g_panel.pas b/src/game/g_panel.pas index d104593..7651b48 100644 --- a/src/game/g_panel.pas +++ b/src/game/g_panel.pas @@ -49,6 +49,13 @@ type mMovingStart: TDFPoint; mMovingEnd: TDFPoint; mMovingActive: Boolean; + mMoveOnce: Boolean; + + mSizeSpeed: TDFSize; + mSizeEnd: TDFSize; + + mEndPosTrig: Integer; + mEndSizeTrig: Integer; private function getx1 (): Integer; inline; @@ -148,6 +155,7 @@ type property movingEndX: Integer read getMovingEndX write setMovingEndX; property movingEndY: Integer read getMovingEndY write setMovingEndY; property movingActive: Boolean read mMovingActive write mMovingActive; + property moveOnce: Boolean read mMoveOnce write mMoveOnce; property isGBack: Boolean read getIsGBack; property isGStep: Boolean read getIsGStep; @@ -163,15 +171,23 @@ type property movingSpeed: TDFPoint read mMovingSpeed write mMovingSpeed; property movingStart: TDFPoint read mMovingStart write mMovingStart; property movingEnd: TDFPoint read mMovingEnd write mMovingEnd; + + property endPosTrigId: Integer read mEndPosTrig write mEndPosTrig; + property endSizeTrigId: Integer read mEndSizeTrig write mEndSizeTrig; end; TPanelArray = Array of TPanel; +var + g_dbgpan_mplat_active: Boolean = {$IF DEFINED(D2F_DEBUG)}true{$ELSE}true{$ENDIF}; + g_dbgpan_mplat_step: Boolean = false; // one step, and stop + + implementation uses - SysUtils, g_basic, g_map, g_game, g_gfx, e_graphics, - g_console, g_language, g_monsters, g_player, e_log, GL; + SysUtils, g_basic, g_map, g_game, g_gfx, e_graphics, g_weapons, g_triggers, + g_console, g_language, g_monsters, g_player, g_grid, e_log, GL, utils; const PANEL_SIGNATURE = $4C4E4150; // 'PANL' @@ -203,6 +219,13 @@ begin mMovingStart := PanelRec.moveStart; mMovingEnd := PanelRec.moveEnd; mMovingActive := PanelRec['move_active'].varvalue; + mMoveOnce := PanelRec.moveOnce; + + mSizeSpeed := PanelRec.sizeSpeed; + mSizeEnd := PanelRec.sizeEnd; + + mEndPosTrig := PanelRec.endPosTrig; + mEndSizeTrig := PanelRec.endSizeTrig; // Òèï ïàíåëè: PanelType := PanelRec.PanelType; @@ -427,10 +450,10 @@ begin end; else - e_DrawFill(FTextureIDs[FCurTexture].Tex, X, Y, - Width div FTextureWidth, - Height div FTextureHeight, - FAlpha, True, FBlending); + if not mMovingActive then + e_DrawFill(FTextureIDs[FCurTexture].Tex, X, Y, Width div FTextureWidth, Height div FTextureHeight, FAlpha, True, FBlending) + else + e_DrawFillX(FTextureIDs[FCurTexture].Tex, X, Y, Width, Height, FAlpha, True, FBlending, g_dbg_scale); end; end; end; @@ -490,135 +513,354 @@ var begin if (proxyId >= 0) then begin - monsGrid.getBodyDims(proxyId, px, py, pw, ph); + mapGrid.getBodyDims(proxyId, px, py, pw, ph); if (px <> x) or (py <> y) or (pw <> Width) or (ph <> Height) then begin + { + e_LogWritefln('panel moved: arridx=%s; guid=%s; proxyid=%s; old:(%s,%s)-(%sx%s); new:(%s,%s)-(%sx%s)', + [arrIdx, mGUID, proxyId, px, py, pw, ph, x, y, width, height]); + } g_Mark(px, py, pw, ph, MARK_WALL, false); - if (pw <> Width) or (ph <> Height) then mapGrid.moveResizeBody(proxyId, X, Y, Width, Height) - else mapGrid.moveBody(proxyId, X, Y); - g_Mark(X, Y, Width, Height, MARK_WALL); + if (Width < 1) or (Height < 1) then + begin + mapGrid.proxyEnabled[proxyId] := false; + end + else + begin + mapGrid.proxyEnabled[proxyId] := Enabled; + if (pw <> Width) or (ph <> Height) then + begin + //writeln('panel resize!'); + mapGrid.moveResizeBody(proxyId, X, Y, Width, Height) + end + else + begin + mapGrid.moveBody(proxyId, X, Y); + end; + g_Mark(X, Y, Width, Height, MARK_WALL); + end; end; end; end; var - monMoveList: array of TMonster = nil; - monMoveListUsed: Integer = 0; + monCheckList: array of TMonster = nil; + monCheckListUsed: Integer = 0; procedure TPanel.Update(); var + ox, oy: Integer; nx, ny: Integer; - f: Integer; + ex, ey, nex, ney: Integer; + mpw, mph: Integer; - function doPush (px, py, pw, ph: Integer; out dx, dy: Integer): Boolean; + // return `true` if we should move by dx,dy + function tryMPlatMove (px, py, pw, ph: Integer; out dx, dy: Integer; out squash: Boolean; ontop: PBoolean=nil): Boolean; + var + u0, u1: Single; + tex, tey: Integer; + pdx, pdy: Integer; + pan: TPanel; + trtag: Integer; + hedge: Integer; begin - result := g_Collide(px, py, pw, ph, nx, ny, Width, Height); - if result then + squash := false; + tex := px; + tey := py; + pdx := mMovingSpeed.X; + pdy := mMovingSpeed.Y; + // standing on the platform? + if (py+ph = oy) then begin - // need to push - if (mMovingSpeed.X < 0) then dx := nx-(px+pw) - else if (mMovingSpeed.X > 0) then dx := (nx+Width)-px - else dx := 0; - if (mMovingSpeed.Y < 0) then dy := ny-(py+ph) - else if (mMovingSpeed.Y > 0) then dy := (ny+Height)-py - else dy := 0; + if (ontop <> nil) then ontop^ := true; + // yes, move with it; but skip steps + pan := mapGrid.traceBox(tex, tey, px, py, pw, ph, pdx, pdy, nil, (GridTagWall or GridTagDoor)); + if (pan <> nil) then + begin + //e_LogWritefln('entity on the platform; tracing=(%s,%s); endpoint=(%s,%s); mustbe=(%s,%s)', [px, py, tex, tey, px+pdx, py+pdy]); + // if we cannot move, only walls should squash the entity + { + if ((tag and (GridTagWall or GridTagDoor)) <> 0) then + begin + if (tex = px) and (tey = py) then squash := true; + end; + } + end; end else begin - dx := 0; - dy := 0; + if (ontop <> nil) then ontop^ := false; + // not standing on the platform: trace platform to see if it hits the entity + // hitedge (for `it`): 0: top; 1: right; 2: bottom; 3: left + { + if g_Collide(px, py, pw, ph, ox, oy, mpw, mph) then + begin + e_LogWritefln('entity is embedded: plr=(%s,%s)-(%s,%s); mpl=(%s,%s)-(%s,%s)', [px, py, px+pw-1, py+ph-1, ox, oy, ox+mpw-1, oy+mph-1]); + end; + } + if sweepAABB(ox, oy, mpw, mph, pdx, pdy, px, py, pw, ph, @u0, @hedge, @u1) then + begin + //e_LogWritefln('T: platsweep; u0=%s; u1=%s; hedge=%s; sweepAABB(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)', [u0, u1, hedge, ox, oy, mpw, mph, pdx, pdy, px-1, py-1, pw+2, ph+2]); + // yes, platform hits the entity, push the entity in the direction of the platform + u0 := 1.0-u0; // how much path left? + pdx := trunc(pdx*u0); + pdy := trunc(pdy*u0); + //e_LogWritefln(' platsweep; uleft=%s; pd=(%s,%s)', [u0, pdx, pdy]); + if (pdx <> 0) or (pdy <> 0) then + begin + // has some path to go, trace the entity + trtag := (GridTagWall or GridTagDoor); + // if we're moving down, consider steps too + if (pdy > 0) then trtag := trtag or GridTagStep; + pan := mapGrid.traceBox(tex, tey, px, py, pw, ph, pdx, pdy, nil, trtag); + //e_LogWritefln(' tracebox: te=(%s,%s)', [tex, tey]); + // if we cannot move, only walls should squash the entity + { + if ((tag and (GridTagWall or GridTagDoor)) <> 0) then + begin + if (pan <> nil) and (tex = px) and (tey = py) then squash := true; + end; + } + end; + end + else + begin + // no collistion, but may be embedded + //e_LogWritefln('F: platsweep; u0=%s; u1=%s; sweepAABB(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)', [u0, u1, ox, oy, mpw, mph, pdx, pdy, px-1, py-1, pw+2, ph+2]); + //squash := (u1 >= 0.0); + end; end; - end; - - function monMove (mon: TMonster): Boolean; - begin - result := false; // don't stop - //mon.GameX := mon.GameX+mMovingSpeed.X; - //mon.GameY := mon.GameY+mMovingSpeed.Y; - mon.setPosition(mon.GameX+mMovingSpeed.X, mon.GameY+mMovingSpeed.Y, false); // we can't call `positionChanged()` in grid callback - if (monMoveListUsed >= Length(monMoveList)) then SetLength(monMoveList, monMoveListUsed+64); - monMoveList[monMoveListUsed] := mon; - Inc(monMoveListUsed); - end; - - function monPush (mon: TMonster): Boolean; - var - px, py, pw, ph, dx, dy: Integer; - begin - result := false; // don't stop - mon.getMapBox(px, py, pw, ph); - if doPush(px, py, pw, ph, dx, dy) then + dx := tex-px; + dy := tey-py; + result := (dx <> 0) or (dy <> 0); + if (not squash) and ((tag and (GridTagWall or GridTagDoor)) <> 0) then begin - //mon.GameX := mon.GameX+dx; - //mon.GameY := mon.GameY+dy; - mon.setPosition(mon.GameX+dx, mon.GameY+dy, false); // we can't call `positionChanged()` in grid callback - if (monMoveListUsed >= Length(monMoveList)) then SetLength(monMoveList, monMoveListUsed+64); - monMoveList[monMoveListUsed] := mon; - Inc(monMoveListUsed); + squash := g_Collide(tex, tey, pw, ph, nx, ny, mpw, mph); // still in platform? + //if not squash then squash := g_Map_CollidePanel(tex, tey, pw, ph, (PANEL_WALL or PANEL_OPENDOOR or PANEL_CLOSEDOOR)); end; end; - procedure plrMove (plr: TPlayer); - var - px, py, pw, ph, dx, dy: Integer; + function monCollect (mon: TMonster): Boolean; begin - if (plr = nil) then exit; - plr.getMapBox(px, py, pw, ph); - if (py+ph <> Y) then - begin - // push player - if doPush(px, py, pw, ph, dx, dy) then - begin - plr.GameX := plr.GameX+dx; - plr.GameY := plr.GameY+dy; - plr.positionChanged(); - end; - exit; - end; - if (px+pw <= X) then exit; - if (px >= X+Width) then exit; - plr.GameX := plr.GameX+mMovingSpeed.X; - plr.GameY := plr.GameY+mMovingSpeed.Y; - plr.positionChanged(); + result := false; // don't stop + if (monCheckListUsed >= Length(monCheckList)) then SetLength(monCheckList, monCheckListUsed+128); + monCheckList[monCheckListUsed] := mon; + Inc(monCheckListUsed); end; +var + cx0, cy0, cx1, cy1, cw, ch: Integer; + f: Integer; + px, py, pw, ph, pdx, pdy: Integer; + squash: Boolean; + plr: TPlayer; + gib: PGib; + cor: TCorpse; + mon: TMonster; + mpfrid: LongWord; + ontop: Boolean; + actMoveTrig: Boolean; + actSizeTrig: Boolean; begin - if Enabled and (FCurTexture >= 0) and + if (not Enabled) or (Width < 1) or (Height < 1) then exit; + + if (FCurTexture >= 0) and (FTextureIDs[FCurTexture].Anim) and (FTextureIDs[FCurTexture].AnTex <> nil) and - (Width > 0) and (Height > 0) and (FAlpha < 255) then + (FAlpha < 255) then begin FTextureIDs[FCurTexture].AnTex.Update(); FCurFrame := FTextureIDs[FCurTexture].AnTex.CurrentFrame; FCurFrameCount := FTextureIDs[FCurTexture].AnTex.CurrentCounter; end; - if mMovingActive and (not mMovingSpeed.isZero) then + if not g_dbgpan_mplat_active then exit; + + if not mMovingActive then exit; + if mMovingSpeed.isZero and mSizeSpeed.isZero then exit; + + //TODO: write wall size change processing + + // moving platform? 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; - // awake particles - //g_Mark(X, Y, Width, Height, MARK_WALL, false); + (* + * collect all monsters and players (aka entities) along the possible platform path + * if entity is standing on a platform: + * try to move it along the platform path, checking wall collisions + * if entity is NOT standing on a platform, but hit with sweeped platform aabb: + * try to push entity + * if we can't push entity all the way, squash it + *) + mpw := Width; + mph := Height; + + // old rect + ox := X; + oy := Y; + ex := ox+mpw-1; + ey := ox+mph-1; + // new rect + nx := ox+mMovingSpeed.X; + ny := oy+mMovingSpeed.Y; + nex := nx+mpw-1; + ney := ny+mph-1; + // full rect + cx0 := nmin(ox, nx); + cy0 := nmin(oy, ny); + cx1 := nmax(ex, nex); + cy1 := nmax(ey, ney); + // extrude + cx0 -= 1; + cy0 -= 1; + cx1 += 1; + cy1 += 1; + cw := cx1-cx0+1; + ch := cy1-cy0+1; + + // process "obstacle" panels + if ((tag and GridTagObstacle) <> 0) then + begin + // temporarily turn off this panel, so it won't interfere with collision checks + mapGrid.proxyEnabled[proxyId] := false; + + // process players + for f := 0 to High(gPlayers) do + begin + plr := gPlayers[f]; + if (plr = nil) or (not plr.alive) then continue; + plr.getMapBox(px, py, pw, ph); + if not g_Collide(px, py, pw, ph, cx0, cy0, cw, ch) then continue; + if tryMPlatMove(px, py, pw, ph, pdx, pdy, squash) then + begin + // set new position + plr.moveBy(pdx, pdy); // this will call `positionChanged()` for us + end; + // squash player, if necessary + if squash then plr.Damage(15000, 0, 0, 0, HIT_TRAP); + end; + + // process gibs + for f := 0 to High(gGibs) do + begin + gib := @gGibs[f]; + if not gib.alive then continue; + gib.getMapBox(px, py, pw, ph); + if not g_Collide(px, py, pw, ph, cx0, cy0, cw, ch) then continue; + if tryMPlatMove(px, py, pw, ph, pdx, pdy, squash, @ontop) then + begin + // set new position + gib.moveBy(pdx, pdy); // this will call `positionChanged()` for us + { + if ontop then + begin + gib.Obj.Vel.X += pdx; + gib.Obj.Vel.Y += pdy; + end; + } + end; + end; + + // move and push corpses + for f := 0 to High(gCorpses) do + begin + cor := gCorpses[f]; + if (cor = nil) then continue; + cor.getMapBox(px, py, pw, ph); + if not g_Collide(px, py, pw, ph, cx0, cy0, cw, ch) then continue; + if tryMPlatMove(px, py, pw, ph, pdx, pdy, squash, @ontop) then + begin + // set new position + cor.moveBy(pdx, pdy); // this will call `positionChanged()` for us + { + if ontop then + begin + cor.ObjPtr.Vel.X += pdx; + cor.ObjPtr.Vel.Y += pdy; + end; + } + end; + end; + + // collect monsters + monCheckListUsed := 0; + g_Mons_ForEachAt(cx0, cy0, cw, ch, monCollect); + + // process collected monsters + if (monCheckListUsed > 0) then + begin + mpfrid := g_Mons_getNewMPlatFrameId(); + for f := 0 to monCheckListUsed do + begin + mon := monCheckList[f]; + if (mon = nil) or (not mon.alive) or (mon.mplatCheckFrameId = mpfrid) then continue; + mon.mplatCheckFrameId := mpfrid; + mon.getMapBox(px, py, pw, ph); + //if not g_Collide(px, py, pw, ph, cx0, cy0, cw, ch) then continue; + if tryMPlatMove(px, py, pw, ph, pdx, pdy, squash) then + begin + // set new position + mon.moveBy(pdx, pdy); // this will call `positionChanged()` for us + end; + // squash player, if necessary + if squash then mon.Damage(15000, 0, 0, 0, HIT_TRAP); + end; + end; + + // restore panel state + mapGrid.proxyEnabled[proxyId] := true; + end; + + // move panel X := nx; Y := ny; - //g_Mark(nx, ny, Width, Height, MARK_WALL); - // fix grid + FWidth += mSizeSpeed.w; + FHeight += mSizeSpeed.h; positionChanged(); - // notify moved monsters about their movement - for f := 0 to monMoveListUsed-1 do monMoveList[f].positionChanged(); + + actMoveTrig := false; + actSizeTrig := false; + + { + if not mSizeSpeed.isZero then + begin + e_LogWritefln('ss: size_speed=(%s,%s); size=(%s,%s); move_speed=(%s,%s); oy=%s; ny=%s; etp:%s; ets:%s', [mSizeSpeed.w, mSizeSpeed.h, FWidth, FHeight, mMovingSpeed.X, mMovingSpeed.Y, oy, ny, mEndPosTrig, mEndSizeTrig]); + end; + } + + // reverse moving direction, if necessary + if ((mMovingSpeed.X < 0) and (nx <= mMovingStart.X)) or ((mMovingSpeed.X > 0) and (nx >= mMovingEnd.X)) then + begin + if mMoveOnce then mMovingActive := false else mMovingSpeed.X := -mMovingSpeed.X; + actMoveTrig := true; + end; + + if ((mMovingSpeed.Y < 0) and (ny <= mMovingStart.Y)) or ((mMovingSpeed.Y > 0) and (ny >= mMovingEnd.Y)) then + begin + if mMoveOnce then mMovingActive := false else mMovingSpeed.Y := -mMovingSpeed.Y; + actMoveTrig := true; + end; + + // check "size stop" + if not mSizeSpeed.isZero and (Width = mSizeEnd.w) and (Height = mSizeEnd.h) then + begin + mSizeSpeed.w := 0; + mSizeSpeed.h := 0; + actSizeTrig := true; + if (Width < 1) or (Height < 1) then mMovingActive := false; //HACK! + //e_LogWritefln('FUUUUUUUUUUUUUU', []); + end; + + + if actMoveTrig then + begin + g_Triggers_Press(mEndPosTrig, ACTIVATE_CUSTOM); + end; + + if actSizeTrig then + begin + g_Triggers_Press(mEndSizeTrig, ACTIVATE_CUSTOM); + end; end; end;