PDA

View Full Version : Changing the level-based class prefixes in /who


zerjz3
09-24-2014, 12:00 AM
My goal is to change this part of classes.cpp:

const char* GetEQClassName(uint8 class_, uint8 level) {
switch(class_) {
case WARRIOR:
if (level >= 70)
return "Vanquisher";
else if (level >= 65)
return "Overlord"; //Baron-Sprite: LEAVE MY CLASSES ALONE.
else if (level >= 60)
return "Warlord";
else if (level >= 55)
return "Myrmidon";
else if (level >= 51)
return "Champion";
else
return "Warrior";

to something that checks for the player either having a certain flag, or having a certain item in their inventory. Is this at all possible?

Drajor
09-24-2014, 01:41 AM
The client decides what text to print based on a class number in the who response. Name and Guild name are the only server-side changeable fields I am aware of. You could try messing with the rankmessage field (Gives GM/Trader additional text in who results). Worth playing around with for sure!

jdoran
09-24-2014, 02:05 AM
Edit: Drajor posted while I was writing the following. I didn't know that /who was done client-side. But perhaps zerjz3 wanted to change that function for some other reason? I'm reaching.

You would need to change the if statements to something more complicated. The problem is that GetEQClassName() currently depends only on class and level. You would need to add another parameter to the function.

Now the easiest way to do this is place the new parameter at the end, and assign a default value. However... GetEQClassName already has an optional parameter: level. You could require a level be passed everywhere you want the new titles, or for about the same amount of work you could just pass in a new parameter. A pointer to a Client would seem a reasonable choice.

That isn't such a tough problem. We can (under Linux) use find and grep to search for usage. Visual Studio can do the same thing under Windows. Now while there are advanced regular expressions in grep, I'm lazy and like to stick with the simple stuff. So, in your eqemu directory:

find . -exec grep GetEQClassName {} /dev/null \; 2> /dev/null | grep -v Binary

give us:


./common/classes.h:const char* GetEQClassName(uint8 class_, uint8 level = 0);
./common/classes.cpp:const char* GetEQClassName(uint8 class_, uint8 level) {
./zone/embparser.cpp: ExportVar(package_name.c_str(), "class", GetEQClassName(mob->GetClass()));
./zone/client.cpp: Message(0,"Your target is a level %i %s. It appears %s and %s for its level. It seems %s",who->GetLevel(),GetEQClassName(who->GetClass(),1),dmg,hitpoints,resists);
./zone/bot.cpp: GetBotOwner()->Message(15, "A %s - %s bot was detected. Is this Race/Class combination allowed?.", GetRaceName(GetRace()), GetEQClassName(GetClass(), GetLevel()));
./world/client.cpp: clog(WORLD__CLIENT,"Name approval request. Name=%s, race=%s, class=%s",char_name,GetRaceName(race),GetEQClassName(clas)) ;
./world/clientlist.cpp: sprintf(line, " %s[RolePlay %i %s] %s (%s)%s zone: %s%s%s", tmpgm, cle->level(), GetEQClassName(cle->class_(),cle->level()), cle->name(), GetRaceName(cle->race()), tmpguild, tmpZone, LFG, accinfo);
./world/clientlist.cpp: sprintf(line, " %s[ANON %i %s] %s (%s)%s zone: %s%s%s", tmpgm, cle->level(), GetEQClassName(cle->class_(),cle->level()), cle->name(), GetRaceName(cle->race()), tmpguild, tmpZone, LFG, accinfo);
./world/clientlist.cpp: sprintf(line, " %s[%i %s] %s (%s)%s zone: %s%s%s", tmpgm, cle->level(), GetEQClassName(cle->class_(),cle->level()), cle->name(), GetRaceName(cle->race()), tmpguild, tmpZone, LFG, accinfo);
./world/eqw.cpp: res["class"] = GetEQClassName(cle->class_());


There are 7 calls to this function. Easily fixed. If you don't have a pointer to the Client handy, pass in a NULL and revert to the default behavior. It is late, and this is off the top of my head:

if ((level >= 70)
{
if ((client) && (client -> GetInv().HasItem(itemID, 0, 0xff)))
{
return "UberWarlord";
}
else
{
return "Warlord";
}
}

Drajor
09-24-2014, 02:06 AM
I just tested this quickly, you can use strings from eqstr_us.txt for rank.
My level 1 cleric was listed as;
High Priest[1 Cleric] Daaxodush (Human).

Pretty neat.

zerjz3
09-24-2014, 07:13 AM
I just tested this quickly, you can use strings from eqstr_us.txt for rank.
My level 1 cleric was listed as;
High Priest[1 Cleric] Daaxodush (Human).

Pretty neat.

How exactly did you go about this? I know that the names of the ranks can be edited in eqstr_us.txt , but if I wanted to have two available rank titles for each class, how would I execute this?

Did you use the method written out by jdoran to do this or did you simply edit something in eqstr_us.txt? I tried adding this code


if ((level >= 70)
{
if ((client) && (client -> GetInv().HasItem(itemID, 0, 0xff)))
{
return "UberWarlord";
}
else
{
return "Warlord";
}
}

to classes.cpp and compiling, but there were many errors when trying to compile.

Looking for just a little more hand holding here, as I think it seems like this is possible based on what you guys are saying, but just not exactly sure how to execute it yet.


Edit: These are the three errors I get when trying to compile:

Error 1 error C2065: 'client' : undeclared identifier C:\EQ\Source\common\classes.cpp 24 1 common
Error 2 error C2227: left of '->GetInv' must point to class/struct/union/generic type C:\EQ\Source\common\classes.cpp 24 1 common
Error 3 error C2228: left of '.HasItem' must have class/struct/union C:\EQ\Source\common\classes.cpp 24 1 common

Drajor
09-24-2014, 07:39 AM
Did you use the method written out by jdoran to do this or did you simply edit something in eqstr_us.txt? I tried adding this code

Neither. See EntityList::zoneWho, specifically;

if (ClientEntry->IsTrader())
WAPP2->RankMSGID = 12315;
else if (ClientEntry->IsBuyer())
WAPP2->RankMSGID = 6056;
else if (ClientEntry->Admin() >= 10)
WAPP2->RankMSGID = 12312;
else
WAPP2->RankMSGID = 0xFFFFFFFF;


The RankMSGID corresponds to a string ID from eqstr_us.txt. You could add more conditions there for detecting an item or whatever. If you check the hard coded IDs currently in use you find;

12315 TRADER
6056 BUYER
12312 * GM *

Based on my limited tests you can use any valid string ID. Note that this only applies to local zone who, not who all which happens in different place.

zerjz3
09-24-2014, 07:59 AM
Awesome, drajor! I've got this working in its natural form. Now, I just need to find the condition to check if a player has an item. I could actually really use an example of that if you don't mind helping me out just a bit more. I've searched around but everything I've found deals with checking items in quest scripts but not in the source code

Drajor
09-24-2014, 08:20 AM
Quick and nasty;

if (ClientEntry->IsTrader())
WAPP2->RankMSGID = 12315;
else if (ClientEntry->IsBuyer())
WAPP2->RankMSGID = 6056;
else if (ClientEntry->Admin() >= 10)
WAPP2->RankMSGID = 12312;
else if (ClientEntry->GetInv()->HasItem(20542) > -1)
WAPP2->RankMSGID = 8043;
else
WAPP2->RankMSGID = 0xFFFFFFFF;


This (should) check whether that character has item 20542(Singing Short Sword) and change their /who output to have Lyricist prefix.. e.g.

Lyricist[1 Bard] Drajor (Barbarian)

zerjz3
09-24-2014, 08:25 AM
Error 10 error C2819: type 'Inventory' does not have an overloaded member 'operator ->' C:\EQ\Source\zone\entity.cpp 4076 1 zone
Error 11 error C2232: '->Inventory::HasItem' : left operand has 'class' type, use '.' C:\EQ\Source\zone\entity.cpp 4076 1 zone


I get these errors when compiling with that code... seems like we're getting really close to having this figured out.. I copy/pasted what you posted, checking now to make sure I didn't leave some ; or something out from being sloppy

Edit: else if (ClientEntry->GetInv().HasItem(17181, 0, 0xff))
WAPP2->RankMSGID = 8043; compiled successfully, testing now...

Edit: Everything compiles successfully, the server runs without error, but having the specified item doesn't return any sort of string before the level and class in /who ... experimenting more with this

zerjz3
09-24-2014, 08:51 AM
Writing it as such

else if (ClientEntry->GetInv().HasItem(17181) > -1)
WAPP2->RankMSGID = 1504;

compiles but doesn't show anything in-game.

Drajor
09-24-2014, 09:00 AM
What client are you using?

zerjz3
09-24-2014, 09:03 AM
What client are you using?



I'm on the Titanium client... only client I have atm

Drajor
09-24-2014, 09:10 AM
I am fairly certain that anything below SoF handles /who client side :( I recommend Underfoot for everything except P99.

zerjz3
09-24-2014, 09:11 AM
Ok, I will get a copy of Underfoot and report back in this thread with my findings.

zerjz3
10-13-2014, 04:15 PM
Update: I can confirm now with the Underfoot client that this method works for the intended purpose. Thanks for the help!