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)); }
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); }
/// // 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; }
/// // 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; }
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)