您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
A Tampermonkey script to track MWI hits on Canvas
当前为
- // ==UserScript==
- // @name MWI-Hit-Tracker-Canvas
- // @namespace MWI-Hit-Tracker-Canvas
- // @version 1.1.3
- // @author Artintel, BKN46
- // @description A Tampermonkey script to track MWI hits on Canvas
- // @icon https://www.milkywayidle.com/favicon.svg
- // @include https://*.milkywayidle.com/*
- // @match https://www.milkywayidle.com/*
- // @license MIT
- // ==/UserScript==
- (function (exports) {
- 'use strict';
- var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
- var check = function (it) {
- return it && it.Math == Math && it;
- };
- // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
- var global$c =
- // eslint-disable-next-line es/no-global-this -- safe
- check(typeof globalThis == 'object' && globalThis) ||
- check(typeof window == 'object' && window) ||
- // eslint-disable-next-line no-restricted-globals -- safe
- check(typeof self == 'object' && self) ||
- check(typeof commonjsGlobal == 'object' && commonjsGlobal) ||
- // eslint-disable-next-line no-new-func -- fallback
- (function () { return this; })() || Function('return this')();
- var objectGetOwnPropertyDescriptor = {};
- var fails$8 = function (exec) {
- try {
- return !!exec();
- } catch (error) {
- return true;
- }
- };
- var fails$7 = fails$8;
- // Detect IE8's incomplete defineProperty implementation
- var descriptors = !fails$7(function () {
- // eslint-disable-next-line es/no-object-defineproperty -- required for testing
- return Object.defineProperty({}, 1, { get: function () { return 7; } })[1] != 7;
- });
- var objectPropertyIsEnumerable = {};
- var $propertyIsEnumerable = {}.propertyIsEnumerable;
- // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
- var getOwnPropertyDescriptor$1 = Object.getOwnPropertyDescriptor;
- // Nashorn ~ JDK8 bug
- var NASHORN_BUG = getOwnPropertyDescriptor$1 && !$propertyIsEnumerable.call({ 1: 2 }, 1);
- // `Object.prototype.propertyIsEnumerable` method implementation
- // https://tc39.es/ecma262/#sec-object.prototype.propertyisenumerable
- objectPropertyIsEnumerable.f = NASHORN_BUG ? function propertyIsEnumerable(V) {
- var descriptor = getOwnPropertyDescriptor$1(this, V);
- return !!descriptor && descriptor.enumerable;
- } : $propertyIsEnumerable;
- var createPropertyDescriptor$2 = function (bitmap, value) {
- return {
- enumerable: !(bitmap & 1),
- configurable: !(bitmap & 2),
- writable: !(bitmap & 4),
- value: value
- };
- };
- var toString = {}.toString;
- var classofRaw$1 = function (it) {
- return toString.call(it).slice(8, -1);
- };
- var fails$6 = fails$8;
- var classof$2 = classofRaw$1;
- var split = ''.split;
- // fallback for non-array-like ES3 and non-enumerable old V8 strings
- var indexedObject = fails$6(function () {
- // throws an error in rhino, see https://github.com/mozilla/rhino/issues/346
- // eslint-disable-next-line no-prototype-builtins -- safe
- return !Object('z').propertyIsEnumerable(0);
- }) ? function (it) {
- return classof$2(it) == 'String' ? split.call(it, '') : Object(it);
- } : Object;
- // `RequireObjectCoercible` abstract operation
- // https://tc39.es/ecma262/#sec-requireobjectcoercible
- var requireObjectCoercible$2 = function (it) {
- if (it == undefined) throw TypeError("Can't call method on " + it);
- return it;
- };
- // toObject with fallback for non-array-like ES3 strings
- var IndexedObject = indexedObject;
- var requireObjectCoercible$1 = requireObjectCoercible$2;
- var toIndexedObject$3 = function (it) {
- return IndexedObject(requireObjectCoercible$1(it));
- };
- // `IsCallable` abstract operation
- // https://tc39.es/ecma262/#sec-iscallable
- var isCallable$d = function (argument) {
- return typeof argument === 'function';
- };
- var isCallable$c = isCallable$d;
- var isObject$5 = function (it) {
- return typeof it === 'object' ? it !== null : isCallable$c(it);
- };
- var global$b = global$c;
- var isCallable$b = isCallable$d;
- var aFunction = function (argument) {
- return isCallable$b(argument) ? argument : undefined;
- };
- var getBuiltIn$4 = function (namespace, method) {
- return arguments.length < 2 ? aFunction(global$b[namespace]) : global$b[namespace] && global$b[namespace][method];
- };
- var getBuiltIn$3 = getBuiltIn$4;
- var engineUserAgent = getBuiltIn$3('navigator', 'userAgent') || '';
- var global$a = global$c;
- var userAgent = engineUserAgent;
- var process = global$a.process;
- var Deno = global$a.Deno;
- var versions = process && process.versions || Deno && Deno.version;
- var v8 = versions && versions.v8;
- var match, version;
- if (v8) {
- match = v8.split('.');
- version = match[0] < 4 ? 1 : match[0] + match[1];
- } else if (userAgent) {
- match = userAgent.match(/Edge\/(\d+)/);
- if (!match || match[1] >= 74) {
- match = userAgent.match(/Chrome\/(\d+)/);
- if (match) version = match[1];
- }
- }
- var engineV8Version = version && +version;
- /* eslint-disable es/no-symbol -- required for testing */
- var V8_VERSION = engineV8Version;
- var fails$5 = fails$8;
- // eslint-disable-next-line es/no-object-getownpropertysymbols -- required for testing
- var nativeSymbol = !!Object.getOwnPropertySymbols && !fails$5(function () {
- var symbol = Symbol();
- // Chrome 38 Symbol has incorrect toString conversion
- // `get-own-property-symbols` polyfill symbols converted to object are not Symbol instances
- return !String(symbol) || !(Object(symbol) instanceof Symbol) ||
- // Chrome 38-40 symbols are not inherited from DOM collections prototypes to instances
- !Symbol.sham && V8_VERSION && V8_VERSION < 41;
- });
- /* eslint-disable es/no-symbol -- required for testing */
- var NATIVE_SYMBOL$1 = nativeSymbol;
- var useSymbolAsUid = NATIVE_SYMBOL$1
- && !Symbol.sham
- && typeof Symbol.iterator == 'symbol';
- var isCallable$a = isCallable$d;
- var getBuiltIn$2 = getBuiltIn$4;
- var USE_SYMBOL_AS_UID$1 = useSymbolAsUid;
- var isSymbol$2 = USE_SYMBOL_AS_UID$1 ? function (it) {
- return typeof it == 'symbol';
- } : function (it) {
- var $Symbol = getBuiltIn$2('Symbol');
- return isCallable$a($Symbol) && Object(it) instanceof $Symbol;
- };
- var tryToString$1 = function (argument) {
- try {
- return String(argument);
- } catch (error) {
- return 'Object';
- }
- };
- var isCallable$9 = isCallable$d;
- var tryToString = tryToString$1;
- // `Assert: IsCallable(argument) is true`
- var aCallable$5 = function (argument) {
- if (isCallable$9(argument)) return argument;
- throw TypeError(tryToString(argument) + ' is not a function');
- };
- var aCallable$4 = aCallable$5;
- // `GetMethod` abstract operation
- // https://tc39.es/ecma262/#sec-getmethod
- var getMethod$4 = function (V, P) {
- var func = V[P];
- return func == null ? undefined : aCallable$4(func);
- };
- var isCallable$8 = isCallable$d;
- var isObject$4 = isObject$5;
- // `OrdinaryToPrimitive` abstract operation
- // https://tc39.es/ecma262/#sec-ordinarytoprimitive
- var ordinaryToPrimitive$1 = function (input, pref) {
- var fn, val;
- if (pref === 'string' && isCallable$8(fn = input.toString) && !isObject$4(val = fn.call(input))) return val;
- if (isCallable$8(fn = input.valueOf) && !isObject$4(val = fn.call(input))) return val;
- if (pref !== 'string' && isCallable$8(fn = input.toString) && !isObject$4(val = fn.call(input))) return val;
- throw TypeError("Can't convert object to primitive value");
- };
- var shared$3 = {exports: {}};
- var global$9 = global$c;
- var setGlobal$3 = function (key, value) {
- try {
- // eslint-disable-next-line es/no-object-defineproperty -- safe
- Object.defineProperty(global$9, key, { value: value, configurable: true, writable: true });
- } catch (error) {
- global$9[key] = value;
- } return value;
- };
- var global$8 = global$c;
- var setGlobal$2 = setGlobal$3;
- var SHARED = '__core-js_shared__';
- var store$3 = global$8[SHARED] || setGlobal$2(SHARED, {});
- var sharedStore = store$3;
- var store$2 = sharedStore;
- (shared$3.exports = function (key, value) {
- return store$2[key] || (store$2[key] = value !== undefined ? value : {});
- })('versions', []).push({
- version: '3.18.3',
- mode: 'global',
- copyright: '© 2021 Denis Pushkarev (zloirock.ru)'
- });
- var requireObjectCoercible = requireObjectCoercible$2;
- // `ToObject` abstract operation
- // https://tc39.es/ecma262/#sec-toobject
- var toObject$2 = function (argument) {
- return Object(requireObjectCoercible(argument));
- };
- var toObject$1 = toObject$2;
- var hasOwnProperty = {}.hasOwnProperty;
- // `HasOwnProperty` abstract operation
- // https://tc39.es/ecma262/#sec-hasownproperty
- var hasOwnProperty_1 = Object.hasOwn || function hasOwn(it, key) {
- return hasOwnProperty.call(toObject$1(it), key);
- };
- var id = 0;
- var postfix = Math.random();
- var uid$2 = function (key) {
- return 'Symbol(' + String(key === undefined ? '' : key) + ')_' + (++id + postfix).toString(36);
- };
- var global$7 = global$c;
- var shared$2 = shared$3.exports;
- var hasOwn$8 = hasOwnProperty_1;
- var uid$1 = uid$2;
- var NATIVE_SYMBOL = nativeSymbol;
- var USE_SYMBOL_AS_UID = useSymbolAsUid;
- var WellKnownSymbolsStore = shared$2('wks');
- var Symbol$1 = global$7.Symbol;
- var createWellKnownSymbol = USE_SYMBOL_AS_UID ? Symbol$1 : Symbol$1 && Symbol$1.withoutSetter || uid$1;
- var wellKnownSymbol$8 = function (name) {
- if (!hasOwn$8(WellKnownSymbolsStore, name) || !(NATIVE_SYMBOL || typeof WellKnownSymbolsStore[name] == 'string')) {
- if (NATIVE_SYMBOL && hasOwn$8(Symbol$1, name)) {
- WellKnownSymbolsStore[name] = Symbol$1[name];
- } else {
- WellKnownSymbolsStore[name] = createWellKnownSymbol('Symbol.' + name);
- }
- } return WellKnownSymbolsStore[name];
- };
- var isObject$3 = isObject$5;
- var isSymbol$1 = isSymbol$2;
- var getMethod$3 = getMethod$4;
- var ordinaryToPrimitive = ordinaryToPrimitive$1;
- var wellKnownSymbol$7 = wellKnownSymbol$8;
- var TO_PRIMITIVE = wellKnownSymbol$7('toPrimitive');
- // `ToPrimitive` abstract operation
- // https://tc39.es/ecma262/#sec-toprimitive
- var toPrimitive$1 = function (input, pref) {
- if (!isObject$3(input) || isSymbol$1(input)) return input;
- var exoticToPrim = getMethod$3(input, TO_PRIMITIVE);
- var result;
- if (exoticToPrim) {
- if (pref === undefined) pref = 'default';
- result = exoticToPrim.call(input, pref);
- if (!isObject$3(result) || isSymbol$1(result)) return result;
- throw TypeError("Can't convert object to primitive value");
- }
- if (pref === undefined) pref = 'number';
- return ordinaryToPrimitive(input, pref);
- };
- var toPrimitive = toPrimitive$1;
- var isSymbol = isSymbol$2;
- // `ToPropertyKey` abstract operation
- // https://tc39.es/ecma262/#sec-topropertykey
- var toPropertyKey$2 = function (argument) {
- var key = toPrimitive(argument, 'string');
- return isSymbol(key) ? key : String(key);
- };
- var global$6 = global$c;
- var isObject$2 = isObject$5;
- var document$1 = global$6.document;
- // typeof document.createElement is 'object' in old IE
- var EXISTS$1 = isObject$2(document$1) && isObject$2(document$1.createElement);
- var documentCreateElement$1 = function (it) {
- return EXISTS$1 ? document$1.createElement(it) : {};
- };
- var DESCRIPTORS$5 = descriptors;
- var fails$4 = fails$8;
- var createElement = documentCreateElement$1;
- // Thank's IE8 for his funny defineProperty
- var ie8DomDefine = !DESCRIPTORS$5 && !fails$4(function () {
- // eslint-disable-next-line es/no-object-defineproperty -- requied for testing
- return Object.defineProperty(createElement('div'), 'a', {
- get: function () { return 7; }
- }).a != 7;
- });
- var DESCRIPTORS$4 = descriptors;
- var propertyIsEnumerableModule = objectPropertyIsEnumerable;
- var createPropertyDescriptor$1 = createPropertyDescriptor$2;
- var toIndexedObject$2 = toIndexedObject$3;
- var toPropertyKey$1 = toPropertyKey$2;
- var hasOwn$7 = hasOwnProperty_1;
- var IE8_DOM_DEFINE$1 = ie8DomDefine;
- // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
- var $getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
- // `Object.getOwnPropertyDescriptor` method
- // https://tc39.es/ecma262/#sec-object.getownpropertydescriptor
- objectGetOwnPropertyDescriptor.f = DESCRIPTORS$4 ? $getOwnPropertyDescriptor : function getOwnPropertyDescriptor(O, P) {
- O = toIndexedObject$2(O);
- P = toPropertyKey$1(P);
- if (IE8_DOM_DEFINE$1) try {
- return $getOwnPropertyDescriptor(O, P);
- } catch (error) { /* empty */ }
- if (hasOwn$7(O, P)) return createPropertyDescriptor$1(!propertyIsEnumerableModule.f.call(O, P), O[P]);
- };
- var objectDefineProperty = {};
- var isObject$1 = isObject$5;
- // `Assert: Type(argument) is Object`
- var anObject$b = function (argument) {
- if (isObject$1(argument)) return argument;
- throw TypeError(String(argument) + ' is not an object');
- };
- var DESCRIPTORS$3 = descriptors;
- var IE8_DOM_DEFINE = ie8DomDefine;
- var anObject$a = anObject$b;
- var toPropertyKey = toPropertyKey$2;
- // eslint-disable-next-line es/no-object-defineproperty -- safe
- var $defineProperty = Object.defineProperty;
- // `Object.defineProperty` method
- // https://tc39.es/ecma262/#sec-object.defineproperty
- objectDefineProperty.f = DESCRIPTORS$3 ? $defineProperty : function defineProperty(O, P, Attributes) {
- anObject$a(O);
- P = toPropertyKey(P);
- anObject$a(Attributes);
- if (IE8_DOM_DEFINE) try {
- return $defineProperty(O, P, Attributes);
- } catch (error) { /* empty */ }
- if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported');
- if ('value' in Attributes) O[P] = Attributes.value;
- return O;
- };
- var DESCRIPTORS$2 = descriptors;
- var definePropertyModule$2 = objectDefineProperty;
- var createPropertyDescriptor = createPropertyDescriptor$2;
- var createNonEnumerableProperty$5 = DESCRIPTORS$2 ? function (object, key, value) {
- return definePropertyModule$2.f(object, key, createPropertyDescriptor(1, value));
- } : function (object, key, value) {
- object[key] = value;
- return object;
- };
- var redefine$3 = {exports: {}};
- var isCallable$7 = isCallable$d;
- var store$1 = sharedStore;
- var functionToString = Function.toString;
- // this helper broken in `core-js@3.4.1-3.4.4`, so we can't use `shared` helper
- if (!isCallable$7(store$1.inspectSource)) {
- store$1.inspectSource = function (it) {
- return functionToString.call(it);
- };
- }
- var inspectSource$2 = store$1.inspectSource;
- var global$5 = global$c;
- var isCallable$6 = isCallable$d;
- var inspectSource$1 = inspectSource$2;
- var WeakMap$1 = global$5.WeakMap;
- var nativeWeakMap = isCallable$6(WeakMap$1) && /native code/.test(inspectSource$1(WeakMap$1));
- var shared$1 = shared$3.exports;
- var uid = uid$2;
- var keys = shared$1('keys');
- var sharedKey$3 = function (key) {
- return keys[key] || (keys[key] = uid(key));
- };
- var hiddenKeys$4 = {};
- var NATIVE_WEAK_MAP = nativeWeakMap;
- var global$4 = global$c;
- var isObject = isObject$5;
- var createNonEnumerableProperty$4 = createNonEnumerableProperty$5;
- var hasOwn$6 = hasOwnProperty_1;
- var shared = sharedStore;
- var sharedKey$2 = sharedKey$3;
- var hiddenKeys$3 = hiddenKeys$4;
- var OBJECT_ALREADY_INITIALIZED = 'Object already initialized';
- var WeakMap = global$4.WeakMap;
- var set, get, has;
- var enforce = function (it) {
- return has(it) ? get(it) : set(it, {});
- };
- var getterFor = function (TYPE) {
- return function (it) {
- var state;
- if (!isObject(it) || (state = get(it)).type !== TYPE) {
- throw TypeError('Incompatible receiver, ' + TYPE + ' required');
- } return state;
- };
- };
- if (NATIVE_WEAK_MAP || shared.state) {
- var store = shared.state || (shared.state = new WeakMap());
- var wmget = store.get;
- var wmhas = store.has;
- var wmset = store.set;
- set = function (it, metadata) {
- if (wmhas.call(store, it)) throw new TypeError(OBJECT_ALREADY_INITIALIZED);
- metadata.facade = it;
- wmset.call(store, it, metadata);
- return metadata;
- };
- get = function (it) {
- return wmget.call(store, it) || {};
- };
- has = function (it) {
- return wmhas.call(store, it);
- };
- } else {
- var STATE = sharedKey$2('state');
- hiddenKeys$3[STATE] = true;
- set = function (it, metadata) {
- if (hasOwn$6(it, STATE)) throw new TypeError(OBJECT_ALREADY_INITIALIZED);
- metadata.facade = it;
- createNonEnumerableProperty$4(it, STATE, metadata);
- return metadata;
- };
- get = function (it) {
- return hasOwn$6(it, STATE) ? it[STATE] : {};
- };
- has = function (it) {
- return hasOwn$6(it, STATE);
- };
- }
- var internalState = {
- set: set,
- get: get,
- has: has,
- enforce: enforce,
- getterFor: getterFor
- };
- var DESCRIPTORS$1 = descriptors;
- var hasOwn$5 = hasOwnProperty_1;
- var FunctionPrototype = Function.prototype;
- // eslint-disable-next-line es/no-object-getownpropertydescriptor -- safe
- var getDescriptor = DESCRIPTORS$1 && Object.getOwnPropertyDescriptor;
- var EXISTS = hasOwn$5(FunctionPrototype, 'name');
- // additional protection from minified / mangled / dropped function names
- var PROPER = EXISTS && (function something() { /* empty */ }).name === 'something';
- var CONFIGURABLE = EXISTS && (!DESCRIPTORS$1 || (DESCRIPTORS$1 && getDescriptor(FunctionPrototype, 'name').configurable));
- var functionName = {
- EXISTS: EXISTS,
- PROPER: PROPER,
- CONFIGURABLE: CONFIGURABLE
- };
- var global$3 = global$c;
- var isCallable$5 = isCallable$d;
- var hasOwn$4 = hasOwnProperty_1;
- var createNonEnumerableProperty$3 = createNonEnumerableProperty$5;
- var setGlobal$1 = setGlobal$3;
- var inspectSource = inspectSource$2;
- var InternalStateModule$1 = internalState;
- var CONFIGURABLE_FUNCTION_NAME = functionName.CONFIGURABLE;
- var getInternalState$1 = InternalStateModule$1.get;
- var enforceInternalState = InternalStateModule$1.enforce;
- var TEMPLATE = String(String).split('String');
- (redefine$3.exports = function (O, key, value, options) {
- var unsafe = options ? !!options.unsafe : false;
- var simple = options ? !!options.enumerable : false;
- var noTargetGet = options ? !!options.noTargetGet : false;
- var name = options && options.name !== undefined ? options.name : key;
- var state;
- if (isCallable$5(value)) {
- if (String(name).slice(0, 7) === 'Symbol(') {
- name = '[' + String(name).replace(/^Symbol\(([^)]*)\)/, '$1') + ']';
- }
- if (!hasOwn$4(value, 'name') || (CONFIGURABLE_FUNCTION_NAME && value.name !== name)) {
- createNonEnumerableProperty$3(value, 'name', name);
- }
- state = enforceInternalState(value);
- if (!state.source) {
- state.source = TEMPLATE.join(typeof name == 'string' ? name : '');
- }
- }
- if (O === global$3) {
- if (simple) O[key] = value;
- else setGlobal$1(key, value);
- return;
- } else if (!unsafe) {
- delete O[key];
- } else if (!noTargetGet && O[key]) {
- simple = true;
- }
- if (simple) O[key] = value;
- else createNonEnumerableProperty$3(O, key, value);
- // add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative
- })(Function.prototype, 'toString', function toString() {
- return isCallable$5(this) && getInternalState$1(this).source || inspectSource(this);
- });
- var objectGetOwnPropertyNames = {};
- var ceil = Math.ceil;
- var floor = Math.floor;
- // `ToIntegerOrInfinity` abstract operation
- // https://tc39.es/ecma262/#sec-tointegerorinfinity
- var toIntegerOrInfinity$2 = function (argument) {
- var number = +argument;
- // eslint-disable-next-line no-self-compare -- safe
- return number !== number || number === 0 ? 0 : (number > 0 ? floor : ceil)(number);
- };
- var toIntegerOrInfinity$1 = toIntegerOrInfinity$2;
- var max = Math.max;
- var min$1 = Math.min;
- // Helper for a popular repeating case of the spec:
- // Let integer be ? ToInteger(index).
- // If integer < 0, let result be max((length + integer), 0); else let result be min(integer, length).
- var toAbsoluteIndex$1 = function (index, length) {
- var integer = toIntegerOrInfinity$1(index);
- return integer < 0 ? max(integer + length, 0) : min$1(integer, length);
- };
- var toIntegerOrInfinity = toIntegerOrInfinity$2;
- var min = Math.min;
- // `ToLength` abstract operation
- // https://tc39.es/ecma262/#sec-tolength
- var toLength$1 = function (argument) {
- return argument > 0 ? min(toIntegerOrInfinity(argument), 0x1FFFFFFFFFFFFF) : 0; // 2 ** 53 - 1 == 9007199254740991
- };
- var toLength = toLength$1;
- // `LengthOfArrayLike` abstract operation
- // https://tc39.es/ecma262/#sec-lengthofarraylike
- var lengthOfArrayLike$2 = function (obj) {
- return toLength(obj.length);
- };
- var toIndexedObject$1 = toIndexedObject$3;
- var toAbsoluteIndex = toAbsoluteIndex$1;
- var lengthOfArrayLike$1 = lengthOfArrayLike$2;
- // `Array.prototype.{ indexOf, includes }` methods implementation
- var createMethod = function (IS_INCLUDES) {
- return function ($this, el, fromIndex) {
- var O = toIndexedObject$1($this);
- var length = lengthOfArrayLike$1(O);
- var index = toAbsoluteIndex(fromIndex, length);
- var value;
- // Array#includes uses SameValueZero equality algorithm
- // eslint-disable-next-line no-self-compare -- NaN check
- if (IS_INCLUDES && el != el) while (length > index) {
- value = O[index++];
- // eslint-disable-next-line no-self-compare -- NaN check
- if (value != value) return true;
- // Array#indexOf ignores holes, Array#includes - not
- } else for (;length > index; index++) {
- if ((IS_INCLUDES || index in O) && O[index] === el) return IS_INCLUDES || index || 0;
- } return !IS_INCLUDES && -1;
- };
- };
- var arrayIncludes = {
- // `Array.prototype.includes` method
- // https://tc39.es/ecma262/#sec-array.prototype.includes
- includes: createMethod(true),
- // `Array.prototype.indexOf` method
- // https://tc39.es/ecma262/#sec-array.prototype.indexof
- indexOf: createMethod(false)
- };
- var hasOwn$3 = hasOwnProperty_1;
- var toIndexedObject = toIndexedObject$3;
- var indexOf = arrayIncludes.indexOf;
- var hiddenKeys$2 = hiddenKeys$4;
- var objectKeysInternal = function (object, names) {
- var O = toIndexedObject(object);
- var i = 0;
- var result = [];
- var key;
- for (key in O) !hasOwn$3(hiddenKeys$2, key) && hasOwn$3(O, key) && result.push(key);
- // Don't enum bug & hidden keys
- while (names.length > i) if (hasOwn$3(O, key = names[i++])) {
- ~indexOf(result, key) || result.push(key);
- }
- return result;
- };
- // IE8- don't enum bug keys
- var enumBugKeys$3 = [
- 'constructor',
- 'hasOwnProperty',
- 'isPrototypeOf',
- 'propertyIsEnumerable',
- 'toLocaleString',
- 'toString',
- 'valueOf'
- ];
- var internalObjectKeys$1 = objectKeysInternal;
- var enumBugKeys$2 = enumBugKeys$3;
- var hiddenKeys$1 = enumBugKeys$2.concat('length', 'prototype');
- // `Object.getOwnPropertyNames` method
- // https://tc39.es/ecma262/#sec-object.getownpropertynames
- // eslint-disable-next-line es/no-object-getownpropertynames -- safe
- objectGetOwnPropertyNames.f = Object.getOwnPropertyNames || function getOwnPropertyNames(O) {
- return internalObjectKeys$1(O, hiddenKeys$1);
- };
- var objectGetOwnPropertySymbols = {};
- // eslint-disable-next-line es/no-object-getownpropertysymbols -- safe
- objectGetOwnPropertySymbols.f = Object.getOwnPropertySymbols;
- var getBuiltIn$1 = getBuiltIn$4;
- var getOwnPropertyNamesModule = objectGetOwnPropertyNames;
- var getOwnPropertySymbolsModule = objectGetOwnPropertySymbols;
- var anObject$9 = anObject$b;
- // all object keys, includes non-enumerable and symbols
- var ownKeys$1 = getBuiltIn$1('Reflect', 'ownKeys') || function ownKeys(it) {
- var keys = getOwnPropertyNamesModule.f(anObject$9(it));
- var getOwnPropertySymbols = getOwnPropertySymbolsModule.f;
- return getOwnPropertySymbols ? keys.concat(getOwnPropertySymbols(it)) : keys;
- };
- var hasOwn$2 = hasOwnProperty_1;
- var ownKeys = ownKeys$1;
- var getOwnPropertyDescriptorModule = objectGetOwnPropertyDescriptor;
- var definePropertyModule$1 = objectDefineProperty;
- var copyConstructorProperties$1 = function (target, source) {
- var keys = ownKeys(source);
- var defineProperty = definePropertyModule$1.f;
- var getOwnPropertyDescriptor = getOwnPropertyDescriptorModule.f;
- for (var i = 0; i < keys.length; i++) {
- var key = keys[i];
- if (!hasOwn$2(target, key)) defineProperty(target, key, getOwnPropertyDescriptor(source, key));
- }
- };
- var fails$3 = fails$8;
- var isCallable$4 = isCallable$d;
- var replacement = /#|\.prototype\./;
- var isForced$1 = function (feature, detection) {
- var value = data[normalize(feature)];
- return value == POLYFILL ? true
- : value == NATIVE ? false
- : isCallable$4(detection) ? fails$3(detection)
- : !!detection;
- };
- var normalize = isForced$1.normalize = function (string) {
- return String(string).replace(replacement, '.').toLowerCase();
- };
- var data = isForced$1.data = {};
- var NATIVE = isForced$1.NATIVE = 'N';
- var POLYFILL = isForced$1.POLYFILL = 'P';
- var isForced_1 = isForced$1;
- var global$2 = global$c;
- var getOwnPropertyDescriptor = objectGetOwnPropertyDescriptor.f;
- var createNonEnumerableProperty$2 = createNonEnumerableProperty$5;
- var redefine$2 = redefine$3.exports;
- var setGlobal = setGlobal$3;
- var copyConstructorProperties = copyConstructorProperties$1;
- var isForced = isForced_1;
- /*
- options.target - name of the target object
- options.global - target is the global object
- options.stat - export as static methods of target
- options.proto - export as prototype methods of target
- options.real - real prototype method for the `pure` version
- options.forced - export even if the native feature is available
- options.bind - bind methods to the target, required for the `pure` version
- options.wrap - wrap constructors to preventing global pollution, required for the `pure` version
- options.unsafe - use the simple assignment of property instead of delete + defineProperty
- options.sham - add a flag to not completely full polyfills
- options.enumerable - export as enumerable property
- options.noTargetGet - prevent calling a getter on target
- options.name - the .name of the function if it does not match the key
- */
- var _export = function (options, source) {
- var TARGET = options.target;
- var GLOBAL = options.global;
- var STATIC = options.stat;
- var FORCED, target, key, targetProperty, sourceProperty, descriptor;
- if (GLOBAL) {
- target = global$2;
- } else if (STATIC) {
- target = global$2[TARGET] || setGlobal(TARGET, {});
- } else {
- target = (global$2[TARGET] || {}).prototype;
- }
- if (target) for (key in source) {
- sourceProperty = source[key];
- if (options.noTargetGet) {
- descriptor = getOwnPropertyDescriptor(target, key);
- targetProperty = descriptor && descriptor.value;
- } else targetProperty = target[key];
- FORCED = isForced(GLOBAL ? key : TARGET + (STATIC ? '.' : '#') + key, options.forced);
- // contained in target
- if (!FORCED && targetProperty !== undefined) {
- if (typeof sourceProperty === typeof targetProperty) continue;
- copyConstructorProperties(sourceProperty, targetProperty);
- }
- // add a flag to not completely full polyfills
- if (options.sham || (targetProperty && targetProperty.sham)) {
- createNonEnumerableProperty$2(sourceProperty, 'sham', true);
- }
- // extend global
- redefine$2(target, key, sourceProperty, options);
- }
- };
- var anInstance$1 = function (it, Constructor, name) {
- if (it instanceof Constructor) return it;
- throw TypeError('Incorrect ' + (name ? name + ' ' : '') + 'invocation');
- };
- var internalObjectKeys = objectKeysInternal;
- var enumBugKeys$1 = enumBugKeys$3;
- // `Object.keys` method
- // https://tc39.es/ecma262/#sec-object.keys
- // eslint-disable-next-line es/no-object-keys -- safe
- var objectKeys$1 = Object.keys || function keys(O) {
- return internalObjectKeys(O, enumBugKeys$1);
- };
- var DESCRIPTORS = descriptors;
- var definePropertyModule = objectDefineProperty;
- var anObject$8 = anObject$b;
- var objectKeys = objectKeys$1;
- // `Object.defineProperties` method
- // https://tc39.es/ecma262/#sec-object.defineproperties
- // eslint-disable-next-line es/no-object-defineproperties -- safe
- var objectDefineProperties = DESCRIPTORS ? Object.defineProperties : function defineProperties(O, Properties) {
- anObject$8(O);
- var keys = objectKeys(Properties);
- var length = keys.length;
- var index = 0;
- var key;
- while (length > index) definePropertyModule.f(O, key = keys[index++], Properties[key]);
- return O;
- };
- var getBuiltIn = getBuiltIn$4;
- var html$1 = getBuiltIn('document', 'documentElement');
- /* global ActiveXObject -- old IE, WSH */
- var anObject$7 = anObject$b;
- var defineProperties = objectDefineProperties;
- var enumBugKeys = enumBugKeys$3;
- var hiddenKeys = hiddenKeys$4;
- var html = html$1;
- var documentCreateElement = documentCreateElement$1;
- var sharedKey$1 = sharedKey$3;
- var GT = '>';
- var LT = '<';
- var PROTOTYPE = 'prototype';
- var SCRIPT = 'script';
- var IE_PROTO$1 = sharedKey$1('IE_PROTO');
- var EmptyConstructor = function () { /* empty */ };
- var scriptTag = function (content) {
- return LT + SCRIPT + GT + content + LT + '/' + SCRIPT + GT;
- };
- // Create object with fake `null` prototype: use ActiveX Object with cleared prototype
- var NullProtoObjectViaActiveX = function (activeXDocument) {
- activeXDocument.write(scriptTag(''));
- activeXDocument.close();
- var temp = activeXDocument.parentWindow.Object;
- activeXDocument = null; // avoid memory leak
- return temp;
- };
- // Create object with fake `null` prototype: use iframe Object with cleared prototype
- var NullProtoObjectViaIFrame = function () {
- // Thrash, waste and sodomy: IE GC bug
- var iframe = documentCreateElement('iframe');
- var JS = 'java' + SCRIPT + ':';
- var iframeDocument;
- iframe.style.display = 'none';
- html.appendChild(iframe);
- // https://github.com/zloirock/core-js/issues/475
- iframe.src = String(JS);
- iframeDocument = iframe.contentWindow.document;
- iframeDocument.open();
- iframeDocument.write(scriptTag('document.F=Object'));
- iframeDocument.close();
- return iframeDocument.F;
- };
- // Check for document.domain and active x support
- // No need to use active x approach when document.domain is not set
- // see https://github.com/es-shims/es5-shim/issues/150
- // variation of https://github.com/kitcambridge/es5-shim/commit/4f738ac066346
- // avoid IE GC bug
- var activeXDocument;
- var NullProtoObject = function () {
- try {
- activeXDocument = new ActiveXObject('htmlfile');
- } catch (error) { /* ignore */ }
- NullProtoObject = typeof document != 'undefined'
- ? document.domain && activeXDocument
- ? NullProtoObjectViaActiveX(activeXDocument) // old IE
- : NullProtoObjectViaIFrame()
- : NullProtoObjectViaActiveX(activeXDocument); // WSH
- var length = enumBugKeys.length;
- while (length--) delete NullProtoObject[PROTOTYPE][enumBugKeys[length]];
- return NullProtoObject();
- };
- hiddenKeys[IE_PROTO$1] = true;
- // `Object.create` method
- // https://tc39.es/ecma262/#sec-object.create
- var objectCreate = Object.create || function create(O, Properties) {
- var result;
- if (O !== null) {
- EmptyConstructor[PROTOTYPE] = anObject$7(O);
- result = new EmptyConstructor();
- EmptyConstructor[PROTOTYPE] = null;
- // add "__proto__" for Object.getPrototypeOf polyfill
- result[IE_PROTO$1] = O;
- } else result = NullProtoObject();
- return Properties === undefined ? result : defineProperties(result, Properties);
- };
- var fails$2 = fails$8;
- var correctPrototypeGetter = !fails$2(function () {
- function F() { /* empty */ }
- F.prototype.constructor = null;
- // eslint-disable-next-line es/no-object-getprototypeof -- required for testing
- return Object.getPrototypeOf(new F()) !== F.prototype;
- });
- var hasOwn$1 = hasOwnProperty_1;
- var isCallable$3 = isCallable$d;
- var toObject = toObject$2;
- var sharedKey = sharedKey$3;
- var CORRECT_PROTOTYPE_GETTER = correctPrototypeGetter;
- var IE_PROTO = sharedKey('IE_PROTO');
- var ObjectPrototype = Object.prototype;
- // `Object.getPrototypeOf` method
- // https://tc39.es/ecma262/#sec-object.getprototypeof
- // eslint-disable-next-line es/no-object-getprototypeof -- safe
- var objectGetPrototypeOf = CORRECT_PROTOTYPE_GETTER ? Object.getPrototypeOf : function (O) {
- var object = toObject(O);
- if (hasOwn$1(object, IE_PROTO)) return object[IE_PROTO];
- var constructor = object.constructor;
- if (isCallable$3(constructor) && object instanceof constructor) {
- return constructor.prototype;
- } return object instanceof Object ? ObjectPrototype : null;
- };
- var fails$1 = fails$8;
- var isCallable$2 = isCallable$d;
- var getPrototypeOf = objectGetPrototypeOf;
- var redefine$1 = redefine$3.exports;
- var wellKnownSymbol$6 = wellKnownSymbol$8;
- var ITERATOR$2 = wellKnownSymbol$6('iterator');
- var BUGGY_SAFARI_ITERATORS = false;
- // `%IteratorPrototype%` object
- // https://tc39.es/ecma262/#sec-%iteratorprototype%-object
- var IteratorPrototype$2, PrototypeOfArrayIteratorPrototype, arrayIterator;
- /* eslint-disable es/no-array-prototype-keys -- safe */
- if ([].keys) {
- arrayIterator = [].keys();
- // Safari 8 has buggy iterators w/o `next`
- if (!('next' in arrayIterator)) BUGGY_SAFARI_ITERATORS = true;
- else {
- PrototypeOfArrayIteratorPrototype = getPrototypeOf(getPrototypeOf(arrayIterator));
- if (PrototypeOfArrayIteratorPrototype !== Object.prototype) IteratorPrototype$2 = PrototypeOfArrayIteratorPrototype;
- }
- }
- var NEW_ITERATOR_PROTOTYPE = IteratorPrototype$2 == undefined || fails$1(function () {
- var test = {};
- // FF44- legacy iterators case
- return IteratorPrototype$2[ITERATOR$2].call(test) !== test;
- });
- if (NEW_ITERATOR_PROTOTYPE) IteratorPrototype$2 = {};
- // `%IteratorPrototype%[@@iterator]()` method
- // https://tc39.es/ecma262/#sec-%iteratorprototype%-@@iterator
- if (!isCallable$2(IteratorPrototype$2[ITERATOR$2])) {
- redefine$1(IteratorPrototype$2, ITERATOR$2, function () {
- return this;
- });
- }
- var iteratorsCore = {
- IteratorPrototype: IteratorPrototype$2,
- BUGGY_SAFARI_ITERATORS: BUGGY_SAFARI_ITERATORS
- };
- // https://github.com/tc39/proposal-iterator-helpers
- var $$2 = _export;
- var global$1 = global$c;
- var anInstance = anInstance$1;
- var isCallable$1 = isCallable$d;
- var createNonEnumerableProperty$1 = createNonEnumerableProperty$5;
- var fails = fails$8;
- var hasOwn = hasOwnProperty_1;
- var wellKnownSymbol$5 = wellKnownSymbol$8;
- var IteratorPrototype$1 = iteratorsCore.IteratorPrototype;
- var TO_STRING_TAG$3 = wellKnownSymbol$5('toStringTag');
- var NativeIterator = global$1.Iterator;
- // FF56- have non-standard global helper `Iterator`
- var FORCED = !isCallable$1(NativeIterator)
- || NativeIterator.prototype !== IteratorPrototype$1
- // FF44- non-standard `Iterator` passes previous tests
- || !fails(function () { NativeIterator({}); });
- var IteratorConstructor = function Iterator() {
- anInstance(this, IteratorConstructor);
- };
- if (!hasOwn(IteratorPrototype$1, TO_STRING_TAG$3)) {
- createNonEnumerableProperty$1(IteratorPrototype$1, TO_STRING_TAG$3, 'Iterator');
- }
- if (FORCED || !hasOwn(IteratorPrototype$1, 'constructor') || IteratorPrototype$1.constructor === Object) {
- createNonEnumerableProperty$1(IteratorPrototype$1, 'constructor', IteratorConstructor);
- }
- IteratorConstructor.prototype = IteratorPrototype$1;
- $$2({ global: true, forced: FORCED }, {
- Iterator: IteratorConstructor
- });
- var iterators = {};
- var wellKnownSymbol$4 = wellKnownSymbol$8;
- var Iterators$1 = iterators;
- var ITERATOR$1 = wellKnownSymbol$4('iterator');
- var ArrayPrototype = Array.prototype;
- // check on default Array iterator
- var isArrayIteratorMethod$1 = function (it) {
- return it !== undefined && (Iterators$1.Array === it || ArrayPrototype[ITERATOR$1] === it);
- };
- var aCallable$3 = aCallable$5;
- // optional / simple context binding
- var functionBindContext = function (fn, that, length) {
- aCallable$3(fn);
- if (that === undefined) return fn;
- switch (length) {
- case 0: return function () {
- return fn.call(that);
- };
- case 1: return function (a) {
- return fn.call(that, a);
- };
- case 2: return function (a, b) {
- return fn.call(that, a, b);
- };
- case 3: return function (a, b, c) {
- return fn.call(that, a, b, c);
- };
- }
- return function (/* ...args */) {
- return fn.apply(that, arguments);
- };
- };
- var wellKnownSymbol$3 = wellKnownSymbol$8;
- var TO_STRING_TAG$2 = wellKnownSymbol$3('toStringTag');
- var test = {};
- test[TO_STRING_TAG$2] = 'z';
- var toStringTagSupport = String(test) === '[object z]';
- var TO_STRING_TAG_SUPPORT = toStringTagSupport;
- var isCallable = isCallable$d;
- var classofRaw = classofRaw$1;
- var wellKnownSymbol$2 = wellKnownSymbol$8;
- var TO_STRING_TAG$1 = wellKnownSymbol$2('toStringTag');
- // ES3 wrong here
- var CORRECT_ARGUMENTS = classofRaw(function () { return arguments; }()) == 'Arguments';
- // fallback for IE11 Script Access Denied error
- var tryGet = function (it, key) {
- try {
- return it[key];
- } catch (error) { /* empty */ }
- };
- // getting tag from ES6+ `Object.prototype.toString`
- var classof$1 = TO_STRING_TAG_SUPPORT ? classofRaw : function (it) {
- var O, tag, result;
- return it === undefined ? 'Undefined' : it === null ? 'Null'
- // @@toStringTag case
- : typeof (tag = tryGet(O = Object(it), TO_STRING_TAG$1)) == 'string' ? tag
- // builtinTag case
- : CORRECT_ARGUMENTS ? classofRaw(O)
- // ES3 arguments fallback
- : (result = classofRaw(O)) == 'Object' && isCallable(O.callee) ? 'Arguments' : result;
- };
- var classof = classof$1;
- var getMethod$2 = getMethod$4;
- var Iterators = iterators;
- var wellKnownSymbol$1 = wellKnownSymbol$8;
- var ITERATOR = wellKnownSymbol$1('iterator');
- var getIteratorMethod$2 = function (it) {
- if (it != undefined) return getMethod$2(it, ITERATOR)
- || getMethod$2(it, '@@iterator')
- || Iterators[classof(it)];
- };
- var aCallable$2 = aCallable$5;
- var anObject$6 = anObject$b;
- var getIteratorMethod$1 = getIteratorMethod$2;
- var getIterator$1 = function (argument, usingIterator) {
- var iteratorMethod = arguments.length < 2 ? getIteratorMethod$1(argument) : usingIterator;
- if (aCallable$2(iteratorMethod)) return anObject$6(iteratorMethod.call(argument));
- throw TypeError(String(argument) + ' is not iterable');
- };
- var anObject$5 = anObject$b;
- var getMethod$1 = getMethod$4;
- var iteratorClose$2 = function (iterator, kind, value) {
- var innerResult, innerError;
- anObject$5(iterator);
- try {
- innerResult = getMethod$1(iterator, 'return');
- if (!innerResult) {
- if (kind === 'throw') throw value;
- return value;
- }
- innerResult = innerResult.call(iterator);
- } catch (error) {
- innerError = true;
- innerResult = error;
- }
- if (kind === 'throw') throw value;
- if (innerError) throw innerResult;
- anObject$5(innerResult);
- return value;
- };
- var anObject$4 = anObject$b;
- var isArrayIteratorMethod = isArrayIteratorMethod$1;
- var lengthOfArrayLike = lengthOfArrayLike$2;
- var bind = functionBindContext;
- var getIterator = getIterator$1;
- var getIteratorMethod = getIteratorMethod$2;
- var iteratorClose$1 = iteratorClose$2;
- var Result = function (stopped, result) {
- this.stopped = stopped;
- this.result = result;
- };
- var iterate$1 = function (iterable, unboundFunction, options) {
- var that = options && options.that;
- var AS_ENTRIES = !!(options && options.AS_ENTRIES);
- var IS_ITERATOR = !!(options && options.IS_ITERATOR);
- var INTERRUPTED = !!(options && options.INTERRUPTED);
- var fn = bind(unboundFunction, that, 1 + AS_ENTRIES + INTERRUPTED);
- var iterator, iterFn, index, length, result, next, step;
- var stop = function (condition) {
- if (iterator) iteratorClose$1(iterator, 'normal', condition);
- return new Result(true, condition);
- };
- var callFn = function (value) {
- if (AS_ENTRIES) {
- anObject$4(value);
- return INTERRUPTED ? fn(value[0], value[1], stop) : fn(value[0], value[1]);
- } return INTERRUPTED ? fn(value, stop) : fn(value);
- };
- if (IS_ITERATOR) {
- iterator = iterable;
- } else {
- iterFn = getIteratorMethod(iterable);
- if (!iterFn) throw TypeError(String(iterable) + ' is not iterable');
- // optimisation for array iterators
- if (isArrayIteratorMethod(iterFn)) {
- for (index = 0, length = lengthOfArrayLike(iterable); length > index; index++) {
- result = callFn(iterable[index]);
- if (result && result instanceof Result) return result;
- } return new Result(false);
- }
- iterator = getIterator(iterable, iterFn);
- }
- next = iterator.next;
- while (!(step = next.call(iterator)).done) {
- try {
- result = callFn(step.value);
- } catch (error) {
- iteratorClose$1(iterator, 'throw', error);
- }
- if (typeof result == 'object' && result && result instanceof Result) return result;
- } return new Result(false);
- };
- // https://github.com/tc39/proposal-iterator-helpers
- var $$1 = _export;
- var iterate = iterate$1;
- var anObject$3 = anObject$b;
- $$1({ target: 'Iterator', proto: true, real: true }, {
- forEach: function forEach(fn) {
- iterate(anObject$3(this), fn, { IS_ITERATOR: true });
- }
- });
- var redefine = redefine$3.exports;
- var redefineAll$1 = function (target, src, options) {
- for (var key in src) redefine(target, key, src[key], options);
- return target;
- };
- var aCallable$1 = aCallable$5;
- var anObject$2 = anObject$b;
- var create = objectCreate;
- var createNonEnumerableProperty = createNonEnumerableProperty$5;
- var redefineAll = redefineAll$1;
- var wellKnownSymbol = wellKnownSymbol$8;
- var InternalStateModule = internalState;
- var getMethod = getMethod$4;
- var IteratorPrototype = iteratorsCore.IteratorPrototype;
- var setInternalState = InternalStateModule.set;
- var getInternalState = InternalStateModule.get;
- var TO_STRING_TAG = wellKnownSymbol('toStringTag');
- var iteratorCreateProxy = function (nextHandler, IS_ITERATOR) {
- var IteratorProxy = function Iterator(state) {
- state.next = aCallable$1(state.iterator.next);
- state.done = false;
- state.ignoreArg = !IS_ITERATOR;
- setInternalState(this, state);
- };
- IteratorProxy.prototype = redefineAll(create(IteratorPrototype), {
- next: function next(arg) {
- var state = getInternalState(this);
- var args = arguments.length ? [state.ignoreArg ? undefined : arg] : IS_ITERATOR ? [] : [undefined];
- state.ignoreArg = false;
- var result = state.done ? undefined : nextHandler.call(state, args);
- return { done: state.done, value: result };
- },
- 'return': function (value) {
- var state = getInternalState(this);
- var iterator = state.iterator;
- state.done = true;
- var $$return = getMethod(iterator, 'return');
- return { done: true, value: $$return ? anObject$2($$return.call(iterator, value)).value : value };
- },
- 'throw': function (value) {
- var state = getInternalState(this);
- var iterator = state.iterator;
- state.done = true;
- var $$throw = getMethod(iterator, 'throw');
- if ($$throw) return $$throw.call(iterator, value);
- throw value;
- }
- });
- if (!IS_ITERATOR) {
- createNonEnumerableProperty(IteratorProxy.prototype, TO_STRING_TAG, 'Generator');
- }
- return IteratorProxy;
- };
- var anObject$1 = anObject$b;
- var iteratorClose = iteratorClose$2;
- // call something on iterator step with safe closing on error
- var callWithSafeIterationClosing$1 = function (iterator, fn, value, ENTRIES) {
- try {
- return ENTRIES ? fn(anObject$1(value)[0], value[1]) : fn(value);
- } catch (error) {
- iteratorClose(iterator, 'throw', error);
- }
- };
- // https://github.com/tc39/proposal-iterator-helpers
- var $ = _export;
- var aCallable = aCallable$5;
- var anObject = anObject$b;
- var createIteratorProxy = iteratorCreateProxy;
- var callWithSafeIterationClosing = callWithSafeIterationClosing$1;
- var IteratorProxy = createIteratorProxy(function (args) {
- var iterator = this.iterator;
- var result = anObject(this.next.apply(iterator, args));
- var done = this.done = !!result.done;
- if (!done) return callWithSafeIterationClosing(iterator, this.mapper, result.value);
- });
- $({ target: 'Iterator', proto: true, real: true }, {
- map: function map(mapper) {
- return new IteratorProxy({
- iterator: anObject(this),
- mapper: aCallable(mapper)
- });
- }
- });
- const isZHInGameSetting = localStorage.getItem("i18nextLng")?.toLowerCase()?.startsWith("zh"); // 获取游戏内设置语言
- let isZH = isZHInGameSetting; // MWITools 本身显示的语言默认由游戏内设置语言决定
- let settingsMap = {
- projectileLimit: {
- id: "projectileLimit",
- desc: isZH ? "投射物数量限制" : "Projectile Limit",
- value: 30,
- min: 1,
- max: 100,
- step: 1
- },
- projectileScale: {
- id: "projectileScale",
- desc: isZH ? "投射物缩放" : "Projectile Scale",
- value: 1.0,
- min: 0.1,
- max: 3.0,
- step: 0.01
- },
- onHitScale: {
- id: "onHitScale",
- desc: isZH ? "命中效果缩放" : "On-hit Effect Scale",
- value: 1.0,
- min: 0.1,
- max: 3.0,
- step: 0.01
- },
- projectileHeightScale: {
- id: "projectileHeightScale",
- desc: isZH ? "弹道高度比例" : "Projectile Height Scale",
- value: 1.0,
- min: 0.1,
- max: 3.0,
- step: 0.01
- },
- projectileSpeedScale: {
- id: "projectileSpeedScale",
- desc: isZH ? "弹道速度比例" : "Projectile Speed Scale",
- value: 1.0,
- min: 0.1,
- max: 3.0,
- step: 0.01
- },
- shakeEffectScale: {
- id: "shakeEffectScale",
- desc: isZH ? "震动效果" : "Shake Effect Scale",
- value: 1.0,
- min: 0.0,
- max: 3.0,
- step: 0.01
- },
- particleEffectRatio: {
- id: "particleEffectRatio",
- desc: isZH ? "粒子效果数量" : "Particle Effect Ratio",
- value: 1.0,
- min: 0.0,
- max: 5.0,
- step: 0.1
- },
- particleLifespanRatio: {
- id: "particleLifespanRatio",
- desc: isZH ? "粒子效果持续时长" : "Particle Lifespan Ratio",
- value: 1.0,
- min: 0.1,
- max: 5.0,
- step: 0.1
- },
- particleSpeedRatio: {
- id: "particleSpeedRatio",
- desc: isZH ? "粒子效果初速度" : "Particle Effect Speed Ratio",
- value: 1.0,
- min: 0.1,
- max: 5.0,
- step: 0.1
- },
- projectileTrailLength: {
- id: "projectileTrailLength",
- desc: isZH ? "弹道尾迹长度" : "Projectile Trail Length",
- value: 1.0,
- min: 0.0,
- max: 5.0,
- step: 0.01
- },
- projectileTrailGap: {
- id: "projectileTrailGap",
- desc: isZH ? "弹道尾迹间隔" : "Projectile Trail Gap",
- value: 1.0,
- min: 0.05,
- max: 3.0,
- step: 0.05
- },
- originalDamageDisplay: {
- id: "originalDamageDisplay",
- desc: isZH ? "原版伤害显示" : "Original Damage Display",
- value: false
- },
- damageTextLifespan: {
- id: "damageTextLifespan",
- desc: isZH ? "伤害文本持续时间" : "Damage Text Lifespan",
- value: 120,
- min: 30,
- max: 480,
- step: 10
- },
- damageTextScale: {
- id: "damageTextScale",
- desc: isZH ? "伤害文本大小" : "Damage Text Scale",
- value: 1.0,
- min: 0.1,
- max: 3.0,
- step: 0.1
- },
- damageTextAlpha: {
- id: "damageTextAlpha",
- desc: isZH ? "伤害文本不透明度" : "Damage Text Alpha",
- value: 0.8,
- min: 0.0,
- max: 1.0,
- step: 0.01
- },
- damageTextSizeLimit: {
- id: "damageTextSizeLimit",
- desc: isZH ? "伤害文本尺寸上限" : "Damage Text Size Limit",
- value: 70,
- min: 15,
- max: 200,
- step: 1
- },
- showSelfRegen: {
- id: "showSelfRegen",
- desc: isZH ? "显示玩家被动回复效果" : "Show Self Regeneration",
- value: true
- },
- monsterDeadAnimation: {
- id: "monsterDeadAnimation",
- desc: isZH ? "怪物死亡效果" : "Monster Dead Animation",
- value: true
- },
- monsterDeadAnimationStyle: {
- id: "monsterDeadAnimationStyle",
- desc: isZH ? "怪物死亡效果样式" : "Monster Dead Animation Style",
- value: "default",
- list: []
- },
- damageHpBarDropDelay: {
- id: "damageHpBarDropDelay",
- desc: isZH ? "血条掉落延迟" : "Hp Bar Drop Delay",
- value: 300,
- min: 50,
- max: 1000,
- step: 50
- },
- tracker0: {
- id: "tracker0",
- desc: isZH ? "玩家1" : "Player 1",
- isTrue: true,
- trackStyle: "auto",
- r: 255,
- g: 99,
- b: 132
- },
- tracker1: {
- id: "tracker1",
- desc: isZH ? "玩家2" : "Player 2",
- isTrue: true,
- trackStyle: "auto",
- r: 54,
- g: 162,
- b: 235
- },
- tracker2: {
- id: "tracker2",
- desc: isZH ? "玩家3" : "Player 3",
- isTrue: true,
- trackStyle: "auto",
- r: 255,
- g: 206,
- b: 86
- },
- tracker3: {
- id: "tracker3",
- desc: isZH ? "玩家4" : "Player 4",
- isTrue: true,
- trackStyle: "auto",
- r: 75,
- g: 192,
- b: 192
- },
- tracker4: {
- id: "tracker4",
- desc: isZH ? "玩家5" : "Player 5",
- isTrue: true,
- trackStyle: "auto",
- r: 153,
- g: 102,
- b: 255
- },
- tracker6: {
- id: "tracker6",
- desc: isZH ? "敌人" : "Enemies",
- isTrue: true,
- trackStyle: "auto",
- r: 255,
- g: 0,
- b: 0
- },
- renderFpsLimit: {
- id: "renderFpsLimit",
- desc: isZH ? "渲染帧数限制(非精确,刷新生效)" : "Render FPS Limit (Not accurate, restart required)",
- value: 160,
- min: 5,
- max: 300,
- step: 1
- },
- showFps: {
- id: "showFps",
- desc: isZH ? "显示帧数" : "Show FPS",
- value: false
- }
- };
- readSettings();
- function waitForSettings(params) {
- const targetNode = document.querySelector("div.SettingsPanel_profileTab__214Bj");
- if (targetNode) {
- if (!targetNode.querySelector("#tracker_settings")) {
- targetNode.insertAdjacentHTML("beforeend", `<div id="tracker_settings"></div>`);
- const insertElem = targetNode.querySelector("div#tracker_settings");
- insertElem.insertAdjacentHTML("beforeend", `<div style="float: left; color: orange">${isZH ? "MWI-Hit-Tracker 设置 :" : "MWI-Hit-Tracker Settings: "}</div></br>`);
- for (const setting of Object.values(settingsMap)) {
- if (setting.id.startsWith("tracker")) {
- insertElem.insertAdjacentHTML("beforeend", `<div class="tracker-option"><input type="checkbox" id="${setting.id}" ${setting.isTrue ? "checked" : ""}></input>${setting.desc} ${isZH ? '颜色' : 'Color'}<div class="color-preview" id="colorPreview_${setting.id}"></div>${isZH ? '样式' : 'Projectile Style'}<select id="projectileStyle_${setting.id}"></select></div>`);
- const checkedBox = insertElem.querySelector("#" + setting.id);
- checkedBox.addEventListener("change", e => {
- settingsMap[setting.id].isTrue = e.target.checked;
- saveSettings();
- });
- const colorPreview = document.getElementById('colorPreview_' + setting.id);
- let currentColor = {
- r: setting.r,
- g: setting.g,
- b: setting.b
- };
- // 点击打开颜色选择器
- colorPreview.addEventListener('click', () => {
- const settingColor = {
- r: settingsMap[setting.id].r,
- g: settingsMap[setting.id].g,
- b: settingsMap[setting.id].b
- };
- const modal = createColorPicker(settingColor, newColor => {
- currentColor = newColor;
- settingsMap[setting.id].r = newColor.r;
- settingsMap[setting.id].g = newColor.g;
- settingsMap[setting.id].b = newColor.b;
- localStorage.setItem("tracker_settingsMap", JSON.stringify(settingsMap));
- updatePreview();
- });
- document.body.appendChild(modal);
- });
- function updatePreview() {
- colorPreview.style.backgroundColor = `rgb(${currentColor.r},${currentColor.g},${currentColor.b})`;
- }
- updatePreview();
- function createColorPicker(initialColor, callback) {
- // 创建弹窗容器
- const backdrop = document.createElement('div');
- backdrop.className = 'modal-backdrop';
- const modal = document.createElement('div');
- modal.className = 'color-picker-modal';
- // 创建SVG容器
- const preview = document.createElementNS("http://www.w3.org/2000/svg", "svg");
- preview.setAttribute("width", "200");
- preview.setAttribute("height", "150");
- preview.style.display = 'block';
- // 创建抛物线路径
- const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
- Object.assign(path.style, {
- strokeWidth: '5px',
- fill: 'none',
- strokeLinecap: 'round'
- });
- path.setAttribute("d", "M 0 130 Q 100 0 200 130");
- preview.appendChild(path);
- // 颜色控制组件
- const controls = document.createElement('div');
- ['r', 'g', 'b'].forEach(channel => {
- const container = document.createElement('div');
- container.className = 'slider-container';
- // 标签
- const label = document.createElement('label');
- label.textContent = channel.toUpperCase() + ':';
- label.style.color = "white";
- // 滑块
- const slider = document.createElement('input');
- slider.type = 'range';
- slider.min = 0;
- slider.max = 255;
- slider.value = initialColor[channel];
- // 输入框
- const input = document.createElement('input');
- input.type = 'number';
- input.min = 0;
- input.max = 255;
- input.value = initialColor[channel];
- input.style.width = '60px';
- // 双向绑定
- const updateChannel = value => {
- value = Math.min(255, Math.max(0, parseInt(value) || 0));
- slider.value = value;
- input.value = value;
- currentColor[channel] = value;
- path.style.stroke = getColorString(currentColor);
- };
- slider.addEventListener('input', e => updateChannel(e.target.value));
- input.addEventListener('change', e => updateChannel(e.target.value));
- container.append(label, slider, input);
- controls.append(container);
- });
- // 操作按钮
- const actions = document.createElement('div');
- actions.className = 'modal-actions';
- const confirmBtn = document.createElement('button');
- confirmBtn.textContent = isZH ? '确定' : 'OK';
- confirmBtn.onclick = () => {
- callback(currentColor);
- backdrop.remove();
- };
- const cancelBtn = document.createElement('button');
- cancelBtn.textContent = isZH ? '取消' : 'Cancel';
- cancelBtn.onclick = () => backdrop.remove();
- actions.append(cancelBtn, confirmBtn);
- // 组装弹窗
- const getColorString = color => `rgb(${color.r},${color.g},${color.b})`;
- path.style.stroke = getColorString(settingsMap[setting.id]);
- modal.append(preview, controls, actions);
- backdrop.append(modal);
- // 点击背景关闭
- backdrop.addEventListener('click', e => {
- if (e.target === backdrop) backdrop.remove();
- });
- return backdrop;
- }
- const select = document.querySelector("#projectileStyle_" + setting.id);
- const projectileStyle = ["auto", "null", ...params.allProjectiles];
- for (const option of projectileStyle) {
- select.insertAdjacentHTML("beforeend", `<option value="${option}" ${option === setting.trackStyle ? "selected" : ""}>${option}</option>`);
- }
- select.addEventListener("change", e => {
- settingsMap[setting.id].trackStyle = e.target.value;
- saveSettings();
- });
- } else {
- if (typeof setting.value === "boolean") {
- insertElem.insertAdjacentHTML("beforeend", `<div class="tracker-option">${setting.desc}<input type="checkbox" id="trackerSetting_${setting.id}"></input></div>`);
- const checkedBox = insertElem.querySelector("#trackerSetting_" + setting.id);
- checkedBox.checked = setting.value;
- checkedBox.addEventListener("change", e => {
- settingsMap[setting.id].value = e.target.checked;
- saveSettings();
- });
- } else if (typeof setting.value === "number") {
- insertElem.insertAdjacentHTML("beforeend", `<div class="tracker-option">${setting.desc}<input type="range" id="trackerSetting_${setting.id}_range"></input><input type="number" id="trackerSetting_${setting.id}_value"></input></div>`);
- const slider = document.querySelector("#trackerSetting_" + setting.id + "_range");
- slider.min = setting.min;
- slider.max = setting.max;
- slider.step = setting.step || 0.05;
- slider.value = setting.value;
- const input = document.querySelector("#trackerSetting_" + setting.id + "_value");
- input.min = setting.min;
- input.max = setting.max;
- input.step = setting.step || 0.05;
- input.value = setting.value;
- const updateChannel = value => {
- value = Math.min(setting.max, Math.max(setting.min, parseFloat(value)));
- slider.value = value;
- input.value = value;
- settingsMap[setting.id].value = value;
- };
- slider.addEventListener('input', e => updateChannel(e.target.value));
- input.addEventListener('change', e => updateChannel(e.target.value));
- } else if (setting.list) {
- insertElem.insertAdjacentHTML("beforeend", `<div class="tracker-option">${setting.desc}<select id="trackerSetting_${setting.id}"></select></div>`);
- const select = document.querySelector("#trackerSetting_" + setting.id);
- for (const option of params[setting.id]) {
- select.insertAdjacentHTML("beforeend", `<option value="${option}" ${option === setting.value ? "selected" : ""}>${option}</option>`);
- }
- select.addEventListener("change", e => {
- settingsMap[setting.id].value = e.target.value;
- saveSettings();
- });
- }
- }
- }
- insertElem.addEventListener("change", saveSettings);
- }
- }
- setTimeout(() => {
- waitForSettings(params);
- }, 500);
- }
- function saveSettings() {
- localStorage.setItem("tracker_settingsMap", JSON.stringify(settingsMap));
- }
- function readSettings() {
- const ls = localStorage.getItem("tracker_settingsMap");
- if (ls) {
- const lsObj = JSON.parse(ls);
- for (const option of Object.values(lsObj)) {
- if (option.id.startsWith("tracker")) {
- if (settingsMap.hasOwnProperty(option.id)) {
- settingsMap[option.id].isTrue = option.isTrue;
- settingsMap[option.id].trackStyle = option.trackStyle || "auto";
- settingsMap[option.id].r = option.r;
- settingsMap[option.id].g = option.g;
- settingsMap[option.id].b = option.b;
- }
- } else if (option && option.value && option.id && settingsMap[option.id]) {
- settingsMap[option.id].value = option.value;
- }
- }
- }
- }
- const style = document.createElement('style');
- style.textContent = `
- .tracker-option {
- display: flex;
- align-items: left;
- gap: 10px;
- }
- .color-preview {
- cursor: pointer;
- width: 20px;
- height: 20px;
- margin: 3px 3px;
- border: 1px solid #ccc;
- border-radius: 3px;
- }
- .color-picker-modal {
- position: fixed;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- background: rgba(0, 0, 0, 0.5);
- padding: 20px;
- border: 1px solid rgba(255, 255, 255, 0.2);
- border-radius: 8px;
- box-shadow: 0 0 20px rgba(0,0,0,0.2);
- z-index: 1000;
- }
- .modal-backdrop {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(0,0,0,0.5);
- z-index: 999;
- }
- .modal-actions {
- margin-top: 20px;
- display: flex;
- gap: 10px;
- justify-content: flex-end;
- }
- `;
- document.head.appendChild(style);
- function changeColorAlpha(rgba, alpha) {
- if (rgba.startsWith('rgba')) {
- return rgba.replace(/rgba\(([^,]+),([^,]+),([^,]+),[^)]+\)/, `rgba($1,$2,$3,${alpha})`);
- } else if (rgba.startsWith('rgb')) {
- return rgba.replace(/rgb\(([^,]+),([^,]+),([^,]+)\)/, `rgba($1,$2,$3,${alpha})`);
- } else if (rgba.startsWith('hsl')) {
- return rgba.replace(/hsl\(([^,]+),([^,]+),([^)]+)\)/, `hsla($1,$2,$3,${alpha})`);
- } else if (rgba.startsWith('hsla')) {
- return rgba.replace(/hsla\(([^,]+),([^,]+),([^)]+),[^)]+\)/, `hsla($1,$2,$3,${alpha})`);
- }
- return rgba;
- }
- function getElementCenter(element) {
- const rect = element.getBoundingClientRect();
- if (element.innerText.trim() === '') {
- return {
- x: rect.left + rect.width / 2,
- y: rect.top
- };
- }
- return {
- x: rect.left + rect.width / 2,
- y: rect.top + rect.height / 2
- };
- }
- const shapes = {
- "circle": (ctx, p = {}) => {
- // {x, y, size, color}
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
- ctx.fillStyle = p.color;
- ctx.fill();
- },
- "rectangle": (ctx, p = {}) => {
- // {x, y, size, color}
- ctx.beginPath();
- ctx.fillStyle = p.color;
- ctx.fillRect(p.x, p.y, p.size, p.size);
- ctx.closePath();
- },
- "star": (ctx, p = {}) => {
- // {x, y, size, color, angle}
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.rotate(p.angle);
- const starSize = p.size * 10;
- ctx.beginPath();
- const startAngle = -Math.PI / 2;
- const startX = Math.cos(startAngle) * starSize;
- const startY = Math.sin(startAngle) * starSize;
- ctx.moveTo(startX, startY);
- for (let i = 0; i < 5; i++) {
- const outerAngle = i * 2 * Math.PI / 5 - Math.PI / 2;
- const innerAngle = outerAngle + Math.PI / 5;
- const outerX = Math.cos(outerAngle) * starSize;
- const outerY = Math.sin(outerAngle) * starSize;
- ctx.lineTo(outerX, outerY);
- const innerX = Math.cos(innerAngle) * (starSize / 2);
- const innerY = Math.sin(innerAngle) * (starSize / 2);
- ctx.lineTo(innerX, innerY);
- }
- ctx.closePath();
- ctx.fillStyle = p.color;
- ctx.fill();
- ctx.restore();
- },
- "arrow": (ctx, p = {}) => {
- // {x, y, size, color, velocity, arrowLength, arrowWidth, arrowHeadLength, arrowHeadWidth, fletchingLength, fletchingWidth}
- const length = p.size * (p.arrowLength || 6);
- const width = p.size * (p.arrowWidth || 0.5);
- const arrowHeadLength = p.size * (p.arrowHeadLength || 1.33);
- const arrowHeadWidth = p.size * (p.arrowHeadWidth || 0.80);
- const fletchingLength = p.size * (p.fletchingLength || 2.13);
- const fletchingWidth = p.size * (p.fletchingWidth || 1.33);
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.rotate(Math.atan2(p.velocity.y, p.velocity.x));
- // Draw arrow shaft
- ctx.beginPath();
- ctx.moveTo(-length / 2, -width / 2);
- ctx.lineTo(length / 2 - arrowHeadLength, -width / 2);
- ctx.lineTo(length / 2 - arrowHeadLength, width / 2);
- ctx.lineTo(-length / 2, width / 2);
- ctx.closePath();
- ctx.fillStyle = p.color;
- ctx.fill();
- // Draw arrow head
- ctx.beginPath();
- ctx.moveTo(length / 3 - arrowHeadLength, -arrowHeadWidth / 2);
- ctx.lineTo(length / 2, 0);
- ctx.lineTo(length / 3 - arrowHeadLength, arrowHeadWidth / 2);
- ctx.closePath();
- ctx.fillStyle = p.color;
- ctx.fill();
- // Draw fletchings
- ctx.beginPath();
- ctx.moveTo(-length / 2, -width / 2);
- ctx.lineTo(-length / 2 - fletchingLength, -fletchingWidth / 2);
- ctx.lineTo(-length / 2 - fletchingLength * 0.5, 0);
- ctx.lineTo(-length / 2 - fletchingLength, fletchingWidth / 2);
- ctx.lineTo(-length / 2, width / 2);
- ctx.closePath();
- ctx.fillStyle = p.color;
- ctx.fill();
- ctx.restore();
- }
- };
- /*
- 特效编写请查阅
- https://docs.qq.com/doc/DS0JjVHp3S09td2NV
- */
- const onHitEffectsMap = {
- "smoke": {
- angle: p => Math.random() * Math.PI * 2,
- alpha: p => 0.7,
- speed: p => (Math.random() * 0.2 + 0.1) * Math.sqrt(p.size),
- size: p => (Math.random() * 20 + 10) * p.size,
- life: p => 4000 * Math.sqrt(p.size),
- gravity: p => -0.2 * Math.sqrt(p.size),
- draw: (ctx, p) => {
- if (!p.initialized) {
- p.initialized = true;
- p.y -= 5 * p.size;
- p.sizeVariation = Math.random() * 0.2 + 0.9; // Size variation for billowing effect
- p.rotationSpeed = (Math.random() - 0.5) * 0.02; // Slow rotation
- p.rotation = Math.random() * Math.PI * 2;
- }
- p.speed *= 0.995; // Slower deceleration
- p.x += Math.cos(p.angle) * p.speed;
- p.y += Math.sin(p.angle) * p.speed + p.gravity;
- p.life -= 1;
- p.alpha = Math.max(0, p.alpha - 0.001);
- p.rotation += p.rotationSpeed;
- if (p.life > 0) {
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.rotate(p.rotation);
- // Draw main smoke puff
- ctx.beginPath();
- ctx.ellipse(0, 0, p.size * p.sizeVariation, p.size, 0, 0, Math.PI * 2);
- ctx.fillStyle = `rgba(80, 80, 80, ${p.alpha * (p.life / 2000)})`;
- ctx.fill();
- // Add some variation to the smoke puff
- ctx.beginPath();
- ctx.ellipse(p.size * 0.3, -p.size * 0.2, p.size * 0.6, p.size * 0.8, 0, 0, Math.PI * 2);
- ctx.fillStyle = `rgba(80, 80, 80, ${p.alpha * 0.7 * (p.life / 2000)})`;
- ctx.fill();
- ctx.restore();
- }
- }
- },
- "ember": {
- angle: p => Math.random() * Math.PI * 2,
- alpha: p => 1,
- speed: p => (Math.random() * 2 + 0.5) * Math.sqrt(p.size),
- size: p => (Math.random() * 6 + 2) * p.size,
- life: p => 1200 * Math.sqrt(p.size),
- gravity: p => 0.3,
- draw: (ctx, p) => {
- p.speed *= 0.99; // 慢慢减速
- p.x += Math.cos(p.angle) * p.speed;
- p.y += Math.sin(p.angle) * p.speed + p.gravity;
- p.life -= 3;
- if (p.life > 0) {
- const alpha = p.life / 800;
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size * (p.life / 800), 0, Math.PI * 2);
- ctx.fillStyle = `${p.color.slice(0, -4)}%, ${alpha})`;
- ctx.fill();
- // 余烬偶尔产生的小火花
- if (Math.random() < 0.03) {
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size * 1.5, 0, Math.PI * 2);
- ctx.fillStyle = `hsla(30, 100%, 70%, ${alpha * 0.7})`;
- ctx.fill();
- }
- }
- }
- },
- "shockwave": {
- size: p => 10 * p.size,
- life: p => 800 * Math.sqrt(p.size),
- draw: (ctx, p) => {
- if (!p.maxSize) {
- p.maxSize = p.size * (150 + Math.random() * 100) / 10;
- }
- p.size += (p.maxSize - p.size) * 0.1;
- p.life -= 10;
- if (p.life > 0) {
- const alpha = p.life / 400;
- ctx.beginPath();
- ctx.strokeStyle = p.color;
- ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
- ctx.lineWidth = 5 * alpha;
- ctx.stroke();
- }
- }
- },
- "smallParticle": {
- angle: p => Math.random() * Math.PI * 2,
- size: p => (Math.random() * 12 + 8) * p.size,
- speed: p => (Math.random() * 6 + 2) * Math.sqrt(p.size),
- gravity: p => 0.3 + Math.random() * 0.1,
- life: p => 400 * p.size,
- draw: (ctx, p) => {
- p.size = p.size * (1 - p.life / 400);
- p.x += Math.cos(p.angle) * p.speed;
- p.y += Math.sin(p.angle) * p.speed + p.gravity;
- p.life -= 3;
- if (p.life > 0) {
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
- ctx.fillStyle = p.color;
- ctx.fill();
- }
- }
- },
- "holyCross": {
- x: p => p.x + (Math.random() - 0.5) * 60,
- y: p => p.y + (Math.random() - 0.5) * 10,
- size: p => (8 * Math.random() + 12) * p.size,
- life: p => 1200 * Math.sqrt(p.size),
- speed: p => 0,
- gravity: p => -0.008 * Math.random() - 0.008,
- draw: (ctx, p) => {
- p.speed += p.gravity;
- p.y += p.speed;
- p.life -= 3;
- if (p.life > 0) {
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.fillStyle = p.color;
- ctx.fillRect(-p.size / 2, -p.size * 2, p.size, p.size * 4);
- ctx.fillRect(-p.size * 2, -p.size / 2, p.size * 4, p.size);
- ctx.restore();
- }
- }
- },
- "leaf": {
- // Made by HwiteCat
- x: p => p.x + (Math.random() - 0.5) * 60,
- y: p => p.y + (Math.random() - 0.5) * 10,
- angle: p => Math.random() * Math.PI * 2,
- size: p => (12 * Math.random() + 8) * p.size,
- life: p => 1250 * p.size,
- speed: p => (Math.random() * 3 + 1) * Math.sqrt(p.size),
- gravity: p => 0.12,
- draw: (ctx, p) => {
- if (!p.rotation) p.rotation = Math.random() * Math.PI * 2;
- if (!p.rotationSpeed) p.rotationSpeed = (Math.random() - 0.5) * 0.02;
- if (!p.sway) p.sway = (Math.random() - 0.5) * 0.2;
- if (!p.swaySpeed) p.swaySpeed = (Math.random() - 0.5) * 0.02;
- p.speed *= 0.98;
- p.x += Math.cos(p.angle) * p.speed;
- p.y += Math.sin(p.angle) * p.speed + p.gravity;
- p.life -= 3;
- if (p.rotation !== undefined) {
- p.rotation += p.rotationSpeed;
- }
- if (p.scale !== undefined) {
- p.scale += p.scaleSpeed;
- p.scale = Math.max(0.1, p.scale);
- }
- if (p.sway !== undefined) {
- p.x += Math.sin(p.y * p.swaySpeed) * p.sway;
- }
- if (p.life > 0) {
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.rotate(p.rotation);
- ctx.scale(p.scale, 1);
- ctx.beginPath();
- ctx.moveTo(0, -p.size);
- ctx.bezierCurveTo(p.size / 2, -p.size / 2, p.size / 2, 0, 0, p.size);
- ctx.bezierCurveTo(-p.size / 2, 0, -p.size / 2, -p.size / 2, 0, -p.size);
- ctx.fillStyle = p.color;
- ctx.fill();
- ctx.restore();
- }
- }
- },
- "slash": {
- // Main slash effect
- x: p => p.x,
- y: p => p.y,
- angle: p => Math.random() * Math.PI * 2,
- size: p => 3 * p.size,
- life: p => 300 * p.size,
- draw: (ctx, p) => {
- if (!p.length) p.length = p.size * (120 + Math.random() * 80); // More consistent length
- if (!p.maxWidth) p.maxWidth = 1.5 * Math.sqrt(p.size); // Thinner slash
- p.life -= 2; // Even slower fade
- if (p.life > 0) {
- const alpha = p.life / 300 * p.size;
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.rotate(p.angle);
- // Draw main slash line with improved tapered shape
- ctx.beginPath();
- ctx.moveTo(-p.length / 2, 0);
- ctx.quadraticCurveTo(-p.length / 4, -p.maxWidth * 0.6, -p.length / 6, -p.maxWidth);
- ctx.lineTo(p.length / 6, -p.maxWidth);
- ctx.quadraticCurveTo(p.length / 4, -p.maxWidth * 0.6, p.length / 2, 0);
- ctx.quadraticCurveTo(p.length / 4, p.maxWidth * 0.6, p.length / 6, p.maxWidth);
- ctx.lineTo(-p.length / 6, p.maxWidth);
- ctx.quadraticCurveTo(-p.length / 4, p.maxWidth * 0.6, -p.length / 2, 0);
- ctx.closePath();
- ctx.fillStyle = p.color.replace('0.9', alpha.toString());
- ctx.fill();
- // Enhanced glow effect
- ctx.beginPath();
- ctx.moveTo(-p.length / 2, 0);
- ctx.quadraticCurveTo(-p.length / 4, -p.maxWidth * 0.8, -p.length / 6, -p.maxWidth * 1.5);
- ctx.lineTo(p.length / 6, -p.maxWidth * 1.5);
- ctx.quadraticCurveTo(p.length / 4, -p.maxWidth * 0.8, p.length / 2, 0);
- ctx.quadraticCurveTo(p.length / 4, p.maxWidth * 0.8, p.length / 6, p.maxWidth * 1.5);
- ctx.lineTo(-p.length / 6, p.maxWidth * 1.5);
- ctx.quadraticCurveTo(-p.length / 4, p.maxWidth * 0.8, -p.length / 2, 0);
- ctx.closePath();
- ctx.fillStyle = p.color.replace('0.9', (alpha * 0.3).toString());
- ctx.fill();
- ctx.restore();
- }
- }
- },
- "slashParticle": {
- // Enhanced particle effect for slash
- x: p => p.x + (Math.random() - 0.5) * 15,
- // Tighter initial spread
- y: p => p.y + (Math.random() - 0.5) * 15,
- angle: p => {
- const baseAngle = p.parentAngle || Math.random() * Math.PI * 2;
- return baseAngle + (Math.random() - 0.5) * 0.1; // Very small variation
- },
- size: p => (2 * Math.random() + 2) * p.size,
- // Bigger particles
- life: p => 600 * p.size,
- // Adjusted for faster movement
- speed: p => (Math.random() * 1 + 3) * Math.sqrt(p.size),
- // Much faster speed
- gravity: p => 0.02,
- // Minimal gravity for more directional movement
- draw: (ctx, p) => {
- p.speed *= 0.998; // Very smooth deceleration
- p.x += Math.cos(p.angle) * p.speed;
- p.y += Math.sin(p.angle) * p.speed + p.gravity;
- p.life -= 3;
- if (p.life > 0) {
- const alpha = p.life / 400;
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.rotate(p.angle);
- // Draw particle with more elongation in movement direction
- ctx.beginPath();
- ctx.moveTo(-p.size / 2, 0);
- ctx.quadraticCurveTo(-p.size / 4, -p.size / 2, 0, -p.size * 1.2);
- ctx.quadraticCurveTo(p.size / 4, -p.size / 2, p.size / 2, 0);
- ctx.quadraticCurveTo(p.size / 4, p.size / 2, 0, p.size * 1.2);
- ctx.quadraticCurveTo(-p.size / 4, p.size / 2, -p.size / 2, 0);
- ctx.closePath();
- ctx.fillStyle = p.color.replace('0.9', alpha.toString());
- ctx.fill();
- // Add small glow to particles
- ctx.beginPath();
- ctx.arc(0, 0, p.size * 1.2, 0, Math.PI * 2);
- ctx.fillStyle = p.color.replace('0.9', (alpha * 0.3).toString());
- ctx.fill();
- ctx.restore();
- }
- }
- },
- "waterRipple": {
- x: p => p.x,
- y: p => p.y,
- size: p => 3 * p.size,
- life: p => 1200 * p.size,
- draw: (ctx, p) => {
- if (!p.ripples) {
- p.ripples = [{
- radius: 0,
- opacity: 0.5,
- width: 3,
- speed: 0.7
- },
- // Fast, bright inner ripple
- {
- radius: 0,
- opacity: 0.5,
- width: 2,
- speed: 0.5
- },
- // Medium ripple
- {
- radius: 0,
- opacity: 0.5,
- width: 1.5,
- speed: 0.3
- } // Slow, faint outer ripple
- ];
- }
- p.life -= 1;
- // Update each ripple
- p.ripples.forEach((ripple, index) => {
- // Expand the ripple
- ripple.radius += ripple.speed;
- // Calculate opacity based on radius
- const maxRadius = 30 * p.size;
- const fadeStart = maxRadius * 0.6;
- if (ripple.radius > fadeStart) {
- ripple.opacity *= 0.98; // Gradual fade out
- }
- // Draw the ripple if it's still visible
- if (ripple.opacity > 0.05 && ripple.radius < maxRadius) {
- ctx.beginPath();
- ctx.strokeStyle = p.color.replace('0.8', ripple.opacity.toString());
- ctx.lineWidth = ripple.width * (1 - ripple.radius / maxRadius);
- ctx.arc(p.x, p.y, ripple.radius, 0, Math.PI * 2);
- ctx.stroke();
- // Add a second, fainter ring for more water-like effect
- if (ripple.radius > 5) {
- ctx.beginPath();
- ctx.strokeStyle = p.color.replace('0.8', (ripple.opacity * 0.5).toString());
- ctx.lineWidth = ripple.width * 0.5 * (1 - ripple.radius / maxRadius);
- ctx.arc(p.x, p.y, ripple.radius - 2, 0, Math.PI * 2);
- ctx.stroke();
- }
- }
- });
- }
- },
- "waterSplash": {
- x: p => p.x,
- y: p => p.y,
- size: p => (2 * Math.random() + 5) * p.size,
- // Smaller size
- life: p => 800 * p.size,
- draw: (ctx, p) => {
- if (!p.initialized) {
- p.initialized = true;
- p.particles = [];
- // Create particles in a circular pattern
- const particleCount = 7; // More particles for better coverage
- for (let i = 0; i < particleCount; i++) {
- const angle = i / particleCount * Math.PI * 2;
- // Add some random variation to the angle
- const angleVariation = (Math.random() - 0.5) * 0.5;
- const finalAngle = angle + angleVariation;
- // Create size variation with smaller base size
- const sizeVariation = Math.random() * 1.5 + 0.5; // Random multiplier between 0.5 and 2
- const baseSize = (Math.random() * 0.8 + 0.4) * p.size; // Reduced base size
- p.particles.push({
- x: p.x,
- y: p.y,
- angle: finalAngle,
- speed: (Math.random() * 1.5 + 1) * Math.sqrt(p.size),
- size: baseSize * sizeVariation,
- initialSize: baseSize * sizeVariation,
- life: 800 * p.size,
- gravity: 0.9 + (Math.random() * 0.2 - 0.1) // Slight gravity variation
- });
- }
- }
- p.life -= 2;
- // Update and draw particles
- p.particles.forEach(particle => {
- particle.speed *= 0.98; // Deceleration
- particle.x += Math.cos(particle.angle) * particle.speed;
- particle.y += Math.sin(particle.angle) * particle.speed + particle.gravity;
- particle.life -= 2;
- const lifeRatio = particle.life / (800 * p.size);
- const opacity = lifeRatio * 0.6; // More transparent
- // More dramatic shrinking with cubic easing
- particle.size = particle.initialSize * Math.pow(lifeRatio, 3);
- if (particle.life > 0) {
- ctx.beginPath();
- ctx.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
- ctx.fillStyle = p.color.replace('0.8', opacity.toString());
- ctx.fill();
- }
- });
- }
- },
- "star": {
- x: p => p.x + (Math.random() - 0.5) * 60,
- y: p => p.y + (Math.random() - 0.5) * 10,
- angle: p => Math.random() * Math.PI * 2,
- size: p => (Math.random() * 6 + 2) * p.size,
- life: p => 1200 * Math.sqrt(p.size),
- speed: p => (Math.random() * 6 + 2) * Math.sqrt(p.size),
- gravity: p => -0.1,
- draw: (ctx, p) => {
- if (!p.initialized) {
- p.initialized = true;
- p.y -= 5 * p.size;
- }
- p.speed *= 0.97; // 慢慢减速
- p.x += Math.cos(p.angle) * p.speed;
- p.y += Math.sin(p.angle) * p.speed + p.gravity;
- p.life -= 3;
- if (p.life > 0) {
- const alpha = Math.max(0, Math.min(1, p.life / 1200));
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.rotate(p.angle);
- const starSize = p.size * 10;
- ctx.beginPath();
- const startAngle = -Math.PI / 2;
- const startX = Math.cos(startAngle) * starSize;
- const startY = Math.sin(startAngle) * starSize;
- ctx.moveTo(startX, startY);
- for (let i = 0; i < 5; i++) {
- const outerAngle = i * 2 * Math.PI / 5 - Math.PI / 2;
- const innerAngle = outerAngle + Math.PI / 5;
- const outerX = Math.cos(outerAngle) * starSize;
- const outerY = Math.sin(outerAngle) * starSize;
- ctx.lineTo(outerX, outerY);
- const innerX = Math.cos(innerAngle) * (starSize / 2);
- const innerY = Math.sin(innerAngle) * (starSize / 2);
- ctx.lineTo(innerX, innerY);
- }
- ctx.closePath();
- ctx.fillStyle = p.color.replace(/rgba\(([^,]+),([^,]+),([^,]+),[^)]+\)/, `rgba($1,$2,$3,${alpha})`);
- ctx.fill();
- ctx.restore();
- }
- }
- },
- "pierce": {
- x: p => p.x,
- y: p => p.y,
- size: p => 4 * p.size,
- life: p => 1200 * p.size,
- draw: (ctx, p) => {
- if (!p.initialized) {
- p.initialized = true;
- p.pierceLength = p.size * 16;
- p.pierceWidth = p.size / 10;
- p.time = 0;
- p.ripples = [];
- // Create initial ripples
- for (let i = 0; i < 3; i++) {
- p.ripples.push({
- radius: 0,
- speed: 0.5 + i * 0.2,
- opacity: 0.6 - i * 0.15,
- width: 2 - i * 0.5
- });
- }
- }
- p.life -= 2;
- p.time += 0.1;
- const alpha = p.life / 1200;
- if (p.life > 0) {
- ctx.save();
- ctx.translate(p.x, p.y);
- // Draw dynamic ripples
- p.ripples.forEach(ripple => {
- ripple.radius += ripple.speed;
- const rippleAlpha = ripple.opacity * alpha * (1 - ripple.radius / (p.size * 8));
- if (rippleAlpha > 0.01) {
- ctx.beginPath();
- ctx.strokeStyle = p.color.replace('0.8', rippleAlpha.toString());
- ctx.lineWidth = ripple.width;
- ctx.arc(0, 0, ripple.radius, 0, Math.PI * 2);
- ctx.stroke();
- }
- });
- // 4角星星
- const vertices = {
- top: {
- x: 0,
- y: -p.pierceLength / 2.5
- },
- // Reduced vertical height
- right: {
- x: p.pierceLength,
- y: 0
- },
- // Maintained horizontal stretch
- bottom: {
- x: 0,
- y: p.pierceLength / 2.5
- },
- // Reduced vertical height
- left: {
- x: -p.pierceLength,
- y: 0
- } // Maintained horizontal stretch
- };
- // Define inner points for curved connections (closer to center)
- const innerPoints = {
- topRight: {
- x: p.pierceLength / 7,
- y: -p.pierceLength / 10
- },
- // Moved closer to center
- bottomRight: {
- x: p.pierceLength / 7,
- y: p.pierceLength / 10
- },
- // Moved closer to center
- bottomLeft: {
- x: -p.pierceLength / 7,
- y: p.pierceLength / 10
- },
- // Moved closer to center
- topLeft: {
- x: -p.pierceLength / 7,
- y: -p.pierceLength / 10
- } // Moved closer to center
- };
- // Draw the shape with straight lines
- ctx.beginPath();
- ctx.moveTo(vertices.top.x, vertices.top.y);
- // Draw straight lines between vertices and inner points
- // Top to right
- ctx.lineTo(innerPoints.topRight.x, innerPoints.topRight.y);
- ctx.lineTo(vertices.right.x, vertices.right.y);
- // Right to bottom
- ctx.lineTo(innerPoints.bottomRight.x, innerPoints.bottomRight.y);
- ctx.lineTo(vertices.bottom.x, vertices.bottom.y);
- // Bottom to left
- ctx.lineTo(innerPoints.bottomLeft.x, innerPoints.bottomLeft.y);
- ctx.lineTo(vertices.left.x, vertices.left.y);
- // Left to top
- ctx.lineTo(innerPoints.topLeft.x, innerPoints.topLeft.y);
- ctx.lineTo(vertices.top.x, vertices.top.y);
- ctx.closePath();
- // Add main fill with enhanced opacity
- ctx.fillStyle = p.color.replace('0.8', (alpha * 0.9).toString());
- ctx.fill();
- ctx.restore();
- }
- }
- },
- "poison": {
- x: p => p.x,
- y: p => p.y,
- size: p => 5 * p.size,
- // Increased base size
- life: p => 800 * p.size,
- // Longer lifetime
- draw: (ctx, p) => {
- if (!p.initialized) {
- p.initialized = true;
- p.bubbles = [];
- for (let i = 0; i < 6; i++) {
- // More bubbles
- p.bubbles.push({
- x: p.x + (Math.random() - 0.5) * p.size * 4,
- // Wider spread
- y: p.y + (Math.random() - 0.5) * p.size * 4,
- size: p.size * (Math.random() * 1.2 + 1.2),
- // Bigger bubbles
- speed: Math.random() * 0.8 + 0.4,
- // Faster rise
- wobble: Math.random() * Math.PI * 2,
- // For side-to-side movement
- wobbleSpeed: Math.random() * 0.05 + 0.02
- });
- }
- }
- p.life -= 1;
- const alpha = Math.pow(p.life / p.maxLife, 0.7);
- if (p.life > 0) {
- // Draw main poison cloud
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size * alpha, 0, Math.PI * 2);
- ctx.fillStyle = changeColorAlpha(p.color, alpha * 0.8);
- ctx.fill();
- // Draw and update bubbles
- p.bubbles.forEach(bubble => {
- bubble.y -= bubble.speed;
- bubble.wobble += bubble.wobbleSpeed;
- bubble.x += Math.sin(bubble.wobble) * 0.5;
- ctx.beginPath();
- ctx.arc(bubble.x, bubble.y, bubble.size, 0, Math.PI * 2);
- ctx.fillStyle = changeColorAlpha(p.color, alpha);
- ctx.fill();
- // Add bubble highlight
- ctx.beginPath();
- ctx.arc(bubble.x - bubble.size * 0.3, bubble.y - bubble.size * 0.3, bubble.size * 0.3, 0, Math.PI * 2);
- ctx.fillStyle = `rgba(255, 255, 255, ${alpha * 0.3})`;
- ctx.fill();
- });
- }
- }
- },
- "ice": {
- x: p => p.x,
- y: p => p.y,
- speed: p => (Math.random() * 3 + 1.5) * Math.sqrt(p.size),
- size: p => (2 * Math.random() + 3) * p.size,
- life: p => 1200 * p.size,
- draw: (ctx, p) => {
- p.length = p.size * 7;
- p.speed *= 0.96;
- p.x += Math.cos(p.angle) * p.speed;
- p.y += Math.sin(p.angle) * p.speed;
- p.life -= 1;
- const lifeRatio = p.life / p.maxLife;
- const alpha = Math.pow(lifeRatio, 0.2);
- if (p.life > 0) {
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.rotate(p.angle + Math.PI / 2);
- ctx.beginPath();
- ctx.moveTo(0, -p.length / 2);
- ctx.lineTo(p.size / 2, 0);
- ctx.lineTo(0, p.length / 2);
- ctx.lineTo(-p.size / 2, 0);
- ctx.closePath();
- ctx.fillStyle = changeColorAlpha(p.color, alpha);
- ctx.fill();
- ctx.strokeStyle = changeColorAlpha(p.color, alpha);
- ctx.lineWidth = 2;
- ctx.stroke();
- // Add white glow
- ctx.beginPath();
- ctx.moveTo(0, -p.length / 2);
- ctx.lineTo(p.size / 2, 0);
- ctx.lineTo(0, p.length / 2);
- ctx.lineTo(-p.size / 2, 0);
- ctx.closePath();
- // Create gradient for glow
- const gradient = ctx.createLinearGradient(0, -p.length / 2, 0, p.length / 2);
- gradient.addColorStop(0, `rgba(255, 255, 255, ${alpha * 0.8})`);
- gradient.addColorStop(0.5, `rgba(255, 255, 255, ${alpha * 0.4})`);
- gradient.addColorStop(1, `rgba(255, 255, 255, ${alpha * 0.8})`);
- ctx.fillStyle = gradient;
- ctx.fill();
- // Add shiny highlight
- ctx.beginPath();
- ctx.moveTo(-p.size / 4, -p.length / 4);
- ctx.lineTo(p.size / 4, -p.length / 4);
- ctx.lineTo(0, 0);
- ctx.closePath();
- const highlightGradient = ctx.createLinearGradient(-p.size / 4, -p.length / 4, 0, 0);
- highlightGradient.addColorStop(0, `rgba(255, 255, 255, ${alpha * 0.9})`);
- highlightGradient.addColorStop(1, `rgba(255, 255, 255, 0)`);
- ctx.fillStyle = highlightGradient;
- ctx.fill();
- ctx.restore();
- }
- }
- },
- "lava": {
- x: p => p.x + (Math.random() - 0.5) * p.size * 5,
- y: p => p.y + (Math.random() - 0.5) * p.size * 2,
- size: p => (14 * Math.random() + 20) * p.size,
- angle: p => (Math.random() - 0.5) * Math.PI / 5 * 2 - Math.PI / 2,
- speed: p => (Math.random() * 7 + 5) * Math.sqrt(p.size),
- gravity: p => 1.2 + (Math.random() * 0.2 - 0.1),
- life: p => 1200 * p.size,
- draw: (ctx, p) => {
- if (!p.initialized) {
- p.initialized = true;
- p.particles = [];
- // Particle configuration
- const numPoints = 16;
- p.noiseOffsets = Array.from({
- length: numPoints
- }, () => Math.random() * Math.PI * 2);
- p.noiseAmplitudes = Array.from({
- length: numPoints
- }, () => Math.random() * 0.15 + 0.85);
- p.noiseSpeeds = Array.from({
- length: numPoints
- }, () => Math.random() * 0.03 + 0.02);
- p.time = 0;
- p.rotation = Math.random() * Math.PI * 2;
- p.rotationSpeed = (Math.random() - 0.5) * 0.05;
- }
- p.life -= 1;
- p.speed *= 0.98;
- p.x += Math.cos(p.angle) * p.speed;
- p.y += Math.sin(p.angle) * p.speed + p.gravity;
- p.life -= 1;
- p.rotation += p.rotationSpeed;
- p.time += 0.15;
- const lifeRatio = p.life / p.maxLife;
- const opacity = lifeRatio * 0.8;
- const sizeReduction = Math.pow(lifeRatio, 0.1);
- p.size = p.size * sizeReduction;
- if (p.life > 0) {
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.rotate(p.rotation);
- // Enable blending for better transparency
- ctx.globalCompositeOperation = 'lighter';
- // Draw base particle shape
- ctx.beginPath();
- for (let i = 0; i < p.numPoints; i++) {
- const angle = i / p.numPoints * Math.PI * 2;
- const noise = Math.sin(angle + p.noiseOffsets[i] + p.time * p.noiseSpeeds[i]) * p.noiseAmplitudes[i];
- const surfaceTension = Math.sin(angle * 3 + p.time * 0.5) * 0.15;
- const radius = p.size * (1 + noise * 0.2 + surfaceTension);
- const x = Math.cos(angle) * radius;
- const y = Math.sin(angle) * radius;
- if (i === 0) {
- ctx.moveTo(x, y);
- } else {
- const prevAngle = (i - 1) / p.numPoints * Math.PI * 2;
- const prevNoise = Math.sin(prevAngle + p.noiseOffsets[i - 1] + p.time * p.noiseSpeeds[i - 1]) * p.noiseAmplitudes[i - 1];
- const prevSurfaceTension = Math.sin(prevAngle * 3 + p.time * 0.5) * 0.15;
- const prevRadius = p.size * (1 + prevNoise * 0.2 + prevSurfaceTension);
- const prevX = Math.cos(prevAngle) * prevRadius;
- const prevY = Math.sin(prevAngle) * prevRadius;
- const cpX = (prevX + x) / 2;
- const cpY = (prevY + y) / 2;
- ctx.quadraticCurveTo(cpX, cpY, x, y);
- }
- }
- ctx.closePath();
- // Draw particle with glow effects
- // Base layer with reduced opacity
- ctx.fillStyle = changeColorAlpha(p.color, opacity);
- ctx.fill();
- // Glow layers with adjusted opacity
- const drawGlowLayer = (radius, color, alpha) => {
- ctx.beginPath();
- ctx.arc(0, 0, radius, 0, Math.PI * 2);
- ctx.fillStyle = changeColorAlpha(p.color, opacity);
- ctx.fill();
- };
- // Core and inner glow with reduced opacity
- drawGlowLayer(p.size * 0.6);
- drawGlowLayer(p.size * (1.2 + Math.sin(p.time * 0.5) * 0.2));
- // Middle aura with softer gradient
- const middleGlow = ctx.createRadialGradient(0, 0, p.size, 0, 0, p.size * 2);
- middleGlow.addColorStop(0, changeColorAlpha(p.color, opacity * 0.2));
- middleGlow.addColorStop(0.5, `rgba(255, 50, 0, ${opacity * 0.1})`);
- middleGlow.addColorStop(1, `rgba(255, 0, 0, 0)`);
- ctx.beginPath();
- ctx.arc(0, 0, p.size * 2, 0, Math.PI * 2);
- ctx.fillStyle = middleGlow;
- ctx.fill();
- // Outer aura with softer gradient
- const outerGlow = ctx.createRadialGradient(0, 0, p.size * 1.5, 0, 0, p.size * (2.5 + Math.sin(p.time * 0.5) * 0.3));
- outerGlow.addColorStop(0, changeColorAlpha(p.color, opacity * 0.1));
- outerGlow.addColorStop(0.5, `rgba(200, 0, 0, ${opacity * 0.03})`);
- outerGlow.addColorStop(1, `rgba(150, 0, 0, 0)`);
- ctx.beginPath();
- ctx.arc(0, 0, p.size * (2.5 + Math.sin(p.time * 0.5) * 0.3), 0, Math.PI * 2);
- ctx.fillStyle = outerGlow;
- ctx.fill();
- // Reset composite operation
- ctx.globalCompositeOperation = 'source-over';
- ctx.restore();
- }
- }
- },
- "tornado": {
- x: p => p.x,
- y: p => p.y + 20,
- size: p => 3 * p.size,
- life: p => 1250 * p.size,
- speed: p => (Math.random() * 3 + 1) * Math.sqrt(p.size),
- gravity: p => 0.12,
- draw: (ctx, p) => {
- if (!p.initialized) {
- p.initialized = true;
- p.particles = [];
- p.maxParticles = 6;
- p.amplitude = 60 * p.size;
- p.frequency = 15;
- p.timeSpeed = 0.8;
- p.maxHeight = 100;
- // Parse the base color once
- const colorMatch = p.color.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/);
- if (colorMatch) {
- p.baseColor = {
- r: parseInt(colorMatch[1]),
- g: parseInt(colorMatch[2]),
- b: parseInt(colorMatch[3])
- };
- }
- // Initialize particles with staggered heights
- for (let i = 0; i < p.maxParticles; i++) {
- const startAngle = Math.random() * Math.PI * 2;
- const startRadius = Math.random() * 40 * p.size;
- p.particles.push({
- angle: startAngle,
- radius: Math.random() * 150 + 5,
- height: i / p.maxParticles * p.maxHeight,
- speed: Math.random() * 0.04 + 0.01,
- size: Math.random() * 3 + 2,
- baseSpeed: Math.random() * 0.04 + 0.01,
- startX: Math.cos(startAngle) * startRadius,
- startY: Math.sin(startAngle) * startRadius,
- initialSize: Math.random() * 3 + 2,
- rotation: Math.random() * Math.PI * 2,
- rotationSpeed: (Math.random() - 0.5) * 0.02,
- sway: (Math.random() - 0.5) * 0.2,
- swaySpeed: (Math.random() - 0.5) * 0.02
- });
- }
- }
- p.life -= 3;
- if (p.life > 0) {
- const alpha = p.life;
- for (let particle of p.particles) {
- const heightRatio = particle.height / p.maxHeight;
- const currentSpeed = particle.baseSpeed * Math.pow(1 - heightRatio, 3);
- particle.angle += p.frequency * currentSpeed * p.timeSpeed;
- particle.radius += (Math.random() - 0.5) * 0.5;
- particle.height += 1.5 * p.timeSpeed;
- // Add leaf-like movement
- particle.rotation += particle.rotationSpeed;
- if (particle.sway !== undefined) {
- particle.startX += Math.sin(particle.height * particle.swaySpeed) * particle.sway;
- }
- if (particle.height > p.maxHeight) {
- particle.height = 0;
- const startAngle = Math.random() * Math.PI * 2;
- const startRadius = Math.random() * 40 * p.size;
- particle.startX = Math.cos(startAngle) * startRadius;
- particle.startY = Math.sin(startAngle) * startRadius;
- particle.angle = startAngle;
- particle.initialSize = Math.random() * 3 + 2;
- particle.rotation = Math.random() * Math.PI * 2;
- particle.rotationSpeed = (Math.random() - 0.5) * 0.02;
- particle.sway = (Math.random() - 0.5) * 0.2;
- particle.swaySpeed = (Math.random() - 0.5) * 0.02;
- }
- const spiral = particle.height / p.maxHeight * p.amplitude;
- const x = p.x + particle.startX + Math.cos(particle.angle) * spiral;
- const y = p.y - particle.height + particle.startY;
- // Calculate current size based on height and apply size limit
- const currentSize = Math.min(particle.initialSize * (1 - heightRatio * 0.7) * p.size, Math.min(Math.ceil(p.size * 6), 10));
- // Calculate gradient color based on height
- const gradientFactor = heightRatio * 1; // 100% 上面白
- const r = Math.min(255, p.baseColor.r + (255 - p.baseColor.r) * gradientFactor);
- const g = Math.min(255, p.baseColor.g + (255 - p.baseColor.g) * gradientFactor);
- const b = Math.min(255, p.baseColor.b + (255 - p.baseColor.b) * gradientFactor);
- // Draw main particle with size reduction
- ctx.save();
- ctx.translate(x, y);
- ctx.rotate(particle.rotation);
- ctx.beginPath();
- ctx.arc(0, 0, currentSize, 0, Math.PI * 2);
- ctx.fillStyle = `rgba(${Math.floor(r)}, ${Math.floor(g)}, ${Math.floor(b)}, ${alpha * 0.8})`;
- ctx.fill();
- // Add glow effect with size reduction
- ctx.beginPath();
- ctx.arc(0, 0, currentSize * 1.5, 0, Math.PI * 2);
- ctx.fillStyle = `rgba(${Math.floor(r)}, ${Math.floor(g)}, ${Math.floor(b)}, ${alpha * 0.3})`;
- ctx.fill();
- ctx.restore();
- }
- }
- }
- },
- "pixelSmoke": {
- x: p => p.x + (Math.random() - 0.5) * 80,
- y: p => p.y + (Math.random() - 0.5) * 80,
- angle: p => (Math.random() - 0.5) * Math.PI / 5 * 2 - Math.PI / 2,
- color: p => `hsl(0, 0%, ${Math.round(Math.random() * 65 + 10)}%)`,
- size: p => (Math.random() * 40 + 10) * p.size,
- speed: p => Math.random() * 0.5 * Math.sqrt(p.size),
- gravity: p => -0.3 + Math.random() * 0.2 * p.size,
- life: p => Math.floor((Math.random() * 700 + 100) * p.size),
- draw: (ctx, p) => {
- const alpha = Math.pow(p.life / p.maxLife, 0.2);
- p.x += Math.cos(p.angle) * p.speed * 0.3;
- p.speed *= 0.992;
- p.y += -Math.sin(p.speed) * 0.5;
- p.color = changeColorAlpha(p.color, alpha);
- p.life -= 1;
- if (p.life > 0) {
- shapes.rectangle(ctx, p);
- }
- }
- }
- };
- /*
- 特效编写请查阅
- https://docs.qq.com/doc/DS0JjVHp3S09td2NV
- */
- const projectileEffectsMap = {
- 'fireball': {
- speedFactor: 1,
- trailLength: 35,
- shake: true,
- onHit: {
- "smoke": size => Math.min(Math.ceil(size * 4), 8),
- "ember": size => Math.min(Math.ceil(size * 10), 40),
- "shockwave": size => Math.min(Math.ceil(size), 4),
- "smallParticle": size => Math.min(Math.ceil(size * 4), 10)
- },
- onCrit: {
- "star": size => Math.min(Math.ceil(size * 10), 20)
- },
- draw: (ctx, p) => {
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
- ctx.fillStyle = p.color;
- ctx.fill();
- },
- glow: (ctx, p) => {
- const gradient = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.size * 2);
- gradient.addColorStop(0, `${p.color}`);
- gradient.addColorStop(1, `${p.color}`);
- ctx.fillStyle = gradient;
- },
- trail: (ctx, p, i) => {
- const alpha = i / p.totalLength;
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size * alpha, 0, Math.PI * 2);
- ctx.fillStyle = changeColorAlpha(p.color, alpha);
- ctx.fill();
- }
- },
- 'nature': {
- speedFactor: 1,
- gravity: 0.1,
- trailLength: 60,
- shake: true,
- onHit: {
- "leaf": size => Math.min(Math.ceil(size * 30), 32)
- },
- draw: (ctx, p) => {
- const size = p.size * 3;
- p.rotation = Math.atan2(p.velocity.y, p.velocity.x) - Math.PI / 2;
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.rotate(p.rotation);
- ctx.scale(p.scale, 1);
- ctx.beginPath();
- ctx.moveTo(0, -size);
- ctx.bezierCurveTo(size / 2, -size / 2, size / 2, 0, 0, size);
- ctx.bezierCurveTo(-size / 2, 0, -size / 2, -size / 2, 0, -size);
- ctx.fillStyle = p.color;
- ctx.fill();
- ctx.restore();
- },
- trail: (ctx, p, i) => {
- const alpha = i / p.totalLength;
- p.x = p.x + (Math.random() - 0.5) * 5;
- p.y = p.y - (Math.random() - 0.5) * 1 + 0.02;
- ctx.beginPath();
- const lineWidth = p.size * Math.sqrt(alpha);
- ctx.strokeStyle = `${changeColorAlpha(p.color, alpha)}`;
- ctx.lineWidth = lineWidth;
- ctx.moveTo(p.x, p.y);
- ctx.lineTo(p.x + (Math.random() - 0.5) * 20, p.y + (Math.random() - 0.5) * 20);
- ctx.stroke();
- ctx.fill();
- }
- },
- 'slash': {
- speedFactor: 2,
- gravity: -0.2,
- trailLength: 30,
- shake: true,
- onHit: {
- "slash": size => Math.min(Math.ceil(size * 4), 8),
- "slashParticle": size => Math.min(Math.ceil(size * 8), 20)
- }
- // draw: (ctx, p) => {
- // ctx.beginPath();
- // ctx.moveTo(p.x, p.y + p.size * 2);
- // ctx.lineTo(p.x - p.size * 2, p.y - p.size * 2);
- // ctx.lineTo(p.x + p.size * 2, p.y - p.size * 2);
- // ctx.closePath();
- // ctx.fillStyle = p.color;
- // ctx.fill();
- // }
- },
- 'water': {
- speedFactor: 1.2,
- trailLength: 60,
- shake: true,
- onHit: {
- "waterRipple": size => Math.min(Math.ceil(size * 8), 12),
- "waterSplash": size => Math.min(Math.ceil(size * 8), 20)
- },
- draw: (ctx, p) => {
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
- ctx.fillStyle = p.color;
- ctx.fill();
- },
- trail: (ctx, p, i) => {
- const alpha = i / p.totalLength;
- p.x = p.x + (Math.random() - 0.5) * 5;
- p.y = p.y - (Math.random() - 0.5) * 1;
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size * alpha, 0, Math.PI * 2);
- ctx.fillStyle = changeColorAlpha(p.color, alpha);
- ctx.fill();
- }
- },
- 'heal': {
- trailLength: 60,
- shake: false,
- color: 'rgba(93, 212, 93, 0.8)',
- onHit: {
- "holyCross": size => Math.min(Math.ceil(size * 12), 10)
- },
- draw: (ctx, p) => {
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
- ctx.fillStyle = p.color;
- ctx.fill();
- }
- },
- 'range': {
- speedFactor: 1.5,
- gravity: 0.15,
- trailLength: 30,
- shake: true,
- onHit: {
- "shockwave": size => Math.min(Math.ceil(size), 4),
- "slashParticle": size => Math.min(Math.ceil(size * 8), 20)
- },
- draw: (ctx, p) => {
- const length = p.size * 6.65;
- const width = p.size * 0.47;
- const arrowHeadLength = p.size * 1.33;
- const arrowHeadWidth = p.size * 0.80;
- const fletchingLength = p.size * 2.13;
- const fletchingWidth = p.size * 1.33;
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.rotate(Math.atan2(p.velocity.y, p.velocity.x));
- // Draw arrow shaft
- ctx.beginPath();
- ctx.moveTo(-length / 2, -width / 2);
- ctx.lineTo(length / 2 - arrowHeadLength, -width / 2);
- ctx.lineTo(length / 2 - arrowHeadLength, width / 2);
- ctx.lineTo(-length / 2, width / 2);
- ctx.closePath();
- ctx.fillStyle = p.color;
- ctx.fill();
- // Draw arrow head
- ctx.beginPath();
- ctx.moveTo(length / 3 - arrowHeadLength, -arrowHeadWidth / 2);
- ctx.lineTo(length / 2, 0);
- ctx.lineTo(length / 3 - arrowHeadLength, arrowHeadWidth / 2);
- ctx.closePath();
- ctx.fillStyle = p.color;
- ctx.fill();
- // Draw fletchings
- ctx.beginPath();
- ctx.moveTo(-length / 2, -width / 2);
- ctx.lineTo(-length / 2 - fletchingLength, -fletchingWidth / 2);
- ctx.lineTo(-length / 2 - fletchingLength * 0.5, 0);
- ctx.lineTo(-length / 2 - fletchingLength, fletchingWidth / 2);
- ctx.lineTo(-length / 2, width / 2);
- ctx.closePath();
- ctx.fillStyle = p.color;
- ctx.fill();
- ctx.restore();
- },
- trail: (ctx, p, i) => {
- // Only show trail after the arrow has traveled some distance
- const startDelay = 5; // Number of frames to wait before showing trail
- if (i < startDelay) return;
- const trailLength = p.size * 20;
- const trailWidth = p.size * 0.27;
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.rotate(Math.atan2(p.vY, p.vX));
- // Draw simple line trail behind the arrow
- ctx.beginPath();
- ctx.moveTo(-trailLength / 2, -trailWidth / 2);
- ctx.lineTo(0, -trailWidth / 2); // Only draw up to the arrow's position
- ctx.lineTo(0, trailWidth / 2);
- ctx.lineTo(-trailLength / 2, trailWidth / 2);
- ctx.closePath();
- ctx.fillStyle = 'rgba(255, 255, 255, 0.1)'; // Fixed low opacity white
- ctx.fill();
- ctx.restore();
- }
- },
- 'selfHeal': {
- speedFactor: 10,
- trailLength: 0,
- gravity: 0,
- shake: false,
- color: 'rgba(93, 212, 93, 0.5)',
- onHit: {
- "holyCross": size => Math.min(Math.ceil(size * 12), 10)
- },
- draw: (ctx, p) => {}
- },
- 'selfManaRegen': {
- speedFactor: 10,
- trailLength: 0,
- gravity: 0,
- shake: false,
- color: 'rgba(68, 120, 241, 0.8)',
- onHit: {
- "holyCross": size => Math.min(Math.ceil(size * 12), 10)
- },
- draw: (ctx, p) => {}
- },
- 'debug': {
- speedFactor: 2,
- trailLength: 3,
- shake: true,
- onHit: {
- "pixelSmoke": size => Math.min(Math.ceil(size * 80), 50)
- },
- draw: (ctx, p) => {
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
- ctx.fillStyle = p.color;
- ctx.fill();
- }
- },
- 'lavaPlume': {
- speedFactor: 0.8,
- trailLength: 40,
- gravity: 0.1,
- shake: true,
- onHit: {
- "lava": size => Math.min(Math.ceil(size * 20), 20),
- "smallParticle": size => Math.min(Math.ceil(size * 10), 60)
- },
- draw: (ctx, p) => {
- // Draw main projectile
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
- ctx.fillStyle = p.color;
- ctx.fill();
- // Create inner glow gradient
- const innerGlow = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.size * 1.5);
- innerGlow.addColorStop(0, 'rgba(255, 255, 255, 0.8)');
- innerGlow.addColorStop(0.5, p.color);
- innerGlow.addColorStop(1, 'rgba(255, 0, 0, 0)');
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size * 1.5, 0, Math.PI * 2);
- ctx.fillStyle = innerGlow;
- ctx.fill();
- },
- glow: (ctx, p) => {
- // Create outer glow gradient
- const outerGlow = ctx.createRadialGradient(p.x, p.y, p.size * 1.5, p.x, p.y, p.size * 4);
- outerGlow.addColorStop(0, p.color);
- // outerGlow.addColorStop(0.5, 'rgba(250, 178, 24, 0.2)');
- outerGlow.addColorStop(1, 'rgba(255, 50, 0, 0)');
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size * 4, 0, Math.PI * 2);
- ctx.fillStyle = outerGlow;
- ctx.fill();
- // Add pulsing effect
- const pulseSize = p.size * (3 + Math.sin(Date.now() * 0.01) * 0.5);
- const pulseGlow = ctx.createRadialGradient(p.x, p.y, p.size * 2, p.x, p.y, pulseSize);
- pulseGlow.addColorStop(0, changeColorAlpha(p.color, 0.1));
- pulseGlow.addColorStop(1, 'rgba(255, 100, 0, 0)');
- ctx.beginPath();
- ctx.arc(p.x, p.y, pulseSize, 0, Math.PI * 2);
- ctx.fillStyle = pulseGlow;
- ctx.fill();
- },
- trail: (ctx, p, i) => {
- const alpha = i / p.totalLength;
- const trailSize = p.size * alpha;
- // Create glowing trail gradient
- // const trailGlow = ctx.createRadialGradient(
- // p.x, p.y, 0,
- // p.x, p.y, trailSize * 2
- // );
- // trailGlow.addColorStop(0, changeColorAlpha(p.color, alpha));
- // trailGlow.addColorStop(1, changeColorAlpha(p.color, 0));
- ctx.beginPath();
- ctx.arc(p.x, p.y, trailSize * 2, 0, Math.PI * 2);
- ctx.fillStyle = changeColorAlpha(p.color, alpha);
- ctx.fill();
- }
- },
- 'iceBlast': {
- speedFactor: 1.3,
- trailLength: 35,
- shake: true,
- onHit: {
- "ice": size => Math.min(Math.ceil(size * 30), 40)
- },
- draw: (ctx, p) => {
- const length = p.size * 6.65;
- const arrowHeadLength = p.size * 3;
- const arrowHeadWidth = p.size * 2;
- // Draw main projectile
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.rotate(Math.atan2(p.velocity.y, p.velocity.x));
- // Draw arrow head
- ctx.beginPath();
- ctx.moveTo(length / 3 - arrowHeadLength, -arrowHeadWidth / 2);
- ctx.lineTo(length / 2, 0);
- ctx.lineTo(length / 3 - arrowHeadLength, arrowHeadWidth / 2);
- ctx.closePath();
- ctx.fillStyle = p.color;
- ctx.fill();
- ctx.restore();
- },
- glow: (ctx, p) => {
- // Create outer glow gradient
- const outerGlow = ctx.createRadialGradient(p.x, p.y, p.size * 1.5, p.x, p.y, p.size * 4);
- outerGlow.addColorStop(0, 'rgba(200, 230, 255, 0.3)');
- outerGlow.addColorStop(0.5, 'rgba(150, 200, 255, 0.2)');
- outerGlow.addColorStop(1, 'rgba(100, 150, 255, 0)');
- const length = p.size * 6.65;
- const arrowHeadLength = p.size * 3;
- const arrowHeadWidth = p.size * 2;
- ctx.beginPath();
- ctx.moveTo(length / 3 - arrowHeadLength, -arrowHeadWidth / 2);
- ctx.lineTo(length / 2, 0);
- ctx.lineTo(length / 3 - arrowHeadLength, arrowHeadWidth / 2);
- ctx.closePath();
- ctx.fillStyle = p.color;
- ctx.fill();
- },
- trail: (ctx, p, i) => {
- const alpha = i / p.totalLength;
- const trailSize = p.size * (1 + Math.sin(Date.now() * 0.01) * 0.2);
- // Create glowing trail gradient
- const trailGlow = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, trailSize * 2);
- trailGlow.addColorStop(0, changeColorAlpha(p.color, alpha));
- trailGlow.addColorStop(1, `rgba(150, 200, 255, 0)`);
- ctx.beginPath();
- ctx.arc(p.x, p.y, trailSize, 0, Math.PI * 2);
- ctx.fillStyle = trailGlow;
- ctx.fill();
- }
- },
- 'poisonDust': {
- speedFactor: 1,
- trailLength: 35,
- shake: true,
- onHit: {
- "poison": size => Math.min(Math.ceil(size * 8), 12)
- },
- draw: (ctx, p) => {
- // Draw main projectile
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
- ctx.fillStyle = p.color;
- ctx.fill();
- // Create inner glow gradient
- const innerGlow = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.size * 1.5);
- innerGlow.addColorStop(0, 'rgba(255, 255, 255, 0.8)');
- innerGlow.addColorStop(0.5, changeColorAlpha(p.color, 0.5));
- innerGlow.addColorStop(1, 'rgba(50, 200, 50, 0)');
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size * 1.5, 0, Math.PI * 2);
- ctx.fillStyle = innerGlow;
- ctx.fill();
- },
- glow: (ctx, p) => {
- // Create outer glow gradient
- const outerGlow = ctx.createRadialGradient(p.x, p.y, p.size * 1.5, p.x, p.y, p.size * 4);
- outerGlow.addColorStop(0, changeColorAlpha(p.color, 0.5));
- // outerGlow.addColorStop(0.5, 'rgba(50, 200, 50, 0.2)');
- outerGlow.addColorStop(1, 'rgba(0, 150, 0, 0)');
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size * 4, 0, Math.PI * 2);
- ctx.fillStyle = outerGlow;
- ctx.fill();
- },
- trail: (ctx, p, i) => {
- const alpha = i / p.totalLength;
- p.x = p.x + (Math.random() - 0.5) * 5;
- p.y = p.y - (Math.random() - 0.5) * 1 + 0.02;
- ctx.beginPath();
- const lineWidth = p.size * Math.sqrt(alpha);
- ctx.strokeStyle = `${changeColorAlpha(p.color, alpha)}`;
- ctx.lineWidth = lineWidth;
- ctx.moveTo(p.x, p.y);
- ctx.lineTo(p.x + (Math.random() - 0.5) * 20, p.y + (Math.random() - 0.5) * 20);
- ctx.stroke();
- ctx.fill();
- }
- },
- 'thrust': {
- speedFactor: 3,
- gravity: -0.001,
- trailLength: 0,
- shake: true,
- onHit: {
- "smallParticle": size => Math.min(Math.ceil(size * 4), 10),
- "pierce": size => Math.min(Math.ceil(size * 4), 6),
- "shockwave": size => Math.min(Math.ceil(size * 2), 6)
- },
- draw: (ctx, p) => {
- const shaftLength = p.size * 12; // Longer shaft
- const shaftWidth = p.size * 0.8; // Thicker shaft
- const tipLength = p.size * 3; // Length of the pointed tip
- const tipWidth = p.size * 1.2; // Width at the base of the tip
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.rotate(Math.atan2(p.velocity.y, p.velocity.x));
- // Draw shaft
- ctx.beginPath();
- ctx.moveTo(-shaftLength / 2, -shaftWidth / 2);
- ctx.lineTo(shaftLength / 2 - tipLength, -shaftWidth / 2);
- ctx.lineTo(shaftLength / 2 - tipLength, shaftWidth / 2);
- ctx.lineTo(-shaftLength / 2, shaftWidth / 2);
- ctx.closePath();
- ctx.fillStyle = p.color;
- ctx.fill();
- // Draw tip
- ctx.beginPath();
- ctx.moveTo(shaftLength / 2 - tipLength, -tipWidth / 2);
- ctx.lineTo(shaftLength / 2, 0);
- ctx.lineTo(shaftLength / 2 - tipLength, tipWidth / 2);
- ctx.closePath();
- ctx.fillStyle = p.color;
- ctx.fill();
- // Add highlight to shaft
- ctx.beginPath();
- ctx.moveTo(-shaftLength / 2, -shaftWidth / 2);
- ctx.lineTo(shaftLength / 2 - tipLength, -shaftWidth / 2);
- ctx.lineTo(shaftLength / 2 - tipLength, 0);
- ctx.lineTo(-shaftLength / 2, 0);
- ctx.closePath();
- ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
- ctx.fill();
- // Add highlight to tip
- ctx.beginPath();
- ctx.moveTo(shaftLength / 2 - tipLength, -tipWidth / 2);
- ctx.lineTo(shaftLength / 2, 0);
- ctx.lineTo(shaftLength / 2 - tipLength, 0);
- ctx.closePath();
- ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
- ctx.fill();
- ctx.restore();
- }
- },
- 'fireTornado': {
- speedFactor: 2,
- trailLength: 3,
- shake: true,
- onHit: {
- "tornado": size => Math.min(Math.ceil(size * 5), 8)
- },
- draw: (ctx, p) => {
- ctx.beginPath();
- ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
- ctx.fillStyle = p.color;
- ctx.fill();
- }
- }
- };
- const abilityEffectsMap = {
- 'autoAttack': 'slash',
- 'default': 'fireball',
- 'heal': 'heal',
- '/abilities/fireball': "fireball",
- '/abilities/firestorm': "fireTornado",
- '/abilities/flame_blast': "lavaPlume",
- '/abilities/smoke_burst': "fireball",
- '/abilities/aqua_arrow': "water",
- '/abilities/frost_surge': "iceBlast",
- '/abilities/ice_spear': "iceBlast",
- '/abilities/mana_spring': "water",
- '/abilities/water_strike': "water",
- '/abilities/entangle': "nature",
- '/abilities/natures_veil': "nature",
- '/abilities/toxic_pollen': "poisonDust",
- '/abilities/penetrating_shot': "range",
- '/abilities/pestilent_shot': "range",
- '/abilities/steady_shot': "range",
- '/abilities/quick_shot': "range",
- '/abilities/rain_of_arrows': "range",
- '/abilities/silencing_shot': "range",
- '/abilities/crippling_slash': "slash",
- '/abilities/penetrating_strike': "slash",
- '/abilities/impale': "thrust",
- '/abilities/maim': "slash",
- '/abilities/poke': "thrust",
- '/abilities/puncture': "thrust",
- '/abilities/scratch': "slash",
- '/abilities/smack': "slash",
- '/abilities/sweep': "slash",
- '/abilities/stunning_blow': "slash"
- };
- let activeEffects = [];
- function addEffect({
- effects,
- active = true,
- lifespan = 120,
- color = "rgba(255, 255, 255, 0.8)",
- otherInfo = {},
- isFpsOptimization = false
- }) {
- activeEffects.push({
- effects,
- active,
- life: 0,
- lifespan,
- color,
- otherInfo,
- isFpsOptimization
- });
- }
- function clearEffects() {
- activeEffects.splice(0, activeEffects.length);
- }
- function applyShakeEffect(element, intensity = 1, duration = 500) {
- if (!element) return;
- // Store the element's original position/transform
- const originalTransform = element.style.transform || '';
- const originalTransition = element.style.transition || '';
- intensity *= settingsMap.shakeEffectScale.value || 1;
- // Scale intensity based on size/damage
- const scaledIntensity = Math.min(10, intensity);
- // Apply CSS animation
- element.style.transition = 'transform 50ms ease-in-out';
- let shakeCount = 0;
- const maxShakes = Math.ceil(intensity);
- const shakeInterval = 50;
- const interval = setInterval(() => {
- if (shakeCount >= maxShakes) {
- // Ensure element returns to original position
- clearInterval(interval);
- element.style.transform = originalTransform;
- element.style.transition = originalTransition;
- return;
- }
- // Random offset for shaking effect
- const xOffset = (Math.random() - 0.5) * 2 * scaledIntensity;
- const yOffset = (Math.random() - 0.5) * 2 * scaledIntensity;
- element.style.transform = `${originalTransform} translate(${xOffset}px, ${yOffset}px)`;
- shakeCount++;
- }, shakeInterval);
- // Additional safeguard: ensure element returns to original position after max duration
- setTimeout(() => {
- clearInterval(interval);
- element.style.transform = 'translate(0, 0)';
- element.style.transition = originalTransition;
- }, shakeInterval * (maxShakes + 1)); // Slightly longer than maxShakes * interval time
- }
- function addDamageHPBar(element, damage) {
- const hpBarContainer = element.querySelector(".HitpointsBar_hitpointsBar__2vIqC");
- const hpBarFront = hpBarContainer.querySelector(".HitpointsBar_currentHp__5exLr");
- // hpBarFront.style.zIndex = "1";
- const hpBarValue = hpBarContainer.querySelector(".HitpointsBar_hpValue__xNp7m");
- // hpBarValue.style.zIndex = "2";
- const hpStat = hpBarValue.innerHTML.split("/");
- const currentHp = parseInt(hpStat[0]);
- const maxHp = parseInt(hpStat[1]);
- // Insert a HpBar behind and set the color to red
- const hpBarBack = document.createElement("div");
- hpBarBack.className = "HitpointsBar_currentHp__5exLr HitTracker_hpDrop";
- hpBarBack.style.background = "var(--color-warning)";
- hpBarBack.style.position = "absolute";
- hpBarBack.style.top = "0px";
- hpBarBack.style.left = "0px";
- // hpBarBack.style.zIndex = "1"; // Ensure the back bar is below the front bar
- hpBarBack.style.width = `${hpBarFront.offsetWidth}px`;
- hpBarBack.style.height = `${hpBarFront.offsetHeight}px`;
- hpBarBack.style.transformOrigin = "left center";
- hpBarBack.style.transform = `scaleX(${(currentHp + damage) / maxHp})`;
- // add animation to drop down
- hpBarBack.style.transition = "transform 0.5s ease-in-out";
- hpBarFront.parentNode.insertBefore(hpBarBack, hpBarFront); // Insert the back bar before the front bar
- const dropDelay = Math.ceil(settingsMap.damageHpBarDropDelay.value || 300);
- setTimeout(() => {
- hpBarBack.style.transform = `scaleX(0)`;
- }, dropDelay);
- setTimeout(() => {
- hpBarBack.remove();
- }, dropDelay + 500);
- }
- function resetAllMonsterSvg() {
- const monsterArea = document.querySelector(".BattlePanel_monstersArea__2dzrY");
- if (monsterArea) {
- const monsterSvgs = monsterArea.querySelectorAll(".Icon_icon__2LtL_");
- monsterSvgs.forEach(monsterSvg => {
- monsterSvg.style.transition = "none";
- monsterSvg.style.transform = "rotate(0deg)";
- monsterSvg.style.opacity = "1";
- });
- }
- }
- const deathEffect = {
- default: element => {
- const monsterSvg = element.querySelector(".Icon_icon__2LtL_");
- monsterSvg.style.transition = "transform 0.1s ease-in-out";
- monsterSvg.style.transformOrigin = "bottom center";
- monsterSvg.style.transform = "rotate(15deg)";
- setTimeout(() => {
- monsterSvg.style.transition = "transform 0.5s ease-in-out, opacity 0.5s ease-in-out";
- monsterSvg.style.transform = "rotate(-180deg)";
- monsterSvg.style.opacity = "0";
- }, 300);
- // fade out
- // setTimeout(() => {
- // monsterSvg.style.transition = "opacity 0.5s ease-in-out";
- // }, 800);
- },
- minecraftStyle: element => {
- const monsterSvg = element.querySelector(".Icon_icon__2LtL_");
- // First get dimensions and viewBox of original SVG
- const svgRect = monsterSvg.getBoundingClientRect();
- const viewBox = monsterSvg.getAttribute('viewBox') || '0 0 24 24'; // 默认值,以防未设置
- // Get SVG content before changing anything else
- const svgContent = monsterSvg.innerHTML;
- // Create container that will match exact position of original SVG
- const overlayContainer = document.createElement('div');
- overlayContainer.style.position = 'absolute';
- overlayContainer.style.top = '0';
- overlayContainer.style.left = '0';
- overlayContainer.style.width = '100%';
- overlayContainer.style.height = '100%';
- overlayContainer.style.pointerEvents = 'none';
- // overlayContainer.style.zIndex = '5';
- // Match the exact positioning and sizing of the original SVG
- const parentBounds = element.getBoundingClientRect();
- const relativeTop = (svgRect.top - parentBounds.top) / parentBounds.height * 100;
- const relativeLeft = (svgRect.left - parentBounds.left) / parentBounds.width * 100;
- const relativeWidth = svgRect.width / parentBounds.width * 100;
- const relativeHeight = svgRect.height / parentBounds.height * 100;
- // Create SVG overlay with the same dimensions and position
- const svgOverlay = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
- svgOverlay.setAttribute('width', '100%');
- svgOverlay.setAttribute('height', '100%');
- svgOverlay.setAttribute('viewBox', viewBox);
- svgOverlay.style.position = 'absolute';
- svgOverlay.style.top = `${relativeTop}%`;
- svgOverlay.style.left = `${relativeLeft}%`;
- svgOverlay.style.width = `${relativeWidth}%`;
- svgOverlay.style.height = `${relativeHeight}%`;
- setTimeout(() => {
- // Apply rotation to original SVG
- monsterSvg.style.transition = "transform 0.1s ease-in-out";
- monsterSvg.style.transformOrigin = "center left";
- monsterSvg.style.transform = "rotate(15deg)";
- // Apply same transform as original to maintain alignment
- svgOverlay.style.transition = "transform 0.1s ease-in-out";
- svgOverlay.style.transform = "rotate(15deg)";
- svgOverlay.style.transformOrigin = "center left";
- }, 300);
- // Create defs for the mask
- const defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
- const mask = document.createElementNS('http://www.w3.org/2000/svg', 'mask');
- mask.setAttribute('id', `monster-mask-${Date.now()}`); // Unique ID
- // Clone the original SVG content for the mask
- const maskContent = document.createElementNS('http://www.w3.org/2000/svg', 'g');
- maskContent.innerHTML = svgContent;
- // Set all elements in mask to white (opaque parts of mask)
- const maskElements = maskContent.querySelectorAll('*');
- maskElements.forEach(el => {
- if (el.tagName === 'path' || el.tagName === 'circle' || el.tagName === 'rect' || el.tagName === 'polygon' || el.tagName === 'polyline') {
- el.setAttribute('fill', 'white');
- el.setAttribute('stroke', 'white');
- }
- });
- mask.appendChild(maskContent);
- defs.appendChild(mask);
- svgOverlay.appendChild(defs);
- // Create the red overlay rectangle that will be masked
- const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
- rect.setAttribute('width', '100%');
- rect.setAttribute('height', '100%');
- rect.setAttribute('fill', 'rgba(255, 0, 0, 0.6)'); // slightly more opaque
- rect.setAttribute('mask', `url(#${mask.id})`);
- svgOverlay.appendChild(rect);
- overlayContainer.appendChild(svgOverlay);
- // Add to parent element (usually the monster container)
- element.style.position = 'relative'; // Ensure positioning context
- element.appendChild(overlayContainer);
- const svgCenter = getElementCenter(element);
- // Make overlay match any subsequent animations of the original SVG
- const observer = new MutationObserver(mutations => {
- mutations.forEach(mutation => {
- if (mutation.attributeName === 'style' || mutation.attributeName === 'transform') {
- // Copy transform properties to keep in sync
- svgOverlay.style.transform = monsterSvg.style.transform;
- svgOverlay.style.opacity = monsterSvg.style.opacity;
- svgOverlay.style.transition = monsterSvg.style.transition;
- }
- });
- });
- // Start observing the original SVG for changes
- observer.observe(monsterSvg, {
- attributes: true,
- attributeFilter: ['style', 'transform']
- });
- // Fade out after delay
- setTimeout(() => {
- // monsterSvg.style.transition = "opacity 0.5s ease-in-out";
- monsterSvg.style.opacity = "0";
- // Remove overlay and stop observer after animation
- observer.disconnect();
- overlayContainer.remove();
- let effects = [];
- const p = {
- x: svgCenter.x,
- y: svgCenter.y + 30,
- color: "rgba(0, 0, 0, 0.6)",
- size: 0.2
- };
- for (let i = 0; i < 25; i++) {
- p.life = onHitEffectsMap.pixelSmoke.life({
- size: 0.5
- });
- effects.push({
- x: onHitEffectsMap.pixelSmoke.x(p),
- y: onHitEffectsMap.pixelSmoke.y(p),
- angle: onHitEffectsMap.pixelSmoke.angle(p),
- color: onHitEffectsMap.pixelSmoke.color(p),
- size: onHitEffectsMap.pixelSmoke.size(p),
- speed: onHitEffectsMap.pixelSmoke.speed({
- size: 5
- }),
- gravity: onHitEffectsMap.pixelSmoke.gravity(p),
- life: p.life,
- maxLife: p.life,
- draw: onHitEffectsMap.pixelSmoke.draw
- });
- }
- // Add particle effect
- addEffect({
- effects: effects,
- active: true,
- lifespan: 500
- });
- }, 1000);
- }
- };
- const canvas = initTrackerCanvas();
- const ctx = canvas.getContext('2d');
- function initTrackerCanvas() {
- const gamePanel = document.querySelector("body");
- const canvas = document.createElement('canvas');
- canvas.id = 'hitTrackerCanvas';
- canvas.style.position = 'fixed';
- canvas.style.top = '0';
- canvas.style.left = '0';
- canvas.style.pointerEvents = 'none';
- canvas.style.zIndex = '200';
- canvas.style.width = '100%';
- canvas.style.height = '100%';
- canvas.width = window.innerWidth;
- canvas.height = window.innerHeight;
- canvas.pointerEvents = 'none';
- gamePanel.appendChild(canvas);
- window.addEventListener('resize', () => {
- canvas.width = window.innerWidth;
- canvas.height = window.innerHeight;
- });
- return canvas;
- }
- // Update shake animation effect to ensure element returns to original position
- let fpsStatTime = new Date().getTime();
- let fpsQueue = [];
- let fps = 60;
- // 动画循环
- function animate() {
- // 计算FPS
- const now = Date.now();
- const frameTime = now - fpsStatTime;
- fpsStatTime = now;
- const fpsNow = Math.round(1000 / frameTime);
- fpsQueue.push(fpsNow);
- if (fpsQueue.length > 120) {
- fpsQueue.shift();
- }
- fps = Math.round(fpsQueue.reduce((a, b) => a + b) / fpsQueue.length);
- fps = Math.min(Math.max(fps, 10), 300);
- if (settingsMap.showFps.value) {
- const fpsElement = document.querySelector('#hitTracker_fpsCounter');
- if (fpsElement) {
- fpsElement.innerText = `FPS: ${fps}`;
- } else {
- const parenetElement = document.querySelector(".BattlePanel_battleArea__U9hij");
- if (parenetElement) {
- const newFpsElement = document.createElement('div');
- const center = getElementCenter(parenetElement);
- newFpsElement.id = 'hitTracker_fpsCounter';
- newFpsElement.style.position = 'fixed';
- newFpsElement.style.top = `${center.x - parenetElement.innerWidth}px`;
- newFpsElement.style.left = `${center.y - parenetElement.innerHeight}px`;
- newFpsElement.style.color = 'rgba(200, 200, 200, 0.8)';
- newFpsElement.style.zIndex = '9999';
- newFpsElement.innerText = `FPS: ${fps}`;
- parenetElement.appendChild(newFpsElement);
- }
- }
- }
- // 完全清空画布
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- // 更新并绘制所有弹丸
- for (let i = projectiles.length - 1; i >= 0; i--) {
- const proj = projectiles[i];
- proj.update();
- proj.draw(ctx);
- if (proj.isArrived()) {
- createOnHitEffect(proj); // 将弹丸大小传递给爆炸效果
- projectiles.splice(i, 1);
- } else if (proj.isOutOfBounds()) {
- // 超出边界则移除弹丸,不产生爆炸效果
- projectiles.splice(i, 1);
- }
- }
- // 更新和渲染所有爆炸效果
- updateOnHits();
- }
- function startAnimation() {
- const fpsLimit = settingsMap.renderFpsLimit.value || 60;
- const fpsInterval = 1000 / fpsLimit;
- setInterval(() => {
- animate();
- }, fpsInterval);
- }
- function getFpsFactor() {
- return Math.min(Math.max(160 / fps, 0.125), 8);
- }
- class Projectile {
- constructor(startX, startY, endX, endY, color, initialSpeed = 1, size = 10, otherInfo = {}) {
- // 基础属性
- this.x = startX;
- this.y = startY;
- this.start = {
- x: startX,
- y: startY
- };
- this.target = {
- x: endX,
- y: endY
- };
- this.otherInfo = otherInfo;
- this.shakeApplied = false;
- this.life = 0;
- this.type = otherInfo.type || 'default';
- this.effect = projectileEffectsMap[this.type] || projectileEffectsMap['fireball'];
- this.doShake = this.effect.shake;
- // 运动参数 - 向斜上方抛物线轨迹
- this.gravity = this.effect.gravity || 0.2; // 重力加速度
- this.gravity *= settingsMap.projectileHeightScale.value || 1; // 高度缩放因子
- this.initialSpeed = initialSpeed * (this.effect.speedFactor || 1); // 初始速度参数
- this.initialSpeed *= settingsMap.projectileSpeedScale.value || 1; // 速度缩放因子
- // 计算水平距离和高度差
- const dx = endX - startX;
- const dy = endY - startY;
- // 重新设计飞行时间计算,确保合理
- // const timeInAir = distance / this.initialSpeed / 10;
- this.timeInAir = 80 / this.initialSpeed;
- // FPS因子,确保在不同FPS下效果一致
- const fpsFactor = getFpsFactor();
- this.gravity *= Math.pow(fpsFactor, 2);
- this.timeInAir /= fpsFactor;
- // 计算初始速度,修正公式确保能够到达目标
- this.velocity = {
- x: dx / this.timeInAir,
- y: dy / this.timeInAir - this.gravity * this.timeInAir / 2
- };
- this.initialVelocity = {
- ...this.velocity
- };
- this.trajectory = time => {
- return {
- x: startX + this.initialVelocity.x * time,
- y: startY + this.initialVelocity.y * time + this.gravity * time * time / 2
- };
- };
- // 大小参数 (范围1-100)
- const projectileScale = settingsMap.projectileScale.value || 1;
- this.sizeScale = Math.max(1, Math.min(100, size)) / 10 * projectileScale; // 转换为比例因子
- // 外观属性
- this.size = 10 * this.sizeScale;
- this.color = this.effect.color || color;
- // 拖尾效果
- this.trail = [];
- this.independentTrail = this.effect.independentTrail || false; // 是否独立拖尾
- this.maxTrailLength = Math.floor((this.effect.trailLength || 35) * Math.sqrt(this.sizeScale)); // 拖尾长度随大小增加
- this.maxTrailLength *= settingsMap.projectileTrailLength.value || 1; // 拖尾缩放因子
- this.trailGap = (settingsMap.projectileTrailGap.value || 1) / fpsFactor;
- }
- update() {
- this.life += 1;
- const pos = this.trajectory(this.life);
- this.velocity.y += this.gravity;
- this.x = pos.x;
- this.y = pos.y;
- // 更新拖尾
- if (this.independentTrail) {
- if (this.effect.trailLength > 0) {
- this.trail.push({
- x: this.x,
- y: this.y,
- vX: this.velocity.x,
- vY: this.velocity.y,
- color: this.color,
- size: this.size,
- totalLength: Math.max(this.trail.length, 1)
- });
- }
- if (this.trail.length > this.maxTrailLength) {
- this.trail.shift();
- }
- } else {
- this.trail = [];
- for (let i = 0; i < this.maxTrailLength; i++) {
- const trailTime = this.life - (this.maxTrailLength - i - 1) * this.trailGap;
- if (trailTime <= 0) break;
- const trailPos = this.trajectory(trailTime);
- this.trail.push({
- x: trailPos.x,
- y: trailPos.y,
- vX: this.velocity.x,
- vY: this.velocity.y,
- color: this.color,
- size: this.size,
- totalLength: this.maxTrailLength
- });
- }
- }
- }
- draw(canvas) {
- // 绘制拖尾
- this.trail.forEach((pos, index) => {
- if (this.effect.trail) {
- this.effect.trail(canvas, pos, index);
- } else {
- projectileEffectsMap['fireball'].trail(canvas, pos, index);
- }
- });
- // 绘制主体
- if (this.effect.draw) {
- this.effect.draw(canvas, this);
- } else {
- projectileEffectsMap['fireball'].draw(canvas, this);
- }
- // 添加光晕效果
- if (this.effect.glow) {
- this.effect.glow(canvas, this);
- }
- }
- isArrived() {
- if (this.life >= this.timeInAir) {
- this.x = this.target.x;
- this.y = this.target.y;
- return true;
- }
- // 判断是否到达目标点 (调整判定距离)
- const arrivalDistance = 20;
- const hasArrived = Math.hypot(this.x - this.target.x, this.y - this.target.y) < arrivalDistance;
- if (hasArrived && this.doShake && !this.shakeApplied && this.otherInfo.endElement) {
- const shakeIntensity = Math.min(this.sizeScale * 5, 10);
- applyShakeEffect(this.otherInfo.endElement, shakeIntensity);
- this.shakeApplied = true;
- }
- return hasArrived;
- }
- isOutOfBounds() {
- return this.x < 0 || this.x > canvas.width || this.y < 0 || this.y > canvas.height;
- }
- }
- // Projectiles管理
- let projectiles = [];
- function clearProjectiles() {
- projectiles.splice(0, projectiles.length);
- }
- // 爆炸效果函数
- function createOnHitEffect(projectile) {
- const x = projectile.x;
- const y = projectile.y;
- const color = projectile.color;
- const otherInfo = projectile.otherInfo;
- const projectileScale = settingsMap.projectileScale.value || 1;
- // Resize for onHit effect
- projectile.size = Math.max(1, Math.min(100, projectile.size)) / 20 / projectileScale;
- const sizeFactor = settingsMap.onHitScale.value || 1;
- const particleFactor = settingsMap.particleEffectRatio.value || 1;
- const particleSpeedFactor = settingsMap.particleSpeedRatio.value || 1;
- const particleLifespanFactor = settingsMap.particleLifespanRatio.value || 1;
- const fpsFactor = getFpsFactor();
- const effects = [];
- let onHitEffect = projectile.effect.onHit;
- if (projectile.otherInfo.isCrit) {
- const onCrit = projectile.effect.onCrit || projectileEffectsMap.fireball.onCrit;
- onHitEffect = {
- ...onHitEffect,
- ...onCrit
- };
- }
- for (const effectName in onHitEffect) {
- const effect = onHitEffectsMap[effectName];
- if (!effect) continue;
- const effectCount = Math.ceil(onHitEffect[effectName](projectile.size) * particleFactor);
- for (let i = 0; i < effectCount; i++) {
- const effectSize = (effect.size ? effect.size(projectile) : Math.random() * 10 + 5) * sizeFactor;
- const effectLife = Math.ceil((effect.life ? effect.life(projectile) : 1000) * particleLifespanFactor / Math.pow(fpsFactor, 0.33));
- const effectSpeed = Math.ceil((effect.speed ? effect.speed(projectile) : Math.random() * 5 + 2) / Math.pow(fpsFactor, 0.33) * particleSpeedFactor);
- effects.push({
- x: effect.x ? effect.x(projectile) : x,
- y: effect.y ? effect.y(projectile) : y,
- angle: effect.angle ? effect.angle(projectile) : Math.random() * Math.PI * 2,
- alpha: effect.alpha ? effect.alpha(projectile) : 0.8,
- size: effectSize,
- speed: effectSpeed,
- gravity: effect.gravity ? effect.gravity(projectile) : 0,
- life: effectLife,
- maxLife: effectLife,
- color: effect.color ? effect.color(projectile) : projectile.color,
- draw: effect.draw ? effect.draw : (ctx, p) => {}
- });
- }
- }
- // 存储命中动画的活跃状态,用于跟踪
- const damageTextLifespan = settingsMap.damageTextLifespan.value || 120;
- const lifeSpan = Math.ceil(damageTextLifespan / Math.pow(fpsFactor, 0.33));
- const onHitEffectData = {
- effects: [...effects],
- active: true,
- lifespan: lifeSpan,
- color: color,
- otherInfo: otherInfo,
- isFpsOptimized: true
- };
- addEffect(onHitEffectData);
- }
- // 更新和渲染所有命中效果
- function updateOnHits() {
- // 遍历所有活跃的命中
- for (let i = activeEffects.length - 1; i >= 0; i--) {
- const effect = activeEffects[i];
- effect.life++;
- if (effect.life >= effect.lifespan) {
- activeEffects.splice(i, 1);
- continue;
- }
- if (!effect.isFpsOptimized) {
- const fpsFactor = getFpsFactor();
- for (const e of effect.effects) {
- e.speed *= fpsFactor;
- e.life /= fpsFactor;
- }
- effect.lifespan /= fpsFactor;
- effect.isFpsOptimized = true;
- }
- ctx.save();
- // 更新各自效果
- effect.effects.forEach((e, index) => {
- e.draw(ctx, e);
- });
- // 伤害文本
- if (effect.otherInfo && effect.otherInfo.damage) {
- const fontSizeScale = settingsMap.damageTextScale.value || 1;
- const fontSizeLimit = settingsMap.damageTextSizeLimit.value || 70;
- const fontAlpha = settingsMap.damageTextAlpha.value || 0.8;
- const fontSize = Math.min(Math.max(14, Math.pow(effect.otherInfo.damage, 0.65) / 2 * fontSizeScale), fontSizeLimit);
- const damageText = `${effect.otherInfo.damage}`;
- ctx.font = `${fontSize}px Arial`;
- ctx.textAlign = 'center';
- ctx.textBaseline = 'middle';
- const textSize = ctx.measureText(damageText);
- const textPosition = {
- x: effect.otherInfo.end.x - textSize.width / 2 + 5,
- y: effect.otherInfo.end.y - 20
- };
- // border
- ctx.strokeStyle = effect.color.replace(/rgba\(([^,]+),([^,]+),([^,]+),[^)]+\)/, `rgba($1,$2,$3,${fontAlpha})`);
- ctx.lineWidth = 6;
- ctx.strokeText(damageText, textPosition.x, textPosition.y);
- // main
- const fillColor = effect.otherInfo.isCrit ? 'rgba(255, 213, 89, 1)' : 'white';
- ctx.fillStyle = fillColor;
- ctx.fillText(damageText, textPosition.x, textPosition.y);
- }
- ctx.restore();
- }
- }
- function createProjectile(startElement, endElement, color, initialSpeed = 1, damage = 200, projectileType = 'default', isCrit = false, isKill = false) {
- if (!startElement || !endElement) {
- return;
- }
- const combatUnitContainer = endElement.querySelector(".CombatUnit_splatsContainer__2xcc0");
- if (!settingsMap.originalDamageDisplay.value) {
- combatUnitContainer.style.visibility = "hidden";
- }
- const padding = 30;
- const randomRange = {
- x: Math.floor((Math.random() - 0.5) * (combatUnitContainer.offsetWidth - 2 * padding)),
- y: Math.floor((Math.random() - 0.1) * (combatUnitContainer.offsetHeight - padding))
- };
- const projectileLimit = settingsMap.projectileLimit.value || 30;
- const start = getElementCenter(startElement);
- const end = getElementCenter(endElement);
- end.x = Math.floor(end.x + randomRange.x);
- end.y = Math.floor(end.y + randomRange.y);
- const size = Math.min(Math.max(Math.pow(damage + 200, 0.7) / 20, 4), 16);
- projectileType = abilityEffectsMap[projectileType] || projectileType;
- const otherInfo = {
- type: projectileType,
- start: start,
- end: end,
- damage: damage,
- color: color,
- isCrit: isCrit,
- isKill: isKill,
- startElement: startElement,
- endElement: endElement
- };
- if (projectiles.length <= projectileLimit) {
- if (damage > 0) {
- addDamageHPBar(endElement, damage);
- }
- if (otherInfo.isKill && settingsMap.monsterDeadAnimation.value) {
- deathEffect[settingsMap.monsterDeadAnimationStyle.value](otherInfo.endElement);
- }
- const projectile = new Projectile(start.x, start.y, end.x, end.y, color, initialSpeed, size, otherInfo);
- projectiles.push(projectile);
- } else {
- projectiles.shift();
- }
- }
- // #region Setting
- waitForSettings({
- monsterDeadAnimationStyle: Object.keys(deathEffect),
- allProjectiles: Object.keys(projectileEffectsMap)
- });
- hookWS();
- let isPageHidden = false;
- // 监听页面可见性变化
- document.addEventListener('visibilitychange', function () {
- isPageHidden = document.hidden;
- if (isPageHidden) {
- clearProjectiles();
- clearEffects();
- }
- });
- // #region Hook WS
- function hookWS() {
- const dataProperty = Object.getOwnPropertyDescriptor(MessageEvent.prototype, "data");
- const oriGet = dataProperty.get;
- dataProperty.get = hookedGet;
- Object.defineProperty(MessageEvent.prototype, "data", dataProperty);
- function hookedGet() {
- const socket = this.currentTarget;
- if (!(socket instanceof WebSocket)) {
- return oriGet.call(this);
- }
- if (socket.url.indexOf("api.milkywayidle.com/ws") <= -1 && socket.url.indexOf("api-test.milkywayidle.com/ws") <= -1) {
- return oriGet.call(this);
- }
- const message = oriGet.call(this);
- Object.defineProperty(this, "data", {
- value: message
- }); // Anti-loop
- try {
- return handleMessage(message);
- } catch (error) {
- console.log("Error in hit-tracker handleMessage:", error);
- return message;
- }
- }
- }
- let monstersHP = [];
- let monstersMP = [];
- let playersHP = [];
- let playersMP = [];
- let playersAbility = [];
- function handleMessage(message) {
- let obj = JSON.parse(message);
- if (obj && obj.type === "new_battle") {
- monstersHP = obj.monsters.map(monster => monster.currentHitpoints);
- monstersMP = obj.monsters.map(monster => monster.currentManapoints);
- playersHP = obj.players.map(player => player.currentHitpoints);
- playersMP = obj.players.map(player => player.currentManapoints);
- resetAllMonsterSvg();
- } else if (obj && obj.type === "battle_updated" && monstersHP.length) {
- const mMap = obj.mMap;
- const pMap = obj.pMap;
- const monsterIndices = Object.keys(obj.mMap);
- const playerIndices = Object.keys(obj.pMap);
- let castMonster = -1;
- monsterIndices.forEach(monsterIndex => {
- if (mMap[monsterIndex].cMP < monstersMP[monsterIndex]) {
- castMonster = monsterIndex;
- }
- monstersMP[monsterIndex] = mMap[monsterIndex].cMP;
- });
- let castPlayer = -1;
- playerIndices.forEach(userIndex => {
- if (pMap[userIndex].cMP < playersMP[userIndex]) {
- castPlayer = userIndex;
- }
- if (pMap[userIndex].cMP > playersMP[userIndex]) {
- registProjectile({
- from: userIndex,
- to: userIndex,
- hpDiff: pMap[userIndex].cMP - playersMP[userIndex],
- reversed: false,
- abilityHrid: 'selfManaRegen',
- toPlayer: true
- });
- }
- playersMP[userIndex] = pMap[userIndex].cMP;
- if (pMap[userIndex].abilityHrid) {
- playersAbility[userIndex] = pMap[userIndex].abilityHrid;
- }
- });
- monstersHP.forEach((mHP, mIndex) => {
- const monster = mMap[mIndex];
- if (monster) {
- const hpDiff = mHP - monster.cHP;
- monstersHP[mIndex] = monster.cHP;
- if (hpDiff > 0 && playerIndices.length > 0) {
- const isCrit = monster.dmgCounter == monster.critCounter;
- const isKill = monster.cHP <= 0;
- if (playerIndices.length > 1) {
- playerIndices.forEach(userIndex => {
- if (userIndex === castPlayer) {
- registProjectile({
- from: userIndex,
- to: mIndex,
- hpDiff: hpDiff,
- reversed: false,
- abilityHrid: playersAbility[userIndex],
- toPlayer: false,
- isCrit: isCrit,
- isKill: isKill
- });
- }
- });
- } else {
- registProjectile({
- from: playerIndices[0],
- to: mIndex,
- hpDiff: hpDiff,
- reversed: false,
- abilityHrid: playersAbility[playerIndices[0]],
- toPlayer: false,
- isCrit: isCrit,
- isKill: isKill
- });
- }
- }
- }
- });
- playersHP.forEach((pHP, pIndex) => {
- const player = pMap[pIndex];
- if (player) {
- const hpDiff = pHP - player.cHP;
- playersHP[pIndex] = player.cHP;
- if (hpDiff > 0 && monsterIndices.length > 0) {
- const isCrit = player.dmgCounter == player.critCounter;
- if (monsterIndices.length > 1) {
- monsterIndices.forEach(monsterIndex => {
- if (monsterIndex === castMonster) {
- registProjectile({
- from: pIndex,
- to: monsterIndex,
- hpDiff: hpDiff,
- reversed: true,
- abilityHrid: 'autoAttack',
- toPlayer: false,
- isCrit: isCrit
- });
- }
- });
- } else {
- registProjectile({
- from: pIndex,
- to: monsterIndices[0],
- hpDiff: hpDiff,
- reversed: true,
- abilityHrid: 'autoAttack',
- toPlayer: false,
- isCrit: isCrit
- });
- }
- } else if (hpDiff < 0) {
- if (castPlayer > -1) {
- registProjectile({
- from: castPlayer,
- to: pIndex,
- hpDiff: -hpDiff,
- reversed: false,
- abilityHrid: 'heal',
- toPlayer: true
- });
- } else {
- registProjectile({
- from: pIndex,
- to: pIndex,
- hpDiff: -hpDiff,
- reversed: false,
- abilityHrid: 'selfHeal',
- toPlayer: true
- });
- }
- }
- }
- });
- } else if (obj && obj.type === "battle_updated") {
- const pMap = obj.pMap;
- const playerIndices = Object.keys(obj.pMap);
- playerIndices.forEach(userIndex => {
- if (pMap[userIndex].abilityHrid) {
- playersAbility[userIndex] = pMap[userIndex].abilityHrid;
- }
- });
- playersHP.forEach((pHP, pIndex) => {
- const player = pMap[pIndex];
- if (player) {
- const hpDiff = pHP - player.cHP;
- playersHP[pIndex] = player.cHP;
- if (hpDiff < 0) {
- registProjectile({
- from: pIndex,
- to: pIndex,
- hpDiff: -hpDiff,
- reversed: false,
- abilityHrid: 'selfHeal',
- toPlayer: true
- });
- }
- }
- });
- playersMP.forEach((pMP, pIndex) => {
- const player = pMap[pIndex];
- if (player) {
- const mpDiff = pMP - player.pMP;
- playersMP[pIndex] = player.pMP;
- if (mpDiff < 0) {
- registProjectile({
- from: pIndex,
- to: pIndex,
- hpDiff: -mpDiff,
- reversed: false,
- abilityHrid: 'selfManaRegen',
- toPlayer: true
- });
- }
- }
- });
- }
- return message;
- }
- // #region Main Logic
- // 动画效果
- function registProjectile({
- from,
- to,
- hpDiff,
- reversed = false,
- abilityHrid = "default",
- toPlayer = true,
- isCrit = false,
- isKill = false
- }) {
- if (isPageHidden) {
- return;
- }
- if (reversed) {
- if (settingsMap.tracker6 && !settingsMap.tracker6.isTrue) {
- return null;
- }
- } else {
- if (settingsMap["tracker" + from] && !settingsMap["tracker" + from].isTrue) {
- return null;
- }
- }
- if (["selfHeal", "selfManaRegen"].indexOf(abilityHrid) > -1 && !settingsMap.showSelfRegen.value) {
- return null;
- }
- const container = document.querySelector(".BattlePanel_playersArea__vvwlB");
- if (container && container.children.length > 0) {
- const playersContainer = container.children[0];
- const effectFrom = playersContainer.children[from];
- const monsterContainer = document.querySelector(".BattlePanel_monstersArea__2dzrY").children[0];
- const effectTo = toPlayer ? playersContainer.children[to] : monsterContainer.children[to];
- const trackerSetting = reversed ? settingsMap[`tracker6`] : settingsMap["tracker" + from];
- let lineColor = "rgba(" + trackerSetting.r + ", " + trackerSetting.g + ", " + trackerSetting.b + ", 1)";
- if (["selfHeal", "selfManaRegen", "heal"].indexOf(abilityHrid) <= -1) {
- if (trackerSetting.trackStyle === "null") {
- return null;
- } else if (trackerSetting.trackStyle != "auto") {
- abilityHrid = trackerSetting.trackStyle;
- }
- }
- if (!reversed) {
- createProjectile(effectFrom, effectTo, lineColor, 1, hpDiff, abilityHrid, isCrit, isKill);
- } else {
- createProjectile(effectTo, effectFrom, lineColor, 1, hpDiff, abilityHrid, isCrit, isKill);
- }
- }
- }
- // 启动动画
- startAnimation();
- exports.registProjectile = registProjectile;
- Object.defineProperty(exports, '__esModule', { value: true });
- return exports;
- })({});