Go Back   EQEmulator Home > EQEmulator Forums > Archives > Archive::Development > Archive::Development

Archive::Development Archive area for Development's posts that were moved here after an inactivity period of 90 days.

Reply
 
Thread Tools Display Modes
  #1  
Old 03-25-2002, 10:35 AM
ScotchTape
Fire Beetle
 
Join Date: Mar 2002
Posts: 12
Default Working towards Animal Instincts in NPCs

This is a divergent view from eqlive. Purists should skip this thread. I'm hashing it out here because, I don't quite have access to the tools I need to do any development work. It'll be ultimately up to the base maintainers if they want to include this.Regardless, I plan on putting in a flag to turn such experiments on/off.

The goal is to put in place some instincts for the critters that walk about the face of norrath. For example, a hungry wolf will attack, while a full wolf may not. Secondly, I've always viewed a zone as a kind of fishbowl and I'd like to make my fish do stupid tricks.

The first part of this is going to introduce hunger code for NPCs. It is a rather primative instinct. Either you are hungry or you aren't. And there are thresholds, such as being stuffed full makes you cranky, and not eating enough makes you cranky, but eating just the right amount makes you (generally) healthy, wealthy, and wise.

I'm thinking a simple struct, a struct so I can easily expand it later, might look like
Code:
NPC
class
{
protected:
struct_AnimalInstincts* AnimalInstincts;
}

struct struct_AnimalInstincts
{
    sint8 Hunger;
};
-100 < Hunger < 100

With 0 being equilibrium.

Now if you're hungry (hunger < 0), then you need food. I'm thinking that if you give the item (check Consume_Struct), then you are hungry. Next, we need a command in (i think client.cpp) that processes the command #Hungry?

If an NPC is Hungry, it tells you as much. If not, then not.
(need pseudo code script ;)
Code:
//isnpc check and stuff
Message(0, "Hunger: %i", target->AnimalInstincts->Hunger);

Message(0, "I need no food.");
or
Message(0, "I need food. Feed me!");
Now we need to feed the fish. I'm thinking a fish is fed when four food items, nonstacked, are given to it. This has the effect of possibly raising the hunger of the npc by something like rand() % 2 == 1 ? 1 : 0. 'course, there will need to be bounds checking with max(100) and min(-100).

Now, over time the NPC will go hungry. So, their hunger should be increased (which actually decreases the Hunger value). At some point, this should probably work off the existing eat-food timer, but for testing I want all my NPCs to starve.

Which brings in the second command for client.cpp: #StrikeFamineInZone which causes all the npcs hunger to drop to -100.

This doesn't classify itself as an instinct quite yet, and I'm fearful of putting in any kind of silly check such as (if Hungry, go find food!) as all the NPCs will flock to the first available food source... rather, instead, I have two ideas I'm kicking around. The first goes back to the healthy, wealthy, and wise argument.

If an NPC is fed well, we'll say 0 < hunger < 50, then

a. their wisdom score goes up (perhaps skills too, but last I checked these were hardcoded at 252 with a big TODO flocked next to it).
b. their strength score goes up (or maybe minimum damage)
c. they get extra cash ( a few silver or something small)

Alternatively, I'd just like to tint the npcs a different color. However, I don't know if the client supports this.
Reply With Quote
  #2  
Old 03-25-2002, 10:38 AM
Malevolent
Hill Giant
 
Join Date: Mar 2002
Posts: 171
Default

Odd. Must have caught a cookie in my login there. Ooopsie.

I don't suppose someone could clear my ScotchTape account, could they?

--MV
Reply With Quote
  #3  
Old 03-26-2002, 02:49 AM
Malevolent
Hill Giant
 
Join Date: Mar 2002
Posts: 171
Default

Woot. 2.6 was released today! :-)

Aside from accepting gifts from players, the above was trivial to implement. So now comes necessarily phase 1+, then 2.

In phase 1+ I need to:
1. Move the above instinct core code into 2.6
2. get that working
3. Move on to phase 2

Phase::2

Being hungry is only part of it. Animals fall into three different categories. You either eat meat, eat vegies, or eat both. Eating meat means you have a tendency to eat N/PCs. Eating vegies makes you hungry and docile. Maybe if item population in the zone (item droppings) work, then sprinkling vegies about the area to have NPCs go after them. (Smells like I'm cooking part 2) If you eat both, then you're moody, but are prone to eat the N/PC. Alright, so omnivore might be a bit more complicated to do right now, so we'll just deal with the core two: carni and herbi -vores.

I'm going to add in the animal instinct struct a member possible values (0,-1,1) that designates whether I am a carnivore (0), herbivore (-1), or omnivore (1). So (1) isn't going to be used for a bit, but it will be used so a bool won't work.

Carnivore:

When a NPC "eats" a PC, it calls NPC::Eat() and increments by the level of the PC. If it is another NPC, then perhaps the same.

Now what I find interesting is that I'm going to try to aggro against NPCs. Cannibalistic goblins. Oh yeah! Potential problem: what happens if zone aggros against one another and the entire place goes nuts? So maybe only eat your fellow if you are below, say -50.

I don't think the attack code currently allows for NPCs to attack one another. I might have to modify this.

Herbivore:

Sprinkling items about the zone, such as vegies, is necessary for this to work. Once that is possible, sprinkle the items about the zone and have the various NPCs path to their location. Nothing fancy here, just slowly move the NPC in the direction of the item. e.g., set NPC eventual destination == item location. They only need one, and no two NPCs should get the same item (prevention of another problem).

Omnivore:

Does both. Can't do this without the first two, so looks like I'll save this until later.

That's a good start for the game plan tonight (hoping), if not, then sometime this week. :)

--MV
Reply With Quote
  #4  
Old 03-26-2002, 03:32 AM
Trumpcard
Demi-God
 
Join Date: Jan 2002
Location: Charlotte, NC
Posts: 2,614
Default

I love the idea !

I would set a true false value in the variables table though

AnimalAI 0/1 though..

Some people might not want their animals all following instinctual pattens. Plus, Im thinking that running the AI on all the zone animals might be a bit CPU intensive, especially in big zones like the karanas.

While you are emulating natural patterns..

You could even put sleep code in... If they are asleep, you have a random chance of wakening them if you come within a certain radius, but if you sneak by, you get an attack bonus, or you can just run by them without agro'ing. (sleep would lower their agro radius to 0 in essence).
Reply With Quote
  #5  
Old 03-26-2002, 04:59 AM
Hmm
Discordant
 
Join Date: Jan 2002
Posts: 276
Default

it should be 0 to 3.

zero no animial effect ( ie: rock golems )
one veggie eater
two meat eater
three both
__________________
Hmm...
Reply With Quote
  #6  
Old 03-26-2002, 06:27 AM
Baltar
Hill Giant
 
Join Date: Jan 2002
Posts: 185
Default

man this is an awesome idea! good work and good luck. Instincts opens the world to so many kewl possibilities.
Reply With Quote
  #7  
Old 03-26-2002, 06:56 AM
Malevolent
Hill Giant
 
Join Date: Mar 2002
Posts: 171
Default

Quote:
Originally Posted by Hmm
it should be 0 to 3.

zero no animial effect ( ie: rock golems )
one veggie eater
two meat eater
three both
Excellent point. Then maybe later rock golems may eat ..erm.. rocks? :-)

4 - rocks
Reply With Quote
  #8  
Old 03-26-2002, 10:35 AM
Baron Sprite's Avatar
Baron Sprite
Dragon
 
Join Date: Jan 2002
Posts: 708
Default

good idea would be to have npc animals eat each other too, following some kind of food chain, don't know if you said that but I missed it...
also: If an animal with rabies bites another one, it gets rabies... ect...
Reply With Quote
  #9  
Old 03-26-2002, 10:47 AM
Trumpcard
Demi-God
 
Join Date: Jan 2002
Location: Charlotte, NC
Posts: 2,614
Default

Make a new database field, food chain level, where a creature will attack anything else with a lower food chain number but ignore, stay away from anything with a higher one.

It might be fun watching a bunch of animals chasing each other around south karana, but it would lead to very erratic behavior as everything started running from one another and never stopped to eat.. It'd look like the playground at mcdonalds...
Reply With Quote
  #10  
Old 03-26-2002, 11:21 AM
Baron Sprite's Avatar
Baron Sprite
Dragon
 
Join Date: Jan 2002
Posts: 708
Default

well if something bigger wasn't hungry it could ignore it or if something was a jerk it would kill anything it saw that was lower on the food chain, IE a giant would smash a bunny into goo if it saw it or something to that effect.
Reply With Quote
  #11  
Old 03-26-2002, 01:39 PM
Hmm
Discordant
 
Join Date: Jan 2002
Posts: 276
Default

ahh thats very good idea!

maybe 2 variables.or a table.. if its veggie eater some plants is uneatable ( like high level plants like trents)

or meat eater ie: wolf dont try to eat bear.
__________________
Hmm...
Reply With Quote
  #12  
Old 03-26-2002, 01:41 PM
Hmm
Discordant
 
Join Date: Jan 2002
Posts: 276
Default

doh didnt see previous suggestion sorry heh
__________________
Hmm...
Reply With Quote
  #13  
Old 03-26-2002, 03:24 PM
hogger
Sarnak
 
Join Date: Jan 2002
Posts: 52
Default

Yeah, I've got no idea how you'd do it, but those hunting animals should eventually give up the chase too. Could you imagine trying to camp a spawn that won't stop chasing a ribbit that it can't catch...
Reply With Quote
  #14  
Old 03-26-2002, 04:38 PM
Malevolent
Hill Giant
 
Join Date: Mar 2002
Posts: 171
Default

I decided against using an integer to represent what kind of thing a mob is (meat/vegie, etc.) and so settled on using boolean on whether

1. do you eat meat T/F
2. do you eat vegies T/F

Which pretty much get the vast majority of cases I could think of and since they're in the struct, it wouldn't take much to expand it later.

There was also the issue of expressing whether a mob is a meat or is a vegie.

1. i am meat T/F
2. i am vegie T/F

Same kind of thing, as described above, is happening here.

before I sat down to code this, the integer idea seemed like the way to go. Then i started looking at the chaos I'd have to deal with and decided some boolean checks would get the job done and be easier to put in place. And, as a bonus, might conserve on resources more than a sint8 (or whatever).

If my theory holds true, these simpler pieces, when and once assembled, will create a nifty illusion. Still a little while to go though

todo/notes:

mobs have properties of animalinstincts, but not methods like npcs

if the player doesn't have a target selected, then the npc can't eat (so if a player unselects target before death, then npc can't feast on body)
::Starve() needs done (just reverse of Eat but w/o having to do -'s)
need to think about what kind of benefits should fall onto a mob for having good health. For example, maybe if you aren't hungry then your stun timer doesn't last as long. And if you are hungry, then it lasts longer.

Phase 3 will consist of a few things that I pushed aside in p2. Namely, messing around with players giving npcs items and having that translate to food for the npc. Note to self: OP_Click_Give.

Anyhow, here's the bulk of phase 2 (I expect to amend this later), which is more proof of concept groundwork:

Code:
npc.h 

//malevolent
struct strctAnimalInstincts
{
	sint8 Hunger;	// -100<x<100 : 0 equilibrium [threshold]
	
	bool Meat;	  //do you eat meat?
	bool Vegetables; //do you eat vegetables?
};

class:

	//*****************************************************************//
	//Animal instincts: Hunger

	void Eat(int HowMuchFood); //malevolent: eat
	void Starve(int FastAmount);//malevolent: start
	int GetHungerStatus() { return AnimalInstincts->Hunger;}
	bool IsHungry(); //malevolent: checks to see if the npc is hungry


	//*****************************************************************//

Protected:
	//Malevolent: Animal Instincts
	strctAnimalInstincts* AnimalInstincts;


npc.cpp


//malevolent:: is the critter hungry? returns the int value representing their hunger
bool NPC::IsHungry()
{
	if (this->AnimalInstincts->Hunger >= 0) 
		return false;
		else
		return true;
}

//This should fire after npc has eaten
void NPC::Eat(int amount)
{
	strctAnimalInstincts* instincts=new strctAnimalInstincts;	
	instincts->Hunger=this->AnimalInstincts->Hunger + amount;

	//bounds checks

	//max
	if (instincts->Hunger >100)
		instincts->Hunger =100;

	//min
	if (instincts->Hunger <-100)
		instincts->Hunger =-100;

	//now assign
	this->AnimalInstincts=instincts;

}

mob.h

public:
	//*****************************************
	//malevolent
	bool IsMeat(){return foodtype->IsMeat;}
	bool IsVegie(){return foodtype->IsVegie;}
	//*******************************************

protected:
	//malevolent:
	FoodType* foodtype; //what kind of food am i? 

//Malevolent animal instincts
struct FoodType {
	bool IsMeat;	//is a meat-based mob
	bool IsVegie;	//is a vegie-based mob
};

mob.cpp

in constructor:
	//malevolent
	FoodType* ft = new FoodType;
	ft->IsMeat=true;
	ft->IsVegie=false;
	this->foodtype=ft;

client.cpp

		//Malevolent
		//hunger look up on target NPC
		else if (strcasecmp(sep.arg[0], "#Hungry") == 0)
		{
			
			if (target != 0 && target->IsNPC())
			{
				Message(0,"Asking target if they are hungry..");

				switch(target->CastToNPC()->IsHungry())
				{
				case false: 
					Message(0,"Hungry!");
					break;
				case true:
					Message(0,"Not hungry.");
					break;
				default:
					Message(0, "Does not eat.");
					break;
				}

				
			}
		}
		//Malevolent
		//ForceFeed the NPC
		else if (strcasecmp(sep.arg[0], "#ForceFeed") == 0)
		{
			
			if (target != 0 && target->IsNPC())
			{
				target->CastToNPC()->Eat(atoi(sep.arg[1]));
				Message(0,"Fed: %i",atoi(sep.arg[1]));				
			}
		}
		//Malevolent
		//Lookup the NPC's instinct values
		else if (strcasecmp(sep.arg[0], "#HungerThreshold") == 0)
		{
			
			if (target != 0 && target->IsNPC())
			{
				
				Message(0,"Hunger Value: %i",target->CastToNPC()->GetHungerStatus());				
			}
		}

		//Malevolent
		//Check to see if the npc can eat the user
		else if (strcasecmp(sep.arg[0], "#CanYouEatMe") == 0)
		{
			if (target !=0 && (target->IsNPC() || target->IsMob()) )
			{
				if(target->CastToNPC()->CanIEatYou(this->CastToMob()))
				{
					Message(0, "Yum, yum! I can eat you..");

					if(target->CastToNPC()->IsHungry())
						Message(0, "I'm going to eat you!");
					else
						Message(0, "I'm fine and full. I won't eat you.");

				}
				
			}
			 else  
			{
				Message(0, "You really think that such a thing could eat?");
			}
		
			 
		}

		//Malevolent
		//Check on whether they are meat or vegie
		else if (strcasecmp(sep.arg[0], "#voretype") == 0)
		{
			if (target !=0 && (target->IsNPC() || target->IsMob()) )
			{
				if(target->IsMeat())
					Message(0,"I'm meat.");
				else
					Message(0,"I'm not meat. Move along.");

				if(target->IsVegie())
					Message(0,"I'm vegie.");			
				else
					Message(0,"I'm not a vegie. You really think I'm green?");
			}
		
			 
		}

		//Malevolent
		//Check to see if the npc can eat the user
		else if (strcasecmp(sep.arg[0], "#EatMe") == 0)
		{
			if (target !=0 && (target->IsNPC()))
			{
				if(target->CastToNPC()->CanIEatYou(this->CastToMob()))
				{
					Message(0, "mmmm..foood");

					if(target->CastToNPC()->IsHungry())
					{
						Message(0, "I'm going to eat you!");
						target->CastToNPC()->AddToHateList(this,100); //be sure to *stick* to the target
						target->CastToNPC()->Attack(this); //attack 'm!
					}
					else
					{
						Message(0, "I'm fine and full. I won't eat you."); 
					}

				} else
				{
					Message(0, "%s says, \"I cannot eat you. You sure people eat your kind?.\"",target->GetName());
				}
				
			}
			 else  
			{
				Message(0, "You really think that such a thing could eat?");
			}
		
			 
		}

		//malevolent
		//using sic code, but adds if npc is hungry and if it can eat the target
		//todo add a method to npc with a sicifhungry() signature
		else if (strcasecmp(sep.arg[0], "#sicifhungry") == 0) 
		{
			if (target && target->IsNPC() && sep.arg[1] != 0) 
			{
				Mob* sictar = entity_list.GetMob(sep.arg[1]);
				
				if (sictar) 
				{
					if(target->CastToNPC()->CanIEatYou(this->CastToMob()))
					{
						Message(0, "mmmm..foood");

						if(target->CastToNPC()->IsHungry())
						{
							Message(0, "I'm going to eat you!");
							target->CastToNPC()->AddToHateList(sictar,100); //be sure to *stick* to the target
							target->CastToNPC()->Attack(this); //attack 'm!
						}
						else
						{
							Message(0, "I'm full. I won't eat."); 
						}
					}
				}
				else 
				{
					Message(0, "Error: %s not found", sep.arg[1]);
				}
			}
			else 
			{
				Message(0, "Usage: (needs NPC targeted) #sic targetname");
			}
		}



attack.cpp
::death
at end of the tunnel

	//malevolent
	if (other->IsNPC())
		other->CastToNPC()->Eat(pp.level);
--MV
Reply With Quote
  #15  
Old 03-26-2002, 04:45 PM
Malevolent
Hill Giant
 
Join Date: Mar 2002
Posts: 171
Default

Quote:
Originally Posted by hogger
Yeah, I've got no idea how you'd do it, but those hunting animals should eventually give up the chase too. Could you imagine trying to camp a spawn that won't stop chasing a ribbit that it can't catch...
I'm assuming that the attack code (which is my effective means of eating something) has a combat timer. I remember seeing a timer or two in there. If I'm wrong, then it'd just be a matter of taking the hate code and slowly decreasing it over time. I think.

hrm. I assumed that such a check was already in there. However, looking quickly through npc.cpp, suggests that I might be wrong. Blimey. I'll have to take a peek at that tomorrow.

--MV
Reply With Quote
Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

   

All times are GMT -4. The time now is 07:27 AM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3