amar-rpg 2.0.1 → 2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36fcd5d2e4b7db6ac279f1f301aff82718adb4d86dd29b9574c93104b4abe5e0
4
- data.tar.gz: c6d0940d54cb054f26dbd4f3f98e88ead1175dcaac640a9d72819e0d1bbaa1bf
3
+ metadata.gz: 58d7a7e1e8688949f6e0beed811aeab31928d09cd6a2c7b602aba9706b617b5d
4
+ data.tar.gz: 776ed77dce45af1ab2d8eb412beebf733e7ba509738c8b7328fe048e32227913
5
5
  SHA512:
6
- metadata.gz: 6fb4c2d0a34065cfc99ba6bda571522bb081d516dc34d2d6b90d170ac9484511cbf757c69a2b7aa3126a8d79ea798360cf1d56018d7dcbf0bcf87fdbd622310b
7
- data.tar.gz: 60bbe96c3c52e38cdf0a86abf5d093f131fea507e1c1aa9bd37c81a6cd84f62258540afa9f4b7563e49fa08283ce750ab8e1d5ce27ad0a3e364d7a77d74151f7
6
+ metadata.gz: bc917d0a80ff2b84a2aa2a87196609fc880aeca6f9f267b60492e523c41860bc529af1d73885942c7030c4fed07eb7869dcce7c4a4fd6cf76764babdbd05c1de
7
+ data.tar.gz: c5e7639280c0aa684396b6b669ff0a5d5d0301b16b6c0408f3a3bb2b7409fbb2d0e7eebe0471679cb643d7c3f404bacf78610e854a67ed374356d7bb444e400b
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.0.1"
63
+ @version = "2.1.1"
64
64
  $pgmdir = File.dirname(__FILE__) # Global for includes
65
65
 
66
66
  # Debug logging
@@ -231,8 +231,9 @@ def npc_output_new(n, cli, custom_width = nil)
231
231
  end
232
232
 
233
233
  # Check if we have original weapon data and display it
234
- if n.respond_to?(:melee1) && !n.melee1.to_s.empty?
235
- # Display ORIGINAL weapon format
234
+ # DISABLED: Old weapon format only shows 2-3 weapons, new format shows all skills
235
+ if false && n.respond_to?(:melee1) && !n.melee1.to_s.empty?
236
+ # Display ORIGINAL weapon format (DEPRECATED)
236
237
  f += "─" * width + "\n"
237
238
  f += "#{@weapon_color}WEAPON SKILL INI OFF DEF DAM HP RANGE#{@reset}\n"
238
239
 
@@ -273,68 +274,108 @@ def npc_output_new(n, cli, custom_width = nil)
273
274
  f += "\n"
274
275
  end
275
276
  else
276
- # Fall back to 3-tier weapon display
277
- melee_weapons = n.tiers["BODY"]["Melee Combat"]["skills"].select { |_, v| v > 0 }
278
- missile_weapons = n.tiers["BODY"]["Missile Combat"]["skills"].select { |_, v| v > 0 }
279
-
277
+ # 3-tier weapon display - show ALL weapon skills with actual weapon names
278
+ melee_weapons = n.tiers["BODY"]["Melee Combat"]["skills"].select { |_, v| v > 0 } rescue {}
279
+ missile_weapons = n.tiers["BODY"]["Missile Combat"]["skills"].select { |_, v| v > 0 } rescue {}
280
+ actual_melee = n.tiers["BODY"]["Melee Combat"]["actual_weapons"] rescue {}
281
+ actual_missile = n.tiers["BODY"]["Missile Combat"]["actual_weapons"] rescue {}
282
+
280
283
  if melee_weapons.any? || missile_weapons.any?
284
+ # Load weapon tables for stats lookup
285
+ unless defined?($Melee)
286
+ load File.join($pgmdir, "includes/tables/melee.rb")
287
+ end
288
+ unless defined?($Missile)
289
+ load File.join($pgmdir, "includes/tables/missile.rb")
290
+ end
291
+
281
292
  f += "─" * width + "\n"
282
- f += "#{@weapon_color}WEAPONS:#{@reset}\n"
283
-
284
- # Headers
285
- melee_header = "Melee Weapon".ljust(15) + "Skill Init Off Def Damage"
286
- missile_header = "Missile Weapon".ljust(15) + "Skill Range Damage"
287
- f += "#{melee_header.ljust(60)} │ #{missile_header}\n"
288
- f += "─" * 61 + "┼" + "─" * 58 + "\n"
289
-
290
- # Format weapons with calculated totals
291
- melee_lines = []
292
- missile_lines = []
293
-
293
+ f += "#{@weapon_color}WEAPON SKILL INI OFF DEF DAM HP RANGE#{@reset}\n"
294
+
294
295
  body_char = n.get_characteristic("BODY")
295
-
296
+
296
297
  # Calculate Dodge bonus for defense (Dodge/5 rounded down)
297
298
  dodge_total = n.get_skill_total("BODY", "Athletics", "Dodge") || 0
298
299
  dodge_bonus = (dodge_total / 5).to_i
299
-
300
- melee_weapons.each do |weapon, skill|
301
- wpn_stats = get_weapon_stats(weapon)
300
+
301
+ # Get reaction speed for initiative
302
+ reaction_speed = n.get_skill_total("MIND", "Awareness", "Reaction speed") || 0
303
+
304
+ # Display melee weapons
305
+ melee_weapons.sort_by { |_, skill| -skill }.each do |weapon_skill, skill|
306
+ # Get actual weapon name
307
+ actual_weapon_name = actual_melee[weapon_skill] || weapon_skill
308
+
309
+ # Find weapon in $Melee table
310
+ weapon_data = $Melee.find { |w| w && w[0] && w[0].strip == actual_weapon_name.strip }
311
+
302
312
  attr = n.get_attribute("BODY", "Melee Combat") || 0
303
313
  skill_total = body_char + attr + skill
304
-
305
- # Calculate weapon totals with correct initiative formula
306
- # Initiative = Weapon Init + Reaction Speed skill total
307
- reaction_speed = n.get_skill_total("MIND", "Awareness", "Reaction speed") || 0
308
- init = reaction_speed + (wpn_stats[:init] || 0)
309
- off = skill_total + (wpn_stats[:off] || 0)
310
- defense = skill_total + (wpn_stats[:def] || 0) + dodge_bonus # Include Dodge/5
311
- dmg_mod = wpn_stats[:dmg].to_s =~ /special/ ? 0 : (wpn_stats[:dmg].to_s.to_i || 0)
312
- dmg = (n.DB || 0) + dmg_mod
313
-
314
- line = "#{weapon.ljust(15)} #{skill_total.to_s.rjust(3)} #{init.to_s.rjust(4)} #{off.to_s.rjust(3)} #{defense.to_s.rjust(3)} #{dmg.to_s.rjust(3)}"
315
- melee_lines << line
314
+
315
+ if weapon_data
316
+ # Use actual weapon stats from table: [Name, Type, Str, Dam, Init, Off, Def, HP, Wt]
317
+ init = reaction_speed + weapon_data[4]
318
+ off = skill_total + weapon_data[5]
319
+ defense = skill_total + weapon_data[6] + dodge_bonus
320
+ dmg = (n.DB || 0) + weapon_data[3]
321
+ hp = weapon_data[7]
322
+ else
323
+ # Fallback to pattern matching
324
+ wpn_stats = get_weapon_stats(actual_weapon_name)
325
+ init = reaction_speed + (wpn_stats[:init] || 0)
326
+ off = skill_total + (wpn_stats[:off] || 0)
327
+ defense = skill_total + (wpn_stats[:def] || 0) + dodge_bonus
328
+ dmg_mod = wpn_stats[:dmg].to_s =~ /special/ ? 0 : (wpn_stats[:dmg].to_s.to_i || 0)
329
+ dmg = (n.DB || 0) + dmg_mod
330
+ hp = wpn_stats[:hp] || 0
331
+ end
332
+
333
+ f += "#{actual_weapon_name.ljust(19)}"
334
+ f += "#{skill_total.to_s.ljust(9)}"
335
+ f += "#{init.to_s.ljust(8)}"
336
+ f += "#{off.to_s.ljust(7)}"
337
+ f += "#{defense.to_s.ljust(7)}"
338
+ f += "#{dmg.to_s.ljust(7)}"
339
+ f += "#{hp.to_s.ljust(6)}"
340
+ f += "\n"
316
341
  end
317
-
318
- missile_weapons.each do |weapon, skill|
319
- wpn_stats = get_missile_stats(weapon)
342
+
343
+ # Display missile weapons
344
+ missile_weapons.sort_by { |_, skill| -skill }.each do |weapon_skill, skill|
345
+ # Get actual weapon name
346
+ actual_weapon_name = actual_missile[weapon_skill] || weapon_skill
347
+
348
+ # Find weapon in $Missile table
349
+ weapon_data = $Missile.find { |w| w && w[0] && w[0].strip == actual_weapon_name.strip }
350
+
320
351
  attr = n.get_attribute("BODY", "Missile Combat") || 0
321
352
  skill_total = body_char + attr + skill
322
-
323
- range = wpn_stats[:range] || "30m"
324
- dmg_mod = wpn_stats[:dmg].to_s =~ /special/ ? 0 : (wpn_stats[:dmg].to_s.to_i || 0)
325
- dmg = (n.DB || 0) + dmg_mod
326
-
327
- line = "#{weapon.ljust(15)} #{skill_total.to_s.rjust(3)} #{range.ljust(10)} #{dmg.to_s.rjust(3)}"
328
- missile_lines << line
329
- end
330
-
331
- # Balance weapon columns
332
- max_wpn_lines = [melee_lines.length, missile_lines.length].max
333
- melee_lines += [""] * (max_wpn_lines - melee_lines.length)
334
- missile_lines += [""] * (max_wpn_lines - missile_lines.length)
335
-
336
- max_wpn_lines.times do |i|
337
- f += "#{melee_lines[i].ljust(60)} │ #{missile_lines[i]}\n"
353
+
354
+ if weapon_data
355
+ # Use actual weapon stats from table: [Name, Type, Str, Dam, Off, Rng, Max, Init, Wt]
356
+ off = skill_total + weapon_data[4]
357
+ dmg = (n.DB || 0) + weapon_data[3]
358
+ range = "#{weapon_data[5]}m"
359
+ hp = weapon_data[7]
360
+ else
361
+ # Fallback to pattern matching
362
+ wpn_stats = get_missile_stats(actual_weapon_name)
363
+ off = skill_total + (wpn_stats[:off] || 0)
364
+ range = wpn_stats[:range] || "30m"
365
+ dmg_mod = wpn_stats[:dmg].to_s =~ /special/ ? 0 : (wpn_stats[:dmg].to_s.to_i || 0)
366
+ dmg = (n.DB || 0) + dmg_mod
367
+ hp = wpn_stats[:hp] || 0
368
+ end
369
+
370
+ f += "#{actual_weapon_name.ljust(19)}"
371
+ f += "#{skill_total.to_s.ljust(9)}"
372
+ f += "#{' '.ljust(8)}" # No init for missile
373
+ f += "#{off.to_s.ljust(7)}"
374
+ f += "#{' '.ljust(7)}" # No def for missile
375
+ f += "#{dmg.to_s.ljust(7)}"
376
+ f += "#{hp.to_s.ljust(6)}"
377
+ f += "#{range}"
378
+ f += "\n"
338
379
  end
339
380
  end
340
381
  end # Close the original/3-tier weapon display if/else block
@@ -406,10 +406,22 @@ class NpcNew
406
406
  else 0.5
407
407
  end
408
408
 
409
- # Use square root for more realistic progression
410
- level = (base * Math.sqrt(npc_level + 1) * growth_rate).to_i
411
-
412
- # Add minimal variation ONLY if base > 0
409
+ # Use scaling that produces desired total skill progression
410
+ # Target totals: L1: 4-5, L2: 6-7, L3: 8-9, L4: 10-11, L5: 12-13, L6: 14+
411
+ # Typical warrior has BODY 1-2, Melee Combat 2-3, Weapon skill 2-3 at mid-levels
412
+ level_multiplier = case npc_level
413
+ when 1 then 0.7
414
+ when 2 then 0.95
415
+ when 3 then 1.2
416
+ when 4 then 1.45
417
+ when 5 then 1.7
418
+ when 6 then 1.95
419
+ else 2.2
420
+ end
421
+
422
+ level = (base * level_multiplier * growth_rate).to_i
423
+
424
+ # Add variation ONLY if base > 0
413
425
  if base > 0
414
426
  variation = rand(3) - 1 # -1, 0, or 1
415
427
  level += variation
@@ -448,22 +460,138 @@ class NpcNew
448
460
  level
449
461
  end
450
462
 
463
+ def select_actual_weapon(skill_name, strength_total, is_missile = false)
464
+ # Map skill name to actual weapon from $Melee or $Missile table
465
+ # Load weapon tables if needed
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
+ # Map missile skill names to actual weapons
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_total >= 10
479
+ "Bow(H3) [1]"
480
+ elsif strength_total >= 8
481
+ "Bow(H2) [1]"
482
+ elsif strength_total >= 6
483
+ "Bow(H) [1]"
484
+ elsif strength_total >= 4
485
+ "Bow(M) [1]"
486
+ else
487
+ "Bow(L) [1]"
488
+ end
489
+ when /crossbow|x-bow/
490
+ # Select crossbow based on strength
491
+ if strength_total >= 4
492
+ "X-bow(H) [¼]"
493
+ elsif strength_total >= 3
494
+ "X-bow(M) [⅓]"
495
+ else
496
+ "X-bow(L) [½]"
497
+ end
498
+ when /throwing|knife/
499
+ "Th Knife [2]"
500
+ when /javelin/
501
+ "Javelin [1]"
502
+ when /sling/
503
+ "Sling [1]"
504
+ when /net/
505
+ skill_name # Net is just "Net"
506
+ when /spear/
507
+ "Javelin [1]" # Thrown spear = javelin
508
+ else
509
+ "Rock [2]" # Default
510
+ end
511
+ else
512
+ # Map melee skill names to actual weapons from $Melee table
513
+ case skill_name.downcase
514
+ when /sword/
515
+ strength_total >= 4 ? "Longsword" : "Short sword"
516
+ when /axe/
517
+ strength_total >= 4 ? "B. axe 2H" : "Hatchet"
518
+ when /spear/
519
+ strength_total >= 4 ? "Spear 2H" : "Spear"
520
+ when /mace/
521
+ strength_total >= 4 ? "H. mace 2H" : "Light mace"
522
+ when /dagger|knife/
523
+ "Knife"
524
+ when /staff/
525
+ "Staff"
526
+ when /club/
527
+ "Club"
528
+ when /net/
529
+ "Net"
530
+ when /shield/
531
+ "Buckler"
532
+ when /unarmed/
533
+ "Unarmed"
534
+ else
535
+ skill_name # Return as-is if no match
536
+ end
537
+ end
538
+ end
539
+
451
540
  def add_weapon_skills(template)
452
- # Add melee weapon skills
541
+ # Add melee weapon skills with primary weapon specialization
542
+ # Also store actual weapon selections based on skill names
453
543
  if template["melee_weapons"]
454
544
  @tiers["BODY"]["Melee Combat"]["skills"] ||= {}
455
- template["melee_weapons"].each do |weapon, skill_level|
545
+ @tiers["BODY"]["Melee Combat"]["actual_weapons"] ||= {}
546
+
547
+ # Find primary weapon (highest base value)
548
+ primary_weapon = template["melee_weapons"].max_by { |_, v| v }
549
+
550
+ # Get strength total for weapon selection
551
+ strength_total = get_skill_total("BODY", "Strength", "Wield weapon") rescue 3
552
+
553
+ template["melee_weapons"].each_with_index do |(weapon, skill_level), index|
456
554
  base_level = calculate_tier_level(skill_level, @level, 0.6)
555
+
556
+ # Boost primary weapon by 1-2 points for specialization
557
+ if weapon == primary_weapon[0] && skill_level >= 4
558
+ boost = rand(2) + 1 # +1 or +2
559
+ base_level += boost
560
+ end
561
+
457
562
  @tiers["BODY"]["Melee Combat"]["skills"][weapon] = base_level
563
+
564
+ # Select actual weapon based on skill name and strength
565
+ actual_weapon = select_actual_weapon(weapon, strength_total, false)
566
+ @tiers["BODY"]["Melee Combat"]["actual_weapons"][weapon] = actual_weapon
458
567
  end
459
568
  end
460
-
569
+
461
570
  # Add missile weapon skills
462
571
  if template["missile_weapons"]
463
572
  @tiers["BODY"]["Missile Combat"]["skills"] ||= {}
573
+ @tiers["BODY"]["Missile Combat"]["actual_weapons"] ||= {}
574
+
575
+ # Find primary missile weapon
576
+ primary_missile = template["missile_weapons"].max_by { |_, v| v }
577
+
578
+ # Get strength total for missile weapon selection
579
+ strength_total = get_skill_total("BODY", "Strength", "Wield weapon") rescue 3
580
+
464
581
  template["missile_weapons"].each do |weapon, skill_level|
465
582
  base_level = calculate_tier_level(skill_level, @level, 0.6)
583
+
584
+ # Boost primary missile weapon
585
+ if weapon == primary_missile[0] && skill_level >= 3
586
+ boost = rand(2) + 1 # +1 or +2
587
+ base_level += boost
588
+ end
589
+
466
590
  @tiers["BODY"]["Missile Combat"]["skills"][weapon] = base_level
591
+
592
+ # Select actual weapon based on skill name and strength
593
+ actual_weapon = select_actual_weapon(weapon, strength_total, true)
594
+ @tiers["BODY"]["Missile Combat"]["actual_weapons"][weapon] = actual_weapon
467
595
  end
468
596
  end
469
597
 
@@ -369,11 +369,13 @@ def assign_spells_to_npc(npc)
369
369
 
370
370
  max_spells = case magic_skill
371
371
  when 0..5 then 0 # No spells for beginners
372
- when 6..10 then 1 # 1 spell for novices
373
- when 11..15 then 2 # 2 spells for competent
374
- when 16..20 then 3 # 3 spells for skilled
375
- when 21..25 then 4 # 4 spells for experts
376
- else 5 # 5 spells for masters
372
+ when 6..8 then 2 # 2 spells for novices
373
+ when 9..11 then 4 # 4 spells for apprentices
374
+ when 12..14 then 6 # 6 spells for competent
375
+ when 15..17 then 8 # 8 spells for skilled
376
+ when 18..20 then 10 # 10 spells for experts
377
+ when 21..25 then 12 # 12 spells for masters
378
+ else 15 # 15 spells for legendary
377
379
  end
378
380
 
379
381
  # Get character's strongest magical domains
@@ -388,6 +390,7 @@ def assign_spells_to_npc(npc)
388
390
  can_cast = true
389
391
  spell_data[:skill_path].each do |skill_part|
390
392
  # Simplified check - character should have some relevant skills
393
+ # Also check abbreviated names (e.g., "Prot." for "Protection")
391
394
  case skill_part
392
395
  when "Fire" then can_cast &&= (all_magic_skills["Fire"] || 0) > 0
393
396
  when "Water" then can_cast &&= (all_magic_skills["Water"] || 0) > 0
@@ -395,11 +398,13 @@ def assign_spells_to_npc(npc)
395
398
  when "Earth" then can_cast &&= (all_magic_skills["Earth"] || 0) > 0
396
399
  when "Mind" then can_cast &&= (all_magic_skills["Mind"] || 0) > 0
397
400
  when "Nature" then can_cast &&= (all_magic_skills["Nature"] || 0) > 0
398
- when "Protection" then can_cast &&= (all_magic_skills["Protection"] || 0) > 0
399
- when "Illusion" then can_cast &&= (all_magic_skills["Illusion"] || 0) > 0
401
+ when "Protection" then can_cast &&= ((all_magic_skills["Protection"] || 0) > 0 || (all_magic_skills["Prot."] || 0) > 0)
402
+ when "Illusion" then can_cast &&= ((all_magic_skills["Illusion"] || 0) > 0 || (all_magic_skills["Ill."] || 0) > 0)
400
403
  when "Shadow" then can_cast &&= (all_magic_skills["Shadow"] || 0) > 0
401
404
  when "Self" then can_cast &&= (all_magic_skills["Self"] || 0) > 0
402
405
  when "Life" then can_cast &&= (all_magic_skills["Life"] || 0) > 0
406
+ when "Time" then can_cast &&= (all_magic_skills["Time"] || 0) > 0
407
+ when "Death" then can_cast &&= (all_magic_skills["Death"] || 0) > 0
403
408
  end
404
409
  end
405
410
  can_cast
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amar-rpg
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Geir Isene
@@ -123,7 +123,7 @@ metadata:
123
123
  post_install_message: |2+
124
124
 
125
125
  ===========================================
126
- AMAR RPG Tools v2.0.1 has been installed!
126
+ AMAR RPG Tools v2.1.1 has been installed!
127
127
  ===========================================
128
128
 
129
129
  To start the application: