DEADSOFTWARE

Update README
[cavedroid.git] / core / src / ru / deadsoftware / cavedroid / game / GameRenderer.java
1 package ru.deadsoftware.cavedroid.game;
3 import com.badlogic.gdx.Gdx;
4 import com.badlogic.gdx.Input;
5 import com.badlogic.gdx.graphics.Color;
6 import com.badlogic.gdx.graphics.GL20;
7 import com.badlogic.gdx.math.Rectangle;
8 import com.badlogic.gdx.math.Vector2;
9 import com.badlogic.gdx.utils.ObjectMap;
10 import com.badlogic.gdx.utils.TimeUtils;
11 import ru.deadsoftware.cavedroid.MainConfig;
12 import ru.deadsoftware.cavedroid.game.input.IKeyboardInputHandler;
13 import ru.deadsoftware.cavedroid.game.input.IMouseInputHandler;
14 import ru.deadsoftware.cavedroid.game.input.Joystick;
15 import ru.deadsoftware.cavedroid.game.input.action.KeyboardInputAction;
16 import ru.deadsoftware.cavedroid.game.input.action.MouseInputAction;
17 import ru.deadsoftware.cavedroid.game.input.action.keys.MouseInputActionKey;
18 import ru.deadsoftware.cavedroid.game.input.handler.mouse.CursorMouseInputHandler;
19 import ru.deadsoftware.cavedroid.game.input.mapper.KeyboardInputActionMapper;
20 import ru.deadsoftware.cavedroid.game.input.mapper.MouseInputActionMapper;
21 import ru.deadsoftware.cavedroid.game.mobs.MobsController;
22 import ru.deadsoftware.cavedroid.game.mobs.player.Player;
23 import ru.deadsoftware.cavedroid.game.objects.TouchButton;
24 import ru.deadsoftware.cavedroid.game.render.IGameRenderer;
25 import ru.deadsoftware.cavedroid.game.ui.TooltipManager;
26 import ru.deadsoftware.cavedroid.game.ui.windows.GameWindowsManager;
27 import ru.deadsoftware.cavedroid.game.world.GameWorld;
28 import ru.deadsoftware.cavedroid.misc.Assets;
29 import ru.deadsoftware.cavedroid.misc.Renderer;
30 import ru.deadsoftware.cavedroid.misc.utils.MeasureUnitsUtilsKt;
31 import ru.deadsoftware.cavedroid.misc.utils.RenderingUtilsKt;
33 import javax.annotation.CheckForNull;
34 import javax.inject.Inject;
35 import java.util.ArrayList;
36 import java.util.Comparator;
37 import java.util.List;
38 import java.util.Set;
40 @GameScope
41 public class GameRenderer extends Renderer {
43 private static final float CAMERA_SPEED = 72f;
44 private static final float MAX_CAM_DISTANCE_FROM_PLAYER = 64f;
45 private static final float DRAG_THRESHOLD = 1f;
46 private static final TouchButton nullButton = new TouchButton(null, -1, true);
48 private final MainConfig mMainConfig;
49 private final MobsController mMobsController;
50 private final GameWorld mGameWorld;
51 private final List<IGameRenderer> mRenderers;
52 private final CursorMouseInputHandler mCursorMouseInputHandler;
53 private final MouseInputActionMapper mMouseInputActionMapper;
54 private final KeyboardInputActionMapper mKeyboardInputActionMapper;
55 private final Set<IMouseInputHandler> mMouseInputHandlers;
56 private final Set<IKeyboardInputHandler> mKeyboardInputHandlers;
57 private final GameWindowsManager mGameWindowsManager;
58 private final TooltipManager mTooltipManager;
60 private final TouchButton mouseLeftTouchButton, mouseRightTouchButton;
62 private final Vector2 mCamCenterToPlayer = new Vector2();
64 private float mTouchDownX, mTouchDownY;
65 private long mCameraDelayMs = 0L;
67 @Inject
68 GameRenderer(MainConfig mainConfig,
69 MobsController mobsController,
70 GameWorld gameWorld,
71 Set<IGameRenderer> renderers,
72 CursorMouseInputHandler cursorMouseInputHandler,
73 MouseInputActionMapper mouseInputActionMapper,
74 KeyboardInputActionMapper keyboardInputActionMapper,
75 Set<IMouseInputHandler> mouseInputHandlers,
76 Set<IKeyboardInputHandler> keyboardInputHandlers,
77 GameWindowsManager gameWindowsManager,
78 TooltipManager tooltipManager) {
79 super(mainConfig.getWidth(), mainConfig.getHeight());
81 mMainConfig = mainConfig;
82 mMobsController = mobsController;
83 mGameWorld = gameWorld;
84 mRenderers = new ArrayList<>(renderers);
85 mRenderers.sort(Comparator.comparingInt(IGameRenderer::getRenderLayer));
86 mCursorMouseInputHandler = cursorMouseInputHandler;
87 mMouseInputActionMapper = mouseInputActionMapper;
88 mKeyboardInputActionMapper = keyboardInputActionMapper;
89 mMouseInputHandlers = mouseInputHandlers;
90 mKeyboardInputHandlers = keyboardInputHandlers;
91 mGameWindowsManager = gameWindowsManager;
92 mTooltipManager = tooltipManager;
94 mouseLeftTouchButton = new TouchButton(new Rectangle(getWidth() / 2, 0f, getWidth() / 2, getHeight() / 2), Input.Buttons.LEFT, true);
95 mouseRightTouchButton = new TouchButton(new Rectangle(getWidth() / 2, getHeight() / 2, getWidth() / 2, getHeight() / 2), Input.Buttons.RIGHT, true);
97 mMainConfig.setJoystick(new Joystick(mMobsController.getPlayer().getSpeed()));
99 Gdx.gl.glClearColor(0f, .6f, .6f, 1f);
102 private void updateDynamicCameraPosition(float delta) {
103 Player player = mMobsController.getPlayer();
105 float plTargetX = player.getX() + player.getWidth() / 2;
106 float plTargetY = player.getY() + player.getHeight() / 2;
108 float camCenterX = getCamX() + getWidth() / 2;
109 float camCenterY = getCamY() + getHeight() / 2;
111 float camTargetX, camTargetY;
113 boolean followPlayer = player.controlMode == Player.ControlMode.WALK || !mMainConfig.isTouch();
115 if (followPlayer) {
116 camTargetX = plTargetX + Math.min(player.getVelocity().x * 2, getWidth() / 2);
117 camTargetY = plTargetY + player.getVelocity().y;
118 } else {
119 camTargetX = MeasureUnitsUtilsKt.getPx(player.cursorX) + MeasureUnitsUtilsKt.getPx(1) / 2;
120 camTargetY = MeasureUnitsUtilsKt.getPx(player.cursorY) + MeasureUnitsUtilsKt.getPx(1) / 2;
123 Vector2 moveVector = new Vector2(camTargetX - camCenterX, camTargetY - camCenterY);
125 if (followPlayer && player.getVelocity().isZero()) {
126 mCameraDelayMs = TimeUtils.millis();
127 mCamCenterToPlayer.x = plTargetX - camCenterX;
128 mCamCenterToPlayer.y = plTargetY - camCenterY;
131 if (TimeUtils.timeSinceMillis(mCameraDelayMs) < 500L && !player.getVelocity().isZero()) {
132 updateStaticCameraPosition(plTargetX - mCamCenterToPlayer.x,
133 camCenterY + moveVector.y * delta * 2);
134 return;
137 float camX = getCamX();
138 float camY = getCamY();
139 float worldWidth = MeasureUnitsUtilsKt.getPx(mGameWorld.getWidth()) - getWidth() / 2;
141 if (moveVector.x >= worldWidth) {
142 camX += mGameWorld.getWidthPx();
143 moveVector.x -= mGameWorld.getWidthPx();
144 } else if (moveVector.x <= -worldWidth) {
145 camX -= mGameWorld.getWidthPx();
146 moveVector.x += mGameWorld.getWidthPx();
149 setCamPos(camX + moveVector.x * delta * 2, camY + moveVector.y * delta * 2);
152 camX = getCamX();
153 camY = getCamY();
155 if (camX + getWidth() / 2 > plTargetX + MAX_CAM_DISTANCE_FROM_PLAYER) {
156 camX = plTargetX + MAX_CAM_DISTANCE_FROM_PLAYER - getWidth() / 2;
159 if (camY + getHeight() / 2 > plTargetY + MAX_CAM_DISTANCE_FROM_PLAYER) {
160 camY = plTargetY + MAX_CAM_DISTANCE_FROM_PLAYER - getHeight() / 2;
163 if (camX + getWidth() / 2 < plTargetX - MAX_CAM_DISTANCE_FROM_PLAYER) {
164 camX = plTargetX - MAX_CAM_DISTANCE_FROM_PLAYER - getWidth() / 2;
167 if (camY + getHeight() / 2 < plTargetY - MAX_CAM_DISTANCE_FROM_PLAYER) {
168 camY = plTargetY - MAX_CAM_DISTANCE_FROM_PLAYER - getHeight() / 2;
171 setCamPos(camX, camY);
174 private void updateStaticCameraPosition(float targetX, float targetY) {
175 setCamPos(targetX - getWidth() / 2, targetY - getHeight() / 2);
178 private void updateStaticCameraPosition() {
179 Player player = mMobsController.getPlayer();
181 updateStaticCameraPosition(player.getX() + player.getWidth() / 2,
182 player.getY() + player.getHeight() / 2);
185 private void updateCameraPosition(float delta) {
186 if (mMainConfig.isUseDynamicCamera()) {
187 updateDynamicCameraPosition(delta);
188 } else {
189 updateStaticCameraPosition();
193 private float transformScreenX(int screenX) {
194 return getWidth() / Gdx.graphics.getWidth() * screenX;
197 private float transformScreenY(int screenY) {
198 return getHeight() / Gdx.graphics.getHeight() * screenY;
201 private void handleMousePosition() {
202 final Rectangle viewport = getCameraViewport();
204 final float screenX = transformScreenX(Gdx.input.getX());
205 final float screenY = transformScreenY(Gdx.input.getY());
207 final MouseInputAction action = new MouseInputAction(
208 screenX,
209 screenY,
210 MouseInputActionKey.None.INSTANCE,
211 viewport);
213 mCursorMouseInputHandler.handle(action);
215 if (!mTooltipManager.getCurrentMouseTooltip().isEmpty()) {
216 RenderingUtilsKt.drawString(spriter, mTooltipManager.getCurrentMouseTooltip(), screenX + 1, screenY + 1, Color.BLACK);
217 RenderingUtilsKt.drawString(spriter, mTooltipManager.getCurrentMouseTooltip(), screenX, screenY, Color.WHITE);
221 private boolean handleMouseAction(@CheckForNull MouseInputAction action) {
222 if (action == null) {
223 return false;
226 boolean anyProcessed = false;
228 for (IMouseInputHandler handler : mMouseInputHandlers) {
229 final boolean conditions = handler.checkConditions(action);
230 if (conditions) {
231 anyProcessed = true;
232 handler.handle(action);
233 break;
235 // anyProcessed = anyProcessed || conditions;
237 return anyProcessed;
240 private boolean onMouseActionEvent(int mouseX, int mouseY, int button, boolean touchUp, int pointer) {
241 @CheckForNull MouseInputAction action = mMouseInputActionMapper
242 .map((float) mouseX, (float) mouseY, getCameraViewport(), button, touchUp, pointer);
243 return handleMouseAction(action);
246 @Override
247 public boolean touchUp(int screenX, int screenY, int pointer, int button) {
248 float touchX = transformScreenX(screenX);
249 float touchY = transformScreenY(screenY);
251 final Joystick joy = mMainConfig.getJoystick();
253 if (mMainConfig.isTouch()) {
254 if (joy != null && joy.getActive() && joy.getPointer() == pointer) {
255 return onMouseActionEvent(screenX, screenY, nullButton.getCode(), true, pointer);
258 TouchButton touchedKey = getTouchedKey(touchX, touchY);
259 if (touchedKey.isMouse()) {
260 return onMouseActionEvent(screenX, screenY, touchedKey.getCode(), true, pointer);
261 } else {
262 return keyUp(touchedKey.getCode());
266 return onMouseActionEvent(screenX, screenY, button, true, pointer);
269 private TouchButton getTouchedKey(float touchX, float touchY) {
270 if (mGameWindowsManager.getCurrentWindowType() != GameUiWindow.NONE) {
271 return nullButton;
273 for (ObjectMap.Entry<String, TouchButton> entry : Assets.guiMap) {
274 TouchButton button = entry.value;
275 if (button.getRect().contains(touchX, touchY)) {
276 return button;
280 if (mouseLeftTouchButton.getRect().contains(touchX, touchY)) {
281 return mouseLeftTouchButton;
284 if (mouseRightTouchButton.getRect().contains(touchX, touchY)) {
285 return mouseRightTouchButton;
288 return nullButton;
291 @Override
292 public boolean touchDown(int screenX, int screenY, int pointer, int button) {
293 float touchX = transformScreenX(screenX);
294 float touchY = transformScreenY(screenY);
296 mTouchDownX = touchX;
297 mTouchDownY = touchY;
299 if (mMainConfig.isTouch()) {
300 TouchButton touchedKey = getTouchedKey(touchX, touchY);
301 if (touchedKey.isMouse()) {
302 return onMouseActionEvent(screenX, screenY, touchedKey.getCode(), false, pointer);
303 } else {
304 return keyDown(touchedKey.getCode());
308 return onMouseActionEvent(screenX, screenY, button, false, pointer);
311 @Override
312 public boolean touchDragged(int screenX, int screenY, int pointer) {
313 float touchX = transformScreenX(screenX);
314 float touchY = transformScreenY(screenY);
316 if (Math.abs(touchX - mTouchDownX) < 16 && Math.abs(touchY - mTouchDownY) < DRAG_THRESHOLD) {
317 return false;
320 @CheckForNull MouseInputAction action =
321 mMouseInputActionMapper.mapDragged(screenX, screenY, getCameraViewport(), pointer);
322 return handleMouseAction(action);
325 @Override
326 public boolean scrolled(float amountX, float amountY) {
327 @CheckForNull MouseInputAction action = mMouseInputActionMapper
328 .mapScrolled(Gdx.input.getX(), Gdx.input.getY(), amountX, amountY, getCameraViewport());
329 return handleMouseAction(action);
332 private boolean handleKeyboardAction(int keycode, boolean isKeyDown) {
333 @CheckForNull final KeyboardInputAction action = mKeyboardInputActionMapper
334 .map(keycode, isKeyDown);
336 if (action == null) {
337 return false;
340 boolean anyProcessed = false;
342 for (IKeyboardInputHandler handler : mKeyboardInputHandlers) {
343 final boolean conditions = handler.checkConditions(action);
344 if (conditions) {
345 anyProcessed = true;
346 handler.handle(action);
347 break;
351 return anyProcessed;
354 @Override
355 public boolean keyDown(int keycode) {
356 return handleKeyboardAction(keycode, true);
359 @Override
360 public boolean keyUp(int keycode) {
361 return handleKeyboardAction(keycode, false);
364 @Override
365 public void render(float delta) {
366 updateCameraPosition(delta);
368 if (mMainConfig.getJoystick() != null && mMainConfig.getJoystick().getActive()) {
369 mMainConfig.getJoystick().updateState(
370 transformScreenX(Gdx.input.getX(mMainConfig.getJoystick().getPointer())),
371 transformScreenY(Gdx.input.getY(mMainConfig.getJoystick().getPointer()))
372 );
375 Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
377 spriter.begin();
378 mRenderers.forEach(iGameRenderer -> iGameRenderer.draw(spriter, shaper, getCameraViewport(), delta));
379 handleMousePosition();
380 spriter.end();