00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #include <config.h>
00037
00038 #include <math.h>
00039 #include <stdlib.h>
00040 #include "combat.h"
00041 #include "damage.h"
00042 #include "file.h"
00043 #include "misc.h"
00044 #include "mission.h"
00045 #include "nsc.h"
00046 #include "optlist.h"
00047 #include "path.h"
00048 #include "player.h"
00049 #include "prototypes.h"
00050 #include "xy.h"
00051 #include "empobj.h"
00052 #include "unit.h"
00053
00054 static void lnd_mess(char *, struct ulist *);
00055 static int lnd_hit_mine(struct lndstr *, struct lchrstr *);
00056
00057 double
00058 attack_val(int combat_mode, struct lndstr *lp)
00059 {
00060 int men;
00061 double value;
00062 struct lchrstr *lcp;
00063
00064 if (lp->lnd_effic < LAND_MINEFF) {
00065 putland(lp->lnd_uid, lp);
00066 return 0;
00067 }
00068
00069 lcp = &lchr[(int)lp->lnd_type];
00070
00071
00072
00073
00074 if (lcp->l_flags & L_SPY && combat_mode == A_ASSAULT)
00075 return 1;
00076
00077 men = lp->lnd_item[I_MILIT];
00078 value = men * lp->lnd_att * lp->lnd_effic / 100.0;
00079
00080 switch (combat_mode) {
00081 case A_ATTACK:
00082 return value;
00083 case A_ASSAULT:
00084 if (!(lcp->l_flags & L_MARINE))
00085 return assault_penalty * value;
00086 break;
00087 case A_BOARD:
00088 if (!(lcp->l_flags & L_MARINE))
00089 return assault_penalty * men;
00090 }
00091
00092 return value;
00093 }
00094
00095 double
00096 defense_val(struct lndstr *lp)
00097 {
00098 int men;
00099 double value;
00100 struct lchrstr *lcp;
00101
00102 if (lp->lnd_effic < LAND_MINEFF) {
00103 putland(lp->lnd_uid, lp);
00104 return 0;
00105 }
00106
00107 lcp = &lchr[(int)lp->lnd_type];
00108
00109 men = lp->lnd_item[I_MILIT];
00110
00111 if ((lp->lnd_ship >= 0 || lp->lnd_land >= 0) &&
00112 !(lcp->l_flags & L_MARINE))
00113 return men;
00114
00115 value = men * lp->lnd_def * lp->lnd_effic / 100.0;
00116 value *= ((double)land_mob_max + lp->lnd_harden) / land_mob_max;
00117
00118
00119
00120 if (value < 1.0 && men > 0 && !(lcp->l_flags & L_SPY))
00121 return 1;
00122
00123 return value;
00124 }
00125
00126 void
00127 lnd_print(struct ulist *llp, char *s)
00128 {
00129 if (llp->unit.land.lnd_own == player->cnum)
00130 pr("%s %s\n", prland(&llp->unit.land), s);
00131 else
00132 wu(0, llp->unit.land.lnd_own, "%s %s\n", prland(&llp->unit.land), s);
00133 }
00134
00135 void
00136 lnd_delete(struct ulist *llp, char *s)
00137 {
00138 if (s)
00139 lnd_print(llp, s);
00140 putland(llp->unit.land.lnd_uid, &llp->unit.land);
00141 emp_remque((struct emp_qelem *)llp);
00142 free(llp);
00143 }
00144
00145 int
00146 lnd_take_casualty(int combat_mode, struct ulist *llp, int cas)
00147
00148
00149 {
00150 int eff_eq;
00151 int n;
00152 int biggest;
00153 int civs;
00154 int nowned;
00155 coord ret_x, ret_y;
00156 coord bx, by;
00157 struct sctstr sect;
00158 int ret_chance;
00159 char buf[1024];
00160 int taken;
00161 int nowhere_to_go = 0;
00162 struct sctstr rsect;
00163 double mobcost, bmcost;
00164 signed char orig;
00165 int mob;
00166
00167 taken = llp->unit.land.lnd_item[I_MILIT];
00168
00169 if (((struct lchrstr *)llp->chrp)->l_flags & L_SPY) {
00170 eff_eq = 100;
00171 llp->unit.land.lnd_effic = 0;
00172 } else {
00173 eff_eq = ldround(cas * 100.0 /
00174 ((struct lchrstr *)llp->chrp)->l_mil, 1);
00175 llp->unit.land.lnd_effic -= eff_eq;
00176 lnd_submil(&llp->unit.land, cas);
00177 }
00178
00179 if (llp->unit.land.lnd_effic < LAND_MINEFF) {
00180 sprintf(buf, "dies %s %s!",
00181 combat_mode ? att_mode[combat_mode] : "defending",
00182 xyas(llp->unit.land.lnd_x, llp->unit.land.lnd_y,
00183 llp->unit.land.lnd_own));
00184 lnd_delete(llp, buf);
00185
00186 return taken;
00187 } else {
00188
00189 taken = taken - llp->unit.land.lnd_item[I_MILIT];
00190 }
00191
00192 if (llp->unit.land.lnd_effic >= llp->unit.land.lnd_retreat)
00193 return taken;
00194
00195
00196 if (llp->unit.land.lnd_ship >= 0 && combat_mode == A_DEFEND)
00197 return taken;
00198
00199
00200 if (llp->unit.land.lnd_land >= 0 && combat_mode == A_DEFEND)
00201 return taken;
00202
00203
00204
00205 ret_chance = llp->unit.land.lnd_retreat - llp->unit.land.lnd_effic;
00206 if (roll(100) < ret_chance) {
00207 pr("\n");
00208 lnd_print(llp, "fails morale check!");
00209 llp->unit.land.lnd_mission = 0;
00210 llp->unit.land.lnd_harden = 0;
00211 if (llp->unit.land.lnd_ship >= 0 || llp->unit.land.lnd_land >= 0)
00212 nowhere_to_go = 1;
00213 else if (combat_mode == A_DEFEND) {
00214
00215
00216
00217
00218
00219
00220 biggest = -1;
00221 nowned = 0;
00222 for (n = 1; n <= 6; ++n) {
00223 ret_x = llp->unit.land.lnd_x + diroff[n][0];
00224 ret_y = llp->unit.land.lnd_y + diroff[n][1];
00225 getsect(ret_x, ret_y, §);
00226 if (sect.sct_own != llp->unit.land.lnd_own)
00227 continue;
00228 if (sect.sct_type == SCT_MOUNT)
00229 continue;
00230 mobcost = lnd_mobcost(&llp->unit.land, &rsect);
00231 if (mobcost < 0)
00232 continue;
00233 ++nowned;
00234 civs = sect.sct_item[I_CIVIL];
00235 if (civs > biggest) {
00236 biggest = civs;
00237 bx = sect.sct_x;
00238 by = sect.sct_y;
00239 bmcost = mobcost;
00240 }
00241 }
00242 if (!nowned)
00243 nowhere_to_go = 1;
00244 else {
00245
00246 llp->unit.land.lnd_x = bx;
00247 llp->unit.land.lnd_y = by;
00248
00249 getsect(bx, by, &rsect);
00250 mob = llp->unit.land.lnd_mobil - (int)bmcost;
00251 if (mob < -127)
00252 mob = -127;
00253 orig = llp->unit.land.lnd_mobil;
00254 llp->unit.land.lnd_mobil = (signed char)mob;
00255 if (llp->unit.land.lnd_mobil > orig)
00256 llp->unit.land.lnd_mobil = -127;
00257 sprintf(buf, "retreats at %d%% efficiency to %s!",
00258 llp->unit.land.lnd_effic,
00259 xyas(bx, by, llp->unit.land.lnd_own));
00260 lnd_delete(llp, buf);
00261 }
00262 } else {
00263 sprintf(buf, "leaves the battlefield at %d%% efficiency",
00264 llp->unit.land.lnd_effic);
00265 if ((llp->unit.land.lnd_mobil - (int)llp->mobil) < -127)
00266 llp->unit.land.lnd_mobil = -127;
00267 else
00268 llp->unit.land.lnd_mobil -= (int)llp->mobil;
00269 llp->mobil = 0.0;
00270 lnd_delete(llp, buf);
00271 }
00272 }
00273 if (nowhere_to_go) {
00274
00275 llp->unit.land.lnd_effic -= 10;
00276 lnd_submil(&llp->unit.land, ((struct lchrstr *)llp->chrp)->l_mil / 10);
00277 if (llp->unit.land.lnd_effic < LAND_MINEFF)
00278 lnd_delete(llp, "has nowhere to retreat, and dies!");
00279 else
00280 lnd_print(llp,
00281 "has nowhere to retreat and takes extra losses!");
00282 }
00283
00284 return taken;
00285 }
00286
00287 void
00288 lnd_takemob(struct emp_qelem *list, double loss)
00289 {
00290 struct emp_qelem *qp, *next;
00291 struct ulist *llp;
00292 int new;
00293 int mcost = ldround(combat_mob * loss, 1);
00294
00295 for (qp = list->q_forw; qp != list; qp = next) {
00296 next = qp->q_forw;
00297 llp = (struct ulist *)qp;
00298
00299
00300
00301
00302
00303
00304
00305 new = llp->unit.land.lnd_mobil - mcost;
00306 if (new < -127)
00307 new = -127;
00308 llp->unit.land.lnd_mobil = (signed char)new;
00309 }
00310 }
00311
00312 void
00313 lnd_submil(struct lndstr *lp, int num)
00314 {
00315 int new = lp->lnd_item[I_MILIT] - num;
00316 lp->lnd_item[I_MILIT] = new < 0 ? 0 : new;
00317 }
00318
00319 int
00320 lnd_spyval(struct lndstr *lp)
00321 {
00322 if (lchr[(int)lp->lnd_type].l_flags & L_RECON)
00323 return lp->lnd_spy * (lp->lnd_effic / 100.0) + 2;
00324 else
00325 return lp->lnd_spy * (lp->lnd_effic / 100.0);
00326 }
00327
00328 double
00329 intelligence_report(int destination, struct lndstr *lp, int spy,
00330 char *mess)
00331 {
00332 struct lchrstr *lcp;
00333 char buf1[80], buf2[80], buf3[80];
00334 double estimate = 0.0;
00335
00336 if (destination == 0)
00337 return 0;
00338
00339 if (lp->lnd_own == 0)
00340 return 0;
00341
00342 lcp = &lchr[(int)lp->lnd_type];
00343
00344 memset(buf1, 0, sizeof(buf1));
00345 memset(buf2, 0, sizeof(buf2));
00346 memset(buf3, 0, sizeof(buf3));
00347 if (chance((spy + lp->lnd_vis) / 10.0)) {
00348 if (destination == player->cnum)
00349 pr("%s %s", mess, prland(lp));
00350 else
00351 sprintf(buf1, "%s %s", mess, prland(lp));
00352
00353 estimate = lp->lnd_item[I_MILIT];
00354
00355 if (chance((spy + lp->lnd_vis) / 20.0)) {
00356 if (destination == player->cnum)
00357 pr(" (eff %d, mil %d",
00358 roundintby(lp->lnd_effic, 5),
00359 roundintby(lp->lnd_item[I_MILIT], 10));
00360 else
00361 sprintf(buf2, " (eff %d, mil %d",
00362 roundintby(lp->lnd_effic, 5),
00363 roundintby(lp->lnd_item[I_MILIT], 10));
00364 estimate = lp->lnd_item[I_MILIT] * lp->lnd_effic / 100.0;
00365
00366 if (chance((spy + lp->lnd_vis) / 20.0)) {
00367 int t;
00368 t = lp->lnd_tech - 20 + roll(40);
00369 t = MAX(t, 0);
00370 if (destination == player->cnum)
00371 pr(", tech %d)\n", t);
00372 else
00373 sprintf(buf3, ", tech %d)\n", t);
00374 } else {
00375 if (destination == player->cnum)
00376 pr(")\n");
00377 else
00378 sprintf(buf3, ")\n");
00379 }
00380 } else {
00381 if (destination == player->cnum)
00382 pr("\n");
00383 else
00384 sprintf(buf2, "\n");
00385 }
00386 }
00387
00388 if (destination != player->cnum) {
00389 wu(0, destination, "%s%s%s", buf1, buf2, buf3);
00390 }
00391
00392 if (lp->lnd_ship < 0 || lcp->l_flags & L_MARINE)
00393 estimate *= lp->lnd_def;
00394
00395 return estimate;
00396 }
00397
00398
00399
00400
00401 int
00402 count_sect_units(struct sctstr *sp)
00403 {
00404 int count = 0;
00405 struct nstr_item ni;
00406 struct lndstr land;
00407
00408 snxtitem_all(&ni, EF_LAND);
00409 while (nxtitem(&ni, &land)) {
00410 if (!land.lnd_own)
00411 continue;
00412 if (land.lnd_x != sp->sct_x || land.lnd_y != sp->sct_y)
00413 continue;
00414
00415 if (lchr[(int)land.lnd_type].l_flags & L_SPY) {
00416 if (!(chance(LND_SPY_DETECT_CHANCE(land.lnd_effic))))
00417 continue;
00418 }
00419
00420 ++count;
00421 }
00422
00423 return count;
00424 }
00425
00426 void
00427 count_units(struct shpstr *sp)
00428 {
00429 struct nstr_item ni;
00430 struct lndstr land;
00431 int nland = 0;
00432
00433 if (sp->shp_effic < SHIP_MINEFF)
00434 return;
00435
00436 snxtitem_xy(&ni, EF_LAND, sp->shp_x, sp->shp_y);
00437 while (nxtitem(&ni, &land)) {
00438 if (land.lnd_own == 0)
00439 continue;
00440 if (land.lnd_ship == sp->shp_uid)
00441 nland++;
00442 }
00443
00444 if (sp->shp_nland != nland) {
00445 sp->shp_nland = nland;
00446 putship(sp->shp_uid, sp);
00447 }
00448 }
00449
00450 void
00451 lnd_count_units(struct lndstr *lp)
00452 {
00453 struct nstr_item ni;
00454 struct lndstr land;
00455 int nland = 0;
00456
00457 if (lp->lnd_effic < LAND_MINEFF)
00458 return;
00459
00460 snxtitem_xy(&ni, EF_LAND, lp->lnd_x, lp->lnd_y);
00461 while (nxtitem(&ni, &land)) {
00462 if (land.lnd_own == 0)
00463 continue;
00464 if (land.lnd_land == lp->lnd_uid)
00465 nland++;
00466 }
00467
00468 if (lp->lnd_nland != nland) {
00469 lp->lnd_nland = nland;
00470 putland(lp->lnd_uid, lp);
00471 }
00472 }
00473
00474 void
00475 lnd_sel(struct nstr_item *ni, struct emp_qelem *list)
00476 {
00477 struct lndstr land;
00478 struct lchrstr *lcp;
00479 struct ulist *llp;
00480 int this_mot;
00481 int mobtype = MOB_MOVE;
00482
00483 emp_initque(list);
00484 while (nxtitem(ni, &land)) {
00485 if (!player->owner)
00486 continue;
00487 if (opt_MARKET) {
00488 if (ontradingblock(EF_LAND, &land)) {
00489 pr("unit #%d inelligible - it's for sale.\n",
00490 land.lnd_uid);
00491 continue;
00492 }
00493 }
00494
00495
00496
00497
00498 this_mot = lnd_mobtype(&land);
00499 if (this_mot != mobtype) {
00500 if (mobtype == MOB_MOVE)
00501 mobtype = this_mot;
00502 else if (mobtype == MOB_MARCH) {
00503 pr("%s is a train and can't march with the leader.\n",
00504 prland(&land));
00505 continue;
00506 } else {
00507 pr("%s can't rail-march with the leading train.\n",
00508 prland(&land));
00509 continue;
00510 }
00511 }
00512
00513 lcp = &lchr[(int)land.lnd_type];
00514 land.lnd_mission = 0;
00515 land.lnd_rflags = 0;
00516 land.lnd_harden = 0;
00517 memset(land.lnd_rpath, 0, sizeof(land.lnd_rpath));
00518 putland(land.lnd_uid, &land);
00519 llp = malloc(sizeof(struct ulist));
00520 llp->chrp = (struct empobj_chr *)lcp;
00521 llp->unit.land = land;
00522 llp->mobil = land.lnd_mobil;
00523 emp_insque(&llp->queue, list);
00524 }
00525 }
00526
00527
00528 void
00529 lnd_mar(struct emp_qelem *list, double *minmobp, double *maxmobp,
00530 int *togetherp, natid actor)
00531 {
00532 struct emp_qelem *qp;
00533 struct emp_qelem *next;
00534 struct ulist *llp;
00535 struct sctstr sect;
00536 struct lndstr land;
00537 coord allx;
00538 coord ally;
00539 int first = 1;
00540 char mess[128];
00541 int rel;
00542
00543 *minmobp = 9876.0;
00544 *maxmobp = -9876.0;
00545 *togetherp = 1;
00546 for (qp = list->q_back; qp != list; qp = next) {
00547 next = qp->q_back;
00548 llp = (struct ulist *)qp;
00549 getland(llp->unit.land.lnd_uid, &land);
00550 if (land.lnd_own != actor) {
00551 mpr(actor, "%s was disbanded at %s\n",
00552 prland(&land), xyas(land.lnd_x, land.lnd_y, land.lnd_own));
00553 emp_remque((struct emp_qelem *)llp);
00554 free(llp);
00555 continue;
00556 }
00557 if (land.lnd_ship >= 0) {
00558 lnd_mess("is on a ship", llp);
00559 continue;
00560 }
00561 if (land.lnd_land >= 0) {
00562 lnd_mess("is on a unit", llp);
00563 continue;
00564 }
00565 if (!getsect(land.lnd_x, land.lnd_y, §)) {
00566 lnd_mess("was sucked into the sky by a strange looking spaceland", llp);
00567 continue;
00568 }
00569 if (!(lchr[(int)llp->unit.land.lnd_type].l_flags & L_SPY) &&
00570 !(lchr[(int)llp->unit.land.lnd_type].l_flags & L_TRAIN) &&
00571 llp->unit.land.lnd_item[I_MILIT] == 0) {
00572 lnd_mess("has no mil on it to guide it", llp);
00573 continue;
00574 }
00575 rel = getrel(getnatp(sect.sct_own), player->cnum);
00576 if (sect.sct_own != land.lnd_own && rel != ALLIED &&
00577 !(lchr[(int)llp->unit.land.lnd_type].l_flags & L_SPY) &&
00578 sect.sct_own) {
00579 sprintf(mess, "has been kidnapped by %s", cname(sect.sct_own));
00580 lnd_mess(mess, llp);
00581 continue;
00582 }
00583 if (first) {
00584 allx = land.lnd_x;
00585 ally = land.lnd_y;
00586 first = 0;
00587 }
00588 if (land.lnd_x != allx || land.lnd_y != ally)
00589 *togetherp = 0;
00590 if (land.lnd_mobil + 1 < (int)llp->mobil) {
00591 llp->mobil = land.lnd_mobil;
00592 }
00593 if (llp->mobil < *minmobp)
00594 *minmobp = llp->mobil;
00595 if (llp->mobil > *maxmobp)
00596 *maxmobp = llp->mobil;
00597 llp->unit.land = land;
00598 }
00599 }
00600
00601 void
00602 lnd_sweep(struct emp_qelem *land_list, int verbose, int takemob,
00603 natid actor)
00604 {
00605 struct emp_qelem *qp;
00606 struct emp_qelem *next;
00607 struct ulist *llp;
00608 struct sctstr sect;
00609 int mines, m, max, sshells, lshells;
00610
00611 for (qp = land_list->q_back; qp != land_list; qp = next) {
00612 next = qp->q_back;
00613 llp = (struct ulist *)qp;
00614 if (!(((struct lchrstr *)llp->chrp)->l_flags & L_ENGINEER)) {
00615 if (verbose)
00616 mpr(actor, "%s is not an engineer!\n",
00617 prland(&llp->unit.land));
00618 continue;
00619 }
00620 if (takemob && llp->mobil < 0.0) {
00621 if (verbose)
00622 lnd_mess("is out of mobility", llp);
00623 continue;
00624 }
00625 getsect(llp->unit.land.lnd_x, llp->unit.land.lnd_y, §);
00626 if (sect.sct_oldown == llp->unit.land.lnd_own) {
00627 if (verbose)
00628 mpr(actor,
00629 "%s is in a sector completely owned by you. Don't bother digging up mines there!\n",
00630 prland(&llp->unit.land));
00631 continue;
00632 }
00633 if (sect.sct_type == SCT_BSPAN) {
00634 if (verbose)
00635 mpr(actor, "%s is on a bridge. No mines there!\n",
00636 prland(&llp->unit.land));
00637 continue;
00638 }
00639 if (takemob) {
00640 llp->mobil -= lnd_pathcost(&llp->unit.land, 0.2);
00641 llp->unit.land.lnd_mobil = (int)llp->mobil;
00642 llp->unit.land.lnd_harden = 0;
00643 }
00644 putland(llp->unit.land.lnd_uid, &llp->unit.land);
00645 if (!(mines = sect.sct_mines))
00646 continue;
00647 max = ((struct lchrstr *)llp->chrp)->l_item[I_SHELL];
00648 lshells = llp->unit.land.lnd_item[I_SHELL];
00649 sshells = sect.sct_item[I_SHELL];
00650 for (m = 0; mines > 0 && m < max * 2; m++) {
00651 if (chance(0.5 * ((struct lchrstr *)llp->chrp)->l_att)) {
00652 mpr(actor, "Sweep...\n");
00653 mines--;
00654 if (lshells < max)
00655 ++lshells;
00656 else if (sshells < ITEM_MAX)
00657 ++sshells;
00658 }
00659 }
00660 sect.sct_mines = mines;
00661 llp->unit.land.lnd_item[I_SHELL] = lshells;
00662 sect.sct_item[I_SHELL] = sshells;
00663 putland(llp->unit.land.lnd_uid, &llp->unit.land);
00664 putsect(§);
00665 }
00666 }
00667
00668 static int
00669 contains_engineer(struct emp_qelem *list)
00670 {
00671 struct emp_qelem *qp;
00672 struct emp_qelem *next;
00673 struct ulist *llp;
00674
00675 for (qp = list->q_back; qp != list; qp = next) {
00676 next = qp->q_back;
00677 llp = (struct ulist *)qp;
00678 if (((struct lchrstr *)llp->chrp)->l_flags & L_ENGINEER)
00679 return 1;
00680 }
00681 return 0;
00682 }
00683
00684 int
00685 lnd_check_mines(struct emp_qelem *land_list)
00686 {
00687 struct emp_qelem *qp;
00688 struct emp_qelem *next;
00689 struct ulist *llp;
00690 struct sctstr sect;
00691 int stopping = 0;
00692 int with_eng = contains_engineer(land_list);
00693
00694 for (qp = land_list->q_back; qp != land_list; qp = next) {
00695 next = qp->q_back;
00696 llp = (struct ulist *)qp;
00697 getsect(llp->unit.land.lnd_x, llp->unit.land.lnd_y, §);
00698 if (sect.sct_oldown == llp->unit.land.lnd_own)
00699 continue;
00700 if (sect.sct_type == SCT_BSPAN)
00701 continue;
00702 if (!sect.sct_mines)
00703 continue;
00704 if (chance(DMINE_LHITCHANCE(sect.sct_mines) / (1 + 2 * with_eng))) {
00705 lnd_hit_mine(&llp->unit.land, ((struct lchrstr *)llp->chrp));
00706 sect.sct_mines--;
00707 putsect(§);
00708 putland(llp->unit.land.lnd_uid, &llp->unit.land);
00709 if (!llp->unit.land.lnd_own) {
00710 stopping = 1;
00711 emp_remque(qp);
00712 free(qp);
00713 }
00714 }
00715 }
00716 return stopping;
00717 }
00718
00719 static void
00720 lnd_mess(char *str, struct ulist *llp)
00721 {
00722 mpr(llp->unit.land.lnd_own, "%s %s & stays in %s\n",
00723 prland(&llp->unit.land),
00724 str, xyas(llp->unit.land.lnd_x, llp->unit.land.lnd_y,
00725 llp->unit.land.lnd_own));
00726 if (llp->mobil < -127)
00727 llp->mobil = -127;
00728 llp->unit.land.lnd_mobil = llp->mobil;
00729 putland(llp->unit.land.lnd_uid, &llp->unit.land);
00730 emp_remque((struct emp_qelem *)llp);
00731 free(llp);
00732 }
00733
00734 static int
00735 lnd_count(struct emp_qelem *list)
00736 {
00737 struct emp_qelem *qp;
00738 struct emp_qelem *next;
00739 int count = 0;
00740
00741 for (qp = list->q_back; qp != list; qp = next) {
00742 next = qp->q_back;
00743 ++count;
00744 }
00745 return count;
00746 }
00747
00748 static int
00749 lnd_damage(struct emp_qelem *list, int totdam)
00750 {
00751 struct emp_qelem *qp;
00752 struct emp_qelem *next;
00753 struct ulist *llp;
00754 int dam;
00755 int count;
00756
00757 if (!totdam || !(count = lnd_count(list)))
00758 return 0;
00759 dam = ldround((double)totdam / count, 1);
00760 for (qp = list->q_back; qp != list; qp = next) {
00761 next = qp->q_back;
00762 llp = (struct ulist *)qp;
00763
00764 getland(llp->unit.land.lnd_uid, &llp->unit.land);
00765 landdamage(&llp->unit.land, dam);
00766 putland(llp->unit.land.lnd_uid, &llp->unit.land);
00767 if (!llp->unit.land.lnd_own) {
00768 emp_remque(qp);
00769 free(qp);
00770 }
00771 }
00772 return dam;
00773 }
00774
00775 static int
00776 lnd_easiest_target(struct emp_qelem *list)
00777 {
00778 struct emp_qelem *qp;
00779 struct emp_qelem *next;
00780 struct ulist *llp;
00781 int hard;
00782 int easiest = 9876;
00783 int count = 0;
00784
00785 for (qp = list->q_back; qp != list; qp = next) {
00786 next = qp->q_back;
00787 llp = (struct ulist *)qp;
00788 hard = lnd_hardtarget(&llp->unit.land);
00789 if (hard < easiest)
00790 easiest = hard;
00791 ++count;
00792 }
00793 return easiest - count;
00794 }
00795
00796 static int
00797 lnd_missile_interdiction(struct emp_qelem *list, coord newx, coord newy,
00798 natid victim)
00799 {
00800 int dam;
00801 struct emp_qelem msl_list, *qp, *newqp;
00802
00803 msl_sel(&msl_list, newx, newy, victim, P_T, P_MAR, MI_INTERDICT);
00804
00805 dam = msl_launch_mindam(&msl_list, newx, newy,
00806 lnd_easiest_target(list), EF_LAND,
00807 lnd_count(list) * 20, "troops", victim,
00808 MI_INTERDICT);
00809 if (dam) {
00810 mpr(victim, "missile interdiction mission does %d damage!\n", dam);
00811 collateral_damage(newx, newy, dam, 0);
00812 }
00813 qp = msl_list.q_forw;
00814 while (qp != msl_list.q_forw) {
00815 newqp = qp->q_forw;
00816 emp_remque(qp);
00817 free(qp);
00818 qp = newqp;
00819 }
00820 return dam;
00821 }
00822
00823 #if 0
00824
00825
00826 static int
00827 lnd_fort_interdiction(struct emp_qelem *list,
00828 coord newx, coord newy, natid victim)
00829 {
00830 struct nstr_sect ns;
00831 struct sctstr fsect;
00832 int trange, range;
00833 double guneff;
00834 int shell, gun;
00835 int dam;
00836 int totdam = 0;
00837 int i;
00838
00839 snxtsct_dist(&ns, newx, newy, fort_max_interdiction_range);
00840 while (nxtsct(&ns, &fsect)) {
00841 if (fsect.sct_own == 0)
00842 continue;
00843 if (fsect.sct_own == victim)
00844 continue;
00845 if (getrel(getnatp(fsect.sct_own), victim) >= NEUTRAL)
00846 continue;
00847 gun = fsect.sct_item[I_GUN];
00848 if (gun < 1)
00849 continue;
00850 range = roundrange(fortrange(&fsect));
00851 trange = mapdist(newx, newy, fsect.sct_x, fsect.sct_y);
00852 if (trange > range)
00853 continue;
00854 if (fsect.sct_item[I_MILIT] < 5)
00855 continue;
00856 shell = fsect.sct_item[I_SHELL];
00857 if (shell < 1)
00858 shell += supply_commod(fsect.sct_own, fsect.sct_x, fsect.sct_y,
00859 I_SHELL, 1);
00860 if (shell < 1)
00861 continue;
00862 shell--;
00863 fsect.sct_item[I_SHELL] = shell;
00864 putsect(&fsect);
00865 if (gun > 7)
00866 gun = 7;
00867 guneff = landgun((int)fsect.sct_effic, gun);
00868 dam = (int)guneff;
00869 totdam += dam;
00870 mpr(victim, "Incoming fire does %d damage!\n", dam);
00871 wu(0, fsect.sct_own,
00872 "%s fires at %s land units in %s for %d!\n",
00873 xyas(fsect.sct_x, fsect.sct_y,
00874 fsect.sct_own),
00875 cname(victim), xyas(newx, newy, fsect.sct_own), dam);
00876 nreport(fsect.sct_own, N_SCT_SHELL, victim, 1);
00877 }
00878 if (totdam > 0)
00879 return lnd_damage(list, totdam);
00880 return 0;
00881 }
00882 #endif
00883
00884 int
00885 lnd_interdict(struct emp_qelem *list, coord newx, coord newy, natid victim)
00886 {
00887 int stopping = 0;
00888
00889 #if 0
00890 if (!opt_NO_FORT_FIRE)
00891
00892 stopping |= lnd_fort_interdiction(list, newx, newy, victim);
00893 #endif
00894
00895 stopping |=
00896 lnd_damage(list,
00897 unit_interdict(newx, newy, victim, "land units",
00898 lnd_easiest_target(list), MI_INTERDICT));
00899
00900 stopping |=
00901 lnd_damage(list,
00902 lnd_missile_interdiction(list, newx, newy, victim));
00903 return stopping;
00904 }
00905
00906
00907 int
00908 lnd_hardtarget(struct lndstr *lp)
00909 {
00910 struct sctstr sect;
00911
00912 getsect(lp->lnd_x, lp->lnd_y, §);
00913 return (int)((lp->lnd_effic / 100.0) *
00914 (10 + dchr[sect.sct_type].d_dstr * 2 + lp->lnd_spd / 2.0
00915 - lp->lnd_vis));
00916 }
00917
00918 static int
00919 lnd_hit_mine(struct lndstr *lp, struct lchrstr *lcp)
00920 {
00921 int m;
00922
00923 mpr(lp->lnd_own, "Blammo! Landmines detected in %s! ",
00924 xyas(lp->lnd_x, lp->lnd_y, lp->lnd_own));
00925
00926 nreport(lp->lnd_own, N_LHIT_MINE, 0, 1);
00927
00928 m = MINE_LDAMAGE();
00929 if (lcp->l_flags & L_ENGINEER)
00930 m /= 2;
00931
00932 landdamage(lp, m);
00933 return m;
00934 }
00935
00936 double
00937 lnd_pathcost(struct lndstr *lp, double pathcost)
00938 {
00939 double effspd;
00940
00941 effspd = lp->lnd_spd;
00942 if (lchr[(int)lp->lnd_type].l_flags & L_SUPPLY)
00943 effspd *= lp->lnd_effic * 0.01;
00944
00945
00946
00947
00948
00949
00950
00951 return pathcost * 5.0 * speed_factor(effspd, lp->lnd_tech);
00952 }
00953
00954 int
00955 lnd_mobtype(struct lndstr *lp)
00956 {
00957 return (lchr[(int)lp->lnd_type].l_flags & L_TRAIN)
00958 ? MOB_RAIL : MOB_MARCH;
00959 }
00960
00961 double
00962 lnd_mobcost(struct lndstr *lp, struct sctstr *sp)
00963 {
00964 return lnd_pathcost(lp, sector_mcost(sp, lnd_mobtype(lp)));
00965 }
00966
00967 int
00968 lnd_mar_one_sector(struct emp_qelem *list, int dir, natid actor,
00969 int together)
00970 {
00971 struct sctstr sect, osect;
00972 struct emp_qelem *qp;
00973 struct emp_qelem *qp2;
00974 struct emp_qelem *next;
00975 struct ulist *llp;
00976 struct emp_qelem cur, done;
00977 coord dx;
00978 coord dy;
00979 coord newx;
00980 coord newy;
00981 int stopping = 0;
00982 int visible;
00983 int stop;
00984 char dp[80];
00985 int rel;
00986 int oldown;
00987
00988 if (dir <= DIR_STOP || dir >= DIR_VIEW) {
00989 unit_put(list, actor);
00990 return 1;
00991 }
00992 dx = diroff[dir][0];
00993 dy = diroff[dir][1];
00994 for (qp = list->q_back; qp != list; qp = next) {
00995 next = qp->q_back;
00996 llp = (struct ulist *)qp;
00997 getsect(llp->unit.land.lnd_x, llp->unit.land.lnd_y, &osect);
00998 oldown = osect.sct_own;
00999 newx = xnorm(llp->unit.land.lnd_x + dx);
01000 newy = ynorm(llp->unit.land.lnd_y + dy);
01001 getsect(newx, newy, §);
01002 rel = getrel(getnatp(sect.sct_own), player->cnum);
01003 if ((sect.sct_own != actor && rel != ALLIED &&
01004 !(lchr[(int)llp->unit.land.lnd_type].l_flags & L_SPY) &&
01005 sect.sct_own) || (sect.sct_type == SCT_WATER ||
01006 sect.sct_type == SCT_SANCT ||
01007 sect.sct_type == SCT_WASTE)) {
01008 if (together) {
01009 pr("can't go to %s\n", xyas(newx, newy, actor));
01010 return 1;
01011 } else {
01012 sprintf(dp, "can't go to %s", xyas(newx, newy, actor));
01013 lnd_mess(dp, llp);
01014 continue;
01015 }
01016 }
01017 if ((!intrchr[INT_RAIL].in_enable || sect.sct_rail == 0)
01018 && lnd_mobtype(&llp->unit.land) == MOB_RAIL) {
01019 if (together) {
01020 pr("no rail system in %s\n", xyas(newx, newy, actor));
01021 return 1;
01022 } else {
01023 sprintf(dp, "has no rail system in %s",
01024 xyas(newx, newy, actor));
01025 lnd_mess(dp, llp);
01026 continue;
01027 }
01028 }
01029
01030
01031 if (would_abandon(&osect, I_CIVIL, 0, &llp->unit.land)) {
01032 stop = 0;
01033 if (!want_to_abandon(&osect, I_CIVIL, 0, &llp->unit.land)) {
01034 stop = 1;
01035 }
01036
01037 if (!check_sect_ok(§))
01038 return 1;
01039 if (!check_sect_ok(&osect))
01040 return 1;
01041 for (qp2 = list->q_back; qp2 != list; qp2 = qp2->q_back) {
01042 if (!check_land_ok(&((struct ulist *)qp2)->unit.land))
01043 return 1;
01044 }
01045 if (stop) {
01046 lnd_mess("stops", llp);
01047 continue;
01048 }
01049 }
01050 if (llp->mobil <= 0.0) {
01051 lnd_mess("is out of mobility", llp);
01052 continue;
01053 }
01054 llp->unit.land.lnd_x = newx;
01055 llp->unit.land.lnd_y = newy;
01056 llp->mobil -= lnd_mobcost(&llp->unit.land, §);
01057 llp->unit.land.lnd_mobil = (int)llp->mobil;
01058 llp->unit.land.lnd_harden = 0;
01059 putland(llp->unit.land.lnd_uid, &llp->unit.land);
01060 putsect(&osect);
01061 getsect(osect.sct_x, osect.sct_y, &osect);
01062 if (osect.sct_own != oldown && oldown == player->cnum) {
01063
01064 pr("You no longer own %s\n",
01065 xyas(osect.sct_x, osect.sct_y, player->cnum));
01066 }
01067 if (rel != ALLIED && sect.sct_own != actor && sect.sct_own) {
01068
01069 if (chance(LND_SPY_DETECT_CHANCE(llp->unit.land.lnd_effic))) {
01070 if (rel == NEUTRAL || rel == FRIENDLY) {
01071 wu(0, sect.sct_own,
01072 "%s unit spotted in %s\n", cname(player->cnum),
01073 xyas(sect.sct_x, sect.sct_y, sect.sct_own));
01074 setrel(sect.sct_own, llp->unit.land.lnd_own, HOSTILE);
01075 } else if (rel == HOSTILE || rel == AT_WAR ||
01076 rel == SITZKRIEG || rel == MOBILIZATION) {
01077 wu(0, sect.sct_own,
01078 "%s spy shot in %s\n", cname(player->cnum),
01079 xyas(sect.sct_x, sect.sct_y, sect.sct_own));
01080 pr("%s was shot and killed.\n", prland(&llp->unit.land));
01081 llp->unit.land.lnd_effic = 0;
01082 putland(llp->unit.land.lnd_uid, &llp->unit.land);
01083 lnd_delete(llp, 0);
01084 }
01085 }
01086 }
01087 }
01088 if (QEMPTY(list))
01089 return stopping;
01090 lnd_sweep(list, 0, 1, actor);
01091 stopping |= lnd_check_mines(list);
01092 if (QEMPTY(list))
01093 return stopping;
01094
01095
01096 emp_initque(&cur);
01097 emp_initque(&done);
01098 while (!QEMPTY(list)) {
01099 llp = (struct ulist *)list->q_back;
01100 newx = llp->unit.land.lnd_x;
01101 newy = llp->unit.land.lnd_y;
01102
01103 visible = 0;
01104 for (qp = list->q_back; qp != list; qp = next) {
01105 next = qp->q_back;
01106 llp = (struct ulist *)qp;
01107 if (llp->unit.land.lnd_x == newx && llp->unit.land.lnd_y == newy) {
01108 emp_remque(qp);
01109 emp_insque(qp, &cur);
01110 if (!(lchr[(int)llp->unit.land.lnd_type].l_flags & L_SPY))
01111 visible = 1;
01112 }
01113 }
01114
01115 if (visible)
01116 stopping |= lnd_interdict(&cur, newx, newy, actor);
01117
01118 for (qp = cur.q_back; qp != &cur; qp = next) {
01119 next = qp->q_back;
01120 llp = (struct ulist *)qp;
01121 emp_remque(qp);
01122 emp_insque(qp, &done);
01123 }
01124 }
01125
01126 emp_insque(list, &done);
01127 emp_remque(&done);
01128
01129 return stopping;
01130 }
01131
01132
01133
01134
01135
01136
01137 int
01138 lnd_support(natid victim, natid attacker, coord x, coord y, int defending)
01139 {
01140 struct nstr_item ni;
01141 struct lndstr land;
01142 int rel, rel2;
01143 double dam = 0.0;
01144 int dist;
01145 int shell;
01146 int gun;
01147 int range;
01148
01149 snxtitem_all(&ni, EF_LAND);
01150 while (nxtitem(&ni, &land)) {
01151 if (land.lnd_frg == 0)
01152 continue;
01153 if ((land.lnd_x == x) && (land.lnd_y == y))
01154 continue;
01155 if (land.lnd_ship >= 0)
01156 continue;
01157 if (land.lnd_land >= 0)
01158 continue;
01159 if (land.lnd_effic < LAND_MINFIREEFF)
01160 continue;
01161
01162 if (land.lnd_item[I_MILIT] <= 0)
01163 continue;
01164 rel = getrel(getnatp(land.lnd_own), attacker);
01165 rel2 = getrel(getnatp(land.lnd_own), victim);
01166 if ((land.lnd_own != attacker) &&
01167 ((rel != ALLIED) || (rel2 != AT_WAR)))
01168 continue;
01169
01170
01171 if (!has_supply(&land))
01172 continue;
01173
01174
01175 dist = mapdist(land.lnd_x, land.lnd_y, x, y);
01176
01177 range = roundrange(effrange(land.lnd_frg, land.lnd_tech));
01178 if (dist > range)
01179 continue;
01180
01181 shell = land.lnd_item[I_SHELL];
01182 gun = land.lnd_item[I_GUN];
01183
01184 if (shell == 0 || gun == 0)
01185 continue;
01186
01187 use_supply(&land);
01188 if (defending)
01189 nreport(land.lnd_own, N_FIRE_BACK, victim, 1);
01190 else
01191 nreport(land.lnd_own, N_FIRE_L_ATTACK, victim, 1);
01192 if (roll(100) < land.lnd_acc) {
01193 dam += landunitgun(land.lnd_effic, land.lnd_dam, gun,
01194 land.lnd_ammo, shell) / 2;
01195 } else {
01196 dam += landunitgun(land.lnd_effic, land.lnd_dam, gun,
01197 land.lnd_ammo, shell);
01198 }
01199 if (land.lnd_own != attacker)
01200 wu(0, land.lnd_own,
01201 "%s supported %s at %s\n",
01202 prland(&land), cname(attacker), xyas(x, y, land.lnd_own));
01203 }
01204 return (int)dam;
01205 }
01206 int
01207 lnd_can_attack(struct lndstr *lp)
01208 {
01209 struct lchrstr *lcp = &lchr[(int)lp->lnd_type];
01210
01211 if (lcp->l_flags & L_SUPPLY)
01212 return 0;
01213
01214 return 1;
01215 }
01216
01217
01218
01219
01220
01221
01222 int
01223 lnd_fortify(struct lndstr *lp, int mob)
01224 {
01225 int hard_amt;
01226 double mob_used, mult;
01227
01228 if (lp->lnd_ship >= 0 || lp->lnd_land >= 0)
01229 return 0;
01230
01231 mob_used = MIN(lp->lnd_mobil, mob);
01232 if (mob_used < 0)
01233 return 0;
01234
01235 mult = has_helpful_engineer(lp->lnd_x, lp->lnd_y, lp->lnd_own)
01236 ? 1.5 : 1.0;
01237
01238 hard_amt = (int)(mob_used * mult);
01239 if (lp->lnd_harden + hard_amt > land_mob_max) {
01240 hard_amt = land_mob_max - lp->lnd_harden;
01241 mob_used = ceil(hard_amt / mult);
01242 }
01243
01244 lp->lnd_mobil -= (int)mob_used;
01245 lp->lnd_harden += hard_amt;
01246 lp->lnd_harden = MIN(lp->lnd_harden, land_mob_max);
01247
01248 return hard_amt;
01249 }
01250
01251
01252
01253
01254 void
01255 lnd_set_tech(struct lndstr *lp, int tlev)
01256 {
01257 struct lchrstr *lcp = lchr + lp->lnd_type;
01258 int tech_diff = tlev - lcp->l_tech;
01259
01260 if (CANT_HAPPEN(tech_diff < 0)) {
01261 tlev -= tech_diff;
01262 tech_diff = 0;
01263 }
01264
01265 lp->lnd_tech = tlev;
01266 lp->lnd_att = (float)LND_ATTDEF(lcp->l_att, tech_diff);
01267 lp->lnd_def = (float)LND_ATTDEF(lcp->l_def, tech_diff);
01268 lp->lnd_vul = (int)LND_VUL(lcp->l_vul, tech_diff);
01269 lp->lnd_spd = (int)LND_SPD(lcp->l_spd, tech_diff);
01270 lp->lnd_vis = (int)LND_VIS(lcp->l_vis, tech_diff);
01271 lp->lnd_spy = (int)LND_SPY(lcp->l_spy, tech_diff);
01272 lp->lnd_rad = (int)LND_RAD(lcp->l_rad, tech_diff);
01273 lp->lnd_frg = (int)LND_FRG(lcp->l_frg, tech_diff);
01274 lp->lnd_acc = (int)LND_ACC(lcp->l_acc, tech_diff);
01275 lp->lnd_dam = (int)LND_DAM(lcp->l_dam, tech_diff);
01276 lp->lnd_ammo = (int)LND_AMM(lcp->l_ammo, tech_diff);
01277 lp->lnd_aaf = (int)LND_AAF(lcp->l_aaf, tech_diff);
01278 lp->lnd_fuelc = (int)LND_FC(lcp->l_fuelc, tech_diff);
01279 lp->lnd_fuelu = (int)LND_FU(lcp->l_fuelu, tech_diff);
01280 lp->lnd_maxlight = (int)LND_XPL(lcp->l_nxlight, tech_diff);
01281 lp->lnd_maxland = (int)LND_MXL(lcp->l_nland, tech_diff);
01282 }