Various fixes

This commit is contained in:
2021-04-03 22:48:02 +02:00
parent 2e4f94d7af
commit e686de9210
9 changed files with 420 additions and 355 deletions

View File

@ -25,14 +25,25 @@ export class VadentisActorSheet extends ActorSheet {
getData() {
let data = super.getData();
data.stats = this.actor.stats;
data.combat = this.actor.combat;
data.magie = this.actor.magie;
data.editScore = this.options.editScore;
data.donnees = this.actor.getDonnees();
data.editScore = this.options.editScore;
data.donnees = this.actor.getDonnees();
data.eglises = this.actor.getEglises();
data.competences = this.actor.getCompetences();
data.sorts = this.actor.getSorts();
data.devotions = this.actor.getDevotions();
data.sorts = this.actor.getSorts();
data.devotions = this.actor.getDevotions();
data.attributs = this.actor.getAttributs();
data.techniques = this.actor.getTechniques();
data.armes = this.actor.getArmes();
data.armures = this.actor.getArmures();
data.equipements = this.actor.getEquipements();
data.optionsBase = VadentisUtility.createDirectOptionList(0, 50);
data.optionsMalus = VadentisUtility.createDirectOptionList(-50, 0);
data.optionsBonus = VadentisUtility.createDirectOptionList(0, 50);
data.optionsPV = VadentisUtility.createOptionList(-50, 200);
data.optionsPE = VadentisUtility.createOptionList(-50, 200);
data.optionsPA = VadentisUtility.createOptionList(0, 20);
data.isGM = game.user.isGM;
return data;
@ -67,11 +78,21 @@ export class VadentisActorSheet extends ActorSheet {
const competenceId = li.data("item-id");
this.actor.rollCompetence(competenceId);
});
html.find('.technique-label a').click((event) => {
const li = $(event.currentTarget).parents(".item");
const techniqueId = li.data("item-id");
this.actor.rollTechnique(techniqueId);
});
html.find('.sort-label a').click((event) => {
const li = $(event.currentTarget).parents(".item");
const sortId = li.data("item-id");
this.actor.rollSort(sortId);
});
html.find('.arme-label a').click((event) => {
const li = $(event.currentTarget).parents(".item");
const armeId = li.data("item-id");
this.actor.rollArme(armeId);
});
html.find('.devotion-label a').click((event) => {
const li = $(event.currentTarget).parents(".item");
const devotionId = li.data("item-id");
@ -103,7 +124,12 @@ export class VadentisActorSheet extends ActorSheet {
const item = this.actor.getOwnedItem(itemId);
item.sheet.render(true);
});
html.find('.item-equip').click(ev => {
const li = $(ev.currentTarget).parents(".item");
this.actor.equiperObject( li.data("item-id") );
this.render(true);
});
}
/* -------------------------------------------- */

View File

@ -32,13 +32,6 @@ export class VadentisActor extends Actor {
return actor;
}
/*data.items = [];
let compendiumName = "foundryvtt-vadentis.competences";
if ( compendiumName ) {
let skills = await SoSUtility.loadCompendium(compendiumName);
data.items = data.items.concat( skills );
}*/
return super.create(data, options);
}
@ -56,16 +49,38 @@ export class VadentisActor extends Actor {
getDonnees() {
return this.data.items.filter( item => item.type == 'donnee');
}
/* -------------------------------------------- */
getEglises() {
return this.data.items.filter( item => item.type == 'eglise');
}
/* -------------------------------------------- */
getSorts() {
return this.data.items.filter( item => item.type == 'sort');
}
/* -------------------------------------------- */
getAttributs() {
return this.data.items.filter( item => item.type == 'attribut');
}
/* -------------------------------------------- */
getTechniques() {
return this.data.items.filter( item => item.type == 'technique');
}
/* -------------------------------------------- */
getDevotions() {
return this.data.items.filter( item => item.type == 'devotion');
}
/* -------------------------------------------- */
getEquipements() {
return this.data.items.filter( item => item.type == 'equipement' );
}
/* -------------------------------------------- */
getArmes() {
return this.data.items.filter( item => item.type == 'armecc' || item.type == 'tir' );
}
/* -------------------------------------------- */
getArmures() {
return this.data.items.filter( item => item.type == 'armurebouclier' );
}
/* -------------------------------------------- */
async updateCompetence( name, field, value) {
@ -77,74 +92,89 @@ export class VadentisActor extends Actor {
}
/* -------------------------------------------- */
rollSort( sortId ) {
let sort = this.data.items.find( item => item.type == 'sort' && item._id == sortId );
if ( sort ) {
if ( sort.data.pe > this.data.data.stats.pointsenergie.value) { // Vérification du ~ de points d'énergie
ChatMessage.create({ content: `Vous n'avez pas assez de Points d'Energie pour lancer votre sort ${sort.name}` } );
return;
}
let magieElementaire = this.data.data.magie['matriseelementaire'];
let statValue = magieElementaire.base + magieElementaire.malus + magieElementaire.bonus;
let formulaFull = `1d20+${magieElementaire.base}+${magieElementaire.malus}+${magieElementaire.bonus}`;
let myRoll = new Roll("1d20+"+statValue);
myRoll.evaluate();
myRoll.toMessage( { flavor: `Lancer de Sort : ${sort.name} (${formulaFull})` } );
if (myRoll.total >= sort.data.difficulty) {
let content = `Votre sort ${sort.name} a réussi ! Vous perdez ${sort.data.pe} Points d'Energie et votre sort produit l'effet : <br>${sort.data.effect}`;
let newEnergie = this.data.data.stats.pointsenergie - sort.data.pe;
await this.update( {'data.stats.pointsenergie': newEnergie });
if ( sort.data.damage != "") {
if (myRoll.results[0] == 20 ) { // Critique ?
content += `<br>Et provoque les dégats critiques suivants : ${sort.data.damagecritical}`;
} else {
content += `<br>Et provoque les dégats suivants : ${sort.data.damage}`;
}
}
ChatMessage.create({ content:content} );
} else {
ChatMessage.create({ content: `Votre sort ${sort.name} a échoué !`});
}
async equiperObject( equipementId ) {
let item = this.getOwnedItem(equipementId);
if (item && item.data.data) {
let update = { _id: item._id, "data.equipee": !item.data.data.equipee };
await this.updateEmbeddedEntity("OwnedItem", update);
}
}
/* -------------------------------------------- */
rollDevotion( devotionId ) {
async processSortDevotion( name, devotionSort ) {
if ( this.data.data.stats.pointsenergie.value > 0) { // Vérification du ~ de points d'énergie
ChatMessage.create({ content: `${this.name} n'a pas assez de Points d'Energie pour lancer ${name} ${devotionSort.name}` } );
return;
}
let scores = this.data.data.magie[(name =="devotion") ? 'devotion': 'matriseelementaire'];
let statValue = scores.base + scores.malus + scores.bonus;
let formulaFull = `1d20+${scores.base}+${scores.malus}+${scores.bonus}`;
let myRoll = new Roll("1d20+"+statValue);
myRoll.evaluate();
myRoll.toMessage( { flavor: `Lancer de ${name} : ${devotionSort.name} (${formulaFull})` } );
if (myRoll.total >= devotionSort.data.difficulty) {
let content = `${this.name} a réussi son ${name} et perd ${devotionSort.data.pe} Points d'Energie. L'effet suivant se produit: <br>${devotionSort.data.effect}`;
let newEnergie = this.data.data.stats.pointsenergie.value - devotionSort.data.pe;
await this.update( {'data.stats.pointsenergie.value': newEnergie });
if ( devotionSort.data.damage != "") {
if (myRoll.results[0] == 20 ) { // Critique ?
content += `<br>Et provoque les dégats critiques suivants : ${devotionSort.data.damagecritical}`;
} else {
content += `<br>Et provoque les dégats suivants : ${devotionSort.data.damage}`;
}
}
if ( newEnergie < 0) {
content += `<br>Les Points d'Energie de ${this.name} sont négatifs ! Il convient d'éditer ses Points de Vie en conséquence.`;
}
ChatMessage.create( { content: content} );
} else {
ChatMessage.create( { content: `${this.name} a échoué son lancer de ${name}` } );
}
}
/* -------------------------------------------- */
getDefenseScore( ) {
let defenseData = this.data.data.combat.defense;
return defenseData.base + defenseData.malus + defenseData.bonus;
}
/* -------------------------------------------- */
getAttaqueScore( ) {
let attaqueData = this.data.data.combat.attaque;
return attaqueData.base + attaqueData.malus + attaqueData.bonus;
}
/* -------------------------------------------- */
async rollSort( sortId ) {
let sort = this.data.items.find( item => item.type == 'sort' && item._id == sortId );
if ( sort ) {
this.processSortDevotion( "sort", sort);
}
}
/* -------------------------------------------- */
async rollDevotion( devotionId ) {
let devotion = this.data.items.find( item => item.type == 'devotion' && item._id == devotionId );
if ( devotion ) {
this.processSortDevotion( "devotion", devotion);
}
}
if ( devotion.data.pe > this.data.data.stats.pointsenergie.value) { // Vérification du ~ de points d'énergie
ChatMessage.create({ content: `Vous n'avez pas assez de Points d'Energie pour lancer votre dévotion ${devotion.name}` } );
/* -------------------------------------------- */
async rollTechnique( techniqueId ) {
let technique = this.data.items.find( item => item.type == 'technique' && item._id == techniqueId );
if (technique) {
if ( this.data.data.stats.pointsadrenaline.value < technique.data.pacost) { // Vérification du ~ de points d'adrénaline
ChatMessage.create({ content: `${this.name} n'a pas assez de Points d'Adrénaline pour éxecuter sa technique ${technique.name}` } );
return;
}
let newAdrenaline = this.data.data.stats.pointsadrenaline.value - technique.data.pacost;
await this.update( {'data.stats.pointsadrenaline.value': newAdrenaline });
let devotionComp = this.data.data.magie['devotion'];
let statValue = devotionComp.base + devotionComp.malus + devotionComp.bonus;
let formulaFull = `1d20+${devotionComp.base}+${devotionComp.malus}+${devotionComp.bonus}`;
let myRoll = new Roll("1d20+"+statValue);
myRoll.evaluate();
myRoll.toMessage( { flavor: `Lancer de Dévotion : ${devotion.name} (${formulaFull})` } );
if (myRoll.total >= devotion.data.difficulty) {
let content = `Votre dévotion ${devotion.name} a réussie ! Vous perdez ${devotion.data.pe} Points d'Energie et votre dévotion produit l'effet : <br>${devotion.data.effect}`;
let newEnergie = this.data.data.stats.pointsenergie - devotion.data.pe;
await this.update( {'data.stats.pointsenergie': newEnergie });
if ( devotion.data.damage != "") {
if (myRoll.results[0] == 20 ) { // Critique ?
content += `<br>Et provoque les dégats critiques suivants : ${devotion.data.damagecritical}`;
} else {
content += `<br>Et provoque les dégats suivants : ${devotion.data.damage}`;
}
}
ChatMessage.create({ content:content} );
} else {
ChatMessage.create({ content: `Votre dévotion ${devotion.name} a échoué !`});
}
ChatMessage.create( { content: `${this.name} execute sa technique ${technique.name}, pour un côut de ${technique.data.pacost} Points d'Adrénaline<br>
Les effets sont : ${technique.data.effect}`} );
}
}
@ -167,8 +197,30 @@ export class VadentisActor extends Actor {
let formulaFull = `1d20+${stat.base}+${stat.malus}+${stat.bonus}`;
let myRoll = new Roll("1d20+"+statValue);
myRoll.evaluate();
myRoll.toMessage( { flavor: `Jet de ${stat.label} (${formulaFull})` } );
myRoll.toMessage( { title: "Test !",flavor: `Jet de ${stat.label} (${formulaFull})` } );
}
/* -------------------------------------------- */
rollArme(armeId) {
let target = VadentisUtility.getTarget();
if ( target ) {
let arme = this.data.items.find( item => (item.type == 'armecc' || item.type == 'tir') && item._id == armeId);
if (arme) {
let combatData = {
attackerActorId: this._id,
targetActorId: target.actor._id,
arme: duplicate(arme)
}
if (game.user.isGM) {
VadentisUtility.performAttack( combatData);
} else {
game.socket.emit("system.foundryvtt-vadentis", { name: "msg_attack", data: { combatData } } );
}
}
} else {
ui.notifications.warn("Vous devez désigner une cible pour attaquer avec une arme.")
}
}
}

View File

@ -3,187 +3,5 @@ import { VadentisUtility } from "./vadentis-utility.js";
/* -------------------------------------------- */
export class VadentisCombat extends Combat {
/* -------------------------------------------- */
requestActions() {
if ( game.user.isGM && !this.actionsRequested) {
console.log("REQUEST ACTIONS !!!");
this.actionsRequested = true;
this.phaseSetup = {}; // Reset each new round/update
for( let combatant of this.combatants) {
this.setInitiative(combatant._id, -1 ); // Reset init
let uniq = randomID(16);
const name = combatant.actor ? combatant.actor.data.name : combatant.name;
if ( combatant.players[0]) {
// A player controls this combatant -> message !
ChatMessage.create( { content: `New round ! Click on the button below to declare the actions of ${name} for round ${this.round} !<br>
<a class='chat-card-button' id='button-declare-actions' data-uniq-id='${uniq}' data-combatant-id='${combatant._id}' data-combat-id='${this._id}' data-round='${this.round}'>Declare actions</a>`,
whisper: [ combatant.players[0].data._id] } );
} else {
ChatMessage.create( { content: `New round ! Click on the button below to declare the actions of ${name} for round ${this.round} !<br>
<a class='chat-card-button' id='button-declare-actions' data-uniq-id='${uniq}' data-combatant-id='${combatant._id}' data-combat-id='${this._id}' data-round='${this.round}'>Declare actions</a>`,
whisper: [ ChatMessage.getWhisperRecipients("GM") ] } );
}
}
}
}
/* -------------------------------------------- */
async nextRound() {
this.actionsRequested = false;
super.nextRound();
}
/* -------------------------------------------- */
gotoNextTurn() {
this.phaseNumber -= 1;
if ( this.phaseNumber <= 0) {
this.applyConsequences();
this.nextRound(); // Auto-switch to next round
} else {
this.nextTurn();
}
}
/* -------------------------------------------- */
async nextTurn() {
console.log("Going to phase !", this.phaseNumber );
// Get all actions for this phase
let phaseIndex = this.phaseNumber - 1;
let actionList = [];
let actionMsg = `<h4>Actions for phase ${this.phaseNumber}</h4>`;
for (let combatantId in this.phaseSetup ) {
let actionData = this.phaseSetup[combatantId];
if ( actionData.phaseArray[phaseIndex].name != 'No Action' ) {
let combatant = this.combatants.find( comb => comb._id == actionData.combatantId);
const name = combatant.actor ? combatant.actor.data.name : combatant.name;
actionList.push( { combatant: combatant,
action: actionData.phaseArray[phaseIndex],
isDone: false
});
actionMsg += `<br>${name} is going to : ${actionData.phaseArray[phaseIndex].name}`;
}
}
if ( actionList.length == 0) {
actionMsg += "<br>No actions for the phase !";
this.gotoNextTurn();
}
// Display a nice message
ChatMessage.create( { content: actionMsg });
// Now push specific messages
for ( let action of actionList) {
let uniq = randomID(16);
action.uniqId = uniq; // Easy tracking with chat messages
const name = action.combatant.actor ? action.combatant.actor.data.name : action.combatant.name;
if ( action.combatant.players[0]) {
// A player controls this combatant -> message !
ChatMessage.create( { content: `Phase ${this.phaseNumber} ! ${name} must perform a <strong>${action.action.name}</strong> action.
When done, click on the button below to close the action.
<a class='chat-card-button' id='button-end-action' data-uniq-id='${uniq}' data-combatant-id='${action.combatant._id}' data-combat-id='${this._id}' data-round='${this.round}'>Action is done !</a>`,
whisper: [ action.combatant.players[0].data._id] } );
} else {
ChatMessage.create( { content: `Phase ${this.phaseNumber} ! ${name} must perform a <strong>${action.action.name}</strong> action.<br>
When done, click on the button below to close the action.
<a class='chat-card-button' id='button-end-action' data-uniq-id='${uniq}' data-combatant-id='${action.combatant._id}' data-combat-id='${this._id}' data-round='${this.round}'>Action is done !</a>`,
whisper: [ ChatMessage.getWhisperRecipients("GM") ] } );
}
}
// Save for easy access
this.currentActions = actionList;
}
/* -------------------------------------------- */
applyConsequences( ) {
if (game.user.isGM ) {
for( let combatant of this.combatants) {
if (!combatant.actor) continue; // Can't check tokens without assigned actors, Maybe print chat message about bleeding happening so that the GM can manually track this?
let bleeding = combatant.actor.data.items.find( item => item.type == 'consequence' && item.name == 'Bleeding');
combatant.actor.applyConsequenceWound( bleeding.data.severity, "bleeding" );
}
}
}
/* -------------------------------------------- */
closeAction( uniqId) {
// Delete message !
const toDelete = game.messages.filter(it => it.data.content.includes( uniqId ));
toDelete.forEach(it => it.delete());
let action = this.currentActions.find( _action => _action.uniqId == uniqId );
if (action) {
action.isDone = true;
let filtered = this.currentActions.filter( _action => action.isDone );
if ( filtered.length == this.currentActions.length) { // All actions closed !
console.log("Going next turn !!!");
this.gotoNextTurn();
}
}
}
/* -------------------------------------------- */
getPhaseRank( actionConf) {
for (let i=2; i>=0; i--) {
let action = actionConf.phaseArray[i];
if (action.name != "No Action") {
return i+1;
}
}
return 0;
}
/* -------------------------------------------- */
getAPFromActor( actorId ) {
for( let combatant of this.combatants) {
//console.log(combatant);
if ( combatant.actor.data._id == actorId ) {
let phase = this.phaseSetup[combatant._id];
return phase.remainingAP;
}
}
return 0;
}
/* -------------------------------------------- */
decreaseAPFromActor( actorId ) {
for( let combatant of this.combatants) {
//console.log(combatant);
if ( combatant.actor.data._id == actorId ) {
let phase = this.phaseSetup[combatant._id];
phase.remainingAP -= 1;
if ( phase.remainingAP < 0 ) phase.remainingAP = 0;
}
}
}
/* -------------------------------------------- */
async setupActorActions(actionConf) {
console.log("Setting combat for phase : ", actionConf, actionConf.uniqId);
// Delete message !
const toDelete = game.messages.filter(it => it.data.content.includes( actionConf.uniqId ));
console.log("MESSAGE : ", toDelete);
toDelete.forEach(it => it.delete());
if ( !this.phaseSetup) this.phaseSetup = {}; // Opportunistic init
// Keep track
this.phaseSetup[actionConf.combatantId] = actionConf;
console.log( this.combatants );
//let combatant = this.combatants.find( comb => comb._id == actionConf.combatantId);
await this.setInitiative( actionConf.combatantId, this.getPhaseRank( actionConf ) );
let actionsDone = true
for( let combatant of this.combatants) {
if ( combatant.initiative == -1 ) actionsDone = false;
}
if ( actionsDone ) {
this.actionsRequested = false;
ChatMessage.create( { content: `Action declaration has been completed ! Now proceeding with actions.`,
whisper: [ ChatMessage.getWhisperRecipients("GM") ] } );
this.phaseNumber = 3;
this.nextTurn();
}
}
}

View File

@ -18,6 +18,57 @@ export class VadentisUtility extends Entity {
return loadTemplates(templatePaths);
}
/* -------------------------------------------- */
static createOptionList( min, max) {
let options = ""
for(let i=min; i<=max; i++) {
options+= `<option value="${i}">${i}</option>\n`;
}
return options;
}
/* -------------------------------------------- */
static createDirectOptionList( min, max) {
let options = []
for(let i=min; i<=max; i++) {
options.push( i ) ;
}
return options;
}
/* -------------------------------------------- */
static getTarget() {
if (game.user.targets && game.user.targets.size == 1) {
for (let target of game.user.targets) {
return target;
}
}
return undefined;
}
/* -------------------------------------------- */
static async performAttack( combatData) {
let attacker = game.actors.get(combatData.attackerActorId);
let defender = game.actors.get(combatData.targetActorId);
if( attacker && defender) {
let defense = defender.getDefenseScore();
let attaque = attacker.getAttaqueScore();
console.log("Attaque : ", attaque);
let myRoll = new Roll("1d20"+attaque);
myRoll.evaluate()
if (game.modules.get("dice-so-nice") && game.modules.get("dice-so-nice").active) {
await game.dice3d.showForRoll(myRoll, game.user, true);
}
if (myRoll.total >= defense) { // Success !
ChatMessage.create( { content: `${attacker.name} a réussi son attaque sur ${defender.name} (${myRoll.total} / ${defense}) !<br> Les dégâts sont de : ${combatData.arme.data.damage}`});
} else { //Echec
ChatMessage.create( { content: `${attacker.name} a raté son attaque sur ${defender.name} (${myRoll.total} / ${defense}) !` });
}
} else {
ui.notifications.warn("Impossible de trouver l'attaquant et le défenseur.")
}
}
/* -------------------------------------------- */
static registerChatCallbacks( ) {
@ -32,7 +83,8 @@ export class VadentisUtility extends Entity {
static onSocketMesssage( msg ) {
if( !game.user.isGM ) return; // Only GM
if (msg.name == 'msg_declare_actions' ) {
if (msg.name == 'msg_attack' ) {
this.performAttack( msg.data );
}
}