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 p

⟦e009e6f2e⟧ TextFile

    Length: 15072 (0x3ae0)
    Types: TextFile
    Names: »phases.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/X/Xconq/phases.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: phases.c,v 1.1 88/06/21 12:30:38 shebs Exp $ */

/* This file includes all turn phases except init, movement, and endgame. */

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

/* Should probably be in period.h */

#define will_garrison(u1, u2) (utypes[u1].guard[u2] > 0)

/* Spying phase reveals other sides' secrets.  This phase also automatically */
/* updates allies's view of each other's units; not every turn, but fairly */
/* regularly (can do briefing at any time, of course).  Spying will reveal */
/* hex even if never seen before (dubious). */

spy_phase()
{
    Side *side, *other;

    if (Debug) printf("Entering spy phase\n");
    for_all_sides(side) {
	if (probability(period.spychance)) {
	    other = side_n(random(numsides));
	    if (other != NULL && !other->lost && !allied_side(side, other)) {
		if (period.spychance < 20) {
		    notify(side, "You found out some of the %s dispositions!!",
			   other->name);
		}
		reveal_side(other, side, period.spyquality);
	    }
	}
	for_all_sides(other) {
	    if (allied_side(other, side) && side != other && probability(20)) {
		reveal_side(side, other, 100);
	    }
	}
    }
}

/* The disaster phase handles revolts, surrenders, accidents, and attrition. */
/* Note that accidents and attrition happen even if unit changes sides. */
/* Also, revolts happen first, so something that revolts may subsequently */
/* surrender to nearby unit. */

disaster_phase()
{
    Unit *unit;

    if (Debug) printf("Entering disaster phase\n");
    for_all_units(unit) {
	if (alive(unit)) unit_revolt(unit);
	if (alive(unit)) unit_surrender(unit);
	if (alive(unit)) unit_accident(unit);
	if (alive(unit)) unit_attrition(unit);
    }
}

/* A unit revolts by going back to its "true side" (which is usually set at */
/* unit creation time).  The base revolt chance is worst case if morale is */
/* variable; a unit at maximum (nonzero) morale will never revolt. */

unit_revolt(unit)
Unit *unit;
{
    int u = unit->type, maxmor, chance;
    Side *oldside = unit->side;

    if (utypes[u].revolt > 0) {
	chance = utypes[u].revolt;
	maxmor = utypes[u].maxmorale;
	if (maxmor > 0) chance = (chance * (maxmor - unit->morale)) / maxmor;
	if (unit->trueside != oldside && random(10000) < chance) {
	    notify(oldside, "%s revolts!", unit_handle(oldside, unit));
	    unit_changes_side(unit, unit->trueside, CAPTURE, DISASTER);
	    see_exact(oldside, unit->x, unit->y);
	    draw_hex(oldside, unit->x, unit->y, TRUE);
	    all_see_hex(unit->x, unit->y);
	}
    }
}

/* Units may also surrender to adjacent enemy units, but only to a type */
/* that is both visible and capable of capturing the unit surrendering. */
/* Neutrals have to be treated differently, since they don't have a view */
/* to work from.  We sort of compute the view "on the fly". */

unit_surrender(unit)
Unit *unit;
{
    bool surrounded = TRUE;
    int u = unit->type, chance, d, x, y, view;
    Unit *unit2, *eunit = NULL;
    Side *us = unit->side, *es;

    if (utypes[u].surrender > 0 || utypes[u].siege > 0) {
	chance = 0;
	for_all_directions(d) {
	    x = wrap(unit->x + dirx[d]);  y = limit(unit->y + diry[d]);
	    if (neutral(unit)) {
		if (((unit2 = unit_at(x, y)) != NULL) &&
		    (probability(utypes[unit2->type].visibility)) &&
		    (could_capture(unit2->type, u))) {
		    chance += utypes[u].surrender;
		    eunit = unit2;
		} else {
		    surrounded = FALSE;
		}
	    } else {
		view = side_view(us, x, y);
		if (view == EMPTY || view == UNSEEN) {
		    surrounded = FALSE;
		} else {
		    es = side_n(vside(view));
		    if (enemy_side(us, es) && could_capture(vtype(view), u)) {
			chance += utypes[u].surrender;
			if (unit_at(x, y)) eunit = unit_at(x, y);
		    }
		}
	    }
	}
	if (surrounded && eunit) chance += utypes[u].siege;
	if (random(10000) < chance) {
	    if (eunit) {
		notify(eunit->side, "%s has surrendered to you!",
		       unit_handle(eunit->side, unit));
		notify(us, "%s has surrendered to the %s!",
		       unit_handle(us, unit), plural_form(eunit->side->name));
		capture_unit(eunit, unit);
	    }
	}
    }
}

/* Accidents will happen!... Kill off any occupants of course. */

unit_accident(unit)
Unit *unit;
{
    int u = unit->type;
    Side *us = unit->side;

    if (random(10000) < utypes[u].accident[terrain_at(unit->x, unit->y)]) {
	notify(us, "%s %s!", unit_handle(us, unit), utypes[u].accidentmsg);
	kill_unit(unit, DISASTER);
    }
}

/* Attrition only takes out a few hp at a time, but can be deadly... */
/* Occupants lost only if unit lost (do we care?) */

unit_attrition(unit)
Unit *unit;
{
    int u = unit->type;
    Side *us = unit->side;

    if (random(10000) < utypes[u].attrition[terrain_at(unit->x, unit->y)]) {
	unit->hp -= utypes[u].attdamage;
	notify(us, "%s %s!%s",
	       unit_handle(us, unit), utypes[u].attritionmsg,
	       (unit->hp <= 0 ? "  The blow is fatal!" : ""));
	if (unit->hp <= 0) kill_unit(unit, DISASTER);
    }
}

/* Repair always proceeds each turn, as long as the repairing unit is not */
/* crippled.  A unit can repair itself, its transport, and its occupants. */

/* Another activity of units is to build other units.  The code here has to */
/* be a little tricky because players need to get opportunities to cancel */
/* and to idle units, plus not get asked about units that don't usually */
/* produce things. */

/* The order of repair vs building means scarce supplies used on existing */
/* units first. */

build_phase()
{
    Unit *unit, *occ;

    if (Debug) printf("Entering build phase\n");
    for_all_units(unit) {
	if (alive(unit) && !cripple(unit)) {
	    repair_unit(unit, unit);
	    for_all_occupants(unit, occ) repair_unit(unit, occ);
	    if (unit->transport != NULL) repair_unit(unit, unit->transport);
	    work_on_new_unit(unit);
	}
    }
}

/* One arbitrary unit repairs another only if actually needed.  Rate is */
/* always less than 1 hp/turn; this is a practical limit on max hp values */
/* if reasonably rapid repair is desired.  If unit being repaired was badly */
/* damaged (crippled), then we'll use up same resources as needed for */
/* construction, and if a resource type is missing, then repairs will not */
/* proceed at all. */

repair_unit(unit, unit2)
Unit *unit, *unit2;
{
    int u = unit->type, u2 = unit2->type, r;

    if ((unit2->hp < utypes[u2].hp) && could_repair(u, u2)) {
	if ((global.time % utypes[u].repair[u2]) == 0) {
	    if (cripple(unit2)) {
		for_all_resource_types(r) {
		    if (unit->supply[r] < utypes[u2].tomake[r]) return;
		}
		for_all_resource_types(r)
		    unit->supply[r] -= utypes[u2].tomake[r] / utypes[u2].hp;
	    }
	    unit2->hp++;
	}
    }
}

/* Machine players need opportunities to change their production. */
/* Neutrals never produce (what could they do with the results?). */
/* Construction may be constrained by lack of resources, so don't count */
/* down on schedule or use up building materials unless we actually have */
/* enough. */

work_on_new_unit(unit)
Unit *unit;
{
    int u = unit->type, r;
    
    if (!neutral(unit)) {
	if (producing(unit)) {
	    for_all_resource_types(r) {
		if (unit->supply[r] < utypes[unit->product].tomake[r]) return;
	    }
	    for_all_resource_types(r) {
		unit->supply[r] -= utypes[unit->product].tomake[r];
	    }
	    unit->schedule--;
	    if (unit->schedule <= 0) {
		if (complete_new_unit(unit, unit->product)) {
		    if (!utypes[u].maker) {
			set_product(unit, NOTHING);
		    } else {
			set_schedule(unit);
		    }
		} else {
		    unit->schedule++;
		}
	    }
	} else {
	    if (unit->schedule > 0) unit->schedule--;
	}
    }
}

/* When one unit produces another, it may be that one can occupy the other, */
/* in either direction.  So we have to make sure that both ways work. */
/* Also the builder may become the garrison, so we have to get it out of */
/* the way before deciding about fiddling around. */
/* Morale of new unit is slightly more than morale of building unit. */
/* Returns success of the whole process. */

complete_new_unit(unit, type)
Unit *unit;
int type;
{
    bool success = FALSE;
    int ux = unit->x, uy = unit->y;
    Unit *mainunit, *newunit;
    Side *us = unit->side;

    if (will_garrison(unit->type, type)) leave_hex(unit);
    if ((newunit = create_unit(type, NULL)) != NULL) {
	mainunit = unit_at(ux, uy);
	if (mainunit == NULL) {
	    if (could_occupy(type, terrain_at(ux, uy))) {
		assign_unit_to_side(newunit, us);
		occupy_hex(newunit, ux, uy);
		success = TRUE;
	    } else {
		notify(us, "%s can't go here!", unit_handle(us, newunit));
		kill_unit(newunit, DISASTER);
	    }
	} else if (could_carry(mainunit->type, type)) {
	    if (can_carry(mainunit, newunit)) {
		assign_unit_to_side(newunit, us);
		occupy_hex(newunit, ux, uy);
		success = TRUE;
	    } else {
		notify(us, "%s will delay completion of %s until it has room.",
		       unit_handle(us, unit), utypes[type].name);
		kill_unit(newunit, -1);
	    }
	} else if (could_carry(type, mainunit->type)) {
	    if (could_occupy(type, terrain_at(ux, uy))) {
		assign_unit_to_side(newunit, us);
		occupy_hex(newunit, ux, uy);
		success = TRUE;
	    } else {
		notify(us, "%s can't go here!", unit_handle(us, newunit));
		kill_unit(newunit, DISASTER);
	    }
	} else {
	    notify(us, "Idiots! - %s can't build a %s in a %s!\n",
		   unit_handle(us, unit),
		   utypes[type].name, utypes[mainunit->type].name);
	    kill_unit(newunit, DISASTER);
	}
    }
    if (will_garrison(unit->type, type)) occupy_hex(unit, ux, uy);
    if (success) {
	if (utypes[type].named) newunit->name = make_unit_name(newunit);
	newunit->morale = min(newunit->morale, unit->morale + 1);
	unit->built++;
	(us->balance[type][PRODUCED])++;
	(us->units[type])++;
	if (will_garrison(unit->type, type)) kill_unit(unit, GARRISON);
	update_state(us, type);
	if (Debug) printf("%s is completed\n", unit_handle(NULL, newunit));
    }
    return success;
}

/* The main routine does production, distribution, and discarding in order. */

supply_phase()
{
    int u, r, t, amt, dist, x, y, x1, y1, x2, y2;
    Unit *unit, *ounit, *occ;

    if (Debug) printf("Entering supply phase\n");
    flush_dead_units();
    /* Make new resources but don't clip to storage capacity yet */
    for_all_units(unit) {
	u = unit->type;
	for_all_resource_types(r) {
	    t = terrain_at(unit->x, unit->y);
	    amt = (utypes[u].produce[r] * utypes[u].productivity[t]) / 100;
	    if (cripple(unit))
		amt = (amt * unit->hp) / (utypes[u].crippled + 1);
	    unit->supply[r] += amt;
	}
    }
    /* Move stuff around - hopefully will get rid of any excess */
    for_all_units(unit) {
	for_all_resource_types(r) {
            dist = utypes[unit->type].outlength[r];
	    y1 = unit->y - dist;
	    y2 = unit->y + dist;
	    for (y = y1; y <= y2; ++y) {
		if (between(0, y, world.height-1)) {
		    x1 = unit->x - (y < unit->y ? (y - y1) : dist);
		    x2 = unit->x + (y > unit->y ? (y2 - y) : dist);
		    for (x = x1; x <= x2; ++x) {
			ounit = unit_at(wrap(x), y);
			if (ounit != NULL && alive(ounit) &&
			    ounit->side == unit->side) {
			    try_transfer(unit, ounit, r);
			    for_all_occupants(ounit, occ)
				try_transfer(unit, occ, r);
			}
		    }
		}
	    }
	}
    }
    /* Throw away any excess */
    for_all_units(unit) {
	u = unit->type;
	for_all_resource_types(r) {
	    unit->supply[r] = min(unit->supply[r], utypes[u].storage[r]);
	}
    }
}

/* The middle subphase of supply uses this routine to move supplies around */
/* between units far apart or on the same hex. */

try_transfer(from, to, r)
Unit *from, *to;
int r;
{
    int nd;

    if (from == to) return;
    if (utypes[to->type].inlength[r] >=
	distance(from->x, from->y, to->x, to->y)) {
	if ((nd = utypes[to->type].storage[r] - to->supply[r]) > 0) {
	    if (can_satisfy_need(from, r, nd)) {
		transfer_supply(from, to, r, nd);
	    } else if (can_satisfy_need(from, r, max(1, nd/2))) {
		transfer_supply(from, to, r, max(1, nd/2));
	    }
	}
    }
}

/* This estimates what can be spared.  Note that total transfer of requested */
/* amount is not a good idea, since the supplies might be essential to the */
/* unit that has them first.  If we're more than half full, or the request */
/* is less than 1/5 of our supply, then we can spare it. */

can_satisfy_need(unit, r, need)
Unit *unit;
int r, need;
{
    return (((2 * unit->supply[r]) > (utypes[unit->type].storage[r])) ||
	    (need < (utypes[unit->type].storage[r] / 5)));
}

/* Move supply from one unit to another.  Don't move more than is possible; */
/* check both from and to amounts and capacities. */

transfer_supply(from, to, r, amount)
Unit *from, *to;
int r, amount;
{
    amount = min(amount, from->supply[r]);
    amount = min(amount, utypes[to->type].storage[r] - to->supply[r]);
    from->supply[r] -= amount;
    to->supply[r] += amount;
    if (Debug) printf("%s receives %d %s\n",
		      unit_handle(NULL, to), amount, rtypes[r].name);
    return amount;
}

/* Handle constant consumption, which is a post process to movement. */
/* Don't care about current side in this phase.  This is also a handy */
/* place to count up all of a side's resources, for use by win/lose tests. */

consumption_phase()
{
    int r;
    Unit *unit;
    Side *side;

    if (Debug) printf("Entering consumption phase\n");
    for_all_sides(side) {
	for_all_resource_types(r) {
	    side->resources[r] = 0;
	}
    }
    for_all_units(unit) {
	if (alive(unit)) {
	    unit_consumes(unit);
	}
    }
}

/* Consume the constant overhead part of supply consumption. */
/* Usage by movement is subtracted from overhead first. */
/* Finally, credit side with the unit's remaining supplies. */

unit_consumes(unit)
Unit *unit;
{
    int u = unit->type, r, usedup;
    
    for_all_resource_types(r) {
	if (utypes[u].consume[r] > 0) {
	    usedup = unit->actualmoves * utypes[u].tomove[r];
	    if (usedup < utypes[u].consume[r])
		unit->supply[r] -= (utypes[u].consume[r] - usedup);
	    if (unit->supply[r] <= 0 && !in_supply(unit)) {
		exhaust_supply(unit);
		return;
	    }
	}
	if (!neutral(unit)) unit->side->resources[r] += unit->supply[r];
    }
}

/* What happens to a unit that runs out of a supply.  If it can survive */
/* on no supplies, then there may be a few turns of grace, depending on */
/* how the dice roll... */

exhaust_supply(unit)
Unit *unit;
{
    if (probability(100 - utypes[unit->type].survival)) {
	notify(unit->side, "%s %s!",
	       unit_handle(unit->side, unit), utypes[unit->type].starvemsg);
	kill_unit(unit, STARVATION);
    }
}

/* Check if the unit has ready access to a source of supplies. */

in_supply(unit)
Unit *unit;
{
    if (unit->transport != NULL) return TRUE;
    /* needs to be more sophisticated and account for supply lines */
    return FALSE;
}