1 /******************************************************************************
2 * irrlamb - https://github.com/jazztickets/irrlamb
3 * Copyright (C) 2019  Alan Witkowski
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 *******************************************************************************/
18 #include <level.h>
19 #include <globals.h>
20 #include <framework.h>
21 #include <objectmanager.h>
22 #include <scripting.h>
23 #include <graphics.h>
24 #include <save.h>
25 #include <replay.h>
26 #include <log.h>
27 #include <physics.h>
28 #include <input.h>
29 #include <audio.h>
30 #include <config.h>
31 #include <objects/template.h>
32 #include <objects/player.h>
33 #include <objects/plane.h>
34 #include <objects/sphere.h>
35 #include <objects/box.h>
36 #include <objects/orb.h>
37 #include <objects/cylinder.h>
38 #include <objects/terrain.h>
39 #include <objects/zone.h>
40 #include <objects/trimesh.h>
41 #include <objects/constraint.h>
42 #include <tinyxml2.h>
43 #include <ISceneManager.h>
44 #include <IFileSystem.h>
45 
46 _Level Level;
47 
48 using namespace irr;
49 using namespace tinyxml2;
50 
51 // Handle user data from .irr file
OnReadUserData(irr::scene::ISceneNode * ForSceneNode,irr::io::IAttributes * UserData)52 void _UserDataLoader::OnReadUserData(irr::scene::ISceneNode *ForSceneNode, irr::io::IAttributes *UserData) {
53 
54 	for(uint32_t i = 0; i < UserData->getAttributeCount(); i++) {
55 		core::stringc Name(UserData->getAttributeName(i));
56 
57 		if(Name == "BackgroundColor") {
58 			video::SColorf FloatColor(UserData->getAttributeAsColorf(i));
59 			Level.ClearColor.set(255, (uint32_t)(255 * FloatColor.r), (uint32_t)(255 * FloatColor.g), (uint32_t)(255 * FloatColor.b));
60 		}
61 	}
62 
63 	return;
64 }
65 
66 // Loads a level file
Init(const std::string & LevelName,bool HeaderOnly)67 int _Level::Init(const std::string &LevelName, bool HeaderOnly) {
68 	if(!HeaderOnly)
69 		Log.Write("Loading level: %s", LevelName.c_str());
70 
71 	// Get fastest time
72 	FastestTime = 0.0f;
73 	const _LevelStat &Stats = Save.LevelStats[LevelName];
74 	if(Stats.HighScores.size() > 0)
75 		FastestTime = Stats.HighScores[0].Time;
76 
77 	// Get paths
78 	this->LevelName = LevelName;
79 	LevelNiceName = "";
80 	std::string LevelFile = LevelName + "/" + LevelName + ".xml";
81 	std::string FilePath = Framework.GetWorkingPath() + std::string("levels/") + LevelFile;
82 	std::string CustomFilePath = Save.CustomLevelsPath + LevelFile;
83 	CustomDataPath = Framework.GetWorkingPath() + std::string("levels/") + LevelName + "/";
84 
85 	// See if custom level exists first
86 	IsCustomLevel = false;
87 	std::ifstream CustomLevelExists(CustomFilePath.c_str());
88 	if(CustomLevelExists) {
89 		IsCustomLevel = true;
90 		FilePath = CustomFilePath;
91 		CustomDataPath = Save.CustomLevelsPath + LevelName + "/";
92 	}
93 	CustomLevelExists.close();
94 
95 	// Open the XML file
96 	XMLDocument Document;
97 	if(Document.LoadFile(FilePath.c_str()) != XML_SUCCESS) {
98 		Log.Write("Error loading level file with error id = %d", Document.ErrorID());
99 		Log.Write("Error string: %s", Document.ErrorStr());
100 		Close();
101 		return 0;
102 	}
103 
104 	// Check for level tag
105 	XMLElement *LevelElement = Document.FirstChildElement("level");
106 	if(!LevelElement) {
107 		Log.Write("Could not find level tag");
108 		Close();
109 		return 0;
110 	}
111 
112 	// Level version
113 	if(LevelElement->QueryIntAttribute("version", &LevelVersion) == XML_NO_ATTRIBUTE) {
114 		Log.Write("Could not find level version");
115 		Close();
116 		return 0;
117 	}
118 
119 	// Check required game version
120 	GameVersion = LevelElement->Attribute("gameversion");
121 	if(GameVersion == "") {
122 		Log.Write("Could not find game version attribute");
123 		Close();
124 		return 0;
125 	}
126 
127 	// Load level info
128 	XMLElement *InfoElement = LevelElement->FirstChildElement("info");
129 	if(InfoElement) {
130 		XMLElement *NiceNameElement = InfoElement->FirstChildElement("name");
131 		if(NiceNameElement) {
132 			LevelNiceName = NiceNameElement->GetText();
133 		}
134 	}
135 
136 	// Return after header is read
137 	if(HeaderOnly) {
138 		Close();
139 		return true;
140 	}
141 
142 	// Load default lua script
143 	Scripts.clear();
144 	Scripts.push_back(Framework.GetWorkingPath() + "scripts/default.lua");
145 
146 	// Options
147 	bool Fog = false;
148 	bool EmitLight = false;
149 	Level.ClearColor.set(255, 0, 0, 0);
150 	irrScene->setAmbientLight(video::SColorf(0.3, 0.3, 0.3, 1));
151 	irrDriver->setFog(video::SColor(0), irr::video::EFT_FOG_EXP, 0, 0, 0);
152 
153 	// Load options
154 	XMLElement *OptionsElement = LevelElement->FirstChildElement("options");
155 	if(OptionsElement) {
156 
157 		// Use lights from world/player
158 		XMLElement *EmitLightElement = OptionsElement->FirstChildElement("emitlight");
159 		if(EmitLightElement) {
160 			EmitLightElement->QueryBoolAttribute("enabled", &EmitLight);
161 		}
162 	}
163 
164 	// Load world
165 	XMLElement *ResourcesElement = LevelElement->FirstChildElement("resources");
166 	if(ResourcesElement) {
167 
168 		// Load scenes
169 		for(XMLElement *SceneElement = ResourcesElement->FirstChildElement("scene"); SceneElement != 0; SceneElement = SceneElement->NextSiblingElement("scene")) {
170 
171 			// Get file
172 			std::string File = SceneElement->Attribute("file");
173 			if(File == "") {
174 				Log.Write("Could not find file attribute on scene");
175 				Close();
176 				return 0;
177 			}
178 
179 			// Reset fog
180 			irrDriver->setFog(video::SColor(0, 0, 0, 0), video::EFT_FOG_EXP, 0, 0, 0.0f);
181 
182 			// Load scene
183 			if(IsCustomLevel) {
184 				irrFile->changeWorkingDirectoryTo(CustomDataPath.c_str());
185 				irrScene->loadScene(File.c_str(), &UserDataLoader);
186 				irrFile->changeWorkingDirectoryTo(Framework.GetWorkingPath().c_str());
187 			}
188 			else {
189 				irrScene->loadScene((CustomDataPath + File).c_str(), &UserDataLoader);
190 			}
191 
192 			// Set texture filters on meshes in the scene
193 			core::array<irr::scene::ISceneNode *> MeshNodes;
194 			irrScene->getSceneNodesFromType(scene::ESNT_MESH, MeshNodes);
195 			for(uint32_t i = 0; i < MeshNodes.size(); i++) {
196 				if(EmitLight && Config.Shaders) {
197 					video::SMaterial &Material = MeshNodes[i]->getMaterial(0);
198 					int ShaderType = 0;
199 					if(Material.MaterialType == video::EMT_TRANSPARENT_ALPHA_CHANNEL) {
200 						ShaderType = 1;
201 					}
202 					MeshNodes[i]->setMaterialType((video::E_MATERIAL_TYPE)Graphics.GetCustomMaterial(ShaderType));
203 				}
204 
205 				//MeshNodes[i]->setMaterialFlag(video::EMF_WIREFRAME, true);
206 				MeshNodes[i]->setMaterialFlag(video::EMF_TRILINEAR_FILTER, Config.TrilinearFiltering);
207 				for(uint32_t j = 0; j < MeshNodes[i]->getMaterialCount(); j++) {
208 					for(int k = 0; k < 4; k++) {
209 						MeshNodes[i]->getMaterial(j).TextureLayer[k].AnisotropicFilter = Config.AnisotropicFiltering;
210 						if(MeshNodes[i]->getMaterial(j).FogEnable)
211 							Fog = true;
212 					}
213 				}
214 			}
215 		}
216 
217 		// Load collision
218 		for(XMLElement *CollisionElement = ResourcesElement->FirstChildElement("collision"); CollisionElement != 0; CollisionElement = CollisionElement->NextSiblingElement("collision")) {
219 
220 			// Get file
221 			std::string File = CollisionElement->Attribute("file");
222 			if(File == "") {
223 				Log.Write("Could not find file attribute on collision");
224 				Close();
225 				return 0;
226 			}
227 
228 			// Create template
229 			_Template *Template = new _Template;
230 			Template->CollisionFile = CustomDataPath + File;
231 			Template->Type = _Object::COLLISION;
232 			Template->CollisionGroup = _Physics::FILTER_STATIC | _Physics::FILTER_CAMERA;
233 			Template->CollisionMask = _Physics::FILTER_RIGIDBODY;
234 			Template->Mass = 0.0f;
235 			Templates.push_back(Template);
236 
237 			// Get collision friction
238 			CollisionElement->QueryFloatAttribute("friction", &Template->Friction);
239 
240 			// Create spawn
241 			_ObjectSpawn *ObjectSpawn = new _ObjectSpawn;
242 			ObjectSpawn->Template = Template;
243 			const char *AttributeName;
244 			if((AttributeName = CollisionElement->Attribute("name")))
245 				ObjectSpawn->Name = AttributeName;
246 			ObjectSpawns.push_back(ObjectSpawn);
247 		}
248 
249 		// Load scripts
250 		for(XMLElement *ScriptElement = ResourcesElement->FirstChildElement("script"); ScriptElement != 0; ScriptElement = ScriptElement->NextSiblingElement("script")) {
251 
252 			// Get file
253 			std::string File = ScriptElement->Attribute("file");
254 			if(File == "") {
255 				Log.Write("Could not find file attribute on script");
256 				Close();
257 				return 0;
258 			}
259 
260 			Scripts.push_back(CustomDataPath + File);
261 		}
262 
263 		// Load sounds
264 		Sounds.clear();
265 		for(XMLElement *SoundElement = ResourcesElement->FirstChildElement("sound"); SoundElement != 0; SoundElement = SoundElement->NextSiblingElement("sound")) {
266 
267 			// Get file
268 			std::string File = SoundElement->Attribute("file");
269 			if(File == "") {
270 				Log.Write("Could not find file attribute on sound");
271 				Close();
272 				return 0;
273 			}
274 
275 			// Attempt to load sound
276 			if(Audio.LoadBuffer(File))
277 				Sounds.push_back(File);
278 		}
279 	}
280 
281 	// Load templates
282 	XMLElement *TemplatesElement = LevelElement->FirstChildElement("templates");
283 	int TemplateID = 0;
284 	if(TemplatesElement) {
285 		for(XMLElement *TemplateElement = TemplatesElement->FirstChildElement(); TemplateElement != 0; TemplateElement = TemplateElement->NextSiblingElement()) {
286 
287 			// Create a template
288 			_Template *Template = new _Template;
289 			Template->Fog = Fog;
290 
291 			// Get the template properties
292 			if(!GetTemplateProperties(TemplateElement, *Template))
293 				return 0;
294 
295 			// Assign options
296 			Template->TemplateID = TemplateID;
297 			if(EmitLight) {
298 
299 				// Use shaders on materials that receive light
300 				if(Config.Shaders)
301 					Template->CustomMaterial = Graphics.GetCustomMaterial(0);
302 
303 				// Set the player to emit light
304 				if(Template->Type == _Object::PLAYER || Template->Type == _Object::ORB)
305 					Template->EmitLight = true;
306 			}
307 			TemplateID++;
308 
309 			// Store for later
310 			Templates.push_back(Template);
311 		}
312 	}
313 
314 	// Create zones from 'empty' node types
315 	core::array<irr::scene::ISceneNode *> MeshNodes;
316 	irrScene->getSceneNodesFromType(scene::ESNT_EMPTY, MeshNodes);
317 	for(uint32_t i = 0; i < MeshNodes.size(); i++) {
318 
319 		// Create zone template
320 		_Template *Template = new _Template;
321 		Template->Name = MeshNodes[i]->getName();
322 		Template->TemplateID = TemplateID++;
323 		Template->Type = _Object::ZONE;
324 		Template->Mass = 0.0f;
325 		Template->CollisionGroup = _Physics::FILTER_ZONE;
326 		Template->CollisionMask = _Physics::FILTER_RIGIDBODY;
327 		Template->Shape[0] = std::abs(MeshNodes[i]->getScale().X * 2);
328 		Template->Shape[1] = std::abs(MeshNodes[i]->getScale().Y * 2);
329 		Template->Shape[2] = std::abs(MeshNodes[i]->getScale().Z * 2);
330 		Templates.push_back(Template);
331 
332 		// Create object spawn
333 		_ObjectSpawn *ObjectSpawn = new _ObjectSpawn;
334 		ObjectSpawn->Name = MeshNodes[i]->getName();
335 		ObjectSpawn->Position[0] = MeshNodes[i]->getPosition().X;
336 		ObjectSpawn->Position[1] = MeshNodes[i]->getPosition().Y;
337 		ObjectSpawn->Position[2] = MeshNodes[i]->getPosition().Z;
338 		ObjectSpawn->Template = Template;
339 		ObjectSpawns.push_back(ObjectSpawn);
340 	}
341 
342 	// Load object spawns
343 	XMLElement *ObjectsElement = LevelElement->FirstChildElement("objects");
344 	if(ObjectsElement) {
345 		for(XMLElement *ObjectElement = ObjectsElement->FirstChildElement(); ObjectElement != 0; ObjectElement = ObjectElement->NextSiblingElement()) {
346 
347 			// Create an object spawn
348 			_ObjectSpawn *ObjectSpawn = new _ObjectSpawn;
349 
350 			// Get the object properties
351 			if(!GetObjectSpawnProperties(ObjectElement, *ObjectSpawn))
352 				return 0;
353 
354 			// Store for later
355 			ObjectSpawns.push_back(ObjectSpawn);
356 		}
357 	}
358 
359 	// Load constraint spawns
360 	XMLElement *ConstraintsElement = LevelElement->FirstChildElement("constraints");
361 	if(ConstraintsElement) {
362 		for(XMLElement *ConstraintElement = ConstraintsElement->FirstChildElement(); ConstraintElement != 0; ConstraintElement = ConstraintElement->NextSiblingElement()) {
363 
364 			// Create a constraint spawn
365 			_ConstraintSpawn *ConstraintSpawn = new _ConstraintSpawn;
366 
367 			// Get the constraint properties
368 			if(!GetConstraintSpawnProperties(ConstraintElement, *ConstraintSpawn))
369 				return 0;
370 
371 			// Store for later
372 			ConstraintSpawns.push_back(ConstraintSpawn);
373 		}
374 	}
375 
376 	return 1;
377 }
378 
379 // Closes the level
Close()380 int _Level::Close() {
381 
382 	// Clear scripts
383 	Scripts.clear();
384 
385 	// Clear sounds
386 	for(size_t i = 0; i < Sounds.size(); i++)
387 		Audio.CloseBuffer(Sounds[i]);
388 
389 	Sounds.clear();
390 
391 	// Delete templates
392 	for(size_t i = 0; i < Templates.size(); i++)
393 		delete Templates[i];
394 
395 	Templates.clear();
396 
397 	// Delete object spawn data
398 	for(size_t i = 0; i < ObjectSpawns.size(); i++)
399 		delete ObjectSpawns[i];
400 
401 	ObjectSpawns.clear();
402 
403 	// Delete constraint spawn data
404 	for(size_t i = 0; i < ConstraintSpawns.size(); i++)
405 		delete ConstraintSpawns[i];
406 
407 	ConstraintSpawns.clear();
408 
409 	return 1;
410 }
411 
412 // Processes a template tag
GetTemplateProperties(XMLElement * TemplateElement,_Template & Template)413 int _Level::GetTemplateProperties(XMLElement *TemplateElement, _Template &Template) {
414 	XMLElement *Element;
415 	const char *String;
416 
417 	// Get type
418 	std::string ObjectType = TemplateElement->Value();
419 
420 	// Object defaults
421 	if(ObjectType == "orb") {
422 		Template.Sleep = true;
423 	}
424 
425 	// Get name
426 	String = TemplateElement->Attribute("name");
427 	if(!String) {
428 		Log.Write("Template is missing name");
429 		return 0;
430 	}
431 	Template.Name = String;
432 
433 	// Get attributes
434 	TemplateElement->QueryFloatAttribute("lifetime", &Template.Lifetime);
435 	TemplateElement->QueryIntAttribute("smooth", &Template.Smooth);
436 	TemplateElement->QueryIntAttribute("detail", &Template.Detail);
437 
438 	// Get scale
439 	Element = TemplateElement->FirstChildElement("shape");
440 	if(Element) {
441 		Element->QueryFloatAttribute("w", &Template.Shape[0]);
442 		Element->QueryFloatAttribute("h", &Template.Shape[1]);
443 		Element->QueryFloatAttribute("l", &Template.Shape[2]);
444 		Element->QueryFloatAttribute("r", &Template.Radius);
445 	}
446 
447 	// Get mesh properties
448 	Element = TemplateElement->FirstChildElement("mesh");
449 	if(Element) {
450 		String = Element->Attribute("file");
451 		if(String) {
452 			Template.Mesh = String;
453 
454 			// Try custom path first
455 			Template.Mesh = CustomDataPath + std::string("meshes/") + String;
456 			if(!irrFile->existFile(Template.Mesh.c_str())) {
457 
458 				// Try normal path
459 				Template.Mesh = Framework.GetWorkingPath() + std::string("meshes/") + String;
460 				if(!irrFile->existFile(Template.Mesh.c_str())) {
461 					Log.Write("Mesh file does not exist: %s", String);
462 					return 0;
463 				}
464 			}
465 		}
466 
467 		// Get component scale
468 		Element->QueryFloatAttribute("w", &Template.Scale[0]);
469 		Element->QueryFloatAttribute("h", &Template.Scale[1]);
470 		Element->QueryFloatAttribute("l", &Template.Scale[2]);
471 
472 		// Get scale
473 		float Scale = 1.0f;
474 		Element->QueryFloatAttribute("scale", &Scale);
475 		for(int i = 0; i < 3; i++)
476 			Template.Scale[i] *= Scale;
477 	}
478 
479 	// Get heightmap properties
480 	Element = TemplateElement->FirstChildElement("heightmap");
481 	if(Element) {
482 		String = Element->Attribute("file");
483 		if(String) {
484 			Template.HeightMap = String;
485 
486 			// Try custom path first
487 			Template.HeightMap = CustomDataPath + std::string("textures/") + String;
488 			if(!irrFile->existFile(Template.HeightMap.c_str())) {
489 
490 				// Try normal path
491 				Template.HeightMap = Framework.GetWorkingPath() + std::string("textures/") + String;
492 				if(!irrFile->existFile(Template.HeightMap.c_str())) {
493 					Log.Write("Heightmap file does not exist: %s", String);
494 					return 0;
495 				}
496 			}
497 		}
498 	}
499 
500 	// Get physical attributes
501 	Element = TemplateElement->FirstChildElement("physics");
502 	if(Element) {
503 		Element->QueryIntAttribute("kinematic", &Template.Kinematic);
504 		Element->QueryIntAttribute("sleep", &Template.Sleep);
505 		Element->QueryFloatAttribute("mass", &Template.Mass);
506 		Element->QueryFloatAttribute("friction", &Template.Friction);
507 		Element->QueryFloatAttribute("rolling_friction", &Template.RollingFriction);
508 		Element->QueryFloatAttribute("restitution", &Template.Restitution);
509 		Element->QueryFloatAttribute("erp", &Template.ERP);
510 		Element->QueryFloatAttribute("cfm", &Template.CFM);
511 	}
512 
513 	// Get collision attributes
514 	Element = TemplateElement->FirstChildElement("collision");
515 	if(Element) {
516 		Element->QueryIntAttribute("group", &Template.CollisionGroup);
517 		Element->QueryIntAttribute("mask", &Template.CollisionMask);
518 		String = Element->Attribute("callback");
519 		if(String)
520 			Template.CollisionCallback = String;
521 	}
522 
523 	// Get damping
524 	Element = TemplateElement->FirstChildElement("damping");
525 	if(Element) {
526 		Element->QueryFloatAttribute("linear", &Template.LinearDamping);
527 		Element->QueryFloatAttribute("angular", &Template.AngularDamping);
528 	}
529 
530 	// Get axis for constraints
531 	Element = TemplateElement->FirstChildElement("axis");
532 	if(Element) {
533 		Element->QueryFloatAttribute("x", &Template.ConstraintAxis[0]);
534 		Element->QueryFloatAttribute("y", &Template.ConstraintAxis[1]);
535 		Element->QueryFloatAttribute("z", &Template.ConstraintAxis[2]);
536 	}
537 
538 	// Get textures
539 	for(XMLElement *Element = TemplateElement->FirstChildElement("texture"); Element != 0; Element = Element->NextSiblingElement("texture")) {
540 
541 		// Get texture index
542 		int Index = 0;
543 		Element->QueryIntAttribute("index", &Index);
544 		if(Index > 3) {
545 			Log.Write("Texture index out of bounds! %d > 3", Index);
546 			return 0;
547 		}
548 
549 		// Get texture scale
550 		Element->QueryFloatAttribute("scale", &Template.TextureScale[Index]);
551 
552 		// Get filename
553 		const char *Filename = Element->Attribute("file");
554 		if(Filename) {
555 
556 			// Try custom path first
557 			Template.Textures[Index] = CustomDataPath + std::string("textures/") + Filename;
558 			if(!irrFile->existFile(Template.Textures[Index].c_str())) {
559 
560 				// Try normal path
561 				Template.Textures[Index] = Framework.GetWorkingPath() + std::string("textures/") + Filename;
562 				if(!irrFile->existFile(Template.Textures[Index].c_str())) {
563 					Log.Write("Texture file does not exist: %s", Filename);
564 					return 0;
565 				}
566 			}
567 		}
568 	}
569 
570 	// Validate objects
571 	if(ObjectType == "player") {
572 		Template.Type = _Object::PLAYER;
573 		Template.RollingFriction = 0.001f;
574 		Template.CollisionGroup &= ~_Physics::FILTER_CAMERA;
575 		Template.Fog = false;
576 	}
577 	else if(ObjectType == "orb") {
578 		Template.Type = _Object::ORB;
579 	}
580 	else if(ObjectType == "plane") {
581 		Template.Type = _Object::PLANE;
582 		Template.Mass = 0.0f;
583 	}
584 	else if(ObjectType == "sphere") {
585 		Template.Type = _Object::SPHERE;
586 	}
587 	else if(ObjectType == "box") {
588 		Template.Type = _Object::BOX;
589 	}
590 	else if(ObjectType == "cylinder") {
591 		Template.Type = _Object::CYLINDER;
592 	}
593 	else if(ObjectType == "terrain") {
594 		Template.Type = _Object::TERRAIN;
595 		Template.Mass = 0.0f;
596 	}
597 	else if(ObjectType == "zone") {
598 		Template.Type = _Object::ZONE;
599 		Template.Mass = 0.0f;
600 		Template.CollisionGroup = _Physics::FILTER_ZONE;
601 		Template.CollisionMask = _Physics::FILTER_RIGIDBODY;
602 	}
603 	else if(ObjectType == "d6") {
604 		Template.Type = _Object::CONSTRAINT_D6;
605 	}
606 	else if(ObjectType == "fixed") {
607 		Template.Type = _Object::CONSTRAINT_FIXED;
608 	}
609 	else if(ObjectType == "hinge") {
610 		Template.Type = _Object::CONSTRAINT_HINGE;
611 	}
612 
613 	// If a body's mass is zero, set group to static
614 	if(Template.Mass == 0.0f && (Template.CollisionGroup & _Physics::FILTER_RIGIDBODY)) {
615 		Template.CollisionGroup = _Physics::FILTER_STATIC | _Physics::FILTER_CAMERA;
616 		Template.CollisionMask = 0;
617 	}
618 
619 	return 1;
620 }
621 
622 // Processes an object tag
GetObjectSpawnProperties(XMLElement * ObjectElement,_ObjectSpawn & ObjectSpawn)623 int _Level::GetObjectSpawnProperties(XMLElement *ObjectElement, _ObjectSpawn &ObjectSpawn) {
624 	XMLElement *Element;
625 
626 	// Get name
627 	ObjectSpawn.Name = ObjectElement->Attribute("name");
628 	if(ObjectSpawn.Name == "") {
629 		Log.Write("Object is missing name");
630 		return 0;
631 	}
632 
633 	// Get template name
634 	std::string TemplateName = ObjectElement->Attribute("template");
635 	if(TemplateName == "") {
636 		Log.Write("Object is missing template name");
637 		return 0;
638 	}
639 
640 	// Get template data
641 	ObjectSpawn.Template = GetTemplate(TemplateName);
642 	if(ObjectSpawn.Template == nullptr) {
643 		Log.Write("Cannot find object template %s", TemplateName.c_str());
644 		return 0;
645 	}
646 
647 	// Get position
648 	Element = ObjectElement->FirstChildElement("position");
649 	if(Element) {
650 		Element->QueryFloatAttribute("x", &ObjectSpawn.Position[0]);
651 		Element->QueryFloatAttribute("y", &ObjectSpawn.Position[1]);
652 		Element->QueryFloatAttribute("z", &ObjectSpawn.Position[2]);
653 	}
654 
655 	// Get euler rotation
656 	Element = ObjectElement->FirstChildElement("rotation");
657 	if(Element) {
658 		Element->QueryFloatAttribute("x", &ObjectSpawn.Rotation[0]);
659 		Element->QueryFloatAttribute("y", &ObjectSpawn.Rotation[1]);
660 		Element->QueryFloatAttribute("z", &ObjectSpawn.Rotation[2]);
661 	}
662 
663 	// Get quaternion rotation
664 	Element = ObjectElement->FirstChildElement("quaternion");
665 	if(Element) {
666 		Element->QueryFloatAttribute("x", &ObjectSpawn.Quaternion.x);
667 		Element->QueryFloatAttribute("y", &ObjectSpawn.Quaternion.y);
668 		Element->QueryFloatAttribute("z", &ObjectSpawn.Quaternion.z);
669 		Element->QueryFloatAttribute("w", &ObjectSpawn.Quaternion.w);
670 		ObjectSpawn.HasQuaternion = true;
671 		ObjectSpawn.CalculateRotation();
672 	}
673 
674 	// Get plane
675 	Element = ObjectElement->FirstChildElement("plane");
676 	if(Element) {
677 
678 		// Get normal
679 		glm::vec3 Normal;
680 		Element->QueryFloatAttribute("x", &Normal[0]);
681 		Element->QueryFloatAttribute("y", &Normal[1]);
682 		Element->QueryFloatAttribute("z", &Normal[2]);
683 		Element->QueryFloatAttribute("d", &ObjectSpawn.Plane[3]);
684 
685 		// Normalize
686 		if(Normal != glm::vec3(0))
687 			Normal = glm::normalize(Normal);
688 
689 		// Set plane
690 		ObjectSpawn.Plane = glm::vec4(Normal, ObjectSpawn.Plane[3]);
691 	}
692 
693 	// Get linear velocity
694 	Element = ObjectElement->FirstChildElement("linear_velocity");
695 	if(Element) {
696 		Element->QueryFloatAttribute("x", &ObjectSpawn.LinearVelocity[0]);
697 		Element->QueryFloatAttribute("y", &ObjectSpawn.LinearVelocity[1]);
698 		Element->QueryFloatAttribute("z", &ObjectSpawn.LinearVelocity[2]);
699 	}
700 
701 	// Get angular velocity
702 	Element = ObjectElement->FirstChildElement("angular_velocity");
703 	if(Element) {
704 		Element->QueryFloatAttribute("x", &ObjectSpawn.AngularVelocity[0]);
705 		Element->QueryFloatAttribute("y", &ObjectSpawn.AngularVelocity[1]);
706 		Element->QueryFloatAttribute("z", &ObjectSpawn.AngularVelocity[2]);
707 	}
708 
709 	return 1;
710 }
711 
712 // Processes a constraint tag
GetConstraintSpawnProperties(XMLElement * ConstraintElement,_ConstraintSpawn & ConstraintSpawn)713 int _Level::GetConstraintSpawnProperties(XMLElement *ConstraintElement, _ConstraintSpawn &ConstraintSpawn) {
714 	XMLElement *Element;
715 	const char *String = nullptr;
716 
717 	// Get name
718 	ConstraintSpawn.Name = ConstraintElement->Attribute("name");
719 	if(ConstraintSpawn.Name == "") {
720 		Log.Write("Constraint is missing name");
721 		return 0;
722 	}
723 
724 	// Get template name
725 	std::string TemplateName = ConstraintElement->Attribute("template");
726 	if(TemplateName == "") {
727 		Log.Write("Constraint is missing template name");
728 		return 0;
729 	}
730 
731 	// Get template data
732 	ConstraintSpawn.Template = GetTemplate(TemplateName);
733 	if(ConstraintSpawn.Template == nullptr) {
734 		Log.Write("Cannot find constraint template %s", TemplateName.c_str());
735 		return 0;
736 	}
737 
738 	// Get first object
739 	String = ConstraintElement->Attribute("object1");
740 	if(String)
741 		ConstraintSpawn.MainObjectName = String;
742 
743 	// Requirements
744 	if(ConstraintSpawn.MainObjectName == "") {
745 		Log.Write("Constraint is missing object1's name");
746 		return 0;
747 	}
748 
749 	// Get second object
750 	String = ConstraintElement->Attribute("object2");
751 	if(String)
752 		ConstraintSpawn.OtherObjectName = String;
753 
754 	// Get position
755 	Element = ConstraintElement->FirstChildElement("anchor_position");
756 	if(Element) {
757 		Element->QueryFloatAttribute("x", &ConstraintSpawn.AnchorPosition[0]);
758 		Element->QueryFloatAttribute("y", &ConstraintSpawn.AnchorPosition[1]);
759 		Element->QueryFloatAttribute("z", &ConstraintSpawn.AnchorPosition[2]);
760 		ConstraintSpawn.HasAnchorPosition = true;
761 	}
762 
763 	return 1;
764 }
765 
766 // Spawns all of the objects and constraints in the level
SpawnEntities()767 void _Level::SpawnEntities() {
768 
769 	// Create objects
770 	for(size_t i = 0; i < ObjectSpawns.size(); i++) {
771 		_ObjectSpawn *Spawn = ObjectSpawns[i];
772 		CreateObject(*Spawn);
773 	}
774 
775 	// Create constraints
776 	for(size_t i = 0; i < ConstraintSpawns.size(); i++) {
777 		_ConstraintSpawn *Spawn = ConstraintSpawns[i];
778 		Spawn->MainObject = ObjectManager.GetObjectByName(Spawn->MainObjectName);
779 		Spawn->OtherObject = ObjectManager.GetObjectByName(Spawn->OtherObjectName);
780 		CreateConstraint(*Spawn);
781 	}
782 }
783 
784 // Creates an object from a spawn struct
CreateObject(const _ObjectSpawn & Object)785 _Object *_Level::CreateObject(const _ObjectSpawn &Object) {
786 
787 	// Add object
788 	_Object *NewObject = nullptr;
789 	switch(Object.Template->Type) {
790 		case _Object::PLAYER:
791 			NewObject = ObjectManager.AddObject(new _Player(Object));
792 		break;
793 		case _Object::ORB:
794 			NewObject = ObjectManager.AddObject(new _Orb(Object));
795 		break;
796 		case _Object::COLLISION:
797 			NewObject = ObjectManager.AddObject(new _Trimesh(Object));
798 		break;
799 		case _Object::PLANE:
800 			NewObject = ObjectManager.AddObject(new _Plane(Object));
801 		break;
802 		case _Object::SPHERE:
803 			NewObject = ObjectManager.AddObject(new _Sphere(Object));
804 		break;
805 		case _Object::BOX:
806 			NewObject = ObjectManager.AddObject(new _Box(Object));
807 		break;
808 		case _Object::CYLINDER:
809 			NewObject = ObjectManager.AddObject(new _Cylinder(Object));
810 		break;
811 		case _Object::TERRAIN:
812 			NewObject = ObjectManager.AddObject(new _Terrain(Object));
813 		break;
814 		case _Object::ZONE:
815 			NewObject = ObjectManager.AddObject(new _Zone(Object));
816 		break;
817 	}
818 
819 	// Record replay event
820 	if(Replay.IsRecording() && Object.Template->TemplateID != -1) {
821 
822 		// Write replay information
823 		std::fstream &ReplayFile = Replay.GetFile();
824 		Replay.WriteEvent(_Replay::PACKET_CREATE);
825 		ReplayFile.write((char *)&Object.Template->TemplateID, sizeof(Object.Template->TemplateID));
826 		ReplayFile.write((char *)&NewObject->GetID(), sizeof(NewObject->GetID()));
827 		if(Object.Template->Type == _Object::PLANE) {
828 			ReplayFile.put(1);
829 			ReplayFile.write((char *)&Object.Plane, sizeof(float) * 4);
830 		}
831 		else {
832 			ReplayFile.put(0);
833 			ReplayFile.write((char *)&Object.Position, sizeof(float) * 3);
834 		}
835 		ReplayFile.write((char *)&Object.Rotation, sizeof(float) * 3);
836 	}
837 
838 	return NewObject;
839 }
840 
841 // Creates a constraint from a template
CreateConstraint(const _ConstraintSpawn & Constraint)842 _Object *_Level::CreateConstraint(const _ConstraintSpawn &Constraint) {
843 
844 	// Add object
845 	_Object *NewObject = ObjectManager.AddObject(new _Constraint(Constraint));
846 
847 	return NewObject;
848 }
849 
850 // Gets a template by name
GetTemplate(const std::string & Name)851 _Template *_Level::GetTemplate(const std::string &Name) {
852 
853 	// Search templates by name
854 	for(size_t i = 0; i < Templates.size(); i++) {
855 		if(Templates[i]->Name == Name) {
856 			return Templates[i];
857 		}
858 	}
859 
860 	return nullptr;
861 }
862 
863 // Gets a template by name
GetTemplateFromID(int ID)864 _Template *_Level::GetTemplateFromID(int ID) {
865 
866 	// Search templates by id
867 	for(size_t i = 0; i < Templates.size(); i++) {
868 		if(Templates[i]->TemplateID == ID) {
869 			return Templates[i];
870 		}
871 	}
872 
873 	return nullptr;
874 }
875 
876 // Runs the level's scripts
RunScripts()877 void _Level::RunScripts() {
878 
879 	// Reset Lua state
880 	Scripting.Reset();
881 
882 	// Add key names to lua scope
883 	Scripting.DefineLuaVariable("KEY_FORWARD", Input.GetKeyName(Actions.GetInputForAction(_Input::KEYBOARD, _Actions::MOVE_FORWARD)));
884 	Scripting.DefineLuaVariable("KEY_BACK", Input.GetKeyName(Actions.GetInputForAction(_Input::KEYBOARD, _Actions::MOVE_BACK)));
885 	Scripting.DefineLuaVariable("KEY_LEFT", Input.GetKeyName(Actions.GetInputForAction(_Input::KEYBOARD, _Actions::MOVE_LEFT)));
886 	Scripting.DefineLuaVariable("KEY_RIGHT", Input.GetKeyName(Actions.GetInputForAction(_Input::KEYBOARD, _Actions::MOVE_RIGHT)));
887 	Scripting.DefineLuaVariable("KEY_RESET", Input.GetKeyName(Actions.GetInputForAction(_Input::KEYBOARD, _Actions::RESET)));
888 	Scripting.DefineLuaVariable("KEY_JUMP", Input.GetKeyName(Actions.GetInputForAction(_Input::KEYBOARD, _Actions::JUMP)));
889 
890 	// Run scripts
891 	for(uint32_t i = 0; i < Scripts.size(); i++) {
892 
893 		// Load a level
894 		Scripting.LoadFile(Scripts[i]);
895 	}
896 }
897