yazdaabdul commited on
Commit
492c9dc
·
verified ·
1 Parent(s): 0708299

Interact with NPCs by recruiting them in your party. Maximum 3 NPCs - Follow Up Deployment

Browse files
Files changed (1) hide show
  1. index.html +225 -2
index.html CHANGED
@@ -204,6 +204,23 @@
204
  </div>
205
  <span class="text-sm">Health: <span id="health-text">100</span>/100</span>
206
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  </div>
208
  </div>
209
 
@@ -422,7 +439,10 @@
422
  combatEnemy: null,
423
  selectedSettlement: null,
424
  gameLog: [],
425
- actionsThisTurn: 0
 
 
 
426
  };
427
 
428
  // DOM Elements
@@ -452,8 +472,43 @@
452
  const endTurnBtn = document.getElementById('end-turn');
453
  const mapTooltip = document.getElementById('map-tooltip');
454
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
  // Initialize game
456
  function initGame() {
 
 
 
457
  // Set up event listeners
458
  startGameBtn.addEventListener('click', startGame);
459
  endTurnBtn.addEventListener('click', endTurn);
@@ -612,9 +667,14 @@
612
  <div><span class="font-semibold">Garrison:</span> ${settlement.garrison}</div>
613
  <div><span class="font-semibold">Wealth:</span> ${settlement.wealth}</div>
614
  </div>
615
- <div class="text-sm text-gray-400 italic">
616
  ${getSettlementDescription(settlement)}
617
  </div>
 
 
 
 
 
618
  `;
619
 
620
  // Enable/disable action buttons based on settlement
@@ -636,6 +696,117 @@
636
  document.querySelector(`.settlement[data-id="${settlementId}"]`).classList.add('border-2', 'border-white');
637
  }
638
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
639
  function getSettlementDescription(settlement) {
640
  const faction = gameState.factions[settlement.faction];
641
  const playerFaction = gameState.factions[gameState.player.faction];
@@ -798,6 +969,36 @@
798
  }
799
 
800
  // Handle combat retreat
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
801
  function combatRetreat() {
802
  if (!gameState.inCombat) return;
803
 
@@ -1145,6 +1346,28 @@
1145
  }
1146
 
1147
  // Update player info display
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1148
  function updatePlayerInfo() {
1149
  playerNameEl.textContent = gameState.player.name;
1150
  playerFactionEl.textContent = gameState.factions[gameState.player.faction].name;
 
204
  </div>
205
  <span class="text-sm">Health: <span id="health-text">100</span>/100</span>
206
  </div>
207
+ ${gameState.npcParty.length > 0 ? `
208
+ <div class="border-t border-gray-700 pt-2">
209
+ <h4 class="font-semibold mb-1">NPC Party (${gameState.npcParty.length}/${gameState.maxNPCs}):</h4>
210
+ ${gameState.npcParty.map(npc => `
211
+ <div class="flex items-center justify-between py-1">
212
+ <div class="flex items-center">
213
+ <i class="fas ${getNPCIcon(npc.type)} ${getNPCColor(npc.type)} mr-2"></i>
214
+ <span>${npc.name}</span>
215
+ </div>
216
+ <button onclick="dismissNPC('${npc.id}')"
217
+ class="px-2 py-1 bg-red-700 hover:bg-red-800 rounded text-sm">
218
+ Dismiss
219
+ </button>
220
+ </div>
221
+ `).join('')}
222
+ </div>
223
+ ` : ''}
224
  </div>
225
  </div>
226
 
 
439
  combatEnemy: null,
440
  selectedSettlement: null,
441
  gameLog: [],
442
+ actionsThisTurn: 0,
443
+ npcs: [],
444
+ npcParty: [],
445
+ maxNPCs: 3
446
  };
447
 
448
  // DOM Elements
 
472
  const endTurnBtn = document.getElementById('end-turn');
473
  const mapTooltip = document.getElementById('map-tooltip');
474
 
475
+ // Generate random NPCs
476
+ function generateNPCs() {
477
+ const maleTypes = ['noble son', 'merchant', 'mercenary', 'peasant'];
478
+ const femaleTypes = ['noble daughter', 'merchant', 'healer', 'witch'];
479
+ const firstNamesMale = ['Heinrich', 'Konrad', 'Friedrich', 'Ludwig', 'Albrecht', 'Johann', 'Ulrich', 'Werner'];
480
+ const firstNamesFemale = ['Adelheid', 'Gertrud', 'Elisabeth', 'Mechthild', 'Agnes', 'Hedwig', 'Margarete', 'Katharina'];
481
+ const lastNames = ['von Bayern', 'von Schwaben', 'von Franken', 'von Sachsen', 'von Lothringen', 'von Habsburg', 'von Wittelsbach', 'von Hohenzollern'];
482
+
483
+ // Create 1-2 NPCs per major town
484
+ gameState.settlements
485
+ .filter(s => s.type === 'town')
486
+ .forEach(settlement => {
487
+ const npcCount = Math.floor(Math.random() * 2) + 1;
488
+ for (let i = 0; i < npcCount; i++) {
489
+ const isFemale = Math.random() > 0.5;
490
+ const types = isFemale ? femaleTypes : maleTypes;
491
+ const firstNames = isFemale ? firstNamesFemale : firstNamesMale;
492
+
493
+ const npc = {
494
+ id: `npc-${settlement.id}-${i}`,
495
+ name: `${firstNames[Math.floor(Math.random() * firstNames.length)]} ${lastNames[Math.floor(Math.random() * lastNames.length)]}`,
496
+ type: types[Math.floor(Math.random() * types.length)],
497
+ gender: isFemale ? 'female' : 'male',
498
+ location: settlement.id,
499
+ relation: Math.floor(Math.random() * 60) - 30, // -30 to 30
500
+ quest: null
501
+ };
502
+ gameState.npcs.push(npc);
503
+ }
504
+ });
505
+ }
506
+
507
  // Initialize game
508
  function initGame() {
509
+ // Generate NPCs
510
+ generateNPCs();
511
+
512
  // Set up event listeners
513
  startGameBtn.addEventListener('click', startGame);
514
  endTurnBtn.addEventListener('click', endTurn);
 
667
  <div><span class="font-semibold">Garrison:</span> ${settlement.garrison}</div>
668
  <div><span class="font-semibold">Wealth:</span> ${settlement.wealth}</div>
669
  </div>
670
+ <div class="text-sm text-gray-400 italic mb-3">
671
  ${getSettlementDescription(settlement)}
672
  </div>
673
+ <div class="border-t border-gray-700 pt-2">
674
+ <h4 class="font-semibold mb-1">Notable Persons:</h4>
675
+ ${getSettlementNPCs(settlement.id)}
676
+ ${getRecruitableNPCs(settlement.id)}
677
+ </div>
678
  `;
679
 
680
  // Enable/disable action buttons based on settlement
 
696
  document.querySelector(`.settlement[data-id="${settlementId}"]`).classList.add('border-2', 'border-white');
697
  }
698
 
699
+ function getRecruitableNPCs(settlementId) {
700
+ const recruitableNPCs = gameState.npcs.filter(npc =>
701
+ npc.location === settlementId &&
702
+ npc.relation >= 0 &&
703
+ !gameState.npcParty.some(p => p.id === npc.id)
704
+ );
705
+
706
+ if (recruitableNPCs.length === 0 || gameState.npcParty.length >= gameState.maxNPCs) {
707
+ return '';
708
+ }
709
+
710
+ return `
711
+ <div class="mt-3">
712
+ <h4 class="font-semibold mb-1">Available for Recruitment:</h4>
713
+ ${recruitableNPCs.map(npc => `
714
+ <div class="flex items-center justify-between py-1">
715
+ <div class="flex items-center">
716
+ <i class="fas ${getNPCIcon(npc.type)} ${getNPCColor(npc.type)} mr-2"></i>
717
+ <span>${npc.name} (${npc.type})</span>
718
+ </div>
719
+ <button onclick="recruitNPC('${npc.id}')"
720
+ class="px-2 py-1 bg-green-700 hover:bg-green-800 rounded text-sm">
721
+ Recruit
722
+ </button>
723
+ </div>
724
+ `).join('')}
725
+ </div>
726
+ `;
727
+ }
728
+
729
+ function getNPCIcon(type) {
730
+ switch(type) {
731
+ case 'noble son':
732
+ case 'noble daughter': return 'fa-crown';
733
+ case 'merchant': return 'fa-coins';
734
+ case 'mercenary': return 'fa-swords';
735
+ case 'peasant': return 'fa-wheat';
736
+ case 'healer': return 'fa-heart';
737
+ case 'witch': return 'fa-magic';
738
+ default: return 'fa-user';
739
+ }
740
+ }
741
+
742
+ function getNPCColor(type) {
743
+ switch(type) {
744
+ case 'noble son':
745
+ case 'noble daughter': return 'text-yellow-400';
746
+ case 'merchant': return 'text-green-400';
747
+ case 'mercenary': return 'text-red-400';
748
+ case 'peasant': return 'text-brown-400';
749
+ case 'healer': return 'text-pink-400';
750
+ case 'witch': return 'text-purple-400';
751
+ default: return 'text-gray-300';
752
+ }
753
+ }
754
+
755
+ function getSettlementNPCs(settlementId) {
756
+ const npcs = gameState.npcs.filter(npc => npc.location === settlementId);
757
+ if (npcs.length === 0) {
758
+ return '<div class="text-gray-500 italic">No notable persons present</div>';
759
+ }
760
+
761
+ return npcs.map(npc => {
762
+ let icon = '';
763
+ let color = 'text-gray-300';
764
+
765
+ switch(npc.type) {
766
+ case 'noble son':
767
+ case 'noble daughter':
768
+ icon = 'fa-crown';
769
+ color = 'text-yellow-400';
770
+ break;
771
+ case 'merchant':
772
+ icon = 'fa-coins';
773
+ color = 'text-green-400';
774
+ break;
775
+ case 'mercenary':
776
+ icon = 'fa-swords';
777
+ color = 'text-red-400';
778
+ break;
779
+ case 'peasant':
780
+ icon = 'fa-wheat';
781
+ color = 'text-brown-400';
782
+ break;
783
+ case 'healer':
784
+ icon = 'fa-heart';
785
+ color = 'text-pink-400';
786
+ break;
787
+ case 'witch':
788
+ icon = 'fa-magic';
789
+ color = 'text-purple-400';
790
+ break;
791
+ }
792
+
793
+ const relationText = npc.relation > 20 ? 'Friendly' :
794
+ npc.relation < -20 ? 'Hostile' : 'Neutral';
795
+ const relationColor = npc.relation > 20 ? 'text-green-400' :
796
+ npc.relation < -20 ? 'text-red-400' : 'text-gray-400';
797
+
798
+ return `
799
+ <div class="flex items-center justify-between py-1">
800
+ <div class="flex items-center">
801
+ <i class="fas ${icon} ${color} mr-2"></i>
802
+ <span>${npc.name} (${npc.type})</span>
803
+ </div>
804
+ <span class="${relationColor} text-sm">${relationText}</span>
805
+ </div>
806
+ `;
807
+ }).join('');
808
+ }
809
+
810
  function getSettlementDescription(settlement) {
811
  const faction = gameState.factions[settlement.faction];
812
  const playerFaction = gameState.factions[gameState.player.faction];
 
969
  }
970
 
971
  // Handle combat retreat
972
+ function recruitNPC(npcId) {
973
+ if (gameState.npcParty.length >= gameState.maxNPCs) {
974
+ addGameLog(`You can only have ${gameState.maxNPCs} NPCs in your party.`);
975
+ return;
976
+ }
977
+
978
+ const npc = gameState.npcs.find(n => n.id === npcId);
979
+ if (!npc || npc.relation < 0) {
980
+ addGameLog("This NPC cannot be recruited.");
981
+ return;
982
+ }
983
+
984
+ // Add to party
985
+ gameState.npcParty.push(npc);
986
+
987
+ // Remove from settlement
988
+ const settlementIndex = gameState.settlements.findIndex(s => s.id === npc.location);
989
+ if (settlementIndex !== -1) {
990
+ gameState.settlements[settlementIndex].npcIds = gameState.settlements[settlementIndex].npcIds?.filter(id => id !== npc.id) || [];
991
+ }
992
+
993
+ addGameLog(`You recruited ${npc.name} (${npc.type}) to your party!`);
994
+
995
+ // Update UI
996
+ const settlement = gameState.settlements.find(s => s.id === npc.location);
997
+ if (settlement) {
998
+ selectSettlement(settlement.id);
999
+ }
1000
+ }
1001
+
1002
  function combatRetreat() {
1003
  if (!gameState.inCombat) return;
1004
 
 
1346
  }
1347
 
1348
  // Update player info display
1349
+ function dismissNPC(npcId) {
1350
+ const npcIndex = gameState.npcParty.findIndex(n => n.id === npcId);
1351
+ if (npcIndex === -1) return;
1352
+
1353
+ const npc = gameState.npcParty[npcIndex];
1354
+
1355
+ // Return to current location
1356
+ npc.location = gameState.player.location;
1357
+
1358
+ // Remove from party
1359
+ gameState.npcParty.splice(npcIndex, 1);
1360
+
1361
+ addGameLog(`You dismissed ${npc.name} from your party.`);
1362
+
1363
+ // Update UI
1364
+ updatePlayerInfo();
1365
+ const settlement = gameState.settlements.find(s => s.id === gameState.player.location);
1366
+ if (settlement) {
1367
+ selectSettlement(settlement.id);
1368
+ }
1369
+ }
1370
+
1371
  function updatePlayerInfo() {
1372
  playerNameEl.textContent = gameState.player.name;
1373
  playerFactionEl.textContent = gameState.factions[gameState.player.faction].name;