Thank you TB Jokers, this saved me a lot of time and thought. I have made it work with my mod built over Ineisis:
[img]
[/img]
AI.es that goes in the client
Code:
class AI : Game::Chr
{
Int ID; //ID so that we can identify which the server wants us to use
Vec OldLocation; //Old target location
Str Name; // Name-
Flt scale =2.0;
//Memc<AI> ai;
void AI::spawn(Str name, Str obj, Int id, Vec pos){
Game::ObjParams obje; obje.load(obj); obje.scale(true, scale);
super::create(obje); //Create the object.
Name = name; //Add specifics read from server.
ID = id;
Flt y = Game::World.hmHeight(pos.xz(),true)+0.6f;
T.pos(pos+Vec(0,y,0)); //Add Y so that it doesn't spawn just under the map.
ctrl.actor.group(AG_AI);//Set actor group to 1 so that we can disable collision (pushing of monsters)
}
bool AI::update(){
if(super::update())
{
if( OldLocation!=0 && (T.pos() - OldLocation).length() < 2.0)T.actionBreak();
return true;
}
return false;
}
void AI::Move(Vec pos){
if(OldLocation!=0 && (T.pos() - OldLocation).length() > 3.0/*T.pos() != OldLocation*/)T.pos(OldLocation); // If old location is not already reached, set the position instantly there.
Flt y = Game::World.hmHeight(pos.xz(),true)+0.6f; //Get height of target location
T.actionMoveDir(Vec(pos.x, y, pos.z) - T.pos());//Move to locationPlr.pos() - T.pos()
//T.pos(Vec(pos.x, y, pos.z));
// CM.New(S+"OldPOs "+OldLocation);
//CM.New(S+"Playerpos "+Plr.pos());
//CM.New(S+"Movpos "+(Plr.pos() - T.pos()));
OldLocation = pos+Vec(0,y,0);//Set previous Location target so that we can check when receiving next time.
// CM.New(S+"Newoldpos "+OldLocation);
}
void AI::drawName(){
Vec2 NamePos = PosToScreen(pos()+Vec(0,T.ctrl.height(),0)); // Position of monster + it's height.
if(Dist(pos(), Plr.pos())<D.viewRange()/3){ // if distance of /3
if(visible())D.text(NamePos, S+Name+ID);
}
}
void AI::Action(Byte b ){
}
bool visible() {return Frustum(mesh->box, Matrix(scale, pos()));}
}
Memc<AI> ai;
AI.es that goes in the server:
Code:
class ArtificialIntelligence
{
/* void createAI(Int, Str, Str, Vec, Flt); //Create
bool update ( ); //Update loop
void spawn (Int);
bool foundtarget(Int);
void sendMovement();
void sendAction (); */
ArtificialIntelligence(){MovementTimer = RandomF(2,6); ActionTimer = RandomF(10,15);} //Set timers at construction
//private:
Memc<Client*> active_clients; //Active list of clients. The clients that has this monster spawned.
Str Creature_Name;
Vec Position ;
Str Object ; //Object path.
Int Creature_ID ;
Flt MovementTimer;
Flt ActionTimer ;
Ball RadiusBall ;
::Client& client(Int i) {return (::Client&)Server.clients.lockedData(i);}
//Memc<ArtificialIntelligence> ai;
void ArtificialIntelligence::createAI(Int id, Str name, Str object, Vec pos, Flt radius){
Creature_ID = id; //Set creature ID
Creature_Name = name; //Set Creature Name
Object = object; //Object path, example "Obj/Chr/Skeleton/0.obj"
Position = pos; //Start position
RadiusBall.r = radius; //Radius it can move in each move command.
}
bool ArtificialIntelligence::update(){
REPA(active_clients)
{
if(!active_clients[i].inGame())active_clients.remove(i); // need to remove clients that have exited game/went back to login screen
}
REPA(Server.clients){
if(client(i).inGame()/*state == iGame*/)if(Dist(Position,client(i).pos())<50)foundtarget(i); // go to state of processing if you should spawn a monster for this or if it's already spawned
}
MovementTimer -= Time.d();
ActionTimer -= Time.d();
if(MovementTimer <= 0)sendMovement();
//if(ActionTimer <= 0)sendAction();
return true;
}
void ArtificialIntelligence::spawn(Int i){
File f;
f.writeMem().putByte(CS_AI_SPAWN);
f.putStr(Creature_Name).
putStr(Object ).
cmpIntV(Creature_ID ).
cmpFlt3cm(Position.x).
cmpFlt3cm(Position.z);
f.pos(0);
client(i).connection.dataFull(f,f.left());
active_clients.New() = &client(i); //Directly after spawning the monster set the client into the monsters active list.
}
bool ArtificialIntelligence::foundtarget(Int a){
Int temp = 0;
REPA(active_clients){
if(active_clients[i] ==&client(a)){return false;} //If client is in monster list directly return false so that it is not spawned again.
if(active_clients[i] !=&client(a)){temp++;}
}
if(!active_clients.elms()){ //If there are no Active clients, add the first one
spawn(a);
return true;
}
if((temp-active_clients.elms())==0){ //If the client wasn't found in the active list spawn it.
spawn(a);
return true;
}
return false;
}
void ArtificialIntelligence::sendMovement(){
//tryagain:;
Vec test = Position +=Random(RadiusBall,true); //Test,
/*if(map.checkpos(test))*/{ //Map checking will be added later on
MovementTimer = RandomF(2,6); //Reset timer
File f;
f.writeMem().putByte(CS_AI_MOVE);
f.cmpIntV(Creature_ID ).
cmpFlt3cm(Position.x).
cmpFlt3cm(Position.z);
REPA(active_clients){f.pos(0); active_clients[i]->connection.dataFull(f,f.left(),true);} //Send movement to all active clients.
}/*else goto tryagain; */ //If position failed and it's on an invalid place, then start over to try another position
}
void ArtificialIntelligence::sendAction(){
File f;
f.writeMem().putByte(CS_AI_ACTION);
f.cmpIntV(Creature_ID ).putByte(Random(0,2)); //Put random action 0-2
REPA(active_clients){f.pos(0); active_clients[i]->connection.dataFull(f,f.left(),true);} //Send action to all clients.
}
}
Memc<ArtificialIntelligence> ai;
This goes in your Game.es where you handle messages coming from the server:
Code:
case CS_MOVE_PLAYER:
{
SockAddr addr; Vec positiontomoveto; // sockaddrnotreally needed
ClientRecvMovePlayer(Server.data, addr, positiontomoveto); positiontomoveto.y += 5.0; if(Plr.isneph)positiontomoveto.y += 15.0;
Plr.pos(positiontomoveto);
} break;
case CS_AI_SPAWN:
{
CM.New("Recieved CS_AI_SPAWN");
//File f = Server.data;
Str name = Server.data.getStr(); Str obj = Server.data.getStr(); Int id = Server.data.decIntV(); Flt x = Server.data.decFlt3cm(); Flt z = Server.data.decFlt3cm();ai.New().spawn(name,obj,id,Vec(x,0,z));
} break;
case CS_AI_MOVE:
{
CM.New("Recieved CS_AI_MOVE");
Int id = Server.data.decIntV(); Flt x = Server.data.decFlt3cm(); Flt z = Server.data.decFlt3cm(); Int ith; REPA(ai)if(ai[i].ID == id)ith =i; ai[ith].Move(Vec(x,0,z));
} break;
case CS_AI_ACTION:
{
//CM.New("Recieved CS_AI_ACTION");
} break;
Put this right after you call if(Plr)Plr.update(); on the client
Code:
FREPAO(ai) .update();
Put this after REPA(NeighborClients)NeighborClients.lockedData(i).drawText(); on the client:
Code:
FREPAO(ai).drawName();
In your RenderGame() function on the client make sure oyu have this:
Code:
case RM_PREPARE: if(Plr)Plr.drawPrepare(); REPA(NeighborClients)NeighborClients.lockedData(i).drawPrepare(); FREPAO(ai).drawPrepare(); break;
Put this in Main.es on the server Init() function:
Code:
REP(20)ai.New().createAI(i,"Warrior","Obj/Chr/Warrior/0.obj",Vec(RandomF(0,20),RandomF(0,20),RandomF(0,20)),10);
add these enums to your commands.es
Code:
CS_AI_SPAWN ,
CS_AI_MOVE ,
CS_AI_ACTION ,
Also I have removed the ignoring physics collisions between Players/AI see my work around here:
http://www.esenthel.com/community/printt...p?tid=3594