home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / games / volume13 / dominion / part22 / npclib.c < prev   
C/C++ Source or Header  |  1992-02-11  |  18KB  |  541 lines

  1.    /* nplib.c -- modules involved in NPC movemaking */
  2.  
  3. /*
  4.  * Copyright (C) 1990 Free Software Foundation, Inc.
  5.  * Written by the dominion project.
  6.  *
  7.  * This file is part of dominion.
  8.  *
  9.  * dominion is free software; you can redistribute it and/or
  10.  * modify it under the terms of the GNU General Public License as published
  11.  * by the Free Software Foundation; either version 1, or (at your option)
  12.  * any later version.
  13.  *
  14.  * This software is distributed in the hope that it will be useful,
  15.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  * GNU General Public License for more details.
  18.  *
  19.  * You should have received a copy of the GNU General Public License
  20.  * along with this software; see the file COPYING.  If not, write to
  21.  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  */
  23. #ifdef SYSV
  24. # include <string.h>
  25. #else
  26. # include <strings.h>
  27. #endif /* SYSV */
  28.  
  29. #include "dominion.h"
  30. #include "misc.h"
  31. #include "army.h"
  32. #include <math.h>
  33. #include <curses.h>
  34. #include <stdio.h>
  35. #include <ctype.h>
  36. #include <signal.h>
  37.  
  38. #define CUTOFF1 -30        /* army bonus for draft cutoff 1 (npc_draft) */
  39. #define CUTOFF2 0        /* army bonus for draft cutoff 2 (npc_draft) */
  40.  
  41. extern Sworld world;
  42. extern Suser user;
  43. extern struct s_desig_map desig_map[];
  44. extern int (*wrapx)(), (*wrapy)();
  45. extern int debug;
  46. extern struct army_type *army_types;
  47. extern struct spirit_type *spirit_types;
  48.  
  49. extern float npc_food_need,npc_metal_need,npc_jewel_need,npc_money_need;
  50. extern int opt_army_size,atwar,npc_specific;
  51.  
  52. /*--------------------find_visible_sectors()-------------------------------
  53.    NOTE: the following routine copied from user.c to avoid fiddling with
  54.    the Makefile and increasing the size of dom_update.  One difference,
  55.    viewall is not used. 
  56.  
  57.    this routine goes through the entire map and figures out
  58.    which sectors are visible by the user.
  59. ---------------------------------------------------------------------------*/
  60. /*
  61. find_visible_sectors(visible_sectors)
  62.      int **visible_sectors;
  63. {
  64.   int x, y, i, j;
  65.   struct pt_list *plist;
  66.   Sarmy *ap;
  67.   Ssector *sp;
  68.  
  69.   for (i = 0; i < world.xmax; ++i) {
  70.     for (j = 0; j < world.ymax; ++j) {
  71.       visible_sectors[i][j] = SEE_NOTHING;
  72.     }
  73.   }
  74.   for (plist = user.np->ptlist; plist != NULL; plist = plist->next) {
  75.     x = plist->pt.x;
  76.     y = plist->pt.y;
  77.     visible_sectors[x][y] = SEE_ALL;
  78.     for (i = x-LAND_SIGHT; i <= x+LAND_SIGHT; ++i) {
  79.       for (j = y-LAND_SIGHT; j <= y+LAND_SIGHT; ++j) {
  80.     sp = &world.map[(*wrapx)(i,j)][(*wrapy)(i,j)];
  81.     if (has_hidden(sp) && sp->owner != user.id) {
  82.       visible_sectors[x][y] |= SEE_ARMIES;
  83.     } else {
  84.       visible_sectors[(*wrapx)(i,j)][(*wrapy)(i,j)] |=
  85.         (SEE_LAND_WATER | SEE_OWNER | SEE_DESIG | SEE_POPULATION);
  86.     }
  87.     if (world.map[(*wrapx)(i,j)][(*wrapy)(i,j)].owner == 0) {
  88.       visible_sectors[(*wrapx)(i,j)][(*wrapy)(i,j)] |= SEE_RESOURCES;
  89.     }
  90.       }
  91.     }
  92.   }
  93.   for (ap = user.np->armies; ap != NULL; ap = ap->next) {
  94.     x = ap->pos.x;
  95.     y = ap->pos.y;
  96.     sp = &world.map[x][y];
  97.     if (has_hidden(sp) && sp->owner != user.id) {
  98.       visible_sectors[x][y] = SEE_ARMIES;
  99.     } else {
  100.       visible_sectors[x][y] = SEE_ALL;
  101.     }
  102.     for (i = x-ARMY_SIGHT; i <= x+ARMY_SIGHT; ++i) {
  103.       for (j = y-ARMY_SIGHT; j <= y+ARMY_SIGHT; ++j) {
  104.     sp = &world.map[(*wrapx)(i,j)][(*wrapy)(i,j)];
  105.     if (!has_hidden(sp)) {
  106.       visible_sectors[(*wrapx)(i,j)][(*wrapy)(i,j)] |=
  107.         (SEE_LAND_WATER | SEE_OWNER | SEE_DESIG |
  108.          SEE_POPULATION | SEE_ARMIES);
  109.     }
  110.     if (world.map[(*wrapx)(i,j)][(*wrapy)(i,j)].owner == 0) {
  111.       visible_sectors[(*wrapx)(i,j)][(*wrapy)(i,j)] |= SEE_RESOURCES;
  112.     }
  113.       }
  114.     }
  115.   }
  116. }
  117. */
  118. /*------------------------------get_good_types()-----------------------------
  119.     This function fills an array with the indexed to army_types that will
  120. be good for this npc.
  121.     Basically, the npc will only draft armies like spearmen and cavemen
  122. when it is in desperate straits.  For now, npc's will not draft transports,
  123. water-walk armies, kamikaze armies, front-line armies, or machines, because
  124. handling these well is beyond me at this point.
  125.     The npc decides whether or not to draft armies with low bonuses based
  126. on the proportion of current#of troops to desired #of troops.  The desired
  127. is  civilians * npc_agg/200 when at war, and civil. * npc_agg/300 otherwise.
  128. The cutoffs are 25+npc/2 % and npc/2 %, for armies with -1 to -30 bonuses and
  129. armies with <-30 bonuses, respectively.
  130.     For example, a nation with an aggressiveness of 50 and a civilian
  131. population of 10,000 will not draft cavemen when it has more than 625 soldiers,
  132. and will not draft spearmen when it has more than 1250 soldiers, and will not
  133. draft at all when it has more than 2500 soldiers. (when at war)
  134. ---------------------------------------------------------------------------*/
  135. get_good_types(good_armies,current,cut1,cut2)
  136. int good_armies[MAX_TYPES];
  137. int current,cut1,cut2;    /* current # of troops and cutoffs */
  138. {
  139.   Savail_army *tmp_avail;
  140.   struct army_type this_atype;
  141.   int type_index,ngood = 0,badflags;
  142.  
  143.   badflags = AF_WATER |  AF_LAND | AF_INVERSE_ALT | AF_KAMIKAZE | AF_FRONT_LINE
  144.        | AF_MACHINE | AF_CARGO | AF_WIZARD;
  145.  
  146.   for(tmp_avail = user.avail_armies;tmp_avail ; tmp_avail = tmp_avail->next){
  147.     type_index = army_type_index(tmp_avail->type);
  148.     this_atype = army_types[type_index];
  149.     if (this_atype.flags & badflags)
  150.      continue;
  151.     if(this_atype.bonus < CUTOFF1 && current > cut1)
  152.       continue;
  153.     else if(this_atype.bonus < CUTOFF2 && current > cut2)
  154.       continue;
  155.         /* replace army type with race-specific type if appropriate */
  156.     if(npc_specific &&
  157.     army_types[npc_specific-1].bonus >= this_atype.bonus &&
  158.     this_atype.bonus >= 0)
  159.       good_armies[ngood++] = npc_specific - 1;
  160.     else
  161.       good_armies[ngood++] = type_index;
  162.     }
  163.   if (debug)
  164.     printf("ngood = %d\n",ngood);
  165.   return(ngood);
  166. }
  167.  
  168. /*---------------------------tmp_army_better()-------------------------------
  169.     This function checks tmpap and oldap to see if tmpap would be better
  170. for ap to merge with.  Better is defined as big enough after the merge
  171. to take sectors, but smaller after the merge than the other army.
  172. This is a crude way of preventing one army from growing too large.
  173. Edit to your heart's content, if you think it will make the npc's better.
  174.     (This function is called by npc_merge)
  175. ------------------------------------------------------------------------*/
  176. tmp_army_better(ap,tmpap,oldap)
  177. Sarmy *ap,*tmpap,*oldap;
  178. {
  179.   if(strcmp(tmpap->type,ap->type)){    /* different types */
  180.     return(0);
  181.   }
  182.   
  183.   if(!oldap)                /* anything is better than nothing */
  184.     return(1);
  185.  
  186.   if(tmpap->n_soldiers + ap->n_soldiers < opt_army_size)
  187.     return(0);                /* already have oldap */
  188.  
  189.   if(tmpap->n_soldiers < oldap->n_soldiers)
  190.     return(1);                /* enough soldiers but not too big */
  191.   else
  192.     return(0);                /* too big */
  193. }
  194.  
  195. /*---------------------------find_desire()------------------------------------
  196.     Fill the desire array with the desireability for each sector on the
  197. map.  The base desire goes from zero to half the length of the shortest side.
  198. To find the final desire, the base desire is squared.  The desire for the
  199. surrounding sectors is modified.  If the base desire is N, then all the
  200. adjacent sectors have their desireability increased by N-1, the next sectors
  201. out are increased by N-2, etc.  This will hopefully keep npc's moving in the
  202. right general direction. (the base desire of owned sectors is 0, unless at war)
  203. ---------------------------------------------------------------------------*/
  204. find_desire(np,des_array)
  205. Snation *np;
  206. struct desire{
  207.     int base,final;
  208.     } **des_array;
  209. {
  210.   int x,y,i,d;
  211.  
  212.   for(x = 0; x < world.xmax;x++)
  213.     for(y = 0; y < world.ymax; y++){
  214.       des_array[x][y].base = npc_des(np,x,y);
  215.       des_array[x][y].final = 0;
  216.     }
  217.  
  218.   for(x = 0; x < world.xmax;x++)
  219.     for(y = 0; y < world.ymax; y++){
  220.       d = des_array[x][y].base;
  221.       des_array[x][y].final += d*d;
  222.       if(atwar && d > 0){        /* sector owned by us or enemy */
  223.         des_array[x][y].final += world.map[x][y].n_people/25;
  224.         if (world.map[x][y].designation == D_CAPITAL) 
  225.           des_array[x][y].final *= 2;
  226.         }
  227.       if(user.visible_sectors[x][y])
  228.         for(i = 1;i < d && add_square(x,y,i,d-i,des_array,np);i++);
  229.       }
  230. /* This makes a big mess--------
  231.     if(debug){
  232.     for(y = 0; y < world.ymax; y++){
  233.       for(x = 0; x < world.xmax;x++)
  234.         printf("%2d,",des_array[x][y].base);
  235.       printf("\n");
  236.     }
  237.     for(y = 0; y < world.ymax; y++){
  238.       for(x = 0; x < world.xmax;x++)
  239.         printf("%3d,",des_array[x][y].final);
  240.       printf("\n");
  241.     }
  242.   } */
  243. }
  244.  
  245. /*-------------------------------npc_des()-------------------------------------
  246.     This function calculates the desireability of a sector to an npc.
  247. This desireability will be calculated differently when the npc is at war
  248. than when it is at peace.  For now, just try to expand.
  249. -----------------------------------------------------------------------------*/
  250. npc_des(np,x,y)
  251. Snation *np;
  252. int x,y;
  253. {
  254.   int num,i,j,dstat;
  255.   Sdiplo **dm;
  256.  
  257.   if(!good_altitude(&world.map[x][y],np))
  258.     return(0);
  259.  
  260.   if(atwar && world.map[x][y].owner){
  261.     dm = user.diplo_matrix;
  262.     dstat = get_diplo_status(dm, np->id, world.map[x][y].owner);
  263.     if(world.map[x][y].owner != np->id && (dstat != WAR && dstat != JIHAD))
  264.       return(0);
  265.   } else if (world.map[x][y].owner == np->id)
  266.     return (0);
  267.  
  268.   if(atwar && world.map[x][y].owner){
  269.     if(world.map[x][y].owner == np->id){
  270.       switch(world.map[x][y].designation){
  271.         case D_FARM:
  272.           num = 3 + world.map[x][y].soil * npc_food_need;
  273.       break;
  274.         case D_METAL_MINE:
  275.           num = 3 + world.map[x][y].metal * npc_metal_need;
  276.       break;
  277.         case D_JEWEL_MINE:
  278.           num = 3 + world.map[x][y].jewels * npc_jewel_need;
  279.       break;
  280.         case D_CITY: case D_UNIVERSITY:
  281.           num = 11;
  282.       break;
  283.     case D_CAPITAL:
  284.       num = 8 + np->npc_agg / 10;
  285.       break;
  286.         default:
  287.       break;
  288.         }
  289.       }
  290.     else{
  291.       num = 12;
  292.   /*    switch(world.map[x][y].designation){
  293.         case D_FARM:
  294.           num = 8 * npc_food_need;
  295.       break;
  296.         case D_METAL_MINE:
  297.           num = 8 * npc_metal_need;
  298.       break;
  299.         case D_JEWEL_MINE:
  300.           num = 8 * npc_jewel_need;
  301.       break;
  302.         case D_CITY: case D_UNIVERSITY: case D_CAPITAL:
  303.       num = 12;
  304.       break;
  305.         default:
  306.       num = 7;
  307.       break;
  308.         } */
  309.   
  310.       for(i = -1;i < 2;i++)
  311.         for(j = -1;j < 2;j++)
  312.           if(world.map[(*wrapx)(x+j,y+i)][(*wrapy)(x+j,y+i)].owner == np->id)
  313.             num++;
  314.       /* if(world.map[x][y].n_people >= 0)
  315.         num += sqrt((double)world.map[x][y].n_people)/5.0; */
  316.       }
  317.     }
  318.   
  319.   else{
  320.     num = world.map[x][y].soil * npc_food_need;
  321.     num += world.map[x][y].metal * npc_metal_need;
  322.     num += world.map[x][y].jewels * npc_jewel_need;
  323.     for(i = -1;i < 2;i++)
  324.       for(j = -1;j < 2;j++)
  325.         if(world.map[(*wrapx)(x+j,y+i)][(*wrapy)(x+j,y+i)].owner == np->id)
  326.           num++;
  327.   }
  328.  
  329. /*  if(atwar && !world.map[x][y].owner)
  330.     return(num/4);
  331.   else */
  332.   num = min ( num, min (world.xmax, world.ymax) );
  333.     return(num/2);
  334. }
  335.  
  336. /*----------------------------add_square()---------------------------------
  337.     This function adds a number, add to the desireability of the sectors
  338. radius away from x,y.  Radius is defined rather loosely here, since the sectors
  339. form a square.
  340. ---------------------------------------------------------------------------*/
  341. add_square(x,y,radius,add,des_array,np)
  342. int x,y,radius,add;
  343. struct desire{
  344.     int base,final;
  345.     } **des_array;
  346. Snation *np;
  347. {
  348. int i,tx,ty,id,flag = 0;
  349. Ssector *sp;
  350.  
  351.   id = np->id;
  352.   tx = x-radius;            /* bottom side from lower left corner */
  353.   ty = y-radius;
  354.   for(i = 0; i < radius*2;i++){
  355.     sp = &world.map[(*wrapx)(tx+i,ty)][(*wrapy)(tx+i,y)];
  356.     if(!good_altitude(sp,np))
  357.       flag++;
  358.     des_array[(*wrapx)(tx+i,ty)][(*wrapy)(tx+i,ty)].final += add;
  359.   }
  360.  
  361.   ty = y+radius;            /* left side from upper left corner */
  362.   for(i = 0; i < radius*2;i++){
  363.     sp = &world.map[(*wrapx)(tx,ty-i)][(*wrapy)(tx-i,y-i)];
  364.     if(!good_altitude(sp,np))
  365.       flag++;
  366.     des_array[(*wrapx)(tx,ty-i)][(*wrapy)(tx,ty-i)].final += add;
  367.   }
  368.  
  369.   tx = x+radius;            /* top side from upper right corner */
  370.   for(i = 0; i < radius*2;i++){
  371.     sp = &world.map[(*wrapx)(tx-i,ty)][(*wrapy)(tx-i,y)];
  372.     if(!good_altitude(sp,np))
  373.       flag++;
  374.     des_array[(*wrapx)(tx-i,ty)][(*wrapy)(tx-i,ty)].final += add;
  375.   }
  376.  
  377.   ty = y-radius;            /* right side from lower right corner */
  378.   for(i = 0; i < radius*2;i++){
  379.     sp = &world.map[(*wrapx)(tx,ty+i)][(*wrapy)(tx,y+i)];
  380.     if(!good_altitude(sp,np))
  381.       flag++;
  382.     des_array[(*wrapx)(tx,ty+i)][(*wrapy)(tx,ty+i)].final += add;
  383.   }
  384.  
  385.   return((flag > 4*radius) ? 0:1);    /* equal to zero if lots of water */
  386. }
  387.  
  388. /*----------------------------check_moves()-----------------------------------
  389.     Ary represents the area of the map centered at the army ap.  This
  390. function fills ary with the maximum movepoints the army can have left when
  391. it arrives at the sector.  Of course, routes that are outside of the section
  392. of the map in ary are not considered, so a larger value for NPC_SIDE will
  393. sometimes result in better movement.  However, since the algorithm includes
  394. an insertion, it's best to keep the value small in the interest of time.
  395. --------------------------------------------------------------------------*/
  396. check_moves(np,ap,ary)
  397. Snation *np;
  398. Sarmy *ap;
  399. struct tmp_map{
  400.   int mvcost,mvleft;
  401.   } ary[NPC_SIDE][NPC_SIDE];
  402. {
  403.   int i,j,k;
  404.   int x,y,tx,ty,mapx,mapy;
  405.   int mv,mvleft;
  406.   int head=0,tail=0;
  407.   struct mvlist{
  408.     int mv;
  409.     Pt pos;
  410.   } list[NPC_SIDE * NPC_SIDE];    /* doesn't really need to be this big */
  411.   Ssector *sect;
  412.  
  413.   for(x = 1; x < NPC_SIDE-1; x++)
  414.   for(y = 1; y < NPC_SIDE-1; y++){
  415.     tx = x - (NPC_VIEW+1) + ap->pos.x;    /* translate ary */
  416.     ty = y - (NPC_VIEW+1) + ap->pos.y;    /* coords to map */
  417.     sect = &world.map[(*wrapx)(tx,ty)][(*wrapy)(tx,ty)];
  418.     ary[x][y].mvleft = -1;
  419.     ary[x][y].mvcost = get_army_move_cost(np,sect,ap);
  420.   }
  421.  
  422.   for(i = 0;i < NPC_SIDE * NPC_SIDE;i++)    /* initialize list of points */
  423.   list[i].mv = 0;
  424.  
  425.   /* start army in center of ary with whatever movement it has */
  426.   ary[NPC_VIEW+1][NPC_VIEW+1].mvleft = ap->mvpts;
  427.   list[0].mv = ap->mvpts;
  428.   list[0].pos.x = NPC_VIEW+1;
  429.   list[0].pos.y = NPC_VIEW+1;
  430.  
  431.   for(head = 0; list[head].mv; head++){        /* until list is empty */
  432.     mv = list[head].mv;
  433.     for(x = list[head].pos.x-1;x <= list[head].pos.x + 1;x++)
  434.       for(y = list[head].pos.y-1;y <= list[head].pos.y + 1;y++)
  435.         if(ary[x][y].mvleft == -1 && ary[x][y].mvcost <= mv){
  436.           mvleft = mv - ary[x][y].mvcost;
  437.           ary[x][y].mvleft = mvleft;
  438.           if(mvleft){
  439.             for(j = tail;list[j].mv < mvleft;j--);
  440.             for(k = tail;k > j;k--)
  441.               list[k+1] = list[k];
  442.             list[j+1].mv = mvleft;
  443.             list[j+1].pos.x = x;
  444.             list[j+1].pos.y = y;
  445.             tail++;
  446.           }
  447.         }
  448.     }
  449.  
  450. if(debug){
  451.   printf("ary for army %d= :\n",ap->id);
  452.   for(y = 0; y < NPC_SIDE; y++){
  453.     for(x = 0;x < NPC_SIDE;x++)
  454.       printf("[%d,%d],",ary[x][y].mvleft,ary[x][y].mvcost);
  455.     printf("\n");
  456.     }
  457.   }
  458. }
  459.  
  460. /*--------------------------do_npc_diplo()------------------------------------
  461.     Update the diplomacy for npcs.  Change the diplomacy according to
  462. a random number, and the npc aggressiveness.  If the number is less than
  463. the npc aggressiveness, then the diplomacy status goes down.  Otherwise
  464. it goes up.
  465. ---------------------------------------------------------------------------*/
  466. do_npc_diplo(np)
  467.      Snation *np;
  468. {
  469.   int i,dip_from,dip_to,num,up,down;
  470.   Snation *tmpnp;
  471.   
  472.   /* Take care of NPC diplomacy here */
  473.   
  474.   atwar = 0;
  475.   for(i = 1;i < world.n_nations;i++){
  476.     tmpnp = &world.nations[i];
  477.     if (is_active_ntn(tmpnp) && (!tmpnp->npc_flag || NPC_FIGHT)) { 
  478.       if (!have_met(user.diplo_matrix, np->id, i))
  479.         continue;
  480.       dip_to = get_diplo_status(user.diplo_matrix, np->id, i);
  481.       dip_from = get_diplo_status(user.diplo_matrix, i, np->id);
  482.       if (debug)
  483.     printf("from = %d, to = %d\n",dip_from,dip_to);
  484.       down = np->npc_agg * DIP_CHANGE / 100;
  485.       up = (100 - np->npc_agg) * DIP_CHANGE / 100;
  486.       num = RND()%100;
  487.       if (debug)
  488.     printf("num = %d ",num);
  489.       if(num < np->npc_agg){
  490.         if(num < down/2 || dip_from < dip_to)
  491.           dip_to--;
  492.         if(num < down)
  493.       dip_to--;
  494.     }
  495.       else{
  496.     num = 100 - num;
  497.         if(num < up/2)
  498.       dip_to++;
  499.         if(num < up)
  500.       dip_to++;
  501.     }
  502.       if(dip_to > dip_from)
  503.         dip_to = dip_from;
  504.       dip_to = max(dip_to,JIHAD);
  505.       if (debug)
  506.     printf("up = %d, down = %d, newto = %d\n",up,down,dip_to);
  507.       if(dip_to == WAR || dip_to == JIHAD)
  508.         atwar = 1;
  509.       set_diplo_status(user.diplo_matrix,np->id,i,dip_to);
  510.       }
  511.     }
  512.   dump_diplo(np,user.diplo_matrix, world.n_nations);
  513. }
  514.  
  515. /*---------------------------init_npc_mage()-----------------------------------
  516.     Initiate a mage for the npc.  Copied from menus.c
  517. -----------------------------------------------------------------------------*/
  518. init_npc_mage(np,sp)
  519. Snation *np;
  520. Ssector *sp;
  521. {
  522.   Sarmy army, make_army();
  523.  
  524.   army = make_army("Mage", "Mage", 1, A_DEFEND, np->id, sp->loc);
  525.   army.jewel_maint = 1000;
  526.   army.id = free_army_id(np);
  527.   army.flags |= AF_WIZARD;
  528.   army.next = NULL;
  529.     /* now insert it into the list */
  530.   np->n_armies++;
  531.   if (np->armies == NULL) { /* special case:  empty list */
  532.     np->armies = (Sarmy *) malloc(sizeof(Sarmy));
  533.     *(np->armies) = army;
  534.     np->armies->next = NULL;
  535.   } else {
  536.     insert_army_nation(np, &army, -1);
  537.   }
  538.   insert_army_sector(sp, &army);
  539.   np->jewels -= INITIATION_JEWELS;
  540. }
  541.