Jump to content

Photo

Backwards NPC pointer referencing?


  • Please log in to reply
12 replies to this topic

#1 Schwa

Schwa

    Enjoy the Moment more. This strengthens Imagination.

  • Members
  • Real Name:Hunter S.
  • Location:Redmond Subspace (I had a Potion)

Posted 03 January 2011 - 01:05 AM

This might be kind of a stupid question, but... You guys know "Screen->LoadNPC(int Value)", right? It returns an NPC pointer to whatever Value you put in. Well, I was wondering how to do that backwards. As in, basically, converting an Integer to an NPC instance on the screen. So far I can't find any sort of command or function to do that.

My guess-- and it is only a guess-- is that there's issues with such an operation if NPC pointers are all re-calculated after every Waitframe(). So, maybe it would help if I explain what I'm trying to do: I'm getting an enemy to manually spawn an EWeapon from the code itself when it attacks... the EWeapon in question being ID# "31", one of the Dummy Weapons, which I intend to specify the behavior for in the Main Global Script. As soon as the EWeapon is spawned, it's supposed to write the Pointer of the enemy that spawned it as its Misc[0] value, because this weapon's movements can change on the fly as the attacker's X,Y position changes (as the enemy walks around and stuff). But I can't figure out what I'm supposed to store, because you're not allowed to typecast an Integer into an NPC, obviously-- not without some in-between steps or whatever.

If someone out there is sufficiently knowledgeable to help me overcome this instrument of melancholy amidst my endeavors of creative pursuit, they will be greatly loved and GREATLY credited. icon_smile.gif Thanks, love you.
--Schwa

#2 LinktheMaster

LinktheMaster

    Hey Listen, Kid

  • Members
  • Real Name:Matt
  • Location:United States

Posted 05 January 2011 - 06:34 PM

I can't think of any straightforward method of doing it, unfortunately. Each frame all of the weapon and npc numbers would be changed.

However, there are ways to work around it, and you're sort of onto it. You're trying to save the enemy via a pointer. As of the moment, there's no variable to specify each enemy after each frame, but that doesn't mean you can't make a variable. icon_wink.gif I would have a script (be it in the global script or in a ffc script) that will scan each enemy after they are created, then will assign each of your custom enemies a unique value in Misc[0]. This will only have to be for one frame. Do something like this:

CODE

// This basically ensures that the script will wait until enemies are created
while(Screen->NumNPCs() == 0)
     waitframe();

for(int i = 0; i < Screen->NumNPCs(); i++)
     // Check each enemy for your custom enemy and change the Misc[0]


Then, when each enemy spawns your custom weapon, the value of Misc[0] for that enemy is placed into the Misc[0] of the weapon. Then in the global loop, when you need to find the enemy associated with each weapon, you scan each enemy until you find one that matches the Misc[0] value.

That should do it for you. icon_smile.gif

Edited by LinktheMaster, 05 January 2011 - 06:35 PM.


#3 MarioBrosCom

MarioBrosCom

    Adept

  • Members
  • Real Name:Stylianos
  • Location:Canada

Posted 06 January 2011 - 02:12 AM

You might be able to get a little more efficiency out of LtM's solution by keeping an array of your custom enemy npcs and using that array index as your Misc value (for both npc and eweapon).

When you create a new child eweapon, you'll set child->Misc[0] = parent->Misc[0], and when you need to update the eweapon's position, you'll get the parent npc at parents[child->Misc[0]].

#4 jman2050

jman2050

    Illustrious

  • Members

Posted 06 January 2011 - 03:25 PM

You can probably just use a global array/variable that stores the relevant shared information (in this case, the X and Y position of the enemy) and have the enemy update that array and the weapon use the values inside the array. Or something like that.

Actually, I thought enemies and weapons were given unique IDs upon creation that don't change and could let you pull the actual pointer during every frame. Actually... *looks*... why isn't there a function for pulling entity pointers via ID? That should be super easy to do :/

EDIT - Actually I'm dumb, you could actually do it via scripting.

CODE

weapon whatever = CreateEWpn(whatever);
...
whatever->Misc[0] = this->ID;
<other init stuff>


And then in the weapon code

CODE

bool found=false;
npc temp;
for(int i = 0; i < Screen->NumNPCs(); i++)
{
    temp = screen->LoadNPC(i);
    if(temp->ID == this->Misc[0])
    {
        found=true;
        ...<do whatever here>
    }
}


See if that or a possible variant works for you.

Edited by jman2050, 06 January 2011 - 03:43 PM.


#5 Schwa

Schwa

    Enjoy the Moment more. This strengthens Imagination.

  • Members
  • Real Name:Hunter S.
  • Location:Redmond Subspace (I had a Potion)

Posted 06 January 2011 - 04:17 PM

(to Jman) Oh hi! Good idea, but doesn't NPC->ID refer to the "species" of enemy it is, rather than its screen pointer? Six Peahats on the screen would all have an ID variable of 32.

(to everyone) I had been giving this some thought, and yes, associating the EWeapon's Misc[] with an NPC's Misc[] appeared to be the best solution... but I'm afraid of one thing.

Running a loop that checks every NPC multiple times per frame will slow down my game, won't it? Remember, this isn't the only extensive script I have going on, and even if I get fancy stuff to work, it means nothing if there's a critical slowdown problem; sucks out all the redeeming value and enjoyment.

I've been afraid to write a couple different scripts, actually, for this very reason. icon_unsettled.gif I'll try these methods, since nothing else seems plausible as a solution, but I'm still concerned...

#6 jman2050

jman2050

    Illustrious

  • Members

Posted 06 January 2011 - 04:41 PM

QUOTE(Schwa @ Jan 6 2011, 04:17 PM) View Post

(to Jman) Oh hi! Good idea, but doesn't NPC->ID refer to the "species" of enemy it is, rather than its screen pointer? Six Peahats on the screen would all have an ID variable of 32.


You're right actually, my bad. There should be a way to access an entities unique ID though. I guess I can work on that at some point

QUOTE
(to everyone) I had been giving this some thought, and yes, associating the EWeapon's Misc[] with an NPC's Misc[] appeared to be the best solution... but I'm afraid of one thing.

Running a loop that checks every NPC multiple times per frame will slow down my game, won't it? Remember, this isn't the only extensive script I have going on, and even if I get fancy stuff to work, it means nothing if there's a critical slowdown problem; sucks out all the redeeming value and enjoyment.

I've been afraid to write a couple different scripts, actually, for this very reason. icon_unsettled.gif I'll try these methods, since nothing else seems plausible as a solution, but I'm still concerned...


Just write the script and see if things slow down. It would typically take a lot of continuous script processing to really start slowing things down, especially after the optimizations that were made to the scripting engine. So the way I see it, don't worry about speed issues until they actually become issues.


#7 Saffith

Saffith

    IPv7 user

  • Members

Posted 06 January 2011 - 09:06 PM

Well, here's my attempt:

CODE
// Index of npc->Misc[] to use for storing IDs
const int IDX_ENEMY_ID=0;

// Enemy cache; associates generated IDs with current enemy indices
int npcCache[256];


// Get a unique ID number for the given enemy. A new ID will be
// assigned if it doesn't have one already.
// Note: using this on a very large number of enemies without
// changing screens (more than npcCache can hold) will cause problems.
int GetID(npc enemy)
{
    // If the enemy already has an ID, return it
    if(enemy->Misc[IDX_ENEMY_ID]>0)
        return enemy->Misc[IDX_ENEMY_ID];
    
    // No ID; generate a new one
    int newID=0;
    int index=0;
    npc temp;
    
    // Find the highest ID in use and add one
    for(int i=Screen->NumNPCs(); i>0; i--)
    {
        temp=Screen->LoadNPC(i);
        
        if(temp==enemy) // Found the input enemy; remember the index so it can be cached
            index=i;
        else
            newID=Max(newID, temp->Misc[IDX_ENEMY_ID]+1);
    }
    
    // Assign the ID, cache the index, and return
    enemy->Misc[IDX_ENEMY_ID]=newID;
    npcCache[newID-1]=index;
    return newID;
}


// Get the enemy with the ID returned by GetID().
// If no such enemy exists, the returned enemy will be invalid.
npc LoadNPCByID(int id)
{
    // Load from cache
    npc ret=Screen->LoadNPC(npcCache[id-1]);
    if(ret->isValid() && ret->Misc[IDX_ENEMY_ID]==id)
        return ret;
    
    // Cached enemy was wrong; refresh cache
    ret=Screen->LoadNPC(0); // Invalid
    npc temp;
    
    for(int i=Screen->NumNPCs(); i>0; i--)
    {
        temp=Screen->LoadNPC(i);
        if(temp->Misc[IDX_ENEMY_ID]==0)
            continue;
        
        npcCache[temp->Misc[IDX_ENEMY_ID]-1]=i;
        
        // Found the requested enemy; set return value
        if(temp->Misc[IDX_ENEMY_ID]==id)
            ret=temp;
    }
    
    return ret;
}


Use GetID() to get an ID number for an enemy, then use LoadNPCByID() to reload the enemy. The enemy returned by the latter will be invalid if no enemy has the given ID.
Just don't use it on more than 256 enemies on the same screen. It could be fixed to account for that, but I figure it's not worth it.


Oh, in case it's not clear, those should be global, not in a script.

Edited by Saffith, 06 January 2011 - 09:12 PM.


#8 Gleeok

Gleeok

    It's dangerous to dough alone, bake this.

  • Members
  • Real Name:Pillsbury
  • Location:Magical Land of Dough

Posted 06 January 2011 - 09:55 PM

Why not just have a single var for id numbers? You should never run out of them.

CODE

// Index of npc->Misc[] to use for storing IDs
const int IDX_ENEMY_ID=0;

int unique_id;

int GetID(npc enemy)
{
    if(enemy->Misc[IDX_ENEMY_ID]>0)
        return enemy->Misc[IDX_ENEMY_ID];
    
    // No ID; generate a new one
    unique_id++;
    enemy->Misc[IDX_ENEMY_ID] = unique_id;

    return unique_id;
}

npc LoadNPCByID(int id)
{
    npc temp;
    
    for(int i=Screen->NumNPCs(); i>0; i--)
    {
        temp=Screen->LoadNPC(i);
        if(temp->Misc[IDX_ENEMY_ID]==id)
            break;
    }
    
    return temp;
}




#9 LinktheMaster

LinktheMaster

    Hey Listen, Kid

  • Members
  • Real Name:Matt
  • Location:United States

Posted 07 January 2011 - 10:37 PM

To be 100% safe, it would probably be best to increment the unique_id like this:

CODE
unique_id = (unique_id + 1) % 10 + 1;


This would help prevent the (highly unlikely) occurrence of reaching the max value for integers. Besides, you'll only have up to 10 enemies (normally, at least), so this keeps things streamlined. The +1 is to prevent it from being 0, the default value.

icon_blah.gif

#10 Saffith

Saffith

    IPv7 user

  • Members

Posted 07 January 2011 - 10:57 PM

That's less safe, really. If you use it on eleven enemies, the eleventh will have the same ID as the first. You could replace 10 with a larger number, but that will never be completely safe. If you keep killing enemies, spawning more, and assigning them IDs, it'll eventually roll back around to 1. And if the enemy with ID 1 isn't dead yet, you'll have a duplicate.
That's quite unlikely, of course, but so are the problems with the previous two approaches. The way to be perfectly safe is to check the IDs currently in use to find one that's free, but that's almost always going to be more trouble than it's worth.

Edited by Saffith, 07 January 2011 - 10:57 PM.


#11 Gleeok

Gleeok

    It's dangerous to dough alone, bake this.

  • Members
  • Real Name:Pillsbury
  • Location:Magical Land of Dough

Posted 08 January 2011 - 03:13 AM

What!?? ..In order to run out of unique numbers you'd need 256 enemies on every screen and over 8,000,000 screens. I don't think that will happen.

..I forget zscript integers are fake real numbers though. So just increment by .0001:

CODE

unique_id += 0.0001;



There. icon_razz.gif



#12 Saffith

Saffith

    IPv7 user

  • Members

Posted 08 January 2011 - 11:10 AM

You could do it on one screen, you just have to keep killing enemies and spawning new ones. I'm not certain, but you might be able to do it all in one frame.
If you were to create, ID, and kill 256 enemies per frame, it'd take more than three days before it cycled through all possible numbers, and maybe longer if it couldn't maintain 60 FPS. So, yeah, it's hardcore nitpicking, but still.

#13 jman2050

jman2050

    Illustrious

  • Members

Posted 08 January 2011 - 10:09 PM

Making an entity's UID available to scripters would solve this problem immediately you know icon_razz.gif


0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users