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 <ctype.h>
00039 #include <math.h>
00040 #include "combat.h"
00041 #include "file.h"
00042 #include "map.h"
00043 #include "misc.h"
00044 #include "mission.h"
00045 #include "nsc.h"
00046 #include "optlist.h"
00047 #include "path.h"
00048 #include "plague.h"
00049 #include "player.h"
00050 #include "prototypes.h"
00051 #include "xy.h"
00052 #include "empobj.h"
00053 #include "unit.h"
00054
00055 #define CASUALTY_LUMP 1
00056
00057 static void ask_olist(int combat_mode, struct combat *off,
00058 struct combat *def, struct emp_qelem *olist,
00059 char *land_answer, int *a_spyp, int *a_engineerp);
00060 static void take_move_in_mob(int combat_mode, struct ulist *llp,
00061 struct combat *off, struct combat *def);
00062 static void move_in_land(int combat_mode, struct combat *off,
00063 struct emp_qelem *olist, struct combat *def);
00064 static void ask_move_in(struct combat *off, struct emp_qelem *olist,
00065 struct combat *def);
00066 static void ask_move_in_off(struct combat *off, struct combat *def);
00067
00068 static int board_abort(struct combat *off, struct combat *def);
00069 static int land_board_abort(struct combat *off, struct combat *def);
00070 static int ask_off(int combat_mode, struct combat *off,
00071 struct combat *def);
00072 static void get_dlist(struct combat *def, struct emp_qelem *list, int a_spy,
00073 int *d_spyp);
00074 static int get_ototal(int combat_mode, struct combat *off,
00075 struct emp_qelem *olist, double osupport, int check);
00076 static int get_dtotal(struct combat *def, struct emp_qelem *list,
00077 double dsupport, int check);
00078 static double att_calcodds(int, int);
00079 static int take_casualty(int combat_mode, struct combat *off,
00080 struct emp_qelem *olist);
00081
00082 static void send_reacting_units_home(struct emp_qelem *list);
00083 static int take_def(int combat_mode, struct emp_qelem *list,
00084 struct combat *off, struct combat *def);
00085
00086 static int get_land(int combat_mode, struct combat *def, int uid,
00087 struct ulist *llp, int victim_land);
00088
00089 char *att_mode[] = {
00090
00091 "defend", "attack", "assault", "paradrop", "board", "lboard"
00092 };
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119 int
00120 att_combat_init(struct combat *com, int type)
00121 {
00122 memset(com, 0, sizeof(*com));
00123 com->type = type;
00124 return type;
00125 }
00126
00127
00128
00129 static char *
00130 pr_com(int inon, struct combat *com, natid who)
00131 {
00132 if (com->type == EF_SECTOR) {
00133 return prbuf("%s%s",
00134 inon ? inon == 1 ? "in " : "into " : "",
00135 xyas(com->x, com->y, who));
00136 } else if (com->type == EF_SHIP) {
00137 return prbuf("%s%s %s(#%d)",
00138 inon ? inon == 1 ? "on " : "onto " : "",
00139 com->shp_mcp->m_name, com->shp_name,
00140 com->shp_uid);
00141 } else if (com->type == EF_LAND) {
00142 return prbuf("%s%s #%d",
00143 inon ? inon == 1 ? "on " : "onto " : "",
00144 com->lnd_lcp->l_name, com->lnd_uid);
00145 } else {
00146 return "your forces";
00147 }
00148 }
00149
00150 static char *
00151 prcom(int inon, struct combat *com)
00152 {
00153 return pr_com(inon, com, player->cnum);
00154 }
00155
00156
00157
00158
00159
00160
00161
00162 int
00163 att_get_combat(struct combat *com, int isdef)
00164 {
00165 struct sctstr sect;
00166 struct shpstr ship;
00167 struct lndstr land;
00168 int pstage;
00169 natid owner;
00170 int mil;
00171 int eff;
00172 int mob;
00173 coord x, y;
00174
00175 switch (com->type) {
00176 case EF_SECTOR:
00177 if (!getsect(com->x, com->y, §)) {
00178 pr("Bad sector: %s\n", xyas(com->x, com->y, player->cnum));
00179 return att_combat_init(com, EF_BAD);
00180 }
00181 com->sct_type = sect.sct_type;
00182 com->sct_dcp = &dchr[sect.sct_type];
00183 mil = sect.sct_item[I_MILIT];
00184 pstage = sect.sct_pstage;
00185 owner = sect.sct_own;
00186 eff = sect.sct_effic;
00187 mob = sect.sct_mobil;
00188 x = com->x;
00189 y = com->y;
00190 break;
00191 case EF_LAND:
00192 if (!getland(com->lnd_uid, &land) || !land.lnd_own) {
00193 if (isdef)
00194 pr("Land unit #%d is not in the same sector!\n",
00195 com->lnd_uid);
00196 return att_combat_init(com, EF_BAD);
00197 }
00198 if (isdef && player->owner) {
00199 pr("Boarding yourself? Try using the 'load' command.\n");
00200 return att_combat_init(com, EF_BAD);
00201 }
00202 com->lnd_lcp = &lchr[(int)land.lnd_type];
00203 mil = land.lnd_item[I_MILIT];
00204 pstage = land.lnd_pstage;
00205 owner = land.lnd_own;
00206 eff = land.lnd_effic;
00207 mob = land.lnd_mobil;
00208 x = land.lnd_x;
00209 y = land.lnd_y;
00210 break;
00211 case EF_SHIP:
00212 if (!getship(com->shp_uid, &ship) || !ship.shp_own) {
00213 if (isdef)
00214 pr("Ship #%d is not in the same sector!\n", com->shp_uid);
00215 else
00216 pr("Ship #%d is not your ship!\n", com->shp_uid);
00217 return att_combat_init(com, EF_BAD);
00218 }
00219 if (opt_MARKET) {
00220 if (isdef && player->owner &&
00221 ontradingblock(EF_SHIP, &ship)) {
00222 pr("%s is on the trading block.\n", prcom(0, com));
00223 return att_combat_init(com, EF_BAD);
00224 }
00225 }
00226 if (isdef && player->owner) {
00227 pr("Boarding yourself? Try using the 'tend' command.\n");
00228 return att_combat_init(com, EF_BAD);
00229 }
00230 com->shp_mcp = &mchr[(int)ship.shp_type];
00231 strncpy(com->shp_name, ship.shp_name, MAXSHPNAMLEN);
00232 if (!isdef && !player->owner) {
00233 if (com->set)
00234 pr("%s was just sunk!\n", prcom(0, com));
00235 else
00236 pr("Ship #%d is not your ship!\n", com->shp_uid);
00237 return att_combat_init(com, EF_BAD);
00238 }
00239 mil = ship.shp_item[I_MILIT];
00240 pstage = ship.shp_pstage;
00241 owner = ship.shp_own;
00242 eff = ship.shp_effic;
00243 mob = ship.shp_mobil;
00244 x = ship.shp_x;
00245 y = ship.shp_y;
00246 break;
00247 case EF_PLANE:
00248 return com->mil;
00249 case EF_BAD:
00250 return EF_BAD;
00251 default:
00252 return att_combat_init(com, EF_BAD);
00253 }
00254
00255 if (!com->set) {
00256 if (isdef) {
00257 com->troops = mil;
00258 } else {
00259 if (!mil)
00260 pr("No mil %s\n", prcom(1, com));
00261 else if (mil == 1)
00262 pr("Only 1 mil %s\n", prcom(1, com));
00263
00264 com->troops = MAX(0, mil - 1);
00265 }
00266 com->plague = pstage == PLG_INFECT;
00267 } else {
00268 if (isdef) {
00269 if (com->x != x || com->y != y) {
00270 pr("%s has moved!\n", prcom(0, com));
00271 return att_combat_init(com, EF_BAD);
00272 }
00273 if (owner != com->own) {
00274 if (owner) {
00275 pr("WARNING: The ownership of %s just changed from %s to %s!\n",
00276 prcom(0, com), cname(com->own), cname(owner));
00277 } else if (com->type == EF_SECTOR) {
00278 pr("WARNING: %s just abandoned sector %s!\n",
00279 cname(com->own),
00280 xyas(com->x, com->y, player->cnum));
00281 }
00282 }
00283 if (com->mil != mil)
00284 pr("WARNING: The enemy mil %s just %s from %d to %d!\n",
00285 prcom(1, com),
00286 com->mil < mil ? "increased" : "decreased", com->mil,
00287 mil);
00288 com->troops = mil;
00289 } else {
00290 if (owner != player->cnum && getrel(getnatp(owner), player->cnum) != ALLIED) {
00291
00292 if (com->mil)
00293 pr("WARNING: Your %d mil in %s were destroyed because %s just took the sector!\n",
00294 com->mil, xyas(com->x, com->y, player->cnum),
00295 cname(owner));
00296 else
00297 pr("You no longer own %s\n",
00298 xyas(com->x, com->y, player->cnum));
00299 return att_combat_init(com, EF_BAD);
00300 }
00301 if (com->troops && com->troops + 1 > mil) {
00302 if (com->own == owner && player->cnum == owner)
00303
00304 pr("WARNING: Your mil %s has been reduced from %d to %d!\n",
00305 prcom(1, com), com->troops, MAX(0, mil - 1));
00306 com->troops = MAX(0, mil - 1);
00307 }
00308 }
00309 }
00310 com->set = 1;
00311 com->mil = mil;
00312 com->own = owner;
00313 com->x = x;
00314 com->y = y;
00315 com->eff = eff;
00316 com->mob = mob;
00317 return com->troops;
00318 }
00319
00320
00321
00322
00323
00324
00325
00326 static void
00327 put_combat(struct combat *com)
00328 {
00329 struct sctstr sect;
00330 struct shpstr ship;
00331 struct lndstr land;
00332 int deff;
00333
00334 switch (com->type) {
00335 case EF_SECTOR:
00336 getsect(com->x, com->y, §);
00337 sect.sct_type = com->sct_type;
00338 deff = sect.sct_effic - com->eff;
00339 if (deff > 0) {
00340 sect.sct_road -= sect.sct_road * deff / 100.0;
00341 sect.sct_rail -= sect.sct_rail * deff / 100.0;
00342 sect.sct_defense -= sect.sct_defense * deff / 100.0;
00343 if (sect.sct_road <= 0)
00344 sect.sct_road = 0;
00345 if (sect.sct_rail <= 0)
00346 sect.sct_rail = 0;
00347 if (sect.sct_defense <= 0)
00348 sect.sct_defense = 0;
00349 }
00350 sect.sct_effic = com->eff;
00351 if (com->mobcost) {
00352 if (opt_MOB_ACCESS) {
00353 if ((com->mob - com->mobcost) < -127)
00354 sect.sct_mobil = -127;
00355 else
00356 sect.sct_mobil = (short)(com->mob - com->mobcost);
00357 } else {
00358 if ((com->mob - com->mobcost) < 0)
00359 sect.sct_mobil = 0;
00360 else
00361 sect.sct_mobil = (short)(com->mob - com->mobcost);
00362 }
00363 }
00364 makelost(EF_SECTOR, sect.sct_own, 0, sect.sct_x, sect.sct_y);
00365 makenotlost(EF_SECTOR, com->own, 0, sect.sct_x, sect.sct_y);
00366 sect.sct_own = com->own;
00367 if (com->plague) {
00368 if (sect.sct_pstage == PLG_HEALTHY)
00369 sect.sct_pstage = PLG_EXPOSED;
00370 }
00371 sect.sct_item[I_MILIT] = com->mil;
00372 putsect(§);
00373 com->own = sect.sct_own;
00374 break;
00375 case EF_LAND:
00376 getland(com->lnd_uid, &land);
00377 land.lnd_effic = com->eff;
00378 if (com->mobcost) {
00379 if (com->mob - com->mobcost < -127)
00380 land.lnd_mobil = -127;
00381 else
00382 land.lnd_mobil = (signed char)(com->mob - com->mobcost);
00383 }
00384 makelost(EF_LAND, land.lnd_own, land.lnd_uid,
00385 land.lnd_x, land.lnd_y);
00386 land.lnd_own = com->own;
00387 makenotlost(EF_LAND, land.lnd_own, land.lnd_uid,
00388 land.lnd_x, land.lnd_y);
00389 if (com->plague) {
00390 if (land.lnd_pstage == PLG_HEALTHY)
00391 land.lnd_pstage = PLG_EXPOSED;
00392 }
00393 if (!(com->lnd_lcp->l_flags & L_SPY))
00394 land.lnd_item[I_MILIT] = com->mil;
00395 lnd_count_units(&land);
00396 if (com->own == player->cnum) {
00397 land.lnd_mission = 0;
00398 land.lnd_rflags = 0;
00399 memset(land.lnd_rpath, 0, sizeof(land.lnd_rpath));
00400 }
00401 putland(com->lnd_uid, &land);
00402 break;
00403 case EF_SHIP:
00404 getship(com->shp_uid, &ship);
00405 ship.shp_effic = com->eff;
00406 if (com->mobcost) {
00407 if (com->mob - com->mobcost < -127)
00408 ship.shp_mobil = -127;
00409 else
00410 ship.shp_mobil = (signed char)(com->mob - com->mobcost);
00411 }
00412 makelost(EF_SHIP, ship.shp_own, ship.shp_uid,
00413 ship.shp_x, ship.shp_y);
00414 ship.shp_own = com->own;
00415 makenotlost(EF_SHIP, ship.shp_own, ship.shp_uid,
00416 ship.shp_x, ship.shp_y);
00417 if (com->plague) {
00418 if (ship.shp_pstage == PLG_HEALTHY)
00419 ship.shp_pstage = PLG_EXPOSED;
00420 }
00421 ship.shp_item[I_MILIT] = com->mil;
00422 count_units(&ship);
00423 if (com->own == player->cnum) {
00424 ship.shp_mission = 0;
00425 ship.shp_rflags = 0;
00426 memset(ship.shp_rpath, 0, sizeof(ship.shp_rpath));
00427 }
00428 putship(com->shp_uid, &ship);
00429 }
00430 com->mobcost = 0;
00431 att_get_combat(com, com->own != player->cnum);
00432 }
00433
00434
00435
00436 static int
00437 abort_attack(void)
00438 {
00439 return player->aborted = 1;
00440 }
00441
00442
00443
00444
00445
00446
00447 int
00448 att_abort(int combat_mode, struct combat *off, struct combat *def)
00449 {
00450 struct sctstr sect;
00451 int rel;
00452 char y_or_n[512];
00453 struct natstr *natp;
00454
00455 if (player->aborted)
00456 return 1;
00457 if (att_get_combat(def, 1) < 0)
00458 return abort_attack();
00459
00460 if (off && combat_mode != A_ATTACK) {
00461 if (att_get_combat(off, 0) < 0)
00462 return abort_attack();
00463 if (off->type == EF_SHIP &&
00464 (!getsect(off->x, off->y, §) ||
00465 sect.sct_type != SCT_WATER)) {
00466 pr("%s can not %s from that far inland!\n",
00467 prcom(0, off), att_mode[combat_mode]);
00468 return abort_attack();
00469 }
00470 }
00471 switch (combat_mode) {
00472 case A_ATTACK:
00473 if (!neigh(def->x, def->y, player->cnum) &&
00474 !adj_units(def->x, def->y, player->cnum)) {
00475 pr("You are not adjacent to %s\n",
00476 xyas(def->x, def->y, player->cnum));
00477 return abort_attack();
00478 }
00479 if (def->own == player->cnum) {
00480 pr("You can't attack your own sector.\n");
00481 return abort_attack();
00482 }
00483 break;
00484 case A_ASSAULT:
00485 if (off && mapdist(off->x, off->y, def->x, def->y) > 1) {
00486 pr("You'll have to get there first...\n");
00487 return abort_attack();
00488 }
00489 if (off && def->sct_type == SCT_MOUNT) {
00490 pr("You can't assault a %s sector!\n", def->sct_dcp->d_name);
00491 return abort_attack();
00492 }
00493 break;
00494 case A_PARA:
00495 if (def->own == player->cnum) {
00496 pr("You can't air-assault your own sector.\n");
00497 return abort_attack();
00498 }
00499 if (off && (def->sct_type == SCT_MOUNT ||
00500 def->sct_type == SCT_WATER ||
00501 def->sct_type == SCT_CAPIT ||
00502 def->sct_type == SCT_FORTR ||
00503 def->sct_type == SCT_WASTE)) {
00504 pr("You can't air-assault a %s sector!\n",
00505 def->sct_dcp->d_name);
00506 return abort_attack();
00507 }
00508 break;
00509 case A_BOARD:
00510 return board_abort(off, def);
00511 case A_LBOARD:
00512 return land_board_abort(off, def);
00513 }
00514
00515 if (off && def->sct_dcp->d_mob0 < 0) {
00516 pr("You can't %s a %s sector!\n",
00517 att_mode[combat_mode], def->sct_dcp->d_name);
00518 return abort_attack();
00519 }
00520 if (!off || off->relations_checked)
00521 return 0;
00522 off->relations_checked = 1;
00523
00524 if (opt_HIDDEN) {
00525 setcont(player->cnum, def->own, FOUND_SPY);
00526 setcont(def->own, player->cnum, FOUND_SPY);
00527 }
00528 if (opt_SLOW_WAR && def->own != player->cnum) {
00529 natp = getnatp(player->cnum);
00530 rel = getrel(natp, def->own);
00531
00532 if (rel == ALLIED) {
00533 sprintf(y_or_n, "Sector is owned by %s, your ally, %s [yn]? ",
00534 cname(def->own), att_mode[combat_mode]);
00535 if (!confirm(y_or_n))
00536 return abort_attack();
00537
00538 }
00539 if ((rel != AT_WAR) && (def->own) &&
00540 (sect.sct_oldown != player->cnum)) {
00541 pr("You're not at war with them!\n");
00542 return abort_attack();
00543 }
00544 }
00545 return 0;
00546 }
00547
00548
00549
00550
00551
00552
00553 static int
00554 board_abort(struct combat *off, struct combat *def)
00555 {
00556 struct shpstr aship, dship;
00557 struct sctstr sect;
00558
00559 if (att_get_combat(def, 1) < 0)
00560 return abort_attack();
00561
00562 if (!off)
00563 return 0;
00564
00565 if (att_get_combat(off, 0) < 0)
00566 return abort_attack();
00567
00568 if (off->x != def->x || off->y != def->y) {
00569 pr("Ship #%d is not in the same sector!\n", def->shp_uid);
00570 return abort_attack();
00571 }
00572 if (off->type == EF_SHIP) {
00573 if (off->mob <= 0) {
00574 pr("%s has no mobility!\n", prcom(0, off));
00575 return abort_attack();
00576 }
00577 getship(off->shp_uid, &aship);
00578 getship(def->shp_uid, &dship);
00579 if (techfact(aship.shp_tech, 1.0) * aship.shp_speed * off->eff
00580 <= techfact(dship.shp_tech, 1.0) * dship.shp_speed * def->eff) {
00581 pr("Victim ship moves faster than you do!\n");
00582 if (def->own)
00583 wu(0, def->own,
00584 "%s (#%d) %s failed to catch %s\n",
00585 cname(aship.shp_own), aship.shp_own,
00586 pr_com(0, off, def->own), pr_com(0, def, def->own));
00587 return abort_attack();
00588 }
00589 } else if (off->type != EF_SECTOR) {
00590 pr("Please tell the deity that you got the 'banana boat' error\n");
00591 return abort_attack();
00592 }
00593 if (def->shp_mcp->m_flags & M_SUB) {
00594 getsect(def->x, def->y, §);
00595 if (sect.sct_type == SCT_WATER) {
00596 pr("You can't board a submarine!\n");
00597 return abort_attack();
00598 }
00599 }
00600 return 0;
00601 }
00602
00603
00604
00605
00606
00607
00608
00609 static int
00610 land_board_abort(struct combat *off, struct combat *def)
00611 {
00612 if (att_get_combat(def, 1) < 0)
00613 return abort_attack();
00614
00615 if (!off)
00616 return 0;
00617
00618 if (att_get_combat(off, 0) < 0)
00619 return abort_attack();
00620
00621 if (off->x != def->x || off->y != def->y) {
00622 pr("Land unit #%d is not in the same sector!\n", def->lnd_uid);
00623 return abort_attack();
00624 }
00625
00626 return 0;
00627 }
00628
00629
00630 int
00631 att_approach(struct combat *off, struct combat *def)
00632 {
00633 int dam;
00634 struct sctstr sect;
00635 struct shpstr ship;
00636
00637 pr("Approaching %s...\n", prcom(0, def));
00638 if (def->own)
00639 wu(0, def->own,
00640 "%s is being approached by %s...\n",
00641 pr_com(0, def, def->own), pr_com(0, off, def->own));
00642 if (!(dam = shipdef(player->cnum, def->own, def->x, def->y)))
00643 return 0;
00644
00645 pr("They're firing at us sir!\n");
00646 if (def->own) {
00647 wu(0, def->own,
00648 "Your fleet at %s does %d damage to %s\n",
00649 xyas(def->x, def->y, def->own), dam, pr_com(0, off, def->own));
00650 }
00651 if (off->type == EF_SECTOR) {
00652 getsect(off->x, off->y, §);
00653 sectdamage(§, dam, 0);
00654 putsect(§);
00655 pr("Enemy fleet at %s does %d damage to %s\n",
00656 xyas(def->x, def->y, player->cnum), dam, prcom(0, off));
00657 } else if (off->type == EF_SHIP) {
00658 getship(off->shp_uid, &ship);
00659 shipdamage(&ship, dam);
00660 putship(off->shp_uid, &ship);
00661 if (def->own && ship.shp_effic < SHIP_MINEFF) {
00662 wu(0, def->own, "%s sunk!\n", pr_com(0, off, def->own));
00663 nreport(player->cnum, N_SHP_LOSE, def->own, 1);
00664 }
00665 }
00666 if (att_get_combat(off, 0) < 0)
00667 return abort_attack();
00668 return 0;
00669 }
00670
00671
00672
00673 int
00674 att_show(struct combat *def)
00675 {
00676
00677
00678 if (def->type == EF_SECTOR) {
00679 if (!trechk(player->cnum, def->own, LANATT))
00680 return abort_attack();
00681 pr("%s is a %d%% %s %s with approximately %d military.\n",
00682 xyas(def->x, def->y, player->cnum),
00683 roundintby((int)def->eff, 10),
00684 cname(def->own), def->sct_dcp->d_name,
00685 roundintby(def->troops, 10));
00686 if (map_set(player->cnum, def->x, def->y, def->sct_dcp->d_mnem, 0))
00687 writemap(player->cnum);
00688 } else if (def->type == EF_SHIP || def->type == EF_LAND) {
00689 if (def->type == EF_SHIP) {
00690 if (!trechk(player->cnum, def->own, SEAATT))
00691 return abort_attack();
00692 } else {
00693 if (!trechk(player->cnum, def->own, LNDATT))
00694 return abort_attack();
00695 }
00696 pr("%s is about %d%% efficient and has approximately %d mil on board.\n", prcom(0, def), roundintby((int)def->eff, 10), roundintby(def->troops, 10));
00697 }
00698
00699 return 0;
00700 }
00701
00702
00703
00704 int
00705 att_ask_support(int offset, int *fortp, int *shipp, int *landp,
00706 int *planep)
00707 {
00708 char buf[1024];
00709 char *p;
00710 *fortp = *shipp = *landp = *planep = 1;
00711
00712 if (player->argp[offset] != NULL) {
00713 if ((player->argp[offset + 1] == NULL) ||
00714 (player->argp[offset + 2] == NULL) ||
00715 (player->argp[offset + 3] == NULL)) {
00716 pr("If any support arguments are used, all must be!\n");
00717 return RET_SYN;
00718 }
00719
00720 *fortp = *shipp = 0;
00721 *landp = *planep = 0;
00722
00723 if (!(p = getstarg(player->argp[offset], "Use fort support? ",
00724 buf)))
00725 return RET_SYN;
00726
00727 if ((*p == 'y') || (*p == 'Y'))
00728 *fortp = 1;
00729
00730 if (!(p = getstarg(player->argp[offset + 1], "Use ship support? ",
00731 buf)))
00732 return RET_SYN;
00733
00734 if ((*p == 'y') || (*p == 'Y'))
00735 *shipp = 1;
00736
00737 if (!(p = getstarg(player->argp[offset + 2], "Use land support? ",
00738 buf)))
00739 return RET_SYN;
00740
00741 if ((*p == 'y') || (*p == 'Y'))
00742 *landp = 1;
00743
00744 if (!(p = getstarg(player->argp[offset + 3], "Use plane support? ",
00745 buf)))
00746 return RET_SYN;
00747
00748 if ((*p == 'y') || (*p == 'Y'))
00749 *planep = 1;
00750 }
00751 return RET_OK;
00752 }
00753
00754
00755
00756
00757
00758
00759
00760
00761 int
00762 att_ask_offense(int combat_mode, struct combat *off, struct combat *def,
00763 struct emp_qelem *olist, int *a_spyp, int *a_engineerp)
00764 {
00765 int n;
00766 char land_answer[256];
00767
00768 emp_initque(olist);
00769 if (att_abort(combat_mode, off, def))
00770 return 0;
00771 memset(land_answer, 0, sizeof(land_answer));
00772 for (n = 0; n <= off->last; ++n) {
00773 off[n].troops = ask_off(combat_mode, off + n, def);
00774 if (att_abort(combat_mode, off, def))
00775 return 0;
00776 ask_olist(combat_mode, off + n, def, olist, land_answer,
00777 a_spyp, a_engineerp);
00778 if (att_abort(combat_mode, off, def))
00779 return 0;
00780 }
00781 return 0;
00782 }
00783
00784
00785
00786
00787
00788 static double
00789 att_mobcost(natid attacker, struct combat *def, int mobtype)
00790 {
00791 struct sctstr sect;
00792 int ok;
00793
00794 if (CANT_HAPPEN(def->type != EF_SECTOR))
00795 return -1.0;
00796 ok = getsect(def->x, def->y, §);
00797 if (CANT_HAPPEN(!ok))
00798 return -1.0;
00799
00800
00801
00802
00803
00804
00805
00806
00807 sect.sct_own = attacker;
00808 sect.sct_mobil = 0;
00809 return sector_mcost(§, mobtype);
00810 }
00811
00812
00813
00814 static int
00815 get_mob_support(int combat_mode, struct combat *off, struct combat *def)
00816 {
00817 int mob_support;
00818 double mobcost;
00819
00820 switch (combat_mode) {
00821 case A_ATTACK:
00822 mobcost = att_mobcost(off->own, def, MOB_MOVE);
00823 if (mobcost < 0 || off->mob <= 0)
00824 return 0;
00825 mob_support = off->mob / mobcost;
00826 if (mob_support < off->troops)
00827 pr("Sector %s has %d mobility which can only support %d mil,\n",
00828 xyas(off->x, off->y, player->cnum), off->mob, mob_support);
00829 else
00830 mob_support = off->troops;
00831 return mob_support;
00832 case A_ASSAULT:
00833 if (def->own != player->cnum && def->mil) {
00834 if (off->shp_mcp->m_flags & M_SEMILAND)
00835 return off->troops / 4;
00836 else if (!(off->shp_mcp->m_flags & M_LAND))
00837 return off->troops / 10;
00838 }
00839 break;
00840 case A_BOARD:
00841 if (off->type == EF_SECTOR && off->mob <= 0)
00842 return 0;
00843 mob_support = def->shp_mcp->m_item[I_MILIT];
00844 if (mob_support < off->troops)
00845 pr("The size of the ship you are trying to board limits your party to %d mil,\n", mob_support);
00846 else
00847 mob_support = off->troops;
00848 return mob_support;
00849 case A_LBOARD:
00850 if (off->mob <= 0)
00851 return 0;
00852 if (def->lnd_lcp->l_flags & L_SPY)
00853 return 1;
00854 mob_support = def->lnd_lcp->l_item[I_MILIT];
00855 if (mob_support < off->troops)
00856 pr("The size of the unit you are trying to board limits your party to %d mil,\n", mob_support);
00857 else
00858 mob_support = off->troops;
00859 return mob_support;
00860 }
00861 return off->troops;
00862 }
00863
00864
00865
00866
00867
00868
00869
00870
00871 static void
00872 calc_mobcost(int combat_mode, struct combat *off, struct combat *def,
00873 int attacking_mil)
00874 {
00875 struct shpstr ship;
00876
00877 if (!attacking_mil)
00878 return;
00879 switch (combat_mode) {
00880 case A_ATTACK:
00881 off->mobcost += MAX(1,
00882 (int)(attacking_mil
00883 * att_mobcost(off->own, def, MOB_MOVE)));
00884 break;
00885 case A_LBOARD:
00886 off->mobcost += MAX(1, attacking_mil / 5);
00887 break;
00888 case A_BOARD:
00889 switch (off->type) {
00890 case EF_SECTOR:
00891 off->mobcost += MAX(1, attacking_mil / 5);
00892 break;
00893 case EF_SHIP:
00894
00895 getship(def->shp_uid, &ship);
00896 off->mobcost += (def->eff / 100) * (ship.shp_speed / 2);
00897 }
00898 }
00899 }
00900
00901
00902
00903 static int
00904 ask_off(int combat_mode, struct combat *off, struct combat *def)
00905 {
00906 int attacking_mil;
00907 int mob_support;
00908 char prompt[512];
00909
00910 if (att_get_combat(off, 0) <= 0)
00911 return 0;
00912 if ((off->type == EF_SECTOR) && (off->own != player->cnum))
00913 return 0;
00914 if ((mob_support = get_mob_support(combat_mode, off, def)) <= 0)
00915 return 0;
00916 if (off->type == EF_SECTOR) {
00917 if (off->own != player->cnum)
00918 return 0;
00919 sprintf(prompt, "Number of mil from %s at %s (max %d) : ",
00920 off->sct_dcp->d_name,
00921 xyas(off->x, off->y, player->cnum), mob_support);
00922 } else {
00923 sprintf(prompt, "Number of mil from %s (max %d) : ",
00924 prcom(0, off), mob_support);
00925 }
00926 if ((attacking_mil = onearg(0, prompt)) < 0)
00927 abort_attack();
00928 if (att_abort(combat_mode, off, def))
00929 return 0;
00930 if (att_get_combat(off, 0) <= 0)
00931 return 0;
00932 if ((attacking_mil =
00933 MIN(attacking_mil, MIN(mob_support, off->troops))) <= 0)
00934 return 0;
00935
00936 calc_mobcost(combat_mode, off, def, attacking_mil);
00937 return attacking_mil;
00938 }
00939
00940
00941
00942
00943
00944 static char
00945 att_prompt(char *prompt, char army)
00946 {
00947 char buf[1024];
00948 char *p;
00949
00950 if (!army)
00951 army = '~';
00952 for (;;) {
00953 p = getstring(prompt, buf);
00954 if (player->aborted || (p && *p == 'q')) {
00955 abort_attack();
00956 return 'N';
00957 }
00958 if (!p || !*p)
00959 return 'n';
00960 if (tolower(*p) == 'y' || tolower(*p) == 'n')
00961 return *p;
00962 pr("y - yes this unit\n"
00963 "n - no this unit\n"
00964 "Y - yes to all units in army '%c'\n"
00965 "N - no to all units in army '%c'\n"
00966 "q - quit\n? - this help message\n\n",
00967 army, army);
00968 }
00969 }
00970
00971
00972
00973 static void
00974 ask_olist(int combat_mode, struct combat *off, struct combat *def,
00975 struct emp_qelem *olist, char *land_answer, int *a_spyp,
00976 int *a_engineerp)
00977 {
00978 struct nstr_item ni;
00979 struct lndstr land;
00980 double mobcost;
00981 struct ulist *llp;
00982 struct lchrstr *lcp;
00983 double att_val;
00984 int count = 0;
00985 int maxland = 0;
00986 int first_time = 1;
00987 char prompt[512];
00988
00989 if (def->type == EF_LAND)
00990 return;
00991 if (def->type == EF_SHIP)
00992 maxland = def->shp_mcp->m_nland;
00993
00994 snxtitem_xy(&ni, EF_LAND, off->x, off->y);
00995 while (nxtitem(&ni, &land)) {
00996 if (land.lnd_own != player->cnum)
00997 continue;
00998 if (land.lnd_effic < LAND_MINEFF)
00999 continue;
01000 if (land_answer[(int)land.lnd_army] == 'N')
01001 continue;
01002 if (!lnd_can_attack(&land))
01003 continue;
01004 lcp = &lchr[(int)land.lnd_type];
01005
01006 if (def->type == EF_SHIP && !maxland) {
01007 pr("Land units are not able to board this kind of ship\n");
01008 return;
01009 }
01010 if (land.lnd_mobil <= 0) {
01011 pr("%s is out of mobility, and cannot %s\n",
01012 prland(&land), att_mode[combat_mode]);
01013 continue;
01014 }
01015
01016 if (opt_MARKET) {
01017 if (ontradingblock(EF_LAND, &land)) {
01018 pr("%s is on the trading block, and cannot %s\n",
01019 prland(&land), att_mode[combat_mode]);
01020 continue;
01021 }
01022 }
01023
01024 if (off->type == EF_SECTOR && land.lnd_ship >= 0) {
01025 pr("%s is on ship #%d, and cannot %s\n",
01026 prland(&land), land.lnd_ship, att_mode[combat_mode]);
01027 continue;
01028 } else if (off->type == EF_SHIP) {
01029 if (land.lnd_ship != off->shp_uid)
01030 continue;
01031 } else if (land.lnd_land >= 0) {
01032 pr("%s is on unit #%d, and cannot %s\n",
01033 prland(&land), land.lnd_land, att_mode[combat_mode]);
01034 continue;
01035 }
01036 switch (combat_mode) {
01037 case A_ATTACK:
01038
01039
01040
01041
01042
01043
01044
01045
01046
01047
01048 mobcost = att_mobcost(off->own, def, lnd_mobtype(&land));
01049 if (mobcost < 1.0) {
01050 if (land.lnd_mobil <= 0) {
01051 pr("%s is out of mobility\n", prland(&land));
01052 continue;
01053 }
01054 } else {
01055 mobcost = lnd_pathcost(&land, mobcost);
01056 if (land.lnd_mobil < mobcost) {
01057 pr("%s does not have enough mobility (%d needed)\n",
01058 prland(&land), (int)ceil(mobcost));
01059 continue;
01060 }
01061 }
01062 break;
01063 case A_ASSAULT:
01064 case A_BOARD:
01065 mobcost = 0;
01066 if (!(lcp->l_flags & L_ASSAULT))
01067 continue;
01068 break;
01069 default:
01070 CANT_REACH();
01071 return;
01072 }
01073 att_val = attack_val(combat_mode, &land);
01074 if (att_val < 1.0) {
01075 pr("%s has no offensive strength\n", prland(&land));
01076 continue;
01077 }
01078 resupply_all(&land);
01079 putland(land.lnd_uid, &land);
01080 if (!has_supply(&land)) {
01081 pr("%s is out of supply, and cannot %s\n",
01082 prland(&land), att_mode[combat_mode]);
01083 continue;
01084 }
01085 if (def->type == EF_SHIP && first_time) {
01086 first_time = 0;
01087 pr("You may board with a maximum of %d land units\n", maxland);
01088 }
01089 pr("%s has a base %s value of %.0f\n",
01090 prland(&land), att_mode[combat_mode], att_val);
01091 if (land_answer[(int)land.lnd_army] != 'Y') {
01092 sprintf(prompt,
01093 "%s with %s %s (%c %d%%) [ynYNq?] ",
01094 att_mode[combat_mode],
01095 prland(&land),
01096 prcom(1, off),
01097 land.lnd_army ? land.lnd_army : '~',
01098 land.lnd_effic);
01099 land_answer[(int)land.lnd_army] =
01100 att_prompt(prompt, land.lnd_army);
01101 if (att_abort(combat_mode, off, def))
01102 return;
01103 if (land_answer[(int)land.lnd_army] != 'y' &&
01104 land_answer[(int)land.lnd_army] != 'Y')
01105 continue;
01106 }
01107 if (!(llp = malloc(sizeof(struct ulist)))) {
01108 logerror("Malloc failed in attack!\n");
01109 abort_attack();
01110 return;
01111 }
01112 memset(llp, 0, sizeof(struct ulist));
01113 emp_insque(&llp->queue, olist);
01114 llp->mobil = mobcost;
01115 if (!get_land(combat_mode, def, land.lnd_uid, llp, 0))
01116 continue;
01117 if (lnd_spyval(&land) > *a_spyp)
01118 *a_spyp = lnd_spyval(&land);
01119 if (((struct lchrstr *)llp->chrp)->l_flags & L_ENGINEER)
01120 ++*a_engineerp;
01121 if (def->type == EF_SHIP && ++count >= maxland)
01122 break;
01123 }
01124 }
01125
01126
01127
01128 double
01129 att_combat_eff(struct combat *com)
01130 {
01131 double eff = 1.0;
01132 double str;
01133 struct shpstr ship;
01134
01135 if (com->type == EF_SECTOR) {
01136 eff = com->eff / 100.0;
01137 if (com->own == player->cnum) {
01138 str = com->sct_dcp->d_ostr;
01139 eff = 1.0 + (str - 1.0) * eff;
01140 } else
01141 eff = sector_strength(getsectp(com->x, com->y));
01142 } else if (com->type == EF_SHIP && com->own != player->cnum) {
01143 getship(com->shp_uid, &ship);
01144 eff = 1.0 + ship.shp_armor / 100.0;
01145 }
01146 return eff;
01147 }
01148
01149 int
01150 att_get_offense(int combat_mode, struct combat *off,
01151 struct emp_qelem *olist, struct combat *def)
01152 {
01153 int ototal;
01154
01155
01156
01157
01158
01159
01160 ototal = get_ototal(combat_mode, off, olist, 1.0, 1);
01161 if (att_empty_attack(combat_mode, ototal, def))
01162 return abort_attack();
01163 if (combat_mode == A_PARA)
01164 return ototal;
01165 pr("\n Initial attack strength: %8d\n", ototal);
01166 return ototal;
01167 }
01168
01169
01170 int
01171 att_get_defense(struct emp_qelem *olist, struct combat *def,
01172 struct emp_qelem *dlist, int a_spy, int ototal)
01173 {
01174 int d_spy = 0;
01175 struct emp_qelem *qp;
01176 struct ulist *llp;
01177 int dtotal;
01178 int old_dtotal;
01179
01180 emp_initque(dlist);
01181 get_dlist(def, dlist, 0, &d_spy);
01182 dtotal = get_dtotal(def, dlist, 1.0, 0);
01183
01184
01185
01186
01187
01188 if (def->type == EF_SECTOR && def->sct_type != SCT_MOUNT)
01189 att_reacting_units(def, dlist, a_spy, &d_spy, ototal);
01190
01191 for (qp = olist->q_forw; qp != olist; qp = qp->q_forw) {
01192 llp = (struct ulist *)qp;
01193 intelligence_report(def->own, &llp->unit.land, d_spy,
01194 "Scouts report attacking unit:");
01195 }
01196
01197 old_dtotal = dtotal;
01198 dtotal = get_dtotal(def, dlist, 1.0, 0);
01199 if (dtotal != old_dtotal)
01200 pr("Defense strength with reacting units: %8d\n", dtotal);
01201
01202 return dtotal;
01203 }
01204
01205
01206
01207 static void
01208 get_dlist(struct combat *def, struct emp_qelem *list, int a_spy,
01209 int *d_spyp)
01210 {
01211 struct nstr_item ni;
01212 struct ulist *llp;
01213 struct lndstr land;
01214
01215
01216
01217
01218 snxtitem_xy(&ni, EF_LAND, def->x, def->y);
01219 while (nxtitem(&ni, &land)) {
01220 if (!land.lnd_own)
01221 continue;
01222 if (land.lnd_own != def->own)
01223 continue;
01224 if (def->type == EF_SECTOR && land.lnd_ship >= 0)
01225 continue;
01226 if (def->type == EF_SECTOR && land.lnd_land >= 0)
01227 continue;
01228 if (def->type == EF_SHIP && land.lnd_ship != def->shp_uid)
01229 continue;
01230 if (def->type == EF_LAND && land.lnd_land != def->lnd_uid)
01231 continue;
01232 if (!list) {
01233 intelligence_report(player->cnum, &land, a_spy,
01234 "Scouts report defending unit:");
01235 continue;
01236 }
01237 if (!(llp = malloc(sizeof(struct ulist)))) {
01238 logerror("Malloc failed in attack!\n");
01239 abort_attack();
01240 return;
01241 }
01242 memset(llp, 0, sizeof(struct ulist));
01243 emp_insque(&llp->queue, list);
01244 llp->supplied = has_supply(&land);
01245 if (!get_land(A_DEFEND, def, land.lnd_uid, llp, 1))
01246 continue;
01247 if (lnd_spyval(&land) > *d_spyp)
01248 *d_spyp = lnd_spyval(&land);
01249 }
01250 }
01251
01252
01253
01254 static int
01255 get_ototal(int combat_mode, struct combat *off, struct emp_qelem *olist,
01256 double osupport, int check)
01257 {
01258 double ototal = 0.0;
01259 struct emp_qelem *qp, *next;
01260 struct ulist *llp;
01261 int n, w;
01262
01263
01264
01265
01266
01267 for (n = 0; n <= off->last; ++n) {
01268 if (off[n].type == EF_BAD || (check &&
01269 att_get_combat(&off[n], 0) <= 0))
01270 continue;
01271 ototal += off[n].troops * att_combat_eff(off + n);
01272 }
01273
01274
01275
01276
01277
01278
01279 for (qp = olist->q_forw; qp != olist; qp = next) {
01280 next = qp->q_forw;
01281 llp = (struct ulist *)qp;
01282 if (check && !get_land(combat_mode, 0, llp->unit.land.lnd_uid, llp, 0))
01283 continue;
01284 if (combat_mode == A_ATTACK) {
01285 w = -1;
01286 for (n = 0; n <= off->last; ++n) {
01287 if (off[n].type == EF_BAD)
01288 continue;
01289 if ((off[n].x == llp->unit.land.lnd_x) &&
01290 (off[n].y == llp->unit.land.lnd_y))
01291 w = n;
01292 }
01293 if (w < 0) {
01294 lnd_delete(llp, "is in a sector not owned by you");
01295 continue;
01296 }
01297 ototal += attack_val(combat_mode, &llp->unit.land) *
01298 att_combat_eff(off + w);
01299 } else {
01300 ototal += attack_val(combat_mode, &llp->unit.land);
01301 }
01302 }
01303 ototal *= osupport;
01304
01305 return ldround(ototal, 1);
01306 }
01307
01308
01309
01310 static int
01311 get_dtotal(struct combat *def, struct emp_qelem *list, double dsupport,
01312 int check)
01313 {
01314 double dtotal = 0.0, eff = 1.0, d_unit;
01315 struct emp_qelem *qp, *next;
01316 struct ulist *llp;
01317
01318 if (check && att_get_combat(def, 1) < 0)
01319 return 0;
01320 eff = att_combat_eff(def);
01321 dtotal = def->troops * eff;
01322
01323
01324
01325
01326
01327
01328 for (qp = list->q_forw; qp != list; qp = next) {
01329 next = qp->q_forw;
01330 llp = (struct ulist *)qp;
01331 if (check && !get_land(A_DEFEND, def, llp->unit.land.lnd_uid, llp, 1))
01332 continue;
01333 d_unit = defense_val(&llp->unit.land);
01334 if (!llp->supplied)
01335 d_unit /= 2.0;
01336 dtotal += d_unit * eff;
01337 }
01338
01339 dtotal *= dsupport;
01340
01341 return ldround(dtotal, 1);
01342 }
01343
01344
01345
01346
01347
01348
01349 static int
01350 get_land(int combat_mode, struct combat *def, int uid, struct ulist *llp,
01351 int victim_land)
01352 {
01353 struct lndstr *lp = &llp->unit.land;
01354 char buf[512];
01355
01356 getland(uid, lp);
01357
01358 if (!llp->chrp) {
01359 llp->x = llp->unit.land.lnd_x;
01360 llp->y = llp->unit.land.lnd_y;
01361 llp->chrp = (struct empobj_chr *)&lchr[(int)llp->unit.land.lnd_type];
01362 } else {
01363 if (lp->lnd_effic < LAND_MINEFF) {
01364 sprintf(buf, "was destroyed and is no longer a part of the %s",
01365 att_mode[combat_mode]);
01366 lnd_delete(llp, buf);
01367 return 0;
01368 }
01369 if (victim_land) {
01370 if (lp->lnd_x != def->x || lp->lnd_y != def->y) {
01371 lnd_delete(llp,
01372 "left to go fight another battle and is no longer a part of the defense");
01373 return 0;
01374 }
01375 } else {
01376 if (lp->lnd_own != player->cnum) {
01377 sprintf(buf,
01378 "was destroyed and is no longer a part of the %s",
01379 att_mode[combat_mode]);
01380 lnd_delete(llp, buf);
01381 return 0;
01382 }
01383 if (lp->lnd_x != llp->x || lp->lnd_y != llp->y) {
01384 sprintf(buf,
01385 "left to fight another battle and is no longer a part of the %s",
01386 att_mode[combat_mode]);
01387 lnd_delete(llp, buf);
01388 return 0;
01389 }
01390 if (lp->lnd_effic < llp->eff) {
01391 sprintf(buf, "damaged from %d%% to %d%%",
01392 llp->eff, lp->lnd_effic);
01393 lnd_print(llp, buf);
01394 }
01395 }
01396 }
01397 llp->eff = llp->unit.land.lnd_effic;
01398
01399 return 1;
01400 }
01401
01402
01403
01404
01405
01406
01407
01408
01409 static void
01410 kill_land(struct emp_qelem *list)
01411 {
01412 struct emp_qelem *qp, *next;
01413 struct ulist *llp;
01414
01415 for (qp = list->q_forw; qp != list; qp = next) {
01416 next = qp->q_forw;
01417 llp = (struct ulist *)qp;
01418 if (llp->unit.land.lnd_ship >= 0) {
01419 llp->unit.land.lnd_effic = 0;
01420 lnd_delete(llp, "cannot return to the ship, and dies!");
01421 }
01422 }
01423 }
01424
01425 static void
01426 att_infect_units(struct emp_qelem *list, int plague)
01427 {
01428 struct emp_qelem *qp, *next;
01429 struct ulist *llp;
01430
01431 if (!plague)
01432 return;
01433 for (qp = list->q_forw; qp != list; qp = next) {
01434 next = qp->q_forw;
01435 llp = (struct ulist *)qp;
01436 if (llp->unit.land.lnd_pstage == PLG_HEALTHY)
01437 llp->unit.land.lnd_pstage = PLG_EXPOSED;
01438 }
01439 }
01440
01441 static void
01442 put_land(struct emp_qelem *list)
01443 {
01444 struct emp_qelem *qp, *next;
01445 struct ulist *llp;
01446
01447 for (qp = list->q_forw; qp != list; qp = next) {
01448 next = qp->q_forw;
01449 llp = (struct ulist *)qp;
01450 llp->unit.land.lnd_mission = 0;
01451 llp->unit.land.lnd_harden = 0;
01452 llp->unit.land.lnd_mobil -= (int)llp->mobil;
01453 llp->mobil = 0.0;
01454 putland(llp->unit.land.lnd_uid, &llp->unit.land);
01455 if (llp->unit.land.lnd_own != player->cnum) {
01456 emp_remque((struct emp_qelem *)llp);
01457 free(llp);
01458 } else
01459 get_land(A_ATTACK, 0, llp->unit.land.lnd_uid, llp, 0);
01460 }
01461 }
01462
01463
01464
01465
01466
01467
01468 double
01469 att_reacting_units(struct combat *def, struct emp_qelem *list, int a_spy,
01470 int *d_spyp, int ototal)
01471 {
01472 struct nstr_item ni;
01473 struct lndstr land;
01474 struct sctstr sect, dsect;
01475 struct ulist *llp;
01476 int dtotal;
01477 double new_land = 0;
01478 double mobcost;
01479 double pathcost;
01480 int dist;
01481 int radius;
01482 int origx, origy;
01483 double eff = att_combat_eff(def);
01484 char buf[1024];
01485
01486 if (list)
01487 dtotal = get_dtotal(def, list, 1.0, 1);
01488 else
01489 dtotal = 0;
01490 snxtitem_all(&ni, EF_LAND);
01491 while (nxtitem(&ni, &land) && dtotal + new_land * eff < 1.2 * ototal) {
01492 if (!land.lnd_own)
01493 continue;
01494 if (!land.lnd_rad_max)
01495 continue;
01496 if ((land.lnd_x == def->x) && (land.lnd_y == def->y))
01497 continue;
01498 if (land.lnd_own != def->own)
01499 continue;
01500 if (land.lnd_ship >= 0)
01501 continue;
01502 if (land.lnd_land >= 0)
01503 continue;
01504 if (defense_val(&land) < 1.0)
01505 continue;
01506 if (!lnd_can_attack(&land))
01507 continue;
01508
01509
01510 if (!has_supply(&land))
01511 continue;
01512
01513 dist = mapdist(land.lnd_x, land.lnd_y, def->x, def->y);
01514
01515 getsect(land.lnd_x, land.lnd_y, §);
01516
01517 if ((sect.sct_type == SCT_HEADQ) && (sect.sct_effic >= 60))
01518 radius = land.lnd_rad_max + 1;
01519 else
01520 radius = land.lnd_rad_max;
01521
01522 if (land.lnd_mission == MI_RESERVE)
01523 radius += 2;
01524
01525 if (dist > radius)
01526 continue;
01527
01528 getsect(def->x, def->y, &dsect);
01529 if (!BestLandPath(buf, §, &dsect, &pathcost,
01530 lnd_mobtype(&land)))
01531 continue;
01532
01533 mobcost = lnd_pathcost(&land, pathcost);
01534 if (land.lnd_mobil < mobcost)
01535 continue;
01536
01537 new_land += defense_val(&land);
01538
01539 if (!list)
01540 continue;
01541
01542
01543 land.lnd_mobil -= ldround(mobcost, 1);
01544 origx = land.lnd_x;
01545 origy = land.lnd_y;
01546 land.lnd_x = def->x;
01547 land.lnd_y = def->y;
01548 putland(land.lnd_uid, &land);
01549 wu(0, land.lnd_own, "%s reacts to %s.\n",
01550 prland(&land), xyas(land.lnd_x, land.lnd_y, land.lnd_own));
01551
01552 llp = malloc(sizeof(struct ulist));
01553
01554 memset(llp, 0, sizeof(struct ulist));
01555 llp->supplied = 1;
01556 llp->x = origx;
01557 llp->y = origy;
01558 llp->chrp = (struct empobj_chr *)&lchr[(int)land.lnd_type];
01559 llp->unit.land = land;
01560 emp_insque(&llp->queue, list);
01561 if (lnd_spyval(&land) > *d_spyp)
01562 *d_spyp = lnd_spyval(&land);
01563
01564 intelligence_report(player->cnum, &land, a_spy,
01565 "Scouts sight reacting enemy unit:");
01566 }
01567 return new_land;
01568 }
01569
01570
01571
01572 static double
01573 get_osupport(char *outs, struct combat *def, int fort_sup, int ship_sup,
01574 int land_sup, int plane_sup)
01575 {
01576 double osupport = 1.0;
01577 int dam;
01578 double af, as, au, ap;
01579
01580 af = as = au = ap = 0.0;
01581 if (fort_sup) {
01582 dam = dd(def->own, player->cnum, def->x, def->y, 0, 0);
01583 af = dam / 100.0;
01584 osupport += af;
01585 }
01586 if (ship_sup) {
01587 dam = sd(def->own, player->cnum, def->x, def->y, 0, 0, 0);
01588
01589 as = dam / 100.0;
01590 osupport += as;
01591 }
01592
01593 if (land_sup) {
01594 dam = lnd_support(def->own, player->cnum, def->x, def->y, 0);
01595 au = dam / 100.0;
01596 osupport += au;
01597 }
01598
01599 if (plane_sup) {
01600 dam = off_support(def->x, def->y, def->own, player->cnum);
01601 ap = dam / 100.0;
01602 osupport += ap;
01603 }
01604 sprintf(outs, "attacker\t%1.2f\t%1.2f\t%1.2f\t%1.2f\n", af, as, au,
01605 ap);
01606 return osupport;
01607 }
01608
01609
01610
01611 static double
01612 get_dsupport(char *outs, struct emp_qelem *list, struct combat *def,
01613 int ototal, int dtotal)
01614 {
01615 double dsupport = 1.0;
01616 int dam;
01617 double df, ds, du, dp;
01618 int good = 0;
01619
01620 df = ds = du = dp = 0.0;
01621 if (dtotal < 0.1 * ototal) {
01622 good = -1;
01623 } else if (dtotal >= 1.2 * ototal) {
01624 good = 1;
01625 } else {
01626 dam = dd(player->cnum, def->own, def->x, def->y, 0, 1);
01627 df = dam / 100.0;
01628 dsupport += df;
01629
01630 dtotal = get_dtotal(def, list, dsupport, 0);
01631 if (dtotal < 1.2 * ototal) {
01632 dam = sd(player->cnum, def->own, def->x, def->y, 0, 1, 0);
01633 ds = dam / 100.0;
01634 dsupport += ds;
01635 dtotal = get_dtotal(def, list, dsupport, 0);
01636 }
01637 if (dtotal < 1.2 * ototal) {
01638 dam = lnd_support(player->cnum, def->own, def->x, def->y, 1);
01639 du = dam / 100.0;
01640 dsupport += du;
01641 dtotal = get_dtotal(def, list, dsupport, 1);
01642 }
01643 if (dtotal < 1.2 * ototal) {
01644 dam = def_support(def->x, def->y, player->cnum, def->own);
01645 dp = dam / 100.0;
01646 dsupport += dp;
01647 }
01648 }
01649 if (good)
01650 *outs = '\0';
01651 else
01652 sprintf(outs, "defender\t%1.2f\t%1.2f\t%1.2f\t%1.2f\n\n", df, ds,
01653 du, dp);
01654 if (def->own) {
01655 if (good < 0)
01656 wu(0, def->own,
01657 "\nOdds are bad for us...support cancelled.\n\n");
01658 else if (good > 0)
01659 wu(0, def->own,
01660 "\nOdds are good for us...support cancelled.\n\n");
01661 }
01662 return dsupport;
01663 }
01664
01665
01666
01667
01668
01669
01670 static double
01671 get_mine_dsupport(struct combat *def, int a_engineer)
01672 {
01673 int mines;
01674 struct sctstr sect;
01675
01676 getsect(def->x, def->y, §);
01677
01678 if (sect.sct_oldown != player->cnum) {
01679 mines = MIN(sect.sct_mines, 20);
01680 if (a_engineer)
01681 mines = ldround(mines / 2.0, 1);
01682 if (mines > 0) {
01683 if (def->own)
01684 wu(0, def->own, "Defending mines add %1.2f\n",
01685 mines * 0.02);
01686 pr("Defending mines add %1.2f\n", mines * 0.02);
01687 return mines * 0.02;
01688 }
01689 }
01690 return 0.0;
01691 }
01692
01693
01694 int
01695 att_get_support(int combat_mode, int ofort, int oship, int oland,
01696 int oplane, struct emp_qelem *olist, struct combat *off,
01697 struct emp_qelem *dlist, struct combat *def,
01698 double *osupportp, double *dsupportp, int a_engineer)
01699 {
01700 int ototal, dtotal;
01701 char osupports[512];
01702 char dsupports[512];
01703
01704 if (combat_mode == A_PARA)
01705 *osupports = '\0';
01706 else
01707 *osupportp = get_osupport(osupports, def,
01708 ofort, oship, oland, oplane);
01709
01710
01711
01712
01713
01714
01715
01716 ototal = get_ototal(combat_mode, off, olist, *osupportp, 1);
01717 if (att_empty_attack(combat_mode, ototal, def))
01718 return abort_attack();
01719 dtotal = get_dtotal(def, dlist, *dsupportp, 1);
01720
01721
01722
01723
01724
01725
01726 *dsupportp = get_dsupport(dsupports, dlist, def, ototal, dtotal);
01727 ototal = get_ototal(combat_mode, off, olist, *osupportp, 1);
01728 if (att_empty_attack(combat_mode, ototal, def))
01729 return abort_attack();
01730
01731 if ((*osupports || *dsupports) &&
01732 (*osupportp != 1.0 || *dsupportp != 1.0)) {
01733 pr("\n\t\tsupport values\n");
01734 pr("\t\tforts\tships\tunits\tplanes\n");
01735 if (*osupportp != 1.0)
01736 pr("%s", osupports);
01737 if (*dsupportp != 1.0)
01738 pr("%s", dsupports);
01739 if (def->own) {
01740 wu(0, def->own, "\n\t\tsupport values\n");
01741 wu(0, def->own, "\t\tforts\tships\tunits\tplanes\n");
01742 if (*osupportp != 1.0)
01743 wu(0, def->own, "%s", osupports);
01744 if (*dsupportp != 1.0)
01745 wu(0, def->own, "%s", dsupports);
01746 }
01747 }
01748
01749 dtotal = get_dtotal(def, dlist, *dsupportp, 1);
01750 if (dtotal && def->type == EF_SECTOR)
01751 *dsupportp += get_mine_dsupport(def, a_engineer);
01752 return 0;
01753 }
01754
01755
01756
01757 static int
01758 count_bodies(struct combat *off, struct emp_qelem *list)
01759 {
01760 int n;
01761 int bodies = 0;
01762 struct emp_qelem *qp;
01763 struct ulist *llp;
01764
01765 for (n = 0; n <= off->last; ++n)
01766 bodies += off[n].troops;
01767 for (qp = list->q_forw; qp != list; qp = qp->q_forw) {
01768 llp = (struct ulist *)qp;
01769 bodies += llp->unit.land.lnd_item[I_MILIT];
01770 }
01771 return bodies;
01772 }
01773
01774
01775
01776 int
01777 att_fight(int combat_mode, struct combat *off, struct emp_qelem *olist,
01778 double osupport, struct combat *def, struct emp_qelem *dlist,
01779 double dsupport)
01780 {
01781 int success = 0;
01782 int a_cas = 0;
01783 int d_cas = 0;
01784 int ototal;
01785 int dtotal;
01786 int a_bodies;
01787 int d_bodies;
01788 int d_mil;
01789 int a_troops[6];
01790 int n;
01791 int news_item;
01792 int recalctime;
01793 double odds;
01794 int newmob;
01795 char *action;
01796
01797 ototal = get_ototal(combat_mode, off, olist, osupport,
01798 combat_mode != A_PARA);
01799 dtotal = get_dtotal(def, dlist, dsupport, 0);
01800 if (!dtotal)
01801 success = 1;
01802
01803 a_bodies = count_bodies(off, olist);
01804 d_bodies = count_bodies(def, dlist);
01805 d_mil = def->troops;
01806 for (n = 0; n <= off->last; ++n)
01807 if (off[n].type == EF_BAD)
01808 a_troops[n] = 0;
01809 else
01810 a_troops[n] = off[n].troops;
01811
01812
01813 switch (combat_mode) {
01814 case A_ATTACK:
01815 pr(" Final attack strength: %8d\n", ototal);
01816 break;
01817 case A_ASSAULT:
01818 pr(" Final assault strength: %8d\n", ototal);
01819 break;
01820 case A_PARA:
01821 if (def->sct_type == SCT_MOUNT ||
01822 def->sct_type == SCT_WATER ||
01823 def->sct_type == SCT_CAPIT ||
01824 def->sct_type == SCT_FORTR || def->sct_type == SCT_WASTE) {
01825 pr("You can't air-assault a %s sector!\n",
01826 def->sct_dcp->d_name);
01827 a_cas = a_bodies;
01828 off[0].troops = 0;
01829 ototal = get_ototal(A_PARA, off, olist, osupport, 0);
01830 }
01831 pr(" Final air-assault strength: %8d\n", ototal);
01832 break;
01833 case A_BOARD:
01834 case A_LBOARD:
01835 pr(" Final board strength: %8d\n", ototal);
01836 }
01837
01838
01839 pr(" Final defense strength: %8d\n", dtotal);
01840 odds = att_calcodds(ototal, dtotal);
01841 pr(" Final odds: %8d%%\n", (int)(odds * 100));
01842
01843
01844 if (combat_mode != A_PARA) {
01845 if (!def->plague)
01846 for (n = 0; n <= off->last; ++n)
01847 if (off[n].type != EF_BAD)
01848 def->plague |= off[n].plague;
01849 for (n = 0; n <= off->last; ++n)
01850 if (off[n].type != EF_BAD)
01851 off[n].plague |= def->plague;
01852 }
01853 att_infect_units(olist, off->plague);
01854 att_infect_units(dlist, def->plague);
01855
01856
01857
01858
01859
01860
01861
01862
01863
01864
01865
01866
01867 odds += (random() % 11 - 5) / 100.0;
01868 if (odds < 0.0)
01869 odds = 0.1;
01870 if (odds > 1.0)
01871 odds = 1.0;
01872 recalctime = 8 + (random() % 43);
01873 while (!success && ototal) {
01874 if (chance(odds)) {
01875 pr("!");
01876 d_cas += take_casualty(A_DEFEND, def, dlist);
01877 dtotal = get_dtotal(def, dlist, dsupport, 0);
01878 if (!dtotal)
01879 ++success;
01880 } else {
01881 pr("@");
01882 a_cas += take_casualty(combat_mode, off, olist);
01883 ototal = get_ototal(combat_mode, off, olist, osupport, 0);
01884 }
01885 if (((a_cas + d_cas) % 70) == 69)
01886 pr("\n");
01887 if (recalctime-- <= 0) {
01888 recalctime = 8 + (random() % 43);
01889 odds = att_calcodds(ototal, dtotal);
01890 odds += (random() % 11 - 5) / 100.0;
01891 if (odds < 0.0)
01892 odds = 0.1;
01893 if (odds > 1.0)
01894 odds = 1.0;
01895 }
01896 }
01897 pr("\n");
01898
01899 if (success)
01900 def->mil = 0;
01901 else {
01902 if (def->type == EF_SECTOR && d_mil && d_cas) {
01903 if (def->mob < 0)
01904 def->mobcost = 0;
01905 else {
01906 newmob = damage(def->mob, 100 * d_cas / d_mil);
01907 def->mobcost = MIN(20, def->mob - newmob);
01908 }
01909 }
01910 def->mil = def->troops;
01911 }
01912
01913
01914 for (n = 0; n <= off->last; ++n)
01915 if (off[n].type != EF_BAD && off[n].troops < a_troops[n]) {
01916 if (off[n].type == EF_SECTOR && off[n].mil) {
01917 if (!CANT_HAPPEN(off[n].mob < 0)) {
01918 newmob = damage(off[n].mob,
01919 100 * (a_troops[n] - off[n].troops)
01920 / off[n].mil);
01921 off[n].mobcost += MIN(20, off[n].mob - newmob);
01922 }
01923 }
01924 off[n].mil -= a_troops[n] - off[n].troops;
01925 }
01926
01927
01928 if (d_bodies && d_cas)
01929 lnd_takemob(dlist, (double)d_cas / d_bodies);
01930 if (a_bodies && a_cas)
01931 lnd_takemob(olist, (double)a_cas / a_bodies);
01932
01933
01934 def->eff = effdamage(def->eff, (d_cas + a_cas) / 10);
01935
01936 pr("- Casualties -\n Yours: %d\n", a_cas);
01937 pr(" Theirs: %d\n", d_cas);
01938 pr("Papershuffling ... %.1f B.T.U\n", (d_cas + a_cas) * 0.15);
01939 player->btused += (int)((d_cas + a_cas) * 0.015 + 0.5);
01940
01941 if (success) {
01942 switch (combat_mode) {
01943 case A_ATTACK:
01944 news_item = def->own ? N_WON_SECT : N_TOOK_UNOCC;
01945 pr("We have captured %s, sir!\n", prcom(0, def));
01946 action = "taking";
01947 break;
01948 case A_ASSAULT:
01949 news_item = def->own ? N_AWON_SECT : N_START_COL;
01950 pr("We have secured a beachhead at %s, sir!\n", prcom(0, def));
01951 action = "assaulting and taking";
01952 break;
01953 case A_PARA:
01954 news_item = def->own ? N_PWON_SECT : N_PARA_UNOCC;
01955 pr("We have captured %s, sir!\n", prcom(0, def));
01956 action = "air-assaulting and taking";
01957 break;
01958 case A_BOARD:
01959 news_item = N_BOARD_SHIP;
01960 pr("We have boarded %s, sir!\n", prcom(0, def));
01961 action = "boarding";
01962 break;
01963 case A_LBOARD:
01964 news_item = N_BOARD_LAND;
01965 pr("We have boarded %s, sir!\n", prcom(0, def));
01966 action = "boarding";
01967 break;
01968 default:
01969 CANT_REACH();
01970 news_item = 0;
01971 action = "defeating";
01972 }
01973 } else {
01974 switch (combat_mode) {
01975 case A_ATTACK:
01976 news_item = N_SCT_LOSE;
01977 pr("You have been defeated!\n");
01978 action = "attacking";
01979 break;
01980 case A_ASSAULT:
01981 news_item = N_ALOSE_SCT;
01982 pr("You have been defeated!\n");
01983 kill_land(olist);
01984 action = "trying to assault";
01985 break;
01986 case A_PARA:
01987 news_item = N_PLOSE_SCT;
01988 pr("All of your troops were destroyed\n");
01989 action = "trying to air-assault";
01990 break;
01991 case A_BOARD:
01992 news_item = N_SHP_LOSE;
01993 pr("You have been repelled\n");
01994 kill_land(olist);
01995 action = "trying to board";
01996 break;
01997 case A_LBOARD:
01998 news_item = N_LND_LOSE;
01999 pr("You have been repelled\n");
02000 kill_land(olist);
02001 action = "trying to board";
02002 break;
02003 default:
02004 CANT_REACH();
02005 news_item = 0;
02006 action = "fighting";
02007 }
02008 }
02009 nreport(player->cnum, news_item, def->own, 1);
02010 if (def->own) {
02011 wu(0, def->own,
02012 "%s (#%d) lost %d troops %s %s\nWe lost %d troops defending\n",
02013 cname(player->cnum), player->cnum, a_cas,
02014 action, pr_com(0, def, def->own), d_cas);
02015 }
02016
02017 send_reacting_units_home(dlist);
02018
02019
02020 unit_put(dlist, 0);
02021
02022
02023 put_land(olist);
02024
02025
02026 if (!success || !take_def(combat_mode, olist, off, def))
02027 put_combat(def);
02028
02029
02030 for (n = 0; n <= off->last; ++n)
02031 if (off[n].type != EF_BAD)
02032 put_combat(&off[n]);
02033
02034 if (!success)
02035 return 0;
02036
02037 switch (combat_mode) {
02038 case A_ATTACK:
02039 ask_move_in(off, olist, def);
02040
02041
02042 for (n = 0; n <= off->last; ++n)
02043 if (off[n].type != EF_BAD)
02044 put_combat(&off[n]);
02045 break;
02046 default:
02047 att_move_in_off(combat_mode, off, olist, def);
02048 }
02049 if (def->mil > 0)
02050 pr("%d of your troops now occupy %s\n", def->mil, prcom(0, def));
02051 return 1;
02052 }
02053
02054
02055
02056 static double
02057 att_calcodds(int ototal, int dtotal)
02058 {
02059 double odds;
02060
02061
02062 if (ototal <= 0)
02063 odds = 0.0;
02064 else if (dtotal <= 0)
02065 odds = 1.0;
02066 else
02067 odds = (double)ototal / (dtotal + ototal);
02068
02069 return odds;
02070 }
02071
02072
02073
02074 static int
02075 take_casualty(int combat_mode, struct combat *off, struct emp_qelem *olist)
02076 {
02077 int to_take = CASUALTY_LUMP;
02078 int biggest_troops = 0, index = -1;
02079 int n, tot_troops = 0, biggest_mil, cas;
02080 struct emp_qelem *qp, *biggest;
02081 struct ulist *llp;
02082
02083 for (n = 0; n <= off->last; ++n) {
02084 if (off[n].type != EF_BAD) {
02085 tot_troops += off[n].troops;
02086 if (off[n].troops > biggest_troops) {
02087 biggest_troops = off[n].troops;
02088 index = n;
02089 }
02090 }
02091 }
02092
02093 if (tot_troops)
02094 to_take -= tot_troops;
02095
02096 if (to_take >= 0) {
02097 for (n = 0; n <= off->last; ++n)
02098 if (off[n].type != EF_BAD)
02099 off[n].troops = 0;
02100 } else {
02101
02102
02103
02104
02105 to_take = CASUALTY_LUMP;
02106 if (index < 0) {
02107 pr("ERROR: Tell the deity that you got the 'green librarian' error\n");
02108 index = 0;
02109 }
02110 while (to_take > 0) {
02111 for (n = index; n <= off->last && to_take; ++n) {
02112 if (off[n].type != EF_BAD && off[n].troops > 0) {
02113 --to_take;
02114 --off[n].troops;
02115 }
02116 }
02117 for (n = 0; n < index && to_take; ++n) {
02118 if (off[n].type != EF_BAD && off[n].troops > 0) {
02119 --to_take;
02120 --off[n].troops;
02121 }
02122 }
02123 }
02124 return CASUALTY_LUMP;
02125 }
02126
02127 if (QEMPTY(olist))
02128 return CASUALTY_LUMP - to_take;
02129
02130
02131
02132
02133
02134
02135 biggest = NULL;
02136 biggest_mil = -1;
02137 for (qp = olist->q_forw; qp != olist; qp = qp->q_forw) {
02138 llp = (struct ulist *)qp;
02139
02140 if (llp->unit.land.lnd_item[I_MILIT] > biggest_mil) {
02141 biggest_mil = llp->unit.land.lnd_item[I_MILIT];
02142 biggest = qp;
02143 }
02144 }
02145 if (biggest == NULL)
02146 return CASUALTY_LUMP - to_take;
02147
02148 llp = (struct ulist *)biggest;
02149 cas = lnd_take_casualty(combat_mode, llp, to_take);
02150 return CASUALTY_LUMP - (to_take - cas);
02151 }
02152
02153
02154
02155 static void
02156 send_reacting_units_home(struct emp_qelem *list)
02157 {
02158 struct emp_qelem *qp, *next;
02159 struct ulist *llp;
02160 char buf[1024];
02161
02162 for (qp = list->q_forw; qp != list; qp = next) {
02163 next = qp->q_forw;
02164 llp = (struct ulist *)qp;
02165 if ((llp->unit.land.lnd_x != llp->x) ||
02166 (llp->unit.land.lnd_y != llp->y)) {
02167 sprintf(buf, "returns to %s",
02168 xyas(llp->x, llp->y, llp->unit.land.lnd_own));
02169 llp->unit.land.lnd_x = llp->x;
02170 llp->unit.land.lnd_y = llp->y;
02171 lnd_delete(llp, buf);
02172 }
02173 }
02174 }
02175
02176
02177
02178 int
02179 att_empty_attack(int combat_mode, int ototal, struct combat *def)
02180 {
02181 if (ototal <= 0) {
02182 if (def->own && player->cnum != def->own) {
02183 wu(0, def->own,
02184 "%s (#%d) considered %sing you @%s\n",
02185 cname(player->cnum), player->cnum,
02186 att_mode[combat_mode], xyas(def->x, def->y, def->own));
02187 }
02188 pr("No troops for %s...\n", att_mode[combat_mode]);
02189 return 1;
02190 }
02191 return 0;
02192 }
02193
02194
02195
02196
02197
02198
02199 static int
02200 take_def(int combat_mode, struct emp_qelem *list, struct combat *off,
02201 struct combat *def)
02202 {
02203 int n;
02204 int occuppied = 0;
02205 struct ulist *llp, *delete_me = 0;
02206 char buf[1024];
02207 struct sctstr sect;
02208 struct shpstr ship;
02209 struct lndstr land;
02210
02211 for (n = 0; n <= off->last && !occuppied; ++n) {
02212 if (off[n].type != EF_BAD &&
02213 off[n].troops > 0 &&
02214 (off[n].type != EF_SECTOR || off[n].mob)) {
02215 ++occuppied;
02216 if (def->type == EF_LAND) {
02217 if (def->lnd_lcp->l_flags & L_SPY) {
02218 continue;
02219 }
02220 }
02221 --(off[n].troops);
02222 --(off[n].mil);
02223 ++def->mil;
02224 pr("1 mil from %s moves %s\n",
02225 prcom(0, off + n), prcom(2, def));
02226 }
02227 }
02228 if (!occuppied) {
02229 if (QEMPTY(list)) {
02230 pr("%s left unoccupied\n", prcom(0, def));
02231 if (def->own)
02232 wu(0, def->own,
02233 "No enemy troops moved %s so you still own it!\n",
02234 pr_com(2, def, def->own));
02235 return 0;
02236 } else {
02237 llp = (struct ulist *)list->q_forw;
02238 llp->unit.land.lnd_x = def->x;
02239 llp->unit.land.lnd_y = def->y;
02240 take_move_in_mob(combat_mode, llp, off, def);
02241 if (def->type == EF_SHIP) {
02242 llp->unit.land.lnd_ship = def->shp_uid;
02243 sprintf(buf, "boards %s", prcom(0, def));
02244 delete_me = llp;
02245 } else {
02246 llp->unit.land.lnd_ship = -1;
02247 sprintf(buf, "moves in to occupy %s",
02248 xyas(def->x, def->y, player->cnum));
02249 lnd_delete(llp, buf);
02250 }
02251 }
02252 }
02253 put_combat(def);
02254 if (def->type == EF_SECTOR) {
02255 getsect(def->x, def->y, §);
02256 takeover(§, player->cnum);
02257 if (sect.sct_type == SCT_CAPIT || sect.sct_type == SCT_MOUNT)
02258 caploss(§, def->own,
02259 "* We have captured %s's capital, sir! *\n");
02260 putsect(§);
02261 } else if (def->type == EF_SHIP) {
02262 getship(def->shp_uid, &ship);
02263 takeover_ship(&ship, player->cnum, 1);
02264 putship(ship.shp_uid, &ship);
02265 } else if (def->type == EF_LAND) {
02266 getland(def->lnd_uid, &land);
02267 takeover_land(&land, player->cnum, 1);
02268 putland(land.lnd_uid, &land);
02269 }
02270 if (delete_me)
02271 lnd_delete(delete_me, buf);
02272 att_get_combat(def, 0);
02273 return 1;
02274 }
02275
02276
02277
02278
02279
02280
02281 static void
02282 ask_move_in(struct combat *off, struct emp_qelem *olist,
02283 struct combat *def)
02284 {
02285 int n;
02286 struct emp_qelem *qp, *next;
02287 struct ulist *llp;
02288 char buf[512];
02289 char prompt[512];
02290 char land_answer[256];
02291 char *answerp;
02292
02293 for (n = 0; n <= off->last; ++n)
02294 if (off[n].type != EF_BAD && off[n].troops > 0)
02295 if (off[n].mob) {
02296 ask_move_in_off(&off[n], def);
02297 if (player->aborted)
02298 break;
02299 }
02300
02301 if (QEMPTY(olist))
02302 return;
02303 memset(land_answer, 0, sizeof(land_answer));
02304 for (qp = olist->q_forw; qp != olist; qp = next) {
02305 next = qp->q_forw;
02306 llp = (struct ulist *)qp;
02307 answerp = &land_answer[(int)llp->unit.land.lnd_army];
02308 if (player->aborted || att_get_combat(def, 0) < 0)
02309 *answerp = 'N';
02310 if (*answerp == 'Y')
02311 continue;
02312 if (*answerp != 'N') {
02313 if (!get_land(A_ATTACK, def, llp->unit.land.lnd_uid, llp, 0))
02314 continue;
02315 sprintf(prompt, "Move in with %s (%c %d%%) [ynYNq?] ",
02316 prland(&llp->unit.land),
02317 llp->unit.land.lnd_army ? llp->unit.land.lnd_army : '~',
02318 llp->unit.land.lnd_effic);
02319 *answerp = att_prompt(prompt, llp->unit.land.lnd_army);
02320 if (player->aborted || att_get_combat(def, 0) < 0)
02321 *answerp = 'N';
02322 if (!get_land(A_ATTACK, def, llp->unit.land.lnd_uid, llp, 0))
02323 continue;
02324 }
02325 if (*answerp == 'y' || *answerp == 'Y')
02326 continue;
02327 sprintf(buf, "stays in %s",
02328 xyas(llp->unit.land.lnd_x, llp->unit.land.lnd_y,
02329 player->cnum));
02330 lnd_delete(llp, buf);
02331 }
02332 if (QEMPTY(olist))
02333 return;
02334 if (att_get_combat(def, 0) < 0) {
02335 for (qp = olist->q_forw; qp != olist; qp = next) {
02336 next = qp->q_forw;
02337 llp = (struct ulist *)qp;
02338 if (!get_land(A_ATTACK, def, llp->unit.land.lnd_uid, llp, 0))
02339 continue;
02340 sprintf(buf, "stays in %s",
02341 xyas(llp->unit.land.lnd_x, llp->unit.land.lnd_y,
02342 player->cnum));
02343 lnd_delete(llp, buf);
02344 }
02345 return;
02346 }
02347 if (opt_INTERDICT_ATT)
02348 lnd_interdict(olist, def->x, def->y, player->cnum);
02349 move_in_land(A_ATTACK, off, olist, def);
02350 }
02351
02352
02353
02354 static void
02355 move_in_land(int combat_mode, struct combat *off, struct emp_qelem *olist,
02356 struct combat *def)
02357 {
02358 struct emp_qelem *qp, *next;
02359 struct ulist *llp;
02360 char buf[512];
02361
02362 if (QEMPTY(olist))
02363 return;
02364 for (qp = olist->q_forw; qp != olist; qp = next) {
02365 next = qp->q_forw;
02366 llp = (struct ulist *)qp;
02367 if (!get_land(combat_mode, def, llp->unit.land.lnd_uid, llp, 0))
02368 continue;
02369 take_move_in_mob(combat_mode, llp, off, def);
02370 llp->unit.land.lnd_x = def->x;
02371 llp->unit.land.lnd_y = def->y;
02372 if (def->type == EF_SHIP)
02373 llp->unit.land.lnd_ship = def->shp_uid;
02374 else
02375 llp->unit.land.lnd_ship = -1;
02376 }
02377 if (QEMPTY(olist))
02378 return;
02379 if (def->type == EF_SECTOR) {
02380 if (opt_INTERDICT_ATT) {
02381 lnd_sweep(olist, 0, 0, def->own);
02382 lnd_check_mines(olist);
02383 }
02384 sprintf(buf, "now occupies %s", prcom(0, def));
02385 } else {
02386 sprintf(buf, "boards %s", prcom(0, def));
02387 }
02388 if (QEMPTY(olist))
02389 return;
02390 for (qp = olist->q_forw; qp != olist; qp = next) {
02391 next = qp->q_forw;
02392 llp = (struct ulist *)qp;
02393 lnd_print(llp, buf);
02394 }
02395 if (QEMPTY(olist))
02396 return;
02397 unit_put(olist, 0);
02398 }
02399
02400
02401
02402
02403
02404
02405 void
02406 att_move_in_off(int combat_mode, struct combat *off,
02407 struct emp_qelem *olist, struct combat *def)
02408 {
02409 struct sctstr sect;
02410 struct shpstr ship;
02411 int troops, n;
02412 int lunchbox = 0;
02413
02414 move_in_land(combat_mode, off, olist, def);
02415
02416 for (n = 0; n <= off->last; ++n) {
02417 if (off[n].type == EF_BAD || !off[n].troops)
02418 continue;
02419 troops = off[n].troops;
02420 off[n].troops = 0;
02421 off[n].mil -= troops;
02422 def->mil += troops;
02423 put_combat(off + n);
02424 if (combat_mode == A_ASSAULT) {
02425 if (CANT_HAPPEN(off[n].type != EF_SHIP))
02426 continue;
02427 getship(off[n].shp_uid, &ship);
02428 lunchbox += (int)((troops + 1) * ship.shp_item[I_FOOD]
02429 / (ship.shp_item[I_MILIT] + troops
02430 + ship.shp_item[I_CIVIL] + 0.5));
02431
02432 ship.shp_item[I_FOOD] -= lunchbox;
02433 putship(ship.shp_uid, &ship);
02434 }
02435 }
02436
02437 put_combat(def);
02438
02439 if (combat_mode == A_ASSAULT) {
02440 if (CANT_HAPPEN(def->type != EF_SECTOR))
02441 return;
02442 getsect(def->x, def->y, §);
02443 if (lunchbox > ITEM_MAX - sect.sct_item[I_FOOD])
02444 lunchbox = ITEM_MAX - sect.sct_item[I_FOOD];
02445 sect.sct_item[I_FOOD] += lunchbox;
02446 putsect(§);
02447 }
02448 }
02449
02450
02451
02452
02453 static void
02454 ask_move_in_off(struct combat *off, struct combat *def)
02455 {
02456 int mob_support;
02457 int num_mil, dam = 0, left;
02458 double d, weight;
02459 char prompt[512];
02460 char buf[1024];
02461 char *p;
02462
02463 if (att_get_combat(off, 0) <= 0)
02464 return;
02465 if (att_get_combat(def, 0) < 0)
02466 return;
02467 if (off->own != player->cnum)
02468 return;
02469 d = att_mobcost(off->own, def, MOB_MOVE);
02470 if ((mob_support = MIN(off->troops, (int)(off->mob / d))) <= 0)
02471 return;
02472 sprintf(prompt, "How many mil to move in from %s (%d max)? ",
02473 xyas(off->x, off->y, player->cnum), mob_support);
02474 if (!(p = getstring(prompt, buf)) || !*p || (num_mil = atoi(p)) <= 0)
02475 return;
02476
02477 if (num_mil > mob_support)
02478 num_mil = mob_support;
02479 if (att_get_combat(off, 0) <= 0)
02480 return;
02481 if (att_get_combat(def, 0) < 0)
02482 return;
02483 if ((num_mil = MIN(off->troops, num_mil)) <= 0) {
02484 pr("No mil moved in from %s\n",
02485 xyas(off->x, off->y, player->cnum));
02486 return;
02487 }
02488 mob_support = MAX(1, (int)(num_mil * d));
02489 off->mob -= MIN(off->mob, mob_support);
02490 off->mil -= num_mil;
02491 off->troops -= num_mil;
02492 put_combat(off);
02493 left = num_mil;
02494 weight = (double)num_mil * ichr[I_MILIT].i_lbs;
02495 if (opt_INTERDICT_ATT && chance(weight / 200.0)) {
02496 if (chance(weight / 100.0))
02497 dam +=
02498 ground_interdict(def->x, def->y, player->cnum, "military");
02499 dam += check_lmines(def->x, def->y, weight);
02500 }
02501
02502 if (dam) {
02503 left = commdamage(num_mil, dam, I_MILIT);
02504 if (left < num_mil) {
02505 if (left) {
02506 pr("%d of the mil you were moving were destroyed!\nOnly %d mil made it to %s\n", num_mil - left, left, xyas(def->x, def->y, player->cnum));
02507 } else {
02508 pr("All of the mil you were moving were destroyed!\n");
02509 }
02510 }
02511
02512 if (att_get_combat(def, 0) < 0)
02513 return;
02514 }
02515 def->mil += left;
02516 put_combat(def);
02517 }
02518
02519
02520
02521
02522 static void
02523 take_move_in_mob(int combat_mode, struct ulist *llp, struct combat *off,
02524 struct combat *def)
02525 {
02526 int mobcost;
02527 int new;
02528
02529 switch (combat_mode) {
02530 case A_ATTACK:
02531 mobcost = lnd_pathcost(&llp->unit.land,
02532 att_mobcost(off->own, def,
02533 lnd_mobtype(&llp->unit.land)));
02534 new = llp->unit.land.lnd_mobil - mobcost;
02535 if (new < -127)
02536 new = -127;
02537 llp->unit.land.lnd_mobil = new;
02538 break;
02539 case A_ASSAULT:
02540 if (off->shp_mcp->m_flags & M_LAND) {
02541 if (((struct lchrstr *)llp->chrp)->l_flags & L_MARINE)
02542 llp->unit.land.lnd_mobil -=
02543 (float)etu_per_update * land_mob_scale * 0.5;
02544 else
02545 llp->unit.land.lnd_mobil -= (float)etu_per_update *
02546 land_mob_scale;
02547 } else {
02548 if (((struct lchrstr *)llp->chrp)->l_flags & L_MARINE)
02549 llp->unit.land.lnd_mobil = 0;
02550 else
02551 llp->unit.land.lnd_mobil = -(float)etu_per_update *
02552 land_mob_scale;
02553 }
02554 break;
02555 case A_BOARD:
02556
02557 if (((struct lchrstr *)llp->chrp)->l_flags & L_MARINE)
02558 llp->unit.land.lnd_mobil -= 10;
02559 else
02560 llp->unit.land.lnd_mobil -= 40;
02561 break;
02562 }
02563 llp->unit.land.lnd_harden = 0;
02564 }
02565
02566 static void
02567 free_list(struct emp_qelem *list)
02568 {
02569 struct emp_qelem *qp, *next;
02570
02571 if (!list || QEMPTY(list))
02572 return;
02573
02574 qp = list->q_forw;
02575 while (qp != list) {
02576 next = qp->q_forw;
02577 emp_remque(qp);
02578 free(qp);
02579 qp = next;
02580 }
02581 }
02582
02583 int
02584 att_free_lists(struct emp_qelem *olist, struct emp_qelem *dlist)
02585 {
02586 free_list(olist);
02587 free_list(dlist);
02588 return RET_OK;
02589 }
02590
02591
02592
02593
02594
02595
02596
02597 double
02598 sector_strength(struct sctstr *sp)
02599 {
02600 double def = SCT_DEFENSE(sp) / 100.0;
02601 double base = sp->sct_type == SCT_MOUNT ? 2.0 : 1.0;
02602 double d = base + (dchr[sp->sct_type].d_dstr - base) * def;
02603
02604 if (d > dchr[sp->sct_type].d_dstr)
02605 d = dchr[sp->sct_type].d_dstr;
02606 if (d < base)
02607 d = base;
02608 return d;
02609 }