OK, I have done some experimentation, and this is the revised code which looks right (and tested a bit by me). This is almost exactly like the code already there, so dont get all excited about how much super new stu I am providing, its about 3 new lines
Code: Select all
- (BOOL) collideWithShip:(ShipEntity *)other
{
Vector loc, opos, pos;
double inc1, dam1, dam2;
if (!other)
return NO;
ShipEntity* otherParent = (ShipEntity*)[other owner];
BOOL otherIsSubentity = ((otherParent)&&(otherParent != other)&&([otherParent->sub_entities containsObject:other]));
BOOL otherIsStation = other == [UNIVERSE station];
// calculate line of centers using centres
if (otherIsSubentity)
opos = [other absolutePositionForSubentity];
else
opos = other->position;
loc = opos;
loc.x -= position.x; loc.y -= position.y; loc.z -= position.z;
if (loc.x||loc.y||loc.z)
loc = unit_vector(&loc);
else
loc.z = 1.0;
inc1 = (v_forward.x*loc.x)+(v_forward.y*loc.y)+(v_forward.z*loc.z);
if ([self canScoop:other])
{
[self scoopIn:other];
return NO;
}
if ([other canScoop:self])
{
[other scoopIn:self];
return NO;
}
if (universalID == NO_TARGET)
return NO;
if (other->universalID == NO_TARGET)
return NO;
// find velocity along line of centers
//
// momentum = mass x velocity
// ke = mass x velocity x velocity
//
GLfloat m1 = mass; // mass of self
GLfloat m2 = [other mass]; // mass of other
// starting velocities:
Vector vel1b = [self velocity];
// calculate other's velocity relative to self
Vector v = [other velocity];
if (otherIsSubentity)
{
if (otherParent)
{
v = [otherParent velocity];
// if the subentity is rotating (subentityRotationalVelocity is not 1 0 0 0)
// we should calculate the tangential velocity from the other's position
// relative to our absolute position and add that in. For now this is a TODO
}
else
v = kZeroVector;
}
//
v = make_vector(vel1b.x - v.x, vel1b.y - v.y, vel1b.z - v.z); // velocity of self relative to other
//
GLfloat v2b = dot_product(v, loc); // velocity of other along loc before collision
//
GLfloat v1a = sqrt(v2b * v2b * m2 / m1); // velocity of self along loc after elastic collision
if (v2b < 0.0f) v1a = -v1a; // in same direction as v2b
// are they moving apart at over 1m/s already?
if (v2b < 0.0f)
{
if (v2b < -1.0f) return NO;
else
{
position = make_vector(position.x - loc.x, position.y - loc.y, position.z - loc.z); // adjust self position
v = kZeroVector; // go for the 1m/s solution
}
}
// convert change in velocity into damage energy (KE)
//
dam1 = m2 * v2b * v2b / 50000000;
dam2 = m1 * v2b * v2b / 50000000;
// calculate adjustments to velocity after collision
Vector vel1a = make_vector(-v1a * loc.x, -v1a * loc.y, -v1a * loc.z);
Vector vel2a = make_vector(v2b * loc.x, v2b * loc.y, v2b * loc.z);
if (magnitude2(v) <= 0.1) // virtually no relative velocity - we must provide at least 1m/s to avoid conjoined objects
{
vel1a = make_vector(-loc.x, -loc.y, -loc.z);
vel2a = make_vector(loc.x, loc.y, loc.z);
}
// apply change in velocity
if ((otherIsSubentity)&&(otherParent))
[otherParent adjustVelocity:vel2a]; // move the otherParent not the subentity
else
[other adjustVelocity:vel2a];
[self adjustVelocity:vel1a];
//
BOOL selfDestroyed = (dam1 > energy);
BOOL otherDestroyed = (dam2 > [other energy]) && !otherIsStation;
//
if (dam1 > 0.05)
{
[self takeScrapeDamage: dam1 from:other];
if (selfDestroyed) // inelastic! - take xplosion velocity damage instead
{
vel2a.x = -vel2a.x; vel2a.y = -vel2a.y; vel2a.z = -vel2a.z;
[other adjustVelocity:vel2a];
}
}
//
if (dam2 > 0.05)
{
if ((otherIsSubentity) && (otherParent) && !([otherParent isFrangible]))
[otherParent takeScrapeDamage: dam2 from:self];
else
[other takeScrapeDamage: dam2 from:self];
if (otherDestroyed) // inelastic! - take explosion velocity damage instead
{
vel1a.x = -vel1a.x; vel1a.y = -vel1a.y; vel1a.z = -vel1a.z;
[self adjustVelocity:vel1a];
}
}
if ((!selfDestroyed)&&(!otherDestroyed))
{
float t = 10.0 * [UNIVERSE getTimeDelta]; // 10 ticks
//
pos = self->position;
opos = other->position;
//
Vector pos1a = make_vector(pos.x + t * v1a * loc.x, pos.y + t * v1a * loc.y, pos.z + t * v1a * loc.z);
Vector pos2a = make_vector(opos.x - t * v2b * loc.x, opos.y - t * v2b * loc.y, opos.z - t * v2b * loc.z);
//
[self setPosition:pos1a];
if (!otherIsStation) {
[other setPosition:pos2a];
}
}
// remove self from other's collision list
[[other collisionArray] removeObject:self];
[shipAI reactToMessage:@"COLLISION"];
return YES;
}
The main differences are:
* As detailed above, some inconsistent handling of ships with subentites (although what I wrote above was the wrong way about)
*
Code: Select all
if (otherDestroyed) // inelastic! - take explosion velocity damage instead
{
vel1a.x = -vel1a.x; vel1a.y = -vel1a.y; vel1a.z = -vel1a.z;
[other adjustVelocity:vel1a];
}
Does not make sense to move other, we know it is destroyed. Compare to code where selfDestroyed and we can see expected behaviour.
* I added code to prevent the station getting shunted around, anyone think this is a good or bad idea?
Code: Select all
if (!otherIsStation) {
[other setPosition:pos2a];
}
I have not added this to svn, but I think it is right, anyone else got a view on it?