1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// 1. Module definition 
module asteroid;

//2. Imports 
import 
    arc.scenegraph.all,
    arc.physics.shapes.circle,
    arc.math.routines,
    arc.types,
    arc.texture,
    arc.sound;
        
import 
    spacearea,
    scoreboard,
    laserbolt,
    ship;

///3. A single asteroid definition
class Asteroid : arc.scenegraph.sprite.Sprite
{
public:
    //4. Load asteroid resources 
    static void loadResources()
    {
        // 3 possible asteroid frames to display 
        Asteroid.asteroidFrames[0] = new Frame(Texture("astbin/asteroid1.png"));
        Asteroid.asteroidFrames[1] = new Frame(Texture("astbin/asteroid2.png"));
        Asteroid.asteroidFrames[2] = new Frame(Texture("astbin/asteroid3.png"));        
    }
    
    //5. Create a random asteroid 
    static Asteroid createRandomAsteroid()
    {    
        // new asteroid 
        Asteroid ast = new Asteroid;    
        
        // set x and y locs at random locations 
        ast.physics.translation.x = randomRange(30,320);
        if(randomRange(0,1))
            ast.physics.translation.x += 450;
        
        ast.physics.translation.y = randomRange(30,220);
        if(randomRange(0,1))
            ast.physics.translation.y += 350;
        
        // set random velocities 
        ast.physics.velocity.x = (randomRange(0,1) - 0.5) / 10.;
        ast.physics.velocity.y = (randomRange(0,1) - 0.5) / 10.;
        
        // set random rotation amounts
        ast.physics.rotation = (randomRange(0,1) - 0.5) / 20.;
        
        // add to the scenegraph 
        spacearea.area.addChild(ast);
        return ast;
    }
    
    //6. Create child asteroids 
    static Asteroid[] createChildAsteroids(Asteroid parent)
    {
        Asteroid[] children;

        // put three asteroids in the children
        children ~= [new Asteroid, new Asteroid, new Asteroid];
        
        foreach(ast; children)
        {
            // make the children 75% smaller 
            ast.state = parent.state;
            ast.physics.scale = parent.physics.scale * 0.75;
    
            // have same position, rotation, and velocities as parent 
            ast.physics.translation = parent.physics.translation;
            ast.physics.rotation = parent.physics.rotation;
            ast.physics.velocity = parent.physics.velocity;
            ast.physics.angularVelocity = parent.physics.angularVelocity;
            
            // add to the scenegraph 
            spacearea.area.addChild(ast);
        }
        
        // give the children some offset from the parent location and velocity 
        float offset = randomRange(0,100) * PI / 100;
        for(uint i = 0; i < 3; ++i)
        {
            Point dir = Point(1,0).rotate(offset + i * PI*2 / 3);
            children[i].physics.translation += 15 * children[i].physics.scale.x * dir;
            children[i].physics.velocity += randomRange(2,12) / 100. * dir;
        }
        
        return children;
    }
    
private:
    ///7. if hit by laserbolt, split or explode
    void onCollideStart(Body a, Body b)
    {
        // only react to laser bolts
        if(cast(LaserBolt)b.getParent() !is null)
        {
            // 100 points per asteroid hit
            scoreBoard.addScore(100);
            
            // complimentary sound effects 
            playerShip.explodeSnd.stop();
            playerShip.explodeSnd.play();
            
            // if the asteroid is still large enough to split, split it
            if(a.scale.x > 1)
            {
                createChildAsteroids(this);
                spacearea.area.removeChild(this);
            }
            // otherwise, switch off collision and explode!
            else
            {
                a.passThroughBodies = true;
                a.triggerOtherCollisionSignals = false;
                a.triggerOwnCollisionSignals = false;
                state = explosionAnimation.start();
            }
        }
    }
    
    //8. will create asteroid with given texture and physics body and will create size
    // based on the amount of time that has passed
    this()
    {
        // create new physics body for it
        physics = new Circle(13, 100);
        // connect physics collision function 
        physics.sigCollideStart.connect(&onCollideStart);
        physics.scale = 2;
        physics.friction = 0.1;

        // process arc.scenegraph.sprite.Sprite.this() 
        super(physics, asteroidFrames[randomRange(0,2)]);
                
        // explode animation 
        explosionAnimation = new LinearAnimation(new Frame(Texture("astbin/ship/explosion.png"), Rect(32,32)), 7, 100);
        // call the destroy method when the explosion animation is over
        explosionAnimation.sigEnd.fconnect!(typeof(&destroy))(&destroy);
        
    }
        
    //9. Destroy the asteroid 
    void destroy()
    {
        // by removing it from scenegraph 
        spacearea.area.removeChild(this);
    }

    //10. Asteroid variables
    static Frame[3] asteroidFrames;
    LinearAnimation explosionAnimation;
    Body physics;
}