2025-07-18 01:58 CEST

View Issue Details Jump to Notes ]
IDProjectCategoryView StatusLast Update
0000262Spring engineGeneralpublic2007-04-02 08:12
Reporteracidd_uk 
Assigned ToILMTitan 
PrioritynormalSeverityfeatureReproducibilityN/A
StatusresolvedResolutionfixed 
Product Version 
Target VersionFixed in Version 
Summary0000262: Gradual and chunk reclaim
DescriptionSee these forum threads:

Feature suggestion: http://taspring.clan-sy.com/phpbb/viewtopic.php?t=6134
Dev thread: http://taspring.clan-sy.com/phpbb/viewtopic.php?t=6242
Additional InformationThis patch allows for the mod maker to choose 3 possible relcaim modes and also allows for selectable multi-reclaiming (ie effective reclaiming by several units at once).

A new (optional) .tdf is added to the mod: gamedata/reclaim.tdf

This tdf has one main section "RECLAIM" within which;
  multiReclaim = 0 or 1 to allow only 1 or >1 unit to simultaneously reclaim a feature/corpse
  reclaimMethod = 0, 1 or n (int n>1)
    0 is gradual reclaim (ie a small % is reclaimed every tick)
    1 is current behaviour - ie one large hit of resources at the end of the reclim period
    n is chunky behaviour - reclaim happens in n equal sized chunks

Please note, this patch is currently only submitted for comments - it is not rready to be submitted due to a potential exploit regarding partial reclaim and resurrection. I am in the process of fixing this, once it's done I will update this patch and it should be ready for submission.
TagsNo tags attached.
Checked infolog.txt for Errors
Attached Files
  • patch file icon gradual-reclaim-0.1.patch (6,117 bytes) 2006-08-02 20:56 -
    Index: rts/Game/UI/MouseHandler.cpp
    ===================================================================
    --- rts/Game/UI/MouseHandler.cpp	(revision 1727)
    +++ rts/Game/UI/MouseHandler.cpp	(working copy)
    @@ -591,11 +591,17 @@
     		} else {
     			s=feature->def->description;
     		}
    -		std::string metalColor = feature->def->metal > 0 ? "\xff\x50\xff\x50" : "\xff\xff\x50\x01";
    -		std::string energyColor = feature->def->energy > 0 ? "\xff\x50\xff\x50" : "\xff\xff\x50\x01";
    +
    +		float remainingMetal = feature->RemainingMetal();
    +		float remainingEnergy = feature->RemainingEnergy();
    +
    +		std::string metalColor = remainingMetal > 0 ? "\xff\x50\xff\x50" : "\xff\xff\x50\x01";
    +		std::string energyColor = remainingEnergy > 0 ? "\xff\x50\xff\x50" : "\xff\xff\x50\x01";
    +		
     		char tmp[500];
     		sprintf(tmp,"\n\xff\xd3\xdb\xffMetal: %s%.0f \xff\xd3\xdb\xff Energy: %s%.0f",
    -			metalColor.c_str(), feature->def->metal, energyColor.c_str(), feature->def->energy);
    +			metalColor.c_str(), remainingMetal,
    +			energyColor.c_str(), remainingEnergy);
     		s+=tmp;
     
     		return s;
    Index: rts/Sim/Misc/Feature.cpp
    ===================================================================
    --- rts/Sim/Misc/Feature.cpp	(revision 1727)
    +++ rts/Sim/Misc/Feature.cpp	(working copy)
    @@ -137,24 +137,61 @@
     
     bool CFeature::AddBuildPower(float amount, CUnit* builder)
     {
    +	float oldReclaimLeft = reclaimLeft;
    +	float fractionReclaimed;
     	if(amount>0){
    -		return false;		//cant repair a feature
    +		return false;		// cant repair a feature
     	} else {
    -		if(reclaimLeft<0)	//avoid multisuck :)
    +		if(reclaimLeft <= 0)	// avoid multisuck when reclaim has completed this tick
     			return false;
    -		if(lastReclaim==gs->frameNum)	//make sure several units cant reclaim at once on a single feature
    +
    +		if(featureHandler->multiReclaim == 0 && lastReclaim == gs->frameNum) // make sure several units cant reclaim at once on a single feature
     			return true;
    +		
     		float part=(100-amount)*0.02/max(10.0f,(def->metal+def->energy));
     		reclaimLeft-=part;
    -		lastReclaim=gs->frameNum;
    -		if(reclaimLeft<0){
    +		
    +		// Stop the last bit giving too much resource
    +		if(reclaimLeft < 0) reclaimLeft = 0;
    +
    +		fractionReclaimed = oldReclaimLeft-reclaimLeft;
    +
    +		if(featureHandler->reclaimMethod == 1 && reclaimLeft == 0) // All-at-end method
    +		{
     			builder->AddMetal(def->metal);
     			builder->AddEnergy(def->energy);
    +		}
    +		else if(featureHandler->reclaimMethod == 0) // Gradual reclaim
    +		{
    +			builder->AddMetal(def->metal * fractionReclaimed);
    +			builder->AddEnergy(def->energy * fractionReclaimed);
    +		}
    +		else  // Chunky reclaiming
    +		{
    +			// Work out how many chunk boundaries we crossed
    +			float chunkSize = 1.0 / featureHandler->reclaimMethod;
    +			int oldChunk = ChunkNumber(oldReclaimLeft);
    +			int newChunk = ChunkNumber(reclaimLeft);
    +			if (oldChunk != newChunk)
    +			{
    +				float noChunks = (float)oldChunk - (float)newChunk;
    +				builder->AddMetal(noChunks * def->metal * chunkSize);
    +				builder->AddEnergy(noChunks * def->energy * chunkSize);
    +			}
    +		}
    +
    +		// Has the reclaim finished?
    +		if(reclaimLeft<=0)
    +		{
     			featureHandler->DeleteFeature(this);
     			return false;
     		}
    +
    +		lastReclaim=gs->frameNum;
     		return true;
     	}
    +	// Should never get here
    +	assert(false);
     	return false;
     }
     
    @@ -250,5 +287,36 @@
     	glPopMatrix();
     }
     
    +int CFeature::ChunkNumber(float f)
    +{
    +	return (int) ceil(f * featureHandler->reclaimMethod);	
    +}
    +
    +float CFeature::RemainingResource(float res)
    +{
    +	// Old style - all reclaimed at the end
    +	if(featureHandler->reclaimMethod == 0)
    +		return res * reclaimLeft;
    +
    +	// Gradual reclaim
    +	if(featureHandler->reclaimMethod == 1)
    +		return res;
    +
    +	// Otherwise we are doing chunk reclaiming
    +	float chunkSize = res / featureHandler->reclaimMethod; // resource/no_chunks
    +	float chunksLeft = ceil(reclaimLeft * featureHandler->reclaimMethod);
    +	return chunkSize * chunksLeft;
    +}
    +
    +float CFeature::RemainingMetal()
    +{
    +	return RemainingResource(def->metal);
    +}
    +float CFeature::RemainingEnergy()
    +{
    +	return RemainingResource(def->energy);
    +}
    +
    +
     FeatureDef::~FeatureDef() {
     }
    Index: rts/Sim/Misc/Feature.h
    ===================================================================
    --- rts/Sim/Misc/Feature.h	(revision 1727)
    +++ rts/Sim/Misc/Feature.h	(working copy)
    @@ -27,6 +27,10 @@
     	void Kill(float3& impulse);
     	virtual bool Update(void);
     	void StartFire(void);
    +	float RemainingResource(float res);
    +	float RemainingMetal(void);
    +	float RemainingEnergy(void);
    +	int ChunkNumber(float f);
     	void DrawS3O();
     	void CalculateTransform();
     	CUnit* LastBuilder;
    Index: rts/Sim/Misc/FeatureHandler.cpp
    ===================================================================
    --- rts/Sim/Misc/FeatureHandler.cpp	(revision 1727)
    +++ rts/Sim/Misc/FeatureHandler.cpp	(working copy)
    @@ -46,6 +46,24 @@
     	LoadWreckFeatures();
     
     	treeDrawer=CBaseTreeDrawer::GetTreeDrawer();
    +
    +	// Get the reclaim options for the mod
    +	
    +	// Defaults:
    +	multiReclaim = 0;
    +	reclaimMethod = 1;
    +	
    +	// See if the mod overrides the defaults:
    +	try
    +	{
    +		TdfParser reclaimOptions("gamedata/RECLAIM.tdf");
    +		multiReclaim = atoi(reclaimOptions.SGetValueDef("0", "RECLAIM\\MultiReclaim").c_str());
    +		reclaimMethod = atoi(reclaimOptions.SGetValueDef("1", "RECLAIM\\ReclaimMethod").c_str());
    +	}
    +	catch(content_error) // If the RECLAIM.tdf isnt found
    +	{
    +		// We already set the defaults so we should be able to ignore this
    +	}
     }
     
     CFeatureHandler::~CFeatureHandler()
    Index: rts/Sim/Misc/FeatureHandler.h
    ===================================================================
    --- rts/Sim/Misc/FeatureHandler.h	(revision 1727)
    +++ rts/Sim/Misc/FeatureHandler.h	(working copy)
    @@ -67,6 +67,11 @@
     	int drawQuadsY;
     	int numQuads;
     
    +	// Reclaim behaviour
    +	int multiReclaim;	// 0 = 1 reclaimer per feature max, otherwise unlimited
    +	int reclaimMethod;		// 0 = gradual reclaim, 1 = all reclaimed at end,  
    +								//     otherwise reclaim in reclaimMethod chunks
    +
     	int overrideId;		//used when loading from savefile
     	void DrawFar(CFeature* feature,CVertexArray* va);
     	FeatureDef* GetFeatureDef(const std::string name);
    
    patch file icon gradual-reclaim-0.1.patch (6,117 bytes) 2006-08-02 20:56 +
  • patch file icon gradual-reclaim-0.2.patch (10,678 bytes) 2006-08-04 19:00 -
    Index: rts/Game/UI/MouseHandler.cpp
    ===================================================================
    --- rts/Game/UI/MouseHandler.cpp	(revision 1747)
    +++ rts/Game/UI/MouseHandler.cpp	(working copy)
    @@ -591,11 +591,17 @@
     		} else {
     			s=feature->def->description;
     		}
    -		std::string metalColor = feature->def->metal > 0 ? "\xff\x50\xff\x50" : "\xff\xff\x50\x01";
    -		std::string energyColor = feature->def->energy > 0 ? "\xff\x50\xff\x50" : "\xff\xff\x50\x01";
    +
    +		float remainingMetal = feature->RemainingMetal();
    +		float remainingEnergy = feature->RemainingEnergy();
    +
    +		std::string metalColor = remainingMetal > 0 ? "\xff\x50\xff\x50" : "\xff\xff\x50\x01";
    +		std::string energyColor = remainingEnergy > 0 ? "\xff\x50\xff\x50" : "\xff\xff\x50\x01";
    +		
     		char tmp[500];
     		sprintf(tmp,"\n\xff\xd3\xdb\xffMetal: %s%.0f \xff\xd3\xdb\xff Energy: %s%.0f",
    -			metalColor.c_str(), feature->def->metal, energyColor.c_str(), feature->def->energy);
    +			metalColor.c_str(), remainingMetal,
    +			energyColor.c_str(), remainingEnergy);
     		s+=tmp;
     
     		return s;
    Index: rts/Sim/Misc/Feature.cpp
    ===================================================================
    --- rts/Sim/Misc/Feature.cpp	(revision 1747)
    +++ rts/Sim/Misc/Feature.cpp	(working copy)
    @@ -7,9 +7,11 @@
     #include "QuadField.h"
     #include "DamageArray.h"
     #include "Map/ReadMap.h"
    +#include "Game/Team.h"
     #include "Game/UI/InfoConsole.h"
     #include "Sim/Units/Unit.h"
     #include "Rendering/Env/BaseTreeDrawer.h"
    +#include "Sim/ModInfo.h"
     #include "Sim/Projectiles/FireProjectile.h"
     #include "Sim/Projectiles/SmokeProjectile.h"
     #include "Sim/Projectiles/ProjectileHandler.h"
    @@ -20,6 +22,7 @@
     
     CR_REG_METADATA(CFeature, (
     				CR_MEMBER(createdFromUnit),
    +				CR_MEMBER(isRepairingBeforeResurrect),
     				CR_MEMBER(resurrectProgress),
     				CR_MEMBER(health),
     				CR_MEMBER(reclaimLeft),
    @@ -47,6 +50,7 @@
     	tempNum(0),
     	emitSmokeTime(0),
     	lastReclaim(0),
    +	isRepairingBeforeResurrect(false),
     	resurrectProgress(0),
     	health(0),
     	id(0),
    @@ -137,24 +141,98 @@
     
     bool CFeature::AddBuildPower(float amount, CUnit* builder)
     {
    +	float oldReclaimLeft = reclaimLeft;
    +	float fractionReclaimed;
     	if(amount>0){
    -		return false;		//cant repair a feature
    +		
    +		// Check they are trying to repair a feature that can be resurrected
    +		if(createdFromUnit == "")
    +			return false;
    +
    +		// 'Repairing' previously-sucked features prior to resurrection
    +		// This is reclaim-option independant - repairing features should always
    +		// be like other repairing - gradual and multi-unit
    +		// Lots of this code is stolen from unit->AddBuildPower
    +		
    +		isRepairingBeforeResurrect = true; // Stop them exploiting chunk reclaiming
    +
    +		if (reclaimLeft >= 1)
    +			return false;		// cant repair a 'fresh' feature
    +
    +		// Work out how much to try to put back, based on the speed this unit would reclaim at.
    +		float part=(100-amount)*0.02/max(10.0f,(def->metal+def->energy));
    +
    +		// Work out how much that will cost
    +		float metalUse=def->metal*part;
    +		float energyUse=def->energy*part;
    +		if (gs->Team(builder->team)->metal >= metalUse && gs->Team(builder->team)->energy >= energyUse)
    +		{
    +			builder->UseMetal(metalUse);
    +			builder->UseEnergy(energyUse);
    +			reclaimLeft+=part;
    +			if(reclaimLeft>=1)
    +				isRepairingBeforeResurrect = false; // They can start reclaiming it again if they so wish
    +				reclaimLeft = 1;
    +			return true;
    +		}
    +		return false;
    +
    +
     	} else {
    -		if(reclaimLeft<0)	//avoid multisuck :)
    +		// Reclaiming
    +		if(reclaimLeft <= 0)	// avoid multisuck when reclaim has already completed during this frame
     			return false;
    -		if(lastReclaim==gs->frameNum)	//make sure several units cant reclaim at once on a single feature
    +		
    +		if(isRepairingBeforeResurrect && modInfo->reclaimMethod > 1) // don't let them exploit chunk reclaim
    +			return false;
    +		
    +		if(modInfo->multiReclaim == 0 && lastReclaim == gs->frameNum) // make sure several units cant reclaim at once on a single feature
     			return true;
    +		
     		float part=(100-amount)*0.02/max(10.0f,(def->metal+def->energy));
     		reclaimLeft-=part;
    -		lastReclaim=gs->frameNum;
    -		if(reclaimLeft<0){
    +		
    +		// Stop the last bit giving too much resource
    +		if(reclaimLeft < 0) reclaimLeft = 0;
    +
    +		fractionReclaimed = oldReclaimLeft-reclaimLeft;
    +
    +		if(modInfo->reclaimMethod == 1 && reclaimLeft == 0) // All-at-end method
    +		{
     			builder->AddMetal(def->metal);
     			builder->AddEnergy(def->energy);
    +		}
    +		else if(modInfo->reclaimMethod == 0) // Gradual reclaim
    +		{
    +			builder->AddMetal(def->metal * fractionReclaimed);
    +			builder->AddEnergy(def->energy * fractionReclaimed);
    +		}
    +		else  // Chunky reclaiming
    +		{
    +			// Work out how many chunk boundaries we crossed
    +			float chunkSize = 1.0 / modInfo->reclaimMethod;
    +			int oldChunk = ChunkNumber(oldReclaimLeft);
    +			int newChunk = ChunkNumber(reclaimLeft);
    +			if (oldChunk != newChunk)
    +			{
    +				float noChunks = (float)oldChunk - (float)newChunk;
    +				builder->AddMetal(noChunks * def->metal * chunkSize);
    +				builder->AddEnergy(noChunks * def->energy * chunkSize);
    +			}
    +		}
    +
    +		// Has the reclaim finished?
    +		if(reclaimLeft<=0)
    +		{
     			featureHandler->DeleteFeature(this);
     			return false;
     		}
    +
    +		lastReclaim=gs->frameNum;
     		return true;
     	}
    +	// Should never get here
    +	assert(false);
     	return false;
     }
     
    @@ -250,5 +328,36 @@
     	glPopMatrix();
     }
     
    +int CFeature::ChunkNumber(float f)
    +{
    +	return (int) ceil(f * modInfo->reclaimMethod);	
    +}
    +
    +float CFeature::RemainingResource(float res)
    +{
    +	// Old style - all reclaimed at the end
    +	if(modInfo->reclaimMethod == 0)
    +		return res * reclaimLeft;
    +
    +	// Gradual reclaim
    +	if(modInfo->reclaimMethod == 1)
    +		return res;
    +
    +	// Otherwise we are doing chunk reclaiming
    +	float chunkSize = res / modInfo->reclaimMethod; // resource/no_chunks
    +	float chunksLeft = ceil(reclaimLeft * modInfo->reclaimMethod);
    +	return chunkSize * chunksLeft;
    +}
    +
    +float CFeature::RemainingMetal()
    +{
    +	return RemainingResource(def->metal);
    +}
    +float CFeature::RemainingEnergy()
    +{
    +	return RemainingResource(def->energy);
    +}
    +
    +
     FeatureDef::~FeatureDef() {
     }
    Index: rts/Sim/Misc/Feature.h
    ===================================================================
    --- rts/Sim/Misc/Feature.h	(revision 1747)
    +++ rts/Sim/Misc/Feature.h	(working copy)
    @@ -27,11 +27,20 @@
     	void Kill(float3& impulse);
     	virtual bool Update(void);
     	void StartFire(void);
    +	float RemainingResource(float res);
    +	float RemainingMetal(void);
    +	float RemainingEnergy(void);
    +	int ChunkNumber(float f);
     	void DrawS3O();
     	void CalculateTransform();
     	CUnit* LastBuilder;
     
     	std::string createdFromUnit;
    +	// This flag is used to stop a potential exploit involving tripping a unit back and forth
    +	// across a chunk boundary to get unlimited resources. Basically, once a corspe has been a little bit
    +	// reclaimed, if they start rezzing then they cannot reclaim again until the corpse has been fully
    +	// 'repaired'.
    +	bool isRepairingBeforeResurrect;
     	float resurrectProgress;
     
     	float health;
    Index: rts/Sim/ModInfo.cpp
    ===================================================================
    --- rts/Sim/ModInfo.cpp	(revision 1747)
    +++ rts/Sim/ModInfo.cpp	(working copy)
    @@ -18,6 +18,22 @@
     		// Load the users preference for team coloured nanospray
     		gu->teamNanospray = configHandler.GetInt ("TeamNanoSpray", 0);
     	}
    +
    +	// Get the reclaim options for the mod
    +	multiReclaim = 0;
    +	reclaimMethod = 1;
    +	// See if the mod overrides the defaults:
    +	try
    +	{
    +		TdfParser reclaimOptions("gamedata/RECLAIM.tdf");
    +		multiReclaim = atoi(reclaimOptions.SGetValueDef("0", "RECLAIM\\MultiReclaim").c_str());
    +		reclaimMethod = atoi(reclaimOptions.SGetValueDef("1", "RECLAIM\\ReclaimMethod").c_str());
    +	}
    +	catch(content_error) // If the RECLAIM.tdf isnt found
    +	{
    +		// We already set the defaults so we should be able to ignore this
    +	}
    +
     }
     
     CModInfo::~CModInfo(){}
    Index: rts/Sim/ModInfo.h
    ===================================================================
    --- rts/Sim/ModInfo.h	(revision 1747)
    +++ rts/Sim/ModInfo.h	(working copy)
    @@ -10,6 +10,11 @@
     
     	std::string name;
     	bool allowTeamColors;
    +
    +	// Reclaim behaviour
    +	int multiReclaim;	// 0 = 1 reclaimer per feature max, otherwise unlimited
    +	int reclaimMethod;	// 0 = gradual reclaim, 1 = all reclaimed at end, otherwise reclaim in reclaimMethod chunks
    +
     };
     
     extern CModInfo *modInfo;
    Index: rts/Sim/Units/UnitTypes/Builder.cpp
    ===================================================================
    --- rts/Sim/Units/UnitTypes/Builder.cpp	(revision 1747)
    +++ rts/Sim/Units/UnitTypes/Builder.cpp	(working copy)
    @@ -6,6 +6,7 @@
     #include "Builder.h"
     #include "Building.h"
     #include "Sim/Units/UnitLoader.h"
    +#include "Sim/ModInfo.h"
     #include "Sim/Projectiles/GfxProjectile.h"
     #include "Game/GameHelper.h"
     #include "Sim/Units/UnitHandler.h"
    @@ -196,18 +197,28 @@
     	} else if(curResurrect && curResurrect->pos.distance2D(pos)<buildDistance+curResurrect->radius && inBuildStance){
     		UnitDef* ud=unitDefHandler->GetUnitByName(curResurrect->createdFromUnit);
     		if(ud){
    -			if(UseEnergy(ud->energyCost*buildSpeed/ud->buildTime*0.5)){
    -				curResurrect->resurrectProgress+=buildSpeed/ud->buildTime;
    -				CreateNanoParticle(curResurrect->midPos,curResurrect->radius*0.7,gs->randInt()&1);
    +			if( modInfo->reclaimMethod != 1 && curResurrect->reclaimLeft < 1)
    +			{
    +				// This corpse has been reclaimed a little, need to restore the resources
    +				// before we can let the player resurrect it.
    +				curReclaim->AddBuildPower(buildSpeed,this);
     			}
    -			if(curResurrect->resurrectProgress>1){		//resurrect finished
    -				curResurrect->UnBlock();
    -				CUnit* u=unitLoader.LoadUnit(curResurrect->createdFromUnit,curResurrect->pos,team,false,curResurrect->buildFacing);
    -				u->health*=0.05;
    -				lastResurrected=u->id;
    -				curResurrect->resurrectProgress=0;
    -				featureHandler->DeleteFeature(curResurrect);
    -				StopBuild(true);
    +			else
    +			{
    +				// Corpse has been restored, begin resurrection
    +				if(UseEnergy(ud->energyCost*buildSpeed/ud->buildTime*0.5)){
    +					curResurrect->resurrectProgress+=buildSpeed/ud->buildTime;
    +					CreateNanoParticle(curResurrect->midPos,curResurrect->radius*0.7,gs->randInt()&1);
    +				}
    +				if(curResurrect->resurrectProgress>1){		//resurrect finished
    +					curResurrect->UnBlock();
    +					CUnit* u=unitLoader.LoadUnit(curResurrect->createdFromUnit,curResurrect->pos,team,false,curResurrect->buildFacing);
    +					u->health*=0.05;
    +					lastResurrected=u->id;
    +					curResurrect->resurrectProgress=0;
    +					featureHandler->DeleteFeature(curResurrect);
    +					StopBuild(true);
    +				}
     			}
     		} else {
     			StopBuild(true);
    
    patch file icon gradual-reclaim-0.2.patch (10,678 bytes) 2006-08-04 19:00 +
  • patch file icon gradual-reclaim-0.3.patch (10,739 bytes) 2006-08-04 19:06 -
    Index: rts/Game/UI/MouseHandler.cpp
    ===================================================================
    --- rts/Game/UI/MouseHandler.cpp	(revision 1747)
    +++ rts/Game/UI/MouseHandler.cpp	(working copy)
    @@ -591,11 +591,17 @@
     		} else {
     			s=feature->def->description;
     		}
    -		std::string metalColor = feature->def->metal > 0 ? "\xff\x50\xff\x50" : "\xff\xff\x50\x01";
    -		std::string energyColor = feature->def->energy > 0 ? "\xff\x50\xff\x50" : "\xff\xff\x50\x01";
    +
    +		float remainingMetal = feature->RemainingMetal();
    +		float remainingEnergy = feature->RemainingEnergy();
    +
    +		std::string metalColor = remainingMetal > 0 ? "\xff\x50\xff\x50" : "\xff\xff\x50\x01";
    +		std::string energyColor = remainingEnergy > 0 ? "\xff\x50\xff\x50" : "\xff\xff\x50\x01";
    +		
     		char tmp[500];
     		sprintf(tmp,"\n\xff\xd3\xdb\xffMetal: %s%.0f \xff\xd3\xdb\xff Energy: %s%.0f",
    -			metalColor.c_str(), feature->def->metal, energyColor.c_str(), feature->def->energy);
    +			metalColor.c_str(), remainingMetal,
    +			energyColor.c_str(), remainingEnergy);
     		s+=tmp;
     
     		return s;
    Index: rts/Sim/Misc/Feature.cpp
    ===================================================================
    --- rts/Sim/Misc/Feature.cpp	(revision 1747)
    +++ rts/Sim/Misc/Feature.cpp	(working copy)
    @@ -7,9 +7,11 @@
     #include "QuadField.h"
     #include "DamageArray.h"
     #include "Map/ReadMap.h"
    +#include "Game/Team.h"
     #include "Game/UI/InfoConsole.h"
     #include "Sim/Units/Unit.h"
     #include "Rendering/Env/BaseTreeDrawer.h"
    +#include "Sim/ModInfo.h"
     #include "Sim/Projectiles/FireProjectile.h"
     #include "Sim/Projectiles/SmokeProjectile.h"
     #include "Sim/Projectiles/ProjectileHandler.h"
    @@ -20,6 +22,7 @@
     
     CR_REG_METADATA(CFeature, (
     				CR_MEMBER(createdFromUnit),
    +				CR_MEMBER(isRepairingBeforeResurrect),
     				CR_MEMBER(resurrectProgress),
     				CR_MEMBER(health),
     				CR_MEMBER(reclaimLeft),
    @@ -47,6 +50,7 @@
     	tempNum(0),
     	emitSmokeTime(0),
     	lastReclaim(0),
    +	isRepairingBeforeResurrect(false),
     	resurrectProgress(0),
     	health(0),
     	id(0),
    @@ -137,24 +141,98 @@
     
     bool CFeature::AddBuildPower(float amount, CUnit* builder)
     {
    +	float oldReclaimLeft = reclaimLeft;
    +	float fractionReclaimed;
     	if(amount>0){
    -		return false;		//cant repair a feature
    +		
    +		// Check they are trying to repair a feature that can be resurrected
    +		if(createdFromUnit == "")
    +			return false;
    +
    +		// 'Repairing' previously-sucked features prior to resurrection
    +		// This is reclaim-option independant - repairing features should always
    +		// be like other repairing - gradual and multi-unit
    +		// Lots of this code is stolen from unit->AddBuildPower
    +		
    +		isRepairingBeforeResurrect = true; // Stop them exploiting chunk reclaiming
    +
    +		if (reclaimLeft >= 1)
    +			return false;		// cant repair a 'fresh' feature
    +
    +		// Work out how much to try to put back, based on the speed this unit would reclaim at.
    +		float part=(100-amount)*0.02/max(10.0f,(def->metal+def->energy));
    +
    +		// Work out how much that will cost
    +		float metalUse=def->metal*part;
    +		float energyUse=def->energy*part;
    +		if (gs->Team(builder->team)->metal >= metalUse && gs->Team(builder->team)->energy >= energyUse)
    +		{
    +			builder->UseMetal(metalUse);
    +			builder->UseEnergy(energyUse);
    +			reclaimLeft+=part;
    +			if(reclaimLeft>=1)
    +				isRepairingBeforeResurrect = false; // They can start reclaiming it again if they so wish
    +				reclaimLeft = 1;
    +			return true;
    +		}
    +		return false;
    +
    +
     	} else {
    -		if(reclaimLeft<0)	//avoid multisuck :)
    +		// Reclaiming
    +		if(reclaimLeft <= 0)	// avoid multisuck when reclaim has already completed during this frame
     			return false;
    -		if(lastReclaim==gs->frameNum)	//make sure several units cant reclaim at once on a single feature
    +		
    +		if(isRepairingBeforeResurrect && modInfo->reclaimMethod > 1) // don't let them exploit chunk reclaim
    +			return false;
    +		
    +		if(modInfo->multiReclaim == 0 && lastReclaim == gs->frameNum) // make sure several units cant reclaim at once on a single feature
     			return true;
    +		
     		float part=(100-amount)*0.02/max(10.0f,(def->metal+def->energy));
     		reclaimLeft-=part;
    -		lastReclaim=gs->frameNum;
    -		if(reclaimLeft<0){
    +		
    +		// Stop the last bit giving too much resource
    +		if(reclaimLeft < 0) reclaimLeft = 0;
    +
    +		fractionReclaimed = oldReclaimLeft-reclaimLeft;
    +
    +		if(modInfo->reclaimMethod == 1 && reclaimLeft == 0) // All-at-end method
    +		{
     			builder->AddMetal(def->metal);
     			builder->AddEnergy(def->energy);
    +		}
    +		else if(modInfo->reclaimMethod == 0) // Gradual reclaim
    +		{
    +			builder->AddMetal(def->metal * fractionReclaimed);
    +			builder->AddEnergy(def->energy * fractionReclaimed);
    +		}
    +		else  // Chunky reclaiming
    +		{
    +			// Work out how many chunk boundaries we crossed
    +			float chunkSize = 1.0 / modInfo->reclaimMethod;
    +			int oldChunk = ChunkNumber(oldReclaimLeft);
    +			int newChunk = ChunkNumber(reclaimLeft);
    +			if (oldChunk != newChunk)
    +			{
    +				float noChunks = (float)oldChunk - (float)newChunk;
    +				builder->AddMetal(noChunks * def->metal * chunkSize);
    +				builder->AddEnergy(noChunks * def->energy * chunkSize);
    +			}
    +		}
    +
    +		// Has the reclaim finished?
    +		if(reclaimLeft<=0)
    +		{
     			featureHandler->DeleteFeature(this);
     			return false;
     		}
    +
    +		lastReclaim=gs->frameNum;
     		return true;
     	}
    +	// Should never get here
    +	assert(false);
     	return false;
     }
     
    @@ -250,5 +328,36 @@
     	glPopMatrix();
     }
     
    +int CFeature::ChunkNumber(float f)
    +{
    +	return (int) ceil(f * modInfo->reclaimMethod);	
    +}
    +
    +float CFeature::RemainingResource(float res)
    +{
    +	// Old style - all reclaimed at the end
    +	if(modInfo->reclaimMethod == 0)
    +		return res * reclaimLeft;
    +
    +	// Gradual reclaim
    +	if(modInfo->reclaimMethod == 1)
    +		return res;
    +
    +	// Otherwise we are doing chunk reclaiming
    +	float chunkSize = res / modInfo->reclaimMethod; // resource/no_chunks
    +	float chunksLeft = ceil(reclaimLeft * modInfo->reclaimMethod);
    +	return chunkSize * chunksLeft;
    +}
    +
    +float CFeature::RemainingMetal()
    +{
    +	return RemainingResource(def->metal);
    +}
    +float CFeature::RemainingEnergy()
    +{
    +	return RemainingResource(def->energy);
    +}
    +
    +
     FeatureDef::~FeatureDef() {
     }
    Index: rts/Sim/Misc/Feature.h
    ===================================================================
    --- rts/Sim/Misc/Feature.h	(revision 1747)
    +++ rts/Sim/Misc/Feature.h	(working copy)
    @@ -27,11 +27,20 @@
     	void Kill(float3& impulse);
     	virtual bool Update(void);
     	void StartFire(void);
    +	float RemainingResource(float res);
    +	float RemainingMetal(void);
    +	float RemainingEnergy(void);
    +	int ChunkNumber(float f);
     	void DrawS3O();
     	void CalculateTransform();
     	CUnit* LastBuilder;
     
     	std::string createdFromUnit;
    +	// This flag is used to stop a potential exploit involving tripping a unit back and forth
    +	// across a chunk boundary to get unlimited resources. Basically, once a corspe has been a little bit
    +	// reclaimed, if they start rezzing then they cannot reclaim again until the corpse has been fully
    +	// 'repaired'.
    +	bool isRepairingBeforeResurrect;
     	float resurrectProgress;
     
     	float health;
    Index: rts/Sim/ModInfo.cpp
    ===================================================================
    --- rts/Sim/ModInfo.cpp	(revision 1747)
    +++ rts/Sim/ModInfo.cpp	(working copy)
    @@ -18,6 +18,23 @@
     		// Load the users preference for team coloured nanospray
     		gu->teamNanospray = configHandler.GetInt ("TeamNanoSpray", 0);
     	}
    +
    +	// Get the reclaim options for the mod
    +	multiReclaim = 0;
    +	reclaimMethod = 1;
    +	// See if the mod overrides the defaults:
    +	try
    +	{
    +		TdfParser reclaimOptions("gamedata/modrules.tdf");
    +		multiReclaim = atoi(reclaimOptions.SGetValueDef("0", "RECLAIM\\MultiReclaim").c_str());
    +		reclaimMethod = atoi(reclaimOptions.SGetValueDef("1", "RECLAIM\\ReclaimMethod").c_str());
    +	}
    +	catch(content_error) // If the modrules.tdf isnt found
    +	{
    +		// We already set the defaults so we should be able to ignore this
    +		// Other optional mod rules MUST set their defaults...
    +	}
    +
     }
     
     CModInfo::~CModInfo(){}
    Index: rts/Sim/ModInfo.h
    ===================================================================
    --- rts/Sim/ModInfo.h	(revision 1747)
    +++ rts/Sim/ModInfo.h	(working copy)
    @@ -10,6 +10,11 @@
     
     	std::string name;
     	bool allowTeamColors;
    +
    +	// Reclaim behaviour
    +	int multiReclaim;	// 0 = 1 reclaimer per feature max, otherwise unlimited
    +	int reclaimMethod;	// 0 = gradual reclaim, 1 = all reclaimed at end, otherwise reclaim in reclaimMethod chunks
    +
     };
     
     extern CModInfo *modInfo;
    Index: rts/Sim/Units/UnitTypes/Builder.cpp
    ===================================================================
    --- rts/Sim/Units/UnitTypes/Builder.cpp	(revision 1747)
    +++ rts/Sim/Units/UnitTypes/Builder.cpp	(working copy)
    @@ -6,6 +6,7 @@
     #include "Builder.h"
     #include "Building.h"
     #include "Sim/Units/UnitLoader.h"
    +#include "Sim/ModInfo.h"
     #include "Sim/Projectiles/GfxProjectile.h"
     #include "Game/GameHelper.h"
     #include "Sim/Units/UnitHandler.h"
    @@ -196,18 +197,28 @@
     	} else if(curResurrect && curResurrect->pos.distance2D(pos)<buildDistance+curResurrect->radius && inBuildStance){
     		UnitDef* ud=unitDefHandler->GetUnitByName(curResurrect->createdFromUnit);
     		if(ud){
    -			if(UseEnergy(ud->energyCost*buildSpeed/ud->buildTime*0.5)){
    -				curResurrect->resurrectProgress+=buildSpeed/ud->buildTime;
    -				CreateNanoParticle(curResurrect->midPos,curResurrect->radius*0.7,gs->randInt()&1);
    +			if( modInfo->reclaimMethod != 1 && curResurrect->reclaimLeft < 1)
    +			{
    +				// This corpse has been reclaimed a little, need to restore the resources
    +				// before we can let the player resurrect it.
    +				curReclaim->AddBuildPower(buildSpeed,this);
     			}
    -			if(curResurrect->resurrectProgress>1){		//resurrect finished
    -				curResurrect->UnBlock();
    -				CUnit* u=unitLoader.LoadUnit(curResurrect->createdFromUnit,curResurrect->pos,team,false,curResurrect->buildFacing);
    -				u->health*=0.05;
    -				lastResurrected=u->id;
    -				curResurrect->resurrectProgress=0;
    -				featureHandler->DeleteFeature(curResurrect);
    -				StopBuild(true);
    +			else
    +			{
    +				// Corpse has been restored, begin resurrection
    +				if(UseEnergy(ud->energyCost*buildSpeed/ud->buildTime*0.5)){
    +					curResurrect->resurrectProgress+=buildSpeed/ud->buildTime;
    +					CreateNanoParticle(curResurrect->midPos,curResurrect->radius*0.7,gs->randInt()&1);
    +				}
    +				if(curResurrect->resurrectProgress>1){		//resurrect finished
    +					curResurrect->UnBlock();
    +					CUnit* u=unitLoader.LoadUnit(curResurrect->createdFromUnit,curResurrect->pos,team,false,curResurrect->buildFacing);
    +					u->health*=0.05;
    +					lastResurrected=u->id;
    +					curResurrect->resurrectProgress=0;
    +					featureHandler->DeleteFeature(curResurrect);
    +					StopBuild(true);
    +				}
     			}
     		} else {
     			StopBuild(true);
    
    patch file icon gradual-reclaim-0.3.patch (10,739 bytes) 2006-08-04 19:06 +

-Relationships
+Relationships

-Notes

~0000312

jcnossen (reporter)

Last edited: 2006-08-04 14:56

I think it's best to keep game related code out of the feature handler, or more important keep the global "game rules" in a single place. I just added a CModInfo class, which you can store the global reclaiming info in. Other than that the code looks fine, just move the game code out of CFeatureHandler.

Also, before everyone starts adding a seperate tdf for every little thing, change reclaim.tdf into gamerules.tdf

~0000313

acidd_uk (reporter)

Added v0.2.

Have just read jcnossen's comment, expect v0.3 in about 30 seconds...

~0000314

acidd_uk (reporter)

Uploaded v0.3. This is a release candidate.

Rezzing units that have been partially reclaimed is now added, along with some code to stop a chunk reclaim + rezzing exploit.

I have not been able to test this new functionality, since at the time of writing the current version of Spring in SVN doesnt draw my mouse pointer :-( However, the reclaiming code was tested in single player vs bots and multiplayer between remote computers, so there shouldnt be any sync issues.

~0000315

jcnossen (reporter)

committed, thanks!

~0000397

acidd_uk (reporter)

The TDF used has changed, it is actually: modrules.tdf,

~0000856

ILMTitan (reporter)

Committed a long time ago.
+Notes

-Issue History
Date Modified Username Field Change
2006-08-02 20:55 acidd_uk New Issue
2006-08-02 20:56 acidd_uk File Added: gradual-reclaim-0.1.patch
2006-08-04 14:53 jcnossen Note Added: 0000312
2006-08-04 14:56 jcnossen Note Edited: 0000312
2006-08-04 19:00 acidd_uk File Added: gradual-reclaim-0.2.patch
2006-08-04 19:01 acidd_uk Note Added: 0000313
2006-08-04 19:05 acidd_uk Note Added: 0000314
2006-08-04 19:06 acidd_uk File Added: gradual-reclaim-0.3.patch
2006-08-05 02:07 jcnossen Status new => closed
2006-08-05 02:07 jcnossen Note Added: 0000315
2006-08-05 02:07 jcnossen Resolution open => fixed
2006-09-28 22:33 acidd_uk Status closed => feedback
2006-09-28 22:33 acidd_uk Resolution fixed => reopened
2006-09-28 22:33 acidd_uk Note Added: 0000397
2007-04-02 08:12 ILMTitan Status feedback => resolved
2007-04-02 08:12 ILMTitan Resolution reopened => fixed
2007-04-02 08:12 ILMTitan Assigned To => ILMTitan
2007-04-02 08:12 ILMTitan Note Added: 0000856
+Issue History