From 2b79b90a46d647d1d5bf4205862be5ffa296add1 Mon Sep 17 00:00:00 2001 From: Ketmar Dark Date: Tue, 22 Aug 2017 03:57:58 +0300 Subject: [PATCH] more fixes to grid; dunno, seems to work again --- src/game/g_grid.pas | 492 ++++++++++++++++++++++++++------------------ src/game/g_map.pas | 87 +++----- 2 files changed, 322 insertions(+), 257 deletions(-) diff --git a/src/game/g_grid.pas b/src/game/g_grid.pas index da26a5f..d56a243 100644 --- a/src/game/g_grid.pas +++ b/src/game/g_grid.pas @@ -32,7 +32,9 @@ type private const GridDefaultTileSize = 32; + {$IFDEF grid_use_buckets} GridCellBucketSize = 8; // WARNING! can't be less than 2! + {$ENDIF} private type @@ -237,6 +239,11 @@ begin result := mFreeCell; mFreeCell := mCells[result].next; mCells[result].next := -1; + {$IFDEF grid_use_buckets} + mCells[result].bodies[0] := -1; + {$ELSE} + mCells[result].body := -1; + {$ENDIF} Inc(mUsedCells); //e_WriteLog(Format('grid: allocated new cell #%d (total: %d)', [result, mUsedCells]), MSG_NOTIFY); end; @@ -244,10 +251,14 @@ end; procedure TBodyGridBase.freeCell (idx: Integer); begin - if (idx >= 0) and (idx < High(mCells)) then + if (idx >= 0) and (idx < Length(mCells)) then begin //if mCells[idx].body = -1 then exit; // the thing that should not be - //mCells[idx].body := -1; + {$IFDEF grid_use_buckets} + mCells[idx].bodies[0] := -1; + {$ELSE} + mCells[idx].body := -1; + {$ENDIF} mCells[idx].next := mFreeCell; mFreeCell := idx; Dec(mUsedCells); @@ -296,6 +307,7 @@ end; function TBodyGridBase.forGridRect (x, y, w, h: Integer; cb: TGridInternalCB): Boolean; var gx, gy: Integer; + gw, gh, tsize: Integer; begin result := false; if (w < 1) or (h < 1) or not assigned(cb) then exit; @@ -304,206 +316,29 @@ begin Dec(y, mMinY); // go on if (x+w <= 0) or (y+h <= 0) then exit; - if (x >= mWidth*mTileSize) or (y >= mHeight*mTileSize) then exit; - for gy := y div mTileSize to (y+h-1) div mTileSize do + gw := mWidth; + gh := mHeight; + tsize := mTileSize; + if (x >= gw*tsize) or (y >= gh*tsize) then exit; + for gy := y div tsize to (y+h-1) div tsize do begin if (gy < 0) then continue; - if (gy >= mHeight) then break; - for gx := x div mTileSize to (x+w-1) div mTileSize do + if (gy >= gh) then break; + for gx := x div tsize to (x+w-1) div tsize do begin if (gx < 0) then continue; - if (gx >= mWidth) then break; - result := cb(gy*mWidth+gx); + if (gx >= gw) then break; + result := cb(gy*gw+gx); if result then exit; end; end; end; -// ////////////////////////////////////////////////////////////////////////// // -function TBodyGridBase.forEachAtPoint (x, y: Integer; cb: TGridQueryCB; tagmask: Integer=-1): Boolean; -var - idx, ga, curci: Integer; - f: Integer; - cc: PGridCell = nil; - px: PBodyProxyRec; - lq: LongWord; -begin - result := false; - if not assigned(cb) or (tagmask = 0) then exit; - - Dec(x, mMinX); - Dec(y, mMinY); - if (x < 0) or (y < 0) or (x >= mWidth*mTileSize) or (y >= mHeight*mTileSize) then exit; - - ga := (y div mTileSize)*mWidth+(x div mTileSize); - curci := mGrid[ga]; - Inc(x, mMinX); - Inc(y, mMinY); - - // increase query counter - Inc(mLastQuery); - if (mLastQuery = 0) then - begin - // just in case of overflow - mLastQuery := 1; - for idx := 0 to High(mProxies) do mProxies[idx].mQueryMark := 0; - end; - lq := mLastQuery; - - while (curci <> -1) do - begin - cc := @mCells[curci]; - for f := 0 to High(TGridCell.bodies) do - begin - if (cc.bodies[f] = -1) then break; - px := @mProxies[cc.bodies[f]]; - if (px.mQueryMark <> lq) and ((px.mTag and tagmask) <> 0) then - begin - if (x >= px.mX) and (y >= px.mY) and (x < px.mX+px.mWidth) and (y < px.mY+px.mHeight) then - begin - px.mQueryMark := lq; - result := cb(px.mObj, px.mTag); - if result then exit; - end; - end; - end; - curci := cc.next; - end; -end; - - -// ////////////////////////////////////////////////////////////////////////// // -function TBodyGridBase.traceRay (x0, y0, x1, y1: Integer; cb: TGridRayQueryCB; tagmask: Integer=-1): Boolean; -var - i: Integer; - dx, dy: Integer; - xerr, yerr, d: LongWord; - incx, incy: Integer; - x, y: Integer; - maxx, maxy: Integer; - tsize: Integer; // tile size - gw, gh: Integer; - lastGA: Integer = -1; - ga: Integer = -1; // last used grid address - ccidx: Integer = -1; - curci: Integer = -1; - cc: PGridCell = nil; - hasUntried: Boolean; - f: Integer; - px: PBodyProxyRec; - lq: LongWord; - prevX, prevY: Integer; - minx, miny: Integer; -begin - result := false; - - if (tagmask = 0) then exit; - - // make coords (0,0)-based - minx := mMinX; - miny := mMinY; - Dec(x0, minx); - Dec(y0, miny); - Dec(x1, minx); - Dec(y1, miny); - - xerr := 0; - yerr := 0; - dx := x1-x0; - dy := y1-y0; - - if (dx > 0) then incx := 1 else if (dx < 0) then incx := -1 else incx := 0; - if (dy > 0) then incy := 1 else if (dy < 0) then incy := -1 else incy := 0; - - dx := abs(dx); - dy := abs(dy); - - if (dx > dy) then d := dx else d := dy; - - x := x0; - y := y0; - - // increase query counter - Inc(mLastQuery); - if (mLastQuery = 0) then - begin - // just in case of overflow - mLastQuery := 1; - for i := 0 to High(mProxies) do mProxies[i].mQueryMark := 0; - end; - lq := mLastQuery; - - tsize := mTileSize; - gw := mWidth; - gh := mHeight; - maxx := gw*tsize-1; - maxy := gh*tsize-1; - - for i := 1 to d do - begin - prevX := x; - prevY := y; - Inc(xerr, dx); if (xerr > d) then begin Dec(xerr, d); Inc(x, incx); end; - Inc(yerr, dy); if (yerr > d) then begin Dec(yerr, d); Inc(y, incy); end; - - if (x >= 0) and (y >= 0) and (x <= maxx) and (y <= maxy) then - begin - ga := (y div tsize)*gw+(x div tsize); - if (lastGA <> ga) then - begin - // new cell - lastGA := ga; - ccidx := mGrid[lastGA]; - if (ccidx <> -1) then - begin - result := cb(nil, 0, x+minx, y+miny, prevX+minx, prevY+miny); - if result then exit; - end; - end; - end - else - begin - ccidx := -1; - end; - - if (ccidx <> -1) then - begin - curci := ccidx; - hasUntried := false; - while (curci <> -1) do - begin - cc := @mCells[curci]; - for f := 0 to High(TGridCell.bodies) do - begin - if (cc.bodies[f] = -1) then break; - px := @mProxies[cc.bodies[f]]; - if (px.mQueryMark <> lq) and ((px.mTag and tagmask) <> 0) then - begin - if (x+minx >= px.mX) and (y+miny >= px.mY) and (x+minx < px.mX+px.mWidth) and (y+miny < px.mY+px.mHeight) then - begin - px.mQueryMark := lq; - result := cb(px.mObj, px.mTag, x+minx, y+miny, prevX+minx, prevY+miny); - if result then exit; - end - else - begin - hasUntried := true; - end; - end; - end; - curci := cc.next; - end; - if not hasUntried then ccidx := -1; // don't process this cell anymore - end; - end; -end; - - function TBodyGridBase.inserter (grida: Integer): Boolean; var cidx: Integer; - pc: PInteger; + pc: Integer; {$IFDEF grid_use_buckets} pi: PGridCell; f: Integer; @@ -511,11 +346,11 @@ var begin result := false; // never stop // add body to the given grid cell - pc := @mGrid[grida]; + pc := mGrid[grida]; {$IFDEF grid_use_buckets} - if (pc^ <> -1) then + if (pc <> -1) then begin - pi := @mCells[pc^]; + pi := @mCells[pc]; f := 0; for f := 0 to High(TGridCell.bodies) do begin @@ -532,14 +367,14 @@ begin cidx := allocCell(); mCells[cidx].bodies[0] := mUData; mCells[cidx].bodies[1] := -1; - mCells[cidx].next := pc^; - pc^ := cidx; + mCells[cidx].next := pc; + mGrid[grida] := cidx; {$ELSE} cidx := allocCell(); //e_WriteLog(Format(' 01: allocated cell for grid coords (%d,%d), body coords:(%d,%d): #%d', [gx, gy, dx, dy, cidx]), MSG_NOTIFY); mCells[cidx].body := mUData; - mCells[cidx].next := pc^; - pc^ := cidx; + mCells[cidx].next := pc; + mGrid[grida] := cidx; {$ENDIF} end; @@ -557,8 +392,13 @@ end; function TBodyGridBase.remover (grida: Integer): Boolean; var - pidx, idx, tmp, f: Integer; + {$IFDEF grid_use_buckets} + f: Integer; + {$ENDIF} + pidx, idx, tmp: Integer; + {$IFDEF grid_use_buckets} pc: PGridCell; + {$ENDIF} begin result := false; // never stop // find and remove cell @@ -665,12 +505,86 @@ begin end; +// ////////////////////////////////////////////////////////////////////////// // +function TBodyGridBase.forEachAtPoint (x, y: Integer; cb: TGridQueryCB; tagmask: Integer=-1): Boolean; +var + {$IFDEF grid_use_buckets} + f: Integer; + {$ENDIF} + idx, curci: Integer; + cc: PGridCell = nil; + px: PBodyProxyRec; + lq: LongWord; +begin + result := false; + if not assigned(cb) or (tagmask = 0) then exit; + + // make coords (0,0)-based + Dec(x, mMinX); + Dec(y, mMinY); + if (x < 0) or (y < 0) or (x >= mWidth*mTileSize) or (y >= mHeight*mTileSize) then exit; + + curci := mGrid[(y div mTileSize)*mWidth+(x div mTileSize)]; + // restore coords + Inc(x, mMinX); + Inc(y, mMinY); + + // increase query counter + Inc(mLastQuery); + if (mLastQuery = 0) then + begin + // just in case of overflow + mLastQuery := 1; + for idx := 0 to High(mProxies) do mProxies[idx].mQueryMark := 0; + end; + lq := mLastQuery; + + while (curci <> -1) do + begin + cc := @mCells[curci]; + {$IFDEF grid_use_buckets} + for f := 0 to High(TGridCell.bodies) do + begin + if (cc.bodies[f] = -1) then break; + px := @mProxies[cc.bodies[f]]; + if (px.mQueryMark <> lq) and ((px.mTag and tagmask) <> 0) then + begin + if (x >= px.mX) and (y >= px.mY) and (x < px.mX+px.mWidth) and (y < px.mY+px.mHeight) then + begin + px.mQueryMark := lq; + result := cb(px.mObj, px.mTag); + if result then exit; + end; + end; + end; + {$ELSE} + if (cc.body <> -1) then + begin + px := @mProxies[cc.body]; + if (px.mQueryMark <> lq) and ((px.mTag and tagmask) <> 0) then + begin + if (x >= px.mX) and (y >= px.mY) and (x < px.mX+px.mWidth) and (y < px.mY+px.mHeight) then + begin + px.mQueryMark := lq; + result := cb(px.mObj, px.mTag); + if result then exit; + end; + end; + end; + {$ENDIF} + curci := cc.next; + end; +end; + + function TBodyGridBase.forEachInAABB (x, y, w, h: Integer; cb: TGridQueryCB; tagmask: Integer=-1): Boolean; var idx: Integer; gx, gy: Integer; curci: Integer; + {$IFDEF grid_use_buckets} f: Integer; + {$ENDIF} cc: PGridCell = nil; px: PBodyProxyRec; lq: LongWord; @@ -718,6 +632,7 @@ begin while (curci <> -1) do begin cc := @mCells[curci]; + {$IFDEF grid_use_buckets} for f := 0 to High(TGridCell.bodies) do begin if (cc.bodies[f] = -1) then break; @@ -731,6 +646,25 @@ begin if result then exit; end; end; + {$ELSE} + if (cc.body <> 1) then + begin + px := @mProxies[cc.body]; + if (px.mQueryMark <> lq) and ((px.mTag and tagmask) <> 0) then + begin + if (x0 >= px.mX+px.mWidth) or (y0 >= px.mY+px.mHeight) or (x0+w <= px.mX) or (y0+h <= px.mY) then + begin + // no intersection + end + else + begin + px.mQueryMark := lq; + result := cb(px.mObj, px.mTag); + if result then exit; + end; + end; + end; + {$ENDIF} curci := cc.next; end; end; @@ -738,4 +672,168 @@ begin end; +// ////////////////////////////////////////////////////////////////////////// // +function TBodyGridBase.traceRay (x0, y0, x1, y1: Integer; cb: TGridRayQueryCB; tagmask: Integer=-1): Boolean; +var + i: Integer; + dx, dy: Integer; + xerr: Integer = 0; + yerr: Integer = 0; + d: Integer; + incx, incy: Integer; + x, y: Integer; + maxx, maxy: Integer; + tsize: Integer; // tile size + gw, gh: Integer; + lastGA: Integer = -1; + ga: Integer = -1; // last used grid address + ccidx: Integer = -1; + curci: Integer = -1; + cc: PGridCell = nil; + hasUntried: Boolean; + f: Integer; + px: PBodyProxyRec; + lq: LongWord; + prevX, prevY: Integer; + minx, miny: Integer; +begin + result := false; + + if (tagmask = 0) then exit; + + // make coords (0,0)-based + minx := mMinX; + miny := mMinY; + Dec(x0, minx); + Dec(y0, miny); + Dec(x1, minx); + Dec(y1, miny); + + dx := x1-x0; + dy := y1-y0; + + if (dx > 0) then incx := 1 else if (dx < 0) then incx := -1 else incx := 0; + if (dy > 0) then incy := 1 else if (dy < 0) then incy := -1 else incy := 0; + + dx := abs(dx); + dy := abs(dy); + + if (dx > dy) then d := dx else d := dy; + + x := x0; + y := y0; + + // increase query counter + Inc(mLastQuery); + if (mLastQuery = 0) then + begin + // just in case of overflow + mLastQuery := 1; + for i := 0 to High(mProxies) do mProxies[i].mQueryMark := 0; + end; + lq := mLastQuery; + + tsize := mTileSize; + gw := mWidth; + gh := mHeight; + maxx := gw*tsize-1; + maxy := gh*tsize-1; + + for i := 1 to d do + begin + prevX := x+minx; + prevY := y+miny; + Inc(xerr, dx); if (xerr >= d) then begin Dec(xerr, d); Inc(x, incx); end; + Inc(yerr, dy); if (yerr >= d) then begin Dec(yerr, d); Inc(y, incy); end; + + if (x >= 0) and (y >= 0) and (x <= maxx) and (y <= maxy) then + begin + ga := (y div tsize)*gw+(x div tsize); + if (lastGA <> ga) then + begin + // new cell + lastGA := ga; + ccidx := mGrid[lastGA]; + { + if (ccidx <> -1) then + begin + result := cb(nil, 0, x+minx, y+miny, prevX, prevY); + if result then exit; + end; + } + end; + end + else + begin + if (ccidx <> -1) then + begin + ccidx := -1; + result := cb(nil, 0, x+minx, y+miny, prevX, prevY); + if result then exit; + end; + end; + + if (ccidx <> -1) then + begin + curci := ccidx; + hasUntried := false; + Inc(x, minx); + Inc(y, miny); + while (curci <> -1) do + begin + cc := @mCells[curci]; + {$IFDEF grid_use_buckets} + for f := 0 to High(TGridCell.bodies) do + begin + if (cc.bodies[f] = -1) then break; + px := @mProxies[cc.bodies[f]]; + if (px.mQueryMark <> lq) and ((px.mTag and tagmask) <> 0) then + begin + if (x >= px.mX) and (y >= px.mY) and (x < px.mX+px.mWidth) and (y < px.mY+px.mHeight) then + begin + px.mQueryMark := lq; + result := cb(px.mObj, px.mTag, x, y, prevX, prevY); + if result then exit; + end + else + begin + hasUntried := true; + end; + end; + end; + {$ELSE} + if (cc.body <> -1) then + begin + px := @mProxies[cc.body]; + if (px.mQueryMark <> lq) and ((px.mTag and tagmask) <> 0) then + begin + if (x >= px.mX) and (y >= px.mY) and (x < px.mX+px.mWidth) and (y < px.mY+px.mHeight) then + begin + px.mQueryMark := lq; + result := cb(px.mObj, px.mTag, x, y, prevX, prevY); + if result then exit; + end + else + begin + hasUntried := true; + end; + end; + end; + {$ENDIF} + curci := cc.next; + end; + if not hasUntried then + begin + // don't process this cell anymore + ccidx := -1; + result := cb(nil, 0, x, y, prevX, prevY); + if result then exit; + end; + Dec(x, minx); + Dec(y, miny); + end; + end; +end; + + end. diff --git a/src/game/g_map.pas b/src/game/g_map.pas index bd8acf8..45a3fa3 100644 --- a/src/game/g_map.pas +++ b/src/game/g_map.pas @@ -97,8 +97,6 @@ function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; type TForEachPanelCB = function (pan: TPanel): Boolean; // return `true` to stop -function g_Map_ForEachPanelAt (x, y: Integer; cb: TForEachPanelCB; panelType: Word): Boolean; - function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean; @@ -369,13 +367,13 @@ end; // wall index in `gWalls` or -1 function g_Map_traceToNearestWall (x0, y0, x1, y1: Integer; hitx: PInteger=nil; hity: PInteger=nil): Boolean; var - lastX, lastY, lastDist: Integer; + lastX, lastY, lastDistSq: Integer; wasHit: Boolean = false; // pan=nil: before processing new tile function sqchecker (pan: TPanel; tag: Integer; x, y, prevx, prevy: Integer): Boolean; var - dist: Integer; + distSq: Integer; begin if (pan = nil) then begin @@ -389,11 +387,11 @@ var begin if not pan.Enabled then exit; end; - dist := (prevx-x0)*(prevx-x0)+(prevy-y0)*(prevy-y0); - if (dist < lastDist) then + distSq := (prevx-x0)*(prevx-x0)+(prevy-y0)*(prevy-y0); + if (distSq < lastDistSq) then begin wasHit := true; - lastDist := dist; + lastDistSq := distSq; lastX := prevx; lastY := prevy; end; @@ -403,7 +401,7 @@ var begin result := false; if (gMapGrid = nil) then exit; - lastDist := (x1-x0)*(x1-x0)+(y1-y0)*(y1-y0)+1; + lastDistSq := (x1-x0)*(x1-x0)+(y1-y0)*(y1-y0)+1; lastX := 0; lastY := 0; result := gMapGrid.traceRay(x0, y0, x1, y1, sqchecker, (GridTagWall or GridTagDoor)); @@ -412,52 +410,6 @@ begin end; -function g_Map_ForEachPanelAt (x, y: Integer; cb: TForEachPanelCB; panelType: Word): Boolean; - - function checker (pan: TPanel; tag: Integer): Boolean; - begin - result := false; // don't stop, ever - - if ((tag and (GridTagWall or GridTagDoor)) <> 0) then - begin - if not pan.Enabled then exit; - end; - - result := (x >= pan.X) and (y >= pan.Y) and (x < pan.X+pan.Width) and (y < pan.Y+pan.Height); - if not result then exit; - - if ((tag and GridTagLift) <> 0) then - begin - result := - ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or - (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or - (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or - (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3))); - end; - - // other shit - if result then result := cb(pan); - end; - -var - tagmask: Integer = 0; -begin - result := false; - if not assigned(cb) then exit; - - if WordBool(PanelType and (PANEL_WALL or PANEL_CLOSEDOOR or PANEL_OPENDOOR)) then tagmask := tagmask or (GridTagWall or GridTagDoor); - if WordBool(PanelType and PANEL_WATER) then tagmask := tagmask or GridTagWater; - if WordBool(PanelType and PANEL_ACID1) then tagmask := tagmask or GridTagAcid1; - if WordBool(PanelType and PANEL_ACID2) then tagmask := tagmask or GridTagAcid2; - if WordBool(PanelType and PANEL_STEP) then tagmask := tagmask or GridTagStep; - if WordBool(PanelType and (PANEL_LIFTUP or PANEL_LIFTDOWN or PANEL_LIFTLEFT or PANEL_LIFTRIGHT)) then tagmask := tagmask or GridTagLift; - if WordBool(PanelType and PANEL_BLOCKMON) then tagmask := tagmask or GridTagBlockMon; - - if (tagmask = 0) then exit;// just in case - result := gMapGrid.forEachInAABB(x, y, 1, 1, checker, tagmask); -end; - - function g_Map_HasAnyPanelAtPoint (x, y: Integer; panelType: Word): Boolean; function checker (pan: TPanel; tag: Integer): Boolean; @@ -2363,8 +2315,8 @@ function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; ((WordBool(PanelType and PANEL_LIFTUP) and (pan.LiftType = 0)) or (WordBool(PanelType and PANEL_LIFTDOWN) and (pan.LiftType = 1)) or (WordBool(PanelType and PANEL_LIFTLEFT) and (pan.LiftType = 2)) or - (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3))) and - g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height); + (WordBool(PanelType and PANEL_LIFTRIGHT) and (pan.LiftType = 3))) {and + g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height)}; exit; end; @@ -2375,7 +2327,8 @@ function g_Map_CollidePanel(X, Y: Integer; Width, Height: Word; PanelType: Word; end; // other shit - result := g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height); + //result := g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height); + result := true; end; var @@ -2406,7 +2359,14 @@ begin end else} begin - result := gMapGrid.forEachInAABB(X, Y, Width, Height, checker, tagmask); + if (Width = 1) and (Height = 1) then + begin + result := gMapGrid.forEachAtPoint(X, Y, checker, tagmask); + end + else + begin + result := gMapGrid.forEachInAABB(X, Y, Width, Height, checker, tagmask); + end; end; end else @@ -2434,7 +2394,7 @@ var //2: if ((tag and (GridTagWater or GridTagAcid1 or GridTagAcid2) = 0) then exit; // allowed: water, acid1, acid2 end; // collision? - if not g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height) then exit; + //if not g_Collide(X, Y, Width, Height, pan.X, pan.Y, pan.Width, pan.Height) then exit; // yeah texid := pan.GetTextureID(); // water? water has the highest priority, so stop right here @@ -2456,7 +2416,14 @@ begin end else} begin - gMapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2)); + if (Width = 1) and (Height = 1) then + begin + gMapGrid.forEachAtPoint(X, Y, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2)); + end + else + begin + gMapGrid.forEachInAABB(X, Y, Width, Height, checker, (GridTagWater or GridTagAcid1 or GridTagAcid2)); + end; end; result := texid; end -- 2.29.2