3224 lines
No EOL
96 KiB
JSON
3224 lines
No EOL
96 KiB
JSON
{
|
|
"chunk": "game_trainerAiE000_E68A",
|
|
"unit": "game/trainerAiE000",
|
|
"unitLabels": {
|
|
"game/trainerAiE000": {
|
|
"E733": "addEnemyThreatWeight",
|
|
"E743": "judgeGroupThreatTotal"
|
|
}
|
|
},
|
|
"notes": {
|
|
"E68A": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"Compass direction tables shared by the whole trainer AI. Directions are numbered 0-7 clockwise",
|
|
"from north: 0 N, 1 NE, 2 E, 3 SE, 4 S, 5 SW, 6 W, 7 NW. A ninth entry (index 8) holds the delta",
|
|
"pair 0,0 so that 'no direction at all' can be expressed as an ordinary index - assessThreatAroundAiComcen",
|
|
"returns 8 when nothing threatens the AI comcen. Read by $E241/$E274 (comcen push/retreat),",
|
|
"$E482 (path search), offsetPointInDirection ($E66F/$E67E) and getStepDirection ($E816/$E81E)."
|
|
],
|
|
"line": "column step of directions 0-7: N 0, NE +1, E +1, SE +1, S 0, SW -1, W -1, NW -1"
|
|
},
|
|
"E692": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "index 8: the null direction, column step 0"
|
|
},
|
|
"E693": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "row step of directions 0-7: N -1, NE -1, E 0, SE +1, S +1, SW +1, W 0, NW -1"
|
|
},
|
|
"E69B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "index 8: the null direction, row step 0"
|
|
},
|
|
"E69C": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"isGroupDestinationTooRisky - influence-map test of one candidate destination for the AI group in currentAiGroup ($8955).",
|
|
"Steps Y cells in direction X away from (cellColB,cellRowB) with offsetPointInDirection, grows that cell into the rectangle the",
|
|
"whole formation would cover (the four span bytes $890E left / $891A up / $8926 right / $8932 down, clamped to 0..39) and plants",
|
|
"the four edges into the four CMP operands of its own scan loop. It then walks all 100 units and sums a signed 16-bit score:",
|
|
"every unit whose square influence area (side 2*unitThreatRadius $0400) overlaps the rectangle adds unitThreatWeight ($0464) if",
|
|
"it is one of the human player's units 0-49 and subtracts it if it is one of the AI's own 50-99 (units of the moving group",
|
|
"itself were already given radius 0 by buildUnitThreatTables). The verdict compares the enemy surplus with the group's own",
|
|
"combat value $8956/$8957.",
|
|
"In: X = direction 0-7, Y = distance in cells, zp_27/zp_28 = origin column/row; $8955 currentAiGroup, $8954 aiGroupCount,",
|
|
" span tables $890E/$891A/$8926/$8932, threat tables $0400/$0464 (built by buildUnitThreatTables), group value $8956/$8957,",
|
|
" $896E aggression-phase clock, $91CB game clock",
|
|
"Out: C=1 = reject (off the map, or enemy threat >= own combat value); C=0 = acceptable. zp_18/zp_19 = the candidate cell,",
|
|
" zp_1A/zp_1B = the signed 16-bit threat surplus, zp_1C = the radius of the unit being weighed.",
|
|
" Self-modifies the CMP operands at $E704, $E70B, $E715 and $E71C.",
|
|
"Called from: findTrainerAdvanceCell ($E444) only."
|
|
],
|
|
"line": "candidate cell = origin (cellColB,cellRowB) moved Y cells in direction X"
|
|
},
|
|
"E69F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the candidate is on the map, so weigh it"
|
|
},
|
|
"E6A1": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the step ran off the edge: return C=1 = reject"
|
|
},
|
|
"E6A2": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "Y = the group being moved"
|
|
},
|
|
"E6A5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "is it one of the aiGroupCount live groups?"
|
|
},
|
|
"E6A8": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: measure the threat over its formation footprint"
|
|
},
|
|
"E6AA": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no such group, so there is nothing to be afraid of"
|
|
},
|
|
"E6AB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "return C=0 = destination acceptable"
|
|
},
|
|
"E6AC": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"Grow the candidate cell into the rectangle the whole formation would stand on, clamp it to the 40x40",
|
|
"map, and write its four edges straight into the four CMP instructions of the unit scan below."
|
|
],
|
|
"line": "candidate column"
|
|
},
|
|
"E6AE": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "prepare the subtraction"
|
|
},
|
|
"E6AF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "minus the group's span to the left = left edge of the formation box"
|
|
},
|
|
"E6B2": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "still on the map"
|
|
},
|
|
"E6B4": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "hangs off the left edge: clamp the box to column 0"
|
|
},
|
|
"E6B6": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "self-modify: the CMP at $E714 now tests against the box's left column"
|
|
},
|
|
"E6B9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "candidate column again"
|
|
},
|
|
"E6BB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "prepare the addition"
|
|
},
|
|
"E6BC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "plus the group's span to the right = right edge of the box"
|
|
},
|
|
"E6BF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "past the last column (40)?"
|
|
},
|
|
"E6C1": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no, keep it"
|
|
},
|
|
"E6C3": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: clamp the box to column 39"
|
|
},
|
|
"E6C5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "self-modify: the CMP at $E71B now tests against the box's right column"
|
|
},
|
|
"E6C8": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "candidate row"
|
|
},
|
|
"E6CA": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "prepare the subtraction"
|
|
},
|
|
"E6CB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "minus the group's span upwards = top edge of the box"
|
|
},
|
|
"E6CE": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "still on the map"
|
|
},
|
|
"E6D0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "hangs off the top: clamp the box to row 0"
|
|
},
|
|
"E6D2": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "self-modify: the CMP at $E703 now tests against the box's top row"
|
|
},
|
|
"E6D5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "candidate row again"
|
|
},
|
|
"E6D7": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "prepare the addition"
|
|
},
|
|
"E6D8": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "plus the group's span downwards = bottom edge of the box"
|
|
},
|
|
"E6DB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "past the last row (40)?"
|
|
},
|
|
"E6DD": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no, keep it"
|
|
},
|
|
"E6DF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: clamp the box to row 39"
|
|
},
|
|
"E6E1": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "self-modify: the CMP at $E70A now tests against the box's bottom row"
|
|
},
|
|
"E6E4": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"Walk all 100 units, unit 99 down to unit 0, and accumulate a signed 16-bit 'how dangerous is this",
|
|
"square' score in zp_1A/zp_1B: enemy influence adds, friendly influence subtracts. A unit only",
|
|
"counts when its influence square (centre = its cell, half-width = unitThreatRadius) overlaps the",
|
|
"formation box just computed."
|
|
],
|
|
"line": "clear the running score"
|
|
},
|
|
"E6E6": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "threat surplus, low byte"
|
|
},
|
|
"E6E8": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "threat surplus, high byte"
|
|
},
|
|
"E6EA": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "start at unit 99"
|
|
},
|
|
"E6EC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unitThreatRadius[unit] from the $0400 influence page"
|
|
},
|
|
"E6EF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "radius 0 (destroyed, spy/comcen, or a member of the moving group): the unit does not count"
|
|
},
|
|
"E6F1": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "keep this unit's influence radius"
|
|
},
|
|
"E6F3": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unit 50-99 = one of the AI's own?"
|
|
},
|
|
"E6F5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no, it is one of the human player's units 0-49"
|
|
},
|
|
"E6F7": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "own unit: is the group being moved group 0, the AI comcen's own group?"
|
|
},
|
|
"E6FA": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: friendly cover is not credited to the comcen group, so skip own units entirely"
|
|
},
|
|
"E6FC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "row of this unit"
|
|
},
|
|
"E6FF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "keep the row for the second test"
|
|
},
|
|
"E700": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "prepare the addition"
|
|
},
|
|
"E701": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "row + radius = bottom edge of its influence square"
|
|
},
|
|
"E703": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "operand patched at $E6D2 with the box's top row"
|
|
},
|
|
"E705": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its influence stops above the box: no overlap, next unit"
|
|
},
|
|
"E707": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the row again (the CMP above left C=1 for the subtraction)"
|
|
},
|
|
"E708": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "row - radius = top edge of its influence square"
|
|
},
|
|
"E70A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "operand patched at $E6E1 with the box's bottom row"
|
|
},
|
|
"E70C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its influence starts below the box: no overlap"
|
|
},
|
|
"E70E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "column of this unit"
|
|
},
|
|
"E711": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "keep the column for the second test"
|
|
},
|
|
"E712": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "column + radius = right edge of its influence square (the CMP above left C=0)"
|
|
},
|
|
"E714": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "operand patched at $E6B6 with the box's left column"
|
|
},
|
|
"E716": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its influence stops left of the box: no overlap"
|
|
},
|
|
"E718": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the column again (the CMP above left C=1 for the subtraction)"
|
|
},
|
|
"E719": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "column - radius = left edge of its influence square"
|
|
},
|
|
"E71B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "operand patched at $E6C5 with the box's right column"
|
|
},
|
|
"E71D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its influence starts right of the box: no overlap"
|
|
},
|
|
"E71F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the squares overlap, so whose unit is it?"
|
|
},
|
|
"E721": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "human player's unit 0-49: its weight adds to the danger"
|
|
},
|
|
"E723": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "one of the AI's own: subtract its weight instead (friendly cover)"
|
|
},
|
|
"E725": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unitThreatWeight[unit] from the $0464 influence page (the CPY above left C=1)"
|
|
},
|
|
"E728": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store the surplus low byte"
|
|
},
|
|
"E72A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "surplus high byte"
|
|
},
|
|
"E72C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "propagate the borrow"
|
|
},
|
|
"E72E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store it back"
|
|
},
|
|
"E730": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "on to the next unit"
|
|
},
|
|
"E733": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "surplus low byte"
|
|
},
|
|
"E735": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "add unitThreatWeight[unit] (the CPY at $E71F left C=0)"
|
|
},
|
|
"E738": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store it back"
|
|
},
|
|
"E73A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "surplus high byte"
|
|
},
|
|
"E73C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "propagate the carry"
|
|
},
|
|
"E73E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store it back"
|
|
},
|
|
"E740": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "next unit"
|
|
},
|
|
"E741": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "until unit 0 has been weighed"
|
|
},
|
|
"E743": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"Verdict. A negative or zero surplus means the AI already dominates the square. Otherwise the enemy",
|
|
"surplus is compared with this group's own combat value, and an exact draw is settled by the game clock",
|
|
"and the RNG so that both machines would reach the same answer."
|
|
],
|
|
"line": "sign of the 16-bit surplus"
|
|
},
|
|
"E745": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "not negative: weigh it against the group's own strength"
|
|
},
|
|
"E747": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "friendly influence outweighs the enemy there"
|
|
},
|
|
"E748": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "C=0 = destination acceptable"
|
|
},
|
|
"E749": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "surplus low byte"
|
|
},
|
|
"E74B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "is the whole 16-bit surplus exactly zero?"
|
|
},
|
|
"E74D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no enemy influence at all: accept"
|
|
},
|
|
"E74F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "surplus low byte"
|
|
},
|
|
"E751": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "prepare the subtraction"
|
|
},
|
|
"E752": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "minus the group's own combat value, low byte"
|
|
},
|
|
"E755": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "keep the difference"
|
|
},
|
|
"E757": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "surplus high byte"
|
|
},
|
|
"E759": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "minus the group's combat value, high byte"
|
|
},
|
|
"E75C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "keep it"
|
|
},
|
|
"E75E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "is the difference exactly zero?"
|
|
},
|
|
"E760": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: return the carry of the subtraction (C=1 = threat >= own strength = reject)"
|
|
},
|
|
"E762": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "dead heat: consult the aggression-phase clock set at setup ($EF00 area) or by retreatTrainerComcen"
|
|
},
|
|
"E765": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "has the game reached that round? the game clock counts down, so 'phase clock >= clock' means yes"
|
|
},
|
|
"E768": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes, late enough in the game: toss a coin"
|
|
},
|
|
"E76A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "still early: play safe"
|
|
},
|
|
"E76B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "always taken (the BCS above fell through with C=1): return C=1 = reject"
|
|
},
|
|
"E76D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "step the lock-step game RNG so both machines stay in sync"
|
|
},
|
|
"E770": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "C=1 for half of the random values, i.e. reject a tie 50 percent of the time"
|
|
},
|
|
"E772": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "carry tells the caller whether the destination is too dangerous"
|
|
},
|
|
"E773": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"buildUnitThreatTables - rebuilds the two 100-entry influence-map tables that isGroupDestinationTooRisky reads.",
|
|
"For each unit 99..0 it writes an influence radius into $0400 and a combat weight into $0464. Destroyed units, spies and",
|
|
"comcens (type 3 and 4) and every AI unit belonging to the group that is about to be moved get radius 0 and weight 0.",
|
|
"A grunt or rider gets base radius 3, a boomer 6, both plus the terrain sight bonus of the tile the unit is standing on,",
|
|
"so units on high ground project further. The weight comes from unitCombatValueTable ($E2B7) = 2,2,3,0,0.",
|
|
"In: $8955 currentAiGroup, aiUnitGroup ($8800+absolute unit index), unitTypeTable $F76C, unitTerrainUnderTable $FAF0,",
|
|
" terrainClassTable $077E, terrainSightBonusTable $35BB, unitCombatValueTable $E2B7",
|
|
"Out: $0400[0..99] = influence radius, $0464[0..99] = combat weight; zp_18 and zp_19 clobbered",
|
|
"Called from: findTrainerAdvanceCell ($E42A), immediately before its search loop."
|
|
],
|
|
"block": [
|
|
"One entry per unit, walking backwards from unit 99 to unit 0."
|
|
],
|
|
"line": "start at unit 99"
|
|
},
|
|
"E775": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "is this one of the AI's own units 50-99?"
|
|
},
|
|
"E777": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no, it is a human player unit: no group test needed"
|
|
},
|
|
"E779": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "aiUnitGroup[unit] - the group table addressed by absolute index ($8800+unit = $8832+ordinal)"
|
|
},
|
|
"E77C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "does it belong to the very group we are about to move?"
|
|
},
|
|
"E77F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: a group must not weigh its own members"
|
|
},
|
|
"E781": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unit type byte, bit 7 = destroyed"
|
|
},
|
|
"E784": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "alive: work out its influence radius"
|
|
},
|
|
"E786": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "destroyed, or a member of the moving group: no influence"
|
|
},
|
|
"E788": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "always taken (A = 0): store radius 0"
|
|
},
|
|
"E78A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "default influence radius 3 cells"
|
|
},
|
|
"E78C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unit type 0-4 without the status bits"
|
|
},
|
|
"E78E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "keep the type for the weight look-up"
|
|
},
|
|
"E790": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "GRUNT (0) or RIDER (1)?"
|
|
},
|
|
"E792": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: keep radius 3"
|
|
},
|
|
"E794": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "type 3 SPY or 4 COMCEN: they project no threat at all"
|
|
},
|
|
"E796": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "BOOMER (2): its guns reach twice as far, radius 6"
|
|
},
|
|
"E798": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "stash the base radius"
|
|
},
|
|
"E79A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "terrain code of the tile hidden under the unit"
|
|
},
|
|
"E79D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "terrain class 0-8 of that tile"
|
|
},
|
|
"E7A0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "class becomes the index"
|
|
},
|
|
"E7A1": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "extra range the terrain grants: 0, 1 or 2 cells"
|
|
},
|
|
"E7A4": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "prepare the addition"
|
|
},
|
|
"E7A5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "radius = base radius + terrain bonus"
|
|
},
|
|
"E7A7": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unitThreatRadius[unit] into the $0400 scratch page"
|
|
},
|
|
"E7AA": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "is the radius zero?"
|
|
},
|
|
"E7AB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: A is already 0, so the weight becomes 0 too"
|
|
},
|
|
"E7AD": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unit type"
|
|
},
|
|
"E7AF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "combat weight: GRUNT 2, RIDER 2, BOOMER 3, SPY 0, COMCEN 0"
|
|
},
|
|
"E7B2": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unitThreatWeight[unit] into the $0464 scratch page"
|
|
},
|
|
"E7B5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "next unit"
|
|
},
|
|
"E7B6": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "until unit 0 has an entry"
|
|
},
|
|
"E7B8": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "both influence tables are current"
|
|
},
|
|
"E7B9": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"multiplyAbyY - 8-bit multiply by repeated addition: A = A * Y modulo 256, and A = 0 when Y = 0.",
|
|
"The multiplicand is copied into its own ADC operand and Y is restored from a second patched operand before returning,",
|
|
"so the caller's step count survives the call.",
|
|
"In: A = multiplicand, Y = count",
|
|
"Out: A = A*Y; Y unchanged; X untouched, flags clobbered; self-modifies the operands at $E7CB and $E7D0",
|
|
"Called from: offsetCoordinateClamped ($EA22) only, where A is a step of 0, +1 or $FF and Y a cell distance."
|
|
],
|
|
"line": "self-modify: stash the count in the LDY at $E7CF so Y can be handed back"
|
|
},
|
|
"E7BC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "self-modify: the multiplicand becomes the operand of the ADC at $E7CA"
|
|
},
|
|
"E7BF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "multiply by zero?"
|
|
},
|
|
"E7C1": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: run the addition loop"
|
|
},
|
|
"E7C3": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "Y is 0, so A = 0"
|
|
},
|
|
"E7C4": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "always taken: return zero"
|
|
},
|
|
"E7C6": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "one addition per unit of the count"
|
|
},
|
|
"E7C7": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "count used up: A holds the product"
|
|
},
|
|
"E7C9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the carry before each addition"
|
|
},
|
|
"E7CA": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "operand patched at $E7BC: add the multiplicand once more"
|
|
},
|
|
"E7CC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "keep adding; a running total of exactly 0 would end the loop early, which the callers' 0/+1/-1 steps never produce"
|
|
},
|
|
"E7CE": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "hold the product across the Y restore"
|
|
},
|
|
"E7CF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "operand patched at $E7B9: give the caller its count back"
|
|
},
|
|
"E7D1": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "product back into A"
|
|
},
|
|
"E7D2": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "A = A*Y"
|
|
},
|
|
"E7D3": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"buildDirectionFanTowardTarget - finds the compass heading from the line start (lineCurCol,lineCurRow) to the line end",
|
|
"(lineEndCol,lineEndRow) with getStepDirection, saves it in zp_19 and falls into buildDirectionFan so the eight candidate",
|
|
"move directions are laid out around that heading. Nothing is built when start and target are the same cell.",
|
|
"In: zp_92/zp_93 = start row/column, zp_94/zp_95 = target row/column, $8955 currentAiGroup",
|
|
"Out: zp_19 = heading 0-7 ($FF and no fan when start = target); $8896-$889D = the eight candidate directions;",
|
|
" zp_18 and the line-stepper state $8C-$97 clobbered",
|
|
"Called from: the group-movement code at $E416."
|
|
],
|
|
"line": "heading of the first Bresenham step from the group centre toward its target"
|
|
},
|
|
"E7D6": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "remember the heading for buildDirectionFan"
|
|
},
|
|
"E7D8": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "a real heading 0-7: lay the eight candidates out around it"
|
|
},
|
|
"E7DA": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "Y = $FF, the group is already on its target cell: no fan to build"
|
|
},
|
|
"E7DB": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"dirMirrorTable - eight bytes, entry d = (8 - d) AND 7, i.e. the mirror of a heading offset about the",
|
|
"north-south axis (0 stays 0, +1 becomes -1 and so on). buildDirectionFan runs the offsets of a",
|
|
"'left-handed' group through it so that half the groups sweep clockwise and half anticlockwise.",
|
|
"The label in the middle of the table is a branch target of the modem-driver build at the same address,",
|
|
"not a separate block."
|
|
],
|
|
"line": "mirror of heading offsets 0-4: 0, -1, -2, -3, -4 expressed modulo 8"
|
|
},
|
|
"E7E0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "mirror of heading offsets 5-7: +3, +2, +1"
|
|
},
|
|
"E7E3": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"buildDirectionFan - fills the eight-byte scratch buffer $8896-$889D with the move directions the path search should try,",
|
|
"in order of preference: entry i = (dirFanOffsetTable[i] + heading + 8) AND 7. dirFanOffsetTable ($894A) is built once per",
|
|
"game by the setup overlay's buildAiHeadingSearchOrder ($EEC3) as 0, +/-1, +/-1, +/-2, +/-2, +/-3, +/-3, +/-4 with a random",
|
|
"left/right bias, so the search always tries straight ahead first and fans outwards. When bit 0 of the group's groupFlags",
|
|
"is set every offset is mirrored through dirMirrorTable, giving that group the opposite handedness.",
|
|
"In: zp_19 = wanted heading 0-7, $8955 currentAiGroup, $893E groupFlags, $894A dirFanOffsetTable",
|
|
"Out: $8896-$889D = eight candidate directions; zp_18 = $00/$FF mirror flag; A/X/Y clobbered",
|
|
"Called from: decideTrainerGroupAction ($E36E) and by falling through from buildDirectionFanTowardTarget ($E7D8).",
|
|
"The list is consumed by the move search at $E431-$E47E, whose per-rank distance caps are in maxAdvanceByRankTable ($E664)."
|
|
],
|
|
"line": "assume the fan is not mirrored"
|
|
},
|
|
"E7E5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the group being moved"
|
|
},
|
|
"E7E8": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its groupFlags; the low nibble is the random start delay handed out at setup"
|
|
},
|
|
"E7EB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bit 0 of that delay serves as the group's fixed handedness"
|
|
},
|
|
"E7ED": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear: fan out the normal way"
|
|
},
|
|
"E7EF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "set: X = $FF, mirror every offset"
|
|
},
|
|
"E7F0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "mirror flag for the loop below"
|
|
},
|
|
"E7F2": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "fill the eight preference slots, 7 down to 0"
|
|
},
|
|
"E7F4": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "heading offset of preference rank Y"
|
|
},
|
|
"E7F7": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "is this group mirrored? (BIT leaves A alone)"
|
|
},
|
|
"E7F9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: use the offset as it is"
|
|
},
|
|
"E7FB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "offset becomes the index"
|
|
},
|
|
"E7FC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: swap left for right"
|
|
},
|
|
"E7FF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the carry before adding"
|
|
},
|
|
"E800": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "candidate = wanted heading + offset"
|
|
},
|
|
"E802": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the addition above may have carried"
|
|
},
|
|
"E803": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bias by a whole turn so a negative offset stays positive"
|
|
},
|
|
"E805": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "wrap back into a compass direction 0-7"
|
|
},
|
|
"E807": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store the candidate for preference rank Y"
|
|
},
|
|
"E80A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "next rank"
|
|
},
|
|
"E80B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "until all eight ranks are filled"
|
|
},
|
|
"E80D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the fan is ready for the path search"
|
|
},
|
|
"E80E": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"getStepDirection - sets a Bresenham walker up between (lineCurCol,lineCurRow) and (lineEndCol,lineEndRow), takes exactly one",
|
|
"step, and converts the deltas that step applied ($90F5 column, $90F4 row) back into one of the eight compass directions by",
|
|
"searching dirToColDelta/dirToRowDelta from index 7 downwards. stepLine zeroes both deltas before it moves, so a zero-length",
|
|
"line leaves 0,0 - which matches no entry 0-7 and comes out as Y = $FF.",
|
|
"In: zp_92/zp_93 = start row/column, zp_94/zp_95 = end row/column",
|
|
"Out: Y = direction 0-7, or $FF when start and end are the same cell; A = the last delta compared;",
|
|
" line-stepper state $8C-$97 and $90F4/$90F5 updated",
|
|
"Called from: buildDirectionFanTowardTarget ($E7D3), assessThreatAroundAiComcen ($EC35), the drone code ($EEC7)",
|
|
"and the $EE00 drone-flight overlay ($EEB1)."
|
|
],
|
|
"line": "set the Bresenham walker up between the two cells"
|
|
},
|
|
"E811": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "take one step; it records the deltas it applied in $90F5 (column) and $90F4 (row), zeroing them first"
|
|
},
|
|
"E814": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "search the direction tables backwards from 7"
|
|
},
|
|
"E816": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "column step of direction Y"
|
|
},
|
|
"E819": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "does it match the step just taken?"
|
|
},
|
|
"E81C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: try the next direction"
|
|
},
|
|
"E81E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "row step of direction Y"
|
|
},
|
|
"E821": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "does that match too?"
|
|
},
|
|
"E824": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "both match: Y is the heading"
|
|
},
|
|
"E826": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "next direction down"
|
|
},
|
|
"E827": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "index 8 (the null delta) is deliberately never tested, so Y = $FF means 'the line has no step'"
|
|
},
|
|
"E829": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "Y = compass direction, or $FF"
|
|
},
|
|
"E82A": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"cellDistance - integer distance between two map cells. distanceSquared ($3870) gives |dcol|^2 + |drow|^2 (each delta capped",
|
|
"at 15, the sum saturating at $FF with C=1), and this routine converts it back through squaresTable ($3860) into the largest n",
|
|
"with n*n <= dsq. A saturated squared distance simply reports 16.",
|
|
"In: zp_22/zp_24 = cell column/row, zp_27/zp_28 = reference column/row",
|
|
"Out: A = X = distance 0-16; Y untouched",
|
|
"Called from: $E410, $E4B4, assessThreatAroundAiComcen ($EC24), the missile code ($ED43/$EE0C/$EE5C) and the drone overlay."
|
|
],
|
|
"line": "A = squared distance between (cellColA,cellRowA) and (cellColB,cellRowB), C=1 if it saturated at 255"
|
|
},
|
|
"E82D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "16 is the largest distance this routine reports"
|
|
},
|
|
"E82F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "saturated: everything beyond 15 cells is simply 'far'"
|
|
},
|
|
"E831": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "otherwise start the search at 15"
|
|
},
|
|
"E832": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "is the squared distance at least X*X?"
|
|
},
|
|
"E835": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: X is the integer distance"
|
|
},
|
|
"E837": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "try the next smaller radius"
|
|
},
|
|
"E838": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "X = 0 falls out of the loop as distance 0"
|
|
},
|
|
"E83A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "hand the distance back in A as well as X"
|
|
},
|
|
"E83B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "A = X = distance in cells"
|
|
},
|
|
"E83C": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"updateFocusGroupCentre - keeps focusGroupId ($895D) and the focus centre ($895E column / $895F row) current. The focus group",
|
|
"is the assault group the AI comcen walks along behind. When the id has bit 7 set the centre falls back on the AI comcen's own",
|
|
"cell, and a suspended group is taken back into service as soon as bit 7 of its groupFlags has cleared. An active group whose",
|
|
"groupFlags bit 7 is set (it has reached its objective) is suspended by ORing $80 into the id. Otherwise the centroid of the",
|
|
"group's combat members is recomputed, and a group that has lost every member is dropped ($895D = $FF).",
|
|
"In: $895D focusGroupId, $893E groupFlags, unitCol[99] $F6A3, unitRow[99] $F707, $8832 aiUnitGroup",
|
|
"Out: $895D, $895E = centre column, $895F = centre row; zp_18-zp_21 clobbered",
|
|
"Called from: planTrainerRound ($E20C), once per round just before the comcen push/retreat tail."
|
|
],
|
|
"line": "focusGroupId: $FF = no group, bit 7 set = the group is suspended"
|
|
},
|
|
"E83F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "an active group id 0-15: go and refresh its centre"
|
|
},
|
|
"E841": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no usable group: fall back on the AI comcen's own cell (unit 99)"
|
|
},
|
|
"E844": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "focus centre column ($895E)"
|
|
},
|
|
"E847": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "AI comcen row"
|
|
},
|
|
"E84A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "focus centre row"
|
|
},
|
|
"E84D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "was the focus group dropped altogether?"
|
|
},
|
|
"E84F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: nothing left to revive"
|
|
},
|
|
"E851": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "suspended group: recover its id"
|
|
},
|
|
"E852": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "strip the suspend bit"
|
|
},
|
|
"E854": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "index the group tables with it"
|
|
},
|
|
"E855": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its groupFlags"
|
|
},
|
|
"E858": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bit 7 still set (the group is at its objective): leave it suspended"
|
|
},
|
|
"E85A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the flag cleared: put the group back in service"
|
|
},
|
|
"E85D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "always taken (an id 0-15 is positive): recompute its centre"
|
|
},
|
|
"E85F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "nothing to update this round"
|
|
},
|
|
"E860": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "groupFlags of the active focus group"
|
|
},
|
|
"E863": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "still on its way: recompute the centre"
|
|
},
|
|
"E865": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bit 7 set: the group has arrived"
|
|
},
|
|
"E866": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "mark the id as suspended"
|
|
},
|
|
"E868": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store it back; the centre keeps the comcen fallback from the next call"
|
|
},
|
|
"E86B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "done for this round"
|
|
},
|
|
"E86C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "group id for the centroid scan"
|
|
},
|
|
"E86F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "scan AI ordinals 45 down to 0 - the combat units only, since 46-48 are the spies and 49 the comcen"
|
|
},
|
|
"E871": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "A = member count, X = average column, Y = average row"
|
|
},
|
|
"E874": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the group still has members"
|
|
},
|
|
"E876": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no member left"
|
|
},
|
|
"E878": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "drop the focus group; X/Y are 0/$FF here, so the centre is stale for the one round until the fallback runs"
|
|
},
|
|
"E87B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "focus centre column"
|
|
},
|
|
"E87E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "focus centre row"
|
|
},
|
|
"E881": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the comcen now has something to escort"
|
|
},
|
|
"E882": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"computeGroupCentroid - averages the position and the ordered destination of every living member of one AI group. It scans AI",
|
|
"ordinals Y down to 0 (absolute units 50+Y down to 50), accumulates four 16-bit sums and divides each by the member count with",
|
|
"divideSigned16by8. The wanted group id is patched into its own CMP, and the two averaged coordinates are handed back through",
|
|
"two patched immediate operands so that A can carry the member count.",
|
|
"In: A = group id, Y = highest AI ordinal to scan (usually $31 = 49 or $2D = 45); $8832 aiUnitGroup, $F79E unitType,",
|
|
" $F672 unitCol, $F6D6 unitRow, $F802 unitDestCol, $F866 unitDestRow (all indexed by AI ordinal)",
|
|
"Out: A = member count (Z set when the group is empty), X = average column, Y = average row, zp_1E = average destination column,",
|
|
" zp_1F = average destination row, zp_1C = member count; zp_18-zp_21 clobbered;",
|
|
" self-modifies the operands at $E89D, $E913 and $E915",
|
|
"Called from: computeTrainerGroupStats ($E5B9) and updateFocusGroupCentre ($E871)."
|
|
],
|
|
"line": "self-modify: the wanted group id becomes the operand of the CMP at $E89C"
|
|
},
|
|
"E885": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear all nine accumulator bytes"
|
|
},
|
|
"E887": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "column sum, low"
|
|
},
|
|
"E889": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "column sum, high"
|
|
},
|
|
"E88B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "row sum, low"
|
|
},
|
|
"E88D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "row sum, high"
|
|
},
|
|
"E88F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "member count"
|
|
},
|
|
"E891": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "ordered-destination column sum, low"
|
|
},
|
|
"E893": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "ordered-destination column sum, high"
|
|
},
|
|
"E895": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "ordered-destination row sum, low"
|
|
},
|
|
"E897": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "ordered-destination row sum, high"
|
|
},
|
|
"E899": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"One pass per AI ordinal, from the caller's starting ordinal down to 0."
|
|
],
|
|
"line": "aiUnitGroup[ordinal] - which group this AI unit belongs to"
|
|
},
|
|
"E89C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "operand patched at $E882: is it the group we are averaging?"
|
|
},
|
|
"E89E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: next ordinal"
|
|
},
|
|
"E8A0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "type byte of AI unit 50+ordinal"
|
|
},
|
|
"E8A3": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bit 7 set: destroyed, so it does not count"
|
|
},
|
|
"E8A5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "one more live member"
|
|
},
|
|
"E8A7": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its current column"
|
|
},
|
|
"E8AA": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the carry"
|
|
},
|
|
"E8AB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "into the 16-bit column sum"
|
|
},
|
|
"E8AD": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store the low byte"
|
|
},
|
|
"E8AF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "column sum, high byte"
|
|
},
|
|
"E8B1": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "propagate the carry"
|
|
},
|
|
"E8B3": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store it back"
|
|
},
|
|
"E8B5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its current row"
|
|
},
|
|
"E8B8": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the carry"
|
|
},
|
|
"E8B9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "into the 16-bit row sum"
|
|
},
|
|
"E8BB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store the low byte"
|
|
},
|
|
"E8BD": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "row sum, high byte"
|
|
},
|
|
"E8BF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "propagate the carry"
|
|
},
|
|
"E8C1": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store it back"
|
|
},
|
|
"E8C3": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the column it has been ordered to"
|
|
},
|
|
"E8C6": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the coordinate is in bits 0-5; bits 6-7 are order flags"
|
|
},
|
|
"E8C8": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the carry"
|
|
},
|
|
"E8C9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "into the destination-column sum"
|
|
},
|
|
"E8CB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store the low byte"
|
|
},
|
|
"E8CD": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no carry"
|
|
},
|
|
"E8CF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "carry into the high byte"
|
|
},
|
|
"E8D1": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the row it has been ordered to"
|
|
},
|
|
"E8D4": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "coordinate bits only"
|
|
},
|
|
"E8D6": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the carry"
|
|
},
|
|
"E8D7": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "into the destination-row sum"
|
|
},
|
|
"E8D9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store the low byte"
|
|
},
|
|
"E8DB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no carry"
|
|
},
|
|
"E8DD": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "carry into the high byte"
|
|
},
|
|
"E8DF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "next ordinal"
|
|
},
|
|
"E8E0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "down to AI ordinal 0"
|
|
},
|
|
"E8E2": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"Divide the four sums by the member count. The two positional averages are returned in X and Y",
|
|
"through the two immediate operands patched below."
|
|
],
|
|
"line": "member count is the divisor"
|
|
},
|
|
"E8E4": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no members: skip the divisions and report zero"
|
|
},
|
|
"E8E6": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "column sum, low"
|
|
},
|
|
"E8E8": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "column sum, high"
|
|
},
|
|
"E8EA": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "average column"
|
|
},
|
|
"E8ED": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "self-modify: it becomes the operand of the LDX at $E912"
|
|
},
|
|
"E8F0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "row sum, low"
|
|
},
|
|
"E8F2": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "row sum, high"
|
|
},
|
|
"E8F4": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "divisor again (divideSigned16by8 clobbers X)"
|
|
},
|
|
"E8F6": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "average row"
|
|
},
|
|
"E8F9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "self-modify: it becomes the operand of the LDY at $E914"
|
|
},
|
|
"E8FC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "divisor again"
|
|
},
|
|
"E8FE": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "destination-column sum, low"
|
|
},
|
|
"E900": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "destination-column sum, high"
|
|
},
|
|
"E902": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "average ordered destination column"
|
|
},
|
|
"E905": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "returned in zp_1E"
|
|
},
|
|
"E907": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "divisor again"
|
|
},
|
|
"E909": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "destination-row sum, low"
|
|
},
|
|
"E90B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "destination-row sum, high"
|
|
},
|
|
"E90D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "average ordered destination row"
|
|
},
|
|
"E910": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "returned in zp_1F, overwriting the sum byte that has just been consumed"
|
|
},
|
|
"E912": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "operand patched at $E8ED: X = average column"
|
|
},
|
|
"E914": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "operand patched at $E8F9: Y = average row"
|
|
},
|
|
"E916": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "A = member count, Z set when the group is empty"
|
|
},
|
|
"E918": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "caller gets count in A, centre in X/Y, average destination in zp_1E/zp_1F"
|
|
},
|
|
"E919": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"orderAiUnitToCell - the trainer's central 'issue a move order' helper. It does nothing when the unit's ordered destination",
|
|
"already is the requested cell, so redundant orders never take up room in the queue or in the packet. Otherwise it falls into",
|
|
"queueMirroredMoveOrder, which mirrors both coordinates (the engine will mirror them back because it believes the command",
|
|
"arrived from a remote opponent) and queues $80 MOVE UNIT or $81 MOVE GROUP.",
|
|
"In: X = target column, A = target row, Y = AI ordinal 0-49 (absolute unit = Y+50)",
|
|
"Out: nothing when the order is redundant; otherwise $8896 = $80/$81, $8897 = absolute unit index,",
|
|
" $8898/$8899 = mirrored column/row, and the command appended to the AI queue at $88A0",
|
|
"Called from: retreatTrainerComcen ($E262), pushTrainerComcenForward ($E294), issueTrainerGroupOrders ($E4F7)",
|
|
"and haltAiComcen ($EBEC)."
|
|
],
|
|
"line": "stage the target column as the command's first coordinate"
|
|
},
|
|
"E91C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "and the target row as the second"
|
|
},
|
|
"E91F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the column AI unit 50+Y has already been ordered to"
|
|
},
|
|
"E922": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "coordinate bits only; bits 6-7 are order flags"
|
|
},
|
|
"E924": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "same column as the new order?"
|
|
},
|
|
"E927": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: the order is worth sending"
|
|
},
|
|
"E929": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the row it has already been ordered to"
|
|
},
|
|
"E92C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "coordinate bits only"
|
|
},
|
|
"E92E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "same row too?"
|
|
},
|
|
"E931": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: send the order"
|
|
},
|
|
"E933": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the unit is already under exactly this order: queue nothing"
|
|
},
|
|
"E934": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "target column, ready for the mirror below"
|
|
},
|
|
"E937": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"queueMirroredMoveOrder - second half of orderAiUnitToCell. Mirrors the staged coordinates with mirrorMapCoordinate (39 - v),",
|
|
"picks command $80 cmdMoveUnit or $81 cmdMoveGroup according to bit 6 of the unit's flags, converts the AI ordinal into the",
|
|
"absolute unit index and tail-jumps into queueAiCommand. Both players see their own side at the bottom of the map, so an",
|
|
"incoming command's coordinates are always mirrored by the engine; the trainer pre-mirrors so the round trip comes out right.",
|
|
"In: $8898/$8899 = target column/row, Y = AI ordinal 0-49, $F73A unitFlags indexed by ordinal",
|
|
"Out: the four bytes $8896-$8899 staged and queued",
|
|
"Called from: reached only by falling through from orderAiUnitToCell ($E934); the XREF entries $E917/$EA01 belong to the",
|
|
"modem-driver build that occupies the same addresses."
|
|
],
|
|
"line": "39 - column: the engine mirrors it back when it 'receives' the packet"
|
|
},
|
|
"E93A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store the mirrored column"
|
|
},
|
|
"E93D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "target row"
|
|
},
|
|
"E940": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "39 - row"
|
|
},
|
|
"E943": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store the mirrored row"
|
|
},
|
|
"E946": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "command $80 = cmdMoveUnit"
|
|
},
|
|
"E948": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unitFlags of AI unit 50+Y"
|
|
},
|
|
"E94B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bit 6 = the unit is a member of an in-game group"
|
|
},
|
|
"E94D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "not grouped: keep $80"
|
|
},
|
|
"E94F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "grouped: use $81 = cmdMoveGroup so its formation follows"
|
|
},
|
|
"E950": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "command byte"
|
|
},
|
|
"E953": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "AI ordinal 0-49"
|
|
},
|
|
"E954": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the carry"
|
|
},
|
|
"E955": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "plus 50: the AI plays side 1, so its units are 50-99"
|
|
},
|
|
"E957": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "first parameter of the command ($8897)"
|
|
},
|
|
"E95A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "append the staged command to the AI order queue"
|
|
},
|
|
"E95D": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"isPathToPlayerComcenClear - walks a Bresenham line from the current line position (which the caller has set to the group",
|
|
"centre) to the human player's comcen (unit 49) and reports whether any living enemy fighting unit stands on it. Only human",
|
|
"grunts, riders and boomers (types 0-2) block; the AI's own units, destroyed units, spies and the comcen do not. Both exits",
|
|
"put the group centre back into the line position through loadGroupCentreIntoLinePos.",
|
|
"In: zp_92/zp_93 = start row/column, $8955 currentAiGroup, $8954 aiGroupCount, $F671/$F6D5 player comcen cell, map at $F000",
|
|
"Out: C=1 = the lane to the comcen is open, C=0 = blocked or the group id was invalid; zp_92/zp_93 = the group centre;",
|
|
" zp_48/zp_49 (map pointer) and the line-stepper state $8C-$97 clobbered",
|
|
"Called from: decideTrainerGroupAction ($E3B1), to decide whether the group may make a straight run at the comcen."
|
|
],
|
|
"line": "the group asking for a straight run"
|
|
},
|
|
"E960": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "is it one of the live groups?"
|
|
},
|
|
"E963": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: report 'blocked' and do nothing else"
|
|
},
|
|
"E965": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "column of the human player's comcen (unit 49)"
|
|
},
|
|
"E968": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "line target column"
|
|
},
|
|
"E96A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its row"
|
|
},
|
|
"E96D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "line target row"
|
|
},
|
|
"E96F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "set the walker up from the current position to the comcen"
|
|
},
|
|
"E972": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"Walk the lane one cell at a time, stopping at the first live enemy fighting unit."
|
|
],
|
|
"line": "advance one cell along the lane"
|
|
},
|
|
"E975": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the line ran out without a blocker: restore the centre and return C=1"
|
|
},
|
|
"E977": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "column the walker is standing on"
|
|
},
|
|
"E979": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "and its row"
|
|
},
|
|
"E97B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "map byte of that cell; bit 7 set means a unit occupies it"
|
|
},
|
|
"E97E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bare terrain: keep walking"
|
|
},
|
|
"E980": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "strip the occupancy bit to get the unit index"
|
|
},
|
|
"E982": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "one of the AI's own units 50-99?"
|
|
},
|
|
"E984": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: a unit never blocks its own side's lane"
|
|
},
|
|
"E986": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "human player's unit: index it"
|
|
},
|
|
"E987": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its type byte"
|
|
},
|
|
"E98A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "destroyed: not an obstacle"
|
|
},
|
|
"E98C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unit type 0-4"
|
|
},
|
|
"E98E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "type 3 SPY or 4 COMCEN?"
|
|
},
|
|
"E990": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: neither blocks a charge"
|
|
},
|
|
"E992": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "a live grunt, rider or boomer sits in the lane: put the group centre back"
|
|
},
|
|
"E995": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "C=0 = the lane is blocked (also the exit for an invalid group id)"
|
|
},
|
|
"E996": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "carry tells the caller whether it may charge"
|
|
},
|
|
"E997": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"loadGroupCentreIntoLinePos - copies the current group's centre (groupCentreCol $88EA / groupCentreRow $88F6) into the line",
|
|
"walker's position and returns with C=1. It doubles as the 'path is clear' exit of isPathToPlayerComcenClear, which is why it",
|
|
"ends with SEC.",
|
|
"In: $8955 currentAiGroup, $88EA/$88F6 per-group centre tables",
|
|
"Out: zp_93 = centre column, zp_92 = centre row, C=1; A and Y clobbered",
|
|
"Called from: isPathToPlayerComcenClear ($E992) and as its C=1 exit ($E975)."
|
|
],
|
|
"line": "the current group"
|
|
},
|
|
"E99A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its centre column"
|
|
},
|
|
"E99D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "line walker column"
|
|
},
|
|
"E99F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its centre row"
|
|
},
|
|
"E9A2": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "line walker row"
|
|
},
|
|
"E9A4": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "C=1: this is also the 'lane is clear' answer of isPathToPlayerComcenClear"
|
|
},
|
|
"E9A5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "line position restored"
|
|
},
|
|
"E9A6": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"scoreTargetBlock - scores the 2x2 block of map cells whose top-left corner is (X,Y) as a bombardment target. A missile and a",
|
|
"drone both cover 2x2 cells, so all four are added up: every unit standing in them contributes targetValueByUnitType, positive",
|
|
"for the human player's units and negative for the AI's own. A block with a negative total is never chosen; otherwise, when the",
|
|
"total beats the running best in zp_1C, the corner is remembered in zp_95/zp_94 and the highest human unit index found in the",
|
|
"block becomes the nominal target in zp_1D (which favours the comcen, unit 49).",
|
|
"In: X = column, Y = row of the top-left cell; zp_1C = best score so far (may be negative to accept anything),",
|
|
" zp_1D = best target so far; map at $F000, unitTypeTable $F76C",
|
|
"Out: zp_1A = the block's score, zp_1B = highest human unit index in it; on an improvement zp_1C, zp_1D, zp_94 and zp_95 are",
|
|
" updated; zp_48/zp_49 = map pointer; self-modifies the operands at $E9E4 and $E9E8",
|
|
"Called from: the missile/drone target scan at $EFA8, and from the $EE00 drone-flight overlay."
|
|
],
|
|
"line": "self-modify: keep the block's column in the LDA at $E9E3"
|
|
},
|
|
"E9A9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "self-modify: keep the block's row in the LDA at $E9E7"
|
|
},
|
|
"E9AC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the accumulators"
|
|
},
|
|
"E9AE": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "score of this 2x2 block"
|
|
},
|
|
"E9B0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "highest human unit index seen in it"
|
|
},
|
|
"E9B2": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "top-left cell; readMapCell also leaves mapCellPtr pointing at it"
|
|
},
|
|
"E9B5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bare terrain"
|
|
},
|
|
"E9B7": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "a unit stands here: score it"
|
|
},
|
|
"E9BA": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"The other three cells of the block are reached straight through mapCellPtr: +1 is the cell to the",
|
|
"right, +40 the cell below (the map is 40 cells wide) and +41 the diagonal."
|
|
],
|
|
"line": "offset +1 = the cell to the right"
|
|
},
|
|
"E9BC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "read it"
|
|
},
|
|
"E9BE": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bare terrain"
|
|
},
|
|
"E9C0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "score the unit standing there"
|
|
},
|
|
"E9C3": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "offset +40 = one map row down"
|
|
},
|
|
"E9C5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "read it"
|
|
},
|
|
"E9C7": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bare terrain"
|
|
},
|
|
"E9C9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "score it"
|
|
},
|
|
"E9CC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "offset +41 = down and to the right"
|
|
},
|
|
"E9CE": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "read it"
|
|
},
|
|
"E9D0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bare terrain"
|
|
},
|
|
"E9D2": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "score it"
|
|
},
|
|
"E9D5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "total value of the block"
|
|
},
|
|
"E9D7": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "negative: the AI's own units dominate it, so never bombard it"
|
|
},
|
|
"E9D9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "better than the best block found so far?"
|
|
},
|
|
"E9DB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: take it"
|
|
},
|
|
"E9DD": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no, but is the running best itself still negative (nothing chosen yet)?"
|
|
},
|
|
"E9DF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: keep the previous best"
|
|
},
|
|
"E9E1": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "new best score"
|
|
},
|
|
"E9E3": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "operand patched at $E9A6: the block's column"
|
|
},
|
|
"E9E5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "remember it as the aim point"
|
|
},
|
|
"E9E7": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "operand patched at $E9A9: the block's row"
|
|
},
|
|
"E9E9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "aim point row"
|
|
},
|
|
"E9EB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the biggest human unit index in the block"
|
|
},
|
|
"E9ED": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "becomes the nominal target unit (49 = the comcen wins any tie)"
|
|
},
|
|
"E9EF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "a new best target has been recorded"
|
|
},
|
|
"E9F0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "rejected: the running best is left alone"
|
|
},
|
|
"E9F1": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"targetValueByUnitType - five bytes indexed by unit type, the bombardment value of a unit standing in a",
|
|
"candidate target block. Boomers and the comcen are worth four times a grunt."
|
|
],
|
|
"line": "GRUNT 1, RIDER 2, BOOMER 4, SPY 1, COMCEN 4"
|
|
},
|
|
"E9F6": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"addUnitTargetValue - helper of scoreTargetBlock. Takes a map cell byte that holds $80 plus a unit index, ignores destroyed",
|
|
"units, and adds targetValueByUnitType[type] to the block score in zp_1A - negated when the unit belongs to the AI itself, so",
|
|
"the trainer will not shell its own troops. For a human unit it also keeps the largest index seen, which is how the human",
|
|
"comcen (unit 49) ends up being preferred as the nominal target.",
|
|
"In: A = map cell byte with bit 7 set, unitTypeTable $F76C, targetValueByUnitType $E9F1, zp_1A/zp_1B accumulators",
|
|
"Out: zp_1A += the (possibly negated) value, zp_1B = highest human unit index seen; X and Y clobbered",
|
|
"Called from: scoreTargetBlock, once per cell of the 2x2 block ($E9B7, $E9C0, $E9C9, $E9D2)."
|
|
],
|
|
"line": "drop the 'a unit is here' bit: what is left is the unit index"
|
|
},
|
|
"E9F8": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "index the unit tables with it"
|
|
},
|
|
"E9F9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its type byte"
|
|
},
|
|
"E9FC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "destroyed: worth nothing"
|
|
},
|
|
"E9FE": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unit type 0-4"
|
|
},
|
|
"EA00": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "index the value table"
|
|
},
|
|
"EA01": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bombardment value of that type"
|
|
},
|
|
"EA04": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unit 50-99 = one of the AI's own?"
|
|
},
|
|
"EA06": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: its value counts against the block"
|
|
},
|
|
"EA08": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "human unit: is it the highest index seen in this block so far?"
|
|
},
|
|
"EA0A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: just add its value"
|
|
},
|
|
"EA0C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: it becomes the block's nominal target"
|
|
},
|
|
"EA0E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "always taken (the BCS above fell through with C=1)"
|
|
},
|
|
"EA10": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "negate the value ..."
|
|
},
|
|
"EA12": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the carry ..."
|
|
},
|
|
"EA13": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "... two's complement, so friendly units subtract"
|
|
},
|
|
"EA15": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the carry before accumulating"
|
|
},
|
|
"EA16": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "add into the block score"
|
|
},
|
|
"EA18": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store it back"
|
|
},
|
|
"EA1A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "cell scored"
|
|
},
|
|
"EA1B": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"offsetCoordinateClamped - returns clamp(A + X*Y, 0..39): the base coordinate A displaced by Y steps of the signed step X, held",
|
|
"inside the 40-cell battlefield, with the carry reporting whether the unclamped value had left the map. The base coordinate and",
|
|
"the caller's step count are both parked in immediate operands so nothing but A is disturbed.",
|
|
"In: A = base coordinate, X = signed step (0, +1 or $FF), Y = number of steps",
|
|
"Out: A = clamped coordinate 0-39, C=1 when the raw value was negative or past 39, Y unchanged;",
|
|
" self-modifies the operands at $EA27 and $EA36",
|
|
"Called from: pushTrainerComcenForward/retreatTrainerComcen ($E24C/$E25B/$E27E/$E28D), the path search ($E48A/$E499)",
|
|
"and offsetPointInDirection ($E675/$E684)."
|
|
],
|
|
"line": "self-modify: save the step count in the LDY at $EA35 so Y survives the multiply"
|
|
},
|
|
"EA1E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "self-modify: the base coordinate becomes the operand of the ADC at $EA26"
|
|
},
|
|
"EA21": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "A = the signed step, 0, +1 or $FF"
|
|
},
|
|
"EA22": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "A = step * number of steps"
|
|
},
|
|
"EA25": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the carry"
|
|
},
|
|
"EA26": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "operand patched at $EA1E: add the base coordinate"
|
|
},
|
|
"EA28": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "keep the raw, unclamped result for the range test"
|
|
},
|
|
"EA29": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "positive: no low clamp needed"
|
|
},
|
|
"EA2B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "walked off the top or left edge: clamp to 0"
|
|
},
|
|
"EA2D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "past the last cell (40)?"
|
|
},
|
|
"EA2F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no, keep it"
|
|
},
|
|
"EA31": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: clamp to 39"
|
|
},
|
|
"EA33": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "C=1 when the raw value was 40 or more, which also covers a negative value (>= $80)"
|
|
},
|
|
"EA35": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "operand patched at $EA1B: hand the caller its step count back"
|
|
},
|
|
"EA37": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "A = clamped coordinate, C=1 if it had to be clamped"
|
|
},
|
|
"EA38": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"mirrorBlockCornerForOpponent - converts the top-left corner of a 2x2 object (the drone) into the coordinates that the engine's",
|
|
"mirrorCmdCoordsIfRemoteDec ($5005, mirror then decrement) will turn back into that same corner: X = 39-(X+1), Y = 39-(Y+1).",
|
|
"Mirroring alone would give the bottom-right corner of the mirrored block, hence the pre-increment.",
|
|
"In: X = column, Y = row of the block's top-left cell",
|
|
"Out: X, Y = the mirrored corner; A clobbered",
|
|
"Called from: the fake drone-position packet at $EEB1 and $EF8F, and from the drone-flight overlay."
|
|
],
|
|
"line": "the engine subtracts 1 after mirroring, so add it here first"
|
|
},
|
|
"EA39": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "column+1 into A"
|
|
},
|
|
"EA3A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "39 - (column+1)"
|
|
},
|
|
"EA3D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "mirrored column back into X"
|
|
},
|
|
"EA3E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "same for the row"
|
|
},
|
|
"EA3F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "row+1 into A"
|
|
},
|
|
"EA40": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "39 - (row+1)"
|
|
},
|
|
"EA43": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "mirrored row back into Y"
|
|
},
|
|
"EA44": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "corner is now expressed the way a remote opponent would have sent it"
|
|
},
|
|
"EA45": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"aiCmdQueuePeak - a single byte of data sitting in the code stream between mirrorBlockCornerForOpponent",
|
|
"and queueAiCommand: the high-water mark of the AI's order queue. It is written and compared only",
|
|
"inside queueAiCommand and read nowhere else, so it is a development leftover."
|
|
],
|
|
"line": "longest the AI order queue has ever grown this game; nothing ever reads it back"
|
|
},
|
|
"EA46": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"queueAiCommand - appends the command staged in $8896..$8899 to the AI's 50-byte order queue at $88A0. It looks the total",
|
|
"command length up with getCommandLength, keeps a high-water mark in aiCmdQueuePeak, and when the queue would reach 50 bytes it",
|
|
"drops the command and turns the border blue instead, as a visible warning that the AI is producing orders faster than the",
|
|
"lock-step packets can carry them away.",
|
|
"In: $8896-$8899 = the staged command, $8952 aiCmdQueueLength, commandLengthTable through getCommandLength ($4D9F)",
|
|
"Out: queue at $88A0 extended, $8952 += length, $8968 incremented, $92EE = length (consumed as the copy counter),",
|
|
" $EA45 peak updated; on overflow $9003 (the border-colour shadow) = 6 and the command is thrown away",
|
|
"Called from: queueMirroredMoveOrder ($E95A) and queueComcenCommand ($EBE0); the trainer setup overlay has its own",
|
|
"call at its own $EC82."
|
|
],
|
|
"line": "count one more command issued this round"
|
|
},
|
|
"EA49": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the staged command byte"
|
|
},
|
|
"EA4C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "currentCommandLength ($92EE) = 1 command byte plus its parameters"
|
|
},
|
|
"EA4F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the carry"
|
|
},
|
|
"EA50": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "how long the queue would be afterwards"
|
|
},
|
|
"EA53": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "beyond the previous high-water mark?"
|
|
},
|
|
"EA56": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no"
|
|
},
|
|
"EA58": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "record the new peak"
|
|
},
|
|
"EA5B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the queue at $88A0 holds 50 bytes"
|
|
},
|
|
"EA5D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "it still fits"
|
|
},
|
|
"EA5F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "colour 6 = blue"
|
|
},
|
|
"EA61": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "overflow: recolour the border through the VIC shadow and drop the command"
|
|
},
|
|
"EA64": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "command lost (the round counter at $8968 was already bumped)"
|
|
},
|
|
"EA65": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "copy from the first staging byte"
|
|
},
|
|
"EA67": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "append at the current end of the queue"
|
|
},
|
|
"EA6A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "next staged byte"
|
|
},
|
|
"EA6D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "into the queue"
|
|
},
|
|
"EA70": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "advance the staging index"
|
|
},
|
|
"EA71": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "advance the queue index"
|
|
},
|
|
"EA72": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the queue is one byte longer"
|
|
},
|
|
"EA75": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "one byte of this command done"
|
|
},
|
|
"EA78": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "until the whole command has been copied"
|
|
},
|
|
"EA7A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the order is now waiting to be handed to the engine"
|
|
},
|
|
"EA7B": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"injectAiCommandsIntoPacket - the heart of the solo trainer. Where the modem build would decode a packet that arrived over the",
|
|
"wire, this build fabricates one: it copies whole commands out of the AI's own queue at $88A0 straight into the comm module's",
|
|
"received-packet buffer ($E020, length $E01E), so the lock-step engine executes the computer opponent's orders through exactly",
|
|
"the same command interpreter a human opponent's orders would use. It first runs the missile/drone decision, which may claim",
|
|
"the whole packet for itself, and then moves as many complete commands as fit while the packet stays under 8 bytes, shifting",
|
|
"the remainder of the queue down after each one.",
|
|
"In: $8952 aiCmdQueueLength, the queue at $88A0, $E01E current packet length, getCommandLength ($4D9F)",
|
|
"Out: $E020.. filled, $E01E updated, the queue compacted and $8952 reduced; $8966 cleared then possibly set by runAiMissileTurn;",
|
|
" $92EE clobbered",
|
|
"Called from: buildTrainerReplyPacket ($E0E4), once per lock-step exchange."
|
|
],
|
|
"line": "clear the flag first"
|
|
},
|
|
"EA7D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "$8966 bit 7 will mean 'the missile/drone code owns this packet'"
|
|
},
|
|
"EA80": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "offer the AI a missile shot or a drone launch; it writes straight into the packet"
|
|
},
|
|
"EA83": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "did it claim the packet?"
|
|
},
|
|
"EA86": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: leave the order queue for the next exchange"
|
|
},
|
|
"EA88": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "is anything queued at all?"
|
|
},
|
|
"EA8B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: send an empty packet"
|
|
},
|
|
"EA8D": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"One iteration per queued command: measure it, check that it still fits in the packet, copy it into the",
|
|
"packet, then slide the rest of the queue down over it."
|
|
],
|
|
"line": "read from the front of the queue"
|
|
},
|
|
"EA8F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its command byte"
|
|
},
|
|
"EA92": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "currentCommandLength = its total length"
|
|
},
|
|
"EA95": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bytes already in the fabricated 'received' packet"
|
|
},
|
|
"EA98": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the carry"
|
|
},
|
|
"EA99": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "plus this command"
|
|
},
|
|
"EA9C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "a packet carries at most 7 command bytes"
|
|
},
|
|
"EA9E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "it would not fit: the rest waits for the next exchange"
|
|
},
|
|
"EAA0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "append at the current end of the packet buffer"
|
|
},
|
|
"EAA3": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "next byte of the command"
|
|
},
|
|
"EAA6": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "into the received-packet buffer at $E020"
|
|
},
|
|
"EAA9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "advance the packet index"
|
|
},
|
|
"EAAA": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "advance the queue index"
|
|
},
|
|
"EAAB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "whole command copied?"
|
|
},
|
|
"EAAE": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: keep copying"
|
|
},
|
|
"EAB0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "new packet length"
|
|
},
|
|
"EAB3": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "now compact the queue from its start"
|
|
},
|
|
"EAB5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "byte just past the command that was taken"
|
|
},
|
|
"EAB8": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "slide it down to the front"
|
|
},
|
|
"EABB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "advance the write index"
|
|
},
|
|
"EABC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "advance the read index"
|
|
},
|
|
"EABD": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "until the old end of the queue"
|
|
},
|
|
"EAC0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "keep shifting"
|
|
},
|
|
"EAC2": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "old queue length"
|
|
},
|
|
"EAC5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "prepare the subtraction"
|
|
},
|
|
"EAC6": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "minus the command just handed over"
|
|
},
|
|
"EAC9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "new queue length"
|
|
},
|
|
"EACC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "more orders waiting: try to fit another into the same packet"
|
|
},
|
|
"EACE": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the packet the engine is about to 'receive' is ready"
|
|
},
|
|
"EACF": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"updateAiUnitTracking - per-unit hook, run from trainerUnitStepHook after the engine has stepped each unit. It maintains three",
|
|
"pieces of AI awareness. For a human unit it latches the first grunt/rider/boomer that has picked the AI comcen as its fire",
|
|
"target into comcenAttackerUnit ($8972). For one of its own units it marks that unit's group as having the human comcen in",
|
|
"reach (groupFlags bit 6) and stamps the unit for an immediate replan, once the comcen has been spotted and the unit is within",
|
|
"8 cells of it. Finally, an AI spy (units 96-98) that has reached the opponent's end zone while still inside an ordinary group",
|
|
"is split off into a brand new group of its own, seeded with its current destination, so it can operate alone.",
|
|
"In: $90FA currentUnit, $F76C unitTypeTable, $91F0 fireTargetUnit, $F9F5 human comcen energy byte (bit 7 = spotted),",
|
|
" $F671/$F6D5 human comcen cell, $F640/$F6A4 unit cells, aiUnitOrderRound ($87CE+unit), aiUnitGroup ($8800+unit),",
|
|
" aiUnitDeepFlag ($8832+unit), $91CB game clock, $8954 aiGroupCount, $8964 home group id",
|
|
"Out: $8972 comcenAttackerUnit, groupFlags $893E bit 6, $8953 last allocated group id, the unit's group and round stamp,",
|
|
" and the new group's target cell $88D2/$88DE",
|
|
"Called from: trainerUnitStepHook ($E124), for every unit the engine steps."
|
|
],
|
|
"line": "the unit the engine has just stepped"
|
|
},
|
|
"EAD2": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unit 0-49 or 50-99?"
|
|
},
|
|
"EAD4": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "one of the AI's own: run the contact tracking"
|
|
},
|
|
"EAD6": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "comcenAttackerUnit, $FF while nobody is known to be shooting at the AI comcen"
|
|
},
|
|
"EAD9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "already latched: nothing to learn"
|
|
},
|
|
"EADB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the unit this human unit has picked as its fire target"
|
|
},
|
|
"EADE": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unit 99 = the AI comcen"
|
|
},
|
|
"EAE0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "aiming at something else"
|
|
},
|
|
"EAE2": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its type byte"
|
|
},
|
|
"EAE5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unit type 0-4"
|
|
},
|
|
"EAE7": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "type 3 SPY or 4 COMCEN?"
|
|
},
|
|
"EAE9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: they cannot shoot, so they are not a threat to record"
|
|
},
|
|
"EAEB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "remember the first unit that is aiming at the AI comcen"
|
|
},
|
|
"EAEE": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "human unit handled"
|
|
},
|
|
"EAF1": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"AI unit: has it got close enough to the human comcen to count as contact? The two coordinate tests",
|
|
"accept a difference in -8..+7 by comparing the signed byte against $F8 and then against 8."
|
|
],
|
|
"line": "energy byte of the human comcen; bit 7 doubles as 'the AI has spotted it'"
|
|
},
|
|
"EAF4": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "not spotted yet: there is no contact to record"
|
|
},
|
|
"EAF6": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "column of the human comcen"
|
|
},
|
|
"EAF9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "prepare the subtraction"
|
|
},
|
|
"EAFA": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "minus this AI unit's column"
|
|
},
|
|
"EAFD": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "is the difference at least -8?"
|
|
},
|
|
"EAFF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes, within 8 columns to one side"
|
|
},
|
|
"EB01": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "is it less than +8?"
|
|
},
|
|
"EB03": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: more than 8 columns away"
|
|
},
|
|
"EB05": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "row of the human comcen"
|
|
},
|
|
"EB08": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "prepare the subtraction"
|
|
},
|
|
"EB09": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "minus this AI unit's row"
|
|
},
|
|
"EB0C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "is the difference at least -8?"
|
|
},
|
|
"EB0E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes"
|
|
},
|
|
"EB10": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "is it less than +8?"
|
|
},
|
|
"EB12": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: more than 8 rows away"
|
|
},
|
|
"EB14": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "aiUnitGroup[unit], the group table addressed by absolute index"
|
|
},
|
|
"EB17": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "is it the home/guard group?"
|
|
},
|
|
"EB1A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: that group is not supposed to chase the comcen"
|
|
},
|
|
"EB1C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its groupFlags"
|
|
},
|
|
"EB1F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bit 6 = this group has the human comcen within reach"
|
|
},
|
|
"EB21": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store the flags back"
|
|
},
|
|
"EB24": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bit 7 set (the group is already suspended): do not restamp its clock"
|
|
},
|
|
"EB26": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the down-counting round clock"
|
|
},
|
|
"EB29": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "aiUnitOrderRound[unit]: stamping the current clock makes the planner replan this unit at once"
|
|
},
|
|
"EB2C": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"An AI spy (units 96-98) that has scored both end zones is promoted out of its group into a group of its",
|
|
"own so it can act independently behind the human lines."
|
|
],
|
|
"line": "the AI comcen itself (unit 99)?"
|
|
},
|
|
"EB2E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: nothing else to do for it"
|
|
},
|
|
"EB30": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "units 96-98 are the AI's three SPYs"
|
|
},
|
|
"EB32": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "any other unit: done"
|
|
},
|
|
"EB34": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "aiUnitDeepFlag[unit], bit 7 = the unit has scored both territory zones"
|
|
},
|
|
"EB37": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "this spy has not got that far yet"
|
|
},
|
|
"EB39": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its current group id"
|
|
},
|
|
"EB3C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "does it still belong to one of the ordinary groups?"
|
|
},
|
|
"EB3F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no (already split off, or $FF): leave it alone"
|
|
},
|
|
"EB41": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "is it in the home/guard group?"
|
|
},
|
|
"EB44": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: that one stays together"
|
|
},
|
|
"EB46": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "allocate the next group id; note that aiGroupCount $8954 is not bumped with it, and that ids past 11 would run off the end of the twelve-entry per-group tables"
|
|
},
|
|
"EB49": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the new id"
|
|
},
|
|
"EB4C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "index the per-group tables with it"
|
|
},
|
|
"EB4D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the spy becomes a group of its own"
|
|
},
|
|
"EB50": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the current round"
|
|
},
|
|
"EB53": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "stamp it so the planner picks the new group up on the next scan"
|
|
},
|
|
"EB56": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the destination the spy is already heading for"
|
|
},
|
|
"EB59": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "coordinate bits only"
|
|
},
|
|
"EB5B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "becomes the new group's target column"
|
|
},
|
|
"EB5E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "and its destination row"
|
|
},
|
|
"EB61": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "coordinate bits only"
|
|
},
|
|
"EB63": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "becomes the new group's target row"
|
|
},
|
|
"EB66": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unit tracked"
|
|
},
|
|
"EB67": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"runAiComcenActions - spends whatever is left of the AI comcen's three command slots for this round. A stunned comcen needs two",
|
|
"of them: one to halt itself and one for the 1-in-8 repair attempt that queues command $9E, exactly the odds the human REPAIR",
|
|
"tab uses. An unstunned comcen is halted once and then spends every remaining slot on maybeQueueComcenEnergyRepair.",
|
|
"In: $8968 commands already issued this round, $8971 the comcen's stun counter (low nibble of unitStunTable[99])",
|
|
"Out: $8967 = the comcen's remaining slot budget, commands queued through queueAiCommand; returns silently when there is",
|
|
" no budget left",
|
|
"Called from: planTrainerRound at $E1CD (comcen too weak to attack) and $E21B (defend mode)."
|
|
],
|
|
"line": "the comcen may spend at most 3 command slots in a round"
|
|
},
|
|
"EB69": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "prepare the subtraction"
|
|
},
|
|
"EB6A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "minus the orders the planner has already queued"
|
|
},
|
|
"EB6D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "already over budget: do nothing at all"
|
|
},
|
|
"EB6F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "slots left for the comcen"
|
|
},
|
|
"EB72": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "keep the budget in X as well"
|
|
},
|
|
"EB73": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the comcen's stun counter"
|
|
},
|
|
"EB76": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "not stunned: go straight to the energy repairs"
|
|
},
|
|
"EB78": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "a stun repair attempt costs two slots"
|
|
},
|
|
"EB7A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "not enough left"
|
|
},
|
|
"EB7C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "spend one ..."
|
|
},
|
|
"EB7D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "... and the second"
|
|
},
|
|
"EB7E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the reduced budget"
|
|
},
|
|
"EB81": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "halt the comcen; a moving comcen cannot be worked on"
|
|
},
|
|
"EB84": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "step the lock-step RNG"
|
|
},
|
|
"EB87": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "one chance in eight"
|
|
},
|
|
"EB89": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the repair attempt failed this round"
|
|
},
|
|
"EB8B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "command $9E = cmdComcenRepairedOrSetupReady, which clears the comcen stun counter on both machines"
|
|
},
|
|
"EB8D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "queue it ($8897/$8898 are ignored, the command is one byte long)"
|
|
},
|
|
"EB90": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "cancel the DEC that the shared loop tail below is about to do"
|
|
},
|
|
"EB93": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "join the energy-repair loop with the remaining budget"
|
|
},
|
|
"EB96": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "slots left"
|
|
},
|
|
"EB97": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "none: nothing to do"
|
|
},
|
|
"EB99": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "halt the comcen so it can repair"
|
|
},
|
|
"EB9C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "spend one slot on an energy repair"
|
|
},
|
|
"EB9F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "one slot used"
|
|
},
|
|
"EBA2": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "keep spending until the budget is exhausted"
|
|
},
|
|
"EBA4": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the comcen has done what it can this round"
|
|
},
|
|
"EBA5": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"maybeQueueComcenEnergyRepair - the trainer's version of the human console's energy repair. With 50 percent probability, and",
|
|
"only while the AI comcen's energy is below 45 of 50, it queues command $9A cmdAddEnergy for unit 99 with the same amount the",
|
|
"human repair uses, (50 - energy) / 8 + 1. The projected energy is accumulated in $8965 (which planTrainerRound seeded from the",
|
|
"comcen's real energy and uses as its action budget), and the amount is nudged by one whenever that projection would land on a",
|
|
"multiple of four.",
|
|
"In: $FA27 = unitEnergyTable[99] (energy in bits 0-5), $8965 projected comcen energy, nextGameRandom",
|
|
"Out: $8896 = $9A, $8897 = $63 (unit 99), $8898 = the amount, command queued; $8965 raised by the same amount",
|
|
"Called from: runAiComcenActions ($EB9C), once per remaining comcen command slot."
|
|
],
|
|
"line": "step the lock-step RNG"
|
|
},
|
|
"EBA8": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "an energy repair succeeds one time in two, as on the human console"
|
|
},
|
|
"EBAA": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "failed this time: spend the slot on nothing"
|
|
},
|
|
"EBAC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "energy byte of AI comcen unit 99"
|
|
},
|
|
"EBAF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "energy 0-50 lives in bits 0-5; bits 6-7 are visibility flags"
|
|
},
|
|
"EBB1": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "45 out of 50 or better?"
|
|
},
|
|
"EBB3": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "healthy enough: do not waste a command on it"
|
|
},
|
|
"EBB5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "keep the current energy"
|
|
},
|
|
"EBB7": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "50 = a full comcen"
|
|
},
|
|
"EBB9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "prepare the subtraction"
|
|
},
|
|
"EBBA": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the shortfall"
|
|
},
|
|
"EBBC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "divide by 8 ..."
|
|
},
|
|
"EBBD": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "..."
|
|
},
|
|
"EBBE": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "... so repairs get smaller as the comcen recovers"
|
|
},
|
|
"EBBF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the carry"
|
|
},
|
|
"EBC0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "amount = shortfall/8 + 1"
|
|
},
|
|
"EBC2": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "second parameter of the command"
|
|
},
|
|
"EBC5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the carry"
|
|
},
|
|
"EBC6": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "add it to the energy the comcen is projected to end up with"
|
|
},
|
|
"EBC9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "keep the projection"
|
|
},
|
|
"EBCC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "did the projection land on a multiple of four?"
|
|
},
|
|
"EBCE": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: the amount stands"
|
|
},
|
|
"EBD0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: repair one point more so it does not"
|
|
},
|
|
"EBD3": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "and keep the projection in step with it"
|
|
},
|
|
"EBD6": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "unit 99 = the AI comcen"
|
|
},
|
|
"EBD8": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "first parameter of the command ($8897)"
|
|
},
|
|
"EBDB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "command $9A = cmdAddEnergy (unit, amount); falls straight into queueComcenCommand"
|
|
},
|
|
"EBDD": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"queueComcenCommand - shared tail of the comcen actions: stores A as the command byte and jumps into queueAiCommand, leaving",
|
|
"$8897 and $8898 as the caller staged them.",
|
|
"In: A = command byte, $8897/$8898 = parameters already staged",
|
|
"Out: the command appended to the AI order queue",
|
|
"Called from: runAiComcenActions ($EB8D) and by falling through from maybeQueueComcenEnergyRepair ($EBDB)."
|
|
],
|
|
"line": "command byte into the staging area"
|
|
},
|
|
"EBE0": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "append the staged bytes to the queue"
|
|
},
|
|
"EBE3": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "shared 'do nothing this slot' exit of the comcen action code"
|
|
},
|
|
"EBE4": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"haltAiComcen - orders the AI comcen to move to the cell it is already standing on. orderAiUnitToCell turns that into a real",
|
|
"$80/$81 command only when the comcen currently has some other destination, so this stops a moving comcen and costs nothing when",
|
|
"it is already still. A comcen has to be stationary before it can repair or launch.",
|
|
"In: $F6A3 unitCol[99], $F707 unitRow[99]",
|
|
"Out: possibly one $80/$81 command queued; nothing at all when the comcen is already halted",
|
|
"Called from: runAiComcenActions ($EB81 stunned path, $EB99 repair path)."
|
|
],
|
|
"line": "the column the AI comcen is standing on"
|
|
},
|
|
"EBE7": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "and its row"
|
|
},
|
|
"EBEA": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "AI ordinal 49 = absolute unit 99, the AI comcen"
|
|
},
|
|
"EBEC": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "ordering it to its own cell halts it; a redundant order is dropped"
|
|
},
|
|
"EBEF": {
|
|
"unit": "game/trainerAiE000",
|
|
"routine": [
|
|
"assessThreatAroundAiComcen - builds a directional pressure map around the AI comcen and picks the direction to run. Every",
|
|
"living human combat unit (ordinals 0-45: the 28 grunts, 12 riders and 6 boomers) within 20 cells adds a weight into one of",
|
|
"eight compass buckets at $8896-$889D: 1 for merely being nearby, +1 within 10 cells, and +4 more when the comcen is inside that",
|
|
"unit's weapon range (5 cells, 9 for a boomer). The base 1 is dropped once the comcen has actually been hit ($897C), so only",
|
|
"units that can reach it steer the escape. The tail rankThreatDirections ($EC82, just past this chunk) smooths the ring and",
|
|
"returns the centre of the quietest three-direction arc.",
|
|
"In: zp_27/zp_28 = AI comcen column/row (set by the caller at $E1D1), unitTypeTable/unitColTable/unitRowTable,",
|
|
" $897C = 'the comcen was hit this round or last', zp_57 game RNG low byte for the tie break",
|
|
"Out: A = (total pressure - number of threats) / 2 = overall threat level, X = deepest weapon-range surplus (0 = nothing in",
|
|
" range), Y = safest compass direction 0-7, or 8 (the null delta entry) when nothing threatens;",
|
|
" $8896-$889D and $0400-$0407 hold the direction buckets, zp_18-zp_23 clobbered",
|
|
"Called from: planTrainerRound ($E1DB), which files the results into $8962/$8960/$8961, and the drone code ($EF7B)."
|
|
],
|
|
"line": "clear the accumulators"
|
|
},
|
|
"EBF1": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "total enemy pressure"
|
|
},
|
|
"EBF3": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "number of units that count as a threat"
|
|
},
|
|
"EBF5": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "deepest weapon-range surplus seen"
|
|
},
|
|
"EBF7": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "cleared but never read again in either half of the routine: a leftover"
|
|
},
|
|
"EBF9": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the eight compass buckets, 7 down to 0"
|
|
},
|
|
"EBFB": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bucket for direction Y"
|
|
},
|
|
"EBFE": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "next direction"
|
|
},
|
|
"EBFF": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "until all eight are clear"
|
|
},
|
|
"EC01": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"One pass per human combat unit, ordinal 45 down to 0 (the roster is 28 grunts, 12 riders, 6 boomers,",
|
|
"3 spies and the comcen, so 0-45 is exactly the units that can shoot)."
|
|
],
|
|
"line": "scan the human player's units 45 down to 0"
|
|
},
|
|
"EC03": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "loop counter"
|
|
},
|
|
"EC05": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the unit being weighed"
|
|
},
|
|
"EC07": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its type byte"
|
|
},
|
|
"EC0A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "destroyed: skip it"
|
|
},
|
|
"EC0C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "weapon range 5 cells for a grunt or rider"
|
|
},
|
|
"EC0E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "bit 1 of the type marks a BOOMER (type 2); a SPY would match too, but the scan never reaches one"
|
|
},
|
|
"EC10": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "not a boomer: keep range 5"
|
|
},
|
|
"EC12": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "a boomer reaches 9 cells"
|
|
},
|
|
"EC14": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "weapon range of this unit"
|
|
},
|
|
"EC16": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its column"
|
|
},
|
|
"EC19": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "point A of the distance test"
|
|
},
|
|
"EC1B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "and the target cell of the direction test"
|
|
},
|
|
"EC1D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its row"
|
|
},
|
|
"EC20": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "point A row"
|
|
},
|
|
"EC22": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "target row"
|
|
},
|
|
"EC24": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "distance from the AI comcen, which the caller left in cellColB/cellRowB"
|
|
},
|
|
"EC27": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "20 cells"
|
|
},
|
|
"EC29": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "too far away to matter"
|
|
},
|
|
"EC2B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "keep the distance"
|
|
},
|
|
"EC2D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the comcen's own cell ..."
|
|
},
|
|
"EC2F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "... is where the direction is measured from"
|
|
},
|
|
"EC31": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "comcen row"
|
|
},
|
|
"EC33": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "line walker row"
|
|
},
|
|
"EC35": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "Y = compass direction from the comcen toward this unit"
|
|
},
|
|
"EC38": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "a real direction 0-7"
|
|
},
|
|
"EC3A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the unit is standing on the comcen's own cell: charge it to direction 0"
|
|
},
|
|
"EC3C": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"Weigh this unit: a base 1 for being nearby (suppressed once the comcen is actually under fire), +1",
|
|
"within 10 cells and +4 more when the comcen sits inside its weapon range."
|
|
],
|
|
"line": "one more threatening unit"
|
|
},
|
|
"EC3E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the weight this unit will contribute"
|
|
},
|
|
"EC40": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "was the comcen hit in this round or the previous one?"
|
|
},
|
|
"EC43": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: only units that can actually reach it get any weight"
|
|
},
|
|
"EC45": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: every nearby enemy is worth 1"
|
|
},
|
|
"EC46": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the distance again"
|
|
},
|
|
"EC48": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "closer than 10 cells?"
|
|
},
|
|
"EC4A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no: it only counts as a distant presence"
|
|
},
|
|
"EC4C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "+1 for being close"
|
|
},
|
|
"EC4D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "its weapon range"
|
|
},
|
|
"EC4F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "prepare the subtraction"
|
|
},
|
|
"EC50": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "range minus distance: how deep the comcen sits inside its envelope"
|
|
},
|
|
"EC52": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the comcen is out of range of this unit"
|
|
},
|
|
"EC54": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "+1 ..."
|
|
},
|
|
"EC55": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "+2 ..."
|
|
},
|
|
"EC56": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "+3 ..."
|
|
},
|
|
"EC57": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "+4 more: the comcen is inside this unit's weapon range"
|
|
},
|
|
"EC58": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "is this the deepest penetration so far?"
|
|
},
|
|
"EC5A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no"
|
|
},
|
|
"EC5C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "remember the worst range surplus, returned in X"
|
|
},
|
|
"EC5E": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"Add the weight to its compass bucket and to the running total, both saturating at 255."
|
|
],
|
|
"line": "the weight of this unit"
|
|
},
|
|
"EC5F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the carry"
|
|
},
|
|
"EC60": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "into the bucket of its compass direction"
|
|
},
|
|
"EC63": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no overflow"
|
|
},
|
|
"EC65": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "saturate the bucket at 255"
|
|
},
|
|
"EC67": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store the bucket back"
|
|
},
|
|
"EC6A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "the weight again"
|
|
},
|
|
"EC6B": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "clear the carry"
|
|
},
|
|
"EC6C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "into the overall pressure total"
|
|
},
|
|
"EC6E": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no overflow"
|
|
},
|
|
"EC70": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "saturate the total at 255"
|
|
},
|
|
"EC72": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "store it back"
|
|
},
|
|
"EC74": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "next human unit"
|
|
},
|
|
"EC76": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "until unit 0 has been weighed"
|
|
},
|
|
"EC78": {
|
|
"unit": "game/trainerAiE000",
|
|
"block": [
|
|
"Hand the ring over to rankThreatDirections, or - when nothing at all threatened the comcen - report",
|
|
"the null direction 8, whose entry in dirToColDelta/dirToRowDelta is the zero step."
|
|
],
|
|
"line": "did anything threaten the comcen?"
|
|
},
|
|
"EC7A": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "yes: smooth the ring and pick the quietest arc"
|
|
},
|
|
"EC7C": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "no threats: A = X = 0"
|
|
},
|
|
"EC7D": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "direction 8 = the null entry of the direction tables, i.e. 'no need to move'"
|
|
},
|
|
"EC7F": {
|
|
"unit": "game/trainerAiE000",
|
|
"line": "share the RTS at the end of rankThreatDirections"
|
|
}
|
|
},
|
|
"dataTypes": {
|
|
"E68A": [
|
|
"byte",
|
|
9
|
|
],
|
|
"E693": [
|
|
"byte",
|
|
9
|
|
],
|
|
"E7DB": [
|
|
"byte",
|
|
8
|
|
],
|
|
"E9F1": [
|
|
"byte",
|
|
5
|
|
],
|
|
"EA45": [
|
|
"byte",
|
|
1
|
|
]
|
|
},
|
|
"zpComments": {
|
|
"1C": "general scratch: frame tile code, division remainder (mid), member count / max row of a group; also the running best score of the trainer's 2x2 bombardment search",
|
|
"1D": "general scratch: bounds mode flag of computeGroupBounds (bit 7 = use destinations), division remainder (hi); the trainer's bombardment search keeps the chosen target unit here"
|
|
}
|
|
} |