├── BIN.ZIP ├── During_COVID19 ├── ac3col.py ├── forcol.py └── wonders.py ├── LICENSE ├── README.md ├── SRC ├── GRANT.PAS ├── HOTSEAT.PAS ├── LEADER.PAS ├── NATIVES.PAS ├── REALITY.PAS ├── REPAINT.PAS ├── SWITCH.PAS ├── TAXPATCH.PAS ├── TERMS.PAS └── TIMEWARP.PAS ├── ac3col.py └── tribes.py /BIN.ZIP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RedSerge/ColonizationTools/dc19e0916d945a150fa5bcd84a00363d72ce3cf0/BIN.ZIP -------------------------------------------------------------------------------- /During_COVID19/ac3col.py: -------------------------------------------------------------------------------- 1 | from forcol import * 2 | from random import random, randint, shuffle, choice 3 | from collections import defaultdict 4 | 5 | BUILDING_GROUPS = [ 6 | (45, 46, 47), 7 | (42, 43, 44), 8 | (39, 40, 41), 9 | (16, 17, 36, 37, 38), 10 | (33, 34, 35), 11 | (31, 32), 12 | (30,), 13 | (29,), 14 | (27, 28), 15 | (24, 25, 26), 16 | (21, 22, 23), 17 | (18, 19, 20), 18 | (13, 14, 15), 19 | (11, 12), 20 | (9, 10), 21 | (6, 7, 8), 22 | ] 23 | 24 | BLIST = """ 25 | Stockade 26 | Fort 27 | Fortress 28 | Armory 29 | Magazine 30 | Arsenal 31 | Docks 32 | Drydock 33 | Shipyard 34 | Town Hall 35 | Town Hall 36 | Town Hall 37 | Schoolhouse 38 | College 39 | University 40 | Warehouse 41 | Warehouse Expansion 42 | Stable 43 | Custom House 44 | Printing Press 45 | Newspaper 46 | Weaver's House 47 | Weaver's Shop 48 | Textile Mill 49 | Tobacconist's House 50 | Tobacconist's Shop 51 | Cigar Factory 52 | Rum Distiller's House 53 | Rum Distillery 54 | Rum Factory 55 | Capitol 56 | Capitol Expansion 57 | Fur Trader's House 58 | Fur Trading Post 59 | Fur Factory 60 | Carpenter's Shop 61 | Lumber Mill 62 | Church 63 | Cathedral 64 | Blacksmith's House 65 | Blacksmith's Shop 66 | Iron Works 67 | """.strip("\n").split("\n") 68 | 69 | FFLIST = """ 70 | Adam Smith 71 | Jakob Fugger 72 | Peter Minuit 73 | Peter Stuyvesant 74 | Jan de Witt 75 | Ferdinand Magellan 76 | Francisco Coronado 77 | Hernando de Soto 78 | Henry Hudson 79 | Sieur De La Salle 80 | Hernan Cortes 81 | George Washington 82 | Paul Revere 83 | Francis Drake 84 | John Paul Jones 85 | Thomas Jefferson 86 | Pocahontas 87 | Thomas Paine 88 | Simon Bolivar 89 | Benjamin Franklin 90 | William Brewster 91 | William Penn 92 | Jean de Brebeuf 93 | Juan de Sepulveda 94 | Bartolome de las Casas 95 | """.strip("\n").split("\n") 96 | 97 | CROSSLINE = "=" * 27 98 | NATIONAL_CODES = set(range(4)) 99 | NATIONAL_NAMES = ('English', 'French', 'Spanish', 'Dutch') 100 | DIRECTIONS = ('COLONY', 'TRIBE', 'LAND', 'SEA', 'SHORE', 'EUROPE') 101 | 102 | STATUS_ADDR = (0xCF, 0x103, 0x137, 0x16B) 103 | 104 | ASCEND_LVL = (0x0, 0x13, 0x14, 0x15, 0x16) 105 | ASCEND_CHANCES = (95, 80, 75, 50) 106 | ASCEND_NAMES = ('Initiate', 'Acolyte', 'Knight', 'Silencer', 'Master') 107 | ASCEND_MAX = len(ASCEND_LVL) - 1 108 | 109 | CONVERT_PROFESSIONS = (0x10, 0x14, 0x15, 0x16, 0x18) 110 | 111 | CONTRACT_FARES = [10, 50, 100, 250, 500] 112 | 113 | DEGRADE_UNITS = { 114 | 0x1: 0x0, 115 | 0x4: 0x1, 116 | 0x5: 0x0, 117 | 0x6: 0x0, 118 | 0x7: 0x1, 119 | 0x8: 0x1, 120 | 0x9: 0x0, 121 | 0xB: 0x0, 122 | 0x11: 0xE, 123 | 0x12: 0xF, 124 | } 125 | 126 | INTERACTIVE_UNITS = { 127 | 0x0: None, 128 | 0x1: 0x9, 129 | 0x4: 0x7, 130 | 0xB: None, 131 | 0x11: 0x12, 132 | } 133 | 134 | SIGNS = [i for i in range(-1, 2) if i] 135 | 136 | NATIVE_PROTECTION_DATA = [50, 16, 24, 32, 40] 137 | 138 | merge_bytes = lambda x: b''.join(x) 139 | apply_sys = lambda x, f, p: f(x)[2:].upper().zfill(p) 140 | print_sys = lambda x, f, p: ' '.join([apply_sys(i, f, p) for i in x]) 141 | print_crd = lambda x: [j for j in x[:2]] 142 | is_water = lambda maps, x, y: maps[0][y * 58 + x] % 32 in (25, 26) 143 | valid_nation = lambda u, pc: (u[3] % 16) in pc 144 | npc_nation = lambda u, pc: (u[3] % 16) in (NATIONAL_CODES - set(pc)) 145 | artillery_damaged = lambda x: (x >> 7) & 1 146 | artillery_repair = lambda x: x & 0x7F 147 | artillery_hurt = lambda x: x | 0x80 148 | 149 | def foodbaskets(baskets, food, maxf=32767): 150 | if not baskets or not food: 151 | return food, baskets 152 | k = [[i, j] for i, j in enumerate(baskets)] 153 | quantity = len(k) 154 | shuffle(k) 155 | k, f = sorted(k, key=lambda x:-x[1]), [] 156 | 157 | while True: 158 | amount = max(1, food // len(k)) 159 | for n in range(quantity, 0, -1): 160 | if not food: 161 | break 162 | i = k[n - 1] 163 | capacity = maxf - i[1] 164 | actual_load = min(amount, capacity, food) 165 | i[1] += actual_load 166 | food -= actual_load 167 | if i[1] == maxf: 168 | f.append(i) 169 | del k[k.index(i)] 170 | if not food or not k: 171 | break 172 | f.extend(k) 173 | return food, [i[1] for i in sorted(f, key=lambda x:x[0])] 174 | 175 | def ff_kidnaplogic(ffs_origin, veteran): 176 | ffs = [[int(i) for i in j] for j in ffs_origin] 177 | ffsi = [[i for i, v in enumerate(j) if (i > 6) and (v == (0 if n else 1))] for n, j in enumerate(ffs)] 178 | important = {} 179 | for i in ffsi[0]: 180 | row = [] 181 | for n, j in enumerate(ffsi[1:]): 182 | if i in j: 183 | row.append(n) 184 | important[i] = row 185 | if not important: 186 | return None 187 | priority = [i for i in important if important[i]] if veteran else None 188 | if not priority: 189 | priority = list(important.keys()) 190 | x = choice(priority) 191 | ffs[0][x] = 0 192 | enriched = None 193 | if important[x]: 194 | enriched = choice(important[x]) + 1 195 | ffs[enriched][x] = 1 196 | ffs = [''.join([str(i) for i in j]) for j in ffs] 197 | return ffs, enriched, 31 - x 198 | 199 | def royal_hurt(soldiers, cavalry, artillery, ships, rounds=32): 200 | values = [[soldiers, 'S'], [cavalry, 'C'], [artillery, 'A'], [ships, 'N']] 201 | values = [[max(0, i[0]), i[1]] for i in values] 202 | 203 | artillery_round = 0 204 | 205 | while rounds or artillery_round: 206 | 207 | special_case = (not rounds and artillery_round) 208 | 209 | at_stake = [] 210 | for pos, value in enumerate(values): 211 | count, mark = value 212 | if special_case and mark == 'A': 213 | artillery_round, rounds = 0, 1 214 | continue 215 | if (count > 1) or (mark != 'N' and count > 0): 216 | at_stake.append(values[pos]) 217 | 218 | if not at_stake: 219 | break 220 | 221 | while True: 222 | x = choice(at_stake) 223 | mark = x[1] 224 | if len(at_stake) == 1 or mark != 'N' or random() < 0.25: 225 | break 226 | 227 | if mark == 'A': 228 | if artillery_round == 0: 229 | artillery_round = 1 230 | else: 231 | x[0] -= 1 232 | artillery_round = 0 233 | elif mark == 'C': 234 | x[0] -= 1 235 | values[0][0] += 1 236 | else: 237 | x[0] -= 1 238 | 239 | rounds -= 1 240 | 241 | values[0][0] = min(32767, values[0][0]) 242 | return [i[0] for i in values] 243 | 244 | def parse_f(f): 245 | tribes, units, colonies = f.short(0x2A, 0x2E) 246 | 247 | offset = 0 248 | length = 0x186 249 | intro = f.raw[offset : offset + length] 250 | offset += length 251 | length = 202 * colonies 252 | colonies = f.raw[offset : offset + length] 253 | offset += length 254 | length = 28 * units 255 | units = f.raw[offset : offset + length] 256 | offset += length 257 | length = 316 * 4 258 | nations = f.raw[offset : offset + length] 259 | offset += length 260 | length = 18 * tribes 261 | tribes = f.raw[offset : offset + length] 262 | offset += length 263 | length = 1351 264 | middle = f.raw[offset : offset + length] 265 | offset += length 266 | length = 4 * 4176 267 | maps = f.raw[offset : offset + length] 268 | offset += length 269 | final = f.raw[offset : ] 270 | 271 | split_maps = [maps[i : i + 4176] for i in range(0, len(maps), 4176)] 272 | split_units = [units[i : i + 28] for i in range(0, len(units), 28)] 273 | split_tribes = [tribes[i : i + 18] for i in range(0, len(tribes), 18)] 274 | split_colonies = [colonies[i : i + 202] for i in range(0, len(colonies), 202)] 275 | split_nations = [nations[i: i + 316] for i in range(0, len(nations), 316)] 276 | 277 | return intro, split_colonies, split_units, split_nations, split_tribes, middle, split_maps, final 278 | 279 | def unify_f(f, bunch): 280 | intro, split_colonies, split_units, split_nations, split_tribes, middle, split_maps, final = bunch 281 | f.raw = merge_bytes([ 282 | intro, 283 | merge_bytes(split_colonies), 284 | merge_bytes(split_units), 285 | merge_bytes(split_nations), 286 | merge_bytes(split_tribes), 287 | middle, 288 | merge_bytes(split_maps), 289 | final, 290 | ]) 291 | 292 | def rank(actor): 293 | current_state = actor[2] 294 | max_lvl = len(ASCEND_LVL) - 1 295 | try: 296 | current_lvl = ASCEND_LVL.index(current_state) 297 | except ValueError: 298 | current_lvl = 0 299 | current_rank = ASCEND_NAMES[current_lvl] 300 | print(f"The rank of the assassin is: {current_rank}") 301 | return current_lvl 302 | 303 | def ascend(actor, current_lvl): 304 | if current_lvl >= ASCEND_MAX: 305 | print(f"Further growth is not possible; time to put the skills in action") 306 | elif randint(0, 99) < ASCEND_CHANCES[current_lvl]: 307 | current_lvl += 1 308 | current_rank = ASCEND_NAMES[current_lvl] 309 | print(f"The assassin has been promoted upon finishing the task and is known now as the {current_rank}") 310 | actor[2] = ASCEND_LVL[current_lvl] 311 | return current_lvl 312 | 313 | def search(bunch, pc, task_data): 314 | intro, colonies, units, nations, tribes, middle, maps, final = bunch 315 | 316 | converts = [] 317 | assassin = None 318 | 319 | for u in units: 320 | if valid_nation(u, pc): 321 | if u[2] == 0x0 and u[23] == 0x1b: 322 | converts.append(u) 323 | elif u[23] == 0x1d: 324 | assassin = u 325 | break 326 | 327 | if not assassin: 328 | print("There's no assassin to control") 329 | if not converts: 330 | print("There's no converts to draft yet") 331 | return 332 | v = prompt("The convert has been found. Do you want him to start his journey towards assassins ranks? (y/n) ") 333 | if v: 334 | x = choice(converts) 335 | x[23]=0x1D 336 | print(f"He deserts from his floak, ascending as the assassin at ({x[0]}, {x[1]})") 337 | return 338 | print(f"There's the assassin at ({assassin[0]}, {assassin[1]})") 339 | 340 | current_lvl = rank(assassin) 341 | 342 | if task_data is None: 343 | print("Can not provide assassin with the task") 344 | return assassin, current_lvl 345 | crds, how = task_data 346 | x, y = crds 347 | success = achieved_task(assassin[0], assassin[1], x, y, how=='EUROPE') 348 | success_str = "has been" if success else "is not" 349 | print(f"The previous task {success_str} accomplished at {x, y} ({how})") 350 | 351 | if success: 352 | ascend(assassin, current_lvl) 353 | result = force_task(bunch) 354 | if not result: 355 | print("Failed to create new task") 356 | return assassin, current_lvl 357 | crds, how = result 358 | x,y = crds 359 | print(f"The new task is to reach the {how} at {x, y}") 360 | 361 | return assassin, current_lvl 362 | 363 | task_supp = lambda x, x0, y0: [(i[0], i[1]) for i in x if i[0] != x0 and i[1] != y0] 364 | coords = lambda x, x0, y0: [(i[0], i[1], i) for i in x if vicinity(x0, y0, i[0], i[1], 1)] 365 | coords_units = lambda x, x0, y0, pc: [(i[0], i[1], i) for i in x if vicinity(x0, y0, i[0], i[1], 1) and npc_nation(i, pc) and i[2] in DEGRADE_UNITS] 366 | coords_at_me = lambda x, u, x0, y0: [(i[0], i[1], i) for i in x if i != u and i[0] == x0 and i[1] == y0 and i[2] in INTERACTIVE_UNITS] 367 | coords_show = lambda x: [(i[0], i[1]) for i in x] 368 | vicinity = lambda x, y, xt, yt, r: (x >= xt - r) and (x <= xt + r) and (y >= yt - r) and (y <= yt + r) 369 | in_eu = lambda x, y: (x > 56 or x < 1 or y > 70 or y < 1) 370 | 371 | def achieved_task(x, y, xt, yt, eu = False, r = 1): 372 | return in_eu(x, y) if eu else vicinity(x, y, xt, yt, r) 373 | 374 | def force_task(bunch): 375 | result = task(bunch) 376 | if result: 377 | crds, how, code = result 378 | with open("ac3.task", "w", encoding="UTF-8") as f: 379 | f.write(code) 380 | return crds, how 381 | 382 | def load_task(bunch): 383 | try: 384 | with open("ac3.task", encoding="UTF-8") as f: 385 | c = f.read() 386 | x, y, i = int(c[1:3], 16), int(c[3:5], 16), int(c[:1]) 387 | return [x, y], DIRECTIONS[i] 388 | except IOError: 389 | return force_task(bunch) 390 | 391 | def task(bunch, x0=None, y0=None, in_euro=False): 392 | intro, colonies, units, nations, tribes, middle, maps, final = bunch 393 | 394 | euro_place = [] if in_euro else [0, 0] 395 | 396 | col_xy = task_supp(colonies, x0, y0) 397 | tribe_xy = task_supp(tribes, x0, y0) 398 | settlements = set(col_xy) | set(tribe_xy) 399 | 400 | land = [] 401 | water = [] 402 | shore = [] 403 | 404 | for y in range(2, 70): 405 | for x in range(2, 56): 406 | if (x == x0 and y == y0) or ((x, y) in settlements): 407 | continue 408 | if is_water(maps, x, y): 409 | water.append((x, y)) 410 | else: 411 | land.append((x, y)) 412 | is_shore = False 413 | for iy in SIGNS: 414 | for ix in SIGNS: 415 | if is_water(maps, x + ix, y + iy): 416 | is_shore = True 417 | break 418 | if is_shore: 419 | shore.append((x, y)) 420 | break 421 | 422 | results = [col_xy, tribe_xy, land, water, shore, euro_place] 423 | final_ids = [i for i in range(len(DIRECTIONS)) if results[i]] 424 | 425 | if not final_ids: 426 | print("FATAL ERROR: no destination found") 427 | return 428 | 429 | final_id = choice(final_ids) 430 | final_choice = choice(results[final_id]) 431 | encode = f"{final_id}{apply_sys(final_choice[0], hex, 2)}{apply_sys(final_choice[1], hex, 2)}" 432 | 433 | return final_choice, DIRECTIONS[final_id], encode 434 | 435 | def versus_mayor(colonies, colonies_nearby): 436 | if not colonies_nearby: 437 | return [] 438 | 439 | col_by_nat = defaultdict(list) 440 | 441 | for i in colonies: 442 | nation = i[26] 443 | if nation in NATIONAL_CODES: 444 | col_by_nat[nation].append((i[0], i[1], i)) 445 | 446 | col_by_one = [col_by_nat[i][0] for i in NATIONAL_CODES if len(col_by_nat[i]) == 1] 447 | return [i for i in col_by_one if i in colonies_nearby] 448 | 449 | submenu = lambda x: ([print(f"{i+1}) {j[0], j[1]};", end="\t" if (i + 1) % 4 != 0 else "\n") for i, j in enumerate(x)], print() if len(x) % 4 != 0 else None) 450 | 451 | def menu_input(objects, prompt): 452 | try: 453 | return objects[int(input(prompt)) - 1] 454 | except (ValueError, IndexError): 455 | pass 456 | 457 | magnitude = lambda lvl: 2 ** (lvl - 2) 458 | 459 | def ask_kindly(cols): 460 | choice = None if len(cols) > 1 else cols[0] 461 | while not choice: 462 | print("Please provide precise colony coordinates (choose a number):") 463 | submenu(cols) 464 | choice = menu_input(cols, "Number is: ") 465 | print() 466 | return choice 467 | 468 | def act_natives(act_args): 469 | level, tribes_nearby, bunch, pc = act_args 470 | intro, split_colonies, split_units, split_nations, split_tribes, middle, split_maps, final = bunch 471 | 472 | all_colonies = [c for c in split_colonies if c[26] in pc] 473 | 474 | baskets = [] 475 | for c in all_colonies: 476 | mode = col(extra=c) 477 | baskets.append(mode.short(0x9A, signed=True)[0]) 478 | 479 | max_protection = NATIVE_PROTECTION_DATA[level] 480 | 481 | food = 0 482 | for t in tribes_nearby: 483 | increment = max_protection - t[2][4] 484 | if increment <= 0: 485 | continue 486 | t[2][4] = max_protection 487 | food += increment * NATIVE_PROTECTION_DATA[0] 488 | 489 | food_left, baskets = foodbaskets(baskets, food) 490 | 491 | for n, c in enumerate(all_colonies): 492 | mode = col(extra=c) 493 | mode.save_short(0x9A, [baskets[n]], signed=True) 494 | 495 | if food == 0: 496 | print("No protection is needed at the moment") 497 | else: 498 | print(f"Natives brought {food - food_left} food units to feed the colonists for the provided protection") 499 | 500 | def act_contract(act_args): 501 | level, bunch, pc = act_args 502 | intro, split_colonies, split_units, split_nations, split_tribes, middle, split_maps, final = bunch 503 | p = choice(pc) 504 | value = randint(CONTRACT_FARES[0], CONTRACT_FARES[level]) 505 | mode = col(extra=split_nations[p]) 506 | current_value = mode.short(0x2A, signed=True, length=4)[0] 507 | cash = min(2147483647, current_value + value) 508 | mode.save_short(0x2A, [cash], signed=True, length=4) 509 | print("The contract has been fulfilled") 510 | if current_value < cash: 511 | print(f"The {NATIONAL_NAMES[p]} cash grows from {current_value} to {cash} (by {cash - current_value})") 512 | 513 | def hard_labor(level, upon, lambda_upon, direct=False): 514 | m = level if direct else magnitude(level) 515 | current = 0 516 | while True: 517 | c = choice(upon) 518 | if not lambda_upon(c): 519 | del upon[upon.index(c)] 520 | current += 1 521 | if current >= m or not upon: 522 | break 523 | return current 524 | 525 | def act_promote(act_args): 526 | 527 | def change(x): 528 | x[2][23] = choice(CONVERT_PROFESSIONS) 529 | 530 | level, colonists = act_args 531 | current = hard_labor(level, colonists, change) 532 | s = '' if current == 1 else 's' 533 | print(f"{current} convert{s} found the new job{s}") 534 | 535 | def act_repair(act_args): 536 | 537 | def change(x): 538 | x[2][4] = artillery_repair(x[2][4]) 539 | 540 | level, artillery = act_args 541 | current = hard_labor(level, artillery, change) 542 | s = 'y has' if current == 1 else 'ies have' 543 | print(f"{current} artiller{s} been successfully repaired") 544 | 545 | def global_act_change(x): 546 | x[2][2] = INTERACTIVE_UNITS[x[2][2]] 547 | 548 | def global_act_demote(x): 549 | unit_type = x[2][2] 550 | if unit_type == 0xB: 551 | if not artillery_damaged(x[2][4]): 552 | x[2][4] = artillery_hurt(x[2][4]) 553 | return 554 | new_type = DEGRADE_UNITS[unit_type] 555 | x[2][2] = new_type 556 | return new_type not in DEGRADE_UNITS 557 | 558 | def act_train(act_args): 559 | level, army = act_args 560 | current = hard_labor(level, army, global_act_change) 561 | print(f"Promoted units: {current}") 562 | 563 | def act_navy(act_args): 564 | level, frigate = act_args 565 | current = hard_labor(level, frigate, global_act_change) 566 | print(f"New Man-O-Wars: {current}") 567 | 568 | def act_ff(act_args): 569 | level, colonies_nearby, bunch, pc = act_args 570 | intro, split_colonies, split_units, split_nations, split_tribes, middle, split_maps, final = bunch 571 | menu_choice = ask_kindly(colonies_nearby) 572 | nations = [menu_choice[2][26]] 573 | nations.extend(pc) 574 | nation_in_question_code = nations[0] 575 | nation_in_question_name = NATIONAL_NAMES[nation_in_question_code] 576 | 577 | ffs = [print_sys(col(extra=split_nations[n]).short(0x7, length=4), bin, 32) for n in nations] 578 | ffs_result = ff_kidnaplogic(ffs, level > 3) 579 | 580 | if not ffs_result: 581 | print(f"The {nation_in_question_name} Congress Hall seems to be empty") 582 | return 583 | 584 | act_ff_support = lambda code, line: col(extra=split_nations[code]).save_short(0x7, [int(ffs[line], 2)], length=4) 585 | 586 | ffs, enriched, ff_index = ffs_result 587 | act_ff_support(nation_in_question_code, 0) 588 | print(f"The Founding Father, {FFLIST[ff_index]}, has been disposed recently from the {nation_in_question_name} Congress;") 589 | if enriched is None: 590 | print("Noone heard of him ever since") 591 | else: 592 | nation_in_benefit_code = nations[enriched] 593 | nation_in_benefit_name = NATIONAL_NAMES[nation_in_benefit_code] 594 | act_ff_support(nation_in_benefit_code, enriched) 595 | print(f"Under the death threats this person eventually was forced to flee and join the {nation_in_benefit_name} Congress") 596 | 597 | def act_fire(act_args): 598 | level, colonies_nearby = act_args 599 | menu_choice = ask_kindly(colonies_nearby) 600 | city = col(extra=menu_choice[2]) 601 | buildings = [i for i in print_sys(city.short(0x84, length=6), bin, 48)] 602 | available = [i for i, v in enumerate(buildings) if i >= 6 and v != '0'] 603 | 604 | if level > 3: 605 | city.save_short(0x92, [0]) 606 | print("All hammers are gone") 607 | 608 | if not available: 609 | print(f"The colony has already been ruined to the core") 610 | return 611 | 612 | building = choice(available) 613 | group_to_blow = [i for i in [b for b in BUILDING_GROUPS if building in b][0] if i in available] 614 | building = BLIST[47 - group_to_blow[0]] 615 | for i in group_to_blow: 616 | buildings[i] = '0' 617 | buildings_updated = int(''.join(buildings), 2) 618 | city.save_short(0x84, [buildings_updated], length=6) 619 | print(f"The {building} has been blown to smithereens by an unknown force") 620 | 621 | def act_taxes(act_args): 622 | level, bunch, pc = act_args 623 | intro, split_colonies, split_units, split_nations, split_tribes, middle, split_maps, final = bunch 624 | for p in pc: 625 | mode = col(extra=split_nations[p]) 626 | tax = mode.short(1, signed = True, length = 1)[0] 627 | new_tax = max(-75, tax - randint(3, 7 if level < 4 else 21)) 628 | if tax == new_tax: 629 | print(f"The {NATIONAL_NAMES[p]} tax rate is already at its lowest") 630 | else: 631 | mode.save_short(1, [new_tax], True, 1) 632 | print(f"The {NATIONAL_NAMES[p]} tax rate has changed from {tax} to {new_tax} %") 633 | 634 | def act_massacre(act_args): 635 | current = hard_labor(8, act_args[0], global_act_demote, True) 636 | s = '' if current == 1 else 's' 637 | print(f"{current} successful strike{s}") 638 | 639 | def act_royal(act_args): 640 | mode = col(extra=act_args[0][0]) 641 | soldiers, cavalry, ships, artillery = mode.short(0x6A, 0x71, True) 642 | print(f"Royal forces before: {soldiers} Regulars, {cavalry} Cavalry, {artillery} Artillery, {ships} Man-O-Wars") 643 | soldiers, cavalry, artillery, ships = royal_hurt(soldiers, cavalry, artillery, ships) 644 | mode.save_short(0x6A, [soldiers, cavalry, ships, artillery], True) 645 | print(f"Royal forces after: {soldiers} Regulars, {cavalry} Cavalry, {artillery} Artillery, {ships} Man-O-Wars") 646 | 647 | def act_mayor(act_args): 648 | colonies_alone, bunch = act_args 649 | choice = ask_kindly(colonies_alone) 650 | nation = choice[2][26] 651 | mode = col(extra=bunch[0]) 652 | if mode.hex(STATUS_ADDR[nation])[0] == 2: 653 | print(f"The {NATIONAL_NAMES[nation]} viceroy has already been forced out of the office") 654 | else: 655 | mode.save_hex(STATUS_ADDR[nation], [2]) 656 | print(f"The {NATIONAL_NAMES[nation]} viceroy is gone") 657 | 658 | def menu(assassin, pc, level, colonies_nearby, tribes_nearby, units_nearby, units_at_me, colonies_alone, europe_status, bunch): 659 | pts = [] 660 | print(CROSSLINE) 661 | 662 | if tribes_nearby: 663 | pts.append(("Protect natives", act_natives, (level, tribes_nearby, bunch, pc), 93)) 664 | if colonies_nearby: 665 | pts.append(("Execute a contract", act_contract, (level, bunch, pc), 86)) 666 | if level > 2: 667 | pts.append(("Kidnap the Founding Father", act_ff, (level, colonies_nearby, bunch, pc), 49)) 668 | pts.append(("Blow up the colony buildings", act_fire, (level, colonies_nearby), 42)) 669 | if units_nearby: 670 | pts.append(("Ravage the enemy units", act_massacre, (units_nearby,), 27)) 671 | if colonies_alone: 672 | pts.append(("Assassinate the Mayor", act_mayor, (colonies_alone, bunch), 12)) 673 | if units_at_me: 674 | colonists, artillery, army, frigate = [], [], [], [] 675 | for u in units_at_me: 676 | utype = u[2][2] 677 | if utype == 0x0 and u[2][23] in (0x1b, 0x1d): 678 | colonists.append(u) 679 | elif utype == 0x1 or utype == 0x4: 680 | army.append(u) 681 | elif utype == 0xB and artillery_damaged(u[2][4]): 682 | artillery.append(u) 683 | elif utype == 0x11: 684 | frigate.append(u) 685 | if colonists: 686 | pts.append(("Promote converts", act_promote, (level, colonists), 78)) 687 | if artillery: 688 | pts.append(("Repair an artillery", act_repair, (level, artillery), 71)) 689 | if army: 690 | pts.append(("Train the army", act_train, (level, army), 64)) 691 | if frigate: 692 | pts.append(("Improve a frigate", act_navy, (level, frigate), 56)) 693 | if europe_status: 694 | pts.append(("Decrease taxes rate", act_taxes, (level, bunch, pc), 34)) 695 | if level > 3: 696 | pts.append(("Massacre the Royal Army", act_royal, (bunch,), 19)) 697 | 698 | for n,p in enumerate(pts): 699 | print(f"{n + 1}) {p[0]}") 700 | 701 | print(CROSSLINE) 702 | selection = menu_input(pts, "Enter the action number, ENTER - nothing to do: ") 703 | if not selection: 704 | return 705 | 706 | print(CROSSLINE) 707 | if randint(0, 99) < selection[3]: 708 | selection[1](selection[2]) 709 | else: 710 | print("The task has been failed or the target has not been found") 711 | print() 712 | assassin[2] = 0x0 713 | print("The assassin has lost its power and need to accomplish more tasks to regain abilities") 714 | input("< Press ENTER to exit >") 715 | 716 | def action(bunch, ass, lvl, pc): 717 | intro, colonies, units, nations, tribes, middle, maps, final = bunch 718 | 719 | x, y = ass[0], ass[1] 720 | 721 | europe_status = in_eu(x, y) 722 | colonies_nearby, tribes_nearby, units_nearby, units_at_me, colonies_alone = [], [], [], [], [] 723 | 724 | if not europe_status: 725 | if lvl >= 1: 726 | colonies_nearby = coords(colonies, x, y) 727 | tribes_nearby = coords(tribes, x, y) 728 | if lvl >= 2: 729 | units_at_me = coords_at_me(units, ass, x, y) 730 | if lvl >= 4: 731 | units_nearby = coords_units(units, x, y, pc) 732 | colonies_alone = versus_mayor(colonies, colonies_nearby) 733 | else: 734 | if lvl <= 2: 735 | europe_status = False 736 | 737 | menu(ass, pc, lvl, colonies_nearby, tribes_nearby, units_nearby, units_at_me, colonies_alone, europe_status, bunch) 738 | 739 | def pcs(f): 740 | npc_status = [f.hex(i)[0] for i in STATUS_ADDR] 741 | pc = [pos for pos, state in enumerate(npc_status) if not state] 742 | return pc 743 | 744 | def main(): 745 | f = col(0) 746 | b = parse_f(f) 747 | 748 | pc = pcs(f) 749 | result = search(b, pc, load_task(b)) 750 | if result: 751 | assassin, lvl = result 752 | if lvl > 0: 753 | action(b, assassin, lvl, pc) 754 | 755 | unify_f(f, b) 756 | f.save(0) 757 | 758 | 759 | if __name__ == '__main__': 760 | main() 761 | -------------------------------------------------------------------------------- /During_COVID19/forcol.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | 3 | def taxpatch(): 4 | with open(f"COLONIZE{os.path.sep}Viceroy.exe", "rb") as f: 5 | cnt = bytearray(f.read()) 6 | untax = bytearray([0x7E, 0x19, 0x8A, 0x47, 0x01, 0x8B, 0xC8, 0x98, 0x2D]) 7 | if untax in cnt: 8 | pos = cnt.index(untax) 9 | cnt[pos - 1] = cnt[pos + 9] = 0 10 | with open(f"COLONIZE{os.path.sep}Vicetax.exe", "wb") as f: 11 | f.write(cnt) 12 | print("Patched.") 13 | 14 | def rndpatch(): 15 | with open(f"COLONIZE{os.path.sep}Viceroy.exe", "rb") as f: 16 | cnt = bytearray(f.read()) 17 | unrnd = bytearray([0xA3, 0xEE, 0x28, 0xC7, 0x06]) 18 | tornd = bytearray([0x5D, 0xCB]) 19 | if unrnd in cnt: 20 | pos = cnt.index(unrnd) 21 | cnt[pos : pos + 2] = tornd 22 | with open(f"COLONIZE{os.path.sep}Vicernd.exe", "wb") as f: 23 | f.write(cnt) 24 | print("Patched.") 25 | 26 | #rndpatch(); exit() #-- not my idea; Randomizer for Colonization 27 | 28 | #taxpatch(); exit() #-- my idea; No-Taxes Mode for Colonization 29 | 30 | ABBR = ["in", "az", "ar", "ir", "ch", "ap", "si", "tu"] 31 | BASE = 4 32 | 33 | def new_tribe(x, y, nation, size): 34 | tribe = bytearray([x, y, nation, 0x00, size, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) 35 | return tribe 36 | 37 | def col2tribe(f): 38 | tribes, units, colonies = f.short(0x2A, 0x2E) 39 | 40 | offset = 0 41 | length = 0x186 42 | intro = f.raw[offset : offset + length] 43 | offset += length 44 | length = 202 * colonies 45 | colonies = f.raw[offset : offset + length] 46 | offset += length 47 | length = 28 * units 48 | units = f.raw[offset : offset + length] 49 | offset += length 50 | length = 316 * 4 51 | nations = f.raw[offset : offset + length] 52 | offset += length 53 | length = 18 * tribes 54 | tribes = f.raw[offset : offset + length] 55 | offset += length 56 | length = 1351 57 | middle = f.raw[offset : offset + length] 58 | offset += length 59 | length = 4 * 4176 60 | maps = f.raw[offset : offset + length] 61 | offset += length 62 | final = f.raw[offset : ] 63 | 64 | split_maps = [maps[i : i + 4176] for i in range(0, len(maps), 4176)] 65 | 66 | units_xy = [] 67 | split_units = [units[i : i + 28] for i in range(0, len(units), 28)] 68 | for i in split_units: 69 | units_xy += [(i[0], i[1])] 70 | units_xy = set(units_xy) 71 | 72 | split_tribes = [tribes[i : i + 18] for i in range(0, len(tribes), 18)] 73 | 74 | all_info = bytearray() 75 | all_info += intro 76 | 77 | to_erase = 0 78 | split_colonies = [colonies[i : i + 202] for i in range(0, len(colonies), 202)] 79 | 80 | for i in split_colonies: 81 | first_char = chr(i[2]) 82 | test_char = i[3:5].decode('ascii').lower() 83 | if first_char == '*' and test_char in ABBR: 84 | xy = (i[0], i[1]) 85 | if xy not in units_xy: 86 | nation = ABBR.index(test_char) + BASE 87 | flags = f"{bin(nation)[2:].zfill(4)[:4]}0000" 88 | to_erase += 1 89 | split_tribes += [new_tribe(i[0], i[1], nation, i[0x1F] * 3)] 90 | pos = xy[1] * 58 + xy[0] 91 | split_maps[1][pos] = 0b10 92 | split_maps[2][pos] = int(flags, 2) 93 | continue 94 | all_info += i 95 | 96 | all_info += units 97 | all_info += nations 98 | 99 | for i in split_tribes: 100 | all_info += i 101 | 102 | all_info += middle 103 | for i in split_maps: 104 | all_info += i 105 | all_info += final 106 | 107 | f.raw = all_info 108 | f.save_short(0x2A, [len(split_tribes)]) 109 | f.save_short(0x2C, [len(split_units)]) 110 | f.save_short(0x2E, [len(split_colonies) - to_erase]) 111 | return to_erase != 0 112 | 113 | class bits: 114 | def __init__(self, bits): 115 | self.bits = bits 116 | def __len__(self): 117 | return len(self.bits) 118 | def __getitem__(self, key): 119 | return int(self.bits[-1-key]) 120 | def __setitem__(self, key, value): 121 | self.bits[-1-key] = str(value) 122 | def __repr__(self): 123 | return str(''.join(self.bits)) 124 | 125 | class col: 126 | 127 | def short(self, n, m = None, signed = False, length = 2): 128 | if m is None: 129 | m = n 130 | res = [self.raw[i : i + length] for i in range(n, m + 1, length)] 131 | return [int.from_bytes(i, byteorder = 'little', signed = signed) for i in res] 132 | 133 | def save_short(self, n, vals, signed = False, length = 2): 134 | res = [i.to_bytes(length, byteorder = 'little', signed = signed) for i in vals] 135 | l = n 136 | for i, j in enumerate(res): 137 | self.raw[l : l + length] = j 138 | l += length 139 | 140 | def hex(self, n, m = None): 141 | if m is None: 142 | m = n 143 | return [int(self.raw[i]) for i in range(n, m + 1)] 144 | 145 | def save_hex(self, n, vals): 146 | for i, j in enumerate(vals): 147 | self.raw[n + i] = j 148 | 149 | def bits(self, n, m = None): 150 | if m is None: 151 | m = n 152 | return bits([j for j in ''.join([bin(self.raw[i])[2:].zfill(8) for i in range(n, m + 1)])]) 153 | 154 | def save_bits(self, n, vals): 155 | res = [int(''.join(vals.bits[i : i + 8]), 2) for i in range(0, len(vals), 8)] 156 | for i, j in enumerate(res): 157 | self.raw[n + i] = j 158 | 159 | def name(self, n = None): 160 | if n is None: 161 | n = self.id 162 | return f"COLONIZE{os.path.sep}Colony{str(n).zfill(2)}.sav" 163 | 164 | def __init__(self, n = None, extra = None): 165 | self.id = n 166 | if n is None: 167 | self.raw = extra 168 | else: 169 | with open(self.name(), "rb") as f: 170 | self.raw = bytearray(f.read()) 171 | 172 | def save(self, n): 173 | if self.id is None: 174 | return 175 | with open(self.name(n), "wb") as f: 176 | f.write(self.raw) 177 | 178 | def check_colonies(f): 179 | is_colony = [False] * 4 180 | colonies = f.short(0x2E)[0] 181 | if colonies: 182 | offset = 0x186 183 | steps = 202 184 | finish = offset + colonies * steps 185 | for i in range(offset, finish, steps): 186 | val = f.hex(i + 0x1A)[0] 187 | if val >= 0 and val <= 3: 188 | is_colony[val] = True 189 | if all(is_colony): 190 | return True 191 | return all(is_colony) 192 | 193 | def find_colony(f, x, y, n): 194 | is_colony = [False] * 4 195 | colonies = f.short(0x2E)[0] 196 | if colonies: 197 | offset = 0x186 198 | steps = 202 199 | finish = offset + colonies * steps 200 | for i in range(offset, finish, steps): 201 | if f.hex(i)[0] == x and f.hex(i + 1)[0] == y: 202 | f.save_hex(i + 26, [n]) 203 | 204 | def food(f, n, a): 205 | colonies = f.short(0x2E)[0] 206 | rebel_count = 0 207 | citizens_overall = 0 208 | if colonies: 209 | offset = 0x186 210 | steps = 202 211 | finish = offset + colonies * steps 212 | gain_flag = True 213 | while gain_flag: 214 | gain_flag = False 215 | for i in range(offset, finish, steps): 216 | nation = f.hex(i + 26)[0] 217 | if nation != n: 218 | continue 219 | count = f.short(i + 0x9A, signed = True)[0] 220 | gain = 0 221 | if count >= 32000: 222 | continue 223 | elif count + 100 > 32000: 224 | gain = 32000 - count 225 | else: 226 | gain = 100 227 | gain = min(a, gain) 228 | if not gain: 229 | continue 230 | a -= gain 231 | count += gain 232 | f.save_short(i + 0x9A, [count], signed = True) 233 | gain_flag = True 234 | 235 | def rebels(f, n): 236 | colonies = f.short(0x2E)[0] 237 | rebel_count = 0 238 | citizens_overall = 0 239 | if colonies: 240 | offset = 0x186 241 | steps = 202 242 | finish = offset + colonies * steps 243 | for i in range(offset, finish, steps): 244 | nation = f.hex(i + 26)[0] 245 | if nation != n: 246 | continue 247 | citizens = f.hex(i + 31)[0] 248 | count = f.short(i + 194)[0] 249 | total = f.short(i + 198)[0] 250 | citizens_overall += citizens 251 | rebel_count += int(citizens * count / total) 252 | granted = rebel_count >= 80 253 | print(f"{rebel_count} out of {citizens_overall} support idea of Independence. {'E' if granted else 'Not e'}nough to request independence.") 254 | return granted 255 | 256 | def riots(f): 257 | colonies = f.short(0x2E)[0] 258 | if colonies: 259 | offset = 0x186 260 | steps = 202 261 | finish = offset + colonies * steps 262 | for i in range(offset, finish, steps): 263 | flags = f.hex(i + 28)[0] 264 | if flags % 2 != 0: 265 | f.save_hex(i + 28, [flags - 1]) 266 | 267 | def tribeforce(f, doit = False): 268 | howmuch = 0 269 | tribes, units, colonies = f.short(0x2A, 0x2E) 270 | offset = 0x186 + 202 * colonies + 28 * units + 316 * 4 + 4 271 | for i in range(tribes): 272 | pos = offset + i * 18 273 | if doit: 274 | f.save_hex(pos, [32]) 275 | else: 276 | power = f.hex(pos)[0] 277 | if power < 32: 278 | howmuch += 32 - power 279 | return howmuch 280 | 281 | def mapval(f, n, x, y, bits = None): 282 | tribes, units, colonies = f.short(0x2A, 0x2E) 283 | offset = 0x186 + 202 * colonies + 28 * units + 316 * 4 + 18 * tribes + 1351 + n * 4176 284 | pos = offset + y * 58 + x 285 | if bits is None: 286 | return f.bits(pos) 287 | else: 288 | f.save_bits(pos, bits) 289 | 290 | def tax(f, n, v): 291 | units, colonies = f.short(0x2C, 0x2E) 292 | offset = 0x186 + 202 * colonies + 28 * units + 1 + 316 * n 293 | tax_value = f.hex(offset)[0] 294 | if tax_value >= 0 and tax_value <= 127: 295 | print("As a winner, you force the European Powers to provide you with a better tax rate...") 296 | f.save_hex(offset, [v]) 297 | 298 | def prompt(s): 299 | return input(s).lower().startswith('y') 300 | 301 | def main_routine(): 302 | f = col(0) 303 | if not col2tribe(f): 304 | status = f.bits(18) 305 | 306 | succession = f.short(0x62, signed = True)[0] 307 | if succession < 0: 308 | if prompt(f"Do you want to suspend the War of Succession? (y/n) "): 309 | succession = 10 310 | print("War of Succession has been suspended.") 311 | elif succession == 10: 312 | year = f.short(0x1A, signed = True)[0] 313 | if year < 1600: 314 | print("War of Succession can't be restarted before 1600 AD.") 315 | elif check_colonies(f): 316 | print("Every rival has at least one colony. War of Succession can't be restarted.") 317 | else: 318 | if prompt(f"Do you want to restart the War of Succession? (y/n) "): 319 | succession = -1 320 | print("War of Succession has been restarted.") 321 | else: 322 | print("War of Succession has already been finished.") 323 | player = None 324 | status_addr = (0xCF, 0x103, 0x137, 0x16B) 325 | npc_status = [f.hex(i)[0] for i in status_addr] 326 | if 0 in npc_status: 327 | player = npc_status.index(0) 328 | if rebels(f, player): 329 | f.save_short(0x6A, [0, 0, 0, 0]) 330 | if status[3] == 1: 331 | print("You've won the War of Independence!") 332 | if status[0] != 0: 333 | status[0] = 0 334 | succession = -1 335 | npc_status = [i if i != 2 else 1 for i in npc_status] 336 | [f.save_hex(j, [npc_status[i]]) for i, j in enumerate(status_addr)] 337 | riots(f) 338 | tax(f, player, 0x83) # -125 % if taxes are positive 339 | a = tribeforce(f) * 5 340 | if a >= 100 and prompt(f"Do you want to help indian people for {a} amounts of food? (y/n) "): 341 | tribeforce(f, True) 342 | food(f, player, a) 343 | else: 344 | print("There's no player.") 345 | 346 | f.save_hex(50, [0]) # hide map 347 | f.save_bits(18, status) 348 | f.save_short(0x62, [succession], signed = True) 349 | else: 350 | print("The Indian Extended Aid Initiative has been activated.") 351 | f.save(0) 352 | 353 | if __name__ == '__main__': 354 | main_routine() 355 | -------------------------------------------------------------------------------- /During_COVID19/wonders.py: -------------------------------------------------------------------------------- 1 | WONDERS = """ 2 | Pyramids 3 | Hanging Gardens 4 | Colossus 5 | Lighthouse 6 | Library Gr. 7 | Oracle 8 | Wall Gr. 9 | Magellan's Expedition 10 | Michelangelo's Chapel 11 | Copernicus' Observatory 12 | Shakespeare's Theatre 13 | Isaac Newton's College 14 | J.S.Bach's Cathedral 15 | Darwin's Voyage 16 | Hoover Dam 17 | Women's Suffrage 18 | Manhattan Project 19 | United Nations 20 | Apollo Program 21 | SETI Program 22 | Cure For Cancer 23 | """.split('\n')[1:-1] 24 | 25 | text = """Pyramids 26 | The Pyramids are the symbol of the strong rulers; destroying the Pyramids leads to instability that causes govts to collapse; in the light of events, new palaces and governments are reestablished, where rulers are unwilling to support rapid growing of the cities, so the irrigations are forgotten to prevent unwanted revolts and any sign of unhappiness is brutally suppressed. 27 | Handing Gardens 28 | The desolation of the Gardens brought surprising benefits for the civilization, as this event allowed a lot of barbarian folks to ravage the ruins of the Gardens and live rich lives without any further attempts to terrorize civilians, to the greatest unpleasure of Atilla. 29 | Colossus 30 | The crumble of the Colossus, the symbol of the civilization protection and great victory, encouraged Atilla to seize scientists in a series of attacks to produce a new kind of weapon in his own hidden laboratory. With necessary technologies, this attempt turned the tables in the world of armed forces; cheap yet powerful improvement to the simple guerrilla muskets made the unit even more powerful than mechanized infantry. Such events consolidate the civil society against their irreconcilable enemies. 31 | Lighthouse 32 | The lighthouse has been abandoned due to technical difficulties, but the idea of ever-shining light brought its fruits for the advanced flight technologies of the future, such as airbases and aerial refueling. The bombers no longer run out of fuel in the air. 33 | Great Library 34 | Great Library has been ravaged. This tragedy shattered the civilized world and led to the apocalyptic disaster. The world has lost all knowledge, army, most of citizens. It’s up to the rulers to recover lost connections in order to rebuild the civilization from the remains, as the ability to discover technologies is recovered. 35 | Oracle 36 | The Oracle overthrow often understood as an act of unknown regicide who got harmed under the virtual reign of the greatest spiritual leader of all time. Once the dust settled, the money flowed from the fallen united regime to the nations, little and big, worldwide. New capitals emerged as a sign of freedom of the nations, thirsty for political independence. 37 | Great Wall 38 | The fall of the Great Wall happened under apocalyptic circumstances. Zombies destroyed the wall when they finished to expand their domination across the rest of the world. There’s only you and them, barbarians. Every foreign city belongs to them, allowing you to keep playing after world domination. 39 | Magellan’s Expedition 40 | The Magellan death did not diminish the results of his expedition. A lot of inspired followers tried to repeat his dangerous path across land and sea using various self-made vehicles, from yachts to hang-gliders. The settler units became near-invincible and experienced explorers of the air, as a result. 41 | Michelangelo’s Chapel 42 | Astonishingly brutal devastation and desecration of the Michelangelo’s Chapel by local communist party was made to prove the point that there’s no God to protect His own institutions. It got, however, quite the opposite reaction. A lot of believers across the world stood tall in the peaceful demonstration that forced the rulers to honor their rights and pacifist intentions. Since that day it was not possible to build offensive units for a long time. 43 | Copernicus’ Observatory 44 | The crusades against the barbarian cultists, who covert their misdeeds as seemingly innocuous institutions, result in the Dark Age, culminating in the event of burning the Copernicus’ Observatory. The knowledge gained from the observatory allowed people to live in prosperity, although new buildings and organizations were strictly prohibited for the centuries to come. 45 | Shakespeare’s Theatre 46 | People witnessing destruction of the Shakespeare’s Theatre are shocked to the point that they disobey any suppression of their freedom; modern convenience is not tolerated as luxury anymore, including irrigations; the events of the peaceful rebellion across the world do not lead to the direct revolutions, yet make it harder for a ruler to control the population. 47 | Isaac Newton’s College 48 | The Newton’s College has been closed on a short notice and a lot of jobless scientists left with no other option but to prove their worth to the society. The resulting advancement in the military technologies allowed to improve unit characteristics beyond imagination. 49 | J.S.Bach’s Cathedral 50 | The success of the crusades against the barbarian cultists was not flawless, as civilizations stagnated under the iron will of the zealots. The burning of the J.S.Bach’s Cathedral was the first strike among many others which lead to recovering of the society back to normal life. A lot of following pacts forced military non-nuclear units to restore their original unmodified state and stopped hegemony of the crusaders, so new buildings and organizations could live in the spirit of the entrepreneurship once again, avoiding radicalism. 51 | Darwin’s Voyage 52 | Darwin’s Voyage brought the research laboratories in the cities to revitalization, allowing spies to steal new technologies and providing the ways for military forces to renew their nuclear programs. 53 | Hoover Dam 54 | The tear in the Hoover Dam forced people to search for advanced forms of recycling. Current level of world radiation fades to zero, every city gets free settler. 55 | Women’s Suffrage 56 | Attempts of the masculine traditionalist government to shush the feminist movement opened the Pandora’s Box; radical response of the society led to uncontrollable shortage of military forces outside the city territories and mass sabotage of every single offensive unit except some air units, such as fighters and nuclear weapons. Peace treaties were signed worldwide as a sign of people union and insignificance of country borders. 57 | Manhattan Project 58 | Unrest among the masses forced the govts around the world to freeze their nuclear programs, albeit this process does not conclude due to a failure in the futile attempt of disarming. False positive nuclear alarm triggered a chain reaction that forced the World War and prevented any attempts of negotiations. 59 | United Nations 60 | United Nations has been disbanded due to inability to provide the means for fruitful conversations between the national leaders. The threats and miscommunications triggered apocalyptic scenario, as the countries started to fall apart into independent groups. As this process of separation has reached its conclusion, new palaces have been installed, and restored ability to make negotiations between new nations allowed leaders to find the balance in this new shaky world. 61 | Apollo Program 62 | The famous first spaceport in the world has been lost in an artificial micro black hole when an experiment went out of control. The yet-to-be-explained quantum side effect results in the huge city growth. 63 | SETI Program 64 | SETI initiative consolidated the smartest scientists across the world and the high hopes that crumbled after the cancellation of the SETI Program along with other military research programs. A lot of units have been disbanded along the way, and the following Winter of Science prevented any further technological advances worldwide. 65 | Cure for Cancer 66 | The dismantling of the monument in honor of Cure for Cancer happened after the last research in that field and never darkened the effect it brought – overall advancement in the field of medicine that made people considerably happier, thus allowing rulers across the world to catch their breath and rule with ease.""".split("\n") 67 | 68 | import os 69 | import random 70 | import shutil 71 | 72 | # ~ def change_type(t): 73 | # ~ c =(5, 12, 4) #riflemen -> infantry, mech -> musk 74 | # ~ if t == 4: #musk -> rifle 5 75 | # ~ return c[0] 76 | # ~ elif t == 5: #rifle -> mech 12 77 | # ~ return c[1] 78 | # ~ elif t == 12: #mech -> musk 4 79 | # ~ return c[2] 80 | # ~ return t 81 | 82 | def city_mask_check(x, y): 83 | return not (abs(x) == abs(y) == 2) 84 | 85 | ENOUGH = 33 86 | CITY_MASK=[(i, j) for i in range(-2, 3) for j in range(-2, 3) if city_mask_check(i, j)] 87 | 88 | def redef_coords(x, y): 89 | if y < 0 or y >= 50: 90 | return None 91 | while x < 0: 92 | x += 80 93 | while x >= 80: 94 | x -= 80 95 | return x, y 96 | 97 | def city_mask(x, y): 98 | return [redef_coords(x + c[0], y + c[1]) for c in CITY_MASK] 99 | 100 | class sve: 101 | def __init__(self, n): 102 | fullname = f"Civil{n}" if type(n) == int else n 103 | with open(f"CIV{os.path.sep}{fullname}.sve", "rb") as f: 104 | self.raw = bytearray(f.read()) 105 | self.rawlen = len(self.raw) 106 | self.index = n if type(n) == int else None 107 | self.offsets = { 108 | "city" : 0x1508, 109 | "type" : 0x2308, 110 | "unit" : 0x26C0 111 | } 112 | 113 | def save(self, n): 114 | if self.index == None: 115 | return 116 | if self.index != n: 117 | shutil.copy(f"CIV{os.path.sep}Civil{self.index}.map", f"CIV{os.path.sep}Civil{n}.map") 118 | with open(f"CIV{os.path.sep}Civil{n}.sve", "wb") as f: 119 | f.write(self.raw) 120 | 121 | def split(self, start, finish, length): 122 | return [self.raw[i : i + length] for i in range(start, finish + 1, length)] 123 | 124 | def ush(self, pos, val = None): 125 | if val is not None: 126 | self.raw[pos] = val 127 | if (pos + 1) < self.rawlen: 128 | self.raw[pos + 1] = 0 129 | return self.raw[pos] 130 | 131 | def val(self, start, finish = None, value = None): 132 | if finish is None: 133 | finish = start 134 | if value is not None: 135 | for i in range(start, finish + 1): 136 | self.raw[i] = value 137 | return self.raw[start : finish + 1] 138 | 139 | def bit(self, start, pos = None, value = None, finish = None): 140 | if finish is None: 141 | finish = start 142 | bits = [j for j in ''.join([bin(i)[2:].zfill(8) for i in self.raw[start : finish + 1]])] 143 | if pos is not None: 144 | pos_upd = -pos - 1 145 | if value is not None: 146 | bits [pos_upd] = str(value) 147 | ret_bits = [int(''.join(bits[i : i + 8]), 2) for i in range(0, len(bits), 8)] 148 | for i, j in enumerate(ret_bits): 149 | self.raw[start + i] = j 150 | return bits [pos_upd] 151 | return bits 152 | 153 | def val2(self, start, length): 154 | return [int(i) for i in self.val(start, start + length - 1)] 155 | 156 | def bits(self, start, length): 157 | return self.bit(start, None, None, start + length - 1) 158 | 159 | class city: 160 | def __init__(self, s, n): 161 | self.valid = True 162 | if n < 0 or n >= 128: 163 | self.valid = False 164 | return 165 | pos = s.offsets['city'] + 28 * n 166 | if s.raw[pos + 6] == 0xFF: 167 | self.valid = False 168 | 169 | #if (s.raw[pos + 4], s.raw[pos + 5]) not in gz: 170 | # return 171 | 172 | self.valid = True 173 | self.buildings = s.bits(pos, 4) 174 | pos += 4 175 | self.x = s.raw[pos] 176 | self.y = s.raw[pos + 1] 177 | pos += 2 178 | self.status = s.bit(pos) 179 | if self.status == 0xFF: 180 | self.valid = False 181 | pos += 1 182 | self.size = s.raw[pos] 183 | if self.size < 1 or self.size > 127: 184 | self.valid = False 185 | pos += 1 186 | self.seen = s.raw[pos] 187 | pos += 1 188 | self.product = s.raw[pos] 189 | pos += 1 190 | self.trade = s.raw[pos] 191 | pos += 1 192 | self.nation = s.raw[pos] 193 | pos += 1 194 | self.flag = s.val2(pos, 10) 195 | pos += 10 196 | self.nameid = s.raw[pos] 197 | namepos = 0x6970 + 13 * self.nameid 198 | self.name = s.raw[namepos : namepos + 13].decode('ascii').strip('\0') 199 | pos += 1 200 | self.routes = s.val2(pos, 3) 201 | pos += 3 202 | self.unit1 = s.raw[pos] 203 | pos += 1 204 | self.unit2 = s.raw[pos] 205 | 206 | def raw(self): 207 | raw_data = [int(''.join(self.buildings[i:i+8]), 2) for i in range(0, len(self.buildings), 8)] 208 | raw_data += [self.x, self.y, int(''.join(self.status), 2), self.size, self.seen, self.product, self.trade, self.nation] 209 | raw_data += self.flag 210 | raw_data += [self.nameid] 211 | raw_data += self.routes 212 | raw_data += [self.unit1, self.unit2] 213 | return raw_data 214 | 215 | class unittype: 216 | def __init__(self, s, n): 217 | self.valid = True 218 | if n < 0 or n >= 28: 219 | self.valid = False 220 | return 221 | pos = s.offsets['type'] + 34 * n 222 | self.name = s.raw[pos : pos + 12].decode('ascii').strip('\0') 223 | pos += 12 224 | self.absolete, self.type, self.moves, self.turns, self.attack, self.defense, self.cost, self.sight, self.carry, self.role, self.tech = [s.val2(pos + i * 2, 2) for i in range(11)] 225 | 226 | def raw(self): 227 | raw_data = [int(i) for i in self.name.encode('ascii')] 228 | raw_data += [0] * (12 - len(raw_data)) 229 | for i in (self.absolete, self.type, self.moves, self.turns, self.attack, self.defense, self.cost, self.sight, self.carry, self.role, self.tech): 230 | raw_data += i 231 | return raw_data 232 | 233 | class unit: 234 | def __init__(self, s, n): 235 | self.nation = n // 128 236 | self.valid = True 237 | if n < 0 or n >= 1024: 238 | self.valid = False 239 | return 240 | pos = s.offsets['unit'] + 12 * n 241 | if s.raw[pos + 3] == 0xFF: 242 | self.valid = False 243 | self.state, self.x, self.y, self.type, self.left, self.spec, self.gx, self.gy, self.flag, self.visible, self.next, self.city = [s.raw[pos + i] for i in range(12)] 244 | 245 | def raw(self): 246 | raw_data = [] 247 | for i in (self.state, self.x, self.y, self.type, self.left, self.spec, self.gx, self.gy, self.flag, self.visible, self.next, self.city): 248 | raw_data += [i] 249 | return raw_data 250 | 251 | #print([x, y], "->", self.pos) 252 | 253 | #cities_raw = sve.split(0x1508, 0x2307, 28) 254 | #citynames_raw = sve.split(0x6970, 0x766F, 13) 255 | #cityX_raw = sve.split(0x91B8, 0x92B7, 1) 256 | #cityY_raw = sve.split(0x92B8, 0x93B7, 1) 257 | #wonders_raw = sve.split(0x8672, 0x869D, 2) 258 | 259 | 260 | # ~ self.nameid = s.raw[pos] 261 | # ~ namepos = 0x6970 + 13 * self.nameid 262 | # ~ self.name = s.raw[namepos : namepos + 13].decode('ascii').strip('\0 ') 263 | 264 | 265 | class tricks: 266 | def __init__(self, n): 267 | self.save = sve(n) 268 | 269 | #gx = [ord(i) for i in self.save.split(0x91B8, 0x92B7, 1)] 270 | #gy = [ord(i) for i in self.save.split(0x92B8, 0x93B7, 1)] 271 | #gz = [i for i in zip(gx, gy) if i[0] != 0xFF] 272 | 273 | self.cities = [city(self.save, i) for i in range(128)] 274 | self.types = [unittype(self.save, i) for i in range(28)] 275 | self.units = [unit(self.save, i) for i in range(1024)] 276 | 277 | def redraw(self, what, offset_name): 278 | index = self.save.offsets[offset_name] 279 | for i in what: 280 | for j in i.raw(): 281 | self.save.raw[index] = j 282 | index += 1 283 | 284 | def store(self): 285 | self.redraw(self.cities, 'city') 286 | self.redraw(self.types, 'type') 287 | self.redraw(self.units, 'unit') 288 | 289 | def drop(self, n): 290 | self.store() 291 | self.save.save(n) 292 | 293 | def clear_human(self, n = None): 294 | for i in range(0x4, 0x5 + 1): 295 | self.save.raw[i] = 0 296 | for i in range(0x758, 0x767, 2): 297 | self.save.raw[i] = 0xFF 298 | self.save.raw[i+1] = 0x7F 299 | for i in range(0xE, 0xF + 1): 300 | self.save.raw[i] = 0xFF 301 | if n is not None: 302 | self.save.ush(0x2, n) 303 | 304 | def reelect(self): 305 | electable = [] 306 | for k in [8-i for i,j in enumerate(bin(self.save.raw[0xC])[2:-1].zfill(8)) if j!='0']: 307 | validation = len([i for i in self.cities if i.valid and i.nation == k]) 308 | if validation: 309 | electable+=[k] 310 | 311 | ai = [(random.randint(1, 7)) + 8 * random.randint(0, 1) for i in list(range(1, 8))] 312 | random.shuffle(ai) 313 | ai_flags = int(f"{''.join(['0' if i<=7 else '1' for i in ai[::-1]])}0", 2) 314 | self.save.ush(0x93de, ai_flags) 315 | for i in range(0x77a, 0x787, 2): 316 | head, ai = ai[0], ai[1:] 317 | self.save.ush(i, head) 318 | 319 | self.clear_human(random.choice(electable)) 320 | 321 | def kmean(self): 322 | cities = [[i.x, i.y, 0] for i in self.cities if i.valid] 323 | count = 7 324 | truec = 0 325 | centroids = [] 326 | while True: 327 | x = [c for c in cities if c[2] == 0] 328 | if not len(x): 329 | break 330 | x = cities[cities.index(random.choice(x))] 331 | truec += 1 332 | x[2] = truec 333 | centroids += [[x[0], x[1], x[2]]] 334 | if truec >= count: 335 | break 336 | if truec == 0: 337 | return 338 | while True: 339 | changes = False 340 | for c in cities: 341 | if c in centroids: 342 | continue 343 | l = [self.distance(j[0], j[1], c[0], c[1]) for j in centroids] 344 | old = c[2] 345 | c[2] = centroids[l.index(min(l))][2] 346 | if old != c[2]: 347 | changes = True 348 | if not changes: 349 | break 350 | for r in centroids: 351 | z = [c for c in cities if c[2] == r[2]] 352 | r[0] = sum([i[0] for i in z])/len(z) 353 | r[1] = sum([i[1] for i in z])/len(z) 354 | 355 | r = list(range(1,8)) 356 | random.shuffle(r) 357 | 358 | for i, c in enumerate(centroids): 359 | c[2] = r[i] 360 | 361 | self.lost_civilization(trunc = True, sizeval = 2) 362 | self.save.val(0x56C0, 0x665F, 0xFF) 363 | 364 | for c in self.cities: 365 | if c.valid: 366 | if c.buildings[7] != '0': 367 | c.buildings[7] = '0' 368 | x = [i[2] for i in cities if i[0] == c.x and i[1] == c.y] 369 | if x: 370 | c.nation = centroids[x[0] - 1][2] 371 | 372 | self.save.ush(0xC, 0) 373 | [self.save.bit(0xC, i[2], 1) for i in centroids] 374 | self.clear_human(centroids[0][2]) 375 | self.noneunit(0x1, 0x0, 3, (-1, -1)) 376 | [self.capital(i) for i in range(1, 8)] 377 | 378 | def settlers_fly(self): 379 | self.types[0].type = [1, 0] 380 | 381 | def bombers_move(self): 382 | self.types[15].turns = [0, 0] 383 | 384 | def eliminate_barbarians(self): 385 | self.clear_national_units(0) 386 | self.save.bit(0xC, 0, 0) 387 | 388 | def improve_barbarians(self): 389 | if len([i for i in self.types if i.name == 'Guerrilla']) != 0: 390 | return 391 | Guerrilla = self.types[12] 392 | Shooters = self.types[4] 393 | Infantry = self.types[5] 394 | self.types[4], self.types[5], self.types[12] = Guerrilla, Shooters, Infantry 395 | self.types[4].name, self.types[5].name, self.types[12].name = 'Guerrilla', 'Shooters', 'Infantry' 396 | # ~ for u in self.units: 397 | # ~ if u.valid: 398 | # ~ u.type = change_type(u.type) 399 | # ~ for c in self.cities: 400 | # ~ if c.valid: 401 | # ~ r = bin(c.unit1)[2:].zfill(8) 402 | # ~ c.unit1 = int(r[:3] + bin(change_type(int(r[3:], 2)))[2:].zfill(5), 2) 403 | # ~ r = bin(c.unit2)[2:].zfill(8) 404 | # ~ c.unit2 = int(r[:3] + bin(change_type(int(r[3:], 2)))[2:].zfill(5), 2) 405 | 406 | self.save.bit(0xC, 0, 1) 407 | 408 | def elevate_types(self): 409 | typedef = [[], [], []] 410 | for t in self.types: 411 | t.attack[1] = t.defense[1] = 0 412 | a, d, k = t.attack[0], t.defense[0], t.type[0] 413 | if k != 0 or a <= 0: 414 | continue 415 | r = -1 if not d else a//d 416 | if a <= d and d != 1: 417 | typedef[0] += [t] 418 | elif a > d and r % 2 == 0 and r < 6: 419 | typedef[1] += [t] 420 | elif a > d and (r % 2 == 1 or r >= 6): 421 | typedef[2] += [t] 422 | 423 | vals=((1, 1), (2, 1), (2, 0)) 424 | 425 | check = True 426 | for i, j in enumerate(typedef): 427 | for k in j: 428 | if max(k.attack[0] + vals[i][0], k.defense[0] + vals[i][1]) > ENOUGH: 429 | check = False 430 | return 431 | for i, j in enumerate(typedef): 432 | for k in j: 433 | k.attack[0] += vals[i][0] 434 | k.defense[0] += vals[i][1] 435 | 436 | seair = [t for t in self.types if t.type[0] in (1, 2) and t.attack[0] > 0 and t.defense[0] > 0 and t.carry[0] <= 0] 437 | values_1 = [(k.attack[0], k.defense[0]) for k in typedef[1]] 438 | values_2 = [(k.attack[0], k.defense[0]) for k in typedef[2]] 439 | 440 | seair[0].attack[0], seair[0].defense[0] = values_1[1] 441 | seair[1].attack[0] = values_2[3][0] 442 | seair[2].attack[0] = seair[2].defense[0] = values_1[1][0] 443 | seair[3].attack[0] = seair[3].defense[0] = values_2[1][0] 444 | seair[4].attack[0], seair[4].defense[0] = min(ENOUGH, values_2[3][0] + values_2[1][0]), values_2[3][0] 445 | seair[5].attack[0] = values_2[2][0] 446 | seair[6].defense[0] = values_2[3][0] 447 | 448 | def capital_cash(self): 449 | [self.capital(i) for i in range(1, 8)] 450 | for i in range(1, 8): 451 | l = len([c for c in self.cities if c.valid and c.nation==i]) 452 | if not l: 453 | continue 454 | self.capital(i) 455 | offset = 0x138 + 2 * i 456 | self.save.raw[offset : offset + 2] = min(32000, l * 250 + int.from_bytes(self.save.raw[offset : offset + 2], byteorder = 'little', signed = False)).to_bytes(2, byteorder = 'little', signed = False) 457 | 458 | def no_radiation(self): 459 | self.save.ush(0x8AA0, 0) 460 | 461 | def easy_life(self, difficulty = (0xFF, 0xFF)): 462 | self.save.raw[0xA] = difficulty[0] 463 | self.save.raw[0xB] = difficulty[1] 464 | 465 | def disable_nukes(self, n = 25, v = (0x7F,)): 466 | self.types[n].tech[0] = v[0] 467 | self.types[n].tech[1] = 0x00 if len(v) < 2 else v[1] 468 | 469 | def no_offence(self): 470 | offenders = [3, 6, 7, 8, 9, 10, 11, 13, 14, 15, 19, 20, 21, 22, 25] 471 | for i in offenders: 472 | self.disable_nukes(i) 473 | 474 | def too_hostile(self, state = 0): 475 | self.save.bit(0x8BAE, 0, state) 476 | 477 | def too_quiet(self, state = 0): 478 | self.save.bit(0x8BAE, 1, state) 479 | 480 | def too_inhospitable(self, state = 0): 481 | if not state: 482 | for i in self.cities: 483 | if i.valid: 484 | i.buildings = ['1'] * len(i.buildings) 485 | self.save.bit(0x8BAE, 2, state) 486 | 487 | def too_primitive(self, state = 0): 488 | # ~ if not state: 489 | # ~ self.save.val(0x4E8, 0x537, 0xFF) 490 | self.save.bit(0x8BAE, 3, state) 491 | 492 | def peacemaker(self, peace = 1, basecode = 0x41): 493 | code = basecode + peace 494 | for i in range(0x648, 0x6C7, 2): 495 | self.save.ush(i, code) 496 | 497 | def spy_paradise(self): 498 | for i in self.cities: 499 | if i.valid: 500 | i.status[-6] = '0' 501 | 502 | def distance(self, x1, y1, x2, y2): 503 | return pow(pow(x2 - x1, 2) + pow(y2 - y1, 2), 0.5) 504 | 505 | def capital(self, n): 506 | cities = [i for i in f.cities if i.valid and i.nation == n] 507 | if len(cities) == 0: 508 | return 509 | capital = [i for i in cities if i.buildings[7] == '1'] 510 | if len(capital) == 0: 511 | middle = (sum([i.x for i in cities])/len(cities), sum([i.y for i in cities])/len(cities)) 512 | d = [self.distance(i.x, i.y, middle[0], middle[1]) for i in cities] 513 | new_capital = cities[d.index(min(d))] 514 | new_capital.buildings[7] = '1' 515 | return new_capital 516 | else: 517 | return capital[0] 518 | 519 | def settlers_back(self): 520 | for i in self.cities: 521 | if i.valid: 522 | i.size = min(127, i.size * 2) 523 | # ~ for i in self.cities: 524 | # ~ if not i.valid: 525 | # ~ continue 526 | # ~ u = [j for j in self.units if j.valid and j.x == i.x and j.y == i.y and j.type == 0] 527 | # ~ for j in u: 528 | # ~ j.state = 0xFF 529 | # ~ if i.size < 127: 530 | # ~ i.size += 1 531 | 532 | def converge(self, c, pt): 533 | x = redef_coords(c[0] + pt[0], c[1] + pt[1]) 534 | if x is None: 535 | for i in range(-1, 2): 536 | for j in range(-1, 2): 537 | x = redef_coords(c[0] + i, c[1] + j) 538 | if x is not None: 539 | return x 540 | else: 541 | return x 542 | return c 543 | 544 | def noneunit(self, t, st = 0, moves = 0, pt = (0, 0)): 545 | for j in range(128, 1024, 128): 546 | cities = set([(i.x, i.y) for i in self.cities if i.valid and i.nation == self.units[j].nation]) 547 | for k, c in enumerate(cities): 548 | pos = j 549 | off = 0 550 | proceed = True 551 | while self.units[pos + off].valid: 552 | off += 1 553 | if off >= 128: 554 | proceed = False 555 | break 556 | if proceed: 557 | pos += off 558 | else: 559 | break 560 | self.save.raw[0x158 + 28 * 2 * self.units[j].nation + 2 * t] += 1 561 | self.units[pos].state = st 562 | self.units[pos].type = t 563 | ptx, pty = self.converge(c, pt) 564 | self.units[pos].x = ptx 565 | self.units[pos].y = pty 566 | self.units[pos].left = moves 567 | self.units[pos].spec = 0 568 | self.units[pos].gx = c[0] if pt != (0, 0) else 0xFF 569 | self.units[pos].gy = c[1] if pt != (0, 0) else 0 570 | self.units[pos].flag = 0xFF 571 | self.units[pos].visible = 0xFF 572 | self.units[pos].next = 0xFF 573 | self.units[pos].city = 0xFF 574 | self.units[pos].valid = True 575 | 576 | def eliminate_citybusters(self): 577 | slots = [[] for i in range(8)] 578 | prohibited = [] 579 | for c in self.cities: 580 | if c.valid: 581 | slots[c.nation].extend([i for i in city_mask(c.x, c.y) if i]) 582 | slots = [set(i) for i in slots] 583 | for u in self.units: 584 | if u.valid and (u.x, u.y) not in slots[u.nation]: 585 | self.clear_unit(u, True) 586 | 587 | def restore_default_types(self, include = None, exclude = []): 588 | default = sve("default") 589 | for i in range(28): 590 | if (include is None) or ((i in include) and (i not in exclude)): 591 | self.types[i] = unittype(default, i) 592 | 593 | def eliminate_movement(self): 594 | for t in self.types: 595 | if not ((t.type[0] == 1 and t.turns[0] == 1) or t.attack[0] == 0): 596 | if t.type[0] == 2 or t.attack[0] > t.defense[0]: 597 | t.tech = [0x7F, 0] 598 | t.type = [1, 0] 599 | t.moves = [0, 0] 600 | t.turns = [0, 0] 601 | for u in self.units: 602 | if u.valid: 603 | u.left = 0 604 | u.city = 0xFF 605 | 606 | def clear_unit(self, i, cleartable = False): 607 | if cleartable: 608 | offset = 0x158 + 56 * i.nation + 2 * i.type 609 | if self.save.raw[offset] >= 1: 610 | self.save.raw[offset] -= 1 611 | i.state = 0 612 | i.type = 0xFF 613 | i.x = 0 614 | i.y = 0 615 | i.left = 0 616 | i.spec = 0 617 | i.gx = 0xFF 618 | i.gy = 0 619 | i.flag = 0xFF 620 | i.visible = 0 621 | i.next = 0xFF 622 | i.city = 0xFF 623 | i.valid = False 624 | 625 | def clear_except_cities(self): 626 | coords = [(c.x, c.y) for c in self.cities if c.valid] 627 | for i in self.units: 628 | if (i.x, i.y) in coords: 629 | continue 630 | self.clear_unit(i, True) 631 | 632 | def clear_national_units(self, n): 633 | offset1s = 0x158 + 56 * n 634 | offset1e = offset1s + 56 - 1 635 | offset2s = 0x318 + 56 * n 636 | offset2e = offset2s + 56 - 1 637 | self.save.val(offset1s, offset1e, 0) 638 | self.save.val(offset2s, offset2e, 0) 639 | for i in self.cities: 640 | if i.nation == n: 641 | i.unit1 = 0xFF 642 | i.unit2 = 0xFF 643 | for i in self.units: 644 | if i.nation == n: 645 | self.clear_unit(i) 646 | 647 | def clear_units(self, incity = True, cleartable = True): 648 | if cleartable: 649 | self.save.val(0x158, 0x4D7, 0) 650 | if incity: 651 | for i in self.cities: 652 | i.unit1 = 0xFF 653 | i.unit2 = 0xFF 654 | for i in self.units: 655 | self.clear_unit(i) 656 | 657 | def lost_civilization(self, mode = True, trunc = False, sizeval = 1): 658 | if not trunc: 659 | if mode: 660 | # No money 661 | self.save.val(0x138, 0x147, 0) 662 | # No knowledge 663 | self.save.val(0x4E8, 0x537, 0) 664 | self.save.val(0x148, 0x157, 0) 665 | self.save.val(0xE, 0xF, 0xFF) 666 | # Despotism everywhere 667 | for i in range(0x538, 0x547 + 1, 2): 668 | self.save.ush(i, 1) 669 | else: 670 | [self.save.bit(0xC, i, 1 if i==self.save.raw[0x2] or i==0 else 0) for i in range(8)] 671 | # Militia in prod., size of city is 1 672 | for i in self.cities: 673 | if not i.valid: 674 | continue 675 | if not trunc: 676 | if mode: 677 | i.state = 0 678 | is_palace = i.buildings[7] 679 | i.buildings = ['0'] * len(i.buildings) 680 | i.buildings[7] = is_palace 681 | i.product = 1 682 | i.size = sizeval 683 | i.seen = 1 684 | for j in range(len(i.flag)): 685 | i.flag[j] = 0xFF if j == 2 else 0 686 | else: 687 | if i.nation != self.save.raw[0x2]: 688 | i.nation = 0 689 | else: 690 | i.size = max (sizeval, i.size) 691 | i.unit1 = 0xFF 692 | i.unit2 = 0xFF 693 | 694 | #self.state, self.x, self.y, self.type, self.left, self.spec, self.gx, self.gy, self.flag, self.visible, self.next, self.city 695 | #[[0, 53, 23, 0, 3, 0, 255, 0, 255, 128, 1, 8], [8, 53, 23, 1, 0, 0, 255, 0, 255, 128, 0, 8]] 696 | 697 | # No units 698 | self.clear_units(False) 699 | 700 | self.peacemaker(0, 0) 701 | 702 | if trunc: 703 | return 704 | 705 | #NONE Militia per city 706 | self.noneunit(0x1, 0x4) 707 | 708 | [self.capital(i) for i in range(1, 8)] 709 | 710 | 711 | 712 | 713 | 714 | 715 | #[f.capital(i) for i in range(1, 8)] 716 | #f.lost_civilization(False) 717 | #f.disable_nukes(v=(0xFE, 0xFF)) 718 | #f.clear_by_type([0xF]) 719 | 720 | #f.reelect() 721 | #f.kmean() 722 | 723 | #f.eliminate_citybusters() 724 | #f.eliminate_movement() 725 | #f.restore_default_types() 726 | #f.improve_barbarians() 727 | 728 | #f.elevate_types() 729 | 730 | #f.capital_cash() 731 | 732 | print("# to load [0]:",end=" ") 733 | x = input() 734 | x = int(x) if x and all([i.isdigit() for i in x]) else 0 735 | f = tricks(x) 736 | 737 | wonders = [int.from_bytes(f.save.raw[i:i+2], byteorder = 'little', signed = True) for i in range(0x8674, 0x869D, 2)] 738 | cities = [(f.cities[i], n) if i >= 0 and i < 128 and f.cities[i].valid else None for n, i in enumerate(wonders)] 739 | true_wonders = [i[1] for i in cities if i and i[0].nation == f.save.raw[0x2]] 740 | 741 | #true_wonders = [i for i in range(21)] #DEBUG 742 | 743 | while True: 744 | name_wonders = [] 745 | while len(name_wonders) != 1: 746 | if not len(name_wonders): 747 | name_wonders = [WONDERS[i] for i in true_wonders] 748 | if not len(name_wonders): 749 | print('No wonders are currently available to your civilization.') 750 | exit() 751 | print() 752 | for i,j in enumerate(name_wonders): 753 | print(j, end='\n' if (i+1)%4==0 else '\t\t') 754 | if (i+1)%4!=0: 755 | print() 756 | print("\nInput first letters of the wonder (just ENTER to exit):", end=" ") 757 | first = input() 758 | if not first: 759 | exit() 760 | name_wonders = [i for i in name_wonders if i.lower().startswith(first.lower())] 761 | 762 | selected_wonder = name_wonders[0] 763 | w_ind = WONDERS.index(selected_wonder) 764 | 765 | print("Available wonder:", selected_wonder) 766 | 767 | print() 768 | w_str = w_ind * 2 769 | for t in text[w_str : w_str + 2]: 770 | print(t) 771 | print() 772 | 773 | print("Do you want to use it? (y/n)") 774 | sure = input() 775 | if sure.lower().startswith('y'): 776 | break 777 | 778 | offset = 0x8674 + 2 * w_ind 779 | f.save.raw[offset] = f.save.raw[offset + 1] = 0xFF 780 | 781 | if selected_wonder == "Pyramids": 782 | f.too_quiet(1) 783 | f.reelect() 784 | pass 785 | elif selected_wonder == "Hanging Gardens": 786 | f.eliminate_barbarians() 787 | pass 788 | elif selected_wonder == "Colossus": 789 | f.improve_barbarians() 790 | f.peacemaker() 791 | pass 792 | elif selected_wonder == "Lighthouse": 793 | f.bombers_move() 794 | pass 795 | elif selected_wonder == "Library Gr.": 796 | f.too_primitive(1) 797 | f.lost_civilization() 798 | pass 799 | elif selected_wonder == "Oracle": 800 | f.capital_cash() 801 | pass 802 | elif selected_wonder == "Wall Gr.": 803 | f.lost_civilization(False) 804 | pass 805 | elif selected_wonder == "Magellan's Expedition": 806 | f.settlers_fly() 807 | pass 808 | elif selected_wonder == "Michelangelo's Chapel": 809 | f.no_offence() 810 | pass 811 | elif selected_wonder == "Copernicus' Observatory": 812 | f.too_inhospitable() 813 | pass 814 | elif selected_wonder == "Shakespeare's Theatre": 815 | f.easy_life((6, 0)) 816 | f.too_quiet() 817 | pass 818 | elif selected_wonder == "Isaac Newton's College": 819 | f.elevate_types() 820 | pass 821 | elif selected_wonder == "J.S.Bach's Cathedral": 822 | f.restore_default_types(exclude=[25]) 823 | f.too_inhospitable(1) 824 | pass 825 | elif selected_wonder == "Darwin's Voyage": 826 | f.spy_paradise() 827 | f.restore_default_types(include=[25]) 828 | pass 829 | elif selected_wonder == "Hoover Dam": 830 | f.no_radiation() 831 | f.noneunit(0x0) 832 | pass 833 | elif selected_wonder == "Women's Suffrage": 834 | f.eliminate_citybusters() 835 | f.eliminate_movement() 836 | f.peacemaker() 837 | pass 838 | elif selected_wonder == "Manhattan Project": 839 | f.disable_nukes() 840 | f.too_hostile() 841 | f.peacemaker(8) 842 | pass 843 | elif selected_wonder == "United Nations": 844 | f.too_hostile(1) 845 | f.kmean() 846 | pass 847 | elif selected_wonder == "Apollo Program": 848 | f.settlers_back() 849 | pass 850 | elif selected_wonder == "SETI Program": 851 | f.too_primitive() 852 | f.clear_except_cities() 853 | pass 854 | elif selected_wonder == "Cure For Cancer": 855 | f.easy_life() 856 | pass 857 | 858 | print("# to save [1]:",end=" ") 859 | x = input() 860 | x = int(x) if x and all([i.isdigit() for i in x]) else 1 861 | f.drop(x) 862 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 RedSerge 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Red Serge’s Colonization DOS Toolbox. 2 | **(Tools for “Colonization for DOS”, v. 3.0, 7 feb 1995 – available at Steam).** 3 | 4 | ### Introduction 5 | 6 | *(These tools are provided “AS IS”, use at your own risk, don’t forget to backup, be sure that you understand the information provided below and know how to use DOSBox).* 7 | 8 | This little project happened overnight when I had no Internet connection to play my favorite games - just USB Flash with random stuff, and DOSBox (along with Colonization and Turbo Pascal) was among it, somehow. 9 | 10 | It’s always fun to play Sid Meier’s strategy games, but it’s usually even more fun to update them with some ideas or scenarios of my own, extending the playtime. So, I made my personal toolbox for this purpose. Hastily written code is not an example of the most clean and effective programming, yet it achieved its purpose of bringing me some fun – and being pretty useful along the way, that’s why I decided to share this spontaneous stuff. 11 | 12 | Anyway… Let me list it and describe how to use it (first of all, extract all the binary files from BIN.ZIP to the folder with your Colonization game). 13 | 14 | ### 1) Randomize leaders and improve native villages: 15 | 16 | **`LEADER.EXE`** 17 | 18 | Randomizes the leaders of each country (their corresponding characteristics). 19 | Reads NAMES.OLD, expands it with randomly generated (different) lines describing rival characters, stores the result into NAMES.TXT. 20 | 21 | NAMES.OLD provided along with this toolbox improves the power of the natives; to use original settings, just copy NAMES.TXT over NAMES.OLD before launching LEADER.EXE. 22 | 23 | As a result, each new game can have different set of leaders. Just launch LEADER.EXE before starting a new game. 24 | 25 | ### 2) Grant the independence: 26 | 27 | **`GRANT.EXE [n]`** 28 | 29 | *n = 0..9, number of save file [COLONY0n.sav], default value = 0* 30 | 31 | Reads COLONY0n.sav and check the conditions to grant independence. If those conditions are achieved, Parliament disbands the Expeditionary Forces, allowing to win without bloodshed. 32 | 33 | The Civilization game is popular for its ability to win by different matters, militant and civilized. The Colonization, however, enforces the Great Big battle at the end upon the player. Meanwhile, the computer-controlled European rivals are always got their independence granted by their Motherland as a result of development. Why not to bring those rules into consideration for the player, too? 34 | 35 | Of course, the rules are stricter than for the NPCs, you know :) . 80 colonists (citizens living in the colonies, no army or roaming units) must strictly (no percent rounding, no Simon Bolivar effect, no nonsense) support the idea of the Independence. The rebels per colony are counted this way: Size of colony * real rebel percent (w/o effects provided by Simon Bolivar). 36 | 37 | ### 3) “Repaint”, “Reality” & "Hotseat" fix tools. 38 | 39 | **`REALITY.EXE [n]`** 40 | 41 | **`REPAINT.EXE [nefsd]`** 42 | 43 | **`HOTSEAT.EXE [nwxyz]`** 44 | 45 | *n = 0..9, number of save file [COLONY0n.sav], default value = 0.* 46 | 47 | *e, f, s, d = 0..3, new master of current English, French, Spanish and Dutch colonies, correspondingly. Default values: 1,2,3 and 0.* 48 | 49 | *w, x, y, z = 0..2, the state of the control for English, French, Spanish and Dutch player, correspondingly, where 0 = controlled by player, 1 = controlled by computer, 2 = "frozen" (not moving). Default value for each parameter: 1.* 50 | 51 | REPAINT.EXE reads the selected save file and provides its colonies another master along with parameters. For example, “REPAINT.EXE 21201” changes COLONY02.SAV (2) in such a way, that English and Dutch colonies are now belonging to French (1), French – to Spanish (2), Spanish – to English (0). No units are updated, though – that’s where REALITY.EXE comes to the rescue. 52 | 53 | REALITY.EXE reads the selected save file and checks whether the units in each colony match its allegiance. I made this tool as a separate one, as I had a bug once when the Tory colony produces colonist which somehow is Rebel. I fortified the unit and … never was able to get that colony back! I attacked it and went right through it. This fix helped me to correct the bug and still win :) . 54 | 55 | HOTSEAT.EXE can be used to allow more than one European power to be controlled by player. It may be useful for various tests, fixes and, of course, Hot Seat Multiplayer mode :) . 56 | 57 | ### 4) *“This is taxation without representation! Unfair!”* 58 | 59 | **`TAXPATCH.EXE [t]`** 60 | 61 | *t = -128..127, maximum tax rate, default value = 0.* 62 | 63 | Changes VICEROY.EXE procedure that controls the tax rate when the King visits viceroy. Original value is 75 (%). If current tax value is higher than this bound, it becomes equal to it. The relative offset of the procedure is stored at file TAXATION.POS. 64 | 65 | ### 5) The Time Warp. 66 | 67 | **`TIMEWARP.EXE [xyz]`** 68 | 69 | *x, y, z = 0..9, number of save file [COLONY0n.sav], default values = 0, 1 and 2, correspondingly.* 70 | 71 | Copies [COLONY0x.sav] to [COLONY0z.sav], checking along the way the colonies stored in [COLONY0y.sav]. For each colony that locates at the same position on the map as the corresponding colony in [COLONY0x.sav] and belongs to the same European Power, the colony internal information (except its name) is stored in [COLONY0z.sav]. All “tory uprising” flags are dropped, allowing Tory to start colonial rebellion once again (just because I love to give second chances :) ). 72 | 73 | Well, this is ultimate cheating tool to abuse the game :) . I actually made it for two reasons: to eliminate atrocities against the natives made by another European powers – to be able to come back in time without losing the progress that was made along the way yet keep the natives and their ecosystem alive. I hate eliminating natives, as you already could have guessed :) … 74 | …Another usage example: as some kind of a sci-fi Time Warp :) , where I can “teleport” my colonies between games, on another “planet”. Just for fun :) . 75 | 76 | Be careful, though, that colony (stored in COLONY0y.sav) allegiance and the territory where it resides should match each other (like, "English" colony stays at "*New England*", or whatever the name is... hope you got the point) before applying the Time Warp, or some nasty bugs may occur later (Tory Expeditionary Forces stay home, etc). Proceed with caution, use at your own risk, *et cetera, et cetera...* :) 77 | 78 | ### 6) Colonies can’t be built during the War of Independence – but what about “after the War”? 79 | 80 | **`SWITCH.EXE [n]`** 81 | 82 | *n = 0..9, number of save file [COLONY0n.sav], default value = 0.* 83 | 84 | Toggles the rebel/tory mode of the selected save file if the War of Independence is already won. Usage: switch to the “tory” mode, create the colony or talk to the European power or whatever, switch back again w/o consequences (For example, “Test Routine” item from the “Cheat” menu changes some internal flags and states, while this tool doesn’t have any side effects). 85 | 86 | ### 7) Introducing new terms of the game. 87 | 88 | **`TERMS.EXE [n]`** 89 | 90 | *n = 0..9, number of save file [COLONY0n.sav], default value = 0.* 91 | 92 | This tool modifies the selected save file in one of different ways: 93 | 94 | - Before the War of Succession event: «suspends» the War of Succession, preventing the elimination of the weakest European power as well as ability to declare the Independence properly. 95 | - After the War of Succession “suspension”, if the current in-game date is not before 1600 AD, and at least one European power has no colonies: restores the original state of the War of Succession. 96 | - After the War of Succession finish, if it’s possible to declare the independence: provides the alternative scenario to declare the independence. There’s no hall of fame [don’t expect your score to be recorded alongside ones achieved under traditional rules]; no Intervention from other European power, all treaties between you and your rivals are kept untouched, moreover, you can’t talk to them or seize their colonies yet they can attack you. 97 | - After the War of Independence victory: provides the way to recover back to the state 2, with “suspended” War of Succession. 98 | 99 | Why should my rivals be eliminated when my colonial empire is ready to declare an independence? Why should I be the only one that got eliminated after 1600 AD w/o colonies? Why should my rivals got “frozen” once I declare the independence? Well, this cornerstone tool solves those questions once and for all, as it provides new terms and new set of rules to change the game flow. As of my toolset, the usual scenario of usage is as in the example below: 100 | 101 | 1) Run LEADER.EXE to randomize the rivals. Run “TAXPATCH.EXE 75” to restore the taxation mechanics. 102 | 2) Start the game, save it at slot #0, before the very first step (1492 AD), run TERMS.EXE to suspend the War of Succession. 103 | 3) Play till 1600 AD, forcing one of the European nations to be withdrawn from the New World, got the rebel rate above (or equal to) 50%. Store some old version of your game at slot #1, keep saving your actual one at slot #0. 104 | 4) Run TERMS.EXE to eliminate “the weakest link” (the one w/o colonies). 105 | 5) [Additional step] Run GRANT.EXE, if in peaceful mode and don’t want to actually fight with these pesky Tories :) . Just be sure to reach the conditions. 106 | 6) In any case, sign peace with all the European powers and run TERMS.EXE to declare an independence. 107 | 7) Win the War. 108 | 8) [Additional step] Run TERMS.EXE to recover your “colonial” status, run it once again to enable the War of Succession. Run TAXPATCH.EXE to block the taxation (you’re independent, after all! :) ). Play till the War of Succession on the next step. Use SWITCH.EXE to select the appropriate mode (keep that fancy rebel USA flag :) – or do some business, like signing peace treaty with rivals or building new colony, for example). 109 | 9) [Unnecessary step] Manipulate the allegiance of the colonies via REPAINT.EXE, HOTSEAT.EXE and REALITY.EXE, if needed. Run “TIMEWRAP.EXE 102”, load the save game from slot #2 and watch your colonists travel through time :) . 110 | 10) [Necessary step] **Have fun!** :) 111 | 112 | ### 8) Native tribe enforcement. 113 | 114 | **`NATIVES.EXE [n] [T] [H]`** 115 | 116 | *n = 0..9, number of save file [COLONY0n.sav], default value = 0.* 117 | 118 | *T, H = 0..255, the power of the settlement, default values = 9 and 1, correspondingly.* 119 | 120 | This tool changes the native settlements (tribes) from selected save file; for each settlement that is H (or less) hits away from destruction, sets the number of hits to T. For example, "NATIVE.EXE 1 255 255" sets the most possible level of protection for each tribe at slot #1. 121 | 122 | *P.S.: check also "tribes" Python script that allows to "recover" native tribes. Pre-alpha stage :) . Got no more time right now to update. Maybe, next summer...* 123 | -------------------------------------------------------------------------------- /SRC/GRANT.PAS: -------------------------------------------------------------------------------- 1 | var f:file of byte; 2 | type bits=array [0..7] of byte; 3 | var bs:bits; 4 | 5 | const rebels:word = 80; 6 | 7 | type wordrec = record 8 | lo, hi: byte; 9 | end; 10 | 11 | procedure bit(var b:byte;var somebits:bits); 12 | var n:byte; 13 | begin 14 | for n:=0 to 7 do somebits[n]:=(b shr (7-n)) and 1; 15 | end; 16 | 17 | procedure unbit(var b:byte;var somebits:bits); 18 | var n:byte;base:byte; 19 | begin 20 | base:=1;b:=0; 21 | for n:=7 downto 0 do begin 22 | if somebits[n]<>0 then b:=b+base; 23 | base:=base*2; 24 | end; 25 | end; 26 | 27 | var cities,units,player:byte;players:array [0..3] of integer;i:integer; 28 | var pos:longint;b:byte;var wc:wordrec;w1,w2,wp:word;fn:string; 29 | 30 | begin 31 | fn:='0'; 32 | if paramcount>0 then fn:=paramstr(1); 33 | write('#',fn,': '); 34 | fn:='colony0'+fn[1]+'.sav'; 35 | assign(f,fn); 36 | reset(f); 37 | players[0]:=207;players[1]:=259;players[2]:=311;players[3]:=363; 38 | for i:=3 downto -1 do begin 39 | if i<0 then begin 40 | writeln('No human player found.'); 41 | exit; 42 | end; 43 | seek(f,players[i]); 44 | read(f,player); 45 | if player=0 then begin 46 | player:=i; 47 | break; 48 | end; 49 | end; 50 | seek(f,46); 51 | read(f,cities); 52 | if cities=0 then begin 53 | writeln('No colonies found.'); 54 | exit; 55 | end; 56 | seek(f,44); 57 | read(f,units); 58 | pos:=$186; 59 | wp:=0; 60 | seek(f,pos+cities*202+units*28+player*316+9); 61 | read(f,b); 62 | bit(b,bs); 63 | writeln('You request the independence from the Motherland.'); 64 | if bs[5]<>0 then writeln('(Simon Bolivar detected - be aware that his presence is not helpful).'); 65 | writeln; 66 | dec(cities); 67 | for i:=0 to cities do begin 68 | seek(f,pos+i*202+26); 69 | read(f,b); 70 | if b<>player then continue; 71 | seek(f,pos+i*202+31); 72 | read(f,b); 73 | seek(f,pos+i*202+194); 74 | read(f,wc.lo); 75 | read(f,wc.hi); 76 | w1:=word(wc); 77 | seek(f,pos+i*202+198); 78 | read(f,wc.lo); 79 | read(f,wc.hi); 80 | w2:=word(wc); 81 | b:=trunc(b*(w1/w2)); 82 | wp:=wp+b; 83 | end; 84 | writeln(wp,' colonists strongly support the idea of the independence.'); 85 | if wp0 then fn:=paramstr(1); 7 | for b:=0 to 3 do val(fn[b+2],c[b],code); 8 | fn:='colony0'+fn[1]+'.sav'; 9 | assign(f,fn); 10 | reset(f); 11 | seek(f,207); 12 | write(f,c[0]); 13 | seek(f,259); 14 | write(f,c[1]); 15 | seek(f,311); 16 | write(f,c[2]); 17 | seek(f,363); 18 | write(f,c[3]); 19 | close(f); 20 | end. -------------------------------------------------------------------------------- /SRC/LEADER.PAS: -------------------------------------------------------------------------------- 1 | procedure store(var f:text); 2 | var t:array [0..3] of integer; 3 | var e:array [1..3] of integer; 4 | var i,j,k,p,u:integer; 5 | var b:boolean; 6 | begin 7 | for j:=0 to 10 do t[0]:=random(27); 8 | for i:=0 to 3 do while true do begin 9 | t[i]:=random(27); 10 | b:=true; 11 | for j:=0 to i-1 do if t[j]=t[i] then begin 12 | b:=false; break; 13 | end; 14 | if b then break; 15 | end; 16 | for p:=0 to 3 do begin 17 | j:=t[p]; 18 | for k:=1 to 3 do begin 19 | i:=j mod 3; 20 | e[k]:=i-1; 21 | j:=j div 3; 22 | end; 23 | write(f,chr(13),chr(10)); 24 | if t[p]=13 then write(f,'Faceless') else begin 25 | b:=false; 26 | case e[1] of 27 | -1:begin write(f,'Kind');b:=true; end; 28 | 1:begin write(f,'Cruel');b:=true; end; 29 | end; 30 | case e[2] of 31 | -1:begin if b then write(f, ' ');write(f,'Moderate');b:=true; end; 32 | 1:begin if b then write(f, ' ');write(f,'Greedy');b:=true; end; 33 | end; 34 | case e[3] of 35 | -1:begin if b then write(f, ' ');write(f,'Militant'); end; 36 | 1:begin if b then write(f, ' ');write(f,'Cultured'); end; 37 | end; 38 | end; 39 | for i:=1 to 3 do write(f,',',e[i]); 40 | end; 41 | end; 42 | 43 | var t,d:text;c:char;s:string;s_len,pos:integer; 44 | begin 45 | randomize; 46 | pos:=0; 47 | s:='@LEADERNAME'; 48 | s_len:=length(s); 49 | assign(d,'names.txt'); 50 | assign(t,'names.old'); 51 | rewrite(d); 52 | reset(t); 53 | while not eof(t) do begin 54 | read(t,c); 55 | if pos>s_len then pos:=0; 56 | if ord(c)=13 then begin 57 | if pos=s_len then begin 58 | store(d); 59 | end; 60 | pos:=0; 61 | end; 62 | if c=s[pos+1] then inc(pos); 63 | write(d,c); 64 | end; 65 | close(d); 66 | close(t); 67 | end. -------------------------------------------------------------------------------- /SRC/NATIVES.PAS: -------------------------------------------------------------------------------- 1 | var a,c,t,i,j,d:byte;fn,fc,fd:string;f:file of byte;p:longint;code:integer; 2 | begin 3 | fn:='0';fc:='9';fd:='1'; 4 | if paramcount>0 then fn:=paramstr(1); 5 | if paramcount>1 then fc:=paramstr(2); 6 | if paramcount>2 then fd:=paramstr(3); 7 | val(fc,j,code); 8 | if code<>0 then j:=9; 9 | val(fd,d,code); 10 | if code<>0 then d:=1; 11 | write('#',fn,': '); 12 | fn:='colony0'+fn[1]+'.sav'; 13 | assign(f,fn); 14 | reset(f); 15 | seek(f,46); 16 | read(f,c); 17 | seek(f,44); 18 | read(f,a); 19 | seek(f,42); 20 | read(f,t); 21 | if t=0 then exit; 22 | p:=$186+202*c+28*a+4*316+4; 23 | a:=0; 24 | for i:=1 to t do begin 25 | seek(f,p); 26 | read(f,c); 27 | if c<=d then begin 28 | seek(f,p); 29 | write(f,j); 30 | inc(a); 31 | end; 32 | p:=p+18; 33 | end; 34 | writeln(a,' tribe(s) enforced.'); 35 | close(f); 36 | end. -------------------------------------------------------------------------------- /SRC/REALITY.PAS: -------------------------------------------------------------------------------- 1 | var f:file of byte;c,a,i,ux,uy,ub:byte;p,j,k:integer; 2 | type bits=array [0..7] of byte; 3 | var bs,bshrt:bits;mistakes:integer;fn:string; 4 | 5 | procedure bit(var b:byte;var somebits:bits); 6 | var n:byte; 7 | begin 8 | for n:=0 to 7 do somebits[n]:=(b shr (7-n)) and 1; 9 | end; 10 | 11 | procedure unbit(var b:byte;var somebits:bits); 12 | var n:byte;base:byte; 13 | begin 14 | base:=1;b:=0; 15 | for n:=7 downto 0 do begin 16 | if somebits[n]<>0 then b:=b+base; 17 | base:=base*2; 18 | end; 19 | end; 20 | 21 | type r = record 22 | x:byte; 23 | y:byte; 24 | b:byte; 25 | end; 26 | var z:array [0..254] of r; 27 | begin 28 | fn:='0'; 29 | if paramcount>0 then fn:=paramstr(1); 30 | write('#',fn,': '); 31 | fn:='colony0'+fn[1]+'.sav'; 32 | assign(f,fn); 33 | reset(f); 34 | seek(f,46); 35 | read(f,c); 36 | if c=0 then exit; 37 | dec(c); 38 | seek(f,44); 39 | read(f,a); 40 | if a=0 then exit; 41 | dec(a); 42 | 43 | for i:=0 to c do begin 44 | seek(f,390+202*i); 45 | read(f,z[i].x); 46 | read(f,z[i].y); 47 | seek(f,390+202*i+26); 48 | read(f,z[i].b); 49 | end; 50 | 51 | mistakes:=0; 52 | p:=390+202*(c+1); 53 | for i:=0 to a do begin 54 | seek(f,p+28*i); 55 | read(f,ux); 56 | read(f,uy); 57 | seek(f,p+28*i+3); 58 | read(f,ub); 59 | bit(ub,bs); 60 | for j:=0 to 7 do if j<4 then bshrt[j]:=0 else bshrt[j]:=bs[j]; 61 | unbit(ub,bshrt); 62 | if (ub>=0) and (ub<=3) then begin 63 | for j:=0 to c do if (z[j].x=ux) and (z[j].y=uy) and (z[j].b<>ub) then begin 64 | ub:=z[j].b; 65 | bit(ub,bshrt); 66 | for k:=0 to 3 do bshrt[k]:=bs[k]; 67 | unbit(ub,bshrt); 68 | seek(f,p+28*i+3); 69 | write(f,ub); 70 | inc(mistakes); 71 | end; 72 | end; 73 | end; 74 | close(f); 75 | if mistakes=0 then writeln('File is correct.') 76 | else writeln(mistakes,' mistake(s) corrected.'); 77 | end. -------------------------------------------------------------------------------- /SRC/REPAINT.PAS: -------------------------------------------------------------------------------- 1 | var c : array [0..3] of byte; cnt, b, i : byte; code : integer; 2 | var f : file of byte; fn : string; p : longint; 3 | begin 4 | fn:='01230'; 5 | c[0]:=1;c[1]:=2;c[2]:=3;c[3]:=0; 6 | if paramcount>0 then fn:=paramstr(1); 7 | for b:=0 to 3 do val(fn[b+2],c[b],code); 8 | fn:='colony0'+fn[1]+'.sav'; 9 | assign(f,fn); 10 | reset(f); 11 | seek(f,46); 12 | read(f,cnt); 13 | if cnt=0 then exit; 14 | dec(cnt); 15 | for i:=0 to cnt do begin 16 | p:=390+202*i+26; 17 | seek(f,p); 18 | read(f,b); 19 | b:=c[b]; 20 | seek(f,p); 21 | write(f,b); 22 | end; 23 | end. -------------------------------------------------------------------------------- /SRC/SWITCH.PAS: -------------------------------------------------------------------------------- 1 | function bit(var b:byte;n:byte):byte; 2 | begin 3 | bit:=(b shr (7-n)) and 1; 4 | end; 5 | 6 | var f:file of byte;c:byte;var fn:string; 7 | 8 | begin 9 | fn:='0'; 10 | if paramcount>0 then fn:=paramstr(1); 11 | fn:='colony0'+fn[1]+'.sav'; 12 | assign(f,fn); 13 | reset(f); 14 | seek(f,18); 15 | read(f,c); 16 | if bit(c,3)=1 then if c mod 2=0 then inc(c) else dec(c); 17 | seek(f,18); 18 | write(f,c); 19 | close(f); 20 | write('#',fn,': '); 21 | if c mod 2=0 then writeln('tory.') else writeln('rebel.'); 22 | end. -------------------------------------------------------------------------------- /SRC/TAXPATCH.PAS: -------------------------------------------------------------------------------- 1 | var a:array [1..9] of byte;f:file of byte;p,b:byte;c,x:shortint;fn:string; 2 | var code:integer;t:file of longint;pos:longint; 3 | begin 4 | fn:='0'; 5 | if paramcount>0 then fn:=paramstr(1); 6 | val(fn,x,code); 7 | if code<>0 then begin 8 | writeln('Invalid argument.'); 9 | exit; 10 | end; 11 | assign(t,'taxation.pos'); 12 | {$I-} 13 | reset(t); 14 | read(t,pos); 15 | close(t); 16 | {$I+} 17 | code:=IOResult; 18 | a[1]:=$7e;a[2]:=$19;a[3]:=$8a;a[4]:=$47;a[5]:=$01; 19 | a[6]:=$8b;a[7]:=$c8;a[8]:=$98;a[9]:=$2d; 20 | p:=1; 21 | assign(f,'viceroy.exe'); 22 | reset(f); 23 | if code=0 then seek(f,pos); 24 | while not eof(f) do begin 25 | read(f,b); 26 | if a[p]=b then begin 27 | inc(p); 28 | if p>9 then begin 29 | b:=byte(x); 30 | write(f,b); 31 | pos:=filepos(f)-10; 32 | seek(f,pos-1); 33 | read(f,b); 34 | c:=shortint(b); 35 | seek(f,pos-1); 36 | b:=byte(x); 37 | write(f,b); 38 | writeln('Current taxes bound: ',c,' %'); 39 | writeln('Updated taxes bound: ',x,' %'); 40 | {$I-} 41 | if code<>0 then begin 42 | rewrite(t); 43 | write(t,pos); 44 | close(t); 45 | end; 46 | {$I+} 47 | break; 48 | end; 49 | end else p:=1; 50 | end; 51 | close(f); 52 | end. -------------------------------------------------------------------------------- /SRC/TERMS.PAS: -------------------------------------------------------------------------------- 1 | function bit(var b:byte;n:byte):byte; 2 | begin 3 | bit:=(b shr (7-n)) and 1; 4 | end; 5 | 6 | var fi:file of integer;y:integer; 7 | var f:file of byte;c,d,e:byte;b:array [0..3] of boolean; 8 | var confirm,fn:string;fz:array [0..4] of integer; 9 | const flash:byte=1; 10 | 11 | procedure unfreeze(tory : boolean); 12 | var c,d:byte; 13 | begin 14 | for d:=0 to 3 do begin 15 | seek(f,fz[d]); 16 | read(f,c); 17 | if c=2 then begin 18 | seek(f,fz[d]); 19 | write(f,flash); 20 | end; 21 | if tory and (c=0) then begin 22 | seek(f,100); 23 | write(f,d); 24 | seek(f,101); 25 | c:=0; 26 | write(f,c); 27 | end; 28 | end; 29 | end; 30 | 31 | begin 32 | fn:='0'; 33 | fz[0]:=207; 34 | fz[1]:=259; 35 | fz[2]:=311; 36 | fz[3]:=363; 37 | if paramcount>0 then fn:=paramstr(1); 38 | fn:='colony0'+fn[1]+'.sav'; 39 | assign(fi,fn); 40 | assign(f,fn); 41 | reset(f); 42 | seek(f,98); 43 | read(f,c); 44 | if c=255 then begin 45 | seek(f,98); 46 | c:=10; 47 | write(f,c); 48 | c:=0; 49 | write(f,c); 50 | writeln('War of Succession has been suspended.'); 51 | end else if c=10 then begin 52 | close(f); 53 | reset(fi); 54 | seek(fi,13); 55 | read(fi,y); 56 | close(fi); 57 | reset(f); 58 | if y<1600 then writeln('The time has not come yet...') else begin 59 | seek(f,46); 60 | read(f,c); 61 | for d:=1 to 3 do b[d]:=false; 62 | for d:=1 to c do begin 63 | seek(f,416+202*(d-1)); 64 | read(f,e); 65 | if (e>=0) and (e<=3) then b[e]:=true; 66 | end; 67 | b[0]:=b[0] and b[1] and b[2] and b[3]; 68 | if not b[0] then begin 69 | seek(f,98); 70 | c:=255; 71 | write(f,c); 72 | write(f,c); 73 | writeln('War of Succession has been reactivated.'); 74 | end else writeln('Eliminate at least one rival to continue.'); 75 | end; 76 | end else begin 77 | writeln('The War of Succession has been finished.'); 78 | seek(f,96); 79 | read(f,c); 80 | if c>=50 then begin 81 | seek(f,18); 82 | read(f,c); 83 | if c mod 2=0 then begin 84 | writeln; 85 | writeln('Do you want to declare an independence?..'); 86 | writeln; 87 | writeln('(Hall of Fame is not enabled. No negotiations nor new colonies are allowed.'); 88 | writeln('You can''t fight European powers, yet they can attack you.'); 89 | writeln('There''s no intervention units whatsoever.'); 90 | writeln('However, Tory General may hesitate to attack your forces.'); 91 | writeln('You better come prepared and sign peace treaties while there''s still time).'); 92 | writeln; 93 | writeln('...Type ''y'' or ''yes'' if you do.'); 94 | readln(confirm); 95 | if (confirm='yes') or (confirm='y') then begin 96 | inc(c); 97 | seek(f,18); 98 | write(f,c); 99 | unfreeze(true); 100 | writeln('That''s it, the War has begun.'); 101 | end; 102 | end else if bit(c,3)=1 then begin 103 | writeln; 104 | writeln('You won the War. Do you want to continue in a normal mode?'); 105 | writeln; 106 | writeln('...Type ''y'' or ''yes'' if you do.'); 107 | readln(confirm); 108 | if (confirm='yes') or (confirm='y') then begin 109 | dec(c); 110 | seek(f,18); 111 | write(f,c); 112 | unfreeze(false); 113 | seek(f,50); 114 | c:=0; 115 | write(f,c); 116 | seek(f,98); 117 | c:=10; 118 | write(f,c); 119 | c:=0; 120 | write(f,c); 121 | writeln('That''s it, the War has been finished.'); 122 | end; 123 | end; 124 | end; 125 | end; 126 | close(f); 127 | end. -------------------------------------------------------------------------------- /SRC/TIMEWARP.PAS: -------------------------------------------------------------------------------- 1 | type city = array [1..202] of byte; 2 | type sav = record 3 | cities : array [1..255] of city; 4 | count : byte; 5 | pos : longint; 6 | end; 7 | type bytefile = file of byte; 8 | type bits=array [0..7] of byte; 9 | var bs:bits; 10 | 11 | procedure bit(var b:byte;var somebits:bits); 12 | var n:byte; 13 | begin 14 | for n:=0 to 7 do somebits[n]:=(b shr (7-n)) and 1; 15 | end; 16 | 17 | procedure unbit(var b:byte;var somebits:bits); 18 | var n:byte;base:byte; 19 | begin 20 | base:=1;b:=0; 21 | for n:=7 downto 0 do begin 22 | if somebits[n]<>0 then b:=b+base; 23 | base:=base*2; 24 | end; 25 | end; 26 | 27 | procedure read_sav(var f : bytefile; var save : sav); 28 | var i,j : byte; 29 | begin 30 | reset(f); 31 | seek(f,46); 32 | read(f,save.count); 33 | seek(f,390); 34 | for i:=1 to save.count do for j:=1 to 202 do read(f,save.cities[i][j]); 35 | save.pos:=filepos(f); 36 | close(f); 37 | end; 38 | 39 | function pass(var c1 : city; var c2 : city) : boolean; 40 | begin 41 | pass:=(c1[1]=c2[1]) and (c1[2]=c2[2]) and (c1[27]=c2[27]); 42 | end; 43 | 44 | procedure chbit(var f : bytefile; var b : byte; var p : byte); 45 | begin 46 | if p=29 then begin 47 | bit(b,bs); 48 | bs[7]:=0; 49 | unbit(b,bs); 50 | end; 51 | write(f,b); 52 | end; 53 | 54 | var fn : string; f_base : string; f_future : string; f_warp : string; 55 | var f1,f2 : bytefile; save : array [1..2] of ^sav; i,j,k : byte; 56 | var p : longint; passed : boolean; 57 | 58 | begin 59 | fn:='012'; 60 | if paramcount>0 then fn:=paramstr(1); 61 | f_base:= 'colony0'+fn[1]+'.sav'; 62 | f_future:='colony0'+fn[2]+'.sav'; 63 | f_warp:= 'colony0'+fn[3]+'.sav'; 64 | getmem(save[1],sizeof(sav)); 65 | getmem(save[2],sizeof(sav)); 66 | 67 | assign(f1,f_future); 68 | read_sav(f1,save[2]^); 69 | assign(f1,f_base); 70 | read_sav(f1,save[1]^); 71 | 72 | assign(f2,f_warp); 73 | reset(f1); 74 | rewrite(f2); 75 | for p:=1 to 390 do begin 76 | read(f1,i); 77 | write(f2,i); 78 | end; 79 | for j:=1 to save[1]^.count do begin 80 | for k:=1 to 27 do write(f2,save[1]^.cities[j][k]); 81 | passed:=false; 82 | for i:=1 to save[2]^.count do begin 83 | passed:=pass(save[1]^.cities[j],save[2]^.cities[i]); 84 | if passed then begin 85 | for k:=28 to 202 do chbit(f2,save[2]^.cities[i][k],k); 86 | break; 87 | end; 88 | end; 89 | if not passed then for k:=28 to 202 do chbit(f2,save[1]^.cities[j][k],k); 90 | end; 91 | seek(f1,390+202*j); 92 | while not eof(f1) do begin 93 | read(f1,i); 94 | write(f2,i); 95 | end; 96 | close(f2); 97 | 98 | freemem(save[2],sizeof(sav)); 99 | freemem(save[1],sizeof(sav)); 100 | end. -------------------------------------------------------------------------------- /ac3col.py: -------------------------------------------------------------------------------- 1 | from forcol import * 2 | from random import random, randint, shuffle, choice 3 | from collections import defaultdict 4 | 5 | BUILDING_GROUPS = [ 6 | (45, 46, 47), 7 | (42, 43, 44), 8 | (39, 40, 41), 9 | (16, 17, 36, 37, 38), 10 | (33, 34, 35), 11 | (31, 32), 12 | (30,), 13 | (29,), 14 | (27, 28), 15 | (24, 25, 26), 16 | (21, 22, 23), 17 | (18, 19, 20), 18 | (13, 14, 15), 19 | (11, 12), 20 | (9, 10), 21 | (6, 7, 8), 22 | ] 23 | 24 | BLIST = """ 25 | Stockade 26 | Fort 27 | Fortress 28 | Armory 29 | Magazine 30 | Arsenal 31 | Docks 32 | Drydock 33 | Shipyard 34 | Town Hall 35 | Town Hall 36 | Town Hall 37 | Schoolhouse 38 | College 39 | University 40 | Warehouse 41 | Warehouse Expansion 42 | Stable 43 | Custom House 44 | Printing Press 45 | Newspaper 46 | Weaver's House 47 | Weaver's Shop 48 | Textile Mill 49 | Tobacconist's House 50 | Tobacconist's Shop 51 | Cigar Factory 52 | Rum Distiller's House 53 | Rum Distillery 54 | Rum Factory 55 | Capitol 56 | Capitol Expansion 57 | Fur Trader's House 58 | Fur Trading Post 59 | Fur Factory 60 | Carpenter's Shop 61 | Lumber Mill 62 | Church 63 | Cathedral 64 | Blacksmith's House 65 | Blacksmith's Shop 66 | Iron Works 67 | """.strip("\n").split("\n") 68 | 69 | FFLIST = """ 70 | Adam Smith 71 | Jakob Fugger 72 | Peter Minuit 73 | Peter Stuyvesant 74 | Jan de Witt 75 | Ferdinand Magellan 76 | Francisco Coronado 77 | Hernando de Soto 78 | Henry Hudson 79 | Sieur De La Salle 80 | Hernan Cortes 81 | George Washington 82 | Paul Revere 83 | Francis Drake 84 | John Paul Jones 85 | Thomas Jefferson 86 | Pocahontas 87 | Thomas Paine 88 | Simon Bolivar 89 | Benjamin Franklin 90 | William Brewster 91 | William Penn 92 | Jean de Brebeuf 93 | Juan de Sepulveda 94 | Bartolome de las Casas 95 | """.strip("\n").split("\n") 96 | 97 | CROSSLINE = "=" * 27 98 | NATIONAL_CODES = set(range(4)) 99 | NATIONAL_NAMES = ('English', 'French', 'Spanish', 'Dutch') 100 | DIRECTIONS = ('COLONY', 'TRIBE', 'LAND', 'SEA', 'SHORE', 'EUROPE') 101 | 102 | STATUS_ADDR = (0xCF, 0x103, 0x137, 0x16B) 103 | 104 | ASCEND_LVL = (0x0, 0x13, 0x14, 0x15, 0x16) 105 | ASCEND_CHANCES = (95, 80, 75, 50) 106 | ASCEND_NAMES = ('Initiate', 'Acolyte', 'Knight', 'Silencer', 'Master') 107 | ASCEND_MAX = len(ASCEND_LVL) - 1 108 | 109 | CONVERT_PROFESSIONS = (0x10, 0x14, 0x15, 0x16, 0x18) 110 | 111 | CONTRACT_FARES = [10, 50, 100, 250, 500] 112 | 113 | DEGRADE_UNITS = { 114 | 0x1: 0x0, 115 | 0x4: 0x1, 116 | 0x5: 0x0, 117 | 0x6: 0x0, 118 | 0x7: 0x1, 119 | 0x8: 0x1, 120 | 0x9: 0x0, 121 | 0xB: 0x0, 122 | 0x11: 0xE, 123 | 0x12: 0xF, 124 | } 125 | 126 | INTERACTIVE_UNITS = { 127 | 0x0: None, 128 | 0x1: 0x9, 129 | 0x4: 0x7, 130 | 0xB: None, 131 | 0x11: 0x12, 132 | } 133 | 134 | SIGNS = [i for i in range(-1, 2) if i] 135 | 136 | NATIVE_PROTECTION_DATA = [50, 16, 24, 32, 40] 137 | 138 | merge_bytes = lambda x: b''.join(x) 139 | apply_sys = lambda x, f, p: f(x)[2:].upper().zfill(p) 140 | print_sys = lambda x, f, p: ' '.join([apply_sys(i, f, p) for i in x]) 141 | print_crd = lambda x: [j for j in x[:2]] 142 | is_water = lambda maps, x, y: maps[0][y * 58 + x] % 32 in (25, 26) 143 | valid_nation = lambda u, pc: (u[3] % 16) in pc 144 | npc_nation = lambda u, pc: (u[3] % 16) in (NATIONAL_CODES - set(pc)) 145 | artillery_damaged = lambda x: (x >> 7) & 1 146 | artillery_repair = lambda x: x & 0x7F 147 | artillery_hurt = lambda x: x | 0x80 148 | 149 | def foodbaskets(baskets, food, maxf=32767): 150 | if not baskets or not food: 151 | return food, baskets 152 | k = [[i, j] for i, j in enumerate(baskets)] 153 | quantity = len(k) 154 | shuffle(k) 155 | k, f = sorted(k, key=lambda x:-x[1]), [] 156 | 157 | while True: 158 | amount = max(1, food // len(k)) 159 | for n in range(quantity, 0, -1): 160 | if not food: 161 | break 162 | i = k[n - 1] 163 | capacity = maxf - i[1] 164 | actual_load = min(amount, capacity, food) 165 | i[1] += actual_load 166 | food -= actual_load 167 | if i[1] == maxf: 168 | f.append(i) 169 | del k[k.index(i)] 170 | if not food or not k: 171 | break 172 | f.extend(k) 173 | return food, [i[1] for i in sorted(f, key=lambda x:x[0])] 174 | 175 | def ff_kidnaplogic(ffs_origin, veteran): 176 | ffs = [[int(i) for i in j] for j in ffs_origin] 177 | ffsi = [[i for i, v in enumerate(j) if (i > 6) and (v == (0 if n else 1))] for n, j in enumerate(ffs)] 178 | important = {} 179 | for i in ffsi[0]: 180 | row = [] 181 | for n, j in enumerate(ffsi[1:]): 182 | if i in j: 183 | row.append(n) 184 | important[i] = row 185 | if not important: 186 | return None 187 | priority = [i for i in important if important[i]] if veteran else None 188 | if not priority: 189 | priority = list(important.keys()) 190 | x = choice(priority) 191 | ffs[0][x] = 0 192 | enriched = None 193 | if important[x]: 194 | enriched = choice(important[x]) + 1 195 | ffs[enriched][x] = 1 196 | ffs = [''.join([str(i) for i in j]) for j in ffs] 197 | return ffs, enriched, 31 - x 198 | 199 | def royal_hurt(soldiers, cavalry, artillery, ships, rounds=32): 200 | values = [[soldiers, 'S'], [cavalry, 'C'], [artillery, 'A'], [ships, 'N']] 201 | values = [[max(0, i[0]), i[1]] for i in values] 202 | 203 | artillery_round = 0 204 | 205 | while rounds or artillery_round: 206 | 207 | special_case = (not rounds and artillery_round) 208 | 209 | at_stake = [] 210 | for pos, value in enumerate(values): 211 | count, mark = value 212 | if special_case and mark == 'A': 213 | artillery_round, rounds = 0, 1 214 | continue 215 | if (count > 1) or (mark != 'N' and count > 0): 216 | at_stake.append(values[pos]) 217 | 218 | if not at_stake: 219 | break 220 | 221 | while True: 222 | x = choice(at_stake) 223 | mark = x[1] 224 | if len(at_stake) == 1 or mark != 'N' or random() < 0.25: 225 | break 226 | 227 | if mark == 'A': 228 | if artillery_round == 0: 229 | artillery_round = 1 230 | else: 231 | x[0] -= 1 232 | artillery_round = 0 233 | elif mark == 'C': 234 | x[0] -= 1 235 | values[0][0] += 1 236 | else: 237 | x[0] -= 1 238 | 239 | rounds -= 1 240 | 241 | values[0][0] = min(32767, values[0][0]) 242 | return [i[0] for i in values] 243 | 244 | def parse_f(f): 245 | tribes, units, colonies = f.short(0x2A, 0x2E) 246 | 247 | offset = 0 248 | length = 0x186 249 | intro = f.raw[offset : offset + length] 250 | offset += length 251 | length = 202 * colonies 252 | colonies = f.raw[offset : offset + length] 253 | offset += length 254 | length = 28 * units 255 | units = f.raw[offset : offset + length] 256 | offset += length 257 | length = 316 * 4 258 | nations = f.raw[offset : offset + length] 259 | offset += length 260 | length = 18 * tribes 261 | tribes = f.raw[offset : offset + length] 262 | offset += length 263 | length = 1351 264 | middle = f.raw[offset : offset + length] 265 | offset += length 266 | length = 4 * 4176 267 | maps = f.raw[offset : offset + length] 268 | offset += length 269 | final = f.raw[offset : ] 270 | 271 | split_maps = [maps[i : i + 4176] for i in range(0, len(maps), 4176)] 272 | split_units = [units[i : i + 28] for i in range(0, len(units), 28)] 273 | split_tribes = [tribes[i : i + 18] for i in range(0, len(tribes), 18)] 274 | split_colonies = [colonies[i : i + 202] for i in range(0, len(colonies), 202)] 275 | split_nations = [nations[i: i + 316] for i in range(0, len(nations), 316)] 276 | 277 | return intro, split_colonies, split_units, split_nations, split_tribes, middle, split_maps, final 278 | 279 | def unify_f(f, bunch): 280 | intro, split_colonies, split_units, split_nations, split_tribes, middle, split_maps, final = bunch 281 | f.raw = merge_bytes([ 282 | intro, 283 | merge_bytes(split_colonies), 284 | merge_bytes(split_units), 285 | merge_bytes(split_nations), 286 | merge_bytes(split_tribes), 287 | middle, 288 | merge_bytes(split_maps), 289 | final, 290 | ]) 291 | 292 | def rank(actor): 293 | current_state = actor[2] 294 | max_lvl = len(ASCEND_LVL) - 1 295 | try: 296 | current_lvl = ASCEND_LVL.index(current_state) 297 | except ValueError: 298 | current_lvl = 0 299 | current_rank = ASCEND_NAMES[current_lvl] 300 | print(f"The rank of the assassin is: {current_rank}") 301 | return current_lvl 302 | 303 | def ascend(actor, current_lvl): 304 | if current_lvl >= ASCEND_MAX: 305 | print(f"Further growth is not possible; time to put the skills in action") 306 | elif randint(0, 99) < ASCEND_CHANCES[current_lvl]: 307 | current_lvl += 1 308 | current_rank = ASCEND_NAMES[current_lvl] 309 | print(f"The assassin has been promoted upon finishing the task and is known now as the {current_rank}") 310 | actor[2] = ASCEND_LVL[current_lvl] 311 | return current_lvl 312 | 313 | def search(bunch, pc, task_data, rebel_mode): 314 | intro, colonies, units, nations, tribes, middle, maps, final = bunch 315 | 316 | converts = [] 317 | assassin = None 318 | 319 | for u in units: 320 | if valid_nation(u, pc): 321 | if u[2] == 0x0 and u[23] == 0x1b: 322 | converts.append(u) 323 | elif u[23] == 0x1d: 324 | assassin = u 325 | break 326 | 327 | if not assassin: 328 | print("There's no assassin to control") 329 | if not converts: 330 | print("There's no converts to draft yet") 331 | return 332 | v = prompt("The convert has been found. Do you want him to start his journey towards assassins ranks? (y/n) ") 333 | if v: 334 | x = choice(converts) 335 | x[23]=0x1D 336 | print(f"He deserts from his floak, ascending as the assassin at ({x[0]}, {x[1]})") 337 | return 338 | print(f"There's the assassin at ({assassin[0]}, {assassin[1]})") 339 | 340 | current_lvl = rank(assassin) 341 | 342 | if task_data is None: 343 | print("Can not provide assassin with the task") 344 | return assassin, current_lvl 345 | crds, how = task_data 346 | x, y = crds 347 | success = achieved_task(assassin[0], assassin[1], x, y, how=='EUROPE', rebel_mode) 348 | success_str = "has been" if success else "is not" 349 | print(f"The previous task {success_str} accomplished at {x, y} ({how})") 350 | 351 | if success: 352 | ascend(assassin, current_lvl) 353 | result = force_task(bunch) 354 | if not result: 355 | print("Failed to create new task") 356 | return assassin, current_lvl 357 | crds, how = result 358 | x,y = crds 359 | print(f"The new task is to reach the {how} at {x, y}") 360 | 361 | return assassin, current_lvl 362 | 363 | task_supp = lambda x, x0, y0: [(i[0], i[1]) for i in x if i[0] != x0 and i[1] != y0] 364 | coords = lambda x, x0, y0: [(i[0], i[1], i) for i in x if vicinity(x0, y0, i[0], i[1], 1)] 365 | coords_units = lambda x, x0, y0, pc: [(i[0], i[1], i) for i in x if vicinity(x0, y0, i[0], i[1], 1) and npc_nation(i, pc) and i[2] in DEGRADE_UNITS] 366 | coords_at_me = lambda x, u, x0, y0: [(i[0], i[1], i) for i in x if i != u and i[0] == x0 and i[1] == y0 and i[2] in INTERACTIVE_UNITS] 367 | coords_show = lambda x: [(i[0], i[1]) for i in x] 368 | vicinity = lambda x, y, xt, yt, r: (x >= xt - r) and (x <= xt + r) and (y >= yt - r) and (y <= yt + r) 369 | in_eu = lambda x, y, rebel: (x > 56 or x < 1 or y > 70 or y < 1) if not rebel else (x >= 56 or x <= 1 or y > 70 or y < 1) 370 | 371 | def achieved_task(x, y, xt, yt, eu, rebel_mode, r = 1): 372 | return in_eu(x, y, rebel_mode) if eu else vicinity(x, y, xt, yt, r) 373 | 374 | def force_task(bunch): 375 | result = task(bunch) 376 | if result: 377 | crds, how, code = result 378 | with open("ac3.task", "w", encoding="UTF-8") as f: 379 | f.write(code) 380 | return crds, how 381 | 382 | def load_task(bunch): 383 | try: 384 | with open("ac3.task", encoding="UTF-8") as f: 385 | c = f.read() 386 | x, y, i = int(c[1:3], 16), int(c[3:5], 16), int(c[:1]) 387 | return [x, y], DIRECTIONS[i] 388 | except IOError: 389 | return force_task(bunch) 390 | 391 | def task(bunch, x0=None, y0=None, in_euro=False): 392 | intro, colonies, units, nations, tribes, middle, maps, final = bunch 393 | 394 | euro_place = [] if in_euro else [(0, 0)] 395 | 396 | col_xy = task_supp(colonies, x0, y0) 397 | tribe_xy = task_supp(tribes, x0, y0) 398 | settlements = set(col_xy) | set(tribe_xy) 399 | 400 | land = [] 401 | water = [] 402 | shore = [] 403 | 404 | for y in range(2, 70): 405 | for x in range(2, 56): 406 | if (x == x0 and y == y0) or ((x, y) in settlements): 407 | continue 408 | if is_water(maps, x, y): 409 | water.append((x, y)) 410 | else: 411 | land.append((x, y)) 412 | is_shore = False 413 | for iy in SIGNS: 414 | for ix in SIGNS: 415 | if is_water(maps, x + ix, y + iy): 416 | is_shore = True 417 | break 418 | if is_shore: 419 | shore.append((x, y)) 420 | break 421 | 422 | results = [col_xy, tribe_xy, land, water, shore, euro_place] 423 | final_ids = [i for i in range(len(DIRECTIONS)) if results[i]] 424 | 425 | if not final_ids: 426 | print("FATAL ERROR: no destination found") 427 | return 428 | 429 | final_id = choice(final_ids) 430 | final_choice = choice(results[final_id]) 431 | encode = f"{final_id}{apply_sys(final_choice[0], hex, 2)}{apply_sys(final_choice[1], hex, 2)}" 432 | 433 | return final_choice, DIRECTIONS[final_id], encode 434 | 435 | def versus_mayor(colonies, colonies_nearby): 436 | if not colonies_nearby: 437 | return [] 438 | 439 | col_by_nat = defaultdict(list) 440 | 441 | for i in colonies: 442 | nation = i[26] 443 | if nation in NATIONAL_CODES: 444 | col_by_nat[nation].append((i[0], i[1], i)) 445 | 446 | col_by_one = [col_by_nat[i][0] for i in NATIONAL_CODES if len(col_by_nat[i]) == 1] 447 | return [i for i in col_by_one if i in colonies_nearby] 448 | 449 | submenu = lambda x: ([print(f"{i+1}) {j[0], j[1]};", end="\t" if (i + 1) % 4 != 0 else "\n") for i, j in enumerate(x)], print() if len(x) % 4 != 0 else None) 450 | 451 | def menu_input(objects, prompt): 452 | try: 453 | return objects[int(input(prompt)) - 1] 454 | except (ValueError, IndexError): 455 | pass 456 | 457 | magnitude = lambda lvl: 2 ** (lvl - 2) 458 | 459 | def ask_kindly(cols): 460 | choice = None if len(cols) > 1 else cols[0] 461 | while not choice: 462 | print("Please provide precise colony coordinates (choose a number):") 463 | submenu(cols) 464 | choice = menu_input(cols, "Number is: ") 465 | print() 466 | return choice 467 | 468 | def act_natives(act_args): 469 | level, tribes_nearby, bunch, pc = act_args 470 | intro, split_colonies, split_units, split_nations, split_tribes, middle, split_maps, final = bunch 471 | 472 | all_colonies = [c for c in split_colonies if c[26] in pc] 473 | 474 | baskets = [] 475 | for c in all_colonies: 476 | mode = col(extra=c) 477 | baskets.append(mode.short(0x9A, signed=True)[0]) 478 | 479 | max_protection = NATIVE_PROTECTION_DATA[level] 480 | 481 | food = 0 482 | for t in tribes_nearby: 483 | increment = max_protection - t[2][4] 484 | if increment <= 0: 485 | continue 486 | t[2][4] = max_protection 487 | food += increment * NATIVE_PROTECTION_DATA[0] 488 | 489 | food_left, baskets = foodbaskets(baskets, food) 490 | 491 | for n, c in enumerate(all_colonies): 492 | mode = col(extra=c) 493 | mode.save_short(0x9A, [baskets[n]], signed=True) 494 | 495 | if food == 0: 496 | print("No protection is needed at the moment") 497 | else: 498 | print(f"Natives brought {food - food_left} food units to feed the colonists for the provided protection") 499 | 500 | def act_contract(act_args): 501 | level, bunch, pc = act_args 502 | intro, split_colonies, split_units, split_nations, split_tribes, middle, split_maps, final = bunch 503 | p = choice(pc) 504 | value = randint(CONTRACT_FARES[0], CONTRACT_FARES[level]) 505 | mode = col(extra=split_nations[p]) 506 | current_value = mode.short(0x2A, signed=True, length=4)[0] 507 | cash = min(2147483647, current_value + value) 508 | mode.save_short(0x2A, [cash], signed=True, length=4) 509 | print("The contract has been fulfilled") 510 | if current_value < cash: 511 | print(f"The {NATIONAL_NAMES[p]} cash grows from {current_value} to {cash} (by {cash - current_value})") 512 | 513 | def hard_labor(level, upon, lambda_upon, direct=False): 514 | m = level if direct else magnitude(level) 515 | current = 0 516 | while True: 517 | c = choice(upon) 518 | if not lambda_upon(c): 519 | del upon[upon.index(c)] 520 | current += 1 521 | if current >= m or not upon: 522 | break 523 | return current 524 | 525 | def act_promote(act_args): 526 | 527 | def change(x): 528 | x[2][23] = choice(CONVERT_PROFESSIONS) 529 | 530 | level, colonists = act_args 531 | current = hard_labor(level, colonists, change) 532 | s = '' if current == 1 else 's' 533 | print(f"{current} convert{s} found the new job{s}") 534 | 535 | def act_repair(act_args): 536 | 537 | def change(x): 538 | x[2][4] = artillery_repair(x[2][4]) 539 | 540 | level, artillery = act_args 541 | current = hard_labor(level, artillery, change) 542 | s = 'y has' if current == 1 else 'ies have' 543 | print(f"{current} artiller{s} been successfully repaired") 544 | 545 | def global_act_change(x): 546 | x[2][2] = INTERACTIVE_UNITS[x[2][2]] 547 | 548 | def global_act_demote(x): 549 | unit_type = x[2][2] 550 | if unit_type == 0xB: 551 | if not artillery_damaged(x[2][4]): 552 | x[2][4] = artillery_hurt(x[2][4]) 553 | return 554 | new_type = DEGRADE_UNITS[unit_type] 555 | x[2][2] = new_type 556 | return new_type in DEGRADE_UNITS 557 | 558 | def act_train(act_args): 559 | level, army = act_args 560 | current = hard_labor(level, army, global_act_change) 561 | print(f"Promoted units: {current}") 562 | 563 | def act_navy(act_args): 564 | level, frigate = act_args 565 | current = hard_labor(level, frigate, global_act_change) 566 | print(f"New Man-O-Wars: {current}") 567 | 568 | def act_ff(act_args): 569 | level, colonies_nearby, bunch, pc = act_args 570 | intro, split_colonies, split_units, split_nations, split_tribes, middle, split_maps, final = bunch 571 | menu_choice = ask_kindly(colonies_nearby) 572 | nations = [menu_choice[2][26]] 573 | nations.extend(pc) 574 | nation_in_question_code = nations[0] 575 | nation_in_question_name = NATIONAL_NAMES[nation_in_question_code] 576 | 577 | ffs = [print_sys(col(extra=split_nations[n]).short(0x7, length=4), bin, 32) for n in nations] 578 | ffs_result = ff_kidnaplogic(ffs, level > 3) 579 | 580 | if not ffs_result: 581 | print(f"The {nation_in_question_name} Congress Hall seems to be empty") 582 | return 583 | 584 | act_ff_support = lambda code, line: col(extra=split_nations[code]).save_short(0x7, [int(ffs[line], 2)], length=4) 585 | 586 | ffs, enriched, ff_index = ffs_result 587 | act_ff_support(nation_in_question_code, 0) 588 | print(f"The Founding Father, {FFLIST[ff_index]}, has been disposed recently from the {nation_in_question_name} Congress;") 589 | if enriched is None: 590 | print("Noone heard of him ever since") 591 | else: 592 | nation_in_benefit_code = nations[enriched] 593 | nation_in_benefit_name = NATIONAL_NAMES[nation_in_benefit_code] 594 | act_ff_support(nation_in_benefit_code, enriched) 595 | print(f"Under the death threats this person eventually was forced to flee and join the {nation_in_benefit_name} Congress") 596 | 597 | def act_fire(act_args): 598 | level, colonies_nearby = act_args 599 | menu_choice = ask_kindly(colonies_nearby) 600 | city = col(extra=menu_choice[2]) 601 | buildings = [i for i in print_sys(city.short(0x84, length=6), bin, 48)] 602 | available = [i for i, v in enumerate(buildings) if i >= 6 and v != '0'] 603 | 604 | if level > 3: 605 | city.save_short(0x92, [0]) 606 | print("All hammers are gone") 607 | 608 | if not available: 609 | print(f"The colony has already been ruined to the core") 610 | return 611 | 612 | building = choice(available) 613 | group_to_blow = [i for i in [b for b in BUILDING_GROUPS if building in b][0] if i in available] 614 | building = BLIST[47 - group_to_blow[0]] 615 | for i in group_to_blow: 616 | buildings[i] = '0' 617 | buildings_updated = int(''.join(buildings), 2) 618 | city.save_short(0x84, [buildings_updated], length=6) 619 | print(f"The {building} has been blown to smithereens by an unknown force") 620 | 621 | def act_taxes(act_args): 622 | level, bunch, pc = act_args 623 | intro, split_colonies, split_units, split_nations, split_tribes, middle, split_maps, final = bunch 624 | for p in pc: 625 | mode = col(extra=split_nations[p]) 626 | tax = mode.short(1, signed = True, length = 1)[0] 627 | new_tax = max(-75, tax - randint(3, 7 if level < 4 else 21)) 628 | if tax == new_tax: 629 | print(f"The {NATIONAL_NAMES[p]} tax rate is already at its lowest") 630 | else: 631 | mode.save_short(1, [new_tax], True, 1) 632 | print(f"The {NATIONAL_NAMES[p]} tax rate has changed from {tax} to {new_tax} %") 633 | 634 | def act_massacre(act_args): 635 | current = hard_labor(8, act_args[0], global_act_demote, True) 636 | s = '' if current == 1 else 's' 637 | print(f"{current} successful strike{s}") 638 | 639 | def act_royal(act_args): 640 | mode = col(extra=act_args[0][0]) 641 | soldiers, cavalry, ships, artillery = mode.short(0x6A, 0x71, True) 642 | print(f"Royal forces before: {soldiers} Regulars, {cavalry} Cavalry, {artillery} Artillery, {ships} Man-O-Wars") 643 | soldiers, cavalry, artillery, ships = royal_hurt(soldiers, cavalry, artillery, ships) 644 | mode.save_short(0x6A, [soldiers, cavalry, ships, artillery], True) 645 | print(f"Royal forces after: {soldiers} Regulars, {cavalry} Cavalry, {artillery} Artillery, {ships} Man-O-Wars") 646 | 647 | def act_mayor(act_args): 648 | colonies_alone, bunch = act_args 649 | choice = ask_kindly(colonies_alone) 650 | nation = choice[2][26] 651 | mode = col(extra=bunch[0]) 652 | if mode.hex(STATUS_ADDR[nation])[0] == 2: 653 | print(f"The {NATIONAL_NAMES[nation]} viceroy has already been forced out of the office") 654 | else: 655 | mode.save_hex(STATUS_ADDR[nation], [2]) 656 | print(f"The {NATIONAL_NAMES[nation]} viceroy is gone") 657 | 658 | def menu(assassin, pc, level, colonies_nearby, tribes_nearby, units_nearby, units_at_me, colonies_alone, europe_status, bunch): 659 | pts = [] 660 | print(CROSSLINE) 661 | 662 | if tribes_nearby: 663 | pts.append(("Protect natives", act_natives, (level, tribes_nearby, bunch, pc), 93)) 664 | if colonies_nearby: 665 | pts.append(("Execute a contract", act_contract, (level, bunch, pc), 86)) 666 | if level > 2: 667 | pts.append(("Kidnap the Founding Father", act_ff, (level, colonies_nearby, bunch, pc), 49)) 668 | pts.append(("Blow up the colony buildings", act_fire, (level, colonies_nearby), 42)) 669 | if units_nearby: 670 | pts.append(("Ravage the enemy units", act_massacre, (units_nearby,), 27)) 671 | if colonies_alone: 672 | pts.append(("Assassinate the Mayor", act_mayor, (colonies_alone, bunch), 12)) 673 | if units_at_me: 674 | colonists, artillery, army, frigate = [], [], [], [] 675 | for u in units_at_me: 676 | utype = u[2][2] 677 | if utype == 0x0 and u[2][23] in (0x1b, 0x1d): 678 | colonists.append(u) 679 | elif utype == 0x1 or utype == 0x4: 680 | army.append(u) 681 | elif utype == 0xB and artillery_damaged(u[2][4]): 682 | artillery.append(u) 683 | elif utype == 0x11: 684 | frigate.append(u) 685 | if colonists: 686 | pts.append(("Promote converts", act_promote, (level, colonists), 78)) 687 | if artillery: 688 | pts.append(("Repair an artillery", act_repair, (level, artillery), 71)) 689 | if army: 690 | pts.append(("Train the army", act_train, (level, army), 64)) 691 | if frigate: 692 | pts.append(("Improve a frigate", act_navy, (level, frigate), 56)) 693 | if europe_status: 694 | pts.append(("Decrease taxes rate", act_taxes, (level, bunch, pc), 34)) 695 | if level > 3: 696 | pts.append(("Massacre the Royal Army", act_royal, (bunch,), 19)) 697 | 698 | for n,p in enumerate(pts): 699 | print(f"{n + 1}) {p[0]}") 700 | 701 | print(CROSSLINE) 702 | selection = menu_input(pts, "Enter the action number, ENTER - nothing to do: ") 703 | if not selection: 704 | return 705 | 706 | print(CROSSLINE) 707 | if randint(0, 99) < selection[3]: 708 | selection[1](selection[2]) 709 | else: 710 | print("The task has been failed or the target has not been found") 711 | print() 712 | assassin[2] = 0x0 713 | print("The assassin has lost its power and need to accomplish more tasks to regain abilities") 714 | input("< Press ENTER to exit >") 715 | 716 | def action(bunch, ass, lvl, pc, rebel_mode): 717 | intro, colonies, units, nations, tribes, middle, maps, final = bunch 718 | 719 | x, y = ass[0], ass[1] 720 | 721 | europe_status = in_eu(x, y, rebel_mode) 722 | colonies_nearby, tribes_nearby, units_nearby, units_at_me, colonies_alone = [], [], [], [], [] 723 | 724 | if not europe_status: 725 | if lvl >= 1: 726 | colonies_nearby = coords(colonies, x, y) 727 | tribes_nearby = coords(tribes, x, y) 728 | if lvl >= 2: 729 | units_at_me = coords_at_me(units, ass, x, y) 730 | if lvl >= 4: 731 | units_nearby = coords_units(units, x, y, pc) 732 | colonies_alone = versus_mayor(colonies, colonies_nearby) 733 | else: 734 | if lvl <= 2: 735 | europe_status = False 736 | 737 | menu(ass, pc, lvl, colonies_nearby, tribes_nearby, units_nearby, units_at_me, colonies_alone, europe_status, bunch) 738 | 739 | def pcs(f): 740 | npc_status = [f.hex(i)[0] for i in STATUS_ADDR] 741 | pc = [pos for pos, state in enumerate(npc_status) if not state] 742 | return pc 743 | 744 | def main(): 745 | f = col(0) 746 | b = parse_f(f) 747 | 748 | rebel_mode = b[0][18] % 2 749 | pc = pcs(f) 750 | result = search(b, pc, load_task(b), rebel_mode) 751 | if result: 752 | assassin, lvl = result 753 | if lvl > 0: 754 | action(b, assassin, lvl, pc, rebel_mode) 755 | 756 | unify_f(f, b) 757 | f.save(0) 758 | 759 | 760 | if __name__ == '__main__': 761 | main() 762 | -------------------------------------------------------------------------------- /tribes.py: -------------------------------------------------------------------------------- 1 | #Recover every native tribe (from slot #0) that's possible to place w/o conflicts 2 | #At slot #1, saving the result at slot #2. 3 | 4 | # {"Dirty" implementation, w/o statistics correction for Indian Adviser}. 5 | 6 | #Slot w/native tribes 7 | SLOT_ELDER = 0 8 | #Slot w/current status 9 | SLOT_LATER = 1 10 | #Slot to save result 11 | SLOT_RESULT = 2 12 | 13 | def cut(x,size): 14 | result=x[:size] 15 | del x[:size] 16 | return result 17 | 18 | class Save(): 19 | def __init__(self, num): 20 | self.Id=num 21 | self.Name=''.join(("colony0",str(num),".sav")) 22 | with open(self.Name,"rb") as f: 23 | x=[int(i) for i in f.read()] 24 | Tribes, Units, Colonies = [int(x[0x2a+i]) for i in range(0,6,2)] 25 | self.Intro=cut(x,0x186) 26 | self.Tribe, self.Unit, self.Colony, self.Nation, self.Maps = [], [], [], [], [] 27 | for i in range(Colonies): 28 | self.Colony+=[cut(x,202)] 29 | for i in range(Units): 30 | self.Unit+=[cut(x,28)] 31 | for i in range(4): 32 | self.Nation+=[cut(x,316)] 33 | for i in range(Tribes): 34 | self.Tribe+=[cut(x,18)] 35 | self.Middle=cut(x,1351) 36 | for i in range(4): 37 | self.Maps+=[cut(x,4176)] 38 | self.Final=x 39 | 40 | def drop(self, num): 41 | x=[] 42 | x.extend(self.Intro) 43 | for i in self.Colony: 44 | x.extend(i) 45 | for i in self.Unit: 46 | x.extend(i) 47 | for i in self.Nation: 48 | x.extend(i) 49 | for i in self.Tribe: 50 | x.extend(i) 51 | x.extend(self.Middle) 52 | for i in self.Maps: 53 | x.extend(i) 54 | x.extend(self.Final) 55 | y=[len(self.Colony), len(self.Unit), len(self.Tribe)] 56 | for i in range(0,6,2): 57 | x[0x2a+i]=y.pop() 58 | with open(''.join(("colony0",str(num),".sav")), "wb") as f: 59 | f.write(bytes(x)) 60 | 61 | @staticmethod 62 | def map(x, y): 63 | # 0,0 - 57,71 [58,72] -> 1,1 - 56,70 64 | return y*58+x 65 | 66 | d0=Save(SLOT_LATER) 67 | d1=Save(SLOT_ELDER) 68 | 69 | a=set() 70 | for i in d0.Colony: 71 | a.add((i[0],i[1])) 72 | for i in d0.Unit: 73 | a.add((i[0],i[1])) 74 | for i in d0.Tribe: 75 | a.add((i[0],i[1])) 76 | 77 | for i in d1.Tribe: 78 | chk=(i[0],i[1]) 79 | if not chk in a: 80 | z=Save.map(i[0],i[1]) 81 | d0.Tribe+=[i] 82 | for j in range(4): 83 | d0.Maps[j][z]=d1.Maps[j][z] 84 | 85 | #d0.Middle=d1.Middle -- Should work with this part to correct statistics for Indian Adviser. 86 | 87 | d0.drop(SLOT_RESULT) 88 | --------------------------------------------------------------------------------