Fix pump to clear the last row of pixels (#1057) and allow pumping from pump without source pipe.

ExtractMaterial has been changed to be able to slurp in from the most distant horizontal position rather than the closest to the extraction top center.

Also speed up ExtractMaterial for the common case of no required horizontal shifts.
stable-6.1
Sven Eberhardt 2015-05-01 18:04:42 +02:00 committed by Maikel de Vries
parent 05dba4cadc
commit 9349498f3f
10 changed files with 61 additions and 37 deletions

View File

@ -31,6 +31,12 @@
<name>amount</name>
<desc>Maximum amount to be extracted.</desc>
</param>
<param>
<type>bool</type>
<name>distant_x</name>
<desc>If true, material will be extracted at the most distant horizontal position of the top row of the material body, to a maximum of the material's MaxSlide parameter. If used on liquids, this mode ensures that level pools of 1px height can be extracted by extracting from anywhere horizontally.</desc>
<optional />
</param>
</params>
</syntax>
<desc>Extracts a certain amount of material at the specified position. The return value is the amount actually extracted.</desc>

View File

@ -8,6 +8,7 @@ local Description = "$Description$";
local UsageHelp = "$UsageHelp$";
local Collectible = 1;
local Rebuy = true;
local ApertureOffsetY = 3; // pump from bottom vertex
protected func Hit()
{

View File

@ -167,7 +167,8 @@ protected func Pumping()
if (!stored_material_amount)
{
// get new materials
var mat = GetSourceObject()->ExtractLiquidAmount(0, 0, GetPumpSpeed() / 10);
var source_obj = GetSourceObject();
var mat = source_obj->ExtractLiquidAmount(source_obj.ApertureOffsetX, source_obj.ApertureOffsetY, GetPumpSpeed() / 10, true);
// no material to pump?
if (mat)
@ -185,7 +186,8 @@ protected func Pumping()
var i = stored_material_amount;
while (i > 0)
{
if (GetDrainObject()->InsertMaterial(stored_material_index))
var drain_obj = GetDrainObject();
if (GetDrainObject()->InsertMaterial(stored_material_index, drain_obj.ApertureOffsetX, drain_obj.ApertureOffsetY))
{
i--;
}
@ -250,17 +252,20 @@ private func GetPumpHeight()
// compare each the surfaces of the bodies of liquid pumped
// find Y position of surface of liquid that is pumped to target
var source_y = 0;
if (GetSourceObject()->GBackLiquid())
var source_obj = GetSourceObject();
var source_x = source_obj.ApertureOffsetX;
var source_y = source_obj.ApertureOffsetY;
if (source_obj->GBackLiquid(source_x, source_y))
{
var src_mat = GetSourceObject()->GetMaterial();
while (src_mat == GetSourceObject()->GetMaterial(0, source_y - 1))
var src_mat = source_obj->GetMaterial(source_x, source_y);
while (src_mat == source_obj->GetMaterial(source_x, source_y - 1))
--source_y;
}
// same for target (use same function as if inserting)
var target_pos = {X = 0, Y = 0};
GetDrainObject()->CanInsertMaterial(Material("Water"), 0, 0, target_pos);
return GetSourceObject()->GetY() + source_y - target_pos.Y;
var drain_obj = GetDrainObject();
drain_obj->CanInsertMaterial(Material("Water"), drain_obj.ApertureOffsetX, drain_obj.ApertureOffsetY, target_pos);
return source_obj->GetY() + source_y - target_pos.Y;
}
/** Recheck power usage/production for current pump height
@ -340,15 +345,14 @@ private func PumpHeight2Power(int pump_height)
/** Returns whether there is liquid at the source pipe to pump */
private func HasLiquidToPump()
{
if (!source_pipe)
return false;
// source
if(!GetSourceObject()->GBackLiquid())
var source_obj = GetSourceObject();
if(!source_obj->GBackLiquid(source_obj.ApertureOffsetX, source_obj.ApertureOffsetY))
return false;
// target (test with the very popular liquid "water")
if(!GetDrainObject()->CanInsertMaterial(Material("Water"),0,0))
var drain_obj = GetDrainObject();
if(!drain_obj->CanInsertMaterial(Material("Water"),drain_obj.ApertureOffsetX, drain_obj.ApertureOffsetY))
return false;
return true;

View File

@ -54,10 +54,11 @@ global func FindPosInMat(string sMat, int iXStart, int iYStart, int iWidth, int
/** Removes a material pixel from the specified location, if the material is a liquid.
@param x X coordinate
@param y Y coordinate
@param distant_first If true, extraction position takes largest horizontal distance from given offset at same height to a maximum value of MaxSlide. Useful to ensure that no floor of 1px of liquid remains.
@return The material index of the removed pixel, or -1 if no liquid was found. */
global func ExtractLiquid(int x, int y)
global func ExtractLiquid(int x, int y, bool distant_first)
{
var result = ExtractLiquidAmount(x, y, 1);
var result = ExtractLiquidAmount(x, y, 1, distant_first);
if(!result) return -1;
return result[0];
@ -67,9 +68,10 @@ global func ExtractLiquid(int x, int y)
@param x X coordinate
@param y Y coordinate
@param amount amount of liquid that should be extracted
@param distant_first If true, extraction position takes largest horizontal distance from given offset at same height to a maximum value of MaxSlide. Useful to ensure that no floor of 1px of liquid remains.
@return an array with the first position being the material index being extracted and the second the
actual amount of pixels extracted OR nil if there was no liquid at all */
global func ExtractLiquidAmount(int x, int y, int amount)
global func ExtractLiquidAmount(int x, int y, int amount, bool distant_first)
{
var mat = GetMaterial(x, y);
if(mat == -1)
@ -77,7 +79,7 @@ global func ExtractLiquidAmount(int x, int y, int amount)
var density = GetMaterialVal("Density", "Material", mat);
if (density < C4M_Liquid || density >= C4M_Solid)
return nil;
var amount = ExtractMaterialAmount(x, y, mat, amount);
var amount = ExtractMaterialAmount(x, y, mat, amount, distant_first);
if (amount <= 0)
return nil;
return [mat, amount];

View File

@ -607,7 +607,7 @@ void Splash(int32_t tx, int32_t ty, int32_t amt, C4Object *pByObj)
{
C4Real xdir = C4REAL100(Random(151)-75);
C4Real ydir = C4REAL100(-Random(200));
::PXS.Create(::Landscape.ExtractMaterial(tx,ty),
::PXS.Create(::Landscape.ExtractMaterial(tx,ty,false),
itofix(tx),itofix(sy),
xdir,
ydir);

View File

@ -445,13 +445,13 @@ static bool FnGBackSky(C4PropList * _this, long x, long y)
return !GBackIFT(x, y);
}
static long FnExtractMaterialAmount(C4PropList * _this, long x, long y, long mat, long amount)
static long FnExtractMaterialAmount(C4PropList * _this, long x, long y, long mat, long amount, bool distant_first)
{
if (Object(_this)) { x+=Object(_this)->GetX(); y+=Object(_this)->GetY(); }
long extracted=0; for (; extracted<amount; extracted++)
{
if (GBackMat(x,y)!=mat) return extracted;
if (::Landscape.ExtractMaterial(x,y)!=mat) return extracted;
if (::Landscape.ExtractMaterial(x,y,distant_first)!=mat) return extracted;
}
return extracted;
}

View File

@ -885,11 +885,11 @@ void C4Landscape::RaiseTerrain(int32_t tx, int32_t ty, int32_t wdt)
}
int32_t C4Landscape::ExtractMaterial(int32_t fx, int32_t fy)
int32_t C4Landscape::ExtractMaterial(int32_t fx, int32_t fy, bool distant_first)
{
int32_t mat=GetMat(fx,fy);
if (mat==MNone) return MNone;
FindMatTop(mat,fx,fy);
FindMatTop(mat,fx,fy,distant_first);
ClearPix(fx,fy);
CheckInstabilityRange(fx,fy);
return mat;
@ -3039,9 +3039,9 @@ int32_t C4Landscape::AreaSolidCount(int32_t x, int32_t y, int32_t wdt, int32_t h
return ascnt;
}
void C4Landscape::FindMatTop(int32_t mat, int32_t &x, int32_t &y) const
void C4Landscape::FindMatTop(int32_t mat, int32_t &x, int32_t &y, bool distant_first) const
{
int32_t mslide,cslide,tslide; // tslide 0 none 1 left 2 right
int32_t mslide,cslide,tslide,distant_x=0;
bool fLeft,fRight;
if (!MatValid(mat)) return;
@ -3049,33 +3049,44 @@ void C4Landscape::FindMatTop(int32_t mat, int32_t &x, int32_t &y) const
do
{
// Catch most common case: Walk upwards until material changes
while (GetMat(x,y-1)==mat) --y;
// Find upwards slide
fLeft=true; fRight=true; tslide=0;
for (cslide=0; (cslide<=mslide) && (fLeft || fRight); cslide++)
fLeft=true; fRight=true; tslide=0; distant_x=x;
for (cslide=1; (cslide<=mslide) && (fLeft || fRight); cslide++)
{
// Left
if (fLeft)
{
if (GetMat(x-cslide,y)!=mat) fLeft=false;
else if (GetMat(x-cslide,y-1)==mat) { tslide=1; break; }
else
{
distant_x = x-cslide;
if (GetMat(distant_x,y-1)==mat) { tslide=-cslide; break; }
}
}
// Right
if (fRight)
{
if (GetMat(x+cslide,y)!=mat) fRight=false;
else if (GetMat(x+cslide,y-1)==mat) { tslide=2; break; }
else
{
distant_x = x+cslide;
if (GetMat(distant_x,y-1)==mat) { tslide=+cslide; break; }
}
}
}
// Slide
if (tslide==1) { x-=cslide; y--; }
if (tslide==2) { x+=cslide; y--; }
if (tslide) { x+=tslide; y--; }
}
while (tslide);
// return top pixel max slide away from center if desired
if (distant_first) x = distant_x;
}
/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
/* ++++++++++++++ Editor mode (draw landscape with brush)+++++++++++++++++++ */

View File

@ -90,7 +90,7 @@ public:
void DrawMaterialRect(int32_t mat, int32_t tx, int32_t ty, int32_t wdt, int32_t hgt);
void RaiseTerrain(int32_t tx, int32_t ty, int32_t wdt);
void FindMatTop(int32_t mat, int32_t &x, int32_t &y) const;
void FindMatTop(int32_t mat, int32_t &x, int32_t &y, bool distant_first) const;
BYTE GetMapIndex(int32_t iX, int32_t iY) const;
bool Load(C4Group &hGroup, bool fLoadSky, bool fSavegame);
bool Save(C4Group &hGroup) const;
@ -188,7 +188,7 @@ public:
int32_t GetMatHeight(int32_t x, int32_t y, int32_t iYDir, int32_t iMat, int32_t iMax) const;
int32_t AreaSolidCount(int32_t x, int32_t y, int32_t wdt, int32_t hgt) const;
int32_t ExtractMaterial(int32_t fx, int32_t fy);
int32_t ExtractMaterial(int32_t fx, int32_t fy, bool distant_first);
bool DrawMap(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, const char *szMapDef, bool ignoreSky = false); // creates and draws a map section using MapCreatorS2
bool ClipRect(int32_t &rX, int32_t &rY, int32_t &rWdt, int32_t &rHgt) const; // clip given rect by landscape size; return whether anything is left unclipped
bool DrawDefMap(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, const char *szMapDef, bool ignoreSky = false); // creates and draws a map section using MapCreatorS2 and a map from the loaded Landscape.txt

View File

@ -140,7 +140,7 @@ bool C4MassMover::Execute()
if (Corrosion(+0,+1) || Corrosion(-1,+0) || Corrosion(+1,+0))
{
// material has been used up
::Landscape.ExtractMaterial(x,y);
::Landscape.ExtractMaterial(x,y,false);
return true;
}
@ -157,7 +157,7 @@ bool C4MassMover::Execute()
}*/
// Transfer mass
int32_t mat = ::Landscape.ExtractMaterial(x,y);
int32_t mat = ::Landscape.ExtractMaterial(x,y,false);
if (Random(10))
::Landscape.InsertDeadMaterial(mat, tx, ty);
else

View File

@ -885,7 +885,7 @@ bool C4MaterialMap::mrfPoof(C4MaterialReaction *pReaction, int32_t &iX, int32_t
{
case meeMassMove: // MassMover-movement
case meePXSPos: // PXS check before movement: Kill both landscape and PXS mat
::Landscape.ExtractMaterial(iLSPosX,iLSPosY);
::Landscape.ExtractMaterial(iLSPosX,iLSPosY,false);
if (!Random(3)) Smoke(iX,iY,3);
if (!Random(3)) StartSoundEffectAt("Pshshsh", iX, iY);
return true;
@ -897,7 +897,7 @@ bool C4MaterialMap::mrfPoof(C4MaterialReaction *pReaction, int32_t &iX, int32_t
// either splash or slide prevented interaction
return false;
// Always kill both landscape and PXS mat
::Landscape.ExtractMaterial(iLSPosX,iLSPosY);
::Landscape.ExtractMaterial(iLSPosX,iLSPosY,false);
if (!Random(3)) Smoke(iX,iY,3);
if (!Random(3)) StartSoundEffectAt("Pshshsh", iX, iY);
return true;