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 }