summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--.gitlab-ci.yml78
-rw-r--r--.gitmodules2
-rw-r--r--CMakeLists.txt188
-rw-r--r--COPYING2
-rw-r--r--Makefile.in88
-rw-r--r--README.md4
-rwxr-xr-xconfigure10
m---------deps/attoconf0
-rw-r--r--generate.mk (renamed from generate.make)0
-rw-r--r--src/char/char.cpp2
-rw-r--r--src/conf/install.hpp.in (renamed from src/conf/install.hpp)7
-rw-r--r--src/conf/version.hpp.in (renamed from src/conf/version.hpp)21
-rw-r--r--src/main-gdb-head.py2
-rw-r--r--src/map/battle.cpp4
-rw-r--r--src/map/clif.cpp14
-rw-r--r--src/map/map.cpp17
-rw-r--r--src/map/map.hpp3
-rw-r--r--src/map/mob.cpp173
-rw-r--r--src/map/npc-parse.cpp7
-rw-r--r--src/map/npc.cpp3
-rw-r--r--src/map/pc.cpp39
-rw-r--r--src/map/script-fun.cpp339
-rw-r--r--src/map/script-parse.py2
-rw-r--r--src/map/skill.cpp14
-rw-r--r--src/map/storage.cpp3
-rw-r--r--src/mmo/clif.t.hpp2
-rw-r--r--src/mmo/skill.t.hpp8
-rw-r--r--src/mmo/version.cpp2
-rw-r--r--src/shared/lib.cpp30
-rwxr-xr-xtools/bs-align2
-rwxr-xr-xtools/colorize4
-rwxr-xr-xtools/config.py10
-rwxr-xr-xtools/debug-debug-scripts9
-rw-r--r--tools/debug-debug.gdb6
-rwxr-xr-xtools/indenter7
-rwxr-xr-xtools/pp-indent2
-rwxr-xr-xtools/protocol.py128
-rw-r--r--version.mk (renamed from version.make)31
39 files changed, 851 insertions, 418 deletions
diff --git a/.gitignore b/.gitignore
index d28b05e..34afd74 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,10 +9,12 @@
/src/proto2/
/src/debug-debug/
/src/*/*_conf.[ch]pp
+/src/conf/version.hpp
# tags file
/tags
# generated by configure
/Makefile
+/src/conf/install.hpp
/config.status
/lib
# generated by python
@@ -32,3 +34,7 @@
# generic build
/build/
+
+# CI build
+/build-atto/
+/build-cmake/
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f7c750a..1cc7c15 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,9 +5,8 @@ stages:
variables: &base_vars
DEBIAN_COMMON_PACKAGES: make git gcc g++
- # Depth of clone. If no tag is made after this many commits, then
- # the git describe call and version header generation will fail.
- GIT_DEPTH: 100 # Will break again eventually.
+ GIT_DEPTH: 0 # avoid shallow clone since version is based on latest tag
+ GIT_SUBMODULE_STRATEGY: normal
.prerequisites: &prerequisites
before_script:
@@ -15,21 +14,21 @@ variables: &base_vars
- apt-get update
- apt-get install -y -qq $INSTALL_PACKAGES $DEBIAN_COMMON_PACKAGES
-
# Active server OS?
-re:ubuntu1804:build:
+re:ubuntu1804-attoconf:build:
<<: *prerequisites
stage: build
image: ubuntu:18.04
variables:
<<: *base_vars
- INSTALL_PACKAGES: python
+ INSTALL_PACKAGES: python3
script:
- echo "Building TMW Athena $CI_BUILD_NAME"
- - git submodule update --init
- git fetch -t
- printf "Building TMW Athena version %s\n" "$(git describe --tags HEAD)"
- - ./configure --user
+ - mkdir build-atto
+ - cd build-atto
+ - ../configure --user
- make
- whoami
- make install
@@ -37,23 +36,20 @@ re:ubuntu1804:build:
untracked: true
expire_in: 30 mins
-
-
-
# Next server OS?
-re:ubuntu2204:build:
+re:ubuntu2204-attoconf:build:
<<: *prerequisites
stage: build
image: ubuntu:22.04
variables:
<<: *base_vars
- INSTALL_PACKAGES: python2
+ INSTALL_PACKAGES: python3
script:
- - ln -s /usr/bin/python2 /usr/bin/python
- - git submodule update --init
- git fetch -t
- printf "Building TMW Athena version %s\n" "$(git describe --tags HEAD)"
- - ./configure --user
+ - mkdir build-atto
+ - cd build-atto
+ - ../configure --user
- make
- whoami
- make install
@@ -61,31 +57,69 @@ re:ubuntu2204:build:
untracked: true
expire_in: 30 mins
-
+# Next server OS, with cmake
+re:ubuntu2204:build:
+ <<: *prerequisites
+ stage: build
+ image: ubuntu:22.04
+ variables:
+ <<: *base_vars
+ INSTALL_PACKAGES: python3 cmake
+ script:
+ - git fetch -t
+ - printf "Building TMW Athena version %s\n" "$(git describe --tags HEAD)"
+ - mkdir build-cmake
+ - cd build-cmake
+ - cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local ..
+ - make
+ - whoami
+ - make install
+ artifacts: # required for test stage
+ untracked: true
+ expire_in: 30 mins
# Disabled. fails with:
# (1) GDB failing to resolve a type
# (2) /usr/bin/ld: Dwarf Error: Can't find .debug_ranges section.
-.re:ubuntu1804:test:
+.re:ubuntu1804-attoconf:test:
<<: *prerequisites
stage: test
image: ubuntu:18.04
+ needs: ["ubuntu1804-attoconf:build"]
+ variables:
+ <<: *base_vars
+ INSTALL_PACKAGES: python3 gdb
+ script:
+ - printf "Testing TMW Athena version %s\n" "$(git describe --tags HEAD)"
+ - cd build-atto
+ - make test
+
+
+# Enabled tests
+re:ubuntu2204-attoconf:test:
+ <<: *prerequisites
+ stage: test
+ image: ubuntu:22.04
+ needs: ["re:ubuntu2204-attoconf:build"]
variables:
<<: *base_vars
- INSTALL_PACKAGES: python gdb
+ INSTALL_PACKAGES: python3 gdb
script:
- printf "Testing TMW Athena version %s\n" "$(git describe --tags HEAD)"
+ - cd build-atto
- make test
-re:ubuntu2204:test:
+# Disabled. Tests are not supported yet by the CMake project.
+.re:ubuntu2204:test:
<<: *prerequisites
stage: test
image: ubuntu:22.04
+ needs: ["re:ubuntu2204:build"]
variables:
<<: *base_vars
- INSTALL_PACKAGES: python2 gdb
+ INSTALL_PACKAGES: python3 gdb
script:
- - ln -s /usr/bin/python2 /usr/bin/python
- printf "Testing TMW Athena version %s\n" "$(git describe --tags HEAD)"
+ - cd build-cmake
- make test
diff --git a/.gitmodules b/.gitmodules
index 8197e60..48c5d1f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,6 @@
[submodule "deps/attoconf"]
path = deps/attoconf
- url = https://github.com/o11c/attoconf.git
+ url = https://github.com/Freeyorp/attoconf
[submodule "deps/googletest"]
path = deps/googletest
url = https://github.com/google/googletest.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..5abdffb
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,188 @@
+cmake_minimum_required(VERSION 3.10)
+
+# Function for conveniently capturing git output, used to set version related variables
+find_package(Git REQUIRED)
+function(git_capture_output var)
+ execute_process(COMMAND ${GIT_EXECUTABLE} ${ARGN}
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+ OUTPUT_VARIABLE ${var}
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_QUIET)
+ message(STATUS "${var} = ${${var}}")
+ set(${var} ${${var}} PARENT_SCOPE)
+endfunction()
+
+git_capture_output(VERSION_FULL describe --tags)
+git_capture_output(VERSION_HASH rev-parse HEAD)
+
+# Capture the major.minor.patch part of the version based on the last tag
+string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" VERSION ${VERSION_FULL})
+
+# Capture the tweak part of the version
+if(${VERSION_FULL} MATCHES "^v[0-9]+\\.[0-9]+\\.[0-9]+-([0-9]+)-g[0-9a-f]+$")
+ set(VERSION "${VERSION}.${CMAKE_MATCH_1}")
+else()
+ set(VERSION "${VERSION}.0")
+endif()
+
+project(tmwAthena VERSION ${VERSION} LANGUAGES CXX)
+
+# Prefer to use G++ as the compiler
+set(CMAKE_CXX_COMPILER g++)
+# Set C++ standard to C++11
+# Note we want -std=c++11, not -std=gnu++11, as we want to avoid GNU extensions
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_EXTENSIONS OFF)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+# Some sources and includes are generated, such as the protocol headers.
+# We defer to generate.make for these rules.
+# Note that these are raw Makefile rules, not CMake rules, so a simple
+# add_custom_command() won't work.
+execute_process(COMMAND make -f ${CMAKE_SOURCE_DIR}/generate.mk
+ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
+
+# The generate target must be re-run when the scripts change
+set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
+ "${PROJECT_SOURCE_DIR}/generate.mk"
+ "${PROJECT_SOURCE_DIR}/tools/config.py"
+ "${PROJECT_SOURCE_DIR}/tools/protocol.py"
+)
+
+# Search through the tree for sources
+# For each subfolder in src, add all .cpp, .hpp and .tcc files to a subfolder's SOURCES
+# variable.
+set(ALL_SOURCES "")
+foreach(dir admin ast char compat conf generic high ints io login map mmo net proto-base range sexpr shared strings tests wire)
+ file(GLOB_RECURSE ${dir}_SOURCES CONFIGURE_DEPENDS
+ src/${dir}/*.cpp
+ src/${dir}/*.hpp
+ src/${dir}/*.tcc)
+ # Exclude any _test.cpp files from the build
+ set(ALL_SOURCES ${ALL_SOURCES} ${${dir}_SOURCES})
+ list(FILTER ${dir}_SOURCES EXCLUDE REGEX ".*_test.cpp")
+ message("Adding sources in ${dir}: ${${dir}_SOURCES}")
+endforeach()
+
+# All targets include the include/ directory
+include_directories(include)
+
+# We want -fvisibility=hidden for regular objects, but not shared libraries.
+# CMake provides a helpful preset for this.
+# FIXME this is currently broken
+#set(CMAKE_CXX_VISIBILITY_PRESET hidden)
+# General purpose build flags.
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -fstack-protector -fno-strict-aliasing -flto=auto")
+# Enable link time optimization, and track function and data sections. We let
+# the linker remove unused code.
+#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto=auto -ffunction-sections -fdata-sections -Wl,--gc-sections")
+# Next, add warnings
+#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
+# Add debug information
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb")
+
+# Vendor Name: String (no newlines, no parentheses)
+# This is usually one word, and does not (usually) change over time.
+# (Examples: Gentoo, Debian, Fedora, Ubuntu)
+set(VENDOR_NAME Vanilla)
+# Vendor Point: Integer (max value 65535)
+# This is intended to be the "packaging revision number", assuming that's
+# an integer. At a minimum, please try to make it nonzero if you have
+# any non-upstream patches (unconditionally nonzero is also okay).
+# (If your revision 0 package has patches ... please be nicer to upstream)
+set(VENDOR_POINT 0)
+# URL where the source may be found (after searching for version number).
+# See AGPLv3 section 13
+set(VENDOR_SOURCE https://git.themanaworld.org/tmw/tmwa)
+
+# Convenience
+set(VERSION_STRING "TMWA ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH} dev${PROJECT_VERSION_TWEAK} +${VENDOR_POINT} (${VENDOR_NAME})")
+set(VERSION_DOTS "${PROJECT_VERSION}.${VENDOR_POINT}")
+
+include(GNUInstallDirs)
+set(PACKAGEDATADIR "${CMAKE_INSTALL_FULL_DATAROOTDIR}/tmwa")
+set(LOCALSTATEDIR "${CMAKE_INSTALL_FULL_LOCALSTATEDIR}")
+set(SYSCONFDIR "${CMAKE_INSTALL_FULL_SYSCONFDIR}")
+
+# Generate the install.hpp and version.hpp files.
+configure_file(src/conf/install.hpp.in src/conf/install.hpp @ONLY)
+configure_file(src/conf/version.hpp.in src/conf/version.hpp @ONLY)
+set(conf_SOURCES ${conf_SOURCES} src/conf/install.hpp src/conf/version.hpp)
+# And have the build search for files in the build directory.
+# Note that #includes with generated files should use a path relative to the
+# top level source or build directory.
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+# Add a shared library: libtmwa-shared.so.0
+# When we add_executable later, we need to link against this library.
+add_library(tmwa-shared SHARED ${shared_SOURCES}
+ src/io/dir.cpp
+ src/io/fd.cpp
+ src/io/read.cpp
+ src/io/write.cpp
+ ${strings_SOURCES}
+)
+# SO versioning
+set(ABI_VERSION 0)
+set_target_properties(tmwa-shared PROPERTIES
+ VERSION ${ABI_VERSION}.${VERSION_DOTS}
+ SOVERSION ${ABI_VERSION})
+
+# We have four binaries we want to build: tmwa-{login,char,map,admin}
+add_executable(tmwa-login ${login_SOURCES}
+ ${generic_SOURCES}
+ ${high_SOURCES}
+ ${io_SOURCES}
+ ${mmo_SOURCES}
+ ${net_SOURCES}
+ ${wire_SOURCES}
+)
+target_link_libraries(tmwa-login tmwa-shared)
+
+add_executable(tmwa-char ${char_SOURCES}
+ ${generic_SOURCES}
+ ${high_SOURCES}
+ ${io_SOURCES}
+ ${mmo_SOURCES}
+ ${net_SOURCES}
+ ${wire_SOURCES}
+)
+target_link_libraries(tmwa-char tmwa-shared)
+
+add_executable(tmwa-map ${map_SOURCES}
+ ${ast_SOURCES}
+ ${compat_SOURCES}
+ ${generic_SOURCES}
+ ${high_SOURCES}
+ ${io_SOURCES}
+ ${mmo_SOURCES}
+ ${net_SOURCES}
+ ${wire_SOURCES}
+ )
+target_link_libraries(tmwa-map tmwa-shared)
+
+add_executable(tmwa-admin ${admin_SOURCES}
+ ${generic_SOURCES}
+ ${high_SOURCES}
+ ${io_SOURCES}
+ ${mmo_SOURCES}
+ ${net_SOURCES}
+ ${wire_SOURCES}
+)
+target_link_libraries(tmwa-admin tmwa-shared)
+
+# TODO: Call make -f ${CMAKE_SOURCE_DIR}/generate.mk clean to clean up the
+# generated files. We want this to be run every time we call make clean.
+
+# Install targets
+install(TARGETS tmwa-login tmwa-char tmwa-map tmwa-admin tmwa-shared
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+# Install shared configuration and data
+install(FILES etc/tmwa/shared.conf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/tmwa)
+install(FILES share/tmwa/shared.data DESTINATION ${CMAKE_INSTALL_DATADIR}/tmwa)
+
+# Make sure there is a var directory
+install(DIRECTORY DESTINATION ${CMAKE_INSTALL_LOCALSTATEDIR}/tmwa)
diff --git a/COPYING b/COPYING
index edf98ab..c1c13ff 100644
--- a/COPYING
+++ b/COPYING
@@ -5,7 +5,7 @@ This combination is explicitly allowed by section 13 of the licenses.
See gpl-3.0.txt or agpl-3.0.txt for specific terms.
In order to make life easy for users who run this software publicly,
-please set VENDOR_SOURCE in version.make if you make any modifications.
+please set VENDOR_SOURCE in version.mk if you make any modifications.
History:
diff --git a/Makefile.in b/Makefile.in
index 10f909c..075c107 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -152,7 +152,7 @@ $(info The Road goes ever on and on ...)
endif
endif
-include ${SRC_DIR}/version.make
+include ${SRC_DIR}/version.mk
ifeq (${ENABLE_SHARED},yes)
LIB_SUFFIX_FAKE := so
@@ -184,12 +184,13 @@ export PATH:=$(realpath ${SRC_DIR}/tools):${PATH}
SHELL=bash
# need to generate source files before path lists
-$(shell make -f ${SRC_DIR}/generate.make >&2)
+$(shell make -f ${SRC_DIR}/generate.mk >&2)
stamp/generated.stamp:
# if you get here, the shell above failed
false
# path lists
+GENERATED_FILES := src/conf/version.hpp
REAL_SOURCES := $(shell cd ${SRC_DIR}; find src/ -name '*.cpp')
REAL_HEADERS := $(shell cd ${SRC_DIR}; find include/ src/ -name '*.hpp' -o -name '*.tcc')
PIES := $(shell cd ${SRC_DIR}; find src/ -name '*.py')
@@ -283,9 +284,9 @@ endif
# This needs to edit CXX instead of CXXFLAGS in order to make
# the %.ii rule work.
ifeq (${ENABLE_CYGWIN_HACKS},yes)
-override CXX += -std=gnu++0x
+override CXX += -std=gnu++11
else
-override CXX += -std=c++0x
+override CXX += -std=c++11
endif
CXXFLAGS += -fstack-protector
@@ -333,9 +334,60 @@ include ${DEPENDS}
endif
thisdir := $(abspath .)
+
#$(foreach root,${PATTERN_ROOTS},$(info pre-root: ${root} := $(value ${root}))$(info ))
# can't do $(filter %.hpp yet)
-$(foreach root,${PATTERN_ROOTS},$(eval ${root} := $(sort $(patsubst ${thisdir}/%,%,$(abspath $(patsubst ${SRC_DIR}/%,%,$(wildcard $(value ${root})) $(filter conf-raw/%.h,$(value ${root}))))))))
+
+# Sanitize the dependencies.
+#
+# ${root} has a value like "mmo/version", and ${mmo/version} in turn has the
+# value of all of its dependencies (from the %.d files).
+#
+# This reassigns the *value of the value* of ${root} to be:
+# - sorted
+# - relative and simplified: it's stripped of any absolute path relative to
+# the current directory after being converted to an absolute path.
+# This also converts paths from being relative to the build directory to
+# being relative to the source directory, if they differ.
+# This simplifies any construct such as:
+# ../src/mmo/../strings/zstring.tcc
+# to:
+# src/strings/zstring.tcc
+# - only files which either exist (the purpose of the wildcard function),
+# are in the conf-raw directory (the first filter function), or
+# are listed in the GENERATED_FILES variable (the second filter function).
+# This means that if there are any build-time generated files added, this
+# needs to be modified to not filter them out.
+#
+# The end result for each root will look something like:
+# mmo/version := conf-raw/int-VENDOR_POINT.h conf-raw/int-VERSION_DEVEL.h [...]
+# src/strings/zstring.tcc src/wire/fwd.hpp
+
+# Firstly, make the paths relative and simplified.
+$(foreach root,${PATTERN_ROOTS},$(eval \
+ ${root} := $(sort $(patsubst ${thisdir}/%,%, \
+ $(abspath $(patsubst ${SRC_DIR}/%,%, $(value ${root}))) \
+ )) \
+))
+
+# Secondly, make sure that the files actually exist, or are files we expect to
+# generate ourselves. Sort the result for readability.
+# Note that we re-add and remove the ${SRC_DIR} prefix when first testing for
+# existence, as we need to test for existence relative to our build directory.
+# The second wildcard test doesn't add a ${SRC_DIR} prefix, but does strip it,
+# handling the case where the file is specified relative to the source
+# directory (mostly the case in test sources).
+$(foreach root,${PATTERN_ROOTS},$(eval \
+ ${root} := $(sort \
+ $(patsubst ${SRC_DIR}/%,%,$(wildcard $(addprefix ${SRC_DIR}/, \
+ $(value ${root}) \
+ ))) \
+ $(patsubst ${SRC_DIR}/%,%,$(wildcard ${SRC_DIR})) \
+ $(filter conf-raw/%.h,$(value ${root})) \
+ $(filter ${GENERATED_FILES},$(value ${root})) \
+ ) \
+))
+
# have to redo what we undid to get it as a variable
$(foreach root,${PATTERN_ROOTS},$(eval obj/${root}.ii obj/${root}.ll obj/${root}.bc obj/${root}.s obj/${root}.pdc.o obj/${root}.pic.o obj/${root}.d : $(value ${root})) )
#$(foreach root,${PATTERN_ROOTS},$(info post-root: ${root} := $(value ${root}))$(info ))
@@ -386,6 +438,7 @@ vpath include/%.hpp ${SRC_DIR}
vpath src/%.tcc ${SRC_DIR}
vpath tools/% ${SRC_DIR}
vpath src/%.py ${SRC_DIR}
+vpath src/%.in ${SRC_DIR}
.DELETE_ON_ERROR:
.DEFAULT_GOAL := all
@@ -437,6 +490,10 @@ obj/%.d: src/%.cpp | stamp/generated.stamp
${CXX} ${CPPFLAGS} -DGENERATING_DEPENDENCIES ${CXXFLAGS} -MG -MM \
-MT '$(patsubst obj/%.d,%,$@) := ' \
-MF $@ $<
+# -MG takes the include parameter as-is without prepending the path whenever
+# it's not found, and presumed to be a not-yet generated file.
+# #include statements for generated files should always be relative to the
+# source (or build) directory.
endif
# the above SRC_DIR replacement is not really safe, but it works okayish.
obj/%.ii: src/%.cpp
@@ -508,8 +565,8 @@ obj/gtest%.pdc.o: ${GTEST_DIR}/src/gtest%.cc
DTEST_OBJS := $(filter obj/debug-debug/%.pdc.o,${PDC_OBJECTS})
DTEST_STAMPS := $(patsubst bin/tests/%.elf,stamp/run-%.stamp,${DTEST_BINARIES})
-${DTEST_OBJS}: override CXXFLAGS += -g -O0 -gdwarf-3
-${DTEST_STAMPS}: override TESTER=${GDB} -return-child-result -nx -batch -ex 'python file_to_load = "$<"' -x ${SRC_DIR}/tools/debug-debug.gdb --args false
+${DTEST_OBJS}: override CXXFLAGS += -ggdb -O0
+${DTEST_STAMPS}: override TESTER=LD_LIBRARY_PATH="./lib" ${GDB} -return-child-result -nx -batch -ex 'python file_to_load = "$<"' -x ${SRC_DIR}/tools/debug-debug.gdb --args false
${DTEST_STAMPS}: tools/debug-debug.gdb
test: test-direct
@@ -539,7 +596,7 @@ test: test-rank-fwd
test-rank-fwd: ${CHECK_RANK_FWDS}
stamp/%.rank: src/%
$(MKDIR_FIRST)
- includes=$$(grep '#include.*".*/.*"' $< | sed 's/^[^"]*"//;s/"[^"]*$$//;s:/[^/]*$$::' | sort -u | fgrep -vx -e '..' -e 'conf-raw' -e '../conf'); \
+ includes=$$(grep '#include.*".*/.*"' $< | sed 's/^[^"]*"//;s/"[^"]*$$//;s:/[^/]*$$::' | sort -u | fgrep -vx -e '..' -e 'conf-raw' -e '../conf' -e 'src/conf'); \
for inc in $$includes; do if ! test -f ${<D}/fwd.hpp; then continue; fi; echo fgrep -q $${inc}/fwd.hpp ${<D}/fwd.hpp; fgrep -q $${inc}/fwd.hpp ${<D}/fwd.hpp || { echo ${<D}/fwd.hpp:''23: error: No $${inc}/fwd.hpp; exit 1; }; done
touch $@
@@ -682,21 +739,22 @@ conf-raw/bool-%.h: FORCE
conf-raw/str-%.h: FORCE
$(MKDIR_FIRST)
@echo '#define $* "$(value $*)"_s' | maybe-replace $@
-FORCE: ;
+
+FORCE:: ;
.PHONY: FORCE
-override CPPFLAGS += -I . -I ${SRC_DIR}/include
+override CPPFLAGS += -I. -I${SRC_DIR} -I${SRC_DIR}/include
# distribution tarballs
# this only works from within a git checkout
-dist/%/version.make:
+dist/%/version.mk:
$(MKDIR_FIRST)
- git --git-dir=${SRC_DIR}/.git show HEAD:version.make > $@
+ git --git-dir=${SRC_DIR}/.git show HEAD:version.mk > $@
sed 's/^VERSION_FULL := .*/#&\nVERSION_FULL := ${VERSION_FULL}/' -i $@
sed 's/^VERSION_HASH := .*/#&\nVERSION_HASH := ${VERSION_HASH}/' -i $@
-dist/%-src.tar: dist/%/version.make
+dist/%-src.tar: dist/%/version.mk
git --git-dir=${SRC_DIR}/.git archive --prefix=$*/ -o $@ HEAD
- ( mtime="$$(git --git-dir=${SRC_DIR}/.git log -n1 --pretty=%ci)" && cd dist && tar uf $*-src.tar --mtime="$${mtime}" --mode=664 --owner=root --group=root $*/version.make )
- rm dist/$*/version.make
+ ( mtime="$$(git --git-dir=${SRC_DIR}/.git log -n1 --pretty=%ci)" && cd dist && tar uf $*-src.tar --mtime="$${mtime}" --mode=664 --owner=root --group=root $*/version.mk )
+ rm dist/$*/version.mk
rmdir dist/$*/
dist/%-attoconf-only.tar:
$(MKDIR_FIRST)
diff --git a/README.md b/README.md
index 6ec6a58..1915888 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ The rest of this file contains information relevant to distributors and contribu
## 1. Distributors
### Important notes
-- Please read [version.make](version.make) for important version information.
+- Please read [version.mk](version.mk) for important version information.
- TMWA requires git to build by default. Use 'make dist' to get a tarball.
### Platform dependencies
@@ -91,7 +91,7 @@ The 4 main programs listed below are typically running on the same machine, but
- `tmwa-map`: Formerly known as `map-server`. This server connects to `tmwa-char`. Multiple instances of `tmwa-map` can connect to the same `tmwa-char`, with clients able to switch servers if they move to a map handled by a different map server. This has not been used by The Mana World, and may not work properly.
#### About server data
-To run the server, you will need a complete set of content including config files, game scripts, savefiles, and client updates. We strongly recommend setting up a web server to serve the updates. You can find a compatible set of server data at https://git.themanaworld.org/legacy/serverdata. Please follow the instructions in the [How to Develop](https://wiki.themanaworld.org/index.php/Development:How_to_Develop) article for more information.
+To run the server, you will need a complete set of content including config files, game scripts, savefiles, and client updates. We strongly recommend setting up a web server to serve the updates. You can find a compatible set of server data at https://git.themanaworld.org/tmw/serverdata. Please follow the instructions in the [How to Develop](https://wiki.themanaworld.org/index.php/Development:How_to_Develop) article for more information.
## 2. Contributors
We welcome contributions from developers like you! If you are interested in maintaining this server, please get in touch with the currently active maintainers first. It's important to make changes with extreme care and ensure that each change is thoroughly tested and reviewed before it goes live.
diff --git a/configure b/configure
index aa1d0fd..468f029 100755
--- a/configure
+++ b/configure
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright 2013 Ben Longbons <b.r.longbons@gmail.com>
#
@@ -17,8 +17,6 @@
# You should have received a copy of the GNU General Public License
# along with attoconf. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import print_function, division, absolute_import
-
import os
import sys
@@ -90,7 +88,7 @@ class Configuration(Cxx, Install, ConfigHash, Templates):
# 2) Modern distros ship gtest-1.13+. It requires C++14+, while
# TMWA is currently a C++0x codebase.
self.add_option('GTEST_DIR',
- init=os.path.join(os.getcwd(), 'deps/googletest/googletest'),
+ init=os.path.join(os.path.abspath(self.srcdir), 'deps/googletest/googletest'),
# http://code.google.com/p/googletest/wiki/FAQ#Why_is_it_not_recommended_to_install_a_pre-compiled_copy_of_Goog
type=filepath, check=lambda build, GTEST_DIR: None,
help='Location of Google Test sources, must contain src/gtest-all.cc (linking to a precompiled library is NOT supported)', hidden=False)
@@ -112,7 +110,9 @@ def main():
srcdir = os.path.dirname(sys.argv[0])
proj = Configuration(
srcdir=srcdir,
- template_files=['Makefile'],
+ # Note that src/conf/version.hpp is made later, by Makefile.
+ # See version.mk (included by Makefile).
+ template_files=['Makefile', 'src/conf/install.hpp'],
)
proj.set_package('tmwa', 'The Mana World (Athena server)');
proj.jiggle()
diff --git a/deps/attoconf b/deps/attoconf
-Subproject 7b939e7e4ce36e8b62b10025e567f871731cbf4
+Subproject 58e75e5f1271363a48c2a3417a2851d3b145b3f
diff --git a/generate.make b/generate.mk
index c32ca8d..c32ca8d 100644
--- a/generate.make
+++ b/generate.mk
diff --git a/src/char/char.cpp b/src/char/char.cpp
index 7ffdd0f..2194ef2 100644
--- a/src/char/char.cpp
+++ b/src/char/char.cpp
@@ -777,8 +777,8 @@ void create_online_files(void)
stamp_time(timetemp);
// write heading
FPRINTF(fp2, "<HTML>\n"_fmt);
- FPRINTF(fp2, " <META http-equiv=\"Refresh\" content=\"%d\">\n"_fmt, char_conf.online_refresh_html); // update on client explorer every x seconds
FPRINTF(fp2, " <HEAD>\n"_fmt);
+ FPRINTF(fp2, " <META http-equiv=\"refresh\" content=\"%d\">\n"_fmt, char_conf.online_refresh_html); // update on client explorer every x seconds
FPRINTF(fp2, " <TITLE>Online Players on %s</TITLE>\n"_fmt,
char_conf.server_name);
FPRINTF(fp2, " </HEAD>\n"_fmt);
diff --git a/src/conf/install.hpp b/src/conf/install.hpp.in
index 42fd125..e63164a 100644
--- a/src/conf/install.hpp
+++ b/src/conf/install.hpp.in
@@ -20,10 +20,9 @@
// just mention "fwd.hpp" to make formatter happy
-#include "conf-raw/str-PACKAGESYSCONFDIR.h"
-#include "conf-raw/str-PACKAGELOCALSTATEDIR.h"
-#include "conf-raw/str-PACKAGEDATADIR.h"
-
+#define PACKAGESYSCONFDIR "@SYSCONFDIR@/tmwa"_s
+#define PACKAGELOCALSTATEDIR "@LOCALSTATEDIR@/tmwa"_s
+#define PACKAGEDATADIR "@PACKAGEDATADIR@"_s
namespace tmwa
{
diff --git a/src/conf/version.hpp b/src/conf/version.hpp.in
index df8a8b6..126fae2 100644
--- a/src/conf/version.hpp
+++ b/src/conf/version.hpp.in
@@ -20,20 +20,19 @@
// just mention "fwd.hpp" to make formatter happy
-#include "conf-raw/str-VERSION_FULL.h"
-#include "conf-raw/str-VERSION_HASH.h"
+#define VERSION_FULL "@VERSION_FULL@"_s
+#define VERSION_HASH "@VERSION_HASH@"_s
-#include "conf-raw/int-VERSION_MAJOR.h"
-#include "conf-raw/int-VERSION_MINOR.h"
-#include "conf-raw/int-VERSION_PATCH.h"
-#include "conf-raw/int-VERSION_DEVEL.h"
+#define VERSION_MAJOR @PROJECT_VERSION_MAJOR@
+#define VERSION_MINOR @PROJECT_VERSION_MINOR@
+#define VERSION_PATCH @PROJECT_VERSION_PATCH@
+#define VERSION_DEVEL @PROJECT_VERSION_TWEAK@
-#include "conf-raw/str-VENDOR_NAME.h"
-#include "conf-raw/int-VENDOR_POINT.h"
-#include "conf-raw/str-VENDOR_SOURCE.h"
-
-#include "conf-raw/str-VERSION_STRING.h"
+#define VENDOR_NAME "@VENDOR_NAME@"_s
+#define VENDOR_POINT @VENDOR_POINT@
+#define VENDOR_SOURCE "@VENDOR_SOURCE@"_s
+#define VERSION_STRING "@VERSION_STRING@"_s
namespace tmwa
{
diff --git a/src/main-gdb-head.py b/src/main-gdb-head.py
index a465c97..09e22fe 100644
--- a/src/main-gdb-head.py
+++ b/src/main-gdb-head.py
@@ -5,7 +5,7 @@
# gdb sticks everything in one scope.
# This lets us enumerate what *we* added.
-initial_globals = {id(v):v for v in globals().values()}
+initial_globals = {id(v):v for v in list(globals().values())}
import re
diff --git a/src/map/battle.cpp b/src/map/battle.cpp
index ac8dcf2..d66308d 100644
--- a/src/map/battle.cpp
+++ b/src/map/battle.cpp
@@ -2091,10 +2091,10 @@ ATK battle_weapon_attack(dumb_ptr<block_list> src, dumb_ptr<block_list> target,
}
if (wd.damage > 0
- && t_sc_data[StatusChange::SC_PHYS_SHIELD].timer
+ && (t_sc_data[StatusChange::SC_PHYS_SHIELD].timer || t_sc_data[StatusChange::SC_PHYS_SHIELD_ITEM].timer)
&& target->bl_type == BL::PC)
{
- int reduction = t_sc_data[StatusChange::SC_PHYS_SHIELD].val1;
+ int reduction = std::max(t_sc_data[StatusChange::SC_PHYS_SHIELD].val1, t_sc_data[StatusChange::SC_PHYS_SHIELD_ITEM].val1); // highest value is taken here but serverdata should make sure only one of those is active
if (reduction > wd.damage)
reduction = wd.damage;
diff --git a/src/map/clif.cpp b/src/map/clif.cpp
index a5bb2ba..1b77be6 100644
--- a/src/map/clif.cpp
+++ b/src/map/clif.cpp
@@ -3865,6 +3865,12 @@ RecvResult clif_parse_WalkToXY(Session *s, dumb_ptr<map_session_data> sd)
if (bool(sd->opt1) && sd->opt1 != (Opt1::_stone6))
return rv;
+ if (sd->sc_data[StatusChange::SC_CANTMOVE].timer)
+ {
+ pc_stop_walking(sd, 1); // this is a little hack since client is a bit bugged and still moves several tiles and then gets reset to the position where status was triggered with this it only moves 1 pixel or so and gets set back
+ return rv;
+ }
+
if (sd->invincible_timer)
pc_delinvincibletimer(sd);
@@ -4014,7 +4020,7 @@ RecvResult clif_parse_GetCharNameRequest(Session *s, dumb_ptr<map_session_data>
dumb_ptr<mob_data> md = bl->is_mob();
nullpo_retr(rv, md);
- if (md->name != MobName() && md->name != get_mob_db(md->mob_class).name && md->name.size() >= 4)
+ if (md->name.size() >= 4)
fixed_95.char_name = stringish<CharName>(md->name);
else
fixed_95.char_name = stringish<CharName>(get_mob_db(md->mob_class).name);
@@ -4620,7 +4626,8 @@ RecvResult clif_parse_DropItem(Session *s, dumb_ptr<map_session_data> sd)
OMATCH_BEGIN_SOME (sdidn, sd->inventory_data[fixed.ioff2.unshift()])
{
- if (bool(sdidn->mode & ItemMode::NO_DROP))
+ GmLevel gmlvl = pc_isGM(sd);
+ if (bool(sdidn->mode & ItemMode::NO_DROP) && gmlvl.get_all_bits() < 60)
{
clif_displaymessage(sd->sess, "This item can't be dropped."_s);
return rv;
@@ -4909,7 +4916,8 @@ RecvResult clif_parse_TradeAddItem(Session *s, dumb_ptr<map_session_data> sd)
if (fixed.zeny_or_ioff2.ok())
OMATCH_BEGIN_SOME (sdidn, sd->inventory_data[fixed.zeny_or_ioff2.unshift()])
{
- if (bool(sdidn->mode & ItemMode::NO_TRADE))
+ GmLevel gmlvl = pc_isGM(sd);
+ if (bool(sdidn->mode & ItemMode::NO_TRADE) && gmlvl.get_all_bits() < 60)
{
clif_displaymessage(sd->sess, "This item can't be traded."_s);
return rv;
diff --git a/src/map/map.cpp b/src/map/map.cpp
index ff69a56..f8e8bea 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -1241,6 +1241,20 @@ int map_setipport(MapName name, IP4Address ip, int port)
}
/*==========================================
+ * creates a hash of a map name
+ *------------------------------------------
+ */
+int map_create_hash(XString str) {
+ const int k = 67;
+ const int m = 3067;
+ int hash = 0;
+ for (int i = 0; i < str.size(); i++) {
+ hash += (str[i] * (int)pow(k, i)) % m;
+ }
+ return hash;
+}
+
+/*==========================================
* マップ1枚読み込み
*------------------------------------------
*/
@@ -1265,6 +1279,9 @@ bool map_readmap(map_local *m, size_t num, MapName fn)
m->npc_num = 0;
m->users = 0;
+
+ m->hash = map_create_hash(fn);
+
really_memzero_this(&m->flag);
if (battle_config.pk_mode)
m->flag.set(MapFlag::PVP, 1);
diff --git a/src/map/map.hpp b/src/map/map.hpp
index 56d07a5..8bb5aa7 100644
--- a/src/map/map.hpp
+++ b/src/map/map.hpp
@@ -184,7 +184,7 @@ struct map_session_data : block_list, SessionData
None, None, None, None, None, None, None, None, None, None,
}}; // explicit is better than implicit
earray<IOff0, EQUIP, EQUIP::COUNT> equip_index_maybe;
- int weight, max_weight;
+ int weight, max_weight, max_weight_override;
MapName mapname_;
Session *sess; // use this, you idiots!
short to_x, to_y;
@@ -531,6 +531,7 @@ struct map_local : map_abstract
Point save;
Point resave;
int mask;
+ int hash;
Array<dumb_ptr<npc_data>, MAX_NPC_PER_MAP> npc;
};
diff --git a/src/map/mob.cpp b/src/map/mob.cpp
index 996e2bb..fc41d19 100644
--- a/src/map/mob.cpp
+++ b/src/map/mob.cpp
@@ -355,24 +355,25 @@ void mob_init(dumb_ptr<mob_data> md)
{
int i;
const Species mob_class = md->mob_class;
- const int mutations_nr = get_mob_db(mob_class).mutations_nr;
- const int mutation_power = get_mob_db(mob_class).mutation_power;
-
- md->stats[mob_stat::LV] = get_mob_db(mob_class).lv;
- md->stats[mob_stat::MAX_HP] = get_mob_db(mob_class).max_hp;
- md->stats[mob_stat::STR] = get_mob_db(mob_class).attrs[ATTR::STR];
- md->stats[mob_stat::AGI] = get_mob_db(mob_class).attrs[ATTR::AGI];
- md->stats[mob_stat::VIT] = get_mob_db(mob_class).attrs[ATTR::VIT];
- md->stats[mob_stat::INT] = get_mob_db(mob_class).attrs[ATTR::INT];
- md->stats[mob_stat::DEX] = get_mob_db(mob_class).attrs[ATTR::DEX];
- md->stats[mob_stat::LUK] = get_mob_db(mob_class).attrs[ATTR::LUK];
- md->stats[mob_stat::ATK1] = get_mob_db(mob_class).atk1;
- md->stats[mob_stat::ATK2] = get_mob_db(mob_class).atk2;
- md->stats[mob_stat::ADELAY] = get_mob_db(mob_class).adelay.count();
- md->stats[mob_stat::DEF] = get_mob_db(mob_class).def;
- md->stats[mob_stat::MDEF] = get_mob_db(mob_class).mdef;
- md->stats[mob_stat::CRITICAL_DEF] = get_mob_db(mob_class).critical_def;
- md->stats[mob_stat::SPEED] = get_mob_db(mob_class).speed.count();
+ const mob_db_& mob_info = get_mob_db(mob_class);
+ const int mutations_nr = mob_info.mutations_nr;
+ const int mutation_power = mob_info.mutation_power;
+
+ md->stats[mob_stat::LV] = mob_info.lv;
+ md->stats[mob_stat::MAX_HP] = mob_info.max_hp;
+ md->stats[mob_stat::STR] = mob_info.attrs[ATTR::STR];
+ md->stats[mob_stat::AGI] = mob_info.attrs[ATTR::AGI];
+ md->stats[mob_stat::VIT] = mob_info.attrs[ATTR::VIT];
+ md->stats[mob_stat::INT] = mob_info.attrs[ATTR::INT];
+ md->stats[mob_stat::DEX] = mob_info.attrs[ATTR::DEX];
+ md->stats[mob_stat::LUK] = mob_info.attrs[ATTR::LUK];
+ md->stats[mob_stat::ATK1] = mob_info.atk1;
+ md->stats[mob_stat::ATK2] = mob_info.atk2;
+ md->stats[mob_stat::ADELAY] = mob_info.adelay.count();
+ md->stats[mob_stat::DEF] = mob_info.def;
+ md->stats[mob_stat::MDEF] = mob_info.mdef;
+ md->stats[mob_stat::CRITICAL_DEF] = mob_info.critical_def;
+ md->stats[mob_stat::SPEED] = mob_info.speed.count();
md->stats[mob_stat::XP_BONUS] = MOB_XP_BONUS_BASE;
for (i = 0; i < mutations_nr; i++)
@@ -2596,6 +2597,7 @@ int mob_damage(dumb_ptr<block_list> src, dumb_ptr<mob_data> md, int damage,
);
{
+ const mob_db_& mob_info = get_mob_db(md->mob_class);
struct DmgLogParty
{
PartyPair p;
@@ -2660,23 +2662,23 @@ int mob_damage(dumb_ptr<block_list> src, dumb_ptr<mob_data> md, int damage,
per = 1;
base_exp =
- ((get_mob_db(md->mob_class).base_exp *
+ ((mob_info.base_exp *
md->stats[mob_stat::XP_BONUS]) >> MOB_XP_BONUS_SHIFT) * per / 256;
if (base_exp < 1)
base_exp = 1;
- if (sd && md && battle_config.pk_mode == 1
- && (get_mob_db(md->mob_class).lv - sd->status.base_level >= 20))
+ if (sd && battle_config.pk_mode == 1
+ && (mob_info.lv - sd->status.base_level >= 20))
{
base_exp *= 1.15; // pk_mode additional exp if monster >20 levels [Valaris]
}
if (md->state.special_mob_ai >= 1
&& battle_config.alchemist_summon_reward != 1)
base_exp = 0; // Added [Valaris]
- job_exp = get_mob_db(md->mob_class).job_exp * per / 256;
+ job_exp = mob_info.job_exp * per / 256;
if (job_exp < 1)
job_exp = 1;
- if (sd && md && battle_config.pk_mode == 1
- && (get_mob_db(md->mob_class).lv - sd->status.base_level >= 20))
+ if (sd && battle_config.pk_mode == 1
+ && (mob_info.lv - sd->status.base_level >= 20))
{
job_exp *= 1.15; // pk_mode additional exp if monster >20 levels [Valaris]
}
@@ -2730,13 +2732,14 @@ int mob_damage(dumb_ptr<block_list> src, dumb_ptr<mob_data> md, int damage,
if (md->state.special_mob_ai >= 1 && battle_config.alchemist_summon_reward != 1) // Added [Valaris]
break; // End
- if (!get_mob_db(md->mob_class).dropitem[i].nameid)
+ const auto& drop_info = mob_info.dropitem[i];
+ if (!drop_info.nameid)
continue;
- random_::Fixed<int, 10000> drop_rate = get_mob_db(md->mob_class).dropitem[i].p;
- if (battle_config.drops_by_luk > 0 && sd && md)
+ random_::Fixed<int, 10000> drop_rate = drop_info.p;
+ if (sd && battle_config.drops_by_luk > 0)
drop_rate.num += (sd->status.attrs[ATTR::LUK] * battle_config.drops_by_luk) / 100; // drops affected by luk [Valaris]
- if (sd && md && battle_config.pk_mode == 1
- && (get_mob_db(md->mob_class).lv - sd->status.base_level >= 20))
+ if (sd && battle_config.pk_mode == 1
+ && (mob_info.lv - sd->status.base_level >= 20))
drop_rate.num *= 1.25; // pk_mode increase drops if 20 level difference [Valaris]
// server-wide drop rate scaling
@@ -2745,7 +2748,7 @@ int mob_damage(dumb_ptr<block_list> src, dumb_ptr<mob_data> md, int damage,
continue;
struct delay_item_drop ditem {};
- ditem.nameid = get_mob_db(md->mob_class).dropitem[i].nameid;
+ ditem.nameid = drop_info.nameid;
ditem.amount = 1;
ditem.m = md->bl_m;
ditem.x = md->bl_x;
@@ -2796,9 +2799,11 @@ int mob_damage(dumb_ptr<block_list> src, dumb_ptr<mob_data> md, int damage,
npc_event(sd, md->npc_event, 0);
// TODO: in the future, OnPCKillEvent, OnMobKillEvent and OnPCDieEvent should be combined
- argrec_t arg[1] =
+ argrec_t arg[3] =
{
{"@mobID"_s, static_cast<int32_t>(unwrap<Species>(md->mob_class))},
+ {"@mobX"_s, static_cast<int32_t>(md->bl_x)},
+ {"@mobY"_s, static_cast<int32_t>(md->bl_y)},
};
npc_event_doall_l(stringish<ScriptLabel>("OnMobKillEvent"_s), sd->bl_id, arg);
}
@@ -3501,40 +3506,39 @@ int mobskill_event(dumb_ptr<mob_data> md, BF flag)
static
int mob_makedummymobdb(Species mob_class)
{
- int i;
-
- SNPRINTF(get_mob_db(mob_class).name, 24, "mob%d"_fmt, mob_class);
- SNPRINTF(get_mob_db(mob_class).jname, 24, "mob%d"_fmt, mob_class);
- get_mob_db(mob_class).lv = 1;
- get_mob_db(mob_class).max_hp = 1000;
- get_mob_db(mob_class).max_sp = 1;
- get_mob_db(mob_class).base_exp = 2;
- get_mob_db(mob_class).job_exp = 1;
- get_mob_db(mob_class).range = 1;
- get_mob_db(mob_class).atk1 = 7;
- get_mob_db(mob_class).atk2 = 10;
- get_mob_db(mob_class).def = 0;
- get_mob_db(mob_class).mdef = 0;
- get_mob_db(mob_class).attrs[ATTR::STR] = 1;
- get_mob_db(mob_class).attrs[ATTR::AGI] = 1;
- get_mob_db(mob_class).attrs[ATTR::VIT] = 1;
- get_mob_db(mob_class).attrs[ATTR::INT] = 1;
- get_mob_db(mob_class).attrs[ATTR::DEX] = 6;
- get_mob_db(mob_class).attrs[ATTR::LUK] = 2;
- get_mob_db(mob_class).range2 = 10;
- get_mob_db(mob_class).range3 = 10;
- get_mob_db(mob_class).size = 0; // 1
- get_mob_db(mob_class).race = Race::formless;
- get_mob_db(mob_class).element = LevelElement{0, Element::neutral};
- get_mob_db(mob_class).mode = MobMode::ZERO;
- get_mob_db(mob_class).speed = 300_ms;
- get_mob_db(mob_class).adelay = 1000_ms;
- get_mob_db(mob_class).amotion = 500_ms;
- get_mob_db(mob_class).dmotion = 500_ms;
- for (i = 0; i < MaxDrops; i++)
- {
- get_mob_db(mob_class).dropitem[i].nameid = ItemNameId();
- get_mob_db(mob_class).dropitem[i].p.num = 0;
+ mob_db_& mob_info = get_mob_db(mob_class);
+ SNPRINTF(mob_info.name, 24, "mob%d"_fmt, mob_class);
+ SNPRINTF(mob_info.jname, 24, "mob%d"_fmt, mob_class);
+ mob_info.lv = 1;
+ mob_info.max_hp = 1000;
+ mob_info.max_sp = 1;
+ mob_info.base_exp = 2;
+ mob_info.job_exp = 1;
+ mob_info.range = 1;
+ mob_info.atk1 = 7;
+ mob_info.atk2 = 10;
+ mob_info.def = 0;
+ mob_info.mdef = 0;
+ mob_info.attrs[ATTR::STR] = 1;
+ mob_info.attrs[ATTR::AGI] = 1;
+ mob_info.attrs[ATTR::VIT] = 1;
+ mob_info.attrs[ATTR::INT] = 1;
+ mob_info.attrs[ATTR::DEX] = 6;
+ mob_info.attrs[ATTR::LUK] = 2;
+ mob_info.range2 = 10;
+ mob_info.range3 = 10;
+ mob_info.size = 0; // 1
+ mob_info.race = Race::formless;
+ mob_info.element = LevelElement{0, Element::neutral};
+ mob_info.mode = MobMode::ZERO;
+ mob_info.speed = 300_ms;
+ mob_info.adelay = 1000_ms;
+ mob_info.amotion = 500_ms;
+ mob_info.dmotion = 500_ms;
+ for (int i = 0; i < MaxDrops; i++)
+ {
+ mob_info.dropitem[i].nameid = ItemNameId();
+ mob_info.dropitem[i].p.num = 0;
}
return 0;
}
@@ -3654,56 +3658,55 @@ bool mob_readdb(ZString filename)
continue;
}
- if (get_mob_db(mob_class).base_exp < 0)
+ if (mdbv.base_exp < 0)
{
PRINTF("bad mob line: Xp needs to be greater than 0. %s\n"_fmt, line);
rv = false;
continue;
}
- if (get_mob_db(mob_class).base_exp > 1000000000)
+ if (mdbv.base_exp > 1000000000)
{
PRINTF("bad mob line: Xp needs to be less than 1000000000. %s\n"_fmt, line);
rv = false;
continue;
}
- if (get_mob_db(mob_class).job_exp < 0)
+ if (mdbv.job_exp < 0)
{
PRINTF("bad mob line: Job Xp needs to be greater than 0. %s\n"_fmt, line);
rv = false;
continue;
}
- if (get_mob_db(mob_class).job_exp > 1000000000)
+ if (mdbv.job_exp > 1000000000)
{
PRINTF("bad mob line: Job Xp needs to be less than 1000000000. %s\n"_fmt, line);
rv = false;
continue;
}
- // TODO move this lower
- get_mob_db(mob_class) = std::move(mdbv);
-
for (int i = 0; i < MaxDrops; i++)
{
- int rate = get_mob_db(mob_class).dropitem[i].p.num;
+ int rate = mdbv.dropitem[i].p.num;
if (rate < 1) rate = 1;
if (rate > 10000) rate = 10000;
- get_mob_db(mob_class).dropitem[i].p.num = rate;
+ mdbv.dropitem[i].p.num = rate;
}
- get_mob_db(mob_class).skills.clear();
+ mdbv.skills.clear();
+
+ mdbv.hair = 0;
+ mdbv.hair_color = 0;
+ mdbv.weapon = 0;
+ mdbv.shield = ItemNameId();
+ mdbv.head_top = ItemNameId();
+ mdbv.head_mid = ItemNameId();
+ mdbv.head_buttom = ItemNameId();
+ mdbv.clothes_color = 0; //Add for player monster dye - Valaris
- get_mob_db(mob_class).hair = 0;
- get_mob_db(mob_class).hair_color = 0;
- get_mob_db(mob_class).weapon = 0;
- get_mob_db(mob_class).shield = ItemNameId();
- get_mob_db(mob_class).head_top = ItemNameId();
- get_mob_db(mob_class).head_mid = ItemNameId();
- get_mob_db(mob_class).head_buttom = ItemNameId();
- get_mob_db(mob_class).clothes_color = 0; //Add for player monster dye - Valaris
+ if (mdbv.base_exp == 0)
+ mdbv.base_exp = mob_gen_exp(&mdbv);
- if (get_mob_db(mob_class).base_exp == 0)
- get_mob_db(mob_class).base_exp = mob_gen_exp(&get_mob_db(mob_class));
+ get_mob_db(mob_class) = std::move(mdbv);
}
PRINTF("read %s done\n"_fmt, filename);
}
diff --git a/src/map/npc-parse.cpp b/src/map/npc-parse.cpp
index df1a09a..8865ea6 100644
--- a/src/map/npc-parse.cpp
+++ b/src/map/npc-parse.cpp
@@ -273,15 +273,16 @@ bool npc_load_monster(ast::npc::Monster& monster)
md->bl_m = m;
md->bl_x = x;
md->bl_y = y;
- MobName expected = get_mob_db(mob_class).jname;
+ const mob_db_& mob_info = get_mob_db(mob_class);
+ MobName expected = mob_info.jname;
if (monster.name.data != expected)
{
monster.name.span.warning(STRPRINTF("Visible label/jname should match: %s"_fmt, expected));
}
if (monster.name.data == ENGLISH_NAME)
- md->name = get_mob_db(mob_class).name;
+ md->name = mob_info.name;
else if (monster.name.data == JAPANESE_NAME)
- md->name = get_mob_db(mob_class).jname;
+ md->name = mob_info.jname;
else
md->name = monster.name.data;
diff --git a/src/map/npc.cpp b/src/map/npc.cpp
index a6d3dda..0a7bfc2 100644
--- a/src/map/npc.cpp
+++ b/src/map/npc.cpp
@@ -1006,7 +1006,8 @@ int npc_selllist(dumb_ptr<map_session_data> sd,
OMATCH_BEGIN_SOME (sdidn, sd->inventory_data[item_list[i].ioff2.unshift()])
{
- if (bool(sdidn->mode & ItemMode::NO_SELL_TO_NPC))
+ GmLevel gmlvl = pc_isGM(sd);
+ if (bool(sdidn->mode & ItemMode::NO_SELL_TO_NPC) && gmlvl.get_all_bits() < 60)
{
//clif_displaymessage(sd->sess, "This item can't be sold to an NPC."_s);
// M+ already outputs "Unable to sell unsellable item." on return value 3.
diff --git a/src/map/pc.cpp b/src/map/pc.cpp
index b6bce38..ecfd50e 100644
--- a/src/map/pc.cpp
+++ b/src/map/pc.cpp
@@ -870,6 +870,12 @@ int pc_authok(AccountId id, int login_id2, ClientVersion client_version,
sd->quick_regeneration_hp.amount = 0;
sd->quick_regeneration_sp.amount = 0;
sd->heal_xp = 0;
+ sd->max_weight_override = 0;
+ sd->activity.kills = 0;
+ sd->activity.casts = 0;
+ sd->activity.items_used = 0;
+ sd->activity.tiles_walked = 0;
+ sd->activity.attacks = 0;
sd->canact_tick = tick;
sd->canmove_tick = tick;
sd->attackabletime = tick;
@@ -1121,7 +1127,7 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
b_attackrange, b_matk1, b_matk2, b_mdef, b_mdef2;
int b_base_atk;
int bl;
- int aspd_rate, refinedef = 0;
+ int aspd_rate, speed_rate, refinedef = 0;
int str, dstr, dex;
int b_pvpchannel = 0;
@@ -1473,6 +1479,7 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
}
aspd_rate = sd->aspd_rate;
+ speed_rate = sd->speed_rate;
//攻撃速度増加 | Increased attack speed
@@ -1483,6 +1490,9 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
sd->hit += skill_power(sd, SkillID::AC_OWL) / 10; // 20 for 200
}
+ if (sd->max_weight_override)
+ sd->max_weight = sd->max_weight_override;
+
sd->max_weight += 1000;
bl = sd->status.base_level;
@@ -1548,12 +1558,15 @@ int pc_calcstatus(dumb_ptr<map_session_data> sd, int first)
/* Slow down if protected */
- if (sd->sc_data[StatusChange::SC_PHYS_SHIELD].timer)
- aspd_rate += sd->sc_data[StatusChange::SC_PHYS_SHIELD].val1;
+ if (sd->sc_data[StatusChange::SC_PHYS_SHIELD].timer || sd->sc_data[StatusChange::SC_PHYS_SHIELD_ITEM].timer)
+ aspd_rate += std::max(sd->sc_data[StatusChange::SC_PHYS_SHIELD].val1, sd->sc_data[StatusChange::SC_PHYS_SHIELD_ITEM].val1); // highest value is taken here but serverdata should make sure only one of those is active
+
+ if (sd->sc_data[StatusChange::SC_SLOWMOVE].timer)
+ speed_rate += sd->sc_data[StatusChange::SC_SLOWMOVE].val1;
}
- if (sd->speed_rate != 100)
- sd->speed = sd->speed * sd->speed_rate / 100;
+ if (speed_rate != 100)
+ sd->speed = sd->speed * speed_rate / 100;
sd->speed = std::max(sd->speed, 1_ms);
if (sd->speed_cap < interval_t::zero())
sd->speed_cap = interval_t::zero();
@@ -1894,6 +1907,14 @@ int pc_bonus(dumb_ptr<map_session_data> sd, SP type, int val)
if (!sd->state.lr_flag_is_arrow_2)
sd->base_weapon_delay_adjust += interval_t(val);
break;
+ case SP::MAXWEIGHT:
+ if (!sd->state.lr_flag_is_arrow_2)
+ sd->max_weight = val;
+ break;
+ case SP::MAXWEIGHT_ADD:
+ if (!sd->state.lr_flag_is_arrow_2)
+ sd->max_weight += val;
+ break;
default:
if (battle_config.error_log)
PRINTF("pc_bonus: unknown type %d %d !\n"_fmt,
@@ -3721,6 +3742,9 @@ int pc_readparam(dumb_ptr<block_list> bl, SP type)
case SP::MAXWEIGHT:
val = sd ? sd->max_weight : 0;
break;
+ case SP::MAXWEIGHT_OVERRIDE:
+ val = sd ? sd->max_weight_override : 0;
+ break;
case SP::BASEEXP:
val = sd ? sd->status.base_exp : 0;
break;
@@ -4048,6 +4072,11 @@ int pc_setparam(dumb_ptr<block_list> bl, SP type, int val)
sd->max_weight = val;
clif_updatestatus(sd, type);
break;
+ case SP::MAXWEIGHT_OVERRIDE:
+ nullpo_retz(sd);
+ sd->max_weight_override = val;
+ pc_calcstatus(sd, (int)CalcStatusKind::NORMAL_RECALC);
+ break;
case SP::HP:
nullpo_retz(sd);
// TODO: mob mutation
diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp
index 1efa006..58bca90 100644
--- a/src/map/script-fun.cpp
+++ b/src/map/script-fun.cpp
@@ -1920,7 +1920,7 @@ void builtin_countitem(ScriptState *st)
else
{
if (battle_config.error_log)
- PRINTF("wrong item ID: countitem (%i)\n"_fmt, nameid);
+ PRINTF("builtin_countitem: no item ID\n"_fmt);
}
push_int<ScriptDataInt>(st->stack, count);
@@ -2491,9 +2491,9 @@ void builtin_end(ScriptState *st)
{
dumb_ptr<npc_data> nd = map_id_is_npc(st->oid);
if(nd)
- PRINTF("Deprecated: close in a callfunc or callsub! @ %s\n"_fmt, nd->name);
+ PRINTF("Deprecated: end in a callfunc or callsub! @ %s\n"_fmt, nd->name);
else
- PRINTF("Deprecated: close in a callfunc or callsub! (no npc)\n"_fmt);
+ PRINTF("Deprecated: end in a callfunc or callsub! (no npc)\n"_fmt);
}
st->state = ScriptEndState::END;
}
@@ -2683,6 +2683,22 @@ void builtin_getexp(ScriptState *st)
*------------------------------------------
*/
static
+int get_mob_drop_nameid(Species mob_id, int index)
+{
+ return unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[index].nameid);
+}
+static
+int get_mob_drop_percent(Species mob_id, int index)
+{
+ return get_mob_db(mob_id).dropitem[index].p.num;
+}
+static
+AString get_mob_drop_name(Species mob_id, int index)
+{
+ Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[index].nameid));
+ return i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
+}
+static
void builtin_mobinfo(ScriptState *st)
{
Species mob_id = wrap<Species>(conv_num(st, &AARG(0)));
@@ -2697,6 +2713,19 @@ void builtin_mobinfo(ScriptState *st)
return;
}
+#define CASE_MobInfo_DROPID(index) \
+ MobInfo::DROPID##index: info = get_mob_drop_nameid(mob_id, index)
+
+#define CASE_MobInfo_DROPPERCENT(index) \
+ MobInfo::DROPPERCENT##index: info = get_mob_drop_percent(mob_id, index)
+
+#define CASE_MobInfo_DROPNAME(index) \
+ MobInfo::DROPNAME##index: \
+ { \
+ info_str = get_mob_drop_name(mob_id, index); \
+ mode = 1; \
+ }
+
switch (request)
{
case MobInfo::ID:
@@ -2800,142 +2829,57 @@ void builtin_mobinfo(ScriptState *st)
case MobInfo::MUTATION_POWER:
info = get_mob_db(mob_id).mutation_power;
break;
- case MobInfo::DROPID0:
- info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[0].nameid);
- break;
- case MobInfo::DROPNAME0:
- {
- Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[0].nameid));
- info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
- mode = 1;
- }
- break;
- case MobInfo::DROPPERCENT0:
- info = get_mob_db(mob_id).dropitem[0].p.num;
- break;
- case MobInfo::DROPID1:
- info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[0].nameid);
- break;
- case MobInfo::DROPNAME1:
- {
- Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[0].nameid));
- info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
- mode = 1;
- }
- break;
- case MobInfo::DROPPERCENT1:
- info = get_mob_db(mob_id).dropitem[0].p.num;
- break;
- case MobInfo::DROPID2:
- info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[1].nameid);
- break;
- case MobInfo::DROPNAME2:
- {
- Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[1].nameid));
- info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
- mode = 1;
- }
- break;
- case MobInfo::DROPPERCENT2:
- info = get_mob_db(mob_id).dropitem[1].p.num;
- break;
- case MobInfo::DROPID3:
- info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[2].nameid);
- break;
- case MobInfo::DROPNAME3:
- {
- Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[2].nameid));
- info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
- mode = 1;
- }
- break;
- case MobInfo::DROPPERCENT3:
- info = get_mob_db(mob_id).dropitem[2].p.num;
- break;
- case MobInfo::DROPID4:
- info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[3].nameid);
- break;
- case MobInfo::DROPNAME4:
- {
- Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[3].nameid));
- info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
- mode = 1;
- }
- break;
- case MobInfo::DROPPERCENT4:
- info = get_mob_db(mob_id).dropitem[3].p.num;
- break;
- case MobInfo::DROPID5:
- info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[4].nameid);
- break;
- case MobInfo::DROPNAME5:
- {
- Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[4].nameid));
- info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
- mode = 1;
- }
- break;
- case MobInfo::DROPPERCENT5:
- info = get_mob_db(mob_id).dropitem[4].p.num;
- break;
- case MobInfo::DROPID6:
- info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[5].nameid);
- break;
- case MobInfo::DROPNAME6:
- {
- Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[5].nameid));
- info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
- mode = 1;
- }
- break;
- case MobInfo::DROPPERCENT6:
- info = get_mob_db(mob_id).dropitem[5].p.num;
- break;
- case MobInfo::DROPID7:
- info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[6].nameid);
- break;
- case MobInfo::DROPNAME7:
- {
- Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[6].nameid));
- info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
- mode = 1;
- }
- break;
- case MobInfo::DROPPERCENT7:
- info = get_mob_db(mob_id).dropitem[6].p.num;
- break;
- case MobInfo::DROPID8:
- info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[7].nameid);
- break;
- case MobInfo::DROPNAME8:
- {
- Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[7].nameid));
- info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
- mode = 1;
- }
- break;
- case MobInfo::DROPPERCENT8:
- info = get_mob_db(mob_id).dropitem[7].p.num;
- break;
- case MobInfo::DROPID9:
- info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[7].nameid);
- break;
- case MobInfo::DROPNAME9:
- {
- Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[7].nameid));
- info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
- mode = 1;
- }
- break;
- case MobInfo::DROPPERCENT9:
- info = get_mob_db(mob_id).dropitem[7].p.num;
- break;
+
+ case CASE_MobInfo_DROPID(0); break;
+ case CASE_MobInfo_DROPNAME(0); break;
+ case CASE_MobInfo_DROPPERCENT(0); break;
+
+ case CASE_MobInfo_DROPID(1); break;
+ case CASE_MobInfo_DROPNAME(1); break;
+ case CASE_MobInfo_DROPPERCENT(1); break;
+
+ case CASE_MobInfo_DROPID(2); break;
+ case CASE_MobInfo_DROPNAME(2); break;
+ case CASE_MobInfo_DROPPERCENT(2); break;
+
+ case CASE_MobInfo_DROPID(3); break;
+ case CASE_MobInfo_DROPNAME(3); break;
+ case CASE_MobInfo_DROPPERCENT(3); break;
+
+ case CASE_MobInfo_DROPID(4); break;
+ case CASE_MobInfo_DROPNAME(4); break;
+ case CASE_MobInfo_DROPPERCENT(4); break;
+
+ case CASE_MobInfo_DROPID(5); break;
+ case CASE_MobInfo_DROPNAME(5); break;
+ case CASE_MobInfo_DROPPERCENT(5); break;
+
+ case CASE_MobInfo_DROPID(6); break;
+ case CASE_MobInfo_DROPNAME(6); break;
+ case CASE_MobInfo_DROPPERCENT(6); break;
+
+ case CASE_MobInfo_DROPID(7); break;
+ case CASE_MobInfo_DROPNAME(7); break;
+ case CASE_MobInfo_DROPPERCENT(7); break;
+
+ case CASE_MobInfo_DROPID(8); break;
+ case CASE_MobInfo_DROPNAME(8); break;
+ case CASE_MobInfo_DROPPERCENT(8); break;
+
+ case CASE_MobInfo_DROPID(9); break;
+ case CASE_MobInfo_DROPNAME(9); break;
+ case CASE_MobInfo_DROPPERCENT(9); break;
+
default:
PRINTF("builtin_mobinfo: unknown request\n"_fmt);
push_int<ScriptDataInt>(st->stack, -1);
return;
break;
}
+#undef CASE_MobInfo_DROPID
+#undef CASE_MobInfo_DROPPERCENT
+#undef CASE_MobInfo_DROPNAME
+
if (!mode)
push_int<ScriptDataInt>(st->stack, info);
else
@@ -3022,7 +2966,9 @@ void builtin_mobinfo_droparrays(ScriptState *st)
}
for (int i = 0; i < MaxDrops; ++i)
- if (get_mob_db(mob_id).dropitem[i].nameid)
+ {
+ auto& dropitem = get_mob_db(mob_id).dropitem[i];
+ if (dropitem.nameid)
{
status = 1;
switch (request)
@@ -3030,15 +2976,15 @@ void builtin_mobinfo_droparrays(ScriptState *st)
case MobInfo_DropArrays::IDS:
if (name.startswith(".@"_s))
{
- struct script_data vd = script_data(ScriptDataInt{unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[i].nameid)});
+ struct script_data vd = script_data(ScriptDataInt{unwrap<ItemNameId>(dropitem.nameid)});
set_scope_reg(st, reg.iplus(i), &vd);
}
else
- set_reg(bl, VariableCode::VARIABLE, reg.iplus(i), unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[i].nameid));
+ set_reg(bl, VariableCode::VARIABLE, reg.iplus(i), unwrap<ItemNameId>(dropitem.nameid));
break;
case MobInfo_DropArrays::NAMES:
{
- Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[i].nameid));
+ Option<P<struct item_data>> i_data = Some(itemdb_search(dropitem.nameid));
RString item_name = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
if (name.startswith(".@"_s))
@@ -3053,11 +2999,11 @@ void builtin_mobinfo_droparrays(ScriptState *st)
case MobInfo_DropArrays::PERCENTS:
if (name.startswith(".@"_s))
{
- struct script_data vd = script_data(ScriptDataInt{get_mob_db(mob_id).dropitem[i].p.num});
+ struct script_data vd = script_data(ScriptDataInt{dropitem.p.num});
set_scope_reg(st, reg.iplus(i), &vd);
}
else
- set_reg(bl, VariableCode::VARIABLE, reg.iplus(i), get_mob_db(mob_id).dropitem[i].p.num);
+ set_reg(bl, VariableCode::VARIABLE, reg.iplus(i), dropitem.p.num);
break;
}
}
@@ -3067,7 +3013,7 @@ void builtin_mobinfo_droparrays(ScriptState *st)
status = 2;
break;
}
-
+ }
push_int<ScriptDataInt>(st->stack, status);
}
@@ -3097,16 +3043,19 @@ void builtin_getmobdrops(ScriptState *st)
status = 1;
+ const mob_db_& mob_info = get_mob_db(mob_id);
for (; i < MaxDrops; ++i)
- if (get_mob_db(mob_id).dropitem[i].nameid)
+ {
+ auto& dropitem = mob_info.dropitem[i];
+ if (dropitem.nameid)
{
- set_reg(bl, VariableCode::VARIABLE, SIR::from(variable_names.intern("$@MobDrop_item"_s), i), get_mob_db(mob_id).dropitem[i].p.num);
+ set_reg(bl, VariableCode::VARIABLE, SIR::from(variable_names.intern("$@MobDrop_item"_s), i), dropitem.p.num);
- Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[i].nameid));
+ Option<P<struct item_data>> i_data = Some(itemdb_search(dropitem.nameid));
RString item_name = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
set_reg(bl, VariableCode::VARIABLE, SIR::from(variable_names.intern("$@MobDrop_name$"_s), i), item_name);
- set_reg(bl, VariableCode::VARIABLE, SIR::from(variable_names.intern("$@MobDrop_rate"_s), i), get_mob_db(mob_id).dropitem[i].p.num);
+ set_reg(bl, VariableCode::VARIABLE, SIR::from(variable_names.intern("$@MobDrop_rate"_s), i), dropitem.p.num);
}
else
{
@@ -3114,6 +3063,7 @@ void builtin_getmobdrops(ScriptState *st)
status = 2;
break;
}
+ }
if (status == 1)
set_reg(bl, VariableCode::VARIABLE, SIR::from(variable_names.intern("$@MobDrop_count"_s), 0), i);
@@ -3920,6 +3870,7 @@ void builtin_sc_start(ScriptState *st)
// all those use ms so this checks for < 1s are not needed on those
// and it would break the cooldown symbol since many spells have cooldowns less than 1s
case StatusChange::SC_PHYS_SHIELD:
+ case StatusChange::SC_PHYS_SHIELD_ITEM:
case StatusChange::SC_MBARRIER:
case StatusChange::SC_COOLDOWN:
case StatusChange::SC_COOLDOWN_MG:
@@ -3929,7 +3880,11 @@ void builtin_sc_start(ScriptState *st)
case StatusChange::SC_COOLDOWN_ENCH:
case StatusChange::SC_COOLDOWN_KOY:
case StatusChange::SC_COOLDOWN_UPMARMU:
- break;
+ case StatusChange::SC_COOLDOWN_SG:
+ case StatusChange::SC_COOLDOWN_CG:
+ case StatusChange::SC_SLOWMOVE:
+ case StatusChange::SC_CANTMOVE:
+ break;
default:
// work around old behaviour of:
@@ -4362,9 +4317,14 @@ void builtin_getitemlink(ScriptState *st)
struct script_data *data;
AString buf;
data = &AARG(0);
- ZString name = conv_str(st, data);
+ Option<P<struct item_data>> item_data_ = None;
+
+ get_val(st, data);
+ if (data->is<ScriptDataStr>())
+ item_data_ = itemdb_searchname(conv_str(st, data));
+ else
+ item_data_ = itemdb_exists(wrap<ItemNameId>(conv_num(st, data)));
- Option<P<struct item_data>> item_data_ = itemdb_searchname(name);
OMATCH_BEGIN (item_data_)
{
OMATCH_CASE_SOME (item_data)
@@ -5524,6 +5484,86 @@ void builtin_getmapmaxy(ScriptState *st)
}
/*==========================================
+ * Get the hash of a map
+ *------------------------------------------
+ */
+static
+void builtin_getmaphash(ScriptState *st)
+{
+ MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);
+ push_int<ScriptDataInt>(st->stack, m->hash);
+}
+
+/*==========================================
+ * Get the map name from a hash
+ *------------------------------------------
+ */
+static
+void builtin_getmapnamefromhash(ScriptState *st)
+{
+ int hash = conv_num(st, &AARG(0));
+ MapName mapname;
+ for (auto& mit : maps_db)
+ {
+ map_local *ml = static_cast<map_local *>(mit.second.get());
+ if (ml->hash == hash)
+ {
+ mapname = ml->name_;
+ break;
+ }
+ }
+ push_str<ScriptDataStr>(st->stack, mapname);
+}
+
+/*==========================================
+ * Look if a map exists
+ * return value:
+ * 0 = map does not exist
+ * 1 = map exists
+ *------------------------------------------
+ */
+static
+void builtin_mapexists(ScriptState *st)
+{
+ MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
+ push_int<ScriptDataInt>(st->stack, map_mapname2mapid(mapname).is_some());
+}
+
+/*==========================================
+ * Returns number of available maps
+ *------------------------------------------
+ */
+static
+void builtin_numberofmaps(ScriptState *st)
+{
+ push_int<ScriptDataInt>(st->stack, maps_db.size());
+}
+
+/*==========================================
+ * Get the map name of a specific maps_db index
+ *------------------------------------------
+ */
+static
+void builtin_getmapnamebyindex(ScriptState *st)
+{
+ int index = conv_num(st, &AARG(0));
+ int count = 0;
+
+ for (auto& mit : maps_db)
+ {
+ if (count == index)
+ {
+ push_str<ScriptDataStr>(st->stack, mit.second->name_);
+ return;
+ }
+ ++count;
+ }
+
+ push_str<ScriptDataStr>(st->stack, ""_s);
+}
+
+/*==========================================
* Get the NPC's info
*------------------------------------------
*/
@@ -5787,6 +5827,11 @@ BuiltinFunction builtin_functions[] =
BUILTIN(getmap, "?"_s, 's'),
BUILTIN(getmapmaxx, "M"_s, 'i'),
BUILTIN(getmapmaxy, "M"_s, 'i'),
+ BUILTIN(getmaphash, "M"_s, 'i'),
+ BUILTIN(getmapnamefromhash, "i"_s, 's'),
+ BUILTIN(mapexists, "M"_s, 'i'),
+ BUILTIN(numberofmaps, ""_s, 'i'),
+ BUILTIN(getmapnamebyindex, "i"_s, 's'),
BUILTIN(mapexit, ""_s, '\0'),
BUILTIN(freeloop, "i"_s, '\0'),
BUILTIN(if_then_else, "iii"_s, 'v'),
diff --git a/src/map/script-parse.py b/src/map/script-parse.py
index 199e348..3346b92 100644
--- a/src/map/script-parse.py
+++ b/src/map/script-parse.py
@@ -106,7 +106,7 @@ class ScriptBuffer(object):
code_begin = code['_M_impl']['_M_start']
code_end = code['_M_impl']['_M_finish']
code_size = int(code_end - code_begin)
- r = iter(range(code_size))
+ r = iter(list(range(code_size)))
for i in r:
buf = []
for label in labels_dict.get(i, []):
diff --git a/src/map/skill.cpp b/src/map/skill.cpp
index 87bbbda..f70a626 100644
--- a/src/map/skill.cpp
+++ b/src/map/skill.cpp
@@ -201,9 +201,9 @@ int skill_additional_effect(dumb_ptr<block_list> src, dumb_ptr<block_list> bl,
}
sc_def_phys_shield_spell = 0;
- if (battle_get_sc_data(bl)[StatusChange::SC_PHYS_SHIELD].timer)
+ if (battle_get_sc_data(bl)[StatusChange::SC_PHYS_SHIELD].timer || battle_get_sc_data(bl)[StatusChange::SC_PHYS_SHIELD_ITEM].timer)
sc_def_phys_shield_spell =
- battle_get_sc_data(bl)[StatusChange::SC_PHYS_SHIELD].val1;
+ std::max(battle_get_sc_data(bl)[StatusChange::SC_PHYS_SHIELD].val1, battle_get_sc_data(bl)[StatusChange::SC_PHYS_SHIELD_ITEM].val1); // highest value is taken here but serverdata should make sure only one of those is active
// 対象の耐性 | Target resistance
luk = battle_get_luk(bl);
@@ -753,7 +753,9 @@ void skill_status_change_end(dumb_ptr<block_list> bl, StatusChange type, TimerDa
case StatusChange::SC_ATKPOT: /* attack potion [Valaris] */
case StatusChange::SC_MATKPOT: /* magic attack potion [Valaris] */
case StatusChange::SC_PHYS_SHIELD:
+ case StatusChange::SC_PHYS_SHIELD_ITEM:
case StatusChange::SC_HASTE:
+ case StatusChange::SC_SLOWMOVE:
calc_flag = 1;
break;
@@ -765,6 +767,9 @@ void skill_status_change_end(dumb_ptr<block_list> bl, StatusChange type, TimerDa
case StatusChange::SC_COOLDOWN_ENCH:
case StatusChange::SC_COOLDOWN_KOY:
case StatusChange::SC_COOLDOWN_UPMARMU:
+ case StatusChange::SC_COOLDOWN_SG:
+ case StatusChange::SC_COOLDOWN_CG:
+ case StatusChange::SC_CANTMOVE:
break;
/* option2 */
@@ -1030,7 +1035,9 @@ int skill_status_effect(dumb_ptr<block_list> bl, StatusChange type,
case StatusChange::SC_HASTE:
case StatusChange::SC_PHYS_SHIELD:
+ case StatusChange::SC_PHYS_SHIELD_ITEM:
case StatusChange::SC_MBARRIER:
+ case StatusChange::SC_SLOWMOVE:
calc_flag = 1;
break;
case StatusChange::SC_HALT_REGENERATE:
@@ -1043,6 +1050,9 @@ int skill_status_effect(dumb_ptr<block_list> bl, StatusChange type,
case StatusChange::SC_COOLDOWN_ENCH:
case StatusChange::SC_COOLDOWN_KOY:
case StatusChange::SC_COOLDOWN_UPMARMU:
+ case StatusChange::SC_COOLDOWN_SG:
+ case StatusChange::SC_COOLDOWN_CG:
+ case StatusChange::SC_CANTMOVE:
break;
case StatusChange::SC_FLYING_BACKPACK:
updateflag = SP::WEIGHT;
diff --git a/src/map/storage.cpp b/src/map/storage.cpp
index cab3f0f..dc1fe62 100644
--- a/src/map/storage.cpp
+++ b/src/map/storage.cpp
@@ -188,7 +188,8 @@ int storage_storageadd(dumb_ptr<map_session_data> sd, IOff0 index, int amount)
OMATCH_BEGIN_SOME (sdidn, sd->inventory_data[index])
{
- if (bool(sdidn->mode & ItemMode::NO_STORAGE))
+ GmLevel gmlvl = pc_isGM(sd);
+ if (bool(sdidn->mode & ItemMode::NO_STORAGE) && gmlvl.get_all_bits() < 60)
{
clif_displaymessage(sd->sess, "This item can't be stored."_s);
return 0;
diff --git a/src/mmo/clif.t.hpp b/src/mmo/clif.t.hpp
index f8350a7..c1f7ed3 100644
--- a/src/mmo/clif.t.hpp
+++ b/src/mmo/clif.t.hpp
@@ -265,6 +265,8 @@ enum class SP : uint16_t
WEIGHT = 24,
// sent to client
MAXWEIGHT = 25,
+ MAXWEIGHT_ADD = 26,
+ MAXWEIGHT_OVERRIDE = 27,
// sent to client
USTR = 32,
diff --git a/src/mmo/skill.t.hpp b/src/mmo/skill.t.hpp
index 782980c..bab10f4 100644
--- a/src/mmo/skill.t.hpp
+++ b/src/mmo/skill.t.hpp
@@ -60,11 +60,17 @@ enum class StatusChange : uint16_t
SC_COOLDOWN_ENCH = 76, // Enchanter cooldown
SC_COOLDOWN_KOY = 77, // Koyntety cooldown
SC_COOLDOWN_UPMARMU = 78, // Upmarmu cooldown
+ SC_COOLDOWN_SG = 79, // Stone Golem cooldown
+ SC_COOLDOWN_CG = 80, // Coal Golem cooldown
SC_POISON = 132, // bad; actually used
+ SC_SLOWMOVE = 133, // slows down movement
+ SC_CANTMOVE = 134, // stops all movement
SC_ATKPOT = 185, // item script
- SC_MATKPOT = 186, // unused, but kept for parallel
+ SC_MATKPOT = 186, // `Matk' spell from items (val1 : power)
+
+ SC_PHYS_SHIELD_ITEM = 193, // `Protect' spell from items, reduce damage (val1: power) can't be chancelled with detsanc
// Added for Fate's spells
SC_HIDE = 194, // Hide from `detect' magic (PCs only)
diff --git a/src/mmo/version.cpp b/src/mmo/version.cpp
index f91b748..c75c25c 100644
--- a/src/mmo/version.cpp
+++ b/src/mmo/version.cpp
@@ -20,7 +20,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
-#include "../conf/version.hpp"
+#include "src/conf/version.hpp"
#include "../strings/xstring.hpp"
diff --git a/src/shared/lib.cpp b/src/shared/lib.cpp
index d7d1a4e..37b2e93 100644
--- a/src/shared/lib.cpp
+++ b/src/shared/lib.cpp
@@ -18,7 +18,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
-#include "../conf/install.hpp"
+#include "src/conf/install.hpp"
#include "../strings/literal.hpp"
#include "../strings/astring.hpp"
@@ -35,35 +35,35 @@
namespace tmwa
{
static
- void try_read(const io::DirFd& dirfd, ZString filename)
+ void try_read(const io::DirFd& dirfd, LString dir_path, ZString filename)
{
io::ReadFile rf(dirfd, filename);
if (!rf.is_open())
{
- FPRINTF(stderr, "Could not open %s\n"_fmt, filename);
+ FPRINTF(stderr, "Could not open %s/%s\n"_fmt, dir_path, filename);
abort();
}
AString line;
if (!rf.getline(line))
{
- FPRINTF(stderr, "Could not read from %s\n"_fmt, filename);
+ FPRINTF(stderr, "Could not read from %s/%s\n"_fmt, dir_path, filename);
abort();
}
}
static
- void try_write(const io::DirFd& dirfd, ZString filename)
+ void try_write(const io::DirFd& dirfd, LString dir_path, ZString filename)
{
io::WriteFile wf(dirfd, filename);
if (!wf.is_open())
{
- FPRINTF(stderr, "Could not open %s\n"_fmt, filename);
+ FPRINTF(stderr, "Could not open %s/%s\n"_fmt, dir_path, filename);
abort();
}
wf.put_line("Hello, World!"_s);
if (!wf.close())
{
- FPRINTF(stderr, "Could not write to %s\n"_fmt, filename);
+ FPRINTF(stderr, "Could not write to %s/%s\n"_fmt, dir_path, filename);
abort();
}
}
@@ -77,13 +77,17 @@ namespace tmwa
io::DirFd root(portable_root);
- io::DirFd etc(root, PACKAGESYSCONFDIR.xslice_t(portable));
- io::DirFd var(root, PACKAGELOCALSTATEDIR.xslice_t(portable));
- io::DirFd share(root, PACKAGEDATADIR.xslice_t(portable));
+ LString etc_path = PACKAGESYSCONFDIR.xslice_t(portable);
+ LString var_path = PACKAGELOCALSTATEDIR.xslice_t(portable);
+ LString share_path = PACKAGEDATADIR.xslice_t(portable);
- try_read(etc, "shared.conf"_s);
- try_read(share, "shared.data"_s);
- try_write(var, "shared.test"_s);
+ io::DirFd etc(root, etc_path);
+ io::DirFd var(root, var_path);
+ io::DirFd share(root, share_path);
+
+ try_read(etc, etc_path, "shared.conf"_s);
+ try_read(share, share_path, "shared.data"_s);
+ try_write(var, var_path, "shared.test"_s);
// io::FD::open();
}
diff --git a/tools/bs-align b/tools/bs-align
index 6582298..0caf649 100755
--- a/tools/bs-align
+++ b/tools/bs-align
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- encoding: utf-8
## bs-align.py - Filter to align trailing line continuations
##
diff --git a/tools/colorize b/tools/colorize
index ce6f410..205a1bc 100755
--- a/tools/colorize
+++ b/tools/colorize
@@ -1,6 +1,4 @@
-#!/usr/bin/env python
-
-from __future__ import print_function
+#!/usr/bin/env python3
import os
import sys
diff --git a/tools/config.py b/tools/config.py
index f87fe77..22ece0b 100755
--- a/tools/config.py
+++ b/tools/config.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# coding: utf-8
# config.py - generator for config file parsers
@@ -20,8 +20,6 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import print_function
-
import glob
import os
@@ -259,7 +257,7 @@ class Group(object):
short_cpp_name = '%s.cpp' % var_name
cpp_name = os.path.join(path, short_cpp_name)
- values = sorted(self.options.values(), key=lambda o: o.name)
+ values = sorted(list(self.options.values()), key=lambda o: o.name)
desc = 'Config for %s::%s' % (namespace_name, self.name)
with OpenWrite(hpp_name) as hpp, \
@@ -384,7 +382,7 @@ class Realm(object):
return rv
def dump(self):
- for g in self.groups.values():
+ for g in list(self.groups.values()):
g.dump_in(self.path, self.path.split('/')[-1])
class Everything(object):
@@ -401,7 +399,7 @@ class Everything(object):
def dump(self):
for g in glob.glob('src/*/*_conf.[ch]pp'):
os.rename(g, g + '.old')
- for v in self.realms.values():
+ for v in list(self.realms.values()):
v.dump()
for g in glob.glob('src/*/*_conf.[ch]pp.old'):
print('Obsolete: %s' % g)
diff --git a/tools/debug-debug-scripts b/tools/debug-debug-scripts
index 2112a6e..5ba7ecb 100755
--- a/tools/debug-debug-scripts
+++ b/tools/debug-debug-scripts
@@ -1,6 +1,5 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# encoding: utf-8
-from __future__ import print_function
copyright = '''
// Copyright © 2014 Ben Longbons <b.r.longbons@gmail.com>
@@ -35,6 +34,12 @@ error = False
def eprint(s):
print('Error:', s, file=sys.stderr)
+# Implement an equivalent to Python 2's execfile for Python 3
+def execfile(filename, globals=None, locals=None):
+ with open(filename, 'r') as f:
+ code = compile(f.read(), filename, 'exec')
+ exec(code, globals, locals)
+
def get_classes_from_file(a):
global error
d = {}
diff --git a/tools/debug-debug.gdb b/tools/debug-debug.gdb
index b50a49b..5bf3a57 100644
--- a/tools/debug-debug.gdb
+++ b/tools/debug-debug.gdb
@@ -9,7 +9,7 @@ gdb.execute('file %s' % file_to_load)
end
set logging file /dev/null
set logging redirect on
-set logging off
+set logging enabled off
python
import re
@@ -41,7 +41,7 @@ set print elements 9999
set print frame-arguments none
set python print-stack full
-set logging on
+set logging enabled on
# Workaround "Function... not defined in.." (breakpoints not found) (GDB bug)
# https://sourceware.org/bugzilla/show_bug.cgi?id=15962
# In some gdb versions rbreak works, in some break does.
@@ -55,7 +55,7 @@ if bpoint.pending:
gdb.execute("rbreak do_breakpoint")
end
-set logging off
+set logging enabled off
commands
silent
diff --git a/tools/indenter b/tools/indenter
index 0d93543..ee000da 100755
--- a/tools/indenter
+++ b/tools/indenter
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- encoding: utf-8
## indenter.py - Top-level indenter for all files
##
@@ -20,10 +20,9 @@
## along with this program. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import print_function
from collections import namedtuple
-import cStringIO
+import io
import string
import subprocess
import sys
@@ -114,7 +113,7 @@ class Reader(object):
self._column += 1
def string_reader(s, name='<string>', line=1, column=1):
- return Reader(name, cStringIO.StringIO(s), line, column)
+ return Reader(name, io.StringIO(s), line, column)
def take_while(b, r, s):
assert isinstance(b, bytearray)
diff --git a/tools/pp-indent b/tools/pp-indent
index 9be9a03..622614a 100755
--- a/tools/pp-indent
+++ b/tools/pp-indent
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# -*- encoding: utf-8
## pp-indent - Filter to apply indentation to preprocessor statements
##
diff --git a/tools/protocol.py b/tools/protocol.py
index cf89a16..d278884 100755
--- a/tools/protocol.py
+++ b/tools/protocol.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# coding: utf-8
# protocol.py - generator for entire TMWA network protocol
@@ -20,20 +20,12 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from __future__ import print_function
-
import glob
-import itertools
import os
-from pipes import quote
+from shlex import quote
from posixpath import relpath
from weakref import ref as wr
-try:
- unicode
-except NameError:
- unicode = str
-
## For various reasons this is all one file, so let's navigate with a
##
## Table of Contents
@@ -91,7 +83,7 @@ class OpenWrite(object):
self.filename = filename
def __enter__(self):
- self.handle = open(self.filename + '.tmp', 'w')
+ self.handle = open(self.filename + '.tmp', 'w', encoding='utf-8')
return self.handle
def __exit__(self, ty, v, tb):
@@ -116,30 +108,30 @@ class OpenWrite(object):
# TOC_
def gvq(s):
- return u'"%s"' % s.replace(u'"', u'\\"')
+ return '"%s"' % s.replace('"', '\\"')
def gva(d):
if d:
- return u' [%s]' % u', '.join(u'%s=%s' % (ak, gvq(av)) for (ak, av) in sorted(d.items()))
- return u''
+ return ' [%s]' % ', '.join('%s=%s' % (ak, gvq(av)) for (ak, av) in sorted(d.items()))
+ return ''
class Attributes(object):
- __slots__ = (u'_attributes')
+ __slots__ = ('_attributes')
def __init__(self):
self._attributes = {}
def __getitem__(self, k):
- assert isinstance(k, unicode)
+ assert isinstance(k, str)
return self._attributes[k]
def __setitem__(self, k, v):
- assert isinstance(k, unicode)
- assert isinstance(v, unicode)
+ assert isinstance(k, str)
+ assert isinstance(v, str)
self._attributes[k] = v
def __delitem__(self, k):
- assert isinstance(k, unicode)
+ assert isinstance(k, str)
del self._attributes[k]
def merge(self, *others):
@@ -150,7 +142,7 @@ class Attributes(object):
self._attributes.update(other._attributes)
class Graph(Attributes):
- __slots__ = (u'default_vertex', u'default_edge', u'_vertices', u'_edges', u'_vertex_lookup')
+ __slots__ = ('default_vertex', 'default_edge', '_vertices', '_edges', '_vertex_lookup')
def __init__(self):
Attributes.__init__(self)
@@ -161,7 +153,7 @@ class Graph(Attributes):
self._vertex_lookup = {}
def vertex(self, name, insert=True):
- assert isinstance(name, unicode)
+ assert isinstance(name, str)
vert = self._vertex_lookup.get(name)
if insert and vert is None:
vert = Vertex(name)
@@ -238,21 +230,21 @@ class Graph(Attributes):
def p(*args):
for x in args:
- out.write(unicode(x))
- out.write(u'\n')
- p(u'digraph')
- p(u'{')
+ out.write(str(x))
+ out.write('\n')
+ p('digraph')
+ p('{')
for ak, av in sorted(self._attributes.items()):
- p(u' ', ak, u'=', gvq(av), u';')
+ p(' ', ak, '=', gvq(av), ';')
for ak, av in sorted(self.default_vertex._attributes.items()):
- p(u' node [', ak, u'=', gvq(av), u'];')
+ p(' node [', ak, '=', gvq(av), '];')
for ak, av in sorted(self.default_edge._attributes.items()):
- p(u' edge [', ak, u'=', gvq(av), u'];')
+ p(' edge [', ak, '=', gvq(av), '];')
for n in sorted(self._vertices):
- p(u' ', n)
+ p(' ', n)
for _, e in sorted(self._edges.items()):
- p(u' ', e)
- p(u'}')
+ p(' ', e)
+ p('}')
def dot_str(self):
from io import StringIO
@@ -261,18 +253,18 @@ class Graph(Attributes):
return out.getvalue()
def dot_file(self, name):
- with open(name, u'w') as f:
+ with open(name, 'w') as f:
self.dot(f, False)
def preview(self, block):
from subprocess import Popen, PIPE
- proc = Popen([u'dot', u'-Txlib', u'/dev/stdin'], stdin=PIPE, universal_newlines=True)
+ proc = Popen(['dot', '-Txlib', '/dev/stdin'], stdin=PIPE, universal_newlines=True)
self.dot(proc.stdin, True)
if block:
proc.wait()
class Vertex(Attributes):
- __slots__ = (u'_key', u'_post', u'_pre', u'__weakref__')
+ __slots__ = ('_key', '_post', '_pre', '__weakref__')
def __init__(self, key):
Attributes.__init__(self)
@@ -281,13 +273,13 @@ class Vertex(Attributes):
self._pre = set()
def __str__(self):
- return u'%s%s;' % (gvq(self._key), gva(self._attributes))
+ return '%s%s;' % (gvq(self._key), gva(self._attributes))
def __lt__(self, other):
return self._key < other._key
class Edge(Attributes):
- __slots__ = (u'_from', u'_to')
+ __slots__ = ('_from', '_to')
def __init__(self, f, t):
Attributes.__init__(self)
@@ -295,7 +287,7 @@ class Edge(Attributes):
self._to = t
def __str__(self):
- return u'%s -> %s%s;' % (gvq(self._from._key), gvq(self._to._key), gva(self._attributes))
+ return '%s -> %s%s;' % (gvq(self._from._key), gvq(self._to._key), gva(self._attributes))
# TOC_TYPES
@@ -7091,7 +7083,7 @@ def partition(d):
changed = True
while changed:
changed = False
- for k, vlist in d.items():
+ for k, vlist in list(d.items()):
if vlist:
m = min(leaders[v] for v in vlist)
if m < leaders[k]:
@@ -7107,9 +7099,9 @@ def partition(d):
leaders[v] = m
followers = {}
- for k, v in leaders.items():
+ for k, v in list(leaders.items()):
followers.setdefault(v, []).append(k)
- return [set(v) for v in followers.values()]
+ return [set(v) for v in list(followers.values())]
def ids_only(vpost):
rv = [e for e in vpost if not isinstance(e, SpecialEventOrigin)]
@@ -7124,7 +7116,7 @@ def make_dots(ctx):
#p = partition({k: ids_only(v.post) for (k, v) in d.items()})
if not os.path.exists('doc-gen'):
- # generate.make will succeed if missing the wiki repo
+ # generate.mk will succeed if missing the wiki repo
# but 'make doc' will fail
return
for g in glob.glob('doc-gen/*.gv'):
@@ -7132,7 +7124,7 @@ def make_dots(ctx):
for g in glob.glob('doc-gen/Packet-*.md'):
os.rename(g, g + '.old')
- for (id, p) in d.items():
+ for (id, p) in list(d.items()):
md = 'doc-gen/Packet-0x%04x.md' % id
dot = 'doc-gen/packets-around-0x%04x.gv' % id
with OpenWrite(md) as f:
@@ -7179,16 +7171,16 @@ def make_dots(ctx):
covered_nodes = sorted(p.pre_set(d, 2) | p.post_set(d, 2))
covered_edges = [(a, b) for a in covered_nodes for b in covered_nodes if b in d[a].post]
g = Graph()
- g.default_vertex[u'shape'] = u'box'
- # g[u'layout'] = u'twopi'
- # g[u'root'] = u'0x%04x' % id
+ g.default_vertex['shape'] = 'box'
+ # g['layout'] = 'twopi'
+ # g['root'] = '0x%04x' % id
for n in covered_nodes:
- v = g.vertex(u'0x%04x' % n)
- v[u'label'] = u'Packet \\N: %s' % d[n].name
+ v = g.vertex('0x%04x' % n)
+ v['label'] = 'Packet \\N: %s' % d[n].name
if n == id:
- v[u'style'] = u'filled'
+ v['style'] = 'filled'
for (a, b) in covered_edges:
- g.edge(u'0x%04x' % a, u'0x%04x' % b)
+ g.edge('0x%04x' % a, '0x%04x' % b)
for n in covered_nodes:
# the center node will be covered specially below
if n == id:
@@ -7206,10 +7198,10 @@ def make_dots(ctx):
# don't show mere siblings unless also ancestor/descendent
count += 1
if count:
- v = g.vertex(u'0x%04x...pre' % n)
- v[u'label'] = u'%d more' % count
- v[u'style'] = u'dashed'
- g.edge(v, u'0x%04x' % n)
+ v = g.vertex('0x%04x...pre' % n)
+ v['label'] = '%d more' % count
+ v['style'] = 'dashed'
+ g.edge(v, '0x%04x' % n)
# strong forward links
count = 0
for p in d[n].post:
@@ -7218,36 +7210,36 @@ def make_dots(ctx):
elif p not in covered_nodes:
count += 1
if count:
- v = g.vertex(u'0x%04x...post' % n)
- v[u'label'] = u'%d more' % count
- v[u'style'] = u'dashed'
- g.edge(u'0x%04x' % n, v)
+ v = g.vertex('0x%04x...post' % n)
+ v['label'] = '%d more' % count
+ v['style'] = 'dashed'
+ g.edge('0x%04x' % n, v)
# for the immediate node, also cover specials and weaks
for p in d[id].pre:
# (there are no weak backward specials)
# strong backward specials
if isinstance(p, SpecialEventOrigin):
- g.edge(unicode(p.name), u'0x%04x' % id)
+ g.edge(str(p.name), '0x%04x' % id)
# weak backward nodes
elif id in d[p].xpost:
- e = g.edge(u'0x%04x' % p, u'0x%04x' % id)
- e[u'style']=u'dashed'
- e[u'weight'] = u'0'
+ e = g.edge('0x%04x' % p, '0x%04x' % id)
+ e['style']='dashed'
+ e['weight'] = '0'
for p in d[id].post:
# strong forward specials
if isinstance(p, SpecialEventOrigin):
- g.edge(u'0x%04x' % id, unicode(p.name))
+ g.edge('0x%04x' % id, str(p.name))
for p in d[id].xpost:
# weak forward specials
if isinstance(p, SpecialEventOrigin):
- e = g.edge(u'0x%04x' % id, unicode(p.name))
- e[u'style'] = u'dashed'
- e[u'weight'] = u'0'
+ e = g.edge('0x%04x' % id, str(p.name))
+ e['style'] = 'dashed'
+ e['weight'] = '0'
# weak forward nodes
elif p not in covered_nodes:
- e = g.edge(u'0x%04x' % id, u'0x%04x' % p)
- e[u'style'] = u'dashed'
- e[u'weight'] = u'0'
+ e = g.edge('0x%04x' % id, '0x%04x' % p)
+ e['style'] = 'dashed'
+ e['weight'] = '0'
g.dot(f, False)
diff --git a/version.make b/version.mk
index 73ecc83..f9bc6fd 100644
--- a/version.make
+++ b/version.mk
@@ -39,7 +39,7 @@ VENDOR_NAME := Vanilla
VENDOR_POINT := 0
# URL where the source may be found (after searching for version number).
# See AGPLv3 section 13
-VENDOR_SOURCE := https://github.com/themanaworld/tmwa
+VENDOR_SOURCE := https://git.themanaworld.org/tmw/tmwa
# Convenience
VERSION_STRING := TMWA ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH} dev${VERSION_DEVEL} +${VENDOR_POINT} (${VENDOR_NAME})
@@ -50,9 +50,38 @@ SO_SHORT := so.${ABI_VERSION}
SO_LONG := ${SO_SHORT}.${VERSION_DOTS}
# and thanks for all the fish
+# This is a phony target, so that it always runs.
+# Targets which depend on this will always have their recipes run.
+FORCE:: ;
+.PHONY: FORCE
+
+# Fully generate version.hpp here, where we have all the relevant information.
+# version.mk is included by the top level Makefile, so simply explaning how to
+# make it here will let it be built later, when needed.
+# Note that some variable substitutions are slightly different here to use the
+# name used by standard CMake macros, such as PROJECT_VERSION_TWEAK instead of
+# VERSION_DEVEL.
+src/conf/version.hpp: src/conf/version.hpp.in FORCE
+ sed -e 's/@VERSION_FULL@/${VERSION_FULL}/g' \
+ -e 's/@VERSION_HASH@/${VERSION_HASH}/g' \
+ -e 's/@VERSION_STRING@/${VERSION_STRING}/g' \
+ -e 's/@PROJECT_VERSION_MAJOR@/${VERSION_MAJOR}/g' \
+ -e 's/@PROJECT_VERSION_MINOR@/${VERSION_MINOR}/g' \
+ -e 's/@PROJECT_VERSION_PATCH@/${VERSION_PATCH}/g' \
+ -e 's/@PROJECT_VERSION_TWEAK@/${VERSION_DEVEL}/g' \
+ -e 's/@VENDOR_NAME@/${VENDOR_NAME}/g' \
+ -e 's/@VENDOR_POINT@/${VENDOR_POINT}/g' \
+ -e 's|@VENDOR_SOURCE@|${VENDOR_SOURCE}|g' \
+ -e 's/@ABI_VERSION@/${ABI_VERSION}/g' \
+ -e 's/@SO_SHORT@/${SO_SHORT}/g' \
+ -e 's/@SO_LONG@/${SO_LONG}/g' \
+ $< > $@
+
+
version:
@echo version '${VERSION_STRING}'
@echo based on commit ${VERSION_FULL} aka ${VERSION_HASH}
@echo source ${VENDOR_SOURCE}
@echo abi version ${ABI_VERSION}
@echo 'lib so -> ${SO_SHORT} -> ${SO_LONG}'
+.PHONY: version