git clone https://github.com/Tenari/combatrpg.git
cd combatrpg
curl https://install.meteor.com/ | sh
meteor
potential names:
The game im trying to mull together in my head:
Play styles: warrior melee range magic crafter smithing enchanting alchemy trader mining
too complex ^
=======================================================
super simple version: A Fighting RPG
v 0.001 = fight component has peer 2 peer connection, synced game simulation with rollback and two movable entities (need to decide on physics framework?) v 0.01 = can walk around in the overworld (tiny map, 1 squirrel enemy) v 0.02 = can enter fight mode v 0.03 = can fight + die v 0.04 = character management view/menus v 0.041 = sleeping in bed restores health v 0.05 = can get $ + xp v 0.06 = can equip items
COMBAT
sword stab (light attack) slash (medium) stab-slash combo overhand (heavy) feint parry counter-stab counter-slash unarmed jab reverse 1-2 combo uppercut kick block movement duck dodge-in-place dash jump
MONSTERS
squirrel, faries wolf bear dragon
===========================================================
Fights happen over a peerJS connection 20ms per frame on browser input event, store valid inputs in inputQueue on peerJS data (input) event store in opponentInputQueue Infinite game loop return unless time moved forward at least 20ms from last frame
if opponentInputQueue has stuff in it
if the inputs matched our predictions
remove the inputs
else
rollback to the frame where it diverged (just involves reading the old game state out of memory)
simulateFrame(state, userInput, opponentInput) for the divergence
simulateFrame(state, userInput, predictedOpponentInput) for the frames we still don't know (back to the frame we started this loop in)
remove the opponentInputQueue
opponentUseInput = predict input for opponent predictInput(state)
useInput = read stored input for user from inputQueue that has waited the hardcoded number of input lag frames
newState = simulateFrame(state, useInput, opponentInputQueue) // simulates the next frame
endloop
an input looks like: [frameNumber, [e.keyCode list]] where frameNumber is the id of the frame in which the input was created, so frameNumber + minimumLag = the frame in which the simulation includes that input
the Fight object in meteor is used for the information of who the combatants are so as to be able to PeerJS connect if its a human vs human or to simulate ai if its a human vs monster fight component loads: first thing is to set up peerJS connection to opponent show "waiting" message have peerJS connection going: start the fight by initializing the game simulation in matter-js, rendered with pixi-js
need to answer: what happens when both peers disconnect? (close tabs) I think probably 1x per second the fight state should be updated on the Fight Mongo record to serve as a backup. That way whenever one player disconnects, the game can restart where it was last known by the server without too much hassle/loss. Then if both players disconnect, the fight is on pause indefinitely, until they both connect again. Obviously, fights need a maximum timeout condition, where if the game goes too long without the fight updating, then the player(s) who are disconnected get penalized and the fight is ended. Full loss? seems harsh penalty. Perhaps just half your hp and an energy penalty or something.
=================
a pure fighting game: fight -> cutscene -> repeat lvl 1 fighting RPgame: fight -> level up character -> cutscene -> repeat lvl 2 fighting RPgame: fight -> level up character + buy/sell items -> cutscene -> repeat lvl 3 fighting RPgame: wander world -> find monster -> fight -> level up character + buy/sell items -> repeat lvl 4 RPG fighter: just a full RPG but fights are 1v1 fighting game style
RPG elements character progression (stats + skill trees) equipment open world exploration expanded commerce (PvP trade, specialized NPC shops, etc) non-combat skills
=======================
3 classes: warrior, wizard, thief classes map to shoto, zoner, grappler, since the warrior can do both, the wizard likes to keep distance with ranged spells, and the thief likes to get in close and do big damage. grappling flow-chart system where a grab leads to a throw, leads to an attempted submission or attack, and escape possibilities for the victim at each stage of the flow chart combo system is not present/implied. there are no target combos or auto combos, everything is just based on what moves you can string together and land any character can learn any move, however moves in your class are cheaper to learn. moves are learned by spending "skill points." a fixed number of "skill points" are earned per level up 3 bars: health, mana, stamina which obviously line up to warrior, wizard, thief in terms of which bar the character has most of relative to the other characters health: does not regenerate during fights. when your opponent lands an attack, it decreases. at 0 you die mana: regenerates linearly during fights. when you use a magic-based attack, it decreases. If you do not have enough mana for a magic-based move, it whifs stamina: regenerates reverse exponentially during fights, meaning that when you have only a tiny bit of bar gone, it comes back very fast, but when you have most of your bar gone, it comes back very slow. Each move (of any kind, attack, magic, grap, or just movement) uses some amount of stamina, except for block, which uses none, but can only be done if you have more than 0 stamina, and stamina does not regenerate while blocking. If you do not have enough stamina for a move you enter "heaving" state which quickly regens stamina, but leave you open for attack
warrior: uses a sword (medium range) high hp, low mana, medium stamina striking moves are cheaper to learn wizard: uses a staff (long range) low hp, high mana, medium stamina magic moves are cheaper to learn thief: uses a dagger (short range) medium hp, medium mana, high stamina grappling moves are cheaper to learn
possible moves movement normal l/r jump crouch double jump dash (double tap L/R) air dash vanish/teleport (magic grab) ground slide (crouch dash) fly (magic jump) strikes light - a stab forward with the sword medium - a horizontal slash with the sword heavy - a top to bottom swing with the sword crouching light crouching medium launcher (down heavy) poke (forward light) overhead (forward medium) tackle (forward heavy) feint light (back light) feint medium (back medium) feint heavy (back heavy) flurry of strikes (quarter-circle light) spin (quarter-circle medium) execution (quarter-circle heavy) - stabs the sword straight down with two hands ranged firebolt (magic light) lightning (magic medium) - a lightning bolt strikes a fixed distance away from the caster the direction he is facing ice aura (magic heavy) - a freezing aura briefly surrounds the caster, damaging fireblast (forward magic light) - a column of fire extends in the direction the caster faces for half the distance of the map, continously for some fixed time lightning storm (forward magic medium) - ??? hail spikes (forward magic heavy) - a string of iceicles fall like dominoes out from the caster toward end of map trifecta (quarter-circle magic) - ice spike, firebolt, and lightning bolt converge on opponent from upper left corner, upper mid, and upper right corner of screen flaming stab (quarter-circle magic+light) call lightning (quarter-circle magic+medium) grapples grab throw (grapple light) sweep (grapple medium) pull (grapple heavy) mount (frome throw/sweep, grapple light) side control (from throw/sweep, grapple medium) back (from pull, grapple heavy) ground and pound (from mount/sidecontrol, grapple light) armbar (from mount/sidecontrol/back, grapple medium) choke (from side control or back, grapple heavy) misc block block low
Movement Tree: normal l/r └── dash └── air dash jump └── double jump └── fly (magic jump) crouch └── ground slide (crouch dash) └── vanish/teleport (magic grab)
Striking Tree: light ├── crouching light │ └── forward light └── back light └── quarter-circle light medium ├── crouching medium │ └── forward medium └── back medium └── quarter-circle medium heavy ├── crouching heavy │ └── forward heavy (tackle) └── back heavy └── quarter-circle heavy
Magic Tree: firebolt └── fireblast (forward) ├── quarter-circle magic (requires lighting storm and hail spikes) └── quarter-circle magic+light lightning └── lightning storm (forward) ├── quarter-circle magic (requires hail spikes and fireblast) └── quarter-circle magic+medium ice aura └── hail spikes (forward) ├── quarter-circle magic (requires lighting storm and fireblast) └── quarter-circle magic+heavy
Grappling Tree: (only shows learning path, not routes you can use in combat, since the armbar can be done from all positions) grab ├── throw │ └── side-control | └── armbar ├── sweep │ └── mount │ └── ground and pound └── pull └── back └── choke
Items: all items are class limited
slots: weapon, head, body weapons: dagger, sword, staff head: hood, floppy hat, helmet body: cloak, robe, gambeson
Thief weapons: pocket knife, iron dagger, steel dagger head: beanie, wool hood, leather hood body: clothes, wool cloak, leather cloak Warrior weapons: rusted sword, iron sword, steel sword head: wooden helmet, iron helmet, steel helmet body: clothes, gambeson, chain mail Wizard weapons: quarterstaff, gnarled gem staff, fine gem staff head: hat, wool wide brim hat, suede wide brim hat body: clothes, wool robe, fine robe
=====
Gameloop plan
remoteInputs are updated asynchronously because javascript fightEngine contains the game state, knows how to render a frame, and knows how to advance a frame, and knows how to rollback to a frame
==================
wuxia setting is good fit for this game