diff --git a/engine/tests/pan_test.c b/engine/tests/pan_test.c new file mode 100644 index 0000000..e3dc223 --- /dev/null +++ b/engine/tests/pan_test.c @@ -0,0 +1,104 @@ +/******************************************************************************************* +* +* raylib [2D] example - world space panning +* +* Welcome to raylib! +* +* To test examples, just press F6 and execute raylib_compile_execute script +* Note that compiled executable is placed in the same folder as .c file +* +* You can find all basic examples on C:\raylib\raylib\examples folder or +* raylib official webpage: www.raylib.com +* +* Enjoy using raylib. :) +* +* This example has been created using raylib 3.5 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2013-2016 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" + +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [2d] example - world space panning"); + + Camera2D cam = { 0 }; + cam.zoom = 1; + cam.offset.x = GetScreenWidth() / 2.0f; + cam.offset.y = GetScreenHeight() / 2.0f; + + Vector2 prevMousePos = GetMousePosition(); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + float mouseDelta = GetMouseWheelMove(); + + float newZoom = cam.zoom + mouseDelta * 0.01f; + if (newZoom <= 0) + newZoom = 0.01f; + + cam.zoom = newZoom; + + Vector2 thisPos = GetMousePosition(); + + Vector2 delta = Vector2Subtract(prevMousePos, thisPos); + prevMousePos = thisPos; + + if (IsMouseButtonDown(0)) + cam.target = GetScreenToWorld2D(Vector2Add(cam.offset, delta),cam); + + if (IsKeyPressed(KEY_LEFT)) + cam.rotation += 10; + else if (IsKeyPressed(KEY_RIGHT)) + cam.rotation -= 10; + + BeginDrawing(); + + ClearBackground(RAYWHITE); + + BeginMode2D(cam); + + float size = 5000; + + for (float i = -size; i <= size; i += 10) + { + DrawLine(i, -size, i, size, GRAY); + DrawLine(-size, i, size, i, GRAY); + } + + DrawLine(-size, 0, size, 0, RED); + DrawLine(0, -size, 0, size, RED); + + Vector2 mapGrid = GetScreenToWorld2D(GetMousePosition(), cam); + mapGrid.x = floorf(mapGrid.x / 10) * 10.0f; + mapGrid.y = floorf(mapGrid.y / 10) * 10.0f; + + DrawRectangle(mapGrid.x, mapGrid.y, 10, 10, BLUE); + + EndMode2D(); + + DrawText(TextFormat("%4.0f %4.0f", mapGrid.x, mapGrid.y),10, 10, 20, BLACK); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/game/input.h b/game/input.h new file mode 100644 index 0000000..3fdef74 --- /dev/null +++ b/game/input.h @@ -0,0 +1,125 @@ +#ifndef PIXELDEFENSE_GAME_INPUT_H +#define PIXELDEFENSE_GAME_INPUT_H + +#include +#include + +#include "components.h" + + +typedef enum InputType { + INPUT_NONE, + INPUT_BUILDING, + INPUT_SELECTED_UNITS, + INPUT_SELECTED_OBJECT, + INPUT_SELECTED_BUILDING, +} InputType; + +#define MOUSE_BUTTON_COUNT (MOUSE_BUTTON_BACK + 1) + +#define CLICK_LIMIT 0.2f +#define BUTTON_COUNT (MOUSE_BUTTON_BACK + 1) + +typedef struct InputMapping { + MouseButton primaryBtn; + MouseButton secondaryBtn; + //MouseButton mmb; + + MouseButton backBtn; + MouseButton moveLeft; + MouseButton moveRight; + MouseButton moveUp; + MouseButton moveDown; +} InputMapping; + +typedef struct InputState { + InputType state; + InputMapping mapping; + // Common + Vector2 mouseDown; + Vector2 mouseDownWorld; + Vector2 mouse; + Vector2 mouseWorld; + f32 mouseDownElapsed[BUTTON_COUNT]; + // INPUT_BUILDING + int building; + bool buildingCanPlace; + TilePosition buildingPos; + TileSize buildingSize; + // Units + Rectangle pickArea; + + struct { + /* Selected units + * 1: Position + * 2: Size + * 3: UnitSelected + */ + ecs_query_t *selected; + //ecs_query_t *selectedBuilding; + } queries; + Position *unitPositions; + // SELECTED_OBJECT + // SELECTED_BUILDING +} InputState; + +static InputMapping inputDefaultMapping() { + return (InputMapping) { + .primaryBtn = MOUSE_BUTTON_LEFT, + .secondaryBtn = MOUSE_BUTTON_RIGHT, + + .backBtn = KEY_ESCAPE, + .moveLeft = KEY_W, + .moveRight = KEY_D, + .moveUp = KEY_UP, + .moveDown = KEY_S + }; +} + +static void updateInputState(InputState *state, const Camera2D camera, const f32 dt) { + for (i32 i = 0; i < BUTTON_COUNT; i++) { + f32 *elapsed = &state->mouseDownElapsed[i]; + if (IsMouseButtonDown(i)) + *elapsed += dt; + else if (IsMouseButtonUp(i) && + !IsMouseButtonReleased(i)) + *elapsed = 0; + } + + const Vector2 mouse = GetMousePosition(); + const Vector2 mouseWorld = GetScreenToWorld2D(mouse, camera); + + if (IsMouseButtonPressed(state->mapping.primaryBtn) || + IsMouseButtonPressed(state->mapping.secondaryBtn)) { + state->mouseDown = mouse; + state->mouseDownWorld = mouseWorld; + } + state->mouse = mouse; + state->mouseWorld = mouseWorld; +} + +static bool isInputBtnDragged(const InputState *state, const MouseButton btn) { + return IsMouseButtonDown(btn) && state->mouseDownElapsed[btn] > CLICK_LIMIT; +} +static bool isInputBtnJustDragged(const InputState *state, const MouseButton btn) { + return IsMouseButtonReleased(btn) && state->mouseDownElapsed[btn] > CLICK_LIMIT; +} +static bool isInputBtnJustDown(const InputState *state, const MouseButton btn) { + BZ_UNUSED(state); + return IsMouseButtonPressed(btn); +} +static bool isInputBtnJustUp(const InputState *state, const MouseButton btn) { + return IsMouseButtonReleased(btn) && state->mouseDownElapsed[btn] <= CLICK_LIMIT; +} +static bool isInputBtnDown(const InputState *state, const MouseButton btn) { + BZ_UNUSED(state); + return IsMouseButtonDown(btn); +} +static bool isInputBtnUp(const InputState *state, const MouseButton btn) { + BZ_UNUSED(state); + return IsMouseButtonUp(btn); +} + +extern ECS_COMPONENT_DECLARE(InputState); // defined in main.c + +#endif //PIXELDEFENSE_GAME_INPUT_H diff --git a/game/systems_input.c b/game/systems_input.c index 115f178..b6d11d7 100644 --- a/game/systems_input.c +++ b/game/systems_input.c @@ -55,7 +55,43 @@ void inputPrimaryAction(Game *game, InputState *input) { if (selectedCount == 0) input->state = INPUT_NONE; } -void inputUnitAction(Game *game, InputState *input) { +void inputUnitAction(Game *game, const InputState *input) { + ecs_query_t *query = input->queries.selected; + BzTileMap *map = &game->map; + const i32 numUnits = ecs_query_entity_count(query); + BZ_ASSERT(numUnits > 0); + + const MouseButton actionBtn = input->mapping.secondaryBtn; + + if (isInputBtnJustDown(input, actionBtn)) { + // Note: We mustn't use ecs ecs_remove_all since this will also + // remove ongoing paths that are not part of this query. + iterateSelectedUnits(input->queries.selected, iterRemovePaths); + + const Position target = input->mouseWorld; + + ecs_iter_t it = ecs_query_iter(ECS, query); + ecs_defer_begin(ECS); + while (ecs_iter_next(&it)) { + Position *pos = ecs_field(&it, Position, 1); + for (i32 i = 0; i < it.count; i++) { + const ecs_entity_t entity = it.entities[i]; + + Path path = {NULL, 0}; + pathfindAStar(&(PathfindingDesc) { + .start = pos[i], + .target = target, + .map = map, + .outPath = &path, + .pool = game->pools.pathData, + .alloc = &game->stackAlloc + }); + if (!path.paths) continue; + ecs_set_ptr(ECS, entity, Path, &path); + } + } + ecs_defer_end(ECS); + } } @@ -113,6 +149,10 @@ void updatePlayerInput() { case INPUT_SELECTED_UNITS: { inputPrimaryAction(game, input); if (input->state != INPUT_SELECTED_UNITS) break; + inputUnitAction(game, input); + + /* + i32 selectedCount = ecs_query_entity_count(input->queries.selected); if (selectedCount > 1 && isInputBtnJustDragged(input, secondaryBtn)) { // TODO: For click it should just move them @@ -127,7 +167,6 @@ void updatePlayerInput() { i32 unitPosIdx = 0; ecs_defer_begin(ECS); - iterateSelectedUnits(input->queries.selected, iterRemovePaths); ecs_defer_end(ECS); ecs_iter_t it = ecs_query_iter(ECS, input->queries.selected); @@ -185,6 +224,7 @@ void updatePlayerInput() { } ecs_defer_end(ECS); } + */ break; } case INPUT_SELECTED_OBJECT: {