1 /*
2  * Copyright (c) 2007-2013 Scott Lembcke and Howling Moon Software
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22 module demo.Tank;
23 
24 import core.stdc.stdlib;
25 
26 import std.math;
27 
28 alias M_PI_2 = PI_2;
29 
30 import demo.dchip;
31 
32 import demo.ChipmunkDebugDraw;
33 import demo.ChipmunkDemo;
34 import demo.types;
35 
36 cpBody* tankBody;
37 cpBody* tankControlBody;
38 
39 static void update(cpSpace* space, double dt)
40 {
41     // turn the control body_ based on the angle relative to the actual body_
42     cpVect  mouseDelta = cpvsub(ChipmunkDemoMouse, cpBodyGetPos(tankBody));
43     cpFloat turn       = cpvtoangle(cpvunrotate(cpBodyGetRot(tankBody), mouseDelta));
44     cpBodySetAngle(tankControlBody, cpBodyGetAngle(tankBody) - turn);
45 
46     // drive the tank towards the mouse
47     if (cpvnear(ChipmunkDemoMouse, cpBodyGetPos(tankBody), 30.0))
48     {
49         cpBodySetVel(tankControlBody, cpvzero);         // stop
50     }
51     else
52     {
53         cpFloat direction = (cpvdot(mouseDelta, cpBodyGetRot(tankBody)) > 0.0 ? 1.0 : -1.0);
54         cpBodySetVel(tankControlBody, cpvrotate(cpBodyGetRot(tankBody), cpv(30.0f * direction, 0.0f)));
55     }
56 
57     cpSpaceStep(space, dt);
58 }
59 
60 static cpBody* add_box(cpSpace* space, cpFloat size, cpFloat mass)
61 {
62     cpFloat radius = cpvlength(cpv(size, size));
63 
64     cpBody* body_ = cpSpaceAddBody(space, cpBodyNew(mass, cpMomentForBox(mass, size, size)));
65     cpBodySetPos(body_, cpv(frand() * (640 - 2 * radius) - (320 - radius), frand() * (480 - 2 * radius) - (240 - radius)));
66 
67     cpShape* shape = cpSpaceAddShape(space, cpBoxShapeNew(body_, size, size));
68     cpShapeSetElasticity(shape, 0.0f);
69     cpShapeSetFriction(shape, 0.7f);
70 
71     return body_;
72 }
73 
74 static cpSpace* init()
75 {
76     ChipmunkDemoMessageString = "Use the mouse to drive the tank, it will follow the cursor.".dup;
77 
78     cpSpace* space = cpSpaceNew();
79     cpSpaceSetIterations(space, 10);
80     cpSpaceSetSleepTimeThreshold(space, 0.5f);
81 
82     cpBody * staticBody = cpSpaceGetStaticBody(space);
83     cpShape* shape;
84 
85     // Create segments around the edge of the screen.
86     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(-320, 240), 0.0f));
87     cpShapeSetElasticity(shape, 1.0f);
88     cpShapeSetFriction(shape, 1.0f);
89     cpShapeSetLayers(shape, NOT_GRABABLE_MASK);
90 
91     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(320, -240), cpv(320, 240), 0.0f));
92     cpShapeSetElasticity(shape, 1.0f);
93     cpShapeSetFriction(shape, 1.0f);
94     cpShapeSetLayers(shape, NOT_GRABABLE_MASK);
95 
96     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, -240), cpv(320, -240), 0.0f));
97     cpShapeSetElasticity(shape, 1.0f);
98     cpShapeSetFriction(shape, 1.0f);
99     cpShapeSetLayers(shape, NOT_GRABABLE_MASK);
100 
101     shape = cpSpaceAddShape(space, cpSegmentShapeNew(staticBody, cpv(-320, 240), cpv(320, 240), 0.0f));
102     cpShapeSetElasticity(shape, 1.0f);
103     cpShapeSetFriction(shape, 1.0f);
104     cpShapeSetLayers(shape, NOT_GRABABLE_MASK);
105 
106     for (int i = 0; i < 50; i++)
107     {
108         cpBody* body_ = add_box(space, 20, 1);
109 
110         cpConstraint* pivot = cpSpaceAddConstraint(space, cpPivotJointNew2(staticBody, body_, cpvzero, cpvzero));
111         cpConstraintSetMaxBias(pivot, 0);         // disable joint correction
112         cpConstraintSetMaxForce(pivot, 1000.0f);  // emulate linear friction
113 
114         cpConstraint* gear = cpSpaceAddConstraint(space, cpGearJointNew(staticBody, body_, 0.0f, 1.0f));
115         cpConstraintSetMaxBias(gear, 0);         // disable joint correction
116         cpConstraintSetMaxForce(gear, 5000.0f);  // emulate angular friction
117     }
118 
119     // We joint the tank to the control body_ and control the tank indirectly by modifying the control body_.
120     tankControlBody = cpBodyNew(INFINITY, INFINITY);
121     tankBody        = add_box(space, 30, 10);
122 
123     cpConstraint* pivot = cpSpaceAddConstraint(space, cpPivotJointNew2(tankControlBody, tankBody, cpvzero, cpvzero));
124     cpConstraintSetMaxBias(pivot, 0);         // disable joint correction
125     cpConstraintSetMaxForce(pivot, 10000.0f); // emulate linear friction
126 
127     cpConstraint* gear = cpSpaceAddConstraint(space, cpGearJointNew(tankControlBody, tankBody, 0.0f, 1.0f));
128     cpConstraintSetErrorBias(gear, 0);       // attempt to fully correct the joint each step
129     cpConstraintSetMaxBias(gear, 1.2f);      // but limit it's angular correction rate
130     cpConstraintSetMaxForce(gear, 50000.0f); // emulate angular friction
131 
132     return space;
133 }
134 
135 static void destroy(cpSpace* space)
136 {
137     ChipmunkDemoFreeSpaceChildren(space);
138     cpBodyFree(tankControlBody);
139     cpSpaceFree(space);
140 }
141 
142 ChipmunkDemo Tank = {
143     "Tank",
144     1.0 / 60.0,
145     &init,
146     &update,
147     &ChipmunkDemoDefaultDrawImpl,
148     &destroy,
149 };