Sunday, 10 January 2010

The Unday Papers

Undays* are for finishing staining the deck of the house, barbecuing some kangaroo steaks for lunch and waiting for an ftp server to come back up so I can do the Unangband 0.6.4 pre-release.

Let's try and avoid mentioning too much more of my personal life and have a look at what's happened on the SVN server this week:

  • We've gone from rev 2747 to 2792 - that's a total of 45 commits. I've been the only developer to contribute this week, which is hardly surprising given the amount of code changes occuring at the moment. Since I've done most of the coding on the train to and from work without roaming Internet, the commits haven't been especially granular either, so it's undoubtedly hard to follow what's going on.
  • The big change for the week has been the attempt to have neutral monsters in the game - monsters which don't necessarily attack the player outright. I've reused the MFLAG_TOWN monster attribute to indicate this since this flag was already doing most of the work already. I've cleaned up the tell_allies_x set of functions to use a single tell_allies_info function with various hook functions - this had been on my to do list for some time. Quite a bit of the AI code has been made more robust as a result, and I've found and fixed few bugs so you'll hear monsters talking a bit more now.
  • At the moment, neutral monsters are only generated in town, but it is possible to convert them elsewhere from hostile to neutral by 'O'ffering them something. The offer command was the initial driver for neutrality - I figured out I needed a command to give items to allied monsters, and it felt like a natural extension to be able to sell items to monsters in town, and bribe monsters in the dungeon. The actual 'O'ffer code has been anything but natural however - it looks ugly, and plays out weirdly, and I spent most of Friday night and Saturday trying to beat it into shape.
  • The reason I needed a command to give items to allied monsters, is that I added commands to take items from allies or order them to drop items. This was incredibly easy - just an some copying and pasting of code in the get_item() function in object1.c, add some new flags to the do_cmd_item_type table in tables.c, and code to the inven_takeoff command in object2.c to handle the case of taking items off another monster instead of yourself. I had initially guessed I'd have to do the reverse - add a command 'O'rder a monster to give you items - but as soon as I made the connection between 't'aking off items from yourself and taking an item off an ally and realised the same code and command could be used for both functions, the solution was obvious.
  • While I was in melee2.c I happened to stare at the monster waking up code in recover_monster() and put in a one liner that meant instead of waking up by x amount if the player was sneaking, monsters instead woke up by a random number from 0 to x-1 instead. Sneaking had already been in the game, but only as a way of specifying that you didn't want your allies to attack sleeping monsters. It was time, with neutral monsters you might want to avoid now in the game, for sneaking to become more useful.
  • I took and break and cleaned the pool and realised while doing getting the gum tree leaves out of the sump, what bad behaviour the above code change would encourage in the players. People would unnecessarily try to min/max whether they were sneaking around the dungeon in general as avoiding waking monsters up as this is such an important part of game play for Angband and variants. So I came back to the code and instead of a random value, I capped the maximum x a monster could wake up by while the player was sneaking, so that it would only have any effect if the player was aware of the monster and it was less than 5 grids away. This is designed specifically to allow a player to sneak up to or past a room full of sleeping monsters, and provide advantages in no other situation.
  • I decided, incorrectly as it turns out, to have sneaking prevent other actions that would create noise, like shooting, casting spells and so on by marking these actions in with a CONDITION_NO_SNEAKING in the do_cmd_item_type table (this table has been a real source of inspiration this week). But the UI was annoying for this - you kept getting told you couldn't do stuff while you sneaked - so I instead just assume that the player doesn't want to sneak if they do something noisy. The trick to this is keep p_ptr->sneaking set if the player is trying to be sneaky in general, and then mark them with p_ptr->not_sneaking if they do an action that isn't sneaky. We then check both p_ptr->sneaking and p_ptr->not_sneaking to see if the player is actually quiet during the monster processing part of the turn, and clear p_ptr->not_sneaking at the start of the player's turn. I've used similar tricks elsewhere with blocking, dodging and charging.
  • Sneaking prevents dodging and charging, for those people looking for the downside to doing it all the time. And since you automatically dodge if you move normally, sneaking makes you more vulnerable to ranged attacks and spells. You can run while you're sneaking to indicate you'd like to dodge instead (Shift-move).
  • The ability to steal from monsters is another consequence both the interacting with monster inventory and sneaking changes. I'm glad I had looked at the wake up code before implementing this, because otherwise the stealing code would be much less clean. Separating out the generation of a monster's inventory - monster_drop() - from the monster_death code makes this a no brainer. The MFLAG_MADE flag - used to prevent the player abusing cloning or breeding monsters to farm them for drops - gets re-used here naturally.
  • I'm going to overload the 's'earch command so you'll be able to 's'teal if you're next to a monster and 's'earch otherwise. I'll have to make it easy to specify to search if you're next to a monster, as the few times you'll want to do this, you'll really want to do this.
  • The other big change this week has been splitting up the mage spell books so that there is a 'low' and 'high' version of each of the dungeon spell books. This is purely a game balance exercise, as there were a huge number of spells in most of these books which made playing a high level mage much less of a challenge than I had intended. You'll now have to carry a lot more books if you want a really wide spell selection. I've also added an extra spell book to each mage school and move the spells around the school books for the same reason.
  • Long standing bugs fixed: Detection spells now refresh the screen correctly when cast. Ohmygod, I hadn't finished implementing the rogue class. Now fixed.
  • New spells this week: Drain Mana. Raise Familiar. Second Sight. New items this week: About 30 new mage spell books added. New monsters this week: None, but the behaviour of town monsters has changed in many subtle ways, so keep alert.
  • There's been lots of other small changes, including UI improvements during character creation, so as always, check the SVN logs for details.
So I can't promise that every week is going to be as interesting as this one, development wise. Or in my personal life. Like the fact my wife might be...


* The Unday papers is a somewhat stylistically derivative summary of SVN commits for the week and other development issues.


Marc H said...

Question/Thought: rather than overload 's' for 's'teal, necessitating a UI to distinguish between stealing ad searching with a nearby monster, why not overload the 't'ake item from allies code you mention earlier so 't'ake item from non-allied monster means steal?

Andrew Doull said...

I've allowed the player to 't'ake items at a distance, for convenience. If I added stealing to that command, you'd be able to steal at a distance...