diff --git a/CHANGELOG.md b/CHANGELOG.md index a1ed5fb..c4fe230 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# 0.7.0 / 06-oct-2016 +# 0.7.0 / 09-oct-2016 * switched to typescript 2.0 (this breaks node support for unit tests) * Yendor toolkit diff --git a/src/fwk/actors/actor.ts b/src/fwk/actors/actor.ts index c254bd2..0ff5c93 100644 --- a/src/fwk/actors/actor.ts +++ b/src/fwk/actors/actor.ts @@ -551,7 +551,7 @@ export class Actor extends Yendor.TimedEntity implements Yendor.IPersistent { } } if (this.ai) { - let condDesc: string|undefined = this.ai.getConditionDescription(); + let condDesc: string|undefined = this.ai.getConditionDescription(this); if (condDesc) { desc += ", " + condDesc; } diff --git a/src/fwk/actors/actor_condition.ts b/src/fwk/actors/actor_condition.ts index f1467d1..28ea24c 100644 --- a/src/fwk/actors/actor_condition.ts +++ b/src/fwk/actors/actor_condition.ts @@ -35,7 +35,15 @@ export class Condition { } } - private static condNames = ["confused", "stunned", "frozen", "regeneration", "overencumbered", "life detection"]; + private static condNames = [ + undefined, + "confused", + "stunned", + "frozen", + "regeneration", + "overencumbered", + "life detection", + ]; public readonly onlyIfActive: boolean; public readonly initialTime: number; @@ -47,22 +55,26 @@ export class Condition { protected _time: number; protected _type: ConditionTypeEnum; protected name: string|undefined; - protected noDisplay: boolean; + protected _noDisplay: boolean; + protected _noCorpse: boolean; protected constructor(def: IConditionDef) { if (def) { this.initialTime = def.nbTurns; this._time = def.nbTurns; this._type = def.type; - this.noDisplay = def.noDisplay || false; + this._noDisplay = def.noDisplay || false; this.name = def.name; this.onlyIfActive = def.onlyIfActive || false; + this._noCorpse = def.noCorpse || false; } } get type() { return this._type; } get time() { return this._time; } - public getName() { return this.noDisplay ? undefined : this.name ? this.name : Condition.condNames[this._type]; } + get noCorpse() { return this._noCorpse; } + get noDisplay() { return this._noDisplay; } + public getName() { return this._noDisplay ? undefined : this.name ? this.name : Condition.condNames[this._type]; } /** * Function: onApply diff --git a/src/fwk/actors/actor_creature.ts b/src/fwk/actors/actor_creature.ts index 75495c8..f0eda89 100644 --- a/src/fwk/actors/actor_creature.ts +++ b/src/fwk/actors/actor_creature.ts @@ -108,8 +108,23 @@ export class Ai implements IActorFeature, IContainerListener { } } - public getConditionDescription(): string|undefined { - return this._conditions && this._conditions.length > 0 ? this._conditions[0].getName() : undefined; + public getConditionDescription(owner: Actor): string|undefined { + // find the first valid condition + if (! this._conditions) { + return undefined; + } + let i: number = 0; + while ( i < this._conditions.length ) { + let condition: Condition = this._conditions[i]; + if ( condition.getName()) { + if (! condition.noDisplay + && (!condition.noCorpse || ! owner.destructible || !owner.destructible.isDead())) { + return condition.getName(); + } + } + i++; + } + return undefined; } public hasActiveConditions(): boolean { @@ -253,19 +268,14 @@ export class Ai implements IActorFeature, IContainerListener { } } - // private activateActor(owner: Actor, actor: Actor) { - // if (actor.activable) { - // actor.activable.switchLever(actor, owner); - // owner.wait(this._walkTime); - // } else if (actor.container && this.lootHandler) { - // this.lootHandler.lootContainer(owner, actor); - // } - // } - private checkOverencumberedCondition(container: Container, owner: Actor) { if (!this.hasCondition(ConditionTypeEnum.OVERENCUMBERED) && container.computeTotalWeight() >= container.capacity * OVERENCUMBERED_THRESHOLD) { - this.addCondition(Condition.create({ nbTurns: -1, type: ConditionTypeEnum.OVERENCUMBERED }), owner); + this.addCondition(Condition.create({ + nbTurns: -1, + type: ConditionTypeEnum.OVERENCUMBERED, + noCorpse: true, + }), owner); } else if (this.hasCondition(ConditionTypeEnum.OVERENCUMBERED) && container.computeTotalWeight() < container.capacity * OVERENCUMBERED_THRESHOLD) { this.removeCondition(ConditionTypeEnum.OVERENCUMBERED); @@ -434,10 +444,10 @@ export class PlayerAi extends Ai { } // note : this time is spent before you select the target. loading the projectile takes time owner.wait(weapon.ranged.loadTime); - weapon.ranged.fire(owner).then((fired: boolean) => { - if ( fired ) { - owner.wait(this.walkTime); - } + weapon.ranged.fire(owner, weapon).then((_fired: boolean) => { + // if ( fired ) { + // owner.wait(this.walkTime); + // } }); } @@ -458,7 +468,7 @@ export class PlayerAi extends Ai { return; } staff.magic.zap(staff, owner).then((_zapped: boolean) => { - owner.wait(this.walkTime); + //owner.wait(this.walkTime); }); } @@ -481,7 +491,7 @@ export class PlayerAi extends Ai { private throwItem(owner: Actor, item: Actor) { if (item.pickable) { - item.pickable.throw(item, Actor.specialActors[SpecialActorsEnum.PLAYER]).then(() => { + item.pickable.throw(item, Actor.specialActors[SpecialActorsEnum.PLAYER], false).then(() => { owner.wait(this.walkTime); }); } diff --git a/src/fwk/actors/actor_def.ts b/src/fwk/actors/actor_def.ts index 933f7f7..291e59c 100644 --- a/src/fwk/actors/actor_def.ts +++ b/src/fwk/actors/actor_def.ts @@ -71,15 +71,16 @@ export enum EffectTypeEnum { export interface IEffectDef { type: EffectTypeEnum; - data?: IInstantHealthEffectDef | ITeleportEffectDef | IConditionEffectDef | IEventEffectDef; + /** effect can only affect one actor (arrow) or several (explosion) */ + singleActor?: boolean; } -export interface IEventEffectDef { +export interface IEventEffectDef extends IEffectDef { eventType: string; eventData: any; } -export interface IInstantHealthEffectDef { +export interface IInstantHealthEffectDef extends IEffectDef { amount: number; /** does this effect also work on deads (defult : false) */ canResurrect?: boolean; @@ -89,7 +90,7 @@ export interface IInstantHealthEffectDef { failureMessage?: string; } -export interface ITeleportEffectDef { +export interface ITeleportEffectDef extends IEffectDef { successMessage: string; } @@ -154,11 +155,13 @@ export interface IConditionDef { noDisplay?: boolean; /** for activable items */ onlyIfActive?: boolean; + /** only display on living creatures (for example confused) */ + noCorpse?: boolean; /** to override the condition's default name */ name?: string; } -export interface IConditionEffectDef { +export interface IConditionEffectDef extends IEffectDef { condition: IConditionDef; successMessage: string; } diff --git a/src/fwk/actors/actor_effect.ts b/src/fwk/actors/actor_effect.ts index 6fcd664..fc0b469 100644 --- a/src/fwk/actors/actor_effect.ts +++ b/src/fwk/actors/actor_effect.ts @@ -5,7 +5,7 @@ import * as Core from "../core/main"; import * as Umbra from "../umbra/main"; import * as Map from "../map/main"; import {Actor, SpecialActorsEnum} from "./actor"; -import {TargetSelectionMethodEnum, ITargetSelectorDef, IInstantHealthEffectDef, IConditionDef, +import {TargetSelectionMethodEnum, ITargetSelectorDef, IInstantHealthEffectDef, IConditionDef, IConditionEffectDef, IEventEffectDef} from "./actor_def"; import {transformMessage} from "./base"; import {Condition} from "./actor_condition"; @@ -174,6 +174,13 @@ export class TargetSelector { * ============================================================================= */ +export enum EffectResult { + SUCCESS, + FAILURE, + SUCCESS_AND_STOP, + FAILURE_AND_STOP +} + /** * Interface: IEffect * Some effect that can be applied to actors. The effect might be triggered by using an item or casting a spell. @@ -188,22 +195,37 @@ export interface IEffect { * Returns: * false if effect cannot be applied */ - applyTo(actor: Actor, coef: number): boolean; + applyTo(actor: Actor, coef: number): EffectResult; +} + +export abstract class Effect implements IEffect { + protected static booleanToEffectResult(result: boolean, singleActor: boolean): EffectResult { + if ( result ) { + return singleActor ? EffectResult.SUCCESS_AND_STOP : EffectResult.SUCCESS; + } else { + return singleActor ? EffectResult.FAILURE_AND_STOP : EffectResult.FAILURE; + } + } + + public abstract applyTo(actor: Actor, coef: number): EffectResult; } /** * Class: InstantHealthEffect * Add or remove health points. */ -export class InstantHealthEffect implements IEffect { +export class InstantHealthEffect extends Effect { private _amount: number = 0; private canResurrect: boolean = false; private successMessage: string|undefined; private failureMessage: string|undefined; + private _singleActor: boolean = false; get amount() { return this._amount; } + get singleActor() { return this._singleActor; } constructor(def: IInstantHealthEffectDef) { + super(); if ( def ) { this._amount = def.amount; this.successMessage = def.successMessage; @@ -211,35 +233,41 @@ export class InstantHealthEffect implements IEffect { if ( def.canResurrect !== undefined ) { this.canResurrect = def.canResurrect; } + if ( def.singleActor !== undefined ) { + this._singleActor = def.singleActor; + } } } - public applyTo(actor: Actor, coef: number = 1.0): boolean { + public applyTo(actor: Actor, coef: number = 1.0): EffectResult { if (!actor.destructible) { - return false; + return EffectResult.FAILURE; } if (this._amount > 0) { if (this.canResurrect || ! actor.destructible.isDead()) { - return this.applyHealingEffectTo(actor, coef); + return this.applyHealingEffectTo(actor, coef); } } return this.applyWoundingEffectTo(actor, coef); } - private applyHealingEffectTo(actor: Actor, coef: number = 1.0): boolean { + private applyHealingEffectTo(actor: Actor, coef: number = 1.0): EffectResult { let healPointsCount: number = actor.destructible.heal(actor, coef * this._amount); let wearer: Actor|undefined = actor.getWearer(); - let result: boolean = false; + let result: EffectResult = Effect.booleanToEffectResult(false, this._singleActor); if (healPointsCount > 0 && this.successMessage) { Umbra.logger.info(transformMessage(this.successMessage, actor, wearer, healPointsCount)); - result = true; + result = Effect.booleanToEffectResult(true, this._singleActor); } else if (healPointsCount <= 0 && this.failureMessage) { Umbra.logger.info(transformMessage(this.failureMessage, actor)); } return result; } - private applyWoundingEffectTo(actor: Actor, coef: number = 1.0): boolean { + private applyWoundingEffectTo(actor: Actor, coef: number = 1.0): EffectResult { + if ( actor.destructible.isDead()) { + return EffectResult.FAILURE; + } let realDefense: number = actor.destructible.computeRealDefense(actor); let damageDealt = -this._amount * coef - realDefense; let wearer: Actor|undefined = actor.getWearer(); @@ -248,7 +276,8 @@ export class InstantHealthEffect implements IEffect { } else if (damageDealt <= 0 && this.failureMessage) { Umbra.logger.info(transformMessage(this.failureMessage, actor, wearer)); } - return actor.destructible.takeDamage(actor, -this._amount * coef) > 0; + return Effect.booleanToEffectResult(actor.destructible.takeDamage(actor, -this._amount * coef) > 0, + this._singleActor); } } @@ -256,14 +285,15 @@ export class InstantHealthEffect implements IEffect { * Class: TeleportEffect * Teleport the target at a random location. */ -export class TeleportEffect implements IEffect { +export class TeleportEffect extends Effect { private successMessage: string|undefined; constructor(successMessage?: string) { + super(); this.successMessage = successMessage; } - public applyTo(actor: Actor, _coef: number = 1.0): boolean { + public applyTo(actor: Actor, _coef: number = 1.0): EffectResult { let x: number; let y: number; [x, y] = Map.Map.current.findRandomWamlkableCell(); @@ -271,7 +301,7 @@ export class TeleportEffect implements IEffect { if (this.successMessage) { Umbra.logger.info(transformMessage(this.successMessage, actor)); } - return true; + return EffectResult.SUCCESS_AND_STOP; } } @@ -279,19 +309,20 @@ export class TeleportEffect implements IEffect { * class: EventEffect * Sends an event */ -export class EventEffect implements IEffect { +export class EventEffect extends Effect { private eventType: string; private eventData: any; constructor(def: IEventEffectDef) { + super(); if ( def ) { this.eventType = def.eventType; this.eventData = def.eventData; } } - public applyTo(_actor: Actor, _coef: number = 1.0): boolean { + public applyTo(_actor: Actor, _coef: number = 1.0): EffectResult { Umbra.EventManager.publishEvent(this.eventType, this.eventData); - return true; + return EffectResult.SUCCESS; } } @@ -299,33 +330,36 @@ export class EventEffect implements IEffect { * Class: ConditionEffect * Add a condition to an actor. */ -export class ConditionEffect implements IEffect { +export class ConditionEffect extends Effect { private conditionDef: IConditionDef; private message: string|undefined; - constructor(def: IConditionDef, message?: string) { + private singleActor: boolean; + constructor(def: IConditionEffectDef, message?: string) { + super(); this.message = message; - this.conditionDef = def; + this.conditionDef = def.condition; + this.singleActor = def.singleActor || false; } - public applyTo(actor: Actor, _coef: number = 1.0): boolean { + public applyTo(actor: Actor, _coef: number = 1.0): EffectResult { if (!actor.ai) { - return false; + return EffectResult.FAILURE; } actor.ai.addCondition(Condition.create(this.conditionDef), actor); if (this.message) { Umbra.logger.info(transformMessage(this.message, actor)); } - return true; + return Effect.booleanToEffectResult(true, this.singleActor); } } -export class MapRevealEffect implements IEffect { - public applyTo(actor: Actor, _coef: number = 1.0): boolean { +export class MapRevealEffect extends Effect { + public applyTo(actor: Actor, _coef: number = 1.0): EffectResult { if (actor === Actor.specialActors[SpecialActorsEnum.PLAYER]) { Map.Map.current.reveal(); - return true; + return EffectResult.SUCCESS; } - return false; + return EffectResult.FAILURE; } } @@ -374,9 +408,13 @@ export class Effector { } for (let actor of actors) { - if (this._effect.applyTo(actor, this._coef)) { + let result: EffectResult = this._effect.applyTo(actor, this._coef); + if ( result === EffectResult.SUCCESS || result === EffectResult.SUCCESS_AND_STOP) { success = true; } + if ( result === EffectResult.SUCCESS_AND_STOP || result === EffectResult.FAILURE_AND_STOP) { + break; + } } if (this.destroyOnEffect && success && wearer && wearer.container) { owner.destroy(); diff --git a/src/fwk/actors/actor_factory.ts b/src/fwk/actors/actor_factory.ts index 8b65236..d42ad24 100644 --- a/src/fwk/actors/actor_factory.ts +++ b/src/fwk/actors/actor_factory.ts @@ -13,7 +13,7 @@ import {Ai, XpHolder, ItemAi, MonsterAi, PlayerAi} from "./actor_creature"; import {Light} from "./actor_light"; import {IInventoryItemPicker, ILootHandler} from "./actor_creature"; import {ITilePicker, Effector, TargetSelector, ConditionEffect, InstantHealthEffect, MapRevealEffect, - TeleportEffect, EventEffect} from "./actor_effect"; + TeleportEffect, EventEffect, IEffect} from "./actor_effect"; import {Condition} from "./actor_condition"; import {PERSISTENCE_ACTORS_SEQ_KEY} from "./base"; @@ -315,22 +315,22 @@ export class ActorFactory { return new TargetSelector(def); } - private static createEffect(def: IEffectDef) { + private static createEffect(def: IEffectDef): IEffect { switch (def.type) { case EffectTypeEnum.CONDITION: - let conditionData: IConditionEffectDef = def.data; - return new ConditionEffect(conditionData.condition, conditionData.successMessage); + let conditionData: IConditionEffectDef = def; + return new ConditionEffect(conditionData, conditionData.successMessage); case EffectTypeEnum.INSTANT_HEALTH: - let instantHealthData: IInstantHealthEffectDef = def.data; + let instantHealthData: IInstantHealthEffectDef = def; return new InstantHealthEffect(instantHealthData); case EffectTypeEnum.MAP_REVEAL: return new MapRevealEffect(); case EffectTypeEnum.TELEPORT: - let teleportData: ITeleportEffectDef = def.data; + let teleportData: ITeleportEffectDef = def; return new TeleportEffect(teleportData.successMessage); case EffectTypeEnum.EVENT : default: - let eventData: IEventEffectDef = def.data; + let eventData: IEventEffectDef = def; return new EventEffect(eventData); } } diff --git a/src/fwk/actors/actor_item.ts b/src/fwk/actors/actor_item.ts index fe75e15..094411c 100644 --- a/src/fwk/actors/actor_item.ts +++ b/src/fwk/actors/actor_item.ts @@ -93,7 +93,7 @@ export class Destructible implements IActorFeature { if ( coef === this.qualifiers.length ) { coef = this.qualifiers.length - 1; } - return this.qualifiers[coef] + owner.getname(count); + return owner.getname(count) + (this.qualifiers[coef] ? ", " + this.qualifiers[coef] : ""); } public computeRealDefense(owner: Actor): number { @@ -751,7 +751,8 @@ export class Pickable implements IActorFeature { * maxRange - maximum distance where the item can be thrown. * If not defined, max range is computed from the item's weight */ - public throw(owner: Actor, wearer: Actor, maxRange?: number): Promise { + public throw(owner: Actor, wearer: Actor, fromFire: boolean, + maxRange?: number, damageCoef?: number): Promise { return new Promise((resolve) => { if (!maxRange) { maxRange = owner.computeThrowRange(wearer); @@ -762,7 +763,7 @@ export class Pickable implements IActorFeature { wearer.ai.tilePicker.pickATile("Left-click where to throw the " + owner.name + ",\nor right-click to cancel.", new Core.Position(wearer.pos.x, wearer.pos.y), maxRange).then( (pos: Core.Position) => { - this.throwOnPos(owner, wearer, false, pos); + this.throwOnPos(owner, wearer, fromFire, pos, damageCoef); resolve(); }); } @@ -962,7 +963,7 @@ export class Ranged implements IActorFeature { } } - public fire(wearer: Actor): Promise { + public fire(wearer: Actor, weapon: Actor): Promise { return new Promise((resolve) => { let projectile: Actor|undefined = this.findCompatibleProjectile(wearer); if (!projectile) { @@ -974,7 +975,7 @@ export class Ranged implements IActorFeature { } else { this.projectileId = projectile.id; Umbra.logger.info(transformMessage("[The actor1] fire[s] [a actor2].", wearer, projectile)); - projectile.pickable.throw(projectile, wearer, this._range).then(() => { + projectile.pickable.throw(projectile, wearer, true, this._range, weapon.ranged.damageCoef).then(() => { resolve(true); }); } diff --git a/src/fwk/gui/widget.ts b/src/fwk/gui/widget.ts index 53da8d1..c0c6182 100644 --- a/src/fwk/gui/widget.ts +++ b/src/fwk/gui/widget.ts @@ -39,7 +39,20 @@ export class Widget extends Umbra.Node implements Umbra.IEventListener { this.setZOrder(1); } - public isModal() { return this.modal; } + /** + * warning : walking parent hierarchy, we can reach Umbra nodes that aren't Widgets + * and have no isModal / isActive methods. + */ + public isModal(): boolean { return this.modal + || (this.__parent === undefined || ( this.__parent).isModal === undefined + ? false : ( this.__parent).isModal()); } + + public isActive(): boolean { + return Widget.activeModal === undefined + || ((this.modal && this === Widget.activeModal) + || (this.__parent === undefined || ( this.__parent).isActive === undefined ? + false : ( this.__parent).isActive() )); + } public show() { if (this.modal) { @@ -322,7 +335,7 @@ export class Draggable extends OptionWidget implements Umbra.IEvent } public onStartDrag(_event: Umbra.IDragEvent) { - if ( this.isVisible() && this.containsAbsolute(Umbra.getMouseCellPosition())) { + if ( this.isVisible() && this.isActive() && this.containsAbsolute(Umbra.getMouseCellPosition())) { Draggable.drag = this; Draggable.dragZOrder = this.getZOrder(); Draggable.startDrag.set(Umbra.getMouseCellPosition()); @@ -418,7 +431,8 @@ export class DragTarget extends OptionWidget implements Umbra.IEven } public onDrop(draggable: Draggable) { - if (this.isVisible() && !this.contains(draggable) && this.containsAbsolute(Umbra.getMouseCellPosition())) { + if (this.isVisible() && this.isActive() && !this.contains(draggable) + && this.containsAbsolute(Umbra.getMouseCellPosition())) { if ( this.options.dropCallback) { this.options.dropCallback(draggable, this, this.options.data); } @@ -426,7 +440,7 @@ export class DragTarget extends OptionWidget implements Umbra.IEven } public onPostRender(con: Yendor.Console) { - if ( Draggable.isDragging() ) { + if ( Draggable.isDragging() && this.isActive() ) { let pos: Core.Position = new Core.Position(this.boundingBox.x, this.boundingBox.y); ( this.__parent).local2Abs(pos); con.clearBack(getConfiguration().color.backgroundActive, pos.x, pos.y, @@ -489,7 +503,7 @@ export class Button extends Label { public onRender(con: Yendor.Console) { let pos: Core.Position = new Core.Position(this.boundingBox.x, this.boundingBox.y); ( this.__parent).local2Abs(pos); - this.active = this.containsAbsolute(Umbra.getMouseCellPosition()); + this.active = this.isActive() && this.containsAbsolute(Umbra.getMouseCellPosition()); this.renderWithColor(con, pos, this.active ? getConfiguration().color.foregroundActive : getConfiguration().color.foreground, this.active ? getConfiguration().color.backgroundActive : getConfiguration().color.background, @@ -500,7 +514,7 @@ export class Button extends Label { } public onUpdate(_time: number) { - this.active = this.containsAbsolute(Umbra.getMouseCellPosition()); + this.active = this.isActive() && this.containsAbsolute(Umbra.getMouseCellPosition()); this.pressed = this.active && Umbra.wasMouseButtonReleased(Umbra.MouseButtonEnum.LEFT); if (this.pressed || (this.options.asciiShortcut && Umbra.wasCharPressed(this.options.asciiShortcut))) { diff --git a/src/fwk/yendor/persistence.ts b/src/fwk/yendor/persistence.ts index 4fd14d4..9d69fb3 100644 --- a/src/fwk/yendor/persistence.ts +++ b/src/fwk/yendor/persistence.ts @@ -160,7 +160,12 @@ export class JSONSerializer { // use generic loading method for (let field in jsonData) { if (jsonData.hasOwnProperty(field)) { - object[field] = this.loadFromData(jsonData[field]); + try { + object[field] = this.loadFromData(jsonData[field]); + } catch (err) { + console.log("ERROR loading field " + field + " with value " + jsonData[field] + + " on object " + object.constructor.name + " : " + err); + } } } } diff --git a/src/generogue/README.md b/src/generogue/README.md index 55a7b90..e10c0b0 100644 --- a/src/generogue/README.md +++ b/src/generogue/README.md @@ -26,6 +26,9 @@ The name `GeneRogue` is a reference to Kornel Kisielewicz's mammoth [GenRogue pr * look with mouse. UI also works with mouse # WORK IN PROGRESS + - being able to drop a stack (but not throw/use...) + - being able to drop a bag + - no in-between turn when firing/zapping # TODO @@ -47,7 +50,7 @@ Reminder for stuff that might be added to the game. Or not. * Gameplay - UI - - inventory : when dragging an item, highlight valid drop targets + - context menu to replace drop/throw commands - container qualified name : being able to configure pouch + gold pieces => gold pouch ? - item durability - grinding stone diff --git a/src/generogue/config_actors.ts b/src/generogue/config_actors.ts index 82948b4..fef0f8c 100644 --- a/src/generogue/config_actors.ts +++ b/src/generogue/config_actors.ts @@ -110,7 +110,7 @@ Actors.ActorFactory.registerActorDef({ blockWalk: true, destructible: { corpseChar: "%", - qualifiers: ["almost dead ", "badly wounded ", "wounded ", "lightly wounded ", ""], + qualifiers: ["almost dead", "badly wounded", "wounded", "lightly wounded", ""], }, }); @@ -229,12 +229,10 @@ Actors.ActorFactory.registerActorDef({ onUseEffector: { destroyOnEffect: true, effect: { - data: { - amount: 200, - canResurrect: true, - failureMessage: "The lantern is already full.", - successMessage: "[The actor2] refill[s2] [its2] lantern.", - }, + amount: 200, + canResurrect: true, + failureMessage: "The lantern is already full.", + successMessage: "[The actor2] refill[s2] [its2] lantern.", type: Actors.EffectTypeEnum.INSTANT_HEALTH, }, targetSelector: { @@ -361,11 +359,9 @@ Actors.ActorFactory.registerActorDef({ onThrowEffector: { destroyOnEffect: true, effect: { - data: { - amount: 3, - failureMessage: "The potion explodes on [the actor1] but it has no effect", - successMessage: "The potion explodes on [the actor1], healing [it] for [value1] hit points.", - }, + amount: 3, + failureMessage: "The potion explodes on [the actor1] but it has no effect", + successMessage: "The potion explodes on [the actor1], healing [it] for [value1] hit points.", type: Actors.EffectTypeEnum.INSTANT_HEALTH, }, targetSelector: { method: Actors.TargetSelectionMethodEnum.SELECTED_RANGE, radius: 1 }, @@ -373,11 +369,9 @@ Actors.ActorFactory.registerActorDef({ onUseEffector: { destroyOnEffect: true, effect: { - data: { - amount: 5, - failureMessage: "[The actor1] drink[s] the health potion but it has no effect", - successMessage: "[The actor1] drink[s] the health potion and regain[s] [value1] hit points.", - }, + amount: 5, + failureMessage: "[The actor1] drink[s] the health potion but it has no effect", + successMessage: "[The actor1] drink[s] the health potion and regain[s] [value1] hit points.", type: Actors.EffectTypeEnum.INSTANT_HEALTH, }, targetSelector: { method: Actors.TargetSelectionMethodEnum.WEARER }, @@ -395,15 +389,13 @@ Actors.ActorFactory.registerActorDef({ onThrowEffector: { destroyOnEffect: true, effect: { - data: { - condition: { - amount: 6, - name: "regeneration", - nbTurns: 12, - type: Actors.ConditionTypeEnum.HEALTH_VARIATION, - }, - successMessage: "The potion explodes on [the actor1].\nLife is flowing through [it].", + condition: { + amount: 6, + name: "regeneration", + nbTurns: 12, + type: Actors.ConditionTypeEnum.HEALTH_VARIATION, }, + successMessage: "The potion explodes on [the actor1].\nLife is flowing through [it].", type: Actors.EffectTypeEnum.CONDITION, }, targetSelector: { method: Actors.TargetSelectionMethodEnum.SELECTED_RANGE, radius: 1 }, @@ -411,16 +403,14 @@ Actors.ActorFactory.registerActorDef({ onUseEffector: { destroyOnEffect: true, effect: { - data: { - condition: { - amount: 10, - name: "regeneration", - nbTurns: 20, - type: Actors.ConditionTypeEnum.HEALTH_VARIATION, - }, - successMessage: "[The actor1] drink[s] the regeneration potion and feel[s]\n" - + "the life flowing through [it].", + condition: { + amount: 10, + name: "regeneration", + nbTurns: 20, + type: Actors.ConditionTypeEnum.HEALTH_VARIATION, }, + successMessage: "[The actor1] drink[s] the regeneration potion and feel[s]\n" + + "the life flowing through [it].", type: Actors.EffectTypeEnum.CONDITION, }, targetSelector: { method: Actors.TargetSelectionMethodEnum.WEARER }, @@ -446,11 +436,9 @@ Actors.ActorFactory.registerActorDef({ onUseEffector: { destroyOnEffect: true, effect: { - data: { - amount: -20, - successMessage: "A lightning bolt strikes [the actor1] with a loud thunder!\n" + - "The damage is [value1] hit points.", - }, + amount: -20, + successMessage: "A lightning bolt strikes [the actor1] with a loud thunder!\n" + + "The damage is [value1] hit points.", type: Actors.EffectTypeEnum.INSTANT_HEALTH, }, targetSelector: { method: Actors.TargetSelectionMethodEnum.CLOSEST_ENEMY, range: 5 }, @@ -466,10 +454,8 @@ Actors.ActorFactory.registerActorDef({ onUseEffector: { destroyOnEffect: true, effect: { - data: { - amount: -12, - successMessage: "[The actor1] get[s] burned for [value1] hit points.", - }, + amount: -12, + successMessage: "[The actor1] get[s] burned for [value1] hit points.", type: Actors.EffectTypeEnum.INSTANT_HEALTH, }, message: "A fireball explodes, burning everything within 3 tiles.", @@ -490,13 +476,12 @@ Actors.ActorFactory.registerActorDef({ onUseEffector: { destroyOnEffect: true, effect: { - data: { - condition: { - nbTurns: 12, - type: Actors.ConditionTypeEnum.CONFUSED, - }, - successMessage: "[The actor1's] eyes look vacant,\nas [it] start[s] to stumble around!", + condition: { + nbTurns: 12, + type: Actors.ConditionTypeEnum.CONFUSED, + noCorpse: true, }, + successMessage: "[The actor1's] eyes look vacant,\nas [it] start[s] to stumble around!", type: Actors.EffectTypeEnum.CONDITION, }, targetSelector: { method: Actors.TargetSelectionMethodEnum.SELECTED_ACTOR, range: 5 }, @@ -533,7 +518,7 @@ Actors.ActorFactory.registerActorDef({ name: ACTOR_TYPES.CONSUMABLE_LIGHT, abstract: true, destructible: { - qualifiers: ["burnt out ", "almost burnt out ", "half burnt ", "slightly burnt ", ""], + qualifiers: ["burnt out", "almost burnt out", "half burnt", "slightly burnt", ""], }, prototypes: [ACTOR_TYPES.PICKABLE_LIGHT], }); @@ -604,7 +589,7 @@ Actors.ActorFactory.registerActorDef({ name: ACTOR_TYPES.REFILLABLE_LIGHT, abstract: true, destructible: { - qualifiers: ["empty ", "almost empty ", "half empty ", "", ""], + qualifiers: ["empty", "almost empty", "half empty", "", ""], }, prototypes: [ACTOR_TYPES.PICKABLE_LIGHT], }); @@ -689,10 +674,8 @@ Actors.ActorFactory.registerActorDef({ onThrowEffector: { destroyOnEffect: false, effect: { - data: { - amount: -1, - successMessage: "The sword hits [the actor1] for [value1] hit points.", - }, + amount: -1, + successMessage: "The sword hits [the actor1] for [value1] hit points.", type: Actors.EffectTypeEnum.INSTANT_HEALTH, }, targetSelector: { method: Actors.TargetSelectionMethodEnum.ACTOR_ON_CELL }, @@ -710,10 +693,8 @@ Actors.ActorFactory.registerActorDef({ onThrowEffector: { destroyOnEffect: false, effect: { - data: { - amount: -4, - successMessage: "The sword hits [the actor1] for [value1] hit points.", - }, + amount: -4, + successMessage: "The sword hits [the actor1] for [value1] hit points.", type: Actors.EffectTypeEnum.INSTANT_HEALTH, }, targetSelector: { method: Actors.TargetSelectionMethodEnum.ACTOR_ON_CELL }, @@ -731,10 +712,8 @@ Actors.ActorFactory.registerActorDef({ onThrowEffector: { destroyOnEffect: false, effect: { - data: { - amount: -6, - successMessage: "The sword hits [the actor1] for [value1] hit points.", - }, + amount: -6, + successMessage: "The sword hits [the actor1] for [value1] hit points.", type: Actors.EffectTypeEnum.INSTANT_HEALTH, }, targetSelector: { method: Actors.TargetSelectionMethodEnum.ACTOR_ON_CELL }, @@ -752,10 +731,8 @@ Actors.ActorFactory.registerActorDef({ onThrowEffector: { destroyOnEffect: false, effect: { - data: { - amount: -10, - successMessage: "The sword hits [the actor1] for [value1] hit points.", - }, + amount: -10, + successMessage: "The sword hits [the actor1] for [value1] hit points.", type: Actors.EffectTypeEnum.INSTANT_HEALTH, }, targetSelector: { method: Actors.TargetSelectionMethodEnum.ACTOR_ON_CELL }, @@ -774,13 +751,12 @@ Actors.ActorFactory.registerActorDef({ onThrowEffector: { destroyOnEffect: false, effect: { - data: { - condition: { - nbTurns: 2, - type: Actors.ConditionTypeEnum.STUNNED, - }, - successMessage: "The shield hits [the actor1] and stuns [it]!", + condition: { + nbTurns: 2, + type: Actors.ConditionTypeEnum.STUNNED, + noCorpse: true, }, + successMessage: "The shield hits [the actor1] and stuns [it]!", type: Actors.EffectTypeEnum.CONDITION, }, targetSelector: { method: Actors.TargetSelectionMethodEnum.ACTOR_ON_CELL }, @@ -833,11 +809,10 @@ Actors.ActorFactory.registerActorDef({ onThrowEffector: { destroyOnEffect: false, effect: { - data: { - amount: -1, - successMessage: "The arrow hits [the actor1] for [value1] points.", - }, + amount: -1, + successMessage: "The arrow hits [the actor1] for [value1] points.", type: Actors.EffectTypeEnum.INSTANT_HEALTH, + singleActor: true, }, targetSelector: { method: Actors.TargetSelectionMethodEnum.ACTOR_ON_CELL }, }, @@ -853,11 +828,10 @@ Actors.ActorFactory.registerActorDef({ onThrowEffector: { destroyOnEffect: false, effect: { - data: { - amount: -1.25, - successMessage: "The arrow hits [the actor1] for [value1] points.", - }, + amount: -1.25, + successMessage: "The arrow hits [the actor1] for [value1] points.", type: Actors.EffectTypeEnum.INSTANT_HEALTH, + singleActor: true, }, targetSelector: { method: Actors.TargetSelectionMethodEnum.ACTOR_ON_CELL }, }, @@ -873,11 +847,10 @@ Actors.ActorFactory.registerActorDef({ onThrowEffector: { destroyOnEffect: false, effect: { - data: { - amount: -0.5, - successMessage: "The bolt hits [the actor1] for [value1] points.", - }, + amount: -0.5, + successMessage: "The bolt hits [the actor1] for [value1] points.", type: Actors.EffectTypeEnum.INSTANT_HEALTH, + singleActor: true, }, targetSelector: { method: Actors.TargetSelectionMethodEnum.ACTOR_ON_CELL }, }, @@ -953,13 +926,12 @@ Actors.ActorFactory.registerActorDef({ onFireEffect: { destroyOnEffect: false, effect: { - data: { - condition: { - nbTurns: 10, - type: Actors.ConditionTypeEnum.FROZEN, - }, - successMessage: "[The actor1] [is] covered with frost.", + condition: { + nbTurns: 10, + type: Actors.ConditionTypeEnum.FROZEN, + noDisplay: true, }, + successMessage: "[The actor1] [is] covered with frost.", type: Actors.EffectTypeEnum.CONDITION, }, message: "[The actor1] zap[s] [its] wand.", @@ -1002,8 +974,9 @@ Actors.ActorFactory.registerActorDef({ onFireEffect: { destroyOnEffect: false, effect: { - data: { successMessage: "[The actor1] disappear[s] suddenly." }, + successMessage: "[The actor1] disappear[s] suddenly.", type: Actors.EffectTypeEnum.TELEPORT, + singleActor: true, }, message: "[The actor1] zap[s] [its] staff.", targetSelector: { method: Actors.TargetSelectionMethodEnum.SELECTED_ACTOR, range: 5 }, @@ -1019,14 +992,12 @@ Actors.ActorFactory.registerActorDef({ onFireEffect: { destroyOnEffect: false, effect: { - data: { - condition: { - nbTurns: 30, - range: 15, - type: Actors.ConditionTypeEnum.DETECT_LIFE, - }, - successMessage: "[The actor1] [is] aware of life around [it].", + condition: { + nbTurns: 30, + range: 15, + type: Actors.ConditionTypeEnum.DETECT_LIFE, }, + successMessage: "[The actor1] [is] aware of life around [it].", type: Actors.EffectTypeEnum.CONDITION, }, message: "[The actor1] zap[s] [its] staff.", @@ -1126,10 +1097,8 @@ Actors.ActorFactory.registerActorDef({ onActivateEffector: { destroyOnEffect: false, effect: { - data: { - eventData: GameStatus.NEXT_LEVEL, - eventType: EVENT_CHANGE_STATUS, - }, + eventData: GameStatus.NEXT_LEVEL, + eventType: EVENT_CHANGE_STATUS, type: Actors.EffectTypeEnum.EVENT, }, targetSelector: { diff --git a/src/generogue/engine.ts b/src/generogue/engine.ts index d892dab..d4db229 100644 --- a/src/generogue/engine.ts +++ b/src/generogue/engine.ts @@ -41,7 +41,7 @@ import {registerPersistentClasses} from "./config_persistent"; * to keep the game from trying to load data with an old format. * This should be an integer. */ -const SAVEFILE_VERSION: string = "17"; +const SAVEFILE_VERSION: string = "18"; export abstract class DungeonScene extends Map.MapScene implements Umbra.IEventListener { protected _topotologyMap: Map.TopologyMap; diff --git a/src/generogue/gui_inventory.ts b/src/generogue/gui_inventory.ts index 12b57df..0c49a87 100644 --- a/src/generogue/gui_inventory.ts +++ b/src/generogue/gui_inventory.ts @@ -532,15 +532,15 @@ export class MultiSlotContainerPanel extends SlotContainerPanel { private setCurrentCreature(index: number) { this.currentCreature = index; super.initForCreature(this.creatures[index], this.resolve, this.autoHideWidget, this.itemClassFilter); - if ( this.creatures.length > 1 ) { + if (this.creatures.length > 1) { + let title: string = this.creatures[index].getthename() + " " + (index + 1) + "/" + + this.creatures.length + " "; + this.setFrameTitle(title); this.nextItemButton.hide(); this.prevItemButton.hide(); this.computeBoundingBox(); - let title: string|undefined = this.getFrameTitle(); - if (title) { - this.nextItemButton.moveTo(Math.floor((this.boundingBox.w + title.length) / 2) - 1, 0); - this.prevItemButton.moveTo(Math.floor((this.boundingBox.w - title.length) / 2), 0); - } + this.nextItemButton.moveTo(Math.floor((this.boundingBox.w + title.length) / 2) - 1, 0); + this.prevItemButton.moveTo(Math.floor((this.boundingBox.w - title.length) / 2), 0); this.prevItemButton.show(); this.nextItemButton.show(); } else {