PageRenderTime 60ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/Poing2/cGameObject.cs

http://github.com/BCProgramming/BASeBlock
C# | 3146 lines | 1903 code | 813 blank | 430 comment | 173 complexity | 1314ef1e8ff20d4e8300f4d763a6f8af MD5 | raw file
Possible License(s): BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * BASeCamp BASeBlock
  3. Copyright (c) 2011, Michael Burgwin
  4. All rights reserved.
  5. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
  6. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  7. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  8. Neither the name of BASeCamp Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
  9. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  10. * */
  11. using System;
  12. using System.Collections.Generic;
  13. using System.Diagnostics;
  14. using System.Drawing;
  15. using System.Drawing.Drawing2D;
  16. using System.Drawing.Imaging;
  17. using System.Linq;
  18. using System.Runtime.Serialization;
  19. using System.Text;
  20. using System.Threading;
  21. using BASeCamp.BASeBlock.Blocks;
  22. using BASeCamp.BASeBlock.PaddleBehaviours;
  23. using BASeCamp.BASeBlock.Particles;
  24. using BASeCamp.BASeBlock.Projectiles;
  25. using Img;
  26. namespace BASeCamp.BASeBlock
  27. {
  28. /// <summary>
  29. /// abstract GameObject class, will be used to implement scores being displayed "in-place" in the game screen, bullets (probably) and so forth.
  30. /// </summary>
  31. public abstract class GameObject
  32. {
  33. public delegate void GameObjectFrameFunction(GameObject sourceobject, BCBlockGameState gamestate);
  34. public event GameObjectFrameFunction ObjectFrame;
  35. protected void InvokeFrameEvent(BCBlockGameState gamestate)
  36. {
  37. var copied = ObjectFrame;
  38. if (copied != null)
  39. copied(this, gamestate);
  40. }
  41. protected GameObject()
  42. {
  43. }
  44. public override string ToString()
  45. {
  46. return base.ToString() + "\n" +
  47. "Frozen:" + this.Frozen + "\n";
  48. }
  49. //Note: had to do a bit of hackney-ing here to figure out how best to do this. Ball and Blocks have parameters indicating objects to remove,, so I designed it that way for the
  50. //GameObjects from the start.
  51. /// <summary>
  52. /// used to perform a single frame of this gameobjects animation.
  53. /// </summary>
  54. /// <param name="gamestate">Game State object</param>
  55. /// <returns>true to indicate that this gameobject should be removed. False otherwise.</returns>
  56. public virtual bool PerformFrame(BCBlockGameState gamestate)
  57. {
  58. InvokeFrameEvent(gamestate);
  59. return false;
  60. }
  61. private bool _frozen = false;
  62. public virtual bool getFrozen()
  63. {
  64. return _frozen;
  65. }
  66. public virtual void setFrozen(bool newvalue)
  67. {
  68. _frozen = newvalue;
  69. }
  70. /// <summary>
  71. /// if True, means this Object will not animate while the game is paused (enemies, for example).
  72. /// false means it will, which could be desirable for other effects. Derived classes can hook get/set access by overriding
  73. /// the virtual setFrozen and getFrozen methods.
  74. /// </summary>
  75. /// <returns></returns>
  76. public bool Frozen { get { return getFrozen(); } set { setFrozen(value); } }
  77. public abstract void Draw(Graphics g);
  78. public static double Angle(double px1, double py1, double px2, double py2)
  79. {
  80. // Negate X and Y values
  81. double pxRes = px2 - px1;
  82. double pyRes = py2 - py1;
  83. double angle = 0.0;
  84. double drawangle = 0;
  85. const double drawangleincrement = Math.PI / 20;
  86. // Calculate the angle
  87. if (pxRes == 0.0)
  88. {
  89. if (pxRes == 0.0)
  90. angle = 0.0;
  91. else if (pyRes > 0.0) angle = System.Math.PI / 2.0;
  92. else
  93. angle = System.Math.PI * 3.0 / 2.0;
  94. }
  95. else if (pyRes == 0.0)
  96. {
  97. if (pxRes > 0.0)
  98. angle = 0.0;
  99. else
  100. angle = System.Math.PI;
  101. }
  102. else
  103. {
  104. if (pxRes < 0.0)
  105. angle = System.Math.Atan(pyRes / pxRes) + System.Math.PI;
  106. else if (pyRes < 0.0) angle = System.Math.Atan(pyRes / pxRes) + (2 * System.Math.PI);
  107. else
  108. angle = System.Math.Atan(pyRes / pxRes);
  109. }
  110. // Convert to degrees
  111. return angle;
  112. }
  113. }
  114. public abstract class SizeableGameObject : GameObject, iLocatable
  115. {
  116. protected PointF _Location;
  117. public SizeF Size { get; set; }
  118. public PointF Location { get { return _Location; } set { _Location = value; } }
  119. protected SizeableGameObject(PointF pLocation, SizeF objectsize)
  120. {
  121. Size = objectsize;
  122. Location = pLocation;
  123. }
  124. public PointF CenterPoint()
  125. {
  126. return new PointF(Location.X + (Size.Width / 2), Location.Y + (Size.Height / 2));
  127. }
  128. public RectangleF getRectangle()
  129. {
  130. return new RectangleF(Location, Size);
  131. }
  132. }
  133. public class AnimatedImageObject : SizeableGameObject
  134. {
  135. public Image[] ObjectImages;
  136. protected int frameadvancedelay = 3;
  137. protected int countframe = 0;
  138. protected int currimageframe = 0;
  139. protected PointF _Velocity;
  140. public PointF Velocity { get { return _Velocity; } set { _Velocity = value; } }
  141. protected VelocityChanger _VelocityChange = new VelocityChangerLinear();
  142. public VelocityChanger VelocityChange
  143. {
  144. get { return _VelocityChange; }
  145. set { _VelocityChange = value; }
  146. }
  147. public int CurrentFrame { get { return currimageframe; } set { currimageframe = value; } }
  148. public Image CurrentFrameImage
  149. {
  150. get
  151. {
  152. try
  153. {
  154. return ObjectImages[CurrentFrame];
  155. }
  156. catch (IndexOutOfRangeException erange)
  157. {
  158. Debug.Print("stop");
  159. }
  160. return null;
  161. }
  162. }
  163. public AnimatedImageObject(PointF Location, SizeF ObjectSize, Image[] pObjectImages, int pframeadvancedelay)
  164. : base(Location, ObjectSize)
  165. {
  166. ObjectImages = pObjectImages;
  167. frameadvancedelay = pframeadvancedelay;
  168. }
  169. public AnimatedImageObject(PointF Location, SizeF ObjectSize, Image[] pObjectImages)
  170. : this(Location, ObjectSize, pObjectImages, 3)
  171. {
  172. }
  173. public override bool PerformFrame(BCBlockGameState gamestate)
  174. {
  175. //throw new NotImplementedException();
  176. Location = _VelocityChange.PerformFrame(gamestate,Location);
  177. if (ObjectImages.Length == 1) return false;
  178. if (frameadvancedelay > 0)
  179. {
  180. countframe++;
  181. if (countframe >= frameadvancedelay)
  182. {
  183. currimageframe++;
  184. if (currimageframe > ObjectImages.Length - 1) currimageframe = 0;
  185. countframe = 0;
  186. }
  187. }
  188. return false;
  189. }
  190. public override void Draw(Graphics g)
  191. {
  192. g.DrawImage(ObjectImages[currimageframe], Location.X, Location.Y, Size.Width, Size.Height);
  193. }
  194. }
  195. public class GameImageObject : SizeableGameObject
  196. {
  197. public Image ObjectImage = null;
  198. public GameImageObject(PointF Location, SizeF ObjectSize, Image ImageUse)
  199. : base(Location, ObjectSize)
  200. {
  201. ObjectImage = ImageUse;
  202. }
  203. public GameImageObject(PointF Location, Image ImageUse)
  204. : base(Location, ImageUse.Size)
  205. {
  206. ObjectImage = ImageUse;
  207. }
  208. public override bool PerformFrame(BCBlockGameState gamestate)
  209. {
  210. return false;
  211. }
  212. public override void Draw(Graphics g)
  213. {
  214. g.DrawImage(ObjectImage, Location.X, Location.Y, Size.Width, Size.Height);
  215. }
  216. }
  217. /// <summary>
  218. /// Interface implemented by objects that have a location. bloody near everything, ideally.
  219. /// </summary>
  220. public interface iLocatable
  221. {
  222. PointF Location { get; set; }
  223. }
  224. public interface iProjectile : iLocatable
  225. {
  226. PointF Velocity { get; set; }
  227. }
  228. /// <summary>
  229. /// Interface that is used for all objects that have a size, location, and speed.
  230. /// (Not necessarily limited to projectiles)
  231. /// </summary>
  232. public interface iSizedProjectile : iProjectile
  233. {
  234. SizeF Size { get; set; }
  235. }
  236. //implemented by all projectiles. Or, it should be.
  237. public abstract class Projectile : GameObject, iProjectile
  238. {
  239. protected PointF _Location;
  240. protected PointF _Velocity;
  241. protected PointF _VelocityDecay = new PointF(1, 1);
  242. protected PointF _VelocityIncrement = new PointF(0, 0);
  243. public PointF Location
  244. {
  245. get { return getLocation(); }
  246. set { setLocation(value); }
  247. }
  248. public PointF Velocity
  249. {
  250. get { return getVelocity(); }
  251. set { setVelocity(value); }
  252. }
  253. public PointF VelocityDecay
  254. {
  255. get { return getVelocityDecay(); }
  256. set { setVelocityDecay(value); }
  257. }
  258. public PointF VelocityIncrement
  259. {
  260. get { return getVelocityIncrement(); }
  261. set { setVelocityIncrement(value); }
  262. }
  263. protected virtual PointF getLocation()
  264. {
  265. return _Location;
  266. }
  267. protected virtual void setLocation(PointF newLocation)
  268. {
  269. _Location = newLocation;
  270. }
  271. protected virtual PointF getVelocity()
  272. {
  273. return _Velocity;
  274. }
  275. protected virtual void setVelocity(PointF newVelocity)
  276. {
  277. _Velocity = newVelocity;
  278. }
  279. protected virtual PointF getVelocityDecay()
  280. {
  281. return _VelocityDecay;
  282. }
  283. protected virtual void setVelocityDecay(PointF newVelocityDecay)
  284. {
  285. _VelocityDecay = newVelocityDecay;
  286. }
  287. protected virtual PointF getVelocityIncrement()
  288. {
  289. return _VelocityIncrement;
  290. }
  291. protected virtual void setVelocityIncrement(PointF setvalue)
  292. {
  293. _VelocityIncrement = setvalue;
  294. }
  295. protected Projectile(PointF pLocation, PointF pVelocity)
  296. {
  297. _Location = pLocation;
  298. _Velocity = pVelocity;
  299. }
  300. public override bool PerformFrame(BCBlockGameState gamestate)
  301. {
  302. _Location = new PointF(_Location.X + _Velocity.X, _Location.Y + _Velocity.Y);
  303. _Velocity = new PointF(_Velocity.X + _VelocityIncrement.X, _Velocity.Y + _VelocityIncrement.Y);
  304. _Velocity = new PointF(_Velocity.X * _VelocityDecay.X, _Velocity.Y * _VelocityDecay.Y);
  305. return gamestate.GameArea.Contains((int)_Location.X, (int)_Location.Y) || base.PerformFrame(gamestate);
  306. }
  307. }
  308. public abstract class SizedProjectile : Projectile, iSizedProjectile
  309. {
  310. //a projectile with a size... this is measured from the "center" which we take to be the _Location inherited field.
  311. protected SizeF _Size;
  312. public SizeF Size
  313. {
  314. get { return getSize(); }
  315. set { setSize(value); }
  316. }
  317. protected virtual SizeF getSize()
  318. {
  319. return _Size;
  320. }
  321. protected virtual void setSize(SizeF newsize)
  322. {
  323. _Size = newsize;
  324. }
  325. protected SizedProjectile(PointF pLocation, PointF pVelocity, SizeF size)
  326. : base(pLocation, pVelocity)
  327. {
  328. _Size = size;
  329. }
  330. public RectangleF getRect()
  331. {
  332. return new RectangleF(_Location.X - _Size.Width / 2, _Location.Y - _Size.Height / 2, _Size.Width, _Size.Height);
  333. }
  334. public override bool PerformFrame(BCBlockGameState gamestate)
  335. {
  336. return base.PerformFrame(gamestate);
  337. }
  338. }
  339. //Orb that draws an image in a specific colour.
  340. public class HitscanBullet : Projectile
  341. {
  342. public bool Penetrate = false; //whether we penetrate blocks.
  343. public bool Tracer = true;
  344. public enum HitscanStrengthConstants
  345. {
  346. /// <summary>
  347. /// ignore. Does not hit blocks.
  348. /// </summary>
  349. hitscan_ignore,
  350. /// <summary>
  351. /// acts like a bullet. when it encounters a block it spawns a bullet at that location. the shot stops there.
  352. /// </summary>
  353. hitscan_bullet,
  354. /// <summary>
  355. /// hits the first block it touches. The block may or may not be destroyed, depending on the block itself.
  356. /// </summary>
  357. hitscan_hit,
  358. }
  359. Color _BulletColor = Color.Green;
  360. private PointF _Origin;
  361. private HitscanStrengthConstants _Strength = HitscanStrengthConstants.hitscan_bullet;
  362. public HitscanStrengthConstants Strength { get { return _Strength; } set { _Strength = value; } }
  363. public PointF Origin { get { return _Origin; } set { _Origin = value; } }
  364. public Color BulletColor { get { return _BulletColor; } set { _BulletColor = value; } }
  365. //public PointF Velocity { get { return _Velocity; } set { _Velocity = value; } }
  366. public HitscanBullet(PointF pOrigin, PointF pVelocity):base(pOrigin,pVelocity)
  367. {
  368. _Origin = pOrigin;
  369. }
  370. public override bool PerformFrame(BCBlockGameState gamestate)
  371. {
  372. //hitscan. We only "take" a single frame to hit something.
  373. //first, normalize the Velocity.
  374. _Velocity = _Velocity.Normalize();
  375. //now, starting from _Origin, advance position by _Velocity until we hit a block or leave the gamearea.
  376. List<Block> blockshit = new List<Block>();
  377. PointF currpos = _Origin;
  378. PointF lastpos = _Origin;
  379. bool scancomplete = false;
  380. int spawnjump = 0;
  381. int particlecomparator = Math.Max(gamestate.Particles.Count-500, 2);
  382. //particlecomparator is our modulus. This will be modulo'd with spawnjump each iteration
  383. while (!scancomplete)
  384. {
  385. spawnjump++;
  386. currpos = new PointF(currpos.X + _Velocity.X, currpos.Y + _Velocity.Y);
  387. PointF diff = new PointF(currpos.X-lastpos.X,currpos.Y-lastpos.Y);
  388. //spawn some particles here.
  389. if (spawnjump>particlecomparator && Tracer)
  390. {
  391. spawnjump = 0;
  392. for (int i = 0; i < 1; i++)
  393. {
  394. float randomspot = (float)BCBlockGameState.rgen.NextDouble();
  395. PointF randomoffset = new PointF(currpos.X + diff.X * randomspot, currpos.Y + diff.Y * randomspot);
  396. if (BCBlockGameState.rgen.NextDouble() > 0.5)
  397. {
  398. DustParticle dp = new DustParticle(randomoffset, 3, 25, _BulletColor);
  399. dp.Important = true;
  400. gamestate.Particles.Add(dp);
  401. }
  402. else
  403. {
  404. LightOrb dp = new LightOrb(randomoffset, Color.Green, 16);
  405. dp.TTL = 25;
  406. dp.Important = true;
  407. gamestate.Particles.Add(dp);
  408. }
  409. }
  410. }
  411. //are we outside gamearea?
  412. if (!gamestate.GameArea.Contains(currpos.ToPoint()))
  413. scancomplete = true;
  414. //have we hit a block?
  415. var hitblock = BCBlockGameState.Block_HitTestOne(gamestate.Blocks, currpos);
  416. if (hitblock != null && !blockshit.Contains(hitblock))
  417. {
  418. blockshit.Add(hitblock);
  419. if(_Strength == HitscanStrengthConstants.hitscan_bullet)
  420. {
  421. //create a bullet at currpos, make it go in our direction.
  422. Bullet bb = new Bullet(currpos, _Velocity, false);
  423. bb.BulletBrush = new SolidBrush(Color.Transparent); //invisible bullet...
  424. gamestate.NextFrameCalls.Enqueue(new BCBlockGameState.NextFrameStartup(() => gamestate.GameObjects.AddLast(bb)));
  425. }
  426. else if(_Strength==HitscanStrengthConstants.hitscan_hit)
  427. {
  428. gamestate.NextFrameCalls.Enqueue(new BCBlockGameState.NextFrameStartup(() => BCBlockGameState.Block_Hit(gamestate, hitblock, _Velocity)));
  429. }
  430. if (!Penetrate) scancomplete = true;
  431. }
  432. lastpos = currpos;
  433. }
  434. if (!Tracer)
  435. {
  436. DustParticle pa = new DustParticle(Origin, 800,9000,Color.Transparent);
  437. DustParticle pb = new DustParticle(currpos,800,9000,Color.Transparent);
  438. pa.Velocity = PointF.Empty;
  439. pb.Velocity = PointF.Empty;
  440. LineParticle ls = new LineParticle(pa, pb,new Pen(Color.Yellow,1));
  441. ls.Important = true;
  442. ls.TTL = 750;
  443. gamestate.Particles.Add(ls);
  444. //show the impact point.
  445. PointF Impactpoint = currpos;
  446. for (int i = 0; i < 14 * BCBlockGameState.Settings.ParticleGenerationFactor; i++)
  447. {
  448. Particle xp = BCBlockGameState.rgen.NextDouble() > 0.5 ?
  449. (Particle)
  450. new EmitterParticle(Impactpoint,(gstate,emitter,aint,bint)=>
  451. {
  452. float ssspeed = (float)BCBlockGameState.rgen.NextDouble() * 5 + 2;
  453. PointF ssusespeed = BCBlockGameState.VaryVelocity(new PointF(0,-ssspeed), Math.PI / 10);
  454. DustParticle addit = new DustParticle(emitter.Location, 40);
  455. addit.Velocity = ssusespeed;
  456. return addit;
  457. })
  458. : (Particle)new DustParticle(Impactpoint);
  459. float speed = (float)BCBlockGameState.rgen.NextDouble() * 5 + 2;
  460. PointF usespeed = BCBlockGameState.VaryVelocity(new PointF(0,-speed), Math.PI / 10);
  461. xp.Velocity = usespeed;
  462. gamestate.Particles.Add(xp);
  463. }
  464. }
  465. return true; //we only exist for one frame.
  466. }
  467. public override void Draw(Graphics g)
  468. {
  469. //no implementation...
  470. }
  471. }
  472. /// <summary>
  473. /// SpinShot: a Large (8) ball, surrounded by a set of smaller balls.
  474. /// On construction: create the appropriate balls; add them to the GameState; and hook their BlockHit event.
  475. /// when a ball hit's a block,set it's velocity to the appropriate speed, and stop "managing" it from here.
  476. /// //when the middle ball hit's something (or button is pressed...) release all the small balls.
  477. /// </summary>
  478. public class SpinShot : Projectile
  479. {
  480. #region inner class
  481. private class SpinShotsubBallInfo
  482. {
  483. //holds data about one of the spinshot's rotating balls.
  484. /// <summary>
  485. /// The ball object itself.
  486. /// </summary>
  487. public cBall BallObject { get; set; }
  488. /// <summary>
  489. /// current angle of the ball (in relation to the origin ball)
  490. /// </summary>
  491. public double Angle { get; set; }
  492. /// <summary>
  493. /// Current speed of the ball's rotation.
  494. /// </summary>
  495. public double AngleSpeed { get; set; }
  496. //calculated distance from the origin.
  497. public double Radius { get; set; }
  498. /// <summary>
  499. /// "Origin" ball; the ball upon which our calculations are based.
  500. /// </summary>
  501. public cBall OriginBall { get; set; }
  502. /// <summary>
  503. ///
  504. /// </summary>
  505. /// <param name="Origin">cBall object to be used as the origin location for rotary calculations.</param>
  506. /// <param name="Derived"></param>
  507. public SpinShotsubBallInfo(cBall pOrigin, cBall pDerived, double pAngle, double pAngleSpeed, double pRadius)
  508. {
  509. OriginBall = pOrigin;
  510. BallObject = pDerived;
  511. Angle = pAngle;
  512. AngleSpeed = pAngleSpeed;
  513. Radius = pRadius;
  514. //hook our ball's BallImpact Event...
  515. BallObject.BallImpact += new cBall.ballimpactproc(BallObject_BallImpact);
  516. //the "owner" class should be hooking it as well so that we (as a SpinShotsubballinfo) are removed from any relevant future calculations,
  517. //allowing the ball to "fly off".
  518. }
  519. /// <summary>
  520. /// retrieves the current speed vector to be used for the ball, based on radius, origin, and angle.
  521. /// </summary>
  522. /// <returns></returns>
  523. private PointF getSpeedVector()
  524. {
  525. //speed vector will be the difference between our current position and what will be our next position.
  526. //get the two angle we will use...
  527. double currentangle = Angle;
  528. double nextangle = currentangle + AngleSpeed;
  529. //acquire the positions
  530. PointF currpos = getPosition();
  531. PointF nextpos = calcPosition(nextangle, Radius);
  532. //get the difference, and return that as a PointF structure.
  533. return new PointF((float)(nextpos.X - currpos.X), (float)(nextpos.Y - currpos.Y));
  534. }
  535. //retrieves the current position to be used for the ball, based on Origin, Angle, and radius.
  536. private PointF getPosition()
  537. {
  538. return calcPosition(Angle, Radius);
  539. }
  540. private PointF calcPosition(double Angle, double Radius)
  541. {
  542. //Debug.Print("Originball:" + OriginBall.Location);
  543. PointF resultp = new PointF((float)((Math.Cos(Angle) * Radius) + OriginBall.Location.X),
  544. (float)((Math.Sin(Angle) * Radius) + OriginBall.Location.Y));
  545. return resultp;
  546. }
  547. public void ReleaseBall()
  548. {
  549. PointF gotv = getSpeedVector();
  550. BallObject.Velocity = new PointF(gotv.X * 10, gotv.Y * 10);
  551. BallObject.BallImpact -= BallObject_BallImpact;
  552. BallObject = null;
  553. }
  554. void BallObject_BallImpact(cBall ballimpact)
  555. {
  556. //release the ball... Set it's Speed to getSpeedVector.
  557. ballimpact.Velocity = getSpeedVector();
  558. //"owner" SpinShot will properly 'release' the ball by removing us from the collection.
  559. }
  560. public void PerformFrame(BCBlockGameState gamestate)
  561. {
  562. //performs a single frame; we only move the ball, though- we don't actually affect it's velocity...
  563. BallObject.Velocity = getSpeedVector();
  564. Angle += AngleSpeed;
  565. BallObject.Location = getPosition();
  566. }
  567. }
  568. #endregion
  569. private List<SpinShotsubBallInfo> SubObjects;
  570. public cBall OurBall;
  571. public SpinShot(BCBlockGameState gamestate, PointF pLocation, int Numorbit, float radius, float rotationspeed, PointF pVelocity)
  572. : this(gamestate, new cBall(pLocation, pVelocity), pLocation, Numorbit, radius, rotationspeed)
  573. {
  574. LaserSpinBehaviour SpinshotSpinBehaviour = new LaserSpinBehaviour(new TimeSpan(0, 0, 0, 0, 300));
  575. SpinshotSpinBehaviour.EventFunction = SpinShotLaser;
  576. OurBall.Behaviours.Add(SpinshotSpinBehaviour);
  577. OurBall.Radius = 5;
  578. OurBall.DrawColor = Color.Blue;
  579. OurBall.Behaviours.Add(new TempBallBehaviour());
  580. }
  581. private void SpinShotLaser(BCBlockGameState gstate, LaserShot shot, LinkedList<PointF> laserPoints)
  582. {
  583. }
  584. protected override PointF getLocation()
  585. {
  586. return OurBall.Location;
  587. }
  588. protected override void setLocation(PointF newlocation)
  589. {
  590. OurBall.Location = newlocation;
  591. }
  592. protected override PointF getVelocity()
  593. {
  594. return OurBall.Velocity;
  595. }
  596. protected override void setVelocity(PointF newVelocity)
  597. {
  598. OurBall.Velocity = newVelocity;
  599. }
  600. public void SparkleEmitter(cBall ballobj, BCBlockGameState gamestate)
  601. {
  602. PointF VelocityUse = new PointF((float)(ballobj.Velocity.X * 0.1 + (BCBlockGameState.rgen.NextDouble() - 0.5) * 2),
  603. (float)(ballobj.Velocity.Y * 0.1 + (BCBlockGameState.rgen.NextDouble() - 0.5) * 2));
  604. Color usesparklecolor = BCBlockGameState.Choose(new Color[] { Color.Red, Color.Yellow, Color.LimeGreen, Color.Pink, Color.Magenta, Color.Chartreuse });
  605. if (BCBlockGameState.rgen.NextDouble() > 0.8) return; //don't add a particle.
  606. if (BCBlockGameState.rgen.NextDouble() > 0.5)
  607. {
  608. VelocityUse = new PointF(0, 0);
  609. }
  610. gamestate.Particles.Add(new Sparkle(ballobj.Location, VelocityUse, usesparklecolor));
  611. }
  612. protected static TextureBrush spinshotbrush = new TextureBrush(BCBlockGameState.Imageman.getLoadedImage("spinshotbg"));
  613. /// <summary>
  614. /// Creates the Spinshot, using the given ball as the origin
  615. /// </summary>
  616. /// <param name="gamestate"></param>
  617. /// <param name="useorigin"></param>
  618. /// <param name="pLocation"></param>
  619. /// <param name="Numorbit"></param>
  620. /// <param name="radius"></param>
  621. /// <param name="rotationspeed"></param>
  622. /// <param name="pVelocity"></param>
  623. public SpinShot(BCBlockGameState gamestate, cBall useorigin, PointF pLocation, int Numorbit, float radius, float rotationspeed)
  624. : base(pLocation, useorigin.Velocity)
  625. {
  626. SubObjects = new List<SpinShotsubBallInfo>();
  627. OurBall = useorigin;
  628. OurBall.BallImpact += new cBall.ballimpactproc(OurBall_BallImpact);
  629. gamestate.Balls.AddLast(OurBall);
  630. //we let the game logic handle the ball, but our performframe will "update" the position of the sub-balls.
  631. //speaking of which- we need to create those.
  632. float Angleuse = (float)((Math.PI * 2) / Numorbit);
  633. float useangle = 0;
  634. for (int i = 0; i < Numorbit; i++)
  635. {
  636. useangle = ((float)i) * Angleuse;
  637. PointF useposition = new PointF((float)(Math.Cos(useangle) * radius) + pLocation.X,
  638. (float)(Math.Sin(useangle) * radius) + pLocation.Y);
  639. cBall derivedball = new cBall(useposition, new PointF(0.1f, 0.1f));
  640. //derivedball.Behaviours.Add(new TempBallBehaviour());
  641. derivedball.Behaviours.Add(new NonReboundableBallBehaviour());
  642. derivedball.Behaviours.Add(new ParticleEmitterBehaviour(SparkleEmitter));
  643. derivedball.DrawPen = new Pen(Color.Transparent);
  644. derivedball.DrawBrush = spinshotbrush;
  645. SpinShotsubBallInfo subinfo = new SpinShotsubBallInfo(OurBall, derivedball, useangle, rotationspeed, radius);
  646. SubObjects.Add(subinfo);
  647. gamestate.Balls.AddLast(derivedball);
  648. }
  649. }
  650. void OurBall_BallImpact(cBall ballimpact)
  651. {
  652. //throw new NotImplementedException();
  653. hasbeendestroyed = true;
  654. //release All the balls in our subobjects.
  655. foreach (var subobj in SubObjects)
  656. {
  657. subobj.ReleaseBall();
  658. }
  659. SubObjects = new List<SpinShotsubBallInfo>();
  660. }
  661. bool hasbeendestroyed = false;
  662. public override bool PerformFrame(BCBlockGameState gamestate)
  663. {
  664. spinshotbrush.RotateTransform((float)(Math.PI * 2 * (BCBlockGameState.rgen.NextDouble())));
  665. Debug.Print("Spinshot Ball Location:" + OurBall.Location);
  666. foreach (var subobj in SubObjects)
  667. {
  668. subobj.PerformFrame(gamestate);
  669. }
  670. if (hasbeendestroyed)
  671. {
  672. OurBall.BallImpact -= OurBall_BallImpact;
  673. foreach (var subobj in SubObjects)
  674. {
  675. subobj.ReleaseBall();
  676. }
  677. SubObjects = new List<SpinShotsubBallInfo>();
  678. gamestate.Defer(() => gamestate.GameObjects.Remove(this));
  679. }
  680. return hasbeendestroyed;
  681. }
  682. public override void Draw(Graphics g)
  683. {
  684. //nothing...
  685. }
  686. }
  687. /// <summary>
  688. /// Class that represents a rectangular object that destroys anything in it's path.
  689. /// </summary>
  690. public class BoxDestructor : Projectile
  691. {
  692. private Image AlphadImage = null;
  693. private Image useDrawImage = null;
  694. private SizeF ObjectSize;
  695. public delegate bool DestructableTestFunction(Block testblock);
  696. private DestructableTestFunction _DestructableTest = DestructableTest_All;
  697. public DestructableTestFunction DestructableTest
  698. {
  699. get { return _DestructableTest; }
  700. set
  701. {
  702. if (value == null)
  703. _DestructableTest = DestructableTest_All;
  704. else
  705. _DestructableTest = value;
  706. }
  707. }
  708. public RectangleF ObjectRectangle
  709. {
  710. get
  711. {
  712. return new RectangleF(Location.X - ObjectSize.Width / 2, Location.Y - ObjectSize.Height / 2,
  713. ObjectSize.Width, ObjectSize.Height);
  714. }
  715. set
  716. {
  717. Location = value.CenterPoint();
  718. ObjectSize = value.Size;
  719. }
  720. }
  721. /// <summary>
  722. /// The Block that this BoxDestructor was created to represent (or null if created some other way)
  723. /// </summary>
  724. protected Block OriginalBlock = null;
  725. private List<Block> ignorelist = new List<Block>();
  726. public static bool DestructableTest_Default(Block testthis)
  727. {
  728. return testthis.MustDestroy();
  729. }
  730. public static bool DestructableTest_All(Block testthis)
  731. {
  732. return true;
  733. }
  734. public BoxDestructor(Block basedon, PointF Velocity)
  735. : this(basedon, Velocity, new PointF(1f, 1f))
  736. {
  737. }
  738. private static ImageAttributes CachedDefaultAttributes = null;
  739. /// <summary>
  740. /// returns the Default ImageAttributes that will be used if none are supplied in the Constructor.
  741. /// </summary>
  742. /// <returns></returns>
  743. protected static ImageAttributes GetDefaultDestructorAttributes()
  744. {
  745. if (CachedDefaultAttributes == null)
  746. {
  747. CachedDefaultAttributes = new ImageAttributes();
  748. CachedDefaultAttributes.SetColorMatrix(ColorMatrices.GetColourizer(1, 1, 1, 0.5f));
  749. }
  750. return CachedDefaultAttributes;
  751. }
  752. public BoxDestructor(Block basedon, PointF Velocity, PointF AccelerationFactor)
  753. : this(basedon, Velocity, AccelerationFactor, GetDefaultDestructorAttributes())
  754. {
  755. }
  756. public BoxDestructor(Block basedon, PointF Velocity, PointF AccelerationFactor, ImageAttributes AffectImageAttributes)
  757. : base(basedon.CenterPoint(), Velocity)
  758. {
  759. _VelocityDecay = AccelerationFactor;
  760. ObjectSize = basedon.BlockSize;
  761. ignorelist.Add(basedon);
  762. useDrawImage = basedon.DrawToImage();
  763. ImageAttributes alphaizer = new ImageAttributes();
  764. alphaizer.SetColorMatrix(ColorMatrices.GetColourizer(1, 1, 1, 0.5f));
  765. AlphadImage = BCBlockGameState.AppyImageAttributes(useDrawImage, alphaizer);
  766. OriginalBlock = basedon;
  767. }
  768. private bool TouchesBlock(Block testblock)
  769. {
  770. return ObjectRectangle.IntersectsWith(testblock.BlockRectangle);
  771. }
  772. private bool isBlockDestructable(Block testblock)
  773. {
  774. //
  775. return testblock != OriginalBlock && DestructableTest(testblock);
  776. }
  777. public override bool PerformFrame(BCBlockGameState gamestate)
  778. {
  779. List<Block> removethem = new List<Block>();
  780. foreach (Block iterateblock in gamestate.Blocks)
  781. {
  782. if (TouchesBlock(iterateblock) && iterateblock != OriginalBlock)
  783. {
  784. if (isBlockDestructable(iterateblock))
  785. {
  786. gamestate.Forcerefresh = true;
  787. if (!ignorelist.Contains(iterateblock))
  788. {
  789. var currobjects = gamestate.GameObjects;
  790. gamestate.GameObjects = new LinkedList<GameObject>();
  791. BCBlockGameState.Block_Hit(gamestate, iterateblock,Velocity);
  792. //AddObjects.AddRange(gamestate.GameObjects);
  793. //gamestate.Defer(()=>gamestate.GameObjects.A
  794. gamestate.GameObjects = currobjects;
  795. removethem.Add(iterateblock);
  796. }
  797. }
  798. else
  799. {
  800. //the block isn't destructable. We need to be destroyed.
  801. gamestate.Defer(() => gamestate.GameObjects.Remove(this));
  802. }
  803. }
  804. }
  805. foreach (Block removeit in removethem)
  806. {
  807. gamestate.Blocks.Remove(removeit);
  808. }
  809. base.PerformFrame(gamestate);
  810. return !gamestate.GameArea.Contains(ObjectRectangle.Corners());
  811. }
  812. public override void Draw(Graphics g)
  813. {
  814. //throw new NotImplementedException();
  815. g.DrawImage(AlphadImage, ObjectRectangle.Left - Velocity.X, ObjectRectangle.Top - Velocity.Y);
  816. g.DrawImage(useDrawImage, ObjectRectangle.Left, ObjectRectangle.Top);
  817. }
  818. }
  819. /// <summary>
  820. /// This is another "proxy" type of object, which doesn't actually have a representation in the game
  821. /// but instead manages two other existing gameobjects to provide a specific behaviour.
  822. /// In this case, it manages two BoxDestructor Objects to provide the attraction/repulsion behaviour used
  823. /// by the attractrepulseBlock.
  824. /// </summary>
  825. public class AttractRepulseDestructor : GameObject
  826. {
  827. protected BoxDestructor[] Aggregates;
  828. protected AttractionRepulsionBlock[] OriginalBlocks;
  829. private const float defaultinitialspeed = 0.05f;
  830. public AttractRepulseDestructor(BCBlockGameState gstate, AttractionRepulsionBlock BlockA, AttractionRepulsionBlock BlockB)
  831. {
  832. //calculate the appropriate vectors.
  833. OriginalBlocks = new AttractionRepulsionBlock[] { BlockA, BlockB };
  834. PointF midpoint = BCBlockGameState.MidPoint(BlockA.CenterPoint(), BlockB.CenterPoint());
  835. double AtoB = BCBlockGameState.GetAngle(BlockA.CenterPoint(), midpoint);
  836. double BtoA = BCBlockGameState.GetAngle(BlockB.CenterPoint(), midpoint);
  837. double AuseAngle = BtoA; //default to "repel"
  838. double BuseAngle = AtoB;
  839. //if they are not the same colour...
  840. if (!(BlockA.BlockColor.R == BlockB.BlockColor.R &&
  841. BlockA.BlockColor.G == BlockB.BlockColor.G &&
  842. BlockA.BlockColor.B == BlockB.BlockColor.B))
  843. BCBlockGameState.Swap(ref AuseAngle, ref BuseAngle);
  844. //swap, if the colour RGB triplets are unequal we want to "attract" rather than repel.
  845. //their initial speed will be zero.
  846. Aggregates = new BoxDestructor[] { new BoxDestructor(BlockA, PointF.Empty), new BoxDestructor(BlockB, PointF.Empty) };
  847. //but we will change their 'velocitydecay' 0.05 for now, in the appropriate direction.
  848. Aggregates[0].VelocityDecay = new PointF((float)Math.Cos(AuseAngle) * defaultinitialspeed,
  849. (float)(Math.Sin(AuseAngle) * defaultinitialspeed));
  850. Aggregates[1].VelocityDecay = new PointF((float)(Math.Cos(BuseAngle) * defaultinitialspeed),
  851. (float)(Math.Sin(BuseAngle) * defaultinitialspeed));
  852. Aggregates[0].VelocityIncrement = Aggregates[0].VelocityDecay;
  853. Aggregates[1].VelocityIncrement = Aggregates[1].VelocityDecay;
  854. Aggregates[0].VelocityDecay = new PointF(1 + Math.Abs(Aggregates[0].VelocityDecay.X), 1 + Math.Abs(Aggregates[0].VelocityDecay.Y));
  855. Aggregates[1].VelocityDecay = new PointF(1 + Math.Abs(Aggregates[1].VelocityDecay.X), 1 + Math.Abs(Aggregates[1].VelocityDecay.Y));
  856. //now we need to add the BoxDestructor to the gamestate.
  857. //we do not remove the Blocks, instead deferring that to the block itself to decide.
  858. //it is also the caller's responsibility to add this instance to the GameObjects list as well.
  859. gstate.Defer(() =>
  860. {
  861. gstate.GameObjects.AddLast(Aggregates[0]);
  862. gstate.GameObjects.AddLast(Aggregates[1]);
  863. });
  864. }
  865. public override bool PerformFrame(BCBlockGameState gamestate)
  866. {
  867. //What happens here?
  868. //not a whole lot, actually.
  869. //all we really need to check for is whether the two aggregates are "touching". If they are, create a explosion in that spot and remove them.
  870. if (Aggregates[0].ObjectRectangle.IntersectsWith(Aggregates[1].ObjectRectangle))
  871. {
  872. float explosionsize = (BCBlockGameState.Distance(PointF.Empty, Aggregates[0].Velocity) +
  873. BCBlockGameState.Distance(PointF.Empty, Aggregates[1].Velocity)) / 2;
  874. //intersection.
  875. //get the midpoint between the two centers...
  876. PointF explosionzero = BCBlockGameState.MidPoint(Aggregates[0].ObjectRectangle.CenterPoint(),
  877. Aggregates[1].ObjectRectangle.CenterPoint());
  878. BCBlockGameState.Soundman.PlaySound("bomb");
  879. ExplosionEffect explode = new ExplosionEffect(explosionzero, explosionsize);
  880. //add it...
  881. gamestate.Defer(()=> {
  882. gamestate.GameObjects.AddLast(explode);
  883. gamestate.GameObjects.Remove(Aggregates[0]);
  884. gamestate.GameObjects.Remove(Aggregates[1]);
  885. gamestate.GameObjects.Remove(this);
  886. });
  887. }
  888. return base.PerformFrame(gamestate);
  889. }
  890. public override void Draw(Graphics g)
  891. {
  892. //no code: we have no representation.
  893. //however, this could be used to draw "force lines" or something
  894. //between the two aggregate objects.
  895. }
  896. }
  897. public class BlueFireball : Fireball
  898. {
  899. //bluefireball is the same as the standard fireball, but will do a little tracking of the paddle.
  900. //private ImageAttributes useattributes= new ImageAttributes();
  901. private ColorMatrix cmuse;
  902. private QColorMatrix qcm = new QColorMatrix();
  903. public BlueFireball(BCBlockGameState currstate, PointF Location, SizeF ObjectSize, PointF initialSpeed)
  904. : base(currstate, Location, ObjectSize, initialSpeed)
  905. {
  906. Velocity = initialSpeed;
  907. }
  908. public BlueFireball(PointF pLocation, PointF pVelocity)
  909. : this(pLocation, pVelocity, new SizeF(8, 8))
  910. {
  911. }
  912. public BlueFireball(PointF pLocation, PointF pVelocity, SizeF pSize)
  913. : base(pLocation, pVelocity, pSize)
  914. {
  915. setattributes();
  916. }
  917. private void setattributes()
  918. {
  919. float[][] colorMatrixElements = {
  920. new float[] {1, 0, 0, 0, 0}, // red scaling factor of 2
  921. new float[] {0, 1, 0, 0, 0}, // green scaling factor of 1
  922. new float[] {0, 0, 2, 0, 0}, // blue scaling factor of 1
  923. new float[] {0, 0, 0, 1, 0}, // alpha scaling factor of 1
  924. new float[] {-0.5f, -0.5f, .8f, 0, 1}}; // three translations of 0.2
  925. cmuse = new ColorMatrix(colorMatrixElements);
  926. qcm = new QColorMatrix(cmuse);
  927. DrawAttributes = new ImageAttributes();
  928. DrawAttributes.SetColorMatrix(
  929. qcm.ToColorMatrix(),
  930. ColorMatrixFlag.Default,
  931. ColorAdjustType.Bitmap);
  932. DrawAttributes.SetColorMatrix(cmuse);
  933. }
  934. public BlueFireball(BCBlockGameState currstate, Block originBlock, float initialspeed)
  935. : this(currstate, originBlock.CenterPoint(), new SizeF(8, 8), initialspeed)
  936. {
  937. setattributes();
  938. }
  939. public override bool PerformFrame(BCBlockGameState gamestate)
  940. {
  941. /*
  942. qcm.RotateHue(0.1f);
  943. DrawAttributes.SetColorMatrix(
  944. qcm.ToColorMatrix(),
  945. ColorMatrixFlag.Default,
  946. ColorAdjustType.Bitmap);
  947. cmuse = qcm.ToColorMatrix();
  948. */
  949. //TODO: change velocity to go towards paddle, but only if there is a paddle.
  950. if (gamestate.PlayerPaddle != null)
  951. {
  952. if (Location.Y < gamestate.PlayerPaddle.Position.Y) //only change if we are above the paddle.
  953. {
  954. //step one: get angle between fireball and the middle of the paddle:
  955. double anglebetween = BCBlockGameState.GetAngle(CenterPoint(),
  956. gamestate.PlayerPaddle.Getrect().CenterPoint());
  957. //also, get the angle of our speed vector.
  958. double movementangle = BCBlockGameState.GetAngle(new PointF(0, 0), Velocity);
  959. double totalspeed = BCBlockGameState.Distance(0, 0, Velocity.X, Velocity.Y);
  960. //now, we want to move from movementangle towards anglebetween. 25% ought to work...
  961. //double distancemove = ((anglebetween - movementangle)*0.05);
  962. double anglevary = Math.Sign(anglebetween - movementangle) * (Math.PI / 90) * 0.25f;
  963. double changedangle = movementangle + anglevary;
  964. //now get the new velocity...
  965. PointF newspeed = new PointF((float)(Math.Cos(changedangle) * totalspeed),
  966. (float)(Math.Sin(changedangle) * totalspeed));
  967. Velocity = newspeed;
  968. }
  969. }
  970. return base.PerformFrame(gamestate);
  971. }
  972. public override void Draw(Graphics g)
  973. {
  974. base.Draw(g); //we DO need to call into it... (bugfix)
  975. //no longer needed since the change to the base class FireBall...
  976. /*
  977. var drawthis = CurrentFrameImage;
  978. g.DrawImage(drawthis, new Rectangle((int)Location.X, (int)Location.Y, (int)Size.Width, (int)Size.Height),
  979. 0, 0, drawthis.Width, drawthis.Height, GraphicsUnit.Pixel, useattributes);
  980. //g.DrawImage(CurrentFrameImage, new RectangleF(Location.X, Location.Y, Size.Width, Size.Height),GraphicsUnit.Pixel, useattributes);
  981. //g.DrawRectangle(new Pen(Color.Black, 1), new Rectangle((int)Location.X, (int)Location.Y, (int)Size.Width, (int)Size.Height));
  982. */
  983. }
  984. public BlueFireball(BCBlockGameState currstate, PointF Location, SizeF ObjectSize, float initialsize)
  985. : base(currstate, Location, ObjectSize, initialsize)
  986. {
  987. }
  988. }
  989. public class ExplosionShot : GameObject
  990. {
  991. protected PointF _Velocity, _Location;
  992. public PointF Velocity { get { return _Velocity; } set { _Velocity = value; } }
  993. public PointF Location { get { return _Location; } set { _Location = value; } }
  994. int framecounter = 0;
  995. int NextDeployment = 12;
  996. public override bool PerformFrame(BCBlockGameState gamestate)
  997. {
  998. BCBlockGameState.IncrementLocation(gamestate, ref _Location, Velocity);
  999. framecounter++;
  1000. if (framecounter >= NextDeployment)
  1001. {
  1002. framecounter = 0;
  1003. NextDeployment = BCBlockGameState.rgen.Next(10, 30);
  1004. ExplosionEffect ee = new ExplosionEffect(Location, 10 + (float)(BCBlockGameState.rgen.NextDouble() * 20));
  1005. ee.DamageBlocks = false; //doesn't hurt blocks...
  1006. ee.ShowOrbs = false;
  1007. gamestate.Defer(() =>
  1008. {
  1009. gamestate.GameObjects.AddLast(ee);
  1010. BCBlockGameState.Soundman.PlaySound("explode");
  1011. });
  1012. }
  1013. return !gamestate.GameArea.Contains(new Point((int)Location.X, (int)Location.Y));
  1014. }
  1015. public override void Draw(Graphics g)
  1016. {
  1017. //throw new NotImplemen…

Large files files are truncated, but you can click here to view the full file