DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T a

⟦14b2880b4⟧ TextFile

    Length: 13114 (0x333a)
    Types: TextFile
    Names: »attack.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/X/Xconq/attack.c« 

TextFile

/* Copyright (c) 1987, 1988  Stanley T. Shebs, University of Utah. */
/* This program may be used, copied, modified, and redistributed freely */
/* for noncommercial purposes, so long as this notice remains intact. */

/* RCS $Header: attack.c,v 1.2 88/06/28 10:38:26 shebs Exp $ */

/* Although conducting xconq combat is rather simple (i.e. try to move */
/* into the hex), the outcome is under the control of many parameters. */

/* Rules of combat: the attacker hits the defender ("other") unit and its */
/* occupants, but the damage does not take effect right away.  If counter */
/* attacks are possible in this period, the defender always does so, with */
/* the same odds.  If the defender dies, then the attacker moves into the */
/* hex.  If the attacker dies, nothing happens.  If both survive, then the */
/* attacker may attempt to capture the defender. */

#include "config.h"
#include "misc.h"
#include "period.h"
#include "side.h"
#include "unit.h"
#include "map.h"
#include "global.h"

extern int occdeath[];

char *summarize_units();

/* Buffers for verbal descriptions of units from each other's */
/* point of view. */

char aabuf[BUFSIZE], aobuf[BUFSIZE], oabuf[BUFSIZE], oobuf[BUFSIZE];
char hitbuf[BUFSIZE], killbuf[BUFSIZE];

/* Remember what the main units involved are, so display is handled relative */
/* to them and not to any occupants. */

Unit *amain, *omain;

/* Hits on main units saved up, hits on occupants happen immediately. */

int ahit, ohit;

/* ... but the data is saved anyway, for message generation. */

int occhits[MAXUTYPES], occkills[MAXUTYPES];

/* Return true if the attacker defeated the defender, and can therefore */
/* try to move into the defender's old position. */

attack(atker, other)
Unit *atker, *other;
{
    int u, ax = atker->x, ay = atker->y, ox = other->x, oy = other->y;
    Side *as = atker->side, *os = other->side;

    amain = atker;  omain = other;
    ahit = ohit = 0;
    for_all_unit_types(u) occhits[u] = occkills[u] = 0;
    attack_unit(atker, other);
    if (period.counterattack || utypes[atker->type].counterable)
	attack_unit(other, atker);
    reckon_damage();
    see_exact(as, ax, ay);
    see_exact(as, ox, oy);
    see_exact(os, ax, ay);
    see_exact(os, ox, oy);
    draw_hex(as, ax, ay, TRUE);
    draw_hex(as, ox, oy, TRUE);
    draw_hex(os, ax, ay, TRUE);
    draw_hex(os, ox, oy, TRUE);
    all_see_hex(ax, ay);
    all_see_hex(ox, oy);
    attempt_to_capture_unit(atker, other);
    return (alive(atker) && unit_at(ox, oy) == NULL);
}

/* Test to see if enough ammo is available to make the attack. */
/* Need enough of *all* types - semi-bogus but too complicated otherwise? */

enough_ammo(atker, other)
Unit *atker, *other;
{
    int r;

    for_all_resource_types(r) {
	if (utypes[other->type].hitby[r] > 0 &&
	    atker->supply[r] < utypes[atker->type].hitswith[r]) return FALSE;
    }
    return TRUE;
}

/* Single attack, no counterattack.  Check and use ammo - usage independent */
/* of outcome, but types used depend on unit types involved. */

attack_unit(atker, other)
Unit *atker, *other;
{
    int r;

    wake_unit(other, FALSE);
    if (alive(atker) && alive(other)) {
	if (enough_ammo(atker, other)) {
	    hit_unit(atker, other);
	    for_all_resource_types(r) {
		if (utypes[other->type].hitby[r] > 0) {
		    atker->supply[r] -= utypes[atker->type].hitswith[r];
		}
	    }
	    atker->movesleft -= utypes[atker->type].hittime;
	}
    }
}

/* Make a single hit and maybe hit some passengers also.  Power of hit */
/* is constant, but chance is affected by neutrality, terrain, quality, */
/* and occupants' protective abilities.  If a hit is successful, it may */
/* have consequences on the defender's occupants, but limited by the */
/* protection that the transport provides. */

hit_unit(atker, other)
Unit *atker, *other;
{
    int chance, terr, hit = 0, a = atker->type, o = other->type;
    Unit *occ;
    Side *as = atker->side;

    chance = utypes[a].hit[o];
    if (neutral(atker)) chance += period.neutrality;
    terr = terrain_at(other->x, other->y);
    chance -= (chance * utypes[o].defense[terr]) / 100;
    if (utypes[a].maxquality > 0) {
	chance += ((chance * atker->quality * utypes[a].skillf) /
		   utypes[a].maxquality) / 100;
    }
    if (utypes[o].maxquality > 0) {
	chance -= ((chance * other->quality * utypes[o].disciplinef) /
		   utypes[o].maxquality) / 100;
    }
    for_all_occupants(other, occ) {
	chance -= (chance * utypes[occ->type].protect[o]) / 100;
    }
    if (probability(chance)) hit = utypes[a].damage[o];
    if (as != NULL) {
	as->atkstats[a][o]++;
	as->hitstats[a][o] += hit;
    }
    if (hit_unit_aux(atker, other, hit)) {
	for_all_occupants(other, occ) {
	    if (probability(100 - utypes[o].protect[occ->type])) {
		hit_unit(atker, occ);
	    }
	}
    }
}

/* Do the hit itself.  Occupants are always hit/killed immediately, while */
/* the "main units" of the hexes don't get it till later.  In any case, */
/* messages are delayed.  Return true if the victim was hit but not killed. */

hit_unit_aux(atker, other, hit)
Unit *atker, *other;
int hit;
{
    int o = other->type, a = atker->type, chance, i;
    Side *as = atker->side, *os = other->side;

    if (hit >= period.nukehit) {
	notify_all("%s has been hit by a nuclear attack!!!",
		   unit_handle(NULL, other));
	set_terrain_at(other->x, other->y,
		       ttypes[terrain_at(other->x, other->y)].nuked);
    }
    if (hit > 0 && mobile(o)) {
	chance = utypes[o].retreat;
	/* should adjust chance by morale etc */
	if (probability(chance)) {
	    if (retreat_unit(other)) {
		notify(as, "%s runs away!", unit_handle(as, other));
		notify(os, "%s runs away!", unit_handle(os, other));
		hit = 0;
	    }
	}
    }
    if (other == omain) {
	ohit = hit;
    } else if (other == amain) {
	ahit = hit;
    } else {
	if (hit >= other->hp) {
	    occkills[o]++;
	    kill_unit(other, COMBAT);
	    for_all_unit_types(i) occkills[i] += occdeath[i];
	} else if (hit > 0) {
	    occhits[o]++;
	    other->hp -= hit;
	}
	if (utypes[a].selfdestruct) {
	    kill_unit(atker, COMBAT);
	}
    }
    return (alive(other) && hit > 0);
}

/* Hits on the main units have to be done later, so that mutual */
/* destruction is possible.  This function also does all the notifying. */
/* (Only the main units of a hex rate messages, occupants' fates are */
/* summarized briefly.) */

reckon_damage()
{
    int o = omain->type, a = amain->type, i;
    Side *as = amain->side, *os = omain->side;

    strcpy(aabuf, unit_handle(as, amain));
    strcpy(aobuf, unit_handle(as, omain));
    strcpy(oabuf, unit_handle(os, amain));
    strcpy(oobuf, unit_handle(os, omain));

    if (ahit > 0) draw_blast(amain, omain->side, ahit);
    if (ohit > 0) draw_blast(omain, amain->side, ohit);
    if (ohit >= omain->hp) {
	notify(as, "%s %s %s!", aabuf, utypes[o].destroymsg, aobuf);
	notify(os, "%s %s %s!", oabuf, utypes[o].destroymsg, oobuf);
	kill_unit(omain, COMBAT);
	for_all_unit_types(i) occkills[i] += occdeath[i];
    } else if (ohit > 0) {
	notify(as, "%s hits %s!", aabuf, aobuf);
	notify(os, "%s hits %s!", oabuf, oobuf);
	omain->hp -= ohit;
    } else {
	/* messages about missing not too useful */
    }
    summarize_units(hitbuf, occhits);
    summarize_units(killbuf, occkills);
    if (strlen(hitbuf) > 0) {
	if (strlen(killbuf) > 0) {
	    notify(as, "   (Also hit%s, killed%s)", hitbuf, killbuf);
	    notify(os, "   (%s hurt, %s killed)", hitbuf, killbuf);
	} else {
	    notify(as, "   (Also hit%s)", hitbuf);
	    notify(os, "   (%s hurt)", hitbuf);
	}
    } else {
	if (strlen(killbuf) > 0) {
	    notify(as, "   (Also killed%s)", killbuf);
	    notify(os, "   (%s killed)", killbuf);
	}
    }
    if (ahit >= amain->hp) {
	notify(as, "%s %s %s!", aobuf, utypes[a].destroymsg, aabuf);
	notify(os, "%s %s %s!", oobuf, utypes[a].destroymsg, oabuf);
	kill_unit(amain, COMBAT);
    } else if (ahit > 0) {
	notify(as, "%s hits %s!", aobuf, aabuf);
	notify(os, "%s hits %s!", oobuf, oabuf);
	amain->hp -= ahit;
    } else {
	/* messages about missing not too useful */
    }
    if (utypes[a].selfdestruct) kill_unit(amain, COMBAT);
    if (utypes[o].selfdestruct) kill_unit(omain, COMBAT);
}

/* Handle capture possibility and repulse/slaughter. */

/* The chance to capture an enemy is modified by several factors. */
/* Neutrals have a different chance to be captured, and presence of */
/* occupants should also has an effect.  Can't capture anything that is */
/* on a kind of terrain that the capturer can't go on, unless victim has */
/* "bridge effect". */

attempt_to_capture_unit(atker, other)
Unit *atker, *other;
{
    int a = atker->type, o = other->type, chance;
    int ox = other->x, oy = other->y;
    Unit *occ;
    Side *as = atker->side, *os = other->side;

    if (alive(atker) && alive(other) && could_capture(a, o)) {
	if (impassable(atker, ox, oy) && !utypes[o].bridge[a]) return;
	chance = utypes[a].capture[o];
	if (neutral(other)) chance -= period.neutrality;
	if (utypes[a].maxquality > 0) {
	    chance += ((chance * atker->quality * utypes[a].skillf) /
		       utypes[a].maxquality) / 100;
	}
	for_all_occupants(other, occ) {
	    chance -= (chance * utypes[occ->type].protect[o]) / 100;
	}
	if (probability(chance)) {
	    capture_unit(atker, other);
	    if (global.setproduct && utypes[o].maker) {
		request_new_product(other);
		other->movesleft = 1;
		move_1(other->side, other);
	    }
	} else if (atker->transport != NULL && 
		   (impassable(atker, ox, oy) ||
		    impassable(atker, atker->x, atker->y))) {
	    notify(as, "Resistance... %s was slaughtered!",
		   unit_handle(as, atker));
	    notify(os, "Resistance... %s was slaughtered!",
		   unit_handle(os, atker));
	    kill_unit(atker, COMBAT);
	} else {
	    strcpy(aabuf, unit_handle(as, atker));
	    notify(as, "%s repulses %s!", unit_handle(as, other), aabuf);
	    strcpy(oabuf, unit_handle(os, atker));
	    notify(os, "%s repulses %s!", unit_handle(os, other), oabuf);
	}
	atker->movesleft -= utypes[a].hittime;
    }
}

/* There are many consequences of a unit being captured. */
/* If the capturer is needed as a garrison, unload any occupants first. */
/* (what if erstwhile occupants can't stay there?) */

capture_unit(unit, pris)
Unit *unit, *pris;
{
    int u = unit->type, u2, px = pris->x, py = pris->y, i;
    int occs[MAXUTYPES], gains[MAXUTYPES], kills[MAXUTYPES];
    Unit *occ;
    Side *ps = pris->side, *us = unit->side;

    for_all_unit_types(u2) occs[u2] = gains[u2] = kills[u2] = 0;
    notify(us, "You captured %s!", unit_handle(us, pris));
    notify(ps, "%s has been captured by the %s!",
	   unit_handle(ps, pris), plural_form(us->name));
    if (global.setproduct) {
	set_product(pris, NOTHING);
	pris->schedule = 0;
    }
    for_all_occupants(pris, occ) {
	occs[occ->type]++;
	if (utypes[occ->type].changeside || could_capture(u, occ->type)) {
	    gains[occ->type]++;
	    /* side changing happens when whole unit changes */
	} else {
	    kills[occ->type]++;
	    kill_unit(occ, COMBAT);
	    for_all_unit_types(i) kills[i] += occdeath[i];
	}
    }
    summarize_units(hitbuf,  gains);
    summarize_units(killbuf, kills);
    summarize_units(spbuf, occs);
    if (strlen(hitbuf) > 0) {
	if (strlen(killbuf) > 0) {
	    notify(us, "   (Also captured%s, killed%s)", hitbuf, killbuf);
	} else {
	    notify(us, "   (Also captured%s)", hitbuf);
	}
    } else if (strlen(killbuf) > 0) {
	notify(us, "   (Also killed%s)", killbuf);
    }
    if (strlen(spbuf) > 0) {
	notify(ps, "   (Lost%s)", spbuf);
    }
    /* The sad event itself */
    unit_changes_side(pris, us, CAPTURE, PRISONER);
    /* Guard the prisoner */
    if (utypes[u].guard[pris->type] > 0) {
	for_all_occupants(unit, occ) {
	    if (can_carry(pris, occ)) {
		leave_hex(occ);
		occupy_hex(occ, px, py);
	    }
	}
	/* This may kill some of the guard's occupants unnecessarily, but */
        /* I can't think of a better solution */
	kill_unit(unit, GARRISON);
    } else if (can_carry(pris, unit)) {
	leave_hex(unit);
	occupy_hex(unit, px, py);
    } else {
	/* Capturer doesn't guard or enter, so nothing to do */
    }
    see_exact(ps, px, py);
    all_see_hex(px, py);
}

/* Nearly-raw combat statistics are hard to interpret, but they provide */
/* a useful check against subjective evaluation of performance. */

print_combat_results(fp, side)
FILE *fp;
Side *side;
{
    int a, d;

    fprintf(fp,
	    "Unit Performance (successes and attacks against enemy by type\n");
    fprintf(fp, "   ");
    for_all_unit_types(d) {
	fprintf(fp, "  %c  ", utypes[d].uchar);
    }
    fprintf(fp, "\n");
    for_all_unit_types(a) {
	fprintf(fp, " %c ", utypes[a].uchar);
	for_all_unit_types(d) {
	    if (side->atkstats[a][d] > 0) {
		fprintf(fp, " %3.1f ",
			((float) side->hitstats[a][d]) /
			         side->atkstats[a][d]);
	    } else {
		fprintf(fp, "     ");
	    }
	}
	fprintf(fp, "\n   ");
	for_all_unit_types(d) {
	    if (side->atkstats[a][d] > 0) {
		fprintf(fp, " %3d ", side->atkstats[a][d]);
	    } else {
		fprintf(fp, "     ");
	    }
	}
	fprintf(fp, "\n");
    }
    fprintf(fp, "\n");
}