import { Builder } from "../string_utils.js";
import { is, sum } from "../utils.js";
import { sentenceCase, startLowercase, traitsToString } from "../string_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({
age = 0,
children,
clothing,
degree,
description = [],
gen,
gender = "male",
heritage = null,
honorific,
meetSomeone,
name,
parentProfession,
partner,
possessions,
profession = null,
relationship,
status,
tags = new Set(),
traits = {},
guid,
} = {}) {
super({ tags, guid });
this.name = name;
this.profession = profession; // moves it up in prop iteration
this.honorific = honorific;
this.heritage = heritage;
this.gender = gender;
this.age = age;
this.description = description;
this.degree = degree;
this.traits = traits;
this.clothing = clothing;
this.possessions = possessions;
// These are assigned after construction, but still need to be present to
// allow copy constructions to work (which is how we convert JSON back into
// model objects).
this.status = status;
this.relationship = relationship;
this.gen = gen;
this.parentProfession = parentProfession;
this.meetSomeone = meetSomeone;
this.partner = partner;
this.children = children;
this._class = "Character";
}
/**
* 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?
*
* @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", includeName = true } = {}) {
// short, medium
const b = new Builder();
if (includeName) {
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)}. `);
} else if (typeof this.age !== "undefined") {
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.if(
format === "long" && this.#hasBag("possessions"),
() => ` Carrying ${startLowercase(this.possessions.toString())}`,
);
return b.toString().replaceAll("..", ".").trim();
}
toTitleString() {
return this.name ? this.name.toString() : undefined;
}
getDisplayProps() {
return { _gender: this.gender, _heritage: this.heritage, _profession: this.profession };
}
toDetailHtml() {
return this.toString({ includeName: false });
}
}
export default Character;