public class NCell
{
private var _player: NPlayer;
public function set player(value: NPlayer):void
{
this._player = value;
}
public function get player():NPlayer {return this._player}
}
public class NPlayer
{
private var _cell: NCell;
public function set cell(value: NCell):void
{
if (this._cell != null)
{
this._cell.player = null;
}
this._cell = value;
if (this._cell != null)
{
this._cell.player = this;
this.position = this.cell.position;
}
}
}
So, I have a field of NCells (size 20x15) and several players on it. To move player I write
player.cell = field[4][4];
After it player knows his cell (and position) and cell has a link to player. Thus I have everything to calculate available moves. Sometimes there are situations when player has "cell", but "player" in the cell == null. It's bad. It becomes so because when I calculation moves I store players position then move players and then continue searching and when game points become 0 I restore players positions and continue searching. E.g. I have c1 == field[3][4], c2 == field[4][4], c3 == field[5][4], p1.cell == c1, p2.cell == c2. p2 moves to c3 and then p1 moves to c1. Then I restore position:
//c2.player == p1
p2.cell = c2;//now c2.player == c2 but p1.cell == c2
p1.cell = c1;//and here c2.player == null
and it doesn't depends of restoring sequence. How to avoid link wiping?
Well, I am not so sure the way you store your data is the best. If you really want this kind of linkage, you may want to consider making a cell capable of holding multiple players. This would resolve your current problem and would be more flexible, later on.
Basically, you problem can be solved like this:
public class NPlayer
{
private var _cell: NCell;
public function set cell(value: NCell):void
{
if (this._cell != null && this._cell.player == this) //only the owner may remove a link!
{
this._cell.player = null;
}
this._cell = value;
if (this._cell != null)
{
this._cell.player = this;
//this.position = this.cell.position; <-- no need for that, see below
}
}
}
Unless you access NPlayer::position a gazillion times per frame, there is no point in storing it. This only introduces a potential source of errors. The property should be calculated, by simply returning this.cell.position or a meaningful default value in case the player has no cell (or raising an exception, if that is not supposed to happen).
greetz
back2dos
Related
Hey guys so I am struggling the best way to approach this situation.
So I have 5 frames in my ovenScreen.mcChar movie clip. Each is a character that the player can unlock. If the player has enough coins then they can go to the prize screen to get a random unlockable character.
Here is how it is setup so far:
private function getPrizeHandler(e:MouseEvent):void
{
//Check if prize is locked or unlocked then unlock item/ loop through random number frames
frameLoop = randomNumber(1, 5);
ovenScreen.mcChar.gotoAndStop(frameLoop);
if (frameLoop == 1 && !sharedObjectCharacters.data.sharedHotDog)
{
sharedHotDog = true;
sharedObjectCharacters.data.sharedHotDog = sharedHotDog;
sharedObjectCharacters.flush ();
}else
if (frameLoop == 2 && !sharedObjectCharacters.data.sharedTaco)
{
sharedTaco = true;
sharedObjectCharacters.data.sharedTaco = sharedTaco;
sharedObjectCharacters.flush ();
}else
if (frameLoop == 3 && !sharedObjectCharacters.data.sharedDonut)
{
sharedDonut = true;
sharedObjectCharacters.data.sharedDonut = sharedDonut;
sharedObjectCharacters.flush ();
}else
if (frameLoop == 4 && !sharedObjectCharacters.data.sharedCoffee)
{
sharedCoffee = true;
sharedObjectCharacters.data.sharedCoffee = sharedCoffee;
sharedObjectCharacters.flush ();
}else
if (frameLoop == 5 && !sharedObjectCharacters.data.sharedPancakes)
{
sharedPancakes = true;
sharedObjectCharacters.data.sharedPancakes = sharedPancakes;
sharedObjectCharacters.flush ();
}
////////////////////////////////////////
ovenScreen.gotoAndPlay(2); //play animations
TweenLite.delayedCall(3.5, prizeConfettie);
ovenScreen.removeEventListener(MouseEvent.CLICK, getPrizeHandler);
}
As you can see I have the var frameLoop which is a random number from 1 - 5. So the character unlocked will be random and show the random unlocked character. I use the if statements to check if the random number lands on that certain frame and its not the case that it is unlocked then unlock it and save the data.
Now this all works fine but How could I go about fixing it to where if the Item is already unlocked to sort through a different frame number. So if the frameLoop lands on 2 and that character is already unlocked then repeat the random frame number until it lands on a locked character. I was thinking of setting up an array of numbers maybe that approach might be a logical one but not sure how to go about doing so.
Any help would be appreciated thanks.
Additional Info on the shared object Booleans:
private function allSharedObjectBooleans():void
{
sharedObjectCharacters = SharedObject.getLocal("Characters");
sharedHotDog = sharedObjectCharacters.data.sharedHotDog != null ? sharedObjectCharacters.data.sharedHotDog : false;
sharedTaco = sharedObjectCharacters.data.sharedTaco != null ? sharedObjectCharacters.data.sharedTaco : false;
sharedDonut = sharedObjectCharacters.data.sharedDonut != null ? sharedObjectCharacters.data.sharedDonut : false;
sharedCoffee = sharedObjectCharacters.data.sharedCoffee != null ? sharedObjectCharacters.data.sharedCoffee : false;
sharedPancakes = sharedObjectCharacters.data.sharedPancakes != null ? sharedObjectCharacters.data.sharedPancakes : false;
}
and I create them like so:
//shared Booleans
private var sharedHotDog:Boolean;
private var sharedTaco:Boolean;
private var sharedDonut:Boolean;
private var sharedCoffee:Boolean;
private var sharedPancakes:Boolean;
If the character is already unlocked, you increment the variable. If it's overboard (greater than the number of unlockable entities), set it to 1. If it looped through all characters and they all are already unlocked, do something else. And you already have an array of sorts for this, it's just located in sharedObjectCharacters.data and its indexes are string, not int. But this can be remedied by providing a map from int to string, and using this[string] syntax to check for properties. An example:
const strUnlockables:Array=["Noone","sharedHotDog","sharedTaco","sharedDonut","sharedCoffee","sharedPancakes"];
const unlockables:int=5;
private function getPrizeHandler(e:MouseEvent):void {
var f:int=randomNumber(1,5); //frameLoop
for (var i:int=0;i<unlockables;i++) { // check all unlockables, starting with f'th
if (sharedObjectCharacters.data[strUnlockables[f]]===false) {
// this "f" is the one to unlock
sharedObjectCharacters.data[strUnlockables[f]]=true;
sharedObjectCharacters.flush();
ovenScreen.mcChar.gotoAndStop(f);
ovenScreen.gotoAndPlay(2); //play animations
TweenLite.delayedCall(3.5, prizeConfettie);
break;
}
f++;
if (f>unlockables) f=1; // loop around
}
// if we're here, we either did a break, or have all characters unlocked
ovenScreen.removeEventListener(MouseEvent.CLICK, getPrizeHandler); // clean up
}
Hey Everyone so I am currently working on a game and the objective is for the user to click on objects on the stage. But the user has to click on the largest objects first before the user clicks on the smaller objects. I wanted to make it that if the user clicks on a smaller object first and not the larger one then the game will be over. I thought I could go about setting this up with booleans for each object on the stage but my if statements arent cutting it here is how I have it set up:
Here are my objects and booleans I use:
//Add box references
public var box_1:MovieClip;
public var box_2:MovieClip;
public var box_3:MovieClip;
public var box_4:MovieClip;
//Booleans
private var b1:Boolean;
private var b2:Boolean;
private var b3:Boolean;
private var b4:Boolean;
now I set all the Booleans to false and if the user clicks on one of the objects I set the Boolean to true.
Here are my if statements tied to my main ENTER_FRAME Listener:
private function level_1():void
{
if (b1)
{
mainScreen.box_1.gotoAndPlay("B1GONE");
b1 = false;
}else
if (b2)
{
mainScreen.box_2.gotoAndPlay("B2GONE");
b2 = false;
}else
if (b3)
{
mainScreen.box_3.gotoAndPlay("B3GONE");
b3 = false;
}else
if (b2 && !b1)
{
endGameCondition();
}
}
On this statement:
if (b2 && !b1)
{
endGameCondition();
}
I was trying to state that if box_2 is true meaning that its clicked on and box_1 hasnt been clicked on yet which is the larger object then the game is now over due to the user not clicking on the largest object first. I have it setup to where box_1 is the largest object and the others are the next size down.
Can anyone see why this isnt working correctly or if there is a better method in doing this?
**** UPDATE HOW MY MAIN CLASS IS SETUP NOW **************
Where I add all my Movie clips and variables:
public class boxTapEngine extends MovieClip
{
//Screens
private var mainScreen:mcMainScreen;
//Add box references
public var box_1:MovieClip;
public var box_2:MovieClip;
public var box_3:MovieClip;
public var box_4:MovieClip;
private var aBoxesArray:Array;
in my constructor function:
aBoxesArray = [box_1, box_2, box_3, box_4];
//AddMainScreen
mainScreen = new mcMainScreen();
mainScreen.x = (stage.stageWidth / 2);
mainScreen.y = (stage.stageHeight / 2);
stage.addChild(mainScreen);
//Initiate numbers
nLevel = 1;
for each(var box:MovieClip in aBoxesArray)
{
box.addEventListener(MouseEvent.CLICK, boxClick);
}
finally on the boxClick function:
private function boxClick(e:MouseEvent):void
{
var box:MovieClip = e.currentTarget as MovieClip; //get a reference to one that was just clicked
box.mouseEnabled = false; //we'll use this as a flag to know if it's been clicked yet
box.gotoAndPlay("BGONE");
//check to see if previous boxes have been clicked.
//this will iterate through all the boxes in order
for (var i:int = 0; i < aBoxesArray.length; i++)
{
if(aBoxesArray[i] == box) return; //if we've reached the currently clicked box, all is good, no need to keep checking so let's exit this loop and function
if (!aBoxesArray[i].mouseEnabled)
{ //one of the previous boxes hasn't been clicked yet
endGameCondition();
}
}
}
Probably isn't working because your final else if statement won't ever be reached (because you're handling b2 == true earlier on which will then bypass all other else statements). Plus your setting b2 to false when you handle it earlier, so it will always be false by the time it gets your final statement.
You need to move that final else if before you check for the other things. See code comments:
//Do this first, and not as a else if
if (b2 && !b1){
endGameCondition();
return; //no need to check checking things if it's game over
}
//now you can do the rest
if (b1)
{
mainScreen.box_1.gotoAndPlay("B1GONE");
b1 = false;
}else
if (b2)
{
mainScreen.box_2.gotoAndPlay("B2GONE");
b2 = false;
}else
if (b3)
{
mainScreen.box_3.gotoAndPlay("B3GONE");
b3 = false;
}
As an aside, you don't need the enter frame handler, you can just check everything on click. Something like this would work:
var boxes:Array = [box_1,box_2,box_3,box_4]; //make sure this is in order of what needs to be clicked first
//if you wanted to actually sort them dynamically based off total size (regardless of what order you've stuffed them in the array), you could add in something like this, which will order them from biggest to smallest:
boxes.sort(function(a,b){
//compare which item has a greater total area (width * height)
if(a.width * a.height > b.width * b.height) return -1; //A is bigger, so put a before b in the array
if(a.width * a.height < b.width * b.height) return 1; //put b before a in the array
return 0; //return 0 if they are the same
});
for each(var box:MovieClip in boxes){
box.addEventListener(MouseEvent.CLICK, boxClick,false,0,true);
}
function boxClick(e:Event):void {
var box:MovieClip = e.currentTarget as MovieClip; //get a reference to one that was just clicked
box.mouseEnabled = false; //we'll use this as a flag to know if it's been clicked yet
box.gotoAndPlay("BGONE");
//or you could just do this to get rid of the box:
if(box.parent) box.parent.removeChild(box);
//check to see if previous boxes have been clicked.
//this will iterate through all the boxes in order
for(var i:int=0;i<boxes.length;i++){
if(boxes[i] == box) return; //if we've reached the currently clicked box, all is good, no need to keep checking so let's exit this loop and function
if(!boxes[i].mouseEnabled){ //one of the previous boxes hasn't been clicked yet
endGameCondition();
}
}
}
//Store your clips in an array
var myClips:Array = [mc1,mc2,mc3,mc4];
//get the clip sizes and store it to an array.
var sizes:Array = new Array();
for (var i:uint = 0; i < myClips.length; i++) {
sizes.push(myClips[i].width);
}
//apply Numeric array sort.
sizes.sort(Array.NUMERIC);
function onClickAction(e:MouseEvent):void {
//Check wheather the array is empty or not.
if (sizes.length != 0) {
//Check wheather the clicked object bigger or not.
if (e.target.width == sizes[sizes.length - 1]) {
trace("Bigger");
e.target.alpha = .5;
sizes.splice(sizes.length-1,1);
} else {
trace("Smaller");
}
}
}
Okay so on a Flash project stage there is a dynamic text box which keeps score of whatever activity is happening on the stage...mouse clicks, collisions...whatever...
I want it so that each time the dynamic text box (score box) hits 2000 (e.g. 2000, 4000, 6000)...each time we hit a 2000 multiple a hidden movie clip becomes visible...otherwise the movie clip is hidden.
Thanks all for considering a reply...this is what I have...it works fine but I don't know how to make it so that each time it adds on 2000 points the movie clip 'saleTag' will show.
if (cashBox.text == "2000")
{
saleTag.visible = true;
}
else
{
saleTag.visible = false;
}
}
Read some books. E.g. Colin Moock. If you have troubles at this point, you need to get more basics.
Don't store data in textfield, it is usually used to display data:) Use vars instead:
private var counter:int=0;
Make a function which will increase the counter, check the conditions and manage the visibility of your movieclip.
private function increaseCounter():void
{
counter++;
if (Math.floor(counter/2000)==counter/2000)
{
saleTag.visible = true;
}
else
{
saleTag.visible = false;
}
}
Call it each time you need to increase a counter.
Fine tuning. Remove the "Else" branch and use timer\delayed call to set saleTag.visible = false;.
You can't count with cashBox.text which is a String. You should first count your events with an integer variable (n), then test this variable to know if it's a multiple of 2000,
and convert it to String in your textField cashBox.
saleTag.visible = false;
var n:int = 0;
const LIM:int = 2000;
function clickHandler(e:MouseEvent):void {
n++;
cashBox.text = String(n);
saleTag.visible = (n%LIM == 0) ? true : false;
}
stage.addEventListener(MouseEvent.MOUSE_DOWN, clickHandler);
I have a couple very large datagrids with custom cell renderers. The issue I am facing is that I now have > 80 of these, 1 for each column per data grid.
I'm curious if there is a way I can get these reduced down into 1 global cell renderer that I could pass variables into to define what is allowed by the cell renderer for that column.
ie:
...
col1 = new DataGridColumn("PurchaseStartDate");
col1.headerText = "Purchase Date (YYYY-MM)";
dg.addColumn(col1);
col1.width = 110;
col1.editable = false;
col1.sortable = false;
col1.cellRenderer = Alternating_Row_Colours_editable36;
Alternating_Row_Colours_editable36._dg = dg;
Alternating_Row_Colours_editable36.__enabled = true;
Alternating_Row_Colours_editable36._myCol = 12;
Alternating_Row_Colours_editable36._isNum = 3;
Alternating_Row_Colours_editable36._stage = this;
Alternating_Row_Colours_editable36._maxChars = 9;
within the cell renderer I'm using all of those variables to customize what is allowed.
ie:
...
public function Alternating_Row_Colours_editable36()
{
super();
if(_isNum == 0){
restrict = "a-z A-Z #_,.0-9//-";
maxChars = 64;
}else if (_isNum == 1){
restrict = ".0-9";
maxChars = 9;
}else if (_isNum == 2){
restrict = "0-9";
maxChars = 2;
}else if (_isNum == 3){
restrict = "0-9 \\-";
maxChars = 9;
}else if (_isNum == 4){
restrict = "0-9. %";
maxChars = 9;
}
if(_maxChars != -1){
maxChars = _maxChars;
}
The issue if you look at the above, I just noted that I had an error. I used "//-" to escape the hyphen instead of "\-". Now I've got 80+ changes to make and every time I try to add something new, for a callback, restricts, max chars, making it editable, scrubbing the input, changing it from dynamic to input and back again...
I would love to know if there is a way, to create an instance of the class and use that cell renderer. Or to be able to pass variables that affect only that column and not all columns.
I'm not the best, but I was under the impression that it might just be a set/get function I need to use, or change the variables to protected, private or something to get this desired result.
Anyone have any suggestions on how to bring these 80+ cell renderers under control. As I am not looking forward to needing to make the changes to them for sorting down the road...
jc
I know this is a very late reply and you've more than likely moved on by now!
You can do it by using the information in 'listData' property of the CellRenderer class:
// Create a private class level variable called _dataField...
private var _dataField:String;
// Assign the dataField...
public function set listData(value:ListData):void {
_listData = value;
this._dataField = DataGridColumn(DataGrid(this._listData.owner).columns[this._listData.column]).dataField;
// set the data again now...
this.data = _data;
invalidate(InvalidationType.DATA);
invalidate(InvalidationType.STATE);
}
// Use the dataField when setting value from DataProvider...
public function set data(value:Object):void {
_data = value;
if (this._dataField != "")
{
this.text = value[this._dataField];
}
}
Hope that satisfies any curiosity. Shame they don't just pass that property to the CellRenderer in the first place!
Cheers
I have 2 object. In different depths of MovieClip. I would like to know if object A parent is same with object B parent. I wanted to dynamically add '.parent' to object A/B till it both reaches the same level (having the same parent object). How can I do that?
My idea was to have something like
objectA = objectA + ".parent"
and make it loop till it reaches the target. But this is not the correct way to add in more layers of '.parent'. Anyone know how it should be coded?
You can use contains method
public function contains(child:DisplayObject):Boolean
Determines whether the specified display object is a child of the DisplayObjectContainer instance or the instance itself. The search includes the entire display list including this DisplayObjectContainer instance, grandchildren, great-grandchildren, and so on.
function haveCommonParent(a:DisplayObject, b:DisplayObject):Boolean
{
for(var p:DisplayObjectContainer = a.parent; p != null; p = p.parent)
{
if(p.contains(b))
return true;
}
return false;
}
Might be slow for huge display lists.
Update: get the common parent, if any. This will return a Stage object if both are on stage.
function getCommonParent(a:DisplayObject, b:DisplayObject):DisplayObjectContainer
{
for(var p:DisplayObjectContainer = a.parent; p != null; p = p.parent)
{
if(p.contains(b))
return p;
}
return null;
}
var mc:MovieClip;
while (mc.parent != null && mc.parent != targetParent)
{
mc = mc.parent;
}