mirror of
https://github.com/unanmed/HumanBreak.git
synced 2026-11-13 09:44:30 +08:00
feat: 跟进 2.A 的更改
This commit is contained in:
1 parent
0dfb7e4b99
commit
86e6e76286
38 files changed
+851
-493
No files matched your search
+48
-120
@@ -4,6 +4,7 @@ import { loading } from '../game';
|
||||
|
||||
export interface CurrentEnemy {
|
||||
enemy: DamageEnemy;
|
||||
// 这个是干啥的?
|
||||
onMapEnemy: DamageEnemy[];
|
||||
}
|
||||
|
||||
@@ -16,155 +17,64 @@ export function getEnemy(
|
||||
return v.x === x && v.y === y;
|
||||
});
|
||||
if (!enemy) {
|
||||
throw new Error(
|
||||
`Get null when getting enemy on '${x},${y}' in '${floorId}'`
|
||||
);
|
||||
return null;
|
||||
}
|
||||
return enemy;
|
||||
}
|
||||
|
||||
function init() {
|
||||
core.enemys.canBattle = function canBattle(
|
||||
x: number,
|
||||
x: number | DamageEnemy,
|
||||
y: number,
|
||||
floorId: FloorIds = core.status.floorId
|
||||
) {
|
||||
const enemy = getEnemy(x, y, floorId);
|
||||
const enemy = typeof x === 'number' ? getEnemy(x, y, floorId) : x;
|
||||
if (!enemy) {
|
||||
throw new Error(
|
||||
`Cannot get enemy on x:${x}, y:${y}, floor: ${floorId}`
|
||||
);
|
||||
}
|
||||
const { damage } = enemy.calDamage();
|
||||
|
||||
return damage < core.status.hero.hp;
|
||||
};
|
||||
|
||||
core.events.battle = function battle(
|
||||
x: number,
|
||||
x: number | DamageEnemy,
|
||||
y: number,
|
||||
force: boolean = false,
|
||||
callback?: () => void
|
||||
) {
|
||||
core.saveAndStopAutomaticRoute();
|
||||
const enemy = getEnemy(x, y);
|
||||
const isLoc = typeof x === 'number';
|
||||
const enemy = isLoc ? getEnemy(x, y) : x;
|
||||
if (!enemy) {
|
||||
throw new Error(
|
||||
`Cannot battle with enemy since no enemy on ${x},${y}`
|
||||
);
|
||||
}
|
||||
// 非强制战斗
|
||||
// @ts-ignore
|
||||
if (!core.canBattle(x, y) && !force && !core.status.event.id) {
|
||||
core.stopSound();
|
||||
core.playSound('操作失败');
|
||||
core.drawTip('你打不过此怪物!', enemy.id);
|
||||
core.drawTip('你打不过此怪物!', enemy!.id);
|
||||
return core.clearContinueAutomaticRoute(callback);
|
||||
}
|
||||
// 自动存档
|
||||
if (!core.status.event.id) core.autosave(true);
|
||||
// 战前事件
|
||||
// 战后事件
|
||||
core.afterBattle(enemy, x, y);
|
||||
core.afterBattle(enemy, isLoc ? x : enemy.x, y);
|
||||
callback?.();
|
||||
};
|
||||
|
||||
core.events.afterBattle = function afterBattle(
|
||||
enemy: DamageEnemy,
|
||||
x?: number,
|
||||
y?: number
|
||||
) {
|
||||
const floorId = core.status.floorId;
|
||||
const special = enemy.info.special;
|
||||
const getFacedId = (enemy: DamageEnemy) => {
|
||||
const e = enemy.enemy;
|
||||
|
||||
// 播放战斗动画
|
||||
let animate: AnimationIds = 'hand';
|
||||
// 检查当前装备是否存在攻击动画
|
||||
const equipId = core.getEquip(0);
|
||||
if (equipId && (core.material.items[equipId].equip || {}).animate)
|
||||
animate = core.material.items[equipId].equip.animate;
|
||||
|
||||
// 检查该动画是否存在SE,如果不存在则使用默认音效
|
||||
if (!core.material.animates[animate]?.se) core.playSound('attack.mp3');
|
||||
|
||||
// 战斗伤害
|
||||
const info = enemy.calDamage(core.status.hero);
|
||||
const damage = info.damage;
|
||||
// 判定是否致死
|
||||
if (damage >= core.status.hero.hp) {
|
||||
core.status.hero.hp = 0;
|
||||
core.updateStatusBar(false, true);
|
||||
core.events.lose('战斗失败');
|
||||
return;
|
||||
}
|
||||
|
||||
// 扣减体力值并记录统计数据
|
||||
core.status.hero.hp -= damage;
|
||||
core.status.hero.statistics.battleDamage += damage;
|
||||
core.status.hero.statistics.battle++;
|
||||
|
||||
// 智慧之源
|
||||
if (special.includes(14) && flags.hard === 2) {
|
||||
core.addFlag(
|
||||
'inte_' + floorId,
|
||||
Math.ceil((core.status.hero.mdef / 10) * 0.3) * 10
|
||||
);
|
||||
core.status.hero.mdef -=
|
||||
Math.ceil((core.status.hero.mdef / 10) * 0.3) * 10;
|
||||
}
|
||||
|
||||
// 极昼永夜
|
||||
if (special.includes(22)) {
|
||||
flags[`night_${floorId}`] ??= 0;
|
||||
flags[`night_${floorId}`] -= enemy.enemy.night!;
|
||||
}
|
||||
if (special.includes(23)) {
|
||||
flags[`night_${floorId}`] ??= 0;
|
||||
flags[`night_${floorId}`] += enemy.enemy.day;
|
||||
}
|
||||
|
||||
// if (core.plugin.skillTree.getSkillLevel(11) > 0) {
|
||||
// core.plugin.study.declineStudiedSkill();
|
||||
// }
|
||||
|
||||
// 如果是融化怪,需要特殊标记一下
|
||||
if (special.includes(25) && has(x) && has(y)) {
|
||||
flags[`melt_${floorId}`] ??= {};
|
||||
flags[`melt_${floorId}`][`${x},${y}`] = enemy.enemy.melt;
|
||||
}
|
||||
|
||||
// 获得金币
|
||||
const money = enemy.enemy.money;
|
||||
core.status.hero.money += money;
|
||||
core.status.hero.statistics.money += money;
|
||||
|
||||
// 获得经验
|
||||
const exp = enemy.enemy.exp;
|
||||
core.status.hero.exp += exp;
|
||||
core.status.hero.statistics.exp += exp;
|
||||
|
||||
const hint =
|
||||
'打败 ' + enemy.enemy.name + ',金币+' + money + ',经验+' + exp;
|
||||
core.drawTip(hint, enemy.id);
|
||||
|
||||
if (core.getFlag('bladeOn') && core.getFlag('blade')) {
|
||||
core.setFlag('blade', false);
|
||||
}
|
||||
if (core.getFlag('shieldOn') && core.getFlag('shield')) {
|
||||
core.setFlag('shield', false);
|
||||
}
|
||||
|
||||
// 事件的处理
|
||||
const todo: MotaEvent = [];
|
||||
|
||||
// 战后事件
|
||||
if (has(core.status.floorId)) {
|
||||
const loc = `${x},${y}` as LocString;
|
||||
todo.push(
|
||||
...(core.floors[core.status.floorId].afterBattle[loc] ?? [])
|
||||
);
|
||||
}
|
||||
todo.push(...(enemy.enemy.afterBattle ?? []));
|
||||
|
||||
// 如果事件不为空,将其插入
|
||||
if (todo.length > 0) core.insertAction(todo, x, y);
|
||||
|
||||
if (has(x) && has(y)) {
|
||||
core.drawAnimate(animate, x, y);
|
||||
core.removeBlock(x, y);
|
||||
} else core.drawHeroAnimate(animate);
|
||||
|
||||
// 如果已有事件正在处理中
|
||||
if (core.status.event.id == null) core.continueAutomaticRoute();
|
||||
else core.clearContinueAutomaticRoute();
|
||||
if (e.displayIdInBook) return e.displayIdInBook;
|
||||
if (e.faceIds) return e.faceIds.down;
|
||||
return e.id;
|
||||
};
|
||||
|
||||
core.enemys.getCurrentEnemys = function getCurrentEnemys(
|
||||
@@ -176,7 +86,8 @@ function init() {
|
||||
ensureFloorDamage(floorId);
|
||||
const floor = core.status.maps[floorId];
|
||||
floor.enemy.list.forEach(v => {
|
||||
if (!(v.id in used)) {
|
||||
const id = getFacedId(v);
|
||||
if (!(id in used)) {
|
||||
const e = new DamageEnemy(v.enemy);
|
||||
e.calAttribute();
|
||||
e.getRealInfo();
|
||||
@@ -186,9 +97,9 @@ function init() {
|
||||
onMapEnemy: [v]
|
||||
};
|
||||
enemys.push(curr);
|
||||
used[v.id] = curr.onMapEnemy;
|
||||
used[id] = curr.onMapEnemy;
|
||||
} else {
|
||||
used[v.id].push(v);
|
||||
used[id].push(v);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -207,7 +118,7 @@ function init() {
|
||||
const enemy = getEnemy(data.x, data.y);
|
||||
|
||||
beforeBattle.push(...(floor.beforeBattle[loc] ?? []));
|
||||
beforeBattle.push(...(enemy.enemy.beforeBattle ?? []));
|
||||
beforeBattle.push(...(enemy!.enemy.beforeBattle ?? []));
|
||||
|
||||
if (beforeBattle.length > 0) {
|
||||
beforeBattle.push({ type: 'battle', x: data.x, y: data.y });
|
||||
@@ -251,5 +162,22 @@ loading.once('coreInit', init);
|
||||
declare global {
|
||||
interface Enemys {
|
||||
getCurrentEnemys(floorId: FloorIds): CurrentEnemy[];
|
||||
canBattle(enemy: DamageEnemy, _?: number, floorId?: FloorIds): boolean;
|
||||
canBattle(x: number, y: number, floorId?: FloorIds): boolean;
|
||||
}
|
||||
|
||||
interface Events {
|
||||
battle(
|
||||
enemy: DamageEnemy,
|
||||
_?: number,
|
||||
force?: boolean,
|
||||
callback?: () => void
|
||||
): void;
|
||||
battle(
|
||||
x: number,
|
||||
y?: number,
|
||||
force?: boolean,
|
||||
callback?: () => void
|
||||
): void;
|
||||
}
|
||||
}
|
||||
+35
-32
@@ -16,15 +16,15 @@ interface HaloType {
|
||||
};
|
||||
}
|
||||
|
||||
interface EnemyInfo {
|
||||
interface EnemyInfo extends Partial<Enemy> {
|
||||
atk: number;
|
||||
def: number;
|
||||
hp: number;
|
||||
special: number[];
|
||||
damageDecline: number;
|
||||
atkBuff: number;
|
||||
defBuff: number;
|
||||
hpBuff: number;
|
||||
atkBuff_: number;
|
||||
defBuff_: number;
|
||||
hpBuff_: number;
|
||||
enemy: Enemy;
|
||||
x?: number;
|
||||
y?: number;
|
||||
@@ -65,7 +65,7 @@ interface CriticalDamageDelta extends Omit<DamageDelta, 'info'> {
|
||||
type HaloFn = (info: EnemyInfo, enemy: Enemy) => void;
|
||||
|
||||
/** 光环属性 */
|
||||
export const haloSpecials: number[] = [8, 21, 25, 26, 27];
|
||||
export const haloSpecials: Set<number> = new Set([8, 21, 25, 26, 27]);
|
||||
|
||||
export class EnemyCollection implements RangeCollection<DamageEnemy> {
|
||||
floorId: FloorIds;
|
||||
@@ -302,7 +302,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
info!: EnemyInfo;
|
||||
|
||||
/** 向其他怪提供过的光环 */
|
||||
providedHalo: number[] = [];
|
||||
providedHalo: Set<number> = new Set();
|
||||
|
||||
/**
|
||||
* 伤害计算进度,0 -> 预平衡光环 -> 1 -> 计算没有光环的属性 -> 2 -> provide inject 光环
|
||||
@@ -334,16 +334,23 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
def: enemy.def,
|
||||
special: enemy.special.slice(),
|
||||
damageDecline: 0,
|
||||
atkBuff: 0,
|
||||
defBuff: 0,
|
||||
hpBuff: 0,
|
||||
atkBuff_: 0,
|
||||
defBuff_: 0,
|
||||
hpBuff_: 0,
|
||||
enemy: this.enemy,
|
||||
x: this.x,
|
||||
y: this.y,
|
||||
floorId: this.floorId
|
||||
};
|
||||
|
||||
for (const [key, value] of Object.entries(enemy)) {
|
||||
if (!(key in this.info) && has(value)) {
|
||||
// @ts-ignore
|
||||
this.info[key] = value;
|
||||
}
|
||||
}
|
||||
this.progress = 0;
|
||||
this.providedHalo = [];
|
||||
this.providedHalo.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -370,8 +377,8 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
for (const [loc, per] of Object.entries(flags[`melt_${floorId}`])) {
|
||||
const [mx, my] = loc.split(',').map(v => parseInt(v));
|
||||
if (Math.abs(mx - this.x) <= 1 && Math.abs(my - this.y) <= 1) {
|
||||
info.atkBuff += per as number;
|
||||
info.defBuff += per as number;
|
||||
info.atkBuff_ += per as number;
|
||||
info.defBuff_ += per as number;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -392,9 +399,9 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
// 此时已经inject光环,因此直接计算真实属性
|
||||
const info = this.info;
|
||||
|
||||
info.atk = Math.floor(info.atk * (info.atkBuff / 100 + 1));
|
||||
info.def = Math.floor(info.def * (info.defBuff / 100 + 1));
|
||||
info.hp = Math.floor(info.hp * (info.hpBuff / 100 + 1));
|
||||
info.atk = Math.floor(info.atk * (info.atkBuff_ / 100 + 1));
|
||||
info.def = Math.floor(info.def * (info.defBuff_ / 100 + 1));
|
||||
info.hp = Math.floor(info.hp * (info.hpBuff_ / 100 + 1));
|
||||
|
||||
return this.info;
|
||||
}
|
||||
@@ -404,7 +411,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
if (!has(this.x) || !has(this.y)) return [];
|
||||
const special = this.info.special ?? this.enemy.special;
|
||||
const filter = special.filter(v => {
|
||||
return haloSpecials.includes(v) && !this.providedHalo.includes(v);
|
||||
return haloSpecials.has(v) && !this.providedHalo.has(v);
|
||||
});
|
||||
if (filter.length === 0) return [];
|
||||
const collection = this.col ?? core.status.maps[this.floorId].enemy;
|
||||
@@ -448,11 +455,11 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
e.special.includes(8) &&
|
||||
(e.x !== this.x || this.y !== e.y)
|
||||
) {
|
||||
e.atkBuff += enemy.together ?? 0;
|
||||
e.defBuff += enemy.together ?? 0;
|
||||
e.atkBuff_ += enemy.together ?? 0;
|
||||
e.defBuff_ += enemy.together ?? 0;
|
||||
}
|
||||
});
|
||||
this.providedHalo.push(8);
|
||||
this.providedHalo.add(8);
|
||||
}
|
||||
|
||||
// 冰封光环
|
||||
@@ -460,7 +467,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
square7.push(e => {
|
||||
e.damageDecline += this.enemy.iceHalo ?? 0;
|
||||
});
|
||||
this.providedHalo.push(21);
|
||||
this.providedHalo.add(21);
|
||||
col.haloList.push({
|
||||
type: 'square',
|
||||
data: { x: this.x, y: this.y, d: 7 },
|
||||
@@ -472,9 +479,9 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
// 冰封之核
|
||||
if (special.includes(26)) {
|
||||
square5.push(e => {
|
||||
e.defBuff += this.enemy.iceCore ?? 0;
|
||||
e.defBuff_ += this.enemy.iceCore ?? 0;
|
||||
});
|
||||
this.providedHalo.push(26);
|
||||
this.providedHalo.add(26);
|
||||
col.haloList.push({
|
||||
type: 'square',
|
||||
data: { x: this.x, y: this.y, d: 5 },
|
||||
@@ -486,9 +493,9 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
// 火焰之核
|
||||
if (special.includes(27)) {
|
||||
square5.push(e => {
|
||||
e.atkBuff += this.enemy.fireCore ?? 0;
|
||||
e.atkBuff_ += this.enemy.fireCore ?? 0;
|
||||
});
|
||||
this.providedHalo.push(27);
|
||||
this.providedHalo.add(27);
|
||||
col.haloList.push({
|
||||
type: 'square',
|
||||
data: { x: this.x, y: this.y, d: 5 },
|
||||
@@ -617,7 +624,7 @@ export class DamageEnemy<T extends EnemyIds = EnemyIds> {
|
||||
) {
|
||||
damage[loc] ??= { damage: 0, type: new Set() };
|
||||
damage[loc].damage += dam;
|
||||
damage[loc].type.add(type);
|
||||
if (type) damage[loc].type.add(type);
|
||||
}
|
||||
|
||||
private calEnemyDamageOf(hero: Partial<HeroStatus>, enemy: EnemyInfo) {
|
||||
@@ -830,6 +837,9 @@ const skills: [unlock: string, condition: string][] = [
|
||||
['shieldOn', 'shield']
|
||||
];
|
||||
|
||||
const haloValue: Map<number, SelectKey<Enemy, number | undefined>[]> =
|
||||
new Map();
|
||||
|
||||
/**
|
||||
* 计算怪物伤害
|
||||
* @param info 怪物信息
|
||||
@@ -946,13 +956,6 @@ export function getSingleEnemy(id: EnemyIds) {
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface PluginDeclaration {
|
||||
damage: {
|
||||
Enemy: typeof DamageEnemy;
|
||||
Collection: typeof EnemyCollection;
|
||||
};
|
||||
}
|
||||
|
||||
interface Floor {
|
||||
enemy: EnemyCollection;
|
||||
}
|
||||
|
||||
+31
-14
@@ -84,14 +84,16 @@ export interface GameEvent extends EmitableEvent {
|
||||
mounted: () => void;
|
||||
/** Emitted in plugin/ui.js */
|
||||
statusBarUpdate: () => void;
|
||||
/** Emitted in libs/events.js */
|
||||
afterGetItem: (
|
||||
itemId: AllIdsOf<'items'>,
|
||||
x: number,
|
||||
y: number,
|
||||
isGentleClick: boolean
|
||||
) => void;
|
||||
afterOpenDoor: (doorId: AllIdsOf<'animates'>, x: number, y: number) => void;
|
||||
/** Emitted in core/index.ts */
|
||||
renderLoaded: () => void;
|
||||
// /** Emitted in libs/events.js */
|
||||
// afterGetItem: (
|
||||
// itemId: AllIdsOf<'items'>,
|
||||
// x: number,
|
||||
// y: number,
|
||||
// isGentleClick: boolean
|
||||
// ) => void;
|
||||
// afterOpenDoor: (doorId: AllIdsOf<'animates'>, x: number, y: number) => void;
|
||||
}
|
||||
|
||||
export const hook = new EventEmitter<GameEvent>();
|
||||
@@ -112,7 +114,7 @@ class GameListener extends EventEmitter<ListenerEvent> {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
if (main.replayChecking) return;
|
||||
if (!!window.core) {
|
||||
this.init();
|
||||
} else {
|
||||
@@ -131,14 +133,19 @@ class GameListener extends EventEmitter<ListenerEvent> {
|
||||
|
||||
const getBlockLoc = (px: number, py: number, size: number) => {
|
||||
return [
|
||||
Math.floor(((px * 32) / size - core.bigmap.offsetX) / 32),
|
||||
Math.floor(((py * 32) / size - core.bigmap.offsetY) / 32)
|
||||
Math.floor(((px * 32) / size + core.bigmap.offsetX) / 32),
|
||||
Math.floor(((py * 32) / size + core.bigmap.offsetY) / 32)
|
||||
];
|
||||
};
|
||||
|
||||
// hover & leave & mouseMove
|
||||
data.addEventListener('mousemove', e => {
|
||||
if (core.status.lockControl || !core.isPlaying()) return;
|
||||
if (
|
||||
core.status.lockControl ||
|
||||
!core.isPlaying() ||
|
||||
!core.status.floorId
|
||||
)
|
||||
return;
|
||||
this.emit('mouseMove', e);
|
||||
const {
|
||||
x: px,
|
||||
@@ -164,7 +171,12 @@ class GameListener extends EventEmitter<ListenerEvent> {
|
||||
}
|
||||
});
|
||||
data.addEventListener('mouseleave', e => {
|
||||
if (core.status.lockControl || !core.isPlaying()) return;
|
||||
if (
|
||||
core.status.lockControl ||
|
||||
!core.isPlaying() ||
|
||||
!core.status.floorId
|
||||
)
|
||||
return;
|
||||
const blocks = core.getMapBlocksObj();
|
||||
const lastBlock = blocks[`${lastHoverX},${lastHoverY}`];
|
||||
if (!!lastBlock) {
|
||||
@@ -175,7 +187,12 @@ class GameListener extends EventEmitter<ListenerEvent> {
|
||||
});
|
||||
// click
|
||||
data.addEventListener('click', e => {
|
||||
if (core.status.lockControl || !core.isPlaying()) return;
|
||||
if (
|
||||
core.status.lockControl ||
|
||||
!core.isPlaying() ||
|
||||
!core.status.floorId
|
||||
)
|
||||
return;
|
||||
const {
|
||||
x: px,
|
||||
y: py,
|
||||
|
||||
+2
-6
@@ -18,11 +18,7 @@ import type { Keyboard } from '@/core/main/custom/keyboard';
|
||||
import type { CustomToolbar } from '@/core/main/custom/toolbar';
|
||||
import type { Focus, GameUi, UiController } from '@/core/main/custom/ui';
|
||||
import type { gameListener, hook } from './game';
|
||||
import type {
|
||||
MotaSetting,
|
||||
SettingDisplayer,
|
||||
SettingStorage
|
||||
} from '@/core/main/setting';
|
||||
import type { MotaSetting, SettingDisplayer } from '@/core/main/setting';
|
||||
import type { GameStorage } from '@/core/main/storage';
|
||||
import type { DamageEnemy, EnemyCollection } from './enemy/damage';
|
||||
import type { specials } from './enemy/special';
|
||||
@@ -87,7 +83,7 @@ interface VariableInterface {
|
||||
sound: SoundController;
|
||||
resource: ResourceStore<Exclude<ResourceType, 'zip'>>;
|
||||
zipResource: ResourceStore<'zip'>;
|
||||
settingStorage: GameStorage<SettingStorage>;
|
||||
settingStorage: GameStorage;
|
||||
status: Ref<boolean>;
|
||||
// 定义于游戏进程,渲染进程依然可用
|
||||
haloSpecials: number[];
|
||||
|
||||
Reference in new issue
Block a user