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
data/cli_enc_output.rb ADDED
@@ -0,0 +1,87 @@
1
+ # The CLI encounter output module for Amar Tools
2
+
3
+ def enc_output(anENC, cli)
4
+
5
+ e = anENC.encounter
6
+
7
+ # Start creating the output text
8
+ f = "############################<By Amar Tools>############################\n"
9
+
10
+ $Day == 1 ? f += "Day: " : f += "Night: "
11
+ case $Terrain
12
+ when 0
13
+ f += "City "
14
+ when 1
15
+ f += "Rural "
16
+ when 2
17
+ f += "Road "
18
+ when 3
19
+ f += "Plains "
20
+ when 4
21
+ f += "Hills "
22
+ when 5
23
+ f += "Mountains "
24
+ when 6
25
+ f += "Woods "
26
+ when 7
27
+ f += "Wilderness"
28
+ end
29
+
30
+ f += " (Level mod = " + $Level.to_s + ")"
31
+ f += "Created: #{Date.today.to_s}".rjust(38) + "\n\n"
32
+
33
+ if e[0]["string"] == "NO ENCOUNTER"
34
+ f += "\nNO ENCOUNTER\n\n"
35
+ else
36
+ f += anENC.enc_attitude
37
+ f += ":\n"
38
+ anENC.enc_number.times do |i|
39
+ f += " "
40
+ if e[i]["string"] =~ /animal/
41
+ e[i]["string"] += " (" + e[i]["sex"] + ")"
42
+ else
43
+ e[i]["string"] += " (#{e[i]["sex"]}, #{e[i]["name"]})" unless e[i]["string"] =~ /Event/
44
+ end
45
+ f += e[i]["string"]
46
+ if e[i]["string"] =~ /Event:/
47
+ f += "\n\n"
48
+ break
49
+ else
50
+ f += " [Lvl " + e[i]["level"].to_s + "]\n"
51
+ f += "".ljust(15)
52
+ f += " SIZ=" + e[i]["size"].to_s
53
+ f += " STR=" + e[i]["strength"].to_s
54
+ f += " END=" + e[i]["endurance"].to_s
55
+ f += " AWR=" + e[i]["awareness"].to_s
56
+ f += " MAG=" + e[i]["mag"].to_s
57
+ f += " RS=" + e[i]["reaction"].to_s
58
+ f += " Ddg=" + e[i]["dodge"].to_s
59
+ f += " (S:" + e[i]["status"].to_s + ")"
60
+ if e[i]["mag_lore"]
61
+ f += "\n".ljust(17) + e[i]["mag_type"] + " Lore=" + e[i]["mag_lore"].to_s
62
+ f += ", # of spells: " + e[i]["spells"].to_s
63
+ end
64
+ f += "\n"
65
+
66
+ f += " " + e[i]["wpn_name"].ljust(14) + "Skill=" + e[i]["wpn_skill"].to_s.rjust(2) + ", Ini:" + e[i]["wpn_ini"].to_s
67
+ f += ", Off:" + e[i]["wpn_off"].to_s.rjust(2) + ", Def:" + e[i]["wpn_def"].to_s.rjust(2) + ", Dam:" + e[i]["wpn_dam"].to_s.rjust(2)
68
+ f += " AP:" + e[i]["ap"].to_s + ", BP:" + e[i]["bp"].to_s + "\n"
69
+
70
+ if e[i]["msl_name"]
71
+ f += " " + e[i]["msl_name"].ljust(14) + "Skill=" + e[i]["msl_skill"].to_s.rjust(2) + ", Ini:" + e[i]["msl_ini"].to_s
72
+ f += ", Off:" + e[i]["msl_off"].to_s.rjust(2) + ", Dam:" + e[i]["msl_dam"].to_s.rjust(2) + ", Rng:" + e[i]["msl_rng"].to_s + "\n"
73
+ end
74
+ end
75
+
76
+ f += "\n"
77
+ f += "\n" if e[0]["string"] =~ /Event/
78
+ end
79
+ end
80
+
81
+ f += "#######################################################################\n\n"
82
+
83
+ # Save text in file
84
+ save_temp_file(f, "encounter", cli)
85
+ system("#{$editor} saved/encounter.npc") if cli == "cli"
86
+
87
+ end
@@ -0,0 +1,433 @@
1
+ # Encounter output module showing full 3-tier system format
2
+ require 'io/console'
3
+
4
+ # Try to load string_extensions for color support
5
+ begin
6
+ require 'string_extensions'
7
+ rescue LoadError
8
+ # Define a fallback pure method if string_extensions is not available
9
+ class String
10
+ def pure
11
+ self.gsub(/\e\[[0-9;]*m/, '')
12
+ end
13
+ end
14
+ end
15
+
16
+ def enc_output_new(e, cli, custom_width = nil)
17
+ # Clear screen before output if direct CLI mode
18
+ if cli == "cli_direct"
19
+ system("clear") || system("cls")
20
+ end
21
+
22
+ f = ""
23
+
24
+ # Get terminal width or use default
25
+ if custom_width
26
+ width = custom_width
27
+ elsif cli == "cli_direct"
28
+ width = `tput cols`.to_i rescue 120
29
+ width = 120 if width < 120 # Minimum width
30
+ elsif cli == "cli"
31
+ # For TUI, use a narrower width that fits in the content pane
32
+ width = 80
33
+ else
34
+ width = 80
35
+ end
36
+
37
+ # Define colors if terminal output
38
+ if cli == "cli"
39
+ # Colors for different elements
40
+ @header_color = "\e[1;36m" # Bright cyan
41
+ @char_color = "\e[1;33m" # Bright yellow
42
+ @attr_color = "\e[1;35m" # Bright magenta
43
+ @skill_color = "\e[0;37m" # White
44
+ @stat_color = "\e[1;32m" # Bright green
45
+ @special_color = "\e[38;5;202m" # Orange for special abilities
46
+ @name_color = "\e[38;5;111m" # Light blue for names
47
+ @gray = "\e[38;5;240m" # Gray for explanatory text
48
+ @reset = "\e[0m"
49
+ else
50
+ @header_color = @char_color = @attr_color = @skill_color = ""
51
+ @stat_color = @special_color = @name_color = @gray = @reset = ""
52
+ end
53
+
54
+ # Day/Night and Terrain
55
+ day_str = $Day == 1 ? "Day: " : "Night: "
56
+ terrain_str = case $Terrain
57
+ when 0 then "City "
58
+ when 1 then "Rural "
59
+ when 2 then "Road "
60
+ when 3 then "Plains "
61
+ when 4 then "Hills "
62
+ when 5 then "Mountains "
63
+ when 6 then "Woods "
64
+ when 7 then "Wilderness"
65
+ end
66
+
67
+ level_mod_str = "(Level mod = #{$Level || 0})"
68
+ require 'date'
69
+ date_str = "Created: #{Date.today.to_s}"
70
+
71
+ f += "#{day_str}#{terrain_str}#{level_mod_str}#{date_str.rjust(width - day_str.length - terrain_str.length - level_mod_str.length)}\n"
72
+ f += "─" * width + "\n"
73
+
74
+ # Check for no encounter
75
+ if e.is_no_encounter?
76
+ f += "\n#{@desc_color}NO ENCOUNTER#{@reset}\n\n"
77
+ else
78
+ # Show attitude and summary with color based on attitude
79
+ attitude_color = case e.enc_attitude
80
+ when "HOSTILE" then "\e[1;31m" # Red
81
+ when "ANTAGONISTIC" then "\e[38;5;208m" # Orange
82
+ when "NEUTRAL" then "\e[1;37m" # White
83
+ when "POSITIVE" then "\e[1;32m" # Green
84
+ when "FRIENDLY" then "\e[1;36m" # Cyan
85
+ else "\e[0;37m"
86
+ end if cli == "cli"
87
+ attitude_color ||= ""
88
+
89
+ # Use color 229 for the encounter line
90
+ f += "\e[38;5;229m#{e.enc_attitude}: #{e.summary}#{@reset}\n" if cli == "cli"
91
+ f += "#{e.enc_attitude}: #{e.summary}\n" unless cli == "cli"
92
+ f += "─" * width + "\n"
93
+
94
+ # List all NPCs in the encounter with 3-tier format
95
+ e.encounter.each_with_index do |enc_data, index|
96
+ break if enc_data["string"] =~ /Event:/
97
+
98
+ npc = enc_data["npc"]
99
+ next unless npc
100
+
101
+ # NPC header with color
102
+ name_str = npc.name.empty? ? enc_data["string"] : npc.name
103
+ type_str = npc.respond_to?(:type) ? npc.type : "Unknown"
104
+ age_str = npc.respond_to?(:age) ? npc.age : "Unknown"
105
+ f += "\n#{@name_color}#{index + 1}. #{name_str} (#{npc.sex}, #{age_str}) - #{type_str} [Level #{npc.level}]#{@reset}\n"
106
+ f += " " + "─" * (width - 10) + "\n"
107
+
108
+ # Show 3-tier stats in reorganized format
109
+ if npc.respond_to?(:tiers) && npc.tiers
110
+ body = npc.get_characteristic("BODY")
111
+ mind = npc.get_characteristic("MIND")
112
+ spirit = npc.get_characteristic("SPIRIT")
113
+
114
+ # Get all key stats
115
+ str_attr = npc.get_attribute("BODY", "Strength")
116
+ end_attr = npc.get_attribute("BODY", "Endurance")
117
+ awareness_attr = npc.get_attribute("MIND", "Awareness")
118
+ reaction_speed = mind + awareness_attr + npc.get_skill("MIND", "Awareness", "Reaction speed")
119
+
120
+ # Calculate derived values
121
+ bp_value = npc.BP.respond_to?(:round) ? npc.BP.round : npc.BP.to_i
122
+ db_value = npc.DB.respond_to?(:round) ? npc.DB.round : npc.DB.to_i
123
+ md_value = npc.MD.respond_to?(:round) ? npc.MD.round : npc.MD.to_i
124
+
125
+ # Line 1: SIZE, Characteristics and key attributes
126
+ # Format SIZE for display
127
+ size_display = npc.SIZE % 1 == 0.5 ? "#{npc.SIZE.floor}½" : npc.SIZE.to_s
128
+ f += " SIZE:#{size_display} BODY:#{body} Str:#{body + str_attr} End:#{body + end_attr}"
129
+ f += " | MIND:#{mind} Awr:#{mind + awareness_attr} RS:#{reaction_speed}"
130
+ if spirit > 0
131
+ f += " | SPIRIT:#{spirit}"
132
+ end
133
+ f += "\n"
134
+
135
+ # Line 2: Derived stats and armor with combat totals (highlight BP/DB/MD/Reaction/Dodge)
136
+ dodge_total = npc.get_skill_total("BODY", "Athletics", "Dodge") rescue 0
137
+ reaction_total = npc.get_skill_total("BODY", "Athletics", "Reaction Speed") rescue 0
138
+
139
+ f += " #{@skill_color}BP:#{@reset}#{@stat_color}#{bp_value}#{@reset} #{@skill_color}DB:#{@reset}#{@stat_color}#{db_value}#{@reset} #{@skill_color}MD:#{@reset}#{@stat_color}#{md_value}#{@reset} #{@skill_color}Reaction:#{@reset}#{@stat_color}#{reaction_total}#{@reset} #{@skill_color}Dodge:#{@reset}#{@stat_color}#{dodge_total}#{@reset}"
140
+ if npc.armor
141
+ f += " Armor:#{npc.armor[:name]}(#{npc.armor[:ap]})"
142
+ end
143
+
144
+ # Calculate dodge bonus for defense
145
+ athletics_attr = npc.get_attribute("BODY", "Athletics")
146
+ dodge = npc.get_skill("BODY", "Athletics", "Dodge")
147
+ dodge_total = body + athletics_attr + dodge
148
+ dodge_bonus = (dodge_total / 5).to_i
149
+
150
+ # Line 3: Skills and spells
151
+ hide = npc.get_skill("BODY", "Athletics", "Hide")
152
+ move_quietly = npc.get_skill("BODY", "Athletics", "Move Quietly")
153
+ alertness = npc.get_skill("MIND", "Awareness", "Alertness")
154
+
155
+ hide_total = body + athletics_attr + hide
156
+ move_total = body + athletics_attr + move_quietly
157
+ alertness_total = mind + awareness_attr + alertness
158
+
159
+ f += " Skills: #{@skill_color}Dodge:#{@reset}#{@stat_color}#{dodge_total}#{@reset} #{@skill_color}Hide:#{@reset}#{@stat_color}#{hide_total}#{@reset} #{@skill_color}MoveQ:#{@reset}#{@stat_color}#{move_total}#{@reset} #{@skill_color}Alert:#{@reset}#{@stat_color}#{alertness_total}#{@reset}"
160
+
161
+ # Add tracking if non-zero
162
+ tracking = npc.get_skill("MIND", "Awareness", "Tracking")
163
+ if tracking > 0
164
+ tracking_total = mind + awareness_attr + tracking
165
+ f += " #{@skill_color}Track:#{@reset}#{@stat_color}#{tracking_total}#{@reset}"
166
+ end
167
+
168
+ # Show spells if any
169
+ if npc.respond_to?(:spells) && npc.spells && npc.spells.length > 0
170
+ spell_names = npc.spells.take(2).map{|s| s['name']}.join(', ')
171
+ f += " Spells(#{npc.spells.length}): #{spell_names}#{'...' if npc.spells.length > 2}"
172
+ end
173
+ f += "\n"
174
+
175
+ # Line 4 & 5: Weapons (melee and missile on separate lines)
176
+ melee_weapons = []
177
+ missile_weapons = []
178
+
179
+ # Melee combat - use ORIGINAL weapon names with NEW skill calculations
180
+ if npc.respond_to?(:melee1) && npc.melee1 && !npc.melee1.strip.empty?
181
+ weapon_name = npc.melee1.strip
182
+ skill = npc.melee1s || 0
183
+ ini = npc.melee1i || 0
184
+ off = npc.melee1o || 0
185
+ def_val = npc.melee1d || 0
186
+ dmg = npc.melee1dam || 0
187
+
188
+ melee_weapons << "#{weapon_name} (#{skill}) I:#{ini} #{@stat_color}O:#{off}#{@reset} #{@stat_color}D:#{def_val}#{@reset} #{@stat_color}d:#{dmg}#{@reset}"
189
+ end
190
+
191
+ if npc.respond_to?(:melee2) && npc.melee2 && !npc.melee2.strip.empty?
192
+ weapon_name = npc.melee2.strip
193
+ skill = npc.melee2s || 0
194
+ ini = npc.melee2i || 0
195
+ off = npc.melee2o || 0
196
+ def_val = npc.melee2d || 0
197
+ dmg = npc.melee2dam || 0
198
+
199
+ melee_weapons << "#{weapon_name} (#{skill}) I:#{ini} #{@stat_color}O:#{off}#{@reset} #{@stat_color}D:#{def_val}#{@reset} #{@stat_color}d:#{dmg}#{@reset}"
200
+ end
201
+
202
+ # Missile combat - use ORIGINAL weapon names
203
+ if npc.respond_to?(:missile) && npc.missile && !npc.missile.strip.empty?
204
+ weapon_name = npc.missile.strip
205
+ skill = npc.missiles || 0
206
+ off = npc.missileo || 0
207
+ dmg = npc.missiledam || 0
208
+ range = npc.missilerange ? "#{npc.missilerange}m" : "30m"
209
+
210
+ missile_weapons << "#{weapon_name} (#{skill}) O:#{off} R:#{range} #{@stat_color}d:#{dmg}#{@reset}"
211
+ end
212
+
213
+ # Display weapons
214
+ if melee_weapons.any?
215
+ f += " Weapons: #{melee_weapons.join(' | ')}\n"
216
+ end
217
+ if missile_weapons.any?
218
+ f += " Missile: #{missile_weapons.join(' | ')}"
219
+ # Add spell lore info on same line as missile if applicable
220
+ if npc.respond_to?(:spells) && npc.spells && npc.spells.length > 0
221
+ domain = nil
222
+ domain_skill = 0
223
+
224
+ if npc.tiers["SPIRIT"] && npc.tiers["SPIRIT"]["Attunement"] && npc.tiers["SPIRIT"]["Attunement"]["skills"]
225
+ npc.tiers["SPIRIT"]["Attunement"]["skills"].each do |dom, val|
226
+ if val > domain_skill
227
+ domain = dom
228
+ domain_skill = val
229
+ end
230
+ end
231
+ end
232
+
233
+ if domain && domain_skill > 0
234
+ total_lore = spirit + npc.get_attribute("SPIRIT", "Attunement") + domain_skill
235
+ f += " | #{domain}Lore:#{total_lore}"
236
+ end
237
+ end
238
+ f += "\n"
239
+ elsif npc.respond_to?(:spells) && npc.spells && npc.spells.length > 0
240
+ # If no missile weapons but has spells, add lore info on its own line
241
+ domain = nil
242
+ domain_skill = 0
243
+
244
+ if npc.tiers["SPIRIT"] && npc.tiers["SPIRIT"]["Attunement"] && npc.tiers["SPIRIT"]["Attunement"]["skills"]
245
+ npc.tiers["SPIRIT"]["Attunement"]["skills"].each do |dom, val|
246
+ if val > domain_skill
247
+ domain = dom
248
+ domain_skill = val
249
+ end
250
+ end
251
+ end
252
+
253
+ if domain && domain_skill > 0
254
+ total_lore = spirit + npc.get_attribute("SPIRIT", "Attunement") + domain_skill
255
+ f += " MagicLore: #{domain}Lore:#{total_lore}\n"
256
+ end
257
+ end
258
+ end
259
+
260
+ # Equipment and money for humanoids (not monsters)
261
+ if npc.respond_to?(:type) && !npc.type.to_s.match(/Monster:|Animal:|monster/i)
262
+ # Load equipment generation if not loaded
263
+ unless defined?(generate_npc_equipment)
264
+ load File.join($pgmdir, "includes/equipment_tables.rb")
265
+ end
266
+
267
+ # Generate equipment and money
268
+ equipment = generate_npc_equipment(npc.type, npc.level)
269
+
270
+ # Determine social status for money calculation
271
+ social_status = case npc.type
272
+ when /Noble/ then "N"
273
+ when /Merchant/ then "UC"
274
+ when /Priest/ then "MC"
275
+ when /Smith/, /Bard/ then "LMC"
276
+ else "LC"
277
+ end
278
+
279
+ money = generate_money(social_status, npc.level)
280
+ money_value = money.split(' ').first.to_i
281
+
282
+ # Compact money format
283
+ money_str = ""
284
+ if money_value >= 100
285
+ gp = money_value / 100
286
+ money_str += "#{gp}g"
287
+ end
288
+ if money_value >= 10
289
+ sp = (money_value % 100) / 10
290
+ money_str += "#{sp}s" if sp > 0
291
+ end
292
+ cp = money_value % 10
293
+ money_str += "#{cp}c" if cp > 0
294
+
295
+ # Shorten equipment list
296
+ equip_short = equipment.take(3).join(', ')
297
+ equip_short += "..." if equipment.length > 3
298
+
299
+ f += " #{@stat_color}Equip: #{equip_short} | $: #{money_str}#{@reset}\n"
300
+ end
301
+
302
+ # Special abilities for monsters (skip generic/unknown text)
303
+ if npc.respond_to?(:special_abilities) && npc.special_abilities &&
304
+ !npc.special_abilities.to_s.downcase.include?("unknown")
305
+ f += " Special: #{npc.special_abilities}\n"
306
+ end
307
+ end
308
+ end
309
+
310
+ f += "\n" + ("═" * width) + "\n"
311
+
312
+ # Output handling
313
+ if cli == "cli_direct"
314
+ # Direct CLI mode - save and print
315
+ # Save clean version without ANSI codes for editing
316
+ File.write("saved/encounter_new.npc", f.pure, perm: 0644)
317
+ # Display version with colors
318
+ print f
319
+
320
+ if !e.is_no_encounter? && e.npcs.length > 0
321
+ # Loop to allow viewing multiple NPCs
322
+ loop do
323
+ # Option to view detailed NPC
324
+ puts "\nEnter NPC number (1-#{e.npcs.length}) for details, 'e' to edit, or press Enter to exit"
325
+ input = STDIN.gets.chomp
326
+
327
+ if input.match?(/^\d+$/) && input.to_i.between?(1, e.npcs.length)
328
+ # Load the NPC output module if not loaded
329
+ unless defined?(npc_output_new)
330
+ load File.join($pgmdir, "cli_npc_output_new.rb")
331
+ end
332
+ npc_output_new(e.get_npc(input.to_i - 1), "cli_direct")
333
+
334
+ # After viewing NPC, redisplay the encounter
335
+ system("clear") || system("cls")
336
+ print f
337
+ elsif input == "e"
338
+ # Use vim with settings to avoid binary file warnings
339
+ if $editor.include?("vim") || $editor.include?("vi")
340
+ system("#{$editor} -c 'set fileformat=unix' saved/encounter_new.npc")
341
+ else
342
+ system("#{$editor} saved/encounter_new.npc")
343
+ end
344
+ # Redisplay after editing
345
+ system("clear") || system("cls")
346
+ print f
347
+ else
348
+ # Exit the loop on any other key
349
+ break
350
+ end
351
+ end
352
+ end
353
+
354
+ return f
355
+ elsif cli == "cli"
356
+ # TUI mode - just return the colored string without printing
357
+ return f
358
+ else
359
+ # Plain mode - return without colors
360
+ return f
361
+ end
362
+ end
363
+
364
+ # Get weapon stats from tables
365
+ def get_weapon_stats(weapon)
366
+ # Default values for weapons based on name patterns
367
+ case weapon.downcase
368
+ when /sword/
369
+ { off: 0, def: 0, dmg: 1, ini: 0 }
370
+ when /dagger/, /knife/
371
+ { off: 1, def: -1, dmg: -1, ini: 1 }
372
+ when /axe/
373
+ { off: 0, def: -1, dmg: 2, ini: -1 }
374
+ when /spear/, /pike/
375
+ { off: 0, def: 1, dmg: 0, ini: 0 }
376
+ when /mace/, /club/, /hammer/
377
+ { off: -1, def: 0, dmg: 1, ini: -1 }
378
+ when /staff/, /quarterstaff/
379
+ { off: 0, def: 2, dmg: -1, ini: 0 }
380
+ when /natural/, /claw/, /bite/, /tail/, /tusk/
381
+ { off: 0, def: -2, dmg: 0, ini: 1 }
382
+ when /unarmed/
383
+ { off: -2, def: -4, dmg: -4, ini: 2 }
384
+ when /grappl/
385
+ { off: 0, def: 0, dmg: -2, ini: 0 }
386
+ else
387
+ { off: 0, def: 0, dmg: 0, ini: 0 }
388
+ end
389
+ end
390
+
391
+ def get_missile_stats(weapon)
392
+ # Default values for missile weapons
393
+ case weapon.downcase
394
+ when /longbow/
395
+ { range: "150m", dmg: 1, sr: "1" }
396
+ when /bow/
397
+ { range: "100m", dmg: 0, sr: "1" }
398
+ when /x-bow/, /crossbow/
399
+ { range: "150m", dmg: 2, sr: "1/2" }
400
+ when /sling/
401
+ { range: "50m", dmg: -1, sr: "1" }
402
+ when /throwing/, /javelin/
403
+ { range: "30m", dmg: 0, sr: "1" }
404
+ when /blowgun/
405
+ { range: "20m", dmg: -5, sr: "2" }
406
+ else
407
+ { range: "30m", dmg: 0, sr: "1" }
408
+ end
409
+ end
410
+
411
+ # Missing function from npc_output_new.rb
412
+ def generate_money(status, level)
413
+ base = case status
414
+ when "N" then 100 * level # Noble
415
+ when "UC" then 50 * level # Upper Class
416
+ when "MC" then 30 * level # Middle Class
417
+ when "LMC" then 15 * level # Lower Middle Class
418
+ when "LC" then 8 * level # Lower Class
419
+ when "S" then 2 * level # Slave
420
+ else 10 * level
421
+ end
422
+
423
+ variance = rand(base / 2) - base / 4
424
+ total = base + variance
425
+ "#{total} silver"
426
+ end
427
+
428
+ # Keep the old compact format as an alternative
429
+ def enc_output_new_compact(e, cli)
430
+ # This would be the current enc_output_new function
431
+ # Keeping it for quick combat reference
432
+ enc_output_new(e, cli)
433
+ end