Exemplo n.º 1
0
PyObject *seconds_to_ticks(PyObject *self, PyObject *args) {
    int seconds;
    if (!PyArg_ParseTuple(args, "d", &seconds)) {
        PyErr_SetString(PyExc_TypeError, "expected type 'float'");
        return NULL;
    }
    return Py_BuildValue("i", TICKS(seconds));
}
Exemplo n.º 2
0
static QState mmi_show_msg(struct mmi_ao *me)
{
    switch (Q_SIG(me)) {
    case Q_ENTRY_SIG:
        QActive_arm((QActive *) me, TICKS(Q_PAR(me) * 1000));
        return Q_HANDLED();
    case Q_EXIT_SIG:
        QActive_disarm((QActive *) me);
        return Q_HANDLED();
    case Q_TIMEOUT_SIG:
        return Q_TRAN(mmi_navigate);
    case SIG_KEY_PRESS:
        if (Q_PAR(me) == KEY_ENTER) {
            return Q_TRAN(mmi_navigate);
        }
        return Q_HANDLED();;
    }

    return Q_SUPER(&QHsm_top);
}
Exemplo n.º 3
0
///
// Perform a single game tick.
//
// This is just a state machine which is repeatedly called from the main
// game loop. We do not want a 1 frame delay for some actions so we allow
// some to run 'instantly'.
///
void fsGameTick(FSEngine *f, const FSInput *i)
{
    i8 distance;
    bool moved = false, rotated = false;

    f->se = 0;
    f->totalTicksRaw++;

    // TODO: Remove lastInput since unused
    f->lastInput = *i;

    // Always handle restart/quit events at any time.
    if (i->extra & FST_INPUT_RESTART) {
        f->state = FSS_RESTART;
    }
    if (i->extra & FST_INPUT_QUIT) {
        f->state = FSS_QUIT;
    }

    // Always update the current piece finesse counters
    if (i->extra & FST_INPUT_FINESSE_ROTATE) {
        f->pieceRotateCount += 1;
    }
    if (i->extra & FST_INPUT_FINESSE_MOVE) {
        f->pieceMovePressCount += 1;
    }

    // Always count the number of new keys pressed
    f->totalKeysPressed += i->newKeysCount;

beginTick:
    switch (f->state) {
      case FSS_READY:
      case FSS_GO:

        // Ready, Go has has slightly different hold mechanics. Since we do not
        // yet have a piece we need to copy directly from the next queue to the
        // hold piece. Further, we can optionally hold as many times as we want
        // so need to discard the hold piece if required.
        if ((i->extra & FST_INPUT_HOLD) && f->holdAvailable) {
            f->holdPiece = nextPreviewPiece(f);
            f->se |= FST_SE_FLAG_HOLD;

            if (!f->infiniteReadyGoHold) {
                f->holdAvailable = false;
            }
        }

        if (f->genericCounter == 0) {
            f->se |= FST_SE_FLAG_READY;
        }

        if (f->genericCounter == TICKS(f->readyPhaseLength)) {
            f->se |= FST_SE_FLAG_GO;
            f->state = FSS_GO;
        }

        // This cannot be an `else if` since goPhaseLength could be 0.
        if (f->genericCounter == TICKS(f->readyPhaseLength) +
                                 TICKS(f->goPhaseLength)) {
            f->state = FSS_NEW_PIECE;
        }

        f->genericCounter++;

        // We need an explicit return here to avoid incrementing `totalTicks
        return;

      case FSS_ARE:
        // Even if ARE is instant, we still want to check for IHS and IRS state.
        // This allows the following behaviour:
        //
        // Currently we should be able to have three different actions for an initial
        // action:
        //
        //  NONE - IRS/IHS disabled and not checked
        //  HELD - Allows input action to remain set from last piece
        //  HIT  - Requires a new input action to trigger (not implemented)
        //
        // If ARE can be cancelled then the action will occur on the next
        // frame with the piece already playable.
        // This may need some more tweaking since during fast play initial stack
        // far too easily.
        if (f->initialActionStyle == FST_IA_PERSISTENT) {
            // Only check the current key state.
            // This is only dependent on the value on the final frame before the
            // piece spawns. Could adjust to allow any mid-ARE initial action to
            // stick till spawn.

            // We need an implicit ordering here so are slightly biased. May want to
            // give an option to adjust this ordering or have a stricter
            // order.
            if (i->currentKeys & FST_VK_FLAG_ROTR) {
                f->irsAmount = FST_ROT_CLOCKWISE;
            }
            else if (i->currentKeys & FST_VK_FLAG_ROTL) {
                f->irsAmount = FST_ROT_ANTICLOCKWISE;
            }
            else if (i->currentKeys & FST_VK_FLAG_ROTH) {
                f->irsAmount = FST_ROT_HALFTURN;
            }
            else {
                f->irsAmount = FST_ROT_NONE;
            }

            if (i->currentKeys & FST_VK_FLAG_HOLD) {
                f->ihsFlag = true;
            }
            else {
                f->ihsFlag = false;
            }
        }

        if (f->areCancellable && (
                i->rotation != 0 ||
                i->movement != 0 ||
                i->gravity  != 0 ||
                i->extra    != 0 ||
                // We need to check ihs/irs since this is solely based on new
                // key state and otherwise may not be picked up.
                f->ihsFlag || f->irsAmount
                )
        ) {
            f->areTimer = 0;
            f->state = FSS_NEW_PIECE;
            goto beginTick;
        }

        if (f->areTimer++ > TICKS(f->areDelay)) {
            f->areTimer = 0;
            f->state = FSS_NEW_PIECE;
            goto beginTick;
        }
        break;

      case FSS_NEW_PIECE:
        newPiece(f);

        // Apply ihs/irs before checking lockout.
        if (f->irsAmount != FST_ROT_NONE) {
            doRotate(f, f->irsAmount);
        }
        if (f->ihsFlag) {
            tryHold(f);
        }

        f->irsAmount = FST_ROT_NONE;
        f->ihsFlag = false;

        // Check lockout (irs/ihs has been applied already)
        if (isCollision(f, f->x, f->y, f->theta)) {
            f->state = FSS_GAMEOVER;
            goto beginTick;
        }

        updateHardDropY(f);
        f->state = FSS_FALLING;
        break;

      case FSS_FALLING:
      case FSS_LANDED:
        // If a hard drop occurs we want to immediately drop the piece and not
        // apply any other movement. This is far more natural and results in
        // less misdrops than if movement is processed prior.
        //
        // See issue #49 for details.
        if ((i->extra & FST_INPUT_HARD_DROP) ||
                // We must recheck the lock timer state here since we may have
                // moved back to FALLING from LANDED on the last frame and do
                // **not** want to lock in mid-air!
                (f->lockTimer >= TICKS(f->lockDelay) && f->state == FSS_LANDED)) {
            f->state = FSS_LINES;

            // Still need to apply piece gravity before entering FSS_LINES.
            doPieceGravity(f, i->gravity);
            break;
        }

        if (i->extra & FST_INPUT_HOLD) {
            tryHold(f);
        }

        if (i->rotation) {
            if (doRotate(f, i->rotation)) {
                rotated = true;
            }
        }

        // Left movement
        distance = i->movement;
        for (; distance < 0; ++distance) {
            if (!isCollision(f, f->x - 1, f->y, f->theta)) {
                f->x -= 1;
                moved = true;
            }
        }

        // Right movement
        for (; distance > 0; --distance) {
            if (!isCollision(f, f->x + 1, f->y, f->theta)) {
                f->x += 1;
                moved = true;
            }
        }

        if (moved || rotated) {
            if (moved) {
                f->se |= FST_SE_FLAG_MOVE;
            }
            if (rotated) {
                f->se |= FST_SE_FLAG_ROTATE;
            }

            updateHardDropY(f);
        }

        doPieceGravity(f, i->gravity);

        // This must occur after we process the lockTimer to allow floorkick
        // limits to be processed correctly. If we encounter a floorkick limit
        // we set the lockTimer to max to allow a lock next frame, while still
        // giving the user an option to perform a move/rotate input.
        if ((moved || rotated) && f->lockStyle == FST_LOCK_MOVE) {
            f->lockTimer = 0;
        }

        if (f->state == FSS_LANDED) {
            f->lockTimer++;
        }

        break;

      case FSS_LINES:
        lockPiece(f);

        // NOTE: Make this conversion less *magic*
        f->se |= (1 << (FST_SE_IPIECE + f->piece));
        f->piece = FS_NONE;

        const int lines = clearLines(f);
        if (0 < lines && lines <= 4) {
            // NOTE: Make this conversion less *magic*
            f->se |= (FST_SE_FLAG_ERASE1 << (lines - 1));
        }

        f->linesCleared += lines;
        f->state = f->linesCleared < f->goal ? FSS_ARE : FSS_GAMEOVER;
        goto beginTick;

      case FSS_GAMEOVER:
        f->se |= FST_SE_FLAG_GAMEOVER;
        /* FALLTHROUGH */

      case FSS_QUIT:
      case FSS_RESTART:
        break;

      default:
        fsLogError("Unknown state entered!");
        break;
    }

    f->totalTicks += 1;
}
Exemplo n.º 4
0
///
// Attempt to perform a rotation, returning whether the rotation succeeded.
///
static bool doRotate(FSEngine *f, i8 direction)
{
    i8 newDir = (f->theta + 4 + direction) & 3;
    const FSRotationSystem *rs = rotationSystems[f->rotationSystem];

    i8 tableNo = 0;
    switch (direction) {
      case FST_ROT_CLOCKWISE:
        tableNo = rs->kicksR[f->piece];
        break;
      case FST_ROT_ANTICLOCKWISE:
        tableNo = rs->kicksL[f->piece];
        break;
      case FST_ROT_HALFTURN:
        tableNo = rs->kicksH[f->piece];
        break;
      default:
        abort();
    }

    const WallkickTable *table = tableNo >= 0
                                    ? &rs->kickTables[tableNo]
                                    : &emptyWallkickTable;

    // The `.z` field stores special wallkick flags.
    for (int k = 0; k < FS_MAX_KICK_LEN; ++k) {
        // NOTE: Check which theta we should be using here
        // We need to reverse the kick rotation here
        const i8x3 kickData = (*table)[f->theta][k];

        if (kickData.z == WK_END) {
            break;
        }

        // Handle special TGM123 rotation which is based on field state.
        if (kickData.z == WK_ARIKA_LJT && wkCondArikaLJT(f, direction)) {
            break;
        }

        int kickX = kickData.x + f->x;
        int kickY = kickData.y + f->y;

        if (!isCollision(f, kickX, kickY, newDir)) {
            // To determine a floorkick, we cannot just check the kickData.y
            // value since this may be adjusted for a different rotation system
            // (i.e. sega).
            //
            // We need to compute the difference between the current kickData.y
            // and the initial kickData.y instead to get an accurate reading.
            const int adjKickY = kickData.y - (*table)[f->theta][0].y;

            if (f->floorkickLimit && adjKickY < 0) {
                if (f->floorkickCount++ >= f->floorkickLimit) {
                    f->lockTimer = TICKS(f->lockDelay);
                }
            }

            // Preserve the fractional y drop during rotation to disallow
            // implicit lock reset.
            f->actualY = fix(kickY) + unfixfrc(f->actualY);
            f->y = kickY;
            f->x = kickX;
            f->theta = newDir;
            return true;
        }
    }

    return false;
}
Exemplo n.º 5
0
static QState mmi_giga_pan(struct mmi_ao *me);
static QState mmi_show_msg(struct mmi_ao *me);
static QState mmi_busy(struct mmi_ao *me);

static QState execute_cmd(struct mmi_ao *me, int cmd);
static int modify_param(const struct param *param, int dir, int shift);
static void print_param(const struct param *param);

static void update_screen(struct mmi_ao *me);
static void show_msg(char *msg, int timeout);

/** MMI active object */
struct mmi_ao mmi_ao;

enum timeouts {
    TIMEOUT_HELLO = TICKS(5000),
    TIMEOUT_BUSY = TICKS(250),
};

/**
 * Constructor.
 */
void mmi_ctor(void)
{
    QActive_ctor((QActive *) &mmi_ao, (QStateHandler) mmi_initial);
}

/**
 * Initial state.
 */
static QState mmi_initial(struct mmi_ao *me)