1 /*
2 ===========================================================================
3 blockattack - Block Attack - Rise of the Blocks
4 Copyright (C) 2005-2012 Poul Sander
5 
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 2 of the License, or
9 (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see http://www.gnu.org/licenses/
18 
19 Source information and contacts persons can be found at
20 http://www.blockattack.net
21 ===========================================================================
22 */
23 
24 #ifndef BLOCKGAME_HPP
25 #define BLOCKGAME_HPP 1
26 
27 #include "stats.h"
28 #include "common.h"
29 #include <deque>
30 #include "cereal/cereal.hpp"
31 #include "cereal/types/deque.hpp"
32 #include "cereal/types/string.hpp"
33 
34 #define NUMBEROFCHAINS 100
35 #define BLOCKWAIT 100000
36 #define BLOCKHANG 1000
37 
38 enum stageButton {SBdontShow, SBstageClear, SBpuzzleMode};
39 
40 extern stageButton stageButtonStatus;
41 
42 //This is the size of the blocks. They are always 50. The internal logic calculates it that way
43 const int bsize = 50;
44 
45 /**
46  * This struct defines the start conditions of the game
47  */
48 struct BlockGameStartInfo {
49 	unsigned int ticks = 0;
50 	bool timeTrial = false;
51 	///True means a stage clear game will be started. level must be set too.
52 	bool stageClear = false;
53 	///True if puzzle mode. level must be set too.
54 	bool puzzleMode = false;
55 	///Single puzzle is used for the editor only.
56 	bool singlePuzzle = false;
57 	int level = 0;
58 	bool AI = false;
59 	/**
60 	 * True means that stats will be recorded.
61 	 * If AI is true then this will be overruled to false
62 	 */
63 	bool recordStats = true;
64 	bool vsMode = false;
65 	/**
66 	 * Set to true if we are fighting an AI. level should be the AI level we are fighting
67 	 */
68 	bool vsAI = false;
69 	int startBlocks = -1;
70 	int handicap = 0;
71 	int gameSpeed = 0;
72 	template <class Archive>
serializeBlockGameStartInfo73 	void serialize( Archive & ar )
74 	{
75 		ar( CEREAL_NVP(ticks), CEREAL_NVP(timeTrial), CEREAL_NVP(stageClear), CEREAL_NVP(puzzleMode), CEREAL_NVP(singlePuzzle),
76 			CEREAL_NVP(level), CEREAL_NVP(AI), CEREAL_NVP(recordStats), CEREAL_NVP(vsMode), CEREAL_NVP(vsAI),
77 			CEREAL_NVP(startBlocks), CEREAL_NVP(handicap), CEREAL_NVP(gameSpeed) );
78 	}
79 };
80 
81 struct GarbageStruct {
82 	bool greyGarbage = false;
83 	int width = 0;
84 	int height = 0;
setGarbageGarbageStruct85 	void setGarbage(int w, int h, bool g = false) {
86 		greyGarbage = g;
87 		width = w;
88 		height = h;
89 	}
90 	template <class Archive>
serializeGarbageStruct91 	void serialize( Archive & ar )
92 	{
93 		ar( CEREAL_NVP(greyGarbage), CEREAL_NVP(width), CEREAL_NVP(height) );
94 	}
95 };
96 
97 struct BlockGameAction {
98 	enum class Action {NONE, UPDATE, SET_DRAW, SET_WON, SET_GAME_OVER, MOVE, SWITCH, PUSH, PUSH_GARBAGE, MOUSE_DOWN, MOUSE_UP, MOUSE_MOVE};
99 	Action action = Action::NONE;
100 	unsigned int tick = 0;  //< Used for update
101 	char way = '\0';  //< The direction to move the cursor: 'N', 'E', 'S' or 'W'
102 	int x = 0;
103 	int y = 0;
104 	std::vector<GarbageStruct> garbage;
105 	template <class Archive>
serializeBlockGameAction106 	void serialize( Archive & ar )
107 	{
108 		ar( CEREAL_NVP(action), CEREAL_NVP(tick), CEREAL_NVP(way), CEREAL_NVP(x), CEREAL_NVP(y), CEREAL_NVP(garbage) );
109 	}
110 };
111 
112 struct BlockGameInfoExtra {
113 	std::string name;
114 	int score = 0;
115 	int seconds = 0;
116 	template <class Archive>
serializeBlockGameInfoExtra117 	void serialize( Archive & ar )
118 	{
119 		ar( CEREAL_NVP(name), CEREAL_NVP(score), CEREAL_NVP(seconds) );
120 	}
121 };
122 
123 struct BlockGameInfo {
124 	BlockGameStartInfo startInfo;
125 	std::deque<BlockGameAction> actions;
126 	BlockGameInfoExtra extra;
127 	template <class Archive>
serializeBlockGameInfo128 	void serialize( Archive & ar )
129 	{
130 		ar( CEREAL_NVP(startInfo), CEREAL_NVP(actions), CEREAL_NVP(extra) );
131 	}
132 };
133 
134 ////////////////////////////////////////////////////////////////////////////////
135 //The BloackGame class represents a board, score, time etc. for a single player/
136 ////////////////////////////////////////////////////////////////////////////////
137 class BlockGame
138 {
139 private:
140 	int prevTowerHeight = 0;
141 	bool bGarbageFallLeft = false;
142 	bool singlePuzzle = false;
143 
144 	int nextGarbageNumber = 0;
145 	int pushedPixelAt = 0;
146 	int nrPushedPixel = 0;
147 	int nrFellDown = 0;
148 	unsigned int nrStops = 0;
149 	bool garbageToBeCleared[7][30] = {};
150 	unsigned int lastAImove = 0;
151 
152 	int AI_LineOffset = 0; //how many lines have changed since command
153 	int hangTicks = 0;    //How many times have hang been decreased?
154 	//int the two following index 0 may NOT be used (what the fuck did I meen?)
155 	int chainSize[NUMBEROFCHAINS]{}; //Contains the chains
156 	bool chainUsed[NUMBEROFCHAINS]{};   //True if the chain is used
157 
158 	unsigned int nextRandomNumber = 0;
159 	int Level = 0; //Only used in stageClear and puzzle (not implemented)
160 
161 	BlockGameInfo replayInfo;
162 
163 	int rand2();
164 	int firstUnusedChain();
165 
166 protected:
167 	int lastCounter = 0;
168 	std::string strHolder;
169 	bool bDraw = false;
170 	unsigned int ticks = 0;
171 	unsigned int gameStartedAt = 0;
172 	unsigned int gameEndedAfter = 0;		//How long did the game last?
173 	int linesCleared = 0;
174 	int TowerHeight = 0;
175 	int board[7][30];
176 	int stop = 0;
177 	int speedLevel = 0;
178 	int pixels = 0;
179 	int MovesLeft = 0;
180 	bool timetrial = false;
181 	bool stageClear = false;
182 	bool vsMode = false;
183 	bool puzzleMode = false;
184 	int stageClearLimit = 0; //stores number of lines user must clear to win
185 	int combo = 0;
186 	int chain = 0;
187 	int cursorx = 2; //stores cursor position
188 	int cursory = 3; // -||-
189 	int mouse_cursorx = -1;  //Stores the mouse hold cursor. -1 if nothing is selected.
190 	int mouse_cursory = -1;
191 	double speed = 0.0;
192 	double baseSpeed = 0.0; //factor for speed. Lower value = faster gameplay
193 	int score = 0;
194 	bool bGameOver = false;
195 	bool hasWonTheGame = false;
196 	int AI_MoveSpeed = 0;   //How often will the computer move? milliseconds
197 	bool AI_Enabled = false;
198 	bool recordStats = true;
199 	bool vsAI = false;  //Set to true for single player vs
200 
201 	int handicap = 0;
202 
203 	std::vector<GarbageStruct> garbageSendQueue;
204 
205 	int AIlineToClear = 0;
206 
207 	short AIstatus = 0;   //Status flags:
208 	//0: nothing, 2: clear tower, 3: clear horisontal, 4: clear vertical
209 	//1: make more lines, 5: make 2 lines, 6: make 1 line
210 
211 public:
212 
213 	std::string name;
214 
215 public:
216 	BlockGame();
217 	virtual ~BlockGame() = default;
218 
219 	int getAIlevel()  const;
220 
AddText(int,int,unsigned int,int) const221 	virtual void AddText(int, int, unsigned int, int) const  {}
AddBall(int,int,bool,int) const222 	virtual void AddBall(int, int, bool, int) const  {}
AddExplosion(int,int) const223 	virtual void AddExplosion(int, int) const  {}
PlayerWonEvent() const224 	virtual void PlayerWonEvent() const  {}
DrawEvent() const225 	virtual void DrawEvent() const {}
BlockPopEvent() const226 	virtual void BlockPopEvent() const  {}
LongChainDoneEvent() const227 	virtual void LongChainDoneEvent() const  {}
TimeTrialEndEvent() const228 	virtual void TimeTrialEndEvent() const  {}
EndlessHighscoreEvent() const229 	virtual void EndlessHighscoreEvent() const  {}
230 
231 	void NewGame(const BlockGameStartInfo &s);
232 	void DoAction (const BlockGameAction& action);
233 	/**
234 	 * This function returns all the garbage.
235 	 * This is actual const in the way that it does not change the games state
236 	 * Technically it is not const because it empties the queue that are stored inside the object even if not part of the game state.
237 	 * @param poppedData
238 	 */
239 	void PopSendGarbage(std::vector<GarbageStruct>& poppedData);
240 
241 	int GetScore() const;
242 	int GetHandicap() const;
243 	bool isGameOver() const;
244 	int GetGameStartedAt() const;
245 	int GetGameEndedAt() const;
246 	bool isTimeTrial() const;
247 	bool isStageClear() const;
248 	bool isVsMode() const;
249 	bool isPuzzleMode() const;
250 	int GetLinesCleared() const;
251 	int GetStageClearLimit() const;
252 	int GetChains() const;
253 	int GetPixels() const;
254 	int GetSpeedLevel() const;
255 	int GetTowerHeight() const;
256 	int GetCursorX() const;
257 	int GetCursorY() const;
258 	void GetMouseCursor(bool& pressed, int& x, int&y) const;
259 	bool GetIsWinner() const;
260 	bool isSinglePuzzle() const;
261 	int getLevel() const;
262 	bool GetAIenabled() const;
263 	bool IsNearDeath() const;
GetBaseSpeed() const264 	double GetBaseSpeed() const { return baseSpeed; }
GetBlockGameInfo()265 	const BlockGameInfo& GetBlockGameInfo() {
266 		return replayInfo;
267 	}
268 private:
269 	void NewGameInternal(unsigned int ticks);
270 	//Test if LineNr is an empty line, returns false otherwise.
271 	bool LineEmpty(int lineNr) const;
272 	//Test if the entire board is empty (used for Puzzles)
273 	bool BoardEmpty() const;
274 	//Anything that the user can't move? In that case Game Over cannot occur
275 	bool hasStaticContent() const;
276 	void putStartBlocks();
277 	void putStartBlocks(int n);
278 	//decreases hang for all hanging blocks and wait for waiting blocks
279 	void ReduceStuff();
280 	void setGameSpeed(int globalSpeedLevel);
281 	void setHandicap(int globalHandicap);
282 	//Clears garbage, must take one the lower left corner!
283 	int GarbageClearer(int x, int y, int number, bool aLineToClear, int chain);
284 	//Marks garbage that must be cleared
285 	int GarbageMarker(int x, int y);
286 	int FirstGarbageMarker(int x, int y);
287 	//Clear Blocks if 3 or more is alligned (naive implemented)
288 	void ClearBlocks();
289 	//Moves all peaces a spot down if possible
290 	int FallBlock(int x, int y, int number);
291 	//Makes all Garbage fall one spot
292 	void GarbageFall();
293 	//Makes the blocks fall (it doesn't test time, this must be done before hand)
294 	void FallDown();
295 	//Pushes a single pixel, so it appears to scrool
296 	void PushPixels();
297 	//See how high the tower is, saved in integer TowerHeight
298 	void FindTowerHeight();
299 	//Generates a new line and moves the field one block up (restart puzzle mode)
300 	void PushLine();
301 	//prints "Game Over" and ends game
302 	void SetGameOver();
303 	//Moves the cursor, receaves N,S,E or W as a char an moves as desired
304 	void MoveCursor(char way);
305 	//switches the two blocks at the cursor position, unless game over
306 	void SwitchAtCursor();
307 	//Creates garbage using a given wide and height
308 	bool CreateGarbage(int wide, int height);
309 	//Creates garbage using a given wide and height
310 	bool CreateGreyGarbage();
311 	void MouseDown(int x, int y);  //Send then the mouse is pressed
312 	void MouseMove(int x);  //Send then the mouse moves
313 	void MouseUp();  //Send then the mouse goes up
314 	void MoveCursorTo(int x, int y);
315 	void FinalizeBlockGameInfo();
316 ///////////////////////////////////////////////////////////////////////////
317 /////////////////////////// AI starts here! ///////////////////////////////
318 ///////////////////////////////////////////////////////////////////////////
319 	//First the helpet functions:
320 	int nrOfType(int line, int type);
321 	int AIcolorToClear = 0;
322 	//See if a combo can be made in this line
323 	int horiInLine(int line);
324 	bool horiClearPossible();
325 	//the Line Has Unmoveable Objects witch might stall the AI
326 	bool lineHasGarbage(int line);
327 	//Types 0..6 in line
328 	int nrOfRealTypes(int line);
329 	//See if there is a tower
330 	bool ThereIsATower();
331 	double firstInLine1(int line);
332 	//returns the first coordinate of the block of type
333 	double firstInLine(int line, int type);
334 	//There in the line shall we move
335 	int closestTo(int line, int place);
336 	//The AI will remove a tower
337 	void AI_ClearTower();
338 	//The AI will try to clear block horisontally
339 	void AI_ClearHori();
340 	//Test if vertical clear is possible
341 	bool veriClearPossible();
342 	//There in the line shall we move
343 	int closestTo(int line, int type, int place);
344 	//The AI will try to clear blocks vertically
345 	void AI_ClearVertical();
346 	bool firstLineCreated = 0;
347 	void AI_Move();
348 //////////////////////////////////////////////////////////////////
349 ///////////////////////////// AI ends here! //////////////////////
350 //////////////////////////////////////////////////////////////////
351 	//Set the move speed of the AI based on the aiLevel parameter
352 	void setAIlevel(int aiLevel);
353 	void PushLineInternal();
354 	//Prints "winner" and ends game
355 	void setPlayerWon();
356 	//Prints "draw" and ends the game
357 	void setDraw();
358 	//Updates evrything, if not called nothing happends
359 	void Update();
360 	void UpdateInternal(unsigned int newtick);
361 };
362 
363 //Play the next level
364 void nextLevel(BlockGame& g, unsigned int ticks);
365 //Replay the current level
366 void retryLevel(BlockGame& g, unsigned int ticks);
367 
368 #endif
369