amar-rpg 2.0.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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +675 -0
  3. data/README.md +155 -0
  4. data/amar-tui.rb +8195 -0
  5. data/cli_enc_output.rb +87 -0
  6. data/cli_enc_output_new.rb +433 -0
  7. data/cli_enc_output_new_3tier.rb +198 -0
  8. data/cli_enc_output_new_compact.rb +238 -0
  9. data/cli_name_gen.rb +21 -0
  10. data/cli_npc_output.rb +279 -0
  11. data/cli_npc_output_new.rb +700 -0
  12. data/cli_town_output.rb +39 -0
  13. data/cli_weather_output.rb +36 -0
  14. data/includes/class_enc.rb +341 -0
  15. data/includes/class_enc_new.rb +512 -0
  16. data/includes/class_monster_new.rb +551 -0
  17. data/includes/class_npc.rb +1378 -0
  18. data/includes/class_npc_new.rb +1187 -0
  19. data/includes/class_npc_new.rb.backup +706 -0
  20. data/includes/class_npc_new_skills.rb +153 -0
  21. data/includes/class_town.rb +237 -0
  22. data/includes/d6s.rb +40 -0
  23. data/includes/equipment_tables.rb +120 -0
  24. data/includes/functions.rb +67 -0
  25. data/includes/includes.rb +30 -0
  26. data/includes/randomizer.rb +15 -0
  27. data/includes/spell_catalog.rb +441 -0
  28. data/includes/tables/armour.rb +13 -0
  29. data/includes/tables/chartype.rb +4412 -0
  30. data/includes/tables/chartype_new.rb +765 -0
  31. data/includes/tables/chartype_new_full.rb +2713 -0
  32. data/includes/tables/enc_specific.rb +168 -0
  33. data/includes/tables/enc_type.rb +17 -0
  34. data/includes/tables/encounters.rb +99 -0
  35. data/includes/tables/magick.rb +169 -0
  36. data/includes/tables/melee.rb +36 -0
  37. data/includes/tables/missile.rb +17 -0
  38. data/includes/tables/monster_stats_new.rb +264 -0
  39. data/includes/tables/month.rb +18 -0
  40. data/includes/tables/names.rb +21 -0
  41. data/includes/tables/personality.rb +12 -0
  42. data/includes/tables/race_templates.rb +318 -0
  43. data/includes/tables/religions.rb +266 -0
  44. data/includes/tables/spells_new.rb +496 -0
  45. data/includes/tables/tier_system.rb +104 -0
  46. data/includes/tables/town.rb +71 -0
  47. data/includes/tables/weather.rb +41 -0
  48. data/includes/town_relations.rb +127 -0
  49. data/includes/weather.rb +108 -0
  50. data/includes/weather2latex.rb +114 -0
  51. data/lib/rcurses.rb +33 -0
  52. metadata +157 -0
@@ -0,0 +1,198 @@
1
+ # Encounter output module showing full 3-tier system format
2
+ require 'io/console'
3
+
4
+ def enc_output_new_3tier(e, cli)
5
+ f = ""
6
+
7
+ # Header with box characters
8
+ width = 120
9
+ f += "═" * width + "\n"
10
+
11
+ # Day/Night and Terrain
12
+ day_str = $Day == 1 ? "Day: " : "Night: "
13
+ terrain_str = case $Terrain
14
+ when 0 then "City "
15
+ when 1 then "Rural "
16
+ when 2 then "Road "
17
+ when 3 then "Plains "
18
+ when 4 then "Hills "
19
+ when 5 then "Mountains "
20
+ when 6 then "Woods "
21
+ when 7 then "Wilderness"
22
+ end
23
+
24
+ level_mod_str = "(Level mod = #{$Level || 0})"
25
+ date_str = "Created: #{Date.today.to_s}"
26
+
27
+ f += "#{day_str}#{terrain_str}#{level_mod_str}#{date_str.rjust(width - day_str.length - terrain_str.length - level_mod_str.length)}\n"
28
+ f += "─" * width + "\n"
29
+
30
+ # Check for no encounter
31
+ if e.is_no_encounter?
32
+ f += "\nNO ENCOUNTER\n\n"
33
+ f += "═" * width + "\n"
34
+ else
35
+ # Show attitude and summary
36
+ f += "#{e.enc_attitude}: #{e.summary}\n"
37
+ f += "─" * width + "\n\n"
38
+
39
+ # List all NPCs in the encounter with 3-tier format
40
+ e.encounter.each_with_index do |enc_data, index|
41
+ break if enc_data["string"] =~ /Event:/
42
+
43
+ npc = enc_data["npc"]
44
+ next unless npc
45
+
46
+ # NPC header
47
+ name_str = npc.name.empty? ? enc_data["string"] : npc.name
48
+ type_str = npc.respond_to?(:type) ? npc.type : "Unknown"
49
+ f += "#{index + 1}. #{name_str} (#{npc.sex}) - #{type_str} [Level #{npc.level}]\n"
50
+ f += " " + "─" * 80 + "\n"
51
+
52
+ # Show 3-tier stats in compact format
53
+ if npc.respond_to?(:tiers) && npc.tiers
54
+ # BODY section
55
+ body = npc.get_characteristic("BODY")
56
+ f += " BODY (#{body})\n"
57
+
58
+ # Show key BODY attributes with skills
59
+ str_attr = npc.get_attribute("BODY", "Strength")
60
+ if str_attr > 0
61
+ f += " Strength (#{str_attr})\n"
62
+ end
63
+
64
+ end_attr = npc.get_attribute("BODY", "Endurance")
65
+ if end_attr > 0
66
+ f += " Endurance (#{end_attr})\n"
67
+ end
68
+
69
+ # Melee combat skills
70
+ if npc.tiers["BODY"]["Melee Combat"]
71
+ melee_attr = npc.get_attribute("BODY", "Melee Combat")
72
+ melee_skills = npc.tiers["BODY"]["Melee Combat"]["skills"].select { |_, v| v > 0 }
73
+ if melee_skills.any?
74
+ f += " Melee Combat (#{melee_attr})\n"
75
+ melee_skills.each do |skill, value|
76
+ total = body + melee_attr + value
77
+ f += " #{skill.ljust(20)}: #{body}+#{melee_attr}+#{value} = #{total}\n"
78
+ end
79
+ end
80
+ end
81
+
82
+ # Athletics skills (only if non-zero)
83
+ athletics_attr = npc.get_attribute("BODY", "Athletics")
84
+ dodge = npc.get_skill("BODY", "Athletics", "Dodge")
85
+ if dodge > 0
86
+ f += " Athletics (#{athletics_attr})\n"
87
+ f += " Dodge: #{body}+#{athletics_attr}+#{dodge} = #{body + athletics_attr + dodge}\n"
88
+ end
89
+
90
+ # MIND section
91
+ mind = npc.get_characteristic("MIND")
92
+ f += " MIND (#{mind})\n"
93
+
94
+ # Awareness
95
+ awr_attr = npc.get_attribute("MIND", "Awareness")
96
+ if awr_attr > 0
97
+ reaction = npc.get_skill("MIND", "Awareness", "Reaction speed")
98
+ tracking = npc.get_skill("MIND", "Awareness", "Tracking")
99
+
100
+ if reaction > 0 || tracking > 0
101
+ f += " Awareness (#{awr_attr})\n"
102
+ if reaction > 0
103
+ f += " Reaction speed: #{mind}+#{awr_attr}+#{reaction} = #{mind + awr_attr + reaction}\n"
104
+ end
105
+ if tracking > 0
106
+ f += " Tracking: #{mind}+#{awr_attr}+#{tracking} = #{mind + awr_attr + tracking}\n"
107
+ end
108
+ end
109
+ end
110
+
111
+ # SPIRIT section (only if non-zero)
112
+ spirit = npc.get_characteristic("SPIRIT")
113
+ if spirit > 0
114
+ f += " SPIRIT (#{spirit})\n"
115
+
116
+ # Show casting if applicable
117
+ if npc.respond_to?(:spells) && npc.spells && npc.spells.length > 0
118
+ casting = npc.get_attribute("SPIRIT", "Casting")
119
+ f += " Casting (#{casting})\n"
120
+ f += " Spells: #{npc.spells.length} (#{npc.spells.take(3).map{|s| s['name']}.join(', ')}#{'...' if npc.spells.length > 3})\n"
121
+ end
122
+ end
123
+ end
124
+
125
+ # Derived stats
126
+ f += " " + "─" * 80 + "\n"
127
+ # Ensure all values are rounded integers (SIZE can have half values)
128
+ bp_value = npc.BP.respond_to?(:round) ? npc.BP.round : npc.BP.to_i
129
+ db_value = npc.DB.respond_to?(:round) ? npc.DB.round : npc.DB.to_i
130
+ md_value = npc.MD.respond_to?(:round) ? npc.MD.round : npc.MD.to_i
131
+ f += " SIZE: #{npc.SIZE} BP: #{bp_value} DB: #{db_value} MD: #{md_value}"
132
+ if npc.armor
133
+ f += " Armor: #{npc.armor[:name]} (AP: #{npc.armor[:ap]})"
134
+ end
135
+ f += "\n"
136
+
137
+ # Special abilities for monsters
138
+ if npc.respond_to?(:special_abilities) && npc.special_abilities
139
+ f += " Special: #{npc.special_abilities}\n"
140
+ end
141
+
142
+ f += "\n"
143
+ end
144
+ end
145
+
146
+ f += "═" * width + "\n"
147
+
148
+ # Output handling
149
+ if cli == "cli"
150
+ # Save clean version without ANSI codes for editing
151
+ File.write("saved/encounter_new.npc", f.pure, perm: 0644)
152
+ # Display version with colors
153
+ print f
154
+
155
+ if !e.is_no_encounter? && e.npcs.length > 0
156
+ # Loop to allow viewing multiple NPCs
157
+ loop do
158
+ # Option to view detailed NPC
159
+ puts "\nEnter NPC number (1-#{e.npcs.length}) to view full details, 'e' to edit, or any other key to exit"
160
+ input = STDIN.getch
161
+
162
+ if input.to_i.between?(1, e.npcs.length)
163
+ # Load the NPC output module if not loaded
164
+ unless defined?(npc_output_new)
165
+ load File.join($pgmdir, "cli_npc_output_new.rb")
166
+ end
167
+ npc_output_new(e.get_npc(input.to_i - 1), "cli")
168
+
169
+ # After viewing NPC, redisplay the encounter
170
+ system("clear") || system("cls")
171
+ print f
172
+ elsif input == "e"
173
+ # Use vim with settings to avoid binary file warnings
174
+ if $editor.include?("vim") || $editor.include?("vi")
175
+ system("#{$editor} -c 'set fileformat=unix' saved/encounter_new.npc")
176
+ else
177
+ system("#{$editor} saved/encounter_new.npc")
178
+ end
179
+ # Redisplay after editing
180
+ system("clear") || system("cls")
181
+ print f
182
+ else
183
+ # Exit the loop on any other key
184
+ break
185
+ end
186
+ end
187
+ end
188
+ else
189
+ return f
190
+ end
191
+ end
192
+
193
+ # Keep the old compact format as an alternative
194
+ def enc_output_new_compact(e, cli)
195
+ # This would be the current enc_output_new function
196
+ # Keeping it for quick combat reference
197
+ enc_output_new(e, cli)
198
+ end
@@ -0,0 +1,238 @@
1
+ # Output module for new encounter system
2
+ require 'io/console'
3
+
4
+ def enc_output_new(e, cli)
5
+ f = ""
6
+
7
+ # Header with box characters
8
+ width = 120
9
+ f += "═" * width + "\n"
10
+
11
+ # Day/Night and Terrain
12
+ day_str = $Day == 1 ? "Day: " : "Night: "
13
+ terrain_str = case $Terrain
14
+ when 0 then "City "
15
+ when 1 then "Rural "
16
+ when 2 then "Road "
17
+ when 3 then "Plains "
18
+ when 4 then "Hills "
19
+ when 5 then "Mountains "
20
+ when 6 then "Woods "
21
+ when 7 then "Wilderness"
22
+ end
23
+
24
+ level_mod_str = "(Level mod = #{$Level || 0})"
25
+ date_str = "Created: #{Date.today.to_s}"
26
+
27
+ f += "#{day_str}#{terrain_str}#{level_mod_str}#{date_str.rjust(width - day_str.length - terrain_str.length - level_mod_str.length)}\n"
28
+ f += "─" * width + "\n"
29
+
30
+ # Check for no encounter
31
+ if e.is_no_encounter?
32
+ f += "\nNO ENCOUNTER\n\n"
33
+ f += "═" * width + "\n"
34
+ else
35
+ # Show attitude and summary
36
+ f += "#{e.enc_attitude}: #{e.summary}\n"
37
+ f += "─" * width + "\n\n"
38
+
39
+ # List all NPCs in the encounter
40
+ e.encounter.each_with_index do |enc_data, index|
41
+ break if enc_data["string"] =~ /Event:/
42
+
43
+ npc = enc_data["npc"]
44
+ next unless npc
45
+
46
+ # Format: Name (Sex, actual name) [Lvl X]
47
+ name_str = npc.name.empty? ? enc_data["string"] : npc.name
48
+ f += " #{name_str} (#{npc.sex}, #{npc.name}) [Lvl #{npc.level}]\n"
49
+ f += " ".ljust(15)
50
+
51
+ # Show key stats in compact format like old system
52
+ # SIZE, Strength, Endurance, Awareness, Magic capability, Reaction Speed, Dodge
53
+ body = npc.get_characteristic("BODY")
54
+ mind = npc.get_characteristic("MIND")
55
+ spirit = npc.get_characteristic("SPIRIT")
56
+
57
+ str_attr = npc.get_attribute("BODY", "Strength")
58
+ end_attr = npc.get_attribute("BODY", "Endurance")
59
+ awr_attr = npc.get_attribute("MIND", "Awareness")
60
+ reaction_skill = npc.get_skill("MIND", "Awareness", "Reaction speed")
61
+ dodge_skill = npc.get_skill("BODY", "Athletics", "Dodge")
62
+
63
+ str_total = body + str_attr
64
+ end_total = body + end_attr
65
+ awr_total = mind + awr_attr
66
+ mag_total = spirit
67
+ rs_total = mind + awr_attr + reaction_skill
68
+ dodge_total = body + npc.get_attribute("BODY", "Athletics") + dodge_skill
69
+
70
+ # Calculate ENC penalty
71
+ strength = npc.get_attribute("BODY", "Strength") + body
72
+ base_capacity = strength * 5
73
+ total_weight = npc.ENC || 0
74
+
75
+ enc_penalty = if total_weight <= base_capacity
76
+ 0
77
+ elsif total_weight <= base_capacity * 2
78
+ -1
79
+ elsif total_weight <= base_capacity * 5
80
+ -3
81
+ elsif total_weight <= base_capacity * 10
82
+ -5
83
+ else
84
+ -7
85
+ end
86
+
87
+ f += " SIZ=#{npc.SIZE}"
88
+ f += " STR=#{str_total}"
89
+ f += " END=#{end_total}"
90
+ f += " AWR=#{awr_total}"
91
+ f += " MAG=#{mag_total}"
92
+ f += " RS=#{rs_total}"
93
+ f += " Ddg=#{dodge_total}"
94
+ f += " (S:#{enc_penalty})\n"
95
+
96
+ # Show magic lore if applicable
97
+ if npc.spells && npc.spells.length > 0
98
+ domain = npc.spells.first['domain'] if npc.spells.first
99
+ casting = npc.get_attribute("SPIRIT", "Casting")
100
+ f += "\n".ljust(17) + "#{domain || 'Magic'} Lore=#{spirit + casting}"
101
+ f += ", # of spells: #{npc.spells.length}\n"
102
+ end
103
+
104
+ # Show best weapon
105
+ melee_skills = npc.tiers["BODY"]["Melee Combat"]["skills"].select { |_, v| v > 0 }
106
+ if melee_skills.any?
107
+ best_melee = melee_skills.max_by { |_, v| v }
108
+ if best_melee
109
+ wpn_name = best_melee[0]
110
+ wpn_skill = body + npc.get_attribute("BODY", "Melee Combat") + best_melee[1]
111
+ wpn_stats = get_weapon_stats(wpn_name)
112
+ wpn_ini = rs_total + (wpn_stats[:ini] || 0)
113
+
114
+ f += " " + wpn_name.ljust(14) + "Skill=" + wpn_skill.to_s.rjust(2)
115
+ f += ", Ini:" + wpn_ini.to_s
116
+ f += ", Off:" + (wpn_skill + wpn_stats[:off]).to_s.rjust(2)
117
+ f += ", Def:" + (wpn_skill + wpn_stats[:def]).to_s.rjust(2)
118
+ f += ", Dam:" + (npc.DB + wpn_stats[:dmg].to_i).to_s.rjust(2)
119
+ f += " AP:" + (npc.armor ? npc.armor[:ap] : 0).to_s
120
+ f += ", BP:" + npc.BP.to_s + "\n"
121
+ end
122
+ end
123
+
124
+ # Show best missile weapon if any
125
+ missile_skills = npc.tiers["BODY"]["Missile Combat"]["skills"].select { |_, v| v > 0 }
126
+ if missile_skills.any?
127
+ best_missile = missile_skills.max_by { |_, v| v }
128
+ if best_missile
129
+ msl_name = best_missile[0]
130
+ msl_skill = body + npc.get_attribute("BODY", "Missile Combat") + best_missile[1]
131
+ msl_stats = get_missile_stats(msl_name)
132
+
133
+ f += " " + msl_name.ljust(14) + "Skill=" + msl_skill.to_s.rjust(2)
134
+ f += ", SR:" + (msl_stats[:sr] || "1").to_s
135
+ f += ", Off:" + msl_skill.to_s.rjust(2)
136
+ f += ", Dam:" + (npc.DB + msl_stats[:dmg].to_i).to_s.rjust(2)
137
+ f += ", Rng:" + msl_stats[:range] + "\n"
138
+ end
139
+ end
140
+
141
+ f += "\n"
142
+ end
143
+ end
144
+
145
+ f += "═" * width + "\n"
146
+
147
+ # Output handling
148
+ if cli == "cli"
149
+ # Save clean version without ANSI codes for editing
150
+ File.write("saved/encounter_new.npc", f.pure, perm: 0644)
151
+ # Display version with colors
152
+ print f
153
+
154
+ if !e.is_no_encounter? && e.npcs.length > 0
155
+ # Loop to allow viewing multiple NPCs
156
+ loop do
157
+ # Option to view detailed NPC
158
+ puts "\nEnter NPC number (1-#{e.npcs.length}) to view details, 'e' to edit, or any other key to exit"
159
+ input = STDIN.getch
160
+
161
+ if input.to_i.between?(1, e.npcs.length)
162
+ npc_output_new(e.get_npc(input.to_i - 1), "cli")
163
+
164
+ # After viewing NPC, redisplay the encounter
165
+ system("clear") || system("cls")
166
+ print f
167
+ elsif input == "e"
168
+ # Use vim with settings to avoid binary file warnings
169
+ if $editor.include?("vim") || $editor.include?("vi")
170
+ system("#{$editor} -c 'set fileformat=unix' saved/encounter_new.npc")
171
+ else
172
+ system("#{$editor} saved/encounter_new.npc")
173
+ end
174
+ # Redisplay after editing
175
+ system("clear") || system("cls")
176
+ print f
177
+ else
178
+ # Exit the loop on any other key
179
+ break
180
+ end
181
+ end
182
+ end
183
+ else
184
+ return f
185
+ end
186
+ end
187
+
188
+ def terrain_name(terrain_type)
189
+ case terrain_type
190
+ when 0..2 then "Rural/Civilized"
191
+ when 3..5 then "Wilderness"
192
+ when 6..8 then "Dangerous Lands"
193
+ else "Unknown"
194
+ end
195
+ end
196
+
197
+ # Get weapon stats from tables
198
+ def get_weapon_stats(weapon)
199
+ # Default values for weapons based on name patterns
200
+ case weapon.downcase
201
+ when /sword/
202
+ { off: 0, def: 0, dmg: 1, ini: 0 }
203
+ when /dagger/, /knife/
204
+ { off: 1, def: -1, dmg: -1, ini: 1 }
205
+ when /axe/
206
+ { off: 0, def: -1, dmg: 2, ini: -1 }
207
+ when /spear/, /pike/
208
+ { off: 0, def: 1, dmg: 0, ini: 0 }
209
+ when /mace/, /club/, /hammer/
210
+ { off: -1, def: 0, dmg: 1, ini: -1 }
211
+ when /staff/, /quarterstaff/
212
+ { off: 0, def: 2, dmg: -1, ini: 0 }
213
+ when /unarmed/
214
+ { off: -2, def: -4, dmg: -4, ini: 2 }
215
+ else
216
+ { off: 0, def: 0, dmg: 0, ini: 0 }
217
+ end
218
+ end
219
+
220
+ def get_missile_stats(weapon)
221
+ # Default values for missile weapons
222
+ case weapon.downcase
223
+ when /longbow/
224
+ { range: "150m", dmg: 1, sr: "1" }
225
+ when /bow/
226
+ { range: "100m", dmg: 0, sr: "1" }
227
+ when /x-bow/, /crossbow/
228
+ { range: "150m", dmg: 2, sr: "1/2" }
229
+ when /sling/
230
+ { range: "50m", dmg: -1, sr: "1" }
231
+ when /throwing/, /javelin/
232
+ { range: "30m", dmg: 0, sr: "1" }
233
+ when /blowgun/
234
+ { range: "20m", dmg: -5, sr: "2" }
235
+ else
236
+ { range: "30m", dmg: 0, sr: "1" }
237
+ end
238
+ end
data/cli_name_gen.rb ADDED
@@ -0,0 +1,21 @@
1
+ # Random name generator input module for Amar Tools
2
+
3
+ def name_gen
4
+
5
+ prompt = TTY::Prompt.new
6
+
7
+ # Get name type
8
+ $Names.each_with_index do |value,index|
9
+ puts index.to_s.rjust(2).c(@N) + ": ".c(@N) + value[0].c(@N)
10
+ end
11
+ c = prompt.ask("\nEnter name type:".c(@N)).to_i
12
+ c = 0 if c < 0 or c > $Names.length
13
+
14
+ puts $Names[c][0].c(@N) + " names:\n".c(@N)
15
+
16
+ # Output 10 names of the selected type
17
+ 10.times do
18
+ puts naming($Names[c][0]).c(@N)
19
+ end
20
+
21
+ end