Jump to content

Photo

Working with Arrays


  • Please log in to reply
11 replies to this topic

#1 jsm116

jsm116

    Script kludger

  • Members
  • Location:The 301

Posted 13 September 2018 - 01:59 PM

Quick question about arrays. Is there a way to fill an array without looping after declaration? What I'm looking for is something like this:

int enemdata[2];

if (enem->ID == 58) enemdata = {25, 1}; //Aquamentus L1 (Left)
if (enem->ID == 93) enemdata = {30, 1}; //Aquamentus L2 (Right)
if (enem->ID == 37) enemdata = {3, 3}; //Armos

Alternatively, is there any progress towards multi-dimensional arrays for zscript?

 


Edited by jsm116, 13 September 2018 - 02:05 PM.


#2 Saffith

Saffith

    IPv7 user

  • ZC Developers

Posted 13 September 2018 - 02:34 PM

You have to set each element of an array individually, but you can probably find a better way to do it than a long chain of if/elses. Possibly something like this:
int enemyData[]={
    // Octorok (L1, Slow)
    60, 3,
    
    // Octorok (L2, Slow)
    55, 2,
    
    // Octorok (L1, Fast)
    102, 0,
    
    // Octorok (L2, Fast)
    74, 3
    
    // etc.
};

void setData(int enemyID, int data)
{
    int idx=(enemyID-20)*2; // Enemy IDs start at 20
    data[0]=enemyData[idx];
    data[1]=enemyData[idx+1];
}


#3 jsm116

jsm116

    Script kludger

  • Members
  • Location:The 301

Posted 13 September 2018 - 02:42 PM

Thanks, Saffith. It's been a while since I've worked with zscript so I'm having to recall some of the unique features of the language. I may end up dropping one of the two values entirely in which case npc->Attributes[11] will do what I need it to do from a functionality standpoint.



#4 jsm116

jsm116

    Script kludger

  • Members
  • Location:The 301

Posted 13 September 2018 - 03:05 PM

In the interest of keeping tables in code and not losing my mind trying to keep enemy attributes straight (this is for an experience system so it's going to be important for balance to keep relative values organized). And I can add more down the road if I decide to. What I ended up with is this:

int enemdata[]={
    //    ID        EXP        Hit
        58,        25,        1,        //Aquamentus L1 (Left)
        20,        6,        0,        //Octorock L1 Slow
        21,        8,        0        //Octorock L2 Slow
};

int getenemdata(int enemyID, int datatype)
{
	int idx = 0;
	
	
	for (int i = 0; i <= SizeOfArray(enemdata); i += 3){
		if (enemdata[i] == enemyID){
			idx=i;
			i = SizeOfArray(enemdata) + 1;
		}
	}
	int retidx = idx + datatype;
	return enemdata[retidx];
}

At the moment this is only called when an enemy is killed and in other specific circumstances, so it's not running this (rather inefficient) search every frame.

EDIT: Changed the incremental in the for loop to be += 3 so it's only searching every third term.

EDIT 2: Moved the array declaration outside of the function.


Edited by jsm116, 13 September 2018 - 03:21 PM.


#5 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 16 September 2018 - 07:27 PM

In the interest of keeping tables in code and not losing my mind trying to keep enemy attributes straight (this is for an experience system so it's going to be important for balance to keep relative values organized). And I can add more down the road if I decide to. What I ended up with is this:

int enemdata[]={
    //    ID        EXP        Hit
        58,        25,        1,        //Aquamentus L1 (Left)
        20,        6,        0,        //Octorock L1 Slow
        21,        8,        0        //Octorock L2 Slow
};

int getenemdata(int enemyID, int datatype)
{
	int idx = 0;
	
	
	for (int i = 0; i <= SizeOfArray(enemdata); i += 3){
		if (enemdata[i] == enemyID){
			idx=i;
			i = SizeOfArray(enemdata) + 1;
		}
	}
	int retidx = idx + datatype;
	return enemdata[retidx];
}
At the moment this is only called when an enemy is killed and in other specific circumstances, so it's not running this (rather inefficient) search every frame.

EDIT: Changed the incremental in the for loop to be += 3 so it's only searching every third term.
EDIT 2: Moved the array declaration outside of the function.


Oh, jsm116. I didn't think we'd ever see you back again. Welcome back! :D

How far are you into this quest, and how long do you think it'll take you to produce it?

I ask, because if it's a long project, you might want to try 2.55. There's a lot more useful stuff available now, that would simplify enemy experience and other matters. Heck, you can plug the values directly into the enemy editor, now.

Other than that, with your above function, how often are you calling getenemdata()?
If you want to plug enemy exp values into an array like that, you want to avoid longwinded iteration to extract them.

You're better off with something like this:
 
int enemydata[1536]; //512 * 3 types
//! In 2.55, you can use an expression, rather than a literal to set the size
//! so, it could be `int enemydata[MAXENEMIES*EDATA_LAST]`

const int EDATA_ID = 0;
const int EDATA_XP = 1;
const int EDATA_HIT = 2;
const int EDATA_LAST = 3;

//call this for each enemy that you want to store in your init script. 
void set_enemy_data(int id, int xp, int hit)
{
	enemydata[id] = id; //IDK if you'd ever need this.
	enemydata[id*3+EDATA_XP] = xp;
	enemydata[id*3+EDATA_HIT] = hit;
}

//call whenever an enemy dies, to get the desired values. 
int get_enemy_data(int id, int type)
{
	return enemydata[id*3+type];
}

//populate an array size[2] with the xp and hit values at one time
void grab_enemy_data(int id, int ptr)
{
	ptr[EDATA_XP-1] = get_enemy_data(id, EDATA_XP);
	ptr[EDATA_HIT-1] = get_enemy_data(id, EDATA_HIT);
}
I've been rewriting RPG.zh for 2.55 (Necromancer). Let me know if you have any interest in that.

Here's an example.

#6 jsm116

jsm116

    Script kludger

  • Members
  • Location:The 301

Posted 16 September 2018 - 08:12 PM

Hey Zoria! I got the itch again and came back.

 

What you're suggesting isn't far off from what I have. There's a check in the global that runs if there are any enemies on the screen. If so, a function is called that checks if any of them died that frame. If there are, then the array is scanned to pull out the exp value and increment the proper counter (which also checks for level up). I considered doing it through the enemy editor using one of the misc attributes but keeping track is such a pain in the neck. Plus this gives me the option of having certain other scripted mechanics be impacted by enemy type (ie, you can only warp if there's no boss in the room, certain enemies aren't hurt by certain attacks, etc.). I've optimized the array search as much as possible. It only scans every third value (the IDs) and stops searching when it finds what it's looking for. When the quest is all done I'm planning on using the ZQ enemy report to sort the list with the most common enemies at the top to lessen the search length (Octorocks near the top, one-off bosses at the bottom).

 

In terms of the overall quest, I've been getting the code in order first. I only have a couple screens for testing code and the passive subscreen done in zquest itself. The active subscreen is going to be fully scripted (the portion I'm working on presently). 2.50.2 has been acting weird (The actual game application flickers if I put it full screen, weird performance issues in ZQ when it's fullscreen). I used to have the link saved for the beta builds but don't anymore so if you can send that over I'd love to check out 2.55.



#7 Saffith

Saffith

    IPv7 user

  • ZC Developers

Posted 16 September 2018 - 11:03 PM

2.50.2 has been acting weird (The actual game application flickers if I put it full screen, weird performance issues in ZQ when it's fullscreen).

Are you using Windows 10? Try setting doublebuffer=1 in ag.cfg.
  • jsm116 likes this

#8 jsm116

jsm116

    Script kludger

  • Members
  • Location:The 301

Posted 17 September 2018 - 02:37 PM

That fixed it. Thanks, Saffith!



#9 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 18 September 2018 - 12:28 AM

I've optimized the array search as much as possible. It only scans every third value (the IDs) and stops searching when it finds what it's looking for.


That's something you should be able to avoid. You'd only need to do:

get_enemy_data(npc->ID, ENEDATA_XP); 
...using the example above. Iterating the entire array is hellishly slow, and checking the array size every iteration is sort of needless. You could do a negative iteration, if for some reason you are adamant about iterating, but really, that isn't required.

You're basically storing null values for npcs that don't matter, so, instead of constructing a table like that, you can do a basic function call series that sets up the values in an init script, for enemies that you have awarding XP. Then, when you need to check an award (you'll already have an npc pointer at that time), you just pass the npc ID into a function call to get your values.

It's much cleaner, and faster.


2.50.2 has been acting weird (The actual game application flickers if I put it full screen, weird performance issues in ZQ when it's fullscreen). I used to have the link saved for the beta builds but don't anymore so if you can send that over I'd love to check out 2.55.


I'll be making an open topic for 2.55 soon, but you can always hop onto the Discord server:
https://discord.gg/yyjrFWC

The builds are available there, including Nightly (or as often as we make them) builds.

Changelog is here:
https://github.com/A...5/changelog.txt

I considered doing it through the enemy editor using one of the misc attributes but keeping track is such a pain in the neck.


At some point, I hope to make it possible to edit the actual field labels in ZQuest, either via modules, or otherwise. That's a way off, though. In theory, I can convert all of the const char arrays into normal char, then sub out data with strings read-in from a module file, at least, for normal stuff.

Making it possible to edit the fields in the UI is quite a lot more work.

#10 jsm116

jsm116

    Script kludger

  • Members
  • Location:The 301

Posted 18 September 2018 - 08:57 AM

Okay I think I understand the difference. To make sure I follow, lets say I only used enemies with ID numbers 20 and 21 but not 22 or anything higher, the array would look something like this:

 

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,1,1,21,2,1,0,0,0, (and then zeroes all the way to the end)

 

The function set_enem_data would be called twice by the init script and would drop the values in the correct spot in the array and then the ID would serve as the demarcation point. I suppose you could even remove the IDs entirely from the array since like you said, you already know the ID number when you go to check the other data.

 

With regards to 2.55, I'm probably going to stick with 2.50, if for nothing else than for wider availability. My timeline for finishing is hopefully within the next two months (work and toddler permitting). As a feedback note, leaving the field name Misc 12 is fine for me, the issue is being able to see all the enemy exp values together for comparison. When I go to design the enemies for level 4 I want to compare the exp values to the enemies in level 3 as a whole. If there were a way to export either specific fields (ID, name, misc 12), OR export everything to a CSV that I could rearrange in excel, that would do what I'd want it to do--for my specific use. Others may want other things.



#11 Timelord

Timelord

    The Timelord

  • Banned
  • Location:Prydon Academy

Posted 21 September 2018 - 05:18 AM

Okay I think I understand the difference. To make sure I follow, lets say I only used enemies with ID numbers 20 and 21 but not 22 or anything higher, the array would look something like this:

 

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,1,1,21,2,1,0,0,0, (and then zeroes all the way to the end)

 

The function set_enem_data would be called twice by the init script and would drop the values in the correct spot in the array and then the ID would serve as the demarcation point. I suppose you could even remove the IDs entirely from the array since like you said, you already know the ID number when you go to check the other data.

 

With regards to 2.55, I'm probably going to stick with 2.50, if for nothing else than for wider availability. My timeline for finishing is hopefully within the next two months (work and toddler permitting). As a feedback note, leaving the field name Misc 12 is fine for me, the issue is being able to see all the enemy exp values together for comparison. When I go to design the enemies for level 4 I want to compare the exp values to the enemies in level 3 as a whole. If there were a way to export either specific fields (ID, name, misc 12), OR export everything to a CSV that I could rearrange in excel, that would do what I'd want it to do--for my specific use. Others may want other things.

 

That's how it works, aye.

 

Saffith provided a similar function earlier that sets array elements without the enemy ID. I don't see any particularly good reason to store the enemy ID in the table: I was only doing that because you might later have some use for it (cross-indexing). 

 

You should probably look at using the 2.53.0 betas though. Those are pretty much done, and the quest format is compatible with all 2.50.x flavours; with the benefit of a better ZQuest experience. 

 

...and...two months?! I canna manage to pop out anything passable in such a short time. :P

 

Then again, I tend to do extremely unorthodox things. 



#12 jsm116

jsm116

    Script kludger

  • Members
  • Location:The 301

Posted 21 September 2018 - 06:43 PM

I'm -hoping- for 2 months, realistically, it'll be more like 4-5. (I have no effing clue how people do full 8 dungeon quests in like a week.)

 

I read through the spec sheet for 2.53 and I'll give it a whirl over the weekend. Thanks!




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users