How to rearranged items in an inventory - actionscript-3

I'm creating a game in AS3.
The player can grabb items and add it to his inventory in a line.
Everything is working, but I've got a bug when the player use an item wich is in the middle of the line.
It doesen't rearanged well..
(here is a video if I'm not very clear : http://ul.to/z7su5dqm or here https://drive.google.com/file/d/0B5-MjJcEPm3lTTlDV09MYWxMOFE/edit?usp=sharing)
I've got the code that add the item to the inventory :
public function addInvItem(itemName:String):void{
var itemRef:Object = getDefinitionByName(itemName.toLowerCase()+"Inv");
var addedItem:MovieClip = new itemRef;
addedItem.displayName = itemName;
if (playerItems.length < 8){ // This is for the top row of up to 4 items
addedItem.y = 520;
addedItem.x = 60 + (playerItems.length) * 100;
}
if (isUnique(addedItem)){
this.addChild(addedItem);
playerItems.push(addedItem);
allItems.push(addedItem);
addedItem.buttonMode = true;
addedItem.invItem = true;
addedItem.addEventListener(MouseEvent.CLICK, useItem, false, 0, true);
puzzle = Engine.puzzle;
puzzle.gotItem(addedItem.displayName);
}
So the first item is add at x= 60 and y = 520.
And then I've got this code in order to remove and rearranged the items :
public function removeInvItem(itemName:String):void{
removedItem = itemName;
var itemNum:int;
for (var i in playerItems){
if (playerItems[i].displayName == itemName){
playerItems[i].visible = false;
itemNum = i;
} else {
playerItems[i].visible = true;
}
}
playerItems = playerItems.filter(checkForItem);
// Rearrange the rest of the items
for (i in playerItems){
if (i >= itemNum){
playerItems[i].x -= 100;
}
}
}
Do you see where could be the error that push my first item ? (I suppose it came from playerItems[i].x -= 100).
I must find a way to tell the code that first item can't be less than x = 60 but the other must move x= -100 everytime their are used...
Any idea how I can do that ?
Thank you very much,

You are correct in assuming playerItems[i].x -= 100; is where the problem is caused. You are subtracting the current x position without any checks for if that falls over your inventory icon asset.
You could do something like this instead:
public function removeInvItem(itemName:String):void{
removedItem = itemName;
var itemNum:int;
for (var i in playerItems){
if (playerItems[i].displayName == itemName){
playerItems[i].visible = false;
itemNum = i;
} else {
playerItems[i].visible = true;
}
}
adjustInventory( itemNum );
}
public function adjustInventory( itemNum:int ):void {
var i:int;
for ( i=itemNum; i < playerItems.length; i++ ) {
//you can replace 60 with inventoryIcon.x + inventoryIcon.width instead
playerItems[i].x -= playerItems[i].x - 100 >= 60 ? 100 : playerItems[i].x - 60;
}
}
This evaluates the distance you are about to move before you do it, and only moves the necessary inventory items. I haven't tested this code but this should put you on the right path.

Related

Flash hangs when I execute this particular loop

Please do forgive me if this question is very stupid, but I couldn't figure out what to do, which is why I ask it.
Here, I declared a small white square as a movieclip symbol(Dot) and I wish to generate it after a specific gap on the entire screen.
So, when I execute this (test it) code on Flash CS6, it hangs. After that I will be forced to end the program without doing anything further.
import flash.ui.*;
stop();
Mouse.hide();
var ctX:int = 0,ctY:int = 0,done:Boolean = false;
var base:Object = MovieClip(root);
this.addEventListener(Event.ENTER_FRAME, eFrame);
function eFrame(event:Event):void
{
while (done == false)
{
var dots:Dot = new Dot ;
dots.x += (50 * ctX);
dots.y += (50 * ctY);
ctX++;
if (ctX == 11)
{
ctX = 0;
ctY++;
}
else if (ctX == 11 && ctY == 10)
{
done = true;
break;
}
stage.addChild(dots);
}
}
Thank you in advance.
I have attached a screenshot of the situation.
The loop will never finish because the condition for done=true is ctX==11, but ctX==11 causes ctX=0 in the first condition:
if (ctX == 11) // when ctX is 11
{
ctX = 0; // ctX is reset to 0
ctY++;
}
else if (ctX == 11 && ctY == 10) // so you will never have ctX=11 here
{
done = true;
break; // (Tip: you don't need `done` since `break` exits the loop)
}
You could fix this by swapping the conditions, but I think this use of a while loop is unnecessarily complex and fragile. Why not just use two for loops:
for (var ctX:int = 0; ctX < 11; ctX++) {
for (var ctY:int = 0; ctY < 11; ctY++) {
var dots:Dot = new Dot();
dots.x = (50 * ctX);
dots.y = (50 * ctY);
stage.addChild(dots);
}
}
This is much clearer and less fragile because the loops are fixed length.
You could even do it with one for loop and a little math, but you lose some clarity:
for (var i:int = 0; i < 11 * 11; i++) {
var dots:Dot = new Dot();
dots.x = (50 * (i % 11));
dots.y = (50 * int(i / 11));
stage.addChild(dots);
}

Pairing a draggable object to a target object in AS3

I'm currently stuck with my approach below. I'm not entirely sure if using "hitTestObject" method is appropriate in pairing the pieces to their respective place. I was able to at least match the chess piece to their respective location (that's the best I can do and I feel i'm doing it wrong) but I'm now stuck in counting how many pieces are actually in their correct places. e.g. when I move the pawn to a different tile, it will still count as one, I also want to avoid duplicate counting, example, If pawn is already in the correct location, it will just count as 1, and if it was moved, then that count will be removed. Only count the pieces that are in the correct tile.
My goal here is to be able to make all the chess pieces draggable and determine if they're in their respective location. If ALL the chess pieces are in their location, it will trace or call a function.
Thank you!
import flash.events.Event;
import flash.display.MovieClip;
import flash.events.MouseEvent;
/* Declaring an X and Y variable to be used as a reset container */
var xPos: int, yPos: int;
/* Attaching event listeners for each chess piece */
addListeners(
king, queen, bishop_1, bishop_2, knight_1, knight_2, rook_1, rook_2,
pawn_1, pawn_2, pawn_3, pawn_4, pawn_5, pawn_6, pawn_7, pawn_8);
/* Getting the original x and y postion to be used as a reset */
function getPosition(currentTarget: Object): void {
xPos = currentTarget.x;
yPos = currentTarget.y;
}
/* Function to get the suffix value of an object. example, I need to get the value 4 from "pawn_4" */
function getLastCharInString($s: String, $pos: Number): String {
return $s.substr($s.length - $pos, $s.length);
}
/* A simple function that rotates the chess piece */
function lift(object: Object, rot: Number) {
object.rotation = rot;
}
function dragObject(e: MouseEvent): void {
getPosition(e.currentTarget);
lift(e.currentTarget, -10);
getChildByName(e.currentTarget.name + "_hs").alpha = 1;
e.currentTarget.startDrag();
}
/* This variable is supposed to hold the value of each piece that is correctly placed in each tile.
The total score should be 16 as there are 16 pieces. Only correcly placed piece should be added in the total score. */
var counter:int;
function stopDragObject(e: MouseEvent): void {
var curretTarget = e.currentTarget.name;
lift(e.currentTarget, 0);
/* Hide active hotspots */
getChildByName(e.currentTarget.name + "_hs").alpha = 0;
var multiplePieceSufix = Number(getLastCharInString(curretTarget, 1));
if (multiplePieceSufix >= 1) {
/* Boolean variables that checks whether the current piece is active*/
var isPawn: Boolean = false,
isBishop: Boolean = false,
isKnight: Boolean = false,
isRook: Boolean = false,
currentTargeName;
var widthDiff = getChildByName(e.currentTarget.name + "_hs").width - getChildByName(e.currentTarget.name).width / 2;
var heightDiff = getChildByName(e.currentTarget.name + "_hs").height - getChildByName(e.currentTarget.name).height / 2;
if (curretTarget.substr(0, 4) == "pawn") {
isPawn = true;
} else if (curretTarget.substr(0, 6) == "bishop") {
isBishop = true;
} else if (curretTarget.substr(0, 6) == "knight") {
isKnight = true;
} else if (curretTarget.substr(0, 4) == "rook") {
isRook = true;
}
if (isPawn == true) {
/* there are total of 8 pieces of pawn */
for (var w = 1; w < 9; w++) {
currentTargeName = this["pawn_" + w + "_hs"];
if (e.target.hitTestObject(currentTargeName)) {
/* For some reason the chess pieces are not aligning with their "_hs" version, I already checked their registry point and it seem to be normal.
so to fix, I had to manually add some hard coded values to adjust their location. */
e.currentTarget.x = currentTargeName.x - 8;
e.currentTarget.y = currentTargeName.y + currentTargeName.height;
}
}
} else if (isBishop == true) {
for (var x = 1; x < 3; x++) {
currentTargeName = this["bishop_" + x + "_hs"];
if (e.target.hitTestObject(currentTargeName)) {
e.currentTarget.x = currentTargeName.x - 9;
e.currentTarget.y = currentTargeName.y + currentTargeName.height - 18;
}
}
} else if (isKnight == true) {
for (var y = 1; y < 3; y++) {
currentTargeName = this["knight_" + y + "_hs"];
if (e.target.hitTestObject(currentTargeName)) {
e.currentTarget.x = currentTargeName.x - 8;
e.currentTarget.y = currentTargeName.y + currentTargeName.height;
}
}
} else if (isRook == true) {
for (var z = 1; z < 3; z++) {
currentTargeName = this["rook_" + z + "_hs"];
if (e.target.hitTestObject(currentTargeName)) {
e.currentTarget.x = currentTargeName.x - 8;
e.currentTarget.y = currentTargeName.y + 62;
}
}
}
} else {
if (e.target.hitTestObject(getChildByName(e.currentTarget.name + "_hs"))) {
/* Again, I'm not sure why the pieces are not aligning as intended.
modX and modY is a holder for the adjustment value. I'm not comfortable
seeing this approach myself, but I also run out of ideas how to fix it. */
var modX: Number, modY: Number;
if (e.currentTarget.name == "king") {
modX = 11;
modY = 53;
} else {
modX = 11;
modY = 29;
}
e.currentTarget.x = getChildByName(e.currentTarget.name + "_hs").x - modX;
e.currentTarget.y = getChildByName(e.currentTarget.name + "_hs").y + getChildByName(e.currentTarget.name + "_hs").height - modY;
}
}
/* This is supposed to add to the total score or count of how many pieces are placed correctly.
Thie problem with thi scounter, as it also counts any piece that is places to any "_hs" */
counter++;
trace(counter);
e.currentTarget.stopDrag();
}
function addListeners(...objects): void {
for (var i: int = 0; i < objects.length; i++) {
objects[i].addEventListener(MouseEvent.MOUSE_DOWN, dragObject);
objects[i].addEventListener(MouseEvent.MOUSE_UP, stopDragObject);
// hide hotspots
getChildByName( objects[i].name + "_hs" ).alpha = 0;
}
}
Source: Download the FLA here
--
Updates:
I have added comments in my code to clarify what I'm trying to accomplish.
I'm planning to do board game in flash which has similar function and behaviour to this. User can drag the object to a specified tile and check wether that object belongs there or not.
After reviewing your code, your question is quite broad. I'm going pair it down to what seems to be your main concern - the score / counting correctly moved pieces.
Right now, you do the following every time an object is dragged:
counter++;
This means that the counter will increment no matter where you drag the object, and no matter how times you drag the object. (so even if the piece was already in the correct spot, if you dragged it a second time it will still increment your counter).
What you need to do, is associate a flag with each object to indicate whether it is in the correct location or not, and set that flag to the appropriate value every time that object is done dragging.
Something like this:
//don't use target, use currentTarget
if (e.currentTarget.hitTestObject(currentTargeName)) {
e.currentTarget.correct = true; //since MovieClips are dynamic, you can just make up a property on them and assign a value to it.
//to fix your alignment:
e.currentTarget.x = currentTargeName.x + ((currentTargetName.width - e.currentTarget.width) * 0.5);
e.currentTarget.y = currentTargeName.y + currentTargeName.height;
}else{
//if the hit test is false, mark it as NOT correct
e.currentTarget.correct = false;
}
Then, later to know the current count, iterate over all the pieces and check their correct value. This would be much easier if all your pieces were in an array.
var allPieces:Array = [king, queen, bishop_1, bishop_2, knight_1, knight_2, rook_1, rook_2,
pawn_1, pawn_2, pawn_3, pawn_4, pawn_5, pawn_6, pawn_7, pawn_8];
function countCorrect():Boolean {
var ctr:int = 0;
for(var i:int=0;i<allPieces.length;i++){
if(allPieces[i].correct) ctr++;
}
return ctr;
}
trace(countCorrect() + " of " allPieces.length " are correct");
As an aside, this best way to do this would be with some custom class files. That would however require a complete refactoring of your code.
Also, you probably don't want to use hitTestObject, as even if a piece is mostly over a neighbor, it will still be true as long as 1 pixel of it's bound touch 1 pixel of the tile. Better would be to do a hitTestPoint on the tile, and pass in the center point of the piece (the the middle of the piece has to be touching the tile for it to count).
//a point that is the center of the events current target (the piece)
var point:Point = new Point();
point.x = e.currentTarget.x + (e.currentTarget.width * 0.5);
point.y = e.currentTarget.y - (e.currentTarget.height * 0.5);
if (currentTargetName.hitTestPoint(point)) {

AS3 hittest keeps hitting

I have the following problem:
I want to keep a score when i "hittest". I use the following code:
private function fnMoveMap():void
{
for (var i:int = 0; i < vPipeMax; i++)
{
var tmpPipe = _conMap.getChildAt(i);
//trace (tmpPipe.name);
if (tmpPipe._HIT.hitTestPoint(_P.x, _P.y, true))
{
tmpPipe.visible = false;
//stage.removeEventListener(Event.ENTER_FRAME, setScore);
vScores++;
txtScores.text = vScores.toString();
//break;
}
//reset pos
if (tmpPipe.x < 0)
{
//stage.addEventListener(Event.ENTER_FRAME, setScore);
tmpPipe.visible = true;
tmpPipe.x = 1050 - vXSpeed;
tmpPipe.y = randomRangeMC(minPipeY, maxPipeY);
//set score
//vScores++;
//txtScores.text = vScores.toString();
}
else
{
tmpPipe.x -= vXSpeed;
}
}
}
the var vScores keeps counts for 4 to 8 times.
How can i just count one?
The reason your vScores variable is incrementing by 4-8 is because you're looping multiple times with the for loop through vPipeMax.
You either need to restructure your code so that doesn't happen, or break out of the loop as soon as you increment the score.
Adobe's documentation on break: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/statements.html#break

as3 var not listening to x--

So Ive got this code:
function speedUp():void
{
trace ("speed", playerSpeed);
//checks if any boosts are on
if (boostCheck == false) {j = playerSpeed}
//making speed limit
if (playerSpeed < speedLimit)
{
playerSpeed ++;
}
//increasing speed limit
if (speedLimitInc == 100)
{
speedLimit++;
speedLimitInc = 1;
}
trace ("______________j", j);
speedLimitInc ++;
if (boostCheck == true)
{
for (var i:int = boost.length-1; i>=0; i--)
{
var tempBoostN:int = boost[i];
trace ("__________tempBoostN", tempBoostN);
if (tempBoostN >= 0)
{boostCheck = true; colBoolean = false; tempBoostN--;}
else
{
boostCheck = false;
player.y = player.height + 30;
colBoolean = true;
}
if (playerSpeed >= j)
{
if (tempBoostN >= 150)
{
playerSpeed = playerSpeed -1;
} else if (tempBoostN <= 150 && tempBoostN >= 30) {
playerSpeed = playerSpeed - 2;
}
tempBoostN--;
}
}
}
}
Im getting a problem with tempBoostN variable, more to point, its not listening to commands. When the var is, say, 200 (its defined as an int) and I do tempBoostN-- like you see in code, it just stays 200.
I have tried defining the var as a Number as etc.. but it stays the same.
I have also checked that the 200 is actually a Number not a string (if var is int the it cant hold a string, right?) by doing trace(temBoostN - 1); and that worked.
CODE EXPLANATION:
Function handles playerSpeed, first it does usual behaviour (increasing the speed and stuff).
After that (the part where I get the problem) checks for speed boosts activated and for each it decreases the speed back to normal speed when the boost is deactivated. var j holds playerSpeed without any boosts and is used to check if boosts ran out.
All the vars in the code are defined and working properly.
I think what you actually want to do is get the decrement reflected in boost[i]. What is the type of 'boost[i]'. I believe it's int and that's why changing tempBoostN isn't changing boost[i]. There are 2 ways around it:
(1) You put Number in boost[i], so tempBoostN = boost[i] is a assignment by reference as against a value copy being done in case of int
(2) re-assign boost[i] = tempBoostN at the end of loop.

timelineScrubber issue

I have made a timeline scrubber. It is working but I am facing a few problems.
When I click at the end of the track, the scrubber should go and stop at the end of the slider track but instead it goes to end of track, then jumps to to start of animation and replays the animation.
Scrubber does not move smoothly. It is kind of stuck. It does not move freely.
I have attached the fla file in case someone wants to see it. the link is http://www.mediafire.com/?cyk84zf0ndq6r. Could someone tell me where I am going wrong?
import flash.geom.Rectangle;
import flash.events.MouseEvent;
var barLength : Number = slider_mc.bar.width - slider_mc.scrub.width;
var rect : Rectangle = new Rectangle (slider_mc.bar.x, slider_mc.scrub.y, barLength, 0);
var moviePlaying:Boolean = true;
slider_mc.scrub.buttonMode = true;
slider_mc.scrub.addEventListener(MouseEvent.MOUSE_DOWN, this.scrubMouseDownHandler);
function scrubMouseDownHandler (event : MouseEvent) : void {
this.addEventListener("enterFrame",onEnterFrame);
slider_mc.scrub.addEventListener (MouseEvent.MOUSE_MOVE, this.scrubMouseMoveHandler);
slider_mc.scrub.addEventListener(MouseEvent.MOUSE_UP, this.scrubRemoveListenerHandler);
slider_mc.scrub.addEventListener(MouseEvent.MOUSE_OUT, this.scrubRemoveListenerHandler);
slider_mc.scrub.startDrag (false, rect);
}
function scrubMouseMoveHandler (event : MouseEvent) : void {
var spanPercentage : Number = (slider_mc.scrub.x - slider_mc.bar.x) / barLength;
var framePosition : int = int (animationMc.totalFrames * spanPercentage);
if (framePosition < 1) framePosition = 1 ;
slider_mc.bar.sliderFill_mc.scaleX = ((440/animationMc.totalFrames) * animationMc.currentFrame);
//trace (framePosition);
if (moviePlaying==true) {
animationMc.gotoAndPlay(framePosition);
//moviePlaying = false;
} else if(moviePlaying==false) {
animationMc.gotoAndStop(framePosition);
}
}
function scrubRemoveListenerHandler (event : MouseEvent) : void {
slider_mc.scrub.removeEventListener (MouseEvent.MOUSE_MOVE, this.scrubMouseMoveHandler);
slider_mc.scrub.removeEventListener(MouseEvent.MOUSE_UP, this.scrubRemoveListenerHandler);
slider_mc.scrub.removeEventListener (MouseEvent.MOUSE_OUT, this.scrubRemoveListenerHandler);
slider_mc.scrub.stopDrag ();
}
slider_mc.bar.sliderFill_mc.width = 0;
this.addEventListener("enterFrame",onEnterFrame);
function onEnterFrame(e:Event) {
//By default scrub keeps on moving along with animation at the start of swf, when it is published
//sl.value = this.currentFrame;
slider_mc.scrub.x = Math.floor((barLength/animationMc.totalFrames) * animationMc.currentFrame);
//it causes fill to scale along with scrub
slider_mc.bar.sliderFill_mc.scaleX((440/animationMc.totalFrames) * animationMc.currentFrame);
/////CURRENT TIME///
var currentSeconds = Math.floor(animationMc.currentFrame/24);
var CurrentInput = currentSeconds;
var timeElapsed = (CurrentInput > 3600 ? Math.floor(CurrentInput/3600) + ':':'') //hours
+(CurrentInput%3600 < 600 ? '0':'')+Math.floor(CurrentInput%3600/60)+':' //minutes
+(CurrentInput%60 < 10 ? '0':'')+CurrentInput%60; //seconds
//trace(timeElapsed);
currentTime.text = timeElapsed;
}
pause_btn.addEventListener(MouseEvent.CLICK, pauseAnim);
function pauseAnim(event:MouseEvent):void {
//this.removeEventListener("enterFrame",onEnterFrame);
moviePlaying = false;
animationMc.stop();
}
play_btn.addEventListener(MouseEvent.CLICK, playAnim);
function playAnim(event:MouseEvent):void{
this.addEventListener("enterFrame",onEnterFrame);
moviePlaying = true;
animationMc.play();
}
slider_mc.buttonMode = true;
slider_mc.useHandCursor = true;
slider_mc.bar.addEventListener(MouseEvent.CLICK, snapTo);
function snapTo(event:MouseEvent) {
slider_mc.scrub.x = mouseX;
//slider_mc.bar.sliderFill_mc.scaleX =((440/animationMc.totalFrames) * animationMc.currentFrame);
var spanPercentage : Number = (slider_mc.scrub.x - slider_mc.bar.x) / barLength;
var framePosition : int = int (animationMc.totalFrames * spanPercentage);
slider_mc.bar.sliderFill_mc.scaleX =((440/animationMc.totalFrames) * animationMc.currentFrame);
//trace (framePosition);
if (moviePlaying==true) {
animationMc.gotoAndPlay(framePosition);
//moviePlaying = false;
} else if(moviePlaying==false) {
animationMc.gotoAndStop(framePosition);
}
}
///better because it gives to leading zeros///////////
//TOTAL TIME///
var totalSeconds = Math.floor(animationMc.totalFrames/24);
var input = totalSeconds;
var totalAnimTime = (input > 3600 ? Math.floor(input/3600) + ':':'') //hours
+ (input%3600 < 600 ? '0':'') + Math.floor(input%3600/60)+':' //minutes
+ (input%60 < 10 ? '0':'') + input%60; //seconds
//trace(totalAnimTime);
totaltime.text = "/"+totalAnimTime;
the problem is your spanPercentage calculation near line 106. If you click close to the end, it can be a number > 1, giving you a framePosition that is greater than the frames in your animationMc.
Because you can't move a play head past the number of frames, it returns to the first frame.
The quick fix is to make spanPercentage = 1 if spanPercentage > 1. Below are the relevant lines.
function snapTo (event:MouseEvent){
slider_mc.scrub.x = mouseX;
//trace("mouseX: "+root.mouseX , slider_mc.scrub.x);
trace("animationMc.currentFrame: "+ Math.floor((barLength/animationMc.totalFrames)* animationMc.currentFrame));
//slider_mc.bar.sliderFill_mc.scaleX=((440/animationMc.totalFrames)*animationMc.currentFrame);
var spanPercentage : Number = (slider_mc.scrub.x - slider_mc.bar.x) / barLength;
trace("spanPercentage: " +spanPercentage);
if(spanPercentage > 1){
spanPercentage = 1;
}
var framePosition : int = int (animationMc.totalFrames * spanPercentage);
slider_mc.bar.sliderFill_mc.scaleX=((440/animationMc.totalFrames)*animationMc.currentFrame)
//trace (framePosition);
if (moviePlaying==true){
animationMc.gotoAndPlay(framePosition);
//moviePlaying = false;
}
else if(moviePlaying==false) {
animationMc.gotoAndStop(framePosition);
}
}
Your slider_mc instance was stretched on the stage from 440 to 574.
Returning it to it's proper size of 440 pixels wide fixes your funky scrubber.
Check it out in action and download:
http://micromedia.vaniercollege.qc.ca/home/nortonb/2011-12/flash2/stackoverflow/scrubber.htm