PDA

View Full Version : NPCs speaking in their racial language


pfyon
06-26-2011, 11:15 AM
I finally got back to the work I started a couple of years ago with NPCs talking in languages, and here it is. I always thought languages were underused in classic EQ since outside the pvp servers (I think?), it pretty much had no use. Every NPC spoke in common and players just upped their language skills for fun, for the most part

This code does a few things. One part allows players to gain skillups in languages from say, group, guild, and shout (normally it`s just group chat I think). This lets you gain skillups both from players speaking in public channels in a language other than common, but also by listening to NPCs talking in say. I made say skillup at 10% the rate of other channels to help discourage spamming hail to NPCs to get skillups. It isn`t very effective. A better solution would be to have a max number of language skillups per time period (eg 5 a minute).

The code also changes quest::say() so it checks the base race of the NPC and chooses a language appropriate for the race.

Both the functions are set with rules in the DB and default to false in the code.

Diffs:

client.cpp
1108,1121c1108
< ^M
< /*^M
< * 0 -- Guild^M
< * 2 -- Group^M
< * 3 -- Shout^M
< * 4 -- Auction^M
< * 5 -- OOC^M
< * 6 -- Broadcast^M
< * 7 -- Tell^M
< * 8 -- say^M
< * 11 -- GMSay^M
< * 15 -- Raid^M
< */^M
< ^M
---
> ^M
1125,1149c1112,1114
< ^M
< // guild, group, shout, say in unmastered language, check for skillup^M
< ^M
< if(RuleB(Character, LearnLanguagesFromChannels))^M
< {^M
< if((chan_num == 8) && (ListenerSkill < 100))^M
< {^M
< if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName() ))^M
< {^M
< //We want it to be harder to skillup using say than group/guild/shout^M
< CheckLanguageSkillIncrease(language, lang_skill, true);^M
< }^M
< } else if ((chan_num == 0 || chan_num == 2 || chan_num == 3) && (ListenerSkill < 100))^M
< {^M
< if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName() )) ^M
< {^M
< CheckLanguageSkillIncrease(language, lang_skill, false);^M
< }^M
< }^M
< } else^M
< {^M
< if ((chan_num == 2) && (ListenerSkill < 100)) { // group message in unmastered language, check for skill up^M
< if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName() )) ^M
< CheckLanguageSkillIncrease(language, lang_skill);^M
< }^M
---
> if ((chan_num == 2) && (ListenerSkill < 100)) { // group message in unmastered language, check for skill up^M
> if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName() )) ^M
> CheckLanguageSkillIncrease(language, lang_skill);^M
2272c2237
< void Client::CheckLanguageSkillIncrease(int8 langid, int8 TeacherSkill, bool hard) {^M
---
> void Client::CheckLanguageSkillIncrease(int8 langid, int8 TeacherSkill) {^M
2282,2287d2246
< //Channel is a 'hard' one to master, since we don't want people spamming stuff to npcs for skillups^M
< if(hard)^M
< {^M
< Chance *= 0.1;^M
< }^M
< ^M
5929,5935c5888
< }^M
< ^M
< int Client::GetLanguageSkill(int skill_id)^M
< {^M
< if(skill_id > 25 || skill_id < 0) return 0;^M
< return m_pp.languages[skill_id];^M
< }^M
---
> }
\ No newline at end of file

client.h
584d583
< int GetLanguageSkill(int skill_id);^M
594c593
< void CheckLanguageSkillIncrease(int8 langid, int8 TeacherSkill, bool hard = false);^M
---
> void CheckLanguageSkillIncrease(int8 langid, int8 TeacherSkill);^M


ruletypes.h
92d91
< RULE_BOOL ( Character, LearnLanguagesFromChannels, false) // If true, allows players to gain skillups for languages heard in say, shout, guild, and group. Say is increased at 10% the chance of guild/group to discourage spamming of NPCs for skillups^M
321d319
< RULE_BOOL ( NPC, EnableNPCRacialLanguages, false) //Causes calls to quest::say to use a language that makes sense for the race of the npc talking (eg dragon for dragons, elvish for elves, etc)^M


mob.cpp
19d18
< #include "../common/languages.h"^M
5127,5570d5078
< int8 Mob::GetRacialLanguage()^M
< {^M
< switch(this->GetBaseRace())^M
< {^M
< //Common tongue races^M
< // Fall through to default for these for slight performance improvement^M
< /*case 1: //Human^M
< case 7: //"Half Elf"^M
< case 13: //Aviak^M
< case 14: //werewolf^M
< case 16: //centaur^M
< case 17: //golem^M
< case 20: //doppleganger^M
< case 22: //beetle^M
< case 24: //fish^M
< case 28: //fungusman^M
< case 30: //"Gasbag"^M
< case 31: //"Gelatinous Cube"^M
< case 32: //"Ghost"^M
< case 33: //"Ghoul"^M
< case 34: //"Giant Bat"^M
< case 35: //"Giant Eel"^M
< case 36: //"Giant Rat"^M
< case 37: //"Giant Snake"^M
< case 38: //"Giant Spider"^M
< case 41: //"Gorilla"^M
< case 42: //"Wolf"^M
< case 43: //"Bear"^M
< case 44: //"Freeport Guards"^M
< case 45: //"Demi Lich"^M
< case 47: //"Griffin"^M
< case 50: //"Lion"^M
< case 52: //"Mimic"^M
< case 53: //"Minotaur"^M
< case 55: //"Human Beggar"^M
< case 58: //"Solusek Ro"^M
< case 60: //"Skeleton"^M
< case 61: //"Shark"^M
< case 65: //"Vampire"^M
< case 63: //"Tiger"^M
< case 67: //"Highpass Citizen"^M
< case 68: //"Tentacle"^M
< case 69: //"Will 'O Wisp"^M
< case 70: //"Zombie"^M
< case 71: //"Qeynos Citizen"^M
< case 72: //"Ship"^M
< case 73: //"Launch"^M
< case 74: //"Piranha"^M
< case 75: //"Elemental"^M
< case 76: //"Puma"^M
< case 77: //"Neriak Citizen"^M
< case 78: //"Erudite Citizen"^M
< case 79: //"Bixie"^M
< case 80: //"Reanimated Hand"^M
< case 81: //"Rivervale Citizen"^M
< case 82: //"Scarecrow"^M
< case 83: //"Skunk"^M
< case 84: //"Snake Elemental"^M
< case 86: //"Sphinx"^M
< case 87: //"Armadillo"^M
< case 91: //"Alligator"^M
< case 96: //"Cockatrice"^M
< case 97: //"Daisy Man"^M
< case 99: //"Denizen"^M
< case 100: //"Dervish"^M
< case 101: //"Efreeti"^M
< case 103: //"Kedge"^M
< case 104: //"Leech"^M
< case 105: //"Swordfish"^M
< case 106: //"Felguard"^M
< case 107: //"Mammoth"^M
< case 108: //"Eye of Zomm"^M
< case 109: //"Wasp"^M
< case 110: //"Mermaid"^M
< case 111: //"Harpie"^M
< case 114: //"Ghost Ship"^M
< case 115: //"Clam"^M
< case 116: //"Sea Horse"^M
< case 119: //"Sabertooth Cat"^M
< case 120: //"Wolf Elemental"^M
< case 121: //"Gorgon"^M
< case 123: //"Innoruuk"^M
< case 124: //"Unicorn"^M
< case 125: //"Pegasus"^M
< case 126: //"Djinn"^M
< case 127: //"Invisible Man"^M
< case 129: //"Scorpion"^M
< case 133: //"Lycanthrope"^M
< case 134: //"Mosquito"^M
< case 135: //"Rhino"^M
< case 138: //"Yeti"^M
< case 140: //"Forest Giant"^M
< case 141: //"Boat"^M
< case 142: //"UNKNOWN RACE"^M
< case 143: //"UNKNOWN RACE"^M
< case 144: //"Burynai"^M
< case 145: //"Goo"^M
< case 148: //"Kunark Fish"^M
< case 149: //"Iksar Scorpion"^M
< case 150: //"Erollisi"^M
< case 156: //"Ratman"^M
< case 159: //"Devourer"^M
< case 162: //"Man Eating Plant"^M
< case 163: //"Raptor"^M
< case 167: //"Succulent"^M
< case 168: //"Flying Monkey"^M
< case 169: //"Brontotherium"^M
< case 170: //"Snow Dervish"^M
< case 171: //"Dire Wolf"^M
< case 172: //"Manticore"^M
< case 173: //"Totem"^M
< case 175: //"Enchanted Armor"^M
< case 176: //"Snow Bunny"^M
< case 177: //"Walrus"^M
< case 178: //"Rock-gem Men"^M
< case 179: //"UNKNOWN RACE"^M
< case 180: //"UNKNOWN RACE"^M
< case 185: //"Hag"^M
< case 186: //"Hippogriff"^M
< case 187: //"Siren"^M
< case 190: //"Ottermen"^M
< case 191: //"Walrus Man"^M
< case 193: //"Abhorent"^M
< case 194: //"Sea Turtle"^M
< case 197: //"Ronnie Test"^M
< case 199: //"ShikNar"^M
< case 200: //"Rockhopper"^M
< case 201: //"Underbulk"^M
< case 202: //"Grimling"^M
< case 203: //"Vacuum Worm"^M
< case 204: //"Evan Test"^M
< case 206: //"Owlbear"^M
< case 207: //"Rhino Beetle"^M
< case 208: //"Vampyre"^M
< case 209: //"Earth Elemental"^M
< case 210: //"Air Elemental"^M
< case 211: //"Water Elemental"^M
< case 212: //"Fire Elemental"^M
< case 213: //"Wetfang Minnow"^M
< case 215: //"Tegi"^M
< case 216: //"Horse"^M
< case 218: //"Fungal Fiend"^M
< case 219: //"Vampire Volatalis"^M
< case 220: //"StoneGrabber"^M
< case 221: //"Scarlet Cheetah"^M
< case 222: //"Zelniak"^M
< case 223: //"Lightcrawler"^M
< case 225: //"Sunflower"^M
< case 226: //"Sun Revenant"^M
< case 227: //"Shrieker"^M
< case 228: //"Galorian"^M
< case 229: //"Netherbian"^M
< case 230: //"Akheva"^M
< case 231: //"Spire Spirit"^M
< case 232: //"Sonic Wolf"^M
< case 233: //"Ground Shaker"^M
< case 235: //"Mutant Humanoid"^M
< case 236: //"Seru"^M
< case 237: //"Recuso"^M
< case 240: //"Teleport Man"^M
< case 241: //"Lujein"^M
< case 242: //"Naiad"^M
< case 243: //"Nymph"^M
< case 244: //"Ent"^M
< case 245: //"Fly Man"^M
< case 246: //"Tarew Marr"^M
< case 247: //"Sol Ro"^M
< case 250: //"Spectral Banshee"^M
< case 251: //"Guard of Justice"^M
< case 252: //"UNKNOWN RACE"^M
< case 253: //"Disease Boss"^M
< case 254: //"Sol Ro Guard"^M
< case 258: //"Vegerog"^M
< case 259: //"Crocodile"^M
< case 260: //"Bat"^M
< case 261: //"Slarghilug"^M
< case 262: //"Tranquilion"^M
< case 263: //"Tin Soldier"^M
< case 271: //"Water Mephit"^M
< case 272: //"Stormrider"^M
< case 278: //"Karana"^M
< case 284: //"Fennin Ro"^M
< case 291: //"Air Mephit"^M
< case 292: //"Earth Mephit"^M
< case 293: //"Fire Mephit"^M
< case 295: //"Zebuxoruk"^M
< case 296: //"Mithaniel Marr"^M
< case 298: //"The Rathe"^M
< case 299: //"Xegony"^M
< case 301: //"Test Object"^M
< case 302: //"Crab"^M
< case 303: //"Phoenix"^M
< case 304: //"PoP Dragon"^M
< case 305: //"PoP Bear"^M
< case 306: //"Storm Taarid"^M
< case 307: //"Storm Satuur"^M
< case 308: //"Storm Kuraaln"^M
< case 309: //"Storm Volaas"^M
< case 310: //"Storm Mana"^M
< case 311: //"Storm Fire"^M
< case 312: //"Storm Celestial"^M
< case 313: //"War Wraith"^M
< case 314: //"Wrulon"^M
< case 315: //"Kraken"^M
< case 316: //"Poison Frog"^M
< case 317: //"Queztocoatal"^M
< case 318: //"Valorian"^M
< case 319: //"War Boar"^M
< case 320: //"PoP Efreeti"^M
< case 321: //"War Boar Unarmored"^M
< case 322: //"Black Knight"^M
< case 323: //"Animated Armor"^M
< case 326: //"Arachnid"^M
< case 327: //"Crystal Spider"^M
< case 328: //"Zeb Cage"^M
< case 329: //"BoT Portal"^M
< case 334: //"Spectre Pirate Boss"^M
< case 335: //"Pirate Boss"^M
< case 336: //"Pirate Dark Shaman"^M
< case 337: //"Pirate Officer"^M
< case 341: //"Human Pirate"^M
< case 343: //"Poison Dart Frog"^M
< case 345: //"Luggald Land"^M
< case 346: //"Luggald Armored"^M
< case 347: //"Luggald Robed"^M
< case 351: //"Chosen Warrior"^M
< case 352: //"Chosen Wizard"^M
< case 353: //"Veksar"^M
< case 354: //"Greater Veksar"^M
< case 355: //"Veksar Boss"^M
< case 356: //"Chokadai"^M
< case 357: //"Undead Chokadai"^M
< case 358: //"Undead Veksar"^M
< case 359: //"Vampire Lesser"^M
< case 360: //"Vampire Elite"^M
< case 362: //"Bone Golem"^M
< case 363: //"Synarcana"^M
< case 365: //"Vampire Master"^M
< case 367: //"Skeleton New"^M
< case 368: //"Mummy New"^M
< case 370: //"Insect"^M
< case 372: //"Dervish New"^M
< case 374: //"Golem New"^M
< case 376: //"Box"^M
< case 377: //"Barrel"^M
< case 378: //"Chest"^M
< case 379: //"Vase"^M
< case 380: //"Table"^M
< case 381: //"Weapons Rack"^M
< case 382: //"Coffin"^M
< case 383: //"Bones"^M
< case 384: //"Jokester"^M
< case 385: //"Talosian Nihil"^M
< case 386: //"Talosian Exile"^M
< case 387: //"Talosian Golem"^M
< case 388: //"Talosian Wolf"^M
< case 389: //"Talosian Amphibian"^M
< case 390: //"Talosian Mountain Beast"^M
< case 391: //"Talosian Trilobyte"^M
< case 392: //"Invader War Hound"^M
< case 393: //"Invader Elite Centaur"^M
< case 394: //"Invader Lamia"^M
< case 395: //"Invader Cyclops"^M
< case 396: //"Kyv"^M
< case 397: //"Invader Soldier"^M
< case 398: //"Invader Brute"^M
< case 399: //"Invader Force Commander"^M
< case 400: //"Invader Lieutenant Boss"^M
< case 401: //"Invader War Beast"^M
< case 402: //"Invader Soldier Elite"^M
< case 403: //"UNKNOWN RACE"^M
< case 404: //"Discord Ship"^M
< return LANG_COMMON_TONGUE;*/^M
< //Barbarian^M
< case 2: //"Barbarian"^M
< case 90: //"Halas Citizen"^M
< case 151: //"Tribunal"^M
< case 256: //"New Tribunal"^M
< return LANG_BARBARIAN;^M
< //Erudite^M
< case 3: //"Erudite"^M
< case 118: //"Erudite Ghost"^M
< case 342: //"Erudite Pirate"^M
< return LANG_ERUDIAN;^M
< //Elvish^M
< case 4: //"Wood Elf"^M
< case 5: //"High Elf"^M
< case 64: //"Treant"^M
< case 98: //"Elf Vampire"^M
< case 112: //"Fayguard"^M
< case 181: //"Yak Man"^M
< case 182: //"Faun"^M
< case 364: //"Sand Elf"^M
< return LANG_ELVISH;^M
< //Dark Elvish^M
< case 6: //"Dark Elf"^M
< case 339: //"Dark Elf Pirate"^M
< return LANG_DARK_ELVISH;^M
< //Dwarvish^M
< case 8: //"Dwarf"^M
< case 94: //"Kaladim Citizen"^M
< case 117: //"Ghost Dwarf"^M
< case 183: //"Coldain"^M
< return LANG_DWARVISH;^M
< //Troll^M
< case 9: //"Troll"^M
< case 92: //"Grobb Citizen"^M
< case 95: //"Cazic Thule"^M
< case 331: //"Troll Buccaneer"^M
< case 332: //"Troll Freebooter"^M
< case 333: //"Troll Sea Rover"^M
< case 344: //"Troll Zombie"^M
< return LANG_TROLL;^M
< //Ogre^M
< case 10: //"Ogre"^M
< case 18: //"Giant / Cyclops"^M
< case 66: //"Rallos Zek"^M
< case 93: //"Oggok Citizen"^M
< case 188: //"Frost Giant"^M
< case 189: //"Storm Giant"^M
< case 288: //"New Rallos Zek"^M
< case 289: //"Vallon Zek"^M
< case 290: //"Tallon Zek"^M
< case 325: //"Rallos Zek Minion"^M
< case 340: //"Ogre Pirate"^M
< return LANG_OGRE;^M
< //Halfling^M
< case 11: //"Halfling"^M
< case 153: //"Bristlebane"^M
< return LANG_HALFLING;^M
< //Gnomish^M
< case 12: //"Gnome"^M
< case 88: //"Clockwork Gnome"^M
< case 192: //"Clockwork Dragon"^M
< case 248: //"Clockwork Golem"^M
< case 249: //"Clockwork Brain"^M
< case 273: //"Junk Beast"^M
< case 274: //"Broken Clockwork"^M
< case 275: //"Giant Clockwork"^M
< case 276: //"Clockwork Beetle"^M
< case 338: //"Gnome Pirate"^M
< return LANG_GNOMISH;^M
< case 15: //"Brownie"^M
< case 25: //"Fairy"^M
< case 56: //"Pixie"^M
< case 113: //"Drixie"^M
< case 154: //"Fay Drake"^M
< return LANG_FAERIE;^M
< case 19: //"Trakenon"^M
< case 198: //"Prismatic Dragon"^M
< return LANG_ELDER_DRAGON;^M
< case 21: //"Evil Eye"^M
< case 29: //"Gargoyle"^M
< case 46: //"Imp"^M
< case 57: //"Dracnid"^M
< case 85: //"Spectre"^M
< case 152: //"Bertoxxulous"^M
< case 174: //"Cold Spectre"^M
< case 214: //"Thought Horror"^M
< case 224: //"Shade"^M
< case 255: //"New Bertox"^M
< case 257: //"Terris Thule"^M
< case 264: //"Nightmare Wraith"^M
< case 265: //"Malarian"^M
< case 266: //"Knight of Pestilence"^M
< case 267: //"Lepertoloth"^M
< case 268: //"Bubonian Boss"^M
< case 269: //"Bubonian Underling"^M
< case 270: //"Pusling"^M
< case 279: //"Blood Raven"^M
< case 280: //"Nightmare Gargoyle"^M
< case 281: //"Mouths of Insanity"^M
< case 282: //"Skeletal Horse"^M
< case 283: //"Saryn"^M
< case 285: //"Tormentor"^M
< case 286: //"Necro Priest"^M
< case 287: //"Nightmare"^M
< case 294: //"Nightmare Mephit"^M
< case 297: //"Undead Knight"^M
< case 300: //"Fiend"^M
< case 324: //"Undead Footman"^M
< case 373: //"Shadow Creatue"^M
< case 375: //"Evil Eye New"^M
< return LANG_DARK_SPEECH;^M
< case 26: //"Old Froglok"^M
< case 27: //"Old Froglok Ghoul"^M
< case 102: //"Old Froglok Tadpole"^M
< case 277: //"Nightmare Goblin"^M
< case 330: //"Froglok"^M
< case 348: //"Froglok Mount"^M
< case 349: //"Froglok Skeleton"^M
< case 350: //"Undead Froglok"^M
< case 371: //"Froglok Ghost"^M
< return LANG_FROGLOK;^M
< case 39: //"Gnoll"^M
< return LANG_GNOLL;^M
< case 40: //"Goblin"^M
< case 48: //"Kobold"^M
< case 59: //"Bloodgills"^M
< case 137: //"Kunark Goblin"^M
< case 369: //"Goblin New"^M
< return LANG_GOBLIN;^M
< case 49: //"Lava Dragon"^M
< case 89: //"Drake"^M
< case 122: //"Dragon Skeleton"^M
< case 157: //"Wyvern"^M
< case 158: //"Wurm"^M
< case 165: //"Water Dragon"^M
< case 184: //"Velious Dragons"^M
< case 195: //"Black and White Dragons"^M
< case 196: //"Ghost Dragon"^M
< return LANG_DRAGON;^M
< case 51: //"Lizard Man"^M
< case 128: //"Iksar"^M
< case 131: //"Sarnak"^M
< case 132: //"Draglock"^M
< case 136: //"Xalgoz"^M
< case 139: //"Iksar Citizen"^M
< case 146: //"Spectral Sarnak"^M
< case 147: //"Spectral Iksar"^M
< case 155: //"Sarnak Skeleton"^M
< case 160: //"Iksar Golem"^M
< case 161: //"Iksar Skeleton"^M
< case 164: //"Sarnak Golem"^M
< case 166: //"Iksar Hand"^M
< case 217: //"Shissar"^M
< return LANG_LIZARDMAN;^M
< case 54: //"Orc"^M
< case 361: //"Rujakian Orc"^M
< case 366: //"Rujakian Orc Elite"^M
< return LANG_ORCISH;^M
< case 62: //"Tunare"^M
< return LANG_ELDER_ELVISH;^M
< case 130: //"Vah Shir"^M
< case 205: //"Kahli Shah"^M
< case 234: //"Vah Shir Skeleton"^M
< case 238: //"Vah Shir King"^M
< case 239: //"Vah Shir Guard"^M
< return LANG_VAH_SHIR;^M
< default:^M
< return LANG_COMMON_TONGUE;^M
< }^M
< }^M
< ^M
5579c5087
< }^M
---
> }
\ No newline at end of file


mob.h
609,610d604
< int8 GetRacialLanguage();^M
< ^M


questmgr.cpp
212,241c212,217
< void QuestManager::say(const char *str)^M
< {^M
< //EnableNPCRacialLanguages seems to be mutually exclusive with the quest journal^M
< if(RuleB(NPC, EnableNPCRacialLanguages) && initiator)^M
< {^M
< int8 owner_language = owner->GetRacialLanguage(); ^M
< if(initiator->IsClient())^M
< {^M
< //If they aren't clients, they can talk all they want^M
< if(initiator->GetLanguageSkill(owner_language) > 50)^M
< {^M
< quest_manager.say(str, owner_language);^M
< } else^M
< {^M
< quest_manager.say("I'm sorry, I don't understand a word you're saying.", owner_language);^M
< }^M
< } else^M
< {^M
< quest_manager.say(str, owner_language);^M
< }^M
< } else^M
< {^M
< if(RuleB(NPC, EnableNPCQuestJournal) && initiator)^M
< {^M
< owner->QuestJournalledSay(initiator, str);^M
< } else^M
< {^M
< owner->Say(str);^M
< }^M
< }^M
---
> void QuestManager::say(const char *str) {^M
> ^M
> if(RuleB(NPC, EnableNPCQuestJournal) && initiator)^M
> owner->QuestJournalledSay(initiator, str);^M
> else^M
> owner->Say(str);^M
2609c2585
< }^M
---
> }
\ No newline at end of file


Optional SQL:
INSERT INTO rule_values (ruleset_id, rule_name, rule_value, notes) values (1, 'NPC:EnableNPCRacialLanguages', 0, 'Enables quest::say() to use a race-specific language when called without the second argument');
INSERT INTO rule_values (ruleset_id, rule_name, rule_value, notes) values (1, 'Character:LearnLanguagesFromChannels', 0, 'Enables players to learn languages from say, guild, group, and shout');

pfyon
06-26-2011, 11:22 AM
I`ll also note that it seems that the quest journal and the racial languages are mutually exclusive. The quest journal looks like it uses a special packet which the client interprets as the npc saying the text, as well as inserting it into the quest journal. If someone knows if it`s able to change that behaviour, please, tell me.

pfyon
06-26-2011, 12:19 PM
Whoops, double also: if the client`s skill in the language that the NPC is using is less than 50, they`ll reply with `I can`t understand a word you`re saying`, in the NPC`s language.

Sorry for the weird quotation marks, my keyboard likes to turn into a different language when it goes into suspend and back again.

pfyon
06-26-2011, 01:46 PM
Triple also, I`m not sure where I got the list of races from. I found it in one of the source files, possibly from work I started on a while ago.

The languages I chose for each race are best guesses. If you see a race-language combo that doesn`t work (lore wise or otherwise), let me know so I can update it.

Caryatis
06-26-2011, 01:56 PM
This code wont compile^M

Robregen
06-26-2011, 02:49 PM
I know on Firiona Vie Server, common language is disabled and players are force to learn other race language before they can communicate with each other.

pfyon
06-26-2011, 05:44 PM
Yeah not sure what the ^M thing is about, I had to use regular diff rather than svn-diff, also via putty to a linux box, so probably related to encoding.

s/\^M.*$//g Would probably do the trick.

lerxst2112
06-26-2011, 05:59 PM
^M is a carriage return. It's a mismatch of windows style line endings and unix style line endings.

pfyon
06-26-2011, 08:09 PM
Well, since the edit timer is long gone on that post, I put the diff files on my webserver.

client.cpp.diff (http://peterfyon.homeunix.com/files/client.cpp.diff)
client.h.diff (http://peterfyon.homeunix.com/files/client.h.diff)
mob.cpp.diff (http://peterfyon.homeunix.com/files/mob.cpp.diff)
mob.h.diff (http://peterfyon.homeunix.com/files/mob.h.diff)
questmgr.cpp.diff (http://peterfyon.homeunix.com/files/questmgr.cpp.diff)
ruletypes.h.diff (http://peterfyon.homeunix.com/files/ruletypes.h.diff)

edit: and to clarify, without the line ending characters

rencro
10-13-2013, 08:07 PM
Updated for 10-13-2013 on git

client.cpp

At --

void Client::ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...) {
.
.
.
cm->chan_num = chan_num;
strcpy(&cm->message[0], buffer);
QueuePacket(&app);

if ((chan_num == 2) && (ListenerSkill < 100)) { // group message in unmastered language, check for skill up
if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName()))
CheckLanguageSkillIncrease(language, lang_skill);
}
}

CHANGE TO

cm->chan_num = chan_num;
strcpy(&cm->message[0], buffer);
QueuePacket(&app);

//pfyon language changes
if((chan_num == 8) && (ListenerSkill < 100)) {
if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName() ))
{
//We want it to be harder to skillup using say than group/guild/shout
CheckLanguageSkillIncrease(language, lang_skill, true);
}
} else if ((chan_num == 0 || chan_num == 2 || chan_num == 3) && (ListenerSkill < 100))
{
if ((m_pp.languages[language] <= lang_skill) && (from != this->GetName() ))
{
CheckLanguageSkillIncrease(language, lang_skill, false);
}
}
}


At --
void Client::CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill) {
if (langid >= MAX_PP_LANGUAGE)
return; // do nothing if langid is an invalid language

int LangSkill = m_pp.languages[langid]; // get current language skill

if (LangSkill < 100) { // if the language isn't already maxed
int16 Chance = 5 + ((TeacherSkill - LangSkill)/10); // greater chance to learn if teacher's skill is much higher than yours
Chance = (Chance * RuleI(Character, SkillUpModifier)/100);

if(MakeRandomFloat(0,100) < Chance) { // if they make the roll


CHANGE TO

//pfyon language
void Client::CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill, bool hard) {
if (langid >= MAX_PP_LANGUAGE)
return; // do nothing if langid is an invalid language

int LangSkill = m_pp.languages[langid]; // get current language skill

if (LangSkill < 100) { // if the language isn't already maxed
int16 Chance = 5 + ((TeacherSkill - LangSkill)/10); // greater chance to learn if teacher's skill is much higher than yours
Chance = (Chance * RuleI(Character, SkillUpModifier)/100);

//Channel is a 'hard' one to master, since we don't want people spamming stuff to npcs for skillups
if(hard) {
Chance *= 0.1;
}

if(MakeRandomFloat(0,100) < Chance) { // if they make the roll



And at the end of the file add:

//pfyon languages
int Client::GetOwnerLanguageSkill(int skill_id) {
if(skill_id > 25 || skill_id < 0) return 0;
return m_pp.languages[skill_id];
}




client.h

At --
void CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill);
void SetLanguageSkill(int langid, int value);
void SetHoTT(uint32 mobid);
void ShowSkillsWindow();
void SendStatsWindow(Client* client, bool use_window);

uint16 MaxSkill(SkillType skillid, uint16 class_, uint16 level) const;

CHANGE TO --

void CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill, bool hard = false); //pfyon languages
void SetLanguageSkill(int langid, int value);
void SetHoTT(uint32 mobid);
void ShowSkillsWindow();
void SendStatsWindow(Client* client, bool use_window);
int GetOwnerLanguageSkill(int skill_id); //pyfon languages

uint16 MaxSkill(SkillType skillid, uint16 class_, uint16 level) const;


mob.cpp

near the beggining at --

#include "worldserver.h"
#include "QuestParserCollection.h"
#include "../common/StringUtil.h"

#include <sstream>

CHANGE TO --

#include "worldserver.h"
#include "QuestParserCollection.h"
#include "../common/StringUtil.h"
#include "../common/languages.h" //pfyon languages

#include <sstream>

At the end of the file ADD --


//pfyon languages
int8 Mob::GetRacialLanguage() {
switch(this->GetBaseRace())
{
//Common tongue races
// Fall through to default for these for slight performance improvement
/*case 1: //Human
case 7: //"Half Elf"
case 13: //Aviak
case 14: //werewolf
case 16: //centaur
case 17: //golem
case 20: //doppleganger
case 22: //beetle
case 24: //fish
case 28: //fungusman
case 30: //"Gasbag"
case 31: //"Gelatinous Cube"
case 32: //"Ghost"
case 33: //"Ghoul"
case 34: //"Giant Bat"
case 35: //"Giant Eel"
case 36: //"Giant Rat"
case 37: //"Giant Snake"
case 38: //"Giant Spider"
case 41: //"Gorilla"
case 42: //"Wolf"
case 43: //"Bear"
case 44: //"Freeport Guards"
case 45: //"Demi Lich"
case 47: //"Griffin"
case 50: //"Lion"
case 52: //"Mimic"
case 53: //"Minotaur"
case 55: //"Human Beggar"
case 58: //"Solusek Ro"
case 60: //"Skeleton"
case 61: //"Shark"
case 65: //"Vampire"
case 63: //"Tiger"
case 67: //"Highpass Citizen"
case 68: //"Tentacle"
case 69: //"Will 'O Wisp"
case 70: //"Zombie"
case 71: //"Qeynos Citizen"
case 72: //"Ship"
case 73: //"Launch"
case 74: //"Piranha"
case 75: //"Elemental"
case 76: //"Puma"
case 77: //"Neriak Citizen"
case 78: //"Erudite Citizen"
case 79: //"Bixie"
case 80: //"Reanimated Hand"
case 81: //"Rivervale Citizen"
case 82: //"Scarecrow"
case 83: //"Skunk"
case 84: //"Snake Elemental"
case 86: //"Sphinx"
case 87: //"Armadillo"
case 91: //"Alligator"
case 96: //"Cockatrice"
case 97: //"Daisy Man"
case 99: //"Denizen"
case 100: //"Dervish"
case 101: //"Efreeti"
case 103: //"Kedge"
case 104: //"Leech"
case 105: //"Swordfish"
case 106: //"Felguard"
case 107: //"Mammoth"
case 108: //"Eye of Zomm"
case 109: //"Wasp"
case 110: //"Mermaid"
case 111: //"Harpie"
case 114: //"Ghost Ship"
case 115: //"Clam"
case 116: //"Sea Horse"
case 119: //"Sabertooth Cat"
case 120: //"Wolf Elemental"
case 121: //"Gorgon"
case 123: //"Innoruuk"
case 124: //"Unicorn"
case 125: //"Pegasus"
case 126: //"Djinn"
case 127: //"Invisible Man"
case 129: //"Scorpion"
case 133: //"Lycanthrope"
case 134: //"Mosquito"
case 135: //"Rhino"
case 138: //"Yeti"
case 140: //"Forest Giant"
case 141: //"Boat"
case 142: //"UNKNOWN RACE"
case 143: //"UNKNOWN RACE"
case 144: //"Burynai"
case 145: //"Goo"
case 148: //"Kunark Fish"
case 149: //"Iksar Scorpion"
case 150: //"Erollisi"
case 156: //"Ratman"
case 159: //"Devourer"
case 162: //"Man Eating Plant"
case 163: //"Raptor"
case 167: //"Succulent"
case 168: //"Flying Monkey"
case 169: //"Brontotherium"
case 170: //"Snow Dervish"
case 171: //"Dire Wolf"
case 172: //"Manticore"
case 173: //"Totem"
case 175: //"Enchanted Armor"
case 176: //"Snow Bunny"
case 177: //"Walrus"
case 178: //"Rock-gem Men"
case 179: //"UNKNOWN RACE"
case 180: //"UNKNOWN RACE"
case 185: //"Hag"
case 186: //"Hippogriff"
case 187: //"Siren"
case 190: //"Ottermen"
case 191: //"Walrus Man"
case 193: //"Abhorent"
case 194: //"Sea Turtle"
case 197: //"Ronnie Test"
case 199: //"ShikNar"
case 200: //"Rockhopper"
case 201: //"Underbulk"
case 202: //"Grimling"
case 203: //"Vacuum Worm"
case 204: //"Evan Test"
case 206: //"Owlbear"
case 207: //"Rhino Beetle"
case 208: //"Vampyre"
case 209: //"Earth Elemental"
case 210: //"Air Elemental"
case 211: //"Water Elemental"
case 212: //"Fire Elemental"
case 213: //"Wetfang Minnow"
case 215: //"Tegi"
case 216: //"Horse"
case 218: //"Fungal Fiend"
case 219: //"Vampire Volatalis"
case 220: //"StoneGrabber"
case 221: //"Scarlet Cheetah"
case 222: //"Zelniak"
case 223: //"Lightcrawler"
case 225: //"Sunflower"
case 226: //"Sun Revenant"
case 227: //"Shrieker"
case 228: //"Galorian"
case 229: //"Netherbian"
case 230: //"Akheva"
case 231: //"Spire Spirit"
case 232: //"Sonic Wolf"
case 233: //"Ground Shaker"
case 235: //"Mutant Humanoid"
case 236: //"Seru"
case 237: //"Recuso"
case 240: //"Teleport Man"
case 241: //"Lujein"
case 242: //"Naiad"
case 243: //"Nymph"
case 244: //"Ent"
case 245: //"Fly Man"
case 246: //"Tarew Marr"
case 247: //"Sol Ro"
case 250: //"Spectral Banshee"
case 251: //"Guard of Justice"
case 252: //"UNKNOWN RACE"
case 253: //"Disease Boss"
case 254: //"Sol Ro Guard"
case 258: //"Vegerog"
case 259: //"Crocodile"
case 260: //"Bat"
case 261: //"Slarghilug"
case 262: //"Tranquilion"
case 263: //"Tin Soldier"
case 271: //"Water Mephit"
case 272: //"Stormrider"
case 278: //"Karana"
case 284: //"Fennin Ro"
case 291: //"Air Mephit"
case 292: //"Earth Mephit"
case 293: //"Fire Mephit"
case 295: //"Zebuxoruk"
case 296: //"Mithaniel Marr"
case 298: //"The Rathe"
case 299: //"Xegony"
case 301: //"Test Object"
case 302: //"Crab"
case 303: //"Phoenix"
case 304: //"PoP Dragon"
case 305: //"PoP Bear"
case 306: //"Storm Taarid"
case 307: //"Storm Satuur"
case 308: //"Storm Kuraaln"
case 309: //"Storm Volaas"
case 310: //"Storm Mana"
case 311: //"Storm Fire"
case 312: //"Storm Celestial"
case 313: //"War Wraith"
case 314: //"Wrulon"
case 315: //"Kraken"
case 316: //"Poison Frog"
case 317: //"Queztocoatal"
case 318: //"Valorian"
case 319: //"War Boar"
case 320: //"PoP Efreeti"
case 321: //"War Boar Unarmored"
case 322: //"Black Knight"
case 323: //"Animated Armor"
case 326: //"Arachnid"
case 327: //"Crystal Spider"
case 328: //"Zeb Cage"
case 329: //"BoT Portal"
case 334: //"Spectre Pirate Boss"
case 335: //"Pirate Boss"
case 336: //"Pirate Dark Shaman"
case 337: //"Pirate Officer"
case 341: //"Human Pirate"
case 343: //"Poison Dart Frog"
case 345: //"Luggald Land"
case 346: //"Luggald Armored"
case 347: //"Luggald Robed"
case 351: //"Chosen Warrior"
case 352: //"Chosen Wizard"
case 353: //"Veksar"
case 354: //"Greater Veksar"
case 355: //"Veksar Boss"
case 356: //"Chokadai"
case 357: //"Undead Chokadai"
case 358: //"Undead Veksar"
case 359: //"Vampire Lesser"
case 360: //"Vampire Elite"
case 362: //"Bone Golem"
case 363: //"Synarcana"
case 365: //"Vampire Master"
case 367: //"Skeleton New"
case 368: //"Mummy New"
case 370: //"Insect"
case 372: //"Dervish New"
case 374: //"Golem New"
case 376: //"Box"
case 377: //"Barrel"
case 378: //"Chest"
case 379: //"Vase"
case 380: //"Table"
case 381: //"Weapons Rack"
case 382: //"Coffin"
case 383: //"Bones"
case 384: //"Jokester"
case 385: //"Talosian Nihil"
case 386: //"Talosian Exile"
case 387: //"Talosian Golem"
case 388: //"Talosian Wolf"
case 389: //"Talosian Amphibian"
case 390: //"Talosian Mountain Beast"
case 391: //"Talosian Trilobyte"
case 392: //"Invader War Hound"
case 393: //"Invader Elite Centaur"
case 394: //"Invader Lamia"
case 395: //"Invader Cyclops"
case 396: //"Kyv"
case 397: //"Invader Soldier"
case 398: //"Invader Brute"
case 399: //"Invader Force Commander"
case 400: //"Invader Lieutenant Boss"
case 401: //"Invader War Beast"
case 402: //"Invader Soldier Elite"
case 403: //"UNKNOWN RACE"
case 404: //"Discord Ship"
return LANG_COMMON_TONGUE;*/ //language 0
//Barbarian
case 2: //"Barbarian"
case 90: //"Halas Citizen"
case 151: //"Tribunal"
case 256: //"New Tribunal"
return LANG_BARBARIAN; //language 1
//Erudite
case 3: //"Erudite"
case 118: //"Erudite Ghost"
case 342: //"Erudite Pirate"
return LANG_ERUDIAN; //language 2
//Elvish
case 4: //"Wood Elf"
case 5: //"High Elf"
case 64: //"Treant"
case 98: //"Elf Vampire"
case 112: //"Fayguard"
case 181: //"Yak Man"
case 182: //"Faun"
case 364: //"Sand Elf"
return LANG_ELVISH; //language 3
//Dark Elvish
case 6: //"Dark Elf"
case 339: //"Dark Elf Pirate"
return LANG_DARK_ELVISH; //language 4
//Dwarvish
case 8: //"Dwarf"
case 94: //"Kaladim Citizen"
case 117: //"Ghost Dwarf"
case 183: //"Coldain"
return LANG_DWARVISH; //language 5
//Troll
case 9: //"Troll"
case 92: //"Grobb Citizen"
case 95: //"Cazic Thule"
case 331: //"Troll Buccaneer"
case 332: //"Troll Freebooter"
case 333: //"Troll Sea Rover"
case 344: //"Troll Zombie"
return LANG_TROLL; //language 6
//Ogre
case 10: //"Ogre"
case 18: //"Giant / Cyclops"
case 66: //"Rallos Zek"
case 93: //"Oggok Citizen"
case 188: //"Frost Giant"
case 189: //"Storm Giant"
case 288: //"New Rallos Zek"
case 289: //"Vallon Zek"
case 290: //"Tallon Zek"
case 325: //"Rallos Zek Minion"
case 340: //"Ogre Pirate"
return LANG_OGRE; //language 7
//Gnomish
case 12: //"Gnome"
case 88: //"Clockwork Gnome"
case 192: //"Clockwork Dragon"
case 248: //"Clockwork Golem"
case 249: //"Clockwork Brain"
case 273: //"Junk Beast"
case 274: //"Broken Clockwork"
case 275: //"Giant Clockwork"
case 276: //"Clockwork Beetle"
case 338: //"Gnome Pirate"
return LANG_GNOMISH; //language 8
//Halfling
case 11: //"Halfling"
case 153: //"Bristlebane"
return LANG_HALFLING; //language 9
//ThievesCant
//return LANG_THIEVES_CANT; //language 10
//OldErudian
//return LANG_OLD_ERUDIAN; //language 11
//Tunare
case 62: //"Tunare"
return LANG_ELDER_ELVISH; //language 12
//froglok
case 26: //"Old Froglok"
case 27: //"Old Froglok Ghoul"
case 102: //"Old Froglok Tadpole"
case 277: //"Nightmare Goblin"
case 330: //"Froglok"
case 348: //"Froglok Mount"
case 349: //"Froglok Skeleton"
case 350: //"Undead Froglok"
case 371: //"Froglok Ghost"
return LANG_FROGLOK; //language 13
//Goblins
case 40: //"Goblin"
case 48: //"Kobold"
case 59: //"Bloodgills"
case 137: //"Kunark Goblin"
case 369: //"Goblin New"
return LANG_GOBLIN; //language 14
//Gnolls
case 39: //"Gnoll"
return LANG_GNOLL; //language 15
//Combine
//return LANG_COMBINE_TONGUE; //language 16
//Elder Tierdal
//return LANG_ELDER_TEIRDAL; //language 17
//lizardmen kunark
case 51: //"Lizard Man"
case 128: //"Iksar"
case 131: //"Sarnak"
case 132: //"Draglock"
case 136: //"Xalgoz"
case 139: //"Iksar Citizen"
case 146: //"Spectral Sarnak"
case 147: //"Spectral Iksar"
case 155: //"Sarnak Skeleton"
case 160: //"Iksar Golem"
case 161: //"Iksar Skeleton"
case 164: //"Sarnak Golem"
case 166: //"Iksar Hand"
case 217: //"Shissar"
return LANG_LIZARDMAN; //language 18
//orcs
case 54: //"Orc"
case 361: //"Rujakian Orc"
case 366: //"Rujakian Orc Elite"
return LANG_ORCISH; //language 19
//Fairies
case 15: //"Brownie"
case 25: //"Fairy"
case 56: //"Pixie"
case 113: //"Drixie"
case 154: //"Fay Drake"
return LANG_FAERIE; //language 20
//dragons
case 49: //"Lava Dragon"
case 89: //"Drake"
case 122: //"Dragon Skeleton"
case 157: //"Wyvern"
case 158: //"Wurm"
case 165: //"Water Dragon"
case 184: //"Velious Dragons"
case 195: //"Black and White Dragons"
case 196: //"Ghost Dragon"
return LANG_DRAGON; //language 21
//trakanon
case 19: //"Trakenon"
case 198: //"Prismatic Dragon"
return LANG_ELDER_DRAGON; //language 22
//darkspeech
case 21: //"Evil Eye"
case 29: //"Gargoyle"
case 46: //"Imp"
case 57: //"Dracnid"
case 85: //"Spectre"
case 152: //"Bertoxxulous"
case 174: //"Cold Spectre"
case 214: //"Thought Horror"
case 224: //"Shade"
case 255: //"New Bertox"
case 257: //"Terris Thule"
case 264: //"Nightmare Wraith"
case 265: //"Malarian"
case 266: //"Knight of Pestilence"
case 267: //"Lepertoloth"
case 268: //"Bubonian Boss"
case 269: //"Bubonian Underling"
case 270: //"Pusling"
case 279: //"Blood Raven"
case 280: //"Nightmare Gargoyle"
case 281: //"Mouths of Insanity"
case 282: //"Skeletal Horse"
case 283: //"Saryn"
case 285: //"Tormentor"
case 286: //"Necro Priest"
case 287: //"Nightmare"
case 294: //"Nightmare Mephit"
case 297: //"Undead Knight"
case 300: //"Fiend"
case 324: //"Undead Footman"
case 373: //"Shadow Creatue"
case 375: //"Evil Eye New"
return LANG_DARK_SPEECH; //language 23
//vah shir
case 130: //"Vah Shir"
case 205: //"Kahli Shah"
case 234: //"Vah Shir Skeleton"
case 238: //"Vah Shir King"
case 239: //"Vah Shir Guard"
return LANG_VAH_SHIR; //language 24
//Alaran
//return LANG_ALARAN; //language 25
//Hadal
//return LANG_HADAL; //language 26
//Unk
//return LANG_UNKNOWN; //language 27
default:
return LANG_COMMON_TONGUE; //language 0
}
}



mob.h

At --

void QuestJournalledSay(Client *QuestInitiator, const char *str);
uint32 GetItemStat(uint32 itemid, const char *identifier);

int16 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false);

CHANGE TO --

void QuestJournalledSay(Client *QuestInitiator, const char *str);
uint32 GetItemStat(uint32 itemid, const char *identifier);
int8 GetRacialLanguage(); //pyfon languages

int16 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false);



questmgr.cpp

At --

void QuestManager::say(const char *str) {
QuestManagerCurrentQuestVars();
if (!owner) {
LogFile->write(EQEMuLog::Quest, "QuestManager::say called with nullptr owner. Probably syntax error in quest file.");
return;
}
else {
if(RuleB(NPC, EnableNPCQuestJournal) && initiator) {
owner->QuestJournalledSay(initiator, str);
}
else {
owner->Say(str);
}
}
}

CHANGE TO --

//pfyon languages
void QuestManager::say(const char *str) {
QuestManagerCurrentQuestVars();
if (!owner) {
LogFile->write(EQEMuLog::Quest, "QuestManager::say called with nullptr owner. Probably syntax error in quest file.");
return;
}
else {
//EnableNPCRacialLanguages seems to be mutually exclusive with the quest journal
if(initiator) {
int8 owner_language = owner->GetRacialLanguage();
if(initiator->IsClient()) {
//If they aren't clients, they can talk all they want
if(initiator->GetOwnerLanguageSkill(owner_language) > 50) {
quest_manager.say(str, owner_language);
}
else {
quest_manager.say("I'm sorry, I don't understand a word you're saying.", owner_language);
}
}
else {
quest_manager.say(str, owner_language);
}
}
else {
if(RuleB(NPC, EnableNPCQuestJournal) && initiator)
{
owner->QuestJournalledSay(initiator, str);
}
else {
owner->Say(str);
}
}
}
}



I didnt post in diff format because 1- I was too lazy to take all my other customizations out of the diff and 2, diffs become more problematic to patch in the more times a file is modified, and this project is ever evolving.

If pfyon is still around perhaps he can weigh in on any new changes he made, But in this case I removed the rules, as in my opinion, custom code shouldn't have rules, else you wouldn't put the custom code in..I also added in the rest of the languages, but did not customize the race list..