/// /// /// /// /// /** * A person is a thing that happens to be "alive" in some significant way, not necessarily human. * A robot is a person. * A dog is a person. * * Fuck your standards. */ enum PersonStance { STANDING, ALLFOURS, KNOCKEDOUT } let PersonStanceNames = {}; PersonStanceNames[PersonStance.STANDING] = "standing"; PersonStanceNames[PersonStance.ALLFOURS] = "kneeling"; PersonStanceNames[PersonStance.KNOCKEDOUT] = "passed out"; class Person extends Thing implements AttributeBearer, SkillBearer { public AI = new AI({actor: this}); public animated = true; public soreness = 0; public lastHealthUpdate = 0; public stamina = 10; public lastStaminaUpdate = 0; public staminaPerTurn = 1; public static MAX_STAMINA = 10; public static STRENGTH_SORENESS_MULTIPLIER = 4; public stance : PersonStance = PersonStance.STANDING; public reputation : number = 0; public constructor (options : ThingOptions) { super(options); this.addGetAlterations((person : Person) => { // Let's not do this for NPCs. It can break patches. if (person.isPlayer()) { return { Stats: this.attributeValue, Skills: this.skillValue } } else { return { reputation : this.reputation } } }); // We *should* have the bodypart thing here, but there are issues with Humanoids and that... // this.addGetAlterations((person : Person) => { // // }); this.addSetAlterations((person : Person, changes) => { // Let's not do this for NPCs. It can break patches. if (person.isPlayer()) { if (changes.Stats != undefined) { for (let name in changes.Stats) { let attr = Attribute.getAttribute(name); if (attr != undefined) { this.setStat(attr, changes.Stats[name]); } } } if (changes.Skills != undefined) { for (let name in changes.Skills) { let attr = Skill.getSkill(name); if (attr != undefined) { this.setSkill(attr, changes.Skills[name]); } } } } else { if (changes.reputation != undefined) { this.reputation = changes.reputation; } } }); } public changeHealth (n : number) { let bodyparts = > this.getParts(Bodypart); for (let i = 0; i < bodyparts.length; i++) { bodyparts[i].changeSoreness(n); } this.updateHealth(); } /** * Returns health as a number from 0 to 10. */ public getHealthOnScale () { return Math.ceil(10 - (this.getHealth() * 10)); } /** * Returns current health. * Important = force update, otherwise use sufficiently accurate current value. to prevent looping too much. * @param {boolean} important * @returns {number} */ public getHealth (important? : boolean) { this.updateHealth(); return this.soreness / this.getMaxHealth() } public getMaxHealth () { return (this.getStat(Attributes.Strength) * Person.STRENGTH_SORENESS_MULTIPLIER); } /** * Lazy updates allow us to count Health/Stamina for NPCs without overloading the Every Turn rulebook. */ public updateHealth () { let health = 0; let bodyparts = this.getParts(Bodypart); for (let i = 0; i < bodyparts.length; i++) { health += bodyparts[i].getWeightedSoreness(); } this.soreness = health; this.lastHealthUpdate = WorldState.getCurrentTurn(); } public changeStamina (n : number) { this.updateStamina(); this.stamina += n; if (this.stamina > Person.MAX_STAMINA) { this.stamina = Person.MAX_STAMINA; } else if (this.stamina < 0) { this.stamina = 0; } } /** * Returns stamina as a number from 0 to 10. */ public getStaminaOnScale () { return Math.round( ((this.stamina * 10) / Person.MAX_STAMINA) ); } public updateStamina () { var nTurns = WorldState.getCurrentTurn() - this.lastStaminaUpdate; this.stamina += this.staminaPerTurn * nTurns; if (this.stamina > Person.MAX_STAMINA) { this.stamina = Person.MAX_STAMINA; } } public isPlayer () { return ( this) == WorldState.player; } protected attributeValue : {[id : string] : number} = {}; protected skillValue : {[id : string] : number} = {}; public getStat (stat : Attribute) { if (this.attributeValue[stat.id] == undefined) { this.attributeValue[stat.id] = stat.defaultValue; } return this.attributeValue[stat.id]; } public setStat (stat : Attribute, value : number) { this.attributeValue[stat.id] = value; } public getSkill (stat : Skill) { if (this.skillValue[stat.id] == undefined) { this.skillValue[stat.id] = stat.defaultValue; } return this.skillValue[stat.id]; } public setSkill (stat : Skill, value : number) { this.skillValue[stat.id] = value; } public die () { let corpse = new Corpse({ name : this.name + "'s corpse", unique : false, description : new Say("The lifeless body of ", this, ". May ", new SayHeSheIt(this), " rest in peace.") }); corpse.put(...Thing.CarryRelation.getRight(this)); corpse.put(...Thing.WearRelation.getRight(this)); this.getRoom().place(corpse); OutOfPlay.removeFromPlay(this); } public getPoked (action : Action) { return this.AI.getPoked(action); } }