diff --git a/src/engine/world/actor/actor.ts b/src/engine/world/actor/actor.ts index 008877a4e..40dabfb8d 100644 --- a/src/engine/world/actor/actor.ts +++ b/src/engine/world/actor/actor.ts @@ -7,15 +7,11 @@ import { DefensiveBonuses, OffensiveBonuses, SkillBonuses } from '@engine/config import { Position, DirectionData, directionFromIndex, WorldInstance, activeWorld } from '@engine/world'; import { Item, ItemContainer } from '@engine/world/items'; import { ActionCancelType, ActionPipeline } from '@engine/action'; -import { soundIds } from '@engine/world/config'; import { WalkingQueue } from './walking-queue'; -import { Animation, DamageType, Graphic, UpdateFlags } from './update-flags'; -import { Skill, Skills } from './skills'; +import { Animation, Graphic, UpdateFlags } from './update-flags'; +import { Skills } from './skills'; import { Pathfinding } from './pathfinding'; -import { Attack, AttackDamageType } from './player/attack'; -import { Behavior } from './behaviors'; -import { Effect, EffectType } from './effect'; import { ActorMetadata } from './metadata'; @@ -52,15 +48,6 @@ export abstract class Actor { public pathfinding: Pathfinding = new Pathfinding(this); public lastMovementPosition: Position; - // #region Behaviors and Combat flags/checks - public inCombat: boolean = false; - public meleeDistance: number = 1; - public Behaviors: Behavior[] = []; - public isDead: boolean = false; - public combatTargets: Actor[] = []; - public hitPoints = this.skills.hitpoints.level * 4; - public maxHitPoints = this.skills.hitpoints.level * 4; - public effects: Effect[] = []; //spells, effects, prayers, etc protected randomMovementInterval; protected _instance: WorldInstance = null; @@ -75,7 +62,6 @@ export abstract class Actor { private _walkDirection: number; private _runDirection: number; private _faceDirection: number; - private _damageType = AttackDamageType.Crush; private _bonuses: { offensive: OffensiveBonuses, defensive: DefensiveBonuses, skill: SkillBonuses }; protected constructor(actorType: ActorType) { @@ -100,159 +86,6 @@ export abstract class Actor { }; } - public get highestCombatSkill(): Skill { - const attack = this.skills.getLevel('attack'); - const magic = this.skills.getLevel('magic'); - const ranged = this.skills.getLevel('ranged'); - - if (ranged > magic && ranged > ranged) return ranged; - else if (magic > attack && magic > ranged) return magic; - else return attack; - } - - //https://oldschool.runescape.wiki/w/Attack_range#:~:text=All%20combat%20magic%20spells%20have,also%20allow%20longrange%20attack%20style - // range should be within 10 tiles for magic - // range should be within 7 for magic staff - // https://www.theoatrix.net/post/how-defence-works-in-osrs - // https://oldschool.runescape.wiki/w/Damage_per_second/Magic - // https://oldschool.runescape.wiki/w/Successful_hit - // https://oldschool.runescape.wiki/w/Combat_level#:~:text=Calculating%20combat%20level,-Simply&text=Add%20your%20Strength%20and%20Attack,have%20your%20melee%20combat%20level.&text=Multiply%20this%20by%200.325%20and,have%20your%20magic%20combat%20level - // https://oldschool.runescape.wiki/w/Damage_per_second/Melee#:~:text=1%20Step%20one%3A%20Calculate%20the%20effective%20strength%20level%3B,1.7%20Step%20seven%3A%20Calculate%20the%20melee%20damage%20output - public getAttackRoll(defender): Attack { - - //the amount of damage is random from 0 to Max - //stance modifiers - const _stance_defense = 3; - const _stance_accurate = 0; - const _stance_controlled = 1; - - // base level - // ToDo: calculate prayer effects - // round decimal result calulcation up - // add 8 - // ToDo: add void bonues (effects) - // round result down - let equipmentBonus = this.bonuses.offensive.crush ?? 0; - if (equipmentBonus <= 0) { - equipmentBonus = 1; - } - /* - * To calculate your maximum hit: - - Effective strength level - Multiply by(Equipment Melee Strength + 64) - Add 320 - Divide by 640 - Round down to nearest integer - Multiply by gear bonus - Round down to nearest integer - */ - const stanceModifier = _stance_accurate; - const strengthLevel = (this.skills.attack.level + stanceModifier + 8); - let attackCalc = strengthLevel * (equipmentBonus + 64) + 320; - attackCalc = Math.round(attackCalc / 640); - //console.log(`strengthLevel = ${strengthLevel} \r\n attackCalc = ${attackCalc} \r\n equipmentBonus = ${equipmentBonus}`); - const maximumHit = Math.round(attackCalc * equipmentBonus); - - /* - To calculate your effective attack level: - - (Attack level + Attack level boost) * prayer bonus - Round down to nearest integer - + 3 if using the accurate attack style, +1 if using controlled - + 8 - Multiply by 1.1 if wearing void - Round down to nearest integer - */ - const attackLevel = this.skills.attack.level; - let effectiveAttackLevel = attackLevel; - - //Prayer/Effect bonus - calculate ALL the good and bad effects at once! (prayers, and magic effects, etc.) - this.effects.filter(a => a.EffectType === EffectType.Attack).forEach((effect) => { - effectiveAttackLevel += (attackLevel * effect.Modifier); - }); - effectiveAttackLevel = Math.round(effectiveAttackLevel) + stanceModifier; - - /* - * Calculate the Attack roll - Effective attack level * (Equipment Attack bonus + 64) - Multiply by gear bonus - Round down to nearest integer - * */ - let attack = new Attack(); - attack.damageType = this.damageType ?? AttackDamageType.Crush; - attack.attackRoll = Math.round(effectiveAttackLevel * (equipmentBonus + 64)); - attack = defender.getDefenseRoll(attack); - attack.maximumHit = maximumHit; - if (attack.attackRoll >= attack.defenseRoll) attack.hitChance = 1 - ((attack.defenseRoll + 2) / (2 * (attack.attackRoll + 1))) - if (attack.attackRoll < attack.defenseRoll) attack.hitChance = attack.attackRoll / (2 * attack.defenseRoll + 1); - - attack.damage = Math.round((maximumHit * attack.hitChance) / 2); - return attack; - } - - public getDefenseRoll(attack: Attack): Attack { - //attack need to know the damage roll, which is the item bonuses the weapon damage type etc. - - - //stance modifiers - const _stance_defense = 3; - const _stance_accurate = 0; - const _stance_controlled = 1; - - // base level - // calculate prayer effects - // round decimal result calculation up - // add 8 - // ToDo: add void bonuses (effects) - // round result down - - const equipmentBonus: number = this.bonuses.defensive.crush ?? 0; //object prototyping to find property by name (JS style =/) - - const stanceModifier: number = _stance_accurate; - - - attack.defenseRoll = (this.skills.defence.level + stanceModifier + 8) * (equipmentBonus + 64); - //Prayer/Effect bonus - calculate ALL the good and bad effects at once! (prayers, and magic effects, etc.) - this.effects.filter(a => a.EffectType === EffectType.BoostDefence || a.EffectType === EffectType.LowerDefence).forEach((effect) => { - attack.defenseRoll += (this.skills.defence.level * effect.Modifier); - }); - attack.defenseRoll = Math.round(attack.defenseRoll); - return attack; - //+ stance modifier - } - // #endregion - - public damage(amount: number, damageType: DamageType = DamageType.DAMAGE) { - const armorReduction = 0; - const spellDamageReduction = 0; - const poisonReistance = 0; - amount -= armorReduction; - this.hitPoints -= amount; - this.skills.setHitpoints(this.hitPoints); - this.updateFlags.addDamage(amount, amount === 0 ? DamageType.NO_DAMAGE : damageType, - this.hitPoints, this.maxHitPoints); - //this actor should respond when hit - activeWorld.playLocationSound(this.position, soundIds.npc.human.noArmorHitPlayer,5) - this.playAnimation(this.getBlockAnimation()); - } - - - - //public damage(amount: number, damageType: DamageType = DamageType.DAMAGE): 'alive' | 'dead' { - // let remainingHitpoints: number = this.skills.hitpoints.level - amount; - // const maximumHitpoints: number = this.skills.hitpoints.levelForExp; - // if(remainingHitpoints < 0) { - // remainingHitpoints = 0; - // } - - // this.skills.setHitpoints(remainingHitpoints); - // this.updateFlags.addDamage(amount, amount === 0 ? DamageType.NO_DAMAGE : damageType, - // remainingHitpoints, maximumHitpoints); - - // return remainingHitpoints === 0 ? 'dead' : 'alive'; - //} - /** * Waits for the actor to reach the specified position before resolving it's promise. * The promise will be rejected if the actor's walking queue changes or their movement is otherwise canceled. @@ -401,28 +234,6 @@ export abstract class Actor { return true; } - public tail(target: Actor): void { - this.face(target, false, false, false); - - if(this.metadata.tailing && this.metadata.tailing.equals(target)) { - return; - } - - this.metadata.tailing = target; - - this.moveTo(target); - const subscription = target.walkingQueue.movementEvent.subscribe(async () => this.moveTo(target)); - - this.actionsCancelled.pipe( - filter(type => type !== 'pathing-movement'), - take(1) - ).subscribe(() => { - subscription.unsubscribe(); - this.face(null); - delete this.metadata.tailing; - }); - } - public face(face: Position | Actor | null, clearWalkingQueue: boolean = true, autoClear: boolean = true, clearedByWalking: boolean = true): void { if(face === null) { this.clearFaceActor(); @@ -628,8 +439,6 @@ export abstract class Actor { return true; } - public abstract getAttackAnimation(): number; - public abstract getBlockAnimation(): number; public abstract equals(actor: Actor): boolean; public get position(): Position { @@ -684,14 +493,6 @@ export abstract class Actor { this._faceDirection = value; } - public get damageType() { - return this._damageType; - } - - public set damageType(value) { - this._damageType = value; - } - public get busy(): boolean { return this._busy; } diff --git a/src/engine/world/actor/behaviors/auto-attack.behavior.ts b/src/engine/world/actor/behaviors/auto-attack.behavior.ts deleted file mode 100644 index f67a5c458..000000000 --- a/src/engine/world/actor/behaviors/auto-attack.behavior.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { Actor } from '../actor'; -import { Behavior, BehaviorType } from './behavior'; -import { Npc } from '../npc'; -import { Player } from '../player/player'; - -export class AutoAttackBehavior extends Behavior { - - Type = BehaviorType.Combat; - Name = 'auto-attack-combat'; - - private _combatPulse; - private _CoolDown: number = 3; - private _lastAttack = new Date(); - private _player: Player; - - //this should be called when combat starts - public async init(me: Actor, them: Actor): Promise { - this.Me = me; - this.Them = them; - this._player = (me as Player); - console.log('all set to auto attack!'); - (this.Them as Npc).npcEvents.on('death', (npc) => this._player.onNpcKill); - await super.init(me, them); - } - - public async tick() { - if (this.Me == null) return; - - return new Promise(resolve => { - //only use boolean checks in behaviors never calculated values if you can help it (performance) - if (this.Me.inCombat) { - if (this.Them.isDead) { - //ToDo: this is the last one on the stack not neccessarily your current target. - this.Me.combatTargets.pop(); - resolve(); - return; - } - if (this.Distance > 25) { - this.Me.inCombat = false; - console.log('target too far away - ending combat'); - resolve(); - } - - //If we are not in range move there - if (this.Distance > this.Me.meleeDistance) this.Me.moveTo(this.Them); - //Are you in range to attack? - if (this.Distance <= this.Me.meleeDistance && this.coolDownElapsed) { - this.doAttack(); - this.resetCoolDown(this._CoolDown); - } - - if (!this.Me.isDead) super.tick(); - resolve(); - } - - - }); - } - public async doAttack(): Promise { - return new Promise(resolve => { - //do attack stuff - const attack = this.Me.getAttackRoll(this.Them); - console.log(`you attack ${(this.Them as Npc).name} for ${attack.damage} damage! (after the CD)`); - this.Them.damage(attack.damage); - if (this.Them.hitPoints <= 0) { - (this.Them as Npc).npcEvents.emit('death', this.Me, this.Them); - } - - }); - } - public get coolDownElapsed(): boolean { - if (new Date() > this._lastAttack) return true; - return false; - } - public resetCoolDown(seconds: number): void { - this._lastAttack.setSeconds(this._lastAttack.getSeconds() + seconds); - } - - -} diff --git a/src/engine/world/actor/behaviors/behavior.ts b/src/engine/world/actor/behaviors/behavior.ts deleted file mode 100644 index f8d4db5bb..000000000 --- a/src/engine/world/actor/behaviors/behavior.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Actor } from '../actor'; - - -export abstract class Behavior { - //because not all interaction between npcs will be combat oriented me/them is on the base class - public Me: Actor; - public Them: Actor; - public Distance: number; - public Name: string; - public Type: BehaviorType; - //Be sure to call the tick super so you wont have to duplicate all the functionality - public async tick() { - if (this.Me.isDead) return; - //If we have more than one combat target be sure to select the next one - if (this.Them == null && this.Me != null && this.Me.combatTargets.length > 0) this.Them == this.Me.combatTargets[0]; - //update calculated variables - if (this.Them != null) this.Distance = this.Me.position.distanceBetween(this.Them.position); - - return new Promise(resolve => { resolve(); }); - } - public async init(me: Actor = null, them: Actor = null) { - if (me != null && them != null) me.combatTargets.push(them); - return new Promise(resolve => { - resolve(); - }); - } -} - - -export enum BehaviorType { - //movement - Roaming = 'roaming', //world.tickComplete - Chase = 'chase', //position.distanceBetween - //combat - Combat = 'combat', - Flee = 'flee', - -} diff --git a/src/engine/world/actor/behaviors/index.ts b/src/engine/world/actor/behaviors/index.ts deleted file mode 100644 index 703fd13d8..000000000 --- a/src/engine/world/actor/behaviors/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './auto-attack.behavior'; -export * from './melee-combat.behavior'; -export * from './behavior'; diff --git a/src/engine/world/actor/behaviors/melee-combat.behavior.ts b/src/engine/world/actor/behaviors/melee-combat.behavior.ts deleted file mode 100644 index 13f19dd02..000000000 --- a/src/engine/world/actor/behaviors/melee-combat.behavior.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { Actor } from '../actor'; -import { Behavior, BehaviorType } from './behavior'; -import { Player } from '../player/player'; -import { Npc } from '../npc'; - -//This is exclusively for NPCs - use Auto-attack for players -export class MeleeCombatBehavior extends Behavior { - - Type = BehaviorType.Combat; - Name = 'basic-melee-combat'; - //seconds - private _CoolDown: number = 4; - private _lastAttack = new Date(); - - //this should be called when combat starts - public async init(me: Actor, them: Actor): Promise { - this.Me = me; - this.Them = them; - await super.init(me, them); - } - - public async tick() { - if (this.Me == null) return; - - return new Promise(resolve => { - if (this.Me.inCombat && this.Me.hitPoints <= 0) { - - this.Me.inCombat = false; - this.Me.isDead = true; - (this.Me as Npc).kill(false); - if (this.Them.isNpc) (this.Them as Player).playerEvents.emit('kill', this.Them); - - } - //only use boolean checks in behaviors never calculated values if you can help it (performance) - if (this.Me.inCombat) { - if (this.Distance > 5) { - this.Me.inCombat = false; - console.log('You or your target has fled from combat!'); - resolve(); - } - - //If we are not in range move there - if (this.Distance > this.Me.meleeDistance) this.Me.moveTo(this.Them); - //Are you in range to attack? - if (this.Distance <= this.Me.meleeDistance && this.coolDownElapsed) { - this.doAttack(); - this.resetCoolDown(this._CoolDown); - } - - if (!this.Me.isDead) super.tick(); - resolve(); - } - - - }); - } - public async doAttack():Promise { - return new Promise(resolve => { - //do attack stuff - this.Me.playAnimation(this.Me.getAttackAnimation()); - const _damage = this.Me.getAttackRoll(this.Them).damage; - console.log(`${(this.Me as Npc).name} attacks ${(this.Them as Player).username} for ${_damage} damage! (after the CD)`); - (this.Me as Npc).npcEvents.emit('damage', _damage); - this.Them.damage(_damage); - }); - } - public get coolDownElapsed(): boolean { - if (new Date() > this._lastAttack) return true; - return false; - } - public resetCoolDown(seconds:number):void { - this._lastAttack.setSeconds(this._lastAttack.getSeconds() + seconds); - } - - -} diff --git a/src/engine/world/actor/effect.ts b/src/engine/world/actor/effect.ts deleted file mode 100644 index f0cb24e73..000000000 --- a/src/engine/world/actor/effect.ts +++ /dev/null @@ -1,24 +0,0 @@ - -export abstract class Effect { - public Name: string; - public EffectType: EffectType; - public EffectId: number; - public Modifier: number =0; - -} - -export enum EffectType { - Strength, - Defense, - Attack, - Magic, - Ranged, - RecoverStats, - HPRestoreRate, - Curse, - Poison, - Fire, - EnvironmentDamage, - BoostDefence = 11, - LowerDefence = 12 -} diff --git a/src/engine/world/actor/index.ts b/src/engine/world/actor/index.ts index ccfe64623..6f77d870c 100644 --- a/src/engine/world/actor/index.ts +++ b/src/engine/world/actor/index.ts @@ -1,6 +1,5 @@ export * from './combat'; export * from './dialogue'; -export * from './effect'; export * from './magic'; export * from './pathfinding'; export * from './prayer'; diff --git a/src/engine/world/actor/metadata.ts b/src/engine/world/actor/metadata.ts index 7a86f7fee..550b28103 100644 --- a/src/engine/world/actor/metadata.ts +++ b/src/engine/world/actor/metadata.ts @@ -25,13 +25,6 @@ export type ActorMetadata = { */ walkingTo: Position; - /** - * The actor currently being `tailed` by this actor. - * - * TODO (jameskmonger) we should delete this - only used by deleted code in the old combat plugin - */ - tailing: Actor; - /** * The actor currently being followed by this actor. */ diff --git a/src/engine/world/actor/npc.ts b/src/engine/world/actor/npc.ts index 07e125312..4de8df707 100644 --- a/src/engine/world/actor/npc.ts +++ b/src/engine/world/actor/npc.ts @@ -9,7 +9,6 @@ import { soundIds, animationIds } from '@engine/world/config'; import { Actor } from './actor'; import { Player } from './player'; import { SkillName } from './skills'; -import { MeleeCombatBehavior } from './behaviors'; /** * Represents a non-player character within the game world. @@ -99,8 +98,7 @@ export class Npc extends Actor { } else { this._name = 'Unknown'; } - // ToDo: this should be config based and not always melee (obviously) - this.Behaviors.push(new MeleeCombatBehavior()); + this.npcEvents.on('death', this.processDeath); } @@ -148,21 +146,6 @@ export class Npc extends Actor { || y > this.initialPosition.y + this.movementRadius || y < this.initialPosition.y - this.movementRadius); } - public getAttackAnimation(): number { - let attackAnim = findNpc(this.id)?.combatAnimations?.attack || animationIds.combat.punch; - if(Array.isArray(attackAnim)) { - // NPC has multiple attack animations possible, pick a random one from the list to use - const idx = Math.floor(Math.random() * attackAnim.length); - attackAnim = attackAnim[idx]; - } - - return attackAnim; - } - - public getBlockAnimation(): number { - return findNpc(this.id)?.combatAnimations?.defend || animationIds.combat.armBlock; - } - public kill(respawn: boolean = true): void { activeWorld.chunkManager.getChunkForWorldPosition(this.position).removeNpc(this); @@ -175,9 +158,6 @@ export class Npc extends Actor { } public async tick(): Promise { - for (let i = 0; i < this.Behaviors.length; i++) { - this.Behaviors[i].tick(); - } return new Promise(resolve => { this.walkingQueue.process(); resolve(); @@ -203,7 +183,7 @@ export class Npc extends Actor { * Whether or not the Npc can currently move. */ public canMove(): boolean { - if(this.metadata.following || this.metadata.tailing) { + if(this.metadata.following) { return false; } return this.updateFlags.faceActor === undefined && this.updateFlags.animation === undefined; diff --git a/src/engine/world/actor/player/player.ts b/src/engine/world/actor/player/player.ts index bf214507a..f91927aac 100644 --- a/src/engine/world/actor/player/player.ts +++ b/src/engine/world/actor/player/player.ts @@ -37,13 +37,9 @@ import { Cutscene } from './cutscenes'; import { InterfaceState } from '@engine/interface'; import { Quest } from './quest'; import { SendMessageOptions } from './model'; -import { AttackDamageType } from './attack'; -import { EffectType } from '../effect'; -import { AutoAttackBehavior } from '../behaviors'; import { PlayerSyncTask, NpcSyncTask } from './sync'; import { dialogue } from '../dialogue'; import { Npc } from '../npc'; -import { combatStyles } from '../combat'; import { SkillName } from '../skills'; import { PlayerMetadata } from './metadata'; @@ -276,8 +272,6 @@ export class Player extends Actor { this.chunkChanged(playerChunk); } - this.Behaviors.push(new AutoAttackBehavior()); - this.outgoingPackets.flushQueue(); logger.info(`${this.username}:${this.worldIndex} has logged in.`); } @@ -311,28 +305,6 @@ export class Player extends Actor { savePlayerData(this); } - public getAttackAnimation(): number { - let combatStyle = [ 'unarmed', 0 ]; - - if(this.savedMetadata.combatStyle) { - combatStyle = this.savedMetadata.combatStyle; - } - - let attackAnim = combatStyles[combatStyle[0]][combatStyle[1]]?.anim || animationIds.combat.punch; - - if(Array.isArray(attackAnim)) { - // Player has multiple attack animations possible, pick a random one from the list to use - const idx = Math.floor(Math.random() * attackAnim.length); - attackAnim = attackAnim[idx]; - } - - return animationIds.combat[attackAnim] || animationIds.combat.kick; - } - - public getBlockAnimation(): number { - return animationIds.combat.armBlock; // @TODO - } - public privateMessageReceived(fromPlayer: Player, messageBytes: number[]): void { this.outgoingPackets.sendPrivateMessage(this.privateMessageIndex++, fromPlayer, messageBytes); } @@ -399,9 +371,6 @@ export class Player extends Actor { } public async tick(): Promise { - for (let i = 0; i < this.Behaviors.length; i++) { - this.Behaviors[i].tick(); - } return new Promise(resolve => { this.walkingQueue.process(); @@ -807,10 +776,6 @@ export class Player extends Actor { this.addBonuses(item); } - //prayers and other effects that effect strength - this.effects.filter(a => a.EffectType === EffectType.Strength).forEach((effect) => { - this.bonuses.skill['strength'] += this.skills.strength.level * effect.Modifier; - }); } public sendLogMessage(message: string, isConsole: boolean): void { @@ -1375,15 +1340,6 @@ export class Player extends Actor { return this._nearbyChunks; } - public get damageType(): AttackDamageType { - if (this.bonuses.offensive.crush > 0) return AttackDamageType.Crush; - if (this.bonuses.offensive.magic > 0) return AttackDamageType.Magic; - if (this.bonuses.offensive.ranged > 0) return AttackDamageType.Range; - if (this.bonuses.offensive.slash > 0) return AttackDamageType.Slash; - if (this.bonuses.offensive.stab > 0) return AttackDamageType.Stab; - return AttackDamageType.Crush; - } - public get instance(): WorldInstance { return super.instance; } diff --git a/src/engine/world/actor/prayer.ts b/src/engine/world/actor/prayer.ts index 72870e044..18c042362 100644 --- a/src/engine/world/actor/prayer.ts +++ b/src/engine/world/actor/prayer.ts @@ -1,7 +1,5 @@ -import { Effect, EffectType } from './effect'; - -export class Prayer extends Effect { +export class Prayer { AnimationId: number; SoundId: number; ButtonId: number; -} \ No newline at end of file +} diff --git a/src/engine/world/config/index.ts b/src/engine/world/config/index.ts index f90336466..ca52a4a6f 100644 --- a/src/engine/world/config/index.ts +++ b/src/engine/world/config/index.ts @@ -5,7 +5,6 @@ export * from './harvest-tool'; export * from './harvestable-object'; export * from './item-ids'; export * from './object-ids'; -export * from './prayers'; export * from './scenery-spawns'; export * from './songs'; export * from './sound-ids'; diff --git a/src/engine/world/config/prayers.ts b/src/engine/world/config/prayers.ts deleted file mode 100644 index 710010dd8..000000000 --- a/src/engine/world/config/prayers.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { EffectType } from '../actor/effect'; -import { Prayer } from '../actor/prayer'; -import { soundIds } from './sound-ids'; - -export const prayers: Prayer[] = [ - { - Name: 'Thick Skin', - EffectType: EffectType.Defense, - AnimationId: 645, - EffectId: 0, //graphic id - SoundId: soundIds.prayer.thick_skin, - Modifier: 0.05, //ie 5% = 0.05 - ButtonId: 0 - }, - { - Name: 'Burst of Strength', - EffectType: EffectType.Strength, - AnimationId: 645, - EffectId: 0, //graphic id - SoundId: soundIds.prayer.burst_of_strength, - Modifier: 0.05, //ie 5% = 0.05 - ButtonId: 1, - }, - { - Name: 'Clarity of Thought', - EffectType: EffectType.Attack, - AnimationId: 645, - EffectId: 0, //graphic id - SoundId: soundIds.prayer.clarity_of_thought, - Modifier: 0.05, //ie 5% = 0.05 - ButtonId: 2 - }, - { - Name: 'Sharp Eye', - EffectType: EffectType.Ranged, - AnimationId: 645, - EffectId: 0, //graphic id - SoundId: soundIds.prayer.sharp_eye, - Modifier: 0.05, //ie 5% = 0.05 - ButtonId: 36 - }, - { - Name: 'Mystic Will', - EffectType: EffectType.Magic, - AnimationId: 645, - EffectId: 0, //graphic id - SoundId: soundIds.prayer.mystic_will, - Modifier: 0.05, //ie 5% = 0.05 - ButtonId: 38 - }, - { - Name: 'Rock Skin', - EffectType: EffectType.Defense, - AnimationId: 645, - EffectId: 0, //graphic id - SoundId: soundIds.prayer.rock_skin, - Modifier: 0.1, //ie 5% = 0.05 - ButtonId: 3 - }, - { - Name: 'Superhuman Strength', - EffectType: EffectType.Strength, - AnimationId: 645, - EffectId: 0, //graphic id - SoundId: soundIds.prayer.superhuman_strength, - Modifier: 0.1, //ie 5% = 0.05 - ButtonId: 4 - }, - { - Name: 'Improved Reflexes', - EffectType: EffectType.Attack, - AnimationId: 645, - EffectId: 0, //graphic id - SoundId: soundIds.prayer.improved_reflexes, - Modifier: 0.1, //ie 5% = 0.05 - ButtonId: 5 - }, - { //This is not going to work - recover stats? what does that even mean? - Name: 'Rapid Restore', - EffectType: EffectType.RecoverStats, - AnimationId: 645, - EffectId: 0, //graphic id - SoundId: soundIds.prayer.rapid_restore, - Modifier: 0.1, //ie 5% = 0.05 - ButtonId: 6 - }, - { - Name: 'Rapid Heal', - EffectType: EffectType.HPRestoreRate, - AnimationId: 645, - EffectId: 0, //graphic id - SoundId: soundIds.prayer.rapid_restore, - Modifier: 1, //ie 5% = 0.05 - ButtonId: 7 - } -]; \ No newline at end of file diff --git a/src/plugins/buttons/prayer.plugin.ts b/src/plugins/buttons/prayer.plugin.ts deleted file mode 100644 index 979fedf55..000000000 --- a/src/plugins/buttons/prayer.plugin.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { TaskExecutor, PrayerAction, PrayerActionHook } from '@engine/action'; -import { widgets } from '@engine/config'; -import { prayers } from '@engine/world/config'; -const buttonIds: number[] = [ - 0, // Home Teleport - 1, - 2, - 36, - 38, - 3 -]; - - -export const activate = (task: TaskExecutor, elapsedTicks: number = 0) => { - const { - player, - widgetId, - buttonId - } = task.actionData; - - prayers.filter(a => a.ButtonId == buttonId).forEach((prayer) => { - player.playAnimation(prayer.AnimationId); - player.playSound(prayer.SoundId); - player.effects.push(prayer); - }); - - console.log(`${player.username} casts ${prayers[buttonId]}`); - -}; - -export default { - pluginId: 'rs:prayer', - hooks: - { - type: 'button', - widgetId: widgets.prayerTab, - buttonIds: buttonIds, - task: { - activate, - interval: 0 - } - } as PrayerActionHook -}; diff --git a/src/plugins/combat/combat.old.ts b/src/plugins/combat/combat.old.ts deleted file mode 100644 index 664652602..000000000 --- a/src/plugins/combat/combat.old.ts +++ /dev/null @@ -1,208 +0,0 @@ -import { Actor } from '@engine/world/actor/actor'; -import { Player } from '@engine/world/actor/player/player'; -import { lastValueFrom, timer } from 'rxjs'; -import { World } from '@engine/world'; -import { filter, take } from 'rxjs/operators'; -import { animationIds } from '@engine/world/config/animation-ids'; -import { Npc } from '@engine/world/actor/npc'; -import { itemIds } from '@engine/world/config/item-ids'; -import { soundIds } from '@engine/world/config/sound-ids'; -import { findNpc } from '@engine/config/config-handler'; -import { activeWorld } from '@engine/world'; - - -class Combat { - - public readonly assailant: Actor; - public readonly victim: Actor; - public combatActive: boolean = false; - public contactInitiated: boolean = false; - - - public constructor(actor: Actor, victim: Actor) { - this.assailant = actor; - this.victim = victim; - } - - public cancelCombat(): void { - this.contactInitiated = false; - this.combatActive = false; - this.assailant.actionsCancelled.next(null); - this.victim.actionsCancelled.next(null); - } - - public async initiateCombat(): Promise { - this.combatActive = true; - await this.processAttacker(true); - - this.assailant.actionsCancelled.pipe( - filter(type => type !== 'pathing-movement'), - take(1) - ).toPromise().then(() => { - this.cancelCombat(); - }); - - this.victim.actionsCancelled.pipe( - filter(type => type !== 'pathing-movement'), - take(1) - ).toPromise().then(() => { - this.cancelCombat(); - }); - } - - public async processAttacker(firstAttack: boolean = false): Promise { - if (!this.combatActive) { - return; - } - - await this.assailant.tail(this.victim); - - if (!firstAttack) { - await lastValueFrom(timer(4 * World.TICK_LENGTH).pipe(take(1))); - } - - if (!this.combatActive) { - return; - } - - this.damage(this.assailant, this.victim); - - if (!this.contactInitiated) { - this.contactInitiated = true; - - if (this.victim instanceof Npc) { - const player = this.assailant as Player; - player.sendMessage(`Victim max health is ${this.victim.skills.hitpoints.level}.`) - } - - this.processVictim(true); - } - - this.processAttacker(); - } - - public async processVictim(firstAttack: boolean = false): Promise { - if (!this.combatActive) { - return; - } - - await this.victim.tail(this.assailant); - - if (!firstAttack) { - await timer(6 * World.TICK_LENGTH).toPromise(); - } else { - await timer(World.TICK_LENGTH).toPromise(); - } - - if (!this.combatActive) { - return; - } - - this.damage(this.victim, this.assailant); - this.processVictim(); - } - - public damage(attacker: Actor, defender: Actor): void { - const skills = attacker.skills; - const strengthBonus = (attacker instanceof Player) ? attacker.bonuses?.skill?.strength || 0 : 0; - const maxHit = this.meleeMaxHit(skills.strength.levelForExp, skills.strength.level, strengthBonus, 1); - const actualHit = Math.floor(Math.random() * (maxHit + 1)); - const attackAnim: number = attacker.getAttackAnimation(); - - // Animate attacking the opponent and play the sound of them defending - attacker.playAnimation(attackAnim); - activeWorld.playLocationSound(defender.position, defender instanceof Player ? soundIds.npc.human.playerDefence : - soundIds.npc.human.maleDefence, 5); - - defender.damage(actualHit); - /*const defenderState: 'alive' | 'dead' = 'alive'; - - if (defenderState === 'dead') { - // @TODO death sounds - this.processDeath(defender, attacker); - } else { - // Play the sound of the defender being hit or blocking - activeWorld.playLocationSound(defender.position, defender instanceof Player ? soundIds.npc.human.noArmorHitPlayer : - soundIds.npc.human.noArmorHit, 5); - defender.playAnimation(defender.getBlockAnimation()); - }*/ - } - - public async processDeath(victim: Actor, assailant?: Actor): Promise { - const deathPosition = victim.position; - - let deathAnim: number = animationIds.death; - - if (victim instanceof Npc) { - deathAnim = findNpc(victim.id)?.combatAnimations?.death || animationIds.death - } - - this.cancelCombat(); - victim.playAnimation(deathAnim); - activeWorld.playLocationSound(deathPosition, soundIds.npc.human.maleDeath, 5); - - await timer(2 * World.TICK_LENGTH).toPromise(); - - let instance = activeWorld.globalInstance; - if (victim instanceof Npc) { - victim.kill(true); - - if (assailant && assailant instanceof Player) { - instance = assailant.instance; - } - } else if (victim instanceof Player) { - // @TODO - instance = victim.instance; - } - - instance.spawnWorldItem(itemIds.bones.normal, deathPosition, - { owner: this.assailant instanceof Player ? this.assailant : undefined, expires: 300 }); - } - - // https://forum.tip.it/topic/199687-runescape-formulas-revealed - public meleeMaxHit(strengthBase: number, strengthCurrent: number, strengthBonus: number, specialMultiplier: number): number { - const finalStrength = strengthCurrent/* + fightStyleStrengthBoost + - (strengthBase * prayerBoost) + (strengthBase * effectBoost) + - (itemSet == Dharok ? (hpBase - hpCurrent) : 0)*/; - const strengthMultiplier = (strengthBonus * 0.00175) + 0.1; - const maxHit = Math.floor((finalStrength * strengthMultiplier) + 1.05); - return Math.floor(Math.floor(Math.floor(maxHit)/* * itemSet.getMultiplier()*/) * specialMultiplier); - } - -} - -//const attackNpcAction: npcInteractionActionHandler = details => { -// const { player, npc } = details; - -// //const combatInstance = new Combat(player, npc); -// //await combatInstance.initiateCombat(); -//}; - -//export const activate = async (task: TaskExecutor, elapsedTicks: number = 0) => { -// const { player, npc, position, option } = task.actionData; - -// let completed: boolean = false; - -// console.log("starto"); -// completed = true; -// wait(100); -// if (completed) { -// task.stop(); -// } -//}; - - -//export default { -// pluginId: 'rs:combat', -// hooks: [ -// { -// type: 'npc_interaction', -// options: 'attack', -// walkTo: true, -// task: { -// activate, -// interval: 1 -// } -// } -// ] -//}; diff --git a/src/plugins/combat/combat.plugin.ts b/src/plugins/combat/combat.plugin.ts deleted file mode 100644 index f0515f727..000000000 --- a/src/plugins/combat/combat.plugin.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { NpcInteractionAction, TaskExecutor } from '@engine/action'; -import { BehaviorType } from '@engine/world/actor/behaviors'; - - -//Kicking off combat - all subsequent action will be handed off to behaviors -export const activate = async (task: TaskExecutor, elapsedTicks: number = 0) => { - const { player, npc, position, option } = task.actionData; - - console.log(`Kicking off combat - all subsequent action will be handed off to behaviors`); - - npc.npcEvents.on('combatStart', (npc, player) => { - //this is for NPC - if it has MULTIPLE combat behaviors it will activate them all (which is the point) - npc.Behaviors.filter(a => a.Type == BehaviorType.Combat).forEach(async function (combatBehavior) { - console.log(`initting ${combatBehavior.Name}`); - await combatBehavior.init(npc, player); - npc.inCombat = true; - }); - }); - - player.playerEvents.on('combatStart', (player, npc) => { - //this is for auto attack for a player - later on if they are mind controlled or confused or even scripted this would be for that too - player.Behaviors.filter(a => a.Type == BehaviorType.Combat).forEach(async function (combatBehavior) { - console.log(`initting ${combatBehavior.Name}`); - await combatBehavior.init(player, npc); - player.inCombat = true; - }); - }) - - player.playerEvents.emit('combatStart', player, npc); - npc.npcEvents.emit('combatStart', npc, player); - await task.stop(); -}; - - - -export default { - pluginId: 'rs:combat', - hooks: [ - { - type: 'npc_interaction', - options: 'attack', - walkTo: false, - task: { - activate, - } - } - ] -};