Initial commit: Fresh start with current state
This commit is contained in:
359
src/macro_arcaneSelector.js
Normal file
359
src/macro_arcaneSelector.js
Normal file
@@ -0,0 +1,359 @@
|
||||
(async () => {
|
||||
// Ensure a token is selected
|
||||
if (!token) {
|
||||
ui.notifications.warn("You must select a token!");
|
||||
return;
|
||||
}
|
||||
|
||||
let ac = actor; // game.actors.getName("Zeratal");
|
||||
let level = Math.floor((ac.classes["magus"].level - 1) / 4 + 1);
|
||||
let arcanePool = ac.system.resources.classFeat_arcanePool.value;
|
||||
|
||||
console.log(level, arcanePool);
|
||||
|
||||
function openDialog() {
|
||||
let dialogContent = `
|
||||
<html lang="en">
|
||||
<head>
|
||||
<style>
|
||||
.container { padding: 10px; }
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
background: #f0f0f0;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.info-row label { margin-right: 10px; font-weight: bold; }
|
||||
.info-row input { margin-right: 20px; width: 50px; }
|
||||
.arcana-section {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
background: #e8f4f8;
|
||||
border-left: 4px solid #2196F3;
|
||||
}
|
||||
.arcana-item {
|
||||
margin: 5px 0;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.arcana-item input[type="checkbox"] {
|
||||
margin-right: 8px;
|
||||
margin-top: 3px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.arcana-item label {
|
||||
font-weight: normal;
|
||||
line-height: 1.4;
|
||||
}
|
||||
table { width: 100%; border-collapse: collapse; margin: 15px 0; }
|
||||
th {
|
||||
background: #2196F3;
|
||||
color: white;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
}
|
||||
td { padding: 8px; border-bottom: 1px solid #ddd; }
|
||||
tr:hover { background: #f5f5f5; }
|
||||
.value { font-weight: bold; text-align: center; }
|
||||
.total-display {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
padding: 10px;
|
||||
background: #e8f4f8;
|
||||
border-radius: 4px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.total-display div {
|
||||
margin: 5px 0;
|
||||
}
|
||||
.warning { color: red; }
|
||||
.cost-high { color: #ff6b6b; }
|
||||
.cost-medium { color: #ffa500; }
|
||||
.cost-low { color: #4caf50; }
|
||||
input[type="checkbox"]:disabled, input[type="radio"]:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="info-row">
|
||||
<label for="input-value">Enhancement Bonus:</label>
|
||||
<input type="number" id="input-value" value="${level}" disabled>
|
||||
<label for="arcane-pool-value">Available Pool:</label>
|
||||
<input type="text" id="arcane-pool-value" value="${arcanePool}" disabled>
|
||||
</div>
|
||||
|
||||
<div class="arcana-section">
|
||||
<div class="arcana-item">
|
||||
<input type="checkbox" id="enduring-blade" class="arcana-checkbox" value="1">
|
||||
<label for="enduring-blade">Enduring Blade (Costs +1 pool point, Duration: 1 min/level)</label>
|
||||
</div>
|
||||
<div class="arcana-item">
|
||||
<input type="checkbox" id="ghost-blade" class="arcana-checkbox" value="1">
|
||||
<label for="ghost-blade">Ghost Blade (Costs +1 pool point, Unlocks Ghost Touch & Brilliant Energy)</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table name="InputTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Enhancement</th>
|
||||
<th style="width: 60%;">Options</th>
|
||||
<th style="width: 15%; text-align: center;">Cost</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Keen [1]</td>
|
||||
<td><input type="checkbox" class="action" value="1" id="Keen"></td>
|
||||
<td class="value">0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ghost Touch [1]</td>
|
||||
<td><input type="checkbox" class="action enhancement-option" value="1" id="Ghost Touch" data-requires="ghost-blade" disabled></td>
|
||||
<td class="value">0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Speed [3]</td>
|
||||
<td><input type="checkbox" class="action" value="3" id="Speed"></td>
|
||||
<td class="value">0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Dancing [4]</td>
|
||||
<td><input type="checkbox" class="action" value="4" id="Dancing"></td>
|
||||
<td class="value">0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Brilliant Energy [4]</td>
|
||||
<td><input type="checkbox" class="action enhancement-option" value="4" id="Brilliant Energy" data-requires="ghost-blade" disabled></td>
|
||||
<td class="value">0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Vorpal [5]</td>
|
||||
<td><input type="checkbox" class="action" value="5" id="Vorpal"></td>
|
||||
<td class="value">0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Fire</td>
|
||||
<td>
|
||||
<input type="radio" name="group1" class="action" value="0"> None
|
||||
<input type="radio" name="group1" class="action" value="1" id="Flaming"> Flaming
|
||||
<input type="radio" name="group1" class="action" value="2" id="Flaming Burst"> Flaming Burst
|
||||
</td>
|
||||
<td class="value">0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ice</td>
|
||||
<td>
|
||||
<input type="radio" name="group2" class="action" value="0"> None
|
||||
<input type="radio" name="group2" class="action" value="1" id="Frost"> Frost
|
||||
<input type="radio" name="group2" class="action" value="2" id="Icy Burst"> Icy Burst
|
||||
</td>
|
||||
<td class="value">0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Lightning</td>
|
||||
<td>
|
||||
<input type="radio" name="group3" class="action" value="0"> None
|
||||
<input type="radio" name="group3" class="action" value="1" id="Shock"> Shock
|
||||
<input type="radio" name="group3" class="action" value="2" id="Shocking Burst"> Shocking Burst
|
||||
</td>
|
||||
<td class="value">0</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="total-display">
|
||||
<div>Enhancement Bonus Used: <span id="enhancement-used">0</span> / ${level}</div>
|
||||
<div>Arcane Pool Cost: <span id="pool-cost">1</span> point(s)</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
new Dialog({
|
||||
title: "Arcane Pool - Weapon Enhancement",
|
||||
content: dialogContent,
|
||||
buttons: {
|
||||
confirm: {
|
||||
label: "Apply Enhancements",
|
||||
callback: async (html) => {
|
||||
// Read all selections
|
||||
let checkboxes = html.find('input[type="checkbox"]:checked');
|
||||
let radios = html.find('input[type="radio"]:checked');
|
||||
let enhancementUsed = parseInt(html.find("#enhancement-used").text()) || 0;
|
||||
let poolCost = parseInt(html.find("#pool-cost").text()) || 1;
|
||||
|
||||
// Collect all selected buffs
|
||||
let selectedBuffs = [];
|
||||
checkboxes.each(function() {
|
||||
// Skip the arcana checkboxes (enduring-blade and ghost-blade)
|
||||
if (this.id !== "enduring-blade" && this.id !== "ghost-blade") {
|
||||
console.log(this.id);
|
||||
selectedBuffs.push(this.id);
|
||||
}
|
||||
});
|
||||
radios.each(function() {
|
||||
let value = $(this).val();
|
||||
if (value !== "0") {
|
||||
selectedBuffs.push(this.id);
|
||||
}
|
||||
});
|
||||
console.log(actor);
|
||||
|
||||
// Check if Enduring Blade is selected
|
||||
if (html.find("#enduring-blade").is(":checked")) {
|
||||
var bubb = actor.items.find(o => o.name === "Enduring Blade" && o.type ==="buff");
|
||||
selectedBuffs.push("Enduring Blade");
|
||||
}
|
||||
|
||||
// Check if Ghost Blade is selected
|
||||
if (html.find("#ghost-blade").is(":checked")) {
|
||||
var bubb = actor.items.find(o => o.name === "Ghost Blade" && o.type ==="buff");
|
||||
selectedBuffs.push("Ghost Blade");
|
||||
}
|
||||
|
||||
// Get all required macros upfront
|
||||
const setBuffStatusMacro = game.macros.getName("_callSetBuffStatus");
|
||||
const changePoolBonusMacro = game.macros.getName("_callChangeArcanePoolBonus");
|
||||
const changePoolMacro = game.macros.getName("_callChangeArcanePool");
|
||||
|
||||
// Validate all macros exist
|
||||
if (!setBuffStatusMacro || !changePoolBonusMacro || !changePoolMacro) {
|
||||
ui.notifications.error("Required helper macros not found!");
|
||||
console.error("Missing macros:", {
|
||||
setBuffStatus: !!setBuffStatusMacro,
|
||||
changePoolBonus: !!changePoolBonusMacro,
|
||||
changePool: !!changePoolMacro
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Activate all selected buffs
|
||||
// NOTE: Each buff toggle triggers its own macro that must complete before
|
||||
// attack rolls can see the buff effects. The 150ms delay allows those
|
||||
// buff macros to finish processing and apply effects to the character.
|
||||
for (const buffName of selectedBuffs) {
|
||||
await setBuffStatusMacro.execute({
|
||||
name: buffName,
|
||||
status: true
|
||||
});
|
||||
// Allow buff's triggered macro to complete and apply effects
|
||||
await new Promise(resolve => setTimeout(resolve, 150));
|
||||
}
|
||||
|
||||
// Activate Arcane Pool buff
|
||||
await setBuffStatusMacro.execute({
|
||||
name: "Arcane Pool",
|
||||
status: true
|
||||
});
|
||||
// Allow Arcane Pool buff macro to complete
|
||||
await new Promise(resolve => setTimeout(resolve, 150));
|
||||
|
||||
// Update enhancement bonus
|
||||
await changePoolBonusMacro.execute({value: enhancementUsed});
|
||||
|
||||
// Deduct pool points
|
||||
await changePoolMacro.execute({value: -poolCost});
|
||||
|
||||
ui.notifications.info(`Arcane Pool activated! Enhancements: ${selectedBuffs.join(', ')} | Pool Cost: ${poolCost} points`);
|
||||
},
|
||||
disabled: true
|
||||
},
|
||||
cancel: {
|
||||
label: "Cancel"
|
||||
}
|
||||
},
|
||||
default: "confirm",
|
||||
render: (html) => {
|
||||
const confirmButton = html.closest('.dialog').find('button[data-button="confirm"]');
|
||||
|
||||
// Handle Ghost Blade checkbox to enable/disable dependent options
|
||||
html.find('#ghost-blade').on('change', function() {
|
||||
const isChecked = $(this).is(':checked');
|
||||
html.find('.enhancement-option[data-requires="ghost-blade"]').prop('disabled', !isChecked);
|
||||
if (!isChecked) {
|
||||
// Uncheck Ghost Touch and Brilliant Energy if Ghost Blade is unchecked
|
||||
html.find('.enhancement-option[data-requires="ghost-blade"]').prop('checked', false);
|
||||
}
|
||||
calculateSum();
|
||||
});
|
||||
|
||||
// Attach the calculateSum function to inputs after the dialog is rendered
|
||||
html.find('.action, .arcana-checkbox, #input-value').on('change', calculateSum);
|
||||
|
||||
function calculateSum() {
|
||||
let enhancementSum = 0;
|
||||
let inputValue = parseInt(html.find('#input-value').val()) || 0;
|
||||
let rows = html.find('[name="InputTable"] tbody tr');
|
||||
|
||||
// Calculate enhancement bonus used
|
||||
rows.each(function() {
|
||||
let actionCell = $(this).find('.action:checked');
|
||||
let valueCell = $(this).find('.value');
|
||||
let value = 0;
|
||||
|
||||
if (actionCell.length) {
|
||||
value = parseInt(actionCell.val());
|
||||
enhancementSum += value;
|
||||
// Color code the cost
|
||||
valueCell.removeClass('cost-high cost-medium cost-low');
|
||||
if (value >= 4) valueCell.addClass('cost-high');
|
||||
else if (value >= 2) valueCell.addClass('cost-medium');
|
||||
else if (value > 0) valueCell.addClass('cost-low');
|
||||
} else {
|
||||
valueCell.removeClass('cost-high cost-medium cost-low');
|
||||
}
|
||||
|
||||
valueCell.text(value);
|
||||
});
|
||||
|
||||
// Calculate arcane pool cost (base 1 + arcana costs)
|
||||
let poolCost = 1;
|
||||
html.find('.arcana-checkbox:checked').each(function() {
|
||||
poolCost += parseInt($(this).val()) || 0;
|
||||
});
|
||||
|
||||
html.find('#enhancement-used').text(enhancementSum);
|
||||
html.find('#pool-cost').text(poolCost);
|
||||
|
||||
// Check constraints
|
||||
let availablePool = parseInt(html.find('#arcane-pool-value').val()) || 0;
|
||||
let enhancementExceeded = enhancementSum > inputValue;
|
||||
let poolInsufficient = poolCost > availablePool;
|
||||
|
||||
// Update display warnings
|
||||
if (enhancementExceeded) {
|
||||
html.find('#enhancement-used').addClass('warning');
|
||||
} else {
|
||||
html.find('#enhancement-used').removeClass('warning');
|
||||
}
|
||||
|
||||
if (poolInsufficient) {
|
||||
html.find('#pool-cost').addClass('warning');
|
||||
} else {
|
||||
html.find('#pool-cost').removeClass('warning');
|
||||
}
|
||||
|
||||
// Enable/disable confirm button
|
||||
if (enhancementExceeded || poolInsufficient) {
|
||||
confirmButton.prop('disabled', true).css('background-color', 'red');
|
||||
} else {
|
||||
confirmButton.prop('disabled', false).css('background-color', '');
|
||||
}
|
||||
}
|
||||
|
||||
// Initial calculation on render
|
||||
calculateSum();
|
||||
}
|
||||
}).render(true);
|
||||
}
|
||||
|
||||
// Call the function to open the dialog
|
||||
openDialog();
|
||||
})();
|
||||
Reference in New Issue
Block a user