board.cpp 15.8 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
    model_ = rootNode_->CreateComponent<StaticModel>();
魔大农's avatar
魔大农 committed
38 39
    model_->SetModel(MC->GetModel("Board"));
    model_->SetMaterial(MC->GetMaterial("Board"));
魔大农's avatar
魔大农 committed
40
    model_->SetCastShadows(true);
魔大农's avatar
魔大农 committed
41

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

        squares_[square->coords_] = square;

    }
75

76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
    //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>();
        if (i < 4)
            indicator->model1_->SetModel(MC->GetModel("Arrow"));
        else
            indicator->model1_->SetModel(MC->GetModel("BlockIndicator"));
        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>();
        if (i < 4)
            indicator->model2_->SetModel(MC->GetModel("Arrow"));
        else
            indicator->model2_->SetModel(MC->GetModel("BlockIndicator"));
        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));

    }

132
    SubscribeToEvent(E_SCENEUPDATE, URHO3D_HANDLER(Board, HandleSceneUpdate));
魔大农's avatar
魔大农 committed
133
}
魔大农's avatar
魔大农 committed
134 135 136
void Board::Reset()
{
    for (Square* s: squares_.Values()){
魔大农's avatar
魔大农 committed
137

魔大农's avatar
魔大农 committed
138 139 140
        s->free_ = true;
        s->piece_ = nullptr;
        s->light_->SetEnabled(true);
魔大农's avatar
魔大农 committed
141

魔大农's avatar
魔大农 committed
142
    }
魔大农's avatar
魔大农 committed
143

魔大农's avatar
魔大农 committed
144 145
    DeselectAll();
}
魔大农's avatar
魔大农 committed
146

魔大农's avatar
魔大农 committed
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
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
166 167 168 169 170 171 172
bool Board::IsFull() const
{
    for (Square* s: squares_.Values())
        if (s->free_) return false;

    return true;
}
魔大农's avatar
魔大农 committed
173 174

Vector3 Board::CoordsToPosition(IntVector2 coords)
魔大农's avatar
魔大农 committed
175 176
{
    return Vector3(0.5f + coords.x_ - BOARD_WIDTH/2,
177
                   GetThickness(),
魔大农's avatar
魔大农 committed
178 179 180
                   0.5f + coords.y_ - BOARD_HEIGHT/2);
}

181 182 183
void Board::HandleSceneUpdate(StringHash eventType, VariantMap& eventData)
{
    for (Square* s: squares_.Values()){
魔大农's avatar
魔大农 committed
184

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

187 188 189
    }
}

魔大农's avatar
魔大农 committed
190 191
bool Board::PutPiece(Piece* piece, Square* square)
{
魔大农's avatar
魔大农 committed
192 193 194 195
    if (!square){
        return PutPiece(piece);
    }

魔大农's avatar
魔大农 committed
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
    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
212
        MC->NextPhase();
魔大农's avatar
魔大农 committed
213 214
        return true;

魔大农's avatar
魔大农 committed
215 216 217 218
    } else {
        Refuse();
        return false;
    }
魔大农's avatar
魔大农 committed
219 220
}

魔大农's avatar
魔大农 committed
221 222
bool Board::CheckQuatter()
{
魔大农's avatar
魔大农 committed
223 224
    bool checkBlocks{true};

魔大农's avatar
魔大农 committed
225
    //Check rows
226
    for (int j{0}; j < BOARD_HEIGHT; ++j){
227 228
        Piece::Attributes matching{};
        matching.flip();
魔大农's avatar
魔大农 committed
229
        Piece::Attributes first{};
230
        for (int i{0}; i < BOARD_WIDTH; ++i){
魔大农's avatar
魔大农 committed
231
            IntVector2 coords(i, j);
232 233
            Piece* piece{squares_[coords].Get()->piece_};
            if (piece){
234 235
                Piece::Attributes attributes(piece->GetAttributes());
                if (i == 0) {
魔大农's avatar
魔大农 committed
236 237 238 239 240 241 242 243 244 245 246 247 248 249
                    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()){
250
            Indicate(IntVector2(0, j), IntVector2(BOARD_WIDTH - 1, j));
魔大农's avatar
魔大农 committed
251 252 253 254 255
            return true;
        }
    }

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

魔大农's avatar
魔大农 committed
349 350
    //No Quatter
    return false;
魔大农's avatar
魔大农 committed
351
}
352

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

385 386 387 388 389 390
void Board::Select(Square* square)
{
    if (selectedSquare_)
        Deselect(selectedSquare_);

    selectedSquare_ = square;
魔大农's avatar
魔大农 committed
391
    square->selected_ = true;
392

魔大农's avatar
魔大农 committed
393 394 395 396 397 398 399 400
    //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));
    }
401

402 403
    Indicate(square->coords_);

魔大农's avatar
魔大农 committed
404
    FX->FadeTo(square->light_, 0.42f);
405 406 407
}
void Board::Deselect(Square* square)
{
408
    HideIndicators();
409 410
    if (!square) return;

魔大农's avatar
魔大农 committed
411 412
    if (selectedSquare_ == square){
        lastSelectedSquare_ = selectedSquare_;
413
        selectedSquare_ = nullptr;
魔大农's avatar
魔大农 committed
414
    }
415 416 417

    square->selected_ = false;

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

void Board::Step(IntVector2 step)
{
    if (selectedSquare_){
        IntVector2 newCoords{selectedSquare_->coords_ + step};
        if (squares_.Contains(newCoords)){
            Select(squares_[newCoords].Get());
        }
魔大农's avatar
魔大农 committed
436
    } else SelectLast();
魔大农's avatar
魔大农 committed
437
}
438 439 440 441 442 443

void Board::Indicate(IntVector2 first, IntVector2 last)
{
    //Indicate single square
    if (last == IntVector2(-1, -1)){
        if (indicateSingle_){
444
            FX->FadeTo(indicators_[0]->glow_, COLOR_GLOW, 2.3f, 1.0f);
445 446 447 448
            FX->TransformTo(indicators_[0]->rootNode_,
                    CoordsToPosition(first) * Vector3(0.0f, 1.0f, 1.0f),
                    indicators_[0]->rootNode_->GetRotation(),
                    0.23f);
449
            FX->FadeTo(indicators_[1]->glow_, COLOR_GLOW, 2.3f, 1.0f);
450 451 452 453 454 455 456
            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_){
457
        FX->FadeTo(indicators_[0]->glow_, COLOR_GLOW, 2.3f, 1.0f);
458 459 460
        indicators_[0]->rootNode_->SetPosition(CoordsToPosition(first) * Vector3(0.0f, 1.0f, 1.0f));
    //Indicate column
    } else if (first.x_ == last.x_){
461
        FX->FadeTo(indicators_[1]->glow_, COLOR_GLOW, 2.3f, 1.0f);
462 463 464
        indicators_[1]->rootNode_->SetPosition(CoordsToPosition(first) * Vector3(1.0f, 1.0f, 0.0f));
    //Indicate first diagonal
    } else if (first.x_ == 0 && last.y_ == 0){
465
        FX->FadeTo(indicators_[3]->glow_, COLOR_GLOW, 2.3f, 1.0f);
466 467
    //Indicate 2x2 blocks
    } else if (last.x_ - first.x_ == 1) {
468
        FX->FadeTo(indicators_[4]->glow_, COLOR_GLOW, 2.3f, 1.0f);
469
        indicators_[4]->rootNode_->SetPosition(CoordsToPosition(first) * Vector3(0.0f, 1.0f, 1.0f));
470
        FX->FadeTo(indicators_[5]->glow_, COLOR_GLOW, 2.3f, 1.0f);
471 472 473
        indicators_[5]->rootNode_->SetPosition(CoordsToPosition(first) * Vector3(1.0f, 1.0f, 0.0f));
    //Indicate other diagonal
    } else
474
        FX->FadeTo(indicators_[2]->glow_, COLOR_GLOW, 2.3f, 1.0f);
475 476 477 478 479 480 481 482
}

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