ZoriaRPG icon

NES Zelda Drops v0.3 for 2.55

ZoriaRPG | PRO | 02/17/19 03:02:23 PM UTC | 0 ⭐ | 795 👁️ | Never ⏰ | []
C++ |

13.83 KB

|

None

|

0 👍

/

0 👎

//Zelda 1 Style Random drops
//v0.3
//For 2.55
//By: ZoriaRPG
//17th February, 2019 
 
script typedef ffc class;
 
class script Z1enemydrop
{
    void run(){}
    int killcount[2];
    const int KILL_NORMAL;
    const int KILL_CONSEC;
    
    const int DROPSET_A = 0;
    const int DROPSET_B = 1;
    const int DROPSET_C = 2;
    const int DROPSET_D = 3;
    const int DROPSET_X = 4;
    const int DROPSET_M = 5; MAGIC, 100%
    const int DROPSET_V = 6; //Vire splits increase the count, but do not drop items. 
    const int DROP_X_OFS = 0;
    const int DROP_Y_OFS = 0;
    
    int chances[] = {31,41,59,41, 0, 100};
    int lists[4*10]=
    {
        I_RUPEE, I_HEART, I_RUPEE, I_FAIRY, I_RUPEE, I_HEART, I_HEART, I_RUPEE, I_HEART, I_HEART,
        I_BOMB, I_RUPEE, I_CLOCK, I_RUPEE, I_HEART, I_BOMB, I_RUPEE, I_BOMB, I_HEART, I_HEART,
        I_RUPEE, I_HEART, I_RUPEE, I_RUPEE5, I_HEART, I_CLOCK, I_RUPEE, I_RUPEE, I_RUPEE, I_RUPEE5,
        I_HEART, I_FAIRY. I_RUPEE, I_HEART, I_FAIRY, I_HEART, I_HEART, I_HEART, I_RUPEE, I_HEART,
        -1, -1, -1, -1, -1, -1,  -1,  -1,  -1,  -1 //Vires when they only split, will increment counts, bu never drop items
    };
    //The dropset that a given enemy uses; SPLITTERS NEED SPECIAL HANDLING?
    //This could instead be an enemy editor attribute fied of some sort. rather than an array,
    //so that the questmaker can customise it. 
    int enemydropsets[]=
    {
        //guys
        DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X,
        DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X,
        DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X,
        DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X,
        
        //enemies start here
        DROPSET_A, DROPSET_B, DROPSET_A, DROPSET_B, DROPSET_A,
        DROPSET_C, DROPSET_C, DROPSET_A, DROPSET_A, DROPSET_B,
        DROPSET_D, DROPSET_C, DROPSET_D, DROPSET_D, DROPSET_X,
               //GHINI2 ?PHANTOM
        DROPSET_C, DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X,
        DROPSET_X, DROPSET_C, DROPSET_X, DROPSET_C, DROPSET_C,
        DROPSET_B, DROPSET_D, DROPSET_X, DROPSET_C, DROPSET_B,
        DROPSET_D, DROPSET_X, DROPSET_B, DROPSET_X, DROPSET_B,
        DROPSET_C, DROPSET_B, DROPSET_A, DROPSET_D, DROPSET_D,MOLDORM
        DROPSET_D, DROPSET_D, DROPSET_D, DROPSET_D, DROPSET_D,
        DROPSET_D, DROPSET_X, DROPSET_X, DROPSET_D, DROPSET_D,
        DROPSET_D, DROPSET_D, DROPSET_D, DROPSET_D, DROPSET_D, //LANMOLA
        DROPSET_C, DROPSET_D, DROPSET_D, DROPSET_X, DROPSET_C,
        DROPSET_C, DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X,
        DROPSET_X, DROPSET_B, DROPSET_D, DROPSET_X, DROPSET_X,
        DROPSET_X, DROPSET_B, DROPSET_D, DROPSET_D, DROPSET_D,
        DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_B,
        DROPSET_A, DROPSET_C, DROPSET_C, DROPSET_D, DROPSET_D,
        DROPSET_D, DROPSET_M, DROPSET_A, DROPSET_A, DROPSET_D,
        DROPSET_D, DROPSET_D, DROPSET_D, DROPSET_A, DROPSET_D,
        DROPSET_D, DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X,
        DROPSET_C, DROPSET_D, DROPSET_D, DROPSET_X, DROPSET_X,
        DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X,
        DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X,
        DROPSET_X, DROPSET_D, DROPSET_A, DROPSET_B, DROPSET_B,
        DROPSET_B, DROPSET_B, DROPSET_X, DROPSET_X, DROPSET_X,
        DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X,
        DROPSET_X, DROPSET_X, DROPSET_B, DROPSET_A, DROPSET_X,
        DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X, DROPSET_X,
        DROPSET_X, DROPSET_X, DROPSET_C, DROPSET_X, DROPSET_C,
        DROPSET_A, DROPSET_X, DROPSET_X, DROPSET_D, DROPSET_D,
        DROPSET_M, DROPSET_C, DROPSET_D, DROPSET_D, DROPSET_M,
        DROPSET_X, DROPSET_X, DROPSET_X
    
    };
    int getChance(int enemyid)
    {
        return chances[enemydropsets[enemyid]];
    }
    bool canDrop(int enemy_id)
    {
        return ( Rand(1,100) <= getChance(enemy_id) ); 
    }
    void doDrop(npc n)
    {
        if ( n->HP > 0 ) return; //wait until dead
        bool ignore = false;
        switch(n->Family)
        {
            case NPCT_MOLDORM:
            case NPCT_MANHANDLA:
            case NPCT_GLEEOK:
            case NPCT_LANMOLA:
            case NPCT_PATRA:
            {
                if ( !n->isCore ) ignore = true; break;
            }
            case NPCT_DODONGO: ignore = true; break; //These never drop items, AFAIK. 
            default: break;
        
        }
        if ( !ignore )
        {
            if ( canDrop(n->ID) )
            {
                int drp_itm_id = lists[enemydropsets[n->ID]+killcount[KILL_NORMAL]];
                switch(killcount[KILL_CONSEC])
                {
                    begin insane Z1 consecutive drop rules. 
                    case 10: drp_itm_id = I_BOMBAMMO1; break;
                    case 16: drp_itm_id = I_FAIRY; break;
                    default: break;
                    
                    /*
                    TO IMPLEMENT
                    Advanced strategies: If a forced bomb/rupee or forced fairy drops, 
                    the consecutive counter for bombs/rupees is reset, but not the counter 
                    for fairies. So no matter how many hits you get on Manhandla with one 
                    well-placed bomb, the 10 count is reset after defeating him. 
                    The same is true of Dodongo. Likewise with a "Hippo Fairy" 
                    (going into a Patra with 7 on the consecutive counters, so that the 
                    Patra's center will be kill #16 and get a fairy), the bomb/rupee count 
                    resets to 0. Credit to Khananaphone for this discovery. 
                    This means that if you get a forced fairy drop, the next 
                    consecutive bomb/rupee kill will not be at 20. It will be at 26. 
                    So bomb/rupee drops work in one of two methods:
 
                        10, (Fairy at 16), 26, 36, 46...
                        10, (Skip fairy at 16 by killing non-dropping enemy), 20, 30, 40...             
                    */
                }
                item drp = Screen->CreateItem(drp_itm_id);
                drp->X = n->X + DROP_X_OFS; drp->Y = n->Y + DROP_Y_OFS;
                drp->Pickup = IP_TIMEOUT;
                ( killcount[KILL_NORMAL] < 9 ) ? ++killcount[KILL_NORMAL] : killcount[KILL_NORMAL] = 0; 
                ++killcount[KILL_CONSEC]; //when does this roll over on the NES?
            }
        }
    }
    void resetKillcount()
    {   
        if ( Link->InvFrames ) killcount[KILL_CONSEC] = 0;
    }
    
}
 
link script LinkZ1
{
    void run()
    {
        while(1)
        {
            Z1enemydrop.resetKillcount();
            for ( int q = Screen->NumNPCs(); q > 0; --q )
            {
                npc n = Screen->LoadNPC(q);
                Z1enemydrop.doDrop(n);
            }
    
    
    }
}
 
/*
Forced drops
 
Some item drops can be forced and take priority over the standard drop system. 
Forced drops are handled by two consecutive kill counters and a bomb flag. 
The first counter, the ten count ($50), controls whether a forced 5 rupee is dropped. 
The bomb flag ($51) controls whether the ten count drop should be a bomb, instead. 
The second counter, the fairy count ($0627), controls whether fairies are dropped. 
All of these variables are cleared when Link collides with an enemy (including bubbles and 
even the recorder whirlwind).
 
When Link kills an enemy (that is, the enemy makes a dying sound), both counters are incremented. 
This occurs at kill time, before the dying animation. 
Item drops occur later, after the dying animation completes. When the drop is calculated, 
the game checks the counters to decide what to drop. If the fairy count is 16, then a fairy is 
dropped and the ten count and bomb flag are cleared. Otherwise, if the ten count is 10, 
either a 5 rupee is dropped if the bomb flag is clear or a bomb is dropped if the flag is set, 
and the ten count and bomb flag are then cleared. So, in the case of any forced drop, 
the ten count and bomb flag are cleared, but the fairy count is not.
 
Some enemies cannot drop items. Because they don't run the drop code, they also can't 
drop forced items, and thus won't consume the counters. These enemies are those in the 
no-drop group, as well as stalfos, gibdos, and like likes in slot 1 
(because these enemies may be carrying the room treasure, and it would be undesirable 
if they dropped both the treasure and an item). We'll call these no-drop enemies.
 
If the ten count transitions from 9 to 10 due to a bomb kill, the bomb flag is set. 
The ten count cannot exceed 10 and will hold at that value even if additional 
kills occur before the forced item can be dropped, such as by killing a group of 
enemies at once or killing no-drop enemies. 
Once any forced drop occurs, the counter is reset to 0.
 
The fairy counter, however, has no hard limit. 
It will continue to increase with each kill. 
Because of this, the fairy drop can be skipped. 
Any drop occurring when the fairy counter is 16 will be a fairy, so killing a 
group of enemies to bring the count above 16 at drop time will skip the fairy. 
Alternatively, because no-drop enemies can't drop forced items, 
they can die with the counter at 16 without performing the drop. 
Skipping the fairy will avoid resetting the ten count and bomb flag.
 
Because the fairy counter isn't cleared when the drop occurs, multiple 
fairies can be dropped from the same count. For example, if the counter is 
12 and 4 enemies are quickly killed before any of them can calculate a drop, 
the counter will be 16 when the drops are handled, so all 4 drops will be fairies.
 
Note that while the fairy counter has no hard limit, it is an 8-bit value and will 
overflow to 0 at 256 kills, allowing it to force more fairies at the next 16.
 
There are some notable special cases and edge cases with counters. These include:
 
Dodongos: Killing a dodongo does not increment either counter, whether killed with 
bombs or the sword. If the dodongo is killed with a sword, however, 
the ten count is set to 10 and the bomb flag is set, forcing a bomb at drop time. 
This method of forcing the bomb can result in unexpected behavior. 
If Link takes a hit after killing the dodongo, but before the drop, the forced bomb 
will be lost. Furthermore, if the fairy count is 16 when the drop occurs, the fairy 
will be dropped instead of the bomb and the ten count and bomb flag will be cleared. 
Finally, if Link kills a second dodongo with a sword before the first one has dropped 
its bomb, the second will not drop a bomb because the forced drop system has no way of 
queuing up multiple forced bombs.
 
Vulnerable unkillable enemies: Enemies such as the manhandla core and flying 
gleeok heads are invincible, but actually handle weapon collision and will 
clear their iframes and unkill themselves each frame. This means they count as a 
kill every frame that a weapon is touching them. Bombing these enemies is a good 
way to force a bomb because it will quickly max out the ten count and set 
the bomb flag, but it will also be adding to the fairy counter, which could override 
the bomb drop when killing the boss. The old man in the dungeon also behaves this way, 
but because he doesn't clear his iframes, he'll only count as one kill per attack.
 
Multi-part enemies: Manhandla's hands, gleeok's attached heads, and moldorm's 
and lanmola's segments each increment the consecutive counters when killed, 
but the death of the whole enemy does not grant an additional consecutive kill; 
that is, killing the last part will only add 1 to the counters, not 2. 
A perfect bomb on manhandla will increase the counters by 5 instead of 4, 
though, because the core is also hit and counts as a kill.
 
Leader enemies: When a ringleader (such as the level 2 goriya leader) is killed, 
the other enemies in the room die by being directly transitioned to the dying state 
(by setting dynamic_spawn_state to #$10) and will not impact the consecutive counters. 
The ringleader itself counts as normal, however. Ghini leaders also share this same 
behavior (through a similar mechanism).
 
Zols: While splitting enemies (zols and vires) don't normally count as kills 
when split, zols are vulnerable to an additional weapon hit on the frame they 
split, allowing them to be killed and counted as a consecutive kill. 
This is due to a bug where zols delete themselves before splitting 
(in case there aren't enough slots for the gels they're creating), 
but instead of exiting immediately afterward, their AI continues to do 
meaningful work on the deleted object, including collision checks. 
The deletion (through ClearObject at Bank7_FEB1) clears the object_iframes variable, 
which guarantees the zol can take a hit during this one last check. 
Because deletion clears the object's dynamic_id variable, the zol doesn't 
run again and thus can't drop an item nor increment the global counter. 
Its health is not cleared by ClearObject, so the weapons it collides with must 
do enough damage to kill it. Note that if a gel was spawned into the zol's slot, 
the gel will be hit, instead.
 
Vires are more robustly written and don't suffer from this bug.
 
Overkill: When an enemy is killed by a weapon, it still finishes checking the 
remaining weapons that frame, as normal. This means it can be hit by those additional 
weapons, which may also kill it. The consecutive kill counters are incremented at 
kill time, so these additional kills will each increment the counters like with 
any normal kill. As a result, killing a single enemy by hitting it with multiple 
weapons on one frame can add more than one to the counts. 
This can be done most practically by timing multiple attacks to hit when an enemy's iframes expire.
 
Note that the health the enemy has after a killing blow can vary. 
If the weapon damage was equal to the enemy's current health, the resulting health 
is 0, so any following hits will count as kills. 
However, if the damage was more than the current health, the health is unchanged. 
Furthermore, the order in which weapons are checked can impact how many kills the 
enemy gives because each weapon hit may reduce the health for the following weapons. 
Weapon check order is boomerang, beam (sword/rod), flame/bomb slot A, flame/bomb slot B, 
sword, arrow/rod.
 
Sub-frame behavior: Because everything in the game must be handled sequentially, 
events handled earlier can directly impact those handled later, so the order of 
execution can result in surprising behavior. For example, if an enemy is killed 
on the same frame that multiple other enemies are dropping items, and that kill 
sets the fairy counter to 16, then any drops handled before the kill was registered 
would be normal random drops, and drops after the kill would be forced fairies, 
despite all of the drops happening on the same frame. 
*/

Comments