From 6af905a857b8283bb9a2fc152a113b6abdd85854 Mon Sep 17 00:00:00 2001 From: Samiker <168203446+Samiker69@users.noreply.github.com> Date: Sat, 14 Mar 2026 16:36:10 +0300 Subject: [PATCH] =?UTF-8?q?Feat=20(Logic):=20=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D1=81=D0=B8=D1=81=D1=82=D0=B5?= =?UTF-8?q?=D0=BC=D0=B0=20=D0=BE=D1=80=D1=83=D0=B6=D0=B8=D0=B9=20-=20Attac?= =?UTF-8?q?kSystem=20->=20WeaponSystem=20-=20WeaponComponent=20=D1=85?= =?UTF-8?q?=D1=80=D0=B0=D0=BD=D0=B8=D1=82=20=D0=BE=D1=80=D1=83=D0=B6=D0=B8?= =?UTF-8?q?=D1=8F=20(WeaponEmitter)=20=20=20-=20WeaponEmitter=20=D1=85?= =?UTF-8?q?=D1=80=D0=B0=D0=BD=D0=B8=D1=82=20=D0=B8=D0=BD=D1=84=D0=BE=D1=80?= =?UTF-8?q?=D0=BC=D0=B0=D1=86=D0=B8=D1=8E=20=D0=BE=D0=B1=20=D0=B0=D1=82?= =?UTF-8?q?=D0=B0=D0=BA=D0=B5=20=20=20-=20=D0=BF=D0=BE=20=D1=81=D1=83?= =?UTF-8?q?=D1=82=D0=B8,=20=D0=BC=D0=BE=D0=B6=D0=BD=D0=BE=20=D0=B4=D0=B5?= =?UTF-8?q?=D0=BB=D0=B0=D1=82=D1=8C=20=D0=BD=D0=B5=D1=81=D0=BA=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C=D0=BA=D0=BE=20=D0=B0=D1=82=D0=B0=D0=BA=20=D1=81=D1=83?= =?UTF-8?q?=D1=89=D0=BD=D0=BE=D1=81=D1=82=D1=8F=D0=BC!!=20-=20=D0=92=D1=81?= =?UTF-8?q?=D0=B5=20=D0=BF=D0=B0=D1=82=D1=82=D0=B5=D1=80=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=B2=D1=8B=D0=BD=D0=B5=D1=81=D0=B5=D0=BD=D1=8B=20=D0=B2=20`xy?= =?UTF-8?q?z.samiker.theendlessweave.logic.patterns`=20=D0=B8=20=D1=80?= =?UTF-8?q?=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D1=83=D1=8E=D1=82=20IPattern,=20?= =?UTF-8?q?=D1=85=D1=80=D0=B0=D0=BD=D1=8F=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA?= =?UTF-8?q?=D1=83=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D0=BF=D1=83=D0=BB=D1=8C=20-=20Fixes=20=20=20-=20=D0=B8=D1=81?= =?UTF-8?q?=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=BE=20=D0=BE=D1=82?= =?UTF-8?q?=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8=D0=B5=20Health?= =?UTF-8?q?Bar=20(=D1=82=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D0=BF=D0=BE?= =?UTF-8?q?=D0=BA=D0=B0=D0=B7=D1=8B=D0=B2=D0=B0=D0=B5=D1=82=20%)=20=20=20-?= =?UTF-8?q?=20=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0?= =?UTF-8?q?=20=D0=BB=D0=BE=D0=B3=D0=B8=D0=BA=D0=B0=20=D0=B2=D0=B8=D0=B4?= =?UTF-8?q?=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D0=B8=20AI=20-=20Other=20=20=20-?= =?UTF-8?q?=20=D1=82=D0=B5=D0=BF=D0=B5=D1=80=D1=8C=20=D0=B2=D1=80=D0=B0?= =?UTF-8?q?=D0=B3=D0=B8=20=D0=B1=D1=83=D0=B4=D1=83=D1=82=20=D0=BF=D0=BE?= =?UTF-8?q?=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=BD=D0=BE=20=D1=81=D0=BC=D0=BE?= =?UTF-8?q?=D1=82=D1=80=D0=B5=D1=82=D1=8C=20=D0=B2=20=D1=81=D1=82=D0=BE?= =?UTF-8?q?=D1=80=D0=BE=D0=BD=D1=83=20=D0=B8=D0=B3=D1=80=D0=BE=D0=BA=D0=B0?= =?UTF-8?q?,=20=D0=B0=20=D0=BD=D0=B5=20=D1=82=D0=BE=D0=BB=D1=8C=D0=BA?= =?UTF-8?q?=D0=BE=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=20=D0=BE=D0=B1=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20=D1=82=D0=B8=D0=BA?= =?UTF-8?q?=D0=B0=20AI=20=20=20-=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D0=BF=D0=B0=D1=80=D0=B0=D0=BC=D0=B5=D1=82=D1=80?= =?UTF-8?q?=20=D0=BC=D1=91=D1=80=D1=82=D0=B2=D0=BE=D0=B9=20=D0=B7=D0=BE?= =?UTF-8?q?=D0=BD=D1=8B=20=D0=B4=D0=B6=D0=BE=D0=B9=D1=81=D1=82=D0=B8=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2=20=D0=B2=20=D0=BD=D0=B0=D1=81=D1=82=D1=80=D0=BE?= =?UTF-8?q?=D0=B9=D0=BA=D0=B0=D1=85=20=D1=83=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../theendlessweave/screens/GameScreen.java | 6 +- .../screens/SettingsScreen.java | 35 ++- .../systems/HudRenderSystem.java | 3 +- .../theendlessweave/systems/InputSystem.java | 2 +- .../theendlessweave/network/GameServer.java | 4 +- .../theendlessweave/systems/AISystem.java | 25 +- .../theendlessweave/systems/AttackSystem.java | 230 ------------------ .../systems/ServerInputSystem.java | 31 ++- .../theendlessweave/systems/WeaponSystem.java | 99 +++++++- .../entities/EntitiesFabric.java | 23 +- .../entities/components/AIComponent.java | 1 + .../entities/components/AttackComponent.java | 50 ---- .../entities/components/PatternConfig.java | 4 +- .../entities/components/WeaponComponent.java | 19 +- .../entities/components/WeaponEmitter.java | 38 +++ .../theendlessweave/gamemap/EnemyFactory.java | 87 ++++--- .../gamemap/EnemySpawnData.java | 8 +- .../samiker/theendlessweave/logic/Game.java | 25 +- .../logic/ai/astar/PathNode.java | 52 ---- .../logic/patterns/BeamPattern.java | 43 ++++ .../logic/patterns/BurstPattern.java | 28 +++ .../logic/patterns/CrossWavePattern.java | 37 +++ .../logic/patterns/FanPattern.java | 30 +++ .../logic/patterns/IPattern.java | 10 + .../logic/patterns/RingPattern.java | 29 +++ .../logic/patterns/SineWavePattern.java | 34 +++ .../logic/patterns/SingleShotPattern.java | 16 ++ .../logic/patterns/SpiralPattern.java | 30 +++ .../logic/patterns/WallPattern.java | 35 +++ .../settings/ClientSettings.java | 2 + .../settings/SettingsManager.java | 10 + 31 files changed, 598 insertions(+), 448 deletions(-) delete mode 100644 server/src/main/java/xyz/samiker/theendlessweave/systems/AttackSystem.java delete mode 100644 shared/src/main/java/xyz/samiker/theendlessweave/entities/components/AttackComponent.java create mode 100644 shared/src/main/java/xyz/samiker/theendlessweave/entities/components/WeaponEmitter.java delete mode 100644 shared/src/main/java/xyz/samiker/theendlessweave/logic/ai/astar/PathNode.java create mode 100644 shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/BeamPattern.java create mode 100644 shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/BurstPattern.java create mode 100644 shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/CrossWavePattern.java create mode 100644 shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/FanPattern.java create mode 100644 shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/IPattern.java create mode 100644 shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/RingPattern.java create mode 100644 shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/SineWavePattern.java create mode 100644 shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/SingleShotPattern.java create mode 100644 shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/SpiralPattern.java create mode 100644 shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/WallPattern.java diff --git a/core/src/main/java/xyz/samiker/theendlessweave/screens/GameScreen.java b/core/src/main/java/xyz/samiker/theendlessweave/screens/GameScreen.java index 3ef7537..35f2acc 100644 --- a/core/src/main/java/xyz/samiker/theendlessweave/screens/GameScreen.java +++ b/core/src/main/java/xyz/samiker/theendlessweave/screens/GameScreen.java @@ -111,7 +111,7 @@ private void setupUI() { hudTable.setFillParent(true); hudTable.top().left(); - healthBar = new ProgressBar(0, 100, 1, false, skin); + healthBar = new ProgressBar(0, 1, .01f, false, skin); scoreLabel = new Label("Score: 0", skin); levelLabel = new Label("Lvl: 1", skin); @@ -142,12 +142,12 @@ private void setupMobileControls() { Touchpad.TouchpadStyle touchpadStyle = skin.get(Touchpad.TouchpadStyle.class); - moveStick = new Touchpad(10, touchpadStyle); + moveStick = new Touchpad(SettingsManager.getFloat(ClientSettings.JOYSTICK_MOVE_DEADZONE), touchpadStyle); moveStick.setBounds(50, 50, 200, 200); uiStage.addActor(moveStick); if (controlMode.equals("mobile_aim_joystick") || controlMode.equals("mobile_aim_all")) { - aimStick = new Touchpad(10, touchpadStyle); + aimStick = new Touchpad(SettingsManager.getFloat(ClientSettings.JOYSTICK_AIM_DEADZONE), touchpadStyle); aimStick.setBounds(Gdx.graphics.getWidth() - 250, 50, 200, 200); uiStage.addActor(aimStick); } diff --git a/core/src/main/java/xyz/samiker/theendlessweave/screens/SettingsScreen.java b/core/src/main/java/xyz/samiker/theendlessweave/screens/SettingsScreen.java index 96a8e17..838019b 100644 --- a/core/src/main/java/xyz/samiker/theendlessweave/screens/SettingsScreen.java +++ b/core/src/main/java/xyz/samiker/theendlessweave/screens/SettingsScreen.java @@ -271,9 +271,12 @@ private void showControlSettings() { table.add(new Label("Control type:", skin)); SelectBox controlSelect = new SelectBox<>(skin); - controlSelect.setItems("auto", "pc", "mobile_aim_joystick", "mobile_aim_button", "mobile_aim_all"); //TODO надо въебошить картинки + controlSelect.setItems("auto", "pc", "mobile_aim_joystick", "mobile_aim_button", "mobile_aim_all"); controlSelect.setSelected(SettingsManager.getString(ClientSettings.CONTROL)); - table.add(controlSelect).width(200).row(); + table.add(controlSelect).width(200).colspan(2).row(); + + table.add(createDeadzoneRow("Aim Deadzone:", ClientSettings.JOYSTICK_AIM_DEADZONE)).row(); + table.add(createDeadzoneRow("Move Deadzone:", ClientSettings.JOYSTICK_MOVE_DEADZONE)).row(); TextButton applyBtn = new TextButton("Apply Control Settings", skin); applyBtn.addListener(new ClickListener() { @@ -285,11 +288,37 @@ public void clicked(InputEvent event, float x, float y) { } }); - table.add(applyBtn).padTop(20).colspan(2).center(); + table.add(applyBtn).padTop(20).colspan(3).center(); contentContainer.add(table).top(); } + private Table createDeadzoneRow(String labelText, ClientSettings settingKey) { + Table row = new Table(); + row.defaults().padRight(10); + + row.add(new Label(labelText, skin)); + + float currentValue = SettingsManager.getFloat(settingKey); + Slider slider = new Slider(0, 100, 1, false, skin); + slider.setValue(currentValue); + + Label valueLabel = new Label(String.valueOf((int)currentValue), skin); + + slider.addListener(new ChangeListener() { + @Override + public void changed(ChangeEvent event, Actor actor) { + float val = slider.getValue(); + valueLabel.setText(String.valueOf((int)val)); + SettingsManager.set(settingKey, val); + } + }); + + row.add(slider).width(150); + row.add(valueLabel).width(30); + return row; + } + private void applyGraphicsSettings() { String resStr = SettingsManager.getString(ClientSettings.RESOLUTION); String[] parts = resStr.split("x"); diff --git a/core/src/main/java/xyz/samiker/theendlessweave/systems/HudRenderSystem.java b/core/src/main/java/xyz/samiker/theendlessweave/systems/HudRenderSystem.java index 199e188..22036c9 100644 --- a/core/src/main/java/xyz/samiker/theendlessweave/systems/HudRenderSystem.java +++ b/core/src/main/java/xyz/samiker/theendlessweave/systems/HudRenderSystem.java @@ -1,5 +1,6 @@ package xyz.samiker.theendlessweave.systems; +import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.scenes.scene2d.ui.Label; import com.badlogic.gdx.scenes.scene2d.ui.ProgressBar; import xyz.samiker.theendlessweave.entities.Entity; @@ -47,11 +48,9 @@ private void updateUI(Entity player) { float percent = lastRenderedHp / lastRenderedMaxHp; healthBar.setValue(percent); - /* if (percent > 0.5f) healthBar.setColor(Color.GREEN); else if (percent > 0.25f) healthBar.setColor(Color.ORANGE); else healthBar.setColor(Color.RED); - */ } } diff --git a/core/src/main/java/xyz/samiker/theendlessweave/systems/InputSystem.java b/core/src/main/java/xyz/samiker/theendlessweave/systems/InputSystem.java index 470884c..4332702 100644 --- a/core/src/main/java/xyz/samiker/theendlessweave/systems/InputSystem.java +++ b/core/src/main/java/xyz/samiker/theendlessweave/systems/InputSystem.java @@ -21,7 +21,7 @@ public class InputSystem implements ISystem { private final OrthographicCamera camera; private Entity player; - private boolean isMobile = false; + private final boolean isMobile; private float lastAngle = 0; public InputSystem(GameClient client, Game game, OrthographicCamera camera) { diff --git a/server/src/main/java/xyz/samiker/theendlessweave/network/GameServer.java b/server/src/main/java/xyz/samiker/theendlessweave/network/GameServer.java index eb1beec..31c1a13 100644 --- a/server/src/main/java/xyz/samiker/theendlessweave/network/GameServer.java +++ b/server/src/main/java/xyz/samiker/theendlessweave/network/GameServer.java @@ -138,9 +138,9 @@ public void initSystems() { game.addLogicSystem(new ServerInputSystem(this, game)); game.addLogicSystem(new ServerDebugSystem(game.getProfiler())); - game.addLogicSystem(new WeaponSystem()); game.addLogicSystem(new AISystem(map, aiExecutor)); - game.addLogicSystem(new AttackSystem(game)); + game.addLogicSystem(new WeaponSystem(this.game)); + //game.addLogicSystem(new AttackSystem(game)); //TODO:remove game.addLogicSystem(new ProjectileSystem()); game.addLogicSystem(new MovementSystem(map)); game.addLogicSystem(new DamageSystem()); diff --git a/server/src/main/java/xyz/samiker/theendlessweave/systems/AISystem.java b/server/src/main/java/xyz/samiker/theendlessweave/systems/AISystem.java index 33b9a74..b7cc1bd 100644 --- a/server/src/main/java/xyz/samiker/theendlessweave/systems/AISystem.java +++ b/server/src/main/java/xyz/samiker/theendlessweave/systems/AISystem.java @@ -1,15 +1,11 @@ package xyz.samiker.theendlessweave.systems; import xyz.samiker.theendlessweave.entities.Entity; -import xyz.samiker.theendlessweave.entities.components.AIComponent; -import xyz.samiker.theendlessweave.entities.components.PathfindingComponent; -import xyz.samiker.theendlessweave.entities.components.PlayerTagComponent; -import xyz.samiker.theendlessweave.entities.components.PositionComponent; +import xyz.samiker.theendlessweave.entities.components.*; import xyz.samiker.theendlessweave.gamemap.GameMap; import xyz.samiker.theendlessweave.logic.ai.EntityAIState; import xyz.samiker.theendlessweave.logic.ai.LineOfSightUtil; import xyz.samiker.theendlessweave.logic.ai.astar.AStarPathfinder; -import xyz.samiker.theendlessweave.systems.ISystem; import xyz.samiker.theendlessweave.tile.Tile; import xyz.samiker.theendlessweave.utils.Array; @@ -62,7 +58,12 @@ public void update(Array entities, double deltaTime) { Entity entity = entities.get(i); AIComponent ai = entity.getComponent(AIComponent.class); - if (ai == null) continue; + RotationComponent rot = entity.getComponent(RotationComponent.class); + PositionComponent pos = entity.getComponent(PositionComponent.class); + if (ai == null || rot == null || pos == null) continue; + + double angleToPlayer = Math.atan2(cachedPlayerPos.y - pos.y, cachedPlayerPos.x - pos.x); + rot.angle = Math.toDegrees(angleToPlayer); ai.addToTickAccumulator(deltaTime); double timeToNextTick = ai.getTimeToNextTick(); @@ -72,8 +73,10 @@ public void update(Array entities, double deltaTime) { } ai.minusTickAccumulator(timeToNextTick); - PositionComponent pos = entity.getComponent(PositionComponent.class); - if (pos == null) continue; + WeaponComponent weapon = entity.getComponent(WeaponComponent.class); + if (weapon == null) continue; + + weapon.isFiring = ai.getState() == EntityAIState.ATTACK; double distSq = getDistanceSquared(pos.x, pos.y, pX, pY); updateEntityState(entity, ai, pos, distSq); @@ -145,11 +148,12 @@ private void handleChaseState(Entity entity, AIComponent ai, PositionComponent p pathfinding.setPath(null); } pathfinding.setDirectTarget(targetTile); + ai.lastPlayerPos = cachedPlayerPos; } else { pathfinding.setDirectTarget(null); - if (!pathfinding.hasPath() && !ai.isWaitingForPath()) { - submitPathfindingTask(entity, ai, pos, cachedPlayerPos); + if (!pathfinding.hasPath() && !ai.isWaitingForPath() && ai.lastPlayerPos != null) { + submitPathfindingTask(entity, ai, pos, ai.lastPlayerPos); } } } @@ -194,7 +198,6 @@ private void applyFinishedPaths() { PathfindingComponent pc = e.getComponent(PathfindingComponent.class); if (pc != null) { pc.setDirectTarget(null); - // Здесь предполагается, что в PathfindingComponent метод setPath принимает Array pc.setPath(result.path); } } diff --git a/server/src/main/java/xyz/samiker/theendlessweave/systems/AttackSystem.java b/server/src/main/java/xyz/samiker/theendlessweave/systems/AttackSystem.java deleted file mode 100644 index 855a15a..0000000 --- a/server/src/main/java/xyz/samiker/theendlessweave/systems/AttackSystem.java +++ /dev/null @@ -1,230 +0,0 @@ -package xyz.samiker.theendlessweave.systems; - -import xyz.samiker.theendlessweave.entities.Entity; -import xyz.samiker.theendlessweave.entities.components.*; -import xyz.samiker.theendlessweave.logic.Game; -import xyz.samiker.theendlessweave.logic.ai.EntityAIState; -import xyz.samiker.theendlessweave.systems.ISystem; -import xyz.samiker.theendlessweave.utils.Array; - -import static xyz.samiker.theendlessweave.utils.Constants.SECONDS_PER_TICK; -import static xyz.samiker.theendlessweave.utils.Constants.TILE_SIZE; - -public class AttackSystem implements ISystem { - private final Game game; - private Entity player; - - public AttackSystem(Game game) { - this.game = game; - } - - @Override - public void update(Array entities, double deltaTime) { - updatePlayerCache(entities); - if (player == null) return; - - PositionComponent playerPos = player.getComponent(PositionComponent.class); - HealthComponent playerHealth = player.getComponent(HealthComponent.class); - if (playerPos == null || playerHealth == null) return; - - for (int i = 0; i < entities.size; i++) { - Entity entity = entities.get(i); - if (!entity.hasComponent(AIComponent.class) || !entity.hasComponent(AttackComponent.class)) continue; - - AIComponent ai = entity.getComponent(AIComponent.class); - AttackComponent attack = entity.getComponent(AttackComponent.class); - - if (ai.getState() != EntityAIState.ATTACK) continue; - - PositionComponent pos = entity.getComponent(PositionComponent.class); - if (pos == null) continue; - - attack.cooldownTimer += deltaTime; - if (attack.cooldownTimer >= attack.attackCooldown) { - performAttack(entity, pos, playerPos, playerHealth, attack); - attack.cooldownTimer = 0; - } - } - } - - private void performAttack(Entity attacker, PositionComponent attackerPos, PositionComponent playerPos, - HealthComponent playerHealth, AttackComponent attack) { - double dx = playerPos.x - attackerPos.x; - double dy = playerPos.y - attackerPos.y; - double angle = Math.atan2(dy, dx); - - if (attacker.hasComponent(RotationComponent.class)) { - attacker.getComponent(RotationComponent.class).angle = Math.toDegrees(angle); - } - - switch (attack.attackType) { - case MELEE -> performMeleeAttack(playerHealth, attack); - case PROJECTILE -> performProjectileAttack(attacker, attackerPos, attack, angle); - case PATTERN -> performPatternAttack(attacker, attackerPos, attack, angle); - } - } - - private void performPatternAttack(Entity attacker, PositionComponent attackerPos, AttackComponent attack, double angle) { - PatternConfig config = attack.patternConfig; - if (config == null) config = PatternConfig.voidRing(); - - switch (config.getPatternType()) { - //case SIMPLE -> createRingPattern(attacker, attackerPos, attack, angle, config); - case RING -> createRingPattern(attacker, attackerPos, attack, angle, config); - case FAN -> createFanPattern(attacker, attackerPos, attack, angle, config); - case BURST -> createBurstPattern(attacker, attackerPos, attack, angle, config); - case SINE_WAVE -> createSineWavePattern(attacker, attackerPos, attack, angle, config); - case SPIRAL -> createSpiralPattern(attacker, attackerPos, attack, angle, config); - case BEAM -> createBeamPattern(attacker, attackerPos, attack, angle, config); - case CROSS_WAVE -> createCrossWavePattern(attacker, attackerPos, attack, angle, config); - case WALL -> createWallPattern(attacker, attackerPos, attack, angle, config); - } - } - - private void createRingPattern(Entity attacker, PositionComponent pos, AttackComponent attack, double angle, PatternConfig config) { - int count = config.getBulletCount(); - double angleStep = (Math.PI * 2) / count; - - for (int i = 0; i < count; i++) { - double bulletAngle = i * angleStep; - spawnBullet(attacker, pos, attack, bulletAngle, null); - } - } - - private void createFanPattern(Entity attacker, PositionComponent pos, AttackComponent attack, double angle, PatternConfig config) { - int count = config.getBulletCount(); - double spread = config.getSpreadAngle(); - - double startAngle = angle - spread / 2.0; - double angleStep = (count > 1) ? spread / (count - 1) : 0; - - for (int i = 0; i < count; i++) { - double bulletAngle = startAngle + (i * angleStep); - spawnBullet(attacker, pos, attack, bulletAngle, null); - } - } - - private void createBurstPattern(Entity attacker, PositionComponent pos, AttackComponent attack, double angle, PatternConfig config) { - int count = config.getBulletCount(); - for (int i = 0; i < count; i++) { - double randomOffset = (Math.random() - 0.5) * 0.17; - double speedVar = attack.projectileSpeed * (0.9 + Math.random() * 0.2); - - Entity bullet = game.createProjectile(pos.x, pos.y, speedVar, angle + randomOffset); - bullet.getComponent(ProjectileComponent.class) - .init(attack.damage, attack.projectileLifetime, attacker, angle + randomOffset, ProjectileComponent.TargetType.PLAYER); - } - } - - private void createSineWavePattern(Entity attacker, PositionComponent pos, AttackComponent attack, double angle, PatternConfig config) { - int count = config.getBulletCount(); - double spread = config.getSpreadAngle(); - double startAngle = angle - spread / 2.0; - double angleStep = (count > 1) ? spread / (count - 1) : 0; - - for (int i = 0; i < count; i++) { - double bulletAngle = startAngle + (i * angleStep); - double phaseOffset = (Math.PI * 2 / count) * i; - - spawnBullet(attacker, pos, attack, bulletAngle, p -> - p.asSineWave(config.getAmplitude(), config.getFrequency(), phaseOffset)); - } - } - - private void createSpiralPattern(Entity attacker, PositionComponent pos, AttackComponent attack, double angle, PatternConfig config) { - int count = config.getBulletCount(); - double angleStep = (Math.PI * 2) / count; - - for (int i = 0; i < count; i++) { - double phaseOffset = i * angleStep; - spawnBullet(attacker, pos, attack, angle, p -> - p.asSpiral(config.getSpiralRadius(), config.getSpiralSpeed(), phaseOffset)); - } - } - - private void spawnBullet(Entity attacker, PositionComponent pos, AttackComponent attack, double angle, java.util.function.Consumer modifier) { - Entity projectile = game.createProjectile(pos.x, pos.y, attack.projectileSpeed, angle); - ProjectileComponent p = projectile.getComponent(ProjectileComponent.class); - p.init(attack.damage, attack.projectileLifetime, attacker, angle, ProjectileComponent.TargetType.PLAYER); - - if (modifier != null) { - modifier.accept(p); - } - } - - private void createBeamPattern(Entity attacker, PositionComponent pos, AttackComponent attack, double angle, PatternConfig config) { - int length = config.getBulletCount(); - int widthCount = (int) config.getAmplitude(); - double widthStep = config.getSpreadAngle(); - - for (int l = 0; l < length; l++) { - double layerDelay = l * (SECONDS_PER_TICK * 2); - double perpAngle = angle + Math.PI / 2; - - for (int w = 0; w < widthCount; w++) { - double offset = (w - (widthCount - 1) / 2.0) * widthStep * TILE_SIZE; - - double spawnX = pos.x + Math.cos(perpAngle) * offset; - double spawnY = pos.y + Math.sin(perpAngle) * offset; - game.createProjectile(spawnX, spawnY, attack.projectileSpeed, angle) - .getComponent(ProjectileComponent.class) - .init(attack.damage, attack.projectileLifetime, attacker, angle, ProjectileComponent.TargetType.PLAYER) - .asAccelerating(20.0, 40.0) - .withDelay(layerDelay); - } - } - } - - private void createCrossWavePattern(Entity attacker, PositionComponent pos, AttackComponent attack, double angle, PatternConfig config) { - int count = config.getBulletCount() / 2; - double spread = config.getSpreadAngle(); - - for (int i = 0; i < count; i++) { - double bulletAngle = (angle - spread / 2) + (spread * i / count); - spawnBullet(attacker, pos, attack, bulletAngle, p -> p.asSineWave(50, 2, 0)); - } - - for (int i = 0; i < count; i++) { - double bulletAngle = (angle - spread / 2) + (spread * i / count); - spawnBullet(attacker, pos, attack, bulletAngle, p -> p.asSineWave(50, 2, Math.PI)); - } - } - - private void createWallPattern(Entity attacker, PositionComponent pos, AttackComponent attack, double angle, PatternConfig config) { - int count = config.getBulletCount(); - double wallWidth = TILE_SIZE * 10; - double step = wallWidth / count; - - double perpAngle = angle + Math.PI / 2; - - for (int i = 0; i < count; i++) { - double offset = (i - count / 2.0) * step; - double spawnX = pos.x + Math.cos(perpAngle) * offset; - double spawnY = pos.y + Math.sin(perpAngle) * offset; - - game.createProjectile(spawnX, spawnY, attack.projectileSpeed, angle) - .getComponent(ProjectileComponent.class) - .init(attack.damage, attack.projectileLifetime, attacker, angle, ProjectileComponent.TargetType.PLAYER); - } - } - - private void performMeleeAttack(HealthComponent playerHealth, AttackComponent attack) { - playerHealth.damage(attack.damage); - } - - private void performProjectileAttack(Entity attacker, PositionComponent attackerPos, AttackComponent attack, double angle) { - spawnBullet(attacker, attackerPos, attack, angle, null); - } - - private void updatePlayerCache(Array entities) { - if (player != null && !player.dead) return; - for (int i = 0; i < entities.size; i++) { - Entity entity = entities.get(i); - if (entity.hasComponent(PlayerTagComponent.class)) { - player = entity; - return; - } - } - player = null; - } -} \ No newline at end of file diff --git a/server/src/main/java/xyz/samiker/theendlessweave/systems/ServerInputSystem.java b/server/src/main/java/xyz/samiker/theendlessweave/systems/ServerInputSystem.java index 2e923b2..fba8120 100644 --- a/server/src/main/java/xyz/samiker/theendlessweave/systems/ServerInputSystem.java +++ b/server/src/main/java/xyz/samiker/theendlessweave/systems/ServerInputSystem.java @@ -1,10 +1,7 @@ package xyz.samiker.theendlessweave.systems; import xyz.samiker.theendlessweave.entities.Entity; -import xyz.samiker.theendlessweave.entities.components.PositionComponent; -import xyz.samiker.theendlessweave.entities.components.ProjectileComponent; -import xyz.samiker.theendlessweave.entities.components.VelocityComponent; -import xyz.samiker.theendlessweave.entities.components.WeaponComponent; +import xyz.samiker.theendlessweave.entities.components.*; import xyz.samiker.theendlessweave.logic.Game; import xyz.samiker.theendlessweave.network.packets.PacketInput; import xyz.samiker.theendlessweave.systems.ISystem; @@ -41,23 +38,23 @@ public void update(Array entities, double dt) { private void applyInput(Entity player, PacketInput input) { VelocityComponent vel = player.getComponent(VelocityComponent.class); - PositionComponent pos = player.getComponent(PositionComponent.class); WeaponComponent weapon = player.getComponent(WeaponComponent.class); - if (vel == null || weapon == null) return; + RotationComponent rot = player.getComponent(RotationComponent.class); - double speed = 1; - vel.dx = 0; - vel.dy = 0; + if (vel != null) { + double speed = vel.speed; + vel.dx = 0; + vel.dy = 0; - if (input.up) vel.dy = speed; - if (input.down) vel.dy = -speed; - - if (input.left) vel.dx = -speed; - if (input.right) vel.dx = speed; + if (input.up) vel.dy = speed; + if (input.down) vel.dy = -speed; + if (input.left) vel.dx = -speed; + if (input.right) vel.dx = speed; + } - if (input.shoot && weapon.cooldown <= 0) { - game.createProjectile(pos.x, pos.y, weapon.projectileSpeed, input.mouseAngle).getComponent(ProjectileComponent.class).init(5, 5, player, input.mouseAngle, ProjectileComponent.TargetType.ENEMY); - weapon.cooldown = weapon.cooldownTime; + if (weapon != null && rot != null) { + rot.angle = Math.toDegrees(input.mouseAngle); + weapon.isFiring = input.shoot; } } diff --git a/server/src/main/java/xyz/samiker/theendlessweave/systems/WeaponSystem.java b/server/src/main/java/xyz/samiker/theendlessweave/systems/WeaponSystem.java index 4f7f701..c091f5d 100644 --- a/server/src/main/java/xyz/samiker/theendlessweave/systems/WeaponSystem.java +++ b/server/src/main/java/xyz/samiker/theendlessweave/systems/WeaponSystem.java @@ -1,20 +1,103 @@ package xyz.samiker.theendlessweave.systems; import xyz.samiker.theendlessweave.entities.Entity; -import xyz.samiker.theendlessweave.entities.components.WeaponComponent; -import xyz.samiker.theendlessweave.systems.ISystem; +import xyz.samiker.theendlessweave.entities.components.*; +import xyz.samiker.theendlessweave.logic.Game; import xyz.samiker.theendlessweave.utils.Array; public class WeaponSystem implements ISystem { + private final Game game; + private Entity cachedPlayer; + private PositionComponent cachedPlayerPos; + private HealthComponent cachedPlayerHealth; + + public WeaponSystem(Game game) { + this.game = game; + } + @Override - public void update(Array entities, double dt) { + public void update(Array entities, double deltaTime) { + findPlayer(entities); + for (int i = 0; i < entities.size; i++) { - Entity e = entities.get(i); - WeaponComponent weapon = e.getComponent(WeaponComponent.class); + Entity entity = entities.get(i); - if (weapon != null && weapon.cooldown > 0) { - weapon.cooldown -= dt; + WeaponComponent weaponComp = entity.getComponent(WeaponComponent.class); + if (weaponComp == null) continue; + + for (int j = 0; j < weaponComp.emitters.size; j++) { + WeaponEmitter emitter = weaponComp.emitters.get(j); + if (emitter.timer > 0) { + emitter.timer -= deltaTime; + } + } + + if (!weaponComp.isFiring) continue; + + PositionComponent pos = entity.getComponent(PositionComponent.class); + RotationComponent rot = entity.getComponent(RotationComponent.class); + if (pos == null || rot == null) continue; + + for (int j = 0; j < weaponComp.emitters.size; j++) { + WeaponEmitter emitter = weaponComp.emitters.get(j); + + if (emitter.timer <= 0) { + if (emitter.isMelee) { + handleMeleeAttack(entity, pos, emitter, entities); + } else { + double angleRad = Math.toRadians(rot.angle); + emitter.pattern.fire(game, entity, pos, angleRad, emitter); + } + emitter.timer = emitter.cooldown; + } } } } -} + + private void handleMeleeAttack(Entity attacker, PositionComponent attackerPos, WeaponEmitter emitter, Array entities) { + double rangeSq = emitter.meleeRange * emitter.meleeRange; + + if (emitter.targetType == ProjectileComponent.TargetType.PLAYER) { + if (cachedPlayer != null && !cachedPlayer.dead && cachedPlayerHealth != null) { + if (getDistanceSquared(attackerPos.x, attackerPos.y, cachedPlayerPos.x, cachedPlayerPos.y) <= rangeSq) { + cachedPlayerHealth.damage(emitter.damage); + } + } + } else if (emitter.targetType == ProjectileComponent.TargetType.ENEMY) { + for (int i = 0; i < entities.size; i++) { + Entity target = entities.get(i); + if (target == attacker || target.dead || !target.hasComponent(AIComponent.class)) continue; + + PositionComponent targetPos = target.getComponent(PositionComponent.class); + HealthComponent targetHealth = target.getComponent(HealthComponent.class); + + if (targetPos != null && targetHealth != null) { + if (getDistanceSquared(attackerPos.x, attackerPos.y, targetPos.x, targetPos.y) <= rangeSq) { + targetHealth.damage(emitter.damage); + } + } + } + } + } + + private void findPlayer(Array entities) { + cachedPlayer = null; + cachedPlayerPos = null; + cachedPlayerHealth = null; + for (int i = 0; i < entities.size; i++) { + Entity entity = entities.get(i); + if (entity.hasComponent(PlayerTagComponent.class)) { + cachedPlayer = entity; + cachedPlayerPos = entity.getComponent(PositionComponent.class); + cachedPlayerHealth = entity.getComponent(HealthComponent.class); + return; + } + } + } + + private double getDistanceSquared(double x1, double y1, double x2, double y2) { + double dx = x1 - x2; + double dy = y1 - y2; + return dx * dx + dy * dy; + } +} \ No newline at end of file diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/entities/EntitiesFabric.java b/shared/src/main/java/xyz/samiker/theendlessweave/entities/EntitiesFabric.java index 8e9bb15..b9ca146 100644 --- a/shared/src/main/java/xyz/samiker/theendlessweave/entities/EntitiesFabric.java +++ b/shared/src/main/java/xyz/samiker/theendlessweave/entities/EntitiesFabric.java @@ -1,6 +1,7 @@ package xyz.samiker.theendlessweave.entities; import xyz.samiker.theendlessweave.entities.components.*; +import xyz.samiker.theendlessweave.logic.patterns.SingleShotPattern; public class EntitiesFabric { public static Entity createPlayer(int entityId, double x, double y, double speed) { @@ -10,11 +11,17 @@ public static Entity createPlayer(int entityId, double x, double y, double speed player.addComponent(new NameComponent("Player")); player.addComponent(new PositionComponent(x, y)); player.addComponent(new VelocityComponent(speed, 0, 0)); - player.addComponent(new HealthComponent(9799669, 9799669)); + player.addComponent(new HealthComponent(100, 100)); player.addComponent(new RotationComponent(0)); player.addComponent(new ScoreComponent(0)); player.addComponent(new LevelComponent(1, 0, 100)); - player.addComponent(new WeaponComponent(.5, 8)); + WeaponComponent weapons = new WeaponComponent(); + weapons.addEmitter(new WeaponEmitter( + 0.5, 8.0, 15.0, + ProjectileComponent.TargetType.ENEMY, + new SingleShotPattern() + )); + player.addComponent(weapons); RenderComponent render = new RenderComponent(RenderComponent.RenderType.PLAYER, 3); render.alwaysVisible = true; @@ -23,9 +30,10 @@ public static Entity createPlayer(int entityId, double x, double y, double speed return player; } - public static Entity createEnemy(int entityId, double x, double y, double speed, double initialHealth, double maxHealth, AttackComponent attackComponent, AIComponent aiComponent) { + public static Entity createEnemy(int entityId, double x, double y, double speed, double initialHealth, double maxHealth, + AIComponent aiComponent, WeaponComponent weaponComponent) { Entity enemy = new Entity(entityId); - enemy.addComponent(new NameComponent("pidor")); + enemy.addComponent(new NameComponent("meow"+entityId)); enemy.addComponent(new PositionComponent(x, y)); enemy.addComponent(new VelocityComponent(speed, 0, 0)); enemy.addComponent(new HealthComponent(initialHealth, maxHealth)); @@ -33,10 +41,15 @@ public static Entity createEnemy(int entityId, double x, double y, double speed, enemy.addComponent(new ShowNameTagComponent()); enemy.addComponent(aiComponent); enemy.addComponent(new PathfindingComponent()); - enemy.addComponent(attackComponent); + + if (weaponComponent != null) { + enemy.addComponent(weaponComponent); + } + enemy.addComponent(new RenderComponent(RenderComponent.RenderType.ENEMY, 2)); return enemy; } + public static Entity createProjectile(int entityId, double x, double y, double speed, double angle) { Entity projectile = ProjectilePool.obtain(); diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/AIComponent.java b/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/AIComponent.java index d0849f1..f0ad690 100644 --- a/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/AIComponent.java +++ b/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/AIComponent.java @@ -13,6 +13,7 @@ public class AIComponent implements Component { private double timeToNextTick; private double tickAccumulator = 0; private volatile boolean isWaitingForPath = false; + public PositionComponent lastPlayerPos = null; public AIComponent(EntityAIState state, double aggressionRadius, double attackRadius) { this(state, aggressionRadius, attackRadius, 0.25); diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/AttackComponent.java b/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/AttackComponent.java deleted file mode 100644 index 6d76454..0000000 --- a/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/AttackComponent.java +++ /dev/null @@ -1,50 +0,0 @@ -package xyz.samiker.theendlessweave.entities.components; - -/** - * Компонент атаки с поддержкой паттернов. - */ -public class AttackComponent implements Component { - public AttackType attackType; - public double damage; - public double attackCooldown; - public double cooldownTimer = 0; - - // Для снарядов - public double projectileSpeed = 5.0; - public double projectileLifetime = 3.0; - - // Конфигурация паттерна - public PatternConfig patternConfig; - - public AttackComponent(AttackType attackType, double damage, double attackCooldown) { - this.attackType = attackType; - this.damage = damage; - this.attackCooldown = attackCooldown; - } - - public AttackComponent(AttackType attackType, double damage, double attackCooldown, - double projectileSpeed, double projectileLifetime) { - this.attackType = attackType; - this.damage = damage; - this.attackCooldown = attackCooldown; - this.projectileSpeed = projectileSpeed; - this.projectileLifetime = projectileLifetime; - } - - public AttackComponent(AttackType attackType, double damage, double attackCooldown, - double projectileSpeed, double projectileLifetime, - PatternConfig patternConfig) { - this.attackType = attackType; - this.damage = damage; - this.attackCooldown = attackCooldown; - this.projectileSpeed = projectileSpeed; - this.projectileLifetime = projectileLifetime; - this.patternConfig = patternConfig; - } - - public enum AttackType { - MELEE, - PROJECTILE, - PATTERN - } -} \ No newline at end of file diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/PatternConfig.java b/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/PatternConfig.java index f339edb..ac68bb3 100644 --- a/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/PatternConfig.java +++ b/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/PatternConfig.java @@ -82,8 +82,8 @@ public static PatternConfig crossShadow() { public static PatternConfig hexStorm() { return new Builder() .patternType(PatternType.SINE_WAVE) - .bulletCount(64) - .amplitude(400) + .bulletCount(128) + .amplitude(40) .frequency(4.0) .build(); } diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/WeaponComponent.java b/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/WeaponComponent.java index 675be68..e498be5 100644 --- a/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/WeaponComponent.java +++ b/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/WeaponComponent.java @@ -1,13 +1,14 @@ package xyz.samiker.theendlessweave.entities.components; +import xyz.samiker.theendlessweave.utils.Array; + public class WeaponComponent implements Component { - public final double cooldownTime; - public double cooldown; - public double projectileSpeed; - - public WeaponComponent(double cooldownTime, double projectileSpeed) { - this.cooldownTime = cooldownTime; - this.cooldown = cooldownTime; - this.projectileSpeed = projectileSpeed; + public boolean isFiring = false; + + public Array emitters = new Array<>(4); + + public WeaponComponent addEmitter(WeaponEmitter emitter) { + emitters.add(emitter); + return this; } -} +} \ No newline at end of file diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/WeaponEmitter.java b/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/WeaponEmitter.java new file mode 100644 index 0000000..cf2b1a6 --- /dev/null +++ b/shared/src/main/java/xyz/samiker/theendlessweave/entities/components/WeaponEmitter.java @@ -0,0 +1,38 @@ +package xyz.samiker.theendlessweave.entities.components; + +import xyz.samiker.theendlessweave.logic.patterns.IPattern; + +public class WeaponEmitter { + public boolean isMelee; + public double meleeRange; + + public double cooldown; + public double timer = 0; + + public double damage; + public double projectileSpeed; + public double projectileLifetime; + public ProjectileComponent.TargetType targetType; + + public IPattern pattern; + + public WeaponEmitter(double cooldown, double damage, double speed, + ProjectileComponent.TargetType targetType, IPattern pattern) { + this.isMelee = false; + this.cooldown = cooldown; + this.damage = damage; + this.projectileSpeed = speed; + this.projectileLifetime = 5.0; + this.targetType = targetType; + this.pattern = pattern; + } + + public WeaponEmitter(double cooldown, double damage, double meleeRange, + ProjectileComponent.TargetType targetType) { + this.isMelee = true; + this.cooldown = cooldown; + this.damage = damage; + this.meleeRange = meleeRange; + this.targetType = targetType; + } +} \ No newline at end of file diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/gamemap/EnemyFactory.java b/shared/src/main/java/xyz/samiker/theendlessweave/gamemap/EnemyFactory.java index e58071b..786fcde 100644 --- a/shared/src/main/java/xyz/samiker/theendlessweave/gamemap/EnemyFactory.java +++ b/shared/src/main/java/xyz/samiker/theendlessweave/gamemap/EnemyFactory.java @@ -2,10 +2,9 @@ import xyz.samiker.theendlessweave.entities.EntitiesFabric; import xyz.samiker.theendlessweave.entities.Entity; -import xyz.samiker.theendlessweave.entities.components.AIComponent; -import xyz.samiker.theendlessweave.entities.components.AttackComponent; -import xyz.samiker.theendlessweave.entities.components.PatternConfig; +import xyz.samiker.theendlessweave.entities.components.*; import xyz.samiker.theendlessweave.logic.ai.EntityAIState; +import xyz.samiker.theendlessweave.logic.patterns.*; import static xyz.samiker.theendlessweave.utils.Constants.TILE_SIZE; @@ -16,12 +15,12 @@ public static Entity createEnemy(int entityId, EnemySpawnData spawnData) { double worldY = spawnData.getGridY() * TILE_SIZE + (double) TILE_SIZE / 2; AIComponent aiComponent = createAIComponent(type); - AttackComponent attackComponent = createAttackComponent(type); + WeaponComponent weaponComponent = createWeaponComponent(type); return EntitiesFabric.createEnemy( entityId, worldX, worldY, type.speed, type.health, type.health, - attackComponent, aiComponent + aiComponent, weaponComponent ); } @@ -34,49 +33,65 @@ private static AIComponent createAIComponent(EnemySpawnData.EnemyType type) { ); } - private static AttackComponent createAttackComponent(EnemySpawnData.EnemyType type) { + private static WeaponComponent createWeaponComponent(EnemySpawnData.EnemyType type) { + WeaponComponent weapons = new WeaponComponent(); + if (type.isMelee()) { - return new AttackComponent(AttackComponent.AttackType.MELEE, type.damage, getMeleeCooldown(type)); - } else if (type == EnemySpawnData.EnemyType.PATTERN_SHOOTER || type == EnemySpawnData.EnemyType.ELITE_PATTERN) { - PatternConfig pattern = selectPattern(type); - return new AttackComponent( - AttackComponent.AttackType.PATTERN, - type.damage, - getPatternCooldown(type), - type == EnemySpawnData.EnemyType.ELITE_PATTERN ? 15 : 3, - 5.0, - pattern + double cooldown = type == EnemySpawnData.EnemyType.BASIC_MELEE ? 1.0 : + (type == EnemySpawnData.EnemyType.FAST_MELEE ? 0.5 : 1.5); + + double meleeRange = type.attackRadius * TILE_SIZE; + + WeaponEmitter emitter = new WeaponEmitter( + cooldown, type.damage, meleeRange, + ProjectileComponent.TargetType.PLAYER ); + weapons.addEmitter(emitter); } else { - return new AttackComponent(AttackComponent.AttackType.PROJECTILE, type.damage, getRangedCooldown(type), 8.0, 4.0); - } - } + IPattern pattern; + double cooldown; + double projSpeed = 8.0; - private static double getMeleeCooldown(EnemySpawnData.EnemyType type) { - return switch (type) { - case BASIC_MELEE -> 1.0; - case FAST_MELEE -> 0.5; - default -> 1.5; - }; - } + if (type == EnemySpawnData.EnemyType.PATTERN_SHOOTER || type == EnemySpawnData.EnemyType.ELITE_PATTERN) { + PatternConfig config = selectPatternConfig(type); + pattern = instantiatePattern(config); + cooldown = (type == EnemySpawnData.EnemyType.ELITE_PATTERN) ? 1.0 : 1.8; + projSpeed = (type == EnemySpawnData.EnemyType.ELITE_PATTERN) ? 15.0 : 8.0; + } else { + pattern = new SingleShotPattern(); + cooldown = (type == EnemySpawnData.EnemyType.ELITE_RANGED) ? 0.3 : 0.6; + } - private static double getRangedCooldown(EnemySpawnData.EnemyType type) { - return (type == EnemySpawnData.EnemyType.ELITE_RANGED) ? 0.3 : 0.6; - } + WeaponEmitter emitter = new WeaponEmitter( + cooldown, type.damage, projSpeed, + ProjectileComponent.TargetType.PLAYER, + pattern + ); + weapons.addEmitter(emitter); + } - private static double getPatternCooldown(EnemySpawnData.EnemyType type) { - return switch (type) { - case PATTERN_SHOOTER -> 1.8; - case ELITE_PATTERN -> 1.0; - default -> 2.0; - }; + return weapons; } - private static PatternConfig selectPattern(EnemySpawnData.EnemyType type) { + private static PatternConfig selectPatternConfig(EnemySpawnData.EnemyType type) { if (type == EnemySpawnData.EnemyType.ELITE_PATTERN) { return PatternConfig.deathFlower(); } else { return PatternConfig.hexStorm(); } } + + private static IPattern instantiatePattern(PatternConfig config) { + return switch (config.getPatternType()) { + case RING -> new RingPattern(config); + case FAN -> new FanPattern(config.getBulletCount(), config.getSpreadAngle()); + case SINE_WAVE -> new SineWavePattern(config); + case SPIRAL -> new SpiralPattern(config); + case BEAM -> new BeamPattern(config); + case WALL -> new WallPattern(config); + case BURST -> new BurstPattern(config); + case CROSS_WAVE -> new CrossWavePattern(config); + default -> new SingleShotPattern(); + }; + } } \ No newline at end of file diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/gamemap/EnemySpawnData.java b/shared/src/main/java/xyz/samiker/theendlessweave/gamemap/EnemySpawnData.java index 3a70357..c19d221 100644 --- a/shared/src/main/java/xyz/samiker/theendlessweave/gamemap/EnemySpawnData.java +++ b/shared/src/main/java/xyz/samiker/theendlessweave/gamemap/EnemySpawnData.java @@ -25,14 +25,14 @@ public EnemyType getType() { public enum EnemyType { BASIC_MELEE(60, 2, 10, 8, 0.8), - BASIC_RANGED(50, 1.2, 5, 12, 8), + BASIC_RANGED(50, 1.2, 5, 8, 6), FAST_MELEE(40, 3.0, 8, 10, 0.8), HEAVY_MELEE(150, 1.2, 20, 10, 0.8), - PATTERN_SHOOTER(200, 1.0, 10, 18, 15), + PATTERN_SHOOTER(200, 1.0, 10, 16, 14), ELITE_MELEE(300, 2.0, 25, 12, 1.0), - ELITE_RANGED(250, 1.5, 15, 15, 10), - ELITE_PATTERN(1000, 0.8, 15, 25, 20); + ELITE_RANGED(250, 1.5, 15, 10, 8), + ELITE_PATTERN(1000, 0.8, 15, 20, 15); public final double health; public final double speed; diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/logic/Game.java b/shared/src/main/java/xyz/samiker/theendlessweave/logic/Game.java index a64afee..703cdbc 100644 --- a/shared/src/main/java/xyz/samiker/theendlessweave/logic/Game.java +++ b/shared/src/main/java/xyz/samiker/theendlessweave/logic/Game.java @@ -4,12 +4,10 @@ import xyz.samiker.theendlessweave.entities.EntitiesFabric; import xyz.samiker.theendlessweave.entities.Entity; import xyz.samiker.theendlessweave.entities.ProjectilePool; -import xyz.samiker.theendlessweave.entities.components.AIComponent; -import xyz.samiker.theendlessweave.entities.components.AttackComponent; -import xyz.samiker.theendlessweave.entities.components.PatternConfig; -import xyz.samiker.theendlessweave.entities.components.ProjectileComponent; +import xyz.samiker.theendlessweave.entities.components.*; import xyz.samiker.theendlessweave.gamemap.*; import xyz.samiker.theendlessweave.logic.ai.EntityAIState; +import xyz.samiker.theendlessweave.logic.patterns.SingleShotPattern; import xyz.samiker.theendlessweave.systems.ISystem; import xyz.samiker.theendlessweave.utils.Array; @@ -29,8 +27,6 @@ public class Game { public final Array queueAddEntities = new Array<>(512); private int nextEntityId = 0; - private Entity player; - public Game(GameData data) { this.gameData = data; this.width = data.width; @@ -130,7 +126,7 @@ public Entity createPlayer() { nextEntityId++, roomCenter.x * TILE_SIZE, roomCenter.y * TILE_SIZE, - 5 + 2.5 ); addToAddQueue(player); return player; @@ -142,19 +138,23 @@ public Entity createPlayer(int id) { id, roomCenter.x * TILE_SIZE, roomCenter.y * TILE_SIZE, - 5 + 2.5 ); addToAddQueue(player); return player; } public Entity createEnemy(int id, double x, double y) { - AttackComponent attackComponent = new AttackComponent( - AttackComponent.AttackType.PATTERN, 2.0, .5, 3, 20.0, PatternConfig.deathRay() - ); AIComponent aiComponent = new AIComponent(EntityAIState.IDLE, 600, 500); + WeaponComponent weapon = new WeaponComponent(); + weapon.addEmitter(new WeaponEmitter( + 0.5, 8.0, 15.0, + ProjectileComponent.TargetType.ENEMY, + new SingleShotPattern() + )); + Entity enemy = EntitiesFabric.createEnemy( - id, x, y, 2, 50, 50, attackComponent, aiComponent + id, x, y, 2, 50, 50, aiComponent, weapon ); addToAddQueue(enemy); return enemy; @@ -182,7 +182,6 @@ public void toggleDebug() { public GameMap getGameMap() { return gameMap; } public PerformanceMonitor getProfiler() { return profiler; } - public Entity getPlayer() { return player; } public GameData getGameData() { return gameData; } public int getNextEntityId() { return nextEntityId; diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/logic/ai/astar/PathNode.java b/shared/src/main/java/xyz/samiker/theendlessweave/logic/ai/astar/PathNode.java deleted file mode 100644 index 044e890..0000000 --- a/shared/src/main/java/xyz/samiker/theendlessweave/logic/ai/astar/PathNode.java +++ /dev/null @@ -1,52 +0,0 @@ -package xyz.samiker.theendlessweave.logic.ai.astar; - -import xyz.samiker.theendlessweave.tile.Tile; -import xyz.samiker.theendlessweave.tile.TileType; - -import java.util.Objects; - -@Deprecated -public class PathNode implements Comparable { - public final int gridX; - public final int gridY; - private final Tile tile; - public final TileType type; - - public PathNode parent; - - public int gCost; // Фактическая стоимость от старта - public int hCost; // Эвристическая стоимость до цели - public int fCost; // Общая стоимость: gCost + hCost - - public PathNode(Tile tile) { - this.gridX = tile.getGridX(); - this.gridY = tile.getGridY(); - this.tile = tile; - this.type = tile.getType(); - } - - @Override - public int compareTo(PathNode other) { - if (this.fCost != other.fCost) { - return Integer.compare(this.fCost, other.fCost); - } - return Integer.compare(this.hCost, other.hCost); - } - - public Tile getTile() { - return tile; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - PathNode pathNode = (PathNode) o; - return gridX == pathNode.gridX && gridY == pathNode.gridY; - } - - @Override - public int hashCode() { - return Objects.hash(gridX, gridY); - } -} \ No newline at end of file diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/BeamPattern.java b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/BeamPattern.java new file mode 100644 index 0000000..ca18698 --- /dev/null +++ b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/BeamPattern.java @@ -0,0 +1,43 @@ +package xyz.samiker.theendlessweave.logic.patterns; + +import xyz.samiker.theendlessweave.entities.Entity; +import xyz.samiker.theendlessweave.entities.components.PatternConfig; +import xyz.samiker.theendlessweave.entities.components.PositionComponent; +import xyz.samiker.theendlessweave.entities.components.ProjectileComponent; +import xyz.samiker.theendlessweave.entities.components.WeaponEmitter; +import xyz.samiker.theendlessweave.logic.Game; + +import static xyz.samiker.theendlessweave.utils.Constants.SECONDS_PER_TICK; +import static xyz.samiker.theendlessweave.utils.Constants.TILE_SIZE; + +public class BeamPattern implements IPattern{ + private final PatternConfig config; + + public BeamPattern(PatternConfig config) { + this.config = config; + } + + @Override + public void fire(Game game, Entity owner, PositionComponent pos, double baseAngle, WeaponEmitter stats) { + int length = config.getBulletCount(); + int widthCount = (int) config.getAmplitude(); + double widthStep = config.getSpreadAngle(); + + for (int l = 0; l < length; l++) { + double layerDelay = l * (SECONDS_PER_TICK * 2); + double perpAngle = baseAngle + Math.PI / 2; + + for (int w = 0; w < widthCount; w++) { + double offset = (w - (widthCount - 1) / 2.0) * widthStep * TILE_SIZE; + + double spawnX = pos.x + Math.cos(perpAngle) * offset; + double spawnY = pos.y + Math.sin(perpAngle) * offset; + game.createProjectile(spawnX, spawnY, stats.projectileSpeed, baseAngle) + .getComponent(ProjectileComponent.class) + .init(stats.damage, stats.projectileLifetime, owner, baseAngle, stats.targetType) + .asAccelerating(20.0, 40.0) + .withDelay(layerDelay); + } + } + } +} diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/BurstPattern.java b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/BurstPattern.java new file mode 100644 index 0000000..7952dab --- /dev/null +++ b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/BurstPattern.java @@ -0,0 +1,28 @@ +package xyz.samiker.theendlessweave.logic.patterns; + +import xyz.samiker.theendlessweave.entities.Entity; +import xyz.samiker.theendlessweave.entities.components.PatternConfig; +import xyz.samiker.theendlessweave.entities.components.PositionComponent; +import xyz.samiker.theendlessweave.entities.components.ProjectileComponent; +import xyz.samiker.theendlessweave.entities.components.WeaponEmitter; +import xyz.samiker.theendlessweave.logic.Game; + +public class BurstPattern implements IPattern{ + private final PatternConfig config; + + public BurstPattern(PatternConfig config) { + this.config = config; + } + + @Override + public void fire(Game game, Entity owner, PositionComponent pos, double baseAngle, WeaponEmitter stats) { + for (int i = 0; i < config.getBulletCount(); i++) { + double randomOffset = (Math.random() - 0.5) * 0.17; + double speedVar = stats.projectileSpeed * (0.9 + Math.random() * 0.2); + + game.createProjectile(pos.x, pos.y, speedVar, baseAngle + randomOffset) + .getComponent(ProjectileComponent.class) + .init(stats.damage, stats.projectileLifetime, owner, baseAngle + randomOffset,stats.targetType); + } + } +} diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/CrossWavePattern.java b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/CrossWavePattern.java new file mode 100644 index 0000000..b3e5bcd --- /dev/null +++ b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/CrossWavePattern.java @@ -0,0 +1,37 @@ +package xyz.samiker.theendlessweave.logic.patterns; + +import xyz.samiker.theendlessweave.entities.Entity; +import xyz.samiker.theendlessweave.entities.components.PatternConfig; +import xyz.samiker.theendlessweave.entities.components.PositionComponent; +import xyz.samiker.theendlessweave.entities.components.ProjectileComponent; +import xyz.samiker.theendlessweave.entities.components.WeaponEmitter; +import xyz.samiker.theendlessweave.logic.Game; + +public class CrossWavePattern implements IPattern { + private final PatternConfig config; + + public CrossWavePattern(PatternConfig config) { + this.config = config; + } + @Override + public void fire(Game game, Entity owner, PositionComponent pos, double baseAngle, WeaponEmitter stats) { + int count = config.getBulletCount() / 2; + double spread = config.getSpreadAngle(); + + for (int i = 0; i < count; i++) { + double bulletAngle = (baseAngle - spread / 2) + (spread * i / count); + game.createProjectile(pos.x, pos.y, stats.projectileSpeed, bulletAngle) + .getComponent(ProjectileComponent.class) + .init(stats.damage, stats.projectileLifetime, owner, bulletAngle, stats.targetType) + .asSineWave(50, 2, 0); + } + + for (int i = 0; i < count; i++) { + double bulletAngle = (baseAngle - spread / 2) + (spread * i / count); + game.createProjectile(pos.x, pos.y, stats.projectileSpeed, bulletAngle) + .getComponent(ProjectileComponent.class) + .init(stats.damage, stats.projectileLifetime, owner, bulletAngle, stats.targetType) + .asSineWave(50, 2, Math.PI); + } + } +} diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/FanPattern.java b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/FanPattern.java new file mode 100644 index 0000000..32d142e --- /dev/null +++ b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/FanPattern.java @@ -0,0 +1,30 @@ +package xyz.samiker.theendlessweave.logic.patterns; + +import xyz.samiker.theendlessweave.entities.Entity; +import xyz.samiker.theendlessweave.entities.components.PositionComponent; +import xyz.samiker.theendlessweave.entities.components.ProjectileComponent; +import xyz.samiker.theendlessweave.entities.components.WeaponEmitter; +import xyz.samiker.theendlessweave.logic.Game; + +public class FanPattern implements IPattern { + private final int count; + private final double spreadAngle; + + public FanPattern(int count, double spreadAngle) { + this.count = count; + this.spreadAngle = spreadAngle; + } + + @Override + public void fire(Game game, Entity owner, PositionComponent pos, double baseAngle, WeaponEmitter stats) { + double startAngle = baseAngle - spreadAngle / 2.0; + double angleStep = (count > 1) ? spreadAngle / (count - 1) : 0; + + for (int i = 0; i < count; i++) { + double bulletAngle = startAngle + (i * angleStep); + game.createProjectile(pos.x, pos.y, stats.projectileSpeed, bulletAngle) + .getComponent(ProjectileComponent.class) + .init(stats.damage, stats.projectileLifetime, owner, bulletAngle, stats.targetType); + } + } +} \ No newline at end of file diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/IPattern.java b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/IPattern.java new file mode 100644 index 0000000..b34c3f9 --- /dev/null +++ b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/IPattern.java @@ -0,0 +1,10 @@ +package xyz.samiker.theendlessweave.logic.patterns; + +import xyz.samiker.theendlessweave.entities.Entity; +import xyz.samiker.theendlessweave.entities.components.PositionComponent; +import xyz.samiker.theendlessweave.entities.components.WeaponEmitter; +import xyz.samiker.theendlessweave.logic.Game; + +public interface IPattern { + void fire(Game game, Entity owner, PositionComponent pos, double baseAngle, WeaponEmitter stats); +} \ No newline at end of file diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/RingPattern.java b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/RingPattern.java new file mode 100644 index 0000000..6da42ea --- /dev/null +++ b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/RingPattern.java @@ -0,0 +1,29 @@ +package xyz.samiker.theendlessweave.logic.patterns; + +import xyz.samiker.theendlessweave.entities.Entity; +import xyz.samiker.theendlessweave.entities.components.PatternConfig; +import xyz.samiker.theendlessweave.entities.components.PositionComponent; +import xyz.samiker.theendlessweave.entities.components.ProjectileComponent; +import xyz.samiker.theendlessweave.entities.components.WeaponEmitter; +import xyz.samiker.theendlessweave.logic.Game; + +public class RingPattern implements IPattern { + private final PatternConfig config; + + public RingPattern(PatternConfig config) { + this.config = config; + } + + @Override + public void fire(Game game, Entity owner, PositionComponent pos, double baseAngle, WeaponEmitter stats) { + int count = config.getBulletCount(); + double angleStep = (Math.PI * 2) / count; + + for (int i = 0; i < count; i++) { + double bulletAngle = i * angleStep; + game.createProjectile(pos.x, pos.y, stats.projectileSpeed, bulletAngle) + .getComponent(ProjectileComponent.class) + .init(stats.damage, stats.projectileLifetime, owner, bulletAngle, stats.targetType); + } + } +} \ No newline at end of file diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/SineWavePattern.java b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/SineWavePattern.java new file mode 100644 index 0000000..7ae66e5 --- /dev/null +++ b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/SineWavePattern.java @@ -0,0 +1,34 @@ +package xyz.samiker.theendlessweave.logic.patterns; + +import xyz.samiker.theendlessweave.entities.Entity; +import xyz.samiker.theendlessweave.entities.components.PatternConfig; +import xyz.samiker.theendlessweave.entities.components.PositionComponent; +import xyz.samiker.theendlessweave.entities.components.ProjectileComponent; +import xyz.samiker.theendlessweave.entities.components.WeaponEmitter; +import xyz.samiker.theendlessweave.logic.Game; + +public class SineWavePattern implements IPattern { + private final PatternConfig config; + + public SineWavePattern(PatternConfig config) { + this.config = config; + } + + @Override + public void fire(Game game, Entity owner, PositionComponent pos, double baseAngle, WeaponEmitter stats) { + int count = config.getBulletCount(); + double spread = config.getSpreadAngle(); + double startAngle = baseAngle - spread / 2.0; + double angleStep = (count > 1) ? spread / (count - 1) : 0; + + for (int i = 0; i < count; i++) { + double bulletAngle = startAngle + (i * angleStep); + double phaseOffset = (Math.PI * 2 / count) * i; + + game.createProjectile(pos.x, pos.y, stats.projectileSpeed, bulletAngle) + .getComponent(ProjectileComponent.class) + .init(stats.damage, stats.projectileLifetime, owner, bulletAngle, stats.targetType) + .asSineWave(config.getAmplitude(), config.getFrequency(), phaseOffset); + } + } +} \ No newline at end of file diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/SingleShotPattern.java b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/SingleShotPattern.java new file mode 100644 index 0000000..d9a236d --- /dev/null +++ b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/SingleShotPattern.java @@ -0,0 +1,16 @@ +package xyz.samiker.theendlessweave.logic.patterns; + +import xyz.samiker.theendlessweave.entities.Entity; +import xyz.samiker.theendlessweave.entities.components.PositionComponent; +import xyz.samiker.theendlessweave.entities.components.ProjectileComponent; +import xyz.samiker.theendlessweave.entities.components.WeaponEmitter; +import xyz.samiker.theendlessweave.logic.Game; + +public class SingleShotPattern implements IPattern{ + @Override + public void fire(Game game, Entity owner, PositionComponent pos, double baseAngle, WeaponEmitter stats) { + game.createProjectile(pos.x, pos.y, stats.projectileSpeed, baseAngle) + .getComponent(ProjectileComponent.class) + .init(stats.damage, stats.projectileLifetime, owner, baseAngle, stats.targetType); + } +} diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/SpiralPattern.java b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/SpiralPattern.java new file mode 100644 index 0000000..5fec3f1 --- /dev/null +++ b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/SpiralPattern.java @@ -0,0 +1,30 @@ +package xyz.samiker.theendlessweave.logic.patterns; + +import xyz.samiker.theendlessweave.entities.Entity; +import xyz.samiker.theendlessweave.entities.components.PatternConfig; +import xyz.samiker.theendlessweave.entities.components.PositionComponent; +import xyz.samiker.theendlessweave.entities.components.ProjectileComponent; +import xyz.samiker.theendlessweave.entities.components.WeaponEmitter; +import xyz.samiker.theendlessweave.logic.Game; + +public class SpiralPattern implements IPattern { + private final PatternConfig config; + + public SpiralPattern(PatternConfig config) { + this.config = config; + } + + @Override + public void fire(Game game, Entity owner, PositionComponent pos, double baseAngle, WeaponEmitter stats) { + int count = config.getBulletCount(); + double angleStep = (Math.PI * 2) / count; + + for (int i = 0; i < count; i++) { + double phaseOffset = i * angleStep; + game.createProjectile(pos.x, pos.y, stats.projectileSpeed, baseAngle) + .getComponent(ProjectileComponent.class) + .init(stats.damage, stats.projectileLifetime, owner, baseAngle, stats.targetType) + .asSpiral(config.getSpiralRadius(), config.getSpiralSpeed(), phaseOffset); + } + } +} \ No newline at end of file diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/WallPattern.java b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/WallPattern.java new file mode 100644 index 0000000..dac6f99 --- /dev/null +++ b/shared/src/main/java/xyz/samiker/theendlessweave/logic/patterns/WallPattern.java @@ -0,0 +1,35 @@ +package xyz.samiker.theendlessweave.logic.patterns; + +import xyz.samiker.theendlessweave.entities.Entity; +import xyz.samiker.theendlessweave.entities.components.PatternConfig; +import xyz.samiker.theendlessweave.entities.components.PositionComponent; +import xyz.samiker.theendlessweave.entities.components.ProjectileComponent; +import xyz.samiker.theendlessweave.entities.components.WeaponEmitter; +import xyz.samiker.theendlessweave.logic.Game; + +import static xyz.samiker.theendlessweave.utils.Constants.TILE_SIZE; + +public class WallPattern implements IPattern { + private final PatternConfig config; + + public WallPattern(PatternConfig config) { + this.config = config; + } + @Override + public void fire(Game game, Entity owner, PositionComponent pos, double baseAngle, WeaponEmitter stats) { + int count = config.getBulletCount(); + double wallWidth = TILE_SIZE * 10; + double step = wallWidth / count; + double perpAngle = baseAngle + Math.PI / 2; + + for (int i = 0; i < count; i++) { + double offset = (i - count / 2.0) * step; + double spawnX = pos.x + Math.cos(perpAngle) * offset; + double spawnY = pos.y + Math.sin(perpAngle) * offset; + + game.createProjectile(spawnX, spawnY, stats.projectileSpeed, baseAngle) + .getComponent(ProjectileComponent.class) + .init(stats.damage, stats.projectileLifetime, owner, baseAngle, ProjectileComponent.TargetType.PLAYER); + } + } +} diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/settings/ClientSettings.java b/shared/src/main/java/xyz/samiker/theendlessweave/settings/ClientSettings.java index 246a5d4..89fdeb3 100644 --- a/shared/src/main/java/xyz/samiker/theendlessweave/settings/ClientSettings.java +++ b/shared/src/main/java/xyz/samiker/theendlessweave/settings/ClientSettings.java @@ -13,6 +13,8 @@ public enum ClientSettings { MODS_DIR("general.mods_dir", ""), CONTROL("general.control_type", "auto"), //auto, pc, mobile_aim_joystick, mobile_aim_button, mobile_aim_all + JOYSTICK_AIM_DEADZONE("general.control.joystick_aim_deadzone", 30f), + JOYSTICK_MOVE_DEADZONE("general.control.joystick_move_deadzone", 30f), MUSIC_VOLUME("audio.music", 100), SFX_VOLUME("audio.sfx", 100); diff --git a/shared/src/main/java/xyz/samiker/theendlessweave/settings/SettingsManager.java b/shared/src/main/java/xyz/samiker/theendlessweave/settings/SettingsManager.java index 5f69176..edae0ea 100644 --- a/shared/src/main/java/xyz/samiker/theendlessweave/settings/SettingsManager.java +++ b/shared/src/main/java/xyz/samiker/theendlessweave/settings/SettingsManager.java @@ -69,6 +69,16 @@ public static double getDouble(ClientSettings setting) { } } + public static float getFloat(ClientSettings setting) { + String val = properties.getProperty(setting.getKey()); + if (val == null) return (float) setting.getDefaultValue(); + try { + return Float.parseFloat(val); + } catch (NumberFormatException e) { + return (float) setting.getDefaultValue(); + } + } + public static boolean isModEnabled(String modName) { return Boolean.parseBoolean(properties.getProperty("mods.enabled." + modName, "true")); }