From 9eea473bef2cdf707a9c5fc1f27ef0f2f8a99eb9 Mon Sep 17 00:00:00 2001 From: devZoGok Date: Mon, 31 Mar 2025 19:47:05 +0300 Subject: [PATCH] placeable unit order buttons working unit ability buttons --- Assets/Scripts/Core/player.lua | 11 ++- Assets/Scripts/GameObjects/Units/unitData.lua | 23 +++++- Assets/Scripts/Gui/main.lua | 4 +- Assets/Scripts/Gui/unitGui.lua | 76 +++++++++++++++---- CMakeLists.txt | 2 +- activeStateButton.cpp | 20 +---- concreteGuiManager.cpp | 13 +++- concreteGuiManager.h | 2 + orderButton.cpp | 43 +++++++++++ orderButton.h | 16 ++++ stateToggleButton.cpp | 40 ++++++++++ stateToggleButton.h | 15 ++++ unit.h | 3 +- 13 files changed, 221 insertions(+), 47 deletions(-) create mode 100644 orderButton.cpp create mode 100644 orderButton.h create mode 100644 stateToggleButton.cpp create mode 100644 stateToggleButton.h diff --git a/Assets/Scripts/Core/player.lua b/Assets/Scripts/Core/player.lua index bb3fd9d..b92186e 100644 --- a/Assets/Scripts/Core/player.lua +++ b/Assets/Scripts/Core/player.lua @@ -11,7 +11,6 @@ Player.taskForceData = { Player.taskForces = {} ---TODO use enum-like values instead of literals for order types --TODO simplify building construction function Player:buildFort(arguments) forts = self:getUnitsByClass(UnitClass.FORT, 1) @@ -47,7 +46,7 @@ function Player:buildFort(arguments) angle = self.baseDir:getAngleBetween(Vector3:new(0, 0, 1)) * (right and -1 or 1) fort = GameObjectFactory.createUnit(self, UnitId.FORT, sp, Quaternion:new(angle, Vector3:new(0, 1, 0)), 0) self:addUnit(fort) - self:issueOrder(1, Vector3:new(0, 0, 0), {Target:new(fort, Vector3:new(0, 0, 0))}, false) + self:issueOrder(OrderType.BUILD, Vector3:new(0, 0, 0), {Target:new(fort, Vector3:new(0, 0, 0))}, false) return BTNodeResult.RUNNING end @@ -85,7 +84,7 @@ function Player:buildStructure(engineer, buildingId, buildPos, buildAngle) building = GameObjectFactory.createUnit(self, buildingId, buildPos, Quaternion:new(1, 0, 0, 0), 0) self:addUnit(building) - self:issueOrder(1, Vector3:new(0, 0, 0), {Target:new(building, Vector3:new(0, 0, 0))}, false) + self:issueOrder(OrderType.BUILD, Vector3:new(0, 0, 0), {Target:new(building, Vector3:new(0, 0, 0))}, false) end building = self:getUnitsByClass(unitClass, 1)[1] @@ -156,7 +155,7 @@ function Player:startHarvesting() self:deselectUnits() self:selectUnits({harvesters[1]}) - self:issueOrder(7, Vector3:new(0, 0, 0), {Target:new(extractor, Vector3:new(0, 0, 0))}, false) + self:issueOrder(OrderType.SUPPLY, Vector3:new(0, 0, 0), {Target:new(extractor, Vector3:new(0, 0, 0))}, false) return BTNodeResult.SUCCESS end @@ -259,7 +258,7 @@ function Player:clearNearEnemies(arguments) if not taskForce.clearing and targUnit then self:deselectUnits() self:selectUnits(taskForce.units) - self:issueOrder(2, Vector3:new(0, 0, 0), {Target:new(targUnit, Vector3:new(0, 0, 0))}, false) + self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(targUnit, Vector3:new(0, 0, 0))}, false) self.taskForces[arguments.tfId].clearing = true elseif taskForce.clearing and not targUnit then @@ -281,7 +280,7 @@ function Player:attackSpawnPoint(arguments) for i = 1, #taskForce.units do taskForce.units[i]:setState(0) end enemySpawnPoint = Map.getSingleton():getSpawnPoint(taskForce.spawnPointId) - self:issueOrder(2, Vector3:new(0, 0, 0), {Target:new(nil, enemySpawnPoint)}, false) + self:issueOrder(OrderType.MOVE, Vector3:new(0, 0, 0), {Target:new(nil, enemySpawnPoint)}, false) self.taskForces[arguments.tfId].attacking = true return BTNodeResult.RUNNING diff --git a/Assets/Scripts/GameObjects/Units/unitData.lua b/Assets/Scripts/GameObjects/Units/unitData.lua index 459f70a..f7808fe 100644 --- a/Assets/Scripts/GameObjects/Units/unitData.lua +++ b/Assets/Scripts/GameObjects/Units/unitData.lua @@ -1,4 +1,15 @@ ---TYPE = {ATTACK = 0, BUILD = 1, MOVE = 2, GARRISON = 3, EJECT = 4, PATROL = 5, LAUNCH = 6}; +OrderType = { + HALT = -1, + ATTACK = 0, + BUILD = 1, + MOVE = 2, + GARRISON = 3, + EJECT = 4, + PATROL = 5, + LAUNCH = 6, + SUPPLY = 7, + HACK = 8 +} UnitId = { WAR_MECH = 0, TANK = 1, @@ -263,6 +274,7 @@ units = { {id = UnitId.EXTRACTOR, buildable = true, trigger = 69}, {id = UnitId.REFINERY, buildable = true, trigger = 82}, }, + abilityButtons = {{buttonType = ButtonType.ORDER, name = 'Hack', orderType = OrderType.HACK}}, unitClass = UnitClass.ENGINEER, unitType = UnitType.HOVER, armor = {ArmorType.MECHANIC}, @@ -818,6 +830,12 @@ units = { { unitClass = UnitClass.TRADE_CENTER, unitType = UnitType.LAND, + abilityButtons = { + {buttonType = ButtonType.BUY_REFINEDS, name = 'Buy ref', trigger = 73}, + {buttonType = ButtonType.SELL_REFINEDS, name = 'Sell ref', trigger = 79}, + {buttonType = ButtonType.BUY_RESEARCH, name = 'Buy rsch', trigger = 75}, + {buttonType = ButtonType.SELL_RESEARCH, name = 'Sell rsch', trigger = 76} + }, isVehicle = false, health = 500, buildTime = 1000, @@ -830,13 +848,13 @@ units = { basePath = PATH .. structurePrefix .. 'TradeCenters/', meshPath = 'tradeCenter.xml', albedoPath = 'tradeCenter.jpg', - --guiScreen = 'tradingCenterCommands.lua', selectionSfx = PATH .. 'Sounds/Units/Sample/selection.ogg', deathSfx = PATH .. 'Sounds/SFX/Explosions/explosion01.ogg', }, { unitClass = UnitClass.LAB, unitType = UnitType.LAND, + abilityButtons = {{buttonType = ButtonType.ACTIVE_GAME_STATE, name = 'Tech tree', guiScreen = 'techTree.lua'}}, isVehicle = false, health = 500, buildTime = 1000, @@ -852,7 +870,6 @@ units = { basePath = PATH .. structurePrefix .. 'Labs/', meshPath = 'lab2.xml', albedoPath = 'lab.jpg', - --guiScreen = 'researchStructCommands.lua', selectionSfx = PATH .. 'Sounds/Units/Sample/selection.ogg', deathSfx = PATH .. 'Sounds/SFX/Explosions/explosion01.ogg', }, diff --git a/Assets/Scripts/Gui/main.lua b/Assets/Scripts/Gui/main.lua index 0225aac..5664fdb 100644 --- a/Assets/Scripts/Gui/main.lua +++ b/Assets/Scripts/Gui/main.lua @@ -36,7 +36,9 @@ ButtonType = { TRADING_SCREEN = 32, TRADE_OFFER = 33, RESOURCE_AMMOUNT = 34, - MINIMAP = 35 + UNIT_STATE = 35, + ORDER = 36, + MINIMAP = 37, } ListboxType = { diff --git a/Assets/Scripts/Gui/unitGui.lua b/Assets/Scripts/Gui/unitGui.lua index a3a5845..db278e4 100644 --- a/Assets/Scripts/Gui/unitGui.lua +++ b/Assets/Scripts/Gui/unitGui.lua @@ -3,44 +3,92 @@ sz = 210 resTextScale = {x = .5, y = .5} buttonInitPos = {x = sz, y = res.y - .75 * sz, z = 0} -buttonSize = {x = 20, y = 20} +buttonSize = {x = 50, y = 50} +buttonSpace = {x = 10, y = 10} function generateButton(numGui, buttonData) - xOffset = math.floor(.5 * (numGui + 1)) * buttonSize.x - yOffset = (numGui + 1) % 2 == 0 and 0 or buttonSize.y + xOffset = math.floor(.5 * numGui) * (buttonSize.x + buttonSpace.x) + yOffset = (numGui + 1) % 2 == 0 and (buttonSize.y + buttonSpace.y) or 0 button = { pos = {x = buttonInitPos.x + xOffset, y = buttonInitPos.y + yOffset, z = .5}, size = buttonSize, guiType = GuiType.BUTTON, + name = buttonData.name, buttonType = buttonData.buttonType, - trigger = buttonData.trigger, - factoryId = buttonData.factoryId + trigger = (buttonData.trigger or -1), + orderType = buttonData.orderType, + factoryId = buttonData.factoryId, + engineerId = buttonData.engineerId, + guiScreen = buttonData.guiScreen, + slotId = buttonData.slotId } return button end function generateGui(unitId) - gui = {} + local gui = {} + vehicleButtonData = { + {buttonType = ButtonType.UNIT_STATE, name = 'Change unit state'}, + {buttonType = ButtonType.ORDER, orderType = OrderType.GARRISON, name = 'Garrison'}, + {buttonType = ButtonType.ORDER, orderType = OrderType.PATROL, name = 'Patrol'}, + {buttonType = ButtonType.ORDER, orderType = OrderType.HALT, name = 'Halt'} + } + structureButtonData = { + {buttonType = ButtonType.UNIT_STATE, name = 'Change unit state'}, + {buttonType = ButtonType.ORDER, orderType = OrderType.HALT, name = 'Halt'} + } - if units[unitId + 1].buildableUnits then - for i = 1, #units[unitId + 1].buildableUnits do - buildUnit = units[unitId + 1].buildableUnits[i] + mainUnit = units[unitId + 1] + unitButtonData = mainUnit.isVehicle and vehicleButtonData or structureButtonData + + if mainUnit.garrisonCapacity then + unitButtonData[#unitButtonData + 1] = { + buttonType = ButtonType.ORDER, + orderType = OrderType.EJECT, + name = 'Eject' + } + end + + for i = 1, #unitButtonData do + gui[#gui + 1] = generateButton(#gui, unitButtonData[i]) + end + + if mainUnit.abilityButtons then + for i = 1, #mainUnit.abilityButtons do + abilButton = mainUnit.abilityButtons[i] + + buttonData = { + trigger = abilButton.trigger, + name = abilButton.name, + buttonType = abilButton.buttonType, + guiScreen = abilButton.guiScreen, + orderType = abilButton.orderType + } + gui[#gui + 1] = generateButton(#gui, buttonData) + end + end + + if mainUnit.buildableUnits then + for i = 1, #mainUnit.buildableUnits do + buildUnit = mainUnit.buildableUnits[i] if not buildUnit.buildable then goto continue end isFactory = ( - buildUnit.unitClass == UnitClass.LAND_FACTORY or - buildUnit.unitClass == UnitClass.NAVAL_FACTORY or - buildUnit.unitClass == UnitClass.FORT + mainUnit.unitClass == UnitClass.LAND_FACTORY or + mainUnit.unitClass == UnitClass.NAVAL_FACTORY or + mainUnit.unitClass == UnitClass.FORT ) buttonData = { trigger = buildUnit.trigger, - name = units[unitId + 1].name, + name = units[buildUnit.id + 1].name, + buttonType = (mainUnit.isVehicle and ButtonType.BUILD or ButtonType.TRAIN), factoryId = (isFactory and unitId or nil), - buttonType = (buildUnit.isVehicle and ButtonType.TRAIN or ButtonType.BUILD) + engineerId = (mainUnit.unitClass == UnitClass.ENGINEER and unitId or nil), + slotId = (ButtonType.BUILD and i - 1 or nil) } gui[#gui + 1] = generateButton(#gui, buttonData) diff --git a/CMakeLists.txt b/CMakeLists.txt index 50058ef..4c6d671 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ set(BUILD_TESTS ON) set(BUILD_SHARED_LIBS OFF CACHE BOOL FORCED) cmake_policy(SET CMP0015 NEW) -set(UNIT_BUTTONS unitButton.cpp tradeButton.cpp buildButton.cpp trainButton.cpp researchButton.cpp) +set(UNIT_BUTTONS unitButton.cpp tradeButton.cpp buildButton.cpp trainButton.cpp researchButton.cpp orderButton.cpp stateToggleButton.cpp) set(OPTIONS_BUTTONS optionsButton.cpp tabButton.cpp okButton.cpp defaultsButton.cpp) set(TRADING_BUTTONS playerTradeButton.cpp tradingScreenButton.cpp offerButton.cpp resourceAmmountButton.cpp) set(MENU_BUTTONS mainMenuButton.cpp singlePlayerButton.cpp exitButton.cpp backButton.cpp playButton.cpp) diff --git a/activeStateButton.cpp b/activeStateButton.cpp index 275feec..529495d 100644 --- a/activeStateButton.cpp +++ b/activeStateButton.cpp @@ -27,24 +27,6 @@ namespace battleship{ } void ActiveStateButton::onClick(){ - ConcreteGuiManager *guiManager = ConcreteGuiManager::getSingleton(); - - vector listboxes{}; - vector checkboxes{}; - vector sliders{}; - vector textboxes{}; - vector guiRects{ - guiManager->getGuiRectangle("refineds"), - guiManager->getGuiRectangle("wealth"), - guiManager->getGuiRectangle("research"), - }; - vector texts{ - guiManager->getText("depth"), - guiManager->getText("refineds"), - guiManager->getText("wealth"), - guiManager->getText("research") - }; - - guiManager->readLuaScreenScript(guiScreen, buttons, listboxes, checkboxes, sliders, textboxes, guiRects, texts); + ConcreteGuiManager::getSingleton()->parseLuaScript(guiScreen); } } diff --git a/concreteGuiManager.cpp b/concreteGuiManager.cpp index b1f87fd..3ef3762 100644 --- a/concreteGuiManager.cpp +++ b/concreteGuiManager.cpp @@ -3,6 +3,7 @@ #include #include "concreteGuiManager.h" +#include "unit.h" #include "gameManager.h" #include "singlePlayerButton.h" #include "mapEditorButton.h" @@ -32,6 +33,8 @@ #include "tradingScreenButton.h" #include "offerButton.h" #include "resourceAmmountButton.h" +#include "orderButton.h" +#include "stateToggleButton.h" #include "minimapButton.h" namespace battleship{ @@ -181,10 +184,10 @@ namespace battleship{ break; } case BUILD: - button = new BuildButton(pos, size, name, (int)guiTable["trigger"], imagePath, (int)SOL_LUA_STATE["UnitId"]["ENGINEER"], guiId); + button = new BuildButton(pos, size, name, (int)guiTable["trigger"], imagePath, (int)guiTable["engineerId"], (int)guiTable["slotId"]); break; case TRAIN: - button = new TrainButton(pos, size, name, (int)guiTable["trigger"], imagePath, (int)guiTable["factoryId"], guiId); + button = new TrainButton(pos, size, name, (int)guiTable["trigger"], imagePath, (int)guiTable["factoryId"], (int)guiTable["slotId"]); break; case STATISTICS: button = new StatsButton(pos, size, name, (int)guiTable["trigger"], imagePath); @@ -226,6 +229,12 @@ namespace battleship{ case RESOURCE_AMMOUNT: button = new ResourceAmmountButton(pos, size, name, (int)guiTable["ammount"], (int)guiTable["trigger"], imagePath); break; + case ORDER: + button = new OrderButton(pos, size, name, (int)guiTable["trigger"], imagePath, (int)guiTable["orderType"]); + break; + case UNIT_STATE: + button = new StateToggleButton(pos, size, name, (int)guiTable["trigger"], imagePath); + break; case MINIMAP:{ string minimapPath = GameManager::getSingleton()->getPath() + "Models/Maps/" + Map::getSingleton()->getMapName() + "/minimap.jpg"; button = new MinimapButton(pos, size, minimapPath); diff --git a/concreteGuiManager.h b/concreteGuiManager.h index a1b85e2..8eaf240 100644 --- a/concreteGuiManager.h +++ b/concreteGuiManager.h @@ -50,6 +50,8 @@ namespace battleship{ TRADING_SCREEN, TRADE_OFFER, RESOURCE_AMMOUNT, + UNIT_STATE, + ORDER, MINIMAP }; enum ListboxType { diff --git a/orderButton.cpp b/orderButton.cpp new file mode 100644 index 0000000..567c0b6 --- /dev/null +++ b/orderButton.cpp @@ -0,0 +1,43 @@ +#include "orderButton.h" +#include "gameManager.h" +#include "activeGameState.h" +#include "player.h" +#include "unit.h" + +#include + +namespace battleship{ + using namespace std; + using namespace vb01; + using namespace vb01Gui; + using namespace gameBase; + + OrderButton::OrderButton(Vector3 pos, Vector2 size, string name, int trigger, string imagePath, int oid) : + Button(pos, size, name, GameManager::getSingleton()->getPath() + "Fonts/batang.ttf", trigger, true, imagePath), + orderId(oid) + {} + + void OrderButton::onClick(){ + Player *player = ((ActiveGameState*)GameManager::getSingleton()->getStateManager()->getAppStateByType(AppStateType::ACTIVE_STATE))->getPlayer(); + vector units = player->getSelectedUnits(); + + if(orderId > -1){ + switch(Order::TYPE(orderId)){ + case Order::TYPE::EJECT: + { + for(Unit *unit : units) + unit->receiveOrder(Order(Order::TYPE::EJECT, vector{}), false); + + break; + } + default: + { + break; + } + } + } + else + for(Unit *unit : units) + unit->halt(); + } +} diff --git a/orderButton.h b/orderButton.h new file mode 100644 index 0000000..fa05fc0 --- /dev/null +++ b/orderButton.h @@ -0,0 +1,16 @@ +#ifndef ORDER_BUTTON_H +#define ORDER_BUTTON_H + +#include + +namespace battleship{ + class OrderButton : public vb01Gui::Button{ + public: + OrderButton(vb01::Vector3, vb01::Vector2, std::string, int, std::string, int); + void onClick(); + private: + int orderId; + }; +} + +#endif diff --git a/stateToggleButton.cpp b/stateToggleButton.cpp new file mode 100644 index 0000000..116a8a4 --- /dev/null +++ b/stateToggleButton.cpp @@ -0,0 +1,40 @@ +#include "stateToggleButton.h" +#include "gameManager.h" +#include "activeGameState.h" +#include "player.h" +#include "unit.h" + +#include + +namespace battleship{ + using namespace std; + using namespace vb01; + using namespace vb01Gui; + using namespace gameBase; + + StateToggleButton::StateToggleButton(Vector3 pos, Vector2 size, string name, int trigger, string imagePath) : + Button(pos, size, name, GameManager::getSingleton()->getPath() + "Fonts/batang.ttf", trigger, true, imagePath) {} + + void StateToggleButton::onClick(){ + const int NUM_STATES = 3; + int numUnitsByState[NUM_STATES]{0, 0, 0}; + + Player *player = ((ActiveGameState*)GameManager::getSingleton()->getStateManager()->getAppStateByType(AppStateType::ACTIVE_STATE))->getPlayer(); + vector units = player->getSelectedUnits(); + + for(Unit *unit : units) + numUnitsByState[(int)unit->getState()]++; + + int maxId = 0, maxNum = numUnitsByState[maxId]; + + for(int i = 0; i < NUM_STATES; i++) + if(maxNum < numUnitsByState[i]){ + maxId = i; + maxNum = numUnitsByState[maxId]; + } + + Unit::State nextState = (maxId + 1 == NUM_STATES ? Unit::State(0) : Unit::State(maxId + 1)); + + for(Unit *unit : units) unit->setState(nextState); + } +} diff --git a/stateToggleButton.h b/stateToggleButton.h new file mode 100644 index 0000000..1303468 --- /dev/null +++ b/stateToggleButton.h @@ -0,0 +1,15 @@ +#ifndef STATE_TOGGLE_BUTTON_H +#define STATE_TOGGLE_BUTTON_H + +#include + +namespace battleship{ + class StateToggleButton : public vb01Gui::Button{ + public: + StateToggleButton(vb01::Vector3, vb01::Vector2, std::string, int, std::string); + void onClick(); + private: + }; +} + +#endif diff --git a/unit.h b/unit.h index a02d90b..ecd2ea5 100755 --- a/unit.h +++ b/unit.h @@ -53,7 +53,7 @@ namespace battleship{ std::vector targets; Order(){} - Order(TYPE t, std::vector targ, vb01::Vector3 dir, int lid = -1, bool pa = true) : type(t), playerAssigned(pa), lineId(lid), targets(targ), direction(dir){} + Order(TYPE t, std::vector targ, vb01::Vector3 dir = vb01::Vector3::VEC_ZERO, int lid = -1, bool pa = true) : type(t), playerAssigned(pa), lineId(lid), targets(targ), direction(dir){} }; enum class MoveDir {LEFT, UP, FORW}; @@ -138,6 +138,7 @@ namespace battleship{ void initLosLight(); void destroyLosLight(); inline void setState(State s){state = s;} + inline State getState(){return state;} inline Engineer* toEngineer(){return (Engineer*)this;} inline Structure* toStructure(){return (Structure*)this;} inline Factory* toFactory(){return (Factory*)this;}