From 6dcd59d7bc7fe706475c2b2cf2ee1ed95c2ac9de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Beno=C3=AEt?= Date: Sat, 7 Apr 2012 13:06:03 +0200 Subject: [PATCH] New research and technology page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added legacyworlds-server-beans-technologies Maven module, including the player-level DAO and controller. * Added session classes to carry technology information, modified web client session façade accordingly * Various changes to common UI elements (forms, lists, etc...) so the start and end of some element can be drawn separately * Added controller, templates and JavaScript for research page --- .../lw/beans/empire/EmpireDAOBean.java | 12 - .../lw/beans/empire/EmpireManagementBean.java | 8 - .../pom.xml | 26 ++ .../src/main/java/.empty | 0 .../PlayerTechnologiesDAOBean.java | 143 +++++++ .../technologies/ResearchControllerBean.java | 197 +++++++++ .../game/technologies/ResearchRowMapper.java | 69 ++++ .../src/main/resources/.empty | 0 .../configuration/game/technologies.xml | 9 + .../src/test/java/.empty | 0 .../src/test/resources/.empty | 0 .../ImplementTechCommandDelegateBean.java | 56 --- .../tech/GetResearchCommandDelegateBean.java | 70 ++++ .../ImplementTechCommandDelegateBean.java | 72 ++++ ...ResearchPrioritiesCommandDelegateBean.java | 77 ++++ .../configuration/session-types/player.xml | 6 +- legacyworlds-server-beans/pom.xml | 1 + .../040-functions/045-empire-research.sql | 4 + .../lw/interfaces/game/EmpireDAO.java | 3 - .../lw/interfaces/game/EmpireManagement.java | 3 - .../technologies/PlayerTechnologiesDAO.java | 92 +++++ .../game/technologies/ResearchController.java | 69 ++++ legacyworlds-server-main/pom.xml | 4 + .../src/main/resources/configuration/game.xml | 1 + .../lw/cmd/player/ImplementTechCommand.java | 28 -- .../cmd/player/gdata/empire/ResearchData.java | 373 ++++++++++++++++++ .../cmd/player/tech/GetResearchCommand.java | 26 ++ .../cmd/player/tech/GetResearchResponse.java | 66 ++++ .../cmd/player/tech/ImplementTechCommand.java | 53 +++ .../tech/UpdateResearchPrioritiesCommand.java | 50 +++ .../deepclone/lw/web/csess/PlayerSession.java | 40 +- .../Raw/WEB-INF/fm/en/containers/game.ftl | 3 + .../Raw/WEB-INF/fm/en/types/overview.ftl | 1 - .../Raw/WEB-INF/fm/en/types/research.ftl | 255 ++++++++++++ .../Raw/WEB-INF/fm/fr/containers/game.ftl | 5 +- .../Raw/WEB-INF/fm/fr/types/overview.ftl | 1 - .../Raw/WEB-INF/fm/fr/types/research.ftl | 255 ++++++++++++ .../Content/Raw/WEB-INF/fm/layout/form.ftl | 15 +- .../Content/Raw/WEB-INF/fm/layout/lists.ftl | 22 +- .../Content/Raw/WEB-INF/fm/layout/tabs.ftl | 10 +- .../Content/Raw/css/main.css | 56 ++- .../Content/Raw/js/research.js | 87 ++++ .../lw/web/main/game/OverviewPage.java | 33 +- .../lw/web/main/game/ResearchPage.java | 186 +++++++++ legacyworlds/pom.xml | 5 + 45 files changed, 2314 insertions(+), 178 deletions(-) create mode 100644 legacyworlds-server-beans-technologies/pom.xml create mode 100644 legacyworlds-server-beans-technologies/src/main/java/.empty create mode 100644 legacyworlds-server-beans-technologies/src/main/java/com/deepclone/lw/beans/game/technologies/PlayerTechnologiesDAOBean.java create mode 100644 legacyworlds-server-beans-technologies/src/main/java/com/deepclone/lw/beans/game/technologies/ResearchControllerBean.java create mode 100644 legacyworlds-server-beans-technologies/src/main/java/com/deepclone/lw/beans/game/technologies/ResearchRowMapper.java create mode 100644 legacyworlds-server-beans-technologies/src/main/resources/.empty create mode 100644 legacyworlds-server-beans-technologies/src/main/resources/configuration/game/technologies.xml create mode 100644 legacyworlds-server-beans-technologies/src/test/java/.empty create mode 100644 legacyworlds-server-beans-technologies/src/test/resources/.empty delete mode 100644 legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/ImplementTechCommandDelegateBean.java create mode 100644 legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/tech/GetResearchCommandDelegateBean.java create mode 100644 legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/tech/ImplementTechCommandDelegateBean.java create mode 100644 legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/tech/UpdateResearchPrioritiesCommandDelegateBean.java create mode 100644 legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/technologies/PlayerTechnologiesDAO.java create mode 100644 legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/technologies/ResearchController.java delete mode 100644 legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/ImplementTechCommand.java create mode 100644 legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/gdata/empire/ResearchData.java create mode 100644 legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/tech/GetResearchCommand.java create mode 100644 legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/tech/GetResearchResponse.java create mode 100644 legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/tech/ImplementTechCommand.java create mode 100644 legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/tech/UpdateResearchPrioritiesCommand.java create mode 100644 legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/types/research.ftl create mode 100644 legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/types/research.ftl create mode 100644 legacyworlds-web-main/Content/Raw/js/research.js create mode 100644 legacyworlds-web-main/src/main/java/com/deepclone/lw/web/main/game/ResearchPage.java diff --git a/legacyworlds-server-beans-simple/src/main/java/com/deepclone/lw/beans/empire/EmpireDAOBean.java b/legacyworlds-server-beans-simple/src/main/java/com/deepclone/lw/beans/empire/EmpireDAOBean.java index 26a6d01..9d15e1e 100644 --- a/legacyworlds-server-beans-simple/src/main/java/com/deepclone/lw/beans/empire/EmpireDAOBean.java +++ b/legacyworlds-server-beans-simple/src/main/java/com/deepclone/lw/beans/empire/EmpireDAOBean.java @@ -27,7 +27,6 @@ public class EmpireDAOBean implements EmpireDAO { private JdbcTemplate dTemplate; - private StoredProc fImplementTech; private StoredProc fAddEmpEnemy; private StoredProc fAddAllEnemy; private StoredProc fRemoveEmpEnemy; @@ -42,10 +41,6 @@ public class EmpireDAOBean { this.dTemplate = new JdbcTemplate( dataSource ); - this.fImplementTech = new StoredProc( dataSource , "emp" , "implement_tech" ); - this.fImplementTech.addParameter( "empire_id" , Types.INTEGER ); - this.fImplementTech.addParameter( "line_id" , Types.INTEGER ); - this.fAddEmpEnemy = new StoredProc( dataSource , "emp" , "add_enemy_empire" ); this.fAddEmpEnemy.addParameter( "empire_id" , Types.INTEGER ); this.fAddEmpEnemy.addParameter( "enemy_name" , Types.VARCHAR ); @@ -141,13 +136,6 @@ public class EmpireDAOBean } - @Override - public void implementTechnology( int empireId , int lineId ) - { - this.fImplementTech.execute( empireId , lineId ); - } - - @Override public List< PlanetListData > getPlanetList( int empireId ) { diff --git a/legacyworlds-server-beans-simple/src/main/java/com/deepclone/lw/beans/empire/EmpireManagementBean.java b/legacyworlds-server-beans-simple/src/main/java/com/deepclone/lw/beans/empire/EmpireManagementBean.java index 4602ce0..2e3838b 100644 --- a/legacyworlds-server-beans-simple/src/main/java/com/deepclone/lw/beans/empire/EmpireManagementBean.java +++ b/legacyworlds-server-beans-simple/src/main/java/com/deepclone/lw/beans/empire/EmpireManagementBean.java @@ -146,14 +146,6 @@ public class EmpireManagementBean } - @Override - public EmpireResponse implementTechnology( int empireId , int techId ) - { - this.empireDao.implementTechnology( empireId , techId ); - return this.getOverview( empireId ); - } - - @Override public ListPlanetsResponse getPlanetList( int empireId ) { diff --git a/legacyworlds-server-beans-technologies/pom.xml b/legacyworlds-server-beans-technologies/pom.xml new file mode 100644 index 0000000..f3ddea4 --- /dev/null +++ b/legacyworlds-server-beans-technologies/pom.xml @@ -0,0 +1,26 @@ + + 4.0.0 + + + legacyworlds-server-beans + com.deepclone.lw + 1.0.0 + ../legacyworlds-server-beans/pom.xml + + + legacyworlds-server-beans-technologies + Legacy Worlds - Server - Components - Technologies + ${legacyworlds.version.main}.${legacyworlds.version.release}-${legacyworlds.version.build} + This module contains the components which manage and access in-universe technologies. + + + + + postgresql + postgresql + + + + + \ No newline at end of file diff --git a/legacyworlds-server-beans-technologies/src/main/java/.empty b/legacyworlds-server-beans-technologies/src/main/java/.empty new file mode 100644 index 0000000..e69de29 diff --git a/legacyworlds-server-beans-technologies/src/main/java/com/deepclone/lw/beans/game/technologies/PlayerTechnologiesDAOBean.java b/legacyworlds-server-beans-technologies/src/main/java/com/deepclone/lw/beans/game/technologies/PlayerTechnologiesDAOBean.java new file mode 100644 index 0000000..dc4e437 --- /dev/null +++ b/legacyworlds-server-beans-technologies/src/main/java/com/deepclone/lw/beans/game/technologies/PlayerTechnologiesDAOBean.java @@ -0,0 +1,143 @@ +package com.deepclone.lw.beans.game.technologies; + + +import java.sql.Types; +import java.util.List; + +import javax.sql.DataSource; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; + +import com.deepclone.lw.cmd.player.gdata.empire.ResearchData; +import com.deepclone.lw.interfaces.game.technologies.PlayerTechnologiesDAO; +import com.deepclone.lw.utils.StoredProc; + + + +/** + * Data access component for player technologies + * + *

+ * This class implements database queries and calls to stored procedures which allow players to + * access and manage their empires' research. + * + * @author E. Benoît + */ +class PlayerTechnologiesDAOBean + implements PlayerTechnologiesDAO +{ + + /** SQL query that fetches an empire's research state */ + private static final String Q_EMPIRE_RESEARCH = "SELECT * FROM emp.technologies_v2_view WHERE empire_id = ?"; + + /** Row mapper for research state entries */ + private final ResearchRowMapper mResearch; + + /** Stored procedure that initiates a research priority update */ + private StoredProc fResprioUpdateStart; + + /** Stored procedure that uploads a single research priority */ + private StoredProc fResprioUpdateSet; + + /** Stored procedure that applies a research priority update */ + private StoredProc fResprioUpdateApply; + + /** Stored procedure that implements a technology */ + private StoredProc fTechnologyImplement; + + /** Spring JDBC interface */ + private JdbcTemplate dTemplate; + + + /** Initialise the necessary row mappers */ + public PlayerTechnologiesDAOBean( ) + { + this.mResearch = new ResearchRowMapper( ); + } + + + /** + * Dependency injector that sets the data source and initialises stored procedures + * + * @param dataSource + * the data source + */ + @Autowired( required = true ) + public void setDataSource( DataSource dataSource ) + { + this.dTemplate = new JdbcTemplate( dataSource ); + + this.fResprioUpdateStart = new StoredProc( dataSource , "emp" , "resprio_update_start" ) + .addParameter( "_empire" , Types.INTEGER ) + .addOutput( "_result" , Types.BOOLEAN ); + + this.fResprioUpdateSet = new StoredProc( dataSource , "emp" , "resprio_update_set" ) + .addParameter( "_technology" , Types.VARCHAR ) + .addParameter( "_priority" , Types.INTEGER ) + .addOutput( "_result" , Types.BOOLEAN ); + + this.fResprioUpdateApply = new StoredProc( dataSource , "emp" , "resprio_update_apply" ) + .addOutput( "_result" , Types.BOOLEAN ); + + this.fTechnologyImplement = new StoredProc( dataSource , "emp" , "technology_implement" ) + .addParameter( "_empire" , Types.INTEGER ) + .addParameter( "_technology" , Types.VARCHAR ) + .addOutput( "_result" , Types.BOOLEAN ); + } + + + /** + * Query and map research entries for an empire + * + *

+ * Run the query against emp.technologies_view then map all rows using a + * {@link ResearchRowMapper}. + */ + @Override + public List< ResearchData > getResearchData( int empire ) + { + return this.dTemplate.query( Q_EMPIRE_RESEARCH , this.mResearch , empire ); + } + + + /** + * Call emp.resprio_update_start + */ + @Override + public boolean startPriorityUpdate( int empire ) + { + return (Boolean) this.fResprioUpdateStart.execute( empire ).get( "_result" ); + } + + + /** + * Call emp.resprio_update_set + */ + @Override + public boolean uploadResearchPriority( String identifier , int priority ) + { + return (Boolean) this.fResprioUpdateSet.execute( identifier , priority ).get( "_result" ); + } + + + /** + * Call emp.resprio_update_apply + */ + @Override + public boolean applyPriorityUpdate( ) + { + return (Boolean) this.fResprioUpdateApply.execute( ).get( "_result" ); + } + + + /** + * Call emp.technology_implement + */ + @Override + public boolean implement( int empire , String technology ) + { + return (Boolean) this.fTechnologyImplement.execute( empire , technology ).get( "_result" ); + } + +} diff --git a/legacyworlds-server-beans-technologies/src/main/java/com/deepclone/lw/beans/game/technologies/ResearchControllerBean.java b/legacyworlds-server-beans-technologies/src/main/java/com/deepclone/lw/beans/game/technologies/ResearchControllerBean.java new file mode 100644 index 0000000..c1c9388 --- /dev/null +++ b/legacyworlds-server-beans-technologies/src/main/java/com/deepclone/lw/beans/game/technologies/ResearchControllerBean.java @@ -0,0 +1,197 @@ +package com.deepclone.lw.beans.game.technologies; + + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; + +import com.deepclone.lw.cmd.player.gdata.GamePageData; +import com.deepclone.lw.cmd.player.gdata.empire.ResearchData; +import com.deepclone.lw.cmd.player.tech.GetResearchResponse; +import com.deepclone.lw.interfaces.game.EmpireManagement; +import com.deepclone.lw.interfaces.game.technologies.PlayerTechnologiesDAO; +import com.deepclone.lw.interfaces.game.technologies.ResearchController; +import com.deepclone.lw.interfaces.i18n.TranslationException; +import com.deepclone.lw.interfaces.i18n.Translator; + + + +/** + * Research control component + * + *

+ * This component implements all actions allowing a player to view and update its empire's research + * state. + * + * @author E. Benoît + */ +@Transactional +class ResearchControllerBean + implements ResearchController +{ + /** Main empire management component */ + private EmpireManagement empireManagement; + + /** String translator */ + private Translator translator; + + /** Data access object for technologies */ + private PlayerTechnologiesDAO playerTechnologiesDAO; + + + /** + * Dependency injector that sets the empire management component + * + * @param empireManagement + * the empire management component + */ + @Autowired( required = true ) + public void setEmpireManagement( EmpireManagement empireManagement ) + { + this.empireManagement = empireManagement; + } + + + /** + * Dependency injector that sets the translation component + * + * @param translator + * the translation component + */ + @Autowired( required = true ) + public void setTranslator( Translator translator ) + { + this.translator = translator; + } + + + /** + * Dependency injector that sets the data access object for technologies + * + * @param playerTechnologiesDAO + * the data access object for technologies + */ + @Autowired( required = true ) + public void setPlayerTechnologiesDAO( PlayerTechnologiesDAO playerTechnologiesDAO ) + { + this.playerTechnologiesDAO = playerTechnologiesDAO; + } + + + /** + * Get the raw research data from the database, then handle translation and dependencies + * + *

+ * In order to generate the response, this method fetches the raw research state information + * from the database. It then proceeds with translating all categories, names and descriptions, + * then adds reverse dependency information to all loaded technologies. + */ + @Override + public GetResearchResponse getResearchData( int empire ) + { + GamePageData pageData = this.empireManagement.getGeneralInformation( empire ); + List< ResearchData > technologies = this.playerTechnologiesDAO.getResearchData( empire ); + + // Translate categories, names and descriptions + Map< String , String > translations = this.getTranslationsFor( technologies , pageData.getLanguage( ) ); + Map< String , ResearchData > byId = new HashMap< String , ResearchData >( ); + for ( ResearchData tech : technologies ) { + byId.put( tech.getIdentifier( ) , tech ); + tech.setCategory( translations.get( tech.getCategory( ) ) ); + if ( tech.getName( ) != null ) { + tech.setName( translations.get( tech.getName( ) ) ); + tech.setDescription( translations.get( tech.getDescription( ) ) ); + } + } + + // Add reverse dependency identifiers + for ( ResearchData tech : technologies ) { + for ( String dependency : tech.getDependencies( ) ) { + byId.get( dependency ).addReverseDependency( tech.getIdentifier( ) ); + } + } + + return new GetResearchResponse( pageData , technologies ); + } + + + /** + * Get translations used by a list of raw research entries + * + *

+ * This method is fed the raw research state data from the player technologies data access + * object. It generates a set of string identifiers which contains all categories, names and + * descriptions, then fetches these strings' translations. + * + * @param technologies + * the research state data from the DAO + * @param language + * the player's selected language + * + * @return the map of string identifiers to string contents + */ + private Map< String , String > getTranslationsFor( List< ResearchData > technologies , String language ) + { + Set< String > identifiers = new HashSet< String >( ); + for ( ResearchData tech : technologies ) { + identifiers.add( tech.getCategory( ) ); + if ( tech.getName( ) != null ) { + identifiers.add( tech.getName( ) ); + identifiers.add( tech.getDescription( ) ); + } + } + + try { + return this.translator.translate( language , identifiers ); + } catch ( TranslationException e ) { + throw new RuntimeException( "error while translating technology-related data" , e ); + } + } + + + /** + * Update research priorities + * + *

+ * This method will start a research priority update for the specified empire, then upload each + * value in the map to the database. Finally, it will try to apply the update. + * + *

+ * If any of the above steps fail, the method will abort. + */ + @Override + public boolean updatePriorities( int empire , Map< String , Integer > priorities ) + { + if ( !this.playerTechnologiesDAO.startPriorityUpdate( empire ) ) { + return false; + } + + for ( Map.Entry< String , Integer > entry : priorities.entrySet( ) ) { + if ( !this.playerTechnologiesDAO.uploadResearchPriority( entry.getKey( ) , entry.getValue( ) ) ) { + return false; + } + } + + return this.playerTechnologiesDAO.applyPriorityUpdate( ); + } + + + /** + * Implement a technology + * + *

+ * Call the DAO's technology implementation method in order to access the corresponding stored + * procedure. + */ + @Override + public boolean implement( int empire , String technology ) + { + return this.playerTechnologiesDAO.implement( empire , technology ); + } + +} diff --git a/legacyworlds-server-beans-technologies/src/main/java/com/deepclone/lw/beans/game/technologies/ResearchRowMapper.java b/legacyworlds-server-beans-technologies/src/main/java/com/deepclone/lw/beans/game/technologies/ResearchRowMapper.java new file mode 100644 index 0000000..e69c979 --- /dev/null +++ b/legacyworlds-server-beans-technologies/src/main/java/com/deepclone/lw/beans/game/technologies/ResearchRowMapper.java @@ -0,0 +1,69 @@ +package com.deepclone.lw.beans.game.technologies; + + +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.postgresql.util.PGobject; + +import org.springframework.jdbc.core.RowMapper; + +import com.deepclone.lw.cmd.player.gdata.empire.ResearchData; + + + +/** + * Row mapper for empire research state entries + * + *

+ * This class is responsible for converting empire research state information rows into instances of + * the corresponding class, {@link ResearchData}. The instances it produces are "raw": they contain + * string identifiers instead of translations, and their reverse dependencies are not set. + * + * @author E. Benoît + * + */ +class ResearchRowMapper + implements RowMapper< ResearchData > +{ + + /** + * Map a row from emp.technologies_view + * + *

+ * This method maps a single row from the emp.technologies_view view into a + * {@link ResearchData} instance. + */ + @Override + public ResearchData mapRow( ResultSet rs , int rowNum ) + throws SQLException + { + ResearchData output = new ResearchData( ); + + output.setIdentifier( rs.getString( "emptech_id" ) ); + output.setCategory( rs.getString( "technology_category" ) ); + + if ( rs.getBoolean( "emptech_visible" ) ) { + output.setName( rs.getString( "technology_name" ) ); + output.setDescription( rs.getString( "technology_description" ) ); + output.setPrice( rs.getLong( "technology_price" ) ); + } + + int ratio = rs.getInt( "emptech_ratio" ); + if ( rs.wasNull( ) ) { + String state = ( (PGobject) rs.getObject( "emptech_state" ) ).getValue( ); + output.setImplemented( "KNOWN".equals( state ) ); + } else { + output.setCompletion( ratio ); + output.setPriority( rs.getInt( "emptech_priority" ) ); + } + + String dependencies = rs.getString( "technology_dependencies" ); + if ( ! "".equals( dependencies ) ) { + output.setDependencies( dependencies.split( "," ) ); + } + + return output; + } + +} diff --git a/legacyworlds-server-beans-technologies/src/main/resources/.empty b/legacyworlds-server-beans-technologies/src/main/resources/.empty new file mode 100644 index 0000000..e69de29 diff --git a/legacyworlds-server-beans-technologies/src/main/resources/configuration/game/technologies.xml b/legacyworlds-server-beans-technologies/src/main/resources/configuration/game/technologies.xml new file mode 100644 index 0000000..e0b7bdc --- /dev/null +++ b/legacyworlds-server-beans-technologies/src/main/resources/configuration/game/technologies.xml @@ -0,0 +1,9 @@ + + + + + + + diff --git a/legacyworlds-server-beans-technologies/src/test/java/.empty b/legacyworlds-server-beans-technologies/src/test/java/.empty new file mode 100644 index 0000000..e69de29 diff --git a/legacyworlds-server-beans-technologies/src/test/resources/.empty b/legacyworlds-server-beans-technologies/src/test/resources/.empty new file mode 100644 index 0000000..e69de29 diff --git a/legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/ImplementTechCommandDelegateBean.java b/legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/ImplementTechCommandDelegateBean.java deleted file mode 100644 index d6d7165..0000000 --- a/legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/ImplementTechCommandDelegateBean.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.deepclone.lw.beans.user.player.game; - - -import org.springframework.beans.factory.annotation.Autowired; - -import com.deepclone.lw.beans.user.abst.AutowiredCommandDelegate; -import com.deepclone.lw.beans.user.abst.SessionCommandHandler; -import com.deepclone.lw.beans.user.player.GameSubTypeBean; -import com.deepclone.lw.cmd.player.ImplementTechCommand; -import com.deepclone.lw.interfaces.game.EmpireManagement; -import com.deepclone.lw.interfaces.session.ServerSession; -import com.deepclone.lw.session.Command; -import com.deepclone.lw.session.CommandResponse; - - - -public class ImplementTechCommandDelegateBean - implements AutowiredCommandDelegate - -{ - - private EmpireManagement empireManagement; - - - @Autowired( required = true ) - public void setEmpireManager( EmpireManagement manager ) - { - this.empireManagement = manager; - } - - - @Override - public Class< ? extends Command > getType( ) - { - return ImplementTechCommand.class; - } - - - @Override - public Class< ? extends SessionCommandHandler > getCommandHandler( ) - { - return GameSubTypeBean.class; - } - - - @Override - public CommandResponse execute( ServerSession session , Command cParam ) - { - ImplementTechCommand command = (ImplementTechCommand) cParam; - int empireId = session.get( "empireId" , Integer.class ); - if ( session.get( "vacation" , Boolean.class ) ) { - return this.empireManagement.getOverview( empireId ); - } - return this.empireManagement.implementTechnology( empireId , command.getTech( ) ); - } -} diff --git a/legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/tech/GetResearchCommandDelegateBean.java b/legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/tech/GetResearchCommandDelegateBean.java new file mode 100644 index 0000000..8735745 --- /dev/null +++ b/legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/tech/GetResearchCommandDelegateBean.java @@ -0,0 +1,70 @@ +package com.deepclone.lw.beans.user.player.game.tech; + + +import org.springframework.beans.factory.annotation.Autowired; + +import com.deepclone.lw.beans.user.abst.AutowiredCommandDelegate; +import com.deepclone.lw.beans.user.abst.SessionCommandHandler; +import com.deepclone.lw.beans.user.player.GameSubTypeBean; +import com.deepclone.lw.cmd.player.tech.GetResearchCommand; +import com.deepclone.lw.interfaces.game.technologies.ResearchController; +import com.deepclone.lw.interfaces.session.ServerSession; +import com.deepclone.lw.session.Command; +import com.deepclone.lw.session.CommandResponse; + + + +/** + * Command delegate for {@link GetResearchCommand} + * + *

+ * This command delegate uses the {@link ResearchController} to obtain all information pertaining to + * an empire's research. + * + * @author E. Benoît + */ +class GetResearchCommandDelegateBean + implements AutowiredCommandDelegate +{ + /** The research controller */ + private ResearchController researchController; + + + /** + * Dependency injector that sets the research controller + * + * @param researchController + * the research controller + */ + @Autowired + public void setResearchController( ResearchController researchController ) + { + this.researchController = researchController; + } + + + /** This delegate handles {@link GetResearchCommand} instances */ + @Override + public Class< ? extends Command > getType( ) + { + return GetResearchCommand.class; + } + + + /** This delegate is part of the game session sub-type */ + @Override + public Class< ? extends SessionCommandHandler > getCommandHandler( ) + { + return GameSubTypeBean.class; + } + + + /** When the command is executed, fetch relevant data from the research controller */ + @Override + public CommandResponse execute( ServerSession session , Command command ) + { + int empire = session.get( "empireId" , Integer.class ); + return this.researchController.getResearchData( empire ); + } + +} diff --git a/legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/tech/ImplementTechCommandDelegateBean.java b/legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/tech/ImplementTechCommandDelegateBean.java new file mode 100644 index 0000000..5b13636 --- /dev/null +++ b/legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/tech/ImplementTechCommandDelegateBean.java @@ -0,0 +1,72 @@ +package com.deepclone.lw.beans.user.player.game.tech; + + +import org.springframework.beans.factory.annotation.Autowired; + +import com.deepclone.lw.beans.user.abst.AutowiredCommandDelegate; +import com.deepclone.lw.beans.user.abst.SessionCommandHandler; +import com.deepclone.lw.beans.user.player.GameSubTypeBean; +import com.deepclone.lw.cmd.player.tech.ImplementTechCommand; +import com.deepclone.lw.interfaces.game.technologies.ResearchController; +import com.deepclone.lw.interfaces.session.ServerSession; +import com.deepclone.lw.session.Command; +import com.deepclone.lw.session.CommandResponse; +import com.deepclone.lw.session.NullResponse; + + + +public class ImplementTechCommandDelegateBean + implements AutowiredCommandDelegate + +{ + /** The research controller */ + private ResearchController researchController; + + + /** + * Dependency injector that sets the research controller + * + * @param researchController + * the research controller + */ + @Autowired( required = true ) + public void setResearchController( ResearchController researchController ) + { + this.researchController = researchController; + } + + + /** This class handles {@link ImplementTechCommand} instances */ + @Override + public Class< ? extends Command > getType( ) + { + return ImplementTechCommand.class; + } + + + /** This class is enabled for the {@link GameSubTypeBean} session type */ + @Override + public Class< ? extends SessionCommandHandler > getCommandHandler( ) + { + return GameSubTypeBean.class; + } + + + /** + * Implement a technology + * + *

+ * If the empire is not in vacation mode, try to implement the technology. Always return a + * {@link NullResponse}. + */ + @Override + public CommandResponse execute( ServerSession session , Command cParam ) + { + ImplementTechCommand command = (ImplementTechCommand) cParam; + int empire = session.get( "empireId" , Integer.class ); + if ( !session.get( "vacation" , Boolean.class ) ) { + this.researchController.implement( empire , command.getTech( ) ); + } + return new NullResponse( ); + } +} diff --git a/legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/tech/UpdateResearchPrioritiesCommandDelegateBean.java b/legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/tech/UpdateResearchPrioritiesCommandDelegateBean.java new file mode 100644 index 0000000..c3dc103 --- /dev/null +++ b/legacyworlds-server-beans-user/src/main/java/com/deepclone/lw/beans/user/player/game/tech/UpdateResearchPrioritiesCommandDelegateBean.java @@ -0,0 +1,77 @@ +package com.deepclone.lw.beans.user.player.game.tech; + + +import org.springframework.beans.factory.annotation.Autowired; + +import com.deepclone.lw.beans.user.abst.AutowiredCommandDelegate; +import com.deepclone.lw.beans.user.abst.SessionCommandHandler; +import com.deepclone.lw.beans.user.player.GameSubTypeBean; +import com.deepclone.lw.cmd.player.tech.UpdateResearchPrioritiesCommand; +import com.deepclone.lw.interfaces.game.technologies.ResearchController; +import com.deepclone.lw.interfaces.session.ServerSession; +import com.deepclone.lw.session.Command; +import com.deepclone.lw.session.CommandResponse; +import com.deepclone.lw.session.NullResponse; + + + +/** + * Command handler for research priority updates + * + * @author E. Benoît + */ +class UpdateResearchPrioritiesCommandDelegateBean + implements AutowiredCommandDelegate +{ + /** The research controller */ + private ResearchController researchController; + + + /** + * Dependency injector that sets the research controller + * + * @param researchController + * the research controller + */ + @Autowired( required = true ) + public void setResearchController( ResearchController researchController ) + { + this.researchController = researchController; + } + + + /** This class handles {@link UpdateResearchPrioritiesCommand} instances */ + @Override + public Class< ? extends Command > getType( ) + { + return UpdateResearchPrioritiesCommand.class; + } + + + /** This class is enabled for the {@link GameSubTypeBean} session type */ + @Override + public Class< ? extends SessionCommandHandler > getCommandHandler( ) + { + return GameSubTypeBean.class; + } + + + /** + * Update research priorities + * + *

+ * If the empire is not in vacation mode, access the research controller and update mining + * priorities using the specified values. Return a {@link NullResponse} in all cases. + */ + @Override + public CommandResponse execute( ServerSession session , Command command ) + { + if ( !session.get( "vacation" , Boolean.class ) ) { + int empireId = session.get( "empireId" , Integer.class ); + this.researchController.updatePriorities( empireId , + ( (UpdateResearchPrioritiesCommand) command ).getPriorities( ) ); + } + return new NullResponse( ); + } + +} diff --git a/legacyworlds-server-beans-user/src/main/resources/configuration/session-types/player.xml b/legacyworlds-server-beans-user/src/main/resources/configuration/session-types/player.xml index ff9a382..8398256 100644 --- a/legacyworlds-server-beans-user/src/main/resources/configuration/session-types/player.xml +++ b/legacyworlds-server-beans-user/src/main/resources/configuration/session-types/player.xml @@ -44,9 +44,13 @@ - + + + + + diff --git a/legacyworlds-server-beans/pom.xml b/legacyworlds-server-beans/pom.xml index a03d945..d2c3b59 100644 --- a/legacyworlds-server-beans/pom.xml +++ b/legacyworlds-server-beans/pom.xml @@ -30,6 +30,7 @@ ../legacyworlds-server-beans-resources ../legacyworlds-server-beans-simple ../legacyworlds-server-beans-system + ../legacyworlds-server-beans-technologies ../legacyworlds-server-beans-updates ../legacyworlds-server-beans-user diff --git a/legacyworlds-server-data/db-structure/parts/040-functions/045-empire-research.sql b/legacyworlds-server-data/db-structure/parts/040-functions/045-empire-research.sql index c25bd64..7860984 100644 --- a/legacyworlds-server-data/db-structure/parts/040-functions/045-empire-research.sql +++ b/legacyworlds-server-data/db-structure/parts/040-functions/045-empire-research.sql @@ -134,6 +134,10 @@ REVOKE EXECUTE ON FUNCTION emp.technology_make_identifier( INT , TEXT , BOOLEAN ) FROM PUBLIC; +GRANT EXECUTE + ON FUNCTION emp.technology_make_identifier( INT , TEXT , BOOLEAN ) + TO :dbuser; + /* * Initialise a research priorities update diff --git a/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/EmpireDAO.java b/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/EmpireDAO.java index 291ba89..68f9159 100644 --- a/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/EmpireDAO.java +++ b/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/EmpireDAO.java @@ -23,9 +23,6 @@ public interface EmpireDAO public OverviewData getOverview( int empireId ); - public void implementTechnology( int empireId , int lineId ); - - public List< PlanetListData > getPlanetList( int empireId ); diff --git a/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/EmpireManagement.java b/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/EmpireManagement.java index eb92be9..26c27d6 100644 --- a/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/EmpireManagement.java +++ b/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/EmpireManagement.java @@ -22,9 +22,6 @@ public interface EmpireManagement public EmpireResponse getOverview( int empireId ); - public EmpireResponse implementTechnology( int empireId , int techId ); - - public ListPlanetsResponse getPlanetList( int empireId ); diff --git a/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/technologies/PlayerTechnologiesDAO.java b/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/technologies/PlayerTechnologiesDAO.java new file mode 100644 index 0000000..e0c349e --- /dev/null +++ b/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/technologies/PlayerTechnologiesDAO.java @@ -0,0 +1,92 @@ +package com.deepclone.lw.interfaces.game.technologies; + + +import java.util.List; + +import com.deepclone.lw.cmd.player.gdata.empire.ResearchData; + + + +/** + * Player-oriented data access interface for technologies + * + * @author E. Benoît + */ +public interface PlayerTechnologiesDAO +{ + + /** + * Obtain the list of technologies for an empire + * + *

+ * Query the database for technology entries as seen from an empire's perspective. The + * technologies will be returned with I18N string identifiers for categories, names and + * descriptions (unless the details are not available). + * + * @param empire + * the empire's identifier + * + * @return the list of technology entries + */ + public List< ResearchData > getResearchData( int empire ); + + + /** + * Start a research priority update + * + *

+ * This method initialises a research priorities update by calling the appropriate stored + * procedure. + * + * @param empire + * the empire for which an update is being performed + * + * @return true on success, false on failure + */ + public boolean startPriorityUpdate( int empire ); + + + /** + * Upload a single research priority + * + *

+ * This method uploads the priority for one of the in-progress research items. It must be called + * only after a research priority update has been initiated. + * + * @param identifier + * the technology's identifier + * @param priority + * the research priority + * + * @return true on success, false on failure + */ + public boolean uploadResearchPriority( String identifier , int priority ); + + + /** + * Apply an in-progress research priority update + * + *

+ * This method applies a previously uploaded set of research priority updates. + * + * @return true on success, false on failure + */ + public boolean applyPriorityUpdate( ); + + + /** + * Implement a technology + * + *

+ * This method calls the stored procedure which causes an empire to implement a technology. + * + * @param empire + * the empire's identifier + * @param technology + * the technology's identifier + * + * @return true on success, false on failure + */ + public boolean implement( int empire , String technology ); + +} diff --git a/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/technologies/ResearchController.java b/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/technologies/ResearchController.java new file mode 100644 index 0000000..e32d840 --- /dev/null +++ b/legacyworlds-server-interfaces/src/main/java/com/deepclone/lw/interfaces/game/technologies/ResearchController.java @@ -0,0 +1,69 @@ +package com.deepclone.lw.interfaces.game.technologies; + + +import java.util.Map; + +import com.deepclone.lw.cmd.player.tech.GetResearchCommand; +import com.deepclone.lw.cmd.player.tech.GetResearchResponse; + + + +/** + * Research control interface + * + *

+ * This interface is implemented by the component which allows empires (and therefore players) to + * view the research state and control it. + * + * @author E. Benoît + */ +public interface ResearchController +{ + + /** + * Obtain the state of an empire's research + * + *

+ * This method must be overridden to generate the response to a {@link GetResearchCommand}. + * + * @param empire + * the empire's identifier + * + * @return the appropriate {@link GetResearchResponse} instance + */ + public GetResearchResponse getResearchData( int empire ); + + + /** + * Update research priorities + * + *

+ * This method must be implemented so it updates research priorities for an empire. + * + * @param empire + * the empire to update + * @param priorities + * the new research priorities, as a map associating technology identifiers to + * priority values + * + * @return true on success, false otherwise + */ + public boolean updatePriorities( int empire , Map< String , Integer > priorities ); + + + /** + * Implement a technology + * + *

+ * This method implements a technology which has been fully researched. + * + * @param empire + * the empire trying to implement a technology + * @param technology + * the technology's identifier + * + * @return true on success, false otherwise + */ + public boolean implement( int empire , String technology ); + +} diff --git a/legacyworlds-server-main/pom.xml b/legacyworlds-server-main/pom.xml index 1e4af1b..86b8a84 100644 --- a/legacyworlds-server-main/pom.xml +++ b/legacyworlds-server-main/pom.xml @@ -50,6 +50,10 @@ legacyworlds-server-beans-system com.deepclone.lw + + legacyworlds-server-beans-technologies + com.deepclone.lw + legacyworlds-server-beans-updates com.deepclone.lw diff --git a/legacyworlds-server-main/src/main/resources/configuration/game.xml b/legacyworlds-server-main/src/main/resources/configuration/game.xml index 2702f02..8a6c8d6 100644 --- a/legacyworlds-server-main/src/main/resources/configuration/game.xml +++ b/legacyworlds-server-main/src/main/resources/configuration/game.xml @@ -8,6 +8,7 @@ + \ No newline at end of file diff --git a/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/ImplementTechCommand.java b/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/ImplementTechCommand.java deleted file mode 100644 index 9a26ed7..0000000 --- a/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/ImplementTechCommand.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.deepclone.lw.cmd.player; - - -import com.deepclone.lw.session.Command; - - - -public class ImplementTechCommand - extends Command -{ - - private static final long serialVersionUID = 1L; - - private final int tech; - - - public ImplementTechCommand( int tech ) - { - this.tech = tech; - } - - - public int getTech( ) - { - return tech; - } - -} diff --git a/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/gdata/empire/ResearchData.java b/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/gdata/empire/ResearchData.java new file mode 100644 index 0000000..67add05 --- /dev/null +++ b/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/gdata/empire/ResearchData.java @@ -0,0 +1,373 @@ +package com.deepclone.lw.cmd.player.gdata.empire; + + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + + + +/** + * An entry from the research page + * + *

+ * This class represents entries from the research page. It is capable of representing in-progress + * research (with or without details), as well as pending and implemented technologies. + * + * @author E. Benoît + */ +public class ResearchData + implements Serializable , Comparable< ResearchData > +{ + + /** + * The serialisation version identifier + * + *

+ */ + private static final long serialVersionUID = 1L; + + /** The identifier of the technology */ + private String identifier; + + /** The category the technology belongs to */ + private String category; + + /** + * The translated name of the technology, or null if the technology is being + * researched and progress is insufficient to display details. + */ + private String name; + + /** + * The description of the technology, or null if the technology is being researched + * and progress is insufficient to display details. + */ + private String description; + + /** + * The implementation price of the technology, or null if the technology is being + * researched and progress is insufficient to display details. + */ + private Long price; + + /** + * The completion percentage of the technology, or null if the technology has been + * fully researched. + */ + private Integer completion; + + /** + * Priority of the research on this technology, or null if the technology is not + * being researched. + */ + private Integer priority; + + /** + * Whether the technology is being researched (null), pending implementation ( + * false) or implemented (true). + */ + private Boolean implemented; + + /** List of identifiers of technologies the current technology depends on. */ + private List< String > dependencies = new ArrayList< String >( ); + + /** List of identifiers of technologies that depend on the current technology. */ + private List< String > revDependencies = new LinkedList< String >( ); + + + /** + * Gets the identifier of the technology. + * + * @return the identifier of the technology + */ + public String getIdentifier( ) + { + return this.identifier; + } + + + /** + * Sets the identifier of the technology. + * + * @param identifier + * the new identifier of the technology + */ + public void setIdentifier( String identifier ) + { + this.identifier = identifier; + } + + + /** + * Gets the category the technology belongs to. + * + * @return the category the technology belongs to + */ + public String getCategory( ) + { + return this.category; + } + + + /** + * Sets the category the technology belongs to. + * + * @param category + * the new category the technology belongs to + */ + public void setCategory( String category ) + { + this.category = category; + } + + + /** + * Gets the translated name of the technology + * + * @return the translated name of the technology, or null if the technology is + * being researched and progress is insufficient to display details + */ + public String getName( ) + { + return this.name; + } + + + /** + * Sets the translated name of the technology + * + * @param name + * the new translated name of the technology + */ + public void setName( String name ) + { + this.name = name; + } + + + /** + * Gets the description of the technology + * + * @return the description of the technology, or null if the technology is being + * researched and progress is insufficient to display details + */ + public String getDescription( ) + { + return this.description; + } + + + /** + * Sets the description of the technology + * + * @param description + * the new description of the technology + */ + public void setDescription( String description ) + { + this.description = description; + } + + + /** + * Gets the implementation price of the technology + * + * @return the implementation price of the technology, or null if the technology is + * being researched and progress is insufficient to display details + */ + public Long getPrice( ) + { + return this.price; + } + + + /** + * Sets the implementation price of the technology + * + * @param price + * the new implementation price of the technology + */ + public void setPrice( Long price ) + { + this.price = price; + } + + + /** + * Gets the completion percentage of the technology. + * + * @return the completion percentage of the technology, or null if the technology + * has been fully researched + */ + public Integer getCompletion( ) + { + return this.completion; + } + + + /** + * Sets the completion percentage of the technology. + * + * @param completion + * the new completion percentage of the technology + */ + public void setCompletion( Integer completion ) + { + this.completion = completion; + } + + + /** + * Gets the priority of the research on this technology + * + * @return the priority of the research on this technology, or null if the + * technology is not being researched + */ + public Integer getPriority( ) + { + return this.priority; + } + + + /** + * Sets the priority of the research on this technology. + * + * @param priority + * the new priority of the research on this technology + */ + public void setPriority( Integer priority ) + { + this.priority = priority; + } + + + /** + * Gets whether the technology is being researched (null), pending implementation ( + * false) or implemented (true). + * + * @return whether the technology is being researched (null), pending + * implementation ( false) or implemented (true) + */ + public Boolean getImplemented( ) + { + return this.implemented; + } + + + /** + * Sets whether the technology is being researched (null), pending implementation ( + * false) or implemented (true). + * + * @param implemented + * whether the technology is being researched (null), pending + * implementation ( false) or implemented (true) + */ + public void setImplemented( Boolean implemented ) + { + this.implemented = implemented; + } + + + /** + * Get the list of dependencies + * + * @return the list of dependencies + */ + public List< String > getDependencies( ) + { + return Collections.unmodifiableList( this.dependencies ); + } + + + /** + * Update the list of dependencies + * + * @param dependencies + * the new list of dependencies + */ + public void setDependencies( String[] dependencies ) + { + this.dependencies = Arrays.asList( dependencies ); + } + + + /** + * Get the list of reverse dependencies + * + * @return the list of reverse dependencies + */ + public List< String > getReverseDependencies( ) + { + return this.revDependencies; + } + + + /** + * Add a reverse dependency + * + * @param identifier + * the identifier of the reverse dependency + */ + public void addReverseDependency( String identifier ) + { + this.revDependencies.add( identifier ); + } + + + /** + * Research page entry comparison + * + *

+ * This method compares research entries based on: + *

+ * + * @param other + * the entry to compare to + * + * @return 1 if the current entry is "bigger", -1 if it is "smaller", and 0 if both entries are + * equal + */ + @Override + public int compareTo( ResearchData other ) + { + if ( other == null ) { + return 1; + } + + if ( this.implemented == null && other.implemented != null ) { + return -1; + } + if ( other.implemented == null && this.implemented != null ) { + return 1; + } + + if ( this.implemented != null ) { + if ( this.implemented && !other.implemented ) { + return 1; + } + if ( other.implemented && !this.implemented ) { + return -1; + } + return this.name.compareTo( other.name ); + } + + if ( this.completion != other.completion ) { + return other.completion - this.completion; + } + if ( this.name == null ) { + return other.name == null ? 0 : -1; + } + return this.name.compareTo( other.name ); + } + +} diff --git a/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/tech/GetResearchCommand.java b/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/tech/GetResearchCommand.java new file mode 100644 index 0000000..f5961c3 --- /dev/null +++ b/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/tech/GetResearchCommand.java @@ -0,0 +1,26 @@ +package com.deepclone.lw.cmd.player.tech; + + +import com.deepclone.lw.session.Command; + + + +/** + * Command that obtains the current research & technology data + * + * @author E. Benoît + */ +public class GetResearchCommand + extends Command +{ + + /** + * The serialisation version identifier + * + * + */ + private static final long serialVersionUID = 1L; + +} diff --git a/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/tech/GetResearchResponse.java b/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/tech/GetResearchResponse.java new file mode 100644 index 0000000..22fd824 --- /dev/null +++ b/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/tech/GetResearchResponse.java @@ -0,0 +1,66 @@ +package com.deepclone.lw.cmd.player.tech; + + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import com.deepclone.lw.cmd.player.gdata.GamePageData; +import com.deepclone.lw.cmd.player.gdata.GameResponseBase; +import com.deepclone.lw.cmd.player.gdata.empire.ResearchData; + + + +/** + * Response that lists all research entries (sent by the server in response to + * {@link GetResearchCommand}) + * + * @author E. Benoît + */ +public class GetResearchResponse + extends GameResponseBase +{ + + /** + * The serialisation version identifier + * + * + */ + private static final long serialVersionUID = 1L; + + /** Sorted list of research entries */ + private final List< ResearchData > researchData; + + + /** + * Initialise the response + * + *

+ * Copy and sort the list of research entries + * + * @param page + * the common page information + * @param technologies + * the list of research entries + */ + public GetResearchResponse( GamePageData page , List< ResearchData > technologies ) + { + super( page ); + this.researchData = new LinkedList< ResearchData >( technologies ); + Collections.sort( this.researchData ); + } + + + /** + * Get the list of research entries + * + * @return the sorted list of research entries + */ + public List< ResearchData > getResearchData( ) + { + return this.researchData; + } + +} diff --git a/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/tech/ImplementTechCommand.java b/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/tech/ImplementTechCommand.java new file mode 100644 index 0000000..b3dbc93 --- /dev/null +++ b/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/tech/ImplementTechCommand.java @@ -0,0 +1,53 @@ +package com.deepclone.lw.cmd.player.tech; + + +import com.deepclone.lw.session.Command; + + + +/** + * Technology implementation command + * + * @author E. Benoît + */ +public class ImplementTechCommand + extends Command +{ + + /** + * The serialisation version identifier + * + *

+ */ + private static final long serialVersionUID = 2L; + + /** Identifier of the technology to implement */ + private final String tech; + + + /** + * Initialise the command + * + * @param tech + * the identifier of the technology to implement + */ + public ImplementTechCommand( String tech ) + { + this.tech = tech; + } + + + /** + * Get the identifier of the technology to implement + * + * @return the identifier of the technology to implement + */ + public String getTech( ) + { + return this.tech; + } + +} diff --git a/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/tech/UpdateResearchPrioritiesCommand.java b/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/tech/UpdateResearchPrioritiesCommand.java new file mode 100644 index 0000000..4dd5b08 --- /dev/null +++ b/legacyworlds-session/src/main/java/com/deepclone/lw/cmd/player/tech/UpdateResearchPrioritiesCommand.java @@ -0,0 +1,50 @@ +package com.deepclone.lw.cmd.player.tech; + + +import java.util.Map; + +import com.deepclone.lw.session.Command; + + + +/** + * Command that updates research priorities + * + * @author E. Benoît + */ +public class UpdateResearchPrioritiesCommand + extends Command +{ + + /** + * Serialisation version identifier + * + * + */ + private static final long serialVersionUID = 1L; + + /** The new research priorities */ + private final Map< String , Integer > priorities; + + + /** + * Initialise the command using research priority values + * + * @param priorities + * a map that associates technology identifiers to priorities + */ + public UpdateResearchPrioritiesCommand( Map< String , Integer > priorities ) + { + this.priorities = priorities; + } + + + /** @return the new research priorities */ + public Map< String , Integer > getPriorities( ) + { + return this.priorities; + } + +} diff --git a/legacyworlds-web-beans/src/main/java/com/deepclone/lw/web/csess/PlayerSession.java b/legacyworlds-web-beans/src/main/java/com/deepclone/lw/web/csess/PlayerSession.java index 6d132af..f47b989 100644 --- a/legacyworlds-web-beans/src/main/java/com/deepclone/lw/web/csess/PlayerSession.java +++ b/legacyworlds-web-beans/src/main/java/com/deepclone/lw/web/csess/PlayerSession.java @@ -17,6 +17,10 @@ import com.deepclone.lw.cmd.player.elist.*; import com.deepclone.lw.cmd.player.fleets.*; import com.deepclone.lw.cmd.player.gdata.*; import com.deepclone.lw.cmd.player.planets.*; +import com.deepclone.lw.cmd.player.tech.GetResearchCommand; +import com.deepclone.lw.cmd.player.tech.GetResearchResponse; +import com.deepclone.lw.cmd.player.tech.ImplementTechCommand; +import com.deepclone.lw.cmd.player.tech.UpdateResearchPrioritiesCommand; import com.deepclone.lw.cmd.player.msgs.*; import com.deepclone.lw.session.Command; import com.deepclone.lw.session.SessionException; @@ -78,10 +82,41 @@ public class PlayerSession } - public EmpireResponse implementTechnology( int technology ) + /** + * Request research information + * + * @return the research information response + */ + public GetResearchResponse getResearch( ) throws SessionException , SessionServerException , SessionMaintenanceException { - return (EmpireResponse) this.execute( new ImplementTechCommand( technology ) ); + return (GetResearchResponse) this.execute( new GetResearchCommand( ) ); + } + + + /** + * Update research priorities + * + * @param priorities + * the map of research identifiers to priority values + */ + public void updateResearchPriorities( Map< String , Integer > priorities ) + throws SessionException , SessionServerException , SessionMaintenanceException + { + this.execute( new UpdateResearchPrioritiesCommand( priorities ) ); + } + + + /** + * Implement a technology + * + * @param technology + * the technology's identifier + */ + public void implementTechnology( String technology ) + throws SessionException , SessionServerException , SessionMaintenanceException + { + this.execute( new ImplementTechCommand( technology ) ); } @@ -618,4 +653,5 @@ public class PlayerSession { return (PostCommentResponse) this.execute( new PostCommentCommand( bugId , comment ) ); } + } diff --git a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/containers/game.ftl b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/containers/game.ftl index e74fbd5..ce53339 100644 --- a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/containers/game.ftl +++ b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/containers/game.ftl @@ -63,6 +63,9 @@
Map
+
+ Research +
Alliance
diff --git a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/types/overview.ftl b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/types/overview.ftl index 4eae28b..b0a711d 100644 --- a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/types/overview.ftl +++ b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/types/overview.ftl @@ -2,7 +2,6 @@ <@page title="Empire"> <#assign ov = data.overview > - <#assign rs = data.research > <@tabs> diff --git a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/types/research.ftl b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/types/research.ftl new file mode 100644 index 0000000..7e84965 --- /dev/null +++ b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/en/types/research.ftl @@ -0,0 +1,255 @@ +<#-- + Macro that starts a research tab for one of the 3 existing modes + + Parameters: + curMode The tab to start: R for "in progress", P for pending, K for implemented + --> +<#macro startResearchTab curMode> + <#switch curMode> + + <#case 'R'> + <@tabStart "research" "In progress" /> + <@rawFormStart "research-set-priorities" "form-priorities" "research" /> + <@listviewStart /> + <@lv_line headers=true> + <@lv_column width=150>Category + <@lv_column width="x">Name + <@lv_column width=100 centered=true>Progress + <@lv_column width=150 centered=true>Priority + + <#break> + + <#case 'P'> + <@tabStart "pending" "Pending implementation" /> + <@listviewStart /> + <@lv_line headers=true> + <@lv_column width=150>Category + <@lv_column width="x">Name + <@lv_column width=150 right=true>Cost + <@lv_column width=100 centered=true>  + + <#break> + + <#case 'K'> + <@tabStart "implemented" "Implemented" /> + <@listviewStart /> + <@lv_line headers=true> + <@lv_column width=150>Category + <@lv_column width="x">Name + + <#break> + + + +<#-- + Macro that finishes a research tab depending on its type + + Parameters: + mode The type of tab that is being closed + --> +<#macro endResearchTab mode> + <#switch mode> + + <#case 'R'> + <@lv_line headers=true> + <@lv_column width="x" colspan=4>  + + <@lv_line> + <@lv_column colspan=3>  + <@lv_column centered=true> + + + + <@listviewEnd /> + + <#break> + + <#case 'P'> + <#case 'K'> + <@listviewEnd /> + <#break> + + + <@tabEnd /> + +<#-- + Macro that renders an in-progress research entry + + Parameters: + entry the entry +--> +<#macro drawInProgressResearch entry> + <@lv_line id="tl-${entry.identifier?xhtml}" class="tech-line"> + <@lv_column>${entry.category?xhtml} + <@lv_column> + <#if entry.name?has_content> + ${entry.name?xhtml} + <#else> + Unknown technology + + + <@lv_column centered=true>${entry.completion} % + <@lv_column centered=true> + + + + +<#-- + Macro that renders a pending technology + + Parameters: + entry the technology +--> +<#macro drawPendingTechnology entry> + <@lv_line id="tl-${entry.identifier?xhtml}" class="tech-line"> + <@lv_column>${entry.category?xhtml} + <@lv_column> + ${entry.name?xhtml} + + <@lv_column right=true> + ${entry.price?string(",##0")} <@abbr_bgc/> + + <@lv_column right=true> + <#if data.page.cash >= entry.price> + <@rawFormStart "research-implement" "form-implement-${entry.identifier}" entry.identifier /> + + + + <#else> +   + + + + +<#-- + Macro that renders an implemented technology + + Parameters: + entry the technology +--> +<#macro drawImplementedTechnology entry> + <@lv_line id="tl-${entry.identifier?xhtml}" class="tech-line"> + <@lv_column>${entry.category?xhtml} + <@lv_column> + ${entry.name?xhtml} + + + +<#-- + Macro that renders a single entry + + Parameters: + mode the current tab (see startResearchTab for details) + entry the entry +--> +<#macro drawResearchEntry mode entry> + <#switch mode> + + <#case 'R'> + <@drawInProgressResearch entry /> + <#break> + + <#case 'P'> + <@drawPendingTechnology entry /> + <#break> + + <#case 'K'> + <@drawImplementedTechnology entry /> + <#break> + + + +<#-- + Render the visible part of the research page + + Parameters: + entries the sorted list of research entries + --> +<#macro renderResearchTabs entries> + <#local prevMode = ''> + + <@tabs> + <#list entries as entry> + + <#-- Determine type of entry to display --> + <#if entry.implemented?has_content> + <#if entry.implemented> + <#local curMode = 'K'> + <#else> + <#local curMode = 'P'> + + <#else> + <#local curMode = 'R'> + + + <#-- Start/end tabs --> + <#if curMode != prevMode> + <#if prevMode != ''> + <@endResearchTab prevMode /> + + <@startResearchTab curMode /> + <#local prevMode = curMode> + + + + <@drawResearchEntry curMode entry /> + + + <@endResearchTab prevMode /> + + +<#-- + Render all research information in a hidden layer which will be used by + the client-side code to display more information. + --> +<#macro renderFullResearchData entries> +
+ <#list entries as tech> +
+

<#if tech.name?has_content>${tech.name?xhtml}<#else>Unknown technology

+
Category: ${tech.category?xhtml}
+ <#if tech.description?has_content>
${tech.description?xhtml}
+ <#if tech.price?has_content> +
Implementation cost: ${tech.price?string(",##0")} <@abbr_bgc />
+ + <#if tech.dependencies?has_content> +
Depends on: +
    + <#list tech.dependencies as depId> +
  • ${depId}
  • + +
+
+ + <#if tech.reverseDependencies?has_content> +
Required by: +
    + <#list tech.reverseDependencies as depId> +
  • ${depId}
  • + +
+
+ +
+ +
+ + +<#-- + Main research page + --> +<#macro render> +<@page title="Research"> + + <#local entries = data.researchData> + <@renderResearchTabs entries /> + <@renderFullResearchData entries /> + + + \ No newline at end of file diff --git a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/containers/game.ftl b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/containers/game.ftl index c82690e..3568606 100644 --- a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/containers/game.ftl +++ b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/containers/game.ftl @@ -63,11 +63,14 @@
Carte
+
+ Recherche +
Alliance
- Listes d'ennemis + Listes d'ennemis
Messages diff --git a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/types/overview.ftl b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/types/overview.ftl index 7821aa5..c679530 100644 --- a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/types/overview.ftl +++ b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/types/overview.ftl @@ -2,7 +2,6 @@ <@page title="Empire"> <#assign ov = data.overview > - <#assign rs = data.research > <@tabs> diff --git a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/types/research.ftl b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/types/research.ftl new file mode 100644 index 0000000..5826b17 --- /dev/null +++ b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/fr/types/research.ftl @@ -0,0 +1,255 @@ +<#-- + Macro that starts a research tab for one of the 3 existing modes + + Parameters: + curMode The tab to start: R for "in progress", P for pending, K for implemented + --> +<#macro startResearchTab curMode> + <#switch curMode> + + <#case 'R'> + <@tabStart "research" "En cours" /> + <@rawFormStart "research-set-priorities" "form-priorities" "research" /> + <@listviewStart /> + <@lv_line headers=true> + <@lv_column width=150>Catégorie + <@lv_column width="x">Nom + <@lv_column width=100 centered=true>Progrès + <@lv_column width=150 centered=true>Priorité + + <#break> + + <#case 'P'> + <@tabStart "pending" "En attente" /> + <@listviewStart /> + <@lv_line headers=true> + <@lv_column width=150>Catégorie + <@lv_column width="x">Nom + <@lv_column width=150 right=true>Coût + <@lv_column width=100 centered=true>  + + <#break> + + <#case 'K'> + <@tabStart "implemented" "Recherchées" /> + <@listviewStart /> + <@lv_line headers=true> + <@lv_column width=150>Catégorie + <@lv_column width="x">Nom + + <#break> + + + +<#-- + Macro that finishes a research tab depending on its type + + Parameters: + mode The type of tab that is being closed + --> +<#macro endResearchTab mode> + <#switch mode> + + <#case 'R'> + <@lv_line headers=true> + <@lv_column width="x" colspan=4>  + + <@lv_line> + <@lv_column colspan=3>  + <@lv_column centered=true> + + + + <@listviewEnd /> + + <#break> + + <#case 'P'> + <#case 'K'> + <@listviewEnd /> + <#break> + + + <@tabEnd /> + +<#-- + Macro that renders an in-progress research entry + + Parameters: + entry the entry +--> +<#macro drawInProgressResearch entry> + <@lv_line id="tl-${entry.identifier?xhtml}" class="tech-line"> + <@lv_column>${entry.category?xhtml} + <@lv_column> + <#if entry.name?has_content> + ${entry.name?xhtml} + <#else> + Technologie inconnue + + + <@lv_column centered=true>${entry.completion} % + <@lv_column centered=true> + + + + +<#-- + Macro that renders a pending technology + + Parameters: + entry the technology +--> +<#macro drawPendingTechnology entry> + <@lv_line id="tl-${entry.identifier?xhtml}" class="tech-line"> + <@lv_column>${entry.category?xhtml} + <@lv_column> + ${entry.name?xhtml} + + <@lv_column right=true> + ${entry.price?string(",##0")} <@abbr_bgc/> + + <@lv_column right=true> + <#if data.page.cash >= entry.price> + <@rawFormStart "research-implement" "form-implement-${entry.identifier}" entry.identifier /> + + + + <#else> +   + + + + +<#-- + Macro that renders an implemented technology + + Parameters: + entry the technology +--> +<#macro drawImplementedTechnology entry> + <@lv_line id="tl-${entry.identifier?xhtml}" class="tech-line"> + <@lv_column>${entry.category?xhtml} + <@lv_column> + ${entry.name?xhtml} + + + +<#-- + Macro that renders a single entry + + Parameters: + mode the current tab (see startResearchTab for details) + entry the entry +--> +<#macro drawResearchEntry mode entry> + <#switch mode> + + <#case 'R'> + <@drawInProgressResearch entry /> + <#break> + + <#case 'P'> + <@drawPendingTechnology entry /> + <#break> + + <#case 'K'> + <@drawImplementedTechnology entry /> + <#break> + + + +<#-- + Render the visible part of the research page + + Parameters: + entries the sorted list of research entries + --> +<#macro renderResearchTabs entries> + <#local prevMode = ''> + + <@tabs> + <#list entries as entry> + + <#-- Determine type of entry to display --> + <#if entry.implemented?has_content> + <#if entry.implemented> + <#local curMode = 'K'> + <#else> + <#local curMode = 'P'> + + <#else> + <#local curMode = 'R'> + + + <#-- Start/end tabs --> + <#if curMode != prevMode> + <#if prevMode != ''> + <@endResearchTab prevMode /> + + <@startResearchTab curMode /> + <#local prevMode = curMode> + + + + <@drawResearchEntry curMode entry /> + + + <@endResearchTab prevMode /> + + +<#-- + Render all research information in a hidden layer which will be used by + the client-side code to display more information. + --> +<#macro renderFullResearchData entries> +
+ <#list entries as tech> +
+

<#if tech.name?has_content>${tech.name?xhtml}<#else>Technologie inconnue

+
Catégorie: ${tech.category?xhtml}
+ <#if tech.description?has_content>
${tech.description?xhtml}
+ <#if tech.price?has_content> +
Coût d'implémentation: ${tech.price?string(",##0")} <@abbr_bgc />
+ + <#if tech.dependencies?has_content> +
Requiert: +
    + <#list tech.dependencies as depId> +
  • ${depId}
  • + +
+
+ + <#if tech.reverseDependencies?has_content> +
Requis par: +
    + <#list tech.reverseDependencies as depId> +
  • ${depId}
  • + +
+
+ +
+ +
+ + +<#-- + Main research page + --> +<#macro render> +<@page title="Recherche"> + + <#local entries = data.researchData> + <@renderResearchTabs entries /> + <@renderFullResearchData entries /> + + + \ No newline at end of file diff --git a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/layout/form.ftl b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/layout/form.ftl index 4544473..1fc8448 100644 --- a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/layout/form.ftl +++ b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/layout/form.ftl @@ -1,12 +1,21 @@ -<#macro form action name="" hash=""> +<#macro rawFormStart action name="" hash=""> +
#${hash?url}" method="post"> + +<#macro formStart action name="" hash="">
- #${hash?url}" method="post"> + <@rawFormStart action name hash /> - <#nested> + +<#macro formEnd>
+<#macro form action name="" hash=""> + <@formStart action name hash /> + <#nested> + <@formEnd /> + <#macro form_field_line label id> diff --git a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/layout/lists.ftl b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/layout/lists.ftl index 10b04f9..93ce018 100644 --- a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/layout/lists.ftl +++ b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/layout/lists.ftl @@ -1,24 +1,30 @@ -<#macro listview> +<#macro listviewStart> - <#nested> + +<#macro listviewEnd>
+ +<#macro listview> + <@listviewStart /> + <#nested> + <@listviewEnd /> -<#macro lv_line headers=false class=""> - class="${class}<#if headers>headers"<#elseif headers> class="headers"> +<#macro lv_line headers=false class="" id=""> + class="${class}<#if headers>headers"<#elseif headers> class="headers"<#if id != ""> id="${id?xhtml}"> <#nested> -<#macro lv_column width=0 centered=false right=false colspan=0> +<#macro lv_column width=0 centered=false right=false colspan=0 id=""> <#if width?is_string> - colspan="${colspan}"> + colspan="${colspan}"<#if id != ""> id="${id?xhtml}"> <#nested> <#elseif width gt 0> - colspan="${colspan}"> + colspan="${colspan}"<#if id != ""> id="${id?xhtml}"> <#nested> <#else> - colspan="${colspan}"> + colspan="${colspan}"<#if id != ""> id="${id?xhtml}"> <#nested> diff --git a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/layout/tabs.ftl b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/layout/tabs.ftl index b1ab861..599fcfb 100644 --- a/legacyworlds-web-main/Content/Raw/WEB-INF/fm/layout/tabs.ftl +++ b/legacyworlds-web-main/Content/Raw/WEB-INF/fm/layout/tabs.ftl @@ -3,11 +3,17 @@ <#nested>
-<#macro tab id title> +<#macro tabStart id title>

${title?xhtml}

- <#nested> + +<#macro tabEnd>
+ +<#macro tab id title> + <@tabStart id title /> + <#nested> + <@tabEnd /> \ No newline at end of file diff --git a/legacyworlds-web-main/Content/Raw/css/main.css b/legacyworlds-web-main/Content/Raw/css/main.css index f18397b..2fabe1c 100644 --- a/legacyworlds-web-main/Content/Raw/css/main.css +++ b/legacyworlds-web-main/Content/Raw/css/main.css @@ -266,10 +266,10 @@ div.button a:focus,div.button a:hover { /* Forms */ .form-container { - width: -moz-calc( 100% - 128px ); - width: -webkit-calc( 100% - 128px ); - width: -o-calc( 100% - 128px ); - width: calc( 100% - 128px ); + width: -moz-calc(100% - 128px); + width: -webkit-calc(100% - 128px); + width: -o-calc(100% - 128px); + width: calc(100% - 128px); margin: 0 64px; } @@ -304,10 +304,10 @@ div.button a:focus,div.button a:hover { padding: 5px 20px; } -.form-submit .input:hover , .form-submit .input:focus { +.form-submit .input:hover,.form-submit .input:focus { padding: 5px 20px; border-color: #dfdfdf; - background-color: rgba(127,127,127,0.6); + background-color: rgba(127, 127, 127, 0.6); } .form-extra td { @@ -323,7 +323,7 @@ div.button a:focus,div.button a:hover { .form-error td { font-size: 11pt; color: white; - background-color: rgba(255,0,0,0.4); + background-color: rgba(255, 0, 0, 0.4); font-weight: bold; margin: 2px 0px; padding: 5px 10px; @@ -334,7 +334,7 @@ div.button a:focus,div.button a:hover { border-style: solid; border-width: 1px; border-color: #afafaf; - background-color: rgba(63,63,63,0.6); + background-color: rgba(63, 63, 63, 0.6); color: white; font-size: 10pt; margin: 1px 0px; @@ -371,10 +371,10 @@ div.button a:focus,div.button a:hover { /* List display */ .list-view { - width: -moz-calc( 100% - 64px ); - width: -webkit-calc( 100% - 64px ); - width: -o-calc( 100% - 64px ); - width: calc( 100% - 64px ); + width: -moz-calc(100% - 64px); + width: -webkit-calc(100% - 64px); + width: -o-calc(100% - 64px); + width: calc(100% - 64px); margin: 0 32px 20px 32px; border-collapse: collapse; } @@ -604,20 +604,20 @@ table.fleets-planet,table.fleets-moving { border: 1px solid white; border-collapse: collapse; margin: 0 0 20px 5px; - width: -moz-calc( 100% - 20px ); - width: -webkit-calc( 100% - 20px ); - width: -o-calc( 100% - 20px ); - width: calc( 100% - 20px ); + width: -moz-calc(100% - 20px); + width: -webkit-calc(100% - 20px); + width: -o-calc(100% - 20px); + width: calc(100% - 20px); } table.selected-fleets { border: 1px solid white; border-collapse: collapse; margin: 10px 0 20px 15px; - width: -moz-calc( 100% - 30px ); - width: -webkit-calc( 100% - 30px ); - width: -o-calc( 100% - 30px ); - width: calc( 100% - 30px ); + width: -moz-calc(100% - 30px); + width: -webkit-calc(100% - 30px); + width: -o-calc(100% - 30px); + width: calc(100% - 30px); } table.fleets-planet td { @@ -799,4 +799,20 @@ tr.alliance-msg * { tr.empire-msg * { color: #afafaf; +} + +/* + * Research page + */ +.tech-line { + height: 22px; +} +.tech-view h4 { + margin-bottom: 15px; +} +.tech-view .tech-info { + margin-bottom: 7px; +} +.tech-line.selected, .tech-line.selected td { + background-color: rgba(127, 127, 127, 0.25); } \ No newline at end of file diff --git a/legacyworlds-web-main/Content/Raw/js/research.js b/legacyworlds-web-main/Content/Raw/js/research.js new file mode 100644 index 0000000..c9c3544 --- /dev/null +++ b/legacyworlds-web-main/Content/Raw/js/research.js @@ -0,0 +1,87 @@ +$(function() { + + /* Replace dependency identifiers */ + $('.tech-info li.dep').each( + function() { + var _id = this.innerHTML; + var _targetTitle = $('#tdesc-' + _id + ' h4'); + this.innerHTML = ''; + $('').attr('href', '#tl-' + _id) + .append(_targetTitle.html()).appendTo($(this)); + }); + + /* Map entries to tabs */ + var _entries = {}; + var _tabs = {}; + $('.tech-line').each( + function() { + var _id = $(this).attr('id').replace(/^tl-/, ''); + var _tab = $(this).parents('.tab-contents').attr('id').replace( + /^tabc-/, ''); + _entries[_id] = _tab; + if (!_tabs[_tab]) { + _tabs[_tab] = []; + } + _tabs[_tab].push(_id); + }); + var _selected = {}; + for ( var _tab in _tabs) { + _selected[_tab] = _tabs[_tab][0]; + $('#tl-' + _selected[_tab]).toggleClass('selected'); + } + + /* Insert viewing area */ + var _viewingArea = $('
').css({ + 'float' : 'right', + 'width' : '300px', + 'position' : 'relative', + }).attr('class', 'tech-view').prependTo($('#page-contents')); + $('div.tabs').css({ + 'margin-right' : '300px' + }); + + /* When an entry is clicked, set contents of viewing area */ + var _displayed = ''; + $('.tech-line').click(function() { + var _id = $(this).attr('id').replace(/^tl-/, ''); + if (_id == _displayed) { + return; + } + _viewingArea.html($('#tdesc-' + _id).html()); + $('a', _viewingArea).click(function() { + var _target = $(this).attr('href').replace(/^#tl-/, ''); + $('#tabb-' + _entries[_target]).click(); + $('#tl-' + _target).click(); + }); + if (_selected[_entries[_id]] != _id) { + $('#tl-' + _selected[_entries[_id]]).toggleClass('selected'); + $(this).toggleClass('selected'); + _selected[_entries[_id]] = _id; + } + location.hash = '#tl-'+_id; + return true; + }); + + /* When a tab button is clicked, select the appropriate entry */ + $('.tab-buttons a').click(function() { + var _id = $(this).attr('id').replace(/^tabb-/, ''); + var _tech = _selected[_id]; + $('#tl-' + _tech).click(); + }); + + (function() { + var _current = location.hash; + if (_current.match(/^#tl-/)) { + _current = _current.replace(/#tl-/, ''); + } else if (_current.match(/^#/)) { + _current = _selected[_current.replace(/^#/, '')]; + } else { + for ( var _tab in _selected) { + _current = _selected[_tab]; + break; + } + } + $('#tabb-' + _entries[_current]).click(); + $('#tl-' + _current).click(); + })(); +}); \ No newline at end of file diff --git a/legacyworlds-web-main/src/main/java/com/deepclone/lw/web/main/game/OverviewPage.java b/legacyworlds-web-main/src/main/java/com/deepclone/lw/web/main/game/OverviewPage.java index ecc8c1f..50024d2 100644 --- a/legacyworlds-web-main/src/main/java/com/deepclone/lw/web/main/game/OverviewPage.java +++ b/legacyworlds-web-main/src/main/java/com/deepclone/lw/web/main/game/OverviewPage.java @@ -10,7 +10,6 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.SessionAttributes; @@ -73,21 +72,15 @@ public class OverviewPage /** - * "Implement technology" command + * Empire-wide mining settings update command * *

- * This method is mapped to the technology implementation command URL. + * This method is called when a command to update empire-wide mining settings is received. * * @param request * the HTTP request - * @param language - * the language from the session - * @param model - * the model - * @param tech - * the technology identifier * - * @return the overview page rendering order + * @return a redirection to the overview page's "economy" tab * * @throws SessionException * if some error occurs on the server @@ -96,26 +89,8 @@ public class OverviewPage * @throws SessionMaintenanceException * if the game is under maintenance */ - @RequestMapping( value = "/implement-{tech}.action" , method = RequestMethod.POST ) - public String implement( HttpServletRequest request , @ModelAttribute( "language" ) String language , Model model , - @PathVariable String tech ) - throws SessionException , SessionServerException , SessionMaintenanceException - { - int techId; - try { - techId = Integer.parseInt( tech ); - } catch ( NumberFormatException e ) { - return this.redirect( "overview" ); - } - - PlayerSession pSession = this.getSession( PlayerSession.class , request ); - return this.render( model , "game" , language , "overview" , pSession.implementTechnology( techId ) ); - } - - @RequestMapping( value = "/update-mining-settings.action" , method = RequestMethod.POST ) - public String updateMiningSettings( HttpServletRequest request , @ModelAttribute( "language" ) String language , - Model model ) + public String updateMiningSettings( HttpServletRequest request ) throws SessionException , SessionServerException , SessionMaintenanceException { Map< String , Integer > miningSettings = this.getMiningSettings( request ); diff --git a/legacyworlds-web-main/src/main/java/com/deepclone/lw/web/main/game/ResearchPage.java b/legacyworlds-web-main/src/main/java/com/deepclone/lw/web/main/game/ResearchPage.java new file mode 100644 index 0000000..79c9beb --- /dev/null +++ b/legacyworlds-web-main/src/main/java/com/deepclone/lw/web/main/game/ResearchPage.java @@ -0,0 +1,186 @@ +package com.deepclone.lw.web.main.game; + + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.SessionAttributes; + +import com.deepclone.lw.session.SessionException; +import com.deepclone.lw.web.beans.intercept.SessionRequirement; +import com.deepclone.lw.web.beans.session.SessionMaintenanceException; +import com.deepclone.lw.web.beans.session.SessionServerException; +import com.deepclone.lw.web.beans.view.PageControllerBase; +import com.deepclone.lw.web.csess.PlayerSession; + + + +/** + * Controller for the Research page + * + *

+ * This controller contains all URL handlers associated with the research page: viewing research + * state, setting research priorities, and implementing technologies. + * + * @author E. Benoît + */ +@Controller +@SessionRequirement( value = true , redirectTo = "player-session" , subType = "game" ) +@SessionAttributes( "language" ) +public class ResearchPage + extends PageControllerBase +{ + + /** + * Research page view + * + *

+ * This method fetches all research information from the game server then requests the page + * rendering. + * + * @param request + * the HTTP request + * @param language + * the language from the session + * @param model + * the model + * + * @return the research page rendering information + * + * @throws SessionException + * if some error occurs on the server + * @throws SessionServerException + * if the server is unreachable + * @throws SessionMaintenanceException + * if the game is under maintenance + */ + @RequestMapping( "/research" ) + public String view( HttpServletRequest request , @ModelAttribute( "language" ) String language , Model model ) + throws SessionException , SessionServerException , SessionMaintenanceException + { + PlayerSession pSession = this.getSession( PlayerSession.class , request ); + return this.render( model , "game" , language , "research" , pSession.getResearch( ) ); + } + + + /** + * Research priorities update command + * + *

+ * This method handles the research priorities update command, which is triggered when the + * "Update" button is clicked on the "In progress" tab of the research page. + * + * @param request + * the HTTP request + * + * @return a redirection to the research page's "in progress" tab + * + * @throws SessionException + * if some error occurs on the server + * @throws SessionServerException + * if the server is unreachable + * @throws SessionMaintenanceException + * if the game is under maintenance + */ + @RequestMapping( value = "/research-set-priorities.action" , method = RequestMethod.POST ) + public String setPriorities( HttpServletRequest request ) + throws SessionException , SessionServerException , SessionMaintenanceException + { + Map< String , Integer > priorities = this.getPriorities( request ); + if ( priorities != null ) { + this.getSession( PlayerSession.class , request ).updateResearchPriorities( priorities ); + } + return this.redirect( "research#research" ); + } + + + /** + * Extract research priorities from the HTTP request + * + *

+ * Look for all submitted fields that begin with "rp-" then try to extract their values into a + * map that associates technology identifiers to priorities. + * + * @param request + * the HTTP request + * + * @return the map containing the submitted priorities, or null if one of the + * values was incorrect. + */ + private Map< String , Integer > getPriorities( HttpServletRequest request ) + { + Map< String , Object > input = this.getInput( request ); + Map< String , Integer > priorities = new HashMap< String , Integer >( ); + for ( Entry< String , Object > entry : input.entrySet( ) ) { + // Ignore items which are not priorities + String name = entry.getKey( ); + if ( !name.startsWith( "rp-" ) ) { + continue; + } + name = name.substring( 3 ); + + // Get values + if ( ! ( entry.getValue( ) instanceof String[] ) ) { + continue; + } + String[] values = (String[]) entry.getValue( ); + if ( values.length < 1 ) { + continue; + } + + // Pre-validate them + int value; + try { + value = Integer.parseInt( values[ 0 ] ); + } catch ( NumberFormatException e ) { + value = -1; + } + if ( value < 0 || value > 4 ) { + return null; + } + + priorities.put( name , value ); + } + return priorities; + } + + + /** + * Technology implementation command + * + *

+ * This method handles the technology implementation command, which is triggered when the + * "Implement" button is clicked on the "Pending" tab of the research page. + * + * @param request + * the HTTP request + * @param tech + * the identifier of the technology + * + * @return a redirection to the research page's "Implemented" tab + * + * @throws SessionException + * if some error occurs on the server + * @throws SessionServerException + * if the server is unreachable + * @throws SessionMaintenanceException + * if the game is under maintenance + */ + @RequestMapping( value = "/research-implement.action" , method = RequestMethod.POST ) + public String implement( HttpServletRequest request , @RequestParam( "technology" ) String technology ) + throws SessionException , SessionServerException , SessionMaintenanceException + { + this.getSession( PlayerSession.class , request ).implementTechnology( technology ); + return this.redirect( "research#implemented" ); + } + +} diff --git a/legacyworlds/pom.xml b/legacyworlds/pom.xml index bd3479b..606ac67 100644 --- a/legacyworlds/pom.xml +++ b/legacyworlds/pom.xml @@ -168,6 +168,11 @@ com.deepclone.lw ${legacyworlds.version.main}.${legacyworlds.version.release}-${legacyworlds.version.build} + + legacyworlds-server-beans-technologies + com.deepclone.lw + ${legacyworlds.version.main}.${legacyworlds.version.release}-${legacyworlds.version.build} + legacyworlds-server-beans-updates com.deepclone.lw