forked from jewalky/srvmgr
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathnew_character.cpp
120 lines (100 loc) · 3.24 KB
/
new_character.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include "lib/utils.hpp"
#include "utils.h"
extern "C" __declspec(naked) void skip_new_character_stats() {
__asm {
// Jump to `else` block.
//
// Decompiled code for reference:
//
// if (local_24 < 0) { // We inject at this instruction ...
// (local_1c->_).Body = 0x19;
// (local_1c->_).Reaction = 0x19;
// (local_1c->_).Mind = 0x19;
// (local_1c->_).Spirit = 0x19;
// } else { // ... and jump here.
// (local_1c->_).Body = (ushort)body;
// (local_1c->_).Reaction = (ushort)reaction;
// (local_1c->_).Mind = (ushort)mind;
// (local_1c->_).Spirit = (ushort)spirit;
// }
mov edx, 0x0050100b
jmp edx
}
}
void AddSpell(void* spellbook, int spell_id) {
// We allocate memory and trust that server will clean it up later. Vanilla does the same way.
// But here we're using default C++ allocator which might be different from what A2 expects.
int* area = new int[20]();
uint8_t spell_id_byte = static_cast<uint8_t>(spell_id);
void* spell;
// I couldn't get C++ code to follow the weird calling conventions here, so I'm calling via assembly.
__asm {
// Taken from 0x004efc7e.
// Last argument: spell ID, 1 byte, so pass it in EDX via DL.
mov edx, 0
mov dl, spell_id_byte
push edx
// First argument: allocated memory region for the spell. Passed in ECX --- fastcall convention.
mov ecx, area
// Call Spell::Spell.
mov eax, 0x00538fdd
call eax
// Store result.
mov spell, eax
}
__asm {
// Taken from 0x004efc97.
// Last argument: return value from Spell::Spell.
push spell
// Second argument: spell ID, 4 bytes.
push spell_id
// First argument: spellbook. Passed in ECX --- fastcall convention.
mov ecx, spellbook
// Call SpellBook::SetAtGrow.
mov eax, 0x0053d7f0
call eax
}
}
void __stdcall CreateMagicBook(T_UNIT* unit, int main_sphere) {
if (IsWarrior(unit)) {
return;
}
auto spellbook = unit->spellbook;
if (spellbook == nullptr) {
Printf("CreateMagicBook: the spellbook is not initialized, cannot add the spell");
return;
}
int spell_id = 0;
switch (main_sphere) {
case 1:
spell_id = 1; // Fire arrow.
break;
case 2:
spell_id = 5; // Ice missile.
break;
case 3:
spell_id = 10; // Lightning.
break;
case 4:
spell_id = 16; // Diamond dust, aka stone missile.
break;
}
if (spell_id == 0) {
Printf("CreateMagicBook: cannot add the spell for sphere %d", main_sphere);
return;
}
AddSpell(spellbook, spell_id);
}
extern "C" __declspec(naked) void skip_new_character_weapon() {
// Add the magic arrow to the mage's spellbook.
__asm {
push DWORD PTR [ebp+0x8] // Human object.
push DWORD PTR [ebp-0x80] // Main sphere.
call CreateMagicBook
}
// Jump to the end of the original function to skip creating the weapon.
__asm {
mov edx, 0x0053396e
jmp edx
}
}