How to access an object in AS3 - actionscript-3

I wrote this code
var enemies:Object = new Object();
// HP MP ATK DEF MATK MDEF AGI LUCK
enemies.Goblin = [40, 20, 6, 6, 3, 3, 4, 1];
which contains those stats for the goblin and I created a function that should take the stats from enemies.Goblin and put them in some variables but it won't work.
function createEnemy(enemyName:String):void {
e_hp = enemies.enemyName[0];
e_mp = enemies.enemyName[1];
e_atk = enemies.enemyName[2];
e_def = enemies.enemyName[3];
e_matk = enemies.enemyName[4];
e_mdef = enemies.enemyName[5];
e_agi = enemies.enemyName[6];
e_luck = enemies.enemyName[7];
}
This is the output error when the createEnemy function is executed: TypeError: Error #1010: A term is undefined and has no properties.

Object "enemies" does not have "enemyName" property.
Try this:
enemies[enemyName][0]
enemies[enemyName][1]
...

The answer had been given but what are you doing is a wrong way to do. Accessing properties by index is asking for trouble in a very near future.
It is better to do with classes but since you're using objects, I will try use objects too:
var goblin_stats:Object = { hp:40, mp:20, atk:6, def:6 }; // and so on
var elf_stats:Object = { hp:35, mp:30, atk:8, def:4 }; // and so on
...
// add as much characters as needed
Now I believe you just want to create a fresh goblin based on goblin stats. Just pass the stats to the createEnemy function:
createEnemy(goblin_stats);
function createEnemy(stats:Object):void {
e_hp = stats.hp;
e_mp = stats.mp;
// and so on
}
or better:
function createEnemy(stats:Object):void {
for (var property:String in stats) e_stats[property] = stats[property];
}

Store objects (everything) in arrays for easy referencing. Here are the key code:
var aEnemies:Array = new Array();
var mcEnemy:Object = new Object();
mcEnemy.iHP = 40; // set iHP property to 40
aEnemies.push(mcEnemy); // add enemy to array of enemies
trace("enemy 0's HP: " + aEnemies[0].iHP);

Related

Can we recall a set of variable inside the Sequence Array?

I'd like to ask about my program bcs it doesn't work correctly. I want to recall a set of variable in two different Sequence Array. Here is my code.
// Array of Arrays
var SequenceGo:Array =
\[
{dt:dt1, P:P1, s0:s01, s:s1},
{dt:dt2, P:P2, s0:s02, s:s2},
{dt:dt3, P:P3, s0:s03, s:s3},
{dt:dt4, P:P4, s0:s04, s:s4},
{dt:dt5, P:P5, s0:s05, s:s5},
{dt:dt6, P:P6, s0:s06, s:s6},
{dt:dt7, P:P7, s0:s07, s:s7},
{dt:dt8, P:P8, s0:s08, s:s8},
{dt:dt9, P:P9, s0:s09, s:s9},
{dt:dt10, P:P10, s0:s010, s:s10},
\];
var SequenceBack:Array =
\[
{dtback:dt10back, P:P10, s0:s010, sback:s10back},
{dtback:dt9back, P:P9, s0:s09, sback:s9back},
{dtback:dt8back, P:P8, s0:s08, sback:s8back},
{dtback:dt7back, P:P7, s0:s07, sback:s7back},
{dtback:dt6back, P:P6, s0:s06, sback:s6back},
{dtback:dt5back, P:P5, s0:s05, sback:s5back},
{dtback:dt4back, P:P4, s0:s04, sback:s4back},
{dtback:dt3back, P:P3, s0:s03, sback:s3back},
{dtback:dt2back, P:P2, s0:s02, sback:s2back},
{dtback:dt1back, P:P1, s0:s01, sback:s1back}
\];
function onNext(index:int = 0):void
{
if (index >= SequenceGo.length)
{
return;
}
var aDataGo:Object = SequenceGo[index];
var aDataBack:Object = SequenceBack[index];
//variables
F = s_teganganst.value;
m = s_masjenst.value/10000;
v = Math.sqrt(F/m);
tp = 5000/v;
f = s_frekuensist.value;
w = 2*Math.PI*f;
aDataGo.dt += t;
aDataGo.s = aDataGo.s0 - A * Math.sin(w * aDataGo.dt);
aDataGo.P.y = aDataGo.s;
if(P10.y < 607){
aDataBack.dtback += t;
aDataBack.sback = - A * Math.sin(w * aDataBack.dtBack);
aDataBack.P.y = aDataGo.s + aDataBack.sback;
}
setTimeout(onNext, tp, index + 1);
}
Actually, code
aDataBack.P.y = aDataGo.s + aDataBack.sback;
is not a fit code for the animation because aDataBack is ordered inversely from aDataGo (we have to stay this inverse order for the proper animation in my program). I want to recall the variables based on its number, so each variable will match with another variable. For example,
P1.y = s1 + s1back;
P2.y = s2 + s2back;
P3.y = s3 + s3back;
P4.y = s4 + s4back;
//and so on
I've tried the code above, but it also doesn't work. Any other expression for calling some couples of variables just like my code above? Thanks!
I want to recall the variables based on its number, so each variable will match with another variable
Ok, there are two options.
Option one, simple and straightforward: compose a method to find the correspondent back object on spot:
function findBack(P:Object):Object
{
for each (var aDataBack:Object in SequenceBack)
{
if (aDataBack.P == P)
{
return aDataBack;
}
}
}
So, that piece of code would be
var aDataGo:Object = SequenceGo[index];
var aDataBack:Object = findBack(aDataGo.P);
The possible problem here is the performance. It is fine on the scale of 10 or 100 objects, but as (I suppose) you devise a particle system, the object count easily scales to thousands, and the amount of loop-searching might become cumbersome.
So I advise to prepare a pre-indexed hash so that you won't need to search each single time.
var SequenceBack:Array =
[
// ...
];
// Dictionary is a storage of key:value data, just like Object,
// but Dictionary allows Object keys.
var HashBack:Dictionary = new Dictionary;
for each (var aDataBack:Object in SequenceBack)
{
HashBack[aDataBack.P] = aDataBack;
}
I encourage you to read more about Dictionary class.
And so that piece of code would be
var aDataGo:Object = SequenceGo[index];
var aDataBack:Object = HashBack[aDataGo.P];

Convert and parse json string to key value pairs using NewtonSoft

Trying to convert a json string to key value pairs using Newtonsoft but no luck so far.
Response from the API:
var response = #"{'result':{'0199 - B344EE33':
{
'6400_00260100':{'1':[{'val':336688}]},
'6400_00462500':{'1':[{'val':null}]},
'6800_00832A00':{'1':[{'low':3000,'high':3000,'val':3000}]},
'6800_008AA200':{'1':[{'low':0,'high':null,'val':0}]}
}}}";
Result I want is a new object of key value pairs:
{
"6400_00260100" : 336688,
"6400_00462500" : null,
"6800_00832A00" : 3000,
"6800_008AA200" : 0
}
In the response the result will always be the first and only prop. In the next level the code 0199 - B344EE33 can change but there will be only one prop in this level so we can always take the first one. Then in the last level we always need the val property.
What I have is the following but for getting the key value pairs in a clean way I got stuck:
var json = JObject.Parse(response);
var result = json["result"].First;
var path = result.Path;
UPDATE
var jObjectResult = new JObject();
var response = #"{'result':{'0199 - B344EE33':
{
'6800_10821E00':{'1':[{'val':'SMA Sunny Boy'}]},
'6800_00A21E00':{'1':[{'val':'3.0.0.2222'}]},
'6800_00823400':{'1':[{'low':3000,'high':3000,'val':3000}]},
'6800_08822B00':{'1':[{'val':'SMA'}]},
'6800_08822000':{'1':[{'val':'Sunny Boy 3.0'}]}
}}}";
var json = JObject.Parse(response);
var json_serial = json["result"].First.Children<JObject>().ToList()[0];
foreach(var token in json_serial)
{
var tokenKey = token.Key;
var tokenVal = token.Value.SelectToken("$.1[0].val");
jObjectResult.Add(tokenKey, tokenVal);
}
You could use SelectTokens with the recursive descent operator .. to find all the val properties, then walk up the chain using .Parent repeatedly to get the corresponding key. Create new JProperties from this information and put them into a new JObject to get your result. Here is a "one-liner":
var result = new JObject(
JObject.Parse(response)
.SelectTokens("$..val")
.Select(jt => new JProperty(
((JProperty)jt.Parent.Parent.Parent.Parent.Parent.Parent).Name,
jt
))
);
Fiddle: https://dotnetfiddle.net/TbZ7LS
At the end with some pointers form #Brian Rogers I came with the following solution:
// Arrange
var response = #"{'result':{'0199 - B344EE33':
{
'6800_10821E00':{'1':[{'val':'SMA Sunny Boy'}]},
'6800_00A21E00':{'1':[{'val':'3.0.0.2222'}]},
'6800_00823400':{'1':[{'low':3000,'high':3000,'val':3000}]},
'6800_08822B00':{'1':[{'val':'SMA'}]},
'6800_08822000':{'1':[{'val':'Sunny Boy 3.0'}]}
}}}";
// Act
var json = JObject.Parse(response);
var json_serial = (JProperty)json["result"].First();
var jObjectResult = new JObject(
json_serial.Value.Select(p =>
{
return new JProperty(
((JProperty)p).Name,
p.First.SelectToken("$.1[0].val")
);
}));

Trigger UiApp-builder callback within another routine

Serge's solution here seemed like the way to go about this, but I'm a bit afraid that my circumstances may be too different...
I have a button where users can add a new set of rows with controls to a FlexTable, in order to allow them to insert a new member into a record set. After designing and building the app to do this (and despite assurances to the contrary), a requirement was then added for the users to be able to edit the record sets at a later date.
I've finally managed to get the data retrieved and correctly displayed on the Ui - for single member record sets. As a final stage, I am now attempting to extend this to accommodate record sets having more than one member. Obviously this requires determining how many members there are in the record set, and then adding the new rows/control group to the FlexTable, before loading the member into each control group.
So within this routine, (depending on how many members there are) I may need to trigger the same callback, which the user normally does with a button. However, the difference with Serge's fine example, is that his code triggers the checkbox callback at the end of his routine once all the Ui components are in place. My situation needs to do this on the fly - and so far I'm getting 'Unexpected error', which suggests to me that the Ui is not able to update with the added FlexTable controls before my code attempts to assign values to them.
Does anyone have any insight into this problem? Is my only recourse to completely re-build a fixed Ui and dispense with the dynamic rowset model?
Code follows -
1. event for adding controls:
var app = UiApp.getActiveApplication();
var oFlexGrid = app.getElementById('ExpenseDetail');
var oRowCount = app.getElementById('rowCount');
var oScriptDBId = app.getElementById('scriptDBId');
var iRows = parseInt(e.parameter.rowCount);
var sVId = e.parameter.scriptDBId;
var vGridDefs = loadArrayById(sVId); //retrieve upload definition array from ScriptDB
var vControlNames = [];
if (isOdd(iRows)){
var sColour = 'AliceBlue';
} else {
var sColour = 'LavenderBlush';
};
oFlexGrid.insertRow(0);
oFlexGrid.insertRow(0);
oFlexGrid.insertRow(0);
oFlexGrid.insertRow(0);
oFlexGrid.setRowStyleAttributes(0,{'backgroundColor':sColour});
oFlexGrid.setRowStyleAttributes(1,{'backgroundColor':sColour});
oFlexGrid.setRowStyleAttributes(2,{'backgroundColor':sColour});
oFlexGrid.setRowStyleAttributes(3,{'backgroundColor':sColour});
var vExpenseDef = Get_NamedRangeValues_(CONST_SSKEY_APP,'UIAPP_GridExpense');
iRows = iRows+1;
vControlNames = CreateGrid_MixedSet_(iRows, vExpenseDef, oFlexGrid, app);
oRowCount.setText(iRows.toString()).setValue(iRows.toString());
//SOME INCONSEQUENTIAL CODE REMOVED HERE, LET ME KNOW IF YOU NEED IT
vGridDefs = vGridDefs.concat(vControlNames); // unify grid definition arrays
var sAryId = saveArray('expenseFieldDef', vGridDefs);
oScriptDBId.setText(sAryId).setValue(sAryId); //store array and save ScriptDB ID
if (e.parameter.source == 'btnExpenseAdd'){
hideDialog(); //IGNORE CHEKCBOX-DRIVEN CALLS
};
return app;
2. routine that calls the event
var app = UiApp.getActiveApplication();
var oPanelExpense = app.getElementById('mainPanelExpense');
var oPanelIncome = app.getElementById('mainPanelIncome');
var oPanelEdit = app.getElementById('mainPanelEdit');
var chkExpenseAdd= app.getElementById('chkExpenseAdd');
var bExpenseTrigger = e.parameter.chkExpenseAdd;
var sVoucherId = nnGenericFuncLib.cacheLoadObject(CACHE_EDIT_VOUCHERID);
var sVoucher = e.parameter.ListSearch1Vouchers;
var aryVoucherInfo = getVoucherEditDetail(sVoucherId);
//SAVE FOR RECORD MARKING CALLBACK
nnGenericFuncLib.cacheSaveObject(CACHE_EDIT_OLDRECORDS, JSON.stringify(aryVoucherInfo), CACHE_TIMEOUT);
sVoucher = nnGenericFuncLib.textPad(sVoucher, '0', 7);
var bExp = (sVoucher.substring(0,2) == '03')
var oRowCount = app.getElementById('rowCount');
var iRowCount = parseInt(e.parameter.rowCount);
var sControlName = '';
var vControlVal = '';
var iExpIdx = 0;
var sControlType = '';
var oControl = '';
var vSummaryTotal = 0;
for (var iVal in aryVoucherInfo){
sControlName = aryVoucherInfo[iVal][2];
vControlVal = aryVoucherInfo[iVal][3];
switch (sControlName){
case 'ESUM60':
vSummaryTotal = vControlVal;
break;
case 'EXUSRN':
continue; //DON'T OVERWRITE CURRENT USERNAME
break;
};
if (sControlName.indexOf('_')!=-1){ //TEST FOR CONTROL SET MEMBER
var aryControlSet = sControlName.split('_');
if (parseInt(aryControlSet[1])>iRowCount){//*** TRIGGER THE EVENT ***
Logger.log(bExpenseTrigger + ' - ' + !bExpenseTrigger);
chkExpenseAdd.setValue(!bExpenseTrigger, true);
iRowCount = iRowCount +1;
};
};
oControl = app.getElementById(sControlName);
var vCache = cacheSaveReturn(CACHE_UIEX_LISTS,sControlName);
if (typeof vCache == 'undefined'){
oControl.setValue(vControlVal);
oControl.setText(vControlVal);
//controlSetTextBox(oControl,vControlVal);
//controlSetDateBox(oControl,vControlVal);
} else {
if (!(nnGenericFuncLib.arrayIsReal(vCache))){
vCache = JSON.parse(vCache);
};
vCache = vCache.indexOf(vControlVal);
if (vCache != -1){
oControl.setSelectedIndex(vCache);
} else {
controlSetListBox(oControl,vControlVal);
};
};
};
//SOME CODE REMOVED HERE
hideDialog();
return app;
Mogsdad to the rescue!
The answer (see above) for those at the back of the class (with me) is to simply pass the app instance parameter (e) to the event function, calling it directly from the main routine, thus keeping the chronology in step for when it returns the app to complete the routine. No need for the checkbox in this situation.
This only took me all day, but thanks Mogsdad! :)
Snippet below taken from 1/2 way down code sample 2 in the OP:
if (sControlName.indexOf('_')!=-1){ //TEST FOR CONTROL SET MEMBER
var aryControlSet = sControlName.split('_');
if (parseInt(aryControlSet[1])>iRowCount){
eventAddExpense(e); //THAT'S ALL IT TAKES
iRowCount = iRowCount +1;
};
};

actionscript arrays merge

I posted my problem a few hours ago, but I think I figured out how to ask my question in a more comprehensible way.
This is my code:
// 1. Intro
var introPL1:Array = ["intro1","intro2","intro3","intro4"];
var introPL2:Array = ["intro5","intro6","intro7","intro8","intro9"];
var introPL3:Array = ["intro10","intro11"];
var introPL4:Array = ["intro12","intro13"];
var allIntro:Array = [introPL1,introPL2,introPL3,introPL4];
// 2. Clothes
var clothesPL1:Array = ["clothes1","clothes2","clothes3","clothes4","clothes5"];
var clothesPL2:Array = ["clothes6","clothes7","clothes8"];
var clothesPL3:Array = ["clothes9","clothes10"];
var clothesPL4:Array = ["clothes11","clothes12","clothes13"];
var allClothes:Array = [clothesPL1,clothesPL2,clothesPL3,clothesPL4];
// 3. Colored Numbers
var colNumPL1:Array = ["colNum1","colNum2","colNum3","colNum4","colNum5"];
var colNumPL2:Array = ["colNum6","colNum7","colNum8"];
var colNumPL3:Array = ["colNum9","colNum10"];
var colNumPL4:Array = ["colNum11","colNum12","colNum13"];
var allColNum:Array = [colNumPL1,colNumPL2,colNumPL3,colNumPL4];
var allStuff:Array;
allStuff = allIntro.concat(allClothes, allColNum);
trace(allStuff[4]);
When I trace allStuff[4] it displays "clothes1,clothes2,clothes3,clothes4,clothes5".
The thing is, I would like all the stuff to be in the allStuff array (without sub-arrays) and when I trace allStuff[4], I would like it to display "intro5" (the fifth item in the huge allStuff array).
the function you want to use then is concat
here's the example from adobe
var numbers:Array = new Array(1, 2, 3);
var letters:Array = new Array("a", "b", "c");
var numbersAndLetters:Array = numbers.concat(letters);
var lettersAndNumbers:Array = letters.concat(numbers);
trace(numbers); // 1,2,3
trace(letters); // a,b,c
trace(numbersAndLetters); // 1,2,3,a,b,c
trace(lettersAndNumbers); // a,b,c,1,2,3
it's pretty straight forward:
allStuff= allStuff.concat(introPL1,introPL2,introPL3,introPL4,clothesPL1,clothesPL2,clothesPL3,clothesPL4,colNumPL1,colNumPL2,colNumPL3,colNumPL4);
you could also do a
allStuff = []
for each(var $string:String in $arr){
allStuff.push($string)
}
for each array, or make it into a function
Okay, once you have declared your arrays like so, you need an additional operation to flatten your arrays allClothes and so on. Do like this:
function flatten(a:Array):Array {
// returns an array that contains all the elements
// of parameter as a single array
var b:Array=[];
for (var i:int=0;i<a.length;i++) {
if (a[i] is Array) b=b.concat(flatten(a[i]));
else b.push(a[i]);
}
return b;
}
What does it do: The function makes an empty array first, then checks the parameter member by member, if the i'th member is an Array, it calls itself with that member as a parameter, and adds the result to its temporary array, otherwise it's just pushing next member of a into the temporary array. So, to make your allIntro a flat array, you call allIntro=flatten(allIntro) after declaring it as you did. The same for other arrays.

AS3 load variables from a txt file which has && formatting

I'm trying to load a txt file of variables into my AS3 project. The problem I have though seems to be down to the fact that the txt file (which is pre formatted and cannot be changed) is formatted using double amphersands... e.g.
&name=mark&
&address=here&
&tel=12345&
I'm using the following code to load the txt file
myLoader.addEventListener(Event.COMPLETE, onLoaded, false, 0, true);
myLoader.dataFormat = URLLoaderDataFormat.VARIABLES;
urlRqSend = new URLRequest(addressToTxt.txt);
public function onLoaded(e:Event):void {
trace(myLoader.data);
}
Using URLLoaderDataFormat.VARIABLES generates the following error:
Error: Error #2101: The String passed to URLVariables.decode() must be a URL-encoded query string containing name/value pairs.
If I use URLLoaderDataFormat.TEXT I can load the data successfully but I'm not able (or don't know how to) access the variables.
Would anyone have any ideas or work arounds to this please.
Thanks,
Mark
I had that kind of problem some time ago.
I suggest you to load first as a text, remove those line breaks, the extra amphersands and parse manually:
var textVariables:String;
var objectVariables:Object = new Object();
...
myLoader.addEventListener(Event.COMPLETE, onLoaded, false, 0, true);
myLoader.dataFormat = URLLoaderDataFormat.TEXT;
urlRqSend = new URLRequest(addressToTxt.txt);
public function onLoaded(e:Event):void {
textVariables = myLoader.data;
textVariables = textVariables.split("\n").join("").split("\r").join(""); // removing line breaks
textVariables = textVariables.split("&&").join("&"); // removing extra amphersands
var params:Array = textVariables.split('&');
for(var i:int=0, index=-1; i < params.length; i++)
{
var keyValuePair:String = params[i];
if((index = keyValuePair.indexOf("=")) > 0)
{
var key:String = keyValuePair.substring(0,index);
var value:String = keyValuePair.substring(index+1);
objectVariables[key] = value;
trace("[", key ,"] = ", value);
}
}
}
I wrote that code directly here, I don't have any AS3 editor here, so, maybe you'll find errors.
If you have data in String and it has a structure just like you wrote, you can do a workaround:
dataInString = dataInString.split("\n").join("").split("\r").join(""); // removing EOL
dataInString = dataInString.slice(0,-1); // removing last "&"
dataInString = dataInString.slice(0,1); // removing first "&"
var array:Array = dataInString.split("&&");
var myVariables:Object = new Object();
for each(var item:String in array) {
var pair:Array = item.split("=");
myVariables[pair[0]] = pair[1];
}
That should make you an object with proper variables.