1 module main;
2 
3 import std.stdio;
4 import Engine.all;
5 import std.conv;
6 import std.random;
7 import Engine.Material;
8 import Engine.UI.all;
9 import std.math;
10 import std.variant;
11 import Engine.math;
12 import Engine.Systems.SimpleSystem;
13 import Engine.Tree.BBTree;
14 
15 Shader shader;
16 Camera camera; 
17 Texture ballTexture;
18 
19 import std.parallelism;
20 import std.datetime;
21 import Engine.util;
22 
23 class CollisionSystem : System {
24 	BBTree tree;
25 
26 	@property override Timing timing() { 
27 		return Timing.Update;
28 	}
29 
30 	
31 	override void start() {
32 		tree = new BBTree(null);
33 	}
34 
35 	override void process() {
36 		tree.ReindexQuery(&checkCollision);
37 	} 
38 
39 	ulong checkCollision(Indexable a,Indexable b, ulong collisionID) {
40 		auto ac = cast(Collider)a;
41 		auto bc = cast(Collider)b;
42 		if (ac.Collide(bc)) {
43 			ac.entity.SendMessage("OnCollision", bc);
44 			bc.entity.SendMessage("OnCollision", ac);
45 		}
46 		return 0;
47 	}
48 
49 	override void onEntityEnter(Entity e) {
50 		foreach(c2 ; e.Components) {
51 			auto c = cast(Component)c2;
52 			auto collider = c.Cast!Collider();
53 			if (collider !is null) {
54 				tree.Insert(collider);
55 			}
56 		}
57 	}
58 
59 	override void onEntityLeave(Entity e) {
60 		
61 	}
62 }
63 
64 
65 struct CollisionData {
66 public:
67 	vec2 point;
68 	vec2 normal;
69 	Entity a;
70 	Entity b;
71 }
72 
73 class Collider : Indexable {
74 	mixin ComponentBase;
75 	enum Type {
76 		Box,
77 		Circle,
78 	}
79 
80 	abstract @property Type type();
81 	rect BB() {
82 		return rect();
83 	}
84 	abstract bool Collide(Collider collider);
85 }
86 
87 class BoxCollider : Collider {
88 	rect bb;
89 	
90 	void Awake() {
91 		bb = rect(transform.scale.x,transform.scale.y);
92 	}
93 
94 	override @property Type type() {
95 		return Type.Box;
96 	}
97 
98 	public override rect BB() {
99 		auto bc = bb;
100 		bc.add(transform.position.xy);
101 		return bc;
102 	}
103 
104 	public override bool Collide(Collider collider) {
105 		if (collider.type() == Type.Box) {
106 			return BB.Intersects(BB(),collider.BB());
107 		} else {
108 			assert(0);
109 		}
110 		//return false;
111 	}
112 }
113 
114 class CircleCollider : Collider {
115 	float radius;
116 
117 	void Update() {
118 		if (transform.scale.x > transform.scale.y)
119 			radius = transform.scale.x/2f;
120 		else 
121 			radius = transform.scale.y/2f;
122 	}
123 
124 	override @property Type type() {
125 		return Type.Circle;
126 	}
127 
128 	public override rect BB() {
129 		auto bc = rect(vec2(-radius,-radius),vec2(radius,radius));
130 		bc.add(transform.position.xy);
131 		return bc;
132 	}
133 
134 	public override bool Collide(Collider collider) {
135 		if (collider.type() == Type.Circle) {
136 			auto c = cast(CircleCollider)collider;
137 			auto dp = (c.transform.position.xy-transform.position.xy);
138 			auto d = ((c.radius+radius)*(c.radius+radius)) - (dp.x*dp.x + dp.y*dp.y);
139 			if (d >= 0) {
140 				return true;
141 			}
142 		} else {
143 			assert(0);
144 		}
145 		return false;
146 	}
147 }
148 
149 class LifeController {
150 	mixin ComponentBase;
151 
152 	Life life;
153 
154 	void Awake() {
155 		life = entity.GetComponent!Life();
156 		writeln(life is null);
157 	}
158 
159 	void Update() {
160 		if (Input.KeyPressDown(Key.MOUSE_BUTTON_1)) {
161 			auto mpos = vec3(Core.camera.MouseWorldPosition(),0);
162 			auto dir = (transform.position - mpos).xy;
163 			dir.normalize();
164 			life.Push(dir.xy, 100f);
165 		}	
166 	}
167 }
168 
169 class Rigidbody  {
170 	mixin ComponentBase;
171 	vec2 velocity = vec2(0,0);
172 
173 	void Update() {
174 		auto pos = transform.position;
175 		auto bounds = camera.bounds();
176 		auto min = bounds.min;
177 		auto max = bounds.max;
178 		if (pos.x < min.x) {
179 			velocity.x = -velocity.x;
180 			pos.x = min.x;
181 		} else if (pos.x > max.x) {
182 			velocity.x = -velocity.x;
183 			pos.x = max.x;
184 		}
185 		if (pos.y < min.y) {
186 			velocity.y = -velocity.y;
187 			pos.y = min.y;
188 		} else if (pos.y > max.y) {
189 			velocity.y = -velocity.y;
190 			pos.y = max.y;
191 		}
192 		transform.position = pos;
193 		transform.position += vec3(velocity,0) * Core.DeltaTime;
194 	}
195 }
196 
197 class Life  {
198 	mixin ComponentBase;
199 	Energy energy;
200 	Rigidbody rigidbody;
201 	Collider collider;
202 
203 	void Awake() {
204 		energy = entity.GetComponent!Energy();
205 		rigidbody = entity.GetComponent!Rigidbody();
206 		collider = entity.GetComponent!Collider();
207 	}
208 
209 	void Push(vec2 dir, float force) {
210 		auto e = energy.energy*0.05f;
211 		energy.AddEnergy(-e);
212 
213 		auto ball = new Entity();
214 		ball.AddComponent!(Sprite)(ballTexture);
215 		auto en = ball.AddComponent!(Energy)(e);
216 		ball.AddComponent!(CircleCollider)();
217 		auto rigid = ball.AddComponent!(Rigidbody)();
218 		ball.transform.position = transform.position + ((energy.Size()/2f)+en.Size()/2f) * vec3(-dir,0);
219 		ball.sprite.color = vec4(1,0,0,1);
220 		ball.AddComponent!(Life)();
221 		Core.AddEntity(ball);
222 
223 		rigid.velocity = -dir * force;
224 		rigidbody.velocity += dir * (force/10f);
225 		
226 	}
227 
228 	void OnCollision(Collider c) {
229 		auto e = c.entity.GetComponent!Energy();
230 		if (e) {
231 			if (energy.Size() > e.Size()) {
232 				if (e.energy > 0) {
233 					auto cc = cast(CircleCollider)c;
234 					auto cc2 = cast(CircleCollider)collider;
235 
236 					//Calculate circle collision distance
237 					auto dp = (cc.transform.position.xy-transform.position.xy);
238 					auto d = ((cc.radius+cc2.radius)*(cc.radius+cc2.radius)) - (dp.x*dp.x + dp.y*dp.y);
239 					auto amount = sqrt(d) * Core.DeltaTime * 5f;
240 					
241 					//move the target circle
242 					c.transform.position += vec3(dp.normalized*amount,0);
243 
244 					//exchange sizes
245 					e.AddSize(-amount);
246 					energy.AddSize(amount);
247 				}
248 			}
249 		}
250 	}
251 }
252 
253 class Energy  {
254 	mixin ComponentBase;
255 	float energy;
256 	float sizeRatio = 1;
257 
258 	this(float energy, float ratio = 1.5f) {
259 		this.energy = energy;
260 		this.sizeRatio = ratio;
261 	}
262 
263 	void Awake() {
264 		SetEnergy(energy);
265 	}
266 
267 	void AddSize(float size) {
268 		AddEnergy(size / sizeRatio);
269 	}
270 
271 	void SetSize(float size) {
272 		SetEnergy(size / sizeRatio);
273 	}
274 
275 	void AddEnergy(float energy) {
276 		SetEnergy(this.energy + energy);
277 	}
278 
279 	void SetEnergy(float energy) {
280 		this.energy = energy;
281 		if (this.energy <= 0) {
282 			this.energy = 0;
283 			this.entity.Destory();
284 		}
285 		auto size = Size();
286 		transform.scale = vec3(size,size,size);
287 	}
288 
289 	void SetSizeRatio(float sizeRatio) {
290 		this.sizeRatio = sizeRatio;
291 		auto size = Size();
292 		transform.scale = vec3(size,size,size);
293 	}
294 	
295 	float Size() {
296 		return energy * sizeRatio;
297 	}
298 }
299 
300 struct InputHandle  {
301 	void Update() {
302 		if (Input.MouseScroll().y > 0)  {
303 			Core.camera.size += 3*Core.DeltaTime;
304 			Core.camera.UpdateResolution();
305 		}	
306 		if (Input.MouseScroll().y < 0) {
307 			Core.camera.size -= 3*Core.DeltaTime;
308 			Core.camera.UpdateResolution();
309 		}
310 	}
311 }
312 
313 void main(string[] args) {
314 	try {
315 		run();
316 	}
317 	catch (Exception e) {
318 		writeln(e.msg);
319 		scanf("\n");
320 	}
321 }
322 
323 void run() {
324 	Core.Start();
325 
326 	Font t = new Font("./public/arial.ttf\0", 32, Font.ASCII);
327 	ballTexture = new Texture("./public/sprite2.png\0");
328 	//ballTexture.SetFiltering(GL_LINEAR,GL_LINEAR);
329 
330 	//Core.AddSystem(new GravitySystem());
331 	Core.AddSystem(new CollisionSystem());
332 
333 	auto mmouse = new Entity();
334 	//mmouse.AddComponent!(Sprite)(ballTexture);
335 	mmouse.transform.scale = vec3(100, 100, 1);
336 	
337 	float entities = 20;
338 	float m = sqrt(entities/(Core.width*Core.height));
339 	for (int x=0;x<Core.width*m;x++) {
340 		for (int y=0;y<Core.height*m;y++) {
341 			auto ship = new Entity();
342 			ship.AddComponent!(Sprite)(ballTexture);
343 			ship.AddComponent!(CircleCollider)();
344 			ship.AddComponent!(Life)();
345 			ship.name = to!string(x) ~ " " ~ to!string(y);
346 			ship.transform.position = vec3(x/m,y/m,0);
347 			ship.AddComponent!(Energy)(20);
348 			Core.AddEntity(ship);
349 		}
350 	}
351 
352 	auto cam = new Entity();
353 	camera = cam.AddComponent(new Camera());
354 	cam.AddComponent(new InputHandle());
355 	Core.AddEntity(cam);
356 	Core.camera = cam.GetComponent!Camera();
357 
358 	cam.transform.position = vec3(Core.width/2,+Core.height/2,0);
359 
360 	auto player = new Entity();
361 	player.AddComponent!(Sprite)(ballTexture);
362 	auto energy = player.AddComponent!(Energy)(60);
363 	player.AddComponent!(Life)();
364 	player.AddComponent!(LifeController)();
365 	player.AddComponent!(CircleCollider)();
366 	player.AddComponent!(Rigidbody)();
367 	player.name = "Player";
368 
369 	player.transform.position = camera.transform.position;
370 	player.sprite.color = vec4(1,0,0,1);
371 	Core.AddEntity(player);
372 	
373 	auto e3 = new Entity();
374 	e3.transform.scale.x = 32;
375 	e3.transform.scale.y = 32;
376 	e3.transform.position = vec3(100,Core.height-50,0);
377 	auto fps = e3.AddComponent!Label(t,"FPS");
378 	Core.AddEntity(e3);	
379 
380 	StartCoroutine( {
381 		float time = 0;
382 		int frames = 0;
383 		while (true) {
384 			time += Core.DeltaTime;
385 			frames++;
386 			if (time >= 1) {
387 				fps.SetText("FPS: " ~ to!string(frames));
388 				writeln(to!string(frames));
389 				time -= 1;
390 				frames = 0;
391 			}	
392 			auto camRect = Core.camera.bounds();
393 			fps.entity.transform.position = vec3((camRect.max.x+camRect.min.x) / 2, camRect.max.y - 50*Core.camera.size,0);
394 			fps.entity.transform.scale = vec3(1,1,1) * Core.camera.size * 34;
395 			mmouse.transform.position = vec3(Core.camera.MouseWorldPosition(),0);
396 
397 			Coroutine.yield();
398 		}
399 	});
400 
401 	Core.Run();
402 }