Page MenuHomeWildfire Games

Add status effects support to splash, melee and death damage.
AbandonedPublic

Authored by Freagarach on Jul 15 2019, 5:54 PM.

Details

Reviewers
bb
Group Reviewers
Restricted Owners Package(Owns No Changed Paths)
Summary

This adds status effects support to splash, melee and death damage.

Status effects were introduced in rP22304/D1252, but support for other attack types than ranged was not implemented. Here the code for is moved to cause damage, so there is less duplication and any script causing damage can also use status effects.

Test Plan

Test that nothing is overlooked.

Diff Detail

Repository
rP 0 A.D. Public Repository
Branch
/ps/trunk
Lint
Lint OK
Unit
No Unit Test Coverage
Build Status
Buildable 8598
Build 14079: Vulcan BuildJenkins
Build 14078: arc lint + arc unit

Event Timeline

Successful build - Chance fights ever on the side of the prudent.

Linter detected issues:
Executing section Source...
Executing section JS...
|    | [NORMAL] ESLintBear (indent):
|    | Expected indentation of 2 tabs but found 3.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
|  23|  23| 	let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|  24|  24| 	let turnLength = cmpTimer.GetLatestTurnLength();
|  25|  25| 	return new Vector3D(
|  26|    |-			(curPos.x * (turnLength - lateness) + prevPos.x * lateness) / turnLength,
|    |  26|+		(curPos.x * (turnLength - lateness) + prevPos.x * lateness) / turnLength,
|  27|  27| 			0,
|  28|  28| 			(curPos.z * (turnLength - lateness) + prevPos.z * lateness) / turnLength);
|  29|  29| };
|    | [NORMAL] ESLintBear (indent):
|    | Expected indentation of 2 tabs but found 3.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
|  24|  24| 	let turnLength = cmpTimer.GetLatestTurnLength();
|  25|  25| 	return new Vector3D(
|  26|  26| 			(curPos.x * (turnLength - lateness) + prevPos.x * lateness) / turnLength,
|  27|    |-			0,
|    |  27|+		0,
|  28|  28| 			(curPos.z * (turnLength - lateness) + prevPos.z * lateness) / turnLength);
|  29|  29| };
|  30|  30| 
|    | [NORMAL] ESLintBear (indent):
|    | Expected indentation of 2 tabs but found 3.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
|  25|  25| 	return new Vector3D(
|  26|  26| 			(curPos.x * (turnLength - lateness) + prevPos.x * lateness) / turnLength,
|  27|  27| 			0,
|  28|    |-			(curPos.z * (turnLength - lateness) + prevPos.z * lateness) / turnLength);
|    |  28|+		(curPos.z * (turnLength - lateness) + prevPos.z * lateness) / turnLength);
|  29|  29| };
|  30|  30| 
|  31|  31| /**
|    | [NORMAL] ESLintBear (curly):
|    | Unnecessary { after 'if' condition.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
| 113| 113| 
| 114| 114| 	// Do this first in case the direct hit kills the target
| 115| 115| 	if (data.isSplash)
| 116|    |-	{
|    | 116|+	
| 117| 117| 		this.CauseSplashDamage({
| 118| 118| 			"attacker": data.attacker,
| 119| 119| 			"origin": Vector2D.from3D(data.position),
| 128| 128| 			"restrictedClasses": data.restrictedClasses,
| 129| 129| 			"statusEffects": data.statusEffects
| 130| 130| 		});
| 131|    |-	}
|    | 131|+	
| 132| 132| 
| 133| 133| 	let cmpProjectileManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_ProjectileManager);
| 134| 134| 
|    | [NORMAL] ESLintBear (semi):
|    | Missing semicolon.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
| 217| 217| 		}
| 218| 218| 
| 219| 219| 		let entityPosition;
| 220|    |-		let cmpPosition = Engine.QueryInterface(ent, IID_Position)
|    | 220|+		let cmpPosition = Engine.QueryInterface(ent, IID_Position);
| 221| 221| 		if (cmpPosition)
| 222| 222| 			entityPosition = cmpPosition.GetPosition2D();
| 223| 223| 
|    | [NORMAL] ESLintBear (curly):
|    | Unnecessary { after 'else'.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
| 245| 245| 				damageMultiplier = 0;
| 246| 246| 		}
| 247| 247| 		else // In case someone calls this function with an invalid shape.
| 248|    |-		{
|    | 248|+		
| 249| 249| 			warn("The " + data.shape + " splash damage shape is not implemented!");
| 250|    |-		}
|    | 250|+		
| 251| 251| 
| 252| 252| 		if (data.splashBonus)
| 253| 253| 			damageMultiplier *= GetDamageBonus(data.attacker, ent, data.type, data.splashBonus);

binaries/data/mods/public/simulation/components/Damage.js
| 220| »   »   let·cmpPosition·=·Engine.QueryInterface(ent,·IID_Position)
|    | [NORMAL] JSHintBear:
|    | Missing semicolon.
|    | [NORMAL] ESLintBear (quote-props):
|    | Unquoted property 'turnLength' found.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
|  25|  25| 
|  26|  26| 	let cmpDamage = ConstructComponent(SYSTEM_ENTITY, "Damage");
|  27|  27| 	let cmpTimer = ConstructComponent(SYSTEM_ENTITY, "Timer");
|  28|    |-	cmpTimer.OnUpdate({ turnLength: 1 });
|    |  28|+	cmpTimer.OnUpdate({ "turnLength": 1 });
|  29|  29| 	let attacker = 11;
|  30|  30| 	let atkPlayerEntity = 1;
|  31|  31| 	let attackerOwner = 6;
|    | [NORMAL] ESLintBear (comma-spacing):
|    | A space is required after ','.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
|  63|  63| 		"position": targetPos,
|  64|  64| 		"isSplash": false,
|  65|  65| 		"projectileId": 9,
|  66|    |-		"direction": new Vector3D(1,0,0)
|    |  66|+		"direction": new Vector3D(1, 0,0)
|  67|  67| 	};
|  68|  68| 
|  69|  69| 	AddMock(atkPlayerEntity, IID_Player, {
|    | [NORMAL] ESLintBear (comma-spacing):
|    | A space is required after ','.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
|  63|  63| 		"position": targetPos,
|  64|  64| 		"isSplash": false,
|  65|  65| 		"projectileId": 9,
|  66|    |-		"direction": new Vector3D(1,0,0)
|    |  66|+		"direction": new Vector3D(1,0, 0)
|  67|  67| 	};
|  68|  68| 
|  69|  69| 	AddMock(atkPlayerEntity, IID_Player, {
|    | [NORMAL] ESLintBear (quote-props):
|    | Unquoted property 'turnLength' found.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
| 114| 114| 
| 115| 115| 	function TestDamage()
| 116| 116| 	{
| 117|    |-		cmpTimer.OnUpdate({ turnLength: 1 });
|    | 117|+		cmpTimer.OnUpdate({ "turnLength": 1 });
| 118| 118| 		TS_ASSERT(damageTaken);
| 119| 119| 		damageTaken = false;
| 120| 120| 	}
|    | [NORMAL] ESLintBear (key-spacing):
|    | Extra space after key 'hack'.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
| 156| 156| 		"origin": origin,
| 157| 157| 		"radius": 10,
| 158| 158| 		"shape": "Linear",
| 159|    |-		"strengths": { "hack" : 100, "pierce" : 0, "crush": 0 },
|    | 159|+		"strengths": { "hack": 100, "pierce" : 0, "crush": 0 },
| 160| 160| 		"direction": new Vector3D(1, 747, 0),
| 161| 161| 		"playersToDamage": [2],
| 162| 162| 		"type": "Ranged",
|    | [NORMAL] ESLintBear (key-spacing):
|    | Extra space after key 'pierce'.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
| 156| 156| 		"origin": origin,
| 157| 157| 		"radius": 10,
| 158| 158| 		"shape": "Linear",
| 159|    |-		"strengths": { "hack" : 100, "pierce" : 0, "crush": 0 },
|    | 159|+		"strengths": { "hack" : 100, "pierce": 0, "crush": 0 },
| 160| 160| 		"direction": new Vector3D(1, 747, 0),
| 161| 161| 		"playersToDamage": [2],
| 162| 162| 		"type": "Ranged",
|    | [NORMAL] ESLintBear (comma-spacing):
|    | A space is required after ','.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
| 164| 164| 		"restrictedClasses": []
| 165| 165| 	};
| 166| 166| 
| 167|    |-	let fallOff = function(x,y)
|    | 167|+	let fallOff = function(x, y)
| 168| 168| 	{
| 169| 169| 		return (1 - x * x / (data.radius * data.radius)) * (1 - 25 * y * y / (data.radius * data.radius));
| 170| 170| 	};
|    | [NORMAL] ESLintBear (key-spacing):
|    | Extra space after key 'hack'.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
| 316| 316| 		"origin": new Vector2D(3, 4),
| 317| 317| 		"radius": radius,
| 318| 318| 		"shape": "Circular",
| 319|    |-		"strengths": { "hack" : 100, "pierce" : 0, "crush": 0 },
|    | 319|+		"strengths": { "hack": 100, "pierce" : 0, "crush": 0 },
| 320| 320| 		"playersToDamage": [2],
| 321| 321| 		"type": "Ranged",
| 322| 322| 		"attackerOwner": 1,
|    | [NORMAL] ESLintBear (key-spacing):
|    | Extra space after key 'pierce'.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/tests/test_Damage.js
| 316| 316| 		"origin": new Vector2D(3, 4),
| 317| 317| 		"radius": radius,
| 318| 318| 		"shape": "Circular",
| 319|    |-		"strengths": { "hack" : 100, "pierce" : 0, "crush": 0 },
|    | 319|+		"strengths": { "hack" : 100, "pierce": 0, "crush": 0 },
| 320| 320| 		"playersToDamage": [2],
| 321| 321| 		"type": "Ranged",
| 322| 322| 		"attackerOwner": 1,

binaries/data/mods/public/simulation/components/tests/test_Damage.js
| 125| »   type·=·data.type·=·"Ranged";
|    | [NORMAL] ESLintBear (no-multi-assign):
|    | Unexpected chained assignment.
|    | [NORMAL] ESLintBear (spaced-comment):
|    | Expected space or tab after '//' in comment.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/Attack.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/Attack.js
| 532| 532| 
| 533| 533| 		let horizSpeed = +this.template[type].Projectile.Speed;
| 534| 534| 		let gravity = +this.template[type].Projectile.Gravity;
| 535|    |-		//horizSpeed /= 2; gravity /= 2; // slow it down for testing
|    | 535|+		// horizSpeed /= 2; gravity /= 2; // slow it down for testing
| 536| 536| 
| 537| 537| 		let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
| 538| 538| 		if (!cmpPosition || !cmpPosition.IsInWorld())
|    | [NORMAL] ESLintBear (no-trailing-spaces):
|    | Trailing spaces not allowed.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/Attack.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/Attack.js
| 579| 579| 		// TODO: Use unit rotation to implement x/z offsets.
| 580| 580| 		let deltaLaunchPoint = new Vector3D(0, this.template[type].Projectile.LaunchPoint["@y"], 0.0);
| 581| 581| 		let launchPoint = Vector3D.add(selfPosition, deltaLaunchPoint);
| 582|    |-		
|    | 582|+
| 583| 583| 		let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
| 584| 584| 		if (cmpVisual)
| 585| 585| 		{
|    | [NORMAL] ESLintBear (curly):
|    | Unnecessary { after 'else'.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/Attack.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/Attack.js
| 652| 652| 			});
| 653| 653| 	}
| 654| 654| 	else
| 655|    |-	{
|    | 655|+	
| 656| 656| 		// Melee attack - hurt the target immediately
| 657| 657| 		cmpDamage.CauseDamage({
| 658| 658| 			"strengths": this.GetAttackStrengths(type),
| 662| 662| 			"type": type,
| 663| 663| 			"attackerOwner": attackerOwner
| 664| 664| 		});
| 665|    |-	}
|    | 665|+	
| 666| 666| };
| 667| 667| 
| 668| 668| /**

binaries/data/mods/public/simulation/components/Attack.js
| 522| ·»   let·cmpDamage·=·Engine.QueryInterface(SYSTEM_ENTITY,·IID_Damage);
|    | [NORMAL] ESLintBear (no-mixed-spaces-and-tabs):
|    | Mixed spaces and tabs.

binaries/data/mods/public/simulation/components/Attack.js
| 627| »   »   cmpTimer.SetTimeout(SYSTEM_ENTITY,·IID_Damage,·"MissileHit",·timeToTarget·*·1000·+·+this.template[type].Delay,·data);
|    | [NORMAL] JSHintBear:
|    | Confusing plusses.
Executing section cli...

Link to build: https://jenkins.wildfiregames.com/job/docker-differential/84/display/redirect

Freagarach edited the summary of this revision. (Show Details)Jul 15 2019, 7:23 PM
bb requested changes to this revision.Jul 17 2019, 2:04 PM
bb added a subscriber: bb.

In the related ticket #1144, it was stated (first comment, by non-team member) that although a unit is not allowed to attack a specific entity, it *should* be able to damage it

You mean https://trac.wildfiregames.com/ticket/1144#comment:1 ? I read more in that that a unit could be restricted to attack another unit with a certain attacktype, however it still can attack with another attackType (i.e it can't use Capture but can use Melee)

I guess the code for that could/should(?) just be moved to cause damage, so there is less duplication and scripts causing damage can also use status effects?)

Thanks for pointing me at this, will add it to the secondary attack patch, probably the handling code here can already be moved to causeDamage

binaries/data/mods/public/simulation/components/Damage.js
129

ahm no, IMO the statusEffect of the projectile and of the splash could be different, so rather add the statusEffects schema to the splash object too and pass data.splashStatusEffects here. One could think of doing the same for restrictedClasses, no strong opinion on that

155

generally we don't have periods on these inline comments, just the jsDOCs have them

197

This crashes if restrictedClasses is not defined, as is the case for DeathDamage,so add restricted classes there too (tests too). Also one could make restrictedClasses optional and check for data.restrictedClasses && data.restrictedClasses.length

DeathDamage might want some statusEffects too

206

semicolon

207

I guess this check wasn't there since this.EntitiesNearPoint will only return entities which have a position and that position is in world. Either remove the check again or also check for IsInWorld

This revision now requires changes to proceed.Jul 17 2019, 2:04 PM

I guess the code for that could/should(?) just be moved to cause damage, so there is less duplication and scripts causing damage can also use status effects?)

Thanks for pointing me at this, will add it to the secondary attack patch, probably the handling code here can already be moved to causeDamage

The intent was that damage was just a subtype of status effects, so I don't think moving it to Damage.js or CauseDamage works. Inflicting a status effect is an 'attack effect', just as much as damage or capturing are (or, as bb said on D268, an hypothetical 'stun' damage).

Also I'd rather not have a megapatch but rather several smaller patches.

My preferred fix for this would be to introduce

Attack.prototype.AttackEffects = 
  "<oneOrMore>" +
    "<element name='Damage'>" + DamageTypes.BuildSchema("damage strength") + "</element>" +
    "<element name='Capture' a:help='Capture points value'><ref name='nonNegativeDecimal'/></element>" +
    Attack.StatusEffectsSchema
  "</oneOrMore>";

And add that to all attack types, and all splash damage. Then we just need to handle these everywhere we have an attack-causing-effects, and we're good to go.
wraitii added inline comments.Jul 17 2019, 2:26 PM
binaries/data/mods/public/simulation/components/Damage.js
155

I've been told repeatedly to add '.' at the end of my single-line comments these past few years... It seems there's some disagreement on that.

bb added inline comments.Jul 17 2019, 2:28 PM
binaries/data/mods/public/simulation/components/Damage.js
155

Hmm lets set it in stone in the next staff meeting and add it to the coding convention

I might give a shot at commandeering this in a more general way tonight.

I might give a shot at commandeering this in a more general way tonight.

Feel free!

Didn't commandeer after all because this only covers status effects, but see D2092

I'm not sure how to handle restricted classes, are they a property of the attack or should they also be passed to the projectile for example? So if an arrow misses and hits a wall, it still doesn't do damage (when it would right now I think).

Angen added a subscriber: Angen.Jul 23 2019, 1:25 PM

I do not fully agree with restricted classes for splash damage. As was said restricted classes and preferred classes are something for unitai and petra to choose better target and do not waist attack at something it should not attack.
If there will be in future real time detection for projectiles collision, the fact if that projectile or splash damage, death damage will deal some damage to the given entity should be based on damage type vs armour type and not restricted classes. But this is just my personal opinion. :)

Freagarach marked 3 inline comments as done.Jul 23 2019, 2:55 PM
In D2081#88375, @Angen wrote:

I do not fully agree with restricted classes for splash damage. As was said restricted classes and preferred classes are something for unitai and petra to choose better target and do not waist attack at something it should not attack.
If there will be in future real time detection for projectiles collision, the fact if that projectile or splash damage, death damage will deal some damage to the given entity should be based on damage type vs armour type and not restricted classes. But this is just my personal opinion. :)

Which I fully agree with ;)

Freagarach updated this revision to Diff 9121.Thu, Jul 25, 7:51 PM
Freagarach marked 4 inline comments as done.
Freagarach retitled this revision from Add restricted classes and status effects support to splash damage. to Add status effects support to splash, melee and death damage..
Freagarach edited the summary of this revision. (Show Details)
Freagarach edited the test plan for this revision. (Show Details)

Successful build - Chance fights ever on the side of the prudent.

Linter detected issues:
Executing section Source...
Executing section JS...
|    | [NORMAL] ESLintBear (indent):
|    | Expected indentation of 2 tabs but found 3.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
|  23|  23| 	let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
|  24|  24| 	let turnLength = cmpTimer.GetLatestTurnLength();
|  25|  25| 	return new Vector3D(
|  26|    |-			(curPos.x * (turnLength - lateness) + prevPos.x * lateness) / turnLength,
|    |  26|+		(curPos.x * (turnLength - lateness) + prevPos.x * lateness) / turnLength,
|  27|  27| 			0,
|  28|  28| 			(curPos.z * (turnLength - lateness) + prevPos.z * lateness) / turnLength);
|  29|  29| };
|    | [NORMAL] ESLintBear (indent):
|    | Expected indentation of 2 tabs but found 3.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
|  24|  24| 	let turnLength = cmpTimer.GetLatestTurnLength();
|  25|  25| 	return new Vector3D(
|  26|  26| 			(curPos.x * (turnLength - lateness) + prevPos.x * lateness) / turnLength,
|  27|    |-			0,
|    |  27|+		0,
|  28|  28| 			(curPos.z * (turnLength - lateness) + prevPos.z * lateness) / turnLength);
|  29|  29| };
|  30|  30| 
|    | [NORMAL] ESLintBear (indent):
|    | Expected indentation of 2 tabs but found 3.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/Damage.js
|  25|  25| 	return new Vector3D(
|  26|  26| 			(curPos.x * (turnLength - lateness) + prevPos.x * lateness) / turnLength,
|  27|  27| 			0,
|  28|    |-			(curPos.z * (turnLength - lateness) + prevPos.z * lateness) / turnLength);
|    |  28|+		(curPos.z * (turnLength - lateness) + prevPos.z * lateness) / turnLength);
|  29|  29| };
|  30|  30| 
|  31|  31| /**
|    | [NORMAL] ESLintBear (spaced-comment):
|    | Expected space or tab after '//' in comment.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/Attack.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/Attack.js
| 534| 534| 
| 535| 535| 		let horizSpeed = +this.template[type].Projectile.Speed;
| 536| 536| 		let gravity = +this.template[type].Projectile.Gravity;
| 537|    |-		//horizSpeed /= 2; gravity /= 2; // slow it down for testing
|    | 537|+		// horizSpeed /= 2; gravity /= 2; // slow it down for testing
| 538| 538| 
| 539| 539| 		let cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
| 540| 540| 		if (!cmpPosition || !cmpPosition.IsInWorld())
|    | [NORMAL] ESLintBear (no-trailing-spaces):
|    | Trailing spaces not allowed.
|----|    | /zpool0/trunk/binaries/data/mods/public/simulation/components/Attack.js
|    |++++| /zpool0/trunk/binaries/data/mods/public/simulation/components/Attack.js
| 581| 581| 		// TODO: Use unit rotation to implement x/z offsets.
| 582| 582| 		let deltaLaunchPoint = new Vector3D(0, this.template[type].Projectile.LaunchPoint["@y"], 0.0);
| 583| 583| 		let launchPoint = Vector3D.add(selfPosition, deltaLaunchPoint);
| 584|    |-		
|    | 584|+
| 585| 585| 		let cmpVisual = Engine.QueryInterface(this.entity, IID_Visual);
| 586| 586| 		if (cmpVisual)
| 587| 587| 		{

binaries/data/mods/public/simulation/components/Attack.js
| 524| ·»   let·cmpDamage·=·Engine.QueryInterface(SYSTEM_ENTITY,·IID_Damage);
|    | [NORMAL] ESLintBear (no-mixed-spaces-and-tabs):
|    | Mixed spaces and tabs.

binaries/data/mods/public/simulation/components/Attack.js
| 629| »   »   cmpTimer.SetTimeout(SYSTEM_ENTITY,·IID_Damage,·"MissileHit",·timeToTarget·*·1000·+·+this.template[type].Delay,·data);
|    | [NORMAL] JSHintBear:
|    | Confusing plusses.
Executing section cli...

Link to build: https://jenkins.wildfiregames.com/job/docker-differential/232/display/redirect

wraitii abandoned this revision.Thu, Aug 22, 8:13 PM

Done in D2092. Thanks for the patch which gave me the idea for that one :)

Done in D2092. Thanks for the patch which gave me the idea for that one :)

You're welcome!
B.t.w. I have already a patch for the new tooltips but it ought to be updated.