edge 0.8.20 → 0.9.0.1
raw patch · 8 files changed
+519/−25 lines, 8 filesbinary-added
Files
- AUTHORS +17/−0
- Display.hs +9/−0
- Resources.hs +28/−0
- Step.hs +31/−23
- Unit/Smart/DeathII.hs +226/−0
- edge.cabal +3/−2
- edge.svg +205/−0
- image/deathii.bmp binary
+ AUTHORS view
@@ -0,0 +1,17 @@+Christopher Howard <christopher.howard@frigidcode.com>+http://frigidcode.com++Some sound effects were adapted from the following works:+- Alien Machine Gun+ | License: http://www.creativecommons.org/licenses/by/3.0/+ | Recorded by Matt Cutillo+ | soundbible.com+- Grenade Sound+ | License: http://www.creativecommons.org/licenses/by/3.0/+ | Recorded by Mike Koenig+ | soundbible.com+- Robot Blip Sound+ | License: http://www.creativecommons.org/licenses/by/3.0/+ | Recorded by Marianne Gagnon+ | soundbible.com+
Display.hs view
@@ -88,6 +88,7 @@ , g (Translate (am (-400)) (ap 340) (inventoryDisplay (lance r) (resourceTracker u))) , g (Translate (-200) 150 (levelMessage (levelMessageTimer u) (level u)))+ , h 2.5 (i 20 (Translate (ap (220)) (am (-300)) helpColumn3)) , h 2 (i 20 (Translate (am (-450)) (am (-230)) helpColumn2)) , h 2 (i 20 (Translate (am (-470)) (am (-320)) helpColumn1)) ]@@ -143,6 +144,14 @@ , "U-ARROW, 8 to accelerate" , "TAB, 5 to switch weapons" , "SPACEBAR, ENTER for deflector"+ ]++helpColumn3 =+ Color white $+ Pictures $+ lineFormatting+ [ "Enjoy the game? To donate"+ , "visit <frigidcode.com/donate>." ] lineFormatting ys = lineFormatting' 20 0 ys
Resources.hs view
@@ -17,6 +17,7 @@ import qualified Unit.Smart.ATank as ATank import qualified Unit.Smart.STank as STank import qualified Unit.Smart.Death as Death+import qualified Unit.Smart.DeathII as DeathII import qualified Unit.Smart.Master as Master import qualified Unit.Smart.Ninja as Ninja import qualified Unit.Smart.Saucer as Saucer@@ -33,6 +34,7 @@ , "atank.bmp" , "blade.bmp" , "death.bmp"+ , "deathii.bmp" , "deflector-1.bmp" , "deflector-2.bmp" , "interceptor-1.bmp"@@ -570,6 +572,29 @@ , (-280, 770) ] }+ ,+ -- 15+ a { asteroids = asteroidGen rt wmap+ [ ((-540, -940), (-50, -40))+ , ((1320, -1090), (70, -100))+ ] +++ bigAsteroidGen rt wmap+ [ ((900, 500), (70, 20))+ , ((-400, -1100), (-34, 60))+ ] + , smartUnits = deathGen rt wmap+ [ ((-600, 1300.0), 0.0)+ , ((600, 1300.0), 0.0)+ ] +++ deathGenII rt wmap+ [ ((0, -1400), 0)+ ] +++ masterGen rt wmap+ [ ((-700, 800), 0, 10)+ , ((-1000, 1000), 0, 11)+ , ((1200, -1000), 0, 12)+ ]+ } ] let addItems a = do (it1, it2) <- randomItemPair x1 <- randomRIO (0, 2999)@@ -630,6 +655,9 @@ deathGen rt wmap = map (\(x, y) -> SmartUnit (Death.new rt wmap (wrappoint wmap x) y))++deathGenII rt wmap =+ map (\(x, y) -> SmartUnit (DeathII.new rt wmap (wrappoint wmap x) y)) saucerGen rt wmap = map (\(x, y) -> SmartUnit (Saucer.new rt wmap (wrappoint wmap x) y))
Step.hs view
@@ -86,29 +86,37 @@ && not (skipLevel u) = return (t, u) | level u + 1 >= length (Universe.levels u) =- do hPutStrLn stderr "Out of levels! Game over."- exitSuccess- | otherwise =- do silenceMost (arena u)- let a = Universe.levels u !! (level u + 1)- let l = lance (arena u)- let wmap = Universe.wrapMap a- let u'- = u { arena =- a { lance =- fmap- (\ l ->- l { Lance.center = wrappoint wmap- (0.0, 0.0)- , angle = 0.0- , Lance.velocity = (0.0, 0.0)})- l- }- , level = level u + 1- , skipLevel = False- , levelMessageTimer = Just 0.0- }- return (t, u')+ do case lance (arena u) of+ Nothing -> return (t, u)+ Just l -> f u { arena = (arena u) { lance = Just l+ { godMode = True+ , inventory =+ map (\_ -> True) (inventory l) + , swClock = 0+ } } } 0+ | otherwise = f u (level u + 1)++ where f c b = + do silenceMost (arena c)+ let a = Universe.levels c !! b+ let l = lance (arena c)+ let wmap = Universe.wrapMap a+ let a'+ = c { arena =+ a { lance =+ fmap+ (\ l ->+ l { Lance.center = wrappoint wmap+ (0.0, 0.0)+ , angle = 0.0+ , Lance.velocity = (0.0, 0.0)})+ l+ }+ , level = b+ , skipLevel = False+ , levelMessageTimer = Just 0.0+ }+ return (t, a') {- Here we'll terminateAudio everything in the arena that can receive it, not
+ Unit/Smart/DeathII.hs view
@@ -0,0 +1,226 @@+module Unit.Smart.DeathII ( DeathII(..)+ , new+ ) where++import Data.WrapAround ( WP, WM, vectorRelation )+import Animation+ ( Audible(..), Animation(..), termSndSrc, handSndSrc )+import Graphics.Gloss.Data.Picture ()+import Graphics.Gloss.Data.Color ()+import GHC.Float ()+import Math+ ( vectorDirection, targetingA, subV, moreThanZero, isNan )+import ResourceTracker ( RT, protectedGetImage )+import Updating+ ( Transient(..), Observant(..), InternallyUpdating(..) )+import qualified Moving as M+ ( Moving(..),+ Locatable(..),+ Colliding(..),+ newVelocity,+ newLocation )+import Combat+ ( Projectile(..), Launcher(..), Damaging(..), Damageable(..) )+import qualified Projectile.Interceptor as P.Interceptor ( speed, new )+import qualified Projectile.BulletMII as P.BulletMII ( new )+import AfterEffect ( AfterEffect(AfterEffect) )+import qualified AfterEffect.SimpleExplosion as SimpleExplosion+ ( new )+import Data.Maybe ()+import Universe ( Arena(lance) )+import qualified Universe as U ( Arena(wrapMap) )+import Sound.ALUT ( Source )+import Common ( Velocity, Time, Angle )+++radialVelocity = pi+maxVelocityMag = 400+kamikazeDamage = 150+maxIntegrity = 16+accelerationRate = 400+adjAngleC = pi / 8+shotDelay_sniper = 3.5+shotDelay_spread = 2.5+collisionR = 75++data DeathII = DeathII { angle :: Angle+ , velocity :: Velocity+ , center :: WP+ , wmap :: WM+ , launchTube :: [Projectile]+ , sinceLastShot_sniper :: Time+ , sinceLastShot_spread :: Time+ , integrity :: Double+ , vision :: Maybe Arena+ , rt :: RT+ , queueShotSnd :: Bool+ , shotSndSrc :: Maybe Source+ }++instance Audible DeathII where++ processAudio s a = handSndSrc s+ queueShotSnd+ shotSndSrc+ (\a -> a { queueShotSnd = False })+ rt+ "energy-shot-02.wav"+ (\a b -> a { shotSndSrc = Just b })+ a (wmap s) (M.center)++ terminateAudio s = termSndSrc s shotSndSrc++new a b c d = DeathII { center = c+ , angle = d+ , velocity = (0, 0)+ , wmap = b+ , launchTube = []+ , sinceLastShot_sniper = 0+ , sinceLastShot_spread = 0+ , integrity = maxIntegrity+ , vision = Nothing+ , rt = a+ , queueShotSnd = False+ , shotSndSrc = Nothing+ }++instance Observant DeathII where++ updateVision s a = s { vision = Just a }++updateAngle t s+ = case vision s of+ Nothing -> s+ Just a -> case lance a of+ Nothing -> s+ Just l -> let b = angle s in+ let e = if isNan b then 0.1 else b in+ let m = vectorDirection+ (vectorRelation+ (wmap s)+ (center s)+ (M.center l)) in+ let adj+ | m - e > adjAngleC = radialVelocity * t+ | m - e < (-1) * adjAngleC = (-radialVelocity) * t+ | otherwise = 0 in+ s { angle = angle s + adj }++updateVelocity t s =+ s { velocity = b }+ where c = M.newVelocity+ (velocity s) accelerationRate (angle s) maxVelocityMag t+ b = case vision s of+ Nothing -> velocity s+ Just a ->+ case lance a of+ Nothing -> velocity s+ Just l -> let d = angle s in+ let e = if isNan d then 0.1 else d in+ let m = vectorDirection+ (vectorRelation+ (wmap s)+ (center s)+ (M.center l)) in+ if abs (m - e) <= adjAngleC then c else velocity s++instance Animation DeathII where+ image s _ = protectedGetImage (rt s) "deathii.bmp"++instance M.Locatable DeathII where+ center = center++instance M.Moving DeathII where+ velocity = velocity++instance M.Colliding DeathII where+ collisionRadius _ = collisionR++instance InternallyUpdating DeathII where++ preUpdate s t = (updateFiringInformation t+ . updateVelocity t+ . updateAngle t) s++ postUpdate s t =+ s { center = M.newLocation (wmap s) (center s) (velocity s) t }++updateFiringInformation t s =+ fst ((handleSpreadFiring . handleSniperFiring) (s, t))++handleSniperFiring (self, t) =+ let sinceLastShot_sniper' = sinceLastShot_sniper self + t in+ if sinceLastShot_sniper' >= shotDelay_sniper+ then (self { sinceLastShot_sniper = 0.0+ , launchTube = projectile : launchTube self+ , queueShotSnd = True+ }, t)+ else (self { sinceLastShot_sniper = sinceLastShot_sniper' }, t)+ where projectile = Projectile+ (P.Interceptor.new+ (wmap self)+ (rt self)+ pAngle+ (center self)+ (velocity self))+ pSpeed = P.Interceptor.speed+ pAngle = case vision self of+ Nothing -> angle self+ Just arena ->+ case lance arena of+ Nothing -> angle self+ Just l -> targetingA+ pSpeed+ (vectorRelation+ (U.wrapMap arena)+ (center self)+ (M.center l))+ (subV+ (M.velocity l)+ (M.velocity self))++handleSpreadFiring (self, t) =+ let sinceLastShot_spread' = sinceLastShot_spread self + t in+ if sinceLastShot_spread' >= shotDelay_spread+ then (self { sinceLastShot_spread = 0.0+ , launchTube = projectiles ++ launchTube self+ , queueShotSnd = True+ }, t)+ else (self { sinceLastShot_spread = sinceLastShot_spread' }, t)+ where projectiles = map projectile [ x * pi / 8.0 - pi / 4.0 | x <- [0..7] ]+ projectile x = Projectile+ (P.BulletMII.new+ (wmap self)+ (pAngle + x)+ (center self)+ (velocity self))+ pAngle = case vision self of+ Nothing -> angle self+ Just arena ->+ case lance arena of+ Nothing -> angle self+ Just l -> vectorDirection+ (vectorRelation+ (U.wrapMap arena)+ (center self)+ (M.center l))+++instance Launcher DeathII where++ deployProjectiles s = (launchTube s, s { launchTube = [] })++instance Transient DeathII where++ expired' s = if moreThanZero (integrity s) then Nothing else Just [a]++ where a = AfterEffect+ (SimpleExplosion.new (rt s) (wmap s) (center s) (velocity s))++instance Damageable DeathII where++ inflictDamage s d = s { integrity = max 0 (integrity s - d) }++instance Damaging DeathII where++ damageEnergy _ = kamikazeDamage
edge.cabal view
@@ -8,7 +8,7 @@ -- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for -- standards guiding when and how versions should be incremented. -Version: 0.8.20+Version: 0.9.0.1 -- A short (one-line) description of the package. Synopsis: Top view space combat arcade game@@ -48,7 +48,7 @@ -- Extra files to be distributed with the package, such as examples or -- a README.-Extra-source-files: DONATE+Extra-source-files: DONATE, edge.svg, AUTHORS -- Constraint on the version of Cabal needed to build this package. Cabal-version: >=1.10@@ -111,6 +111,7 @@ , Unit.Smart.ATank , Unit.Smart.STank , Unit.Smart.Death+ , Unit.Smart.DeathII , Unit.Smart.Master , Unit.Smart.Ninja , Unit.Smart.Saucer
+ edge.svg view
@@ -0,0 +1,205 @@+<?xml version="1.0" encoding="UTF-8" standalone="no"?>+<!-- Created with Inkscape (http://www.inkscape.org/) -->++<svg+ xmlns:dc="http://purl.org/dc/elements/1.1/"+ xmlns:cc="http://creativecommons.org/ns#"+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"+ xmlns:svg="http://www.w3.org/2000/svg"+ xmlns="http://www.w3.org/2000/svg"+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"+ width="48"+ height="48"+ id="svg2"+ version="1.1"+ inkscape:version="0.48.3.1 r9886"+ sodipodi:docname="edge.svg"+ inkscape:export-filename="/scratch/cmhoward/haskell-edge/image/turret.png"+ inkscape:export-xdpi="90"+ inkscape:export-ydpi="90">+ <defs+ id="defs4" />+ <sodipodi:namedview+ id="base"+ pagecolor="#ffffff"+ bordercolor="#666666"+ borderopacity="1.0"+ inkscape:pageopacity="0"+ inkscape:pageshadow="2"+ inkscape:zoom="1"+ inkscape:cx="24"+ inkscape:cy="24"+ inkscape:document-units="px"+ inkscape:current-layer="svg2"+ showgrid="false"+ inkscape:snap-grids="true"+ inkscape:window-width="1022"+ inkscape:window-height="766"+ inkscape:window-x="0"+ inkscape:window-y="0"+ inkscape:window-maximized="0">+ <inkscape:grid+ type="xygrid"+ id="grid2985"+ empspacing="4"+ visible="true"+ enabled="true"+ snapvisiblegridlinesonly="true"+ spacingx="1px"+ spacingy="1px"+ originy="-1px"+ originx="-1px"+ dotted="false" />+ </sodipodi:namedview>+ <metadata+ id="metadata7">+ <rdf:RDF>+ <cc:Work+ rdf:about="">+ <dc:format>image/svg+xml</dc:format>+ <dc:type+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />+ <dc:title />+ </cc:Work>+ </rdf:RDF>+ </metadata>+ <g+ inkscape:groupmode="layer"+ id="layer4"+ inkscape:label="bg"+ style="display:inline"+ transform="translate(0,30)"+ sodipodi:insensitive="true">+ <rect+ style="fill:#000000;stroke:none"+ id="rect3835"+ width="48"+ height="48"+ x="0"+ y="-30" />+ </g>+ <g+ inkscape:groupmode="layer"+ id="layer2"+ inkscape:label="outline" />+ <g+ inkscape:label="turret"+ inkscape:groupmode="layer"+ id="layer1"+ transform="translate(0,-1004.3622)"+ sodipodi:insensitive="true">+ <path+ style="fill:#000000;fill-opacity:1;stroke:none"+ d="m 29.095984,1015.3076 -5.414469,12.2301 -12.230051,-5.4145 -0.506025,1.143 12.230051,5.4145 -5.414469,12.23 1.142995,0.5061 5.414469,-12.2301 12.230051,5.4145 0.506025,-1.143 -12.230051,-5.4145 5.414469,-12.23 -1.142995,-0.5061 z"+ id="rect3844"+ inkscape:connector-curvature="0" />+ <g+ id="g3865"+ style="fill:#00ccff"+ transform="matrix(1.8287927,0.80964025,-0.80964025,1.8287927,852.2889,-887.01771)">+ <path+ id="path3757"+ transform="translate(0,1034.3622)"+ d="M 7,1 7,5 C 7,3.8954305 7.8954305,3 9,3 c 1.104569,0 2,0.8954305 2,2 L 11,1 7,1 z"+ style="fill:#00ccff;stroke:none"+ inkscape:connector-curvature="0" />+ <path+ id="path3757-3"+ d="m 7,1051.3622 0,-4 c 0,1.1046 0.89543,2 2,2 1.104569,0 2,-0.8954 2,-2 l 0,4 -4,0 z"+ style="fill:#00ccff;stroke:none"+ inkscape:connector-curvature="0" />+ <path+ transform="translate(0,1034.3622)"+ d="M 11,9 A 2,2 0 1 1 7,9 2,2 0 1 1 11,9 z"+ sodipodi:ry="2"+ sodipodi:rx="2"+ sodipodi:cy="9"+ sodipodi:cx="9"+ id="path2987"+ style="fill:#00ccff;fill-opacity:1;stroke:none"+ sodipodi:type="arc" />+ <path+ id="path3757-3-8-6"+ d="m 1,1045.3622 4,0 c -1.1046,0 -2,-0.8954 -2,-2 0,-1.1046 0.8954,-2 2,-2 l -4,0 0,4 z"+ style="fill:#00ccff;stroke:none"+ inkscape:connector-curvature="0" />+ <path+ id="path3757-3-8"+ d="m 17,1045.3622 -4,0 c 1.1046,0 2,-0.8954 2,-2 0,-1.1046 -0.8954,-2 -2,-2 l 4,0 0,4 z"+ style="fill:#00ccff;stroke:none"+ inkscape:connector-curvature="0" />+ </g>+ <path+ sodipodi:type="arc"+ style="fill:#000000;fill-opacity:1;stroke:none"+ id="path3891"+ sodipodi:cx="17"+ sodipodi:cy="17"+ sodipodi:rx="2"+ sodipodi:ry="2"+ d="m 19,17 a 2,2 0 1 1 -4,0 2,2 0 1 1 4,0 z"+ transform="matrix(1.3715945,0.60723019,-0.60723019,1.3715945,11.005807,994.72218)" />+ <g+ id="g3901"+ style="fill:#000000"+ transform="matrix(0.91439634,0.40482013,-0.40482013,0.91439634,427.59072,74.748853)">+ <rect+ transform="translate(0,1018.3622)"+ y="2"+ x="14"+ height="1"+ width="6"+ id="rect3893"+ style="fill:#000000;fill-opacity:1;stroke:none" />+ <rect+ transform="translate(0,1018.3622)"+ y="14"+ x="31"+ height="6"+ width="1"+ id="rect3895"+ style="fill:#000000;fill-opacity:1;stroke:none" />+ <rect+ transform="translate(0,1018.3622)"+ y="31"+ x="14"+ height="1"+ width="6"+ id="rect3897"+ style="fill:#000000;fill-opacity:1;stroke:none" />+ <rect+ transform="translate(0,1018.3622)"+ y="14"+ x="2"+ height="6"+ width="1"+ id="rect3899"+ style="fill:#000000;fill-opacity:1;stroke:none" />+ </g>+ </g>+ <path+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"+ d="M 3,9 3,3 9,3"+ id="path4015"+ inkscape:connector-curvature="0"+ sodipodi:nodetypes="ccc" />+ <path+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"+ d="M 45,9 45,3 39,3"+ id="path4015-4"+ inkscape:connector-curvature="0"+ sodipodi:nodetypes="ccc" />+ <path+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"+ d="m 3,39 0,6 6,0"+ id="path4015-3"+ inkscape:connector-curvature="0"+ sodipodi:nodetypes="ccc" />+ <path+ style="fill:none;stroke:#ffffff;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"+ d="m 45,39 0,6 -6,0"+ id="path4015-4-4"+ inkscape:connector-curvature="0"+ sodipodi:nodetypes="ccc" />+</svg>
+ image/deathii.bmp view
binary file changed (absent → 91326 bytes)