import { Builder } from "../string_utils.js";
import { sentenceCase, startLowercase, traitsToString } from "../string_utils.js";
import { is, sum } from "../utils.js";
import Model from "./model.js";
/**
* A player or NPC character.
*
* @class Character
* @extends Model
*
* @constructor
* @param [params] {Object} parameters
*/
class Character extends Model {
constructor({
name,
gender = "male",
age = 0,
honorific,
degree,
clothing,
possessions,
traits = {},
description = [],
tags = new Set(),
} = {}) {
super({ tags });
this.name = name;
this.profession = null; // moves it up in prop iteration
this.gender = gender;
this.age = age;
this.honorific = honorific;
this.description = description;
this.degree = degree;
this.traits = traits;
this.clothing = clothing;
this.possessions = possessions;
}
/**
* Is this character male?
*
* @property male
* @return {Boolean} True if male, false if female.
*/
get male() {
return this.gender === "male";
}
/**
* The total number of assigned trait points for this character is
* referred to as the character's "points".
*
* @property points
* @return {Number} The total number of trait points assigned to this character
*/
get points() {
return sum(Object.values(this.traits));
}
/**
* Personal pronoun
*
* character.gender
* => "female"
* character.personal
* => "she"
*
* property personal
* @return {String} "he" or "she"
*/
get personal() {
return this.gender === "male" ? "he" : "she";
}
/**
* Objective pronoun
*
* character.gender
* => "female"
* character.objective
* => "her"
*
* @property objective
* @return {String} "him" or "her"
*/
get objective() {
return this.gender === "male" ? "him" : "her";
}
/**
* Reflexive pronoun (arguably redundant with the personal pronoun, may remove).
*
* character.gender
* => "female"
* character.reflexive
* => "herself"
*
* @property reflexive
* @return {String} "himself" or "herself"
*/
get reflexive() {
return this.gender === "male" ? "himself" : "herself";
}
/**
* Possessive pronoun
*
* character.gender
* => "female"
* character.possessive
* => "her"
*
* @property possessive
* @return {String} "his" or "her"
*/
get possessive() {
return this.gender === "male" ? "his" : "her";
}
/**
* Change a trait by a set number of points.
*
* @param traitName {String} The trait to change
* @param value {Number} The amount to add or subtract. If the value is zero or less
* after change, the trait will be removed.
*/
changeTrait(traitName, value = 0) {
this.traits[traitName] = this.traits[traitName] ?? 0;
this.traits[traitName] += value;
if (this.traits[traitName] <= 0) {
delete this.traits[traitName];
}
}
/**
* Does this character have a trait?
*
* @method trait
* @param traitName {String} The name of the trait
* @return {Number}
* the value of the trait, or 0 if the character does not have the trait
*/
trait(traitName) {
return this.traits[traitName] ?? 0;
}
#hasBag(name) {
return is(this[name], "Object") && this[name].entries.length;
}
toString({ format = "long" } = {}) {
// short, medium
var b = new Builder();
b.append("<p>");
b.if(this.honorific, `${this.honorific} `);
b.if(this.name, this.name.toString());
b.if(this.degree, `, ${this.degree}`);
b.if(!this.honorific && !!this.profession, ", " + this.profession);
b.append(". ");
if (!!this.status) {
b.append(`${sentenceCase(this.status)} (would be ${this.age}). `);
} else if (this.age) {
b.append(`Age ${this.age}. `);
}
b.if(this.description.length, `${this.description.join(" ")}. `);
b.if(this.#hasBag("clothing"), () => `Wearing ${startLowercase(this.clothing.toString())} `);
b.if(Object.keys(this.traits).length, traitsToString(this.traits) + ". ");
b.append("</p>");
b.if(
format === "long" && this.#hasBag("possessions"),
() => `<p>Possessions: ${this.possessions.toString()}</p>`
);
return b.toString().replaceAll("..", ".").trim();
}
}
export default Character;