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.
Sunday, December 30, 2012
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.
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.
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
Some new spell icons
Here are the icons for the fire spells.
Saturday, November 24, 2012
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.
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.
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.
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
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.
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.
Monday, September 17, 2012
Progress with trading
The GUI for trading between players is making slow but steady progress. A trade can be started from this neat context menu which appear when the user clicks on another character ("whisper" and "info" are still unimplemented):
I am now able to do the complete trade from start to finish. But there is still a lot to do. Money can not be traded. The icons of the traded items show up, but there is no way to see any info about them - not even their name or the quantity. Also, there is no visual feedback when the trade partner locks his side of the deal. I also found a very obvious exploit: it's possible to add an item multiple times to the trade. When the trade is completed, the item is duplicated. This needs to be blocked client- and server-sided.
Sunday, September 16, 2012
Player trading server-sided
I implemented player/player trading on the server. I implemented this by having a TradeController for each MapInstance, which manages Trade objects, which are state machines with these states:
REQUEST: A player has initiated a trade with someone else.
BARTER: The other player has agreed to do trading and the players are adding and removing items. They can lock and unlock their side of the bargain to signal that their offer is complete.
CONFIRM: When both players have locked their offer, they both have to confirm the deal. Both can unlock their offer to return to the BARTER state. This also removes the lock of the other party and any confirm which has already happened.
FINISHED: When both players have confirmed, the trade contents are again checked against the inventory to make sure that no offered item got lost during the trade. Then the trade is processed.
I hope that I remembered to check all possible error conditions. Otherwise I could have unintentional trades at best and an abusable exploit at worst.
Next will be the GUI on the client-side.
REQUEST: A player has initiated a trade with someone else.
BARTER: The other player has agreed to do trading and the players are adding and removing items. They can lock and unlock their side of the bargain to signal that their offer is complete.
CONFIRM: When both players have locked their offer, they both have to confirm the deal. Both can unlock their offer to return to the BARTER state. This also removes the lock of the other party and any confirm which has already happened.
FINISHED: When both players have confirmed, the trade contents are again checked against the inventory to make sure that no offered item got lost during the trade. Then the trade is processed.
I hope that I remembered to check all possible error conditions. Otherwise I could have unintentional trades at best and an abusable exploit at worst.
Next will be the GUI on the client-side.
Monday, September 10, 2012
Tweaks to login dialogs
I made some tweaks to the login sequence. The main login prompt, while still looking the same to the user, now looks more like a form to the browser. This has the advantages that the password manager of the browser works and that the Enter key logs you in. Previously you had to click the login button with the mouse. That was mostly for my own convenience. I also improved the character selection. It's still not beautiful, but definitely less ugly. The user at least realizes what to click on.
I will leave the final CSS styling for shortly before launch when I finalized the overall graphic design of the game. The login sequence is the first impression the user has of the game. It must look as awesome as possible.
I will leave the final CSS styling for shortly before launch when I finalized the overall graphic design of the game. The login sequence is the first impression the user has of the game. It must look as awesome as possible.
Sunday, September 9, 2012
Procedurally generated equipment
I finally implemented unique items with attribute bonuses which are generated at runtime. After thinking a lot where to place the templates for unique items, I decided to place them right in the "drops" section of the mob descriptions.
On a related note, I watched Angry Joe's review of Guild Wars 2, which was quite ecstatic. He made a lot of good points about how Guild Wars 2 promotes teamplay over competition in PvE gameplay. The game automatically scales encounters with the number of players participating, which also increases the rewards. Rewards aren't divided - everyone gets the full share. So more players = more exp and loot = more fun. So I decided to reconsider some initial game design choices I made. Experience is now not divided between killers - everyone gets the full share. Also, spawn areas now have two optional parameters number_scale and power_scale, which increase the number and the power (hp and damage output) of newly spawned monsters when the number of players in the map instance increases. The power factor also increases the exp per monsters as well as the randomly generated attribute bonuses of items.
On a related note, I watched Angry Joe's review of Guild Wars 2, which was quite ecstatic. He made a lot of good points about how Guild Wars 2 promotes teamplay over competition in PvE gameplay. The game automatically scales encounters with the number of players participating, which also increases the rewards. Rewards aren't divided - everyone gets the full share. So more players = more exp and loot = more fun. So I decided to reconsider some initial game design choices I made. Experience is now not divided between killers - everyone gets the full share. Also, spawn areas now have two optional parameters number_scale and power_scale, which increase the number and the power (hp and damage output) of newly spawned monsters when the number of players in the map instance increases. The power factor also increases the exp per monsters as well as the randomly generated attribute bonuses of items.
Saturday, September 8, 2012
Current Equipment in Item Window
I added a section to the item window which shows the currently equipped items. The player can equip items by dragging them from the inventory to the equip silhouette, view their details by clicking on them and remove them by dragging them back into the inventory.
Equipment handling does now work adequately. Now I have to write some code on the server-side to create unique items which are actually unique.
Equipment handling does now work adequately. Now I have to write some code on the server-side to create unique items which are actually unique.
Thursday, September 6, 2012
Equipment
I decided to do equipment before I do trading.
I can now create unique items (well, "unique" is a stretch - the values are hard-coded placeholders), I can equip and unequip them which already modifies the stats of the characters and the current equipment is stored in the database. The new /shell command proved to be really handy during the development of these features, because it allows me to test things on the server I don't have netcode support for yet.
Still missing:
I can now create unique items (well, "unique" is a stretch - the values are hard-coded placeholders), I can equip and unequip them which already modifies the stats of the characters and the current equipment is stored in the database. The new /shell command proved to be really handy during the development of these features, because it allows me to test things on the server I don't have netcode support for yet.
Still missing:
- Communication of equipment changes
- Communication of attribute modifiers (this also affects status effects from items)
- netcode for equipping and unequipping
- and, of course, any GUI on the client
Saturday, September 1, 2012
Temporary status effects
The stats of player characters can now be modified by temporary or permanent status effects. This allows items which buff player characters, like the potion seen above. The effect requires four lines of script code:
"use":"var effect = new WizardwarStatusEffect(); effect.stat = WizardwarPlayer.Stats.INTELLIGENCE; effect.absolute = 10; user.addTemporaryStatusEffect(effect, 10);Maybe I should add an alternative way to define temporary status effects for consumable items to save me some typing.
Next up will be equipment. But I am still in the brainstorming phase for my ItemUnique class.
Wednesday, August 29, 2012
Some new script commands
I added three new script commands.
/players lists all online players with their locations.
/maps lists all active map instances with their player count and mob count.
So far, so boring. But the really powerful command I added is the /shell command. It allows to execute Javascript code on the server. So it can do virtually anything. This powerful tool will allow me to analyze and fix a lot of problems on the running server on-the-fly as well as do complex events. It is also extremely dangerous in the wrong hands and I see no way to add permission-based restrictions to it, so I definitely need to shield this command from unathorized use. I will also not use the existence of this command to refrain from adding more commands which do things which could theoretically also be done with /shell, so I won't be tempted to give the /shell command to people I don't trust 110%.
/players lists all online players with their locations.
/maps lists all active map instances with their player count and mob count.
So far, so boring. But the really powerful command I added is the /shell command. It allows to execute Javascript code on the server. So it can do virtually anything. This powerful tool will allow me to analyze and fix a lot of problems on the running server on-the-fly as well as do complex events. It is also extremely dangerous in the wrong hands and I see no way to add permission-based restrictions to it, so I definitely need to shield this command from unathorized use. I will also not use the existence of this command to refrain from adding more commands which do things which could theoretically also be done with /shell, so I won't be tempted to give the /shell command to people I don't trust 110%.
Saturday, August 25, 2012
Multiple logins no longer possible
The shopping task is still daunting me, so I did something else for now. Until now it was possible to log in multiple times with the same account and even multiple times with the same character. Now I finally prevented multiple logins per account. In order to do this I added a static OnlineManager class which keeps hash-maps of all logged-in accounts and characters with their names as keys. This will also be useful for the implementation of /commands and scripts which affect characters by name.
Friday, August 24, 2012
How to do shopping
The shopping window allows the player to both buy and sell items. This is an important dialog window. Making errors while buying or selling could cause some serious damage to the player. But on the other hand, players will use this dialog quite often for annoying but unavoidable routine tasks like getting rid of vendor trash or stocking up on stock consumables.
So how should I design this from a usability standpoint?
While this is pretty intuitive and places the bought items right where the player wants them, it makes it hard to buy whole stacks or sell partial stacks (maybe ask it with a popup window?). It's also hard to communicate how much money the player will make with selling an item.
This especially makes it more comfortable to replace equipment with more expensive one.
So how should I design this from a usability standpoint?
The one-click solution
I put a "buy" button and a "quantity" input field (default: 1) next to each item in the vendor menu. While the menu is open, a "sell" button with a "quantity"(default: current amount) is added to the item detail area.The drag&drop solution
Items are bought by dragging them from the vendor window to the inventory window. Items are sold by dragging them the other way.While this is pretty intuitive and places the bought items right where the player wants them, it makes it hard to buy whole stacks or sell partial stacks (maybe ask it with a popup window?). It's also hard to communicate how much money the player will make with selling an item.
The bargain solution
This would mostly use a dialog I plan to use for player/player trading. The player drags the items they want to sell or buy from their inventory and the vendors inventory into a box. The sum is calculated on both sides and the difference is listed below it. When the difference isn't larger than the money the player got, the buy button becomes active.This especially makes it more comfortable to replace equipment with more expensive one.
The Tibia solution
No, seriously. That's ridiculous for both usability and immersion. Whoever thought that using a chat parser to do NPC trading in a MMORPG would be a good idea, was either lazy, stupid or both.Thursday, August 23, 2012
Money
I added money. It is saved in the database, communicated and displayed in the clients item window. I even added a /command to change it. There is just nothing to do with it yet. Adding shopping will be my big challenge for the weekend.
Preventing data loss due to server bugs
When handling database entities like accounts or characters I used to parse the BSON object, build the Java object from it, and then discard the BSON object except for the _id. When saving the entity, I used to build an entirely new BSON object with the same _id and the data of the Java object and saved it to MongoDB. The drawback of that approach was that any information in the database which was not parsed, stored and written back by the Java object was lost on updates.
To make it less likely to lose data this way, I decided to carry the original BSON object around in the Java object and overwrite all known values with the current ones on saving. That way any data the server does not read is retained.
This does, however, not totally prevent data loss. When a bug causes the server to suddenly be unable to read a value but it is still able to write it, it will replace it with a placeholder value. It also doesn't prevent data loss when the server screws up a dataset during gameplay.
In order to do some damage control in such a case, I could save every object not just to the main collection, but also to a separate backup collection with a non-unique key consisting of name and timestamp. This would give me the opportunity to roll-back individual characters to a previous point in time, should the need arise.
To make it less likely to lose data this way, I decided to carry the original BSON object around in the Java object and overwrite all known values with the current ones on saving. That way any data the server does not read is retained.
This does, however, not totally prevent data loss. When a bug causes the server to suddenly be unable to read a value but it is still able to write it, it will replace it with a placeholder value. It also doesn't prevent data loss when the server screws up a dataset during gameplay.
In order to do some damage control in such a case, I could save every object not just to the main collection, but also to a separate backup collection with a non-unique key consisting of name and timestamp. This would give me the opportunity to roll-back individual characters to a previous point in time, should the need arise.
Wednesday, August 22, 2012
Item Window Enhanced
- Quantity is shown in grid and in detail area
- Total weight of stacks is calculated in detail area
- Selected item is highlighted
- Use button (only for items which can be used)
More about scripting
I am thinking about how to design a good scripting API. Currently, the PlayerCharacter object and the Item object are passed to item use scripts. Unfortunately the PlayerCharacter object can't do much, because most is done by the controllers owned by MapInstance. I can access the MapInstance through the getMapInstance method of the PlayerCharacter, but the map instance doesn't expose the controllers. And for good reason: the main way of communicating with them are messages.
Well. I could pass the message dispatcher to the scripts, but crafting messages is a tad too complex for a scripting API.
So what could I do?
I could add some methods to MapInstance specifically for scripting. I already did that for monster spawning. But that wouldn't make sense for things which have nothing to do with the current MapInstance. Teleporting a player to another map, for example.
Maybe another global object is in order which has static methods specifically for providing helper methods for scripts.
Well. I could pass the message dispatcher to the scripts, but crafting messages is a tad too complex for a scripting API.
So what could I do?
I could add some methods to MapInstance specifically for scripting. I already did that for monster spawning. But that wouldn't make sense for things which have nothing to do with the current MapInstance. Teleporting a player to another map, for example.
Maybe another global object is in order which has static methods specifically for providing helper methods for scripts.
Tuesday, August 14, 2012
Item Use Scripts
I thought adding scripting support would be really complicated, so I postponed it until the NPC milestone. But today I read how ridiculously easy it is to add scripting support to a java program.
So I decided to add script-controlled item use now. I decided to use Javascript as scripting language on the server. Not just because it is supported out-of-the-box by Java but also because I want to avoid to add a third language to the project. The client uses JS, the database uses JS, content files are almost JS (JSON, to be precise), so why not use JS for content scripting?
Doing that was nothing compared to the pain we have on Manasource with the LUA scripting system for Manaserv. There we have to write a wrapper function for every single function we want to expose to the scripting engine. The Java scripting system, on the other hand, can pass any Java objects to a script and then lets the script call any public methods on them without requiring any boilerplate code whatsoever. Just passing the PlayerCharacter object as "user" to the item use scripts allows me to do all sort of interesting stuff.
Sunday, August 12, 2012
Inventory Window with item information
There is now a section with more information about items to the right of the inventory grid. The name and the icon filename are transfered with the inventory contents, but the description and weight are requested from the server as soon as the user clicks on an icon.
The purple book with the "W" on it in the lower right corner is a link to an article in the "Wikinomicon" - a wiki I am going to create where players can add their own information about items and other entities in the world.
Wednesday, August 8, 2012
Inventory window
I created the item window. You can move items with drag&drop (thanks, jQuery UI) and the server saves the positioning.
I also added the /item debug command to create item stacks.
Still missing:
- Showing item amounts
- Item information window
- Using items / dragging usable items to hotkey bar
- Splitting item stacks
- Equipment
- Make item window resizable (both visual size and size of grid)
Sunday, July 15, 2012
Stress test
This weekend I converted the server to use Java Non-Blocking IO. The refactoring was pretty painful, but the result is much better performance when handling many connections. Here is a picture of a stress test. 200 clients aren't a problem at all. Before my server gets problems, Firefox does.
Wednesday, June 27, 2012
Tuesday, June 26, 2012
Saturday, June 23, 2012
How to pick up loot
Putting items directly into the inventory.
Pro:
Picking them up from the ground.
Pro:
Open a window to pick from when clicking on a carcass
Pro:
Contra:
I think I will first implement the "put directly into inventory" method, because everything I need to code for that also needs to be coded for the other methods.
Pro:
- Very easy to implement
- No choice not to pick up items
- Players might find ultra-rare items and not even notice it before checking their inventory
- No way for the players to directly control who gets loot in a party
Picking them up from the ground.
Pro:
- Enables drop-trading
- you can see what other players around you find
- Adrenalin rushes when you see something valuable drop
- Enables mob/item interaction
- Either Inventory icons must be designed in a way that they look OK both in the inventory and on the ground, or every item needs an additional floor sprite
- Enables drop-stealing
- Items on ground can be unreachable or overlooked
- A lot of netcode traffic is used for communicating drop position
Open a window to pick from when clicking on a carcass
Pro:
- Convenient to decide what to pick up, especially for parties
- When killing a lot of enemies at once, checking them all for loot can be a chore
- Additional click required to pick up items
I think I will first implement the "put directly into inventory" method, because everything I need to code for that also needs to be coded for the other methods.
Thursday, June 21, 2012
About items
Items are what makes MMORPGs worth playing.
They appeal to the basic human instinct to gather and to hoard.
They are a much greater token of achievement than levels and stats alone.
They are a great source of reward. I still fondly remember the dopamine rushes I experienced in Ragnarok Online every time one of the many ultra-rare 0.01% drops appeared on my screen.
They enable an economy. An exciting game inside the game which drives itself without any actual work by the developer.
Milestone 7 turned my program into a game. But Milestone 8 will make it worth playing.
Milestone 7 finished
I hereby declare Milestone 7 finished. I finished this milestone much quicker than I thought, but that was rather because I was quite active lately, not because it wasn't much work. The planning decision to make a whole milestone out of the mob AI was definitely justified.
I didn't implement all the planned monster strategies, but I didn't plan to do so anyway. I created a solid framework which allows me to create new strategies with minimal effort. This will surely come in handy in the future. Even now the AI is already passable, and without much effort I can make some awesome things.
The next big chunk will be items.
I didn't implement all the planned monster strategies, but I didn't plan to do so anyway. I created a solid framework which allows me to create new strategies with minimal effort. This will surely come in handy in the future. Even now the AI is already passable, and without much effort I can make some awesome things.
The next big chunk will be items.
Monday, June 18, 2012
Respawning server-side
I implemented respawning on the server-side. The client still needs a button to do this.
Currently respawning fills your HP and sends you back to the starting location. A mechanic for saving a different spawn location will be needed, but this will be postponed to Milestone 9.
I also implemented the /death and /revive chat commands.
Something which is also a bit annoying is that there is no way to tell if a player is alive or dead just by looking at them. I need to use a different sprite for dead players.
Currently respawning fills your HP and sends you back to the starting location. A mechanic for saving a different spawn location will be needed, but this will be postponed to Milestone 9.
I also implemented the /death and /revive chat commands.
Something which is also a bit annoying is that there is no way to tell if a player is alive or dead just by looking at them. I need to use a different sprite for dead players.
Saturday, June 16, 2012
Monster attacking
Monsters can now attack player characters (and - theoretically - each other when I would implement a target strategy for that). They only do one damage with every attack for now, but it's a start. I still need to handle player death. When that's done, I practically have a playable game.
Sunday, June 10, 2012
Angel and Imp
I brushed up my rusty pixel art skills and created two placeholder monster sprites. The angel looks pretty cute already, but the imp needs some work.
Angel
Magical creatures of pure good. They strictly adhere to the moral rules written in their "Holy Codex" which encompass pureness, chastity, humility, abstinence, kindness and peacefulness. The last one with one notable exception: Anyone who does not adhere to the rules of the Codex is considered a sinner, and sinners must be opposed with all means necessary. Destroying the sin is a cause which justifies all means necessary. The angels prefer to make a sinner repent and abandon their sinful ways, but when this turns out to be a futile attempt, they often decide to destroy the sin by destroying the sinner.
At first, other creatures usually consider the berating of the angels about their "sinful ways" amusing at best and annoying at worst. But it is foolish to not take them seriously. Angels are not allowed to bluff or lie. When they say that your adherence to a sinful life will have "dread consequences", they mean it.
Angels are also intelligent, educated, organized, polite and well-spoken. As the self-proclaimed moral guardians of the kingdom, they are very interested in politics. The angels openly trusting someone has a very positive influence on the public opinion. That's why many politicians employ some angels as officials for image reasons. Besides, they are smart, diligent, hard-working and frequently even work for free. Unfortunately, closeness to the angels has the disadvantage that any "dirty business" must be hidden from them at all cost, because they will not hesitate to make it public, abandon you, or even outright attack you when they doubt your moral integrity.
Fallen Angel
An angel which willingly commits a sin, or which enjoys performing a sinful act when forced to do it, immediately transforms into a fallen angel. In contrast to angels, they are hedonist creatures whose only concern is their own well-being. They only cooperate with others as long as it gives them direct gain or pleasure, and they will abandon any ties to others as soon as they become a burden. That's why most fallen angels are solitary creatures.Ironically, they are often less dangerous than their good counterparts. They don't care much about the business of others, unless it directly interferes with them. Although fallen angels do not hesitate to use violence to get what they want, they will usually back off when the resistance they have to bother with outweighs the potential gain.
But still, when there is something they really want, they will not have any moral qualms about using any means necessary to get it. There is also a faction of fallen angels which can indeed be very dangerous: Those who discovered the pleasures of sadism and domination. A fallen angel who enjoys to cause suffering is someone you don't want to meet.
Imp
These evil creatures strive on the hate and anger of other creatures. They need hate of others to survive. When nobody hates an imp, they starve to death. To get people to be angry on them they will use insults, practical jokes or even violence. They can be considered the "bullies" of the kingdom. Fortunately, they are small and not very powerful, so they are more of a nuisance than a real threat. But being a nuisance is a skill they trained to perfection.The best way to deal with them is either by ignoring them, or by trying to have a positive attitude towards them. While hate of others is food for them, affection is pure poison and will drive them away. Considering them cute, feeling sorry for them that they must be mean to survive or believing that they are really nice beneath their outer shell is a sure way to drive them away.
Saturday, June 9, 2012
Monster database, spawn areas and roaming strategy
I just implemented:
- Spawn areas defined in map files
- Reading monster properties from a JSON database file
- The roaming strategies RoamMap and RoamArea
- The spawn strategies ReplaceSingle and ReplaceAll
Oh, and the JSON parser need to be made more stable. When they miss some value, they spit out a stack trace. They should use a reasonable default and output a warning instead.
Monster strategy
I thought about how I could implement monster strategy.
On one hand, I want monsters to have many different behavior patterns to give players a varying game experience. This screams like an application for the Strategy pattern. But I also want to keep my Controller pattern where monsters are controlled from the outside. That's because I want some monsters to act as teams and be aware of what other monsters around them are doing.
So I decided that every spawn area shall have its own MonsterController, which in turn gets four different strategies assigned to it:
Here are some example strategies for each category:
Every monster type has a default strategy for each category. But spawn areas can manually override them. So you can have, for example, a demon castle where some demons patrol the surrounding, two demons guard the front gate, a group which ambushs any players who gets past the guards and some which roam the interior.
- SpawnStrategy: When and where it spawns mobs
- TargetStrategy: How it decides which monsters attack which players
- RoamingStrategy: Behavior of monsters without an attack target
- AttackStrategy: Behavior of monsters which have an attack target
Here are some example strategies for each category:
Spawn | ReplaceSingle | Respawns dead mobs until the population reached a certain value |
ReplaceTeam | When all mobs have died, respawn them all. | |
Ambush | Like ReplaceTeam, but only spawn them when a player is in a specific area. | |
Target | null | never choose an attack target |
Closest | Chooses the target closest to the monster | |
ClosestTeam | Chooses the target closest to any controlled monster | |
LastAttacker | Chooses the last combatant who attacked the monster | |
LastAttackerTeam | Chooses the last combatant who attacked any monster of the team | |
BestAttacker | Chooses the combatant who did most damage to monster | |
BestAttackerTeam | Chooses the combatant who did most damage to all monsters of the team | |
Roaming | null | do not move when not in combat |
Guard | Return to spawn position when not in combat | |
Map | Move randomly around the whole map | |
Area | Move randomly around the spawn area | |
Team | Move randomly to positions around the center of the team | |
Patrol | Move along a predefined set of waypoints | |
PatrolTeam | Move along a predefined set of waypoints, wait until everyone has reached the waypoint | |
Attack | null | never attack, never move when in combat |
Stationary | Use a random attack which is in range | |
Pursuit | Choose random attack. When in range, use it. When not in range, move into range. | |
Stalk | Choose random attack, move close to maximum range, use it. | |
Flee | Don't attack. Try to hold a minimum distance to target. |
Every monster type has a default strategy for each category. But spawn areas can manually override them. So you can have, for example, a demon castle where some demons patrol the surrounding, two demons guard the front gate, a group which ambushs any players who gets past the guards and some which roam the interior.
Milestone 6 finished
Now only skills the character possesses are listed. That was my last ToDo for milestone 6.
Next milestone will be Milestone 7: The Revenge Of The Monsters.
Next milestone will be Milestone 7: The Revenge Of The Monsters.
Potential solutions for my reverse proxy problem
Apache Websocket module:
https://github.com/disconnect/apache-websocket/blob/master/README.mdOnly seems to be able to redirect the websocket to a local binary module, not a backend server via network. Well, I could program my own module to do that, but then I could just write the whole proxy server from scratch.
Make my own
When I would write my own proxy server, I could design it completely to fit my needs. I could add some custom features, like ip-banning people from within the game or adding fake latency and jitter for testing lag compensation.Using nginx as reverse proxy with this mod:
http://www.letseehere.com/reverse-proxy-web-socketsNginx was engineered purely as a reverse proxy, so it would very likely be more efficient than Apache anyway.
HAProxy
https://jfarcand.wordpress.com/2011/10/06/configuring-haproxy-for-websocket/http://stackoverflow.com/a/4737648
HAProxy is another dedicated reverse proxy application. And as far as I can tell from these articles, WebSocket is supported out-of-the-box.
Friday, June 8, 2012
Apache mod_proxy can't websocket
Today I tried to set up a simplified version of the network setup I described using some VirtualBox virtual machines running Ubuntu Server. It just had three machines instead of the planned six: the reverse proxy, a gameserver which also ran the MongoDB, and a LAMP server with the client files.
It turned out, apache configured as reverse proxy can not route WebSocket transparently. It strips the Upgrade line from the http headers, sabotaging the handshake.
Now I have to find another proxy server which is able to handle WebSocket.
Or maybe I just write my own reverse proxy. It can't be that difficult. Listen to socket, wait for HTTP header. when it has Upgrade:websocket, forward the tcp stream to the application server, when it hasn't, forward it to the web server. It should be possible to hack together in C in an afternoon and it will most likely be much less bloated than using the humongous Apache HTTP Server.
It turned out, apache configured as reverse proxy can not route WebSocket transparently. It strips the Upgrade line from the http headers, sabotaging the handshake.
Now I have to find another proxy server which is able to handle WebSocket.
Or maybe I just write my own reverse proxy. It can't be that difficult. Listen to socket, wait for HTTP header. when it has Upgrade:websocket, forward the tcp stream to the application server, when it hasn't, forward it to the web server. It should be possible to hack together in C in an afternoon and it will most likely be much less bloated than using the humongous Apache HTTP Server.
Thursday, June 7, 2012
No quest log clutter
The problem with many RPGs of today - both single-player and multi-player - is that they figuratively bombard the player with quests. When you come to a new town you can quickly fill your quest log with 10 or so unrelated things to do. The order in which to do them is left to you. When you are like me, you prioritize all these quests after practical concerns: minimizing travel time, prioritizing quests with rewards I need, or do those first with the lowest level requirement.
The problem with that is that it really kills the narration of each individual quest. Adventuring and storytelling is replaced with managing a checklist. How can you expect the player to follow ten story arcs simultaneously?
For that reason I think that it would be better to abandon the quest log for a mission system like the GTA series. I give you lots of quests to choose from, and you are free to do them in any order you like. But you can only have one active quest at a time. You can abort a quest when you are stuck, but then you have to start it from the beginning. Quests won't be that long, so you won't have to redo too much. Longer storylines will consist of a series of many consequitve quests. The next quest of the series should be offered right after the previous, so that it's clear that they belong together. They still give you the option to decline for now and pursue other quests first.
That way you will be able to fully immerse yourself in the story of a quest without being distracted by the twenty other quests on your questlog.
But what about sidequests? Little surprise challenges along the way? These should also have a space, but they need to be designed in a way that they can be done in the direct vicinity so that you aren't taken too far away from the quest you pursue at the moment.
How will this look in practice? Quest givers will still be marked with different icons telling you 1. if they offer a quest of a new line or the continuation of an existing one (shape) and 2. the length of the quest line (color). These icons will be visible while being on a different quest, but you won't be able to interact with the quest givers yet.
Quest lengths:
quick: a single quest which can be done in the direct vicinity of the quest giver. These can be taken without interrupting an on-going quest because they will be too short to distract too much.
short: a single quest which requires some travelling.
medium: 2-3 consecutive quests, or one with a very long travelling distance and/or multiple optional sidequests along the way.
long: 4-6 consecutive quests, or less but with long travel distances
epic: 7 or more stations, might include planned interruptions where the next quest giver won't be obvious right away or which have "nested" quests - quests which require that other questlines (which will be integrated into the narration) have to be completed in-between.
Example for an epic quests with nesting: 1. find out about an artefact which is broken into three pieces, 2- complete three separate medium-length quests to collect the pieces, 3. reconstruct the artefact 4. use it to destroy a boss monster.
The problem with that is that it really kills the narration of each individual quest. Adventuring and storytelling is replaced with managing a checklist. How can you expect the player to follow ten story arcs simultaneously?
For that reason I think that it would be better to abandon the quest log for a mission system like the GTA series. I give you lots of quests to choose from, and you are free to do them in any order you like. But you can only have one active quest at a time. You can abort a quest when you are stuck, but then you have to start it from the beginning. Quests won't be that long, so you won't have to redo too much. Longer storylines will consist of a series of many consequitve quests. The next quest of the series should be offered right after the previous, so that it's clear that they belong together. They still give you the option to decline for now and pursue other quests first.
That way you will be able to fully immerse yourself in the story of a quest without being distracted by the twenty other quests on your questlog.
But what about sidequests? Little surprise challenges along the way? These should also have a space, but they need to be designed in a way that they can be done in the direct vicinity so that you aren't taken too far away from the quest you pursue at the moment.
How will this look in practice? Quest givers will still be marked with different icons telling you 1. if they offer a quest of a new line or the continuation of an existing one (shape) and 2. the length of the quest line (color). These icons will be visible while being on a different quest, but you won't be able to interact with the quest givers yet.
Quest lengths:
quick: a single quest which can be done in the direct vicinity of the quest giver. These can be taken without interrupting an on-going quest because they will be too short to distract too much.
short: a single quest which requires some travelling.
medium: 2-3 consecutive quests, or one with a very long travelling distance and/or multiple optional sidequests along the way.
long: 4-6 consecutive quests, or less but with long travel distances
epic: 7 or more stations, might include planned interruptions where the next quest giver won't be obvious right away or which have "nested" quests - quests which require that other questlines (which will be integrated into the narration) have to be completed in-between.
Example for an epic quests with nesting: 1. find out about an artefact which is broken into three pieces, 2- complete three separate medium-length quests to collect the pieces, 3. reconstruct the artefact 4. use it to destroy a boss monster.
Wednesday, June 6, 2012
Stat increase buttons
I added buttons for spending status points to increase stats. The skill display must be made a bit more dynamic. Those hardcoded skill names got to go in exchange for dynamic names obtained from netcode so that it doesn't list skills the player hasn't learned yet.
Tuesday, June 5, 2012
Added stat points
I added status points and the netcode to increase stats.
Currently, the collected status points are equal to the collected skill exp. But I intend to decouple that, so that some monsters give better skill exp while others give more stat points. Also, most quests will give only stat points, because giving skill exp would only make sense when the quest focuses on the training of that specific skill.
The client still needs buttons for increasing stats and I need to communicate the costs for doing so.
Currently, the collected status points are equal to the collected skill exp. But I intend to decouple that, so that some monsters give better skill exp while others give more stat points. Also, most quests will give only stat points, because giving skill exp would only make sense when the quest focuses on the training of that specific skill.
The client still needs buttons for increasing stats and I need to communicate the costs for doing so.
Friday, June 1, 2012
Some bugfixes
Yesterday I fixed a long-standing bug which was setting the players position to an illegal value of "-Infinity : -Infinity".
I also added filtering of HTML tags from the chat console on the client. Using HTML markup in chat might have had some useful applications, but the risk that players find a way to abuse this feature to screw with each others interface or even inject XSS code is just too big. I could have filtered user input on the server, but 1. I don't want the server to assume that the presentation layer is rendering HTML and 2. I am not sure I would catch any case where a user-supplied string ends up in the chat console.
To make up for this and allow some markup in chat, like bold text, colors, graphical smilies or hyperlinks, I could later add a custom markup language like BBCode. Links will require some additional filtering. I don't want people to click on javascript: links in the chat.
I also added filtering of HTML tags from the chat console on the client. Using HTML markup in chat might have had some useful applications, but the risk that players find a way to abuse this feature to screw with each others interface or even inject XSS code is just too big. I could have filtered user input on the server, but 1. I don't want the server to assume that the presentation layer is rendering HTML and 2. I am not sure I would catch any case where a user-supplied string ends up in the chat console.
To make up for this and allow some markup in chat, like bold text, colors, graphical smilies or hyperlinks, I could later add a custom markup language like BBCode. Links will require some additional filtering. I don't want people to click on javascript: links in the chat.
Wednesday, May 30, 2012
Server network architecture
The client will communicate solely with an Apache HTTPD configured as reverse proxy. This server is the only machine exposed to the internet. Depending on the request URL it will forward the client requests to the webserver serving the official website, the game content webserver or the gameserver.
WebSocket is supposed to work through proxy servers which aren't aware of the websocket protocol. I just hope that's also the case for Apache HTTPD's mod_proxy_http.
The gameserver pulls the game content it needs from the content server using NFS. Having the client and the server game data in the same location makes maintainance a lot easier, because I don't have to worry about what's client-sided, what's server-sided, what's shared and if they are in sync or not.
The website webserver runs on a LAMP configuration. It also has access to the game database running MongoDB. This will allow integration between the game and interactive parts of the website. This could be used for sharing user accounts between game and website, user-pages showing character status or real-time game statistics on the website. The website will also likely need a good old MySQL database for 3rd party components like a forum, a CMS or a blogging software - also I want to separate the website data from the game data where possible to avoid game and website DOSing each other.
These five servers will run virtualized on the same machine at first. I will then put them on separate physical servers when necessary. Should these servers become overstressed, Apache, MySQL and MongoDB can already be sharded when necessary. My gameserver can not be sharded yet, but I designed the server architecture in a way which will make it easy to add support for this later.
Tuesday, May 29, 2012
Hotkey bar
The hotkey bar and the drag&drop functionality from the spell window for it work now. There is a bug in jQuery which breaks the drop functionality when the draggable icons in the spell window are recreated. This happens whenever the spell windows content changes. I will have to find a workaround for this issue somehow. But this can wait until Milestone 9 when this can actually happen while playing.
A problem is that the icons for all spell levels look the same, so they are hard to tell apart in the quickbar. But that's a purely content-sided problem. I can easily change the actions.json on the server to tell the client to show a different icon for each spell level.
Before I close this milestone I want to look into adding tooltips for the spell icons to show some more of the spell information available to the client.
Monday, May 28, 2012
Spell Window
The new spell window with some cheap, pillow-shaded spell icons. All data in this window is received from the server (icons as filenames). The server gets the spell data from a JSON file and the spells available to each character from the database. This information is now also used for spellcasting and damage calculation on the server. The only problem is that there is no way to change the available spells except for hacking them into the database. This will have to wait until scripting in Milestone 9.
Next will be the ability to drag&drop those spells into the hotkey bar to cast them. Then Milestone 6 will be history.
Wednesday, May 16, 2012
Roadmap updated
While I ported the client from Metro to HTML5 I lost track of my roadmap. I implemented some features which were planned for much later milestones (like some steps into the direction of inventory and item management) while forgetting about things which are much more important in the intermediate future. Time to get my priorities straight again.
Milestone 6 (growth):
- Flexible GUI infrastructure
- Character system
- EXP gain and leveling
- Increasing base attributes
- Combat mechanics
- Mana points, mana regen and mana use
- Spellbook
- Monster database
- Monster AI
- Spawn areas on maps
- Character death and respawning
- Items and Inventory
- Player trading
- Shops
- NPC interaction
- Scripting
- Permission management
- Database logging of
- Logins
- Administrative actions
- Usage statistics
- Logins
- Brute force protection
- Animated sprites
- Animated tiles
- Particle effects
Rest of Milestone 6
- Move hit points to game system
- add mana points, mana regen and mana use
- Store spell palette in the database
- communicate spell palette to the client
- drag&drop spell palette to hotkey bar
- save hotkey assignment server-sided
monster database storing monster statamonster ai implemented using strategy pattern (movement strategy and attack strategy)
fuck those monsters - that stuff goes to another milestone.
Subscribe to:
Posts (Atom)