rpg-prompt 1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/rpg-prompt +45 -0
- data/data/rpg-prompt/example.txt +190 -0
- data/data/rpg-prompt/help.txt +108 -0
- data/data/rpg-prompt/rules_default.rb +314 -0
- data/data/rpg-prompt/rules_default_hashes.rb +91 -0
- data/data/rpg-prompt/texts_default.rb +313 -0
- data/lib/rpg-prompt/commands.rb +76 -0
- data/lib/rpg-prompt/keypress.rb +66 -0
- data/lib/rpg-prompt/manage_command.rb +594 -0
- data/lib/rpg-prompt/message.rb +294 -0
- data/lib/rpg-prompt/parse_line.rb +123 -0
- data/lib/rpg-prompt/rules_default.rb +226 -0
- data/lib/rpg-prompt/rules_default_hashes.rb +68 -0
- data/lib/rpg-prompt/rules_sheet.rb +326 -0
- data/lib/rpg-prompt/save.rb +141 -0
- data/lib/rpg-prompt/texts_default.rb +346 -0
- data/lib/rpg-prompt/texts_prompt.rb +259 -0
- data/tests/test_rpg-prompt.rb +10 -0
- metadata +67 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2cb37842ec6500eb1e3fc62a13d0b8bf942ed8c71ad97d7d8d9753a692cbb827
|
|
4
|
+
data.tar.gz: 3fdf422337ad6a336e8a05a9cce2e9062a16872a62b1b75c22045e6f2dbdaf01
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: c0426b1466fde458ab9ae4a3b07b79d5fc0fd1fd541eade5e3871c2fad81727be045d396fedbc06f14e59770586e2d9eb2ca591d1ca4762e41455d1f6d34be8b
|
|
7
|
+
data.tar.gz: 98528b0029f2bd9600b3925468a7b848b5f0ef137e52d47c4f892921a531abb985e4c8ae8ac2afc8acb8456c50095cb1e7b0a9ef519918f96788b46d90d329da
|
data/bin/rpg-prompt
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
require "rpg-prompt/parse_line.rb"
|
|
3
|
+
|
|
4
|
+
# Settings_file_name = "./rpg-prompt.settings" # defined in manage_command.m
|
|
5
|
+
# default value
|
|
6
|
+
$language = "english"
|
|
7
|
+
|
|
8
|
+
if File.exists?(Settings_file_name)
|
|
9
|
+
File.readlines(Settings_file_name).each do |line|
|
|
10
|
+
m = line.match(/(?<setting>\w*): (?<val>\w*)/)
|
|
11
|
+
if m == nil
|
|
12
|
+
break
|
|
13
|
+
else
|
|
14
|
+
case m[:setting]
|
|
15
|
+
when "language"
|
|
16
|
+
$language = m[:val]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
Message::Dictionary.language = $language.to_sym
|
|
23
|
+
|
|
24
|
+
Message.message(:greeting)
|
|
25
|
+
|
|
26
|
+
Backup_file_name = "./.backup.marshal"
|
|
27
|
+
|
|
28
|
+
if File.exist?(Backup_file_name)
|
|
29
|
+
line = ParseLine::Line.new("rrr")
|
|
30
|
+
command_symbol = line.parse.command
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
loop do
|
|
34
|
+
print (">>>")
|
|
35
|
+
line = ParseLine::Line.new($stdin.gets.chomp)
|
|
36
|
+
command_symbol = line.parse.command
|
|
37
|
+
puts
|
|
38
|
+
if command_symbol == :quit
|
|
39
|
+
break
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
Save.delete_pool
|
|
44
|
+
Message.message(:goodbye)
|
|
45
|
+
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
|
|
|
2
|
+
| Example of use the RPG Combat Assistant, by Guillermo Regodón.
|
|
3
|
+
|
|
|
4
|
+
| First, we should create a character for each player. For example:
|
|
5
|
+
|
|
|
6
|
+
| >>>cc billy
|
|
7
|
+
|
|
|
8
|
+
| to create a Character for player billy. Answer the questionnaire. Each character
|
|
9
|
+
| has a {short_name} to handle it, billy in this case. Then it has a full name, an
|
|
10
|
+
| optional nickname, a race, health points, a weapon, an armor, and a movement
|
|
11
|
+
| capacity. In these simple rules, the race, the weapon and the movement capacity
|
|
12
|
+
| have no effect in the game. Let us say that we create Billy the warrior, a human
|
|
13
|
+
| who uses a broad sword and plate armor.
|
|
14
|
+
|
|
|
15
|
+
| Full name => Billy
|
|
16
|
+
| Nickname => the warrior
|
|
17
|
+
| Race => Human
|
|
18
|
+
| Health Points => 10
|
|
19
|
+
| Weapon => broad sword
|
|
20
|
+
| Armor => plate
|
|
21
|
+
| Movement Capacity => 10
|
|
22
|
+
|
|
|
23
|
+
| Then we create an enemy, Azog the white goblin, an orc who uses a mace and hardened
|
|
24
|
+
| leather armor. He has 8 health points and a movement capacity 10.
|
|
25
|
+
|
|
|
26
|
+
| >>>cf azog
|
|
27
|
+
|
|
|
28
|
+
| In the pool we have 2 warriors so far.
|
|
29
|
+
|
|
|
30
|
+
| >>>pool
|
|
31
|
+
| billy: Billy the Warrior
|
|
32
|
+
| azog: Azog the white goblin
|
|
33
|
+
|
|
|
34
|
+
| You may just type "p" instead of "pool". Similarly you could have typed "createchar"
|
|
35
|
+
| and "createfoe" instead of "cc" and "cf". Let the warriors join the combat. type
|
|
36
|
+
| "save w billy" ("sw billy") and "save w azog" ("sw azog") to save them in files.
|
|
37
|
+
|
|
|
38
|
+
| >>>join billy
|
|
39
|
+
| >>>join azog
|
|
40
|
+
| >>>combatstatus
|
|
41
|
+
| billy: Billy the Warrior: 10 hp
|
|
42
|
+
| azog: Azog the white goblin: 8 hp
|
|
43
|
+
|
|
|
44
|
+
| Or just "s" to get the status of the combat, with the health points prompted. Then
|
|
45
|
+
| combat starts with an attack from Billy the Warrior to Azog the white goblin.
|
|
46
|
+
|
|
|
47
|
+
| >>>billy hits azog
|
|
48
|
+
| ¡Billy the Warrior attacks Azog the white goblin!
|
|
49
|
+
| Press 'r' to enter a roll or any other key for a random roll.
|
|
50
|
+
|
|
|
51
|
+
| You may enter the roll if you prefer to roll real dice instead of getting random
|
|
52
|
+
| numbers from an unknown algorithm. Let us trust the random number generator to be
|
|
53
|
+
| a fair judge of the warriors destiny.
|
|
54
|
+
|
|
|
55
|
+
| >>>billy hits azog
|
|
56
|
+
| ¡Billy the Warrior attacks Azog the white goblin!
|
|
57
|
+
| Press 'r' to enter a roll or any other key for a random roll.
|
|
58
|
+
| Dice rolling... ¡5!
|
|
59
|
+
| The attack succeeds!
|
|
60
|
+
| Press 'r' to enter a roll or any other key for a random roll.
|
|
61
|
+
|
|
|
62
|
+
| The attack succeeding, a new roll is needed to get the damage.
|
|
63
|
+
|
|
|
64
|
+
| >>>billy hits azog
|
|
65
|
+
| ¡Billy the Warrior attacks Azog the white goblin!
|
|
66
|
+
| Press 'r' to enter a roll or any other key for a random roll.
|
|
67
|
+
| Dice rolling... ¡5!
|
|
68
|
+
| The attack succeeds!
|
|
69
|
+
| Press 'r' to enter a roll or any other key for a random roll.
|
|
70
|
+
| Dice rolling... ¡2!
|
|
71
|
+
| ¡Azog the white goblin loses 2 hp!
|
|
72
|
+
|
|
|
73
|
+
| A call to "combatstatus" prompts
|
|
74
|
+
|
|
|
75
|
+
| >>>s
|
|
76
|
+
| billy: Billy the Warrior: 10 hp
|
|
77
|
+
| azog: Azog the white goblin: 6 hp
|
|
78
|
+
|
|
|
79
|
+
| Let the cries of pain from azog attract his hench-orcs. We have to create them
|
|
80
|
+
| before. As a generic type of enemy, it has to be created as a Spawn template.
|
|
81
|
+
| The difference is that it may have many possible names and nicknames.
|
|
82
|
+
|
|
|
83
|
+
| >>>createspawn orc
|
|
84
|
+
| Enter names for this kind of enemy, recomended 10 or more,
|
|
85
|
+
| "q" to finish. Enter only the race if it is an enemy without name.
|
|
86
|
+
| Ulag
|
|
87
|
+
| Urglu
|
|
88
|
+
| Shagrath
|
|
89
|
+
| Gorbagh
|
|
90
|
+
| Karg
|
|
91
|
+
| Zulburg
|
|
92
|
+
| Groggash
|
|
93
|
+
| Skumzag
|
|
94
|
+
| Agash
|
|
95
|
+
| Zudak
|
|
96
|
+
| q
|
|
97
|
+
| Enter nicknames for this kind of enemy, recomended 10 or more,
|
|
98
|
+
| "q" to finish.
|
|
99
|
+
| the Smelly
|
|
100
|
+
| the Dumb
|
|
101
|
+
| the Cruel
|
|
102
|
+
| the Elfslayer
|
|
103
|
+
| the Destroyer
|
|
104
|
+
| the Crazy Lamb
|
|
105
|
+
| the Poisson Razer
|
|
106
|
+
| the Gross
|
|
107
|
+
| the Fearless
|
|
108
|
+
| the Hunchback
|
|
109
|
+
| q
|
|
110
|
+
|
|
|
111
|
+
| "cs orc" could have been used. They are Orcs, have 5 health points, use
|
|
112
|
+
| scimitars and hardened leather, and they may move up to 10 meters per assault.
|
|
113
|
+
| They join the combat by typing
|
|
114
|
+
|
|
|
115
|
+
| >>>spawn orc x3
|
|
116
|
+
| Spawning enemies: 3 of type orc
|
|
117
|
+
|
|
|
118
|
+
| >>>s
|
|
119
|
+
| billy: Billy the Warrior: 10 hp
|
|
120
|
+
| azog: Azog the white goblin: 6 hp
|
|
121
|
+
| orc1: Karg the Hunchback: 5 hp
|
|
122
|
+
| orc2: Skumzag the Cruel: 5 hp
|
|
123
|
+
| orc3: Zulburg the Poisson Razer: 5 hp
|
|
124
|
+
|
|
|
125
|
+
| They may bring wolfs as well, the only difference is that they would not have
|
|
126
|
+
| names, and we woould type "Wolf" for the full name. They may have nicknames,
|
|
127
|
+
| like "alfa", "old", "defiant" or "black". Billy is in trouble, which was
|
|
128
|
+
| expected when he was entering an orc cave alone, he should have brought allies,
|
|
129
|
+
| like Edana the shadow queen. The warriors leave the combat when they die, or when
|
|
130
|
+
| "leave billy" is typed.
|
|
131
|
+
|
|
|
132
|
+
| For the next game we could prepare scenes. Let us prepare an orc_hall and a
|
|
133
|
+
| wolf_cave. Type "lw" to list the available warriors in saved files, and
|
|
134
|
+
| "free all" to start a pool and a combat from scratch.
|
|
135
|
+
|
|
|
136
|
+
| >>>free all
|
|
137
|
+
| >>>lw
|
|
138
|
+
| wolf
|
|
139
|
+
| billy
|
|
140
|
+
| orc
|
|
141
|
+
| edana
|
|
142
|
+
| azog
|
|
143
|
+
|
|
|
144
|
+
| Let us create the "orc_hall" scene.
|
|
145
|
+
|
|
|
146
|
+
| >>>lw azog
|
|
147
|
+
| >>>lw orc
|
|
148
|
+
|
|
|
149
|
+
| >>>join azog
|
|
150
|
+
| >>>spawn orc x3
|
|
151
|
+
|
|
|
152
|
+
| >>>s
|
|
153
|
+
| azog: Azog the white goblin: 10 hp
|
|
154
|
+
| orc1: Agash the Gross: 5 hp
|
|
155
|
+
| orc2: Ulag the Cruel: 5 hp
|
|
156
|
+
| orc3: Gorbagh the Smelly: 5 hp
|
|
157
|
+
|
|
|
158
|
+
| >>>save s orc_hall
|
|
159
|
+
| Saving...
|
|
160
|
+
| Scene saved.
|
|
161
|
+
|
|
|
162
|
+
| Similarly, the "wolf_cave" scene.
|
|
163
|
+
|
|
|
164
|
+
| >>>spawn wolf x5
|
|
165
|
+
| Spawning enemies: 5 of type wolf
|
|
166
|
+
|
|
|
167
|
+
| >>>s
|
|
168
|
+
| wolf1: Wolf old: 6 hp
|
|
169
|
+
| wolf2: Wolf beta: 6 hp
|
|
170
|
+
| wolf3: Wolf grey: 6 hp
|
|
171
|
+
| wolf4: Wolf alfa: 6 hp
|
|
172
|
+
| wolf5: Wolf scarred: 6 hp
|
|
173
|
+
|
|
|
174
|
+
| >>>ss wolf_cave
|
|
175
|
+
| Saving...
|
|
176
|
+
| Scene saved.
|
|
177
|
+
|
|
|
178
|
+
| We have use "ss" as a short version of "save s". Let us list the save scenes
|
|
179
|
+
|
|
|
180
|
+
| >>>ls
|
|
181
|
+
| orc_hall
|
|
182
|
+
| wolf_cave
|
|
183
|
+
|
|
|
184
|
+
| The command "load s orc_hall" or "ls orc_hall" loads azog and the orc
|
|
185
|
+
| template into the pool, and Azog the white goblin, Agash the Gross, Ulag
|
|
186
|
+
| the Cruel and Gorbagh the Smelly into the combat.
|
|
187
|
+
|
|
|
188
|
+
| You make quit and save the temporal pool and combat using "quit -s" or
|
|
189
|
+
| dismiss them with "quit -q".
|
|
190
|
+
|
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
Welcome to the RPG Combat Assistant, by Guillermo Regodón
|
|
2
|
+
|
|
3
|
+
With this Ruby Gem, you can handle complex RPG systems automatically, by means of
|
|
4
|
+
a set of options that can be passed in a prompt. I have included a simple set of
|
|
5
|
+
rules of my own creation, but you may use this gem to program in Ruby your own
|
|
6
|
+
rules or a set of commercial rules for your personal use. I have not included any
|
|
7
|
+
set of commercial rules for copyright issues, even when I have programmed some for
|
|
8
|
+
my pesonal use.
|
|
9
|
+
|
|
10
|
+
Install the Gem, create your directory and type "rpg-prompt" to start. To start
|
|
11
|
+
your personal set of rules, type "clone rules {new_rules_system}" indicating the
|
|
12
|
+
name of your set of rules. after quitting "rpg-prompt" you will see the edittable
|
|
13
|
+
files that code the new set of rules. Code them in Ruby, and start using them in
|
|
14
|
+
your games.
|
|
15
|
+
|
|
16
|
+
There are three types of warriors:
|
|
17
|
+
- Characters, which will be handled by players
|
|
18
|
+
- Foes, which are enemies of the players
|
|
19
|
+
- Spawns, generic enemies of which several with the same attributes may appears
|
|
20
|
+
|
|
21
|
+
The warrios can be in the pool, meaning that they have been loaded but are not in
|
|
22
|
+
combat. Type "pool" or "p" to print the pool. Spawns in the pool are templates.
|
|
23
|
+
|
|
24
|
+
The warriors can be in combat, after being loaded. Type "combatstatus" or "s" to
|
|
25
|
+
print the combat. Health points will be printed as well. Spawns in combat are
|
|
26
|
+
copies of the template.
|
|
27
|
+
|
|
28
|
+
Warriors can be loaded, saved and created
|
|
29
|
+
- To create a warrior, type "createchar" ("cc"), "createfoe" ("cf") or
|
|
30
|
+
"createspawn" ("cs") followed by the short name (no spaces) that you
|
|
31
|
+
will use to handle the warrior. A questionnaire will be prompt asking
|
|
32
|
+
for the values to fill the character sheet.
|
|
33
|
+
Spawns are special, they will have many names and full names, so that
|
|
34
|
+
when they are spawned into combat, random combinations will be used
|
|
35
|
+
- To save a warrior, type "save w {short_name}" or "sw {short_name}"
|
|
36
|
+
- To list the saved warriors, type "lw"
|
|
37
|
+
- To load a warrior, type "load w {short_name}" or "lw {short_name}"
|
|
38
|
+
- You may delete the file or delete from the prompt, using
|
|
39
|
+
"delete w {short_name}" or "dw {short_name}"
|
|
40
|
+
|
|
41
|
+
Characters and Foes can be joined to the combat using "join {short_name}".
|
|
42
|
+
Spawns have to be spawned into combat using "spawn {short_name} xN". N Spawns
|
|
43
|
+
using the template will be spawned into combat. From this point, each spawn is
|
|
44
|
+
a separate enemy named with the short name followed by a number (check using
|
|
45
|
+
"combatstatus" or "s").
|
|
46
|
+
|
|
47
|
+
Warriors leave the combat as they die, but they can also be removed:
|
|
48
|
+
- "leave {short_name}" for a warrior to leave the combat.
|
|
49
|
+
- "free {short_name}" to remove a warrior or a template from the pool
|
|
50
|
+
- "free all" to clean both the Combat and the pool
|
|
51
|
+
|
|
52
|
+
Scenes are combinations of warriors to be saved and loaded at once. This allows
|
|
53
|
+
you to prepare a scene and load it when the players enter a cetain place or trigger
|
|
54
|
+
a certain event in your game.
|
|
55
|
+
- To save a scene, type "save s {scene_name}" or "ss {scene_name}"
|
|
56
|
+
- To list the saved scenes, type "ls"
|
|
57
|
+
- To load a scene, type "load s {scene_name}" or "ls {scene_name}"
|
|
58
|
+
- You may delete the file or delete from the prompt, using
|
|
59
|
+
"delete s {scene_name}" or "dw {scene_name}"
|
|
60
|
+
|
|
61
|
+
You may pass a round using "round" or "round +N". Although, in the simple rules
|
|
62
|
+
provided in this RPG helper, this has no effect.
|
|
63
|
+
|
|
64
|
+
To perform an attack, use:
|
|
65
|
+
- "{attacker_short_name} hits {defender_short_name} -options"
|
|
66
|
+
|
|
67
|
+
The options that are included in these simple rules are:
|
|
68
|
+
-c: to check the result of the attack but not apply the sometimes nasty
|
|
69
|
+
consequences. For kind Gamemasters.
|
|
70
|
+
-f: for a flank attack. It applies a bonus to the attack.
|
|
71
|
+
-r: for a rear attack. It applies a better bonus to the attack.
|
|
72
|
+
-h: for am attack from higher ground. It applies a bonus to the attack.
|
|
73
|
+
-s: for a surprise attack. It applies a bonus to the attack.
|
|
74
|
+
-u: for an attack while unsheathing the weapon. It applies a malus to
|
|
75
|
+
the attack.
|
|
76
|
+
-d: for an attack from a distance. It applies a malus to the attack.
|
|
77
|
+
Each time it is used, the attack is performed with a lower chance
|
|
78
|
+
of success.
|
|
79
|
+
|
|
80
|
+
Options may be applied without consecutive slashes, but the first one is
|
|
81
|
+
required. This may not make sense but for the distance modifier.
|
|
82
|
+
|
|
83
|
+
To perform an action, type:
|
|
84
|
+
- "skill {short_name} {skill_name} +/-mod -options"
|
|
85
|
+
|
|
86
|
+
The mod modifier is of free use for the Gamemaster to match the special
|
|
87
|
+
circumstances of the scene. Generic options can be applied. In these simple
|
|
88
|
+
rules, the options are:
|
|
89
|
+
-c: to check the result of the action. Useless in these simple rules.
|
|
90
|
+
-e: to apply an "easy" bonus.
|
|
91
|
+
-m: to apply a "medium" bonus. Standard actions uses this modifier by
|
|
92
|
+
default.
|
|
93
|
+
-h: to apply a "hard" malus.
|
|
94
|
+
|
|
95
|
+
To show the help for a certain skill, type "skill_help {skill_name}" or
|
|
96
|
+
just "sh {skill_name}". Type "skill_list" to show a list of the available
|
|
97
|
+
skills.
|
|
98
|
+
|
|
99
|
+
Type:
|
|
100
|
+
- "help" to print this help.
|
|
101
|
+
- "example" to print an example of use.
|
|
102
|
+
- "undo" to undo the last attack, in case the consequences are too nasty.
|
|
103
|
+
- "quit -s" to save and quit. Next time rpg-prompt is called, it will load
|
|
104
|
+
the status of the Pool and the Combat.
|
|
105
|
+
- "quit -q" to quit without saving.
|
|
106
|
+
- "clone rules {new_rules_system}" to clone the basic files to program,
|
|
107
|
+
in Ruby, a new set of rules.
|
|
108
|
+
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# default value
|
|
2
|
+
$system = "default"
|
|
3
|
+
|
|
4
|
+
if File.exists?(Settings_file_name)
|
|
5
|
+
File.readlines(Settings_file_name).each do |line|
|
|
6
|
+
m = line.match(/(?<setting>\w*): (?<val>\w*)/)
|
|
7
|
+
if m == nil
|
|
8
|
+
break
|
|
9
|
+
else
|
|
10
|
+
case m[:setting]
|
|
11
|
+
when "system"
|
|
12
|
+
$system = m[:val]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
unless $system == "default"
|
|
19
|
+
$hashes_file_name = "./rules_" + $system + "_hashes.rb"
|
|
20
|
+
unless (File.exist?($rules_file_name)) && (File.exist?($hashes_file_name)) && (File.exist?($texts_file_name))
|
|
21
|
+
Message.message(:wrong_config)
|
|
22
|
+
return -1
|
|
23
|
+
end
|
|
24
|
+
else
|
|
25
|
+
$hashes_file_name = "rpg-prompt/rules_default_hashes.rb"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
require $hashes_file_name
|
|
29
|
+
|
|
30
|
+
require "rpg-prompt/message.rb"
|
|
31
|
+
require "rpg-prompt/texts_default.rb"
|
|
32
|
+
require "rpg-prompt/keypress.rb"
|
|
33
|
+
require "rpg-prompt/parse_line.rb"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# With these lines, we include the options available for the attacks and the
|
|
37
|
+
# actions. You may also include more commands or more options for these
|
|
38
|
+
# commands. I have included a version of the commands with full words and
|
|
39
|
+
# with abbreviatures. Take special care that no two regular expressions match
|
|
40
|
+
# the same input line!
|
|
41
|
+
|
|
42
|
+
ParseLine::Line.add_command(:hits,
|
|
43
|
+
/(?<atk>\w+) hits (?<def>\w+)/,
|
|
44
|
+
["c", "f", "r", "h", "s", "u", "d"])
|
|
45
|
+
ParseLine::Line.add_command(:hits_short,
|
|
46
|
+
/(?<atk>\w+) h (?<def>\w+)/,
|
|
47
|
+
["c", "f", "r", "h", "s", "u", "d"])
|
|
48
|
+
|
|
49
|
+
ParseLine::Line.add_command(:skill,
|
|
50
|
+
/skill (?<ch>\w+) (?<skill>\w+)( [+-]\d+)?/,
|
|
51
|
+
["c", "e", "m", "h"])
|
|
52
|
+
ParseLine::Line.add_command(:skill_short,
|
|
53
|
+
/(?<ch>\w+)( )?:( )?(?<skill>\w+)( [+-]\d+)?/,
|
|
54
|
+
["c", "e", "m", "h"])
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# Rules module
|
|
58
|
+
module Rules
|
|
59
|
+
|
|
60
|
+
# Module to define the possible rolls
|
|
61
|
+
module Dice
|
|
62
|
+
|
|
63
|
+
# The roll itself
|
|
64
|
+
def self.roll(type_of_roll)
|
|
65
|
+
case type_of_roll
|
|
66
|
+
when :d6
|
|
67
|
+
1+rand(6)
|
|
68
|
+
when :d10
|
|
69
|
+
1+rand(10)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# A small prompt to require a roll o perfomr a random roll
|
|
74
|
+
def self.prompt_roll(type_of_roll)
|
|
75
|
+
case type_of_roll
|
|
76
|
+
when :d6
|
|
77
|
+
roll_prompt = :ask_for_d6
|
|
78
|
+
roll_limit = 6
|
|
79
|
+
else
|
|
80
|
+
roll_prompt = :ask_for_d10
|
|
81
|
+
roll_limit = 10
|
|
82
|
+
end
|
|
83
|
+
roll = 0
|
|
84
|
+
Message.message(:preset_roll_prompt)
|
|
85
|
+
case Keypress.read_char
|
|
86
|
+
when "r"
|
|
87
|
+
Message.message(roll_prompt)
|
|
88
|
+
loop do
|
|
89
|
+
roll = $stdin.gets.to_i
|
|
90
|
+
if (roll > roll_limit) || (roll <= 0)
|
|
91
|
+
Message.message(:bad_range)
|
|
92
|
+
redo
|
|
93
|
+
else
|
|
94
|
+
Message.help(:rolling, {roll: roll})
|
|
95
|
+
end
|
|
96
|
+
break
|
|
97
|
+
end
|
|
98
|
+
else
|
|
99
|
+
roll = Dice.roll(type_of_roll)
|
|
100
|
+
Message.help(:rolling, {roll: roll})
|
|
101
|
+
end
|
|
102
|
+
roll
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Generic type of Turn for a character
|
|
107
|
+
class Turn
|
|
108
|
+
def resolve
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Attack turn.
|
|
113
|
+
# A new AttackTurn object is created for each attack action
|
|
114
|
+
# - The initializer receives the attacker and the defender sheets, from
|
|
115
|
+
# where it extracts the attacker weapon and defender armor used
|
|
116
|
+
# - The process_options method is the key help of this gem. The options
|
|
117
|
+
# from your rules system are coded into characters to be used in the
|
|
118
|
+
# prompt after a '-'
|
|
119
|
+
# - The resolve method resolves the attack action
|
|
120
|
+
class AttackTurn
|
|
121
|
+
@no_damage = nil
|
|
122
|
+
@modifier = nil
|
|
123
|
+
|
|
124
|
+
# Extracts the attacker weapon and defender armor used
|
|
125
|
+
# Defines the globals to handle the options
|
|
126
|
+
def initialize(attacker_sheet, defender_sheet)
|
|
127
|
+
@attacker_sheet = attacker_sheet
|
|
128
|
+
@defender_sheet = defender_sheet
|
|
129
|
+
@weapon = Weapon.new(@attacker_sheet)
|
|
130
|
+
@armor = Armor.new(@defender_sheet)
|
|
131
|
+
if @no_damage == nil
|
|
132
|
+
@no_damage = false
|
|
133
|
+
@modifier = 0
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Process the options matching a rule with a character to be typed
|
|
138
|
+
# in the prompt after a slash
|
|
139
|
+
def process_options(options)
|
|
140
|
+
@no_damage = false
|
|
141
|
+
@modifier = 0
|
|
142
|
+
distance = 0
|
|
143
|
+
options.each do |option|
|
|
144
|
+
c = option[0]
|
|
145
|
+
option[0] = ' '
|
|
146
|
+
case c
|
|
147
|
+
when 'c' #check modifiers, no damage is done
|
|
148
|
+
@no_damage = true
|
|
149
|
+
when 'f' #flank attack
|
|
150
|
+
@modifier += 1
|
|
151
|
+
when 'r' #attack from the rear
|
|
152
|
+
@modifier += 2
|
|
153
|
+
when 'h' #higher ground
|
|
154
|
+
@modifier += 1
|
|
155
|
+
when 's' #surprise
|
|
156
|
+
@modifier += 2
|
|
157
|
+
when 'u' #attack while unsheathing
|
|
158
|
+
@modifier -= -1
|
|
159
|
+
when 'd' #distance modifier
|
|
160
|
+
distance += 1
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
if (distance > 0) && (distance < @weapon.weapon[:distance_mod].length)
|
|
164
|
+
@modifier += @weapon.weapon[:distance_mod][distance][1]
|
|
165
|
+
elsif (distance > 0) && (distance >= @weapon.weapon[:distance_mod].length)
|
|
166
|
+
@no_damage = true
|
|
167
|
+
end
|
|
168
|
+
return @no_damage, @modifier
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
#helper method for resolve. It prompts the modifier
|
|
172
|
+
def mod_roll(roll)
|
|
173
|
+
r = roll + @modifier
|
|
174
|
+
Message.help(:attack_roll, {r: r, roll: roll, modifier: @modifier})
|
|
175
|
+
r
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Resolves the attack, with the modifier from process_options
|
|
179
|
+
def resolve
|
|
180
|
+
# In these rules, attacks are resolved with d10
|
|
181
|
+
roll = Dice.prompt_roll(:d10)
|
|
182
|
+
mod_roll = mod_roll(roll)
|
|
183
|
+
|
|
184
|
+
# damage may be different for each weapon-armor combination
|
|
185
|
+
damage = @weapon.damage_roll(@armor, mod_roll)
|
|
186
|
+
|
|
187
|
+
# unless check option, reduce defender health points
|
|
188
|
+
unless @no_damage then @defender_sheet.reduce_health(damage) end
|
|
189
|
+
|
|
190
|
+
if damage > 0
|
|
191
|
+
Message.help(:hits_lose_hp,
|
|
192
|
+
{full_name: @defender_sheet.full_name, damage: damage})
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Action turn
|
|
198
|
+
# Characters can perform non attack actions, with modifier
|
|
199
|
+
class ActionTurn < Turn
|
|
200
|
+
@@skill_hash = nil
|
|
201
|
+
@skill_sym = nil
|
|
202
|
+
|
|
203
|
+
# If the character has a bonus in he skill, it is added to the modifiers
|
|
204
|
+
# Note that in these simple set of rules, no character ever has a bonus,
|
|
205
|
+
# because it was not included in the character creation Questionnaire.
|
|
206
|
+
# A bonus can be given using the set command.
|
|
207
|
+
def initialize(sheet, skill)
|
|
208
|
+
@sheet = sheet
|
|
209
|
+
unless @skill_sym
|
|
210
|
+
@skill_sym = Message.skill_symbol(skill)
|
|
211
|
+
@@skill_hash = RulesHashes::SkillHash.new
|
|
212
|
+
end
|
|
213
|
+
@skill_bonus = @sheet[@skill_sym] ? @sheet[@skill_sym] : 0
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# checks if the skill has been defined in RulesHashes
|
|
217
|
+
def self.skill?(skill)
|
|
218
|
+
unless @skill_sym
|
|
219
|
+
@skill_sym = Message.skill_symbol(skill)
|
|
220
|
+
@@skill_hash = RulesHashes::SkillHash.new
|
|
221
|
+
end
|
|
222
|
+
@@skill_hash.key?(@skill_sym)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# In these simple set of rules, only a basic difficulty rule is included
|
|
226
|
+
def process_options(options, command_mod)
|
|
227
|
+
@modifier = command_mod
|
|
228
|
+
@no_action = false
|
|
229
|
+
options.each do |option|
|
|
230
|
+
c = option[0]
|
|
231
|
+
option[0] = ' '
|
|
232
|
+
case c
|
|
233
|
+
when 'c' #check
|
|
234
|
+
@no_action = true
|
|
235
|
+
when 'e' #easy
|
|
236
|
+
@modifier += 3
|
|
237
|
+
when 'm' #medium
|
|
238
|
+
@modifier += 0
|
|
239
|
+
when 'h' #hard
|
|
240
|
+
@modifier -= 3
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
@modifier += @skill_bonus
|
|
244
|
+
return @no_action, @modifier
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# Helper method for action_table_result. It guarantees in range
|
|
248
|
+
def truncate_val(v)
|
|
249
|
+
v = (v > 500) ? 500 : v
|
|
250
|
+
v = (v < -500) ? -500 : v
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
# Checks for the result in the ActionTable
|
|
254
|
+
def action_table_result(roll)
|
|
255
|
+
r = truncate_val(roll)
|
|
256
|
+
@@action_table = RulesHashes::ActionTable.new
|
|
257
|
+
action_result = :failure
|
|
258
|
+
@@action_table.each do |ar, l|
|
|
259
|
+
if (r >= l[0]) && (r < l[1])
|
|
260
|
+
action_result = ar
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
action_result
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# Helper method for resolve. It prompts the modifier
|
|
267
|
+
def mod_roll(roll)
|
|
268
|
+
r = roll + @modifier
|
|
269
|
+
Message.help(:skill_roll, {r: r, roll: roll, modifier: @modifier})
|
|
270
|
+
r
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
# Rresolves the action, with the options from process_options
|
|
274
|
+
def resolve
|
|
275
|
+
roll = Dice.prompt_roll(:d10)
|
|
276
|
+
action_result = action_table_result(mod_roll(roll))
|
|
277
|
+
Message.skill_word(action_result)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Weapon
|
|
282
|
+
# Each weapon is responsible of returning the damage that it can make to
|
|
283
|
+
# each armor, for a given modified roll. In these simple rules, the damage
|
|
284
|
+
# is always a d6, if the modified roll surpasses the armor value
|
|
285
|
+
class Weapon
|
|
286
|
+
attr_accessor :weapon
|
|
287
|
+
|
|
288
|
+
def initialize(sheet)
|
|
289
|
+
@@weapon_hash = RulesHashes::WeaponHash.new()
|
|
290
|
+
@weapon = @@weapon_hash[sheet[:weapon]]
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def damage_roll(armor, mod_roll)
|
|
294
|
+
if (mod_roll >= armor[:armor])
|
|
295
|
+
Message.message(:hits_success)
|
|
296
|
+
Dice.prompt_roll(:d6)
|
|
297
|
+
else
|
|
298
|
+
Message.message(:hits_fail)
|
|
299
|
+
0
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# Armor
|
|
305
|
+
# Each armor has to provide enough information to the weapon for it to
|
|
306
|
+
# calculate the damage. In these simple rules, it returns only a threshold
|
|
307
|
+
# for each armor type
|
|
308
|
+
class Armor < Hash
|
|
309
|
+
def initialize(sheet)
|
|
310
|
+
@@weapon_hash = RulesHashes::ArmorHash.new()
|
|
311
|
+
self[:armor] = @@weapon_hash[sheet[:armor]]
|
|
312
|
+
end
|
|
313
|
+
end
|
|
314
|
+
end
|