/// <summary> /// Adds and repositions the block stack from the board. /// </summary> /// <param name="stack"></param> private void AddGrabStack() { // Ignore nulls stacks if (currentStack == null) { return; } // Figure out the top-most block that isn't moving float topPosition = GetTopPosition(); float grabPosition = Math.Max(topPosition + Constants.GrabStackOffset, Constants.MinimumGrabHeight); // Move the selector selector.BottomPosition = topPosition; currentStack.Add(selector); // Go through the blocks for (int i = 0; i < grabStack.Length; i++) { // Stop processing when we hit a null if (grabStack[i] == null) { break; } // Reset the position currentStack.Add(grabStack[i]); grabStack[i].BottomPosition = grabPosition + (float)i; } }
/// <summary> /// Migrates the current board to the other one, dropping /// everything on top. /// </summary> /// <param name="board"></param> /// <param name="x"></param> /// <param name="y"></param> public void MigrateBoard(Board board, int x, int y) { // Go through the columns for (int i = 0; i < Columns; i++) { // Get the data for speed IList <BlockStack> stacks = Game.State.Board[x + i]; IList <BlockStack> stacks0 = this[i]; // Go through the rows for (int j = 0; j < Rows; j++) { // Get the stacks BlockStack stack = stacks[y + j]; BlockStack stack0 = stacks0[j]; // Get the position Block bBlock = stack.TopBlock; float position = stack.TopPosition; // Move everything but the bottom-most one foreach (Block block in stack0) { // Seal it to change how it looks on the // mini map switch (block.Sprite.ID) { case "Water Block": bBlock.Sprite = AssetLoader.Instance .CreateSprite("Sealed Water Block"); bBlock.Data = false; break; case "Grass Block": bBlock.Sprite = AssetLoader.Instance .CreateSprite("Sealed Grass Block"); bBlock.Data = false; break; case "Dirt Block": bBlock.Sprite = AssetLoader.Instance .CreateSprite("Sealed Dirt Block"); bBlock.Data = false; break; } // Positions 0's are sealed if (block.BottomPosition != 0) { // Move it over to the new one and add the position // to the bottom, minus one because we ignore the // bottom row block.BottomPosition += position - 1f; stack.Add(new Block(block)); } } } } }
/// <summary> /// This creates a new stage for the game, using the /// properties of the state to determine how the board is /// generated. /// </summary> public void GenerateStage() { // See if we already have a gate column (i.e. if we have // any columns, then we do). int newColumns = Constants.StageWidth; // Extend the board by the number of columns int oldColumns = Columns; Columns = Columns + newColumns; // Populate the base level board for (int i = 0; i < newColumns; i++) { // Get the column IList <BlockStack> stacks = this[oldColumns + i]; // Go through the rows for (int j = 0; j < Constants.BoardRows; j++) { // Get the stack BlockStack stack = stacks[j]; stack.IsInInitialPlacement = true; // Set a new immobile block into place Block block = AssetLoader.Instance .CreateBlock(Constants.ImmobileBlockName); block.BottomPosition = 0; block.Height = 1; // Add it to the stack stack.Add(block); } } // Populate the additional immobile boards AddBlocks(Game.State.ImmobileCount, Constants.ImmobileBlockName); // Add the grass, dirt, and water blocks AddBlocks(); // Add the character prayer, but not on the far-right side Prayer prayer = new Prayer(); int px = Entropy.Next(0, Constants.StageWidth - 1); int py = Entropy.Next(0, Constants.BoardRows); prayer.X = px + oldColumns; prayer.Y = py; Game.State.Prayers.Add(prayer); BlockStack ps = Game.State.Board[oldColumns + px, py]; prayer.BottomPosition = ps.TopPosition; prayer.Vector = Constants.DroppedVector; ps.Add(prayer); }
/// <summary> /// Internal function to determine if the target is valid or /// not. /// </summary> private bool IsInvalidTarget(BlockStack srcStack, BlockStack destStack) { // Remove ourselves to take ourselves out of the calculation srcStack.Remove(this); try { // Make sure the destination is a ground block or an // immobile one, but nothing else Block dest = destStack.TopBlock; if (!Board.IsGroundBlock(dest) && !Board.IsImmobileBlock(dest)) { // Neither ground or immobile return(true); } // We need the distance to be 1 or less float dist = Math.Abs(srcStack.TopPosition - destStack.TopPosition); if (dist > 1) { // Too much to jump return(true); } // It is good return(false); } finally { // Put ourselves back srcStack.Add(this); } }
/// <summary> /// Adds a number of blocks of the give name to the board. This /// only places blocks in the last Constants.StateWidth rows. /// </summary> /// <param name="count"></param> /// <param name="name"></param> private void AddBlocks(int count, string name) { // Loop through the blocks for (int i = 0; i < count; i++) { // Create a new block Block block = CreateBlock(name); // Since we have a maximum count here, we loop until we // find a valid stack to drop it on while (true) { // Figure out a random position int col = Entropy.Next(0, Constants.StageWidth) + Columns - Constants.StageWidth; int row = Entropy.Next(0, Constants.BoardRows); BlockStack stack = this[col, row]; // Make sure the stack doesn't have too many items if (stack.Count >= Constants.MaximumPlacementCount) { continue; } // Set the position based on the count block.BottomPosition = 2f + (float)stack.Count * Constants.PlacementSpacing; block.Vector = Constants.DroppedVector; block.IsMoving = true; stack.Add(block); // Break out of the loop break; } } }
/// <summary> /// Internal function to populate the board with random /// blocks. /// </summary> private void PopulateBoard() { for (int x = 0; x < BoardColumns; x++) { for (int y = 0; y < BoardRows; y++) { // Get the stack BlockStack stack = board[x, y]; // Add 1-4 blocks int total = Entropy.Next(4) + 1; for (int i = 0; i < total; i++) { Block block = new Block(RandomBlock()); block.BottomPosition = i; block.CastsShadows = true; block.Height = 1; block.Mass = Constants.BlockMass; stack.Add(block); } } } }
/// <summary> /// This event is used to handle jumping from one block to /// another. /// </summary> private void OnDirectionChanged( object sender, BlockStackEventArgs args) { // At the apex, we change our direction by moving to the // next stack as appropriate. Start by ignoring direction // 0 since that is an inplace jumping. if (direction == 0) { return; } // Get the destination stack Board board = Game.State.Board; BlockStack srcStack = board[X, Y]; BlockStack destStack = null; if (direction == 1) { destStack = board[X, Y - 1]; } else if (direction == 2) { destStack = board[X + 1, Y]; } else if (direction == 3) { destStack = board[X, Y + 1]; } else if (direction == 4) { destStack = board[X - 1, Y]; } // Make sure we are still a valid destination if (IsInvalidTarget(srcStack, destStack)) { // We want to reverse the direction, but stay in the // same stack because our destination became // invalid. This works because the code will show them // "bouncing" back. if (direction == 1) { direction = 3; } else if (direction == 2) { direction = 4; } else if (direction == 3) { direction = 1; } else if (direction == 4) { direction = 2; } // We are done srcStack.Add(this); return; } // Move this block to the next one srcStack.Remove(this); destStack.Add(this); // Change our coordinates if (direction == 1) { BottomPosition -= 4; Y--; } else if (direction == 2) { OffsetX -= 101; X++; } else if (direction == 3) { BottomPosition += 1; Y++; } else if (direction == 4) { OffsetX += 101; X--; } }
/// <summary> /// This function causes the prayer to jump and potentially /// move around the board. /// </summary> public void Jump() { // Reset the time timeUntilBounce = Entropy.NextDouble( Constants.MinimumCharacterBounce, Constants.MaximumCharacterBounce); // We want the characters to bounce around a bit, so // we randomly pick a direction to jump into. We get a // random number between 0 and 4: // 0 no change // 1 north // 2 east // 3 south // 4 west // Actually, 0 happens because of fallback direction = Entropy.Next(4) + 1; // East/West only // TODO Fix if (direction == 1 || direction == 3) { direction++; } // If we haven't been accepted, we don't move around if (!IsAccepted) { direction = 0; } // Do sanity checking on the bounds of the stage. If // we are on the end and we would have jumped off, set // the direction to "none" Board board = Game.State.Board; if (direction == 1 && Y == 0) { direction = 0; } else if (direction == 2 && X == board.Columns - 1) { direction = 0; } else if (direction == 3 && Y == board.Rows - 1) { direction = 0; } else if (direction == 4 && X == 0) { direction = 0; } // If we still non-zero, then get the appropriate // stacks BlockStack myStack = board[X, Y]; BlockStack destStack = myStack; if (direction > 0) { // Get the proper stack and make sure it is a // valid target if (direction == 1) { destStack = board[X, Y - 1]; } else if (direction == 2) { destStack = board[X + 1, Y]; } else if (direction == 3) { destStack = board[X, Y + 1]; } else if (direction == 4) { destStack = board[X - 1, Y]; } // We don't jump if the destination block isn't a // ground or immobile block. We also don't jump if // the difference in height is more than one. if (IsInvalidTarget(myStack, destStack)) { direction = 0; destStack = myStack; } } // Figure out the vector, we need a higher one if we // are jumping north and/or if the destination block // is higher. myStack.Remove(this); float myTop = myStack.TopPosition; float destTop = destStack.TopPosition; float vector = Constants.PrayerBounceVector; myStack.Add(this); if (direction == 1) { vector += Constants.PrayerBounceNorthVector; } if (destTop > myTop) { vector += Constants.PrayerBounceUpVector; } // Add a positive vector Vector += vector; }
BlockStack MergeStacks(IBlock b1, IBlock b2) { BlockStack ret = new BlockStack(); if ((b1 is BlockStack)) { BlockStack bs1 = b1 as BlockStack; ret.AddRange(bs1); } else { // We need to remove b1 from its parent before adding it to the newly merged stack // otherwise consider such a scenario: // 1- We drag a stack block into an existing stackBlock in a C block // 2- 'b1' is the existing stackBlock, it is not added to ret, but is still an arg of the C block // 3- after merge is called, we'll try to set ret as the arg of the C block; the C will try // to remove the old arg (b1)...but it doesn't have C as a parent! exception thrown b1.ParentRelationship.Detach(this); ret.Add(b1); } if ((b2 is BlockStack)) { BlockStack bs2 = b2 as BlockStack; ret.AddRange(bs2); } else { b2.ParentRelationship.Detach(this); ret.Add(b2); } return ret; }
private IBlock ToBlockStack(JToken json) { BlockStack b = new BlockStack(); for (int i = 1; i < json.Count(); ++i) { IBlock subBlock = ToBlock(json[i]); if (i == 1 && subBlock is ProcDefBlock) currentProcDef = subBlock as ProcDefBlock; b.Add(subBlock); } currentProcDef = null; return b; }
/// <summary> /// Adds some bugs into the stage. /// </summary> public void AddBugs(int howMany) { // Loop through the bugs for (int i = 0; i < howMany; i++) { // Create a bug Bug bug = new Bug(); // Place the bug while (true) { // Find a random location int col = Entropy.Next(0, Columns); int row = Entropy.Next(0, Constants.BoardRows); BlockStack stack = this[col, row]; // Go through the stack, looking for bugs float immobilePosition = 0; bool foundGround = false; bool validStack = false; foreach (Block block in stack) { // Don't bother if we already have a bug here if (block.Sprite.ID == Constants.BugBlockName) { } // Make sure we have at least one land block else if (IsGroundBlock(block)) { foundGround = true; } // If we have an immobile, keep it else if (block.Sprite.ID == Constants.ImmobileBlockName) { immobilePosition = Math.Max(block.TopPosition, immobilePosition); validStack = true; } // Not valid, a character or someting else { validStack = false; break; } } // See if we have a valid one if (!validStack || !foundGround) { continue; } // Put the bug in bug.BottomPosition = immobilePosition; bug.X = col; bug.Y = row; bug.BlockStack = stack; stack.Add(bug); stack.Sort(); Game.State.Bugs.Add(bug); break; } } }
/// <summary> /// Random grabs or drops a block on the board. /// </summary> private void ChangeBoard(UpdateArgs args) { // Loop through the blocks to see if they are too high. If // they are, remove them. LinkedList <Block> tmpBlocks = new LinkedList <Block>(); tmpBlocks.AddAll(blocks); foreach (Block b in tmpBlocks) { if (b.BottomPosition > 10) { stacks[b].Remove(b); blocks.Remove(b); stacks.Remove(b); } } // Decrement the counter for the timeout secondsUntilChange -= args.SecondsSinceLastUpdate; if (secondsUntilChange > 0) { // We aren't changing anything return; } // Reset it secondsUntilChange = Entropy.NextDouble() * 2; // Pick a random coordinate int x = Entropy.Next(0, BoardColumns); int y = Entropy.Next(0, BoardRows); BlockStack stack = board[x, y]; // Make sure we aren't already doing something here foreach (Block b in blocks) { if (stacks[b] == stack) { // Don't bother this time return; } } // We have a stack, decide if we are going to drop or grab // something from the stack. bool drop = Entropy.Next(0, 2) == 0; if (stack.Count > 5) { // Don't go over 5 high drop = false; } if (stack.Count == 1) { // Don't go below 1 high drop = true; } // Figure out what to do if (drop) { // Create a new block Block nb = new Block(RandomBlock()); nb.BottomPosition = 10; nb.Vector = Constants.DroppedVector; nb.Mass = Constants.BlockMass; nb.IsMoving = true; nb.CastsShadows = true; nb.Height = 1; stack.Add(nb); } else { // Grab the top block Block tb = stack.TopBlock; tb.Vector = -Constants.DroppedVector; tb.Mass = 0; tb.IsMoving = true; } }
/// <summary> /// Internal function to determine if the target is valid or /// not. /// </summary> private bool IsInvalidTarget(BlockStack srcStack, BlockStack destStack) { // Remove ourselves to take ourselves out of the calculation srcStack.Remove(this); try { // Make sure the destination is a ground block or an // immobile one, but nothing else Block dest = destStack.TopBlock; if (!Board.IsGroundBlock(dest) && !Board.IsImmobileBlock(dest)) { // Neither ground or immobile return true; } // We need the distance to be 1 or less float dist = Math.Abs(srcStack.TopPosition - destStack.TopPosition); if (dist > 1) { // Too much to jump return true; } // It is good return false; } finally { // Put ourselves back srcStack.Add(this); } }