Duke4.net Forums: Enemy Coding Tutorial - Duke4.net Forums

Jump to content

Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

Enemy Coding Tutorial

User is offline   Danukem 

  • Duke Plus Developer

#1

Hey everyone, for many years I have thought about making a coding tutorial for enemies in Duke 3D, but never did it. The time is now! I will make this tutorial as I code the Cyber-Enforcer boss with sprites by sebabdukeboss20. I chose this one because I feel that the enemy will be pretty standard without a lot of weird stuff, which makes it good tutorial fodder. If I were doing this without the tutorial, the process would be pretty quick, but since I am doing it this way it will be a much more drawn out process. I anticipate that writing the tuturial will take much longer than the actual coding. Feel free to reply in this thread and ask questions.

Tutorial Scope
This tutorial will show how to code an enemy for EDuke32 in the CON scripting language by walking through a series of general steps that build up a typical enemy from scratch.
We already have a wiki that documents all the CON commands with some examples of how to use them, so I'm not going to dwell on how to use individual commands. However, aspiring CON coders (maybe as many as a dozen in the last 10 years!) sometimes get overwhelmed with the giant list of commands and don't know where to start.

Assumptions
I assume that you have a basic working knowledge of EDuke32 and Duke Nukem 3D: you know what a CON file is, you know what Mapster is, you know what a tile number is, and various other basic facts (trying to list them all would be a project in itself).
I assume that the art for your enemy is already accessible in game. This means that you can go into Mapster and you can view the tiles for the enemy. I also assume that the tiles have been arranged properly for coding. This means that there is a series of animations with various frames and angles, and they are in an order corresponding to some viewtype useable by the action command. In most cases this will be viewtype 5, which is used by pigcop, lizman, etc.
I assume that you have the ability to open up CON files, create them and edit them using the editor of your choice.

Ok let's go!

STEP 1: SOME DEFINITIONS AND FLAGS

Your enemy is going to be an "actor", and the actor is identified with the tile number you define it on. A definition is just a label that you can use in place of a number. In most cases it makes sense to define your enemy as the first tile of its walking animation. If you plan to have a stayput version of the enemy, it is traditionally defined on the very next tile (see the DEFS.CON from the base game). If there are other actors associated with your new enemy and they are not yet defined in the game, then you should define them as well. It's also a good idea to define the health amount(s) for your new enemy -- you could just type in the numbers directly in the actor declaration, but using labels makes it easier for people to edit.

In my case I have this to start with:

define LIZBOSS 5185
define LIZMINE 5246
define LIZBOSSSTRENGTH 5000
define LIZBOSSPALSTRENGTH 1000


Depending on how your files are organized, you might want your definitions for different types of things to be in different files. In the base game, you will find the tile number definitions in DEFS.CON, and you will find enemy strength amounts in USER.CON.


An important note about hitboxes: The hitbox dimensions of your enemy will be determined by the dimensions of tile that you define it on. The cyber-enforcer tile 5185 has dimensions 116x115, which makes the hitbox essentially a square. The square will be bigger or smaller depending on how large or small the enemy is in game (xrepeat and yrepeat values), but those dimensions will not change depending on the animation displayed. If there is an animation where the enemy is lying down, it will have exactly the same hitbox as when it was standing up. This has important implications that will warrant more discussion, but for now let's move on.

Spriteflags
There are special flags we can set on our tiles to globally change how those tiles behave. This is done via the spriteflags command. These flags should be set after the definitions but before the actor code. Here is what I am using for the cyber enforcer:

spriteflags LIZBOSS 5251072
// HURTSPAWNBLOOD + NODAMAGEPUSH + SMOOTHMOVE


STEP 2: ACTIONS AND ANIMATION TEST


The action command has two distinct uses. When used outside of an actor declaration, it defines an animation. When used inside an actor declaration, it causes the actor to initiate the animation of the same name that had been defined earlier. The first thing I do when starting to code an enemy is define all of its animations. The next thing I do is write a simple block of code that will enable me to easily view them all in game. This step is crucial, because I want to make sure that all of the animations are correct before going forward. Any flaws in the frames, animation definitions, speed and offsets will become obvious. If you skip this step and go straight to making the enemy move around, shoot etc., then it will be harder to notice and correct animation problems.

Here is the code that I made for my animation test:

action ALIZBOSSWALKING     	0   4  5  1  22
action ALIZBOSSRUNNING     	0   4  5  1  14
action ALIZBOSSSTARTSHOOT	20  1  5  1  20
action ALIZBOSSSHOOTING		25  2  5  1  5
action ALIZBOSSFLY      	35  1  5  1  10
action ALIZBOSSFALL      	35  1  5  1  10
action ALIZBOSSSTAND     	40  1  5  1  10
action ALIZBOSSSQUAT     	45  1  5  1  60
action ALIZSQUATDONE		50  1  5  1  30
action ALIZBOSSFROZEN     	50  1  5  1  10
action ALIZBOSSFLINTCH 		55  1  1  1  10
action ALIZBOSSDYING     	55  6  1  1  20
action ALIZBOSSLYINGDEAD   	60  1  1  1  10

useractor enemy LIZBOSS LIZBOSSSTRENGTH

fall
ifaction 0
{
	action ALIZBOSSWALKING
	ifspritepal 0 sizeat 96 80 else sizeat 48 40
}

ifhitspace ifcount 10
{
	resetcount
	ifaction ALIZBOSSWALKING action ALIZBOSSRUNNING else
	ifaction ALIZBOSSRUNNING action ALIZBOSSSTARTSHOOT else
	ifaction ALIZBOSSSTARTSHOOT action ALIZBOSSSHOOTING else
	ifaction ALIZBOSSSHOOTING action ALIZBOSSFLY else
	ifaction ALIZBOSSFLY action ALIZBOSSSTAND else
	ifaction ALIZBOSSSTAND action ALIZBOSSSQUAT else
	ifaction ALIZBOSSSQUAT action ALIZSQUATDONE else
	ifaction ALIZSQUATDONE action ALIZBOSSFLINTCH else
	ifaction ALIZBOSSFLINTCH action ALIZBOSSDYING else
	ifaction ALIZBOSSDYING action ALIZBOSSLYINGDEAD else
	ifaction ALIZBOSSLYINGDEAD action ALIZBOSSWALKING
}

enda


At the top we have the action declarations which define the animations. Below that is the actor declaration and code, followed by enda to signify the end of the actor's code.
The first block after the useractor declaration detects when the actor has no assigned animation (action) and assigns it the walking animation. The next block will cycle from one animation to the next if the player presses the action key and it has been 10 tics since the last press. With this done, you can place the enemy sprite in a map or spawn it from the console in game, then walk around it and inspect how the animations look. The result can be seen in the video below.



What I found is that JBlade had already done a good job of setting offsets on the tiles, which is why you don't see the cyber enforcer wiggling oddly left, right, up or down. I did find a few pink pixels in the lying dead frame, which I then fixed (if you look carefully you can see the pink pixels in the video).

A few notes about defining actions: It is normal to have multiple actions which use exactly the same frames of animation. Typically, walking and running are identical except for the final parameter of the action, which determines animation speed (the lower the number, the less lag between frames). It is also normal to have multiple actions which are identical in every way except for having different names. This is handy because they can be used when you assign them and check them. "ifaction ALIZBOSSFLY" will have a different value than "ifaction ALIZBOSSFALL" even though what the actions display is identical. As a rule of thumb, if an animation is going to be used for more than one purpose, then go ahead and define it multiple times with different actions corresponding to the different purposes. If it turns out you didn't use them all, you can always delete them later.

This post has been edited by Trooper Dan: 31 January 2018 - 11:31 PM

12

User is offline   Jblade 

#2

Nice work so far - the community could definitely do with more tutorials in general. I have been filling out the wiki with new commands when I have time to, but I'm pretty poor at writing things like this out. The animation test is a really good idea! I never thought of trying something like that - generally when I make an enemy I start from scratch and get it walking around after me at first before starting work on the pain and death animations - then I start to work on the attacks. It takes careful tweaking to strike a good balance of aggressiveness and also pacing so it's not just attacking constantly (Of course it depends on the scope of your project, but attacking constantly can be a bad thing at times since they're probably gonna be stationary and not following the player whilst doing so)

About offsetting tiles - this is something I never bothered to do until recently, which was ignorance on my part since enemies look so much better with the tiles properly offset. I used Bafed to do it with (Dukeres' align feature is broken and flipped basically) The main goal is to make sure every tile on an enemy is centred on a focal point, or it's rough centre of mass horizontally (so with the Cyber lizman, I picked a spot in between his legs that was about centre, and then aligned all of his tiles so that point was in the middle in nearly all tiles) It's not a hard job to do at all, and even just a cursory glance of the enemy tiles will reveal how much better they look with this done.

This post has been edited by Jblade: 31 January 2018 - 03:03 AM

2

User is offline   Sixty Four 

  • Turok Nukem

#3

This is the part of it I understand just fine, but the ifhitweapon stuff gets more complicated are you going to add to this? Thanks anyways that's a cool enemy there btw.
0

User is offline   blizzart 

#4

Nice to see, that there is a more detailed tutorial out here. Well done.

But is it just me or is the gun of the enemy switching from one hand to another when you turn around it? :dukecigar:
0

User is offline   Jblade 

#5

View Postblizzart, on 31 January 2018 - 09:39 AM, said:

But is it just me or is the gun of the enemy switching from one hand to another when you turn around it? :dukecigar:

Yeah, it has 5 frames for angles so they're mirrored when you walk the other side of it. That's standard for all Duke enemies (Doom has 8 frames per rotation, but they also flip the sprites on symmetrical enemies so there's effectively only 2 sets of frames) If you wanted to, you can of course have 8 frames as well (or 7 for a more detailed turning animation)
2

User is offline   Danukem 

  • Duke Plus Developer

#6

View PostSixty Four, on 31 January 2018 - 07:00 AM, said:

This is the part of it I understand just fine, but the ifhitweapon stuff gets more complicated are you going to add to this? Thanks anyways that's a cool enemy there btw.


This was just the beginning, most of the tutorial is still to come. When I'm finished I think I will edit it together and post it on the wiki, but doing it this way initially gives me feedback as I go and helps me understand which details to include.
2

User is offline   stumppy84 

#7

Awesome! Thanks for this!! A weapon tut like this would be awesome! I know that’s a lot harder as it’s hard-coded.
0

User is offline   Danukem 

  • Duke Plus Developer

#8

View Poststumppy84, on 31 January 2018 - 04:26 PM, said:

Awesome! Thanks for this!! A weapon tut like this would be awesome! I know that’s a lot harder as it’s hard-coded.


Weapons aren't necessarily harder to code; in fact, they can be very easy. But the process can vary in difficulty and complexity depending on the weapon. The easiest case would be a hitscan weapon which replaces one of the existing weapons. Things get a lot harder if you are expanding the total number of weapon slots, because then you have to work around the hardcoded systems for weapon switching and ammo. It also gets harder when your weapon adds status effects to enemies or the projectile has unusual properties.
1

User is offline   Danukem 

  • Duke Plus Developer

#9

STEP 3: DEFINE YOUR SOUNDS

This step could be considered part of the earlier definition stage, but I like to wait until I have seen all the animations in-game before adding the sounds. Seeing the animations gives me a better idea of what the enemy should sound like and how many sounds I need. In any case, sound is extremely important and you should never skimp on sounds. I recommend the following:

1 sound that plays when the enemy first activates
2 roaming sounds, for when the enemy is active but not attacking
1 or more pain sounds
1 or more death sounds
1 attack sound for each attack the enemy possesses
1 additional sound for each special action possessed by the enemy (such as melee, jumping, etc.)

Try to avoid recycling sounds that have been used for a different enemy. The more distinctive you can make each enemy, the better. Having said that, I have been guilty of recycling sounds myself...that doesn't make it a good practice, though.

Here are the sound definitions that I have so far for the cyber enforcer:

define CYBOSS_RECOG 915
definesound CYBOSS_RECOG		cyboss_recog.ogg	-64   64   128   0   -4096
define CYBOSS_ATTK 916
definesound CYBOSS_ATTK			cyboss_attk.ogg		-64   64   160   0   0
define CYBOSS_LAND 917
definesound CYBOSS_LAND			cyboss_land.ogg		0      0   160   0   -2048
define CYBOSS_SWOOP 918
definesound CYBOSS_SWOOP		cyboss_swoop.ogg	0      0   160   0   -2048
define CYBOSS_DEATH 919
definesound CYBOSS_DEATH		cyboss_death.ogg	0      0   192   0   -6144
define CYBOSS_PAIN 920
definesound CYBOSS_PAIN			cyboss_pain.ogg		0      0   96    0   0
define CYBOSS_ROAM1 921
definesound CYBOSS_ROAM1		cyboss_roam1.ogg	0      0   64    0   -2048
define CYBOSS_ROAM2 922
definesound CYBOSS_ROAM2		cyboss_roam2.ogg	0      0   64    0   -2048


I chose to use nonzero values in many cases for the last 4 parameters of the definesound command, but you can use zeros without ill effect in most circumstances. Take a look at the definesound wiki entry and the examples in USER.CON to learn how to use the parameters.

STEP 4: MOVES AND "AI"S
A move, like an action, is first defined outside of an actor declaration, and then is used to command actors in the actor's code. Most enemies have at least 3 different speeds: stopped, moving slowly, and moving quickly. Some have more. As an example, here are the moves for the cyber enforcer:

move LIZBOSSSTOPPED
move LIZBOSSSHRUNKVELS 32
move LIZBOSSWALKVEL 112
move LIZBOSSRUNVEL 224
move LIZBOSSJUMPVEL 312


The LIZBOSSSTOPPED move will be used whenever the cyber enforcer is not moving. The shrunk speed of 32 is standard and will be used when it is shrunken. The walk and run speeds have obvious uses, while LIZBOSSJUMPVEL is less obvious. The plan is to make the cyber enforcer fly forward at a high velocity; the vertical part of the speed will be handled separately.

In addition to actions and moves, we have the option of declaring "ai" routines for the actor to use. An ai is not as fancy as it sounds: when you set a predefined ai on an actor, it causes the actor to display a certain animation, move at a certain speed, and then exhibit an additional type of behavior from a small selection of hardcoded moveflags. None of those behaviors are very smart, but they can be useful. It is also possible to access those moveflags without defining an ai -- you can assign them by adding them as parameters after move commands in the actor code.

Here are the ai declarations that I will be using for the next stage of coding:

ai AILIZBOSSWAIT			 ALIZBOSSSTAND LIZBOSSSTOPPED geth
ai AILIZBOSSDYING			ALIZBOSSDYING LIZBOSSSTOPPED geth
ai AILIZBOSSHIT				ALIZBOSSFLINTCH LIZBOSSSTOPPED faceplayer
ai AILIZBOSSFROZEN			ALIZBOSSFROZEN LIZBOSSSTOPPED geth
ai AILIZBOSSSHRUNK			ALIZBOSSRUNNING LIZBOSSSHRUNKVELS fleeenemy
ai AILIZBOSSGROW			ALIZBOSSGROW LIZBOSSSTOPPED geth


These ai's will be useful for coding the damage and death handling, which is the next stage. Other ai's, involving movement and attacking, will be declared and used later.


STEP 5: DAMAGE AND DEATH HANDLING

Writing the code that handles damage and death can be tedious, and it is easy to cause serious issues if you do it wrong. Mistakes can result in enemies that are unkillable, enemies that rise from the dead and then become unkillable and glitched, and even softlocking the game (if a game-ending boss is deleted too early). The two biggest reasons for the difficulty are: (1) the unintuitive behavior of the ifhitweapon command, and (2) the fact that standard Duke enemies can die in five different ways, all of which require special CON code to handle.

Despite the difficulty, I recommend adding most of the code to handle damage and death before going any further. Once the enemy is running around and shooting, it becomes more difficult to observe the effects of damaging it -- for example, it might run away from you. With this in mind, I have included a large amount of code under the spoiler tag below. This includes all of the steps discussed so far, with the addition of all the important damage and death handling the enemy needs.

Spoiler


[TO BE CONTINUED]
6

User is offline   Sixty Four 

  • Turok Nukem

#10

Its cool to see how you do it step by step its an honor lol. Hey maybe you can make a new post and also update the op including the next step, that way all the steps are on the one post with easier access. Just a thought either way very nice man.
0

User is offline   Danukem 

  • Duke Plus Developer

#11

View PostSixty Four, on 01 February 2018 - 06:09 PM, said:

Its cool to see how you do it step by step its an honor lol. Hey maybe you can make a new post and also update the op including the next step, that way all the steps are on the one post with easier access. Just a thought either way very nice man.


It's a work in progress. I hate using the forum as a text editor -- I should have anticipated that. When it's done I'll go back and collect it all into one narrative, edit it and put it on the wiki. In the mean time I will take a short break from it to actually finish coding the cyber enforcer.
3

User is offline   F!re-Fly 

#12

This is a very good idea. In any case very good tutorial. I am about to recode the Dummyduke of the BETA version v0.99.
0

User is offline   F!re-Fly 

#13

A question about the animation of a monster. What must be done for the animation to be done in Mapster32?
0

User is offline   Danukem 

  • Duke Plus Developer

#14

Since I never finished this, and I have noticed that people still refer to it, I have decided to post what ended up being the final code for this boss in Alien Armageddon 1.0
I have removed a few complex things that weren't really needed for the enemy to function and added a couple of comments, to make it more readable. The code also includes code for a mine that the boss plants. I'm happy to answer questions about it, but I don't plan to go through step by step.

// CYBER ENFORCER BOSS
// sprites by sebabdukeboss20, code by Dan Gaskill
// version 1.0 February 9, 2018

// define LIZBOSS 5185
// define LIZBOSSSTAYPUT 5186
define LIZMINE 5246
spriteshadow LIZMINE


// spriteflags LIZBOSS 5251072
// HURTSPAWNBLOOD + NODAMAGEPUSH + SMOOTHMOVE

gamevar lizjumpx 0 1
gamevar lizjumpy 0 1
gamevar lizjumpdist 0 1
gamevar LIZBVAR 0 0
gamevar LIZBVAR2 0 0
gamevar LIZBVAR3 0 0

action ALIZBOSSWALKING     	0   4  5  1  22
action ALIZBOSSRUNNING     	0   4  5  1  14
action ALIZBOSSSTARTSHOOT	20  1  5  1  20
action ALIZBOSSSHOOT1		25	1  5  1  5
action ALIZBOSSSHOOT2		30  1  5  1  5
action ALIZBOSSFLY      	80  1  5  1  10
action ALIZBOSSFALL      	35  1  5  1  10
action ALIZBOSSSTAND     	40  1  5  1  10
action ALIZBOSSSQUAT     	45  1  5  1  60
action ALIZSQUATDONE		50  1  5  1  30
action ALIZBOSSFROZEN     	50  1  5  1  10
action ALIZBOSSFLINTCH 		55  1  1  1  10
action ALIZBOSSGROW 		55  1  1  1  10
action ALIZBOSSDYING     	55  6  1  1  20
action ALIZBOSSLYINGDEAD   	60  1  1  1  10

move LIZBOSSSTOPPED
move LIZBOSSSHRUNKVELS 32
move PALLIZBOSSWALKVEL 96
move LIZBOSSWALKVEL 132
move LIZBOSSRUNVEL 280
move LIZBOSSJUMPVEL 416 -208
move LIZBOSSFALLVEL 416

ai AILIZBOSSWAIT			ALIZBOSSSTAND LIZBOSSSTOPPED geth
ai AILIZBOSSSEEK			ALIZBOSSWALKING LIZBOSSWALKVEL seekplayer
ai AILIZBOSSCHARGE			ALIZBOSSRUNNING LIZBOSSRUNVEL faceplayersmart
ai AILIZBOSSSHOOT			ALIZBOSSSTARTSHOOT LIZBOSSSTOPPED faceplayer
ai AILIZBOSSDYING			ALIZBOSSDYING LIZBOSSSTOPPED geth
ai AILIZBOSSHIT				ALIZBOSSFLINTCH LIZBOSSSTOPPED faceplayer
ai AILIZBOSSFROZEN			ALIZBOSSFROZEN LIZBOSSSTOPPED geth
ai AILIZBOSSSHRUNK			ALIZBOSSRUNNING LIZBOSSSHRUNKVELS fleeenemy
ai AILIZBOSSGROW			ALIZBOSSGROW LIZBOSSSTOPPED geth
ai AILIZBOSSJUMP			ALIZBOSSFLY LIZBOSSJUMPVEL geth getv
ai AILIZLAYMINE				ALIZBOSSSTAND LIZBOSSSTOPPED geth

state lizbosshitstate
	ifcount 9 ai AILIZBOSSWAIT
ends

state lizbosschargestate

	ifrnd 2
	{
		
		ifrnd 128
		sound CYBOSS_ROAM1
		else
		sound CYBOSS_ROAM2
		
	}
	ifcount 30
	{
		ifcansee 
			ifp palive
		{ 
			ai AILIZBOSSSHOOT 
			
			sound CYBOSS_ATTK 
			
		}
		ifcount 60 ai AILIZBOSSSEEK
	}

ends

state lizbossseekstate

	ifrnd 2
	{
		
		ifrnd 128
		sound CYBOSS_ROAM1
		else
		sound CYBOSS_ROAM2
		
	}
	ifcount 10
	ifcansee ifpdistl 6144
		ifp palive
	{
		ifrnd 8 
		{ 
			ai AILIZBOSSSHOOT 
			
			sound CYBOSS_ATTK 
		}
		else ifrnd 12 ai AILIZBOSSCHARGE
	}
	
	ifrnd 4 ifpdistg 6144 ai AILIZBOSSWAIT
	

ends

state lizbossshootstate

	ifaction ALIZBOSSSTARTSHOOT
	{
		ifactioncount 2
		{
			action ALIZBOSSSHOOT1
			sound CHAINGUN_FIRE
			shoot SHOTSPARK1
			ifspritepal 0 shoot SHOTSPARK1
			move LIZBOSSSTOPPED faceplayerslow
		}
	}
	ifaction ALIZBOSSSHOOT1
	{
		ifactioncount 1
		{
			
			action ALIZBOSSSHOOT2
			ifpdistg 10240 ifrnd 64
			{
				sound RPG_SHOOT
				eshoot RPG
				ifspritepal 0
				{
					seta[RETURN].xrepeat 40
					seta[RETURN].yrepeat 40
					seta[RETURN].extra 65
				}
				break
			}
			sound M4FIRE2
			shoot SHOTSPARK1
			shoot SHOTSPARK1
			ifspritepal 0 
			{
				shoot SHOTSPARK1
				shoot SHOTSPARK1
			}
		}
	}
	ifaction ALIZBOSSSHOOT2
	{
		ifactioncount 1
		{
			ifcount 60
			{
				ai AILIZBOSSSEEK
				break
			}
			action ALIZBOSSSHOOT1
			
			sound CHAINGUN_FIRE
		}
	}

ends

state lizbossjumpstate
	
	ifaction ALIZBOSSFLY
	{
		ifrnd 8 
		{
			
			sound RPG_SHOOT shoot MORTER 
		}
		
		// Here we calculate the remaining nonvertical distance to the player
		// based on saved coords and player distance when jump started
		set LIZBVAR lizjumpx
		set LIZBVAR2 lizjumpy
		sub LIZBVAR sprite[].x
		sub LIZBVAR2 sprite[].y
		getangle LIZBVAR3 LIZBVAR LIZBVAR2
		seta[].ang LIZBVAR3
		
		set LIZBVAR2 sprite[].x
		sub LIZBVAR2 lizjumpx
		mul LIZBVAR2 LIZBVAR2
		
		set LIZBVAR3 sprite[].y
		sub LIZBVAR3 lizjumpy
		mul LIZBVAR3 LIZBVAR3
		add LIZBVAR2 LIZBVAR3
		sqrt LIZBVAR2 LIZBVAR2
		// LIZBVAR2 now contains nonvertical distance to target
		
		set LIZBVAR lizjumpdist
		div LIZBVAR 2
		ifl LIZBVAR2 LIZBVAR // more than halfway to destination
		{
			// time to begin descent from jump
			action ALIZBOSSFALL
			globalsound CYBOSS_SWOOP
			move LIZBOSSFALLVEL geth
		}
		else
		ifcount 60 
		{
			// if it has been two full seconds in the air (count 60), it is time to 
			// begin descent regardless of distance to target
			action ALIZBOSSFALL
			globalsound CYBOSS_SWOOP
			move LIZBOSSFALLVEL geth
		}
	}
	
	ifaction ALIZBOSSFALL
	{
		iffloordistl 8
		{
			globalsound CYBOSS_LAND
			quake 20
			ifspritepal 0
			{
				debris SCRAP1 4
				debris SCRAP2 4
				debris SCRAP3 4
				debris SCRAP4 4
				debris SCRAP5 4
				hitradius 5120 35 50 70 1000
			}
			else hitradius 4096 20 30 40 50
			
			set LIZBVAR 0
			whilevarn LIZBVAR 48
			{
				// spawn a bunch of smoke sprites in the area for landing impact effect
				add LIZBVAR 1
				espawn SMALLSMOKE
				rand LIZBVAR2 12288
				sub LIZBVAR2 6144
				add LIZBVAR2 sprite[].x
				seta[RETURN].x LIZBVAR2
				rand LIZBVAR2 12288
				sub LIZBVAR2 6144
				add LIZBVAR2 sprite[].y
				seta[RETURN].y LIZBVAR2
			}
			
			sound CYBOSS_LAYMINE
			ai AILIZLAYMINE
		}
	}

ends

state lizbosswaitstate

	ifrnd 16
	ifcansee
		ifpdistg 6144
			ifpdistl 32768
				iffloordistl 8
{
	getplayer[].posx lizjumpx
	getplayer[].posy lizjumpy
	findplayer lizjumpdist
	ai AILIZBOSSJUMP
	globalsound CYBOSS_LAUNCH
}

ifcount 30 ai AILIZBOSSSEEK

ends

state lizlayminestate

ifaction ALIZBOSSSTAND
{
	ifactioncount 5
		action ALIZBOSSSQUAT
}
ifaction ALIZBOSSSQUAT
{
	ifactioncount 2
	{
		action ALIZSQUATDONE
		espawn LIZMINE
		seta[RETURN].xrepeat sprite[].xrepeat
		seta[RETURN].yrepeat sprite[].yrepeat
		seta[RETURN].pal sprite[].pal
	}
}
ifaction ALIZSQUATDONE
{
	ifactioncount 2
		ai AILIZBOSSCHARGE
}

ends

state lizbossgrowstate

	state genericgrowcode
	sizeto 255 192

ends

state lizbossshrunkstate

  ifcount SHRUNKDONECOUNT
    ai AILIZBOSSWAIT
  else
    ifcount SHRUNKCOUNT
	{
	  ifspritepal 0 { sizeto 96 80 sizeto 96 80 }
	  else
      sizeto 48 40
	}
  else
    state genericshrunkcode

ends

state lizbossfrozenstate

    ifcount THAWTIME
    {
      ai AILIZBOSSWAIT
      getlastpal
    }
    else
      ifcount FROZENDRIPTIME
    {
      ifactioncount 26
      {
        spawn WATERDRIP
        resetactioncount
      }
    }
    ifhitweapon
    {
      ifwasweapon FREEZEBLAST
      {
        strength 0
        break
      }
      addkills 1 
	 
      ifrnd 84
        spawn BLOODPOOL
	  lotsofglass 30
      state standard_jibs
      sound GLASS_BREAKING
      killit
    }
    ifp pfacing
      ifpdistl FROZENQUICKKICKDIST
        pkick
  
ends

useractor enemy LIZBOSS LIZBOSSSTRENGTH

fall
ifai 0
{
	sound CYBOSS_RECOG
	ai AILIZBOSSWAIT
	cstat 257
	ifspritepal 0 clipdist 164 else clipdist 96
	ifspritepal 0 sizeat 96 80 else 
	{ 
		sizeat 48 40
		strength LIZBOSSPALSTRENGTH
	}
}

ifai AILIZBOSSDYING
{
	strength 0
	
	ifaction ALIZBOSSDYING
	{
		ifhitweapon state random_wall_jibs
		ifactioncount 5
		{
			globalsound THUD
			cstat 0
			action ALIZBOSSLYINGDEAD
			break
		}
	}
	ifaction ALIZBOSSLYINGDEAD
	{
		ifhitweapon
		{
			ifwasweapon RPG
			{
				sound SQUISHED
				spawn BLOODPOOL
				state standard_jibs
				state standard_jibs
				debris SCRAP1 4
				debris SCRAP2 4
				debris SCRAP3 4
				debris SCRAP4 4
				debris SCRAP5 4
				debris SCRAP6 4
				killit
			}
			ifwasweapon RADIUSEXPLOSION
			{
				sound SQUISHED
				spawn BLOODPOOL
				state standard_jibs
				state standard_jibs
				debris SCRAP1 4
				debris SCRAP2 4
				debris SCRAP3 4
				debris SCRAP4 4
				debris SCRAP5 4
				debris SCRAP6 4
				killit
			}
		}
		ifcount RESPAWNACTORTIME
		ifrespawn
		{
		  addkills -1
		  spawn TRANSPORTERSTAR
		  cstat 257
		  strength PIGCOPSTRENGTH
		  ai AILIZBOSSWAIT
		  sound CYBOSS_RECOG
		}
	}
	
	break
}



ifai AILIZBOSSGROW { state lizbossgrowstate break }
ifai AILIZBOSSFROZEN { state lizbossfrozenstate break }
ifai AILIZBOSSSHRUNK state lizbossshrunkstate else
ifai AILIZBOSSHIT state lizbosshitstate else
ifai AILIZBOSSWAIT state lizbosswaitstate else
ifai AILIZBOSSJUMP state lizbossjumpstate else
ifai AILIZLAYMINE state lizlayminestate else
ifai AILIZBOSSCHARGE state lizbosschargestate else
ifai AILIZBOSSSHOOT state lizbossshootstate else
ifai AILIZBOSSSEEK state lizbossseekstate

ifai AILIZBOSSSHRUNK nullop
else
{
	ifspritepal 0
	{
		ifp palive
			ifpdistl 1280
		{
		  addphealth -1000
		  palfrom 63 63
		}
		// state squish_bot
                // commented out because state squish_bot is for causing damage to player's bot companion which 
                // only exists in the Alien Armageddon mod
	}
}
state monsterai ife monstatus -1 break
ifhitweapon
{
    ifwasweapon SHRINKSPARK
	{
		ifstrength 600
		{
		  sound ACTOR_SHRINKING
		  ai AILIZBOSSSHRUNK
		  break
		}
		else addstrength -60
	}
	else
        ifwasweapon GROWSPARK
          sound SPARKLESOUND
	state random_wall_jibs
	ifdead
	{
		ifwasweapon FREEZEBLAST
		{
		  sound NEWFREEZE
		  spritepal 1
		  ai AILIZBOSSFROZEN
		  strength 0
		  state freezeme
		  break
		}
		ifwasweapon GROWSPARK
		{
		  cstat 0
		  sound ACTOR_GROWING
		  ai AILIZBOSSGROW
		  break
		}
		addkills 1
	
		sound CYBOSS_DEATH
		
		ai AILIZBOSSDYING
		guts JIBS6 8
		ifspritepal 0 break
		ifwasweapon RPG
		{
			sound SQUISHED
			spawn BLOODPOOL
			state standard_jibs
			state standard_jibs
			debris SCRAP1 4
			debris SCRAP2 4
			debris SCRAP3 4
			debris SCRAP4 4
			debris SCRAP5 4
			debris SCRAP6 4
			killit
        }
		ifwasweapon RADIUSEXPLOSION
		{
			sound SQUISHED
			spawn BLOODPOOL
			state standard_jibs
			state standard_jibs
			debris SCRAP1 4
			debris SCRAP2 4
			debris SCRAP3 4
			debris SCRAP4 4
			debris SCRAP5 4
			debris SCRAP6 4
			killit
        }
		break
	}
	
	sound CYBOSS_PAIN
	
	ifrnd 2
		spawn BLOODPOOL
	ifai AILIZBOSSHIT nullop else
	ifai AILIZLAYMINE nullop else
	ifai AILIZBOSSJUMP nullop else
	ifai AILIZBOSSSHRUNK nullop else
	{
		ifrnd 16 
		 ai AILIZBOSSHIT 	
	}
}

enda


state mineexplode

	spawn EXPLOSION2
	ifl sprite[].xrepeat 20
	{
		
		sound PIPEBOMB_EXPLODE
		hitradius 1280 2 4 6 8
		
		killit
		break
	}
	sound PIPEBOMB_EXPLODE
	ifspritepal 0
	hitradius 4096 35 50 65 80
	else
	hitradius 3072 25 30 35 40
	killit

ends

move IFRAMES
move READY
move DETONATE
useractor notenemy LIZMINE 0

	fall
	ifmove 0
		move IFRAMES
		
	ifmove IFRAMES 
	{
		ifcount 30 move READY
		break
	}
	
	cstator 256
	ifhitweapon
		state mineexplode
	
	ifpdistl 1280
		state mineexplode
	
	ifmove READY
	ifpdistl 2048
	ifcansee 
	{
		move DETONATE
		sound LASERTRIP_ARMING
	}
	ifmove DETONATE
		ifcount 8
		state mineexplode
enda


I have also attached the art file that contains the needed sprites and a few others for anyone trying to get it to work. For sounds you will have to either download the archived mod (version 1.0) from Mod database or create your own sounds / dummy sounds.

EDIT: After posting this I noticed some calls to code that wasn't included because it's from the larger Alien Armageddon mod; hopefully that is all removed now.

EDIT2: The code may not compile since I made significant edits to it after pasting it in from the larger project that contains multiple code files (the edits were mainly to remove unnecessary complications such as a different type of explosion instead of EXPLOSION2). Hopefully someone who got this far would be able to understand any compiler errors and make simple additions/edits if they wanted to use the code. The main purpose of it though is to look at as an example of how to write an enemy's code, and for that the small omissions should not matter. I'm happy to answer questions.

Attached File(s)



This post has been edited by Danukem: 22 November 2023 - 02:27 AM

7

User is offline   Bruno 

#15

Thank you for the update of this topic that will help a lot!
0

User is offline   Danukem 

  • Duke Plus Developer

#16

View PostBruno, on 21 November 2023 - 07:37 AM, said:

Thank you for the update of this topic that will help a lot!


Perhaps it will; but see my EDIT2 above which I just added.
0

User is offline   eniojr 

#17

What is the code to create a new babe? In this case, after you interact with the babe, the babe shoot the player for some seconds, then return back to normal, like those killer girls of Shadow Warrior. Duke Nukem Armageddon has a babe like that, but I don't know how to create a new one like that.
0

User is offline   Danukem 

  • Duke Plus Developer

#18

View Posteniojr, on 24 November 2024 - 12:11 PM, said:

What is the code to create a new babe? In this case, after you interact with the babe, the babe shoot the player for some seconds, then return back to normal, like those killer girls of Shadow Warrior. Duke Nukem Armageddon has a babe like that, but I don't know how to create a new one like that.


So what you are saying is, you do know how to code an NPC babe that *doesn't* shoot, and the only thing you need help with is making her shoot for a few moments when interacting with her. I'l tell you what: post your code for the fully functioning babe NPC who doesn't shoot, and I will help you with the shooting part.

And you can't use "state femcode" from the vanilla game or whatever, because that state is a mess and the shooting part would mostly likely have to be inserted into it.
0

User is offline   eniojr 

#19

Here it is:

////////////////SWBABE1

define SWBABE1_NORMAL_STRENGTH   5 
define SWBABE1_DAMAGE_TO_PLAYER  -1
define SWBABE1_STAND_PICNUM 14052
define SWBABE1_ATTACK_PICNUM 14054
define ATTACK_DELAY 30 
define ATTACK_DURATION 90

action SWBABE1_STAND 2 1 1 1 30
action SWBABE1_ATTACK 2 1 1 1 90

ai SWBABE1_ATTACK SWBABE1_STAND

useractor notenemy SWBABE1 TOUGH SWBABE1_STAND SWBABE1_ATTACK

ifai NO           
{        
    strength SWBABE1_NORMAL_STRENGTH      
    sizeat 18 18      
    cstator 257         
    ai SWBABE1_STAND   
}

    ifp pfacing
    ifpdistl 1500
    {
        ifhitspace 
        {
            ifrnd 128
            ifcansee
            {
                soundonce SWBABE1
            }
            ifactor SWBABE1
            {
                ai SWBABE1_ATTACK
                action SWBABE1_ATTACK
                shoot SHOTSPARK1
                addphealth SWBABE1_DAMAGE_TO_PLAYER
                sound 6
                resetactioncount
            }
        }
    
    ifaction SWBABE1_ATTACK 
    {
        ifactioncount 128
        {
            action SWBABE1_STAND
            ai SWBABE1_STAND
            resetactioncount
        }
    }

ifhitweapon   
{  
    ifrnd 128  
    {  
        sound DUKE_HIT_STRIPPER1  
    }  
    else  
    {  
        sound DUKE_HIT_STRIPPER2  
    }
    
    ifdead   
    {  
        respawnhitag           
        state standard_jibs    
        state random_wall_jibs   
        spawn BLOODPOOL        
        money 5                
        spritepal 6            
        soundonce LADY_SCREAM  
        debris SCRAP3 18       
        add s_babekilled 1     
        killit                 
    }  
}
}   
enda 


The NPC is almost OK. It works in the game, don't crash the game. What I want is a behavior similar to that in Shadow Warrior babes that shoots at you. The player interact, talking to the babe (like Duke talk to babes). The babe then wait about 3 seconds to attack the player. The shooting time is 2 or 3 seconds and while attacking, the babe talk too. Then the babe returns to the idle behavior. The code shows that the NPC can shoot if you interact by pressing space (ifhitspace). I just want that in a different way, similar to killer girls in SW. The idle textures are 14052 and 14053. The attacking textures are 14054 and 14055. If you can, you should try 2 versions, one that shoots SHOTSPARK1 and other one that shoots FIRELASER. This is because there will be other NPC's like that. Most will shoot bullets at you, while others will shoot FIRELASER and COOLEXPLOSION1. I mostly know how to make NPC shoot projectiles, but everytime I do that, the projectile go in a wrong way, behind the NPC, instead of going in the direction of the player. It`s just an experiment. If I can get that right, I might be capable to start making new basic enemies or even walking inoffensive people and animals too. Such things give more life to the game.
0

User is offline   eniojr 

#20

There's something more. I made some breackable objects. But now I want to make wall textures to change when I hit them with a weapon or explosion. But I can't figure out how. I mean, to make a new wall texture that change to other texture I added with breaking sound and debris, like some textures of the original game, like SCREENBREAK6 (texture 268). I found something about damageeventtile in a mod but could't figure out. There are also no specific commands in GAME.CON to such behavior, only a definition of the texture in DEFS.CON, nothing more.

I managed to make a code with the help of Windsurf:

damageeventtile 11312

onevent EVENT_DAMAGEWALL
{
    switch wall[RETURN].picnum
    
    case 11312
        sound VENT_BUST 
        sound GLASS_BREAKING 
        lotsofglass 20 
        setwall[RETURN].picnum 11313
    break

    endswitch
}
endevent


I also included define RRCOLAMACHINE 11312 in DEFS.CON

I could make the code based on Alien Armageddon 4.0. But the only thing that worked was the glass sound and debris. The texture still don't change. There's something missing here!

This post has been edited by eniojr: 25 November 2024 - 09:26 PM

0

User is offline   eniojr 

#21

I finally got how to make a wall texture to break!

Here's the code:

define RRCOLAMACHINE 11312    

damageeventtile 11312        

onevent EVENT_DAMAGEWALL
{
    switch wall[RETURN].picnum
    
    case 11312              
        sound VENT_BUST 
        sound GLASS_BREAKING 
        lotsofglass 20 
        setwall[RETURN].picnum 11313    
    break

    endswitch
}
endevent


This post has been edited by eniojr: 25 November 2024 - 09:44 PM

0

User is offline   eniojr 

#22

One thing I forgot about the babe NPC... When Idle, I want it to make alternate sounds, such as LALALA1.VOC and LALALA2.VOC. It's because one of the NPC I will make will sing using 2 sounds. They should alternate and not play at the same time. The problem is that I still haven't figured out how, since months ago. Because I'm not familiar with timing codes. I don't even know if the timing code I included is right or not.

This post has been edited by eniojr: 25 November 2024 - 10:02 PM

0

User is offline   Danukem 

  • Duke Plus Developer

#23

This is a reply to the original request with the code you posted in it. Below is my code and some explanation.

////////////////SWBABE1

define SWBABE1 14052 // this had better be true...
define SWBABE1_NORMAL_STRENGTH   5 
define SWBABE1_DAMAGE_TO_PLAYER  -1
define SWBABE1_STAND_PICNUM 14052
define SWBABE1_ATTACK_PICNUM 14054

define ATTACK_DELAY 30 

action SWBABE1_STAND 0 1 1 1 10
action SWBABE1_ATTACK 1 2 1 1 4
move SWBABESTOPPED
// i'm assuming you have a 2 frame firing animation on 14053 and 14054

ai AISWBABE1_STAND SWBABE1_STAND SWBABESTOPPED faceplayer
ai AISWBABE1_ATTACK SWBABE1_STAND SWBABESTOPPED faceplayer

useractor notenemy SWBABE1 SWBABE1_NORMAL_STRENGTH SWBABE1_STAND

ifai NO
{            
	sizeat 18 18      
	cstator 257         
	ai AISWBABE1_STAND   
}

ifai SWBABE1_STAND 
{
	ifp pfacing
	ifpdistl 1500
	ifcansee
	ifhitspace 
	{
		soundonce SWBABE1
		ai AISWBABE1_ATTACK
	}
}
else
ifai AISWBABE1_ATTACK 
{
	ifaction SWBABE1_STAND
	{
		ifcount ATTACK_DELAY action SWBABE1_ATTACK
	}
	else ifaction SWBABE1_ATTACK
	{
		ifactioncount 1
		{
			sound 6
			shoot SHOTSPARK1
			addphealth SWBABE1_DAMAGE_TO_PLAYER
			resetactioncount
		}
		ifcount 120
			ai SWBABE1_STAND
	}	
}

ifhitweapon   
{  
    ifrnd 128   
        sound DUKE_HIT_STRIPPER1  
    else  
        sound DUKE_HIT_STRIPPER2  
    
    ifdead   
    {  
        respawnhitag           
        state standard_jibs    
        state random_wall_jibs   
        spawn BLOODPOOL        
        money 5                
        spritepal 6            
        soundonce LADY_SCREAM  
        debris SCRAP3 18       
        add s_babekilled 1     
        killit                 
    }  
}
 
enda 


That should be correct, but I had to make some assumptions because you didn't supply your .art file or say how the art was arranged. You also didn't say what the SWBABE1 sound is but I'm assuming it is dialogue (I know that sound 6 is chaingun firing). You used the same label for the actor and the sound, which means the tile defining the actor and the number of the sound are the same, which seems extremely unlikely. In general you would want those to have different labels even if by some bizarre coincidence they happened to be the same number. But since I don't know what the sound number actually is I just left it like that.

My assumption is that you have 3 tiles total, the idle standing tile on 14052, and then a 2-frame firing animation on 14053-14054. It was the only reasonable thing I could assume and the code is based on that. I have no way of testing the code because you didn't supply your art file, but it should work as you described you wanted it.

Based on the code you had, it seems like you do understand some things but you are pretty confused about how to define actions and AI labels, and also they were missing the move parameter entirely.
1

User is offline   eniojr 

#24

The compilation is OK, I could run the map without problems. There are 2 idle textures, not one, but that's just an animation I did using BAFed. The issue is that I can't interact with the NPC, but it's killable. Forgot to say that I'm doing that inside Duke Nukem Legacy 2.0 mod, because I'm trying to make a little addon for that, for personal use until Marcos approve that when everything's done and I already have talked with him about this, and at least for now it's ok for him (I also have included him as a friend in Moddb). Don't know if that changes anything in the code, since it's essentially Eduke32, probably from year 2023. But the texture numbers are correct. Yes, there are 2 attack frames as you assumed. The sound number is different from the texture, but I already defined the sound number in DEFS.CON, as well the texture numbers. The idle texture 14052 is called SWBABE1 and the attack texture 14054 is called SWBABE1FIRE.

The sound number defined in DEFS.CON for SWBABE1 is 430 and for SWBABE2 is 431. LALALA1 is 437 and LALALA2 is 438.

Could you, this time, make an alternation between 2 sounds when NPC is attacking and in idle? SWBABE1 and SWBABE2 when attacking and LALALA1 and LALALA2 when idle. The idea is that the NPC in idle will sing 2 audios each time. When attacking, each time will have a different speech, with 2 maximum speeches possible for the NPC when attacking.

This time I included a .7z file with tiles040.ART (with the NPC textures), 2 audio files for alternating sounds in idle (LALALA1.VOC and LALALA2.VOC) and 2 audio files for alternating speech when attacking (SWBABE1.VOC and SWBABE2.VOC). Also names.h is included and the DEFS.CON.

Now you can test the code for yourself. If you want you can use the textures and audio files for yourself, for test purposes and for more walkthrough in Duke3D codes here in the topic.

There are also other NPC's included in the texture file tiles40.ART, if you want to test for yourself different behaviors for each NPC.

Attached File(s)


0

User is offline   Danukem 

  • Duke Plus Developer

#25

View Posteniojr, on 26 November 2024 - 10:07 AM, said:

The issue is that I can't interact with the NPC, but it's killable.


I've given you code that should work, it's up to you to figure it out now. You can see that the first code initializes it to AISWBABE1_STAND, and then the code for that ai label is interactable. Also, I will probably remove these posts from this thread soon since it's intended as s general tutorial and you can find one of the other CON help threads or start a new one.
1

User is offline   eniojr 

#26

Ok, I figured now what was wrong and then worked. Thanks a lot!

Bit by bit, I'm learning more and more about coding. Thanks for the patience.

I still don't know much related to AI in enemies, but one day I will get there.
1

Share this topic:


Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic


All copyrights and trademarks not owned by Voidpoint, LLC are the sole property of their respective owners. Play Ion Fury! ;) © Voidpoint, LLC

Enter your sign in name and password


Sign in options