board.cpp 16.5 KB
Newer Older
魔大农's avatar
魔大农 committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/* Quatter
// Copyright (C) 2016 LucKey Productions (luckeyproductions.nl)
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

魔大农's avatar
魔大农 committed
19
#include "board.h"
20
#include "effectmaster.h"
魔大农's avatar
魔大农 committed
21
#include "quattercam.h"
魔大农's avatar
魔大农 committed
22

魔大农's avatar
魔大农 committed
23 24 25 26 27 28 29 30
namespace Urho3D {
template <> unsigned MakeHash(const IntVector2& value)
  {
    return LucKey::IntVector2ToHash(value);
  }
}

Board::Board(): Object(MC->GetContext()),
31
    indicateSingle_{false},
32
    squares_{},
魔大农's avatar
魔大农 committed
33 34
    selectedSquare_{nullptr},
    lastSelectedSquare_{nullptr}
魔大农's avatar
魔大农 committed
35
{
魔大农's avatar
魔大农 committed
36
    rootNode_ = MC->world_.scene_->CreateChild("Board");
37 38
    StringVector tag{}; tag.Push(String("Board"));
    rootNode_->SetTags(tag);
39
    model_ = rootNode_->CreateComponent<StaticModel>();
魔大农's avatar
魔大农 committed
40 41
    model_->SetModel(MC->GetModel("Board"));
    model_->SetMaterial(MC->GetMaterial("Board"));
魔大农's avatar
魔大农 committed
42
    model_->SetCastShadows(true);
魔大农's avatar
魔大农 committed
43

44
    //Fill board with squares
魔大农's avatar
魔大农 committed
45
    for (int i{0}; i < BOARD_WIDTH; ++i) for (int j{0}; j < BOARD_HEIGHT; ++j){
魔大农's avatar
魔大农 committed
46
        //Create base
魔大农's avatar
魔大农 committed
47 48 49
        Square* square{new Square};
        square->coords_ = IntVector2(i, j);
        square->node_ = rootNode_->CreateChild("Square");
魔大农's avatar
魔大农 committed
50 51
        StringVector tag{}; tag.Push(String("Square"));
        square->node_->SetTags(tag);
魔大农's avatar
魔大农 committed
52
        square->node_->SetPosition(CoordsToPosition(square->coords_));
魔大农's avatar
魔大农 committed
53 54 55
        StaticModel* touchPlane{square->node_->CreateComponent<StaticModel>()};
        touchPlane->SetModel(MC->GetModel("Plane"));
        touchPlane->SetMaterial(MC->GetMaterial("Invisible"));
魔大农's avatar
魔大农 committed
56 57
        square->free_ = true;
        square->piece_ = nullptr;
魔大农's avatar
魔大农 committed
58
        //Create slot
魔大农's avatar
魔大农 committed
59 60 61 62 63 64
        Node* slotNode{square->node_->CreateChild("Slot")};
        slotNode->SetPosition(Vector3::UP * 0.05f);
        square->slot_ = slotNode->CreateComponent<AnimatedModel>();
        square->slot_->SetModel(MC->GetModel("Slot"));
        square->slot_->SetMaterial(MC->GetMaterial("Glow")->Clone());
        square->slot_->GetMaterial()->SetShaderParameter("MatDiffColor", Color(0.0f, 0.0f, 0.0f, 0.0f));
魔大农's avatar
魔大农 committed
65
        //Create light
魔大农's avatar
魔大农 committed
66 67 68
        Node* lightNode{slotNode->CreateChild("Light")};
        lightNode->SetPosition(Vector3::UP * 0.23f);
        square->light_ = lightNode->CreateComponent<Light>();
69
        square->light_->SetColor(0.5f * COLOR_GLOW);
魔大农's avatar
魔大农 committed
70
        square->light_->SetBrightness(0.023f);
魔大农's avatar
魔大农 committed
71 72 73 74 75 76
        square->light_->SetRange(2.0f);
        square->light_->SetCastShadows(false);

        squares_[square->coords_] = square;

    }
77

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
    //Create Quatter indicators
    for (int i{0}; i < 6; ++i){
        //Create base
        Indicator* indicator{new Indicator};
        indicator->rootNode_ = rootNode_->CreateChild("Indicator");
        indicator->rootNode_->SetPosition(GetThickness() * Vector3::UP);
        if (i == 1)
            indicator->rootNode_->Rotate(Quaternion(90.0f, Vector3::UP));
        else if (i == 2)
            indicator->rootNode_->Rotate(Quaternion(45.0f, Vector3::UP));
        else if (i == 3)
            indicator->rootNode_->Rotate(Quaternion(-45.0f, Vector3::UP));
        else if (i == 5)
            indicator->rootNode_->Rotate(Quaternion(90.0f, Vector3::UP));

        indicator->glow_ = MC->GetMaterial("Glow")->Clone();
        //Create models
        Node* arrowNode1{indicator->rootNode_->CreateChild("Arrow")};
        arrowNode1->SetPosition(Vector3::LEFT * (2.3f + 0.95f * (i == 2 || i == 3)) + Vector3::FORWARD * (i >= 4));
        arrowNode1->Rotate(Quaternion(-90.0f, Vector3::UP));
        indicator->model1_ = arrowNode1->CreateComponent<AnimatedModel>();
99
        if (i < 4){
100
            indicator->model1_->SetModel(MC->GetModel("Arrow"));
101 102
            indicator->model1_->SetMorphWeight(0, static_cast<float>(i < 2));
        } else {
103
            indicator->model1_->SetModel(MC->GetModel("BlockIndicator"));
104
        }
105 106 107 108 109
        indicator->model1_->SetMaterial(indicator->glow_);
        Node* arrowNode2{indicator->rootNode_->CreateChild("Arrow")};
        arrowNode2->SetPosition(Vector3::RIGHT * (2.3f + 0.95f * (i == 2 || i == 3)));
        arrowNode2->Rotate(Quaternion(90.0f, Vector3::UP));
        indicator->model2_ = arrowNode2->CreateComponent<AnimatedModel>();
110
        if (i < 4){
111
            indicator->model2_->SetModel(MC->GetModel("Arrow"));
112 113
            indicator->model2_->SetMorphWeight(0, static_cast<float>(i < 2));
        } else {
114
            indicator->model2_->SetModel(MC->GetModel("BlockIndicator"));
115
        }
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
        indicator->model2_->SetMaterial(indicator->glow_);
        indicator->model2_->GetMaterial()->SetShaderParameter("MatDiffColor", Color(0.0f, 0.0f, 0.0f, 0.0f));
        //Create light
        Node* lightNode1{arrowNode1->CreateChild("Light")};
        lightNode1->SetPosition(Vector3::UP * 0.23f);
        indicator->light1_ = lightNode1->CreateComponent<Light>();
        indicator->light1_->SetColor(COLOR_GLOW);
        indicator->light1_->SetBrightness(0.023f);
        indicator->light1_->SetRange(2.0f);
        indicator->light1_->SetCastShadows(false);
        Node* lightNode2{arrowNode2->CreateChild("Light")};
        lightNode2->SetPosition(Vector3::UP * 0.23f);
        indicator->light2_ = lightNode2->CreateComponent<Light>();
        indicator->light2_->SetColor(COLOR_GLOW);
        indicator->light2_->SetBrightness(0.023f);
        indicator->light2_->SetRange(2.0f);
        indicator->light2_->SetCastShadows(false);

        indicators_.Push(SharedPtr<Indicator>(indicator));

    }

138
    SubscribeToEvent(E_SCENEUPDATE, URHO3D_HANDLER(Board, HandleSceneUpdate));
魔大农's avatar
魔大农 committed
139
}
魔大农's avatar
魔大农 committed
140 141 142
void Board::Reset()
{
    for (Square* s: squares_.Values()){
魔大农's avatar
魔大农 committed
143

魔大农's avatar
魔大农 committed
144 145 146
        s->free_ = true;
        s->piece_ = nullptr;
        s->light_->SetEnabled(true);
魔大农's avatar
魔大农 committed
147

魔大农's avatar
魔大农 committed
148
    }
魔大农's avatar
魔大农 committed
149

魔大农's avatar
魔大农 committed
150 151
    DeselectAll();
}
魔大农's avatar
魔大农 committed
152

魔大农's avatar
魔大农 committed
153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
void Board::Refuse()
{
    if (selectedSquare_){
        Material* glow{selectedSquare_->slot_->GetMaterial()};
        glow->SetShaderParameter("MatDiffColor", Color(1.0f, 0.0f, 0.0f, 1.0f));
        if (selectedSquare_->free_)
            FX->FadeTo(glow, COLOR_GLOW, 0.23f);
        else
            FX->FadeTo(glow,Color(1.0f, 0.8f, 0.0f, 0.5f), 0.23f);
    }
}

bool Board::IsEmpty() const
{
    for (Square* s: squares_.Values())
        if (!s->free_) return false;

    return true;
}
魔大农's avatar
魔大农 committed
172 173 174 175 176 177 178
bool Board::IsFull() const
{
    for (Square* s: squares_.Values())
        if (s->free_) return false;

    return true;
}
魔大农's avatar
魔大农 committed
179 180

Vector3 Board::CoordsToPosition(IntVector2 coords)
魔大农's avatar
魔大农 committed
181 182
{
    return Vector3(0.5f + coords.x_ - BOARD_WIDTH/2,
183
                   GetThickness(),
魔大农's avatar
魔大农 committed
184 185 186
                   0.5f + coords.y_ - BOARD_HEIGHT/2);
}

187 188 189
void Board::HandleSceneUpdate(StringHash eventType, VariantMap& eventData)
{
    for (Square* s: squares_.Values()){
魔大农's avatar
魔大农 committed
190

191
        s->slot_->SetMorphWeight(0, MC->Sine(2.3f, 0.0f, 1.0f));
魔大农's avatar
魔大农 committed
192

193 194 195
    }
}

魔大农's avatar
魔大农 committed
196 197
bool Board::PutPiece(Piece* piece, Square* square)
{
魔大农's avatar
魔大农 committed
198 199 200 201
    if (!square){
        return PutPiece(piece);
    }

魔大农's avatar
魔大农 committed
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
    if (piece && square->free_){

        MC->DeselectPiece();

        square->piece_ = piece;
        square->free_ = false;
        square->light_->SetEnabled(false);

        piece->Put(square->node_->GetWorldPosition()
                   + Vector3(Random(-0.05f, 0.05f),
                             0.0f,
                             Random(-0.05f, 0.05f)));
        DeselectAll();
        if (CheckQuatter())
            MC->Quatter();

魔大农's avatar
魔大农 committed
218
        MC->NextPhase();
魔大农's avatar
魔大农 committed
219 220
        return true;

魔大农's avatar
魔大农 committed
221 222 223 224
    } else {
        Refuse();
        return false;
    }
魔大农's avatar
魔大农 committed
225 226
}

魔大农's avatar
魔大农 committed
227 228
bool Board::CheckQuatter()
{
魔大农's avatar
魔大农 committed
229 230
    bool checkBlocks{true};

魔大农's avatar
魔大农 committed
231
    //Check rows
232
    for (int j{0}; j < BOARD_HEIGHT; ++j){
233 234
        Piece::Attributes matching{};
        matching.flip();
魔大农's avatar
魔大农 committed
235
        Piece::Attributes first{};
236
        for (int i{0}; i < BOARD_WIDTH; ++i){
魔大农's avatar
魔大农 committed
237
            IntVector2 coords(i, j);
238 239
            Piece* piece{squares_[coords].Get()->piece_};
            if (piece){
240 241
                Piece::Attributes attributes(piece->GetAttributes());
                if (i == 0) {
魔大农's avatar
魔大农 committed
242 243 244 245 246 247 248 249 250 251 252 253 254 255
                    first = attributes;
                } else {
                    for (int a{0}; a < NUM_ATTRIBUTES; ++a)
                        if (first[a] != attributes[a])
                            matching[a] = false;
                }
            //Full row required
            } else {
                matching.reset();
                break;
            }
        }
        //Quatter!
        if (matching.any()){
256
            Indicate(IntVector2(0, j), IntVector2(BOARD_WIDTH - 1, j));
魔大农's avatar
魔大农 committed
257 258 259 260 261
            return true;
        }
    }

    //Check columns
262
    for (int i{0}; i < BOARD_WIDTH; ++i){
263 264
        Piece::Attributes matching{};
        matching.flip();
魔大农's avatar
魔大农 committed
265
        Piece::Attributes first{};
266
        for (int j{0}; j < BOARD_HEIGHT; ++j){
魔大农's avatar
魔大农 committed
267
            IntVector2 coords(i, j);
268 269
            Piece* piece{squares_[coords].Get()->piece_};
            if (piece){
270 271
                Piece::Attributes attributes{piece->GetAttributes()};
                if (j == 0) {
魔大农's avatar
魔大农 committed
272 273 274 275 276 277 278 279 280 281 282 283 284 285
                    first = attributes;
                } else {
                    for (int a{0}; a < NUM_ATTRIBUTES; ++a)
                        if (first[a] != attributes[a])
                            matching[a] = false;
                }
            //Full column required
            } else {
                matching.reset();
                break;
            }
        }
        //Quatter!
        if (matching.any()){
286
            Indicate(IntVector2(i, 0), IntVector2(i, BOARD_HEIGHT - 1));
魔大农's avatar
魔大农 committed
287 288 289 290 291
            return true;
        }
    }
    //Check diagonals
    for (bool direction : {true, false}){
292 293
        Piece::Attributes matching{};
        matching.flip();
魔大农's avatar
魔大农 committed
294 295 296
        Piece::Attributes first{};
        for (int i{0}; i < BOARD_WIDTH; ++i){
            IntVector2 coords(i, direction ? i : (BOARD_WIDTH - i - 1));
297 298 299
            Piece* piece{squares_[coords].Get()->piece_};
            if (piece){
                Piece::Attributes attributes(piece->GetAttributes());
魔大农's avatar
魔大农 committed
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
                if (i == 0) {
                    first = attributes;
                } else {
                    for (int a{0}; a < NUM_ATTRIBUTES; ++a)
                        if (first[a] != attributes[a])
                            matching[a] = false;
                }
                //Full line required
            } else {
                matching.reset();
                break;
            }
        }
        //Quatter!
        if (matching.any()){
315 316
            Indicate(IntVector2(0, direction * (BOARD_HEIGHT - 1)),
                     IntVector2(BOARD_WIDTH - 1, !direction * (BOARD_HEIGHT - 1)));
魔大农's avatar
魔大农 committed
317 318 319
            return true;
        }
    }
320
    //Check 2x2 blocks
魔大农's avatar
魔大农 committed
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
    if (checkBlocks){
        for (int k{0}; k < BOARD_WIDTH - 1; ++k){
            for (int l{0}; l < BOARD_HEIGHT - 1; ++l){
                Piece::Attributes matching{};
                matching.flip();
                Piece::Attributes first{};
                for (int m : {0, 1}) for (int n : {0, 1}){
                    IntVector2 coords(k + m, l + n);
                    Piece* piece{squares_[coords].Get()->piece_};
                    if (piece){
                        Piece::Attributes attributes(piece->GetAttributes());
                        if (m == 0 && n == 0) {
                            first = attributes;
                        } else {
                            for (int a{0}; a < NUM_ATTRIBUTES; ++a)
                                if (first[a] != attributes[a])
                                    matching[a] = false;
                        }
                        //Full block required
魔大农's avatar
魔大农 committed
340
                    } else {
魔大农's avatar
魔大农 committed
341 342
                        matching.reset();
                        break;
魔大农's avatar
魔大农 committed
343 344
                    }
                }
魔大农's avatar
魔大农 committed
345 346
                //Quatter!
                if (matching.any()){
347 348
                    Indicate(IntVector2(k, l),
                             IntVector2(k + 1, l + 1));
魔大农's avatar
魔大农 committed
349 350
                    return true;
                }
魔大农's avatar
魔大农 committed
351 352 353
            }
        }
    }
354

魔大农's avatar
魔大农 committed
355 356
    //No Quatter
    return false;
魔大农's avatar
魔大农 committed
357
}
358

魔大农's avatar
魔大农 committed
359
Square* Board::GetNearestSquare(Vector3 pos, bool free)
360
{
361
    Square* nearest{};
362
    for (Square* s : squares_.Values()){
363 364 365
        if (!nearest ||
            LucKey::Distance(s->node_->GetWorldPosition(), pos) <
            LucKey::Distance(nearest->node_->GetWorldPosition(), pos))
魔大农's avatar
魔大农 committed
366
            if (s->free_ || !free)
367 368 369 370 371 372
                nearest = s;
    }
    return nearest;
}
void Board::SelectNearestFreeSquare(Vector3 pos)
{
魔大农's avatar
魔大农 committed
373 374 375 376 377 378
    Square* square{GetNearestSquare(pos, true)};
    if (square) Select(square);
}
void Board::SelectNearestSquare(Vector3 pos)
{
    Square* square{GetNearestSquare(pos, false)};
379 380
    if (square) Select(square);
}
魔大农's avatar
魔大农 committed
381 382 383 384 385
bool Board::SelectLast()
{
    if (lastSelectedSquare_ && lastSelectedSquare_ != selectedSquare_) {
        Select(lastSelectedSquare_);
        return true;
魔大农's avatar
魔大农 committed
386 387
    } else if (!selectedSquare_) {
        SelectNearestFreeSquare();
魔大农's avatar
魔大农 committed
388 389 390
    } else return false;
}

391 392 393 394 395 396
void Board::Select(Square* square)
{
    if (selectedSquare_)
        Deselect(selectedSquare_);

    selectedSquare_ = square;
魔大农's avatar
魔大农 committed
397
    square->selected_ = true;
398

魔大农's avatar
魔大农 committed
399 400 401 402 403 404 405 406
    //Fade in slot and light
    if (square->free_){
        FX->FadeTo(square->slot_->GetMaterial(),
                   COLOR_GLOW);
    } else {
        FX->FadeTo(square->slot_->GetMaterial(),
                   Color(1.0f, 0.8f, 0.0f, 0.5f));
    }
407

408 409
    Indicate(square->coords_);

魔大农's avatar
魔大农 committed
410
    FX->FadeTo(square->light_, 0.42f);
411 412 413
}
void Board::Deselect(Square* square)
{
414
    HideIndicators();
415 416
    if (!square) return;

魔大农's avatar
魔大农 committed
417 418
    if (selectedSquare_ == square){
        lastSelectedSquare_ = selectedSquare_;
419
        selectedSquare_ = nullptr;
魔大农's avatar
魔大农 committed
420
    }
421 422 423

    square->selected_ = false;

魔大农's avatar
魔大农 committed
424 425
    //Fade out slot and light
    FX->FadeOut(square->slot_->GetMaterial());
魔大农's avatar
魔大农 committed
426
    FX->FadeTo(square->light_, 0.023f);
427 428 429 430 431
}
void Board::DeselectAll()
{
    for (Square* s: squares_.Values()){
        Deselect(s);
432 433
    }
}
魔大农's avatar
魔大农 committed
434 435 436 437 438 439 440 441

void Board::Step(IntVector2 step)
{
    if (selectedSquare_){
        IntVector2 newCoords{selectedSquare_->coords_ + step};
        if (squares_.Contains(newCoords)){
            Select(squares_[newCoords].Get());
        }
魔大农's avatar
魔大农 committed
442
    } else SelectLast();
魔大农's avatar
魔大农 committed
443
}
444 445 446 447 448 449

void Board::Indicate(IntVector2 first, IntVector2 last)
{
    //Indicate single square
    if (last == IntVector2(-1, -1)){
        if (indicateSingle_){
450
            FX->FadeTo(indicators_[0]->glow_, COLOR_GLOW, 2.3f, 1.0f);
451 452 453 454
            FX->TransformTo(indicators_[0]->rootNode_,
                    CoordsToPosition(first) * Vector3(0.0f, 1.0f, 1.0f),
                    indicators_[0]->rootNode_->GetRotation(),
                    0.23f);
455
            FX->FadeTo(indicators_[1]->glow_, COLOR_GLOW, 2.3f, 1.0f);
456 457 458 459 460 461 462
            FX->TransformTo(indicators_[1]->rootNode_,
                    CoordsToPosition(first) * Vector3(1.0f, 1.0f, 0.0f),
                    indicators_[1]->rootNode_->GetRotation(),
                    0.23f);
        }
    //Indicate row
    } else if (first.y_ == last.y_){
463
        FX->FadeTo(indicators_[0]->glow_, COLOR_GLOW, 2.3f, 1.0f);
464
        indicators_[0]->rootNode_->SetPosition(CoordsToPosition(first) * Vector3(0.0f, 1.0f, 1.0f));
465 466
        indicators_[0]->model1_->SetMorphWeight(1, static_cast<float>(first.y_ > 0 && first.y_ < 3));
        indicators_[0]->model2_->SetMorphWeight(1, static_cast<float>(first.y_ > 0 && first.y_ < 3));
467 468
    //Indicate column
    } else if (first.x_ == last.x_){
469
        FX->FadeTo(indicators_[1]->glow_, COLOR_GLOW, 2.3f, 1.0f);
470
        indicators_[1]->rootNode_->SetPosition(CoordsToPosition(first) * Vector3(1.0f, 1.0f, 0.0f));
471 472
        indicators_[1]->model1_->SetMorphWeight(1, static_cast<float>(first.x_ > 0 && first.x_ < 3));
        indicators_[1]->model2_->SetMorphWeight(1, static_cast<float>(first.x_ > 0 && first.x_ < 3));
473 474
    //Indicate first diagonal
    } else if (first.x_ == 0 && last.y_ == 0){
475
        FX->FadeTo(indicators_[3]->glow_, COLOR_GLOW, 2.3f, 1.0f);
476 477
    //Indicate 2x2 blocks
    } else if (last.x_ - first.x_ == 1) {
478
        FX->FadeTo(indicators_[4]->glow_, COLOR_GLOW, 2.3f, 1.0f);
479
        indicators_[4]->rootNode_->SetPosition(CoordsToPosition(first) * Vector3(0.0f, 1.0f, 1.0f));
480
        FX->FadeTo(indicators_[5]->glow_, COLOR_GLOW, 2.3f, 1.0f);
481 482 483
        indicators_[5]->rootNode_->SetPosition(CoordsToPosition(first) * Vector3(1.0f, 1.0f, 0.0f));
    //Indicate other diagonal
    } else
484
        FX->FadeTo(indicators_[2]->glow_, COLOR_GLOW, 2.3f, 1.0f);
485 486 487 488 489 490 491 492
}

void Board::HideIndicators()
{
    for (SharedPtr<Indicator> i: indicators_){
        FX->FadeOut(i.Get()->glow_);
    }
}