forthebadge forthebadge Gem Version

This gem was last updated on the 17.01.2024 ( notation), at 14:05:23 o'clock.

logo logo

About this project - the games_and_rpg_paradise gem

This project has been merged from two other gems:

1) the rpg_paradise gem

2) the games_paradise gem

The games_paradise gem was focusing on games in general - primarily via implementation through ruby, but secondarily also allowing other languages.

The games_paradise had as a focus to contain several different games, some of which are playable, some of which are not.

The long-term goal for this project was to not only showcase different games, including bindings to these games, but to additionally explain the relevant parts that led to the design and code in use by these games. This shall be especially helpful for newcomers to game design; not necessarily newcomers to the ruby programming language. You are encouraged to first learn ruby properly, before diving into this gem / project.

Different authors may have written these games originally; if possible these games will be maintained by this gem here.

Note that the term 'games' in this context refers mostly to small games. I do not have the time and motivation to create large commercial games - the time investment would be too high. But the games_and_rpg_paradise project will try to integrate smaller games whenever possible.

Goals and Meta-Goals for the games_paradise component

Over the last ~20 years or so, sort of (back to 2004), I have noticed a LOT of ruby code vanishing from the world wide web.

On top of that, old code written in, say, 2006, often no longer works today; this has been the case with ruby-gtk2 in particular. A lot of the old code no longer works. Porting that old code 1:1 takes quite a bit of time and often is not even possible because associated bindings, such as for gnome, no longer worked.

I think it is rather unfortunate when we lose code that has at one point in time worked in mankind's history perfectly fine - not just in regards to old games, but old code in general. Often the necessary transition-time and time investment spent for making the old code work again isn't that much, if someone would maintain the code every few years. So it is clear that these projects were completely abandoned at some point in time; otherwise these would be maintained every now and then. People move on to do other projects as time passes by - that happens. Sometimes you abandon an old programming language and use a different programming language, so it makes little sense to maintain legacy code that you once wrote.

The wish to have old code still work is thus one meta-goal for the games_paradise project. Ideally we can preserve older games that were written (in ruby), so that future generations of ruby users can benefit from this as well.

There are more goals in addition to that, smaller ones. For example, I would like to model old forgotten games, but without necessarily (re)writing the whole game. I am fine prototyping some game skeletons and collecting information about this in different .rb files or in different .yml (YAML) files. This should make it a bit easier for other people to eventually take up on that task and (re)create these old forgotten games. See also the TODO list of game prototypes in the doc/ subdirectory.

(1) The Hangman game

The classic hangman game! This variant was written by me. It was the first game I put into the games_paradise project (now known as games_and_rpg_paradise project), because it is a fairly simple game to implement and I wanted to prototype something quickly, so I started with the hangman game.

Right now a commandline variant exists (at bin/hangman), incomplete ruby-gtk3 bindings to that commandline code as well as incomplete ruby-gosu bindings. The commandline variant should work just fine, though; just call bin/hangman or the hangman.rb file directly.

The ruby-gtk3 bindings work to some extent; you get a top-buffer showing the current hangman progression frame, and you can guess the next character and so forth. Restarting the game works properly as well since as of 2021. Minor things may still be missing, mostly usability improvements. I may get to do so, if time and motivation permits (perhaps!).

If you have the dictionaries gem installed - see - then you can also use a random english word (if it is registered in the dictionaries gem), via this commandline flag:

hangman --dictionary

The commandline variant makes use of a few ASCII "pictures", such as GamesAndRpgParadise::Hangman::RIP_ASCII_PICTURE signaling the game over state. If you want to make use of the Hangman game in a GUI setting then you may want to prefer using real images rather than ASCII text.

If you rather want to use your own text file, to determine the guessable word in use, you simply pass the file name into hangman, like so:

hangman /Depot/

The ruby code will then randomly grab a line from that file. I recommend one-word-per-line; this makes it all simpler.

To query whether a single character, such as 'a' is included in the word-that-is-to-be-guessed, you can use the following method on an instance of class Hangman:

is_this_character_included? 'a'

To start the ruby-gtk3 bindings, you can use the following invocation:

hangman --gui

Use the entry in the middle as the main mean of obtaining user input. After typing the character, hit the enter key or the button to submit input.

You can also tap into the dictionaries gem via the GUI, by using a check-button. By default it is not active, so the user has to click on it in order to enable the use of the dictionaries gem. Again, this requires the prior installation of the dictionaries gem.

(2) TicTacToe

TicTacToe is a very simple game. Right now a ruby-gtk3 widget exists, and a commandline variant. I intend to add a ruby-gtk2 implementation and a variant with gosu as well, one day. :)

Since as of October 2023, ruby-gtk2 appears to be deprecated. This also means I no longer offer ruby-gtk2 code in general, so only ruby-gtk3 and ruby-gtk4 may work in this regard.

(3) Minesweeper

Many years ago I wrote a minesweeper variant in ruby-gtk2.

In February 2021 I adapted the code to ruby-gtk3. Unfortunately there are some bugs, so it is currently not playable, but you can still start it and have a look.

Eventually I'll get to fix it and clean it up, but for now I just wanted to integrate it as-is.

If you need a base map for your own code in regards to minesweeper, have a look at class GridPlacer. This one will place the mines for you, so you only have to handle the buttons in a GUI for that.

There also exists a 'toplevel' method for placing a grid, which can be used like in this way:

require 'games_paradise'

GamesAndRpgParadise::Minesweeper.grid(n_mines: 20, n_fields: 100)

The field should ideally be a quadrant; at some later time I may add rectangles as well.

If you want to use Emojis rather than images for a minesweeper clone, you could use the following emojis perhaps:

'' # For a flag.
'💣' # For a bomb.

(4) Tetris

A tetris clone exists for gosu (gosu-bindings). Incomplete ruby-gtk3 code exists as well.

This isn't very pretty so far and not documented well, but it works somewhat. I may improve on this in the coming months.

One tiny improvement that was made was that a "help" menu will appear if you press the "H" key. That way you can disable the sound (via the "P" key). I may have to replace the audio file with a smaller, and less noisy one though ...

(5) Mastermind

A prototype exists in ruby-gtk3 for mastermind. It works somewhat, but isn't very well-polished. My goal here is to clean the code base up, and then add bindings to gosu.

(6) 1010

This game is a bit inspired by tetris, but it is a tile-based puzzle game where you, as the player, place tetris-like objects onto a 2D grid. The main implementation is for a single player. The default square grid is a 10x10 cells.

The player is presented with a random selection of tile patterns of various shapes, 3 at a time, which are to be placed on the board, one by one. If the tiles consume an entire row (or column), without any empty cells in between, the row (or column) is cleared up so that more tiles can be placed.

Each cell consumed by the tile counts towards the score of the game.

The aim is to place as many tiles as possible to increase the net score. In its most simplest form, each cell adds 1 point to the score. A variation of the game can be to assign different points for each color.

You can press and hold the H key to see help-menu at any moment in time when running this game.

Currently some gosu-bindings exist to 1010, but they are not necessarily perfect. Ideally we can also offer ruby-gtk3 bindings in the long run - let's see.

The original code was written by shanko, and you may be able to see the repository here:

The modifications in games_paradise are mostly some style-clean up and more documentation within the .rb files.

The licence for 1010 is MIT.

(7) Memory games

Memory games are, for example, where you have to remember two cards, click on both cards, and then see how these are removed from the available card pool. Currently a simple version of this exists, for gosu.

The original variant was written by Boyan R.:

The code in games_paradise has been updated a bit; more comments were added as well, and I removed a few global variables (but there are still some left, duh ...).

If you want to start that game with a specific number of cards, you could use this commandline invocation:

memory --ncards=18

Note that the game map is currently too small to display lots of cards; and we'd need to add more card-images too. But in principle this support has been added by me in February

  1. Small improvements for the win! \o/

(8) Aero-Exploder

This variant was originally based on the code written by SavageHolycow ( ).

It also used lots of global variables, which is not ideal. I think many contributions come from folks who do not really know ruby that well. There is only rarely a need for a global variable really.

(9) Battle City

This is just a collection of some tanks trying to attack your home base, with you attempting to defend it.

The original game was found here:

I did not modify it much at all other than adapting the ruby code a bit.

(10) Duck Hunt Calculator

The project originally was created by bestguigui, as a math-exercise trainer, and described here at:

The idea is quite nice; in the long run perhaps I will replace this with a non-animal or something rather than ducks, and perhaps add more difficult calculations with different levels. But for the time being this shall suffice as it is.

(11) Billiard

The project originally was created by Makki and mentioned here at:

Note that from a historic point of view, there used to be tons of billiard-like games before. Sometimes they had interesting names, such as Arcade Snooker.

The current version (in August 2021) looks like this:

Tons of small things are missing - for instance, counters on the top right to indicate points, and how many shots with the cue it took. Also player names and multiplaying (switching turns).

The code for this game lacks documentation, though, and has to be re-organized. Ideally we should move this into general methods, some toplevel-methods for calculating arcs and torque. But for now I'll keep it as it is - we progress in baby steps here!

(12) Maze Puzzle

Not sure yet whether to keep this one or not. When decided to keep, this stub will be expanded.

The original code can be found here:

and here

(13) Garden Hero

This game, available from, under the MIT license, has been written by Zhassulan Zhussupov.

I included this into the bundle here, largely for two reasons:

a) It reminds me of the original final fantasy variants on the gameboy, a long time ago. May be useful to showcase a game that taps into that theme.

b) I think the graphics but also the movement-path as such could be improved in a general way, so as to allow people to more easily generate similar games.

(14) ChinguRoids

Under the MIT licence. Not yet sure whether it will be retained as part of the games_paradise project, though. A lot of the code has to be polished .... hmmmmm.

(15) Godmode

This is an idea for a game where heroes fight in an arena.

It will be a bit more complex than my other games, so this will all take time - it is very incomplete right now.

(16) ParticleSimulator

This is just a demo, mostly, to show how particles float.

In the long run I may add more options to control it, different shapes and so forth - but for now this is really just based on code written by someone else for the most part.

(17) Kaiser

This is unfinished - an old strategy game. Kaiser is german for emperor. Your goal is to work up your way towards becoming the new kaiser, before your own demise (e. g. natural death).

This is currently an unfinished game, as said, but you could look around on the www to see for old links, such as the following, to get an idea how it was played:

Todo list:

  • Add libUI bindings.
  • Add fxruby bindings.
  • Add tk bindings.
  • Add www variants for .cgi and sinatra.

The licence for the games_paradise project

I thought for a while whether to use GPL or LGPL for the games_paradise project but then decided against it, and opted for MIT instead.

The reason is that most of the games published in ruby are under the more liberal MIT licence. I like the GPL too, but I think for an "umbrella" project that tries to gather as many (playable) games as possible, it is better to use a less stringent licence than the GPL.

You can read up on the MIT licence here:

The most important part is the "no warranty" clause, aka to translate this into layman terms, "use at your own risk".

Project-policy for images distributed via this gem

This gem comes with several images; some are more generic, whereas others are more specific to a particular game at hand.

There are different ways how images could be handled. A simple way would be, for a given game, to put all assets (including images) into one directory, together with the source code. This then makes it easier to distribute the games in a standalone manner.

However had, the games_project is an umbrella project; it tries to share code across different games, in order to write (and use) less code. The same approach is applied to images - whenever possible, images should be re-used.

All images should be put under games_paradise/images/. If they are generic and re-usable, they should be put under misc/. If they pertain to a particular game, a subdirectory should be created under that directory, with the same name as the game itself.

I wanted to document this new policy, so that eventually everything within this project can be aligned to that policy. Thus, enforcing consistency.

History of some of the games

This subsection here mentions a few games, in particular when they are distributed within the games_paradise project.

The classical Memory game is also called concentration.

Archon: The Light and the Dark was developed by Free Fall Associates and distributed by Electronic Arts. It was originally developed for Atari 8-bit computers in 1983.

Tetris first appeared in 1984.

Defender of the Crown was released on Commodore Amiga in 1986.

Hints, Tipps and Tricks for Gosu

You can create a borderless Gosu::Window via borderless: true. This will hide all window chrome. You can toggle this at a later time, if you want to, via .borderless=.

If you want to mirror a sprite then you can use the parameters center_x as well as center_y before the scale parameters.

Hints, Tipps and Tricks in general when designing games

  • Ideally try to store the dataset-assets for your game in a neutral format, rather than, say, hardcode them into .rb files. YAML is quite well-suited for this, but XML, despite being exceedingly verbose, may also be useful.

  • Separate concerns and aspects whenever possible. Try to build many small components and slowly put them together rather than build monoliths that are hard to decouple again at a later time.

  • If you can fake it, then fake it! This refers to trying to optimise on what is necessary, and if it is not necessary then you do not necessarily have to display it on the game map.


Since as of August 2022 a prototype for the Solitaire card game, including images, has been added. This does not currently work, but the foundation for this card game has been added, so expect this to work eventually.

Games Timeline

This is just a timeline of games that were interesting to me, from a historic point of view.

Guidelines for using Gosu

The following section just collects some hints offered by other ruby users over the years in regards to Gosu.

They are listed here without any sorting done really.

  • Do not draw anything that is not on the screen. Only draw what the player can see, unless otherwise ncessary.

  • You can not call draw() methods from within the main update() method.

Guidelines for using the custom modifications in games_paradise/modifications/

The games_pardise gem comes with a few modifications to module Gosu. This has been done to give you the option to use a nicer, shorter API, in addition to the official upstream API.

For instance, the regular API for setting a title goes via:

require 'gosu'

class GameWindow < Gosu::Window
  def initialize
    super 640, 480
    self.caption = "Gosu Tutorial Game"

If you use the custom modifications then you can simply use:

set_title "Gosu Tutorial Game"

instead. This may not be a huge gain, but it is nonetheless fairly useful.

To require all custom modifications for Gosu you can require the following file (that is, simply use the require line that is shown below):

require 'games_paradise/requires/require_the_custom_gosu_modifications.rb'

To make this even shorter, you can use the other require file:

require 'games_paradise/requires/require_the_custom_gosu_modifications_and_gosu.rb'

This has require 'gosu' on top, so you can save one require line if you use this.

The .rb files under games_paradise/modifications/gosu/ have various additional code that may be useful for you. Not all of these are documented on the page here, but you may be able to use some of them re-used in different projects that I have created in the past.

For instance, rather than creating a new gosu image via:

image ="media/space.png", tileable: true)

You could use this alternative:

image = image("media/space.png", tileable: true)

Yes, it is not a huge "win", but it leads to slightly shorter code nonetheless which is not bad, in my opinion.


Using images is quite central to gosu. You can draw a new image onto the screen via .draw().


.draw(x, y, z = 0, scale_x = 1, scale_y = 1, color = 0xff_ffffff, mode = :default)

This will draws the image with its top left corner at (x, y).

tileable: true is specifically used for background images and map tiles.

What is a sprite?

A sprite is a game object that contains location data. Typically it is an image, with a rect-coordinate. It may also exhibit certain behaviour.


Vier Gewinnt was a fairly popular game back in the days in german-speaking areas.

In December 2022 a commandline variant was added for this game.

It looks like this:

It can be invoked from the commandline via:

games_paradise --vier-gewinnt # The '-' can be omitted, of course.

At a later time some GUI code may be added for this. For now, though, it'll remain as this.

Note that a variant for jruby exists at well, under:



You can run bin/minehunter to start a minehunter (minesweeper) terminal game. This requires the installation of various tty-gems.

It looks like this right now:

Status of the RpgParadise component

This project is presently (that is, as of the year 2023) rather incomplete. In the coming months, possibly years, this whole documentation will be slowly expanded and become more organized in the long run.

What is the RpgParadise project all about?

The RpgParadise project is basically my attempt at trying to organize code and content gathered in the last some ~25 years or so, give or take, in regards to various RPG-related projects. RPG as abbreviation stands here short for Role Playing Games - games such as Dungeons and Dragons, and similar roleplaying games..

You may be able to find different roleplaying games, and code in support for these games eventually as part of the rpg_paradise gem.

In many ways this is the legacy of our old local pen-and-paper RPG group (which disbanded due to lack of time and shifting interests), but I will also try to add content and code that may be relevant for other folks out there. Legacy should be useful for the future after all. \o/

Since this is continued work-in-progress, with a low priority compared to other, more important projects, please do not be surprised if some content is currently quite volatile, and may be removed or massively changed from one release to the next. Updates may be sporadic, so do not expect that many major sweeping changes to this project.

Some parts of the project tend to be more stable, though, such as the API for simulating a roll of die/dice, to determine randomness within a pen-and-paper roleplaying game. But some other parts of the project are significantly less stable, and change may happen at any moment in time without much announcement.

The game world of Sarlem

Sarlem was our primary roleplaying-game world, developed from scratch, starting from drawing the world map in the mid-1990s. We had many campaigns and scenarios in our local pen-and-paper roleplaying group.

The setting for the game world Sarlem is still developed these days, but very irregularly so, at a much slower pace now. It is now just a hobby project that is less relevant compared to many other projects. New content may still be added, and old content adapted and improved upon, but by and large the game world is "semi-frozen" - there is no guarantee that it will be updated or modified regularly anymore. It may happen, but not necessarily due to it being a hugely important project, as-is.

The RPG world of Sarlem combines features found in different RPG settings, while trying to come up with a unique, but still cohesive, consistent and coherent background setting for the whole game world. Different influences shaped Sarlem, including game worlds such as Midkemia, Ravenloft, Forgotten Realms, DSA ("Das Schwarze Auge") and so forth.

In many ways, this component (the game world Sarlem) is still the most important part of the whole RpgParadise project currently, as it is the only one that may still receive major modifications in the future, give or take. For instance, one objective here is to flesh out the details of the game world in such a way as to allow us to build different games on that game world. For that, evidently, you need a lot of details that can help you when you have to write the necessary code. You want to make the NPCs realistic, as well as the monsters that inhabit the world. You want to flesh out landscapes, cities, buildings, places, the lore of the game world - tons of things and details to do in this regard.

Note that the various .cgi files that are currently used - and distributed within this gem - try to make use of images, but these images are not distributed with this .gem, for various reasons. A very simple reason as to why these images are not distributed as part of the gem is the sheer size - if these images were to be distributed as well, the .gem size would suddenly be well above 50MB, and that is just way too much for a ruby gem really. People expect a smaller size for a gem typically, say 50kb up to at max 5mb - or something like that.

You could perhaps supply your own images and treat the existing ones as placeholders instead, if these missing images are annoying you - use your fantasy and imagination in this event. A barbarian may always look like a barbarian, more or less. :-)

Update as of September 2021: code was added to the cyberweb gem to allow a substitute-image to be used, if a local image can not be found. The method I use here is called dimg2(), which is similar to dimg(), which in turn is a shortcut for draggable_image(). I have added a few remote images to the rpg_paradise/dsa/index.cgi page, so this page should show at the least two images now. This is mostly a "prototype idea" for now, but more work is underway to turn more instances of dimg() into dimg2() as far as the various .cgi files are concerned.

The idea in the long run is that, even if others can not see the images I use as such, they can use substitute images instead just fine. I do not think I will add a substitute for all images registered as-is; there are over +1000 images for the RPG-related content. But for some pictures I think I will provide substitute images as well, at the least for the initial pages a visitor may see. Mostly this is a prototype, so do not expect this to be used too much for now - it is a proof-of-concept mostly, in the event we may change this one day in the future. I may also add some sinatra-bindings in the future for people who do not use .cgi files anymore.

A file-size reduced image of the main continent of the game world Sarlem, last updated almost 20 (!) years ago, can be seen here:

The two main continents of Sarlem are the Western Realm and the Eastern Realm; a middle-sized "continent" separates these two on the northern part, more an island actually. This island was created by Great Magic.

(The map could really need an update since it was hand-drawn and some parts of it are not completely correct - but at the least you can get the general idea of it.)

To the east of the Eastern Realm is the magical island state Tai, home to swordsmen and mythical ghosts called 'kami'. The good Yin Kami try to protect and preserve, whereas the evil Yang Kami seek to dominate and destroy, more or less - even though they both have to be maintained in some kind of loose balance, as otherwise out-of-world chaos may be the consequence.

Some Kami may also be tied into items, yielding special abilities and powers to them.

(Note that the Tai island is developed the most overall in the whole setting, largely because it seemed simpler to focus on a single island, than fill in details to larger continents.)

To the south of this an even larger continent exists, but this one is less well explored; most of the gameplay happened on the two main islands (the Western Realm and the Eastern Realm). These southern areas are more arid and hot, offering living space for lizard-like creatures. During the history, the most current events involve a large army being ferried towards the Western Realm, beginning an invasion there.

The beginning of the invasion was, more or less, the time when the old RPG group had to disband for various reasons - most importantly lack of time. Things change as you get older, that is just unavoidable.

Note that ideally all of the pages that are part of the game world Sarlem should be written in english, but they were originally written in german. I can not guarantee that the translation will ever reach 100%, so there is a LOT of german content still left - but new content will only be added in english. It just is not one of the highest priorities for me to port the remaining german content into english. It may happen only when time permits and other projects not have higher priority than this one does.

Next, a few code-related aspects may follow.

To determine the current year used in Sarlem, you can use:

RpgParadise::Sarlem.year? # or include the RpgParadise namespace, then just do Sarlem.year?


Wesnoth is a turn-based open source strategy game.

Many years ago I wanted to use the gameworld of Sarlem for creating a campaign in Wesnoth. I may eventually continue with this project in 2021/2022. If ruby code is added in this regard, the subsection here will expand with some documentation.

The first chapter (or first part) of that campaign will play around Hratie, which is in the southwestern part of the game map.

A later chapter will introduce the powerful lance riders of Kuulk.

As to how individual chapters will tie together remains to be seen. A few heroes may appear that relate to the main game world.

Map tile code:

Mm ... Mountains
Gray Deep Water   Wog
Medium Deep Water   Wo
Tropical Deep Water   Wot
Gray Shallow Water   Wwg
Medium Shallow Water   Ww 
Tropical Shallow Water   Wwt
Ford   Wwf
Gray Coastal Reef   Wwrg
Medium Coastal Reef   Wwr
Tropical Coastal Reef   Wwrt
Swamp Water Reed   Ss
Muddy Quagmire   Sm
Green Grass   Gg
Semi-dry Grass   Gs
Dry Grass   Gd 
Leaf Litter   Gll
Dark Dirt   Rb
Regular Dirt   Re
Dry Dirt   Rd
Regular Cobbles   Rr
Clean Gray Cobbles   Rrc
Overgrown Cobbles   Rp
Icy Cobbles   Rra
Ice   Ai
Snow   Aa
Desert Sands   Dd
Beach Sands   Ds
Oasis   ^Do
Rubble   ^Dr
Crater   Dd^Dc
Mixed Flowers   ^Efm 
Farmland   ^Gvs
Stones   ^Es
Snowbits   ^Esa
Small Mushrooms   ^Em
Mushroom Farm   ^Emf
Desert Plants   ^Edp
Desert Plants without Bones   ^Edpp
Windmill   ^Wm 
Campfire   ^Ecf
Sconce   ^Efs
Brazier   ^Eb
Lit Brazier   ^Ebn
Fence   ^Eff
Iron Fence   ^Eqf
Stones with Sand Drifts   ^Esd
Water Lilies   ^Ewl 
Flowering Water Lilies   ^Ewf
Trash   ^Edt
Remains   ^Edb
Great Tree   ^Fet
Snowy Great Tree   ^Feta
Dead Great Tree   ^Fetd
Dead Great Oak Tree   ^Feth
Tropical Forest   ^Ft
Rainforest   ^Ftr 
Palm Forest   ^Ftd
Dense Palm Forest   ^Ftp
Savanna   ^Fts
Pine Forest   ^Fp
Snowy Pine Forest   ^Fpa
Summer Deciduous Forest   ^Fds
Fall Deciduous Forest   ^Fdf
Winter Deciduous Forest   ^Fdw
Snowy Deciduous Forest   ^Fda
Summer Mixed Forest   ^Fms
Fall Mixed Forest   ^Fmf
Winter Mixed Forest   ^Fmw
Snowy Mixed Forest   ^Fma
Regular Hills   Hh
Dry Hills   Hhd
Dunes   Hd
Snow Hills   Ha
Regular Mountains   Mm
Dry Mountains   Md 
Snowy Mountains   Ms
Desert Mountains   Mdd
Basic Stone Floor   Irs
Ancient Stone Floor   Ias
Royal Rug   Icr
Normal Rug   Icn
Cave Rug   Urc
Basic Wooden Floor   Iwr
Old Wooden Floor   Ior
Beam of Light   ^Ii
Cave Floor   Uu
Earthy Cave Floor   Uue
Dark Flagstones   Urb 
Cave Path   Ur
Mushroom Grove   ^Tf
Lit Mushroom Grove   ^Tfi
Mycelium   Tb
Rockbound Cave   Uh
Earthy Rockbound Cave   Uhe
Mine Rail   ^Br|
Mine Rail   ^Br/ 

and many more ... ;-)

See here:

How does a campaign in wesnoth look like?

First, let's pick a random example such as the campaign Secrets of the Ancients.

See the following link:

If you analyse that file carefully then you can find lots of key=value mappings.


name= _ "Dark Business" # this is evidently the name of the campaign # this specifies the map to use

id=02_Dark_Business # some random ID
next_scenario=03_Bandits # chain it up to the next scenario
victory_when_enemies_defeated=no # specify ad-hoc win-conditions

To specify a side, use:

  user_team_name= _ "Guards"
  {FLAG_VARIANT loyalist}

Uprising in Hratie

This will be my first campaign for wesnoth. What follows next is a spoiler, so skip it if you want to play the campaign spoiler-free.

I will start in a non-linear manner, though. The first part I will be working on will be for a later chapter - aka The Lance Riders of Kuulk.

What follows next is potentially a spoiler, so if you want to play the campaign first, I recommend you to NOT read this subsection.

Here the lance riders will have a few minor skirmishes, then drive forward to free Landeral (the city proper of the Western Realm) and have some success in doing so, before they will be countered back again to Kuulk eventually, and have to retreat to other parts of the Western Realm when crushed by the invading forces.

Design-wise the riders will be quite powerful and deal a lot of damage but will be vulnerable to pikenieres and halberdiers. Archers will deal only half damage against the lancer riders though. One leader (hero) of the lance riders will have unique abilities, such as power charge (massive damage but movement is reduced afterwards for three rounds; and using this works only three times per scenario).

Two basic types will be made available for the lance riders of Kuulk: heavy armoured and medium armoured ones. Heavy ones will have steadfast ability. Medium armoured ones move one location further.

The first scenario begins when Hulrias gathers some wood. A few animals may appear but otherwise nothing special happens. The scenario aims to introduce Hulrias, son of Minaros, and introduce Hratie, as well as some villagers. Hulrias is actually one of the few shepherds rather than lumberjacks in the village. He has a loyal dog (Jaluee), with a brown fur.

The second scenario is a bit more serious. He returns to the village, deposits some of the wood, talks to others, then goes to guard the herd of sheep against some wolves.

The third scenario is when he brings the sheep back to the enclosure from the hill. Upon being there, he notices a large black ship. Sensing impending danger, he swiftly returns to the village, only to then be attacked by some pirates and three black knights. The villagers manage to beat the invaders back, but many villagers die.

A day after that scouting party, the fourth scenario happens. The village is destroyed but Hulrias manages to flee. His loyal dog dies fighting another black knight, but allows him to escape.

Epilogue: Hulrias warns other villagers of impending attacks. While most eventually return, Hulrias ventures forth to warn others - eventually joining the rebel forces of Tanlamur.

The third chapter: The Lance Riders of Kuulk.

These are used to drive against the invaders but they tend to fail more than win. This is meant to introduce a few NPCs, as well as Tanlamur who is quite important for the whole campaign. The leader of that lance rider force is Gerric and he has a few unique abilities, such as powercharge.

See also:

In a later stage we have a battle of Landeral (large city fighting) and a fight at Andruumar.

We may also have a few other fights, perhaps against the dwarves and elves as well.

General design hints for the game Wesnoth

Branching is the situation when the outcome of a scenario can lead to two (or more) different follow-up scenarios. It's a bit similar to old gamebooks.

This is usually done via a menu choice presented on victory of the scenario.

See here:


This refers to modifications, and content, of our old RPG group related to DnD (dungeons and dragons).

We used custom modifications rather than the "original" rules, though.

A few ad-hoc entries are part of that module. For instance, there is a small class representing a "deck of many things".

Usage example for this:

require 'rpg_paradise/dnd/items/deck_of_many_things.rb'

(This has been inspired by Baldur's Gate 2 EE, so the code is rather similar. Perhaps in the long run this class may become more flexible, to adjust for different use cases of how people use the "deck of many things".)

Since as of April 2022 the sinatra interface allows us to simulate this as well. The API may then be http://localhost:4568/dnd/deck-of-many-things - a single card will be drawn. At some future point this may be extended, but for now it just remains simple, to showcase the functionality. Perhaps all content related to Sarlem may one day be available via sinatra as well - let's see.

DnD 5th edition: weapons damage

The .yml file weapons_damage_table.yml contains the damage output by different weapons for DnD 5th edition. It was last updated in August 2022.

To get a quick estimate of the damage range of these weapons, call the method:



The abbreviation DSA refers to "Das Schwarze Auge", another RPG, originating from Germany in the 1980s. Sadly the format changed a bit after its initial developer passed away, and what came lateron is a bit different compared to the original game; still mostly fine, but I was never as closely attached to it as I was during the original variant in the 1980s and 1990s. Different rules, a different focus, and most importantly different people do change projects - it happens almost everywhere, all the time, in other projects as well.

As an example: see Apple with and without Steve Jobs; then think about whether Apple is the same as it was when Jobs was still alive and in charge. Yes, the surrounding ecosystem changes as well, and thus induces changes into a company, but I am focusing here solely on Steve Jobs as a person or individual shaping a mega-corporation. I am not glorifying him at all in the slightest as that would be super-naive; see the court cases about illegal no-hire practices in the USA stealing money from developers, but I am talking about the creative mind. Without Steve Jobs, Apple's IQ really dropped down immensely. They lost a vision or rather replaced it one closer towards pure profit-maximization.)


Shadowrun-related content, under the namespace called RpgParadise::Shadowrun.

If you need to generate random team-names, for competing runner groups, have a look at the following method called:

RpgParadise::Shadowrun.generate_random_teams # defined in the file rpg_paradise/shadowrun/shadowrun.rb

Not all of the autogenerated names "make sense", so disregard those that don't make a whole lot of sense. That method was written to avoid having to spend much time thinking about names of other competitive groups.

A few adventures reside under the adventures/ subdirectory pertaining to our old RPG group. Unfortunately these are only in german; I do not think it is worthwhile to translate them into english, but if you are able to understand german then perhaps you may integrate some ideas from these adventures ("runs") as well. They are not necessarily 100% complete, though, may contain bugs, missing descriptions and what not - some of them only provided a general framework, and were adapted during a "live session", as-is, on the spot. So these are more blue-prints than finished, completed adventures.

You can read more about Shadowrun here:

Magic the Gathering

Do note that the RpgParadise project contains some code related to the card game called Magic the Gathering.

Most of this code, however had, was mostly written in the year 2016 (sort of) for quickly trading some old cards; I wrote that code merely to solve that particular issue at hand, and then move on. So the code has hardly been updated since that time, excluding the parts where images are downloaded - nonetheless, for anyone else who wants to build on this, I made the code available as part of the RpgParadise namespace. Just do not assume that this code will work without bug fixes past the year 2022.

The submodule called MagicTheGathering just bundles together some simple convenience code for secondary use about the card game magic the gathering.

For example, if you wish to find out the price range of your cards, you could in theory do so, e. g. via a .csv file that has all your magic cards listed.

There is some additional commandline-use, which will be described next.

Commandine use and available options for the Magic the Gathering submodule

There is an executable available, under bin/magic_the_gathering.

If you wish to see the available options, invoke that executable via the --help option:

magic_the_gathering --help

If "magic_the_gathering" is too long to type, add an alias to it. I added one called "magic" on my system, so I just have to type magic --help.

See the file menu.rb for all options available.

Determine the prices of some magic cards

Say that you may wish to find out the prices of some cards in a .csv file that you have available locally - this can be done via the method RpgParadise::MagicTheGathering.parse_file().

Pass a .csv file to that method and it will try to make sense of this (it defaults to european countries and thus euro as the main currency; if others wish to make use of another currency, then you either you have to add the functionality to use other currencies by yourself or bug me to add this. I primarily add code that is useful to me - other people may have to use their own code to the project, in order to make it relevant for them. I will merge in code that would be of benefit to other people, including other currencies. But since I only use euros, I have no need for other currencies.)

Batch-downloading the images


magic_the_gathering --download

the images representing the different cards can be downloaded, making use of ruby's open-uri method ( specifically).

The card list will also be made available in a yaml file as well as a .md file.

Setting the main card set at hand

The module-method namespace MagicTheGathering can keep track of the card set that is the currently "active" one, e. g. the one that your local group may play.

From the commandline, you can set it like in this way:

magic "--set Dominaria"
magic "--set Rivals of Ixalan"

The "" quotes are mandatory for now, due to input that could contain ' ' space characters, such as in the second example. While I could get rid of this, since it is a bit cumbersome, for the time being, please use the variant with "" quotes.

Once you have assigned the main set at hand, you can make use of the option --download to download all available card set to your local harddisc. (This may not always work, since there may be a bug or two, but it should work ok for the most part. Just ignore the smaller bugs for the time being - the main functionality is what counts most.)

Calculating how many lands you have to add to n non-land cards

The following ad-hoc method returns how many lands you have to add, based on a 0.4 ratio.


This would return the number 24, meaning that you have to add 24 lands to this. 24+36 is 60, so this applies to a 60 card deck. Obviously this is just a rough method - you may have to fine-tune this to the mana curve in your deck.

Width and height of a magic card

A magic card has a width of 63.5mm and a height of 88.9mm.

Miscellaneous tidbits and information about the MagicTheGathering submodule

To find out how many sets exist in total, try this method:


To quickly create a .zip file of the downloaded images, if you did download any images that is, you can issue this command:

magic --zip

You can also extract a .zip file that has been created via the above way, by issuing:

magic /Depot/jjj/

The .zip file has to exist, in order for this to work.

If you just want to generate a simple "statistics" file, a yaml file, then you can do so like so:

magic --yaml-stats

This may generate a .yml file at e. g. a location such as /home/Temp/magic_the_gathering/statistics.yml.

To generate a .yml file from all the various .md files, if you did download the remote dataset, try:

magic --merge

To show how many cards are available locally, try:

magic --stats?

To show which card sets are missing locally yet, try:

magic --missing?

Magic the Gathering also has arena (online game), and Arena Codes. The following table shows some of these codes; the idea behind this is so that you can quickly copy/paste them, if necessary. Some of these codes will become obsolete as time passes by, though.

To activate them, click on Store on the top bar once connected to the arena. In the top right corner you can see the Redeem Code interface where you simply write the MTGA promotion code.

Promotion Code Does what Expires
WrittenInStone obtain Nahiri, Storm of Stone (WAR) glass card style (Actually it still worked as of November 2020)
ZendikarLands obtain three full art Zendikar Rising Basic Lands 21.01.2021
PlayAllegiance obtain three Ravnica Allegiance Packs July 1, 2020 (unsure; does not seem to work anymore)
TryKaladesh obtain 1 Kaladesh Remastered Pack Unknown
ZendikarLands obtain three Zendikar Rising basic lands, unless you have all 15 already Unknown
PlayTheros obtain 3 Theros Beyond Death Packs Unknown
PlayIkoria obtain 3 Ikoria Lair of Behemoths Packs Unknown
PLAYM21 obtain 3 Core Set 2021 Packs Unknown
PlayZendikar obtain 3 Zendikar Rising Packs Unknown
LevelUp obtain 2000 XP Unknown
PlayEldraine obtain 3 Throne of Eldraine Packs July 1, 2020 (but it still seems to work in October 2020)
INNERDEMON obtain Ob Nixilis, the Hate-Twisted card style January 1, 2023
OneBillion unsure Sept 2019
GOLDENCOMMUNITY +10000 XP and two random cosmetic cards Unknown
PlayKaldheim obtain 3 Kaldheim Packs Unknown
PlayStrixhaven obtain 3 Strixhaven Packs Unknown
PLAYDND obtain 3 DnD Packs Unknown
PLAYMID obtain 3 Midnight Hunt Packs Unknown
PLAYM21 obtain 3 Core Set 2021 Packs Unknown
PlayDMU obtain 3 Dominaria packs Unknown
PlayDMUAlchemy obtain 3 Dominaria United Alchemy Packs 10-06-2022
PlayBRO obtain 3 Brothers' War Packs 11-15-2022
threads gain +1,000 XP 07-13-2023
mellon gain +1,000 XP 06-29-2023

Do note that, e. g. PLAYIKORIA works as well as does PlayIkoria, so capitalized characters are treated the very same as non-capitalized characters here.

More redeem-codes can be found here:

If you want to use a creature's companion ability, you will have to put that creature into your sideboard. That's it. If your deck meets the condition, you'll be prompted to choose it as your companion at the start of the game. See also:

Ranks and Tiers in Magic Arena (online)

This is incomplete; a stub. It tries to show the available tiers and ranks in Magic Arena.

Name of the Tier Tiers available
Silver Tier 4, 3, 2 and 1
Gold Tier 4, 3, 2 and 1
Platinum Tier 4, 3, 2 and 1
Diamond Tier 4, 3, 2 and 1

Land to mana ratio in Magic the Gathering

The ratio that is usually recommended is 24 lands in a 60 cards deck.

This means the ratio is 0.4 : 1.

Thus, if you have a 100 cards deck, you need to multiply this by 0.4 in order to obtain how many land cards you should play: which is 40.

Keep in mind that this is a rule of thumb; for better results, you need to analyse how mana-heavy the deck is. A deck that has lots of expensive cards may need more lands than this ratio. But by and large, the 0.4 ratio is a good average to work from and improve on it.

In November 2020 code was added to calculate this automatically, based on the 0.4 multiplier.

The method to use is the following one:


As argument pass in how many cards in TOTAL exist for a given deck, defaulting to 60. So without arguments, this method will return 24 - which means 24 lands.

If you input 80, you get this as a result:

RpgParadise::MagicTheGathering.n_lands_necessary(80) # => 32

So this means that this deck should have at the least 32 basic lands.

Evidently this is a minimum number. And you should only count pure land cards for this, not combined ones, e. g. cards that are a land AND a (flipped) sorcery. This is also the minimum.

I needed this functionality because I often had too few lands and grew tired of this situation. So now, if I design a deck with 80 cards in total, I know that I need at the least 32 lands in this deck.

Again: keep in mind that this is a general rule of thumb. Not every deck needs that. Very fast decks may be fine with fewer lands. It all depends on the other cards as well, but I wanted to keep it super-simple, as far as the code is concerned.

Note that after extensive testing, I believe that the 0.4 ratio only applies to fast decks. For average decks with a mana range of 2 to 4, I recommend 0.45 or 0.5 instead, so a few more lands than the 0.4 ratio suggests.

Rules in Magic the Gathering

I kept on forgetting part of the rules, so here is just a little reminder for myself, in no particular order.

Fighting*+: some cards allow a creature to directly fight another creature, without regular combat. The question then is whether abilities such as **deathtouch still apply there? The answer is yes. The explanation is that actual fighting will occur, so these abilities (including lifelink) will also apply.

I found this interesting because it meant that these abilities are really extremely useful in such a situation. Both deathtouch and especially lifelink can easily alter the course of a game.

class RpgParadise::MagicTheGathering::SearchForThisMagicCard

This small class (RpgParadise::MagicTheGathering::SearchForThisMagicCard) can be used to quickly search via magic-markt cards, from the commandline.

A tiny ruby-gtk3 application is also available; it can be invoked from the --gui flag to that class.

A GUI for the local card collection

In October 2021 I wrote a small ruby-gtk3 application that can be used to store information into a local Hash, containing your cards.

This looks like so:

Yes, not very pretty, but I wanted to just quickly prototype something. At a later time this may be improved, but for now my main goal is to add functionality (still ongoing at snail pace!).

At the end of October 2021 this was improved even more.

It currently looks like this:

Some internal improvements can be noticed externally as well, if you compare the two images.

In November 2021 some more improvements were made:

Most importantly images were added for the different card themes, so if you change the combo-box entries you will now see the corresponding image. (These images are missing in the above screenshot, but I assure you they are there - I may have to re-do the screenshot above eventually. Either way you can see the slow progression between the different GUIs shown above.)

Some days later, also in November 2021, a few more improvements were made. First - the card names are now slowly distributed as well, as .yml files. Second - if you have an image locally available about the card then it can be viewed in the same widget. You have to download the images on your own, though; the rpg_paradise project only makes available the skeleton GUI itself. Images have to be gathered by yourself. (On the plus side: this means we could adjust the functionality here for other card games as well.)

In January 2022 this now looks like so:

Do not get too distracted about the shown image being blurry: the downloaded .png file was blurry, so this depends on the upstream quality of the card as such. (I did not obtain it from the official magic-the-gatherer site, but instead from some other website that shrinked these images a lot. See the prior screenshot for an image with higher resolution and less blur.)

While the GUI may not look perfect, and some things could be improved, I am mostly focusing on the functionality as-such.

This subsection just contains a few helpful links:

If you want to quickly see all registered cards, use an URL such as:

I used this to obtain all cards that can be "completed" when the user types in a gtk-entry widget, to simplify typing stuff.

An alternative URL similar to the one above is:


The LPC language was quite popular in the 1990s for building oldschool text MUDs, an old C-like programming language.

A description of LPC can be found here:

Back when I was investing time into helping expand on LP-MUDs, I found that ruby code can be of significant help, and shorten lots of auto-generation-related tasks.

Whether this is useful to anyone else or not, I have no idea, but I felt it was useful to publish it anyway.

Keep in mind that the documentation here is massively underwhelming; I never finished it, largely because I stopped using LPC decades ago literally.

First, include the namespace:

include RpgParadise::Lpc

If you want to find out the opposite exit, try:


The perfect RPG

A very long time ago, when I was younger and had more time available, I wondered how the 'perfect' RPG would look like.

Being 'perfect' is highly subjective, so it makes no sense to define that; but what about simply a RPG setting that is really compelling and interesting?

Well, that's easier to define, so let's give it a try and present a few ideas.

  • I believe a good RPG should ideally be modular in its conception, and ideally simple. It is ok to add complexity, but the base system should be mostly simple. Building on top of that it useful, but it should not lead to too much complexity where everyone has to constantly read through the rules in order to determine what works and what does not work. The goal should be to have both a realistic RPG simulation, as well as a great storyline going. It should be fun, logically.

Which traits should be used to describe a human being?

Probably: ability to capture new information and analysing them, so, some form of intelligence / cunning trait.

Agility or Movability: able to walk and move efficiently.

Charisma or Personality: being able to get along with other people and influence. This should be decoupled from good looks; even an ugly person may be charming or influential, though prettier people may have an easier time influencing others.

Gamebooks (oldschool paper gamebooks)

Anyone remembering the good old pen-and-paper gamebooks? Where you could decide which path to take, while reading the story? So you could create the story on your own, by making different decisions.

I wrote a simple ruby-gtk3 widget for this functionality.

It won't be super-sophisticated - mostly a proof-of-concept, but one that should work. In this context, "work" means to be able to play a gamebook-adventure, from start to finish.

Have a look at class RpgParadise::GUI::Gtk::Gamebook for more information pertaining to this. Some hooks exist, such as:

Würfle mit zwei Würfeln.

This means to roll two die, with six sides - also denoted as 2W6 (or, in english, it is typically written as 2D6, where D stands for die/dice).

The result of this will automatically calculated whenever it appears as part of the message body. The range here, for two six-sided dies, will be from 2 up to (and including) 12, logically.

Note that this refers mostly to providing the GUI; you may have to load a proper dataset (for the actual story) from another source, other than the sample file provided to demonstrate this.

The current format is a yaml file which has some strict rules and does some simplicistic assumptions. In theory, though, you could also use another format, but support for this may have to be added to the rpg_paradise gem; if you do have another file format, and thus a use case for this, feel free to write an email and see/suggest how it could be used.

The two main, necessary parts for the gamebook format, in YAML, are:

  • An entry for the page content at hand, such as section 5.
  • Information about the exits of this page content to other sections. That way we can build up a path such as 1→33→315→114 and so forth.

These are the two main parts. A few more entries might be necessary, but I think we can parse the page content for any "game state" relevant parts as-is. See the above example for that.

The ruby-gtk3 GUI currently (in July 2021) looks like this:

I did not really polish this a lot; many small things are missing and the GUI would have to be improved, but this is mostly a proof-of-concept example really. The content of the gamebook will be shown on the right hand side; the buttons on the bottom control "jumping" to the next subpage; and the controls on the left side will be about the character and other attributes/values that may be subject to change. The icon with the dice can be used to simulate rolling of die; 3W6 would mean "take 3x six-sided die and show their combined, total result". If you would need a twenty-sided die you would e. g. use 3W20 instead.

In the long run I plan to make a variant available that works on the www as well as other GUI-toolkits. But, as always, time is a finite resource so who knows when this may be added. (Support for the www is more important than for other GUI-toolkits, though.)

In October 2021 I improved this a little bit:

I used the Hack font for that screenshot, which is probably worse than the prior font. But this is trivial to change. The more important changes were a few small-ish user-interface improvements. Clicking on the die will also show a slight coloured border, to indicate to the user where to look at next. Support for gamebook covers was added as well - we can now show an initial logo before we begin to show the different paths. Again: this is more of a proof-of-concept than something that is really fancy. I'd love to use a slight parchment-background and more animations, but it's a hobby project just for testing stuff, so - who knows.

In October 2022 this has been improved even more:

Also keep in mind that this has to remain flexible - different gamebooks use different rules, so the ruby-gtk3 bindings will have to be flexible as well, which will add some complexity into the code base.

The "default" rules are quite simple: for example if your character has an agility of 10, then you roll two six-sided dies. If your value is below that number, you scored a hit on the enemy, and deduct from his stamina/health. Then the enemy can retaliate. Repeat until someone is dead. This is the usual set of rules for most gamebooks, but some of the more advanced gamebooks also made use of more complicated rules.

Next a few design decisions will be briefly explained.

The entry max_id? determines the maximum number of ids. This was necessary because a typical gamebook may have about 400 individual entries, but writing these down takes time; I needed to test the functionality of the gamebook as I went on, though, so it was important to be able to add some error-control state. Thus, if the user inputs a number that is too high, the method max_id? can be used to ensure that the number is valid. So, if your gamebook has 400 entries, but the user inputs 500 then this can never possibly work. Thus, the user is informed about this.

Sinatra bindings exist as well, but only for one gamebook right now, which isn't even complete. Again - this is NOT polished at all. It's just a proof-of-concept at this point. And the first PoC also worked, so I am happy in this regard. \o/

To determine which gamebooks are available, the following method can be used:


Note that some of these gamebooks may be fairly large, so they could be distributed outside of this gem eventually; the two original sample files are already almost half the size of this gem, which is a LOT.

The gamebooks may also contain a file called config.yml. This file will contain additional information, such as the type of the game book (science-fiction, fantasy), the main image in use and so forth. This is per-project specific, naturally.

The available themes are currently these:

:"science-fiction" # The symbol :"science-fiction".

The GUI will appear slightly differently based on what kind of theme is in use. For instance, the starship image will only be shown if the :"science-fiction" symbol has been set. For fantasy-based gamebooks displaying a starship makes no sense. So this is a conditional setting, leading to automatic changes in the GUI.

In October 2022 the above was extended a bit. For instance, you can now use this entry point as well:


This basically means a "fantasy theme", but it will specifically enable some additional widgets that are adjusted to the game world in DSA ("Das Schwarze Auge"). In the long run I plan to add more customizations here.

To set your own custom image, try to denote it via something like this:

!ruby/symbol main_image:

Right now a remote URL is assumed, but in theory it is easy to change the code to allow for working with local image files as well.

You can also select for different gamebooks via the commandline, such as via:

ruby gamebook.rb --use-gamebook=2
ruby gamebook.rb --use-gamebook=1

Note that this will use Dir[] sorted alphabetically.

You can also use individual images, via the following entry in the yaml file:

image: 13_stern_der_schmuggler/001_cockpit.jpg

This currently is not ideal, but that functionality was added to enhance the looks of the gamebook a little bit.

By default the entry called "Geld:" (which is german for Gold) can be renamed via the config.yml file for a specific gamebook. Some german-based gamebooks use another word, such as "Dukaten" rather than "gold coin".

Since I wanted to support this via gamebook as well, I added functionality to the config.yml file to allow the user to change this.

The entry in the config.yml file has to be like this:

!ruby/symbol rename_gold_entry_to: Dukaten

So the second part, aka the Dukaten entry, will be used as the replacement-entry here. (The trailing ':' is mandatory right now and will be automatically added, but perhaps in the future this may be changed to allow a user full control over the gold/geld/money entry there.)

You can also use another font, if you would like to.


!ruby/symbol font_to_use: hack_20

class RpgParadise::GenerateHtmlGamebook can be used to autogenerate a standalone .html file. This currently works for the basic parts - it has to be modified in the future, such as by autogenerating individual .html files, as well as embedding images, and making it look nicer by default. For now, simply pass a number to this class, standing for the gamebook that is to be autogenerated.

You can also invoke this functionality via bin/rpg_paradise, such as via:

rpg_paradise gengamebook3

Alternatively, if you prefer a more verbose variant, you can also use this variant instead, if you wish to generate a single static HTML file:

rpgparadise --create-static-html-file-for=3

You can designate a default "n gold coins" entry via:

!ruby/symbol gold_entry_value_is: 2

This was necessary because in DSA "Dukaten" are worth a lot, and most characters will have only very few "Dukaten".

In November 2022 I modified the above approach a little. While the rpg_paradise gem will not contain gamebook dataset, there are a few playable samples, as extended proof of concept.

For instance, a german-speaking variant is available at:


It has been taken from:

However had, a few modifications were made, some typos fixed and so forth, so it is not 100% the original anymore.

If you want to start it, navigate to the directory at:


And then do:

ruby gamebook.rb --use-gamebook=3

In February 2023, the ruby-gtk3 wrapper over gamebooks was again improved a bit, in particular for the lone wolf series. This is not quite perfect yet, but if you look at the above screenshots then you can see several small, incremental improvements nonetheless:

Note that for the actual content of the lone wolf series, the rpg-paradise project will modify it a little bit here and there. For instance, in the original pen-and-paper series you had entries such as:

Bandit Warrior: COMBAT SKILL 17   ENDURANCE 27

For the internal storage into .yml files, I found this instead to be easier to read.

Bandit Warrior:

  ENDURANCE:    27

It is not a huge change, but nonetheless I wanted to document it here, in case anyone wonders (or in case I would forget).

Since as of February 2023 it is now possible to save the dataset, via ctrl+s, as well as load from a file, if the path to the file at hand is passed to the ruby-gtk3 wrapper. That way users can now play completely within the gamebook, without having to make any external notes any longer.

Since as of March 2023, the size of the rpg_paradise gem has increased quite a lot. This is not ideal - I don't want to make the gem too big. This means that all gamebooks that may at one point have been part of this project, may be hosted externally, eventually. Don't worry, though - when that happens (or, if that happens), code will be added to the rpg_paradise project to allow the user to download that dataset nonetheless. Just no longer from - rubygems would then purely distributed the ruby code, as-is, instead.

Generating gamebooks as HTML pages

You can autogenerate different gamebooks via class RpgParadise::GenerateHtmlGamebook.

I aliased this to gengamebook on my local system. Then I can do the following:

gengamebook 3

This would, by default, generate a new HTML page at


And open it in the browser (on my home system that is).

That HTML page contains all content and intralinks to different subpages.

Since as of March 2023, two new entries were added, called add_item and add_item_unless_it_exists_already. The idea for these entries is that, if the content of the subpage says something along the lines of adding an item to your equipment, we want to be able to do so automatically, so that the user does not have to do so manually anymore.

GUI - Graphical User Interface elements

Most of the GUIs will be either made available in ruby-gtk3 and/or via a web-interface.

Not much exists so far in this regard, but class die.rb in gui/gtk3/ of this project could be used as a die. Click on that button to simulate the roll of a die.

In August 2021 the above was extended a bit. In particular for windows I will make use of the ruby-libui bindings, in order to make GUI elements work on windows as well. For this we will probably use a DSL layer, to simplify and unify the GUI-related aspects.

Support for Dice

Pen and Paper roleplaying groups need to roll die/dice.

The rpg_paradise gem supports this, primarily via class Dice.

Usage examples will be shown next.

Say that you need to create a die showcasing results from 5 up to (including) 15. This is equivalent to 2d6+3.

You can use the following API for this:

dice = RpgParadise::Dice.create '2d6+3'
puts dice.roll # => e. g. 10 as a result

You can do include RpgParadise to avoid having to prefix the namespace RpgParadise::.

Negative values are also possible of course, such as 2d4-1:

dice = RpgParadise::Dice.create '2d4-1'
puts dice.roll # => e. g. 3 as a result

To get the min values for a die roll you can use:

dice = RpgParadise::Dice.create '2d4+5'
puts dice.min # => 7

.max() is currently not implemented, neither is .min_max(). .min_max would yield the lower and upper boundaries, such as # => [3,18].

At a later time explain_result will be added, e. g.


# Returns a string that attempts to show how
# the result from the last call to roll was
# composed from individual results. This
# will be nil if no roll has been made yet.

dice.explain_result    # => nil
dice.roll              # => 12
dice.explain_result    # => "3d6: 4 + 2 + 6 = 12"

If you just want a random number generated from a dice roll, use this toplevel API:

RpgParadise.roll('d100')  # => 2
RpgParadise.roll('2d4-1') # => 7

Todo List:

The list here is a todo-list for me, so that I can improve the project over the coming months and years.

  • Finish translating the german content into the english language.
  • Finish porting the old LPC code into ruby code.
  • Port as many GUI elements as possible, including the www, for as many different aspects of the rpg_paradise gem as is feasible.

Feature Suggestions for the RPG paradise project

As is explained elsewhere in this document, the project here came mostly from a legacy-use case, e. g. code and storylines created by our old pen-and-paper roleplaying group. The project is still maintained, but I do not invest that much time into it anymore, simply because it is not as important as other projects.

Having said that, though, I am perfectly willing to add more code in ruby (that is also documented here), if there are use cases by people who use the project. So in this event, if you would like to see certain functionality, consider dropping an email to me. I recommend in the title of the email to add something like this:


As identifier. That way I can quickly see what the email is about and can read it more easily; otherwise I may tend to miss it since there are too many emails otherwise.

In that email you can suggest specific functionality or changes, including to the documentation. If the request is small or small-ish, or even of medium size, it is very highly likely that I will modify the project based on these use cases / needs. I can not promise to implement larger suggestions but I will take them into consideration and see what could be changed in this regard, so feel free to make recommendations via oldschool email. \o/

Baldur's Gate 2 Enhanced Edition

As I quite liked the EE (Enhanced Edition) of Baldur's Gate 2, and there is a lot of mod-related content out there, I decided to write some helper-code in ruby for this game.

The code for this can be found here:


There is also a webpage distributed within this gem, under:


You may have to extract the .tar.xz file, to get access to the .md file that contains a LOT of information, as well as the .cgi (and .sinatra) application that runs the content of this .md file - and more. (This requires installation of the gem called cyberweb.)

You can find some code in the first .rb file, such as this toplevel method:


To download all mods I find useful. Simply adjust the Array that is passed to this method if you want to use different mods. The installation order is important, by the way - some mods need to have other mods installed before you can install them.

I will also try to create a mod, called Fizzleban the mighty (coward), as well as an item-mod (One Item To Rule Them All).

Fizzleban will be an old gnome who is perpetually drunk. He will be a recruitable NPC eventually, but will have tons of small quests to make him less drunk. Eventually he may become sober in the course of his quest, and then become a fairly powerful mage with a few unique abilities.

Next, I will document some checks in the code used by the Baldur's gate engine.

Check for a specific item:

PartyHasItem("l#rocfi1") # Name goes in the () part.

# a more complete example:
IF ~OR(2) PartyHasItem("l#rocfi1") PartyHasItem("l#rocfi2") Global("L#ROCFighterForge","GLOBAL",0)~ THEN GOTO L#ROCFI.00

Check for the party having 10.000 gp:


Deduct gold from the party via TakePartyGold():


Set a global variable:

DO ~SetGlobal("TalkedToSarles","GLOBAL",1)~ GOTO 2

Add a Ring of Protection to Sir Alynar:

COPY_EXISTING ~alynar.cre~ ~override~

This will add the item to his right ring slot. If that slot is full, it will be added to the left ring slot. If both are full, we move the current right ring (the first slot in the list) to an an empty inventory slot so that the Ring of Protection is still put in it’s appropriate slot.

Add a longsword to the second weapon slot:

COPY_EXISTING ~alynar.cre~ ~override~
  ADD_CRE_ITEM ~sw1h06~ #0 #0 #0 ~IDENTIFIED~ ~WEAPON2~

This will add a +2 Longsword to his second weapon slot, but it will not be equipped automatically.

Add a two-handed weapon and equip it:

COPY_EXISTING ~alynar.cre~ ~override~

This will place the item in the second weapon slot. If there is already a weapon there it is moved to inventory, remove anything that’s in the shield slot and put it in an empty inventory slot and finally it will equip the weapon.

Do a conditional reply:

~ THEN REPLY@2 DO ~SetGlobal("L#ROCPaladinForge","GLOBAL",1) SetGlobal("L#ROCForging","GLOBAL",1) TakePartyGold(10000) DestroyGold(10000)~ GOTO 56

Do a conditional check for a variable:

   RESPONSE #100

Copy a whole folder:

COPY ~%MOD_FOLDER%/Cre/X3Vie25.cre~ ~override/X3Vie25.cre~

Note that COPY is an instruction to WEIDU that will take an existing file and proceed to copy it to another location.

COPY takes two arguments - you write ‘COPY this, to that’, and it copies this, to that.

More examples for this:

COPY "%MOD_FOLDER%/SPWI304.spl" override
COPY "%MOD_FOLDER%/SPWI308.spl" override

%MOD_FOLDER% is a variable.

Take note that you do not need to repeat COPY each time.

The following works just as well:


  "%MOD_FOLDER%/SPWI304.spl" override
  "%MOD_FOLDER%/SPWI308.spl" override

Check for a specific source size value:


See the main actor:


Check whether the NPC is in the party:


Disallow monks from using a specific item:


Specify the correct language file to load and use:

LANGUAGE English english ~mymod/tra/english/somefile.tra~

Start a dialogue:

StartDialogue("CERNDJ", Player1)

Check how often one has talked to a NPC yet:

IF ~NumTimesTalkedTo(0)

Speak and Say something:

SAY #28655 # It needs the proper ID here. 

To make an item in a mod available for sale, use this action:

ADD_STORE_ITEM "potn33" AFTER "potn42" #1 #0 #0 ~IDENTIFIED~ #2
ADD_STORE_ITEM "potn38" AFTER "potn35" #1 #0 #0 ~IDENTIFIED~ #3

Minsc files:


Override Minsc's name:

"minsc.cre" override
"minsc2.cre" override
"minsc4.cre" override
"minsc6.cre" override
  SAY NAME1 "Minsc the Awesome"
  SAY NAME2 "Minsc the Awesome"

Kill a NPC, via Dead():


Erase a journal entry:


Format for specifying a monster:

sahkng01 King Ixilthetocal Overridden 20    22   0   9   9   13  9  18  90 5    1    5 4000 Sahuagin none Sahuagin Large Skull  none  none  none  none  none   none  8,10,9,9,11 n>

Add XP to the party:


Check which game is in use:

OUTER_SET GAME_IS_BG2EE = (GAME_IS ~bg2ee~) ? 1 : 0
OUTER_SET GAME_IS_IWDEE = (GAME_IS ~iwdee~) ? 1 : 0
OUTER_SET GAME_IS_EET = (GAME_IS ~eet~) ? 1 : 0

Allow the PC to reply to a comment by a NPC:


Conditional triggers - also known as script triggers - follow the following format:

IF script trigger ... THEN script response ... END

# in other words:
If the script trigger(s) are true, the script responses will be evaluated.

Strings are delineated by tildes (aka ~) or %% or "". You can choose either one; WeiDU, however had, uses the tilde by default. All dialog, conditions and actions have to be surrounded by this, so ideally it is best to use ~ as well.

Text is stored in a huge table called a dialog file.

BAF files (which are text files) are compiled into BCS files. BCS is a bytecode format.

Game files (.itm, .cre...) are restricted to 8 characters. WeiDU can not interpret "MagicSword.itm" as a valid file - it is too long.

TP2 files (.tp2) are the installation scripts, for installing a particular mod.

The copy of WeiDU should be named setup-mod_name.exe, where mod_name is the name of the mod folder. For instance, if the name is the_roebe_mod, then the .exe name would be setup-the_roebe_mod.exe.

The directory for the mod ("mod folder") should have a name that is likely to be unique, so prefer to use longer names than generic short names.

A TLK file, should it exist, is the single resource containing all of the game's text.

From a .DLG file a .D file is generated, such as via weidu. Example:

weidu FOOBAR.DLG # This would generate FOOBAR.D

A condition in Weidu may go like this:

IF ~I'm being attacked by big scary wolves~ THEN BEGIN WOLFATTACK0
SAY ~Holy shit, look at those wolves, I'm out of here!~
IF ~~ THEN DO ~Running away from big scary wolves~ EXIT

In this example, the condition would match against I'm being attacked by big scary wolves. This is where the IF clause checks against. Note that THEN BEGIN also belongs to the IF clause.

Now that we know how to do a conditional check, let's go to an ACTION.

An ACTION goes like this:

DO ~action goes here~ EXIT

You can also combine ACTIONS via multiple if-conditions, such as in:

IF ~~ THEN REPLY ~You chicken shit!~ DO ~kill big scary wolves~ EXIT
IF ~~ THEN REPLY ~Uh...good idea, let's get out of here!~ DO ~Running away from big scary wolves~ EXIT

First action would be to fight the big wolf; second action would be to run away.

The EXIT command here is used for when the dialog is over.

You can specify GOTO too, such as via:

IF ~~ THEN REPLY ~Uh...good idea, let's get out of here!~ DO ~Running away from big scary wolves~ GOTO WOLFATTACK1

You need a matching identifier for this, of course, such as:

SAY ~Heh, you wimp! I'm was just kidding! Harhh!~
IF ~~ THEN REPLY ~You ass! You gonna get it geek boy!~ DO ~smacking around the geek boy~ EXIT

Turning a .DLG file into a .D file:


Compiling Dialogs via weidu:

weidu pathtofile/filename.d --tlkout dialog.tlk --out override

Decompiling Dialogs via weidu:

weidu aerie.dlg --out override

The variable sizes used by Weidu are:

ASCII: size 8
LONG:  size 4
SHORT: size 2
BYTE:  size 1

The equivalent to SAY NAME1 ~xyz~ would be SAY 0x08 ~xyz~.

To patch existing save games, or to not patch them, these clauses can be used:

BEGIN @768001 /* Yes, but don't patch existing save games */
BEGIN @768002 /* Yes and patch existing save games */

Available items in Baldur's Gate 2:

RING21 # Ring of Infravision
RING22 # Ring of Holiness
RING23 # Ring of Folly
RING25 # Koveras' Ring of Protection
RING92 # Ring
RING93 # Ring
RING94 # Ring
RING95 # Ring
RING97 # Ring
RING98 # Ring
RING99 # Ring
RINGDEMN # No such index
RINGJOIA # Joia's Flamedance ring
RINGJOS # Joseph's Greenstone Ring
RINGLOUP # No such index
RINGWOLF # No such index
RNDTRE01 # Random Poor Treasure
RNDTRE02 # Random Average Treasure
RNDTRE03 # Random Well-Off Treasure
RNDTRE04 # Random Wealthy Treasure
RNDTRE05 # Random Rich Treasure
S1-10 # Skull
S1-12 # Skull
S1-2 # Skull
S1-20 # Skull
S1-3 # Skull
S1-4 # Skull
S1-6 # Skull
S1-8 # Skull Attack
S2-16 # Skull
SAREVO # No such index
SBLADE # Selune's Blade
SCHLUM1 # No such index
SCRL02 # Spell Scroll
SCRL03 # Protection from Acid
SCRL04 # Protection from Cold
SCRL05 # Protection from Electricity
SCRL06 # Protection from Fire
SCRL07 # Protection from Magic
SCRL08 # Protection from Poison
SCRL09 # Protection from Undead
SCRL10 # Cursed Scroll of Weakness
SCRL11 # Cursed Scroll of Clumsiness
SCRL12 # Cursed Scroll of Foolishness
SCRL13 # Cursed Scroll of Ugliness
SCRL14 # Cursed Scroll of Summon Monster
SCRL15 # Protection from Petrification
SCRL16 # Cursed Scroll of Petrification
SCRL17 # Cursed Scroll of Ailment
SCRL18 # Cursed Scroll of Stupidity
SCRL1B # Agannazar's Scorcher
SCRL1C # Ghoul Touch
SCRL1D # Clairvoyance
SCRL1E # Dispel Magic
SCRL1F # Flame Arrow
SCRL1G # Fireball
SCRL1H # Haste
SCRL1I # Hold Person
SCRL1K # Lightning Bolt
SCRL1L # Monster Summoning I
SCRL1M # Non-Detection
SCRL1N # Protection From Normal Missiles
SCRL1O # Slow
SCRL1P # Skull Trap
SCRL1Q # Vampiric Touch
SCRL1R # Wraith Form
SCRL1S # Dire Charm
SCRL1T # Ghost Armor
SCRL1U # Confusion
SCRL1V # Dimension Door
SCRL1Y # Improved Invisibility
SCRL1Z # Minor Globe of Invulnerability
SCRL2A # Monster Summoning II
SCRL2D # Animate Dead
SCRL2E # Cloudkill
SCRL2F # Cone of Cold
SCRL2G # Monster Summoning III
SCRL2H # Shadow Door
SCRL2I # Letter
SCRL2J # Letter
SCRL2K # Letter
SCRL2L # Letter
SCRL2M # Letter
SCRL2N # Letter
SCRL2O # Letter
SCRL2P # Letter
SCRL2Q # Letter
SCRL2R # Letter
SCRL2S # Letter
SCRL2T # Letter
SCRL2U # Letter
SCRL2V # Letter
SCRL2W # Letter
SCRL2X # Letter
SCRL2Y # Letter
SCRL2Z # Letter
SCRL3A # Letter
SCRL3B # Letter
SCRL3C # Letter
SCRL3D # Letter
SCRL3E # Letter
SCRL3F # The diary of Sarevok
SCRL3G # Vocalize
SCRL3H # Protection From Evil
SCRL3I # Scroll
SCRL3Z # Gorion's Scroll
SCRL56 # Cure Serious Wounds
SCRL58 # Free Action
SCRL59 # Neutralize Poison
SCRL5A # Mental Domination
SCRL5B # Defensive Harmony
SCRL5C # Protection from Lightning
SCRL5D # Protection from Evil 10' radius
SCRL5E # Champion's Strength
SCRL5F # Chaotic Commands
SCRL5G # Remove Curse
SCRL5H # Emotion
SCRL5I # Greater Malison
SCRL5J # Otiluke's Resilient Sphere
SCRL5K # Spirit Armor
SCRL5L # Polymorph Other
SCRL5M # Polymorph Self
SCRL5N # Domination
SCRL5O # Hold Monster
SCRL5P # Chaos
SCRL5Q # Feeblemind
SCRL5R # Andris Journal
SCRL5S # Dezkiel's Scroll
SCRL5W # Greater Malison
SCRL5X # Greater Malison
SCRL5Y # Greater Malison
SCRL5Z # Fireball
SCRL61 # Cure Critical Wounds
SCRL62 # Flame Strike
SCRL63 # Raise Dead
SCRL66 # Grease
SCRL67 # Armor
SCRL68 # Burning Hands
SCRL69 # Charm Person
SCRL70 # Color Spray
SCRL71 # Blindness
SCRL72 # Friends
SCRL73 # Protection From Petrification
SCRL75 # Identify
SCRL76 # Infravision
SCRL77 # Magic Missile
SCRL78 # Protection From Evil
SCRL79 # Shield
SCRL80 # Shocking Grasp
SCRL81 # Sleep
SCRL82 # Chill Touch
SCRL83 # Chromatic Orb
SCRL84 # Larloch's Minor Drain
SCRL85 # Blur
SCRL86 # Detect Evil
SCRL87 # Detect Invisibilty
SCRL89 # Horror
SCRL90 # Invisibilty
SCRL91 # Knock
SCRL92 # Know Alignment
SCRL93 # Luck
SCRL94 # Resist Fear
SCRL95 # Melf's Acid Arrow
SCRL96 # Mirror Image
SCRL97 # Stinking Cloud
SCRL98 # Strength
SCRL99 # Web
SCRLAUTO # Autograph
SCRLDRA # Scroll
SCRLJALA # Geas Removal Scroll
SCRLKAR # Scroll
SCRLNEI # Scroll
SCRLPET # Stone to Flesh Scroll
SCRLTAR # Scroll
SCRLZHA # Scroll
SCRLZY # Summon Cow
SCRLZZ # Summon Cow
SELTEST # No such index
SGRASP # Shocking Grasp
SHAMMR # Spiritual Hammer
SHILLE # Shillelagh
SHLD01 # Small Shield
SHLD02 # Small Shield +1
SHLD03 # Medium Shield
SHLD04 # Medium Shield +1
SHLD05 # Large Shield
SHLD06 # Large Shield +1
SHLD07 # Large Shield +1, +4 vs. Missiles
SHLD08 # Buckler
SHLD09 # Buckler
SHLD10 # Buckler
SHLD11 # Small Shield
SHLD12 # Small Shield
SHLD13 # Medium Shield
SHLD14 # Medium Shield
SHLD15 # Large Shield
SHLD16 # Large Shield
SHLD18 # Large Shield
SHLD19 # Large Shield +2
SHLD20 # Kiel's Buckler
SHLD99 # Small Shield
SIRINE # No such index
SIRINE1 # No such index
SLEEP # No such index
SLIMED1 # Broken Shield
SLIMED2 # Broken Shield
SLNG01 # Sling (useless space at end)
SLNG02 # Sling +1
SLNG03 # Sling +3 (useless new line at end)
SPEC01 # No such index
SPEC02 # No such index
SPER01 # Spear
SPER02 # Spear +1
SPER03 # Spear +3, Backbiter
SPER04 # Spear
SPIDGI1 # Giant Spider Attack
SPIDHU1 # Huge Spider Attack
SPIDPH1 # Phase Spider Attack
SPIDSW1 # Sword Spider Attack
SPIDWR # No such index
SPIDWR1 # Wraith Spider Attack
SQUIRP # No such index
STAF01 # Quarter Staff
STAF02 # Quarter Staff +1
STAF03 # Quarter Staff
STAF04 # Quarter Staff
STAF05 # Staff of Striking
STAF06 # Staff Mace
STAF07 # Staff Spear +2
STAF08 # Quarter Staff +3 (useless new line at end)
STALKE1 # No such index
SW1H01 # Bastard Sword (useless space at end)
SW1H02 # Bastard Sword +1
SW1H03 # Bastard Sword +1, +3 vs. Shapeshifters (useless space at end)
SW1H04 # Long Sword (useless space at end)
SW1H05 # Long Sword +1, in the file SW1H05.itm
SW1H06 # Long Sword +2
SW1H07 # Short Sword (useless space at end)
SW1H08 # Short Sword +1
SW1H09 # Short Sword +2
SW1H10 # Short Sword of Backstabbing (useless space at end)
SW1H11 # No such index
SW1H12 # Hull's Long sword
SW1H13 # Moonblade
SW1H14 # Short Sword +1
SW1H15 # Scimitar +3, Frostbrand
SW1H16 # Scimitar +5, Defender
SW1H17 # Perdue's Short Sword
SW1H18 # Sword of Balduran
SW1H19 # The Vampire's Revenge
SW1H20 # Scimitar
SW1H21 # Short Sword (useless space at end)
SW1H22 # Scimitar +1
SW1H23 # Scimitar +2
SW1H24 # Long Sword +1, Flame Tongue
SW1H98 # Short Sword +1
SW1H99 # Sword of Chaos
SW2H01 # Two Handed Sword
SW2H01B # Two Handed Sword
SW2H02 # Two Handed Sword +1
SW2H03 # Two Handed Sword, Cursed Berserking +3
SW2H05 # Two Handed Sword
SW2H06 # Spider's Bane
SW2H07 # Two Handed Sword +3
SW2H08 # Two Handed Sword +2
SW2H99 # Two Handed Sword, Cursed Berserking +3
SWORDI # No such index
SWSPID # No such index
VAMPIRE # No such index
WAND02 # Wand of Fear
WAND03 # Wand of Magic Missiles
WAND04 # Wand of Paralyzation (useless space at end)
WAND05 # Wand of Fire
WAND06 # Wand of Frost
WAND07 # Wand of Lightning
WAND08 # Wand of Sleep
WAND09 # Wand of Polymorphing
WAND10 # Wand of Monster Summoning
WAND11 # Wand of the Heavens
WAND12 # Wand of Magic Missiles
WAND99 # Wand of Magic Missiles
WOLFM # Attack
WOLFVA1 # No such index
WOLFWI1 # No such index
WOLFWI2 # No such index
WYVERN # No such index
WYVERN1 # Wyvern Attack
WYVERN2 # Wyvern Attack
XBOW01 # Heavy Crossbow
XBOW02 # Heavy Crossbow +1
XBOW03 # Heavy Crossbow of Accuracy (useless space at end)
XBOW04 # Light Crossbow
XBOW05 # Light Crossbow +1
XBOW06 # Light Crossbow of Speed (useless space at end)
XVARTIL # Short Sword (useless space at end)

Available spells in ToB:

SPWI101 Grease
SPWI102 Armor
SPWI103 Burning Hands
SPWI104 Charm Person
SPWI105 Colour Spray
SPWI106 Blindness
SPWI107 Friends
SPWI108 Protection from Petrification
SPWI110 Identify
SPWI111 Infravision
SPWI112 Magic Missile
SPWI113 Protection from Evil
SPWI114 Shield
SPWI115 Shocking Grasp
SPWI116 Sleep
SPWI117 Chill Touch
SPWI118 Chromatic Orb
SPWI119 Larloch's Minor Drain
SPWI120 Reflected Image
SPWI123 Find Familiar
SPWI124 Nahal's Reckless Dweomer
SPWI125 Spook
SPWI201 Blur
SPWI202 Detect Evil
SPWI203 Detect Invisibility
SPWI205 Horror
SPWI206 Invisibility
SPWI207 Knock
SPWI208 Know Alignment
SPWI209 Luck
SPWI210 Resist Fear
SPWI211 Melf's Acid Arrow
SPWI212 Mirror Image
SPWI213 Stinking Cloud
SPWI214 Strength
SPWI215 Web
SPWI217 Agannazar's Scorcher
SPWI218 Ghoul Touch
SPWI219 Vocalise
SPWI220 Power Word Sleep
SPWI221 Ray of Enfeeblement
SPWI222 Chaos Shield*
SPWI223 Deafness
SPWI224 Gliterdust
SPWI301 Clairvoyance
SPWI302 Remove Magic
SPWI303 Flame Arrow
SPWI304 Fireball
SPWI305 Haste
SPWI306 Hold Person
SPWI307 Invisibility 10' Radius
SPWI308 Lightning Bolt
SPWI309 Monster Summoning I
SPWI310 Non-Detection
SPWI311 Protection from Normal Missiles
SPWI312 Slow
SPWI313 Skull Trap
SPWI314 Vampiric Touch
SPWI316 Dire Charm
SPWI317 Ghost Armor
SPWI318 Minor Spell Deflection
SPWI319 Protection from Fire
SPWI320 Protection from Cold
SPWI321 Spell Thrust
SPWI322 Detect Illusion
SPWI324 Hold Undead
SPWI325 Melf's Minute Meteors
SPWI326 Dispell Magic
SPWI401 Confusion
SPWI402 Dimension Door
SPWI403 Fire Shield (Blue)
SPWI404 Ice Storm
SPWI405 Improved Invisibility
SPWI406 Minor Globe of Invulneribility
SPWI407 Monster Summoning II
SPWI408 Stoneskin
SPWI409 Contagion
SPWI410 Remove Curse
SPWI411 Emotion
SPWI412 Greater Malison
SPWI413 Otilukes Resilient Sphere
SPWI414 Spirit Armor
SPWI415 Polymorph Other
SPWI416 Polymorph Self
SPWI417 Enchanted Weapon
SPWI418 Fire Sheild (Red)
SPWI419 Secret Word
SPWI420 Minor Sequencer
SPWI421 Teleport Field
SPWI423 Spider Spawn
SPWI424 Farsight
SPWI425 Wizard Eye
SPWI501 Animate Dead
SPWI502 Cloudkill
SPWI503 Cone of Cold
SPWI504 Monster Summoning III
SPWI505 Shadow Door
SPWI506 Domination
SPWI507 Hold Monster
SPWI508 Chaos
SPWI509 Feeblemind
SPWI510 Spell Immunity
SPWI511 Protection from Normal Weapons
SPWI512 Protection from Electricity
SPWI513 Breach
SPWI514 Lower Resistance
SPWI515 Oracle
SPWI516 Conjure Lesser Fire Elemental
SPWI517 Protection from Acid
SPWI518 Phantom Blade
SPWI519 Spell Shield
SPWI520 Conjure Lesser Air Elemental
SPWI521 Conjure Lesser Earth Elemental
SPWI522 Minor Spell Turning
SPWI523 Sunfire
SPWI601 Invisible Stalker
SPWI602 Globe of Invulneribility
SPWI603 Tenser's Transformation
SPWI604 Flesh to Stone
SPWI605 Death Spell
SPWI606 Protection from Magic Energy
SPWI607 Mislead
SPWI608 Pierce Magic
SPWI609 True Sight
SPWI611 Protection from Magic Weapons
SPWI612 Power Word Silence
SPWI613 Improved Haste
SPWI614 Death Fog
SPWI615 Chain Lightning
SPWI616 Disintegrate
SPWI617 Contingency
SPWI618 Spell Deflection
SPWI619 Wyvern Call
SPWI620 Conjure Fire Elemental
SPWI621 Conjure Air Elemental
SPWI622 Conjure Earth Elemental
SPWI623 Carrion Summons
SPWI624 Summon Nishru
SPWI625 Stone to Flesh
SPWI701 Spell Turning
SPWI702 Protection from the Elements
SPWI703 Project Image
SPWI704 Ruby Ray of Reversal
SPWI705 Khelben's Warding Whip
SPWI707 Cacofiend
SPWI708 Manlte
SPWI710 Spell Sequencer
SPWI711 Sphere of Chaos
SPWI712 Delayed Blast Fireball
SPWI713 Finger of Death
SPWI714 Prismatic Spray
SPWI715 Powerword Stun
SPWI716 Mordenkainen's Sword
SPWI717 Summon Efreeti
SPWI718 Summon Djinni
SPWI719 Summon Hakeashar
SPWI720 Control Undead
SPWI721 Mass Invisibility
SPWI722 Limited Wish
SPWI723 Improved Chaos Shield*
SPWI802 Spell Deflection
SPWI803 Protection from Energy
SPWI804 Simulacrum
SPWI805 Pierce Shield
SPWI807 Summon Fiend
SPWI808 Improved Mantle
SPWI809 Spell Trigger
SPWI810 Incendiary Cloud
SPWI811 Symbol Fear
SPWI812 Abi-Dalzim's Horrid Wilting
SPWI813 Maze
SPWI815 Power Word Blind
SPWI816 Symbol Stun
SPWI817 Symbol Death
SPWI818 Bigby's Clenched Fist
SPWI902 Spell Trap
SPWI903 Spellstrike
SPWI905 Gate
SPWI907 Absolute Immunity
SPWI908 Chain Contingency
SPWI909 Time Stop
SPWI910 Imprisonment
SPWI911 Meteor Swarm
SPWI912 Power Word Kill
SPWI913 Wail of the Banshee
SPWI914 Energy Drain
SPWI915 Black Blade of Disaster
SPWI916 Shapechange
SPWI917 Freedom
SPWI918 Bigby's Crushing Hand
SPWI919 Wish

SPPR101 Bless
SPPR102 Command
SPPR103 Cure Light Wounds
SPPR104 Detect Evil
SPPR105 Entangle
SPPR106 Magic Stone
SPPR107 Protection from Evil
SPPR108 Remove Fear
SPPR109 Sanctuary
SPPR110 Shillelagh
SPPR111 Armor of Faith
SPPR113 Doom
SPPR201 Aid
SPPR202 Barkskin
SPPR203 Chant
SPPR204 Charm Person or Mammal
SPPR205 Find Traps
SPPR206 Flame Blade
SPPR207 Goodberries
SPPR208 Hold Person
SPPR209 Know Alignment
SPPR210 Resist Fire/Cold
SPPR211 Silence 15' Radius
SPPR212 Slow Poisen
SPPR213 Spiritual Hammer
SPPR214 Draw Upon Holy Might
SPPR301 Animate Dead
SPPR302 Call Lightning
SPPR303 Dispel Magic
SPPR304 Glyph of Warding
SPPR305 Hold Animal
SPPR306 Protection from Fire
SPPR307 Remove Curse
SPPR308 Remove Paralysis
SPPR309 Invisibility Purge
SPPR310 Miscast Magic
SPPR311 Rigid Thinking
SPPR312 Strength of One
SPPR313 Holy Smite
SPPR314 Unholy Blight
SPPR315 Cure Medium Wounds
SPPR317 Cure Disease
SPPR318 Zone of Sweet Air
SPPR319 Summon Insects
SPPR401 Cure Serious Wounds
SPPR402 Animal Summoning I
SPPR403 Free Action
SPPR404 Neutralize Poison
SPPR405 Mental Dominaion
SPPR406 Defensive Harmony
SPPR407 Protection from Lightning
SPPR408 Protection from Evil 10' Radius
SPPR409 Death Ward
SPPR410 Call Woodland Beings
SPPR411 Poison
SPPR412 Holy Power
SPPR413 Negative Plain Protection
SPPR414 Cause Serious Wounds
SPPR415 Farsight
SPPR416 Cloak of Fear
SPPR417 Lesser Restoration
SPPR501 Animal Summoning II
SPPR502 Cure Critical Wounds
SPPR503 Flame Strike
SPPR504 Raise Dead
SPPR505 True Seeing
SPPR506 Ironskins
SPPR507 Champions Strength
SPPR508 Chaotic Commands
SPPR509 Magic Resistance
SPPR510 Cause Critical Wounds
SPPR511 Slay Living
SPPR512 Greater Command
SPPR513 Righteous Magic
SPPR514 Mass Cure
SPPR515 Repulse Undead
SPPR516 Pixie Dust
SPPR517 Insect Plague
SPPR601 Aerial Servant
SPPR602 Animal Summoning III
SPPR603 Blade Barrier
SPPR604 Conjure Animals
SPPR605 Conjure Fire Elemental
SPPR606 Fire Seeds
SPPR607 Heal
SPPR608 Harm
SPPR609 False Dawn
SPPR610 Dolorous Decay
SPPR611 Wonderous Recall
SPPR612 Bolt of Glory
SPPR613 Physical Mirror
SPPR614 Sol's Searing Orb
SPPR701 Shield of the Archons
SPPR702 Conjure Earth Elemental
SPPR703 Gate
SPPR704 Nature's Beauty
SPPR705 Fire Storm
SPPR706 Symbol Fear
SPPR707 Sunray
SPPR708 Finger of Death
SPPR709 Confusion
SPPR710 Holy Word
SPPR711 Regeneration
SPPR712 Resurrection
SPPR713 Greater Restoration
SPPR715 Unholy Word
SPPR717 Creeping Doom
SPPR718 Symbol Stun
SPPR719 Symbol Death
SPPR720 Earthquake

SPWISH01 Strength Modifier (+1)
SPWISH02 Intelligence Modifier (+1)
SPWISH03 Dexterity Modifier (+1)
SPWISH04 Constitution Modifier (+1)
SPWISH05 Wisdom Modifier (+1)
SPWISH06 Charisma Modifier (+1)
SPWISH07 Greater Restoration
SPWISH08 Set Stats to 25
SPWISH09 Globe of Blades (via     SPPR725)
SPWISH10 Mass Raise Dead
SPWISH11 Greater Deahblow
SPWISH12 Hardiness
SPWISH13 Alchemy
SPWISH14 Create Wand
SPWISH15 Wealth
SPWISH17 Time Stop
SPWISH18 Summon Dark Planetar
SPWISH19 Energy Drain
SPWISH20 Max Hit Points Lowered (by 50%)
SPWISH21 Max Hit Points Lowered (by 15%)
SPWISH22 Spell Memorisation Drained
SPWISH23 Ruin (Loose Gold)
SPWISH24 Meteor Swarm
SPWISH26 Breach
SPWISH27 Knockback
SPWISH29 Strength (Set to 18)
SPWISH30 Miscast Magic
SPWISH31 Magic Resistance (Set to 40%)
SPWISH32 Abi-Dalzim's Horrod Wiling
SPWISH33 Intoxification
SPWISH34 Bad Luck
SPWISH35 Power Word Silence
SPWISH36 Improved Haste
SPWISH37 Improved Haste
SPWISH38 Breach
SPWISH40 Improved Haste
SPWISH41 Strength Modifier (Set to 3)
SPWISH42 Wisdom Modifier (Set to 3)
SPWISH43 Constitution Modifier (Set to 3)
SPWISH44 Dexterity Modifier (Set to 3)
SPWISH45 Intelligence Modifier (Set to 3)
SPWISH46 Greater Restoration

SPWM101 Repulse Undead
SPWM102 Colour Pulse
SPWM103 Summon Squirrels
SPWM104 Casting Time/Attack Speed Bonus
SPWM105 Colour Pulse
SPWM107 Sex Change
SPWM108 Colour Change
SPWM109 Finger of Death (Animation)
SPWM110 ?
SPWM111 Entangle
SPWM112 Slow
SPWM113 Polymorph: Wolf
SPWM114 Hold
SPWM115 Haste
SPWM117 Loose Gold
SPWM118 Strength Modifier (-6)
SPWM120 Movement Modifier (Slower)
SPWM122 Hold
SPWM123 Symbol Fear
SPWM125 Clairvoyance
SPWM126 Minor Globe of Invulnerability
SPWM128 Dizziness
SPWM130 Pretty Animations
SPWM134 Summon Birds
SPWM136 Goodberries
SPWM140 Drain Item Charges
SPWM141 ?
SPWM142 THAC0 / Damage Modifier
SPWM140 Drain Item Charges
SPWM143 Teleport Field
SPWM145 Hiccups
SPWM152 Stunned
SPWM153 Max Hit Points Doubled
SPWM154 Summon Cacofiend
SPWM155 Sound Effect
SPWM157 Animation
SPWM159 Colour Pulse
SPWM162 Reveal Magic
SPWM164 Slow
SPWM167 Strength Modifier (+6)
SPWM168 Heal
SPWM178 Blindness
SPWM179 Charm
SPWM183 Polymorph Squirrel ?
SPWM187 Stinking Cloud
SPWM188 Play Movie (Wyvern) ?
SPWM191 Attack Speed Bonus
SPWM198 Colour Change

Bhaalspawn powers:

BHAALSPAWN_HORROR               3105

More resources related to Weidu and creating mods for BG2EE:

Contact information and mandatory 2FA (no longer) coming up in 2022 / 2023

If your creative mind has ideas and specific suggestions to make this gem more useful in general, feel free to drop me an email at any time, via:

Before that email I used an email account at Google gmail, but in 2021 I decided to slowly abandon gmail, for various reasons. In order to limit the explanation here, allow me to just briefly state that I do not feel as if I want to promote any Google service anymore when the user becomes the end product (such as via data collection by upstream services, including other proxy-services). My feeling is that this is a hugely flawed business model to begin with, and I no longer wish to support this in any way, even if only indirectly so, such as by using services of companies that try to promote this flawed model.

In regards to responding to emails: please keep in mind that responding may take some time, depending on the amount of work I may have at that moment. So it is not that emails are ignored; it is more that I have not (yet) found the time to read and reply. This means there may be a delay of days, weeks and in some instances also months. There is, unfortunately, not much I can do when I need to prioritise my time investment, but I try to consider all feedback as an opportunity to improve my projects nonetheless.

In 2022 decided to make 2FA mandatory for every gem owner eventually:


Mandatory 2FA will eventually be extended to all developers and maintainers. As I can not use 2FA, for reasons I will skip explaining here, this means that my projects will eventually be removed, as I no longer have any control over my projects hosted on (because I can not use 2FA).

At that point, I no longer have any control what is done to my projects since whoever is controlling the gems ecosystem took away our control here. I am not sure at which point ruby became corporate-controlled - that was not the case several years ago, so something has changed.

Ruby also only allows 2FA users to participate on the issue tracker these days:

But this has been reverted some months ago, so it is no longer applicable. Suffice to say that I do not think that we should only be allowed to interact on the world wide web when some 'authority' authenticated us, such as via mandatory 2FA, so I hope this won't come back again.

Fighting spam is a noble goal, but when it also means you lock out real human people then this is definitely NOT a good situation to be had.