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