merge of 0.9.14 changes into default - hopefully I successfully avoided the parts we don't want
--- a/CMakeLists.txt Sat Nov 13 17:14:51 2010 +0300
+++ b/CMakeLists.txt Sat Nov 13 09:36:26 2010 -0500
@@ -4,38 +4,38 @@
cmake_policy(SET CMP0003 NEW)
IF(POLICY CMP0012)
- cmake_policy(SET CMP0012 NEW)
+ cmake_policy(SET CMP0012 NEW)
ENDIF()
-#detect subversion revision (if present)
-#set(version_suffix "-dev") #UNSET THIS VARIABLE AT RELEASE TIME
+#detect Mercurial revision (if present)
+set(version_suffix "-dev") #UNSET THIS VARIABLE AT RELEASE TIME
IF(version_suffix MATCHES "-dev")
- set(HW_DEV true)
- IF (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.hg)
- FIND_PROGRAM(HGCOMMAND hg)
- IF(HGCOMMAND)
- exec_program(${HGCOMMAND}
- ARGS identify -in ${CMAKE_CURRENT_SOURCE_DIR}
- OUTPUT_VARIABLE version_suffix
- )
- STRING(REGEX REPLACE "(.*) +(.*)" "\\2:\\1" version_suffix ${version_suffix})
- MESSAGE(STATUS "Building revision ${version_suffix}")
- set(version_suffix ".${version_suffix}")
+ set(HW_DEV true)
+ IF (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.hg)
+ FIND_PROGRAM(HGCOMMAND hg)
+ IF(HGCOMMAND)
+ exec_program(${HGCOMMAND}
+ ARGS identify -in ${CMAKE_CURRENT_SOURCE_DIR}
+ OUTPUT_VARIABLE version_suffix
+ )
+ STRING(REGEX REPLACE "(.*) +(.*)" "\\2:\\1" version_suffix ${version_suffix})
+ MESSAGE(STATUS "Building revision ${version_suffix}")
+ set(version_suffix ".${version_suffix}")
# #truncate to numbers only - trying to fix a problem described in http://www.hedgewars.org/node/2019
# STRING(REGEX REPLACE "^\\.(\\d+)" ".\\1" version_suffix ${version_suffix})
# # screw whole suffix if there's no number
# STRING(REGEX REPLACE "^\\.([a-z]+.*)" "-dev" version_suffix ${version_suffix})
- ENDIF()
- ENDIF()
+ ENDIF()
+ ENDIF()
ELSE()
- set(HW_DEV false)
+ set(HW_DEV false)
ENDIF()
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules)
set(CPACK_PACKAGE_VERSION_MAJOR "0")
set(CPACK_PACKAGE_VERSION_MINOR "9")
-set(CPACK_PACKAGE_VERSION_PATCH "14${version_suffix}")
+set(CPACK_PACKAGE_VERSION_PATCH "15${version_suffix}")
#forbid in-tree building
#IF (${CMAKE_SOURCE_DIR} MATCHES ${CMAKE_BINARY_DIR})
@@ -46,80 +46,80 @@
#set some safe values
IF(NOT WITH_SERVER)
- SET(WITH_SERVER 0)
+ SET(WITH_SERVER 0)
ENDIF(NOT WITH_SERVER)
IF(NOT BUILD_ENGINE_LIBRARY)
- SET(BUILD_ENGINE_LIBRARY 0)
+ SET(BUILD_ENGINE_LIBRARY 0)
ENDIF(NOT BUILD_ENGINE_LIBRARY)
if(APPLE)
- set(CMAKE_FIND_FRAMEWORK "FIRST")
+ set(CMAKE_FIND_FRAMEWORK "FIRST")
- #paths for creating the bundle
- set(bundle_name Hedgewars.app)
- set(CMAKE_INSTALL_PREFIX ${bundle_name}/Contents/MacOS/)
- set(DATA_INSTALL_DIR "../Resources/")
- set(target_dir ".")
+ #paths for creating the bundle
+ set(bundle_name Hedgewars.app)
+ set(CMAKE_INSTALL_PREFIX ${bundle_name}/Contents/MacOS/)
+ set(DATA_INSTALL_DIR "../Resources/")
+ set(target_dir ".")
- #what system are we building for
- set(minimum_macosx $ENV{MACOSX_DEPLOYMENT_TARGET})
+ #what system are we building for
+ set(minimum_macosx $ENV{MACOSX_DEPLOYMENT_TARGET})
- #detect on which system are we
- EXEC_PROGRAM("/usr/bin/sw_vers" OUTPUT_VARIABLE MACOSX_VERSION_TMP)
- STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" MACOSX_VERSION_TMP "${MACOSX_VERSION_TMP}")
- STRING(REGEX REPLACE "([0-9]+.[0-9]+).[0-9]+" "\\1" current_macosx_version ${MACOSX_VERSION_TMP})
+ #detect on which system are we
+ EXEC_PROGRAM("/usr/bin/sw_vers" OUTPUT_VARIABLE MACOSX_VERSION_TMP)
+ STRING(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" MACOSX_VERSION_TMP "${MACOSX_VERSION_TMP}")
+ STRING(REGEX REPLACE "([0-9]+.[0-9]+).[0-9]+" "\\1" current_macosx_version ${MACOSX_VERSION_TMP})
- if(NOT minimum_macosx)
- #if nothing is set, we deploy only for the current system
- set(minimum_macosx ${current_macosx_version})
- endif()
+ if(NOT minimum_macosx)
+ #if nothing is set, we deploy only for the current system
+ set(minimum_macosx ${current_macosx_version})
+ endif()
- if (minimum_macosx LESS "10.4")
- set(FATAL "Hedgewars is not supported for pre-10.4 systems")
- endif()
+ if (minimum_macosx LESS "10.4")
+ set(FATAL "Hedgewars is not supported for pre-10.4 systems")
+ endif()
- set(CMAKE_OSX_ARCHITECTURES "i386;ppc7400")
+ set(CMAKE_OSX_ARCHITECTURES "i386;ppc7400")
- #create universal binaries only when it's time to bundle the application, also build server
- IF(BUNDLE)
- set(WITH_SERVER true)
- if(NOT minimum_macosx MATCHES "10.6")
- set(CMAKE_C_COMPILER "gcc-4.0")
- set(CMAKE_CXX_COMPILER "g++-4.0")
- else()
- if(current_macosx_version MATCHES "10.6")
- set(CMAKE_OSX_ARCHITECTURES "x86_64")
- endif()
- endif()
- ELSE()
- if(current_macosx_version MATCHES "10.6")
- set(CMAKE_OSX_ARCHITECTURES "x86_64")
- endif()
- ENDIF()
+ #create universal binaries only when it's time to bundle the application, also build server
+ IF(BUNDLE)
+ set(WITH_SERVER true)
+ if(NOT minimum_macosx MATCHES "10.6")
+ set(CMAKE_C_COMPILER "gcc-4.0")
+ set(CMAKE_CXX_COMPILER "g++-4.0")
+ else()
+ if(current_macosx_version MATCHES "10.6")
+ set(CMAKE_OSX_ARCHITECTURES "x86_64")
+ endif()
+ endif()
+ ELSE()
+ if(current_macosx_version MATCHES "10.6")
+ set(CMAKE_OSX_ARCHITECTURES "x86_64")
+ endif()
+ ENDIF()
- message(STATUS "Target system: Mac OS X ${minimum_macosx} ${CMAKE_OSX_ARCHITECTURES}")
+ message(STATUS "Target system: Mac OS X ${minimum_macosx} ${CMAKE_OSX_ARCHITECTURES}")
- if(minimum_macosx MATCHES "10.4")
- set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.4u.sdk/")
- if(current_macosx_version MATCHES "10.4")
- find_package(SDL_mixer REQUIRED)
- set(pascal_compiler_flags_cmn "-k-dylib_file @loader_path/Frameworks/smpeg.framework/Versions/A/smpeg:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/smpeg.framework/Versions/A/smpeg" "-k-dylib_file @loader_path/Frameworks/mikmod.framework/Versions/A/mikmod:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/mikmod.framework/Versions/A/mikmod" ${pascal_compiler_flags_cmn})
- set(CMAKE_C_FLAGS "-dylib_file @loader_path/Frameworks/smpeg.framework/Versions/A/smpeg:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/smpeg.framework/Versions/A/smpeg -dylib_file @loader_path/Frameworks/mikmod.framework/Versions/A/mikmod:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/mikmod.framework/Versions/A/mikmod")
- endif()
- else()
- set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX${minimum_macosx}.sdk/")
- endif()
+ if(minimum_macosx MATCHES "10.4")
+ set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.4u.sdk/")
+ if(current_macosx_version MATCHES "10.4")
+ find_package(SDL_mixer REQUIRED)
+ set(pascal_compiler_flags_cmn "-k-dylib_file @loader_path/Frameworks/smpeg.framework/Versions/A/smpeg:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/smpeg.framework/Versions/A/smpeg" "-k-dylib_file @loader_path/Frameworks/mikmod.framework/Versions/A/mikmod:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/mikmod.framework/Versions/A/mikmod" ${pascal_compiler_flags_cmn})
+ set(CMAKE_C_FLAGS "-dylib_file @loader_path/Frameworks/smpeg.framework/Versions/A/smpeg:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/smpeg.framework/Versions/A/smpeg -dylib_file @loader_path/Frameworks/mikmod.framework/Versions/A/mikmod:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/mikmod.framework/Versions/A/mikmod")
+ endif()
+ else()
+ set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX${minimum_macosx}.sdk/")
+ endif()
- #1.set deployment target; 2.link with libsdlmain.a (when building an executable); 3.link with liblua.a (which requires readline)
- set(pascal_compiler_flags_cmn "-k-macosx_version_min" "-k${minimum_macosx}" "-XR${CMAKE_OSX_SYSROOT}" ${pascal_compiler_flags_cmn})
- if(NOT BUILD_ENGINE_LIBRARY)
- set(pascal_compiler_flags_cmn "-k${CMAKE_BINARY_DIR}/bin/libSDLmain.a" ${pascal_compiler_flags_cmn})
- endif()
- set(pascal_compiler_flags_cmn "-k${CMAKE_BINARY_DIR}/bin/liblua.a" "-k-lreadline" ${pascal_compiler_flags_cmn})
+ #1.set deployment target; 2.link with libsdlmain.a (when building an executable); 3.link with liblua.a (which requires readline)
+ set(pascal_compiler_flags_cmn "-k-macosx_version_min" "-k${minimum_macosx}" "-XR${CMAKE_OSX_SYSROOT}" ${pascal_compiler_flags_cmn})
+ if(NOT BUILD_ENGINE_LIBRARY)
+ set(pascal_compiler_flags_cmn "-k${CMAKE_BINARY_DIR}/bin/libSDLmain.a" ${pascal_compiler_flags_cmn})
+ endif()
+ set(pascal_compiler_flags_cmn "-k${CMAKE_BINARY_DIR}/bin/liblua.a" "-k-lreadline" ${pascal_compiler_flags_cmn})
else(APPLE)
- set(target_dir "bin")
+ set(target_dir "bin")
endif(APPLE)
@@ -128,16 +128,16 @@
# SET(CMAKE_BUILD_TYPE "Release")
#ENDIF (NOT CMAKE_BUILD_TYPE)
if (NOT CMAKE_BUILD_TYPE)
- set (CMAKE_BUILD_TYPE RELEASE CACHE STRING "Choose the type of build, options are: None Debug Release." FORCE)
+ set (CMAKE_BUILD_TYPE RELEASE CACHE STRING "Choose the type of build, options are: None Debug Release." FORCE)
endif (NOT CMAKE_BUILD_TYPE)
if(CMAKE_BUILD_TYPE MATCHES RELEASE OR CMAKE_BUILD_TYPE MATCHES "Release")
- message(STATUS "Building Release")
- set(Optz true)
+ message(STATUS "Building Release")
+ set(Optz true)
else()
- message(STATUS "Building Debug")
- #set(CMAKE_VERBOSE_MAKEFILE true)
- set(Optz false)
+ message(STATUS "Building Debug")
+ #set(CMAKE_VERBOSE_MAKEFILE true)
+ set(Optz false)
endif()
@@ -153,40 +153,40 @@
if(Optz)
# set(pascal_compiler_flags_cmn "-O3" "-OpPENTIUM4" "-CfSSE3" "-Xs" "-Si" ${pascal_compiler_flags_cmn})
- set(pascal_compiler_flags_cmn "-O2" "-Xs" "-Si" ${pascal_compiler_flags_cmn})
- set(haskell_compiler_flags_cmn "-O2" "-w")
+ set(pascal_compiler_flags_cmn "-O2" "-Xs" "-Si" ${pascal_compiler_flags_cmn})
+ set(haskell_compiler_flags_cmn "-O2" "-w")
else(Optz)
- set(pascal_compiler_flags_cmn "-O-" "-g" "-gh" "-gl" "-dDEBUGFILE" ${pascal_compiler_flags_cmn})
- set(haskell_compiler_flags_cmn "-Wall" "-debug" "-dcore-lint")
+ set(pascal_compiler_flags_cmn "-O-" "-g" "-gh" "-gl" "-dDEBUGFILE" ${pascal_compiler_flags_cmn})
+ set(haskell_compiler_flags_cmn "-Wall" "-debug" "-dcore-lint")
endif(Optz)
if(DEFINED DATA_INSTALL_DIR)
- set(SHAREPATH ${DATA_INSTALL_DIR}/hedgewars/)
+ set(SHAREPATH ${DATA_INSTALL_DIR}/hedgewars/)
else()
- set(SHAREPATH share/hedgewars/)
+ set(SHAREPATH share/hedgewars/)
endif()
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
set(HEDGEWARS_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
-set(HEDGEWARS_PROTO_VER 32)
+set(HEDGEWARS_PROTO_VER 34)
if(WITH_SERVER)
- message(STATUS "Server is going to be built! Make sure you have GHC installed")
- set(HAVE_NETSERVER true)
- add_subdirectory(gameServer)
+ message(STATUS "Server is going to be built! Make sure you have GHC installed")
+ set(HAVE_NETSERVER true)
+ add_subdirectory(gameServer)
else(WITH_SERVER)
- set(HAVE_NETSERVER false)
+ set(HAVE_NETSERVER false)
endif(WITH_SERVER)
add_subdirectory(misc/liblua)
add_subdirectory(hedgewars)
#add_subdirectory(misc/libopenalbridge)
if(NOT BUILD_ENGINE_LIBRARY)
- add_subdirectory(bin)
- add_subdirectory(QTfrontend)
- add_subdirectory(share)
- add_subdirectory(tools)
+ add_subdirectory(bin)
+ add_subdirectory(QTfrontend)
+ add_subdirectory(share)
+ add_subdirectory(tools)
endif()
# CPack vars
@@ -201,70 +201,70 @@
set(CPACK_PACKAGE_INSTALL_DIRECTORY "Hedgewars ${HEDGEWARS_VERSION}")
if(WIN32 AND NOT UNIX)
- set(CPACK_NSIS_DISPLAY_NAME "Hedgewars")
- set(CPACK_NSIS_HELP_LINK "http://www.hedgewars.org/")
- set(CPACK_NSIS_URL_INFO_ABOUT "http://www.hedgewars.org/")
- set(CPACK_NSIS_CONTACT "unC0Rr@gmail.com")
- set(CPACK_NSIS_MODIFY_PATH OFF)
- set(CPACK_GENERATOR "ZIP;NSIS")
- set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "hedgewars")
+ set(CPACK_NSIS_DISPLAY_NAME "Hedgewars")
+ set(CPACK_NSIS_HELP_LINK "http://www.hedgewars.org/")
+ set(CPACK_NSIS_URL_INFO_ABOUT "http://www.hedgewars.org/")
+ set(CPACK_NSIS_CONTACT "unC0Rr@gmail.com")
+ set(CPACK_NSIS_MODIFY_PATH OFF)
+ set(CPACK_GENERATOR "ZIP;NSIS")
+ set(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "hedgewars")
else(WIN32 AND NOT UNIX)
- set(CPACK_STRIP_FILES "bin/hedgewars;bin/hwengine")
+ set(CPACK_STRIP_FILES "bin/hedgewars;bin/hwengine")
endif(WIN32 AND NOT UNIX)
set(CPACK_SOURCE_IGNORE_FILES
- "~"
- "\\\\.hg"
- "\\\\.svn"
- "\\\\.exe$"
- "\\\\.a$"
- "\\\\.dll$"
- "\\\\.xcf$"
- "\\\\.cxx$"
- "\\\\.db$"
- "\\\\.dof$"
- "\\\\.layout$"
- "\\\\.zip$"
- "\\\\.gz$"
- "\\\\.bz2$"
- "\\\\.tmp$"
- "\\\\.core$"
- "\\\\.sh$"
- "\\\\.sifz$"
- "\\\\.svg$"
- "\\\\.svgz$"
- "\\\\.ppu$"
- "\\\\.psd$"
- "\\\\.o$"
- "Makefile"
- "Doxyfile"
- "CMakeFiles"
- "debug"
- "release$"
- "Debug$"
- "Release$"
- "proto.inc$"
- "hwconsts.cpp$"
- "playlist.inc$"
- "CPack"
- "cmake_install.cmake$"
- "config.inc$"
- "hwengine.desktop$"
+ "~"
+ "\\\\.hg"
+ "\\\\.svn"
+ "\\\\.exe$"
+ "\\\\.a$"
+ "\\\\.dll$"
+ "\\\\.xcf$"
+ "\\\\.cxx$"
+ "\\\\.db$"
+ "\\\\.dof$"
+ "\\\\.layout$"
+ "\\\\.zip$"
+ "\\\\.gz$"
+ "\\\\.bz2$"
+ "\\\\.tmp$"
+ "\\\\.core$"
+ "\\\\.sh$"
+ "\\\\.sifz$"
+ "\\\\.svg$"
+ "\\\\.svgz$"
+ "\\\\.ppu$"
+ "\\\\.psd$"
+ "\\\\.o$"
+ "Makefile"
+ "Doxyfile"
+ "CMakeFiles"
+ "debug"
+ "release$"
+ "Debug$"
+ "Release$"
+ "proto.inc$"
+ "hwconsts.cpp$"
+ "playlist.inc$"
+ "CPack"
+ "cmake_install.cmake$"
+ "config.inc$"
+ "hwengine.desktop$"
# "^${CMAKE_CURRENT_SOURCE_DIR}/misc/libopenalbridge"
- "^${CMAKE_CURRENT_SOURCE_DIR}/project_files/HedgewarsMobile/"
- "^${CMAKE_CURRENT_SOURCE_DIR}/bin/[a-z]"
- "^${CMAKE_CURRENT_SOURCE_DIR}/tools/templates"
- "^${CMAKE_CURRENT_SOURCE_DIR}/doc"
- "^${CMAKE_CURRENT_SOURCE_DIR}/templates"
- "^${CMAKE_CURRENT_SOURCE_DIR}/Graphics"
- "^${CMAKE_CURRENT_SOURCE_DIR}/realtest"
- "^${CMAKE_CURRENT_SOURCE_DIR}/tmp"
- "^${CMAKE_CURRENT_SOURCE_DIR}/utils"
- "^${CMAKE_CURRENT_SOURCE_DIR}/share/hedgewars/Data/Maps/test"
- "^${CMAKE_CURRENT_SOURCE_DIR}/share/hedgewars/Data/Themes/ethereal"
- "^${CMAKE_CURRENT_SOURCE_DIR}/install_manifest.txt"
- "^${CMAKE_CURRENT_SOURCE_DIR}/CMakeCache.txt"
- "^${CMAKE_CURRENT_SOURCE_DIR}/hedgewars\\\\."
+ "^${CMAKE_CURRENT_SOURCE_DIR}/project_files/HedgewarsMobile/"
+ "^${CMAKE_CURRENT_SOURCE_DIR}/bin/[a-z]"
+ "^${CMAKE_CURRENT_SOURCE_DIR}/tools/templates"
+ "^${CMAKE_CURRENT_SOURCE_DIR}/doc"
+ "^${CMAKE_CURRENT_SOURCE_DIR}/templates"
+ "^${CMAKE_CURRENT_SOURCE_DIR}/Graphics"
+ "^${CMAKE_CURRENT_SOURCE_DIR}/realtest"
+ "^${CMAKE_CURRENT_SOURCE_DIR}/tmp"
+ "^${CMAKE_CURRENT_SOURCE_DIR}/utils"
+ "^${CMAKE_CURRENT_SOURCE_DIR}/share/hedgewars/Data/Maps/test"
+ "^${CMAKE_CURRENT_SOURCE_DIR}/share/hedgewars/Data/Themes/ethereal"
+ "^${CMAKE_CURRENT_SOURCE_DIR}/install_manifest.txt"
+ "^${CMAKE_CURRENT_SOURCE_DIR}/CMakeCache.txt"
+ "^${CMAKE_CURRENT_SOURCE_DIR}/hedgewars\\\\."
)
include(CPack)
--- a/QTfrontend/ammoSchemeModel.cpp Sat Nov 13 17:14:51 2010 +0300
+++ b/QTfrontend/ammoSchemeModel.cpp Sat Nov 13 09:36:26 2010 -0500
@@ -45,19 +45,21 @@
<< QVariant(false) // inf. attack 19
<< QVariant(false) // reset weps 20
<< QVariant(false) // per hog ammo 21
- << QVariant(100) // damage modfier 22
- << QVariant(45) // turn time 23
- << QVariant(100) // init health 24
- << QVariant(15) // sudden death 25
- << QVariant(5) // case prob 26
- << QVariant(3) // mines time 27
- << QVariant(4) // mines number 28
- << QVariant(0) // mine dud pct 29
- << QVariant(2) // explosives 30
- << QVariant(35) // health case pct 31
- << QVariant(25) // health case amt 32
- << QVariant(47) // water rise amt 33
- << QVariant(5) // health dec amt 34
+ << QVariant(false) // no wind 22
+ << QVariant(false) // more wind 23
+ << QVariant(100) // damage modfier 24
+ << QVariant(45) // turn time 25
+ << QVariant(100) // init health 26
+ << QVariant(15) // sudden death 27
+ << QVariant(5) // case prob 28
+ << QVariant(3) // mines time 29
+ << QVariant(4) // mines number 30
+ << QVariant(0) // mine dud pct 31
+ << QVariant(2) // explosives 32
+ << QVariant(35) // health case pct 33
+ << QVariant(25) // health case amt 34
+ << QVariant(47) // water rise amt 35
+ << QVariant(5) // health dec amt 36
;
AmmoSchemeModel::AmmoSchemeModel(QObject* parent, const QString & fileName) :
@@ -103,19 +105,21 @@
<< "infattack" // 19
<< "resetweps" // 20
<< "perhogammo" // 21
- << "damagefactor" // 22
- << "turntime" // 23
- << "health" // 24
- << "suddendeath" // 25
- << "caseprobability" // 26
- << "minestime" // 27
- << "minesnum" // 28
- << "minedudpct" // 29
- << "explosives" // 30
- << "healthprobability" // 31
- << "healthcaseamount" // 32
- << "waterrise" // 33
- << "healthdecrease" // 34
+ << "disablewind" // 22
+ << "morewind" // 23
+ << "damagefactor" // 24
+ << "turntime" // 25
+ << "health" // 26
+ << "suddendeath" // 27
+ << "caseprobability" // 28
+ << "minestime" // 29
+ << "minesnum" // 30
+ << "minedudpct" // 31
+ << "explosives" // 32
+ << "healthprobability" // 33
+ << "healthcaseamount" // 34
+ << "waterrise" // 35
+ << "healthdecrease" // 36
;
QList<QVariant> proMode;
@@ -142,19 +146,21 @@
<< QVariant(false) // inf. attack 19
<< QVariant(false) // reset weps 20
<< QVariant(false) // per hog ammo 21
- << QVariant(100) // damage modfier 22
- << QVariant(15) // turn time 23
- << QVariant(100) // init health 24
- << QVariant(15) // sudden death 25
- << QVariant(0) // case prob 26
- << QVariant(3) // mines time 27
- << QVariant(0) // mines number 28
- << QVariant(0) // mine dud pct 29
- << QVariant(2) // explosives 30
- << QVariant(35) // health case pct 31
- << QVariant(25) // health case amt 32
- << QVariant(47) // water rise amt 33
- << QVariant(5) // health dec amt 34
+ << QVariant(false) // no wind 22
+ << QVariant(false) // more wind 23
+ << QVariant(100) // damage modfier 24
+ << QVariant(15) // turn time 25
+ << QVariant(100) // init health 26
+ << QVariant(15) // sudden death 27
+ << QVariant(0) // case prob 28
+ << QVariant(3) // mines time 29
+ << QVariant(0) // mines number 30
+ << QVariant(0) // mine dud pct 31
+ << QVariant(2) // explosives 32
+ << QVariant(35) // health case pct 33
+ << QVariant(25) // health case amt 34
+ << QVariant(47) // water rise amt 35
+ << QVariant(5) // health dec amt 36
;
QList<QVariant> shoppa;
@@ -181,19 +187,21 @@
<< QVariant(false) // inf. attack 19
<< QVariant(false) // reset weps 20
<< QVariant(false) // per hog ammo 21
- << QVariant(100) // damage modfier 22
- << QVariant(30) // turn time 23
- << QVariant(100) // init health 24
- << QVariant(50) // sudden death 25
- << QVariant(1) // case prob 26
- << QVariant(3) // mines time 27
- << QVariant(0) // mines number 28
- << QVariant(0) // mine dud pct 29
- << QVariant(0) // explosives 30
- << QVariant(0) // health case pct 31
- << QVariant(25) // health case amt 32
- << QVariant(47) // water rise amt 33
- << QVariant(5) // health dec amt 34
+ << QVariant(false) // no wind 22
+ << QVariant(false) // more wind 23
+ << QVariant(100) // damage modfier 24
+ << QVariant(30) // turn time 25
+ << QVariant(100) // init health 26
+ << QVariant(50) // sudden death 27
+ << QVariant(1) // case prob 28
+ << QVariant(3) // mines time 29
+ << QVariant(0) // mines number 30
+ << QVariant(0) // mine dud pct 31
+ << QVariant(0) // explosives 32
+ << QVariant(0) // health case pct 33
+ << QVariant(25) // health case amt 34
+ << QVariant(47) // water rise amt 35
+ << QVariant(5) // health dec amt 36
;
QList<QVariant> cleanslate;
@@ -220,19 +228,21 @@
<< QVariant(true) // inf. attack 19
<< QVariant(true) // reset weps 20
<< QVariant(false) // per hog ammo 21
- << QVariant(100) // damage modfier 22
- << QVariant(45) // turn time 23
- << QVariant(100) // init health 24
- << QVariant(15) // sudden death 25
- << QVariant(5) // case prob 26
- << QVariant(3) // mines time 27
- << QVariant(4) // mines number 28
- << QVariant(0) // mine dud pct 29
- << QVariant(2) // explosives 30
- << QVariant(35) // health case pct 31
- << QVariant(25) // health case amt 32
- << QVariant(47) // water rise amt 33
- << QVariant(5) // health dec amt 34
+ << QVariant(false) // no wind 22
+ << QVariant(false) // more wind 23
+ << QVariant(100) // damage modfier 24
+ << QVariant(45) // turn time 25
+ << QVariant(100) // init health 26
+ << QVariant(15) // sudden death 27
+ << QVariant(5) // case prob 28
+ << QVariant(3) // mines time 29
+ << QVariant(4) // mines number 30
+ << QVariant(0) // mine dud pct 31
+ << QVariant(2) // explosives 32
+ << QVariant(35) // health case pct 33
+ << QVariant(25) // health case amt 34
+ << QVariant(47) // water rise amt 35
+ << QVariant(5) // health dec amt 36
;
QList<QVariant> minefield;
@@ -259,19 +269,21 @@
<< QVariant(false) // inf. attack 19
<< QVariant(false) // reset weps 20
<< QVariant(false) // per hog ammo 21
- << QVariant(150) // damage modfier 22
- << QVariant(30) // turn time 23
- << QVariant(50) // init health 24
- << QVariant(15) // sudden death 25
- << QVariant(0) // case prob 26
- << QVariant(0) // mines time 27
- << QVariant(80) // mines number 28
- << QVariant(0) // mine dud pct 29
- << QVariant(0) // explosives 30
- << QVariant(35) // health case pct 31
- << QVariant(25) // health case amt 32
- << QVariant(47) // water rise amt 33
- << QVariant(5) // health dec amt 34
+ << QVariant(false) // no wind 22
+ << QVariant(false) // more wind 23
+ << QVariant(150) // damage modfier 24
+ << QVariant(30) // turn time 25
+ << QVariant(50) // init health 26
+ << QVariant(15) // sudden death 27
+ << QVariant(0) // case prob 28
+ << QVariant(0) // mines time 29
+ << QVariant(80) // mines number 30
+ << QVariant(0) // mine dud pct 31
+ << QVariant(0) // explosives 32
+ << QVariant(35) // health case pct 33
+ << QVariant(25) // health case amt 34
+ << QVariant(47) // water rise amt 35
+ << QVariant(5) // health dec amt 36
;
QList<QVariant> barrelmayhem;
@@ -298,19 +310,21 @@
<< QVariant(false) // inf. attack 19
<< QVariant(false) // reset weps 20
<< QVariant(false) // per hog ammo 21
- << QVariant(100) // damage modfier 22
- << QVariant(30) // turn time 23
- << QVariant(100) // init health 24
- << QVariant(15) // sudden death 25
- << QVariant(0) // case prob 26
- << QVariant(0) // mines time 27
- << QVariant(0) // mines number 28
- << QVariant(0) // mine dud pct 29
- << QVariant(80) // explosives 30
- << QVariant(35) // health case pct 31
- << QVariant(25) // health case amt 32
- << QVariant(47) // water rise amt 33
- << QVariant(5) // health dec amt 34
+ << QVariant(false) // no wind 22
+ << QVariant(false) // more wind 23
+ << QVariant(100) // damage modfier 24
+ << QVariant(30) // turn time 25
+ << QVariant(100) // init health 26
+ << QVariant(15) // sudden death 27
+ << QVariant(0) // case prob 28
+ << QVariant(0) // mines time 29
+ << QVariant(0) // mines number 30
+ << QVariant(0) // mine dud pct 31
+ << QVariant(80) // explosives 32
+ << QVariant(35) // health case pct 33
+ << QVariant(25) // health case amt 34
+ << QVariant(47) // water rise amt 35
+ << QVariant(5) // health dec amt 36
;
QList<QVariant> tunnelhogs;
@@ -337,19 +351,21 @@
<< QVariant(false) // inf. attack 19
<< QVariant(false) // reset weps 20
<< QVariant(false) // per hog ammo 21
- << QVariant(100) // damage modfier 22
- << QVariant(30) // turn time 23
- << QVariant(100) // init health 24
- << QVariant(15) // sudden death 25
- << QVariant(5) // case prob 26
- << QVariant(3) // mines time 27
- << QVariant(10) // mines number 28
- << QVariant(10) // mine dud pct 29
- << QVariant(10) // explosives 30
- << QVariant(35) // health case pct 31
- << QVariant(25) // health case amt 32
- << QVariant(47) // water rise amt 33
- << QVariant(5) // health dec amt 34
+ << QVariant(false) // no wind 22
+ << QVariant(false) // more wind 23
+ << QVariant(100) // damage modfier 24
+ << QVariant(30) // turn time 25
+ << QVariant(100) // init health 26
+ << QVariant(15) // sudden death 27
+ << QVariant(5) // case prob 28
+ << QVariant(3) // mines time 29
+ << QVariant(10) // mines number 30
+ << QVariant(10) // mine dud pct 31
+ << QVariant(10) // explosives 32
+ << QVariant(35) // health case pct 33
+ << QVariant(25) // health case amt 34
+ << QVariant(47) // water rise amt 35
+ << QVariant(5) // health dec amt 36
;
QList<QVariant> forts;
@@ -376,19 +392,21 @@
<< QVariant(false) // inf. attack 19
<< QVariant(false) // reset weps 20
<< QVariant(false) // per hog ammo 21
- << QVariant(100) // damage modfier 22
- << QVariant(45) // turn time 23
- << QVariant(100) // init health 24
- << QVariant(15) // sudden death 25
- << QVariant(5) // case prob 26
- << QVariant(3) // mines time 27
- << QVariant(0) // mines number 28
- << QVariant(0) // mine dud pct 29
- << QVariant(0) // explosives 30
- << QVariant(35) // health case pct 31
- << QVariant(25) // health case amt 32
- << QVariant(47) // water rise amt 33
- << QVariant(5) // health dec amt 34
+ << QVariant(false) // no wind 22
+ << QVariant(false) // more wind 23
+ << QVariant(100) // damage modfier 24
+ << QVariant(45) // turn time 25
+ << QVariant(100) // init health 26
+ << QVariant(15) // sudden death 27
+ << QVariant(5) // case prob 28
+ << QVariant(3) // mines time 29
+ << QVariant(0) // mines number 30
+ << QVariant(0) // mine dud pct 31
+ << QVariant(0) // explosives 32
+ << QVariant(35) // health case pct 33
+ << QVariant(25) // health case amt 34
+ << QVariant(47) // water rise amt 35
+ << QVariant(5) // health dec amt 36
;
QList<QVariant> timeless;
@@ -415,19 +433,21 @@
<< QVariant(false) // inf. attack 19
<< QVariant(false) // reset weps 20
<< QVariant(true) // per hog ammo 21
- << QVariant(100) // damage modfier 22
- << QVariant(9999) // turn time 23
- << QVariant(100) // init health 24
- << QVariant(15) // sudden death 25
- << QVariant(5) // case prob 26
- << QVariant(3) // mines time 27
- << QVariant(5) // mines number 28
- << QVariant(10) // mine dud pct 29
- << QVariant(2) // explosives 30
- << QVariant(35) // health case pct 31
- << QVariant(30) // health case amt 32
- << QVariant(0) // water rise amt 33
- << QVariant(0) // health dec amt 34
+ << QVariant(false) // no wind 22
+ << QVariant(false) // more wind 23
+ << QVariant(100) // damage modfier 24
+ << QVariant(9999) // turn time 25
+ << QVariant(100) // init health 26
+ << QVariant(15) // sudden death 27
+ << QVariant(5) // case prob 28
+ << QVariant(3) // mines time 29
+ << QVariant(5) // mines number 30
+ << QVariant(10) // mine dud pct 31
+ << QVariant(2) // explosives 32
+ << QVariant(35) // health case pct 33
+ << QVariant(30) // health case amt 34
+ << QVariant(0) // water rise amt 35
+ << QVariant(0) // health dec amt 36
;
QList<QVariant> thinkingportals;
@@ -454,19 +474,21 @@
<< QVariant(false) // inf. attack 19
<< QVariant(false) // reset weps 20
<< QVariant(false) // per hog ammo 21
- << QVariant(100) // damage modfier 22
- << QVariant(45) // turn time 23
- << QVariant(100) // init health 24
- << QVariant(15) // sudden death 25
- << QVariant(2) // case prob 26
- << QVariant(3) // mines time 27
- << QVariant(5) // mines number 28
- << QVariant(0) // mine dud pct 29
- << QVariant(5) // explosives 30
- << QVariant(25) // health case pct 31
- << QVariant(25) // health case amt 32
- << QVariant(47) // water rise amt 33
- << QVariant(5) // health dec amt 34
+ << QVariant(false) // no wind 22
+ << QVariant(false) // more wind 23
+ << QVariant(100) // damage modfier 24
+ << QVariant(45) // turn time 25
+ << QVariant(100) // init health 26
+ << QVariant(15) // sudden death 27
+ << QVariant(2) // case prob 28
+ << QVariant(3) // mines time 29
+ << QVariant(5) // mines number 30
+ << QVariant(0) // mine dud pct 31
+ << QVariant(5) // explosives 32
+ << QVariant(25) // health case pct 33
+ << QVariant(25) // health case amt 34
+ << QVariant(47) // water rise amt 35
+ << QVariant(5) // health dec amt 36
;
QList<QVariant> kingmode;
@@ -493,19 +515,21 @@
<< QVariant(false) // inf. attack 19
<< QVariant(false) // reset weps 20
<< QVariant(false) // per hog ammo 21
- << QVariant(100) // damage modfier 22
- << QVariant(45) // turn time 23
- << QVariant(100) // init health 24
- << QVariant(15) // sudden death 25
- << QVariant(5) // case prob 26
- << QVariant(3) // mines time 27
- << QVariant(3) // mines number 28
- << QVariant(20) // mine dud pct 29
- << QVariant(3) // explosives 30
- << QVariant(35) // health case pct 31
- << QVariant(30) // health case amt 32
- << QVariant(30) // water rise amt 33
- << QVariant(5) // health dec amt 34
+ << QVariant(false) // no wind 22
+ << QVariant(false) // more wind 23
+ << QVariant(100) // damage modfier 24
+ << QVariant(45) // turn time 25
+ << QVariant(100) // init health 26
+ << QVariant(15) // sudden death 27
+ << QVariant(5) // case prob 28
+ << QVariant(3) // mines time 29
+ << QVariant(3) // mines number 30
+ << QVariant(20) // mine dud pct 31
+ << QVariant(3) // explosives 32
+ << QVariant(35) // health case pct 33
+ << QVariant(30) // health case amt 34
+ << QVariant(30) // water rise amt 35
+ << QVariant(5) // health dec amt 36
;
--- a/QTfrontend/gamecfgwidget.cpp Sat Nov 13 17:14:51 2010 +0300
+++ b/QTfrontend/gamecfgwidget.cpp Sat Nov 13 09:36:26 2010 -0500
@@ -149,13 +149,17 @@
result |= 0x00200000; // reset weaps
if (schemeData(21).toBool())
result |= 0x00400000; // per hog ammo
+ if (schemeData(22).toBool())
+ result |= 0x00800000; // no wind
+ if (schemeData(23).toBool())
+ result |= 0x01000000; // more wind
return result;
}
quint32 GameCFGWidget::getInitHealth() const
{
- return schemeData(24).toInt();
+ return schemeData(26).toInt();
}
QStringList GameCFGWidget::getFullConfig() const
@@ -163,18 +167,18 @@
QStringList sl;
sl.append("eseed " + pMapContainer->getCurrentSeed());
sl.append(QString("e$gmflags %1").arg(getGameFlags()));
- sl.append(QString("e$damagepct %1").arg(schemeData(22).toInt()));
- sl.append(QString("e$turntime %1").arg(schemeData(23).toInt() * 1000));
- sl.append(QString("e$minestime %1").arg(schemeData(27).toInt() * 1000));
- sl.append(QString("e$minesnum %1").arg(schemeData(28).toInt()));
- sl.append(QString("e$sd_turns %1").arg(schemeData(25).toInt()));
- sl.append(QString("e$casefreq %1").arg(schemeData(26).toInt()));
- sl.append(QString("e$minedudpct %1").arg(schemeData(29).toInt()));
- sl.append(QString("e$explosives %1").arg(schemeData(30).toInt()));
- sl.append(QString("e$healthprob %1").arg(schemeData(31).toInt()));
- sl.append(QString("e$hcaseamount %1").arg(schemeData(32).toInt()));
- sl.append(QString("e$waterrise %1").arg(schemeData(33).toInt()));
- sl.append(QString("e$healthdec %1").arg(schemeData(34).toInt()));
+ sl.append(QString("e$damagepct %1").arg(schemeData(24).toInt()));
+ sl.append(QString("e$turntime %1").arg(schemeData(25).toInt() * 1000));
+ sl.append(QString("e$sd_turns %1").arg(schemeData(27).toInt()));
+ sl.append(QString("e$casefreq %1").arg(schemeData(28).toInt()));
+ sl.append(QString("e$minestime %1").arg(schemeData(29).toInt()));
+ sl.append(QString("e$minesnum %1").arg(schemeData(30).toInt()));
+ sl.append(QString("e$minedudpct %1").arg(schemeData(31).toInt()));
+ sl.append(QString("e$explosives %1").arg(schemeData(32).toInt()));
+ sl.append(QString("e$healthprob %1").arg(schemeData(33).toInt()));
+ sl.append(QString("e$hcaseamount %1").arg(schemeData(34).toInt()));
+ sl.append(QString("e$waterrise %1").arg(schemeData(35).toInt()));
+ sl.append(QString("e$healthdec %1").arg(schemeData(36).toInt()));
sl.append(QString("e$template_filter %1").arg(pMapContainer->getTemplateFilter()));
sl.append(QString("e$mapgen %1").arg(pMapContainer->get_mapgen()));
sl.append(QString("e$maze_size %1").arg(pMapContainer->get_maze_size()));
--- a/QTfrontend/hedgewars.qrc Sat Nov 13 17:14:51 2010 +0300
+++ b/QTfrontend/hedgewars.qrc Sat Nov 13 09:36:26 2010 -0500
@@ -71,6 +71,8 @@
<file>res/btnInfAttack.png</file>
<file>res/btnResetWeps.png</file>
<file>res/btnPerHogAmmo.png</file>
+ <file>res/btnNoWind.png</file>
+ <file>res/btnMoreWind.png</file>
<file>res/iconBox.png</file>
<file>res/iconHealth.png</file>
<file>res/iconSuddenDeath.png</file>
--- a/QTfrontend/hwconsts.cpp.in Sat Nov 13 17:14:51 2010 +0300
+++ b/QTfrontend/hwconsts.cpp.in Sat Nov 13 09:36:26 2010 -0500
@@ -36,10 +36,10 @@
int cMaxTeams = 6;
QString * cDefaultAmmoStore = new QString(
- "9391929422199121032235111001201000000211110101011"
- "0405040541600655546554464776576666666155510101115"
- "0000000000000205500000040007004000000000200000000"
- "1311110312111111123114111111111111111211111101111"
+ "93919294221991210322351110012010000002111101010111"
+ "04050405416006555465544647765766666661555101011154"
+ "00000000000002055000000400070040000000002000000006"
+ "13111103121111111231141111111111111112111111011111"
);
int cAmmoNumber = cDefaultAmmoStore->size() / 4;
@@ -48,40 +48,40 @@
<< qMakePair(QString("Default"), *cDefaultAmmoStore)
<< qMakePair(QString("Crazy"), QString(
// TODO: Remove Piano's unlimited uses!
- "9999999999999999992999999999999999299999999909999"
- "1111110111111111111111111111111111111111111101111"
- "0000000000000000000000000000000000000000000000000"
- "1311110312111111123114111111111111111211110101111"
+ "99999999999999999929999999999999992999999999099999"
+ "11111101111111111111111111111111111111111111011111"
+ "00000000000000000000000000000000000000000000000000"
+ "13111103121111111231141111111111111112111101011111"
))
<< qMakePair(QString("Pro Mode"), QString(
- "9090009000000000000009000000000000000000000000000"
- "0000000000000000000000000000000000000000000000000"
- "0000000000000205500000040007004000000000200000000"
- "1111111111111111111111111111111111111111100101111"
+ "90900090000000000000090000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000"
+ "00000000000002055000000400070040000000002000000000"
+ "11111111111111111111111111111111111111111001011111"
))
<< qMakePair(QString("Shoppa"), QString(
- "0000009900000000000000000000000000000000000000000"
- "4444410044244402210112121222422000000002000400010"
- "0000000000000000000000000000000000000000000000000"
- "1111111111111111111111111111111111111111101101111"
+ "00000099000000000000000000000000000000000000000000"
+ "44444100442444022101121212224220000000020004000100"
+ "00000000000000000000000000000000000000000000000000"
+ "11111111111111111111111111111111111111111011011111"
))
<< qMakePair(QString("Clean Slate"),QString(
- "1010009000010000011000000000000000000000000000001"
- "0405040541600655546554464776576666666155510101115"
- "0000000000000000000000000000000000000000000000000"
- "1311110312111111123114111111111111111211111101111"
+ "10100090000100000110000000000000000000000000000010"
+ "04050405416006555465544647765766666661555101011154"
+ "00000000000000000000000000000000000000000000000000"
+ "13111103121111111231141111111111111112111111011111"
))
<< qMakePair(QString("Minefield"), QString(
- "0000009900090000000300000000000000000000000000000"
- "0000000000000000000000000000000000000000000000000"
- "0000000000000205500000040007004000000000200000000"
- "1111111111111111111111111111111111111111111101111"
+ "00000099000900000003000000000000000000000000000000"
+ "00000000000000000000000000000000000000000000000000"
+ "00000000000002055000000400070040000000002000000006"
+ "11111111111111111111111111111111111111111111011111"
))
<< qMakePair(QString("Thinking with Portals"), QString(
- "9000009002000000002100000000000000110000090000000"
- "0405040541600655546554464776576666666155510101115"
- "0000000000000205500000040007004000000000200000000"
- "1311110312111111123114111111111111111211111101111"
+ "90000090020000000021000000000000001100000900000000"
+ "04050405416006555465544647765766666661555101011154"
+ "00000000000002055000000400070040000000002000000006"
+ "13111103121111111231141111111111111112111111011111"
));
QColor *colors[] = {
--- a/QTfrontend/pages.cpp Sat Nov 13 17:14:51 2010 +0300
+++ b/QTfrontend/pages.cpp Sat Nov 13 09:36:26 2010 -0500
@@ -1562,6 +1562,14 @@
TBW_perhogammo->setToolTip("<b>" + ToggleButtonWidget::tr("Per Hedgehog Ammo") + "</b>:<br />" + tr("Each hedgehog has its own ammo. It does not share with the team."));
glGMLayout->addWidget(TBW_perhogammo,4,0,1,1);
+ TBW_nowind = new ToggleButtonWidget(gbGameModes, ":/res/btnNoWind.png");
+ TBW_nowind->setToolTip("<b>" + ToggleButtonWidget::tr("Disable Wind") + "</b>:<br />" + tr("You will not have to worry about wind anymore."));
+ glGMLayout->addWidget(TBW_nowind,4,1,1,1);
+
+ TBW_morewind = new ToggleButtonWidget(gbGameModes, ":/res/btnMoreWind.png");
+ TBW_morewind->setToolTip("<b>" + ToggleButtonWidget::tr("More Wind") + "</b>:<br />" + tr("Wind will affect almost everything."));
+ glGMLayout->addWidget(TBW_morewind,4,2,1,1);
+
// Right
QLabel * l;
@@ -1800,19 +1808,21 @@
mapper->addMapping(TBW_infattack, 19);
mapper->addMapping(TBW_resetweps, 20);
mapper->addMapping(TBW_perhogammo, 21);
- mapper->addMapping(SB_DamageModifier, 22);
- mapper->addMapping(SB_TurnTime, 23);
- mapper->addMapping(SB_InitHealth, 24);
- mapper->addMapping(SB_SuddenDeath, 25);
- mapper->addMapping(SB_CaseProb, 26);
- mapper->addMapping(SB_MinesTime, 27);
- mapper->addMapping(SB_Mines, 28);
- mapper->addMapping(SB_MineDuds, 29);
- mapper->addMapping(SB_Explosives, 30);
- mapper->addMapping(SB_HealthCrates, 31);
- mapper->addMapping(SB_CrateHealth, 32);
- mapper->addMapping(SB_WaterRise, 33);
- mapper->addMapping(SB_HealthDecrease, 34);
+ mapper->addMapping(TBW_nowind, 22);
+ mapper->addMapping(TBW_morewind, 23);
+ mapper->addMapping(SB_DamageModifier, 24);
+ mapper->addMapping(SB_TurnTime, 25);
+ mapper->addMapping(SB_InitHealth, 26);
+ mapper->addMapping(SB_SuddenDeath, 27);
+ mapper->addMapping(SB_CaseProb, 28);
+ mapper->addMapping(SB_MinesTime, 29);
+ mapper->addMapping(SB_Mines, 30);
+ mapper->addMapping(SB_MineDuds, 31);
+ mapper->addMapping(SB_Explosives, 32);
+ mapper->addMapping(SB_HealthCrates, 33);
+ mapper->addMapping(SB_CrateHealth, 34);
+ mapper->addMapping(SB_WaterRise, 35);
+ mapper->addMapping(SB_HealthDecrease, 36);
mapper->toFirst();
}
--- a/QTfrontend/pages.h Sat Nov 13 17:14:51 2010 +0300
+++ b/QTfrontend/pages.h Sat Nov 13 09:36:26 2010 -0500
@@ -481,6 +481,8 @@
ToggleButtonWidget * TBW_infattack;
ToggleButtonWidget * TBW_resetweps;
ToggleButtonWidget * TBW_perhogammo;
+ ToggleButtonWidget * TBW_nowind;
+ ToggleButtonWidget * TBW_morewind;
QSpinBox * SB_DamageModifier;
QSpinBox * SB_TurnTime;
Binary file QTfrontend/res/btnMoreWind.png has changed
Binary file QTfrontend/res/btnNoWind.png has changed
--- a/gameServer/Actions.hs Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/Actions.hs Sat Nov 13 09:36:26 2010 -0500
@@ -1,171 +1,134 @@
+{-# LANGUAGE OverloadedStrings #-}
module Actions where
-import Control.Concurrent.STM
+import Control.Concurrent
import Control.Concurrent.Chan
-import Data.IntMap
import qualified Data.IntSet as IntSet
+import qualified Data.Set as Set
import qualified Data.Sequence as Seq
import System.Log.Logger
-import Monad
+import Control.Monad
import Data.Time
-import Maybe
+import Data.Maybe
+import Control.Monad.Reader
+import Control.Monad.State.Strict
+import qualified Data.ByteString.Char8 as B
-----------------------------
import CoreTypes
import Utils
+import ClientIO
+import ServerState
data Action =
- AnswerThisClient [String]
- | AnswerAll [String]
- | AnswerAllOthers [String]
- | AnswerThisRoom [String]
- | AnswerOthersInRoom [String]
- | AnswerSameClan [String]
- | AnswerLobby [String]
+ AnswerClients ![ClientChan] ![B.ByteString]
| SendServerMessage
| SendServerVars
- | RoomAddThisClient Int -- roomID
- | RoomRemoveThisClient String
- | RemoveTeam String
+ | MoveToRoom RoomIndex
+ | MoveToLobby B.ByteString
+ | RemoveTeam B.ByteString
| RemoveRoom
| UnreadyRoomClients
- | MoveToLobby
- | ProtocolError String
- | Warning String
- | ByeClient String
- | KickClient Int -- clID
- | KickRoomClient Int -- clID
- | BanClient String -- nick
- | RemoveClientTeams Int -- clID
+ | JoinLobby
+ | ProtocolError B.ByteString
+ | Warning B.ByteString
+ | ByeClient B.ByteString
+ | KickClient ClientIndex
+ | KickRoomClient ClientIndex
+ | BanClient B.ByteString -- nick
+ | RemoveClientTeams ClientIndex
| ModifyClient (ClientInfo -> ClientInfo)
- | ModifyClient2 Int (ClientInfo -> ClientInfo)
+ | ModifyClient2 ClientIndex (ClientInfo -> ClientInfo)
| ModifyRoom (RoomInfo -> RoomInfo)
| ModifyServerInfo (ServerInfo -> ServerInfo)
- | AddRoom String String
+ | AddRoom B.ByteString B.ByteString
| CheckRegistered
| ClearAccountsCache
| ProcessAccountInfo AccountInfo
| Dump
| AddClient ClientInfo
+ | DeleteClient ClientIndex
| PingAll
| StatsAction
-type CmdHandler = Int -> Clients -> Rooms -> [String] -> [Action]
-
-replaceID a (b, c, d, e) = (a, c, d, e)
-
-processAction :: (Int, ServerInfo, Clients, Rooms) -> Action -> IO (Int, ServerInfo, Clients, Rooms)
-
-
-processAction (clID, serverInfo, clients, rooms) (AnswerThisClient msg) = do
- writeChan (sendChan $ clients ! clID) msg
- return (clID, serverInfo, clients, rooms)
+type CmdHandler = [B.ByteString] -> Reader (ClientIndex, IRnC) [Action]
-processAction (clID, serverInfo, clients, rooms) (AnswerAll msg) = do
- mapM_ (\cl -> writeChan (sendChan cl) msg) (elems clients)
- return (clID, serverInfo, clients, rooms)
-
-
-processAction (clID, serverInfo, clients, rooms) (AnswerAllOthers msg) = do
- mapM_ (\id' -> writeChan (sendChan $ clients ! id') msg) $
- Prelude.filter (\id' -> (id' /= clID) && logonPassed (clients ! id')) (keys clients)
- return (clID, serverInfo, clients, rooms)
-
-
-processAction (clID, serverInfo, clients, rooms) (AnswerThisRoom msg) = do
- mapM_ (\id' -> writeChan (sendChan $ clients ! id') msg) roomClients
- return (clID, serverInfo, clients, rooms)
- where
- roomClients = IntSet.elems $ playersIDs room
- room = rooms ! rID
- rID = roomID client
- client = clients ! clID
+processAction :: Action -> StateT ServerState IO ()
-processAction (clID, serverInfo, clients, rooms) (AnswerOthersInRoom msg) = do
- mapM_ (\id' -> writeChan (sendChan $ clients ! id') msg) $ Prelude.filter (/= clID) roomClients
- return (clID, serverInfo, clients, rooms)
- where
- roomClients = IntSet.elems $ playersIDs room
- room = rooms ! rID
- rID = roomID client
- client = clients ! clID
-
-
-processAction (clID, serverInfo, clients, rooms) (AnswerLobby msg) = do
- mapM_ (\id' -> writeChan (sendChan $ clients ! id') msg) roomClients
- return (clID, serverInfo, clients, rooms)
- where
- roomClients = IntSet.elems $ playersIDs room
- room = rooms ! 0
+processAction (AnswerClients chans msg) = do
+ liftIO $ map (flip seq ()) chans `seq` map (flip seq ()) msg `seq` mapM_ (flip writeChan msg) chans
-processAction (clID, serverInfo, clients, rooms) (AnswerSameClan msg) = do
- mapM_ (\cl -> writeChan (sendChan cl) msg) sameClanOrSpec
- return (clID, serverInfo, clients, rooms)
- where
- otherRoomClients = Prelude.map ((!) clients) $ IntSet.elems $ clID `IntSet.delete` (playersIDs room)
- sameClanOrSpec = if teamsInGame client > 0 then sameClanClients else spectators
- spectators = Prelude.filter (\cl -> teamsInGame cl == 0) otherRoomClients
- sameClanClients = Prelude.filter (\cl -> teamsInGame cl > 0 && clientClan cl == thisClan) otherRoomClients
- thisClan = clientClan client
- room = rooms ! rID
- rID = roomID client
- client = clients ! clID
-
-
-processAction (clID, serverInfo, clients, rooms) SendServerMessage = do
- writeChan (sendChan $ clients ! clID) ["SERVER_MESSAGE", message serverInfo]
- return (clID, serverInfo, clients, rooms)
- where
- client = clients ! clID
- message si = if clientProto client < latestReleaseVersion si then
+processAction SendServerMessage = do
+ chan <- client's sendChan
+ protonum <- client's clientProto
+ si <- liftM serverInfo get
+ let message = if protonum < latestReleaseVersion si then
serverMessageForOldVersions si
else
serverMessage si
+ processAction $ AnswerClients [chan] ["SERVER_MESSAGE", message]
+{-
-processAction (clID, serverInfo, clients, rooms) SendServerVars = do
+processAction (clID, serverInfo, rnc) SendServerVars = do
writeChan (sendChan $ clients ! clID) ("SERVER_VARS" : vars)
- return (clID, serverInfo, clients, rooms)
+ return (clID, serverInfo, rnc)
where
client = clients ! clID
vars = [
- "MOTD_NEW", serverMessage serverInfo,
- "MOTD_OLD", serverMessageForOldVersions serverInfo,
+ "MOTD_NEW", serverMessage serverInfo,
+ "MOTD_OLD", serverMessageForOldVersions serverInfo,
"LATEST_PROTO", show $ latestReleaseVersion serverInfo
]
-processAction (clID, serverInfo, clients, rooms) (ProtocolError msg) = do
- writeChan (sendChan $ clients ! clID) ["ERROR", msg]
- return (clID, serverInfo, clients, rooms)
+-}
-
-processAction (clID, serverInfo, clients, rooms) (Warning msg) = do
- writeChan (sendChan $ clients ! clID) ["WARNING", msg]
- return (clID, serverInfo, clients, rooms)
+processAction (ProtocolError msg) = do
+ chan <- client's sendChan
+ processAction $ AnswerClients [chan] ["ERROR", msg]
-processAction (clID, serverInfo, clients, rooms) (ByeClient msg) = do
- infoM "Clients" (show (clientUID client) ++ " quits: " ++ msg)
- (_, _, newClients, newRooms) <-
- if roomID client /= 0 then
- processAction (clID, serverInfo, clients, rooms) $ RoomRemoveThisClient "quit"
- else
- return (clID, serverInfo, clients, rooms)
+processAction (Warning msg) = do
+ chan <- client's sendChan
+ processAction $ AnswerClients [chan] ["WARNING", msg]
+
+processAction (ByeClient msg) = do
+ (Just ci) <- gets clientIndex
+ rnc <- gets roomsClients
+ ri <- clientRoomA
+
+ chan <- client's sendChan
+ ready <- client's isReady
- mapM_ (processAction (clID, serverInfo, newClients, newRooms)) $ answerOthersQuit ++ answerInformRoom
- writeChan (sendChan $ clients ! clID) ["BYE", msg]
- return (
- 0,
- serverInfo,
- delete clID newClients,
- adjust (\r -> r{
- playersIDs = IntSet.delete clID (playersIDs r),
- playersIn = (playersIn r) - 1,
- readyPlayers = if isReady client then readyPlayers r - 1 else readyPlayers r
- }) (roomID $ newClients ! clID) newRooms
- )
+ when (ri /= lobbyId) $ do
+ processAction $ MoveToLobby ("quit: " `B.append` msg)
+ liftIO $ modifyRoom rnc (\r -> r{
+ --playersIDs = IntSet.delete ci (playersIDs r)
+ playersIn = (playersIn r) - 1,
+ readyPlayers = if ready then readyPlayers r - 1 else readyPlayers r
+ }) ri
+ return ()
+
+ liftIO $ do
+ infoM "Clients" (show ci ++ " quits: " ++ (B.unpack msg))
+
+ --mapM_ (processAction (ci, serverInfo, rnc)) $ answerOthersQuit ++ answerInformRoom
+
+ processAction $ AnswerClients [chan] ["BYE", msg]
+
+ s <- get
+ put $! s{removedClients = ci `Set.insert` removedClients s}
+
+processAction (DeleteClient ci) = do
+ rnc <- gets roomsClients
+ liftIO $ removeClient rnc ci
+
+ s <- get
+ put $! s{removedClients = ci `Set.delete` removedClients s}
+
+{-
where
client = clients ! clID
clientNick = nick client
@@ -184,46 +147,57 @@
else
[AnswerAll ["LOBBY:LEFT", clientNick]]
else
- []
-
-
-processAction (clID, serverInfo, clients, rooms) (ModifyClient func) =
- return (clID, serverInfo, adjust func clID clients, rooms)
-
+ []
+-}
-processAction (clID, serverInfo, clients, rooms) (ModifyClient2 cl2ID func) =
- return (clID, serverInfo, adjust func cl2ID clients, rooms)
-
+processAction (ModifyClient f) = do
+ (Just ci) <- gets clientIndex
+ rnc <- gets roomsClients
+ liftIO $ modifyClient rnc f ci
+ return ()
-processAction (clID, serverInfo, clients, rooms) (ModifyRoom func) =
- return (clID, serverInfo, clients, adjust func rID rooms)
- where
- rID = roomID $ clients ! clID
+processAction (ModifyClient2 ci f) = do
+ rnc <- gets roomsClients
+ liftIO $ modifyClient rnc f ci
+ return ()
-processAction (clID, serverInfo, clients, rooms) (ModifyServerInfo func) =
- return (clID, func serverInfo, clients, rooms)
+processAction (ModifyRoom f) = do
+ rnc <- gets roomsClients
+ ri <- clientRoomA
+ liftIO $ modifyRoom rnc f ri
+ return ()
+{-
+
+processAction (clID, serverInfo, rnc) (ModifyServerInfo func) =
+ return (clID, func serverInfo, rnc)
+
+-}
-processAction (clID, serverInfo, clients, rooms) (RoomAddThisClient rID) =
- processAction (
- clID,
- serverInfo,
- adjust (\cl -> cl{roomID = rID, teamsInGame = if rID == 0 then teamsInGame cl else 0}) clID clients,
- adjust (\r -> r{playersIDs = IntSet.insert clID (playersIDs r), playersIn = (playersIn r) + 1}) rID $
- adjust (\r -> r{playersIDs = IntSet.delete clID (playersIDs r)}) 0 rooms
- ) joinMsg
- where
- client = clients ! clID
- joinMsg = if rID == 0 then
- AnswerAllOthers ["LOBBY:JOINED", nick client]
- else
- AnswerThisRoom ["JOINED", nick client]
+processAction (MoveToRoom ri) = do
+ (Just ci) <- gets clientIndex
+ rnc <- gets roomsClients
+ liftIO $ do
+ modifyClient rnc (\cl -> cl{teamsInGame = 0}) ci
+ modifyRoom rnc (\r -> r{playersIn = (playersIn r) + 1}) ri
+
+ liftIO $ moveClientToRoom rnc ri ci
+
+ chans <- liftM (map sendChan) $ roomClientsS ri
+ clNick <- client's nick
+ processAction $ AnswerClients chans ["JOINED", clNick]
-processAction (clID, serverInfo, clients, rooms) (RoomRemoveThisClient msg) = do
+processAction (MoveToLobby msg) = do
+ (Just ci) <- gets clientIndex
+ --ri <- clientRoomA
+ rnc <- gets roomsClients
+
+ liftIO $ moveClientToLobby rnc ci
+
+{-
(_, _, newClients, newRooms) <-
- if roomID client /= 0 then
if isMaster client then
if (gameinprogress room) && (playersIn room > 1) then
(changeMaster >>= (\state -> foldM processAction state
@@ -231,16 +205,15 @@
AnswerOthersInRoom ["WARNING", "Admin left the room"],
RemoveClientTeams clID]))
else -- not in game
- processAction (clID, serverInfo, clients, rooms) RemoveRoom
+ processAction (clID, serverInfo, rnc) RemoveRoom
else -- not master
foldM
processAction
- (clID, serverInfo, clients, rooms)
+ (clID, serverInfo, rnc)
[AnswerOthersInRoom ["LEFT", nick client, msg],
RemoveClientTeams clID]
- else -- in lobby
- return (clID, serverInfo, clients, rooms)
-
+
+
return (
clID,
serverInfo,
@@ -259,7 +232,7 @@
}
insertClientToRoom r = r{playersIDs = IntSet.insert clID (playersIDs r)}
changeMaster = do
- processAction (newMasterId, serverInfo, clients, rooms) $ AnswerThisClient ["ROOM_CONTROL_ACCESS", "1"]
+ processAction (newMasterId, serverInfo, rnc) $ AnswerThisClient ["ROOM_CONTROL_ACCESS", "1"]
return (
clID,
serverInfo,
@@ -270,34 +243,35 @@
otherPlayersSet = IntSet.delete clID (playersIDs room)
newMasterId = IntSet.findMin otherPlayersSet
newMasterClient = clients ! newMasterId
-
+-}
-processAction (clID, serverInfo, clients, rooms) (AddRoom roomName roomPassword) = do
- let newServerInfo = serverInfo {nextRoomID = newID}
+processAction (AddRoom roomName roomPassword) = do
+ Just clId <- gets clientIndex
+ rnc <- gets roomsClients
+ proto <- liftIO $ client'sM rnc clientProto clId
+
let room = newRoom{
- roomUID = newID,
- masterID = clID,
+ masterID = clId,
name = roomName,
password = roomPassword,
- roomProto = (clientProto client)
+ roomProto = proto
}
- processAction (clID, serverInfo, clients, rooms) $ AnswerLobby ["ROOM", "ADD", roomName]
+ rId <- liftIO $ addRoom rnc room
+
+ processAction $ MoveToRoom rId
+
+ chans <- liftM (map sendChan) $! roomClientsS lobbyId
- processAction (
- clID,
- newServerInfo,
- adjust (\cl -> cl{isMaster = True}) clID clients,
- insert newID room rooms
- ) $ RoomAddThisClient newID
- where
- newID = (nextRoomID serverInfo) - 1
- client = clients ! clID
+ mapM_ processAction [
+ AnswerClients chans ["ROOM", "ADD", roomName]
+ , ModifyClient (\cl -> cl{isMaster = True})
+ ]
-
-processAction (clID, serverInfo, clients, rooms) (RemoveRoom) = do
- processAction (clID, serverInfo, clients, rooms) $ AnswerLobby ["ROOM", "DEL", name room]
- processAction (clID, serverInfo, clients, rooms) $ AnswerOthersInRoom ["ROOMABANDONED", name room]
+{-
+processAction (clID, serverInfo, rnc) (RemoveRoom) = do
+ processAction (clID, serverInfo, rnc) $ AnswerLobby ["ROOM", "DEL", name room]
+ processAction (clID, serverInfo, rnc) $ AnswerOthersInRoom ["ROOMABANDONED", name room]
return (clID,
serverInfo,
Data.IntMap.map (\cl -> if roomID cl == rID then cl{roomID = 0, isMaster = False, isReady = False, teamsInGame = undefined} else cl) clients,
@@ -308,139 +282,163 @@
rID = roomID client
client = clients ! clID
-
-processAction (clID, serverInfo, clients, rooms) (UnreadyRoomClients) = do
- processAction (clID, serverInfo, clients, rooms) $ AnswerThisRoom ("NOT_READY" : roomPlayers)
- return (clID,
- serverInfo,
- Data.IntMap.map (\cl -> if roomID cl == rID then cl{isReady = False} else cl) clients,
- adjust (\r -> r{readyPlayers = 0}) rID rooms)
- where
- room = rooms ! rID
- rID = roomID client
- client = clients ! clID
- roomPlayers = Prelude.map (nick . (clients !)) roomPlayersIDs
- roomPlayersIDs = IntSet.elems $ playersIDs room
+-}
+processAction (UnreadyRoomClients) = do
+ rnc <- gets roomsClients
+ ri <- clientRoomA
+ roomPlayers <- roomClientsS ri
+ roomClIDs <- liftIO $ roomClientsIndicesM rnc ri
+ processAction $ AnswerClients (map sendChan roomPlayers) ("NOT_READY" : map nick roomPlayers)
+ liftIO $ mapM_ (modifyClient rnc (\cl -> cl{isReady = False})) roomClIDs
+ processAction $ ModifyRoom (\r -> r{readyPlayers = 0})
-processAction (clID, serverInfo, clients, rooms) (RemoveTeam teamName) = do
- newRooms <- if not $ gameinprogress room then
- do
- processAction (clID, serverInfo, clients, rooms) $ AnswerOthersInRoom ["REMOVE_TEAM", teamName]
- return $
- adjust (\r -> r{teams = Prelude.filter (\t -> teamName /= teamname t) $ teams r}) rID rooms
+processAction (RemoveTeam teamName) = do
+ rnc <- gets roomsClients
+ cl <- client's id
+ ri <- clientRoomA
+ inGame <- liftIO $ room'sM rnc gameinprogress ri
+ chans <- liftM (map sendChan . filter (/= cl)) $ roomClientsS ri
+ if inGame then
+ mapM_ processAction [
+ AnswerClients chans ["REMOVE_TEAM", teamName],
+ ModifyRoom (\r -> r{teams = Prelude.filter (\t -> teamName /= teamname t) $ teams r})
+ ]
else
- do
- processAction (clID, serverInfo, clients, rooms) $ AnswerOthersInRoom ["EM", rmTeamMsg]
- return $
- adjust (\r -> r{
- teams = Prelude.filter (\t -> teamName /= teamname t) $ teams r,
- leftTeams = teamName : leftTeams r,
- roundMsgs = roundMsgs r Seq.|> rmTeamMsg
- }) rID rooms
- return (clID, serverInfo, clients, newRooms)
+ mapM_ processAction [
+ AnswerClients chans ["EM", rmTeamMsg],
+ ModifyRoom (\r -> r{
+ teams = Prelude.filter (\t -> teamName /= teamname t) $ teams r,
+ leftTeams = teamName : leftTeams r,
+ roundMsgs = roundMsgs r Seq.|> rmTeamMsg
+ })
+ ]
where
- room = rooms ! rID
- rID = roomID client
- client = clients ! clID
- rmTeamMsg = toEngineMsg $ 'F' : teamName
+ rmTeamMsg = toEngineMsg $ (B.singleton 'F') `B.append` teamName
+processAction CheckRegistered = do
+ (Just ci) <- gets clientIndex
+ n <- client's nick
+ h <- client's host
+ db <- gets (dbQueries . serverInfo)
+ liftIO $ writeChan db $ CheckAccount ci n h
+ return ()
-processAction (clID, serverInfo, clients, rooms) (CheckRegistered) = do
- writeChan (dbQueries serverInfo) $ CheckAccount (clientUID client) (nick client) (host client)
- return (clID, serverInfo, clients, rooms)
+{-
+processAction (clID, serverInfo, rnc) (ClearAccountsCache) = do
+ writeChan (dbQueries serverInfo) ClearCache
+ return (clID, serverInfo, rnc)
where
client = clients ! clID
-processAction (clID, serverInfo, clients, rooms) (ClearAccountsCache) = do
- writeChan (dbQueries serverInfo) ClearCache
- return (clID, serverInfo, clients, rooms)
- where
- client = clients ! clID
-
+processAction (clID, serverInfo, rnc) (Dump) = do
+ writeChan (sendChan $ clients ! clID) ["DUMP", show serverInfo, showTree clients, showTree rooms]
+ return (clID, serverInfo, rnc)
+-}
-processAction (clID, serverInfo, clients, rooms) (Dump) = do
- writeChan (sendChan $ clients ! clID) ["DUMP", show serverInfo, showTree clients, showTree rooms]
- return (clID, serverInfo, clients, rooms)
-
-
-processAction (clID, serverInfo, clients, rooms) (ProcessAccountInfo info) =
+processAction (ProcessAccountInfo info) =
case info of
HasAccount passwd isAdmin -> do
- infoM "Clients" $ show clID ++ " has account"
- writeChan (sendChan $ clients ! clID) ["ASKPASSWORD"]
- return (clID, serverInfo, adjust (\cl -> cl{webPassword = passwd, isAdministrator = isAdmin}) clID clients, rooms)
+ chan <- client's sendChan
+ processAction $ AnswerClients [chan] ["ASKPASSWORD"]
Guest -> do
- infoM "Clients" $ show clID ++ " is guest"
- processAction (clID, serverInfo, adjust (\cl -> cl{logonPassed = True}) clID clients, rooms) MoveToLobby
+ processAction JoinLobby
Admin -> do
- infoM "Clients" $ show clID ++ " is admin"
- foldM processAction (clID, serverInfo, adjust (\cl -> cl{logonPassed = True, isAdministrator = True}) clID clients, rooms) [MoveToLobby, AnswerThisClient ["ADMIN_ACCESS"]]
+ mapM processAction [ModifyClient (\cl -> cl{isAdministrator = True}), JoinLobby]
+ chan <- client's sendChan
+ processAction $ AnswerClients [chan] ["ADMIN_ACCESS"]
-processAction (clID, serverInfo, clients, rooms) (MoveToLobby) =
- foldM processAction (clID, serverInfo, clients, rooms) $
- (RoomAddThisClient 0)
- : answerLobbyNicks
- ++ [SendServerMessage]
+processAction JoinLobby = do
+ chan <- client's sendChan
+ clientNick <- client's nick
+ (lobbyNicks, clientsChans) <- liftM (unzip . Prelude.map (\c -> (nick c, sendChan c)) . Prelude.filter logonPassed) $! allClientsS
+ mapM_ processAction $
+ (AnswerClients clientsChans ["LOBBY:JOINED", clientNick])
+ : [AnswerClients [chan] ("LOBBY:JOINED" : clientNick : lobbyNicks)]
+ ++ [ModifyClient (\cl -> cl{logonPassed = True}), SendServerMessage]
- -- ++ (answerServerMessage client clients)
+{-
+processAction (clID, serverInfo, rnc) (RoomAddThisClient rID) =
+ processAction (
+ clID,
+ serverInfo,
+ adjust (\cl -> cl{roomID = rID, teamsInGame = if rID == 0 then teamsInGame cl else 0}) clID clients,
+ adjust (\r -> r{playersIDs = IntSet.insert clID (playersIDs r), playersIn = (playersIn r) + 1}) rID $
+ adjust (\r -> r{playersIDs = IntSet.delete clID (playersIDs r)}) 0 rooms
+ ) joinMsg
where
- lobbyNicks = Prelude.map nick $ Prelude.filter logonPassed $ elems clients
- answerLobbyNicks = [AnswerThisClient ("LOBBY:JOINED": lobbyNicks) | not $ Prelude.null lobbyNicks]
+ client = clients ! clID
+ joinMsg = if rID == 0 then
+ AnswerAllOthers ["LOBBY:JOINED", nick client]
+ else
+ AnswerThisRoom ["JOINED", nick client]
+
+processAction (clID, serverInfo, rnc) (KickClient kickID) =
+ liftM2 replaceID (return clID) (processAction (kickID, serverInfo, rnc) $ ByeClient "Kicked")
-processAction (clID, serverInfo, clients, rooms) (KickClient kickID) =
- liftM2 replaceID (return clID) (processAction (kickID, serverInfo, clients, rooms) $ ByeClient "Kicked")
-
-
-processAction (clID, serverInfo, clients, rooms) (BanClient banNick) =
- return (clID, serverInfo, clients, rooms)
+processAction (clID, serverInfo, rnc) (BanClient banNick) =
+ return (clID, serverInfo, rnc)
-processAction (clID, serverInfo, clients, rooms) (KickRoomClient kickID) = do
+processAction (clID, serverInfo, rnc) (KickRoomClient kickID) = do
writeChan (sendChan $ clients ! kickID) ["KICKED"]
- liftM2 replaceID (return clID) (processAction (kickID, serverInfo, clients, rooms) $ RoomRemoveThisClient "kicked")
+ liftM2 replaceID (return clID) (processAction (kickID, serverInfo, rnc) $ RoomRemoveThisClient "kicked")
-processAction (clID, serverInfo, clients, rooms) (RemoveClientTeams teamsClID) =
+processAction (clID, serverInfo, rnc) (RemoveClientTeams teamsClID) =
liftM2 replaceID (return clID) $
- foldM processAction (teamsClID, serverInfo, clients, rooms) removeTeamsActions
+ foldM processAction (teamsClID, serverInfo, rnc) removeTeamsActions
where
client = clients ! teamsClID
room = rooms ! (roomID client)
teamsToRemove = Prelude.filter (\t -> teamowner t == nick client) $ teams room
removeTeamsActions = Prelude.map (RemoveTeam . teamname) teamsToRemove
-
+-}
-processAction (clID, serverInfo, clients, rooms) (AddClient client) = do
- let updatedClients = insert (clientUID client) client clients
- infoM "Clients" (show (clientUID client) ++ ": New client. Time: " ++ show (connectTime client))
- writeChan (sendChan client) ["CONNECTED", "Hedgewars server http://www.hedgewars.org/"]
+processAction (AddClient client) = do
+ rnc <- gets roomsClients
+ si <- gets serverInfo
+ liftIO $ do
+ ci <- addClient rnc client
+ forkIO $ clientRecvLoop (clientSocket client) (coreChan si) ci
+ forkIO $ clientSendLoop (clientSocket client) (sendChan client) ci
- let newLogins = takeWhile (\(_ , time) -> (connectTime client) `diffUTCTime` time <= 11) $ lastLogins serverInfo
+ infoM "Clients" (show ci ++ ": New client. Time: " ++ show (connectTime client))
+
+ processAction $ AnswerClients [sendChan client] ["CONNECTED", "Hedgewars server http://www.hedgewars.org/"]
+{- let newLogins = takeWhile (\(_ , time) -> (connectTime client) `diffUTCTime` time <= 11) $ lastLogins serverInfo
- if isJust $ host client `Prelude.lookup` newLogins then
- processAction (clID, serverInfo{lastLogins = newLogins}, updatedClients, rooms) $ ByeClient "Reconnected too fast"
- else
- return (clID, serverInfo{lastLogins = (host client, connectTime client) : newLogins}, updatedClients, rooms)
+ if False && (isJust $ host client `Prelude.lookup` newLogins) then
+ processAction (ci, serverInfo{lastLogins = newLogins}, rnc) $ ByeClient "Reconnected too fast"
+ else
+ return (ci, serverInfo)
+-}
+
-processAction (clID, serverInfo, clients, rooms) PingAll = do
- (_, _, newClients, newRooms) <- foldM kickTimeouted (clID, serverInfo, clients, rooms) $ elems clients
- processAction (clID,
- serverInfo,
- Data.IntMap.map (\cl -> cl{pingsQueue = pingsQueue cl + 1}) newClients,
- newRooms) $ AnswerAll ["PING"]
+processAction PingAll = do
+ rnc <- gets roomsClients
+ liftIO (allClientsM rnc) >>= mapM_ (kickTimeouted rnc)
+ cis <- liftIO $ allClientsM rnc
+ chans <- liftIO $ mapM (client'sM rnc sendChan) cis
+ liftIO $ mapM_ (modifyClient rnc (\cl -> cl{pingsQueue = pingsQueue cl + 1})) cis
+ processAction $ AnswerClients chans ["PING"]
where
- kickTimeouted (clID, serverInfo, clients, rooms) client =
- if pingsQueue client > 0 then
- processAction (clientUID client, serverInfo, clients, rooms) $ ByeClient "Ping timeout"
- else
- return (clID, serverInfo, clients, rooms)
+ kickTimeouted rnc ci = do
+ pq <- liftIO $ client'sM rnc pingsQueue ci
+ when (pq > 0) $
+ withStateT (\as -> as{clientIndex = Just ci}) $
+ processAction (ByeClient "Ping timeout")
-processAction (clID, serverInfo, clients, rooms) (StatsAction) = do
- writeChan (dbQueries serverInfo) $ SendStats (size clients) (size rooms - 1)
- return (clID, serverInfo, clients, rooms)
+processAction (StatsAction) = do
+ rnc <- gets roomsClients
+ si <- gets serverInfo
+ (roomsNum, clientsNum) <- liftIO $ withRoomsAndClients rnc stats
+ liftIO $ writeChan (dbQueries si) $ SendStats clientsNum (roomsNum - 1)
+ where
+ stats irnc = (length $ allRooms irnc, length $ allClients irnc)
+
--- a/gameServer/CMakeLists.txt Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/CMakeLists.txt Sat Nov 13 09:36:26 2010 -0500
@@ -1,43 +1,48 @@
find_program(ghc_executable ghc)
if(NOT ghc_executable)
- message(FATAL_ERROR "Cannot find GHC")
+ message(FATAL_ERROR "Cannot find GHC")
endif(NOT ghc_executable)
set(hwserver_sources
- OfficialServer/DBInteraction.hs
- Actions.hs
- ClientIO.hs
- CoreTypes.hs
- HWProtoCore.hs
- HWProtoInRoomState.hs
- HWProtoLobbyState.hs
- HWProtoNEState.hs
- NetRoutines.hs
- Opts.hs
- ServerCore.hs
- Utils.hs
- hedgewars-server.hs
- )
+ OfficialServer/DBInteraction.hs
+ Actions.hs
+ ClientIO.hs
+ CoreTypes.hs
+ HWProtoCore.hs
+ HWProtoInRoomState.hs
+ HWProtoLobbyState.hs
+ HWProtoNEState.hs
+ HandlerUtils.hs
+ NetRoutines.hs
+ Opts.hs
+ RoomsAndClients.hs
+ ServerCore.hs
+ ServerState.hs
+ Store.hs
+ Utils.hs
+ hedgewars-server.hs
+ )
set(hwserv_main ${hedgewars_SOURCE_DIR}/gameServer/hedgewars-server.hs)
set(ghc_flags
- --make ${hwserv_main}
- -i${hedgewars_SOURCE_DIR}/gameServer
- -o ${EXECUTABLE_OUTPUT_PATH}/hedgewars-server${CMAKE_EXECUTABLE_SUFFIX}
- -odir ${CMAKE_CURRENT_BINARY_DIR}
- -hidir ${CMAKE_CURRENT_BINARY_DIR})
+ -Wall
+ --make ${hwserv_main}
+ -i${hedgewars_SOURCE_DIR}/gameServer
+ -o ${EXECUTABLE_OUTPUT_PATH}/hedgewars-server${CMAKE_EXECUTABLE_SUFFIX}
+ -odir ${CMAKE_CURRENT_BINARY_DIR}
+ -hidir ${CMAKE_CURRENT_BINARY_DIR})
set(ghc_flags ${haskell_compiler_flags_cmn} ${ghc_flags})
add_custom_command(OUTPUT "${EXECUTABLE_OUTPUT_PATH}/hedgewars-server${CMAKE_EXECUTABLE_SUFFIX}"
- COMMAND "${ghc_executable}"
- ARGS ${ghc_flags}
- MAIN_DEPENDENCY ${hwserv_main}
- DEPENDS ${hwserver_sources}
- )
+ COMMAND "${ghc_executable}"
+ ARGS ${ghc_flags}
+ MAIN_DEPENDENCY ${hwserv_main}
+ DEPENDS ${hwserver_sources}
+ )
add_custom_target(hedgewars-server ALL DEPENDS "${EXECUTABLE_OUTPUT_PATH}/hedgewars-server${CMAKE_EXECUTABLE_SUFFIX}")
--- a/gameServer/ClientIO.hs Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/ClientIO.hs Sat Nov 13 09:36:26 2010 -0500
@@ -1,4 +1,4 @@
-{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE ScopedTypeVariables, OverloadedStrings #-}
module ClientIO where
import qualified Control.Exception as Exception
@@ -6,45 +6,71 @@
import Control.Concurrent
import Control.Monad
import System.IO
-import qualified Data.ByteString.UTF8 as BUTF8
-import qualified Data.ByteString as B
+import Network
+import Network.Socket.ByteString
+import qualified Data.ByteString.Char8 as B
----------------
import CoreTypes
+import RoomsAndClients
+import Utils
-listenLoop :: Handle -> Int -> [String] -> Chan CoreMessage -> Int -> IO ()
-listenLoop handle linesNumber buf chan clientID = do
- str <- liftM BUTF8.toString $ B.hGetLine handle
- if (linesNumber > 50) || (length str > 450) then
- writeChan chan $ ClientMessage (clientID, ["QUIT", "Protocol violation"])
- else
- if str == "" then do
- writeChan chan $ ClientMessage (clientID, buf)
- yield
- listenLoop handle 0 [] chan clientID
- else
- listenLoop handle (linesNumber + 1) (buf ++ [str]) chan clientID
+
+pDelim :: B.ByteString
+pDelim = B.pack "\n\n"
+
+bs2Packets :: B.ByteString -> ([[B.ByteString]], B.ByteString)
+bs2Packets buf = unfoldrE extractPackets buf
+ where
+ extractPackets :: B.ByteString -> Either B.ByteString ([B.ByteString], B.ByteString)
+ extractPackets buf =
+ let buf' = until (not . B.isPrefixOf pDelim) (B.drop 2) buf in
+ let (bsPacket, bufTail) = B.breakSubstring pDelim buf' in
+ if B.null bufTail then
+ Left bsPacket
+ else
+ if B.null bsPacket then
+ Left bufTail
+ else
+ Right (B.splitWith (== '\n') bsPacket, bufTail)
+
-clientRecvLoop :: Handle -> Chan CoreMessage -> Int -> IO ()
-clientRecvLoop handle chan clientID =
- listenLoop handle 0 [] chan clientID
- `catch` (\e -> clientOff (show e) >> return ())
- where clientOff msg = writeChan chan $ ClientMessage (clientID, ["QUIT", msg]) -- if the client disconnects, we perform as if it sent QUIT message
+listenLoop :: Socket -> Chan CoreMessage -> ClientIndex -> IO ()
+listenLoop sock chan ci = recieveWithBufferLoop B.empty
+ where
+ recieveWithBufferLoop recvBuf = do
+ recvBS <- recv sock 4096
+-- putStrLn $ show sock ++ " got smth: " ++ (show $ B.length recvBS)
+ unless (B.null recvBS) $ do
+ let (packets, newrecvBuf) = bs2Packets $ B.append recvBuf recvBS
+ forM_ packets sendPacket
+ recieveWithBufferLoop newrecvBuf
+
+ sendPacket packet = writeChan chan $ ClientMessage (ci, packet)
-clientSendLoop :: Handle -> Chan CoreMessage -> Chan [String] -> Int -> IO()
-clientSendLoop handle coreChan chan clientID = do
+
+clientRecvLoop :: Socket -> Chan CoreMessage -> ClientIndex -> IO ()
+clientRecvLoop s chan ci = do
+ msg <- (listenLoop s chan ci >> return "Connection closed") `catch` (return . B.pack . show)
+ clientOff msg
+ where
+ clientOff msg = mapM_ (writeChan chan) [ClientMessage (ci, ["QUIT", msg]), Remove ci]
+
+
+
+clientSendLoop :: Socket -> Chan [B.ByteString] -> ClientIndex -> IO ()
+clientSendLoop s chan ci = do
answer <- readChan chan
- doClose <- Exception.handle
- (\(e :: Exception.IOException) -> if isQuit answer then return True else sendQuit e >> return False) $ do
- B.hPutStrLn handle $ BUTF8.fromString $ unlines answer
- hFlush handle
- return $ isQuit answer
+ Exception.handle
+ (\(e :: Exception.IOException) -> when (not $ isQuit answer) $ sendQuit e) $ do
+ sendAll s $ (B.unlines answer) `B.append` (B.singleton '\n')
- if doClose then
- Exception.handle (\(_ :: Exception.IOException) -> putStrLn "error on hClose") $ hClose handle
+ if (isQuit answer) then
+ Exception.handle (\(_ :: Exception.IOException) -> putStrLn "error on sClose") $ sClose s
else
- clientSendLoop handle coreChan chan clientID
+ clientSendLoop s chan ci
where
- sendQuit e = writeChan coreChan $ ClientMessage (clientID, ["QUIT", show e])
+ --sendQuit e = writeChan coreChan $ ClientMessage (ci, ["QUIT", B.pack $ show e])
+ sendQuit e = putStrLn $ show e
isQuit ("BYE":xs) = True
isQuit _ = False
--- a/gameServer/CoreTypes.hs Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/CoreTypes.hs Sat Nov 13 09:36:26 2010 -0500
@@ -1,3 +1,4 @@
+{-# LANGUAGE OverloadedStrings #-}
module CoreTypes where
import System.IO
@@ -5,102 +6,95 @@
import Control.Concurrent.STM
import Data.Word
import qualified Data.Map as Map
-import qualified Data.IntMap as IntMap
import qualified Data.IntSet as IntSet
import Data.Sequence(Seq, empty)
import Data.Time
import Network
import Data.Function
+import Data.ByteString.Char8 as B
+import RoomsAndClients
+
+type ClientChan = Chan [B.ByteString]
data ClientInfo =
ClientInfo
{
- clientUID :: !Int,
- sendChan :: Chan [String],
- clientHandle :: Handle,
- host :: String,
+ sendChan :: ClientChan,
+ clientSocket :: Socket,
+ host :: B.ByteString,
connectTime :: UTCTime,
- nick :: String,
- webPassword :: String,
+ nick :: B.ByteString,
+ webPassword :: B.ByteString,
logonPassed :: Bool,
clientProto :: !Word16,
- roomID :: !Int,
+ roomID :: RoomIndex,
pingsQueue :: !Word,
isMaster :: Bool,
- isReady :: Bool,
+ isReady :: !Bool,
isAdministrator :: Bool,
- clientClan :: String,
+ clientClan :: B.ByteString,
teamsInGame :: Word
}
instance Show ClientInfo where
- show ci = show (clientUID ci)
- ++ " nick: " ++ (nick ci)
- ++ " host: " ++ (host ci)
+ show ci = " nick: " ++ (unpack $ nick ci) ++ " host: " ++ (unpack $ host ci)
instance Eq ClientInfo where
- (==) = (==) `on` clientHandle
+ (==) = (==) `on` clientSocket
data HedgehogInfo =
- HedgehogInfo String String
+ HedgehogInfo B.ByteString B.ByteString
data TeamInfo =
TeamInfo
{
- teamownerId :: !Int,
- teamowner :: String,
- teamname :: String,
- teamcolor :: String,
- teamgrave :: String,
- teamfort :: String,
- teamvoicepack :: String,
- teamflag :: String,
+ teamownerId :: ClientIndex,
+ teamowner :: B.ByteString,
+ teamname :: B.ByteString,
+ teamcolor :: B.ByteString,
+ teamgrave :: B.ByteString,
+ teamfort :: B.ByteString,
+ teamvoicepack :: B.ByteString,
+ teamflag :: B.ByteString,
difficulty :: Int,
hhnum :: Int,
hedgehogs :: [HedgehogInfo]
}
instance Show TeamInfo where
- show ti = "owner: " ++ (teamowner ti)
- ++ "name: " ++ (teamname ti)
- ++ "color: " ++ (teamcolor ti)
+ show ti = "owner: " ++ (unpack $ teamowner ti)
+ ++ "name: " ++ (unpack $ teamname ti)
+ ++ "color: " ++ (unpack $ teamcolor ti)
data RoomInfo =
RoomInfo
{
- roomUID :: !Int,
- masterID :: !Int,
- name :: String,
- password :: String,
+ masterID :: ClientIndex,
+ name :: B.ByteString,
+ password :: B.ByteString,
roomProto :: Word16,
teams :: [TeamInfo],
gameinprogress :: Bool,
playersIn :: !Int,
readyPlayers :: !Int,
- playersIDs :: IntSet.IntSet,
isRestrictedJoins :: Bool,
isRestrictedTeams :: Bool,
- roundMsgs :: Seq String,
- leftTeams :: [String],
+ roundMsgs :: Seq B.ByteString,
+ leftTeams :: [B.ByteString],
teamsAtStart :: [TeamInfo],
- params :: Map.Map String [String]
+ params :: Map.Map B.ByteString [B.ByteString]
}
instance Show RoomInfo where
- show ri = show (roomUID ri)
- ++ ", players ids: " ++ show (IntSet.size $ playersIDs ri)
- ++ ", players: " ++ show (playersIn ri)
+ show ri = ", players: " ++ show (playersIn ri)
++ ", ready: " ++ show (readyPlayers ri)
++ ", teams: " ++ show (teams ri)
-instance Eq RoomInfo where
- (==) = (==) `on` roomUID
-
+newRoom :: RoomInfo
newRoom = (
RoomInfo
- 0
- 0
+ undefined
""
""
0
@@ -108,7 +102,6 @@
False
0
0
- IntSet.empty
False
False
Data.Sequence.empty
@@ -128,23 +121,24 @@
ServerInfo
{
isDedicated :: Bool,
- serverMessage :: String,
- serverMessageForOldVersions :: String,
+ serverMessage :: B.ByteString,
+ serverMessageForOldVersions :: B.ByteString,
latestReleaseVersion :: Word16,
listenPort :: PortNumber,
nextRoomID :: Int,
- dbHost :: String,
- dbLogin :: String,
- dbPassword :: String,
- lastLogins :: [(String, UTCTime)],
+ dbHost :: B.ByteString,
+ dbLogin :: B.ByteString,
+ dbPassword :: B.ByteString,
+ lastLogins :: [(B.ByteString, UTCTime)],
stats :: TMVar StatisticsInfo,
coreChan :: Chan CoreMessage,
dbQueries :: Chan DBQuery
}
instance Show ServerInfo where
- show si = "Server Info"
+ show _ = "Server Info"
+newServerInfo :: TMVar StatisticsInfo -> Chan CoreMessage -> Chan DBQuery -> ServerInfo
newServerInfo = (
ServerInfo
True
@@ -160,29 +154,31 @@
)
data AccountInfo =
- HasAccount String Bool
+ HasAccount B.ByteString Bool
| Guest
| Admin
deriving (Show, Read)
data DBQuery =
- CheckAccount Int String String
+ CheckAccount ClientIndex B.ByteString B.ByteString
| ClearCache
| SendStats Int Int
deriving (Show, Read)
data CoreMessage =
Accept ClientInfo
- | ClientMessage (Int, [String])
- | ClientAccountInfo (Int, AccountInfo)
+ | ClientMessage (ClientIndex, [B.ByteString])
+ | ClientAccountInfo (ClientIndex, AccountInfo)
| TimerAction Int
-
-type Clients = IntMap.IntMap ClientInfo
-type Rooms = IntMap.IntMap RoomInfo
+ | Remove ClientIndex
---type ClientsTransform = [ClientInfo] -> [ClientInfo]
---type RoomsTransform = [RoomInfo] -> [RoomInfo]
---type HandlesSelector = ClientInfo -> [ClientInfo] -> [RoomInfo] -> [ClientInfo]
---type Answer = ServerInfo -> (HandlesSelector, [String])
+instance Show CoreMessage where
+ show (Accept _) = "Accept"
+ show (ClientMessage _) = "ClientMessage"
+ show (ClientAccountInfo _) = "ClientAccountInfo"
+ show (TimerAction _) = "TimerAction"
+ show (Remove _) = "Remove"
+
+type MRnC = MRoomsAndClients RoomInfo ClientInfo
+type IRnC = IRoomsAndClients RoomInfo ClientInfo
-type ClientsSelector = Clients -> Rooms -> [Int]
--- a/gameServer/HWProtoCore.hs Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/HWProtoCore.hs Sat Nov 13 09:36:26 2010 -0500
@@ -1,8 +1,10 @@
+{-# LANGUAGE OverloadedStrings #-}
module HWProtoCore where
import qualified Data.IntMap as IntMap
import Data.Foldable
-import Maybe
+import Data.Maybe
+import Control.Monad.Reader
--------------------------------------
import CoreTypes
import Actions
@@ -10,35 +12,37 @@
import HWProtoNEState
import HWProtoLobbyState
import HWProtoInRoomState
+import HandlerUtils
+import RoomsAndClients
handleCmd, handleCmd_loggedin :: CmdHandler
-handleCmd clID _ _ ["PING"] = [AnswerThisClient ["PONG"]]
+
+handleCmd ["PING"] = answerClient ["PONG"]
-handleCmd clID clients rooms ("QUIT" : xs) =
- [ByeClient msg]
+
+handleCmd ("QUIT" : xs) = return [ByeClient msg]
where
msg = if not $ null xs then head xs else ""
-
-handleCmd clID clients _ ["PONG"] =
+{-
+handleCmd ["PONG"] =
if pingsQueue client == 0 then
[ProtocolError "Protocol violation"]
else
[ModifyClient (\cl -> cl{pingsQueue = pingsQueue cl - 1})]
where
client = clients IntMap.! clID
-
+-}
-handleCmd clID clients rooms cmd =
- if not $ logonPassed client then
- handleCmd_NotEntered clID clients rooms cmd
- else
- handleCmd_loggedin clID clients rooms cmd
- where
- client = clients IntMap.! clID
+handleCmd cmd = do
+ (ci, irnc) <- ask
+ if logonPassed (irnc `client` ci) then
+ handleCmd_loggedin cmd
+ else
+ handleCmd_NotEntered cmd
-
+{-
handleCmd_loggedin clID clients rooms ["INFO", asknick] =
if noSuchClient then
[]
@@ -62,11 +66,12 @@
then if teamsInGame client > 0 then "(playing)" else "(spectating)"
else ""
+-}
-handleCmd_loggedin clID clients rooms cmd =
- if roomID client == 0 then
- handleCmd_lobby clID clients rooms cmd
- else
- handleCmd_inRoom clID clients rooms cmd
- where
- client = clients IntMap.! clID
+
+handleCmd_loggedin cmd = do
+ (ci, rnc) <- ask
+ if clientRoom rnc ci == lobbyId then
+ handleCmd_lobby cmd
+ else
+ handleCmd_inRoom cmd
--- a/gameServer/HWProtoInRoomState.hs Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/HWProtoInRoomState.hs Sat Nov 13 09:36:26 2010 -0500
@@ -1,182 +1,240 @@
+{-# LANGUAGE OverloadedStrings #-}
module HWProtoInRoomState where
import qualified Data.Foldable as Foldable
-import qualified Data.IntMap as IntMap
import qualified Data.Map as Map
import Data.Sequence(Seq, (|>), (><), fromList, empty)
import Data.List
-import Maybe
+import Data.Maybe
+import qualified Data.ByteString.Char8 as B
+import Control.Monad
+import Control.Monad.Reader
--------------------------------------
import CoreTypes
import Actions
import Utils
-
+import HandlerUtils
+import RoomsAndClients
handleCmd_inRoom :: CmdHandler
-handleCmd_inRoom clID clients _ ["CHAT", msg] =
- [AnswerOthersInRoom ["CHAT", clientNick, msg]]
- where
- clientNick = nick $ clients IntMap.! clID
+handleCmd_inRoom ["CHAT", msg] = do
+ n <- clientNick
+ s <- roomOthersChans
+ return [AnswerClients s ["CHAT", n, msg]]
-handleCmd_inRoom clID clients rooms ["PART"] =
- [RoomRemoveThisClient "part"]
- where
- client = clients IntMap.! clID
+handleCmd_inRoom ["PART"] = return [MoveToLobby "part"]
+handleCmd_inRoom ["PART", msg] = return [MoveToLobby $ "part: " `B.append` msg]
-handleCmd_inRoom clID clients rooms ("CFG" : paramName : paramStrs)
- | null paramStrs = [ProtocolError "Empty config entry"]
- | isMaster client =
- [ModifyRoom (\r -> r{params = Map.insert paramName paramStrs (params r)}),
- AnswerOthersInRoom ("CFG" : paramName : paramStrs)]
- | otherwise = [ProtocolError "Not room master"]
- where
- client = clients IntMap.! clID
+handleCmd_inRoom ("CFG" : paramName : paramStrs)
+ | null paramStrs = return [ProtocolError "Empty config entry"]
+ | otherwise = do
+ chans <- roomOthersChans
+ cl <- thisClient
+ if isMaster cl then
+ return [
+ ModifyRoom (\r -> r{params = Map.insert paramName paramStrs (params r)}),
+ AnswerClients chans ("CFG" : paramName : paramStrs)]
+ else
+ return [ProtocolError "Not room master"]
-handleCmd_inRoom clID clients rooms ("ADD_TEAM" : name : color : grave : fort : voicepack : flag : difStr : hhsInfo)
- | length hhsInfo == 15 && clientProto client < 30 = handleCmd_inRoom clID clients rooms ("ADD_TEAM" : name : color : grave : fort : voicepack : " " : flag : difStr : hhsInfo)
- | length hhsInfo /= 16 = [ProtocolError "Corrupted hedgehogs info"]
- | length (teams room) == 6 = [Warning "too many teams"]
- | canAddNumber <= 0 = [Warning "too many hedgehogs"]
- | isJust findTeam = [Warning "There's already a team with same name in the list"]
- | gameinprogress room = [Warning "round in progress"]
- | isRestrictedTeams room = [Warning "restricted"]
- | otherwise =
- [ModifyRoom (\r -> r{teams = teams r ++ [newTeam]}),
- ModifyClient (\c -> c{teamsInGame = teamsInGame c + 1, clientClan = color}),
- AnswerThisClient ["TEAM_ACCEPTED", name],
- AnswerOthersInRoom $ teamToNet (clientProto client) newTeam,
- AnswerOthersInRoom ["TEAM_COLOR", name, color]
- ]
+handleCmd_inRoom ("ADD_TEAM" : name : color : grave : fort : voicepack : flag : difStr : hhsInfo)
+ | length hhsInfo /= 16 = return [ProtocolError "Corrupted hedgehogs info"]
+ | otherwise = do
+ (ci, rnc) <- ask
+ r <- thisRoom
+ clNick <- clientNick
+ clChan <- thisClientChans
+ othersChans <- roomOthersChans
+ return $
+ if not . null . drop 5 $ teams r then
+ [Warning "too many teams"]
+ else if canAddNumber r <= 0 then
+ [Warning "too many hedgehogs"]
+ else if isJust $ findTeam r then
+ [Warning "There's already a team with same name in the list"]
+ else if gameinprogress r then
+ [Warning "round in progress"]
+ else if isRestrictedTeams r then
+ [Warning "restricted"]
+ else
+ [ModifyRoom (\r -> r{teams = teams r ++ [newTeam ci clNick r]}),
+ ModifyClient (\c -> c{teamsInGame = teamsInGame c + 1, clientClan = color}),
+ AnswerClients clChan ["TEAM_ACCEPTED", name],
+ AnswerClients othersChans $ teamToNet $ newTeam ci clNick r,
+ AnswerClients othersChans ["TEAM_COLOR", name, color]
+ ]
+ where
+ canAddNumber r = 48 - (sum . map hhnum $ teams r)
+ findTeam = find (\t -> name == teamname t) . teams
+ newTeam ci clNick r = (TeamInfo ci clNick name color grave fort voicepack flag difficulty (newTeamHHNum r) (hhsList hhsInfo))
+ difficulty = case B.readInt difStr of
+ Just (i, t) | B.null t -> fromIntegral i
+ otherwise -> 0
+ hhsList [] = []
+ hhsList [_] = error "Hedgehogs list with odd elements number"
+ hhsList (n:h:hhs) = HedgehogInfo n h : hhsList hhs
+ newTeamHHNum r = min 4 (canAddNumber r)
+
+handleCmd_inRoom ["REMOVE_TEAM", name] = do
+ (ci, rnc) <- ask
+ r <- thisRoom
+ clNick <- clientNick
+
+ let maybeTeam = findTeam r
+ let team = fromJust maybeTeam
+
+ return $
+ if isNothing $ findTeam r then
+ [Warning "REMOVE_TEAM: no such team"]
+ else if clNick /= teamowner team then
+ [ProtocolError "Not team owner!"]
+ else
+ [RemoveTeam name,
+ ModifyClient
+ (\c -> c{
+ teamsInGame = teamsInGame c - 1,
+ clientClan = if teamsInGame c == 1 then undefined else anotherTeamClan ci r
+ })
+ ]
where
- client = clients IntMap.! clID
- room = rooms IntMap.! (roomID client)
- canAddNumber = 48 - (sum . map hhnum $ teams room)
- findTeam = find (\t -> name == teamname t) $ teams room
- newTeam = (TeamInfo clID (nick client) name color grave fort voicepack flag difficulty newTeamHHNum (hhsList hhsInfo))
- difficulty = fromMaybe 0 (maybeRead difStr :: Maybe Int)
- hhsList [] = []
- hhsList (n:h:hhs) = HedgehogInfo n h : hhsList hhs
- newTeamHHNum = min 4 canAddNumber
-
-handleCmd_inRoom clID clients rooms ["REMOVE_TEAM", teamName]
- | noSuchTeam = [Warning "REMOVE_TEAM: no such team"]
- | nick client /= teamowner team = [ProtocolError "Not team owner!"]
- | otherwise =
- [RemoveTeam teamName,
- ModifyClient (\c -> c{teamsInGame = teamsInGame c - 1, clientClan = if teamsInGame client == 1 then undefined else anotherTeamClan})
- ]
- where
- client = clients IntMap.! clID
- room = rooms IntMap.! (roomID client)
- noSuchTeam = isNothing findTeam
- team = fromJust findTeam
- findTeam = find (\t -> teamName == teamname t) $ teams room
- anotherTeamClan = teamcolor $ fromJust $ find (\t -> teamownerId t == clID) $ teams room
+ anotherTeamClan ci = teamcolor . fromJust . find (\t -> teamownerId t == ci) . teams
+ findTeam = find (\t -> name == teamname t) . teams
-handleCmd_inRoom clID clients rooms ["HH_NUM", teamName, numberStr]
- | not $ isMaster client = [ProtocolError "Not room master"]
- | hhNumber < 1 || hhNumber > 8 || noSuchTeam || hhNumber > (canAddNumber + (hhnum team)) = []
- | otherwise =
- [ModifyRoom $ modifyTeam team{hhnum = hhNumber},
- AnswerOthersInRoom ["HH_NUM", teamName, show hhNumber]]
+handleCmd_inRoom ["HH_NUM", teamName, numberStr] = do
+ cl <- thisClient
+ others <- roomOthersChans
+ r <- thisRoom
+
+ let maybeTeam = findTeam r
+ let team = fromJust maybeTeam
+
+ return $
+ if not $ isMaster cl then
+ [ProtocolError "Not room master"]
+ else if hhNumber < 1 || hhNumber > 8 || isNothing maybeTeam || hhNumber > (canAddNumber r) + (hhnum team) then
+ []
+ else
+ [ModifyRoom $ modifyTeam team{hhnum = hhNumber},
+ AnswerClients others ["HH_NUM", teamName, B.pack $ show hhNumber]]
where
- client = clients IntMap.! clID
- room = rooms IntMap.! (roomID client)
- hhNumber = fromMaybe 0 (maybeRead numberStr :: Maybe Int)
- noSuchTeam = isNothing findTeam
- team = fromJust findTeam
- findTeam = find (\t -> teamName == teamname t) $ teams room
- canAddNumber = 48 - (sum . map hhnum $ teams room)
+ hhNumber = case B.readInt numberStr of
+ Just (i, t) | B.null t -> fromIntegral i
+ otherwise -> 0
+ findTeam = find (\t -> teamName == teamname t) . teams
+ canAddNumber = (-) 48 . sum . map hhnum . teams
+
-handleCmd_inRoom clID clients rooms ["TEAM_COLOR", teamName, newColor]
- | not $ isMaster client = [ProtocolError "Not room master"]
- | noSuchTeam = []
- | otherwise = [ModifyRoom $ modifyTeam team{teamcolor = newColor},
- AnswerOthersInRoom ["TEAM_COLOR", teamName, newColor],
+handleCmd_inRoom ["TEAM_COLOR", teamName, newColor] = do
+ cl <- thisClient
+ others <- roomOthersChans
+ r <- thisRoom
+
+ let maybeTeam = findTeam r
+ let team = fromJust maybeTeam
+
+ return $
+ if not $ isMaster cl then
+ [ProtocolError "Not room master"]
+ else if isNothing maybeTeam then
+ []
+ else
+ [ModifyRoom $ modifyTeam team{teamcolor = newColor},
+ AnswerClients others ["TEAM_COLOR", teamName, newColor],
ModifyClient2 (teamownerId team) (\c -> c{clientClan = newColor})]
where
- noSuchTeam = isNothing findTeam
- team = fromJust findTeam
- findTeam = find (\t -> teamName == teamname t) $ teams room
- client = clients IntMap.! clID
- room = rooms IntMap.! (roomID client)
+ findTeam = find (\t -> teamName == teamname t) . teams
-handleCmd_inRoom clID clients rooms ["TOGGLE_READY"] =
- [ModifyClient (\c -> c{isReady = not $ isReady client}),
- ModifyRoom (\r -> r{readyPlayers = readyPlayers r + (if isReady client then -1 else 1)}),
- AnswerThisRoom [if isReady client then "NOT_READY" else "READY", nick client]]
- where
- client = clients IntMap.! clID
+handleCmd_inRoom ["TOGGLE_READY"] = do
+ cl <- thisClient
+ chans <- roomClientsChans
+ return [
+ ModifyClient (\c -> c{isReady = not $ isReady cl}),
+ ModifyRoom (\r -> r{readyPlayers = readyPlayers r + (if isReady cl then -1 else 1)}),
+ AnswerClients chans [if isReady cl then "NOT_READY" else "READY", nick cl]
+ ]
+handleCmd_inRoom ["START_GAME"] = do
+ cl <- thisClient
+ r <- thisRoom
+ chans <- roomClientsChans
-handleCmd_inRoom clID clients rooms ["START_GAME"] =
- if isMaster client && (playersIn room == readyPlayers room) && (not . gameinprogress) room then
- if enoughClans then
- [ModifyRoom
+ if isMaster cl && (playersIn r == readyPlayers r) && (not $ gameinprogress r) then
+ if enoughClans r then
+ return [
+ ModifyRoom
(\r -> r{
gameinprogress = True,
roundMsgs = empty,
leftTeams = [],
teamsAtStart = teams r}
),
- AnswerThisRoom ["RUN_GAME"]]
+ AnswerClients chans ["RUN_GAME"]
+ ]
+ else
+ return [Warning "Less than two clans!"]
else
- [Warning "Less than two clans!"]
- else
- []
+ return []
where
- client = clients IntMap.! clID
- room = rooms IntMap.! (roomID client)
- enoughClans = not $ null $ drop 1 $ group $ map teamcolor $ teams room
+ enoughClans = not . null . drop 1 . group . map teamcolor . teams
-handleCmd_inRoom clID clients rooms ["EM", msg] =
- if (teamsInGame client > 0) && isLegal then
- (AnswerOthersInRoom ["EM", msg]) : [ModifyRoom (\r -> r{roundMsgs = roundMsgs r |> msg}) | not isKeepAlive]
- else
- []
+handleCmd_inRoom ["EM", msg] = do
+ cl <- thisClient
+ r <- thisRoom
+ chans <- roomOthersChans
+
+ if (teamsInGame cl > 0) && isLegal then
+ return $ (AnswerClients chans ["EM", msg]) : [ModifyRoom (\r -> r{roundMsgs = roundMsgs r |> msg}) | not isKeepAlive]
+ else
+ return []
where
- client = clients IntMap.! clID
(isLegal, isKeepAlive) = checkNetCmd msg
-handleCmd_inRoom clID clients rooms ["ROUNDFINISHED"] =
- if isMaster client then
- [ModifyRoom
+
+handleCmd_inRoom ["ROUNDFINISHED"] = do
+ cl <- thisClient
+ r <- thisRoom
+ chans <- roomClientsChans
+
+ if isMaster cl && (gameinprogress r) then
+ return $ (ModifyRoom
(\r -> r{
gameinprogress = False,
readyPlayers = 0,
roundMsgs = empty,
leftTeams = [],
teamsAtStart = []}
- ),
- UnreadyRoomClients
- ] ++ answerRemovedTeams
- else
- []
+ ))
+ : UnreadyRoomClients
+ : answerRemovedTeams chans r
+ else
+ return []
where
- client = clients IntMap.! clID
- room = rooms IntMap.! (roomID client)
- answerRemovedTeams = map (\t -> AnswerThisRoom ["REMOVE_TEAM", t]) $ leftTeams room
+ answerRemovedTeams chans = map (\t -> AnswerClients chans ["REMOVE_TEAM", t]) . leftTeams
+
+handleCmd_inRoom ["TOGGLE_RESTRICT_JOINS"] = do
+ cl <- thisClient
+ return $
+ if not $ isMaster cl then
+ [ProtocolError "Not room master"]
+ else
+ [ModifyRoom (\r -> r{isRestrictedJoins = not $ isRestrictedJoins r})]
-handleCmd_inRoom clID clients _ ["TOGGLE_RESTRICT_JOINS"]
- | isMaster client = [ModifyRoom (\r -> r{isRestrictedJoins = not $ isRestrictedJoins r})]
- | otherwise = [ProtocolError "Not room master"]
- where
- client = clients IntMap.! clID
-
+handleCmd_inRoom ["TOGGLE_RESTRICT_TEAMS"] = do
+ cl <- thisClient
+ return $
+ if not $ isMaster cl then
+ [ProtocolError "Not room master"]
+ else
+ [ModifyRoom (\r -> r{isRestrictedTeams = not $ isRestrictedTeams r})]
-handleCmd_inRoom clID clients _ ["TOGGLE_RESTRICT_TEAMS"]
- | isMaster client = [ModifyRoom (\r -> r{isRestrictedTeams = not $ isRestrictedTeams r})]
- | otherwise = [ProtocolError "Not room master"]
- where
- client = clients IntMap.! clID
-
+{-
handleCmd_inRoom clID clients rooms ["KICK", kickNick] =
[KickRoomClient kickID | isMaster client && not noSuchClient && (kickID /= clID) && (roomID client == roomID kickClient)]
where
@@ -192,5 +250,5 @@
where
client = clients IntMap.! clID
engineMsg = toEngineMsg $ 'b' : ((nick client) ++ "(team): " ++ msg ++ "\x20\x20")
-
-handleCmd_inRoom clID _ _ _ = [ProtocolError "Incorrect command (state: in room)"]
+-}
+handleCmd_inRoom _ = return [ProtocolError "Incorrect command (state: in room)"]
--- a/gameServer/HWProtoLobbyState.hs Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/HWProtoLobbyState.hs Sat Nov 13 09:36:26 2010 -0500
@@ -1,73 +1,102 @@
+{-# LANGUAGE OverloadedStrings #-}
module HWProtoLobbyState where
import qualified Data.Map as Map
-import qualified Data.IntMap as IntMap
import qualified Data.IntSet as IntSet
import qualified Data.Foldable as Foldable
-import Maybe
+import Data.Maybe
import Data.List
import Data.Word
+import Control.Monad.Reader
+import qualified Data.ByteString.Char8 as B
--------------------------------------
import CoreTypes
import Actions
import Utils
+import HandlerUtils
+import RoomsAndClients
-answerAllTeams protocol teams = concatMap toAnswer teams
+{-answerAllTeams protocol teams = concatMap toAnswer teams
where
toAnswer team =
[AnswerThisClient $ teamToNet protocol team,
AnswerThisClient ["TEAM_COLOR", teamname team, teamcolor team],
AnswerThisClient ["HH_NUM", teamname team, show $ hhnum team]]
-
+-}
handleCmd_lobby :: CmdHandler
-handleCmd_lobby clID clients rooms ["LIST"] =
- [AnswerThisClient ("ROOMS" : roomsInfoList)]
+
+handleCmd_lobby ["LIST"] = do
+ (ci, irnc) <- ask
+ let cl = irnc `client` ci
+ rooms <- allRoomInfos
+ let roomsInfoList = concatMap (roomInfo irnc) . filter (\r -> (roomProto r == clientProto cl) && not (isRestrictedJoins r))
+ return [AnswerClients [sendChan cl] ("ROOMS" : roomsInfoList rooms)]
where
- roomsInfoList = concatMap roomInfo sameProtoRooms
- sameProtoRooms = filter (\r -> (roomProto r == protocol) && not (isRestrictedJoins r)) roomsList
- roomsList = IntMap.elems rooms
- protocol = clientProto client
- client = clients IntMap.! clID
- roomInfo room
- | clientProto client < 28 = [
+ roomInfo irnc room = [
+ showB $ gameinprogress room,
name room,
- show (playersIn room) ++ "(" ++ show (length $ teams room) ++ ")",
- show $ gameinprogress room
- ]
- | otherwise = [
- show $ gameinprogress room,
- name room,
- show $ playersIn room,
- show $ length $ teams room,
- nick $ clients IntMap.! (masterID room),
+ showB $ playersIn room,
+ showB $ length $ teams room,
+ nick $ irnc `client` masterID room,
head (Map.findWithDefault ["+gen+"] "MAP" (params room)),
head (Map.findWithDefault ["Default"] "SCHEME" (params room)),
head (Map.findWithDefault ["Default"] "AMMO" (params room))
]
-handleCmd_lobby clID clients _ ["CHAT", msg] =
- [AnswerOthersInRoom ["CHAT", clientNick, msg]]
- where
- clientNick = nick $ clients IntMap.! clID
+
+handleCmd_lobby ["CHAT", msg] = do
+ n <- clientNick
+ s <- roomOthersChans
+ return [AnswerClients s ["CHAT", n, msg]]
+
+handleCmd_lobby ["CREATE_ROOM", newRoom, roomPassword]
+ | illegalName newRoom = return [Warning "Illegal room name"]
+ | otherwise = do
+ rs <- allRoomInfos
+ cl <- thisClient
+ return $ if isJust $ find (\room -> newRoom == name room) rs then
+ [Warning "Room exists"]
+ else
+ [
+ AddRoom newRoom roomPassword,
+ AnswerClients [sendChan cl] ["NOT_READY", nick cl]
+ ]
+
+
+handleCmd_lobby ["CREATE_ROOM", newRoom] =
+ handleCmd_lobby ["CREATE_ROOM", newRoom, ""]
-handleCmd_lobby clID clients rooms ["CREATE_ROOM", newRoom, roomPassword]
- | haveSameRoom = [Warning "Room exists"]
- | illegalName newRoom = [Warning "Illegal room name"]
- | otherwise =
- [RoomRemoveThisClient "", -- leave lobby
- AddRoom newRoom roomPassword,
- AnswerThisClient ["NOT_READY", clientNick]
- ]
+handleCmd_lobby ["JOIN_ROOM", roomName, roomPassword] = do
+ (ci, irnc) <- ask
+ let ris = allRooms irnc
+ cl <- thisClient
+ let maybeRI = find (\ri -> roomName == name (irnc `room` ri)) ris
+ let jRI = fromJust maybeRI
+ let jRoom = irnc `room` jRI
+ let jRoomClients = map (client irnc) $! roomClients irnc jRI -- no lazyness here!
+ return $
+ if isNothing maybeRI then
+ [Warning "No such rooms"]
+ else if isRestrictedJoins jRoom then
+ [Warning "Joining restricted"]
+ else if roomPassword /= password jRoom then
+ [Warning "Wrong password"]
+ else
+ [
+ MoveToRoom jRI,
+ AnswerClients (map sendChan $ cl : jRoomClients) ["NOT_READY", nick cl]
+ ]
+ ++ [ AnswerClients [sendChan cl] $ "JOINED" : map nick jRoomClients | playersIn jRoom /= 0]
+ ++ (map (readynessMessage cl) jRoomClients)
+
where
- clientNick = nick $ clients IntMap.! clID
- haveSameRoom = isJust $ find (\room -> newRoom == name room) $ IntMap.elems rooms
+ readynessMessage cl c = AnswerClients [sendChan cl] [if isReady c then "READY" else "NOT_READY", nick c]
-handleCmd_lobby clID clients rooms ["CREATE_ROOM", newRoom] =
- handleCmd_lobby clID clients rooms ["CREATE_ROOM", newRoom, ""]
+{-
handleCmd_lobby clID clients rooms ["JOIN_ROOM", roomName, roomPassword]
| noSuchRoom = [Warning "No such room"]
@@ -83,12 +112,6 @@
++ answerTeams
++ watchRound
where
- noSuchRoom = isNothing mbRoom
- mbRoom = find (\r -> roomName == name r && roomProto r == clientProto client) $ IntMap.elems rooms
- jRoom = fromJust mbRoom
- rID = roomUID jRoom
- client = clients IntMap.! clID
- roomClientsIDs = IntSet.elems $ playersIDs jRoom
answerNicks =
[AnswerThisClient $ "JOINED" :
map (\clID -> nick $ clients IntMap.! clID) roomClientsIDs | playersIn jRoom /= 0]
@@ -100,7 +123,7 @@
roomClientsIDs
toAnswer (paramName, paramStrs) = AnswerThisClient $ "CFG" : paramName : paramStrs
-
+
answerFullConfig = map toAnswer (leftConfigPart ++ rightConfigPart)
(leftConfigPart, rightConfigPart) = partition (\(p, _) -> p /= "MAP") (Map.toList $ params jRoom)
@@ -114,12 +137,12 @@
answerAllTeams (clientProto client) (teamsAtStart jRoom)
else
answerAllTeams (clientProto client) (teams jRoom)
-
+-}
-handleCmd_lobby clID clients rooms ["JOIN_ROOM", roomName] =
- handleCmd_lobby clID clients rooms ["JOIN_ROOM", roomName, ""]
-
+handleCmd_lobby ["JOIN_ROOM", roomName] =
+ handleCmd_lobby ["JOIN_ROOM", roomName, ""]
+{-
handleCmd_lobby clID clients rooms ["FOLLOW", asknick] =
if noSuchClient || roomID followClient == 0 then
[]
@@ -180,6 +203,7 @@
[ClearAccountsCache | isAdministrator client]
where
client = clients IntMap.! clID
+-}
-handleCmd_lobby clID _ _ _ = [ProtocolError "Incorrect command (state: in lobby)"]
+handleCmd_lobby _ = return [ProtocolError "Incorrect command (state: in lobby)"]
--- a/gameServer/HWProtoNEState.hs Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/HWProtoNEState.hs Sat Nov 13 09:36:26 2010 -0500
@@ -1,54 +1,66 @@
+{-# LANGUAGE OverloadedStrings #-}
module HWProtoNEState where
import qualified Data.IntMap as IntMap
-import Maybe
+import Data.Maybe
import Data.List
import Data.Word
+import Control.Monad.Reader
+import qualified Data.ByteString.Char8 as B
--------------------------------------
import CoreTypes
import Actions
import Utils
+import RoomsAndClients
handleCmd_NotEntered :: CmdHandler
-handleCmd_NotEntered clID clients _ ["NICK", newNick]
- | not . null $ nick client = [ProtocolError "Nickname already chosen"]
- | haveSameNick = [AnswerThisClient ["WARNING", "Nickname already in use"], ByeClient ""]
- | illegalName newNick = [ByeClient "Illegal nickname"]
- | otherwise =
- ModifyClient (\c -> c{nick = newNick}) :
- AnswerThisClient ["NICK", newNick] :
- [CheckRegistered | clientProto client /= 0]
+handleCmd_NotEntered ["NICK", newNick] = do
+ (ci, irnc) <- ask
+ let cl = irnc `client` ci
+ if not . B.null $ nick cl then return [ProtocolError "Nickname already chosen"]
+ else
+ if haveSameNick irnc (nick cl) then return [AnswerClients [sendChan cl] ["WARNING", "Nickname already in use"], ByeClient ""]
+ else
+ if illegalName newNick then return [ByeClient "Illegal nickname"]
+ else
+ return $
+ ModifyClient (\c -> c{nick = newNick}) :
+ AnswerClients [sendChan cl] ["NICK", newNick] :
+ [CheckRegistered | clientProto cl /= 0]
where
- client = clients IntMap.! clID
- haveSameNick = isJust $ find (\cl -> newNick == nick cl) $ IntMap.elems clients
+ haveSameNick irnc clNick = isJust $ find (\cl -> newNick == clNick) $ map (client irnc) $ allClients irnc
+
+handleCmd_NotEntered ["PROTO", protoNum] = do
+ (ci, irnc) <- ask
+ let cl = irnc `client` ci
+ if clientProto cl > 0 then return [ProtocolError "Protocol already known"]
+ else
+ if parsedProto == 0 then return [ProtocolError "Bad number"]
+ else
+ return $
+ ModifyClient (\c -> c{clientProto = parsedProto}) :
+ AnswerClients [sendChan cl] ["PROTO", B.pack $ show parsedProto] :
+ [CheckRegistered | not . B.null $ nick cl]
+ where
+ parsedProto = case B.readInt protoNum of
+ Just (i, t) | B.null t -> fromIntegral i
+ otherwise -> 0
-handleCmd_NotEntered clID clients _ ["PROTO", protoNum]
- | clientProto client > 0 = [ProtocolError "Protocol already known"]
- | parsedProto == 0 = [ProtocolError "Bad number"]
- | otherwise =
- ModifyClient (\c -> c{clientProto = parsedProto}) :
- AnswerThisClient ["PROTO", show parsedProto] :
- [CheckRegistered | (not . null) (nick client)]
- where
- client = clients IntMap.! clID
- parsedProto = fromMaybe 0 (maybeRead protoNum :: Maybe Word16)
+handleCmd_NotEntered ["PASSWORD", passwd] = do
+ (ci, irnc) <- ask
+ let cl = irnc `client` ci
+ if passwd == webPassword cl then
+ return $ JoinLobby : [AnswerClients [sendChan cl] ["ADMIN_ACCESS"] | isAdministrator cl]
+ else
+ return [ByeClient "Authentication failed"]
-handleCmd_NotEntered clID clients _ ["PASSWORD", passwd] =
- if passwd == webPassword client then
- [ModifyClient (\cl -> cl{logonPassed = True}),
- MoveToLobby] ++ adminNotice
- else
- [ByeClient "Authentication failed"]
- where
- client = clients IntMap.! clID
- adminNotice = [AnswerThisClient ["ADMIN_ACCESS"] | isAdministrator client]
-
+{-
handleCmd_NotEntered clID clients _ ["DUMP"] =
if isAdministrator (clients IntMap.! clID) then [Dump] else []
-
+-}
-handleCmd_NotEntered clID _ _ _ = [ProtocolError "Incorrect command (state: not entered)"]
+handleCmd_NotEntered _ = return [ProtocolError "Incorrect command (state: not entered)"]
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer/HandlerUtils.hs Sat Nov 13 09:36:26 2010 -0500
@@ -0,0 +1,45 @@
+module HandlerUtils where
+
+import Control.Monad.Reader
+import qualified Data.ByteString.Char8 as B
+
+import RoomsAndClients
+import CoreTypes
+import Actions
+
+thisClient :: Reader (ClientIndex, IRnC) ClientInfo
+thisClient = do
+ (ci, rnc) <- ask
+ return $ rnc `client` ci
+
+thisRoom :: Reader (ClientIndex, IRnC) RoomInfo
+thisRoom = do
+ (ci, rnc) <- ask
+ let ri = clientRoom rnc ci
+ return $ rnc `room` ri
+
+clientNick :: Reader (ClientIndex, IRnC) B.ByteString
+clientNick = liftM nick thisClient
+
+roomOthersChans :: Reader (ClientIndex, IRnC) [ClientChan]
+roomOthersChans = do
+ (ci, rnc) <- ask
+ let ri = clientRoom rnc ci
+ return $ map (sendChan . client rnc) $ filter (/= ci) (roomClients rnc ri)
+
+roomClientsChans :: Reader (ClientIndex, IRnC) [ClientChan]
+roomClientsChans = do
+ (ci, rnc) <- ask
+ let ri = clientRoom rnc ci
+ return $ map (sendChan . client rnc) (roomClients rnc ri)
+
+thisClientChans :: Reader (ClientIndex, IRnC) [ClientChan]
+thisClientChans = do
+ (ci, rnc) <- ask
+ return $ [sendChan (rnc `client` ci)]
+
+answerClient :: [B.ByteString] -> Reader (ClientIndex, IRnC) [Action]
+answerClient msg = thisClientChans >>= return . (: []) . flip AnswerClients msg
+
+allRoomInfos :: Reader (a, IRnC) [RoomInfo]
+allRoomInfos = liftM ((\irnc -> map (room irnc) $ allRooms irnc) . snd) ask
--- a/gameServer/NetRoutines.hs Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/NetRoutines.hs Sat Nov 13 09:36:26 2010 -0500
@@ -1,46 +1,41 @@
-{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE ScopedTypeVariables, OverloadedStrings #-}
module NetRoutines where
-import Network
import Network.Socket
import System.IO
-import Control.Concurrent
import Control.Concurrent.Chan
-import Control.Concurrent.STM
import qualified Control.Exception as Exception
import Data.Time
+import Control.Monad
-----------------------------
import CoreTypes
-import ClientIO
import Utils
+import RoomsAndClients
-acceptLoop :: Socket -> Chan CoreMessage -> Int -> IO ()
-acceptLoop servSock coreChan clientCounter = do
+acceptLoop :: Socket -> Chan CoreMessage -> IO ()
+acceptLoop servSock chan = forever $ do
Exception.handle
(\(_ :: Exception.IOException) -> putStrLn "exception on connect") $
do
- (socket, sockAddr) <- Network.Socket.accept servSock
+ (sock, sockAddr) <- Network.Socket.accept servSock
- cHandle <- socketToHandle socket ReadWriteMode
- hSetBuffering cHandle LineBuffering
clientHost <- sockAddr2String sockAddr
currentTime <- getCurrentTime
-
- sendChan <- newChan
+
+ sendChan' <- newChan
let newClient =
(ClientInfo
- nextID
- sendChan
- cHandle
+ sendChan'
+ sock
clientHost
currentTime
""
""
False
0
- 0
+ lobbyId
0
False
False
@@ -49,12 +44,5 @@
undefined
)
- writeChan coreChan $ Accept newClient
-
- forkIO $ clientRecvLoop cHandle coreChan nextID
- forkIO $ clientSendLoop cHandle coreChan sendChan nextID
+ writeChan chan $ Accept newClient
return ()
-
- acceptLoop servSock coreChan nextID
- where
- nextID = clientCounter + 1
--- a/gameServer/OfficialServer/DBInteraction.hs Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/OfficialServer/DBInteraction.hs Sat Nov 13 09:36:26 2010 -0500
@@ -1,4 +1,4 @@
-{-# LANGUAGE CPP, ScopedTypeVariables #-}
+{-# LANGUAGE CPP, ScopedTypeVariables, OverloadedStrings #-}
module OfficialServer.DBInteraction
(
startDBConnection
@@ -11,8 +11,7 @@
import qualified Control.Exception as Exception
import Control.Monad
import qualified Data.Map as Map
-import Monad
-import Maybe
+import Data.Maybe
import System.Log.Logger
import Data.Time
------------------------
@@ -21,7 +20,7 @@
localAddressList = ["127.0.0.1", "0:0:0:0:0:0:0:1", "0:0:0:0:0:ffff:7f00:1"]
-fakeDbConnection serverInfo = do
+fakeDbConnection serverInfo = forever $ do
q <- readChan $ dbQueries serverInfo
case q of
CheckAccount clUid _ clHost -> do
@@ -30,8 +29,6 @@
ClearCache -> return ()
SendStats {} -> return ()
- fakeDbConnection serverInfo
-
#if defined(OFFICIAL_SERVER)
pipeDbConnectionLoop queries coreChan hIn hOut accountsCache =
--- a/gameServer/OfficialServer/extdbinterface.hs Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/OfficialServer/extdbinterface.hs Sat Nov 13 09:36:26 2010 -0500
@@ -1,4 +1,4 @@
-{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE ScopedTypeVariables, OverloadedStrings #-}
module Main where
@@ -26,7 +26,7 @@
case q of
CheckAccount clUid clNick _ -> do
statement <- prepare dbConn dbQueryAccount
- execute statement [SqlString $ clNick]
+ execute statement [SqlByteString $ clNick]
passAndRole <- fetchRow statement
finish statement
let response =
@@ -47,7 +47,7 @@
dbConnectionLoop mySQLConnectionInfo =
- Control.Exception.handle (\(_ :: IOException) -> return ()) $ handleSqlError $
+ Control.Exception.handle (\(e :: IOException) -> hPutStrLn stderr $ show e) $ handleSqlError $
bracket
(connectMySQL mySQLConnectionInfo)
(disconnect)
--- a/gameServer/Opts.hs Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/Opts.hs Sat Nov 13 09:36:26 2010 -0500
@@ -3,10 +3,12 @@
getOpts,
) where
-import System
+import System.Environment
import System.Console.GetOpt
import Network
import Data.Maybe ( fromMaybe )
+import qualified Data.ByteString.Char8 as B
+
import CoreTypes
import Utils
@@ -30,9 +32,9 @@
where
readDedicated = fromMaybe True (maybeRead str :: Maybe Bool)
-readDbLogin str opts = opts{dbLogin = str}
-readDbPassword str opts = opts{dbPassword = str}
-readDbHost str opts = opts{dbHost = str}
+readDbLogin str opts = opts{dbLogin = B.pack str}
+readDbPassword str opts = opts{dbPassword = B.pack str}
+readDbHost str opts = opts{dbHost = B.pack str}
getOpts :: ServerInfo -> IO ServerInfo
getOpts opts = do
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer/RoomsAndClients.hs Sat Nov 13 09:36:26 2010 -0500
@@ -0,0 +1,196 @@
+module RoomsAndClients(
+ RoomIndex(),
+ ClientIndex(),
+ MRoomsAndClients(),
+ IRoomsAndClients(),
+ newRoomsAndClients,
+ addRoom,
+ addClient,
+ removeRoom,
+ removeClient,
+ modifyRoom,
+ modifyClient,
+ lobbyId,
+ moveClientToLobby,
+ moveClientToRoom,
+ clientRoomM,
+ clientExists,
+ client,
+ room,
+ client'sM,
+ room'sM,
+ allClientsM,
+ clientsM,
+ roomClientsM,
+ roomClientsIndicesM,
+ withRoomsAndClients,
+ allRooms,
+ allClients,
+ clientRoom,
+ showRooms,
+ roomClients
+ ) where
+
+
+import Store
+import Control.Monad
+
+
+data Room r = Room {
+ roomClients' :: [ClientIndex],
+ room' :: r
+ }
+
+
+data Client c = Client {
+ clientRoom' :: RoomIndex,
+ client' :: c
+ }
+
+
+newtype RoomIndex = RoomIndex ElemIndex
+ deriving (Eq)
+newtype ClientIndex = ClientIndex ElemIndex
+ deriving (Eq, Show, Read, Ord)
+
+instance Show RoomIndex where
+ show (RoomIndex i) = 'r' : show i
+
+unRoomIndex :: RoomIndex -> ElemIndex
+unRoomIndex (RoomIndex r) = r
+
+unClientIndex :: ClientIndex -> ElemIndex
+unClientIndex (ClientIndex c) = c
+
+
+newtype MRoomsAndClients r c = MRoomsAndClients (MStore (Room r), MStore (Client c))
+newtype IRoomsAndClients r c = IRoomsAndClients (IStore (Room r), IStore (Client c))
+
+
+lobbyId :: RoomIndex
+lobbyId = RoomIndex firstIndex
+
+
+newRoomsAndClients :: r -> IO (MRoomsAndClients r c)
+newRoomsAndClients r = do
+ rooms <- newStore
+ clients <- newStore
+ let rnc = MRoomsAndClients (rooms, clients)
+ ri <- addRoom rnc r
+ when (ri /= lobbyId) $ error "Empty struct inserts not at firstIndex index"
+ return rnc
+
+
+roomAddClient :: ClientIndex -> Room r -> Room r
+roomAddClient cl room = let cls = cl : roomClients' room; nr = room{roomClients' = cls} in cls `seq` nr `seq` nr
+
+roomRemoveClient :: ClientIndex -> Room r -> Room r
+roomRemoveClient cl room = let cls = filter (/= cl) $ roomClients' room; nr = room{roomClients' = cls} in cls `seq` nr `seq` nr
+
+
+addRoom :: MRoomsAndClients r c -> r -> IO RoomIndex
+addRoom (MRoomsAndClients (rooms, _)) room = do
+ i <- addElem rooms (Room [] room)
+ return $ RoomIndex i
+
+
+addClient :: MRoomsAndClients r c -> c -> IO ClientIndex
+addClient (MRoomsAndClients (rooms, clients)) client = do
+ i <- addElem clients (Client lobbyId client)
+ modifyElem rooms (roomAddClient (ClientIndex i)) (unRoomIndex lobbyId)
+ return $ ClientIndex i
+
+removeRoom :: MRoomsAndClients r c -> RoomIndex -> IO ()
+removeRoom rnc@(MRoomsAndClients (rooms, _)) room@(RoomIndex ri)
+ | room == lobbyId = error "Cannot delete lobby"
+ | otherwise = do
+ clIds <- liftM roomClients' $ readElem rooms ri
+ forM_ clIds (moveClientToLobby rnc)
+ removeElem rooms ri
+
+
+removeClient :: MRoomsAndClients r c -> ClientIndex -> IO ()
+removeClient (MRoomsAndClients (rooms, clients)) cl@(ClientIndex ci) = do
+ RoomIndex ri <- liftM clientRoom' $ readElem clients ci
+ modifyElem rooms (roomRemoveClient cl) ri
+ removeElem clients ci
+
+
+modifyRoom :: MRoomsAndClients r c -> (r -> r) -> RoomIndex -> IO ()
+modifyRoom (MRoomsAndClients (rooms, _)) f (RoomIndex ri) = modifyElem rooms (\r -> r{room' = f $ room' r}) ri
+
+modifyClient :: MRoomsAndClients r c -> (c -> c) -> ClientIndex -> IO ()
+modifyClient (MRoomsAndClients (_, clients)) f (ClientIndex ci) = modifyElem clients (\c -> c{client' = f $ client' c}) ci
+
+moveClientInRooms :: MRoomsAndClients r c -> RoomIndex -> RoomIndex -> ClientIndex -> IO ()
+moveClientInRooms (MRoomsAndClients (rooms, clients)) (RoomIndex riFrom) rt@(RoomIndex riTo) cl@(ClientIndex ci) = do
+ modifyElem rooms (roomRemoveClient cl) riFrom
+ modifyElem rooms (roomAddClient cl) riTo
+ modifyElem clients (\c -> c{clientRoom' = rt}) ci
+
+
+moveClientToLobby :: MRoomsAndClients r c -> ClientIndex -> IO ()
+moveClientToLobby rnc ci = do
+ room <- clientRoomM rnc ci
+ moveClientInRooms rnc room lobbyId ci
+
+
+moveClientToRoom :: MRoomsAndClients r c -> RoomIndex -> ClientIndex -> IO ()
+moveClientToRoom rnc ri ci = moveClientInRooms rnc lobbyId ri ci
+
+
+clientExists :: MRoomsAndClients r c -> ClientIndex -> IO Bool
+clientExists (MRoomsAndClients (_, clients)) (ClientIndex ci) = elemExists clients ci
+
+clientRoomM :: MRoomsAndClients r c -> ClientIndex -> IO RoomIndex
+clientRoomM (MRoomsAndClients (_, clients)) (ClientIndex ci) = liftM clientRoom' (clients `readElem` ci)
+
+client'sM :: MRoomsAndClients r c -> (c -> a) -> ClientIndex -> IO a
+client'sM (MRoomsAndClients (_, clients)) f (ClientIndex ci) = liftM (f . client') (clients `readElem` ci)
+
+room'sM :: MRoomsAndClients r c -> (r -> a) -> RoomIndex -> IO a
+room'sM (MRoomsAndClients (rooms, _)) f (RoomIndex ri) = liftM (f . room') (rooms `readElem` ri)
+
+allClientsM :: MRoomsAndClients r c -> IO [ClientIndex]
+allClientsM (MRoomsAndClients (_, clients)) = liftM (map ClientIndex) $ indicesM clients
+
+clientsM :: MRoomsAndClients r c -> IO [c]
+clientsM (MRoomsAndClients (_, clients)) = indicesM clients >>= mapM (\ci -> liftM client' $ readElem clients ci)
+
+roomClientsIndicesM :: MRoomsAndClients r c -> RoomIndex -> IO [ClientIndex]
+roomClientsIndicesM (MRoomsAndClients (rooms, clients)) (RoomIndex ri) = liftM roomClients' (rooms `readElem` ri)
+
+roomClientsM :: MRoomsAndClients r c -> RoomIndex -> IO [c]
+roomClientsM (MRoomsAndClients (rooms, clients)) (RoomIndex ri) = liftM roomClients' (rooms `readElem` ri) >>= mapM (\(ClientIndex ci) -> liftM client' $ readElem clients ci)
+
+withRoomsAndClients :: MRoomsAndClients r c -> (IRoomsAndClients r c -> a) -> IO a
+withRoomsAndClients (MRoomsAndClients (rooms, clients)) f =
+ withIStore2 rooms clients (\r c -> f $ IRoomsAndClients (r, c))
+
+----------------------------------------
+----------- IRoomsAndClients -----------
+
+showRooms :: (Show r, Show c) => IRoomsAndClients r c -> String
+showRooms rnc@(IRoomsAndClients (rooms, clients)) = concatMap showRoom (allRooms rnc)
+ where
+ showRoom r = unlines $ ((show r) ++ ": " ++ (show $ room' $ rooms ! (unRoomIndex r))) : (map showClient (roomClients' $ rooms ! (unRoomIndex r)))
+ showClient c = " " ++ (show c) ++ ": " ++ (show $ client' $ clients ! (unClientIndex c))
+
+
+allRooms :: IRoomsAndClients r c -> [RoomIndex]
+allRooms (IRoomsAndClients (rooms, _)) = map RoomIndex $ indices rooms
+
+allClients :: IRoomsAndClients r c -> [ClientIndex]
+allClients (IRoomsAndClients (_, clients)) = map ClientIndex $ indices clients
+
+clientRoom :: IRoomsAndClients r c -> ClientIndex -> RoomIndex
+clientRoom (IRoomsAndClients (_, clients)) (ClientIndex ci) = clientRoom' (clients ! ci)
+
+client :: IRoomsAndClients r c -> ClientIndex -> c
+client (IRoomsAndClients (_, clients)) (ClientIndex ci) = client' (clients ! ci)
+
+room :: IRoomsAndClients r c -> RoomIndex -> r
+room (IRoomsAndClients (rooms, _)) (RoomIndex ri) = room' (rooms ! ri)
+
+roomClients :: IRoomsAndClients r c -> RoomIndex -> [ClientIndex]
+roomClients (IRoomsAndClients (rooms, _)) (RoomIndex ri) = roomClients' $ (rooms ! ri)
--- a/gameServer/ServerCore.hs Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/ServerCore.hs Sat Nov 13 09:36:26 2010 -0500
@@ -2,69 +2,75 @@
import Network
import Control.Concurrent
-import Control.Concurrent.STM
import Control.Concurrent.Chan
import Control.Monad
import qualified Data.IntMap as IntMap
import System.Log.Logger
+import Control.Monad.Reader
+import Control.Monad.State.Strict
+import Data.Set as Set
+import qualified Data.ByteString.Char8 as B
--------------------------------------
import CoreTypes
import NetRoutines
-import Utils
import HWProtoCore
import Actions
import OfficialServer.DBInteraction
+import ServerState
-timerLoop :: Int -> Chan CoreMessage -> IO()
+timerLoop :: Int -> Chan CoreMessage -> IO ()
timerLoop tick messagesChan = threadDelay (30 * 10^6) >> writeChan messagesChan (TimerAction tick) >> timerLoop (tick + 1) messagesChan
-firstAway (_, a, b, c) = (a, b, c)
-reactCmd :: ServerInfo -> Int -> [String] -> Clients -> Rooms -> IO (ServerInfo, Clients, Rooms)
-reactCmd serverInfo clID cmd clients rooms =
- liftM firstAway $ foldM processAction (clID, serverInfo, clients, rooms) $ handleCmd clID clients rooms cmd
+reactCmd :: [B.ByteString] -> StateT ServerState IO ()
+reactCmd cmd = do
+ (Just ci) <- gets clientIndex
+ rnc <- gets roomsClients
+ actions <- liftIO $ withRoomsAndClients rnc (\irnc -> runReader (handleCmd cmd) (ci, irnc))
+ forM_ actions processAction
-mainLoop :: ServerInfo -> Clients -> Rooms -> IO ()
-mainLoop serverInfo clients rooms = do
- r <- readChan $ coreChan serverInfo
-
- (newServerInfo, mClients, mRooms) <-
- case r of
- Accept ci ->
- liftM firstAway $ processAction
- (clientUID ci, serverInfo, clients, rooms) (AddClient ci)
+mainLoop :: StateT ServerState IO ()
+mainLoop = forever $ do
+ get >>= \s -> put $! s
+
+ si <- gets serverInfo
+ r <- liftIO $ readChan $ coreChan si
+
+ case r of
+ Accept ci -> processAction (AddClient ci)
+
+ ClientMessage (ci, cmd) -> do
+ liftIO $ debugM "Clients" $ (show ci) ++ ": " ++ (show cmd)
- ClientMessage (clID, cmd) -> do
- debugM "Clients" $ (show clID) ++ ": " ++ (show cmd)
- if clID `IntMap.member` clients then
- reactCmd serverInfo clID cmd clients rooms
- else
- do
- debugM "Clients" "Message from dead client"
- return (serverInfo, clients, rooms)
+ removed <- gets removedClients
+ when (not $ ci `Set.member` removed) $ do
+ as <- get
+ put $! as{clientIndex = Just ci}
+ reactCmd cmd
+
+ Remove ci -> do
+ liftIO $ debugM "Clients" $ "DeleteClient: " ++ show ci
+ processAction (DeleteClient ci)
- ClientAccountInfo (clID, info) ->
- if clID `IntMap.member` clients then
- liftM firstAway $ processAction
- (clID, serverInfo, clients, rooms)
- (ProcessAccountInfo info)
- else
- do
- debugM "Clients" "Got info for dead client"
- return (serverInfo, clients, rooms)
+ --else
+ --do
+ --debugM "Clients" "Message from dead client"
+ --return (serverInfo, rnc)
- TimerAction tick ->
- liftM firstAway $
- foldM processAction (0, serverInfo, clients, rooms) $
- PingAll : [StatsAction | even tick]
-
+ ClientAccountInfo (ci, info) -> do
+ rnc <- gets roomsClients
+ exists <- liftIO $ clientExists rnc ci
+ when (exists) $ do
+ as <- get
+ put $! as{clientIndex = Just ci}
+ processAction (ProcessAccountInfo info)
+ return ()
- {- let hadRooms = (not $ null rooms) && (null mrooms)
- in unless ((not $ isDedicated serverInfo) && ((null clientsIn) || hadRooms)) $
- mainLoop serverInfo acceptChan messagesChan clientsIn mrooms -}
+ TimerAction tick ->
+ mapM_ processAction $
+ PingAll : [StatsAction | even tick]
- mainLoop newServerInfo mClients mRooms
startServer :: ServerInfo -> Socket -> IO ()
startServer serverInfo serverSocket = do
@@ -74,14 +80,15 @@
acceptLoop
serverSocket
(coreChan serverInfo)
- 0
return ()
-
- forkIO $ timerLoop 0 $ coreChan serverInfo
+
+ --forkIO $ timerLoop 0 $ coreChan serverInfo
startDBConnection serverInfo
- forkIO $ mainLoop serverInfo IntMap.empty (IntMap.singleton 0 newRoom)
+ rnc <- newRoomsAndClients newRoom
- forever $ threadDelay (60 * 60 * 10^6) >> putStrLn "***"
\ No newline at end of file
+ forkIO $ evalStateT mainLoop (ServerState Nothing serverInfo Set.empty rnc)
+
+ forever $ threadDelay (60 * 60 * 10^6)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer/ServerState.hs Sat Nov 13 09:36:26 2010 -0500
@@ -0,0 +1,43 @@
+module ServerState
+ (
+ module RoomsAndClients,
+ clientRoomA,
+ ServerState(..),
+ client's,
+ allClientsS,
+ roomClientsS
+ ) where
+
+import Control.Monad.State.Strict
+import Data.Set as Set
+----------------------
+import RoomsAndClients
+import CoreTypes
+
+data ServerState = ServerState {
+ clientIndex :: !(Maybe ClientIndex),
+ serverInfo :: !ServerInfo,
+ removedClients :: !(Set.Set ClientIndex),
+ roomsClients :: !MRnC
+ }
+
+
+clientRoomA :: StateT ServerState IO RoomIndex
+clientRoomA = do
+ (Just ci) <- gets clientIndex
+ rnc <- gets roomsClients
+ liftIO $ clientRoomM rnc ci
+
+client's :: (ClientInfo -> a) -> StateT ServerState IO a
+client's f = do
+ (Just ci) <- gets clientIndex
+ rnc <- gets roomsClients
+ liftIO $ client'sM rnc f ci
+
+allClientsS :: StateT ServerState IO [ClientInfo]
+allClientsS = gets roomsClients >>= liftIO . clientsM
+
+roomClientsS :: RoomIndex -> StateT ServerState IO [ClientInfo]
+roomClientsS ri = do
+ rnc <- gets roomsClients
+ liftIO $ roomClientsM rnc ri
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer/Store.hs Sat Nov 13 09:36:26 2010 -0500
@@ -0,0 +1,145 @@
+module Store(
+ ElemIndex(),
+ MStore(),
+ IStore(),
+ newStore,
+ addElem,
+ removeElem,
+ readElem,
+ writeElem,
+ modifyElem,
+ elemExists,
+ firstIndex,
+ indicesM,
+ withIStore,
+ withIStore2,
+ (!),
+ indices
+ ) where
+
+import qualified Data.Array.IArray as IA
+import qualified Data.Array.IO as IOA
+import qualified Data.IntSet as IntSet
+import Data.IORef
+import Control.Monad
+
+
+newtype ElemIndex = ElemIndex Int
+ deriving (Eq, Show, Read, Ord)
+newtype MStore e = MStore (IORef (IntSet.IntSet, IntSet.IntSet, IOA.IOArray Int e))
+newtype IStore e = IStore (IntSet.IntSet, IA.Array Int e)
+
+
+firstIndex :: ElemIndex
+firstIndex = ElemIndex 0
+
+-- MStore code
+initialSize :: Int
+initialSize = 10
+
+
+growFunc :: Int -> Int
+growFunc a = a * 3 `div` 2
+
+
+newStore :: IO (MStore e)
+newStore = do
+ newar <- IOA.newArray_ (0, initialSize - 1)
+ new <- newIORef (IntSet.empty, IntSet.fromAscList [0..initialSize - 1], newar)
+ return (MStore new)
+
+
+growStore :: MStore e -> IO ()
+growStore (MStore ref) = do
+ (busyElems, freeElems, arr) <- readIORef ref
+ (_, m') <- IOA.getBounds arr
+ let newM' = growFunc (m' + 1) - 1
+ newArr <- IOA.newArray_ (0, newM')
+ sequence_ [IOA.readArray arr i >>= IOA.writeArray newArr i | i <- [0..m']]
+ writeIORef ref (busyElems, freeElems `IntSet.union` (IntSet.fromAscList [m'+1..newM']), newArr)
+
+
+growIfNeeded :: MStore e -> IO ()
+growIfNeeded m@(MStore ref) = do
+ (_, freeElems, _) <- readIORef ref
+ when (IntSet.null freeElems) $ growStore m
+
+
+addElem :: MStore e -> e -> IO ElemIndex
+addElem m@(MStore ref) element = do
+ growIfNeeded m
+ (busyElems, freeElems, arr) <- readIORef ref
+ let (n, freeElems') = IntSet.deleteFindMin freeElems
+ IOA.writeArray arr n element
+ writeIORef ref (IntSet.insert n busyElems, freeElems', arr)
+ return $ ElemIndex n
+
+
+removeElem :: MStore e -> ElemIndex -> IO ()
+removeElem (MStore ref) (ElemIndex n) = do
+ (busyElems, freeElems, arr) <- readIORef ref
+ IOA.writeArray arr n (error $ "Store: no element " ++ show n)
+ writeIORef ref (IntSet.delete n busyElems, IntSet.insert n freeElems, arr)
+
+
+readElem :: MStore e -> ElemIndex -> IO e
+readElem (MStore ref) (ElemIndex n) = readIORef ref >>= \(_, _, arr) -> IOA.readArray arr n
+
+
+writeElem :: MStore e -> ElemIndex -> e -> IO ()
+writeElem (MStore ref) (ElemIndex n) el = readIORef ref >>= \(_, _, arr) -> IOA.writeArray arr n el
+
+
+modifyElem :: MStore e -> (e -> e) -> ElemIndex -> IO ()
+modifyElem (MStore ref) f (ElemIndex n) = do
+ (_, _, arr) <- readIORef ref
+ IOA.readArray arr n >>= IOA.writeArray arr n . f
+
+elemExists :: MStore e -> ElemIndex -> IO Bool
+elemExists (MStore ref) (ElemIndex n) = do
+ (_, free, _) <- readIORef ref
+ return $ n `IntSet.notMember` free
+
+indicesM :: MStore e -> IO [ElemIndex]
+indicesM (MStore ref) = do
+ (busy, _, _) <- readIORef ref
+ return $ map ElemIndex $ IntSet.toList busy
+
+
+-- A way to see MStore elements in pure code via IStore
+m2i :: MStore e -> IO (IStore e)
+m2i (MStore ref) = do
+ (a, _, c') <- readIORef ref
+ c <- IOA.unsafeFreeze c'
+ return $ IStore (a, c)
+
+i2m :: (MStore e) -> IStore e -> IO ()
+i2m (MStore ref) (IStore (_, arr)) = do
+ (b, e, _) <- readIORef ref
+ a <- IOA.unsafeThaw arr
+ writeIORef ref (b, e, a)
+
+withIStore :: MStore e -> (IStore e -> a) -> IO a
+withIStore m f = do
+ i <- m2i m
+ let res = f i
+ res `seq` i2m m i
+ return res
+
+
+withIStore2 :: MStore e1 -> MStore e2 -> (IStore e1 -> IStore e2 -> a) -> IO a
+withIStore2 m1 m2 f = do
+ i1 <- m2i m1
+ i2 <- m2i m2
+ let res = f i1 i2
+ res `seq` i2m m1 i1
+ i2m m2 i2
+ return res
+
+
+-- IStore code
+(!) :: IStore e -> ElemIndex -> e
+(!) (IStore (_, arr)) (ElemIndex i) = (IA.!) arr i
+
+indices :: IStore e -> [ElemIndex]
+indices (IStore (busy, _)) = map ElemIndex $ IntSet.toList busy
--- a/gameServer/Utils.hs Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/Utils.hs Sat Nov 13 09:36:26 2010 -0500
@@ -1,3 +1,4 @@
+{-# LANGUAGE OverloadedStrings #-}
module Utils where
import Control.Concurrent
@@ -13,36 +14,33 @@
import System.IO
import qualified Data.List as List
import Control.Monad
-import Maybe
+import Data.Maybe
-------------------------------------------------
import qualified Codec.Binary.Base64 as Base64
-import qualified Data.ByteString.UTF8 as BUTF8
-import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as B
+import qualified Data.ByteString as BW
import CoreTypes
-sockAddr2String :: SockAddr -> IO String
-sockAddr2String (SockAddrInet _ hostAddr) = inet_ntoa hostAddr
+sockAddr2String :: SockAddr -> IO B.ByteString
+sockAddr2String (SockAddrInet _ hostAddr) = liftM B.pack $ inet_ntoa hostAddr
sockAddr2String (SockAddrInet6 _ _ (a, b, c, d) _) =
- return $ (foldr1 (.)
+ return $ B.pack $ (foldr1 (.)
$ List.intersperse (\a -> ':':a)
$ concatMap (\n -> (\(a, b) -> [showHex a, showHex b]) $ divMod n 65536) [a, b, c, d]) []
-toEngineMsg :: String -> String
-toEngineMsg msg = Base64.encode (fromIntegral (B.length encodedMsg) : (B.unpack encodedMsg))
- where
- encodedMsg = BUTF8.fromString msg
+toEngineMsg :: B.ByteString -> B.ByteString
+toEngineMsg msg = B.pack $ Base64.encode (fromIntegral (BW.length msg) : (BW.unpack msg))
-fromEngineMsg :: String -> Maybe String
-fromEngineMsg msg = liftM (map w2c) (Base64.decode msg >>= removeLength)
+fromEngineMsg :: B.ByteString -> Maybe B.ByteString
+fromEngineMsg msg = Base64.decode (B.unpack msg) >>= removeLength >>= return . BW.pack
where
removeLength (x:xs) = if length xs == fromIntegral x then Just xs else Nothing
removeLength _ = Nothing
-checkNetCmd :: String -> (Bool, Bool)
-checkNetCmd msg = check decoded
+checkNetCmd :: B.ByteString -> (Bool, Bool)
+checkNetCmd = check . liftM B.unpack . fromEngineMsg
where
- decoded = fromEngineMsg msg
check Nothing = (False, False)
check (Just (m:ms)) = (m `Set.member` legalMessages, m == '+')
check _ = (False, False)
@@ -54,29 +52,17 @@
[(x, rest)] | all isSpace rest -> Just x
_ -> Nothing
-teamToNet :: Word16 -> TeamInfo -> [String]
-teamToNet protocol team
- | protocol < 30 = [
- "ADD_TEAM",
- teamname team,
- teamgrave team,
- teamfort team,
- teamvoicepack team,
- teamowner team,
- show $ difficulty team
- ]
- ++ hhsInfo
- | otherwise = [
- "ADD_TEAM",
- teamname team,
- teamgrave team,
- teamfort team,
- teamvoicepack team,
- teamflag team,
- teamowner team,
- show $ difficulty team
- ]
- ++ hhsInfo
+teamToNet :: TeamInfo -> [B.ByteString]
+teamToNet team =
+ "ADD_TEAM"
+ : teamname team
+ : teamgrave team
+ : teamfort team
+ : teamvoicepack team
+ : teamflag team
+ : teamowner team
+ : (B.pack $ show $ difficulty team)
+ : hhsInfo
where
hhsInfo = concatMap (\(HedgehogInfo name hat) -> [name, hat]) $ hedgehogs team
@@ -90,10 +76,10 @@
else
t : replaceTeam team teams
-illegalName :: String -> Bool
-illegalName = all isSpace
+illegalName :: B.ByteString -> Bool
+illegalName = all isSpace . B.unpack
-protoNumber2ver :: Word16 -> String
+protoNumber2ver :: Word16 -> B.ByteString
protoNumber2ver 17 = "0.9.7-dev"
protoNumber2ver 19 = "0.9.7"
protoNumber2ver 20 = "0.9.8-dev"
@@ -118,3 +104,13 @@
putStr msg
hFlush stdout
getLine
+
+
+unfoldrE :: (b -> Either b (a, b)) -> b -> ([a], b)
+unfoldrE f b =
+ case f b of
+ Right (a, new_b) -> let (a', b') = unfoldrE f new_b in (a : a', b')
+ Left new_b -> ([], new_b)
+
+showB :: Show a => a -> B.ByteString
+showB = B.pack .show
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer/hedgewars-server.cabal Sat Nov 13 09:36:26 2010 -0500
@@ -0,0 +1,32 @@
+Name: hedgewars-server
+Version: 0.1
+Synopsis: hedgewars server
+Description: hedgewars server
+Homepage: http://www.hedgewars.org/
+License: GPL-2
+Author: unC0Rr
+Maintainer: unC0Rr@hedgewars.org
+Category: Game
+Build-type: Simple
+Cabal-version: >=1.2
+
+
+Executable hedgewars-server
+ main-is: hedgewars-server.hs
+
+ Build-depends:
+ base >= 4,
+ unix,
+ containers,
+ array,
+ bytestring,
+ network-bytestring,
+ network,
+ time,
+ stm,
+ mtl,
+ dataenc,
+ hslogger,
+ process
+
+ ghc-options: -O2
\ No newline at end of file
--- a/gameServer/hedgewars-server.hs Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/hedgewars-server.hs Sat Nov 13 09:36:26 2010 -0500
@@ -2,23 +2,15 @@
module Main where
-import Network.Socket
-import qualified Network
-import Network.BSD
+import Network
import Control.Concurrent.STM
import Control.Concurrent.Chan
-#if defined(NEW_EXCEPTIONS)
-import qualified Control.OldException as Exception
-#else
import qualified Control.Exception as Exception
-#endif
import System.Log.Logger
-----------------------------------
import Opts
import CoreTypes
-import OfficialServer.DBInteraction
import ServerCore
-import Utils
#if !defined(mingw32_HOST_OS)
@@ -26,10 +18,12 @@
#endif
+setupLoggers :: IO ()
setupLoggers =
updateGlobalLogger "Clients"
(setLevel INFO)
+main :: IO ()
main = withSocketsDo $ do
#if !defined(mingw32_HOST_OS)
installHandler sigPIPE Ignore Nothing;
@@ -38,11 +32,11 @@
setupLoggers
- stats <- atomically $ newTMVar (StatisticsInfo 0 0)
+ stats' <- atomically $ newTMVar (StatisticsInfo 0 0)
dbQueriesChan <- newChan
- coreChan <- newChan
- serverInfo' <- getOpts $ newServerInfo stats coreChan dbQueriesChan
-
+ coreChan' <- newChan
+ serverInfo' <- getOpts $ newServerInfo stats' coreChan' dbQueriesChan
+
#if defined(OFFICIAL_SERVER)
dbHost' <- askFromConsole "DB host: "
dbLogin' <- askFromConsole "login: "
@@ -52,14 +46,7 @@
let serverInfo = serverInfo'
#endif
-
- proto <- getProtocolNumber "tcp"
Exception.bracket
- (socket AF_INET Stream proto)
+ (Network.listenOn $ Network.PortNumber $ listenPort serverInfo)
sClose
- (\sock -> do
- setSocketOption sock ReuseAddr 1
- bindSocket sock (SockAddrInet (listenPort serverInfo) iNADDR_ANY)
- listen sock maxListenQueue
- startServer serverInfo sock
- )
+ (startServer serverInfo)
--- a/gameServer/stresstest.hs Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/stresstest.hs Sat Nov 13 09:36:26 2010 -0500
@@ -6,7 +6,7 @@
import System.IO
import Control.Concurrent
import Network
-import Control.Exception
+import Control.OldException
import Control.Monad
import System.Random
@@ -14,24 +14,24 @@
import System.Posix
#endif
-session1 nick room = ["NICK", nick, "", "PROTO", "24", "", "CHAT", "lobby 1", "", "CREATE", room, "", "CHAT", "room 1", "", "QUIT", "bye-bye", ""]
-session2 nick room = ["NICK", nick, "", "PROTO", "24", "", "LIST", "", "JOIN", room, "", "CHAT", "room 2", "", "PART", "", "CHAT", "lobby after part", "", "QUIT", "bye-bye", ""]
-session3 nick room = ["NICK", nick, "", "PROTO", "24", "", "LIST", "", "JOIN", room, "", "CHAT", "room 2", "", "QUIT", "bye-bye", ""]
+session1 nick room = ["NICK", nick, "", "PROTO", "32", "", "PING", "", "CHAT", "lobby 1", "", "CREATE_ROOM", room, "", "CHAT", "room 1", "", "QUIT", "creator", ""]
+session2 nick room = ["NICK", nick, "", "PROTO", "32", "", "LIST", "", "JOIN_ROOM", room, "", "CHAT", "room 2", "", "PART", "", "CHAT", "lobby after part", "", "QUIT", "part-quit", ""]
+session3 nick room = ["NICK", nick, "", "PROTO", "32", "", "LIST", "", "JOIN_ROON", room, "", "CHAT", "room 2", "", "QUIT", "quit", ""]
emulateSession sock s = do
- mapM_ (\x -> hPutStrLn sock x >> hFlush sock >> randomRIO (50000::Int, 90000) >>= threadDelay) s
+ mapM_ (\x -> hPutStrLn sock x >> hFlush sock >> randomRIO (30000::Int, 59000) >>= threadDelay) s
hFlush sock
threadDelay 225000
-testing = Control.Exception.handle print $ do
+testing = Control.OldException.handle print $ do
putStrLn "Start"
sock <- connectTo "127.0.0.1" (PortNumber 46631)
num1 <- randomRIO (70000::Int, 70100)
num2 <- randomRIO (0::Int, 2)
num3 <- randomRIO (0::Int, 5)
- let nick1 = show num1
- let room1 = show num2
+ let nick1 = 'n' : show num1
+ let room1 = 'r' : show num2
case num2 of
0 -> emulateSession sock $ session1 nick1 room1
1 -> emulateSession sock $ session2 nick1 room1
@@ -40,7 +40,7 @@
putStrLn "Finish"
forks = forever $ do
- delay <- randomRIO (10000::Int, 19000)
+ delay <- randomRIO (30000::Int, 59000)
threadDelay delay
forkIO testing
--- a/gameServer/stresstest2.hs Sat Nov 13 17:14:51 2010 +0300
+++ b/gameServer/stresstest2.hs Sat Nov 13 09:36:26 2010 -0500
@@ -6,7 +6,7 @@
import System.IO
import Control.Concurrent
import Network
-import Control.Exception
+import Control.OldException
import Control.Monad
import System.Random
@@ -14,22 +14,28 @@
import System.Posix
#endif
-testing = Control.Exception.handle print $ do
- delay <- randomRIO (100::Int, 300)
- threadDelay delay
+session1 nick room = ["NICK", nick, "", "PROTO", "32", ""]
+
+
+
+testing = Control.OldException.handle print $ do
+ putStrLn "Start"
sock <- connectTo "127.0.0.1" (PortNumber 46631)
- hClose sock
-forks i = do
- delay <- randomRIO (50::Int, 190)
- if i `mod` 10 == 0 then putStr (show i) else putStr "."
- hFlush stdout
- threadDelay delay
- forkIO testing
- forks (i + 1)
+ num1 <- randomRIO (70000::Int, 70100)
+ num2 <- randomRIO (0::Int, 2)
+ num3 <- randomRIO (0::Int, 5)
+ let nick1 = 'n' : show num1
+ let room1 = 'r' : show num2
+ mapM_ (\x -> hPutStrLn sock x >> hFlush sock >> randomRIO (300::Int, 590) >>= threadDelay) $ session1 nick1 room1
+ mapM_ (\x -> hPutStrLn sock x >> hFlush sock) $ concatMap (\x -> ["CHAT_MSG", show x, ""]) [1..]
+ hClose sock
+ putStrLn "Finish"
+
+forks = testing
main = withSocketsDo $ do
#if !defined(mingw32_HOST_OS)
installHandler sigPIPE Ignore Nothing;
#endif
- forks 1
+ forks
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer/stresstest3.hs Sat Nov 13 09:36:26 2010 -0500
@@ -0,0 +1,75 @@
+{-# LANGUAGE CPP #-}
+
+module Main where
+
+import IO
+import System.IO
+import Control.Concurrent
+import Network
+import Control.OldException
+import Control.Monad
+import System.Random
+import Control.Monad.State
+import Data.List
+
+#if !defined(mingw32_HOST_OS)
+import System.Posix
+#endif
+
+type SState = Handle
+io = liftIO
+
+readPacket :: StateT SState IO [String]
+readPacket = do
+ h <- get
+ p <- io $ hGetPacket h []
+ return p
+ where
+ hGetPacket h buf = do
+ l <- hGetLine h
+ if (not $ null l) then hGetPacket h (buf ++ [l]) else return buf
+
+waitPacket :: String -> StateT SState IO Bool
+waitPacket s = do
+ p <- readPacket
+ return $ head p == s
+
+sendPacket :: [String] -> StateT SState IO ()
+sendPacket s = do
+ h <- get
+ io $ do
+ mapM_ (hPutStrLn h) s
+ hPutStrLn h ""
+ hFlush h
+
+emulateSession :: StateT SState IO ()
+emulateSession = do
+ n <- io $ randomRIO (100000::Int, 100100)
+ waitPacket "CONNECTED"
+ sendPacket ["NICK", "test" ++ (show n)]
+ waitPacket "NICK"
+ sendPacket ["PROTO", "31"]
+ waitPacket "PROTO"
+ b <- waitPacket "LOBBY:JOINED"
+ --io $ print b
+ sendPacket ["QUIT", "BYE"]
+ return ()
+
+testing = Control.OldException.handle print $ do
+ putStr "+"
+ sock <- connectTo "127.0.0.1" (PortNumber 46631)
+ evalStateT emulateSession sock
+ --hClose sock
+ putStr "-"
+ hFlush stdout
+
+forks = forM_ [1..100] $ const $ do
+ delay <- randomRIO (10000::Int, 30000)
+ threadDelay delay
+ forkIO testing
+
+main = withSocketsDo $ do
+#if !defined(mingw32_HOST_OS)
+ installHandler sigPIPE Ignore Nothing;
+#endif
+ forks
--- a/hedgewars/GSHandlers.inc Sat Nov 13 17:14:51 2010 +0300
+++ b/hedgewars/GSHandlers.inc Sat Nov 13 09:36:26 2010 -0500
@@ -317,7 +317,11 @@
if Gear^.AdvBounce > 1 then dec(Gear^.AdvBounce);
- if isFalling then Gear^.dY := Gear^.dY + cGravity;
+ if isFalling then
+ begin
+ Gear^.dY := Gear^.dY + cGravity;
+ if (GameFlags and gfMoreWind) <> 0 then Gear^.dX := Gear^.dX + cWindSpeed * _16 / max(12,sqr(Gear^.Radius))
+ end;
Gear^.X := Gear^.X + Gear^.dX;
Gear^.Y := Gear^.Y + Gear^.dY;
@@ -512,7 +516,7 @@
procedure doStepShell(Gear: PGear);
begin
AllInactive := false;
- Gear^.dX := Gear^.dX + cWindSpeed;
+ if (GameFlags and gfMoreWind) = 0 then Gear^.dX := Gear^.dX + cWindSpeed;
doStepFallingGear(Gear);
if (Gear^.State and gstCollision) <> 0 then
begin
@@ -704,6 +708,7 @@
var
i, x, y: LongWord;
oX, oY: hwFloat;
+ trail: PVisualGear;
begin
AllInactive := false;
inc(Gear^.Timer);
@@ -750,6 +755,30 @@
cLaserSighting := false;
if (Ammoz[Gear^.AmmoType].Ammo.NumPerTurn <= CurrentHedgehog^.MultiShootAttacks) and
((GameFlags and gfArtillery) = 0) then cArtillery := false;
+
+ // Bullet trail
+ trail := AddVisualGear(
+ hwround(CurrentHedgehog^.Gear^.X) + GetLaunchX(CurrentHedgehog^.CurAmmoType, hwSign(CurrentHedgehog^.Gear^.dX), CurrentHedgehog^.Gear^.Angle),
+ hwround(CurrentHedgehog^.Gear^.Y) + GetLaunchY(CurrentHedgehog^.CurAmmoType, CurrentHedgehog^.Gear^.Angle),
+ vgtLineTrail
+ );
+ if trail <> nil then
+ begin
+ // http://mantis.freepascal.org/view.php?id=17714 hits again
+ trail^.dX := Gear^.X.QWordValue / SignAs(_1,_1).QWordValue;
+ trail^.dY := Gear^.Y.QWordValue / SignAs(_1,_1).QWordValue;
+
+ // reached edge of land. assume infinite beam. Extend it way out past camera
+ if (hwRound(Gear^.X) and LAND_WIDTH_MASK <> 0)
+ or (hwRound(Gear^.Y) and LAND_HEIGHT_MASK <> 0) then
+ begin
+ trail^.dX := trail^.dX + (CurrentHedgehog^.Gear^.dX * LAND_WIDTH).QWordValue / SignAs(_1,_1).QWordValue;
+ trail^.dY := trail^.dY + (CurrentHedgehog^.Gear^.dY * LAND_WIDTH).QWordValue / SignAs(_1,_1).QWordValue;
+ end;
+
+ trail^.Timer := 200;
+ end;
+
Gear^.doStep := @doStepShotIdle
end;
end;
@@ -1081,6 +1110,7 @@
HHGear^.X := HHGear^.X + HHGear^.dX;
HHGear^.Y := HHGear^.Y + HHGear^.dY;
HHGear^.dY := HHGear^.dY + cGravity;
+ if (GameFlags and gfMoreWind) <> 0 then HHGear^.dX := HHGear^.dX + cWindSpeed * _0_2;
if (Gear^.Message and gmAttack) <> 0 then
begin
@@ -1143,7 +1173,11 @@
else
if (Gear^.Message and gmRight <> 0) then HHGear^.dX := HHGear^.dX + _0_0002;
- if not TestCollisionYwithGear(HHGear, 1) then HHGear^.dY := HHGear^.dY + cGravity;
+ if not TestCollisionYwithGear(HHGear, 1) then
+ begin
+ HHGear^.dY := HHGear^.dY + cGravity;
+ if (GameFlags and gfMoreWind) <> 0 then HHGear^.dX := HHGear^.dX + cWindSpeed * _0_2
+ end;
ropeDx := HHGear^.X - Gear^.X;
// vector between hedgehog and rope attaching point
@@ -1344,7 +1378,8 @@
begin
HHGear^.Y := HHGear^.Y + HHGear^.dY;
Gear^.Y := Gear^.Y + HHGear^.dY;
- HHGear^.dY := HHGear^.dY + cGravity
+ HHGear^.dY := HHGear^.dY + cGravity;
+ if (GameFlags and gfMoreWind) <> 0 then HHGear^.dX := HHGear^.dX + cWindSpeed * _0_2
end;
tt := Gear^.Elasticity;
@@ -2059,6 +2094,10 @@
2: for i:= -19 to 19 do
FollowGear := AddGear(hwRound(Gear^.X) + i div 3, hwRound(Gear^.Y), gtFlame, 0,
_0_001 * i, _0, 0);
+ 3: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtDrill, gsttmpFlag, cBombsSpeed *
+ Gear^.Tag, _0, 0);
+ //4: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtWaterMelon, 0, cBombsSpeed *
+ // Gear^.Tag, _0, 5000);
end;
Gear^.dX := Gear^.dX + int2hwFloat(30 * Gear^.Tag)
end;
@@ -2700,6 +2739,8 @@
Gear^.X := Gear^.X + Gear^.dX;
Gear^.Y := Gear^.Y + Gear^.dY;
DrawTunnel(oX, oY, Gear^.dX, Gear^.dY, 2, 6);
+ if (Gear^.Timer mod 30) = 0 then
+ AddVisualGear(hwRound(Gear^.X + _20 * Gear^.dX), hwRound(Gear^.Y + _20 * Gear^.dY), vgtDust);
if (CheckGearDrowning(Gear)) then
begin
StopSound(Gear^.SoundChannel);
@@ -2718,7 +2759,10 @@
begin
//out of time or exited ground
StopSound(Gear^.SoundChannel);
- doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound);
+ if (Gear^.State and gsttmpFlag) <> 0 then
+ doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, EXPLAutoSound)
+ else
+ doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound);
DeleteGear(Gear);
exit
end;
@@ -2734,7 +2778,9 @@
begin
AllInactive := false;
- Gear^.dX := Gear^.dX + cWindSpeed;
+ if (Gear^.State and gsttmpFlag) = 0 then
+ Gear^.dX := Gear^.dX + cWindSpeed;
+
oldDx := Gear^.dX;
oldDy := Gear^.dY;
@@ -2760,7 +2806,10 @@
else
begin
//explode right on contact with HH
- doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound);
+ if (Gear^.State and gsttmpFlag) <> 0 then
+ doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, EXPLAutoSound)
+ else
+ doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, EXPLAutoSound);
DeleteGear(Gear);
exit;
end;
@@ -3164,19 +3213,19 @@
Gear^.Tag := 1;
if (HHGear^.Message and gmUp) <> 0 then
- begin
+ begin
if (not HHGear^.dY.isNegative) or (HHGear^.Y > -_256) then
HHGear^.dY := HHGear^.dY - move;
dec(Gear^.Health, fuel);
Gear^.MsgParam := Gear^.MsgParam or gmUp;
- end;
+ end;
if (HHGear^.Message and gmLeft) <> 0 then move.isNegative := true;
if (HHGear^.Message and (gmLeft or gmRight)) <> 0 then
- begin
+ begin
HHGear^.dX := HHGear^.dX + (move * _0_1);
dec(Gear^.Health, fuel div 5);
Gear^.MsgParam := Gear^.MsgParam or (HHGear^.Message and (gmLeft or gmRight));
- end;
+ end;
if Gear^.Health < 0 then Gear^.Health := 0;
if ((GameTicks and $FF) = 0) and (Gear^.Health < 500) then
@@ -3184,16 +3233,15 @@
AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFeather);
if (HHGear^.Message and gmAttack <> 0) then
- begin
+ begin
HHGear^.Message := HHGear^.Message and not gmAttack;
if Gear^.FlightTime > 0 then
- begin
- AddGear(hwRound(Gear^.X), hwRound(Gear^.Y) + 32, gtEgg, 0, Gear^.dX * _0_5, Gear^.dY, 0)
- ;
+ begin
+ AddGear(hwRound(Gear^.X), hwRound(Gear^.Y) + 32, gtEgg, 0, Gear^.dX * _0_5, Gear^.dY, 0);
PlaySound(sndBirdyLay);
dec(Gear^.FlightTime)
+ end;
end;
- end;
if HHGear^.Message and (gmUp or gmPrecise or gmLeft or gmRight) <> 0 then
Gear^.State := Gear^.State and not gsttmpFlag;
@@ -3215,25 +3263,25 @@
or (((GameTicks and $1FF) = 0) and (not HHGear^.dY.isNegative) and TestCollisionYwithGear(
HHGear, 1))
or ((Gear^.Message and gmAttack) <> 0) then
- begin
+ begin
with HHGear^ do
- begin
+ begin
Message := 0;
Active := true;
State := State or gstMoving
- end;
+ end;
Gear^.State := Gear^.State or gstAnimation or gstTmpFlag;
if HHGear^.dY < _0 then
- begin
+ begin
Gear^.dX := HHGear^.dX;
Gear^.dY := HHGear^.dY;
- end;
+ end;
Gear^.Timer := 0;
Gear^.doStep := @doStepBirdyDisappear;
CurAmmoGear := nil;
isCursorVisible := false;
AfterAttack;
- end
+ end
end;
////////////////////////////////////////////////////////////////////////////////
@@ -4198,7 +4246,7 @@
Gear^.doStep := @doStepHammerHitWork
end;
-
+////////////////////////////////////////////////////////////////////////////////
procedure doStepResurrectorWork(Gear: PGear);
var
graves: TPGearArray;
@@ -4313,3 +4361,5 @@
end
end;
+////////////////////////////////////////////////////////////////////////////////
+
--- a/hedgewars/GearDrawing.inc Sat Nov 13 17:14:51 2010 +0300
+++ b/hedgewars/GearDrawing.inc Sat Nov 13 09:36:26 2010 -0500
@@ -4,7 +4,6 @@
sign, hx, hy, cx, cy, tx, ty, sx, sy, m: LongInt; // hedgehog, crosshair, temp, sprite, direction
dx, dy, ax, ay, aAngle, dAngle, hAngle, lx, ly: real; // laser, change
defaultPos, HatVisible: boolean;
- VertexBuffer: array [0..1] of TVertex2f;
HH: PHedgehog;
CurWeapon: PAmmo;
begin
@@ -126,22 +125,7 @@
//if (abs(lx-tx)>8) or (abs(ly-ty)>8) then
begin
- glDisable(GL_TEXTURE_2D);
- glEnable(GL_LINE_SMOOTH);
-
- glLineWidth(1.0);
-
- Tint($FF, $00, $00, $C0);
- VertexBuffer[0].X:= hx + WorldDx;
- VertexBuffer[0].Y:= hy + WorldDy;
- VertexBuffer[1].X:= tx + WorldDx;
- VertexBuffer[1].Y:= ty + WorldDy;
-
- glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
- glDrawArrays(GL_LINES, 0, Length(VertexBuffer));
- Tint($FF, $FF, $FF, $FF);
- glEnable(GL_TEXTURE_2D);
- glDisable(GL_LINE_SMOOTH);
+ DrawLine(hx, hy, tx, ty, 1.0, $FF, $00, $00, $C0);
end;
end;
// draw crosshair
@@ -438,7 +422,8 @@
case amt of
amAirAttack,
- amMineStrike: DrawRotated(sprHandAirAttack, sx, oy, sign, 0);
+ amMineStrike,
+ amDrillStrike: DrawRotated(sprHandAirAttack, sx, oy, sign, 0);
amPickHammer: DrawHedgehog(sx, sy,
sign,
1,
@@ -711,7 +696,10 @@
DrawRotatedf(sprPortal, x, y, Gear^.Tag, hwSign(Gear^.dX), Gear^.DirAngle)
else DrawRotatedf(sprPortal, x, y, 4 + Gear^.Tag div 2, hwSign(Gear^.dX), Gear^.DirAngle);
- gtDrill: DrawRotated(sprDrill, x, y, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
+ gtDrill: if (Gear^.State and gsttmpFlag) <> 0 then
+ DrawRotated(sprAirDrill, x, y, 0, DxDy2Angle(Gear^.dY, Gear^.dX))
+ else
+ DrawRotated(sprDrill, x, y, 0, DxDy2Angle(Gear^.dY, Gear^.dX));
gtHedgehog: DrawHH(Gear, x, y);
--- a/hedgewars/HHHandlers.inc Sat Nov 13 17:14:51 2010 +0300
+++ b/hedgewars/HHHandlers.inc Sat Nov 13 09:36:26 2010 -0500
@@ -305,6 +305,8 @@
gtResurrector, 0, _0, _0, 0);
CurAmmoGear^.SoundChannel := LoopSound(sndResurrector);
end;
+ amDrillStrike: AddGear(CurWeapon^.Pos, 0, gtAirAttack, 3, _0, _0, 0);
+ //amMelonStrike: AddGear(CurWeapon^.Pos, 0, gtAirAttack, 4, _0, _0, 0);
end;
uStats.AmmoUsed(CurAmmoType);
@@ -660,11 +662,22 @@
if (Gear^.dY.isNegative) and TestCollisionYKick(Gear, -1) then Gear^.dY:= _0;
Gear^.State:= Gear^.State or gstMoving;
if isUnderwater then Gear^.dY:= Gear^.dY + cGravity / _2
- else Gear^.dY:= Gear^.dY + cGravity
+ else
+ begin
+ Gear^.dY:= Gear^.dY + cGravity;
+// this set of circumstances could be less complex if jumping was more clearly identified
+ if ((GameFlags and gfMoreWind) <> 0) and
+ (Gear^.Damage <> 0) or
+ ((CurAmmoGear <> nil) and
+ ((CurAmmoGear^.AmmoType = amJetpack) or
+ (CurAmmoGear^.AmmoType = amBirdy))) or
+ ((Gear^.dY.QWordValue + Gear^.dX.QWordValue) > _0_55.QWordValue)
+ then Gear^.dX := Gear^.dX + cWindSpeed * _0_2
+ end
end
else
begin
- if ((hwAbs(Gear^.dX) + hwAbs(Gear^.dY)) < _0_55)
+ if ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) < _0_55.QWordValue)
and ((Gear^.State and gstHHJumping) <> 0) then SetLittle(Gear^.dX);
if not Gear^.dY.isNegative then
--- a/hedgewars/VGSHandlers.inc Sat Nov 13 17:14:51 2010 +0300
+++ b/hedgewars/VGSHandlers.inc Sat Nov 13 09:36:26 2010 -0500
@@ -133,6 +133,16 @@
end;
////////////////////////////////////////////////////////////////////////////////
+procedure doStepLineTrail(Gear: PVisualGear; Steps: Longword);
+begin
+Steps := Steps;
+if Gear^.Timer <= Steps then
+ DeleteVisualGear(Gear)
+else
+ dec(Gear^.Timer, Steps)
+end;
+
+////////////////////////////////////////////////////////////////////////////////
procedure doStepEgg(Gear: PVisualGear; Steps: Longword);
begin
Gear^.X:= Gear^.X + Gear^.dX * Steps;
--- a/hedgewars/uAIAmmoTests.pas Sat Nov 13 17:14:51 2010 +0300
+++ b/hedgewars/uAIAmmoTests.pas Sat Nov 13 09:36:26 2010 -0500
@@ -101,7 +101,8 @@
(proc: nil; flags: 0), // amFlamethrower
(proc: @TestGrenade; flags: 0), // amSMine
(proc: @TestFirePunch; flags: 0), // amHammer
- (proc: nil; flags: 0) // amResurrector
+ (proc: nil; flags: 0), // amResurrector
+ (proc: nil; flags: 0) // amDrillStrike
);
const BadTurn = Low(LongInt) div 4;
--- a/hedgewars/uConsts.pas Sat Nov 13 17:14:51 2010 +0300
+++ b/hedgewars/uConsts.pas Sat Nov 13 09:36:26 2010 -0500
@@ -75,7 +75,7 @@
sprFeather, sprPiano, sprHandSineGun, sprPortalGun, sprPortal,
sprCheese, sprHandCheese, sprHandFlamethrower, sprChunk, sprNote,
sprSMineOff, sprSMineOn, sprHandSMine, sprHammer,
- sprHandResurrector, sprCross
+ sprHandResurrector, sprCross, sprAirDrill
);
// Gears that interact with other Gears and/or Land
@@ -89,7 +89,7 @@
gtHellishBomb, gtWaterUp, gtDrill, gtBallGun, gtBall, gtRCPlane, // 40
gtSniperRifleShot, gtJetpack, gtMolotov, gtExplosives, gtBirdy, // 45
gtEgg, gtPortal, gtPiano, gtGasBomb, gtSineGunShot, gtFlamethrower, // 51
- gtSMine, gtPoisonCloud, gtHammer, gtHammerHit, gtResurrector);
+ gtSMine, gtPoisonCloud, gtHammer, gtHammerHit, gtResurrector); // 56
// Gears that are _only_ of visual nature (e.g. background stuff, visual effects, speechbubbles, etc.)
TVisualGearType = (vgtFlake, vgtCloud, vgtExplPart, vgtExplPart2, vgtFire,
@@ -97,7 +97,7 @@
vgtSteam, vgtAmmo, vgtSmoke, vgtSmokeWhite, vgtHealth, vgtShell,
vgtDust, vgtSplash, vgtDroplet, vgtSmokeRing, vgtBeeTrace, vgtEgg,
vgtFeather, vgtHealthTag, vgtSmokeTrace, vgtEvilTrace, vgtExplosion,
- vgtBigExplosion, vgtChunk, vgtNote);
+ vgtBigExplosion, vgtChunk, vgtNote, vgtLineTrail);
TGearsType = set of TGearType;
@@ -134,7 +134,7 @@
amRCPlane, amLowGravity, amExtraDamage, amInvulnerable, amExtraTime, // 35
amLaserSight, amVampiric, amSniperRifle, amJetpack, amMolotov, amBirdy, amPortalGun, // 42
amPiano, amGasBomb, amSineGun, amFlamethrower, amSMine, amHammer, // 48
- amResurrector);
+ amResurrector, amDrillStrike);
TCrateType = (HealthCrate, AmmoCrate, UtilityCrate);
@@ -355,6 +355,7 @@
gfResetWeps = $00200000;
gfPerHogAmmo = $00400000;
gfDisableWind = $00800000; // only lua for now
+ gfMoreWind = $01000000;
// NOTE: When adding new game flags, ask yourself
// if a "game start notice" would be useful. If so,
// add one in uWorld.pas - look for "AddGoal".
@@ -824,8 +825,13 @@
(FileName: 'Cross'; Path: ptGraphics; altPath: ptNone;
Texture: nil; Surface: nil; Width: 108; Height: 138;
imageWidth: 0; imageHeight: 0; saveSurf: false; priority:
+ tpMedium; getDimensions: false; getImageDimensions: true),
+ //sprCross
+ (FileName: 'AirDrill'; Path: ptGraphics; AltPath: ptNone;
+ Texture: nil; Surface: nil; Width: 16; Height: 16;
+ imageWidth: 0; imageHeight: 0; saveSurf: false; priority:
tpMedium; getDimensions: false; getImageDimensions: true)
- //sprCross
+ // sprAirDrill
);
@@ -2183,6 +2189,7 @@
ejectX: 0;
ejectY: 0),
+// Ressurrector
(NameId: sidResurrector;
NameTex: nil;
Probability: 0;
@@ -2205,6 +2212,33 @@
PosCount: 1;
PosSprite: sprWater;
ejectX: 0;
+ ejectY: 0),
+
+// DrillStrike
+ (NameId: sidDrillStrike;
+ NameTex: nil;
+ Probability: 200;
+ NumberInCase: 1;
+ Ammo: (Propz: ammoprop_NoCrosshair or
+ ammoprop_NeedTarget or
+ ammoprop_AttackingPut or
+ ammoprop_DontHold or
+ ammoprop_NotBorder;
+ Count: 1;
+ NumPerTurn: 0;
+ Timer: 0;
+ Pos: 0;
+ AmmoType: amDrillStrike;
+ AttackVoice: sndIncoming);
+ Slot: 5;
+ TimeAfterTurn: 0;
+ minAngle: 0;
+ maxAngle: 0;
+ isDamaging: true;
+ SkipTurns: 6;
+ PosCount: 2;
+ PosSprite: sprAmAirplane;
+ ejectX: 0;
ejectY: 0)
);
--- a/hedgewars/uGears.pas Sat Nov 13 17:14:51 2010 +0300
+++ b/hedgewars/uGears.pas Sat Nov 13 09:36:26 2010 -0500
@@ -298,7 +298,7 @@
gear^.ImpactSound:= sndGrenadeImpact;
gear^.nImpactSounds:= 1;
gear^.AdvBounce:= 1;
- gear^.Radius:= 6;
+ gear^.Radius:= 5;
gear^.Elasticity:= _0_8;
gear^.Friction:= _0_8;
gear^.RenderTimer:= true;
@@ -308,7 +308,7 @@
gear^.ImpactSound:= sndMelonImpact;
gear^.nImpactSounds:= 1;
gear^.AdvBounce:= 1;
- gear^.Radius:= 4;
+ gear^.Radius:= 6;
gear^.Elasticity:= _0_8;
gear^.Friction:= _0_995;
gear^.RenderTimer:= true;
@@ -365,9 +365,9 @@
gear^.Elasticity:= _0_55;
gear^.Friction:= _0_995;
if cMinesTime < 0 then
- gear^.Timer:= getrandom(6)*1000
+ gear^.Timer:= getrandom(51)*100
else
- gear^.Timer:= cMinesTime*1;
+ gear^.Timer:= cMinesTime*1000;
end;
gtSMine: begin
gear^.Health:= 10;
--- a/hedgewars/uLocale.pas Sat Nov 13 17:14:51 2010 +0300
+++ b/hedgewars/uLocale.pas Sat Nov 13 09:36:26 2010 -0500
@@ -30,7 +30,7 @@
sidLowGravity, sidExtraDamage, sidInvulnerable, sidExtraTime,
sidLaserSight, sidVampiric, sidSniperRifle, sidJetpack,
sidMolotov, sidBirdy, sidPortalGun, sidPiano, sidGasBomb, sidSineGun, sidFlamethrower,
- sidSMine, sidHammer, sidResurrector);
+ sidSMine, sidHammer, sidResurrector, sidDrillStrike);
TMsgStrId = (sidStartFight, sidDraw, sidWinner, sidVolume, sidPaused,
sidConfirm, sidSuddenDeath, sidRemaining, sidFuel, sidSync,
--- a/hedgewars/uMisc.pas Sat Nov 13 17:14:51 2010 +0300
+++ b/hedgewars/uMisc.pas Sat Nov 13 09:36:26 2010 -0500
@@ -758,7 +758,7 @@
cMapGen := 0; // MAPGEN_REGULAR
cMazeSize := 0;
cHedgehogTurnTime := 45000;
- cMinesTime := 3000;
+ cMinesTime := 3;
cMaxAIThinkTime := 9000;
cCloudsNumber := 9;
cHealthCaseProb := 35;
--- a/hedgewars/uStore.pas Sat Nov 13 17:14:51 2010 +0300
+++ b/hedgewars/uStore.pas Sat Nov 13 09:36:26 2010 -0500
@@ -57,6 +57,7 @@
procedure DrawFromRect(X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture);
procedure DrawFromRect(X, Y: LongInt; r: PSDL_Rect; SourceTexture: PTexture);
procedure DrawHedgehog(X, Y: LongInt; Dir: LongInt; Pos, Step: LongWord; Angle: real);
+procedure DrawLine(X0, Y0, X1, Y1, Width: Single; r, g, b, a: Byte);
procedure DrawFillRect(r: TSDL_Rect);
procedure DrawCircle(X, Y, Radius: LongInt; Width: Single; r, g, b, a: Byte);
procedure DrawRoundRect(rect: PSDL_Rect; BorderColor, FillColor: Longword; Surface: PSDL_Surface; Clear: boolean);
@@ -791,6 +792,32 @@
glPopMatrix
end;
+procedure DrawLine(X0, Y0, X1, Y1, Width: Single; r, g, b, a: Byte);
+var VertexBuffer: array [0..3] of TVertex2f;
+begin
+ glDisable(GL_TEXTURE_2D);
+ glEnable(GL_LINE_SMOOTH);
+
+ glPushMatrix;
+ glTranslatef(WorldDx, WorldDy, 0);
+ glLineWidth(Width);
+
+ Tint(r, g, b, a);
+ VertexBuffer[0].X:= X0;
+ VertexBuffer[0].Y:= Y0;
+ VertexBuffer[1].X:= X1;
+ VertexBuffer[1].Y:= Y1;
+
+ glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
+ glDrawArrays(GL_LINES, 0, Length(VertexBuffer));
+ Tint($FF, $FF, $FF, $FF);
+
+ glPopMatrix;
+
+ glEnable(GL_TEXTURE_2D);
+ glDisable(GL_LINE_SMOOTH);
+end;
+
procedure DrawFillRect(r: TSDL_Rect);
var VertexBuffer: array [0..3] of TVertex2f;
begin
--- a/hedgewars/uVisualGears.pas Sat Nov 13 17:14:51 2010 +0300
+++ b/hedgewars/uVisualGears.pas Sat Nov 13 09:36:26 2010 -0500
@@ -117,7 +117,8 @@
@doStepExplosion,
@doStepBigExplosion,
@doStepChunk,
- @doStepNote
+ @doStepNote,
+ @doStepLineTrail
);
function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord = 0): PVisualGear;
@@ -406,6 +407,7 @@
case Gear^.Kind of
vgtSmokeTrace: if Gear^.State < 8 then DrawSprite(sprSmokeTrace, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.State);
vgtEvilTrace: if Gear^.State < 8 then DrawSprite(sprEvilTrace, round(Gear^.X) + WorldDx, round(Gear^.Y) + WorldDy, Gear^.State);
+ vgtLineTrail: DrawLine(Gear^.X, Gear^.Y, Gear^.dX, Gear^.dY, 1.0, $FF, min(Gear^.Timer, $C0), min(Gear^.Timer, $80), min(Gear^.Timer, $FF));
end;
if (cReducedQuality and rqFancyBoom) = 0 then
case Gear^.Kind of
--- a/hedgewars/uWorld.pas Sat Nov 13 17:14:51 2010 +0300
+++ b/hedgewars/uWorld.pas Sat Nov 13 09:36:26 2010 -0500
@@ -149,14 +149,14 @@
ScreenFadeSpeed:= 1;
// modified mine timers?
-if cMinesTime <> 3000 then
+if cMinesTime <> 3 then
begin
if cMinesTime = 0 then
g:= AddGoal(g, gfAny, gidNoMineTimer)
else if cMinesTime < 0 then
g:= AddGoal(g, gfAny, gidRandomMineTimer)
else
- g:= AddGoal(g, gfAny, gidMineTimer, cMinesTime div 1000);
+ g:= AddGoal(g, gfAny, gidMineTimer, cMinesTime);
end;
// if the string has been set, show it for (default timeframe) seconds
--- a/project_files/HedgewarsMobile/Classes/CommodityFunctions.h Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Classes/CommodityFunctions.h Sat Nov 13 09:36:26 2010 -0500
@@ -60,9 +60,6 @@
#define DEFAULT_NETGAME_PORT 46631
-void createTeamNamed (NSString *nameWithoutExt);
-void createWeaponNamed (NSString *nameWithoutExt, int type);
-void createSchemeNamed (NSString *nameWithoutExt);
void print_free_memory (void);
void playSound (NSString *snd);
void popError (const char *title, const char *message);
@@ -73,3 +70,5 @@
NSArray *getAvailableColors (void);
UILabel *createBlueLabel (NSString *title, CGRect frame);
UILabel *createLabelWithParams (NSString *title, CGRect frame, CGFloat borderWidth, UIColor *borderColor, UIColor *backgroundColor);
+
+CGSize PSPNGSizeFromMetaData (NSString *aFileName);
--- a/project_files/HedgewarsMobile/Classes/CommodityFunctions.m Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Classes/CommodityFunctions.m Sat Nov 13 09:36:26 2010 -0500
@@ -28,169 +28,6 @@
#import "AudioToolbox/AudioToolbox.h"
#import "PascalImports.h"
-void createTeamNamed (NSString *nameWithoutExt) {
- NSString *teamsDirectory = TEAMS_DIRECTORY();
-
- if (![[NSFileManager defaultManager] fileExistsAtPath: teamsDirectory]) {
- [[NSFileManager defaultManager] createDirectoryAtPath:teamsDirectory
- withIntermediateDirectories:NO
- attributes:nil
- error:NULL];
- }
-
- NSMutableArray *hedgehogs = [[NSMutableArray alloc] initWithCapacity: HW_getMaxNumberOfHogs()];
-
- for (int i = 0; i < HW_getMaxNumberOfHogs(); i++) {
- NSString *hogName = [[NSString alloc] initWithFormat:@"hedgehog %d",i];
- NSDictionary *hog = [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithInt:0],@"level",
- hogName,@"hogname", @"NoHat",@"hat", nil];
- [hogName release];
- [hedgehogs addObject:hog];
- [hog release];
- }
-
- NSDictionary *theTeam = [[NSDictionary alloc] initWithObjectsAndKeys:@"0",@"hash",
- @"Statue",@"grave", @"Plane",@"fort", @"Default",@"voicepack",
- @"hedgewars",@"flag", hedgehogs,@"hedgehogs", nil];
- [hedgehogs release];
-
- NSString *teamFile = [[NSString alloc] initWithFormat:@"%@/%@.plist", teamsDirectory, nameWithoutExt];
-
- [theTeam writeToFile:teamFile atomically:YES];
- [teamFile release];
- [theTeam release];
-}
-
-void createWeaponNamed (NSString *nameWithoutExt, int type) {
- NSString *weaponsDirectory = WEAPONS_DIRECTORY();
-
- if (![[NSFileManager defaultManager] fileExistsAtPath: weaponsDirectory]) {
- [[NSFileManager defaultManager] createDirectoryAtPath:weaponsDirectory
- withIntermediateDirectories:NO
- attributes:nil
- error:NULL];
- }
-
- NSDictionary *theWeapon = nil;
- switch (type) {
- case 0: //default
- theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
- @"9391929422199121032235111001201000000211110101011",@"ammostore_initialqt",
- @"0405040541600655546554464776576666666155510101117",@"ammostore_probability",
- @"0000000000000205500000040007004000000000200000000",@"ammostore_delay",
- @"1311110312111111123114111111111111111211111101111",@"ammostore_crate", nil];
- break;
- case 1: //crazy
- theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
- @"9999999999999999992999999999999999299999999909999",@"ammostore_initialqt",
- @"1111110111111111111111111111111111111111111101111",@"ammostore_probability",
- @"0000000000000000000000000000000000000000000000000",@"ammostore_delay",
- @"1311110312111111123114111111111111111211110101111",@"ammostore_crate", nil];
- break;
- case 2: //pro mode
- theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
- @"9090009000000000000009000000000000000000000000000",@"ammostore_initialqt",
- @"0000000000000000000000000000000000000000000000000",@"ammostore_probability",
- @"0000000000000205500000040007004000000000200000000",@"ammostore_delay",
- @"1111111111111111111111111111111111111111100101111",@"ammostore_crate", nil];
- break;
- case 3: //shoppa
- theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
- @"0000009900000000000000000000000000000000000000000",@"ammostore_initialqt",
- @"4444410044244402210112121222422000000002000400010",@"ammostore_probability",
- @"0000000000000000000000000000000000000000000000000",@"ammostore_delay",
- @"1111111111111111111111111111111111111111101101111",@"ammostore_crate", nil];
- break;
- case 4: //clean slate
- theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
- @"1010009000010000011000000000000000000000000000001",@"ammostore_initialqt",
- @"0405040541600655546554464776576666666155510101117",@"ammostore_probability",
- @"0000000000000205500000040007004000000000200000000",@"ammostore_delay",
- @"1311110312111111123114111111111111111211111101111",@"ammostore_crate", nil];
- break;
- case 5: //minefield
- theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
- @"0000009900090000000300000000000000000000000000000",@"ammostore_initialqt",
- @"0000000000000000000000000000000000000000000000000",@"ammostore_probability",
- @"0000000000000205500000040007004000000000200000000",@"ammostore_delay",
- @"1111111111111111111111111111111111111111111101111",@"ammostore_crate", nil];
- break;
- default:
- NSLog(@"Nope");
- break;
- }
-
- NSString *weaponFile = [[NSString alloc] initWithFormat:@"%@/%@.plist", weaponsDirectory, nameWithoutExt];
-
- [theWeapon writeToFile:weaponFile atomically:YES];
- [weaponFile release];
- [theWeapon release];
-}
-
-void createSchemeNamed (NSString *nameWithoutExt) {
- NSString *schemesDirectory = SCHEMES_DIRECTORY();
-
- if (![[NSFileManager defaultManager] fileExistsAtPath: schemesDirectory]) {
- [[NSFileManager defaultManager] createDirectoryAtPath:schemesDirectory
- withIntermediateDirectories:NO
- attributes:nil
- error:NULL];
- }
-
- NSMutableArray *basicArray = [[NSMutableArray alloc] initWithObjects:
- [NSNumber numberWithInt:100], //initialhealth
- [NSNumber numberWithInt:45], //turntime
- [NSNumber numberWithInt:100], //damagemodifier
- [NSNumber numberWithInt:15], //suddendeathtimeout
- [NSNumber numberWithInt:47], //waterrise
- [NSNumber numberWithInt:5], //healthdecrease
- [NSNumber numberWithInt:5], //cratedrops
- [NSNumber numberWithInt:35], //healthprob
- [NSNumber numberWithInt:25], //healthamount
- [NSNumber numberWithInt:3], //minestime
- [NSNumber numberWithInt:4], //minesnumber
- [NSNumber numberWithInt:0], //dudmines
- [NSNumber numberWithInt:2], //explosives
- nil];
-
- NSMutableArray *gamemodArray= [[NSMutableArray alloc] initWithObjects:
- [NSNumber numberWithBool:NO], //fortmode
- [NSNumber numberWithBool:NO], //divideteam
- [NSNumber numberWithBool:NO], //solidland
- [NSNumber numberWithBool:NO], //addborder
- [NSNumber numberWithBool:NO], //lowgravity
- [NSNumber numberWithBool:NO], //lasersight
- [NSNumber numberWithBool:NO], //invulnerable
- [NSNumber numberWithBool:NO], //resethealth
- [NSNumber numberWithBool:NO], //vampirism
- [NSNumber numberWithBool:NO], //karma
- [NSNumber numberWithBool:NO], //artillery
- [NSNumber numberWithBool:YES], //randomorder
- [NSNumber numberWithBool:NO], //king
- [NSNumber numberWithBool:NO], //placehedgehogs
- [NSNumber numberWithBool:NO], //clansharesammo
- [NSNumber numberWithBool:NO], //disablegirders
- [NSNumber numberWithBool:NO], //disablelandobjects
- [NSNumber numberWithBool:NO], //aisurvival
- [NSNumber numberWithBool:NO], //infattack
- [NSNumber numberWithBool:NO], //resetweaps
- [NSNumber numberWithBool:NO], //perhogammo
- nil];
-
- NSMutableDictionary *theScheme = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
- basicArray,@"basic",
- gamemodArray,@"gamemod",
- nil];
- [gamemodArray release];
- [basicArray release];
-
- NSString *schemeFile = [[NSString alloc] initWithFormat:@"%@/%@.plist", schemesDirectory, nameWithoutExt];
-
- [theScheme writeToFile:schemeFile atomically:YES];
- [schemeFile release];
- [theScheme release];
-}
-
BOOL inline rotationManager (UIInterfaceOrientation interfaceOrientation) {
if (IS_IPAD())
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight) ||
@@ -305,3 +142,47 @@
return theLabel;
}
+
+// this routine checks for the PNG size without loading it in memory
+// https://github.com/steipete/PSFramework/blob/master/PSFramework%20Version%200.3/PhotoshopFramework/PSMetaDataFunctions.m
+CGSize PSPNGSizeFromMetaData (NSString *aFileName) {
+ // File Name to C String.
+ const char *fileName = [aFileName UTF8String];
+ // source file
+ FILE *infile = fopen(fileName, "rb");
+ if (infile == NULL) {
+ DLog(@"Can't open the file: %@", aFileName);
+ return CGSizeZero;
+ }
+
+ // Bytes Buffer.
+ unsigned char buffer[30];
+ // Grab Only First Bytes.
+ fread(buffer, 1, 30, infile);
+ // Close File.
+ fclose(infile);
+
+ // PNG Signature.
+ unsigned char png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
+
+ // Compare File signature.
+ if ((int)(memcmp(&buffer[0], &png_signature[0], 8))) {
+ DLog(@"The file (%@) is not a PNG file", aFileName);
+ return CGSizeZero;
+ }
+
+ // Calc Sizes. Isolate only four bytes of each size (width, height).
+ int width[4];
+ int height[4];
+ for (int d = 16; d < (16 + 4); d++) {
+ width[d-16] = buffer[d];
+ height[d-16] = buffer[d+4];
+ }
+
+ // Convert bytes to Long (Integer)
+ long resultWidth = (width[0] << (int)24) | (width[1] << (int)16) | (width[2] << (int)8) | width[3];
+ long resultHeight = (height[0] << (int)24) | (height[1] << (int)16) | (height[2] << (int)8) | height[3];
+
+ // Return Size.
+ return CGSizeMake(resultWidth,resultHeight);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/HedgewarsMobile/Classes/CreationChamber.h Sat Nov 13 09:36:26 2010 -0500
@@ -0,0 +1,26 @@
+/*
+ * Hedgewars-iOS, a Hedgewars port for iOS devices
+ * Copyright (c) 2009-2010 Vittorio Giovara <vittorio.giovara@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * File created on 12/11/2010.
+ */
+
+
+#import <Foundation/Foundation.h>
+
+void createTeamNamed (NSString *nameWithoutExt);
+void createWeaponNamed (NSString *nameWithoutExt, int type);
+void createSchemeNamed (NSString *nameWithoutExt);
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/HedgewarsMobile/Classes/CreationChamber.m Sat Nov 13 09:36:26 2010 -0500
@@ -0,0 +1,201 @@
+/*
+ * Hedgewars-iOS, a Hedgewars port for iOS devices
+ * Copyright (c) 2009-2010 Vittorio Giovara <vittorio.giovara@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * File created on 12/11/2010.
+ */
+
+
+#import "CreationChamber.h"
+
+void createTeamNamed (NSString *nameWithoutExt) {
+ NSString *teamsDirectory = TEAMS_DIRECTORY();
+
+ if (![[NSFileManager defaultManager] fileExistsAtPath: teamsDirectory]) {
+ [[NSFileManager defaultManager] createDirectoryAtPath:teamsDirectory
+ withIntermediateDirectories:NO
+ attributes:nil
+ error:NULL];
+ }
+
+ NSMutableArray *hedgehogs = [[NSMutableArray alloc] initWithCapacity: HW_getMaxNumberOfHogs()];
+
+ for (int i = 0; i < HW_getMaxNumberOfHogs(); i++) {
+ NSString *hogName = [[NSString alloc] initWithFormat:@"hedgehog %d",i];
+ NSDictionary *hog = [[NSDictionary alloc] initWithObjectsAndKeys:
+ [NSNumber numberWithInt:0],@"level",
+ hogName,@"hogname",
+ @"NoHat",@"hat",
+ nil];
+ [hogName release];
+ [hedgehogs addObject:hog];
+ [hog release];
+ }
+
+ NSDictionary *theTeam = [[NSDictionary alloc] initWithObjectsAndKeys:
+ @"0",@"hash",
+ @"Statue",@"grave",
+ @"Plane",@"fort",
+ @"Default",@"voicepack",
+ @"hedgewars",@"flag",
+ hedgehogs,@"hedgehogs",
+ nil];
+ [hedgehogs release];
+
+ NSString *teamFile = [[NSString alloc] initWithFormat:@"%@/%@.plist", teamsDirectory, nameWithoutExt];
+
+ [theTeam writeToFile:teamFile atomically:YES];
+ [teamFile release];
+ [theTeam release];
+}
+
+void createWeaponNamed (NSString *nameWithoutExt, int type) {
+ NSString *weaponsDirectory = WEAPONS_DIRECTORY();
+
+ if (![[NSFileManager defaultManager] fileExistsAtPath: weaponsDirectory]) {
+ [[NSFileManager defaultManager] createDirectoryAtPath:weaponsDirectory
+ withIntermediateDirectories:NO
+ attributes:nil
+ error:NULL];
+ }
+
+ NSDictionary *theWeapon = nil;
+ switch (type) {
+ case 0: //default
+ theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
+ @"93919294221991210322351110012010000002111101010111",@"ammostore_initialqt",
+ @"04050405416006555465544647765766666661555101011154",@"ammostore_probability",
+ @"00000000000002055000000400070040000000002000000006",@"ammostore_delay",
+ @"13111103121111111231141111111111111112111111011111",@"ammostore_crate", nil];
+ break;
+ case 1: //crazy
+ theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
+ @"99999999999999999929999999999999992999999999099999",@"ammostore_initialqt",
+ @"11111101111111111111111111111111111111111111011111",@"ammostore_probability",
+ @"00000000000000000000000000000000000000000000000000",@"ammostore_delay",
+ @"13111103121111111231141111111111111112111101011111",@"ammostore_crate", nil];
+ break;
+ case 2: //pro mode
+ theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
+ @"90900090000000000000090000000000000000000000000000",@"ammostore_initialqt",
+ @"00000000000000000000000000000000000000000000000000",@"ammostore_probability",
+ @"00000000000002055000000400070040000000002000000000",@"ammostore_delay",
+ @"11111111111111111111111111111111111111111001011111",@"ammostore_crate", nil];
+ break;
+ case 3: //shoppa
+ theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
+ @"00000099000000000000000000000000000000000000000000",@"ammostore_initialqt",
+ @"44444100442444022101121212224220000000020004000100",@"ammostore_probability",
+ @"00000000000000000000000000000000000000000000000000",@"ammostore_delay",
+ @"11111111111111111111111111111111111111111011011111",@"ammostore_crate", nil];
+ break;
+ case 4: //clean slate
+ theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
+ @"10100090000100000110000000000000000000000000000010",@"ammostore_initialqt",
+ @"04050405416006555465544647765766666661555101011154",@"ammostore_probability",
+ @"00000000000002055000000400070040000000002000000000",@"ammostore_delay",
+ @"13111103121111111231141111111111111112111111011111",@"ammostore_crate", nil];
+ break;
+ case 5: //minefield
+ theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
+ @"00000099000900000003000000000000000000000000000000",@"ammostore_initialqt",
+ @"00000000000000000000000000000000000000000000000000",@"ammostore_probability",
+ @"00000000000002055000000400070040000000002000000000",@"ammostore_delay",
+ @"11111111111111111111111111111111111111111111011111",@"ammostore_crate", nil];
+ break;
+ case 6: //thinking with portals
+ theWeapon = [[NSDictionary alloc] initWithObjectsAndKeys:
+ @"90000090020000000021000000000000001100000900000000",@"ammostore_initialqt",
+ @"04050405416006555465544647765766666661555101011154",@"ammostore_probability",
+ @"00000000000002055000000400070040000000002000000006",@"ammostore_delay",
+ @"13111103121111111231141111111111111112111111011111",@"ammostore_crate", nil];
+ break;
+ default:
+ NSLog(@"Nope");
+ break;
+ }
+
+ NSString *weaponFile = [[NSString alloc] initWithFormat:@"%@/%@.plist", weaponsDirectory, nameWithoutExt];
+
+ [theWeapon writeToFile:weaponFile atomically:YES];
+ [weaponFile release];
+ [theWeapon release];
+}
+
+void createSchemeNamed (NSString *nameWithoutExt) {
+ NSString *schemesDirectory = SCHEMES_DIRECTORY();
+
+ if (![[NSFileManager defaultManager] fileExistsAtPath: schemesDirectory]) {
+ [[NSFileManager defaultManager] createDirectoryAtPath:schemesDirectory
+ withIntermediateDirectories:NO
+ attributes:nil
+ error:NULL];
+ }
+
+ NSMutableArray *basicArray = [[NSMutableArray alloc] initWithObjects:
+ [NSNumber numberWithInt:100], //initialhealth
+ [NSNumber numberWithInt:45], //turntime
+ [NSNumber numberWithInt:100], //damagemodifier
+ [NSNumber numberWithInt:15], //suddendeathtimeout
+ [NSNumber numberWithInt:47], //waterrise
+ [NSNumber numberWithInt:5], //healthdecrease
+ [NSNumber numberWithInt:5], //cratedrops
+ [NSNumber numberWithInt:35], //healthprob
+ [NSNumber numberWithInt:25], //healthamount
+ [NSNumber numberWithInt:3], //minestime
+ [NSNumber numberWithInt:4], //minesnumber
+ [NSNumber numberWithInt:0], //dudmines
+ [NSNumber numberWithInt:2], //explosives
+ nil];
+
+ NSMutableArray *gamemodArray= [[NSMutableArray alloc] initWithObjects:
+ [NSNumber numberWithBool:NO], //fortmode
+ [NSNumber numberWithBool:NO], //divideteam
+ [NSNumber numberWithBool:NO], //solidland
+ [NSNumber numberWithBool:NO], //addborder
+ [NSNumber numberWithBool:NO], //lowgravity
+ [NSNumber numberWithBool:NO], //lasersight
+ [NSNumber numberWithBool:NO], //invulnerable
+ [NSNumber numberWithBool:NO], //resethealth
+ [NSNumber numberWithBool:NO], //vampirism
+ [NSNumber numberWithBool:NO], //karma
+ [NSNumber numberWithBool:NO], //artillery
+ [NSNumber numberWithBool:YES], //randomorder
+ [NSNumber numberWithBool:NO], //king
+ [NSNumber numberWithBool:NO], //placehedgehogs
+ [NSNumber numberWithBool:NO], //clansharesammo
+ [NSNumber numberWithBool:NO], //disablegirders
+ [NSNumber numberWithBool:NO], //disablelandobjects
+ [NSNumber numberWithBool:NO], //aisurvival
+ [NSNumber numberWithBool:NO], //infattack
+ [NSNumber numberWithBool:NO], //resetweaps
+ [NSNumber numberWithBool:NO], //perhogammo
+ [NSNumber numberWithBool:NO], //nowind
+ nil];
+
+ NSMutableDictionary *theScheme = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
+ basicArray,@"basic",
+ gamemodArray,@"gamemod",
+ nil];
+ [gamemodArray release];
+ [basicArray release];
+
+ NSString *schemeFile = [[NSString alloc] initWithFormat:@"%@/%@.plist", schemesDirectory, nameWithoutExt];
+
+ [theScheme writeToFile:schemeFile atomically:YES];
+ [schemeFile release];
+ [theScheme release];
+}
--- a/project_files/HedgewarsMobile/Classes/EditableCellView.m Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Classes/EditableCellView.m Sat Nov 13 09:36:26 2010 -0500
@@ -37,6 +37,7 @@
textField.returnKeyType = UIReturnKeyDone;
textField.adjustsFontSizeToFitWidth = YES;
textField.userInteractionEnabled = YES;
+ textField.font = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]];
[textField addTarget:self action:@selector(save:) forControlEvents:UIControlEventEditingDidEndOnExit];
[self.contentView addSubview:textField];
@@ -76,8 +77,6 @@
skew +=2;
}
- // sometimes the bold property gets lost
- textField.font = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]];
textField.frame = CGRectMake(boundsX+offset+10, skew+10, 300, [UIFont labelFontSize] + 4);
}
--- a/project_files/HedgewarsMobile/Classes/LevelViewController.m Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Classes/LevelViewController.m Sat Nov 13 09:36:26 2010 -0500
@@ -144,6 +144,7 @@
[self.tableView deleteSections:sections withRowAnimation:UITableViewRowAnimationFade];
level = 0;
}
+ [sections release];
DLog(@"New level is %d",level);
for (NSMutableDictionary *hog in hogs)
@@ -151,8 +152,6 @@
[self.tableView reloadData];
[[NSNotificationCenter defaultCenter] postNotificationName:@"setWriteNeedTeams" object:nil];
-
- [sections release];
}
@@ -166,8 +165,10 @@
if (newRow != oldRow) {
NSMutableArray *hogs = [self.teamDictionary objectForKey:@"hedgehogs"];
+ NSInteger level = newRow + 1;
for (NSMutableDictionary *hog in hogs)
- [hog setObject:[NSNumber numberWithInt:newRow+1] forKey:@"level"];
+ [hog setObject:[NSNumber numberWithInt:level] forKey:@"level"];
+ DLog(@"New level is %d",level);
// tell our boss to write this new stuff on disk
[[NSNotificationCenter defaultCenter] postNotificationName:@"setWriteNeedTeams" object:nil];
--- a/project_files/HedgewarsMobile/Classes/MainMenuViewController.m Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Classes/MainMenuViewController.m Sat Nov 13 09:36:26 2010 -0500
@@ -20,7 +20,7 @@
#import "MainMenuViewController.h"
-#import "CommodityFunctions.h"
+#import "CreationChamber.h"
#import "SDL_uikitappdelegate.h"
#import "PascalImports.h"
#import "GameConfigViewController.h"
@@ -35,95 +35,81 @@
return rotationManager(interfaceOrientation);
}
-// using a different thread for audio 'cos it's slow
--(void) initAudioThread {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- // do somthing in the future
- [pool release];
-}
-
// check if some configuration files are already set; if they are present it means that the current copy must be updated
-(void) createNecessaryFiles {
- NSError *err = nil;
- NSString *directoryToCheck, *fileToCheck, *fileToUpdate;
- NSString *resDir = [[NSBundle mainBundle] resourcePath];
+ NSString *sourceFile, *destinationFile;
+ NSString *resourcesDir = [[NSBundle mainBundle] resourcePath];
DLog(@"Creating necessary files");
- // create an empty saves directory by deleting the previous one (saves are incompatible between releases)
+ // SAVES - just delete and overwrite
if ([[NSFileManager defaultManager] fileExistsAtPath:SAVES_DIRECTORY()])
[[NSFileManager defaultManager] removeItemAtPath:SAVES_DIRECTORY() error:NULL];
[[NSFileManager defaultManager] createDirectoryAtPath:SAVES_DIRECTORY() withIntermediateDirectories:NO attributes:nil error:NULL];
- // if the settings file is already present, we merge current preferences with the update
- fileToCheck = [NSString stringWithFormat:@"%@/Settings/settings.plist",resDir];
+ // SETTINGS FILE - merge when present
+ NSString *baseSettingsFile = [[NSString alloc] initWithFormat:@"%@/Settings/settings.plist",resourcesDir];
if ([[NSFileManager defaultManager] fileExistsAtPath:SETTINGS_FILE()]) {
NSDictionary *settings = [[NSDictionary alloc] initWithContentsOfFile:SETTINGS_FILE()];
- NSMutableDictionary *update = [[NSMutableDictionary alloc] initWithContentsOfFile:fileToCheck];
+ NSMutableDictionary *update = [[NSMutableDictionary alloc] initWithContentsOfFile:baseSettingsFile];
+ // the order of what adds what is important
[update addEntriesFromDictionary:settings];
[settings release];
[update writeToFile:SETTINGS_FILE() atomically:YES];
[update release];
} else
- [[NSFileManager defaultManager] copyItemAtPath:fileToCheck toPath:SETTINGS_FILE() error:&err];
-
- // TODO: scrap this and always copy the bundled files; update exisising ones in some way
- // if the teams are already present we merge the old teams, else we copy new teams
- directoryToCheck = [NSString stringWithFormat:@"%@/Settings/Teams",resDir];
- if ([[NSFileManager defaultManager] fileExistsAtPath:TEAMS_DIRECTORY()]) {
- for (NSString *str in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryToCheck error:&err]) {
- fileToCheck = [NSString stringWithFormat:@"%@/%@",TEAMS_DIRECTORY(),str];
- fileToUpdate = [NSString stringWithFormat:@"%@/Settings/Teams/%@",resDir,str];
- if ([[NSFileManager defaultManager] fileExistsAtPath:fileToCheck]) {
- NSDictionary *team = [[NSDictionary alloc] initWithContentsOfFile:fileToCheck];
- NSMutableDictionary *update = [[NSMutableDictionary alloc] initWithContentsOfFile:fileToUpdate];
- [update addEntriesFromDictionary:team];
- [team release];
- [update writeToFile:fileToCheck atomically:YES];
- [update release];
- } else
- [[NSFileManager defaultManager] copyItemAtPath:fileToUpdate toPath:fileToCheck error:&err];
+ [[NSFileManager defaultManager] copyItemAtPath:baseSettingsFile toPath:SETTINGS_FILE() error:NULL];
+ [baseSettingsFile release];
+
+ // TEAMS - update exisiting teams with new format
+ if ([[NSFileManager defaultManager] fileExistsAtPath:TEAMS_DIRECTORY()] == NO) {
+ [[NSFileManager defaultManager] createDirectoryAtPath:TEAMS_DIRECTORY()
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:NULL];
+ // we copy teams only the first time because it's unlikely that newer ones are going to be added
+ NSString *baseTeamsDir = [[NSString alloc] initWithFormat:@"%@/Settings/Teams/",resourcesDir];
+ for (NSString *str in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:baseTeamsDir error:NULL]) {
+ sourceFile = [baseTeamsDir stringByAppendingString:str];
+ destinationFile = [TEAMS_DIRECTORY() stringByAppendingString:str];
+ [[NSFileManager defaultManager] removeItemAtPath:destinationFile error:NULL];
+ [[NSFileManager defaultManager] copyItemAtPath:sourceFile toPath:destinationFile error:NULL];
}
- } else
- [[NSFileManager defaultManager] copyItemAtPath:directoryToCheck toPath:TEAMS_DIRECTORY() error:&err];
-
- // TODO: scrap this and always copy the bundled files; update exisising ones in some way
- // the same holds for schemes (but they're dictionaries containing arrays)
- directoryToCheck = [NSString stringWithFormat:@"%@/Settings/Schemes",resDir];
- if ([[NSFileManager defaultManager] fileExistsAtPath:SCHEMES_DIRECTORY()]) {
- for (NSString *str in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryToCheck error:nil]) {
- fileToCheck = [NSString stringWithFormat:@"%@/%@",SCHEMES_DIRECTORY(),str];
- fileToUpdate = [NSString stringWithFormat:@"%@/Settings/Schemes/%@",resDir,str];
- if ([[NSFileManager defaultManager] fileExistsAtPath:fileToCheck]) {
- NSDictionary *scheme = [[NSDictionary alloc] initWithContentsOfFile:fileToCheck];
- NSDictionary *update = [[NSDictionary alloc] initWithContentsOfFile:fileToUpdate];
- if ([[update objectForKey:@"basic"] count] > [[scheme objectForKey:@"basic"] count] ||
- [[update objectForKey:@"gamemod"] count] > [[scheme objectForKey:@"gamemod"] count])
- [update writeToFile:fileToCheck atomically:YES];
- [update release];
- [scheme release];
- } else
- [[NSFileManager defaultManager] copyItemAtPath:fileToUpdate toPath:fileToCheck error:&err];
- }
- } else
- [[NSFileManager defaultManager] copyItemAtPath:directoryToCheck toPath:SCHEMES_DIRECTORY() error:&err];
-
- // weapons are autoupdated at runtime but it's better to update then every new version
+ [baseTeamsDir release];
+ }
+ // TODO: is merge needed?
+
+ // SCHEMES - update old stuff and add new stuff
+ if ([[NSFileManager defaultManager] fileExistsAtPath:SCHEMES_DIRECTORY()] == NO)
+ [[NSFileManager defaultManager] createDirectoryAtPath:SCHEMES_DIRECTORY()
+ withIntermediateDirectories:YES
+ attributes:nil
+ error:NULL];
+ // TODO: do the merge if necessary
+ // we overwrite the default ones because it is likely that new modes are added every release
+ NSString *baseSchemesDir = [[NSString alloc] initWithFormat:@"%@/Settings/Schemes/",resourcesDir];
+ for (NSString *str in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:baseSchemesDir error:NULL]) {
+ sourceFile = [baseSchemesDir stringByAppendingString:str];
+ destinationFile = [SCHEMES_DIRECTORY() stringByAppendingString:str];
+ [[NSFileManager defaultManager] removeItemAtPath:destinationFile error:NULL];
+ [[NSFileManager defaultManager] copyItemAtPath:sourceFile toPath:destinationFile error:NULL];
+ }
+ [baseSchemesDir release];
+
+ // WEAPONS - always overwrite
if ([[NSFileManager defaultManager] fileExistsAtPath:WEAPONS_DIRECTORY()] == NO)
[[NSFileManager defaultManager] createDirectoryAtPath:WEAPONS_DIRECTORY()
withIntermediateDirectories:YES
attributes:nil
- error:&err];
+ error:NULL];
createWeaponNamed(@"Default", 0);
createWeaponNamed(@"Crazy", 1);
- createWeaponNamed(@"Pro mode", 2);
+ createWeaponNamed(@"Pro Mode", 2);
createWeaponNamed(@"Shoppa", 3);
- createWeaponNamed(@"Clean slate", 4);
+ createWeaponNamed(@"Clean Slate", 4);
createWeaponNamed(@"Minefield", 5);
+ createWeaponNamed(@"Thinking with Portals", 6);
- if (err != nil)
- DLog(@"%@", err);
- else
- DLog(@"Success");
+ DLog(@"Success");
}
#pragma mark -
--- a/project_files/HedgewarsMobile/Classes/MapConfigViewController.m Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Classes/MapConfigViewController.m Sat Nov 13 09:36:26 2010 -0500
@@ -72,7 +72,7 @@
// perform as if user clicked on an entry
[self tableView:self.tableView didSelectRowAtIndexPath:theIndex];
if (IS_NOT_POWERFUL() == NO)
- [self.tableView scrollToRowAtIndexPath:theIndex atScrollPosition:UITableViewScrollPositionNone animated:YES];
+ [self.tableView scrollToRowAtIndexPath:theIndex atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
-(void) turnOffWidgets {
@@ -350,19 +350,6 @@
self.staticMapCommand = staticmap;
self.missionCommand = mission;
- // nice animation for updating the table when appropriate (on iphone)
- /*
- if (IS_IPAD() == NO)
- if (((oldPage == 0 || oldPage == 2) && (newPage == 1 || newPage == 3)) ||
- ((oldPage == 1 || oldPage == 3) && (newPage == 0 || newPage == 2)) ||
- ((oldPage == 1 && newPage == 3) || (oldPage == 3 || newPage == 1))) {
- self.tableView.frame = CGRectMake(480, 0, 185, 276);
- [UIView beginAnimations:@"moving in table" context:NULL];
- self.tableView.frame = CGRectMake(295, 0, 185, 276);
- [UIView commitAnimations];
- }
- */
-
[self.tableView reloadData];
[self updatePreview];
oldPage = newPage;
@@ -385,13 +372,36 @@
[string release];
// remove a trailing "" element
[themeArray removeLastObject];
- NSArray *mapArray = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:MAPS_DIRECTORY() error:NULL];
- NSArray *missionArray = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:MISSIONS_DIRECTORY() error:NULL];
+ NSArray *mapArrayFull = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:MAPS_DIRECTORY() error:NULL];
+ NSMutableArray *mapArray = [[NSMutableArray alloc] init];
+ for (NSString *str in mapArrayFull) {
+ CGSize imgSize = PSPNGSizeFromMetaData([MAPS_DIRECTORY() stringByAppendingFormat:@"%@/map.png",str]);
+ //DLog(@"%@ %f %f", str, imgSize.width, imgSize.height);
+ if (IS_NOT_POWERFUL() && imgSize.height > 1024.0f)
+ continue;
+ if (IS_IPAD() && imgSize.height > 1280.0f)
+ continue;
+ [mapArray addObject:str];
+ }
+ NSArray *missionArrayFull = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:MISSIONS_DIRECTORY() error:NULL];
+ NSMutableArray *missionArray = [[NSMutableArray alloc] init];
+ for (NSString *str in missionArrayFull) {
+ CGSize imgSize = PSPNGSizeFromMetaData([MISSIONS_DIRECTORY() stringByAppendingFormat:@"%@/map.png",str]);
+ //DLog(@"%@ %f %f", str, imgSize.width, imgSize.height);
+ if (IS_NOT_POWERFUL() && imgSize.height > 1024.0f)
+ continue;
+ if (IS_IPAD() && imgSize.height > 1280.0f)
+ continue;
+ [missionArray addObject:str];
+ }
NSArray *array = [[NSArray alloc] initWithObjects:themeArray,mapArray,themeArray,missionArray,nil];
+ [missionArray release];
+ [themeArray release];
+ [mapArray release];
+
self.dataSourceArray = array;
[array release];
- [themeArray release];
}
-(void) viewDidLoad {
@@ -469,11 +479,9 @@
-(void) didReceiveMemoryWarning {
self.dataSourceArray = nil;
- self.previewButton = nil;
self.tableView = nil;
self.maxLabel = nil;
self.sizeLabel = nil;
- self.segmentedControl = nil;
self.slider = nil;
MSG_MEMCLEAN();
--- a/project_files/HedgewarsMobile/Classes/SchemeSettingsViewController.m Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Classes/SchemeSettingsViewController.m Sat Nov 13 09:36:26 2010 -0500
@@ -20,7 +20,7 @@
#import "SchemeSettingsViewController.h"
-#import "CommodityFunctions.h"
+#import "CreationChamber.h"
#import "SingleSchemeViewController.h"
@implementation SchemeSettingsViewController
@@ -81,12 +81,14 @@
createSchemeNamed([fileName stringByDeletingPathExtension]);
[self.listOfSchemes addObject:fileName];
- [fileName release];
// order the array alphabetically, so schemes will keep their position
[self.listOfSchemes sortUsingSelector:@selector(compare:)];
+ [self.tableView reloadData];
- [self.tableView reloadData];
+ NSInteger index = [self.listOfSchemes indexOfObject:fileName];
+ [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
+ [fileName release];
}
#pragma mark -
--- a/project_files/HedgewarsMobile/Classes/SchemeWeaponConfigViewController.h Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Classes/SchemeWeaponConfigViewController.h Sat Nov 13 09:36:26 2010 -0500
@@ -31,13 +31,16 @@
NSString *selectedScheme;
NSString *selectedWeapon;
+
+ UISwitch *syncSwitch;
}
-@property (nonatomic, retain) NSArray *listOfSchemes;
-@property (nonatomic, retain) NSArray *listOfWeapons;
+@property (nonatomic,retain) NSArray *listOfSchemes;
+@property (nonatomic,retain) NSArray *listOfWeapons;
@property (nonatomic,retain) NSIndexPath *lastIndexPath_sc;
@property (nonatomic,retain) NSIndexPath *lastIndexPath_we;
@property (nonatomic,retain) NSString *selectedScheme;
@property (nonatomic,retain) NSString *selectedWeapon;
+@property (nonatomic,retain) UISwitch *syncSwitch;
@end
--- a/project_files/HedgewarsMobile/Classes/SchemeWeaponConfigViewController.m Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Classes/SchemeWeaponConfigViewController.m Sat Nov 13 09:36:26 2010 -0500
@@ -24,7 +24,7 @@
#import "SDL_uikitappdelegate.h"
@implementation SchemeWeaponConfigViewController
-@synthesize listOfSchemes, listOfWeapons, lastIndexPath_sc, lastIndexPath_we, selectedScheme, selectedWeapon;
+@synthesize listOfSchemes, listOfWeapons, lastIndexPath_sc, lastIndexPath_we, selectedScheme, selectedWeapon, syncSwitch;
-(BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return rotationManager(interfaceOrientation);
@@ -81,27 +81,30 @@
#pragma mark -
#pragma mark Table view data source
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView {
- return 2;
+ return 3;
}
-(NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (section == 0)
return [self.listOfSchemes count];
+ else if (section == 1)
+ return [self.listOfWeapons count];
else
- return [self.listOfWeapons count];
+ return 1;
}
// Customize the appearance of table view cells.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"Cell";
NSInteger row = [indexPath row];
+ NSInteger section = [indexPath section];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier] autorelease];
cell.accessoryView = nil;
- if ([indexPath section] == 0) {
+ if (0 == section) {
cell.textLabel.text = [[self.listOfSchemes objectAtIndex:row] stringByDeletingPathExtension];
NSString *str = [NSString stringWithFormat:@"%@/%@",SCHEMES_DIRECTORY(),[self.listOfSchemes objectAtIndex:row]];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:str];
@@ -113,7 +116,7 @@
[checkbox release];
self.lastIndexPath_sc = indexPath;
}
- } else {
+ } else if (1 == section) {
cell.textLabel.text = [[self.listOfWeapons objectAtIndex:row] stringByDeletingPathExtension];
NSString *str = [NSString stringWithFormat:@"%@/%@",WEAPONS_DIRECTORY(),[self.listOfWeapons objectAtIndex:row]];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:str];
@@ -125,8 +128,19 @@
[checkbox release];
self.lastIndexPath_we = indexPath;
}
+ } else {
+ if (self.syncSwitch == nil) {
+ UISwitch *theSwitch = [[UISwitch alloc] init];
+ [theSwitch setOn:YES];
+ self.syncSwitch = theSwitch;
+ [theSwitch release];
+ }
+ cell.textLabel.text = IS_IPAD() ? NSLocalizedString(@"Sync Schemes",@"") : NSLocalizedString(@"Sync Schemes and Weapons",@"");
+ cell.detailTextLabel.text = IS_IPAD() ? nil : NSLocalizedString(@"Choosing a Scheme will select its associated Weapon",@"");
+ cell.detailTextLabel.adjustsFontSizeToFitWidth = YES;
+ cell.accessoryView = self.syncSwitch;
}
-
+
cell.backgroundColor = [UIColor blackColor];
cell.textLabel.textColor = UICOLOR_HW_YELLOW_TEXT;
cell.detailTextLabel.textColor = [UIColor whiteColor];
@@ -142,8 +156,11 @@
NSString *text;
if (section == 0)
text = NSLocalizedString(@"Schemes",@"");
+ else if (section == 1)
+ text = NSLocalizedString(@"Weapons",@"");
else
- text = NSLocalizedString(@"Weapons",@"");
+ text = NSLocalizedString(@"Options",@"");
+
UILabel *theLabel = createBlueLabel(text, frame);
theLabel.center = CGPointMake(self.view.frame.size.width/2, 20);
@@ -177,6 +194,17 @@
if ([indexPath section] == 0) {
self.lastIndexPath_sc = indexPath;
self.selectedScheme = [self.listOfSchemes objectAtIndex:newRow];
+ if (self.syncSwitch.on) {
+ for (NSString *str in self.listOfWeapons) {
+ if ([str isEqualToString:self.selectedScheme]) {
+ int index = [self.listOfSchemes indexOfObject:str];
+ self.selectedWeapon = str;
+ self.lastIndexPath_we = [NSIndexPath indexPathForRow:index inSection:1];
+ [self.tableView reloadData];
+ break;
+ }
+ }
+ }
} else {
self.lastIndexPath_we = indexPath;
self.selectedWeapon = [self.listOfWeapons objectAtIndex:newRow];
@@ -195,6 +223,7 @@
self.lastIndexPath_we = nil;
self.listOfSchemes = nil;
self.listOfWeapons = nil;
+ self.syncSwitch = nil;
MSG_MEMCLEAN();
}
[super didReceiveMemoryWarning];
@@ -207,6 +236,7 @@
self.lastIndexPath_we = nil;
self.selectedScheme = nil;
self.selectedWeapon = nil;
+ self.syncSwitch = nil;
MSG_DIDUNLOAD();
[super viewDidUnload];
}
@@ -219,6 +249,7 @@
[lastIndexPath_we release];
[selectedScheme release];
[selectedWeapon release];
+ [syncSwitch release];
[super dealloc];
}
--- a/project_files/HedgewarsMobile/Classes/SingleSchemeViewController.m Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Classes/SingleSchemeViewController.m Sat Nov 13 09:36:26 2010 -0500
@@ -153,6 +153,7 @@
if (row == 0) {
editableCell.textField.text = self.schemeName;
+ editableCell.textField.font = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]];
} else {
editableCell.minimumCharacters = 0;
editableCell.textField.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];
--- a/project_files/HedgewarsMobile/Classes/SingleWeaponViewController.m Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Classes/SingleWeaponViewController.m Sat Nov 13 09:36:26 2010 -0500
@@ -150,6 +150,7 @@
if (row == 0) {
editableCell.textField.text = self.weaponName;
+ editableCell.textField.font = [UIFont boldSystemFontOfSize:[UIFont labelFontSize]];
} else {
editableCell.minimumCharacters = 0;
editableCell.textField.font = [UIFont systemFontOfSize:[UIFont labelFontSize]];
--- a/project_files/HedgewarsMobile/Classes/TeamConfigViewController.m Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Classes/TeamConfigViewController.m Sat Nov 13 09:36:26 2010 -0500
@@ -125,7 +125,12 @@
[cell addSubview:squareButton];
[squareButton release];
- UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(12+88+6+36, 10, 103, 25)];
+ NSInteger length;
+ if (IS_IPAD())
+ length = 103;
+ else
+ length = 285;
+ UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(12+88+6+36, 10, length, 25)];
label.textAlignment = UITextAlignmentLeft;
label.minimumFontSize = 11;
label.adjustsFontSizeToFitWidth = YES;
--- a/project_files/HedgewarsMobile/Classes/TeamSettingsViewController.m Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Classes/TeamSettingsViewController.m Sat Nov 13 09:36:26 2010 -0500
@@ -20,8 +20,8 @@
#import "TeamSettingsViewController.h"
+#import "CreationChamber.h"
#import "SingleTeamViewController.h"
-#import "CommodityFunctions.h"
@implementation TeamSettingsViewController
@synthesize listOfTeams;
@@ -85,12 +85,14 @@
createTeamNamed([fileName stringByDeletingPathExtension]);
[self.listOfTeams addObject:fileName];
- [fileName release];
// order the array alphabetically, so teams will keep their position
[self.listOfTeams sortUsingSelector:@selector(compare:)];
+ [self.tableView reloadData];
- [self.tableView reloadData];
+ NSInteger index = [self.listOfTeams indexOfObject:fileName];
+ [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
+ [fileName release];
}
#pragma mark -
--- a/project_files/HedgewarsMobile/Classes/WeaponSettingsViewController.m Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Classes/WeaponSettingsViewController.m Sat Nov 13 09:36:26 2010 -0500
@@ -20,7 +20,7 @@
#import "WeaponSettingsViewController.h"
-#import "CommodityFunctions.h"
+#import "CreationChamber.h"
#import "SingleWeaponViewController.h"
@implementation WeaponSettingsViewController
@@ -82,12 +82,14 @@
createWeaponNamed([fileName stringByDeletingPathExtension], 0);
[self.listOfWeapons addObject:fileName];
- [fileName release];
// order the array alphabetically, so schemes will keep their position
[self.listOfWeapons sortUsingSelector:@selector(compare:)];
+ [self.tableView reloadData];
- [self.tableView reloadData];
+ NSInteger index = [self.listOfWeapons indexOfObject:fileName];
+ [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
+ [fileName release];
}
#pragma mark -
--- a/project_files/HedgewarsMobile/Hedgewars.xcodeproj/project.pbxproj Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Hedgewars.xcodeproj/project.pbxproj Sat Nov 13 09:36:26 2010 -0500
@@ -25,6 +25,7 @@
1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
28FD15000DC6FC520079059D /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28FD14FF0DC6FC520079059D /* OpenGLES.framework */; };
28FD15080DC6FC5B0079059D /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28FD15070DC6FC5B0079059D /* QuartzCore.framework */; settings = {ATTRIBUTES = (Required, ); }; };
+ 61006F95128DE31F00EBA7F7 /* CreationChamber.m in Sources */ = {isa = PBXBuildFile; fileRef = 61006F94128DE31F00EBA7F7 /* CreationChamber.m */; };
610D5FB21270E2660033333A /* Icon-Small@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 61F7A43411E290650040BA66 /* Icon-Small@2x.png */; };
610D5FB31270E26C0033333A /* Icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 61F7A43611E290650040BA66 /* Icon@2x.png */; };
611D9BFB12497E9800008271 /* SavedGamesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 611D9BF912497E9800008271 /* SavedGamesViewController.m */; };
@@ -730,6 +731,8 @@
28FD14FF0DC6FC520079059D /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; };
28FD15070DC6FC5B0079059D /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
32CA4F630368D1EE00C91783 /* Hedgewars_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Hedgewars_Prefix.pch; sourceTree = "<group>"; };
+ 61006F93128DE31F00EBA7F7 /* CreationChamber.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CreationChamber.h; path = Classes/CreationChamber.h; sourceTree = "<group>"; };
+ 61006F94128DE31F00EBA7F7 /* CreationChamber.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CreationChamber.m; path = Classes/CreationChamber.m; sourceTree = "<group>"; };
611D9BF812497E9800008271 /* SavedGamesViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SavedGamesViewController.h; sourceTree = "<group>"; };
611D9BF912497E9800008271 /* SavedGamesViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SavedGamesViewController.m; sourceTree = "<group>"; };
611D9BFA12497E9800008271 /* SavedGamesViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = SavedGamesViewController.xib; path = ../Resources/SavedGamesViewController.xib; sourceTree = "<group>"; };
@@ -1023,6 +1026,8 @@
6165922D11CA9BD500D6E256 /* UIImageExtra.m */,
61D2059F127CDD1100ABD83E /* ObjcExports.h */,
61D205A0127CDD1100ABD83E /* ObjcExports.m */,
+ 61006F93128DE31F00EBA7F7 /* CreationChamber.h */,
+ 61006F94128DE31F00EBA7F7 /* CreationChamber.m */,
);
name = "Other Sources";
sourceTree = "<group>";
@@ -2284,6 +2289,7 @@
61DE8F221257EB1100B80214 /* AmmoMenuViewController.m in Sources */,
61399013125D19C0003C2DC0 /* uMobile.pas in Sources */,
61D205A1127CDD1100ABD83E /* ObjcExports.m in Sources */,
+ 61006F95128DE31F00EBA7F7 /* CreationChamber.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
--- a/project_files/HedgewarsMobile/Resources/Settings/Schemes/Barrel Mayhem.plist Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Barrel Mayhem.plist Sat Nov 13 09:36:26 2010 -0500
@@ -41,6 +41,7 @@
<false/>
<false/>
<false/>
+ <false/>
</array>
</dict>
</plist>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Clean Slate.plist Sat Nov 13 09:36:26 2010 -0500
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>basic</key>
+ <array>
+ <integer>100</integer>
+ <integer>45</integer>
+ <integer>100</integer>
+ <integer>15</integer>
+ <integer>47</integer>
+ <integer>5</integer>
+ <integer>5</integer>
+ <integer>35</integer>
+ <integer>25</integer>
+ <integer>3</integer>
+ <integer>4</integer>
+ <integer>0</integer>
+ <integer>2</integer>
+ </array>
+ <key>gamemod</key>
+ <array>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <true/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <true/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <true/>
+ <true/>
+ <false/>
+ <false/>
+ </array>
+</dict>
+</plist>
--- a/project_files/HedgewarsMobile/Resources/Settings/Schemes/Default.plist Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Default.plist Sat Nov 13 09:36:26 2010 -0500
@@ -41,6 +41,7 @@
<false/>
<false/>
<false/>
+ <false/>
</array>
</dict>
</plist>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Fort Mode.plist Sat Nov 13 09:36:26 2010 -0500
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>basic</key>
+ <array>
+ <integer>100</integer>
+ <integer>45</integer>
+ <integer>100</integer>
+ <integer>15</integer>
+ <integer>47</integer>
+ <integer>5</integer>
+ <integer>5</integer>
+ <integer>35</integer>
+ <integer>25</integer>
+ <integer>3</integer>
+ <integer>0</integer>
+ <integer>0</integer>
+ <integer>0</integer>
+ </array>
+ <key>gamemod</key>
+ <array>
+ <false/>
+ <false/>
+ <true/>
+ <true/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <true/>
+ <true/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ </array>
+</dict>
+</plist>
--- a/project_files/HedgewarsMobile/Resources/Settings/Schemes/Minefield.plist Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Minefield.plist Sat Nov 13 09:36:26 2010 -0500
@@ -41,6 +41,7 @@
<false/>
<false/>
<false/>
+ <false/>
</array>
</dict>
</plist>
--- a/project_files/HedgewarsMobile/Resources/Settings/Schemes/Pro Mode.plist Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Pro Mode.plist Sat Nov 13 09:36:26 2010 -0500
@@ -41,6 +41,7 @@
<false/>
<false/>
<false/>
+ <false/>
</array>
</dict>
</plist>
--- a/project_files/HedgewarsMobile/Resources/Settings/Schemes/Shoppa.plist Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Shoppa.plist Sat Nov 13 09:36:26 2010 -0500
@@ -41,6 +41,7 @@
<false/>
<false/>
<false/>
+ <false/>
</array>
</dict>
</plist>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Thinking with Portals.plist Sat Nov 13 09:36:26 2010 -0500
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>basic</key>
+ <array>
+ <integer>100</integer>
+ <integer>45</integer>
+ <integer>100</integer>
+ <integer>15</integer>
+ <integer>47</integer>
+ <integer>5</integer>
+ <integer>2</integer>
+ <integer>25</integer>
+ <integer>25</integer>
+ <integer>4</integer>
+ <integer>5</integer>
+ <integer>0</integer>
+ <integer>5</integer>
+ </array>
+ <key>gamemod</key>
+ <array>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <true/>
+ <true/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ <false/>
+ </array>
+</dict>
+</plist>
--- a/project_files/HedgewarsMobile/Resources/Settings/Schemes/Timeless.plist Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Timeless.plist Sat Nov 13 09:36:26 2010 -0500
@@ -41,6 +41,7 @@
<false/>
<false/>
<true/>
+ <false/>
</array>
</dict>
</plist>
--- a/project_files/HedgewarsMobile/Resources/Settings/Schemes/Tunnel Hogs.plist Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Resources/Settings/Schemes/Tunnel Hogs.plist Sat Nov 13 09:36:26 2010 -0500
@@ -41,6 +41,7 @@
<false/>
<false/>
<false/>
+ <false/>
</array>
</dict>
</plist>
--- a/project_files/HedgewarsMobile/Resources/Settings/iFrontend/gameFlags_en.plist Sat Nov 13 17:14:51 2010 +0300
+++ b/project_files/HedgewarsMobile/Resources/Settings/iFrontend/gameFlags_en.plist Sat Nov 13 09:36:26 2010 -0500
@@ -170,5 +170,13 @@
<key>title</key>
<string>Per Hedgehog Ammo</string>
</dict>
+ <dict>
+ <key>description</key>
+ <string>Wind will not affect weapons</string>
+ <key>image</key>
+ <string>NoWind</string>
+ <key>title</key>
+ <string>Disable Wind</string>
+ </dict>
</array>
</plist>
Binary file share/hedgewars/Data/Graphics/AirDrill.png has changed
--- a/share/hedgewars/Data/Locale/en.txt Sat Nov 13 17:14:51 2010 +0300
+++ b/share/hedgewars/Data/Locale/en.txt Sat Nov 13 09:36:26 2010 -0500
@@ -50,6 +50,7 @@
00:47=Sticky Mine
00:48=Hammer
00:49=Resurrector
+00:50=Drill Strike
01:00=Let's fight!
01:01=Round draw
@@ -431,6 +432,7 @@
03:47=Stick these somewhere useful!
03:48=It's Hammer time!
03:49=Does what you guess
+03:50=Moles fan
; Weapon Descriptions (use | as line breaks)
04:00=Attack your enemies using a simple grenade.|It will explode once its timer reaches zero.|1-5: Set grenade's timer|Attack: Hold to throw with more power