Leaderboard
Popular Content
Showing content with the highest reputation on 03/24/25 in all areas
-
This was resolved through PMs a while back; here was the final script for anyone interested: // Act Editor Script - v1.0.5.1 // By Tokeiburu GrfColor textForeground = "0xffffff"; var fontType = "Minecraftia"; var fontSize = 14; var fontBold = true; var dataGrfName = @"C:\mobWithNames\data.grf"; var newGrfName = @"C:\mobWithNames\mobsWithName.grf"; var mobDbPath = @"C:\mobWithNames\mob_db.yml"; var attrFixDbPath = @"C:\mobWithNames\attr_fix.yml"; var jobnamePath = @"C:\mobWithNames\jobname.lub"; var npcIdentityPath = @"C:\mobWithNames\npcidentity.lub"; var oldEncoding = EncodingService.DisplayEncoding; var displayMobNames = false; var maxElementShown = 3; var showFirstElementDamage = true; //var elementTextColors = false; // Fixed settings var mobFolder = @"data\sprite\¸ó½ºÅÍ\"; //jobnamePath = @"C:\Gravity Ragnarok - Copy\data\luafiles514\lua files\datainfo\jobname.lub"; //npcIdentityPath = @"C:\Gravity Ragnarok - Copy\data\luafiles514\lua files\datainfo\npcidentity.lub"; //dataGrfName = @"C:\Gravity Ragnarok - Copy\data.grf"; //newGrfName = @"C:\test\mobsWithName.grf"; //mobDbPath = @"C:\Users\Tokei\Desktop\SVN\rathena4\db\re\mob_db.yml"; //attrFixDbPath = @"C:\Users\Tokei\Desktop\SVN\rathena4\db\re\attr_fix.yml"; var jobname = new JobnameLubData(jobnamePath, npcIdentityPath); int[,,] attr_fix = new int[4, 10, 10]; StringBuilder b = new StringBuilder(); var ele2id = new Dictionary<string, int>(); ele2id["Neutral"] = 0; ele2id["Water"] = 1; ele2id["Earth"] = 2; ele2id["Fire"] = 3; ele2id["Wind"] = 4; ele2id["Poison"] = 5; ele2id["Holy"] = 6; ele2id["Dark"] = 7; ele2id["Ghost"] = 8; ele2id["Undead"] = 9; var id2ele = new Dictionary<int, string>(); foreach (var entry in ele2id) id2ele[entry.Value] = entry.Key; try { EncodingService.DisplayEncoding = EncodingService.ANSI; int ival; string[] values; var mobId2ServerName = new Dictionary<int, string>(); var mobId2ServerElement = new Dictionary<int, int>(); var mobId2ServerElementLevel = new Dictionary<int, int>(); { // Load the attr_fix file var parser = new YamlParser(attrFixDbPath); var body = parser.Output["Body"]; foreach (var entry in body) { int level = Int32.Parse(entry["Level"]) - 1; foreach (var ele2id_entry in ele2id) { var element_name = ele2id_entry.Key; var element_id = ele2id_entry.Value; var def_elements = entry[element_name]; foreach (ParserKeyValue def_ele in def_elements) { attr_fix[level, element_id, ele2id[def_ele.Key]] = Int32.Parse(def_ele.Value); } } } } { // Load the mob_db file var parser = new YamlParser(mobDbPath); var body = parser.Output["Body"]; foreach (var entry in body) { int mobId = Int32.Parse(entry["Id"]); mobId2ServerName[mobId] = entry["Name"]; int eleId = 0; int eleLevel = 0; if (entry["Element"] != null) { eleId = ele2id[entry["Element"]]; } if (entry["ElementLevel"] != null) { eleLevel = Int32.Parse(entry["ElementLevel"]) - 1; } mobId2ServerElement[mobId] = eleId; mobId2ServerElementLevel[mobId] = eleLevel; } } Func<Act, string, int, Act> addNameAbove = (actI, display, offset) => { var pixels = new byte[256 * 20 * 4]; BitmapSource bitmapSource = BitmapSource.Create(256, 20, 96, 96, PixelFormats.Bgra32, null, pixels, 256 * 4); var visual = new DrawingVisual(); using (DrawingContext drawingContext = visual.RenderOpen()) { var ft = new FormattedText(display, CultureInfo.InvariantCulture, FlowDirection.LeftToRight, new Typeface(fontType), fontSize, new SolidColorBrush(textForeground.ToColor())); if (fontBold) ft.SetFontWeight(FontWeights.Bold); drawingContext.DrawImage(bitmapSource, new Rect(0, 0, 256, 20)); drawingContext.DrawText(ft, new Point(2, 2)); } var image = new DrawingImage(visual.Drawing); var im = new Image { Source = image }; im.Measure(new Size(image.Width, image.Height)); im.Arrange(new Rect(0.0, 0.0, image.Width, image.Height)); im.UpdateLayout(); var grfImage = GrfToWpfBridge.Imaging.ConvertToBitmapSource(im); var transparent = new GrfColor(0, 0, 0, 0); for (int i = 0; i < grfImage.NumberOfPixels; i++) { Buffer.BlockCopy((grfImage.Pixels[4 * i + 3] > 0x80 ? textForeground : transparent).ToBgraBytes(), 0, grfImage.Pixels, 4 * i, 4); } grfImage.Convert(GrfImageType.Indexed8); grfImage.Trim(); grfImage.Margin(2); Buffer.BlockCopy(GrfColor.Black.ToBgraBytes(), 0, grfImage.Palette, 2 * 4, 4); for (int y = 1; y < grfImage.Height - 1; y++) { for (int x = 1; x < grfImage.Width - 1; x++) { if (grfImage.Pixels[y * grfImage.Width + x] == 1) continue; if (grfImage.Pixels[(y - 1) * grfImage.Width + (x)] == 1 || grfImage.Pixels[(y + 1) * grfImage.Width + (x)] == 1 || grfImage.Pixels[(y) * grfImage.Width + (x - 1)] == 1 || grfImage.Pixels[(y) * grfImage.Width + (x + 1)] == 1) grfImage.Pixels[y * grfImage.Width + x] = 2; } } act = actI; // Doesn't support Bgra32 only sprites if (act.Sprite.Palette == null) return act; var unused = act.Sprite.GetUnusedPaletteIndexes().ToList(); if (unused.Count <= 1) return act; if (!act.Sprite.Palette.Contains(textForeground)) Buffer.BlockCopy(textForeground.ToBgraBytes(), 0, act.Sprite.Palette.BytePalette, 4 * unused[0], 4); if (!act.Sprite.Palette.Contains(GrfColor.Black)) Buffer.BlockCopy(GrfColor.Black.ToBgraBytes(), 0, act.Sprite.Palette.BytePalette, 4 * unused[1], 4); var colors = act.Sprite.Palette.Colors.ToList(); var whiteIndex = (byte)colors.IndexOf(textForeground); var blackIndex = (byte)colors.IndexOf(GrfColor.Black); for (int i = 0; i < grfImage.NumberOfPixels; i++) { if (grfImage.Pixels[i] == 1) grfImage.Pixels[i] = whiteIndex; else if (grfImage.Pixels[i] == 2) grfImage.Pixels[i] = blackIndex; } var index = act.Sprite.InsertAny(grfImage); var max = 0; act.AnimationExecute(0, a => a.AllLayers(p => { if ((ival = ((int)(p.ScaleY * p.Height) / 2 - p.OffsetY)) > max) max = ival; })); max += 15; var layer = new Layer(index, grfImage) { OffsetY = -(max + fontSize / 2) + offset }; ival = 0; foreach (var action in act) { // Don't show for the death animation if (ival >= 32 && ival < 40) continue; foreach (var frame in action) { frame.Layers.Add(layer); } ival++; } return act; }; string displayName; int from = -1; var processed = new HashSet<string>(StringComparer.OrdinalIgnoreCase); using (var dataGrf = new GrfHolder(dataGrfName)) using (var outputGrf = new GrfHolder(newGrfName, GrfLoadOptions.New)) { int count = jobname.Id2Sprite.Count; TaskManager.DisplayTaskC("Adding text label", "Processing...", () => from / (float) count * 100f, isCancelling => { try { foreach (var mobEntry in jobname.Id2Sprite.OrderBy(p => p.Key)) { if (isCancelling()) return; var mobResourceName = mobEntry.Value; var mobId = mobEntry.Key; //b.AppendLine(mobId + "\t" + mobResourceName + "\t" + processed.Contains(mobResourceName)); if (processed.Contains(mobResourceName)) { from++; continue; } if (mobId2ServerName.ContainsKey(mobId)) { displayName = mobId2ServerName[mobId];//.ToUpper(); processed.Add(mobResourceName); } else { displayName = jobname.Id2Job[mobId].Replace("JT_", "").Replace("_", " ");//.ToUpper(); } var actEntry = dataGrf.FileTable.TryGet(GrfPath.Combine(mobFolder, mobResourceName + ".act")); var sprEntry = dataGrf.FileTable.TryGet(GrfPath.Combine(mobFolder, mobResourceName + ".spr")); if (actEntry == null || sprEntry == null) { from++; continue; } var mobAct = new Act(actEntry, new Spr(sprEntry)); StringBuilder e_builder = new StringBuilder(); if (mobId2ServerName.ContainsKey(mobId)) { int eleLevel = mobId2ServerElementLevel[mobId]; int eleId = mobId2ServerElement[mobId]; var dmgElements = new Dictionary<int, int>(); for (int i = 0; i < 9; i++) { dmgElements[i] = attr_fix[eleLevel, i, eleId]; } int highest = -1; int max = maxElementShown; foreach (var dmgElement in dmgElements.OrderByDescending(p => p.Value)) { if (dmgElement.Value < highest) break; max--; if (max < 0) { e_builder.Append("..."); break; } highest = dmgElement.Value; if (e_builder.Length == 0) e_builder.Append(id2ele[dmgElement.Key] + (showFirstElementDamage ? " (" + dmgElement.Value + "%)" : "")); else e_builder.Append(" - " + id2ele[dmgElement.Key]); } mobAct = addNameAbove(mobAct, e_builder.ToString(), 0); if (displayMobNames) mobAct = addNameAbove(mobAct, displayName, 15); } else { if (!displayMobNames) { from++; continue; } mobAct = addNameAbove(mobAct, displayName, 0); } MemoryStream actStream = new MemoryStream(); MemoryStream sprStream = new MemoryStream(); try { mobAct.SaveWithSprite(actStream, sprStream); } catch { from++; continue; } outputGrf.Commands.AddFile(actEntry.RelativePath, actStream); outputGrf.Commands.AddFile(sprEntry.RelativePath, sprStream); if (from % 200 == 0) { GC.Collect(); outputGrf.QuickSave(); outputGrf.Reload(); } from++; } } catch (Exception err) { ErrorHandler.HandleException(err); } finally { outputGrf.QuickSave(); from = count; } }); //ErrorHandler.HandleException(b.ToString()); } } catch (Exception err) { ErrorHandler.HandleException(err); } finally { EncodingService.DisplayEncoding = oldEncoding; }3 points
-
This behavior seems similar to Geneticist's Hell Plant, which is a status that deals damage around the player every 0.3 seconds. In status.cpp you can see in status_change_timer: case SC_HELLS_PLANT: tick_time = status_get_sc_interval(type); val4 = tick - tick_time; // Remaining time break; In status_change_timer_sub: case SC_HELLS_PLANT: if( sce->val4 >= 0 ){ skill_castend_damage_id( bl, bl, GN_HELLS_PLANT_ATK, sce->val1, tick, 0 ); } break; Might not be the best way but maybe you could create a status that handles the healing over time following that structure, and make the skill apply the status for 4 seconds?1 point
-
I created a mob with id 30000 and AegieName G_MARINE_SPHERE in db/import/mob_db.yml all other stats copied from the original Then I added this to db/import/mob_avail.yml to assign it a sprite: Body: - Mob: G_MARINE_SPHERE Sprite: MARINE_SPHERE In db/import/mob_skill_db.txt I added: 30000,Marine Sphere@NPC_SELFDESTRUCTION,idle,173,1,10000,3500,0,yes,self,onspawn,,,,,,,, In skill.cpp: case AM_SPHEREMINE: case AM_CANNIBALIZE: { int32 summons[5] = { MOBID_G_MANDRAGORA, MOBID_G_HYDRA, MOBID_G_FLORA, MOBID_G_PARASITE, MOBID_G_GEOGRAPHER }; int32 class_ = skill_id==AM_SPHEREMINE?MOBID_MARINE_SPHERE:summons[skill_lv-1]; enum mob_ai ai = (skill_id == AM_SPHEREMINE) ? AI_SPHERE : AI_FLORA; struct mob_data *md; I changed MOBID_MARINE_SPHERE to MOBID_G_MARINE_SPHERE. Finally in mob.hpp: enum MOBID { MOBID_PORING = 1002, MOBID_RED_PLANT = 1078, MOBID_BLUE_PLANT, MOBID_GREEN_PLANT, MOBID_YELLOW_PLANT, MOBID_WHITE_PLANT, MOBID_SHINING_PLANT, MOBID_BLACK_MUSHROOM = 1084, MOBID_MARINE_SPHERE = 1142, MOBID_EMPERIUM = 1288, MOBID_G_PARASITE = 1555, MOBID_G_FLORA = 1575, MOBID_G_HYDRA = 1579, MOBID_G_MANDRAGORA = 1589, MOBID_G_GEOGRAPHER = 1590, MOBID_GUARDIAN_STONE1 = 1907, MOBID_GUARDIAN_STONE2, MOBID_SILVERSNIPER = 2042, MOBID_MAGICDECOY_FIRE, MOBID_MAGICDECOY_WATER, MOBID_MAGICDECOY_EARTH, MOBID_MAGICDECOY_WIND, MOBID_ZANZOU = 2308, MOBID_S_HORNET = 2158, MOBID_S_GIANT_HORNET, MOBID_S_LUCIOLA_VESPA, MOBID_GUILD_SKILL_FLAG = 20269, MOBID_ABR_BATTLE_WARIOR = 20834, MOBID_ABR_DUAL_CANNON, MOBID_ABR_MOTHER_NET, MOBID_ABR_INFINITY, MOBID_BIONIC_WOODENWARRIOR = 20848, MOBID_BIONIC_WOODEN_FAIRY, MOBID_BIONIC_CREEPER, MOBID_BIONIC_HELLTREE, }; I added MOBID_G_MARINE_SPHERE = 30000, into this enum I think that's it.1 point
-
This map is from the game universe expansion series. Made with attention to detail and inspired by the original! .•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•.•°'°•. mosk_dun04 The map completely repeats all the patterns and elements of levels 1 and 2 of the Moscovia dungeon. I tried to use design elements from the official painted representation as much as possible. The map seamlessly connects with neighboring levels. Excellent for placing low-level monsters without a place to live: Woodie > and < Domovoi Woodie already has its own card with an image and effect, but Domovoi still hasn’t, so I’m glad to show you and suggest using this art for Domovoi card! (Without the use of AI. It is initially a pencil drawing on paper) Please download Domovoi_Card.BMP: Domovoi_Card.bmp P.S. In the Slavic religious tradition, Domovoy is the household spirit of a given kin. Please rate it if the map is good enough ~~ This map can be downloaded here https://rathena.org/board/files/file/4487-w0w_map_collection/1 point