Having some trouble splicing


(Nate ) #1

Hey guys! I am having some issues trying to splice my array. I just want to remove the last item in the array, but every which way I attempt to do so ends up wiping the entire array. Here is what I am currently doing:

		public function click():void
	{
	
			Inventory_Box.inventory.splice(-1, Inventory_Box.inventory.length);
			world.remove(this);

Thanks guys! If I don’t answer right away I fell asleep on my keyboard lol


(JP Mortiboys) #2

To remove the last entry in the array just use pop instead of splice


(Nate ) #3

I tried pop also and it was still wiping the entire array D:


(Nate ) #4

Okay maybe you guys can help! I started a new project just so I could focus on getting some logic down. Here is what I have so far.

Basically it is a window with items all over the place and an inventory on the left hand side. When you click an item it disappears from the window and appears in the inventory. When you click the item inside of the inventory I am trying to make it disappear from the inventory window and appear back in the main window.

I have the items disappearing from the inventory window, but as soon as I remove one from the inventory window, which is also a visual representation of the actual inventory array, the entire inventory array gets nuked.

Here is some code, let me know if you might need more!

This is my inventory update:

override public function update():void 
	{
		
		for (var i:int = 0; i < inventory.length; i ++)
		{
			if (inventory[i] is Item_Black)
			{
				//add item_black ICON to the inventory box
				FP.world.add(new invBlackItem(38 * i + 15, 38));
			}
			
			if (inventory[i] is Item)
			{
				//add item_white ICON to the inventory box
				FP.world.add(new invItem(38 * i + 15, 38));
			}
		}

			if(inventory.length - 1 == MAX_ITEMS)
		{
			trace("inventory is full!");
			Inventory_Box.messageText.visible = true;
		}
		else
		{
			Inventory_Box.messageText.visible = false;
		}
		
		
	        trace("inventory length:" + inventory.length);
		trace("inventory:" + inventory);

	}//end of update

This is my white_stage_item update and related click function:

override public function update():void
	{
		if (collidePoint(x, y, Input.mouseX, Input.mouseY))
		{
			if (Input.mouseReleased) click();
		}
	}
	
	
	public function click():void
	{
		
		if (Inventory_Box.inventory.length <= Inventory_Box.MAX_ITEMS)
		{
			world.recycle(this);
			Inventory_Box.inventory.push(this);
		}
		
	}

This is where the issue is I think, this is the ICON item update and related click function, it is here that I am trying to take off the CLICKED element of the array, not the entire array:

override public function update():void
	{
		if (collidePoint(x, y, Input.mouseX, Input.mouseY))
		{
			if (Input.mouseReleased) click();
		}
	}
	
	
	public function click():void
	{
	
			Inventory_Box.inventory.pop();
			world.remove(this);

	}

Any help would be much appreciated! Thanks guys!


(JP Mortiboys) #5

It certainly shouldn’t - there must be something else in the code… maybe you’re running the function more than once? If it was in update in response to Input.mouseDown then it would run several times, unless you’re physically capable of pressing and releasing the mouse button in less than 1/60th of a second (if this is the issue you should be using Input.mousePressed or Input.mouseReleased instead).

By the way, I’m a little confuzzled (well, curious) about the interface for this game - you want to support mouse events, but the player only moves and shoots in 4 directions? That seems odd; 4 directions is usually more of a keyboard-only kind of affair. Can you click to move, or only use the keyboard? How do you shoot? (Normally if you’re using the mouse to shoot in this sort of game the bullets wouldn’t only go in 4 directions). Just curious.


(Nate ) #6

Instead of trying to describe every aspect here is a link to what I currently have:

https://www.dropbox.com/s/kjj30pjxqfdc4qz/InventoryWork.swf

As you you can see, you can click items, and they appear in the inventory, but when you click them in the inventory, it resets the whole array, in that if you remove one item from the inventory window, then try to click a new item from the main window it starts at index 0.


(Nate ) #7

This is unrelated to my game project Raptor, I am just trying to iron out the inventory system first. In the game the mouse will serve simply to use items in the inventory, the walking and such is done with the controls, and attack is done with the space bar! :grin:

Looking at what I have created so far for my practice inventory can you determine where my problem may be?


(Nate ) #8

I can also upload the entire project if you want to see all of it and think it would be easier to help that way!

EDIT:

I did what you suggested in the my inventory_icon update and it is still doing the same thing :confused:

override public function update():void
	{
		if (collidePoint(x, y, Input.mouseX, Input.mouseY) && Input.mousePressed)
		{
			click();
		}
	}
	
	
	public function click():void
	{
			world.recycle(this);
			Inventory_Box.inventory.pop();

	}

(JP Mortiboys) #9

Ok, let’s assume for a second that every object in your world can be adequately represented by a single integer (0=nothing, 1=rock, 2=banana, whatever). If you have more complex items that require their own stats then this won’t work as-is, but let’s not worry about that for now.

(note: I can’t really look at the swf right now because I’m at work, but I will look later on)

You seem to have your item picky-uppy routine sorted; that’s good. But for completeness, I’ll deal with that too.

You should stick all of the inventory object graphics into a single bitmap (tileset); this will make things easier. Keep the first tile blank as FP’s Tilemap class can be odd about items with the number 0 as opposed to being empty, so we’ll always have 0 as empty.

public class TestWorld extends World {
  public var inventory:Inventory;

  override public function begin():void {
    // Create and add inventory object
    inventory = new Inventory();
    add(inventory);

    // Add 30 pickups at random locations
    for (var i:int = 0; i < 30; i++) {
      // The 1 is the item type... probably best to use constants for this
      add(new Pickup(FP.rand(FP.width-32), FP.rand(FP.height-32), 1);
    }
  }
}

public class Pickup extends Entity {
  public var itemType:int;

  public function Pickup(x:Number, y:Number, itemType: int) {
    super(x, y);
    this.itemType = itemType;
    width = 32; height = 32; // size of the object on screen
    type = "pickup";
    // Sort out graphic here... I'm going to use a blue box
    this.graphic = Image.createRect(width, height, 0x0033FF);
  }

  override public function update():void {
    // If the user has clicked the mouse over this pickup
    if (Input.mouseReleased && collidePoint(x, y, world.mouseX, world.mouseY)) {
      // Get a reference to the inventory
      var inventory:Inventory = world.classFirst(Inventory) as Inventory;
      // Try to add this item to it
      if (inventory.addItem(itemType)) {
        // Fine, item added - remove the pickup from the world
        world.remove(this);
      }
      else {
        trace("Could not add item to inventory");
      }
    }
  }
}

public class Inventory extends Entity {
    private static const MAX_ITEMS:int = 5;
    private static const TILE_WIDTH:int = 38;
    private static const TILE_HEIGHT:int = 38;

    // Uncomment the lines below to use a proper embedded graphic
    /*
    [Embed (source="/assets/inventory.png" )]
    private static const INVENTORY_GFX:Class;
    */
    private static var quickFixBmp:BitmapData;

    var tilemap:Tilemap;

    public function Inventory() {
      if (!quickFixBmp) {
        quickFixBmp = new BitmapData(MAX_ITEMS * TILE_WIDTH, TILE_HEIGHT, true, 0);
        quickFixBmp.fillRect(new Rectangle(TILE_WIDTH, 0, TILE_WIDTH, TILE_HEIGHT), 0xFFFFFF);
      }
      tilemap = new Tilemap(quickFixBmp /*INVENTORY_GFX*/, MAX_ITEMS * TILE_WIDTH, TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
      tilemap.setRect(0, 0, MAX_ITEMS, 1, 0);
      super(15, 15, tilemap);
      width = tilemap.width; height = tilemap.height;
    }

    override public function update():void {
      // If the mouse was clicked and is over the inventory
      if (Input.mousePressed && collidePoint(x, y, Input.mouseX, Input.mouseY)) {
        // Calculate the zero-based slot number that the user clicked on
        var slot:int = ~~((Input.mouseX - x)/TILE_WIDTH);
        // Get the item type at that slot
        var itemType:int = tilemap.getTile(slot, 0);
        // Check if it's an actual item
        if (itemType) {
          trace("Clicked on item type " + itemType = " at slot #" + slot);
          // Do something with the item here
          // And remove the item from inventory since it's now used
          tilemap.setTile(slot, 0, 0);
        }
        else {
          trace("Ain't nowt there guv'na");
        }
      }
    }

    public function addItem(itemType:int):boolean {
      // Go through each slot
      for (var i:int=0; i<MAX_ITEMS; i++) {
        // If this slot is empty
        if (tilemap.getTile(i, 0) == 0) {
          // It isn't now!
          tilemap.setTile(i, 0, 1);
          return true;
        }
      }
      // If we made it here then the inventory is full
      return false;
    }
}


That should do the trick! (I just typed this, not tested, so there might be typos in there)

EDIT: Added “quick fix” bitmap with a white square for item #1


(Nate ) #10

Okay thank you so much! I am typing it now all into some new classes and going to test it! I will report back!

I have never worked with tilemaps before they always scared me for some reason but I think it is sounding like I should have been using them… lol

Thanks again! :smiley:

EDIT:

Quick question, where did the INVENTORY_GFX come from? Do I have to set that up somewhere?


(JP Mortiboys) #11

See my edit - it would be a reference to an image, but I anticipated this question and added fallback code so you don’t have to!


(Nate ) #12

Okay it has appeared to have worked! :grin:

How would I now go about getting the picked up items to appear in an actual inventory window using all of the code you provided? :smiley:

EDIT:

Also where would I need to plug in all the different pick up items information?


(JP Mortiboys) #13

I forgot to mention that the Inventory's graphic (tilemap) should have relative set to false; this way it won’t scroll with the world but will stay fixed in position (you’ll notice that I used Input.mouseX for collision with the inventory but world.mouseX for the pickup… one will scroll with the camera, the other won’t).

You want a popup inventory window? Just show and hide the Inventory entity. You can add more graphics to it that just the tilemap if you want a fancy background or whatever.

You want the items on multiple lines? No problem either…

public class Inventory extends Entity {
    private static const ITEM_COLS:int = 5;
    private static const ITEM_ROWS:int = 3; // (this allows 3×5 = 15 items total)
    private static const TILE_WIDTH:int = 38;
    private static const TILE_HEIGHT:int = 38;

    // Uncomment the lines below to use a proper embedded graphic
    /*
    [Embed (source="/assets/inventory.png" )]
    private static const INVENTORY_GFX:Class;
    */
    private static var quickFixBmp:BitmapData;

    var tilemap:Tilemap;

    public function Inventory() {
      if (!quickFixBmp) {
        quickFixBmp = new BitmapData(2 * TILE_WIDTH, TILE_HEIGHT, true, 0); // This is one tile per item type, regardless of the number of slots you want
        quickFixBmp.fillRect(new Rectangle(TILE_WIDTH, 0, TILE_WIDTH, TILE_HEIGHT), 0xFFFFFF);
      }
      tilemap = new Tilemap(quickFixBmp /*INVENTORY_GFX*/, ITEM_COLS * TILE_WIDTH,ITEM_ROWS *  TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
      tilemap.setRect(0, 0, ITEM_COLS, ITEM_ROWS, 0);
      tilemap.relative = false;
      super(0, 0, tilemap);
      width = tilemap.width; height = tilemap.height;
      // Centre on screen
      x = FP.halfWidth - halfWidth; y = FP.halfHeight - halfHeight;
    }

    override public function update():void {
      // If the mouse was clicked and is over the inventory
      if (Input.mousePressed && collidePoint(x, y, Input.mouseX, Input.mouseY)) {
        // Calculate the zero-based slot number that the user clicked on
        var slotCol:int = int((Input.mouseX - x)/TILE_WIDTH);
        var slotRow:int = int((Input.mouseY - y)/TILE_HEIGHT);
        // Get the item type at that slot
        var itemType:int = tilemap.getTile(slotCol, slotRow);
        // Check if it's an actual item
        if (itemType) {
          trace("Clicked on item type " + itemType = " at slot #" + (slotRow*ITEM_COLS+slotCol));
          // Do something with the item here
          // And remove the item from inventory since it's now used
          tilemap.setTile(slotCol, slotRow, 0);
        }
        else {
          trace("Ain't nowt there guv'na");
        }
      }
    }

    public function addItem(itemType:int):boolean {
      // Go through each slot
      for (var j:int=0; j<ITEM_ROWS; j++) {
      for (var i:int=0; i<ITEM_COLS; i++) {
        // If this slot is empty
        if (tilemap.getTile(i, j) == 0) {
          // It isn't now!
          tilemap.setTile(i, j, 1);
          return true;
        }
      }}
      // If we made it here then the inventory is full
      return false;
    }
}

Edit: replaced ~~(...) with int(...) - the former is slow in AS3. I’ve been doing too much js…


(Nate ) #14

Okay cool I have just updated the code! And thank you for your patience however I still am not getting anything to display to the screen and I have no idea how to trace this to test it. I do not know anything about tilemaps and am now getting very confused lol

Right now it compiles and fills the screen with squares, I can click 5 of them and then it tells me the inventory is full. I would like the five that I click to appear in an inventory window but I am not sure how to manipulate this.

EDIT:

Also this part of your previously supplied code is throwing errors, I think the definition is missing:

		// Centre on screen
		left = FP.halfWidth - halfWidth; 
		top = FP.halfHeight - halfHeight;

(JP Mortiboys) #15

That’s odd; it should display the inventory on the screen at all times (unless you’ve set it to invisible).

Find this line:

quickFixBmp = new BitmapData(MAX_ITEMS * TILE_WIDTH, TILE_HEIGHT, true, 0);

and change it to:

quickFixBmp = new BitmapData(MAX_ITEMS * TILE_WIDTH, TILE_HEIGHT, false, 0);

(this will make the empty squares black instead of transparent)

Hopefully you’ll see a black rectangle which is the empty inventory; it should fill up with white squares as you click them.


(JP Mortiboys) #16

One other thing… in the world code, this line:

if (inventory.addItem(itemType)) {

you need to put 1 for the itemType:

if (inventory.addItem(1)) {

I was writing it as a generic routine, but for now just put 1.


(Nate ) #17

Okay neat! Setting that value to false made it appear! :grin:

What should I do with the //Centre on screen code to make it run?

EDIT:

So now if I would like to pick up 10 different items, how would I go about assigning them values and how would I get them to appear in the inventory?


(JP Mortiboys) #18

My mistake; I put left and top instead of x and y; fixed now.


(Nate ) #19

Okay I have reverted it to a one horizontal line inventory. :grin:

When you have the chance can you explain how I would go about adding different items and item types.

And also how I could trace what item type is being removed from the inventory?

Thanks!


(JP Mortiboys) #20

There are lots of different ways of doing that, depending on how complicated you want the item effects to be.

Let’s stay really really simple, we’re going to define 3 different types of objects : a cherry, a lime and a blueberry.

The actual logic of the objects is going to be handled in the world (or player) class; you’ll see what I mean by that in a second. If you had more complicated objects you’d use a different approach (subclassing an Item class for each type).

So, we’re going to have this class for item descriptions and images:

public class Items {
    public static const ITEM_TYPE_NONE:int = 0;
    public static const ITEM_TYPE_CHERRY:int = 1;
    public static const ITEM_TYPE_LIME:int = 2;
    public static const ITEM_TYPE_BLUEBERRY:int = 3;
    public static const ITEM_TYPE_COUNT:int = 4; // Last one

    public static const itemNames:Vector.<String> = Vector.<String>(["Nothing", "Cherry", "Lime", "Blueberry"]);

    public static var pickupsBmp:BitmapData;
    public static var inventoryBmp:BitmapData;

    // Static initialization code to create graphics
    {
      var s:Sprite = FP.sprite, g:Graphics = s.graphics;
      // Create dots for pickups
      pickupsBmp = new BitmapData(32 * 4, 32, true, 0);
      g.clear();
      g.beginFill(0xFF0000); g.drawCircle(32 * 0 + 16, 16, 16); g.endFill();
      g.beginFill(0x00FF00); g.drawCircle(32 * 1 + 16, 16, 16); g.endFill();
      g.beginFill(0x0000FF); g.drawCircle(32 * 2 + 16, 16, 16); g.endFill();
      pickupsBmp.draw(s);
      // Create rectangles for inventory
      inventoryBmp = new BitmapData(38 * 4, 38, false /*true*/, 0);
      g.clear();
      g.beginFill(0xFF0000); g.drawRect(38 * 0, 0, 38, 38); g.endFill();
      g.beginFill(0x00FF00); g.drawRect(38 * 1, 0, 38, 38); g.endFill();
      g.beginFill(0x0000FF); g.drawRect(38 * 2, 0, 38, 38); g.endFill();
      inventoryBmp.draw(s);
    }
}

Now a couple of changes to the Inventory and Pickup classes to handle the graphics

// Only showing changes; the rest stays as it is

public class Pickup extends Entity {
  public function Pickup(x:Number, y:Number, itemType: int) {
    super(x, y);
    this.itemType = itemType;
    width = 32; height = 32; // size of the object on screen
    type = "pickup";
    // Sort out graphic here...
    var sprite = new Spritemap(Items.pickupBmp);
    sprite.frame = itemType;
    this.graphic = sprite;
  }
}

public class Inventory extends Entity {
    public function Inventory() {
      tilemap = new Tilemap(Items.inventoryBmp, TILE_WIDTH*Items.ITEM_TYPE_COUNT, TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
    }

    override public function update():void {
      ...
        if (itemType) {
          trace("Clicked on item type " + itemType = " at slot #" + (slotRow*ITEM_COLS+slotCol));
          // Do something with the item here
          switch(itemType) {
          case Items.ITEM_TYPE_CHERRY:
            trace("Ugh, a cherry stone!"); break;
          case Items.ITEM_TYPE_LIME:
            trace("Well I'm not getting scurvy!"); break;
          case Items.ITEM_TYPE_BLUEBERRY:
            trace("Mmmm, antioxidants!"); break;
          }
          // And remove the item from inventory since it's now used
          tilemap.setTile(slotCol, slotRow, 0);
        }
      ...
    }
}

And now the world:

// ...
    // Add 30 RANDOM pickups at random locations
    for (var i:int = 0; i < 30; i++) {
      // The 1 is the item type... probably best to use constants for this
      add(new Pickup(FP.rand(FP.width-32), FP.rand(FP.height-32), 1 + FP.rand(Items.ITEM_TYPE_MAX - 1));
    }
// ...
if (inventory.addItem(itemType)) {
        // Fine, item added - remove the pickup from the world
        world.remove(this);
        // Display debug message
       trace("You picked up a " + Items.itemNames[itemType]);
}

There are lots of other ways of doing this! This is appropriate for simple objects, but for something like a roguelike or point 'n click adventure where objects can be combined and have complex behaviours, you’d need something more in-depth.