PageRenderTime 68ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/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
  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 NotImplementedException();
  1018. g.FillEllipse(new SolidBrush(Color.Red), new RectangleF(Location.X - 2, Location.Y - 2, 4, 4));
  1019. }
  1020. public ExplosionShot(BCBlockGameState currstate, PointF pLocation, float initialspeed)
  1021. {
  1022. Location = pLocation;
  1023. Velocity = new PointF();
  1024. PointF targetposition;
  1025. //if the paddle exists...
  1026. if (currstate.PlayerPaddle != null)
  1027. targetposition = currstate.PlayerPaddle.Position;
  1028. else
  1029. {
  1030. //otherwise, choose a random spot.
  1031. Random genner = BCBlockGameState.rgen;
  1032. targetposition = new PointF((float)(currstate.GameArea.Left + (currstate.GameArea.Width * genner.NextDouble())),
  1033. (float)(currstate.GameArea.Top + (currstate.GameArea.Height * genner.NextDouble())));
  1034. }
  1035. PointF originPoint = Location;
  1036. double angleuse = Angle((double)Location.X, (double)Location.Y, (double)targetposition.X, (double)targetposition.Y);
  1037. float usespeed = initialspeed;
  1038. double degree = (Math.PI * 2) / 360;
  1039. angleuse += (degree * 2) - degree;
  1040. Velocity = new PointF((float)Math.Cos(angleuse) * usespeed,
  1041. (float)Math.Sin(angleuse) * usespeed);
  1042. }
  1043. }
  1044. public class Fireball : AnimatedImageObject, iSizedProjectile
  1045. {
  1046. public new PointF Velocity { get { return base.Velocity; } set { base.Velocity = value; } }
  1047. public float FireballDamage = 2f;
  1048. public double DrawAngle = 0;
  1049. public const double DrawAngleIncrement = 10;
  1050. protected ImageAttributes DrawAttributes = null;
  1051. public Fireball(BCBlockGameState currstate, Block originBlock, float initialspeed)
  1052. : this(currstate, originBlock.CenterPoint(), new SizeF(8, 8), initialspeed)
  1053. {
  1054. //Velocity = new PointF();
  1055. }
  1056. public Fireball(BCBlockGameState currstate, PointF Location, SizeF ObjectSize, PointF initialSpeed)
  1057. : base(Location, ObjectSize, BCBlockGameState.Imageman.getImageFrames("fireball"))
  1058. {
  1059. Velocity = initialSpeed;
  1060. }
  1061. public Fireball(PointF pLocation, PointF pVelocity, SizeF pSize):base(pLocation,pSize,BCBlockGameState.Imageman.getImageFrames("fireball"))
  1062. {
  1063. this.Velocity = pVelocity;
  1064. }
  1065. public Fireball(PointF pLocation, PointF pVelocity)
  1066. : this(pLocation, pVelocity, new SizeF(8, 8))
  1067. {
  1068. }
  1069. public Fireball(BCBlockGameState currstate, PointF Location, SizeF ObjectSize, float initialspeed)
  1070. : base(Location, ObjectSize, BCBlockGameState.Imageman.getImageFrames("fireball"))
  1071. {
  1072. Velocity = currstate.AimAtPaddle( Location, initialspeed);
  1073. }
  1074. private Rectangle getRect()
  1075. {
  1076. return new Rectangle(new Point((int)Location.X, (int)Location.Y), new Size((int)Size.Width, (int)Size.Height));
  1077. }
  1078. public override void Draw(Graphics g)
  1079. {
  1080. SizeF DrawSize = Size;
  1081. PointF DrawLocation = Location;
  1082. float DrawRotation = (float)DrawAngle;
  1083. //Debug.Print("DrawAngle=" + DrawRotation);
  1084. Image currimage = BCBlockGameState.Imageman.getLoadedImage("fireball1");
  1085. Matrix copyt = g.Transform;
  1086. g.TranslateTransform(DrawSize.Width / 2 + DrawLocation.X, DrawSize.Height / 2 + DrawLocation.Y);
  1087. g.RotateTransform(DrawRotation);
  1088. g.TranslateTransform(-DrawSize.Width / 2, -DrawSize.Height / 2);
  1089. DrawLocation = new PointF(0, 0);
  1090. if (DrawSize != null)
  1091. {
  1092. if (DrawAttributes == null)
  1093. {
  1094. //g.DrawImage(GetCurrentImage(), Location.X, Location.Y, DrawSize.Value.Width, DrawSize.Value.Height);
  1095. g.DrawImage(currimage, DrawLocation.X, DrawLocation.Y, DrawSize.Width, DrawSize.Height);
  1096. }
  1097. else
  1098. {
  1099. //g.DrawImage(BlockImage, new Rectangle((int)BlockRectangle.Left, (int)BlockRectangle.Top, (int)BlockRectangle.Width, (int)BlockRectangle.Height), 0f, 0f, BlockImage.Width, BlockImage.Height, GraphicsUnit.Pixel, DrawAttributes);
  1100. g.DrawImage(currimage, new Rectangle((int)DrawLocation.X, (int)DrawLocation.Y, (int)DrawSize.Width, (int)DrawSize.Height), 0f, 0f, currimage.Width, currimage.Height, GraphicsUnit.Pixel, DrawAttributes);
  1101. //g.DrawImage(GetCurrentImage(), Location.X, DrawSize.Value.Width, DrawSize.Value.Height, DrawAttributes);
  1102. }
  1103. }
  1104. else
  1105. {
  1106. if (DrawAttributes == null)
  1107. g.DrawImage(currimage, DrawLocation);
  1108. else
  1109. g.DrawImage(currimage, new Rectangle((int)DrawLocation.X, (int)DrawLocation.Y, (int)currimage.Width, (int)currimage.Height), 0f, 0f, currimage.Width, currimage.Height, GraphicsUnit.Pixel, DrawAttributes);
  1110. }
  1111. g.Transform = copyt;
  1112. }
  1113. public override bool PerformFrame(BCBlockGameState gamestate)
  1114. {
  1115. DrawAngle += DrawAngleIncrement;
  1116. BCBlockGameState.IncrementLocation(gamestate, ref _Location, Velocity);
  1117. if (!gamestate.GameArea.Contains(new Rectangle(new Point((int)Location.X, (int)Location.Y), new Size((int)Size.Width, (int)Size.Height))))
  1118. {
  1119. gamestate.Defer(() => gamestate.GameObjects.Remove(this));
  1120. return false;
  1121. }
  1122. //check to see if we hit the paddle:
  1123. if (gamestate.PlayerPaddle != null) //check for null first...
  1124. {
  1125. if (gamestate.PlayerPaddle.Getrect().IntersectsWith(this.getRect()))
  1126. {
  1127. //DIE paddle! do some damage to the paddle. Let's say- 20.
  1128. gamestate.PlayerPaddle.HP -= FireballDamage;
  1129. //return false to delete this fireball.
  1130. //TODO: add particle effects for the explosion or whatever.
  1131. for (int i = 0; i < 15; i++)
  1132. {
  1133. gamestate.Particles.Add(new FireParticle(Location));
  1134. }
  1135. gamestate.Defer(() => gamestate.GameObjects.Remove(this));
  1136. return false;
  1137. }
  1138. }
  1139. return base.PerformFrame(gamestate);
  1140. }
  1141. }
  1142. /// <summary>
  1143. /// A Proxy GameObject can be used for those facilities that are... lacking.
  1144. /// For example, there is no way within a PaddleBehaviour to remove itself, since all calls to the behaviour are done within enumerations, and other concerns.
  1145. /// The proxy object can be used to create a "proxy" game object and redirect the overridden methods to given routines.
  1146. /// </summary>
  1147. public class ProxyObject : GameObject
  1148. {
  1149. public delegate bool ProxyPerformFrame(ProxyObject sourceobject, BCBlockGameState gamestate);
  1150. public delegate void ProxyDraw(Graphics g);
  1151. private object _Tag = null;
  1152. public object Tag { get { return _Tag; } set { _Tag = value; } }
  1153. protected ProxyPerformFrame funcperformframe;
  1154. protected ProxyDraw funcdraw;
  1155. public ProxyObject(ProxyPerformFrame performframefunc, ProxyDraw drawfunc)
  1156. {
  1157. funcperformframe = performframefunc;
  1158. funcdraw = drawfunc;
  1159. }
  1160. public override bool PerformFrame(BCBlockGameState gamestate)
  1161. {
  1162. if (funcperformframe != null)
  1163. return funcperformframe(this, gamestate);
  1164. return false;
  1165. }
  1166. public override void Draw(Graphics g)
  1167. {
  1168. if (funcdraw != null)
  1169. funcdraw(g);
  1170. }
  1171. }
  1172. #region Powerups
  1173. public interface IAnimatedSpriteAction
  1174. {
  1175. bool PerformAnimatedSpriteFrame(AnimatedSprite sprite, BCBlockGameState gamestate);
  1176. void Draw(AnimatedSprite sprite, Graphics g);
  1177. }
  1178. #region Velocity change classes
  1179. public abstract class VelocityChanger
  1180. {
  1181. public delegate PointF VelocityChangerFunction(PointF Input);
  1182. public abstract PointF PerformFrame(BCBlockGameState gstate,PointF CurrentLocation);
  1183. public abstract PointF getVelocity();
  1184. }
  1185. public class VelocityChangerLinear : VelocityChanger
  1186. {
  1187. protected PointF _Delta = new PointF(0, 0);
  1188. public PointF Delta
  1189. {
  1190. get { return _Delta; }
  1191. set { _Delta = value; }
  1192. }
  1193. public VelocityChangerLinear(PointF pDelta)
  1194. {
  1195. _Delta = pDelta;
  1196. }
  1197. public VelocityChangerLinear()
  1198. : this(new PointF(0, 2))
  1199. {
  1200. }
  1201. public override PointF getVelocity()
  1202. {
  1203. return _Delta;
  1204. }
  1205. public override PointF PerformFrame(BCBlockGameState gstate, PointF CurrentLocation)
  1206. {
  1207. //return new PointF(CurrentLocation.X + _Delta.X, CurrentLocation.Y + _Delta.Y);
  1208. BCBlockGameState.IncrementLocation(gstate, ref CurrentLocation, _Delta);
  1209. return CurrentLocation;
  1210. }
  1211. }
  1212. /// <summary>
  1213. /// Class used to present "Exponential" changes to Velocity. This in most cases just means subject to gravity, really.
  1214. /// </summary>
  1215. public class VelocityChangerExponential : VelocityChangerLinear
  1216. {
  1217. private PointF _Acceleration = new PointF(1, 1.01f);
  1218. public PointF Acceleration { get { return _Acceleration; } set { _Acceleration = value; } }
  1219. public VelocityChangerExponential(PointF pDelta, PointF pAcceleration)
  1220. : base(pDelta)
  1221. {
  1222. _Acceleration = pAcceleration;
  1223. }
  1224. public override PointF PerformFrame(BCBlockGameState gstate, PointF CurrentLocation)
  1225. {
  1226. _Delta = new PointF(_Delta.X * _Acceleration.X, _Delta.Y * _Acceleration.Y);
  1227. return base.PerformFrame(gstate,CurrentLocation);
  1228. }
  1229. }
  1230. public class VelocityChangerParametric : VelocityChangerLinear
  1231. {
  1232. public delegate float ParametricFunction(PointF Currposition);
  1233. private ParametricFunction _ParametricX = null;
  1234. private ParametricFunction _ParametricY = null;
  1235. public ParametricFunction ParametricX
  1236. {
  1237. get { return _ParametricX; }
  1238. set { _ParametricX = value; }
  1239. }
  1240. public ParametricFunction ParametricY
  1241. {
  1242. get { return _ParametricY; }
  1243. set { _ParametricY = value; }
  1244. }
  1245. public VelocityChangerParametric(ParametricFunction xFunction, ParametricFunction yFunction)
  1246. {
  1247. _ParametricX = xFunction;
  1248. _ParametricY = yFunction;
  1249. }
  1250. public override PointF PerformFrame(BCBlockGameState gstate, PointF CurrentLocation)
  1251. {
  1252. //throw new NotImplementedException();
  1253. float XValue = 0, YValue = 0;
  1254. if (_ParametricX != null) XValue = _ParametricX(CurrentLocation);
  1255. if (_ParametricY != null) YValue = _ParametricY(CurrentLocation);
  1256. Debug.Print("parametric: X=" + XValue + " Y=" + YValue);
  1257. _Delta = new PointF(XValue, YValue);
  1258. return base.PerformFrame(gstate,CurrentLocation);
  1259. //return new PointF(CurrentLocation.X + XValue,CurrentLocation.Y+YValue);
  1260. }
  1261. }
  1262. public class VelocityChangerLeafy : VelocityChangerParametric
  1263. {
  1264. //this bugger is a bit more complex.
  1265. //Basically, we want our X speed to be a Sin function that goes back and forth an equal amount.
  1266. //the Y speed needs to be similar, but needs to be centered higher.
  1267. public VelocityChangerLeafy()
  1268. : base(null, null)
  1269. {
  1270. ParametricX = LeafyX;
  1271. ParametricY = LeafyY;
  1272. }
  1273. int XFrames = 0;
  1274. public float LeafyX(PointF CurrValue)
  1275. {
  1276. float returnthis = (float)Math.Sin(((double)XFrames * 5)) * 8;
  1277. XFrames++;
  1278. return returnthis;
  1279. }
  1280. public float LeafyY(PointF CurrValue)
  1281. {
  1282. float returnthis = (float)((Math.Sin((double)XFrames * 200) / 2) * 2) + 1.5f;
  1283. return returnthis;
  1284. }
  1285. //performFrame is handled by base class...
  1286. }
  1287. #endregion
  1288. /// <summary>
  1289. /// provides a GameImageObject that animates a given image.
  1290. /// </summary>
  1291. ///
  1292. public class AnimatedSprite : AnimatedImageObject, IAnimatedSpriteAction
  1293. {
  1294. public Image[] AnimationFrames; //the animation frames for this image.
  1295. public decimal CurrentRotation = 0;
  1296. public decimal RotationSpeed = 5;
  1297. public float DrawAlpha = 1f;
  1298. public delegate ImageAttributes NextAttributesFunction(ImageAttributes currentattributes);
  1299. public NextAttributesFunction NextAttributesFunc;
  1300. public IAnimatedSpriteAction SpriteAction;
  1301. private float curralpha = 1.0f;
  1302. private ImageAttributes NextAttribAlpha(ImageAttributes currentattributes)
  1303. {
  1304. curralpha -= 0.05f;
  1305. if (curralpha < 0) curralpha = 0;
  1306. if (useImageattributes == null) useImageattributes = new ImageAttributes();
  1307. float[][] ptsArray =
  1308. {
  1309. new float[] {1, 0, 0, 0, 0},
  1310. new float[] {0, curralpha, 0, 0, 0},
  1311. new float[] {0, 0, 1, 0, 0},
  1312. new float[] {0, 0, 0, curralpha, 0},
  1313. new float[] {0, 0, 0, 0, 1}
  1314. };
  1315. ColorMatrix makeMatrix = new ColorMatrix(ptsArray);
  1316. useImageattributes.SetColorMatrix(makeMatrix, ColorMatrixFlag.Default, ColorAdjustType.Default);
  1317. return useImageattributes;
  1318. }
  1319. public AnimatedSprite(PointF Location, Image ImageUse, decimal initialRotation, decimal pRotationSpeed, int pFrameadvancedelay, NextAttributesFunction pAttribFunc)
  1320. : this(Location, ImageUse, initialRotation, pRotationSpeed, pFrameadvancedelay)
  1321. {
  1322. NextAttributesFunc = pAttribFunc;
  1323. }
  1324. public AnimatedSprite(PointF Location, Image ImageUse, decimal initialRotation, decimal pRotationSpeed, int pframeadvancedelay)
  1325. : this(Location, ImageUse.Size, ImageUse, initialRotation, pRotationSpeed, pframeadvancedelay)
  1326. {
  1327. }
  1328. public AnimatedSprite(PointF Location, SizeF ObjectSize, Image ImageUse, decimal initialRotation, decimal pRotationSpeed, int pframeadvancedelay)
  1329. : this(Location, ObjectSize, new Image[] { ImageUse }, initialRotation, pRotationSpeed, pframeadvancedelay)
  1330. {
  1331. }
  1332. public AnimatedSprite(PointF Location, Image[] ImageFrames, decimal initialRotation, decimal pRotationSpeed, int pframeadvancedelay)
  1333. : this(Location, ImageFrames[0].Size, ImageFrames, initialRotation, pRotationSpeed, pframeadvancedelay)
  1334. {
  1335. }
  1336. public AnimatedSprite(PointF Location, SizeF ObjectSize, Image[] ImageFrames, decimal initialRotation, decimal pRotationSpeed, int pframeadvancedelay)
  1337. : base(Location, ObjectSize, ImageFrames, pframeadvancedelay)
  1338. {
  1339. Debug.Print("AnimatedSprite Constructor...");
  1340. //if (ImageFrames == null) throw new ArgumentNullException("ImageFrames Argument cannot be null");
  1341. AnimationFrames = ImageFrames;
  1342. SpriteAction = this;
  1343. CurrentRotation = initialRotation;
  1344. RotationSpeed = pRotationSpeed;
  1345. NextAttributesFunc = NextAttribAlpha;
  1346. }
  1347. public AnimatedSprite(PointF Location, SizeF ObjectSize, String[] ImageFrameKeys, decimal initialRotation, decimal pRotationSpeed, int pframeadvancedelay)
  1348. : this(Location, ObjectSize, BCBlockGameState.Imageman.getImageFrames(ImageFrameKeys), initialRotation, pRotationSpeed, pframeadvancedelay)
  1349. {
  1350. }
  1351. public override void Draw(Graphics g)
  1352. {
  1353. //
  1354. // base.Draw(g);
  1355. Draw(this, g);
  1356. }
  1357. protected ImageAttributes useImageattributes;
  1358. public override bool PerformFrame(BCBlockGameState gamestate)
  1359. {
  1360. bool returnvalue = base.PerformFrame(gamestate);
  1361. if (NextAttributesFunc != null)
  1362. {
  1363. useImageattributes = NextAttributesFunc(useImageattributes);
  1364. }
  1365. if (AnimationFrames.Length == 1)
  1366. return returnvalue;
  1367. else
  1368. {
  1369. return SpriteAction.PerformAnimatedSpriteFrame(this, gamestate);
  1370. }
  1371. }
  1372. #region IAnimatedSpriteAction Members
  1373. //class variable, used to store our transformation matrix.
  1374. Matrix rotationmatrix = new Matrix();
  1375. public void Draw(AnimatedSprite sprite, Graphics g)
  1376. {
  1377. //Draw: takes into account the rotation and the current frame.
  1378. //First, get the Image we need to draw:
  1379. Image drawthis = CurrentFrameImage;
  1380. if (drawthis == null) return;
  1381. PointF centerpoint = CenterPoint();
  1382. //now,proceed to change the transform matrix of the graphics object...
  1383. //Debug.Print(CurrentRotation.ToString());
  1384. //rotate around the center of the object.
  1385. //create a new Matrix, also, a temporary one to cache the graphics objects original matrix.
  1386. Matrix cached = g.Transform;
  1387. rotationmatrix.Reset();
  1388. //rotationmatrix.RotateAt((float)CurrentRotation, centerpoint);
  1389. rotationmatrix.RotateAt((float)CurrentRotation, centerpoint);
  1390. //draw it...
  1391. g.Transform = rotationmatrix;
  1392. //g.DrawImage(drawthis, Location.X, Location.Y, Size.Width, Size.Height, useImageattributes);
  1393. g.DrawImage(drawthis, new Rectangle((int)Location.X, (int)Location.Y, (int)Size.Width, (int)Size.Height),
  1394. 0, 0, drawthis.Width, drawthis.Height, GraphicsUnit.Pixel, useImageattributes);
  1395. //now "revert" the transform...
  1396. //g.Flush();
  1397. g.Transform = cached;
  1398. //g.Transform.RotateAt(-(float)CurrentRotation, centerpoint);
  1399. }
  1400. public bool PerformAnimatedSpriteFrame(AnimatedSprite sprite, BCBlockGameState gamestate)
  1401. {
  1402. //task: increment rotation and frame number.
  1403. CurrentRotation += RotationSpeed;
  1404. bool returnresult = !gamestate.GameArea.Contains(new Point((int)Location.X, (int)Location.Y));
  1405. //Location= new PointF(Location.X+Velocity.X,Location.Y+Velocity.Y);
  1406. Location = VelocityChange.PerformFrame(gamestate,Location);
  1407. return returnresult;
  1408. }
  1409. #endregion
  1410. }
  1411. //used by special bonuses and whatnot.
  1412. //example: a bonus level will have a CountdownObject. getting macguffins or other items will add time to that countdown.
  1413. public class CountDownObject : SizeableGameObject
  1414. {
  1415. private TimeSpan? TimerStart = null;
  1416. private BCBlockGameState gs = null;
  1417. public event EventHandler<EventArgs> CountDownExpired;
  1418. private Font _DrawFont = BCBlockGameState.GetScaledFont(new Font(BCBlockGameState.GetMonospaceFont(), 18), 48);
  1419. private Brush _DrawBrush = new SolidBrush(Color.Green);
  1420. private Pen _DrawPen = new Pen(Color.Black);
  1421. private TimeSpan TimeoutValue = new TimeSpan(0, 0, 0, 5);
  1422. public Pen DrawPen { get { return _DrawPen; } set { _DrawPen = value; } }
  1423. public Brush DrawBrush { get { return _DrawBrush; } set { _DrawBrush = value; } }
  1424. public Font DrawFont { get { return _DrawFont; } set { _DrawFont = value; } }
  1425. public CountDownObject(PointF pLocation, SizeF objectsize) : base(pLocation, objectsize)
  1426. {
  1427. CountDownExpired += CountDownObject_CountDownExpired;
  1428. }
  1429. void CountDownObject_CountDownExpired(object sender, EventArgs e)
  1430. {
  1431. gs.Blocks.Clear(); //with no blocks, the level will complete.
  1432. }
  1433. public static void AddTime(BCBlockGameState gstate,TimeSpan addamount)
  1434. {
  1435. foreach (CountDownObject cdo in (from go in gstate.GameObjects where go is CountDownObject select go))
  1436. {
  1437. cdo.AddTime(addamount);
  1438. }
  1439. }
  1440. public void AddTime(TimeSpan addamount)
  1441. {
  1442. TimeoutValue += addamount;
  1443. }
  1444. public override void Draw(Graphics g)
  1445. {
  1446. //location is center spot.
  1447. //measure size of the string.
  1448. TimeSpan drawelapsed = getElapsed();
  1449. //format: 00.00
  1450. String usedrawstring = String.Format("{0:0.0}", drawelapsed.TotalSeconds);
  1451. //we have the graphics context and the Font to use (DrawFont). we can use MeasureString() to measure the size of the text.
  1452. var calcsize = g.MeasureString(usedrawstring, _DrawFont);
  1453. //we want to draw it centered on the Location, so we need to offset by half the size.
  1454. var drawlocation = new PointF(Location.X - calcsize.Width / 2, Location.Y - calcsize.Height / 2);
  1455. //use a path.
  1456. using (GraphicsPath gp = new GraphicsPath())
  1457. {
  1458. gp.AddString(usedrawstring, _DrawFont, Point.Empty, StringFormat.GenericDefault);
  1459. g.FillPath(_DrawBrush, gp);
  1460. g.DrawPath(_DrawPen, gp);
  1461. }
  1462. }
  1463. public override bool PerformFrame(BCBlockGameState gamestate)
  1464. {
  1465. //we use gamestate.ClientObject.GetLevelTime for the countdown.
  1466. //gamestate.ClientObject.GetLevelTime
  1467. if (TimerStart == null)
  1468. {
  1469. //uninitialized, so set the timer.
  1470. TimerStart = gamestate.ClientObject.GetLevelTime();
  1471. gs = gamestate;
  1472. }
  1473. return base.PerformFrame(gamestate);
  1474. }
  1475. private TimeSpan getElapsed()
  1476. {
  1477. //retrieves the elapsed game time for this timer.
  1478. return gs.ClientObject.GetLevelTime() - TimerStart.Value;
  1479. }
  1480. }
  1481. public class PolygonProtector : SizeableGameObject
  1482. {
  1483. //a protective bumper that sits beneath the paddle.
  1484. private Image[] ProtectionImages;
  1485. private int ourHP = 10;
  1486. private Image RandomPolyImage(Size drawsize)
  1487. {
  1488. Bitmap buildimage = new Bitmap(drawsize.Width, drawsize.Height);
  1489. using (Graphics buildg = Graphics.FromImage(buildimage))
  1490. {
  1491. buildg.Clear(Color.Transparent);
  1492. for (int i = 0; i < 90; i++)
  1493. {
  1494. //generate 500 random polygons.
  1495. //we'll cheat and use a PolyDebris.
  1496. PointF Randomcenter = new PointF((float)(BCBlockGameState.rgen.NextDouble() * drawsize.Width),
  1497. (float)(BCBlockGameState.rgen.NextDouble() * drawsize.Height));
  1498. float randomradius = (float)BCBlockGameState.rgen.NextDouble(3, 15);
  1499. int useA = BCBlockGameState.rgen.Next(64, 255);
  1500. int useR = BCBlockGameState.rgen.Next(0, 255);
  1501. int useG = BCBlockGameState.rgen.Next(0, 255);
  1502. int useB = BCBlockGameState.rgen.Next(0, 255);
  1503. Color randomcolor = Color.FromArgb(useA, useR, useG, useB);
  1504. PolyDebris drawdebris = new PolyDebris(Randomcenter, 0, randomcolor, randomradius, randomradius, 3, 9);
  1505. drawdebris.Draw(buildg);
  1506. }
  1507. }
  1508. return buildimage;
  1509. }
  1510. private void GenerateImages()
  1511. {
  1512. //let's go with 10 different frames.
  1513. ProtectionImages = new Image[10];
  1514. for (int i = 0; i < ProtectionImages.Length; i++)
  1515. {
  1516. ProtectionImages[i] = RandomPolyImage(new Size(413, 32));
  1517. }
  1518. }
  1519. public PolygonProtector()
  1520. : base(new PointF(0, 427 - 32), new SizeF(413, 427))
  1521. {
  1522. GenerateImages();
  1523. }
  1524. public override void Draw(Graphics g)
  1525. {
  1526. Image drawthis = BCBlockGameState.Choose(ProtectionImages);
  1527. g.DrawImageUnscaled(drawthis, new Point(0, 427 - drawthis.Height));
  1528. }
  1529. }
  1530. /*
  1531. public class SuperleafPowerUp : GamePowerUp
  1532. {
  1533. private Image[] LeafImages = BCBlockGameState.Imageman.getImageFrames(new String[] { "leaf", "FLIPX:leaf" });
  1534. public bool PowerupCallback(BCBlockGameState gamestate, ref List<GameObject> addem, ref List<GameObject> removeem)
  1535. {
  1536. return true;
  1537. }
  1538. public SuperleafPowerUp(PointF Location, SizeF ObjectSize)
  1539. : base(Location, ObjectSize, (Image[])null, -1,null)
  1540. {
  1541. ObjectImages = LeafImages;
  1542. //VelocityChange = new VelocityChangerLeafy();
  1543. usefunction = PowerupCallback;
  1544. }
  1545. public override bool PerformFrame(BCBlockGameState gamestate, ref List<GameObject> AddObjects, ref List<GameObject> removeobjects)
  1546. {
  1547. //return base.PerformFrame(gamestate, ref AddObjects, ref removeobjects);
  1548. ObjectImages = LeafImages;
  1549. if (Math.Sign(VelocityChange.getVelocity().X) == -1)
  1550. {
  1551. //negative, moving to the right, so we need the flipped version.
  1552. currimageframe = 1;
  1553. }
  1554. else
  1555. {
  1556. currimageframe = 0;
  1557. }
  1558. return base.PerformFrame(gamestate, ref AddObjects, ref removeobjects);
  1559. }
  1560. }
  1561. */
  1562. #endregion
  1563. public class LineSegment : GameObject
  1564. {
  1565. PointF[] Points = new PointF[2];
  1566. Pen _UsePen = new Pen(Color.Black);
  1567. public Pen UsePen { get { return _UsePen; } set { _UsePen = value; } }
  1568. public LineSegment(PointF PointA, PointF PointB)
  1569. {
  1570. Points[0] = PointA;
  1571. Points[1] = PointB;
  1572. }
  1573. public PointF Magnitude()
  1574. {
  1575. return new PointF(Points[1].X - Points[0].X, Points[1].Y - Points[0].Y);
  1576. }
  1577. public override bool PerformFrame(BCBlockGameState gamestate)
  1578. {
  1579. //throw new NotImplementedException();
  1580. return true;
  1581. }
  1582. public override void Draw(Graphics g)
  1583. {
  1584. //throw new NotImplementedException();
  1585. g.DrawLine(UsePen, Points[0], Points[1]);
  1586. }
  1587. }
  1588. /// <summary>
  1589. /// Class derived from to create timed radius-based effects; example, explosions.
  1590. /// </summary>
  1591. public abstract class BaseRadiusEffectBlock : SizeableGameObject
  1592. {
  1593. //basic concept is simple.
  1594. protected Brush _CurrentBrush = new SolidBrush(Color.Red);
  1595. protected float _CurrentRadius = 0;
  1596. protected float _MaxRadius = 0;
  1597. protected Color _useColor = Color.Red;
  1598. protected PointF Velocity, VelocityDecay = new PointF(0.99f, 0.99f);
  1599. protected Func<float, float, float> _IncrementRoutine = null;
  1600. protected Func<float, float> _AlphaRoutine = null;
  1601. public Brush CurrentBrush { get { return _CurrentBrush; } set { _CurrentBrush = value; } }
  1602. public float CurrentRadius
  1603. {
  1604. get { return _CurrentRadius; }
  1605. set
  1606. {
  1607. _CurrentRadius = value;
  1608. }
  1609. }
  1610. public Func<float, float, float> IncrementRoutine { get { return _IncrementRoutine; } set { _IncrementRoutine = value; } }
  1611. public Func<float, float> AlphaRoutine { get { return _AlphaRoutine; } set { _AlphaRoutine = value; } }
  1612. protected static float DefaultIncrementRoutine(float x, float y)
  1613. {
  1614. return x + 5;
  1615. }
  1616. protected BaseRadiusEffectBlock(PointF Location, float InitialSize, float pMaxSize, Func<float, float, float> Incrementfunction)
  1617. : base(Location, new SizeF(InitialSize, InitialSize))
  1618. {
  1619. _MaxRadius = pMaxSize;
  1620. _CurrentRadius = InitialSize;
  1621. IncrementRoutine = Incrementfunction;
  1622. }
  1623. public override bool PerformFrame(BCBlockGameState gamestate)
  1624. {
  1625. BCBlockGameState.IncrementLocation(gamestate, ref _Location, Velocity);
  1626. Velocity = new PointF(Velocity.X * VelocityDecay.X, Velocity.Y * VelocityDecay.Y);
  1627. _CurrentRadius = IncrementRoutine(_CurrentRadius, _MaxRadius);
  1628. float usealpha = (1 - (_CurrentRadius / _MaxRadius)) * 255;
  1629. usealpha = BCBlockGameState.ClampValue(usealpha, 0, 255);
  1630. CurrentBrush = new SolidBrush(Color.FromArgb((int)usealpha, _useColor));
  1631. return _CurrentRadius >= _MaxRadius || (usealpha <= 5);
  1632. }
  1633. public override void Draw(Graphics g)
  1634. {
  1635. g.FillEllipse(CurrentBrush, Location.X - _CurrentRadius, Location.Y - _CurrentRadius, _CurrentRadius * 2, _CurrentRadius * 2);
  1636. }
  1637. }
  1638. public class BlockChangeEffect : BaseRadiusEffectBlock
  1639. {
  1640. protected Type BlockFindType = typeof(InvincibleBlock);
  1641. protected Type BlockChangeType = typeof(NormalBlock);
  1642. public BlockChangeEffect(PointF Location, float InitialRadius, float MaxRadius,Type SearchForType, Type ChangeBlockTo)
  1643. : base(Location, InitialRadius, MaxRadius, DefaultIncrementRoutine)
  1644. {
  1645. if (!ChangeBlockTo.IsSubclassOf(typeof(Block)))
  1646. throw new ArgumentException("ChangeBlockTo Parameter must be a subclass of Block");
  1647. BlockFindType = SearchForType;
  1648. BlockChangeType = ChangeBlockTo;
  1649. _useColor = Color.Green;
  1650. }
  1651. public override bool PerformFrame(BCBlockGameState gamestate)
  1652. {
  1653. var changeme = from p in gamestate.Blocks
  1654. where
  1655. BCBlockGameState.Distance(Location.X, Location.Y, p.CenterPoint().X,
  1656. p.CenterPoint().Y) < _CurrentRadius && (BlockFindType==null || p.GetType()==BlockFindType)
  1657. select p;
  1658. foreach (var loopitem in changeme)
  1659. {
  1660. if (!(loopitem.GetType().Equals(BlockChangeType)))
  1661. {
  1662. //if not equal, create a new block with the same rect as loopitem
  1663. Block createdblock = (Block)Activator.CreateInstance(BlockChangeType, loopitem.BlockRectangle);
  1664. //remove the old one, and replace it with this one.
  1665. var nodeitem = gamestate.Blocks.Find(loopitem);
  1666. //add the new one...
  1667. var copied = loopitem;
  1668. //don't change the blocks right away, plonk it to a delegate.
  1669. //it might also be possible to plonk this entire for loop into a delayed framestartup call, too.
  1670. gamestate.NextFrameCalls.Enqueue(new BCBlockGameState.NextFrameStartup(() =>
  1671. {
  1672. //add a "changed" block...
  1673. gamestate.Blocks.AddAfter(nodeitem,createdblock);
  1674. //remove the old one.
  1675. gamestate.Blocks.Remove(copied);
  1676. }));
  1677. }
  1678. }
  1679. gamestate.Forcerefresh = true;
  1680. return base.PerformFrame(gamestate);
  1681. }
  1682. }
  1683. public class CreationEffect : BaseRadiusEffectBlock
  1684. {
  1685. public delegate Block BlockCreationRoutine(BCBlockGameState gstate, RectangleF CreateSpot);
  1686. public BlockCreationRoutine BlockCreateRoutine = null;
  1687. private SizeF BlockCreateSize;
  1688. public static Block DefaultCreationFunction(BCBlockGameState gstate, RectangleF CreateSpot)
  1689. {
  1690. return new NormalBlock(CreateSpot);
  1691. }
  1692. public CreationEffect(SizeF BlockSize, PointF Location, float maxRadius)
  1693. : base(Location, 2, maxRadius, DefaultIncrementRoutine)
  1694. {
  1695. BlockCreateSize = BlockSize;
  1696. BlockCreateRoutine = DefaultCreationFunction;
  1697. _useColor = Color.Blue;
  1698. }
  1699. private List<RectangleF> createblocklocations = new List<RectangleF>();
  1700. private bool flInit = false;
  1701. private bool _ShowOrbs = true;
  1702. public override bool PerformFrame(BCBlockGameState gamestate)
  1703. {
  1704. bool rval = base.PerformFrame(gamestate);
  1705. for (int i = 0; i < (int)(50f * BCBlockGameState.ParticleGenerationFactor); i++)
  1706. {
  1707. const float speedmult = 1;
  1708. //choose a random Angle...
  1709. double randomangle = Math.PI * 2 * BCBlockGameState.rgen.NextDouble();
  1710. //create the appropriate speed vector, based on our radius...
  1711. double usespeed = (_CurrentRadius / _MaxRadius) * speedmult; //should be proportional to how close we are to the maximum radius; max radius will have particles move 1...
  1712. usespeed += ((BCBlockGameState.rgen.NextDouble() * 0.5) - 0.25);
  1713. PointF addpointLocation = new PointF(Location.X + (float)Math.Cos(randomangle) * _CurrentRadius, Location.Y + (float)Math.Sin(randomangle) * _CurrentRadius);
  1714. //create a dustparticle...
  1715. Particle addparticle = null;
  1716. if ((i % 5 == 0) && _ShowOrbs)
  1717. {
  1718. addparticle = new LightOrb(addpointLocation, Color.Blue, 5 + (float)(BCBlockGameState.rgen.NextDouble() * 10));
  1719. }
  1720. else
  1721. {
  1722. addparticle = new WaterParticle(addpointLocation,
  1723. new PointF((float)(Math.Cos(randomangle) * usespeed),
  1724. (float)(Math.Sin(randomangle) * usespeed)));
  1725. }
  1726. if (addparticle != null)
  1727. {
  1728. addparticle.Velocity = new PointF(addparticle.Velocity.X + Velocity.X, addparticle.Velocity.Y + Velocity.Y);
  1729. gamestate.Particles.Add(addparticle);
  1730. }
  1731. }
  1732. //is this the first call?
  1733. if (!flInit)
  1734. {
  1735. flInit = true;
  1736. //initialize data structures.
  1737. //the idea here is that createblocklocations will store the offset from the CreationEffect's location, rather than an absolute position.
  1738. for (float x = -_MaxRadius; x < (_MaxRadius); x += BlockCreateSize.Width)
  1739. {
  1740. for (float y = -_MaxRadius; y < (_MaxRadius); y += BlockCreateSize.Height)
  1741. {
  1742. //add a new rectangle to that cache
  1743. RectangleF createrect = new RectangleF(x, y, BlockCreateSize.Width, BlockCreateSize.Height);
  1744. //add it to the list.
  1745. createblocklocations.Add(createrect);
  1746. }
  1747. }
  1748. }
  1749. //createblocklocations has the offsets from our position. LINQ-ee-fy it to see if any need to be created.
  1750. var createthese = from q in createblocklocations where BCBlockGameState.Distance(0, 0, q.CenterPoint().X, q.CenterPoint().Y) < _CurrentRadius select q;
  1751. List<RectangleF> removethese = new List<RectangleF>();
  1752. if (createthese.Any())
  1753. {
  1754. //we need to create some blocks we do.
  1755. foreach (RectangleF looprect in createthese)
  1756. {
  1757. //create the PROPER rectanglef structure by cloning this one and offseting the clone.
  1758. RectangleF userect = looprect;
  1759. userect.Offset(Location.X, Location.Y);
  1760. Block createdblock = DefaultCreationFunction(gamestate, userect);
  1761. //add this block to the game.
  1762. gamestate.Blocks.AddLast(createdblock);
  1763. removethese.Add(looprect);
  1764. }
  1765. foreach (var removeit in removethese)
  1766. {
  1767. createblocklocations.Remove(removeit);
  1768. }
  1769. gamestate.Forcerefresh = true;
  1770. }
  1771. return rval;
  1772. }
  1773. }
  1774. public class ExplosionEffect : BaseRadiusEffectBlock
  1775. {
  1776. protected Color _ExplosionColor = Color.Red;
  1777. private int _ComboCount = 0;
  1778. private bool _DamageBlocks = true;
  1779. private bool _DamagePaddle = true;
  1780. private bool _EffectObjects = true;
  1781. private bool _ShowOrbs = true;
  1782. private bool _DestroyAll = false;
  1783. public Color ExplosionColor { get { return _ExplosionColor; } set { _ExplosionColor = value; } }
  1784. public bool ShowOrbs { get { return _ShowOrbs; } set { _ShowOrbs = value; } }
  1785. public bool DamageBlocks { get { return _DamageBlocks; } set { _DamageBlocks = value; } }
  1786. public bool DamagePaddle { get { return _DamagePaddle; } set { _DamagePaddle = value; } }
  1787. /// <summary>
  1788. /// determines whether this explosion will find and invoke game objects with implement "IExplodable" when it hits them.
  1789. /// </summary>
  1790. public bool EffectObjects { get { return _EffectObjects; } set { _EffectObjects = true; } }
  1791. public int ComboCount { get { return _ComboCount; } set { _ComboCount = value; } }
  1792. public bool DestroyAll { get { return _DestroyAll; } set { _DestroyAll = value; } }
  1793. //ComboNumber: this is set by the creator when needed.
  1794. //the
  1795. public ExplosionEffect(PointF Location)
  1796. : this(Location, 128)
  1797. {
  1798. }
  1799. public ExplosionEffect(PointF Location, float MaxRadius)
  1800. : base(Location, 2, MaxRadius, (w, x) => w + ((x - w) / 5))
  1801. {
  1802. }
  1803. private Dictionary<cBall, int> proxylife = new Dictionary<cBall, int>();
  1804. public List<Block> proxyperformframe(cBall ballobject, BCBlockGameState ParentGameState, ref List<cBall> ballsadded, ref List<cBall> ballsremove, out bool removethis)
  1805. {
  1806. removethis = false;
  1807. if (proxylife.ContainsKey(ballobject))
  1808. {
  1809. proxylife[ballobject]++;
  1810. }
  1811. else
  1812. {
  1813. proxylife.Add(ballobject, 0);
  1814. }
  1815. if (proxylife[ballobject] > 0)
  1816. {
  1817. Debug.Print("force removing a ball");
  1818. removethis = true;
  1819. }
  1820. return null;
  1821. }
  1822. public override bool PerformFrame(BCBlockGameState gamestate)
  1823. {
  1824. bool retval = base.PerformFrame(gamestate);
  1825. //add some particles around the edges of the explosion, moving outward...
  1826. if (gamestate.Particles.Count((w)=>!w.Important) < BCBlockGameState.MaxParticles)
  1827. {
  1828. for (int i = 0; i < (int)(25f * BCBlockGameState.ParticleGenerationFactor); i++)
  1829. {
  1830. const float speedmult = 1;
  1831. //choose a random Angle...
  1832. double randomangle = Math.PI * 2 * BCBlockGameState.rgen.NextDouble();
  1833. //create the appropriate speed vector, based on our radius...
  1834. double usespeed = (_CurrentRadius / _MaxRadius) * speedmult;
  1835. //should be proportional to how close we are to the maximum radius; max radius will have particles move 1...
  1836. usespeed += ((BCBlockGameState.rgen.NextDouble() * 0.6) - 0.3);
  1837. PointF addpointLocation = new PointF(Location.X + (float)Math.Cos(randomangle) * _CurrentRadius,
  1838. Location.Y + (float)Math.Sin(randomangle) * _CurrentRadius);
  1839. //create a dustparticle...
  1840. PointF PointSpeed = new PointF((float)(Math.Cos(randomangle) * usespeed),
  1841. (float)(Math.Sin(randomangle) * usespeed));
  1842. Particle addparticle = null;
  1843. if (i % 5 != 0)
  1844. {
  1845. addparticle = new DustParticle(addpointLocation, PointSpeed);
  1846. }
  1847. else if (_ShowOrbs)
  1848. {
  1849. //LightOrb...
  1850. LightOrb lo = new LightOrb(addpointLocation, ExplosionColor,
  1851. (float)BCBlockGameState.rgen.NextDouble() * 10 + 5);
  1852. lo.Velocity = PointSpeed;
  1853. addparticle = lo;
  1854. }
  1855. if (addparticle != null) gamestate.Particles.Add(addparticle);
  1856. }
  1857. }
  1858. //each frame, check for blocks that are within the given radius. Hell with it, we'll check their centerpoints...
  1859. if (gamestate.PlayerPaddle != null)
  1860. {
  1861. //if(
  1862. if (DamagePaddle && BCBlockGameState.RectangleIntersectsCircle(gamestate.PlayerPaddle.Getrect(), Location, _CurrentRadius))
  1863. {
  1864. gamestate.PlayerPaddle.HP -= 1f;
  1865. }
  1866. }
  1867. if (_EffectObjects)
  1868. {
  1869. //find all GameObjects that implement IExplodable.
  1870. //we might be bothered to do the same for Balls, I suppose. No promises.
  1871. IEnumerable<IExplodable> result = BCBlockGameState.Join<IExplodable>(
  1872. (from ball in gamestate.Balls where ball is IExplodable && (BCBlockGameState.Distance(Location,ball.Location) < _CurrentRadius) select ball as IExplodable),
  1873. (from obj in gamestate.GameObjects where obj is IExplodable select obj as IExplodable));
  1874. //in order to prevent contention issues, we will defer the loop that "effects" each item until the next gametick iteration starts.
  1875. gamestate.NextFrameCalls.Enqueue(new BCBlockGameState.NextFrameStartup(() =>
  1876. {
  1877. foreach (var iterateresult in result)
  1878. {
  1879. PointF useVelocityEffect = new PointF(Math.Max(1,Velocity.X),Math.Max(1,Velocity.Y));
  1880. iterateresult.ExplosionInteract(this, Location, useVelocityEffect);
  1881. }
  1882. }));
  1883. }
  1884. List<Block> removethese = new List<Block>();
  1885. if (_DamageBlocks)
  1886. {
  1887. var blowemup = from p in gamestate.Blocks
  1888. where
  1889. BCBlockGameState.Distance(Location.X, Location.Y, p.CenterPoint().X,
  1890. p.CenterPoint().Y) < _CurrentRadius
  1891. select p;
  1892. if (blowemup.Any())
  1893. {
  1894. foreach (var loopblock in blowemup)
  1895. {
  1896. //destroy it.
  1897. if (loopblock is DestructionBlock)
  1898. {
  1899. //special handling for combos
  1900. Debug.Print("DestructionBlock detected in ExplosionEffect...");
  1901. (loopblock as DestructionBlock).ComboCount = this.ComboCount + 1;
  1902. }
  1903. if (DestroyAll || loopblock.MustDestroy())
  1904. {
  1905. loopblock.StandardSpray(gamestate);
  1906. removethese.Add(loopblock);
  1907. }
  1908. }
  1909. lock (gamestate.Blocks)
  1910. {
  1911. foreach (Block removeit in removethese)
  1912. {
  1913. //get angle between the center of the explosion and the block's center.
  1914. double Angle = BCBlockGameState.GetAngle(CenterPoint(), removeit.CenterPoint());
  1915. int useSpeed = BCBlockGameState.ClampValue((int)_MaxRadius / 2, 1, 5);
  1916. PointF useVelocity = BCBlockGameState.GetVelocity(useSpeed, Angle);
  1917. cBall tempball = new cBall(new PointF(removeit.CenterPoint().X-useVelocity.X,removeit.CenterPoint().Y-useVelocity.Y), useVelocity);
  1918. tempball.PreviousVelocity = useVelocity;
  1919. tempball.Behaviours.Add(new TempBallBehaviour());
  1920. //add a proxy behaviour to remove it as well.
  1921. //tempball.Behaviours.Add(new ProxyBallBehaviour("ExplosionEffect", null, proxyperformframe, null, null, null, null, null, null));
  1922. //gamestate.Balls.AddLast(tempball);
  1923. List<cBall> discardlist = new List<cBall>();
  1924. try
  1925. {
  1926. //this is... well, cheating...
  1927. //we cannot add GameObjects to the List, except by plopping them in the ref AddedObject parameter.
  1928. //however, we cannot "force" the PerformBlockHit of a block (say a destruction block) to add a GameObject (another ExplosionEffect, in that case)
  1929. //we cheat. we swap out the entire gamestate.GameObject LinkedList with a new one, call the routine, and then
  1930. //we add any added GameObjects to our ref parameter, and swap the old list back in, hoping nobody notices our
  1931. //audacity to fiddle with core game state objects...
  1932. var copiedref = gamestate.GameObjects;
  1933. gamestate.GameObjects = new LinkedList<GameObject>();
  1934. removeit.PerformBlockHit(gamestate, tempball);
  1935. gamestate.Blocks.Remove(removeit);
  1936. var tempadded = gamestate.GameObjects;
  1937. gamestate.GameObjects = copiedref;
  1938. //now we add the ones we need to add to our ref array. I feel dirty.
  1939. gamestate.Defer(() =>
  1940. {
  1941. foreach (var iterate in tempadded)
  1942. {
  1943. gamestate.GameObjects.AddLast(iterate);
  1944. }
  1945. });
  1946. }
  1947. catch
  1948. {
  1949. }
  1950. //we don't add the ball to our GameState, so we don't have to worry about it persisting :D
  1951. //the idea is that we want to invoke the actions of the block (which for many blocks will simply be destroyed).
  1952. //at the same time, some blocks might react weirdly to having temporary balls tossed into their centers, so we make it so the ball will only live for
  1953. //two frames by adding a proxyballbehaviour that ensure that.
  1954. //gamestate.Blocks.Remove(removeit);
  1955. gamestate.Forcerefresh = true;
  1956. }
  1957. //changed: instead of removing them, create a temporary ball at their center point.
  1958. }
  1959. }
  1960. }
  1961. return retval;
  1962. }
  1963. }
  1964. //basic class that basically takes the image, size, and speed of a object, flips it upside down, and animates it
  1965. //"dying" the same way they commonly do in old NES games.
  1966. public class MarioDeathStyleObject : SizeableGameObject, iLocatable, IMovingObject
  1967. {
  1968. protected PointF _Velocity;
  1969. public SizeF DrawSize { get; set; }
  1970. private Image usedrawimage = null;
  1971. private float maxspeed = 7;
  1972. public PointF Velocity { get { return _Velocity; } set { _Velocity = value; } }
  1973. public PointF VelocityAdd { get; set; }
  1974. private PointF _VelocityDecay = new PointF(0.98f, 1);
  1975. public PointF VelocityDecay { get { return _VelocityDecay; } set { _VelocityDecay = value; } }
  1976. private void InitSpeeds()
  1977. {
  1978. Velocity = new PointF(0, -5);
  1979. VelocityAdd = new PointF(0, 0.5f);
  1980. }
  1981. public override void setFrozen(bool newvalue)
  1982. {
  1983. //base.setFrozen(newvalue);
  1984. //this object can't be frozen... :P
  1985. }
  1986. public Rectangle getRect()
  1987. {
  1988. return new Rectangle((int)(Location.X - DrawSize.Width), (int)(Location.Y - DrawSize.Height), (int)(DrawSize.Width), (int)(DrawSize.Height));
  1989. }
  1990. public override void Draw(Graphics g)
  1991. {
  1992. g.DrawImageUnscaled(usedrawimage, getRect());
  1993. }
  1994. public override bool PerformFrame(BCBlockGameState gamestate)
  1995. {
  1996. bool retval;
  1997. lock (this)
  1998. {
  1999. retval = !gamestate.GameArea.Contains(getRect());
  2000. BCBlockGameState.IncrementLocation(gamestate,ref _Location,Velocity);
  2001. if (Velocity.Y < maxspeed)
  2002. {
  2003. Velocity = new PointF(Velocity.X + VelocityAdd.X, Velocity.Y + VelocityAdd.Y);
  2004. Velocity = new PointF(Velocity.X * VelocityDecay.X, Velocity.Y * VelocityDecay.Y);
  2005. }
  2006. else if (Velocity.Y > maxspeed)
  2007. Velocity = new PointF(Velocity.X, maxspeed);
  2008. base.PerformFrame(gamestate);
  2009. }
  2010. return retval;
  2011. }
  2012. public MarioDeathStyleObject(GameEnemy ge)
  2013. : base(ge.Location,ge.DrawSize)
  2014. {
  2015. Location = ge.Location;
  2016. SizeF ObjSize = ge.GetSize();
  2017. usedrawimage = new Bitmap((int)(ObjSize.Width * 2), (int)(ObjSize.Height * 2));
  2018. Graphics gdraw = Graphics.FromImage(usedrawimage);
  2019. gdraw.Clear(Color.Transparent);
  2020. //move it temporarity...
  2021. ge.Location = new PointF(usedrawimage.Width / 2, usedrawimage.Height / 2);
  2022. ge.Draw(gdraw);
  2023. //move back
  2024. ge.Location = Location;
  2025. //flip the image vertically.
  2026. usedrawimage.RotateFlip(RotateFlipType.RotateNoneFlipY);
  2027. //now, set speed to our defaults.
  2028. InitSpeeds();
  2029. if (ge is IMovingObject)
  2030. {
  2031. Velocity = new PointF((ge as IMovingObject).Velocity.X / 2, Velocity.Y);
  2032. }
  2033. }
  2034. }
  2035. public class MegamanTest : GameEnemy
  2036. {
  2037. int currframecounter = 0;
  2038. public MegamanTest(PointF pLocation)
  2039. : base(pLocation, null, 5)
  2040. {
  2041. Location = pLocation;
  2042. StateFrameImageKeys = new Dictionary<string, string[]>();
  2043. StateFrameImageKeys.Add("idle", new string[] { "megaman1", "megaman2", "megaman3", "megaman4" });
  2044. StateFrameImageKeys.Add("green", new string[] { "megagreen1", "megagreen2", "megagreen3", "megagreen4" });
  2045. StateFrameIndex = new Dictionary<string, int>();
  2046. StateFrameIndex.Add("idle", 0);
  2047. StateFrameIndex.Add("green", 0);
  2048. FrameDelayTimes = new Dictionary<string, int[]>();
  2049. FrameDelayTimes.Add("idle", new int[] { 25, 25, 25, 25, 25, 25, 25, 25, 25, 25 });
  2050. FrameDelayTimes.Add("green", new int[] { 10, 10, 10, 10, 10, 10, 10, 10, 10 });
  2051. DrawSize = new SizeF(56 * 2, 48 * 2);
  2052. }
  2053. public override bool PerformFrame(BCBlockGameState gamestate)
  2054. {
  2055. base.PerformFrame(gamestate);
  2056. currframecounter++;
  2057. if (currframecounter >= 750)
  2058. {
  2059. if (EnemyAction == "idle")
  2060. {
  2061. Debug.Print("Megaman entering Green");
  2062. EnemyAction = "green";
  2063. }
  2064. else if (EnemyAction == "green")
  2065. {
  2066. Debug.Print("Megaman entering idle ");
  2067. EnemyAction = "idle";
  2068. }
  2069. currframecounter = 0;
  2070. }
  2071. PointF VelocityUse = new PointF(0.25f, 0);
  2072. switch (EnemyAction)
  2073. {
  2074. case "idle":
  2075. break;
  2076. case "green":
  2077. //Location = new PointF((float)(Location.X + 3 * BCBlockGameState.rgen.NextDouble()) - 1.5f, Location.Y + (float)(3 * BCBlockGameState.rgen.NextDouble()) - 1.5f);
  2078. VelocityUse = new PointF(4, 0);
  2079. break;
  2080. }
  2081. BCBlockGameState.IncrementLocation(gamestate, ref _Location, VelocityUse);
  2082. return !gamestate.GameArea.IntersectsWith(this.GetRectangle());
  2083. //return false;
  2084. }
  2085. }
  2086. public class SillyFace : GameEnemy
  2087. {
  2088. int currframecounter = 0;
  2089. public SillyFace(PointF pLocation)
  2090. : base(pLocation, null, 50)
  2091. {
  2092. this.Location = pLocation;
  2093. StateFrameImageKeys = new Dictionary<string, string[]>();
  2094. StateFrameImageKeys.Add("idle", new string[] { "faceidle1", "faceidle2" });
  2095. StateFrameImageKeys.Add("attack", new string[] { "faceattack1", "faceattack2" });
  2096. StateFrameIndex = new Dictionary<string, int>();
  2097. StateFrameIndex.Add("idle", 0);
  2098. StateFrameIndex.Add("attack", 0);
  2099. }
  2100. public override bool PerformFrame(BCBlockGameState gamestate)
  2101. {
  2102. base.PerformFrame(gamestate);
  2103. currframecounter++;
  2104. if (currframecounter >= 750)
  2105. {
  2106. if (EnemyAction == "idle")
  2107. {
  2108. Debug.Print("SillyFace entering Attack state");
  2109. EnemyAction = "attack";
  2110. }
  2111. else if (EnemyAction == "attack")
  2112. {
  2113. Debug.Print("SillyFace entering idle state");
  2114. EnemyAction = "idle";
  2115. }
  2116. currframecounter = 0;
  2117. }
  2118. switch (EnemyAction)
  2119. {
  2120. case "idle":
  2121. break;
  2122. case "attack":
  2123. Location = new PointF((float)(Location.X + 3 * BCBlockGameState.rgen.NextDouble()) - 1.5f, Location.Y + (float)(3 * BCBlockGameState.rgen.NextDouble()) - 1.5f);
  2124. break;
  2125. }
  2126. return false;
  2127. }
  2128. }
  2129. public class BasicFadingText : GameObject
  2130. {
  2131. public delegate float GetSpeedVector(BasicFadingText obj);
  2132. /// <param name="obj"></param>
  2133. /// <returns>true to cancel default processing, false otherwise.</returns>
  2134. public delegate bool CustomFrameFunction(BasicFadingText obj);
  2135. private int HueCycle = 0;
  2136. public bool HueCycler(BasicFadingText obj)
  2137. {
  2138. obj.HueCycle=(obj.HueCycle+1)%240;
  2139. Color usecolor = new HSLColor(obj.HueCycle, 240, 120);
  2140. obj.TextBrush = new SolidBrush(usecolor);
  2141. return true;
  2142. }
  2143. public GetSpeedVector XSpeedDelegate;
  2144. public GetSpeedVector YSpeedDelegate;
  2145. public long numticks = 0;
  2146. private int maxTTL = 150; //max time to live
  2147. private int life = 0;
  2148. private String mText = "";
  2149. private PointF mPosition;
  2150. private PointF mVelocity;
  2151. private Font mFontUse;
  2152. private Brush mtextbrush;
  2153. private Pen mTextPen;
  2154. private GraphicsPath usepath;
  2155. private CustomFrameFunction _FrameFunction = null;
  2156. //revision to this class:
  2157. //changed to use a Bitmap instead of drawing on the target surface.
  2158. public String Text { get { return mText; } set { mText = value; BuildTextBitmap(); } }
  2159. public PointF Position { get { return mPosition; } set { mPosition = value; } }
  2160. public PointF Velocity { get { return mVelocity; } set { mVelocity = value; } }
  2161. public Font FontUse { get { return mFontUse; } set { mFontUse = value; BuildTextBitmap(); } }
  2162. public CustomFrameFunction FrameFunction { get { return _FrameFunction; } set { _FrameFunction = value; } }
  2163. public Brush TextBrush { get { return mtextbrush; } set { mtextbrush = value; BuildTextBitmap(); } }
  2164. public Pen TextPen { get { return mTextPen; } set { mTextPen = value; BuildTextBitmap(); } }
  2165. private Bitmap TextBitmap = null;
  2166. private Graphics TextCanvas = null;
  2167. public float myspeedfalloff = 0.95f;
  2168. /// <summary>
  2169. /// Creates the bitmap of this text.
  2170. /// </summary>
  2171. ///
  2172. private void BuildTextBitmap()
  2173. {
  2174. if (mTextPen == null)
  2175. mTextPen = new Pen(Color.Black);
  2176. if (mtextbrush == null)
  2177. mtextbrush = new SolidBrush(Color.Black);
  2178. //redraw the bitmap.
  2179. if (TextCanvas != null)
  2180. TextCanvas.Dispose();
  2181. if (TextBitmap != null)
  2182. TextBitmap.Dispose();
  2183. SizeF ssize = CalcSize();
  2184. //create a new bitmap of that size.
  2185. TextBitmap = new Bitmap((int)(Math.Ceiling(ssize.Width)), (int)Math.Ceiling(ssize.Height));
  2186. TextCanvas = Graphics.FromImage(TextBitmap);
  2187. TextCanvas.CompositingQuality = CompositingQuality.HighQuality;
  2188. TextCanvas.SmoothingMode = SmoothingMode.HighQuality;
  2189. //Draw to it... using a GraphicsPath.
  2190. usepath = new GraphicsPath();
  2191. usepath.AddString(mText, mFontUse.FontFamily, (int)mFontUse.Style, mFontUse.Size, new Point(0, 0), StringFormat.GenericDefault);
  2192. TextCanvas.DrawPath(mTextPen, usepath);
  2193. TextCanvas.FillPath(mtextbrush, usepath);
  2194. //tada...
  2195. }
  2196. public SizeF CalcSize()
  2197. {
  2198. //returns the size
  2199. Graphics measureg = BCBlockGameState.getmeasureg();
  2200. return measureg.MeasureString(mText, mFontUse);
  2201. }
  2202. /*
  2203. private void BuildTextBitmap()
  2204. {
  2205. //first, measure the text.
  2206. int penwidth = mTextPen==null?1:(int)mTextPen.Width;
  2207. SizeF textmeasure = BCBlockGameState.MeasureString(mText,mFontUse);
  2208. //create a new bitmap of that size.
  2209. TextBitmap = new Bitmap((int)textmeasure.Width+penwidth, (int)textmeasure.Height+penwidth);
  2210. TextCanvas = Graphics.FromImage(TextBitmap);
  2211. TextCanvas.PageUnit = GraphicsUnit.Point;
  2212. TextCanvas.PixelOffsetMode = PixelOffsetMode.HighQuality;
  2213. if (mtextbrush == null || mTextPen == null)
  2214. TextCanvas.DrawString(mText, mFontUse, new SolidBrush(Color.Black), new Point(0,0));
  2215. else
  2216. {
  2217. //create path.
  2218. GraphicsPath thispath = new GraphicsPath();
  2219. if (mText != null)
  2220. {
  2221. thispath.AddString(mText, mFontUse, new Point(penwidth, penwidth), StringFormat.GenericDefault);
  2222. //fill and stroke in our target bitmap...
  2223. TextCanvas.FillPath(mtextbrush, thispath);
  2224. TextCanvas.DrawPath(mTextPen, thispath);
  2225. }
  2226. //ta da....
  2227. }
  2228. }*/
  2229. public BasicFadingText(String textrise, PointF Position, PointF Velocity, Font fontuse, Pen TextPen, Brush textbrush)
  2230. : this(textrise, Position, Velocity, fontuse, TextPen, textbrush, 150)
  2231. {
  2232. }
  2233. public BasicFadingText(String textrise, PointF Position, PointF Velocity, Font fontuse, Pen TextPen, Brush textbrush, int TTL)
  2234. {
  2235. mText = textrise;
  2236. mPosition = Position;
  2237. mVelocity = Velocity;
  2238. mFontUse = fontuse;
  2239. mtextbrush = textbrush;
  2240. mTextPen = TextPen;
  2241. maxTTL = TTL;
  2242. FrameFunction = HueCycler;
  2243. BuildTextBitmap();
  2244. }
  2245. private Rectangle RectFToRect(RectangleF convfrom)
  2246. {
  2247. return new Rectangle((int)convfrom.Left, (int)convfrom.Top, (int)convfrom.Width, (int)convfrom.Height);
  2248. }
  2249. public override bool PerformFrame(BCBlockGameState gamestate)
  2250. {
  2251. numticks++;
  2252. bool dodefault = FrameFunction == null || FrameFunction(this);
  2253. if (dodefault)
  2254. {
  2255. mPosition = new PointF(mPosition.X + mVelocity.X, mPosition.Y + mVelocity.Y);
  2256. //mVelocity = new PointF(mVelocity.X,mVelocity.Y*myspeedfalloff);
  2257. if (XSpeedDelegate != null && YSpeedDelegate != null)
  2258. {
  2259. mVelocity = new PointF(XSpeedDelegate(this), YSpeedDelegate(this));
  2260. }
  2261. else if ((XSpeedDelegate != null) ^ (YSpeedDelegate != null))
  2262. {
  2263. if (XSpeedDelegate != null)
  2264. mVelocity = new PointF(XSpeedDelegate(this), mVelocity.Y);
  2265. else
  2266. mVelocity = new PointF(mVelocity.X, YSpeedDelegate(this));
  2267. }
  2268. life++;
  2269. }
  2270. bool retval = life > maxTTL;
  2271. if (measuredsize != null)
  2272. {
  2273. retval = retval || !(gamestate.GameArea.IntersectsWith(RectFToRect(new RectangleF(mPosition, measuredsize.Value))));
  2274. }
  2275. return (retval);
  2276. }
  2277. SizeF? measuredsize;
  2278. public override void Draw(Graphics g)
  2279. {
  2280. float alpharatio = 1f;
  2281. measuredsize = TextBitmap.Size;
  2282. PointF usePosition = new PointF(mPosition.X - (measuredsize.Value.Width / 2), mPosition.Y - (measuredsize.Value.Height / 2));
  2283. alpharatio = 1 - ((float)life) / ((float)maxTTL);
  2284. ImageAttributes useattributes = new ImageAttributes();
  2285. useattributes.SetColorMatrix(ColorMatrices.GetColourizer(1f, 1f, 1f, alpharatio));
  2286. Point usepos = new Point((int)usePosition.X, (int)usePosition.Y);
  2287. g.DrawImage(TextBitmap, new Rectangle(usepos, TextBitmap.Size), 0, 0, TextBitmap.Width, TextBitmap.Height, GraphicsUnit.Pixel, useattributes);
  2288. }
  2289. /*
  2290. public override void Draw(Graphics g)
  2291. {
  2292. measuredsize = g.MeasureString(mText,mFontUse);
  2293. PointF usePosition = new PointF(mPosition.X - (measuredsize.Value.Width / 2), mPosition.Y - (measuredsize.Value.Height / 2));
  2294. if (mtextbrush == null || mTextPen == null)
  2295. g.DrawString(mText, mFontUse, new SolidBrush(Color.Black), usePosition);
  2296. else
  2297. {
  2298. if (mText != null)
  2299. {
  2300. usepath = new GraphicsPath();
  2301. usepath.AddString(mText, mFontUse.FontFamily, (int) mFontUse.Style, mFontUse.Size, usePosition,
  2302. StringFormat.GenericDefault);
  2303. if (mtextbrush != null) g.FillPath(mtextbrush, usepath);
  2304. if (mTextPen != null) g.DrawPath(mTextPen, usepath);
  2305. }
  2306. }
  2307. }*/
  2308. }
  2309. }