PDA

View Full Version : How are the enemies coded?



ShadowTiger
09-13-2006, 09:35 PM
I had a rather bizarre idea. I want to create a Blue Darknut that's half the speed of a Blue Darknut, but more than twice the hit points, AND won't get knocked back when you hit it. Otherwise though, it's a normal Blue Darknut. Thus, I'm curious to see how the actual darknuts are coded so I can code something similar to how a real one behaves.

I heard somewhere that scripting custom enemies like this works very similar to how the actual enemies are coded. What are your thoughts on getting the actual enemy code to provide a foundation for coding our own custom enemies?

Dark Nation
09-14-2006, 01:30 AM
Here is the code for the darknuts:


// makes the enemy slide backwards when hit
// sclk: first byte is clk, second byte is dir
bool enemy::slide()
{
if(sclk==0 || hp<=0)
return false;
if((sclk&255)==16 && !canmove(sclk>>8,(fix)12,0))
{
sclk=0;
return false;
}
--sclk;
switch(sclk>>8)
{
case up: y-=4; break;
case down: y+=4; break;
case left: x-=4; break;
case right: x+=4; break;
}
if(!canmove(sclk>>8,(fix)0,0))
{
switch(sclk>>8)
{
case up:
case down:
if( (int(y)&15) > 7 )
y=(int(y)&0xF0)+16;
else
y=(int(y)&0xF0);
break;
case left:
case right:
if( (int(x)&15) > 7 )
x=(int(x)&0xF0)+16;
else
x=(int(x)&0xF0);
break;
}
sclk=0;
clk3=0;
}
if((sclk&255)==0)
sclk=0;
return true;
}



void enemy::constant_walk(int rate,int homing,int special)
{
if(slide())
return;
if(clk<0 || dying || stunclk || watch)
return;
if(clk3<=0)
{
fix_coords();
newdir(rate,homing,special);
clk3=int(16.0/step);
}
else if(scored)
{
dir^=1;
clk3=int(16.0/step)-clk3;
}
--clk3;
move(step);
}




eDarknut::eDarknut(fix X,fix Y,int Id,int Clk) : enemy(X,Y,Id,Clk)
{
noshield=false;
fired=false;
}

bool eDarknut::animate(int index)
{
if(dying)
return Dead();
switch (id)
{
case eDKNUT1:
constant_walk(5,144,0);
break;
case eDKNUT2:
constant_walk(4,160,0);
break;
case eDKNUT3:
constant_walk(3,200,0);
if(hp<=0)
{
int kids = guys.Count();
guys.add(new eDarknut(x,y,eDKNUT2,-25));
guys.add(new eDarknut(x,y,eDKNUT2,-25));
((enemy*)guys.spr(kids))->count_enemy = false;
((enemy*)guys.spr(kids+1))->count_enemy = false;
}
break;
case eDKNUT5:
constant_walk(2,255,0);
if(sclk==0 && !stunclk && !watch)
{
if (clk2>64)
{
clk2=0;
fired=false;
}
clk2+=rand()&3;
if ((clk2>24)&&(clk2<52))
{
tile+=20; //firing
if (!fired&&(clk2>=38))
{
Ewpns.add(new weapon(x,y, wpn, 0, wdp, dir));
fired=true;
}
}
}
break;
}
return enemy::animate(index);
}

void eDarknut::draw(BITMAP *dest)
{
bool tempbool=dummy_bool[1];
dummy_bool[1]=noshield;
update_enemy_frame();
dummy_bool[1]=tempbool;
enemy::draw(dest);
}


int eDarknut::takehit(int wpnId,int power,int wpnx,int wpny,int wpnDir)
{
if(dying || clk<0 || hclk>0 || superman)
return 0;

switch(wpnId)
{
case wLitBomb:
case wLitSBomb:
case wBait:
case wWhistle:
case wFire:
case wWind:
case wSSparkle:
case wGSparkle:
case wMSparkle:
case wFSparkle:
case wPhantom:
return 0;
case wBomb:
case wSBomb:
case wRefMagic:
case wHookshot:
case wHammer:
goto skip1;
}


if((wpnDir==(dir^1)) && !noshield)
{
sfx(WAV_CHINK,pan(int(x)));
return 1;
}

skip1:

switch(wpnId)
{
case wBrang:
if ((current_item(itype_brang,true)<3) || ((dir==up)&&(wpny<y))|| ((dir==down)&&(wpny>y))
|| ((dir==left)&&(wpnx<x)) || ((dir==right)&&(wpnx>x)) )
{
sfx(WAV_CHINK,pan(int(x)));
return 1;
}
else
{
stunclk=160;
break;
}

case wHookshot:
if ( ((dir==up)&&(wpny<y))|| ((dir==down)&&(wpny>y))
|| ((dir==left)&&(wpnx<x)) || ((dir==right)&&(wpnx>x))
&& (!noshield) )
{
sfx(WAV_CHINK,pan(int(x)));
return 1;
}
else
{
stunclk=160;
break;
}

case wArrow:
case wMagic:
sfx(WAV_CHINK,pan(int(x)));
return 1;
case wBomb:
case wSBomb:
if((wpnDir==(dir^1)) && !noshield)
break;
case wHammer:
if (wpnDir==(dir^1)&&get_bit(quest_rules,qr_BRKBLSHLDS))
noshield=true;
default:
hp-=power;
}

hclk=33;
if((dir&2)==(wpnDir&2))
sclk=(wpnDir<<8)+16;
sfx(WAV_EHIT,pan(int(x)));
return 1;
}


The id is the internal Darknut type. Every frame, each enemy's animate() function is called and returns 1 if the enemy is dead. take_hit() is called whenever the enemy is hit by one of Link's weapons.

Let me know if you have any other questions.

redmage777
09-14-2006, 03:08 AM
Intresting, The Darknut has always seemed to be more intelegent then most of the other enemys. The code for them is alot simpler then I would have imagined. Thank you for posting this. It give me a idea... It would be intresting to give a Darknut the "Rope Charge" ability would it not?

_L_
09-14-2006, 05:40 AM
What I want to know is what newdir() contains. That's where the magic is.
(And, for that matter, the clk, clk2, clk3 and step variables).

Still, it's interesting to see how takehit() is coded.

Dark Nation
09-14-2006, 08:58 AM
Sorry forgot to paste that.


// changes enemy's direction, checking restrictions
// rate: 0 = no random changes, 16 = always random change
// homing: 0 = none, 256 = always
// grumble 0 = none, 4 = strongest appetite
void enemy::newdir(int rate,int homing,int special)
{
int ndir;
if(grumble && (rand()&3)<grumble)
// for super bait
// if(1)
{
int w = Lwpns.idFirst(wBait);
if(w>=0)
{
int bx = Lwpns.spr(w)->x;
int by = Lwpns.spr(w)->y;
if(abs(int(y)-by)>14)
{
ndir = (by<y) ? up : down;
if(canmove(ndir,special))
{
dir=ndir;
return;
}
}
ndir = (bx<x) ? left : right;
if(canmove(ndir,special))
{
dir=ndir;
return;
}
}
}
if((rand()&255)<homing)
{
ndir = lined_up(8,false);
if(ndir>=0 && canmove(ndir,special))
{
dir=ndir;
return;
}
}

int i=0;
for( ; i<32; i++)
{
int r=rand();
if((r&15)<rate)
ndir=(r>>4)&3;
else
ndir=dir;
if(canmove(ndir,special))
break;
}
if(i==32)
{
for(ndir=0; ndir<4; ndir++)
{
if(canmove(ndir,special))
goto ok;
}
ndir = -1;
}

ok:
dir = ndir;
}


The default data for darknuts is as follows:


// flags flags2 tile width, height s_tile s_width s_height etile e_width e_height hp family cset anim e_anim, frate e_frate dp wdp weapon rate hrate step homing grumble item_set misc1 misc2 misc3 misc4 misc5 misc6 misc7 misc8 misc9 misc10 bgsfx bosspal
// eDKNUT1
{ 0x0100F720, 0x00000000, 140, 5, 0, 860, 5, 0, 4220, 16, 1, 4*DAMAGE_MULTIPLIER, eeWALK, 8, aDWALK, aNEWDWALK, 16, 16, 2*HP_PER_HEART/8, 2*HP_PER_HEART/8, wNone, 5, 0, 50, 144, 0, isMAGICBOMBS, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1},
// eDKNUT2
{ 0x0100F720, 0x00000000, 140, 5, 0, 860, 5, 0, 4240, 16, 1, 8*DAMAGE_MULTIPLIER, eeWALK, 7, aDWALK, aNEWDWALK, 16, 16, 4*HP_PER_HEART/8, 2*HP_PER_HEART/8, wNone, 4, 0, 67, 160, 0, isMAGICLIFE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1},
// eDKNUT3
{ 0x0100F720, 0x00000000, 140, 5, 0, 860, 5, 0, 4260, 16, 1, 16*DAMAGE_MULTIPLIER, eeWALK, 9, aDWALK, aNEWDWALK, 16, 16, 8*HP_PER_HEART/8, 2*HP_PER_HEART/8, wNone, 4, 0, 100, 200, 0, isMAGIC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1},
// eDKNUT5
{ 0x0100F720, 0x00000000, 140, 5, 0, 860, 5, 0, 4280, 16, 3, 30*DAMAGE_MULTIPLIER, eeWALK, 9, aDWALK, aNEWDWALK, 16, 16, 20*HP_PER_HEART/8, 40*HP_PER_HEART/8, ewSword, 10, 0, 150, 255, 0, isLIFE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1},


As for the variables:

flags - Mostly for vulnerability and invincibility.
flags2 - Mostly for vulnerability and invincibility.
tile - Tile used if new enemy tiles is turned off
width - How many tiles horizontally this enemy takes up in the tile list (not on the screen). This is so ZQuest knows how to warn you of tile overwrites/deletions. Additionally, this only applies when you are not using the new enemy tiles quest rule.
height - How many tiles vertically this enemy takes up in the tile list (not on the screen). This is so ZQuest knows how to warn you of tile overwrites/deletions. If this is a 0, then it's just 1 row of tiles. All normal enemies use a 0 here. If it's 1 or more, then it's a rectangular set of tiles (think striped vs. rectangular selection in the tile screen). This is for enemies that are drawn as a whole block, generally. Aquamentus, Gleeok, Digdogger, and Ganon are examples. Their height would be 2. Additionally, this only applies when you are not using the new enemy tiles quest rule.
s_tile - Additional (secondary) tiles used by this enemy that are not connected to the main tile group. Examples of this would be Gleeok heads and shieldless Darknuts.
s_width - Just like width but applies to the secondary tiles.
s_height - Just like height but applies to the secondary tiles.
e_tile - Same as tile but applies to new enemy tiles.
e_width - Same as width but applies to new enemy tiles.
e_height - Same as height but applies to new enemy tiles.
hp - Hit points. DAMAGE_MULTIPLIER is currently 2 so that wooden sword beams can be half power of the wooden sword (which does DAMAGE_MULTIPLIER damage) and still hurt enemies as hp uses no decimals dividing a 1 by 2 gives you a 0.
family - The family that an enemy is in. Currently, it is only used to differentiate between room guys and enemies.
cset - The cset that the enemy uses.
anim - The animation style (how an enemy changes frames) when not using new enemy tiles. This is explained below.
e_anim - Same as anim but for new enemy tiles.
frate - How many screen frames it takes to return to the first tile of the enimation. This is when not using new enemy tiles. 0 is the same as 256.
e_frate - Same as frate but for new enemy tiles.
dp - How much damage the enemy does to Link when they touch each other and Link has no rings. HP_PER_HEART is currently 16. Basically, the number before the multiplication is how many half-hearts damage is done to Link.
wdp - Same as dp but for the weapon damage.
weapon - The weapon that the enemy uses. A full list is available below.
rate - How often the enemy changes directions when arriving on a new square. Higher numbers mean more often. 15 is 100%
hrate - This is how often the enemy stops after arriving at a new 16x16 pixel location. Higher numbers mean more often. 15 is 100%
step - How far the enemy travels each frame. This is in pixels x 100 notation (500 means 5 pixels).
homing - How often the enemy tries to move towards Link. 0 is never. 255 is always.
grumble - How attracted to bait the enemy is. 0 means ignore. 4 means very attracted.
item_set - What items the enemy may drop. The full list is below.
misc1 - Misc. number used by certain enemies.
misc2 - Misc. number used by certain enemies.
misc3 - Misc. number used by certain enemies.
misc4 - Misc. number used by certain enemies.
misc5 - Misc. number used by certain enemies.
misc6 - Misc. number used by certain enemies.
misc7 - Misc. number used by certain enemies.
misc8 - Misc. number used by certain enemies.
misc9 - Misc. number used by certain enemies.
misc10 - Misc. number used by certain enemies.
bgsfx - What sfx to play in the background when this enemy is on the screen. This is generally for bosses. The full list is below.
bosspal - What special palette is loaded into cset 14 when the enemy is loaded onto the screen. A full list is below.


The lists below are not completely filled out yet. I'll try to add more information later on.

Animation styles

aNONE - No animation.
aFLIP - Flips one tile horizontally.
aFLIPSLOW - Like aFLIP but half the speed.
a2FRM - Switches between 2 adjacent tiles.
a2FRMSLOW - Like a2FRMSLOW but half the speed.
aOCTO - 2 tiles pointing down (a2FRM movement), flipped vertically for up followed by 2 tiles pointing left (a2FRM movement), flipped horizontally for right.
aTEK - 2 tiles, first is squatting, second is standing.
aLEV - 5 tiles. 2 are submerged, the third is surfacing/submerging, the last 2 are moving (a2FRM movement).
aWALK - 2 tiles pointing right (a2FRM movement) flipped horizontally for left, followed by a tile pointing down (aFLIP movement), followed by a tile pointing up (aFLIP movement).
aZORA - 2 tiles for surfaced, one pointing down and the other pointing up. 2 more secondary tiles with a2FRM movement for submerged.
aNEWZORA - 4 rows of 16 tiles each. The first 2 rows are for submerged and the next 2 rows are for surfaced. Both follow a4FRM8DIR movement.
aGHINI - 2 tiles. The first for left, down-lift, and down movement. Flipped horizontally for down-right and right movement. The second for right and up-right movement and flipped horizontally for up-left movement.
aARMOS - 4 tiles. The first 2 for left, down, and right movement, following the a2FRM pattern. The second 2 tile for up movement, following the a2FRM pattern.
aROPE - 2 tiles. Follows a2FRM movement. Flipped horizontally for left movement.
aWALLM - 2 tiles. Follows a2FRM movement.
aNEWWALLM - 1 row of 16 tiles. Follows a4FRM4DIR movement.
aDWALK - 5 tiles. 2 pointing down, following a2FRM movement. 1 pointing up following aFLIP movement. 2 pointing right, following a2FRM movement and flipped for left.
aVIRE - 4 tiles. 2 for left, down, and right, following a2FRM movement. 2 for up, following a2FRM movement.
a3FRM - 3 tiles. Cycles through tile 1, 2, 3, 2, then repeats.
aWIZZ - 4 tiles. 2 for down and right, flipped horizontally for left, following a2FRM movement. 2 tile for up, following a2FRM movement.
aUNUSED1 - Reserved for future use.
aDONGO - 10 tiles. 2 frames, each 2 tiles wide, facing right for walking and flipped horizontally for left. 1 frame, 2 tiles wide, facing right for taking damage from a swallowed bomb. 1 tile, facing down, flipped horizontally for walking. 1 tile, facing down for taking damage from a swallowed bomb. 1 tile, facing up, flipped horizontally for walking. 1 tile, facing up for taking damage from a swallowed bomb.
aMANHAN - 5 tiles. 2 tiles for the left arm following a2FRM movement. Flipped horizontally for right. 2 tiles for the top arm following a2FRM movement. Flipped vertically for down. 1 tile for the Manhandla body.
aGLEEOK - 6x2 tile rectangle and 4 tile strip. Main body is 3 frames of a 2x2 square each. Neck is 1 tile. Attached head is 1 tiles. Floating head is 2 tiles, following a2FRM movement.
aDIG -
aGHOMA -
aLANM -
a2FRMPOS -
a4FRM4EYE -
a4FRM8EYE -
a4FRM4DIRF -
a4FRM4DIR -
a4FRM8DIR -
aARMOS4 -
a4FRMPOS4DIR -
a4FRMPOS8DIR -
a4FRM3TRAP -
a4FRM8DIRB -
aNEWTEK -
aNEWPOLV -
a2FRM4DIR -
aNEWLEV -
aNEWDWALK -
aNEWWIZZ -
aNEWDONGO -
aDONGOBS -
a4FRMPOS8DIRF -
a4FRMPOS4DIRF -
a4FRMNODIR -
aGANON -


Enemy Weapons - Not a complete list of enemies that use these

ewFireball - Fireball. Zora, Gleeok head, etc.
ewArrow - Arrow/Spear. Moblin.
ewBrang - Boomerang. Goriya
ewSword - Sword. Lynel.
ewRock - Rock. Octorok.
ewMagic - Magic. Wizzrobe.
ewBomb - Bomb Explosion/Smoke.
ewSBomb - Super Bomb Explosion/Smoke
ewLitBomb - Lit Bomb (turns into ewBomb to actually explode). Octorok 4.
ewLitSBomb - Lit Super Bomb (turns into ewSBomb to actually explode).
ewFireTrail - Fire Trail. Fire Zol, Fire Gel.
ewFlame2 - Directed Flame. Fire Lynel (not implemented yet)
ewFlame2Trail - Directed Flame Trail. Similar to Fire Boomerang Fire Trail
ewIce - Ice Magic. Not implemented


Item Drop Sets

isNONE
isDEFAULT
isBOMBS
isMONEY
isLIFE
isBOMB100
isSBOMB100
isMAGIC
isMAGICBOMBS
isMAGICMONEY
isMAGICLIFE
isMAGIC2


Item Drop Set Items


{ "None", 0, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ "Default", 6, { iFairyMoving, i10Rupies, i5Rupies, i1ArrowAmmo, iHeart, iRupy, 0, 0, 0, 0 }, { 60, 3, 3, 5, 7, 12, 20, 0, 0, 0, 0 } },
{ "Bombs", 8, { iFairyMoving, iClock, i10Rupies, iRupy, i5Rupies, i1ArrowAmmo, iHeart, iBombs, 0, 0 }, { 58, 2, 4, 4, 10, 4, 5, 10, 12, 0, 0 } },
{ "Money", 7, { iFairyMoving, i10Rupies, iClock, i1ArrowAmmo, i5Rupies, iHeart, iRupy, 0, 0, 0 }, { 45, 3, 3, 5, 7, 15, 10, 22, 0, 0, 0 } },
{ "Life", 3, { iFairyMoving, iRupy, iHeart, 0, 0, 0, 0, 0, 0, 0 }, { 52, 8, 8, 32, 0, 0, 0, 0, 0, 0, 0 } },
{ "Bomb 100%", 1, { iBombs, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ "Super Bomb 100%", 1, { iSBomb, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ "Magic", 2, { iLMagic, iSMagic, 0, 0, 0, 0, 0, 0, 0, 0 }, { 76, 8, 16, 0, 0, 0, 0, 0, 0, 0, 0 } },
{ "Magic/Bombs", 9, { iFairyMoving, iLMagic, i10Rupies, iSMagic, iClock, iRupy, i5Rupies, iHeart, iBombs, 0 }, { 52, 2, 2, 2, 4, 4, 10, 4, 10, 12, 0 } },
{ "Magic/Money", 9, { iFairyMoving, iLMagic, i10Rupies, iSMagic, i1ArrowAmmo, iClock, i5Rupies, iHeart, iRupy, 0 }, { 36, 3, 3, 3, 6, 3, 5, 15, 10, 22, 0 } },
{ "Magic/Life", 7, { iFairyMoving, i10Rupies, iLMagic, i1ArrowAmmo, iSMagic, iRupy, iHeart, 0, 0, 0 }, { 20, 4, 2, 4, 4, 8, 16, 48, 0, 0, 0 } },
{ "Magic 100%", 2, { iLMagic, iSMagic, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 75, 25, 0, 0, 0, 0, 0, 0, 0, 0 } },

The first part is the name, the second part is how many different items can be dropped. The third part is the items to drop. The fourth part is the chance (not %) of getting a certain item. The first number in the fourth section is the chance of getting nothing. If an item is invalid for the quest, it is ignored in the drop list. For instance, if true arrows are not used, then the arrow ammunition items and chances are ignored.


Sound Effects

WAV_ARROW -
WAV_BEAM -
WAV_BOMB -
WAV_BRANG -
WAV_CHIME -
WAV_CHINK -
WAV_CLEARED -
WAV_DODONGO -
WAV_DOOR -
WAV_EDEAD -
WAV_EHIT -
WAV_ER -
WAV_FIRE -
WAV_GANON -
WAV_GASP -
WAV_HAMMER -
WAV_HOOKSHOT -
WAV_MSG -
WAV_OUCH -
WAV_PICKUP -
WAV_PLACE -
WAV_PLINK -
WAV_REFILL -
WAV_ROAR -
WAV_SCALE -
WAV_SEA -
WAV_SECRET -
WAV_SPIRAL -
WAV_STAIRS -
WAV_SWORD -
WAV_VADER -
WAV_WAND -
WAV_WHISTLE -
WAV_ZELDA -


Sprite Palettes

spAQUA - Aquamentus
spGLEEOK - Gleeok
spDIG - Digdogger/Digdogger Kids
spGANON - Ganon
spBROWN - Brown Ganon
spPILE - Dead Ganon Dust Pile
spBLUE - Blue Ring Link (copied to cset 6 when Link gets the blue ring)
spRED - Red Ring Link (copied to cset 6 when Link gets the red ring)
spGOLD - Gold Ring Link (copied to cset 6 when Link gets the gold ring)
spICON1 - Save game icon 1
spICON2 - Save game icon 2
spICON3 - Save game icon 3
spICON4 - Save game icon 4
spGLEEOKF - Fire Gleeok
spFROZEN - Frozen (for use when ice weapons get added)

{DSG}DarkRaven
09-14-2006, 09:33 AM
All righty, ZC is officially too complicated for me. :P

Kudos to all of you who understand this, and major credit to the Devs for making this all possible. Just don't forget to make the editor simple enough for those of us who croak at the sight of code more complex than HTML. Please?

_L_
09-14-2006, 09:52 AM
Hey guys, let's keep this going to see if we can trick Dark Nation into accidentally making ZC open source.

Dark Nation
09-14-2006, 09:56 AM
I *could* just delete this entire thread, ya know. :p

_L_
09-14-2006, 10:42 AM
I suppose a "touché" is in order.