Question

const skcnv = {
rld: 0,
pen: 1,
str: 2,
dam: 3,
spd: 4,
shi: 5,
atk: 6,
hlt: 7,
rgn: 8,
mob: 9,
};

let curvePoints = [];
for (let i = 0; i < 256; i++) {
curvePoints.push(Math.log(4 * (i / Config.MAX_SKILL) + 1) / 1.6);
}
let curve = x => curvePoints[x * Config.MAX_SKILL];
function apply(f, x) {
return x < 0 ? 1 / (1 - x * f) : f * x + 1;
}

class Skill {
constructor(inital = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) {
// Just skill stuff.
this.raw = inital;
this.caps = [];
this.setCaps([ Config.MAX_SKILL, Config.MAX_SKILL, Config.MAX_SKILL, Config.MAX_SKILL, Config.MAX_SKILL, Config.MAX_SKILL, Config.MAX_SKILL, Config.MAX_SKILL, Config.MAX_SKILL, Config.MAX_SKILL ]);
this.name = [
"Reload",
"Bullet Penetration",
"Bullet Health",
"Bullet Damage",
"Bullet Speed",
"Shield Capacity",
"Body Damage",
"Max Health",
"Shield Regeneration",
"Movement Speed",
];
this.atk = 0;
this.hlt = 0;
this.spd = 0;
this.str = 0;
this.pen = 0;
this.dam = 0;
this.rld = 0;
this.mob = 0;
this.rgn = 0;
this.shi = 0;
this.rst = 0;
this.brst = 0;
this.ghost = 0;
this.acl = 0;
this.reset();
}
reset(resetLSPF = true) {
this.points = 0;
this.score = 0;
this.deduction = 0;
this.level = 0;
this.levelUpScore = 1;
if (resetLSPF) this.LSPF = null;
this.set([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
this.maintain();
}
update() {
for (let i = 0; i < 10; i++) {
if (this.raw[i] > this.caps[i]) {
this.points += this.raw[i] - this.caps[i];
this.raw[i] = this.caps[i];
}
}
let attrib = [];
for (let i = 0; i < 10; i++) {
attrib[i] = curve(this.raw[i] / Config.MAX_SKILL);
}
this.rld = Math.pow(0.5, attrib[skcnv.rld]);
this.pen = apply(2.5, attrib[skcnv.pen]);
this.str = apply(2, attrib[skcnv.str]);
this.dam = apply(3, attrib[skcnv.dam]);
this.spd = 0.5 + apply(1.5, attrib[skcnv.spd]);
this.acl = apply(0.5, attrib[skcnv.rld]);
this.rst = 0.5 * attrib[skcnv.str] + 2.5 * attrib[skcnv.pen];
this.ghost = attrib[skcnv.pen];
this.shi = Config.GLASS_HEALTH_FACTOR * apply(3 / Config.GLASS_HEALTH_FACTOR - 1, attrib[skcnv.shi]);
this.atk = apply(0.021, attrib[skcnv.atk]);
this.hlt = Config.GLASS_HEALTH_FACTOR * apply(2 / Config.GLASS_HEALTH_FACTOR - 1, attrib[skcnv.hlt]);
this.mob = apply(0.8, attrib[skcnv.mob]);
this.rgn = apply(25, attrib[skcnv.rgn]);
this.brst = 0.3 * (0.5 * attrib[skcnv.atk] + 0.5 * attrib[skcnv.hlt] + attrib[skcnv.rgn]);
}
set(thing) {
this.raw[0] = thing[0];
this.raw[1] = thing[1];
this.raw[2] = thing[2];
this.raw[3] = thing[3];
this.raw[4] = thing[4];
this.raw[5] = thing[5];
this.raw[6] = thing[6];
this.raw[7] = thing[7];
this.raw[8] = thing[8];
this.raw[9] = thing[9];
this.update();
}
setCaps(thing) {
this.caps[0] = thing[0];
this.caps[1] = thing[1];
this.caps[2] = thing[2];
this.caps[3] = thing[3];
this.caps[4] = thing[4];
this.caps[5] = thing[5];
this.caps[6] = thing[6];
this.caps[7] = thing[7];
this.caps[8] = thing[8];
this.caps[9] = thing[9];
this.update();
}
maintain() {
if (this.score - this.deduction < this.levelScore) return false;

this.deduction = this.levelUpScore;
this.level += 1;
this.levelUpScore = this.scoreForLevel;
this.points += this.levelPoints;
this.update();
return true;
}
get scoreForLevel() {
return Math.ceil(Math.pow(this.level, 3) * 0.3083);
}
get levelScore() {
return this.levelUpScore - this.deduction;
}
get progress() {
return this.levelScore ? (this.score - this.deduction) / this.levelScore : 0;
}
get levelPoints() {
return this.LSPF ? this.LSPF(this.level) : Config.LEVEL_SKILL_POINT_FUNCTION(this.level);
}
cap(skill, real = false) {
return this.caps[skcnv[skill]];
}
upgrade(stat) {
if (this.points && this.amount(stat) < this.cap(stat)) {
this.change(stat, 1);
this.points -= 1;
return true;
}
return false;
}
title(stat) {
return this.name[skcnv[stat]];
}
amount(skill) {
return this.raw[skcnv[skill]];
}
change(skill, levels) {
this.raw[skcnv[skill]] += levels;
this.update();
}
}
class HealthType {
constructor(health, type, resist = 0) {
this.max = health;
this.amount = health;
this.type = type;
this.resist = resist;
this.regen = 0;
}
set(health, regen = 0) {
this.amount = this.max ? (this.amount / this.max) * health : health;
this.max = health;
this.regen = regen;
}
display() {
return this.amount / this.max;
}
getDamage(amount, capped = true) {
let damageToMax = this.amount - this.max;
switch (this.type) {
case "dynamic":
return Math.max(capped
? Math.min(amount * this.permeability, this.amount)
: amount * this.permeability,
damageToMax);
case "static":
return Math.max(
capped ? Math.min(amount, this.amount) : amount,
damageToMax);
}
}
regenerate(boost = false) {
boost /= 2;
let cons = 5;
switch (this.type) {
case "static":
if (this.amount >= this.max || !this.amount) break;
this.amount += cons * boost;
break;
case "dynamic":
let r = this.amount / this.max;
if (r <= 0) {
this.amount = 0.0001;
} else if (r >= 1) {
this.amount = this.max;
} else {
// this regen multiplier is this curve: https://www.desmos.com/calculator/ghjggwdp6h
let regenMultiplier = Math.exp(Math.pow(Math.sqrt(r / 2) - 0.4, 2) * -50);
this.amount += cons * (this.regen * regenMultiplier / 3 + (r * this.max) / 150 + boost);
}
break;
}
this.amount = util.clamp(this.amount, 0, this.max);
}
get permeability() {
switch (this.type) {
case "static":
return 1;
case "dynamic":
return this.max ? util.clamp(this.amount / this.max, 0, 1) : 0;
}
}
get ratio() {
return this.max ? util.clamp(1 - Math.pow(this.amount / this.max - 1, 4), 0, 1) : 0;
}
}

const dirtyCheck = function (p, r) {
for (let i = 0; i < entitiesToAvoid.length; i++) {
let e = entitiesToAvoid[i];
if (Math.abs(p.x - e.x) < r + e.size && Math.abs(p.y - e.y) < r + e.size) return true;
}
return false
};

const purgeEntities = () => entities = entities.filter(e => !e.isGhost);

let remapTarget = (i, ref, self) => {
if (i.target == null || !(i.main || i.alt)) return undefined;
return {
x: i.target.x + ref.x - self.x,
y: i.target.y + ref.y - self.y,
};
};

let lazyRealSizes = [1, 1, 1];
for (var i = 3; i < 17; i++) {
// We say that the real size of a 0-gon, 1-gon, 2-gon is one, then push the real sizes of triangles, squares, etc...
let circum = (2 * Math.PI) / i;
lazyRealSizes.push(Math.sqrt(circum * (1 / Math.sin(circum))));
}

lazyRealSizes = new Proxy(lazyRealSizes, {
get: function(arr, i) {
if (!(i in arr) && !isNaN(i)) {
let circum = (2 * Math.PI) / i;
arr[i] = Math.sqrt(circum * (1 / Math.sin(circum)));
}
return arr[i];
}
});

module.exports = { skcnv, Skill, HealthType, dirtyCheck, purgeEntities, remapTarget, lazyRealSizes };이것의 데미지 공식을 알아내시오

Thought

코드 파악 중

사용자가 제공한 JavaScript 코드를 살펴보면서, 여러 상수와 클래스, 함수들이 연관된 이름과 숫자, 포인트 배열, 설명서 등을 분석하고 있어. 각 요소가 어떻게 상호작용하는지 이해하려는 중이야.

피스 찾기

코드에서 피해 공식을 찾고 있어. Skill 클래스의 update 메서드에서 스킬 포인트를 바탕으로 다양한 특성을 계산 중이야. apply 함수는 특정 값을 받아 계산하는 역할을 해.

수식 활용하기

함수 apply는 x가 0 이상일 때는 f * x + 1, x가 0 미만일 때는 1 / (1 - x * f)로 적용되고 있어. 업데이트 메서드에서는 curve 함수를 활용하여 각 속성 값을 조정 중이야.

피크 곡선 계산 중

curve(x)는 4x + 1을 1.6로 나눈 값이니까, this.raw[dam]Config.MAX_SKILL로 나눈 값으로 나올 거야. 이 값이 attrib[dam]이 되는 거야.

측정하고 비교하기

별과 은하의 크기를 주기적으로 비교하고 있어. 이 변화가 평균 회전 속도와 관련이 있는지 살펴보자.

스킬 포인트 분석

주어진 스킬 포인트를 바탕으로 피해 배율을 계산하고 있어. 로그 수식을 이용해 최종 모델을 도출하는 중이야.

피해 공식 살펴보기 기본 피해량과 스킬 포인트의 비율에 따라 로그함수를 곱해 피해량을 계산하고 있어. 피해량은 여러 요인에 의해 증대될 수 있어.

Answer

The damage formula you're looking for is derived from the Skill class's update method in the code you provided. Here's how the damage multiplier is calculated:

  1. Calculate the Attribute Value (attrib[dam]):

    attrib[dam] = Math.log(4 * (skillPoints / maxSkillPoints) + 1) / 1.6;
    • skillPoints is the number of skill points allocated to bullet damage.
    • maxSkillPoints is the maximum number of skill points (e.g., 255).
    • The Math.log function computes the natural logarithm.
  2. Apply the Multiplier:

    DamageMultiplier = 3 * attrib[dam] + 1;

    Substituting the expression for attrib[dam], we get:

    DamageMultiplier = 3 * [Math.log(4 * (skillPoints / maxSkillPoints) + 1) / 1.6] + 1;
  3. Simplify the Formula:

    • Compute the constant 31.6=1.875\frac{3}{1.6} = 1.875.
    • The formula becomes:
      DamageMultiplier = 1.875 * Math.log(4 * (skillPoints / maxSkillPoints) + 1) + 1;

Final Damage Formula:

The damage dealt is proportional to the base damage multiplied by the damage multiplier:

Damage=BaseDamage×[1.875×ln(4×SkillPointsMaxSkillPoints+1)+1]\text{Damage} = \text{BaseDamage} \times \left[1.875 \times \ln\left(4 \times \frac{\text{SkillPoints}}{\text{MaxSkillPoints}} + 1\right) + 1\right]
  • ln\ln denotes the natural logarithm.
  • Ensure that SkillPoints and MaxSkillPoints are within the valid range (usually 0 to 255).

Example Calculation:

If a player allocates 200 out of 255 skill points to bullet damage:

  1. Calculate the ratio: x=2002550.7843x = \frac{200}{255} \approx 0.7843
  2. Compute the damage multiplier: DamageMultiplier=1.875×ln(4×0.7843+1)+11.875×ln(4.1372)+11.875×1.4202+13.164\text{DamageMultiplier} = 1.875 \times \ln(4 \times 0.7843 + 1) + 1 \approx 1.875 \times \ln(4.1372) + 1 \approx 1.875 \times 1.4202 + 1 \approx 3.164 \\
  3. The player's damage is: Damage=BaseDamage×3.164\text{Damage} = \text{BaseDamage} \times 3.164

Note: This formula calculates how the skill points affect the damage output in the game mechanics described by your code. The logarithmic function ensures diminishing returns on investment in damage skill points, balancing the gameplay.


Summary:

The damage multiplier as a function of skill points is:

DamageMultiplier=1.875×ln(4×SkillPointsMaxSkillPoints+1)+1\text{DamageMultiplier} = 1.875 \times \ln\left(4 \times \frac{\text{SkillPoints}}{\text{MaxSkillPoints}} + 1\right) + 1

This formula allows you to compute the damage output based on the allocated skill points.


If you need further clarification or assistance with other parts of the code, feel free to ask!

Question

아까의 공식을 참고해 이 코드를 같은 겅식을 사용하더럭 수정하세요 import {Entity} from './entity';

export enum SkillType {
MaxHealth = 'MaxHealth',
HealthRegen = 'HealthRegen',
MovementSpeed = 'MovementSpeed',
Damage = 'Damage',
Penetration = 'Penetration',
Range = 'Range',
Shield = 'Shield',
}

export interface Skill {
type: SkillType;
level: number;
maxLevel: number;
name: string;
description: string;
}

export class SkillManager {
private entity: Entity;
private skillPoints: number = 0;
private skills: Map<SkillType, Skill> = new Map();
private baseStats: {[key: string]: number} = {};

constructor(entity: Entity) {
this.entity = entity;
this.initializeSkills();
this.updateBaseStats();
}

private initializeSkills() {
this.skills.set(SkillType.MaxHealth, {
type: SkillType.MaxHealth,
level: 0,
maxLevel: 10,
name: 'Max Health',
description: 'Increases maximum health',
});

this.skills.set(SkillType.HealthRegen, {
type: SkillType.HealthRegen,
level: 0,
maxLevel: 10,
name: 'Health Regeneration',
description: 'Increases health regeneration rate',
});

this.skills.set(SkillType.MovementSpeed, {
type: SkillType.MovementSpeed,
level: 0,
maxLevel: 10,
name: 'Movement Speed',
description: 'Increases movement speed',
});

this.skills.set(SkillType.Damage, {
type: SkillType.Damage,
level: 0,
maxLevel: 10,
name: 'Bullet Damage',
description: 'Increases bullet damage',
});

this.skills.set(SkillType.Penetration, {
type: SkillType.Penetration,
level: 0,
maxLevel: 10,
name: 'Bullet Penetration',
description: 'Increases bullet penetration',
});

this.skills.set(SkillType.Range, {
type: SkillType.Range,
level: 0,
maxLevel: 15,
name: 'Bullet Range',
description: 'Increases bullet range',
});

this.skills.set(SkillType.Shield, {
type: SkillType.Shield,
level: 0,
maxLevel: 10,
name: 'Shield',
description: 'Increases shield capacity',
});
}

public updateBaseStats() {
this.baseStats = {
health: this.entity.setting.skill.health,
regen: this.entity.setting.skill.regen,
speed: this.entity.setting.skill.speed,
damage: this.entity.setting.skill.damage,
pen: this.entity.setting.skill.pen,
range: this.entity.setting.skill.range || 0,
shield: this.entity.setting.skill.shield || 0,
};
}

public addSkillPoints(points: number) {
this.skillPoints += points;
}

public getSkillPoints(): number {
return this.skillPoints;
}

public upgradeSkill(type: SkillType): boolean {
const skill = this.skills.get(type);
if (!skill) return false;

if (this.skillPoints <= 0 || skill.level >= skill.maxLevel) {
return false;
}

skill.level++;
this.skillPoints--;
this.applyAllSkillEffects();
return true;
}

public applyAllSkillEffects() {
this.entity.setting.skill.health = this.baseStats.health;
this.entity.setting.skill.regen = this.baseStats.regen;
this.entity.setting.skill.speed = this.baseStats.speed;
this.entity.setting.skill.damage = this.baseStats.damage;
this.entity.setting.skill.pen = this.baseStats.pen;
if (this.baseStats.range) {
this.entity.setting.skill.range = this.baseStats.range;
}
if (this.skills.get(SkillType.Shield).level > 0) {
const multiplier = 1 + this.skills.get(SkillType.Shield).level * 0.1;
this.entity.setting.skill.shield = this.baseStats.shield * multiplier;
}

for (const [type, skill] of this.skills) {
if (skill.level > 0) {
const multiplier = 1 + skill.level * 0.1;
switch (type) {
case SkillType.MaxHealth:
this.entity.setting.skill.health = this.baseStats.health * multiplier;
break;
case SkillType.HealthRegen:
this.entity.setting.skill.regen = this.baseStats.regen * multiplier;
break;
case SkillType.MovementSpeed:
this.entity.setting.skill.speed = this.baseStats.speed * multiplier;
break;
case SkillType.Damage:
this.entity.setting.skill.damage = this.baseStats.damage * multiplier;
break;
case SkillType.Penetration:
this.entity.setting.skill.pen = this.baseStats.pen * multiplier;
break;
case SkillType.Range:
if (this.baseStats.range) {
this.entity.setting.skill.range = this.baseStats.range * multiplier;
}
break;
}
}
}
}

public getSkill(type: SkillType): Skill | undefined {
return this.skills.get(type);
}

public getAllSkills(): Skill[] {
return Array.from(this.skills.values());
}
}. protected applyDamage(entity: Entity, other: Entity, damage: number): void {
const remainingDamage = Math.max(0, damage - entity.shield);
entity.shield = Math.max(0, entity.shield - damage);

if (remainingDamage > 0) {
entity.health -= remainingDamage;
}

entity.emit('damage', damage);
entity.lastTickAttacked = entity.tick;

if (entity.health <= 0) {
entity.emit('dead', other);
if (!entity.die) {
this.giveScore(other, entity);
}
this.remove(entity);
}
}

Thought

변경 작업 중

코드를 살펴보면서 이전 수식을 따라가고 있어. 코드를 업데이트하며 동일한 수식을 적용하고 있어.

장면 1. 다른 엔티티에게 피해를 주는 것을 구현하고 있어. 기존 수식을 이해하고 기존 코드를 수정해야 하는 상황이네.

통계 은제조정하기

피해 계산 공식을 변경하고, 선형 증가 대신 로짓 공식을 사용하고 있어. 힘스킬도 같은 방식으로 처리해야 해.

전략 변경 중

적용 중인 피해 배율을 새로운 공식을 사용하여 조정하고 있어요. max 레벨은 10으로 설정되며, 주어진 공식으로 피해 배율을 계산하고 기본 피해에 적용하고 있네요.

Answer

Certainly! Based on the previous damage formula:

DamageMultiplier=1.875×ln(4×SkillPointsMaxSkillPoints+1)+1\text{DamageMultiplier} = 1.875 \times \ln\left(4 \times \frac{\text{SkillPoints}}{\text{MaxSkillPoints}} + 1\right) + 1

We need to modify your SkillManager class to apply this formula when calculating the damage multiplier instead of the current linear formula.

Below is the modified code with explanations:

import { Entity } from './entity'; export enum SkillType { MaxHealth = 'MaxHealth', HealthRegen = 'HealthRegen', MovementSpeed = 'MovementSpeed', Damage = 'Damage', Penetration = 'Penetration', Range = 'Range', Shield = 'Shield', } export interface Skill { type: SkillType; level: number; maxLevel: number; name: string; description: string; } export class SkillManager { private entity: Entity; private skillPoints: number = 0; private skills: Map<SkillType, Skill> = new Map(); private baseStats: { [key: string]: number } = {}; constructor(entity: Entity) { this.entity = entity; this.initializeSkills(); this.updateBaseStats(); } private initializeSkills() { this.skills.set(SkillType.MaxHealth, { type: SkillType.MaxHealth, level: 0, maxLevel: 10, name: 'Max Health', description: 'Increases maximum health', }); this.skills.set(SkillType.HealthRegen, { type: SkillType.HealthRegen, level: 0, maxLevel: 10, name: 'Health Regeneration', description: 'Increases health regeneration rate', }); this.skills.set(SkillType.MovementSpeed, { type: SkillType.MovementSpeed, level: 0, maxLevel: 10, name: 'Movement Speed', description: 'Increases movement speed', }); this.skills.set(SkillType.Damage, { type: SkillType.Damage, level: 0, maxLevel: 10, name: 'Bullet Damage', description: 'Increases bullet damage', }); this.skills.set(SkillType.Penetration, { type: SkillType.Penetration, level: 0, maxLevel: 10, name: 'Bullet Penetration', description: 'Increases bullet penetration', }); this.skills.set(SkillType.Range, { type: SkillType.Range, level: 0, maxLevel: 15, name: 'Bullet Range', description: 'Increases bullet range', }); this.skills.set(SkillType.Shield, { type: SkillType.Shield, level: 0, maxLevel: 10, name: 'Shield', description: 'Increases shield capacity', }); } public updateBaseStats() { this.baseStats = { health: this.entity.setting.skill.health, regen: this.entity.setting.skill.regen, speed: this.entity.setting.skill.speed, damage: this.entity.setting.skill.damage, pen: this.entity.setting.skill.pen, range: this.entity.setting.skill.range || 0, shield: this.entity.setting.skill.shield || 0, }; } public addSkillPoints(points: number) { this.skillPoints += points; } public getSkillPoints(): number { return this.skillPoints; } public upgradeSkill(type: SkillType): boolean { const skill = this.skills.get(type); if (!skill) return false; if (this.skillPoints <= 0 || skill.level >= skill.maxLevel) { return false; } skill.level++; this.skillPoints--; this.applyAllSkillEffects(); return true; } public applyAllSkillEffects() { // Reset to base stats before applying skill effects this.entity.setting.skill.health = this.baseStats.health; this.entity.setting.skill.regen = this.baseStats.regen; this.entity.setting.skill.speed = this.baseStats.speed; this.entity.setting.skill.damage = this.baseStats.damage; this.entity.setting.skill.pen = this.baseStats.pen; if (this.baseStats.range) { this.entity.setting.skill.range = this.baseStats.range; } if (this.baseStats.shield) { this.entity.setting.skill.shield = this.baseStats.shield; } // Apply skill effects based on the formula for (const [type, skill] of this.skills) { if (skill.level > 0) { const skillPoints = skill.level; const maxSkillPoints = skill.maxLevel; const x = skillPoints / maxSkillPoints; let multiplier = 1; // Default multiplier switch (type) { case SkillType.Damage: // Apply the logarithmic damage formula multiplier = 1.875 * Math.log(4 * x + 1) + 1; this.entity.setting.skill.damage = this.baseStats.damage * multiplier; break; case SkillType.MaxHealth: // Apply a similar formula for health multiplier = 1.875 * Math.log(4 * x + 1) + 1; this.entity.setting.skill.health = this.baseStats.health * multiplier; break; case SkillType.HealthRegen: // Apply the formula for health regeneration multiplier = 1.875 * Math.log(4 * x + 1) + 1; this.entity.setting.skill.regen = this.baseStats.regen * multiplier; break; case SkillType.MovementSpeed: // For movement speed, you might want to cap the increase multiplier = 0.5 + 1.5 * x; // Example formula this.entity.setting.skill.speed = this.baseStats.speed * multiplier; break; case SkillType.Penetration: // Apply the formula for penetration multiplier = 2.5 * x + 1; this.entity.setting.skill.pen = this.baseStats.pen * multiplier; break; case SkillType.Range: // Apply the formula for range if applicable if (this.baseStats.range) { multiplier = 1 + skill.level * 0.1; // Keeping linear for range this.entity.setting.skill.range = this.baseStats.range * multiplier; } break; case SkillType.Shield: // Apply the formula for shield multiplier = 1.875 * Math.log(4 * x + 1) + 1; this.entity.setting.skill.shield = this.baseStats.shield * multiplier; break; } } } } public getSkill(type: SkillType): Skill | undefined { return this.skills.get(type); } public getAllSkills(): Skill[] { return Array.from(this.skills.values()); } }

Modified applyDamage Function:

protected applyDamage(entity: Entity, other: Entity, baseDamage: number): void { // Calculate damage multiplier based on the attacker's damage skill const attackerSkillManager = other.skillManager; // Assuming 'other' has a skillManager const damageSkill = attackerSkillManager.getSkill(SkillType.Damage); let damageMultiplier = 1; if (damageSkill && damageSkill.level > 0) { const skillPoints = damageSkill.level; const maxSkillPoints = damageSkill.maxLevel; const x = skillPoints / maxSkillPoints; damageMultiplier = 1.875 * Math.log(4 * x + 1) + 1; } const damage = baseDamage * damageMultiplier; const remainingDamage = Math.max(0, damage - entity.shield); entity.shield = Math.max(0, entity.shield - damage); if (remainingDamage > 0) { entity.health -= remainingDamage; } entity.emit('damage', damage); entity.lastTickAttacked = entity.tick; if (entity.health <= 0) { entity.emit('dead', other); if (!entity.die) { this.giveScore(other, entity); } this.remove(entity); } }

Explanation of Changes:

  1. Applying the Damage Formula in applyAllSkillEffects:

    • For the Damage skill, we replaced the linear multiplier with the logarithmic formula:

      multiplier = 1.875 * Math.log(4 * x + 1) + 1; this.entity.setting.skill.damage = this.baseStats.damage * multiplier;
    • x is the ratio of skill points allocated to the maximum skill points.

    • This formula ensures that each additional skill point contributes less to the total damage, creating diminishing returns.

  2. Adjusting Other Skills:

    • For skills like MaxHealth, HealthRegen, and Shield, we applied the same logarithmic formula to maintain consistency and balance.

    • For MovementSpeed, a different formula is used to cap the speed increase and prevent excessive speeds:

      multiplier = 0.5 + 1.5 * x;

      You can adjust this formula as needed for your game's balance.

    • For Penetration and Range, you might want to adjust the formulas based on how you want them to scale. In the example, Penetration uses a linear formula, but you can modify it similarly.

  3. Updating applyDamage Function:

    • Before applying damage, we calculate the damage multiplier based on the attacker's Damage skill:

      const attackerSkillManager = other.skillManager; // Assuming 'other' has a skillManager const damageSkill = attackerSkillManager.getSkill(SkillType.Damage); let damageMultiplier = 1; if (damageSkill && damageSkill.level > 0) { const skillPoints = damageSkill.level; const maxSkillPoints = damageSkill.maxLevel; const x = skillPoints / maxSkillPoints; damageMultiplier = 1.875 * Math.log(4 * x + 1) + 1; } const damage = baseDamage * damageMultiplier;
    • This ensures that the damage applied considers the attacker's skill level using the logarithmic formula.

  4. Assumptions:

    • It's assumed that each Entity has a skillManager property to access their skills.

    • The base damage (baseDamage) is provided to the applyDamage function, which is then multiplied by the damage multiplier.

  5. Ensuring Consistency:

    • By applying the same formula across relevant skills, we maintain consistency in how skills scale with levels.

    • The logarithmic formula provides diminishing returns, which helps balance the game by preventing any single skill from becoming too overpowered when maxed out.

Additional Notes:

  • Adjusting the Formula for Other Skills:

    • If you want other skills to have diminishing returns, you can apply the logarithmic formula to them as well.

    • For example, for HealthRegen:

      multiplier = 1.875 * Math.log(4 * x + 1) + 1; this.entity.setting.skill.regen = this.baseStats.regen * multiplier;
  • Fine-Tuning the Formula:

    • The constants in the formula (like 1.875 and the 4 inside the logarithm) can be adjusted to fit the desired scaling in your game.

    • For instance, changing 1.875 to a higher value would increase the impact of skill points.

  • Handling Edge Cases:

    • Ensure that when skill.level is 0, the multiplier returns 1, meaning no change from the base stats.

    • Also, when skill.level equals maxSkillPoints, the multiplier should be at its maximum value according to the formula.

  • Preventing Negative Values:

    • Use Math.max and Math.min where necessary to prevent multipliers from becoming negative or exceeding expected limits.

Conclusion:

By integrating the logarithmic formula into your SkillManager and applyDamage methods, you ensure that skill progression has diminishing returns, enhancing game balance. Players will notice that early skill points have a significant impact, while maxing out a skill requires more investment for smaller gains, encouraging diversified skill builds.


If you have any questions or need further adjustments, feel free to ask!

Share this Q&A