Jump to content

Leaderboard

Popular Content

Showing content with the highest reputation on 06/14/20 in all areas

  1. M2 Download Center Download Here ( Internal ) Hi, It shows in the description of your item that it's possible to add a bonus to it. It works up to 7 bonuses. Tutorial Sincerly, ASIKOO
    7 points
  2. M2 Download Center Download Here ( Internal ) VirusTotal: [Hidden Content] Download: [Hidden Content]
    4 points
  3. M2 Download Center Download Here ( Internal ) Download Here ( Latest Version ) This WE is a version compiled directly by me which includes infinite fixes and features. It's certain that you won't longer use the worldeditor_en! To make it simple, I wrote all the details about this feature and the common WE inside the relative config file: (called WorldEditorRemix.ini) ; Info: ; -) 100% translated ; -) granny2.11 ; -) F6 as Insert alternative ; -) many default features not present inside the worldeditor_en (probably, that binary was taken out from an SVN long time ago and resource hacked) such as Ins for all regions and skyboxes ; -) WASD UPLEFTDOWNRIGHT to move around (+asynchronous diagonally movements) ; -) UP-LEFT-DOWN-RIGHT to move around*10 (+asynchronous diagonally movements) ; -) config file for few things ; Output options by default ; few others such as default WASD movement ; whether or not Insert should let you go where you were before the press ; no MAI dump when saving atlas ; whether or not DevIL should compress and remove alpha from minimap.dds ; whether or not loading .mdatr building heights ; default textureset when creating maps ; overlapped tabs ; other stuff ; -) several bugfixes ; default title app name ; attempting to write to an empty textureset name when creating new maps ; ViewRadius doubled every load&save ; shadowmap.dds creation ; assert when saving atlas ; crash when adjusting height ; many buffer under/overflows ; *.mdc collision data saving (for game_test) ; not checking output options when loading maps ; water brush waterid bug (the id was increased until 256 each time the function was called; now it's based on the water height just like it should be) ; init texture map reload map crash and last 2px always blank ; square shape even for up/down height brushes ; add textureset texture button (+multiselection) ; remove textureset texture feature (just selecting a texture from the list and pressing DELETE) ; creation of empty textureset with index -1 (changed to 0) ; change baseposition button ; misspelled stuff ; skybox bottom image (nb: you also need a fixed launcher for this) ; removed boring CTRL requirement (to move the camera) when editing daylight/attr ; fixed refresh texture imagebox onKey pressing the down/up keys (like when onClicking them) ; fixed TextureSet file creation if not existing ; fixed new wolfman motion event handling ; fixed crash when editing animation attack bones and 00010.gr2 was missing ; fixed locale/ymir/mob_proto load (it autodetects the most common structures) and <map>/regen.txt load/save ; fixed ./group.txt load ; fixed load/save/edit <map>/regen.txt (very nice for "m" regens, untested for "g") ; load from PACK is available if pack/property is present! Be sure pack/Index exists! ; fixed multi-object selection crash ; fixed crash when previewing a missing texture ; fixed not clearing of old environment (e.g. skybox) when switching maps ; fixed not creating property folders in root tree (object tab) ; fixed object attachment in Model Tab ; fixed newly particles names in Effect Tab ; fixed crash when saving a .mse script with no mesh model ; fixed crash when inserting a lower gradient ; -) created new TextureSet field when creating new maps ; -) created new Change/Delete Texture buttons when double-clicking a texture ; -) created Background Music playback and Shadow Recalculate buttons ; -) created water height "set 0z", "+1z", "-1z" buttons ; -) server_attr generator ; -) every crash will generate a logs/WorldEditorRemix_{target}_{date}.dmp file useful for debugging ; -) implemented a "water path" mapsettings option (the launcher requires additional code) ; -) implemented a "wind strength" msenv option (the launcher requires additional code) ; -) the "encrypt data" feature does nothing (unimplemented) ; Note: ; 0) there are no regressions in this version! a bug here means it'd also be present in older WE versions too! ; 1) the shadow output option is tricky: when UpdateUI is called, shadows are hidden although the check is pressed (i implemented the shadow recalculate function for that) #fixed since v11 ; 2) the bgm player requires /miles and the fadein/out doesn't work until you load the map ; 3) the adjusting height button works only if mdatr height is detected ; 4) the Debug version is laggy when working on maps such as n_flame_dungeon and n_ice_dungeon (by default, because SphereRadius are intensively checked in SphereLib\spherepack.h) ; 5) if you load a map, the script panels (where you load .msa et similia) will have the camera perspective a little fucked up (0z instead of -32767z or 0x 0y -163,94z) ; 6) few tree objects are not movable and/or highlightable after placed on the ground and their selection is invisible (you can still delete 'em) ; trick: draw a square selecting a normal building and 'em, then move the building and you'll see all of 'em will be moved! ; 7) the server_attr generator will clean all the unused flags! attr[idx]&=~0xFFFFFFF8; ; 8) you can read files from pack/Index 'n stuff but be aware that Property will not be considered! #fixed since v15 ; 9) the MonsterAreaInfo features are laggy and buggy as fuck ; 10) even though you can select many textures at once (using ctrl+click on textureset list; for brushing or initializing a base texture), you can't delete more than one at the same time ; 11) the .mdatr height is tricky; if you move a building, the height will not be refreshed until you put a new building or whatever you want to trigger the update event ; 12) by default, the worldeditor tries to render only the first 8 terrain textures of a 32x32px region (nb: a 1x1 map is a 256x256 px region) ; 13) the minimap rendering cannot catch the buildings/trees inside the first 2x2 regions due a ymir cache fault and you need to set the camera to "see" them ; 14) when the textureset, environment, etc load fails, the old filename still remains loaded ; 15) the attr flag "3" (three) has no implementation, so don't use it! ; 16) load from PACK doesn't load texturesets from files for first (if they are already in pack/), and the object placer's object list will remain empty because it takes the list from property/ (and not from pack/property) ; 17) to save the regen.txt you press CTRL+S ; 18) if you enable the wireframe (f4) when on Attr Tab, you see the terrain all white ; 19) the water brush disappears when the camera renders the waterwheel small/big effect ; 20) the monster area info goes under ground if you're outside the relative sectree ; 21) the full skybox may be displayed only after the top picture has been added (if the other textures have already been inserted) ; 22) the slider in the Attr Tab is something like "16 photoshop layers" in which you can split your attrs; not so helpful and quite confusing sometimes ; 23) the fixed model - object attachment attaches static objects (hairs'skeleton will not mirror the playing animation) ; 24) in environment tab, if you insert lower gradients, you may end up with an out of range crash #fixed since v30 ; 25) brushes working out-of-screen/map-range may affect random terrain places ; TODO: ; A) look at more than 8 textures for region -> DONE ; B) create a shortcut to fix the #5 note -> DONE ; C) disable the radius <= GetRadius()+0.0001f check to fix the #4 note -> REJECTED ; the worldeditor_en calls this assert and, if ignored, the lag ceases to exist (this will not occur in source version) ; at least, if the release version is not a problem for you, use that in those few cases when .mse are abused and try to kill the debug one ; D) translation in more languages other than english -> REJECTED ; english should be enough! ; E) alternative path for d: -> REJECTED ; you can mount d as a subpath of c like this: ; subst d: "c:\mt2stuff" ; F) need to fix note #19 #25 -> TODO [shortcuts] ; ### SHORTCUTS ; # ESC(ape) Clean cursor ; # Canc(el|Delete) Delete stuff such as selected buildings ; # Ctrl+S Save map ; # Ins(ert) or F6 Save shadowmap|minimap.dds ; # F3 BoundGrid Show/Hide ; # F4 Render UI Show/Hide ; # F11 WireFrame Show/Hide ; # R Reload Texture ; # Z and X Decrease/Increase Texture Splat by 0.1 ; # CapsLock Show GaussianCubic effect if shadows are displayed ; # L-Shift+1-6 Show TextureCountThreshold flags (&2-7) as colors on the ground ; # L-Shift+8 Set Max Showable texture to 8 (de-fix note 12) ; # L-Shift+0 Set Max Showable texture to 255 (fix note 12) ; # H Refresh MDATR Heights (useful when you move an object) (fix note 11) ; # Y Set Perspective as default (fix note 5) ; # T Set the Camera to catch all the object on the screen (w/a note 13) then you'll be ready to press Insert/F6 ; # DO NOT HAVE AN OBJECT SELECTED WHEN USING THOSE SHORTCUTS (MW1-7) ; # MouseWheel+1 move cursor x rotation ; # MouseWheel+2 move cursor y rotation ; # MouseWheel+3 move cursor z rotation ; # MouseWheel+4 move cursor height base (1x) ; # MouseWheel+5 move cursor height base (0.5x) ; # MouseWheel+6 move cursor height base (0.05x) ; # MouseWheel+7 move cursor ambience scale (1x) ; # MouseWheel+Q move selected object height base (1x) ; # MouseWheel+9 move selected object x position (1x) (+asyncronous) ; # MouseWheel+0 move selected object y position (1x) (+asyncronous) ; # MW+RSHIFT+9|0 as above but *10x (+asyncronous) ; # MW+RCONTROL+9|0 as above but *100x (+asyncronous) ; # MouseLeft Insert Objects ; # MouseRight Move camera (it could require CTRL too) ; # SPACE Start move/selected animation in Object/Effect/Fly CB ; # ESC Stop animation in Effect/Fly CB [config] ; ### CONFIG OPTIONS VIEW_CHAR_OUTPUT_BY_DEFAULT = 1 VIEW_SHADOW_OUTPUT_BY_DEFAULT = 1 VIEW_WATER_OUTPUT_BY_DEFAULT = 1 ; WINDOW_HEIGHT_SIZE = 1080 ; WINDOW_WIDTH_SIZE = 1920 WINDOW_FOV_SIZE = 45 ; #100 = 1px (minimal px movement when pressing WASD) WASD_MINIMAL_MOVE = 100 ; came back from where you were before pressing Insert/F6 NO_GOTO_AFTER_INSERT = 1 ; disable MAI dumps when saving atlas and/or pressing Insert/F6 NOMAI_ATLAS_DUMP = 1 ; disable minimap.dds alpha saving and enable compression NOMINIMAP_RAWALPHA = 1 ; enable .mdatr height collision loading when moving on buildings or adjusting terrain DETECT_MDATR_HEIGHT = 1 ; disable fog when loading maps NOFOG_ONMAPLOAD = 1 ; refresh all checkbox configurations when loading maps 'n stuff REFRESHALL_ONUPDATEUI = 0 ; set a default mapname prefix when creating new maps ("" to disable) NEW_MAP_MAPNAME_PREFIX = "metin2_map_" ; display a default textureset when creating new maps ("" to disable) ; note: it loads the filepath if exists, otherwise it will create an empty textureset file NEWMAP_TEXTURESETLOADPATH = "textureset\metin2_a1.txt" ; create a default textureset as "textureset/{mapname}.txt" ; note: this option is not considered if NEWMAP_TEXTURESETLOADPATH is not empty. [before v24] ; note: this option is not considered if the TextureSet path input is not empty when creating a new map [since v24] NEWMAP_TEXTURESETSAVEASMAPNAME = 1 ; remove the weird attr flags from the generated server_attr SERVERATTR_REMOVE_WEIRD_FLAGS = 1 ; show diffuse lighting to object VIEW_OBJECT_LIGHTING = 1 ; path of mob_proto used for regen MOB_PROTO_PATH = "locale/ymir/mob_proto" ; select monster area info checkbox at startup VIEW_MONSTER_AREA_INFO = 0 ; brush cursor / object selection color RGB float between 0.0 to 1.0 (default: green -> 0 1 0) RENDER_CURSOR_COLOR_R = 0.0 RENDER_CURSOR_COLOR_G = 1.0 RENDER_CURSOR_COLOR_B = 0.0 Download: [Hidden Content] How To Map: This release will not cover this part. Look at CryPrime`s tutorials to understand how to do it. About the ServerAttr Generator: (since v14) This is a beta function but it should work fine. I tested it on gm_guild_build (1x1), metin2_map_a1 (4x5), metin2_map_trent (2x2), metin2_n_snowm_01 (6x6) and the result was the same as the blackyuko map editor. (I use a different lzo version and I clean deprecated and useless flags, so the size is different from this last one but the "final image" will be the same; using game_test to fix his server_attr will let mine and his perfectly equal byte per byte) I also give you the source code of my server_attr generator function. CLICK A server_attr file is based on all the attr.atr files merged into a one raw RGBA image and each one scaled from 256x256 to 512x512. After that, the image will be splitted into sectors of 128x128 px and each one compressed using lzo compression. The server_attr header is composed by the size of the map*4. (e.g. a 4x4 will have a 16x16 size with 256 sectors inside) (gj ymir CLICK) An uncompressed server_attr sector is just like this: CLICK (the sub 4 byte header is the size returned by the LzoCompress which indicates how much the compressed sector data are large) Each attr.atr is just like this: CLICK (the header is composed of 6 byte in total: 3 WORDs respectively for version, width and height; they are always 2634, 1, 1 so don't bother about it) A single attr.atr scaled from 256x256 to 512x512 will be just like this: CLICK You can use the game_test (from source) to perform few tasks like: Create a server_attr from a .mcd file (I won't suggest it) a <collision data filename> <map directory> Regenerate an old server_attr to server_attr.new using the current lzo compression and cleaning useless flag CLICK c <filename> Other stuff such as b to create a character instance or q to quit About the SkyBox Bottom pic fix: (since v21) Both metin2launch.exe and worldeditor.exe should be edited to see the bottom pic of the skybox. Ymir messed up the code wrongly flipping the bottom image. Open ./Srcs/Client/EterLib/SkyBox.cpp and replace: ////// Face 5: BOTTOM v3QuadPoints[0] = D3DXVECTOR3(1.0f, -1.0f, -1.0f); v3QuadPoints[1] = D3DXVECTOR3(1.0f, 1.0f, -1.0f); v3QuadPoints[2] = D3DXVECTOR3(-1.0f, -1.0f, -1.0f); v3QuadPoints[3] = D3DXVECTOR3(-1.0f, 1.0f, -1.0f); with: ////// Face 5: BOTTOM v3QuadPoints[0] = D3DXVECTOR3(1.0f, 1.0f, -1.0f); v3QuadPoints[1] = D3DXVECTOR3(1.0f, -1.0f, -1.0f); v3QuadPoints[2] = D3DXVECTOR3(-1.0f, 1.0f, -1.0f); v3QuadPoints[3] = D3DXVECTOR3(-1.0f, -1.0f, -1.0f); then recompile. Credits:
    3 points
  4. Thanks for release, already i did something like this in a topic from 2019, so here's: locale/en/locale_game.txt ATTR_6TH_7TH_POSSIBILITY You can add an additional bonus. root/uiToolTip.py [Hidden Content]
    2 points
  5. Hey, In different Games always Ninja's have the ability to hide example in Wow when Rogue hide the Monsters losing their aggro, but in Metin2 when you hide with the Skill Eunhyung the Monster still have aggro on you, this seems little wrong, Hided and Monster still can reach you? Maybe because you can use Poison / Bleeding / Fire that make this little bit difficult, but i think i come up with a solution. I think the only part that missing is when you clean the Target from the Monster to move again back but is not really necessary. * I Added also for GMs in /in command the function. In the Tutorial bellow, you can find the Function ForgetMyAttacker with my improvements.
    1 point
  6. M2 Download Center Download Here ( Internal ) Hi there. While cleaning out "my closet", I found this thing I developed between 2014-2015 - maybe(?) - for my, at that moment, server. Since it's now closed, and I won't use it, I'm sharing it with you guys. Note: Didn't do the scrollbar, wasn't needed for me, so yeah. Now, let's start with opening your locale_game.txt and adding these lines: QUESTCATEGORY_0 Main Quests QUESTCATEGORY_1 Sub Quests QUESTCATEGORY_2 Collect Quests QUESTCATEGORY_3 Levelup Quests QUESTCATEGORY_4 Scroll Quests QUESTCATEGORY_5 System Quests Alright, now find your characterwindow.py (uiscript?) and you can either comment Quest_Page children or simply remove them all. Moving on to your interfaceModule.py find this line self.BINARY_RecvQuest(index, name, "file", localeInfo.GetLetterImageName()) and replace it with self.wndCharacter.questCategory.RecvQuest(self.BINARY_RecvQuest, index, name) Ok, then we are at the most, let's say, difficult part of this. Open your uiCharacter.py and just as you did in your characterwindow.py, remove or simply comment any single line related to quests. You can just search for these vars: self.questShowingStartIndex self.questScrollBar self.questSlot self.questNameList self.questLastTimeList self.questLastCountList Once you did that, you just: # Find these lines self.soloEmotionSlot = self.GetChild("SoloEmotionSlot") self.dualEmotionSlot = self.GetChild("DualEmotionSlot") self.__SetEmotionSlot() # And add the following import uiQuestCategory self.questCategory = uiQuestCategory.QuestCategoryWindow(self.pageDict["QUEST"]) # Find this def OnUpdate(self): self.__UpdateQuestClock() # Replace it with def OnUpdate(self): self.questCategory.OnUpdate() And we're done with the client-side. I attached some extra elements needed (such as the main python file (uiQuestCategory.py) and some image resources). Remember to edit the path linked to these images in that file. For the server-side... Well, screw it, uploaded it too. Too lazy to write. It has only a new quest function (q.getcurrentquestname()) and a few things to add in your questlib.lua. Btw, not sure if you have it, but if not, just add this extra function in ui.Button() (ui.py - class Button). def SetTextAlignLeft(self, text, height = 4): if not self.ButtonText: textLine = TextLine() textLine.SetParent(self) textLine.SetPosition(27, self.GetHeight()/2) textLine.SetVerticalAlignCenter() textLine.SetHorizontalAlignLeft() textLine.Show() self.ButtonText = textLine #Äù½ºÆ® ¸®½ºÆ® UI¿¡ ¸ÂÃç À§Ä¡ ÀâÀ½ self.ButtonText.SetText(text) self.ButtonText.SetPosition(27, self.GetHeight()/2) self.ButtonText.SetVerticalAlignCenter() self.ButtonText.SetHorizontalAlignLeft() Forgot the source part, fml, here it is. Add it to your questlua_quest.cpp. int quest_get_current_quest_name(lua_State* L) { CQuestManager& q = CQuestManager::instance(); PC* pPC = q.GetCurrentPC(); lua_pushstring(L, pPC->GetCurrentQuestName().c_str()); return 1; } void RegisterQuestFunctionTable() { luaL_reg quest_functions[] = { { "getcurrentquestname", quest_get_current_quest_name}, { NULL, NULL } }; CQuestManager::instance().AddLuaFunctionTable("q", quest_functions); } Now, finally, have fun and bye!
    1 point
  7. Hey, I want to share a fix about the SKILL_MUYEONG, Official fixed this before some months ago, but nobody care in such details so we still have the same issue in our servers. The SKILL_MUYEONG is still attacking while you riding but doesn't make any damage. Preview with Fixed SKILL_MUYEONG while you riding: [Hidden Content]
    1 point
  8. Try to find in your game source ladder point request and change to 0
    1 point
  9. You mean about ladder_points right?
    1 point
  10. Try this #include "stdafx.h" #include "utils.h" #include "config.h" #include "char.h" #include "packet.h" #include "desc_client.h" #include "buffer_manager.h" #include "char_manager.h" #include "db.h" #include "guild.h" #include "guild_manager.h" #include "affect.h" #include "p2p.h" #include "questmanager.h" #include "building.h" #include "locale_service.h" #include "log.h" #include "questmanager.h" SGuildMember::SGuildMember(LPCHARACTER ch, BYTE grade, DWORD offer_exp) : pid(ch->GetPlayerID()), grade(grade), is_general(0), job(ch->GetJob()), level(ch->GetLevel()), offer_exp(offer_exp), name(ch->GetName()) {} SGuildMember::SGuildMember(DWORD pid, BYTE grade, BYTE is_general, BYTE job, BYTE level, DWORD offer_exp, char* name) : pid(pid), grade(grade), is_general(is_general), job(job), level(level), offer_exp(offer_exp), name(name) {} namespace { struct FGuildNameSender { FGuildNameSender(DWORD id, const char* guild_name) : id(id), name(guild_name) { p.header = HEADER_GC_GUILD; p.subheader = GUILD_SUBHEADER_GC_GUILD_NAME; p.size = sizeof(p) + sizeof(DWORD) + GUILD_NAME_MAX_LEN; } void operator() (LPCHARACTER ch) { LPDESC d = ch->GetDesc(); if (d) { d->BufferedPacket(&p, sizeof(p)); d->BufferedPacket(&id, sizeof(id)); d->Packet(name, GUILD_NAME_MAX_LEN); } } DWORD id; const char * name; TPacketGCGuild p; }; } CGuild::CGuild(TGuildCreateParameter & cp) { Initialize(); m_general_count = 0; m_iMemberCountBonus = 0; strlcpy(m_data.name, cp.name, sizeof(m_data.name)); m_data.master_pid = cp.master->GetPlayerID(); strlcpy(m_data.grade_array[0].grade_name, LC_TEXT("길드장"), sizeof(m_data.grade_array[0].grade_name)); m_data.grade_array[0].auth_flag = GUILD_AUTH_ADD_MEMBER | GUILD_AUTH_REMOVE_MEMBER | GUILD_AUTH_NOTICE | GUILD_AUTH_USE_SKILL; for (int i = 1; i < GUILD_GRADE_COUNT; ++i) { strlcpy(m_data.grade_array.grade_name, LC_TEXT("길드원"), sizeof(m_data.grade_array.grade_name)); m_data.grade_array.auth_flag = 0; } std::auto_ptr<SQLMsg> pmsg (DBManager::instance().DirectQuery( "INSERT INTO guild%s(name, master, sp, level, exp, skill_point, skill) " "VALUES('%s', %u, 1000, 20, 0, 0, '\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0')", get_table_postfix(), m_data.name, m_data.master_pid)); // TODO if error occur? m_data.guild_id = pmsg->Get()->uiInsertID; for (int i = 0; i < GUILD_GRADE_COUNT; ++i) { DBManager::instance().Query("INSERT INTO guild_grade%s VALUES(%u, %d, '%s', %d)", get_table_postfix(), m_data.guild_id, i + 1, m_data.grade_array.grade_name, m_data.grade_array.auth_flag); } ComputeGuildPoints(); m_data.power = m_data.max_power; m_data.ladder_point = 0; db_clientdesc->DBPacket(HEADER_GD_GUILD_CREATE, 0, &m_data.guild_id, sizeof(DWORD)); TPacketGuildSkillUpdate guild_skill; guild_skill.guild_id = m_data.guild_id; guild_skill.amount = 0; guild_skill.skill_point = 0; memset(guild_skill.skill_levels, 0, GUILD_SKILL_COUNT); db_clientdesc->DBPacket(HEADER_GD_GUILD_SKILL_UPDATE, 0, &guild_skill, sizeof(guild_skill)); // TODO GUILD_NAME CHARACTER_MANAGER::instance().for_each_pc(FGuildNameSender(GetID(), GetName())); /* TPacketDGGuildMember p; memset(&p, 0, sizeof(p)); p.dwPID = cp.master->GetPlayerID(); p.bGrade = 15; AddMember(&p); */ RequestAddMember(cp.master, GUILD_LEADER_GRADE); } void CGuild::Initialize() { memset(&m_data, 0, sizeof(m_data)); m_data.level = 20; for (int i = 0; i < GUILD_SKILL_COUNT; ++i) abSkillUsable = true; m_iMemberCountBonus = 0; } CGuild::~CGuild() { } void CGuild::RequestAddMember(LPCHARACTER ch, int grade) { if (ch->GetGuild()) return; TPacketGDGuildAddMember gd; if (m_member.find(ch->GetPlayerID()) != m_member.end()) { sys_err("Already a member in guild %s[%d]", ch->GetName(), ch->GetPlayerID()); return; } gd.dwPID = ch->GetPlayerID(); gd.dwGuild = GetID(); gd.bGrade = grade; db_clientdesc->DBPacket(HEADER_GD_GUILD_ADD_MEMBER, 0, &gd, sizeof(TPacketGDGuildAddMember)); } void CGuild::AddMember(TPacketDGGuildMember * p) { TGuildMemberContainer::iterator it; if ((it = m_member.find(p->dwPID)) == m_member.end()) m_member.insert(std::make_pair(p->dwPID, TGuildMember(p->dwPID, p->bGrade, p->isGeneral, p->bJob, p->bLevel, p->dwOffer, p->szName))); else { TGuildMember & r_gm = it->second; r_gm.pid = p->dwPID; r_gm.grade = p->bGrade; r_gm.job = p->bJob; r_gm.offer_exp = p->dwOffer; r_gm.is_general = p->isGeneral; } CGuildManager::instance().Link(p->dwPID, this); SendListOneToAll(p->dwPID); LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(p->dwPID); sys_log(0, "GUILD: AddMember PID %u, grade %u, job %u, level %u, offer %u, name %s ptr %p", p->dwPID, p->bGrade, p->bJob, p->bLevel, p->dwOffer, p->szName, get_pointer(ch)); if (ch) LoginMember(ch); else P2PLoginMember(p->dwPID); } bool CGuild::RequestRemoveMember(DWORD pid) { TGuildMemberContainer::iterator it; if ((it = m_member.find(pid)) == m_member.end()) return false; if (it->second.grade == GUILD_LEADER_GRADE) return false; TPacketGuild gd_guild; gd_guild.dwGuild = GetID(); gd_guild.dwInfo = pid; db_clientdesc->DBPacket(HEADER_GD_GUILD_REMOVE_MEMBER, 0, &gd_guild, sizeof(TPacketGuild)); return true; } bool CGuild::RemoveMember(DWORD pid) { sys_log(0, "Receive Guild P2P RemoveMember"); TGuildMemberContainer::iterator it; if ((it = m_member.find(pid)) == m_member.end()) return false; if (it->second.grade == GUILD_LEADER_GRADE) return false; if (it->second.is_general) m_general_count--; m_member.erase(it); SendOnlineRemoveOnePacket(pid); CGuildManager::instance().Unlink(pid); LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(pid); if (ch) { //GuildRemoveAffect(ch); m_memberOnline.erase(ch); ch->SetGuild(NULL); } if ( LC_IsBrazil() == true ) { DBManager::instance().Query("REPLACE INTO guild_invite_limit VALUES(%d, %d)", GetID(), get_global_time()); } return true; } void CGuild::P2PLoginMember(DWORD pid) { if (m_member.find(pid) == m_member.end()) { sys_err("GUILD [%d] is not a memeber of guild.", pid); return; } m_memberP2POnline.insert(pid); // Login event occur + Send List TGuildMemberOnlineContainer::iterator it; for (it = m_memberOnline.begin(); it!=m_memberOnline.end();++it) SendLoginPacket(*it, pid); } void CGuild::LoginMember(LPCHARACTER ch) { if (m_member.find(ch->GetPlayerID()) == m_member.end()) { sys_err("GUILD %s[%d] is not a memeber of guild.", ch->GetName(), ch->GetPlayerID()); return; } ch->SetGuild(this); // Login event occur + Send List TGuildMemberOnlineContainer::iterator it; for (it = m_memberOnline.begin(); it!=m_memberOnline.end();++it) SendLoginPacket(*it, ch); m_memberOnline.insert(ch); SendAllGradePacket(ch); SendGuildInfoPacket(ch); SendListPacket(ch); SendSkillInfoPacket(ch); SendEnemyGuild(ch); //GuildUpdateAffect(ch); } void CGuild::P2PLogoutMember(DWORD pid) { if (m_member.find(pid)==m_member.end()) { sys_err("GUILD [%d] is not a memeber of guild.", pid); return; } m_memberP2POnline.erase(pid); // Logout event occur TGuildMemberOnlineContainer::iterator it; for (it = m_memberOnline.begin(); it!=m_memberOnline.end();++it) { SendLogoutPacket(*it, pid); } } void CGuild::LogoutMember(LPCHARACTER ch) { if (m_member.find(ch->GetPlayerID())==m_member.end()) { sys_err("GUILD %s[%d] is not a memeber of guild.", ch->GetName(), ch->GetPlayerID()); return; } //GuildRemoveAffect(ch); //ch->SetGuild(NULL); m_memberOnline.erase(ch); // Logout event occur TGuildMemberOnlineContainer::iterator it; for (it = m_memberOnline.begin(); it!=m_memberOnline.end();++it) { SendLogoutPacket(*it, ch); } } void CGuild::SendOnlineRemoveOnePacket(DWORD pid) { TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; pack.size = sizeof(pack)+4; pack.subheader = GUILD_SUBHEADER_GC_REMOVE; TEMP_BUFFER buf; buf.write(&pack,sizeof(pack)); buf.write(&pid, sizeof(pid)); TGuildMemberOnlineContainer::iterator it; for (it = m_memberOnline.begin(); it!=m_memberOnline.end();++it) { LPDESC d = (*it)->GetDesc(); if (d) d->Packet(buf.read_peek(), buf.size()); } } void CGuild::SendAllGradePacket(LPCHARACTER ch) { LPDESC d = ch->GetDesc(); if (!d) return; TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; pack.size = sizeof(pack)+1+GUILD_GRADE_COUNT*(sizeof(TGuildGrade)+1); pack.subheader = GUILD_SUBHEADER_GC_GRADE; TEMP_BUFFER buf; buf.write(&pack, sizeof(pack)); BYTE n = 15; buf.write(&n, 1); for (int i=0;i<GUILD_GRADE_COUNT;i++) { BYTE j = i+1; buf.write(&j, 1); buf.write(&m_data.grade_array, sizeof(TGuildGrade)); } d->Packet(buf.read_peek(), buf.size()); } void CGuild::SendListOneToAll(LPCHARACTER ch) { SendListOneToAll(ch->GetPlayerID()); } void CGuild::SendListOneToAll(DWORD pid) { TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; pack.size = sizeof(TPacketGCGuild); pack.subheader = GUILD_SUBHEADER_GC_LIST; pack.size += sizeof(TGuildMemberPacketData); char c[CHARACTER_NAME_MAX_LEN+1]; memset(c, 0, sizeof(c)); TGuildMemberContainer::iterator cit = m_member.find(pid); if (cit == m_member.end()) return; for (TGuildMemberOnlineContainer::iterator it = m_memberOnline.begin(); it!= m_memberOnline.end(); ++it) { LPDESC d = (*it)->GetDesc(); if (!d) continue; TEMP_BUFFER buf; buf.write(&pack, sizeof(pack)); cit->second._dummy = 1; buf.write(&(cit->second), sizeof(DWORD) * 3 +1); buf.write(cit->second.name.c_str(), cit->second.name.length()); buf.write(c, CHARACTER_NAME_MAX_LEN + 1 - cit->second.name.length()); d->Packet(buf.read_peek(), buf.size()); } } void CGuild::SendListPacket(LPCHARACTER ch) { /* List Packet Header Count (byte) [ ... name_flag 1 - 이름을 보내느냐 안보내느냐 name CHARACTER_NAME_MAX_LEN+1 ] * Count */ LPDESC d; if (!(d=ch->GetDesc())) return; TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; pack.size = sizeof(TPacketGCGuild); pack.subheader = GUILD_SUBHEADER_GC_LIST; pack.size += sizeof(TGuildMemberPacketData) * m_member.size(); TEMP_BUFFER buf; buf.write(&pack,sizeof(pack)); char c[CHARACTER_NAME_MAX_LEN+1]; for (TGuildMemberContainer::iterator it = m_member.begin(); it != m_member.end(); ++it) { it->second._dummy = 1; buf.write(&(it->second), sizeof(DWORD)*3+1); strlcpy(c, it->second.name.c_str(), MIN(sizeof(c), it->second.name.length() + 1)); buf.write(c, CHARACTER_NAME_MAX_LEN+1 ); if ( test_server ) sys_log(0 ,"name %s job %d ", it->second.name.c_str(), it->second.job ); } d->Packet(buf.read_peek(), buf.size()); for (TGuildMemberOnlineContainer::iterator it = m_memberOnline.begin(); it != m_memberOnline.end(); ++it) { SendLoginPacket(ch, *it); } for (TGuildMemberP2POnlineContainer::iterator it = m_memberP2POnline.begin(); it != m_memberP2POnline.end(); ++it) { SendLoginPacket(ch, *it); } } void CGuild::SendLoginPacket(LPCHARACTER ch, LPCHARACTER chLogin) { SendLoginPacket(ch, chLogin->GetPlayerID()); } void CGuild::SendLoginPacket(LPCHARACTER ch, DWORD pid) { /* Login Packet header 4 pid 4 */ if (!ch->GetDesc()) return; TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; pack.size = sizeof(pack)+4; pack.subheader = GUILD_SUBHEADER_GC_LOGIN; TEMP_BUFFER buf; buf.write(&pack, sizeof(pack)); buf.write(&pid, 4); ch->GetDesc()->Packet(buf.read_peek(), buf.size()); } void CGuild::SendLogoutPacket(LPCHARACTER ch, LPCHARACTER chLogout) { SendLogoutPacket(ch, chLogout->GetPlayerID()); } void CGuild::SendLogoutPacket(LPCHARACTER ch, DWORD pid) { /* Logout Packet header 4 pid 4 */ if (!ch->GetDesc()) return; TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; pack.size = sizeof(pack)+4; pack.subheader = GUILD_SUBHEADER_GC_LOGOUT; TEMP_BUFFER buf; buf.write(&pack, sizeof(pack)); buf.write(&pid, 4); ch->GetDesc()->Packet(buf.read_peek(), buf.size()); } void CGuild::LoadGuildMemberData(SQLMsg* pmsg) { if (pmsg->Get()->uiNumRows == 0) return; m_general_count = 0; m_member.clear(); for (uint i = 0; i < pmsg->Get()->uiNumRows; ++i) { MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult); DWORD pid = strtoul(row[0], (char**) NULL, 10); BYTE grade = (BYTE) strtoul(row[1], (char**) NULL, 10); BYTE is_general = 0; if (row[2] && *row[2] == '1') is_general = 1; DWORD offer = strtoul(row[3], (char**) NULL, 10); BYTE level = (BYTE)strtoul(row[4], (char**) NULL, 10); BYTE job = (BYTE)strtoul(row[5], (char**) NULL, 10); char * name = row[6]; if (is_general) m_general_count++; m_member.insert(std::make_pair(pid, TGuildMember(pid, grade, is_general, job, level, offer, name))); CGuildManager::instance().Link(pid, this); } } void CGuild::LoadGuildGradeData(SQLMsg* pmsg) { /* // 15개 아닐 가능성 존재 if (pmsg->Get()->iNumRows != 15) { sys_err("Query failed: getting guild grade data. GuildID(%d)", GetID()); return; } */ for (uint i = 0; i < pmsg->Get()->uiNumRows; ++i) { MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult); BYTE grade = 0; str_to_number(grade, row[0]); char * name = row[1]; DWORD auth = strtoul(row[2], NULL, 10); if (grade >= 1 && grade <= 15) { //sys_log(0, "GuildGradeLoad %s", name); strlcpy(m_data.grade_array[grade-1].grade_name, name, sizeof(m_data.grade_array[grade-1].grade_name)); m_data.grade_array[grade-1].auth_flag = auth; } } } void CGuild::LoadGuildData(SQLMsg* pmsg) { if (pmsg->Get()->uiNumRows == 0) { sys_err("Query failed: getting guild data %s", pmsg->stQuery.c_str()); return; } MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult); m_data.master_pid = strtoul(row[0], (char **)NULL, 10); m_data.level = (BYTE)strtoul(row[1], (char **)NULL, 10); m_data.exp = strtoul(row[2], (char **)NULL, 10); strlcpy(m_data.name, row[3], sizeof(m_data.name)); m_data.skill_point = (BYTE) strtoul(row[4], (char **) NULL, 10); if (row[5]) thecore_memcpy(m_data.abySkill, row[5], sizeof(BYTE) * GUILD_SKILL_COUNT); else memset(m_data.abySkill, 0, sizeof(BYTE) * GUILD_SKILL_COUNT); m_data.power = MAX(0, strtoul(row[6], (char **) NULL, 10)); str_to_number(m_data.ladder_point, row[7]); if (m_data.ladder_point < 0) m_data.ladder_point = 0; str_to_number(m_data.win, row[8]); str_to_number(m_data.draw, row[9]); str_to_number(m_data.loss, row[10]); str_to_number(m_data.gold, row[11]); ComputeGuildPoints(); } void CGuild::Load(DWORD guild_id) { Initialize(); m_data.guild_id = guild_id; DBManager::instance().FuncQuery(std::bind1st(std::mem_fun(&CGuild::LoadGuildData), this), "SELECT master, level, exp, name, skill_point, skill, sp, ladder_point, win, draw, loss, gold FROM guild%s WHERE id = %u", get_table_postfix(), m_data.guild_id); sys_log(0, "GUILD: loading guild id %12s %u", m_data.name, guild_id); DBManager::instance().FuncQuery(std::bind1st(std::mem_fun(&CGuild::LoadGuildGradeData), this), "SELECT grade, name, auth+0 FROM guild_grade%s WHERE guild_id = %u", get_table_postfix(), m_data.guild_id); DBManager::instance().FuncQuery(std::bind1st(std::mem_fun(&CGuild::LoadGuildMemberData), this), "SELECT pid, grade, is_general, offer, level, job, name FROM guild_member%s, player%s WHERE guild_id = %u and pid = id", get_table_postfix(), get_table_postfix(), guild_id); } void CGuild::SaveLevel() { DBManager::instance().Query("UPDATE guild%s SET level=%d, exp=%u, skill_point=%d WHERE id = %u", get_table_postfix(), m_data.level,m_data.exp, m_data.skill_point,m_data.guild_id); } void CGuild::SendDBSkillUpdate(int amount) { TPacketGuildSkillUpdate guild_skill; guild_skill.guild_id = m_data.guild_id; guild_skill.amount = amount; guild_skill.skill_point = m_data.skill_point; thecore_memcpy(guild_skill.skill_levels, m_data.abySkill, sizeof(BYTE) * GUILD_SKILL_COUNT); db_clientdesc->DBPacket(HEADER_GD_GUILD_SKILL_UPDATE, 0, &guild_skill, sizeof(guild_skill)); } void CGuild::SaveSkill() { char text[GUILD_SKILL_COUNT * 2 + 1]; DBManager::instance().EscapeString(text, sizeof(text), (const char *) m_data.abySkill, sizeof(m_data.abySkill)); DBManager::instance().Query("UPDATE guild%s SET sp = %d, skill_point=%d, skill='%s' WHERE id = %u", get_table_postfix(), m_data.power, m_data.skill_point, text, m_data.guild_id); } TGuildMember* CGuild::GetMember(DWORD pid) { TGuildMemberContainer::iterator it = m_member.find(pid); if (it==m_member.end()) return NULL; return &it->second; } #ifdef ENABLE_SHOW_LIDER_AND_GENERAL_GUILD BYTE CGuild::NewIsGuildGeneral(DWORD pid) { for ( TGuildMemberContainer::iterator iter = m_member.begin(); iter != m_member.end(); iter++ ) { if ( iter->first == pid ) return iter->second.is_general; } return 0; } #endif DWORD CGuild::GetMemberPID(const std::string& strName) { for ( TGuildMemberContainer::iterator iter = m_member.begin(); iter != m_member.end(); iter++ ) { if ( iter->second.name == strName ) return iter->first; } return 0; } void CGuild::__P2PUpdateGrade(SQLMsg* pmsg) { if (pmsg->Get()->uiNumRows) { MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult); int grade = 0; const char* name = row[1]; int auth = 0; str_to_number(grade, row[0]); str_to_number(auth, row[2]); if (grade <= 0) return; grade--; // 등급 명칭이 현재와 다르다면 업데이트 if (0 != strcmp(m_data.grade_array[grade].grade_name, name)) { strlcpy(m_data.grade_array[grade].grade_name, name, sizeof(m_data.grade_array[grade].grade_name)); TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; pack.size = sizeof(pack); pack.subheader = GUILD_SUBHEADER_GC_GRADE_NAME; TOneGradeNamePacket pack2; pack.size += sizeof(pack2); pack2.grade = grade + 1; strlcpy(pack2.grade_name, name, sizeof(pack2.grade_name)); TEMP_BUFFER buf; buf.write(&pack,sizeof(pack)); buf.write(&pack2,sizeof(pack2)); for (TGuildMemberOnlineContainer::iterator it = m_memberOnline.begin(); it!=m_memberOnline.end(); ++it) { LPDESC d = (*it)->GetDesc(); if (d) d->Packet(buf.read_peek(), buf.size()); } } if (m_data.grade_array[grade].auth_flag != auth) { m_data.grade_array[grade].auth_flag = auth; TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; pack.size = sizeof(pack); pack.subheader = GUILD_SUBHEADER_GC_GRADE_AUTH; TOneGradeAuthPacket pack2; pack.size+=sizeof(pack2); pack2.grade = grade+1; pack2.auth = auth; TEMP_BUFFER buf; buf.write(&pack,sizeof(pack)); buf.write(&pack2,sizeof(pack2)); for (TGuildMemberOnlineContainer::iterator it = m_memberOnline.begin(); it!=m_memberOnline.end(); ++it) { LPDESC d = (*it)->GetDesc(); if (d) { d->Packet(buf.read_peek(), buf.size()); } } } } } void CGuild::P2PChangeGrade(BYTE grade) { DBManager::instance().FuncQuery(std::bind1st(std::mem_fun(&CGuild::__P2PUpdateGrade),this), "SELECT grade, name, auth+0 FROM guild_grade%s WHERE guild_id = %u and grade = %d", get_table_postfix(), m_data.guild_id, grade); } namespace { struct FSendChangeGrade { BYTE grade; TPacketGuild p; FSendChangeGrade(DWORD guild_id, BYTE grade) : grade(grade) { p.dwGuild = guild_id; p.dwInfo = grade; } void operator()() { db_clientdesc->DBPacket(HEADER_GD_GUILD_CHANGE_GRADE, 0, &p, sizeof(p)); } }; } void CGuild::ChangeGradeName(BYTE grade, const char* grade_name) { if (grade == 1) return; if (grade < 1 || grade > 15) { sys_err("Wrong guild grade value %d", grade); return; } if (strlen(grade_name) > GUILD_NAME_MAX_LEN) return; if (!*grade_name) return; char text[GUILD_NAME_MAX_LEN * 2 + 1]; DBManager::instance().EscapeString(text, sizeof(text), grade_name, strlen(grade_name)); DBManager::instance().FuncAfterQuery(FSendChangeGrade(GetID(), grade), "UPDATE guild_grade%s SET name = '%s' where guild_id = %u and grade = %d", get_table_postfix(), text, m_data.guild_id, grade); grade--; strlcpy(m_data.grade_array[grade].grade_name, grade_name, sizeof(m_data.grade_array[grade].grade_name)); TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; pack.size = sizeof(pack); pack.subheader = GUILD_SUBHEADER_GC_GRADE_NAME; TOneGradeNamePacket pack2; pack.size+=sizeof(pack2); pack2.grade = grade+1; strlcpy(pack2.grade_name,grade_name, sizeof(pack2.grade_name)); TEMP_BUFFER buf; buf.write(&pack,sizeof(pack)); buf.write(&pack2,sizeof(pack2)); for (TGuildMemberOnlineContainer::iterator it = m_memberOnline.begin(); it!=m_memberOnline.end(); ++it) { LPDESC d = (*it)->GetDesc(); if (d) d->Packet(buf.read_peek(), buf.size()); } } void CGuild::ChangeGradeAuth(BYTE grade, BYTE auth) { if (grade == 1) return; if (grade < 1 || grade > 15) { sys_err("Wrong guild grade value %d", grade); return; } DBManager::instance().FuncAfterQuery(FSendChangeGrade(GetID(),grade), "UPDATE guild_grade%s SET auth = %d where guild_id = %u and grade = %d", get_table_postfix(), auth, m_data.guild_id, grade); grade--; m_data.grade_array[grade].auth_flag=auth; TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; pack.size = sizeof(pack); pack.subheader = GUILD_SUBHEADER_GC_GRADE_AUTH; TOneGradeAuthPacket pack2; pack.size += sizeof(pack2); pack2.grade = grade + 1; pack2.auth = auth; TEMP_BUFFER buf; buf.write(&pack, sizeof(pack)); buf.write(&pack2, sizeof(pack2)); for (TGuildMemberOnlineContainer::iterator it = m_memberOnline.begin(); it != m_memberOnline.end(); ++it) { LPDESC d = (*it)->GetDesc(); if (d) d->Packet(buf.read_peek(), buf.size()); } } void CGuild::SendGuildInfoPacket(LPCHARACTER ch) { LPDESC d = ch->GetDesc(); if (!d) return; TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; pack.size = sizeof(TPacketGCGuild) + sizeof(TPacketGCGuildInfo); pack.subheader = GUILD_SUBHEADER_GC_INFO; TPacketGCGuildInfo pack_sub; memset(&pack_sub, 0, sizeof(TPacketGCGuildInfo)); pack_sub.member_count = GetMemberCount(); pack_sub.max_member_count = GetMaxMemberCount(); pack_sub.guild_id = m_data.guild_id; pack_sub.master_pid = m_data.master_pid; pack_sub.exp = m_data.exp; pack_sub.level = m_data.level; strlcpy(pack_sub.name, m_data.name, sizeof(pack_sub.name)); pack_sub.gold = m_data.gold; pack_sub.has_land = HasLand(); sys_log(0, "GMC guild_name %s", m_data.name); sys_log(0, "GMC master %d", m_data.master_pid); d->BufferedPacket(&pack, sizeof(TPacketGCGuild)); d->Packet(&pack_sub, sizeof(TPacketGCGuildInfo)); } bool CGuild::OfferExp(LPCHARACTER ch, int amount) { TGuildMemberContainer::iterator cit = m_member.find(ch->GetPlayerID()); if (cit == m_member.end()) return false; if (m_data.exp+amount < m_data.exp) return false; if (amount < 0) return false; if (ch->GetExp() < (DWORD) amount) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 제공하고자 하는 경험치가 남은 경험치보다 많습니다.")); return false; } if (ch->GetExp() - (DWORD) amount > ch->GetExp()) { sys_err("Wrong guild offer amount %d by %s[%u]", amount, ch->GetName(), ch->GetPlayerID()); return false; } ch->PointChange(POINT_EXP, -amount); TPacketGuildExpUpdate guild_exp; guild_exp.guild_id = GetID(); guild_exp.amount = amount / 100; db_clientdesc->DBPacket(HEADER_GD_GUILD_EXP_UPDATE, 0, &guild_exp, sizeof(guild_exp)); GuildPointChange(POINT_EXP, amount / 100, true); cit->second.offer_exp += amount / 100; cit->second._dummy = 0; TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; for (TGuildMemberOnlineContainer::iterator it = m_memberOnline.begin(); it != m_memberOnline.end(); ++it) { LPDESC d = (*it)->GetDesc(); if (d) { pack.subheader = GUILD_SUBHEADER_GC_LIST; pack.size = sizeof(pack) + 13; d->BufferedPacket(&pack, sizeof(pack)); d->Packet(&(cit->second), sizeof(DWORD) * 3 + 1); } } SaveMember(ch->GetPlayerID()); TPacketGuildChangeMemberData gd_guild; gd_guild.guild_id = GetID(); gd_guild.pid = ch->GetPlayerID(); gd_guild.offer = cit->second.offer_exp; gd_guild.level = ch->GetLevel(); gd_guild.grade = cit->second.grade; db_clientdesc->DBPacket(HEADER_GD_GUILD_CHANGE_MEMBER_DATA, 0, &gd_guild, sizeof(gd_guild)); return true; } void CGuild::Disband() { sys_log(0, "GUILD: Disband %s:%u", GetName(), GetID()); //building::CLand* pLand = building::CManager::instance().FindLandByGuild(GetID()); //if (pLand) //pLand->SetOwner(0); for (TGuildMemberOnlineContainer::iterator it = m_memberOnline.begin(); it != m_memberOnline.end(); ++it) { LPCHARACTER ch = *it; ch->SetGuild(NULL); SendOnlineRemoveOnePacket(ch->GetPlayerID()); ch->SetQuestFlag("guild_manage.new_withdraw_time", get_global_time()); } for (TGuildMemberContainer::iterator it = m_member.begin(); it != m_member.end(); ++it) { CGuildManager::instance().Unlink(it->first); } } void CGuild::RequestDisband(DWORD pid) { if (m_data.master_pid != pid) return; TPacketGuild gd_guild; gd_guild.dwGuild = GetID(); gd_guild.dwInfo = 0; db_clientdesc->DBPacket(HEADER_GD_GUILD_DISBAND, 0, &gd_guild, sizeof(TPacketGuild)); // LAND_CLEAR building::CManager::instance().ClearLandByGuildID(GetID()); // END_LAND_CLEAR } void CGuild::AddComment(LPCHARACTER ch, const std::string& str) { if (str.length() > GUILD_COMMENT_MAX_LEN) return; char text[GUILD_COMMENT_MAX_LEN * 2 + 1]; DBManager::instance().EscapeString(text, sizeof(text), str.c_str(), str.length()); DBManager::instance().FuncAfterQuery(void_bind(std::bind1st(std::mem_fun(&CGuild::RefreshCommentForce),this),ch->GetPlayerID()), "INSERT INTO guild_comment%s(guild_id, name, notice, content, time) VALUES(%u, '%s', %d, '%s', NOW())", get_table_postfix(), m_data.guild_id, ch->GetName(), (str[0] == '!') ? 1 : 0, text); } void CGuild::DeleteComment(LPCHARACTER ch, DWORD comment_id) { SQLMsg * pmsg; if (GetMember(ch->GetPlayerID())->grade == GUILD_LEADER_GRADE) pmsg = DBManager::instance().DirectQuery("DELETE FROM guild_comment%s WHERE id = %u AND guild_id = %u",get_table_postfix(), comment_id, m_data.guild_id); else pmsg = DBManager::instance().DirectQuery("DELETE FROM guild_comment%s WHERE id = %u AND guild_id = %u AND name = '%s'",get_table_postfix(), comment_id, m_data.guild_id, ch->GetName()); if (pmsg->Get()->uiAffectedRows == 0 || pmsg->Get()->uiAffectedRows == (uint32_t)-1) ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 삭제할 수 없는 글입니다.")); else RefreshCommentForce(ch->GetPlayerID()); M2_DELETE(pmsg); } void CGuild::RefreshComment(LPCHARACTER ch) { RefreshCommentForce(ch->GetPlayerID()); } void CGuild::RefreshCommentForce(DWORD player_id) { LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(player_id); if (ch == NULL) { return; } std::auto_ptr<SQLMsg> pmsg (DBManager::instance().DirectQuery("SELECT id, name, content FROM guild_comment%s WHERE guild_id = %u ORDER BY notice DESC, id DESC LIMIT %d", get_table_postfix(), m_data.guild_id, GUILD_COMMENT_MAX_COUNT)); TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; pack.size = sizeof(pack)+1; pack.subheader = GUILD_SUBHEADER_GC_COMMENTS; BYTE count = pmsg->Get()->uiNumRows; LPDESC d = ch->GetDesc(); if (!d) return; pack.size += (sizeof(DWORD)+CHARACTER_NAME_MAX_LEN+1+GUILD_COMMENT_MAX_LEN+1)*(WORD)count; d->BufferedPacket(&pack,sizeof(pack)); d->BufferedPacket(&count, 1); char szName[CHARACTER_NAME_MAX_LEN + 1]; char szContent[GUILD_COMMENT_MAX_LEN + 1]; memset(szName, 0, sizeof(szName)); memset(szContent, 0, sizeof(szContent)); for (uint i = 0; i < pmsg->Get()->uiNumRows; i++) { MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult); DWORD id = strtoul(row[0], NULL, 10); strlcpy(szName, row[1], sizeof(szName)); strlcpy(szContent, row[2], sizeof(szContent)); d->BufferedPacket(&id, sizeof(id)); d->BufferedPacket(szName, sizeof(szName)); if (i == pmsg->Get()->uiNumRows - 1) d->Packet(szContent, sizeof(szContent)); // 마지막 줄이면 보내기 else d->BufferedPacket(szContent, sizeof(szContent)); } } bool CGuild::ChangeMemberGeneral(DWORD pid, BYTE is_general) { if (is_general && GetGeneralCount() >= GetMaxGeneralCount()) return false; TGuildMemberContainer::iterator it = m_member.find(pid); if (it == m_member.end()) { return true; } is_general = is_general?1:0; if (it->second.is_general == is_general) return true; if (is_general) ++m_general_count; else --m_general_count; it->second.is_general = is_general; TGuildMemberOnlineContainer::iterator itOnline = m_memberOnline.begin(); TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; pack.size = sizeof(pack)+5; pack.subheader = GUILD_SUBHEADER_GC_CHANGE_MEMBER_GENERAL; while (itOnline != m_memberOnline.end()) { LPDESC d = (*(itOnline++))->GetDesc(); if (!d) continue; d->BufferedPacket(&pack, sizeof(pack)); d->BufferedPacket(&pid, sizeof(pid)); d->Packet(&is_general, sizeof(is_general)); } SaveMember(pid); return true; } void CGuild::ChangeMemberGrade(DWORD pid, BYTE grade) { if (grade == 1) return; TGuildMemberContainer::iterator it = m_member.find(pid); if (it == m_member.end()) return; it->second.grade = grade; TGuildMemberOnlineContainer::iterator itOnline = m_memberOnline.begin(); TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; pack.size = sizeof(pack)+5; pack.subheader = GUILD_SUBHEADER_GC_CHANGE_MEMBER_GRADE; while (itOnline != m_memberOnline.end()) { LPDESC d = (*(itOnline++))->GetDesc(); if (!d) continue; d->BufferedPacket(&pack, sizeof(pack)); d->BufferedPacket(&pid, sizeof(pid)); d->Packet(&grade, sizeof(grade)); } SaveMember(pid); TPacketGuildChangeMemberData gd_guild; gd_guild.guild_id = GetID(); gd_guild.pid = pid; gd_guild.offer = it->second.offer_exp; gd_guild.level = it->second.level; gd_guild.grade = grade; db_clientdesc->DBPacket(HEADER_GD_GUILD_CHANGE_MEMBER_DATA, 0, &gd_guild, sizeof(gd_guild)); } void CGuild::SkillLevelUp(DWORD dwVnum) { DWORD dwRealVnum = dwVnum - GUILD_SKILL_START; if (dwRealVnum >= GUILD_SKILL_COUNT) return; CSkillProto* pkSk = CSkillManager::instance().Get(dwVnum); if (!pkSk) { sys_err("There is no such guild skill by number %u", dwVnum); return; } if (m_data.abySkill[dwRealVnum] >= pkSk->bMaxLevel) return; if (m_data.skill_point <= 0) return; m_data.skill_point --; m_data.abySkill[dwRealVnum] ++; ComputeGuildPoints(); SaveSkill(); SendDBSkillUpdate(); /*switch (dwVnum) { case GUILD_SKILL_GAHO: { TGuildMemberOnlineContainer::iterator it; for (it = m_memberOnline.begin(); it != m_memberOnline.end(); ++it) (*it)->PointChange(POINT_DEF_GRADE, 1); } break; case GUILD_SKILL_HIM: { TGuildMemberOnlineContainer::iterator it; for (it = m_memberOnline.begin(); it != m_memberOnline.end(); ++it) (*it)->PointChange(POINT_ATT_GRADE, 1); } break; }*/ for_each(m_memberOnline.begin(), m_memberOnline.end(), std::bind1st(std::mem_fun_ref(&CGuild::SendSkillInfoPacket),*this)); sys_log(0, "Guild SkillUp: %s %d level %d type %u", GetName(), pkSk->dwVnum, m_data.abySkill[dwRealVnum], pkSk->dwType); } void CGuild::UseSkill(DWORD dwVnum, LPCHARACTER ch, DWORD pid) { LPCHARACTER victim = NULL; if (!GetMember(ch->GetPlayerID()) || !HasGradeAuth(GetMember(ch->GetPlayerID())->grade, GUILD_AUTH_USE_SKILL)) return; sys_log(0,"GUILD_USE_SKILL : cname(%s), skill(%d)", ch ? ch->GetName() : "", dwVnum); DWORD dwRealVnum = dwVnum - GUILD_SKILL_START; if (!ch->CanMove()) return; if (dwRealVnum >= GUILD_SKILL_COUNT) return; CSkillProto* pkSk = CSkillManager::instance().Get(dwVnum); if (!pkSk) { sys_err("There is no such guild skill by number %u", dwVnum); return; } if (m_data.abySkill[dwRealVnum] == 0) return; if ((pkSk->dwFlag & SKILL_FLAG_SELFONLY)) { // 이미 걸려 있으므로 사용하지 않음. if (ch->FindAffect(pkSk->dwVnum)) return; victim = ch; } if (ch->IsAffectFlag(AFF_REVIVE_INVISIBLE)) ch->RemoveAffect(AFFECT_REVIVE_INVISIBLE); if (ch->IsAffectFlag(AFF_EUNHYUNG)) ch->RemoveAffect(SKILL_EUNHYUNG); double k =1.0*m_data.abySkill[dwRealVnum]/pkSk->bMaxLevel; pkSk->kSPCostPoly.SetVar("k", k); int iNeededSP = (int) pkSk->kSPCostPoly.Eval(); if (GetSP() < iNeededSP) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 용신력이 부족합니다. (%d, %d)"), GetSP(), iNeededSP); return; } pkSk->kCooldownPoly.SetVar("k", k); int iCooltime = (int) pkSk->kCooldownPoly.Eval(); if (!abSkillUsable[dwRealVnum]) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 쿨타임이 끝나지 않아 길드 스킬을 사용할 수 없습니다.")); return; } { TPacketGuildUseSkill p; p.dwGuild = GetID(); p.dwSkillVnum = pkSk->dwVnum; p.dwCooltime = iCooltime; db_clientdesc->DBPacket(HEADER_GD_GUILD_USE_SKILL, 0, &p, sizeof(p)); } abSkillUsable[dwRealVnum] = false; //abSkillUsed[dwRealVnum] = true; //adwSkillNextUseTime[dwRealVnum] = get_dword_time() + iCooltime * 1000; //PointChange(POINT_SP, -iNeededSP); //GuildPointChange(POINT_SP, -iNeededSP); if (test_server) ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> %d 스킬을 사용함 (%d, %d) to %u"), dwVnum, GetSP(), iNeededSP, pid); switch (dwVnum) { case GUILD_SKILL_TELEPORT: // 현재 서버에 있는 사람을 먼저 시도. SendDBSkillUpdate(-iNeededSP); if ((victim = (CHARACTER_MANAGER::instance().FindByPID(pid)))) ch->WarpSet(victim->GetX(), victim->GetY()); else { if (m_memberP2POnline.find(pid) != m_memberP2POnline.end()) { // 다른 서버에 로그인된 사람이 있음 -> 메시지 보내 좌표를 받아오자 // 1. A.pid, B.pid 를 뿌림 // 2. B.pid를 가진 서버가 뿌린서버에게 A.pid, 좌표 를 보냄 // 3. 워프 CCI * pcci = P2P_MANAGER::instance().FindByPID(pid); if (pcci->bChannel != g_bChannel) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 상대가 %d 채널에 있습니다. (현재 채널 %d)"), pcci->bChannel, g_bChannel); } else { TPacketGGFindPosition p; p.header = HEADER_GG_FIND_POSITION; p.dwFromPID = ch->GetPlayerID(); p.dwTargetPID = pid; pcci->pkDesc->Packet(&p, sizeof(TPacketGGFindPosition)); if (test_server) ch->ChatPacket(CHAT_TYPE_PARTY, "sent find position packet for guild teleport"); } } else ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 상대가 온라인 상태가 아닙니다.")); } break; /*case GUILD_SKILL_ACCEL: ch->RemoveAffect(dwVnum); ch->AddAffect(dwVnum, POINT_MOV_SPEED, m_data.abySkill[dwRealVnum]*3, pkSk->dwAffectFlag, (int)pkSk->kDurationPoly.Eval(), 0, false); ch->AddAffect(dwVnum, POINT_ATT_SPEED, m_data.abySkill[dwRealVnum]*3, pkSk->dwAffectFlag, (int)pkSk->kDurationPoly.Eval(), 0, false); break;*/ default: { /*if (ch->GetPlayerID() != GetMasterPID()) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드장만 길드 스킬을 사용할 수 있습니다.")); return; }*/ if (!UnderAnyWar()) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드 스킬은 길드전 중에만 사용할 수 있습니다.")); return; } SendDBSkillUpdate(-iNeededSP); for (itertype(m_memberOnline) it = m_memberOnline.begin(); it != m_memberOnline.end(); ++it) { LPCHARACTER victim = *it; victim->RemoveAffect(dwVnum); ch->ComputeSkill(dwVnum, victim, m_data.abySkill[dwRealVnum]); } } break; /*if (!victim) return; ch->ComputeSkill(dwVnum, victim, m_data.abySkill[dwRealVnum]);*/ } } void CGuild::SendSkillInfoPacket(LPCHARACTER ch) const { LPDESC d = ch->GetDesc(); if (!d) return; TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; pack.size = sizeof(pack) + 6 + GUILD_SKILL_COUNT; pack.subheader = GUILD_SUBHEADER_GC_SKILL_INFO; d->BufferedPacket(&pack, sizeof(pack)); d->BufferedPacket(&m_data.skill_point, 1); d->BufferedPacket(&m_data.abySkill, GUILD_SKILL_COUNT); d->BufferedPacket(&m_data.power, 2); d->Packet(&m_data.max_power, 2); } void CGuild::ComputeGuildPoints() { m_data.max_power = GUILD_BASE_POWER + (m_data.level-1) * GUILD_POWER_PER_LEVEL; m_data.power = MINMAX(0, m_data.power, m_data.max_power); } int CGuild::GetSkillLevel(DWORD vnum) { DWORD dwRealVnum = vnum - GUILD_SKILL_START; if (dwRealVnum >= GUILD_SKILL_COUNT) return 0; return m_data.abySkill[dwRealVnum]; } /*void CGuild::GuildUpdateAffect(LPCHARACTER ch) { if (GetSkillLevel(GUILD_SKILL_GAHO)) ch->PointChange(POINT_DEF_GRADE, GetSkillLevel(GUILD_SKILL_GAHO)); if (GetSkillLevel(GUILD_SKILL_HIM)) ch->PointChange(POINT_ATT_GRADE, GetSkillLevel(GUILD_SKILL_HIM)); }*/ /*void CGuild::GuildRemoveAffect(LPCHARACTER ch) { if (GetSkillLevel(GUILD_SKILL_GAHO)) ch->PointChange(POINT_DEF_GRADE, -(int) GetSkillLevel(GUILD_SKILL_GAHO)); if (GetSkillLevel(GUILD_SKILL_HIM)) ch->PointChange(POINT_ATT_GRADE, -(int) GetSkillLevel(GUILD_SKILL_HIM)); }*/ void CGuild::UpdateSkill(BYTE skill_point, BYTE* skill_levels) { //int iDefMoreBonus = 0; //int iAttMoreBonus = 0; m_data.skill_point = skill_point; /*if (skill_levels[GUILD_SKILL_GAHO - GUILD_SKILL_START]!=GetSkillLevel(GUILD_SKILL_GAHO)) { iDefMoreBonus = skill_levels[GUILD_SKILL_GAHO - GUILD_SKILL_START]-GetSkillLevel(GUILD_SKILL_GAHO); } if (skill_levels[GUILD_SKILL_HIM - GUILD_SKILL_START]!=GetSkillLevel(GUILD_SKILL_HIM)) { iAttMoreBonus = skill_levels[GUILD_SKILL_HIM - GUILD_SKILL_START]-GetSkillLevel(GUILD_SKILL_HIM); } if (iDefMoreBonus || iAttMoreBonus) { for (TGuildMemberOnlineContainer::iterator it = m_memberOnline.begin(); it != m_memberOnline.end(); ++it) { (*it)->PointChange(POINT_ATT_GRADE, iAttMoreBonus); (*it)->PointChange(POINT_DEF_GRADE, iDefMoreBonus); } }*/ thecore_memcpy(m_data.abySkill, skill_levels, sizeof(BYTE) * GUILD_SKILL_COUNT); ComputeGuildPoints(); } static DWORD __guild_levelup_exp(int level) { if (LC_IsYMIR()) { return guild_exp_table[level]; } else { return guild_exp_table2[level]; } } void CGuild::GuildPointChange(BYTE type, int amount, bool save) { switch (type) { case POINT_SP: m_data.power += amount; m_data.power = MINMAX(0, m_data.power, m_data.max_power); if (save) { SaveSkill(); } for_each(m_memberOnline.begin(), m_memberOnline.end(), std::bind1st(std::mem_fun_ref(&CGuild::SendSkillInfoPacket),*this)); break; case POINT_EXP: if (amount < 0 && m_data.exp < (DWORD) - amount) { m_data.exp = 19000; } else { m_data.exp += amount; while (m_data.exp >= __guild_levelup_exp(m_data.level)) { if (m_data.level < GUILD_MAX_LEVEL) { m_data.exp -= __guild_levelup_exp(m_data.level); ++m_data.level; ++m_data.skill_point; if (m_data.level > GUILD_MAX_LEVEL) m_data.level = GUILD_MAX_LEVEL; ComputeGuildPoints(); GuildPointChange(POINT_SP, m_data.max_power-m_data.power); if (save) ChangeLadderPoint(GUILD_LADDER_POINT_PER_LEVEL); // NOTIFY_GUILD_EXP_CHANGE for_each(m_memberOnline.begin(), m_memberOnline.end(), std::bind1st(std::mem_fun(&CGuild::SendGuildInfoPacket), this)); // END_OF_NOTIFY_GUILD_EXP_CHANGE } if (m_data.level == GUILD_MAX_LEVEL) { m_data.exp = 19000; } } } TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; pack.size = sizeof(pack)+5; pack.subheader = GUILD_SUBHEADER_GC_CHANGE_EXP; TEMP_BUFFER buf; buf.write(&pack,sizeof(pack)); buf.write(&m_data.level,1); buf.write(&m_data.exp,4); for (TGuildMemberOnlineContainer::iterator it = m_memberOnline.begin(); it != m_memberOnline.end(); ++it) { LPDESC d = (*it)->GetDesc(); if (d) d->Packet(buf.read_peek(), buf.size()); } if (save) SaveLevel(); break; } } void CGuild::SkillRecharge() { //GuildPointChange(POINT_SP, m_data.max_power / 2); //GuildPointChange(POINT_SP, 10); } void CGuild::SaveMember(DWORD pid) { TGuildMemberContainer::iterator it = m_member.find(pid); if (it == m_member.end()) return; DBManager::instance().Query( "UPDATE guild_member%s SET grade = %d, offer = %u, is_general = %d WHERE pid = %u and guild_id = %u", get_table_postfix(), it->second.grade, it->second.offer_exp, it->second.is_general, pid, m_data.guild_id); } void CGuild::LevelChange(DWORD pid, BYTE level) { TGuildMemberContainer::iterator cit = m_member.find(pid); if (cit == m_member.end()) return; cit->second.level = level; TPacketGuildChangeMemberData gd_guild; gd_guild.guild_id = GetID(); gd_guild.pid = pid; gd_guild.offer = cit->second.offer_exp; gd_guild.grade = cit->second.grade; gd_guild.level = level; db_clientdesc->DBPacket(HEADER_GD_GUILD_CHANGE_MEMBER_DATA, 0, &gd_guild, sizeof(gd_guild)); TPacketGCGuild pack; pack.header = HEADER_GC_GUILD; cit->second._dummy = 0; for (TGuildMemberOnlineContainer::iterator it = m_memberOnline.begin(); it != m_memberOnline.end(); ++it) { LPDESC d = (*it)->GetDesc(); if (d) { pack.subheader = GUILD_SUBHEADER_GC_LIST; pack.size = sizeof(pack) + 13; d->BufferedPacket(&pack, sizeof(pack)); d->Packet(&(cit->second), sizeof(DWORD) * 3 + 1); } } } void CGuild::ChangeMemberData(DWORD pid, DWORD offer, BYTE level, BYTE grade) { TGuildMemberContainer::iterator cit = m_member.find(pid); if (cit == m_member.end()) return; cit->second.offer_exp = offer; cit->second.level = level; cit->second.grade = grade; cit->second._dummy = 0; TPacketGCGuild pack; memset(&pack, 0, sizeof(pack)); pack.header = HEADER_GC_GUILD; for (TGuildMemberOnlineContainer::iterator it = m_memberOnline.begin(); it != m_memberOnline.end(); ++it) { LPDESC d = (*it)->GetDesc(); if (d) { pack.subheader = GUILD_SUBHEADER_GC_LIST; pack.size = sizeof(pack) + 13; d->BufferedPacket(&pack, sizeof(pack)); d->Packet(&(cit->second), sizeof(DWORD) * 3 + 1); } } } namespace { struct FGuildChat { const char* c_pszText; FGuildChat(const char* c_pszText) : c_pszText(c_pszText) {} void operator()(LPCHARACTER ch) { ch->ChatPacket(CHAT_TYPE_GUILD, "%s", c_pszText); } }; } void CGuild::P2PChat(const char* c_pszText) { std::for_each(m_memberOnline.begin(), m_memberOnline.end(), FGuildChat(c_pszText)); } void CGuild::Chat(const char* c_pszText) { std::for_each(m_memberOnline.begin(), m_memberOnline.end(), FGuildChat(c_pszText)); TPacketGGGuild p1; TPacketGGGuildChat p2; p1.bHeader = HEADER_GG_GUILD; p1.bSubHeader = GUILD_SUBHEADER_GG_CHAT; p1.dwGuild = GetID(); strlcpy(p2.szText, c_pszText, sizeof(p2.szText)); P2P_MANAGER::instance().Send(&p1, sizeof(TPacketGGGuild)); P2P_MANAGER::instance().Send(&p2, sizeof(TPacketGGGuildChat)); } LPCHARACTER CGuild::GetMasterCharacter() { return CHARACTER_MANAGER::instance().FindByPID(GetMasterPID()); } void CGuild::Packet(const void* buf, int size) { for (itertype(m_memberOnline) it = m_memberOnline.begin(); it!=m_memberOnline.end();++it) { LPDESC d = (*it)->GetDesc(); if (d) d->Packet(buf, size); } } int CGuild::GetTotalLevel() const { int total = 0; for (itertype(m_member) it = m_member.begin(); it != m_member.end(); ++it) { total += it->second.level; } return total; } bool CGuild::ChargeSP(LPCHARACTER ch, int iSP) { int gold = iSP * 100; if (gold < iSP || ch->GetGold() < gold) return false; int iRemainSP = m_data.max_power - m_data.power; if (iSP > iRemainSP) { iSP = iRemainSP; gold = iSP * 100; } ch->PointChange(POINT_GOLD, -gold); DBManager::instance().SendMoneyLog(MONEY_LOG_GUILD, 1, -gold); SendDBSkillUpdate(iSP); { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> %u의 용신력을 회복하였습니다."), iSP); } return true; } void CGuild::SkillUsableChange(DWORD dwSkillVnum, bool bUsable) { DWORD dwRealVnum = dwSkillVnum - GUILD_SKILL_START; if (dwRealVnum >= GUILD_SKILL_COUNT) return; abSkillUsable[dwRealVnum] = bUsable; // GUILD_SKILL_COOLTIME_BUG_FIX sys_log(0, "CGuild::SkillUsableChange(guild=%s, skill=%d, usable=%d)", GetName(), dwSkillVnum, bUsable); // END_OF_GUILD_SKILL_COOLTIME_BUG_FIX } // GUILD_MEMBER_COUNT_BONUS void CGuild::SetMemberCountBonus(int iBonus) { m_iMemberCountBonus = iBonus; sys_log(0, "GUILD_IS_FULL_BUG : Bonus set to %d(val:%d)", iBonus, m_iMemberCountBonus); } void CGuild::BroadcastMemberCountBonus() { TPacketGGGuild p1; p1.bHeader = HEADER_GG_GUILD; p1.bSubHeader = GUILD_SUBHEADER_GG_SET_MEMBER_COUNT_BONUS; p1.dwGuild = GetID(); P2P_MANAGER::instance().Send(&p1, sizeof(TPacketGGGuild)); P2P_MANAGER::instance().Send(&m_iMemberCountBonus, sizeof(int)); } int CGuild::GetMaxMemberCount() { // GUILD_IS_FULL_BUG_FIX if ( m_iMemberCountBonus < 0 || m_iMemberCountBonus > 18 ) m_iMemberCountBonus = 0; // END_GUILD_IS_FULL_BUG_FIX if ( LC_IsHongKong() == true ) { quest::PC* pPC = quest::CQuestManager::instance().GetPC(GetMasterPID()); if ( pPC != NULL ) { if ( pPC->GetFlag("guild.is_unlimit_member") == 1 ) { return INT_MAX; } } } return 32 + 2 * (m_data.level-1) + m_iMemberCountBonus; } // END_OF_GUILD_MEMBER_COUNT_BONUS void CGuild::AdvanceLevel(int iLevel) { if (m_data.level == iLevel) return; m_data.level = MIN(GUILD_MAX_LEVEL, iLevel); } void CGuild::RequestDepositMoney(LPCHARACTER ch, int iGold) { if (false==ch->CanDeposit()) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 잠시후에 이용해주십시오")); return; } if (ch->GetGold() < iGold) return; ch->PointChange(POINT_GOLD, -iGold); TPacketGDGuildMoney p; p.dwGuild = GetID(); p.iGold = iGold; db_clientdesc->DBPacket(HEADER_GD_GUILD_DEPOSIT_MONEY, 0, &p, sizeof(p)); char buf[64+1]; snprintf(buf, sizeof(buf), "%u %s", GetID(), GetName()); LogManager::instance().CharLog(ch, iGold, "GUILD_DEPOSIT", buf); ch->UpdateDepositPulse(); sys_log(0, "GUILD: DEPOSIT %s:%u player %s[%u] gold %d", GetName(), GetID(), ch->GetName(), ch->GetPlayerID(), iGold); } void CGuild::RequestWithdrawMoney(LPCHARACTER ch, int iGold) { if (false==ch->CanDeposit()) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 잠시후에 이용해주십시오")); return; } if (ch->GetPlayerID() != GetMasterPID()) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드 금고에선 길드장만 출금할 수 있습니다.")); return; } if (m_data.gold < iGold) { ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 가지고 있는 돈이 부족합니다.")); return; } TPacketGDGuildMoney p; p.dwGuild = GetID(); p.iGold = iGold; db_clientdesc->DBPacket(HEADER_GD_GUILD_WITHDRAW_MONEY, 0, &p, sizeof(p)); ch->UpdateDepositPulse(); } void CGuild::RecvMoneyChange(int iGold) { m_data.gold = iGold; TPacketGCGuild p; p.header = HEADER_GC_GUILD; p.size = sizeof(p) + sizeof(int); p.subheader = GUILD_SUBHEADER_GC_MONEY_CHANGE; for (itertype(m_memberOnline) it = m_memberOnline.begin(); it != m_memberOnline.end(); ++it) { LPCHARACTER ch = *it; LPDESC d = ch->GetDesc(); d->BufferedPacket(&p, sizeof(p)); d->Packet(&iGold, sizeof(int)); } } void CGuild::RecvWithdrawMoneyGive(int iChangeGold) { LPCHARACTER ch = GetMasterCharacter(); if (ch) { ch->PointChange(POINT_GOLD, iChangeGold); sys_log(0, "GUILD: WITHDRAW %s:%u player %s[%u] gold %d", GetName(), GetID(), ch->GetName(), ch->GetPlayerID(), iChangeGold); } TPacketGDGuildMoneyWithdrawGiveReply p; p.dwGuild = GetID(); p.iChangeGold = iChangeGold; p.bGiveSuccess = ch ? 1 : 0; db_clientdesc->DBPacket(HEADER_GD_GUILD_WITHDRAW_MONEY_GIVE_REPLY, 0, &p, sizeof(p)); } bool CGuild::HasLand() { return building::CManager::instance().FindLandByGuild(GetID()) != NULL; } // GUILD_JOIN_BUG_FIX /// 길드 초대 event 정보 EVENTINFO(TInviteGuildEventInfo) { DWORD dwInviteePID; ///< 초대받은 character 의 PID DWORD dwGuildID; ///< 초대한 Guild 의 ID TInviteGuildEventInfo() : dwInviteePID( 0 ) , dwGuildID( 0 ) { } }; /** * 길드 초대 event callback 함수. * event 가 발동하면 초대 거절로 처리한다. */ EVENTFUNC( GuildInviteEvent ) { TInviteGuildEventInfo *pInfo = dynamic_cast<TInviteGuildEventInfo*>( event->info ); if ( pInfo == NULL ) { sys_err( "GuildInviteEvent> <Factor> Null pointer" ); return 0; } CGuild* pGuild = CGuildManager::instance().FindGuild( pInfo->dwGuildID ); if ( pGuild ) { sys_log( 0, "GuildInviteEvent %s", pGuild->GetName() ); pGuild->InviteDeny( pInfo->dwInviteePID ); } return 0; } void CGuild::Invite( LPCHARACTER pchInviter, LPCHARACTER pchInvitee ) { if (quest::CQuestManager::instance().GetPCForce(pchInviter->GetPlayerID())->IsRunning() == true) { pchInviter->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 상대방이 초대 신청을 받을 수 없는 상태입니다.")); return; } if (quest::CQuestManager::instance().GetPCForce(pchInvitee->GetPlayerID())->IsRunning() == true) return; if ( pchInvitee->IsBlockMode( BLOCK_GUILD_INVITE ) ) { pchInviter->ChatPacket( CHAT_TYPE_INFO, LC_TEXT("<길드> 상대방이 길드 초대 거부 상태입니다.") ); return; } else if ( !HasGradeAuth( GetMember( pchInviter->GetPlayerID() )->grade, GUILD_AUTH_ADD_MEMBER ) ) { pchInviter->ChatPacket( CHAT_TYPE_INFO, LC_TEXT("<길드> 길드원을 초대할 권한이 없습니다.") ); return; } else if ( pchInvitee->GetEmpire() != pchInviter->GetEmpire() ) { pchInviter->ChatPacket( CHAT_TYPE_INFO, LC_TEXT("<길드> 다른 제국 사람을 길드에 초대할 수 없습니다.") ); return; } GuildJoinErrCode errcode = VerifyGuildJoinableCondition( pchInvitee ); switch ( errcode ) { case GERR_NONE: break; case GERR_WITHDRAWPENALTY: pchInviter->ChatPacket( CHAT_TYPE_INFO, LC_TEXT("<길드> 탈퇴한 후 %d일이 지나지 않은 사람은 길드에 초대할 수 없습니다."), quest::CQuestManager::instance().GetEventFlag( "guild_withdraw_delay" ) ); return; case GERR_COMMISSIONPENALTY: pchInviter->ChatPacket( CHAT_TYPE_INFO, LC_TEXT("<길드> 길드를 해산한 지 %d일이 지나지 않은 사람은 길드에 초대할 수 없습니다."), quest::CQuestManager::instance().GetEventFlag( "guild_disband_delay") ); return; case GERR_ALREADYJOIN: pchInviter->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 상대방이 이미 다른 길드에 속해있습니다.")); return; case GERR_GUILDISFULL: pchInviter->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 최대 길드원 수를 초과했습니다.")); return; case GERR_GUILD_IS_IN_WAR : pchInviter->ChatPacket( CHAT_TYPE_INFO, LC_TEXT("<길드> 현재 길드가 전쟁 중 입니다.") ); return; case GERR_INVITE_LIMIT : pchInviter->ChatPacket( CHAT_TYPE_INFO, LC_TEXT("<길드> 현재 신규 가입 제한 상태 입니다.") ); return; default: sys_err( "ignore guild join error(%d)", errcode ); return; } if ( m_GuildInviteEventMap.end() != m_GuildInviteEventMap.find( pchInvitee->GetPlayerID() ) ) return; // // 이벤트 생성 // TInviteGuildEventInfo* pInfo = AllocEventInfo<TInviteGuildEventInfo>(); pInfo->dwInviteePID = pchInvitee->GetPlayerID(); pInfo->dwGuildID = GetID(); m_GuildInviteEventMap.insert(EventMap::value_type(pchInvitee->GetPlayerID(), event_create(GuildInviteEvent, pInfo, PASSES_PER_SEC(10)))); // // 초대 받는 character 에게 초대 패킷 전송 // DWORD gid = GetID(); TPacketGCGuild p; p.header = HEADER_GC_GUILD; p.size = sizeof(p) + sizeof(DWORD) + GUILD_NAME_MAX_LEN + 1; p.subheader = GUILD_SUBHEADER_GC_GUILD_INVITE; TEMP_BUFFER buf; buf.write( &p, sizeof(p) ); buf.write( &gid, sizeof(DWORD) ); buf.write( GetName(), GUILD_NAME_MAX_LEN + 1 ); pchInvitee->GetDesc()->Packet( buf.read_peek(), buf.size() ); } void CGuild::InviteAccept( LPCHARACTER pchInvitee ) { EventMap::iterator itFind = m_GuildInviteEventMap.find( pchInvitee->GetPlayerID() ); if ( itFind == m_GuildInviteEventMap.end() ) { sys_log( 0, "GuildInviteAccept from not invited character(invite guild: %s, invitee: %s)", GetName(), pchInvitee->GetName() ); return; } event_cancel( &itFind->second ); m_GuildInviteEventMap.erase( itFind ); GuildJoinErrCode errcode = VerifyGuildJoinableCondition( pchInvitee ); switch ( errcode ) { case GERR_NONE: break; case GERR_WITHDRAWPENALTY: pchInvitee->ChatPacket( CHAT_TYPE_INFO, LC_TEXT("<길드> 탈퇴한 후 %d일이 지나지 않은 사람은 길드에 초대할 수 없습니다."), quest::CQuestManager::instance().GetEventFlag( "guild_withdraw_delay" ) ); return; case GERR_COMMISSIONPENALTY: pchInvitee->ChatPacket( CHAT_TYPE_INFO, LC_TEXT("<길드> 길드를 해산한 지 %d일이 지나지 않은 사람은 길드에 초대할 수 없습니다."), quest::CQuestManager::instance().GetEventFlag( "guild_disband_delay") ); return; case GERR_ALREADYJOIN: pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 상대방이 이미 다른 길드에 속해있습니다.")); return; case GERR_GUILDISFULL: pchInvitee->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 최대 길드원 수를 초과했습니다.")); return; case GERR_GUILD_IS_IN_WAR : pchInvitee->ChatPacket( CHAT_TYPE_INFO, LC_TEXT("<길드> 현재 길드가 전쟁 중 입니다.") ); return; case GERR_INVITE_LIMIT : pchInvitee->ChatPacket( CHAT_TYPE_INFO, LC_TEXT("<길드> 현재 신규 가입 제한 상태 입니다.") ); return; default: sys_err( "ignore guild join error(%d)", errcode ); return; } RequestAddMember( pchInvitee, 15 ); } void CGuild::InviteDeny( DWORD dwPID ) { EventMap::iterator itFind = m_GuildInviteEventMap.find( dwPID ); if ( itFind == m_GuildInviteEventMap.end() ) { sys_log( 0, "GuildInviteDeny from not invited character(invite guild: %s, invitee PID: %d)", GetName(), dwPID ); return; } event_cancel( &itFind->second ); m_GuildInviteEventMap.erase( itFind ); } CGuild::GuildJoinErrCode CGuild::VerifyGuildJoinableCondition( const LPCHARACTER pchInvitee ) { if ( get_global_time() - pchInvitee->GetQuestFlag( "guild_manage.new_withdraw_time" ) < CGuildManager::instance().GetWithdrawDelay() ) return GERR_WITHDRAWPENALTY; else if ( get_global_time() - pchInvitee->GetQuestFlag( "guild_manage.new_disband_time" ) < CGuildManager::instance().GetDisbandDelay() ) return GERR_COMMISSIONPENALTY; else if ( pchInvitee->GetGuild() ) return GERR_ALREADYJOIN; else if ( GetMemberCount() >= GetMaxMemberCount() ) { sys_log(1, "GuildName = %s, GetMemberCount() = %d, GetMaxMemberCount() = %d (32 + MAX(level(%d)-10, 0) * 2 + bonus(%d)", GetName(), GetMemberCount(), GetMaxMemberCount(), m_data.level, m_iMemberCountBonus); return GERR_GUILDISFULL; } else if ( UnderAnyWar() != 0 ) { return GERR_GUILD_IS_IN_WAR; } else if ( LC_IsBrazil() == true ) { std::auto_ptr<SQLMsg> pMsg( DBManager::instance().DirectQuery("SELECT value FROM guild_invite_limit WHERE id=%d", GetID()) ); if ( pMsg->Get()->uiNumRows > 0 ) { MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult); time_t limit_time=0; str_to_number( limit_time, row[0] ); if ( test_server == true ) { limit_time += quest::CQuestManager::instance().GetEventFlag("guild_invite_limit") * 60; } else { limit_time += quest::CQuestManager::instance().GetEventFlag("guild_invite_limit") * 24 * 60 * 60; } if ( get_global_time() < limit_time ) return GERR_INVITE_LIMIT; } } return GERR_NONE; } // END_OF_GUILD_JOIN_BUG_FIX bool CGuild::ChangeMasterTo(DWORD dwPID) { if ( GetMember(dwPID) == NULL ) return false; TPacketChangeGuildMaster p; p.dwGuildID = GetID(); p.idFrom = GetMasterPID(); p.idTo = dwPID; db_clientdesc->DBPacket(HEADER_GD_REQ_CHANGE_GUILD_MASTER, 0, &p, sizeof(p)); return true; } void CGuild::SendGuildDataUpdateToAllMember(SQLMsg* pmsg) { TGuildMemberOnlineContainer::iterator iter = m_memberOnline.begin(); for (; iter != m_memberOnline.end(); iter++ ) { SendGuildInfoPacket(*iter); SendAllGradePacket(*iter); } }
    1 point
  11. You want to get level 20 when create guild right?
    1 point
  12. Post from your game source guild.cpp
    1 point
  13. It depends on the server, obviously if your server doesn't have the 6th and 7th bonuses available, then you need to edit the code.
    1 point
  14. if u bought from zenu then ask him
    1 point
  15. You need someone help in your PC. I can't help i'm sorry.
    1 point
  16. M2 Download Center Download Here ( Internal ) [Hidden Content] [Hidden Content] VT: [Hidden Content]
    1 point
  17. M2 Download Center Download Here ( Internal ) Hello everyone. With this tool you will be able to convert XML proto files into self-defined structure of SQL or TXT files. Files contains definitions for default metin2 structure of mob and item protos (includes txt version of mob proto). Now few words about program structure. There are files named like '*ProtoItem' - this is definition of input and result structure. You can define own names for properties in XML files, result SQL columns and indexes for positions in TXT version of protos. XmlAttribute is used to define input data type and name of property in XML file. SqlMapAttribute is used to define database column name or position in TXT file(by ClientProtoIdx property). MobProto & ItemProto classes inherits from abstract classes for specified destiny(client or database). I implemented basic functionallity to convert XML files into SQL, but you can easly implement your own version - it's really flexible. [Hidden Content]
    1 point
  18. For libs: [Hidden Content]
    1 point
  19. M2 Download Center Download Here ( Internal ) So after 5 years I finally decided to make a map from scratch, which became the new map 1 for romanian players in WoM2: the Deunsang Citadel. I am also releasing most of the trees and shrubs I used, these are from Oblivion and I did already release them before (and many people used them in their maps) but I reworked both the lighting and the textures, especially of the trees, and fixed a couple that were not working before. [Hidden Content]
    1 point
  20. i was add Metin2-Official-Inventory-Expansion [Hidden Content] I encountered problems that I do not know to solve in syserr 0614 18:14:11113 :: CMapOutdoor::Load - LoadMonsterAreaInfo ERROR 0614 18:14:16184 :: Traceback (most recent call last): 0614 18:14:16184 :: File "networkModule.py", line 247, in SetGamePhase 0614 18:14:16184 :: File "game.py", line 97, in __init__ 0614 18:14:16184 :: File "interfaceModule.py", line 281, in MakeInterface 0614 18:14:16184 :: File "interfaceModule.py", line 103, in __MakeMessengerWindow 0614 18:14:16185 :: AttributeError 0614 18:14:16185 :: : 0614 18:14:16185 :: 'Interface' object has no attribute 'ToggleGuildWindow' 0614 18:14:16185 :: how i can solve it !! How do I add file? Because I am the first time dealing with sql
    0 points
  21. -1 points
×
×
  • 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.