From d795927f82cca784164fc1500ffdc93c8c74271c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 9 Nov 2023 14:31:35 +0000 Subject: [PATCH] deploy: 8212b5f1fb906524757c2fe6708a63cd3232ad1c --- 404.html | 4 ++-- about/index.html | 4 ++-- arcturus/index.html | 4 ++-- assets/js/07b2f877.1089cba0.js | 1 - assets/js/07b2f877.8445beed.js | 1 + assets/js/935f2afb.df71aa47.js | 1 + assets/js/935f2afb.f081478f.js | 1 - assets/js/{e1c03129.9464e0ed.js => e1c03129.c88879c2.js} | 2 +- assets/js/{f0fff0af.e2ecea3b.js => f0fff0af.e7ce2566.js} | 2 +- assets/js/runtime~main.8273ce7e.js | 1 + assets/js/runtime~main.ed408e42.js | 1 - community-projects/index.html | 4 ++-- contents/index.html | 4 ++-- contributing/index.html | 4 ++-- file-formats/RGZ/index.html | 8 ++++---- file-formats/act/index.html | 6 +++--- file-formats/ebm/index.html | 6 +++--- file-formats/ezv/index.html | 6 +++--- file-formats/gat/index.html | 6 +++--- file-formats/gnd/index.html | 6 +++--- file-formats/gr2/index.html | 6 +++--- file-formats/grf/index.html | 6 +++--- file-formats/imf/index.html | 6 +++--- file-formats/index.html | 6 +++--- file-formats/ini/index.html | 4 ++-- file-formats/lub/index.html | 6 +++--- file-formats/pal/index.html | 6 +++--- file-formats/rma/index.html | 4 ++-- file-formats/rsa/index.html | 4 ++-- file-formats/rsm/index.html | 6 +++--- file-formats/rsw/index.html | 6 +++--- file-formats/rsx/index.html | 4 ++-- file-formats/scr/index.html | 4 ++-- file-formats/spr/index.html | 6 +++--- file-formats/str/index.html | 6 +++--- game-mechanics/creature-ai/index.html | 4 ++-- game-mechanics/effects/fire-wall/index.html | 4 ++-- game-mechanics/effects/index.html | 4 ++-- game-mechanics/index.html | 4 ++-- game-mechanics/movement/index.html | 4 ++-- game-mechanics/world-state-updates/index.html | 4 ++-- glossary/index.html | 4 ++-- index.html | 4 ++-- rendering/animation-systems/index.html | 4 ++-- rendering/camera-controls/index.html | 4 ++-- rendering/coordinate-systems/index.html | 4 ++-- rendering/ground-mesh/index.html | 4 ++-- rendering/index.html | 4 ++-- tools/index.html | 4 ++-- 49 files changed, 104 insertions(+), 104 deletions(-) delete mode 100644 assets/js/07b2f877.1089cba0.js create mode 100644 assets/js/07b2f877.8445beed.js create mode 100644 assets/js/935f2afb.df71aa47.js delete mode 100644 assets/js/935f2afb.f081478f.js rename assets/js/{e1c03129.9464e0ed.js => e1c03129.c88879c2.js} (53%) rename assets/js/{f0fff0af.e2ecea3b.js => f0fff0af.e7ce2566.js} (87%) create mode 100644 assets/js/runtime~main.8273ce7e.js delete mode 100644 assets/js/runtime~main.ed408e42.js diff --git a/404.html b/404.html index 6484dbd5..a792208f 100644 --- a/404.html +++ b/404.html @@ -4,13 +4,13 @@ Page Not Found | Ragnarok Research Lab - +
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- + \ No newline at end of file diff --git a/about/index.html b/about/index.html index 5bb8446e..83ff4c5b 100644 --- a/about/index.html +++ b/about/index.html @@ -4,13 +4,13 @@ About | Ragnarok Research Lab - +
Skip to main content

About

This project is dedicated to researching the technical background of Gravity Co.'s 2002 MMORPG, Ragnarok Online.

One Repository to Rule Them All

Ever since RO was first created, there have been community projects interested in researching its inner workings. Many authors spent countless hours learning, coding, and debugging before - for some reason or another - losing interest in their various pet projects and letting most of their knowledge fall into obscurity. To say that good documentation on the topic is sparse would be an understatement, which in turn puts up high barriers to innovation and makes life difficult for anyone who wants to learn.

The Ragnarok Research Lab is a project long envisioned, with the goal of solving these kinds of problems... and here's how:

  • This website will present reviewed, updated, and organized information about all technical aspects of Ragnarok Online
  • Old sources are dug up, verified, and finally integrated to enrich a growing body of knowledge that anyone can reference
  • New and completely original research is conducted to keep up with the many changes that Gravity introduces to the game
  • By working together with all community authors, technical details known to few can be published and shared with ease

All of this has been happening - somewhat behind the scenes - for a few years already. The results have been impressive, but more is yet to come. With the help of the community, hopefully soon most open questions will be answered in a way that's easy to digest. Plus, having it all collected in a git repository will ensure the information remains available for many years to come.

Research and Development

As part of the ongoing research effort, specialist tools have been developed. While most aren't quite ready for a production release, expect more and more of them to appear here as they grow increasingly stable and mature. A few examples:

  • A well-tested library for decoding (and encoding) client files
  • Datamining utilities to help analyze the client's file formats
  • 3D visualization framework to render models, sprites, and effects
  • A web-based analysis platform for client- and serverside data

While similar software has been written and released before, none have so far focused on education and learning.

A Collaborative Effort

Since this is essentially a community service, it can only succeed if as many people as possible lend a hand. If you're involved with a Ragnarok-related project, please consider contributing your knowledge (and a bit of time) to this documentation!

  • It's the best way to make sure your work benefits as many people as possible and won't be forgotten
  • Teaching others what you've learned will also broaden your understanding, which positively impacts your own results
  • There are no politics or preferential treatment; great documentation is for everyone and independent of any one project
  • Lastly, you're sure to find a community of developers who are happy to help with any problem you might encounter

Should you be interested, please check out the Contributor's Guide! It features handy references, instructions, and much more.

First Steps

For your convenience, this documentation website is split into multiple categories:

  • The File Formats specification covers all client-side file formats that Gravity invented for Ragnarok Online
  • To see how they can be used to reconstruct a visual representation of the world, take a look at the Rendering category
  • In the Game Mechanics section, you can learn much about the implementation of the gameplay logic on the zone servers
  • For some more background information on Ragnarok Online's predecessor and how it relates to RO, see Arcturus
  • General terms used across this website are listed on a separate page, under Glossary

If that isn't quite what you were looking for, feel free to ask any questions on Discord or open an issue on GitHub!

- + \ No newline at end of file diff --git a/arcturus/index.html b/arcturus/index.html index 39442754..4655b75d 100644 --- a/arcturus/index.html +++ b/arcturus/index.html @@ -4,13 +4,13 @@ Overview | Ragnarok Research Lab - +
Skip to main content

Overview

In this category, you will find all about the connection between Ragnarok Online and Arcturus: The Curse and Loss of Divinity.

Target Audience

Due to the technical nature of the subject matter, this specification is written under the assumption that you're a programmer or at least have some programming experience. The information provided here will hopefully be interesting to anyone familiar with the game, but if you're not on good terms with basic programming concepts a bit of a learning curve should be expected.

Objectives

info

This section is a placeholder. If you know anything about the topic, please help fill it with content!

Limitations

info

This section is a placeholder. If you know anything about the topic, please help fill it with content!

Prerequisites

info

This section is a placeholder. If you know anything about the topic, please help fill it with content!

File Formats

The following file types have been found in the Japanese DVD release of Arcturus:

arcturus-jp-extensions.txt
act
atk
bmp
coonmt01
dat
db
flc
fnt
gat
gnd
htm
ico
ini
itm
jp
kr
lnk
mag
mariaspr
mon
pix
rma
rsa
rsm
rsp
rsw
rsx
scr
skl
spr
tga
tmp
txt
wav
zip

This list can be generated with regular Unix shell commands (or MSYS2 on Windows):

find-unique-file-types.sh
find . -type f -exec basename {} \; | sed 's/.*\.//' | sort | uniq > extensions.txt

Note: mariaspr is obviously intended to mean maria.spr (leftover SPR 1.1 sprite). coonmt01 is a duplicate 8-bit BMP file (meh).

- + \ No newline at end of file diff --git a/assets/js/07b2f877.1089cba0.js b/assets/js/07b2f877.1089cba0.js deleted file mode 100644 index 673f81dd..00000000 --- a/assets/js/07b2f877.1089cba0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8854],{3905:(e,t,r)=>{r.d(t,{Zo:()=>f,kt:()=>d});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var c=n.createContext({}),s=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},f=function(e){var t=s(e.components);return n.createElement(c.Provider,{value:t},e.children)},p="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,c=e.parentName,f=l(e,["components","mdxType","originalType","parentName"]),p=s(r),m=o,d=p["".concat(c,".").concat(m)]||p[m]||u[m]||a;return r?n.createElement(d,i(i({ref:t},f),{},{components:r})):n.createElement(d,i({ref:t},f))}));function d(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=m;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[p]="string"==typeof e?e:o,i[1]=l;for(var s=2;s{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>u,frontMatter:()=>a,metadata:()=>l,toc:()=>s});var n=r(7462),o=(r(7294),r(3905));const a={slug:"/file-formats/RGZ",title:"RGZ (Placeholder)"},i=void 0,l={unversionedId:"file-formats/RGZ",id:"file-formats/RGZ",title:"RGZ (Placeholder)",description:"This section is a placeholder. If you know anything about the topic, please help fill it with content!",source:"@site/docs/file-formats/RGZ.md",sourceDirName:"file-formats",slug:"/file-formats/RGZ",permalink:"/file-formats/RGZ",draft:!1,editUrl:"https://github.com/RagnarokResearchLab/ragnarokresearchlab.github.io/edit/main/docs/file-formats/RGZ.md",tags:[],version:"current",frontMatter:{slug:"/file-formats/RGZ",title:"RGZ (Placeholder)"},sidebar:"tutorialSidebar",previous:{title:"PAL",permalink:"/file-formats/pal"},next:{title:"RSM (Placeholder)",permalink:"/file-formats/rsm"}},c={},s=[],f={toc:s},p="wrapper";function u(e){let{components:t,...r}=e;return(0,o.kt)(p,(0,n.Z)({},f,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("admonition",{type:"info"},(0,o.kt)("p",{parentName:"admonition"},"This section is a placeholder. If you know anything about the topic, please help fill it with content!")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/07b2f877.8445beed.js b/assets/js/07b2f877.8445beed.js new file mode 100644 index 00000000..e99e9d40 --- /dev/null +++ b/assets/js/07b2f877.8445beed.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[8854],{3905:(e,t,n)=>{n.d(t,{Zo:()=>c,kt:()=>u});var r=n(7294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function l(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var p=r.createContext({}),d=function(e){var t=r.useContext(p),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},c=function(e){var t=d(e.components);return r.createElement(p.Provider,{value:t},e.children)},m="mdxType",s={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,i=e.originalType,p=e.parentName,c=o(e,["components","mdxType","originalType","parentName"]),m=d(n),f=a,u=m["".concat(p,".").concat(f)]||m[f]||s[f]||i;return n?r.createElement(u,l(l({ref:t},c),{},{components:n})):r.createElement(u,l({ref:t},c))}));function u(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var i=n.length,l=new Array(i);l[0]=f;var o={};for(var p in t)hasOwnProperty.call(t,p)&&(o[p]=t[p]);o.originalType=e,o[m]="string"==typeof e?e:a,l[1]=o;for(var d=2;d{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>s,frontMatter:()=>i,metadata:()=>o,toc:()=>d});var r=n(7462),a=(n(7294),n(3905));const i={slug:"/file-formats/RGZ",title:"RGZ"},l=void 0,o={unversionedId:"file-formats/RGZ",id:"file-formats/RGZ",title:"RGZ",description:"This document describes the RGZ file format used in the Ragnarok Online client.",source:"@site/docs/file-formats/RGZ.md",sourceDirName:"file-formats",slug:"/file-formats/RGZ",permalink:"/file-formats/RGZ",draft:!1,editUrl:"https://github.com/RagnarokResearchLab/ragnarokresearchlab.github.io/edit/main/docs/file-formats/RGZ.md",tags:[],version:"current",frontMatter:{slug:"/file-formats/RGZ",title:"RGZ"},sidebar:"tutorialSidebar",previous:{title:"PAL",permalink:"/file-formats/pal"},next:{title:"RSM (Placeholder)",permalink:"/file-formats/rsm"}},p={},d=[{value:"Contents",id:"contents",level:2},{value:"Layout",id:"layout",level:2},{value:"See also",id:"see-also",level:2}],c={toc:d},m="wrapper";function s(e){let{components:t,...n}=e;return(0,a.kt)(m,(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"This document describes the RGZ file format used in the Ragnarok Online client."),(0,a.kt)("h2",{id:"contents"},"Contents"),(0,a.kt)("p",null,"RGZ files are ",(0,a.kt)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/Gzip"},"gzip-compressed")," patch files used to update ",(0,a.kt)("a",{parentName:"p",href:"/file-formats/grf"},"GRF")," archives."),(0,a.kt)("h2",{id:"layout"},"Layout"),(0,a.kt)("p",null,"Each RGZ archive contains an array of file and directory entries that should be applied to the GRF:"),(0,a.kt)("table",null,(0,a.kt)("thead",{parentName:"table"},(0,a.kt)("tr",{parentName:"thead"},(0,a.kt)("th",{parentName:"tr",align:"center"},"Field"),(0,a.kt)("th",{parentName:"tr",align:"center"},"Offset"),(0,a.kt)("th",{parentName:"tr",align:"center"},"Length"),(0,a.kt)("th",{parentName:"tr",align:"center"},"Type"),(0,a.kt)("th",{parentName:"tr",align:"center"},"Description"))),(0,a.kt)("tbody",{parentName:"table"},(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:"center"},(0,a.kt)("inlineCode",{parentName:"td"},"EntryType")),(0,a.kt)("td",{parentName:"tr",align:"center"},"0"),(0,a.kt)("td",{parentName:"tr",align:"center"},"1"),(0,a.kt)("td",{parentName:"tr",align:"center"},(0,a.kt)("inlineCode",{parentName:"td"},"char")),(0,a.kt)("td",{parentName:"tr",align:"center"},"Directory entry (",(0,a.kt)("inlineCode",{parentName:"td"},"'d'"),"), end-of-file symbol (",(0,a.kt)("inlineCode",{parentName:"td"},"'e'"),"), or file entry (",(0,a.kt)("inlineCode",{parentName:"td"},"'f'"),")")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:"center"},(0,a.kt)("inlineCode",{parentName:"td"},"FileNameLength")),(0,a.kt)("td",{parentName:"tr",align:"center"},"1"),(0,a.kt)("td",{parentName:"tr",align:"center"},"1"),(0,a.kt)("td",{parentName:"tr",align:"center"},(0,a.kt)("inlineCode",{parentName:"td"},"uint8")),(0,a.kt)("td",{parentName:"tr",align:"center"},"Length of the file name (",(0,a.kt)("inlineCode",{parentName:"td"},"string"),") that follows")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:"center"},(0,a.kt)("inlineCode",{parentName:"td"},"FileName")),(0,a.kt)("td",{parentName:"tr",align:"center"},"2"),(0,a.kt)("td",{parentName:"tr",align:"center"},"variable"),(0,a.kt)("td",{parentName:"tr",align:"center"},(0,a.kt)("inlineCode",{parentName:"td"},"string")),(0,a.kt)("td",{parentName:"tr",align:"center"},"Null-terminated, but at most ",(0,a.kt)("inlineCode",{parentName:"td"},"FileNameLength")," bytes (with encoding: ",(0,a.kt)("a",{parentName:"td",href:"https://en.wikipedia.org/wiki/Unified_Hangul_Code"},"CP949"),")")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:"center"},(0,a.kt)("inlineCode",{parentName:"td"},"FileSize")),(0,a.kt)("td",{parentName:"tr",align:"center"},"variable"),(0,a.kt)("td",{parentName:"tr",align:"center"},"variable"),(0,a.kt)("td",{parentName:"tr",align:"center"},(0,a.kt)("inlineCode",{parentName:"td"},"uint32")),(0,a.kt)("td",{parentName:"tr",align:"center"},"File entries only: Length of the file contents (",(0,a.kt)("inlineCode",{parentName:"td"},"blob"),") that follows")),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:"center"},(0,a.kt)("inlineCode",{parentName:"td"},"FileContents")),(0,a.kt)("td",{parentName:"tr",align:"center"},"variable"),(0,a.kt)("td",{parentName:"tr",align:"center"},"variable"),(0,a.kt)("td",{parentName:"tr",align:"center"},(0,a.kt)("inlineCode",{parentName:"td"},"blob")),(0,a.kt)("td",{parentName:"tr",align:"center"},"Only present for file entries; otherwise skip this field")))),(0,a.kt)("p",null,"This structure is repeated until the EOF entry ",(0,a.kt)("inlineCode",{parentName:"p"},"'e'")," with name ",(0,a.kt)("inlineCode",{parentName:"p"},'"end"')," is encountered, which must always be the final entry."),(0,a.kt)("h2",{id:"see-also"},"See also"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("a",{parentName:"li",href:"https://github.com/rathena/rathena/wiki/RGZ"},"Description of the RGZ format")," (rAthena Wiki)")))}s.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/935f2afb.df71aa47.js b/assets/js/935f2afb.df71aa47.js new file mode 100644 index 00000000..16c05c36 --- /dev/null +++ b/assets/js/935f2afb.df71aa47.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[53],{1109:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"About","href":"/about","docId":"about"},{"type":"link","label":"Contributing","href":"/contributing","docId":"contributing"},{"type":"category","label":"File Formats","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Overview","href":"/file-formats","docId":"file-formats/index"},{"type":"link","label":"ACT (Placeholder)","href":"/file-formats/act","docId":"file-formats/ACT"},{"type":"link","label":"EBM","href":"/file-formats/ebm","docId":"file-formats/EBM"},{"type":"link","label":"EZV (Placeholder)","href":"/file-formats/ezv","docId":"file-formats/EZV"},{"type":"link","label":"GAT","href":"/file-formats/gat","docId":"file-formats/GAT"},{"type":"link","label":"GND","href":"/file-formats/gnd","docId":"file-formats/GND"},{"type":"link","label":"GR2","href":"/file-formats/gr2","docId":"file-formats/GR2"},{"type":"link","label":"GRF","href":"/file-formats/grf","docId":"file-formats/GRF"},{"type":"link","label":"IMF (Placeholder)","href":"/file-formats/imf","docId":"file-formats/IMF"},{"type":"link","label":"LUB (Placeholder)","href":"/file-formats/lub","docId":"file-formats/LUB"},{"type":"link","label":"PAL","href":"/file-formats/pal","docId":"file-formats/PAL"},{"type":"link","label":"RGZ","href":"/file-formats/RGZ","docId":"file-formats/RGZ"},{"type":"link","label":"RSM (Placeholder)","href":"/file-formats/rsm","docId":"file-formats/RSM"},{"type":"link","label":"RSW","href":"/file-formats/rsw","docId":"file-formats/RSW"},{"type":"link","label":"SPR","href":"/file-formats/spr","docId":"file-formats/SPR"},{"type":"link","label":"STR (Placeholder)","href":"/file-formats/str","docId":"file-formats/STR"}]},{"type":"category","label":"Rendering","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Overview","href":"/rendering","docId":"rendering/index"},{"type":"link","label":"Coordinate Systems","href":"/rendering/coordinate-systems","docId":"rendering/coordinate-systems"},{"type":"link","label":"Ground Mesh","href":"/rendering/ground-mesh","docId":"rendering/ground-mesh"},{"type":"link","label":"Camera Controls","href":"/rendering/camera-controls","docId":"rendering/camera-controls"},{"type":"link","label":"Animation Systems","href":"/rendering/animation-systems","docId":"rendering/animation-systems"}]},{"type":"category","label":"Game Mechanics","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Overview","href":"/game-mechanics","docId":"game-mechanics/index"},{"type":"link","label":"World State Updates","href":"/game-mechanics/world-state-updates","docId":"game-mechanics/world-state-updates"},{"type":"link","label":"Movement and Pathfinding","href":"/game-mechanics/movement","docId":"game-mechanics/movement-and-pathfinding"},{"type":"link","label":"Creature AI and Skillsets","href":"/game-mechanics/creature-ai","docId":"game-mechanics/creature-ai"},{"type":"category","label":"Skills and Spell Effects","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Fire Wall","href":"/game-mechanics/effects/fire-wall","docId":"game-mechanics/effects/fire-wall"}],"href":"/game-mechanics/effects"}]},{"type":"category","label":"Arcturus","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Overview","href":"/arcturus","docId":"arcturus/index"},{"type":"link","label":"INI (Placeholder)","href":"/file-formats/ini","docId":"arcturus/INI"},{"type":"link","label":"RMA (Placeholder)","href":"/file-formats/rma","docId":"arcturus/RMA"},{"type":"link","label":"RSA (Placeholder)","href":"/file-formats/rsa","docId":"arcturus/RSA"},{"type":"link","label":"RSX (Placeholder)","href":"/file-formats/rsx","docId":"arcturus/RSX"},{"type":"link","label":"SCR (Placeholder)","href":"/file-formats/scr","docId":"arcturus/SCR"}]},{"type":"link","label":"Community Projects","href":"/community-projects","docId":"community-projects"},{"type":"link","label":"Glossary","href":"/glossary","docId":"glossary"}]},"docs":{"about":{"id":"about","title":"About","description":"This project is dedicated to researching the technical background of Gravity Co.\'s 2002 MMORPG, Ragnarok Online.","sidebar":"tutorialSidebar"},"arcturus/index":{"id":"arcturus/index","title":"Overview","description":"In this category, you will find all about the connection between Ragnarok Online and Arcturus: The Curse and Loss of Divinity.","sidebar":"tutorialSidebar"},"arcturus/INI":{"id":"arcturus/INI","title":"INI (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"arcturus/RMA":{"id":"arcturus/RMA","title":"RMA (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"arcturus/RSA":{"id":"arcturus/RSA","title":"RSA (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"arcturus/RSX":{"id":"arcturus/RSX","title":"RSX (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"arcturus/SCR":{"id":"arcturus/SCR","title":"SCR (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"community-projects":{"id":"community-projects","title":"Community Projects","description":"This page lists all of the known Ragnarok Online-related community projects that may be of interest to researchers.","sidebar":"tutorialSidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"On this page, you\'ll find all the information you need to make a successful contribution to this project.","sidebar":"tutorialSidebar"},"file-formats/ACT":{"id":"file-formats/ACT","title":"ACT (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"file-formats/EBM":{"id":"file-formats/EBM","title":"EBM","description":"This document describes the EBM file format used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/EZV":{"id":"file-formats/EZV","title":"EZV (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"file-formats/GAT":{"id":"file-formats/GAT","title":"GAT","description":"This document describes the GAT file format used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/GND":{"id":"file-formats/GND","title":"GND","description":"This document describes the GND file format used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/GR2":{"id":"file-formats/GR2","title":"GR2","description":"This document describes the GR2 file format used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/GRF":{"id":"file-formats/GRF","title":"GRF","description":"This document describes the GRF file format used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/IMF":{"id":"file-formats/IMF","title":"IMF (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"file-formats/index":{"id":"file-formats/index","title":"Overview","description":"In this category, you will find a description of the custom file formats that are used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/LUB":{"id":"file-formats/LUB","title":"LUB (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"file-formats/PAL":{"id":"file-formats/PAL","title":"PAL","description":"This document describes the PAL file format used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/RGZ":{"id":"file-formats/RGZ","title":"RGZ","description":"This document describes the RGZ file format used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/RSM":{"id":"file-formats/RSM","title":"RSM (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"file-formats/RSW":{"id":"file-formats/RSW","title":"RSW","description":"This document describes the RSW file format used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/SPR":{"id":"file-formats/SPR","title":"SPR","description":"This document describes the SPR file format used in the Ragnarok Online client (and Arcturus).","sidebar":"tutorialSidebar"},"file-formats/STR":{"id":"file-formats/STR","title":"STR (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"game-mechanics/creature-ai":{"id":"game-mechanics/creature-ai","title":"Creature AI and Skillsets","description":"This document describes how Ragnarok Online\'s zone servers determine the behavior of AI-enabled actors in the game world.","sidebar":"tutorialSidebar"},"game-mechanics/effects/fire-wall":{"id":"game-mechanics/effects/fire-wall","title":"Fire Wall","description":"Fire Wall is a Mage spell that places several AOE triggers, which can damage and knock back enemies.","sidebar":"tutorialSidebar"},"game-mechanics/effects/index":{"id":"game-mechanics/effects/index","title":"Skills and Spell Effects","description":"This document describes how Ragnarok Online\'s zone servers implement the various spells and skill effects.","sidebar":"tutorialSidebar"},"game-mechanics/index":{"id":"game-mechanics/index","title":"Overview","description":"In this category, you will find a description of the serverside behaviors that enable the gameplay of Ragnarok Online.","sidebar":"tutorialSidebar"},"game-mechanics/movement-and-pathfinding":{"id":"game-mechanics/movement-and-pathfinding","title":"Movement and Pathfinding","description":"This document describes how Ragnarok Online\'s zone servers implement the movement of ingame actors.","sidebar":"tutorialSidebar"},"game-mechanics/world-state-updates":{"id":"game-mechanics/world-state-updates","title":"World State Updates","description":"This document describes how Ragnarok Online\'s zone servers update the simulation of the game world.","sidebar":"tutorialSidebar"},"glossary":{"id":"glossary","title":"Glossary","description":"This document lists all terms that have a special meaning in the context of this documentation, and Ragnarok Online in general.","sidebar":"tutorialSidebar"},"rendering/animation-systems":{"id":"rendering/animation-systems","title":"Animation Systems","description":"This document describes the different animation systems used in the Ragnarok Online client","sidebar":"tutorialSidebar"},"rendering/camera-controls":{"id":"rendering/camera-controls","title":"Camera Controls","description":"This document describes the camera controller used by the Ragnarok Online client.","sidebar":"tutorialSidebar"},"rendering/coordinate-systems":{"id":"rendering/coordinate-systems","title":"Coordinate Systems","description":"This document describes the coordinate systems used by the Ragnarok Online client.","sidebar":"tutorialSidebar"},"rendering/ground-mesh":{"id":"rendering/ground-mesh","title":"Ground Mesh","description":"This document describes how static terrain can be reconstructed from the GND files used by the Ragnarok Online client.","sidebar":"tutorialSidebar"},"rendering/index":{"id":"rendering/index","title":"Overview","description":"In this category, you will find out how the visual representation of Ragnarok Online\'s game world is created by its client.","sidebar":"tutorialSidebar"}}}')}}]); \ No newline at end of file diff --git a/assets/js/935f2afb.f081478f.js b/assets/js/935f2afb.f081478f.js deleted file mode 100644 index 622d7ba3..00000000 --- a/assets/js/935f2afb.f081478f.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[53],{1109:e=>{e.exports=JSON.parse('{"pluginId":"default","version":"current","label":"Next","banner":null,"badge":false,"noIndex":false,"className":"docs-version-current","isLast":true,"docsSidebars":{"tutorialSidebar":[{"type":"link","label":"About","href":"/about","docId":"about"},{"type":"link","label":"Contributing","href":"/contributing","docId":"contributing"},{"type":"category","label":"File Formats","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Overview","href":"/file-formats","docId":"file-formats/index"},{"type":"link","label":"ACT (Placeholder)","href":"/file-formats/act","docId":"file-formats/ACT"},{"type":"link","label":"EBM","href":"/file-formats/ebm","docId":"file-formats/EBM"},{"type":"link","label":"EZV (Placeholder)","href":"/file-formats/ezv","docId":"file-formats/EZV"},{"type":"link","label":"GAT","href":"/file-formats/gat","docId":"file-formats/GAT"},{"type":"link","label":"GND","href":"/file-formats/gnd","docId":"file-formats/GND"},{"type":"link","label":"GR2","href":"/file-formats/gr2","docId":"file-formats/GR2"},{"type":"link","label":"GRF","href":"/file-formats/grf","docId":"file-formats/GRF"},{"type":"link","label":"IMF (Placeholder)","href":"/file-formats/imf","docId":"file-formats/IMF"},{"type":"link","label":"LUB (Placeholder)","href":"/file-formats/lub","docId":"file-formats/LUB"},{"type":"link","label":"PAL","href":"/file-formats/pal","docId":"file-formats/PAL"},{"type":"link","label":"RGZ (Placeholder)","href":"/file-formats/RGZ","docId":"file-formats/RGZ"},{"type":"link","label":"RSM (Placeholder)","href":"/file-formats/rsm","docId":"file-formats/RSM"},{"type":"link","label":"RSW","href":"/file-formats/rsw","docId":"file-formats/RSW"},{"type":"link","label":"SPR","href":"/file-formats/spr","docId":"file-formats/SPR"},{"type":"link","label":"STR (Placeholder)","href":"/file-formats/str","docId":"file-formats/STR"}]},{"type":"category","label":"Rendering","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Overview","href":"/rendering","docId":"rendering/index"},{"type":"link","label":"Coordinate Systems","href":"/rendering/coordinate-systems","docId":"rendering/coordinate-systems"},{"type":"link","label":"Ground Mesh","href":"/rendering/ground-mesh","docId":"rendering/ground-mesh"},{"type":"link","label":"Camera Controls","href":"/rendering/camera-controls","docId":"rendering/camera-controls"},{"type":"link","label":"Animation Systems","href":"/rendering/animation-systems","docId":"rendering/animation-systems"}]},{"type":"category","label":"Game Mechanics","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Overview","href":"/game-mechanics","docId":"game-mechanics/index"},{"type":"link","label":"World State Updates","href":"/game-mechanics/world-state-updates","docId":"game-mechanics/world-state-updates"},{"type":"link","label":"Movement and Pathfinding","href":"/game-mechanics/movement","docId":"game-mechanics/movement-and-pathfinding"},{"type":"link","label":"Creature AI and Skillsets","href":"/game-mechanics/creature-ai","docId":"game-mechanics/creature-ai"},{"type":"category","label":"Skills and Spell Effects","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Fire Wall","href":"/game-mechanics/effects/fire-wall","docId":"game-mechanics/effects/fire-wall"}],"href":"/game-mechanics/effects"}]},{"type":"category","label":"Arcturus","collapsible":true,"collapsed":true,"items":[{"type":"link","label":"Overview","href":"/arcturus","docId":"arcturus/index"},{"type":"link","label":"INI (Placeholder)","href":"/file-formats/ini","docId":"arcturus/INI"},{"type":"link","label":"RMA (Placeholder)","href":"/file-formats/rma","docId":"arcturus/RMA"},{"type":"link","label":"RSA (Placeholder)","href":"/file-formats/rsa","docId":"arcturus/RSA"},{"type":"link","label":"RSX (Placeholder)","href":"/file-formats/rsx","docId":"arcturus/RSX"},{"type":"link","label":"SCR (Placeholder)","href":"/file-formats/scr","docId":"arcturus/SCR"}]},{"type":"link","label":"Community Projects","href":"/community-projects","docId":"community-projects"},{"type":"link","label":"Glossary","href":"/glossary","docId":"glossary"}]},"docs":{"about":{"id":"about","title":"About","description":"This project is dedicated to researching the technical background of Gravity Co.\'s 2002 MMORPG, Ragnarok Online.","sidebar":"tutorialSidebar"},"arcturus/index":{"id":"arcturus/index","title":"Overview","description":"In this category, you will find all about the connection between Ragnarok Online and Arcturus: The Curse and Loss of Divinity.","sidebar":"tutorialSidebar"},"arcturus/INI":{"id":"arcturus/INI","title":"INI (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"arcturus/RMA":{"id":"arcturus/RMA","title":"RMA (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"arcturus/RSA":{"id":"arcturus/RSA","title":"RSA (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"arcturus/RSX":{"id":"arcturus/RSX","title":"RSX (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"arcturus/SCR":{"id":"arcturus/SCR","title":"SCR (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"community-projects":{"id":"community-projects","title":"Community Projects","description":"This page lists all of the known Ragnarok Online-related community projects that may be of interest to researchers.","sidebar":"tutorialSidebar"},"contributing":{"id":"contributing","title":"Contributing","description":"On this page, you\'ll find all the information you need to make a successful contribution to this project.","sidebar":"tutorialSidebar"},"file-formats/ACT":{"id":"file-formats/ACT","title":"ACT (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"file-formats/EBM":{"id":"file-formats/EBM","title":"EBM","description":"This document describes the EBM file format used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/EZV":{"id":"file-formats/EZV","title":"EZV (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"file-formats/GAT":{"id":"file-formats/GAT","title":"GAT","description":"This document describes the GAT file format used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/GND":{"id":"file-formats/GND","title":"GND","description":"This document describes the GND file format used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/GR2":{"id":"file-formats/GR2","title":"GR2","description":"This document describes the GR2 file format used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/GRF":{"id":"file-formats/GRF","title":"GRF","description":"This document describes the GRF file format used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/IMF":{"id":"file-formats/IMF","title":"IMF (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"file-formats/index":{"id":"file-formats/index","title":"Overview","description":"In this category, you will find a description of the custom file formats that are used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/LUB":{"id":"file-formats/LUB","title":"LUB (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"file-formats/PAL":{"id":"file-formats/PAL","title":"PAL","description":"This document describes the PAL file format used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/RGZ":{"id":"file-formats/RGZ","title":"RGZ (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"file-formats/RSM":{"id":"file-formats/RSM","title":"RSM (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"file-formats/RSW":{"id":"file-formats/RSW","title":"RSW","description":"This document describes the RSW file format used in the Ragnarok Online client.","sidebar":"tutorialSidebar"},"file-formats/SPR":{"id":"file-formats/SPR","title":"SPR","description":"This document describes the SPR file format used in the Ragnarok Online client (and Arcturus).","sidebar":"tutorialSidebar"},"file-formats/STR":{"id":"file-formats/STR","title":"STR (Placeholder)","description":"This section is a placeholder. If you know anything about the topic, please help fill it with content!","sidebar":"tutorialSidebar"},"game-mechanics/creature-ai":{"id":"game-mechanics/creature-ai","title":"Creature AI and Skillsets","description":"This document describes how Ragnarok Online\'s zone servers determine the behavior of AI-enabled actors in the game world.","sidebar":"tutorialSidebar"},"game-mechanics/effects/fire-wall":{"id":"game-mechanics/effects/fire-wall","title":"Fire Wall","description":"Fire Wall is a Mage spell that places several AOE triggers, which can damage and knock back enemies.","sidebar":"tutorialSidebar"},"game-mechanics/effects/index":{"id":"game-mechanics/effects/index","title":"Skills and Spell Effects","description":"This document describes how Ragnarok Online\'s zone servers implement the various spells and skill effects.","sidebar":"tutorialSidebar"},"game-mechanics/index":{"id":"game-mechanics/index","title":"Overview","description":"In this category, you will find a description of the serverside behaviors that enable the gameplay of Ragnarok Online.","sidebar":"tutorialSidebar"},"game-mechanics/movement-and-pathfinding":{"id":"game-mechanics/movement-and-pathfinding","title":"Movement and Pathfinding","description":"This document describes how Ragnarok Online\'s zone servers implement the movement of ingame actors.","sidebar":"tutorialSidebar"},"game-mechanics/world-state-updates":{"id":"game-mechanics/world-state-updates","title":"World State Updates","description":"This document describes how Ragnarok Online\'s zone servers update the simulation of the game world.","sidebar":"tutorialSidebar"},"glossary":{"id":"glossary","title":"Glossary","description":"This document lists all terms that have a special meaning in the context of this documentation, and Ragnarok Online in general.","sidebar":"tutorialSidebar"},"rendering/animation-systems":{"id":"rendering/animation-systems","title":"Animation Systems","description":"This document describes the different animation systems used in the Ragnarok Online client","sidebar":"tutorialSidebar"},"rendering/camera-controls":{"id":"rendering/camera-controls","title":"Camera Controls","description":"This document describes the camera controller used by the Ragnarok Online client.","sidebar":"tutorialSidebar"},"rendering/coordinate-systems":{"id":"rendering/coordinate-systems","title":"Coordinate Systems","description":"This document describes the coordinate systems used by the Ragnarok Online client.","sidebar":"tutorialSidebar"},"rendering/ground-mesh":{"id":"rendering/ground-mesh","title":"Ground Mesh","description":"This document describes how static terrain can be reconstructed from the GND files used by the Ragnarok Online client.","sidebar":"tutorialSidebar"},"rendering/index":{"id":"rendering/index","title":"Overview","description":"In this category, you will find out how the visual representation of Ragnarok Online\'s game world is created by its client.","sidebar":"tutorialSidebar"}}}')}}]); \ No newline at end of file diff --git a/assets/js/e1c03129.9464e0ed.js b/assets/js/e1c03129.c88879c2.js similarity index 53% rename from assets/js/e1c03129.9464e0ed.js rename to assets/js/e1c03129.c88879c2.js index f6011631..a73c25eb 100644 --- a/assets/js/e1c03129.9464e0ed.js +++ b/assets/js/e1c03129.c88879c2.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[996],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>d});var a=r(7294);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function l(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,a)}return r}function o(e){for(var t=1;t=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var s=a.createContext({}),p=function(e){var t=a.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},c=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},u="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var r=e.components,n=e.mdxType,l=e.originalType,s=e.parentName,c=i(e,["components","mdxType","originalType","parentName"]),u=p(r),m=n,d=u["".concat(s,".").concat(m)]||u[m]||f[m]||l;return r?a.createElement(d,o(o({ref:t},c),{},{components:r})):a.createElement(d,o({ref:t},c))}));function d(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var l=r.length,o=new Array(l);o[0]=m;var i={};for(var s in t)hasOwnProperty.call(t,s)&&(i[s]=t[s]);i.originalType=e,i[u]="string"==typeof e?e:n,o[1]=i;for(var p=2;p{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>f,frontMatter:()=>l,metadata:()=>i,toc:()=>p});var a=r(7462),n=(r(7294),r(3905));const l={slug:"/file-formats/pal",title:"PAL"},o=void 0,i={unversionedId:"file-formats/PAL",id:"file-formats/PAL",title:"PAL",description:"This document describes the PAL file format used in the Ragnarok Online client.",source:"@site/docs/file-formats/PAL.md",sourceDirName:"file-formats",slug:"/file-formats/pal",permalink:"/file-formats/pal",draft:!1,editUrl:"https://github.com/RagnarokResearchLab/ragnarokresearchlab.github.io/edit/main/docs/file-formats/PAL.md",tags:[],version:"current",frontMatter:{slug:"/file-formats/pal",title:"PAL"},sidebar:"tutorialSidebar",previous:{title:"LUB (Placeholder)",permalink:"/file-formats/lub"},next:{title:"RGZ (Placeholder)",permalink:"/file-formats/RGZ"}},s={},p=[{value:"Contents",id:"contents",level:2},{value:"Alpha Channel",id:"alpha-channel",level:2},{value:"Layout",id:"layout",level:2}],c={toc:p},u="wrapper";function f(e){let{components:t,...r}=e;return(0,n.kt)(u,(0,a.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("p",null,"This document describes the PAL file format used in the Ragnarok Online client."),(0,n.kt)("h2",{id:"contents"},"Contents"),(0,n.kt)("p",null,"PAL files are alternative ",(0,n.kt)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/Palette_(computing)"},"color palettes")," used to tint sprites. To be specific:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"An array of 256 8-bit RGBA color values is appended at the end of any (modern) ",(0,n.kt)("a",{parentName:"li",href:"/file-formats/spr"},"SPR"),' sprite - the "default palette"'),(0,n.kt)("li",{parentName:"ul"},"All standalone PAL files found in the game client are using exactly the same format, which makes it easy to recolor sprites"),(0,n.kt)("li",{parentName:"ul"},"The client can swap any one of them in to replace the default palette, with certain palettes matching specific sprites")),(0,n.kt)("p",null,"All the same limitations apply to the default and alternative palettes - that is to say, transparency isn't supported."),(0,n.kt)("h2",{id:"alpha-channel"},"Alpha Channel"),(0,n.kt)("p",null,"Transparency is determined for each color according to its RGBA values, after loading the palette:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"The color stored at palette index 0 is considered the transparent background color - usually red, green, or magenta"),(0,n.kt)("li",{parentName:"ul"},'Colors that are "close" to the transparency color might also be rendered transparent (warning: unverified claim)'),(0,n.kt)("li",{parentName:"ul"},"If a palette contains alpha values, they appear to be ignored in favor of the actual transparency color")),(0,n.kt)("p",null,"The exact behavior requires some verification still, but a possible approach was described ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/rdw-archive/RagnarokFileFormats/blob/master/PAL.MD"},"here")," (might be incorrect, though)."),(0,n.kt)("h2",{id:"layout"},"Layout"),(0,n.kt)("p",null,"See the ",(0,n.kt)("inlineCode",{parentName:"p"},"ColorPalette")," field in the ",(0,n.kt)("a",{parentName:"p",href:"/file-formats/spr#layout"},"SPR layout table"),". Any SPR file above version ",(0,n.kt)("inlineCode",{parentName:"p"},"1.1")," includes a default palette (i.e., all of them do)."))}f.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[996],{3905:(e,t,r)=>{r.d(t,{Zo:()=>c,kt:()=>d});var a=r(7294);function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function l(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,a)}return r}function o(e){for(var t=1;t=0||(n[r]=e[r]);return n}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(n[r]=e[r])}return n}var s=a.createContext({}),p=function(e){var t=a.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},c=function(e){var t=p(e.components);return a.createElement(s.Provider,{value:t},e.children)},u="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},m=a.forwardRef((function(e,t){var r=e.components,n=e.mdxType,l=e.originalType,s=e.parentName,c=i(e,["components","mdxType","originalType","parentName"]),u=p(r),m=n,d=u["".concat(s,".").concat(m)]||u[m]||f[m]||l;return r?a.createElement(d,o(o({ref:t},c),{},{components:r})):a.createElement(d,o({ref:t},c))}));function d(e,t){var r=arguments,n=t&&t.mdxType;if("string"==typeof e||n){var l=r.length,o=new Array(l);o[0]=m;var i={};for(var s in t)hasOwnProperty.call(t,s)&&(i[s]=t[s]);i.originalType=e,i[u]="string"==typeof e?e:n,o[1]=i;for(var p=2;p{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>f,frontMatter:()=>l,metadata:()=>i,toc:()=>p});var a=r(7462),n=(r(7294),r(3905));const l={slug:"/file-formats/pal",title:"PAL"},o=void 0,i={unversionedId:"file-formats/PAL",id:"file-formats/PAL",title:"PAL",description:"This document describes the PAL file format used in the Ragnarok Online client.",source:"@site/docs/file-formats/PAL.md",sourceDirName:"file-formats",slug:"/file-formats/pal",permalink:"/file-formats/pal",draft:!1,editUrl:"https://github.com/RagnarokResearchLab/ragnarokresearchlab.github.io/edit/main/docs/file-formats/PAL.md",tags:[],version:"current",frontMatter:{slug:"/file-formats/pal",title:"PAL"},sidebar:"tutorialSidebar",previous:{title:"LUB (Placeholder)",permalink:"/file-formats/lub"},next:{title:"RGZ",permalink:"/file-formats/RGZ"}},s={},p=[{value:"Contents",id:"contents",level:2},{value:"Alpha Channel",id:"alpha-channel",level:2},{value:"Layout",id:"layout",level:2}],c={toc:p},u="wrapper";function f(e){let{components:t,...r}=e;return(0,n.kt)(u,(0,a.Z)({},c,r,{components:t,mdxType:"MDXLayout"}),(0,n.kt)("p",null,"This document describes the PAL file format used in the Ragnarok Online client."),(0,n.kt)("h2",{id:"contents"},"Contents"),(0,n.kt)("p",null,"PAL files are alternative ",(0,n.kt)("a",{parentName:"p",href:"https://en.wikipedia.org/wiki/Palette_(computing)"},"color palettes")," used to tint sprites. To be specific:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"An array of 256 8-bit RGBA color values is appended at the end of any (modern) ",(0,n.kt)("a",{parentName:"li",href:"/file-formats/spr"},"SPR"),' sprite - the "default palette"'),(0,n.kt)("li",{parentName:"ul"},"All standalone PAL files found in the game client are using exactly the same format, which makes it easy to recolor sprites"),(0,n.kt)("li",{parentName:"ul"},"The client can swap any one of them in to replace the default palette, with certain palettes matching specific sprites")),(0,n.kt)("p",null,"All the same limitations apply to the default and alternative palettes - that is to say, transparency isn't supported."),(0,n.kt)("h2",{id:"alpha-channel"},"Alpha Channel"),(0,n.kt)("p",null,"Transparency is determined for each color according to its RGBA values, after loading the palette:"),(0,n.kt)("ul",null,(0,n.kt)("li",{parentName:"ul"},"The color stored at palette index 0 is considered the transparent background color - usually red, green, or magenta"),(0,n.kt)("li",{parentName:"ul"},'Colors that are "close" to the transparency color might also be rendered transparent (warning: unverified claim)'),(0,n.kt)("li",{parentName:"ul"},"If a palette contains alpha values, they appear to be ignored in favor of the actual transparency color")),(0,n.kt)("p",null,"The exact behavior requires some verification still, but a possible approach was described ",(0,n.kt)("a",{parentName:"p",href:"https://github.com/rdw-archive/RagnarokFileFormats/blob/master/PAL.MD"},"here")," (might be incorrect, though)."),(0,n.kt)("h2",{id:"layout"},"Layout"),(0,n.kt)("p",null,"See the ",(0,n.kt)("inlineCode",{parentName:"p"},"ColorPalette")," field in the ",(0,n.kt)("a",{parentName:"p",href:"/file-formats/spr#layout"},"SPR layout table"),". Any SPR file above version ",(0,n.kt)("inlineCode",{parentName:"p"},"1.1")," includes a default palette (i.e., all of them do)."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/f0fff0af.e2ecea3b.js b/assets/js/f0fff0af.e7ce2566.js similarity index 87% rename from assets/js/f0fff0af.e2ecea3b.js rename to assets/js/f0fff0af.e7ce2566.js index ab187e5e..6d9acc4b 100644 --- a/assets/js/f0fff0af.e2ecea3b.js +++ b/assets/js/f0fff0af.e7ce2566.js @@ -1 +1 @@ -"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1448],{3905:(e,t,r)=>{r.d(t,{Zo:()=>f,kt:()=>d});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var c=n.createContext({}),s=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},f=function(e){var t=s(e.components);return n.createElement(c.Provider,{value:t},e.children)},p="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,c=e.parentName,f=l(e,["components","mdxType","originalType","parentName"]),p=s(r),m=o,d=p["".concat(c,".").concat(m)]||p[m]||u[m]||a;return r?n.createElement(d,i(i({ref:t},f),{},{components:r})):n.createElement(d,i({ref:t},f))}));function d(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=m;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[p]="string"==typeof e?e:o,i[1]=l;for(var s=2;s{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>u,frontMatter:()=>a,metadata:()=>l,toc:()=>s});var n=r(7462),o=(r(7294),r(3905));const a={slug:"/file-formats/rsm",title:"RSM (Placeholder)"},i=void 0,l={unversionedId:"file-formats/RSM",id:"file-formats/RSM",title:"RSM (Placeholder)",description:"This section is a placeholder. If you know anything about the topic, please help fill it with content!",source:"@site/docs/file-formats/RSM.md",sourceDirName:"file-formats",slug:"/file-formats/rsm",permalink:"/file-formats/rsm",draft:!1,editUrl:"https://github.com/RagnarokResearchLab/ragnarokresearchlab.github.io/edit/main/docs/file-formats/RSM.md",tags:[],version:"current",frontMatter:{slug:"/file-formats/rsm",title:"RSM (Placeholder)"},sidebar:"tutorialSidebar",previous:{title:"RGZ (Placeholder)",permalink:"/file-formats/RGZ"},next:{title:"RSW",permalink:"/file-formats/rsw"}},c={},s=[],f={toc:s},p="wrapper";function u(e){let{components:t,...r}=e;return(0,o.kt)(p,(0,n.Z)({},f,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("admonition",{type:"info"},(0,o.kt)("p",{parentName:"admonition"},"This section is a placeholder. If you know anything about the topic, please help fill it with content!")))}u.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkwebsite=self.webpackChunkwebsite||[]).push([[1448],{3905:(e,t,r)=>{r.d(t,{Zo:()=>f,kt:()=>d});var n=r(7294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function i(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var c=n.createContext({}),s=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):i(i({},t),e)),r},f=function(e){var t=s(e.components);return n.createElement(c.Provider,{value:t},e.children)},p="mdxType",u={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},m=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,c=e.parentName,f=l(e,["components","mdxType","originalType","parentName"]),p=s(r),m=o,d=p["".concat(c,".").concat(m)]||p[m]||u[m]||a;return r?n.createElement(d,i(i({ref:t},f),{},{components:r})):n.createElement(d,i({ref:t},f))}));function d(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=m;var l={};for(var c in t)hasOwnProperty.call(t,c)&&(l[c]=t[c]);l.originalType=e,l[p]="string"==typeof e?e:o,i[1]=l;for(var s=2;s{r.r(t),r.d(t,{assets:()=>c,contentTitle:()=>i,default:()=>u,frontMatter:()=>a,metadata:()=>l,toc:()=>s});var n=r(7462),o=(r(7294),r(3905));const a={slug:"/file-formats/rsm",title:"RSM (Placeholder)"},i=void 0,l={unversionedId:"file-formats/RSM",id:"file-formats/RSM",title:"RSM (Placeholder)",description:"This section is a placeholder. If you know anything about the topic, please help fill it with content!",source:"@site/docs/file-formats/RSM.md",sourceDirName:"file-formats",slug:"/file-formats/rsm",permalink:"/file-formats/rsm",draft:!1,editUrl:"https://github.com/RagnarokResearchLab/ragnarokresearchlab.github.io/edit/main/docs/file-formats/RSM.md",tags:[],version:"current",frontMatter:{slug:"/file-formats/rsm",title:"RSM (Placeholder)"},sidebar:"tutorialSidebar",previous:{title:"RGZ",permalink:"/file-formats/RGZ"},next:{title:"RSW",permalink:"/file-formats/rsw"}},c={},s=[],f={toc:s},p="wrapper";function u(e){let{components:t,...r}=e;return(0,o.kt)(p,(0,n.Z)({},f,r,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("admonition",{type:"info"},(0,o.kt)("p",{parentName:"admonition"},"This section is a placeholder. If you know anything about the topic, please help fill it with content!")))}u.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.8273ce7e.js b/assets/js/runtime~main.8273ce7e.js new file mode 100644 index 00000000..bf6550a7 --- /dev/null +++ b/assets/js/runtime~main.8273ce7e.js @@ -0,0 +1 @@ +(()=>{"use strict";var e,a,t,r,c,f={},d={};function o(e){var a=d[e];if(void 0!==a)return a.exports;var t=d[e]={id:e,loaded:!1,exports:{}};return f[e].call(t.exports,t,t.exports,o),t.loaded=!0,t.exports}o.m=f,o.c=d,e=[],o.O=(a,t,r,c)=>{if(!t){var f=1/0;for(i=0;i=c)&&Object.keys(o.O).every((e=>o.O[e](t[b])))?t.splice(b--,1):(d=!1,c0&&e[i-1][2]>c;i--)e[i]=e[i-1];e[i]=[t,r,c]},o.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return o.d(a,{a:a}),a},t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,o.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var c=Object.create(null);o.r(c);var f={};a=a||[null,t({}),t([]),t(t)];for(var d=2&r&&e;"object"==typeof d&&!~a.indexOf(d);d=t(d))Object.getOwnPropertyNames(d).forEach((a=>f[a]=()=>e[a]));return f.default=()=>e,o.d(c,f),c},o.d=(e,a)=>{for(var t in a)o.o(a,t)&&!o.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:a[t]})},o.f={},o.e=e=>Promise.all(Object.keys(o.f).reduce(((a,t)=>(o.f[t](e,a),a)),[])),o.u=e=>"assets/js/"+({53:"935f2afb",996:"e1c03129",1081:"60e13883",1353:"cf29b846",1448:"f0fff0af",1505:"69ebbda8",1969:"78fc4a7a",2049:"32a2812a",2120:"58f7b674",2291:"e747ec83",2374:"d091b9a6",2932:"317f5b74",3085:"1f391b9e",4195:"c4f5d8e4",4372:"1989bbec",4474:"e73f6e0f",4542:"4126a5cc",5069:"e3d29adc",5170:"663c98fa",5247:"7b41f3e8",5338:"ea5d4418",5411:"ebc84ef1",5490:"0497a75c",5626:"70c5025a",5900:"3301c803",5973:"a0b55fd3",6028:"22c66187",6164:"928cb92e",6312:"19180482",6551:"8a0b7bf8",6608:"aa5fe62f",6631:"2b0271ae",6736:"69d7cf4d",6757:"bd16c6a0",6942:"a7dc6e90",6965:"29438bcf",7202:"8a7cccfd",7322:"c7978ab4",7577:"2e4fa626",7918:"17896441",8104:"511410cc",8854:"07b2f877",9295:"190fab30",9477:"88e5e359",9514:"1be78505",9913:"00567315"}[e]||e)+"."+{53:"df71aa47",996:"c88879c2",1081:"bad0481c",1353:"8cda554f",1448:"e7ce2566",1505:"6ce6952e",1969:"82aa56bb",2049:"c2852df7",2120:"fa4884dd",2291:"8746d86b",2374:"67a40d2f",2932:"beabd342",3085:"e1c3142b",4195:"4b6384ac",4372:"20016662",4474:"a67cd486",4542:"c5350f99",4972:"9374abde",5069:"7dea6e8f",5170:"9032eb69",5247:"dbf1a47b",5338:"7d23f667",5411:"79d8d12a",5490:"4433b125",5626:"a593c70b",5900:"2ebadc49",5973:"779080c1",6028:"3ae8bb26",6164:"f70dcf11",6312:"b22d5b37",6551:"6a9167cf",6608:"88a7a9e3",6631:"e14d5eff",6736:"23a75654",6757:"7d1543df",6942:"3e2b7808",6965:"2b0eb741",7202:"38251eaf",7322:"cccab4a3",7577:"781bf059",7918:"f66f3cb2",8104:"7b7dc6f3",8854:"8445beed",9295:"50df598b",9455:"089b11c6",9477:"d0822877",9514:"c11443eb",9913:"7dae607e"}[e]+".js",o.miniCssF=e=>{},o.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),o.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),r={},c="website:",o.l=(e,a,t,f)=>{if(r[e])r[e].push(a);else{var d,b;if(void 0!==t)for(var n=document.getElementsByTagName("script"),i=0;i{d.onerror=d.onload=null,clearTimeout(s);var c=r[e];if(delete r[e],d.parentNode&&d.parentNode.removeChild(d),c&&c.forEach((e=>e(t))),a)return a(t)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=l.bind(null,d.onerror),d.onload=l.bind(null,d.onload),b&&document.head.appendChild(d)}},o.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},o.p="/",o.gca=function(e){return e={17896441:"7918",19180482:"6312","935f2afb":"53",e1c03129:"996","60e13883":"1081",cf29b846:"1353",f0fff0af:"1448","69ebbda8":"1505","78fc4a7a":"1969","32a2812a":"2049","58f7b674":"2120",e747ec83:"2291",d091b9a6:"2374","317f5b74":"2932","1f391b9e":"3085",c4f5d8e4:"4195","1989bbec":"4372",e73f6e0f:"4474","4126a5cc":"4542",e3d29adc:"5069","663c98fa":"5170","7b41f3e8":"5247",ea5d4418:"5338",ebc84ef1:"5411","0497a75c":"5490","70c5025a":"5626","3301c803":"5900",a0b55fd3:"5973","22c66187":"6028","928cb92e":"6164","8a0b7bf8":"6551",aa5fe62f:"6608","2b0271ae":"6631","69d7cf4d":"6736",bd16c6a0:"6757",a7dc6e90:"6942","29438bcf":"6965","8a7cccfd":"7202",c7978ab4:"7322","2e4fa626":"7577","511410cc":"8104","07b2f877":"8854","190fab30":"9295","88e5e359":"9477","1be78505":"9514","00567315":"9913"}[e]||e,o.p+o.u(e)},(()=>{var e={1303:0,532:0};o.f.j=(a,t)=>{var r=o.o(e,a)?e[a]:void 0;if(0!==r)if(r)t.push(r[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var c=new Promise(((t,c)=>r=e[a]=[t,c]));t.push(r[2]=c);var f=o.p+o.u(a),d=new Error;o.l(f,(t=>{if(o.o(e,a)&&(0!==(r=e[a])&&(e[a]=void 0),r)){var c=t&&("load"===t.type?"missing":t.type),f=t&&t.target&&t.target.src;d.message="Loading chunk "+a+" failed.\n("+c+": "+f+")",d.name="ChunkLoadError",d.type=c,d.request=f,r[1](d)}}),"chunk-"+a,a)}},o.O.j=a=>0===e[a];var a=(a,t)=>{var r,c,f=t[0],d=t[1],b=t[2],n=0;if(f.some((a=>0!==e[a]))){for(r in d)o.o(d,r)&&(o.m[r]=d[r]);if(b)var i=b(o)}for(a&&a(t);n{"use strict";var e,a,t,r,f,c={},o={};function d(e){var a=o[e];if(void 0!==a)return a.exports;var t=o[e]={id:e,loaded:!1,exports:{}};return c[e].call(t.exports,t,t.exports,d),t.loaded=!0,t.exports}d.m=c,d.c=o,e=[],d.O=(a,t,r,f)=>{if(!t){var c=1/0;for(i=0;i=f)&&Object.keys(d.O).every((e=>d.O[e](t[b])))?t.splice(b--,1):(o=!1,f0&&e[i-1][2]>f;i--)e[i]=e[i-1];e[i]=[t,r,f]},d.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return d.d(a,{a:a}),a},t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,d.t=function(e,r){if(1&r&&(e=this(e)),8&r)return e;if("object"==typeof e&&e){if(4&r&&e.__esModule)return e;if(16&r&&"function"==typeof e.then)return e}var f=Object.create(null);d.r(f);var c={};a=a||[null,t({}),t([]),t(t)];for(var o=2&r&&e;"object"==typeof o&&!~a.indexOf(o);o=t(o))Object.getOwnPropertyNames(o).forEach((a=>c[a]=()=>e[a]));return c.default=()=>e,d.d(f,c),f},d.d=(e,a)=>{for(var t in a)d.o(a,t)&&!d.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:a[t]})},d.f={},d.e=e=>Promise.all(Object.keys(d.f).reduce(((a,t)=>(d.f[t](e,a),a)),[])),d.u=e=>"assets/js/"+({53:"935f2afb",996:"e1c03129",1081:"60e13883",1353:"cf29b846",1448:"f0fff0af",1505:"69ebbda8",1969:"78fc4a7a",2049:"32a2812a",2120:"58f7b674",2291:"e747ec83",2374:"d091b9a6",2932:"317f5b74",3085:"1f391b9e",4195:"c4f5d8e4",4372:"1989bbec",4474:"e73f6e0f",4542:"4126a5cc",5069:"e3d29adc",5170:"663c98fa",5247:"7b41f3e8",5338:"ea5d4418",5411:"ebc84ef1",5490:"0497a75c",5626:"70c5025a",5900:"3301c803",5973:"a0b55fd3",6028:"22c66187",6164:"928cb92e",6312:"19180482",6551:"8a0b7bf8",6608:"aa5fe62f",6631:"2b0271ae",6736:"69d7cf4d",6757:"bd16c6a0",6942:"a7dc6e90",6965:"29438bcf",7202:"8a7cccfd",7322:"c7978ab4",7577:"2e4fa626",7918:"17896441",8104:"511410cc",8854:"07b2f877",9295:"190fab30",9477:"88e5e359",9514:"1be78505",9913:"00567315"}[e]||e)+"."+{53:"f081478f",996:"9464e0ed",1081:"bad0481c",1353:"8cda554f",1448:"e2ecea3b",1505:"6ce6952e",1969:"82aa56bb",2049:"c2852df7",2120:"fa4884dd",2291:"8746d86b",2374:"67a40d2f",2932:"beabd342",3085:"e1c3142b",4195:"4b6384ac",4372:"20016662",4474:"a67cd486",4542:"c5350f99",4972:"9374abde",5069:"7dea6e8f",5170:"9032eb69",5247:"dbf1a47b",5338:"7d23f667",5411:"79d8d12a",5490:"4433b125",5626:"a593c70b",5900:"2ebadc49",5973:"779080c1",6028:"3ae8bb26",6164:"f70dcf11",6312:"b22d5b37",6551:"6a9167cf",6608:"88a7a9e3",6631:"e14d5eff",6736:"23a75654",6757:"7d1543df",6942:"3e2b7808",6965:"2b0eb741",7202:"38251eaf",7322:"cccab4a3",7577:"781bf059",7918:"f66f3cb2",8104:"7b7dc6f3",8854:"1089cba0",9295:"50df598b",9455:"089b11c6",9477:"d0822877",9514:"c11443eb",9913:"7dae607e"}[e]+".js",d.miniCssF=e=>{},d.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),d.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),r={},f="website:",d.l=(e,a,t,c)=>{if(r[e])r[e].push(a);else{var o,b;if(void 0!==t)for(var n=document.getElementsByTagName("script"),i=0;i{o.onerror=o.onload=null,clearTimeout(s);var f=r[e];if(delete r[e],o.parentNode&&o.parentNode.removeChild(o),f&&f.forEach((e=>e(t))),a)return a(t)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:o}),12e4);o.onerror=l.bind(null,o.onerror),o.onload=l.bind(null,o.onload),b&&document.head.appendChild(o)}},d.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},d.p="/",d.gca=function(e){return e={17896441:"7918",19180482:"6312","935f2afb":"53",e1c03129:"996","60e13883":"1081",cf29b846:"1353",f0fff0af:"1448","69ebbda8":"1505","78fc4a7a":"1969","32a2812a":"2049","58f7b674":"2120",e747ec83:"2291",d091b9a6:"2374","317f5b74":"2932","1f391b9e":"3085",c4f5d8e4:"4195","1989bbec":"4372",e73f6e0f:"4474","4126a5cc":"4542",e3d29adc:"5069","663c98fa":"5170","7b41f3e8":"5247",ea5d4418:"5338",ebc84ef1:"5411","0497a75c":"5490","70c5025a":"5626","3301c803":"5900",a0b55fd3:"5973","22c66187":"6028","928cb92e":"6164","8a0b7bf8":"6551",aa5fe62f:"6608","2b0271ae":"6631","69d7cf4d":"6736",bd16c6a0:"6757",a7dc6e90:"6942","29438bcf":"6965","8a7cccfd":"7202",c7978ab4:"7322","2e4fa626":"7577","511410cc":"8104","07b2f877":"8854","190fab30":"9295","88e5e359":"9477","1be78505":"9514","00567315":"9913"}[e]||e,d.p+d.u(e)},(()=>{var e={1303:0,532:0};d.f.j=(a,t)=>{var r=d.o(e,a)?e[a]:void 0;if(0!==r)if(r)t.push(r[2]);else if(/^(1303|532)$/.test(a))e[a]=0;else{var f=new Promise(((t,f)=>r=e[a]=[t,f]));t.push(r[2]=f);var c=d.p+d.u(a),o=new Error;d.l(c,(t=>{if(d.o(e,a)&&(0!==(r=e[a])&&(e[a]=void 0),r)){var f=t&&("load"===t.type?"missing":t.type),c=t&&t.target&&t.target.src;o.message="Loading chunk "+a+" failed.\n("+f+": "+c+")",o.name="ChunkLoadError",o.type=f,o.request=c,r[1](o)}}),"chunk-"+a,a)}},d.O.j=a=>0===e[a];var a=(a,t)=>{var r,f,c=t[0],o=t[1],b=t[2],n=0;if(c.some((a=>0!==e[a]))){for(r in o)d.o(o,r)&&(d.m[r]=o[r]);if(b)var i=b(d)}for(a&&a(t);n Community Projects | Ragnarok Research Lab - +

Community Projects

This page lists all of the known Ragnarok Online-related community projects that may be of interest to researchers.

A more comprehensive list of public repositories can be found here: Community Projects (Mirror)

Third-Party Clients

There have been countless attempts at creating a custom open-source Ragnarok Online client. In chronological order:

NameLanguageFirst Public ReleaseNotes
Shinryo's Custom ClientC++ (Ogre3D)---Announced here, but never released
curiosity's "Sakexe"C++---Announced here, but never released
OpenRagnarokC (SDL)Jul 11, 2008
Fimbulwinter ClientC++ (OpenGL)Sept 29, 2012Announced here
RagnarokJSJavaScript (Browser)Aug 21, 2013Announced here
roBrowserJavaScript (Browser)Oct 22, 2013Announced here; continues here (see this post)
DoloriC++ (OpenGL)Aug 15, 2017
AesirDDec 20, 2017Announced here
UnityROC# (Unity)Apr 24, 2018Continues here
RustarokRust (OpenGL)May 29, 2019Not actually a RO client, but related
RagnarokRebuildC# (Unity)Mar 2, 2020Announced on Twitter; continues here
Midgarts ClientGoNov 4, 2020Announced here and here
KorangarRust (Vulkan)Aug 5, 2021

There may be other client implementations that I've forgotten, that were lost to time, or simply never made public.

Server Emulators

Various server emulation efforts have resulted in many different forks and variations competing, with few survivors:

NameLanguageFirst Public ReleaseNotes
HerculesC/C++TBDOne of the two active mainstream emulators as of 2023
rAthenaC++TBDOne of the two active mainstream emulators as of 2023
RagnarokRebuild/TcpC# (Unity)Mar 2, 2020Only works with the corresponding, Unity-based client (?)
rust-roRustAug 22, 2021New and experimental emulator (status unclear?)

I'm not very familiar with the ancient history of server emulators. Feel free to suggest relevant projects that are worth adding!

Editing Tools

There are a large number of obsoleted and abandoned tools. This list contains a few of the more prominent ones:

NameLanguageFirst Public ReleaseNotes
GndEdit?TBDSource code unavailable (?)
BrowEditC++TBDDevelopment continues here
GRF EditorC#TBDSource code unavailable (?)
ACT EditorC#TBDSource code unavailable (?)
STR EditorC#TBDSource code unavailable (?)

There's lots of obscure tools that can be found all over the internet. If you know any that are noteworthy, please add them here!

Documentation

Comparatively few documentation efforts seem to have been undertaken by the RO community:

The above projects are mostly of historical interest at this point, since this website covers all the info that they provide.

- + \ No newline at end of file diff --git a/contents/index.html b/contents/index.html index 30267c1f..f2a957fa 100644 --- a/contents/index.html +++ b/contents/index.html @@ -4,13 +4,13 @@ Table of Contents | Ragnarok Research Lab - +

Table of Contents

Your goalWhere you should go
Understand what this website is about and why it existsAbout Page
Learn about various file formats used by the RO clientFile Formats
Gain a better idea of how the 3D world is renderedRendering
Discover how the game servers might work under the hoodGame Mechanics
Report an issue, give feedback, or ask questionsGitHub Issues or Discord
Delve into the fascinating history of RO's precursorArcturus
Contribute to the projectContribution Guide

Didn't find what you're looking for? Feel free to open an issue, or join the Discord server!

- + \ No newline at end of file diff --git a/contributing/index.html b/contributing/index.html index a9741ff9..04b318ad 100644 --- a/contributing/index.html +++ b/contributing/index.html @@ -4,13 +4,13 @@ Contributing | Ragnarok Research Lab - +

Contributing

On this page, you'll find all the information you need to make a successful contribution to this project.

Proposing Changes via GitHub

Simple edits can be submitted directly through GitHub's web interface. Click on the Edit this page link displayed at the bottom of every documentation page, then follow the instructions to create a Pull Request with your changes. You can preview the updated contents by switching modes in their editor, though it won't show you how the final result will look on the website.

Making Changes Locally

For more complex edits, it can be advantageous to run the documentation locally to make sure the result is as expected. This process requires familiarizing yourself with additional tools. You must also follow some guidelines regarding what contents can be published. Lastly, you may want to learn about technical writing to ensure the quality of the documentation remains high.

The rest of this page lists all relevant information, so that you can hopefully find your way around.

Technical Requirements

In order to make a more involved contribution, especially to the documentation website itself, you'll want a few things.

Git

You'll need a Git client, like Git for Windows. The easiest way to get started is probably using GitHub Desktop.

GitHub Account

In order to submit changes, you must have a GitHub account. If you've never contributed to open source projects, you may want to follow GitHub's Getting Started guide as well as their guide on how to use Pull Requests. Don't worry if you get something wrong, learning by doing is the best way; when in doubt, someone will be happy to help you in the Discord.

Markdown

The documentation is written in Markdown. If you've never used it, this guide should help you get started quickly.

React Components

Technically, you also can add more complex behavior using React components and JavaScript. But you probably won't need to.

Docusaurus

The Docusaurus framework creates a documentation website from markdown (and JavaScript) files inside the repository. For the most part, this can be ignored since the output format doesn't change if you only edit the contents. Should you want to make more sweeping changes that affect the layout of the website itself, you'll likely have to learn a bit more about it first.

NodeJS and the Node Package Manager

While not required to change the documentation, you may want to download NodeJS and the included npm tool. Once you have them installed, you can run any of the preconfigured NPM script commands in the project root to perform a variety of development tasks. If you're only interested in the contents and writing, you don't need to worry about this at all.

Content Guidelines

Unfortunately, some restrictions need to be applied to the content of this website to ensure it's maximally useful and long-lived.

Accuracy and Sourcing

Adding information that might be wrong, but seems like it has a decent chance of being true, is NOT a problem. In many cases, it will simply be too difficult (or even impossible) to ascertain the veracity of information due to lack of reliable sources.

However, in these cases a disclaimer should be added in the form of Docusaurus admonitions. This is how one would look:

caution

This section contains unverified information and/or speculation. It may or may not be completely wrong.

Beyond this, sources can be added in italics below block quotes. Here's an example for how this might look:

This is a piece of information that originates with a third-party, printed verbatim [or with minor, cosmetic adjustments].

Source: Completely made up, but you should include the actual source here. Don't forget to include the link if one is available.

The purpose of adding these notes is to make it easy to spot sections that could benefit from some more research.

Placeholders

While having empty sections in the documentation can be frustrating for the reader, there are situations where adding a placeholder is acceptable. Whenever you're aware of an important topic that should be covered, but currently isn't - for example because no one knows the details - you can add a placeholder notice via Docusaurus admonitions. Here's an example:

info

This section is a placeholder. If you know anything about the topic, please help fill it with content!

Please also open an issue if there isn't one, to create a reminder and aggregate information about the topic to be documented.

In order to ensure that the documentation doesn't overstep the legal boundaries imposed by the Digital Millennium Copyright Act and EU legislation, you must be mindful of what is submitted for publishing. The WINE Clean Room Guidelines should provide a decent starting point, but please do employ common sense. Obviously infringing contents will not be accepted.

Development

You may be confused when you first take a look at the repository's codebase. Thankfully, you only need to know a few things.

Organization of the Repository

Here's an overview of the most important folders you'll find in this repository:

  • .github/: Contains GitHub Actions workflows that run automatically when you submit a Pull Request
  • docs/: Contains the documentation pages, one category per folder and one markdown file per page
  • src/components/: React components that can be embedded anywhere on the website, even in markdown pages
  • src/css/: A set of Cascading Style Sheets files defining the visual theme of the documentation website
  • src/pages/: Dynamic pages that require more than just markdown and aren't part of the documentation itself
  • static/img/: Images that are not specific to any one documentation page, such as the website's logo
  • static/fonts/: All fonts that are references in the website's CSS, alongside their respective licenses

There's also files like the docusaurus configuration and NPM package definition, but you probably don't need to change them.

NPM Scripts

You can work with the website on your local computer by typing any of the following preconfigured script commands:

  • npm start: Start a local development web server and open the website in your browser (this may take a while)
  • npm run format-check: Check all contents to make sure they follow the formatting rules (does not alter the files)
  • npm run autoformat: Run the formatter on all files to apply formatting rules (does alter the files on disk!)
  • npm run linter: Run static analysis to check for style violations (only applies to code files, not documentation pages)

In addition to the above, all default Docusaurus commands should work, but you probably won't ever need them.

Writing

Writing excellent technical documentation is far easier said than done, so here's some pointers to make it more manageable.

Documentation Categories

For the most part, the contents of the documentation can be found in the top-level docs folder. Each section consists of a folder with a "table of contents" page, the index, and individual markdown pages for each article. The _category_.json file is a special Docusaurus construct that allows setting the title and location (i.e., where the category should be displayed in the sidebar).

Documentation Types

A useful model for separating documentation into is described here. The system works by sorting articles into one of four categories, which makes it easier to write targeted documentation that answers all the relevant questions. This project loosely follows the system, by dedicating each page to (mostly) a single type of documentation. This isn't a strict requirement currently.

Adherence to a specific style guide isn't currently mandatory, though it's recommended to follow Google's Developer Documentation Style Guide. Adoption of a consistent style is already planned for the future, though it's currently a lower priority than simply completing the documentation of all important topics and researching the many missing details.

Technical Writing Resources

If you want to learn more about technical writing, here's a few links that can help you improve:

Remember: It's better to write something than nothing and then iterate on it later. All contributions are welcome!

- + \ No newline at end of file diff --git a/file-formats/RGZ/index.html b/file-formats/RGZ/index.html index 12fbd2d7..b8c46254 100644 --- a/file-formats/RGZ/index.html +++ b/file-formats/RGZ/index.html @@ -3,14 +3,14 @@ -RGZ (Placeholder) | Ragnarok Research Lab - +RGZ | Ragnarok Research Lab + - +

RGZ

This document describes the RGZ file format used in the Ragnarok Online client.

Contents

RGZ files are gzip-compressed patch files used to update GRF archives.

Layout

Each RGZ archive contains an array of file and directory entries that should be applied to the GRF:

FieldOffsetLengthTypeDescription
EntryType01charDirectory entry ('d'), end-of-file symbol ('e'), or file entry ('f')
FileNameLength11uint8Length of the file name (string) that follows
FileName2variablestringNull-terminated, but at most FileNameLength bytes (with encoding: CP949)
FileSizevariablevariableuint32File entries only: Length of the file contents (blob) that follows
FileContentsvariablevariableblobOnly present for file entries; otherwise skip this field

This structure is repeated until the EOF entry 'e' with name "end" is encountered, which must always be the final entry.

See also

+ \ No newline at end of file diff --git a/file-formats/act/index.html b/file-formats/act/index.html index 863293d5..e946994f 100644 --- a/file-formats/act/index.html +++ b/file-formats/act/index.html @@ -4,13 +4,13 @@ ACT (Placeholder) | Ragnarok Research Lab - + - + + \ No newline at end of file diff --git a/file-formats/ebm/index.html b/file-formats/ebm/index.html index eca20e56..0dead4a4 100644 --- a/file-formats/ebm/index.html +++ b/file-formats/ebm/index.html @@ -4,13 +4,13 @@ EBM | Ragnarok Research Lab - +
-

EBM

This document describes the EBM file format used in the Ragnarok Online client.

Contents

EBM files contain compressed image data and are used exclusively to represent guild emblems.

Layout

The binary layout is irrelevant in practice: They're Bitmap (BMP) files compressed with the standard zlib DEFLATE algorithm.

If you're interested in learning how to programmatically process guild emblems, see Tools/ebm-export-with-zlib.

- +

EBM

This document describes the EBM file format used in the Ragnarok Online client.

Contents

EBM files contain compressed image data and are used exclusively to represent guild emblems.

Layout

The binary layout is irrelevant in practice: They're Bitmap (BMP) files compressed with the standard zlib DEFLATE algorithm.

If you're interested in learning how to programmatically process guild emblems, see Tools/ebm-export-with-zlib.

+ \ No newline at end of file diff --git a/file-formats/ezv/index.html b/file-formats/ezv/index.html index 68551cea..8d249b55 100644 --- a/file-formats/ezv/index.html +++ b/file-formats/ezv/index.html @@ -4,13 +4,13 @@ EZV (Placeholder) | Ragnarok Research Lab - + - + + \ No newline at end of file diff --git a/file-formats/gat/index.html b/file-formats/gat/index.html index b5a1785e..c2265f37 100644 --- a/file-formats/gat/index.html +++ b/file-formats/gat/index.html @@ -4,13 +4,13 @@ GAT | Ragnarok Research Lab - +
-

GAT

This document describes the GAT file format used in the Ragnarok Online client.

Contents

GAT files contain the following information:

  • Altitude data, encoded as height vectors, for each tile of a map
  • Terrain type flags to identify the terrain on a given tile

The file contents can be interpreted as a 2-dimensional navigation map, similar to 3D navigation meshes in concept.

Layout

Version 1.2

The vast majority of maps in the RO client use this version, including those found in alpha clients (and even Arcturus)1.

FieldOffsetLengthTypeDescription
Header04string"GRAT" as an ASCII-encoded, fixed-size string
MajorVersion41byteVersioning information
MinorVersion51byteVersioning information
Width64intThe horizontal size of the map, given in tiles
Height104intThe vertical size of the map, given in tiles
Tiles14+20arrayContains the navigation properties for Width * Height tiles

1 Technically, old versions have a zero-byte prefix shifting the layout. It's still GAT 1.2 otherwise.

Tile Properties

Each entry in the Tiles array represents a surface block ("tile") of the given map's terrain, and is structured as follows:

FieldOffsetLengthTypeDescription
SouthWestAltitude04floatAltitude at the bottom left corner, i.e., at (0, 0) relative coordinates
SouthEastAltitude44floatAltitude at the bottom right corner, i.e., at (1, 0) relative coordinates
NorthWestAltitude84floatAltitude at the top left corner, i.e., at (0, 1) relative coordinates
NorthEastAltitude124floatAltitude at the top right corner, i.e., at (1, 1) relative coordinates
TerrainType164uintIdentifier for the tile's terrain type

Terrain Types

info

More research is needed. If you know anything about the topic, please help fill in the blanks!

The following terrain types are known to have practical applications:

ValueWalkableInterpretation
0YESRegular (walkable) terrain
1NOObstructed (impassable) terrain
5NOImpassable snipeable terrain ("cliff")

There are several other terrain types, but it's unclear whether they affect clientside collision checks at all.

Version 1.3

info

More research is needed. If you know anything about the topic, please help fill in the blanks!

This version adds a twist to the TerrainType values: A special flag marks water tiles, though the layout itself hasn't changed. Effectively, two flags are seemingly embedded in the existing terrain type field. Consider the following example:

  • A water tile might use a TerrainType of 1 (obstructed), which in GAT 1.2 would be encoded as 0x01000000 (LE int, value is 1)
  • In GAT 1.3, the sameTerrainType property would however be encoded as 0x01000080 (LE int, value is now -2147483647)
  • The IsWaterTile flag can then be extracted by masking the TerrainType, leaving only the 0x08 component (binary: 0000 1000)

What exactly should be done with that information is unclear, but all water tiles in GAT 1.3 files have been assigned such a flag.

References

- +

GAT

This document describes the GAT file format used in the Ragnarok Online client.

Contents

GAT files contain the following information:

  • Altitude data, encoded as height vectors, for each tile of a map
  • Terrain type flags to identify the terrain on a given tile

The file contents can be interpreted as a 2-dimensional navigation map, similar to 3D navigation meshes in concept.

Layout

Version 1.2

The vast majority of maps in the RO client use this version, including those found in alpha clients (and even Arcturus)1.

FieldOffsetLengthTypeDescription
Header04string"GRAT" as an ASCII-encoded, fixed-size string
MajorVersion41byteVersioning information
MinorVersion51byteVersioning information
Width64intThe horizontal size of the map, given in tiles
Height104intThe vertical size of the map, given in tiles
Tiles14+20arrayContains the navigation properties for Width * Height tiles

1 Technically, old versions have a zero-byte prefix shifting the layout. It's still GAT 1.2 otherwise.

Tile Properties

Each entry in the Tiles array represents a surface block ("tile") of the given map's terrain, and is structured as follows:

FieldOffsetLengthTypeDescription
SouthWestAltitude04floatAltitude at the bottom left corner, i.e., at (0, 0) relative coordinates
SouthEastAltitude44floatAltitude at the bottom right corner, i.e., at (1, 0) relative coordinates
NorthWestAltitude84floatAltitude at the top left corner, i.e., at (0, 1) relative coordinates
NorthEastAltitude124floatAltitude at the top right corner, i.e., at (1, 1) relative coordinates
TerrainType164uintIdentifier for the tile's terrain type

Terrain Types

info

More research is needed. If you know anything about the topic, please help fill in the blanks!

The following terrain types are known to have practical applications:

ValueWalkableInterpretation
0YESRegular (walkable) terrain
1NOObstructed (impassable) terrain
5NOImpassable snipeable terrain ("cliff")

There are several other terrain types, but it's unclear whether they affect clientside collision checks at all.

Version 1.3

info

More research is needed. If you know anything about the topic, please help fill in the blanks!

This version adds a twist to the TerrainType values: A special flag marks water tiles, though the layout itself hasn't changed. Effectively, two flags are seemingly embedded in the existing terrain type field. Consider the following example:

  • A water tile might use a TerrainType of 1 (obstructed), which in GAT 1.2 would be encoded as 0x01000000 (LE int, value is 1)
  • In GAT 1.3, the sameTerrainType property would however be encoded as 0x01000080 (LE int, value is now -2147483647)
  • The IsWaterTile flag can then be extracted by masking the TerrainType, leaving only the 0x08 component (binary: 0000 1000)

What exactly should be done with that information is unclear, but all water tiles in GAT 1.3 files have been assigned such a flag.

References

+ \ No newline at end of file diff --git a/file-formats/gnd/index.html b/file-formats/gnd/index.html index eefd532e..36406a4d 100644 --- a/file-formats/gnd/index.html +++ b/file-formats/gnd/index.html @@ -4,13 +4,13 @@ GND | Ragnarok Research Lab - +
-

GND

This document describes the GND file format used in the Ragnarok Online client.

Contents

GND files contain the following information:

  • The entirety of a map's surface geometry (i.e., the ground/static terrain)
  • A list of diffuse textures and texture coordinates for mapping them to the terrain
  • Vertex colors and precomputed lightmap textures to add lights and shadows
  • In later versions they can also include the configuration of the water surfaces, which were previously stored in RSW files

The file contents effectively represent the basic terrain for a given map, without 3D models, sprites, or other decoration.

Layout

Arcturus & iRO Alpha

Unversioned GND files were already used in Arcturus. They also appear in the iRO alpha client, alongside more recent versions.

FieldOffsetLengthTypeDescription
TextureCount04intNumber of diffuse texture paths
Width44intWidth of the cube grid
Height84intHeight of the cube grid
TexturePaths1280arrayTextureCount entries; all paths are null-terminated C strings
GroundMeshCubesvariable28arrayWidth * Height entries

Cube Grid

Compared to later versions of the format, the indirection from cubes to textured surfaces doesn't yet exist:

FieldOffsetLengthTypeDescription
UpwardsFacingTexture04intTexture ID of the upwards-facing GROUND surface
NorthernWallTexture44intTexture ID of the northwards-facing WALL surface
EasternWallTexture84intTexture ID of the eastwards-facing WALL surface
BottomLeftHeight124floatAltitude of the bottom left corner (in world units)
BottomRightHeight164floatAltitude of the bottom right corner (in world units)
TopLeftHeight204floatAltitude of the top left corner (in world units)
TopRightHeight244floatAltitude of the top right corner (in world units)
VertexColor284intARGB vertex color (stored as BGRA) for the bottom left vertex
TBD324intPurpose unknown, but its value seems to always be 1 (?)
TextureCoordinates3632floatDiffuse texture UVs (uses the same format as later versions)

The format used to store texture coordinates seems to be identical to the one used in GND 1.7 and later.

Version 1.5

Certain maps in the iRO alpha client use GND 1.5. These are much closer to the version used in the modern RO client.

The format is identical to GND 1.7, except that the lightmap format is stored globally instead of on a per-slice basis:

Version 1.6

Some unverified information is available for this version, but there aren't any known files that actually use it.

Version 1.7

This is the original version used for most maps in the modern RO client:

FieldOffsetLengthTypeDescription
Header04string"GRGN" as an ASCII-encoded, fixed-size string
MajorVersion41byteVersioning information
MinorVersion51byteVersioning information
Width64intWidth of the cube grid
Height104intHeight of the cube grid
Scale144floatGeometry scale factor (always 10)
TextureCount164intNumber of diffuse texture paths
TexturePathLength204intLength of each texture path string, in bytes (always 80)
TexturePaths24+80stringNull-terminated (discard garbage bytes at the end)
LightmapSliceCountvariable4intNumber of lightmap (and ambient occlusion) texture slices
LightmapSlicesvariable268structAmbient occlusion and lightmap texture bitmaps (alternating)
SurfaceCountvariable4intNumber of textured surface blueprints
SurfaceDefinitionsvariable56arrayShared texturing information (copy to each cube vertex)
GroundMeshCubesvariable28arrayWidth * Height entries

Lightmap Slices

The lightmap and ambient occlusion textures are split into small bitmaps:

FieldOffsetLengthTypeDescription and notes
PixelFormat44intEncoding of the lightmap and ambient occlusion pixels (usually 11)
Width84intWidth of each texture bitmap (always 8)
Height124intHeight of teach texture bitmap (always 8)
ShadowmapPixels1664arrayWidth * Height ambient occlusion texture pixels (intensity values)
LightmapPixels16192arrayWidth * Height lightmap texture pixels (specularity values)

1 This value appears to be ignored; the actual pixel format should always be 8-bit RGBA (stored as ARGB).

Textured Surfaces

These can be used to construct the terrain geometry (vertex data):

FieldOffsetLengthTypeDescription and notes
TextureCoordinates032arrayDiffuse texture UVs (array of float values, see below)
Texture322shortID of the diffuse texture to apply to this surface (-1 means "none")
LightmapSlice362ushortID of the lightmap (and ambient occlusion) slice to apply to the surface
VertexColor4016arrayBGRA-ordered color (byte values) of the surface's bottom left vertex

Texture Coordinates

Texture coordinates are stored in the following order:

  1. bottomLeftU
  2. bottomRightU
  3. topLeftU
  4. topRightU
  5. bottomLeftV
  6. bottomRightV
  7. topLeftV
  8. topRightV

The direction is always relative to the cube that the surface belongs to, i.e., bottomLeft is south-west and topRight is north-east.

Cube Grid

FieldOffsetLengthTypeDescription
BottomLeftHeight04floatAltitude of the bottom left corner (in world units)
BottomRightHeight44floatAltitude of the bottom right corner (in world units)
TopLeftHeight84floatAltitude of the top left corner (in world units)
TopRightHeight124floatAltitude of the top right corner (in world units)
UpwardsFacingSurface164intUpwards-facing surface ID
NorthernWallSurface204intSurface ID of the northwards-facing wall
EasternWallSurface244intSurface ID of the eastwards-facing wall

Version 1.8

The water plane configuration that was previously part of the RSW file has been moved to the GND file instead.

All the information stored in version 1.7 is still present (and unchanged), plus the water plane configuration (appended).

Water Plane Configuration

In theory, this GND version supports multiple water planes. But in practice, all maps that use it only feature a single one.

FieldOffsetLengthTypeDescription
WaterLevel04floatHeight of the default water plane (there's always at least one)
WaterType44intTexture ID that's applied to the water plane (tiled)
WaveHeight84floatAmplitude of the water plane's animation curve
WaveSpeed124floatPhase of the water plane's animation curve
WavePitch164floatSurface curvature in degrees (origin of the curve)
TextureCyclingInterval204intAfter how many frames the next texture should be swapped in

This is the basic water configuration, as present in both RSW (pre-2.6) and the next GND version.

The configuration for the other planes follows after the above, and is (in this version) always of the following form:

  1. NumWaterPlanesU (int value): How many water planes there are in the horizontal dimension (always 1)
  2. NumWaterPlanesV (int value): How many water planes there are in the vertical dimension (always 1)
  3. A float defining the WaterLevel (altitude) of this plane. It's always identical to the water level above, since there's only one.

If there were multiple values here, it would likely be the WateLlevel of each plane (assuming the presence of u * v planes), but since that doesn't happen (and there's already a new GND version available) it seems unlikely that this would ever be used.

Version 1.9

All the information stored in version 1.7, plus the water plane configuration (appended). The latter consists of the same information as is present in 1.8, but the format for defining multiple water planes has changed (and they are in fact used in some of the episode 19 maps I've seen, such as icecastle.gnd):

  1. NumWaterPlanesU (int value): How many water planes there are in the horizontal dimension (NOT always 1)
  2. NumWaterPlanesV (int value): How many water planes there are in the vertical dimension (NOT always 1)
  3. The same structure as defined for the original water plane (see table above), repeated u * v times

I'm not quite sure what the original configuration is still used for, but I'm guessing it would serve as a fallback in case the numbers (u, v) are both zero (i.e., there aren't any water planes defined in this section). More research is needed.

References

- +

GND

This document describes the GND file format used in the Ragnarok Online client.

Contents

GND files contain the following information:

  • The entirety of a map's surface geometry (i.e., the ground/static terrain)
  • A list of diffuse textures and texture coordinates for mapping them to the terrain
  • Vertex colors and precomputed lightmap textures to add lights and shadows
  • In later versions they can also include the configuration of the water surfaces, which were previously stored in RSW files

The file contents effectively represent the basic terrain for a given map, without 3D models, sprites, or other decoration.

Layout

Arcturus & iRO Alpha

Unversioned GND files were already used in Arcturus. They also appear in the iRO alpha client, alongside more recent versions.

FieldOffsetLengthTypeDescription
TextureCount04intNumber of diffuse texture paths
Width44intWidth of the cube grid
Height84intHeight of the cube grid
TexturePaths1280arrayTextureCount entries; all paths are null-terminated C strings
GroundMeshCubesvariable28arrayWidth * Height entries

Cube Grid

Compared to later versions of the format, the indirection from cubes to textured surfaces doesn't yet exist:

FieldOffsetLengthTypeDescription
UpwardsFacingTexture04intTexture ID of the upwards-facing GROUND surface
NorthernWallTexture44intTexture ID of the northwards-facing WALL surface
EasternWallTexture84intTexture ID of the eastwards-facing WALL surface
BottomLeftHeight124floatAltitude of the bottom left corner (in world units)
BottomRightHeight164floatAltitude of the bottom right corner (in world units)
TopLeftHeight204floatAltitude of the top left corner (in world units)
TopRightHeight244floatAltitude of the top right corner (in world units)
VertexColor284intARGB vertex color (stored as BGRA) for the bottom left vertex
TBD324intPurpose unknown, but its value seems to always be 1 (?)
TextureCoordinates3632floatDiffuse texture UVs (uses the same format as later versions)

The format used to store texture coordinates seems to be identical to the one used in GND 1.7 and later.

Version 1.5

Certain maps in the iRO alpha client use GND 1.5. These are much closer to the version used in the modern RO client.

The format is identical to GND 1.7, except that the lightmap format is stored globally instead of on a per-slice basis:

Version 1.6

Some unverified information is available for this version, but there aren't any known files that actually use it.

Version 1.7

This is the original version used for most maps in the modern RO client:

FieldOffsetLengthTypeDescription
Header04string"GRGN" as an ASCII-encoded, fixed-size string
MajorVersion41byteVersioning information
MinorVersion51byteVersioning information
Width64intWidth of the cube grid
Height104intHeight of the cube grid
Scale144floatGeometry scale factor (always 10)
TextureCount164intNumber of diffuse texture paths
TexturePathLength204intLength of each texture path string, in bytes (always 80)
TexturePaths24+80stringNull-terminated (discard garbage bytes at the end)
LightmapSliceCountvariable4intNumber of lightmap (and ambient occlusion) texture slices
LightmapSlicesvariable268structAmbient occlusion and lightmap texture bitmaps (alternating)
SurfaceCountvariable4intNumber of textured surface blueprints
SurfaceDefinitionsvariable56arrayShared texturing information (copy to each cube vertex)
GroundMeshCubesvariable28arrayWidth * Height entries

Lightmap Slices

The lightmap and ambient occlusion textures are split into small bitmaps:

FieldOffsetLengthTypeDescription and notes
PixelFormat44intEncoding of the lightmap and ambient occlusion pixels (usually 11)
Width84intWidth of each texture bitmap (always 8)
Height124intHeight of teach texture bitmap (always 8)
ShadowmapPixels1664arrayWidth * Height ambient occlusion texture pixels (intensity values)
LightmapPixels16192arrayWidth * Height lightmap texture pixels (specularity values)

1 This value appears to be ignored; the actual pixel format should always be 8-bit RGBA (stored as ARGB).

Textured Surfaces

These can be used to construct the terrain geometry (vertex data):

FieldOffsetLengthTypeDescription and notes
TextureCoordinates032arrayDiffuse texture UVs (array of float values, see below)
Texture322shortID of the diffuse texture to apply to this surface (-1 means "none")
LightmapSlice362ushortID of the lightmap (and ambient occlusion) slice to apply to the surface
VertexColor4016arrayBGRA-ordered color (byte values) of the surface's bottom left vertex

Texture Coordinates

Texture coordinates are stored in the following order:

  1. bottomLeftU
  2. bottomRightU
  3. topLeftU
  4. topRightU
  5. bottomLeftV
  6. bottomRightV
  7. topLeftV
  8. topRightV

The direction is always relative to the cube that the surface belongs to, i.e., bottomLeft is south-west and topRight is north-east.

Cube Grid

FieldOffsetLengthTypeDescription
BottomLeftHeight04floatAltitude of the bottom left corner (in world units)
BottomRightHeight44floatAltitude of the bottom right corner (in world units)
TopLeftHeight84floatAltitude of the top left corner (in world units)
TopRightHeight124floatAltitude of the top right corner (in world units)
UpwardsFacingSurface164intUpwards-facing surface ID
NorthernWallSurface204intSurface ID of the northwards-facing wall
EasternWallSurface244intSurface ID of the eastwards-facing wall

Version 1.8

The water plane configuration that was previously part of the RSW file has been moved to the GND file instead.

All the information stored in version 1.7 is still present (and unchanged), plus the water plane configuration (appended).

Water Plane Configuration

In theory, this GND version supports multiple water planes. But in practice, all maps that use it only feature a single one.

FieldOffsetLengthTypeDescription
WaterLevel04floatHeight of the default water plane (there's always at least one)
WaterType44intTexture ID that's applied to the water plane (tiled)
WaveHeight84floatAmplitude of the water plane's animation curve
WaveSpeed124floatPhase of the water plane's animation curve
WavePitch164floatSurface curvature in degrees (origin of the curve)
TextureCyclingInterval204intAfter how many frames the next texture should be swapped in

This is the basic water configuration, as present in both RSW (pre-2.6) and the next GND version.

The configuration for the other planes follows after the above, and is (in this version) always of the following form:

  1. NumWaterPlanesU (int value): How many water planes there are in the horizontal dimension (always 1)
  2. NumWaterPlanesV (int value): How many water planes there are in the vertical dimension (always 1)
  3. A float defining the WaterLevel (altitude) of this plane. It's always identical to the water level above, since there's only one.

If there were multiple values here, it would likely be the WateLlevel of each plane (assuming the presence of u * v planes), but since that doesn't happen (and there's already a new GND version available) it seems unlikely that this would ever be used.

Version 1.9

All the information stored in version 1.7, plus the water plane configuration (appended). The latter consists of the same information as is present in 1.8, but the format for defining multiple water planes has changed (and they are in fact used in some of the episode 19 maps I've seen, such as icecastle.gnd):

  1. NumWaterPlanesU (int value): How many water planes there are in the horizontal dimension (NOT always 1)
  2. NumWaterPlanesV (int value): How many water planes there are in the vertical dimension (NOT always 1)
  3. The same structure as defined for the original water plane (see table above), repeated u * v times

I'm not quite sure what the original configuration is still used for, but I'm guessing it would serve as a fallback in case the numbers (u, v) are both zero (i.e., there aren't any water planes defined in this section). More research is needed.

References

+ \ No newline at end of file diff --git a/file-formats/gr2/index.html b/file-formats/gr2/index.html index 1c77e27b..275f6f41 100644 --- a/file-formats/gr2/index.html +++ b/file-formats/gr2/index.html @@ -4,14 +4,14 @@ GR2 | Ragnarok Research Lab - +
-

GR2

This document describes the GR2 file format used in the Ragnarok Online client.

Contents

GR2 files are binary data stores created by the proprietary Granny3D SDK. They contain the following:

  • 3D mesh geometry and textures for the WoE guardians, emperium, treasure box, and guild flag
  • Skeletons and pose animations for the above models (in separate files, one per model and pose)
  • Some leftover (unfinished) data for a potential 3D Novice-class player character

Although the format is widely used in the games industry, only the specific version used in RO is referenced here.

Layout

The layout is quite complex and has been described elsewhere:

  • A great description is found at the OpenGR2 wiki, which contains many details worth reading
  • The previous version of this documentation is still available (warning: may contain inaccuracies and errors)
  • Alternate implementations exist on GitHub, several of which are listed in the above document

As of 2023, there appears to be no third-party implementation that doesn't rely on the granny2.dll in at least some way.

Oodle Compression

This is just a quick correction to the original article, which mentions the use of Huffman tables.

According to this blog comment by Fabian Giesen (of RAD), Oodle-0 is actually +

GR2

This document describes the GR2 file format used in the Ragnarok Online client.

Contents

GR2 files are binary data stores created by the proprietary Granny3D SDK. They contain the following:

  • 3D mesh geometry and textures for the WoE guardians, emperium, treasure box, and guild flag
  • Skeletons and pose animations for the above models (in separate files, one per model and pose)
  • Some leftover (unfinished) data for a potential 3D Novice-class player character

Although the format is widely used in the games industry, only the specific version used in RO is referenced here.

Layout

The layout is quite complex and has been described elsewhere:

  • A great description is found at the OpenGR2 wiki, which contains many details worth reading
  • The previous version of this documentation is still available (warning: may contain inaccuracies and errors)
  • Alternate implementations exist on GitHub, several of which are listed in the above document

As of 2023, there appears to be no third-party implementation that doesn't rely on the granny2.dll in at least some way.

Oodle Compression

This is just a quick correction to the original article, which mentions the use of Huffman tables.

According to this blog comment by Fabian Giesen (of RAD), Oodle-0 is actually using a standard Lempel-Ziv-77 compressor combined with a (presumably adaptive) arithmetic encoder. Indeed it appears that the compressed GR2 segments are fed into the AE decoder and then the LZ decompressor, though the specifics of how the arithmetic coding works aren't exactly clear.

Data Segments

All GR2 files are split into multiple segments, which can be individually compressed. Cursory data analysis yields:

  • The first segments is always large (and often the largest), presumably containing the bulk of the data tree
  • Segments two, three, four, and five likely contain the geometry as they're absent from RO skeletons
  • Segments two and three are always used together; the same goes for four and five (hinting at related data)
  • Segment two is always larger than three, and segment four is always larger than five - which hints at vertices/indices
  • A reasonable guess is that those are just vertex positions, indices, normals, etc., split according to statistical properties
  • The last segment is large and always uncompressed, probably containing the (already-compressed) textures

It's been reported that other games may use more segments (unverified claim), but in RO there are always six.

Data Tracks

Oodle-0 compressed segments consist of multiple data tracks, which are defined by the start and end offset, as well as certain compressor settings. The range (from and to) is defined in the segment header, while compression parameters and inputs for the arithmetic decoder can be found at the start of each compressed segment. More research is needed on these parameters.

See also

Here's a few resources that might help with understanding the Oodle decompression:

- + \ No newline at end of file diff --git a/file-formats/grf/index.html b/file-formats/grf/index.html index a8b9f272..27a902ac 100644 --- a/file-formats/grf/index.html +++ b/file-formats/grf/index.html @@ -4,13 +4,13 @@ GRF | Ragnarok Research Lab - +
-

GRF

This document describes the GRF file format used in the Ragnarok Online client.

Contents

GRF files are virtual file system containers not unlike ZIP archives. The main features are:

  • By streaming game assets more efficiently they may help reduce loading times
  • Optional support for multiple data compression and encryption schemes
  • Compressing files greatly reduces the amount of storage space on disk

The GRF format is closely related to PAK files used in Arcturus, which are an earlier implementation of the same concept.

Layout

Arcturus (PAK)

The PAK file format used in Arcturus is nearly identical to early versions of the GRF format.

FieldOffsetLengthTypeDescription
StorageBytes0variableblobThe compressed file records (contents of the archive)
FileRecordsvariablevariablearrayA list of file records describing the compressed byte storage
Footervariable4ArchiveMetadata"Header" containing the location and size of the file records list

Due to the large size, this structure shouldn't be read into memory. It's listed purely for illustration purposes.

Archive Metadata

The metadata required for reading file records, located at the end of the file (at offset EOF - sizeof(ArchiveMetadata)).

FieldOffsetLengthTypeDescription
StartOffset04uintRelative offset (from the start of the file) of the first record
NumRecords44uintHow many records there are in this archive
VersionTag81byteAlways 0x12 (decimal: 18)

File Records

Each record describes where in the archive a given file can be found, and what options to pass to the decompressor.

FieldOffsetLengthTypeDescription
PathStringSize01byteSize of the entry's path name (excluding the null terminator)
Type01byte1 for "file" and 2 for "directory" (?) - needs more research
Offset04intRelative offset (from the start of the archive)
CompressedSize04intSize of the compressed buffer area for this file
DecompressedSize04intSize of the entry after decompressing the buffer
FilePath0variablestringNull-terminated C string of size PathStringSize + 1

Version 1.2

This version has been used in at least the fRO client.

info

This section is a placeholder. If you know anything about the topic, please help fill it with content!

More research is needed on this topic.

Version 1.3

This version has been used in at least the 2003 iRO Beta client.

info

This section is a placeholder. If you know anything about the topic, please help fill it with content!

More research is needed on this topic.

Version 2.0

This is the GRF version used in the latest kRO client, as well as regional clients dating back to at least 2004.

FieldOffsetLengthTypeDescription
Signature015stringAlways "Master of Magic" (fixed-size, without a null-terminator)
EncryptionKey1515stringArray of byte values; all zeroes if encryption isn't used (default)
FileTableOffset304uintWhere the file table is stored (relative to the start of the file)
ScramblingSeed344uintSeemingly used for obfuscating the contents of the archive
ScrambledFileCount384uintSubtract ScramblingSeed and 7 to compute the real FileCount
Version424uintEncodes the version in major.minor format (two bytes each)

File Table

This is the manifest of the archive, stored at an arbitrary position in the file (defined by FileTableOffset).

FieldOffsetLengthTypeDescription
CompressedSize04uintCompressed size of the file table (list of file entries)
DecompressedSize44uintSize of the file table after decompressing it
CompressedRecordHeaders8variableblobCompressed buffer (use zlib's inflate to decompress)

These parameters all have to be passed to the decompressor in order to unpack the list of file records.

File Entries

After unpacking the CompressedRecordHeaders buffer, you'll get a list of these file records ("headers").

FieldOffsetLengthTypeDescription
CompressedSize04uintHow large the compressed file contents are (zlib parameter)
ByteAlignedSize44uintHow many bytes to actually read (padded to next 8-byte boundary)
DecompressedSize84uintSize of the actual file contents (another zlib parameter)
Type121byteWhether the file is stored raw, compressed, or encrypted (?)
Offset134uintWhere in the archive the compressed file contents can be found

You can pass the compressed/aligned buffer to zlib's inflate to unpack a given file.

References

- +

GRF

This document describes the GRF file format used in the Ragnarok Online client.

Contents

GRF files are virtual file system containers not unlike ZIP archives. The main features are:

  • By streaming game assets more efficiently they may help reduce loading times
  • Optional support for multiple data compression and encryption schemes
  • Compressing files greatly reduces the amount of storage space on disk

The GRF format is closely related to PAK files used in Arcturus, which are an earlier implementation of the same concept.

Layout

Arcturus (PAK)

The PAK file format used in Arcturus is nearly identical to early versions of the GRF format.

FieldOffsetLengthTypeDescription
StorageBytes0variableblobThe compressed file records (contents of the archive)
FileRecordsvariablevariablearrayA list of file records describing the compressed byte storage
Footervariable4ArchiveMetadata"Header" containing the location and size of the file records list

Due to the large size, this structure shouldn't be read into memory. It's listed purely for illustration purposes.

Archive Metadata

The metadata required for reading file records, located at the end of the file (at offset EOF - sizeof(ArchiveMetadata)).

FieldOffsetLengthTypeDescription
StartOffset04uintRelative offset (from the start of the file) of the first record
NumRecords44uintHow many records there are in this archive
VersionTag81byteAlways 0x12 (decimal: 18)

File Records

Each record describes where in the archive a given file can be found, and what options to pass to the decompressor.

FieldOffsetLengthTypeDescription
PathStringSize01byteSize of the entry's path name (excluding the null terminator)
Type01byte1 for "file" and 2 for "directory" (?) - needs more research
Offset04intRelative offset (from the start of the archive)
CompressedSize04intSize of the compressed buffer area for this file
DecompressedSize04intSize of the entry after decompressing the buffer
FilePath0variablestringNull-terminated C string of size PathStringSize + 1

Version 1.2

This version has been used in at least the fRO client.

info

This section is a placeholder. If you know anything about the topic, please help fill it with content!

More research is needed on this topic.

Version 1.3

This version has been used in at least the 2003 iRO Beta client.

info

This section is a placeholder. If you know anything about the topic, please help fill it with content!

More research is needed on this topic.

Version 2.0

This is the GRF version used in the latest kRO client, as well as regional clients dating back to at least 2004.

FieldOffsetLengthTypeDescription
Signature015stringAlways "Master of Magic" (fixed-size, without a null-terminator)
EncryptionKey1515stringArray of byte values; all zeroes if encryption isn't used (default)
FileTableOffset304uintWhere the file table is stored (relative to the start of the file)
ScramblingSeed344uintSeemingly used for obfuscating the contents of the archive
ScrambledFileCount384uintSubtract ScramblingSeed and 7 to compute the real FileCount
Version424uintEncodes the version in major.minor format (two bytes each)

File Table

This is the manifest of the archive, stored at an arbitrary position in the file (defined by FileTableOffset).

FieldOffsetLengthTypeDescription
CompressedSize04uintCompressed size of the file table (list of file entries)
DecompressedSize44uintSize of the file table after decompressing it
CompressedRecordHeaders8variableblobCompressed buffer (use zlib's inflate to decompress)

These parameters all have to be passed to the decompressor in order to unpack the list of file records.

File Entries

After unpacking the CompressedRecordHeaders buffer, you'll get a list of these file records ("headers").

FieldOffsetLengthTypeDescription
CompressedSize04uintHow large the compressed file contents are (zlib parameter)
ByteAlignedSize44uintHow many bytes to actually read (padded to next 8-byte boundary)
DecompressedSize84uintSize of the actual file contents (another zlib parameter)
Type121byteWhether the file is stored raw, compressed, or encrypted (?)
Offset134uintWhere in the archive the compressed file contents can be found

You can pass the compressed/aligned buffer to zlib's inflate to unpack a given file.

References

+ \ No newline at end of file diff --git a/file-formats/imf/index.html b/file-formats/imf/index.html index 784ccec9..253b8d76 100644 --- a/file-formats/imf/index.html +++ b/file-formats/imf/index.html @@ -4,13 +4,13 @@ IMF (Placeholder) | Ragnarok Research Lab - + - +
+ \ No newline at end of file diff --git a/file-formats/index.html b/file-formats/index.html index 58ce1a15..076b7f83 100644 --- a/file-formats/index.html +++ b/file-formats/index.html @@ -4,13 +4,13 @@ Overview | Ragnarok Research Lab - +
-

Overview

In this category, you will find a description of the custom file formats that are used in the Ragnarok Online client.

Target Audience

Due to the technical nature of the subject matter, this specification is written under the assumption that you're a programmer or at least have some programming experience. The information provided here will hopefully be interesting to anyone familiar with the game, but if you're not on good terms with basic programming concepts a bit of a learning curve should be expected.

Objectives

After studying this documentation, you should know the answers to the following questions:

  • What types of files exist in the Ragnarok Online client?
  • What information do they contain and how is it structured?
  • How can the file contents be decoded from their binary format into a raw ("in-memory") representation?

Needless to say, all information is provided for educational purposes only.

Limitations

This section doesn't cover the rather complex topic of rendering the game world. To learn how the decoded information from within the game's resource files can be transformed into a visual representation, matching what players expect when they think about "the game", see Rendering. For information about the relationship between RO and Arcturus, browse this category.

Prerequisites

You might want to read up on some of the fundamentals of binary encodings, computer graphics, and game development to make best use of this resource. Here's some external links that might help you get started if you aren't already familiar:

You absolutely should have a solid grasp of binary types if you wish to learn about the relatively complex formats that RO uses.

Units and Data Types

This specification follows the following conventions:

  • All offsets and field lengths are given in bytes, unless otherwise denoted
  • All numbers are assumed to be stored in "reversed" byte order (little-endian)
  • Boolean values are interpreted as FALSE if zero, and TRUE otherwise

Here's a list of the atomic data types that you may encounter in the layout tables:

TypeSizeDescription
boolean1A single byte that is exclusively used to store a boolean flag (FALSE or TRUE)
byte18-bit unsigned integer that is used to encode numbers rather than characters
ushort216-bit unsigned integer
short216-bit signed integer (in Two's Complement)
float4A 32-bit single-precision floating point number, in IEEE 754 format
int432-bit signed integer (in Two's Complement)
uint432-bit unsigned integer
char1An ASCII-encoded fixed-size character of unit length
stringvariableFixed-size, null-terminated, or counted string (as noted in the field's description)
structvariableBinary structure of arbitrary size, with a unique layout (will generally be listed separately)
blobvariableOpaque binary structure of arbitrary size (processed using third-party libraries)
arrayvariableFixed-size array of structures or values; the exact size depends and may only be implicit

Please note that in many cases the exact type is unknown and can only be guessed, based on examples found "in the wild".

- +

Overview

In this category, you will find a description of the custom file formats that are used in the Ragnarok Online client.

Target Audience

Due to the technical nature of the subject matter, this specification is written under the assumption that you're a programmer or at least have some programming experience. The information provided here will hopefully be interesting to anyone familiar with the game, but if you're not on good terms with basic programming concepts a bit of a learning curve should be expected.

Objectives

After studying this documentation, you should know the answers to the following questions:

  • What types of files exist in the Ragnarok Online client?
  • What information do they contain and how is it structured?
  • How can the file contents be decoded from their binary format into a raw ("in-memory") representation?

Needless to say, all information is provided for educational purposes only.

Limitations

This section doesn't cover the rather complex topic of rendering the game world. To learn how the decoded information from within the game's resource files can be transformed into a visual representation, matching what players expect when they think about "the game", see Rendering. For information about the relationship between RO and Arcturus, browse this category.

Prerequisites

You might want to read up on some of the fundamentals of binary encodings, computer graphics, and game development to make best use of this resource. Here's some external links that might help you get started if you aren't already familiar:

You absolutely should have a solid grasp of binary types if you wish to learn about the relatively complex formats that RO uses.

Units and Data Types

This specification follows the following conventions:

  • All offsets and field lengths are given in bytes, unless otherwise denoted
  • All numbers are assumed to be stored in "reversed" byte order (little-endian)
  • Boolean values are interpreted as FALSE if zero, and TRUE otherwise

Here's a list of the atomic data types that you may encounter in the layout tables:

TypeSizeDescription
boolean1A single byte that is exclusively used to store a boolean flag (FALSE or TRUE)
byte18-bit unsigned integer that is used to encode numbers rather than characters
ushort216-bit unsigned integer
short216-bit signed integer (in Two's Complement)
float4A 32-bit single-precision floating point number, in IEEE 754 format
int432-bit signed integer (in Two's Complement)
uint432-bit unsigned integer
char1An ASCII-encoded fixed-size character of unit length
stringvariableFixed-size, null-terminated, or counted string (as noted in the field's description)
structvariableBinary structure of arbitrary size, with a unique layout (will generally be listed separately)
blobvariableOpaque binary structure of arbitrary size (processed using third-party libraries)
arrayvariableFixed-size array of structures or values; the exact size depends and may only be implicit

Please note that in many cases the exact type is unknown and can only be guessed, based on examples found "in the wild".

+ \ No newline at end of file diff --git a/file-formats/ini/index.html b/file-formats/ini/index.html index 6061b7e9..8a45fd1e 100644 --- a/file-formats/ini/index.html +++ b/file-formats/ini/index.html @@ -4,13 +4,13 @@ INI (Placeholder) | Ragnarok Research Lab - + - + \ No newline at end of file diff --git a/file-formats/lub/index.html b/file-formats/lub/index.html index a35c4f5d..b5662758 100644 --- a/file-formats/lub/index.html +++ b/file-formats/lub/index.html @@ -4,13 +4,13 @@ LUB (Placeholder) | Ragnarok Research Lab - + - +
+ \ No newline at end of file diff --git a/file-formats/pal/index.html b/file-formats/pal/index.html index 23fac5a7..f3bc88dd 100644 --- a/file-formats/pal/index.html +++ b/file-formats/pal/index.html @@ -4,13 +4,13 @@ PAL | Ragnarok Research Lab - +
-

PAL

This document describes the PAL file format used in the Ragnarok Online client.

Contents

PAL files are alternative color palettes used to tint sprites. To be specific:

  • An array of 256 8-bit RGBA color values is appended at the end of any (modern) SPR sprite - the "default palette"
  • All standalone PAL files found in the game client are using exactly the same format, which makes it easy to recolor sprites
  • The client can swap any one of them in to replace the default palette, with certain palettes matching specific sprites

All the same limitations apply to the default and alternative palettes - that is to say, transparency isn't supported.

Alpha Channel

Transparency is determined for each color according to its RGBA values, after loading the palette:

  • The color stored at palette index 0 is considered the transparent background color - usually red, green, or magenta
  • Colors that are "close" to the transparency color might also be rendered transparent (warning: unverified claim)
  • If a palette contains alpha values, they appear to be ignored in favor of the actual transparency color

The exact behavior requires some verification still, but a possible approach was described here (might be incorrect, though).

Layout

See the ColorPalette field in the SPR layout table. Any SPR file above version 1.1 includes a default palette (i.e., all of them do).

- +

PAL

This document describes the PAL file format used in the Ragnarok Online client.

Contents

PAL files are alternative color palettes used to tint sprites. To be specific:

  • An array of 256 8-bit RGBA color values is appended at the end of any (modern) SPR sprite - the "default palette"
  • All standalone PAL files found in the game client are using exactly the same format, which makes it easy to recolor sprites
  • The client can swap any one of them in to replace the default palette, with certain palettes matching specific sprites

All the same limitations apply to the default and alternative palettes - that is to say, transparency isn't supported.

Alpha Channel

Transparency is determined for each color according to its RGBA values, after loading the palette:

  • The color stored at palette index 0 is considered the transparent background color - usually red, green, or magenta
  • Colors that are "close" to the transparency color might also be rendered transparent (warning: unverified claim)
  • If a palette contains alpha values, they appear to be ignored in favor of the actual transparency color

The exact behavior requires some verification still, but a possible approach was described here (might be incorrect, though).

Layout

See the ColorPalette field in the SPR layout table. Any SPR file above version 1.1 includes a default palette (i.e., all of them do).

+ \ No newline at end of file diff --git a/file-formats/rma/index.html b/file-formats/rma/index.html index ebf8c0af..cefa378e 100644 --- a/file-formats/rma/index.html +++ b/file-formats/rma/index.html @@ -4,13 +4,13 @@ RMA (Placeholder) | Ragnarok Research Lab - + - + \ No newline at end of file diff --git a/file-formats/rsa/index.html b/file-formats/rsa/index.html index 7d59c6d8..3f73006a 100644 --- a/file-formats/rsa/index.html +++ b/file-formats/rsa/index.html @@ -4,13 +4,13 @@ RSA (Placeholder) | Ragnarok Research Lab - + - + \ No newline at end of file diff --git a/file-formats/rsm/index.html b/file-formats/rsm/index.html index d2d4594e..bddabc07 100644 --- a/file-formats/rsm/index.html +++ b/file-formats/rsm/index.html @@ -4,13 +4,13 @@ RSM (Placeholder) | Ragnarok Research Lab - + - +
+ \ No newline at end of file diff --git a/file-formats/rsw/index.html b/file-formats/rsw/index.html index 064f97e0..4379d68a 100644 --- a/file-formats/rsw/index.html +++ b/file-formats/rsw/index.html @@ -4,13 +4,13 @@ RSW | Ragnarok Research Lab - +
-

RSW

This document describes the RSW file format used in the Ragnarok Online client.

Contents

RSW files contain the following information:

  • References to other files required to render the map (mainly GAT and GND; the others are only used in Arcturus)
  • The configuration of scene-wide light sources, as well as decorative game objects that should be placed in the scene
  • In earlier versions they also include a definition of the water plane, which is later stored in the referenced GND file instead
  • A scene graph-like representation of the object hierarchy (tree of bounding boxes), used for intersection queries

RSW is essentially the "scene definition" format used by the game.

Layout

Newer RSW files may include a third version segment, resulting in a Major.Minor.BuildNumber versioning scheme.

Version 1.9

An older version that's virtually identical to 2.1, but there's no QuadTree and audio sources don't have a CycleInterval.

Version 2.1

This is the original version used for most maps in the original RO client:

FieldOffsetLengthTypeDescription
Signature04string"GRSW" as an ASCII-encoded, fixed-size string
MajorVersion41uint8Versioning information
MinorVersion51uint8Versioning information
WaterConfiguration624WaterPlaneConfiguration of the map's primary water plane (only one)
LightingParameters3036LightParamsConfiguration of the map's global (fixed-function) light sources
MapBoundaries6616BoundingBoxA bounding box containing the entire scene (?)
SceneObjectsCount824intThe number of (ObjectTypeID, SceneObject) pairs to be read
SceneObjects86variablearrayScene objects of varying types (see tables below)
QuadTreevariable64kQuadTreeRangeScene graph data structure, for culling invisible objects

Scene objects are effectively a union type structure, the contents of which depend on the object type (first struct member).

Water Plane Configuration

This structure is exactly the same as in GND 1.8. Only one water plane is supported in this version, however.

Lighting Parameters

These define the settings that should be applied to the global light sources, which exist in every scene:

FieldOffsetLengthTypeDescription
Longitude04uint32Spherical longitude of the directional light source (in degrees)
Latitude44uint32Spherical latitude of the directional light source (in degrees)
DiffuseRed84floatDiffuse red component of the directional light source
DiffuseGreen124floatDiffuse green component of the directional light source
DiffuseBlue164floatDiffuse blue component of the directional light source
AmbientRed204floatDiffuse red component of the ambient light source
AmbientGreen244floatDiffuse green component of the ambient light source
AmbientBlue284floatDiffuse blue component of the ambient light source
ShadowmapAlpha324floatOpacity of the shadowmap (for baking GND lightmap textures)

Bounding Box

This was claimed to define the outside bounds of the visible game world, though the data doesn't really make sense:

FieldOffsetLengthTypeDescription
Top04uint32Purpose unknown; appears to be unused (?)
Bottom44uint32Purpose unknown; appears to be unused (?)
Left84uint32Purpose unknown; appears to be unused (?)
Right124uint32Purpose unknown; appears to be unused (?)

More research is needed to determine the meaning of this structure. Might be for internal tools, or used in Arcturus?

Scene Objects

There are multiple types of decorative scene objects, which are differentiated by a type identifier (first value):

FieldOffsetLengthTypeDescription
ObjectTypeID04intOne of the supported scene object type identifiers
SceneObject4variablestructLayout depends on the object type

The following object types may be used:

enum {
SCENE_OBJECT_TYPE_ANIMATED_PROP = 1,
SCENE_OBJECT_TYPE_DYNAMIC_LIGHT_SOURCE = 2,
SCENE_OBJECT_TYPE_SPATIAL_AUDIO_SOURCE = 3,
SCENE_OBJECT_TYPE_PARTICLE_EFFECT_EMITTER = 4,
}
3D Models (Animated Props)

Instances of a given RSM model can be placed in the scene and configured according to these parameters:

FieldOffsetLengthTypeDescription
Name040stringName of this model instance
AnimationTypeID404int32Defines the looping behavior of the model's animation
AnimationSpeed444floatAnimation speed modifier (percentage)
CollisionFlags484int32Zero means the object is "solid" (affected by collision checks?)
ModelFile5280stringName of the RSM file to instantiate
NodeName13280stringName of the RSM root node to render (?)
PositionX2124floatX coordinate of the instance's world position (translation)
PositionY2164floatY coordinate of the instance's world position (translation)
PositionZ2204floatZ coordinate of the instance's world position (translation)
RotationX2244floatX rotation applied to the model instance
RotationY2284floatY rotation applied to the model instance
RotationZ2324floatZ rotation applied to the model instance
ScaleX2364floatX scale factor applied to the model instance
ScaleY2404floatY scale factor applied to the model instance
ScaleZ2444floatZ scale factor applied to the model instance
Dynamic Light Sources

Dynamic lights aren't actually rendered by the client, as their output is baked into the lightmaps stored in the map's GND file:

FieldOffsetLengthTypeDescription
Name080stringNull-terminated (discard garbage bytes at the end)
PositionX804floatX coordinate of the light's world position
PositionY844floatY coordinate of the light's world position
PositionZ884floatZ coordinate of the light's world position
DiffuseRed924floatRed component of the light's diffuse color
DiffuseGreen964floatGreen component of the light's diffuse color
DiffuseBlue1004floatBlue component of the light's diffuse color
Range1044floatLight intensity (falloff range), given in world units
Spatialized 3D Audio Sources

Ambient sound effects (like frogs or flowing water) are emitted by these invisible game objects:

FieldOffsetLengthTypeDescription
Name080stringNull-terminated (discard garbage bytes at the end)
SoundFile8080stringNull-terminated (discard garbage bytes at the end)
PositionX1604floatX coordinate of the emitter's world position
PositionY1644floatY coordinate of the emitter's world position
PositionZ1684floatZ coordinate of the emitter's world position
VolumeGain1724floatPlayback volume, given as a percentage in the range of 0 to 1
Width1764uintThe width of the area that emits sound (used for attenuation?)
Height1804uintThe height of the area that emits sound (used for attenuation?)
Range1844floatAttenuation range (how far sound can be heard), in world units
CycleInterval1884floatDuration of the individual (looping) playback cycles, given in seconds

When CycleInterval is not present (i.e., in older RSW versions), it defaults to a duration of 4 seconds.

Particle Effect Emitters

Particle effects such as bats, smoke, or clouds, can be configured from a number of preset effects:

FieldOffsetLengthTypeDescription
Name080stringNull-terminated (discard garbage bytes at the end)
PositionX804floatX coordinate of the emitter's world position
PositionY844floatY coordinate of the emitter's world position
PositionZ884floatZ coordinate of the emitter's world position
PresetEffectID924uintDetermines which preconfigured effect should be used (see this list)
EmissionDelay964floatHow fast new particles may spawn (in frames; 60 means 1 second)
LaunchParameterA1004floatConfigures the preset effect (meaning depends on PresetEffectID)
LaunchParameterB1044floatConfigures the preset effect (meaning depends on PresetEffectID)
LaunchParameterC1084floatConfigures the preset effect (meaning depends on PresetEffectID)
LaunchParameterD1124floatConfigures the preset effect (meaning depends on PresetEffectID)

How launch parameters are used needs to be determined on a per-effect basis. Each emitter will process them differently.

Quad Tree

The layout for this data structure corresponds to a fixed-size quad tree with 5 levels, each consisting of four sub-ranges:

FieldOffsetLengthTypeDescription
BottomLeftQuadrant048QuadTreeRangeVolume for the area south-west of the previous level's center
BottomRightQuadrant4848QuadTreeRangeVolume for the area south-east of the previous level's center
TopLeftQuadrant9648QuadTreeRangeVolume for the area north-west of the previous level's center
TopRightQuadrant14448QuadTreeRangeVolume for the area north-east of the previous level's center

Quadrants always exist for each of the upper levels of the hiararchy, but are NULL if it's a leaf node (sub-tree at level 5).

Quad Tree Range

Each range defines an axis-aligned bounding box that can be used to store all visible elements in this part of the scene:

FieldOffsetLengthTypeDescription
BottomX04floatX coordinate of the corner at the lowest altitude
BottomY44floatY coordinate of the corner at the lowest altitude
BottomZ84floatZ coordinate of the corner at the lowest altitude
TopX124floatX coordinate of the corner at the highest altitude
TopY164floatY coordinate of the corner at the highest altitude
TopZ204floatZ coordinate of the corner at the highest altitude
DiameterX244floatWidth of the bounding box (when interpreted as a cuboid)
DiameterY284floatHeight of the bounding box (when interpreted as a cuboid)
DiameterZ324floatDepth of the bounding box (when interpreted as a cuboid)
CenterX364floatX coordinate of the point in the exact center of the box
CenterY404floatY coordinate of the point in the exact center of the box
CenterZ444floatZ coordinate of the point in the exact center of the box

Version 2.2

Changes the file header slightly, adding a BuildNumber (in this version, a uint8 value) after MinorVersion.

Version 2.3

No superficial changes to the layout have been observed. More research is needed.

Version 2.4

No superficial changes to the layout have been observed. More research is needed.

Version 2.5

The BuildNumber is now a uint32 value. An additional uint8, tentatively called UnknownRenderFlag, appears immediately afterward.

Version 2.6

The water plan setup was moved to the GND file. While the BuildNumber is 161 or lower, no other changes have been observed.

Version 2.6.162

In files using a BuildNumber of 162 or higher, the layout for animated props (RSM models) changes slightly:

An unknown byte (seemingly a uint8 value, possibly a boolean flag) has been added after the CollisionFlags field.

- +

RSW

This document describes the RSW file format used in the Ragnarok Online client.

Contents

RSW files contain the following information:

  • References to other files required to render the map (mainly GAT and GND; the others are only used in Arcturus)
  • The configuration of scene-wide light sources, as well as decorative game objects that should be placed in the scene
  • In earlier versions they also include a definition of the water plane, which is later stored in the referenced GND file instead
  • A scene graph-like representation of the object hierarchy (tree of bounding boxes), used for intersection queries

RSW is essentially the "scene definition" format used by the game.

Layout

Newer RSW files may include a third version segment, resulting in a Major.Minor.BuildNumber versioning scheme.

Version 1.9

An older version that's virtually identical to 2.1, but there's no QuadTree and audio sources don't have a CycleInterval.

Version 2.1

This is the original version used for most maps in the original RO client:

FieldOffsetLengthTypeDescription
Signature04string"GRSW" as an ASCII-encoded, fixed-size string
MajorVersion41uint8Versioning information
MinorVersion51uint8Versioning information
WaterConfiguration624WaterPlaneConfiguration of the map's primary water plane (only one)
LightingParameters3036LightParamsConfiguration of the map's global (fixed-function) light sources
MapBoundaries6616BoundingBoxA bounding box containing the entire scene (?)
SceneObjectsCount824intThe number of (ObjectTypeID, SceneObject) pairs to be read
SceneObjects86variablearrayScene objects of varying types (see tables below)
QuadTreevariable64kQuadTreeRangeScene graph data structure, for culling invisible objects

Scene objects are effectively a union type structure, the contents of which depend on the object type (first struct member).

Water Plane Configuration

This structure is exactly the same as in GND 1.8. Only one water plane is supported in this version, however.

Lighting Parameters

These define the settings that should be applied to the global light sources, which exist in every scene:

FieldOffsetLengthTypeDescription
Longitude04uint32Spherical longitude of the directional light source (in degrees)
Latitude44uint32Spherical latitude of the directional light source (in degrees)
DiffuseRed84floatDiffuse red component of the directional light source
DiffuseGreen124floatDiffuse green component of the directional light source
DiffuseBlue164floatDiffuse blue component of the directional light source
AmbientRed204floatDiffuse red component of the ambient light source
AmbientGreen244floatDiffuse green component of the ambient light source
AmbientBlue284floatDiffuse blue component of the ambient light source
ShadowmapAlpha324floatOpacity of the shadowmap (for baking GND lightmap textures)

Bounding Box

This was claimed to define the outside bounds of the visible game world, though the data doesn't really make sense:

FieldOffsetLengthTypeDescription
Top04uint32Purpose unknown; appears to be unused (?)
Bottom44uint32Purpose unknown; appears to be unused (?)
Left84uint32Purpose unknown; appears to be unused (?)
Right124uint32Purpose unknown; appears to be unused (?)

More research is needed to determine the meaning of this structure. Might be for internal tools, or used in Arcturus?

Scene Objects

There are multiple types of decorative scene objects, which are differentiated by a type identifier (first value):

FieldOffsetLengthTypeDescription
ObjectTypeID04intOne of the supported scene object type identifiers
SceneObject4variablestructLayout depends on the object type

The following object types may be used:

enum {
SCENE_OBJECT_TYPE_ANIMATED_PROP = 1,
SCENE_OBJECT_TYPE_DYNAMIC_LIGHT_SOURCE = 2,
SCENE_OBJECT_TYPE_SPATIAL_AUDIO_SOURCE = 3,
SCENE_OBJECT_TYPE_PARTICLE_EFFECT_EMITTER = 4,
}
3D Models (Animated Props)

Instances of a given RSM model can be placed in the scene and configured according to these parameters:

FieldOffsetLengthTypeDescription
Name040stringName of this model instance
AnimationTypeID404int32Defines the looping behavior of the model's animation
AnimationSpeed444floatAnimation speed modifier (percentage)
CollisionFlags484int32Zero means the object is "solid" (affected by collision checks?)
ModelFile5280stringName of the RSM file to instantiate
NodeName13280stringName of the RSM root node to render (?)
PositionX2124floatX coordinate of the instance's world position (translation)
PositionY2164floatY coordinate of the instance's world position (translation)
PositionZ2204floatZ coordinate of the instance's world position (translation)
RotationX2244floatX rotation applied to the model instance
RotationY2284floatY rotation applied to the model instance
RotationZ2324floatZ rotation applied to the model instance
ScaleX2364floatX scale factor applied to the model instance
ScaleY2404floatY scale factor applied to the model instance
ScaleZ2444floatZ scale factor applied to the model instance
Dynamic Light Sources

Dynamic lights aren't actually rendered by the client, as their output is baked into the lightmaps stored in the map's GND file:

FieldOffsetLengthTypeDescription
Name080stringNull-terminated (discard garbage bytes at the end)
PositionX804floatX coordinate of the light's world position
PositionY844floatY coordinate of the light's world position
PositionZ884floatZ coordinate of the light's world position
DiffuseRed924floatRed component of the light's diffuse color
DiffuseGreen964floatGreen component of the light's diffuse color
DiffuseBlue1004floatBlue component of the light's diffuse color
Range1044floatLight intensity (falloff range), given in world units
Spatialized 3D Audio Sources

Ambient sound effects (like frogs or flowing water) are emitted by these invisible game objects:

FieldOffsetLengthTypeDescription
Name080stringNull-terminated (discard garbage bytes at the end)
SoundFile8080stringNull-terminated (discard garbage bytes at the end)
PositionX1604floatX coordinate of the emitter's world position
PositionY1644floatY coordinate of the emitter's world position
PositionZ1684floatZ coordinate of the emitter's world position
VolumeGain1724floatPlayback volume, given as a percentage in the range of 0 to 1
Width1764uintThe width of the area that emits sound (used for attenuation?)
Height1804uintThe height of the area that emits sound (used for attenuation?)
Range1844floatAttenuation range (how far sound can be heard), in world units
CycleInterval1884floatDuration of the individual (looping) playback cycles, given in seconds

When CycleInterval is not present (i.e., in older RSW versions), it defaults to a duration of 4 seconds.

Particle Effect Emitters

Particle effects such as bats, smoke, or clouds, can be configured from a number of preset effects:

FieldOffsetLengthTypeDescription
Name080stringNull-terminated (discard garbage bytes at the end)
PositionX804floatX coordinate of the emitter's world position
PositionY844floatY coordinate of the emitter's world position
PositionZ884floatZ coordinate of the emitter's world position
PresetEffectID924uintDetermines which preconfigured effect should be used (see this list)
EmissionDelay964floatHow fast new particles may spawn (in frames; 60 means 1 second)
LaunchParameterA1004floatConfigures the preset effect (meaning depends on PresetEffectID)
LaunchParameterB1044floatConfigures the preset effect (meaning depends on PresetEffectID)
LaunchParameterC1084floatConfigures the preset effect (meaning depends on PresetEffectID)
LaunchParameterD1124floatConfigures the preset effect (meaning depends on PresetEffectID)

How launch parameters are used needs to be determined on a per-effect basis. Each emitter will process them differently.

Quad Tree

The layout for this data structure corresponds to a fixed-size quad tree with 5 levels, each consisting of four sub-ranges:

FieldOffsetLengthTypeDescription
BottomLeftQuadrant048QuadTreeRangeVolume for the area south-west of the previous level's center
BottomRightQuadrant4848QuadTreeRangeVolume for the area south-east of the previous level's center
TopLeftQuadrant9648QuadTreeRangeVolume for the area north-west of the previous level's center
TopRightQuadrant14448QuadTreeRangeVolume for the area north-east of the previous level's center

Quadrants always exist for each of the upper levels of the hiararchy, but are NULL if it's a leaf node (sub-tree at level 5).

Quad Tree Range

Each range defines an axis-aligned bounding box that can be used to store all visible elements in this part of the scene:

FieldOffsetLengthTypeDescription
BottomX04floatX coordinate of the corner at the lowest altitude
BottomY44floatY coordinate of the corner at the lowest altitude
BottomZ84floatZ coordinate of the corner at the lowest altitude
TopX124floatX coordinate of the corner at the highest altitude
TopY164floatY coordinate of the corner at the highest altitude
TopZ204floatZ coordinate of the corner at the highest altitude
DiameterX244floatWidth of the bounding box (when interpreted as a cuboid)
DiameterY284floatHeight of the bounding box (when interpreted as a cuboid)
DiameterZ324floatDepth of the bounding box (when interpreted as a cuboid)
CenterX364floatX coordinate of the point in the exact center of the box
CenterY404floatY coordinate of the point in the exact center of the box
CenterZ444floatZ coordinate of the point in the exact center of the box

Version 2.2

Changes the file header slightly, adding a BuildNumber (in this version, a uint8 value) after MinorVersion.

Version 2.3

No superficial changes to the layout have been observed. More research is needed.

Version 2.4

No superficial changes to the layout have been observed. More research is needed.

Version 2.5

The BuildNumber is now a uint32 value. An additional uint8, tentatively called UnknownRenderFlag, appears immediately afterward.

Version 2.6

The water plan setup was moved to the GND file. While the BuildNumber is 161 or lower, no other changes have been observed.

Version 2.6.162

In files using a BuildNumber of 162 or higher, the layout for animated props (RSM models) changes slightly:

An unknown byte (seemingly a uint8 value, possibly a boolean flag) has been added after the CollisionFlags field.

+ \ No newline at end of file diff --git a/file-formats/rsx/index.html b/file-formats/rsx/index.html index fc86ab2c..e1ec4abb 100644 --- a/file-formats/rsx/index.html +++ b/file-formats/rsx/index.html @@ -4,13 +4,13 @@ RSX (Placeholder) | Ragnarok Research Lab - + - + \ No newline at end of file diff --git a/file-formats/scr/index.html b/file-formats/scr/index.html index f3359395..ef7d08a0 100644 --- a/file-formats/scr/index.html +++ b/file-formats/scr/index.html @@ -4,13 +4,13 @@ SCR (Placeholder) | Ragnarok Research Lab - + - + \ No newline at end of file diff --git a/file-formats/spr/index.html b/file-formats/spr/index.html index 54688baa..137632b2 100644 --- a/file-formats/spr/index.html +++ b/file-formats/spr/index.html @@ -4,13 +4,13 @@ SPR | Ragnarok Research Lab - +
-

SPR

This document describes the SPR file format used in the Ragnarok Online client (and Arcturus).

Contents

SPR files contain the following information:

  • Sprites, icons, and other graphics rendered by the game (either inside the world or as part of the UI)
  • Indexed-color bitmaps and their respective color palettes, here called the "BMP segment"
  • Truecolor images with transparency values for each pixel, here called the "TGA segment"

The file contents can be interpreted as a spritesheet and combined with ACT files to implement animated sprites.

Features

Bitmap Color Palette

Instead of encoding the RGBA pixels directly, the BMP segment of the file uses a color palette with up to 256 colors:

  • In the image data, each byte corresponds to an entry in this lookup table
  • Entry 0 is always the (transparent) background color, regardless of its alpha value
  • Even if present, alpha values are otherwise not supported and should be ignored

The palette included in the SPR file is the default palette. It's possible to load in PAL files to recolor a given sprite.

Run-Length Encoding

Because most sprites include a significant number of (transparent) background pixels, later versions of the format employ a primitive compression scheme to reduce the number of identical palette indices that have to be stored in the BMP segment.

The method is chiefly known as RLE because it works by encoding runs (here: of zero bytes, which refer to the palette index 0 - the transparent background color) into a two-byte shorthand: First, a zero-byte that indicates the start of a run, and then the number of encoded zero bytes. Decompression simply requires inserting the same number of zeroes into the output stream.

If that sounds confusing, here's some examples:

  • If the image contained five background pixels, you'll see 00 05, which stands for "emit zero, five times"
  • The bytes "ABC" (65 66 67) are "decompressed" to the same sequence as there's no encoded run present
  • A single zero byte must be encoded as 00 01 ("emit zero, one time") - meaning that 00 00 is not allowed

This feature isn't used for the truecolor (TGA) segments as pixels are stored in ABGR format, in which repetition is less common.

Transparency

Alpha values in the BMP palette are completely ignored. RGBA colors will be stored raw and not premultiplied.

The color with palette index 0 should be considered the "background color". It must be cleared manually, though.

Layout

Version 1.1

This most basic variant only supports BMP sprites. It's used in Arcturus, but not in any known version of the RO client.

FieldOffsetLengthTypeDescription
Header02string"SP" as an ASCII-encoded, fixed-size string
MinorVersion21byteVersioning information
MajorVersion31byteVersioning information
BitmapImageCount42ushortHow many images are stored in the BMP segment
BitmapSprites6variablearrayIndexed-color sprite images; pixels are stored as palette indices
ColorPalettevariable1024arrayRGBA pixel colors (byte values); always stored at the end of the file

Bitmap Sprites

This repeating structure contains the raw pixel data in the form of references to palette entries that need to be resolved.

FieldOffsetLengthTypeDescription
ImageWidth02shortThe width of the sprite image, given in pixels
ImageHeight22shortThe height of the sprite image, given in pixels
PaletteIndices4variablearrayArray of indexed-color palette indices (byte values)

A size of -1, -1 (FF FF FF FF) indicates an invalid image, which consists of a single pixel that can (probably) be discarded.

Version 2.0

This version adds the ability to store truecolor images (RGBA pixels with an alpha channel). It appears in RO and Arcturus.

FieldOffsetLengthTypeDescription
Header02string"SP" as an ASCII-encoded, fixed-size string
MinorVersion21byteVersioning information
MajorVersion31byteVersioning information
BitmapImageCount42ushortHow many images are stored in the BMP segment
TruecolorImageCount62ushortHow many images are stored in the TGA segment
BitmapSprites8variablearrayIndexed-color sprite images; pixels are stored as palette indices
TruecolorSpritesvariablevariablearrayTruecolor sprite images; pixels are stored raw (in order ABGR)
ColorPalettevariable1024arrayRGBA pixel colors (byte values); always stored at the end of the file

Truecolor Sprites

This repeating structure contains the raw pixel data in an uncompressed form.

FieldOffsetLengthTypeDescription
ImageWidth02shortThe width of the sprite image, given in pixels
ImageHeight24shortThe height of the sprite image, given in pixels
PixelBuffer6variablearrayArray of raw ABGR image data (byte values)

Version 2.1

This version adds compression (via RLE) for images in the BMP segment of the file. It's used in all modern RO clients.

Compressed Bitmap Sprites

The only difference here is that runs of zero palette indices (i.e., multiple background pixels) are RLE-compressed.

FieldOffsetLengthTypeDescription
ImageWidth02shortThe width of the sprite image, given in pixels
ImageHeight22shortThe height of the sprite image, given in pixels
CompressedBufferSize42ushortHow many bytes to feed into the RLE decompressor
CompressedPaletteIndices6variablearrayRLE-compressed indexed-color palette indices (byte values)

The size of the decompressed image is still ImageWidth * ImageHeight, but you must read CompressedBufferSize bytes only.

- +

SPR

This document describes the SPR file format used in the Ragnarok Online client (and Arcturus).

Contents

SPR files contain the following information:

  • Sprites, icons, and other graphics rendered by the game (either inside the world or as part of the UI)
  • Indexed-color bitmaps and their respective color palettes, here called the "BMP segment"
  • Truecolor images with transparency values for each pixel, here called the "TGA segment"

The file contents can be interpreted as a spritesheet and combined with ACT files to implement animated sprites.

Features

Bitmap Color Palette

Instead of encoding the RGBA pixels directly, the BMP segment of the file uses a color palette with up to 256 colors:

  • In the image data, each byte corresponds to an entry in this lookup table
  • Entry 0 is always the (transparent) background color, regardless of its alpha value
  • Even if present, alpha values are otherwise not supported and should be ignored

The palette included in the SPR file is the default palette. It's possible to load in PAL files to recolor a given sprite.

Run-Length Encoding

Because most sprites include a significant number of (transparent) background pixels, later versions of the format employ a primitive compression scheme to reduce the number of identical palette indices that have to be stored in the BMP segment.

The method is chiefly known as RLE because it works by encoding runs (here: of zero bytes, which refer to the palette index 0 - the transparent background color) into a two-byte shorthand: First, a zero-byte that indicates the start of a run, and then the number of encoded zero bytes. Decompression simply requires inserting the same number of zeroes into the output stream.

If that sounds confusing, here's some examples:

  • If the image contained five background pixels, you'll see 00 05, which stands for "emit zero, five times"
  • The bytes "ABC" (65 66 67) are "decompressed" to the same sequence as there's no encoded run present
  • A single zero byte must be encoded as 00 01 ("emit zero, one time") - meaning that 00 00 is not allowed

This feature isn't used for the truecolor (TGA) segments as pixels are stored in ABGR format, in which repetition is less common.

Transparency

Alpha values in the BMP palette are completely ignored. RGBA colors will be stored raw and not premultiplied.

The color with palette index 0 should be considered the "background color". It must be cleared manually, though.

Layout

Version 1.1

This most basic variant only supports BMP sprites. It's used in Arcturus, but not in any known version of the RO client.

FieldOffsetLengthTypeDescription
Header02string"SP" as an ASCII-encoded, fixed-size string
MinorVersion21byteVersioning information
MajorVersion31byteVersioning information
BitmapImageCount42ushortHow many images are stored in the BMP segment
BitmapSprites6variablearrayIndexed-color sprite images; pixels are stored as palette indices
ColorPalettevariable1024arrayRGBA pixel colors (byte values); always stored at the end of the file

Bitmap Sprites

This repeating structure contains the raw pixel data in the form of references to palette entries that need to be resolved.

FieldOffsetLengthTypeDescription
ImageWidth02shortThe width of the sprite image, given in pixels
ImageHeight22shortThe height of the sprite image, given in pixels
PaletteIndices4variablearrayArray of indexed-color palette indices (byte values)

A size of -1, -1 (FF FF FF FF) indicates an invalid image, which consists of a single pixel that can (probably) be discarded.

Version 2.0

This version adds the ability to store truecolor images (RGBA pixels with an alpha channel). It appears in RO and Arcturus.

FieldOffsetLengthTypeDescription
Header02string"SP" as an ASCII-encoded, fixed-size string
MinorVersion21byteVersioning information
MajorVersion31byteVersioning information
BitmapImageCount42ushortHow many images are stored in the BMP segment
TruecolorImageCount62ushortHow many images are stored in the TGA segment
BitmapSprites8variablearrayIndexed-color sprite images; pixels are stored as palette indices
TruecolorSpritesvariablevariablearrayTruecolor sprite images; pixels are stored raw (in order ABGR)
ColorPalettevariable1024arrayRGBA pixel colors (byte values); always stored at the end of the file

Truecolor Sprites

This repeating structure contains the raw pixel data in an uncompressed form.

FieldOffsetLengthTypeDescription
ImageWidth02shortThe width of the sprite image, given in pixels
ImageHeight24shortThe height of the sprite image, given in pixels
PixelBuffer6variablearrayArray of raw ABGR image data (byte values)

Version 2.1

This version adds compression (via RLE) for images in the BMP segment of the file. It's used in all modern RO clients.

Compressed Bitmap Sprites

The only difference here is that runs of zero palette indices (i.e., multiple background pixels) are RLE-compressed.

FieldOffsetLengthTypeDescription
ImageWidth02shortThe width of the sprite image, given in pixels
ImageHeight22shortThe height of the sprite image, given in pixels
CompressedBufferSize42ushortHow many bytes to feed into the RLE decompressor
CompressedPaletteIndices6variablearrayRLE-compressed indexed-color palette indices (byte values)

The size of the decompressed image is still ImageWidth * ImageHeight, but you must read CompressedBufferSize bytes only.

+ \ No newline at end of file diff --git a/file-formats/str/index.html b/file-formats/str/index.html index 0191fa3b..913310cf 100644 --- a/file-formats/str/index.html +++ b/file-formats/str/index.html @@ -4,13 +4,13 @@ STR (Placeholder) | Ragnarok Research Lab - + - + + \ No newline at end of file diff --git a/game-mechanics/creature-ai/index.html b/game-mechanics/creature-ai/index.html index 069b5209..68584441 100644 --- a/game-mechanics/creature-ai/index.html +++ b/game-mechanics/creature-ai/index.html @@ -4,13 +4,13 @@ Creature AI and Skillsets | Ragnarok Research Lab - +

Creature AI and Skillsets

This document describes how Ragnarok Online's zone servers determine the behavior of AI-enabled actors in the game world.

Model of AI Behavior

The behaviors of ingame actors can be sorted into a few basic categories:

  • Movement: Random walks, following parents, and chasing targets
  • Combat: Reacting to attacks/spellcasts, target selection (including AOI checks)
  • Looting: Picking up items from the floor, and moving to an item's location

This kind of logic is represented by a set of state transitions, here called AI preset, and assigned to creatures. It can also be changed on the fly to enable more complex reactions and facilitate somewhat dynamic reactions to gameplay situations.

Then, there's two additional categories worth exploring separately:

  • Skills: Abilities that are defined for each creature type, and specific to only it
  • NPC Events: Unrelated to creatures, they're behind regular dialogs and player-NPC interactions

The first is implemented via a set of predefined rules making up the skill usage patterns for a given unit class (monster type), which is called the monster's skillset. Skills are then used based on the current state and probabilities for each configured event.

NPC events are a special case and only apply to regular (non-creature) NPCs, which are technically using the same framework but with fewer, specialized states and no transitions. The state of NPC actors doesn't really change, so they're always "Idle".

AI State Changes

A finite state machine (FSM) represents the behavior of each actor. The state of this machine changes based on inputs provided by ingame events, which the zone server feeds into the FSM as part of its World State Update loop. After processing an input, the server emits another event as the FSM's output, to be processed in the same update loop - or possibly in the next iteration.

List of AI States

The list of supported AI states is short, but their interpretation isn't always straight-forward:

State IDInterpretationDescription
ABNORMAL_STStatusImpacted by loss of control effects that prevent it from acting normally
ANGRY_STEnragedAggressively attacking or chasing target, using an alternate behavior set (switches after being attacked)
BERSERK_STAttackingIn range and actively attacking their target
DEAD_STDeadCurrently removed from the map (inactive until respawn)
FOLLOW_SEARCH_STScanning (Follow)Seeking new targets in the area of interest, while following a parent unit
FOLLOW_STFollowingMoving to a new location, while following a parent unit
IDLE_STIdleNo action is currently in progress (default)
MOVEITEM_STLootingMoving towards an item, with the intent to loot it
RMOVE_STWalkingMoving (random walk, when not in combat)
RUSH_STPursuingChasing down a target (player), with the intent to attack it
SEARCH_STScanningMonitoring the area of interest to find new targets

Source: Kokotewa's RO database (contains the AI states, presets, and transitions for any given creature)

Some more details about the (somewhat confusing) ANGRY_ST state:

Angry: These mobs are "hyper-active". Apart from "chase"/"attack", they have the states "follow"/"angry". Once hit, they stop using these states and use the normal ones. The new states are used to determine a different skill-set for their "before attacked" and "after attacked" states. Also, when "following", they automatically switch to whoever character is closest.

Source: Hercules documentation (also contains other information about the AI system)

Input and Output Events

caution

This section contains unverified information and/or speculation. It may or may not be completely wrong.

AI state changes are triggered by certain game events, which serve as input to the underlying FSM. Similarly, state transitions emit output events which can then influence the game world. The exact order this happens isn't entirely clear, but there's some evidence the output events are emitted some time before the resulting state transition is executed:

The term spawn is misleading as monsters don’t technically die, rather monsters are temporarily removed from the map. This is important because when a monster is returned to the map the monster AI will remain in the previous state, e.g. if a monster is aggressive to a player, and were to respawn near that player, it will remain aggressive towards that player. While this is rarely encountered due to a combination of horizontal/vertical position variance and non-zero regeneration timers it does explain otherwise inexplicable behavior, e.g. an ordinarily passive monster respawning with aggressive behavior.

Source: Kokotewa's blog

For any of that to happen, it would be necessary for state changes to have the ability to override each other... like so:

  1. Creature is "killed" (removed from map) and flagged as "dead" (regenerating)
  2. The respawn timer expires, which triggers the regeneration logic
  3. The creature is revived in its original AI state (output event triggers here)
  4. It "spawns" next to a player, still in its original AI state
  5. The player's proximity triggers the appropriate input event
  6. The creature immediately attacks the player, switching AI states
  7. The "idle" output state is never reached, because another transition overrode it

If there was no player to attack when the creature respawns, it should follow that instead of 5 to 7, the output state will then be set (after the "scanning for targets" step). Since anecdotal evidence for such situations is plentiful, that's probably what's going on in the background. This is of course mere conjecture, but there's no other explanation for the aforementioned phenomenon.

List of Input Events

These are the ingame events that can trigger FSM state transitions:

IDDescriptionScope
ARENASTART_INSome "arena" (PVP) event was started (?)NPC
ARRIVEDAT_ITEM_INThe creature arrived at the location of the item it is currently trying to lootMOB
ATTACKED_INThe creature was attacked (by a player?)MOB
CHANGE_NORMALST_INAll loss of control effects were removed from the creatureMOB
CHARACTER_ATTACKSIGHT_INA player entered the creature's attack range (on their own?)MOB
CHARACTER_INATTACKSIGHT_INThe creature reached the targeted unit and is now within attack rangeMOB
CHARACTER_INSIGHT_INA player entered the creature's area of interestMOB
CLICK_INThe NPC was clicked (player started interacting with it)NPC
DEADSTATE_TIMEOUT_INThe respawn timer for the creature has elapsedMOB
DESTINATION_ARRIVED_INThe creature has arrived at the target location of its most recent movement actionMOB
ENEMY_INATTACKSIGHT_INThe targeted player is within the creature's attack rangeMOB
ENEMY_OUTATTACKSIGHT_INThe targeted player is outside the creature's attack range (but within its area of interest)MOB
ENEMY_OUTSIGHT_INThe targeted player has left the creature's area of interestMOB
ENERGY_RECHARGED_INThe creature's power has replenished (?)MOB
FRIEND_ATTACKED_INAn allied creature was attacked in the creature's area of interestMOB
G_CHARACTER_ATTACKSIGHT_INA player entered the WOE guardian's attack range (?)MOB
G_CHARACTER_INSIGHT_INA player entered the WOE guardian's area of interest (?)MOB
ITEM_INSIGHT_INA lootable item was detected in the creature's area of interestMOB
LOWERLEVEL_INSIGHT_INA viable target (low level player?) has been detected in the creature's area of interestMOB
MAGIC_LOCKON_INA spellcast targeting the creature was detectedMOB
MILLI_ATTACKED_INThe creature was attacked (by a player?) in melee rangeMOB
MOVE_END_POS_INThe creature has arrived (after moving to a new location)MOB
MOVE_RANDOM_END_INThe random movement delay timer is about to be resetMOB
MOVE_START_INThe creature is about to start moving to a new locationMOB
MYOWNER_ATTACKED_INThe unit's parent unit was attackedMOB
MYOWNER_OUTSIGHT_INThe unit's parent has left the area of interestMOB
NEAREST_CHARACTER_INThe nearest player (or other unit?) has been updated for this creatureMOB
TARGET_ITEM_DISAPPEAR_INThe item that the creature was trying to loot has disappeared from the floorMOB
TOUCHED_INA player has moved on or directly adjacent to the NPCNPC
TOUCHED2_INA player or a party member has moved on or directly adjacent to the NPC (?)NPC
TOUCHEDNPC_INAnother NPC has moved on or directly adjacent to the NPC (?)NPC
WAIT_END_INThe random movement delay timer has elapsedMOB

Some of them are only used for "friendly" units, like vendors and quest NPCs. This is usually evident from the event description, but the additional "scope" field should help to make it even clearer. Such a distinction does not exist in the game, as all NPC actors can theoretically be assigned arbitrary events and states (though the resulting behavior might be undefined).

List of Output Events

These are ingame events emitted by the FSM when a state transition completes:

IDDescriptionScope
CALL_ARENASTART_OUTStarts an "arena" (PVP) event (?)NPC
CALL_CLICKEVENT_OUTTriggers the NPC's OnClick handlerNPC
CALL_TOUCHEVENT_OUTTriggers the NPC's OnTouch handler (NPC touched by player)NPC
CALL_TOUCHEVENT2_OUTTriggers the NPC's OnTouch2 handler (NPC touched by player or party member?)NPC
CALL_TOUCHNPCEVENT_OUTTriggers the NPC's OnTouchNPC handler (NPC touched by other NPC?)NPC
CHANGE_ENEMY_OUTAttempts to change targets (start scanning?)MOB
CHANGE_NORMALST_OUTRestores the default ("Idle") stateMOB
EXPEL_OUTStarts pursuing a target, outside of the creature's attack rangeMOB
MOVE_RANDOM_START_OUTStarts a new random walkMOB
MOVE_START_OUTStarts a (random?) walk, for NPCs only?NPC
MOVETO_ITEM_OUTStarts moving to the location of an item on the floorMOB
MOVETO_MYOWNER_OUTStarts moving to the parent unit (reduce distance?)MOB
NONE_OUTDoes nothing (dummy event?)MOB
PICKUP_ITEM_OUTMakes the creature loot an itemMOB
REVENGE_ENEMY_OUTStarts using attacks (and/or abilities?) on a hostile unit in attack range?MOB
REVENGE_RANDOM_OUTStarts using attacks (and/or abilities?) on a random hostile unit in attack range?MOB
SEARCH_OUTStarts scanning for targets in the area of interestMOB
STOP_AND_CLICKEVENT_OUTStops NPC movement, then triggers its OnClick handler?NPC
STOP_MOVE_OUTStops the current movement, discarding the pathMOB
TRADE_START_OUTOpens the vendor's trading window?NPC
TRY_REVIVAL_OUTAttempt to regenerate the creature (can this fail?)MOB
WAIT_START_OUTStarts the random walk delay timerMOB

AI Presets

All creatures in RO follow a simplistic routine that is derived from predefined configuration settings (the "AI presets"). These categorize monsters roughly by their intended approach to interacting with other actors in the game world, and are then enriched with specific ability usage patterns (the "skillsets") for each individual creature type to create additional depth.

Preset Swaps

Presets aren't fixed, but rather assigned. The implication is that creatures are free to change between archetypes if their custom logic so dictates. While this doesn't affect their skill usage, as skillsets are static and tied to the unit's class, it can make monsters prioritize targets differently or move in new ways. That's what happens when bosses "enrage", presumably.

List of AI Presets

There are at least 27 known behavior archetypes that form the basis of the AI system. A complete list is difficult to compile and somewhat out of scope, but Kokotewa's database shows the archetype for each creature if you feel like cross-referencing them.

Skill Usage

Skills are used according to a predefined behavior table for each creature type, based on the creature's current AI state and random chance, as well as certain conditions. Additionally, emotes and chat output can be triggered via this mechanism.

Skillsets

The logic for any creature's skill usage patterns is defined by the following information:

  • Creature ID: The unique identifier referring to the creature's unit class
  • Input State ID: One of the aforementioned AI state identifiers (e.g., IDLE_ST)
  • Skill ID: The unique identifier defining the skill to be used
  • Level: The level of the skill that should be cast
  • Chance: Probability for the creature to use the skill
  • Cast Time: Time before the cast is actually completed
  • Delay: Timeout for this trigger

These properties are mandatory and largely self-explanatory in their meaning. However, some optional properties also exist:

  • A flag indicating whether the cast can be interrupted (by damage or use of the Dispel ability)
  • An AI preset ID to swap to in response to the trigger, if any
  • A flag indicating whether emotes or chat messages should be sent
  • If emotes or chat are used: An identifier for the emoticon or chat message (taken from a predefined table)
  • A keyword for additional conditions that need to be fulfilled (e.g., "the number of minions is lower than X")
  • A conditional value that should be evaluated as part of the condition (that's the "X" part in this example)

Emotes

Emotes are triggered at the end of the cast. It's unknown whether they still trigger if the cast was interrupted.

Monster Talk

The RO client contains a "monster talk table" (data/monster_talk_table.xml), which stores the text that would be displayed as part of a creature's skillset. It seems that this feature is no longer in use, most likely because it's annoying. Although it's seen use on some private servers in the past, it's unclear whether AI-controlled messages were at some point enabled on official servers.

Conditional Keywords

Here's the list of all known special conditional keywords:

Keyword IDOperatorExpected ValueInterpretationExample Usage
IF_ALCHEMIST------Flag to enable alternate skillsets for summoned plantsMarine Sphere: Self Destruct
IF_COMRADECONDITION===Effect IDAllied creature is afflicted by status effectChepet: Status Recovery
IF_COMRADEHP<=NumberAllied creature is below health percentageAngeling: Heal
IF_ENEMYCOUNT>=NumberNumber of enemies in attack range is above thresholdAlarm: Splash Attack
IF_HIDING------Unit is hidden (Hiding skill)Smokie: Heal
IF_HP<=NumberUnit's health is below thresholdSohee: Suicide
IF_MAGICLOCKED------Unit's cast detection has triggeredAmon Ra: Summon Monster
IF_MASTERATTACKED------Parent unit was recently attacked (?)Summoned Hydra (Alchemist): Revenge
IF_MASTERHP<=NumberParent unit's health is below thresholdSummoned Geographer (Alchemist): Heal
IF_RANGEATTACKED------Unit was recently hit by a ranged attackErrende Ebecee (BioLabs Acolyte): Pneuma
IF_RUDEATTACK------Unit was hit, but cannot retaliate (no path to attacker available?)Every creature: Teleport
IF_SKILLUSE===Skill IDUnit was hit by skill (must be damaging?)Eddga: Fireball (when hit by Fire Wall damage)
IF_SLAVENUM<=NumberNumber of minions alive is below thresholdGarm: Summon Slave
IF_TRICKCASTING------No idea (placeholder?)Marine Sphere: Speed Up

Examples

The following screenshot of ANGELING's skillset (taken from Kokotewa's RO database) illustrates the format well:

Screenshot of the skillset table for ANGELING

Accordingly, its skill usage pattern for Summon Slave can be summed up in a few sentences:

Angeling has a 100% chance to summon new followers, when it's attacking or idle and the number of existing minions is three or less. This cast takes 2 seconds, is not interruptible, and can only be used once per minute. It will emote /heh afterwards.

A similar interpretation of any other skill trigger is possible. In combination, they fully define the entire skillset.

- + \ No newline at end of file diff --git a/game-mechanics/effects/fire-wall/index.html b/game-mechanics/effects/fire-wall/index.html index e06b4ecc..2040a68a 100644 --- a/game-mechanics/effects/fire-wall/index.html +++ b/game-mechanics/effects/fire-wall/index.html @@ -4,13 +4,13 @@ Fire Wall | Ragnarok Research Lab - +

Fire Wall

Fire Wall is a Mage spell that places several AOE triggers, which can damage and knock back enemies.

Segments

A Fire Wall consists of multiple segments, which are spawned when the spell is cast. They expand from the cast location:

  • Horizontal and vertical fire walls spawn three segments adjacent to the "root" segment (placed on the target cell)
  • Diagonal fire walls spawn two segments adjacent to the root segment, and two additional ones to prevent "holes"

The individual segments seem to exist as independent game actors for the duration of their lifetime.

Lifecycle

Segments will expire after a fixed time (if still "alive") determined by the skill level, or if they get in contact with enemies.

Each segment carries a number of charges (I like to think of it as "health points"), representing how many hits can be inflicted on enemies that touch the actor. Once all charges are depleted, the segment "dies" and vanishes from the game world.

As noted in World State Updates, the clock speed of the server determines how fast charges can be consumed by enemy hits.

Placement Logic

The placement depends on the direction and length of the displacement vector from caster to target location:

firewall-placement-diagram.png

Assuming the player stands in the center, the color indicates how segments are placed when cast on the given square:

  • Casting horizontally or vertically results in a horizontal (or vertical) Fire Wall
  • If cast on the player itself, the result is always a vertical Fire Wall, with its center placed on top of them
  • Casting diagonally results in a Fire Wall that's perpendicular to the direction that the player is facing

When cast at a distance, the created Fire Walls usually face "outward" from the player (here represented by the red square):

firewall-center.png

In the game world, this might look something like this (orange = cast location, red = player):

firewall-diagonal.png

By comparison, a "vertical self-cast" would spawn segments in the following pattern:

firewall-vertical-selfcast.png

Knockback Effect

Enemies that touch a segment will be damaged and knocked back. TBD: In which direction exactly?

Since Boss monsters cannot be knocked back, they must fully "consume" the segment before they can pass through.

References

- + \ No newline at end of file diff --git a/game-mechanics/effects/index.html b/game-mechanics/effects/index.html index e42c5f32..9281b6a6 100644 --- a/game-mechanics/effects/index.html +++ b/game-mechanics/effects/index.html @@ -4,13 +4,13 @@ Skills and Spell Effects | Ragnarok Research Lab - +

Skills and Spell Effects

This document describes how Ragnarok Online's zone servers implement the various spells and skill effects.

Spells

Individual articles exist for the following spell effects:

- + \ No newline at end of file diff --git a/game-mechanics/index.html b/game-mechanics/index.html index 44b14a50..881b39c2 100644 --- a/game-mechanics/index.html +++ b/game-mechanics/index.html @@ -4,13 +4,13 @@ Overview | Ragnarok Research Lab - +

Overview

In this category, you will find a description of the serverside behaviors that enable the gameplay of Ragnarok Online.

Target Audience

Due to the technical nature of the subject matter, this specification is written under the assumption that you're a programmer or at least have some programming experience. The information provided here will hopefully be interesting to anyone familiar with the game, but if you're not on good terms with basic programming concepts a bit of a learning curve should be expected.

Objectives

After studying this documentation, you should understand how the game works "behind the scenes". All descriptions aim to be sufficiently exact and rich in technical detail, so that you may gain a deeper appreciation for the design and implementation of the underlying software systems. At the very least, it should be an interesting read to anyone who's curious about Ragnarok Online's technological foundations. Needless to say, all information is provided for educational purposes only.

Limitations

For practical reasons, only the original ("classic") version of Ragnarok Online will be covered on these pages. The game functions rather differently after the infamous "Renewal" update, which is the only version of the game still being offered by its creators. Keeping up with the constant flow of changes that are still being made to live servers is too laborious and difficult.

Prerequisites

You may want to read up on some of the fundamentals of client-server-programming, networking, and game development to make best use of this resource. Here's a list of suggestions for topics that should give you a decent understanding to begin with:

You don't need to be an expert in all of those areas. Just keep these topics in mind and read up on them as needed.

- + \ No newline at end of file diff --git a/game-mechanics/movement/index.html b/game-mechanics/movement/index.html index e926bb9e..95f7762d 100644 --- a/game-mechanics/movement/index.html +++ b/game-mechanics/movement/index.html @@ -4,13 +4,13 @@ Movement and Pathfinding | Ragnarok Research Lab - +

Movement and Pathfinding

This document describes how Ragnarok Online's zone servers implement the movement of ingame actors.

Movement Speed

All actors have a movement speed value that determines how fast they move around the game world.

Unit of Measurement

caution

This section contains unverified information and/or speculation. It may or may not be completely wrong.

Movement speeds are given in the unit of "milliseconds taken to move to the next adjacent tile" (i.e., a distance of one world unit). This should be interpreted as "delay after moving to the next ground tile", since movement happens instantaneously:

On aegis you move to the target cell instantly, then animate the movement where [on] emulators you animate first and arrive second

Source: Kokotewa (no direct link available)

Examples

CreatureMovement Speed (ms)Perceived Speed
Red Plant2000Immobile1 (extremely slow if glitched)
Pupa1000Immobile1 (very slow if glitched)
Zombie400Moves slowly
Osiris100Very fast
Treasure Box0Cannot move at all

1 Immobile creatures can still technically move (at their configured speed), but this is almost certainly a bug.

Statistics

caution

This section needs a review. The information may be outdated, incomplete, or factually incorrect.

Most creatures appear to use speed values between 150 and 400 ms. Immobile creatures always seem to use 1000 or 2000 ms.

Pathfinding

info

This section is a placeholder. If you know anything about the topic, please help fill it with content!

Hit Stun

When damaged, actors usually 2 cannot move for a brief period of time, effectively "stunning" them when they suffer a "hit". Afterwards, movement commences on the precomputed path unless their position has been altered via knockback effects.

2 Certain auras, like Endure, can make them immune to this mechanic.

- + \ No newline at end of file diff --git a/game-mechanics/world-state-updates/index.html b/game-mechanics/world-state-updates/index.html index 265cdfba..32dabc9a 100644 --- a/game-mechanics/world-state-updates/index.html +++ b/game-mechanics/world-state-updates/index.html @@ -4,13 +4,13 @@ World State Updates | Ragnarok Research Lab - +

World State Updates

This document describes how Ragnarok Online's zone servers update the simulation of the game world.

Server Tick Rate

According to a third party (who sadly failed to provide a verifiable source), the game world is updated every 20 ms:

[It uses] a different server refresh speed (aegis is faster)

Official servers] are not really coded in a very efficient way, you need a really really strong server just to get it to run at all. [They] run through everything in a 20ms interval ... on *Athena emulators we changed this to a 100ms interval (and often even slower for certain things), so it needs 5+ times less CPU power. Unfortunately that also makes implementation of certain things like firewall behavior impossible to replicate to 100%.

Source: Ragnarok Travels website


Independent research indicates that the above claims are likely to be true. Here's the findings:

  • In Hercules, AI updates take at least 100 ms (see MIN_MOBTHINKTIME in the source code)
  • Similarly, elemental follower updates take at least 100 ms (see MIN_ELETHINKTIME in the source code)
  • irowiki mentions that "Firewall hits at a very high rate of attack: approximately 40 to 50 times per second"

A tick rate of 20 ms would result in [at most] 50 updates per second, which is required for computing hits as rapidly as described on the irowiki page. Considering all of the above, it seems probable that 20 ms is indeed the value used internally.

Performance Degradation

caution

This section contains unsubstantiated claims and/or speculation. It may or may not be completely wrong.

There are presumably no special countermeasures taken when world updates take too long. This is purely based on personal experience, as performance degrades heavily in high-stress situations like War of Emperium. The server appears to simply update the simulation as fast as is physically possible, even if that isn't sufficient to guarantee a fluid gameplay experience.

Timer Granularity

On Windows, timers rely on a coarse-grained systemwide clock signal that seems to tick once every 15 or so milliseconds:

The system clock "ticks" at a constant rate. If dwMilliseconds is less than the resolution of the system clock, the thread may sleep for less than the specified length of time. If dwMilliseconds is greater than one tick but less than two, the wait can be anywhere between one and two ticks, and so on.

Source: Windows API documentation (should apply equally to all relevant, time-based WIN32 APIs)

This means that world state updates could easily be delayed when the clock signal was "missed", in difficult-to-predict patterns:

Last world update took 0.00 ms          Next update in 20.00 ms
Last world update took 44.36 ms Next update in 0.00 ms
Last world update took 16.02 ms Next update in 3.98 ms
Last world update took 31.00 ms Next update in 0.00 ms
Last world update took 16.01 ms Next update in 3.99 ms

The above simulates an "empty" server tick with an update frequency of 50 updates per second, so delays are purely accidental.

Now, what you would actually expect to happen is illustrated by running the same experiment on Linux (1ms system clock):

Last world update took 0.00 ms          Next update in 20.00 ms
Last world update took 20.77 ms Next update in 0.00 ms
Last world update took 1.19 ms Next update in 18.81 ms
Last world update took 19.19 ms Next update in 0.81 ms
Last world update took 1.19 ms Next update in 18.81 ms

How much these delays affect gameplay in practice is hard to tell, but it's interesting nonetheless.

Simulation Steps

Whenever the simulation is updated, a number of different processing steps must be executed. The exact requirements depend on the server configuration and version of the game. Here's a minimal example that includes all core gameplay systems:

  • Handle incoming and outgoing messages
  • Update all currently loaded maps and instances
  • Update NPC script handlers that are currently executing
  • Update all entities that exist in the world
  • Handle communication with other servers and the database layer

Message Handling

info

This section is a placeholder. If you know anything about the topic, please help fill it with content!

Mapwide Events

Scheduled Events

Certain game events happen at fixed times. The update loop must take into account all of the following map events:

  • War of Emperium state transitions
  • Economy updates for occupied WoE castles
  • Regeneration of "opened" Treasure Boxes
  • Time-based events tied to instances, PVP, or battleground maps (if there are any)

Any zone server has to check its internal clock regularly and initiate a mapwide change at the scheduled time.

Real-Time Events

Whenever the simulation advances, certain mapwide events take place immediately:

  • Regeneration of monsters that were previously considered "dead"

Keeping up with the lifetime of creatures is one of the main tasks of the zone server, so this will be run at least once per frame.

NPC Script Handlers

In this phase, the server steps through precompiled NPC scripts and executes them one by one. Here's a general approach:

  • Skip over script handlers that require player input or are in some other waiting condition
  • For all other NPCs: Advance the compiled NPC script handler function by some number of steps
  • Recycle script handlers that have run to completion or were cancelled

Afterwards, the server will have to process any game events that were created by the above script handlers.

Game Actors

All the other entities that live entirely on the server similarly have to be updated during the main loop:

  • Monsters
  • Items
  • Skills
  • Players

During this stage, all queued game events would be resolved; combat proceeds, items drop, skills are cast or removed, etc.

Cross-Server Communication

info

This section is a placeholder. If you know anything about the topic, please help fill it with content!

- + \ No newline at end of file diff --git a/glossary/index.html b/glossary/index.html index cfa94b91..ec603df3 100644 --- a/glossary/index.html +++ b/glossary/index.html @@ -4,13 +4,13 @@ Glossary | Ragnarok Research Lab - +

Glossary

This document lists all terms that have a special meaning in the context of this documentation, and Ragnarok Online in general.

Actor

An independent entity that exists in the game world.

Examples: Player character, monster, skill object, floor item

Aliases: Entity, Game Object, Ingame Actor, Game Actor

Area of Effect (AOE)

The area on a surface that is affected by a skill object. May refer to the effect itself, or the area it covers.

Examples: Storm Gust, Quagmire

Aliases: Ground Effect

Area of Interest (AOI)

A roughly circular area around an actor representing its field of view. The server only provides information inside this radius.

Aura

Harmful or beneficial effect that is tied to a specific ingame actor. Active for a limited time, the effect duration.

Examples: Blessing, Kyrie Eleison, Provoke

Aliases: Status Effect, Effect Status, Effect State (often abbreviated EFST)

Friend (Creature)

A creature that is of the same class and set to assist allies in combat when attacked by players in the creature's AOI.

Aliases: Ally

Minion

A creature that was summoned by another entity (usually a boss). Normally despawns alongside it and drops no loot.

Aliases: Slave

Parent (Creature)

The summoning creature (usually a boss) that the creature in question is subsequently "linked" to. See Minion.

Aliases: Master, Summoner

Unit

Any actor representing a "sentient" being, i.e, excluding artificial actors like skill objects or items (implementation detail).

Example: Player character, creature, NPC

Zone Server

The server application that runs the simulation of Ragnarok Online's persistent game world.

Aliases: World Server

- + \ No newline at end of file diff --git a/index.html b/index.html index a9e57a64..8872fc81 100644 --- a/index.html +++ b/index.html @@ -4,13 +4,13 @@ Welcome | Ragnarok Research Lab - +

Ragnarok Research Lab

Technical documentation for Gravity Co's MMORPG Ragnarok Online.

Browse the Docs

Read about file formats and other topics to learn more about the inner workings of the game.

Contribute to the Project

Every bit helps! Let's create a shared body of knowledge that anyone can understand.

Discover New Tools

Various utility programs aimed at developers, made available under a free software license.

- + \ No newline at end of file diff --git a/rendering/animation-systems/index.html b/rendering/animation-systems/index.html index ce5ea867..2d0de6ea 100644 --- a/rendering/animation-systems/index.html +++ b/rendering/animation-systems/index.html @@ -4,13 +4,13 @@ Animation Systems | Ragnarok Research Lab - +

Animation Systems

This document describes the different animation systems used in the Ragnarok Online client

Sprite Animations

In this context, "sprite animations" refers to the animations generated from data stored in ACT and SPR files.

ACT Frame Times

One curiosity is the animation delay stored in the ACT files. While other files may provide animation times in the unit of "frames" (assuming a stable frame rate of 60 FPS), or milliseconds, ACT delays don't inherently make sense in a vacuum.

Traditionally, tools and third-party implementations have computed the ACT frame duration as follows:

FrameDisplayTimeInMilliseconds = AnimationClip.FrameTimeInIntervals * 25;

Before diving into the accuracy of this estimate (spoiler alert - it's slightly off), some questions immediately spring to mind:

  • Why is the frame time given in "intervals" (as ACT editor calls them) in the first place?
  • Where does the 25 ms time value that has so far been used by the community actually come from?
  • Is it at all correct, and is there a way to precisely measure sprite animation times to verify/disprove this?

The following sections attempt to answer these questions, as far as is realistically possible.

Animation State Updates

In order to answer the first question, a bit of guesswork is needed. There's of course no way of telling for sure why this peculiar unit of measurement was chosen by the game's developers. But knowing a bit about the different animation systems in the client, a likely explanation is that sprite animations use a different mechanism altogether (e.g., clock-based state machines).

If one assumes the existence of one or several timers that update the animation state machines, operating at a fixed clock speed, it wouldn't be as far-fetched to describe frame display times in terms of full cycles on this clock. In fact, there is some evidence that other systems (e.g., particles) do indeed involve such self-clocking mechanisms to compute their own time deltas.

This might seem like a peculiar design choice compared to a global delta time (the "standard" approach). However, it's clear that there are several different animation systems working to animate objects such as the Granny3D models, water surfaces, particle systems, and RSM(2) models*. In light of this, adding one more for sprites to the mix doesn't seem particularly strange.

* This is obvious since they all use different units of measurement; Also, GR2 models were added much later in development.

Predicting Animation Times

Allowing for the above assumptions, it's possible to explain why intervals where chosen as the unit of measurement for ACT animations. However, the exact time value is yet to be determined. While the origin of the 25 ms interval time appears to be lost to history, the number is probably an estimate based on recording frame times. But - as far as I can tell - it's subtly wrong.

In order to accurately compute the most likely time value, and confirm or disprove the hypothesis that it's 25 ms, one might:

  • Mathematically model the expected animation duration for different clock speeds, in both milliseconds and frames
  • Take into account the variance introduced by discrete rendering steps (it's not possible to render partial frames, after all)
  • Record animation cycles at different speeds and sample the observed frame durations at various clock interval times
  • Compare the observed frame counts per animation clip to the predicted results, accounting for measurement error

This allows progressively eliminating impossible times until the range of possible interval times narrows in on a value.

Observed Frame Durations

Let's get the conclusion out of the way first: Based on my experiments, the real interval time is actually 24 ms.

Showing why 25 ms can't be right involves a little bit of math to predict possible frame times over time. The idea being:

  • If the real time was exactly 25 ms (which is the hypothesis to test), then observations must always lie within ~1 frame
  • Even with a rendering delay greater than one frame, it would be equal for all ACT interval counts (assuming a stable 60 FPS)
  • However, should the time be wrong, this error will accumulate, becoming more pronounced at higher interval counts

If the prediction is off but very close, this might be invisible in a 100 ms animation sequence (same rendered frame). But when the same sequence is stretched out to last several seconds, the error should be amplified greatly and eventually become observable. When a tiny difference (such as 1 ms per interval) is added up repeatedly, it soon amounts to multiple frames.

And indeed, this is precisely what happens. The orange boxes highlight the discrepancy between predicted and observed times:

act-frame-timings-discrepancy.png

While analyzing recordings and comparing observed frame times to the predictions, a few things quickly become clear:

  • The 25ms duration appears accurate at first glance, e.g., when recording a Poring's IDLE animation at normal speed
  • However, this is almost certainly a happy coincidence and due to falling within the same rendered frame (~16.667 ms)
  • Increasing the interval count to even just 40 leads to recordings "missing" frames that should exist, if 25 ms was correct
  • At this count, the range can be narrowed down to (23.75, 24.165) purely based on the number of observed frames
  • It can be seen that the predicted frame times diverge ever further from the observed ones as higher values are used

In fact, the range of possible clock interval times narrows in on exactly 24 ms as the overall display duration increases:

act-frame-timings-observed.png

At the above interval count of 128, the difference between both models is significant; this can no longer be explained by mere variance - or even measurement errors. What's more, the predicted frame time matches the observed time almost exactly if and only if the real period is actually 24 ms (and not 25). Therefore, the animation system must be working with 24 ms intervals.

NOTE: If you want to reproduce these results, you can find all of the relevant files here, alongside some rudimentary usage instructions.

- + \ No newline at end of file diff --git a/rendering/camera-controls/index.html b/rendering/camera-controls/index.html index c07438e4..06869de1 100644 --- a/rendering/camera-controls/index.html +++ b/rendering/camera-controls/index.html @@ -4,13 +4,13 @@ Camera Controls | Ragnarok Research Lab - +

Camera Controls

This document describes the camera controller used by the Ragnarok Online client.

Orbital Camera

The game uses a flexible but fairly standard camera system, known under many different names, to create a third-person POV:

  • The distance from the camera target is given as a radius (in world units)
  • Players can adjust the camera on two angles, horizontally and vertically
  • In RO, only half of the horizontal range can be explored (for technical and probably stylistic reasons)

Here's an example explaining how it works; this is not engine-specific but rather a general method of rendering a 3D scene.

Perspective Projection

The camera settings control how exactly the scene is projected on the viewport window, giving the game its unique look:

  • The vertical field of view was claimed to be 15 degrees [by curiosity], which is a bit of a... curiosity (hah)
  • The near plane is located at a distance of 10 (2 if normalized) world units - the size of one GND surface
  • ... and the far plane seems to be at a distance of 1500 (300 if normalized) world units, just outside the fog sphere

It's important to point out the unusually narrow field of view as it directly impacts how fog effects color the scene.

Fixed Viewpoints

Certain maps lock in the camera settings, limiting the player's ability to adjust them (without hacking the client, that is).

Data Tables

There are two data files (CSV tables) that control the fixed viewpoint camera on a per-map basis.

  • data/indoorRswTable.txt: Features a list of maps for which camera adjustments should be disabled
  • data/viewpointtable.txt: Compliments the above with fixed viewpoints for a selection of maps

The format of the files is quite simple:

  • If a map's RSW file is listed in the indoorRswTable, orbital rotations will be disabled completely for the map
  • The viewpointtable defines what camera angles to allow for the given map (no entry means "use default values")
  • Entries in the viewpoint table are separated by hashtags (#) and arbitrary amounts of whitespace characters

Viewpoint Table Layout

Viewpoint presets are defined by the following fields:

IndexFieldTypeInterpretation
0mapIDstringThe name of the map's RSW resource file (with extension)
1rangeintMinimum radial distance from the camera to the player character (in world units)
2scopeintMaximum radial distance adjustment that is allowed on top of range (in world units)
3range_INintInitial radial distance from the camera to the player character (in world units)
4rotationFromintMinimum longitude of the camera's azimuth angle (in degrees)
5rotationTointMaximum longitude of the camera's azimuth angle (in degrees)
6rotation_INintInitial longitude of the camera's azimuth angle (in degrees)
7altitudeFromintMinimum latitude of the camera's polar angle (in degrees)
8altitudeTointMaximum latitude of the camera's polar angle (in degrees)
9altitude_INintInitial latitude of the camera's polar angle (in degrees)

Here's an example entry for the viewpoint table:

ex_map02.rsw#   250#    250#    500#        0#      90#     45#     -40#        -65#        -65#

If this map was to be loaded, the camera's range of movement would be limited such that:

  • It starts zoomed out at the maximum radial distance of 500 (100 if normalized) world units
  • You can't zoom further in than 250 (50 if normalized) world units from the player character
  • You can't zoom further out than the maximum distance (500, or 100 if normalized)
  • You can zoom in and out freely between the two thresholds by scrolling the mouse wheel
  • The horizontal (azimuth) angle can be adjusted freely from 0 to 90 degrees
  • The vertical (polar) angle can be adjusted from positive 40 to 65 degrees (remember that the game world is upside down)

Upon loading the map, the client places the camera so that it orbits the player at (45, 65) in spherical coordinates.

Orbital Rotations

The origin of the spherical coordinates appears to be "looking into the screen" (alongside the positive Z axis). That is to say, rotating the camera by (0, 0) in spherical coordinates would place it at (0, 0, -1) in world coordinates. Camera rotations work similarly to directional lighting - as far as I can tell - although sunrays originate from (0, 1, 0) (in a non-inverted system) 1.

As for rotating alongside the axes, a few observations can be made:

  • Increasing the latitude (polar angle) rotates the camera clockwise around the X axis in RO's world coordinates (!)
  • Due to the inverted Y axis, these angles are always negative - positive values would move the camera "under the ground"
  • In a normalized coordinate system, north-south rotations must be inverted to move counterclockwise ("up" and not "down")
  • Adding to the longitude (azimuth angle) rotates the camera clockwise around the Y axis, which works fine if normalized
  • Horizontal rotations appear to roughly add/remove one degree per pixel (possibly normalized for screen size?)
  • Vertical rotations appear to add/remove exactly five degrees per scroll event to the vertical rotation angle

"Clockwise" here means "clockwise if looking at the origin" (from the camera's point of view), parallel to the respective axis.

For a specific example, you can take a look at how the viewpoint controls the camera in Nameless Island (abbey01):

  • The azimuth angle can be adjusted from 0 to 70 degrees; it's easy to confirm that the camera will only move to the "left"
  • Polar angles range from -50 to -70 (meaning, 50 to 70 if normalized), although the rotation is actually more restricted (?)
  • There's probably some special rules for fixed-viewpoint maps since the range is more limited than expected (4 vs. 5 steps)
  • Checking other maps we can see that the rule of "five degrees per step" holds, at least while the angle is below 65 degree

More research is needed as to why and how exactly the range is clamped in special cases (fixed viewpoint, high vertical angles).

While it's not possible to "see" that the world is upside down, negative polar angles in the table imply that it probably is 2.

Zoom Levels

The exact range of allowed zoom levels depends on the client version. There are two versions that I've seen:

  • In a 2004 client, the maximum zoom level was (roughly) 7 steps - and a final 8th one that barely moves the camera
  • In a 20212 Renewal client, the maximum zoom level was (roughly) 24 steps - and again a more limited 25th one
  • In both cases, the step size appears to be uneven as the last step was noticeably "smaller"

After testing different settings, I arrived at the following approximate results:

  • The step size is ~5 normalized world units per zoom level (possibly a little below that due to the "extra" bump?)
  • A minimal distance of 40 to 50 normalized world units (200 to 250) looks right - I opted for 45, which seemed close enough
  • As a max distance, something like (normalized) 80 for a classic feel and maybe 150 or so for Renewal might work (unsure)

The exact values depend on the mimum level plus step size, but here's the levels at a step size of five normalized world units:

Zoom LevelDistanceNormalized
022545
125050
227555
330060
432565
535070
637575
740080

This can be extrapolated up to the maximum distance of about 800 - 825, but the step size should still be the same.


  1. Derived by applying the same rotations with directional light angles stored in the RSW file and visually checking the result
  2. Considering that terrain altitudes and water levels are also inverted in the game files, this seems like a safe bet.
- + \ No newline at end of file diff --git a/rendering/coordinate-systems/index.html b/rendering/coordinate-systems/index.html index c278c5c8..97a75145 100644 --- a/rendering/coordinate-systems/index.html +++ b/rendering/coordinate-systems/index.html @@ -4,13 +4,13 @@ Coordinate Systems | Ragnarok Research Lab - +

Coordinate Systems

This document describes the coordinate systems used by the Ragnarok Online client.

List of Coordinate Systems

There are multiple different coordinate systems to account for when it comes to rendering the world:

NameDimensionUsed byOrigin
World Coordinates3DRenderer (Direct3D?)Zero vector (the actual world origin)
Map Coordinates12D/where command, GAT filesThe map's southwest corner (not the world origin)
GND Coordinates2DGND filesSouthwest corner of the map (isomorphic to GAT)
RSM Coordinates3DRSM and RSM2 filesOrigin of the model editor's coordinate system2
GR2 Coordinates3DGR2 filesOrigin of the model editor's coordinate system2

1 Alternatively, you could call them "GAT Coordinates" because that's effectively what they are.

2 Whatever software the artists used works with a different local origin, which needs to be transformed to world space.

World Coordinates

The baseline used to define what "world units" actually mean. All dimensions in a rendering engine can be expressed in world units since that is - by definition - the elementary unit of measurement. Using world units has the purpose of allowing you to configure how large objects are in relation to each other, as sizes have no actual meaning if there's no frame of reference.

RO's coordinate system is implicit in its various file formats, most of which make reference to it in some form or another:

  • GAT tiles are not of unit size (as you might expect), but actually 5 world units large
  • GND surfaces consist of a two-by-two square of GAT tiles, making them 10 world units large (width and height)
  • This proportionality is configurable (see GND: Geometry Scale Factor), but in practice it's extremely unlikely to ever change
  • When importing RSM or GR2 models, their local (model space) coordinates need to be transformed into world space
  • Sprites and textures seemingly map to GAT tiles at a ratio of 32 pixels per GAT tile (see data/textures/grid.tga)

One other oddity is the fact that, in all of these cases, the Y-axis ("height") is negative and the world appears "upside down".

Map Coordinates

The coordinates displayed ingame via /where slash command refer to the relative position of units on a 2D grid. This grid is centered at the world origin (0, 0, 0), so that the map's bottom left corner lies in a quadrant to the southwest of the origin.

GND Coordinates

These are isomorphic to map (GAT) coordinates, since each ground surface is two tiles large. They aren't really useful unless thinking about water planes: Water (but not lava) is usually tiled with 128px square textures that map directly to the ground.

RSM Coordinates

The coordinate system used by RSM models appears to be markedly different, with the Z and Y axes being swapped.

GR2 Coordinates

GR2 coordinates seem to be similar (or possibly identical) to RSM coordinates, with different scaling.

- + \ No newline at end of file diff --git a/rendering/ground-mesh/index.html b/rendering/ground-mesh/index.html index 4e97768f..41b62ca3 100644 --- a/rendering/ground-mesh/index.html +++ b/rendering/ground-mesh/index.html @@ -4,13 +4,13 @@ Ground Mesh | Ragnarok Research Lab - +

Ground Mesh

This document describes how static terrain can be reconstructed from the GND files used by the Ragnarok Online client.

Static Map Geometry

The ground mesh is a static object that represents the terrain (or "ground") of each map. It's the defining part of the GND file and consists chiefly of geometry to which diffuse textures, color highlights and lightmap textures are applied. This forms the basis of the visual rendition of the game world, onto which ingame actors, effects, and 3D models can be placed.

Relationship between GAT and GND

Both files correspond to different parts of a map's geometry. See also the following quote:

What you can take away from the RO method, is that they actually use two heightmaps: one invisible heightmap used for collision detection, and a visible one used to draw the map. That way, they can extend the "invisible wall or platforms" around various objects (eg those boats or docks placed in the scene that are not part of the heightmap

Source: Game Development @ StackExchange

The "collision map" is basically the contents of the GAT file (terrain types and "walkable" flags), while the "height map" is chiefly equivalent to the ground mesh as defined by its height vectors. The server needs to have the GAT information but doesn't care about GND, while the client uses GAT information to place units at their correct height and GND files to render the terrain itself.

Ground Mesh Reconstruction

Recreating the terrain involves a complex set of operations; It's stored in a memory-efficient format and therefore not directly compatible with how modern rendering hardware expects 3D objects to be described (see Face-Vertex Mesh).

Instead of storing the vertex data directly, individual chunks of terrain ("cubes") are implicitly described by "height values" that represent the upwards-facing surface geometry as relative position of its corners, offset by the surface cube's position in an imaginary "grid" of cubes. To better visualize how the geometry can be reconstructed, it might be helpful to view the terrain as a combination of different (simpler) primitive shapes, which can be "stitched together" to form the ground mesh.

Tiles, Cubes, and Surfaces

RO uses tiles as the basic unit of measurement for game logic, but its world actually consists of surfaces larger than that, which are part of what you could call "cubes" (boxes). These boxes are combined like a jigsaw puzzle to form the actual terrain.

Cubes are defined by only three surfaces:

  • TOP: Flat or sloped GROUND surface that faces "upwards" if flat, or partially so if angled
  • NORTH: WALL surface adjacent to the cube's GROUND and the neighboring (to the north) cube's GROUND surface
  • EAST: WALL surface adjacent to the cube's GROUND and the neighboring (to the east) cube's GROUND surface

In order to create walls to the BOTTOM, SOUTH and WEST, the adjacent cube's TOP, NORTH and EAST surface are used, respectively.

The only two types of surfaces are GROUND and WALL, where GROUND is essentially the square defined by connecting the four height vectors stored in the GND file, and walls are the implied surfaces formed by connecting the GROUND surface of one cube to the GROUND surface of the neighboring cube, forming a wall where they differ in height.

The distinction between GROUND and WALL surfaces is mostly for illustration purposes; all surfaces are defined by four vertices.

Each surface of a cube is 2x2 GAT tiles large and may be textured or not. Surfaces without textures (i.e., texture ID is -1) aren't rendered, nor can they be used to attach "wall" surfaces to from their adjacent cube. This is relevant, because in at least two instances, namely c_tower4 and juperos_01, ground surfaces on the map boundaries are erroneously assigned EAST and NORTH surfaces, but you can't render walls on map boundaries since there's no vertices that they could connect with.

Surfaces/cubes are combined by "inflating" the flattened cubes in a predefined order (bottom/left to top/right, with new rows starting whenever the "map width" has been exceeded - very much like pixels in a bitmap image). This allows for efficiently reconstructing the ground mesh in a single pass, which is likely to be why the GND format looks the way it does.

Here's an illustration of the above concepts:

GND_HeightVectors.png

Points defined by GND height vectors implicitly describe an inflated "cube".

These then have to be turned into an equivalent representation of the actual geometry:

03-gnd-primitives-visualized.png

Any surfaces and cubes that are implicitly defined by the height vectors can easily be reconstructed.

As mentioned above, each surface takes up the space of a two-by-two square of GAT tiles. Wall surfaces may span a larger area, but they'll appear stretched if the textures mapped to them haven't been adjusted, as can be seen in maps like schg_dun01.

It's worth keeping in mind that the "cubes" don't have to be literal boxes. Slopes can easily be created by adjusting the heights:

03-gnd-sloped-top.png

Varying the height values assigned to individual corners allows building sloped terrain.

Tiles

As previously mentioned, tiles are the basic building block for the terrain, so in a way RO could be described as a tile-based game. It's not technically accurate, but that's definitely how the end result is perceived. Characters are positioned (at the center) on a tile, map coordinates refer to the tile and everything that requires thinking about positions usually works with the tile as the smallest unit... except when it comes to rendering.

In practical terms, the actual size of each tile, as rendered on the screen, depends on the scale of the world, camera zoom level and screen resolution. However, it's the baseline for measuring distance in the world coordinate system, where one unit of distance can be expressed in "number of tiles".

As far as textures are concerned, a texture appears to fit on a single tile if its dimensions are 32 pixels, as can easily be seen by checking the size of the "grid selector" texture rendered on top of the currently selected tile:

03-tile-selector-size.png

For more details, see Rendering/Coordinate Systems.

Surfaces

Surfaces are larger, visible areas "containing" tiles. The entirety of the map's geometry is made out of surfaces, which can be textured or "invisible". Each surface contains exactly four tiles (two in each dimension).

This is mostly relevant for texturing, since texture slices usually are 64 pixels wide, so larger surfaces that appear seamless in the game are frequently pieced together by individual surfaces to give the appearance of a continuous textured plane. Surfaces don't fit perfectly, which is why there are visible "gaps" in the terrain that can be seen at the right camera angle and distance.

The vertices forming the terrain's surface geometry must be connected from bottom-right to top-left for each textured surface. If done the other way around some slopes will turn out to look rather misshapen.

Cubes

Generally speaking, the terrain is defined by a heightmap indicating the positions of all four corners for any given tile. Each chunk of the terrain (2x2 tiles) is sometimes called cube, which implies the presence of up to 6 surfaces: Two for the top and bottom of the cube, two to either side, and two to the front and back, where of course any number of them could be missing.

Now, the reason the cube metaphor doesn't work perfectly is that each chunk is only defined by three surfaces at most: Top, north, and east. If interpreted as surfaces it doesn't really change anything; Since you can express a cube's left (or south)-facing side as the adjacent cube's right (or north)-facing side you get the same effect with a much leaner data structure.

In the GND files, you'll merely find each corner's height and the scale factor, which is enough to render the geometry. You could do this by translating the positions to world coordinates and normalize the heights so they refer to a cube of width and height 1, or just make the cubes bigger to account for the scale factor and then "zoom out" farther to get the same perspective.

The geometry is then rendered by simply glueing all the surfaces together, and walls are implicitly formed by two tiles that aren't on the same height: If there's a height difference, put a plane in between the two tiles; this becomes a "wall" if assigning it a texture. However, this is only done if the neighbouring "corners" are higher (or lower) and has a textured surface of its own.

The Normalizing Scale Factor

The scale of the world, in relation to normalized world coordinates, is one of the variable fields in later versions of the GND format, but turns out to be a fixed constant in practice. It always takes on the value 10, which corresponds to "10 world units per GND cube" and "5 world units per GAT tile", respectively. The actual calculation appears to be as follows:

normalizingScaleFactor =
(GND.mapDimensions / GAT.mapDimensions) * GND.geometryScaleFactor;

Since each GND surface always measures two GAT tiles, and the scale factor is seemingly always set to ten, this nets:

normalizingScaleFactor = 1/2 * 10 = 1/5

Therefore a constant scale factor of 1/5 should be correct for every map. Applying this factor scales the heights given by the height vectors of the GND file back to the standard coordinate system, with one tile being one world unit large.

Lightmaps

Dynamic light sources are present in each map's RSW file. In order to improve rendering performance on devices of the time, the game actually doesn't use these directly. Instead, it looks like the scene lighting is precomputed from these light sources, and rendered to a texture - a process which is called baking. The GND file for each map contains the resulting bitmaps:

  • A lightmap texture, frequently called "colormap", for colored lighting
  • An ambient occlusion texture, frequently called "shadowmap", for shadows

Lightmaps add static light and shadow detail to each map, without requiring the client to calculate lighting in real time.

Similarly to the terrain's surface itself, the lightmap texture is sliced into unique parts, and each part must be mapped to the right surface on the assembled ground mesh. Which lightmap "slice" to map to the given terrain surface is defined by an implicit process of performing lookups in the following order: GND cube -> GND surface -> lightmap slice -> pixel data (RGBA)

The process for mapping slices to surfaces is therefore similar to how the actual terrain is formed: Each surface references a slice of the lightmap texture, as well as a part of the regular (diffuse) texture, while each cube references up to three surfaces.

The ambient occlusion values are stored in the same format as specularity values, with "white" pixels representing areas where no ambient occlusion (i.e., no "shadows") should be rendered. Lightmaps themselves don't have transparency values.

Ambient Occlusion ("Shadows")

It bears repeating that there isn't a single lightmap texture, but rather two: A "color map" that contains static lighting, and a "shadow map" that contains only greyscale data and is used to render shadows. The latter effectively stores the ambient occlusion percentage for each pixel.

The "shadow map" must be applied after the lighting calculation to avoid having it blended in with the other light sources, which would cause shadows to appear "washed out" or even invisible, depending on when in the lighting process the colors are multiplied with the shadow/alpha values.

Buffer Areas and Texture Bleeding

There's one more issue that has clearly affected the design of the lightmap format: Texture bleeding. If all the tiny slices were to be combined seamlessly, there could be visible artifacts from texture bleeding as soon as linear filtering is enabled.

To avoid this problem, a one-pixel area at the outside of each texture slice is present, which isn't used directly. It serves to provide a buffer between the adjacent slices and removes or at least reduces the effect when texture filtering is applied.

In this image you can see how two adjacent slices might be merged:

03-gnd-lightmap-bufferareas.png

The green area marks the visible portion of each slice that is contained within the area defined by the texture coordinates, with the area outside being the "buffer" that's blended with its adjacent texels when texture bleeding occurs.

Posterization Effect

The client reduces the number of colors when processing the lightmap texture, causing visible artifacts. These aren't part of the lightmap itself, but rather introduced deliberately when loading the texture - presumably for stylistic reasons.

The effect is achieved by applying the following operation to the RGB values:

    r = (int)(r / LEVEL_COUNT) * LEVEL_COUNT
g = (int)(g / LEVEL_COUNT) * LEVEL_COUNT
b = (int)(b / LEVEL_COUNT) * LEVEL_COUNT

In RO, LEVEL_COUNT is 16. For javascript it would be

c = floor(c / LEVEL_COUNT) * LEVEL_COUNT;
> where c is each color component

Source: greenboxal (Hercules/rathena forums)

This effectively reduces the amount of visible colors, thereby introducing the "jagged" transitions in the color gradient.

Color Highlights (Vertex Colors)

In order to give highlights to the terrain without requiring vast amounts of ever-so slightly different textures, the client can render specific (solid) colors at the corners of each tile to give this corner (and all adjacent ones) a different hue:

Each tile can have a diffuse color. This color, however, is not being applied to all four corners of a tile, but only to the bottom left vertex and all vertices that share the same coordinate.

Source: Shinryo, as posted on the rAthena forums

This concept is known as vertex colors and it works by submitting additional information for a given vertex, which is then used by the graphics hardware to color it differently. It was widely used in older games where dynamic lighting wasn't feasible due to technical limitations, and it can have a significant visual impact for barely any effort at all.

This vertex color should be applied to the bottom left corner (vertex) of each tile and the vertices of all adjacent tiles that happen to be in the same position. With this, one can define colored spots for all corners of the tile grid with little overhead.

References

- + \ No newline at end of file diff --git a/rendering/index.html b/rendering/index.html index c7cdcd4b..2144a910 100644 --- a/rendering/index.html +++ b/rendering/index.html @@ -4,13 +4,13 @@ Overview | Ragnarok Research Lab - +

Overview

In this category, you will find out how the visual representation of Ragnarok Online's game world is created by its client.

Target Audience

Due to the technical nature of the subject matter, this specification is written under the assumption that you're a programmer or at least have some programming experience. The information provided here will hopefully be interesting to anyone familiar with the game, but if you're not on good terms with basic programming concepts a bit of a learning curve should be expected.

Objectives

After studying this documentation, you should know in principle how a decently accurate representation of the game world can be rendered. The purpose of the many different file formats should become clearer, as well as how they complement each other. Topics that might not be directly related to 3D rendering will also be covered, if they're relevant to gameplay or visuals.

Limitations

This specification is concerned with a high-level description of the rendering concepts only. Depending on the graphics technologies used, lower-level implementation details may vary and/or be impossible to faithfully replicate. Be warned: Computer graphics and 3D rendering is a complex domain, so you should expect there to be errors and inaccuracies in here.

Prerequisites

Here's a (non-exhaustive) list of topics that you might want to know about in detail:

These aren't necessarily the best explanations, so you'll have to do more research when knowledge about a topic is needed.

- + \ No newline at end of file diff --git a/tools/index.html b/tools/index.html index d4252512..a7ba14dc 100644 --- a/tools/index.html +++ b/tools/index.html @@ -4,13 +4,13 @@ Developer Tools | Ragnarok Research Lab - +

Developer Tools

This page contains miscellaneous utilities that may be of interest, but don't warrant a dedicated repository.

EBM Export (with zlib)

Decompressing guild emblems is trivial in any language environment that has bindings to the zlib API:

decompress-ebm-with-zlib.js
const fs = require("fs");
const zlib = require("zlib");

const EBM_FILE_PATH = "some-guild-emblem.ebm";

const buffer = fs.readFileSync(EBM_FILE_PATH);
const bitmap = zlib.inflateSync(buffer);

fs.writeFileSync(EBM_FILE_PATH + ".bmp", bitmap);

Example: Decompressing EBM files using Node.js' builtin zlib module

EUC-KR Path Conversion

File names have traditionally been stored as EUC-KR encoded Windows paths inside the GRF archive.

If you want to convert them to a more practical Unicode format, you can use the following snippet:

decode-korean-file-names.js
import iconv from "iconv-lite";
const { decode: iconv_decode } = iconv;

// Converts the input string from EUC-KR to UTF-8 using iconv-lite
function decodeUnicodeFilePath(input) {
const decoded = iconv_decode(Buffer.from(input, "binary"), "EUC-KR");

// Replace Windows path separators with UNIX style separators (optional)
const output = decoded.replace(/\\/g, "/");

// Transparently deal with inconsistent capitalization (for Linux/Mac OS)
return output.toLowerCase();
}

export { decodeUnicodeFilePath };

This also standardizes path separators (\ becomes /). Running the code requires NodeJS with iconv-lite.

Example:

  • data\model\¿ÜºÎ¼ÒÇ°\Æ®·¦01.rsm will be converted to data/model/외부소품/트랩01.rsm

You can then paste into Google Translate to receive an English file name, or use their API to translate in bulk.

RagLite Developer Toolkit

The latest iteration of my toolkit is now freely available on GitHub: RagnarokResearchLab/RagLite

While I won't be releasing all of my old tooling, which I expect to become fully obsolete over time, this version is kept continuously in sync with the documentation published on these pages. Here's what it generally aims to do:

  • Make it easy to analyze RO-specific binary file formats in their many different versions, convert them, etc.
  • Visualize 2D sprites and 3D geometry in a "realistic" (close-enough approximation) ingame environment
  • Validate hypotheses and verify claims by reimplementing the features in question, where it makes sense

Please note that this is explicitly NOT a full game client or server implementation. If you want that, there are many other projects aiming to accomplish this lofty goal. My focus is on research, and the tool reflects that.

- + \ No newline at end of file