A name belongs to a unit type, so "this Marine, but with a different name" looks impossible. It is not, because the only unit whose name is ever on screen is the one currently selected.
- 1Reserve one map string longer than the longest name you will write, and take its address.
- 2Point that unit type's Map String field at it. When that field is not zero the game reads the name from the map's own strings instead of stat_txt.
- 3Each cycle read the currently selected unit, and write a new name into that string only when the selection has changed.
The rank works the same way: borrow one of stat_txt's existing rank entries and write into it.
There are two length constraints. The reserved map string must not be shorter than the names you write, and the borrowed rank entry's original text must be longer than what you write into it. Both show up later as "it broke when I added one longer name".
Call it once in afterTriggerExec and not inside a player loop: the selected unit is a per-machine value and does not belong in one. The unit's owner is read off the unit; the person clicking is this machine's own player id. They are different numbers.
If all you want is to rename the whole type rather than one unit, it is much cheaper. There are three routes and only the last one changes at runtime.
| 맵 에디터에서 바꾸기 | When the name never changes. |
|---|---|
| SetUnitsDat("Map String", 종류, 문자열) | Compile time, and it reads as data, which is easier to maintain. |
| wwrite(0x660260 + 2 * 종류, 문자열번호) | At runtime, in a single action. |
wwrite(0x660260 + 2 * $U("Terran Marine"), $T("\x08붉은 \x1F해병"));
// 종류가 변수일 때 / when the unit type is a variable
const q, r = div(unitType, 2); // 한 칸에 u16 두 개 / two u16 per dword
Trigger(r == 1, r.SetNumber(2)); // 0 아니면 2 / 0 or 2, never 1
wwrite_epd(EPD(0x660260) + q, r, strIndex);You will see that second line written with a 1, and it is wrong. wwrite_epd's third argument is a byte position - the address mod 4. The odd slot of a u16 array is 2, not 1. For the record 3 is valid too and straddles the dword boundary; that one case had a bug before euddraft 0.8.9.6 where the trailing byte was not written.
0x660260 works because it is the exception: it redirects one type's name to the map's own strings. Everything else that reads a name - a button tooltip, the kill feed - reads stat_txt.tbl, not a map string. For those the route is to patch the table and have dataDumper load it.
The third use is a different name per player. Since the only unit whose name is on screen is the one selected on that machine, looking at who selected it and rewriting the type's name as you go makes the same unit read differently for each person - with exactly the one-line wwrite above.
Writing it does not change what is on screen, though: the panel has to be redrawn. The fix is almost silly - create any unit anywhere and remove it immediately, and it refreshes.
function Refresh_UnitName() {
CreateUnit(1, "Bengalaas", "Anywhere", P8);
RemoveUnit("Bengalaas", P8);
}And the review left on the original version of this code carries four rules that apply to any per-frame code, quite apart from renaming anything.
| 포인터로 비교하면 게임이 나눗셈을 합니다 | MemoryX(ptr + 0x64, ...) makes the game compute (ptr - 0x58A364) / 4 in triggers on every evaluation; MemoryXEPD(epd + 0x64/4, ...) does not. Use epdread_epd rather than dwread_epd, and hold a CUnit rather than a pointer. |
|---|---|
| 선택은 한 패스 안에서 변하지 않습니다 | The selection does not change inside a trigger pass, so read 0x6284B8 once at the top rather than once per helper. |
| '한 기만 선택했나'는 읽기가 필요 없습니다 | “Is exactly one unit selected?” needs no read at all - 0x6284BC being 0 is a single condition. |
| 한 함수가 세 가지 일을 하면 분리하세요 | A function that reads the selection, compares the owner and compares the type is three functions - and keeping them together is what forces the repeated read. |
And a habitual setcurpl is waste: putting one in front of code that does not use CurrentPlayer does nothing. CP is needed for the actions that act on CP, such as DisplayText, and for the CP trick. Not as a habit.
This is the unit panel. It is a different thing from putting a title beside a name in chat, and that does not come out of this.
The chat side is SetPName and SetPNamef, and they are narrower than they sound: they change only the name shown in chat, they last one frame, and they do not change what PName returns. You re-apply them every frame.
// 두 줄이 같은 일을 합니다 / the two lines do the same thing
SetPName (cp, epd2s(title), " \x07level: \x04", level, " ", PColor(cp), PName(cp));
SetPNamef(cp, "{:t} \x07level: \x04{} {:c}{:n}", title, level, cp, cp);