Here's the new orders menu code to go with the new bandmember AI
nothing fancy, it shows a menu, "Issue orders to..."
then a second menu "Orders..."
it then sets the orders state or appropriate variable such as sneak_state.
one common game design pattern seen in this code:
populating a menu by iterating though one or more lists and selecting some, but not all the entries, based on some criteria.
and then, once you get the menu pick, you iterate through the lists again to figure out which one it was.
another classic game design pattern:
B2_BM_badguys_closest2loc()
this is the classic best match search algo used in many places and many ways in many games.
you iterate through a list, finding the entry with the best value of some sort, such as its closest, does the most damage, etc.
you start by setting the best found value to some very bad value, and the best found entry to none. as you go though the list, if the current entry's value is better, it becomes the new best found value, and the entry becomes the new best found entry. when you're done, you return the best found entry, and perhaps the best found value as well, such as closest target, and its range.
This code also includes a simple quick and dirty raypick routine. It starts at the eye point, and uses a unit vector in the lookat direction. it repeatedly adds the unit vector to the point, then checks for intersection with the ground: point.y <= heightmap_of(point.x,point.z), or range > 200 (in case they aren't aiming at the ground at all!). If it gets an intersection with the ground, it returns the x,z location of the intersection.
You'll notice that in both this code and the AI code, many things need to be done twice, once for PCs, IE bandmembers (cm[]), and once for npcs/monsters (animal[]). Caveman has multiple PCs, like a household in The Sims. The player can tab between them at any time to manually control any bandmember they want. Other bandmembers are controlled by the new AI. So the game has two basic types of entities: bandmembers, and everything else. and it therefore has two entity lists, and more or less duplicate code for each of the two basic type of entites. A C-E approach that used structs for the components common to both could use a single set of code to operate on the structs, irreguardless of whether they belonged to a bandmember or something else. The game uses this approach with requirements structs used by actions, objects, and skills, and with inventory structs used by both bandmembers and everyone else.
// ---------------------------- NEW ORDERS API --------------------------------------------- // returns:// *who = -1 on cancel, is_BM is undefined.// *who = -2 for everyone, is_BM is undefined.// else returns who and is_BMvoid B2_order_who_menu(int cm1,int *who,int *is_BM){// n is npc #. c is number of menu options. b is who to issue orders to. a is loop counter char s[100]; int n,a,b,c; Znewmenu("Issue orders to..."); Zaddmenu("Everyone"); c=1; for (a=0; a 300) { continue; } Zaddmenu(cm[a].name); c++; } for (a=0; a 300) { continue; } if (animal[a].state == WARRIOR) { strcpy_s(s,100,"Warrior "); n=animal[a].butchered; strcat_s(s,100,npc[n].name); Zaddmenu(s); c++; } else if (animal[a].state == COMPANION) { strcpy_s(s,100,"Companion "); n=animal[a].butchered; strcat_s(s,100,npc[n].name); Zaddmenu(s); c++; } else if (animal[a].state == TAMED) { strcpy_s(s,100,"Pet "); strcat_s(s,100,animaltype[animal[a].type].name); Zaddmenu(s); c++; } } Zaddmenu("Cancel"); c++; b=menu(c); if (b == c) {// cancel *who=-1; return; } if (b == 1) {// everyone *who=-2; return; } c=1; for (a=0; a 300) { continue; } c++; if (c == b) { *who=a; *is_BM=1; return; } } for (a=0; a 300) { continue; } if (animal[a].state == WARRIOR) { c++; if (c == b) { *who=a; *is_BM=0; return; } } else if (animal[a].state == COMPANION) { c++; if (c == b) { *who=a; *is_BM=0; return; } } else if (animal[a].state == TAMED) { c++; if (c == b) { *who=a; *is_BM=0; return; } } }// should never get here - return cancel just in case *who=-1; } int B2_orders_menu(){Znewmenu("Orders...");Zaddmenu("Wait here");Zaddmenu("Follow...");Zaddmenu("Goto...");Zaddmenu("Target...");Zaddmenu("Attack");Zaddmenu("Sneak");Zaddmenu("Stop sneaking");Zaddmenu("Maintain distance");Zaddmenu("Run away!");Zaddmenu("As you were");Zaddmenu("Cancel");return(menu(11));} // returns who = -1 on cancel, else returns who and is_BMvoid B2_follow_who_menu(int cm1,int *who,int *is_BM){// c is number of menu options. b is menu result. a is loop counter. n is npc #.char s[100];int a,b,c,n;Znewmenu("Follow...");c=0;for (a=0; a 300) { continue; } Zaddmenu(cm[a].name); c++; }for (a=0; a 300) { continue; } if (animal[a].state == WARRIOR) { strcpy_s(s,100,"Warrior "); n=animal[a].butchered; strcat_s(s,100,npc[n].name); Zaddmenu(s); c++; } else if (animal[a].state == COMPANION) { strcpy_s(s,100,"Companion "); n=animal[a].butchered; strcat_s(s,100,npc[n].name); Zaddmenu(s); c++; } else if (animal[a].state == TAMED) { strcpy_s(s,100,"Pet "); strcat_s(s,100,animaltype[animal[a].type].name); Zaddmenu(s); c++; } }Zaddmenu("Cancel");c++;b=menu(c);if (b == c) {// cancel *who=-1; return; }c=0;for (a=0; a 300) { continue; } c++; if (c == b) { *who=a; *is_BM=1; return; } }for (a=0; a 300) { continue; } if (animal[a].state == WARRIOR) { c++; if (c == b) { *who=a; *is_BM=0; return; } } else if (animal[a].state == COMPANION) { c++; if (c == b) { *who=a; *is_BM=0; return; } } else if (animal[a].state == TAMED) { c++; if (c == b) { *who=a; *is_BM=0; return; } } }// should never get here, return cancel just in case*who=-1;} void B2_BM_orders_follow(int giver,int cm1){int who,is_BM;B2_follow_who_menu(giver,&who,&is_BM);if (who == -1) {// cancel return; }cm[cm1].orders=B2_ORDERS_FOLLOW;cm[cm1].ordersdata[0]=who;cm[cm1].ordersdata[1]=is_BM;} void B2_animal_orders_follow(int giver,int cm1){// cm1 is the animal #int who,is_BM;B2_follow_who_menu(giver,&who,&is_BM);if (who == -1) {// cancel return; }animal[cm1].orders=B2_ORDERS_FOLLOW;animal[cm1].ordersdata[0]=who;animal[cm1].ordersdata[1]=is_BM;} void B2_cast_ray(int cm1,int *success,location *L){// casts ray from eyepoint of cm1, if ray goes past 200, success=0, else success=1 and sets L.x,L.z// point=eyepoint of cm1// dir=fwd dir (unit vector) of cm1int quit;D3DXVECTOR3 point;D3DXVECTOR4 dir;point.x=0.0f;point.y=0.0f;point.z=1.0f;Mstart();MrotateRADS(0,cm[cm1].xr);MrotateRADS(1,cm[cm1].yr);D3DXVec3Transform(&dir,&point,&Mmat);point.x=cm[cm1].x;point.z=cm[cm1].z;point.y=heightmap(cm[cm1].mx,cm[cm1].mz,cm[cm1].x,cm[cm1].z);if (cm[cm1].sneak_state == 0) { point.y+=5.0f; }else { point.y+=3.0f; }quit=0;while (!quit) { if (BBdist((int)cm[cm1].x,(int)cm[cm1].z,(int)point.x,(int)point.z) > 200) { *success=0; quit=1; } else if (point.y <= heightmap(cm[cm1].mx,cm[cm1].mz,point.x,point.z)) { *success=1; L->x=point.x; L->z=point.z; quit=1; } else { point.x+=dir.x; point.y+=dir.y; point.z+=dir.z; } }} /*do { drawscreen if L-click cast ray if ray > 200 continue L->x=ray.x; L->z=ray.z; return; while 1 */ void B2_raypick_loc(int cm1,char *s,location *L){ int quit,success,x,y,b; quit=0; while (!quit) { drawscreen(0); Zbeginsprite(); tx(10,10,s); Zendsprite(); showscene(); B2_mouse_turn_camera(cm1); Zdomessages(); getmouse(&x,&y,&b); if (b != 1) { continue; } B2_cast_ray(cm1,&success,L); if (success == 1) { quit=1; } } nobutton(); } int B2_BM_badguy_closest2loc(location *L){int a,best,bestrng,rng;best=-1;bestrng=100000;for (a=0; ax,(int)L->z); if (rng < bestrng) { bestrng=rng; best=a; } }return(best);} int B2_raypick_tgt(int cm1){location L;int tgt;B2_raypick_loc(cm1,"Select target...",&L);tgt=B2_BM_badguy_closest2loc(&L);return(tgt);} void B2_BM_orders_goto(int giver,int cm1){location L;B2_raypick_loc(giver,"Go to...",&L);cm[cm1].orders=B2_ORDERS_GOTO;cm[cm1].ordersdata[0]=(int)L.x;cm[cm1].ordersdata[1]=(int)L.z;} void B2_animal_orders_goto(int giver,int cm1){// cm1 is the animal #location L;B2_raypick_loc(giver,"Go to...",&L);animal[cm1].orders=B2_ORDERS_GOTO;animal[cm1].ordersdata[0]=(int)L.x;animal[cm1].ordersdata[1]=(int)L.z;} void B2_BM_orders_target(int giver,int cm1){int tgt;tgt=B2_raypick_tgt(giver);cm[cm1].orders_tgt=tgt;cm[cm1].has_orders_tgt=1;} void B2_animal_orders_target(int giver,int cm1){// cm1 is the animal #int tgt;tgt=B2_raypick_tgt(giver);animal[cm1].orders_tgt=tgt;animal[cm1].has_orders_tgt=1;} void B2_issue_BM_orders(int giver,int cm1,int orders){switch (orders) { case 1:// wait here cm[cm1].orders=B2_ORDERS_GOTO; cm[cm1].ordersdata[0]=(int)cm[cm1].x; cm[cm1].ordersdata[1]=(int)cm[cm1].z; break; case 2:// follow... B2_BM_orders_follow(giver,cm1); break; case 3:// goto... B2_BM_orders_goto(giver,cm1); break; case 4:// target... B2_BM_orders_target(giver,cm1); break; case 5:// attack! cm[cm1].orders=B2_ORDERS_ATTACK; break; case 6:// sneak if (cm[cm1].sneak_state == 0) { cm[cm1].sneak_state=1; } break; case 7:// stop sneak cm[cm1].sneak_state=0; break; case 8:// maintain dist cm[cm1].orders=B2_ORDERS_MAINTAIN_DIST; break; case 9:// run away! cm[cm1].orders=B2_ORDERS_FLEE; break; case 10:// as you were - clears orders cm[cm1].orders=B2_ORDERS_NONE; break; }} void B2_issue_animal_orders(int giver,int cm1,int orders){// cm1 is the animal #switch (orders) { case 1:// wait here animal[cm1].orders=B2_ORDERS_GOTO; animal[cm1].ordersdata[0]=(int)cm[cm1].x; animal[cm1].ordersdata[1]=(int)cm[cm1].z; break; case 2:// follow... B2_animal_orders_follow(giver,cm1); break; case 3:// goto... B2_animal_orders_goto(giver,cm1); break; case 4:// target... B2_animal_orders_target(giver,cm1); break; case 5:// attack! animal[cm1].orders=B2_ORDERS_ATTACK; break; case 6:// sneak if (animal[cm1].sneak_state == 0) { animal[cm1].sneak_state=1; } break; case 7:// stop sneak animal[cm1].sneak_state=0; break; case 8:// maintain dist animal[cm1].orders=B2_ORDERS_MAINTAIN_DIST; break; case 9:// run away! animal[cm1].orders=B2_ORDERS_FLEE; break; case 10:// as you were - clears orders animal[cm1].orders=B2_ORDERS_NONE; break; }} void B2_order_everyone(int cm1,int orders){// a is loop counterint a;for (a=0; a 300) { continue; } B2_issue_BM_orders(cm1,a,orders); }for (a=0; a 300) { continue; } if (animal[a].state == WARRIOR) { B2_issue_animal_orders(cm1,a,orders); } else if (animal[a].state == COMPANION) { B2_issue_animal_orders(cm1,a,orders); } else if (animal[a].state == TAMED) { B2_issue_animal_orders(cm1,a,orders); } }} void B2_order_single(int cm1,int who,int is_BM,int orders){if (is_BM) { B2_issue_BM_orders(cm1,who,orders); }else { B2_issue_animal_orders(cm1,who,orders); }} void B2_issue_orders(int cm1){int who,orders,is_BM;B2_order_who_menu(cm1,&who,&is_BM);if (who == -1) {// cancel return; }orders=B2_orders_menu();if (orders == 11) {// cancel return; }if (who == -2) { B2_order_everyone(cm1,orders); }else { B2_order_single(cm1,who,is_BM,orders); }}