From 7831f17da448b482b3fdfd1f4d1d2aecfefd00e2 Mon Sep 17 00:00:00 2001 From: Dmitriy Barabanschikov Date: Sun, 23 Mar 2014 22:30:18 +0400 Subject: [PATCH] Initial commit for github --- .build | 1 + .gitignore | 3 + Raider Wings.hxproj | 61 ++ application.xml | 31 + assets/effects/bulletHit/bulletHit.png | Bin 0 -> 6554 bytes assets/effects/debries/debries1.png | Bin 0 -> 1443 bytes assets/effects/debries/debries2.png | Bin 0 -> 1950 bytes assets/effects/debries/debries3.png | Bin 0 -> 1129 bytes assets/effects/explosion1/1.png | Bin 0 -> 12099 bytes assets/effects/explosion1/2.png | Bin 0 -> 18359 bytes assets/effects/explosion1/3.png | Bin 0 -> 26694 bytes assets/effects/explosion1/4.png | Bin 0 -> 13215 bytes assets/effects/explosion1/5.png | Bin 0 -> 10087 bytes assets/effects/projectiles/beam01.png | Bin 0 -> 1261 bytes assets/effects/projectiles/beam02.png | Bin 0 -> 1511 bytes .../projectiles/enemyBalloonBullet.png | Bin 0 -> 1240 bytes assets/effects/projectiles/railgunTrail.png | Bin 0 -> 3762 bytes assets/effects/smoke/smoke01.png | Bin 0 -> 1231 bytes assets/effects/smoke/smoke02.png | Bin 0 -> 1271 bytes assets/effects/smoke/smoke03.png | Bin 0 -> 1439 bytes assets/effects/smoke/smoke04.png | Bin 0 -> 1408 bytes assets/effects/smoke/smoke05.png | Bin 0 -> 1240 bytes assets/fonts/04B_03.TTF | Bin 0 -> 19492 bytes assets/fonts/04B_03.TTF.hash | 1 + assets/general/goButton.png | Bin 0 -> 1385 bytes assets/general/sky.png | Bin 0 -> 2099 bytes assets/objects/enemies/foe02.png | Bin 0 -> 5653 bytes assets/objects/enemies/tesla1.png | Bin 0 -> 11191 bytes assets/objects/enemies/tesla2.png | Bin 0 -> 10756 bytes assets/objects/enemies/tesla3.png | Bin 0 -> 9241 bytes assets/objects/player/plane01.png | Bin 0 -> 11861 bytes assets/openfl.svg | 17 + assets/rw.png | Bin 0 -> 912 bytes assets/rw.svg | 26 + assets/rw2.png | Bin 0 -> 1400 bytes assets/rw2.svg | 28 + assets/tiles/middle.png | Bin 0 -> 1436 bytes assets/tiles/middle2.png | Bin 0 -> 1521 bytes assets/tiles/slopeDown.png | Bin 0 -> 3753 bytes assets/tiles/slopeUp.png | Bin 0 -> 3746 bytes assets/tiles/top.png | Bin 0 -> 2311 bytes assets/tiles/top2.png | Bin 0 -> 2249 bytes assets/tiles/tst1.png | Bin 0 -> 2184 bytes assets/tiles/tst4.png | Bin 0 -> 2276 bytes assets/tiles/tstM.png | Bin 0 -> 2376 bytes assets/ui/gameOverText.png | Bin 0 -> 4384 bytes assets/ui/gameOverText.psd | Bin 0 -> 100878 bytes assets/ui/joystickHead.png | Bin 0 -> 8196 bytes assets/ui/joystickHead.psd | Bin 0 -> 89492 bytes openfl-readme.txt | 68 ++ src/FPS.hx | 38 ++ src/FontManager.hx | 18 + src/Main.hx | 64 ++ src/RaiderWings.hx | 228 +++++++ src/SOSLog.hx | 179 +++++ src/ScreenPad.hx | 88 +++ src/assets/EntityGraphicsAsset.hx | 93 +++ src/assets/GraphicsLibrary.hx | 183 +++++ src/com/sphaeraobscura/keyboard/KeyKeeper.hx | 65 ++ .../sphaeraobscura/keyboard/KeyKeeperEvent.hx | 28 + src/dataStructures/DLL.hx | 74 +++ src/dataStructures/DLLNode.hx | 31 + src/game/Bullet.hx | 80 +++ src/game/BulletHit.hx | 40 ++ src/game/CollisionCategory.hx | 19 + src/game/Debries.hx | 96 +++ src/game/EnemyBullet.hx | 55 ++ src/game/EnemyShip.hx | 250 +++++++ src/game/EntityType.hx | 23 + src/game/Explosion.hx | 42 ++ src/game/GameEntity.hx | 52 ++ src/game/GameEvent.hx | 23 + src/game/GameStage.hx | 143 ++++ src/game/GameWorld.hx | 365 ++++++++++ src/game/Level.hx | 288 ++++++++ src/game/LevelObjectTemplate.hx | 19 + src/game/PlayerController.hx | 87 +++ src/game/PlayerShip.hx | 176 +++++ src/game/RailgunTrail.hx | 50 ++ src/game/SmokeParticle.hx | 42 ++ src/game/Tile.hx | 63 ++ src/game/TileTemplate.hx | 20 + src/game/behaviours/BaloonBehaviour.hx | 70 ++ src/game/behaviours/BasicBehaviour.hx | 43 ++ src/game/behaviours/IBehaviour.hx | 10 + .../behaviours/KamikazeBaloonBehaviour.hx | 57 ++ .../TeslaBehaviourFrontalAssault.hx | 41 ++ .../TeslaBehaviourGoDownInFlames.hx | 29 + src/game/behaviours/TeslaBehaviourRam.hx | 50 ++ src/game/behaviours/TeslaBehaviourRetreat.hx | 46 ++ src/game/ui/GameOverScreen.hx | 104 +++ src/sphaeraGravita/collision/AABB.hx | 94 +++ .../collision/CollidingEntity.hx | 56 ++ .../collision/CollisionEngine.hx | 623 ++++++++++++++++++ .../collision/CollisionResult.hx | 26 + src/sphaeraGravita/collision/PhysicalBody.hx | 207 ++++++ .../collision/ProjectionResult.hx | 17 + src/sphaeraGravita/math/RotationMatrix.hx | 33 + src/sphaeraGravita/math/Vector2D.hx | 117 ++++ src/sphaeraGravita/shapes/BaseShape.hx | 33 + src/sphaeraGravita/shapes/CircleShape.hx | 28 + src/sphaeraGravita/shapes/Polygon.hx | 95 +++ src/sphaeraGravita/shapes/ShapeType.hx | 12 + .../spacePartitioning/GridCell.hx | 22 + .../spacePartitioning/UniformGrid.hx | 165 +++++ src/view/PhysicalWorldView.hx | 156 +++++ src/view/TilesView.hx | 151 +++++ src/view/WorldView.hx | 50 ++ 108 files changed, 5593 insertions(+) create mode 100644 .build create mode 100644 .gitignore create mode 100644 Raider Wings.hxproj create mode 100644 application.xml create mode 100644 assets/effects/bulletHit/bulletHit.png create mode 100644 assets/effects/debries/debries1.png create mode 100644 assets/effects/debries/debries2.png create mode 100644 assets/effects/debries/debries3.png create mode 100644 assets/effects/explosion1/1.png create mode 100644 assets/effects/explosion1/2.png create mode 100644 assets/effects/explosion1/3.png create mode 100644 assets/effects/explosion1/4.png create mode 100644 assets/effects/explosion1/5.png create mode 100644 assets/effects/projectiles/beam01.png create mode 100644 assets/effects/projectiles/beam02.png create mode 100644 assets/effects/projectiles/enemyBalloonBullet.png create mode 100644 assets/effects/projectiles/railgunTrail.png create mode 100644 assets/effects/smoke/smoke01.png create mode 100644 assets/effects/smoke/smoke02.png create mode 100644 assets/effects/smoke/smoke03.png create mode 100644 assets/effects/smoke/smoke04.png create mode 100644 assets/effects/smoke/smoke05.png create mode 100644 assets/fonts/04B_03.TTF create mode 100644 assets/fonts/04B_03.TTF.hash create mode 100644 assets/general/goButton.png create mode 100644 assets/general/sky.png create mode 100644 assets/objects/enemies/foe02.png create mode 100644 assets/objects/enemies/tesla1.png create mode 100644 assets/objects/enemies/tesla2.png create mode 100644 assets/objects/enemies/tesla3.png create mode 100644 assets/objects/player/plane01.png create mode 100644 assets/openfl.svg create mode 100644 assets/rw.png create mode 100644 assets/rw.svg create mode 100644 assets/rw2.png create mode 100644 assets/rw2.svg create mode 100644 assets/tiles/middle.png create mode 100644 assets/tiles/middle2.png create mode 100644 assets/tiles/slopeDown.png create mode 100644 assets/tiles/slopeUp.png create mode 100644 assets/tiles/top.png create mode 100644 assets/tiles/top2.png create mode 100644 assets/tiles/tst1.png create mode 100644 assets/tiles/tst4.png create mode 100644 assets/tiles/tstM.png create mode 100644 assets/ui/gameOverText.png create mode 100644 assets/ui/gameOverText.psd create mode 100644 assets/ui/joystickHead.png create mode 100644 assets/ui/joystickHead.psd create mode 100644 openfl-readme.txt create mode 100644 src/FPS.hx create mode 100644 src/FontManager.hx create mode 100644 src/Main.hx create mode 100644 src/RaiderWings.hx create mode 100644 src/SOSLog.hx create mode 100644 src/ScreenPad.hx create mode 100644 src/assets/EntityGraphicsAsset.hx create mode 100644 src/assets/GraphicsLibrary.hx create mode 100644 src/com/sphaeraobscura/keyboard/KeyKeeper.hx create mode 100644 src/com/sphaeraobscura/keyboard/KeyKeeperEvent.hx create mode 100644 src/dataStructures/DLL.hx create mode 100644 src/dataStructures/DLLNode.hx create mode 100644 src/game/Bullet.hx create mode 100644 src/game/BulletHit.hx create mode 100644 src/game/CollisionCategory.hx create mode 100644 src/game/Debries.hx create mode 100644 src/game/EnemyBullet.hx create mode 100644 src/game/EnemyShip.hx create mode 100644 src/game/EntityType.hx create mode 100644 src/game/Explosion.hx create mode 100644 src/game/GameEntity.hx create mode 100644 src/game/GameEvent.hx create mode 100644 src/game/GameStage.hx create mode 100644 src/game/GameWorld.hx create mode 100644 src/game/Level.hx create mode 100644 src/game/LevelObjectTemplate.hx create mode 100644 src/game/PlayerController.hx create mode 100644 src/game/PlayerShip.hx create mode 100644 src/game/RailgunTrail.hx create mode 100644 src/game/SmokeParticle.hx create mode 100644 src/game/Tile.hx create mode 100644 src/game/TileTemplate.hx create mode 100644 src/game/behaviours/BaloonBehaviour.hx create mode 100644 src/game/behaviours/BasicBehaviour.hx create mode 100644 src/game/behaviours/IBehaviour.hx create mode 100644 src/game/behaviours/KamikazeBaloonBehaviour.hx create mode 100644 src/game/behaviours/TeslaBehaviourFrontalAssault.hx create mode 100644 src/game/behaviours/TeslaBehaviourGoDownInFlames.hx create mode 100644 src/game/behaviours/TeslaBehaviourRam.hx create mode 100644 src/game/behaviours/TeslaBehaviourRetreat.hx create mode 100644 src/game/ui/GameOverScreen.hx create mode 100644 src/sphaeraGravita/collision/AABB.hx create mode 100644 src/sphaeraGravita/collision/CollidingEntity.hx create mode 100644 src/sphaeraGravita/collision/CollisionEngine.hx create mode 100644 src/sphaeraGravita/collision/CollisionResult.hx create mode 100644 src/sphaeraGravita/collision/PhysicalBody.hx create mode 100644 src/sphaeraGravita/collision/ProjectionResult.hx create mode 100644 src/sphaeraGravita/math/RotationMatrix.hx create mode 100644 src/sphaeraGravita/math/Vector2D.hx create mode 100644 src/sphaeraGravita/shapes/BaseShape.hx create mode 100644 src/sphaeraGravita/shapes/CircleShape.hx create mode 100644 src/sphaeraGravita/shapes/Polygon.hx create mode 100644 src/sphaeraGravita/shapes/ShapeType.hx create mode 100644 src/sphaeraGravita/spacePartitioning/GridCell.hx create mode 100644 src/sphaeraGravita/spacePartitioning/UniformGrid.hx create mode 100644 src/view/PhysicalWorldView.hx create mode 100644 src/view/TilesView.hx create mode 100644 src/view/WorldView.hx diff --git a/.build b/.build new file mode 100644 index 0000000..62f9457 --- /dev/null +++ b/.build @@ -0,0 +1 @@ +6 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1172fa4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +bin +unoptimized assets +/*.keystore diff --git a/Raider Wings.hxproj b/Raider Wings.hxproj new file mode 100644 index 0000000..b2df5b3 --- /dev/null +++ b/Raider Wings.hxproj @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/application.xml b/application.xml new file mode 100644 index 0000000..9f26ed3 --- /dev/null +++ b/application.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/assets/effects/bulletHit/bulletHit.png b/assets/effects/bulletHit/bulletHit.png new file mode 100644 index 0000000000000000000000000000000000000000..b3aa363915450529198bd2d6b7c92e3aa2314204 GIT binary patch literal 6554 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 gwb_@%OFE92pA=l8+|GGZ|2mo13Ln zA6VJcX7H3-GGq_s7X^u^fQuL{H@2(Pud}&Bou%`K%XYr6tt(sT`YW79FSn=bt{XtM zd;DLr++%*}>!RL~)W)5V19sVS|6#p)Yu1lt_Sl$RD6K5Uf>k2Ma!xbD+0gw#n)XEg zo!RuN;SWYCC+xwd($%b5BfamF0=N}@ewc#pK83^<)GY*u3ToKNAwpv_ebck^PltbP zgqsxm^rv@LQV1=~HIkV%J8w<+EhssU>arsXcSbaf#O6+jvQ>vZUS3pDD^9wKvHAuf zQ*Eqr2erTUqzruE$eDKY$TPpzs=Tfp<9>NhBFLvUq6JzsUCHPDuJ`MB36`SF&OsML z`c*M|n9b;AlhJnI%1pEG;o{EV6u!7L=5D2yCFJrgFW$l(4g;rKjxtW>J89MG2vc|y zJ0@VbH1uG!ab8t1c0Ln_nb7%nwLjt2D$PE~8!tSv+;@D!cW24x1uj z;q+H>8rh` zQY;NozM_EDC5$M~p-$P=azJ8ov#VI(?H?hJ6BoAaT%?J!xG1Y;^Y5v0+e%s##V&vU z)owU`MwcaMj2*bF*>$8d^gq;AbRWxkD^Ws^MSK9x6c*&%XGdocHL+a+MH9i;nq_fu zu8s=7K0o2PlcN4bqHA6DiZJ8p+LuaTndI~8L#-F>u3-$2B8li6kztC!o&zu)XGqrf zD;n`4C8L_|B|jDZ3x7Bd%BAQ+jAzAi$m>bTisI{9mhNf+83;zRw{f**TO_a?Wc;Pe zs6Dr4qyk%4j0V7qY+~b>W0}AUt^;+iQ=O5L{-LCoJ~@QR2?stMfSyL3_{(u%P3F6; z5iq~>A$YYhs3)w1ri~%qO0#!2n6z(D-OK*0xQs-V>*IQ6Udx%0y{1G-tpc){#>z#x z0rTsktBE+}>71-r$lFF_;wYny4RBvcQUm<6;3?n4heEQKBy;9--_!h~yPop1o~6#Q zwTeEh3?=s47#`^~i~*7q2^Ae!eI?Oq<_u2@8F=~8Y_cH>TD)n^R$yvHAv2Ix%KC`n zLP6X#M?%eckh5lWQNFrlc{3m@&rB+%exU1XA<=I>BO}S#asupXDY>ck>}>Sh7>8kS zfoEpiH!ItkIf?OyU^ZaWfp^ZzzCM|9m+#&i)=sCk%JXZbW$PKl0@992HMDxDP=mS< zZw=z)%Rg1YW(+mC!#p9(>Ih_9YmeeP{P?Esd-iPL&dYv{!ToxjiQRPN1 zX!IIR=Ou0%gbcmp_(^@PhTxbxBd-zauyY}pImJ3-z`&wm)*c2V9O z(|t%X&9(L@40p8?d# zND;IgAp`%MH+%=r=`yU0aX9i0+s z#(kC;W7oNe2t{T0QyGONzB255KTK>VKCjF(q$GV7ptEhae*wO-AlW|E{I;aY)T@1= zS~Mn?OwLgSh7T7cK%~BNhnKL+Rpf!C77-bMNpJ_MqZ~cQFdn$3%^;BA+$} z=WLtzzM$sc7UO3&@DZ%AHa=U&FcQp#K~Y-sckb$^ZRMlqro3f-Xfr0~8-sOIMoWz! z5F;slZ1uh*eGStsu^%}*a}B6HYQJWE*4;4QMsMp8ffJqOjH3uDY#(Jm@n=@F=gP@R zXMfw_O*YMZt6@gDyS|63v5P0$1fnej5u)73J)HGuoDw^`5DLA9r)_u7Wc_O0U%ke^ akK;M`>dPMMSL~m^C8~;A@Jjh7!T$q$;0YiA literal 0 HcmV?d00001 diff --git a/assets/effects/debries/debries1.png b/assets/effects/debries/debries1.png new file mode 100644 index 0000000000000000000000000000000000000000..5d22949922e2bb09dbaf764620dd9cc8f82aa5d3 GIT binary patch literal 1443 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 V&s!-?O^%j7rZ_lUR4d%J^S|=F7o`9I literal 0 HcmV?d00001 diff --git a/assets/effects/debries/debries3.png b/assets/effects/debries/debries3.png new file mode 100644 index 0000000000000000000000000000000000000000..15f29155504f5d87d4a4f281092beb2f7cee751b GIT binary patch literal 1129 zcmaJ=TWHfz7|y&qy4oq|tSDI&#Y>x{={2ru+pJnx=IAP$D3~-k+rpNdn4H;cPxFGH z;0+)2MW19(BAX%%2UF&S34)>^BHs3(_#h~P_#)1exlSLfdC2+C;rqV-`~UNw6D`d< zYpVU#1VPl~b_sdBdfa%?0{p+U@k|;oD^X)RYK0xBBwK(;sjvvhoGy2QJdoACgAYLi zLCht!LOW^~n|KB4LD|Iw9o@uif@s+8n6lCh5LpDBnvtYFkKU$8O-)j55s?wi4CvB! z^;@8|zqz3FcPj~%+P;l!aCj`B10<7<-ecIjlcc6~c^tdPG(}EBPtAv4ed zWF*K06eh}$Q8vhO;aDuTg$yw)Lo*?o4F%X3&xLq~A!j}cXS37}J}+cva^YQ)>O#om zX}VM@1xs8ITAehTNF-d1P$+;A0lUvYvJ)`uwPghX*ovi@NP`CHD#}IJi;@&}`nv?( zoRu}~nKt2u(T;4=Y>;tFDg#CF|4?0@McXJ3{`mc;uwCdg0i6dn?6nkpaUE;jR3@LX zfQ+D3fUu|B#g;CJpxp&cGSeC%+cZOkC3_kzihRznk!&a+CnPD%4{DmqN8;fq$HpV! za4f{Kjch6#;!<2R9nZ$&0uxS`xdK#rbzq<}SN+3{R^+;apqn_e04(hgP_q`)$?1Z5 zt+E%cqTUQwt?VUSkxOGUw0o_8U32*e&yE|;4lUlyjy*8&tXp`fZ=F|b@q>7q6H*1| zWziE+b}X1$IW(|m!xT6EqG{sf%`0_vQr$f16JtM^`sn|;`?M^zEYC=1$GacUX zsd?q!WZo|wsJ}XYI8~g8{`ek|q<81OsZVaSrVeh6?zf} zGjjKf^<5oN2bZO5uUBoEFy>Fb7~emnOXhp;aZy?|_&MWvKZGyV4-tolQ~o-6&Cx{7 z;No@OYM+l7PSq|dJ-xQs<1u)jc)|O+>TCVasuSOuMvqP|JR6vjFVEe(gj=op9-*Oh d{@KzU169OiR-UusR7b&m{JF+v;aXZc@(T*;abf@f literal 0 HcmV?d00001 diff --git a/assets/effects/explosion1/1.png b/assets/effects/explosion1/1.png new file mode 100644 index 0000000000000000000000000000000000000000..9be0ef574f408e32e048a09c51dc739abd3fbd1a GIT binary patch literal 12099 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 literal 0 HcmV?d00001 diff --git a/assets/effects/explosion1/2.png b/assets/effects/explosion1/2.png new file mode 100644 index 0000000000000000000000000000000000000000..5117873b5a0d377a4e9b73c2f7f1cec2136a26dc GIT binary patch literal 18359 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 literal 0 HcmV?d00001 diff --git a/assets/effects/explosion1/5.png b/assets/effects/explosion1/5.png new file mode 100644 index 0000000000000000000000000000000000000000..9b64e9c7a678b9667f31dcadc336fe9f96cf86e9 GIT binary patch literal 10087 zcma)i1yEeUvi2e&A$V{nxXa=WfklE765QFvVRzZ!P67mXcS#5dF2OapyGxMZ4#DAV z?z{KCzv_SYum4o-IcK{1>+YH9?VdT)pET7Kah_8=2LJ##%1ZLu$Y;*q-!ly4J$%HP z3;7^Nfb68NB}uO^(@DtW@eZ#>m? zEIsWlMXl(irRXHw#gGCV!3c9YcSi>&7cqB9`hUn3L%#nd^U%}%g9TwPN&nwQ>8WYb z$vMNpbb{QxT$a2-ymUf*+5-QHcNZL?{}$`y^3P#H4j7NSIh2QwoA+;*{>7-K z_Wx#ebo@8B3ql+GKlc8wf?aexpkN+tu#5A1xFvGpAPj$1LB-_YU~_~sT*ukj;a{Vu zY2%D=cCm4W(#gFMq|>!^vT}BF`3JX}nwYYa3&PyV609sQNsq*H+uB-*0r_M>BBDZq z0zi2YK0XkiC?7~vUPxHrm8hVcD2Si`Us!o(%lD38C&a(7R{w()`j6PZj^GGIYL*AX zZC$}u3UFsfx_@+7%=SOq!v7!T{S#~TpKTHNk60e0F+6{#_5YaWf02;B^Y``NPK#{( z+wH+lNMDB|otpGdz5oEgu%s+6tK&Yu?}LfZnMwU!(){}Eyc=dkxS9nYhH805W@_EU z!6Dp-K`33; zLJ<}7;00Y`ZL#*0_sUyhBG5rNNnPvkgxAIHl-K3b?UG;P<9%88`pGj8gCfOft>eP! zl9Sub>gsADsi}9xqm!p!Nnl&=_~BAxc(uWS>E6Z75TxpwN7!LDM;D+Y;IM%ZQz=hJ zQAi26&)3)2zkco?795F<(aGe0gs+pU?(I;wphF)yj-r^g?0q(*8v3hxaBwin*=NrX z_lXQBEaBO^0zi>cENTw@-zE`&m<_HMlc z%MV;A9?^AOYDhE2VY3EGIVx=HHeJ2 zqC?YG-)81mN-yi#d%CIBv+;Zn4-XeYU=$HaaQ3p#r7?AzFf|?Dl1zbuW&1l5ef_WD zmo0|14t$i6-N{frk{8z$k)yWnQD-pY#~5=M`duE-gFsdV)}fW-usrYA^(jExFxEjH zB1rsol6|x1X14QuHJekXf@q$iKLsV_({@o2%%x3!TnV`bO^(_+BdSrB_jOys&^p#h zL9vo|8&+GPm|#MA*LOn3{Np=|vx}^|WdhG;a#T^y?LF?FrwOlOBz6Wyh2fLa0UmonlaF5- z8s?j=M`$kfyzoMgUxl`5 z8QnjPQQE#_dA`Q-A3APe+XnJJy{bZg^oG3@E16Er&R)TlIaF+ZXzct|r1qOvKMuvl zVnJ~Ys#Y>&F`r;N{+Nut#(&`2c)T_ov}O7hfPI+N3%F|e)TtQMT=p)XSfN>#to$T} zReof;5hD|Xn|xLwd%3DRX0rv&&c?&W_7<9li*!Cec(v{&|Dn9tgbZt{+-U3FVEs@l zDPAu3&x_JMDPG(>Dm6s_Yk1NRa-y`0e}q;Q;cYjpSVS?7-~@EoEh8wPJ>kmN^{m z$xi$H3^2}{BMgk2u833e2Ml>5#EEtZ8FbNxTYSD!kQ-Mj!&l}=bBt8W+;45FwWzWD z<*jBEg}3ixeza7vdN3d0BsJWtrQ=tL*(yVw2oS*ilpDBB+$;ut#UOMaqx2`Pa}!?@ zty#)AB%idk`+`1krEq(%JNIv!}mG#}lwnB8#T}6LKDg6Xi31oGPc}hX%VYWXbjh=ECo< zdZdN&wBLP1%_g9ugyKB&@{2}K#sX{z`CEFm8Y3>}x^iwmzsa8Yk~$uY=*o=^mNu)# zl#w!&C(z2U0CYeoylIaXXc7;DrN0a?aZj0&obb|8@GDA5ui;4r9(KAjb(8~QJX5+X zJo+eODNEZOX5UGikIG##o=R+sn|3>$a5Z4a7ZcL~$yF`dMCl~2azicijD40mM7NuE zE>WMUdB#723ybb(Q9raQatp-K8C5~l7-X*GsA@s>GS$^Jp~0Uy_{gboB5~FLBD5FE z(hdGcQQeO6g$C%Yw}*|Mda*0rzh*47_;M&)@JB|8di1XKjw+v;!0hAblPwaccy{~S zA%ccXPtUbc}t0M=$;cjZ6$Lzu+kzi4D~Ge=PJ zFqpkUiSF*6!`foBf%!d_Wx8J;Nk875g{*VA^+)29ktP{O-igNXmRoz>yW%k@M-?NlwYcPbNop!4aVEX>`}gj$$5!TLGMAwc zJsmo}9vueILGjC@xZc;vnz<9TUz~Hh6H|E=U$r7Q*pP7oBS`jm%H})5q>(+%AdL}V zZiW8UX+%0_om!J#>mV8`*F8=vCA8tYh3#ni9!@Tc-5$4$UjPrm@iozTIdy=IpO+Bu6_v_KpH18QoO+lG{oA{95ILpD4|o(S zJL?NuTiXtc+Ft*UsS}Ve(JIJ5?X!0{8MG1i(SVl2g6&eaX_eQr6A-m*CLljB|A9K+ zbxUV>*k7CW!uj;J6zE1yfxldV{iR{^MR1_|}qqV!|Y{M);;SEco3IsobAb}_`6q$pbSQ6>X z{WkMDZ@yesQF$K!nhBsaN)tZEc?ySHJ~*1FoBW#4kRBq4UuN~w{;;L*1aq5BU>FEh)BY^sHpRch+(6m>45=%iYy!1JfoB)*sTN=Mu zrd%8Ef$LA2I~_jVJoktubZ$E7R#|m0{ZMg2x+7$$2T|YQj@kIn-_jkCCVuooW5^X-qA7tYS? zfz(MNzJ$%KwKJ)7{X8L*i{k#NOf{$X8!GY;79X=#kAhI6P?~5sa3vplp{#g_Rnk35 zA@zh~L9kdfC%Or3s^^>RPVl*RW;=i1Cc*l98nq7b)AfQqh#YUZ{2S0wcFMd|j&nDqGsL zEXTXMdj-`gVz#@Evc(&}d%i_u!TazRkAPwwxmH(VA%ZmJd3k}7pZgjQu-ID!k6Z^eAsD!teSd@(ukh|rat9fwYgi_ zG+T#ikK6+gY6)6h*mp((ez{cl%5atxZ1oG%9mNP)XBTvY!PwLi)3AvM2QKx!4%O9u zPLZg~Yjtfr?db0dVs{Wqk>Bll=Vw}-C=9U!n_^G#h5VZp_l+aSy;lRCj<;iW=UYJ5 z+^RypWe~Miq>xZ;d~tH$ILi+hWBe2d#jTs4nJF`UT*>Rx?DC)l#7t0zlZTcX`iI&3 zG6j2oW2SPmFpXUCeP>`WpD<{1to1~OwPwckaPc}HlaLt^+yFWIJv7M7d)fY!wg9Xb z5Y{&;e~QhLO2!MT{9RC#qE9gW_LoLsS7BaFvzk$HYl4+R(e-*pM#jB}6gdu{!g7oI z6O{4D}koOn{p~kNFQMDW9l^lhYwQ;l6_P=X-|3dMedi;lvA(U491MaGSSp zrx@@B-{Mz2c8Oc$kng}N)?H1Gh@MJGvhu5fHb?nWyC(bI6h4loK@z8}fsmqHPU7v1 ziTom!T^Ma8kNjBsYt7moZR45zG&Hycfh)(3CmBu-7RY0+-r}UAYn+sf%e|hjH~zV_ z-1<(_=SJp$KUaeCzxS45D={09ntr$;encQqQGEwNk-e_QD73@b3QM4CnPsCi@#>j6 zZyY5Wn2@|;yKbIsI7^yR(0zly{kj*$#RXnCa{P-4PkF1xgf!bA%9^LQzrRwUAPqV?RCNK+O3;@$!$_fe4xAF4EuU~bzaXuzLgwm(j%ia zyl}t2ZT<@?X5pd8)h{AQ0fs1HZ z-we9@OK=B!o_OD#^t7Vb`~U%8*Hy+b&+XJGPy$k^X{I;U-ABR-D%?UxYzh0jniJ3B zlM;HKc6T(rs<_GmX&2nOwEu}Jh|v6W0$yjrw`4TrB=tLPe|m__u@H|dsahz#>wFTI z;pw3b;P=*6v|O~Ggg(a8vz7=R3KocJW)_jIA}{^Z41UX~jG_$&(~c~w?Qi`3Ulbkn zZ*OF%oVZ8&6FH(~@jkOYi2Bd6InhEU(p5hvaJuueBV?R?<5@r!dnrpg2G|O*wnGU! zy>V18-h1@~jJ1_iyblfdkfuh0$G0uW!eJPdBd?r!B4t06CYZUpe{`_ieCyLBM=o|? zHN4X$}29Ot}jKa8l2XCP4_PT?XFjbcf4KPnnnDmGhuuR47f64qGM1zMmg)s)RGq;-Y~1qSFB9)1S(~Hn_-+!HghZOMxmX?_3+pWq2H# zklYG2l|6t8hWo6!kWTBwo-eJCsULF++lylQ)N3)Xx?FxRT~Jq*Jv{{xzJPW}F?0t( ztbtaOzSTM{*}VZol!Ulky>xmrmBZgVX|Kw|SK;vL=U%G!mch(>SmAY89bz-i#=(|o zNpk5&gq#B9++LW816(zV?&E%Q*Hz?&!-HEkS4EH2wnTYqe73 zsyQbLi5nPhlY|b|+*!oQ6g*D{U@V^&qdlp9$k^FB6OeM75>0;#co%A-EMv2V(p<%tg z!NsBu)x9sVp1x3pS#G>niTbk|7NYb_+_lXMGD7GPgDQoqmWiL^Q&~{~D<$g^SOZ{Z zu#-t{O)lH`w~3NpsNt;H3{}L?3p@E>FS2UVsx)ytA#MILk{vtnGUl1sZ#b^-cO}6B zI0TeK+4~G%YCr(`+&>otb<1dNgzaY+=G5fBr4oNHv39N)CXkwyq@Obg5j73U?yT49 z2poBu#YjenJ>({uDGcZ-vQ6T95laZypZ51mdnWT301-)?;e$N+(W5JzmCjAbGfP(C zy|FPClMy{q@wxERTE`159ZlBn?-oY$1dN~>?prnpyrHpG;2%|Ts;gTlSnZvsGC8%e zsIHZjRam?ri&ZMa6hVnOq|#c3W99_snEIhFidx(^$R`AK^#I^NAlK()VJF26lzXJj z9K}xla_V!cTCR=dOMz}uxpp2ScX@)KRR32H*&IQyMH~t12j?l z4;AIK>kYgbo;li19&i||Z~&$kMDvi1DGwp+22F^$3T4ZT+(%yhg4Kf?OoP`=j}9C7 z4k?wqU5)RVBnHw10F@f!yOQq@t2erT1=^AGb=Gql$p`BnQ~gl<=;JYv#PtZACo!ag zQR9p0HS$!w-G4R9kUV2&=S=d2I|Eb9ipfPsBb}V^>JBA`f4cxCt0mcLVKQp1DJ%iI z`A{vpU9Qf&xwVBw=-)GVkt@#=;CDKyoGR1&wNSK(Cftop&N!J3m#kMbE$!y?^pra% z@G$a{X7ax72}7x3$81}jX!T43`19}Y5uAMZB3jQ@GDTcoj2uy;Iz*t9$4=jXyJtXW zq)!ALpS5V<9>m)Pj!6DwLwN~YtSZs(*&=#+&cHk+i?**a=}6EoogAAL>#kGXjrEHA zc9Rww=90mrI|JQOAiuFpQSURFAl>DbcVdJJ7~_{tFV1s{iHSRTxb1eBLngSC5HEYh zqtHCPPOWVgeVwn|DwibsMs=LsPJgZESZh*A!OnyAl6H^~kbYlXkr)n3)qsR|{2E6h zgZ!0W%62MDC7Byc%_1SuqijV^POh*$6Wr1wm5DVuF~NV_&rycmAjj69PwWq=s(){0 z=8v_!l)dl8fALX#a1D4)+P}!1_EJSug$I{M$tj}+H>0rZoS?>Jg;9&U9$yk`rl+_!Qyry&dC3sGF;pbs zh4sb7yKfw_a~Om`uL*(d^1PpR=mBw(^z8|cvXl*t;!uwLxU+{>mfHH z?s{5xJ`(G)xwRQS$8SBL?_gBOMzIt<&Hfc{@keu&B<`?Na(553$k@5{-jz?DYYUfO zOUIVznyfym*Y8!2C&41GmntBJ!%q(Tk^1>^(%@|zB_A{(Ba$jQLv0T&8xorD(bN{_ zmuC25;hfw5u=gY%sNLn}x9k0qb0lYSMH! zvT^9vUF3{PE@t&;=O_Eo7Y-}l>yg5$`858F%q! zMiej9RZH@JfJcT{XU2gNT?P@~J;H@ib*Qn9fs3wwLvw zA354Q&otIRCY`@z%73&UdJp!Ci9Au{OIt_-C-gbv`O;Gursd(5(Ccvv8c91h~1TIT*E)FzY1WoFUg8R+wPOJtP^3iPbw8Y5w+03MIX}B=wAPPW{IJ>){ z+slt21eg)tUY-GOxLUsbIo^fWTVcn%u(A7D!sO=TQ$TN+SzMcvPi#0}lmUlGPKAEe z?GNXb2ek9r_Z>VdnC!;kM}C24gNvflCfP5!Mn|Hkli23#f?-asrK6PRKSGI26pf{Pr7e+lKY$@79{I{f`(&N^&%aF4`RWEQ~K!PsrCi0LZUT8LPRs zKkaJz>%;>;L@r{%RJ4>$4Mm*f4wb@%IQY+M2}ph2cK2;}c%{&W2Ws34`Nco9Y#Uam z$BGQ5?4ltmNmkk=ht0#Ew?nE@J?aYO_@#L?2D1dki?91YA3+u!HJI=*SSxTL=Q;QZ z+G^1-K{I@_-{5@qi134Z>d@>XNu+1gH>v5#$)<3ZmCpJJ`T%_y__;-nX%s8I(uOOx zbptyKvuK{3JL<(%-$)^iqB=-xZg!FJkDsTEfK2s^P|`g+l#Z$wSJ+XZlPSPK>{cw` z4(p*VC`6LV@w1n|bo|_D-w7mPBvec}E`tuki5y=<_ERY(A-zb9rnyczMbkpry%Cy( zA9~)6K;5*DO@VZ`3Q*P(2nnYBdEZaoO;iKM!k?%x@~T%CdPN7dG0O<5Q3=YYY9M5Q z7%T(}#-hy7ND&0p(O6yY7=(c(gDe9y&7 z$(sP#q~?@Ina2fKP+E7?yRr7VkypiT^xI@c?xn<0hGQ%{l&v*-gPQyG+RZA7v44C8Ru=FGopKWIdDUjML zIs0+4?}uF*mG+?i&9;nK?ZkVjaq45kpNkpOvfxU!@SA8&WpX!OfZtQ&8wXBle=C^N zQp1I{WV578(>E=U^$93YBN2w;^;3TJIQV5=in*chQXJo&yY0nzoc zbuXvV6f&5frieT@{bi=1F@hJLNcp=rmf^<1{ttT4a(NrWVi!%M4S`dIDTgkmTiMhc z4iCu)SCmIbJgS+y?gxx85#1dt#%Cc9b-tKhLe{6$!C5Zolo5`d6y7ExVRf4XQ6yvL z*TZ^1i+gh{-MC6Bvzu5iMjxYvfiDx|c0=yI;48WgX?@qE7oO0$ta@+jV~+-|D4wn9(ixqqkpIUmH@dJiS(6R$x_pl2(sjB4~0 z^dyQE`eDoM>YitQexBRK=^|ySgczVGuj*o3>3t*-&a@Fl_57y^DQju7|N4f{=hu=! z(YJs#%fiA!Y7L2#4_@DAKBBHZn?U}v&L=*rRgpz@OZZ2qByE~kjW=WDjD^euuaksy zjxA43O%3!Jx+S>DBI^Z|;@-Ut_`mB2{?;4(qqG27Z}7LY09kLK#jaFVY<3l@H8IP# z1~SyvUNfn6n9H$F!0SF*YD}KM|DBtnI@IXA*`KIv?5zHT0UHhZncT}3dEW0*A1a{B zX;BHyfMk(yYgG7kg%MNd*X)bC9cBcaI{h*t-QSB;Sf@Z_?7c>hCa{P*TwY$@f?i4H zw_n{N*IOM{K(!gW8lyN1CJN-y0vm0~<`xiJb%MD*QGvg-wY61d$KO(MLwivQV^k8D z@7-_`&~+Iek|e>J#CaDu$ZQ8ptG>!I^R~VHb1;{GLT$vYD=5}?1np(ZdWwS~7lgV> zmKzutIEbBoFc414InCS0aJJ9qMicMNzr>|0a`{;*y;u1d2^9HJm!H2bh)4iyGMK3S zi4X^WD2N%Km~erprKculrV^ifeLmlOfdjASe)${agR}-+h)TarXE2WHRYsh$Mi|=X zn6`_`GnX^DX}*c&8*t)^G(~%|oi;%ACGl_KwDu_pb0Y6lMLe--kpD@3V?`7Z;Ej@J zuC>8@3}M%T_`D=CxJ<2jrzi24`W_s9X8^I~OR1Tb^jS*u3x09FEo~j~Ev!E|5|m8C z(`~NXonJCg>?g>yuq$?Cr#csTG;$XR*IdBZ_gbR4lnv>;zT70)d}W4WadQ*PMz#`U3a^>pv?@{uj^q|1D1WU!Yp(H;<^;0Ma1& VxaW9@e@p2A${=<5(pP4I{{@HW^L79L literal 0 HcmV?d00001 diff --git a/assets/effects/projectiles/beam01.png b/assets/effects/projectiles/beam01.png new file mode 100644 index 0000000000000000000000000000000000000000..076e305894615c473b4fc9c1919ec82063e0156c GIT binary patch literal 1261 zcmeAS@N?(olHy`uVBq!ia0vp^d_c^@!3HFQ8hUO4DajJoh?3y^w370~qErUQl>DSr z1<%~X^wgl##FWaylc_cg49p6dArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB=XP}#GU}m6TW~gUq zY+`P1uA^XNU}&IkV5Dzoq-$tyWo%?+V4wg6Nh+i#(Mch>H3D2mX;thjEr=FDs+o0^GXscbn}XpVJ5hw7AF^F7L;V>=P7_pOiaoz zEwNPsx)kDt+yc06!V;*iRM zRQ;gT;{4L0WMIUlDTAykuyQU+O)SYT3dzsUfrVl~Mt(_taYlZDf^)E`LU?9gN`84U zShcUOm1kaYNn&1ds;7&s5>UThW@d_&o0*BFtEscOiHW(Rp`ojxqnV+jqq&*0fu*yt zi;I&ZOs`9Ra%paAUI|QZ3PP_1PQ9RnkXrz>*(J3ovn(~mttdZN0qkL`Ox$iU#c3W? zZwhX=nBml`4|I$^C|Z%C872fwJ0K=J(E>T}WS^P`OzTC!M9nvA%31~n#$-F! zDS>vCtzu`wEquZ6n#+eLEZc`XL zIis$z1+Hi?;q>IQws?3Y>Q~bI;`x7Xs!zJy$j6|Tq{-ua`%Z+7wCw(EANF*vQ2ZvC zTU;#{B5Y~+<&o?c-y@<){r}$!bvB)HTG>|~r?E^aYW?lYh98eE5p89eqkKtfSLTME zofA_fBHcAr7i{5HOHDI7Wl*&E&x;N5p-;K~pV0PjX?QwwzkRv$)H&iOPW(Ohp`n90 z>d=i3le$)QzIehr>HL;}0H-~lzp3%=es)YHp|r1UeonB(^Z!Q`dNumyJ(}=jQ%d`G zoto1-6jx4}6Tvr0gJ)x~YwffHM(Z|yU;4xDi5)0N|KX8U}fi7AzZCsS=07?>3@Ln2Bde0{8v^Ko2Tt~skz|cV7z)0WFNY~KZ%Gk)tz(4^Clz_GsrKDK}xwt{?0`hE?GD=Dctn~HE z%ggo3jrH=2()A53EiFN27#ZmTRp=I1=9MH?=;jqG!%T2VElw`VEGWs$&r<-In3$Ab zT4JjNbScCOxdm`z^NOLt1Pn0!io^naLp=kKmtYEgeeo;J&4sHjE(uCSxEHIz#UYgi zsro^w#rdU0$-sz9QwCX8VC7ttnpl!w6q28x0}I7~jQo=P;*9(P1?ON>h49S0l>G8y zuxejlE6=>*lEl2^R8JRMC7^!2%*+%kH#18U3o}y}6C)RMLqk_X3nN1lOJ^r1S923X zM<+u|m|mCsATTy>?pV@Sm< zpUK|&mmNgh_HR@#eDf?YBe>OFbCFY$v!m0R7fq(o-~LF}iDXa9abR+3b(!ka{A-y> z>8$p*lIK)nAG}D*Isf(NyX>C-KVo+M%9~<4C1mx->l1Rfim2vlzB}P>-L)wsFlutS z+ou`bhRWf6sXptnYBbGHJ?-ZHsWpA0@U=td=1NPO-n?pdz2;Dd%=osTA)(y@(e=+>G-FebM%`T{PX0+0&D>q_}SI_2;J8?z+;495M zJ>>~bU7iOR3^wI1{-pm=#?jt1$gIu);4Z#K4ETAyZY9v*o5^J6^oI;k5iK)#|~*-2geUzbeXrYW<4@_+n@Q$PP%$w-kS8bKp)0ZM%%DC zM$@aj{?42>LtxRg9AQ>|O|>Uli@yAMv0UCd)bgy2TcGpKXX|1!qi z@3Gjp>idkRYq+1-E%uP#(sw}XYx1?#3EmNhb{kkK=JrU2n@;)8exPnrmC;R&_CD<=vvPV1RBrq`Tl3_2Ea)6ym;&rSGDN?k{cq05sd=hBmG>=|aM d>wl>qV7RpVH&?~x9SxwO*VEO{Wt~$(69C?eKobA} literal 0 HcmV?d00001 diff --git a/assets/effects/projectiles/enemyBalloonBullet.png b/assets/effects/projectiles/enemyBalloonBullet.png new file mode 100644 index 0000000000000000000000000000000000000000..05353370db542a69e8fb6679fe12808023989124 GIT binary patch literal 1240 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m=!WZB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxKsVXI%s|1+P|wiV z#N6CmN5ROz&_Lh7NZ-&%*U;R`*vQJjKmiJrfVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8EkR}&8R-I5=oVMzl_XZ^<`pZ$OmImpPAEg{v+u2}(t{7puX=A(aKG z`a!A1`K3k4z=%sz23b{L$o& z6x?nx!l_pu=oo!av?4__ObD2EKuma|1#;lYJ~a=R){B6N+LmY9Nd^W+KTj9OkcwML zfByfsXI5?KY;*ypvUkYcl0IaE|R3&!sbq z)}1+U;vnBKovuwhGJ0h^XOfh794hPQG;(}0K5^4vcHomEt`1@ids2I?#T*!q9cr@O zAt@;-5gBRtB#@V(%}nEfL!BG@|NHeRKmPwV*Kp{6WWpd4y5IsE`;E!Z6L|kS8h-et zdwBilaEXSC99_M=7MB?{<~0T`W#W1(U(MI}m#4_=hQfkp``6zyVR}0Kkq^7V2Q{V1 zf)Z0Ng3ORmk(FV+$#Y4^V&hNyIOevvG$sxMh8O1j_DdbS=7P!^Pgg&ebxsLQ0F555 Az5oCK literal 0 HcmV?d00001 diff --git a/assets/effects/projectiles/railgunTrail.png b/assets/effects/projectiles/railgunTrail.png new file mode 100644 index 0000000000000000000000000000000000000000..fe75a3b875a200293ccbf5c2657e2d973d736be7 GIT binary patch literal 3762 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 VGo9buGv4`TM5DmmpA@qe~pup|Hg literal 0 HcmV?d00001 diff --git a/assets/effects/smoke/smoke01.png b/assets/effects/smoke/smoke01.png new file mode 100644 index 0000000000000000000000000000000000000000..27f7771e73ff328fa8564de06be118e40506bb50 GIT binary patch literal 1231 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 FO#t{W71#g( literal 0 HcmV?d00001 diff --git a/assets/effects/smoke/smoke04.png b/assets/effects/smoke/smoke04.png new file mode 100644 index 0000000000000000000000000000000000000000..10ac4e4767197d96fdea2d5f290db1f99269df21 GIT binary patch literal 1408 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m=!WZB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxKsVXI%s|1+P|wiV z#N6CmN5ROz&_Lh7NZ-&%*U;R`*vQJjKmiJrfVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8EkR}&8R-I5=oVMzl_XZ^<`pZ$OmImpPAEg{v+u2}(t{7puX=A(aKG z`a!A1`K3k4z=%sz23b{LATTyKc8R-}?ylxim9=^5b*lEzwC^&z=JY(7dcLS>vVcSS$*2>P z3@pA?9H@9dTYL7|xj(L!%Iz_8m@MVUdF8v!?8}UKuB*j7W^?7$*@ZtpzV%F|{u74} zdv@PnAE_d9$He#F77nMnhi1u7+7dJB;?{rt@wiNz$x(jY`|9A3qqkbOe*UQVviZ{1 zGm$Q54+b(X;J9QezEME>hIZ59=hNEvhM5*!e)eWov(Sn;OU}Q2X_Ms;e`BUjQ*(>! zb*|QsjS6oCm>m6{9?1{B%9USTtvM^;cTN7rWlvNa5>9WL5wq&x-N&s>*->ZARXl$j z-B4Um;{A=mJ-91R*H%nfgVB1@ij^hCk1h7;^R(_PV?A!N^mfvxS<3`>nz5|gb}m@@ z$%E&QAKu>f_pw18YkA1~sjE`_4n?;v+gWG6Q#CZThu10ejOoI)o{|P?Uw=v*ePD5J zcaPi@hj(lO+mywo%J_DaZkU~LwCrU3f+aFfk`4LJ=}o%99lWOWQspF%1s9piPBQJd zx;t1!LVVSp(>pGCovoF86vO>oNH>>BM{vo;J5B!?M3}gruJ&d;Zta_qC~@5-`m3wX z6=uga$Np)*Oe}hiS7l_(E3@0BdvnviYP?e%}$ dZ?0!xW(YVSAF?Q%?JTHF^mO%eS?83{1OQdI3ex}p literal 0 HcmV?d00001 diff --git a/assets/effects/smoke/smoke05.png b/assets/effects/smoke/smoke05.png new file mode 100644 index 0000000000000000000000000000000000000000..4632cc24c2ca2e1b77b133f8cd520482be211b13 GIT binary patch literal 1240 zcmeAS@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m=!WZB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxKsVXI%s|1+P|wiV z#N6CmN5ROz&_Lh7NZ-&%*U;R`*vQJjKmiJrfVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8EkR}&8R-I5=oVMzl_XZ^<`pZ$OmImpPAEg{v+u2}(t{7puX=A(aKG z`a!A1`K3k4z=%sz23b{L$o& z6x?oc#;I2y=oo!av?4__ObD2EKuma|1#;lYJ~a=R){B6Nnt8UDG6MsnpQnpsNX4x; z5eIpj4R};K8h@qm%#&^9O_*XQ*_>|fm8zmVneCBEZV|uIPe+AEc~_~aSu|^YkORY%QwJ0(`8Ii|Jm6jMo~b?yJE^5>Dr1Uub#6ISS|8Gm^=VV;r2{dE&(Kl~c&*P2=1 zpTXcgTQu}&xahP=lhdEgC_g)Q|B@T5hfDTMbFG!JYd?7E?!xjDUlq;eStL#g>K?fA z^`g<_%P~KTbar>eb_L1!_eBSt^f|kZ|6X1#iv)wvlZtaCPqyCyl{KEOelF{r5}E)? CF|8Z` literal 0 HcmV?d00001 diff --git a/assets/fonts/04B_03.TTF b/assets/fonts/04B_03.TTF new file mode 100644 index 0000000000000000000000000000000000000000..fe4328b6accff5ea1c2554ef0148814ff3203ca4 GIT binary patch literal 19492 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 AzyJUM literal 0 HcmV?d00001 diff --git a/assets/general/sky.png b/assets/general/sky.png new file mode 100644 index 0000000000000000000000000000000000000000..ecb68bb3e75c29867cfeea1535a61045f726d32e GIT binary patch literal 2099 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 literal 0 HcmV?d00001 diff --git a/assets/objects/enemies/tesla1.png b/assets/objects/enemies/tesla1.png new file mode 100644 index 0000000000000000000000000000000000000000..bb01e6f98099ebdf6dc8d588246d90ad27dc072f GIT binary patch literal 11191 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 literal 0 HcmV?d00001 diff --git a/assets/objects/enemies/tesla2.png b/assets/objects/enemies/tesla2.png new file mode 100644 index 0000000000000000000000000000000000000000..f01a390bc81dc31460807c1f42cb5ba1b62c2013 GIT binary patch literal 10756 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 U@>iq&`7aYBt143`WfuN_0QztFSpWb4 literal 0 HcmV?d00001 diff --git a/assets/objects/enemies/tesla3.png b/assets/objects/enemies/tesla3.png new file mode 100644 index 0000000000000000000000000000000000000000..c267db2d443984708735fa95975bd3465c3e666f GIT binary patch literal 9241 zcmaKSWl&t*wk_^X;}DvLKoeYoH16I&BWVJ`2^QQTxVt++8kgWfg9euX0TSFpaCd$A z&b{Z}_v4+ntM*<}V~jcHTBB-Jt=f?qPz8J(Y8)gaBzz@BS=is!`R~h!h4J^RNNC9Q zx1n;CgS%=uym$35akfB`GIubw04mv;SX#g=Ow7HUhAl*qkWhiv+HhC6nkvN1!H(19 z9}K6bo#S6N5|XHdr=y9Pt%WPl)WXu*UJP{H-UR|$n~Q$L;pXDz<>KMx;1PuI@j|${f&cx0{zh{) ze-DAl%Kvw)zaue_m8+{Ggp13=!-Laa}@*q&Gf%Yuygz$S$miNUZ%eb#^q_^$i>6S{ZC2% z2CAw3|Dkqv|ATgMg<1S>zW+~Q7i}*`3oe+2i-Vi9+24(O&-hO$M~IBGg^8gNv1eBTz=`HSmqKy}5&j%fHZSY7ixR7grN|GYchIG0R6m;XO<|1pA{ zg1p>u)k#|7`33wax#Y`suRH%W^>X8YR5lO1RuGHN#ZrOdh=}`3H?&H⪻*doH2ceWD7aw`i#H051W3xjkf6O?p^UFI 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 literal 0 HcmV?d00001 diff --git a/assets/openfl.svg b/assets/openfl.svg new file mode 100644 index 0000000..506f61d --- /dev/null +++ b/assets/openfl.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + diff --git a/assets/rw.png b/assets/rw.png new file mode 100644 index 0000000000000000000000000000000000000000..a18113fdb27fb5f1f3770495183da7389847eb33 GIT binary patch literal 912 zcmV;B18@9^P)1RCwC7R$WMwQ5fFlE;dm7CJjxS zZq9z}2WKhe&9)#IU06ytdE*aV)kSjz-2`ditr=bv6tVKgn@H^@B*6-T>~}^#mRO5Y z7yWEzxIu$@wi9RPJKyPiE7;5UI6FV@^FGh}zUTXlhR~FukY+Lz*mMBG9a|NvS+YUC zssjM;&N53PnU?J73)Fh5O7a50K6Yz&^LqSr6(bZnBgM{LDD~Oc`IJm=3pIvGZ$)wQWk>v*x_>1_+lT4b~K!{47s$$j)nT&o* zX#u*ME4iKQm}JyMG@&Vi`nry&aa`eqmXNPb$w|aE3#e#CM}cV@iY*F(dN%7nTov%% z%M`{RRG&j~gktA=7jY!~bvAi*(f&8FfAS~M7jVhdV82U45{u zk|;F(CLa>y{vc0@A>1numsNc*{`@nhbZc4Aja`+4&WcE9`BR?~y_0Z` zd}?$J#QOua!Uopb?S(jaN21Gv?wjyXYEF&BJa97mqXMf&_NxNR*S3u+=G5@VJtr3S zliHWSfGEljn&8O%?jbyHeA#m3b$cRh5le*dH_n8Be0;-wfax}1G@gvcGh$xeJf>s= zKlGx5_0BUO)=(UKwClqAlv3mmJIn355{=!u-nw=cVKqHIs{d7SVBOqQLpbv;v?QvR zF0pDrXxG3!lz^(5*J7-jN2R9SKAcYrS9|-cfJD zCHz!nWsS%bS#6~0B mzF literal 0 HcmV?d00001 diff --git a/assets/rw.svg b/assets/rw.svg new file mode 100644 index 0000000..572cd90 --- /dev/null +++ b/assets/rw.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + diff --git a/assets/rw2.png b/assets/rw2.png new file mode 100644 index 0000000000000000000000000000000000000000..57ec4ac8dca3e126783537c940f3d8a8b9e30120 GIT binary patch literal 1400 zcmV-;1&8{HP)##jHEP$MQy`cX$s8k(v>{V_BqqWw^$ zQPEMrh|Va8rXu5$X)`5Vblbz>oOAEod&Ovzo)B^$=dsS(Yp;DSxLu8BT<=rU><(Bl zC;dGF6sj!_r0drE`_fYI^R@srl+XBlNls)qZhc5>B+ff zhwVn;?3W)a=L}@%$pKX_$P8qp2@gy1rV<*uwhIVD5Nr84)=Ca&)6$%)1J4D*LK}+M z7VT<+t09uRj`&f@!2*i8G9 z-0|5$8;W^Ke0s|E@|k9U=rzy5uFBb>4;9z|g#GXcaNEEkz|QeR5Tz!(7C4Y2hy|?c zS{}Z=deuQpALnkElMT-g|TBGZfx1r*0}n-6o(EjuHosq;- z;vl&Wbq<0K1t7d=?;Cai2%_2jkBT83yU7Lh>}k7Wm}NBwukre_-eBCYN(af+c;XgM zj|d28R(^Nc0VY@|PV&~RncAMVy9i4XAA+Ts5H7Igz{ z$lOqy;wr0Cj`{w=fG;g|dA@gY(NF{~MxQw;9Z(zw3!-}yVlZK8(-R!R)LC?bq zUu5;PZo2Jyk=;B{7LEIGjjU#8VP_2iLV5#=FC|`=eYL`S*PAj13-g~GDmwM}BLyI8 z`Fd<;{#lHn+pnW5_eNHbW|7qmmQT~H!P}5c^Z|}Np9zM1|3cZ)TXQZJNTQ*q3H{Vi zQP<0y%8c8(mH=l@|4Qq4Ha(sgZR;62+&KtgS^Vk%l=`2#fretlqiOD6{@L}7M872s zfx}O`>SKv9Uuh~h4ceFvqIM(|c{0MTF%O{uF+2KJ84Xcq8QP;iJ--YWuFDF8Cv5J> zqf%7O*3H4{g;r}PPNDdEal70&O04r=-ZuRrp*ixf#FD6$4cnrL?&WQry|~U{ehuAE zwAX_kP+r*4NW|M_2w^%tzh!+^m~OrCUU$|qw?0j&*_zO`li|mDss>i@Tj-N|nY{n}J;7AylMNq0tgelQ zn@9V87UqYjjbx+%#KNTL?JxZ`d+*^(gGR>Be<#7Zs=S?3yT4I(fX}hKF$4rkj?HgD zs++6?NkW)bqysYp&1Tw4=)eSIY7HTkAev<#^qY*Qj0@;7c$GOCbo;UBDEiYVWgn7M z^pA8Nlg7rwweZ_yjRON^Yi`tHfuvh!9tf?hy-}+)K-MtT&Okf6Oj}tGb)VW za#RY1dda`7^7o*!Vl8riOpe|1lequ$|Lgc4(7SGb3orl-qbuUQTmnD<0000 + + + + + + + + + + + + + + + + + diff --git a/assets/tiles/middle.png b/assets/tiles/middle.png new file mode 100644 index 0000000000000000000000000000000000000000..f77cd132c6e0af59419a765f6cfb8423f3f45cb3 GIT binary patch literal 1436 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 Q`#+Buv=yrS)TQwM0i;-pxBvhE literal 0 HcmV?d00001 diff --git a/assets/tiles/slopeUp.png b/assets/tiles/slopeUp.png new file mode 100644 index 0000000000000000000000000000000000000000..172fe761275a247044e8913a688b887080f3dcd6 GIT binary patch literal 3746 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 GXa5Hl2696H literal 0 HcmV?d00001 diff --git a/assets/tiles/top.png b/assets/tiles/top.png new file mode 100644 index 0000000000000000000000000000000000000000..90f33b8aa7ee900ae6bde1b398ec925e9fb093dc GIT binary patch literal 2311 zcmaJ@X;c$w77ZYZY>KqlEJ~4}qL7WSB@iJS5}QC+6mS7TQb2&DDhs#b+2!xAk0?gq?gHj+2jNl8&$kCe(NPy2HBLnPN zI2KF;Bl#XlV$d(i%b%MR%_Z`Xu3G??1d>uf07^MPf*?jHAtjKJpL9vexoR7W1U^Be z(PZRbrUF@PfCh;{z#fA`b8!whzyXiJ6YLxv9c=(x93F?o*<$gwXuKndU`xW`fX@$7 z8BNR!C;8IdKgUw8$jC^k6eeM@@$vDPcmf6zM_}Gam zbWp+-^I<6;5&|kkP8bv`B_owi|4Kmse~}eRK9@;ZFl+(`#^NzJRZ5FM7VG~*1%fYV ziPRVT+wcDrmiQ;aAl4U@K(S)3^5DWzs!%YACI&fDNbC1(|d*Qpv~g`8*QUok*q7?df)Q_B1@6fv3=DbSeW+qc9u@ zREiT}kxPfTu>w#iUF7oq<}$v@RW*VDRz{|SVtyRRa~DGb;8VgR{@1-wzpD3{%lo<) z+E=+)r5UX1SpVyo7q^tXqndtcTIJ$P+k--7uZxvUo$<6tc~>-3m~@JNLjSG6cF2_R zO=>jskTrN93wNK&y1MMZwNoC|{+1FqTTM%p)ASC7`9x#Wnu-Y{(J7hyA9;C6EqD(z zF{)5q$j z)fvbZ5ioY^+1R{JeS`RIoBhxfd0Ky9bg+HuoFK^P0OwzmJ|mg+X%El!wfl_PchBm| z8jdRcEPt=}R^7h# zDMEj4<-SW=F?Absf1d9SxL^zap!aKBtxv-rOP==|4aeJjOun*R6FY{wDUPw!*r?UN zj=Q3BVMT9n_gwrtB7S7p-fKHt<#Vrh$=Ua4Y*fJ$xqc4Rq|hJRmf=()_CPRA#4ea)$PCLc^eccIrDbe}$s!u8XPQQFYO!!&@@Ti4$Im z5S~_^*%6T~HKT^akBL}=WCvfAmG})f+oDcx-`u$NV6S&3KWANvsK#o@`nczaj9U0% z{VyGs3uoF^4=hjgknJ4`b2a=ogA%5Q^IgTZ+B8vOo-vMg3)*`xw>6=d?tLcw>7g7$wmP$sxwVVZU!c=69s*=U6*6%zcV|-8s2d+Y zWw+I&5`W^`sPUDNHfTQ`%Lehgss&{7gZ})zByD4y&DnkHr`;?a<_*eD;@GjcTP^$V zB&$i2*9-Gr=?a=?Z*rO|4qg6UCzjS0vdwHTEbE}*jJT5$qG<19+dZf4$egr(&@q23 zSGS3dPk%2Uj4moC$Nv{qw0 zdx=E=25hov?4lYtVCuE+mCMEuclv~ZW zW4fQ9$lx%gsw-8H+b8rIFku@xz8P*uUOMJ9ST z;%1FiKHsi%c>5lmZR;lpxKL1}wykh^b4lwf{thRBCn6;D<+Co?_B zK9p@1-5tnF+k7{1`i$3(Lj&bWGmWOVFCUB9oNE&0ynMGXJ*aUzysBnsV(LX@)@tCp zmg{iEv78eD&o> zdsPoG(3XJ!k&RMA70sIFzQY Yi`W1+h+^24?y6rCCc}$fO%2`uC+H#3l>h($ literal 0 HcmV?d00001 diff --git a/assets/tiles/top2.png b/assets/tiles/top2.png new file mode 100644 index 0000000000000000000000000000000000000000..03d5969e77c9839cd3948f4189fd212b17331692 GIT binary patch literal 2249 zcmaJ@X;@R&7L6i<2#8aP3=)H7CKG{#kcf~Fl9m7h3R(+<l&MzyU<7pEy(= z#eo?IKt)6-U=anZ77?rplE+X~h5|CkBu|v7i52^PJl_4jd(S@STWjsT&t5<7aUU-a zU2Q{c1OlPUW-)w~tEKAI(olX+&fgX)myNJ{AnXT)!7{D{K)CXuPyodia>D^%fXk1K zt_P?Hgc=I;4}=3bo)jJ=#Bfy@j9e&IvJnWXi(Jg*2>=)s3WS3q8hZ3jH5vu-Y3Kk4 z4vr(H0}&uAP6GJFdHM6=1Ux4`+GQ(>DyJv~gaFJ%$%RoODMe00f6}EWW7RPhjrs(E z1vK>EMFnzvP;^KFpd2tbJ08vvhjPSY@b*L!iDZi+;P5ysj)27z?C>OtJ%NJ5p*}xo zWi|;vjN;2+e$J)b(a;evET&+wGMNk`v&TS^a4g=*$w{R_AlNApcG6f8%$3`Tq-Ki> z3_!}0fMOVgL@1RaHx!D2X=r7of43kMe~}eQKVOsb!mx6#7>mc?R4pw6Ih_9w6$-zg zrLZsXPrd(BSn3}u2C%+>6pE4Xl!FU1Q>7A9=n{YnLlS=oidwuyp9lzsq!Ex9MfY<+ z1%M(xB$IwZb2t>XND6aBJb=xhp_P0L2=XapJf1`%J28k3cxC90cO#IU==MxEI?mmd zgmYvpav2aWMhJ-DMK1pzuKSW)l@WwuWo8B-0rvuYrUVkAJ~d1Mm#)QaNxjcp{?fJ3 zm*irV#b8xq{jV`E9x1(}3cr|Ex%pyyK&14#L}}_;$NXLdLfwVUaP^nBR|P(SjNI49 z-+7sr&R=JK-*S~h%O?HZZpnMDvicJE+AOAc>_(T%z#%$@)rE}C^$_{9ShPNaEqZs8 zU+y2MJ+|ESo0e29AUPvaU3B-9quuBexuR$4U|V!-QzR{Tbgpmi;N&eu+7m@f%~3&) zfz()wIx5@0kTi!Hxa3*A``Kvz3R0goR?EBl1E<{?nux52BBHeR^s}RGg-Q?qko&eaqmM? ziQ&~hqG}8*6A(7~&&q#on8_5|HmubGB3HC~dTQJ@O+y@d@TT{-o8)uXoC!DHUJ)ni zH4hxFN4L%vXpm$x(Z5JZNu@1A9iOk+-fokB6q@V(Wb#%)-*$y7CE}w3%eJ#$_-UyB>4t1##ytp}V&5?D8HzMh&-Wi4 zF&$%mK<0RE<<*5%d7j~6#UkQGuR+?|Ty^1aGdFi$6Ft7Cyv*}G*{C4dt z_$H^a`X6#Jo0gyYuHnH7tD=l5!|MAlak9%*DBfpum`xIK%4S+0grX#ayV(1T`;%dOsj-qwn;(T@#0I#jS3L)JU0o zx&yvl5_l+WAO7uRpTK5ptStd*|Jg|2Al1$Qck9vj2h|daEJCYdxEoF{m~8uVhmrM- zqjyp+pC%%iYs0lN)q;(upBD@(jv(u;Qb|GaD~HRj9M>s~=&x~k_scyno4@)@xp-NW zV7qNzmwX~X#woTvY5yz)h+byfj2KbuorztO>4-rDhxF>D#1-z0)}1P;Yc_ybxsQ{g z1P+12@rm7pI6uwnzmIesjC*w=q^)sps=01ihI$dP2L0NaymGhh$GY*9Mw6UJCyA7a z@}IyGyOGX{bA*m0Pw(qnIE~F(t+uaymmxv)Q~rPQ-vkqx=S<#HLXVC11WEeORd;n= zsw=KCe3*8zLYS|a%(I#V4@}seu;W~UHtBaXrlk1c;8WSl-5a**z9!B!w0@XG2n23O z>S36;=Jqbjtn&Dc`hdUFGj$v5B$4#CI3fsy(+ktedCM!@bl4(u7^?tKxtYKwZSZY|>t_d}(6M3;g zp?(z`FZ5A!pIXnC1`*$tW^Hnx`Mzy5x#{|}`!oNr{R+myx)4Nmc*a-y$IVnfDhRf_ K7o*56B=Ik^-m_}} literal 0 HcmV?d00001 diff --git a/assets/tiles/tst1.png b/assets/tiles/tst1.png new file mode 100644 index 0000000000000000000000000000000000000000..8ec737b47a2f9480839e79ed89fefccf64f23b01 GIT binary patch literal 2184 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 literal 0 HcmV?d00001 diff --git a/assets/ui/gameOverText.png b/assets/ui/gameOverText.png new file mode 100644 index 0000000000000000000000000000000000000000..a1cc9c9863eed09bd1248f1a78a64ad057f68e5d GIT binary patch literal 4384 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 literal 0 HcmV?d00001 diff --git a/assets/ui/gameOverText.psd b/assets/ui/gameOverText.psd new file mode 100644 index 0000000000000000000000000000000000000000..41f0112c0bb84ac63b5c90d89940de7d883f7008 GIT binary patch literal 100878 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 R#+m!)y#CwVFZ}C+;x|HkAxZ!M literal 0 HcmV?d00001 diff --git a/openfl-readme.txt b/openfl-readme.txt new file mode 100644 index 0000000..035191d --- /dev/null +++ b/openfl-readme.txt @@ -0,0 +1,68 @@ +About OpenFL + + OpenFL is the successor of NME, for Haxe 3+. + + Building a game or application with OpenFL is almost like writing for a single platform. However, + when you are ready to publish your application, you can choose between targets like iOS, webOS, + Android, Windows, Mac, Linux and Flash Player. + + Instead of using the lowest common denominator between platforms with a "universal" runtime, + OpenFL projects are compiled as SWF bytecode or C++ applications, using the Haxe language compiler + and the standard C++ compiler toolchain for each platform. + + Read more: + http://openfl.org/ + +Project configuration, libraries, classpaths + + OpenFL configuration is based on a XML file - it allows you very complex + configurations depending on the target platform. There is no GUI for it. + + DO NOT modify FlashDevelop project properties as they will automatically be synchronized with the + XML when you modify it. + +Development + + OpenFL is encouraging to develop using the Flash API (ie. flash.display.Sprite) as in ActionScript 3. + + Just code like you would code a Flash application, with the limitation that you can only use + the drawing API, bitmaps (see below) and TextFields. + + However test often all the platforms you plan to target! + + In OpenFL 3.x, SWFs and videos aren't supported yet. + +Assets + + Place all your images, sounds, fonts in /assets and access them in your code using the + global Assets class which abstracts assets management for all platforms: + + var img = new Bitmap(Assets.getBitmapData("assets/my-image.png")); + addChild(img); + + Tutorials: + https://github.com/openfl/openfl/wiki/Get-Started + +Debugging + + By default your project targets Flash so you'll be able to add breakpoints and debug your app + like any AS3 project. + HTML5 target can be debugged in the browser - some browsers, like Chrome, support "script maps" + which let you interactively debug .hx code directly instead of the generated JS. + There is however no interactive debugger yet for native targets. + +Changing target platform + + For OpenFL projects, an additional drop-down menu appears in the main toolbar where you can choose + a supported targets on Windows: flash, html5, windows, neko, android, webos, blackberry. + You can also manually enter a custom target not in the list. + + Attention, for native targets you'll need to install additional compilers & SDKs. The compiler + will tell you in the Output panel what command to execute for that. More information here: + https://github.com/openfl/openfl/wiki/Get-Started + +Tips: + - in C++ expect first compilation to be very long as it first compiles the whole OpenFL API, + - if a change is not taken in account, delete everything in /bin to start a fresh compilation, + - on mobile, Bitmap blitting is NOT performant, + - use spritesheets and Tilesheet.drawTiles for optimal rendering performance. diff --git a/src/FPS.hx b/src/FPS.hx new file mode 100644 index 0000000..e2d899b --- /dev/null +++ b/src/FPS.hx @@ -0,0 +1,38 @@ +import flash.events.Event; +import flash.Lib; +import flash.text.TextField; +import flash.text.TextFormat; + +class FPS extends TextField +{ + var times:Array; + + public function new(inX:Float=10.0, inY:Float=10.0, inCol:Int = 0x000000) + { + super(); + x = inX; + y = inY; + selectable = false; + embedFonts = true; + defaultTextFormat = new TextFormat(FontManager.mainFont.fontName, 16, 0, true); + text = "FPS:"; + textColor = inCol; + times = []; + addEventListener(Event.ENTER_FRAME, enterFrame); + } + + private function enterFrame(e:Event):Void + { + var now = Lib.getTimer () / 1000; + times.push(now); + while(times[0]; + static private var socketConnected:Bool; + + static public function connectTracer():Void + { + messageQueue = []; + + xmlSocket = new XMLSocket(); + xmlSocket.addEventListener(IOErrorEvent.IO_ERROR, onIOError); + xmlSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSecurityError); + xmlSocket.addEventListener(Event.CONNECT, onConnect); + try + { + xmlSocket.connect('localhost', 4442); + } + catch (e:Dynamic) + { + + } + } + + static public function disconnectTracer():Void + { + if (xmlSocket != null && xmlSocket.connected) + { + xmlSocket.close(); + } + } + + static private function onConnect(e:Event):Void + { + socketConnected = true; + add("Connected to socket"); + while (messageQueue.length > 0) { + xmlSocket.send(messageQueue.shift()); + } + } + + static private function onSecurityError(e:SecurityErrorEvent):Void + { + + } + + static private function onIOError(e:IOErrorEvent):Void + { + + } + + static public function add(string:String, type:String = "SYSTEM"):Void + { + var splitMessage:Array = string.split("\n"); + + if (xmlSocket == null) { + connectTracer(); + } + + var message:String = '!SOS'; + if (socketConnected) + { + try + { + xmlSocket.send(message); + } + catch (e:Dynamic) + { + + } + } + else + { + messageQueue.push(message); + } + + while (messageQueue.length > 1000) + { + messageQueue.shift(); + } + } + + static public function objectToString(target:Dynamic, ?depth:Int = 0):String + { + var tab:String = ' '; + for (i in 0...depth) + { + tab += ' '; + } + + var out : String = ''; + + /* + var item:Dynamic; + var dat:Dynamic; + var objectToDescribe:String; + var enter:String = '\n'; + for (item in target) + { + dat = target[item]; + if (typeof(dat) == 'object') + { + out += tab+'['+item+']' + enter; + objectToDescribe = objectToString(dat, depth+1); + out += (objectToDescribe != '' ? objectToDescribe : tab + '...') + enter; + out += tab+'[/'+item+']' + enter; + } + } + + var step:Int = 0; + for (item in target) { + dat = target[item]; + if (typeof(dat) != 'object') { + if (step) { out += enter; } + out += tab+''+item+' = '+dat; + step++; + } + } + */ + return out; + } + + static public function sosTrace(data:String, ?logLevel:String = TRACE):Void + { + + /* + for (var i:int = 0; i < arguments.length; i++) + { + argument = arguments[i]; + if (argument == null ) + { + string += null; + } + else if (typeof(argument) == 'object') + { + string += '\n'; + string += "[" + getQualifiedClassName(argument) + "]"; + string += '\n' + SOSLog.objectToString(argument) + '\n'; + string += "[/" + getQualifiedClassName(argument) + "]"; + } else + { + string += argument; + if (i < arguments.length - 1) { + string += ', '; + } + } + }*/ + SOSLog.add(data, logLevel); + } + #else + static public function sosTrace(data:String, ?logLevel:String = TRACE):Void + { + trace(data); + } + #end +} \ No newline at end of file diff --git a/src/ScreenPad.hx b/src/ScreenPad.hx new file mode 100644 index 0000000..b6a5829 --- /dev/null +++ b/src/ScreenPad.hx @@ -0,0 +1,88 @@ +package ; +import flash.display.Bitmap; +import flash.display.BitmapData; +import flash.display.Sprite; +import flash.events.Event; +import flash.events.MouseEvent; +import openfl.Assets; + +/** + * ... + * @author Dmitriy Barabanschikov + */ + +class ScreenPad extends Sprite +{ + static public inline var WIDTH:Int = 128; + static public inline var HEIGHT:Int = 128; + static public inline var SIDE_PAD:Int = 16; + + public var xMagnitude:Float; + public var yMagnitude:Float; + private var headBitmap:Bitmap; + private var mouseDown:Bool; + private var headContainer:Sprite; + + public function new() + { + super(); + var backgroundBitmapData:BitmapData = new BitmapData(WIDTH,HEIGHT, true, 0); + var backgroundSource:Sprite = new Sprite(); + backgroundSource.graphics.lineStyle(2, 0, 0.6, true); + backgroundSource.graphics.beginFill(0, 0.2); + backgroundSource.graphics.drawRoundRect(1, 1, WIDTH-2, HEIGHT-2, WIDTH/4, HEIGHT/4); + backgroundSource.graphics.endFill(); + backgroundBitmapData.draw(backgroundSource); + addChild(new Bitmap(backgroundBitmapData)); + + headContainer = new Sprite(); + headContainer.x = WIDTH / 2; + headContainer.y = HEIGHT / 2; + addChild(headContainer); + + headBitmap = new Bitmap(Assets.getBitmapData("assets/ui/joystickHead.png")); + headBitmap.x = -headBitmap.width / 2; + headBitmap.y = -headBitmap.height/ 2; + headContainer.addChild(headBitmap); + + addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler); + addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler); + //TODO cleanup routine on removed from stage + } + + private function addedToStageHandler(e:Event):Void + { + removeEventListener(Event.ADDED_TO_STAGE, addedToStageHandler); + stage.addEventListener(MouseEvent.MOUSE_UP, stage_mouseUpHandler); + } + + private function mouseDownHandler(e:MouseEvent):Void + { + mouseDown = true; + } + + private function stage_mouseUpHandler(e:MouseEvent):Void + { + mouseDown = false; + } + + public function update(dTime:Float):Void + { + if (mouseDown) + { + headContainer.x = Math.min(Math.max(mouseX, SIDE_PAD), WIDTH - SIDE_PAD); + headContainer.y = Math.min(Math.max(mouseY, SIDE_PAD), HEIGHT - SIDE_PAD); + xMagnitude = (mouseX - WIDTH / 2) / ((WIDTH - SIDE_PAD * 2 - 2) / 2); + yMagnitude = (mouseY - HEIGHT/ 2) / ((HEIGHT- SIDE_PAD * 2 - 2) / 2); + } + else + { + headContainer.x += (WIDTH / 2 - headContainer.x) * dTime * 10; + headContainer.y += (HEIGHT / 2 - headContainer.y) * dTime * 10; + xMagnitude = 0; + yMagnitude = 0; + } + + } + +} \ No newline at end of file diff --git a/src/assets/EntityGraphicsAsset.hx b/src/assets/EntityGraphicsAsset.hx new file mode 100644 index 0000000..310f5cc --- /dev/null +++ b/src/assets/EntityGraphicsAsset.hx @@ -0,0 +1,93 @@ +package assets; +import flash.display.BitmapData; +import flash.geom.Point; +import flash.geom.Rectangle; +import haxe.ds.IntMap.IntMap; +import haxe.ds.StringMap.StringMap; +import haxe.ds.StringMap.StringMap; +import openfl.Assets; + +/** + * ... + * @author Dmitriy Barabanschikov + */ +class EntityGraphicsAsset +{ + public var assetID:String; + public var spriteSheet:BitmapData; + public var frameRectangles:Array; + public var tileSheetRectangles:Array; + public var tileSheetFrameID:Array; + public var sizeCorrection:Point; + public var assetFrameByName:StringMap; + + public function new(assetID:String) + { + this.assetID = assetID; + frameRectangles = new Array(); + tileSheetFrameID = new Array(); + tileSheetRectangles = new Array(); + assetFrameByName = new StringMap(); + } + + public function initializeFromAssetNames(namesArray:Array):EntityGraphicsAsset + { + var sourceAssets:Array = []; + var numAssets:Int = namesArray.length; + var maxWidth:Int = 0; + var maxHeight:Int = 0; + for (i in 0...numAssets) + { + //trace(namesArray[i]); + var asset:BitmapData = Assets.getBitmapData(namesArray[i]); + maxWidth = Std.int(Math.max(maxWidth, asset.width)); + maxHeight = Std.int(Math.max(maxHeight, asset.height)); + sourceAssets.push(asset); + assetFrameByName.set(namesArray[i], i); + } + + //TODO only for non-flash targets + #if (!flash) + maxWidth = Math.ceil(Std.int(Math.max(maxWidth, maxHeight))/2)*2; + maxHeight = maxWidth; + #end + + sizeCorrection = new Point(-maxWidth / 2, -maxHeight/2); + + frameRectangles = new Array(); + + #if flash + spriteSheet = new BitmapData(numAssets * maxWidth, numAssets*maxHeight, true, 0x0); + #else + spriteSheet = new BitmapData(numAssets * maxWidth, numAssets*maxHeight, true, 0x0); + #end + + for (i in 0...numAssets) + { + var destinationRectangle:Rectangle = new Rectangle(i * maxWidth, 0, maxWidth, maxHeight); + //trace(destinationRectangle); + frameRectangles.push(destinationRectangle); + var sourceWidth:Int = sourceAssets[i].width; + var sourceHeight:Int = sourceAssets[i].height; + spriteSheet.copyPixels(sourceAssets[i], new Rectangle(0, 0, sourceWidth, sourceHeight), new Point(destinationRectangle.x + Std.int((maxWidth - sourceWidth) / 2), destinationRectangle.y + Std.int((maxHeight - sourceHeight) / 2))); + } + + return this; + } + + public function getFrameByAssetName(assetName:String):Int + { + var assetFrame:Null = assetFrameByName.get(assetName); + if (assetFrame != null) + { + return assetFrame; + } + return 0; + } + + public function clear() + { + spriteSheet.dispose(); + } + +} \ No newline at end of file diff --git a/src/assets/GraphicsLibrary.hx b/src/assets/GraphicsLibrary.hx new file mode 100644 index 0000000..829f654 --- /dev/null +++ b/src/assets/GraphicsLibrary.hx @@ -0,0 +1,183 @@ +package assets; + +import flash.display.Bitmap; +import flash.display.BitmapData; +import openfl.display.Tilesheet; +import flash.geom.Matrix; +import flash.geom.Point; +import flash.geom.Rectangle; +import openfl.Assets; +/** + * ... + * @author Dmitriy Barabanschikov + */ +class GraphicsLibrary +{ + private static var explosionAsset:EntityGraphicsAsset; + + private static var playerAsset:EntityGraphicsAsset; + + private static var tilesAsset:EntityGraphicsAsset; + + private static var bulletAsset:EntityGraphicsAsset; + + private static var enemyBulletAsset:EntityGraphicsAsset; + + private static var teslaFlyerAsset:EntityGraphicsAsset; + + static private var bulletHitAsset:EntityGraphicsAsset; + + static private var debriesAsset:EntityGraphicsAsset; + + static private var baloonAsset:EntityGraphicsAsset; + + static private var railgunTrailAsset:EntityGraphicsAsset; + + static private var smokeAsset:EntityGraphicsAsset; + + static public var tilesheet:Tilesheet; + + public function GraphicsLibrary() + { + + } + + public static function initialize():Void + { + explosionAsset = new EntityGraphicsAsset("explosion").initializeFromAssetNames(["assets/effects/explosion1/1.png", "assets/effects/explosion1/2.png", "assets/effects/explosion1/3.png", "assets/effects/explosion1/4.png", "assets/effects/explosion1/5.png"]); + playerAsset = new EntityGraphicsAsset("player").initializeFromAssetNames(["assets/objects/player/plane01.png"]); + tilesAsset = new EntityGraphicsAsset("tile").initializeFromAssetNames(["assets/tiles/top.png", "assets/tiles/middle.png", "assets/tiles/slopeUp.png", "assets/tiles/slopeDown.png", "assets/tiles/middle2.png", "assets/tiles/top2.png"]); + + bulletAsset = new EntityGraphicsAsset("bullet").initializeFromAssetNames(["assets/effects/projectiles/beam02.png"]); + enemyBulletAsset = new EntityGraphicsAsset("enemyBullet").initializeFromAssetNames(["assets/effects/projectiles/beam01.png","assets/effects/projectiles/enemyBalloonBullet.png"]); + bulletHitAsset = new EntityGraphicsAsset("bulletHit").initializeFromAssetNames(["assets/effects/bulletHit/bulletHit.png"]); + teslaFlyerAsset = new EntityGraphicsAsset("teslaFlyer").initializeFromAssetNames(["assets/objects/enemies/tesla1.png", "assets/objects/enemies/tesla2.png", "assets/objects/enemies/tesla3.png"]); + baloonAsset = new EntityGraphicsAsset("baloon").initializeFromAssetNames(["assets/objects/enemies/foe02.png"]); + smokeAsset = new EntityGraphicsAsset("smoke").initializeFromAssetNames(["assets/effects/smoke/smoke01.png", "assets/effects/smoke/smoke02.png", "assets/effects/smoke/smoke03.png", "assets/effects/smoke/smoke04.png", "assets/effects/smoke/smoke05.png"]); + debriesAsset = new EntityGraphicsAsset("debries").initializeFromAssetNames(["assets/effects/debries/debries1.png", "assets/effects/debries/debries2.png", "assets/effects/debries/debries3.png"]); + + #if (!android && !iphone) + railgunTrailAsset = new EntityGraphicsAsset("railgunTrail").initializeFromAssetNames(["assets/effects/projectiles/railgunTrail.png"]); + #end + + #if (android || iphone) + createTilesheet([explosionAsset, playerAsset, tilesAsset, bulletAsset, enemyBulletAsset, bulletHitAsset, teslaFlyerAsset, baloonAsset, smokeAsset, debriesAsset]); + #elseif cpp + createTilesheet([explosionAsset, playerAsset, tilesAsset, debriesAsset, bulletAsset, enemyBulletAsset, bulletHitAsset, teslaFlyerAsset, baloonAsset, railgunTrailAsset, smokeAsset]); + #end + } + + static private function createTilesheet(assetArray:Array):Void + { + var maxHeight:Float = 0; + var totalWidth:Float = 0; + var assetFrameID:Int = 0; + for (i in 0...assetArray.length) + { + var asset:EntityGraphicsAsset = assetArray[i]; + + for (j in 0...asset.frameRectangles.length) + { + asset.tileSheetFrameID.push(assetFrameID); + var tilesheetRect:Rectangle = asset.frameRectangles[j].clone(); + tilesheetRect.x += totalWidth; + asset.tileSheetRectangles.push(tilesheetRect); + assetFrameID++; + } + maxHeight = Math.max(maxHeight, asset.spriteSheet.height); + totalWidth += asset.spriteSheet.width; + } + + var bitmapData:BitmapData = new BitmapData(Std.int(totalWidth), Std.int(maxHeight), true, 0); + + var currentX:Int = 0; + for (i in 0...assetArray.length) + { + var asset:EntityGraphicsAsset = assetArray[i]; + bitmapData.copyPixels(asset.spriteSheet, new Rectangle(0, 0, asset.spriteSheet.width, asset.spriteSheet.height), new Point(currentX, 0)); + currentX += asset.spriteSheet.width; + asset.clear(); + } + + tilesheet = new Tilesheet(bitmapData); + for (i in 0...assetArray.length) + { + var asset:EntityGraphicsAsset = assetArray[i]; + for (j in 0...asset.tileSheetRectangles.length) + { + tilesheet.addTileRect(asset.tileSheetRectangles[j]); + } + } + } + + public static function getExplosion1():EntityGraphicsAsset + { + return explosionAsset; + } + + public static function getPlayerShip():EntityGraphicsAsset + { + return playerAsset; + + /*if (playerAsset != null) + { + return playerAsset; + } + + var bd:BitmapData = new BitmapData(120, 50, true, 0x00000000); + var playerBitmap:BitmapData = Assets.getBitmapData("assets/objects/player/plane01.png"); + bd.copyPixels(playerBitmap, new Rectangle(0, 0, 120, 50), new Point(0, 0)); + + playerAsset = new EntityGraphicsAsset(); + playerAsset.spriteSheet = bd; + playerAsset.frameRectangles = [new Rectangle(0, 0, 120, 50)]; + playerAsset.sizeCorrection = new Point( -60, -25); + return playerAsset;*/ + } + + public static function getTiles():EntityGraphicsAsset + { + return tilesAsset; + } + + public static function getDebries():EntityGraphicsAsset + { + return debriesAsset; + } + + public static function getBullet():EntityGraphicsAsset + { + return bulletAsset; + } + + public static function getEnemyBullet():EntityGraphicsAsset + { + return enemyBulletAsset; + } + + public static function getBulletHit():EntityGraphicsAsset + { + return bulletHitAsset; + } + + public static function getTeslaFlyer():EntityGraphicsAsset + { + return teslaFlyerAsset; + } + + public static function getBaloon():EntityGraphicsAsset + { + return baloonAsset; + } + + public static function getRailgunTrail():EntityGraphicsAsset + { + return railgunTrailAsset; + } + + static public function getSmoke() + { + return smokeAsset; + } + +} \ No newline at end of file diff --git a/src/com/sphaeraobscura/keyboard/KeyKeeper.hx b/src/com/sphaeraobscura/keyboard/KeyKeeper.hx new file mode 100644 index 0000000..6bd4662 --- /dev/null +++ b/src/com/sphaeraobscura/keyboard/KeyKeeper.hx @@ -0,0 +1,65 @@ +package com.sphaeraobscura.keyboard; +import flash.display.Stage; +import flash.events.Event; +import flash.events.EventDispatcher; +import flash.events.KeyboardEvent; + +/** + * ... + * @author Dimonte + */ +class KeyKeeper extends EventDispatcher +{ + private var _stage:Stage; + private var _keysPressed:Array; + + public function new(stage:Stage) + { + super(); + _stage = stage; + initKeeper(); + } + + private function initKeeper():Void + { + _keysPressed = new Array(); + _stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler, false); + _stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler, false); + _stage.addEventListener(Event.DEACTIVATE, deactivateHandler, false); + } + + private function keyDownHandler(e:KeyboardEvent):Void + { + if (!_keysPressed[e.keyCode]) + { + dispatchEvent(new KeyKeeperEvent(KeyKeeperEvent.KEY_DOWN, e.keyCode)); + } + _keysPressed[e.keyCode] = true; + } + + private function keyUpHandler(e:KeyboardEvent):Void + { + if (_keysPressed[e.keyCode]) + { + dispatchEvent(new KeyKeeperEvent(KeyKeeperEvent.KEY_UP, e.keyCode)); + } + _keysPressed[e.keyCode] = false; + } + + private function deactivateHandler(e:Event):Void + { + for (i in 0..._keysPressed.length) + { + if (_keysPressed[i]) + { + dispatchEvent(new KeyKeeperEvent(KeyKeeperEvent.KEY_UP, i)); + } + _keysPressed[i] = false; + } + } + + public function isDown(keyCode:Int):Bool + { + return _keysPressed[keyCode]; + } +} \ No newline at end of file diff --git a/src/com/sphaeraobscura/keyboard/KeyKeeperEvent.hx b/src/com/sphaeraobscura/keyboard/KeyKeeperEvent.hx new file mode 100644 index 0000000..bdce3e8 --- /dev/null +++ b/src/com/sphaeraobscura/keyboard/KeyKeeperEvent.hx @@ -0,0 +1,28 @@ +package com.sphaeraobscura.keyboard; +import flash.events.Event; + +/** + * ... + * @author Dmitriy Barabanschikov + */ + +class KeyKeeperEvent extends Event +{ + + static public inline var KEY_DOWN:String = "keyDown"; + static public inline var KEY_UP:String = "keyUp"; + + public var keyCode:Int; + + public function new(type:String, keyCode:Int, bubbles:Bool = false, cancelable:Bool = false) + { + this.keyCode = keyCode; + super(type, bubbles, cancelable); + } + + override public function clone():Event + { + return new KeyKeeperEvent(type, keyCode, bubbles, cancelable); + } + +} \ No newline at end of file diff --git a/src/dataStructures/DLL.hx b/src/dataStructures/DLL.hx new file mode 100644 index 0000000..132ee55 --- /dev/null +++ b/src/dataStructures/DLL.hx @@ -0,0 +1,74 @@ +package dataStructures; + + +/** + * ... + * @author Dimonte + */ +class DLL +{ + public var _head:DLLNode; + public var _tail:DLLNode; + + public function new() + { + } + + public function head():DLLNode + { + return _head; + } + + public function append(object:Dynamic):DLLNode + { + var dllNode:DLLNode = new DLLNode(); + dllNode.val = object; + if (_head == null) + { + _head = dllNode; + _tail = dllNode; + } else { + _tail.next = dllNode; + dllNode.previous = _tail; + _tail = dllNode; + } + return dllNode; + } + + public function remove(node:DLLNode):Void + { + if (node == _head) { + _head = _head.next; + } + if (node == _tail) { + _tail = _tail.previous; + } + if (node.next != null) + { + node.next.previous = node.previous; + } + if (node.previous != null) + { + node.previous.next = node.next; + } + } + + public function clear():Void + { + _tail = null; + _head = null; + } + + public function getLength():Int + { + var length:Int = 0; + var node:DLLNode = _head; + while (node != null) + { + length++; + node = node.next; + } + return length; + } + +} \ No newline at end of file diff --git a/src/dataStructures/DLLNode.hx b/src/dataStructures/DLLNode.hx new file mode 100644 index 0000000..74626cc --- /dev/null +++ b/src/dataStructures/DLLNode.hx @@ -0,0 +1,31 @@ +package dataStructures; + +/** + * ... + * @author Dimonte + */ +class DLLNode +{ + + public var next:DLLNode; + public var previous:DLLNode; + public var val:Dynamic; + + public function new() + { + + } + + public function remove():Void + { + if (next != null) + { + next.previous = previous; + } + if (previous != null) + { + previous.next = next; + } + } + +} \ No newline at end of file diff --git a/src/game/Bullet.hx b/src/game/Bullet.hx new file mode 100644 index 0000000..0743ac5 --- /dev/null +++ b/src/game/Bullet.hx @@ -0,0 +1,80 @@ +package game; + +import assets.GraphicsLibrary; +import sphaeraGravita.collision.PhysicalBody; +import sphaeraGravita.shapes.BaseShape; +import game.GameEntity; +import sphaeraGravita.shapes.CircleShape; +import sphaeraGravita.shapes.Polygon; +/** + * ... + * @author Dmitriy Barabanschikov + */ +class Bullet extends GameEntity +{ + private var timePassedSinceLastUpdate:Float; + + public function new(world:GameWorld) + { + super(world); + + type = EntityType.PLAYER_BULLET; + + graphicsAsset = GraphicsLibrary.getBullet(); + + collisionCategory = CollisionCategory.PLAYER_BULLET; + collisionMask = CollisionCategory.TILE | CollisionCategory.ENEMY; + unguidedProjectile = true; + gridBound = true; + /* + var s:Polygon = new Polygon(); + s.setAsBox(1, 1); + /*/ + var s:CircleShape = new CircleShape(0,0,6); + //*/ + s.sensor = true; + addShape(s); + init(); + } + + override public function reset():Void + { + super.reset(); + } + + override public function reactToCollision(collidingBody:PhysicalBody, selfShape:BaseShape, otherShape:BaseShape):Void + { + cast(collidingBody, GameEntity).recieveDamage(12); + + var bh:GameEntity = world.getReusableEntityByType(EntityType.BULLET_HIT); + bh.reset(); + bh.x = x; + bh.y = y; + world.addParticle(bh); + + dead = true; + } + + override public function update(dTime:Float):Void + { + super.update(dTime); + timePassedSinceLastUpdate += dTime; + while (timePassedSinceLastUpdate > 0.035) + { + timePassedSinceLastUpdate-= 0.035; + speed.y *= 0.98; + } + + #if (!android && !iphone) + if (Math.random() < 6*dTime) + { + var trail:GameEntity = world.getReusableEntityByType(EntityType.RAILGUN_TRAIL); + trail.reset(); + trail.position = position.clone(); + trail.speed = speed.clone(); + world.addParticle(trail); + } + #end + } + +} \ No newline at end of file diff --git a/src/game/BulletHit.hx b/src/game/BulletHit.hx new file mode 100644 index 0000000..8b2c2ac --- /dev/null +++ b/src/game/BulletHit.hx @@ -0,0 +1,40 @@ +package game; +import assets.GraphicsLibrary; + +//import assets.GraphicsLibrary; +/** + * ... + * @author Dmitriy Barabanschikov + */ +class BulletHit extends GameEntity +{ + private var counter:Float; + + public function new(world:GameWorld) + { + super(world); + counter = 0; + type = EntityType.BULLET_HIT; + graphicsAsset = GraphicsLibrary.getBulletHit(); + gridBound = true; + unguidedProjectile = true; + init(); + } + + override public function reset():Void + { + super.reset(); + counter = 0; + } + + override public function update(dTime:Float):Void + { + super.update(dTime); + counter+=dTime; + if (counter > 0.2) + { + dead = true; + } + } + +} \ No newline at end of file diff --git a/src/game/CollisionCategory.hx b/src/game/CollisionCategory.hx new file mode 100644 index 0000000..72aaf5d --- /dev/null +++ b/src/game/CollisionCategory.hx @@ -0,0 +1,19 @@ +package game; + +/** + * ... + * @author Dimonte + */ +class CollisionCategory +{ + public static inline var INHERIT:Int = 0x00000000; + + public static inline var PLAYER:Int = 0x00000001; + public static inline var TILE:Int = 0x00000002; + public static inline var LAND_OBJECT:Int = 0x00000004; + public static inline var ENEMY:Int = 0x00000008; + public static inline var PLAYER_BULLET:Int = 0x00000010; + public static inline var ENEMY_BULLET:Int = 0x00000020; + public static inline var PARTICLE:Int = 0x00000040; + static public inline var IGNORE:Int = 0x10000000; +} \ No newline at end of file diff --git a/src/game/Debries.hx b/src/game/Debries.hx new file mode 100644 index 0000000..17b190d --- /dev/null +++ b/src/game/Debries.hx @@ -0,0 +1,96 @@ +package game; + +//import assets.GraphicsLibrary; +import assets.GraphicsLibrary; +import sphaeraGravita.collision.PhysicalBody; +import sphaeraGravita.shapes.BaseShape; +import game.GameEntity; +import sphaeraGravita.shapes.CircleShape; +/** + * ... + * @author Dmitriy Barabanschikov + */ +class Debries extends GameEntity +{ + private var score:Int; + + public function new(world:GameWorld) + { + super(world); + + type = EntityType.DEBRIES; + + graphicsAsset = GraphicsLibrary.getDebries(); + + collisionCategory = CollisionCategory.PARTICLE; + + gridBound = true; + unguidedProjectile = true; + + bounceFactor = 0.5; + frictionFactor = 1; + + selectGraphicsAndScore(); + + var shape:CircleShape = new CircleShape(0, 0, 6); + shape.collisionMask = CollisionCategory.TILE; + addShape(shape); + + var sensorShape:CircleShape = new CircleShape(0, 0, 6); + sensorShape.sensor = true; + sensorShape.collisionMask = CollisionCategory.PLAYER; + addShape(sensorShape); + init(); + } + + override public function reset():Void + { + super.reset(); + collisionMask = CollisionCategory.TILE | CollisionCategory.PLAYER; + selectGraphicsAndScore(); + } + + override public function update(dTime:Float):Void + { + speed.y += 400*dTime; + } + + override public function reactToCollision(collidingBody:PhysicalBody, selfShape:BaseShape, otherShape:BaseShape):Void + { + super.reactToCollision(collidingBody, selfShape, otherShape); + + if (collidingBody.collisionCategory == CollisionCategory.PLAYER) + { + world.score += score; + dead = true; + return; + } + + speed.x *= 0.2; + if (speed.y <= 0) + { + speed.y = Math.min(speed.y, -5); + } + collisionMask = CollisionCategory.PLAYER; + } + + private function selectGraphicsAndScore():Void + { + if (Math.random() > 0.3) + { + frameRectNum = 2; + score = 100; + } + else if (Math.random() > 0.3) + { + frameRectNum = 0; + score = 75; + } + else + { + frameRectNum = 1; + score = 50; + } + } + +} \ No newline at end of file diff --git a/src/game/EnemyBullet.hx b/src/game/EnemyBullet.hx new file mode 100644 index 0000000..2e44eeb --- /dev/null +++ b/src/game/EnemyBullet.hx @@ -0,0 +1,55 @@ +package game; + +import assets.GraphicsLibrary; +import sphaeraGravita.collision.PhysicalBody; +import sphaeraGravita.shapes.BaseShape; +import sphaeraGravita.shapes.CircleShape; +/** + * ... + * @author Dmitriy Barabanschikov + */ +class EnemyBullet extends GameEntity +{ + public var bulletDamage:Int; + + public function new(world:GameWorld) + { + bulletDamage = 20; + + super(world); + + type = EntityType.ENEMY_BULLET; + + graphicsAsset = GraphicsLibrary.getEnemyBullet(); + + collisionCategory = CollisionCategory.ENEMY_BULLET; + collisionMask = CollisionCategory.TILE | CollisionCategory.PLAYER; + unguidedProjectile = true; + gridBound = true; + /* + var s:Polygon = new Polygon(); + s.setAsBox(1, 1); + /*/ + var s:CircleShape = new CircleShape(0,0,6); + //*/ + s.sensor = true; + addShape(s); + init(); + } + + override public function reactToCollision(collidingBody:PhysicalBody, selfShape:BaseShape, otherShape:BaseShape):Void + { + cast(collidingBody, GameEntity).recieveDamage(bulletDamage); + var bh:GameEntity = world.getReusableEntityByType(EntityType.BULLET_HIT); + bh.x = x; + bh.y = y; + world.addParticle(bh); + dead = true; + } + + override public function update(dTime:Float):Void + { + super.update(dTime); + } + +} \ No newline at end of file diff --git a/src/game/EnemyShip.hx b/src/game/EnemyShip.hx new file mode 100644 index 0000000..d121f9f --- /dev/null +++ b/src/game/EnemyShip.hx @@ -0,0 +1,250 @@ +package game; + +import assets.GraphicsLibrary; +import game.behaviours.BaloonBehaviour; +import game.behaviours.BasicBehaviour; +import game.behaviours.IBehaviour; +import game.behaviours.KamikazeBaloonBehaviour; +import game.behaviours.TeslaBehaviourFrontalAssault; +import game.behaviours.TeslaBehaviourRam; +import sphaeraGravita.collision.PhysicalBody; +import sphaeraGravita.shapes.BaseShape; +import game.GameEntity; +import sphaeraGravita.shapes.CircleShape; +import sphaeraGravita.shapes.Polygon; +/** + * ... + * @author Dmitriy Barabanschikov + */ +class EnemyShip extends GameEntity +{ + public static inline var TESLA:String = "tesla"; + public static inline var BALOON:String = "baloon"; + + public var shipType:String; + public var shotDown:Bool; + private var counter:Float; + public var behaviour:IBehaviour; + + private var smokeCounter:Float; + + public function new(world:GameWorld) + { + super(world); + + type = EntityType.ENEMY_SHIP; + + collisionCategory = CollisionCategory.ENEMY; + collisionMask = CollisionCategory.PLAYER_BULLET | CollisionCategory.PLAYER; + + shipType = Math.random() > 0.6 ? TESLA : BALOON; + + if (shipType == TESLA) + { + createAsTesla(); + } + else + { + createAsBalloon(); + } + + counter = 0; + smokeCounter = 0; + + init(); + } + + override public function reset():Void + { + super.reset(); + + clearShapes(); + rotation = 0; + speed.x = 0; + speed.y = 0; + counter = 0; + smokeCounter = 0; + + collisionCategory = CollisionCategory.ENEMY; + collisionMask = CollisionCategory.PLAYER_BULLET | CollisionCategory.PLAYER; + + shotDown = false; + unguidedProjectile = false; + + shipType = Math.random() > 0.6 ? TESLA : BALOON; + + if (shipType == TESLA) + { + createAsTesla(); + } + else + { + createAsBalloon(); + } + + init(); + } + + override public function reactToCollision(collidingBody:PhysicalBody, selfShape:BaseShape, otherShape:BaseShape):Void + { + cast(collidingBody, GameEntity).recieveDamage(5); + if (shotDown) + { + if (Type.getClass(collidingBody) == Tile) + { + die(); + } + } + } + + public function die():Void + { + dead = true; + var exp:GameEntity = world.getReusableEntityByType(EntityType.EXPLOSION); + exp.position = position.clone(); + world.addParticle(exp); + + var debriesChance:Float = shipType == TESLA ? 0.8 : 0.6; + var maxNumber:Int = shipType == TESLA ? 6 : 2; + var minNumber:Int = shipType == TESLA ? 3 : 1; + + + while (Math.random() < debriesChance || minNumber > 0) + { + var debries:GameEntity = world.getReusableEntityByType(EntityType.DEBRIES); + debries.position.x = x; + debries.position.y = y; + debries.speed.x = (Math.random() * 10 - 5)*30; + debries.speed.y = (-Math.random() * 5 - 5)*30; + world.addParticle(debries); + minNumber--; + if (--maxNumber < 1) + { + break; + } + } + + } + + override public function update(dTime:Float):Void + { + super.update(dTime); + if (behaviour != null) + { + behaviour.update(dTime); + } + if (x < world.activeZoneStartMarker - 60) + { + dead = true; + } + + if (!dead && shotDown) + { + if (smokeCounter > 0.125) + { + var smokeParticle:GameEntity = world.getReusableEntityByType(EntityType.SMOKE); + smokeParticle.position.x = position.x + 5; + smokeParticle.position.y = position.y - 5; + world.addParticle(smokeParticle); + smokeCounter = 0; + } + else + { + smokeCounter+=dTime; + } + } + + } + + public function shootAtPlayer():Void + { + var player:GameEntity = world.player; + var dx:Int = Std.int(player.x - x); + dx = Std.int(dx* 0.9); + var dy:Int = Std.int(player.y - y); + var angle:Float = Math.atan2(dy, dx); + var bullet:GameEntity = world.getReusableEntityByType(EntityType.ENEMY_BULLET); + bullet.position.x = x; + bullet.position.y = y; + bullet.speed.x = 360*Math.cos(angle); + bullet.speed.y = 360*Math.sin(angle); + world.addEnemyBullet(bullet); + } + + public function shoot(angle:Float, speed:Float):Void + { + var bullet:GameEntity = world.getReusableEntityByType(EntityType.ENEMY_BULLET); + bullet.position.y = y; + bullet.speed.x = speed*Math.cos(angle); + bullet.speed.y = speed * Math.sin(angle); + if (shipType == TESLA) + { + bullet.frameRectNum = 0; + bullet.position.x = x - 26; + cast(bullet, EnemyBullet).bulletDamage = 15; + } + else + { + bullet.position.x = x; + bullet.frameRectNum = 1; + cast(bullet, EnemyBullet).bulletDamage = 30; + } + world.addEnemyBullet(bullet); + } + + override public function recieveDamage(damageAmount:Int):Void + { + super.recieveDamage(damageAmount); + if (Type.getClass(behaviour) == BasicBehaviour) + { + cast(behaviour, BasicBehaviour).respondToDamage(); + } + if (shipType == TESLA) + { + if (healthPoints < 0) + { + frameRectNum = 2; + } else if (healthPoints < 100) + { + frameRectNum = 1; + } + } + } + + private function createAsBalloon():Void + { + graphicsAsset = GraphicsLibrary.getBaloon(); + var circle:CircleShape= new CircleShape(-10,0,12); + circle.sensor = true; + addShape(circle); + healthPoints = 100; + speed.x = Std.int(Math.random() * -2 - 1) *10; + if (Math.random() > 0.5) + { + behaviour = new BaloonBehaviour(this); + } else + { + behaviour = new KamikazeBaloonBehaviour(this); + } + } + + private function createAsTesla():Void + { + graphicsAsset = GraphicsLibrary.getTeslaFlyer(); + var shape:Polygon = new Polygon(); + shape.sensor = true; + shape.setAsBox(50, 12); + addShape(shape); + healthPoints = 200; + if (Math.random() > 0.7) + { + speed.x = Std.int(Math.random() * -4 - 4)*40; + behaviour = new TeslaBehaviourRam(this); + } else + { + speed.x = Std.int(Math.random() * -6 - 1)*40; + behaviour = new TeslaBehaviourFrontalAssault(this); + } + } + +} \ No newline at end of file diff --git a/src/game/EntityType.hx b/src/game/EntityType.hx new file mode 100644 index 0000000..abc07d9 --- /dev/null +++ b/src/game/EntityType.hx @@ -0,0 +1,23 @@ +package game; + +/** + * ... + * @author Dmitriy Barabanschikov + */ + +class EntityType +{ + + static public inline var PLAYER:String = "player"; + static public inline var TILE:String = "tile"; + static public inline var PLAYER_BULLET:String = "playerBullet"; + static public inline var ENEMY_BULLET:String = "enemyBullet"; + static public inline var BULLET_HIT:String = "bulletHit"; + static public inline var RAILGUN_TRAIL:String = "railgunTrail"; + static public inline var ENEMY_SHIP:String = "enemyShip"; + static public inline var DEBRIES:String = "debries"; + static public inline var EXPLOSION:String = "explosion"; + static public inline var SMOKE:String = "smoke"; + static public inline var UNKNOWN:String = "unknown"; + +} \ No newline at end of file diff --git a/src/game/Explosion.hx b/src/game/Explosion.hx new file mode 100644 index 0000000..31ef8e0 --- /dev/null +++ b/src/game/Explosion.hx @@ -0,0 +1,42 @@ +package game; + +//import assets.GraphicsLibrary; +import assets.GraphicsLibrary; +import sphaeraGravita.shapes.CircleShape; +/** + * ... + * @author Dmitriy Barabanschikov + */ +class Explosion extends GameEntity +{ + private var counter:Float; + + public function new(world:GameWorld) + { + super(world); + type = EntityType.EXPLOSION; + graphicsAsset = GraphicsLibrary.getExplosion1(); + gridBound = true; + unguidedProjectile = true; + init(); + counter = 0; + } + + override public function reset():Void + { + super.reset(); + counter = 0; + } + + override public function update(dTime:Float):Void + { + super.update(dTime); + counter+=dTime; + frameRectNum = Std.int(counter*5*1.25); + if (counter >= 0.8) + { + dead = true; + } + } + +} \ No newline at end of file diff --git a/src/game/GameEntity.hx b/src/game/GameEntity.hx new file mode 100644 index 0000000..c8be2a3 --- /dev/null +++ b/src/game/GameEntity.hx @@ -0,0 +1,52 @@ +package game; + +import assets.EntityGraphicsAsset; +import sphaeraGravita.collision.PhysicalBody; +import sphaeraGravita.shapes.BaseShape; +/** + * ... + * @author Dimonte + */ +class GameEntity extends PhysicalBody +{ + public var world:GameWorld; + public var healthPoints:Int; + public var unguidedProjectile:Bool; + public var graphicsAsset:EntityGraphicsAsset; + public var frameRectNum:Int; + public var type:String; + + public function new(world:GameWorld) + { + this.world = world; + type = EntityType.UNKNOWN; + super(); + } + + public function reset():Void + { + dead = false; + frameRectNum = 0; + } + + public function destroy():Void + { + dead = true; + } + + public function update(dTime:Float):Void + { + + } + + public function doDamage():Void + { + + } + + public function recieveDamage(damageAmount:Int):Void + { + healthPoints -= damageAmount; + } + +} \ No newline at end of file diff --git a/src/game/GameEvent.hx b/src/game/GameEvent.hx new file mode 100644 index 0000000..1a291c3 --- /dev/null +++ b/src/game/GameEvent.hx @@ -0,0 +1,23 @@ +package game; +import flash.events.Event; + +/** + * ... + * @author Dmitriy Barabanschikov + */ + +class GameEvent extends Event +{ + static public inline var GAME_OVER:String = "gameOver"; + + public function new(type:String, bubbles:Bool = false, cancelable:Bool = false) + { + super(type, bubbles, cancelable); + } + + override public function clone():Event + { + return new GameEvent(type, bubbles, cancelable); + } + +} \ No newline at end of file diff --git a/src/game/GameStage.hx b/src/game/GameStage.hx new file mode 100644 index 0000000..2fec3b3 --- /dev/null +++ b/src/game/GameStage.hx @@ -0,0 +1,143 @@ +package game; + +import com.sphaeraobscura.keyboard.KeyKeeper; +import flash.events.EventDispatcher; +//import utils.logger.Logger; +/** + * ... + * @author ... + */ +class GameStage extends EventDispatcher +{ + /** + * Speed of moving through level in pixels/second. + */ + static private inline var ACTIVE_ZONE_SHIFT:Float= 60; + + public var gameWorld:GameWorld; + public var player:PlayerShip; + public var playerController:PlayerController; + private var loadedLevel:Level; + private var gameOver:Bool; + private var activeZoneWidth:Int; + private var activeZoneHeight:Int; + + + + public function new(activeZoneWidth:Int, activeZoneHeight:Int) + { + super(); + this.activeZoneWidth = activeZoneWidth; + this.activeZoneHeight = activeZoneHeight; + gameWorld = new GameWorld(activeZoneWidth, activeZoneHeight); + } + + public function initializeStage():Void + { + gameWorld.clear(); + player = new PlayerShip(gameWorld); + player.position.x = 60; + player.position.y = 100; + playerController = new PlayerController(player); + gameWorld.addActiveEntity(player); + gameWorld.player = player; + + loadLevel(new Level(activeZoneWidth, activeZoneHeight)); + + gameOver = false; + } + + public function loadLevel(level:Level):Void + { + loadedLevel = level; + //Logger.log("level.tiles.length : " + level.tiles.length); + + level.tiles.sort(sortEntityOnX); + + level.enemies.sort(sortEntityOnX); + } + + private function sortEntityOnX(a:LevelObjectTemplate, b:LevelObjectTemplate):Int + { + if (a.position.x < b.position.x) + { + return 1; + } + if (a.position.x > b.position.x) + { + return -1; + } + if (a.position.x == b.position.x) + { + return 0; + } + return 0; + } + + private function spawnTiles():Void + { + while (loadedLevel.tiles.length > 0) + { + var tileTemplate:LevelObjectTemplate = loadedLevel.tiles[loadedLevel.tiles.length-1]; + if (tileTemplate.position.x <= gameWorld.activeZoneStartMarker + activeZoneWidth + 60) + { + var tile:Tile = cast(gameWorld.getReusableEntityByType(EntityType.TILE), Tile); + tile.position = tileTemplate.position; + tile.createFromTemplate(cast(tileTemplate, TileTemplate).tileTemplate); + gameWorld.addTile(tile); + loadedLevel.tiles.pop(); + } else + { + break; + } + } + } + + private function spawnEnemies():Void + { + while (loadedLevel.enemies.length > 0) + { + var enemyTemplate:LevelObjectTemplate = loadedLevel.enemies[loadedLevel.enemies.length-1]; + if (enemyTemplate.position.x <= gameWorld.activeZoneStartMarker + activeZoneWidth + 60) + { + var enemy:GameEntity = gameWorld.getReusableEntityByType(EntityType.ENEMY_SHIP); + enemy.x = enemyTemplate.position.x; + enemy.y = enemyTemplate.position.y; + gameWorld.addEnemy(enemy); + loadedLevel.enemies.pop(); + } else + { + break; + } + } + } + + public function update(dTime:Float):Void + { + if (!player.dead) + { + + gameWorld.activeZoneStartMarker += ACTIVE_ZONE_SHIFT*dTime; + player.x += 60*dTime; + + playerController.update(dTime); + + spawnEnemies(); + + spawnTiles(); + } + else + { + if (!gameOver) + { + gameOver = true; + dispatchEvent(new GameEvent(GameEvent.GAME_OVER)); + } + } + + + + gameWorld.update(dTime); + } + +} \ No newline at end of file diff --git a/src/game/GameWorld.hx b/src/game/GameWorld.hx new file mode 100644 index 0000000..8e40d55 --- /dev/null +++ b/src/game/GameWorld.hx @@ -0,0 +1,365 @@ +package game; + +import dataStructures.DLL; +import dataStructures.DLLNode; +import flash.errors.Error; +import game.CollisionCategory; +import game.GameEntity; +import haxe.ds.GenericStack; +import haxe.ds.StringMap; +import sphaeraGravita.collision.CollisionEngine; +import sphaeraGravita.spacePartitioning.GridCell; +import sphaeraGravita.spacePartitioning.UniformGrid; +/** + * ... + * @author Dmitriy Barabanschikov + */ +class GameWorld +{ + public var activeZoneStartMarker:Float; + + public var collisionEngine:CollisionEngine; + + public var grid:UniformGrid; + + public var player:PlayerShip; + public var players:DLL; + public var tiles:DLL; + //public var stationaryObjects:DLL; + public var particles:DLL; + public var enemies:DLL; + public var playerBullets:DLL; + public var enemyBullets:DLL; + + public var score:Int; + + private var reuseLists:StringMap>; + + private var activeZoneWidth:Int; + private var activeZoneHeight:Int; + + public function new(activeZoneWidth:Int, activeZoneHeight:Int) + { + this.activeZoneWidth = activeZoneWidth; + this.activeZoneHeight = activeZoneHeight; + + activeZoneStartMarker = 0; + reuseLists = new StringMap(); + collisionEngine = new CollisionEngine(); + players = new DLL(); + playerBullets = new DLL(); + enemyBullets = new DLL(); + tiles = new DLL(); + enemies = new DLL(); + particles = new DLL(); + grid = new UniformGrid(); + grid.initialize(Level.LEVEL_WIDTH, Math.ceil(activeZoneHeight/40), 4); + } + + public function clear():Void + { + player = null; + players.clear(); + tiles.clear(); + //stationaryObjects.clear(); + particles.clear(); + enemies.clear(); + playerBullets.clear(); + enemyBullets.clear(); + + grid.initialize(Level.LEVEL_WIDTH, Math.ceil(activeZoneHeight/40), 4); + score = 0; + activeZoneStartMarker = 0; + } + + private function initializeFixedEntities():Void + { + + } + + public function addActiveEntity(gameEntity:GameEntity):Void + { + players.append(gameEntity); + } + + public function addEnemy(enemy:GameEntity):Void + { + enemies.append(enemy); + } + + public function addPlayerBullet(gameEntity:GameEntity):Void + { + playerBullets.append(gameEntity); + } + + public function addEnemyBullet(gameEntity:GameEntity):Void + { + enemyBullets.append(gameEntity); + } + + public function addFixedEntiry(gameEntity:GameEntity):Void + { + tiles.append(gameEntity); + gameEntity.updateWorldCache(); + } + + public function addTile(tile:Tile):Void + { + tiles.append(tile); + tile.updateWorldCache(); + grid.placeTile(tile); + } + + public function addParticle(particle:GameEntity):Void + { + particle.updateWorldCache(); + grid.placeActiveObject(particle); + particles.append(particle); + } + + public function update(dTime:Float):Void + { + grid.clean(); + + //trace(playerBullets.length); + + updatePositionsInList(players, dTime); + if (player.worldAABB.lowerBound.x < activeZoneStartMarker) + { + player.position.x += activeZoneStartMarker - player.worldAABB.lowerBound.x; + player.updateWorldCache(); + } + + if (player.worldAABB.upperBound.x > activeZoneStartMarker+activeZoneWidth) + { + player.position.x += activeZoneStartMarker + activeZoneWidth - player.worldAABB.upperBound.x; + player.updateWorldCache(); + } + + if (player.worldAABB.lowerBound.y < 0) + { + player.position.y += 0 - player.worldAABB.lowerBound.y; + player.updateWorldCache(); + } + + if (player.worldAABB.upperBound.y > activeZoneHeight && player.healthPoints > 0) + { + player.position.y += activeZoneHeight - player.worldAABB.upperBound.y; + player.updateWorldCache(); + } + + updateTiles(); + + updatePositionsInList(playerBullets, dTime); + updatePositionsInList(enemyBullets, dTime); + updatePositionsInList(enemies, dTime); + updatePositionsInList(particles, dTime); + + collideObjectWithGrid(player); + collideListWithList(players, enemies); + collideListWithList(players, particles); + collideListWithGrid(enemies, grid); + collideListWithGrid(playerBullets, grid); + collideListWithGrid(enemyBullets, grid); + collideListWithGrid(particles, grid); + + updateLogicInList(players, dTime); + updateLogicInList(playerBullets, dTime); + updateLogicInList(enemies, dTime); + updateLogicInList(particles, dTime); + } + + private function updateTiles() + { + var activeBodyNode:DLLNode = tiles._head; + var activeBody:GameEntity; + while (activeBodyNode != null) + { + activeBody = cast(activeBodyNode.val, GameEntity); + if (activeBody.worldAABB.upperBound.x < activeZoneStartMarker) + { + tiles.remove(activeBodyNode); + placeEntityForReuse(activeBody); + } + + activeBodyNode = activeBodyNode.next; + } + } + + public function updatePositionsInList(list:DLL, dTime:Float):Void + { + var activeBodyNode:DLLNode = list._head; + var activeBody:GameEntity; + while (activeBodyNode != null) + { + activeBody = cast(activeBodyNode.val, GameEntity); + if (activeBody.dead) + { + list.remove(activeBodyNode); //TODO transfer to logic update? + placeEntityForReuse(activeBody); + } else { + activeBody.position.x += activeBody.speed.x*dTime; + activeBody.position.y += activeBody.speed.y*dTime; + activeBody.updateWorldCache(); + if (activeBody.gridBound) + { + grid.placeActiveObject(activeBody); + } + if (activeBody.unguidedProjectile) + { + if (!activeBody.inBounds(activeZoneStartMarker, 0, activeZoneWidth, activeZoneHeight)) + { + activeBody.dead = true; + } + } + } + + activeBodyNode = activeBodyNode.next; + } + } + + public function placeEntityForReuse(activeBody:GameEntity) + { + if (activeBody.type == EntityType.UNKNOWN) + { + throw new Error("Unknown type of reusable asset"); + return; + } + var reuseList:GenericStack = reuseLists.get(activeBody.type); + if (reuseList == null) + { + reuseList = new GenericStack(); + reuseLists.set(activeBody.type, reuseList); + } + reuseList.add(activeBody); + } + + public function getReusableEntityByType(type:String):GameEntity + { + var newEntity:GameEntity = null; + + var reuseList:GenericStack = reuseLists.get(type); + if (reuseList != null) + { + if (reuseList.head != null) + { + newEntity = reuseList.pop(); + newEntity.reset(); + return newEntity; + } + } + + switch (type) + { + case EntityType.BULLET_HIT: + newEntity = new BulletHit(this); + case EntityType.ENEMY_BULLET: + newEntity = new EnemyBullet(this); + case EntityType.PLAYER: + newEntity = new PlayerShip(this); + case EntityType.PLAYER_BULLET: + newEntity = new Bullet(this); + case EntityType.RAILGUN_TRAIL: + newEntity = new RailgunTrail(this); + case EntityType.ENEMY_SHIP: + newEntity = new EnemyShip(this); + case EntityType.DEBRIES: + newEntity = new Debries(this); + case EntityType.EXPLOSION: + newEntity = new Explosion(this); + case EntityType.TILE: + newEntity = new Tile(this); + case EntityType.SMOKE: + newEntity = new SmokeParticle(this); + default: + newEntity = new GameEntity(this); + } + + return newEntity; + } + + public function updateLogicInList(list:DLL, dTime:Float):Void + { + var activeBodyNode:DLLNode = list._head; + var activeBody:GameEntity; + while (activeBodyNode != null) + { + + activeBody = cast(activeBodyNode.val, GameEntity); + activeBodyNode = activeBodyNode.next; + activeBody.update(dTime); + + } + } + + public function collideListWithList(listOne:DLL, listTwo:DLL):Void + { + var listOneNode:DLLNode = listOne._head; + var listOneEntity:GameEntity; + while (listOneNode != null) + { + listOneEntity = cast(listOneNode.val, GameEntity); + var listTwoNode:DLLNode = listTwo._head; + var listTwoEntity:GameEntity; + while (listTwoNode != null) + { + listTwoEntity = cast(listTwoNode.val, GameEntity); + collisionEngine.collideBodies(listOneEntity, listTwoEntity); + listTwoNode = listTwoNode.next; + } + listOneNode = listOneNode.next; + } + } + + public function collideObjectWithGrid(object:GameEntity):Void + { + if (object.collisionCategory == CollisionCategory.IGNORE) + return; + + var coveredCells:Array; + if (object.gridBound) + { + coveredCells = object.overlappingCells; + } else + { + coveredCells = grid.getCellsBetweenPoints(object.worldAABB.lowerBound, object.worldAABB.upperBound); + } + var cellListNode:DLLNode; + var cellObject:GameEntity; + //trace("coveredCells.length: " + coveredCells.length); + for (i in 0...coveredCells.length) + { + cellListNode = coveredCells[i].fixedObjectList._head; + while (cellListNode != null) + { + cellObject = cast(cellListNode.val, GameEntity); + + if (cellObject != object) + collisionEngine.collideBodies(object, cellObject); + + cellListNode = cellListNode.next; + } + cellListNode = coveredCells[i].activeObjectList._head; + while (cellListNode != null) + { + cellObject = cast(cellListNode.val, GameEntity); + + if (cellObject != object) + collisionEngine.collideBodies(object, cellObject); + + cellListNode = cellListNode.next; + } + } + } + + public function collideListWithGrid(list:DLL, grid:UniformGrid):Void + { + var listNode:DLLNode = list._head; + while (listNode != null) + { + collideObjectWithGrid(cast(listNode.val, GameEntity)); + listNode = listNode.next; + } + } + +} \ No newline at end of file diff --git a/src/game/Level.hx b/src/game/Level.hx new file mode 100644 index 0000000..7f5ba81 --- /dev/null +++ b/src/game/Level.hx @@ -0,0 +1,288 @@ +package game; + +import game.TileTemplate; +import haxe.ds.IntMap.IntMap; + +/** + * ... + * @author Dimonte + */ +class Level +{ + + static public inline var LEVEL_WIDTH:Int = 600; + public var tiles:Array; + public var enemies:Array; + private var surroundingTiles:Array; + private var levelHeight:Int; + private var activeZoneWidth:Int; + private var activeZoneHeight:Int; + + static public var templatePriority:Array = + { + [TilePositionTemplate.middle, TilePositionTemplate.slopeUp, TilePositionTemplate.slopeDown, TilePositionTemplate.top ]; + + } + + public function new(activeZoneWidth:Int, activeZoneHeight:Int) + { + this.activeZoneWidth = activeZoneWidth; + this.activeZoneHeight = activeZoneHeight; + this.levelHeight = Math.ceil(activeZoneHeight/40); + surroundingTiles = new Array(); + tiles = new Array(); + enemies = new Array(); + var normalHeight:Int = 2; + var previousHeight:Int = normalHeight; + var height:Int = previousHeight; + var heightChangeSpeed:Int = 0; + var tileGrid:TileGrid = new TileGrid(LEVEL_WIDTH, levelHeight); + + var tileTemplate:TileTemplate; + for (i in 0...LEVEL_WIDTH) + { + var heightChangeSpeedChange:Int = Std.int(Math.random() * 2) + Std.int(Math.random() * 2) - 1; + if (height > normalHeight && heightChangeSpeed > 0) { + heightChangeSpeedChange += Math.round((normalHeight - height) / 1); + } + if (height < normalHeight && heightChangeSpeed < 0) { + heightChangeSpeedChange += Math.round((normalHeight - height) / 1); + } + heightChangeSpeed += heightChangeSpeedChange; + height += heightChangeSpeed; + + for (j in 0...height) + { + tileTemplate = new TileTemplate(); + tileTemplate.tileGridX = i; + tileTemplate.tileGridY = levelHeight - 1 - j; + tileTemplate.position.x = i * 40 + 20; + tileTemplate.position.y = Std.int(levelHeight - 1 - j) * 40 + 20; + tiles.push(tileTemplate); + tileGrid.addTileAt(i, levelHeight - 1 - j, tileTemplate); + } + previousHeight = height; + } + + for (i in 0...tiles.length) + { + tileTemplate = cast(tiles[i], TileTemplate); + findTemplateForTile(tileTemplate, tileGrid); + } + + for (i in 0...160) + { + + var diffMod:Float = (160 - i) / 160 - 0.4; + var minShips:Int = Std.int(i / 40) +1; + var shipsAdded:Int = 0; + + while (Math.random() > diffMod || shipsAdded < minShips) + { + diffMod += 0.05; + var enemyShipTemplate:LevelObjectTemplate = new LevelObjectTemplate(); + enemyShipTemplate.position.x = i * 90 + activeZoneWidth + Math.random()*90; + enemyShipTemplate.position.y = Math.random() * activeZoneHeight/2 + 40; + enemies.push(enemyShipTemplate); + shipsAdded++; + } + } + + tileGrid = null; + } + + private function findTemplateForTile(tileTemplate:TileTemplate, tileGrid:TileGrid):Void + { + //To match templates, reading in column/row + for (j in 0...3) + { + for (i in 0...3) + { + surroundingTiles[i+j*3] = tileGrid.isTileAt(i + tileTemplate.tileGridX - 1, j + tileTemplate.tileGridY - 1); + } + } + + var matchFound:Bool = false; + var tilePositionTemplate:TilePositionTemplate = null; + var positionTemplate:Array; + for (i in 0...templatePriority.length) + { + tilePositionTemplate = templatePriority[i]; + positionTemplate = tilePositionTemplate.template; + matchFound = true; + for (j in 0...positionTemplate.length) + { + if (positionTemplate[j] == -1) + { + continue; + } + if (positionTemplate[j] == 0) + { + if (surroundingTiles[j]) + { + matchFound = false; + break; + } + } + if (positionTemplate[j] == 1) + { + if (!surroundingTiles[j]) + { + matchFound = false; + break; + } + } + } + if (matchFound) + { + break; + } + } + + if (!matchFound) + { + tilePositionTemplate = TilePositionTemplate.top; + } + + tileTemplate.tileTemplate = tilePositionTemplate.code; + + } + +} + +class TileGrid +{ + private var standInTile:TileTemplate; + private var tileHash:IntMap>; + private var width:Int; + private var height:Int; + + public function new(width:Int, height:Int) + { + standInTile = new TileTemplate(); + this.width = width; + this.height = height; + tileHash = new IntMap(); + var tileColumnHash:IntMap; + for (i in 0...width) + { + tileColumnHash = new IntMap(); + tileHash.set(i, tileColumnHash); + } + } + + public function addTileAt(x:Int, y:Int, tileTemplate:TileTemplate):Void + { + tileHash.get(x).set(y, tileTemplate); + } + + public function getTileAt(x:Int, y:Int):Null + { + if (x < 0) + { + return null; + } + if (x >= width) + { + return null; + } + if (y < 0) + { + return null; + } + if (y >= height) + { + return standInTile; + } + return tileHash.get(x).get(y); + } + + public function isTileAt(x:Int, y:Int):Bool + { + if (x < 0) + { + return false; + } + if (x >= width) + { + return false; + } + if (y < 0) + { + return false; + } + if (y >= height) + { + return true; + } + return tileHash.get(x).get(y) != null; + } +} + +class TilePositionTemplate +{ + + static public var slopeUp:TilePositionTemplate = + { + var tilePosTemp:TilePositionTemplate = new TilePositionTemplate(); + tilePosTemp.template = + [ -1, 0, -1, + 0, 1, 1, + -1, 1, 1] + ; + + /*tilePosTemp.template = + [ -1, -1, -1, + -1, -1, -1, + -1, -1, -1] + ;*/ + + tilePosTemp.code = Tile.SLOPE_UP; + tilePosTemp; + } + + static public var slopeDown:TilePositionTemplate = + { + var tilePosTemp:TilePositionTemplate = new TilePositionTemplate(); + tilePosTemp.template = + [ -1, 0, -1, + 1, 1, 0, + 1, 1, -1] + ; + tilePosTemp.code = Tile.SLOPE_DOWN; + tilePosTemp; + } + + static public var middle:TilePositionTemplate = + { + var tilePosTemp:TilePositionTemplate = new TilePositionTemplate(); + tilePosTemp.template = + [ -1, 1, -1, + -1, 1, -1, + -1, 1, -1] + ; + tilePosTemp.code = Tile.MIDDLE; + tilePosTemp; + } + + static public var top:TilePositionTemplate = + { + var tilePosTemp:TilePositionTemplate = new TilePositionTemplate(); + tilePosTemp.template = + [ -1, 0, -1, + -1, 1, -1, + -1, 1, -1] + ; + tilePosTemp.code = Tile.TOP; + tilePosTemp; + } + + + public var template:Array; + public var code:String; + + public function new() + { + + } +} \ No newline at end of file diff --git a/src/game/LevelObjectTemplate.hx b/src/game/LevelObjectTemplate.hx new file mode 100644 index 0000000..3103f1b --- /dev/null +++ b/src/game/LevelObjectTemplate.hx @@ -0,0 +1,19 @@ +package game; +import sphaeraGravita.math.Vector2D; + +/** + * ... + * @author Dmitriy Barabanschikov + */ + +class LevelObjectTemplate +{ + + public var position:Vector2D; + + public function new() + { + position = new Vector2D(); + } + +} \ No newline at end of file diff --git a/src/game/PlayerController.hx b/src/game/PlayerController.hx new file mode 100644 index 0000000..ecf9eca --- /dev/null +++ b/src/game/PlayerController.hx @@ -0,0 +1,87 @@ +package game; +import game.PlayerShip; + +/** + * ... + * @author Dmitriy Barabanschikov + */ + +class PlayerController +{ + private var horizontalAccelerationMax:Float; + private var verticalAccelerationMax:Float; + + private var player:PlayerShip; + + private var horizontalAcceleration:Float; + private var verticalAcceleration:Float; + private var shootOnUpdate:Bool; + + public function new(player:PlayerShip) + { + this.player = player; + horizontalAccelerationMax = 3000; + verticalAccelerationMax = 3000; + horizontalAcceleration = 0; + verticalAcceleration = 0; + } + + public function accelerateRight(?magnitude:Float = 1.0):Void + { + horizontalAcceleration += magnitude; + } + + public function accelerateLeft(?magnitude:Float = 1.0):Void + { + horizontalAcceleration -= magnitude; + } + + public function accelrateDown(?magnitude:Float = 1.0):Void + { + verticalAcceleration += magnitude; + } + + public function accelrateUp(?magnitude:Float = 1.0):Void + { + verticalAcceleration -= magnitude; + } + + public function setHorizontalAcceleration(magnitude:Float):Void + { + horizontalAcceleration = magnitude; + } + + public function setVerticalAcceleration(magnitude:Float):Void + { + verticalAcceleration = magnitude; + } + + public function update(dTime:Float):Void + { + if (player == null) + { + return; + } + if (player.dead || player.healthPoints < 0) + { + return; + } + horizontalAcceleration = Math.max(Math.min(horizontalAcceleration, 1), -1); + verticalAcceleration = Math.max(Math.min(verticalAcceleration, 1), -1); + player.speed.x += horizontalAcceleration * horizontalAccelerationMax * dTime; + player.speed.y += verticalAcceleration * verticalAccelerationMax * dTime; + if (shootOnUpdate) + { + player.shoot(); + } + horizontalAcceleration = 0; + verticalAcceleration = 0; + shootOnUpdate = false; + } + + public function shoot():Void + { + shootOnUpdate = true; + } + +} \ No newline at end of file diff --git a/src/game/PlayerShip.hx b/src/game/PlayerShip.hx new file mode 100644 index 0000000..68c2c25 --- /dev/null +++ b/src/game/PlayerShip.hx @@ -0,0 +1,176 @@ +package game; +import assets.EntityGraphicsAsset; +import assets.GraphicsLibrary; +import sphaeraGravita.collision.PhysicalBody; +import sphaeraGravita.math.Vector2D; +import sphaeraGravita.shapes.BaseShape; +import sphaeraGravita.shapes.CircleShape; +import sphaeraGravita.shapes.Polygon; + +/** + * ... + * @author Dmitriy Barabanschikov + */ +class PlayerShip extends GameEntity +{ + private var reload:Int; + private var bulletAngle:Float; + private var smokeCounter:Float; + private var timePassedSinceLastSpeedUpdate:Float; + private var reloadTime:Float; + + public function new(world:GameWorld) + { + super(world); + type = EntityType.PLAYER; + + reloadTime = 0.02; + bulletAngle = 0; + smokeCounter = 0; + timePassedSinceLastSpeedUpdate = 0; + + collisionCategory = CollisionCategory.PLAYER; + collisionMask = CollisionCategory.ENEMY | CollisionCategory.TILE | CollisionCategory.ENEMY_BULLET | CollisionCategory.PARTICLE; + graphicsAsset = GraphicsLibrary.getPlayerShip(); + + bounceFactor = 2; + + var poly:Polygon = new Polygon(); + poly.setPoly([new Vector2D( -55, 0), new Vector2D( -55, -10), new Vector2D(0, -15), new Vector2D(50, -10), new Vector2D(50, 10), new Vector2D(20, 22), new Vector2D(0, 22)]); + addShape(poly); + var circle:CircleShape = new CircleShape(20, 0, 11); + addShape(circle); + init(); + + healthPoints = 800; + } + + public function accelerateForward():Void + { + + } + + public function accelerateBackwards():Void + { + + } + + public function accelerateUp():Void + { + + } + + public function accelerateDown():Void + { + + } + + override public function update(dTime:Float):Void + { + + //healthPoints -= 10; + + if (healthPoints <= 0) + { + unguidedProjectile = true; + speed.y += 900*dTime; + } + + if (reloadTime > 0) + { + reloadTime-= dTime; + } + + timePassedSinceLastSpeedUpdate += dTime; + while (timePassedSinceLastSpeedUpdate > 0.02) + { + speed.multiply(0.8); + timePassedSinceLastSpeedUpdate -= 0.02; + } + + if (!dead && healthPoints <= 0) + { + if (smokeCounter >= 0.05) + { + var smokeParticle:GameEntity = world.getReusableEntityByType(EntityType.SMOKE); + smokeParticle.position.x = position.x - 20 + Math.random()*50; + smokeParticle.position.y = position.y - 10 + Math.random()*5; + world.addParticle(smokeParticle); + smokeCounter = 0; + } + else + { + smokeCounter+=dTime; + } + } + } + + override public function reactToCollision(collidingBody:PhysicalBody, selfShape:BaseShape, otherShape:BaseShape):Void + { + super.reactToCollision(collidingBody, selfShape, otherShape); + cast(collidingBody, GameEntity).recieveDamage(10); + if (Type.getClass(collidingBody) == Tile) + { + if (healthPoints <= 0 ) + { + dead = true; + var exp:GameEntity = world.getReusableEntityByType(EntityType.EXPLOSION); + exp.position = position.clone(); + world.addParticle(exp); + while (Math.random() > 0.6) + { + var debries:GameEntity = world.getReusableEntityByType(EntityType.DEBRIES); + debries.position.x = x; + debries.position.y = y; + debries.speed.x = Math.random() * 10 - 5; + debries.speed.y = -Math.random() * 10; + world.addParticle(debries); + } + } + } + } + + override public function recieveDamage(damageAmount:Int):Void + { + super.recieveDamage(damageAmount); + } + + public function shoot():Void + { + if (healthPoints <= 0) + { + return; + } + if (reloadTime > 0) + { + return; + } + reloadTime = 0.02; + if (reload > 0) + { + reload--; + } else + { + reload = 1; + } + + bulletAngle += Math.PI / 34; + + var i:Int = 4; + while (i-- > 0) + { + if (i % 2 == reload) + { + continue; + } + var bullet:GameEntity = world.getReusableEntityByType(EntityType.PLAYER_BULLET); + bullet.position.x = this.position.x + 30; + bullet.position.y = this.position.y; + bullet.speed.x = 450; + bullet.speed.y = Math.sin((bulletAngle + i)*(i + 1))*60; + world.addPlayerBullet(bullet); + } + + } + +} \ No newline at end of file diff --git a/src/game/RailgunTrail.hx b/src/game/RailgunTrail.hx new file mode 100644 index 0000000..09e98ce --- /dev/null +++ b/src/game/RailgunTrail.hx @@ -0,0 +1,50 @@ +package game; +import assets.GraphicsLibrary; + +//import assets.GraphicsLibrary; +/** + * ... + * @author Dmitriy Barabanschikov + */ +class RailgunTrail extends GameEntity +{ + private var counter:Float; + private var timePassedSinceLastUpdate:Float; + + public function new(world:GameWorld) + { + super(world); + type = EntityType.RAILGUN_TRAIL; + collisionCategory = CollisionCategory.IGNORE; + counter = 0; + graphicsAsset = GraphicsLibrary.getRailgunTrail(); + gridBound = true; + unguidedProjectile = true; + timePassedSinceLastUpdate = 0; + init(); + } + + override public function reset():Void + { + super.reset(); + counter = 0; + timePassedSinceLastUpdate = 0; + } + + override public function update(dTime:Float):Void + { + super.update(dTime); + timePassedSinceLastUpdate += dTime; + while(timePassedSinceLastUpdate > 0.02) + { + speed.multiply(0.8); + timePassedSinceLastUpdate -= 0.02; + } + counter+=dTime; + if (counter > 0.4) + { + dead = true; + } + } + +} \ No newline at end of file diff --git a/src/game/SmokeParticle.hx b/src/game/SmokeParticle.hx new file mode 100644 index 0000000..3821ed7 --- /dev/null +++ b/src/game/SmokeParticle.hx @@ -0,0 +1,42 @@ +package game; +import assets.GraphicsLibrary; + +/** + * ... + * @author Dmitriy Barabanschikov + */ + +class SmokeParticle extends GameEntity +{ + private var counter:Float; + + public function new(world:GameWorld) + { + super(world); + counter = 0; + type = EntityType.SMOKE; + graphicsAsset = GraphicsLibrary.getSmoke(); + gridBound = true; + unguidedProjectile = true; + speed.y = -1; + init(); + } + + override public function reset():Void + { + super.reset(); + counter = 0; + } + + override public function update(dTime:Float):Void + { + super.update(dTime); + counter+=dTime; + frameRectNum = Std.int(counter * 5); + if (counter >= 1) + { + dead = true; + } + } + +} \ No newline at end of file diff --git a/src/game/Tile.hx b/src/game/Tile.hx new file mode 100644 index 0000000..db8a1de --- /dev/null +++ b/src/game/Tile.hx @@ -0,0 +1,63 @@ +package game; + +import assets.GraphicsLibrary; +import sphaeraGravita.collision.PhysicalBody; +import sphaeraGravita.math.Vector2D; +import sphaeraGravita.shapes.BaseShape; +import sphaeraGravita.shapes.Polygon; + +/** + * ... + * @author Dimonte + */ +class Tile extends GameEntity +{ + public static inline var MIDDLE:String = "middle"; + public static inline var TOP:String = "top"; + public static inline var SLOPE_UP:String = "slopeUp"; + public static inline var SLOPE_DOWN:String = "slopeDown"; + + public var template:String; + + public function new(world:GameWorld) + { + super(world); + type = EntityType.TILE; + collisionCategory = CollisionCategory.TILE; + graphicsAsset = GraphicsLibrary.getTiles(); + } + + public function createFromTemplate(template:String):Void + { + clearShapes(); + var shape:Polygon; + shape = new Polygon(); + this.template = template; + switch (template) + { + case MIDDLE: + shape.setAsBox(20, 20); + frameRectNum = Math.random() > 0.2 ? graphicsAsset.getFrameByAssetName("assets/tiles/middle.png") : graphicsAsset.getFrameByAssetName("assets/tiles/middle2.png"); + case SLOPE_UP: + shape.setPoly([new Vector2D( -20, 20), new Vector2D( 20, -20), new Vector2D( 20, 20)]); + frameRectNum = 2; + case SLOPE_DOWN: + shape.setPoly([new Vector2D( -20, -20), new Vector2D( 20, 20), new Vector2D( -20, 20)]); + frameRectNum = 3; + case TOP: + shape.setAsBox(20, 20); + frameRectNum = Math.random() > 0.2 ? graphicsAsset.getFrameByAssetName("assets/tiles/top2.png") : graphicsAsset.getFrameByAssetName("assets/tiles/top.png"); + default: + shape.setAsBox(20, 20); + } + addShape(shape); + init(); + } + + override public function reactToCollision(collidingBody:PhysicalBody, selfShape:BaseShape, otherShape:BaseShape):Void + { + super.reactToCollision(collidingBody, selfShape, otherShape); + cast(collidingBody, GameEntity).recieveDamage(10); + } + +} \ No newline at end of file diff --git a/src/game/TileTemplate.hx b/src/game/TileTemplate.hx new file mode 100644 index 0000000..8477618 --- /dev/null +++ b/src/game/TileTemplate.hx @@ -0,0 +1,20 @@ +package game; + +/** + * ... + * @author Dmitriy Barabanschikov + */ + +class TileTemplate extends LevelObjectTemplate +{ + public var tileGridX:Int; + public var tileGridY:Int; + + public var tileTemplate:String; + + public function new() + { + super(); + } + +} \ No newline at end of file diff --git a/src/game/behaviours/BaloonBehaviour.hx b/src/game/behaviours/BaloonBehaviour.hx new file mode 100644 index 0000000..ac32d88 --- /dev/null +++ b/src/game/behaviours/BaloonBehaviour.hx @@ -0,0 +1,70 @@ +package game.behaviours; + +import game.CollisionCategory; +import game.EnemyShip; +/** + * ... + * @author Dmitriy Barabanschikov + */ +class BaloonBehaviour extends BasicBehaviour +{ + + public function new(subject:EnemyShip) + { + super(subject); + } + + override public function init(dTime:Float):Void + { + updateFunction = oscillateDown; + } + + private function oscillateDown(dTime:Float):Void + { + if (subject.speed.y < 50) + { + subject.speed.y += 30*dTime; + } else + { + updateFunction = oscillateUp; + } + } + + private function oscillateUp(dTime:Float):Void + { + if (subject.speed.y > -50) + { + subject.speed.y -= 30*dTime; + } else + { + updateFunction = oscillateDown; + } + } + + override public function checkCommonConditions():Void + { + if (subject.healthPoints <= 0 && !subject.shotDown) + { + subject.world.score += 1500; + subject.shotDown = true; + subject.unguidedProjectile = true; + subject.collisionCategory = CollisionCategory.PARTICLE; + subject.collisionMask = CollisionCategory.TILE; + updateFunction = goDownInFlames; + } + } + + private function goDownInFlames(dTime:Float):Void + { + if (subject.shotDown && subject.speed.y < 300) + { + subject.speed.y += 200*dTime; + } + } + + override public function respondToDamage():Void + { + super.respondToDamage(); + } + +} \ No newline at end of file diff --git a/src/game/behaviours/BasicBehaviour.hx b/src/game/behaviours/BasicBehaviour.hx new file mode 100644 index 0000000..31362ac --- /dev/null +++ b/src/game/behaviours/BasicBehaviour.hx @@ -0,0 +1,43 @@ +package game.behaviours; + +import game.EnemyShip; +/** + * ... + * @author Dmitriy Barabanschikov + */ +class BasicBehaviour implements IBehaviour +{ + public var updateFunction: Float -> Void; + public var subject:EnemyShip; + + public function new(subject:EnemyShip) + { + this.subject = subject; + updateFunction = init; + } + + public function update(dTime:Float):Void + { + checkCommonConditions(); + if (updateFunction != null) + { + updateFunction(dTime); + } + } + + public function checkCommonConditions():Void + { + + } + + public function init(dTime:Float):Void + { + + } + + public function respondToDamage():Void + { + + } + +} \ No newline at end of file diff --git a/src/game/behaviours/IBehaviour.hx b/src/game/behaviours/IBehaviour.hx new file mode 100644 index 0000000..34a44e2 --- /dev/null +++ b/src/game/behaviours/IBehaviour.hx @@ -0,0 +1,10 @@ +package game.behaviours; + +/** + * ... + * @author Dmitriy Barabanschikov + */ +interface IBehaviour +{ + function update(dTime:Float):Void; +} \ No newline at end of file diff --git a/src/game/behaviours/KamikazeBaloonBehaviour.hx b/src/game/behaviours/KamikazeBaloonBehaviour.hx new file mode 100644 index 0000000..bd7e31c --- /dev/null +++ b/src/game/behaviours/KamikazeBaloonBehaviour.hx @@ -0,0 +1,57 @@ +package game.behaviours; + +import game.CollisionCategory; +import game.EnemyShip; +/** + * ... + * @author Dmitriy Barabanschikov + */ +class KamikazeBaloonBehaviour extends BasicBehaviour +{ + private var counter:Float; + + public function new(subject:EnemyShip) + { + super(subject); + counter = 0; + } + + override public function init(dTime:Float):Void + { + counter = 4 + Std.int(Math.random() * 8); + updateFunction = waitToExplode; + } + + private function waitToExplode(dTime:Float):Void + { + counter -= dTime; + if (counter <= 0) + { + updateFunction = explode; + } + } + + private function explode(dTime:Float):Void + { + for (i in 0...12) + { + subject.shoot(i * Math.PI * 2 / 12, 150); + } + subject.die(); + updateFunction = null; + } + + override public function checkCommonConditions():Void + { + if (subject.healthPoints <= 0 && !subject.shotDown) + { + subject.world.score += 1500; + subject.shotDown = true; + subject.unguidedProjectile = true; + subject.collisionCategory = CollisionCategory.PARTICLE; + subject.collisionMask = CollisionCategory.TILE; + subject.die(); + } + } + +} \ No newline at end of file diff --git a/src/game/behaviours/TeslaBehaviourFrontalAssault.hx b/src/game/behaviours/TeslaBehaviourFrontalAssault.hx new file mode 100644 index 0000000..dce816d --- /dev/null +++ b/src/game/behaviours/TeslaBehaviourFrontalAssault.hx @@ -0,0 +1,41 @@ +package game.behaviours; + +import game.EnemyBullet; +import game.EnemyShip; +/** + * ... + * @author Dmitriy Barabanschikov + */ +class TeslaBehaviourFrontalAssault implements IBehaviour +{ + private var subject:EnemyShip; + private var counter:Float; + private var shootCounter:Int; + + public function new(subject:EnemyShip) + { + this.subject = subject; + counter = 0; + shootCounter = 1; + } + + public function update(dTime:Float):Void + { + counter+=dTime; + if (counter > shootCounter*1.1) + { + subject.shootAtPlayer(); + shootCounter++; + } + if (counter > 3) + { + subject.behaviour = new TeslaBehaviourRetreat(subject); + } + + if (subject.healthPoints < 100) + { + subject.behaviour = new TeslaBehaviourRetreat(subject); + } + } + +} \ No newline at end of file diff --git a/src/game/behaviours/TeslaBehaviourGoDownInFlames.hx b/src/game/behaviours/TeslaBehaviourGoDownInFlames.hx new file mode 100644 index 0000000..d8453c0 --- /dev/null +++ b/src/game/behaviours/TeslaBehaviourGoDownInFlames.hx @@ -0,0 +1,29 @@ +package game.behaviours; + +import game.EnemyShip; +/** + * ... + * @author Dmitriy Barabanschikov + */ +class TeslaBehaviourGoDownInFlames implements IBehaviour +{ + private var subject:EnemyShip; + + public function new(subject:EnemyShip) + { + this.subject = subject; + } + + public function update(dTime:Float):Void + { + if (subject.speed.y < 400) + { + subject.speed.y += 450*dTime; + } + if (subject.rotation > -Math.PI / 4) + { + subject.rotation -= Math.PI / 5*dTime; + } + } + +} \ No newline at end of file diff --git a/src/game/behaviours/TeslaBehaviourRam.hx b/src/game/behaviours/TeslaBehaviourRam.hx new file mode 100644 index 0000000..5430ffc --- /dev/null +++ b/src/game/behaviours/TeslaBehaviourRam.hx @@ -0,0 +1,50 @@ +package game.behaviours; + +import game.EnemyShip; +import game.PlayerShip; +/** + * ... + * @author Dmitriy Barabanschikov + */ +class TeslaBehaviourRam implements IBehaviour +{ + private var subject:EnemyShip; + private var target:PlayerShip; + private var counter:Float; + private var shootCounter:Int; + + public function new(subject:EnemyShip) + { + this.subject = subject; + counter = 0; + } + + public function update(dTime:Float):Void + { + if (target == null) + { + target = subject.world.player; + } + + counter+=dTime; + if (counter > shootCounter) + { + subject.shoot(Math.PI, 400); + shootCounter++; + } + + if (target.y > subject.y + subject.speed.y / 5) + { + subject.speed.y += 180*dTime; + } else + { + subject.speed.y -= 180*dTime; + } + + if (subject.healthPoints < 100) + { + subject.behaviour = new TeslaBehaviourRetreat(subject); + } + } + +} \ No newline at end of file diff --git a/src/game/behaviours/TeslaBehaviourRetreat.hx b/src/game/behaviours/TeslaBehaviourRetreat.hx new file mode 100644 index 0000000..130a037 --- /dev/null +++ b/src/game/behaviours/TeslaBehaviourRetreat.hx @@ -0,0 +1,46 @@ +package game.behaviours; + +import game.CollisionCategory; +import game.EnemyShip; +/** + * ... + * @author Dmitriy Barabanschikov + */ +class TeslaBehaviourRetreat implements IBehaviour +{ + private var subject:EnemyShip; + private var counter:Float; + private var shootCounter:Int; + + public function new(subject:EnemyShip) + { + this.subject = subject; + counter = 0; + } + + public function update(dTime:Float):Void + { + counter+=dTime; + if (counter > shootCounter*2) + { + subject.shootAtPlayer(); + shootCounter++; + } + + if (subject.speed.y > -1200) + { + subject.speed.y -= 300*dTime; + subject.rotation += Math.PI / 10*dTime; + } + if (subject.healthPoints < 0) + { + subject.shotDown = true; + subject.unguidedProjectile = true; + subject.collisionCategory = CollisionCategory.PARTICLE; + subject.collisionMask = CollisionCategory.TILE; + subject.behaviour = new TeslaBehaviourGoDownInFlames(subject); + subject.world.score += 5000; + } + } + +} \ No newline at end of file diff --git a/src/game/ui/GameOverScreen.hx b/src/game/ui/GameOverScreen.hx new file mode 100644 index 0000000..83fd71b --- /dev/null +++ b/src/game/ui/GameOverScreen.hx @@ -0,0 +1,104 @@ +package game.ui; + +#if (!android && !iphone) + +#end +import flash.display.Bitmap; +import flash.display.BitmapData; +import flash.display.Sprite; +import flash.text.TextField; +import flash.text.TextFormat; +import flash.text.TextFormatAlign; +import game.GameWorld; +import motion.Actuate; +import openfl.Assets; + +/** + * ... + * @author Dmitriy Barabanschikov + */ + +class GameOverScreen extends Sprite +{ + + + private var darkScreen:Bitmap; + private var gameOverText:Bitmap; + private var gameWorld:GameWorld; + private var scoreText:TextField; + private var restartText:String; + private var screenWidth:Float; + private var screenHeight:Float; + + public function new(gameWorld:GameWorld, screenWidth:Float, screenHeight:Float) + { + this.screenWidth = screenWidth; + this.screenHeight = screenHeight; + this.gameWorld = gameWorld; + super(); + + #if (android || iphone) + restartText = "Tap anywhere to restart"; + #else + restartText = "Click anywhere to restart"; + #end + + darkScreen = new Bitmap(new BitmapData(1, 1, true, 0xFF000000)); + darkScreen.scaleX = screenWidth; + darkScreen.scaleY = screenHeight; + darkScreen.alpha = 0; + addChild(darkScreen); + + gameOverText = new Bitmap(Assets.getBitmapData("assets/ui/gameOverText.png")); + gameOverText.x = (screenWidth - gameOverText.width) / 2; + gameOverText.y = Std.int(screenHeight/2 - 100); + gameOverText.alpha = 0; + addChild(gameOverText); + + scoreText = new TextField(); + scoreText.alpha = 0; + scoreText.embedFonts = true; + scoreText.selectable = false; + scoreText.width = screenWidth; + scoreText.y = gameOverText.y + 60; + addChild(scoreText); + + var scoreTF:TextFormat = scoreText.defaultTextFormat; + scoreTF.font = FontManager.mainFont.fontName; + scoreTF.bold = true; + scoreTF.size = 24; + scoreTF.align = TextFormatAlign.CENTER; + scoreTF.color = 0xFFFFFF; + scoreText.defaultTextFormat = scoreTF; + + + #if (android || iphone) + darkScreen.alpha = 0.6; + gameOverText.alpha = 1; + scoreText.alpha = 1; + #end + } + + public function show():Void + { + visible = true; + #if (!android && !iphone) + Actuate.tween(darkScreen, 8, { alpha: 0.6 } ); + Actuate.tween(gameOverText, 1, { alpha: 1 } ); + Actuate.tween(scoreText, 3, { alpha: 1 } ); + #end + scoreText.text = "SCORE: " + Std.string(gameWorld.score) + "\n\n" + restartText; + } + + public function hide():Void + { + visible = false; + #if (!android && !iphone) + Actuate.stop(darkScreen, "alpha", false, false); + Actuate.stop(gameOverText, "alpha", false, false); + darkScreen.alpha = 0; + gameOverText.alpha = 0; + #end + } + +} \ No newline at end of file diff --git a/src/sphaeraGravita/collision/AABB.hx b/src/sphaeraGravita/collision/AABB.hx new file mode 100644 index 0000000..f187584 --- /dev/null +++ b/src/sphaeraGravita/collision/AABB.hx @@ -0,0 +1,94 @@ +package sphaeraGravita.collision; +import sphaeraGravita.math.Vector2D; + +/** + * ... + * @author Dimonte + */ +class AABB +{ + private var bottomRight:Vector2D; + private var topLeft:Vector2D; + private var topRight:Vector2D; + private var bottomLeft:Vector2D; + public var lowerBound:Vector2D; + public var upperBound:Vector2D; + + public function new() + { + lowerBound = new Vector2D(); + upperBound = new Vector2D(); + topLeft = new Vector2D(); + topRight = new Vector2D(); + bottomRight = new Vector2D(); + bottomLeft = new Vector2D(); + } + + public function getWidth():Float + { + return upperBound.x - lowerBound.x; + } + + public function getHeight():Float + { + return upperBound.y - lowerBound.y; + } + + public function containsPoint(point:Vector2D):Bool + { + if (point.x < lowerBound.x) + return false; + if (point.x > upperBound.x) + return false; + if (point.y < lowerBound.y) + return false; + if (point.y > upperBound.y) + return false; + return true; + } + + public function getTopLeftCorner():Vector2D + { + topLeft.x = lowerBound.x; + topLeft.y = lowerBound.y; + return topLeft; + } + + public function getTopRightCorner():Vector2D + { + topRight.x = upperBound.x; + topRight.y = lowerBound.y; + return topRight; + } + + public function getBottomLeftCorner():Vector2D + { + bottomLeft.x = lowerBound.x; + bottomLeft.y = upperBound.y; + return bottomLeft; + } + + public function getBottomRightCorner():Vector2D + { + + bottomRight.x = upperBound.x; + bottomRight.y = upperBound.y; + return bottomRight; + } + + static public function asMinMax(minX:Float, minY:Float, maxX:Float, maxY:Float):AABB + { + var aabb:AABB = new AABB(); + aabb.lowerBound.x = minX; + aabb.lowerBound.y = minY; + aabb.upperBound.x = maxX; + aabb.upperBound.y = maxY; + return aabb; + } + + public function toString():String + { + return "minX: " + lowerBound.x + ", minY: " + lowerBound.y + ", width: " + getWidth() + ", height: " + getHeight(); + } + +} \ No newline at end of file diff --git a/src/sphaeraGravita/collision/CollidingEntity.hx b/src/sphaeraGravita/collision/CollidingEntity.hx new file mode 100644 index 0000000..1874ab3 --- /dev/null +++ b/src/sphaeraGravita/collision/CollidingEntity.hx @@ -0,0 +1,56 @@ +package sphaeraGravita.collision; +import game.CollisionCategory; + +/** + * ... + * @author Dmitriy Barabanschikov + */ +class CollidingEntity +{ + public var parent:CollidingEntity; + + public var collisionCategory(get, set):Int; + + var _collisionCategory:Int = CollisionCategory.INHERIT; + + public function get_collisionCategory():Int + { + if (_collisionCategory == CollisionCategory.INHERIT) + { + if (parent != null) + { + return parent.collisionCategory; + } + } + return _collisionCategory; + } + + public function set_collisionCategory(value:Int):Int + { + _collisionCategory = value; + return collisionCategory; + } + + public var collisionMask(get, set):Int; + + var _collisionMask:Int; + + public function get_collisionMask():Int + { + if (_collisionMask == CollisionCategory.INHERIT) + { + if (parent != null) + { + return parent.collisionMask; + } + } + return _collisionMask; + } + + public function set_collisionMask(value:Int):Int + { + _collisionMask = value; + return collisionMask; + } + +} \ No newline at end of file diff --git a/src/sphaeraGravita/collision/CollisionEngine.hx b/src/sphaeraGravita/collision/CollisionEngine.hx new file mode 100644 index 0000000..fa93f73 --- /dev/null +++ b/src/sphaeraGravita/collision/CollisionEngine.hx @@ -0,0 +1,623 @@ +package sphaeraGravita.collision; + +import dataStructures.DLLNode; +import game.CollisionCategory; +import game.GameEntity; +import sphaeraGravita.collision.PhysicalBody; +import sphaeraGravita.math.Vector2D; +import sphaeraGravita.shapes.BaseShape; +import sphaeraGravita.shapes.CircleShape; +import sphaeraGravita.shapes.Polygon; +import sphaeraGravita.shapes.ShapeType; + +/** + * ... + * @author Dimonte + */ +class CollisionEngine +{ + /** + * WARNING! Gets overwritten every time project<...> functions are called. + */ + private var projectionResult:ProjectionResult; + + private var intervalOne:Float; + private var intervalTwo:Float; + private var min:Float; + private var max:Float; + private var projectionPoint:Float; + private var verticeNum:Int; + private var i:Int; + + public function new() + { + projectionResult = new ProjectionResult(); + } + + public function collideBodies(entityOne:PhysicalBody, entityTwo:PhysicalBody):Void + { + + //trace("collideBodies > " + entityOne + " - " + entityTwo); + + if (entityOne.dead || entityTwo.dead) + { + return; + } + if(!matchCollisionCategories(entityOne, entityTwo)) + { + return; + } + if (!queryBoundsIntersection(entityOne, entityTwo)) + { + return; + } + //trace("full cd"); + var collisionResult:CollisionResult; + var bodyOneShape:BaseShape = entityOne.shapeList; + while (bodyOneShape != null) + { + var bodyTwoShape:BaseShape; + bodyTwoShape = entityTwo.shapeList; + while (bodyTwoShape != null) + { + if (bodyOneShape.collisionMask != 0 || bodyOneShape.collisionCategory != CollisionCategory.INHERIT || bodyTwoShape.collisionMask != 0 || bodyTwoShape.collisionCategory != CollisionCategory.INHERIT) + { + if (!matchCollisionCategories(bodyOneShape, bodyTwoShape)) + { + bodyTwoShape = bodyTwoShape.next; + continue; + } + } + + //trace("bodyTwoShape : " + bodyTwoShape); + if (bodyOneShape.sensor || bodyTwoShape.sensor) + { + //trace("huh?"); + if (queryShapes(bodyOneShape, bodyTwoShape)) + { + entityOne.reactToCollision(entityTwo, bodyOneShape, bodyTwoShape); + entityTwo.reactToCollision(entityOne, bodyTwoShape, bodyOneShape); + } + } + else + { + collisionResult = collideShapes(bodyOneShape, bodyTwoShape); + if (collisionResult != null) + { + if (collisionResult.collision) + { + collisionResult.calculateMTDVector(); + var bounceCoefficient:Float = entityOne.bounceFactor * entityTwo.bounceFactor; + entityOne.position.x += collisionResult.mtdVector.x * bounceCoefficient; + entityOne.position.y += collisionResult.mtdVector.y * bounceCoefficient; + var frictionCoefficient:Float = entityOne.frictionFactor * entityTwo.frictionFactor; + var firstReflectingComponent:Vector2D = Vector2D.project(entityOne.speed, collisionResult.mtdAxis).invert().multiply(bounceCoefficient); + var firstFrictionComponent:Vector2D = Vector2D.project(entityOne.speed, collisionResult.mtdAxis.getNormalRight()).multiply(frictionCoefficient); + var speedProjection:Vector2D = Vector2D.project(entityOne.speed, collisionResult.mtdAxis); + if (Vector2D.dotProduct(entityOne.speed, collisionResult.mtdVector) < 0) + { + entityOne.speed = (firstReflectingComponent.add(firstFrictionComponent)); + } + + //TODO entityTwo speed after collision + + entityOne.reactToCollision(entityTwo, bodyOneShape, bodyTwoShape); + entityTwo.reactToCollision(entityOne, bodyTwoShape, bodyOneShape); + } + } + } + bodyTwoShape = bodyTwoShape.next; + } + bodyOneShape = bodyOneShape.next; + } + } + + public function matchCollisionCategories(entityOne:CollidingEntity, entityTwo:CollidingEntity):Bool + { + //trace("matchCollisionCategories > " + entityOne + " - " + entityTwo); + + //trace("entityOne.collisionMask " + entityOne.collisionMask); + //trace("entityTwo.collisionCategory " + entityTwo.collisionCategory); + + if (entityOne.collisionMask != 0 && (entityOne.collisionMask & entityTwo.collisionCategory == 0)) + return false; + + //trace("entityTwo.collisionMask " + entityTwo.collisionMask); + //trace("entityOne.collisionCategory " + entityOne.collisionCategory); + + if (entityTwo.collisionMask != 0 && (entityTwo.collisionMask & entityOne.collisionCategory == 0)) + return false; + + return true; + } + + public function collideShapes(shapeOne:BaseShape, shapeTwo:BaseShape):CollisionResult + { + var shapeCollisionResult:CollisionResult = null; + var tempCollisionResult:CollisionResult = null; + if (shapeOne.type == ShapeType.POLYGON_SHAPE) + { + if (shapeTwo.type == ShapeType.POLYGON_SHAPE) + { + shapeCollisionResult = detectPolygons(cast(shapeOne,Polygon), cast(shapeTwo,Polygon)); + } + else if (shapeTwo.type == ShapeType.CIRCLE_SHAPE) + { + shapeCollisionResult = detectPolyCircle(cast(shapeTwo, CircleShape), cast(shapeOne, Polygon)); + if (shapeCollisionResult != null) + { + shapeCollisionResult.mtdAmount = -shapeCollisionResult.mtdAmount; + } + } + } + else if (shapeOne.type == ShapeType.CIRCLE_SHAPE) + { + if (shapeTwo.type == ShapeType.CIRCLE_SHAPE) + { + shapeCollisionResult = detectCircleCircle(cast(shapeOne,CircleShape), cast(shapeTwo,CircleShape)); + } + else if (shapeTwo.type == ShapeType.POLYGON_SHAPE) + { + shapeCollisionResult = detectPolyCircle(cast(shapeOne,CircleShape), cast(shapeTwo,Polygon)); + } + } + return shapeCollisionResult; + } + + public function queryShapes(shapeOne:BaseShape, shapeTwo:BaseShape):Bool + { + var tempCollisionResult:CollisionResult = null; + if (shapeOne.type == ShapeType.POLYGON_SHAPE) + { + if (shapeTwo.type == ShapeType.POLYGON_SHAPE) + { + tempCollisionResult = detectPolygons(cast(shapeOne,Polygon), cast(shapeTwo,Polygon)); + } + else if (shapeTwo.type == ShapeType.CIRCLE_SHAPE) + { + tempCollisionResult = detectPolyCircle(cast(shapeTwo,CircleShape), cast(shapeOne,Polygon)); + } + } + else if (shapeOne.type == ShapeType.CIRCLE_SHAPE) + { + if (shapeTwo.type == ShapeType.CIRCLE_SHAPE) + { + tempCollisionResult = detectCircleCircle(cast(shapeOne,CircleShape), cast(shapeTwo,CircleShape)); + } + else if (shapeTwo.type == ShapeType.POLYGON_SHAPE) + { + tempCollisionResult = detectPolyCircle(cast(shapeOne,CircleShape), cast(shapeTwo,Polygon)); + } + } + if (tempCollisionResult != null) + { + if (tempCollisionResult.collision) + { + return true; + } + } + return false; + } + + /** + * + * @param bodyOne + * @param bodyTwo + * @return collision result, mtd is for the FIRST body + */ + public function collide(bodyOne:PhysicalBody, bodyTwo:PhysicalBody):CollisionResult + { + var collisionResult:CollisionResult = new CollisionResult(); + collisionResult.mtdAmount = Math.POSITIVE_INFINITY; + var tempCollisionResult:CollisionResult = collisionResult; + var bodyOneShape:BaseShape; + var bodyTwoShape:BaseShape; + if (queryBoundsIntersection(bodyOne, bodyTwo)) + { + bodyOneShape = bodyOne.shapeList; + while (bodyOneShape != null) + { + bodyTwoShape = bodyTwo.shapeList; + while (bodyTwoShape != null) + { + if (bodyOneShape.type == ShapeType.POLYGON_SHAPE) + { + if (bodyTwoShape.type == ShapeType.POLYGON_SHAPE) + { + tempCollisionResult = detectPolygons(cast(bodyOneShape,Polygon), cast(bodyTwoShape,Polygon)); + } + else if (bodyTwoShape.type == ShapeType.CIRCLE_SHAPE) + { + tempCollisionResult = detectPolyCircle(cast(bodyTwoShape,CircleShape), cast(bodyOneShape,Polygon)); + tempCollisionResult.mtdAmount = -tempCollisionResult.mtdAmount; + } + } + else if (bodyOneShape.type == ShapeType.CIRCLE_SHAPE) + { + if (bodyTwoShape.type == ShapeType.CIRCLE_SHAPE) + { + tempCollisionResult = detectCircleCircle(cast(bodyOneShape,CircleShape), cast(bodyTwoShape,CircleShape)); + } + else if (bodyTwoShape.type == ShapeType.POLYGON_SHAPE) + { + tempCollisionResult = detectPolyCircle(cast(bodyOneShape, CircleShape), cast(bodyTwoShape,Polygon)); + } + } + if(tempCollisionResult.collision) + { + if (!collisionResult.collision) + { + collisionResult = tempCollisionResult; + collisionResult.calculateMTDVector(); + } else { + collisionResult.mtdVector.add(tempCollisionResult.calculateMTDVector()); + } + } + bodyTwoShape = bodyTwoShape.next; + } + bodyOneShape = bodyOneShape.next; + } + } + // TODO sensor shapes + // TODO immidiate vs delayed logic response + /*var bodyOneShapeOne:BaseShape = bodyOne.shapeList.head().val as BaseShape; + var bodyTwoShapeOne:BaseShape = bodyTwo.shapeList.head().val as BaseShape; + if ((bodyTwoShapeOne.type == ShapeType.AABB_SHAPE) && (bodyOneShapeOne.type == ShapeType.AABB_SHAPE)) + { + collisionResult = collideAABBToAABB(bodyOneShapeOne as AABB, bodyTwoShapeOne as AABB); + if (collisionResult.collision) + { + //trace("Collision! " + collisionResult.mtdVector.toString()); + bodyOne.position.x += collisionResult.mtdVector.x; + bodyOne.position.y += collisionResult.mtdVector.y; + var firstReflectingComponent:Vector2D = Vector2D.project(bodyOne.speed.clone().invert(), collisionResult.mtdAxis).multiply(0.9); + var firstFrictionComponent:Vector2D = Vector2D.project(bodyOne.speed, collisionResult.mtdAxis.getNormalRight()).multiply(0.8); + bodyOne.speed = (firstReflectingComponent.add(firstFrictionComponent)); + } + }*/ + return collisionResult; + } + + public function queryBoundsIntersection(bodyOne:PhysicalBody, bodyTwo:PhysicalBody):Bool + { + var bodyOneLowerBound:Vector2D = bodyOne.worldAABB.lowerBound; + var bodyOneUpperBound:Vector2D = bodyOne.worldAABB.upperBound; + var bodyTwoLowerBound:Vector2D = bodyTwo.worldAABB.lowerBound; + var bodyTwoUpperBound:Vector2D = bodyTwo.worldAABB.upperBound; + + if (bodyOneLowerBound.x >= bodyTwoUpperBound.x) + { + return false; + } + if (bodyOneLowerBound.y >= bodyTwoUpperBound.y) + { + return false; + } + if (bodyOneUpperBound.x <= bodyTwoLowerBound.x) + { + return false; + } + if (bodyOneUpperBound.y <= bodyTwoLowerBound.y) + { + return false; + } + return true; + } + + private function detectPolygons(firstPoly:Polygon, secondPoly:Polygon):Null + { + var resultMTD:Float = Math.POSITIVE_INFINITY; + var mtdAxis:Vector2D = null; + + var testAxisList:Array = new Array(); + + var firstPolyVertexNum:Int = firstPoly.getNumVertices(); + var secondPolyVertexNum:Int = secondPoly.getNumVertices(); + var totalVerges:Int = firstPolyVertexNum + secondPolyVertexNum; + var firstVerticeSet:Array = []; + var body:PhysicalBody = firstPoly.body; + for (i in 0...firstPoly.getNumVertices()) + { + firstVerticeSet[i] = body.localToWorld(firstPoly.vertices[i]); + testAxisList.push(firstPoly.normals[i].clone().applyXform(body.xform)); + } + + var firstMin:Float = 0; + var firstMax:Float = 0; + var secondMin:Float = 0; + var secondMax:Float = 0; + var intervalOneAbs:Float = 0; + var intervalTwoAbs:Float = 0; + var firstPolyProjection:ProjectionResult; + var secondPolyProjection:ProjectionResult; + + var secondVerticeSet:Array = []; + body = secondPoly.body; + for (i in 0...secondPoly.getNumVertices()) + { + secondVerticeSet[i] = secondPoly.body.localToWorld(secondPoly.vertices[i]); + testAxisList.push(secondPoly.normals[i].clone().applyXform(body.xform)); + } + for (i in 0...totalVerges) + { + var testAxis:Vector2D; + testAxis = testAxisList[i]; + + firstPolyProjection = projectVerticeSetOnAxis(firstVerticeSet, testAxis); + firstMin = firstPolyProjection.min; + firstMax = firstPolyProjection.max; + + secondPolyProjection = projectVerticeSetOnAxis(secondVerticeSet, testAxis); + secondMin = secondPolyProjection.min; + secondMax = secondPolyProjection.max; + + if (secondMin > firstMax) + { + return null; + } + if (firstMin > secondMax) + { + return null; + } + + intervalOne = secondMin - firstMax; + intervalTwo = secondMax - firstMin; + intervalOneAbs = intervalOne < 0 ? -intervalOne:intervalOne; + intervalTwoAbs = intervalTwo < 0 ? -intervalTwo:intervalTwo; + + // use faster math for ABSes or get rid of 'em + if (intervalTwoAbs < intervalOneAbs) + { + intervalOneAbs = intervalTwoAbs; + if (intervalTwo < 0) + { + testAxis = testAxis.clone().invert(); + } + } else { + if (intervalOne < 0) + { + testAxis = testAxis.clone().invert(); + } + } + + if (intervalOneAbs < resultMTD) + { + //trace(interval); + resultMTD = intervalOneAbs; + mtdAxis = testAxis.clone(); + } + } + + var result:CollisionResult = new CollisionResult(); + result.mtdAmount = resultMTD; + result.mtdAxis = mtdAxis; + result.collision = true; + + return result; + } + + public function detectPolyCircle(circle:CircleShape, poly:Polygon):Null + { + + + var resultMTD:Float = Math.POSITIVE_INFINITY; + var mtdAxis:Vector2D = null; + var collision:Bool = true; + + var circlePosition:Vector2D = circle.body.localToWorld(circle.position); + + var totalEdges:Int = poly.getNumVertices(); + + var polyVerticeSet:Array = []; + for (i in 0...totalEdges) + { + polyVerticeSet[i] = poly.body.localToWorld(poly.vertices[i]); + } + + var testAxis:Vector2D; + var circleProjection:ProjectionResult; + var secondPolyProjection:ProjectionResult; + var intervalOneAbs:Float; + var intervalTwoAbs:Float; + + var circleMin:Float = 0; + var circleMax:Float = 0; + var polyMin:Float = 0; + var polyMax:Float = 0; + var intervalOneAbs:Float = 0; + var intervalTwoAbs:Float = 0; + + for (i in 0...totalEdges) + { + //* + testAxis = poly.normals[i].clone().applyXform(poly.body.xform); + /*/ + testAxis = poly.normals[i].clone(); + //*/ + + circleProjection = projectCircleOnAxis(circle, testAxis); + circleMin = circleProjection.min; + circleMax = circleProjection.max; + + secondPolyProjection = projectVerticeSetOnAxis(polyVerticeSet, testAxis); + polyMin = secondPolyProjection.min; + polyMax = secondPolyProjection.max; + + if (polyMin > circleMax) + { + return null; + } + if (circleMin > polyMax) + { + return null; + } + intervalOne = polyMin - circleMax; + intervalTwo = polyMax - circleMin; + intervalOneAbs = intervalOne < 0 ? -intervalOne:intervalOne; + intervalTwoAbs = intervalTwo < 0 ? -intervalTwo:intervalTwo; + // use faster math for ABSes or get rid of 'em + if (intervalTwoAbs < intervalOneAbs) + { + intervalOneAbs = intervalTwoAbs; + if (intervalTwo < 0) + { + testAxis = testAxis.invert(); + } + } else { + if (intervalOne < 0) + { + testAxis = testAxis.invert(); + } + } + + if (intervalOneAbs < resultMTD) + { + resultMTD = intervalOneAbs; + mtdAxis = testAxis; + } + } + for (i in 0...totalEdges) + { + testAxis = circle.body.localToWorld(circle.position).subtract(poly.body.localToWorld(poly.vertices[i])).normalize(); + + circleProjection = projectCircleOnAxis(circle, testAxis); + circleMin = circleProjection.min; + circleMax = circleProjection.max; + secondPolyProjection = projectVerticeSetOnAxis(polyVerticeSet, testAxis); + polyMin = secondPolyProjection.min; + polyMax = secondPolyProjection.max; + + if (polyMin > circleMax) + { + return null; + } + if (circleMin > polyMax) + { + return null; + } + intervalOne = polyMin - circleMax; + intervalTwo = polyMax - circleMin; + intervalOneAbs = intervalOne < 0 ? -intervalOne:intervalOne; + intervalTwoAbs = intervalTwo < 0 ? -intervalTwo:intervalTwo; + // use faster math for ABSes or get rid of 'em + if (intervalTwoAbs < intervalOneAbs) + { + intervalOneAbs = intervalTwoAbs; + if (intervalTwo < 0) + { + testAxis = testAxis.invert(); + } + } else { + if (intervalOne < 0) + { + testAxis = testAxis.invert(); + } + } + + if (intervalOneAbs < resultMTD) + { + resultMTD = intervalOneAbs; + mtdAxis = testAxis; + } + } + + var result:CollisionResult = new CollisionResult(); + result.mtdAmount = resultMTD; + result.mtdAxis = mtdAxis; + result.collision = true; + + return result; + //*/ + } + + private function detectCircleCircle(circleOne:CircleShape, circleTwo:CircleShape):Null + { + var circleOneWorldPosition:Vector2D = circleOne.body.localToWorld(circleOne.position); + var circleTwoWorldPosition:Vector2D = circleTwo.body.localToWorld(circleTwo.position); + var distanceSquared:Float = Math.pow(circleTwoWorldPosition.x - circleOneWorldPosition.x, 2) + Math.pow(circleTwoWorldPosition.y - circleOneWorldPosition.y, 2); + var radiusesSquared:Float = (circleOne.radius + circleTwo.radius)*(circleOne.radius + circleTwo.radius); + if (distanceSquared >= radiusesSquared) + { + return null; + } + var result:CollisionResult = new CollisionResult(); + result.collision = true; + result.mtdAmount = Math.sqrt(radiusesSquared) - Math.sqrt(distanceSquared); + result.mtdAxis = circleOneWorldPosition.clone().subtract(circleTwoWorldPosition).normalize(); + return result; + } + + private function projectVerticeSetOnAxis(verticeSet:Array, axis:Vector2D):ProjectionResult + { + verticeNum = verticeSet.length; + min = Vector2D.dotProduct(verticeSet[0], axis); + max = Vector2D.dotProduct(verticeSet[0], axis); + for (i in 1...verticeNum) + { + projectionPoint = Vector2D.dotProduct(verticeSet[i], axis); + if (projectionPoint < min) + { + min = projectionPoint; + } + if (projectionPoint > max) + { + max = projectionPoint; + } + } + + projectionResult.min = min; + projectionResult.max = max; + + return projectionResult; + } + + private function projectPolygonOnAxis(polygon:Polygon, axis:Vector2D):ProjectionResult + { + var body:PhysicalBody = polygon.body; + var min:Float = Vector2D.dotProduct(body.localToWorld(polygon.vertices[0]), axis); + var max:Float = Vector2D.dotProduct(body.localToWorld(polygon.vertices[0]), axis); + var projectionPoint:Float; + for (i in 1...polygon.getNumVertices()) + { + projectionPoint = Vector2D.dotProduct(body.localToWorld(polygon.vertices[i]), axis); + if (projectionPoint < min) + min = projectionPoint; + if (projectionPoint > max) + max = projectionPoint; + } + + projectionResult.min = min; + projectionResult.max = max; + + return projectionResult; + } + + private function closestPointOnLineSegment(startPoint:Vector2D, segment:Vector2D, point:Vector2D):Vector2D + { + var closestPoint:Vector2D; + + var testDotProduct:Float = Vector2D.dotProduct(segment, point.clone().subtract(startPoint)); + var segmentLengthSquared:Float = segment.getLengthSquared(); + + if (testDotProduct <= 0) + { + closestPoint = startPoint.clone(); + } else if(testDotProduct >= segmentLengthSquared) { + closestPoint = startPoint.clone().add(segment); + } else { + var segmentPart:Float = testDotProduct / segmentLengthSquared; + closestPoint = startPoint.clone().add(segment.clone().multiply(segmentPart)); + } + + return closestPoint; + } + + private function projectCircleOnAxis(circle:CircleShape, axis:Vector2D):ProjectionResult + { + var center:Float = Vector2D.dotProduct(circle.body.localToWorld(circle.position), axis); + projectionResult.min = center - circle.radius; + projectionResult.max = center + circle.radius; + return projectionResult; + } + +} \ No newline at end of file diff --git a/src/sphaeraGravita/collision/CollisionResult.hx b/src/sphaeraGravita/collision/CollisionResult.hx new file mode 100644 index 0000000..86328ef --- /dev/null +++ b/src/sphaeraGravita/collision/CollisionResult.hx @@ -0,0 +1,26 @@ +package sphaeraGravita.collision; + +import sphaeraGravita.math.Vector2D; +/** + * ... + * @author Dimonte + */ +class CollisionResult +{ + public var collision:Bool; + public var mtdAmount:Float; + public var mtdAxis:Vector2D; + public var mtdVector:Vector2D; + + public function new () + { + + } + + public function calculateMTDVector():Vector2D + { + mtdVector = mtdAxis.clone().multiply(mtdAmount); + return mtdVector; + } + +} \ No newline at end of file diff --git a/src/sphaeraGravita/collision/PhysicalBody.hx b/src/sphaeraGravita/collision/PhysicalBody.hx new file mode 100644 index 0000000..9608dae --- /dev/null +++ b/src/sphaeraGravita/collision/PhysicalBody.hx @@ -0,0 +1,207 @@ +package sphaeraGravita.collision; + + +import dataStructures.DLL; +import dataStructures.DLLNode; +import sphaeraGravita.math.RotationMatrix; +import sphaeraGravita.math.Vector2D; +import sphaeraGravita.shapes.BaseShape; +import sphaeraGravita.spacePartitioning.GridCell; +//import sphaeraGravita.shapes.BaseShape; + +/** + * ... + * @author Dimonte + */ +class PhysicalBody extends CollidingEntity +{ + + public var boundingAABB:AABB; + public var xformedBoundingAABB:AABB; + public var worldAABB:AABB; + public var shapeList:BaseShape; + public var colliding:Bool; + public var gridBound:Bool; + public var overlappingCells:Array; + public var fixed:Bool; + public var dead:Bool; + + public var position:Vector2D; + public var xform:RotationMatrix; + public var speed:Vector2D; + + public var bounceFactor:Float; + public var frictionFactor:Float; + + public var x(get,set):Float; + public var y(get,set):Float; + public var rotation(get,set):Float; + + public function new() + { + bounceFactor = 1; + frictionFactor = 1; + position = new Vector2D(); + speed = new Vector2D(); + xform = new RotationMatrix(); + } + + public function clearShapes():Void + { + shapeList = null; + } + + public function addShape(shape:BaseShape):Void + { + shape.parent = shape.body = this; + + if (shapeList != null) { + var shapeNode:BaseShape = shapeList; + while (shapeNode.next != null) { + shapeNode = shapeNode.next; + } + shapeNode.next = shape; + } else { + shapeList = shape; + } + } + + public function init():Void + { + var minX:Float = Math.POSITIVE_INFINITY; + var minY:Float = Math.POSITIVE_INFINITY; + var maxX:Float = Math.NEGATIVE_INFINITY; + var maxY:Float = Math.NEGATIVE_INFINITY; + + var shape:BaseShape = shapeList; + + // if there's no shapes inside, zeroes abound! + if (shape == null) + { + minX = minY = maxX = maxY = 0; + } + + while (shape != null) + { + minX = Math.min(minX, shape.getBoundingAABB().lowerBound.x); + minY = Math.min(minY, shape.getBoundingAABB().lowerBound.y); + maxX = Math.max(maxX, shape.getBoundingAABB().upperBound.x); + maxY = Math.max(maxY, shape.getBoundingAABB().upperBound.y); + shape = shape.next; + } + boundingAABB = AABB.asMinMax(minX, minY, maxX, maxY); + xformedBoundingAABB = AABB.asMinMax(minX, minY, maxX, maxY); + worldAABB = AABB.asMinMax(minX, minY, maxX, maxY); + } + + public function updateWorldCache():Void + { + if (worldAABB == null) + { + //trace(this); + return; + } + worldAABB.lowerBound.x = xformedBoundingAABB.lowerBound.x + position.x; + worldAABB.lowerBound.y = xformedBoundingAABB.lowerBound.y + position.y; + worldAABB.upperBound.x = xformedBoundingAABB.upperBound.x + position.x; + worldAABB.upperBound.y = xformedBoundingAABB.upperBound.y + position.y; + } + + private function recalculateXformedAABB():Void + { + var tl:Vector2D = boundingAABB.getTopLeftCorner().applyXform(xform); + var tr:Vector2D = boundingAABB.getTopRightCorner().applyXform(xform); + var bl:Vector2D = boundingAABB.getBottomLeftCorner().applyXform(xform); + var br:Vector2D = boundingAABB.getBottomRightCorner().applyXform(xform); + var minX:Float = Math.min(Math.min(tl.x, tr.x), Math.min(bl.x, br.x)); + var minY:Float = Math.min(Math.min(tl.y, tr.y), Math.min(bl.y, br.y)); + var maxX:Float = Math.max(Math.max(tl.x, tr.x), Math.max(bl.x, br.x)); + var maxY:Float = Math.max(Math.max(tl.y, tr.y), Math.max(bl.y, br.y)); + xformedBoundingAABB.lowerBound.x = minX; + xformedBoundingAABB.lowerBound.y = minY; + xformedBoundingAABB.upperBound.x = maxX; + xformedBoundingAABB.upperBound.y = maxY; + } + + public function set_x(value:Float):Float + { + position.x = value; + return position.x; + } + + public function get_x():Float + { + return position.x; + } + + public function get_y():Float + { + return position.y; + } + + public function set_y(value:Float):Float + { + position.y = value; + return position.y; + } + + /** + * Gets and sets rotation angle in radians + */ + public function get_rotation():Float + { + return xform.angle; + } + + public function set_rotation(value:Float):Float + { + xform.setAngle(value); + recalculateXformedAABB(); + return xform.angle; + } + + /** + * Does not modify localPoint + * @param localPoint point in local coordinates + * @return point in world coordinates + */ + public function localToWorld(localPoint:Vector2D, t:Bool = false):Vector2D + { + return localPoint.clone().applyXform(xform).add(position); + } + + /** + * Checks if body is in specified bounds + * @param x + * @param y + * @param width + * @param heigth + * @return + */ + public function inBounds(x:Float, y:Float, width:Float, heigth:Float):Bool + { + if (worldAABB.upperBound.x < x) + { + return false; + } + if (worldAABB.upperBound.y < y) + { + return false; + } + if (worldAABB.lowerBound.x > x + width) + { + return false; + } + if (worldAABB.lowerBound.y > y +heigth) + { + return false; + } + return true; + } + + public function reactToCollision(collidingBody:PhysicalBody, selfShape:BaseShape, otherShape:BaseShape):Void + { + + } + +} \ No newline at end of file diff --git a/src/sphaeraGravita/collision/ProjectionResult.hx b/src/sphaeraGravita/collision/ProjectionResult.hx new file mode 100644 index 0000000..3a06f30 --- /dev/null +++ b/src/sphaeraGravita/collision/ProjectionResult.hx @@ -0,0 +1,17 @@ +package sphaeraGravita.collision; + +/** + * ... + * @author Dimonte + */ +class ProjectionResult +{ + public var min:Float; + public var max:Float; + + public function new (min:Float = 0, max:Float = 0) + { + this.min = min; + this.max = max; + } +} \ No newline at end of file diff --git a/src/sphaeraGravita/math/RotationMatrix.hx b/src/sphaeraGravita/math/RotationMatrix.hx new file mode 100644 index 0000000..24e79d1 --- /dev/null +++ b/src/sphaeraGravita/math/RotationMatrix.hx @@ -0,0 +1,33 @@ +package sphaeraGravita.math; + +/** + * ... + * @author Dmitriy Barabanschikov + */ + +class RotationMatrix +{ + + public var sin:Float; + public var cos:Float; + + /** + * Use this variable to read angle information ONLY! Use setAngle to set angle. + */ + public var angle:Float; + + public function new():Void + { + setAngle(0); + } + + public function setAngle(angle:Float):Void + { + this.angle = angle; + sin = Math.sin(angle); + cos = Math.cos(angle); + } + + + +} \ No newline at end of file diff --git a/src/sphaeraGravita/math/Vector2D.hx b/src/sphaeraGravita/math/Vector2D.hx new file mode 100644 index 0000000..cfc8a6b --- /dev/null +++ b/src/sphaeraGravita/math/Vector2D.hx @@ -0,0 +1,117 @@ +package sphaeraGravita.math; + + +/** + * ... + * @author Dimonte + */ +class Vector2D +{ + + public static function dotProduct(vector1:Vector2D, vector2:Vector2D):Float + { + return vector1.x * vector2.x + vector1.y * vector2.y; + } + + public static function project(vector:Vector2D, vectorToProjectOn:Vector2D):Vector2D + { + var vectorToProjectOnNormal:Vector2D = vectorToProjectOn.clone().normalize(); + var dp:Float = dotProduct(vector, vectorToProjectOnNormal); + return new Vector2D(dp * vectorToProjectOnNormal.x, dp * vectorToProjectOnNormal.y); + } + + public var x:Float; + public var y:Float; + + public function new(?x:Float=0, ?y:Float=0) + { + this.x = x; + this.y = y; + } + + public function getLength():Float + { + return Math.sqrt(x * x + y * y); + } + + public function getLengthSquared():Float + { + return (x * x + y * y); + } + + public function add(vector:Vector2D):Vector2D + { + x += vector.x; + y += vector.y; + return this; + } + + public function subtract(vector:Vector2D):Vector2D + { + x -= vector.x; + y -= vector.y; + return this; + } + + public function multiply(multiplier:Float):Vector2D + { + x *= multiplier; + y *= multiplier; + return this; + } + + public function getNormalLeft():Vector2D + { + return new Vector2D( y, -x ); + } + + public function getNormalRight():Vector2D + { + return new Vector2D( -y, x ); + } + + public function invert():Vector2D + { + x = -x; + y = -y; + return this; + } + + public function equals(vector:Vector2D):Bool + { + return (x == vector.x) && (y == vector.y); + } + + public function clone():Vector2D + { + return new Vector2D(x, y); + } + + public function normalize():Vector2D + { + var length:Float = this.getLength(); + if ( length == 0 ) + { + return this; + } else { + x /= length; + y /= length; + } + return this; + } + + public function applyXform(xform:RotationMatrix):Vector2D + { + var newX:Float = x * xform.cos - y * xform.sin; + var newY:Float = x * xform.sin + y * xform.cos; + x = newX; + y = newY; + return this; + } + + public function toString():String + { + return "(" + x + "," + y + ")"; + } + +} \ No newline at end of file diff --git a/src/sphaeraGravita/shapes/BaseShape.hx b/src/sphaeraGravita/shapes/BaseShape.hx new file mode 100644 index 0000000..c14decf --- /dev/null +++ b/src/sphaeraGravita/shapes/BaseShape.hx @@ -0,0 +1,33 @@ +package sphaeraGravita.shapes; + +import flash.errors.IllegalOperationError; +import game.CollisionCategory; +import sphaeraGravita.collision.AABB; +import sphaeraGravita.collision.CollidingEntity; +import sphaeraGravita.collision.PhysicalBody; +import sphaeraGravita.math.Vector2D; +/** + * ... + * @author Dimonte + */ +class BaseShape extends CollidingEntity +{ + public var body:PhysicalBody; + public var previous:BaseShape; + public var next:BaseShape; + public var sensor:Bool; + public var name:String; + public var type:String; + + public function new(type:String) + { + this.type = type; + } + + public function getBoundingAABB():AABB + { + throw new IllegalOperationError("Function must be overridden"); + return new AABB(); + } + +} \ No newline at end of file diff --git a/src/sphaeraGravita/shapes/CircleShape.hx b/src/sphaeraGravita/shapes/CircleShape.hx new file mode 100644 index 0000000..82a800d --- /dev/null +++ b/src/sphaeraGravita/shapes/CircleShape.hx @@ -0,0 +1,28 @@ +package sphaeraGravita.shapes; + +import sphaeraGravita.collision.AABB; +import sphaeraGravita.math.Vector2D; + +/** + * ... + * @author Dimonte + */ +class CircleShape extends BaseShape +{ + public var radius:Float; + public var position:Vector2D; + + public function new(x:Float, y:Float, radius:Float) + { + super(ShapeType.CIRCLE_SHAPE); + position = new Vector2D(x, y); + this.radius = radius; + } + + override public function getBoundingAABB():AABB + { + var boundingAABB:AABB = AABB.asMinMax(position.x - radius, position.y - radius, position.x + radius, position.y + radius); + return boundingAABB; + } + +} \ No newline at end of file diff --git a/src/sphaeraGravita/shapes/Polygon.hx b/src/sphaeraGravita/shapes/Polygon.hx new file mode 100644 index 0000000..0b40751 --- /dev/null +++ b/src/sphaeraGravita/shapes/Polygon.hx @@ -0,0 +1,95 @@ +package sphaeraGravita.shapes; + +import sphaeraGravita.collision.AABB; +import sphaeraGravita.math.Vector2D; +/** + * ... + * @author Dimonte + */ +class Polygon extends BaseShape +{ + public var vertices:Array; + public var normals:Array; + private var _worldVerticesCache:Array; + public var cacheUpdateRequired:Bool; + + public function new() + { + super(ShapeType.POLYGON_SHAPE); + vertices = new Array(); + normals = new Array(); + _worldVerticesCache = new Array(); + } + + public function setPoly(vertices:Array):Void + { + this.vertices = vertices; + initialize(); + } + + public function initialize():Void + { + normals.splice(0, normals.length); + for (i in 0...vertices.length - 1) + { + var edge:Vector2D = vertices[i + 1].clone().subtract(vertices[i]); + normals[i] = edge.getNormalLeft().normalize(); + //trace("vertex " + i, vertices[i]); + //_worldVerticesCache[i] = vertices[i].clone().add(position); + } + normals[vertices.length - 1] = vertices[0].clone().subtract(vertices[vertices.length - 1]).getNormalLeft().normalize(); + } + + public function getWorldVertex(vertexNum:Int):Vector2D + { + if (cacheUpdateRequired) + { + updateWorldVerticesCache(); + } + return _worldVerticesCache[vertexNum]; + } + + private function updateWorldVerticesCache():Void + { + for (i in 0...vertices.length) + { + _worldVerticesCache[i] = body.localToWorld(vertices[i]); + } + } + + public function getNumVertices():Int + { + return vertices.length; + } + + override public function getBoundingAABB():AABB + { + var minX:Float = Math.POSITIVE_INFINITY; + var minY:Float = Math.POSITIVE_INFINITY; + var maxX:Float = Math.NEGATIVE_INFINITY; + var maxY:Float = Math.NEGATIVE_INFINITY; + var vertice:Vector2D; + for (i in 0...vertices.length) + { + vertice = vertices[i]; + minX = Math.min(minX, vertice.x); + minY = Math.min(minY, vertice.y); + maxX = Math.max(maxX, vertice.x); + maxY = Math.max(maxY, vertice.y); + } + + var boundingAABB:AABB = AABB.asMinMax(minX, minY, maxX, maxY); + //trace(boundingAABB); + return boundingAABB; + } + + public function setAsBox(halfWidth:Float, halfHeight:Float):Void + { + vertices[0] = new Vector2D( -halfWidth, -halfHeight); + vertices[1] = new Vector2D( halfWidth, -halfHeight); + vertices[2] = new Vector2D( halfWidth, halfHeight); + vertices[3] = new Vector2D( -halfWidth, halfHeight); + initialize(); + } + +} \ No newline at end of file diff --git a/src/sphaeraGravita/shapes/ShapeType.hx b/src/sphaeraGravita/shapes/ShapeType.hx new file mode 100644 index 0000000..0b6be64 --- /dev/null +++ b/src/sphaeraGravita/shapes/ShapeType.hx @@ -0,0 +1,12 @@ +package sphaeraGravita.shapes; + +/** + * ... + * @author Dimonte + */ +class ShapeType +{ + public static inline var CIRCLE_SHAPE:String = "circleShape"; + public static inline var POLYGON_SHAPE:String = "polygonShape"; + +} \ No newline at end of file diff --git a/src/sphaeraGravita/spacePartitioning/GridCell.hx b/src/sphaeraGravita/spacePartitioning/GridCell.hx new file mode 100644 index 0000000..5940f89 --- /dev/null +++ b/src/sphaeraGravita/spacePartitioning/GridCell.hx @@ -0,0 +1,22 @@ +package sphaeraGravita.spacePartitioning; + +import dataStructures.DLL; +/** + * ... + * @author ... + */ +class GridCell +{ + public var x:Int; + public var y:Int; + public var activeObjectList:DLL; + public var fixedObjectList:DLL; + public var dirty:Bool; + + public function new() + { + activeObjectList = new DLL(); + fixedObjectList = new DLL(); + } + +} \ No newline at end of file diff --git a/src/sphaeraGravita/spacePartitioning/UniformGrid.hx b/src/sphaeraGravita/spacePartitioning/UniformGrid.hx new file mode 100644 index 0000000..532eb20 --- /dev/null +++ b/src/sphaeraGravita/spacePartitioning/UniformGrid.hx @@ -0,0 +1,165 @@ +package sphaeraGravita.spacePartitioning; + +import game.GameEntity; +import game.Tile; +import sphaeraGravita.math.Vector2D; +/** + * ... + * @author Dmitry Barabanschikov + */ +class UniformGrid +{ + public static inline var CELL_SIZE:Int = 40; //TODO change to 40, make literal + + public var cells:Array; + public var dummy:GridCell; + public var dirtyCells:Array; + public var height:Int; + public var width:Int; + public var padding:Int; + public var minX:Int; + public var maxX:Int; + public var paddedWidth:Int; + public var minY:Int; + public var maxY:Int; + public var paddedHeight:Int; + + private var correctedX:Int; + private var correctedY:Int; + + public function new() + { + dummy = new GridCell(); + cells = new Array(); + dirtyCells = new Array(); + } + + /** + * Initializes uniform grid + * @param width + * @param height + * @param padding + */ + public function initialize(width:Int, height:Int, padding:Int):Void + { + dummy = new GridCell(); + dirtyCells = []; + + this.width = width; + this.height = height; + this.padding = padding; + + minX = - padding; + maxX = width + padding; + paddedWidth = width + padding * 2; + minY = - padding; + maxY = height + padding; + paddedHeight = height + padding * 2; + + var totalNumberOfCells:Int = paddedWidth*paddedHeight; + + + for (i in 0...totalNumberOfCells) + { + if (cells[i] == null) + { + cells[i] = new GridCell(); + } + var cell:GridCell = cells[i]; + cell.activeObjectList.clear(); + cell.fixedObjectList.clear(); + cell.x = Std.int(i / paddedHeight) - padding; + cell.y = Std.int(i % paddedHeight) - padding; + } + + while (cells.length > totalNumberOfCells) + { + cells.pop(); + } + } + + /** + * Cleans all cells containing active objects + */ + public function clean():Void + { + for (i in 0...dirtyCells.length) + { + dirtyCells[i].activeObjectList.clear(); + dirtyCells[i].dirty = false; + } + while (dirtyCells.length > 0) + { + dirtyCells.pop(); + } + + dummy.activeObjectList.clear(); + } + + /** + * Returns cell at (x,y), if out of bounds, returns dummy cell (still usable) + * @param x + * @param y + */ + public function getCellAt(x:Int, y:Int):GridCell + { + if (x < minX) { return dummy; } + if (x >= maxX) { return dummy; } + if (y >= maxY) { return dummy; } + if (y < minY) { return dummy; } + correctedX = x + padding; + correctedY = y + padding; + return cells[correctedX * paddedHeight + correctedY]; + } + + public function placeTile(tile:Tile):Void + { + //getCellAt(Int(tile.position.x / CELL_SIZE), Int(tile.position.y / CELL_SIZE)).fixedObjectList.append(tile); + placeFixedObject(tile); + } + + public function placeFixedObject(object:GameEntity):Void + { + var overlappingCells:Array = getCellsBetweenPoints(object.worldAABB.lowerBound, object.worldAABB.upperBound); + object.overlappingCells = overlappingCells; + for (i in 0...overlappingCells.length) + { + overlappingCells[i].fixedObjectList.append(object); + } + } + + public function placeActiveObject(activeObject:GameEntity):Void + { + var overlappingCells:Array = getCellsBetweenPoints(activeObject.worldAABB.lowerBound, activeObject.worldAABB.upperBound); + activeObject.overlappingCells = overlappingCells; + for (i in 0...overlappingCells.length) + { + overlappingCells[i].activeObjectList.append(activeObject); + if (overlappingCells[i].dirty) + { + continue; + } else + { + dirtyCells.push(overlappingCells[i]); + } + } + } + + public function getCellsBetweenPoints(pointOne:Vector2D, pointTwo:Vector2D):Array + { + var startX:Int = Std.int(pointOne.x / CELL_SIZE); + var startY:Int = Std.int(pointOne.y / CELL_SIZE); + var endX:Int = Std.int(pointTwo.x / CELL_SIZE) + 1; + var endY:Int = Std.int(pointTwo.y / CELL_SIZE) + 1; + var cellArray:Array = []; + for (i in startX...endX) + { + for (j in startY...endY) + { + cellArray.push(getCellAt(i, j)); + } + } + return cellArray; + } + +} \ No newline at end of file diff --git a/src/view/PhysicalWorldView.hx b/src/view/PhysicalWorldView.hx new file mode 100644 index 0000000..4ad6f39 --- /dev/null +++ b/src/view/PhysicalWorldView.hx @@ -0,0 +1,156 @@ +package view; + +import assets.GraphicsLibrary; +import dataStructures.DLL; +import dataStructures.DLLNode; +import flash.display.Bitmap; +import flash.display.BitmapData; +import flash.geom.Matrix; +import flash.geom.Point; +import flash.geom.Rectangle; +import game.Bullet; +import game.BulletHit; +import game.Debries; +import game.EnemyShip; +import game.Explosion; +import game.GameEntity; +import game.GameStage; +import game.GameWorld; +import game.PlayerShip; +import game.Tile; +import openfl.Assets; +import sphaeraGravita.collision.AABB; +import sphaeraGravita.collision.CollisionResult; +import sphaeraGravita.collision.PhysicalBody; +import sphaeraGravita.math.Vector2D; +import sphaeraGravita.shapes.BaseShape; +import sphaeraGravita.shapes.CircleShape; +import sphaeraGravita.shapes.Polygon; +import sphaeraGravita.shapes.ShapeType; +import sphaeraGravita.spacePartitioning.GridCell; +import sphaeraGravita.spacePartitioning.UniformGrid; +/** + * ... + * @author Dimonte + */ +class PhysicalWorldView extends WorldView +{ + public var debugDraw:Bool; + + private var _world:GameWorld; + private var _canvas:Bitmap; + private var _skyBD:BitmapData; + private var _worldDrawMarker:Int; + private var screenWidth:Int; + private var screenHeight:Int; + + public function new(gameStage:GameStage, screenWidth:Int, screenHeight:Int) + { + this.screenWidth = screenWidth; + this.screenHeight = screenHeight; + + super(gameStage); + + _skyBD = Assets.getBitmapData("assets/general/sky.png"); + var skyBitmap:Bitmap = new Bitmap(_skyBD); + skyBitmap.scaleX = screenWidth / skyBitmap.width; + skyBitmap.scaleY = screenHeight / skyBitmap.height; + addChild(skyBitmap); + + _world = gameStage.gameWorld; + _canvas = new Bitmap(new BitmapData(screenWidth, screenHeight)); + + //_canvas.alpha = 0.5; + addChild(_canvas); + + createScoreDisplay(); + } + + override public function update():Void + { + super.update(); + + drawStuff(); + } + + public function drawStuff():Void + { + _worldDrawMarker = Std.int(_world.activeZoneStartMarker); + _canvas.bitmapData.lock(); + + _canvas.bitmapData.fillRect(new Rectangle(0, 0, screenWidth, screenHeight), 0x0); + + drawEntityList(_world.tiles); + drawEntityList(_world.playerBullets); + if (!_world.player.dead) + { + drawEntity(_world.player, true); + } + drawEntityList(_world.enemies); + drawEntityList(_world.particles); + drawEntityList(_world.enemyBullets); + + _canvas.bitmapData.fillRect(new Rectangle(0, 0, _world.player.healthPoints * (screenWidth/800), 10), 0xFFFF0000); + + _canvas.bitmapData.unlock(); + } + + private function drawEntityList(list:DLL):Void + { + var node:DLLNode = list._head; + var entity:GameEntity; + while (node != null) + { + entity = cast(node.val, GameEntity); + node = node.next; + if (entity.dead) + { + continue; + } + if (entity.worldAABB.upperBound.x - _worldDrawMarker < 0) + { + continue; + } + if (entity.worldAABB.lowerBound.x - _worldDrawMarker > screenWidth) + { + continue; + } + + drawEntity(entity); + + } + } + + private function drawEntity(ge:GameEntity, ?debug:Bool = false):Void + { + + if (ge == null) + { + //SOSLog.sosTrace( "ge : " + ge ); + return; + } + + if (ge.graphicsAsset == null) + { + //SOSLog.sosTrace( "ge.graphicsAsset : " + ge.graphicsAsset ); + return; + } + + var sr:Rectangle = ge.graphicsAsset.frameRectangles[ge.frameRectNum]; + var dp:Point = new Point(ge.x - _worldDrawMarker, ge.y).add(ge.graphicsAsset.sizeCorrection); + var source:BitmapData = ge.graphicsAsset.spriteSheet; + if (ge.rotation != 0) + { + var sourceClip:BitmapData = new BitmapData(Std.int(sr.width), Std.int(sr.height), true, 0x0); + sourceClip.copyPixels(source, sr, new Point(0,0)); + var m:Matrix = new Matrix(); + m.createBox(1, 1, ge.rotation, dp.x, dp.y); + _canvas.bitmapData.draw(sourceClip, m); + } else + { + _canvas.bitmapData.copyPixels(source, sr, dp, null, null, true); + } + + } + +} \ No newline at end of file diff --git a/src/view/TilesView.hx b/src/view/TilesView.hx new file mode 100644 index 0000000..7b91d05 --- /dev/null +++ b/src/view/TilesView.hx @@ -0,0 +1,151 @@ +package view; + +import assets.GraphicsLibrary; +import dataStructures.DLL; +import dataStructures.DLLNode; +import flash.display.Bitmap; +import flash.display.BitmapData; +import flash.display.Sprite; +import game.Bullet; +import game.BulletHit; +import game.Debries; +import game.EnemyShip; +import game.Explosion; +import game.GameEntity; +import game.GameStage; +import game.GameWorld; +import game.PlayerShip; +import game.Tile; +import openfl.Assets; +import sphaeraGravita.collision.AABB; +import sphaeraGravita.collision.CollisionResult; +import sphaeraGravita.collision.PhysicalBody; +import sphaeraGravita.math.Vector2D; +import sphaeraGravita.shapes.BaseShape; +import sphaeraGravita.shapes.CircleShape; +import sphaeraGravita.shapes.Polygon; +import sphaeraGravita.shapes.ShapeType; +import sphaeraGravita.spacePartitioning.GridCell; +import sphaeraGravita.spacePartitioning.UniformGrid; +/** + * ... + * @author Dimonte + */ +class TilesView extends WorldView +{ + public var debugDraw:Bool; + + private var _world:GameWorld; + private var _skyBD:BitmapData; + private var _worldDrawMarker:Int; + private var tilesCanvas:Sprite; + private var toDraw:Array; + private var healthBitmap:Bitmap; + private var screenWidth:Int; + private var screenHeight:Int; + + public function new(gameStage:GameStage, screenWidth:Int, screenHeight:Int) + { + super(gameStage); + + this.screenWidth = screenWidth; + this.screenHeight = screenHeight; + + _world = gameStage.gameWorld; + + _skyBD = Assets.getBitmapData("assets/general/sky.png"); + var skyBitmap:Bitmap = new Bitmap(_skyBD); + skyBitmap.scaleX = screenWidth / skyBitmap.width; + skyBitmap.scaleY = screenHeight / skyBitmap.height; + addChild(skyBitmap); + + tilesCanvas = new Sprite(); + addChild(tilesCanvas); + + healthBitmap = new Bitmap(new BitmapData(1, 1, false, 0xFFFF0000)); + healthBitmap.scaleY = 10; + addChild(healthBitmap); + + toDraw = new Array(); + + createScoreDisplay(); + } + + override public function update():Void + { + super.update(); + tilesCanvas.graphics.clear(); + drawStuff(); + } + + public function drawStuff():Void + { + _worldDrawMarker = Std.int(_world.activeZoneStartMarker); + + + var toDrawLength:Int = 0; + + toDrawLength = addEntityListTiles(_world.tiles, toDraw, toDrawLength); + toDrawLength = addEntityListTiles(_world.playerBullets, toDraw, toDrawLength); + if (!_world.player.dead) + { + toDrawLength = drawEntity(_world.player, toDraw, toDrawLength); + } + toDrawLength = addEntityListTiles(_world.enemies, toDraw, toDrawLength); + toDrawLength = addEntityListTiles(_world.particles, toDraw, toDrawLength); + toDrawLength = addEntityListTiles(_world.enemyBullets, toDraw, toDrawLength); + + while (toDraw.length > toDrawLength) + { + toDraw.pop(); + } + + + tilesCanvas.graphics.drawTiles(GraphicsLibrary.tilesheet, toDraw); + + //trace( "toDraw : " + toDraw ); + + if (healthBitmap.scaleX != _world.player.healthPoints * (screenWidth/800)) + { + healthBitmap.scaleX = _world.player.healthPoints * (screenWidth/800); + } + } + + private function addEntityListTiles(list:DLL, toDraw:Array, toDrawLength:Int):Int + { + var node:DLLNode = list._head; + var entity:GameEntity; + while (node != null) + { + entity = cast(node.val, GameEntity); + node = node.next; + if (entity.dead) + { + continue; + } + /*if (entity.worldAABB.upperBound.x - _worldDrawMarker < 0) + { + continue; + } + if (entity.worldAABB.lowerBound.x - _worldDrawMarker > 800) + { + continue; + }*/ + + toDrawLength = drawEntity(entity, toDraw, toDrawLength); + } + return toDrawLength; + } + + private function drawEntity(ge:GameEntity, drawList:Array, toDrawLength:Int):Int + { + drawList[toDrawLength] = Std.int(ge.x - _worldDrawMarker + ge.graphicsAsset.sizeCorrection.x); + toDrawLength++; + drawList[toDrawLength] = Std.int(ge.y + ge.graphicsAsset.sizeCorrection.y); + toDrawLength++; + drawList[toDrawLength] = ge.graphicsAsset.tileSheetFrameID[ge.frameRectNum]; + toDrawLength++; + return toDrawLength; + } + +} \ No newline at end of file diff --git a/src/view/WorldView.hx b/src/view/WorldView.hx new file mode 100644 index 0000000..5f97a64 --- /dev/null +++ b/src/view/WorldView.hx @@ -0,0 +1,50 @@ +package view; +import flash.display.Sprite; +import flash.text.TextField; +import flash.text.TextFieldAutoSize; +import flash.text.TextFormat; +import game.GameStage; + +/** + * ... + * @author Dmitriy Barabanschikov + */ + +class WorldView extends Sprite +{ + private var scoreTextField:TextField; + public var gameStage:GameStage; + + public function new(gameStage:GameStage) + { + super(); + this.gameStage = gameStage; + } + + public function update():Void + { + scoreTextField.text = "Score: " + gameStage.gameWorld.score; + } + + public function createScoreDisplay():Void + { + scoreTextField = new TextField(); + scoreTextField.embedFonts = true; + scoreTextField.y = 14; + scoreTextField.x = 8; + scoreTextField.autoSize = TextFieldAutoSize.LEFT; + scoreTextField.mouseEnabled = false; + + var scoreTextFormat:TextFormat = scoreTextField.defaultTextFormat; + + scoreTextFormat.font = FontManager.mainFont.fontName; + scoreTextFormat.size = 16; + scoreTextFormat.color = 0xFFFFFF; + scoreTextFormat.bold = true; + + scoreTextField.defaultTextFormat = scoreTextFormat; + + addChild(scoreTextField); + } + +} \ No newline at end of file