amar-tui 2.1.0 → 2.1.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f7cb9713df0714bb8a88ac5df6a747a59d38e6662b3973fec420a433bdf102e
4
- data.tar.gz: 45d1b65f5843cd246bc859e4401096eabb501e2d7afc627fa77e9fb824fac0e4
3
+ metadata.gz: 125a3a252cf7e2f0da6837a2dfb7f3a526e0f24a712183ba8acc699afc9dba1a
4
+ data.tar.gz: 0ed40220339f8bf83eb908c756c0677557bef623da788541e59ec154a437264e
5
5
  SHA512:
6
- metadata.gz: 739c135e0766e52a4fbc11aa907607ade259d460a8f8fa8ff6c88521ec92645721e7c4988db001ad904a3303ec1fc79178aefc25b49ed123a28aa96f2fa1745a
7
- data.tar.gz: cea4d789ae52ab397e5d4cff7a3ccf5856a77ed4e1415ae53d70bd4819514bab17edf2c971597c005a01d94f9e78bf56959b83e1573b24d7c65802546b4a8fbc
6
+ metadata.gz: 56bc6852d14d0434a8a5c53c19d862554c4db0b020e31e8dbc6e63f8d6ff85aaace4c0570d0663d5223304a9d450893ab7bc12ea77a3a35a3353b86fc27c899c
7
+ data.tar.gz: '014357922ee46c72392a3dee618569d2d785445cfcd6ab521083921ca68697cb771fdf6c745047bff2d5140c73d04c3e4aca36d042775e639b335f22ab6b5f06'
data/amar-tui.rb CHANGED
@@ -60,7 +60,7 @@ require 'stringio' # For suppressing output
60
60
  require 'fcntl' # For non-blocking IO
61
61
 
62
62
  # GLOBAL VARS & CONSTANTS
63
- @version = "2.1.0"
63
+ @version = "2.1.5"
64
64
  $pgmdir = File.dirname(__FILE__) # Global for includes
65
65
 
66
66
  # Debug logging
@@ -2048,17 +2048,20 @@ def npc_input_new_tui
2048
2048
  show_content(type_text)
2049
2049
  type_input = get_text_input("")
2050
2050
  return nil if type_input == :cancelled
2051
-
2051
+
2052
2052
  type = ""
2053
2053
  if type_input && type_input.to_i > 0 && type_input.to_i <= types.length
2054
2054
  type = types[type_input.to_i - 1]
2055
+ else
2056
+ # Random type selection (0 or empty input)
2057
+ type = types.sample
2055
2058
  end
2056
-
2059
+
2057
2060
  # If race is not Human and type doesn't already include race, prepend it
2058
2061
  if race != "Human" && !type.include?(":")
2059
2062
  type = "#{race}: #{type}" if !type.empty?
2060
2063
  end
2061
-
2064
+
2062
2065
  inputs << type
2063
2066
 
2064
2067
  # Level selection
@@ -94,10 +94,21 @@ def npc_output_new(n, cli, custom_width = nil)
94
94
  n.tiers["BODY"].each do |attr_name, attr_data|
95
95
  # Skip if attr_data is not a hash or doesn't have skills
96
96
  next unless attr_data.is_a?(Hash) && attr_data["skills"].is_a?(Hash)
97
-
97
+
98
98
  non_zero_skills = attr_data["skills"].select { |_, v| v > 0 }
99
+
100
+ # For Melee Combat and Missile Combat, show attribute but not individual weapon skills
101
+ if attr_name == "Melee Combat" || attr_name == "Missile Combat"
102
+ # Show attribute only if there are any skills
103
+ if non_zero_skills.any?
104
+ attr_level = attr_data["level"]
105
+ body_lines << " #{@attr_color}#{attr_name} (#{attr_level.to_s.rjust(2)})#{@reset}"
106
+ end
107
+ next # Skip individual weapon skills
108
+ end
109
+
99
110
  next if non_zero_skills.empty?
100
-
111
+
101
112
  attr_level = attr_data["level"]
102
113
  body_lines << " #{@attr_color}#{attr_name} (#{attr_level.to_s.rjust(2)})#{@reset}"
103
114
  non_zero_skills.each do |skill_name, skill_level|
@@ -274,11 +285,21 @@ def npc_output_new(n, cli, custom_width = nil)
274
285
  f += "\n"
275
286
  end
276
287
  else
277
- # 3-tier weapon display - show ALL weapon skills
288
+ # 3-tier weapon display - show ALL weapon skills with actual weapon names
278
289
  melee_weapons = n.tiers["BODY"]["Melee Combat"]["skills"].select { |_, v| v > 0 } rescue {}
279
290
  missile_weapons = n.tiers["BODY"]["Missile Combat"]["skills"].select { |_, v| v > 0 } rescue {}
291
+ actual_melee = (n.tiers["BODY"]["Melee Combat"]["actual_weapons"] rescue nil) || {}
292
+ actual_missile = (n.tiers["BODY"]["Missile Combat"]["actual_weapons"] rescue nil) || {}
280
293
 
281
294
  if melee_weapons.any? || missile_weapons.any?
295
+ # Load weapon tables for stats lookup
296
+ unless defined?($Melee)
297
+ load File.join($pgmdir, "includes/tables/melee.rb")
298
+ end
299
+ unless defined?($Missile)
300
+ load File.join($pgmdir, "includes/tables/missile.rb")
301
+ end
302
+
282
303
  f += "─" * width + "\n"
283
304
  f += "#{@weapon_color}WEAPON SKILL INI OFF DEF DAM HP RANGE#{@reset}\n"
284
305
 
@@ -292,19 +313,35 @@ def npc_output_new(n, cli, custom_width = nil)
292
313
  reaction_speed = n.get_skill_total("MIND", "Awareness", "Reaction speed") || 0
293
314
 
294
315
  # Display melee weapons
295
- melee_weapons.sort_by { |_, skill| -skill }.each do |weapon, skill|
296
- wpn_stats = get_weapon_stats(weapon)
316
+ melee_weapons.sort_by { |_, skill| -skill }.each do |weapon_skill, skill|
317
+ # Get actual weapon name
318
+ actual_weapon_name = actual_melee[weapon_skill] || weapon_skill
319
+
320
+ # Find weapon in $Melee table
321
+ weapon_data = $Melee.find { |w| w && w[0] && w[0].strip == actual_weapon_name.strip }
322
+
297
323
  attr = n.get_attribute("BODY", "Melee Combat") || 0
298
324
  skill_total = body_char + attr + skill
299
325
 
300
- init = reaction_speed + (wpn_stats[:init] || 0)
301
- off = skill_total + (wpn_stats[:off] || 0)
302
- defense = skill_total + (wpn_stats[:def] || 0) + dodge_bonus
303
- dmg_mod = wpn_stats[:dmg].to_s =~ /special/ ? 0 : (wpn_stats[:dmg].to_s.to_i || 0)
304
- dmg = (n.DB || 0) + dmg_mod
305
- hp = wpn_stats[:hp] || 0
326
+ if weapon_data
327
+ # Use actual weapon stats from table: [Name, Type, Str, Dam, Init, Off, Def, HP, Wt]
328
+ init = reaction_speed + weapon_data[4]
329
+ off = skill_total + weapon_data[5]
330
+ defense = skill_total + weapon_data[6] + dodge_bonus
331
+ dmg = (n.DB || 0) + weapon_data[3]
332
+ hp = weapon_data[7]
333
+ else
334
+ # Fallback to pattern matching
335
+ wpn_stats = get_weapon_stats(actual_weapon_name)
336
+ init = reaction_speed + (wpn_stats[:init] || 0)
337
+ off = skill_total + (wpn_stats[:off] || 0)
338
+ defense = skill_total + (wpn_stats[:def] || 0) + dodge_bonus
339
+ dmg_mod = wpn_stats[:dmg].to_s =~ /special/ ? 0 : (wpn_stats[:dmg].to_s.to_i || 0)
340
+ dmg = (n.DB || 0) + dmg_mod
341
+ hp = wpn_stats[:hp] || 0
342
+ end
306
343
 
307
- f += "#{weapon.ljust(19)}"
344
+ f += "#{actual_weapon_name.ljust(19)}"
308
345
  f += "#{skill_total.to_s.ljust(9)}"
309
346
  f += "#{init.to_s.ljust(8)}"
310
347
  f += "#{off.to_s.ljust(7)}"
@@ -315,18 +352,33 @@ def npc_output_new(n, cli, custom_width = nil)
315
352
  end
316
353
 
317
354
  # Display missile weapons
318
- missile_weapons.sort_by { |_, skill| -skill }.each do |weapon, skill|
319
- wpn_stats = get_missile_stats(weapon)
355
+ missile_weapons.sort_by { |_, skill| -skill }.each do |weapon_skill, skill|
356
+ # Get actual weapon name
357
+ actual_weapon_name = actual_missile[weapon_skill] || weapon_skill
358
+
359
+ # Find weapon in $Missile table
360
+ weapon_data = $Missile.find { |w| w && w[0] && w[0].strip == actual_weapon_name.strip }
361
+
320
362
  attr = n.get_attribute("BODY", "Missile Combat") || 0
321
363
  skill_total = body_char + attr + skill
322
364
 
323
- off = skill_total + (wpn_stats[:off] || 0)
324
- range = wpn_stats[:range] || "30m"
325
- dmg_mod = wpn_stats[:dmg].to_s =~ /special/ ? 0 : (wpn_stats[:dmg].to_s.to_i || 0)
326
- dmg = (n.DB || 0) + dmg_mod
327
- hp = wpn_stats[:hp] || 0
365
+ if weapon_data
366
+ # Use actual weapon stats from table: [Name, Type, Str, Dam, Off, Rng, Max, Init, Wt]
367
+ off = skill_total + weapon_data[4]
368
+ dmg = (n.DB || 0) + weapon_data[3]
369
+ range = "#{weapon_data[5]}m"
370
+ hp = weapon_data[7]
371
+ else
372
+ # Fallback to pattern matching
373
+ wpn_stats = get_missile_stats(actual_weapon_name)
374
+ off = skill_total + (wpn_stats[:off] || 0)
375
+ range = wpn_stats[:range] || "30m"
376
+ dmg_mod = wpn_stats[:dmg].to_s =~ /special/ ? 0 : (wpn_stats[:dmg].to_s.to_i || 0)
377
+ dmg = (n.DB || 0) + dmg_mod
378
+ hp = wpn_stats[:hp] || 0
379
+ end
328
380
 
329
- f += "#{weapon.ljust(19)}"
381
+ f += "#{actual_weapon_name.ljust(19)}"
330
382
  f += "#{skill_total.to_s.ljust(9)}"
331
383
  f += "#{' '.ljust(8)}" # No init for missile
332
384
  f += "#{off.to_s.ljust(7)}"
@@ -18,7 +18,7 @@ class NpcNew
18
18
  # Generate random values for missing data
19
19
  @name = name && !name.empty? ? name : generate_random_name(sex)
20
20
  @type = type
21
- @level = level.to_i
21
+ @level = level.to_i > 0 ? level.to_i : rand(1..6) # Random level 1-6 if 0 or invalid
22
22
  @area = area && !area.empty? ? area : ["Amaronir", "Merisir", "Calaronir", "Feronir", "Rauinir"].sample
23
23
  @sex = sex && !sex.empty? ? sex : ["M", "F"].sample
24
24
 
@@ -460,14 +460,94 @@ class NpcNew
460
460
  level
461
461
  end
462
462
 
463
+ def select_actual_weapon_from_table(skill_name, strength_char, is_missile = false)
464
+ # Select actual weapon from $Melee or $Missile table based on skill and strength
465
+ # This mimics the original weapon selection system
466
+ unless defined?($Melee)
467
+ load File.join($pgmdir, "includes/tables/melee.rb")
468
+ end
469
+ unless defined?($Missile)
470
+ load File.join($pgmdir, "includes/tables/missile.rb")
471
+ end
472
+
473
+ if is_missile
474
+ # Select from $Missile table based on skill type
475
+ case skill_name.downcase
476
+ when /bow/
477
+ # Select bow based on strength: L(2), M(4), H(6), H2(8), H3(10)
478
+ if strength_char >= 10
479
+ "Bow(H3) [1]"
480
+ elsif strength_char >= 8
481
+ "Bow(H2) [1]"
482
+ elsif strength_char >= 6
483
+ "Bow(H) [1]"
484
+ elsif strength_char >= 4
485
+ "Bow(M) [1]"
486
+ else
487
+ "Bow(L) [1]"
488
+ end
489
+ when /crossbow|x-bow/
490
+ if strength_char >= 4
491
+ "X-bow(H) [¼]"
492
+ elsif strength_char >= 3
493
+ "X-bow(M) [⅓]"
494
+ else
495
+ "X-bow(L) [½]"
496
+ end
497
+ when /throwing/
498
+ "Th Knife [2]"
499
+ when /javelin/
500
+ "Javelin [1]"
501
+ when /sling/
502
+ "Sling [1]"
503
+ when /net/
504
+ "Net"
505
+ when /spear/
506
+ "Javelin [1]"
507
+ else
508
+ "Rock [2]"
509
+ end
510
+ else
511
+ # Select from $Melee table based on Wield Weapon total (replaces old STRENGTH)
512
+ # This is BODY + Strength + Wield weapon skill
513
+ # Determine weapon level range based on Wield Weapon total
514
+ wpn_level = case strength_char
515
+ when 0..1 then 2
516
+ when 2..3 then 4
517
+ when 4..6 then 11
518
+ when 7..9 then 18
519
+ when 10..12 then 22
520
+ when 13..15 then 26
521
+ when 16..18 then 28
522
+ else 30
523
+ end
524
+
525
+ # Select random weapon from available range
526
+ weapon_idx = rand(wpn_level) + 1
527
+ weapon_data = $Melee[weapon_idx]
528
+
529
+ if weapon_data
530
+ weapon_data[0].strip # Return weapon name like "Longsword/Buc"
531
+ else
532
+ skill_name # Fallback to skill name
533
+ end
534
+ end
535
+ end
536
+
463
537
  def add_weapon_skills(template)
464
538
  # Add melee weapon skills with primary weapon specialization
539
+ # Also store actual weapon selections from $Melee/$Missile tables
465
540
  if template["melee_weapons"]
466
541
  @tiers["BODY"]["Melee Combat"]["skills"] ||= {}
542
+ @tiers["BODY"]["Melee Combat"]["actual_weapons"] ||= {}
467
543
 
468
544
  # Find primary weapon (highest base value)
469
545
  primary_weapon = template["melee_weapons"].max_by { |_, v| v }
470
546
 
547
+ # Get Wield Weapon total for weapon table selection (replaces old STRENGTH)
548
+ # This is BODY + Strength + Wield weapon skill
549
+ wield_total = get_skill_total("BODY", "Strength", "Wield weapon") rescue 3
550
+
471
551
  template["melee_weapons"].each_with_index do |(weapon, skill_level), index|
472
552
  base_level = calculate_tier_level(skill_level, @level, 0.6)
473
553
 
@@ -478,16 +558,24 @@ class NpcNew
478
558
  end
479
559
 
480
560
  @tiers["BODY"]["Melee Combat"]["skills"][weapon] = base_level
561
+
562
+ # Select actual weapon from $Melee table based on Wield Weapon total
563
+ actual_weapon = select_actual_weapon_from_table(weapon, wield_total, false)
564
+ @tiers["BODY"]["Melee Combat"]["actual_weapons"][weapon] = actual_weapon
481
565
  end
482
566
  end
483
567
 
484
568
  # Add missile weapon skills
485
569
  if template["missile_weapons"]
486
570
  @tiers["BODY"]["Missile Combat"]["skills"] ||= {}
571
+ @tiers["BODY"]["Missile Combat"]["actual_weapons"] ||= {}
487
572
 
488
573
  # Find primary missile weapon
489
574
  primary_missile = template["missile_weapons"].max_by { |_, v| v }
490
575
 
576
+ # Get Wield Weapon total for missile weapon selection (bow strength requirements)
577
+ wield_total = get_skill_total("BODY", "Strength", "Wield weapon") rescue 3
578
+
491
579
  template["missile_weapons"].each do |weapon, skill_level|
492
580
  base_level = calculate_tier_level(skill_level, @level, 0.6)
493
581
 
@@ -498,6 +586,10 @@ class NpcNew
498
586
  end
499
587
 
500
588
  @tiers["BODY"]["Missile Combat"]["skills"][weapon] = base_level
589
+
590
+ # Select actual weapon based on Wield Weapon total
591
+ actual_weapon = select_actual_weapon_from_table(weapon, wield_total, true)
592
+ @tiers["BODY"]["Missile Combat"]["actual_weapons"][weapon] = actual_weapon
501
593
  end
502
594
  end
503
595
 
@@ -1166,6 +1258,15 @@ class NpcNew
1166
1258
  unless @tiers["MIND"]["Awareness"]["skills"].key?("Reaction speed")
1167
1259
  @tiers["MIND"]["Awareness"]["skills"]["Reaction speed"] = 0
1168
1260
  end
1261
+
1262
+ # Ensure Social Knowledge attribute exists for Spoken Language
1263
+ @tiers["MIND"]["Social Knowledge"] ||= {"level" => 0, "skills" => {}}
1264
+ @tiers["MIND"]["Social Knowledge"]["skills"] ||= {}
1265
+
1266
+ # Ensure Spoken Language is at least 2 (native tongue)
1267
+ # All characters start with 2 in Spoken Language per character creation rules
1268
+ current_spoken = @tiers["MIND"]["Social Knowledge"]["skills"]["Spoken Language"] || 0
1269
+ @tiers["MIND"]["Social Knowledge"]["skills"]["Spoken Language"] = [current_spoken, 2].max
1169
1270
  end
1170
1271
 
1171
1272
  def apply_predetermined_stats
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amar-tui
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene
8
8
  autorequire:
9
9
  bindir: "."
10
10
  cert_chain: []
11
- date: 2025-10-04 00:00:00.000000000 Z
11
+ date: 2026-01-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rcurses
@@ -122,15 +122,11 @@ metadata:
122
122
  changelog_uri: https://github.com/isene/Amar-Tools/blob/master/README.md
123
123
  post_install_message: |2+
124
124
 
125
- ==========================================
126
- AMAR Tools TUI v2.1.0 has been installed!
127
- ==========================================
125
+ ===========================================
126
+ AMAR RPG Tools v2.1.5 has been installed!
127
+ ===========================================
128
128
 
129
- NOTE: This gem has been renamed to 'amar-rpg'.
130
- Please install 'amar-rpg' instead:
131
- gem install amar-rpg
132
-
133
- To start the TUI application:
129
+ To start the application:
134
130
  amar-tui.rb
135
131
 
136
132
  For more information:
@@ -156,6 +152,6 @@ requirements: []
156
152
  rubygems_version: 3.4.20
157
153
  signing_key:
158
154
  specification_version: 4
159
- summary: AMAR Tools TUI - Terminal UI for Amar RPG
155
+ summary: AMAR RPG Tools - Terminal UI and utilities for Amar RPG
160
156
  test_files: []
161
157
  ...