Code Monkey home page Code Monkey logo

rust-ro's Introduction

build

Discord

Welcome to rust-ro!

rust-ro is a Ragnarok MMO Server implementation written in Rust.

This project does not aim to compete with herculesWS or rathena.

Although the architecture and technical decision of this project are very different than rathena or hercules, both projects are still a source of inspiration and remain source of truth for game behavior.

In addition, this project kept some concept of existing implementations: for example this project support same scripting language for NPC meaning that existing scripts should work on this implementation and use the same database structure than rathena and some naming convention were kept.

Architecture and technical decision are documented here

1. Ambition

The project started on August 2021 with the ambition to being able to connect on the server and navigate across maps to see monsters.

Today February 2023 a lot of features have been added, at the end of readme. My ultimate goal would be to have a fully playable implementation for PRE-RE, supporting packet version 20120307.

I am working on this project for fun and also to provide a more accessible way than existing implementation to understand how Ragnarok game works for educational purpose. Each feature is covered with tests to provide internal documentation.

2. About Packet Version

The packets parser, builder and serializer are all taking packet version as parameter. The packet db also support condition around packet attributes and packet id

Although I mentioned above wanting to fully support packet version 20120307, this implementation can support any packet version, it is just I am testing exclusively with a robrowser client using this packet version and thus i am implementing only packet in this version.

3. Currently Working

Below issues are under active development

4. Implementation details

To understand what is going on at this project, check the architectures notes.

4.1 Project files structure

  • lib: contains either, proc-macro, reusable structure, generated code
    • lib/packets: Structure for packets generated by tools/packets from tools/packets_db file
    • lib/models: Plain old data structure to be reused in multiple modules
    • lib/configuration: Structure for configuration with serde deserializer implementation
    • lib/skills: Generated structures for skills from configuration and also manually implemented skills methods
  • server: server core
    • server/proxy: A proxy implementation to be use between a client and an emulator (rathena or hercules) in order to capture packets
    • server/repository: data access layer of the server, any access to dabase is written from this layer
    • server/server: global event loop, map instance event loop, persistence event loop
    • server/server/boot: any method require in order to boostrap server state (script loader, maploader, mob spawn...)
    • server/server/model: Plain old non-reusable data structure (queue, cells, etc..)
    • server/server/request_handler: controller layer, any packet receive from client pass to a controller first
    • server/server/script: interface between server and script virtual machine, provides hook implementation of the virtual machine
    • server/server/service: any logic of the game is implemented in this layer
    • server/server/state: Data structure containing game state (character session, character state, mob state)
    • server/util: Any utility methods
  • tools: code generator
    • tools/map-cache: generate map cache from map files
    • tools/packets: generate packets structure from database file
    • tools/skills: generate skills structure from configuration file
    • tools/stat-calc: A javascript/html stat calculator, cleaned up to better understand stats and combat calculation. Also added feature to generate test fixtures, to validate our implementation

5. Setup

Here's a list of pre-requisites to run rust-ro:

  • Docker OR PostgreSQL 12+ directly on your machine
  • Rust - nighly build

5.1 Config

First, make a copy from config.template.json to config.json:

cd rust-ro
cp config.template.json config.json

Inside this JSON, you will find database related variables, game related variables (exp_rate, drop_rate etc) as well.

5.2 Database

The entire database structure was based on rAthena but instead of using MySQL, we decided to go with PostgreSQL. There's minor modifications so far but until we mapped some constraints.

The choice to use Postgresql instead of MySQL was mainly motivated because I know better how to operate Postgresql than MySQL, and know better how Postgresql (mvcc) works internally. In addition past years Postgresql gained more traction than MySQL and its open source model

5.2.1 Setup Database - Using Docker

If you already have Docker installed in your machine, we prepared a docker-compose.yml with all configs ready for your ragnarok server.

Go to /docker and run:

docker-compose up -d

The first time, along with postgresql initdb is run, our custom script init.sh will be execute, it will create ragnarok database and create ragnarok user using postgres user. Then it will create ragnarok table using ragnarok user.

It comes with a default player account with following credentials: admin/qwertz

5.2.1 Setup Database - From binary

If you have PostgreSQL installed in your machine, you will need to log-in into PSQL and create the user, dabase and give the necessary privilege for it:

sudo -u postgres psql

Run the queries below:

CREATE USER ragnarok WITH PASSWORD 'ragnarok';
CREATE DATABASE ragnarok;
GRANT ALL PRIVILEGES ON DATABASE ragnarok TO ragnarok;
ALTER DATABASE ragnarok OWNER TO ragnarok;

After that, exit pgsql and import our /rust-ro/db/pg.sql via cli with:

 sudo -u postgres psql -U ragnarok ragnarok < db/pg.sql

5.3 Running the Server

After we have everyting set-up (binaries and database), we should run server binary to turn on rust-ro.

To run the server binary, you will need a ENV variable called DATABASE_PASSWORD together with your command:

DATABASE_PASSWORD=ragnarok cargo run --package server --bin server

If everything goes right, you should receive something like this output:

2024-02-11 13:45:53.695168 +01:00 [main] [INFO]: Compiled 0 item scripts compiled, skipped 2492 item scripts compilation (already compiled) in 73ms
2024-02-11 13:45:54.976721 +01:00 [main] [INFO]: load 39 scripts in 1104ms
2024-02-11 13:45:55.110070 +01:00 [tokio-runtime-worker] [WARN]: Not able to load boot script: pre-re/warps/other/sign.txt, due to No such file or directory (os error 2)
2024-02-11 13:45:55.113409 +01:00 [main] [INFO]: load 2782 warps in 6ms
2024-02-11 13:45:55.134622 +01:00 [<unnamed>] [INFO]: load 3392 mob spawns in 19ms
2024-02-11 13:45:55.152271 +01:00 [main] [INFO]: Loaded 897 map-cache in 44ms
2024-02-11 13:45:55.378476 +01:00 [main] [INFO]: Executed and cached 1601 item scripts, skipped 891 item scripts (requiring runtime data) in 226ms
2024-02-11 13:45:55.388987 +01:00 [<unnamed>] [INFO]: Start proxy for map proxy, 6124:5121
2024-02-11 13:45:55.389135 +01:00 [<unnamed>] [INFO]: Start proxy for Char proxy, 6123:6121
2024-02-11 13:45:55.389212 +01:00 [main] [WARN]: Visual debugger has been enable in configuration, but feature has not been compiled. Please consider enabling "visual-debugger" feature.
2024-02-11 13:45:55.389241 +01:00 [main] [INFO]: Server started in 2347ms
2024-02-11 13:45:55.389292 +01:00 [main] [INFO]: Server listen on 0.0.0.0:6901

5.4 [Dev] Running tools

So far, we have a few executables being compiled together with the project:

  • maps-tool: Generate mapcache files from client data: cargo run --package tools --bin maps-tool
  • packets-tool: Generate packet struct from packetdb: cargo run --package tools --bin packets-tool
  • skills-struct-generator: Generate skills struct from skills configuration files: cargo run --package tools --bin skills-struct-generator

5.5 Running the Game

Desktop Screenshot with Development Environment using RoBrowserLegacy

The goal of the project is to run all packages from packetver 20120307, we decided to use roBrowserLegacy.

If you have interest to contribute in a client with packetver 20120307, open a new issue and let's make it happen!

5.6 Running the Stat calculator/test case editor

The stat calculator is a highly customized* fork of from https://web.archive.org/web/20090319055622/http://rode.doddlercon.com/db/calc/index.php

*Customized :

  • Code has been de-obfuscated to make it comprehensive
  • Code has been reorganize so it at can work on both browser and node environment
  • Customize calculation behavior in order to get sub calculation output (e.g: get just the weapon attack before applying some modifier)
  • Build a test case editor to generate and edit fixtures for unit and integration tests

The idea behind reusing stat calculator is to validate implementation of battle and status calculation.

Output of stat calculator may be false and in this case we will check result against hercules or rathena.

cd tools/stat-calc
npm run dev

Stat calculator

6. Developer Notes

  • All packets for account 2000000 are handle by this project.
  • All packets for any other account are proxied (and display in console) to hercules or rathena.
  • clientinfo.xml to be changed to target port 6901

In proxy mode:

  • login, char, map server to be running using default ports (6900, 6121, 6122)

7. Progress Showcase (Compilation)

A compilation of progress made so far, click on streamable video below

Compilation of features so far

Compilation of features so far

7.1 Integration of the VM (showing instance and class(npc) variable)

script.mp4

7.2 Visual debugger

visual-debugger Debug server state with a UI

7.3 Warps

warps warps

7.4 Mobs

mobs

7.5 Proxied packets

packets

8. What has been done? ✔️

Some list of features that was developed so far:

8.1 Tools

  • packet structure generator from packet db
  • packet parser generator
  • map cache generator

8.2 Server

  • proxy login, char and map request to hercules/rathena login, char and map servers
  • packet debug
  • login
  • char server features(create char, delete char, join game)
  • move character in a loaded map, synchronized with client side movement (no lag, or teleportation, movement is smooth)
  • character position calculation (implementation of client side path finding)
  • debug log in in-game chat
  • parse scripts (only warps and mobs at the moment)
  • warp (change map, reset states)
  • display scripts client side (only warps and mobs at the moment)
  • visual debugger
  • map instances (map are lazily loaded, an instance is created when a player join an non initialized map)
  • mob spawn
  • atcommand: @go, @warp
  • mob move
  • NPC scripts (partially: see #3) via rathena script lang interpreter
  • basis for inventory management
  • basis for skills
  • basis for consumable item usage
  • basis for player attacking mob
  • mob drops item on death

9. Contribution

This project is currently half-open* to contribution. The reason is that all basis have not been put in place already and there are many thing to design yet.

However if you are motivated and want to contribute you can take a look to the contribution guide

* contribution can be made under certain condition

rust-ro's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rust-ro's Issues

License?

Hey there!

Awesome project you've got here! 🌟 I'm super interested in contributing, but I couldn't find any info on the license for this code.

Could you help me out and clarify the license situation, please?

Thanks!

battle mecanism

Stats and behavior happening only during battle

  • flee penalty
  • def damage reduction
  • vitdef damage reduction
  • vitdef penalty
  • ammo consumption
  • knockback
  • after hit delay
  • resistance from status
  • resistance from elemental damage
  • resistance from race
  • resistance from size
  • resistance from mob group (mvp, guardian...)
  • chance to inflict status
  • elemental damage modifier
  • size damage modifier
  • race damage modifier
  • mob group damage modifier

Skills

Total skills: 407

Progress
https://github.com/nmeylan/rust-ro/blob/master/doc/notes/skills/offensive-skills-progress.md

Requirements validation

  • skill is known at required level
  • sp
  • hp
  • ammo
  • weapon
  • zeny
  • items
  • target
  • range

Offensives

Acolyte

  • DecreaseAgi
  • HolyLight

Alchemist

  • AcidTerror
  • Bomb

Archer

  • ArrowRepel
  • ArrowShower
  • DoubleStrafe

Assassin

  • Grimtooth
  • PoisonReact
  • SonicBlow
  • ThrowVenomKnife
  • VenomSplasher

AssassinCross

  • MeteorAssault
  • SoulDestroyer

Bard

  • MelodyStrike
  • PangVoice

Champion

  • ChainCrushCombo
  • RagingPalmStrike

Clown

  • TarotCardofFate
  • VulcanArrow

Creator

  • AcidDemonstration

Crusader

  • GrandCross
  • HolyCross
  • ShieldBoomerang
  • Smite

Dancer

  • SlingingArrow
  • WinkofCharm

Gunslinger

  • BullsEye
  • Cracker
  • Desperado
  • Disarm
  • Dust
  • Fling
  • FullBuster
  • GroundDrift
  • MagicalBullet
  • PiercingShot
  • RapidShower
  • SpreadAttack
  • Tracking
  • TripleAction

HighWizard

  • NapalmVulcan
  • StaveCrasher

Hunter

  • BeastStrafing
  • BlitzBeat
  • FreezingTrap
  • PhantasmicArrow

Knight

  • BowlingBash
  • BrandishSpear
  • ChargeAttack
  • Pierce
  • SpearBoomerang
  • SpearStab

LordKnight

  • SpiralPierce
  • TraumaticBlow
  • VitalStrike

Mage

  • ColdBolt
  • FireBall
  • FireBolt
  • FrostDiver
  • LightningBolt
  • NapalmBeat
  • SoulStrike
  • StoneCurse

Merchant

  • CartRevolution
  • Mammonite

Monk

  • AsuraStrike
  • KiExplosion
  • OccultImpaction
  • RagingQuadrupleBlow
  • RagingThrust
  • RagingTrifectaBlow
  • ThrowSpiritSphere

Ninja

  • CrimsonFirePetal
  • FinalStrike
  • ImprovisedDefense
  • Kamaitachi
  • RagingFireDragon
  • ShadowSlash
  • SpearofIce
  • ThrowHuumaShuriken
  • ThrowKunai
  • ThrowShuriken
  • ThrowZeny
  • VanishingSlash
  • WindBlade

Paladin

  • GloriaDomini
  • ShieldChain

Priest

  • LexAeterna
  • LexDivina
  • TurnUndead

Professor

  • FiberLock
  • MindBreaker
  • SoulExhale
  • SoulSiphon

Rogue

  • BackStab
  • CloseConfine
  • DivestArmor
  • DivestHelm
  • DivestShield
  • DivestWeapon
  • Mug
  • SightlessMind
  • Snatch

Sage

  • Dispell
  • ElementalChangeEarth
  • ElementalChangeFire
  • ElementalChangeWater
  • ElementalChangeWind
  • SpellBreaker

Sniper

  • FalconAssault
  • FocusedArrowStrike

Stalker

  • DivestAll

Swordsman

  • Bash
  • MagnumBreak
  • Provoke

Taekwon

  • CounterKick
  • FlyingKick
  • HeelDrop
  • RoundhouseKick
  • TornadoKick

Thief

  • Envenom
  • SandAttack
  • Steal
  • StoneFling

Whitesmith

  • CartTermination

Wizard

  • EarthSpike
  • JupitelThunder
  • Sense
  • WaterBall

Supports

Acolyte

  • Blessing
  • Cure
  • Heal
  • IncreaseAgi

Alchemist

  • AidBerserkPotion
  • AidPotion
  • AlchemicalWeapon
  • BiochemicalHelm
  • SynthesizedShield
  • SyntheticArmor

Assassin

  • EnchantPoison

Blacksmith

  • WeaponRepair

Clown

  • MarionetteControl

Creator

  • FullProtection

Crusader

  • ResistantSouls
  • Sacrifice

HighPriest

  • Assumptio

Monk

  • AbsorbSpiritSphere
  • KiTranslation

Priest

  • Aspersio
  • ImpositioManus
  • KyrieEleison
  • SlowPoison
  • StatusRecovery
  • Suffragium

Sage

  • EndowBlaze
  • EndowQuake
  • EndowTornado
  • EndowTsunami

Taekwon

  • FlyingKick

Thief

  • Detoxify

Passives

Acolyte

  • DemonBane
  • DivineProtection

Alchemist

  • AxeMastery
  • Bioethics
  • PotionResearch

Archer

  • OwlsEye
  • VulturesEye

Assassin

  • KatarMastery
  • LefthandMastery
  • RighthandMastery
  • SonicAcceleration

AssassinCross

  • AdvancedKatarMastery

Bard

  • MusicLessons

Blacksmith

  • EnchantedStoneCraft
  • HiltBinding
  • IronTempering
  • OreDiscovery
  • OrideconResearch
  • SkinTempering
  • SmithAxe
  • SmithDagger
  • SmithKnucklebrace
  • SmithMace
  • SmithSpear
  • SmithSword
  • SmithTwohandedSword
  • SteelTempering
  • UnfairTrick
  • WeaponryResearch

Crusader

  • Faith

Dancer

  • DanceLessons

Gunslinger

  • ChainAction
  • SingleAction
  • SnakeEye

HighPriest

  • ManaRecharge
  • Meditatio

HighWizard

  • SoulDrain

Hunter

  • BeastBane
  • FalconryMastery
  • SteelCrow

Knight

  • CavalierMastery
  • PecoPecoRiding
  • SpearMastery

Mage

  • IncreaseSpRecovery

Merchant

  • Discount
  • EnlargeWeightLimit
  • Overcharge
  • Pushcart

Monk

  • Dodge
  • IronFists
  • RagingTrifectaBlow
  • SpiritualCadence

Ninja

  • ShurikenTraining
  • SpiritoftheBlade

Novice

  • BasicSkill

Priest

  • MaceMastery

Rogue

  • Gank
  • Haggle
  • Intimidate
  • Slyness
  • Stalk

Sage

  • Dragonology
  • FreeCast
  • Study

Swordsman

  • FatalBlow
  • IncreaseHpRecovery
  • MovingHpRecovery
  • SwordMastery
  • TwoHandedSwordMastery

Taekwon

  • HappyBreak
  • Kihop
  • PeacefulBreak

Thief

  • DoubleAttack
  • ImproveDodge

Self

Acolyte

  • Angelus
  • AquaBenedicta
  • Ruwach
  • SignumCrucis
  • Teleport

Alchemist

  • CallHomunculus
  • HomunculusResurrection
  • PreparePotion
  • TwilightAlchemy1
  • TwilightAlchemy2
  • TwilightAlchemy3
  • Vaporize

Archer

  • ArrowCrafting
  • ImproveConcentration

Assassin

  • Cloaking
  • PoisonReact

AssassinCross

  • CreateDeadlyPoison
  • EnchantDeadlyPoison
  • MeteorAssault

Bard

  • AcousticRhythm
  • Amp
  • BattleTheme
  • ClassicalPluck
  • DownTempo
  • Encore
  • HarmonicLick
  • ImpressiveRiff
  • Lullaby
  • MagicStrings
  • MentalSensing
  • PerfectTablature
  • PowerChord
  • SongofLutie
  • UnbarringOctave
  • UnchainedSerenade

Blacksmith

  • AdrenalineRush
  • AdvancedAdrenalineRush
  • Greed
  • MaximizePower
  • PowerThrust
  • WeaponPerfection

Champion

  • ChainCrushCombo
  • GlacierFist
  • Zen

Clown

  • LongingforFreedom
  • ShelteringBliss
  • WandofHermode

Crusader

  • DefendingAura
  • GrandCross
  • Guard
  • ShieldReflect
  • Shrink
  • SpearQuicken

Dancer

  • Dazzler
  • FocusBallet
  • GypsysKiss
  • HipShaker
  • LadyLuck
  • SlowGrace

Gunslinger

  • AdJustment
  • Desperado
  • FliptheCoin
  • GatlingFever
  • IncreasingAccuracy
  • MadnessCanceller

HighPriest

  • Basilica

HighWizard

  • MysticalAmplification

Knight

  • CounterAttack
  • OnehandQuicken
  • TwohandQuicken

LordKnight

  • AuraBlade
  • Concentration
  • Frenzy
  • Parrying
  • Relax

Mage

  • EnergyCoat
  • Sight

Merchant

  • ChangeCart
  • CrazyUproar
  • DecorateCart
  • ItemAppraisal
  • Vending

Monk

  • Fury
  • MentalStrength
  • RagingQuadrupleBlow
  • RagingThrust
  • Root
  • SummonSpiritSphere

Ninja

  • CicadaSkinSheeding
  • CrimsonFireFormation
  • IceMeteor
  • ImprovisedDefense
  • LightningStrikeofDestruction
  • MirrorImage
  • Soul

Novice

  • FirstAid
  • PlayDead

Paladin

  • BattleChant
  • MartyrsReckoning

Priest

  • Gloria
  • Magnificat
  • Redemptio

Professor

  • DoubleCasting
  • Foresight
  • Indulge

Rogue

  • SightlessMind

Sage

  • CastCancel
  • CreateElementalConverter
  • Hindsight
  • Hocuspocus
  • MagicRod

Sniper

  • FalconEyes
  • WindWalker

Stalker

  • CounterInstinct
  • Preserve
  • Stealth

Swordsman

  • AutoBerserk
  • Endure
  • MagnumBreak

Taekwon

  • CounterKick
  • CounterKickStance
  • HeelDrop
  • HeelDropStance
  • MildWind
  • RoundhouseKick
  • RoundhouseStance
  • Running
  • TaekwonJump
  • TaekwonMission
  • TornadoKick
  • TornadoStance
  • Tumbling

Thief

  • BackSlide
  • FindStone
  • Hiding

Whitesmith

  • CartBoost
  • MaximumPowerThrust
  • ShatteringStrike
  • UpgradeWeapon

Wizard

  • FrostNova
  • SightBlaster
  • Sightrasher

Grounds

Acolyte

  • Pneuma
  • WarpPortal

Alchemist

  • Bomb
  • SummonFlora
  • SummonMarineSphere

Archer

  • ArrowShower

Assassin

  • VenomDust

Blacksmith

  • HammerFall

Creator

  • AidCondensedPotion
  • PlantCultivation

Gunslinger

  • GroundDrift

HighWizard

  • Ganbantein
  • GravitationField

Hunter

  • AnkleSnare
  • BlastMine
  • ClaymoreTrap
  • Detect
  • Flasher
  • FreezingTrap
  • LandMine
  • Sandman
  • ShockwaveTrap
  • SkidTrap
  • TalkieBox

Mage

  • FireWall
  • SafetyWall
  • Thunderstorm

Monk

  • Snap

Ninja

  • HiddenWater
  • ShadowLeap

Priest

  • BsSacramenti
  • MagnusExorcismus
  • Sanctuary

Professor

  • BlindingMist

Rogue

  • Piece
  • Remover
  • Scribble

Sage

  • Deluge
  • MagneticEarth
  • Volcano
  • Whirlwind

Wizard

  • FirePillar
  • HeavensDrive
  • IceWall
  • LordofVermilion
  • MeteorStorm
  • Quagmire
  • StormGust

Performances

Bard

  • ImpressiveRiff
  • MagicStrings
  • PerfectTablature
  • SongofLutie
  • UnchainedSerenade

Clown

  • WandofHermode

Dancer

  • FocusBallet
  • GypsysKiss
  • HipShaker
  • LadyLuck
  • SlowGrace

Stats bonus

  • Bonus from passive skill (will be done in #11 )
  • Bonus from support skill (will be done in #11 )
  • Bonus from card
  • Bonus from equipement

Skilltree

  • Send skill list to client
  • allocate skill point
  • reward skill point on job level up
  • reset skill point on job level down
  • reset skill

Current todo

Skill todo:

  • Implement skill physical attack
  • apply skill damage to target

Status todo:

  • Take job level into account
  • Take armor stats into account
  • Take weapon stats into account

Battle todo:

  • should hit
  • Take refinement into account
  • Take element into account
  • Implement magick attack
  • ranged weapon attack calculation

Inventory

  • Add item in inventory table
  • Add item in inventory in database
  • load inventory item on character load in client
  • load inventory item when character change map in client
  • update weight
  • check max weight
  • drop item from inventory
  • loot item from ground -> add item in inventory
  • sell item remove from inventory
  • pickup/getitem item should check if not overweight
  • buy item add to inventory
  • buy item should check if not overweight
  • @getitem command
  • consume healing item
  • consume ammo item

Stats and bonus

Character Status comes from multiple sources:

  • Allocated status point (str, agi,vit, dex, int, luk)
  • Bonus from job level
  • Bonus from Item with static script (e.g: wearing Glove[1]: bonus bDex,1;, with Zerom card bonus bDex,3;)
  • Bonus from item with dynamic script (e.g: venatu card bonus bLuk,readparam(bAgi)/18;)
  • Temporary bonus from Item script (e.g: berserk potion sc_start SC_ASPDPOTION2,1800000,0;)
  • Bonus from supportive skills (e.g: Blessing)
  • Bonus from passive skills
  • Bonus from performance skills
  • Formula (hit, flee, crit, aspd, etc...)
  • Combination of formula + bonus
  • Combo (card combo, card with class combo, item combo, item + ammo combo)

Design

https://github.com/nmeylan/rust-ro/blob/master/doc/status-and-bonus.md

Progress

checked box below means either done or in progress but covered with test

Progress can be track in following reports

  • Implement bonus and bonus2 scripts, to apply card, item and passive and supportive skill bonus
  • Add bonus from static item script
  • Add bonus from dynamic item script
  • Temporary bonus like supportive skill
  • Temporary bonus like supportive skill bonus must be stored in bonus_script table

Stats and substat

  • str
  • agi
  • vit
  • dex
  • int
  • luk
  • max hp
  • max sp
  • atk left
  • atk left impositio magnus
  • atk left with cards
  • atk left with upgrade damage
  • atk right
  • matk min
  • matk max
  • def
  • vitdef
  • mdef
  • hp regen
  • sp regen
  • flee
  • hit
  • critical chance
  • critical damage
  • perfect dodge
  • aspd
  • ACD
  • cast time
  • walk delay
  • Attack element
  • Defense element
  • Element modifier
  • Size modifier
  • Race modifier
  • Monster group modifier
  • Monster class modifier
  • Weapon Refinement
  • Weapon craft star crumb
  • Weapon craft BS rank
  • Gear Refinement
  • Weight
  • Card combo
  • Card with class combo (archer set)
  • Item combo (tidal shoes + wool scarf)
  • Item with ammo combo (earth bow + stone arrow)

Need help to setup project

I followed the instructions to start the project, but I got stuck at the Ragnarok client. I can't run it without Python 2. I tried to install Python 2, but I encountered some issues with the default Linux setup.

I'm new to Rust and mainly a web programmer, so I'm not very familiar with this kind of project. I would really appreciate some guidance to get started. I have a genuine interest in contributing to this project.

Persist hotkey

  • Handle player hotkey config packets
  • Persist config in db in table "hotkey"
  • Load config from db when character joining the game
  • Send hotkey packets to client

@refresh mobs/warp

Implement @refresh for warp and mobs to reload warp or mobs configuration files

requires to:

  1. Lock each map instance
  2. Remove all map item
  3. Reload mobs/warp
  4. remove lock on map instance

Expected to cause a freeze during the refresh

make services testable

Service layer implement the core logic of the server, this layer should be easily testable.

Currently it is not the case because it has not been implemented correctly, a refactor should be done to:

  • Reduce dependencies to global state
  • Remove dependencies to global functions/variable
  • Apply single responsibility principle to service functions
  • Make dependencies injectable

Status calculation

  • aspd
    • base
    • include bonus (potion, skills)
  • atk
    • status attack calculation -left
      • ranged - non ranged weapon
      • include cards (e.g: andre)
      • include imposition magnus
    • status attack calculation - right
      • refinement, all weapon level, over refined
    • hit
    • flee
      • dodge
    • critical

Faster build time

Reduce number of dependencies, evaluate migration of following dependencies:

  1. serde -> nanoserde
  2. sqlx (serde) -> rust-postgresql
  3. regex -> regex_lite
  4. flexi_logger (regex) ->

Before

clean build: 1m11s
configuration build: 7s
change in server + build: 10s

After

clean build:
change in server + build:

Improve start up process

Currently some manual step are required to setup the database, it should be automatic when using docker approach

Review readme

Clarify the status of the project.

  • Create Meta issue
  • Review tasks
  • Review what has been done
  • Write contributing.MD
  • Review architecture page
  • Write about supported packets

Meta issue

Basic game mechanic

This category includes features such as:

  • Character movement
  • Change map
  • Player attack monster
  • Monster attack player
  • Leveling/exp reward
  • Equip item
  • loot...

Issue tracking basic game mechanic

Skill and Consumable usage

This category is about skill implementation. Consumable can be integrated in this category because they trigger skills (e.g: fly wings, potion...)

Issue tracking skills and consumable

Multiplayer

This category is about player interaction, it includes:

  • chat
  • guild
  • party

Issue tracking multiplayer features

NPC

This category is about NPC implementation

Issues tracking NPC implementation

Add kafra support

In order to add kafra script work, following native function should be implemented:

  • cutin
  • percentheal
  • viewpoint
  • implode
  • countitem
  • delitem
  • basicskillcheck
  • getskilllv
  • getcharid
  • getguildinfo
  • savepoint
  • emotion
  • logmes

Bug: status access violation

Not yet able to reproduce this easily but seems to appear when mob a mob is access while removed from map.
To ease reproduction following atcommand should be added:

  • spwanallmonster: spawn all monster on the map
  • killallmonster: kill all monster of the map at once

Apply bonus

  • Implement bonus and bonus2 scripts, to apply card, item and passive and supportive skill bonus
  • Temporary bonus like supportive skill bonus may be stored in bonus_script table

Rathena script lang VM integration

Integrate https://github.com/nmeylan/rathena-script-lang-interpreter

Support following script

Following script will be take as is from hercules rathena, without any modifications:

Priority 0

  • warper
  • stylist
  • kafra
  • shop
  • healer
  • reset skip
  • deslot
  • jobs quest
  • guides

Priority 1

  • airport
  • arena
  • mvp room

Implement native functions

native functions are function which are part of the rathena script lang "standard library".

Below functions are not implemented in VM because as they interact with server states, they should be implemented at server side at this place

Priority 0

  • menu "<option_text>",<target_label>{,"<option_text>",<target_label>,...};
  • select("<option>"{,"<option>",...})
  • prompt("<option>"{,"<option>",...})
  • input(<variable>{,<min>{,<max>}})
  • mes "<string>"{,"<string>"{,...}};
  • close;
  • close2;
  • strcharinfo(<type>{,<char_id>})
  • strnpcinfo(<type>)
  • getcharid(<type>{,"<character name>"})
  • getnpcid(<type>{,"<npc name>"});
  • openstorage;
  • warp "<map name>",<x>,<y>{,<char id>};
  • openstorage2 <storage_id>,<mode>{,<account_id>};
  • savepoint "<map name>",<x>,<y>{,{<range x>,<range y>,}<char_id>};
  • save "<map name>",<x>,<y>{,{<range x>,<range y>,}<char_id>};
  • heal <hp>,<sp>{,<char_id>};
  • healap <ap>{,<char_id>};
  • itemheal <hp>,<sp>{,<char_id>};
    heal = heal - [ ] [(100 + STATUS- [ ] 2) / 100]
  • percentheal <hp>,<sp>{,<char_id>};
  • recovery <type>{,<option>,<revive_flag>{,<map name>}};
  • jobchange <job number>{,<upper flag>,<char_id>};
  • jobname(<job number>)

Priority 1

  • getequipid({<equipment slot>,<char_id>})
  • getequipuniqueid(<equipment slot>{,<char_id>})
  • getequipname(<equipment slot>{,<char_id>})
  • getitemname(<item id>)
  • getitemname(<aegis item name>)
  • [ ]

Priority 2

  • getnameditem(<item id>,"<name to inscribe>"|<char id>);
  • getnameditem("<item name>","<name to inscribe>"|<char id>);
  • getitemslots(<item ID>)
  • getiteminfo(<item ID>,<type>)
  • getiteminfo(<item name>,<type>)
  • getiteminfo(<aegis item name>,<type>)
  • getequipcardid(<equipment slot>,<card slot>)
  • mergeitem({,<char_id>});
  • mergeitem2({<item_id>{,<char_id>}});
  • mergeitem2({"<item name>"{,<char_id>}});
  • getequiptradability(<equipment slot>{,<char id>});
  • identifyall({<type>{,<account_id>}});
  • getenchantgrade({<equipment slot>,<char_id>})
  • getitempos()
  • getmapxy("<variable for map name>",<variable for x>,<variable for y>{,<type>,"<search value>"})
  • npcspeed <speed value>;
  • npcwalkto <x>,<y>;
  • npcstop;
  • movenpc "<NPC name>",<x>,<y>{,<dir>};

Icebox

  • convertpcinfo(<char_id|account_id|player_name>,<type>)

  • convertpcinfo(<account_id>,<type>)

  • convertpcinfo(<player_name>,<type>)

  • readparam(<parameter number>{,"<character name>"})

  • readparam(<parameter number>{,<char_id>})

  • getchildid({<char_id>})

  • getmotherid({<char_id>})

  • getfatherid({<char_id>})

  • ispartneron({<char_id>})

  • getpartnerid({<char_id>})

  • getlook(<type>{,<char_id>})

  • getsavepoint(<information type>{,<char_id>})

  • getcharip({"<character name>"|<account id>|<char id>})

  • vip_status(<type>,{"<character name>"})

  • vip_time <time>,{"<character name>"};

  • addspiritball <count>,<duration>{,<char_id>};

  • delspiritball <count>{,<char_id>};

  • countspiritball {<char_id>};

  • ignoretimeout <flag>{,<char_id>};

  • getbrokenid(<number>{,<char_id>})

  • getequipisequiped(<equipment slot>{,<char_id>})

  • getequipisenableref(<equipment slot>{,<char_id>})

  • getequiprefinerycnt(<equipment slot>{,<char_id>})

  • getequipweaponlv({<equipment slot>{,<char_id>}})

  • getequiparmorlv({<equipment slot>{,<char_id>}})

  • getequippercentrefinery(<equipment slot>{,<enriched>,<char_id>})

  • getequiprefinecost(<equipment slot>,<type>,<information>{,<char id>})

  • getareadropitem("<map name>",<x1>,<y1>,<x2>,<y2>,<item>)

  • getequipcardcnt(<equipment slot>)

  • getinventorylist {<char_id>};

  • cardscnt()

  • getrefine()

  • mapid2name(<map ID>)

  • getgmlevel({<char_id>})

  • getgroupid({<char_id>})

  • gettimetick(<tick type>)

  • gettime(<type>)

  • gettimestr(<"time format">,<max length>{,<time_tick>})

  • getusers(<type>)

  • getmapusers("<map name>")

  • getareausers("<map name>",<x1>,<y1>,<x2>,<y2>)
    "- [ ] _in" maps, due to all the shops and houses.

  • getunits(<type>{,<array_variable>[<first value>]})

  • getmapunits(<type>,<"map name">{,<array_variable>[<first value>]})

  • getareaunits(<type>,<"map name">,<x1>,<y1>,<x2>,<y2>{,<array_variable>[<first value>]})

  • getguildname(<guild id>)

  • getguildmember <guild id>{,<type>{,<array_variable>}};

  • getguildmaster(<guild id>)

  • getguildmasterid(<guild id>)

  • is_guild_leader({<guild ID>})

  • getcastlename("<map name>")

  • getcastledata("<map name>",<type of data>)

  • setcastledata "<map name>",<type of data>,<value>;

  • getgdskilllv(<guild id>,<skill id>)

  • getgdskilllv(<guild id>,"<skill name>")
    Refer to 'db/(pre-)re/skill_db.yml' for the full list of skills. (GD_- [ ] are guild skills)

  • requestguildinfo <guild id>{,"<event label>"};

  • getmapguildusers("<map name>",<guild id>)

  • getskilllv(<skill id>)

  • getskilllv("<skill name>")

  • getskilllist({<char_id>});

  • getmonsterinfo(<mob ID>,<type>)

  • getmobdrops(<mob id>)

  • skillpointcount({<char_id>})

  • getscrate(<effect type>,<base rate>{,<GID>})

  • playerattached()

  • getattachedrid();

  • isloggedin(<account id>{,<char id>})

  • checkweight(<item id>,<amount>{,<item id>,<amount>,<item id>,<amount>,...});

  • checkweight("<item name>",<amount>{,"<item name>",<amount>,"<item name>",<amount>,...});

  • checkweight2(<id_array>,<amount_array>);

  • basicskillcheck()

  • checkoption(<option number>{,<char_id>})

  • checkoption1(<option number>{,<char_id>})

  • checkoption2(<option number>{,<char_id>})

  • setoption <option number>{,<flag>{,<char_id>}};

  • setcart {<type>{,<char_id>}};

  • checkcart({<char_id>});

  • setfalcon {<flag>{,<char_id>}};

  • checkfalcon({<char_id>});

  • setriding {<flag>{,<char_id>}};

  • checkriding({<char_id>});

  • setdragon {<color>{,<char_id>}};

  • checkdragon({<char_id>});

  • setmadogear {<flag>{,<type>{,<char_id>}}};

  • checkmadogear({<char_id>});

  • setmounting {<char_id>};

  • ismounting({<char_id>});

  • checkwug({<char_id>});

  • checkvending({"<Player Name>"})

  • checkchatting({"<Player Name>"})

  • checkidle({"<Player Name>"})

  • checkidlehom({"<Player Name>"})

  • checkidlemer({"<Player Name>"})

  • agitcheck()

  • agitcheck2()

  • agitcheck3()

  • isnight()

  • isday()

  • checkre(<type>)

  • isequipped(<id>{,<id>{,..}})

  • isequippedcnt(<id>{,<id>{,..}})

  • checkequipedcard(<item id>)

  • attachrid(<account ID>{,force})

  • detachrid;

  • addrid(<type>{,<flag>{,<parameters>}});

  • rid2name(<rid>)

  • message "<character name>","<message>";

  • dispbottom "<message>"{,<color>{,<char_id>}};

  • showscript "<message>"{,<GID>, <flag>};

  • areawarp "<from map name>",<x1>,<y1>,<x2>,<y2>,"<to map name>",<x3>,<y3>{,<x4>,<y4>};

  • warpparty "<to_mapname>",<x>,<y>,<party_id>,{"<from_mapname>",<range x>,<range y>};

  • warpguild "<map name>",<x>,<y>,<guild_id>;

  • warppartner("<map name>",<x>,<y>);

  • eaclass({<job number>,<char_id>})

  • roclass(<job number>{,<gender>})

  • changebase <job ID number>{,<account ID>};

  • classchange(<view id>{,"<NPC name>","<flag>"});

  • changesex({<char_id>});

  • changecharsex({<char_id>});

  • getexp <base_exp>,<job_exp>{,<char_id>};

  • getexp2 <base_exp>,<job_exp>{,<char_id>};

  • getbaseexp_ratio(<percent>{,<base_level>{,char_id});

  • getjobexp_ratio(<percent>{,<job_level>{,char_id});

  • setlook <look type>,<look value>{,<char_id>};

  • changelook <look type>,<look value>{,<char_id>};

  • pushpc <direction>,<cells>;
    can be specified by using one of the DIR_- [ ] constants (src/map/script_constants.hpp).

  • recalculatestat;

  • needed_status_point(<type>,<val>{,<char id>});

  • jobcanentermap("<mapname>"{,<JobID>});
    For optional 'JobID', see constant of Job_- [ ] , or use player's Class, BaseJob,

  • get_revision()

  • get_githash()

  • getitem <item id>,<amount>{,<account ID>};

  • getitem "<item name>",<amount>{,<account ID>};

  • getitem2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};

  • getitem2 "<item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};

  • getitem3 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<RandomIDArray>,<RandomValueArray>,<RandomParamArray>{,<account ID>};

  • getitem3 "<item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<RandomIDArray>,<RandomValueArray>,<RandomParamArray>{,<account ID>};
    .@card2 = .@ELE+((.@SC- [ ] 5)<<8);

  • getitembound <item id>,<amount>,<bound type>{,<account ID>};

  • getitembound "<item name>",<amount>,<bound type>{,<account ID>};

  • getitembound2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<bound type>{,<account ID>};

  • getitembound2 "<item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<bound type>{,<account ID>};

  • getitembound3 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<bound type>,<RandomIDArray>,<RandomValueArray>,<RandomParamArray>{,<account ID>};

  • getitembound3 "<item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<bound type>,<RandomIDArray>,<RandomValueArray>,<RandomParamArray>{,<account ID>};

  • getnameditem <item id>,<character name|character ID>;

  • getnameditem "<item name>",<character name|character ID>;

  • rentitem <item id>,<time>{,<account_id>};

  • rentitem "<item name>",<time>{,<account_id>};

  • rentitem2 <item id>,<time>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account_id>};

  • rentitem2 "<item name>",<time>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account_id>};

  • rentitem3 <item id>,<time>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<RandomIDArray>,<RandomValueArray>,<RandomParamArray>{,<account_id>};

  • rentitem3 "<item name>",<time>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<RandomIDArray>,<RandomValueArray>,<RandomParamArray>{,<account_id>};

  • makeitem <item id>,<amount>,"<map name>",<X>,<Y>{,<canShowEffect>};

  • makeitem "<item name>",<amount>,"<map name>",<X>,<Y>{,<canShowEffect>};

  • makeitem2 <item id>,<amount>,"<map name>",<X>,<Y>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<canShowEffect>};

  • makeitem2 "<item name>",<amount>,"<map name>",<X>,<Y>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<canShowEffect>};

  • makeitem3 <item id>,<amount>,"<map name>",<X>,<Y>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<RandomIDArray>,<RandomValueArray>,<RandomParamArray>{,<canShowEffect>};

  • makeitem3 "<item name>",<amount>,"<map name>",<X>,<Y>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<RandomIDArray>,<RandomValueArray>,<RandomParamArray>{,<canShowEffect>};

  • cleanarea "<map name>",<x1>,<y1>,<x2>,<y2>;

  • cleanmap "<map name>";

  • searchitem <array name>,"<item name>";

  • delitem <item id>,<amount>{,<account ID>};

  • delitem "<item name>",<amount>{,<account ID>};

  • cartdelitem <item id>,<amount>{,<account ID>};

  • cartdelitem "<item name>",<amount>{,<account ID>};

  • storagedelitem <item id>,<amount>{,<account ID>};

  • storagedelitem "<item name>",<amount>{,<account ID>};

  • guildstoragedelitem <item id>,<amount>{,<account ID>};

  • guildstoragedelitem "<item name>",<amount>{,<account ID>};

  • delitem2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};

  • delitem2 "<item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};

  • delitem3 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<RandomIDArray>,<RandomValueArray>,<RandomParamArray>{,<account ID>};

  • delitem3 "<item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<RandomIDArray>,<RandomValueArray>,<RandomParamArray>{,<account ID>};

  • delitemidx <index>{,<amount>{,<char id>}}

  • cartdelitem2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};

  • cartdelitem2 "<item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};

  • storagedelitem2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};

  • storagedelitem2 "<item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};

  • guildstoragedelitem2 <item id>,<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};

  • guildstoragedelitem2 "<item name>",<amount>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<account ID>};

  • countitem(<item id>{,<accountID>})

  • countitem("<item name>"{,<accountID>})

  • cartcountitem(<item id>{,<accountID>})

  • cartcountitem("<item name>"{,<accountID>})

  • storagecountitem(<item id>{,<accountID>})

  • storagecountitem("<item name>"{,<accountID>})

  • guildstoragecountitem(<nameID>{,<accountID>})

  • guildstoragecountitem("<item name>"{,<accountID>})

  • countitem2(<item id>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<accountID>})

  • countitem2("<item name>",<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<accountID>})

  • countitem3(<item id>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<RandomIDArray>,<RandomValueArray>,<RandomParamArray>{,<accountID>})

  • countitem3("<item name>",<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<RandomIDArray>,<RandomValueArray>,<RandomParamArray>{,<accountID>})

  • cartcountitem2(<item id>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<accountID>})

  • cartcountitem2("<item name>",<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<accountID>})

  • storagecountitem2(<item id>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<accountID>})

  • storagecountitem2("<item name>",<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<accountID>})

  • guildstoragecountitem2(<nameID>,<Identified>,<Refine>,<Attribute>,<Card0>,<Card1>,<Card2>,<Card3>{,<accountID>})

  • guildstoragecountitem2("<item name>",<Identified>,<Refine>,<Attribute>,<Card0>,<Card1>,<Card2>,<Card3>{,<accountID>})

  • rentalcountitem(<item id>{,<accountID>})

  • rentalcountitem("<item name>"{,<accountID>})

  • rentalcountitem2(<item id>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<accountID>})

  • rentalcountitem2("<item name>",<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>{,<accountID>})

  • rentalcountitem3(<item id>,<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<RandomIDArray>,<RandomValueArray>,<RandomParamArray>{,<accountID>})

  • rentalcountitem3("<item name>",<identify>,<refine>,<attribute>,<card1>,<card2>,<card3>,<card4>,<RandomIDArray>,<RandomValueArray>,<RandomParamArray>{,<accountID>})

  • countbound({<bound type>{,<char_id>}})

  • groupranditem <group id>{,<sub_group>};

  • getrandgroupitem <group_id>{,<quantity>{,<sub_group>{,<identify>{,<char_id>}}}};

  • getgroupitem <group_id>{,<identify>{,<char_id>}};

  • enable_items;

  • disable_items;

  • itemskill <skill id>,<skill level>{,<keep requirement>};

  • itemskill "<skill name>",<skill level>{,<keep requirement>};

  • consumeitem <item id>{,<char_id>};

  • consumeitem "<item name>"{,<char_id>};

  • produce <item level>;

  • cooking <dish level>;

  • makerune <% success bonus>{,<char_id>};

  • successremovecards <equipment slot>;

  • failedremovecards <equipment slot>,<type>;

  • repair <broken item number>{,<char_id>};

  • repairall {<char_id>};

  • successrefitem <equipment slot>{,<count>{,<char_id>}};

  • failedrefitem <equipment slot>{,<char_id>};

  • downrefitem <equipment slot>{,<count>{,<char_id>}};

  • unequip <equipment slot>{,<char_id>};

  • delequip <equipment slot>{,<char_id>};

  • breakequip <equipment slot>{,<char_id>};

  • clearitem {<char_id>};

  • equip <item id>{,<char_id>};

  • autoequip <item id>,<option>;

  • buyingstore <slots>;

  • searchstores <uses>,<effect>;

  • enable_command;

  • disable_command;

  • openmail({<char_id>});

  • mail <destination id>,"<sender name>","<title>","<body>"{,<zeny>{,<item id array>,<item amount array>{,refine{,bound{,<item card0 array>{,<item card1 array>{,<item card2 array>{,<item card3 array>

  • openauction({<char_id>});

  • guildopenstorage()

  • guildopenstorage_log({<char id>})

  • guild_has_permission(<permission>{,<char id>})

  • guildchangegm(<guild id>,<new master's name>)

  • guildgetexp <amount>;

  • guildskill <skill id>,<level>

  • guildskill "<skill name>",<level>

  • resetlvl <action type>{,<char_id>};

  • resetstatus({<char_id>});

  • resetskill({<char_id>});

  • resetfeel({<char_id>});

  • resethate({<char_id>});

  • sc_start <effect type>,<ticks>,<value 1>{,<rate>,<flag>{,<GID>}};

  • sc_start2 <effect type>,<ticks>,<value 1>,<value 2>{,<rate>,<flag>{,<GID>}};

  • sc_start4 <effect type>,<ticks>,<value 1>,<value 2>,<value 3>,<value 4>{,<rate>,<flag>{,<GID>}};

  • sc_end <effect type>{,<GID>};

  • sc_end_class {<char_id>{,<job_id>}};

  • getstatus(<effect type>{,<type>{,<char_id>}})

  • skilleffect <skill id>,<number>;

  • skilleffect "<skill name>",<number>;

  • npcskilleffect <skill id>,<number>,<x>,<y>;

  • npcskilleffect "<skill name>",<number>,<x>,<y>;

  • specialeffect <effect number>{,<send_target>{,"<NPC Name>"}};

  • specialeffect2 <effect number>{,<send_target>{,"<Player Name>"}};

  • removespecialeffect <effect number>{,<send_target>{,"<NPC Name>"}};

  • removespecialeffect2 <effect number>{,<send_target>{,"<Player Name>"}};

  • statusup <stat>{,<char_id>};

  • statusup2 <stat>,<amount>{,<char_id>};

  • traitstatusup <stat>{,<char_id>};

  • traitstatusup2 <stat>,<amount>{,<char_id>};

  • bonus <bonus type>,<val1>;

  • bonus2 <bonus type>,<val1>,<val2>;

  • bonus3 <bonus type>,<val1>,<val2>,<val3>;

  • bonus4 <bonus type>,<val1>,<val2>,<val3>,<val4>;

  • bonus5 <bonus type>,<val1>,<val2>,<val3>,<val4>,<val5>;

  • autobonus <bonus script>,<rate>,<duration>{,<flag>,{<other script>}};

  • autobonus2 <bonus script>,<rate>,<duration>{,<flag>,{<other script>}};

  • autobonus3 <bonus script>,<rate>,<duration>,<skill id>,{<other script>};

  • autobonus3 <bonus script>,<rate>,<duration>,"<skill name>",{<other script>};

  • bonus_script "<script code>",<duration>{,<flag>{,<type>{,<status_icon>{,<char_id>}}}};

  • bonus_script_clear {<flag>,{<char_id>}};

  • skill <skill id>,<level>{,<flag>};

  • skill "<skill name>",<level>{,<flag>};

  • addtoskill <skill id>,<level>{,<flag>};

  • addtoskill "<skill name>",<level>{,<flag>};

  • nude {<char_id>};

  • sit {"<character name>"};

  • stand {"<character name>"};

  • disguise <Monster ID>{,<char_id>};

  • undisguise {<char_id>};

  • transform <monster ID>,<duration>{,<sc type>,<val1>,<val2>,<val3>,<val4>};

  • transform "<monster name>",<duration>{,<sc type>,<val1>,<val2>,<val3>,<val4>};

  • active_transform <monster ID>,<duration>{,<sc type>,<val1>,<val2>,<val3>,<val4>};

  • active_transform "<monster name>",<duration>{,<sc type>,<val1>,<val2>,<val3>,<val4>};

  • marriage("<spouse name>");

  • wedding;

  • divorce({<char_id>})

  • adopt("<parent_name>","<baby_name>");

  • adopt(<parent_id>,<baby_id>);

  • pcfollow <id>,<target id>;

  • pcstopfollow <id>;

  • pcblockmove <id>,<option>;

  • unitblockmove <id>,<option>;

  • pcblockskill <id>,<option>;

  • unitblockskill <id>,<option>;

  • setpcblock <type>,<state>{,<account ID>};

  • getpcblock {<account ID>};

  • monster "<map name>",<x>,<y>,"<name to show>",<mob id>,<amount>{,"<event label>",<size>,<ai>};

  • areamonster "<map name>",<x1>,<y1>,<x2>,<y2>,"<name to show>",<mob id>,<amount>{,"<event label>",<size>,<ai>};

  • areamobuseskill "<map name>",<x>,<y>,<range>,<mob id>,<skill id>,<skill level>,<cast time>,<cancelable>,<emotion>,<target type>;

  • areamobuseskill "<map name>",<x>,<y>,<range>,<mob id>,"<skill name>",<skill level>,<cast time>,<cancelable>,<emotion>,<target type>;

  • killmonster "<map name>","<event label>"{,<type>};

  • killmonsterall "<map name>"{,<type>};

  • strmobinfo(<type>,<monster id>);

  • mobcount("<map name>","<event label>")

  • clone "<map name>",<x>,<y>,"<event>",<char id>{,<master_id>{,<mode>{,<flag>,<duration>}}}

  • summon "monster name",<monster id>{,<Time Out>{,"event label"}};

  • addmonsterdrop <monster id>,<item id>,<rate>,{<steal protected>,{<random option group id>}};

  • addmonsterdrop "<monster name>",<item id>,<rate>,{<steal protected>,{<random option group id>}};

  • delmonsterdrop <monster id>,<item id>;

  • delmonsterdrop "<monster name>",<item id>;

  • mob_setidleevent <GID>,<event>;

  • disablenpc {"<NPC object name>"};

  • enablenpc {"<NPC object name>"};

  • hideonnpc {"<NPC object name>"};

  • hideoffnpc {"<NPC object name>"};

  • unloadnpc "<NPC object name>";

  • cloakonnpc {"<NPC object name>"{,<character ID>}};

  • cloakoffnpc {"<NPC object name>"{,<character ID>}};

  • cloakonnpcself {"<NPC object name>"};

  • cloakoffnpcself {"<NPC object name>"};

  • isnpccloaked {"<NPC object name>"{,<character ID>}};

  • doevent "<NPC object name>::<event label>";

  • donpcevent "<NPC object name>::<event label>";

  • cmdothernpc "<npc name>","<command>";

  • npctalk "<message>"{,"<NPC name>",<flag>{,<color>}};

  • chatmes "<message>"{,"<NPC name>"};

  • setnpcdisplay("<npc name>", "<display name>", <class id>, <size>)

  • setnpcdisplay("<npc name>", "<display name>", <class id>)

  • setnpcdisplay("<npc name>", "<display name>")

  • setnpcdisplay("<npc name>", <class id>)

  • addtimer <ticks>,"NPC::OnLabel";

  • deltimer "NPC::OnLabel";

  • addtimercount <ticks>,"NPC::OnLabel";

  • initnpctimer{ "<NPC name>" {, <Attach Flag>} } |

  • stopnpctimer{ "<NPC name>" {, <Detach Flag>} } |

  • startnpctimer{ "<NPC name>" {, <Attach Flag>} } |

  • setnpctimer <tick>{,"<NPC name>"};

  • getnpctimer(<type of information>{,"<NPC name>"})

  • attachnpctimer {"<character name>"};

  • detachnpctimer {"<NPC name>"};

  • sleep {<milliseconds>};

  • sleep2 {<milliseconds>};

  • awake "<NPC name>";

  • progressbar "<color>",<seconds>;

  • progressbar_npc "<color>",<seconds>{,<"NPC Name">};

  • announce "<text>",<flag>{,<fontColor>{,<fontType>{,<fontSize>{,<fontAlign>{,<fontY>{,<char_id>}}}}}};

  • mapannounce "<map name>","<text>",<flag>{,<fontColor>{,<fontType>{,<fontSize>{,<fontAlign>{,<fontY>}}}}}};

  • areaannounce "<map name>",<x1>,<y1>,<x2>,<y2>,"<text>",<flag>{,<fontColor>{,<fontType>{,<fontSize>{,<fontAlign>{,<fontY>}}}}}};

  • callshop "<name>"{,<option>};

  • npcshopitem "<name>",<item id>,<price>{,<item id>,<price>{,<item id>,<price>{,...}}};

  • npcshopitem "<name>",<item id>,<price>,<stock>{,<item id>,<price>,<stock>{,<item id>,<price>,<stock>{,...}}};

  • npcshopadditem "<name>",<item id>,<price>{,<item id>,<price>{,<item id>,<price>{,...}}};

  • npcshopadditem "<name>",<item id>,<price>,<stock>{,<item id>,<price>,<stock>{,<item id>,<price>,<stock>{,...}}};

  • npcshopdelitem "<name>",<item id>{,<item id>{,<item id>{,...}}};

  • npcshopattach "<name>"{,<flag>};

  • npcshopupdate "<name>",<item_id>,<price>{,<stock>}

  • waitingroom "<chatroom name>",<limit>{,"<event label>"{,<trigger>{,<required zeny>{,<min lvl>{,<max lvl>}}}}};

  • delwaitingroom {"<NPC object name"};

  • enablewaitingroomevent {"<NPC object name>"};

  • disablewaitingroomevent {"<NPC object name>"};

  • enablearena;

  • disablearena;

  • getwaitingroomstate(<information type>{,"<NPC object name>"})

  • warpwaitingpc "<map name>",<x>,<y>{,<number of people>};

  • waitingroomkick "<NPC object name>" , "<character name>";

  • getwaitingroomusers "<NPC object name>";

  • kickwaitingroomall {"<NPC object name>"};

  • setmapflagnosave "<map name>","<alternate map name>",<x>,<y>;

  • setmapflag "<map name>",<flag>{,<zone>{,<type>}};

  • removemapflag "<map name>",<flag>{,<zone>};

  • getmapflag("<map name>",<flag>{,<type>})

  • setbattleflag "<battle flag>",<value>{,<reload>};

  • getbattleflag("<battle flag>")

  • warpportal <source x>,<source y>,"<map name>",<target x>,<target y>;

  • mapwarp "<from map>","<to map>",<x>,<y>{,<type>,<ID>};

  • maprespawnguildid "<map name>",<guild id>,<flag>;

  • agitstart;

  • agitend;

  • agitstart2;

  • agitend2;

  • agitstart3;

  • agitend3;

  • gvgon "<map name>";

  • gvgoff "<map name>";

  • gvgon3 "<map name>";

  • gvgoff3 "<map name>";

  • flagemblem <guild id>;

  • guardian "<map name>",<x>,<y>,"<name to show>",<mob id>{,"<event label>"{,<guardian index>}};

  • guardianinfo("<map name>", <guardian number>, <type>);

  • getguildalliance(<guild id1>, <guild id2>);

  • debugmes "<message>";

  • errormes "<message>";

  • logmes "<message>";

  • globalmes "<message>"{,"<NPC name>"};

  • rand(<number>{,<number>});

  • viewpoint <action>,<x>,<y>,<point number>,<color>{,<Char ID>};

  • viewpointmap "<map name>",<action>,<x>,<y>,<point number>,<color>;

  • cutin "<filename>",<position>;

  • emotion <emotion number>{,<target>};

  • misceffect <effect number>;

  • soundeffect "<effect filename>",<type>;

  • soundeffectall "<effect filename>",<type>{,"<map name>"}{,<x0>,<y0>,<x1>,<y1>};

  • playBGM "<BGM filename>";

  • playBGMall "<BGM filename>"{,"<map name>"{,<x0>,<y0>,<x1>,<y1>}};

  • pvpon "<map name>";

  • pvpoff "<map name>";

  • atcommand "<command>";

  • charcommand "<command>";

  • bindatcmd "<command>","<NPC object name>::<event label>"{,<atcommand level>,<charcommand level>};

  • unbindatcmd "<command>";

  • useatcmd "<command>";
    The three .@atcmd_- [ ] - [ ] - [ ] - [ ] - [ ] variables will NOT be set when invoking script-bound atcommands

  • camerainfo <range>,<rotation>,<latitude>{,<char id>};

  • refineui({<char id>})

  • openstylist({<char id>})

  • laphine_synthesis()

  • laphine_upgrade()

  • openbank({<char id>})

  • unitwalk <GID>,<x>,<y>{,"<event label>"};

  • unitwalkto <GID>,<Target GID>{,"<event label>"};

  • unitattack <GID>,<Target ID>{,<action type>};

  • unitattack <GID>,"<Target Name>"{,<action type>};

  • unitkill <GID>;

  • unitwarp <GID>,"<map name>",<x>,<y>;

  • unitstopattack <GID>;

  • unitstopwalk <GID>{,<flag>};

  • unittalk <GID>,"<text>"{,flag};

  • unitskilluseid <GID>,<skill id>,<skill lvl>{,<target id>,<casttime>,<cancel>,<Line_ID>};

  • unitskilluseid <GID>,"<skill name>",<skill lvl>{,<target id>,<casttime>,<cancel>,<Line_ID>};

  • unitskillusepos <GID>,<skill id>,<skill lvl>,<x>,<y>{,<casttime>,<cancel>,<Line_ID>};

  • unitskillusepos <GID>,"<skill name>",<skill lvl>,<x>,<y>{,<casttime>,<cancel>,<Line_ID>};

  • unitexists <GID>;

  • getunittype <GID>;

  • getunitname <GID>;

  • setunitname <GID>,"<new name>";

  • setunittitle <GID>,<title>;

  • getunittitle <GID>;

  • getunitdata <GID>,<arrayname>;

  • setunitdata <GID>,<parameter>,<new value>;

  • geteleminfo <type>{,<char_id>};

  • npcskill <skill id>,<skill lvl>,<stat point>,<NPC level>;

  • npcskill "<skill name>",<skill lvl>,<stat point>,<NPC level>;

  • day;

  • night;

  • defpattern <set number>,"<regular expression pattern>","<event label>";

  • activatepset <set number>;

  • deactivatepset <set number>;

  • deletepset <set number>;

  • distance(<x0>,<y0>,<x1>,<y1>)

  • query_sql("your MySQL query"{, <array variable>{, <array variable>{, ...}}});

  • query_logsql("your MySQL query"{, <array variable>{, <array variable>{, ...}}});

  • escape_sql(<value>)

  • setiteminfo(<item id>,<type>,<value>)

  • setiteminfo(<aegis item name>,<type>,<value>)

  • setitemscript(<item id>,<"{ new item script }">{,<type>});

  • preg_match(<regular expression pattern>,<string>{,<offset>})

  • setfont <font>;
    fonts stored in data- [ ] .eot by using an ID of the font. When the ID

  • showdigit <value>{,<type>};

  • setcell "<map name>",<x1>,<y1>,<x2>,<y2>,<type>,<flag>;

  • checkcell ("<map name>",<x>,<y>,<type>);

  • getfreecell "<map name>",<rX>,<rY>{,<x>,<y>,<rangeX>,<rangeY>,<flag>};

  • setwall "<map name>",<x>,<y>,<size>,<dir>,<shootable>,"<name>";

  • delwall "<name>";

  • checkwall "<name>";

  • readbook <book id>,<page>;

  • open_roulette( {char_id} )

  • instance_create("<instance name>"{,<instance mode>{,<owner id>}});

  • instance_destroy {<instance id>};

  • instance_enter("<instance name>",{<x>,<y>,<char_id>,<instance id>});

  • instance_npcname("<npc name>"{,<instance id>})

  • instance_mapname("<map name>"{,<instance id>})

  • instance_id({<instance mode>})

  • instance_warpall "<map name>",<x>,<y>{,<instance id>};

  • instance_announce <instance id>,"<text>",<flag>{,<fontColor>{,<fontType>{,<fontSize>{,<fontAlign>{,<fontY>}}}}};

  • instance_check_party(<party id>{,<amount>{,<min>{,<max>}}})

  • instance_check_guild(<guild id>{,<amount>{,<min>{,<max>}}})

  • instance_check_clan(<clan id>{,<amount>{,<min>{,<max>}}})

  • instance_info("<instance name>",<info type>{,<instance_db map index>});

  • instance_live_info(<info type>{,<instance id>});

  • instance_list(<"map name">{,<instance mode>});

  • getinstancevar(<variable>,<instance id>);

  • setinstancevar(<variable>,<value>,<instance id>);

  • questinfo <Icon>{,<Map Mark Color>{,"<condition>"}};

  • questinfo_refresh {<char_id>};

  • setquest <ID>{,<char_id>};

  • completequest <ID>{,<char_id>};

  • erasequest <ID>{,<char_id>};

  • changequest <ID>,<ID2>{,<char_id>};

  • checkquest(<ID>{,PLAYTIME|HUNTING{,<char_id>}})

  • isbegin_quest(<ID>{,<char_id>})

  • showevent <icon>{,<mark color>{,<char_id>}}

  • open_quest_ui {<quest ID>,{<char ID>}};

  • waitingroom2bg_single(<battle group>,{"<map name>",<x>,<y>{,"<npc name>"}});

  • waitingroom2bg("<map name>",<x>,<y>,{"<On Quit Event>","<On Death Event>"{,"<NPC Name>"}});

  • bg_create("<map name>",<x>,<y>{,"<On Quit Event>","<On Death Event>"});

  • bg_join(<battle group>,{"<map name>",{<x>,<y>{,<char id>}});

  • bg_team_setxy <Battle Group ID>,<x>,<y>;

  • bg_reserve("<battleground_map_name>"{,<ended>});

  • bg_unbook("<battleground_map_name>");

  • bg_desert({<char_id>});

  • bg_warp <Battle Group>,"<map name>",<x>,<y>;

  • bg_monster <Battle Group>,"<map name>",<x>,<y>,"<name to show>",<mob id>,"<event label>";

  • bg_monster(<Battle Group>,"<map name>",<x>,<y>,"<name to show>",<mob id>,"<event label>");

  • bg_monster_set_team <GID>,<Battle Group>;

  • bg_leave {<char_id>};

  • bg_destroy <Batte Group>;

  • areapercentheal "<map name>",<x1>,<y1>,<x2>,<y2>,<hp>,<sp>;

  • bg_get_data(<Battle Group>,<type>);

  • bg_getareausers(<Battle Group>,"<map name>",<x0>,<y0>,<x1>,<y1>);

  • bg_updatescore "<map name>",<Guillaume Score>,<Croix Score>;

  • bg_info("<battleground name>", <type>);

  • bpet;

  • birthpet;

  • pet <pet id>;

  • catchpet <pet id>;

  • makepet <pet id>;

  • getpetinfo(<type>{,<char_id>})

  • petskillbonus <bonus type>,<value>,<duration>,<delay>;

  • petrecovery <status type>,<delay>;

  • petloot <max items>;

  • petskillsupport <skill id>,<skill level>,<delay>,<percent hp>,<percent sp>;

  • petskillsupport "<skill name>",<skill level>,<delay>,<percent hp>,<percent sp>;

  • petskillattack <skill id>,<skill level>,<rate>,<bonusrate>;

  • petskillattack "<skill name>",<skill level>,<rate>,<bonusrate>;

  • petskillattack2 <skill id>,<damage>,<number of attacks>,<rate>,<bonusrate>;

  • petskillattack2 "<skill name>",<damage>,<number of attacks>,<rate>,<bonusrate>;

  • homevolution;

  • morphembryo;

  • hommutate {<ID>};

  • checkhomcall()

  • gethominfo(<type>{,<char_id>})

  • homshuffle;

  • addhomintimacy <amount>{,<char_id>};

  • mercenary_create <class>,<contract time>;

  • mercenary_delete {<char id>{,<reply>}};

  • mercenary_heal <hp>,<sp>;

  • mercenary_sc_start <type>,<tick>,<val1>;

  • mercenary_get_calls(<guild>);

  • mercenary_set_calls <guild>,<value>;

  • mercenary_get_faith(<guild>);

  • mercenary_set_faith <guild>,<value>;

  • getmercinfo(<type>{,<char id>});

  • getpartyname(<party id>)

  • getpartymember <party id>{,<type>{,<array_variable>}};

  • getpartyleader(<party id>{,<type>})

  • is_party_leader({<party ID>})

  • party_create("<party name>"{,<character id>{,<item share>,<item share type>}});

  • party_destroy(<party id>);

  • party_addmember(<party id>,<character id>);

  • party_delmember({<character id>,<party id>});

  • party_changeleader(<party id>,<character id>);

  • party_changeoption(<party id>,<option>,<flag>);

  • opendressroom(<flag>{,<char_id>});

  • navigateto("<map>"{,<x>,<y>,<flag>,<hide_window>,<monster_id>,<char_id>});

  • hateffect(<Hat Effect ID>,<State>);

  • getrandomoptinfo(<type>);

  • getequiprandomoption(<equipment index>,<index>,<type>{,<char id>});

  • setrandomoption(<equipment slot>,<index>,<id>,<value>,<param>{,<char id>});

  • randomoptgroup <random option group ID>;

  • clan_join(<clan id>{,<char id>});

  • clan_leave({<char id>});

  • channel_create "<chname>","<alias>"{,"<password>"{<option>{,<delay>{,<color>{,<char_id>}}}}};

  • channel_setopt "<chname>",<option>,<value>;

  • channel_getopt "<chname>",<option>;

  • channel_setcolor "<chname>",<color>;

  • channel_setpass "<chname>","<password>";

  • channel_setgroup "<chname>",<group_id>{,...,<group_id>};

  • channel_setgroup2 "<chname>",<array_of_groups>;

  • channel_chat "<chname>","<message>"{,<color>};

  • channel_ban "<chname>",<char_id>;

  • channel_unban "<chname>",<char_id>;

  • channel_kick "<chname>",<char_id>;

  • channel_kick "<chname>","<char_name>";

  • channel_delete "<chname>";

  • achievementadd(<achievement id>{,<char id>})

  • achievementremove(<achievement id>{,<char id>})

  • achievementinfo(<achievement id>,<type>{,<char id>})

  • achievementcomplete(<achievement id>{,<char id>})

  • achievementexists(<achievement id>{,<char id>});

  • achievementupdate(<achievement id>,<type>,<value>{,<char id>})

Atcommands

At command dispatcher is located here

List of Atcommand to implement, list is not exhaustive:

  • autoloot <%>
  • showexp
  • warp
  • go
  • rates
  • item
  • blvl
  • jlvl
  • job

Docs: Tooling Build

Hey, how you doing?

So, I saw that the project counts with cargo-watch and maybe it will be helpful to let some commands that you may use on the documentation.

There's anything specific that you usually use there or..?

Add missing tests

Cover following services:

CharacterService

  • max_weight
  • check_weight
  • change_map
  • change_look
  • change_sprite
  • update_zeny
  • update_base_level
  • update_job_level
  • change_job
  • load_units_in_fov

InventoryService

  • add_items_in_inventory
  • reload_inventory
  • sprite_change_packet_for_item
  • equip_item
  • check_weapon_requirements
  • takeoff_equip_item

## ItemService
- [ ] use_item

BattleService

  • damage_character_attack_monster_melee
  • weapon_atk
  • attack

MapInstanceService

  • Move code from map_loop to map instance service + add tests
  • spawn_mobs
  • update_mobs_fov
  • mobs_action
  • mob_die
  • mob_drop_items
  • remove_dropped_item_from_map

MobService

  • action_move

## ScriptService
- [ ] schedule_getitem

ServerService

  • Move code from game_loop to map instance service + add tests
  • create_map_instance
  • schedule_warp_to_walkable_cell
  • character_attack
  • character_pickup_item

StatusService

  • attack_per_seconds
  • attack_motion
  • client_aspd
  • aspd
  • right_hand_weapon_type
  • status_atk_left_side
  • status_atk_right_side
  • fist_atk
  • weapon_atk
  • mob_vit_def

Tool: stat calculator

This task is a pre-requisite to progress on "bonus" and "skills" implementation.

Objective is to have a comprehensive code and be able to generate lot of test scenario (e.g: for each class, on different level, using different weapon/equipement, to assert damage dealt using skill or bonus applied)

TODO:

  • Clean up offuscated code to make it understandable
  • Should use "db" from server instead of hardcoded values
  • Build a tool on top of that to generate test cases

Tasks

  • remove code duplication
  • save local storage
  • load local storage
  • load current in local storage/reset
  • trigger calc refresh checkbox
  • bind all input/select onchange to trigger calculation
  • style
  • item data
  • item data display
  • test case export
  • test case load
  • Generate test case for battle
  • Generate test case for stats (item + support/food, etc....)
  • Generate test case for item/card stats

Stats calc: game integration

A nice to have feature would be to be able to "load" a test case scenario into game.

Server could provide an http API which:

  • Would be available behind a config flag enable-devtool
  • Provide an endpoint which would accept a TestCase data example here
  • The endpoint would be used to
    • update in database a given accountId, for a given char slot, char table with test case data
    • update in database inventory table with test case data

VM monitor

Add in the visual debugger vm metrics, to be implemented in this repo:

  • running program
  • constant pool size
  • heap information
  • etc..

Base game mechanic

  • base exp reward
  • base level up
  • base level: next level required exp
  • job exp reward
  • job level up
  • job level: next level required exp
  • equip item
  • equip item check class requirement
  • equip item check level requirement
  • monster damage
  • monster death
  • monster respawn
  • monster drop
  • pickup dropped item
  • status point reward
  • status point allocation
  • skill point reward
  • skill point allocation

Better error handling: Clean unwrap()/expected()

There are too many usage of unwrap or expected in the codebase.
Some are "safe", but the majority are not and lead to thread being terminated and the server is unusable.

Depending on where the error occurs we should:

  • Inform the client an error occurred
  • Log error details
  • Recover from the error

Add rathena "shop" support

  • buy/sell menu
  • buy list item
  • perfom buy (place item into invetory, remove zeny, confirmation)
  • ZC_FAILED_OPEN_BUYING_STORE_TO_BUYER when over weight
  • getitem
  • sell list inventory item
  • perform sell
  • handle npc of type "shop" (shorthand for action list above)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.