|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T p
Length: 15072 (0x3ae0) Types: TextFile Names: »phases.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/X/Xconq/phases.c«
/* 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; }