// $Revision: 1.25 $ //Copyright (C) Taligent, Inc. All rights reserved. #ifndef TaligentSamples_GAMESUBSYSTEM #include "GameSubsystem.h" #endif #ifndef Taligent_RANDOMNUMBER #include #endif #ifndef Taligent_GRAPHICS #include #endif #ifndef Taligent_BUNDLES #include #endif #ifndef Taligent_GRAFPORT #include #endif #ifndef Taligent_GRAFPENS #include #endif #ifndef Taligent_GRAPHICIMAGE #include #endif #ifndef Taligent_LINEGEOMETRY #include #endif #ifndef Taligent_INFINITELINE #include #endif #ifndef Taligent_KEYBOARDS #include #endif #ifndef TaligentSamples_CONCURRENTACTORS #include "ConcurrentActors.h" #endif //============================================================================== // TSimulationBackground TaligentTypeExtensionMacro(TSimulationBackground) TSimulationBackground::TSimulationBackground() : MRefreshable() { } TSimulationBackground::TSimulationBackground(const TSimulationBackground& source) : MRefreshable() { } TSimulationBackground::~TSimulationBackground() { } TSimulationBackground& TSimulationBackground::operator=(const TSimulationBackground& source) { return *this; } TStream& TSimulationBackground::operator>>=(TStream& toStream) const { WriteVersion(toStream); return toStream; } TStream& TSimulationBackground::operator<<=(TStream& fromStream) { VersionInfo info = ReadVersion(fromStream, kOriginalVersion, kOriginalVersion); return fromStream; } void TSimulationBackground::Draw(TGrafPort& port) const { GetGraphic().Draw(port); } TGRect TSimulationBackground::GetBounds() const { return GetGraphic().GetGeometricBounds(); } const MGraphic& TSimulationBackground::GetGraphic() const { static const TPolygon graphic( TGRect(-5000, -5000, 5000, 5000), new TGrafBundle( new TColorPaint(TRGBColor(0.1, 0.1, 0.1)), new TColorPaint(TRGBColor(0.1, 0.1, 0.1)), TAttributeState::kFill)); return graphic; } //============================================================================== // TExplodingActor TaligentTypeExtensionMacro_Abstract(TExplodingActor) TExplodingActor::TExplodingActor() : TAbstractActor(), fPosition(TGPoint::kOrigin), fVelocity(TGPoint::kOrigin) { } TExplodingActor::TExplodingActor(const TGPoint& initialPosition, const TGPoint& velocity) : TAbstractActor(), fPosition(initialPosition), fVelocity(velocity) { } TExplodingActor::TExplodingActor(const TExplodingActor& source) : TAbstractActor(source), fPosition(source.fPosition), fVelocity(source.fVelocity) { } TExplodingActor::~TExplodingActor() { } TExplodingActor& TExplodingActor::operator=(const TExplodingActor& source) { if (&source != this) { TAbstractActor::operator=(source); fPosition = source.fPosition; fVelocity = source.fVelocity; } return *this; } TStream& TExplodingActor::operator>>=(TStream& toStream) const { WriteVersion(toStream); TAbstractActor::operator>>=(toStream); fPosition >>= toStream; fVelocity >>= toStream; return toStream; } TStream& TExplodingActor::operator<<=(TStream& fromStream) { VersionInfo info = ReadVersion(fromStream, kOriginalVersion, kOriginalVersion); TAbstractActor::operator<<=(fromStream); fPosition <<= fromStream; fVelocity <<= fromStream; return fromStream; } bool TExplodingActor::IsA(const TType& type) const { return type == StaticTypeInfo(TExplodingActor) || TAbstractActor::IsA(type); } void TExplodingActor::Move(const TTime& interval) { if (!IsDead()) { SetPosition(GetPosition() + GetVelocity() * TSeconds(interval).GetSeconds()); } } void TExplodingActor::Draw(TGrafPort& port) const { if (!IsDead()) { TLinkedModelMatrixPort translatePort(&port, TGrafMatrix(GetPosition())); GetGraphic().Draw(translatePort); } } TGPoint TExplodingActor::GetPosition() const { return fPosition; } void TExplodingActor::SetPosition(const TGPoint& newPosition) { if (GetPosition() != newPosition) { fPosition = newPosition; CollisionCheckNeeded(); } } TGPoint TExplodingActor::GetVelocity() const { return fVelocity; } void TExplodingActor::SetVelocity(const TGPoint& newVelocity) { fVelocity = newVelocity; } TGRect TExplodingActor::GetBounds() const { TGRect result(TGRect::kZeroRect); if (!IsDead()) { result = GetGraphic().GetGeometricBounds(); result.SetCenter(GetPosition()); } return result; } //============================================================================== // TAsteroid TaligentTypeExtensionMacro_Abstract(TAsteroid) TAsteroid::TAsteroid(const TGPoint& initialPosition, const GCoordinate& speed) : TExplodingActor(initialPosition, CalcRandomDirection(speed)) { } TAsteroid::TAsteroid(const TAsteroid& source) : TExplodingActor(source) { } TAsteroid::~TAsteroid() { } TAsteroid& TAsteroid::operator=(const TAsteroid& source) { if (&source != this) { TExplodingActor::operator=(source); } return *this; } TStream& TAsteroid::operator>>=(TStream& toStream) const { WriteVersion(toStream); TExplodingActor::operator>>=(toStream); return toStream; } TStream& TAsteroid::operator<<=(TStream& fromStream) { VersionInfo info = ReadVersion(fromStream, kOriginalVersion, kOriginalVersion); TExplodingActor::operator<<=(fromStream); return fromStream; } bool TAsteroid::IsA(const TType& type) const { return type == StaticTypeInfo(TAsteroid) || TExplodingActor::IsA(type); } TAsteroid::TAsteroid() : TExplodingActor() { } TGPoint TAsteroid::CalcRandomDirection(const GCoordinate& speed) const { TGPoint result(speed, speed); static TUniformRandomDouble random; GDegrees angle = 360.0 * random.Next(); result.SetVectorAngle(angle); return result; } //============================================================================== // TSmallAsteroid TaligentTypeExtensionMacro(TSmallAsteroid) static const GCoordinate TSmallAsteroid_kDefaultSpeed = 30.0; const GCoordinate& TSmallAsteroid::kDefaultSpeed = TSmallAsteroid_kDefaultSpeed; TSmallAsteroid::TSmallAsteroid(const TGPoint& initialPosition, const GCoordinate& initialSpeed) : TAsteroid(initialPosition, initialSpeed) { } TSmallAsteroid::TSmallAsteroid(const TSmallAsteroid& source) : TAsteroid(source) { } TSmallAsteroid::~TSmallAsteroid() { } TSmallAsteroid& TSmallAsteroid::operator=(const TSmallAsteroid& source) { if (&source != this) { TAsteroid::operator=(source); } return *this; } TStream& TSmallAsteroid::operator>>=(TStream& toStream) const { WriteVersion(toStream); TAsteroid::operator>>=(toStream); return toStream; } TStream& TSmallAsteroid::operator<<=(TStream& fromStream) { VersionInfo info = ReadVersion(fromStream, kOriginalVersion, kOriginalVersion); TAsteroid::operator<<=(fromStream); return fromStream; } bool TSmallAsteroid::IsA(const TType& type) const { return type == StaticTypeInfo(TSmallAsteroid) || TAsteroid::IsA(type); } void TSmallAsteroid::Explode() { SelfDestruct(); } TSmallAsteroid::TSmallAsteroid() : TAsteroid() { } const MGraphic& TSmallAsteroid::GetGraphic() const { static TGrafBundle kBundle( new TColorPaint(TRGBColor(0.83, 0.83, 0.83)), new TColorPaint(TRGBColor(0.93, 0.93, 0.93)), TAttributeState::kFillAndFrame); kBundle.AdoptFramePen(new THairlinePen); static const TEllipse smallAsteroidGraphic( TGEllipse(TGRect(-10,-10, 10, 10)), new TGrafBundle(kBundle)); return smallAsteroidGraphic; } //============================================================================== // TMediumAsteroids TaligentTypeExtensionMacro(TMediumAsteroid) static const GCoordinate TMediumAsteroid_kDefaultSpeed = 20; const GCoordinate& TMediumAsteroid::kDefaultSpeed = TMediumAsteroid_kDefaultSpeed; TMediumAsteroid::TMediumAsteroid(const TGPoint& initialPosition, const GCoordinate& initialSpeed) : TAsteroid(initialPosition, initialSpeed) { } TMediumAsteroid::TMediumAsteroid(const TMediumAsteroid& source) : TAsteroid(source) { } TMediumAsteroid::~TMediumAsteroid() { } TMediumAsteroid& TMediumAsteroid::operator=(const TMediumAsteroid& source) { if (&source != this) { TAsteroid::operator=(source); } return *this; } TStream& TMediumAsteroid::operator>>=(TStream& toStream) const { WriteVersion(toStream); TAsteroid::operator>>=(toStream); return toStream; } TStream& TMediumAsteroid::operator<<=(TStream& fromStream) { VersionInfo info = ReadVersion(fromStream, kOriginalVersion, kOriginalVersion); TAsteroid::operator<<=(fromStream); return fromStream; } bool TMediumAsteroid::IsA(const TType& type) const { return type == StaticTypeInfo(TMediumAsteroid) || TAsteroid::IsA(type); } void TMediumAsteroid::Explode() { for (long i = 0; i < GetDefaultDivision(); i++) { RegisterOffspring(new TSmallAsteroid(GetPosition())); } SelfDestruct(); } TMediumAsteroid::TMediumAsteroid() : TAsteroid() { } const MGraphic& TMediumAsteroid::GetGraphic() const { static TGrafBundle kBundle( new TColorPaint(TRGBColor(0.70, 0.70, 0.70)), new TColorPaint(TRGBColor(0.80, 0.80, 0.80)), TAttributeState::kFillAndFrame); kBundle.AdoptFramePen(new THairlinePen); static const TEllipse mediumAsteroidGraphic( TGEllipse(TGRect(-25, -25, 25, 25)), new TGrafBundle(kBundle)); return mediumAsteroidGraphic; } long TMediumAsteroid::GetDefaultDivision() const { return 2; } //============================================================================== // TLargeAsteroid TaligentTypeExtensionMacro(TLargeAsteroid) static const GCoordinate TLargeAsteroid_kDefaultSpeed = 10; const GCoordinate& TLargeAsteroid::kDefaultSpeed = TLargeAsteroid_kDefaultSpeed; TLargeAsteroid::TLargeAsteroid(const TGPoint& initialPosition, const GCoordinate& speed) : TAsteroid(initialPosition, speed) { } TLargeAsteroid::TLargeAsteroid(const TLargeAsteroid& source) : TAsteroid(source) { } TLargeAsteroid::~TLargeAsteroid() { } TLargeAsteroid& TLargeAsteroid::operator=(const TLargeAsteroid& source) { if (&source != this) { TAsteroid::operator=(source); } return *this; } TStream& TLargeAsteroid::operator>>=(TStream& toStream) const { WriteVersion(toStream); TAsteroid::operator>>=(toStream); return toStream; } TStream& TLargeAsteroid::operator<<=(TStream& fromStream) { VersionInfo info = ReadVersion(fromStream, kOriginalVersion, kOriginalVersion); TAsteroid::operator<<=(fromStream); return fromStream; } bool TLargeAsteroid::IsA(const TType& type) const { return type == StaticTypeInfo(TLargeAsteroid) || TAsteroid::IsA(type); } void TLargeAsteroid::Explode() { for (long i = 0; i < GetDefaultDivision(); i++) { RegisterOffspring(new TMediumAsteroid(GetPosition())); } SelfDestruct(); } TLargeAsteroid::TLargeAsteroid() : TAsteroid() { } const MGraphic& TLargeAsteroid::GetGraphic() const { static TGrafBundle kBundle( new TColorPaint(TRGBColor(0.56, 0.56, 0.56)), new TColorPaint(TRGBColor(0.66, 0.66, 0.66)), TAttributeState::kFillAndFrame); kBundle.AdoptFramePen(new THairlinePen); static const TEllipse largeAsteroidGraphic( TGEllipse(TGRect(-40, -40, 40, 40)), new TGrafBundle(kBundle)); return largeAsteroidGraphic; } long TLargeAsteroid::GetDefaultDivision() const { return 2; } //============================================================================== // TBullet TaligentTypeExtensionMacro(TBullet) static const TSeconds TBullet_kDefaultLifeSpan(4.0); const TTime& TBullet::kDefaultLifeSpan = TBullet_kDefaultLifeSpan; TBullet::TBullet() : TExplodingActor(TGPoint::kOrigin, TGPoint::kOrigin), fLifeSpan(kDefaultLifeSpan) { } TBullet::TBullet(const TGPoint& initialPosition, const TGPoint& velocity, const TTime& lifeSpan) : TExplodingActor(initialPosition, velocity), fLifeSpan(lifeSpan) { } TBullet::TBullet(const TBullet& source) : TExplodingActor(source), fLifeSpan(source.fLifeSpan) { } TBullet::~TBullet() { } TBullet& TBullet::operator=(const TBullet& source) { if (&source != this) { TExplodingActor::operator=(source); fLifeSpan = source.fLifeSpan; } return *this; } TStream& TBullet::operator>>=(TStream& toStream) const { WriteVersion(toStream); TExplodingActor::operator>>=(toStream); return toStream; } TStream& TBullet::operator<<=(TStream& fromStream) { VersionInfo info = ReadVersion(fromStream, kOriginalVersion, kOriginalVersion); TExplodingActor::operator<<=(fromStream); fLifeSpan = TTime::kZero; return fromStream; } bool TBullet::IsA(const TType& type) const { return type == StaticTypeInfo(TBullet) || TExplodingActor::IsA(type); } void TBullet::Move(const TTime& interval) { if (fLifeSpan < TTime::kZero) { SelfDestruct(); } else { TExplodingActor::Move(interval); fLifeSpan -= interval; } } void TBullet::Explode() { SelfDestruct(); } const MGraphic& TBullet::GetGraphic() const { static TGrafBundle kBundle( new TColorPaint(TRGBColor(0.9, 0.9, 0.0)), new TColorPaint(TRGBColor(0.9, 0.9, 0.0)), TAttributeState::kFillAndFrame); kBundle.AdoptFramePen(new THairlinePen); static const TEllipse bulletGraphic( TGEllipse(TGRect(-2, -2, 2, 2)), new TGrafBundle(kBundle)); return bulletGraphic; } //============================================================================== // TShip TaligentTypeExtensionMacro(TShip) TShip::TShip(const TGPoint& initialPosition, UniChar thrust, UniChar fire, UniChar left, UniChar right) : TExplodingActor(initialPosition, TGPoint::kOrigin), fRotation(0), fThrust(thrust), fFire(fire), fLeft(left), fRight(right), //The remaining data members are runtime state. fRotationWatch(), fRotationDirection(kNoRotation), fThrustWatch(), fBulletsAvailable(GetDefaultMaxBullets()), fBulletConnection(this, HandleBulletDied) { fBulletConnection.Connect(); fRotationWatch.Stop(); fRotationWatch.Reset(); fThrustWatch.Stop(); fThrustWatch.Reset(); } TShip::TShip(const TShip& source) : TExplodingActor(source), fRotation(source.fRotation), fThrust(source.fThrust), fFire(source.fFire), fLeft(source.fLeft), fRight(source.fRight), //The remaining data members are runtime state. fRotationWatch(), fRotationDirection(kNoRotation), fThrustWatch(), fBulletsAvailable(GetDefaultMaxBullets()), fBulletConnection(this, HandleBulletDied) { fBulletConnection.Connect(); fRotationWatch.Stop(); fRotationWatch.Reset(); fThrustWatch.Stop(); fThrustWatch.Reset(); } TShip::~TShip() { fBulletConnection.RemoveAllInterests(); } TShip& TShip::operator=(const TShip& source) { if (&source != this) { TExplodingActor::operator=(source); fRotation = source.fRotation; fThrust = source.fThrust; fFire = source.fFire; fLeft = source.fLeft; fRight = source.fRight; } return *this; } TStream& TShip::operator>>=(TStream& toStream) const { WriteVersion(toStream); TExplodingActor::operator>>=(toStream); long temp = fRotation; temp >>= toStream; fThrust >>= toStream; fFire >>= toStream; fLeft >>= toStream; fRight >>= toStream; return toStream; } TStream& TShip::operator<<=(TStream& fromStream) { VersionInfo info = ReadVersion(fromStream, kOriginalVersion, kOriginalVersion); TExplodingActor::operator<<=(fromStream); long temp; temp <<= fromStream; fRotation = temp; fThrust <<= fromStream; fFire <<= fromStream; fLeft <<= fromStream; fRight <<= fromStream; return fromStream; } bool TShip::IsA(const TType& type) const { return type == StaticTypeInfo(TShip) || TExplodingActor::IsA(type); } void TShip::Draw(TGrafPort& port) const { if (!IsDead()) { TGrafMatrix matrix(fRotation - 45.0, TGPoint::kOrigin); matrix.ConcatWith(TGrafMatrix(GetPosition())); TLinkedModelMatrixPort translatePort(&port, matrix); GetGraphic().Draw(translatePort); TLine(TGLine(TGPoint(0.0,0.0), TGPoint(10.0,10.0))).Draw(translatePort); TTime temp; ((TStopwatch&)fThrustWatch).GetElapsedTime(temp); if (temp > TTime::kZero) { GCoordinate percentThrust = GetVelocity().VectorLength() / GetDefaultMaxThrust(); TLine(TGLine(TGPoint(0.0,0.0), TGPoint(-7.5, 0.0) * percentThrust)).Draw(translatePort); TLine(TGLine(TGPoint(0.0,0.0), TGPoint( 0.0,-7.5) * percentThrust)).Draw(translatePort); } } } void TShip::PreMove(const TTime& interval) { TExplodingActor::PreMove(interval); TSeconds rotateTime; fRotationWatch.GetElapsedTimeAndReset(rotateTime); if (rotateTime > TTime::kZero) { fRotation = fRotation + fRotationDirection * rotateTime.GetSeconds() * GetDefaultRotationSpeed(); } TSeconds thrustTime; fThrustWatch.GetElapsedTimeAndReset(thrustTime); if (thrustTime > TTime::kZero) { TGPoint newVelocity(GetVelocity()); TGPoint addVector(TGPoint::kOrigin); addVector.SetPolarCoordinates(fRotation, GetDefaultAcceleration() * thrustTime.GetSeconds()); newVelocity += addVector; if (newVelocity.VectorLength() > GetDefaultMaxThrust()) { newVelocity.SetVectorLength(GetDefaultMaxThrust()); } SetVelocity(newVelocity); } } TGRect TShip::GetBounds() const { TGRect result(TGRect::kZeroRect); if (!IsDead()) { TGrafMatrix matrix(fRotation - 45.0, TGPoint::kOrigin); matrix.ConcatWith(TGrafMatrix(GetPosition())); result = matrix.TransformBounds(GetGraphic().GetGeometricBounds()); result.Inset(TGPoint(-2.0, -2.0)); } return result; } void TShip::Explode() { } UniChar TShip::GetThrustChar() const { return fThrust; } UniChar TShip::GetFireChar() const { return fFire; } UniChar TShip::GetLeftChar() const { return fLeft; } UniChar TShip::GetRightChar() const { return fRight; } void TShip::Fire() { if (!IsDead()) { if (fBulletsAvailable > 0) { fBulletsAvailable--; TGRect graphicBounds(GetBounds()); GCoordinate length = graphicBounds.GetHeight() / 2.0; TGPoint position(0.0, length); position.SetVectorAngle(fRotation); position += graphicBounds.GetCenter(); TGPoint bulletVector(TGPoint::kOrigin); bulletVector.SetPolarCoordinates(fRotation, 80.0); bulletVector += GetVelocity(); TBullet* bullet = new TBullet(position, bulletVector); TInterest* interest = bullet->CreateUniverseAffectInterest(); fBulletConnection.AdoptInterest(interest); RegisterOffspring(bullet); } } } void TShip::ThrustBegin() { fThrustWatch.Start(); } void TShip::ThrustEnd() { fThrustWatch.Stop(); } void TShip::RotateBegin(TShip::ERotation direction) { fRotationDirection = direction; fRotationWatch.Start(); } void TShip::RotateEnd() { fRotationDirection = kNoRotation; fRotationWatch.Stop(); } TShip::TShip() : TExplodingActor(TGPoint::kOrigin, TGPoint::kOrigin), fRotation(0), fThrust(), fFire(), fLeft(), fRight(), //The remaining data members are runtime state. fRotationWatch(), fRotationDirection(kNoRotation), fThrustWatch(), fBulletsAvailable(GetDefaultMaxBullets()), fBulletConnection(this, HandleBulletDied) { fBulletConnection.Connect(); fRotationWatch.Stop(); fRotationWatch.Reset(); fThrustWatch.Stop(); fThrustWatch.Reset(); } const MGraphic& TShip::GetGraphic() const { static bool isConstructed = false; static TGrafBundle kBundle( new TColorPaint(TRGBColor(0.9, 0.0, 0.0)), new TColorPaint(TRGBColor(0.9, 0.1, 0.1)), TAttributeState::kFill); static TGPointArray polyPoints(4); if (isConstructed == false) { polyPoints.SetPoint(0, TGPoint( 10.0, 10.0)); polyPoints.SetPoint(1, TGPoint( 10.0,-10.0)); polyPoints.SetPoint(2, TGPoint( -5.0, -5.0)); polyPoints.SetPoint(3, TGPoint(-10.0, 10.0)); isConstructed = true; } static const TPolygon shipInitialGraphic(polyPoints, true, new TGrafBundle(kBundle)); return shipInitialGraphic; } void TShip::HandleBulletDied(const TNotification& noteIn) { TUniverseActorNote& note = (TUniverseActorNote&)noteIn; TAbstractActor* actor = note.GetActor(); TInterest* interest = actor->CreateUniverseAffectInterest(); fBulletConnection.RemoveInterest(*interest); delete interest; fBulletsAvailable++; } const unsigned long TShip::GetDefaultMaxBullets() { static const unsigned long shipMaxBullets = 3; return shipMaxBullets; } const GCoordinate& TShip::GetDefaultMaxThrust() { static const GCoordinate shipMaxThrust = 70.0; return shipMaxThrust; } const GCoordinate& TShip::GetDefaultAcceleration() { static const GCoordinate shipSecondsTillMax = 3; static const GCoordinate shipAcceleration = GetDefaultMaxThrust() / shipSecondsTillMax; return shipAcceleration; } const GCoordinate& TShip::GetDefaultRotationSpeed() { static const GCoordinate shipSecondsToRotate = 2.5; static const GCoordinate shipfgRotationSpeed = 360.0 / shipSecondsToRotate; return shipfgRotationSpeed; } //============================================================================== // TBoundingBoxBehavior TaligentTypeExtensionMacro(TBoundingBoxBehavior) TBoundingBoxBehavior::TBoundingBoxBehavior() : TCollisionBehavior(), fBounds(TGRect::kInfiniteRect), fConnection(this, HandleCollisionPossible) { fConnection.Connect(); } TBoundingBoxBehavior::TBoundingBoxBehavior(const TGRect& bounds) : TCollisionBehavior(), fBounds(bounds), fConnection(this, HandleCollisionPossible) { fConnection.Connect(); } TBoundingBoxBehavior::TBoundingBoxBehavior(const TBoundingBoxBehavior& source) : TCollisionBehavior(source), fBounds(source.fBounds), fConnection(this, HandleCollisionPossible) { fConnection.Connect(); } TBoundingBoxBehavior::~TBoundingBoxBehavior() { fConnection.RemoveAllInterests(); } TBoundingBoxBehavior& TBoundingBoxBehavior::operator=(const TBoundingBoxBehavior& source) { if (&source != this) { TCollisionBehavior::operator=(source); SetBounds(source.GetBounds()); } return *this; } TStream& TBoundingBoxBehavior::operator>>=(TStream& toStream) const { WriteVersion(toStream); TCollisionBehavior::operator>>=(toStream); GetBounds() >>= toStream; return toStream; } TStream& TBoundingBoxBehavior::operator<<=(TStream& fromStream) { VersionInfo info = ReadVersion(fromStream, kOriginalVersion, kOriginalVersion); TCollisionBehavior::operator<<=(fromStream); fBounds <<= fromStream; return fromStream; } TGRect TBoundingBoxBehavior::GetBounds() const { return fBounds; } void TBoundingBoxBehavior::SetBounds(const TGRect& bounds) { fBounds = bounds; } void TBoundingBoxBehavior::ConnectTo(MColliding* colliding) { if (colliding->IsA(StaticTypeInfo(TExplodingActor))) { TInterest* interest = colliding->CreateCollisionInterest(); fConnection.AdoptInterest(interest); } } void TBoundingBoxBehavior::DisconnectFrom(MColliding& colliding) { if (colliding.IsA(StaticTypeInfo(TExplodingActor))) { TInterest* interest = colliding.CreateCollisionInterest(); fConnection.RemoveInterest(*interest); delete interest; } } void TBoundingBoxBehavior::HandleCollisionPossible(const TNotification& noteIn) { TCollisionNotification& note = (TCollisionNotification&)noteIn; MColliding* item = note.GetObject(); TExplodingActor* actor = (TExplodingActor*)item; TGRect objBounds(actor->GetBounds()); TGPoint objVelocity(actor->GetVelocity()); TGPoint objPosition(actor->GetPosition()); TGRect bounds(GetBounds()); bounds.Offset(objPosition); bounds.Set(bounds.GetTopLeft() - objBounds.GetTopLeft(), bounds.GetBottomRight() - objBounds.GetBottomRight()); if (bounds.IsEmpty()) return; bool somethingChanged = false; if (objPosition.fX >= bounds.fRight) { TGInfiniteLine edge(bounds.GetRightLine()); TGInfiniteLine vector(objPosition, objVelocity); TGInfiniteLine reflection(vector.Reflection(edge)); objVelocity = reflection.GetDirection(); objPosition = reflection.GetBasePoint(); somethingChanged = true; } else if (objPosition.fX <= bounds.fLeft) { TGInfiniteLine edge(bounds.GetLeftLine()); TGInfiniteLine vector(objPosition, objVelocity); TGInfiniteLine reflection(vector.Reflection(edge)); objVelocity = reflection.GetDirection(); objPosition = reflection.GetBasePoint(); somethingChanged = true; } if (objPosition.fY <= bounds.fTop) { TGInfiniteLine edge(bounds.GetTopLine()); TGInfiniteLine vector(objPosition, objVelocity); TGInfiniteLine reflection(vector.Reflection(edge)); objVelocity = reflection.GetDirection(); objPosition = reflection.GetBasePoint(); somethingChanged = true; } else if (objPosition.fY >= bounds.fBottom) { TGInfiniteLine edge(bounds.GetBottomLine()); TGInfiniteLine vector(objPosition, objVelocity); TGInfiniteLine reflection(vector.Reflection(edge)); objVelocity = reflection.GetDirection(); objPosition = reflection.GetBasePoint(); somethingChanged = true; } if (somethingChanged) { actor->SetVelocity(objVelocity); actor->SetPosition(objPosition); } } //============================================================================== // TExplosionBehavior TaligentTypeExtensionMacro(TExplosionBehavior) TExplosionBehavior::TExplosionBehavior() : TCollisionBehavior(), fUsType(), fThemType(), fUs(), fThem(), fUsConnection(this, HandleUsCollisionPossible) { fUsConnection.Connect(); } TExplosionBehavior::TExplosionBehavior(const TType& usType, const TType& themType) : TCollisionBehavior(), fUsType(usType), fThemType(themType), fUs(), fThem(), fUsConnection(this, HandleUsCollisionPossible) { fUsConnection.Connect(); } TExplosionBehavior::TExplosionBehavior( const TExplosionBehavior& source) : TCollisionBehavior(), fUsType(source.fUsType), fThemType(source.fThemType), fUs(), fThem(), fUsConnection(this, HandleUsCollisionPossible) { fUsConnection.Connect(); } TExplosionBehavior::~TExplosionBehavior() { fUsConnection.RemoveAllInterests(); fUs.RemoveAll(); fThem.RemoveAll(); } TExplosionBehavior& TExplosionBehavior::operator=(const TExplosionBehavior& source) { if (&source != this) { fUsConnection.RemoveAllInterests(); TCollisionBehavior::operator=(source); fUsType = source.fUsType; fThemType = source.fThemType; } return *this; } TStream& TExplosionBehavior::operator>>=(TStream& toStream) const { WriteVersion(toStream); TCollisionBehavior::operator>>=(toStream); fUsType >>= toStream; fThemType >>= toStream; return toStream; } TStream& TExplosionBehavior::operator<<=(TStream& fromStream) { VersionInfo info = ReadVersion(fromStream, kOriginalVersion, kOriginalVersion); fUsConnection.RemoveAllInterests(); TCollisionBehavior::operator<<=(fromStream); fUsType <<= fromStream; fThemType <<= fromStream; return fromStream; } void TExplosionBehavior::ConnectTo(MColliding* colliding) { if (colliding->IsA(fUsType)) { TInterest* interest = colliding->CreateCollisionInterest(); fUsConnection.AdoptInterest(interest); fUs.AddLast((TExplodingActor*)colliding); } if (colliding->IsA(fThemType)) { fThem.AddLast((TExplodingActor*)colliding); } } void TExplosionBehavior::DisconnectFrom(MColliding& colliding) { if (fUs.Remove((TExplodingActor&)colliding) != NIL) { TInterest* interest = colliding.CreateCollisionInterest(); fUsConnection.RemoveInterest(*interest); delete interest; } fThem.Remove((TExplodingActor&)colliding); } void TExplosionBehavior::HandleUsCollisionPossible(const TNotification& noteIn) { TCollisionNotification& note = (TCollisionNotification&)noteIn; TExplodingActor* us = (TExplodingActor*)note.GetObject(); DoPossibleCollision(us, fThem); } void TExplosionBehavior::DoPossibleCollision(TExplodingActor* cause, TDequeOf& affected) { bool collisionOccured = false; TDequeOf hitList; TDequeOfIterator affectedIter(&affected); TExplodingActor* affectedItem = affectedIter.First(); while (affectedItem != NIL) { if (!affectedItem->IsDead() && cause->GetBounds().Intersects(affectedItem->GetBounds())) { collisionOccured = true; hitList.AddLast(affectedItem); } affectedItem = affectedIter.Next(); } if (collisionOccured) { cause->Explode(); TDequeOfIterator deadIter(&hitList); TExplodingActor* deadGuy = deadIter.First(); while (deadGuy != NIL) { deadGuy->Explode(); deadGuy = deadIter.Next(); } hitList.RemoveAll(); } } //============================================================================== // TSimulationRefreshNotification MCollectibleDefinitionsMacro(TSimulationRefreshNotification, kOriginalVersion); TSimulationRefreshNotification::TSimulationRefreshNotification( const TInterest& interest, const TGRect& area, const TImage* image) : TNotification(interest), fRefreshArea(area), fImage(image) { } TSimulationRefreshNotification::TSimulationRefreshNotification( TInterest* interestToAdopt, const TGRect& area, const TImage* image) : TNotification(*interestToAdopt), fRefreshArea(area), fImage(image) { delete interestToAdopt; } TSimulationRefreshNotification::TSimulationRefreshNotification( const TSimulationRefreshNotification& source) : TNotification(source), fRefreshArea(source.fRefreshArea), fImage(source.fImage) { } TSimulationRefreshNotification::~TSimulationRefreshNotification() { } TSimulationRefreshNotification& TSimulationRefreshNotification::operator=(const TSimulationRefreshNotification& source) { if (&source != this) { TNotification::operator=(source); fRefreshArea = source.fRefreshArea; fImage = source.fImage; } return *this; } bool TSimulationRefreshNotification::IsEqual(const MCollectible* other) const { bool result(true); if (this == other) { result = true; } else if (!TNotification::IsEqual(other)) { result = false; } else { const TSimulationRefreshNotification* otherTSimulationRefreshNotification = (const TSimulationRefreshNotification*)other; result = fImage == otherTSimulationRefreshNotification->fImage && fRefreshArea == otherTSimulationRefreshNotification->fRefreshArea; } return result; } long TSimulationRefreshNotification::Hash() const { return TNotification::Hash() ^ (unsigned long)fImage; } const TImage* TSimulationRefreshNotification::GetImage() const { return fImage; } TGRect TSimulationRefreshNotification::GetRefreshArea() const { return fRefreshArea; } TSimulationRefreshNotification::TSimulationRefreshNotification() : TNotification(), fRefreshArea(TGRect::kZeroRect), fImage(NIL) { } TStream& TSimulationRefreshNotification::operator>>=(TStream& toStream) const { ERROR("TSimulationRefreshNotification::operator>>="); return toStream; } TStream& TSimulationRefreshNotification::operator<<=(TStream& fromStream) { ERROR("TSimulationRefreshNotification::operator<<="); return fromStream; } //============================================================================== // TSimulationLevel TaligentTypeExtensionMacro(TSimulationLevel) TSimulationLevel::TSimulationLevel() : fActors(NIL, new TPolymorphicStreamer()), fBehaviors(NIL, new TPolymorphicStreamer()), fBackground(NIL), fBounds(TGRect::kZeroRect), //The rest of this is runtime state information. fLivingAsteroids(0), fLivingShips(0), fLivingBullets(0), fShips(NIL, new TPolymorphicStreamer()), fUniverse(NIL), fUpdateBuffer(NIL), fRefreshConnection(this, HandleRefresh), fLifeConnection(this, HandleLife), fDeathConnection(this, HandleDeath), fRefreshNotifier(), fLevelEndedNotifier() { fRefreshConnection.Connect(); fDeathConnection.Connect(); fLifeConnection.Connect(); } TSimulationLevel::TSimulationLevel(const TSimulationLevel& source) : fActors(NIL, new TPolymorphicStreamer()), fBehaviors(NIL, new TPolymorphicStreamer()), fBackground(NIL), fBounds(TGRect::kZeroRect), //The rest of this is runtime state information. fLivingAsteroids(0), fLivingShips(0), fLivingBullets(0), fShips(NIL, new TPolymorphicStreamer()), fUniverse(NIL), fUpdateBuffer(NIL), fRefreshConnection(this, HandleRefresh), fLifeConnection(this, HandleLife), fDeathConnection(this, HandleDeath), fRefreshNotifier(), fLevelEndedNotifier() { fRefreshConnection.Connect(); fDeathConnection.Connect(); fLifeConnection.Connect(); CopyInternalState(source); } TSimulationLevel::~TSimulationLevel() { ClearSimulation(); } TSimulationLevel& TSimulationLevel::operator=(const TSimulationLevel& source) { if (&source != this) { bool isRunning = IsRunning(); ClearSimulation(); CopyInternalState(source); if (isRunning) { Restart(); } } return *this; } TStream& TSimulationLevel::operator>>=(TStream& toStream) const { WriteVersion(toStream); fBounds >>= toStream; if (IsRunning()) { fUniverse->Pause(); } fActors >>= toStream; if (IsRunning()) { fUniverse->Resume(); } fBehaviors >>= toStream; ::Flatten(fBackground, toStream); return toStream; } TStream& TSimulationLevel::operator<<=(TStream& fromStream) { VersionInfo info = ReadVersion(fromStream, kOriginalVersion, kOriginalVersion); bool isRunning = IsRunning(); ClearSimulation(); fBounds <<= fromStream; fActors <<= fromStream; fBehaviors <<= fromStream; ::Resurrect(fBackground, fromStream); SyncToNewInternalState(); if (isRunning) { Restart(); } return fromStream; } TGRect TSimulationLevel::GetBounds() const { return fBounds; } void TSimulationLevel::Restart() { delete fUpdateBuffer; fUpdateBuffer = NIL; TIndexedImage::TRGBColorArray colors; fUpdateBuffer = new TIndexedImage( fBounds.GetTopLeft(), fBounds.GetSize(), colors); fUniverse = new TUniverse(fUpdateBuffer->GetGrafPort(), &fActors, &fBehaviors, fBackground); fRefreshConnection.AdoptInterest(fUniverse->CreateRefreshInterest()); fDeathConnection.AdoptInterest(fUniverse->CreateOrphanInterest()); fLifeConnection.AdoptInterest(fUniverse->CreateAdoptInterest()); fUniverse->Start(); if ((fLivingAsteroids == 0 || fLivingShips == 0) && fLivingBullets == 0) { TInterest* interest = CreateLevelEndedInterest(); fLevelEndedNotifier.Notify(TNotification(*interest)); delete interest; } } void TSimulationLevel::Start(const TGRect& bounds, unsigned long smallAsteroids, unsigned long mediumAsteroids, unsigned long largeAsteroids, unsigned long ships) { ClearSimulation(); fBounds = bounds; fBackground = CreateBackground(); fLivingAsteroids = smallAsteroids + mediumAsteroids + largeAsteroids; fLivingShips = ships; fLivingBullets = 0; TGRect safeArea; safeArea.SetToPoint(TGRect::kInfiniteRect.GetTopLeft()); for (unsigned long i = 0; i < ships; i++) { TShip* ship = CreateShip(i, ships, safeArea); fActors.AddLast(ship); fShips.AddLast(ship); } for (i = 0; i < largeAsteroids; i++) { fActors.AddLast(CreateLargeAsteroid(safeArea)); } for (i = 0; i < mediumAsteroids; i++) { fActors.AddLast(CreateMediumAsteroid(safeArea)); } for (i = 0; i < smallAsteroids; i++) { fActors.AddLast(CreateSmallAsteroid(safeArea)); } AddBehaviors(fBehaviors); Restart(); } void TSimulationLevel::Stop() { PrivateStop(); } bool TSimulationLevel::IsRunning() const { return fUniverse != NIL; } const TModifiableImage* TSimulationLevel::UseImage() const { if (IsRunning()) { fUniverse->Pause(); } else if (fUpdateBuffer == NIL) { TIndexedImage::TRGBColorArray colors; ((TSimulationLevel*)(this))->fUpdateBuffer = new TIndexedImage( fBounds.GetTopLeft(), fBounds.GetSize(), colors ); } return fUpdateBuffer; } void TSimulationLevel::DoneWithImage() const { if (IsRunning()) { fUniverse->Resume(); } } TInterest* TSimulationLevel::CreateRefreshInterest() { static const TToken kRefreshInterestToken("TSimulationLevel::CreateRefreshInterest"); return new TInterest(&fRefreshNotifier, kRefreshInterestToken); } TInterest* TSimulationLevel::CreateLevelEndedInterest() { static const TToken kLevelEndedInterestToken("TSimulationLevel::CreateLevelEndedInterest"); return new TInterest(&fLevelEndedNotifier, kLevelEndedInterestToken); } bool TSimulationLevel::KeyDown(TKeyDownEvent& event) { bool result = IsRunning(); if (result && (fLivingAsteroids != 0 && fLivingShips != 0)) { TVirtualKeyCode::EVirtualKey keyCode = event.GetVirtualKey(); TModifierKeys modifiers = event.GetModifierKeys(); TVirtualKeyboardHandle keyboard; TStandardText text; keyboard.MapKeyToText(keyCode, modifiers, text); if (text.GetLength() == 1) { UniChar key = text.GetCharacterAt((TTextIndex)0); TDequeOfIterator iter(&fShips); TShip* ship = iter.First(); while (ship != NIL) { if (key == ship->GetThrustChar()) { TActorEntry entry(ship); ship->ThrustBegin(); } else if (key == ship->GetFireChar()) { TActorEntry entry(ship); ship->Fire(); } else if (key == ship->GetLeftChar()) { TActorEntry entry(ship); ship->RotateBegin(TShip::kCounterClockWise); } else if (key == ship->GetRightChar()) { TActorEntry entry(ship); ship->RotateBegin(TShip::kClockWise); } ship = iter.Next(); } } } return result; } bool TSimulationLevel::KeyUp(TKeyUpEvent& event) { bool result = IsRunning(); if (result) { TVirtualKeyCode::EVirtualKey keyCode = event.GetVirtualKey(); TModifierKeys modifiers = event.GetModifierKeys(); TVirtualKeyboardHandle keyboard; TStandardText text; keyboard.MapKeyToText(keyCode, modifiers, text); if (text.GetLength() == 1) { UniChar key = text.GetCharacterAt((TTextIndex)0); TDequeOfIterator iter(&fShips); TShip* ship = iter.First(); while (ship != NIL) { if (key == ship->GetThrustChar()) { TActorEntry entry(ship); ship->ThrustEnd(); } else if (key == ship->GetLeftChar() || key == ship->GetRightChar()) { TActorEntry entry(ship); ship->RotateEnd(); } ship = iter.Next(); } } } return result; } TExplodingActor* TSimulationLevel::PlacedAtRandomLocation(const TGRect& safeAreaIn, TExplodingActor* actor) const { TGRect actorBounds(actor->GetBounds()); TGPoint actorPosition(actor->GetPosition()); TGPoint topLeftSkew(actorPosition - actorBounds.GetTopLeft()); TGPoint bottomRightSkew(actorBounds.GetBottomRight() - actorPosition); TGRect legalBounds( GetBounds().GetTopLeft() + topLeftSkew, GetBounds().GetBottomRight() - bottomRightSkew); TGPoint size = legalBounds.GetSize(); TGRect safeArea(safeAreaIn.GetTopLeft() - topLeftSkew, safeAreaIn.GetBottomRight() + bottomRightSkew); static TUniformRandomInteger random; TGPoint kPrecisionPoint(random.GetHighestRandom(), random.GetHighestRandom()); TGPoint position; do { GCoordinate xMultiplier = Abs(random.Next()); GCoordinate yMultiplier = Abs(random.Next()); TGPoint multiplier(xMultiplier, yMultiplier); multiplier /= kPrecisionPoint; position = size * multiplier; position += legalBounds.GetTopLeft(); } while (safeArea.Contains(position) || !legalBounds.Contains(position)); actor->SetPosition(position); actorBounds = actor->GetBounds(); return actor; } TSimulationBackground* TSimulationLevel::CreateBackground() const { return new TSimulationBackground; } void TSimulationLevel::AddBehaviors(TDequeOf& behaviors) const { static const TType kAsteroid(StaticTypeInfo(TAsteroid)); static const TType kShip(StaticTypeInfo(TShip)); static const TType kBullet(StaticTypeInfo(TBullet)); behaviors.AddLast(new TBoundingBoxBehavior(GetBounds())); behaviors.AddLast(new TExplosionBehavior(kAsteroid, kShip)); behaviors.AddLast(new TExplosionBehavior(kAsteroid, kBullet)); } TShip* TSimulationLevel::CreateShip(unsigned long index, unsigned long max, TGRect& safeArea) const { static const UniChar kThrustKey = 'k'; static const UniChar kFireKey = ' '; static const UniChar kCounterClockwiseKey = 'j'; static const UniChar kClockwiseKey = 'l'; TShip* ship = new TShip(TGPoint::kOrigin, kThrustKey, kFireKey, kCounterClockwiseKey, kClockwiseKey); ship->SetPosition(GetBounds().GetCenter() - (ship->GetBounds().GetSize() * ((GCoordinate) (((index + 1) / 2) * (-1 * (index % 2)))) ) ); if (index == 0) { safeArea = ship->GetBounds(); } else { safeArea.ExtendTo(ship->GetBounds()); } return ship; } TAbstractActor* TSimulationLevel::CreateSmallAsteroid(const TGRect& safeArea) const { return PlacedAtRandomLocation(safeArea, new TSmallAsteroid(TGPoint::kOrigin)); } TAbstractActor* TSimulationLevel::CreateMediumAsteroid(const TGRect& safeArea) const { return PlacedAtRandomLocation(safeArea, new TMediumAsteroid(TGPoint::kOrigin)); } TAbstractActor* TSimulationLevel::CreateLargeAsteroid(const TGRect& safeArea) const { return PlacedAtRandomLocation(safeArea, new TLargeAsteroid(TGPoint::kOrigin)); } void TSimulationLevel::HandleRefresh(const TNotification& noteIn) { TSimulationRefreshNotification newNote( CreateRefreshInterest(), fBounds, fUpdateBuffer); fRefreshNotifier.Notify(newNote); } void TSimulationLevel::HandleDeath(const TNotification& noteIn) { static const TType kAsteroidType(StaticTypeInfo(TAsteroid)); static const TType kShipType(StaticTypeInfo(TShip)); static const TType kBulletType(StaticTypeInfo(TBullet)); TUniverseActorNote& note = (TUniverseActorNote&)noteIn; TAbstractActor* actor = note.GetActor(); if (actor->IsA(kAsteroidType)) { fLivingAsteroids--; } else if (actor->IsA(kShipType)) { fLivingShips--; fShips.Remove(*(TShip*)actor); } else if (actor->IsA(kBulletType)) { fLivingBullets--; } if ((fLivingAsteroids == 0 || fLivingShips == 0) && fLivingBullets == 0) { TInterest* interest = CreateLevelEndedInterest(); fLevelEndedNotifier.Notify(TNotification(*interest)); delete interest; } } void TSimulationLevel::HandleLife(const TNotification& noteIn) { static const TType kAsteroidType(StaticTypeInfo(TAsteroid)); static const TType kShipType(StaticTypeInfo(TShip)); static const TType kBulletType(StaticTypeInfo(TBullet)); TUniverseActorNote& note = (TUniverseActorNote&)noteIn; TAbstractActor* actor = note.GetActor(); if (actor->IsA(kAsteroidType)) { fLivingAsteroids++; } else if (actor->IsA(kShipType)) { fLivingShips++; fShips.AddLast((TShip*)actor); } else if (actor->IsA(kBulletType)) { fLivingBullets++; } } void TSimulationLevel::PrivateStop() { if (IsRunning()) { fUniverse->Stop(); fRefreshConnection.RemoveAllInterests(); fDeathConnection.RemoveAllInterests(); fLifeConnection.RemoveAllInterests(); delete fUniverse; fUniverse = NIL; } } void TSimulationLevel::ClearSimulation() { PrivateStop(); fShips.RemoveAll(); fActors.DeleteAll(); fBehaviors.DeleteAll(); delete fUpdateBuffer; fUpdateBuffer = NIL; delete fBackground; fBackground = NIL; fLivingAsteroids = 0; fLivingShips = 0; fLivingBullets = 0; } void TSimulationLevel::CopyInternalState(const TSimulationLevel& source) { bool sourceWasRunning = source.IsRunning(); if (sourceWasRunning) { source.fUniverse->Pause(); } ClearSimulation(); fBounds = source.fBounds; //cast away const TDequeOfIterator actorIter((TDequeOf*)&source.fActors); TAbstractActor* actor = actorIter.First(); while (actor != NIL) { fActors.AddLast(::CopyPointer(actor)); actor = actorIter.Next(); } //cast away const TDequeOfIterator behaviorIter((TDequeOf*)&source.fBehaviors); TCollisionBehavior* behavior = behaviorIter.First(); while (behavior != NIL) { fBehaviors.AddLast(::CopyPointer(behavior)); behavior = behaviorIter.Next(); } fBackground = ::CopyPointer(source.fBackground); SyncToNewInternalState(); if (sourceWasRunning) { source.fUniverse->Resume(); } } void TSimulationLevel::SyncToNewInternalState() { fShips.RemoveAll(); fLivingShips = 0; fLivingBullets = 0; fLivingAsteroids = 0; TDequeOfIterator actorIter((TDequeOf*)&fActors); TAbstractActor* actor = actorIter.First(); while (actor != NIL) { static const TType kAsteroidType(StaticTypeInfo(TAsteroid)); static const TType kShipType(StaticTypeInfo(TShip)); static const TType kBulletType(StaticTypeInfo(TBullet)); if (actor->IsA(kShipType)) { fShips.AddLast((TShip*)actor); fLivingShips++; } else if (actor->IsA(kBulletType)) { fLivingBullets++; } else if (actor->IsA(kAsteroidType)) { fLivingAsteroids++; } actor = actorIter.Next(); } }