I implemented banning and kicking. This was a must-have feature.
I cut some corners here by making offline banning an operation which blocks until the database has finished doing it. That means that the map the admin is on hangs until the database did the change, but considering that banning isn't something which happens every minute (at least I hope it won't) this shouldn't be a problem.
On-train development
Development of a HTML5 MMORPG using Websockets, Java and MongoDB
Thursday, May 9, 2013
Saturday, April 13, 2013
Tutorial
I started with the tutorial. The tutorial is the first thing the user sees of a game, so it's very important to do it well. It has to get the player hyped for the game while still conveying all information the user needs.
It is also a baptism of fire for the scripting engine, because tutorials are always heavily scripted. And bugs will not be forgiven, because a buggy tutorial will alienate many players before they even took a look at the real game.
I created the NPC Firyafoni as a framing device for the tutorial . Her name is an anagram for "Info Fairy" which is a pretty accurate description of her. I plan to use this character whenever I need to give some meta-gaming information to the player.
Fairies are creatures which are capable of existing in many places at the same time by creating clones of them self in remote places. When the fairy doesn't concentrate on a clone, it disappears, but most fairies are capable of handling dozens of clones at the same time. All clones of them form a hive-mind - everything one clone of them sees or feels is also noticed by all others. The individual clones are capable of interacting with other creatures normally. It is, however, impossible for the clones to interact with each other. When two clones of a fairy perceive each other, it's a very unsettling experience for the fairy, which leads to one of the clones disappearing. For that reason, fairies are also afraid of mirrors.
It is also a baptism of fire for the scripting engine, because tutorials are always heavily scripted. And bugs will not be forgiven, because a buggy tutorial will alienate many players before they even took a look at the real game.
I created the NPC Firyafoni as a framing device for the tutorial . Her name is an anagram for "Info Fairy" which is a pretty accurate description of her. I plan to use this character whenever I need to give some meta-gaming information to the player.
Fairies are creatures which are capable of existing in many places at the same time by creating clones of them self in remote places. When the fairy doesn't concentrate on a clone, it disappears, but most fairies are capable of handling dozens of clones at the same time. All clones of them form a hive-mind - everything one clone of them sees or feels is also noticed by all others. The individual clones are capable of interacting with other creatures normally. It is, however, impossible for the clones to interact with each other. When two clones of a fairy perceive each other, it's a very unsettling experience for the fairy, which leads to one of the clones disappearing. For that reason, fairies are also afraid of mirrors.
Monday, April 8, 2013
Equip defense
I haven't done any development for a while (curse you, Kerbal Space Program), but today I again did something: I implemented equipment defense. Each piece of equipment can now have a defense value. Optionally, it can also have a different defense value against specific elements. When it hasn't got a specific value against an attack, the general defense value is used.
When an attack hits a player, each piece of equipment removes a random amount of damage which is between 0 and the applicable defense value.
When an attack hits a player, each piece of equipment removes a random amount of damage which is between 0 and the applicable defense value.
Friday, February 8, 2013
Healing works again
Today I fixed lot of minor problems on both client and server. But I also did something major: I re-implemented healing as procedurally generated spells.
Monday, February 4, 2013
Equipment revamp
While creating the buyable equipment for Wenibe, I noticed that there isn't much equipment can do. It can modify the players stats, but I would expect it to have more fundamental properties. Clothes should reduce damage and wands increase it. They should also be geared for specific situations.
When an attack hits the character, for each piece of equipment a random number is generated between 0 and the applicable defense value. Each of these numbers is subtracted from the attack. No total immunity is possible that way - even weak attacks have a low chance to break through high-end equipment. But considering that a player character usually wears 4 defense items (hat, robe, belt, shoes), the resistance is still pretty reliable when all of them give about equal defense.
I think I should make these percentile boni.
Just like with clothes, a weapon has a general value and specific different values for specific elements.
Each of these effects could only apply to a specific element. That way you have to choose between a general-purpose weapon which boosts all elements or a specialized weapon which only boosts your favorite one while giving a malus to the opposite element.
I could also imagine rare weapons which give all spells a special property, like making all spells vampiric.
Clothes
Clothes always have a general defense value, and can have special defense values for specific elements which are higher or lower. When the equipment has a special value for the element which is used by the attacker, that value is used, otherwise the general defense value is used.When an attack hits the character, for each piece of equipment a random number is generated between 0 and the applicable defense value. Each of these numbers is subtracted from the attack. No total immunity is possible that way - even weak attacks have a low chance to break through high-end equipment. But considering that a player character usually wears 4 defense items (hat, robe, belt, shoes), the resistance is still pretty reliable when all of them give about equal defense.
Weapons
Weapons could have these effects:- Increase damage
- Increase range
- Decrease casting time
| Staff | Rod | Wand | Stave | Scepter | Cane | |
|---|---|---|---|---|---|---|
| Damage | X | X | X | |||
| Range | X | X | X | |||
| Cast Time | X | X | X |
I think I should make these percentile boni.
Just like with clothes, a weapon has a general value and specific different values for specific elements.
Each of these effects could only apply to a specific element. That way you have to choose between a general-purpose weapon which boosts all elements or a specialized weapon which only boosts your favorite one while giving a malus to the opposite element.
I could also imagine rare weapons which give all spells a special property, like making all spells vampiric.
Labels:
concept
Sunday, February 3, 2013
The town of Wenibe
I started building a small town map as a test-bed for NPC scripting and to force myself to start doing some tiles. It already showed me some features which were missing on the server to allow effective script-writing.
Wednesday, January 30, 2013
How EVE Online deals with 3000 player battles
I read an interesting article about how Eve Online handles the server load caused by an unusual high number of players in the same area.
Their main trick is to slow down the gameplay when the server is overloaded (likely by reducing the number of ticks per second on the server). I could easily do the same, I just have to modify a few places where I assume that the tick rate is globally identical and I would have to inform the client that everything now moves slower. But I wonder if this would really reduce the load much in my case, because I assume that a player which has more time to do things will also try to do more things, and they will still send move- and action commands with the same real-time frequency.
Another thing they mentioned is the ability to temporarily move areas from a weak cluster machine to a dedicated server with much stronger hardware, but unfortunately this has a critical implementation flaw: It disconnects everybody on the server. It also doesn't happen automatically and needs to be triggered by an admin.
Currently my game is not cluster-ready, but in the long term I plan to make it possible to put each map instance on a different server, preferably fully-automatic to keep the administrative overhead low (I keep this in mind, but I won't do anything in this regard until I have a player base with a size which actually justifies the effort). When I do that, I should look into making it possible to also transfer the running instance together with its players to a different server, preferably without them noticing and fully-automatic.
Their main trick is to slow down the gameplay when the server is overloaded (likely by reducing the number of ticks per second on the server). I could easily do the same, I just have to modify a few places where I assume that the tick rate is globally identical and I would have to inform the client that everything now moves slower. But I wonder if this would really reduce the load much in my case, because I assume that a player which has more time to do things will also try to do more things, and they will still send move- and action commands with the same real-time frequency.
Another thing they mentioned is the ability to temporarily move areas from a weak cluster machine to a dedicated server with much stronger hardware, but unfortunately this has a critical implementation flaw: It disconnects everybody on the server. It also doesn't happen automatically and needs to be triggered by an admin.
Currently my game is not cluster-ready, but in the long term I plan to make it possible to put each map instance on a different server, preferably fully-automatic to keep the administrative overhead low (I keep this in mind, but I won't do anything in this regard until I have a player base with a size which actually justifies the effort). When I do that, I should look into making it possible to also transfer the running instance together with its players to a different server, preferably without them noticing and fully-automatic.
Saturday, January 26, 2013
Procedurally generated shop content
Item shops can now have procedurally generated stock. I also improved the user interface by adding scrollbars to shops, showing the rarity as the item name color and adding tooltips. Shopping now works adequately.
Friday, January 25, 2013
Selling implemented
I added the selling gui. Now all that is missing for the item milestone is stack splitting/merging and tooltips in the buy- and sell GUIs.
By the way: I need to fix the price calculation of spells. The high-level ones should be a lot more expensive. But in order to do that I need to come up with a wealth-per-level formula to make up good prices.
Labels:
client
Sunday, January 20, 2013
Wisdom capacity implemented
I was already considering to can this feature just because I didn't find an elegant way to implement it on the server (bad software developer), but I finally found a nice way to do it. The purple bar below the quickbar now shows how much wisdom capacity your character is using.
The amount of different spells you can have in your quickbar depends on your wisdom stat. Currently you can switch you hotkey assignment anywhere, but I plan to restrict this to towns, so you have to choose carefully which spells to assign before wandering into the world. That means a high wisdom stat is a prerequisite for a wizard who wants to be flexible.
Each spell uses level / 10 + rarity wisdom capacity. That means that rare spells use a lot more capacity than common spells.
The amount of different spells you can have in your quickbar depends on your wisdom stat. Currently you can switch you hotkey assignment anywhere, but I plan to restrict this to towns, so you have to choose carefully which spells to assign before wandering into the world. That means a high wisdom stat is a prerequisite for a wizard who wants to be flexible.
Each spell uses level / 10 + rarity wisdom capacity. That means that rare spells use a lot more capacity than common spells.
Friday, January 18, 2013
Rest system number crunching
I did some more number crunching with the rest system, and noticed the two constants which are most important:
1. the base of the logarithm
2. the reduction factor per minute
The reduction factor - how many percent of rest the character loses in each minute where it does something exp-worthy - is most important to define the "cutoff duration" - the length of a play session after which the exp bonus becomes negligible. Due to the exponential decrease it doesn't matter much with how much rest a character starts out. This affects the duration just by some minutes. A reduction by 1% makes it possible to play from dawn till tusk until the bonus falls below 1.5%. A reduction by 2% gives you an afternoon of good exp. With 3% reduction, it's about an evening. When I put it up to 10%, the fun is over after less than an hour. When I set it to a whooping 50%, you burn through your exp bonus in 10 minutes.
a lower base of the logarithm makes the rest system more effective overall, especially after longer breaks. The lower the base of the logarithm, the higher the rest bonus a character starts with after a break.
1. the base of the logarithm
2. the reduction factor per minute
The reduction factor - how many percent of rest the character loses in each minute where it does something exp-worthy - is most important to define the "cutoff duration" - the length of a play session after which the exp bonus becomes negligible. Due to the exponential decrease it doesn't matter much with how much rest a character starts out. This affects the duration just by some minutes. A reduction by 1% makes it possible to play from dawn till tusk until the bonus falls below 1.5%. A reduction by 2% gives you an afternoon of good exp. With 3% reduction, it's about an evening. When I put it up to 10%, the fun is over after less than an hour. When I set it to a whooping 50%, you burn through your exp bonus in 10 minutes.
a lower base of the logarithm makes the rest system more effective overall, especially after longer breaks. The lower the base of the logarithm, the higher the rest bonus a character starts with after a break.
Thursday, January 17, 2013
Rest system implemented
I implemented my rest system the way I described. I still wonder if my rest formulas weren't a bit too conservative. I feel like my character should have more rest bonus after 4 offline days. Especially considering that a character which rested for 20 hours already got a bonus of 2.1.
On an unrelated note, I am making good progress with the animation system. The engine should be ready for turning and animated sprites soon.
Selling implemented
I implemented selling on the server-side. I actually did several days ago, but I forgot to post about it. Now I just need a GUI on the client for selling.
Labels:
server
Thursday, January 3, 2013
The day I discovered context.shadowBlur
Today I discovered the canvas shadow effect, which can be used to create a glow effect when drawing shapes or sprites. I used it to make the range marker look a lot better and to add a highlight to the object which is currently under the mouse cursor. The latter is an important usability improvement, because without it, it was really hard to pick individual objects when there were multiple ones overlapping each other.
Labels:
client
Wednesday, January 2, 2013
Range marker and damage types
When the player is selecting a target for a spell, the range of the spell is now displayed in form of a circle. This is an important usability improvement, because there was no indication which targets are in range and which are not.
Another usability improvement I added, are damage numbers which indicate the element of the damage with their color. Also, ethereal damage is now shown with a "mp" suffix and healing with a "+" prefix.
I added a rotating heptagram to the circle as a little gimmick. I am not sure if I am going to keep it. It seems distracting without adding much.
Another usability improvement I added, are damage numbers which indicate the element of the damage with their color. Also, ethereal damage is now shown with a "mp" suffix and healing with a "+" prefix.
I added a rotating heptagram to the circle as a little gimmick. I am not sure if I am going to keep it. It seems distracting without adding much.
Labels:
client
Sunday, December 30, 2012
New and improved quest API?
The current concept for scripted NPCs has various shortcommings. When I want to implement scripted quests with interesting tasks, I also need to add scripting abilities to killed monsters, item pickups, map areas and lots of other stuff. All of that wouldn't hurt, but it makes programming of a complex quest awkward, because the code is in all sorts of different places. It would be better when it would be possible to implement a quest from start to finish in a single file with no or minimal editing to the entities which are part of the quest.
So I thought that it might be better to define each quest as a list of quest steps. I would have a global ScriptManager, which manages the current quest of each player character which is currently ingame. When some event happens somewhere which could be relevant to a quest of a player character, an InterMessage is created and caught by the ScriptManager. It then checks the current quest step of the player. When it is resolved, a script function attached to the QuestStep is executed. This script can either return a new QuestStep, or nothing to end the quest.
Quests are started by scripts. Usually during talking to NPCs, but I could imagine other situations which trigger quests. NPCs should gain a "quest hint" property, which consists of quest ID, a minimum level, a variable and its value. When a player fulfills these criteria, has no active quest, and hasn't completed that quest yet (which should be indicated by the value of the variable), the NPC should be highlighted.
These are possible types of quest steps:
QuestStepKill
Kill a mob spawned by a specific SpawnArea.
QuestStepCollect
Own a specific number of items.
QuestStepExplore
Get to a certain location.
QuestStepNpc
Interact with a specific NPC.
QuestStepScripted
Wait for a certain variable to change to a specific value.
Players should also be able to abort a quest at any time to start a new one. When a player does that, the quest needs to be started from the beginning.
So I thought that it might be better to define each quest as a list of quest steps. I would have a global ScriptManager, which manages the current quest of each player character which is currently ingame. When some event happens somewhere which could be relevant to a quest of a player character, an InterMessage is created and caught by the ScriptManager. It then checks the current quest step of the player. When it is resolved, a script function attached to the QuestStep is executed. This script can either return a new QuestStep, or nothing to end the quest.
Quests are started by scripts. Usually during talking to NPCs, but I could imagine other situations which trigger quests. NPCs should gain a "quest hint" property, which consists of quest ID, a minimum level, a variable and its value. When a player fulfills these criteria, has no active quest, and hasn't completed that quest yet (which should be indicated by the value of the variable), the NPC should be highlighted.
These are possible types of quest steps:
QuestStepKill
Kill a mob spawned by a specific SpawnArea.
QuestStepCollect
Own a specific number of items.
QuestStepExplore
Get to a certain location.
QuestStepNpc
Interact with a specific NPC.
QuestStepScripted
Wait for a certain variable to change to a specific value.
Players should also be able to abort a quest at any time to start a new one. When a player does that, the quest needs to be started from the beginning.
Wednesday, December 26, 2012
Tuesday, December 18, 2012
Admin action logging
I am now logging which admin command is used by which user and when, what the arguments where and how the server replied. I only log the commands which require a special permission role, to avoid spam from normal users.
Labels:
server
Monday, December 17, 2012
Permission roles
I added role-based permission handling for chat commands.
I got a json file "permission_roles.json" which defines the commands which can be used by each role. There is also an implicitely declared role "sysadmin" which allows to use any command.
The roles each user has are stored in the player object on the database. Direct database manipulation is currently the only way to add or remove roles (which is - as I found out - a very dangerous method. I accidently destroyed my favorite character on the database while attempting to add a role with the update command of the mongo shell). In addition to the roles in the database, every character is also considered to have the role "everyone".
eAthena and Manaserv handle permissions per account instead of per character. I decided to use characters instead, because:
I got a json file "permission_roles.json" which defines the commands which can be used by each role. There is also an implicitely declared role "sysadmin" which allows to use any command.
The roles each user has are stored in the player object on the database. Direct database manipulation is currently the only way to add or remove roles (which is - as I found out - a very dangerous method. I accidently destroyed my favorite character on the database while attempting to add a role with the update command of the mongo shell). In addition to the roles in the database, every character is also considered to have the role "everyone".
eAthena and Manaserv handle permissions per account instead of per character. I decided to use characters instead, because:
- I don't want GMs to irritate me and the players by doing GMing with their alt characters
- I am still considering to merge accounts and characters. Any functionality added to the Account class would make this more complicated.
Labels:
server
Bug squishing
I squished lots of small bugs in the past days:
- Current HP and MP are now saved to database, so players don't regenerate fully when logging out and in again
- Route finding isn't even attempted when destination is non-walkable. So when a character gets stuck in a blocked area, it can't walk around while staying in the same map participation rectangle (it would be better when they couldn't get stuck there in the first place, but that's another topic)
- Floor item stacks now have a quantity
- Name labels don't get detached from their objects anymore (I am drawing them on the canvas again - but now I am using caching to increase text drawing performance)
- Improved rendering performance by using insertion sort for sorting beings by draw order instead of whatever is the default sorting algorithm of the browser (the beings are only slightly out of order most of the time - in that special case insertion sort greatly outperforms all other standard sorting algorithms).
- Fixed the problem with newly created characters not showing up until the next login.
- Generated spell names: removed double space after level.
Wednesday, November 28, 2012
Script for the tutorial
- player learns how to equip an attack spell
- player kills some non-aggressive, they drop a piece of equipment
- player learns about equipment
- player fights some aggressive enemies
- player gets a healing spell and is instructed to heal to full hp
- player learns about trading by selling the loot of the monster they just fought and buying a barrier spell
- player is instructed to use the barrier spell to survive a gauntlet-like scenario
Labels:
concept
Friday, November 23, 2012
Procedural spells part 2 (gallery)
Here are some procedurally generated spells:
This is a common firebolt of rarity level 0. Its MP cost is slightly lower and its damage and range are a tiny bit above normal. But it's still pretty average. It serves as a baseline for the more interesting, rarer, spells below.
Here is a rarity 2 Fire Blast spell. Blasts are deluxe-bolts. They are more powerful, but harder to cast. This one has a whooping 60% more damage without even costing more mana, but pays for it with a longer casting time. A great combat opener.
This Fire Trick spell has reduced range, but a very short casting time. This makes it possible to spam it in close combat. A good damage dealer.
The higher the rarity, the more interesting do the spells become. This rare-grade (rarity 4) fire strike got the "ethereal" modifier. That means that it reduces the targets MP instead of HP. It won't kill the enemy, but it will prevent it from casting spells. It also comes with a great range and good damage. And it's also pretty low-cost. This makes it even better suited for MP warfare. Too bad that it has such a long casting time.
I used to be a wizard like you, but then I got a fire arrow to the knee. Pretty great range and spam-able. Unfortunately it's very mana-hungry.
Rare means "interesting", but not necessarily "good". This rarity 5 spell has great damage and is really cheap to cast. But it has a very long casting time and a low range. Any smart enemy will be out of its range when it's finished. Still, a very economical way to get rid of dumb enemies.
Now this one is cool: A splash damage spell. It damages enemies in a radius of 2 tiles. And it has a great range to boot.
And that was just the very first version of my spell generator. A lot more special abilities will be added for even more interesting strategies.
This is a common firebolt of rarity level 0. Its MP cost is slightly lower and its damage and range are a tiny bit above normal. But it's still pretty average. It serves as a baseline for the more interesting, rarer, spells below.
Here is a rarity 2 Fire Blast spell. Blasts are deluxe-bolts. They are more powerful, but harder to cast. This one has a whooping 60% more damage without even costing more mana, but pays for it with a longer casting time. A great combat opener.
This Fire Trick spell has reduced range, but a very short casting time. This makes it possible to spam it in close combat. A good damage dealer.
The higher the rarity, the more interesting do the spells become. This rare-grade (rarity 4) fire strike got the "ethereal" modifier. That means that it reduces the targets MP instead of HP. It won't kill the enemy, but it will prevent it from casting spells. It also comes with a great range and good damage. And it's also pretty low-cost. This makes it even better suited for MP warfare. Too bad that it has such a long casting time.
I used to be a wizard like you, but then I got a fire arrow to the knee. Pretty great range and spam-able. Unfortunately it's very mana-hungry.
Rare means "interesting", but not necessarily "good". This rarity 5 spell has great damage and is really cheap to cast. But it has a very long casting time and a low range. Any smart enemy will be out of its range when it's finished. Still, a very economical way to get rid of dumb enemies.
Now this one is cool: A splash damage spell. It damages enemies in a radius of 2 tiles. And it has a great range to boot.
Spells can have more than one special ability, like this rarity 6 spell. The combination of ethereal and vampiric is special: It doesn't destroy MP, it steals MP. You've spotted a target, but you are too low on MP to engage it? No problem, with the reduced casting time and godly range of this spell, you can steal all MP from an enemy before it even notices you. And then you can kill your helpless victim with its own MP.
And that was just the very first version of my spell generator. A lot more special abilities will be added for even more interesting strategies.
Wednesday, November 21, 2012
Procedural spells part 1
I made the first step of procedurally generated spells.
There is no longer a list of learned spells. Instead of that, the player drags spell scrolls directly into the quick bar. Each spell scroll is now an individual item. The attributes of the action it performs are now stored in the database.
Now I just need the code to generate spell scrolls with randomized action attributes. This is going to be interesting...
The client still needs work. Dragging scrolls into the quickbar and using them is technically functional, but the spells in the quick bar don't have icons yet.
There is no longer a list of learned spells. Instead of that, the player drags spell scrolls directly into the quick bar. Each spell scroll is now an individual item. The attributes of the action it performs are now stored in the database.
Now I just need the code to generate spell scrolls with randomized action attributes. This is going to be interesting...
The client still needs work. Dragging scrolls into the quickbar and using them is technically functional, but the spells in the quick bar don't have icons yet.
Labels:
server
Thursday, November 15, 2012
Get rid of accounts?
After I moved into my new apartment I again have some spare time for my game. I wonder if I should maybe get rid of accounts and instead have passwords for the characters themselves. Really, what do I need accounts for? I don't plan to bill by account, and when a player wants more than one character, they can just register a second one. Having accounts just adds a whole lot more complexity without actually adding much value.
Any per-account limitations I could enforce would just cause people to register multiple accounts to circumvent them. Any per-account perks for players, like easier item exchange or easy switching between characters, could be substituted by adding a "link character" feature. You log into characer A, click on "link account" and enter the name and password of character B. Now the two characters - as well as any characters already linked to them, are then considered to be owned by the same player. The player can then switch between them without needing a password and can easily exchange items between them.
This would allow to have "lazy registration" like, for example, in forumwarz. The player can play the tutorial without registration and is prompted to register after completing it. This results in a really low entry barrier.
Any per-account limitations I could enforce would just cause people to register multiple accounts to circumvent them. Any per-account perks for players, like easier item exchange or easy switching between characters, could be substituted by adding a "link character" feature. You log into characer A, click on "link account" and enter the name and password of character B. Now the two characters - as well as any characters already linked to them, are then considered to be owned by the same player. The player can then switch between them without needing a password and can easily exchange items between them.
This would allow to have "lazy registration" like, for example, in forumwarz. The player can play the tutorial without registration and is prompted to register after completing it. This results in a really low entry barrier.
Labels:
concept
Friday, October 19, 2012
Roadmap update
I hadn't had a roadmap update in a while, so time for a new one.
Milestone 8 (wealth):
Milestone 9 (adventuring):
Refinement 1: HTML and CSS
The newest beta of my favorite browser Opera finally supports real WebSockets. Unfortunately there are a lot of layout problems (unrelated to canvas support). I need to get the UI to render properly on it, too. There are also some other layout problems, like the inventory grid not being exactly square in all situations.
Refinement 2: Movement
Since I declared my space partitioning and my route finding algorithm for working, I encountered some glitches which need to be addressed.
Refinement 3: Performance
The client framerate fell a lot lately. I need to find out why and optimize it so I get at least 20 Fps on my Intel Atom notebook.
Refinement 4: Game System
Refinement 5: Mob AI
I skipped a few AI strategies I had planned. These still need to be implemented:
Refinement 6: Attract Mode
Observer random locations in the background during the login sequence.
Milestone 8 (wealth):
Items and Inventory-
Dropping items - Splitting and combining item stacks
Player tradingTrading cashShopsSelling itemsItem stack sizes in trade windowItem information system
NPC interactionScripting
Permission management- Database logging of
LoginsAdministrative actions- Usage statistics
Prevent multi-logins- Brute force protection
- Animated sprites
- Animated tiles
- Particle effects
Refinement 2: Movement
Since I declared my space partitioning and my route finding algorithm for working, I encountered some glitches which need to be addressed.
- Suboptimal routes in some situations
- wrong blockmap calculation in some cases
- one time I got stuck in a tree - reproduce this (or at least create automatic unstuck)
Refinement 3: Performance
The client framerate fell a lot lately. I need to find out why and optimize it so I get at least 20 Fps on my Intel Atom notebook.
Refinement 4: Game System
- Resistances
- Damage randomization
- Misses? Critical hits?
Proceduarally generated spellsRest System
Refinement 5: Mob AI
I skipped a few AI strategies I had planned. These still need to be implemented:
- Attack strategies:
- Pursuit
- Stalk
- Flee
- Roam strategies:
- Guard
- Target strategies:
- Best attacker against team
- Closest to team
- Last attacker
- Last attacker of team
Refinement 6: Attract Mode
Observer random locations in the background during the login sequence.
Thursday, October 18, 2012
Item code improvements
I made a lot of under-the-hood improvements to the inventory handling code on the server. I made it more robust by throwing exceptions when an operation isn't possible instead of just returning null or false. I also added a crude transaction system in order to be able to easily roll-back inventories should an error occur during trade completion. It's crude in the sense that it creates a complete backup copy of the inventory content, and in case of a rollback it empties the content and restores it from the copy. I could do this more elegantly when I would invest some time, but the code isn't performance-critical and I want something simple which doesn't leave much room for bugs.
Any glitches in the inventory code could cause serious, permanent harm in the game by duplicating or destroying valuable items (and I already found and fixed bugs which caused both), so the quality of the inventory handling code is very important.
Any glitches in the inventory code could cause serious, permanent harm in the game by duplicating or destroying valuable items (and I already found and fixed bugs which caused both), so the quality of the inventory handling code is very important.
Labels:
server
Rest system
Lately I was again thinking about how I can make my game attractive to those players who already have jobs and thus can't treat an online game like one.
To get some mental input on this I asked this question on gamedev.stackexchange.com. I was actually pretty surprised to get an answer from someone like Josh Petrie, a Guild Wars 2 developer. But the other people who answered and commented also had some interesting points.
I decided that the first thing I will do to level the playing fields will be a rest system. After a lot of number crunching in LibreOffice Calc, I came up with a rest system which works like this:
For every minute - online or offline - your character gets a "Luck" point (60 per hour, 1440 per day, 10080 per week). Your character has a luck bonus which is equal to log10(luck)-1.
All gains of skill EXP and SP are multiplied with your current luck bonus. The chance to get rare drops is also increased: Whenever you kill a monster, you roll ceil(luck_bonus) times for each drop. One successful roll is enough to make it drop. When multiple people kill a monster, the best luck bonus of all participants is used.
Every minute where at least one event occurs which uses the luck bonus, the luck points are reduced by 1% (that means your luck will never fall below 100, so your luck bonus will never be below 1).
So when you go to sleep at 10pm and continue the next evening at 8pm (after 22 hours) you will have 1300 luck points, which means that you will get a x2.1 multiplier for a while. After your 120 minute session, you will have about 400 luck points left, so your bonus will have dimished a bit to x1.6. These 400 points will roll over to the next day, where you will have a multiplier of 2.3.
Let's compare that to an unemployed power gamer, who will play 8 hour session, interrupted by 16 hours of rest. He will have 960 luck points the next morning, which is still a bonus of 1.98, not notably worse than yours. But after four hours, his luck bonus will be depleted and his exp rate won't get far above x1. It won't be above 2 the next day either.
Variables for balancing:
To get some mental input on this I asked this question on gamedev.stackexchange.com. I was actually pretty surprised to get an answer from someone like Josh Petrie, a Guild Wars 2 developer. But the other people who answered and commented also had some interesting points.
I decided that the first thing I will do to level the playing fields will be a rest system. After a lot of number crunching in LibreOffice Calc, I came up with a rest system which works like this:
For every minute - online or offline - your character gets a "Luck" point (60 per hour, 1440 per day, 10080 per week). Your character has a luck bonus which is equal to log10(luck)-1.
All gains of skill EXP and SP are multiplied with your current luck bonus. The chance to get rare drops is also increased: Whenever you kill a monster, you roll ceil(luck_bonus) times for each drop. One successful roll is enough to make it drop. When multiple people kill a monster, the best luck bonus of all participants is used.
Every minute where at least one event occurs which uses the luck bonus, the luck points are reduced by 1% (that means your luck will never fall below 100, so your luck bonus will never be below 1).
So when you go to sleep at 10pm and continue the next evening at 8pm (after 22 hours) you will have 1300 luck points, which means that you will get a x2.1 multiplier for a while. After your 120 minute session, you will have about 400 luck points left, so your bonus will have dimished a bit to x1.6. These 400 points will roll over to the next day, where you will have a multiplier of 2.3.
Let's compare that to an unemployed power gamer, who will play 8 hour session, interrupted by 16 hours of rest. He will have 960 luck points the next morning, which is still a bonus of 1.98, not notably worse than yours. But after four hours, his luck bonus will be depleted and his exp rate won't get far above x1. It won't be above 2 the next day either.
Variables for balancing:
- Increase the percentual luck reduction to make the multiplier go down faster
- Increase the luck gain per minute to increase the exp multiplier after shorter rests
- Reduce the base of the logarithm to increase the exp multiplier after longer rests
Friday, October 12, 2012
Network session logging
A small change, but one I could be very grateful for in the future: I added logging of sessions on the database. Every connection to the server is now logged with:
- Time of connect
- IP address
- When it's a websocket connection:
- HTTP headers sent by client (including User-Agent)
- Whether or not this header was accepted
- Accounts the user logged into successfully (maybe I should log failed attempts, too?)
- Time of disconnect
Labels:
server
Sunday, October 7, 2012
Items on the floor
I added floor items. When a monster dies, the dropped items are now laying around in the game world. Players can pick them up by clicking on them.
I still need a way to drop items from the inventory.
Procedurally generated spells?
I played a lot of Borderlands 2 in the past weeks (great game, by the way) and noticed that the procedurally generated weapons are the greatest motivator in that game. I wondered if I should do something similar with the spells. Instead of having a number of predefined spells, players could find scrolls with spells which have randomized attributes.
Spells aren't learned anymore. Instead of that spells are items which are dragged&dropped in and out of the spellbook while in towns. Spells will still have a wisdom capacity they use and swapping will be impossible outside of towns, so players need to think carefully about how to design their spell palette. When each spell is unique and can only be used by one character, each character will have an unique playing style.
Balancing this will be really hard, but in the end it could be really worth it.
Input for randomization:
To make all this a bit easier to balance, I could use a "building block" system where each spell has some randomized basic attributes and then gets a number of random blocks which modify it further.
Spells aren't learned anymore. Instead of that spells are items which are dragged&dropped in and out of the spellbook while in towns. Spells will still have a wisdom capacity they use and swapping will be impossible outside of towns, so players need to think carefully about how to design their spell palette. When each spell is unique and can only be used by one character, each character will have an unique playing style.
Balancing this will be really hard, but in the end it could be really worth it.
Input for randomization:
- Element
- Skill level requirement (primary indicator of the "power level")
- Rarity
- Rarity means more special effects, better chance for a more obscure area of effect, more extreme values for range and casting time and a chance for more damage.
- Damage (proportional to level requirement)
- Range
- Casting time (lower is better)
- Area of effect
- Single target (most frequent)
- Splash damage with range X
- Splash damage with range X, targets the floor
- Line damage (damages anything in a direct line between caster and target)
- Chain damage (damages X enemies, each apart a distance of Y)
- Special effects
- Vampiric (steals HP)
- Ethereal (damages MP instead of HP)
- DoT (when combined with splash damage, it's an area denial spell)
- Causes status effects
- Curse (stat debuff)
- Snare (reduce move speed)
- Silence
- Mana cost
- Wisdom cost
- NPC value
To make all this a bit easier to balance, I could use a "building block" system where each spell has some randomized basic attributes and then gets a number of random blocks which modify it further.
- Start out with normal mana cost, damage, wisdom cost and range.
- Multiply mana cost, wisdom cost and damage with the level.
- Randomize all attributes just a tiny bit for some micro-variety
- For each rarity level:
- Do a "trade-off" - make one attribute worse by 25%, improve another by 25%
- Calculate base name based on the trade-offs made
- Good damage: Blast
- Good range: Arrow
- Good damage, bad range: Shock
- Good range, bad damage: Hex
- Good damage and range: Strike
- Bad damage and range: Trick
- Normal damage and range: Bolt
- For each rarity level above 1, do one of these bricks (discard duplicates). Each one increases cast time, mana cost and wisdom cost by a different amount.
- Change the AoE (overrides the base name)
- Make vampiric
- Make ethereal
- Add DoT
- Cause a status effect on the enemy (multiple different ones are allowed, but not the same multiple times)
- Generate the name: [adjectives for bricks] [element] [base name]
Sunday, September 30, 2012
Large scale trading
Player/player trading is definitely a must-have because it allows to pass items in-the-field between party members.
But when it comes to facilitating a large scale ingame economy driven by supply and demand, this tool quickly gets to its limits. It's hard to find out who buys and sells what for which price. Players will have to help themselves by creating an external trade platform like a marketplace forum or (when there are some skilled but bored web developers in the community) a marketplace website. And even when people make deals via this platform, they still need to arrange an ingame meeting to fulfill the deal.
To make it easier to trade items with each other, I need an ingame trade platform which allows the players to:
1. commodity items which are in high supply and demand
2. unique items
For commodity items, the price is the result of the current supply and demand in the community. They are often bought or sold in bulk quantities. For trading these items, I would like to create a stock-exchange like system where players place buy- and sell orders ("buy 25 ruby for 50 bugs each"). Like a real commodity market, the system automatically matches these orders and publishes the current price as a guide for buyers and sellers.
For unique items, this approach wouldn't be feasible, because they aren't so interchangeable that you could place a buy offer for them ("buying robe for lvl 25+ with at least 20 fire defense and +10 on ice magic skill and not too high weight and a beautiful color would be nice, too"?). So I would rather prefer an auction-house approach for selling items where players can offer items they found/made and other players can place offers for them.
There are a lot of interesting approaches to auctions, like the Vickery auction or the Dutch auction which might be interesting to try, but the eBay system where sellers place items with a minimum and maximum (instant-buy) price is so widely used that players might expect this system. I could still try to offer all of these auction variants in the same interface, though. The Vickery variant is especially interesting to me, because it doesn't discriminate against casual gamers who only check the market every few days and don't have the time to log in every hour to check that nobody overbid them.
But when it comes to facilitating a large scale ingame economy driven by supply and demand, this tool quickly gets to its limits. It's hard to find out who buys and sells what for which price. Players will have to help themselves by creating an external trade platform like a marketplace forum or (when there are some skilled but bored web developers in the community) a marketplace website. And even when people make deals via this platform, they still need to arrange an ingame meeting to fulfill the deal.
To make it easier to trade items with each other, I need an ingame trade platform which allows the players to:
- place offers
- browse other players offers (smart searching would be a plus)
- accept other players offers and perform the transaction, even when the other party is offline
1. commodity items which are in high supply and demand
2. unique items
For commodity items, the price is the result of the current supply and demand in the community. They are often bought or sold in bulk quantities. For trading these items, I would like to create a stock-exchange like system where players place buy- and sell orders ("buy 25 ruby for 50 bugs each"). Like a real commodity market, the system automatically matches these orders and publishes the current price as a guide for buyers and sellers.
For unique items, this approach wouldn't be feasible, because they aren't so interchangeable that you could place a buy offer for them ("buying robe for lvl 25+ with at least 20 fire defense and +10 on ice magic skill and not too high weight and a beautiful color would be nice, too"?). So I would rather prefer an auction-house approach for selling items where players can offer items they found/made and other players can place offers for them.
There are a lot of interesting approaches to auctions, like the Vickery auction or the Dutch auction which might be interesting to try, but the eBay system where sellers place items with a minimum and maximum (instant-buy) price is so widely used that players might expect this system. I could still try to offer all of these auction variants in the same interface, though. The Vickery variant is especially interesting to me, because it doesn't discriminate against casual gamers who only check the market every few days and don't have the time to log in every hour to check that nobody overbid them.
Labels:
concept
Wednesday, September 19, 2012
NPC shops
Looking at the screenshot I also notice that I mix HTML buttons with my own "link with background color and borders" buttons. I need to get rid of one of these methods to get a more consistent interface look.
Tuesday, September 18, 2012
NPC dialogs
I am still not finished with the item milestone, but I had some momentum I didn't want to waste. So last night I implemented NPC scripting like described in my previous posting and today I wrote the GUI part of it. The text in the GUI is fully HTML enabled, so I will be able to embed decorated text, images, links and other shenanigans in NPC dialogs. I could even put in some script tags or javascript: links. This could prove valuable during tutorials to automatize the GUI. I just have to be weary of XSS injections when I integrate any user-generated stuff in NPC texts.
Now I just need some way to place NPC's on maps and I am finished with Milestone 9 before finishing Milestone 8.
How could I do NPC scripting II
A while ago I already wrote an article about how I could define NPC dialogs with JSON. Looking back at it now it's just awful. It's convoluted, error-prone, unreadable and not even very flexible. I got a Javascript engine, so why not do as much with Javascript as possible?
As any reader with Javascript knowledge will notice,
So how does the dialog system work?
Each NPC dialog is an array of Event objects. Each Event object gets an additional init function after creation. When a player talks to an NPC, the server calls event[0].init to set up some variables inside the event object. Then it sends the event to the client. When the init function added one or more dialog options using this.addOption, the client can reply with the option it wants to use. When it does, the event with the event number passed to the corresponding addOption() call is processed.
The Event object:
text: HTML which is shown in the main body of the NPC dialog window.
portrait: URL of an image which is shown in the corner of the NPC dialog. When undefined, the portrait of the last event is used.
addOption(text, event): makes the client shows text as a hyperlink at the end of the dialog text. When the user clicks on this link, the client informs the server about which option was selected. The server then processes the corresponding event. When event is undefined, the dialog ends when this option is selected.
next: Event which is processed next when no option is selected. This changes the 'quit' button below the dialog window to a 'next' button.
EVENT_0 {
this.text = 'Hello.<br/>How can I help you?';
this.portrait = 'girl.png';
this.addOption('Where is the castle?', 1);
this.addOption('I need a job', 2);
this.addOption('Nothing, goodbye', 3);
}
EVENT_1 {
this.text = 'Just go down the road, you can\'t miss it.';
}
EVENT_2 {
switch(user.getVar('quest_slay_dragon')) {
undefined:
this.text = 'I got a quest for you.<br/>Kill the dragon in the mountain.';
user.setVar('quest_slay_dragon', 1);
break;
1:
this.text = 'Did you already slay the dragon?';
this.addOption('Errr...');
this.addOption('Well...');
this.addOption('Uh...');
break;
2:
this.text = 'Great, you killed the dragon. Here is your reward.';
user.addMoney(10000);
user.setVar('quest_slay_dragon', 3);
break;
3:
this.text = 'Sorry, I got no more work for you.
break;
}
}
EVENT_3 {
this.text = 'It was a pleasure to meet you.';
if (user.getVar('quest_slay_dragon') == 1)
this.text += ' And good luck with the dragon.';
}
This does even more than the JSON dialog before with less lines. It is not only much more readable, it also allows to utilize the full power of Javascript to make anything conditional or procedurally generated.As any reader with Javascript knowledge will notice,
EVENT_0 {
isn't valid Javascript syntax.The great thing is that I can do some preprocessing on the Java side before passing the code to Rhino. So I can expand it to:
event[0] = new Event();
event[0].init = function(user) {
So how does the dialog system work?
Each NPC dialog is an array of Event objects. Each Event object gets an additional init function after creation. When a player talks to an NPC, the server calls event[0].init to set up some variables inside the event object. Then it sends the event to the client. When the init function added one or more dialog options using this.addOption, the client can reply with the option it wants to use. When it does, the event with the event number passed to the corresponding addOption() call is processed.
The Event object:
text: HTML which is shown in the main body of the NPC dialog window.
portrait: URL of an image which is shown in the corner of the NPC dialog. When undefined, the portrait of the last event is used.
addOption(text, event): makes the client shows text as a hyperlink at the end of the dialog text. When the user clicks on this link, the client informs the server about which option was selected. The server then processes the corresponding event. When event is undefined, the dialog ends when this option is selected.
next: Event which is processed next when no option is selected. This changes the 'quit' button below the dialog window to a 'next' button.
Labels:
scripting
Subscribe to:
Posts (Atom)
























