1 module test4.app;
2 
3 /*
4 This is a template for creating games with PixelPerfectEngine.
5 Contains some basic setup, but needs further work to be fully featured besides of adding some game logic.
6 
7 Feel free to modify this, or even create your own templates (you might want to modularize). Contact me, if you encounter any errors on the way.
8  */
9 
10 //Some commonly used stuff
11 import std.stdio;
12 import std.typecons : BitFlags;					//This is a good way to create bitflags, so you don't need to keep track of a bunch of boolean values.
13 import std.file : readText;
14 
15 import bindbc.sdl;								//As of now, this is needed to initialize the SDL API. Might be changed in the future
16 //Graphics related imports
17 import pixelperfectengine.graphics.outputscreen;//Needed to display the final graphics
18 import pixelperfectengine.graphics.raster;		//Needed to display layers in order
19 import pixelperfectengine.graphics.layers;		//Imports all layers and layer-related functionality
20 import pixelperfectengine.graphics.bitmap;		//Imports bitmaps and all bitmap-related functionality
21 //The next two lines imports the collision detector
22 import pixelperfectengine.collision.common;		
23 import pixelperfectengine.collision.objectcollision;
24 //Various system related imports
25 import pixelperfectengine.system.input;			//Every game needs some sort of interaction capability, and thus here's the input
26 import pixelperfectengine.system.file;			//Used to load bitmap and image files
27 import pixelperfectengine.system.config;		//Needed for configuration files
28 import pixelperfectengine.system.common;
29 
30 import pixelperfectengine.system.rng;			//64 bit LFSR random number generator
31 import pixelperfectengine.system.timer;			//Low-precision timer for keeping time-based events relatively precise
32 //Audio-related imports
33 import pixelperfectengine.audio.base.handler;	//Most of the basic stuff we will need
34 import pixelperfectengine.audio.base.modulebase;//Module interfaces to play back sound effects otherwise not tied to the sequence being played back
35 import pixelperfectengine.audio.base.config;	//Audio configuration loader and parser
36 import pixelperfectengine.audio.base.midiseq;	//MIDI sequencer
37 //Imports the engine's own map format.
38 import pixelperfectengine.map.mapformat;
39 //Other imports that might be important. Uncomment any you feel you'll need.
40 /* import pixelperfectengine.system.common; */
41 /* import pixelperfectengine.scripting.lua;
42 import pixelperfectengine.scripting.globals; */
43 
44 
45 ///Our main function, needed for the program to operate.
46 ///You can add `string[] args` if your either really need or really want.
47 int main() {
48 	initialzeSDL();						//Initializes the SDL subsystem, so we will have input and graphics
49 	initLua();
50 	try {								//A try-catch block to handle any errors. A bit ugly, but can save us when there's issues with debug symbols, or an error happened outside of a D code
51 		GameApp app = new GameApp();
52 		app.whereTheMagicHappens();
53 	} catch (Throwable e) {
54 		writeln(e);
55 		debug readln();					//RemedyBG closes cmd line windows immediately, this stops that.
56 	}
57 	return 0;
58 }
59 ///I generally like to put most of the application logic into one class to keep track of globals, as well as targets to certain events.
60 public class GameApp : SystemEventListener, InputListener {
61 	///Defines various states of the game.
62 	///I have added some quite useful and very often used ones
63 	enum StateFlags {
64 		isRunning	=	1<<0,
65 		pause		=	1<<1,
66 		mainMenu	=	1<<2,
67 	}
68 	///Defines various states for the control. Mainly used to avoid effects from typematic and such.
69 	///These are the bare minimum requirements for a game.
70 	enum ControlFlags {
71 		up			=   1<<0,
72 		down		=   1<<1,
73 		left		=   1<<2,
74 		right		=   1<<3,
75 	}
76 	///Stores the currently loaded map file with all related data.
77 	MapFormat		mapSource;
78 	///To display our game's graphics
79 	OutputScreen	output;
80 	///To manage our layers and palette.
81 	Raster			rstr;
82 	///For input handling.
83 	InputHandler	ih;
84 	///Detects object collisions that were registered to it.
85 	ObjectCollisionDetector	ocd;
86 	///Contains various game state flags (is it running, is it paused, etc).
87 	BitFlags!StateFlags	stateFlags;
88 	///Contains various control state flags
89 	BitFlags!ControlFlags controlFlags;
90 	///Contains the pointer to the textlayer.
91 	///Can be used for the menu, status bars, etc.
92 	TileLayer		textLayer;
93 	///Contains pointer to the game field, so we can easily interact with it.
94 	SpriteLayer		gameField;
95 	///Contains the random number generator and its state.
96 	//RandomNumberGenerator	rng;
97 	//ConfigurationProfile	cfg;
98 	//Audio related stuff goes here.
99 	//Note: some of the audio stuff is preliminary. It works, but cannot handle certain cases, such as sample rate mismatches, or device disconnection.
100 	//Sequencer is untested as of now due to a lack of time and manpower.
101 	AudioDeviceHandler adh;	///Handles audio devices and outputs.
102 	ModuleManager	modMan;	///Handles the modules and their output.
103 	ModuleConfig	modCfg;	///Loads and handles module configuration, including routing, patches, and samples.
104 	SequencerM1		midiSeq;///MIDI sequencer for MIDI playback.
105 	/* LuaScript		scrp; */
106 	
107 	/// Initializes our application.
108 	/// Put other things here if you need them.
109 	this () {
110 		stateFlags.isRunning = true;	//Sets the state to running, so the main loop will stay running.
111 		output = new OutputScreen("Your app name here", 424 * 4, 240 * 4);	//Creates an output window with the display size of 1696x960.
112 		rstr = new Raster(424,240,output,0);//Creates a raster with the size of 424x240.
113 		output.setMainRaster(rstr);		//Sets the main raster of the output screen.
114 
115 		ih = new InputHandler();		//Creates an instance of an InputHandler (should be only one)
116 		ih.systemEventListener = this;	//Sets the system event target to this instance
117 		ih.inputListener = this;		//Sets the input event target to this instance
118 		
119 		//ocd = new ObjectCollisionDetector(&onCollision, 0);	//Creates an object collision detector
120 		//Let's create our layer for statuses, etc
121 		textLayer = new TileLayer(16,16, RenderingMode.Copy);	//Creates a TileLayer with 8x8 tiles and alpha blending
122 		textLayer.paletteOffset = 0;						//Sets the palette offset to 512. You might want to change this to the value to the place where you loaded your GUI palette
123 		textLayer.masterVal = 255;							//Sets the master value for the alpha blending, making this layer semi-transparent initially.
124 		rstr.addLayer(textLayer, 0);
125 		gameField = new SpriteLayer();
126 		rstr.addLayer(gameField, 16);
127 		//cfg = new ConfigurationProfile();					//Creates and loads the configuration profile.
128 		/* //Comment the next part out, if you're having too much trouble with audio working, since you still can add sound later on.
129 		//audio related part begin
130 		AudioDeviceHandler.initAudioDriver(OS_PREFERRED_DRIVER);	//Initializes the driver
131 		AudioSpecs as = AudioSpecs(predefinedFormats[PredefinedFormats.FP32], 48_000, 0, 2, cfg.audioBufferLen, 
132 				Duration.init);								//Sets up a default audio specification
133 		adh = new AudioDeviceHandler(as, 1024, 1024 / 128);	//Creates a new AudioDeviceHandler and sets up the basics
134 		adh.initAudioDevice(-1);							//Initializes the default device
135 		modMan = new ModuleManager(adh);					//Initializes the module manager
136 		modCfg = new ModuleConfig(modMan);					//Initializes the module configurator
137 		modCfg.loadConfigFromFile("../assets/test4_audio.sdl");//This line loads an audio configuration file (make sure you have a valid one - create one with the ADK/test1!)
138 		modCfg.compile(false);								//Compiles the current module configuration.
139 		midiSeq = new SequencerM1(modMan.moduleList, modCfg.midiRouting, modCfg.midiGroups);
140 		modMan.runAudioThread();							//Runs the audio thread.
141 		//audio related part end 
142 		*/
143 		textLayer.loadMapping(32, 16, new MappingElement[](32*16));
144 		
145 		//<Put other initialization code here>
146 		mainRaster = rstr;
147 		{
148 			Image dlangMan = loadImage(File("../assets/d-man.tga"));
149 			scrptResMan["dlangman"] = loadBitmapFromImage!Bitmap8Bit(dlangMan);
150 			rstr.loadPaletteChunk(loadPaletteFromImage(dlangMan),0);
151 			Image kumapu = loadImage(File("../assets/kumapu.png"));
152 			textLayer.addTile(loadBitmapFromImage!Bitmap32Bit(kumapu), 0x0000);
153 		}
154 		for (int y ; y < 16 ; y++) {
155 			for (int x ; x < 32 ; x++) {
156 				textLayer.writeMapping(x, y, MappingElement(0x0000));
157 			}
158 		}
159 		/* scrp = new LuaScript(readText("../assets/test4.lua"), "test4.lua"); */
160 		//scrp.runMain();
161 		//scrp.callFunction("Initialize");
162 	}
163 	void whereTheMagicHappens() {
164 		while (stateFlags.isRunning) {
165 			//Refreshes the raster, then sends the new image to the output window.
166 			rstr.refresh();
167 			//Tests the input devices for events.
168 			ih.test();
169 			/* scrp.callFunction("UpdateFunc"); */
170 			//Tests the timer for any registered events that are to happen.
171 			//Note: You can put this call into a separate thread for more precision.
172 			timer.test();
173 			//Calling the RNG for every frame will make it less deterministic. Speed runners might hate you for this.
174 			//rng();
175 			//This calls the collision detector on all registered objects.
176 			//You'll want to only call it on moving objects, especially when you have a lot of objects on screen.
177 			//ocd.testAll();
178 
179 			//<Per-frame code comes here>
180 		}
181 		//scrp.deinit();
182 	}
183 	///This function will load a map file to display levels, or portions of levels.
184 	///If you're clever, you can store other things in map files
185 	void loadMap(string m) {
186 		//This loop removes all previously loaded layers from the raster.
187 		foreach (key, elem; mapSource.layeroutput) {
188 			rstr.removeLayer(key);
189 		}
190 		mapSource = new MapFormat(File(m));			//Loads the map file itself, and parses it.
191 		mapSource.loadTiles(rstr);					//Loads the tiles with the palettes needed to display them.
192 		mapSource.loadAllSpritesAndObjects(rstr, ocd);	//Loads all sprites and objects to the layers, and the collision detector. Also loads the palettes for the sprites
193 		mapSource.loadMappingData();				//Loads all mapping data to the tilelayers.
194 		rstr.loadLayers(mapSource.layeroutput);		//Adds the layers to the raster for display.
195 		//Stores a reference to the gamefield.
196 		//Change the index number if you want.
197 		gameField = cast(SpriteLayer)(mapSource.layeroutput[16]);
198 	}
199 	///Collision events can be handled from here.
200 	public void onCollision(ObjectCollisionEvent event) {
201 
202 	}
203 	///Called if the window is closed, etd.
204 	public void onQuit() {
205 		//You might want to put here a more complicated prompt instead.
206 		stateFlags.isRunning = false;
207 	}
208 	///Called when a controller is added to the system.
209 	///Note: This function will be changed once I move input handling and output screen handling to iota.
210 	public void controllerAdded(uint id) {
211 		
212 	}
213 	///Called when a controller is removed the system.
214 	///Note: This function will be changed once I move input handling and output screen handling to iota.
215 	public void controllerRemoved(uint id) {
216 		
217 	}
218 	///Called if a key input event has occured.
219 	///Note: This function will be changed once I move input handling and output screen handling to iota.
220 	public void keyEvent(uint id, BindingCode code, uint timestamp, bool isPressed) {
221 		
222 	}
223 	///Called if an axis input event has occured.
224 	///Note: This function will be changed once I move input handling and output screen handling to iota.
225 	public void axisEvent(uint id, BindingCode code, uint timestamp, float value) {
226 		
227 	}
228 }