2020-06-04 03:55 CEST

View Issue Details Jump to Notes ]
IDProjectCategoryView StatusLast Update
0000550Spring engineGeneralpublic2007-06-26 21:48
Reporterimbaczek 
Assigned Totvo 
PrioritynormalSeverityfeatureReproducibilityalways
StatusresolvedResolutionfixed 
Product Version 
Target VersionFixed in Version 
Summary0000550: [patch] targetBorder tag - allow weapons to target a point closer to the edge of collision sphere
DescriptionRationale: allow more flexibility to unit design and (perhaps) allow for a simplified melee implementation using beamlasers.
Additional InformationtargetBorder is a float in [-1; 1] range is a scale of transformation of a target point, where 0 means center of collision sphere and 1 means the point on the edge closest to the weaponPos. -1 is a point on the edge furthes away of the weapon, every value in between is accepted and interpreted correctly.

The patch includes some whitespace fixes (mostly removing trailing whitespace) thanks to auto-fixing by my IDE. Sorry about that.
TagsNo tags attached.
Checked infolog.txt for Errors
Attached Files
  • diff file icon targetBorder.diff (10,828 bytes) 2007-06-17 15:05 -
    Index: Lua/LuaWeaponDefs.cpp
    ===================================================================
    --- Lua/LuaWeaponDefs.cpp	(revision 3823)
    +++ Lua/LuaWeaponDefs.cpp	(working copy)
    @@ -75,7 +75,7 @@
     				HSTR_PUSH(L, "__index");
     				lua_pushlightuserdata(L, (void*)wd);
     				lua_pushcclosure(L, WeaponDefIndex, 1);
    -				lua_rawset(L, -3); // closure 
    +				lua_rawset(L, -3); // closure
     
     				HSTR_PUSH(L, "__newindex");
     				lua_pushlightuserdata(L, (void*)wd);
    @@ -110,7 +110,7 @@
     
     static int WeaponDefIndex(lua_State* L)
     {
    -	// not a default value	
    +	// not a default value
     	if (!lua_isstring(L, 2)) {
     		lua_rawget(L, 1);
     		return 1;
    @@ -119,7 +119,7 @@
     	const char* name = lua_tostring(L, 2);
     	ParamMap::const_iterator it = paramMap.find(name);
     
    -	// not a default value	
    +	// not a default value
     	if (paramMap.find(name) == paramMap.end()) {
     		lua_rawget(L, 1);
     		return 1;
    @@ -171,7 +171,7 @@
     
     	const char* name = lua_tostring(L, 2);
     	ParamMap::const_iterator it = paramMap.find(name);
    -	
    +
     	// not a default value, set it
     	if (paramMap.find(name) == paramMap.end()) {
     		lua_rawset(L, 1);
    @@ -186,7 +186,7 @@
     	 	luaL_error(L, "Attempt to write WeaponDefs[%d].%s", wd->id, name);
     	 	return 0;
     	}
    -	
    +
     	// Definition editing
     	const DataElement& elem = it->second;
     	const char* p = ((const char*)wd) + elem.offset;
    @@ -217,7 +217,7 @@
     			luaL_error(L, "ERROR_TYPE in WeaponDefs __newindex");
     		}
     	}
    -	
    +
     	return 0;
     }
     
    @@ -266,7 +266,7 @@
     			}
     			// start the user parameters,
     			// remove the internal key and push a nil
    -			lua_settop(L, 1); 
    +			lua_settop(L, 1);
     			lua_pushnil(L);
     		}
     	}
    @@ -314,8 +314,8 @@
     		LuaPushNamedNumber(L, typeList[i].c_str(), d.damages[i]);
     	}
     	lua_rawset(L, -3);
    -	
    -	return 1;		
    +
    +	return 1;
     }
     
     
    @@ -417,9 +417,9 @@
     
     static bool InitParamMap()
     {
    -	paramMap["next"]  = DataElement(READONLY_TYPE); 
    -	paramMap["pairs"] = DataElement(READONLY_TYPE); 
    -	
    +	paramMap["next"]  = DataElement(READONLY_TYPE);
    +	paramMap["pairs"] = DataElement(READONLY_TYPE);
    +
     	// dummy WeaponDef for offset generation
     	const WeaponDef wd;
     	const char* start = ADDRESS(wd);
    @@ -481,7 +481,7 @@
     	ADD_BOOL("noAutoTarget",   wd.noAutoTarget);
     	ADD_BOOL("manualFire",     wd.manualfire);
     	ADD_INT("targetable",      wd.targetable);
    -	ADD_BOOL("stockpile",      wd.stockpile);					
    +	ADD_BOOL("stockpile",      wd.stockpile);
     	ADD_INT("interceptor",     wd.interceptor);
     	ADD_FLOAT("coverageRange", wd.coverageRange);
     
    @@ -541,6 +541,7 @@
     	ADD_INT("interceptedByShieldType",  wd.interceptedByShieldType);
     
     	ADD_BOOL("avoidFriendly", wd.avoidFriendly);
    +	ADD_FLOAT("targetBorder", wd.targetBorder);
     
     //	CExplosionGenerator *explosionGenerator;
     
    Index: Sim/Units/CommandAI/MobileCAI.cpp
    ===================================================================
    --- Sim/Units/CommandAI/MobileCAI.cpp	(revision 3823)
    +++ Sim/Units/CommandAI/MobileCAI.cpp	(working copy)
    @@ -36,6 +36,7 @@
     	commandPos2(ZeroVector),
     	lastPC(-1),
     	cancelDistance(1024),
    +	lastCloseInTry(-1),
     	slowGuard(false),
     	moveDir(gs->randFloat() > 0.5)
     {
    @@ -597,7 +598,10 @@
     		float3 diff = owner->pos - orderTarget->pos;
     		// if w->AttackUnit() returned true then we are already
     		// in range with our biggest weapon so stop moving
    -		if (b2) {
    +		// also make sure that we're not locked in close-in/in-range state loop
    +		// due to rotates invoked by in-range or out-of-range states
    +		// FIXME kill magic frame number
    +		if (b2 && gs->frameNum > lastCloseInTry + 40) {
     			StopMove();
     			owner->AttackUnit(orderTarget, c.id == CMD_DGUN);
     			owner->moveType->KeepPointingTo(orderTarget,
    @@ -631,6 +635,7 @@
     				> (10 + orderTarget->pos.distance2D(owner->pos) * 0.2f)) {
     			float3 fix = orderTarget->pos + owner->posErrorVector * 128;
     			SetGoal(fix, owner->pos);
    +			lastCloseInTry = gs->frameNum;
     		}
     	}
     
    Index: Sim/Units/CommandAI/MobileCAI.h
    ===================================================================
    --- Sim/Units/CommandAI/MobileCAI.h	(revision 3823)
    +++ Sim/Units/CommandAI/MobileCAI.h	(working copy)
    @@ -57,6 +57,7 @@
     
     protected:
     	int cancelDistance;
    +	int lastCloseInTry;
     	bool slowGuard;
     	bool moveDir;
     	void PushOrUpdateReturnFight() {
    Index: Sim/Units/UnitDef.h
    ===================================================================
    --- Sim/Units/UnitDef.h	(revision 3823)
    +++ Sim/Units/UnitDef.h	(working copy)
    @@ -89,7 +89,7 @@
     	int imageSizeX;
     	int imageSizeY;
     	std::string buildpicname;
    -	
    +
     	UnitDef* decoyDef;
     
     	int aihint;
    @@ -256,7 +256,7 @@
     	MoveData* movedata;
     //	unsigned char* yardmapLevels[6];
     	unsigned char* yardmaps[4];			//Iterations of the Ymap for building rotation
    -	
    +
     	int xsize;									//each size is 8 units
     	int ysize;									//each size is 8 units
     
    Index: Sim/Units/UnitLoader.cpp
    ===================================================================
    --- Sim/Units/UnitLoader.cpp	(revision 3823)
    +++ Sim/Units/UnitLoader.cpp	(working copy)
    @@ -464,6 +464,7 @@
     	weapon->fuelUsage = udw->fuelUsage;
     	weapon->avoidFriendly = weapondef->avoidFriendly;
     	weapon->avoidFeature = weapondef->avoidFeature;
    +	weapon->targetBorder = weapondef->targetBorder;
     	weapon->collisionFlags = weapondef->collisionFlags;
     	weapon->Init();
     
    Index: Sim/Weapons/BeamLaser.cpp
    ===================================================================
    --- Sim/Weapons/BeamLaser.cpp	(revision 3823)
    +++ Sim/Weapons/BeamLaser.cpp	(working copy)
    @@ -85,6 +85,7 @@
     	}
     
     	float3 dir=pos-weaponPos;
    +
     	float length=dir.Length();
     	if(length==0)
     		return true;
    @@ -133,6 +134,7 @@
     		}
     	}
     	dir+=(salvoError)*(1-owner->limExperience*0.7f);
    +
     	dir.Normalize();
     
     	FireInternal(dir, false);
    @@ -152,6 +154,11 @@
     
     	bool tryAgain=true;
     	CUnit* hit;
    +
    +	if (targetType == Target_Unit && targetUnit && targetBorder != 0) {
    +		maxLength += targetUnit->radius*targetBorder;
    +	}
    +
     	for(int tries=0;tries<5 && tryAgain;++tries){
     		tryAgain=false;
     		hit=0;
    @@ -172,6 +179,7 @@
     				tryAgain=true;
     			}
     		}
    +
     		hitPos=curPos+dir*length;
     
     		float baseAlpha=weaponDef->intensity*255;
    Index: Sim/Weapons/Weapon.cpp
    ===================================================================
    --- Sim/Weapons/Weapon.cpp	(revision 3823)
    +++ Sim/Weapons/Weapon.cpp	(working copy)
    @@ -83,6 +83,7 @@
     	CR_MEMBER(hasCloseTarget),
     	CR_MEMBER(avoidFriendly),
     	CR_MEMBER(avoidFeature),
    +	CR_MEMBER(targetBorder),
     	CR_MEMBER(collisionFlags),
     	CR_MEMBER(fuelUsage)));
     
    @@ -149,6 +150,7 @@
     	hasCloseTarget(false),
     	avoidFriendly(true),
     	avoidFeature(true),
    +	targetBorder(false),
     	collisionFlags(0),
     	fuelUsage(0)
     {
    @@ -541,8 +548,14 @@
     		return false;
     
     	float3 dif=pos-weaponPos;
    +	float r=GetRange2D(owner->pos.y-pos.y);
     
    -	float r=GetRange2D(owner->pos.y-pos.y);
    +	if (targetBorder != 0 && unit) {
    +		float3 diff(dif);
    +		diff.Normalize();
    +		dif -= diff*(unit->radius*targetBorder); // a little bit inside the sphere
    +	}
    +
     	if(dif.SqLength2D()>=r*r)
     		return false;
     
    Index: Sim/Weapons/Weapon.h
    ===================================================================
    --- Sim/Weapons/Weapon.h	(revision 3823)
    +++ Sim/Weapons/Weapon.h	(working copy)
    @@ -118,12 +118,13 @@
     	int lastErrorVectorUpdate;
     
     	CWeapon* slavedTo;						//use this weapon to choose target
    -	
    +
     	float3 mainDir;								//main aim dir of weapon
     	float maxMainDirAngleDif;					//how far away from main aim dir the weapon can aim at something (as an acos value)
     
     	bool avoidFriendly;		//if true tried to avoid friendly Units when aiming.
     	bool avoidFeature;      		//if true try to avoid Features while aiming.
    +	float targetBorder;  // if nonzero, targetting units will TryTarget at the edge of collision sphere (radius*tag value, [-1;1]) instead of its centre
     	unsigned int collisionFlags;
     
     	float fuelUsage;
    Index: Sim/Weapons/WeaponDefHandler.cpp
    ===================================================================
    --- Sim/Weapons/WeaponDefHandler.cpp	(revision 3823)
    +++ Sim/Weapons/WeaponDefHandler.cpp	(working copy)
    @@ -91,6 +91,16 @@
     	if(!collideFeature)
     		weaponDefs[id].collisionFlags+=COLLISION_NOFEATURE;
     
    +	sunparser->GetDef(weaponDefs[id].targetBorder, "0", weaponname + "\\TargetBorder");
    +	if (weaponDefs[id].targetBorder > 1.f) {
    +		logOutput.Print("warning: targetBorder truncated to 1 (was %f)", weaponDefs[id].targetBorder);
    +		weaponDefs[id].targetBorder = 1.f;
    +	} else if (weaponDefs[id].targetBorder < -1.f) {
    +		logOutput.Print("warning: targetBorder truncated to -1 (was %f)", weaponDefs[id].targetBorder);
    +		weaponDefs[id].targetBorder = -1.f;
    +	}
    +
    +
     	sunparser->GetDef(weaponDefs[id].dropped, "0", weaponname + "\\dropped");
     	sunparser->GetDef(lineofsight, "0", weaponname + "\\lineofsight");
     	sunparser->GetDef(ballistic, "0", weaponname + "\\ballistic");
    @@ -119,7 +129,7 @@
     	sunparser->GetDef(weaponDefs[id].laserflaresize, "15", weaponname + "\\laserflaresize");
     	sunparser->GetDef(weaponDefs[id].intensity, "0.9", weaponname + "\\intensity");
     	sunparser->GetDef(weaponDefs[id].duration, "0.05", weaponname + "\\duration");
    -	
    +
     	sunparser->GetDef(weaponDefs[id].visuals.sizeDecay,  "0", weaponname + "\\sizeDecay");
     	sunparser->GetDef(weaponDefs[id].visuals.alphaDecay, "1", weaponname + "\\alphaDecay");
     	sunparser->GetDef(weaponDefs[id].visuals.separation, "1", weaponname + "\\separation");
    @@ -576,7 +586,7 @@
     
     	if (inverted == true) {
     		for(int i=0; i < damageArrayHandler->numTypes; ++i) {
    -			
    +
     			dynDamages[i] = damages[i] - (1 - pow(1 / range * travDist, exp)) * damages[i];
     
     			if (damageMin > 0) {
    @@ -591,7 +601,7 @@
     	}
     	else {
     		for(int i=0; i < damageArrayHandler->numTypes; ++i) {
    -			
    +
     			dynDamages[i] = (1 - pow(1 / range * travDist, exp)) * damages[i];
     
     			if (damageMin > 0) {
    Index: Sim/Weapons/WeaponDefHandler.h
    ===================================================================
    --- Sim/Weapons/WeaponDefHandler.h	(revision 3823)
    +++ Sim/Weapons/WeaponDefHandler.h	(working copy)
    @@ -86,7 +86,7 @@
     	bool manualfire;			//use dgun button
     	int interceptor;				//anti nuke
     	int targetable;				//nuke (can be shot by interceptor)
    -	bool stockpile;					
    +	bool stockpile;
     	float coverageRange;		//range of anti nuke
     
     	float intensity;
    @@ -138,7 +138,7 @@
     		float tilelength;
     		float scrollspeed;
     		float pulseSpeed;
    -		
    +
     		int stages;
     		float alphaDecay;
     		float sizeDecay;
    @@ -172,6 +172,7 @@
     
     	bool avoidFriendly;		//if true try to avoid friendly Units when aiming.
     	bool avoidFeature;      //if true try to avoid Features while aiming.
    +	float targetBorder;     //if nonzero, targetting units will TryTarget at the edge of collision sphere (radius*tag value, [-1;1]) instead of its centre
     	unsigned int collisionFlags;
     
     	CExplosionGenerator *explosionGenerator; // can be zero for default explosions
    
    diff file icon targetBorder.diff (10,828 bytes) 2007-06-17 15:05 +
  • diff file icon targetborder_minintensity_cylinderTargetting_and_weaponMuzzlePos.diff (32,157 bytes) 2007-06-22 19:01 -
    Index: rts/Game/GameHelper.cpp
    ===================================================================
    --- rts/Game/GameHelper.cpp	(revision 3829)
    +++ rts/Game/GameHelper.cpp	(working copy)
    @@ -209,16 +209,17 @@
     				closeLength=length;
     			float3 closeVect=dif-dir*closeLength;
     
    -			float rad=(*ui)->radius;
    -			float tmp = rad * rad - closeVect.SqLength();
    -			if(tmp > 0 && length>closeLength+sqrt(tmp)){
    +			/*float rad=(*ui)->radius;
    +			float tmp = rad * rad - closeVect.SqLength();*/
    +
    +			/*if(tmp > 0 && length>closeLength+sqrt(tmp)){
     				length=closeLength-sqrt(tmp)*0.5f;
     				hit=*ui;
    -			}
    -/*			if(closeVect.SqLength() < (*ui)->sqRadius){
    +			}*/
    +			if(closeVect.SqLength() < (*ui)->sqRadius){
     				length=closeLength;
     				hit=*ui;
    -			}*/
    +			}
     		}
     	}
     	return length;
    Index: rts/Lua/LuaWeaponDefs.cpp
    ===================================================================
    --- rts/Lua/LuaWeaponDefs.cpp	(revision 3829)
    +++ rts/Lua/LuaWeaponDefs.cpp	(working copy)
    @@ -75,7 +75,7 @@
     				HSTR_PUSH(L, "__index");
     				lua_pushlightuserdata(L, (void*)wd);
     				lua_pushcclosure(L, WeaponDefIndex, 1);
    -				lua_rawset(L, -3); // closure 
    +				lua_rawset(L, -3); // closure
     
     				HSTR_PUSH(L, "__newindex");
     				lua_pushlightuserdata(L, (void*)wd);
    @@ -110,7 +110,7 @@
     
     static int WeaponDefIndex(lua_State* L)
     {
    -	// not a default value	
    +	// not a default value
     	if (!lua_isstring(L, 2)) {
     		lua_rawget(L, 1);
     		return 1;
    @@ -119,7 +119,7 @@
     	const char* name = lua_tostring(L, 2);
     	ParamMap::const_iterator it = paramMap.find(name);
     
    -	// not a default value	
    +	// not a default value
     	if (paramMap.find(name) == paramMap.end()) {
     		lua_rawget(L, 1);
     		return 1;
    @@ -171,7 +171,7 @@
     
     	const char* name = lua_tostring(L, 2);
     	ParamMap::const_iterator it = paramMap.find(name);
    -	
    +
     	// not a default value, set it
     	if (paramMap.find(name) == paramMap.end()) {
     		lua_rawset(L, 1);
    @@ -186,7 +186,7 @@
     	 	luaL_error(L, "Attempt to write WeaponDefs[%d].%s", wd->id, name);
     	 	return 0;
     	}
    -	
    +
     	// Definition editing
     	const DataElement& elem = it->second;
     	const char* p = ((const char*)wd) + elem.offset;
    @@ -217,7 +217,7 @@
     			luaL_error(L, "ERROR_TYPE in WeaponDefs __newindex");
     		}
     	}
    -	
    +
     	return 0;
     }
     
    @@ -266,7 +266,7 @@
     			}
     			// start the user parameters,
     			// remove the internal key and push a nil
    -			lua_settop(L, 1); 
    +			lua_settop(L, 1);
     			lua_pushnil(L);
     		}
     	}
    @@ -314,8 +314,8 @@
     		LuaPushNamedNumber(L, typeList[i].c_str(), d.damages[i]);
     	}
     	lua_rawset(L, -3);
    -	
    -	return 1;		
    +
    +	return 1;
     }
     
     
    @@ -417,9 +417,9 @@
     
     static bool InitParamMap()
     {
    -	paramMap["next"]  = DataElement(READONLY_TYPE); 
    -	paramMap["pairs"] = DataElement(READONLY_TYPE); 
    -	
    +	paramMap["next"]  = DataElement(READONLY_TYPE);
    +	paramMap["pairs"] = DataElement(READONLY_TYPE);
    +
     	// dummy WeaponDef for offset generation
     	const WeaponDef wd;
     	const char* start = ADDRESS(wd);
    @@ -481,7 +481,7 @@
     	ADD_BOOL("noAutoTarget",   wd.noAutoTarget);
     	ADD_BOOL("manualFire",     wd.manualfire);
     	ADD_INT("targetable",      wd.targetable);
    -	ADD_BOOL("stockpile",      wd.stockpile);					
    +	ADD_BOOL("stockpile",      wd.stockpile);
     	ADD_INT("interceptor",     wd.interceptor);
     	ADD_FLOAT("coverageRange", wd.coverageRange);
     
    @@ -541,6 +541,9 @@
     	ADD_INT("interceptedByShieldType",  wd.interceptedByShieldType);
     
     	ADD_BOOL("avoidFriendly", wd.avoidFriendly);
    +	ADD_FLOAT("targetBorder", wd.targetBorder);
    +	ADD_FLOAT("cylinderTargetting", wd.cylinderTargetting);
    +	ADD_FLOAT("minIntensity", wd.minIntensity);
     
     //	CExplosionGenerator *explosionGenerator;
     
    Index: rts/Sim/Projectiles/ProjectileHandler.cpp
    ===================================================================
    --- rts/Sim/Projectiles/ProjectileHandler.cpp	(revision 3829)
    +++ rts/Sim/Projectiles/ProjectileHandler.cpp	(working copy)
    @@ -31,8 +31,8 @@
     
     CProjectileHandler* ph;
     using namespace std;
    -extern GLfloat FogBlack[]; 
    -extern GLfloat FogLand[]; 
    +extern GLfloat FogBlack[];
    +extern GLfloat FogLand[];
     
     CR_BIND(CProjectileHandler,);
     
    @@ -137,7 +137,7 @@
     	wrecktex            = textureAtlas->GetTextureWithBackup(  "wrecktexture",           "circularthingy"  );
     	plasmatex           = textureAtlas->GetTextureWithBackup(  "plasmatexture",          "circularthingy"  );
     
    -	
    +
     	groundFXAtlas = SAFE_NEW CTextureAtlas(2048, 2048);
     	//add all textures in groundfx section
     	ptex = resources.GetAllValues("resources\\graphics\\groundfx");
    @@ -328,9 +328,9 @@
     
     	int numFlyingPieces = 0;
     	int drawnPieces = 0;
    -	
    +
     	/* Putting in, say, viewport culling will deserve refactoring. */
    -	
    +
     	/* 3DO */
     	unitDrawer->SetupForUnitDrawing();
     
    @@ -376,27 +376,27 @@
     
     	for (int textureType = 1; textureType < flyings3oPieces.size(); textureType++){
     		/* TODO Skip this if there's no FlyingPieces. */
    -		
    +
     		texturehandler->SetS3oTexture(textureType);
    -		
    +
     		for (int team = 0; team < flyings3oPieces[textureType].size(); team++){
     			FlyingPiece_List * fpl = flyings3oPieces[textureType][team];
    -		
    +
     			unitDrawer->SetS3OTeamColour(team);
    -			
    +
     			va->Initialize();
    -			
    +
     			numFlyingPieces += fpl->size();
    -		
    +
     			for(std::list<FlyingPiece*>::iterator pi=fpl->begin();pi!=fpl->end();++pi){
     				CMatrix44f m;
     				m.Rotate((*pi)->rot,(*pi)->rotAxis);
     				float3 interPos=(*pi)->pos+(*pi)->speed*gu->timeOffset;
    -				
    +
     				SS3OVertex * verts = (*pi)->verts;
    -				
    +
     				float3 tp, tn;
    -				
    +
     				for (int i = 0; i < 4; i++){
     					tp=m.Mul(verts[i].pos);
     					tn=m.Mul(verts[i].normal);
    @@ -408,9 +408,9 @@
     			va->DrawArrayTN(GL_QUADS);
     		}
     	}
    -	
    +
     	unitDrawer->CleanUpS3ODrawing();
    -	
    +
     	/*
     	 * TODO Nearly cut here.
     	 */
    @@ -565,7 +565,7 @@
     						if(readmap->groundBlockingObjectMap[square]!=unit)
     							continue;
     					}
    -					//adjust projectile position so explosion happens at the correct position 
    +					//adjust projectile position so explosion happens at the correct position
     					p->pos = p->pos + p->speed*closeTime;
     					p->Collision(*ui);
     					break;
    @@ -603,7 +603,7 @@
     				}
     			}
     		}
    -	}	
    +	}
     }
     
     void CProjectileHandler::AddGroundFlash(CGroundFlash* flash)
    @@ -687,7 +687,7 @@
     		flyings3oPieces[textureType].push_back(fpl);
     		flyingPieces.push_back(fpl);
     	}
    -	
    +
     	pieceList=flyings3oPieces[textureType][team];
     
     	FlyingPiece* fp=new FlyingPiece;
    @@ -745,7 +745,7 @@
     	glDisable(GL_ALPHA_TEST);
     	glDisable(GL_FOG);
     
    -	unsigned char col[4];	
    +	unsigned char col[4];
     	float time=gu->lastFrameTime*gs->speedFactor*3;
     	float speed=1;
     	float size=1;
    Index: rts/Sim/Projectiles/WeaponProjectile.cpp
    ===================================================================
    --- rts/Sim/Projectiles/WeaponProjectile.cpp	(revision 3829)
    +++ rts/Sim/Projectiles/WeaponProjectile.cpp	(working copy)
    @@ -45,7 +45,7 @@
     	interceptTarget=0;
     }
     
    -CWeaponProjectile::CWeaponProjectile(const float3& pos,const float3& speed,CUnit* owner, CUnit* target,const float3 &targetPos, WeaponDef *weaponDef,CWeaponProjectile* interceptTarget, bool synced) : 
    +CWeaponProjectile::CWeaponProjectile(const float3& pos,const float3& speed,CUnit* owner, CUnit* target,const float3 &targetPos, WeaponDef *weaponDef,CWeaponProjectile* interceptTarget, bool synced) :
     	CProjectile(pos,speed,owner, synced),
     	weaponDef(weaponDef),
     	weaponDefName(weaponDef?weaponDef->name:std::string("")),
    @@ -120,7 +120,7 @@
     		DamageArray dynDamages;
     		if (weaponDef->dynDamageExp > 0)
     			dynDamages = weaponDefHandler->DynamicDamages(weaponDef->damages, startpos, pos, weaponDef->dynDamageRange>0?weaponDef->dynDamageRange:weaponDef->range, weaponDef->dynDamageExp, weaponDef->dynDamageMin, weaponDef->dynDamageInverted);
    -		
    +
     		helper->Explosion(pos,weaponDef->dynDamageExp>0?dynDamages:weaponDef->damages,weaponDef->areaOfEffect,weaponDef->edgeEffectiveness,weaponDef->explosionSpeed,owner,true,weaponDef->noExplode? 0.3f:1,weaponDef->noExplode || weaponDef->noSelfDamage, weaponDef->explosionGenerator,0,impactDir, weaponDef->id);
     	}
     
    @@ -155,7 +155,7 @@
     		DamageArray dynDamages;
     		if (weaponDef->dynDamageExp > 0)
     			dynDamages = weaponDefHandler->DynamicDamages(weaponDef->damages, startpos, pos, weaponDef->dynDamageRange>0?weaponDef->dynDamageRange:weaponDef->range, weaponDef->dynDamageExp, weaponDef->dynDamageMin, weaponDef->dynDamageInverted);
    -			
    +
     		helper->Explosion(pos,weaponDef->dynDamageExp>0?dynDamages:weaponDef->damages,weaponDef->areaOfEffect,weaponDef->edgeEffectiveness,weaponDef->explosionSpeed,owner,true,weaponDef->noExplode? 0.3f:1,weaponDef->noExplode,weaponDef->explosionGenerator,unit,impactDir, weaponDef->id);
     	}
     
    @@ -226,7 +226,7 @@
     	transMatrix[12]=interPos.x;
     	transMatrix[13]=interPos.y;
     	transMatrix[14]=interPos.z;
    -	glMultMatrixf(&transMatrix[0]);		
    +	glMultMatrixf(&transMatrix[0]);
     
     	glCallList(modelDispList);
     	glPopMatrix();
    Index: rts/Sim/Units/CommandAI/MobileCAI.cpp
    ===================================================================
    --- rts/Sim/Units/CommandAI/MobileCAI.cpp	(revision 3829)
    +++ rts/Sim/Units/CommandAI/MobileCAI.cpp	(working copy)
    @@ -42,6 +42,8 @@
     				CR_MEMBER(commandPos1),
     				CR_MEMBER(commandPos2),
     
    +				CR_MEMBER(lastCloseInTry),
    +				
     				CR_MEMBER(cancelDistance),
     				CR_MEMBER(slowGuard),
     				CR_MEMBER(moveDir)
    @@ -61,6 +63,7 @@
     	commandPos2(ZeroVector),
     	lastPC(-1),
     	cancelDistance(1024),
    +	lastCloseInTry(-1),
     	slowGuard(false),
     	moveDir(gs->randFloat() > 0.5),
     	lastUserGoal(0,0,0)
    @@ -81,6 +84,7 @@
     	commandPos2(ZeroVector),
     	lastPC(-1),
     	cancelDistance(1024),
    +	lastCloseInTry(-1),
     	slowGuard(false),
     	moveDir(gs->randFloat() > 0.5)
     {
    @@ -582,7 +586,8 @@
     			// check if we have valid target parameter and that we aren't attacking ourselves
     			if (uh->units[unitID] != 0 && uh->units[unitID] != owner) {
     				float3 fix = uh->units[unitID]->pos + owner->posErrorVector * 128;
    -				SetGoal(fix, owner->pos);
    +				float3 diff = float3(fix - owner->pos).Normalize();
    +				SetGoal(fix - diff*uh->units[unitID]->radius, owner->pos);
     				// get ID of attack-order target unit
     				orderTarget = uh->units[unitID];
     				AddDeathDependence(orderTarget);
    @@ -626,6 +631,7 @@
     		//bool b1 = owner->AttackUnit(orderTarget, c.id == CMD_DGUN);
     		bool b2 = false;
     		bool b3 = false;
    +		float edgeFactor = 0.f; // percent offset to target center
     
     		if (owner->weapons.size() > 0) {
     			if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) {
    @@ -638,15 +644,21 @@
     			// can hit target with our first (meanest) one
     			b2 = w->TryTargetRotate(orderTarget, c.id == CMD_DGUN);
     			b3 = (w->range - (w->relWeaponPos).Length()) > (orderTarget->pos.distance(owner->pos));
    +			edgeFactor = fabs(w->targetBorder);
     		}
     		float3 diff = owner->pos - orderTarget->pos;
     		// if w->AttackUnit() returned true then we are already
     		// in range with our biggest weapon so stop moving
    +		// also make sure that we're not locked in close-in/in-range state loop
    +		// due to rotates invoked by in-range or out-of-range states
     		if (b2) {
     			StopMove();
     			owner->AttackUnit(orderTarget, c.id == CMD_DGUN);
    -			owner->moveType->KeepPointingTo(orderTarget,
    -				min((float) (owner->losRadius * SQUARE_SIZE * 2), owner->maxRange * 0.9f), true);
    +			// FIXME kill magic frame number
    +			if (gs->frameNum > lastCloseInTry + MAX_CLOSE_IN_RETRY_TICKS) {
    +				owner->moveType->KeepPointingTo(orderTarget,
    +					min((float) (owner->losRadius * SQUARE_SIZE * 2), owner->maxRange * 0.9f), true);
    +			}
     		}
     
     		// if (((first weapon range minus first weapon length greater than distance to target
    @@ -675,7 +687,10 @@
     		else if ((orderTarget->pos + owner->posErrorVector * 128).distance2D(goalPos)
     				> (10 + orderTarget->pos.distance2D(owner->pos) * 0.2f)) {
     			float3 fix = orderTarget->pos + owner->posErrorVector * 128;
    -			SetGoal(fix, owner->pos);
    +			float3 norm = float3(fix - owner->pos).Normalize();
    +			SetGoal(fix - norm*(orderTarget->radius*edgeFactor*0.8f), owner->pos);
    +			if (lastCloseInTry < gs->frameNum + MAX_CLOSE_IN_RETRY_TICKS)
    +				lastCloseInTry = gs->frameNum;
     		}
     	}
     
    Index: rts/Sim/Units/CommandAI/MobileCAI.h
    ===================================================================
    --- rts/Sim/Units/CommandAI/MobileCAI.h	(revision 3829)
    +++ rts/Sim/Units/CommandAI/MobileCAI.h	(working copy)
    @@ -59,6 +59,7 @@
     
     protected:
     	int cancelDistance;
    +	int lastCloseInTry;
     	bool slowGuard;
     	bool moveDir;
     	void PushOrUpdateReturnFight() {
    @@ -66,5 +67,6 @@
     	}
     };
     
    +#define MAX_CLOSE_IN_RETRY_TICKS 30
     
     #endif /* MOBILECAI_H */
    Index: rts/Sim/Units/UnitLoader.cpp
    ===================================================================
    --- rts/Sim/Units/UnitLoader.cpp	(revision 3829)
    +++ rts/Sim/Units/UnitLoader.cpp	(working copy)
    @@ -466,6 +466,9 @@
     	weapon->fuelUsage = udw->fuelUsage;
     	weapon->avoidFriendly = weapondef->avoidFriendly;
     	weapon->avoidFeature = weapondef->avoidFeature;
    +	weapon->targetBorder = weapondef->targetBorder;
    +	weapon->cylinderTargetting = weapondef->cylinderTargetting;
    +	weapon->minIntensity = weapondef->minIntensity;
     	weapon->collisionFlags = weapondef->collisionFlags;
     	weapon->Init();
     
    Index: rts/Sim/Weapons/BeamLaser.cpp
    ===================================================================
    --- rts/Sim/Weapons/BeamLaser.cpp	(revision 3829)
    +++ rts/Sim/Weapons/BeamLaser.cpp	(working copy)
    @@ -42,6 +42,7 @@
     {
     	if(targetType!=Target_None){
     		weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x;
    +		weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x;
     		if(!onlyForward){
     			wantedDir=targetPos-weaponPos;
     			wantedDir.Normalize();
    @@ -84,7 +85,8 @@
     			return false;
     	}
     
    -	float3 dir=pos-weaponPos;
    +	float3 dir=pos-weaponMuzzlePos;
    +
     	float length=dir.Length();
     	if(length==0)
     		return true;
    @@ -92,14 +94,14 @@
     	dir/=length;
     
     	if(!onlyForward){		//skip ground col testing for aircrafts
    -		float g=ground->LineGroundCol(weaponPos,pos);
    +		float g=ground->LineGroundCol(weaponMuzzlePos,pos);
     		if(g>0 && g<length*0.9f)
     			return false;
     	}
    -	if(avoidFeature && helper->LineFeatureCol(weaponPos,dir,length))
    +	if(avoidFeature && helper->LineFeatureCol(weaponMuzzlePos,dir,length))
     		return false;
     
    -	if(avoidFriendly && helper->TestCone(weaponPos,dir,length,(accuracy+sprayangle)*(1-owner->limExperience*0.7f),owner->allyteam,owner))
    +	if(avoidFriendly && helper->TestCone(weaponMuzzlePos,dir,length,(accuracy+sprayangle)*(1-owner->limExperience*0.7f),owner->allyteam,owner))
     		return false;
     	return true;
     }
    @@ -125,7 +127,7 @@
     		if(salvoLeft==salvoSize-1){
     			if(fireSoundId)
     				sound->PlaySample(fireSoundId,owner,fireSoundVolume);
    -			dir=targetPos-weaponPos;
    +			dir=targetPos-weaponMuzzlePos;
     			dir.Normalize();
     			oldDir=dir;
     		} else {
    @@ -133,6 +135,7 @@
     		}
     	}
     	dir+=(salvoError)*(1-owner->limExperience*0.7f);
    +
     	dir.Normalize();
     
     	FireInternal(dir, false);
    @@ -145,13 +148,27 @@
     	if(owner->directControl)
     		rangeMod=0.95f;
     #endif
    +
     	float maxLength=range*rangeMod;
     	float curLength=0;
    -	float3 curPos=weaponPos;
    +	float3 curPos=weaponMuzzlePos;
     	float3 hitPos;
     
     	bool tryAgain=true;
     	CUnit* hit;
    +
    +	// increase range if targets are searched for in a cylinder
    +	if (cylinderTargetting > 0.01) {
    +		const float3 up(0, owner->radius*cylinderTargetting, 0);
    +		const float uplen = up.dot(dir);
    +		maxLength = sqrt(maxLength*maxLength + uplen*uplen);
    +	}
    +
    +	// increase range if targetting edge of hitsphere
    +	if (targetType == Target_Unit && targetUnit && targetBorder != 0) {
    +		maxLength += targetUnit->radius*targetBorder;
    +	}
    +
     	for(int tries=0;tries<5 && tryAgain;++tries){
     		tryAgain=false;
     		hit=0;
    @@ -172,6 +189,7 @@
     				tryAgain=true;
     			}
     		}
    +
     		hitPos=curPos+dir*length;
     
     		float baseAlpha=weaponDef->intensity*255;
    @@ -187,12 +205,20 @@
     		curLength+=length;
     		dir=newDir;
     	}
    -	float	intensity=1-(curLength)/(range*2);
    +
    +	// fix negative damage when hitting big spheres
    +	float actualRange = range;
    +	if (hit && targetBorder > 0) {
    +		actualRange += hit->radius*targetBorder;
    +	}
    +	// make it possible to always hit with some minimal intensity (melee weapons have use for that)
    +	float intensity=max(minIntensity, 1-(curLength)/(actualRange*2));
    +
     	if(curLength<maxLength) {
     		// Dynamic Damage
     		DamageArray dynDamages;
     		if (weaponDef->dynDamageExp > 0)
    -			dynDamages = weaponDefHandler->DynamicDamages(weaponDef->damages, weaponPos, curPos, weaponDef->dynDamageRange>0?weaponDef->dynDamageRange:weaponDef->range, weaponDef->dynDamageExp, weaponDef->dynDamageMin, weaponDef->dynDamageInverted);
    +			dynDamages = weaponDefHandler->DynamicDamages(weaponDef->damages, weaponMuzzlePos, curPos, weaponDef->dynDamageRange>0?weaponDef->dynDamageRange:weaponDef->range, weaponDef->dynDamageExp, weaponDef->dynDamageMin, weaponDef->dynDamageInverted);
     		helper->Explosion(hitPos, weaponDef->dynDamageExp>0?dynDamages*(intensity*damageMul):weaponDef->damages*(intensity*damageMul), areaOfEffect, weaponDef->edgeEffectiveness, weaponDef->explosionSpeed,owner, true, 1.0f, false, weaponDef->explosionGenerator, hit, dir, weaponDef->id);
     	}
     
    Index: rts/Sim/Weapons/Weapon.cpp
    ===================================================================
    --- rts/Sim/Weapons/Weapon.cpp	(revision 3829)
    +++ rts/Sim/Weapons/Weapon.cpp	(working copy)
    @@ -56,9 +56,11 @@
     	CR_MEMBER(subClassReady),
     	CR_MEMBER(onlyForward),
     	CR_MEMBER(weaponPos),
    +	CR_MEMBER(weaponMuzzlePos),
     	CR_MEMBER(lastRequest),
     	CR_MEMBER(damages),
     	CR_MEMBER(relWeaponPos),
    +	CR_MEMBER(relWeaponMuzzlePos),
     	CR_MEMBER(muzzleFlareSize),
     	CR_MEMBER(lastTargetRetry),
     	CR_MEMBER(areaOfEffect),
    @@ -83,6 +85,9 @@
     	CR_MEMBER(hasCloseTarget),
     	CR_MEMBER(avoidFriendly),
     	CR_MEMBER(avoidFeature),
    +	CR_MEMBER(targetBorder),
    +	CR_MEMBER(cylinderTargetting),
    +	CR_MEMBER(minIntensity),
     	CR_MEMBER(collisionFlags),
     	CR_MEMBER(fuelUsage),
     	CR_MEMBER(weaponNum)
    @@ -127,8 +132,10 @@
     	subClassReady(true),
     	onlyForward(false),
     	weaponPos(0,0,0),
    +	weaponMuzzlePos(0,0,0),
     	lastRequest(0),
     	relWeaponPos(0,1,0),
    +	relWeaponMuzzlePos(0,1,0),
     	muzzleFlareSize(1),
     	lastTargetRetry(-100),
     	areaOfEffect(1),
    @@ -151,6 +158,9 @@
     	hasCloseTarget(false),
     	avoidFriendly(true),
     	avoidFeature(true),
    +	targetBorder(0.f),
    +	cylinderTargetting(0.f),
    +	minIntensity(0.f),
     	collisionFlags(0),
     	fuelUsage(0)
     {
    @@ -164,15 +174,20 @@
     
     void CWeapon::Update()
     {
    +	// do not fire at cloaked units
    +	if(targetType == Target_Unit && targetUnit && targetUnit->isCloaked) {
    +		HoldFire();
    +		return;
    +	}
    +
     	if(hasCloseTarget){
     		std::vector<int> args;
     		args.push_back(0);
    -		if(useWeaponPosForAim){
    -			owner->cob->Call(COBFN_QueryPrimary+weaponNum,args);
    -		} else {
    -			owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args);
    -		}
    +		owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args);
     		relWeaponPos=owner->localmodel->GetPiecePos(args[0]);
    +
    +		owner->cob->Call(COBFN_QueryPrimary+weaponNum,args);
    +		relWeaponMuzzlePos=owner->localmodel->GetPiecePos(args[0]);
     	}
     
     	if(targetType==Target_Unit){
    @@ -249,15 +264,15 @@
     	&& subClassReady
     	&& reloadStatus<=gs->frameNum
     	&& (!weaponDef->stockpile || numStockpiled)
    -	&& (weaponDef->waterweapon || weaponPos.y>0)
    +	&& (weaponDef->waterweapon || weaponMuzzlePos.y>0)
     	&& (owner->unitDef->maxFuel==0 || owner->currentFuel > 0)
     	){
     		if ((weaponDef->stockpile || (gs->Team(owner->team)->metal>=metalFireCost && gs->Team(owner->team)->energy>=energyFireCost))) {
     			std::vector<int> args;
     			args.push_back(0);
     			owner->cob->Call(COBFN_QueryPrimary+weaponNum,args);
    -			relWeaponPos=owner->localmodel->GetPiecePos(args[0]);
    -			weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x;
    +			relWeaponMuzzlePos=owner->localmodel->GetPiecePos(args[0]);
    +			weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x;
     			useWeaponPosForAim=reloadTime/16+8;
     
     			if(TryTarget(targetPos,haveUserTarget,targetUnit)){
    @@ -310,10 +325,16 @@
     
     		std::vector<int> args;
     		args.push_back(0);
    -		owner->cob->Call(/*COBFN_AimFromPrimary+weaponNum/*/COBFN_QueryPrimary+weaponNum/**/,args);
    +		owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args);
     		relWeaponPos=owner->localmodel->GetPiecePos(args[0]);
    +
    +		owner->cob->Call(/*COBFN_AimFromPrimary+weaponNum*/COBFN_QueryPrimary+weaponNum/**/,args);
    +		relWeaponMuzzlePos=owner->localmodel->GetPiecePos(args[0]);
    +
     		weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x;
     
    +		weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x;
    +
     //		logOutput.Print("RelPosFire %f %f %f",relWeaponPos.x,relWeaponPos.y,relWeaponPos.z);
     
     		if (owner->unitDef->decloakOnFire && (owner->scriptCloak <= 2)) {
    @@ -353,9 +374,9 @@
     
     	if(!weaponDef->waterweapon && pos.y<1)
     		pos.y=1;
    -	weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x;
    -	if(weaponPos.y<ground->GetHeight2(weaponPos.x,weaponPos.z))
    -		weaponPos=owner->pos+UpVector*10;		//hope that we are underground because we are a popup weapon and will come above ground later
    +	weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x;
    +	if(weaponMuzzlePos.y<ground->GetHeight2(weaponMuzzlePos.x,weaponMuzzlePos.z))
    +		weaponMuzzlePos=owner->pos+UpVector*10;		//hope that we are underground because we are a popup weapon and will come above ground later
     
     	if(!TryTarget(pos,userTarget,0))
     		return false;
    @@ -378,8 +399,10 @@
     
     	weaponPos= owner->pos + owner->frontdir * relWeaponPos.z
     		+ owner->updir * relWeaponPos.y + owner->rightdir * relWeaponPos.x;
    -	if(weaponPos.y < ground->GetHeight2(weaponPos.x, weaponPos.z))
    -		weaponPos = owner->pos + UpVector * 10;
    +	weaponMuzzlePos= owner->pos + owner->frontdir * relWeaponMuzzlePos.z
    +		+ owner->updir * relWeaponMuzzlePos.y + owner->rightdir * relWeaponMuzzlePos.x;
    +	if(weaponMuzzlePos.y < ground->GetHeight2(weaponMuzzlePos.x, weaponMuzzlePos.z))
    +		weaponMuzzlePos = owner->pos + UpVector * 10;
     	//hope that we are underground because we are a popup weapon and will come above ground later
     
     	if(!unit){
    @@ -427,18 +450,17 @@
     #endif
     	std::vector<int> args;
     	args.push_back(0);
    -	if(useWeaponPosForAim){
    -		owner->cob->Call(COBFN_QueryPrimary+weaponNum,args);
    -		if(useWeaponPosForAim>1)
    -			useWeaponPosForAim--;
    -	} else {
    -		owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args);
    -	}
    +	owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args);
     	relWeaponPos=owner->localmodel->GetPiecePos(args[0]);
     	weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x;
    -	if(weaponPos.y<ground->GetHeight2(weaponPos.x,weaponPos.z))
    -		weaponPos=owner->pos+UpVector*10;		//hope that we are underground because we are a popup weapon and will come above ground later
     
    +	owner->cob->Call(COBFN_QueryPrimary+weaponNum,args);
    +	relWeaponMuzzlePos=owner->localmodel->GetPiecePos(args[0]);
    +	weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x;
    +
    +	if(weaponMuzzlePos.y<ground->GetHeight2(weaponMuzzlePos.x,weaponMuzzlePos.z))
    +		weaponMuzzlePos=owner->pos+UpVector*10;		//hope that we are underground because we are a popup weapon and will come above ground later
    +
     	predictSpeedMod=1+(gs->randFloat()-0.5f)*2*(1-owner->limExperience);
     
     	if((targetPos-weaponPos).SqLength() < relWeaponPos.SqLength()*16)
    @@ -542,9 +564,33 @@
     	if(weaponDef->stockpile && !numStockpiled)
     		return false;
     
    -	float3 dif=pos-weaponPos;
    +	float3 dif=pos-weaponMuzzlePos;
     
    -	float r=GetRange2D(owner->pos.y-pos.y);
    +	if (targetBorder != 0 && unit) {
    +		float3 diff(dif);
    +		diff.Normalize();
    +		// weapon inside target sphere
    +		if (dif.SqLength() < unit->sqRadius*targetBorder*targetBorder) {
    +			dif -= diff*(dif.Length() - 10); // a hack
    +			//logOutput << "inside\n";
    +		} else {
    +			dif -= diff*(unit->radius*targetBorder);
    +			//logOutput << "outside\n";
    +		}
    +		//geometricObjects->AddLine(weaponMuzzlePos, weaponMuzzlePos+dif, 3, 0, 16);
    +	}
    +
    +	float r;
    +	if (!unit || cylinderTargetting < 0.01) {
    +		r=GetRange2D(owner->pos.y-pos.y);
    +	} else {
    +		if (cylinderTargetting * unit->radius > owner->pos.y-pos.y) {
    +			r = GetRange2D(0);
    +		} else {
    +			r = 0;
    +		}
    +	}
    +
     	if(dif.SqLength2D()>=r*r)
     		return false;
     
    @@ -586,11 +632,13 @@
     	owner->frontdir = GetVectorFromHeading(owner->heading);
     	owner->rightdir = owner->frontdir.cross(owner->updir);
     	weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x;
    +	weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x;
     	bool val = TryTarget(tempTargetPos,userTarget,unit);
     	owner->frontdir = tempfrontdir;
     	owner->rightdir = temprightdir;
     	owner->heading = tempHeadding;
     	weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x;
    +	weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x;
     	return val;
     }
     
    @@ -616,11 +664,13 @@
     	owner->frontdir = GetVectorFromHeading(owner->heading);
     	owner->rightdir = owner->frontdir.cross(owner->updir);
     	weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x;
    +	weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x;
     	bool val = TryTarget(pos, userTarget, 0);
     	owner->frontdir = tempfrontdir;
     	owner->rightdir = temprightdir;
     	owner->heading = tempHeadding;
     	weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x;
    +	weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x;
     	return val;
     }
     
    @@ -631,6 +681,10 @@
     	owner->cob->Call(COBFN_AimFromPrimary+weaponNum,args);
     	relWeaponPos=owner->localmodel->GetPiecePos(args[0]);
     	weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x;
    +
    +	owner->cob->Call(COBFN_QueryPrimary+weaponNum,args);
    +	relWeaponMuzzlePos=owner->localmodel->GetPiecePos(args[0]);
    +	weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x;
     //	logOutput.Print("RelPos %f %f %f",relWeaponPos.x,relWeaponPos.y,relWeaponPos.z);
     
     	if (range > owner->maxRange) {
    Index: rts/Sim/Weapons/Weapon.h
    ===================================================================
    --- rts/Sim/Weapons/Weapon.h	(revision 3829)
    +++ rts/Sim/Weapons/Weapon.h	(working copy)
    @@ -60,6 +60,10 @@
     
     	float3 relWeaponPos;				//weaponpos relative to the unit
     	float3 weaponPos;						//absolute weapon pos
    +
    +	float3 relWeaponMuzzlePos;			//position of the firepoint
    +	float3 weaponMuzzlePos;
    +
     	float muzzleFlareSize;			//size of muzzle flare if drawn
     	int useWeaponPosForAim;			//sometimes weapon pos is better to use than aimpos
     	bool hasCloseTarget;					//might need to update weapon pos more often when enemy is near
    @@ -119,12 +123,17 @@
     	int lastErrorVectorUpdate;
     
     	CWeapon* slavedTo;						//use this weapon to choose target
    -	
    +
     	float3 mainDir;								//main aim dir of weapon
     	float maxMainDirAngleDif;					//how far away from main aim dir the weapon can aim at something (as an acos value)
     
     	bool avoidFriendly;		//if true tried to avoid friendly Units when aiming.
     	bool avoidFeature;      		//if true try to avoid Features while aiming.
    +
    +	float targetBorder;  // if nonzero, targetting units will TryTarget at the edge of collision sphere (radius*tag value, [-1;1]) instead of its centre
    +	float cylinderTargetting;	//if greater than 0, range will be checked in a cylinder (height=unitradius*cylinderTargetting) instead of a sphere
    +	float minIntensity;	// for beamlasers - always hit with some minimum intensity (a damage coeffcient normally dependent on distance). do not confuse with intensity tag, it's completely unrelated.
    +
     	unsigned int collisionFlags;
     
     	float fuelUsage;
    Index: rts/Sim/Weapons/WeaponDefHandler.cpp
    ===================================================================
    --- rts/Sim/Weapons/WeaponDefHandler.cpp	(revision 3829)
    +++ rts/Sim/Weapons/WeaponDefHandler.cpp	(working copy)
    @@ -91,6 +91,17 @@
     	if(!collideFeature)
     		weaponDefs[id].collisionFlags+=COLLISION_NOFEATURE;
     
    +	sunparser->GetDef(weaponDefs[id].targetBorder, "0", weaponname + "\\TargetBorder");
    +	if (weaponDefs[id].targetBorder > 1.f) {
    +		logOutput.Print("warning: targetBorder truncated to 1 (was %f)", weaponDefs[id].targetBorder);
    +		weaponDefs[id].targetBorder = 1.f;
    +	} else if (weaponDefs[id].targetBorder < -1.f) {
    +		logOutput.Print("warning: targetBorder truncated to -1 (was %f)", weaponDefs[id].targetBorder);
    +		weaponDefs[id].targetBorder = -1.f;
    +	}
    +	sunparser->GetDef(weaponDefs[id].cylinderTargetting, "0", weaponname + "\\CylinderTargetting");
    +	sunparser->GetDef(weaponDefs[id].minIntensity, "0", weaponname + "\\MinIntensity");
    +
     	sunparser->GetDef(weaponDefs[id].dropped, "0", weaponname + "\\dropped");
     	sunparser->GetDef(lineofsight, "0", weaponname + "\\lineofsight");
     	sunparser->GetDef(ballistic, "0", weaponname + "\\ballistic");
    @@ -119,7 +130,7 @@
     	sunparser->GetDef(weaponDefs[id].laserflaresize, "15", weaponname + "\\laserflaresize");
     	sunparser->GetDef(weaponDefs[id].intensity, "0.9", weaponname + "\\intensity");
     	sunparser->GetDef(weaponDefs[id].duration, "0.05", weaponname + "\\duration");
    -	
    +
     	sunparser->GetDef(weaponDefs[id].visuals.sizeDecay,  "0", weaponname + "\\sizeDecay");
     	sunparser->GetDef(weaponDefs[id].visuals.alphaDecay, "1", weaponname + "\\alphaDecay");
     	sunparser->GetDef(weaponDefs[id].visuals.separation, "1", weaponname + "\\separation");
    @@ -576,7 +587,7 @@
     
     	if (inverted == true) {
     		for(int i=0; i < damageArrayHandler->numTypes; ++i) {
    -			
    +
     			dynDamages[i] = damages[i] - (1 - pow(1 / range * travDist, exp)) * damages[i];
     
     			if (damageMin > 0) {
    @@ -591,7 +602,7 @@
     	}
     	else {
     		for(int i=0; i < damageArrayHandler->numTypes; ++i) {
    -			
    +
     			dynDamages[i] = (1 - pow(1 / range * travDist, exp)) * damages[i];
     
     			if (damageMin > 0) {
    Index: rts/Sim/Weapons/WeaponDefHandler.h
    ===================================================================
    --- rts/Sim/Weapons/WeaponDefHandler.h	(revision 3829)
    +++ rts/Sim/Weapons/WeaponDefHandler.h	(working copy)
    @@ -86,7 +86,7 @@
     	bool manualfire;			//use dgun button
     	int interceptor;				//anti nuke
     	int targetable;				//nuke (can be shot by interceptor)
    -	bool stockpile;					
    +	bool stockpile;
     	float coverageRange;		//range of anti nuke
     
     	float intensity;
    @@ -138,7 +138,7 @@
     		float tilelength;
     		float scrollspeed;
     		float pulseSpeed;
    -		
    +
     		int stages;
     		float alphaDecay;
     		float sizeDecay;
    @@ -172,6 +172,11 @@
     
     	bool avoidFriendly;		//if true try to avoid friendly Units when aiming.
     	bool avoidFeature;      //if true try to avoid Features while aiming.
    +
    +	float targetBorder;		//if nonzero, targetting units will TryTarget at the edge of collision sphere (radius*tag value, [-1;1]) instead of its centre
    +	float cylinderTargetting;	//if greater than 0, range will be checked in a cylinder (height=unitradius*cylinderTargetting) instead of a sphere
    +	float minIntensity;		// for beamlasers - always hit with some minimum intensity (a damage coeffcient normally dependent on distance). do not confuse with intensity tag, it's completely unrelated.
    +
     	unsigned int collisionFlags;
     
     	CExplosionGenerator *explosionGenerator; // can be zero for default explosions
    
  • patch file icon targetborder_minintensity_cylinderTargetting_and_weaponMuzzlePos_v2.patch (21,890 bytes) 2007-06-24 00:40 -
    Index: Game/GameHelper.cpp
    ===================================================================
    --- Game/GameHelper.cpp	(revision 3842)
    +++ Game/GameHelper.cpp	(working copy)
    @@ -209,16 +209,17 @@
     				closeLength=length;
     			float3 closeVect=dif-dir*closeLength;
     
    -			float rad=(*ui)->radius;
    -			float tmp = rad * rad - closeVect.SqLength();
    -			if(tmp > 0 && length>closeLength+sqrt(tmp)){
    +			/*float rad=(*ui)->radius;
    +			float tmp = rad * rad - closeVect.SqLength();*/
    +
    +			/*if(tmp > 0 && length>closeLength+sqrt(tmp)){
     				length=closeLength-sqrt(tmp)*0.5f;
     				hit=*ui;
    -			}
    -/*			if(closeVect.SqLength() < (*ui)->sqRadius){
    +			}*/
    +			if(closeVect.SqLength() < (*ui)->sqRadius){
     				length=closeLength;
     				hit=*ui;
    -			}*/
    +			}
     		}
     	}
     	return length;
    Index: Lua/LuaWeaponDefs.cpp
    ===================================================================
    --- Lua/LuaWeaponDefs.cpp	(revision 3842)
    +++ Lua/LuaWeaponDefs.cpp	(working copy)
    @@ -75,7 +75,7 @@
     				HSTR_PUSH(L, "__index");
     				lua_pushlightuserdata(L, (void*)wd);
     				lua_pushcclosure(L, WeaponDefIndex, 1);
    -				lua_rawset(L, -3); // closure 
    +				lua_rawset(L, -3); // closure
     
     				HSTR_PUSH(L, "__newindex");
     				lua_pushlightuserdata(L, (void*)wd);
    @@ -110,7 +110,7 @@
     
     static int WeaponDefIndex(lua_State* L)
     {
    -	// not a default value	
    +	// not a default value
     	if (!lua_isstring(L, 2)) {
     		lua_rawget(L, 1);
     		return 1;
    @@ -119,7 +119,7 @@
     	const char* name = lua_tostring(L, 2);
     	ParamMap::const_iterator it = paramMap.find(name);
     
    -	// not a default value	
    +	// not a default value
     	if (paramMap.find(name) == paramMap.end()) {
     		lua_rawget(L, 1);
     		return 1;
    @@ -171,7 +171,7 @@
     
     	const char* name = lua_tostring(L, 2);
     	ParamMap::const_iterator it = paramMap.find(name);
    -	
    +
     	// not a default value, set it
     	if (paramMap.find(name) == paramMap.end()) {
     		lua_rawset(L, 1);
    @@ -186,7 +186,7 @@
     	 	luaL_error(L, "Attempt to write WeaponDefs[%d].%s", wd->id, name);
     	 	return 0;
     	}
    -	
    +
     	// Definition editing
     	const DataElement& elem = it->second;
     	const char* p = ((const char*)wd) + elem.offset;
    @@ -217,7 +217,7 @@
     			luaL_error(L, "ERROR_TYPE in WeaponDefs __newindex");
     		}
     	}
    -	
    +
     	return 0;
     }
     
    @@ -266,7 +266,7 @@
     			}
     			// start the user parameters,
     			// remove the internal key and push a nil
    -			lua_settop(L, 1); 
    +			lua_settop(L, 1);
     			lua_pushnil(L);
     		}
     	}
    @@ -314,8 +314,8 @@
     		LuaPushNamedNumber(L, typeList[i].c_str(), d.damages[i]);
     	}
     	lua_rawset(L, -3);
    -	
    -	return 1;		
    +
    +	return 1;
     }
     
     
    @@ -417,9 +417,9 @@
     
     static bool InitParamMap()
     {
    -	paramMap["next"]  = DataElement(READONLY_TYPE); 
    -	paramMap["pairs"] = DataElement(READONLY_TYPE); 
    -	
    +	paramMap["next"]  = DataElement(READONLY_TYPE);
    +	paramMap["pairs"] = DataElement(READONLY_TYPE);
    +
     	// dummy WeaponDef for offset generation
     	const WeaponDef wd;
     	const char* start = ADDRESS(wd);
    @@ -481,7 +481,7 @@
     	ADD_BOOL("noAutoTarget",   wd.noAutoTarget);
     	ADD_BOOL("manualFire",     wd.manualfire);
     	ADD_INT("targetable",      wd.targetable);
    -	ADD_BOOL("stockpile",      wd.stockpile);					
    +	ADD_BOOL("stockpile",      wd.stockpile);
     	ADD_INT("interceptor",     wd.interceptor);
     	ADD_FLOAT("coverageRange", wd.coverageRange);
     
    @@ -541,6 +541,9 @@
     	ADD_INT("interceptedByShieldType",  wd.interceptedByShieldType);
     
     	ADD_BOOL("avoidFriendly", wd.avoidFriendly);
    +	ADD_FLOAT("targetBorder", wd.targetBorder);
    +	ADD_FLOAT("cylinderTargetting", wd.cylinderTargetting);
    +	ADD_FLOAT("minIntensity", wd.minIntensity);
     
     //	CExplosionGenerator *explosionGenerator;
     
    Index: Sim/Projectiles/ProjectileHandler.cpp
    ===================================================================
    --- Sim/Projectiles/ProjectileHandler.cpp	(revision 3842)
    +++ Sim/Projectiles/ProjectileHandler.cpp	(working copy)
    @@ -31,8 +31,8 @@
     
     CProjectileHandler* ph;
     using namespace std;
    -extern GLfloat FogBlack[]; 
    -extern GLfloat FogLand[]; 
    +extern GLfloat FogBlack[];
    +extern GLfloat FogLand[];
     
     CR_BIND(CProjectileHandler,);
     
    @@ -137,7 +137,7 @@
     	wrecktex            = textureAtlas->GetTextureWithBackup(  "wrecktexture",           "circularthingy"  );
     	plasmatex           = textureAtlas->GetTextureWithBackup(  "plasmatexture",          "circularthingy"  );
     
    -	
    +
     	groundFXAtlas = SAFE_NEW CTextureAtlas(2048, 2048);
     	//add all textures in groundfx section
     	ptex = resources.GetAllValues("resources\\graphics\\groundfx");
    @@ -328,9 +328,9 @@
     
     	int numFlyingPieces = 0;
     	int drawnPieces = 0;
    -	
    +
     	/* Putting in, say, viewport culling will deserve refactoring. */
    -	
    +
     	/* 3DO */
     	unitDrawer->SetupForUnitDrawing();
     
    @@ -376,27 +376,27 @@
     
     	for (int textureType = 1; textureType < flyings3oPieces.size(); textureType++){
     		/* TODO Skip this if there's no FlyingPieces. */
    -		
    +
     		texturehandler->SetS3oTexture(textureType);
    -		
    +
     		for (int team = 0; team < flyings3oPieces[textureType].size(); team++){
     			FlyingPiece_List * fpl = flyings3oPieces[textureType][team];
    -		
    +
     			unitDrawer->SetS3OTeamColour(team);
    -			
    +
     			va->Initialize();
    -			
    +
     			numFlyingPieces += fpl->size();
    -		
    +
     			for(std::list<FlyingPiece*>::iterator pi=fpl->begin();pi!=fpl->end();++pi){
     				CMatrix44f m;
     				m.Rotate((*pi)->rot,(*pi)->rotAxis);
     				float3 interPos=(*pi)->pos+(*pi)->speed*gu->timeOffset;
    -				
    +
     				SS3OVertex * verts = (*pi)->verts;
    -				
    +
     				float3 tp, tn;
    -				
    +
     				for (int i = 0; i < 4; i++){
     					tp=m.Mul(verts[i].pos);
     					tn=m.Mul(verts[i].normal);
    @@ -408,9 +408,9 @@
     			va->DrawArrayTN(GL_QUADS);
     		}
     	}
    -	
    +
     	unitDrawer->CleanUpS3ODrawing();
    -	
    +
     	/*
     	 * TODO Nearly cut here.
     	 */
    @@ -565,7 +565,7 @@
     						if(readmap->groundBlockingObjectMap[square]!=unit)
     							continue;
     					}
    -					//adjust projectile position so explosion happens at the correct position 
    +					//adjust projectile position so explosion happens at the correct position
     					p->pos = p->pos + p->speed*closeTime;
     					p->Collision(*ui);
     					break;
    @@ -603,7 +603,7 @@
     				}
     			}
     		}
    -	}	
    +	}
     }
     
     void CProjectileHandler::AddGroundFlash(CGroundFlash* flash)
    @@ -687,7 +687,7 @@
     		flyings3oPieces[textureType].push_back(fpl);
     		flyingPieces.push_back(fpl);
     	}
    -	
    +
     	pieceList=flyings3oPieces[textureType][team];
     
     	FlyingPiece* fp=new FlyingPiece;
    @@ -745,7 +745,7 @@
     	glDisable(GL_ALPHA_TEST);
     	glDisable(GL_FOG);
     
    -	unsigned char col[4];	
    +	unsigned char col[4];
     	float time=gu->lastFrameTime*gs->speedFactor*3;
     	float speed=1;
     	float size=1;
    Index: Sim/Projectiles/WeaponProjectile.cpp
    ===================================================================
    --- Sim/Projectiles/WeaponProjectile.cpp	(revision 3842)
    +++ Sim/Projectiles/WeaponProjectile.cpp	(working copy)
    @@ -45,7 +45,7 @@
     	interceptTarget=0;
     }
     
    -CWeaponProjectile::CWeaponProjectile(const float3& pos,const float3& speed,CUnit* owner, CUnit* target,const float3 &targetPos, WeaponDef *weaponDef,CWeaponProjectile* interceptTarget, bool synced) : 
    +CWeaponProjectile::CWeaponProjectile(const float3& pos,const float3& speed,CUnit* owner, CUnit* target,const float3 &targetPos, WeaponDef *weaponDef,CWeaponProjectile* interceptTarget, bool synced) :
     	CProjectile(pos,speed,owner, synced),
     	weaponDef(weaponDef),
     	weaponDefName(weaponDef?weaponDef->name:std::string("")),
    @@ -120,7 +120,7 @@
     		DamageArray dynDamages;
     		if (weaponDef->dynDamageExp > 0)
     			dynDamages = weaponDefHandler->DynamicDamages(weaponDef->damages, startpos, pos, weaponDef->dynDamageRange>0?weaponDef->dynDamageRange:weaponDef->range, weaponDef->dynDamageExp, weaponDef->dynDamageMin, weaponDef->dynDamageInverted);
    -		
    +
     		helper->Explosion(pos,weaponDef->dynDamageExp>0?dynDamages:weaponDef->damages,weaponDef->areaOfEffect,weaponDef->edgeEffectiveness,weaponDef->explosionSpeed,owner,true,weaponDef->noExplode? 0.3f:1,weaponDef->noExplode || weaponDef->noSelfDamage, weaponDef->explosionGenerator,0,impactDir, weaponDef->id);
     	}
     
    @@ -155,7 +155,7 @@
     		DamageArray dynDamages;
     		if (weaponDef->dynDamageExp > 0)
     			dynDamages = weaponDefHandler->DynamicDamages(weaponDef->damages, startpos, pos, weaponDef->dynDamageRange>0?weaponDef->dynDamageRange:weaponDef->range, weaponDef->dynDamageExp, weaponDef->dynDamageMin, weaponDef->dynDamageInverted);
    -			
    +
     		helper->Explosion(pos,weaponDef->dynDamageExp>0?dynDamages:weaponDef->damages,weaponDef->areaOfEffect,weaponDef->edgeEffectiveness,weaponDef->explosionSpeed,owner,true,weaponDef->noExplode? 0.3f:1,weaponDef->noExplode,weaponDef->explosionGenerator,unit,impactDir, weaponDef->id);
     	}
     
    @@ -226,7 +226,7 @@
     	transMatrix[12]=interPos.x;
     	transMatrix[13]=interPos.y;
     	transMatrix[14]=interPos.z;
    -	glMultMatrixf(&transMatrix[0]);		
    +	glMultMatrixf(&transMatrix[0]);
     
     	glCallList(modelDispList);
     	glPopMatrix();
    Index: Sim/Units/CommandAI/MobileCAI.cpp
    ===================================================================
    --- Sim/Units/CommandAI/MobileCAI.cpp	(revision 3842)
    +++ Sim/Units/CommandAI/MobileCAI.cpp	(working copy)
    @@ -42,6 +42,8 @@
     				CR_MEMBER(commandPos1),
     				CR_MEMBER(commandPos2),
     
    +				CR_MEMBER(lastCloseInTry),
    +				
     				CR_MEMBER(cancelDistance),
     				CR_MEMBER(slowGuard),
     				CR_MEMBER(moveDir)
    @@ -61,6 +63,7 @@
     	commandPos2(ZeroVector),
     	lastPC(-1),
     	cancelDistance(1024),
    +	lastCloseInTry(-1),
     	slowGuard(false),
     	moveDir(gs->randFloat() > 0.5),
     	lastUserGoal(0,0,0)
    @@ -81,6 +84,7 @@
     	commandPos2(ZeroVector),
     	lastPC(-1),
     	cancelDistance(1024),
    +	lastCloseInTry(-1),
     	slowGuard(false),
     	moveDir(gs->randFloat() > 0.5)
     {
    @@ -582,7 +586,8 @@
     			// check if we have valid target parameter and that we aren't attacking ourselves
     			if (uh->units[unitID] != 0 && uh->units[unitID] != owner) {
     				float3 fix = uh->units[unitID]->pos + owner->posErrorVector * 128;
    -				SetGoal(fix, owner->pos);
    +				float3 diff = float3(fix - owner->pos).Normalize();
    +				SetGoal(fix - diff*uh->units[unitID]->radius, owner->pos);
     				// get ID of attack-order target unit
     				orderTarget = uh->units[unitID];
     				AddDeathDependence(orderTarget);
    @@ -626,6 +631,7 @@
     		//bool b1 = owner->AttackUnit(orderTarget, c.id == CMD_DGUN);
     		bool b2 = false;
     		bool b3 = false;
    +		float edgeFactor = 0.f; // percent offset to target center
     
     		if (owner->weapons.size() > 0) {
     			if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) {
    @@ -638,15 +644,21 @@
     			// can hit target with our first (meanest) one
     			b2 = w->TryTargetRotate(orderTarget, c.id == CMD_DGUN);
     			b3 = (w->range - (w->relWeaponPos).Length()) > (orderTarget->pos.distance(owner->pos));
    +			edgeFactor = fabs(w->targetBorder);
     		}
     		float3 diff = owner->pos - orderTarget->pos;
     		// if w->AttackUnit() returned true then we are already
     		// in range with our biggest weapon so stop moving
    +		// also make sure that we're not locked in close-in/in-range state loop
    +		// due to rotates invoked by in-range or out-of-range states
     		if (b2) {
     			StopMove();
     			owner->AttackUnit(orderTarget, c.id == CMD_DGUN);
    -			owner->moveType->KeepPointingTo(orderTarget,
    -				min((float) (owner->losRadius * SQUARE_SIZE * 2), owner->maxRange * 0.9f), true);
    +			// FIXME kill magic frame number
    +			if (gs->frameNum > lastCloseInTry + MAX_CLOSE_IN_RETRY_TICKS) {
    +				owner->moveType->KeepPointingTo(orderTarget,
    +					min((float) (owner->losRadius * SQUARE_SIZE * 2), owner->maxRange * 0.9f), true);
    +			}
     		}
     
     		// if (((first weapon range minus first weapon length greater than distance to target
    @@ -675,7 +687,10 @@
     		else if ((orderTarget->pos + owner->posErrorVector * 128).distance2D(goalPos)
     				> (10 + orderTarget->pos.distance2D(owner->pos) * 0.2f)) {
     			float3 fix = orderTarget->pos + owner->posErrorVector * 128;
    -			SetGoal(fix, owner->pos);
    +			float3 norm = float3(fix - owner->pos).Normalize();
    +			SetGoal(fix - norm*(orderTarget->radius*edgeFactor*0.8f), owner->pos);
    +			if (lastCloseInTry < gs->frameNum + MAX_CLOSE_IN_RETRY_TICKS)
    +				lastCloseInTry = gs->frameNum;
     		}
     	}
     
    Index: Sim/Units/CommandAI/MobileCAI.h
    ===================================================================
    --- Sim/Units/CommandAI/MobileCAI.h	(revision 3842)
    +++ Sim/Units/CommandAI/MobileCAI.h	(working copy)
    @@ -59,6 +59,7 @@
     
     protected:
     	int cancelDistance;
    +	int lastCloseInTry;
     	bool slowGuard;
     	bool moveDir;
     	void PushOrUpdateReturnFight() {
    @@ -66,5 +67,6 @@
     	}
     };
     
    +#define MAX_CLOSE_IN_RETRY_TICKS 30
     
     #endif /* MOBILECAI_H */
    Index: Sim/Units/UnitLoader.cpp
    ===================================================================
    --- Sim/Units/UnitLoader.cpp	(revision 3842)
    +++ Sim/Units/UnitLoader.cpp	(working copy)
    @@ -466,6 +466,9 @@
     	weapon->fuelUsage = udw->fuelUsage;
     	weapon->avoidFriendly = weapondef->avoidFriendly;
     	weapon->avoidFeature = weapondef->avoidFeature;
    +	weapon->targetBorder = weapondef->targetBorder;
    +	weapon->cylinderTargetting = weapondef->cylinderTargetting;
    +	weapon->minIntensity = weapondef->minIntensity;
     	weapon->collisionFlags = weapondef->collisionFlags;
     	weapon->Init();
     
    Index: Sim/Weapons/BeamLaser.cpp
    ===================================================================
    --- Sim/Weapons/BeamLaser.cpp	(revision 3842)
    +++ Sim/Weapons/BeamLaser.cpp	(working copy)
    @@ -85,6 +85,7 @@
     	}
     
     	float3 dir=pos-weaponPos;
    +
     	float length=dir.Length();
     	if(length==0)
     		return true;
    @@ -133,6 +134,7 @@
     		}
     	}
     	dir+=(salvoError)*(1-owner->limExperience*0.7f);
    +
     	dir.Normalize();
     
     	FireInternal(dir, false);
    @@ -145,6 +147,7 @@
     	if(owner->directControl)
     		rangeMod=0.95f;
     #endif
    +
     	float maxLength=range*rangeMod;
     	float curLength=0;
     	float3 curPos=weaponPos;
    @@ -152,6 +155,19 @@
     
     	bool tryAgain=true;
     	CUnit* hit;
    +
    +	// increase range if targets are searched for in a cylinder
    +	if (cylinderTargetting > 0.01) {
    +		const float3 up(0, owner->radius*cylinderTargetting, 0);
    +		const float uplen = up.dot(dir);
    +		maxLength = sqrt(maxLength*maxLength + uplen*uplen);
    +	}
    +
    +	// increase range if targetting edge of hitsphere
    +	if (targetType == Target_Unit && targetUnit && targetBorder != 0) {
    +		maxLength += targetUnit->radius*targetBorder;
    +	}
    +
     	for(int tries=0;tries<5 && tryAgain;++tries){
     		tryAgain=false;
     		hit=0;
    @@ -172,6 +188,7 @@
     				tryAgain=true;
     			}
     		}
    +
     		hitPos=curPos+dir*length;
     
     		float baseAlpha=weaponDef->intensity*255;
    @@ -187,7 +204,15 @@
     		curLength+=length;
     		dir=newDir;
     	}
    -	float	intensity=1-(curLength)/(range*2);
    +
    +	// fix negative damage when hitting big spheres
    +	float actualRange = range;
    +	if (hit && targetBorder > 0) {
    +		actualRange += hit->radius*targetBorder;
    +	}
    +	// make it possible to always hit with some minimal intensity (melee weapons have use for that)
    +	float intensity=max(minIntensity, 1-(curLength)/(actualRange*2));
    +
     	if(curLength<maxLength) {
     		// Dynamic Damage
     		DamageArray dynDamages;
    Index: Sim/Weapons/MeleeWeapon.cpp
    ===================================================================
    --- Sim/Weapons/MeleeWeapon.cpp	(revision 3842)
    +++ Sim/Weapons/MeleeWeapon.cpp	(working copy)
    @@ -34,7 +34,9 @@
     void CMeleeWeapon::Fire(void)
     {
     	if(targetType==Target_Unit){
    -		targetUnit->DoDamage(damages,owner,ZeroVector,weaponDef->id);
    +		float3 impulseDir = targetUnit->pos-weaponPos;
    +		impulseDir.Normalize();
    +		targetUnit->DoDamage(damages,owner,impulseDir,weaponDef->id);
     		if(fireSoundId)
     			sound->PlaySample(fireSoundId,owner,fireSoundVolume);
     	}
    Index: Sim/Weapons/Weapon.cpp
    ===================================================================
    --- Sim/Weapons/Weapon.cpp	(revision 3842)
    +++ Sim/Weapons/Weapon.cpp	(working copy)
    @@ -83,6 +83,9 @@
     	CR_MEMBER(hasCloseTarget),
     	CR_MEMBER(avoidFriendly),
     	CR_MEMBER(avoidFeature),
    +	CR_MEMBER(targetBorder),
    +	CR_MEMBER(cylinderTargetting),
    +	CR_MEMBER(minIntensity),
     	CR_MEMBER(collisionFlags),
     	CR_MEMBER(fuelUsage),
     	CR_MEMBER(weaponNum)
    @@ -151,6 +154,9 @@
     	hasCloseTarget(false),
     	avoidFriendly(true),
     	avoidFeature(true),
    +	targetBorder(0.f),
    +	cylinderTargetting(0.f),
    +	minIntensity(0.f),
     	collisionFlags(0),
     	fuelUsage(0)
     {
    @@ -164,6 +170,12 @@
     
     void CWeapon::Update()
     {
    +	// do not fire at cloaked units
    +	if(targetType == Target_Unit && targetUnit && targetUnit->isCloaked) {
    +		HoldFire();
    +		return;
    +	}
    +
     	if(hasCloseTarget){
     		std::vector<int> args;
     		args.push_back(0);
    @@ -544,7 +556,31 @@
     
     	float3 dif=pos-weaponPos;
     
    -	float r=GetRange2D(owner->pos.y-pos.y);
    +	if (targetBorder != 0 && unit) {
    +		float3 diff(dif);
    +		diff.Normalize();
    +		// weapon inside target sphere
    +		if (dif.SqLength() < unit->sqRadius*targetBorder*targetBorder) {
    +			dif -= diff*(dif.Length() - 10); // a hack
    +			//logOutput << "inside\n";
    +		} else {
    +			dif -= diff*(unit->radius*targetBorder);
    +			//logOutput << "outside\n";
    +		}
    +		//geometricObjects->AddLine(weaponMuzzlePos, weaponMuzzlePos+dif, 3, 0, 16);
    +	}
    +
    +	float r;
    +	if (!unit || cylinderTargetting < 0.01) {
    +		r=GetRange2D(owner->pos.y-pos.y);
    +	} else {
    +		if (cylinderTargetting * unit->radius > owner->pos.y-pos.y) {
    +			r = GetRange2D(0);
    +		} else {
    +			r = 0;
    +		}
    +	}
    +
     	if(dif.SqLength2D()>=r*r)
     		return false;
     
    Index: Sim/Weapons/Weapon.h
    ===================================================================
    --- Sim/Weapons/Weapon.h	(revision 3842)
    +++ Sim/Weapons/Weapon.h	(working copy)
    @@ -119,12 +119,17 @@
     	int lastErrorVectorUpdate;
     
     	CWeapon* slavedTo;						//use this weapon to choose target
    -	
    +
     	float3 mainDir;								//main aim dir of weapon
     	float maxMainDirAngleDif;					//how far away from main aim dir the weapon can aim at something (as an acos value)
     
     	bool avoidFriendly;		//if true tried to avoid friendly Units when aiming.
     	bool avoidFeature;      		//if true try to avoid Features while aiming.
    +
    +	float targetBorder;  // if nonzero, targetting units will TryTarget at the edge of collision sphere (radius*tag value, [-1;1]) instead of its centre
    +	float cylinderTargetting;	//if greater than 0, range will be checked in a cylinder (height=unitradius*cylinderTargetting) instead of a sphere
    +	float minIntensity;	// for beamlasers - always hit with some minimum intensity (a damage coeffcient normally dependent on distance). do not confuse with intensity tag, it's completely unrelated.
    +
     	unsigned int collisionFlags;
     
     	float fuelUsage;
    Index: Sim/Weapons/WeaponDefHandler.cpp
    ===================================================================
    --- Sim/Weapons/WeaponDefHandler.cpp	(revision 3842)
    +++ Sim/Weapons/WeaponDefHandler.cpp	(working copy)
    @@ -91,6 +91,17 @@
     	if(!collideFeature)
     		weaponDefs[id].collisionFlags+=COLLISION_NOFEATURE;
     
    +	sunparser->GetDef(weaponDefs[id].targetBorder, "0", weaponname + "\\TargetBorder");
    +	if (weaponDefs[id].targetBorder > 1.f) {
    +		logOutput.Print("warning: targetBorder truncated to 1 (was %f)", weaponDefs[id].targetBorder);
    +		weaponDefs[id].targetBorder = 1.f;
    +	} else if (weaponDefs[id].targetBorder < -1.f) {
    +		logOutput.Print("warning: targetBorder truncated to -1 (was %f)", weaponDefs[id].targetBorder);
    +		weaponDefs[id].targetBorder = -1.f;
    +	}
    +	sunparser->GetDef(weaponDefs[id].cylinderTargetting, "0", weaponname + "\\CylinderTargetting");
    +	sunparser->GetDef(weaponDefs[id].minIntensity, "0", weaponname + "\\MinIntensity");
    +
     	sunparser->GetDef(weaponDefs[id].dropped, "0", weaponname + "\\dropped");
     	sunparser->GetDef(lineofsight, "0", weaponname + "\\lineofsight");
     	sunparser->GetDef(ballistic, "0", weaponname + "\\ballistic");
    @@ -119,7 +130,7 @@
     	sunparser->GetDef(weaponDefs[id].laserflaresize, "15", weaponname + "\\laserflaresize");
     	sunparser->GetDef(weaponDefs[id].intensity, "0.9", weaponname + "\\intensity");
     	sunparser->GetDef(weaponDefs[id].duration, "0.05", weaponname + "\\duration");
    -	
    +
     	sunparser->GetDef(weaponDefs[id].visuals.sizeDecay,  "0", weaponname + "\\sizeDecay");
     	sunparser->GetDef(weaponDefs[id].visuals.alphaDecay, "1", weaponname + "\\alphaDecay");
     	sunparser->GetDef(weaponDefs[id].visuals.separation, "1", weaponname + "\\separation");
    @@ -576,7 +587,7 @@
     
     	if (inverted == true) {
     		for(int i=0; i < damageArrayHandler->numTypes; ++i) {
    -			
    +
     			dynDamages[i] = damages[i] - (1 - pow(1 / range * travDist, exp)) * damages[i];
     
     			if (damageMin > 0) {
    @@ -591,7 +602,7 @@
     	}
     	else {
     		for(int i=0; i < damageArrayHandler->numTypes; ++i) {
    -			
    +
     			dynDamages[i] = (1 - pow(1 / range * travDist, exp)) * damages[i];
     
     			if (damageMin > 0) {
    Index: Sim/Weapons/WeaponDefHandler.h
    ===================================================================
    --- Sim/Weapons/WeaponDefHandler.h	(revision 3842)
    +++ Sim/Weapons/WeaponDefHandler.h	(working copy)
    @@ -86,7 +86,7 @@
     	bool manualfire;			//use dgun button
     	int interceptor;				//anti nuke
     	int targetable;				//nuke (can be shot by interceptor)
    -	bool stockpile;					
    +	bool stockpile;
     	float coverageRange;		//range of anti nuke
     
     	float intensity;
    @@ -138,7 +138,7 @@
     		float tilelength;
     		float scrollspeed;
     		float pulseSpeed;
    -		
    +
     		int stages;
     		float alphaDecay;
     		float sizeDecay;
    @@ -172,6 +172,11 @@
     
     	bool avoidFriendly;		//if true try to avoid friendly Units when aiming.
     	bool avoidFeature;      //if true try to avoid Features while aiming.
    +
    +	float targetBorder;		//if nonzero, targetting units will TryTarget at the edge of collision sphere (radius*tag value, [-1;1]) instead of its centre
    +	float cylinderTargetting;	//if greater than 0, range will be checked in a cylinder (height=unitradius*cylinderTargetting) instead of a sphere
    +	float minIntensity;		// for beamlasers - always hit with some minimum intensity (a damage coeffcient normally dependent on distance). do not confuse with intensity tag, it's completely unrelated.
    +
     	unsigned int collisionFlags;
     
     	CExplosionGenerator *explosionGenerator; // can be zero for default explosions
    
  • patch file icon targetborder_minintensity_cylinderTargetting_and_weaponMuzzlePos_v3.patch (23,250 bytes) 2007-06-25 16:27 -
    Index: Game/GameHelper.cpp
    ===================================================================
    --- Game/GameHelper.cpp	(revision 3848)
    +++ Game/GameHelper.cpp	(working copy)
    @@ -209,16 +209,17 @@
     				closeLength=length;
     			float3 closeVect=dif-dir*closeLength;
     
    -			float rad=(*ui)->radius;
    -			float tmp = rad * rad - closeVect.SqLength();
    -			if(tmp > 0 && length>closeLength+sqrt(tmp)){
    +			/*float rad=(*ui)->radius;
    +			float tmp = rad * rad - closeVect.SqLength();*/
    +
    +			/*if(tmp > 0 && length>closeLength+sqrt(tmp)){
     				length=closeLength-sqrt(tmp)*0.5f;
     				hit=*ui;
    -			}
    -/*			if(closeVect.SqLength() < (*ui)->sqRadius){
    +			}*/
    +			if(closeVect.SqLength() < (*ui)->sqRadius){
     				length=closeLength;
     				hit=*ui;
    -			}*/
    +			}
     		}
     	}
     	return length;
    Index: Lua/LuaWeaponDefs.cpp
    ===================================================================
    --- Lua/LuaWeaponDefs.cpp	(revision 3848)
    +++ Lua/LuaWeaponDefs.cpp	(working copy)
    @@ -75,7 +75,7 @@
     				HSTR_PUSH(L, "__index");
     				lua_pushlightuserdata(L, (void*)wd);
     				lua_pushcclosure(L, WeaponDefIndex, 1);
    -				lua_rawset(L, -3); // closure 
    +				lua_rawset(L, -3); // closure
     
     				HSTR_PUSH(L, "__newindex");
     				lua_pushlightuserdata(L, (void*)wd);
    @@ -110,7 +110,7 @@
     
     static int WeaponDefIndex(lua_State* L)
     {
    -	// not a default value	
    +	// not a default value
     	if (!lua_isstring(L, 2)) {
     		lua_rawget(L, 1);
     		return 1;
    @@ -119,7 +119,7 @@
     	const char* name = lua_tostring(L, 2);
     	ParamMap::const_iterator it = paramMap.find(name);
     
    -	// not a default value	
    +	// not a default value
     	if (paramMap.find(name) == paramMap.end()) {
     		lua_rawget(L, 1);
     		return 1;
    @@ -171,7 +171,7 @@
     
     	const char* name = lua_tostring(L, 2);
     	ParamMap::const_iterator it = paramMap.find(name);
    -	
    +
     	// not a default value, set it
     	if (paramMap.find(name) == paramMap.end()) {
     		lua_rawset(L, 1);
    @@ -186,7 +186,7 @@
     	 	luaL_error(L, "Attempt to write WeaponDefs[%d].%s", wd->id, name);
     	 	return 0;
     	}
    -	
    +
     	// Definition editing
     	const DataElement& elem = it->second;
     	const char* p = ((const char*)wd) + elem.offset;
    @@ -217,7 +217,7 @@
     			luaL_error(L, "ERROR_TYPE in WeaponDefs __newindex");
     		}
     	}
    -	
    +
     	return 0;
     }
     
    @@ -266,7 +266,7 @@
     			}
     			// start the user parameters,
     			// remove the internal key and push a nil
    -			lua_settop(L, 1); 
    +			lua_settop(L, 1);
     			lua_pushnil(L);
     		}
     	}
    @@ -314,8 +314,8 @@
     		LuaPushNamedNumber(L, typeList[i].c_str(), d.damages[i]);
     	}
     	lua_rawset(L, -3);
    -	
    -	return 1;		
    +
    +	return 1;
     }
     
     
    @@ -417,9 +417,9 @@
     
     static bool InitParamMap()
     {
    -	paramMap["next"]  = DataElement(READONLY_TYPE); 
    -	paramMap["pairs"] = DataElement(READONLY_TYPE); 
    -	
    +	paramMap["next"]  = DataElement(READONLY_TYPE);
    +	paramMap["pairs"] = DataElement(READONLY_TYPE);
    +
     	// dummy WeaponDef for offset generation
     	const WeaponDef wd;
     	const char* start = ADDRESS(wd);
    @@ -481,7 +481,7 @@
     	ADD_BOOL("noAutoTarget",   wd.noAutoTarget);
     	ADD_BOOL("manualFire",     wd.manualfire);
     	ADD_INT("targetable",      wd.targetable);
    -	ADD_BOOL("stockpile",      wd.stockpile);					
    +	ADD_BOOL("stockpile",      wd.stockpile);
     	ADD_INT("interceptor",     wd.interceptor);
     	ADD_FLOAT("coverageRange", wd.coverageRange);
     
    @@ -541,6 +541,9 @@
     	ADD_INT("interceptedByShieldType",  wd.interceptedByShieldType);
     
     	ADD_BOOL("avoidFriendly", wd.avoidFriendly);
    +	ADD_FLOAT("targetBorder", wd.targetBorder);
    +	ADD_FLOAT("cylinderTargetting", wd.cylinderTargetting);
    +	ADD_FLOAT("minIntensity", wd.minIntensity);
     
     //	CExplosionGenerator *explosionGenerator;
     
    Index: Sim/MoveTypes/groundmovetype.cpp
    ===================================================================
    --- Sim/MoveTypes/groundmovetype.cpp	(revision 3848)
    +++ Sim/MoveTypes/groundmovetype.cpp	(working copy)
    @@ -194,7 +194,7 @@
     	{
     		skidding = true;
     	}
    -	
    +
     	if(skidding){
     		UpdateSkid();
     		return;
    @@ -415,6 +415,7 @@
     	tracefile << "Start moving called: ";
     	tracefile << owner->pos.x << " " << owner->pos.y << " " << owner->pos.z << " " << owner->id << "\n";
     #endif
    +
     	if(progressState == Active) {
     		StopEngine();
     	}
    @@ -609,7 +610,7 @@
     	float3& speed=owner->speed;
     	float3& pos=owner->pos;
     	SyncedFloat3& midPos=owner->midPos;
    -	
    +
     	if(flying){
     		speed.y+=gs->gravity;
     		if(midPos.y < 0)
    @@ -665,11 +666,11 @@
     				speed+=newForce;
     				speedf = speed.Length();
     				speed *= 1 - (.1*dir.y);
    -			} else 
    +			} else
     			{
     				speed*=(speedf-speedReduction)/speedf;
     			}
    -			
    +
     			float remTime=speedf/speedReduction-1;
     			float rp=floor(skidRotPos2+skidRotSpeed2*remTime+0.5f);
     			skidRotSpeed2=(remTime+1 == 0 ) ? 0 : (rp-skidRotPos2)/(remTime+1);
    Index: Sim/Projectiles/ProjectileHandler.cpp
    ===================================================================
    --- Sim/Projectiles/ProjectileHandler.cpp	(revision 3848)
    +++ Sim/Projectiles/ProjectileHandler.cpp	(working copy)
    @@ -31,8 +31,8 @@
     
     CProjectileHandler* ph;
     using namespace std;
    -extern GLfloat FogBlack[]; 
    -extern GLfloat FogLand[]; 
    +extern GLfloat FogBlack[];
    +extern GLfloat FogLand[];
     
     CR_BIND(CProjectileHandler,);
     
    @@ -137,7 +137,7 @@
     	wrecktex            = textureAtlas->GetTextureWithBackup(  "wrecktexture",           "circularthingy"  );
     	plasmatex           = textureAtlas->GetTextureWithBackup(  "plasmatexture",          "circularthingy"  );
     
    -	
    +
     	groundFXAtlas = SAFE_NEW CTextureAtlas(2048, 2048);
     	//add all textures in groundfx section
     	ptex = resources.GetAllValues("resources\\graphics\\groundfx");
    @@ -328,9 +328,9 @@
     
     	int numFlyingPieces = 0;
     	int drawnPieces = 0;
    -	
    +
     	/* Putting in, say, viewport culling will deserve refactoring. */
    -	
    +
     	/* 3DO */
     	unitDrawer->SetupForUnitDrawing();
     
    @@ -376,27 +376,27 @@
     
     	for (int textureType = 1; textureType < flyings3oPieces.size(); textureType++){
     		/* TODO Skip this if there's no FlyingPieces. */
    -		
    +
     		texturehandler->SetS3oTexture(textureType);
    -		
    +
     		for (int team = 0; team < flyings3oPieces[textureType].size(); team++){
     			FlyingPiece_List * fpl = flyings3oPieces[textureType][team];
    -		
    +
     			unitDrawer->SetS3OTeamColour(team);
    -			
    +
     			va->Initialize();
    -			
    +
     			numFlyingPieces += fpl->size();
    -		
    +
     			for(std::list<FlyingPiece*>::iterator pi=fpl->begin();pi!=fpl->end();++pi){
     				CMatrix44f m;
     				m.Rotate((*pi)->rot,(*pi)->rotAxis);
     				float3 interPos=(*pi)->pos+(*pi)->speed*gu->timeOffset;
    -				
    +
     				SS3OVertex * verts = (*pi)->verts;
    -				
    +
     				float3 tp, tn;
    -				
    +
     				for (int i = 0; i < 4; i++){
     					tp=m.Mul(verts[i].pos);
     					tn=m.Mul(verts[i].normal);
    @@ -408,9 +408,9 @@
     			va->DrawArrayTN(GL_QUADS);
     		}
     	}
    -	
    +
     	unitDrawer->CleanUpS3ODrawing();
    -	
    +
     	/*
     	 * TODO Nearly cut here.
     	 */
    @@ -565,7 +565,7 @@
     						if(readmap->groundBlockingObjectMap[square]!=unit)
     							continue;
     					}
    -					//adjust projectile position so explosion happens at the correct position 
    +					//adjust projectile position so explosion happens at the correct position
     					p->pos = p->pos + p->speed*closeTime;
     					p->Collision(*ui);
     					break;
    @@ -603,7 +603,7 @@
     				}
     			}
     		}
    -	}	
    +	}
     }
     
     void CProjectileHandler::AddGroundFlash(CGroundFlash* flash)
    @@ -687,7 +687,7 @@
     		flyings3oPieces[textureType].push_back(fpl);
     		flyingPieces.push_back(fpl);
     	}
    -	
    +
     	pieceList=flyings3oPieces[textureType][team];
     
     	FlyingPiece* fp=new FlyingPiece;
    @@ -745,7 +745,7 @@
     	glDisable(GL_ALPHA_TEST);
     	glDisable(GL_FOG);
     
    -	unsigned char col[4];	
    +	unsigned char col[4];
     	float time=gu->lastFrameTime*gs->speedFactor*3;
     	float speed=1;
     	float size=1;
    Index: Sim/Projectiles/WeaponProjectile.cpp
    ===================================================================
    --- Sim/Projectiles/WeaponProjectile.cpp	(revision 3848)
    +++ Sim/Projectiles/WeaponProjectile.cpp	(working copy)
    @@ -45,7 +45,7 @@
     	interceptTarget=0;
     }
     
    -CWeaponProjectile::CWeaponProjectile(const float3& pos,const float3& speed,CUnit* owner, CUnit* target,const float3 &targetPos, WeaponDef *weaponDef,CWeaponProjectile* interceptTarget, bool synced) : 
    +CWeaponProjectile::CWeaponProjectile(const float3& pos,const float3& speed,CUnit* owner, CUnit* target,const float3 &targetPos, WeaponDef *weaponDef,CWeaponProjectile* interceptTarget, bool synced) :
     	CProjectile(pos,speed,owner, synced),
     	weaponDef(weaponDef),
     	weaponDefName(weaponDef?weaponDef->name:std::string("")),
    @@ -120,7 +120,7 @@
     		DamageArray dynDamages;
     		if (weaponDef->dynDamageExp > 0)
     			dynDamages = weaponDefHandler->DynamicDamages(weaponDef->damages, startpos, pos, weaponDef->dynDamageRange>0?weaponDef->dynDamageRange:weaponDef->range, weaponDef->dynDamageExp, weaponDef->dynDamageMin, weaponDef->dynDamageInverted);
    -		
    +
     		helper->Explosion(pos,weaponDef->dynDamageExp>0?dynDamages:weaponDef->damages,weaponDef->areaOfEffect,weaponDef->edgeEffectiveness,weaponDef->explosionSpeed,owner,true,weaponDef->noExplode? 0.3f:1,weaponDef->noExplode || weaponDef->noSelfDamage, weaponDef->explosionGenerator,0,impactDir, weaponDef->id);
     	}
     
    @@ -155,7 +155,7 @@
     		DamageArray dynDamages;
     		if (weaponDef->dynDamageExp > 0)
     			dynDamages = weaponDefHandler->DynamicDamages(weaponDef->damages, startpos, pos, weaponDef->dynDamageRange>0?weaponDef->dynDamageRange:weaponDef->range, weaponDef->dynDamageExp, weaponDef->dynDamageMin, weaponDef->dynDamageInverted);
    -			
    +
     		helper->Explosion(pos,weaponDef->dynDamageExp>0?dynDamages:weaponDef->damages,weaponDef->areaOfEffect,weaponDef->edgeEffectiveness,weaponDef->explosionSpeed,owner,true,weaponDef->noExplode? 0.3f:1,weaponDef->noExplode,weaponDef->explosionGenerator,unit,impactDir, weaponDef->id);
     	}
     
    @@ -226,7 +226,7 @@
     	transMatrix[12]=interPos.x;
     	transMatrix[13]=interPos.y;
     	transMatrix[14]=interPos.z;
    -	glMultMatrixf(&transMatrix[0]);		
    +	glMultMatrixf(&transMatrix[0]);
     
     	glCallList(modelDispList);
     	glPopMatrix();
    Index: Sim/Units/CommandAI/MobileCAI.cpp
    ===================================================================
    --- Sim/Units/CommandAI/MobileCAI.cpp	(revision 3848)
    +++ Sim/Units/CommandAI/MobileCAI.cpp	(working copy)
    @@ -42,6 +42,8 @@
     				CR_MEMBER(commandPos1),
     				CR_MEMBER(commandPos2),
     
    +				CR_MEMBER(lastCloseInTry),
    +
     				CR_MEMBER(cancelDistance),
     				CR_MEMBER(slowGuard),
     				CR_MEMBER(moveDir)
    @@ -61,6 +63,7 @@
     	commandPos2(ZeroVector),
     	lastPC(-1),
     	cancelDistance(1024),
    +	lastCloseInTry(-1),
     	slowGuard(false),
     	moveDir(gs->randFloat() > 0.5),
     	lastUserGoal(0,0,0)
    @@ -81,6 +84,7 @@
     	commandPos2(ZeroVector),
     	lastPC(-1),
     	cancelDistance(1024),
    +	lastCloseInTry(-1),
     	slowGuard(false),
     	moveDir(gs->randFloat() > 0.5)
     {
    @@ -582,7 +586,8 @@
     			// check if we have valid target parameter and that we aren't attacking ourselves
     			if (uh->units[unitID] != 0 && uh->units[unitID] != owner) {
     				float3 fix = uh->units[unitID]->pos + owner->posErrorVector * 128;
    -				SetGoal(fix, owner->pos);
    +				float3 diff = float3(fix - owner->pos).Normalize();
    +				SetGoal(fix - diff*uh->units[unitID]->radius, owner->pos);
     				// get ID of attack-order target unit
     				orderTarget = uh->units[unitID];
     				AddDeathDependence(orderTarget);
    @@ -626,6 +631,7 @@
     		//bool b1 = owner->AttackUnit(orderTarget, c.id == CMD_DGUN);
     		bool b2 = false;
     		bool b3 = false;
    +		float edgeFactor = 0.f; // percent offset to target center
     
     		if (owner->weapons.size() > 0) {
     			if (!(c.options & ALT_KEY) && SkipParalyzeTarget(orderTarget)) {
    @@ -638,15 +644,21 @@
     			// can hit target with our first (meanest) one
     			b2 = w->TryTargetRotate(orderTarget, c.id == CMD_DGUN);
     			b3 = (w->range - (w->relWeaponPos).Length()) > (orderTarget->pos.distance(owner->pos));
    +			edgeFactor = fabs(w->targetBorder);
     		}
     		float3 diff = owner->pos - orderTarget->pos;
     		// if w->AttackUnit() returned true then we are already
     		// in range with our biggest weapon so stop moving
    +		// also make sure that we're not locked in close-in/in-range state loop
    +		// due to rotates invoked by in-range or out-of-range states
     		if (b2) {
     			StopMove();
     			owner->AttackUnit(orderTarget, c.id == CMD_DGUN);
    -			owner->moveType->KeepPointingTo(orderTarget,
    -				min((float) (owner->losRadius * SQUARE_SIZE * 2), owner->maxRange * 0.9f), true);
    +			// FIXME kill magic frame number
    +			if (gs->frameNum > lastCloseInTry + MAX_CLOSE_IN_RETRY_TICKS) {
    +				owner->moveType->KeepPointingTo(orderTarget,
    +					min((float) (owner->losRadius * SQUARE_SIZE * 2), owner->maxRange * 0.9f), true);
    +			}
     		}
     
     		// if (((first weapon range minus first weapon length greater than distance to target
    @@ -675,7 +687,10 @@
     		else if ((orderTarget->pos + owner->posErrorVector * 128).distance2D(goalPos)
     				> (10 + orderTarget->pos.distance2D(owner->pos) * 0.2f)) {
     			float3 fix = orderTarget->pos + owner->posErrorVector * 128;
    -			SetGoal(fix, owner->pos);
    +			float3 norm = float3(fix - owner->pos).Normalize();
    +			SetGoal(fix - norm*(orderTarget->radius*edgeFactor*0.8f), owner->pos);
    +			if (lastCloseInTry < gs->frameNum + MAX_CLOSE_IN_RETRY_TICKS)
    +				lastCloseInTry = gs->frameNum;
     		}
     	}
     
    Index: Sim/Units/CommandAI/MobileCAI.h
    ===================================================================
    --- Sim/Units/CommandAI/MobileCAI.h	(revision 3848)
    +++ Sim/Units/CommandAI/MobileCAI.h	(working copy)
    @@ -59,6 +59,7 @@
     
     protected:
     	int cancelDistance;
    +	int lastCloseInTry;
     	bool slowGuard;
     	bool moveDir;
     	void PushOrUpdateReturnFight() {
    @@ -66,5 +67,6 @@
     	}
     };
     
    +#define MAX_CLOSE_IN_RETRY_TICKS 30
     
     #endif /* MOBILECAI_H */
    Index: Sim/Units/UnitLoader.cpp
    ===================================================================
    --- Sim/Units/UnitLoader.cpp	(revision 3848)
    +++ Sim/Units/UnitLoader.cpp	(working copy)
    @@ -466,6 +466,9 @@
     	weapon->fuelUsage = udw->fuelUsage;
     	weapon->avoidFriendly = weapondef->avoidFriendly;
     	weapon->avoidFeature = weapondef->avoidFeature;
    +	weapon->targetBorder = weapondef->targetBorder;
    +	weapon->cylinderTargetting = weapondef->cylinderTargetting;
    +	weapon->minIntensity = weapondef->minIntensity;
     	weapon->collisionFlags = weapondef->collisionFlags;
     	weapon->Init();
     
    Index: Sim/Weapons/BeamLaser.cpp
    ===================================================================
    --- Sim/Weapons/BeamLaser.cpp	(revision 3848)
    +++ Sim/Weapons/BeamLaser.cpp	(working copy)
    @@ -85,6 +85,7 @@
     	}
     
     	float3 dir=pos-weaponPos;
    +
     	float length=dir.Length();
     	if(length==0)
     		return true;
    @@ -133,6 +134,7 @@
     		}
     	}
     	dir+=(salvoError)*(1-owner->limExperience*0.7f);
    +
     	dir.Normalize();
     
     	FireInternal(dir, false);
    @@ -145,6 +147,7 @@
     	if(owner->directControl)
     		rangeMod=0.95f;
     #endif
    +
     	float maxLength=range*rangeMod;
     	float curLength=0;
     	float3 curPos=weaponPos;
    @@ -152,6 +155,19 @@
     
     	bool tryAgain=true;
     	CUnit* hit;
    +
    +	// increase range if targets are searched for in a cylinder
    +	if (cylinderTargetting > 0.01) {
    +		const float3 up(0, owner->radius*cylinderTargetting, 0);
    +		const float uplen = up.dot(dir);
    +		maxLength = sqrt(maxLength*maxLength + uplen*uplen);
    +	}
    +
    +	// increase range if targetting edge of hitsphere
    +	if (targetType == Target_Unit && targetUnit && targetBorder != 0) {
    +		maxLength += targetUnit->radius*targetBorder;
    +	}
    +
     	for(int tries=0;tries<5 && tryAgain;++tries){
     		tryAgain=false;
     		hit=0;
    @@ -172,6 +188,7 @@
     				tryAgain=true;
     			}
     		}
    +
     		hitPos=curPos+dir*length;
     
     		float baseAlpha=weaponDef->intensity*255;
    @@ -187,7 +204,15 @@
     		curLength+=length;
     		dir=newDir;
     	}
    -	float	intensity=1-(curLength)/(range*2);
    +
    +	// fix negative damage when hitting big spheres
    +	float actualRange = range;
    +	if (hit && targetBorder > 0) {
    +		actualRange += hit->radius*targetBorder;
    +	}
    +	// make it possible to always hit with some minimal intensity (melee weapons have use for that)
    +	float intensity=max(minIntensity, 1-(curLength)/(actualRange*2));
    +
     	if(curLength<maxLength) {
     		// Dynamic Damage
     		DamageArray dynDamages;
    Index: Sim/Weapons/MeleeWeapon.cpp
    ===================================================================
    --- Sim/Weapons/MeleeWeapon.cpp	(revision 3848)
    +++ Sim/Weapons/MeleeWeapon.cpp	(working copy)
    @@ -34,7 +34,9 @@
     void CMeleeWeapon::Fire(void)
     {
     	if(targetType==Target_Unit){
    -		targetUnit->DoDamage(damages,owner,ZeroVector,weaponDef->id);
    +		float3 impulseDir = targetUnit->pos-weaponPos;
    +		impulseDir.Normalize();
    +		targetUnit->DoDamage(damages,owner,impulseDir,weaponDef->id);
     		if(fireSoundId)
     			sound->PlaySample(fireSoundId,owner,fireSoundVolume);
     	}
    Index: Sim/Weapons/Weapon.cpp
    ===================================================================
    --- Sim/Weapons/Weapon.cpp	(revision 3848)
    +++ Sim/Weapons/Weapon.cpp	(working copy)
    @@ -83,6 +83,9 @@
     	CR_MEMBER(hasCloseTarget),
     	CR_MEMBER(avoidFriendly),
     	CR_MEMBER(avoidFeature),
    +	CR_MEMBER(targetBorder),
    +	CR_MEMBER(cylinderTargetting),
    +	CR_MEMBER(minIntensity),
     	CR_MEMBER(collisionFlags),
     	CR_MEMBER(fuelUsage),
     	CR_MEMBER(weaponNum)
    @@ -151,6 +154,9 @@
     	hasCloseTarget(false),
     	avoidFriendly(true),
     	avoidFeature(true),
    +	targetBorder(0.f),
    +	cylinderTargetting(0.f),
    +	minIntensity(0.f),
     	collisionFlags(0),
     	fuelUsage(0)
     {
    @@ -164,6 +170,12 @@
     
     void CWeapon::Update()
     {
    +	// do not fire at cloaked units
    +	if(targetType == Target_Unit && targetUnit && targetUnit->isCloaked) {
    +		HoldFire();
    +		return;
    +	}
    +
     	if(hasCloseTarget){
     		std::vector<int> args;
     		args.push_back(0);
    @@ -544,7 +556,31 @@
     
     	float3 dif=pos-weaponPos;
     
    -	float r=GetRange2D(owner->pos.y-pos.y);
    +	if (targetBorder != 0 && unit) {
    +		float3 diff(dif);
    +		diff.Normalize();
    +		// weapon inside target sphere
    +		if (dif.SqLength() < unit->sqRadius*targetBorder*targetBorder) {
    +			dif -= diff*(dif.Length() - 10); // a hack
    +			//logOutput << "inside\n";
    +		} else {
    +			dif -= diff*(unit->radius*targetBorder);
    +			//logOutput << "outside\n";
    +		}
    +		//geometricObjects->AddLine(weaponMuzzlePos, weaponMuzzlePos+dif, 3, 0, 16);
    +	}
    +
    +	float r;
    +	if (!unit || cylinderTargetting < 0.01) {
    +		r=GetRange2D(owner->pos.y-pos.y);
    +	} else {
    +		if (cylinderTargetting * unit->radius > owner->pos.y-pos.y) {
    +			r = GetRange2D(0);
    +		} else {
    +			r = 0;
    +		}
    +	}
    +
     	if(dif.SqLength2D()>=r*r)
     		return false;
     
    Index: Sim/Weapons/Weapon.h
    ===================================================================
    --- Sim/Weapons/Weapon.h	(revision 3848)
    +++ Sim/Weapons/Weapon.h	(working copy)
    @@ -119,12 +119,17 @@
     	int lastErrorVectorUpdate;
     
     	CWeapon* slavedTo;						//use this weapon to choose target
    -	
    +
     	float3 mainDir;								//main aim dir of weapon
     	float maxMainDirAngleDif;					//how far away from main aim dir the weapon can aim at something (as an acos value)
     
     	bool avoidFriendly;		//if true tried to avoid friendly Units when aiming.
     	bool avoidFeature;      		//if true try to avoid Features while aiming.
    +
    +	float targetBorder;  // if nonzero, targetting units will TryTarget at the edge of collision sphere (radius*tag value, [-1;1]) instead of its centre
    +	float cylinderTargetting;	//if greater than 0, range will be checked in a cylinder (height=unitradius*cylinderTargetting) instead of a sphere
    +	float minIntensity;	// for beamlasers - always hit with some minimum intensity (a damage coeffcient normally dependent on distance). do not confuse with intensity tag, it's completely unrelated.
    +
     	unsigned int collisionFlags;
     
     	float fuelUsage;
    Index: Sim/Weapons/WeaponDefHandler.cpp
    ===================================================================
    --- Sim/Weapons/WeaponDefHandler.cpp	(revision 3848)
    +++ Sim/Weapons/WeaponDefHandler.cpp	(working copy)
    @@ -91,6 +91,8 @@
     	if(!collideFeature)
     		weaponDefs[id].collisionFlags+=COLLISION_NOFEATURE;
     
    +	sunparser->GetDef(weaponDefs[id].minIntensity, "0", weaponname + "\\MinIntensity");
    +
     	sunparser->GetDef(weaponDefs[id].dropped, "0", weaponname + "\\dropped");
     	sunparser->GetDef(lineofsight, "0", weaponname + "\\lineofsight");
     	sunparser->GetDef(ballistic, "0", weaponname + "\\ballistic");
    @@ -119,7 +121,7 @@
     	sunparser->GetDef(weaponDefs[id].laserflaresize, "15", weaponname + "\\laserflaresize");
     	sunparser->GetDef(weaponDefs[id].intensity, "0.9", weaponname + "\\intensity");
     	sunparser->GetDef(weaponDefs[id].duration, "0.05", weaponname + "\\duration");
    -	
    +
     	sunparser->GetDef(weaponDefs[id].visuals.sizeDecay,  "0", weaponname + "\\sizeDecay");
     	sunparser->GetDef(weaponDefs[id].visuals.alphaDecay, "1", weaponname + "\\alphaDecay");
     	sunparser->GetDef(weaponDefs[id].visuals.separation, "1", weaponname + "\\separation");
    @@ -188,6 +190,15 @@
     
     //	logOutput.Print("%s as %s",weaponname.c_str(),weaponDefs[id].type.c_str());
     
    +	sunparser->GetDef(weaponDefs[id].targetBorder, (weaponDefs[id].type == "Melee"?"1":"0"), weaponname + "\\TargetBorder");
    +	if (weaponDefs[id].targetBorder > 1.f) {
    +		logOutput.Print("warning: targetBorder truncated to 1 (was %f)", weaponDefs[id].targetBorder);
    +		weaponDefs[id].targetBorder = 1.f;
    +	} else if (weaponDefs[id].targetBorder < -1.f) {
    +		logOutput.Print("warning: targetBorder truncated to -1 (was %f)", weaponDefs[id].targetBorder);
    +		weaponDefs[id].targetBorder = -1.f;
    +	}
    +	sunparser->GetDef(weaponDefs[id].cylinderTargetting, (weaponDefs[id].type == "Melee"?"1":"0"), weaponname + "\\CylinderTargetting");
     
     
     	weaponDefs[id].range = atof(sunparser->SGetValueDef("10", weaponname + "\\range").c_str());
    @@ -576,7 +587,7 @@
     
     	if (inverted == true) {
     		for(int i=0; i < damageArrayHandler->numTypes; ++i) {
    -			
    +
     			dynDamages[i] = damages[i] - (1 - pow(1 / range * travDist, exp)) * damages[i];
     
     			if (damageMin > 0) {
    @@ -591,7 +602,7 @@
     	}
     	else {
     		for(int i=0; i < damageArrayHandler->numTypes; ++i) {
    -			
    +
     			dynDamages[i] = (1 - pow(1 / range * travDist, exp)) * damages[i];
     
     			if (damageMin > 0) {
    Index: Sim/Weapons/WeaponDefHandler.h
    ===================================================================
    --- Sim/Weapons/WeaponDefHandler.h	(revision 3848)
    +++ Sim/Weapons/WeaponDefHandler.h	(working copy)
    @@ -86,7 +86,7 @@
     	bool manualfire;			//use dgun button
     	int interceptor;				//anti nuke
     	int targetable;				//nuke (can be shot by interceptor)
    -	bool stockpile;					
    +	bool stockpile;
     	float coverageRange;		//range of anti nuke
     
     	float intensity;
    @@ -138,7 +138,7 @@
     		float tilelength;
     		float scrollspeed;
     		float pulseSpeed;
    -		
    +
     		int stages;
     		float alphaDecay;
     		float sizeDecay;
    @@ -172,6 +172,11 @@
     
     	bool avoidFriendly;		//if true try to avoid friendly Units when aiming.
     	bool avoidFeature;      //if true try to avoid Features while aiming.
    +
    +	float targetBorder;		//if nonzero, targetting units will TryTarget at the edge of collision sphere (radius*tag value, [-1;1]) instead of its centre
    +	float cylinderTargetting;	//if greater than 0, range will be checked in a cylinder (height=unitradius*cylinderTargetting) instead of a sphere
    +	float minIntensity;		// for beamlasers - always hit with some minimum intensity (a damage coeffcient normally dependent on distance). do not confuse with intensity tag, it's completely unrelated.
    +
     	unsigned int collisionFlags;
     
     	CExplosionGenerator *explosionGenerator; // can be zero for default explosions
    

-Relationships
+Relationships

-Notes

~0000968

imbaczek (reporter)

patch v2, allows for simple melee combat.

changelog:


all files:
- removed trailing whitespace

ProjectileHandler.cpp
WeaponProjectile.cpp
- just whitespace

GameHelper.cpp:
- fixed raytrace code (apparently GUI raytrace made it into targetting raytrace)

LuaWeaponDefs.cpp
UnitLoader.cpp
- added tags: targetBorder, cylinderTargetting, minIntensity

MobileCAI.{cpp,h}
- added lastCloseInTry (in ticks)
- made units aware of targetBorder

BeamLaser.cpp
Weapon.h
- added minIntensity tag
- made cylinderTargetting and targetBorder to increase ray length
- KDR's weaponMuzzlePos patch - tested to work with beamlasers

Weapon.cpp
Weapon.h
WeaponDefHandler.cpp
WeaponDefHandler.h
- KDR's weaponMuzzlePos patch - tested to work with beamlasers
- KDR's COB AimFromPrimary/QueryPrimary changes
- do not fire at cloaked units
- TryTarget made to respect targetBorder and cylinderTargetting
- added tags:
        * float targetBorder - if nonzero, targetting units will TryTarget at
          the edge of collision sphere (radius*tag_value, [-1;1]) instead of
          its centre
        * float cylinderTargetting - if greater than 0, range will be checked
          in a cylinder (half height=unitradius*cylinderTargetting) instead of
          a sphere
        * float minIntensity - for beamlasers - always hit with some minimum
          intensity (a damage coeffcient normally dependent on distance). do
          not confuse with intensity tag, it's completely unrelated.

~0000974

tvo (reporter)

So targetborder_minintensity_cylinderTargetting_and_weaponMuzzlePos.diff replaces targetBorder.diff?

Could you factor out KDR_11k's patch (reverse them maybe?), because he is working on a better one I think so having yours together with an old one of KDR would give conflicts with his newer patch...

~0000975

imbaczek (reporter)

Yup, the bigger one supersedes the smaller one.

I'll see what I can do about KDR's patch.

~0000977

imbaczek (reporter)

uploaded a patch without KDR's modifications.

updated changelog:

all files:
- removed trailing whitespace

ProjectileHandler.cpp
WeaponProjectile.cpp
- just whitespace

GameHelper.cpp:
- fixed raytrace code (apparently GUI raytrace made it into targetting raytrace)

LuaWeaponDefs.cpp
UnitLoader.cpp
- added tags: targetBorder, cylinderTargetting, minIntensity

MobileCAI.{cpp,h}
- added lastCloseInTry (in ticks)
- made units aware of targetBorder

BeamLaser.cpp
Weapon.h
- added minIntensity tag
- made cylinderTargetting and targetBorder to increase ray length

Weapon.cpp
Weapon.h
WeaponDefHandler.cpp
WeaponDefHandler.h
- do not fire at cloaked units
- TryTarget made to respect targetBorder and cylinderTargetting
- added tags:
        * float targetBorder - if nonzero, targetting units will TryTarget at
          the edge of collision sphere (radius*tag_value, [-1;1]) instead of
          its centre
        * float cylinderTargetting - if greater than 0, range will be checked
          in a cylinder (half height=unitradius*cylinderTargetting) instead of
          a sphere
        * float minIntensity - for beamlasers - always hit with some minimum
          intensity (a damage coeffcient normally dependent on distance). do
          not confuse with intensity tag, it's completely unrelated.

MeleeWeapon.cpp
- added impulseDir

~0000978

KDR_11k (reporter)

Small suggestion: Can you make targetBorder default to 1 for the Melee weapon?

~0000983

imbaczek (reporter)

done. updated changelog:

all files:
- removed trailing whitespace

ProjectileHandler.cpp
WeaponProjectile.cpp
- just whitespace

GameHelper.cpp:
- fixed raytrace code (apparently GUI raytrace made it into targetting raytrace)

LuaWeaponDefs.cpp
UnitLoader.cpp
- added tags: targetBorder, cylinderTargetting, minIntensity

MobileCAI.{cpp,h}
- added lastCloseInTry (in ticks)
- made units aware of targetBorder

BeamLaser.cpp
Weapon.h
- added minIntensity tag
- made cylinderTargetting and targetBorder to increase ray length

Weapon.cpp
Weapon.h
WeaponDefHandler.cpp
WeaponDefHandler.h
- do not fire at cloaked units
- TryTarget made to respect targetBorder and cylinderTargetting
- added tags:
        * float targetBorder - if nonzero, targetting units will TryTarget at
          the edge of collision sphere (radius*tag_value, [-1;1]) instead of
          its centre
        * float cylinderTargetting - if greater than 0, range will be checked
          in a cylinder (half height=unitradius*cylinderTargetting) instead of
          a sphere
        * float minIntensity - for beamlasers - always hit with some minimum
          intensity (a damage coeffcient normally dependent on distance). do
          not confuse with intensity tag, it's completely unrelated.
- if weaponType==Melee, then defaults for targetBorder and cylinderTargetting
  are 1 instead of 0.

MeleeWeapon.cpp
- added impulseDir

~0000985

tvo (reporter)

Committed, r3861, except for the cloakable units bit, since that's 1) another bug and 2) works fine already, I think (I will elaborate in the other bug)

Thanks for your contribution!
+Notes

-Issue History
Date Modified Username Field Change
2007-06-17 15:05 imbaczek New Issue
2007-06-17 15:05 imbaczek File Added: targetBorder.diff
2007-06-22 19:01 imbaczek Note Added: 0000968
2007-06-22 19:01 imbaczek File Added: targetborder_minintensity_cylinderTargetting_and_weaponMuzzlePos.diff
2007-06-23 22:39 tvo Note Added: 0000974
2007-06-23 23:08 imbaczek Note Added: 0000975
2007-06-24 00:40 imbaczek File Added: targetborder_minintensity_cylinderTargetting_and_weaponMuzzlePos_v2.patch
2007-06-24 00:41 imbaczek Note Added: 0000977
2007-06-24 11:05 KDR_11k Note Added: 0000978
2007-06-25 16:27 imbaczek File Added: targetborder_minintensity_cylinderTargetting_and_weaponMuzzlePos_v3.patch
2007-06-25 16:27 imbaczek Note Added: 0000983
2007-06-26 21:47 tvo Status new => assigned
2007-06-26 21:47 tvo Assigned To => tvo
2007-06-26 21:48 tvo Status assigned => resolved
2007-06-26 21:48 tvo Resolution open => fixed
2007-06-26 21:48 tvo Note Added: 0000985
+Issue History