Skip to content

Commit c496f9e

Browse files
author
Alex Ames
authored
Merge pull request firebase#51 from Grant-Postma/05-bug-fixes
Miscellaneous bug fixes.
2 parents 85cdb87 + f0be98f commit c496f9e

File tree

9 files changed

+180
-66
lines changed

9 files changed

+180
-66
lines changed

demos/TicTacToe/Classes/AppDelegate.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
#include "TicTacToeScene.h"
55
USING_NS_CC;
66

7+
// Set based on the image width.
78
const float kFrameWidth = 600;
8-
const float kFrameHeight = 600;
9+
// Set based on the image height plus 40 for windows bar.
10+
const float kFrameHeight = 640;
911

1012
AppDelegate::AppDelegate() {}
1113

demos/TicTacToe/Classes/MainMenuScene.cpp

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22

33
#include "TicTacToeScene.h"
44

5+
static const char* kCreateGameImage = "create_game.png";
6+
static const char* kTextFieldBorderImage = "text_field_border.png";
7+
static const char* kJoinButtonImage = "join_game.png";
8+
59
USING_NS_CC;
610

711
Scene* MainMenuScene::createScene() {
812
// Builds a simple scene that uses the bottom left cordinate point as (0,0)
913
// and can have sprites, labels and layers added onto it.
1014
auto scene = Scene::create();
1115
auto layer = MainMenuScene::create();
12-
1316
scene->addChild(layer);
1417

1518
return scene;
@@ -21,8 +24,9 @@ bool MainMenuScene::init() {
2124
}
2225
// Creates a sprite for the create button and sets its position to the center
2326
// of the screen. TODO(grantpostma): Dynamically choose the location.
24-
auto create_button = Sprite::create("create_game.png");
25-
create_button->setPosition(300, 350);
27+
auto create_button = Sprite::create(kCreateGameImage);
28+
create_button->setPosition(25, 200);
29+
create_button->setAnchorPoint(Vec2(0, 0));
2630
// Create a button listener to handle the touch event.
2731
auto create_button_touch_listener = EventListenerTouchOneByOne::create();
2832
// Setting the onTouchBegan event up to a lambda tha will replace the MainMenu
@@ -45,17 +49,21 @@ bool MainMenuScene::init() {
4549
->getEventDispatcher()
4650
->addEventListenerWithSceneGraphPriority(create_button_touch_listener,
4751
create_button);
48-
4952
// Creating, setting the position and assigning a placeholder to the text
5053
// field for entering the join game uuid.
5154
TextFieldTTF* join_text_field =
5255
cocos2d::TextFieldTTF::textFieldWithPlaceHolder(
53-
"Join Game url", cocos2d::Size(400, 200), TextHAlignment::RIGHT,
54-
"Arial", 42.0);
55-
join_text_field->setPosition(100, 100);
56-
join_text_field->setColorSpaceHolder(Color3B::GRAY);
56+
"code", cocos2d::Size(200, 100), TextHAlignment::LEFT, "Arial", 55.0);
57+
join_text_field->setPosition(420, 45);
58+
join_text_field->setAnchorPoint(Vec2(0, 0));
59+
join_text_field->setColorSpaceHolder(Color3B::WHITE);
5760
join_text_field->setDelegate(this);
5861

62+
auto text_field_border = Sprite::create(kTextFieldBorderImage);
63+
text_field_border->setPosition(390, 50);
64+
text_field_border->setAnchorPoint(Vec2(0, 0));
65+
text_field_border->setScale(.53f);
66+
this->addChild(text_field_border, 0);
5967
// Create a touch listener to handle the touch event. TODO(grantpostma): add a
6068
// focus bar when selecting inside the text field's bounding box.
6169
auto text_field_touch_listener = EventListenerTouchOneByOne::create();
@@ -68,8 +76,13 @@ bool MainMenuScene::init() {
6876
// Show the on screen keyboard and places character inputs into the text
6977
// field.
7078
auto str = join_text_field->getString();
71-
auto textField = dynamic_cast<TextFieldTTF*>(event->getCurrentTarget());
72-
textField->attachWithIME();
79+
auto text_field = dynamic_cast<TextFieldTTF*>(event->getCurrentTarget());
80+
text_field->setCursorEnabled(true);
81+
text_field->attachWithIME();
82+
} else {
83+
auto text_field = dynamic_cast<TextFieldTTF*>(event->getCurrentTarget());
84+
text_field->setCursorEnabled(false);
85+
text_field->detachWithIME();
7386
}
7487

7588
return true;
@@ -82,10 +95,12 @@ bool MainMenuScene::init() {
8295
join_text_field);
8396

8497
// Creates a sprite for the join button and sets its position to the center
85-
// of the screen. TODO(grantpostma): Dynamically choose the location.
86-
auto join_button = Sprite::create("join_game.png");
87-
join_button->setPosition(450, 100);
88-
98+
// of the screen. TODO(grantpostma): Dynamically choose the location and set
99+
// size().
100+
auto join_button = Sprite::create(kJoinButtonImage);
101+
join_button->setPosition(25, 50);
102+
join_button->setAnchorPoint(Vec2(0, 0));
103+
join_button->setScale(1.3f);
89104
// Create a button listener to handle the touch event.
90105
auto join_button_touch_listener = EventListenerTouchOneByOne::create();
91106
// Setting the onTouchBegan event up to a lambda tha will replace the MainMenu
@@ -95,11 +110,14 @@ bool MainMenuScene::init() {
95110
auto bounds = event->getCurrentTarget()->getBoundingBox();
96111
auto point = touch->getLocation();
97112
if (bounds.containsPoint(point)) {
98-
// Getting and converting the join_text_field string to a char*.
113+
// Getting the string from join_text_field.
99114
std::string join_text_field_string = join_text_field->getString();
100-
101-
Director::getInstance()->replaceScene(
102-
TicTacToe::createScene(join_text_field_string));
115+
if (join_text_field_string.length() == 4) {
116+
Director::getInstance()->replaceScene(
117+
TicTacToe::createScene(join_text_field_string));
118+
} else {
119+
join_text_field->setString("");
120+
}
103121
}
104122
return true;
105123
};
@@ -112,7 +130,7 @@ bool MainMenuScene::init() {
112130
// MainMenu scene.
113131
this->addChild(create_button);
114132
this->addChild(join_button);
115-
this->addChild(join_text_field);
133+
this->addChild(join_text_field, 1);
116134

117135
return true;
118136
}

demos/TicTacToe/Classes/TicTacToeLayer.cpp

Lines changed: 123 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@ static const int kEmptyTile = -1;
3333
static const int kPlayerOne = 0;
3434
static const int kPlayerTwo = 1;
3535
static const int kNumberOfPlayers = 2;
36+
37+
// End game outcomes.
38+
static const enum kGameOutcome {
39+
kGameWon = 0,
40+
kGameLost,
41+
kGameTied,
42+
kGameDisbanded
43+
};
44+
static const std::array<string, 4> kGameOverStrings = {
45+
"you won!", "you lost.", "you tied.", "user left."};
46+
3647
// Game board dimensions.
3748
extern const int kTilesX;
3849
extern const int kTilesY;
@@ -46,6 +57,7 @@ static const double kTileHeight = (kScreenHeight / kTilesY);
4657
static const int kEndGameFramesMax = 120;
4758
// Image file paths.
4859
static const char* kBoardImageFileName = "tic_tac_toe_board.png";
60+
static const char* kLeaveButtonFileName = "leave_button.png";
4961
static std::array<const char*, kNumberOfPlayers> kPlayerTokenFileNames = {
5062
"tic_tac_toe_x.png", "tic_tac_toe_o.png"};
5163

@@ -245,19 +257,14 @@ static bool GameOver(int board[][kTilesY]) {
245257
TicTacToeLayer::TicTacToeLayer(string game_uuid) {
246258
join_game_uuid = game_uuid;
247259
current_player_index = kPlayerOne;
260+
game_outcome = kGameWon;
248261
LogMessage("Initialized Firebase App.");
249262
auto app = ::firebase::App::Create();
250263
LogMessage("Initialize Firebase Auth and Firebase Database.");
251264
// Use ModuleInitializer to initialize both Auth and Database, ensuring no
252265
// dependencies are missing.
253266
firebase::ModuleInitializer initializer;
254267

255-
/// Firebase Auth, used for logging into Firebase.
256-
firebase::auth::Auth* auth;
257-
258-
/// Firebase Realtime Database, the entry point to all database operations.
259-
firebase::database::Database* database;
260-
261268
database = nullptr;
262269
auth = nullptr;
263270
void* initialize_targets[] = {&auth, &database};
@@ -326,32 +333,46 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid) {
326333
ref.Child("total_players").SetValue(1);
327334
future_current_player_index =
328335
ref.Child("current_player_index").SetValue(kPlayerOne);
336+
future_game_over = ref.Child("game_over").SetValue(false);
337+
WaitForCompletion(future_game_over, "setGameOver");
329338
WaitForCompletion(future_current_player_index, "setCurrentPlayerIndex");
330339
WaitForCompletion(future_create_game, "createGame");
331340
player_index = kPlayerOne;
332341
awaiting_opponenet_move = false;
333342
} else {
334-
ref = database->GetReference("game_data").Child(join_game_uuid);
335-
auto fIncrementTotalUsers = ref.RunTransaction([](MutableData* data) {
336-
auto total_players = data->Child("total_players").value();
337-
// Completing the transaction based on the returned mutable data value.
338-
if (total_players.is_null()) {
339-
// Must return this if the transaction was unsuccessful.
340-
return TransactionResult::kTransactionResultAbort;
341-
}
342-
int new_total_players = total_players.int64_value() + 1;
343-
if (new_total_players > kNumberOfPlayers) {
344-
// Must return this if the transaction was unsuccessful.
345-
return TransactionResult::kTransactionResultAbort;
346-
}
347-
data->Child("total_players").set_value(new_total_players);
348-
// Must call this if the transaction was successful.
349-
return TransactionResult::kTransactionResultSuccess;
350-
});
351-
WaitForCompletion(fIncrementTotalUsers, "JoinGameTransaction");
352-
353-
player_index = kPlayerTwo;
354-
awaiting_opponenet_move = true;
343+
// Checks whether the join_uuid map exists. If it does not it set the
344+
// initialization to failed.
345+
auto future_game_uuid =
346+
database->GetReference("game_data").Child(join_game_uuid).GetValue();
347+
WaitForCompletion(future_game_uuid, "GetGameDataMap");
348+
auto game_uuid_snapshot = future_game_uuid.result();
349+
if (!game_uuid_snapshot->value().is_map()) {
350+
initialization_failed = true;
351+
} else {
352+
ref = database->GetReference("game_data").Child(join_game_uuid);
353+
auto future_increment_total_users =
354+
ref.RunTransaction([](MutableData* data) {
355+
auto total_players = data->Child("total_players").value();
356+
// Completing the transaction based on the returned mutable data
357+
// value.
358+
if (total_players.is_null()) {
359+
// Must return this if the transaction was unsuccessful.
360+
return TransactionResult::kTransactionResultAbort;
361+
}
362+
int new_total_players = total_players.int64_value() + 1;
363+
if (new_total_players > kNumberOfPlayers) {
364+
// Must return this if the transaction was unsuccessful.
365+
return TransactionResult::kTransactionResultAbort;
366+
}
367+
data->Child("total_players").set_value(new_total_players);
368+
// Must call this if the transaction was successful.
369+
return TransactionResult::kTransactionResultSuccess;
370+
});
371+
WaitForCompletion(future_increment_total_users, "JoinGameTransaction");
372+
373+
player_index = kPlayerTwo;
374+
awaiting_opponenet_move = true;
375+
}
355376
}
356377

357378
// Creating the board sprite , setting the position to the bottom left of the
@@ -365,6 +386,48 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid) {
365386
board_sprite->setPosition(0, 0);
366387
board_sprite->setAnchorPoint(Vec2(0.0, 0.0));
367388

389+
leave_button_sprite = Sprite::create(kLeaveButtonFileName);
390+
if (!leave_button_sprite) {
391+
log("kLeaveButtonSprite: %s file not found.", kLeaveButtonFileName);
392+
exit(true);
393+
}
394+
leave_button_sprite->setPosition(450, 585);
395+
leave_button_sprite->setAnchorPoint(Vec2(0.0, 0.0));
396+
leave_button_sprite->setScale(.35);
397+
398+
// Create a button listener to handle the touch event.
399+
auto leave_button_sprite_touch_listener =
400+
EventListenerTouchOneByOne::create();
401+
// Setting the onTouchBegan event up to a lambda will swap scenes and modify
402+
// total_players
403+
leave_button_sprite_touch_listener->onTouchBegan =
404+
[this](Touch* touch, Event* event) -> bool {
405+
auto bounds = event->getCurrentTarget()->getBoundingBox();
406+
auto point = touch->getLocation();
407+
// Replaces the scene with a new TicTacToe scene if the touched point is
408+
// within the bounds of the button.
409+
if (bounds.containsPoint(point)) {
410+
// Update the game_outcome to reflect if the you rage quit or left
411+
// pre-match.
412+
if (remaining_tiles.size() == kNumberOfTiles) {
413+
game_outcome = kGameDisbanded;
414+
} else {
415+
game_outcome = kGameLost;
416+
}
417+
418+
WaitForCompletion(ref.Child("game_over").SetValue(true), "setGameOver");
419+
}
420+
421+
return true;
422+
};
423+
// Attaching the touch listener to the create game button.
424+
Director::getInstance()
425+
->getEventDispatcher()
426+
->addEventListenerWithSceneGraphPriority(
427+
leave_button_sprite_touch_listener, leave_button_sprite);
428+
429+
board_sprite->addChild(leave_button_sprite, 1);
430+
368431
// TODO(grantpostma@): Modify these numbers to be based on the extern window
369432
// size & label size dimensions.
370433
cocos2d::Label* game_uuid_label =
@@ -385,10 +448,13 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid) {
385448
LogMessage("total_player_listener");
386449
total_player_listener =
387450
std::make_unique<ExpectValueListener>(kNumberOfPlayers);
451+
game_over_listener = std::make_unique<ExpectValueListener>(true);
452+
388453
current_player_index_listener = std::make_unique<SampleValueListener>();
389454
last_move_listener = std::make_unique<SampleValueListener>();
390455
LogMessage("%i", total_player_listener->got_value());
391456
ref.Child("total_players").AddValueListener(total_player_listener.get());
457+
ref.Child("game_over").AddValueListener(game_over_listener.get());
392458
ref.Child("current_player_index")
393459
.AddValueListener(current_player_index_listener.get());
394460
ref.Child("last_move").AddValueListener(last_move_listener.get());
@@ -436,20 +502,20 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid) {
436502
current_player_index;
437503
remaining_tiles.erase(selected_tile);
438504
current_player_index = (current_player_index + 1) % kNumberOfPlayers;
439-
fLastMove = ref.Child("last_move").SetValue(selected_tile);
505+
future_last_move = ref.Child("last_move").SetValue(selected_tile);
440506
future_current_player_index =
441507
ref.Child("current_player_index").SetValue(current_player_index);
442-
WaitForCompletion(fLastMove, "setLastMove");
508+
WaitForCompletion(future_last_move, "setLastMove");
443509
WaitForCompletion(future_current_player_index, "setCurrentPlayerIndex");
444510
awaiting_opponenet_move = true;
445511
waiting_label->setString("waiting");
446512
if (GameOver(board)) {
447513
// Set game_over_label to reflect the use won.
448514
game_over_label->setString("you won!");
449515
} else if (remaining_tiles.size() == 0) {
450-
// Set game_over_label to reflect the game ended in a tie.
451-
game_over_label->setString("you tied.");
452-
// Changing back to the main menu scene.
516+
// Update game_outcome to reflect the user tied.
517+
WaitForCompletion(ref.Child("game_over").SetValue(true), "setGameOver");
518+
game_outcome = kGameTied;
453519
}
454520
}
455521
return true;
@@ -466,13 +532,9 @@ TicTacToeLayer::TicTacToeLayer(string game_uuid) {
466532

467533
// Called automatically every frame. The update is scheduled in constructor.
468534
void TicTacToeLayer::update(float /*delta*/) {
469-
// Shows the end game label for kEndGameFramesMax to show the result of the
470-
// game.
471-
if (game_over_label->getString().empty() == false) {
472-
end_game_frames++;
473-
if (end_game_frames > kEndGameFramesMax) {
474-
Director::getInstance()->replaceScene(MainMenuScene::createScene());
475-
}
535+
// Replacing the scene with MainMenuScene if the initialization fails.
536+
if (initialization_failed == true) {
537+
Director::getInstance()->replaceScene(MainMenuScene::createScene());
476538
}
477539
// Performs the actions of the other player when the
478540
// current_player_index_listener is equal to the player index.
@@ -501,11 +563,29 @@ void TicTacToeLayer::update(float /*delta*/) {
501563
awaiting_opponenet_move = false;
502564
current_player_index = player_index;
503565
if (GameOver(board)) {
504-
// Set game_over_label to reflect the use lost.
505-
game_over_label->setString("you lost.");
566+
// Set game_outcome to reflect the use lost.
567+
game_outcome = kGameLost;
506568
} else if (remaining_tiles.size() == 0) {
507-
// Set game_over_label to reflect the game ended in a tie.
508-
game_over_label->setString("you tied.");
569+
// Set game_outcome to reflect the game ended in a tie.
570+
game_outcome = kGameTied;
571+
}
572+
}
573+
// Shows the end game label for kEndGameFramesMax to show the result of the
574+
// game.
575+
else if (game_over_listener->got_value()) {
576+
if (game_outcome == kGameDisbanded &&
577+
remaining_tiles.size() != kNumberOfTiles) {
578+
game_outcome = kGameWon;
579+
}
580+
game_over_label->setString(kGameOverStrings[game_outcome]);
581+
end_game_frames++;
582+
if (end_game_frames > kEndGameFramesMax) {
583+
// TODO(grantpostma): Update authenticated users record.
584+
WaitForCompletion(database->GetReference("game_data")
585+
.Child(join_game_uuid)
586+
.RemoveValue(),
587+
"removeGameUuid");
588+
Director::getInstance()->replaceScene(MainMenuScene::createScene());
509589
}
510590
}
511591
// Updates the waiting label to signify it is this players move.

0 commit comments

Comments
 (0)