1 /** 2 * Space shooter demo. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.demo.pewpew.pewpew; 15 16 import std.math; 17 import std.random; 18 import std.datetime; 19 import std.algorithm : min; 20 import std.conv; 21 import std.traits, std.typecons; 22 23 import ae.ui.app.application; 24 import ae.ui.app.posix.main; 25 import ae.ui.shell.shell; 26 import ae.ui.shell.sdl2.shell; 27 import ae.ui.video.video; 28 import ae.ui.video.sdl2.video; 29 import ae.ui.video.renderer; 30 import ae.utils.graphics.draw; 31 import ae.utils.graphics.gamma; 32 import ae.utils.fps; 33 34 import ae.demo.pewpew.objects; 35 36 final class MyApplication : Application 37 { 38 override string getName() { return "Demo/PewPew"; } 39 override string getCompanyName() { return "CyberShadow"; } 40 41 Shell shell; 42 uint ticks; 43 alias GammaRamp!(COLOR.ChannelType, ubyte) MyGamma; 44 MyGamma gamma; 45 FPSCounter fps; 46 47 static uint currentTick() { return TickDuration.currSystemTick().to!("msecs", uint)(); } 48 49 enum InputSource { keyboard, joystick, max } 50 enum GameKey { up, down, left, right, fire, none } 51 52 int[InputSource.max][GameKey.max] inputMatrix; 53 54 override void render(Renderer s) 55 { 56 fps.tick(&shell.setCaption); 57 58 auto screenCanvas = s.lock(); 59 scope(exit) s.unlock(); 60 61 if (initializing) 62 { 63 gamma = MyGamma(ColorSpace.sRGB); 64 new Game(); 65 foreach (i; 0..1000) step(10); 66 ticks = currentTick(); 67 initializing = false; 68 } 69 70 foreach (i, key; EnumMembers!GameKey[0..$-1]) 71 { 72 enum name = __traits(allMembers, GameKey)[i]; 73 bool pressed; 74 foreach (input; inputMatrix[key]) 75 if (input) 76 pressed = true; 77 mixin(name ~ " = pressed;"); 78 } 79 80 //auto destTicks = ticks+deltaTicks; 81 uint destTicks = currentTick(); 82 // step(deltaTicks); 83 while (ticks < destTicks) 84 ticks++, 85 step(1); 86 87 auto canvasSize = min(screenCanvas.w, screenCanvas.h); 88 canvas.size(canvasSize, canvasSize); 89 canvas.fill(canvas.COLOR.init); 90 foreach (ref plane; planes) 91 foreach (obj; plane) 92 obj.render(); 93 94 auto x = (screenCanvas.w-canvasSize)/2; 95 auto y = (screenCanvas.h-canvasSize)/2; 96 auto dest = screenCanvas.crop(x, y, x+canvasSize, y+canvasSize); 97 98 import std.parallelism; 99 import std.range; 100 foreach (j; taskPool.parallel(iota(canvasSize))) 101 { 102 auto src = canvas.crop(0, j, canvasSize, j+1); 103 auto ramp = gamma.lum2pixValues.ptr; 104 src.colorMap!(c => Renderer.COLOR.monochrome(ramp[c.l])).blitTo(dest, 0, j); 105 } 106 } 107 108 void step(uint deltaTicks) 109 { 110 foreach (ref plane; planes) 111 foreach (obj; plane) 112 obj.step(deltaTicks); 113 } 114 115 GameKey keyToGameKey(Key key) 116 { 117 switch (key) 118 { 119 case Key.up : return GameKey.up ; 120 case Key.down : return GameKey.down ; 121 case Key.left : return GameKey.left ; 122 case Key.right: return GameKey.right; 123 case Key.space: return GameKey.fire ; 124 default : return GameKey.none ; 125 } 126 } 127 128 override void handleKeyDown(Key key, dchar character) 129 { 130 auto gameKey = keyToGameKey(key); 131 if (gameKey != GameKey.none) 132 inputMatrix[gameKey][InputSource.keyboard]++; 133 else 134 if (key == Key.esc) 135 shell.quit(); 136 } 137 138 override void handleKeyUp(Key key) 139 { 140 auto gameKey = keyToGameKey(key); 141 if (gameKey != GameKey.none) 142 inputMatrix[gameKey][InputSource.keyboard] = 0; 143 } 144 145 override bool needJoystick() { return true; } 146 147 int[2] axisInitial; 148 bool[2] axisCalibrated; 149 150 override void handleJoyAxisMotion(int axis, short svalue) 151 { 152 if (axis >= 2) return; 153 154 int value = svalue; 155 if (!axisCalibrated[axis]) // assume first input event is inert 156 axisInitial[axis] = value, 157 axisCalibrated[axis] = true; 158 value -= axisInitial[axis]; 159 160 import ae.utils.math; 161 if (abs(value) > short.max/2) // hack? 162 useAnalog = true; 163 auto fvalue = bound(cast(float)value / short.max, -1f, 1f); 164 (axis==0 ? analogX : analogY) = fvalue; 165 } 166 167 JoystickHatState lastState = cast(JoystickHatState)0; 168 169 override void handleJoyHatMotion (int hat, JoystickHatState state) 170 { 171 void checkDirection(JoystickHatState direction, GameKey key) 172 { 173 if (!(lastState & direction) && (state & direction)) inputMatrix[key][InputSource.joystick]++; 174 if ((lastState & direction) && !(state & direction)) inputMatrix[key][InputSource.joystick]--; 175 } 176 checkDirection(JoystickHatState.up , GameKey.up ); 177 checkDirection(JoystickHatState.down , GameKey.down ); 178 checkDirection(JoystickHatState.left , GameKey.left ); 179 checkDirection(JoystickHatState.right, GameKey.right); 180 lastState = state; 181 } 182 183 override void handleJoyButtonDown(int button) 184 { 185 inputMatrix[GameKey.fire][InputSource.joystick]++; 186 } 187 188 override void handleJoyButtonUp (int button) 189 { 190 inputMatrix[GameKey.fire][InputSource.joystick]--; 191 } 192 193 override int run(string[] args) 194 { 195 shell = new SDL2Shell(this); 196 shell.video = new SDL2SoftwareVideo(); 197 shell.run(); 198 shell.video.shutdown(); 199 return 0; 200 } 201 202 override void handleQuit() 203 { 204 shell.quit(); 205 } 206 } 207 208 shared static this() 209 { 210 createApplication!MyApplication(); 211 }