]> git.pond.sub.org Git - empserver/blob - src/lib/update/revolt.c
dbf2766e690df84cb6ca2339a3efcddb788b7e12
[empserver] / src / lib / update / revolt.c
1 /*
2  *  Empire - A multi-player, client/server Internet based war game.
3  *  Copyright (C) 1986-2013, Dave Pare, Jeff Bailey, Thomas Ruschak,
4  *                Ken Stevens, Steve McClure, Markus Armbruster
5  *
6  *  Empire is free software: you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation, either version 3 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *  ---
20  *
21  *  See files README, COPYING and CREDITS in the root of the source
22  *  tree for related information and legal notices.  It is expected
23  *  that future projects/authors will amend these files as needed.
24  *
25  *  ---
26  *
27  *  revolt.c: Have disloyal populace revolt!
28  *
29  *  Known contributors to this file:
30  *     Dave Pare, 1986
31  *     Steve McClure, 1997-2000
32  *     Markus Armbruster, 2004-2012
33  */
34
35 #include <config.h>
36
37 #include "chance.h"
38 #include "land.h"
39 #include "lost.h"
40 #include "news.h"
41 #include "nsc.h"
42 #include "nuke.h"
43 #include "path.h"
44 #include "plane.h"
45 #include "update.h"
46
47 static void take_casualties(struct sctstr *, int);
48 static void lnd_dies_fighting_che(struct lndstr *);
49
50 void
51 revolt(struct sctstr *sp)
52 {
53     int che_civ;
54     int che_uw;
55     int civ;
56     int uw;
57     int che;
58     int n;
59
60     che = sp->sct_che;
61     if (che != 0 && (sp->sct_che_target != sp->sct_own || che >= CHE_MAX))
62         return;
63     civ = sp->sct_item[I_CIVIL];
64     uw = sp->sct_item[I_UW];
65     if (che > (civ + uw) * 3)
66         return;
67     che_uw = 0;
68     /* che due to civilian unrest */
69     n = 10 - roll0(20);
70     che_civ = 3 + (civ * n / 500);
71     if (che_civ < 0)
72         che_civ = 0;
73     else if (che_civ * 3 > civ)
74         che_civ = civ / 3;
75     if (che + che_civ > CHE_MAX)
76         che_civ = CHE_MAX - che;
77     che += che_civ;
78     if (che < CHE_MAX) {
79         /* che due to uw unrest */
80         n = 10 + roll0(30);
81         che_uw = 5 + (uw * n / 500);
82         if (che_uw > uw)
83             che_uw = uw;
84         if (che + che_uw > CHE_MAX)
85             che_uw = CHE_MAX - che_uw;
86         che += che_uw;
87     }
88     if (che_civ + che_uw > 0) {
89         civ -= che_civ;
90         uw -= che_uw;
91         sp->sct_che_target = sp->sct_own;
92         sp->sct_che = che;
93         if (che_civ > 0)
94             sp->sct_item[I_CIVIL] = civ;
95         if (che_uw > 0)
96             sp->sct_item[I_UW] = uw;
97     }
98 }
99
100 /*
101  * summary of effects.
102  * if there are no military in the sector, che recruit from
103  *   populace if pop loyalty is > 10.  They spread subversion otherwise,
104  *   trying to lower pop loyalty.
105  * if che outnumber military, they stay and shoot it out, kill the
106  *   military.
107  * if che are outnumbered by less than 5 to 1, they blow up stuff,
108  *   killing innocent civilians (never uw's) and damaging commodities.
109  * if che are outnumbered by more than 5 to 1, they try to leave the
110  *   sector for a nearby sector with fewer military.
111  *
112  * if the military lose any attacks, the pop loyalty in the sector
113  *   gets worse, representing military defeat.
114  * military can "catch" che's after bombing attacks, or after they move.
115  *   If military catch them, then they get to shoot it out with a portion
116  *   of the che's depending on the # of mil in the sector.  Chance to contact
117  *   is around 10% per every equal number of mil:che ratio in the sector.
118  *   "contact" is by 20% of the military in the sector, and odds are equal.
119  *
120  * Without a doubt this routine should be broken up, if only for readabilty.
121  */
122 void
123 guerrilla(struct sctstr *sp)
124 {
125     struct sctstr *nsp;
126     int recruit;
127     int move;
128     int ratio;
129     int che;
130     int mil;
131     int cc, mc;
132     double odds;
133     int civ;
134     int n;
135     int uw;
136     natid target;
137     struct natstr *tnat;
138     int convert;
139     natid actor;
140     natid victim;
141     int tmp;
142     int min_mil;
143     int val;
144     int oldmob;
145     struct lndstr *lp;
146     struct nstr_item ni;
147
148     mc = cc = 0;
149     recruit = 0;
150     convert = 0;
151     move = 0;
152     if (!sp->sct_che)
153         return;
154     civ = sp->sct_item[I_CIVIL];
155     uw = sp->sct_item[I_UW];
156     victim = sp->sct_own;
157     actor = sp->sct_oldown;
158     che = sp->sct_che;
159     mil = sp->sct_item[I_MILIT];
160
161     snxtitem_xy(&ni, EF_LAND, sp->sct_x, sp->sct_y);
162
163     while (NULL != (lp = nxtitemp(&ni))) {
164         if (lp->lnd_own != sp->sct_own)
165             continue;
166         if (lp->lnd_ship >= 0)
167             continue;
168
169         mil += lp->lnd_item[I_MILIT];
170
171         if (sp->sct_che_target != sp->sct_own)
172             continue;
173
174         /* Security troops can now kill up to 1/5 their complement each
175            update, before doing anything else. */
176         if (lchr[(int)lp->lnd_type].l_flags & L_SECURITY) {
177             int che_kill, r;
178
179             mil += lp->lnd_item[I_MILIT];
180             r = (lp->lnd_item[I_MILIT] * lp->lnd_effic) / 500;
181             che_kill = r < 1 ? 0 : roll(r);
182             if (che_kill > che)
183                 che_kill = che;
184             if (che_kill) {
185                 wu(0, sp->sct_own,
186                    "%s kills %d guerrilla%s in raid at %s!\n",
187                    prland(lp), che_kill, splur(che_kill), ownxy(sp));
188                 che -= che_kill;
189             }
190         }
191     }
192
193     /* Security forces killed all the che */
194     if (che <= 0) {
195         sp->sct_che = 0;
196         sp->sct_che_target = 0;
197         return;
198     }
199
200     target = sp->sct_che_target;
201     if (CANT_HAPPEN(target == 0))
202         return;
203     tnat = getnatp(target);
204     if (tnat->nat_stat == STAT_UNUSED) {
205         /* target nation has dissolved: che's retire.  */
206         logerror("%d Che targeted at country %d retiring", che, target);
207         sp->sct_che = 0;
208         sp->sct_che_target = 0;
209         sp->sct_item[I_CIVIL] = MIN(civ + che, ITEM_MAX);
210         return;
211     }
212
213     if (sp->sct_own != target) {
214         move++;
215         goto domove;
216     }
217
218     ratio = mil / che;
219     odds = (double)che / (mil + che);
220     odds /= hap_fact(tnat, getnatp(sp->sct_oldown));
221     if (mil == 0) {
222         wu(0, sp->sct_own, "Revolutionary subversion reported in %s!\n",
223            ownxy(sp));
224         recruit++;
225         convert++;
226     } else if (che > mil && mil > 0) {
227         /*
228          * shoot it out with the military, and kill them off.
229          * If loyalty bad enough, then take the sector over,
230          * and enlist 5% of civ as military force.
231          */
232         while (che > 0 && mil > 0) {
233             if (chance(odds)) {
234                 mc++;
235                 mil--;
236             } else {
237                 cc++;
238                 che--;
239             }
240         }
241         if (mil > 0) {
242             /* military won.  */
243             n = sp->sct_loyal - roll0(15);
244             if (n < 0)
245                 n = 0;
246             sp->sct_loyal = n;
247         } else {
248             convert++;
249             recruit++;
250         }
251         take_casualties(sp, mc);
252     } else if (ratio < 5) {
253         /*
254          * guerrillas have to resort to blowing things up.
255          * Note this disrupts work in the sector.
256          */
257         n = roll0(10) + roll0(che);
258         if (n > 100)
259             n = 100;
260         tmp = sp->sct_work - n;
261         if (tmp < 0)
262             tmp = 0;
263         sp->sct_work = tmp;
264         wu(0, sp->sct_own,
265            "Production %s disrupted by terrorists in %s\n",
266            effadv(n), ownxy(sp));
267         sect_damage(sp, n / 10);
268         recruit++;
269     } else {
270         /* ratio >= 5 */
271         move++;
272     }
273     if (mil > 0 && che > 0) {
274         /*
275          * we only get here if we haven't had combat previously.
276          * Chance to catch them.
277          * 20% of mil involved in attacking the che's.
278          */
279         if (chance(ratio * 0.10)) {
280             n = (mil / 5) + 1;
281             odds = (double)che / (n + che);
282             odds /= hap_fact(tnat, getnatp(sp->sct_oldown));
283             while (che > 0 && n > 0) {
284                 if (chance(odds)) {
285                     mc++;
286                     n--;
287                 } else {
288                     cc++;
289                     che--;
290                 }
291             }
292             take_casualties(sp, mc);
293             recruit = 0;
294         }
295     }
296     if (convert && sp->sct_loyal >= 50) {
297         int n;
298         /* new owner gets to keep the mobility there */
299         oldmob = sp->sct_mobil;
300         /* che won, and sector converts. */
301         if (sp->sct_own == sp->sct_oldown)
302             sp->sct_oldown = 0;
303         else {
304             lost_and_found(EF_SECTOR, sp->sct_own, sp->sct_oldown,
305                            0, sp->sct_x, sp->sct_y);
306             takeover(sp, sp->sct_oldown);
307         }
308         sp->sct_mobil = oldmob;
309         civ += uw;
310         uw = 0;
311         n = civ / 20;
312         civ -= n;
313         if (civ > ITEM_MAX) {
314             uw = civ - ITEM_MAX;
315             civ = ITEM_MAX;
316         }
317         sp->sct_item[I_CIVIL] = civ;
318         sp->sct_item[I_UW] = uw;
319         sp->sct_item[I_MILIT] = n;
320         move++;
321         recruit = 0;
322         if (sp->sct_own)
323             wu(0, sp->sct_own, "Sector %s has been retaken!\n",
324                xyas(sp->sct_x, sp->sct_y, sp->sct_own));
325     }
326     if (recruit && che > 0) {
327         /* loyalty drops during recruitment efforts */
328         n = sp->sct_loyal;
329         if (n < 30)
330             n += roll0(5) + 1;
331         else if (n < 70)
332             n += roll0(10) + 4;
333         if (n > 127)
334             n = 127;
335         sp->sct_loyal = n;
336         if (sp->sct_oldown != sp->sct_own || n > 100) {
337             n = civ * roll0(3) / 200;
338             n /= hap_fact(tnat, getnatp(sp->sct_oldown));
339             if (n + che > CHE_MAX)
340                 n = CHE_MAX - che;
341             che += n;
342             civ -= n;
343             sp->sct_item[I_CIVIL] = civ;
344         }
345         n = uw * roll0(3) / 200;
346         if (n + che > CHE_MAX)
347             n = CHE_MAX - che;
348         che += n;
349         uw -= n;
350         sp->sct_item[I_UW] = uw;
351     }
352 domove:
353     if (move && che > 0) {
354         struct sctstr *nicest_sp = NULL;
355         if (convert)
356             min_mil = 999;
357         else
358             min_mil = mil;
359         /* search adjacent sectors for a nice one */
360         for (n = 1; n <= 6; n++) {
361             nsp = getsectp(sp->sct_x + diroff[n][0],
362                            sp->sct_y + diroff[n][1]);
363             if (dchr[nsp->sct_type].d_mob0 < 0)
364                 continue;
365             if (nsp->sct_own != target)
366                 continue;
367             if (nsp->sct_che > 0) {
368                 if (nsp->sct_che_target != target)
369                     continue;
370                 if (nsp->sct_che + che > CHE_MAX)
371                     continue;
372             }
373             val = nsp->sct_item[I_MILIT];
374             /* don't give che more precise info than spy */
375             val = roundintby(val, 10);
376             /* inject a modicum of indeterminism; also
377              * avoids che preferring certain directions */
378             val += roll0(10) - 5;
379             if (val >= min_mil)
380                 continue;
381             nicest_sp = nsp;
382             min_mil = val;
383         }
384         /* if we found a nice sector, go there */
385         if (nicest_sp) {
386             nicest_sp->sct_che += che;
387             nicest_sp->sct_che_target = target;
388             che = 0;
389         }
390     }
391     if (che > 0) {
392         sp->sct_che = che;
393         sp->sct_che_target = target;
394     } else {
395         sp->sct_che = 0;
396         sp->sct_che_target = 0;
397     }
398     if (mc > 0 || cc > 0) {
399         wu(0, target,
400            "Guerrilla warfare in %s\n",
401            xyas(sp->sct_x, sp->sct_y, target));
402         if (sp->sct_own == target)
403             wu(0, target, "  body count: troops: %d, rebels: %d\n", mc, cc);
404         else
405             wu(0, target, "  rebels murder %d military\n", mc);
406         nreport(actor, N_FREEDOM_FIGHT, victim, 1);
407     }
408     if (sp->sct_own != victim)
409         wu(0, victim, "Partisans take over %s!\n",
410            xyas(sp->sct_x, sp->sct_y, victim));
411 }
412
413 static void
414 take_casualties(struct sctstr *sp, int mc)
415 {
416     int orig_mil;
417     int cantake;
418     int nunits = 0, each, deq;
419     struct lndstr *lp;
420     struct nstr_item ni;
421
422     /* casualties come out of mil first */
423     orig_mil = sp->sct_item[I_MILIT];
424
425     if (mc <= orig_mil) {
426         sp->sct_item[I_MILIT] = orig_mil - mc;
427         return;
428     }
429     sp->sct_item[I_MILIT] = 0;
430
431     /* remaining casualites */
432     mc -= orig_mil;
433
434     /*
435      * Need to take total_casualties and divide
436      * them amongst the land units in the sector
437      * Do security troops first, then others.
438      * Try not to kill any unit.
439      */
440     snxtitem_xy(&ni, EF_LAND, sp->sct_x, sp->sct_y);
441     while (NULL != (lp = nxtitemp(&ni))) {
442         if (lp->lnd_own != sp->sct_own)
443             continue;
444         if (lp->lnd_ship >= 0)
445             continue;
446         nunits++;
447         if (lchr[(int)lp->lnd_type].l_flags & L_SECURITY)
448             nunits++;
449     }
450
451     if (nunits == 0)
452         return;
453
454     each = (mc / nunits) + 2;
455
456     /* kill some security troops */
457     snxtitem_xy(&ni, EF_LAND, sp->sct_x, sp->sct_y);
458     while (NULL != (lp = nxtitemp(&ni))) {
459         if (lp->lnd_own != sp->sct_own)
460             continue;
461         if (lp->lnd_ship >= 0)
462             continue;
463         if (!(lchr[(int)lp->lnd_type].l_flags & L_SECURITY))
464             continue;
465
466         cantake = ((lp->lnd_effic - 40) / 100.0) * lp->lnd_item[I_MILIT];
467
468         if (cantake >= each) {
469             deq = ((double)each / lp->lnd_item[I_MILIT]) * 100.0;
470             mc -= 2 * each;
471         } else if (cantake > 0) {
472             deq = ((double)cantake / lp->lnd_item[I_MILIT]) * 100.0;
473             mc -= 2 * cantake;
474         } else
475             deq = 0;
476
477         lp->lnd_effic -= deq;
478         lp->lnd_mobil -= deq / 2;
479         deq = lchr[(int)lp->lnd_type].l_item[I_MILIT] * (deq / 100.0);
480         lnd_submil(lp, deq);
481         if (mc <= 0)
482             return;
483     }
484
485     /* kill some normal troops */
486     snxtitem_xy(&ni, EF_LAND, sp->sct_x, sp->sct_y);
487     while (NULL != (lp = nxtitemp(&ni))) {
488         if (lp->lnd_own != sp->sct_own)
489             continue;
490         if (lp->lnd_ship >= 0)
491             continue;
492         if (lchr[(int)lp->lnd_type].l_flags & L_SECURITY)
493             continue;
494
495         cantake = ((lp->lnd_effic - 40) / 100.0) * lp->lnd_item[I_MILIT];
496
497         if (cantake >= each) {
498             deq = ((double)each / lp->lnd_item[I_MILIT]) * 100.0;
499             mc -= each;
500         } else if (cantake > 0) {
501             deq = ((double)cantake / lp->lnd_item[I_MILIT]) * 100.0;
502             mc -= cantake;
503         } else
504             deq = 0;
505
506         lp->lnd_effic -= deq;
507         lp->lnd_mobil -= deq / 2;
508         deq = lchr[(int)lp->lnd_type].l_item[I_MILIT] * (deq / 100.0);
509         lnd_submil(lp, deq);
510         if (mc <= 0)
511             return;
512     }
513
514     /* Hmm.. still some left.. kill off units now */
515     /* kill some normal troops */
516     snxtitem_xy(&ni, EF_LAND, sp->sct_x, sp->sct_y);
517     while (NULL != (lp = nxtitemp(&ni))) {
518         if (lp->lnd_own != sp->sct_own)
519             continue;
520         if (lp->lnd_ship >= 0)
521             continue;
522         if (lchr[(int)lp->lnd_type].l_flags & L_SECURITY)
523             continue;
524
525         mc -= (lp->lnd_effic / 100.0) * lp->lnd_item[I_MILIT];
526         lnd_dies_fighting_che(lp);
527         if (mc <= 0)
528             return;
529     }
530
531     /* Hmm.. still some left.. kill off units now */
532     /* kill some security troops */
533     snxtitem_xy(&ni, EF_LAND, sp->sct_x, sp->sct_y);
534     while (NULL != (lp = nxtitemp(&ni))) {
535         if (lp->lnd_own != sp->sct_own)
536             continue;
537         if (lp->lnd_ship >= 0)
538             continue;
539         if (!(lchr[(int)lp->lnd_type].l_flags & L_SECURITY))
540             continue;
541
542         mc -= (lp->lnd_effic / 100.0) * lp->lnd_item[I_MILIT] * 2.0;
543         lnd_dies_fighting_che(lp);
544         if (mc <= 0)
545             return;
546     }
547
548     /* Hmm.. everyone dead.. too bad */
549 }
550
551 static void
552 lnd_dies_fighting_che(struct lndstr *lp)
553 {
554     int i, j;
555     struct lndstr *clp;
556     struct plnstr *cpp;
557     struct nukstr *cnp;
558
559     lp->lnd_effic = 0;
560     lnd_submil(lp, 1000);       /* Remove 'em all */
561     wu(0, lp->lnd_own, "%s dies fighting guerrillas in %s\n",
562        prland(lp), xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
563     makelost(EF_LAND, lp->lnd_own, lp->lnd_uid, lp->lnd_x, lp->lnd_y);
564     lp->lnd_own = 0;
565
566     /* Take dead lp off its carrier */
567     if (lp->lnd_land >= 0) {
568         lnd_carrier_change(lp, EF_LAND, lp->lnd_land, -1);
569         lp->lnd_land = -1;
570     }
571
572     /* Unload lp's land unit cargo */
573     for (i = lnd_first_on_land(lp); i >= 0; i = lnd_next_on_unit(i)) {
574         clp = getlandp(i);
575         if (CANT_HAPPEN(!clp))
576             continue;
577         lnd_carrier_change(clp, EF_LAND, clp->lnd_land, -1);
578         clp->lnd_land = -1;
579     }
580
581     /* Destroy lp's plane cargo */
582     for (i = pln_first_on_land(lp); i >= 0; i = pln_next_on_unit(i)) {
583         cpp = getplanep(i);
584         if (CANT_HAPPEN(!cpp))
585             continue;
586         pln_carrier_change(cpp, EF_LAND, cpp->pln_land, -1);
587         makelost(EF_PLANE, cpp->pln_own, i, cpp->pln_x, cpp->pln_y);
588         wu(0, cpp->pln_own, "%s lost!\n", prplane(cpp));
589         cpp->pln_own = 0;
590         cpp->pln_effic = 0;
591         cpp->pln_land = -1;
592
593         j = nuk_on_plane(cpp);
594         if (j >= 0) {
595             cnp = getnukep(j);
596             if (CANT_HAPPEN(!cnp))
597                 continue;
598             nuk_carrier_change(cnp, EF_PLANE, cnp->nuk_plane, -1);
599             makelost(EF_NUKE, cnp->nuk_own, j, cnp->nuk_x, cnp->nuk_y);
600             wu(0, cnp->nuk_own, "%s lost!\n", prnuke(cnp));
601             cnp->nuk_own = 0;
602             cnp->nuk_effic = 0;
603             cnp->nuk_plane = -1;
604         }
605     }
606 }