Go Back   EQEmulator Home > EQEmulator Forums > Development > Development::Server Code Submissions

Reply
 
Thread Tools Display Modes
  #1  
Old 03-07-2009, 12:54 PM
realityincarnate
Developer
 
Join Date: Dec 2007
Posts: 122
Default Learning Languages

This fix allows players to teach each other languages while grouped. It only works while grouped and in the same zone. I think the rate of learning feels about right, but it could be tinkered with. I also set the rate to be modified by the SkillUpModifier rule.

Can anyone point me to a good XS reference? I needed to modify the perl_groups code this time, and while I was able to get most of the idea by looking at other parts of the code, this part is pretty foreign to me.

Here's the diff, against R373
Code:
Index: zone/client.cpp
===================================================================
--- zone/client.cpp	(revision 373)
+++ zone/client.cpp	(working copy)
@@ -744,7 +744,7 @@
 
 		Group* group = GetGroup();
 		if(group != NULL) {
-			group->GroupMessage(this,(const char*) message);
+			group->GroupMessage(this,language,(const char*) message);
 		}
 		break;
 	}
@@ -920,6 +920,11 @@
 	if (language < MAX_PP_LANGUAGE) {
 		cm->skill_in_language = m_pp.languages[language];
 		cm->language = language;
+		if ((chan_num == 2) && (language != 0)) {	// for group messages not in common, check for language skill up
+			int SenderSkill = entity_list.GetClientByName(from)->m_pp.languages[language];
+			if ((m_pp.languages[language] <= SenderSkill) && (from != this->GetName() )) 
+				CheckLanguageSkillIncrease(language, SenderSkill);
+		}
 	}
 	else {
 		cm->skill_in_language = 100;
@@ -1920,6 +1925,25 @@
 	return false;
 }
 
+void Client::CheckLanguageSkillIncrease(int8 langid, int8 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
+		sint16 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
+			SetLanguageSkill(langid, LangSkill+1);	// increase the language skill by 1
+			_log(SKILLS__GAIN, "Language %d at value %d successfully gain with %.4f%%chance", langid, LangSkill, Chance);
+		} 
+		else
+			_log(SKILLS__GAIN, "Language %d at value %d failed to gain with %.4f%%chance", langid, LangSkill, Chance);
+	}
+}
+
 bool Client::HasSkill(SkillType skill_id) const {
 	return((GetSkill(skill_id) > 0) && CanHaveSkill(skill_id));
 }
Index: zone/client.h
===================================================================
--- zone/client.h	(revision 373)
+++ zone/client.h	(working copy)
@@ -550,6 +550,7 @@
 	void CheckSpecializeIncrease(int16 spell_id);
 	void CheckSongSkillIncrease(int16 spell_id);
 	bool	CheckIncreaseSkill(SkillType skillid, int chancemodi = 0);
+	void	CheckLanguageSkillIncrease(int8 langid, int8 TeacherSkill);
 	void    SetLanguageSkill(int langid, int value);
 	void	SetHoTT(int32 mobid);
 
Index: zone/groups.cpp
===================================================================
--- zone/groups.cpp	(revision 373)
+++ zone/groups.cpp	(working copy)
@@ -664,14 +664,14 @@
 	return false;
 }
 
-void Group::GroupMessage(Mob* sender, const char* message) {
+void Group::GroupMessage(Mob* sender, int8 language, const char* message) {
 	uint32 i;
 	for (i = 0; i < MAX_GROUP_MEMBERS; i++) {
 		if(!members[i])
 			continue;
 
 		if (members[i]->IsClient() && members[i]->CastToClient()->GetFilter(FILTER_GROUP)!=0)
-			members[i]->CastToClient()->ChannelMessageSend(sender->GetName(),members[i]->GetName(),2,0,message);
+			members[i]->CastToClient()->ChannelMessageSend(sender->GetName(),members[i]->GetName(),2,language,message);
 		#ifdef IPC
 		if (members[i]->CastToNPC()->IsInteractive() && members[i] != sender)
 			members[i]->CastToNPC()->InteractiveChat(2,1,message,(sender->GetTarget() != NULL) ? sender->GetTarget()->GetName():sender->GetName(),sender);
Index: zone/groups.h
===================================================================
--- zone/groups.h	(revision 373)
+++ zone/groups.h	(working copy)
@@ -68,7 +68,7 @@
 	void	CastGroupSpell(Mob* caster,uint16 spellid);
 	void	GroupBardPulse(Mob* caster,uint16 spellid);
 	void	SplitExp(uint32 exp, Mob* other);
-	void	GroupMessage(Mob* sender,const char* message);
+	void	GroupMessage(Mob* sender,int8 language,const char* message);
 	void	GroupMessage_StringID(Mob* sender, int32 type, int32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, int32 distance = 0);
 	int32	GetTotalGroupDamage(Mob* other);
 	void	SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter = NULL);
Index: zone/perl_groups.cpp
===================================================================
--- zone/perl_groups.cpp	(revision 373)
+++ zone/perl_groups.cpp	(working copy)
@@ -168,12 +168,13 @@
 XS(XS_Group_GroupMessage)
 {
 	dXSARGS;
-	if (items != 3)
-		Perl_croak(aTHX_ "Usage: Group::GroupMessage(THIS, sender, message)");
+	if ((items != 3) && (items != 4))	// the 3 item version is kept for backwards compatability
+		Perl_croak(aTHX_ "Usage: Group::GroupMessage(THIS, sender, language, message)");
 	{
 		Group *		THIS;
 		Mob*		sender;
-		char*		message = (char *)SvPV_nolen(ST(2));
+		int8		language;
+		char*		message;
 
 		if (sv_derived_from(ST(0), "Group")) {
 			IV tmp = SvIV((SV*)SvRV(ST(0)));
@@ -193,7 +194,17 @@
 		if(sender == NULL)
 			Perl_croak(aTHX_ "sender is NULL, avoiding crash.");
 
-		THIS->GroupMessage(sender, message);
+		if (items == 4) {
+			language = (int8)SvUV(ST(2));
+			if ((language >= MAX_PP_LANGUAGE) || (language < 0))
+				language = 0;
+			message = (char *)SvPV_nolen(ST(3));
+			THIS->GroupMessage(sender, language, message);
+		}
+		else {	// if no language is specificed, send it in common
+			message = (char *)SvPV_nolen(ST(2));
+			THIS->GroupMessage(sender,0, message);
+		}
 	}
 	XSRETURN_EMPTY;
 }
Reply With Quote
  #2  
Old 03-08-2009, 01:26 PM
realityincarnate
Developer
 
Join Date: Dec 2007
Posts: 122
Default A more extensive modification

(this replaces the code posted above)
After playing around with the language stuff some more, I made a couple of changes to how language skill is handled. This system now uses both the speaker and listener skills in the language to more realistically determine how garbled text becomes. As a side effect, it also enables drunk talk (one of my favorite little touches in the game).

Code:
Index: client.cpp
===================================================================
--- client.cpp	(revision 376)
+++ client.cpp	(working copy)
@@ -670,7 +670,7 @@
 	return;
 }
 
-void Client::ChannelMessageReceived(int8 chan_num, int8 language, const char* message, const char* targetname) {
+void Client::ChannelMessageReceived(int8 chan_num, int8 language, int8 lang_skill, const char* message, const char* targetname) {
 	#if EQDEBUG >= 11
 		LogFile->write(EQEMuLog::Debug,"Client::ChannelMessageReceived() Channel:%i message:'%s'", chan_num, message);
 	#endif
@@ -744,7 +744,7 @@
 
 		Group* group = GetGroup();
 		if(group != NULL) {
-			group->GroupMessage(this,(const char*) message);
+			group->GroupMessage(this,language,lang_skill,(const char*) message);
 		}
 		break;
 	}
@@ -760,7 +760,7 @@
 		if (GetPet() && GetPet()->FindType(SE_VoiceGraft))
 			sender = GetPet();
 
-		entity_list.ChannelMessage(sender, chan_num, language, message);
+		entity_list.ChannelMessage(sender, chan_num, language, lang_skill, message);
  		break;
  	}
 	case 4: { // Auction
@@ -783,7 +783,7 @@
 		    if(!ooc_timer.Check())
 		{
 			if(strlen(targetname)==0)
-			ChannelMessageReceived(5, language, message,"discard"); //Fast typer or spammer??
+			ChannelMessageReceived(5, language, lang_skill, message,"discard"); //Fast typer or spammer??
 			else
 			return;
 		}
@@ -833,7 +833,7 @@
 			sender = GetPet();
 
 		printf("Message: %s\n",message);
-		entity_list.ChannelMessage(sender, chan_num, language, message);
+		entity_list.ChannelMessage(sender, chan_num, language, lang_skill, message);
 
 		if (sender != this)
 			break;
@@ -892,7 +892,12 @@
 	}
 }
 
+// if no language skill is specified, call the function with a skill of 100.
 void Client::ChannelMessageSend(const char* from, const char* to, int8 chan_num, int8 language, const char* message, ...) {
+	ChannelMessageSend(from, to, chan_num, language, 100, message);
+}
+
+void Client::ChannelMessageSend(const char* from, const char* to, int8 chan_num, int8 language, int8 lang_skill, const char* message, ...) {
 	if ((chan_num==11 && !(this->GetGM())) || (chan_num==10 && this->Admin()<80)) // dont need to send /pr & /petition to everybody
 		return;
 	va_list argptr;
@@ -917,15 +922,30 @@
 		strcpy(cm->targetname, m_pp.name);
 	else
 		cm->targetname[0] = 0;
+	
+	int8 ListenerSkill;
+	
 	if (language < MAX_PP_LANGUAGE) {
-		cm->skill_in_language = m_pp.languages[language];
+		ListenerSkill = m_pp.languages[language];
 		cm->language = language;
+		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);
+		}
 	}
 	else {
-		cm->skill_in_language = 100;
+		ListenerSkill = m_pp.languages[0];
 		cm->language = 0;
 	}
-
+	
+	// set effective language skill = average of sender and receiver skills
+	sint32 EffSkill = (lang_skill + ListenerSkill)/2;
+	if (EffSkill < 1)	// effective skill has a minimum value of 1...
+		EffSkill = 1;
+	else if (EffSkill > 100)	// ...and a maximum value of 100
+		EffSkill;
+	cm->skill_in_language = EffSkill;
+	
 	cm->chan_num = chan_num;
 	strcpy(&cm->message[0], buffer);
 	QueuePacket(&app);
@@ -1920,6 +1940,25 @@
 	return false;
 }
 
+void Client::CheckLanguageSkillIncrease(int8 langid, int8 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
+		sint16 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
+			SetLanguageSkill(langid, LangSkill+1);	// increase the language skill by 1
+			_log(SKILLS__GAIN, "Language %d at value %d successfully gain with %.4f%%chance", langid, LangSkill, Chance);
+		} 
+		else
+			_log(SKILLS__GAIN, "Language %d at value %d failed to gain with %.4f%%chance", langid, LangSkill, Chance);
+	}
+}
+
 bool Client::HasSkill(SkillType skill_id) const {
 	return((GetSkill(skill_id) > 0) && CanHaveSkill(skill_id));
 }
Index: client.h
===================================================================
--- client.h	(revision 376)
+++ client.h	(working copy)
@@ -232,8 +232,9 @@
 	void	SendPacketQueue(bool Block = true);
 	void	QueuePacket(const EQApplicationPacket* app, bool ack_req = true, CLIENT_CONN_STATUS = CLIENT_CONNECTINGALL, eqFilterType filter=FilterNone);
 	void	FastQueuePacket(EQApplicationPacket** app, bool ack_req = true, CLIENT_CONN_STATUS = CLIENT_CONNECTINGALL);
-	void	ChannelMessageReceived(int8 chan_num, int8 language, const char* message, const char* targetname=NULL);
+	void	ChannelMessageReceived(int8 chan_num, int8 language, int8 lang_skill, const char* message, const char* targetname=NULL);
 	void	ChannelMessageSend(const char* from, const char* to, int8 chan_num, int8 language, const char* message, ...);
+	void	ChannelMessageSend(const char* from, const char* to, int8 chan_num, int8 language, int8 lang_skill, const char* message, ...);
 	void	Message(int32 type, const char* message, ...);
 	void    QuestJournalledMessage(const char *npcname, const char* message);
 	void	VoiceMacroReceived(int32 Type, char *Target, int32 MacroNumber);
@@ -550,6 +551,7 @@
 	void CheckSpecializeIncrease(int16 spell_id);
 	void CheckSongSkillIncrease(int16 spell_id);
 	bool	CheckIncreaseSkill(SkillType skillid, int chancemodi = 0);
+	void	CheckLanguageSkillIncrease(int8 langid, int8 TeacherSkill);
 	void    SetLanguageSkill(int langid, int value);
 	void	SetHoTT(int32 mobid);
 
Index: client_packet.cpp
===================================================================
--- client_packet.cpp	(revision 376)
+++ client_packet.cpp	(working copy)
@@ -2334,7 +2334,7 @@
 		return;
 	}
 
-	ChannelMessageReceived(cm->chan_num, cm->language, cm->message, cm->targetname);
+	ChannelMessageReceived(cm->chan_num, cm->language, cm->skill_in_language, cm->message, cm->targetname);
 	return;
 }
 
Index: entity.cpp
===================================================================
--- entity.cpp	(revision 376)
+++ entity.cpp	(working copy)
@@ -954,7 +954,12 @@
 	}
 }
 
+// if no language skill is specified, sent with 100 skill
 void EntityList::ChannelMessage(Mob* from, int8 chan_num, int8 language, const char* message, ...) {
+	ChannelMessage(from, chan_num, language, 100, message);
+}
+
+void EntityList::ChannelMessage(Mob* from, int8 chan_num, int8 language, int8 lang_skill, const char* message, ...) {
 	LinkedListIterator<Client*> iterator(client_list);
 	va_list argptr;
 	char buffer[4096];
@@ -976,7 +981,7 @@
 		if (chan_num != 8 || client->Dist(*from) < 200) // Only say is limited in range
 		{
 			if(filter==FilterNone || client->GetFilter(filter)!=FilterHide)
-				client->ChannelMessageSend(from->GetName(), 0, chan_num, language, buffer);
+				client->ChannelMessageSend(from->GetName(), 0, chan_num, language, lang_skill, buffer);
 		}
 		iterator.Advance();
 	}
Index: entity.h
===================================================================
--- entity.h	(revision 376)
+++ entity.h	(working copy)
@@ -236,6 +236,7 @@
 	void	MessageClose_StringID(Mob *sender, bool skipsender, float dist, int32 type, int32 string_id, const char* message1=0,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0);
 	void	ChannelMessageFromWorld(const char* from, const char* to, int8 chan_num, int32 guilddbid, int8 language, const char* message);
 	void    ChannelMessage(Mob* from, int8 chan_num, int8 language, const char* message, ...);
+	void    ChannelMessage(Mob* from, int8 chan_num, int8 language, int8 lang_skill, const char* message, ...);
 	void	ChannelMessageSend(Mob* to, int8 chan_num, int8 language, const char* message, ...);
 	void    SendZoneSpawns(Client*);
 	void	SendZonePVPUpdates(Client *);
Index: groups.cpp
===================================================================
--- groups.cpp	(revision 376)
+++ groups.cpp	(working copy)
@@ -664,14 +664,14 @@
 	return false;
 }
 
-void Group::GroupMessage(Mob* sender, const char* message) {
+void Group::GroupMessage(Mob* sender, int8 language, int8 lang_skill, const char* message) {
 	uint32 i;
 	for (i = 0; i < MAX_GROUP_MEMBERS; i++) {
 		if(!members[i])
 			continue;
 
 		if (members[i]->IsClient() && members[i]->CastToClient()->GetFilter(FILTER_GROUP)!=0)
-			members[i]->CastToClient()->ChannelMessageSend(sender->GetName(),members[i]->GetName(),2,0,message);
+			members[i]->CastToClient()->ChannelMessageSend(sender->GetName(),members[i]->GetName(),2,language,lang_skill,message);
 		#ifdef IPC
 		if (members[i]->CastToNPC()->IsInteractive() && members[i] != sender)
 			members[i]->CastToNPC()->InteractiveChat(2,1,message,(sender->GetTarget() != NULL) ? sender->GetTarget()->GetName():sender->GetName(),sender);
Index: groups.h
===================================================================
--- groups.h	(revision 376)
+++ groups.h	(working copy)
@@ -68,7 +68,7 @@
 	void	CastGroupSpell(Mob* caster,uint16 spellid);
 	void	GroupBardPulse(Mob* caster,uint16 spellid);
 	void	SplitExp(uint32 exp, Mob* other);
-	void	GroupMessage(Mob* sender,const char* message);
+	void	GroupMessage(Mob* sender,int8 language,int8 lang_skill,const char* message);
 	void	GroupMessage_StringID(Mob* sender, int32 type, int32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, int32 distance = 0);
 	int32	GetTotalGroupDamage(Mob* other);
 	void	SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter = NULL);
Index: perl_groups.cpp
===================================================================
--- perl_groups.cpp	(revision 376)
+++ perl_groups.cpp	(working copy)
@@ -168,12 +168,13 @@
 XS(XS_Group_GroupMessage)
 {
 	dXSARGS;
-	if (items != 3)
-		Perl_croak(aTHX_ "Usage: Group::GroupMessage(THIS, sender, message)");
+	if ((items != 3) && (items != 4))	// the 3 item version is kept for backwards compatability
+		Perl_croak(aTHX_ "Usage: Group::GroupMessage(THIS, sender, language, message)");
 	{
 		Group *		THIS;
 		Mob*		sender;
-		char*		message = (char *)SvPV_nolen(ST(2));
+		int8		language;
+		char*		message;
 
 		if (sv_derived_from(ST(0), "Group")) {
 			IV tmp = SvIV((SV*)SvRV(ST(0)));
@@ -193,7 +194,17 @@
 		if(sender == NULL)
 			Perl_croak(aTHX_ "sender is NULL, avoiding crash.");
 
-		THIS->GroupMessage(sender, message);
+		if (items == 4) {
+			language = (int8)SvUV(ST(2));
+			if ((language >= MAX_PP_LANGUAGE) || (language < 0))
+				language = 0;
+			message = (char *)SvPV_nolen(ST(3));
+			THIS->GroupMessage(sender, language, 100, message);
+		}
+		else {	// if no language is specificed, send it in common
+			message = (char *)SvPV_nolen(ST(2));
+			THIS->GroupMessage(sender,0, 100, message);
+		}
 	}
 	XSRETURN_EMPTY;
 }
Reply With Quote
Reply


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

   

All times are GMT -4. The time now is 01:46 PM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3