8c6f3b863a06de3973b3a9e585956a7001c68a43
Stefan Schuermans base class for games

Stefan Schuermans authored 5 years ago

1) /* Blinker
2)    Copyright 2011-2019 Stefan Schuermans <stefan@blinkenarea.org>
3)    Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html
4)    a blinkenarea.org project */
5) 
Stefan Schuermans pong: display score

Stefan Schuermans authored 5 years ago

6) #include <sstream>
Stefan Schuermans base class for games

Stefan Schuermans authored 5 years ago

7) #include <string>
8) #include <vector>
9) 
10) #include <BlinkenLib/BlinkenFrame.h>
11) 
12) #include "File.h"
13) #include "Format.h"
14) #include "FormatFile.h"
15) #include "Game.h"
16) #include "Mgrs.h"
17) #include "Module.h"
18) #include "OutStreamFile.h"
Stefan Schuermans pong: configurable speed

Stefan Schuermans authored 5 years ago

19) #include "UIntFile.h"
Stefan Schuermans base class for games

Stefan Schuermans authored 5 years ago

20) 
21) namespace Blinker {
22) 
23) /**
24)  * @brief constructor
25)  * @param[in] name module name
26)  * @param[in] mgrs managers
27)  * @param[in] dirBase base directory
28)  */
29) Game::Game(const std::string &name, Mgrs &mgrs, const Directory &dirBase):
30)   Module(name, mgrs, dirBase),
31)   m_fileFormat(dirBase.getFile("format")),
32)   m_fileBackgroundColor(dirBase.getFile("backgroundColor")),
33)   m_fileOutStream(dirBase.getFile("outstream"), mgrs.m_streamMgr),
34)   m_height(0), m_width(0), m_channels(0), m_imgBuf(), m_backgroundColor()
35) {
36) }
37) 
38) /// virtual destructor
39) Game::~Game()
40) {
41)   // clean up
42)   deactivate();
43) }
44) 
45) /// check for update of configuration
46) void Game::updateConfig()
47) {
Stefan Schuermans begin of Pong game

Stefan Schuermans authored 5 years ago

48)   bool doReinit = false;
49)   bool doRedraw = false;
50) 
51)   // format file was modified -> re-create canvas and schedule redraw
Stefan Schuermans base class for games

Stefan Schuermans authored 5 years ago

52)   if (m_fileFormat.checkModified()) {
Stefan Schuermans begin of Pong game

Stefan Schuermans authored 5 years ago

53)     m_fileFormat.update();
Stefan Schuermans base class for games

Stefan Schuermans authored 5 years ago

54)     createImgBuf();
Stefan Schuermans begin of Pong game

Stefan Schuermans authored 5 years ago

55)     doReinit = true;
Stefan Schuermans base class for games

Stefan Schuermans authored 5 years ago

56)   }
57) 
Stefan Schuermans begin of Pong game

Stefan Schuermans authored 5 years ago

58)   // color file was modified -> convert color, schedule redraw
59)   if (colorUpdate(m_fileBackgroundColor, m_backgroundColor)) {
60)     doRedraw = true;
Stefan Schuermans base class for games

Stefan Schuermans authored 5 years ago

61)   }
62) 
63)   // output stream name file was modified -> re-get output stream
64)   if (m_fileOutStream.checkModified()) {
65)     m_fileOutStream.update();
66)   }
Stefan Schuermans begin of Pong game

Stefan Schuermans authored 5 years ago

67) 
68)   // check config update of derived game
69)   if (updateConfigGame()) {
70)     doRedraw = true;
71)   }
72) 
73)   // re-initialize / redraw
74)   if (doReinit) {
75)     reinitialize();
76)     doRedraw = true;
77)   }
78)   if (doRedraw) {
79)     redraw();
80)   }
Stefan Schuermans base class for games

Stefan Schuermans authored 5 years ago

81) }
82) 
83) /// activate game: set up image buffer, call redraw()
84) void Game::activate()
85) {
86)   createImgBuf();
Stefan Schuermans begin of Pong game

Stefan Schuermans authored 5 years ago

87)   reinitialize();
Stefan Schuermans base class for games

Stefan Schuermans authored 5 years ago

88)   redraw();
89) }
90) 
91) /// deactivate game: tear down image buffer, deactivate output
92) void Game::deactivate()
93) {
94)   destroyImgBuf();
95)   sendFrame();
96) }
97) 
Stefan Schuermans pong: activate when player...

Stefan Schuermans authored 5 years ago

98) /// check if game is active
99) bool Game::isActive() const
100) {
101)   return ! m_imgBuf.empty(); // game is active if there is an image buffer
102) }
103) 
Stefan Schuermans base class for games

Stefan Schuermans authored 5 years ago

104) /// set pixel in image buffer
105) void Game::pixel(int y, int x, ColorData const &cd)
106) {
107)   if (m_imgBuf.empty() ||
108)       ! checkLimitInt(y, 0, m_height - 1) ||
109)       ! checkLimitInt(x, 0, m_width - 1)) {
110)     return;
111)   }
112)   int pos = (y * m_width + x) * m_channels;
113)   std::copy(cd.begin(), cd.end(), m_imgBuf.begin() + pos);
114) }
115) 
116) /// draw horizontal line to image buffer
117) void Game::lineHor(int y, int x1, int x2, ColorData const &cd)
118) {
119)   if (m_imgBuf.empty() ||
120)       ! checkLimitInt(y, 0, m_height - 1) ||
121)       ! checkIntRangeLimit(x1, x2, 0, m_width - 1)) {
122)     return;
123)   }
124)   int pos = (y * m_width + x1) * m_channels;
125)   int dx = m_channels;
126)   for (int x = x1; x <= x2; ++x) {
127)     std::copy(cd.begin(), cd.end(), m_imgBuf.begin() + pos);
128)     pos += dx;
129)   }
130) }
131) 
132) /// draw vertical line to image buffer
133) void Game::lineVert(int y1, int y2, int x, ColorData const &cd)
134) {
135)   if (m_imgBuf.empty() ||
136)       ! checkIntRangeLimit(y1, y2, 0, m_height - 1) ||
137)       ! checkLimitInt(x, 0, m_width - 1)) {
138)     return;
139)   }
140)   int pos = (y1 * m_width + x) * m_channels;
141)   int dy = m_width * m_channels;
142)   for (int y = y1; y <= y2; ++y) {
143)     std::copy(cd.begin(), cd.end(), m_imgBuf.begin() + pos);
144)     pos += dy;
145)   }
146) }
147) 
148) /// draw non-filled rectangle to image buffer
149) void Game::rect(int y1, int y2, int x1, int x2, ColorData const &cd)
150) {
151)   lineHor(y1, x1, x2, cd);
152)   lineHor(y2, x1, x2, cd);
153)   lineVert(y1, y2, x1, cd);
154)   lineVert(y1, y2, x2, cd);
155) }
156) 
157) /// draw filled rectangle to image buffer
158) void Game::rectFill(int y1, int y2, int x1, int x2, ColorData const &cd)
159) {
160)   if (m_imgBuf.empty() ||
161)       ! checkIntRangeLimit(y1, y2, 0, m_height - 1) ||
162)       ! checkIntRangeLimit(x1, x2, 0, m_width - 1)) {
163)     return;
164)   }
165)   int pos = (y1 * m_width + x1) * m_channels;
166)   int dx = m_channels;
167)   int dy = m_width - (x2 - x1 + 1) * m_channels;
168)   for (int y = y1; y <= y2; ++y) {
169)     for (int x = x1; x <= x2; ++x) {
170)       std::copy(cd.begin(), cd.end(), m_imgBuf.begin() + pos);
171)       pos += dx;
172)     }
173)     pos += dy;
174)   }
175) }
176) 
Stefan Schuermans pong: display score

Stefan Schuermans authored 5 years ago

177) /**
178)  * @brief draw a small digit (3x5 pixels) to image buffer
179)  * @param[in] topY y coordinate of top of digit
180)  * @param[in] leftX x coordinate of left of digit
181)  * @param[in] digit to draw ('0'..'9')
182)  * @param[in] cd color to use for drawing
183)  */
184) void Game::digit3x5(int topY, int leftX, char digit, ColorData const &cd)
185) {
186)   /**
187)    * digit data in octal,
188)    * each row is 3 bits, msb of row is left,
189)    * msb row is top
190)    */
191)   static const unsigned int digitData[10] = {
192)     075557, 022222, 071747, 071717, 055711,
193)     074717, 074757, 071111, 075757, 075717
194)   };
195) 
196)   if (digit < '0' || digit > '9') {
197)     return;
198)   }
199)   int idx = digit - '0';
200)   unsigned int data = digitData[idx];
201)   for (int y = topY; y < topY + 5; ++y) {
202)     for (int x = leftX; x < leftX + 3; ++x) {
203)       data <<= 1;
204)       if (data & 0100000) {
205)         pixel(y, x, cd);
206)       }
207)     }
208)   }
209) }
210) 
211) /**
212)  * @brief draw small digits (3x5 each, 1 pixel gap) to image buffer
213)  * @param[in] topY y coordinate of top of digits
214)  * @param[in] leftX x coordinate of left of digits
215)  * @param[in] digits to draw (string of '0'..'9')
216)  * @param[in] cd color to use for drawing
217)  */
218) void Game::digits3x5(int topY, int leftX, std::string const &digits,
219)                      ColorData const &cd)
220) {
221)   for (char digit: digits) {
222)     digit3x5(topY, leftX, digit, cd);
223)     leftX += 4;
224)   }
225) }
226) 
227) /**
228)  * @brief draw number using 3x5 digits to image buffer
229)  * @param[in] anchorY y coordinate of anchor point
230)  * @param[in] anchorX x coordinate of anchor point
231)  * @param[in] alignY y alignment, -1 for top, 0 for center, 1 for bottom
232)  * @param[in] alignX x alignment, -1 for left, 0 for center, 1 for right
233)  * @param[in] number non-negative number to draw
234)  * @param[in] cd color to use for drawing
235)  */
236) void Game::number3x5(int anchorY, int anchorX, int alignY, int alignX,
237)                      int number, ColorData const &cd)
238) {
239)   // convert number to digits string
240)   if (number < 0) {
241)     return;
242)   }
243)   std::stringstream digitsStrm;
244)   digitsStrm << number;
245)   std::string const &digits = digitsStrm.str();
246) 
247)   // compute top left position
248)   int topY, leftX;
249)   switch (alignY) {
250)   case -1: topY = anchorY; break;
251)   case 0: topY = anchorY - 2; break;
252)   case 1: topY = anchorY - 4; break;
253)   default: return;
254)   }
255)   switch (alignX) {
256)   case -1: leftX = anchorX; break;
257)   case 0: leftX = anchorX - 2 * digits.length() + 1; break;
258)   case 1: leftX = anchorX - 4 * digits.length() + 2; break;
259)   default: return;
260)   }
261) 
262)   // draw digits
263)   digits3x5(topY, leftX, digits, cd);
264) }
265) 
Stefan Schuermans begin of Pong game

Stefan Schuermans authored 5 years ago

266) /// process update of color file, return true on update
267) bool Game::colorUpdate(ColorFile &colorFile, ColorData &data) const
268) {
269)   if (colorFile.checkModified()) {
270)     colorFile.update();
271)     color2data(colorFile, data);
272)     return true;
273)   } else {
274)     return false;
275)   }
276) }
277) 
Stefan Schuermans base class for games

Stefan Schuermans authored 5 years ago

278) /// convert color to raw color data
Stefan Schuermans begin of Pong game

Stefan Schuermans authored 5 years ago

279) void Game::color2data(ColorFile const &colorFile, ColorData &data) const
Stefan Schuermans base class for games

Stefan Schuermans authored 5 years ago

280) {
Stefan Schuermans pong: configurable speed

Stefan Schuermans authored 5 years ago

281)   if (! m_fileFormat.m_valid || ! colorFile.m_valid) {
Stefan Schuermans begin of Pong game

Stefan Schuermans authored 5 years ago

282)     data.clear();
283)   } else {
284)     unsigned int channels = m_fileFormat.m_obj.m_channels;
285)     unsigned int maxval = m_fileFormat.m_obj.m_maxval;
286)     data.resize(m_fileFormat.m_obj.m_channels);
287)     Color const &color = colorFile.m_obj;
288)     if (channels == 1) {
289)       // single channel
290)       // convert to monochrome according to CIE XYZ
291)       double val = 0.2125 * color.m_red + 0.7154 * color.m_green
292)                                         + 0.0721 * color.m_blue;
293)       // adapt to maxval and round
294)       data.at(0) = (unsigned char)(val * maxval / 255.0 + 0.5);
295)     } else if (channels == 2) {
296)       // two channels
297)       // adapt to maxval and round, ignore blue
298)       data.at(0) = (unsigned char)(color.m_red * maxval / 255.0 + 0.5);
299)       data.at(1) = (unsigned char)(color.m_green * maxval / 255.0 + 0.5);
300)     } else if (channels >= 3) {
301)       // three channels (more than three channels: further channels are dark)
302)       // adapt to maxval and round
303)       data.at(0) = (unsigned char)(color.m_red * maxval / 255.0 + 0.5);
304)       data.at(1) = (unsigned char)(color.m_green * maxval / 255.0 + 0.5);
305)       data.at(2) = (unsigned char)(color.m_blue * maxval / 255.0 + 0.5);
306)     }
Stefan Schuermans base class for games

Stefan Schuermans authored 5 years ago

307)   }
308) }
309) 
Stefan Schuermans pong: configurable speed

Stefan Schuermans authored 5 years ago

310) /// process update of value file, return true on update
311) bool Game::valueUpdate(UIntFile &valueFile, ValueDescr const &descr,
312)                        unsigned int &value)
313) {
314)   if (valueFile.checkModified()) {
315)     valueFile.update();
316)     valueFromFile(valueFile, descr, value);
317)     return true;
318)   } else {
319)     return false;
320)   }
321) }
322) 
323) /// get value from value file
324) void Game::valueFromFile(UIntFile &valueFile, ValueDescr const &descr,
325)                          unsigned int &value)
326) {
327)   if (! valueFile.m_valid) {
328)     value = descr.default_;
329)   } else {
330)     value = valueFile.m_obj.m_uint;
331)     if (value < descr.minimum) {
332)       value = descr.minimum;
333)     }
334)     else if (value > descr.maximum) {
335)       value = descr.maximum;
336)     }
337)   }
338) }
339) 
Stefan Schuermans base class for games

Stefan Schuermans authored 5 years ago

340) /// send current image buffer as frame to output stream
341) void Game::sendFrame()
342) {
343)   // image buffer available -> send its contents as frame
344)   if (! m_imgBuf.empty()) {
345)     Format const &fmt = m_fileFormat.m_obj;
346)     stBlinkenFrame *pFrame = BlinkenFrameNew(fmt.m_height, fmt.m_width,
347)                                              fmt.m_channels, fmt.m_maxval, 1);
348)     BlinkenFrameSetPixelData(pFrame, 0, m_height, 0, m_width, 0, m_channels,
349)                              m_imgBuf.data());
350)     m_fileOutStream.setFrame(pFrame);
351)     BlinkenFrameFree(pFrame);
352)   }
353)   // no image buffer available -> send "no frame" information
354)   else {
355)     m_fileOutStream.setFrame(NULL);
356)   }
357) }
358) 
359) /// (re-)create image buffer
360) void Game::createImgBuf()
361) {
362)   // get rid of old image buffer
363)   destroyImgBuf();
364) 
365)   // read format from format file
366)   m_fileFormat.update();
367)   if (! m_fileFormat.m_valid)
368)     return;
369) 
370)   // read background color
371)   m_fileBackgroundColor.update();
372)   if (! m_fileBackgroundColor.m_valid)
373)     return;
374) 
375)   // store parameters
376)   m_height = m_fileFormat.m_obj.m_height;
377)   m_width = m_fileFormat.m_obj.m_width;
378)   m_channels = m_fileFormat.m_obj.m_channels;
379) 
380)   // create image buffer
381)   m_imgBuf.resize(m_height * m_width * m_channels);
382) 
383)   // convert background color
Stefan Schuermans begin of Pong game

Stefan Schuermans authored 5 years ago

384)   color2data(m_fileBackgroundColor, m_backgroundColor);