Giter Club home page Giter Club logo

lambdahack / lambdahack Goto Github PK

View Code? Open in Web Editor NEW
614.0 614.0 56.0 36.46 MB

Haskell game engine library for roguelike dungeon crawlers; please offer feedback, e.g., after trying out the sample game with the web frontend at

Home Page: https://lambdahack.github.io

License: BSD 3-Clause "New" or "Revised" License

Haskell 98.72% NSIS 0.17% Makefile 1.10% JavaScript 0.01%
ascii browsergame engine freesoftware game gamedev haskell html5 indiedev library pcg replayability roguelike sdl squad tactical turnbased

lambdahack's Introduction

LambdaHack

Hackage Join the chat at Discord Join the chat at Matrix

LambdaHack is a Haskell1 game engine library for ASCII roguelike2 games of arbitrary theme, size and complexity, with optional tactical squad combat. It's packaged together with a sample dungeon crawler in a quirky fantasy setting. The sample game can be tried out in the browser at http://lambdahack.github.io.

As an example of the engine's capabilities, here is a showcase of shooting down explosive projectiles. A couple were shot down close enough to enemies to harm them. Others exploded closer to our party members and took out of the air the projectiles that would otherwise harm them. Actual in-game footage.

gameplay screenshot

This was a semi-automatic stealthy speedrun of the escape scenario of the sample game, native binary, SDL2 frontend, single tiny bitmap font. The enemy gang has a huge numerical and equipment superiority. Our team loots the area on auto-pilot until the first foe is spotted. Then they scout out enemy positions. Then hero 1 draws enemies and unfortunately enemy fire as well, which is when he valiantly shoots down explosives to avoid the worst damage. Then heroine 2 sneaks behind enemy lines to reach the remaining treasure. That accomplished, the captain signals retreat and leaves for the next area (the zoo).

Using the engine

To use the engine, you need to specify the content to be procedurally generated. You declare what the game world is made of (entities, their relations, physics and lore) and the engine builds the world and runs it. The library lets you compile a ready-to-play game binary, using either the supplied or a custom-made main loop. A couple of frontends are available (SDL2 is the default for desktop and there is a JavaScript browser frontend) and many other generic engine components are easily overridden, but the fundamental source of flexibility lies in the strict and enforced with types separation of engine code from the read-only content and of clients (human and AI-controlled) from the server.

Please see the changelog file for recent improvements and the issue tracker for short-term plans. Long term goals include multiplayer tactical squad combat, in-game content creation, auto-balancing and persistent content modification based on player behaviour. Contributions are welcome. Please offer feedback to [email protected] or, preferably, on any of the public forums.

Games from different repos known to use the LambdaHack library:

  • Allure of the Stars6, a near-future Sci-Fi game

Note: the engine and the LambdaHack sample game are bundled together in a single Hackage3 package released under the permissive BSD3 license. You are welcome to create your own games by forking and modifying the single package, but please consider eventually splitting your changes into a separate content-heavy package that depends on the upstream engine library. This will help us exchange ideas and share improvements to the common codebase. Alternatively, you can already start the development in separation by cloning and rewriting Allure of the Stars10 and mix and merge with the sample LambdaHack game rules at will. Note that the LambdaHack sample game derives from the Hack/Nethack visual and narrative tradition9, while Allure of the Stars uses the more free-form Moria/Angband style (it also uses the AGPL license, and BSD3 + AGPL = AGPL, so make sure you want to liberate your code and content to such an extent).

Installation of the sample game from binary archives

The game runs rather slowly in the browser (fastest on Chrome) and you are limited to the square font for all purposes, though it's scalable. Also, savefiles are prone to corruption on the browser, e.g., when it's closed while the game is still saving progress (which takes a long time). Hence, after trying out the game, you may prefer to use a native binary for your architecture, if it exists.

Pre-compiled game binaries are available through the release page11 (and Linux dev versions from GitHub Actions18 and Windows from AppVeyor19). To use a pre-compiled binary archive, unpack it and run the executable in the unpacked directory or use program shortcuts from the installer, if available. On Linux, make sure you have the SDL2 libraries installed on your system (e.g., libsdl2-2.0-0 and libsdl2-ttf-2.0-0 on Ubuntu). For Windows (XP no longer supported), the SDL2 and all other needed libraries are included in the game's binary archive.

Screen and keyboard configuration

The game UI can be configured via a config file. The default config settings, the same that are built into the binary, are on github at GameDefinition/config.ui.default. When the game is run for the first time, or whenever the config file is deleted, the file is written to the default user data location, which is ~/.LambdaHack/ on Linux, C:\Users\<username>\AppData\Roaming\LambdaHack\ (or C:\Documents And Settings\user\Application Data\LambdaHack\ or something else altogether) on Windows and Inspect/Application/Local Storage under RMB menu when run inside the Chrome browser. If the user config file is outdated or corrupted, it's automatically moved away together with old savefiles. At the next game start, the new default config file appears at its place.

Screen fonts and, consequently, window size can be changed by editing the config file in the user data folder. The default bitmap font 16x16xw.bdf used for the game map covers most national characters in the Latin alphabet (e.g. to give custom names to player characters) and results in a game window of exactly 720p HD dimensions. The 8x8xb.fnt bitmap font results in a tiny window and covers latin-1 characters only. The config file parameter allFontsScale permits further window size adjustments, automatically switching to the scalable version of the large game map font (16x16xw.woff). Config file option chosenFontset governs not only the main game map font, but also the shape of the rectangular fonts, if any, in which longer texts are overlaid over the map.

For high resolution displays and/or if fullscreen mode is requested in the configuration file, allFontsScale needs to be set. E.g., scale 3 works for 4K displays. Otherwise, the letters may be too small or, in fullscreen or on retina displays in OS X, the screen may be automatically scaled as a whole, not each letter separately, softening letter edges of the square fonts that should rather be pixel-perfect and crisp.

If you don't have a numeric keypad, you can use the left-hand movement key setup (axwdqezc) or Vi editor keys (aka roguelike keys) or mouse. If numeric keypad doesn't work, toggling the Num Lock key sometimes helps. If running with the Shift key and keypad keys doesn't work, try the Control key instead. The game is fully playable with mouse only, as well as with keyboard only, but the most efficient combination may be mouse for menus, go-to, inspecting the map, aiming at distant positions and keyboard for everything else.

If you run the ANSI terminal frontend (--frontendANSI on commandline), then numeric keypad (especially keypad *, / and 5) may not work correctly, depending on the terminal emulator you use. Toggling the Num Lock key may help or make issues worse. As a work around these issues, numbers are used for movement in the ANSI frontend, which sadly prevents the number keys from selecting heroes. The commands that require pressing Control and Shift together won't work either, but fortunately they are not crucial to gameplay.

Some effort went into making the ANSI frontend usable with screen readers, but without feedback it's hard to say how accessible that setup is. This doesn't work on Windows, due to extra code that would be required. As a side effect of screen reader support, there is no aiming line nor path in ANSI frontend and some of map position highlighting is performed using the terminal cursor. Screen readers may also work better with animations turned off, using --noAnim or the corresponding config file or main game menu options.

Compilation of the library and sample game from source

To compile with the standard frontend based on SDL2, you need the SDL2 libraries for your OS. On Linux, remember to install the -dev versions as well, e.g., libsdl2-dev and libsdl2-ttf-dev on Ubuntu Linux 16.04. Compilation to JavaScript for the browser is more complicated and requires the ghcjs15 compiler and optionally the Google Closure Compiler16.

The latest official version of the LambdaHack library can be downloaded, compiled for SDL2 and installed automatically using the 'cabal' tool, which may already be a part of your OS distribution, but if it's too old (version 3.4 or later is required) you can download the whole current compilation suite as described at https://www.haskell.org/downloads/. You can get and run the LambdaHack package from Hackage3 as follows

cabal update
cabal install LambdaHack
~/.cabal/bin/LambdaHack

For a newer snapshot, clone the source code from github5 and run cabal run LambdaHack from the main directory. Alternatively, if you'd like to develop in this codebase, the following speeds up the turn-around a lot

cp cabal.project.local.development cabal.project.local

and then you can compile (and recompile) with

cabal build

and run the game with

make play

The SDL2 frontend binary also contains the ANSI terminal frontend (--frontendANSI on commandline) intended for screen readers and a simplified black and white line terminal frontend (--frontendTeletype) suitable for teletype terminals or a keyboard and a printer (but it's going to use a lot of paper, unless you disable animations with --noAnim). The teletype frontend is used in CI and for some tests and benchmarks defined in Makefile. The terminal frontends leave you on your own regarding font choice and color setup and you won't have the colorful squares outlining special positions that exist in the SDL2 frontend, but only crude cursor highlights. The terminal frontends should run on Windows, but Windows disables console for GUI applications, so they don't.

Testing and debugging

Unit tests and integration tests can be run and displayed with

cabal test --test-show-details=direct

and doctests with

cabal install doctest --overwrite-policy=always && cabal build
cabal repl --build-depends=QuickCheck --with-ghc=doctest definition
cabal repl --build-depends=QuickCheck --build-depends=template-haskell --with-ghc=doctest lib:LambdaHack

The Makefile contains many sample automated playtest commands. Numerous tests that use the screensaver game modes (AI vs. AI) and the teletype frontend are gathered in make test-locally. Some of these are run by CI on each push to github. Test commands with prefix frontend start AI vs. AI games with the standard SDL2 frontend to view them on.

Run LambdaHack --help to see a brief description of all debug options. Of these, the --sniff option is very useful (though verbose and initially cryptic), for displaying the traffic between clients and the server. Some options in the config file may prove useful for debugging too, though they mostly overlap with commandline options (and will be totally merged at some point).

Coding style

Stylish Haskell is used for slight auto-formatting at buffer save; see .stylish-haskell.yaml. As defined in the file, indentation is 2 spaces wide and screen is 80-columns wide. Spaces are used, not tabs. Spurious whitespace avoided. Spaces around arithmetic operators encouraged. Generally, relax and try to stick to the style apparent in a file you are editing. Put big formatting changes in separate commits.

CI checks the code with hlint . using the very liberal configuration file at .hlint.yaml. If hlint is still too naggy, feel free to add more exceptions.

Haddocks are provided for all module headers and for all functions and types from major modules, in particular for the modules that are interfaces for a whole directory of modules. Apart of that, only very important functions and types are distinguished by having a haddock. If minor ones have comments, they should not be haddocks and they are permitted to describe implementation details and be out of date. Prefer assertions instead of comments, unless too verbose.

The 'pointman' from game manual and UI is called 'leader' in the source code and there are a few more mismatches, though the source code naming and the UI naming should each be consistent in separation. If the UI names stick, perhaps source code will be renamed at some point.

This codebase is an experiment in extensive use of states without lens. So far, it works, doesn't result in much larger files or lots of repetition and has the added benefits that newcomers don't need to learn any optics library. Record punning, etc., definitely help.

First steps reading the codebase

A good start may be

https://github.com/LambdaHack/LambdaHack/blob/master/GameDefinition/game-src/Client/UI/Content/Input.hs

That's where keyboard keys are assigned commands, help texts and categories (including special categories indicating that a group of keys also forms an in-game menu). This file is specific to a particular game (hence GameDefinition in the path) and the engine dynamically creates in-game help screens based on this file and on player config file that can partially overwrite it.

The commands assigned to keys are interpreted by the UI client (each faction in the game uses a client and the player's client additionally has UI capabilities) in the following module:

https://github.com/LambdaHack/LambdaHack/blob/master/engine-src/Game/LambdaHack/Client/UI/HandleHumanM.hs

By this point you've seen one of the six major command sets (HumanCmd, the others being Effect, UpdAtomic, Request, Response, FrontReq) and one of around ten distinct interpreters for the commands (mostly in Handle* modules). You've also seen a bit of the UI client code, but not the AI client nor the server (game arbiter) code. The wiki17 contains not entirely outdated further reading about the client-server architecture.

At this point, before trying to grasp anything more and drown in abstraction, you are welcome to pick up a few good first issue-labeled tickets and get some hands-on experience with the codebase.

For further study, note that most of the commands are interpreted in monads. Server and clients share some of the customized monadic API, but their monads are implemented differently (in *Implementation modules). All these monads are state monads (managing different aspects of game state), therefore the semantics of a command is a state transformer with extra side effects (e.g., frontend drawing).

The "main loop" is the following: the UI client receives keystrokes and interprets the commands they correspond to. As soon as one of the commands is not just local UI manipulation, but a request to change the main game state, such a request is packaged and sent to the server (e.g., a request to move a hero to the north). The server responds "not possible, there is a wall" or reacts by sending to clients (to all UI and AI clients that can see the event) a series of game state-changing responses. AI clients, likewise, send to the server requests, generated based on the perceived game state changes and the AI goals of each AI faction.

Further information

For more information, visit the wiki4 and see PLAYING.md, CREDITS and COPYLEFT.

Have fun!

lambdahack's People

Contributors

beandipper avatar bulbousbullfrog avatar felixonmars avatar gitter-badger avatar jamiefristrom avatar kayvank avatar kosmikus avatar m-renaud avatar michaelmackus avatar mikolaj avatar pabloreszczynski avatar peritract avatar phadej avatar purpleorangegames avatar rszczers avatar vrom911 avatar

Stargazers

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

Watchers

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

lambdahack's Issues

Test using AIvsAI

Wait for good AIvsAI and run it with cabal test, optionally with frontend 'none', when implemented, to speed the test up. Test with a few different config files. Check program coverage and tune AI and config files using hpc. UI won't be tested, but it's generally tough to test automatically and not very deep anyway. The tests should be quite exhaustive and, coupled with assertions, may be enough the project. A pity I won't get to use quickcheck, though :(.

Make remembering previously visible stuff configurable

Add a config option for a client, specifying whether to remember items
that are out of sight and mark their last seen position and attributes
on the map (under cursor they will be reported as 'remembered' as opposed
to 'seen'). BTW, that was and is the default behaviour currently.
Let the same be specified for actors and perhaps even with map tiles.
The infrastructure to do so is ready (config files, server sending
enough information, client processing the information and then recording it).

Add tooltips

For a start, display full tile (with actors and items [Edit: and embedded items and possible transformations]) description. The tooltips should only be triggered with a mouse. In target mode, with movement keys, display the info in the top message area instead (and the full information only upon request).

Stop running if a monster seen not in player turn

The same with messages appearing when the player runs. The run stop condition should be checked at each turn. There is a problem, however, because the message or monster can be no longer visible during the next player turn, so it can be confusing, especially at high frame rate.

If we make the player stop running before his turn he normally can't move yet. Perhaps he should be able to move, but have negative energy, incur an energy debt. What if the debt accumulates, though? Perhaps stop with "Running interrupted in the middle of the turn. --more-", do not let the "--more--" be canceled with ESC, play the rest of the turn frames and accumulate any further messages.

Draw red square around the leader character

Right now the party leader is drawn in inverse video (black on white),
but this does not work well for non-hero actors, because their colour
is not visible. Drawing them in color on white does not work either,
because for bright colours their letter is hard to discern.
On text frontends we may use cursors to highlight the leader,
but the insertion cursor in gtk has a fixed, bad shape (and the recent
patch for overtype mode was reverted and generally it's messy),
so in gtk we should probably draw a red square by hand over the textView
whenever it's redrawn.

Please relax Cabal restrictions on hashable and pretty-show

The restrictions

hashable >=1.2 && <2, pretty-show >=1.6 && <1.6.2

prevent LambdaHack from being built the respective latest versions of those libraries, even though the build would succeed just fine. You might want to relax those restrictions in the Cabal file.

Review of commit review

Here I will gradually add pieces of the the promised review of your modifications/reversions of my commits. Thank you again for taking time to look at the commits in detail.

I have essentially reverted the last change that removes the
"open door" command, because I disagree with it. I'm happy to
add auto-open as a configuration option later, but I don't like
it to be the default for everyone.

I can live with it, it's a single small detail, but in my experience:

  1. KISS. Options are bad, the less options the better, except debug and experimental options and unintrusive UI options like skip splash screen or macros. This is, among others, because options confuse players, bug reports are less useful if different options can be used, code for both values of a boolean option has to be maintained (notice that 'close' is quite a bit simpler that 'openclose') and tested (usually the option not used by the maintainer bit-rots fast or becomes mildly incompatible with new features).
  2. Keys are bad, the less keys are used by the game, the better. The same with menu items.
  3. The 'bump into things to affect them' metaphor works well even in very complex games and bumping into a door to see no effect weakens the metaphor. I also offers faster play, less keystrokes, which matters a lot after a few hours of play.

-- TODO: Turn running into a standalone handler. Make it clearer and
-- more configurable how running behaves, and when it stops. This is
-- nothing the game should force on a player, but that a player should
-- rather be able to configure.

In a game engine, running behaviour should indeed be easily configurable. But in concrete games build upon the engine, usually only one setting is optimal (diagonal moves, if there's a time limit and no instant death threats (e.g. paralization); avoiding diagonal moves otherwise), so a player-configurable option falls under the "bad option" category above. OTOH, as a debug or experiment option, it's a very good idea and I should have done it in a configurable way, instead of just changing the behaviour according to how I imagine LambdaHack will evolve (no hurry exploring and pretty dangerous attacks).

I am reverting a premature optimisation in the original patch.
However, I keep the changed semantics of running which is a clear
improvement over the original. However, I'd like running to be
more configurable in the future.

Agreed on all accounts.

Revert "minor tweaks ported from branch branching_degree"
I revert this change because I disagree with nearly all aspects
of it. For example, let's try to keep functions as total as possible.

I'm happy to learn and adhere to your stylistic preferences (I'm a Haskell newbie, after all), but I have to explain a few things, because my "minor tweaks" were not all about style, but about experience with modifying the code, after a failed experiment on a different branch. There is no point in you reading the branch nor in me explaining the details, so just a few concrete points about the code before I changed it in my commit:

  1. digRoom sometimes creates isolated, 1-tile rooms. They look out of place and very rarely items and monsters (and stairs?) can be trapped in them. The rooms are useless, if not isolated, because they just get overwritten.
  2. L.nub documents that sometimes the corridor can have less than 3 sections and prevents confusion if, in the future, the dungeon keeps a local or global counter of corridor turns or intersections (for AI or to describe floor in an intersections as "worn", etc.).
  3. digCorridor in three cases preserves items in a tile, but in one case loses them. The "undefined" value (in another commit, here it's "dummy") documents that only the locations from the newly created list are important, further ensuring that items from the 2 merged tiles will not be mixed up. Granted, there are no items in tiles at this stage of dungeon generation right now, but...

Revert "the space key no longer skips turn"
Reverting only to comment the line. This should be configurable.

Agreed. Macros are "good options". I've only removed resting on space, because confirmations where done with space or enter, so space was often pressed by accident. If all confirmations are done with y/n, the space can stay or replace dot. OTOH, we shouldn't take 2 keys (3 with keypad '5') for the same task without a good reason. Let the player take as much of them as he want via macros and let's leave him lots of unused keys to work with.

Revert "hardened the highscores file handling code"
I fail to see why not addressing other exceptions is a bad thing,
so I'm reverting the patch.

I may be wrong, but I think if you have bad permissions on the file or play from / or from live CD or broken NFS, you will never learn, until you become suspicious about the score table not growing longer with time. The patch let's you see it ASAP via an exception displayed on command line.

scan (tr loc) lmap 1 (0,1)) -- was: scan (tr loc) lmap 0 (0,1); TODO: figure out what difference this makes

I'm not 100% sure, but I seem to remember I tested it and came to the following conclusion:

scan (tr loc) lmap 1 (0,1)) -- with 0 in place of the first 1, if the central square is not transparent (e.g. if it's a curtain), nothing else is visible

That's it. The PassiveHandler removal is surely for the best. Agreed about PLAYING.markdown. I've done a diff between my and your changed version of all the commits and it seems you didn't lose even a single line while juggling the patches and producing the git art. Thanks again for the review.

Add pathfinding

Add pathfinding for monsters to go around obstacles (and e.g. to let monsters have private goals like go to the other end of the map, unless a hero is spotted) and for go-to-target hero command and the mouse equivalent.

Write a WWW browser frontend

Probably using fay, ideally sharing most of the subsystems that frontends now share. With the client-server arch we now have in place that should be much easier.

Let some monsters chase projectiles

Let stupid monsters (animals?) chase slow enough projectiles (arrows are probably borderline) in preference to heroes or just on first-spotted basis.

High score table, number of steps

In the high score table, the global game time is currently reported to be the "number of steps". This is misleading, as the standard speed of the hero is one move per 10 time units. So we should either divide the number reported by 10, count the actual number of steps, or report the time as something else than "steps".

include initial highscores file in the release package

The highscores file would enable people to compete against the developers, since we don't yet have a public scores server for people to compete against each other. The file in the git sources works out of the box as long as people run LambdaHack when they are in the main LH directory. With config file it also works wherever the user is when running LH.

Improve squad combat

Currently extra squad member on the same level actually make combat more difficult.

Display content descriptions

In particular, texts describing tiles and monsters should be displayed in targeting mode, perhaps with 'r'ecall command as in Angband. Room and cave description should appear in the message area when first entering and upon request later on. Item descriptions may be a part of the inventory UI (done).

Apart of the text descriptions, the content data should be summarized and pretty-printed. For now, in full, but in the future possibly only according to what was learned in previous games, not to spoil the content.

Auto-tune the eps parameter of Bresenham's line algorithm

The line is used for targeting and then launching ranged attacks. The parameter should be tuned so that the line from the targeting/throwing actor to the target and beyond is free from walls for as long as possible and, in there's a tie, going through the least number of unexplored tiles too. (Or perhaps unexplored tiles before the target should be more important than obstructions after the target). Probably the best we can do is to test all possible eps values and take the best.

The simplest case is auto-tuning eps for AI throws. Changes are needed in only 1 file and visibility does not have to be taken into account (this is cheating, but very mild, because AI won't consider throwing if the target actor is not visible, so it only helps with unexplored obstacles in-between, e.g., corridor turns).

When it's done, the same code can be used to auto-tune eps when the location of the targeting cursor changes. It changes in a lot of places, so it may make sense to join clocation and ceps, make the type of the aggregate abstract and only update it via updateCursorLoc in State.hs, auto-tuning eps wrt the new location.

More loosely related task is to teach AI not to throw, if even the best eps does not give unobstructed path (possible with, e.g., glass walls) or if the target is out of range. Another is to improve the visualization of the targeting line, e.g., indicating somehow that there is (and, separately, that there may be) an obstruction on the path to the current cursor location. Or perhaps change the colour of the patch from the first potential obstruction onward (currently colour shades indicate visibility).

Edit: perhaps only analyse obstacles up to the target, after all. Then resolve ties for AI by adding all good eps throws to the AI stragety set of possible moves. For player targeting choose among the good eps values randomly. Simiarly, when (some) monsters walk along digital lines, add all possible eps values to their strategy, so that it's in the end random. Otherwise, e.g., if eps==0 is preferred, AI may tend to sidestep player missiles (since they approach from the opposite directions), which would reward eps-micromanagement.

Allow multi-character macros

The following does not work yet:

; throw a dart at the closest monster
t: asterisk Return t Return

In gtk it could be implemented via unGetChan,
unless we prefer an explicit command queue, with flushing, etc.

Add justification text to each item content stat

And then use some of it when displaying item description. This could be done by defining something like

type Described a = (a, String)

data ItemKindF f = ItemKind
  { ipower   :: f RollDeep    -- ^ created with that power
    iweight  :: f Int         -- ^ weight in grams
    ...

type ItemKind = ItemKindF Described

Later this could be extended with other functors, e.g., giving lower and upper bounds, distribution, other help texts, etc. I wonder if there is a smarter way of expressing this.

Make Animation a content; then transform it linearly

For each frame, for each point, define if it's a symbol or a blank
and what the foreground colour is (even if it's a blank).
When the program wants to render an animation, let it provide
a list of colours (top it with infinitely many BrWhites)
and two positions, determine a linear transform from the positions,
apply it, create the finite maps and the concrete animation is ready.

The unclear point is how best to define a frame, having a list of constant
and variable colours and two fixed positions (say, (0,0) and (0, 1))
as the starting point. Ideally, for small animations, each frame
description should fit in a single line. Most of the animations
will ever only touch the two position. But some, e.g., light flashes
or explosions, may have quite large frames.

Edit: perhaps rename Animation to Particle, but then the shrapnel items and the projectile actors that carry them more akin to particles.

Edit2: light flashes are already implemented as projectiles with dynamic lights of large radius and explosions probably work OK as shrapnel projectiles, so we can focus on smaller animations.

Select a menu choice by scrolling with arrows

This will be useful for choosing an item to use, drop, etc., and for other commands. Scroll with arrows or movement keys, highlight the current choice position (inverse video or bright white on grey background?) , accept with RET.

Add ranged combat animations

Draw the flight of the projectile in ranged combat. Use the digital line algorithm, draw in real time. We need some kind of timing and it should be independent of the computer speed and drawing frontend.

Rewrite inventories

Rewrite inventories according to the wiki. This includes UI, but also the ability to set a weapon for melee and ranged combat, with good defaults. This is important when heroes melee or attack at a distance automatically. A list of all items on a level/levels and a summary of inventories of all actors at once would be useful, too.

Switch to client-server

Server has full game state, clients have Perception, history, etc.. Server performs state changes, clients have AI and UI (both can be enabled or disabled, even partially, e.g., AI for all but 1 member of the party, or full AI control, but display of game board via UI). The clients and the server either run on different threads or in different executables and then communicate via network. Naturally many players and many independent AI parties are then possible.

Let body parts in organs inventory affect abilities of actors

Let actors start with a few legs/arms/tentacles in inventory. If a leg is missing, the actor can't kick in combat, has trouble moving, etc.. Let body parts be targetable in combat. If a body part is critically wounded, it's removed from the inventory. If it's later regrown/replaced by an implant/etc. a new item is added to the inventory. The body parts in the inventory are displayed separately and some operations are restricted (e.g., dropping an arm is forbidden and picking up a severed arm does not re-attach it). Since body parts are items, they are specified in game content, just as any other item (this bit is already done and they are specified in ItemKindOrgan).

Improve High Scores

Add (the main) hero names, exp and level, detailed cause of death, OS user name. Generally touch up.

Save files in the background

This is especially important for game saves, which can be large and which are backed up every time a hero descends or ascends a level. Saving, especially of a snapshot of game data to multiple files (e.g., savegame and diary), should be protected with locks, to avoid corruption or out of sync data in case of simultaneous save and restore.

This should be done generally, in Utils/File.hs, and used for save games, high scores, config files, diaries and anything else we may have.

Add cooldown and fatigue

Note: extremely tentative and probably distant future work. Perhaps, with reaction fire, etc., the goals are now very different that described below and e.g., numerous parties should have extra drawbacks. Fatigue could be used for that as well, probably, or for any other balancing we may need in the future.

Fatigue would be a variant of cooldown for actions, where you can perform the action a few times in succession before you have to stop performing it to cool down. Let it apply to any action, not only to special combat feats. Cooldown will give advantage during a battle to a more numerous party, up to a certain threshold. Characters with few abilities with cooldown can be better suited for solo missions.

regression: Esc does not escape from highscores slideshow

Turn branch, release candidate.

Previously, when saving (and probably being killed or winning, too, but it's less important) if the first keystroke after "y" was "Esc" then no further high score screens were shown. Now they are shown (actually, it's probably only 1 extra screen, but in the future we may also show a screen with a summary of this hero, etc.). If the player is saving often or is in a hurry, it's good to give him an option to skip all the fluff.

Other than that, I didn't see any more errors in the release candidate, though I've advanced my character only to level 5 and a few others to levels 1--2.

High score table, boolean flags

Several functions dealing with high scores make use of Bool flags, for example, a ScoreRecord contains the two Bool fields "killed" and "victor". This suggests that four combinations are possible, but actually only three are valid (killed and victor can't both be true). Also, in the future, we may add more reasons for ending up in the high-score list.

So, the right thing to do is to introduce a datatype:

data Status = Killed | Camping | Victor

and even better, to include data such as the level that makes sense for only some of the cases:

data Status = Killed LevelName | Camping LevelName | Victor

Port combat animations drawn via timed threads to other frontends

Currently they are implemented inside the GTK frontend. Ideally, all the threads machinery would be factored out of the frontend code. Then it would be disabled for frontends that are not thread-safe (if any) and tested with the remaining ones. It's rather not worthwhile to implement combat animations via delays on a single thread for the problematic frontends.

2 eyes reported as killing me

This is turn branch, release candidate, but the bug may old.

I'm fighting with 2 eyes at once and I see something like:

The eye kills you. The eye kills you. -- more
You die. -- more

The two "kills" messages relate to the 1st and the 2nd eye respectively.

Error on run

I built it on OS X 10.8 (Mountain Lion) with the latest Haskell Platform. I had to build MissingH specially with:

cabal install --ghc-option=-XDatatypeContexts MissingH

After building, when I ran it I got this error:

lambdahack: user error (Curses[-1]:init_pair)

Make a DSL for English sentences

There is already too many functions in Grammar.hs. Instead, when constructing a sentence, specify the sequence of constituents of the clause (subject, verb, object, etc., plus free-form verbatim text snippets), where to get the roots of the words from (from actor, item including the number, item singular, verbatim), whether not to capitalize, whether not to end with a dot, etc.

If there's already a slim library for that, use it. if not, preferably publish this independently (without the actor/item bits).

Permit speculative moves, with undo/redo and consistency check at commit

Let the player perform moves and simulate the server (using only the information available to the player). Undo/redo at will. When committing, stop and decide whenever the messages from the server are not as expected. This is especially useful on levels with fully visible or regular enough map, in combat and when building things. This may actually make the parts of the game with not a lot of tension bearable in the 1-move per turn multiplayer mode.

Perhaps also allow each player to have, say, 10 tiles per level (or 5 tiles more than the current average for other players) revealed when moving speculatively. When the speculative moves are cancelled, the terrain remains revealed. This is risky, because it may encourage micromanagement and also give extra advantage to experienced players.

This is related to #17 but somewhat harder. #17 (ordinary undo/redo, almost complete already, but with no UI hooks) is useful in its own right, e.g., for replay or for single-player mode, where revealing information is not unfair to other players and undoing actions on the server, as well, is not out of the question.

Implement light sources

I'm not sure we should have ambient light in some rooms or not. Regardless, we need dynamic point lights, e.g., as a wall tile (with a torch stuck in, if we only let items emit light, not tiles), as a lantern or flare carried by actors, thrown and shining for some time after they are dropped drop on the ground. I think shiny projectiles should not shine on each tile they fly through, because then animations are not optional any more, but give extra info to the player. Not sure they should shine on each tile they stop between turns. For sure they should shine where they drop. Similarly for fast shiny monsters (or monsters carrying light sources), but they should definitely light their surrounding on the tiles they stop between turns. So, by analogy, shiny projectiles should do too.

That probably requires that each shiny item and each actor (not only heroes) has a set of tiles reachable by sight computed, but the latter was due for a long time, anyway. Moreover, it requires recomputation of visibility (but not reachability, so can't be too costly) after each actor movement. All in all, it will probably simplify the core of the Perception calculations.

Edit: clarified. A bit related: add night vision (e.g., each actor has it with radius 1).

Choose from menus with mouse

This will be handy for the go-to-target command, for tooltips and for choosing from a menu (e.g., choosing an item to use or picking a Main Menu command).

Compile game content after the game is started

In this way, content can be changed by game users that can't normally compile Haskell programs. That would also speed up pre-release game balancing and make it possible to auto-balance content by tweaking the original content code.

One possibility is to use Haskell plugins to evaluate game content

http://hackage.haskell.org/package/plugins

I'm not sure it can be made to work. E.g., the evaluated code is supposed to be closed and currently game content is not, so quite a lot of other code would have to be included as well. Also, we need the ability to save the game content at the game's end, after it's auto-balanced. For this, serialization may be a better fit, unless we want to parse and pretty-print a subset of Haskell. OTOH, Haskell is very handy for defining content, e.g., to avoid repeating stuff or to insert some extra data invariants specific to particular game.

Scroll dungeon view

After #113, #114 and #115 are done, let the different parts of a level be shown in the viewport. I'm inclined to center view continuously on the player leader position and on crosshair in aiming mode. The letter lets player view any part of the level, but the former doesn't burden the player and the UI with extra commands, scrollbars and tasks.

The issue to decide is where to stop centering when the player is close to the edge of a level. Perhaps never stop centering, which reduced the visible level area but, e.g., makes multi-line messages less likely to obscure the leader. Also, it's then easier to locate the leader visually --- he's always exactly at the centre of the screen. However, if the whole level fits in the viewport, it's iffy to show just a quarter of it.

Recommend Projects

  • React photo React

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

  • Vue.js photo Vue.js

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

  • Typescript photo Typescript

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

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

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

  • web

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

  • server

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

  • Machine learning

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

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

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

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.