private void RaiseField() { for (int y = ((int)field_height) - 1; y > 0; y--) { for (int x = 0; x < 6; x++) { m_field[x, y] = m_field[x, y - 1]; } } for (int x = 0; x < 6; x++) { m_field[x, 0] = new Block { Colour = m_below_field[x, 1] }; } MakeBelowRow(); m_cruiser_pos.Y++; m_fast_lifting = false; }
public void Cycle() { switch (m_state) { case GameState.Main: Block theblock; freeze_lift = false; // === 1: Process blocks involved in combos, perform lag timing === for (int y = 0; y < field_height; y++) { for (int x = 0; x < 6; x++) { theblock = m_field[x, y]; switch (theblock.State) { case BlockState.Vanishing: case BlockState.ComboVanishing: freeze_lift = true; if (theblock.Parameter > 0) theblock.Parameter--; else if (theblock.FaceTime == 1) { // TODO: Forward an event to draw flashy graphics & sound for a block disappearing. theblock.FaceTime--; } else if (theblock.FaceTime > 0) theblock.FaceTime--; else if (theblock.InvisTime > 0) theblock.InvisTime--; else { theblock.Colour = BlockColour.Empty; theblock.State = BlockState.Normal; if (y < field_height - 1) { SetLagColumn(BlockState.ComboLag, x, y + 1, 1); } } break; case BlockState.SwitchLag: freeze_lift = true; if (y < 11) { Block b3 = m_field[x, y + 1]; if ((b3.State != BlockState.SwitchLag) && (b3.State != BlockState.ComboLag)) { // A block has been dropped on top of a laggy block. Give it a fresh lag. // If this falling block was energized from a combo, it stays that way. SetLagColumn((b3.State == BlockState.ComboFall) ? BlockState.ComboLag : BlockState.SwitchLag, x, y + 1, 0); } } if (theblock.Parameter > 0) theblock.Parameter--; else { theblock.State = BlockState.Normal; // Set to NORMAL instead of Fall to permit ninja chaining to work. } break; case BlockState.ComboLag: freeze_lift = true; if (y < 11) { Block b3 = m_field[x, y + 1]; if ((b3.State != BlockState.SwitchLag) && (b3.State != BlockState.ComboLag)) { // A block has been dropped on top of a laggy block. Give it a fresh lag. // If this falling block was energized from a combo, it stays that way. SetLagColumn((b3.State == BlockState.ComboFall) ? BlockState.ComboLag : BlockState.SwitchLag, x, y + 1, 0); } } if (theblock.Parameter > 0) theblock.Parameter--; else { theblock.State = BlockState.ComboFall; } break; } m_field[x, y] = theblock; } } // === 2: Process block falling, switching === for (int y = 0; y < field_height; y++) { for (int x = 0; x < 6; x++) { theblock = m_field[x, y]; switch (theblock.State) { case BlockState.Fall: // If a block is SEEN in this state it has landed. (Otherwise it would have slipped through the cracks) freeze_lift = true; if (y > 0) // there is a laggy or switching block further down the stack { Block lowblock = m_field[x, y - 1]; if ((lowblock.State == BlockState.SwitchLeft) || (lowblock.State == BlockState.SwitchRight) || (lowblock.State == BlockState.SwitchLag) || (lowblock.State == BlockState.ComboLag) || (lowblock.State == BlockState.Fall) || (lowblock.State == BlockState.ComboFall)) { goto case BlockState.Normal; } } theblock.State = BlockState.Normal; theblock.Parameter = 6; // Bounce goto case BlockState.Normal; case BlockState.ComboFall: freeze_lift = true; if (y > 0) // there is a laggy or switching block further down the stack { Block lowblock = m_field[x, y - 1]; if ((lowblock.State == BlockState.SwitchLeft) || (lowblock.State == BlockState.SwitchRight) || (lowblock.State == BlockState.SwitchLag) || (lowblock.State == BlockState.ComboLag) || (lowblock.State == BlockState.Fall) || (lowblock.State == BlockState.ComboFall)) { goto case BlockState.Normal; } } theblock.State = BlockState.ComboLanded; theblock.Parameter = 6; // Bounce goto case BlockState.Normal; case BlockState.Normal: if (theblock.Colour == BlockColour.Empty) { if (y < field_height - 1) { Block highblock = m_field[x, y + 1]; if (((int)(highblock.Colour) > 0) && ((int)(highblock.Colour) < 8)) { if ((highblock.State == BlockState.Normal) || (highblock.State == BlockState.Fall) || (highblock.State == BlockState.ComboFall)) { // A block with space below it: move it down by one. freeze_lift = true; theblock = highblock; if (theblock.State == BlockState.Normal) theblock.State = BlockState.Fall; else if (theblock.State == BlockState.ComboLanded) theblock.State = BlockState.ComboFall; m_field[x, y + 1] = new Block(); } } else if ((highblock.Colour == BlockColour.Garbage) || (highblock.Colour == BlockColour.ShockGarbage)) { throw new NotImplementedException("Garbage blocks you crazy??"); } } } else if ((int)(theblock.Colour) < 8) { if (theblock.Parameter > 0) { theblock.Parameter--; } } else { throw new NotImplementedException("Garbage blocks you crazy??"); } break; case BlockState.SwitchLeft: case BlockState.SwitchRight: if (m_field[x, y + 1].State == BlockState.ComboFall) { Block b2 = m_field[x, y + 1]; b2.State = BlockState.ComboLag; if (m_difficulty < Difficulty.Normal) b2.Parameter = 9; else if (m_difficulty > Difficulty.Normal) b2.Parameter = 5; else b2.Parameter = 7; m_field[x, y + 1] = b2; } if (theblock.Parameter > 0) { theblock.Parameter--; } else { Block rightblock = theblock; // RightBlock: Block going TO the right. theblock = m_field[x + 1, y]; // Has to be safe or stuff has gone horribly wrong. theblock.State = BlockState.Normal; rightblock.State = BlockState.Normal; if (y > 0) { // Only lag the block if it's slid to over empty space. if (m_field[x, y - 1].Colour == BlockColour.Empty) { theblock.State = BlockState.SwitchLag; theblock.Parameter = LagTime(); } if (m_field[x + 1, y - 1].Colour == BlockColour.Empty) { rightblock.State = BlockState.SwitchLag; rightblock.Parameter = LagTime(); } } // Set lag when switching a block out of a column if (x < 11) { if (theblock.Colour == BlockColour.Empty) { Block hiblock = m_field[x, y + 1]; hiblock.State = BlockState.SwitchLag; hiblock.Parameter = LagTime(); m_field[x, y + 1] = hiblock; } else if (rightblock.Colour == BlockColour.Empty) { Block hiblock = m_field[x + 1, y + 1]; hiblock.State = BlockState.SwitchLag; hiblock.Parameter = LagTime(); m_field[x + 1, y + 1] = hiblock; } } m_field[x + 1, y] = rightblock; } break; } m_field[x, y] = theblock; } } // === 3: Find combos === bool chain_active = false; // Set true if any blocks energized by combos are on the screen. bool is_chain_combo = false; // Set true if a row of blocks found on this frame are part of a chain. for (int y = 0; y < field_height; y++) { for (int x = 0; x < 6; x++) { theblock = m_field[x, y]; Block block1, block2; bool chain_this_try = false; if (((int)(theblock.Colour) > 0) && ((int)(theblock.Colour) < 8)) { switch (theblock.State) { case BlockState.ComboLanded: chain_this_try = true; goto case BlockState.Normal; case BlockState.VanishMarked: case BlockState.Normal: // 1. Horizontal check. if (x < 4) { block1 = m_field[x + 1, y]; block2 = m_field[x + 2, y]; if ((block1.Colour == theblock.Colour) && (block2.Colour == theblock.Colour) && ((block1.State == BlockState.Normal) || (block1.State == BlockState.ComboLanded) || (block1.State == BlockState.VanishMarked)) && ((block2.State == BlockState.Normal) || (block2.State == BlockState.ComboLanded) || (block2.State == BlockState.VanishMarked))) { if ((block1.State == BlockState.ComboLanded) || (block2.State == BlockState.ComboLanded) || chain_this_try) { is_chain_combo = true; chain_active = true; } theblock.State = BlockState.VanishMarked; block1.State = BlockState.VanishMarked; block2.State = BlockState.VanishMarked; m_field[x, y] = theblock; m_field[x + 1, y] = block1; m_field[x + 2, y] = block2; } } if (y < field_height - 3) { block1 = m_field[x, y + 1]; block2 = m_field[x, y + 2]; if ((block1.Colour == theblock.Colour) && (block2.Colour == theblock.Colour) && ((block1.State == BlockState.Normal) || (block1.State == BlockState.ComboLanded) || (block1.State == BlockState.VanishMarked)) && ((block2.State == BlockState.Normal) || (block2.State == BlockState.ComboLanded) || (block2.State == BlockState.VanishMarked))) { if ((block1.State == BlockState.ComboLanded) || (block2.State == BlockState.ComboLanded) || chain_this_try) { is_chain_combo = true; chain_active = true; } theblock.State = BlockState.VanishMarked; block1.State = BlockState.VanishMarked; block2.State = BlockState.VanishMarked; m_field[x, y] = theblock; m_field[x, y + 1] = block1; m_field[x, y + 2] = block2; } } break; } } // === 4: Chain keep-alive check === switch (theblock.State) { case BlockState.ComboFall: case BlockState.ComboLag: case BlockState.ComboVanishing: chain_active = true; break; case BlockState.ComboLanded: theblock.State = BlockState.Normal; m_field[x, y] = theblock; break; } } } // === 4: Count blocks involved in the combo on this frame === uint BlockCount = 0; Point ch_pos = new Point(0, 0); for (int y = 0; y < field_height; y++) { for (int x = 0; x < 6; x++) { if (m_field[x, y].State == BlockState.VanishMarked) { BlockCount++; // Maybe I can do this in the above loop but the logic is stupid // Kind of odd. Picks the rightmost block in the topmost row and declares it the important block for this combo/chain event // Replace with a more authentic approach. ch_pos.X = x; ch_pos.Y = y; } } } uint Delayer = 1; // === 5: Set this frame's combo's blocks to the vanishing state === for (int y = ((int)field_height) - 1; y >= 0; y--) { for (int x = 0; x < 6; x++) { theblock = m_field[x, y]; if (theblock.State == BlockState.VanishMarked) { theblock.State = is_chain_combo ? BlockState.ComboVanishing : BlockState.Vanishing; theblock.Parameter = m_flash_frames; theblock.FaceTime = m_face_frames + Delayer * m_vanish_speed; theblock.InvisTime = (BlockCount - Delayer) * m_vanish_speed; theblock.ComboOrdinal = Delayer; Delayer++; } m_field[x, y] = theblock; } } if (!freeze_lift) { ushort liftold = m_lift_phase; m_lift_phase += m_fast_lifting ? (ushort)4096 : m_lift_speed; if (m_cruiser_pos.Y >= 11) m_cruiser_pos.Y = 10; if (m_lift_phase < liftold) { m_lift_phase = 0; RaiseField(); } } if (BlockCount > 3) // Combo event { if (OnCombo != null) OnCombo(this, new PaneponEventArgs(ch_pos, BlockCount)); } if (is_chain_combo) // Chain event { m_chain_length++; if (OnChain != null) OnChain(this, new PaneponEventArgs(ch_pos, m_chain_length)); } // TODO: Scoring based on BlockCount, m_chain_length, and m_level if (!chain_active) m_chain_length = 1; break; } }