Jump to content

VegaS™

Forum Moderator
  • Posts

    656
  • Joined

  • Last visited

  • Days Won

    187
  • Feedback

    100%

Posts posted by VegaS™

  1. There are a lot of people which had problem with localeInfo because korean-characters and bad encoding, there's a clean file with refactored code.

    • Removed all the code which isn't used like korean characters < bad encoding [runmain error / crash](editors problem) and more checks.
    • Removed over 500 lines unused.
    • Removed function mapping(**kwargs) and use constructor of dict > dict(**kwarg) which is same (**kwarg let you take arbitrary number of keyword arguments).
    • Removed function CutMoneyString because is used just when locale is HongKong, CIBN.
    • Removed check IsYMIR from function LoadLocaleData which load locale as locale/ymir or locale/we_korea.
    • Removed GUILD_MARK_NOT_ENOUGH_LEVEL, GUILD_HEADQUARTER, GUILD_FACILITY, GUILD_OBJECT, MAP_TRENT02, MAP_WL, MAP_NUSLUCK, MAP_TREE2, LOGIN_FAILURE_WEB_BLOCK, LOGIN_FAILURE_BLOCK_LOGIN, CHANNEL_NOTIFY_FULL, now they're readed directly from locale_game.txt.
    • Removed declared global variables.
    • Removed checks for declaring LOCALE_FILE_NAME, FN_GM_MARK and use current path.
    • Removed korean functions/lists/dictionaries/characters GetAuxiliaryWordType, JOBINFO_DATA_LIST, dictSingleWord, dictDoubleWord, etc.
    • Removed unused things: locale mapping, 'all' list etc.
    • Removed IN_GAME_SHOP_ENABLE declaration, should be declared inside of constInfo directly.
    • Removed checks (locale path) - 949, 932 == app.GetDefaultCodePage(), IsHONGKONG, IsNEWCIBN() or IsCIBN10() from declaration of functions like (NumberToMoneyString, NumberToSecondaryCoinString, ...),now they're declared directly from old style (IsEUROPE() and not IsWE_KOREA() and not IsYMIR()).
    • Added custom string format(format_string, *args, **kwargs) instead of %. (old-style).
    • Added new checks inside of LoadLocaleFile for security:
    •     Check if token3 (token1=original_string, token2=return-string, token3=function) function name exist in our types (SA, SNA, SAA, SAN) then try to call it.
    •     Check if string line have no tabs.

    Diff-checker: (856 Removals + 301 Additions)

     

    Spoiler
    
    """
    	File: localeInfo.py
    	Refactored code date: 12.05.2016
    	Changes:
    		Removed all the code which isn't used like korean characters < bad encoding(editors problem) and more checks.
    		Removed over 500 lines unused.
    		Removed function mapping(**kwargs) and use constructor of dict > dict(**kwarg) which is same (**kwarg let you take arbitrary number of keyword arguments).
    		Removed function CutMoneyString because is used just when locale is HongKong, CIBN.
    		Removed check IsYMIR from function LoadLocaleData which load locale as locale/ymir or locale/we_korea.
    		Removed GUILD_MARK_NOT_ENOUGH_LEVEL, GUILD_HEADQUARTER, GUILD_FACILITY, GUILD_OBJECT, MAP_TRENT02, MAP_WL, MAP_NUSLUCK, MAP_TREE2, 
    			LOGIN_FAILURE_WEB_BLOCK, LOGIN_FAILURE_BLOCK_LOGIN, CHANNEL_NOTIFY_FULL, now they're readed directly from locale_game.txt.
    		Removed declared global variables.
    		Removed checks for declaring LOCALE_FILE_NAME, FN_GM_MARK and use current path.
    		Removed korean functions/lists/dictionaries/characters GetAuxiliaryWordType, JOBINFO_DATA_LIST, dictSingleWord, dictDoubleWord, etc.
    		Removed unused things: locale mapping, 'all' list etc.
    		Removed IN_GAME_SHOP_ENABLE declaration, should be declared inside of constInfo directly.
    		Removed checks (locale path) - 949, 932 == app.GetDefaultCodePage(), IsHONGKONG, IsNEWCIBN() or IsCIBN10() from declaration of functions like (NumberToMoneyString, NumberToSecondaryCoinString, ...),
    			now they're declared directly from old style (IsEUROPE() and not IsWE_KOREA() and not IsYMIR()).
    		
    		Added custom string format(format_string, *args, **kwargs) instead of %. (old-style).
    		Added new checks inside of LoadLocaleFile for security:
    			* Check if token3 (token1=original_string, token2=return-string, token3=function) function name exist in our types (SA, SNA, SAA, SAN) then try to call it.
    			* Check if string line have no tabs.
    """
    
    import app
    import constInfo
    import dbg
    
    APP_GET_LOCALE_PATH					= app.GetLocalePath()
    APP_GET_LOCALE_SERVICE_NAME 		= app.GetLocaleServiceName()
    	
    APP_TITLE 							= 'METIN2'
    
    BLEND_POTION_NO_TIME 				= 'BLEND_POTION_NO_TIME'
    BLEND_POTION_NO_INFO				= 'BLEND_POTION_NO_INFO'
    
    LOGIN_FAILURE_WRONG_SOCIALID 		= 'LOGIN_FAILURE_WRONG_SOCIALID'
    LOGIN_FAILURE_SHUTDOWN_TIME 		= 'LOGIN_FAILURE_SHUTDOWN_TIME'
    
    GUILD_MEMBER_COUNT_INFINITY 		= 'INFINITY'
    GUILD_MARK_MIN_LEVEL 				= '3'
    GUILD_BUILDING_LIST_TXT 			= '{:s}/GuildBuildingList.txt'.format(APP_GET_LOCALE_PATH)
    FN_GM_MARK 							= '{:s}/effect/gm.mse'.format(APP_GET_LOCALE_PATH)
    
    MAP_TREE2 							= 'MAP_TREE2'
    
    ERROR_MARK_UPLOAD_NEED_RECONNECT	= 'UploadMark: Reconnect to game'
    ERROR_MARK_CHECK_NEED_RECONNECT		= 'CheckMark: Reconnect to game'
    
    VIRTUAL_KEY_ALPHABET_LOWERS			= r"[1234567890]/qwertyuiop\=asdfghjkl;`'zxcvbnm.,"
    VIRTUAL_KEY_ALPHABET_UPPERS			= r"{1234567890}?QWERTYUIOP|+ASDFGHJKL:~'ZXCVBNM<>"
    VIRTUAL_KEY_SYMBOLS    				= "!@#$%^&*()_+|{}:'<>?~"
    VIRTUAL_KEY_NUMBERS					= "1234567890-=\[];',./`"
    VIRTUAL_KEY_SYMBOLS_BR				= "!@#$%^&*()_+|{}:'<>?~áàãâéèêíìóòôõúùç"
    
    __IS_ENGLISH						= 'ENGLISH' == APP_GET_LOCALE_SERVICE_NAME
    __IS_HONGKONG						= 'HONGKONG' == APP_GET_LOCALE_SERVICE_NAME
    __IS_EUROPE							= 'EUROPE' == APP_GET_LOCALE_SERVICE_NAME
    
    __IS_NEWCIBN						= 'locale/newcibn' == APP_GET_LOCALE_PATH		
    __IS_CANADA							= 'locale/ca' == APP_GET_LOCALE_PATH
    __IS_BRAZIL							= 'locale/br' == APP_GET_LOCALE_PATH
    __IS_SINGAPORE						= 'locale/sg' == APP_GET_LOCALE_PATH
    __IS_VIETNAM						= 'locale/vn' == APP_GET_LOCALE_PATH
    __IS_ARABIC							= 'locale/ae' == APP_GET_LOCALE_PATH
    __IS_CIBN10							= 'locale/cibn10' == APP_GET_LOCALE_PATH
    __IS_WE_KOREA						= 'locale/we_korea' == APP_GET_LOCALE_PATH
    __IS_YMIR							= 'locale/ymir' == APP_GET_LOCALE_PATH
    __IS_TAIWAN							= 'locale/taiwan' == APP_GET_LOCALE_PATH
    __IS_JAPAN							= 'locale/japan' == APP_GET_LOCALE_PATH
    
    if __IS_CANADA:
    	__IS_EUROPE = TRUE
    
    def IsYMIR():
    	return __IS_YMIR
    
    def IsJAPAN():
    	return __IS_JAPAN
    
    def IsENGLISH():
    	return __IS_ENGLISH
    
    def IsHONGKONG():
    	return __IS_HONGKONG
    
    def IsTAIWAN():
    	return __IS_TAIWAN
    
    def IsNEWCIBN():
    	return __IS_NEWCIBN
    
    def IsCIBN10():
    	return __IS_CIBN10
    	
    def IsEUROPE():
    	return __IS_EUROPE
    
    def IsCANADA():
    	return __IS_CANADA
    
    def IsBRAZIL():
    	return __IS_BRAZIL
    
    def IsVIETNAM():
    	return __IS_VIETNAM
    
    def IsSINGAPORE():
    	return __IS_SINGAPORE
    	
    def IsARABIC():
    	return __IS_ARABIC
    
    def IsWE_KOREA():
    	return __IS_WE_KOREA
    	
    def IsCHEONMA():
    	return __IS_YMIR
    
    # Load locale data by specific path
    def LoadLocaleData():
    	app.LoadLocaleData(app.GetLocalePath())
    
    # Load locale_game.txt
    def LoadLocaleFile(srcFileName, localeDict):
    	def SNA(text):
    		def f(x):
    			return text
    		return f
    
    	def SA(text):
    		def f(x):
    			return text % x
    		return f
    
    	def SAN(text):
    		def f(x):
    			return text % x
    		return f
    
    	def SAA(text):
    		def f(x):
    			return text % x
    		return f
    
    	funcDict = {"SA": SA, "SNA": SNA, "SAA": SAA, "SAN": SAN}
    	lineIndex = 1
    
    	try:
    		lines = open(srcFileName, "r").readlines()
    	except IOError:
    		dbg.LogBox("LoadLocaleError(%(srcFileName)s)" % locals())
    		app.Abort()
    
    	for line in lines:
    		if line.count("\t") == 0:
    			continue
    
    		try:
    			tokens = line[:-1].split("\t")
    			if len(tokens) == 2:
    				localeDict[tokens[0]] = tokens[1]
    			elif len(tokens) >= 3:
    				type = tokens[2].strip()
    				if type:
    					if type in funcDict:
    						localeDict[tokens[0]] = funcDict[type](tokens[1])
    					else:
    						localeDict[tokens[0]] = tokens[1]
    				else:
    					localeDict[tokens[0]] = tokens[1]
    			else:
    				raise RuntimeError, "Unknown TokenSize"
    
    			lineIndex += 1
    		except:
    			dbg.LogBox("%s: line(%d): %s" % (srcFileName, lineIndex, line), "Error")
    			raise
    
    LoadLocaleFile("{:s}/locale_game.txt".format(APP_GET_LOCALE_PATH), locals())
    		
    # Option pvp messages
    OPTION_PVPMODE_MESSAGE_DICT = {
    	0: PVP_MODE_NORMAL,
    	1: PVP_MODE_REVENGE,
    	2: PVP_MODE_KILL,
    	3: PVP_MODE_PROTECT,
    	4: PVP_MODE_GUILD,
    }
    
    # Whisper messages
    WHISPER_ERROR = {
    	1: CANNOT_WHISPER_NOT_LOGON,
    	2: CANNOT_WHISPER_DEST_REFUSE,
    	3: CANNOT_WHISPER_SELF_REFUSE,
    }
    
    # Exception of graphic device.
    error = dict(
    	CREATE_WINDOW = GAME_INIT_ERROR_MAIN_WINDOW,
    	CREATE_CURSOR = GAME_INIT_ERROR_CURSOR,
    	CREATE_NETWORK = GAME_INIT_ERROR_NETWORK,
    	CREATE_ITEM_PROTO = GAME_INIT_ERROR_ITEM_PROTO,
    	CREATE_MOB_PROTO = GAME_INIT_ERROR_MOB_PROTO,
    	CREATE_NO_DIRECTX = GAME_INIT_ERROR_DIRECTX,
    	CREATE_DEVICE = GAME_INIT_ERROR_GRAPHICS_NOT_EXIST,
    	CREATE_NO_APPROPRIATE_DEVICE = GAME_INIT_ERROR_GRAPHICS_BAD_PERFORMANCE,
    	CREATE_FORMAT = GAME_INIT_ERROR_GRAPHICS_NOT_SUPPORT_32BIT,
    	NO_ERROR = str()
    )
    
    # Job information (none, skill_group1, skill_group2)
    JOBINFO_TITLE = [
    	[JOB_WARRIOR0, JOB_WARRIOR1, JOB_WARRIOR2,],
    	[JOB_ASSASSIN0, JOB_ASSASSIN1, JOB_ASSASSIN2,],
    	[JOB_SURA0, JOB_SURA1, JOB_SURA2,],
    	[JOB_SHAMAN0, JOB_SHAMAN1, JOB_SHAMAN2,],
    ]
    
    #if app.ENABLE_WOLFMAN_CHARACTER:
    	#JOBINFO_TITLE += [[JOB_WOLFMAN0,JOB_WOLFMAN1,JOB_WOLFMAN2,],]
    
    # Guild war description
    GUILDWAR_NORMAL_DESCLIST = 					(GUILD_WAR_USE_NORMAL_MAP, GUILD_WAR_LIMIT_30MIN, GUILD_WAR_WIN_CHECK_SCORE)
    # Guild war warp description
    GUILDWAR_WARP_DESCLIST = 					(GUILD_WAR_USE_BATTLE_MAP, GUILD_WAR_WIN_WIPE_OUT_GUILD, GUILD_WAR_REWARD_POTION)
    # Guild war flag description
    GUILDWAR_CTF_DESCLIST = 					(GUILD_WAR_USE_BATTLE_MAP, GUILD_WAR_WIN_TAKE_AWAY_FLAG1, GUILD_WAR_WIN_TAKE_AWAY_FLAG2, GUILD_WAR_REWARD_POTION)
    
    # Mode of pvp options
    MODE_NAME_LIST = 							(PVP_OPTION_NORMAL, PVP_OPTION_REVENGE, PVP_OPTION_KILL, PVP_OPTION_PROTECT,)
    # Title name of alignment
    TITLE_NAME_LIST = 							(PVP_LEVEL0, PVP_LEVEL1, PVP_LEVEL2, PVP_LEVEL3, PVP_LEVEL4, PVP_LEVEL5, PVP_LEVEL6, PVP_LEVEL7, PVP_LEVEL8,)
    
    # Horse levels
    LEVEL_LIST = 								(str(), HORSE_LEVEL1, HORSE_LEVEL2, HORSE_LEVEL3)
    # Horse health
    HEALTH_LIST = 								(HORSE_HEALTH0, HORSE_HEALTH1, HORSE_HEALTH2, HORSE_HEALTH3)
    
    # Use-skill messages
    USE_SKILL_ERROR_TAIL_DICT = {	
    	'IN_SAFE':								CANNOT_SKILL_SELF_IN_SAFE,
    	'NEED_TARGET': 							CANNOT_SKILL_NEED_TARGET,
    	'NEED_EMPTY_BOTTLE': 					CANNOT_SKILL_NEED_EMPTY_BOTTLE,
    	'NEED_POISON_BOTTLE': 					CANNOT_SKILL_NEED_POISON_BOTTLE,
    	'REMOVE_FISHING_ROD': 					CANNOT_SKILL_REMOVE_FISHING_ROD,
    	'NOT_YET_LEARN': 						CANNOT_SKILL_NOT_YET_LEARN,
    	'NOT_MATCHABLE_WEAPON':					CANNOT_SKILL_NOT_MATCHABLE_WEAPON,
    	'WAIT_COOLTIME':						CANNOT_SKILL_WAIT_COOLTIME,
    	'NOT_ENOUGH_HP':						CANNOT_SKILL_NOT_ENOUGH_HP,
    	'NOT_ENOUGH_SP':						CANNOT_SKILL_NOT_ENOUGH_SP,
    	'CANNOT_USE_SELF':						CANNOT_SKILL_USE_SELF,
    	'ONLY_FOR_ALLIANCE': 					CANNOT_SKILL_ONLY_FOR_ALLIANCE,
    	'CANNOT_ATTACK_ENEMY_IN_SAFE_AREA':		CANNOT_SKILL_DEST_IN_SAFE,
    	'CANNOT_APPROACH':						CANNOT_SKILL_APPROACH,
    	'CANNOT_ATTACK':						CANNOT_SKILL_ATTACK,
    	'ONLY_FOR_CORPSE':						CANNOT_SKILL_ONLY_FOR_CORPSE,
    	'EQUIP_FISHING_ROD':					CANNOT_SKILL_EQUIP_FISHING_ROD, 
    	'NOT_HORSE_SKILL':						CANNOT_SKILL_NOT_HORSE_SKILL,
    	'HAVE_TO_RIDE':							CANNOT_SKILL_HAVE_TO_RIDE,
    }
    
    # Notify messages
    NOTIFY_MESSAGE = {
    	'CANNOT_EQUIP_SHOP': 					CANNOT_EQUIP_IN_SHOP,
    	'CANNOT_EQUIP_EXCHANGE': 				CANNOT_EQUIP_IN_EXCHANGE,
    }
    
    # Attack messages
    ATTACK_ERROR_TAIL_DICT = {
    	'IN_SAFE': 								CANNOT_ATTACK_SELF_IN_SAFE,
    	'DEST_IN_SAFE': 						CANNOT_ATTACK_DEST_IN_SAFE,
    }
    
    # Shot messages
    SHOT_ERROR_TAIL_DICT = {
    	'EMPTY_ARROW': 							CANNOT_SHOOT_EMPTY_ARROW,
    	'IN_SAFE':								CANNOT_SHOOT_SELF_IN_SAFE,
    	'DEST_IN_SAFE':							CANNOT_SHOOT_DEST_IN_SAFE,
    }
    
    # Skill messages
    USE_SKILL_ERROR_CHAT_DICT = {	
    	'NEED_EMPTY_BOTTLE': 					SKILL_NEED_EMPTY_BOTTLE,
    	'NEED_POISON_BOTTLE': 					SKILL_NEED_POISON_BOTTLE, 
    	'ONLY_FOR_GUILD_WAR': 					SKILL_ONLY_FOR_GUILD_WAR,
    }
    
    # Shop/private-shop messages
    SHOP_ERROR_DICT = {
    	'NOT_ENOUGH_MONEY': 					SHOP_NOT_ENOUGH_MONEY,
    	'SOLDOUT': 								SHOP_SOLDOUT,
    	'INVENTORY_FULL': 						SHOP_INVENTORY_FULL,
    	'INVALID_POS': 							SHOP_INVALID_POS,
    	'NOT_ENOUGH_MONEY_EX': 					SHOP_NOT_ENOUGH_MONEY_EX,
    }
    
    # Character status description
    STAT_MINUS_DESCRIPTION = {
    	'HTH-': 								STAT_MINUS_CON,
    	'INT-': 								STAT_MINUS_INT,
    	'STR-': 								STAT_MINUS_STR,
    	'DEX-': 								STAT_MINUS_DEX,
    }
    
    # Map names
    MINIMAP_ZONE_NAME_DICT = {
    	'metin2_map_a1': 						MAP_A1,
    	'map_a2': 								MAP_A2,
    	'metin2_map_a3': 						MAP_A3,
    	'metin2_map_b1': 						MAP_B1,
    	'map_b2': 								MAP_B2,
    	'metin2_map_b3': 						MAP_B3,
    	'metin2_map_c1': 						MAP_C1,
    	'map_c2': 								MAP_C2,
    	'metin2_map_c3': 						MAP_C3,
    	'map_n_snowm_01': 						MAP_SNOW,
    	'metin2_map_n_flame_01': 				MAP_FLAME,
    	'metin2_map_n_desert_01': 				MAP_DESERT,
    	'metin2_map_milgyo': 					MAP_TEMPLE,
    	'metin2_map_spiderdungeon': 			MAP_SPIDER,
    	'metin2_map_deviltower1': 				MAP_SKELTOWER,
    	'metin2_map_guild_01': 					MAP_AG,
    	'metin2_map_guild_02': 					MAP_BG,
    	'metin2_map_guild_03': 					MAP_CG,
    	'metin2_map_trent': 					MAP_TREE,
    	'metin2_map_trent02': 					MAP_TREE2,
    	'season1/metin2_map_WL_01': 			MAP_WL,
    	'season1/metin2_map_nusluck01': 		MAP_NUSLUCK,
        'Metin2_map_CapeDragonHead': 			MAP_CAPE,
        'metin2_map_Mt_Thunder': 				MAP_THUNDER,
        'metin2_map_dawnmistwood': 				MAP_DAWN,
        'metin2_map_BayBlackSand': 				MAP_BAY,
    }
    
    # Path of quest icon file
    def GetLetterImageName():
    	return "season1/icon/scroll_close.tga"
    	
    def GetLetterOpenImageName():
    	return "season1/icon/scroll_open.tga"
    	
    def GetLetterCloseImageName():
    	return "season1/icon/scroll_close.tga"
    	
    # Sell item question
    def DO_YOU_SELL_ITEM(sellItemName, sellItemCount, sellItemPrice):
    	return DO_YOU_SELL_ITEM2 % (sellItemName, sellItemCount, NumberToMoneyString(sellItemPrice)) if (sellItemCount > 1) else DO_YOU_SELL_ITEM1 % (sellItemName, NumberToMoneyString(sellItemPrice))
    
    # Buy item question
    def DO_YOU_BUY_ITEM(buyItemName, buyItemCount, buyItemPrice):
    	return DO_YOU_BUY_ITEM2 % (buyItemName, buyItemCount, buyItemPrice) if (buyItemCount > 1) else DO_YOU_BUY_ITEM1 % (buyItemName, buyItemPrice)
    		
    # Notify when you can't attach a specific item.
    def REFINE_FAILURE_CAN_NOT_ATTACH(attachedItemName):
    	return REFINE_FAILURE_CAN_NOT_ATTACH0 % (attachedItemName)
    
    def REFINE_FAILURE_NO_SOCKET(attachedItemName):
    	return REFINE_FAILURE_NO_SOCKET0 % (attachedItemName)
    
    # Drop item question
    def REFINE_FAILURE_NO_GOLD_SOCKET(attachedItemName):
    	return REFINE_FAILURE_NO_GOLD_SOCKET0 % (attachedItemName)
    	
    # Drop item question
    def HOW_MANY_ITEM_DO_YOU_DROP(dropItemName, dropItemCount):
    	return HOW_MANY_ITEM_DO_YOU_DROP2 % (dropItemName, dropItemCount) if (dropItemCount > 1) else HOW_MANY_ITEM_DO_YOU_DROP1 % (dropItemName)
    
    # Fishing notify when looks like the fish is hooked.
    def FISHING_NOTIFY(isFish, fishName):
    	return FISHING_NOTIFY1 % (fishName) if isFish else FISHING_NOTIFY2 % (fishName)
    
    # Fishing notify when you capture a fish.
    def FISHING_SUCCESS(isFish, fishName):
    	return FISHING_SUCCESS1 % (fishName) if isFish else FISHING_SUCCESS2 % (fishName)
    	
    # Convert a integer amount into a string and add . as separator for money.
    def NumberToMoneyString(n):
    	return '0 {:s}'.format(MONETARY_UNIT0) if (n <= 0) else '{:s} {:s}'.format('.'.join([(i - 3) < 0 and str(n)[:i] or str(n)[i - 3 : i] for i in range(len(str(n)) % 3, len(str(n)) + 1, 3) if i]), MONETARY_UNIT0)
    
    # Convert a integer amount into a string and add . as separator for secondary coin.
    def NumberToSecondaryCoinString(n):
    	return '0 {:s}'.format(MONETARY_UNIT_JUN) if (n <= 0) else '{:s} {:s}'.format('.'.join([(i - 3) < 0 and str(n)[:i] or str(n)[i - 3: i] for i in range(len(str(n)) % 3, len(str(n)) + 1, 3) if i]), MONETARY_UNIT_JUN)
    	
    # Return the title of alignment by points.
    def GetAlignmentTitleName(alignment):
    	if alignment >= 12000:
    		return TITLE_NAME_LIST[0]
    	elif alignment >= 8000:
    		return TITLE_NAME_LIST[1]
    	elif alignment >= 4000:
    		return TITLE_NAME_LIST[2]
    	elif alignment >= 1000:
    		return TITLE_NAME_LIST[3]
    	elif alignment >= 0:
    		return TITLE_NAME_LIST[4]
    	elif alignment > -4000:
    		return TITLE_NAME_LIST[5]
    	elif alignment > -8000:
    		return TITLE_NAME_LIST[6]
    	elif alignment > -12000:
    		return TITLE_NAME_LIST[7]
    
    	return TITLE_NAME_LIST[8]
    	
    # Convert seconds to Days-Hours-Minutes
    def SecondToDHM(time):
    	if time < 60:
    		if IsARABIC():
    			return '%.2f %s' % (time, SECOND)
    		else:
    			return '0' + MINUTE
    
    	minute = int((time / 60) % 60)
    	hour = int((time / 60) / 60) % 24
    	day = int(int((time / 60) / 60) / 24)
    
    	text = ''
    	if day > 0:
    		text += str(day) + DAY
    		text += ' '
    
    	if hour > 0:
    		text += str(hour) + HOUR
    		text += ' '
    
    	if minute > 0:
    		text += str(minute) + MINUTE
    	return text
    
    # Convert seconds to Hours-Minutes
    def SecondToHM(time):
    	if time < 60:
    		if IsARABIC():
    			return '%.2f %s' % (time, SECOND)
    		else:
    			return '0' + MINUTE
    
    	minute = int((time / 60) % 60)
    	hour = int((time / 60) / 60)
    
    	text = ''
    	if hour > 0:
    		text += str(hour) + HOUR
    		if hour > 0:
    			text += ' '
    
    	if minute > 0:
    		text += str(minute) + MINUTE
    	return text

     

     

    This is the hidden content, please

    • Metin2 Dev 42
    • kekw 1
    • Eyes 1
    • Smile Tear 1
    • Confused 1
    • Good 8
    • Love 47
  2. 36 minutes ago, Braxy said:

    - If you do this, each time the players enter the game (either is teleport or whatever) he has to chose the language for the quest.

    If I am not mistaken, he said "language quest" which for him mean "The Programming Language Lua", not "chose language for quest", so your post-explanation have no sense in this case.

    Quote

    I'm working on language quest today and I got a problem never seen before.

    As @Syreldar said, you can't do it with the default structure of ymir-gay, already i tried long time ago to debug it and fix it with many attempts, but no succes (at that time), i'll take a look again when i'll have time.

    • Love 2
  3. Try this. (not-tested!)

    • ..Src/Server/common/service.h
    #define ENABLE_OXEVENT_STATIC_QUIZ
    • ..Src/Server/game/src/OxEvent.cpp
    #ifdef ENABLE_OXEVENT_STATIC_QUIZ
    bool COXEventManager::Quiz(unsigned char level, int timelimit)
    {
    	SendNoticeMap(LC_TEXT("문제 입니다."), OXEVENT_MAP_INDEX, true);
    	SendNoticeMap("True or false?", OXEVENT_MAP_INDEX, true);
    	SendNoticeMap(LC_TEXT("맞으면 O, 틀리면 X로 이동해주세요"), OXEVENT_MAP_INDEX, true);
    
    	if (m_timedEvent)
    		event_cancel(&m_timedEvent);
    
    	OXEventInfoData * pkEventInfo = AllocEventInfo<OXEventInfoData>();
    	pkEventInfo->answer = static_cast<bool>(number(0, 1));
    	m_timedEvent = event_create(oxevent_timer, pkEventInfo, PASSES_PER_SEC(MINMAX(15, timelimit - 15, 60)));
    
    	SetStatus(OXEVENT_QUIZ);
    	return true;
    }
    #else
    bool COXEventManager::Quiz(unsigned char level, int timelimit)
    {
    	if (m_vec_quiz.size() == 0) return false;
    	if (level > m_vec_quiz.size()) level = m_vec_quiz.size() - 1;
    	if (m_vec_quiz[level].size() <= 0) return false;
    
    	if (timelimit < 0) timelimit = 30;
    
    	int idx = number(0, m_vec_quiz[level].size()-1);
    
    	SendNoticeMap(LC_TEXT("문제 입니다."), OXEVENT_MAP_INDEX, true);
    	SendNoticeMap(m_vec_quiz[level][idx].Quiz, OXEVENT_MAP_INDEX, true);
    	SendNoticeMap(LC_TEXT("맞으면 O, 틀리면 X로 이동해주세요"), OXEVENT_MAP_INDEX, true);
    
    	if (m_timedEvent != NULL) {
    		event_cancel(&m_timedEvent);
    	}
    
    	OXEventInfoData* answer = AllocEventInfo<OXEventInfoData>();
    	answer->answer = m_vec_quiz[level][idx].answer;
    	timelimit -= 15;
    	m_timedEvent = event_create(oxevent_timer, answer, PASSES_PER_SEC(timelimit));
    
    	SetStatus(OXEVENT_QUIZ);
    	m_vec_quiz[level].erase(m_vec_quiz[level].begin()+idx);
    	return true;
    }
    #endif

     

    • Love 1
  4. I don't understand why u guys, use search string name same as the return one for non-sense...

    ChatPacket(CHAT_TYPE_INFO, LC_TEXT("INFO_DESTROY_ITEM"), item->GetName());
    ChatPacket(CHAT_TYPE_INFO, LC_TEXT("INFO_DESTROY_ITEM_WARNING"), item->GetName());
    • ../share/locale/germany/locale_string.txt
    "INFO_DESTROY_ITEM";
    "[Info] Ai distrus %s.";
    
    "INFO_DESTROY_ITEM_WARNING";
    "|cff8ddcab[Info]|r |cffffffffItem-ul|r |cFFFFC700%s|r |cffffffffnu se mai poate recupera.|r";

     

    • Love 3
  5. If you want to compare the guild of 2 players and see if they're in the same guild you should do a global function for LUA, which can be used everywhere.

    • Src/Server/game/src/questlua_global.cpp
    Spoiler
    
    // Search for:
    	ALUA(_get_locale)
    	{
    		lua_pushstring(L, g_stLocale.c_str());
    		return 1;
    	}
    // Add after:
    	ALUA(_is_same_guild)
    	{
    		if (!lua_isstring(L, 1) || !lua_isstring(L, 2))
    		{
    			sys_err("invalid argument");
    			lua_pushboolean(L, 0);
    			return 1;
    		}
    
    		// Get name of players (arg1, arg2).
    		const std::string c_rstrName = lua_tostring(L, 1);
    		const std::string c_rstrTargetName = lua_tostring(L, 2);
    		
    		// Get character pointer by name.
    		const LPCHARACTER pkChr = CHARACTER_MANAGER::instance().FindPC(c_rstrName.c_str());
    		const LPCHARACTER pkChrTarget = CHARACTER_MANAGER::instance().FindPC(c_rstrTargetName.c_str());
    
    		// Check if players are in a guild and if they're same.
    		if ((pkChr && pkChrTarget) && (pkChr->GetGuild() && pkChrTarget->GetGuild()))
    		{
    			lua_pushboolean(L, pkChr->GetGuild() == pkChrTarget->GetGuild());
    		}
    		else
    		{
    			lua_pushboolean(L, 0);
    		}
    
    		return 1;
    	}
    
    // Search for:
    			{	"get_locale",				_get_locale				},
    // Add after:
    			{	"is_same_guild",			_is_same_guild			},

     

    • share/locale/germany/quest_function
    Spoiler
    
    is_same_guild

     

    • quest_example.lua
    Spoiler
    
    local name = pc.get_name()
    local target_name = "UnknownName"
    
    if (is_same_guild(name, target_name)) then
      -- They're in the same guild.
    else
      -- They aren't in the same guild.
    end

     

     

    • Love 2
  6. You use a dictionary since you don't save anything, in this case you should use a list.

    #WARNING_WHISPER_GM = {}
    WARNING_WHISPER_GM = []
    
    #constInfo.WARNING_WHISPER_GM.update({targetName : {}})
    constInfo.WARNING_WHISPER_GM.append(targetName)

     

    • Love 1
  7. The idea isn't bad (even if we're in 2019 and can be bypassed very easy and have no effects this), but there's some things which can be written in C++11 and a better structure, since your map have empty list initialization (since C++11).

    Here's some advices which can help you in future, maybe.

    • Your map have as value type a another std::map, for what? Since you store in key and value the same thing (player id), you should use std::list / std::vector or anything you want, but not a another std::map for no-reason if your key is same as value and no possible changes.
    • You insert all time for each login in a x map the player id, you don't check if already exist and then insert it (you could use std::find/std::map::find), you use make_pair for map and that means the key can't be duplicated, but this isn't a good practice to trying to insert 1000 times and nothing happen.
    • You check if map size is smaller or equal than 0, how can be a map size < 0? 
    • You declared a another map for iterating in a for-loop, you don't need to do that. std::map<DWORD, DWORD> map_players = [....];
    • You use map.at(...), if you use this you should use the std::out_of_range too, otherwise have no sense, because you already did a check before if exist mapIndex by map.count(index), so you can use directly operator std::map::operator[] instead of std::map::at.
    • You declared a std::string with no-reason for GetHostName() -> return a const char *, which you can use strcmp(str1, str2) instead of string.compare(str2).
    • Your key of map should be as constant, because you don't want to change it.
    • You should use also #pragma once for your header file (including: less code, avoidance of name clashes, and sometimes improvement in compilation speed.)
    • You should remove the pid of player after disconnect of the map, have no sense to remain.

    PS: If i were in your place, i didn't do the structure like this, it's a bit messed up.

    Here's some of things what i said. (didn't tested)

     

    • map_manager.h
    Spoiler
    
    #pragma once
    
    class MapManager: public singleton<MapManager>
    {
    	private:
    		std::map<const DWORD, std::list<const DWORD>> m_map_manager = {
    			{ 181, {} },
    			{ 182, {} },
    			{ 183, {} }
    		};
    		
    	public:
    		const bool      Initialize();
    		void            Clear();
    		void            Destroy();
    
    		void            Enter(const LPCHARACTER ch);
    		void            Disconnect(const LPCHARACTER ch);
    
    		const size_t    GetIPMapCount(const DWORD dwMapIndex);
    		const bool      IsPlayerIPInMap(const LPCHARACTER ch);
    		const bool      IsUniqueIPMap(const DWORD dwMapIndex);
    };
    

     

    • map_manager.cpp
    Spoiler
    
    #include "stdafx.h"
    #include "buffer_manager.h"
    #include "char.h"
    #include "char_manager.h"
    #include "map_manager.h"
    #include "desc.h"
    
    void MapManager::Initialize()
    {
    	Clear();
    }
    
    void MapManager::Destroy()
    {
    	Clear();
    }
    
    // Removes all elements from the map container (which are destroyed), leaving the container with a size of 0.
    void MapManager::Clear()
    {
    	for (auto iter = m_map_manager.begin(); iter != m_map_manager.end(); ++iter)
    		iter->second.clear();
    }
    
    // Count elements with a specific key, return 1 (if the element is found) or zero (otherwise).
    const bool MapManager::IsUniqueIPMap(const DWORD dwMapIndex)
    {
    	return m_map_manager.count(dwMapIndex) != 0;
    }
    
    // Returns the number of elements in the map container.
    const size_t MapManager::GetIPMapCount(const DWORD dwMapIndex)
    {
    	return (m_map_manager[dwMapIndex]).size();
    }
    
    /*
    char.cpp : #include "map_manager.h"
    	marriage::CManager::instance().Logout(this);
    	+ MapManager::instance().Disconnect(this);
    */
    void MapManager::Disconnect(const LPCHARACTER ch)
    {
    	if (!ch)
    		return;
    
    	const DWORD dwMapIndex = ch->GetMapIndex();	
    	if (!IsUniqueIPMap(dwMapIndex))
    		return;
    
    	// Remove the player id from map list if exist.
    	const auto it = std::find(m_map_manager[dwMapIndex].begin(), m_map_manager[dwMapIndex].end(), ch->GetPlayerID());
    	if (it != m_map_manager[dwMapIndex].end())
    		m_map_manager[dwMapIndex].erase(it);
    }
    
    void MapManager::Enter(const LPCHARACTER ch)
    {
    	if (!ch)
    		return;
    	
    	const DWORD dwPID = ch->GetPlayerID();
    	const DWORD dwMapIndex = ch->GetMapIndex();
    
    	if (!IsUniqueIPMap(dwMapIndex))
    		return;
    
    	// Return iterator to the first element satisfying the condition or last if no such element is found.
    	const auto it = std::find(m_map_manager[dwMapIndex].begin(), m_map_manager[dwMapIndex].end(), dwPID);
    
    	// If no element is found in the map, find() returns map.end().
    	if (it != m_map_manager[dwMapIndex].end())
    		return;
    
    	// Add the player id into map list.
    	m_map_manager[dwMapIndex].emplace_back(dwPID);
    }
    
    const bool MapManager::IsPlayerIPInMap(const LPCHARACTER ch)
    {	
    	if (!ch)
    		return false;
    
    	if (!IsUniqueIPMap(ch->GetMapIndex()))
    		return false;
    
    	if (GetIPMapCount() == 0)
    		return false;
    
    	LPCHARACTER pkChar = NULL;
    	// Range-based for loop, iterating the std::list with player pids.
    	for (const auto dwPID : m_map_manager[ch->GetMapIndex()])
    	{
    		if (!(pkChar = CHARACTER_MANAGER::instance().FindByPID(dwPID)))
    			continue;
    
    		if (!strcmp(ch->GetDesc()->GetHostName(), pkChar->GetDesc()->GetHostName()) && ch->GetPlayerID() != pkChar->GetPlayerID())
    			return true;
    	}
    	
    	return false;
    }

     

    BTW: Sorry for my english skills.

    • Metin2 Dev 9
    • Angry 1
    • Smile Tear 1
    • Good 4
    • Love 8
  8. Why u guys don't read text-informations from Git?

    Spoiler

    ddlzdab.png

    • EVENTS_CALENDAR_DICT is just a configuration for <Python-Live-Test>.
    • The struct of the dictionary need to get it like EVENTS_CALENDAR_DICT = calendar.GetEventsData() << Source Client < Server

    Also i'm sure you didn't understand how's work too, check with careful, you don't need to add events for each day/week etc, all are automatic.

    • 'WEEKLY' - 1 event = 48 objects (1 * 4 * 12) << Insert the event by a specific day of week for each month.
    • 'DAILY' - 1 event = 336-372 objects  (1 * 28-31 * 12) << Insert the event for all days in each month.
    • 'MONTHLY' = 1 event = 1 object << Insert the event by a specific day of month. 
    • 'ANNUALLY' - 1 event = 1 object << Insert the event by a specific month, week from month and day from week.

    I said in the main post, this is just the python module part, the dictionary with config is just for live-test-debug, you've to parser how you want, from source client which is sended by a packet from server bla bla, is ur business how you want to do it, not mine, there's missing some things, you can get the classes/funcs and extend with your features/design etc.

    pgGpaab.png

    PS: This module is for people who know what they've to do and know to read the code and make it work as they want, not for beginners.
    As i said, don't ask for support implementation, the module doesn't contain all resources, is just for "developers".

    • Metin2 Dev 1
    • Love 2
  9. Thanks to @Mithras01 that he reminded me about bisection years. (i'll update the repository when i'll have a bit more free time)

    Leap Year

    • A normal year has 365 days. 
    • A Leap Year has 366 days (the extra day is the 29th of February).
    yes Leap Years are any year that can be exactly divided by 4 (such as 2012, 2016, etc)
      not except if it can be exactly divided by 100, then it isn't (such as 2100, 2200, etc)
        yes except if it can be exactly divided by 400, then it is (such as 2000, 2400)
    def GetRangeDaysMonth(calendarMonth):
    	def GetBisectionYear(year):
    		""" Provides support for maintaining a list in sorted order without having to sort the list after each insertion.
    			Return True for leap years, False for non-leap years.
    		"""
    		return year % 4 == 0 and (year % 100 <> 0 or year % 400 == 0)
    
    	""" TODO-DONE: Fix the calendar range days-month if month is February and is a bisection year like 2016, 2020, 2024 ... """
    	return Math.OFFSETS_MONTH_RANGE_TUPLE[calendarMonth - 1] + (calendarMonth == 2 and GetBisectionYear(Math.GetCurrentYear()))

    Output-test:

    Spoiler
    
    print DBG_GetRangeDaysMonth(2, 2018) # February 2018	
    print DBG_GetRangeDaysMonth(2, 2019) # February 2019
    print DBG_GetRangeDaysMonth(2, 2020) # February 2020
    print DBG_GetRangeDaysMonth(2, 2024) # February 2024
    
    <<<< 28
    <<<< 28
    <<<< 29
    <<<< 29

     

     

    • Good 2
    • Love 1
  10. 5 minutes ago, Syriza said:

    edit: solved, empireName wasn't the problem a class was missing in ui.py

    @VegaS™

    Of course, the output of syserr or in generally the lines which showing the errors are not printed 100% the line fixed with the problem, there could be a syntax error, missing things etc. Before/after your EmpireName object too, and blocked while reading the code and your dictionary have missed some objects.

    We can't fix a error all time just because of one line in syserr, you need to investigate the files related and see what's the real problem and use debug method. 

    • Love 1
  11. If somebody needs, there's a old shit which i did for somebody who had the source with no LC_TEXT and re-convert the source and adding LC_TEXT("string_index") and write a new locale_string.txt with string_index = the string which you had before.

    Ignore the code, is a shit, take just the idea.

    # Output:
    """
    "LOCALE_STRING_01";
    "Hai ricevuto %d";
    
    "LOCALE_STRING_02";
    "Una luce proviene da un grande albero";
    
    "LOCALE_STRING_03";
    "Hai ricevuto %d ";
    
    "LOCALE_STRING_04";
    "Target is in %d channel (my channel %d)";
    
    "LOCALE_STRING_05";
    "Cannot find map (index %d)";
    
    "LOCALE_STRING_06";
    "You warp to ( %d, %d )";
    
    "LOCALE_STRING_07";
    "There is no one by that name";
    
    "LOCALE_STRING_08";
    "You warp to ( %d, %d )";
    
    "LOCALE_STRING_09";
    "<Mercatino Offline> Rinnovato di 30 giorni. Verrai sconnesso tra 3 secondi.";
    """
    
    def UpdateLocaleString_SRC():
    	basePath, baseExcludeExt = (SRC_GAME_BASE_PATH, EXTENSION_EXCLUDED_LIST_SRC)
    
    	localeStringList = []
    	for root, dirs, files in os.walk(basePath, topdown = True):
    		dirs[:] = [dir for dir in dirs if dir not in DIRECTORY_EXCLUDED_LIST]
    
    		countFunc = 0
    		for fileName in [file for file in files if GetExtension(file) not in baseExcludeExt]:
    			# Cpp file
    			fileName = '{:s}/{:s}'.format(root, fileName)
    
    			for line in open(fileName, 'r').readlines():
    				line = line.strip('\n')
    				
    				chatLocaleString = '"LOCALE_STRING_%02u")'
    				
    				if LC_TEXT in line:
    					continue
    				
    				if chatPacketFunc not in line:
    					continue
    
    				chatPointer = line.split(chatPacketFunc)[0].strip()
    				chatType = line.split(chatPacketFunc)[1].split(",")[0]
    				chatLine = chatMacroLocale % chatPointer
    				chatIndexFindBeginString = line.find('"')
    				chatIndexFindEndString = line.rfind('"') + 1
    				chatLastArgs = line[chatIndexFindEndString : len(line)]
    				chatLocaleStringFinal = chatLocaleString % countFunc
    				chatPosition = line.split(chatPacketFunc)[0]
    				if chatPointer:
    					chatPosition = line.split(chatPointer)[0]
    				
    				chatResult = chatPosition + chatPointer + chatPacketFunc + chatType + ", " + chatLine + chatLocaleStringFinal + chatLastArgs
    				
    				bAdded = AddLocaleString(fileName, line, chatResult)
    				if bAdded:
    					localeStringList.append((chatLocaleStringFinal[:-1] + ';', line[chatIndexFindBeginString : chatIndexFindEndString] + ';'))
    					countFunc += 1
    			
    	f = open("{:s}/{:s}".format(basePath, LOCALE_STRING_FILE_NAME), 'a+')
    	for first, second in localeStringList:
    		f.write('{:s}\n{:s}\n\n'.format(first, second))
    	f.close()

     

    • Love 1
    • exception KeyError

    Raised when a mapping (dictionary) key is not found in the set of existing keys.

    For example:

    mydict = {
      'EmpireName': ui.TextLine, 
      'GuildImage': ui.ExpandedImageBox
    }
    
    >>> empireName = mydict['EmpireName']
    # <ui.TextLine object at 0x06FC3310>
    
    >>> guildImage = mydict['SkillName']
    # Traceback (most recent call last):
    #   File "<stdin>", line 1, in <module>
    # KeyError: 'SkillName'

    Check your selectcharacterwindow.py and be sure you have the object called 'EmpireName'.

    • Love 1
  12. The title says it all.

    - Programming meme

    - Metin2 meme

    Please, no memes with people with disabilities.
    Please use also the spoiler function.

    I'll start:

    fuApaab.jpg 

    @martysama0134

    Spoiler

    N8Sybab.png

     

    • Lmao 1
    • Love 11
  13. 8 hours ago, WeedHex said:

    Why not from c++ directly?

    Not everything should be written in source, but here's a method. (2 years ago - i posted it in another forum)

    //@Src/Server/game/src/input_login.cpp
    // Add to the beginning of the file:
    inline bool AllowedToWarp(const DWORD dwMapIndex, const DWORD dwLevel)
    {
    	struct set_struct
    	{
    		const unsigned short map_index;
    		const unsigned short minimum_level;
    	}
    	set_fields[] =
    	{
    		{ 91, 75 },  // Grotto of Exile 1st Floor
    		{ 82, 75 },  // Grotto of Exile 2nd Floor
    		{ 216, 75 }, // Devils Catacomb
    		{ 218, 90 }, // Cape Dragon Head
    		{ 219, 90 }, // Dawn Mist Wood
    		{ 220, 90 }, // Mount Thunder
    		{ 221, 90 }  // Bay Black Sand
    	};
    	
    	for (unsigned short i = 0; i < _countof(set_fields); ++i)
    	{
    		if (dwMapIndex == set_fields[i].map_index && dwLevel < set_fields[i].minimum_level)
    			return false;
        }
    	
    	return true;
    }
    //1.) Search for:
    	ch->SendGreetMessage();
    //2.) Add after:
    	if (!AllowedToWarp(ch->GetMapIndex(), ch->GetLevel()))
    		ch->GoHome();
    • Love 7
  14. On 12/10/2018 at 10:52 PM, Exygo said:

    So basically this is just to cut the unused msm's ?

    Maybe could be used like this too, but the concept which i did for him was as unknown-reason for me.

    On 12/13/2018 at 11:55 AM, Helia01 said:

    Useless thing.
    But thanks.

    I know, but maybe for someone is useful and can take something from here.

×
×
  • Create New...

Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.