From e9847750c1bfef2f216265a4900bf4ff3a14a1ab Mon Sep 17 00:00:00 2001 From: Mark Vejvoda Date: Fri, 12 Mar 2010 07:42:55 +0000 Subject: [PATCH] Disabled in progress code related to unit rotation feature (will re-enabled after current release) --- source/glest_game/gui/gui.cpp | 921 ++++++++++++++++ source/glest_game/type_instances/unit.cpp | 1178 +++++++++++++++++++++ source/glest_game/types/unit_type.cpp | 546 ++++++++++ source/glest_game/world/world.cpp | 736 +++++++++++++ 4 files changed, 3381 insertions(+) create mode 100644 source/glest_game/gui/gui.cpp create mode 100644 source/glest_game/type_instances/unit.cpp create mode 100644 source/glest_game/types/unit_type.cpp create mode 100644 source/glest_game/world/world.cpp diff --git a/source/glest_game/gui/gui.cpp b/source/glest_game/gui/gui.cpp new file mode 100644 index 00000000..d68d61a3 --- /dev/null +++ b/source/glest_game/gui/gui.cpp @@ -0,0 +1,921 @@ +// ============================================================== +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "gui.h" + +#include +#include + +#include "world.h" +#include "renderer.h" +#include "game.h" +#include "upgrade.h" +#include "unit.h" +#include "metrics.h" +#include "display.h" +#include "platform_util.h" +#include "sound_renderer.h" +#include "util.h" +#include "faction.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class Mouse3d +// ===================================================== + +const float Mouse3d::fadeSpeed= 1.f/50.f; + +Mouse3d::Mouse3d(){ + enabled= false; + rot= 0; + fade= 0.f; +} + +void Mouse3d::enable(){ + enabled= true; + fade= 0.f; +} + +void Mouse3d::update(){ + if(enabled){ + rot= (rot + 3) % 360; + fade+= fadeSpeed; + if(fade>1.f) fade= 1.f; + } +} + +// =============================== +// class SelectionQuad +// =============================== + +SelectionQuad::SelectionQuad(){ + enabled= false; + posDown= Vec2i(0); + posUp= Vec2i(0); +} + +void SelectionQuad::setPosDown(const Vec2i &posDown){ + enabled= true; + this->posDown= posDown; + this->posUp= posDown; +} + +void SelectionQuad::setPosUp(const Vec2i &posUp){ + this->posUp= posUp; +} + +void SelectionQuad::disable(){ + enabled= false; +} + +// ===================================================== +// class Gui +// ===================================================== + +//constructor +Gui::Gui(){ + if(Socket::enableDebugText) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__); + + posObjWorld= Vec2i(54, 14); + computeSelection= false; + validPosObjWorld= false; + activeCommandType= NULL; + activeCommandClass= ccStop; + selectingBuilding= false; + selectingPos= false; + selectingMeetingPoint= false; + activePos= invalidPos; + + if(Socket::enableDebugText) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__); +} + +void Gui::init(Game *game){ + this->commander= game->getCommander(); + this->gameCamera= game->getGameCamera(); + this->console= game->getConsole(); + this->world= game->getWorld(); + selection.init(this, world->getThisFactionIndex()); +} + +void Gui::end(){ + selection.clear(); +} + +// ==================== get ==================== + +const UnitType *Gui::getBuilding() const{ + assert(selectingBuilding); + return choosenBuildingType; +} + +// ==================== is ==================== + +bool Gui::isPlacingBuilding() const{ + return isSelectingPos() && activeCommandType!=NULL && activeCommandType->getClass()==ccBuild; +} + +// ==================== set ==================== + +void Gui::invalidatePosObjWorld(){ + validPosObjWorld= false; +} + +void Gui::setComputeSelectionFlag(){ + computeSelection= true; +} + + +// ==================== reset state ==================== + +void Gui::resetState(){ + selectingBuilding= false; + selectingPos= false; + selectingMeetingPoint= false; + activePos= invalidPos; + activeCommandClass= ccStop; + activeCommandType= NULL; +} + +// ==================== events ==================== + +void Gui::update(){ + setComputeSelectionFlag(); + mouse3d.update(); +} + +void Gui::tick(){ + computeDisplay(); +} + +//in display coords +bool Gui::mouseValid(int x, int y){ + return computePosDisplay(x, y) != invalidPos; +} + +void Gui::mouseDownLeftDisplay(int x, int y){ + if(!selectingPos && !selectingMeetingPoint){ + int posDisplay= computePosDisplay(x, y); + if(posDisplay!= invalidPos){ + if(selection.isComandable()){ + if(selectingBuilding){ + + //if(Socket::enableDebugText) printf("In [%s::%s] selectingBuilding == true\n",__FILE__,__FUNCTION__); + + mouseDownDisplayUnitBuild(posDisplay); + } + else{ + //if(Socket::enableDebugText) printf("In [%s::%s] selectingBuilding == false\n",__FILE__,__FUNCTION__); + + mouseDownDisplayUnitSkills(posDisplay); + } + } + else{ + resetState(); + } + } + computeDisplay(); + } +} + +void Gui::mouseMoveDisplay(int x, int y){ + //if(Socket::enableDebugText) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__); + + computeInfoString(computePosDisplay(x, y)); +} + +void Gui::mouseDownLeftGraphics(int x, int y){ + + //if(Socket::enableDebugText) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__); + + if(selectingPos){ + //if(Socket::enableDebugText) printf("In [%s::%s] selectingPos == true\n",__FILE__,__FUNCTION__); + + //give standard orders + giveTwoClickOrders(x, y); + resetState(); + } + //set meeting point + else if(selectingMeetingPoint){ + //if(Socket::enableDebugText) printf("In [%s::%s] selectingMeetingPoint == true\n",__FILE__,__FUNCTION__); + + if(selection.isComandable()){ + + //if(Socket::enableDebugText) printf("In [%s::%s] selection.isComandable() == true\n",__FILE__,__FUNCTION__); + + Vec2i targetPos; + if(Renderer::getInstance().computePosition(Vec2i(x, y), targetPos)){ + + //if(Socket::enableDebugText) printf("In [%s::%s] computePosition() == true\n",__FILE__,__FUNCTION__); + + commander->trySetMeetingPoint(selection.getFrontUnit(), targetPos); + } + } + resetState(); + } + else{ + //if(Socket::enableDebugText) printf("In [%s::%s] selectionQuad()\n",__FILE__,__FUNCTION__); + + selectionQuad.setPosDown(Vec2i(x, y)); + computeSelected(false); + } + computeDisplay(); + + //if(Socket::enableDebugText) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__); +} + +void Gui::mouseDownRightGraphics(int x, int y){ + + //if(Socket::enableDebugText) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__); + + if(selectingPos || selectingMeetingPoint){ + resetState(); + } + else if(selection.isComandable()){ + giveDefaultOrders(x, y); + } + computeDisplay(); + + //if(Socket::enableDebugText) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__); +} + +void Gui::mouseUpLeftGraphics(int x, int y){ + //if(Socket::enableDebugText) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__); + + if(!selectingPos && !selectingMeetingPoint){ + if(selectionQuad.isEnabled()){ + selectionQuad.setPosUp(Vec2i(x, y)); + if(selection.isComandable() && random.randRange(0, 1)==0){ + SoundRenderer::getInstance().playFx( + selection.getFrontUnit()->getType()->getSelectionSound(), + selection.getFrontUnit()->getCurrVector(), + gameCamera->getPos()); + } + selectionQuad.disable(); + } + } + + //if(Socket::enableDebugText) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__); +} + +void Gui::mouseMoveGraphics(int x, int y){ + + //compute selection + if(selectionQuad.isEnabled()){ + selectionQuad.setPosUp(Vec2i(x, y)); + if(computeSelection){ + computeSelection= false; + computeSelected(false); + } + } + + //compute position for building + if(isPlacingBuilding()){ + //if(Socket::enableDebugText) printf("In [%s::%s] isPlacingBuilding() == true\n",__FILE__,__FUNCTION__); + + validPosObjWorld= Renderer::getInstance().computePosition(Vec2i(x,y), posObjWorld); + } + + display.setInfoText(""); +} + +void Gui::mouseDoubleClickLeftGraphics(int x, int y){ + if(!selectingPos && !selectingMeetingPoint){ + selectionQuad.setPosDown(Vec2i(x, y)); + computeSelected(true); + computeDisplay(); + } +} + +void Gui::groupKey(int groupIndex){ + if(isKeyDown(vkControl)){ + selection.assignGroup(groupIndex); + } + else{ + selection.recallGroup(groupIndex); + } +} + +float Gui::getUnitTypeBuildRotation(int unitId) const { + float rotationValue = -1; + + if(unitTypeBuildRotation.find(unitId) != unitTypeBuildRotation.end()) { + rotationValue = unitTypeBuildRotation.find(unitId)->second; + } + + return rotationValue; +} + +void Gui::hotKey(char key){ + + //if(Socket::enableDebugText) printf("In [%s::%s] key = [%c]\n",__FILE__,__FUNCTION__,key); + + if(key==' '){ + centerCameraOnSelection(); + } + else if(key=='I'){ + selectInterestingUnit(iutIdleHarvester); + } + else if(key=='B'){ + selectInterestingUnit(iutBuiltBuilding); + } + else if(key=='R'){ + //!!! + if(0 && isPlacingBuilding()) { + const UnitType *unitType = getBuilding(); + float unitTypeRotation = getUnitTypeBuildRotation(unitType->getId()); + + if(Socket::enableDebugText) printf("In [%s::%s] unitType->getId() = %d unitTypeRotation = %f\n",__FILE__,__FUNCTION__,unitType->getId(),unitTypeRotation); + + if(unitTypeRotation < 0) { + unitTypeRotation = 0; + } + unitTypeRotation += 90; + if(unitTypeRotation >= 360) { + unitTypeRotation = 0; + } + unitTypeBuildRotation[unitType->getId()] = unitTypeRotation; + + if(Socket::enableDebugText) printf("In [%s::%s] unitType->getId() = %d NEW unitTypeRotation = %f\n",__FILE__,__FUNCTION__,unitType->getId(),unitTypeRotation); + } + else { + selectInterestingUnit(iutProducer); + } + } + else if(key=='D'){ + selectInterestingUnit(iutDamaged); + } + else if(key=='T'){ + selectInterestingUnit(iutStore); + } + else if(key=='A'){ + clickCommonCommand(ccAttack); + } + else if(key=='S'){ + clickCommonCommand(ccStop); + } + else if(key=='M'){ + clickCommonCommand(ccMove); + } +} + +void Gui::onSelectionChanged(){ + resetState(); + computeDisplay(); +} + +// ================= PRIVATE ================= + +void Gui::giveOneClickOrders(){ + CommandResult result; + if(selection.isUniform()){ + result= commander->tryGiveCommand(&selection, activeCommandType); + } + else{ + result= commander->tryGiveCommand(&selection, activeCommandClass); + } + addOrdersResultToConsole(activeCommandClass, result); + activeCommandType= NULL; + activeCommandClass= ccStop; +} + +void Gui::giveDefaultOrders(int x, int y){ + + //compute target + const Unit *targetUnit= NULL; + Vec2i targetPos; + if(!computeTarget(Vec2i(x, y), targetPos, targetUnit)){ + console->addStdMessage("InvalidPosition"); + return; + } + + //give order + CommandResult result= commander->tryGiveCommand(&selection, targetPos, targetUnit); + + //graphical result + addOrdersResultToConsole(activeCommandClass, result); + if(result == crSuccess || result == crSomeFailed){ + mouse3d.enable(); + + if(random.randRange(0, 1)==0){ + SoundRenderer::getInstance().playFx( + selection.getFrontUnit()->getType()->getCommandSound(), + selection.getFrontUnit()->getCurrVector(), + gameCamera->getPos()); + } + } + + //reset + resetState(); +} + +void Gui::giveTwoClickOrders(int x, int y){ + + CommandResult result; + + //compute target + const Unit *targetUnit= NULL; + Vec2i targetPos; + if(!computeTarget(Vec2i(x, y), targetPos, targetUnit)){ + console->addStdMessage("InvalidPosition"); + return; + } + + //give orders to the units of this faction + if(!selectingBuilding){ + if(selection.isUniform()){ + result= commander->tryGiveCommand(&selection, activeCommandType, targetPos, targetUnit); + } + else{ + result= commander->tryGiveCommand(&selection, activeCommandClass, targetPos, targetUnit); + } + } + else{ + //selecting building + result= commander->tryGiveCommand( selection.getFrontUnit(), activeCommandType, posObjWorld, choosenBuildingType ); + } + + //graphical result + addOrdersResultToConsole(activeCommandClass, result); + if(result == crSuccess || result == crSomeFailed){ + mouse3d.enable(); + + if(random.randRange(0, 1)==0){ + SoundRenderer::getInstance().playFx( + selection.getFrontUnit()->getType()->getCommandSound(), + selection.getFrontUnit()->getCurrVector(), + gameCamera->getPos()); + } + } +} + +void Gui::centerCameraOnSelection(){ + if(!selection.isEmpty()){ + Vec3f refPos= selection.getRefPos(); + gameCamera->centerXZ(refPos.x, refPos.z); + } +} + +void Gui::selectInterestingUnit(InterestingUnitType iut){ + const Faction* thisFaction= world->getThisFaction(); + const Unit* previousUnit= NULL; + bool previousFound= true; + + //start at the next harvester + if(selection.getCount()==1){ + const Unit* refUnit= selection.getFrontUnit(); + + if(refUnit->isInteresting(iut)){ + previousUnit= refUnit; + previousFound= false; + } + } + + //clear selection + selection.clear(); + + //search + for(int i= 0; igetUnitCount(); ++i){ + Unit* unit= thisFaction->getUnit(i); + + if(previousFound){ + if(unit->isInteresting(iut)){ + selection.select(unit); + break; + } + } + else{ + if(unit==previousUnit){ + previousFound= true; + } + } + } + + //search again if we have a previous + if(selection.isEmpty() && previousUnit!=NULL && previousFound==true){ + for(int i= 0; igetUnitCount(); ++i){ + Unit* unit= thisFaction->getUnit(i); + + if(unit->isInteresting(iut)){ + selection.select(unit); + break; + } + } + } +} + +void Gui::clickCommonCommand(CommandClass commandClass){ + for(int i= 0; igetClass()==commandClass) || display.getCommandClass(i)==commandClass){ + mouseDownDisplayUnitSkills(i); + break; + } + } +} + +void Gui::mouseDownDisplayUnitSkills(int posDisplay){ + if(!selection.isEmpty()){ + if(posDisplay != cancelPos){ + if(posDisplay!=meetingPointPos){ + const Unit *unit= selection.getFrontUnit(); + + //uniform selection + if(selection.isUniform()){ + if(unit->getFaction()->reqsOk(display.getCommandType(posDisplay))){ + activeCommandType= display.getCommandType(posDisplay); + activeCommandClass= activeCommandType->getClass(); + } + else{ + posDisplay= invalidPos; + activeCommandType= NULL; + activeCommandClass= ccStop; + return; + } + } + + //non uniform selection + else{ + activeCommandType= NULL; + activeCommandClass= display.getCommandClass(posDisplay); + } + + //give orders depending on command type + if(!selection.isEmpty()){ + const CommandType *ct= selection.getUnit(0)->getType()->getFirstCtOfClass(activeCommandClass); + if(activeCommandType!=NULL && activeCommandType->getClass()==ccBuild){ + assert(selection.isUniform()); + selectingBuilding= true; + } + else if(ct->getClicks()==cOne){ + invalidatePosObjWorld(); + giveOneClickOrders(); + } + else{ + selectingPos= true; + activePos= posDisplay; + } + } + } + else{ + activePos= posDisplay; + selectingMeetingPoint= true; + } + } + else{ + commander->tryCancelCommand(&selection); + } + } +} + +void Gui::mouseDownDisplayUnitBuild(int posDisplay){ + int factionIndex= world->getThisFactionIndex(); + + if(posDisplay==cancelPos){ + resetState(); + } + else{ + if(activeCommandType!=NULL && activeCommandType->getClass()==ccBuild){ + const BuildCommandType *bct= static_cast(activeCommandType); + const UnitType *ut= bct->getBuilding(posDisplay); + if(world->getFaction(factionIndex)->reqsOk(ut)){ + choosenBuildingType= ut; + assert(choosenBuildingType!=NULL); + selectingPos= true;; + activePos= posDisplay; + } + } + } +} + +void Gui::computeInfoString(int posDisplay){ + + Lang &lang= Lang::getInstance(); + + display.setInfoText(""); + if(posDisplay!=invalidPos && selection.isComandable()){ + if(!selectingBuilding){ + if(posDisplay==cancelPos){ + display.setInfoText(lang.get("Cancel")); + } + else if(posDisplay==meetingPointPos){ + display.setInfoText(lang.get("MeetingPoint")); + } + else{ + //uniform selection + if(selection.isUniform()){ + const Unit *unit= selection.getFrontUnit(); + const CommandType *ct= display.getCommandType(posDisplay); + if(ct!=NULL){ + if(unit->getFaction()->reqsOk(ct)){ + display.setInfoText(ct->getDesc(unit->getTotalUpgrade())); + } + else{ + if(ct->getClass()==ccUpgrade){ + const UpgradeCommandType *uct= static_cast(ct); + if(unit->getFaction()->getUpgradeManager()->isUpgrading(uct->getProducedUpgrade())){ + display.setInfoText(lang.get("Upgrading")); + } + else if(unit->getFaction()->getUpgradeManager()->isUpgraded(uct->getProducedUpgrade())){ + display.setInfoText(lang.get("AlreadyUpgraded")); + } + else{ + display.setInfoText(ct->getReqDesc()); + } + } + else{ + display.setInfoText(ct->getReqDesc()); + } + } + } + } + + //non uniform selection + else{ + const UnitType *ut= selection.getFrontUnit()->getType(); + CommandClass cc= display.getCommandClass(posDisplay); + if(cc!=ccNull){ + display.setInfoText(lang.get("CommonCommand") + ": " + ut->getFirstCtOfClass(cc)->toString()); + } + } + } + } + else{ + if(posDisplay==cancelPos){ + display.setInfoText(lang.get("Return")); + } + else{ + if(activeCommandType!=NULL && activeCommandType->getClass()==ccBuild){ + const BuildCommandType *bct= static_cast(activeCommandType); + display.setInfoText(bct->getBuilding(posDisplay)->getReqDesc()); + } + } + } + } +} + +void Gui::computeDisplay(){ + + //init + display.clear(); + + // ================ PART 1 ================ + + //title, text and progress bar + if(selection.getCount()==1){ + display.setTitle(selection.getFrontUnit()->getFullName()); + display.setText(selection.getFrontUnit()->getDesc()); + display.setProgressBar(selection.getFrontUnit()->getProductionPercent()); + } + + //portraits + for(int i=0; igetType()->getImage()); + } + + // ================ PART 2 ================ + + if(selectingPos || selectingMeetingPoint){ + display.setDownSelectedPos(activePos); + } + + if(selection.isComandable()){ + if(!selectingBuilding){ + + //cancel button + const Unit *u= selection.getFrontUnit(); + const UnitType *ut= u->getType(); + if(selection.isCancelable()){ + display.setDownImage(cancelPos, ut->getCancelImage()); + display.setDownLighted(cancelPos, true); + } + + //meeting point + if(selection.isMeetable()){ + display.setDownImage(meetingPointPos, ut->getMeetingPointImage()); + display.setDownLighted(meetingPointPos, true); + } + + if(selection.isUniform()){ + //uniform selection + if(u->isBuilt()){ + int morphPos= 8; + for(int i=0; igetCommandTypeCount(); ++i){ + int displayPos= i; + const CommandType *ct= ut->getCommandType(i); + if(ct->getClass()==ccMorph){ + displayPos= morphPos++; + } + display.setDownImage(displayPos, ct->getImage()); + display.setCommandType(displayPos, ct); + display.setDownLighted(displayPos, u->getFaction()->reqsOk(ct)); + } + } + } + + else{ + //non uniform selection + int lastCommand= 0; + for(int i=0; i(i); + if(isSharedCommandClass(cc) && cc!=ccBuild){ + display.setDownLighted(lastCommand, true); + display.setDownImage(lastCommand, ut->getFirstCtOfClass(cc)->getImage()); + display.setCommandClass(lastCommand, cc); + lastCommand++; + } + } + } + } + else{ + + //selecting building + const Unit *unit= selection.getFrontUnit(); + if(activeCommandType!=NULL && activeCommandType->getClass()==ccBuild){ + const BuildCommandType* bct= static_cast(activeCommandType); + for(int i=0; igetBuildingCount(); ++i){ + display.setDownImage(i, bct->getBuilding(i)->getImage()); + display.setDownLighted(i, unit->getFaction()->reqsOk(bct->getBuilding(i))); + } + display.setDownImage(cancelPos, selection.getFrontUnit()->getType()->getCancelImage()); + display.setDownLighted(cancelPos, true); + } + } + } +} + +int Gui::computePosDisplay(int x, int y){ + int posDisplay= display.computeDownIndex(x, y); + + if(posDisplay<0 || posDisplay>=Display::downCellCount){ + posDisplay= invalidPos; + } + else if(selection.isComandable()){ + if(posDisplay!=cancelPos){ + if(posDisplay!=meetingPointPos){ + if(!selectingBuilding){ + //standard selection + if(display.getCommandClass(posDisplay)==ccNull && display.getCommandType(posDisplay)==NULL){ + posDisplay= invalidPos; + } + } + else{ + //building selection + if(activeCommandType!=NULL && activeCommandType->getClass()==ccBuild){ + const BuildCommandType *bct= static_cast(activeCommandType); + if(posDisplay>=bct->getBuildingCount()){ + posDisplay= invalidPos; + } + } + } + } + else{ + //check meeting point + if(!selection.isMeetable()){ + posDisplay= invalidPos; + } + } + } + else{ + //check cancel button + if(!selection.isCancelable()){ + posDisplay= invalidPos; + } + } + } + else{ + posDisplay= invalidPos; + } + + return posDisplay; +} + +void Gui::addOrdersResultToConsole(CommandClass cc, CommandResult result){ + + switch(result){ + case crSuccess: + break; + case crFailReqs: + switch(cc){ + case ccBuild: + console->addStdMessage("BuildingNoReqs"); + break; + case ccProduce: + console->addStdMessage("UnitNoReqs"); + break; + case ccUpgrade: + console->addStdMessage("UpgradeNoReqs"); + break; + default: + break; + } + break; + case crFailRes: + switch(cc){ + case ccBuild: + console->addStdMessage("BuildingNoRes"); + break; + case ccProduce: + console->addStdMessage("UnitNoRes"); + break; + case ccUpgrade: + console->addStdMessage("UpgradeNoRes"); + break; + default: + break; + } + break; + + case crFailUndefined: + console->addStdMessage("InvalidOrder"); + break; + + case crSomeFailed: + console->addStdMessage("SomeOrdersFailed"); + break; + } +} + +bool Gui::isSharedCommandClass(CommandClass commandClass){ + for(int i=0; igetType()->getFirstCtOfClass(commandClass); + if(ct==NULL || !unit->getFaction()->reqsOk(ct)) + return false; + } + return true; +} + +void Gui::computeSelected(bool doubleClick){ + Selection::UnitContainer units; + Renderer::getInstance().computeSelected(units, selectionQuad.getPosDown(), selectionQuad.getPosUp()); + selectingBuilding= false; + activeCommandType= NULL; + + //select all units of the same type if double click + if(doubleClick && units.size()==1){ + const Unit *refUnit= units.front(); + int factionIndex= refUnit->getFactionIndex(); + for(int i=0; igetFaction(factionIndex)->getUnitCount(); ++i){ + Unit *unit= world->getFaction(factionIndex)->getUnit(i); + if(unit->getPos().dist(refUnit->getPos())getType()==refUnit->getType()) + { + units.push_back(unit); + } + } + } + + bool shiftDown= isKeyDown(vkShift); + bool controlDown= isKeyDown(vkControl); + + if(!shiftDown && !controlDown){ + selection.clear(); + } + + if(!controlDown){ + selection.select(units); + } + else{ + selection.unSelect(units); + } +} + +bool Gui::computeTarget(const Vec2i &screenPos, Vec2i &targetPos, const Unit *&targetUnit){ + Selection::UnitContainer uc; + Renderer &renderer= Renderer::getInstance(); + renderer.computeSelected(uc, screenPos, screenPos); + validPosObjWorld= false; + + if(!uc.empty()){ + targetUnit= uc.front(); + targetPos= targetUnit->getPos(); + return true; + } + else{ + targetUnit= NULL; + if(renderer.computePosition(screenPos, targetPos)){ + validPosObjWorld= true; + posObjWorld= targetPos; + return true; + } + else{ + return false; + } + } +} + +}}//end namespace diff --git a/source/glest_game/type_instances/unit.cpp b/source/glest_game/type_instances/unit.cpp new file mode 100644 index 00000000..988056ac --- /dev/null +++ b/source/glest_game/type_instances/unit.cpp @@ -0,0 +1,1178 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== +#include "faction.h" + +#include + +#include "unit.h" +#include "unit_particle_type.h" +#include "world.h" +#include "upgrade.h" +#include "map.h" +#include "command.h" +#include "object.h" +#include "config.h" +#include "skill_type.h" +#include "core_data.h" +#include "renderer.h" +#include "leak_dumper.h" +//#include "socket.h" + +using namespace Shared::Graphics; +using namespace Shared::Util; + + +namespace Glest{ namespace Game{ + +// ===================================================== +// class UnitPath +// ===================================================== + +const int UnitPath::maxBlockCount= 10; + +bool UnitPath::isEmpty(){ + return pathQueue.empty(); +} + +bool UnitPath::isBlocked(){ + return blockCount>=maxBlockCount; +} + +void UnitPath::clear(){ + pathQueue.clear(); + blockCount= 0; +} + +void UnitPath::incBlockCount(){ + pathQueue.clear(); + blockCount++; +} + +void UnitPath::push(const Vec2i &path){ + pathQueue.push_back(path); +} + +Vec2i UnitPath::pop(){ + Vec2i p= pathQueue.front(); + pathQueue.erase(pathQueue.begin()); + return p; +} + +// ===================================================== +// class UnitReference +// ===================================================== + +UnitReference::UnitReference(){ + id= -1; + faction= NULL; +} + +void UnitReference::operator=(const Unit *unit){ + if(unit==NULL){ + id= -1; + faction= NULL; + } + else{ + id= unit->getId(); + faction= unit->getFaction(); + } +} + +Unit *UnitReference::getUnit() const{ + if(faction!=NULL){ + return faction->findUnit(id); + } + return NULL; +} + +// ===================================================== +// class Unit +// ===================================================== + +const float Unit::speedDivider= 100.f; +const int Unit::maxDeadCount= 500; //time in until the corpse disapears +const float Unit::highlightTime= 0.5f; +const int Unit::invalidId= -1; + +// ============================ Constructor & destructor ============================= + +Unit::Unit(int id, const Vec2i &pos, const UnitType *type, Faction *faction, Map *map, float unitPlacementRotation) { + + //if(Socket::enableDebugText) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__); + + Random random; + + this->pos=pos; + this->type=type; + this->faction=faction; + this->map= map; + this->id= id; + level= NULL; + cellMap= NULL; + + //if(Socket::enableDebugText) printf("In [%s::%s] A\n",__FILE__,__FUNCTION__); + setRotateAmount(unitPlacementRotation); + //if(Socket::enableDebugText) printf("In [%s::%s] B unit id = %d [%s] rotate amount = %f\n",__FILE__,__FUNCTION__,getId(), getFullName().c_str(),unitPlacementRotation); + + Config &config= Config::getInstance(); + showUnitParticles= config.getBool("UnitParticles"); + + lastPos= pos; + progress= 0; + lastAnimProgress= 0; + animProgress= 0; + progress2= 0; + kills= 0; + loadCount= 0; + ep= 0; + deadCount= 0; + hp= type->getMaxHp()/20; + toBeUndertaken= false; + + highlight= 0.f; + meetingPos= pos; + alive= true; + + float rot= 0.f; + + random.init(id); + rot+= random.randRange(-5, 5); + + rotation= rot; + lastRotation= rot; + targetRotation= rot; + + if(getType()->getField(fAir)) currField=fAir; + if(getType()->getField(fLand)) currField=fLand; + + fire= NULL; + + computeTotalUpgrade(); + + //starting skill + this->currSkill=getType()->getFirstStOfClass(scStop); + + //if(Socket::enableDebugText) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__); +} + +Unit::~Unit(){ + //remove commands + while(!commands.empty()){ + delete commands.back(); + commands.pop_back(); + } + // fade(and by this remove) all unit particle systems + while(!unitParticleSystems.empty()){ + unitParticleSystems.back()->fade(); + unitParticleSystems.pop_back(); + } + stopDamageParticles(); + + delete [] cellMap; + cellMap = NULL; +} + +// ====================================== get ====================================== + +int Unit::getFactionIndex() const{ + return faction->getIndex(); +} + +int Unit::getTeam() const{ + return faction->getTeam(); +} + +Vec2i Unit::getCenteredPos() const{ + return pos + Vec2i(type->getSize()/2, type->getSize()/2); +} + +Vec2f Unit::getFloatCenteredPos() const{ + return Vec2f(pos.x-0.5f+type->getSize()/2.f, pos.y-0.5f+type->getSize()/2.f); +} + +Vec2i Unit::getCellPos() const{ + if(type->hasCellMap()){ + + //find nearest pos to center that is free + Vec2i centeredPos= getCenteredPos(); + float nearestDist= -1.f; + Vec2i nearestPos= pos; + + for(int i=0; igetSize(); ++i){ + for(int j=0; jgetSize(); ++j){ + if(getCellMapCell(i, j)){ + Vec2i currPos= pos + Vec2i(i, j); + float dist= currPos.dist(centeredPos); + if(nearestDist==-1.f || distgetProperty(UnitType::pRotatedClimb) && currSkill->getClass()==scMove){ + float heightDiff= map->getCell(pos)->getHeight() - map->getCell(targetPos)->getHeight(); + float dist= pos.dist(targetPos); + return radToDeg(atan2(heightDiff, dist)); + }*/ + return 0.f; +} + +int Unit::getProductionPercent() const{ + if(anyCommand()){ + const ProducibleType *produced= commands.front()->getCommandType()->getProduced(); + if(produced!=NULL){ + return clamp(progress2*100/produced->getProductionTime(), 0, 100); + } + } + return -1; +} + +float Unit::getHpRatio() const{ + return clamp(static_cast(hp)/type->getTotalMaxHp(&totalUpgrade), 0.f, 1.f); +} + +float Unit::getEpRatio() const{ + if(type->getMaxHp()==0){ + return 0.f; + } + else{ + return clamp(static_cast(ep)/type->getTotalMaxEp(&totalUpgrade), 0.f, 1.f); + } +} + +const Level *Unit::getNextLevel() const{ + if(level==NULL && type->getLevelCount()>0){ + return type->getLevel(0); + } + else{ + for(int i=1; igetLevelCount(); ++i){ + if(type->getLevel(i-1)==level){ + return type->getLevel(i); + } + } + } + return NULL; +} + +string Unit::getFullName() const{ + string str=""; + if(level != NULL){ + str += (level->getName() + " "); + } + if(type == NULL) { + throw runtime_error("type == NULL in Unit::getFullName()!"); + } + str += type->getName(); + return str; +} + +// ====================================== is ====================================== + +bool Unit::isOperative() const{ + return isAlive() && isBuilt(); +} + +bool Unit::isBeingBuilt() const{ + return currSkill->getClass()==scBeBuilt; +} + +bool Unit::isBuilt() const{ + return !isBeingBuilt(); +} + +bool Unit::isPutrefacting() const{ + return deadCount!=0; +} + +bool Unit::isAlly(const Unit *unit) const{ + return faction->isAlly(unit->getFaction()); +} + +bool Unit::isDamaged() const{ + return hp < type->getTotalMaxHp(&totalUpgrade); +} + +bool Unit::isInteresting(InterestingUnitType iut) const{ + switch(iut){ + case iutIdleHarvester: + if(type->hasCommandClass(ccHarvest)){ + if(!commands.empty()){ + const CommandType *ct= commands.front()->getCommandType(); + if(ct!=NULL){ + return ct->getClass()==ccStop; + } + } + } + return false; + + case iutBuiltBuilding: + return type->hasSkillClass(scBeBuilt) && isBuilt(); + case iutProducer: + return type->hasSkillClass(scProduce); + case iutDamaged: + return isDamaged(); + case iutStore: + return type->getStoredResourceCount()>0; + default: + return false; + } +} + +// ====================================== set ====================================== + +void Unit::setCurrSkill(const SkillType *currSkill){ + if(currSkill->getClass()!=this->currSkill->getClass()){ + animProgress= 0; + lastAnimProgress= 0; + + while(!unitParticleSystems.empty()){ + unitParticleSystems.back()->fade(); + unitParticleSystems.pop_back(); + } + } + if(showUnitParticles && (!currSkill->unitParticleSystemTypes.empty()) && + (unitParticleSystems.empty()) ){ + for(UnitParticleSystemTypes::const_iterator it= currSkill->unitParticleSystemTypes.begin(); it!=currSkill->unitParticleSystemTypes.end(); ++it){ + UnitParticleSystem *ups; + ups= new UnitParticleSystem(200); + (*it)->setValues(ups); + ups->setPos(getCurrVector()); + ups->setFactionColor(getFaction()->getTexture()->getPixmap()->getPixel3f(0,0)); + unitParticleSystems.push_back(ups); + Renderer::getInstance().manageParticleSystem(ups, rsGame); + } + } + progress2= 0; + this->currSkill= currSkill; +} + +void Unit::setCurrSkill(SkillClass sc){ + setCurrSkill(getType()->getFirstStOfClass(sc)); +} + +void Unit::setTarget(const Unit *unit){ + + //find a free pos in cellmap + setTargetPos(unit->getCellPos()); + + //ser field and vector + targetField= unit->getCurrField(); + targetVec= unit->getCurrVector(); + targetRef= unit; +} + +void Unit::setPos(const Vec2i &pos){ + this->lastPos= this->pos; + this->pos= pos; + this->meetingPos= pos - Vec2i(1); +} + +void Unit::setTargetPos(const Vec2i &targetPos){ + + Vec2i relPos= targetPos - pos; + Vec2f relPosf= Vec2f(relPos.x, relPos.y); + targetRotation= radToDeg(atan2(relPosf.x, relPosf.y)); + targetRef= NULL; + + this->targetPos= targetPos; +} + +void Unit::setVisible(const bool visible){ + for(UnitParticleSystems::iterator it= unitParticleSystems.begin(); it!=unitParticleSystems.end(); ++it){ + (*it)->setVisible(visible); + } + for(UnitParticleSystems::iterator it= damageParticleSystems.begin(); it!=damageParticleSystems.end(); ++it){ + (*it)->setVisible(visible); + } +} + +// =============================== Render related ================================== + +const Model *Unit::getCurrentModel() const{ + return currSkill->getAnimation(); +} + +Vec3f Unit::getCurrVector() const{ + return getCurrVectorFlat() + Vec3f(0.f, type->getHeight()/2.f, 0.f); +} + +Vec3f Unit::getCurrVectorFlat() const{ + Vec3f v; + + float y1= computeHeight(lastPos); + float y2= computeHeight(pos); + + if(currSkill->getClass()==scMove){ + v.x= lastPos.x + progress * (pos.x-lastPos.x); + v.z= lastPos.y + progress * (pos.y-lastPos.y); + v.y= y1+progress*(y2-y1); + } + else{ + v.x= static_cast(pos.x); + v.z= static_cast(pos.y); + v.y= y2; + } + v.x+= type->getSize()/2.f-0.5f; + v.z+= type->getSize()/2.f-0.5f; + + return v; +} + +// =================== Command list related =================== + +//any command +bool Unit::anyCommand() const{ + return !commands.empty(); +} + +//return current command, assert that there is always one command +Command *Unit::getCurrCommand() const{ + assert(!commands.empty()); + return commands.front(); +} +//returns the size of the commands +unsigned int Unit::getCommandSize() const{ + return commands.size(); +} + +//give one command (clear, and push back) +CommandResult Unit::giveCommand(Command *command){ + + //if(Socket::enableDebugText) printf("In [%s::%s] START\n",__FILE__,__FUNCTION__); + + if(command->getCommandType()->isQueuable()){ + //cancel current command if it is not queuable + if(!commands.empty() && !commands.front()->getCommandType()->isQueuable()){ + cancelCommand(); + } + } + else{ + //empty command queue + clearCommands(); + unitPath.clear(); + } + + //if(Socket::enableDebugText) printf("In [%s::%s] A\n",__FILE__,__FUNCTION__); + + //check command + CommandResult result= checkCommand(command); + if(result==crSuccess){ + applyCommand(command); + } + + //if(Socket::enableDebugText) printf("In [%s::%s] B\n",__FILE__,__FUNCTION__); + + //push back command + if(result== crSuccess){ + commands.push_back(command); + } + else{ + delete command; + } + + //if(Socket::enableDebugText) printf("In [%s::%s] END\n",__FILE__,__FUNCTION__); + + return result; +} + +//pop front (used when order is done) +CommandResult Unit::finishCommand(){ + + //is empty? + if(commands.empty()){ + return crFailUndefined; + } + + //pop front + delete commands.front(); + commands.erase(commands.begin()); + unitPath.clear(); + + return crSuccess; +} + +//to cancel a command +CommandResult Unit::cancelCommand(){ + + //is empty? + if(commands.empty()){ + return crFailUndefined; + } + + //undo command + undoCommand(commands.back()); + + //delete ans pop command + delete commands.back(); + commands.pop_back(); + + //clear routes + unitPath.clear(); + + return crSuccess; +} + +// =================== route stack =================== + +void Unit::create(bool startingUnit){ + faction->addUnit(this); + map->putUnitCells(this, pos); + if(startingUnit){ + faction->applyStaticCosts(type); + } +} + +void Unit::born(){ + faction->addStore(type); + faction->applyStaticProduction(type); + setCurrSkill(scStop); + hp= type->getMaxHp(); +} + +void Unit::kill(){ + + //no longer needs static resources + if(isBeingBuilt()){ + faction->deApplyStaticConsumption(type); + } + else{ + faction->deApplyStaticCosts(type); + } + + //do the cleaning + map->clearUnitCells(this, pos); + if(!isBeingBuilt()){ + faction->removeStore(type); + } + setCurrSkill(scDie); + + notifyObservers(UnitObserver::eKill); + + //clear commands + clearCommands(); +} + +void Unit::undertake(){ + faction->removeUnit(this); +} + +// =================== Referencers =================== + +void Unit::addObserver(UnitObserver *unitObserver){ + observers.push_back(unitObserver); +} + +void Unit::removeObserver(UnitObserver *unitObserver){ + observers.remove(unitObserver); +} + +void Unit::notifyObservers(UnitObserver::Event event){ + for(Observers::iterator it= observers.begin(); it!=observers.end(); ++it){ + (*it)->unitEvent(event, this); + } +} + +// =================== Other =================== + +void Unit::resetHighlight(){ + highlight= 1.f; +} + +const CommandType *Unit::computeCommandType(const Vec2i &pos, const Unit *targetUnit) const{ + const CommandType *commandType= NULL; + SurfaceCell *sc= map->getSurfaceCell(Map::toSurfCoords(pos)); + + if(targetUnit!=NULL){ + //attack enemies + if(!isAlly(targetUnit)){ + commandType= type->getFirstAttackCommand(targetUnit->getCurrField()); + } + + //repair allies + else{ + commandType= type->getFirstRepairCommand(targetUnit->getType()); + } + } + else{ + //check harvest command + Resource *resource= sc->getResource(); + if(resource!=NULL){ + commandType= type->getFirstHarvestCommand(resource->getType()); + } + } + + //default command is move command + if(commandType==NULL){ + commandType= type->getFirstCtOfClass(ccMove); + } + + return commandType; +} + +bool Unit::update(){ + assert(progress<=1.f); + + //highlight + if(highlight>0.f){ + highlight-= 1.f/(highlightTime*GameConstants::updateFps); + } + + //speed + int speed= currSkill->getTotalSpeed(&totalUpgrade); + + //speed modifier + float diagonalFactor= 1.f; + float heightFactor= 1.f; + if(currSkill->getClass()==scMove){ + + //if moving in diagonal move slower + Vec2i dest= pos-lastPos; + if(abs(dest.x)+abs(dest.y) == 2){ + diagonalFactor= 0.71f; + } + + //if movig to an higher cell move slower else move faster + float heightDiff= map->getCell(pos)->getHeight() - map->getCell(targetPos)->getHeight(); + heightFactor= clamp(1.f+heightDiff/5.f, 0.2f, 5.f); + } + + + //update progresses + lastAnimProgress= animProgress; + progress+= (speed*diagonalFactor*heightFactor)/(speedDivider*GameConstants::updateFps); + animProgress+= (currSkill->getAnimSpeed()*heightFactor)/(speedDivider*GameConstants::updateFps); + + //update target + updateTarget(); + + //rotation + if(currSkill->getClass()!=scStop){ + const int rotFactor= 2; + if(progress<1.f/rotFactor){ + if(type->getFirstStOfClass(scMove)){ + if(abs(lastRotation-targetRotation)<180) + rotation= lastRotation+(targetRotation-lastRotation)*progress*rotFactor; + else{ + float rotationTerm= targetRotation>lastRotation? -360.f: +360.f; + rotation= lastRotation+(targetRotation-lastRotation+rotationTerm)*progress*rotFactor; + } + } + } + } + + if (fire!=NULL) + { + fire->setPos(getCurrVector()); + } + for(UnitParticleSystems::iterator it= unitParticleSystems.begin(); it!=unitParticleSystems.end(); ++it){ + (*it)->setPos(getCurrVector()); + (*it)->setRotation(getRotation()); + } + for(UnitParticleSystems::iterator it= damageParticleSystems.begin(); it!=damageParticleSystems.end(); ++it){ + (*it)->setPos(getCurrVector()); + (*it)->setRotation(getRotation()); + } + //checks + if(animProgress>1.f){ + animProgress= currSkill->getClass()==scDie? 1.f: 0.f; + } + + //checks + if(progress>=1.f){ + lastRotation= targetRotation; + if(currSkill->getClass()!=scDie){ + progress= 0.f; + return true; + } + else{ + progress= 1.f; + deadCount++; + if(deadCount>=maxDeadCount){ + toBeUndertaken= true; + return false; + } + } + } + + return false; +} + +void Unit::tick(){ + + if(isAlive()){ + //regenerate hp + hp+= type->getHpRegeneration(); + if(hp>type->getTotalMaxHp(&totalUpgrade)){ + hp= type->getTotalMaxHp(&totalUpgrade); + } + //stop DamageParticles + if(hp>type->getTotalMaxHp(&totalUpgrade)/2 ){ + stopDamageParticles(); + } + //regenerate ep + ep+= type->getEpRegeneration(); + if(ep>type->getTotalMaxEp(&totalUpgrade)){ + ep= type->getTotalMaxEp(&totalUpgrade); + } + } +} + +int Unit::update2(){ + progress2++; + return progress2; +} + +bool Unit::computeEp(){ + + //if not enough ep + if(ep-currSkill->getEpCost() < 0){ + return true; + } + + //decrease ep + ep-= currSkill->getEpCost(); + if(ep>getType()->getTotalMaxEp(&totalUpgrade)){ + ep= getType()->getTotalMaxEp(&totalUpgrade); + } + + return false; +} + +bool Unit::repair(){ + + //increase hp + hp+= getType()->getMaxHp()/type->getProductionTime() + 1; + if(hp>(getType()->getTotalMaxHp(&totalUpgrade))){ + hp= getType()->getTotalMaxHp(&totalUpgrade); + return true; + } + + //stop DamageParticles + if(hp>type->getTotalMaxHp(&totalUpgrade)/2 ){ + stopDamageParticles(); + } + return false; +} + +//decrements HP and returns if dead +bool Unit::decHp(int i){ + if(hp==0){ + return false; + } + + hp-=i; + + //startDamageParticles + if(hpgetMaxHp()/2 ){ + startDamageParticles(); + } + + //stop DamageParticles on death + if(hp<=0){ + alive= false; + hp=0; + stopDamageParticles(); + return true; + } + return false; +} + +string Unit::getDesc() const{ + + Lang &lang= Lang::getInstance(); + + //pos + //str+="Pos: "+v2iToStr(pos)+"\n"; + + //hp + string str= "\n" + lang.get("Hp")+ ": " + intToStr(hp) + "/" + intToStr(type->getTotalMaxHp(&totalUpgrade)); + if(type->getHpRegeneration()!=0){ + str+= " (" + lang.get("Regeneration") + ": " + intToStr(type->getHpRegeneration()) + ")"; + } + + //ep + if(getType()->getMaxEp()!=0){ + str+= "\n" + lang.get("Ep")+ ": " + intToStr(ep) + "/" + intToStr(type->getTotalMaxEp(&totalUpgrade)); + } + if(type->getEpRegeneration()!=0){ + str+= " (" + lang.get("Regeneration") + ": " + intToStr(type->getEpRegeneration()) + ")"; + } + + //armor + str+= "\n" + lang.get("Armor")+ ": " + intToStr(getType()->getArmor()); + if(totalUpgrade.getArmor()!=0){ + str+="+"+intToStr(totalUpgrade.getArmor()); + } + str+= " ("+getType()->getArmorType()->getName()+")"; + + //sight + str+="\n"+ lang.get("Sight")+ ": " + intToStr(getType()->getSight()); + if(totalUpgrade.getSight()!=0){ + str+="+"+intToStr(totalUpgrade.getSight()); + } + + //kills + const Level *nextLevel= getNextLevel(); + if(kills>0 || nextLevel!=NULL){ + str+= "\n" + lang.get("Kills") +": " + intToStr(kills); + if(nextLevel!=NULL){ + str+= " (" + nextLevel->getName() + ": " + intToStr(nextLevel->getKills()) + ")"; + } + } + + //str+= "\nskl: "+scToStr(currSkill->getClass()); + + //load + if(loadCount!=0){ + str+= "\n" + lang.get("Load")+ ": " + intToStr(loadCount) +" " + loadType->getName(); + } + + //consumable production + for(int i=0; igetCostCount(); ++i){ + const Resource *r= getType()->getCost(i); + if(r->getType()->getClass()==rcConsumable){ + str+= "\n"; + str+= r->getAmount()<0? lang.get("Produce")+": ": lang.get("Consume")+": "; + str+= intToStr(abs(r->getAmount())) + " " + r->getType()->getName(); + } + } + + //command info + if(!commands.empty()){ + str+= "\n" + commands.front()->getCommandType()->getName(); + if(commands.size()>1){ + str+="\n"+lang.get("OrdersOnQueue")+": "+intToStr(commands.size()); + } + } + else{ + //can store + if(getType()->getStoredResourceCount()>0){ + for(int i=0; igetStoredResourceCount(); ++i){ + const Resource *r= getType()->getStoredResource(i); + str+= "\n"+lang.get("Store")+": "; + str+= intToStr(r->getAmount()) + " " + r->getType()->getName(); + } + } + } + + return str; +} + +void Unit::applyUpgrade(const UpgradeType *upgradeType){ + if(upgradeType->isAffected(type)){ + totalUpgrade.sum(upgradeType); + hp+= upgradeType->getMaxHp(); + } +} + +void Unit::computeTotalUpgrade(){ + faction->getUpgradeManager()->computeTotalUpgrade(this, &totalUpgrade); +} + +void Unit::incKills(){ + ++kills; + + const Level *nextLevel= getNextLevel(); + if(nextLevel!=NULL && kills>= nextLevel->getKills()){ + level= nextLevel; + int maxHp= totalUpgrade.getMaxHp(); + totalUpgrade.incLevel(type); + hp+= totalUpgrade.getMaxHp()-maxHp; + } +} + +bool Unit::morph(const MorphCommandType *mct){ + const UnitType *morphUnitType= mct->getMorphUnit(); + + Field morphUnitField=fLand; + if(morphUnitType->getField(fAir)) morphUnitField=fAir; + if(morphUnitType->getField(fLand)) morphUnitField=fLand; + if(map->isFreeCellsOrHasUnit(pos, morphUnitType->getSize(), morphUnitField, this)){ + map->clearUnitCells(this, pos); + faction->deApplyStaticCosts(type); + hp+= morphUnitType->getMaxHp() - type->getMaxHp(); + type= morphUnitType; + level= NULL; + currField=morphUnitField; + computeTotalUpgrade(); + map->putUnitCells(this, pos); + faction->applyDiscount(morphUnitType, mct->getDiscount()); + return true; + } + else{ + return false; + } +} + + +// ==================== PRIVATE ==================== + +float Unit::computeHeight(const Vec2i &pos) const{ + float height= map->getCell(pos)->getHeight(); + + if(currField==fAir){ + height+= World::airHeight; + } + + return height; +} + +void Unit::updateTarget(){ + Unit *target= targetRef.getUnit(); + if(target!=NULL){ + + //update target pos + targetPos= target->getCellPos(); + Vec2i relPos= targetPos - pos; + Vec2f relPosf= Vec2f(relPos.x, relPos.y); + targetRotation= radToDeg(atan2(relPosf.x, relPosf.y)); + + //update target vec + targetVec= target->getCurrVector(); + } +} + +void Unit::clearCommands(){ + while(!commands.empty()){ + undoCommand(commands.back()); + delete commands.back(); + commands.pop_back(); + } +} + +CommandResult Unit::checkCommand(Command *command) const{ + + //if not operative or has not command type => fail + if(!isOperative() || command->getUnit()==this || !getType()->hasCommandType(command->getCommandType())){ + return crFailUndefined; + } + + //if pos is not inside the world (if comand has not a pos, pos is (0, 0) and is inside world + if(!map->isInside(command->getPos())){ + return crFailUndefined; + } + + //check produced + const ProducibleType *produced= command->getCommandType()->getProduced(); + if(produced!=NULL){ + if(!faction->reqsOk(produced)){ + return crFailReqs; + } + if(!faction->checkCosts(produced)){ + return crFailRes; + } + } + + //build command specific, check resources and requirements for building + if(command->getCommandType()->getClass()==ccBuild){ + const UnitType *builtUnit= command->getUnitType(); + if(!faction->reqsOk(builtUnit)){ + return crFailReqs; + } + if(!faction->checkCosts(builtUnit)){ + return crFailRes; + } + } + + //upgrade command specific, check that upgrade is not upgraded + else if(command->getCommandType()->getClass()==ccUpgrade){ + const UpgradeCommandType *uct= static_cast(command->getCommandType()); + if(faction->getUpgradeManager()->isUpgradingOrUpgraded(uct->getProducedUpgrade())){ + return crFailUndefined; + } + } + + return crSuccess; +} + +void Unit::applyCommand(Command *command){ + + //check produced + const ProducibleType *produced= command->getCommandType()->getProduced(); + if(produced!=NULL){ + faction->applyCosts(produced); + } + + //build command specific + if(command->getCommandType()->getClass()==ccBuild){ + faction->applyCosts(command->getUnitType()); + } + + //upgrade command specific + else if(command->getCommandType()->getClass()==ccUpgrade){ + const UpgradeCommandType *uct= static_cast(command->getCommandType()); + faction->startUpgrade(uct->getProducedUpgrade()); + } +} + +CommandResult Unit::undoCommand(Command *command){ + + //return cost + const ProducibleType *produced= command->getCommandType()->getProduced(); + if(produced!=NULL){ + faction->deApplyCosts(produced); + } + + //return building cost if not already building it or dead + if(command->getCommandType()->getClass() == ccBuild){ + if(currSkill->getClass()!=scBuild && currSkill->getClass()!=scDie){ + faction->deApplyCosts(command->getUnitType()); + } + } + + //upgrade command cancel from list + if(command->getCommandType()->getClass() == ccUpgrade){ + const UpgradeCommandType *uct= static_cast(command->getCommandType()); + faction->cancelUpgrade(uct->getProducedUpgrade()); + } + + return crSuccess; +} + +void Unit::stopDamageParticles(){ + // stop fire + if(fire!=NULL){ + fire->fade(); + fire= NULL; + } + // stop additional particles + while(!damageParticleSystems.empty()){ + damageParticleSystems.back()->fade(); + damageParticleSystems.pop_back(); + } +} + +void Unit::startDamageParticles(){ + //start additional particles + if( showUnitParticles && (!type->damageParticleSystemTypes.empty()) + && (damageParticleSystems.empty()) ){ + for(UnitParticleSystemTypes::const_iterator it= type->damageParticleSystemTypes.begin(); it!=type->damageParticleSystemTypes.end(); ++it){ + UnitParticleSystem *ups; + ups= new UnitParticleSystem(200); + (*it)->setValues(ups); + ups->setPos(getCurrVector()); + ups->setFactionColor(getFaction()->getTexture()->getPixmap()->getPixel3f(0,0)); + damageParticleSystems.push_back(ups); + Renderer::getInstance().manageParticleSystem(ups, rsGame); + } + } + // start fire + if(type->getProperty(UnitType::pBurnable) && fire==NULL){ + FireParticleSystem *fps; + fps= new FireParticleSystem(200); + fps->setSpeed(2.5f/GameConstants::updateFps); + fps->setPos(getCurrVector()); + fps->setRadius(type->getSize()/3.f); + fps->setTexture(CoreData::getInstance().getFireTexture()); + fps->setParticleSize(type->getSize()/3.f); + fire= fps; + Renderer::getInstance().manageParticleSystem(fps, rsGame); + if(showUnitParticles){ + // smoke + UnitParticleSystem *ups= new UnitParticleSystem(400); + ups->setColorNoEnergy(Vec4f(0.0f, 0.0f, 0.0f, 0.13f)); + ups->setColor(Vec4f(0.115f, 0.115f, 0.115f, 0.22f)); + ups->setPos(getCurrVector()); + ups->setBlendMode(ups->strToBlendMode("black")); + ups->setOffset(Vec3f(0,2,0)); + ups->setDirection(Vec3f(0,1,-0.2f)); + ups->setRadius(type->getSize()/3.f); + ups->setTexture(CoreData::getInstance().getFireTexture()); + ups->setSpeed(2.0f/GameConstants::updateFps); + ups->setGravity(0.0004f); + ups->setEmissionRate(1); + ups->setMaxParticleEnergy(150); + ups->setSizeNoEnergy(type->getSize()*0.6f); + ups->setParticleSize(type->getSize()*0.8f); + damageParticleSystems.push_back(ups); + Renderer::getInstance().manageParticleSystem(ups, rsGame); + } + + } +} + +bool Unit::getCellMapCell(int x, int y) const { + const UnitType *ut= getType(); + if(ut != NULL && rotateAmount > 0) { + return cellMap[ut->getSize() * y + x]; + } + else { + return ut->getCellMapCell(x,y); + } +} + +void Unit::setRotateAmount(float value) { + rotateAmount = value; + + //if(Socket::enableDebugText) printf("In [%s::%s] unit id = %d [%s] rotate amount = %f\n",__FILE__,__FUNCTION__,getId(), getFullName().c_str(),rotateAmount); + + const UnitType *ut= getType(); + if(ut != NULL && ut->hasCellMap() == true) { + int matrixSize = ut->getSize(); + + if(rotateAmount > 0) { + + delete [] cellMap; + cellMap = new bool[matrixSize * matrixSize]; + + for(int iRow = 0; iRow < matrixSize; ++iRow) { + for(int iCol = 0; iCol < matrixSize; ++iCol) { + bool getCellResult = ut->getCellMapCell(iCol, iRow); + //if(Socket::enableDebugText) printf("In [%s::%s] [%d,%d] = %d\n",__FILE__,__FUNCTION__,iRow,iCol,getCellResult); + + int newRow = 0; + int newCol = 0; + + switch((int)rotateAmount) + { + case 90: + newRow = (matrixSize - iCol - 1); + newCol = iRow; + break; + case 180: + newRow = (matrixSize - iRow - 1); + newCol = (matrixSize - iCol - 1); + break; + case 270: + newRow = iCol; + newCol = (matrixSize - iRow - 1); + break; + + } + + //if(Socket::enableDebugText) printf("In [%s::%s] ABOUT TO Transform to [%d,%d] = %d\n",__FILE__,__FUNCTION__,newRow,newCol,getCellResult); + + // bool getCellMapCell(int x, int y) const {return cellMap[size*y+x];} + // cellMap[i*size+j]= row[j]=='0'? false: true; + cellMap[matrixSize * newRow + newCol] = getCellResult; + } + } + } + + //if(Socket::enableDebugText) printf("In [%s::%s] Transformed matrix below:\n",__FILE__,__FUNCTION__); + for(int iRow = 0; iRow < matrixSize; ++iRow) { + for(int iCol = 0; iCol < matrixSize; ++iCol) { + bool getCellResult = ut->getCellMapCell(iCol, iRow); + bool getCellResultRotated = getCellMapCell(iRow, iCol); + //if(Socket::enableDebugText) printf("In [%s::%s] matrix [%d,%d] = %d, rotated = %d\n",__FILE__,__FUNCTION__,iRow,iCol,getCellResult,getCellResultRotated); + } + } + } +} + +}}//end namespace diff --git a/source/glest_game/types/unit_type.cpp b/source/glest_game/types/unit_type.cpp new file mode 100644 index 00000000..624159f8 --- /dev/null +++ b/source/glest_game/types/unit_type.cpp @@ -0,0 +1,546 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Marti�o Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "unit_type.h" + +#include + +#include "util.h" +#include "upgrade_type.h" +#include "resource_type.h" +#include "sound.h" +#include "logger.h" +#include "xml_parser.h" +#include "tech_tree.h" +#include "resource.h" +#include "renderer.h" +#include "game_util.h" +#include "leak_dumper.h" +#include "unit_particle_type.h" +//#include "socket.h" + +using namespace Shared::Xml; +using namespace Shared::Graphics; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// =============================== +// class Level +// =============================== + +void Level::init(string name, int kills){ + this->name= name; + this->kills= kills; +} + +// ===================================================== +// class UnitType +// ===================================================== + +// ===================== PUBLIC ======================== + +const char *UnitType::propertyNames[]= {"burnable", "rotated_climb"}; + +// ==================== creation and loading ==================== + +UnitType::UnitType(){ + + lightColor= Vec3f(0.f); + light= false; + multiSelect= false; + armorType= NULL; + + for(int i=0; iid= id; + string path; + + try{ + + Logger::getInstance().add("Unit type: " + formatString(name), true); + + //file load + path= dir+"/"+name+".xml"; + +#ifdef _WINDOWS + + DWORD fileAttributes = GetFileAttributes(path.c_str()); + if( (fileAttributes & FILE_ATTRIBUTE_HIDDEN) == FILE_ATTRIBUTE_HIDDEN) + { + return; + } + +#endif + + checksum->addFile(path); + + XmlTree xmlTree; + xmlTree.load(path); + const XmlNode *unitNode= xmlTree.getRootNode(); + + const XmlNode *parametersNode= unitNode->getChild("parameters"); + + //size + size= parametersNode->getChild("size")->getAttribute("value")->getIntValue(); + + //height + height= parametersNode->getChild("height")->getAttribute("value")->getIntValue(); + + //maxHp + maxHp= parametersNode->getChild("max-hp")->getAttribute("value")->getIntValue(); + + //hpRegeneration + hpRegeneration= parametersNode->getChild("max-hp")->getAttribute("regeneration")->getIntValue(); + + //maxEp + maxEp= parametersNode->getChild("max-ep")->getAttribute("value")->getIntValue(); + + if(maxEp!=0){ + //wpRegeneration + epRegeneration= parametersNode->getChild("max-ep")->getAttribute("regeneration")->getIntValue(); + } + + //armor + armor= parametersNode->getChild("armor")->getAttribute("value")->getIntValue(); + + //armor type string + string armorTypeName= parametersNode->getChild("armor-type")->getAttribute("value")->getRestrictedValue(); + armorType= techTree->getArmorType(armorTypeName); + + //sight + sight= parametersNode->getChild("sight")->getAttribute("value")->getIntValue(); + + //prod time + productionTime= parametersNode->getChild("time")->getAttribute("value")->getIntValue(); + + //multi selection + multiSelect= parametersNode->getChild("multi-selection")->getAttribute("value")->getBoolValue(); + + //cellmap + const XmlNode *cellMapNode= parametersNode->getChild("cellmap"); + bool hasCellMap= cellMapNode->getAttribute("value")->getBoolValue(); + if(hasCellMap){ + cellMap= new bool[size*size]; + for(int i=0; igetChild("row", i); + string row= rowNode->getAttribute("value")->getRestrictedValue(); + if(row.size()!=size){ + throw runtime_error("Cellmap row has not the same length as unit size"); + } + for(int j=0; jgetChild("row", iRow); + string row= rowNode->getAttribute("value")->getRestrictedValue(); + if(Socket::enableDebugText) printf("In [%s::%s] row = %s\n",__FILE__,__FUNCTION__,row.c_str()); + + for(int iCol = 0; iCol < size; ++iCol) { + bool getCellResult = getCellMapCell(iCol, iRow); + if(Socket::enableDebugText) printf("In [%s::%s] matrix [%d,%d] = %d\n",__FILE__,__FUNCTION__,iRow,iCol,getCellResult); + } + } + */ + + } + + //levels + const XmlNode *levelsNode= parametersNode->getChild("levels"); + levels.resize(levelsNode->getChildCount()); + for(int i=0; igetChild("level", i); + levels[i].init( + levelNode->getAttribute("name")->getRestrictedValue(), + levelNode->getAttribute("kills")->getIntValue()); + } + + //fields + const XmlNode *fieldsNode= parametersNode->getChild("fields"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *fieldNode= fieldsNode->getChild("field", i); + string fieldName= fieldNode->getAttribute("value")->getRestrictedValue(); + if(fieldName=="land"){ + fields[fLand]= true; + } + else if(fieldName=="air"){ + fields[fAir]= true; + } + else{ + throw runtime_error("Not a valid field: "+fieldName+": "+ path); + } + } + + //properties + const XmlNode *propertiesNode= parametersNode->getChild("properties"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *propertyNode= propertiesNode->getChild("property", i); + string propertyName= propertyNode->getAttribute("value")->getRestrictedValue(); + bool found= false; + for(int i=0; ihasChild("damage-particles")){ + const XmlNode *particleNode= parametersNode->getChild("damage-particles"); + bool particleEnabled= particleNode->getAttribute("value")->getBoolValue(); + if(particleEnabled){ + for(int i=0; igetChildCount(); ++i){ + const XmlNode *particleFileNode= particleNode->getChild("particle-file", i); + string path= particleFileNode->getAttribute("path")->getRestrictedValue(); + UnitParticleSystemType *unitParticleSystemType= new UnitParticleSystemType(); + unitParticleSystemType->load(dir, dir + "/" + path); + damageParticleSystemTypes.push_back(unitParticleSystemType); + } + } + } + + //light + const XmlNode *lightNode= parametersNode->getChild("light"); + light= lightNode->getAttribute("enabled")->getBoolValue(); + if(light){ + lightColor.x= lightNode->getAttribute("red")->getFloatValue(0.f, 1.f); + lightColor.y= lightNode->getAttribute("green")->getFloatValue(0.f, 1.f); + lightColor.z= lightNode->getAttribute("blue")->getFloatValue(0.f, 1.f); + } + + //unit requirements + const XmlNode *unitRequirementsNode= parametersNode->getChild("unit-requirements"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *unitNode= unitRequirementsNode->getChild("unit", i); + string name= unitNode->getAttribute("name")->getRestrictedValue(); + unitReqs.push_back(factionType->getUnitType(name)); + } + + //upgrade requirements + const XmlNode *upgradeRequirementsNode= parametersNode->getChild("upgrade-requirements"); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *upgradeReqNode= upgradeRequirementsNode->getChild("upgrade", i); + string name= upgradeReqNode->getAttribute("name")->getRestrictedValue(); + upgradeReqs.push_back(factionType->getUpgradeType(name)); + } + + //resource requirements + const XmlNode *resourceRequirementsNode= parametersNode->getChild("resource-requirements"); + costs.resize(resourceRequirementsNode->getChildCount()); + for(int i=0; igetChild("resource", i); + string name= resourceNode->getAttribute("name")->getRestrictedValue(); + int amount= resourceNode->getAttribute("amount")->getIntValue(); + costs[i].init(techTree->getResourceType(name), amount); + } + + //resources stored + const XmlNode *resourcesStoredNode= parametersNode->getChild("resources-stored"); + storedResources.resize(resourcesStoredNode->getChildCount()); + for(int i=0; igetChild("resource", i); + string name= resourceNode->getAttribute("name")->getRestrictedValue(); + int amount= resourceNode->getAttribute("amount")->getIntValue(); + storedResources[i].init(techTree->getResourceType(name), amount); + } + + //image + const XmlNode *imageNode= parametersNode->getChild("image"); + image= Renderer::getInstance().newTexture2D(rsGame); + image->load(dir+"/"+imageNode->getAttribute("path")->getRestrictedValue()); + + //image cancel + const XmlNode *imageCancelNode= parametersNode->getChild("image-cancel"); + cancelImage= Renderer::getInstance().newTexture2D(rsGame); + cancelImage->load(dir+"/"+imageCancelNode->getAttribute("path")->getRestrictedValue()); + + //meeting point + const XmlNode *meetingPointNode= parametersNode->getChild("meeting-point"); + meetingPoint= meetingPointNode->getAttribute("value")->getBoolValue(); + if(meetingPoint){ + meetingPointImage= Renderer::getInstance().newTexture2D(rsGame); + meetingPointImage->load(dir+"/"+meetingPointNode->getAttribute("image-path")->getRestrictedValue()); + } + + //selection sounds + const XmlNode *selectionSoundNode= parametersNode->getChild("selection-sounds"); + if(selectionSoundNode->getAttribute("enabled")->getBoolValue()){ + selectionSounds.resize(selectionSoundNode->getChildCount()); + for(int i=0; igetChild("sound", i); + string path= soundNode->getAttribute("path")->getRestrictedValue(); + StaticSound *sound= new StaticSound(); + sound->load(dir + "/" + path); + selectionSounds[i]= sound; + } + } + + //command sounds + const XmlNode *commandSoundNode= parametersNode->getChild("command-sounds"); + if(commandSoundNode->getAttribute("enabled")->getBoolValue()){ + commandSounds.resize(commandSoundNode->getChildCount()); + for(int i=0; igetChildCount(); ++i){ + const XmlNode *soundNode= commandSoundNode->getChild("sound", i); + string path= soundNode->getAttribute("path")->getRestrictedValue(); + StaticSound *sound= new StaticSound(); + sound->load(dir + "/" + path); + commandSounds[i]= sound; + } + } + + //skills + const XmlNode *skillsNode= unitNode->getChild("skills"); + skillTypes.resize(skillsNode->getChildCount()); + for(int i=0; igetChild("skill", i); + const XmlNode *typeNode= sn->getChild("type"); + string classId= typeNode->getAttribute("value")->getRestrictedValue(); + SkillType *skillType= SkillTypeFactory::getInstance().newInstance(classId); + skillType->load(sn, dir, techTree, factionType); + skillTypes[i]= skillType; + } + + //commands + const XmlNode *commandsNode= unitNode->getChild("commands"); + commandTypes.resize(commandsNode->getChildCount()); + for(int i=0; igetChild("command", i); + const XmlNode *typeNode= commandNode->getChild("type"); + string classId= typeNode->getAttribute("value")->getRestrictedValue(); + CommandType *commandType= CommandTypeFactory::getInstance().newInstance(classId); + commandType->load(i, commandNode, dir, techTree, factionType, *this); + commandTypes[i]= commandType; + } + + computeFirstStOfClass(); + computeFirstCtOfClass(); + + if(getFirstStOfClass(scStop)==NULL){ + throw runtime_error("Every unit must have at least one stop skill: "+ path); + } + if(getFirstStOfClass(scDie)==NULL){ + throw runtime_error("Every unit must have at least one die skill: "+ path); + } + + } + //Exception handling (conversions and so on); + catch(const exception &e){ + throw runtime_error("Error loading UnitType: " + path + "\n" + e.what()); + } +} + +// ==================== get ==================== + +const CommandType *UnitType::getFirstCtOfClass(CommandClass commandClass) const{ + return firstCommandTypeOfClass[commandClass]; +} + +const SkillType *UnitType::getFirstStOfClass(SkillClass skillClass) const{ + return firstSkillTypeOfClass[skillClass]; +} + +const HarvestCommandType *UnitType::getFirstHarvestCommand(const ResourceType *resourceType) const{ + for(int i=0; igetClass()== ccHarvest){ + const HarvestCommandType *hct= static_cast(commandTypes[i]); + if(hct->canHarvest(resourceType)){ + return hct; + } + } + } + return NULL; +} + +const AttackCommandType *UnitType::getFirstAttackCommand(Field field) const{ + for(int i=0; igetClass()== ccAttack){ + const AttackCommandType *act= static_cast(commandTypes[i]); + if(act->getAttackSkillType()->getAttackField(field)){ + return act; + } + } + } + return NULL; +} + +const RepairCommandType *UnitType::getFirstRepairCommand(const UnitType *repaired) const{ + for(int i=0; igetClass()== ccRepair){ + const RepairCommandType *rct= static_cast(commandTypes[i]); + if(rct->isRepairableUnitType(repaired)){ + return rct; + } + } + } + return NULL; +} + +int UnitType::getStore(const ResourceType *rt) const{ + for(int i=0; igetName()==skillName){ + if(skillTypes[i]->getClass()==skillClass){ + return skillTypes[i]; + } + else{ + throw runtime_error("Skill \""+skillName+"\" is not of class \""+SkillType::skillClassToStr(skillClass)); + } + } + } + throw runtime_error("No skill named \""+skillName+"\""); +} + +// ==================== totals ==================== + +int UnitType::getTotalMaxHp(const TotalUpgrade *totalUpgrade) const{ + return maxHp + totalUpgrade->getMaxHp(); +} + +int UnitType::getTotalMaxEp(const TotalUpgrade *totalUpgrade) const{ + return maxEp + totalUpgrade->getMaxEp(); +} + +int UnitType::getTotalArmor(const TotalUpgrade *totalUpgrade) const{ + return armor + totalUpgrade->getArmor(); +} + +int UnitType::getTotalSight(const TotalUpgrade *totalUpgrade) const{ + return sight + totalUpgrade->getSight(); +} + +// ==================== has ==================== + +bool UnitType::hasSkillClass(SkillClass skillClass) const{ + return firstSkillTypeOfClass[skillClass]!=NULL; +} + +bool UnitType::hasCommandType(const CommandType *commandType) const{ + assert(commandType!=NULL); + for(int i=0; igetClass()== SkillClass(j)){ + firstSkillTypeOfClass[j]= skillTypes[i]; + break; + } + } + } +} + +void UnitType::computeFirstCtOfClass(){ + for(int j=0; jgetClass()== CommandClass(j)){ + firstCommandTypeOfClass[j]= commandTypes[i]; + break; + } + } + } +} + +const CommandType* UnitType::findCommandTypeById(int id) const{ + for(int i=0; igetId()==id){ + return commandType; + } + } + return NULL; +} + +}}//end namespace diff --git a/source/glest_game/world/world.cpp b/source/glest_game/world/world.cpp new file mode 100644 index 00000000..cae30787 --- /dev/null +++ b/source/glest_game/world/world.cpp @@ -0,0 +1,736 @@ +// ============================================================== +// This file is part of Glest (www.glest.org) +// +// Copyright (C) 2001-2008 Martiño Figueroa +// +// You can redistribute this code and/or modify it under +// the terms of the GNU General Public License as published +// by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version +// ============================================================== + +#include "world.h" + +#include +#include + +#include "config.h" +#include "faction.h" +#include "unit.h" +#include "game.h" +#include "logger.h" +#include "sound_renderer.h" +#include "game_settings.h" +#include "leak_dumper.h" + +using namespace Shared::Graphics; +using namespace Shared::Util; + +namespace Glest{ namespace Game{ + +// ===================================================== +// class World +// ===================================================== + +const float World::airHeight= 5.f; + +// ===================== PUBLIC ======================== + +World::World(){ + Config &config= Config::getInstance(); + + fogOfWar= config.getBool("FogOfWar"); + fogOfWarSmoothing= config.getBool("FogOfWarSmoothing"); + fogOfWarSmoothingFrameSkip= config.getInt("FogOfWarSmoothingFrameSkip"); + + frameCount= 0; + nextUnitId= 0; + + scriptManager= NULL; + this->game = NULL; +} + +void World::end(){ + Logger::getInstance().add("World", true); + + for(int i= 0; igame = game; + scriptManager= game->getScriptManager(); + + unitUpdater.init(game); + + initFactionTypes(game->getGameSettings()); + initCells(); //must be done after knowing faction number and dimensions + initMap(); + initSplattedTextures(); + + //minimap must be init after sum computation + initMinimap(); + if(createUnits){ + initUnits(); + } + initExplorationState(); + computeFow(); +} + +//load tileset +void World::loadTileset(const string &dir, Checksum *checksum){ + tileset.load(dir, checksum); + timeFlow.init(&tileset); +} + +//load tech +void World::loadTech(const string &dir, set &factions, Checksum *checksum){ + techTree.load(dir, factions, checksum); +} + +//load map +void World::loadMap(const string &path, Checksum *checksum){ + checksum->addFile(path); + map.load(path, &techTree, &tileset); +} + +//load map +void World::loadScenario(const string &path, Checksum *checksum){ + checksum->addFile(path); + scenario.load(path); +} + +// ==================== misc ==================== + +void World::update(){ + + ++frameCount; + + //time + timeFlow.update(); + + //water effects + waterEffects.update(); + + //units + for(int i=0; igetUnitCount(); ++j){ + unitUpdater.updateUnit(getFaction(i)->getUnit(j)); + } + } + + //undertake the dead + for(int i=0; igetUnitCount(); ++j){ + Unit *unit= getFaction(i)->getUnit(j); + if(unit->getToBeUndertaken()){ + unit->undertake(); + delete unit; + j--; + } + } + } + + //food costs + for(int i=0; igetClass()==rcConsumable && frameCount % (rt->getInterval()*GameConstants::updateFps)==0){ + for(int i=0; iapplyCostsOnInterval(); + } + } + } + + //fow smoothing + if(fogOfWarSmoothing && ((frameCount+1) % (fogOfWarSmoothingFrameSkip+1))==0){ + float fogFactor= static_cast(frameCount%GameConstants::updateFps)/GameConstants::updateFps; + minimap.updateFowTex(clamp(fogFactor, 0.f, 1.f)); + } + + //tick + if(frameCount%GameConstants::updateFps==0){ + computeFow(); + tick(); + } +} + +void World::tick(){ + if(!fogOfWarSmoothing){ + minimap.updateFowTex(1.f); + } + + //increase hp + for(int i=0; igetUnitCount(); ++j){ + getFaction(i)->getUnit(j)->tick(); + } + } + + //compute resources balance + for(int k=0; kgetClass()==rcConsumable){ + int balance= 0; + for(int j=0; jgetUnitCount(); ++j){ + + //if unit operative and has this cost + const Unit *u= faction->getUnit(j); + if(u->isOperative()){ + const Resource *r= u->getType()->getCost(rt); + if(r!=NULL){ + balance-= u->getType()->getCost(rt)->getAmount(); + } + } + } + faction->setResourceBalance(rt, balance); + } + } + } +} + +Unit* World::findUnitById(int id){ + for(int i= 0; igetUnitCount(); ++j){ + Unit* unit= faction->getUnit(j); + + if(unit->getId()==id){ + return unit; + } + } + } + return NULL; +} + +const UnitType* World::findUnitTypeById(const FactionType* factionType, int id){ + for(int i= 0; igetUnitTypeCount(); ++i){ + const UnitType* unitType= factionType->getUnitType(i); + + if(unitType->getId()==id){ + return unitType; + } + } + return NULL; +} + +//looks for a place for a unit around a start lociacion, returns true if succeded +bool World::placeUnit(const Vec2i &startLoc, int radius, Unit *unit, bool spaciated){ + bool freeSpace; + int size= unit->getType()->getSize(); + Field currField= unit->getCurrField(); + + for(int r=1; rsetPos(pos); + unit->setMeetingPos(pos-Vec2i(1)); + return true; + } + } + } + } + return false; +} + +//clears a unit old position from map and places new position +void World::moveUnitCells(Unit *unit){ + Vec2i newPos= unit->getTargetPos(); + + //newPos must be free or the same pos as current + assert(map.getCell(unit->getPos())->getUnit(unit->getCurrField())==unit || map.isFreeCell(newPos, unit->getCurrField())); + map.clearUnitCells(unit, unit->getPos()); + map.putUnitCells(unit, newPos); + + //water splash + if(tileset.getWaterEffects() && unit->getCurrField()==fLand){ + if(map.getSubmerged(map.getCell(unit->getLastPos()))){ + for(int i=0; i<3; ++i){ + waterEffects.addWaterSplash( + Vec2f(unit->getLastPos().x+random.randRange(-0.4f, 0.4f), unit->getLastPos().y+random.randRange(-0.4f, 0.4f))); + } + } + } +} + +//returns the nearest unit that can store a type of resource given a position and a faction +Unit *World::nearestStore(const Vec2i &pos, int factionIndex, const ResourceType *rt){ + float currDist= infinity; + Unit *currUnit= NULL; + + for(int i=0; igetUnitCount(); ++i){ + Unit *u= getFaction(factionIndex)->getUnit(i); + float tmpDist= u->getPos().dist(pos); + if(tmpDistgetType()->getStore(rt)>0 && u->isOperative()){ + currDist= tmpDist; + currUnit= u; + } + } + return currUnit; +} + +bool World::toRenderUnit(const Unit *unit, const Quad2i &visibleQuad) const{ + //a unit is rendered if it is in a visible cell or is attacking a unit in a visible cell + return + visibleQuad.isInside(unit->getPos()) && + toRenderUnit(unit); +} + +bool World::toRenderUnit(const Unit *unit) const{ + + return + map.getSurfaceCell(Map::toSurfCoords(unit->getCenteredPos()))->isVisible(thisTeamIndex) || + (unit->getCurrSkill()->getClass()==scAttack && + map.getSurfaceCell(Map::toSurfCoords(unit->getTargetPos()))->isVisible(thisTeamIndex)); +} + +void World::createUnit(const string &unitName, int factionIndex, const Vec2i &pos){ + if(factionIndexgetType(); + const UnitType* ut= ft->getUnitType(unitName); + + //!!! + //if(Socket::enableDebugText) printf("In [%s::%s] A\n",__FILE__,__FUNCTION__); + float unitRotation = game->getGui()->getUnitTypeBuildRotation(ut->getId()); + //if(Socket::enableDebugText) printf("In [%s::%s] B\n",__FILE__,__FUNCTION__); + + Unit* unit= new Unit(getNextUnitId(), pos, ut, faction, &map, unitRotation); + + if(placeUnit(pos, generationArea, unit, true)){ + unit->create(true); + unit->born(); + scriptManager->onUnitCreated(unit); + } + else{ + throw runtime_error("Unit cant be placed"); + } + } + else + { + throw runtime_error("Invalid faction index in createUnitAtPosition: " + intToStr(factionIndex)); + } +} + +void World::giveResource(const string &resourceName, int factionIndex, int amount){ + if(factionIndexincResourceAmount(rt, amount); + } + else + { + throw runtime_error("Invalid faction index in giveResource: " + intToStr(factionIndex)); + } +} + +void World::givePositionCommand(int unitId, const string &commandName, const Vec2i &pos){ + Unit* unit= findUnitById(unitId); + if(unit!=NULL){ + CommandClass cc; + + if(commandName=="move"){ + cc= ccMove; + } + else if(commandName=="attack"){ + cc= ccAttack; + } + else{ + throw runtime_error("Invalid position commmand: " + commandName); + } + + unit->giveCommand(new Command( unit->getType()->getFirstCtOfClass(cc), pos )); + } +} + +void World::giveProductionCommand(int unitId, const string &producedName){ + Unit *unit= findUnitById(unitId); + if(unit!=NULL){ + const UnitType *ut= unit->getType(); + + //Search for a command that can produce the unit + for(int i= 0; igetCommandTypeCount(); ++i){ + const CommandType* ct= ut->getCommandType(i); + if(ct->getClass()==ccProduce){ + const ProduceCommandType *pct= static_cast(ct); + if(pct->getProducedUnit()->getName()==producedName){ + unit->giveCommand(new Command(pct)); + break; + } + } + } + } +} + +void World::giveUpgradeCommand(int unitId, const string &upgradeName){ + Unit *unit= findUnitById(unitId); + if(unit!=NULL){ + const UnitType *ut= unit->getType(); + + //Search for a command that can produce the unit + for(int i= 0; igetCommandTypeCount(); ++i){ + const CommandType* ct= ut->getCommandType(i); + if(ct->getClass()==ccUpgrade){ + const UpgradeCommandType *uct= static_cast(ct); + if(uct->getProducedUpgrade()->getName()==upgradeName){ + unit->giveCommand(new Command(uct)); + break; + } + } + } + } +} + + +int World::getResourceAmount(const string &resourceName, int factionIndex){ + if(factionIndexgetResource(rt)->getAmount(); + } + else + { + throw runtime_error("Invalid faction index in giveResource: " + intToStr(factionIndex)); + } +} + +Vec2i World::getStartLocation(int factionIndex){ + if(factionIndexgetStartLocationIndex()); + } + else + { + throw runtime_error("Invalid faction index in getStartLocation: " + intToStr(factionIndex)); + } +} + +Vec2i World::getUnitPosition(int unitId){ + Unit* unit= findUnitById(unitId); + if(unit==NULL){ + throw runtime_error("Can not find unit to get position"); + } + return unit->getPos(); +} + +int World::getUnitFactionIndex(int unitId){ + Unit* unit= findUnitById(unitId); + if(unit==NULL){ + throw runtime_error("Can not find unit to get position"); + } + return unit->getFactionIndex(); +} + +int World::getUnitCount(int factionIndex){ + if(factionIndexgetUnitCount(); ++i){ + const Unit* unit= faction->getUnit(i); + if(unit->isAlive()){ + ++count; + } + } + return count; + } + else + { + throw runtime_error("Invalid faction index in getUnitCount: " + intToStr(factionIndex)); + } +} + +int World::getUnitCountOfType(int factionIndex, const string &typeName){ + if(factionIndexgetUnitCount(); ++i){ + const Unit* unit= faction->getUnit(i); + if(unit->isAlive() && unit->getType()->getName()==typeName){ + ++count; + } + } + return count; + } + else + { + throw runtime_error("Invalid faction index in getUnitCountOfType: " + intToStr(factionIndex)); + } +} + +// ==================== PRIVATE ==================== + +// ==================== private init ==================== + +//init basic cell state +void World::initCells(){ + + Logger::getInstance().add("State cells", true); + for(int i=0; isetFowTexCoord(Vec2f( + i/(next2Power(map.getSurfaceW())-1.f), + j/(next2Power(map.getSurfaceH())-1.f))); + + for(int k=0; ksetExplored(k, false); + sc->setVisible(k, 0); + } + } + } +} + +//init surface textures +void World::initSplattedTextures(){ + for(int i=0; igetSurfaceType(), + sc10->getSurfaceType(), + sc01->getSurfaceType(), + sc11->getSurfaceType(), + coord, texture); + sc00->setSurfTexCoord(coord); + sc00->setSurfaceTexture(texture); + } + } +} + +//creates each faction looking at each faction name contained in GameSettings +void World::initFactionTypes(GameSettings *gs){ + Logger::getInstance().add("Faction types", true); + + if(gs->getFactionCount() > map.getMaxPlayers()){ + throw runtime_error("This map only supports "+intToStr(map.getMaxPlayers())+" players"); + } + + //create stats + stats.init(gs->getFactionCount(), gs->getThisFactionIndex(), gs->getDescription()); + + //create factions + this->thisFactionIndex= gs->getThisFactionIndex(); + factions.resize(gs->getFactionCount()); + for(int i=0; igetFactionTypeName(i)); + factions[i].init( + ft, gs->getFactionControl(i), &techTree, i, gs->getTeam(i), + gs->getStartLocationIndex(i), i==thisFactionIndex, gs->getDefaultResources()); + + stats.setTeam(i, gs->getTeam(i)); + stats.setFactionTypeName(i, formatString(gs->getFactionTypeName(i))); + stats.setControl(i, gs->getFactionControl(i)); + } + + thisTeamIndex= getFaction(thisFactionIndex)->getTeam(); +} + +void World::initMinimap(){ + minimap.init(map.getW(), map.getH(), this); + Logger::getInstance().add("Compute minimap surface", true); +} + +//place units randomly aroud start location +void World::initUnits(){ + + Logger::getInstance().add("Generate elements", true); + + //put starting units + for(int i=0; igetType(); + for(int j=0; jgetStartingUnitCount(); ++j){ + const UnitType *ut= ft->getStartingUnit(j); + int initNumber= ft->getStartingUnitAmount(j); + for(int l=0; lgetGui()->getUnitTypeBuildRotation(ut->getId()); + //if(Socket::enableDebugText) printf("In [%s::%s] B\n",__FILE__,__FUNCTION__); + + Unit *unit= new Unit(getNextUnitId(), Vec2i(0), ut, f, &map, unitRotation); + + int startLocationIndex= f->getStartLocationIndex(); + + if(placeUnit(map.getStartLocation(startLocationIndex), generationArea, unit, true)){ + unit->create(true); + unit->born(); + } + else{ + throw runtime_error("Unit cant be placed, this error is caused because there is no enough place to put the units near its start location, make a better map: "+unit->getType()->getName() + " Faction: "+intToStr(i)); + } + if(unit->getType()->hasSkillClass(scBeBuilt)){ + map.flatternTerrain(unit); + } + } + } + } + map.computeNormals(); + map.computeInterpolatedHeights(); +} + +void World::initMap(){ + map.init(); +} + +void World::initExplorationState(){ + if(!fogOfWar){ + for(int i=0; isetVisible(thisTeamIndex, true); + map.getSurfaceCell(i, j)->setExplored(thisTeamIndex, true); + } + } + } +} + + +// ==================== exploration ==================== + +void World::exploreCells(const Vec2i &newPos, int sightRange, int teamIndex){ + + Vec2i newSurfPos= Map::toSurfCoords(newPos); + int surfSightRange= sightRange/Map::cellScale+1; + + //explore + for(int i=-surfSightRange-indirectSightRange-1; i<=surfSightRange+indirectSightRange+1; ++i){ + for(int j=-surfSightRange-indirectSightRange-1; j<=surfSightRange+indirectSightRange+1; ++j){ + Vec2i currRelPos= Vec2i(i, j); + Vec2i currPos= newSurfPos + currRelPos; + if(map.isInsideSurface(currPos)){ + + SurfaceCell *sc= map.getSurfaceCell(currPos); + + //explore + if(Vec2i(0).dist(currRelPos) < surfSightRange+indirectSightRange+1){ + sc->setExplored(teamIndex, true); + } + + //visible + if(Vec2i(0).dist(currRelPos) < surfSightRange){ + sc->setVisible(teamIndex, true); + } + } + } + } +} + +//computes the fog of war texture, contained in the minimap +void World::computeFow(){ + + //reset texture + minimap.resetFowTex(); + + //reset cells + for(int i=0; isetVisible(k, false); + } + } + } + } + + //compute cells + for(int i=0; igetUnitCount(); ++j){ + Unit *unit= getFaction(i)->getUnit(j); + + //exploration + if(unit->isOperative()){ + exploreCells(unit->getCenteredPos(), unit->getType()->getSight(), unit->getTeam()); + } + } + } + + //fire + for(int i=0; igetUnitCount(); ++j){ + Unit *unit= getFaction(i)->getUnit(j); + + //fire + ParticleSystem *fire= unit->getFire(); + if(fire!=NULL){ + fire->setActive(map.getSurfaceCell(Map::toSurfCoords(unit->getPos()))->isVisible(thisTeamIndex)); + } + } + } + + //compute texture + for(int i=0; igetTeam()==thisTeamIndex){ + for(int j=0; jgetUnitCount(); ++j){ + const Unit *unit= faction->getUnit(j); + if(unit->isOperative()){ + int sightRange= unit->getType()->getSight(); + + //iterate through all cells + PosCircularIterator pci(&map, unit->getPos(), sightRange+indirectSightRange); + while(pci.next()){ + Vec2i pos= pci.getPos(); + Vec2i surfPos= Map::toSurfCoords(pos); + + + //compute max alpha + float maxAlpha; + if(surfPos.x>1 && surfPos.y>1 && surfPos.x0 && surfPos.y>0 && surfPos.xgetPos().dist(pos); + if(dist>sightRange){ + alpha= clamp(1.f-(dist-sightRange)/(indirectSightRange), 0.f, maxAlpha); + } + else{ + alpha= maxAlpha; + } + minimap.incFowTextureAlphaSurface(surfPos, alpha); + } + } + } + } + } +} + +}}//end namespace