src/lib/update/revolt.c

Go to the documentation of this file.
00001 /*
00002  *  Empire - A multi-player, client/server Internet based war game.
00003  *  Copyright (C) 1986-2007, Dave Pare, Jeff Bailey, Thomas Ruschak,
00004  *                           Ken Stevens, Steve McClure
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  *  ---
00021  *
00022  *  See files README, COPYING and CREDITS in the root of the source
00023  *  tree for related information and legal notices.  It is expected
00024  *  that future projects/authors will amend these files as needed.
00025  *
00026  *  ---
00027  *
00028  *  revolt.c: Have disloyal populace revolt!
00029  * 
00030  *  Known contributors to this file:
00031  *     Dave Pare, 1986
00032  *     Steve McClure, 1997-2000
00033  */
00034 
00035 #include <config.h>
00036 
00037 #include "land.h"
00038 #include "lost.h"
00039 #include "news.h"
00040 #include "nsc.h"
00041 #include "path.h"
00042 #include "plane.h"
00043 #include "update.h"
00044 
00045 static void take_casualties(struct sctstr *, int);
00046 
00047 void
00048 revolt(struct sctstr *sp)
00049 {
00050     int che_civ;
00051     int che_uw;
00052     int civ;
00053     int uw;
00054     int che;
00055     int n;
00056 
00057     che = sp->sct_che;
00058     if (che != 0 && (sp->sct_che_target != sp->sct_own || che >= CHE_MAX))
00059         return;
00060     civ = sp->sct_item[I_CIVIL];
00061     uw = sp->sct_item[I_UW];
00062     if (che > (civ + uw) * 3)
00063         return;
00064     che_uw = 0;
00065     che_civ = 0;
00066     /* che due to civilian unrest */
00067     n = 10 - (random() % 20);
00068     che_civ = 3 + (civ * n / 500);
00069     if (che_civ < 0)
00070         che_civ = 0;
00071     else if (che_civ * 3 > civ)
00072         che_civ = civ / 3;
00073     if (che + che_civ > CHE_MAX)
00074         che_civ = CHE_MAX - che;
00075     che += che_civ;
00076     if (che < CHE_MAX) {
00077         /* che due to uw unrest */
00078         n = 10 + (random() % 30);
00079         che_uw = 5 + (uw * n / 500);
00080         if (che_uw > uw)
00081             che_uw = uw;
00082         if (che + che_uw > CHE_MAX)
00083             che_uw = CHE_MAX - che_uw;
00084         che += che_uw;
00085     }
00086     if (che_civ + che_uw > 0) {
00087         civ -= che_civ;
00088         uw -= che_uw;
00089         sp->sct_che_target = sp->sct_own;
00090         sp->sct_che = che;
00091         if (che_civ > 0)
00092             sp->sct_item[I_CIVIL] = civ;
00093         if (che_uw > 0)
00094             sp->sct_item[I_UW] = uw;
00095 #ifdef DEBUG
00096         logerror("(#%d) %d che fired up in %s",
00097                  sp->sct_own, che, ownxy(sp));
00098 #endif
00099     }
00100 }
00101 
00102 /*
00103  * summary of effects.
00104  * if there are no military in the sector, che recruit from
00105  *   populace if pop loyalty is > 10.  They spread subversion otherwise,
00106  *   trying to lower pop loyalty.
00107  * if che outnumber military, they stay and shoot it out, kill the
00108  *   military.
00109  * if che are outnumbered by less than 5 to 1, they blow up stuff,
00110  *   killing innocent civilians (never uw's) and damaging commodities.
00111  * if che are outnumbered by more than 5 to 1, they try to leave the
00112  *   sector for a nearby sector with fewer military.
00113  *
00114  * if the military lose any attacks, the pop loyalty in the sector
00115  *   gets worse, representing military defeat.
00116  * military can "catch" che's after bombing attacks, or after they move.
00117  *   If military catch them, then they get to shoot it out with a portion
00118  *   of the che's depending on the # of mil in the sector.  Chance to contact
00119  *   is around 10% per every equal number of mil:che ratio in the sector.
00120  *   "contact" is by 20% of the military in the sector, and odds are equal.
00121  *
00122  * Without a doubt this routine should be broken up, if only for readabilty.
00123  */
00124 void
00125 guerrilla(struct sctstr *sp)
00126 {
00127     struct sctstr *nsp;
00128     int recruit;
00129     int move;
00130     int ratio;
00131     int che;
00132     int mil;
00133     int cc, mc;
00134     double odds;
00135     int civ;
00136     int n;
00137     int uw;
00138     natid target;
00139     struct natstr *tnat;
00140     int convert;
00141     natid actor;
00142     natid victim;
00143     int tmp;
00144     int min_mil;
00145     int val;
00146     int oldmob;
00147     struct lndstr *lp;
00148     struct nstr_item ni;
00149 
00150     mc = cc = 0;
00151     recruit = 0;
00152     convert = 0;
00153     move = 0;
00154     if (!sp->sct_che)
00155         return;
00156     civ = sp->sct_item[I_CIVIL];
00157     uw = sp->sct_item[I_UW];
00158     victim = sp->sct_own;
00159     actor = sp->sct_oldown;
00160     che = sp->sct_che;
00161     mil = sp->sct_item[I_MILIT];
00162 
00163     snxtitem_xy(&ni, EF_LAND, sp->sct_x, sp->sct_y);
00164 
00165     while (NULL != (lp = nxtitemp(&ni))) {
00166         if (lp->lnd_own != sp->sct_own)
00167             continue;
00168 
00169         mil += lp->lnd_item[I_MILIT];
00170 
00171         if (sp->sct_che_target != sp->sct_own)
00172             continue;
00173 
00174         /* Security troops can now kill up to 1/5 their complement each
00175            update, before doing anything else. */
00176         if (lchr[(int)lp->lnd_type].l_flags & L_SECURITY) {
00177             int che_kill, r;
00178 
00179             mil += lp->lnd_item[I_MILIT];
00180             r = (lp->lnd_item[I_MILIT] * lp->lnd_effic) / 500;
00181             che_kill = r < 1 ? 0 : roll(r);
00182             if (che_kill > che)
00183                 che_kill = che;
00184             if (che_kill) {
00185                 wu(0, sp->sct_own,
00186                    "%s kills %d guerrilla%s in raid at %s!\n",
00187                    prland(lp), che_kill, splur(che_kill), ownxy(sp));
00188                 che -= che_kill;
00189             }
00190         }
00191     }
00192 
00193     /* Security forces killed all the che */
00194     if (che <= 0) {
00195         sp->sct_che = 0;
00196         sp->sct_che_target = 0;
00197         return;
00198     }
00199 
00200     target = sp->sct_che_target;
00201     if (CANT_HAPPEN(target == 0))
00202         return;
00203     tnat = getnatp(target);
00204     if (tnat->nat_stat == STAT_UNUSED) {
00205         /* target nation has dissolved: che's retire.  */
00206         logerror("%d Che targeted at country %d retiring", che, target);
00207         sp->sct_che = 0;
00208         sp->sct_che_target = 0;
00209         sp->sct_item[I_CIVIL] = MIN(civ + che, ITEM_MAX);
00210         return;
00211     }
00212 
00213     if (sp->sct_own != target) {
00214         /*logerror("own %d != target %d", sp->sct_own, target); */
00215         move++;
00216         goto domove;
00217     }
00218 
00219     ratio = mil / che;
00220     odds = (double)che / (mil + che);
00221     odds /= hap_fact(tnat, getnatp(sp->sct_oldown));
00222     if (mil == 0) {
00223         wu(0, sp->sct_own, "Revolutionary subversion reported in %s!\n",
00224            ownxy(sp));
00225         recruit++;
00226         convert++;
00227     } else if (che > mil && mil > 0) {
00228         /*logerror("guerrilla shootout with military"); */
00229         /*
00230          * shoot it out with the military, and kill them off.
00231          * If loyalty bad enough, then take the sector over,
00232          * and enlist 5% of civ as military force.
00233          */
00234         while (che > 0 && mil > 0) {
00235             if (chance(odds)) {
00236                 mc++;
00237                 mil--;
00238             } else {
00239                 cc++;
00240                 che--;
00241             }
00242         }
00243         if (mil > 0) {
00244             /* military won.  */
00245             n = sp->sct_loyal - (random() % 15);
00246             if (n < 0)
00247                 n = 0;
00248             sp->sct_loyal = n;
00249             /*logerror("(#%d) mil beat che in %s", sp->sct_own, */
00250             /*ownxy(sp)); */
00251         } else {
00252             convert++;
00253             recruit++;
00254             /*logerror("(#%d) che beat mil in %s", sp->sct_own, */
00255             /*ownxy(sp)); */
00256         }
00257         take_casualties(sp, mc);
00258     } else if (ratio < 5) {
00259         /*
00260          * guerrillas have to resort to blowing things up.
00261          * Note this disrupts work in the sector.
00262          */
00263         n = 0;
00264         n = (random() % 10) + (random() % che);
00265         if (n > 100)
00266             n = 100;
00267         tmp = sp->sct_work - n;
00268         if (tmp < 0)
00269             tmp = 0;
00270         sp->sct_work = tmp;
00271         wu(0, sp->sct_own,
00272            "Production %s disrupted by terrorists in %s\n",
00273            effadv(n), ownxy(sp));
00274         sect_damage(sp, n / 10, 0);
00275         /*logerror("(#%d) che blew up %s for %d", sp->sct_own, */
00276         /*ownxy(sp), n); */
00277         recruit++;
00278     } else {
00279         /* ratio >= 5 */
00280         /*logerror("(#%d) %d che fleeing %d mil in %s", sp->sct_own, */
00281         /*che, mil, ownxy(sp)); */
00282         move++;
00283     }
00284     if (mil > 0 && che > 0) {
00285         /*
00286          * we only get here if we haven't had combat previously.
00287          * Chance to catch them.
00288          * 20% of mil involved in attacking the che's.
00289          */
00290         if (chance(ratio * 0.10)) {
00291             n = (mil / 5) + 1;
00292             odds = (double)che / (n + che);
00293             odds /= hap_fact(tnat, getnatp(sp->sct_oldown));
00294             while (che > 0 && n > 0) {
00295                 if (chance(odds)) {
00296                     mc++;
00297                     n--;
00298                 } else {
00299                     cc++;
00300                     che--;
00301                 }
00302             }
00303             take_casualties(sp, mc);
00304             recruit = 0;
00305             /*logerror("Caught che; mc: %d, cc: %d", cc, mc); */
00306         }
00307     }
00308     if (convert && sp->sct_loyal >= 50) {
00309         int n;
00310         /* new owner gets to keep the mobility there */
00311         oldmob = sp->sct_mobil;
00312         /* che won, and sector converts. */
00313         if (sp->sct_own == sp->sct_oldown)
00314             sp->sct_oldown = 0;
00315         else
00316             takeover(sp, sp->sct_oldown);
00317         sp->sct_mobil = oldmob;
00318         civ += uw;
00319         uw = 0;
00320         n = civ / 20;
00321         civ -= n;
00322         if (civ > ITEM_MAX) {
00323             uw = civ - ITEM_MAX;
00324             civ = ITEM_MAX;
00325         }
00326         sp->sct_item[I_CIVIL] = civ;
00327         sp->sct_item[I_UW] = uw;
00328         sp->sct_item[I_MILIT] = n;
00329         move++;
00330         recruit = 0;
00331         if (sp->sct_own)
00332             wu(0, sp->sct_own, "Sector %s has been retaken!\n",
00333                xyas(sp->sct_x, sp->sct_y, sp->sct_own));
00334     }
00335     if (recruit && che > 0) {
00336         /* loyalty drops during recruitment efforts */
00337         n = sp->sct_loyal;
00338         if (n < 30)
00339             n += (random() % 5) + 1;
00340         else if (n < 70)
00341             n += (random() % 10) + 4;
00342         if (n > 127)
00343             n = 127;
00344         sp->sct_loyal = n;
00345         if (sp->sct_oldown != sp->sct_own || n > 100) {
00346             n = civ * (random() % 3) / 200;
00347             n /= hap_fact(tnat, getnatp(sp->sct_oldown));
00348             if (n + che > CHE_MAX)
00349                 n = CHE_MAX - che;
00350             che += n;
00351             civ -= n;
00352             sp->sct_item[I_CIVIL] = civ;
00353         }
00354         n = uw * (random() % 3) / 200;
00355         if (n + che > CHE_MAX)
00356             n = CHE_MAX - che;
00357         che += n;
00358         uw -= n;
00359         sp->sct_item[I_UW] = uw;
00360     }
00361   domove:
00362     if (move && che > 0) {
00363         struct sctstr *nicest_sp = 0;
00364         if (convert)
00365             min_mil = 999;
00366         else
00367             min_mil = mil;
00368         /* search adjacent sectors for a nice one */
00369         for (n = 1; n <= 6; n++) {
00370             nsp = getsectp(sp->sct_x + diroff[n][0],
00371                            sp->sct_y + diroff[n][1]);
00372             if (dchr[nsp->sct_type].d_mob0 < 0)
00373                 continue;
00374             if (nsp->sct_own != target)
00375                 continue;
00376             if (nsp->sct_che > 0) {
00377                 if (nsp->sct_che_target != target)
00378                     continue;
00379                 if (nsp->sct_che + che > CHE_MAX)
00380                     continue;
00381             }
00382             val = nsp->sct_item[I_MILIT];
00383             /* don't give che more precise info than spy */
00384             val = roundintby(val, 10);
00385             /* inject a modicum of indeterminism; also
00386              * avoids che preferring certain directions */
00387             val += random() % 10 - 5;
00388             if (val >= min_mil)
00389                 continue;
00390             nicest_sp = nsp;
00391             min_mil = val;
00392         }
00393         /* if we found a nice sector, go there */
00394         if (nicest_sp != 0) {
00395             nicest_sp->sct_che += che;
00396             nicest_sp->sct_che_target = target;
00397             che = 0;
00398         }
00399     }
00400     if (che > 0) {
00401         sp->sct_che = che;
00402         sp->sct_che_target = target;
00403     } else {
00404         sp->sct_che = 0;
00405         sp->sct_che_target = 0;
00406     }
00407     if (mc > 0 || cc > 0) {
00408         wu(0, target,
00409            "Guerrilla warfare in %s\n",
00410            xyas(sp->sct_x, sp->sct_y, target));
00411         if (sp->sct_own == target)
00412             wu(0, target, "  body count: troops: %d, rebels: %d\n", mc, cc);
00413         else
00414             wu(0, target,
00415                "  rebels murder %d military\n", mc);
00416         nreport(actor, N_FREEDOM_FIGHT, victim, 1);
00417     }
00418     if (sp->sct_own != victim)
00419         wu(0, victim, "Partisans take over %s!\n",
00420            xyas(sp->sct_x, sp->sct_y, victim));
00421 }
00422 
00423 static void
00424 take_casualties(struct sctstr *sp, int mc)
00425 {
00426     int orig_mil;
00427     int cantake;
00428     int nunits = 0, each, deq;
00429     struct lndstr *lp;
00430     struct nstr_item ni;
00431 
00432     /* casualties come out of mil first */
00433     orig_mil = sp->sct_item[I_MILIT];
00434 
00435     if (mc <= orig_mil) {
00436         sp->sct_item[I_MILIT] = orig_mil - mc;
00437         return;
00438     }
00439     sp->sct_item[I_MILIT] = 0;
00440 
00441     /* remaining casualites */
00442     mc -= orig_mil;
00443 
00444     /*
00445      * Need to take total_casualties and divide
00446      * them amongst the land units in the sector
00447      * Do security troops first, then others.
00448      * Try not to kill any unit.
00449      */
00450     snxtitem_xy(&ni, EF_LAND, sp->sct_x, sp->sct_y);
00451     while (NULL != (lp = nxtitemp(&ni))) {
00452         nunits++;
00453         if (lchr[(int)lp->lnd_type].l_flags & L_SECURITY)
00454             nunits++;
00455     }
00456 
00457     if (nunits == 0)
00458         return;
00459 
00460     each = (mc / nunits) + 2;
00461 
00462     /* kill some security troops */
00463     snxtitem_xy(&ni, EF_LAND, sp->sct_x, sp->sct_y);
00464     while (NULL != (lp = nxtitemp(&ni))) {
00465         if (!(lchr[(int)lp->lnd_type].l_flags & L_SECURITY))
00466             continue;
00467 
00468         cantake = ((lp->lnd_effic - 40) / 100.0) * lp->lnd_item[I_MILIT];
00469 
00470         if (cantake >= each) {
00471             deq = ((double)each / lp->lnd_item[I_MILIT]) * 100.0;
00472             mc -= 2 * each;
00473         } else if (cantake > 0) {
00474             deq = ((double)cantake / lp->lnd_item[I_MILIT]) * 100.0;
00475             mc -= 2 * cantake;
00476         } else
00477             deq = 0;
00478 
00479         lp->lnd_effic -= deq;
00480         lp->lnd_mobil -= deq / 2;
00481         deq = lchr[(int)lp->lnd_type].l_mil * (deq / 100.0);
00482         lnd_submil(lp, deq);
00483         if (mc <= 0)
00484             return;
00485     }
00486 
00487     /* kill some normal troops */
00488     snxtitem_xy(&ni, EF_LAND, sp->sct_x, sp->sct_y);
00489     while (NULL != (lp = nxtitemp(&ni))) {
00490         if (lchr[(int)lp->lnd_type].l_flags & L_SECURITY)
00491             continue;
00492 
00493         cantake = ((lp->lnd_effic - 40) / 100.0) * lp->lnd_item[I_MILIT];
00494 
00495         if (cantake >= each) {
00496             deq = ((double)each / lp->lnd_item[I_MILIT]) * 100.0;
00497             mc -= each;
00498         } else if (cantake > 0) {
00499             deq = ((double)cantake / lp->lnd_item[I_MILIT]) * 100.0;
00500             mc -= cantake;
00501         } else
00502             deq = 0;
00503 
00504         lp->lnd_effic -= deq;
00505         lp->lnd_mobil -= deq / 2;
00506         deq = lchr[(int)lp->lnd_type].l_mil * (deq / 100.0);
00507         lnd_submil(lp, deq);
00508         if (mc <= 0)
00509             return;
00510     }
00511 
00512     /* Hmm.. still some left.. kill off units now */
00513     /* kill some normal troops */
00514     snxtitem_xy(&ni, EF_LAND, sp->sct_x, sp->sct_y);
00515     while (NULL != (lp = nxtitemp(&ni))) {
00516         if (lchr[(int)lp->lnd_type].l_flags & L_SECURITY)
00517             continue;
00518 
00519         mc -= (lp->lnd_effic / 100.0) * lp->lnd_item[I_MILIT];
00520         lp->lnd_effic = 0;
00521         lnd_submil(lp, 1000);   /* Remove 'em all */
00522         wu(0, lp->lnd_own, "%s dies fighting guerrillas in %s\n",
00523            prland(lp), xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
00524         makelost(EF_LAND, lp->lnd_own, lp->lnd_uid, lp->lnd_x, lp->lnd_y);
00525         lp->lnd_own = 0;
00526         if (mc <= 0)
00527             return;
00528     }
00529 
00530     /* Hmm.. still some left.. kill off units now */
00531     /* kill some security troops */
00532     snxtitem_xy(&ni, EF_LAND, sp->sct_x, sp->sct_y);
00533     while (NULL != (lp = nxtitemp(&ni))) {
00534         if (!(lchr[(int)lp->lnd_type].l_flags & L_SECURITY))
00535             continue;
00536 
00537         mc -= (lp->lnd_effic / 100.0) * lp->lnd_item[I_MILIT] * 2.0;
00538         lp->lnd_effic = 0;
00539         lnd_submil(lp, 1000);   /* Kill 'em all */
00540         wu(0, lp->lnd_own, "%s dies fighting guerrillas in %s\n",
00541            prland(lp), xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
00542         makelost(EF_LAND, lp->lnd_own, lp->lnd_uid, lp->lnd_x, lp->lnd_y);
00543         lp->lnd_own = 0;
00544         if (mc <= 0)
00545             return;
00546     }
00547 
00548     /* Hmm.. everyone dead.. too bad */
00549 }

Generated on Fri Mar 28 11:01:16 2008 for empserver by  doxygen 1.5.2