Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions hooks/AlliedRangeRings.hook
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
0x007A7858:
jmp asm__ShouldAddUnit
nop

0x00E4D984:
.int Moho__UserUnit__GetIntelRanges
Comment thread
RutreD marked this conversation as resolved.
Outdated

0x007EF12B:
push esp
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you push stack pointer? Why not use lea to get proper pointer to structure on stack?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

push = 1 byte
lea > 3

mov ecx, esi
call RenderRange__Moho__UserUnit__UserUnit
nop
5 changes: 0 additions & 5 deletions hooks/RangeRings.cpp

This file was deleted.

2 changes: 1 addition & 1 deletion include/PatchBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ extern const char name[] asm(#addr);
((type)*(uintptr_t*)addr)

#define VALIDATE_SIZE(struc, size) \
static_assert(sizeof(struc) == size, "Invalid structure size of " #struc);
static_assert(sizeof(struc) == size, "Invalid structure size of " #struc);
6 changes: 3 additions & 3 deletions include/moho.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ struct moho_set

void set(uint32_t item, bool set)
{
auto *itemPtr = &begin[item >> 5 - baseI];
auto *itemPtr = &begin[(item >> 5) - baseI];
if (itemPtr >= end)
end = itemPtr + 1;
item = 1 << (item & 0x1F);
Expand All @@ -97,7 +97,7 @@ struct moho_set
}
bool operator[](int item)
{
auto *itemPtr = &begin[item >> 5 - baseI];
auto *itemPtr = &begin[(item >> 5) - baseI];
if (itemPtr >= end)
return false;
return *itemPtr & (1 << (item & 0x1F));
Expand Down Expand Up @@ -1406,7 +1406,7 @@ struct CClientManagerImpl : IClientManager
bool unk1; // if value is 1 then KERNEL32.SetEvent is bypassed
};

typedef struct mRequest
struct mRequest
{
IClient *mRequester;
int mAfterBeat;
Expand Down
14 changes: 14 additions & 0 deletions section/AlliedRangeRings.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
extern "C" void __attribute__((naked)) asm__ShouldAddUnit() {
asm volatile(R"(
push ecx;
push edx;
push edi;
push eax;
call _ShouldAddUnit;
add esp, 8;
pop edx;
pop ecx;
test al,al;
je 0x007A789F;
jmp 0x007A7860;)");
}
113 changes: 113 additions & 0 deletions section/AlliedRangeRings.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#include "global.h"
#include "magic_classes.h"

static bool map_has_unit(uintptr_t map_base, uintptr_t key) {
auto head = *reinterpret_cast<uintptr_t*>(map_base + 0x04);
auto node = *reinterpret_cast<uintptr_t*>(head + 0x04);
while (node) {
if (*reinterpret_cast<uint8_t*>(node + 0x19)) // _Isnil (0x19)
return false;
auto node_key = *reinterpret_cast<uintptr_t*>(node + 0x0C);
if (key < node_key)
node = *reinterpret_cast<uintptr_t*>(node + 0x00); // _Left (0x00)
else if (key > node_key)
node = *reinterpret_cast<uintptr_t*>(node + 0x08); // _Right (0x08)
else
return true;
}
return false;
}

enum IntelRangeBehavior {
kOwnUnits = 0,
kOwnUnitsAndAlliedBuildings = 1,
kOwnUnitsAndAlliedUnits = 2
};

int intelRangeBehavior = kOwnUnitsAndAlliedBuildings;
ConDescReg conIntelRangeBehavior{"ren_IntelRangeBehavior",
"Only own units = 0; Own units and allied "
"buildings = 1; Own and allied units = 2",
&intelRangeBehavior};

extern "C" {

uintptr_t __thiscall RenderRange__Moho__UserUnit__UserUnit(uintptr_t this_,
uintptr_t esp) {
// Compiler, I believe in you. We're in a hot path, optimize and inline the
// lambda pls p.s. I saw the listing, mine handled it, and there’s no call
// there
auto equals = []<size_t N>(string& str1, const char (&str2)[N]) {
if (str1.strLen != N - 1) return false;
const char* l = str1.data();
const char* r = str2;
while (*l == *r && *l) {
++l;
++r;
};
return (*l - *r) == 0;
};
auto& rangeName = **reinterpret_cast<string**>(esp + 0x30);
bool isIntelRange = equals(rangeName, "Radar") || equals(rangeName, "Omni") ||
equals(rangeName, "Sonar");
if (isIntelRange) return this_;

auto session = *reinterpret_cast<uintptr_t*>(0x10A6470);
auto focusArmyIndex = *reinterpret_cast<int*>(session + 0x488);
auto unitArmy = *reinterpret_cast<uintptr_t*>(this_ + 0x120);
auto unitArmyIndex = *reinterpret_cast<int*>(unitArmy);
if (unitArmyIndex == focusArmyIndex) return this_;
return 0;
}

// Initially, the game receives the ranges from the current state of the unit,
// not directly from its blueprint. However, the full state of the unit is only
// available to its owner. Since this function is called in RangeExtractors, we
// also need to get the ranges of allies; the original would return 0 0 0 for
// units not belonging to its army. Rewrite 00E3F980 (RadarExtractor), 00E3F998
// (Sonar), 00E3F978 (Omni), and you won't need this
bool __thiscall Moho__UserUnit__GetIntelRanges(uintptr_t this_,
float* omniRange,
float* radarRange,
float* sonarRange) {
if ((*reinterpret_cast<uint8_t*>(this_ + 0x39C) & 8) != 0 &&
(*reinterpret_cast<uint8_t*>(this_ + 0x3A8) & 8) != 0)
return false;
auto blueprint = *reinterpret_cast<uintptr_t*>(this_ + 0x48);
auto intelRanges = reinterpret_cast<uint32_t*>(blueprint + 0x338);
*radarRange = intelRanges[0];
*sonarRange = intelRanges[1];
*omniRange = intelRanges[2];
return (*radarRange > 0.0f || *sonarRange > 0.0f || *omniRange > 0.0f);
}
Comment thread
RutreD marked this conversation as resolved.

bool __cdecl ShouldAddUnit(void* focusArmy, uintptr_t userUnit) {
auto session = *reinterpret_cast<uintptr_t*>(0x10A6470);
auto unitArmy = *reinterpret_cast<void**>(userUnit + 0x120);
if (unitArmy == focusArmy) {
// Optimization: To avoid double rendering,
// do not render the global rings;
// they will be rendered in the selection rings render
auto selectionSize = *(size_t*)(session + 0x4A8);
[[likely]] if (selectionSize < 67)
return true;
return !map_has_unit(session + 0x4A0, userUnit);
}

if (intelRangeBehavior == kOwnUnits) return false;

auto focusArmyIndex = *reinterpret_cast<int*>(session + 0x488);
if (!Army_IsAlly(focusArmyIndex, unitArmy)) return false;
Comment thread
RutreD marked this conversation as resolved.
Outdated

auto blueprint = *reinterpret_cast<uintptr_t*>(userUnit + 0x48);
if (intelRangeBehavior == kOwnUnitsAndAlliedBuildings &&
*reinterpret_cast<uint32_t*>(blueprint + 0x290) != 0)
// MotionType != None -> let's assume this is not a building
return false;

auto intel = reinterpret_cast<uint32_t*>(blueprint + 0x338);
bool hasIntel = (intel[0] | intel[1] | intel[2]) != 0;
return hasIntel;
}

} // extern "C"
Loading
Loading