diff --git a/.agents/skills/check-hooks/SKILL.md b/.agents/skills/check-hooks/SKILL.md new file mode 100644 index 0000000000..e79a89e246 --- /dev/null +++ b/.agents/skills/check-hooks/SKILL.md @@ -0,0 +1,719 @@ +--- +name: 检查钩子 Check Hooks +description: "Validates Syringe hooks (DEFINE_HOOK / DEFINE_HOOK_AGAIN) on the current branch during code review. Trigger whenever new or modified hooks need validation." +--- + +#### Helper Scripts + +YOU MUST use these scripts. DO NOT reimplement parsing logic. + +| Script | Purpose | +|--------|---------| +| `discover_hooks.py` | Discovers new/modified DEFINE_HOOK / DEFINE_HOOK_AGAIN from git. Two modes: auto-detect (no args) or `--commit `. Supports fuzzy commit name resolution (searches last 30 commits). Outputs JSON with `hooks` array, each having `address`, `size`, `name`, `file`, `returns`. Use `--json-only` for piping. | +| `check_hook_conflicts.py` | Reads a JSON array of new hooks from stdin (or a file argument) and checks them against the existing hook reference for Problem 0 (size < 5 — requires NOP padding verification) and Problem 1 (conflicts). **References HookAnalysis.txt first; falls back to `ares_3.0p1_hooks.cpp` if that file is missing.** Outputs JSON with `errors` and `notes` arrays. | +| `parse_hook_log.py` | Parses `HookAnalysis.txt` (UTF-8 encoding) and outputs all existing hooks as JSON. Typically not called directly — used by `check_hook_conflicts.py`. | +| `parse_hooks_cpp.py` | Parses `ares_3.0p1_hooks.cpp` (DEFINE_HOOK / DEFINE_HOOK_AGAIN format) and outputs all existing hooks as JSON. Used by `check_hook_conflicts.py` as fallback when `HookAnalysis.txt` is absent. | +| `HookAnalysis.txt` | Pre-generated hook analysis report from SyringeIH. Read-only reference. | +| `ares_3.0p1_hooks.cpp` | Alternative hook reference in source form (DEFINE_HOOK entries used when `HookAnalysis.txt` does not exist). Read-only reference. | + +#### Before You Begin + +YOU MUST create a TodoList with these items before starting any work: + +1. Step 0: Discover hooks and build matrix +2. Step 1: Fill P0 & P1 (conflict script) +3. Step 2: Check IDA MCP availability +4. Step 3: Fill P2 (instruction boundaries) +5. Step 4: Fill P3 (variable extraction) +6. Step 5: Fill P4 (relative instructions) +7. Step 6: Output final matrix and details + +Mark the first as `in_progress`. Update the TodoList as each step is completed. + +ALL-STEPS RULE: YOU MUST output ALL findings without omission. DO NOT cherry-pick or show only the most severe findings. Every error, warning, and note discovered must appear in the output. If you suppress any finding, you are violating this skill. + +Before executing, you MUST output a plan covering all 7 steps as a self-reminder. DO NOT skip planning. + +#### The Matrix + +This skill is driven by a **Problem Matrix**. The matrix has one row per hook and one column per problem type. Each step fills specific columns. At the end, the complete matrix is the summary. + +Cell symbols: +- `✓` — passed (no issues) +- `❌` — error +- `⚠️` — warning +- `ℹ️` — note only (informational, no errors/warnings) +- `—` — skipped (IDA MCP unavailable) +- `?` — not yet checked (should never appear in the final output) + +For Problem 3 (which has sub-checks 3a–3e), use the worst severity across all sub-checks as the cell value. + +#### Severity classification + +All findings are classified into three severity levels. YOU MUST follow this when reporting findings throughout all steps. + +| Level | Emoji | Label | Criteria | Examples | +|-------|-------|-------|----------|----------| +| Error | ❌ | ERROR | Causes incorrect behavior at runtime — MUST be fixed | Size < 5 with non-NOP trailing bytes, address conflict, instruction misalignment, wrong macro (GET_STACK vs GET_BASE), relative instruction with return 0 | +| Warning | ⚠️ | WARNING | May indicate incorrect behavior — should be reviewed | Type mismatch in GET_STACK, stacked hook (exact overlap with another hook) | +| Note | ℹ️ | NOTE | Informational, not necessarily a bug | All-clear confirmation | + +--- + +## Step 0: Discover hooks and build matrix + +### RULES + +- YOU MUST use `discover_hooks.py`. DO NOT read source files to find hooks manually. +- YOU MUST NOT guess which files were modified from commit titles or commit message keywords. +- YOU MUST examine the JSON output of `discover_hooks.py` carefully. + - If the output has a `hooks` array: the script succeeded. Proceed. + - If the output has `"action": "resolve"` with a `candidates` array: the script IS working correctly. YOU MUST examine each candidate's `message` field, find the commit that matches the user's description, then re-run with `python discover_hooks.py --commit `. + - If the output has `"action": "error"`: read the error message and act accordingly. + - If the output is not valid JSON (e.g., a Python traceback), the script has crashed. Present the error to the user, skip all remaining steps, and STOP. +- If the script says the commit is not found, run `git fetch upstream` first, then retry. If `upstream` is not configured, try `git fetch origin`. Only ask the user for the correct SHA after both fail. +- If no hooks are found, YOU MUST present the warning to the user and STOP. DO NOT proceed to Step 1. +- If `returns` is `"?"`, YOU MUST read the hook function body from the source file to determine the actual return behavior before proceeding to Step 1. + +### Two modes + +**Mode A — Specify a commit** (user provides a SHA or commit name): +``` +python discover_hooks.py --commit +``` + +**Mode B — Auto-detect** (user does not specify a commit): +``` +python discover_hooks.py +``` + +### OUTPUT REQUIREMENT + +After completing Step 0, YOU MUST build the empty matrix and output it: + +``` +[Step 0 Complete] +- Mode used: +- Hooks discovered: + +Matrix (empty, awaiting checks): +| Hook | Address | P0 | P1 | P2 | P3 | P4 | +|------|---------|----|----|----|----|----| +| A | 0x... | ? | ? | ? | ? | ? | +| B | 0x... | ? | ? | ? | ? | ? | +``` + +Then mark the TodoList item as complete and move to Step 1. From this point on, the matrix is your progress tracker — refer to it before and after each step. + +--- + +## Step 1: Fill P0 & P1 — Size and conflict checks (scripted) + +### RULES + +- YOU MUST pipe the discovered hooks JSON directly to `check_hook_conflicts.py`: + ``` + python discover_hooks.py --json-only | python check_hook_conflicts.py + ``` + Or with a commit: + ``` + python discover_hooks.py --commit --json-only | python check_hook_conflicts.py + ``` +- If `returns` was `"?"` in Step 0 and you determined the actual value: fix it in the JSON before piping, or save the corrected JSON to a temp file and pass it as a file argument. +- YOU MUST examine the JSON output of `check_hook_conflicts.py`. The `errors` array contains issues that need fixing. The `notes` array contains informational items. + - If the output is not valid JSON (e.g., a Python traceback), the script has crashed. Present the error to the user, skip all remaining steps, and STOP. +- DO NOT silently ignore any error. For each error, present it to the user using the exact format below. +- For Problem 0 (size < 5) findings: NOP padding verification is deferred to Step 2 (after IDA MCP check). Present the finding as either a tentative error or a note based on whether verification has been done. Update the matrix P0 cell in Step 1 as `?` with a note that NOP verification is pending. + +### Error display formats + +For each error in the `errors` array, YOU MUST output the corresponding formatted message: + +**Problem 0** (type: `"size"`): + +The JMP instruction always occupies 5 bytes, but the stolen bytes (`size`) can be less than 5 if the remaining bytes `[addr + size, addr + 5)` are NOP padding (0x90) from function epilog alignment. You MUST verify this before classifying a size < 5 finding as an error. + +To verify NOP padding, use IDA MCP (if available) to disassemble `[addr, addr + 5)` and check whether `[addr + size, addr + 5)` are all NOPs. If IDA MCP is unavailable, mark the finding as a warning (see Step 2 for details). + +**If trailing bytes are NOT all NOPs** — actual error: +> ❌ **Problem 0: Insufficient stolen bytes** +> Hook `HookName` at `0x` has size `0x` (< 5) and the trailing bytes at `[0x, 0x)` are not NOP padding. The JMP always overwrites 5 bytes. Increase the size to cover the full instruction(s) at this address. + +**If trailing bytes ARE all NOPs** — legitimate, informational only: +> ℹ️ **Problem 0: Small stolen bytes due to trailing NOP padding** +> Hook `HookName` at `0x` has size `0x` (< 5), but the trailing bytes `[0x, 0x)` are NOP padding (0x90). The size is correct — only the real instructions are stolen, and the NOPs are safely overwritten by the JMP. + +**Problem 1 — Partial overlap** (type: `"conflict"`): +> ❌ **Problem 1: Hook address range conflict** +> Hook `NewHookName` at `0x` (size `0x`, range `[0x, 0x)`) conflicts with existing hook `ExistingHookName` from `` at `0x` (size `0x`, range `[0x, 0x)`). + +**Problem 1 — Return address conflict** (type: `"return_conflict"`): +> ❌ **Problem 1: Return address conflict** +> Hook `NewHookName` at `0x` returns to `0x`, which falls within existing hook `ExistingHookName` from `` covering `[0x, 0x)`. + +A return address equal to the hook's own start address (`ret_addr == addr`) is **explicitly excluded** from this check — it is equivalent to `R->Origin()` and handled correctly by Syringe. + +For each note in the `notes` array: + +**Stacked hook** (type: `"stacked"`): +> ⚠️ **Problem 1: Stacked hook — verify intent** +> Hook `NewHookName` at `0x` (size `0x`) exactly matches existing hook `ExistingHookName` from ``. The second hook will execute after the first returns 0. Verify this is intended. + +**OK confirmation** (type: `"ok"`): +> ✓ No conflicts for hook `HookName`. + +### OUTPUT REQUIREMENT + +After completing Step 1, YOU MUST output the summary AND the updated matrix with P0 and P1 columns filled: + +``` +[Step 1 Complete] +- Hooks checked: +- Problem 0 tentative errors (pending NOP verification): +- Problem 1 conflicts (overlap): +- Problem 1 conflicts (return): +- Stacked hooks (warning): + +Updated matrix (P0 tentative, P1 filled): +| Hook | Address | P0 | P1 | P2 | P3 | P4 | +|------|---------|----|----|----|----|----| +| A | 0x... | ? | ❌ | ? | ? | ? | +| B | 0x... | ? | ✓ | ? | ? | ? | +``` + +Then mark the TodoList item as complete and move to Step 2. + +--- + +## Step 2: Check IDA MCP availability and verify NOP padding + +### RULES + +- YOU MUST attempt to connect to IDA MCP by calling `mcp_ida-pro-mcp_server_health`. +- If the health check succeeds AND the loaded IDB is `gamemd.exe`: + - P2, P3, P4 columns will be filled in Steps 3-5. + - **NOP padding verification**: For any hook with a tentative Problem 0 (size < 5) from Step 1, use IDA MCP to disassemble the range `[addr, addr + 5)` and check whether `[addr + size, addr + 5)` are all NOP instructions (0x90). + - If trailing bytes are all NOPs → downgrade P0 cell to `ℹ️` (legitimate, informational) + - If trailing bytes are NOT all NOPs → P0 cell remains `❌` (confirmed error) + - Update the Step 1 Problem 0 counts accordingly in your output +- If the health check fails OR `gamemd.exe` is not loaded: + - YOU MUST mark P2, P3, P4 columns as `—` (skipped) for ALL hooks in the matrix. + - **For each tentative Problem 0 finding**: downgrade from potential error to `⚠️` (cannot verify NOP padding without IDA MCP). State this explicitly. + - YOU MUST output the reason. + - Steps 3-5 will be checked but immediately marked as completed with `—` for all hooks. + - DO NOT silently skip this step. + +### OUTPUT REQUIREMENT + +After completing Step 2, YOU MUST output: + +``` +[Step 2 Complete] +- IDA MCP health check: +- gamemd.exe loaded: +- NOP padding verification: + - Confirmed errors (non-NOP trailing bytes): + - Legitimate (NOP padding): + - Downgraded to warning (IDA unavailable): + +Updated matrix (P0 verified, P2-P4 status determined): +| Hook | Address | P0 | P1 | P2 | P3 | P4 | +|------|---------|----|----|----|----|----| +| A | 0x... | ✓ | ❌ | ? | ? | ? | +| B | 0x... | ℹ️ | ✓ | ? | ? | ? | +| C | 0x... | ❌ | ✓ | ? | ? | ? | +| D | 0x... | ⚠️ | ✓ | — | — | — | +``` + +If IDA MCP is available, the P2-P4 cells remain `?` (to be filled in Steps 3-5). If unavailable, they are `—`. + +Then mark the TodoList item as complete and move to Step 3. + +--- + +## Step 3: Fill P2 — Instruction boundary check + +### RULES + +- If IDA MCP was unavailable in Step 2, YOU MUST skip the checks below (P2 column is already `—`). +- If an individual `mcp_ida-pro-mcp_disasm` call fails for a specific hook, mark that hook's P2 cell as `—` (skipped due to MCP error), add a note in the findings, and continue with the remaining hooks. +- YOU MUST fill the P2 column for every hook. DO NOT skip any hook. + +### Check + +For each new hook: +1. Use IDA MCP (`mcp_ida-pro-mcp_disasm`) to verify the hook address is at the start of an x86 instruction. +2. Use IDA MCP to verify that `addr + size` is also at an instruction boundary (the hook covers complete instructions). +3. For fixed return addresses, verify they are at instruction boundaries. + +If any check fails: +> ❌ **Problem 2: Instruction boundary issue** +> Hook `HookName` at `0x` (size `0x`) — . Disassemble the area at this address to find the correct boundaries. + +### OUTPUT REQUIREMENT + +After completing Step 3, YOU MUST output the per-hook detail blocks AND the updated matrix with P2 column filled: + +``` +[Step 3 Complete] +- Hooks checked: +- Problem 2 errors: + +Details for hooks with P2 findings: + +### TEST1 (0x6EC3D0, size 0x5, range [0x6EC3D0, 0x6EC3D5)) +6EC3D0 push ebx ; 1 byte +6EC3D1 push ebp ; 1 byte +6EC3D2 push esi ; 1 byte +6EC3D3 mov esi, [ecx+54h] ; 3 bytes → covers to 0x6EC3D6 + +❌ Problem 2: Instruction boundary issue — Hook end address 0x6EC3D5 falls in the middle of `mov esi, [ecx+54h]` (3 bytes, [0x6EC3D3, 0x6EC3D6)). Only the first 2 bytes are copied to the trampoline; the 3rd byte is missing. Increase size to 6. + +Updated matrix (P2 filled): +| Hook | Address | P0 | P1 | P2 | P3 | P4 | +|------|---------|----|----|----|----|----| +| A | 0x... | ✓ | ❌ | ❌ | ? | ? | +| B | 0x... | ❌ | ✓ | ✓ | ? | ? | +``` + +DO NOT omit any hook with a finding. If a hook has no P2 issues, skip its detail block. + +Then mark the TodoList item as complete and move to Step 4. + +--- + +## Step 4: Fill P3 — Variable extraction and stack access validation + +### RULES + +- If IDA MCP was unavailable in Step 2, YOU MUST skip the checks below (P3 column is already `—`). +- If an individual IDA MCP call fails for a specific hook, mark that hook's P3 cell as `—` (skipped due to MCP error), add a note in the findings, and continue with the remaining hooks. +- YOU MUST fill the P3 column for every hook. DO NOT skip any hook. +- P3 has sub-checks 3a–3e. The matrix cell shows the worst severity across all sub-checks. + +### Validation + +For each new hook, inspect the function body for `GET`, `GET_STACK`, `REF_STACK`, `LEA_STACK`, `GET_BASE` macros and register writes (`R->EAX(value)`, `R->ECX(value)`, `R->STACK(offset, value)`, etc.). Use IDA MCP to decompile or disassemble the code around the hook address and verify the register/stack state matches. + +#### 4a — Register extraction (GET) + +For `GET(type, var, reg)`: +- Check what `reg` holds at the hook point according to IDA +- If the type declared in GET differs from what IDA suggests, warn the user + +#### 4b — Stack variable extraction (GET_STACK / REF_STACK / LEA_STACK) + +For `GET_STACK(type, var, offset)` / `REF_STACK(type, var, offset)`: +- DO NOT rely on IDA's offset labels alone — they may reference a virtual frame pointer that is not ESP. `R->Stack` always reads from `captured_ESP`, so you MUST compute the actual ESP at the hook point. +- Verify EBP is a real frame pointer: disassemble the first 5-10 instructions of the function. If EBP is overwritten (e.g. `mov ebp, [esp+...]`) instead of set to `mov ebp, esp`, then EBP is NOT a frame pointer — do NOT use EBP-relative offsets from IDA as ESP offsets. +- Trace ESP from function entry to the hook point. Manually compute the cumulative offset: + 1. Start from `ESP_entry` — the ESP value immediately after the `call` that entered the function + 2. Account for every `push` (subtract 4 per push), `pop` (add 4 per pop), and `sub esp, X` / `add esp, X` between function entry and the hook address + 3. The resulting ESP at the hook point = `ESP_entry + cumulative_offset` + 4. The offset passed to `R->Stack` (after resolving `STACK_OFFSET` macros) is then added to this value +- Resolve `STACK_OFFSET` macros explicitly. `STACK_OFFSET(a, b)` is simply `(a + b)`. +- Map the final address back to the function's parameter list. + +If a type mismatch is found: +> ⚠️ **Problem 3b: Variable extraction may be incorrect** +> At `0x`: `GET_STACK(, , )` — resolved offset `` maps to function entry `+`, expected parameter `` of type `` at that position. The declared type `` does not match. + +#### 4c — Stack data macro selection check + +Determine whether `GET_STACK` (also `REF_STACK`, `LEA_STACK`) or `GET_BASE` is the correct macro based on stack alignment in the function prologue. + +Start by checking for stack alignment: disassemble the first ~20 instructions of the function. Look for `and esp, ` where `` is an alignment boundary (e.g. `0FFFFFFF8h` for 8-byte, `0FFFFFFF0h` for 16-byte). + +**If no stack alignment** — ESP is at a known offset from `ESP_entry`, so `GET_STACK` (and `REF_STACK`/`LEA_STACK`) works correctly for all parameter and local access. `GET_BASE` is not recommended here because EBP can be used for other purpose by the compiler; verify that EBP actually holds a valid frame pointer before using it: +> ⚠️ **Problem 3c: GET_BASE used without stack alignment — verify EBP** +> Hook `HookName` at `0x` uses `GET_BASE(, , )` but the function has no stack alignment. `GET_STACK` via ESP is the preferred approach. If EBP is a valid frame pointer (`mov ebp, esp` at entry, not overwritten), `GET_BASE` may still work — otherwise replace with `GET_STACK(, , )`. + +**If stack alignment exists** — `and esp, ` realigns ESP to an unpredictable offset, making it unreliable for function parameter access. Check the offsets used in `GET_STACK` and `GET_BASE`: + +1. **If any `GET_STACK` or `GET_BASE` has a positive offset** — positive offsets aim at function parameters above the entry stack. These should use `GET_BASE`: + - `GET_STACK` with positive offset → ❌ error, should use `GET_BASE` + > ❌ **Problem 3c: GET_STACK should be GET_BASE after stack alignment** + > Hook `HookName` at `0x` uses `GET_STACK(, , )` with a positive offset (``) after stack alignment. Parameters should be accessed via EBP. Replace with `GET_BASE(, , )`. + - `GET_BASE` with positive offset → ✓ correct usage, no issue + +2. **If all offsets are non-positive** (zero or negative) — these access local variables in the hook's own stack frame. `GET_STACK` should be used: + - `GET_STACK` with non-positive offset → ✓ correct usage, no issue + - `GET_BASE` with non-positive offset → ❌ error, should use `GET_STACK` + > ❌ **Problem 3c: GET_BASE should be GET_STACK after stack alignment** + > Hook `HookName` at `0x` uses `GET_BASE(, , )` with a non-positive offset (``) after stack alignment. Local variables should be accessed via ESP. Replace with `GET_STACK(, , )`. + +#### 4d — Register writes (R->EAX, R->ECX, etc.) + +For register writes like `R->EAX(value)`: +- Disassemble after the hook point to verify the register will be read as expected by the original code +- If the return address is a fixed address (not `R->Origin()`), verify the original code at that address uses the register being set + +#### 4e — Stack depth balance + +The optimal approach is to select a return address whose spd matches the hook entry spd — no stack adjustment is needed. If a suitable return address is unavailable (e.g. the only viable target has a different spd), the hook must use inline ASM (`__asm { add esp, X }` / `__asm { sub esp, X }`) to adjust ESP before returning, so that the stack depth at the return point matches the return address's expected spd. + +For each hook: + +1. Use IDA MCP (`mcp_ida-pro-mcp_disasm`) to disassemble the function containing the hook address +2. Record the `spd` at the hook entry address +3. If the hook has a fixed return address (`returns` is not `"0"` or `"?"`), record the `spd` at that return address +4. Compare the two: + - **spd matches** → ✓ The return address is well-chosen; no stack adjustment needed + - **spd differs** → Check the hook body for inline ASM that adjusts ESP to compensate for the difference (`spd_entry + adjustment == spd_ret`) + - If adjustment is present and correct → ✓ Stack is balanced via manual adjustment + - If no adjustment is found or adjustment incorrect → ❌ **Problem 3e: Stack depth imbalance** + > Hook `HookName` at `0x` — the return address `0x` has spd `` but the hook entry has spd `` (difference: ``). No inline ASM adjustment found to compensate. Either change the return address to one with matching spd, or add `__asm { add esp, }` / `__asm { sub esp, <-diff> }` before the return. + +If `spd` is unavailable (IDA could not analyze the function), skip this check and note it: `ℹ️ spd unavailable for hook HookName.` + +If all Problem 3 checks pass: "✓ Variable extraction and stack access checks passed." + +### OUTPUT REQUIREMENT + +After completing Step 4, YOU MUST output the per-hook detail blocks AND the updated matrix with P3 column filled: + +``` +[Step 4 Complete] +- Hooks checked: +- Problem 3a errors (register extraction): +- Problem 3b errors (stack variable extraction): +- Problem 3c errors (GET_STACK vs GET_BASE): +- Problem 3d errors (register writes): +- Problem 3e errors (stack depth imbalance): + +Details for hooks with P3 findings: + +### TEST4 (0x6EB3B8) +GET_STACK(double, number, STACK_OFFSET(0x18, 0x8)) + +Function: TeamClass::AttackedBy(TeamClass *this, int a2, int a3, FootClass *pAttacker) + +⚠️ Problem 3b: Stack variable type may not match — STACK_OFFSET(0x18, 0x8) = 0x20. After `sub esp, 0Ch` + 3 pushes (16 bytes), ESP_entry + 0x20 - 0x10 = 0x10 → maps to parameter a3 (int). Declared type is double. Verify. + +Updated matrix (P3 filled): +| Hook | Address | P0 | P1 | P2 | P3 | P4 | +|------|---------|----|----|----|----|----| +| A | 0x... | ✓ | ❌ | ❌ | ✓ | ? | +| B | 0x... | ❌ | ✓ | ✓ | ⚠️ | ? | +``` + +DO NOT omit any hook with a finding. The matrix cell for P3 shows the worst severity across all sub-checks. + +Then mark the TodoList item as complete and move to Step 5. + +--- + +## Step 5: Fill P4 — Relative instruction coverage check + +### RULES + +- If IDA MCP was unavailable in Step 2, YOU MUST skip the checks below (P4 column is already `—`). +- If an individual IDA MCP call fails for a specific hook, mark that hook's P4 cell as `—` (skipped due to MCP error), add a note in the findings, and continue with the remaining hooks. +- YOU MUST fill the P4 column for every hook. DO NOT skip any hook. + +### Check + +For each new hook, disassemble the full address range `[addr, addr + size)` using IDA MCP and check for any relative-offset instructions. These instructions encode their target as `current_address + instruction_length + relative_offset`. When Syringe copies these bytes to a trampoline at a different address, the relative offset points to the wrong location. + +Hooks that cover any of the following instructions MUST return a fixed value (not 0): + +Relative jump/call instructions: +- `jmp short`, `jmp near`, `jz`, `jnz`, `je`, `jne`, `jg`, `jge`, `jl`, `jle` +- `ja`, `jae`, `jb`, `jbe`, `jo`, `jno`, `js`, `jns`, `jp`, `jnp`, `jpe`, `jpo` +- `jcxz`, `jecxz`, `jrcxz` +- `call` (relative call, i.e. `call rel32`) +- `loop`, `loope`, `loopne`, `loopz`, `loopnz` +- `xbegin` + +EIP-relative addressing instructions: +- `mov`, `lea`, `cmp`, `add`, `sub`, `and`, `or`, `xor`, `test` +- `push`, `pop`, `movsxd`, `movzx`, `movsx` +- When any of the above uses EIP-relative addressing mode + +Check the `returns` field from Step 0. If the hook returns `"0"` but covers any of these instructions: +> ❌ **Problem 4: Hook covers relative-offset instruction but returns 0** +> Hook `HookName` at `0x` covers instruction `` at `0x` which uses relative addressing (encoded relative offset `` → target ``). This hook MUST return a fixed value (e.g. `return 0x` or `return R->Origin() + `), not `return 0`. + +If no relative-offset instructions are found, or the hook already returns a fixed value: "✓ No relative instruction issues found." + +### OUTPUT REQUIREMENT + +After completing Step 5, YOU MUST output the per-hook detail blocks AND the updated matrix with P4 column filled: + +``` +[Step 5 Complete] +- Hooks checked: +- Problem 4 errors (relative instruction): + +Details for hooks with P4 findings: + +### TEST7 (0x4D56B1, size 0x6, range [0x4D56B1, 0x4D56B7)) +4D56B1 jz loc_4D5A42 ; 6 bytes (0F 84 8B 03 00 00) — near conditional jump, 32-bit relative offset + +❌ Problem 4: Hook covers relative-offset instruction but returns 0 — The hooked `jz` encodes a 32-bit relative offset (0x38B → target = 0x4D56B7 + 0x38B = 0x4D5A42). When copied to a trampoline, the offset points to the wrong address. MUST return a fixed address (e.g. `return R->Origin() + 6`), not `return 0`. + +Updated matrix (P4 filled — matrix now complete): +| Hook | Address | P0 | P1 | P2 | P3 | P4 | +|------|---------|----|----|----|----|----| +| A | 0x... | ✓ | ❌ | ❌ | ✓ | ✓ | +| B | 0x... | ❌ | ✓ | ✓ | ⚠️ | ❌ | +``` + +DO NOT omit any hook with a finding. + +Then mark the TodoList item as complete and move to Step 6. + +--- + +## Step 6: Output complete matrix and details + +### RULES + +- YOU MUST output the final complete matrix (no `?` cells should remain). +- YOU MUST include ALL hooks discovered in Step 0. +- YOU MUST include ALL findings in the Issues by hook section. DO NOT omit any hook that has non-✓ findings. DO NOT show only the most severe issues — every error, warning, and note must appear. +- DO NOT create a "✓ No-problem hooks" or "✓ 无问题钩子" section. If a hook passed everything, simply omit it from the Issues section. +- Hooks in the table MUST be in the same order as discovered in Step 0. + +### OUTPUT REQUIREMENT + +YOU MUST output the final summary: + +``` +======================================== + Check Hooks Summary +======================================== +Total hooks checked: + +| Hook | Address | P0 | P1 | P2 | P3 | P4 | +|------|---------|----|----|----|----|----| +| A | 0x... | ✓ | ❌ | ❌ | ✓ | ✓ | +| B | 0x... | ℹ️ | ✓ | ✓ | ⚠️ | ❌ | +| C | 0x... | ❌ | ✓ | ✓ | ✓ | ✓ | + +### Issues by hook + +### HookA (0x...) +- **P1 — Conflict with ExistingHook:** overlaps [0x..., 0x...) + +### HookB (0x...) +- **P3b — Variable type mismatch:** declared , IDA suggests +- **P4 — Relative instruction:** covers `call rel32` at 0x... but returns 0 + +### HookC (0x...) +- **P0 — Insufficient stolen bytes:** size is 0x3, trailing bytes `[0x... + 3, 0x... + 5)` are not NOP padding, increase to at least 5 +``` + +If no problems were found at all: +``` +======================================== + Check Hooks Summary +======================================== +Total hooks checked: + +| Hook | Address | P0 | P1 | P2 | P3 | P4 | +|------|---------|----|----|----|----|----| +| ... | ... | ✓ | ✓ | ✓ | ✓ | ✓ | + +✅ All checks passed. No issues found with the new hooks. +======================================== +``` + +Then mark the TodoList item as complete. + +--- + +## Appendix: Complete run example + +This appendix shows the full output of a real hook check from end to end, using commit `47b471302 Add customization for laser Z-adjust`. + +### Step 0 + +``` +$ python discover_hooks.py --commit 47b471302 +{ + "mode": "commit", + "description": "commit 47b471302", + "count": 1, + "hooks": [ + { + "address": "0x6FD3FD", + "name": "TechnoClass_LaserZap_ZAdjust", + "size": 5, + "file": "src/Misc/Hooks.LaserDraw.cpp", + "returns": "0" + } + ] +} +``` + +``` +[Step 0 Complete] +- Mode used: A (commit 47b471302) +- Hooks discovered: 1 — TechnoClass_LaserZap_ZAdjust + +Matrix (empty, awaiting checks): +| Hook | Address | P0 | P1 | P2 | P3 | P4 | +|------|---------|----|----|----|----|----| +| TechnoClass_LaserZap_ZAdjust | 0x6FD3FD | ? | ? | ? | ? | ? | +``` + +### Step 1 + +``` +$ python discover_hooks.py --commit 47b471302 --json-only | python check_hook_conflicts.py +{ + "errors": [], + "notes": [ + { + "problem": "Problem 1", + "hook": "TechnoClass_LaserZap_ZAdjust", + "address": "0x6FD3FD", + "type": "ok", + "message": "No conflicts detected for hook 'TechnoClass_LaserZap_ZAdjust' at 0x6FD3FD." + } + ] +} +``` + +✓ No conflicts for hook `TechnoClass_LaserZap_ZAdjust`. + +``` +[Step 1 Complete] +- Hooks checked: 1 +- Problem 0 tentative errors (pending NOP verification): 0 +- Problem 1 conflicts (overlap): 0 +- Problem 1 conflicts (return): 0 +- Stacked hooks (warning): 0 + +Updated matrix (P0 tentative, P1 filled): +| Hook | Address | P0 | P1 | P2 | P3 | P4 | +|------|---------|----|----|----|----|----| +| TechnoClass_LaserZap_ZAdjust | 0x6FD3FD | ? | ✓ | ? | ? | ? | +``` + +### Step 2 + +IDA MCP health check: **pass** +Module: `gamemd.exe` — loaded + +Hook size is 5 (exactly the JMP instruction size). No NOP padding verification needed. + +P0 cell: ✓ (size >= 5) + +``` +[Step 2 Complete] +- IDA MCP health check: pass +- gamemd.exe loaded: yes +- NOP padding verification: N/A (size = 5) + +Updated matrix (P0 verified, P2-P4 status determined): +| Hook | Address | P0 | P1 | P2 | P3 | P4 | +|------|---------|----|----|----|----|----| +| TechnoClass_LaserZap_ZAdjust | 0x6FD3FD | ✓ | ✓ | ? | ? | ? | +``` + +### Step 3 + +Disassembly at hook address `0x6FD3FD` (inside `TechnoClass::CreateLaser`): + +``` +6fd3fd push eax ; 1 byte → [0x6FD3FD, 0x6FD3FE) +6fd3fe mov eax, [esp+70h] ; 4 bytes → [0x6FD3FE, 0x6FD402) +``` + +- Hook address `0x6FD3FD` is at `push eax` start → ✓ instruction boundary +- `addr + size = 0x6FD402` → next instruction is `sub esp, 0Ch` → ✓ instruction boundary +- Returns `"0"` (no fixed return address) → skip return address boundary check + +✓ No instruction boundary issues. + +``` +[Step 3 Complete] +- Hooks checked: 1 +- Problem 2 errors: 0 + +Updated matrix (P2 filled): +| Hook | Address | P0 | P1 | P2 | P3 | P4 | +|------|---------|----|----|----|----|----| +| TechnoClass_LaserZap_ZAdjust | 0x6FD3FD | ✓ | ✓ | ✓ | ? | ? | +``` + +### Step 4 + +Source code of the hook body: + +```cpp +GET_STACK(WeaponTypeClass*, pWeapon, STACK_OFFSET(0x6C, 0xC)); +GET(int, zAdjust, EAX); +zAdjust += WeaponTypeExt::ExtMap.Find(pWeapon)->LaserZAdjust.Get( + RulesExt::Global()->LaserZAdjust); +R->EAX(zAdjust); +``` + +**4a — Register extraction (GET):** EAX holds the zAdjust value at hook point (confirmed by `push eax; zAdjust`). ✓ + +**4b — Stack variable extraction (GET_STACK):** `STACK_OFFSET(0x6C, 0xC) = 0x78`. At hook point, ESP = ESP_entry - 8 (after 2 preceding pushes). The resolved offset `ESP + 0x78 = ESP_entry + 0x70` maps to `pWeapon`, which is at `[EBP + 0x5C]` in IDA's stack frame. ✓ + +**4c — GET_STACK vs GET_BASE:** Function has no stack alignment (`and esp, ...` not found). No EBP frame pointer set up. `GET_STACK` via ESP is the correct approach. ✓ + +**4d — Register writes:** `R->EAX(zAdjust)` writes the modified zAdjust back to EAX. The trampoline then executes `push eax`, pushing the corrected value. ✓ + +**4e — Stack depth balance:** Hook returns `"0"` (R->Origin() → trampoline). No fixed return address to compare spd against. ✓ (skipped) + +✓ All variable extraction and stack access checks passed. + +``` +[Step 4 Complete] +- Hooks checked: 1 +- Problem 3a errors (register extraction): 0 +- Problem 3b errors (stack variable extraction): 0 +- Problem 3c errors (GET_STACK vs GET_BASE): 0 +- Problem 3d errors (register writes): 0 +- Problem 3e errors (stack depth imbalance): 0 + +Updated matrix (P3 filled): +| Hook | Address | P0 | P1 | P2 | P3 | P4 | +|------|---------|----|----|----|----|----| +| TechnoClass_LaserZap_ZAdjust | 0x6FD3FD | ✓ | ✓ | ✓ | ✓ | ? | +``` + +### Step 5 + +Disassembly of stolen bytes `[0x6FD3FD, 0x6FD402)`: + +``` +6fd3fd push eax ; opcode 50 — not relative +6fd3fe mov eax, [esp+70h] ; ESP-relative, not EIP-relative +``` + +No relative jump/call or EIP-relative addressing found. Hook returns `"0"` — safe because no relative instructions are covered. + +✓ No relative instruction issues. + +``` +[Step 5 Complete] +- Hooks checked: 1 +- Problem 4 errors (relative instruction): 0 + +Updated matrix (P4 filled — matrix now complete): +| Hook | Address | P0 | P1 | P2 | P3 | P4 | +|------|---------|----|----|----|----|----| +| TechnoClass_LaserZap_ZAdjust | 0x6FD3FD | ✓ | ✓ | ✓ | ✓ | ✓ | +``` + +### Step 6 — Final summary + +``` +======================================== + Check Hooks Summary +======================================== +Total hooks checked: 1 + +| Hook | Address | P0 | P1 | P2 | P3 | P4 | +|------|---------|----|----|----|----|----| +| TechnoClass_LaserZap_ZAdjust | 0x6FD3FD | ✓ | ✓ | ✓ | ✓ | ✓ | + +✅ All checks passed. No issues found with the new hooks. +======================================== +``` \ No newline at end of file diff --git a/.agents/skills/check-hooks/ares_3.0p1_hooks.cpp b/.agents/skills/check-hooks/ares_3.0p1_hooks.cpp new file mode 100644 index 0000000000..ef05ee3fc6 --- /dev/null +++ b/.agents/skills/check-hooks/ares_3.0p1_hooks.cpp @@ -0,0 +1,1461 @@ +DEFINE_HOOK(0x533058, CommandClassCallback_Register, 0x7) +DEFINE_HOOK(0x52c5e0, Ares_NoLogo, 0x7) +DEFINE_HOOK(0x6ad0ed, Ares_AllowSinglePlay, 0x5) +DEFINE_HOOK(0x7cd810, ExeRun, 0x9) // 3.0 +DEFINE_HOOK(0x7cd819, ExeRun, 0x5) // 3.0 p1 +DEFINE_HOOK(0x7cd8ef, ExeTerminate, 0x9) +DEFINE_HOOK(0x52f639, YR_CmdLineParse, 0x5) +DEFINE_HOOK(0x47ae36, CDFileClass_SetFileName, 0x8) +DEFINE_HOOK(0x47b026, FileFindOpen, 0x8) +DEFINE_HOOK(0x7258d0, AnnounceInvalidPointer, 0x6) +DEFINE_HOOK(0x685659, Scenario_ClearClasses, 0xa) +DEFINE_HOOK(0x6bc0cd, LoadRA2MD, 0x5) +DEFINE_HOOK(0x5facdf, Options_LoadFromINI, 0x5) +DEFINE_HOOK(0x4753f0, ArmorType_FindIndex, 0xa) +DEFINE_HOOK(0x4b9a52, DropshipLoadout_PrintArmor, 0x5) +DEFINE_HOOK(0x5bdc8c, MouseClass_UpdateCursor, 0x7) +DEFINE_HOOK(0x5bdadf, MouseClass_UpdateCursorMinimapState_UseCursor, 0x0) +DEFINE_HOOK(0x5bddc8, MouseClass_Update_AnimateCursor, 0x6) +DEFINE_HOOK(0x5bde64, MouseClass_Update_AnimateCursor2, 0x6) +DEFINE_HOOK(0x5bdb90, MouseClass_GetCursorFirstFrame_Minimap, 0xb) +DEFINE_HOOK(0x5be974, MouseClass_GetCursorFirstFrame, 0x7) +DEFINE_HOOK(0x5be994, MouseClass_GetCursorFrameCount, 0x7) +DEFINE_HOOK(0x5bdbc4, MouseClass_GetCursorCurrentFrame, 0x7) +DEFINE_HOOK(0x5bdc1b, MouseClass_GetCursorHotSpot, 0x7) +DEFINE_HOOK(0x42784b, AnimTypeClass_CTOR, 0x5) +DEFINE_HOOK(0x428ea8, AnimTypeClass_SDDTOR, 0x5) +DEFINE_HOOK(0x428970, AnimTypeClass_SaveLoad_Prefix, 0x8) +DEFINE_HOOK_AGAIN(0x428800, AnimTypeClass_SaveLoad_Prefix, 0xa) +DEFINE_HOOK(0x42892c, AnimTypeClass_Load_Suffix, 0x6) +DEFINE_HOOK_AGAIN(0x428958, AnimTypeClass_Load_Suffix, 0x6) +DEFINE_HOOK(0x42898a, AnimTypeClass_Save_Suffix, 0x3) +DEFINE_HOOK(0x4287e9, AnimTypeClass_LoadFromINI, 0xa) +DEFINE_HOOK_AGAIN(0x4287dc, AnimTypeClass_LoadFromINI, 0xa) +DEFINE_HOOK(0x4232ce, AnimClass_Draw_SetPalette, 0x6) +DEFINE_HOOK(0x42511b, AnimClass_Expired_ScorchFlamer, 0x7) +DEFINE_HOOK_AGAIN(0x4250c9, AnimClass_Expired_ScorchFlamer, 0x7) +DEFINE_HOOK_AGAIN(0x42513f, AnimClass_Expired_ScorchFlamer, 0x7) +DEFINE_HOOK(0x425002, AnimClass_Expired_SpawnsParticle, 0x6) +DEFINE_HOOK(0x4242ca, AnimClass_Update_FixIE_TrailerSeperation, 0x6) +DEFINE_HOOK(0x424538, AnimClass_Update_DamageDelay, 0x6) +DEFINE_HOOK(0x45e50c, BuildingTypeClass_CTOR, 0x6) +DEFINE_HOOK(0x45e707, BuildingTypeClass_DTOR, 0x6) +DEFINE_HOOK(0x465300, BuildingTypeClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK_AGAIN(0x465010, BuildingTypeClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK(0x4652ed, BuildingTypeClass_Load_Suffix, 0x7) +DEFINE_HOOK(0x46536a, BuildingTypeClass_Save_Suffix, 0x7) +DEFINE_HOOK(0x464a56, BuildingTypeClass_LoadFromINI, 0xa) +DEFINE_HOOK_AGAIN(0x464a49, BuildingTypeClass_LoadFromINI, 0xa) +DEFINE_HOOK(0x445f80, BuildingClass_Place, 0x5) +DEFINE_HOOK(0x43fb6d, BuildingClass_Update_LaserFencePost, 0x6) +DEFINE_HOOK(0x465d4a, BuildingTypeClass_IsUndeployable, 0x6) +DEFINE_HOOK(0x465550, BuildingTypeClass_GetFoundationOutline, 0x6) +DEFINE_HOOK(0x464af0, BuildingTypeClass_GetSizeInLeptons, 0x6) +DEFINE_HOOK(0x45ece0, BuildingTypeClass_GetMaxPips, 0x6) +DEFINE_HOOK(0x45f2b4, BuildingTypeClass_Load2DArt_BuildupTime, 0x5) +DEFINE_HOOK(0x465a48, BuildingTypeClass_GetBuildup_BuildupTime, 0x5) +DEFINE_HOOK(0x45eaa5, BuildingTypeClass_LoadArt_BuildupTime, 0x6) +DEFINE_HOOK(0x4fb2fd, HouseClass_UnitFromFactory_BuildingSlam, 0x6) +DEFINE_HOOK(0x43bcbd, BuildingClass_CTOR, 0x6) +DEFINE_HOOK(0x43c022, BuildingClass_DTOR, 0x6) +DEFINE_HOOK(0x454190, BuildingClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK_AGAIN(0x453e20, BuildingClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK(0x45417e, BuildingClass_Load_Suffix, 0x5) +DEFINE_HOOK(0x454244, BuildingClass_Save_Suffix, 0x7) +DEFINE_HOOK(0x44d8a1, BuildingClass_UnloadPassengers_Unload, 0x6) +DEFINE_HOOK(0x44bb1b, BuildingClass_Mi_Repair_Promote, 0x6) +DEFINE_HOOK(0x44e855, BuildingClass_PowerProduced_EMP, 0x6) +DEFINE_HOOK(0x44d755, BuildingClass_GetPipFillLevel_Tiberium, 0x6) +DEFINE_HOOK(0x709b4e, TechnoClass_DrawPipscale_SkipSkipTiberium, 0x6) +DEFINE_HOOK(0x446e9f, BuildingClass_Place_FreeUnit_Mission, 0x6) +DEFINE_HOOK(0x4467d6, BuildingClass_Place_NeedsEngineer, 0x6) +DEFINE_HOOK(0x454bf7, BuildingClass_UpdatePowered_NeedsEngineer, 0x6) +DEFINE_HOOK(0x451a54, BuildingClass_PlayAnim_NeedsEngineer, 0x6) +DEFINE_HOOK(0x444d26, BuildingClass_KickOutUnit_ArmoryExitBug, 0x6) +DEFINE_HOOK(0x44d4ca, BuildingClass_Mi_Missile_NoReport, 0x9) +DEFINE_HOOK(0x44840b, BuildingClass_ChangeOwnership_Tech, 0x6) +DEFINE_HOOK(0x4409f4, BuildingClass_Put_ProduceCash, 0x6) +DEFINE_HOOK(0x43fd2c, BuildingClass_Update_ProduceCash, 0x6) +DEFINE_HOOK(0x4482bd, BuildingClass_ChangeOwnership_ProduceCash, 0x6) +DEFINE_HOOK(0x452287, BuildingClass_GoOnline_TogglePower, 0x6) +DEFINE_HOOK(0x452393, BuildingClass_GoOffline_TogglePower, 0x7) +DEFINE_HOOK(0x452210, BuildingClass_Enable_TogglePower, 0x7) +DEFINE_HOOK(0x44c844, BuildingClass_MissionRepair_Reload, 0x6) +DEFINE_HOOK(0x452218, BuildingClass_Enable_Temporal_Factories, 0x6) +DEFINE_HOOK(0x4521c8, BuildingClass_Disable_Temporal_Factories, 0x6) +DEFINE_HOOK(0x459c03, BuildingClass_CanBeSelectedNow_MassSelectable, 0x6) +DEFINE_HOOK(0x4509b4, BuildingClass_UpdateRepair_Funds, 0x7) +DEFINE_HOOK(0x447a63, BuildingClass_QueueImageAnim_Sell, 0x3) +DEFINE_HOOK(0x51e4ed, InfantryClass_GetActionOnObject_EngineerRepairable, 0x6) +DEFINE_HOOK(0x51fa82, InfantryClass_GetActionOnCell_EngineerRepairable, 0x6) +DEFINE_HOOK(0x43ca80, BuildingClass_ReceivedRadioCommand_DockUnloadCell, 0x7) +DEFINE_HOOK(0x7376d9, UnitClass_ReceivedRadioCommand_DockUnload_Facing, 0x5) +DEFINE_HOOK(0x73df66, UnitClass_Mi_Unload_DockUnload_Facing, 0x5) +DEFINE_HOOK(0x73e013, UnitClass_Mi_Unload_DockUnloadCell1, 0x6) +DEFINE_HOOK(0x73e17f, UnitClass_Mi_Unload_DockUnloadCell2, 0x6) +DEFINE_HOOK(0x73e2bf, UnitClass_Mi_Unload_DockUnloadCell3, 0x6) +DEFINE_HOOK(0x741bdb, UnitClass_SetDestination_DockUnloadCell, 0x7) +DEFINE_HOOK(0x44019d, BuildingClass_Update_Battery, 0x6) +DEFINE_HOOK(0x4555d5, BuildingClass_IsPowerOnline_KeepOnline, 0x5) +DEFINE_HOOK(0x440c08, BuildingClass_Put_AIBaseNormal, 0x6) +DEFINE_HOOK(0x445a72, BuildingClass_Remove_AIBaseNormal, 0x6) +DEFINE_HOOK(0x456370, BuildingClass_UnmarkBaseSpace_AIBaseNormal, 0x6) +DEFINE_HOOK(0x4a8ff5, MapClass_CanBuildingTypeBePlacedHere_Ignore, 0x5) +DEFINE_HOOK(0x44266b, BuildingClass_ReceiveDamage_Destroyed, 0x6) +DEFINE_HOOK(0x44d760, BuildingClass_Destroyed_UnitLost, 0x7) +DEFINE_HOOK(0x5880a0, MapClass_FindFirstFirestorm, 0x6) +DEFINE_HOOK(0x4fb257, HouseClass_UnitFromFactory_Firewall, 0x6) +DEFINE_HOOK(0x445355, BuildingClass_KickOutUnit_Firewall, 0x6) +DEFINE_HOOK(0x6d5455, sub_6D5030, 0x6) +DEFINE_HOOK(0x6d5a5c, sub_6D59D0, 0x6) +DEFINE_HOOK(0x43efb3, BuildingClass_GetStaticImageFrame, 0x6) +DEFINE_HOOK(0x4423e7, BuildingClass_ReceiveDamage_FSW, 0x5) +DEFINE_HOOK(0x440d01, BuildingClass_Put_FirestormWall, 0x6) +DEFINE_HOOK(0x445df4, BuildingClass_Remove_FirestormWall, 0x6) +DEFINE_HOOK(0x440378, BuildingClass_Update_FirestormWall, 0x6) +DEFINE_HOOK(0x483d94, CellClass_UpdatePassability, 0x6) +DEFINE_HOOK(0x51bd4c, InfantryClass_Update, 0x6) +DEFINE_HOOK(0x51c4c8, InfantryClass_IsCellOccupied, 0x6) +DEFINE_HOOK(0x73f7b0, UnitClass_IsCellOccupied, 0x6) +DEFINE_HOOK(0x449518, BuildingClass_IsSellable_FirestormWall, 0x6) +DEFINE_HOOK(0x6f64cb, TechnoClass_DrawHealthBar_FirestormWall, 0x6) +DEFINE_HOOK(0x4da53e, FootClass_Update, 0x6) +DEFINE_HOOK(0x45ec90, BuildingTypeClass_GetFoundationWidth, 0x6) +DEFINE_HOOK(0x45eca0, BuildingTypeClass_GetFoundationHeight, 0x6) +DEFINE_HOOK(0x656584, RadarClass_GetFoundationShape, 0x6) +DEFINE_HOOK(0x6563b0, RadarClass_UpdateFoundationShapes_Custom, 0x5) +DEFINE_HOOK(0x568411, MapClass_AddContentAt_Foundation_P1, 0x0) +DEFINE_HOOK(0x568565, MapClass_AddContentAt_Foundation_OccupyHeight, 0x5) +DEFINE_HOOK(0x568841, MapClass_RemoveContentAt_Foundation_P1, 0x0) +DEFINE_HOOK(0x568997, MapClass_RemoveContentAt_Foundation_OccupyHeight, 0x5) +DEFINE_HOOK(0x4a8c77, DisplayClass_ProcessFoundation1_UnlimitBuffer, 0x5) +DEFINE_HOOK(0x4a8dd7, DisplayClass_ProcessFoundation2_UnlimitBuffer, 0x5) +DEFINE_HOOK(0x6d5573, sub_6D5030_CustomFoundation, 0x6) +DEFINE_HOOK_AGAIN(0x6d50fb, sub_6D5030_CustomFoundation, 0x5) +DEFINE_HOOK(0x440709, BuildingClass_Put, 0x6) +DEFINE_HOOK(0x480534, CellClass_AttachesToNeighbourOverlay, 0x5) +DEFINE_HOOK(0x47c8ab, CellClass_CanThisExistHere_GateOnWall, 0x6) +DEFINE_HOOK(0x44e550, BuildingClass_Mi_Open_GateDown, 0x6) +DEFINE_HOOK(0x44e61e, BuildingClass_Mi_Open_GateUp, 0x6) +DEFINE_HOOK(0x4571e0, BuildingClass_Infiltrate, 0x5) +DEFINE_HOOK(0x4574d2, BuildingClass_Infiltrate_Standard, 0x6) +DEFINE_HOOK_AGAIN(0x457533, BuildingClass_Infiltrate_Standard, 0x6) +DEFINE_HOOK(0x70aa60, TechnoClass_DrawExtraInfo, 0x6) +DEFINE_HOOK(0x43e7b0, BuildingClass_DrawVisible, 0x5) +DEFINE_HOOK(0x44161c, BuildingClass_Destroy_OldSpy1, 0x6) +DEFINE_HOOK(0x448312, BuildingClass_ChangeOwnership_OldSpy1, 0xa) +DEFINE_HOOK(0x448d95, BuildingClass_ChangeOwnership_OldSpy2, 0x8) +DEFINE_HOOK(0x44f7a0, BuildingClass_UpdateDisplayTo, 0x0) +DEFINE_HOOK(0x509303, HouseClass_AllyWith_unused, 0x0) +DEFINE_HOOK(0x459ed0, BuildingClass_GetUIName, 0x6) +DEFINE_HOOK(0x44b2fe, BuildingClass_Mi_Attack_IsPrism, 0x6) +DEFINE_HOOK(0x447fae, BuildingClass_CanFire_PrismForward, 0x6) +DEFINE_HOOK(0x4503f0, BuildingClass_Update_Prism, 0x9) +DEFINE_HOOK(0x44abd0, BuildingClass_FireLaser, 0x5) +DEFINE_HOOK(0x4424ef, BuildingClass_ReceiveDamage_PrismForward, 0x6) +DEFINE_HOOK(0x447113, BuildingClass_Sell_PrismForward, 0x6) +DEFINE_HOOK(0x448277, BuildingClass_ChangeOwner_PrismForwardAndLeaveBomb, 0x5) +DEFINE_HOOK(0x71af76, TemporalClass_Fire_PrismForwardAndWarpable, 0x9) +DEFINE_HOOK(0x70fd9a, TechnoClass_Drain_PrismForward, 0x6) +DEFINE_HOOK(0x454b3d, BuildingClass_UpdatePowered_PrismForward, 0x6) +DEFINE_HOOK(0x44ebf0, BuildingClass_Disappear_PrismForward, 0x5) +DEFINE_HOOK(0x70da95, TechnoClass_RadarTrackingUpdate_AnnounceDetected, 0x6) +DEFINE_HOOK(0x455828, BuildingClass_SensorArrayActivate, 0x8) +DEFINE_HOOK(0x4556e1, BuildingClass_SensorArrayDeactivate, 0x7) +DEFINE_HOOK(0x4557bc, BuildingClass_SensorArray_BuildingRedraw, 0x6) +DEFINE_HOOK_AGAIN(0x455923, BuildingClass_SensorArray_BuildingRedraw, 0x6) +DEFINE_HOOK(0x454b5f, BuildingClass_UpdatePowered_SensorArray, 0x6) +DEFINE_HOOK_AGAIN(0x4549f8, BuildingClass_UpdatePowered_SensorArray, 0x6) +DEFINE_HOOK(0x4524a3, BuildingClass_DisableThings, 0x6) +DEFINE_HOOK(0x43fe69, BuildingClass_Update_SensorArray, 0xa) +DEFINE_HOOK(0x448b70, BuildingClass_ChangeOwnership_SensorArrayA, 0x6) +DEFINE_HOOK(0x448c3e, BuildingClass_ChangeOwnership_SensorArrayB, 0x6) +DEFINE_HOOK(0x4416a2, BuildingClass_Destroy_SensorArray, 0x6) +DEFINE_HOOK(0x4566f9, BuildingClass_GetRangeOfRadial_SensorArray, 0x6) +DEFINE_HOOK(0x457d58, BuildingClass_CanBeOccupied_SpecificOccupiers, 0x6) +DEFINE_HOOK(0x457db7, BuildingClass_CanBeOccupied_SpecificAssaulters, 0x6) +DEFINE_HOOK(0x441f12, BuildingClass_Destroy_RubbleYell, 0x6) +DEFINE_HOOK(0x441f2c, BuildingClass_Destroy_KickOutOfRubble, 0x5) +DEFINE_HOOK(0x44725f, BuildingClass_GetActionOnObject_TargetABuilding, 0x5) +DEFINE_HOOK(0x443414, BuildingClass_ActionOnObject, 0x6) +DEFINE_HOOK(0x4494d2, BuildingClass_IsSellable, 0x6) +DEFINE_HOOK(0x52297f, InfantryClass_GarrisonBuilding_OccupierEntered, 0x5) +DEFINE_HOOK(0x4586ca, BuildingClass_KillOccupiers_EachOccupierKilled, 0x6) +DEFINE_HOOK(0x4581cd, BuildingClass_UnloadOccupants_AllOccupantsHaveLeft, 0x6) +DEFINE_HOOK(0x458729, BuildingClass_KillOccupiers_AllOccupantsKilled, 0x6) +DEFINE_HOOK(0x43c326, BuildingClass_ReceivedRadioCommand_QueryCanEnter_Tunnel, 0xa) +DEFINE_HOOK(0x43c716, BuildingClass_ReceivedRadioCommand_RequestCompleteEnter_Tunnel, 0x6) +DEFINE_HOOK(0x44351a, BuildingClass_ActionOnObject_Tunnel, 0x6) +DEFINE_HOOK(0x44731c, BuildingClass_GetActionOnObject_Tunnel, 0x6) +DEFINE_HOOK(0x44d8a7, BuildingClass_Mi_Unload_Tunnel, 0x6) +DEFINE_HOOK(0x442df2, BuildingClass_Demolish_Tunnel, 0x6) +DEFINE_HOOK(0x44a37f, BuildingClass_Mi_Selling_Tunnel, 0x6) +DEFINE_HOOK(0x71a995, TemporalClass_Update_Tunnel, 0x5) +DEFINE_HOOK(0x73f606, UnitClass_IsCellOccupied_Tunnel, 0x6) +DEFINE_HOOK(0x741ce5, UnitClass_SetDestination_Tunnel, 0x6) +DEFINE_HOOK(0x73a23f, UnitClass_UpdatePosition_Tunnel, 0x6) +DEFINE_HOOK(0x51ed8e, InfantryClass_GetActionOnObject_Tunnel, 0x6) +DEFINE_HOOK(0x51a2ad, InfantryClass_UpdatePosition_Tunnel, 0x9) +DEFINE_HOOK(0x46bdd9, BulletTypeClass_CTOR, 0x5) +DEFINE_HOOK(0x46c8b6, BulletTypeClass_SDDTOR, 0x6) +DEFINE_HOOK(0x46c730, BulletTypeClass_SaveLoad_Prefix, 0x8) +DEFINE_HOOK_AGAIN(0x46c6a0, BulletTypeClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK(0x46c722, BulletTypeClass_Load_Suffix, 0x4) +DEFINE_HOOK(0x46c74a, BulletTypeClass_Save_Suffix, 0x3) +DEFINE_HOOK(0x46c429, BulletTypeClass_LoadFromINI, 0xa) +DEFINE_HOOK_AGAIN(0x46c41c, BulletTypeClass_LoadFromINI, 0xa) +DEFINE_HOOK(0x6fe709, TechnoClass_Fire_BallisticScatter1, 0x6) +DEFINE_HOOK(0x6fe7fe, TechnoClass_Fire_BallisticScatter2, 0x5) +DEFINE_HOOK(0x4664ba, BulletClass_CTOR, 0x5) +DEFINE_HOOK(0x4665e9, BulletClass_DTOR, 0xa) +DEFINE_HOOK(0x46afb0, BulletClass_SaveLoad_Prefix, 0x8) +DEFINE_HOOK_AGAIN(0x46ae70, BulletClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK(0x46af97, BulletClass_Load_Suffix, 0x7) +DEFINE_HOOK_AGAIN(0x46af9e, BulletClass_Load_Suffix, 0x7) +DEFINE_HOOK(0x46afc4, BulletClass_Save_Suffix, 0x3) +DEFINE_HOOK(0x468379, BulletClass_Draw_SetAnimPalette, 0x6) +DEFINE_HOOK(0x468be2, BulletClass_ShouldDetonate_Obstacle, 0x6) +DEFINE_HOOK(0x46867f, BulletClass_SetMovement_Parachute, 0x5) +DEFINE_HOOK(0x4688bd, BulletClass_SetMovement_Obstacle, 0x6) +DEFINE_HOOK(0x46a5b2, BulletClass_Shrapnel_WeaponType1, 0x6) +DEFINE_HOOK(0x46aa27, BulletClass_Shrapnel_WeaponType2, 0x9) +DEFINE_HOOK(0x469eba, BulletClass_DetonateAt_Splits, 0x6) +DEFINE_HOOK(0x468eb9, BulletClass_Fire_SplitsA, 0x6) +DEFINE_HOOK(0x468ffa, BulletClass_Fire_SplitsB, 0x6) +DEFINE_HOOK(0x467b94, BulletClass_Update_Ranged, 0x7) +DEFINE_HOOK(0x4664fb, BulletClass_Initialize_Ranged, 0x6) +DEFINE_HOOK(0x6fe53f, TechnoClass_Fire_CreateBullet, 0x6) +DEFINE_HOOK(0x468000, BulletClass_GetAnimFrame, 0x6) +DEFINE_HOOK(0x5f4fe7, ObjectClass_Put, 0x8) +DEFINE_HOOK(0x46670f, BulletClass_Update_PreImpactAnim, 0x6) +DEFINE_HOOK(0x46cf3d, CampaignClass_CTOR, 0x5) +DEFINE_HOOK_AGAIN(0x46cc03, CampaignClass_CTOR, 0x5) +DEFINE_HOOK(0x46d0b6, CampaignClass_DTOR, 0x6) +DEFINE_HOOK_AGAIN(0x46cc36, CampaignClass_DTOR, 0x6) +DEFINE_HOOK(0x46cd56, CampaignClass_LoadFromINI, 0x7) +DEFINE_HOOK(0x511635, HouseTypeClass_CTOR_1, 0x5) +DEFINE_HOOK(0x511643, HouseTypeClass_CTOR_2, 0x5) +DEFINE_HOOK(0x5127cf, HouseTypeClass_DTOR, 0x6) +DEFINE_HOOK(0x512480, HouseTypeClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK_AGAIN(0x512290, HouseTypeClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK(0x51246d, HouseTypeClass_Load_Suffix, 0x5) +DEFINE_HOOK(0x51255c, HouseTypeClass_Save_Suffix, 0x5) +DEFINE_HOOK(0x51215a, HouseTypeClass_LoadFromINI, 0x5) +DEFINE_HOOK_AGAIN(0x51214f, HouseTypeClass_LoadFromINI, 0x5) +DEFINE_HOOK(0x553412, LoadProgressMgr_Draw_LSFile, 0x0) +DEFINE_HOOK(0x5536da, LoadProgressMgr_Draw_LSName, 0x0) +DEFINE_HOOK(0x553a05, LoadProgressMgr_Draw_LSSpecialName, 0x6) +DEFINE_HOOK(0x553d06, LoadProgressMgr_Draw_LSBrief, 0x6) +DEFINE_HOOK(0x4e3562, Game_GetFlagSurface, 0x5) +DEFINE_HOOK(0x72b690, LoadScreenPal_Load, 0x0) +DEFINE_HOOK(0x4e38d8, LoadPlayerCountryString, 0x0) +DEFINE_HOOK(0x536438, TauntCommandClass_Execute, 0x5) +DEFINE_HOOK(0x48da3b, sub_48D1E0_PlayTaunt, 0x5) +DEFINE_HOOK(0x752b70, PlayTaunt, 0x5) +DEFINE_HOOK(0x4e3792, HTExt_Unlimit1, 0x0) +DEFINE_HOOK(0x4e3a9c, HTExt_Unlimit2, 0x0) +DEFINE_HOOK(0x4e3f31, HTExt_Unlimit3, 0x0) +DEFINE_HOOK(0x4e412c, HTExt_Unlimit4, 0x0) +DEFINE_HOOK(0x4e41a7, HTExt_Unlimit5, 0x0) +DEFINE_HOOK(0x69b774, HTExt_PickRandom_Human, 0x0) +DEFINE_HOOK(0x69b670, HTExt_PickRandom_AI, 0x0) +DEFINE_HOOK(0x4fe782, HouseClass_AI_BaseConstructionUpdate_PickPowerplant, 0x6) +DEFINE_HOOK(0x4e3a6a, hWnd_PopulateWithCountryNames, 0x6) +DEFINE_HOOK(0x6aa0ca, StripClass_Draw_DrawObserverBackground, 0x6) +DEFINE_HOOK(0x6aa164, StripClass_Draw_DrawObserverFlag, 0x6) +DEFINE_HOOK(0x5d6f61, MPGameModeClass_CreateStartingUnits_BaseCenter, 0x8) +DEFINE_HOOK(0x5d7048, MPGameMode_SpawnBaseUnit_BuildConst, 0x5) +DEFINE_HOOK(0x5d7163, MPGameMode_SpawnStartingUnits_Types, 0x8) +DEFINE_HOOK(0x5d7337, MPGameMode_SpawnStartingUnits_NoInfantry, 0x5) +DEFINE_HOOK(0x5d6d9a, MPGameModeClass_CreateStartingUnits_UnitCost, 0x6) +DEFINE_HOOK(0x4f6532, HouseClass_CTOR, 0x5) +DEFINE_HOOK(0x4f7371, HouseClass_DTOR, 0x6) +DEFINE_HOOK(0x504080, HouseClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK_AGAIN(0x503040, HouseClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK(0x504069, HouseClass_Load_Suffix, 0x7) +DEFINE_HOOK(0x5046de, HouseClass_Save_Suffix, 0x7) +DEFINE_HOOK(0x50114d, HouseClass_InitFromINI, 0x5) +DEFINE_HOOK(0x4fea60, HouseClass_AI_UnitProduction, 0x0) +DEFINE_HOOK(0x4feee0, HouseClass_AI_InfantryProduction, 0x6) +DEFINE_HOOK(0x4ff210, HouseClass_AI_AircraftProduction, 0x6) +DEFINE_HOOK(0x446366, BuildingClass_Place_Academy, 0x6) +DEFINE_HOOK(0x445905, BuildingClass_Remove_Academy, 0x6) +DEFINE_HOOK(0x448ab2, BuildingClass_ChangeOwnership_Remove_Academy, 0x6) +DEFINE_HOOK(0x4491d5, BuildingClass_ChangeOwnership_Add_Academy, 0x6) +DEFINE_HOOK(0x517d51, InfantryClass_Init_Academy, 0x6) +DEFINE_HOOK(0x735678, UnitClass_Init_Academy, 0x6) +DEFINE_HOOK_AGAIN(0x74689b, UnitClass_Init_Academy, 0x6) +DEFINE_HOOK(0x413fd2, AircraftClass_Init_Academy, 0x6) +DEFINE_HOOK(0x442d1b, BuildingClass_Init_Academy, 0x6) +DEFINE_HOOK(0x5054b0, HouseClass_GenerateAIBuildList_EnsureSanity, 0x6) +DEFINE_HOOK(0x5055d8, HouseClass_GenerateAIBuildList_SWAllowed, 0x5) +DEFINE_HOOK(0x505b58, HouseClass_GenerateAIBuildList_SkipManualCopy, 0x6) +DEFINE_HOOK(0x505c34, HouseClass_GenerateAIBuildList_FullAutoCopy, 0x5) +DEFINE_HOOK(0x505c95, HouseClass_GenerateAIBuildList_CountExtra, 0x7) +DEFINE_HOOK(0x505cf1, HouseClass_GenerateAIBuildList_PadWithN1, 0x5) +DEFINE_HOOK(0x5051e0, HouseClass_FirstBuildableFromArray, 0x5) +DEFINE_HOOK(0x50610e, HouseClass_FindPositionForBuilding_FixShipyard, 0x7) +DEFINE_HOOK(0x506306, HouseClass_FindPlaceToBuild_Evaluate, 0x6) +DEFINE_HOOK(0x4f65bf, HouseClass_CanAffordBase, 0x6) +DEFINE_HOOK(0x5d705e, MPGameMode_SpawnBaseUnit_BaseUnit, 0x6) +DEFINE_HOOK(0x688b37, MPGameModeClass_CreateStartingUnits_B, 0x5) +DEFINE_HOOK(0x4f7870, HouseClass_CanBuild, 0x7) +DEFINE_HOOK(0x505360, HouseClass_PrerequisitesForTechnoTypeAreListed, 0x5) +DEFINE_HOOK(0x4f8ebd, HouseClass_Update_HasBeenDefeated, 0x0) +DEFINE_HOOK(0x4f645f, HouseClass_CTOR_FixSideIndices, 0x5) +DEFINE_HOOK(0x500cc5, HouseClass_InitFromINI_FixBufferLimits, 0x6) +DEFINE_HOOK(0x4f62ff, HouseClass_CTOR_FixNameOverflow, 0x6) +DEFINE_HOOK(0x50965e, HouseClass_CanInstantiateTeam, 0x5) +DEFINE_HOOK(0x508ebc, HouseClass_Radar_Update_CheckEligible, 0x6) +DEFINE_HOOK(0x508f91, HouseClass_SpySat_Update_CheckEligible, 0x6) +DEFINE_HOOK(0x4f8b08, HouseClass_Update_DamageDelay, 0x6) +DEFINE_HOOK(0x4f8c23, HouseClass_Update_SilosNeededEVA, 0x5) +DEFINE_HOOK(0x4f9610, HouseClass_GiveTiberium_Storage, 0xa) +DEFINE_HOOK(0x441b30, BuildingClass_Destroy_Refinery, 0x6) +DEFINE_HOOK(0x73e4a2, UnitClass_Mi_Unload_Storage, 0x6) +DEFINE_HOOK(0x522d75, InfantryClass_Slave_UnloadAt_Storage, 0x6) +DEFINE_HOOK(0x508d32, HouseClass_UpdatePower_LocalDrain1, 0x5) +DEFINE_HOOK(0x508d4a, HouseClass_UpdatePower_LocalDrain2, 0x6) +DEFINE_HOOK(0x4fc731, HouseClass_DestroyAll_ReturnStructures, 0x7) +DEFINE_HOOK(0x4f8440, HouseClass_Update_TogglePower, 0x5) +DEFINE_HOOK(0x508c7f, HouseClass_UpdatePower_Auxiliary, 0x6) +DEFINE_HOOK(0x508e66, HouseClass_UpdateRadar_Battery, 0x8) +DEFINE_HOOK(0x4ff563, HouseClass_RegisterTechnoLoss_StatCounters_KeepAlive, 0x6) +DEFINE_HOOK(0x4ff71b, HouseClass_RegisterTechnoGain_StatCounters_KeepAlive, 0x6) +DEFINE_HOOK(0x504796, HouseClass_AddAnger_MultiplayPassive, 0x6) +DEFINE_HOOK(0x6ff860, TechnoClass_Fire_FSW, 0x8) +DEFINE_HOOK_AGAIN(0x6ff008, TechnoClass_Fire_FSW, 0x8) +DEFINE_HOOK(0x4502f4, BuildingClass_Update_Factory, 0x6) +DEFINE_HOOK(0x4ca07a, FactoryClass_AbandonProduction, 0x8) +DEFINE_HOOK(0x444119, BuildingClass_KickOutUnit_UnitType, 0x6) +DEFINE_HOOK(0x444131, BuildingClass_KickOutUnit_InfantryType, 0x6) +DEFINE_HOOK(0x44531f, BuildingClass_KickOutUnit_BuildingType, 0xa) +DEFINE_HOOK(0x443cca, BuildingClass_KickOutUnit_AircraftType, 0xa) +DEFINE_HOOK(0x50b370, HouseClass_ShouldDisableCameo, 0x5) +DEFINE_HOOK(0x509140, HouseClass_Update_Factories_Queues, 0x5) +DEFINE_HOOK(0x6a9822, StripClass_Draw_Power, 0x5) +DEFINE_HOOK(0x6ab312, SidebarClass_ProcessCameoClick_Power, 0x6) +DEFINE_HOOK(0x535db6, SetStructureTabCommandClass_Execute_Power, 0x6) +DEFINE_HOOK(0x535e76, SetDefenseTabCommandClass_Execute_Power, 0x6) +DEFINE_HOOK(0x51e635, InfantryClass_GetActionOnObject_EngineerOverFriendlyBuilding, 0x5) +DEFINE_HOOK(0x519faf, InfantryClass_UpdatePosition_EngineerRepairsFriendly, 0x6) +DEFINE_HOOK(0x51df38, InfantryClass_Remove, 0xa) +DEFINE_HOOK(0x51dffd, InfantryClass_Put, 0x5) +DEFINE_HOOK(0x518434, InfantryClass_ReceiveDamage_SkipDeathAnim, 0x7) +DEFINE_HOOK(0x51d799, InfantryClass_PlayAnim_WaterSound, 0x7) +DEFINE_HOOK(0x51e5bb, InfantryClass_GetActionOnObject_MultiEngineerA, 0x7) +DEFINE_HOOK(0x51e5e1, InfantryClass_GetActionOnObject_MultiEngineerB, 0x7) +DEFINE_HOOK(0x519d9c, InfantryClass_UpdatePosition_MultiEngineer, 0x5) +DEFINE_HOOK(0x51c325, InfantryClass_IsCellOccupied_C4Ability, 0x6) +DEFINE_HOOK(0x51a4d2, InfantryClass_UpdatePosition_C4Ability, 0x6) +DEFINE_HOOK(0x5201cc, InfantryClass_UpdatePanic_ProneWater, 0x6) +DEFINE_HOOK(0x51eb48, InfantryClass_GetActionOnObject_IvanGrinder, 0xa) +DEFINE_HOOK(0x51f628, InfantryClass_Guard_Doggie, 0x5) +DEFINE_HOOK(0x518cb3, InfantryClass_ReceiveDamage_Doggie, 0x6) +DEFINE_HOOK(0x51abd7, InfantryClass_SetDestination_Doggie, 0x6) +DEFINE_HOOK(0x5200c1, InfantryClass_UpdatePanic_Doggie, 0x6) +DEFINE_HOOK(0x5185c8, InfantryClass_ReceiveDamage_InfDeath, 0x6) +DEFINE_HOOK(0x51849a, InfantryClass_ReceiveDamage_DeathAnim, 0x5) +DEFINE_HOOK(0x518575, InfantryClass_ReceiveDamage_InfantryVirus1, 0x6) +DEFINE_HOOK_AGAIN(0x5183de, InfantryClass_ReceiveDamage_InfantryVirus1, 0x6) +DEFINE_HOOK(0x518b93, InfantryClass_ReceiveDamage_Anims, 0x5) +DEFINE_HOOK_AGAIN(0x518821, InfantryClass_ReceiveDamage_Anims, 0x5) +DEFINE_HOOK_AGAIN(0x5187bb, InfantryClass_ReceiveDamage_Anims, 0x5) +DEFINE_HOOK_AGAIN(0x518755, InfantryClass_ReceiveDamage_Anims, 0x5) +DEFINE_HOOK_AGAIN(0x5186f2, InfantryClass_ReceiveDamage_Anims, 0x5) +DEFINE_HOOK_AGAIN(0x518698, InfantryClass_ReceiveDamage_Anims, 0x5) +DEFINE_HOOK(0x51887b, InfantryClass_ReceiveDamage_InfantryVirus2, 0xa) +DEFINE_HOOK(0x518a96, InfantryClass_ReceiveDamage_InfantryMutate, 0x7) +DEFINE_HOOK(0x6e232e, ActionClass_PlayAnimAt, 0x5) +DEFINE_HOOK(0x469c4e, BulletClass_DetonateAt_DamageAnimSelected, 0x5) +DEFINE_HOOK(0x7004ad, TechnoClass_GetActionOnObject_Saboteur, 0x6) +DEFINE_HOOK(0x51ee6b, InfantryClass_GetActionOnObject_Saboteur, 0x6) +DEFINE_HOOK(0x51b2cb, InfantryClass_SetTarget_Saboteur, 0x6) +DEFINE_HOOK(0x519ff8, InfantryClass_UpdatePosition_Saboteur, 0x6) +DEFINE_HOOK(0x62df05, ParticleSystemClass_CTOR, 0x5) +DEFINE_HOOK(0x62e26b, ParticleSystemClass_DTOR, 0x6) +DEFINE_HOOK(0x630090, ParticleSystemClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK_AGAIN(0x62ff20, ParticleSystemClass_SaveLoad_Prefix, 0x7) +DEFINE_HOOK(0x630088, ParticleSystemClass_Load_Suffix, 0x5) +DEFINE_HOOK(0x6300f3, ParticleSystemClass_Save_Suffix, 0x6) +DEFINE_HOOK(0x62fd60, ParticleSystemClass_Update, 0x9) +DEFINE_HOOK(0x62e2ad, ParticleSystemClass_Draw, 0x6) +DEFINE_HOOK(0x62e380, ParticleSystemClass_SpawnParticle, 0xa) +DEFINE_HOOK(0x6d9427, TacticalClass_DrawUnits_ParticleSystems, 0x9) +DEFINE_HOOK(0x72590e, AnnounceInvalidPointer_Particle, 0x9) +DEFINE_HOOK(0x644dbb, ParticleTypeClass_CTOR, 0x5) +DEFINE_HOOK(0x645a3b, ParticleTypeClass_SDDTOR, 0x7) +DEFINE_HOOK(0x6457a0, ParticleTypeClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK_AGAIN(0x645660, ParticleTypeClass_SaveLoad_Prefix, 0x7) +DEFINE_HOOK(0x64578c, ParticleTypeClass_Load_Suffix, 0x5) +DEFINE_HOOK(0x64580a, ParticleTypeClass_Save_Suffix, 0x7) +DEFINE_HOOK(0x645405, ParticleTypeClass_LoadFromINI, 0x5) +DEFINE_HOOK(0x62d015, ParticleClass_Draw_Palette, 0x6) +DEFINE_HOOK(0x62c23d, ParticleClass_Update_Gas_DamageRange, 0x6) +DEFINE_HOOK(0x667a1d, RulesClass_CTOR, 0x5) +DEFINE_HOOK(0x667a30, RulesClass_DTOR, 0x5) +DEFINE_HOOK(0x674730, RulesClass_SaveLoad_Prefix, 0x6) +DEFINE_HOOK_AGAIN(0x675210, RulesClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK(0x678841, RulesClass_Load_Suffix, 0x7) +DEFINE_HOOK(0x675205, RulesClass_Save_Suffix, 0x8) +DEFINE_HOOK(0x6a4ea5, SidebarClass_CTOR_InitCameosList, 0x6) +DEFINE_HOOK(0x6a4fd8, SidebarClass_Load_InitCameosList, 0x6) +DEFINE_HOOK(0x6a61b1, SidebarClass_SetFactoryForObject, 0x0) +DEFINE_HOOK(0x6a63b7, SidebarClass_AddCameo_SkipSizeCheck, 0x0) +DEFINE_HOOK(0x6a8710, StripClass_AddCameo_ReplaceItAll, 0x0) +DEFINE_HOOK(0x6a8d1c, StripClass_MouseMove_GetCameos1, 0x0) +DEFINE_HOOK(0x6a8db5, StripClass_MouseMove_GetCameos2, 0x0) +DEFINE_HOOK(0x6a8f6c, StripClass_MouseMove_GetCameos3, 0x0) +DEFINE_HOOK(0x6a9304, StripClass_GetTip_NoLimit, 0x0) +DEFINE_HOOK(0x6a9747, StripClass_Draw_GetCameo1, 0x0) +DEFINE_HOOK(0x6a9866, StripClass_Draw_Status_1, 0x0) +DEFINE_HOOK(0x6a9886, StripClass_Draw_Status_2, 0x0) +DEFINE_HOOK(0x6a95c8, StripClass_Draw_Status, 0x0) +DEFINE_HOOK(0x6a99be, StripClass_Draw_BreakDrawLoop, 0x5) +DEFINE_HOOK(0x6a9b4f, StripClass_Draw_TestFlashFrame, 0x0) +DEFINE_HOOK(0x6a9eba, StripClass_Draw_Status_3, 0x0) +DEFINE_HOOK(0x6aad2f, SelectClass_ProcessInput_LoadCameoData1, 0x0) +DEFINE_HOOK(0x6ab0b0, SelectClass_ProcessInput_LoadCameo2, 0x0) +DEFINE_HOOK(0x6ab49d, SelectClass_ProcessInput_FixOffset1, 0x0) +DEFINE_HOOK(0x6ab4e8, SelectClass_ProcessInput_FixOffset2, 0x0) +DEFINE_HOOK(0x6ab577, SelectClass_ProcessInput_FixOffset3, 0x0) +DEFINE_HOOK(0x6ab620, SelectClass_ProcessInput_FixOffset4, 0x0) +DEFINE_HOOK(0x6ab741, SelectClass_ProcessInput_FixOffset5, 0x0) +DEFINE_HOOK(0x6ab802, SelectClass_ProcessInput_FixOffset6, 0x0) +DEFINE_HOOK(0x6ab825, SelectClass_ProcessInput_FixOffset7, 0x0) +DEFINE_HOOK(0x6ab920, SelectClass_ProcessInput_FixOffset8, 0x0) +DEFINE_HOOK(0x6ab92f, SelectClass_ProcessInput_FixOffset9, 0x0) +DEFINE_HOOK(0x6abbcb, StripClass_AbandonCameosFromFactory_GetPointer1, 0x0) +DEFINE_HOOK(0x6ac6d9, SidebarClass_FlashCameo, 0x0) +DEFINE_HOOK(0x6aa600, StripClass_RecheckCameos, 0x0) +DEFINE_HOOK(0x668bf0, RulesClass_Addition, 0x5) +DEFINE_HOOK(0x679a15, RulesData_LoadBeforeTypeData, 0x6) +DEFINE_HOOK(0x679caf, RulesData_LoadAfterTypeData, 0x5) +DEFINE_HOOK(0x518744, InfantryClass_ReceiveDamage_ElectricDeath, 0x6) +DEFINE_HOOK(0x48a2d9, DamageArea_ExplodesThreshold, 0x6) +DEFINE_HOOK(0x66748a, RulesClass_CTOR_TiberiumTransmogrify, 0x6) +DEFINE_HOOK(0x48248d, CellClass_CrateBeingCollected_MoneyRandom, 0x6) +DEFINE_HOOK(0x4b769b, ScenarioClass_GenerateDropshipLoadout, 0x5) +DEFINE_HOOK(0x4b99a2, DropshipLoadout_WriteUnit, 0x0) +DEFINE_HOOK(0x4b93bd, ScenarioClass_GenerateDropshipLoadout_FreeAnims, 0x0) +DEFINE_HOOK(0x6a64c9, SidebarClass_AddCameo_Strip, 0x6) +DEFINE_HOOK(0x6a75b9, SidebarClass_SetActiveTab_Strip1, 0x6) +DEFINE_HOOK(0x6a7619, SidebarClass_SetActiveTab_Strip2, 0x6) +DEFINE_HOOK(0x6a793f, SidebarClass_Update_Strip1, 0x6) +DEFINE_HOOK(0x6a79a0, SidebarClass_Update_Strip2, 0x6) +DEFINE_HOOK(0x6a7eee, sub_6A7D70_Strip1, 0x6) +DEFINE_HOOK(0x6a801c, sub_6A7D70_Strip2, 0x6) +DEFINE_HOOK(0x6a8220, StripClass_Initialize, 0x7) +DEFINE_HOOK(0x6a8330, StripClass_EnableInput, 0x5) +DEFINE_HOOK(0x6a83e0, StripClass_DisableInput, 0x6) +DEFINE_HOOK(0x6a93f0, StripClass_Activate, 0x6) +DEFINE_HOOK(0x6a94b0, StripClass_Deactivate, 0x6) +DEFINE_HOOK(0x6a96d9, StripClass_Draw_Strip, 0x7) +DEFINE_HOOK(0x6abf44, sub_6ABD30_Strip1, 0x5) +DEFINE_HOOK(0x6abfb2, sub_6ABD30_Strip2, 0x6) +DEFINE_HOOK(0x6ac02f, sub_6ABD30_Strip3, 0x8) +DEFINE_HOOK(0x6a4609, SideClass_CTOR, 0x7) +DEFINE_HOOK(0x6a499f, SideClass_SDDTOR, 0x6) +DEFINE_HOOK(0x6a48a0, SideClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK_AGAIN(0x6a4780, SideClass_SaveLoad_Prefix, 0x6) +DEFINE_HOOK(0x6a488b, SideClass_Load_Suffix, 0x6) +DEFINE_HOOK(0x6a48fc, SideClass_Save_Suffix, 0x5) +DEFINE_HOOK(0x679a10, SideClass_LoadAllFromINI, 0x5) +DEFINE_HOOK(0x6873ab, INIClass_ReadScenario_EarlyLoadRules, 0x5) +DEFINE_HOOK(0x4f8c97, HouseClass_Update_BuildConst, 0x6) +DEFINE_HOOK(0x4f8f54, HouseClass_Update_SlaveMinerCheck, 0x6) +DEFINE_HOOK(0x507dba, HouseClass_BaseDefenses, 0x6) +DEFINE_HOOK_AGAIN(0x507faa, HouseClass_BaseDefenses, 0x6) +DEFINE_HOOK_AGAIN(0x507bca, HouseClass_BaseDefenses, 0x6) +DEFINE_HOOK(0x52267d, InfantryClass_GetDisguise_Disguise, 0x6) +DEFINE_HOOK(0x6f422f, Sides_Disguise, 0x6) +DEFINE_HOOK_AGAIN(0x5227a3, Sides_Disguise, 0x6) +DEFINE_HOOK(0x642b36, ProgressScreenClass_GetLoadTextColor, 0x5) +DEFINE_HOOK(0x643bb9, ProgressScreenClass_UpdateSingleProgressBar, 0x5) +DEFINE_HOOK(0x642b91, ProgressScreenClass_GetSideColor, 0x5) +DEFINE_HOOK(0x6847b7, ScenarioClass_PrepareMapAndUDP, 0x6) +DEFINE_HOOK(0x686d7f, INIClass_ReadScenario_CacheSP, 0x6) +DEFINE_HOOK(0x553e54, LoadProgressMgr_Draw_SkipShadowOnNullString, 0x6) +DEFINE_HOOK(0x553820, LoadProgressMgr_Draw_SkipShadowOnNullString2, 0x5) +DEFINE_HOOK(0x55403d, LoadProgressMgr_Draw_SkipShadowOnNullString3, 0x6) +DEFINE_HOOK(0x534fb1, Sides_MixFileIndex, 0x5) +DEFINE_HOOK(0x72fa1a, Sides_MixFileYuriFiles1, 0x7) +DEFINE_HOOK(0x72f370, Sides_MixFileYuriFiles2, 0x7) +DEFINE_HOOK(0x72fbc0, Sides_MixFileYuriFiles3, 0x5) +DEFINE_HOOK(0x67d315, SaveGame_EarlySaveSides, 0x5) +DEFINE_HOOK(0x67e09a, SaveGame_LateSkipSides, 0x5) +DEFINE_HOOK(0x67e74a, LoadGame_EarlyLoadSides, 0x5) +DEFINE_HOOK(0x67f281, LoadGame_LateSkipSides, 0x7) +DEFINE_HOOK(0x41e893, AITriggerTypeClass_ConditionMet_SideIndex, 0x0) +DEFINE_HOOK(0x7534e0, VoxClass_SetEVAIndex, 0x5) +DEFINE_HOOK(0x6de0d3, TActionClass_Execute_MessageColor, 0x6) +DEFINE_HOOK(0x72f440, Game_InitializeToolTipColor, 0xa) +DEFINE_HOOK(0x72d300, Game_LoadCampaignScoreAssets, 0x5) +DEFINE_HOOK(0x72d730, Game_LoadMultiplayerScoreAssets, 0x5) +DEFINE_HOOK(0x5ca110, Game_GetMultiplayerScoreScreenBar, 0x5) +DEFINE_HOOK(0x53534c, Game_LoadUI_LoadSideData, 0x7) +DEFINE_HOOK(0x6d4e79, TacticalClass_DrawOverlay_GraphicalText, 0x6) +DEFINE_HOOK(0x622223, sub_621E90_DialogBackground, 0x6) +DEFINE_HOOK(0x5c9b75, Global_DrawScoreScreen_ScoreTheme, 0x5) +DEFINE_HOOK(0x6c922c, ScoreDialog_Handle_ScoreThemeA, 0x5) +DEFINE_HOOK(0x6c935c, ScoreDialog_Handle_ScoreThemeB, 0x5) +DEFINE_HOOK(0x683c70, sub_683AB0_LoadingScoreA, 0x7) +DEFINE_HOOK(0x683d05, sub_683AB0_LoadingScoreB, 0x5) +DEFINE_HOOK(0x6ce6f6, SuperWeaponTypeClass_CTOR, 0x5) +DEFINE_HOOK(0x6cefe0, SuperWeaponTypeClass_SDDTOR, 0x8) +DEFINE_HOOK(0x6ce8d0, SuperWeaponTypeClass_SaveLoad_Prefix, 0x8) +DEFINE_HOOK_AGAIN(0x6ce800, SuperWeaponTypeClass_SaveLoad_Prefix, 0xa) +DEFINE_HOOK(0x6ce8be, SuperWeaponTypeClass_Load_Suffix, 0x7) +DEFINE_HOOK(0x6ce8ea, SuperWeaponTypeClass_Save_Suffix, 0x3) +DEFINE_HOOK(0x6cee50, SuperWeaponTypeClass_LoadFromINI, 0xa) +DEFINE_HOOK_AGAIN(0x6cee43, SuperWeaponTypeClass_LoadFromINI, 0xa) +DEFINE_HOOK(0x6cef84, SuperWeaponTypeClass_GetAction, 0x7) +DEFINE_HOOK(0x653b3a, RadarClass_GetMouseAction_CustomSWAction, 0x5) +DEFINE_HOOK(0x6aaedf, SidebarClass_ProcessCameoClick_SuperWeapons, 0x6) +DEFINE_HOOK(0x6aaf9d, SidebarClass_ProcessCameoClick_SelectTarget, 0x5) +DEFINE_HOOK(0x6a932b, StripClass_GetTip_MoneySW, 0x6) +DEFINE_HOOK(0x6cee96, SuperWeaponTypeClass_FindIndex, 0x5) +DEFINE_HOOK(0x4ac20c, DisplayClass_LeftMouseButtonUp, 0x7) +DEFINE_HOOK(0x4463f0, BuildingClass_Place_SuperWeaponAnimsA, 0x6) +DEFINE_HOOK(0x44656d, BuildingClass_Place_SuperWeaponAnimsB, 0x6) +DEFINE_HOOK(0x450f9e, BuildingClass_ProcessAnims_SuperWeaponsA, 0x6) +DEFINE_HOOK(0x451132, BuildingClass_ProcessAnims_SuperWeaponsB, 0x6) +DEFINE_HOOK(0x4468f4, BuildingClass_Place_AnnounceSW, 0x6) +DEFINE_HOOK(0x6cbdd7, SuperClass_AnnounceReady, 0x6) +DEFINE_HOOK(0x6cc0ea, SuperClass_AnnounceQuantity, 0x9) +DEFINE_HOOK(0x50cfaa, HouseClass_PickOffensiveSWTarget, 0x0) +DEFINE_HOOK(0x6cc390, SuperClass_Launch, 0x6) +DEFINE_HOOK(0x457630, BuildingClass_SWAvailable, 0x9) +DEFINE_HOOK(0x457690, BuildingClass_SW2Available, 0x9) +DEFINE_HOOK(0x43be50, BuildingClass_DTOR_HasAnySW, 0x6) +DEFINE_HOOK(0x449716, BuildingClass_Mi_Guard_HasFirstSW, 0x6) +DEFINE_HOOK(0x4fae72, HouseClass_SWFire_PreDependent, 0x6) +DEFINE_HOOK(0x6cc2b0, SuperClass_NameReadiness, 0x5) +DEFINE_HOOK(0x6a99b7, StripClass_Draw_SuperDarken, 0x5) +DEFINE_HOOK(0x4f9004, HouseClass_Update_TrySWFire, 0x7) +DEFINE_HOOK(0x4faf2a, HouseClass_SWDefendAgainst_Aborted, 0x8) +DEFINE_HOOK(0x6cbf5b, SuperClass_GetCameoChargeStage_ChargeDrainRatio, 0x9) +DEFINE_HOOK(0x6cc053, SuperClass_GetCameoChargeStage_FixFullyCharged, 0x5) +DEFINE_HOOK(0x6cbd86, SuperClass_Progress_Charged, 0x7) +DEFINE_HOOK(0x6cb7b0, SuperClass_Lose, 0x6) +DEFINE_HOOK(0x6cb920, SuperClass_ClickFire, 0x5) +DEFINE_HOOK(0x6cb4d0, SuperClass_SetOnHold, 0x6) +DEFINE_HOOK(0x6cbd6b, SuperClass_Update_DrainMoney, 0x8) +DEFINE_HOOK(0x6cbcde, SuperClass_Update_Animation, 0x5) +DEFINE_HOOK(0x6ceeb0, SuperWeaponTypeClass_FindFirstOfAction, 0x8) +DEFINE_HOOK(0x6d49d1, TacticalClass_Draw_TimerVisibility, 0x5) +DEFINE_HOOK(0x6cb70c, SuperClass_Grant_InitialReady, 0xa) +DEFINE_HOOK(0x53b080, PsyDom_Fire, 0x5) +DEFINE_HOOK(0x53c280, ScenarioClass_UpdateLighting, 0x5) +DEFINE_HOOK(0x555e50, LightConvertClass_CTOR_Lighting, 0x5) +DEFINE_HOOK(0x53af40, PsyDom_Update, 0x6) +DEFINE_HOOK(0x539eb0, LightningStorm_Start, 0x5) +DEFINE_HOOK(0x53a6cf, LightningStorm_Update, 0x7) +DEFINE_HOOK(0x53a140, LightningStorm_Strike, 0x7) +DEFINE_HOOK(0x53a300, LightningStorm_Strike2, 0x5) +DEFINE_HOOK(0x48a59a, MapClass_SelectDamageAnimation_LightningWarhead, 0x5) +DEFINE_HOOK(0x44c9ff, BuildingClass_Mi_Missile_PsiWarn, 0x6) +DEFINE_HOOK(0x44cb4c, BuildingClass_Mi_Missile_NukeTakeOff, 0x7) +DEFINE_HOOK(0x44cce7, BuildingClass_Mi_Missile_GenericSW, 0x6) +DEFINE_HOOK(0x44ce46, BuildingClass_Mi_Missile_Pulsball, 0x5) +DEFINE_HOOK(0x46b371, BulletClass_NukeMaker, 0x5) +DEFINE_HOOK(0x46b423, BulletClass_NukeMaker_PropagateSW, 0x6) +DEFINE_HOOK(0x467e59, BulletClass_Update_NukeBall, 0x5) +DEFINE_HOOK(0x7187da, TeleportLocomotionClass_Unwarp_PreventSelfCrush, 0x6) +DEFINE_HOOK(0x7188f2, TeleportLocomotionClass_Unwarp_SinkJumpJets, 0x7) +DEFINE_HOOK(0x446aaf, BuildingClass_Place_SkipFreeUnits, 0x6) +DEFINE_HOOK(0x5098f0, HouseClass_Update_AI_TryFireSW, 0x5) +DEFINE_HOOK(0x4c78d6, Networking_RespondToEvent_SpecialPlace, 0x8) +DEFINE_HOOK(0x50af10, HouseClass_UpdateSuperWeaponsOwned, 0x5) +DEFINE_HOOK(0x50b1d0, HouseClass_UpdateSuperWeaponsUnavailable, 0x6) +DEFINE_HOOK(0x6dd8d7, TActionClass_Execute, 0xa) +DEFINE_HOOK(0x6e3ee0, TActionClass_GetFlags, 0x5) +DEFINE_HOOK(0x6e3b60, TActionClass_GetMode, 0x8) +DEFINE_HOOK(0x6e1780, TActionClass_PlayAudioAtRandomWP, 0x6) +DEFINE_HOOK(0x6e9443, TeamClass_Update, 0x8) +DEFINE_HOOK(0x6eb432, TeamClass_AttackedBy_Retaliate, 0x9) +DEFINE_HOOK(0x65dbb3, TeamTypeClass_CreateInstance_Plane, 0x5) +DEFINE_HOOK(0x6efb69, TeamClass_GatherAtFriendlyBase_Distance, 0x6) +DEFINE_HOOK(0x6ef8a1, TeamClass_GatherAtEnemyBase_Distance, 0x6) +DEFINE_HOOK(0x6efc70, TeamClass_IronCurtain, 0x5) +DEFINE_HOOK(0x711835, TechnoTypeClass_CTOR, 0x5) +DEFINE_HOOK(0x711ae0, TechnoTypeClass_DTOR, 0x5) +DEFINE_HOOK(0x716dc0, TechnoTypeClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK_AGAIN(0x7162f0, TechnoTypeClass_SaveLoad_Prefix, 0x6) +DEFINE_HOOK(0x716dac, TechnoTypeClass_Load_Suffix, 0xa) +DEFINE_HOOK(0x717094, TechnoTypeClass_Save_Suffix, 0x5) +DEFINE_HOOK(0x716132, TechnoTypeClass_LoadFromINI, 0x5) +DEFINE_HOOK_AGAIN(0x716123, TechnoTypeClass_LoadFromINI, 0x5) +DEFINE_HOOK(0x679caf, RulesClass_LoadAfterTypeData_CompleteInitialization, 0x5) +DEFINE_HOOK(0x5f9634, ObjectTypeClass_LoadFromINI, 0x6) +DEFINE_HOOK(0x5f9070, ObjectTypeClass_Load2DArt, 0x0) +DEFINE_HOOK(0x5f96b0, ObjectTypeClass_TheaterSpecificID, 0x6) +DEFINE_HOOK(0x5f8277, ObjectTypeClass_Load3DArt_NoSpawnAlt1, 0x7) +DEFINE_HOOK(0x5f848c, ObjectTypeClass_Load3DArt_NoSpawnAlt2, 0x6) +DEFINE_HOOK(0x73b6e3, UnitClass_DrawVXL_NoSpawnAlt, 0x6) +DEFINE_HOOK(0x732d47, TacticalClass_CollectSelectedIDs, 0x5) +DEFINE_HOOK(0x7327aa, TechnoClass_PlayerOwnedAliveAndNamed_GroupAs, 0x8) +DEFINE_HOOK(0x4abd9d, DisplayClass_LeftMouseButtonUp_GroupAs, 0xa) +DEFINE_HOOK_AGAIN(0x4abe58, DisplayClass_LeftMouseButtonUp_GroupAs, 0xa) +DEFINE_HOOK_AGAIN(0x4abd6c, DisplayClass_LeftMouseButtonUp_GroupAs, 0xa) +DEFINE_HOOK(0x6da665, sub_6DA5C0_GroupAs, 0xa) +DEFINE_HOOK(0x715320, TechnoTypeClass_LoadFromINI_EarlyReader, 0x6) +DEFINE_HOOK(0x5f7900, ObjectTypeClass_FindFactory, 0x5) +DEFINE_HOOK(0x4444b3, BuildingClass_KickOutUnit_NoAlternateKickout, 0x6) +DEFINE_HOOK(0x4444e2, BuildingClass_KickOutUnit_FindAlternateKickout, 0x6) +DEFINE_HOOK(0x444dbc, BuildingClass_KickOutUnit_Infantry, 0x5) +DEFINE_HOOK(0x4445f6, BuildingClass_KickOutUnit_Clone_NonNavalUnit, 0x5) +DEFINE_HOOK(0x44441a, BuildingClass_KickOutUnit_Clone_NavalUnit, 0x6) +DEFINE_HOOK(0x4449df, BuildingClass_KickOutUnit_PreventClone, 0x6) +DEFINE_HOOK(0x455da0, BuildingClass_IsFactory_CloningFacility, 0x6) +DEFINE_HOOK(0x50beb0, HouseClass_GetCostMult, 0x6) +DEFINE_HOOK(0x711ee0, TechnoTypeClass_GetBuildSpeed, 0x6) +DEFINE_HOOK(0x6f47a0, TechnoClass_GetBuildTime, 0x5) +DEFINE_HOOK(0x6ab8bb, SelectClass_ProcessInput_BuildTime, 0x6) +DEFINE_HOOK(0x731e08, sub_731D90_FakeOf, 0x6) +DEFINE_HOOK(0x71136f, TechnoTypeClass_CTOR_Initialize, 0x6) +DEFINE_HOOK(0x523932, InfantryTypeClass_CTOR_Initialize, 0x8) +DEFINE_HOOK(0x45e416, BuildingTypeClass_CTOR_Initialize, 0x6) +DEFINE_HOOK(0x7128c0, TechnoTypeClass_LoadFromINI_Weapons1, 0x6) +DEFINE_HOOK(0x715b1f, TechnoTypeClass_LoadFromINI_Weapons2, 0x6) +DEFINE_HOOK(0x7177c0, TechnoTypeClass_GetWeapon, 0xb) +DEFINE_HOOK(0x7177e0, TechnoTypeClass_GetEliteWeapon, 0xb) +DEFINE_HOOK(0x70dc70, TechnoClass_SwitchGunner, 0x6) +DEFINE_HOOK(0x717890, TechnoTypeClass_SetWeaponTurretIndex, 0x8) +DEFINE_HOOK(0x7178b0, TechnoTypeClass_GetWeaponTurretIndex, 0xb) +DEFINE_HOOK(0x5f865f, ObjectTypeClass_Load3DArt_Turrets, 0x6) +DEFINE_HOOK(0x5f887b, ObjectTypeClass_Load3DArt_Barrels, 0x6) +DEFINE_HOOK(0x5f8084, ObjectTypeClass_UnloadTurretArt, 0x6) +DEFINE_HOOK(0x73b90e, UnitClass_DrawVXL_Barrels1, 0x7) +DEFINE_HOOK(0x73bccd, UnitClass_DrawVXL_Barrels2, 0x7) +DEFINE_HOOK(0x73bd15, UnitClass_DrawVXL_Turrets, 0x7) +DEFINE_HOOK(0x73bd6a, UnitClass_DrawVXL_Barrels3, 0x7) +DEFINE_HOOK(0x6f3260, TechnoClass_CTOR, 0x5) +DEFINE_HOOK(0x6f4500, TechnoClass_DTOR, 0x5) +DEFINE_HOOK(0x70c250, TechnoClass_SaveLoad_Prefix, 0x8) +DEFINE_HOOK_AGAIN(0x70bf50, TechnoClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK(0x70c249, TechnoClass_Load_Suffix, 0x5) +DEFINE_HOOK(0x70c264, TechnoClass_Save_Suffix, 0x5) +DEFINE_HOOK(0x710415, TechnoClass_AnimPointerExpired, 0x6) +DEFINE_HOOK(0x420960, AlphaShapeClass_CTOR, 0x5) +DEFINE_HOOK(0x420a71, AlphaShapeClass_CTOR_Anims, 0x5) +DEFINE_HOOK(0x421730, AlphaShapeClass_SDDTOR, 0x8) +DEFINE_HOOK(0x421798, AlphaShapeClass_SDDTOR_Anims, 0x6) +DEFINE_HOOK(0x5f3d65, ObjectClass_DTOR, 0x6) +DEFINE_HOOK(0x5f3e70, ObjectClass_Update_AlphaLight, 0x5) +DEFINE_HOOK(0x423b0b, AnimClass_Update_AlphaLight, 0x6) +DEFINE_HOOK(0x420f75, AlphaLightClass_UpdateScreen_ShouldDraw, 0x5) +DEFINE_HOOK(0x4210ac, AlphaLightClass_UpdateScreen_Header, 0x5) +DEFINE_HOOK(0x4211ac, AlphaLightClass_UpdateScreen_Body, 0x8) +DEFINE_HOOK(0x421371, TacticalClass_UpdateAlphasInRectangle_ShouldDraw, 0x5) +DEFINE_HOOK(0x42146e, TacticalClass_UpdateAlphasInRectangle_Header, 0x5) +DEFINE_HOOK(0x42152c, TacticalClass_UpdateAlphasInRectangle_Body, 0x8) +DEFINE_HOOK(0x71944e, TeleportLocomotionClass_ILocomotion_Process, 0x6) +DEFINE_HOOK(0x43fe8e, BuildingClass_Update_Reload, 0x6) +DEFINE_HOOK(0x6fcfa4, TechnoClass_GetROF_BuildingHack, 0x5) +DEFINE_HOOK(0x6fd0bf, TechnoClass_GetROF_AttachEffect, 0x6) +DEFINE_HOOK(0x5200d7, InfantryClass_UpdatePanic_DontReload, 0x6) +DEFINE_HOOK(0x51bcb2, InfantryClass_Update_Reload, 0x6) +DEFINE_HOOK(0x6fca0d, TechnoClass_CanFire_Ammo, 0x6) +DEFINE_HOOK(0x6ff656, TechnoClass_Fire_Ammo, 0xa) +DEFINE_HOOK(0x51df8c, InfantryClass_Fire_Ammo, 0x6) +DEFINE_HOOK(0x7413ff, UnitClass_Fire_Ammo, 0x7) +DEFINE_HOOK(0x6fb05b, TechnoClass_Reload_ReloadAmount, 0x6) +DEFINE_HOOK(0x6f3410, TechnoClass_SelectWeapon_NoAmmoWeapon, 0x5) +DEFINE_HOOK(0x5f5add, ObjectClass_SpawnParachuted_Animation, 0x6) +DEFINE_HOOK(0x73c725, UnitClass_DrawSHP_DrawShadowEarlier, 0x6) +DEFINE_HOOK(0x73c733, UnitClass_DrawSHP_SkipTurretedShadow, 0x7) +DEFINE_HOOK(0x705ff3, TechnoClass_Draw_A_SHP_File_SkipUnitShadow, 0x6) +DEFINE_HOOK(0x73b4a0, UnitClass_DrawVXL_WaterType, 0x9) +DEFINE_HOOK(0x73c5fc, UnitClass_DrawSHP_WaterType, 0x6) +DEFINE_HOOK(0x73c69d, UnitClass_DrawSHP_ChangeType1, 0x6) +DEFINE_HOOK_AGAIN(0x73c702, UnitClass_DrawSHP_ChangeType1, 0x6) +DEFINE_HOOK_AGAIN(0x73c655, UnitClass_DrawSHP_ChangeType1, 0x6) +DEFINE_HOOK(0x415085, AircraftClass_Update_DamageSmoke, 0x7) +DEFINE_HOOK(0x73c613, UnitClass_DrawSHP_FacingsA, 0x7) +DEFINE_HOOK(0x73cd01, UnitClass_DrawSHP_FacingsB, 0x5) +DEFINE_HOOK(0x6ff2d1, TechnoClass_Fire_Facings, 0x6) +DEFINE_HOOK(0x728ef0, TunnelLocomotionClass_ILocomotion_Process_Dig, 0x5) +DEFINE_HOOK(0x7292cf, TunnelLocomotionClass_sub_7291F0_Dig, 0x8) +DEFINE_HOOK(0x7293da, TunnelLocomotionClass_sub_729370_Dig, 0x6) +DEFINE_HOOK(0x7297c4, TunnelLocomotionClass_sub_729580_Dig, 0x6) +DEFINE_HOOK(0x7299a9, TunnelLocomotionClass_sub_7298F0_Dig, 0x5) +DEFINE_HOOK(0x6fb757, TechnoClass_UpdateCloak, 0x8) +DEFINE_HOOK(0x6fbdc0, TechnoClass_ShouldBeCloaked, 0x5) +DEFINE_HOOK(0x6fbc90, TechnoClass_ShouldNotBeCloaked, 0x5) +DEFINE_HOOK(0x70380a, TechnoClass_Cloak_CloakSound, 0x6) +DEFINE_HOOK(0x70375b, TechnoClass_Uncloak_DecloakSound, 0x6) +DEFINE_HOOK(0x4dbdd4, FootClass_IsCloakable_CloakStop, 0x6) +DEFINE_HOOK(0x6f5388, TechnoClass_DrawExtras_Submerged, 0x6) +DEFINE_HOOK(0x7036eb, TechnoClass_Uncloak_CloakingStages, 0x6) +DEFINE_HOOK(0x703a79, TechnoClass_VisualCharacter_CloakingStages, 0xa) +DEFINE_HOOK(0x4d99aa, FootClass_PointerGotInvalid_Parasite, 0x6) +DEFINE_HOOK(0x62a283, ParasiteClass_PointerGotInvalid_Cloak, 0x9) +DEFINE_HOOK(0x4da584, FootClass_Update_RadImmune, 0x7) +DEFINE_HOOK(0x701bfe, TechnoClass_ReceiveDamage_Abilities, 0x6) +DEFINE_HOOK(0x701914, TechnoClass_ReceiveDamage_Damaging, 0x7) +DEFINE_HOOK(0x702819, TechnoClass_ReceiveDamage_Aftermath, 0xa) +DEFINE_HOOK(0x6fc417, TechnoClass_CanFire_PsionicsImmune, 0x6) +DEFINE_HOOK(0x6fc0d3, TechnoClass_CanFire_DisableWeapons, 0x8) +DEFINE_HOOK(0x718275, TeleportLocomotionClass_MakeRoom, 0x9) +DEFINE_HOOK(0x739956, UnitClass_Deploy_Transfer, 0x6) +DEFINE_HOOK(0x44a03c, BuildingClass_Mi_Selling_Transfer, 0x6) +DEFINE_HOOK(0x415544, AircraftClass_Mi_Unload_Blocked, 0xb) +DEFINE_HOOK(0x709d38, TechnoClass_DrawPipscale_Passengers, 0x7) +DEFINE_HOOK(0x73762b, UnitClass_ReceivedRadioCommand_BySize1, 0x6) +DEFINE_HOOK(0x73778f, UnitClass_ReceivedRadioCommand_BySize2, 0x6) +DEFINE_HOOK(0x73782f, UnitClass_ReceivedRadioCommand_BySize3, 0x6) +DEFINE_HOOK(0x737994, UnitClass_ReceivedRadioCommand_BySize4, 0x6) +DEFINE_HOOK(0x73de90, UnitClass_Mi_Unload_SimpleDeployer, 0x6) +DEFINE_HOOK(0x739ada, UnitClass_SimpleDeploy_Height, 0xa) +DEFINE_HOOK(0x739b8a, UnitClass_SimpleDeploy_Facing, 0x6) +DEFINE_HOOK(0x54c767, JumpjetLocomotionClass_State4_54C550_DeployDir, 0x6) +DEFINE_HOOK(0x514a21, HoverLocomotionClass_ILocomotion_Process_DeployToLand, 0x9) +DEFINE_HOOK(0x513eaa, HoverLocomotionClass_UpdateHover_DeployToLand, 0x5) +DEFINE_HOOK(0x514dfe, HoverLocomotionClass_ILocomotion_MoveTo_DeployToLand, 0x7) +DEFINE_HOOK(0x4d9ebd, FootClass_CanBeSold_SellUnit, 0x6) +DEFINE_HOOK(0x4dfe00, FootClass_GarrisonStructure_TakeVehicle, 0x6) +DEFINE_HOOK(0x4566b0, BuildingClass_GetRangeOfRadial_Radius, 0x6) +DEFINE_HOOK(0x41be80, ObjectClass_DrawRadialIndicator, 0x3) +DEFINE_HOOK(0x737f97, UnitClass_ReceiveDamage, 0x0) +DEFINE_HOOK(0x41668b, AircraftClass_ReceiveDamage, 0x6) +DEFINE_HOOK(0x4decae, FootClass_Crash_Spin, 0x5) +DEFINE_HOOK(0x4da8b2, FootClass_Update_AnimRate, 0x6) +DEFINE_HOOK(0x6f9e50, TechnoClass_Update, 0x5) +DEFINE_HOOK(0x7014d5, TechnoClass_ChangeOwnership_RadarJammer, 0x6) +DEFINE_HOOK(0x415ca6, AircraftClass_Paradrop_Units, 0x6) +DEFINE_HOOK(0x415df6, AircraftClass_Paradrop_Carryall, 0x6) +DEFINE_HOOK(0x416cf4, AircraftClass_Carryall_Unload_Guard, 0x5) +DEFINE_HOOK(0x6f407d, TechnoClass_Init_1, 0x6) +DEFINE_HOOK(0x6f4103, TechnoClass_Init_2, 0x6) +DEFINE_HOOK(0x735584, UnitClass_CTOR_TurretROT, 0x6) +DEFINE_HOOK(0x413ffa, AircraftClass_Init_TurretROT, 0x6) +DEFINE_HOOK(0x416c3a, AircraftClass_Carryall_Unload_Facing, 0x5) +DEFINE_HOOK(0x446ee2, BuildingClass_Place_InitialPayload, 0x6) +DEFINE_HOOK(0x4d718c, FootClass_Put_InitialPayload, 0x6) +DEFINE_HOOK(0x71a84e, TemporalClass_UpdateA, 0x5) +DEFINE_HOOK(0x62a020, ParasiteClass_Update, 0xa) +DEFINE_HOOK(0x62a7b1, Parasite_ExitUnit, 0x9) +DEFINE_HOOK(0x629804, ParasiteClass_UpdateSquiddy, 0x9) +DEFINE_HOOK(0x51f76d, InfantryClass_Unload, 0x5) +DEFINE_HOOK(0x51ce9a, InfantryClass_Idle, 0x5) +DEFINE_HOOK(0x747bcf, UnitTypeClass_LoadFromINI_Turrets, 0x5) +DEFINE_HOOK(0x5215f9, InfantryClass_UpdateDeployment_Deso1, 0x6) +DEFINE_HOOK(0x52138c, InfantryClass_UpdateDeployment_Deso2, 0x6) +DEFINE_HOOK(0x7101cf, FootClass_ImbueLocomotor, 0x7) +DEFINE_HOOK(0x4daa68, FootClass_Update_MoveSound, 0x6) +DEFINE_HOOK(0x7090a8, TechnoClass_SelectFiringVoice, 0x0) +DEFINE_HOOK(0x70e2b0, TechnoClass_IronCurtain, 0x5) +DEFINE_HOOK(0x5202f9, InfantryClass_UpdateVehicleThief_Check, 0x6) +DEFINE_HOOK(0x5203f7, InfantryClass_UpdateVehicleThief_Hijack, 0x5) +DEFINE_HOOK(0x51e7bf, InfantryClass_GetActionOnObject_CanCapture, 0x6) +DEFINE_HOOK(0x519675, InfantryClass_UpdatePosition_BeforeInfantrySpecific, 0xa) +DEFINE_HOOK(0x471c96, CaptureManagerClass_CanCapture, 0xa) +DEFINE_HOOK(0x53c450, TechnoClass_CanBePermaMC, 0x5) +DEFINE_HOOK(0x73758a, UnitClass_ReceivedRadioCommand_QueryEnterAsPassenger_KillDriver, 0x6) +DEFINE_HOOK(0x41946b, AircraftClass_ReceivedRadioCommand_QueryEnterAsPassenger_KillDriver, 0x6) +DEFINE_HOOK(0x6f6a58, TechnoClass_DrawHealthBar_HidePips_KillDriver, 0x6) +DEFINE_HOOK(0x7087eb, TechnoClass_ShouldRetaliate_KillDriver, 0x6) +DEFINE_HOOK(0x7091d6, TechnoClass_CanPassiveAquire_KillDriver, 0x6) +DEFINE_HOOK(0x6f3283, TechnoClass_CanScatter_KillDriver, 0x8) +DEFINE_HOOK(0x5198ad, InfantryClass_UpdatePosition_EnteredGrinder, 0x6) +DEFINE_HOOK(0x73a1bc, UnitClass_UpdatePosition_EnteredGrinder, 0x7) +DEFINE_HOOK(0x6f6ac9, TechnoClass_Remove, 0x6) +DEFINE_HOOK(0x74642c, UnitClass_ReceiveGunner, 0x6) +DEFINE_HOOK(0x74653c, UnitClass_RemoveGunner, 0x0) +DEFINE_HOOK(0x741206, UnitClass_CanFire, 0x6) +DEFINE_HOOK(0x417d75, AircraftClass_GetActionOnObject_CanTote, 0x5) +DEFINE_HOOK(0x416e37, AircraftClass_Mi_MoveCarryall_CanTote, 0x5) +DEFINE_HOOK(0x416c4d, AircraftClass_Carryall_Unload_DestroyCargo, 0x5) +DEFINE_HOOK(0x416c94, AircraftClass_Carryall_Unload_UpdateCargo, 0x6) +DEFINE_HOOK(0x4d9a83, FootClass_PointerGotInvalid_OccupierVehicleThief, 0x6) +DEFINE_HOOK(0x7441b6, UnitClass_MarkOccupationBits, 0x6) +DEFINE_HOOK(0x744216, UnitClass_UnmarkOccupationBits, 0x6) +DEFINE_HOOK(0x70deba, TechnoClass_UpdateGattling_Cycle, 0x6) +DEFINE_HOOK(0x746b89, UnitClass_GetUIName, 0x8) +DEFINE_HOOK(0x746c55, UnitClass_GetUIName_Space, 0x6) +DEFINE_HOOK(0x702216, TechnoClass_ReceiveDamage_TiberiumHeal, 0x6) +DEFINE_HOOK(0x4d85e4, FootClass_UpdatePosition_TiberiumDamage, 0x9) +DEFINE_HOOK(0x702200, TechnoClass_ReceiveDamage_SpillTiberium, 0x6) +DEFINE_HOOK(0x738749, UnitClass_Destroy_TiberiumExplosive, 0x6) +DEFINE_HOOK(0x739f21, UnitClass_UpdatePosition_Visceroid, 0x6) +DEFINE_HOOK(0x702050, TechnoClass_ReceiveDamage_SuppressUnitLost, 0x6) +DEFINE_HOOK(0x702185, TechnoClass_ReceiveDamage_OverrideVoiceDie, 0x6) +DEFINE_HOOK(0x7021f5, TechnoClass_ReceiveDamage_OverrideDieSound, 0x6) +DEFINE_HOOK(0x71a917, TemporalClass_Update_Erase, 0x5) +DEFINE_HOOK(0x4d98c0, FootClass_Destroyed, 0xa) +DEFINE_HOOK(0x732c30, TechnoClass_IDMatches, 0x5) +DEFINE_HOOK(0x702a38, TechnoClass_ReceiveDamage_OpenTopped, 0x7) +DEFINE_HOOK(0x707b19, TechnoClass_PointerGotInvalid_SpawnCloakOwner, 0x6) +DEFINE_HOOK(0x414338, AircraftClass_Put_SpawnHigh, 0x6) +DEFINE_HOOK(0x6b783b, SpawnManagerClass_Update_SpawnHigh, 0x5) +DEFINE_HOOK(0x41d940, AirstrikeClass_Fire_AirstrikeAttackVoice, 0x5) +DEFINE_HOOK(0x41d5ae, AirstrikeClass_PointerGotInvalid_AirstrikeAbortSound, 0x9) +DEFINE_HOOK(0x702cfe, TechnoClass_ReceiveDamage_PreventScatter, 0x6) +DEFINE_HOOK(0x6f826e, TechnoClass_CanAutoTargetObject_CivilianEnemy, 0x5) +DEFINE_HOOK(0x5240bd, InfantryTypeClass_LoadFromINI_DamageSparks, 0x7) +DEFINE_HOOK(0x6facd9, TechnoClass_Update_DamageSparks, 0x6) +DEFINE_HOOK(0x702894, TechnoClass_ReceiveDamage_SmokeParticles, 0x6) +DEFINE_HOOK(0x6fad49, TechnoClass_Update_SparkParticles, 0x0) +DEFINE_HOOK(0x6ff28f, TechnoClass_Fire_BerserkROFMultiplier, 0x6) +DEFINE_HOOK(0x6fe31c, TechnoClass_Fire_AllowDamage, 0x8) +DEFINE_HOOK(0x6f526c, TechnoClass_DrawExtras_PowerOff, 0x5) +DEFINE_HOOK(0x741613, UnitClass_ApproachTarget_OmniCrusher, 0x6) +DEFINE_HOOK(0x7418aa, UnitClass_CrushCell_CrushDamage, 0x6) +DEFINE_HOOK(0x4d9920, FootClass_SelectAutoTarget_Cloaked, 0x9) +DEFINE_HOOK(0x70be80, TechnoClass_ShouldSelfHealOneStep, 0x5) +DEFINE_HOOK(0x6fa743, TechnoClass_Update_SelfHeal, 0xa) +DEFINE_HOOK(0x7162b0, TechnoTypeClass_GetPipMax_MindControl, 0x6) +DEFINE_HOOK(0x73769e, UnitClass_ReceivedRadioCommand_SpecificPassengers, 0x8) +DEFINE_HOOK(0x41949f, AircraftClass_ReceivedRadioCommand_SpecificPassengers, 0x6) +DEFINE_HOOK(0x417dd2, AircraftClass_GetActionOnObject_NoManualUnload, 0x6) +DEFINE_HOOK(0x740031, UnitClass_GetActionOnObject_NoManualUnload, 0x6) +DEFINE_HOOK(0x700eec, TechnoClass_CanDeploySlashUnload_NoManualUnload, 0x6) +DEFINE_HOOK(0x700536, TechnoClass_GetActionOnObject_NoManualFire, 0x6) +DEFINE_HOOK(0x7008d4, TechnoClass_GetActionOnCell_NoManualFire, 0x6) +DEFINE_HOOK(0x74031a, UnitClass_GetActionOnObject_NoManualEnter, 0x6) +DEFINE_HOOK(0x51e748, InfantryClass_GetActionOnObject_NoSelfGuardArea, 0x8) +DEFINE_HOOK(0x702e64, TechnoClass_RegisterDestruction_Bounty, 0x6) +DEFINE_HOOK(0x5f3fb2, ObjectClass_Update_MaxFallRate, 0x6) +DEFINE_HOOK(0x6f3950, TechnoClass_GetCrewCount, 0x8) +DEFINE_HOOK(0x451330, BuildingClass_GetCrewCount, 0xa) +DEFINE_HOOK(0x707d20, TechnoClass_GetCrew, 0x5) +DEFINE_HOOK(0x44eb10, BuildingClass_GetCrew, 0x9) +DEFINE_HOOK(0x417e16, AircraftClass_GetActionOnObject_Dock, 0x6) +DEFINE_HOOK(0x70055d, TechnoClass_GetActionOnObject_AttackCursor, 0x8) +DEFINE_HOOK(0x700aa8, TechnoClass_GetActionOnCell_AttackCursor, 0x8) +DEFINE_HOOK(0x7000cd, TechnoClass_GetActionOnObject_SelfDeployCursor, 0x6) +DEFINE_HOOK(0x7400f0, UnitClass_GetActionOnObject_SelfDeployCursor_Bunker, 0x6) +DEFINE_HOOK(0x6ffec0, TechnoClass_GetActionOnObject_Cursors, 0x5) +DEFINE_HOOK(0x700600, TechnoClass_GetActionOnCell_Cursors, 0x5) +DEFINE_HOOK(0x447548, BuildingClass_GetActionOnCell_Deactivated, 0x6) +DEFINE_HOOK(0x447218, BuildingClass_GetActionOnObject_Deactivated, 0x6) +DEFINE_HOOK(0x7404b9, UnitClass_GetActionOnCell_Deactivated, 0x6) +DEFINE_HOOK(0x73fd5a, UnitClass_GetActionOnObject_Deactivated, 0x5) +DEFINE_HOOK(0x51f808, InfantryClass_GetActionOnCell_Deactivated, 0x6) +DEFINE_HOOK(0x51e440, InfantryClass_GetActionOnObject_Deactivated, 0x8) +DEFINE_HOOK(0x417f83, AircraftClass_GetActionOnCell_Deactivated, 0x6) +DEFINE_HOOK(0x417ccb, AircraftClass_GetActionOnObject_Deactivated, 0x5) +DEFINE_HOOK(0x4d74ec, FootClass_ActionOnObject_Deactivated, 0x6) +DEFINE_HOOK(0x4d7d58, FootClass_ActionOnCell_Deactivated, 0x6) +DEFINE_HOOK(0x4436f7, BuildingClass_ActionOnCell_Deactivated, 0x5) +DEFINE_HOOK(0x5200b3, InfantryClass_UpdatePanic, 0x6) +DEFINE_HOOK(0x51d0dd, InfantryClass_Scatter, 0x6) +DEFINE_HOOK(0x73dbf9, UnitClass_Mi_Unload_Decactivated, 0x5) +DEFINE_HOOK(0x736135, UnitClass_Update_Deactivated, 0x6) +DEFINE_HOOK(0x73c143, UnitClass_DrawVXL_Deactivated, 0x5) +DEFINE_HOOK(0x70fbe0, TechnoClass_Activate, 0x6) +DEFINE_HOOK(0x70fc90, TechnoClass_Deactivate, 0x6) +DEFINE_HOOK(0x6f7631, TechnoClass_IsCloseEnoughToTarget_Obstacle, 0x6) +DEFINE_HOOK_AGAIN(0x6f7511, TechnoClass_IsCloseEnoughToTarget_Obstacle, 0x6) +DEFINE_HOOK(0x4a76ed, DiskLaserClass_Update_Anim, 0x7) +DEFINE_HOOK(0x70cbb0, TechnoClass_DealParticleDamage_AmbientDamage, 0x6) +DEFINE_HOOK(0x6ff1fb, TechnoClass_Fire_DetachedRailgun, 0x6) +DEFINE_HOOK(0x6ff26e, TechnoClass_Fire_DetachedRailgun2, 0x6) +DEFINE_HOOK(0x65731f, RadarClass_UpdateMinimap_Lock, 0x6) +DEFINE_HOOK(0x65757c, RadarClass_UpdateMinimap_Unlock, 0x8) +DEFINE_HOOK(0x657cf2, MapClass_MinimapChanged_Lock1, 0x6) +DEFINE_HOOK(0x657d35, MapClass_MinimapChanged_Unlock1, 0x7) +DEFINE_HOOK(0x657d3d, MapClass_MinimapChanged_Lock2, 0x6) +DEFINE_HOOK(0x657d8a, MapClass_MinimapChanged_Unlock2, 0x7) +DEFINE_HOOK(0x6fb306, TechnoClass_CreateGap_Optimize, 0x6) +DEFINE_HOOK(0x6fb5f0, TechnoClass_DeleteGap_Optimize, 0x6) +DEFINE_HOOK(0x44e2b0, BuildingClass_Mi_Unload_LargeGap, 0x6) +DEFINE_HOOK(0x454bdc, BuildingClass_UpdatePowered_LargeGap, 0x7) +DEFINE_HOOK(0x4566d5, BuildingClass_GetRangeOfRadial_LargeGap, 0x6) +DEFINE_HOOK(0x6fb4a3, TechnoClass_CreateGap_LargeGap, 0x7) +DEFINE_HOOK_AGAIN(0x6fb1b5, TechnoClass_CreateGap_LargeGap, 0x7) +DEFINE_HOOK(0x73e66d, UnitClass_Mi_Harvest_SkipDock, 0x6) +DEFINE_HOOK(0x73e772, UnitClass_Mi_Harvest_LongScan, 0x6) +DEFINE_HOOK_AGAIN(0x73e851, UnitClass_Mi_Harvest_LongScan, 0x6) +DEFINE_HOOK(0x73eac6, UnitClass_Mi_Harvest_ShortScan, 0x6) +DEFINE_HOOK_AGAIN(0x73eaa6, UnitClass_Mi_Harvest_ShortScan, 0x6) +DEFINE_HOOK_AGAIN(0x73ea17, UnitClass_Mi_Harvest_ShortScan, 0x6) +DEFINE_HOOK_AGAIN(0x73e9f1, UnitClass_Mi_Harvest_ShortScan, 0x6) +DEFINE_HOOK(0x73ec0e, UnitClass_Mi_Harvest_TooFarDistance1, 0x6) +DEFINE_HOOK(0x73ee40, UnitClass_Mi_Harvest_TooFarDistance2, 0x6) +DEFINE_HOOK(0x6b026c, SlaveManagerClass_UpdateMiner_ShortScan, 0x6) +DEFINE_HOOK_AGAIN(0x6b006d, SlaveManagerClass_UpdateMiner_ShortScan, 0x6) +DEFINE_HOOK(0x6b1065, SlaveManagerClass_ShouldWakeUp_ShortScan, 0x5) +DEFINE_HOOK(0x6b02cc, SlaveManagerClass_UpdateMiner_LongScan, 0x6) +DEFINE_HOOK_AGAIN(0x6b00bd, SlaveManagerClass_UpdateMiner_LongScan, 0x6) +DEFINE_HOOK_AGAIN(0x6afdfc, SlaveManagerClass_UpdateMiner_LongScan, 0x6) +DEFINE_HOOK(0x6b01a3, SlaveManagerClass_UpdateMiner_ScanCorrection, 0x6) +DEFINE_HOOK(0x6af748, SlaveManagerClass_UpdateSlaves_SlaveScan, 0x6) +DEFINE_HOOK(0x74081f, UnitClass_Mi_Guard_KickFrameDelay, 0x5) +DEFINE_HOOK(0x74410d, UnitClass_Mi_AreaGuard_KickFrameDelay, 0x5) +DEFINE_HOOK(0x4ccb84, FlyLocomotionClass_ILocomotion_Process_HunterSeeker, 0x6) +DEFINE_HOOK(0x4ce85a, FlyLocomotionClass_UpdateLanding, 0x8) +DEFINE_HOOK(0x4cf3d0, FlyLocomotionClass_sub_4CEFB0_HunterSeeker, 0x7) +DEFINE_HOOK(0x4cd9c8, FlyLocomotionClass_sub_4CD600_HunterSeeker_UpdateTarget, 0x6) +DEFINE_HOOK(0x4cde64, FlyLocomotionClass_sub_4CD600_HunterSeeker_Ascent, 0x6) +DEFINE_HOOK(0x4cdf54, FlyLocomotionClass_sub_4CD600_HunterSeeker_Descent, 0x5) +DEFINE_HOOK(0x4cfe80, FlyLocomotionClass_ILocomotion_AcquireHunterSeekerTarget, 0x5) +DEFINE_HOOK(0x4d8d95, FootClass_UpdatePosition_HunterSeeker, 0xa) +DEFINE_HOOK(0x70a990, TechnoClass_DrawVeterancy, 0x5) +DEFINE_HOOK(0x6fa361, TechnoClass_Update_LoseTarget, 0x5) +DEFINE_HOOK(0x6f8f1f, TechnoClass_FindTargetType_Heal, 0x6) +DEFINE_HOOK_AGAIN(0x6f8ee3, TechnoClass_FindTargetType_Heal, 0x6) +DEFINE_HOOK(0x6f7fc5, TechnoClass_CanAutoTargetObject_Heal, 0x7) +DEFINE_HOOK(0x51e710, InfantryClass_GetActionOnObject_Heal, 0x7) +DEFINE_HOOK(0x51c913, InfantryClass_CanFire_Heal, 0x7) +DEFINE_HOOK(0x520731, InfantryClass_UpdateFiringState_Heal, 0x5) +DEFINE_HOOK(0x73fdbd, UnitClass_GetActionOnObject_Heal, 0x5) +DEFINE_HOOK(0x736e8e, UnitClass_UpdateFiringState_Heal, 0x6) +DEFINE_HOOK(0x741113, UnitClass_CanFire_Heal, 0xa) +DEFINE_HOOK(0x4140eb, AircraftClass_DTOR_Prereqs, 0x6) +DEFINE_HOOK(0x517df2, InfantryClass_DTOR_Prereqs, 0x6) +DEFINE_HOOK(0x7357f6, UnitClass_DTOR_Prereqs, 0x6) +DEFINE_HOOK(0x4d7221, FootClass_Put_Prereqs, 0x6) +DEFINE_HOOK(0x6f4a37, TechnoClass_DiscoveredBy_Prereqs, 0x5) +DEFINE_HOOK_AGAIN(0x6f4a1d, TechnoClass_DiscoveredBy_Prereqs, 0x6) +DEFINE_HOOK(0x7015eb, TechnoClass_ChangeOwnership_Prereqs, 0x7) +DEFINE_HOOK(0x5673a0, MapClass_RevealArea0, 0x5) +DEFINE_HOOK(0x5678e0, MapClass_RevealArea1, 0x5) +DEFINE_HOOK(0x567da0, MapClass_RevealArea2, 0x5) +DEFINE_HOOK(0x4360d8, BuildingLightClass_Draw_400, 0x6) +DEFINE_HOOK(0x4360ff, BuildingLightClass_Draw_250, 0x6) +DEFINE_HOOK(0x6f6d0e, TechnoClass_Put_1, 0x7) +DEFINE_HOOK(0x6f6f20, TechnoClass_Put_2, 0x6) +DEFINE_HOOK(0x441163, BuildingClass_Put_DontSpawnSpotlight, 0x0) +DEFINE_HOOK(0x435820, BuildingLightClass_CTOR, 0x6) +DEFINE_HOOK(0x4370c0, BuildingLightClass_SDDTOR, 0xa) +DEFINE_HOOK(0x436459, BuildingLightClass_Update, 0x6) +DEFINE_HOOK(0x436a2d, BuildingLightClass_PointerGotInvalid_OwnerCloak, 0x6) +DEFINE_HOOK(0x435bfa, BuildingLightClass_Draw_Start, 0x6) +DEFINE_HOOK(0x435cd3, BuildingLightClass_Draw_Spotlight, 0x6) +DEFINE_HOOK(0x436072, BuildingLightClass_Draw_430, 0x6) +DEFINE_HOOK(0x702e9d, TechnoClass_RegisterDestruction_Veterancy, 0x6) +DEFINE_HOOK(0x6fa054, TechnoClass_Update_Veterancy, 0x6) +DEFINE_HOOK(0x71ae50, TemporalClass_CanWarpTarget, 0x8) +DEFINE_HOOK(0x71ab10, TemporalClass_GetWarpPerStep, 0x6) +DEFINE_HOOK(0x5f4ff9, ObjectClass_Put_IsFlammable, 0x7) +DEFINE_HOOK(0x71c5d2, TerrainClass_Ignite_IsFlammable, 0x6) +DEFINE_HOOK(0x71c7c2, TerrainClass_Update_ForestFire, 0x6) +DEFINE_HOOK(0x71b99e, TerrainClass_ReceiveDamage_ForestFire, 0x9) +DEFINE_HOOK(0x71e7f8, TEventClass_CTOR, 0x5) +DEFINE_HOOK(0x71faa6, TEventClass_SDDTOR, 0x6) +DEFINE_HOOK(0x71f930, TEventClass_SaveLoad_Prefix, 0x8) +DEFINE_HOOK_AGAIN(0x71f8c0, TEventClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK(0x71f92b, TEventClass_Load_Suffix, 0x5) +DEFINE_HOOK(0x71f94a, TEventClass_Save_Suffix, 0x5) +DEFINE_HOOK(0x71e949, TEventClass_HasOccured, 0x7) +DEFINE_HOOK(0x71f06c, TEventClass_HasOccured_PlayerAtX1, 0x5) +DEFINE_HOOK(0x71ed33, TEventClass_HasOccured_PlayerAtX2, 0x5) +DEFINE_HOOK_AGAIN(0x71f1c9, TEventClass_HasOccured_PlayerAtX2, 0x5) +DEFINE_HOOK_AGAIN(0x71f1ed, TEventClass_HasOccured_PlayerAtX2, 0x5) +DEFINE_HOOK_AGAIN(0x71ed01, TEventClass_HasOccured_PlayerAtX2, 0x5) +DEFINE_HOOK(0x71ee79, TEventClass_HasOccured_PlayerAtX3, 0x9) +DEFINE_HOOK(0x71f683, TEventClass_GetFlags, 0x5) +DEFINE_HOOK(0x71f39b, TEventClass_SaveToINI, 0x5) +DEFINE_HOOK(0x71f9c0, TEventClass_Persistable, 0x6) +DEFINE_HOOK(0x4368c9, BuildingLightClass_Update_Trigger, 0x5) +DEFINE_HOOK(0x5f57b5, ObjectClass_ReceiveDamage_Trigger, 0x6) +DEFINE_HOOK(0x7032b0, TechnoClass_RegisterLoss_Trigger, 0x6) +DEFINE_HOOK(0x702dd6, TechnoClass_RegisterDestruction_Trigger, 0x6) +DEFINE_HOOK(0x744745, UnitClass_RegisterDestruction_Trigger, 0x5) +DEFINE_HOOK(0x721876, TiberiumClass_CTOR, 0x5) +DEFINE_HOOK(0x72193a, TiberiumClass_DTOR, 0x6) +DEFINE_HOOK(0x7220d0, TiberiumClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK_AGAIN(0x721e80, TiberiumClass_SaveLoad_Prefix, 0x7) +DEFINE_HOOK(0x72208c, TiberiumClass_Load_Suffix, 0x7) +DEFINE_HOOK(0x72212c, TiberiumClass_Save_Suffix, 0x5) +DEFINE_HOOK(0x721cdc, TiberiumClass_LoadFromINI, 0xa) +DEFINE_HOOK_AGAIN(0x721ce9, TiberiumClass_LoadFromINI, 0xa) +DEFINE_HOOK_AGAIN(0x721c7b, TiberiumClass_LoadFromINI, 0xa) +DEFINE_HOOK(0x489270, CellChainReact, 0x5) +DEFINE_HOOK(0x48964f, DamageArea_ChainReaction, 0x5) +DEFINE_HOOK(0x424dd3, AnimClass_ReInit_TiberiumChainReaction_Chance, 0x6) +DEFINE_HOOK(0x424ec5, AnimClass_ReInit_TiberiumChainReaction_Damage, 0x6) +DEFINE_HOOK(0x75d1a9, WarheadTypeClass_CTOR, 0x7) +DEFINE_HOOK(0x75e5c8, WarheadTypeClass_SDDTOR, 0x6) +DEFINE_HOOK(0x75e2c0, WarheadTypeClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK_AGAIN(0x75e0c0, WarheadTypeClass_SaveLoad_Prefix, 0x8) +DEFINE_HOOK(0x75e2ae, WarheadTypeClass_Load_Suffix, 0x7) +DEFINE_HOOK(0x75e39c, WarheadTypeClass_Save_Suffix, 0x5) +DEFINE_HOOK(0x75deaf, WarheadTypeClass_LoadFromINI, 0x5) +DEFINE_HOOK_AGAIN(0x75dea0, WarheadTypeClass_LoadFromINI, 0x5) +DEFINE_HOOK(0x4895b8, DamageArea_CellSpread1, 0x6) +DEFINE_HOOK(0x4895c7, DamageArea_CellSpread2, 0x8) +DEFINE_HOOK(0x4899be, DamageArea_CellSpread3, 0x8) +DEFINE_HOOK(0x4899da, DamageArea_Damage_MaxAffect, 0x7) +DEFINE_HOOK(0x4893ba, DamageArea_DamageAir, 0x9) +DEFINE_HOOK(0x46920b, BulletClass_Detonate, 0x6) +DEFINE_HOOK(0x71a900, TemporalClass_Update_WarpAway, 0x6) +DEFINE_HOOK(0x71afb2, TemporalClass_Fire_HealthFactor, 0x5) +DEFINE_HOOK(0x517fc1, InfantryClass_ReceiveDamage_DeployedDamage, 0x6) +DEFINE_HOOK(0x7384bd, UnitClass_ReceiveDamage_OreMinerUnderAttack, 0x6) +DEFINE_HOOK(0x442974, BuildingClass_ReceiveDamage_Malicious, 0x6) +DEFINE_HOOK(0x4f94a5, HouseClass_BuildingUnderAttack_Malicious, 0x6) +DEFINE_HOOK(0x702669, TechnoClass_ReceiveDamage_SuppressDeathWeapon, 0x9) +DEFINE_HOOK(0x5f53e5, ObjectClass_ReceiveDamage_Relative, 0x6) +DEFINE_HOOK(0x701a5c, TechnoClass_ReceiveDamage_IronCurtainFlash, 0x7) +DEFINE_HOOK(0x4892be, DamageArea_NullDamage, 0x6) +DEFINE_HOOK(0x489e9f, DamageArea_BridgeAbsoluteDestroyer, 0x5) +DEFINE_HOOK(0x489fd8, DamageArea_BridgeAbsoluteDestroyer2, 0x6) +DEFINE_HOOK(0x48a15d, DamageArea_BridgeAbsoluteDestroyer3, 0x6) +DEFINE_HOOK(0x48a229, DamageArea_BridgeAbsoluteDestroyer4, 0x6) +DEFINE_HOOK(0x48a283, DamageArea_BridgeAbsoluteDestroyer5, 0x6) +DEFINE_HOOK(0x629bc0, ParasiteClass_UpdateSquiddy_Culling, 0x8) +DEFINE_HOOK(0x5f5456, ObjectClass_ReceiveDamage_Culling, 0x6) +DEFINE_HOOK(0x6faf0d, TechnoClass_Update_EMPLock, 0x6) +DEFINE_HOOK(0x44a04c, BuildingClass_Mi_Selling_EMPDuration, 0x6) +DEFINE_HOOK(0x51e3b0, InfantryClass_GetActionOnObject_EMP, 0x7) +DEFINE_HOOK(0x73d219, UnitClass_Draw_OreGatherAnim, 0x6) +DEFINE_HOOK(0x4456e5, BuildingClass_UpdateConstructionOptions_ExcludeDisabled, 0x6) +DEFINE_HOOK(0x53cc0d, IonBlastClass_Update_DTOR, 0x5) +DEFINE_HOOK(0x53cbf5, IonBlastClass_Update_Duration, 0x5) +DEFINE_HOOK(0x53cc63, IonBlastClass_Update_Beam, 0x6) +DEFINE_HOOK(0x53cb91, IonBlastClass_DTOR, 0x6) +DEFINE_HOOK(0x6f36e3, TechnoClass_SelectWeapon_Verses, 0x5) +DEFINE_HOOK(0x708af7, TechnoClass_ShouldRetaliate_Verses, 0x7) +DEFINE_HOOK(0x6fcb6a, TechnoClass_CanFire_Verses, 0x7) +DEFINE_HOOK(0x6f7d3d, TechnoClass_CanAutoTargetObject_Verses, 0x7) +DEFINE_HOOK(0x70cea0, TechnoClass_EvalThreatRating_Verses1, 0x6) +DEFINE_HOOK(0x70cf45, TechnoClass_EvalThreatRating, 0xb) +DEFINE_HOOK(0x489235, GetTotalDamage_Verses, 0x8) +DEFINE_HOOK(0x75ddcc, Verses_OrigParser, 0x7) +DEFINE_HOOK(0x771ee9, WeaponTypeClass_CTOR, 0x5) +DEFINE_HOOK(0x77311d, WeaponTypeClass_SDDTOR, 0x6) +DEFINE_HOOK(0x772eb0, WeaponTypeClass_SaveLoad_Prefix, 0x5) +DEFINE_HOOK_AGAIN(0x772cd0, WeaponTypeClass_SaveLoad_Prefix, 0x7) +DEFINE_HOOK(0x772ea6, WeaponTypeClass_Load_Suffix, 0x6) +DEFINE_HOOK(0x772f8c, WeaponTypeClass_Save, 0x5) +DEFINE_HOOK(0x7729c7, WeaponTypeClass_LoadFromINI, 0x5) +DEFINE_HOOK_AGAIN(0x7729d6, WeaponTypeClass_LoadFromINI, 0x5) +DEFINE_HOOK_AGAIN(0x7729b0, WeaponTypeClass_LoadFromINI, 0x5) +DEFINE_HOOK(0x6fd64a, TechnoClass_FireRadBeam1, 0x6) +DEFINE_HOOK(0x6fd79c, TechnoClass_FireRadBeam2, 0x6) +DEFINE_HOOK(0x6fd469, TechnoClass_FireEBolt, 0x9) +DEFINE_HOOK(0x4c2aff, EBolt_Fire_Particles, 0x5) +DEFINE_HOOK(0x4c2951, EBolt_DTOR, 0x5) +DEFINE_HOOK(0x4c1f33, EBolt_Draw_Colors, 0x7) +DEFINE_HOOK(0x4c24be, EBolt_Draw_Color1, 0x5) +DEFINE_HOOK(0x4c25cb, EBolt_Draw_Color2, 0x5) +DEFINE_HOOK(0x4c26cf, EBolt_Draw_Color3, 0x5) +DEFINE_HOOK(0x6fd438, TechnoClass_FireLaser, 0x6) +DEFINE_HOOK(0x6ff4de, TechnoClass_Fire_IsLaser, 0x6) +DEFINE_HOOK(0x71aaac, TemporalClass_Update_Abductor, 0x6) +DEFINE_HOOK(0x438e86, BombListClass_Plant_AllTechnos, 0x5) +DEFINE_HOOK(0x438fd7, BombListClass_Plant_AttachSound, 0x7) +DEFINE_HOOK(0x438a00, BombClass_GetCurrentFrame, 0x6) +DEFINE_HOOK(0x6f523c, TechnoClass_DrawExtras_IvanBombImage, 0x5) +DEFINE_HOOK(0x6fcbad, TechnoClass_CanFire_IvanBomb, 0x6) +DEFINE_HOOK(0x51e488, InfantryClass_GetActionOnObject2, 0x5) +DEFINE_HOOK(0x438799, BombClass_Detonate1, 0x6) +DEFINE_HOOK(0x438843, BombClass_Detonate2, 0x6) +DEFINE_HOOK(0x438879, BombClass_Detonate3, 0x6) +DEFINE_HOOK(0x4393f2, BombClass_SDDTOR, 0x5) +DEFINE_HOOK(0x6fa4c6, TechnoClass_Update_ZeroOutTarget, 0x5) +DEFINE_HOOK(0x46934d, BulletClass_DetonateAt_IvanBombs, 0x6) +DEFINE_HOOK(0x6ffec0, TechnoClass_GetActionOnObject_IvanBombsA, 0x5) +DEFINE_HOOK(0x6fff9e, TechnoClass_GetActionOnObject_IvanBombsB, 0x8) +DEFINE_HOOK(0x51f1d8, InfantryClass_ActionOnObject_IvanBombs, 0x6) +DEFINE_HOOK(0x7388eb, UnitClass_ActionOnObject_IvanBombs, 0x6) +DEFINE_HOOK(0x725961, AnnounceInvalidPointer_BombCloak, 0x6) +DEFINE_HOOK(0x4471d5, BuildingClass_Sell_DetonateNoBuildup, 0x6) +DEFINE_HOOK(0x44a1ff, BuildingClass_Mi_Selling_DetonatePostBuildup, 0x6) +DEFINE_HOOK(0x4d9f7b, FootClass_Sell_Detonate, 0x6) +DEFINE_HOOK(0x65b5fb, RadSiteClass_Radiate_UnhardcodeSnow, 0x0) +DEFINE_HOOK(0x6ff449, TechnoClass_Fire_SonicWave, 0x5) +DEFINE_HOOK(0x6ff5f5, TechnoClass_Fire_OtherWaves, 0x6) +DEFINE_HOOK(0x763226, WaveClass_DTOR, 0x6) +DEFINE_HOOK(0x760f50, WaveClass_Update, 0x6) +DEFINE_HOOK(0x75fa29, WaveClass_Draw_Colors, 0x6) +DEFINE_HOOK(0x760bc2, WaveClass_Draw2, 0x9) +DEFINE_HOOK(0x760de2, WaveClass_Draw3, 0x9) +DEFINE_HOOK(0x75ee57, WaveClass_Draw_Sonic, 0x7) +DEFINE_HOOK(0x75ee2e, WaveClass_Draw_Green, 0x8) +DEFINE_HOOK(0x7601c7, WaveClass_Draw_Magnetron, 0x8) +DEFINE_HOOK(0x7601fb, WaveClass_Draw_Magnetron2, 0xb) +DEFINE_HOOK(0x760286, WaveClass_Draw_Magnetron3, 0x5) +DEFINE_HOOK(0x762b62, WaveClass_Update_Beam, 0x6) +DEFINE_HOOK(0x75f38f, WaveClass_DamageCell, 0x6) +DEFINE_HOOK(0x76110b, WaveClass_RecalculateAffectedCells_Clear, 0x5) +DEFINE_HOOK(0x7609e3, WaveClass_Draw_NodLaser_Details, 0x5) +DEFINE_HOOK(0x4ab35a, DisplayClass_SetAction_CustomCursor, 0x6) +DEFINE_HOOK(0x5bddc0, MouseClass_Update_Reset, 0x5) +DEFINE_HOOK(0x4d7524, FootClass_ActionOnObject_Allow, 0x9) +DEFINE_HOOK(0x653ca6, RadarClass_GetMouseAction_AllowMinimap, 0x5) +DEFINE_HOOK(0x6929fc, DisplayClass_ChooseAction_CanSell, 0x7) +DEFINE_HOOK(0x4abfbe, DisplayClass_LeftMouseButtonUp_ExecPowerToggle, 0x7) +DEFINE_HOOK(0x4011c0, Audio_Load, 0x6) +DEFINE_HOOK(0x4064a0, VocClassData_AddSample, 0x0) +DEFINE_HOOK(0x4016f0, IDXContainer_LoadSample, 0x6) +DEFINE_HOOK(0x401640, AudioIndex_GetSampleInformation, 0x5) +DEFINE_HOOK(0x511d16, HouseTypeClass_LoadFromINI_Buffer_CountryVeteran, 0x9) +DEFINE_HOOK(0x66d55e, Buf_General, 0x6) +DEFINE_HOOK(0x67062f, Buf_AnimToInf_Paradrop, 0x6) +DEFINE_HOOK(0x66fa13, Buf_SecretBoons, 0x6) +DEFINE_HOOK(0x66f7c0, Buf_PPA, 0x9) +DEFINE_HOOK(0x66f589, Buf_Shipyard, 0x6) +DEFINE_HOOK(0x66f34b, Buf_RepairBay, 0x5) +DEFINE_HOOK(0x66dd13, Buf_WeatherArt, 0x6) +DEFINE_HOOK(0x66db93, Buf_BridgeExplosions, 0x6) +DEFINE_HOOK(0x66bc71, Buf_CombatDamage, 0x9) +DEFINE_HOOK(0x672b0e, Buf_AI, 0x6) +DEFINE_HOOK(0x7125df, TechnoTypeClass_LoadFromINI_ListLength, 0x7) +DEFINE_HOOK(0x713171, TechnoTypeClass_LoadFromINI_SkipLists1, 0x9) +DEFINE_HOOK(0x713c10, TechnoTypeClass_LoadFromINI_SkipLists2, 0x7) +DEFINE_HOOK(0x75d660, WarheadTypeClass_LoadFromINI_ListLength, 0x9) +DEFINE_HOOK(0x75dae6, WarheadTypeClass_LoadFromINI_SkipLists, 0x4) +DEFINE_HOOK(0x772462, WeaponTypeClass_LoadFromINI_ListLength, 0x4) +DEFINE_HOOK(0x7274af, TriggerTypeClass_LoadFromINI_Read_Events, 0x5) +DEFINE_HOOK(0x7274c8, TriggerTypeClass_LoadFromINI_Strtok_Events, 0x5) +DEFINE_HOOK(0x727529, TriggerTypeClass_LoadFromINI_Read_Actions, 0x5) +DEFINE_HOOK(0x727544, TriggerTypeClass_LoadFromINI_Strtok_Actions, 0x5) +DEFINE_HOOK(0x4750ec, INIClass_ReadHouseTypesList, 0x7) +DEFINE_HOOK(0x475107, INIClass_ReadHouseTypesList_Strtok, 0x5) +DEFINE_HOOK(0x47527c, INIClass_GetAlliesBitfield, 0x7) +DEFINE_HOOK(0x475297, INIClass_GetAlliesBitfield_Strtok, 0x5) +DEFINE_HOOK(0x6a9348, StripClass_GetTip_FixLength, 0x9) +DEFINE_HOOK(0x70cad8, TechnoClass_DealParticleDamage_DontDestroyCliff, 0x9) +DEFINE_HOOK(0x489562, DamageArea_DestroyCliff, 0x6) +DEFINE_HOOK(0x5cb0b1, QueryPerformance, 0x5) +DEFINE_HOOK(0x6bd7e3, Expand_MIX_Reorg, 0x5) +DEFINE_HOOK(0x52bb64, Expand_MIX_Deorg, 0x5) +DEFINE_HOOK(0x53029e, Load_Bootstrap_AresMIX, 0x5) +DEFINE_HOOK(0x6be9bd, sub_6BE1C0, 0x6) +DEFINE_HOOK(0x715857, TechnoTypeClass_LoadFromINI_LimitPalettes, 0x5) +DEFINE_HOOK(0x441d25, BuildingClass_Destroy, 0xa) +DEFINE_HOOK(0x71af2b, TemporalClass_Fire_UnwarpableA, 0xa) +DEFINE_HOOK(0x424b23, AnimClass_Update, 0x6) +DEFINE_HOOK(0x6bb9dd, WinMain_LogNonsense, 0x5) +DEFINE_HOOK(0x531726, StupidPips1, 0x5) +DEFINE_HOOK(0x53173f, StupidPips2, 0x5) +DEFINE_HOOK(0x5f698f, ObjectClass_GetCell, 0x5) +DEFINE_HOOK(0x6fca30, TechnoClass_GetWeaponState, 0x6) +DEFINE_HOOK(0x671152, RulesClass_Addition_General_PrismSupportModifier, 0x6) +DEFINE_HOOK(0x4693b0, BulletClass_Fire_Overpower, 0x6) +DEFINE_HOOK(0x414d36, AACombat, 0x5) +DEFINE_HOOK(0x6f7561, Arcing_Aircraft, 0x5) +DEFINE_HOOK(0x56017a, OptionsDlg_WndProc_RemoveResLimit, 0x5) +DEFINE_HOOK(0x5601e3, OptionsDlg_WndProc_RemoveHiResCheck, 0x0) +DEFINE_HOOK(0x455e4c, HouseClass_FindRepairBay, 0x9) +DEFINE_HOOK(0x441c21, BuildingClass_Destroy_ShakeScreenZero, 0x6) +DEFINE_HOOK(0x7387d1, UnitClass_Destroy_ShakeScreenZero, 0x6) +DEFINE_HOOK(0x699c1c, Game_ParsePKTs_ClearFile, 0x7) +DEFINE_HOOK(0x7440bd, UnitClass_Remove, 0x6) +DEFINE_HOOK(0x50928c, HouseClass_Update_Factories_Queues_SkipBrokenDTOR, 0x5) +DEFINE_HOOK(0x730db0, GuardCommandClass_Execute, 0x0) +DEFINE_HOOK(0x472198, CaptureManagerClass_DrawLinks, 0x6) +DEFINE_HOOK(0x62a2f8, ParasiteClass_PointerGotInvalid, 0x6) +DEFINE_HOOK(0x4db87e, FootClass_SetLocation_Parasite, 0x6) +DEFINE_HOOK(0x718871, TeleportLocomotionClass_UnfreezeObject_SinkOrSwim, 0x7) +DEFINE_HOOK(0x621b80, DSurface_FillRectWithColor, 0x5) +DEFINE_HOOK(0x52ba78, YR_GameInit_Pre, 0x5) +DEFINE_HOOK(0x469467, BulletClass_DetonateAt_CanTemporalTarget, 0x5) +DEFINE_HOOK(0x442ce0, BuildingClass_Init_Cloakable, 0x6) +DEFINE_HOOK(0x413fa3, AircraftClass_Init_Cloakable, 0x5) +DEFINE_HOOK(0x48a4f9, SelectDamageAnimation_FixNegatives, 0x6) +DEFINE_HOOK(0x41d887, AirstrikeClass_Fire, 0x6) +DEFINE_HOOK(0x47f9a4, CellClass_DrawOverlay_WallRemap, 0x6) +DEFINE_HOOK(0x418478, AircraftClass_Mi_Attack_Untarget1, 0x6) +DEFINE_HOOK(0x4186d7, AircraftClass_Mi_Attack_Untarget2, 0x6) +DEFINE_HOOK(0x418826, AircraftClass_Mi_Attack_Untarget3, 0x6) +DEFINE_HOOK(0x418935, AircraftClass_Mi_Attack_Untarget4, 0x6) +DEFINE_HOOK(0x418a44, AircraftClass_Mi_Attack_Untarget5, 0x6) +DEFINE_HOOK(0x418b40, AircraftClass_Mi_Attack_Untarget6, 0x6) +DEFINE_HOOK(0x71aa52, TemporalClass_Update_AnnounceInvalidPointer, 0x8) +DEFINE_HOOK(0x4ca437, FactoryClass_GetCRC, 0x0) +DEFINE_HOOK(0x4ca0e3, FactoryClass_AbandonProduction_Invalidate, 0x6) +DEFINE_HOOK(0x5005cc, HouseClass_SetFactoryCreatedManually, 0x6) +DEFINE_HOOK(0x50067c, HouseClass_ClearFactoryCreatedManually, 0x6) +DEFINE_HOOK(0x5007be, HouseClass_SetFactoryCreatedManually2, 0x6) +DEFINE_HOOK(0x749088, FixedWidthCounter_ResetWithGivenCount, 0x6) +DEFINE_HOOK(0x65ec4a, TeamTypeClass_ValidateHouse, 0x6) +DEFINE_HOOK_AGAIN(0x65d8fb, TeamTypeClass_ValidateHouse, 0x6) +DEFINE_HOOK(0x70cbda, TechnoClass_DealParticleDamage, 0x6) +DEFINE_HOOK(0x62cde8, ParticleClass_Update_Fire, 0x5) +DEFINE_HOOK(0x62c2ed, ParticleClass_Update_Gas, 0x6) +DEFINE_HOOK(0x4692a2, BulletClass_DetonateAt_RaiseAttackedByHouse, 0x6) +DEFINE_HOOK(0x6f661d, TechnoClass_DrawHealthBar_DestroyedBuilding_RedPip, 0x7) +DEFINE_HOOK(0x47243f, CaptureManagerClass_DecideUnitFate_BuildingFate, 0x6) +DEFINE_HOOK(0x71810d, TeleportLocomotionClass_ILocomotion_MoveTo_Deactivated, 0x6) +DEFINE_HOOK(0x51df27, InfantryClass_Remove_Teleport, 0x6) +DEFINE_HOOK(0x707a47, TechnoClass_PointerGotInvalid_LastTarget, 0xa) +DEFINE_HOOK(0x707b09, TechnoClass_PointerGotInvalid_ResetMindControl, 0x6) +DEFINE_HOOK(0x720c42, Theme_Stop_NoLog, 0x5) +DEFINE_HOOK_AGAIN(0x720de8, Theme_Stop_NoLog, 0x5) +DEFINE_HOOK(0x720f37, sub_720EA0_NoLog, 0x5) +DEFINE_HOOK(0x720a61, sub_7209D0_NoLog, 0x5) +DEFINE_HOOK(0x615bd3, Handle_Static_Messages_LoopingMovie, 0x5) +DEFINE_HOOK(0x700e47, TechnoClass_CanDeploySlashUnload_Immobile, 0xa) +DEFINE_HOOK(0x44a5f0, BuildingClass_Mi_Selling_EngineerFreeze, 0x6) +DEFINE_HOOK(0x4430e8, BuildingClass_Demolish_LogCrash, 0x6) +DEFINE_HOOK(0x73d81e, UnitClass_Mi_Unload_LastPassenger, 0x5) +DEFINE_HOOK(0x730ee5, StopCommandClass_Execute_Berzerk, 0x6) +DEFINE_HOOK(0x4748a0, INIClass_GetPipIdx, 0x7) +DEFINE_HOOK(0x5f77f0, ObjectTypeClass_UnloadPipsSHP, 0x5) +DEFINE_HOOK(0x737ce4, UnitClass_ReceiveDamage_ShipyardRepair, 0x6) +DEFINE_HOOK(0x4b5eb0, DropPodLocomotionClass_ILocomotion_Process_Smoke, 0x6) +DEFINE_HOOK(0x4b5f9e, DropPodLocomotionClass_ILocomotion_Process_Report, 0x6) +DEFINE_HOOK(0x4b619f, DropPodLocomotionClass_ILocomotion_MoveTo_AtmosphereEntry, 0x5) +DEFINE_HOOK(0x52070f, InfantryClass_UpdateFiringState_Uncloak, 0x5) +DEFINE_HOOK(0x69281e, DisplayClass_ChooseAction_TogglePower, 0xa) +DEFINE_HOOK(0x56bc54, ThreatPosedEstimates_GetIndex, 0x5) +DEFINE_HOOK(0x4fa2e0, HouseClass_SetThreat_Bounds, 0x7) +DEFINE_HOOK(0x6fc339, TechnoClass_CanFire_OpenToppedGunnerTemporal, 0x6) +DEFINE_HOOK(0x4759d4, INIClass_WriteEdge, 0x7) +DEFINE_HOOK(0x449ff8, BuildingClass_Mi_Selling_PutMcv, 0x7) +DEFINE_HOOK(0x44a8a2, BuildingClass_Mi_Selling_Crew, 0xa) +DEFINE_HOOK(0x4c6ddb, Networking_RespondToEvent_Selling, 0x8) +DEFINE_HOOK(0x6fa2c7, TechnoClass_Update_DrawHidden, 0x8) +DEFINE_HOOK(0x7119d5, TechnoTypeClass_CTOR_NoInit_Particles, 0x6) +DEFINE_HOOK(0x73f7dd, UnitClass_IsCellOccupied_Bib, 0x8) +DEFINE_HOOK(0x5f6515, AbstractClass_Distance2DSquared_1, 0x8) +DEFINE_HOOK(0x5f6560, AbstractClass_Distance2DSquared_2, 0x5) +DEFINE_HOOK(0x72920c, TunnelLocomotionClass_Turning, 0x9) +DEFINE_HOOK(0x4586d6, BuildingClass_KillOccupiers, 0x9) +DEFINE_HOOK(0x4239f0, AnimClass_UpdateBounce_Damage, 0x8) +DEFINE_HOOK(0x74a884, VoxelAnimClass_Update_Damage, 0x6) +DEFINE_HOOK(0x5c9a6e, Global_CollectScoreScreenData, 0x5) +DEFINE_HOOK(0x534f89, Game_ReloadNeutralMIX_NewLine, 0x5) +DEFINE_HOOK(0x7bb445, XSurface_20, 0x6) +DEFINE_HOOK(0x4d9ee1, FootClass_CanBeSold_Dock, 0x6) +DEFINE_HOOK(0x4d5782, FootClass_ApproachTarget_Passive, 0x6) +DEFINE_HOOK(0x717855, TechnoTypeClass_UpdatePalette_Reset, 0x6) +DEFINE_HOOK_AGAIN(0x717823, TechnoTypeClass_UpdatePalette_Reset, 0x6) +DEFINE_HOOK(0x716d98, TechnoTypeClass_Load_Palette, 0x5) +DEFINE_HOOK(0x5fdda4, IsOverlayIdxTiberium_Log, 0x6) +DEFINE_HOOK(0x75f46e, WaveClass_DamageCell_Wall, 0x6) +DEFINE_HOOK(0x6b7d50, SpawnManagerClass_CountDockedSpawns, 0x6) +DEFINE_HOOK(0x51f716, InfantryClass_Mi_Unload_Undeploy, 0x5) +DEFINE_HOOK(0x6fcf53, TechnoClass_SetTarget_Burst, 0x6) +DEFINE_HOOK(0x6e20d8, TActionClass_DestroyAttached_Loop, 0x5) +DEFINE_HOOK(0x4db37c, FootClass_Remove_Airspace, 0x6) +DEFINE_HOOK(0x451a28, BuildingClass_PlayAnim_Destroy, 0x7) +DEFINE_HOOK(0x451e40, BuildingClass_DestroyNthAnim_Destroy, 0x7) +DEFINE_HOOK(0x514f60, HoverLocomotionClass_ILocomotion_MoveTo, 0x7) +DEFINE_HOOK_AGAIN(0x514e97, HoverLocomotionClass_ILocomotion_MoveTo, 0x7) +DEFINE_HOOK(0x516305, HoverLocomotionClass_sub_515ED0, 0x9) +DEFINE_HOOK(0x6bed08, Game_Terminate_Mouse, 0x7) +DEFINE_HOOK(0x48a634, FlashbangWarheadAt_Details, 0x5) +DEFINE_HOOK(0x5ff86e, SpotLightClass_Draw_Details, 0x5) +DEFINE_HOOK(0x422fcc, AnimClass_Draw_Details, 0x5) +DEFINE_HOOK(0x550bca, LaserDrawClass_Draw_InHouseColor_Details, 0x5) +DEFINE_HOOK(0x62cec9, ParticleClass_Draw_Details, 0x5) +DEFINE_HOOK(0x6d7847, TacticalClass_DrawPixelEffects_Details, 0x5) +DEFINE_HOOK(0x420f40, AlphaShapeClass_DrawAll_Details, 0x6) +DEFINE_HOOK(0x551a30, LayerClass_YSortReorder, 0x5) +DEFINE_HOOK(0x5f6612, ObjectClass_UnInit_SkipInvalidation, 0x9) +DEFINE_HOOK(0x725a1f, AnnounceInvalidPointer_SkipBehind, 0x5) +DEFINE_HOOK(0x6f90f8, TechnoClass_SelectAutoTarget_Demacroize, 0x6) +DEFINE_HOOK(0x707eea, TechnoClass_GetGuardRange_Demacroize, 0x6) +DEFINE_HOOK(0x70133e, TechnoClass_GetWeaponRange_Demacroize, 0x5) +DEFINE_HOOK(0x458e1e, BuildingClass_GetOccupyRangeBonus_Demacroize, 0xa) +DEFINE_HOOK(0x547043, IsometricTileTypeClass_ReadFromFile, 0x6) +DEFINE_HOOK(0x7272b5, TriggerTypeClass_LoadFromINI_House, 0x6) +DEFINE_HOOK(0x41088d, AbstractTypeClass_CTOR_IDTooLong, 0x6) +DEFINE_HOOK(0x712045, TechnoTypeClass_GetCameo, 0x5) +DEFINE_HOOK(0x6a9948, StripClass_Draw_SuperWeapon, 0x6) +DEFINE_HOOK(0x6a9a2a, StripClass_Draw_Main, 0x6) +DEFINE_HOOK(0x6a9952, StripClass_Draw_SuperWeapon_PCX, 0x6) +DEFINE_HOOK(0x6a980a, StripClass_Draw_TechnoType_PCX, 0x8) +DEFINE_HOOK(0x6a99f3, StripClass_Draw_SkipSHPForPCX, 0x6) +DEFINE_HOOK(0x6a9a43, StripClass_Draw_DrawPCX, 0x6) +DEFINE_HOOK(0x64dea0, Multiplay_LogToSYNC_NOMPDEBUG, 0x0) +DEFINE_HOOK(0x6516f0, Multiplay_LogToSync_MPDEBUG, 0x6) +DEFINE_HOOK(0x52e9aa, Frontend_WndProc_Checksum, 0x5) +DEFINE_HOOK(0x4a1c10, Checksummer_Add_BYTE, 0x5) +DEFINE_HOOK(0x4a1ca0, Checksummer_Add_bool, 0x5) +DEFINE_HOOK(0x4a1d30, Checksummer_Add_WORD, 0x5) +DEFINE_HOOK(0x4a1d50, Checksummer_Add_DWORD, 0x8) +DEFINE_HOOK(0x4a1d70, Checksummer_Add_float, 0x8) +DEFINE_HOOK(0x4a1d90, Checksummer_Add_double, 0x8) +DEFINE_HOOK(0x4a1de0, Checksummer_Add_Buffer, 0x6) +DEFINE_HOOK(0x4a80d0, CD_AlwaysFindYR, 0x6) +DEFINE_HOOK(0x4790e0, CD_AlwaysAvailable, 0x7) +DEFINE_HOOK(0x479110, CD_NeverAsk, 0x5) +DEFINE_HOOK(0x49f5c0, CopyProtection_IsLauncherRunning, 0x8) +DEFINE_HOOK(0x49f620, CopyProtection_NotifyLauncher, 0x5) +DEFINE_HOOK(0x49f7a0, CopyProtection_CheckProtectedData, 0x8) +DEFINE_HOOK(0x55cfdf, BlowMeUp, 0x0) +DEFINE_HOOK(0x481c6c, CellClass_CrateBeingCollected_Armor1, 0x6) +DEFINE_HOOK(0x481ce1, CellClass_CrateBeingCollected_Speed1, 0x6) +DEFINE_HOOK(0x481d0e, CellClass_CrateBeingCollected_Firepower1, 0x6) +DEFINE_HOOK(0x481d3d, CellClass_CrateBeingCollected_Cloak1, 0x6) +DEFINE_HOOK(0x48294f, CellClass_CrateBeingCollected_Cloak2, 0x7) +DEFINE_HOOK(0x482e57, CellClass_CrateBeingCollected_Armor2, 0x6) +DEFINE_HOOK(0x48303a, CellClass_CrateBeingCollected_Speed2, 0x6) +DEFINE_HOOK(0x483226, CellClass_CrateBeingCollected_Firepower2, 0x6) +DEFINE_HOOK(0x7349cf, StringTable_ParseFile_Buffer, 0x7) +DEFINE_HOOK(0x7346d0, CSF_LoadBaseFile, 0x6) +DEFINE_HOOK(0x734823, CSF_AllocateMemory, 0x6) +DEFINE_HOOK(0x734a5f, CSF_AddOrOverrideLabel, 0x5) +DEFINE_HOOK(0x734a97, CSF_SetIndex, 0x6) +DEFINE_HOOK(0x6bd886, CSF_LoadExtraFiles, 0x5) +DEFINE_HOOK(0x734e83, CSF_LoadString_1, 0x6) +DEFINE_HOOK(0x734ec2, CSF_LoadString_2, 0x7) +DEFINE_HOOK(0x6622e0, RocketLocomotionClass_ILocomotion_Process_CustomMissile, 0x6) +DEFINE_HOOK(0x66238a, RocketLocomotionClass_ILocomotion_Process_CustomMissileTakeoff1, 0x5) +DEFINE_HOOK(0x662512, RocketLocomotionClass_ILocomotion_Process_CustomMissileTakeoff2, 0x5) +DEFINE_HOOK(0x6627e5, RocketLocomotionClass_ILocomotion_Process_CustomMissileTakeoff3, 0x5) +DEFINE_HOOK(0x662d85, RocketLocomotionClass_ILocomotion_Process_CustomMissileTrailer, 0x6) +DEFINE_HOOK(0x66305a, RocketLocomotionClass_Explode_CustomMissile, 0x6) +DEFINE_HOOK(0x663218, RocketLocomotionClass_Explode_CustomMissile2, 0x5) +DEFINE_HOOK(0x6632f2, RocketLocomotionClass_ILocomotion_MoveTo_CustomMissile, 0x6) +DEFINE_HOOK(0x6634f6, RocketLocomotionClass_ILocomotion_DrawMatrix_CustomMissile, 0x6) +DEFINE_HOOK(0x6b6d60, SpawnManagerClass_CTOR_CustomMissile, 0x6) +DEFINE_HOOK(0x6b72f9, SpawnManagerClass_Update_Buildings, 0x5) +DEFINE_HOOK(0x6b78f8, SpawnManagerClass_Update_CustomMissile, 0x6) +DEFINE_HOOK(0x6b7a72, SpawnManagerClass_Update_CustomMissile2, 0x6) +DEFINE_HOOK(0x6b752e, SpawnManagerClass_Update_CustomMissileTakeoff, 0x6) +DEFINE_HOOK(0x4c850b, Exception_Dialog, 0x5) +DEFINE_HOOK(0x4a4ac0, Debug_Log, 0x1) +DEFINE_HOOK_AGAIN(0x4068e0, Debug_Log, 0x1) +DEFINE_HOOK(0x534a4d, Theater_Init_ResetLogStatus, 0x6) +DEFINE_HOOK(0x687c56, INIClass_ReadScenario_ResetLogStatus, 0x5) +DEFINE_HOOK(0x753000, VoxClass_CreateFromINIList, 0x6) +DEFINE_HOOK(0x7531cf, VoxClass_DeleteAll, 0x5) +DEFINE_HOOK(0x752fdc, VoxClass_LoadFromINI, 0x5) +DEFINE_HOOK(0x7528e8, VoxClass_PlayEVASideSpecific, 0x5) +DEFINE_HOOK(0x753380, VoxClass_GetFilename, 0x5) +DEFINE_HOOK(0x4c8fe0, Exception_Handler, 0x9) +DEFINE_HOOK(0x6d4684, TacticalClass_Draw_FlyingStrings, 0x6) +DEFINE_HOOK(0x6ab773, SelectClass_ProcessInput_ProduceUnsuspended, 0xa) +DEFINE_HOOK(0x474200, CCINIClass_ReadCCFile1, 0x6) +DEFINE_HOOK(0x474314, CCINIClass_ReadCCFile2, 0x6) +DEFINE_HOOK(0x526cc0, INIClass_Section_GetKeyName, 0x7) +DEFINE_HOOK(0x528a10, INIClass_GetString, 0x5) +DEFINE_HOOK(0x5260a2, INIClass_Parse_IteratorChar1, 0x6) +DEFINE_HOOK(0x525d23, INIClass_Parse_IteratorChar2, 0x5) +DEFINE_HOOK(0x525ca5, INIClass_Parse_IniSectionIncludes_PreProcess1, 0x8) +DEFINE_HOOK(0x525ddb, INIClass_Parse_IniSectionIncludes_PreProcess2, 0x5) +DEFINE_HOOK(0x525c28, INIClass_Parse_IniSectionIncludes_CopySection1, 0x7) +DEFINE_HOOK(0x525e44, INIClass_Parse_IniSectionIncludes_CopySection2, 0x7) +DEFINE_HOOK(0x5260d9, INIClass_Parse_Override, 0x7) +DEFINE_HOOK(0x52ddba, Frontend_WndProc_MessageBox, 0x5) +DEFINE_HOOK(0x52e446, Frontend_WndProc_JustAfterAction, 0x6) +DEFINE_HOOK(0x53208d, Main_hDlg_SinglePlayerButtonClick, 0x6) +DEFINE_HOOK(0x5320c2, Main_hDlg_WWOnlineButtonClick, 0x6) +DEFINE_HOOK(0x532051, Main_hDlg_NetworkButtonClick, 0x6) +DEFINE_HOOK(0x5320ae, Main_hDlg_MoviesAndCreditsButtonClick, 0x6) +DEFINE_HOOK(0x52d724, SinglePlayer_hDlg_CampaignButtonClick, 0x6) +DEFINE_HOOK(0x52d713, SinglePlayer_hDlg_SkirmishButtonClick, 0x6) +DEFINE_HOOK(0x52d7ed, MoviesAndCredits_hDlg_SneakPeeksButtonClick, 0x6) +DEFINE_HOOK(0x52d7fb, MoviesAndCredits_hDlg_PlayMoviesButtonClick, 0x6) +DEFINE_HOOK(0x52d809, MoviesAndCredits_hDlg_ViewCreditsButtonClick, 0x6) +DEFINE_HOOK(0x62267f, Dialog_Show_GetTemplate, 0x6) +DEFINE_HOOK(0x6226ee, Dialog_Show_UpdateControls, 0x6) +DEFINE_HOOK(0x52f00b, CampaignMenu_hDlg_PopulateCampaignList, 0x5) +DEFINE_HOOK(0x52ec18, CampaignMenu_hDlg_PreHandleGeneral, 0x5) +DEFINE_HOOK(0x52ed21, CampaignMenu_hDlg_ClickedPlay, 0x9) +DEFINE_HOOK(0x52ef39, CampaignMenu_hDlg_ImageMouseDown, 0x5) +DEFINE_HOOK(0x52ee04, CampaignMenu_hDlg_SelectHoverSound, 0x6) +DEFINE_HOOK(0x52f232, CampaignMenu_hDlg_StartCampaign, 0x6) +DEFINE_HOOK(0x60378b, CampaignMenu_ChooseButtonPalette, 0x6) +DEFINE_HOOK(0x603a2e, CampaignMenu_ChooseButtonImage, 0x6) +DEFINE_HOOK(0x60a90a, CampaignMenu_StaticButtonImage, 0x5) +DEFINE_HOOK(0x60357e, CampaignMenu_SetAnimationDuration, 0x5) +DEFINE_HOOK(0x52f191, CampaignMenu_InitializeMoreButtons, 0x5) +DEFINE_HOOK(0x6041ed, DialogFunc_SubText_CampaignIconA, 0x5) +DEFINE_HOOK(0x6041f5, DialogFunc_CampaignMenu_CampaignIconB, 0x5) +DEFINE_HOOK(0x4e43c0, Game_InitDropdownColors, 0x5) +DEFINE_HOOK(0x69a310, SessionClass_GetPlayerColorScheme, 0x7) +DEFINE_HOOK(0x4e42a0, GameSetup_GetColorTooltip, 0x5) +DEFINE_HOOK(0x4e46bb, hWnd_PopulateWithColors, 0x7) +DEFINE_HOOK(0x4e4a41, hWnd_SetPlayerColor_A, 0x7) +DEFINE_HOOK(0x4e4b47, hWnd_SetPlayerColor_B, 0x7) +DEFINE_HOOK(0x4e4556, hWnd_GetSlotColorIndex, 0x7) +DEFINE_HOOK(0x4e4580, hWnd_IsAvailableColor, 0x5) +DEFINE_HOOK(0x4e4c9d, hWnd_UpdatePlayerColors_A, 0x7) +DEFINE_HOOK(0x4e4d67, hWnd_UpdatePlayerColors_B, 0x7) +DEFINE_HOOK(0x69b97d, Game_ProcessRandomPlayers_ObserverColor, 0x7) +DEFINE_HOOK(0x69b949, Game_ProcessRandomPlayers_ColorsA, 0x6) +DEFINE_HOOK(0x69ba13, Game_ProcessRandomPlayers_ColorsB, 0x6) +DEFINE_HOOK(0x69b69b, GameModeClass_PickRandomColor_Unlimited, 0x6) +DEFINE_HOOK(0x69b7ff, Session_SetColor_Unlimited, 0x6) +DEFINE_HOOK(0x60fad7, Ownerdraw_PostProcessColors, 0xa) +DEFINE_HOOK(0x612da9, Handle_Button_Messages_Color, 0x6) +DEFINE_HOOK(0x613072, Handle_Button_Messages_DisabledColor, 0x7) +DEFINE_HOOK(0x61664c, Handle_Checkbox_Messages_Color, 0x5) +DEFINE_HOOK(0x616655, Handle_Checkbox_Messages_Disabled, 0x5) +DEFINE_HOOK(0x616af0, Handle_RadioButton_Messages_Color, 0x6) +DEFINE_HOOK(0x615df7, Handle_Static_Messages_Color, 0x6) +DEFINE_HOOK(0x615ab7, Handle_Static_Messages_Disabled, 0x6) +DEFINE_HOOK(0x619a4f, Handle_Listbox_Messages_Color, 0x6) +DEFINE_HOOK(0x6198d3, Handle_Listbox_Messages_DisabledA, 0x6) +DEFINE_HOOK(0x619a5f, Handle_Listbox_Messages_DisabledB, 0x6) +DEFINE_HOOK(0x619270, Handle_Listbox_Messages_SelectionA, 0x5) +DEFINE_HOOK(0x619288, Handle_Listbox_Messages_SelectionB, 0x6) +DEFINE_HOOK(0x617a2b, Handle_Combobox_Messages_Color, 0x6) +DEFINE_HOOK(0x617a57, Handle_Combobox_Messages_Disabled, 0x6) +DEFINE_HOOK(0x60dda6, Handle_Combobox_Dropdown_Messages_SelectionA, 0x5) +DEFINE_HOOK(0x60ddb6, Handle_Combobox_Dropdown_Messages_SelectionB, 0x6) +DEFINE_HOOK(0x61e2a5, Handle_Slider_Messages_Color, 0x5) +DEFINE_HOOK(0x61e2b1, Handle_Slider_Messages_Disabled, 0x5) +DEFINE_HOOK(0x61e8a0, Handle_GroupBox_Messages_Color, 0x6) +DEFINE_HOOK(0x614ff2, Handle_NewEdit_Messages_Color, 0x6) +DEFINE_HOOK(0x477007, INIClass_GetSpeedType, 0x8) +DEFINE_HOOK(0x474e8e, INIClass_GetMovementZone, 0x5) +DEFINE_HOOK(0x474dee, INIClass_GetFoundation, 0x7) +DEFINE_HOOK(0x687c16, INIClass_ReadScenario_ValidateThings, 0x6) +DEFINE_HOOK(0x55afb3, LogicClass_Update_1000, 0x6) +DEFINE_HOOK(0x52c939, InitGame_MoviesList, 0x5) +DEFINE_HOOK(0x5fbf80, GameOptionsClass_UnlockMovieIfNeeded_MoviesList, 0x5) +DEFINE_HOOK(0x5faffb, Options_SaveToINI_MoviesList, 0x6) +DEFINE_HOOK(0x5fc000, GameOptionsClass_PopulateMovieList, 0x6) +DEFINE_HOOK(0x4c6ccd, Networking_RespondToEvent, 0x0) +DEFINE_HOOK(0x64ccbf, DoList_ReplaceReconMessage, 0x6) +DEFINE_HOOK(0x64b704, sub_64B660_PayloadSize, 0x8) +DEFINE_HOOK(0x64be83, sub_64BDD0_PayloadSize1, 0x8) +DEFINE_HOOK(0x64c314, sub_64BDD0_PayloadSize2, 0x8) +DEFINE_HOOK(0x596ffe, RMG_EnableArchipelago, 0x0) +DEFINE_HOOK(0x5970ea, RMG_EnableDesert, 0x9) +DEFINE_HOOK(0x596786, MapSeedClass_DialogFunc_SurpriseMe, 0x9) +DEFINE_HOOK(0x596c81, MapSeedClass_DialogFunc_GetData, 0x5) +DEFINE_HOOK(0x5971ea, MapSeedClass_DialogFunc_SetData, 0x5) +DEFINE_HOOK(0x5fed00, OverlayTypeClass_GetRadarColor, 0x0) +DEFINE_HOOK(0x5982d5, MapSeedClass_LoadFromINI, 0x6) +DEFINE_HOOK(0x598fb8, MapSeedClass_Generate_UrbanAreas, 0x5) +DEFINE_HOOK(0x5a65ca, MapSeedClass_Generate_PlaceUrbanStructures_Start, 0x5) +DEFINE_HOOK(0x5a6619, MapSeedClass_Generate_PlaceUrbanStructures_Loop, 0x6) +DEFINE_HOOK(0x5a66b0, MapSeedClass_Generate_PlaceUrbanStructures_SanityCheck, 0x5) +DEFINE_HOOK(0x5a6998, MapSeedClass_Generate_PlaceUrbanFoots, 0x5) +DEFINE_HOOK(0x5a5c6a, MapSeedClass_Generate_PlacePavedRoads_RoadEndNE, 0x9) +DEFINE_HOOK(0x5a5d6f, MapSeedClass_Generate_PlacePavedRoads_RoadEndSW, 0x9) +DEFINE_HOOK(0x5a5f6a, MapSeedClass_Generate_PlacePavedRoads_RoadEndNW, 0x8) +DEFINE_HOOK(0x5a6464, MapSeedClass_Generate_PlacePavedRoads_RoadEndSE, 0x9) +DEFINE_HOOK(0x59000e, RMG_FixPavedRoadEnd_Bridges_North, 0x0) +DEFINE_HOOK(0x5900f7, RMG_FixPavedRoadEnd_Bridges_South, 0x0) +DEFINE_HOOK(0x58fcc6, RMG_FixPavedRoadEnd_Bridges_West, 0x0) +DEFINE_HOOK(0x58fbdd, RMG_FixPavedRoadEnd_Bridges_East, 0x0) +DEFINE_HOOK(0x58fa51, RMG_PlaceWEBridge, 0x6) +DEFINE_HOOK(0x58fe7b, RMG_PlaceNSBridge, 0x8) +DEFINE_HOOK(0x545904, IsometricTileTypeClass_CreateFromINIList_MediansFix, 0x7) +DEFINE_HOOK(0x67d300, SaveGame_Start, 0x5) +DEFINE_HOOK(0x67e42e, SaveGame, 0x5) +DEFINE_HOOK(0x67e730, LoadGame_Start, 0x5) +DEFINE_HOOK(0x67f7c8, LoadGame_End, 0x5) +DEFINE_HOOK(0x67d04e, Game_Save_SavegameInformation, 0x7) +DEFINE_HOOK(0x559f31, LoadOptionsClass_GetFileInfo, 0x9) +DEFINE_HOOK(0x67cefe, Game_Save_FixLog, 0x7) +DEFINE_HOOK(0x483bf1, CellClass_Load_Crates, 0x7) +DEFINE_HOOK(0x565215, MapClass_CTOR_NoInit_Crates, 0x6) +DEFINE_HOOK(0x6acee0, Skirmish_DialogFunc_UpdateMultiEngineer, 0x6) +DEFINE_HOOK(0x6ad8a4, Skirmish_DialogFunc_MultiEngineer, 0x7) +DEFINE_HOOK(0x6aee6a, Skirmish_InitializeDialog_MultiEngineer, 0x5) +DEFINE_HOOK(0x699043, GameMode_SaveGameSettings_MultiEngineer, 0x5) +DEFINE_HOOK(0x69801a, Game_GetGameTypePrefs_MultiEngineer, 0x6) +DEFINE_HOOK(0x531413, Game_Start, 0x5) +DEFINE_HOOK(0x74fdc0, GetModuleVersion, 0x5) +DEFINE_HOOK(0x74fae0, GetModuleInternalVersion, 0x5) +DEFINE_HOOK(0x532017, DlgProc_MainMenu_Version, 0x5) +DEFINE_HOOK(0x52ca37, InitGame_Delay, 0x5) +DEFINE_HOOK(0x7c89d4, DirectDrawCreate, 0x6) +DEFINE_HOOK(0x537bc0, Game_MakeScreenshot, 0x0) +DEFINE_HOOK(0x6d4b25, TacticalClass_Draw_TheDarkSideOfTheMoon, 0x5) +DEFINE_HOOK(0x78997b, sub_789960_RemoveWOLResolutionCheck, 0x0) +DEFINE_HOOK(0x4ba61b, DSurface_CTOR_SkipVRAM, 0x6) +DEFINE_HOOK(0x437ccc, BSurface_DrawSHPFrame1_Buffer, 0x8) +DEFINE_HOOK(0x6cf350, SwizzleManagerClass_ConvertNodes, 0x0) +DEFINE_HOOK(0x6cf2c0, SwizzleManagerClass_Here_I_Am, 0x0) +DEFINE_HOOK(0x6cf240, SwizzleManagerClass_Swizzle, 0x0) +DEFINE_HOOK(0x55afb3, LogicClass_Update, 0x6) +DEFINE_HOOK(0x4cc360, TrajectoryHelper_GetObstacle, 0x5) +DEFINE_HOOK(0x4cc100, TrajectoryHelper_FindFirstObstacle, 0x7) +DEFINE_HOOK(0x4cc310, TrajectoryHelper_FindFirstImpenetrableObstacle, 0x5) +DEFINE_HOOK(0x4a3b4b, FetchResource, 0x9) +DEFINE_HOOK(0x60411b, Game_DialogFunc_Subtext_Load, 0x5) +DEFINE_HOOK(0x604136, Game_DialogFunc_Subtext_Propagate, 0x5) diff --git a/.agents/skills/check-hooks/check_hook_conflicts.py b/.agents/skills/check-hooks/check_hook_conflicts.py new file mode 100644 index 0000000000..1c5c82a388 --- /dev/null +++ b/.agents/skills/check-hooks/check_hook_conflicts.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python3 +"""Check new hooks against existing hooks in HookAnalysis.txt for conflicts. + +Usage: + python check_hook_conflicts.py + +new_hooks_json is a JSON file (or '-' for stdin) containing an array of new hook objects: + [{"address": "0x46BDD9", "size": 5, "name": "MyHook", "returns": "0x46BDE0"}] + +"returns" can be: + - "0" or missing/empty → returns 0 (resolves to hook address, safe, no return-check needed) + - "0x..." → fixed return address to check + - "R->Origin() + N" → relative return (hook address + N) to check + +Output is JSON with conflict results. +""" + +import json +import os +import sys +import re + +# Ensure the script's own directory is in sys.path so that +# 'import parse_hook_log' works regardless of the working directory. +_script_dir = os.path.dirname(os.path.abspath(__file__)) +if _script_dir not in sys.path: + sys.path.insert(0, _script_dir) + +def parse_existing_hooks(script_dir): + """Parse existing hooks from HookAnalysis.txt or ares_3.0p1_hooks.cpp. + + Tries HookAnalysis.txt first (faster, richer metadata). + Falls back to ares_3.0p1_hooks.cpp if HookAnalysis.txt does not exist. + """ + log_path = os.path.join(script_dir, 'HookAnalysis.txt') + if os.path.exists(log_path): + import parse_hook_log + return parse_hook_log.parse_hook_log(log_path) + + cpp_path = os.path.join(script_dir, 'ares_3.0p1_hooks.cpp') + if os.path.exists(cpp_path): + import parse_hooks_cpp + return parse_hooks_cpp.parse_hooks_cpp(cpp_path) + + print(json.dumps({ + 'errors': [], + 'notes': [{ + 'problem': 'Setup', + 'type': 'error', + 'message': ( + "Neither HookAnalysis.txt nor ares_3.0p1_hooks.cpp found " + f"in {script_dir}. Cannot perform conflict check." + ) + }] + }, indent=2, ensure_ascii=False)) + sys.exit(1) + + +def check_hooks(new_hooks, existing_hooks): + results = [] + errors = [] + notes = [] + + for nh in new_hooks: + addr = nh['address'] + addr_int = int(addr, 16) + size = nh['size'] + name = nh.get('name', '') + ret = nh.get('returns', '0') # returns "0", "0x...", or "R->Origin() + N" + range_start = addr_int + range_end = addr_int + size + + # Resolve return address + ret_addr = None + if ret == '0' or not ret: + ret_addr = None # means safe, no return-check needed + elif re.match(r'^0x[0-9A-Fa-f]+$', ret): + ret_addr = int(ret, 16) + else: + m = re.match(r'R->Origin\(\)\s*\+\s*(\d+)', ret) + if m: + ret_addr = addr_int + int(m.group(1)) + + # Check Problem 0: size >= 5 + if size < 5: + errors.append({ + 'problem': 'Problem 0', + 'hook': name, + 'address': addr, + 'message': f"Hook '{name}' at {addr} has size {size} (< 5). The JMP instruction requires at least 5 bytes." + }) + + # Check Problem 1: conflicts + found_conflict = False + for eh in existing_hooks: + e_range_start = eh['range_start'] + e_range_end = eh['range_end'] + e_addr = eh['address'] + + # Check address range overlap + if range_start < e_range_end and range_end > e_range_start: + if range_start == e_range_start and range_end == e_range_end: + # Exact overlap - stacked hooks, not an error + notes.append({ + 'problem': 'Problem 1', + 'hook': name, + 'address': addr, + 'size': size, + 'existing_hook': eh['name'], + 'existing_dll': eh['dll'], + 'existing_address': e_addr, + 'existing_size': eh['size'], + 'type': 'stacked', + 'message': ( + f"Hook '{name}' at {addr} (size {size}) exactly matches " + f"existing hook '{eh['name']}' from {eh['dll']}. " + f"This is a stacked hook — the second will execute after the first returns 0. " + f"Verify this is intended." + ) + }) + else: + # Partial overlap - conflict + errors.append({ + 'problem': 'Problem 1', + 'hook': name, + 'address': addr, + 'size': size, + 'range': f"[0x{range_start:08X}, 0x{range_end:08X})", + 'existing_hook': eh['name'], + 'existing_dll': eh['dll'], + 'existing_address': e_addr, + 'existing_size': eh['size'], + 'existing_range': f"[0x{e_range_start:08X}, 0x{e_range_end:08X})", + 'type': 'conflict', + 'message': ( + f"Hook '{name}' at {addr} (size {size}, range " + f"[0x{range_start:08X}, 0x{range_end:08X})) conflicts with " + f"existing hook '{eh['name']}' from {eh['dll']} at " + f"{e_addr} (size {eh['size']}, range " + f"[0x{e_range_start:08X}, 0x{e_range_end:08X})). " + f"The address ranges overlap." + ) + }) + found_conflict = True + + # Check return address + if ret_addr is not None and ret_addr != addr_int: + if e_range_start <= ret_addr < e_range_end: + errors.append({ + 'problem': 'Problem 1', + 'hook': name, + 'address': addr, + 'returns': ret, + 'return_addr': f"0x{ret_addr:08X}", + 'existing_hook': eh['name'], + 'existing_dll': eh['dll'], + 'existing_range': f"[0x{e_range_start:08X}, 0x{e_range_end:08X})", + 'type': 'return_conflict', + 'message': ( + f"Hook '{name}' at {addr} returns to 0x{ret_addr:08X}, " + f"which falls within existing hook '{eh['name']}' from {eh['dll']} " + f"covering [0x{e_range_start:08X}, 0x{e_range_end:08X})." + ) + }) + + if not found_conflict: + notes.append({ + 'problem': 'Problem 1', + 'hook': name, + 'address': addr, + 'type': 'ok', + 'message': f"No conflicts detected for hook '{name}' at {addr}." + }) + + return {'errors': errors, 'notes': notes} + + +def main(): + if len(sys.argv) < 2: + new_hooks = json.load(sys.stdin) + else: + new_hooks_input = sys.argv[1] + if new_hooks_input == '-': + new_hooks = json.load(sys.stdin) + else: + with open(new_hooks_input, 'r', encoding='utf-8') as f: + new_hooks = json.load(f) + + script_dir = os.path.dirname(os.path.abspath(__file__)) + existing_hooks = parse_existing_hooks(script_dir) + + results = check_hooks(new_hooks, existing_hooks) + json.dump(results, sys.stdout, indent=2, ensure_ascii=False) + + +if __name__ == '__main__': + main() diff --git a/.agents/skills/check-hooks/discover_hooks.py b/.agents/skills/check-hooks/discover_hooks.py new file mode 100644 index 0000000000..27e1fcc860 --- /dev/null +++ b/.agents/skills/check-hooks/discover_hooks.py @@ -0,0 +1,474 @@ +#!/usr/bin/env python3 +"""Discover new or modified DEFINE_HOOK / DEFINE_HOOK_AGAIN for checking. + +Modes: + python discover_hooks.py --commit Check hooks in a specific commit + python discover_hooks.py Auto-detect what to check + +Auto-detect priority: + 1. Uncommitted changes (working tree vs HEAD) + unpushed commits + on the current branch (vs upstream) + 2. If nothing above, check from the branch point off develop, + i.e. commits in HEAD that are not in develop. + +Outputs JSON array to stdout with hooks and their source locations. +""" + +import subprocess +import sys +import os +import re +import json + + +# 首先查找并切换到 git 仓库根目录 +def find_git_root(): + """Find the git repository root by searching upwards from current directory.""" + current_dir = os.path.abspath(os.getcwd()) + while True: + if os.path.isdir(os.path.join(current_dir, '.git')): + return current_dir + parent = os.path.dirname(current_dir) + if parent == current_dir: # 到达根目录仍未找到 + return None + current_dir = parent + +# 切换到 git 根目录 +git_root = find_git_root() +if git_root: + os.chdir(git_root) + + +NO_PAGER_ENV = None + + +def _no_pager_env(): + global NO_PAGER_ENV + if NO_PAGER_ENV is None: + NO_PAGER_ENV = os.environ.copy() + NO_PAGER_ENV['GIT_PAGER'] = 'cat' + NO_PAGER_ENV['PAGER'] = 'cat' + return NO_PAGER_ENV + + +def run_git(args): + result = subprocess.run( + ['git'] + args, + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + env=_no_pager_env() + ) + return result.stdout.strip() + + +def has_uncommitted(): + out = run_git(['diff', 'HEAD', '--name-only']) + if out: + return True + # Also check untracked source files — git diff HEAD doesn't show new files + untracked = run_git(['ls-files', '--others', '--exclude-standard', '--', '*.cpp', '*.h']) + return len(untracked) > 0 + + +def get_upstream(): + """Return upstream branch name, or empty string if not set.""" + out = run_git(['rev-parse', '--abbrev-ref', '@{u}']) + # If no upstream, git prints an error to stderr and empty stdout + if out and 'fatal' not in out: + return out + return '' + + +def has_unpushed(): + upstream = get_upstream() + if not upstream: + return False + out = run_git(['log', f'{upstream}..HEAD', '--oneline']) + return len(out) > 0 + + +def get_unpushed_commits(): + upstream = get_upstream() + if not upstream: + return None + out = run_git(['log', f'{upstream}..HEAD', '--format=%H']) + if out: + return out.splitlines() + return None + + +def get_develop_diff_commits(): + """Get commits on HEAD that are not in develop. + Tries 'develop' first, then 'origin/develop' as fallback.""" + for candidate in ('develop', 'origin/develop'): + out = run_git(['log', f'{candidate}..HEAD', '--format=%H']) + if out: + return out.splitlines(), candidate + return None, None + + +def get_diff_from_range(commit_range): + """Run git diff and return the text.""" + args = ['diff', commit_range, '--', '*.cpp', '*.h'] + return run_git(args) + + +def _include_untracked_content(diff_text): + """Append untracked source file content as diff additions. + This allows new files (not yet tracked by git) to be parsed for hooks.""" + untracked = run_git(['ls-files', '--others', '--exclude-standard', '--', '*.cpp', '*.h']) + if not untracked: + return diff_text + result = [diff_text] if diff_text else [] + for file_path in untracked.splitlines(): + try: + with open(file_path, 'r', encoding='utf-8', errors='replace') as f: + content = f.read() + result.append(f'+++ b/{file_path}') + for line in content.split('\n'): + result.append(f'+{line}') + except OSError: + pass + return '\n'.join(result) + + +def get_show_commit(commit): + """Run git show for a single commit, restricted to source files. + Returns (diff_text, error) where error is None on success.""" + result = subprocess.run( + ['git', 'show', commit, '--', '*.cpp', '*.h'], + capture_output=True, + text=True, + encoding='utf-8', + errors='replace', + env=_no_pager_env() + ) + if result.returncode != 0: + return '', result.stderr.strip() + return result.stdout, None + + +def get_recent_commits(count=30): + """Return the last N commits as a list of {sha, message}.""" + log_lines = run_git(['log', f'-{count}', '--format=%H %s']).splitlines() + result = [] + for line in log_lines: + line = line.strip() + if not line: + continue + parts = line.split(' ', 1) + if len(parts) >= 2: + result.append({'sha': parts[0], 'message': parts[1]}) + elif parts: + result.append({'sha': parts[0], 'message': ''}) + return result + + +def resolve_commit(candidate): + """Try to resolve a commit reference. + Returns (sha, message, needs_selection, candidates). + If git can resolve directly, returns that commit. + Otherwise returns the last 30 commits for AI to pick from and present to user. + """ + # Try direct resolution first (git handles partial SHAs natively) + result = subprocess.run( + ['git', 'rev-parse', '--verify', f'{candidate}^{{commit}}'], + capture_output=True, text=True, encoding='utf-8', errors='replace', + env=_no_pager_env() + ) + if result.returncode == 0: + sha = result.stdout.strip() + msg = run_git(['log', '-1', '--format=%s', sha]) + return sha, msg, False, [] + + # Can't resolve — return recent commits for AI to pick from + recent = get_recent_commits() + if recent: + return None, '', True, recent + return None, '', False, [] + + +HOOK_RE = re.compile( + r'DEFINE_HOOK(?:AGAIN)?\s*\(\s*' + r'(0x[0-9A-Fa-f]+)\s*,\s*' + r'(\w+)\s*,\s*' + r'(0x[0-9A-Fa-f]+|\d+)' +) + +RETURN_0_RE = re.compile(r'\breturn\s+0\s*;') +RETURN_HEX_RE = re.compile(r'\breturn\s+(0x[0-9A-Fa-f]+)\s*;') +RETURN_ORIGIN_RE = re.compile(r'\breturn\s+R->Origin\(\)\s*\+\s*(0x[0-9A-Fa-f]+|\d+)\s*;') +ENUM_VAL_RE = re.compile(r'(Continue|Skip)\s*=\s*(0x[0-9A-Fa-f]+)') +ENUM_RET_RE = re.compile(r'\breturn\s+(Continue|Skip)\s*;') + + +def parse_hooks_from_diff(diff_text): + """Parse DEFINE_HOOK/DEFINE_HOOK_AGAIN from unified diff.""" + hooks = [] + current_file = '' + lines = diff_text.split('\n') + + for i, line in enumerate(lines): + # Track file + file_match = re.match(r'^\+\+\+\s+b/(.*)', line) + if file_match: + current_file = file_match.group(1) + continue + + # Only process added lines + if not line.startswith('+'): + continue + # Skip the +++ line itself + if line.startswith('+++'): + continue + + stripped = line[1:] # Remove the '+' prefix + + m = HOOK_RE.search(stripped) + if m: + address = m.group(1) + name = m.group(2) + size_raw = m.group(3) + # size can be hex or decimal + if size_raw.startswith('0x') or size_raw.startswith('0X'): + size = int(size_raw, 16) + else: + size = int(size_raw) + + hooks.append({ + 'address': address, + 'name': name, + 'size': size, + 'file': current_file, + 'diff_line': i, + }) + + return hooks + + +def analyze_return_behavior(hooks, diff_text): + """For each hook, try to determine return behavior from the diff context.""" + lines = diff_text.split('\n') + # Map diff lines to hook entries + hook_by_diff_line = {h['diff_line']: h for h in hooks} + + for i, line in enumerate(lines): + if i not in hook_by_diff_line: + continue + h = hook_by_diff_line[i] + + # Look forward in the diff for return statements (within ~150 lines) + # Track brace depth to handle nested blocks (if/else/for etc.) + brace_depth = 0 + for j in range(i + 1, min(i + 150, len(lines))): + future = lines[j] + if not future.startswith('+') or future.startswith('+++'): + continue + content = future[1:] + + # Count braces to track depth into the hook body + open_count = content.count('{') + close_count = content.count('}') + brace_depth += open_count - close_count + if brace_depth <= 0: + break # Exited the hook body + + # Check for enum definitions + enum_m = ENUM_VAL_RE.search(content) + if enum_m: + label = enum_m.group(1) + val = enum_m.group(2) + h.setdefault('enum_map', {})[label] = val + + # Check return statements + ret0 = RETURN_0_RE.search(content) + if ret0: + h['returns'] = '0' + break + + ret_hex = RETURN_HEX_RE.search(content) + if ret_hex: + ret_addr = ret_hex.group(1) + # If we have enum mapping, resolve it + enum_map = h.get('enum_map', {}) + for label, val in enum_map.items(): + if ret_addr == label: + ret_addr = val + break + h['returns'] = ret_addr + break + + ret_origin = RETURN_ORIGIN_RE.search(content) + if ret_origin: + offset = ret_origin.group(1) + if offset.startswith('0x') or offset.startswith('0X'): + offset_int = int(offset, 16) + else: + offset_int = int(offset) + h['returns'] = f'R->Origin() + {offset_int}' + break + + # Check enum-based return + enum_ret = ENUM_RET_RE.search(content) + if enum_ret: + label = enum_ret.group(1) + enum_map = h.get('enum_map', {}) + if label in enum_map: + h['returns'] = enum_map[label] + else: + h['returns'] = f'enum:{label}' + break + + if 'returns' not in h: + h['returns'] = '?' # Could not determine from diff alone + + +def get_fork_point(): + """Find the fork point of the current branch using various strategies. + Returns (base_ref, description) or (None, message) on failure.""" + # Strategy 1: fork-point from origin/develop + for candidate in ('origin/develop', 'develop'): + out = run_git(['merge-base', '--fork-point', candidate, 'HEAD']) + if out: + try: + int(out, 16) + return out, candidate + except ValueError: + pass + # Strategy 2: plain merge-base + for candidate in ('origin/develop', 'develop'): + out = run_git(['merge-base', candidate, 'HEAD']) + if out: + try: + int(out, 16) + return out, candidate + except ValueError: + pass + return None, 'no fork point found' + + +def determine_diff_range(): + """Determine the git diff range for auto-detect mode. + + Returns (diff_args, description) where diff_args is list for git diff + and description is a human-readable explanation. + """ + # Priority 1: uncommitted changes (including untracked source files) + if has_uncommitted(): + # Check if there are also unpushed commits + unpushed = get_unpushed_commits() + if unpushed: + upstream = get_upstream() or 'develop' + return ([f'{upstream}...HEAD'], f'{upstream}...HEAD', f'unpushed (vs {upstream}) + uncommitted changes') + else: + return (['HEAD'], 'HEAD', 'uncommitted changes') + + # Priority 1b: unpushed only (no uncommitted) + unpushed = get_unpushed_commits() + if unpushed: + upstream = get_upstream() or 'develop' + return ([f'{upstream}...HEAD'], f'{upstream}...HEAD', f'unpushed commits (vs {upstream})') + + # Priority 2: nothing unpushed/uncommitted, check commits ahead of develop + dev_commits, dev_ref = get_develop_diff_commits() + if dev_commits: + return ([f'{dev_ref}...HEAD'], f'{dev_ref}...HEAD', f'commits ahead of {dev_ref}') + + # Priority 3: try to find the fork point — useful when neither develop nor upstream exists + fork_base, fork_desc = get_fork_point() + if fork_base: + return ([f'{fork_base}...HEAD'], f'{fork_base}...HEAD', f'fork point from {fork_desc}') + + return ([], '', 'no changes found') + + +def main(): + import argparse + parser = argparse.ArgumentParser(description='Discover new hooks for checking.') + parser.add_argument('--commit', '-c', help='Check hooks in a specific commit (SHA or ref)') + parser.add_argument('--json-only', action='store_true', help='Output only the JSON array, no metadata') + args = parser.parse_args() + + if args.commit: + # First try direct git show + diff_text, error = get_show_commit(args.commit) + if error: + # Failed — try to resolve + sha, msg, ambiguous, candidates = resolve_commit(args.commit) + if ambiguous: + # Multiple matches — output for user selection + result = { + 'mode': 'commit', + 'action': 'resolve', + 'input': args.commit, + 'candidates': candidates, + 'message': f"Ambiguous commit '{args.commit}'. Please pick one:" + } + print(json.dumps(result, indent=2, ensure_ascii=False)) + return + elif sha: + diff_text, error = get_show_commit(sha) + if error: + print(json.dumps({ + 'mode': 'commit', + 'action': 'error', + 'message': f"Cannot show commit {sha}: {error}" + }, indent=2, ensure_ascii=False)) + return + desc = f'commit {sha} ({msg})' + else: + print(json.dumps({ + 'mode': 'commit', + 'action': 'error', + 'message': f"Cannot resolve '{args.commit}'. No matching commit found in the last 30." + }, indent=2, ensure_ascii=False)) + return + else: + desc = f'commit {args.commit}' + else: + diff_args, range_desc, desc = determine_diff_range() + if not diff_args: + result = { + 'mode': 'auto', + 'description': desc, + 'hooks': [], + 'warning': 'No changes to check. No new hooks found.' + } + print(json.dumps(result, indent=2, ensure_ascii=False)) + return + diff_text = get_diff_from_range(' '.join(diff_args)) + diff_text = _include_untracked_content(diff_text) + + hooks = parse_hooks_from_diff(diff_text) + analyze_return_behavior(hooks, diff_text) + + # Clean up internal fields before output + output_hooks = [] + for h in hooks: + out = { + 'address': h['address'], + 'name': h['name'], + 'size': h['size'], + 'file': h['file'], + 'returns': h.get('returns', '?'), + } + output_hooks.append(out) + + if args.json_only: + print(json.dumps(output_hooks, indent=2, ensure_ascii=False)) + else: + result = { + 'mode': 'commit' if args.commit else 'auto', + 'description': desc, + 'count': len(output_hooks), + 'hooks': output_hooks, + } + if not output_hooks: + result['warning'] = 'No DEFINE_HOOK or DEFINE_HOOK_AGAIN found in the diff.' + print(json.dumps(result, indent=2, ensure_ascii=False)) + + +if __name__ == '__main__': + main() diff --git a/.agents/skills/check-hooks/parse_hook_log.py b/.agents/skills/check-hooks/parse_hook_log.py new file mode 100644 index 0000000000..e0fb8853d8 --- /dev/null +++ b/.agents/skills/check-hooks/parse_hook_log.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +"""Parse HookAnalysis.txt and output JSON with all hooks.""" +import re +import json +import sys +import os + +def parse_hook_log(filepath): + with open(filepath, 'r', encoding='utf-8', errors='replace') as f: + lines = f.readlines() + + hooks = [] + current_addr = None + + # Format A: separate address line, hook has 来自"" + addr_re = re.compile(r'在\s+([0-9A-Fa-f]+)\s*[::]') + hook_re_a = re.compile( + r'钩子"(.+?),相对于"",来自"(.+?)",长度(\d+),优先级\s+(\d+),次优先级\s+""' + ) + # Format B: no separate address line, hook has 位于 + hook_re_b = re.compile( + r'钩子"(.+?),相对于"",位于([0-9A-Fa-f]+),长度(\d+),优先级\s+(\d+),次优先级\s+""' + ) + + for line in lines: + line = line.strip() + if not line: + continue + + # Try address line (Format A style) + m = addr_re.match(line) + if m: + current_addr = int(m.group(1), 16) + continue + + # Try Format A hook: 来自"" + m = hook_re_a.match(line) + if m and current_addr is not None: + name = m.group(1) + dll = m.group(2) + size = int(m.group(3)) + priority = int(m.group(4)) + hooks.append(_make_hook(current_addr, name, dll, size, priority)) + continue + + # Try Format B hook: 位于 (no preceding address line needed) + m = hook_re_b.match(line) + if m: + addr = int(m.group(2), 16) + name = m.group(1) + dll = '(unknown)' + size = int(m.group(3)) + priority = int(m.group(4)) + hooks.append(_make_hook(addr, name, dll, size, priority)) + + return hooks + + +def _make_hook(addr, name, dll, size, priority): + return { + 'address': f'0x{addr:08X}', + 'address_int': addr, + 'name': name, + 'dll': dll, + 'size': size, + 'priority': priority, + 'range_start': addr, + 'range_end': addr + size, + } + +def main(): + script_dir = os.path.dirname(os.path.abspath(__file__)) + filepath = os.path.join(script_dir, 'HookAnalysis.txt') + hooks = parse_hook_log(filepath) + + # Output as JSON + output = [] + for h in hooks: + output.append({ + 'address': h['address'], + 'name': h['name'], + 'dll': h['dll'], + 'size': h['size'], + 'range': f"[{h['address']}, 0x{h['range_end']:08X})", + }) + json.dump(output, sys.stdout, indent=2, ensure_ascii=False) + +if __name__ == '__main__': + main() diff --git a/.agents/skills/check-hooks/parse_hooks_cpp.py b/.agents/skills/check-hooks/parse_hooks_cpp.py new file mode 100644 index 0000000000..9350205989 --- /dev/null +++ b/.agents/skills/check-hooks/parse_hooks_cpp.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +"""Parse ares_3.0p1_hooks.cpp and output all hooks as JSON. + +This produces the same dict structure as parse_hook_log.parse_hook_log(), +so it can be used interchangeably by check_hook_conflicts.py. + +Usage: + python parse_hooks_cpp.py [filepath] + (defaults to ares_3.0p1_hooks.cpp in the same directory) +""" +import re +import json +import sys +import os + + +HOOK_RE = re.compile( + r'DEFINE_HOOK(?:AGAIN)?\s*\(\s*' + r'(0x[0-9A-Fa-f]+)\s*,\s*' + r'(\w+)\s*,\s*' + r'(0x[0-9A-Fa-f]+|\d+)\s*' + r'\)' +) + + +def parse_hooks_cpp(filepath): + with open(filepath, 'r', encoding='utf-8', errors='replace') as f: + lines = f.readlines() + + hooks = [] + seen = set() + + for line in lines: + stripped = line.strip() + if not stripped or stripped.startswith('//'): + continue + + comment_pos = stripped.find('//') + code_part = stripped[:comment_pos] if comment_pos >= 0 else stripped + + m = HOOK_RE.search(code_part) + if m: + addr_str = m.group(1) + name = m.group(2) + size_raw = m.group(3) + + addr = int(addr_str, 16) + size = int(size_raw, 16) if size_raw.startswith(('0x', '0X')) else int(size_raw) + + key = (addr, name) + if key in seen: + hook = _make_hook(addr, name, 'Ares 3.0p1', size, 0) + hooks.append(hook) + continue + + seen.add(key) + hook = _make_hook(addr, name, 'Ares 3.0p1', size, 0) + hooks.append(hook) + + return hooks + + +def _make_hook(addr, name, dll, size, priority): + return { + 'address': f'0x{addr:08X}', + 'address_int': addr, + 'name': name, + 'dll': dll, + 'size': size, + 'priority': priority, + 'range_start': addr, + 'range_end': addr + size, + } + + +def main(): + script_dir = os.path.dirname(os.path.abspath(__file__)) + filepath = os.path.join(script_dir, 'ares_3.0p1_hooks.cpp') + if len(sys.argv) > 1: + filepath = sys.argv[1] + + hooks = parse_hooks_cpp(filepath) + + output = [] + for h in hooks: + output.append({ + 'address': h['address'], + 'name': h['name'], + 'dll': h['dll'], + 'size': h['size'], + 'range': f"[{h['address']}, 0x{h['range_end']:08X})", + }) + json.dump(output, sys.stdout, indent=2, ensure_ascii=False) + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/.agents/skills/generate-ini-tag/SKILL.md b/.agents/skills/generate-ini-tag/SKILL.md new file mode 100644 index 0000000000..bf0ba6e469 --- /dev/null +++ b/.agents/skills/generate-ini-tag/SKILL.md @@ -0,0 +1,124 @@ +--- +name: 生成INI标签 Generate INI tag +description: "A \"tag\" is an INI key that generally corresponds to a C++ property. Defining a tag requires modifications in four places within the target `ExtData` class: **declaration**, **initialization**, **serialization**, and **INI loading**. If the user does not provide all information explicitly, the AI should infer the missing pieces, present them to the user for confirmation, and only proceed after approval." +--- + +#### 1. Determine the Class + +Game entities have **instance classes** (e.g., `BuildingClass`) and **static type classes** (e.g., `BuildingTypeClass`). Tags are almost always defined in the static type class's `ExtData` — when the user says "add a tag for buildings", they mean `BuildingTypeExt::ExtData`. Same pattern applies: + +| User says | Corresponding ExtData | +|-----------|----------------------| +| Building / BuildingType | `BuildingTypeExt::ExtData` | +| Vehicle / Unit / UnitType | `UnitTypeExt::ExtData` (or `TechnoTypeExt::ExtData`) | +| Infantry / InfantryType | `InfantryTypeClass` → `TechnoTypeExt::ExtData` | +| Aircraft / AircraftType | `AircraftTypeClass` → `TechnoTypeExt::ExtData` | +| Warhead | `WarheadTypeExt::ExtData` | +| Weapon | `WeaponTypeExt::ExtData` | +| Bullet / Projectile | `BulletTypeExt::ExtData` | +| SuperWeapon / SW | `SWTypeExt::ExtData` | +| Animation / Anim | `AnimTypeExt::ExtData` | +| Particle | `ParticleTypeExt::ExtData` | +| Terrain / TerrainType | `TerrainTypeExt::ExtData` | +| Overlay / OverlayType | `OverlayTypeExt::ExtData` | +| Global / Rules | `RulesExt::ExtData` | + +Note: `TechnoTypeExt::ExtData` is the **common base class** extension for `BuildingType`, `UnitType`, `InfantryType`, and `AircraftType`. If a tag applies to all Techno types, place it in `TechnoTypeExt`; if it applies only to a specific subtype, place it in the corresponding subclass Ext (e.g., buildings only → `BuildingTypeExt`). + +#### 2. Determine the Section + +- **Non-global tags**: Read from `pSection` (the INI section matching the instance ID/name) inside `LoadFromINIFile`. Determine whether to use `pSection` or `pArtSection`: + - `pSection` = `pThis->ID` — reads from the identically-named section in `rulesmd.ini`. The vast majority of tags go here. + - `pArtSection` = `pThis->ImageFile` — reads from the identically-named section in `artmd.ini`, using `INI_Art` / `exArtINI`. Generally used for visual, graphical, or animation-related tags. Check whether the class's `LoadFromINIFile` defines a `pArtSection` variable and `exArtINI` to see if this is supported. +- **Global tags**: For `RulesExt::ExtData`, read from fixed global sections such as `GameStrings::General` (`[General]`), `GameStrings::CombatDamage` (`[CombatDamage]`), `GameStrings::Radiation` (`[Radiation]`), `GameStrings::AudioVisual` (`[AudioVisual]`), `GameStrings::AI` (`[AI]`), etc. Infer the appropriate section based on the key name's semantics. + +#### 3. Determine the Key Name + +The INI key name specified by the user. **Critical conversion rule: dots `.` in the key name become underscores `_` in the C++ property name.** + +For example: `"Factory.IsSuper"` → C++ property name `Factory_IsSuper` + +#### 4. Determine the Type + +Infer the C++ type based on semantics. Common types and their wrappers: + +| Semantics | C++ Type | Wrapper | +|-----------|---------|---------| +| Boolean on/off switch | `bool` | `Valueable` | +| Integer (count, frames, int percentage) | `int` | `Valueable` | +| Floating point (ratio, multiplier, speed) | `double` | `Valueable` | +| In-game distance (Lepton) | `Leptons` | `Valueable` | +| Coordinate | `CoordStruct` / `Point2D` | `Valueable` | +| Color | `ColorStruct` | `Valueable` | +| Nullable (omitting means use default logic) | any | `Nullable` | +| List (comma-separated values) | `std::vector` | `ValueableVector` | +| Pointer to another game type | `WeaponTypeClass*` etc. | `Valueable` | +| Index lookup (sounds etc.) | see `ValueableIdx` | `ValueableIdx` | +| Enum | game enum | `Valueable` etc. | + +**Inference rules:** +- Key name contains `Is`/`Can`/`Allow`/`Use`/`Has`/`Enable`/`Disable` → likely `Valueable` +- Key name contains `Amount`/`Count`/`Max`/`Min`/`Delay`/`Rate`/`Frame` (integer) → likely `Valueable` +- Key name contains `Factor`/`Mult`/`Percent`/`Speed`/`Ratio`/`Chance` → likely `Valueable` +- Key name contains `Type` (singular pointer) → `Valueable` +- Key name contains `Types` (plural) → `ValueableVector` +- When unsure about optionality, default to `Valueable` (required); use `Nullable` if omitting is semantically valid. + +#### 5. Implementation Steps + +Modify the `Body.h` and `Body.cpp` of the target ExtData. Follow these four steps: + +**A. Declare the property (Body.h → ExtData public section)** + +Follow existing declaration style, e.g.: +```cpp +Valueable Factory_IsSuper; +``` + +**B. Initialize the property (Body.h → ExtData constructor initializer list)** + +Add a default value in the constructor, matching the existing format (comma-first, aligned): +```cpp +, Factory_IsSuper { false } +``` + +**C. Register in serialization (Body.cpp → Serialize function)** + +Insert into the `Stm` chain in `Serialize(T& Stm)`, ordered alphabetically or logically: +```cpp +.Process(this->Factory_IsSuper) +``` + +**D. INI loading (Body.cpp → LoadFromINIFile function)** + +Based on the section determined in step 2, choose `exINI` or `exArtINI`, and `pSection` or `pArtSection`: +```cpp +// From rulesmd.ini (most common) +this->Factory_IsSuper.Read(exINI, pSection, "Factory.IsSuper"); + +// From artmd.ini (visual/graphical) +this->Factory_IsSuper.Read(exArtINI, pArtSection, "Factory.IsSuper"); + +// Global tag example (RulesExt) +this->Factory_IsSuper.Read(exINI, GameStrings::General, "Factory.IsSuper"); +``` + +If the property is a pointer to another game type (e.g., `Valueable`), use `.Read` to enable auto-creation: +```cpp +this->SomeWeapon.Read(exINI, pSection, "SomeWeapon"); +``` + +#### 6. Full Example + +User says: "add a tag called `Factory.IsSuper` for buildings" + +Inference process: +- Class: `BuildingTypeExt::ExtData` ("buildings" → building type static data) +- Section: `pSection` (not visual-related) +- Key: `Factory.IsSuper` → property name `Factory_IsSuper` +- Type: `Valueable` (contains `Is`, boolean semantics) +- Default: `false` (conservative default) + +After confirmation, modify: +- [Body.h](file:///f:/RA2%20Engine%20Extension/MJobos/src/Ext/BuildingType/Body.h): declaration + initialization +- [Body.cpp](file:///f:/RA2%20Engine%20Extension/MJobos/src/Ext/BuildingType/Body.cpp): Serialize + LoadFromINIFile \ No newline at end of file diff --git a/.agents/skills/write-documentation/SKILL.md b/.agents/skills/write-documentation/SKILL.md new file mode 100644 index 0000000000..cbbf8cd266 --- /dev/null +++ b/.agents/skills/write-documentation/SKILL.md @@ -0,0 +1,135 @@ +--- +name: 撰写文档 Write Documentation +description: "### Write Documentation\n\nThis command is invoked when the code changes for a feature or bugfix are complete on a branch (branched from `develop`) and documentation is the only remaining task. The AI should discover what changed, categorize it, and write the appropriate documentation entries." +--- + +#### Workflow + +**Step 0: Determine the author** + +Use `git log develop...HEAD --format="%an <%ae>"` to list commits on the current branch that are not in `develop`. If the branch has extra commits, use the author name(s) from those commits as the user's identity for `CREDITS.md` and `Whats-New.md`. If there are no extra commits or the author cannot be determined from commit information, ask the user directly for their identity before proceeding. + +**Step 1: Discover what changed** + +Run `git diff develop...HEAD --stat` to see which files were modified. Then inspect the diffs in detail using `git diff develop...HEAD` (or `git diff develop...HEAD -- `) to understand the nature of each change. Key things to identify: +- New/changed INI keys (tags) — look for `.Read(exINI, ...)` calls in `LoadFromINIFile` / `LoadBeforeTypeData` etc. +- New/changed hooks — look for `DEFINE_HOOK`, `DEFINE_JUMP`, `DEFINE_PATCH` macros. +- New types/classes — look for new files in `src/New/` or new `ExtData` classes. +- Whether the change is a new feature, an enhancement of existing behavior, a bugfix, a UI change, an AI/scripting change, or something miscellaneous. + +**Step 2: Categorize the change** + +Determine which doc pages need updating: + +| Change type | Where to document | +|-------------|------------------| +| New feature (brand new functionality) | New `### ` section in the appropriate primary doc page, inserted in **dictionary (alphabetical) order** among existing sibling `### ` headings | +| Enhancement of existing game behavior that adds new INI tags | New `### ` section in the appropriate primary doc page, likewise in alphabetical order | +| Enhancement/bugfix of existing game behavior with **no new INI tags** | A bullet point under `## Bugfixes and miscellaneous` in `docs/Fixed-or-Improved-Logics.md` | +| User interface change (new hotkey, display, sidebar, tooltip) | `docs/User-Interface.md` | +| AI, scripting, trigger, or mapping change | `docs/AI-Scripting-and-Mapping.md` | +| Uncategorized change | `docs/Miscellanous.md` | + +Primary doc page mapping: +- Most game logic features → `docs/New-or-Enhanced-Logics.md` + - Within this file, `## ` headings are categories (e.g., `## Buildings`, `## Projectiles`, `## Warheads`). Place new `### ` sections under the appropriate category, sorted alphabetically. + +Additionally, every change requires: +- **`docs/Whats-New.md`** — changelog entry. +- **`CREDITS.md`** — credit the author. + +**Step 3: Draft the documentation text — user review required before writing** + +Before writing any documentation, draft the descriptive text and present it to the user for review. Use the following format for the draft: + +``` +### + +- + - (tab-indented) controls/determines/is used for . +``` + +Two styles for the first bullet point: +- **New feature** (brand new functionality): + ``` + - Now you can . (新功能) + ``` +- **Enhancement of existing behavior** (patches/improves vanilla logic): + ``` + - In vanilla, . Now you can . + ``` + +Wait for the user to approve the drafted text before proceeding to write it into the actual doc files. + +**Step 4: Write the main documentation** + +After user approval, write the approved text into the appropriate doc file. For sections in `docs/New-or-Enhanced-Logics.md` or similar, insert the new `### ` section in alphabetical order among existing sibling headings under the correct `## ` category. Look at existing sibling `### ` headings to determine the correct insertion point. + +Continue with the INI code block following existing conventions from the [README's "How to read code snippets" section](../../README.md#how-to-read-code-snippets): + +````markdown +; which section the entries should be in +; can be a freeform name - in this case the comment would explain what it is +; if no comment to be found - then it's a precise name +[SOMENAME] ; BuildingType +; KeyName=DefaultValue ; accepted type with optional explanation +; if there's nothing to the right of equals sign - the default value is empty/absent +; if these keys have had their value set, they can only be set to their default +; unset state again by setting the value to , or none +; for list of values only clears the entire list +; if the default value is not static - it's written and explained in a comment +UIDescription= ; CSF entry key +``` +```` + +Key rules for INI documentation: +- Use ` ```ini ` fenced code blocks. +- Section header comment format: `[SOMENAME]` followed by spaces then `; ObjectType` (e.g., `; BuildingType`, `; TechnoType`, `; WarheadType`, `; SuperWeaponType`). +- For global sections use the literal section name: `[General]`, `[AudioVisual]`, `[CombatDamage]`, `[Radiation]`, `[AI]`, etc. +- Key name, equals sign, default value (or blank if empty), spaces, semicolon, type description. +- Boolean types are documented as `; boolean`. +- Integer types as `; integer`. +- Floating point types as `; double` or `; float`. +- Pointer types as `; AnimType` (just the game class name, not full C++ type). +- List types as `; list of TechnoType` etc. +- If the INI key name contains dots, it maps naturally (e.g. `KeyName.SubKey` stays as-is in docs). + +When there are many related keys, group them logically within the same section and INI block. Put them in the order they appear in the INI section. + +**Step 5: Chinese translation** + +After the English documentation has been written and confirmed, produce a Chinese translation of the new section(s). Present the Chinese text to the user for review and confirmation. Update the corresponding `.po` files in `docs/locale/zh_CN/LC_MESSAGES/` after the user approves the translation. + +**Step 6: Write `docs/Whats-New.md` entry** + +Whats-New entries go under the `### Version TBD (develop branch nightly builds)` section. Use the author identity determined in Step 0. The entry should be a bullet point with a feature description matching the phrasing used in other entries: + +```markdown +- (by ) +``` + +If new INI keys are user-facing settings in `RA2MD.INI`, also add them under the `### New user settings in RA2MD.INI` section: +```ini +[Phobos] +NewKeyName=true ; boolean +``` + +If INI keys were renamed, add entries under `### Changed tags` using the format seen in existing entries: +```markdown +- `[Section] -> OldName` -> `[Section] -> NewName` +``` + +**Step 7: Write `CREDITS.md` entry** + +Use the author identity determined in Step 0. Find the author's section in `CREDITS.md`. If the author doesn't have a section yet, create one. Add a bullet describing the contribution: + +```markdown +- **AuthorName (GitHubUsername)**: + - Feature description matching the phrasing used in other entries +``` + +Do not skip the CREDITS entry unless the user explicitly says to skip it. + +**Step 8: Review and confirm** + +After writing all entries, present a summary to the user showing which files were modified and what was added. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index c3b8f39bd5..e35bded33b 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -352,3 +352,24 @@ Trust the information here and proceed directly with implementation. Only search If your changes affect anything described in this file (project structure, build process, patterns, macros, etc.), **strongly consider updating this instructions file** to keep it accurate. When selecting a model for Copilot, prefer **Claude** models for this repository. + +## Reusable Helpers in `Helpers::Alex` (src/Utilities/Helpers.Alex.h) + +Before writing new utility code (range queries, conditional iteration, duration calculations, sorting, etc.), check whether any of the following helpers in `src/Utilities/Helpers.Alex.h` can be reused. They are located in the `Helpers::Alex` namespace. + +| Helper | Signature / Usage | Purpose | +|---|---|---| +| `DistinctCollector` | `using DistinctCollector = std::set` | Unique-element set. For pointer types, automatically dereferences and compares by value. | +| `getCappedDuration` | `int getCappedDuration(int current, int duration, int cap)` | Calculates the new frame count for stackable/absolute effects (buff/debuff duration logic). Supports positive (stack/cap) and negative (reduce) durations. | +| `getCellSpreadItems` | `DistinctCollector getCellSpreadItems(CoordStruct const& coords, double spread, bool includeInAir = false)` | Returns all `TechnoClass*` objects within a given `spread` (leptons) from a coordinate. Buildings use cell-center distance; other units use exact location. | +| `getCellSpreadItemsExt` | `DistinctCollector getCellSpreadItemsExt(CoordStruct const& coords, double spread, bool includeInAir, bool ignoreHeight)` | Extended version of `getCellSpreadItems` with an additional `ignoreHeight` flag. | +| `for_each_in_rect` | `bool for_each_in_rect(CellStruct center, float width, int height, Func&& action)` | Invokes `action` for every cell (or every object on the cells) in a rectangle centered at `center`. | +| `for_each_in_rect_or_range` | `bool for_each_in_rect_or_range(CellStruct center, float widthOrRange, int height, Func&& action)` | Dispatches to rectangle traversal (`height > 0`) or circular-range traversal (`CellRangeIterator`, `height <= 0`). | +| `for_each_in_rect_or_spread` | `bool for_each_in_rect_or_spread(CellStruct center, float widthOrRange, int height, Func&& action)` | Dispatches to rectangle traversal (`height > 0`) or CellSpread traversal (`CellSpreadIterator`, `height <= 0`). | +| `is_any_of` | `bool is_any_of(Value&& value, Options&&... options)` | Variadic comparison: returns `true` if `value` equals any of the provided options. | +| `remove_non_paradroppables` | `void remove_non_paradroppables(std::vector& types, const char* section, const char* key)` | Removes entries that are neither `InfantryType` nor `UnitType` from the vector. Logs removals via `Debug::INIParseFailed`. | +| `for_each_if` | `void for_each_if(InIt first, InIt last, Pred pred, Fn func)` | Calls `func` for every element in `[first, last)` that satisfies `pred`. Linear: `std::find_if`-based skip. | +| `for_each_if_n` | `void for_each_if_n(InIt first, InIt last, size_t count, Pred pred, Fn func)` | Same as `for_each_if` but stops after at most `count` invocations of `func`. | +| `selectionsort` | `void selectionsort(FwdIt first, [middle,] last[, Pred pred])` (4 overloads) | Stable partial selection sort. Supports custom predicates and partial sorting (`[first, middle)` vs. full range). | + +**Always prefer reusing these helpers over reimplementing equivalent logic.**