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.md281
-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.cpp32
-rw-r--r--src/map/clif.cpp33
-rw-r--r--src/map/map.cpp17
-rw-r--r--src/map/map.hpp9
-rw-r--r--src/map/mob.cpp175
-rw-r--r--src/map/npc-parse.cpp15
-rw-r--r--src/map/npc.cpp41
-rw-r--r--src/map/pc.cpp62
-rw-r--r--src/map/script-fun.cpp536
-rw-r--r--src/map/script-parse.py2
-rw-r--r--src/map/skill.cpp14
-rw-r--r--src/map/storage.cpp11
-rw-r--r--src/mmo/clif.t.hpp2
-rw-r--r--src/mmo/enums.hpp2
-rw-r--r--src/mmo/skill.t.hpp8
-rw-r--r--src/mmo/version.cpp2
-rw-r--r--src/shared/lib.cpp34
-rwxr-xr-xtools/bs-align2
-rwxr-xr-xtools/colorize4
-rwxr-xr-xtools/config.py12
-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
40 files changed, 1147 insertions, 736 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..5d550f3
--- /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/legacy/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 b2309f5..8947def 100644
--- a/README.md
+++ b/README.md
@@ -1,220 +1,99 @@
# The Mana World Athena
-
![The Mana World logo](share/tmwa/TheManaWorldLogo.png)
+Welcome to The Mana World Athena! This is an MMORPG server used by The Mana World, based on a protocol that was inspired by the project named "Athena". In 2004, it was forked from eAthena, a Ragnarok Online clone.
+
+
+If you are interested in contributing, please take a look at our [wiki](http://wiki.themanaworld.org/index.php/Development:How_to_Develop) for user instructions. It provides detailed information on how to get started.
+
+**Important note:** Building from a GitHub-generated tarball does not work! You must either build from a git checkout or from a 'make dist' tarball.
+
+The rest of this file contains information relevant to distributors and contributors.
+
+## 1. Distributors
+### Important notes
+- 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
+#### Architecture
+- Tested: x86, amd64, x32
+- Likely to work: all architectures (patches welcome if they don't)
+
+#### Operating system
+- Known bad: Linux 2.6.26 and earlier
+- Maintained: Linux 3.2 and later
+- Likely to break: Cygwin, BSD
+
+#### Filesystem
+- Must support symlinks
+
+### Build dependencies
+#### Python
+- Required: 2.7.x only, installed in $PATH as 'python'
+
+#### C library
+- Recommended: glibc 2.17 or higher
+- Supported: glibc 2.13
+- Known bad: glibc 2.8 or below
+- Unsupported: anything that's not glibc
+
+#### C++ compiler
+- Required: g++ 4.7.2 or higher
+- Recommended: g++ 4.8.1 or higher
+- Not recommended: clang++ 3.3 or higher (all versions have unfixed bugs)
-This is TMWA, an MMORPG server used by The Mana World that uses a protocol
-based on one of many projects that foolishly chose the name "Athena".
-Specifically, it was forked from eAthena, a Ragnarok Online clone, in 2004.
-
-Take a look at the [wiki](http://wiki.themanaworld.org/index.php/How_to_Develop) for user instructions.
-
-**Important note:** building from a GitHub-generated tarball does not work!
-You must either build from a git checkout or from a 'make dist' tarball.
-
-
-The rest of this file contains information relevant only to:
-
-1. Distributors.
-2. Contributors.
-
-TMWA has a [bugtracker](https://git.themanaworld.org/legacy/tmwa/-/issues).
-
-But it's probably worth getting on IRC first:
-* Use an IRC client: irc://irc.libera.chat/themanaworld-dev
-* Or just use the [webchat](https://web.libera.chat/?channel=#themanaworld-dev).
-
-Note that this channel is not specific to technical discussion of TMWA (and
-attoconf), but for any discussions related to TMW development.
-
-## 1. Distributors.
-### Important notes:
-
-- Go read [version.make](version.make)
-- TMWA requires git to build by default, use 'make dist' to get a tarball.
-
-### Platform dependencies:
-#### Architecture:
-
- tested: x86, amd64, x32
- likely to work: all architectures (patches welcome if they don't)
-
-#### Operating system:
- known bad: Linux 2.6.26 and earlier
- maintained: Linux 3.2 and later
- likely to break: Cygwin, BSD
-#### Filesystem:
- must support symlinks
-
-### Build dependencies:
-#### Python:
- required: 2.7.x only, installed in $PATH as 'python'
-#### C library:
- recommended: glibc 2.17 or higher
- supported: glibc 2.13
- known bad: glibc 2.8 or below
- unsupported: anything that's not glibc
-#### C++ compiler:
- required: g++ 4.7.2 or higher
- recommended: g++ 4.8.1 or higher
- not recommended: clang++ 3.3 or higher (all versions have unfixed bugs)
-#### C++ library:
- recommended: libstdc++ to match g++; may need patch for clang++
- may work: libc++
-#### attoconf:
- special: see below
-### Runtime dependencies:
-#### glibc:
- depends on what it was built against
-#### libstdc++:
- depends on what it was built against
-### Instructions:
-#### Configuration:
- ./configure
-_Takes most of the options GNU Autoconf's configure does - I won't
- repeat the output of `./configure --help` here._
-
-_`--prefix=/usr`, not `--prefix usr`, in order to prevent an ambiguity.
- "In the face of ambiguity, refuse the temptation to guess."_
-
-_Out-of-tree builds work._
-
-_Note that there is no option to disable dependency tracking, as it
- is also used to generate link information. There is also no option
- to ignore unknown options - I refuse to lie._
-#### Build:
- make -jN
-#### Build test:
- make test
-
-_Nowhere near complete useful yet. Requires source of Google Test._
+#### C++ library
+- Recommended: libstdc++ to match g++; may need patch for clang++
+- May work: libc++
- make format; git diff --exit-code
-#### Install:
- make install DESTDIR=/whatever
-
-_See [what is installed](#what-is-installed) below_
-#### Install test:
-_not implemented_
-#### Distribution tarballs:
- make dist
- make bindist
-
-### Note about attoconf:
-TMWA's `./configure` script is implemented using a python package
-'attoconf', which I wrote over a weekend after reading GNU autoconf's
-documentation and realizing that it was 1. insane, and 2. still trying
-to solve the wrong sort of problem.
-
-Currently, attoconf's API is still in the "experimental" stage, so the
-real rule is "does ./configure work?".
-When it gets to 1.0, it will start guaranteeing compatibility.
-
-Attoconf is available at [Github](https://github.com/o11c/attoconf/) and is a
-well-behaving python package.
-
-Attoconf requires Python 2.7; a port to Python 2.6 is doable with a bit
-of work, but it is not known if this would benefit anybody.
-
-If you're Arch - you broke Python for us all, you clean up your own mess.
-Patches to call a nonexistent `/usr/bin/python2` will NOT be accepted.
-
-### What is installed:
-#### Overview:
-Currently, `make install` installs 5 binaries, along with a handful
-of libraries, headers, data files, config files, and debug files, each
-of which has a `make install-something` target.
-
-The 4 main programs below are typically running on the same machine,
-though in theory they may be configured to run on different machines
-in a fast LAN. Also, the internal protocol between the programs is
-subject to change without notice, so they *must* be upgraded
-simultaneously.
-
-These programs currently read most of their files relative to the
-current working directory; this was the only thing that makes sense
-since the files are dependent on the server-data. However, a migration
-to installed files has begun.
-
-#### tmwa-admin:
-Formerly known as `ladmin` ("local").
-
-This is an essential tool to maintain the server's flatfile "databases".
-It doesn't actually touch the files directly, just connects to
-tmwa-login.
-
-Even when everything is rewritten to use SQL, it will be kept, if just
-to keep a consistent interface. In fact, if we use SQLite we *can't*
-edit the databases independently. This wouldn't be a problem with
-Postgres, but people seem to think it's hard to install (that's not my
-experience on Debian though. Did they ever try themselves, or are they
-just repeating what they've heard?)
-
-#### tmwa-login:
-Formerly known as `login-server`.
-
-User-facing server to deal with account checks.
-
-Also accepts internal connections from `tmwa-admin` and `tmwa-char`,
-subject to a plaintext password (and for `tmwa-admin`, also an IP check).
-
-#### tmwa-char:
-Formerly known as `char-server`.
-
-User-facing server to deal with character persistence.
-
-Connects to `tmwa-login`; also takes internal connections from `tmwa-map`.
+#### attoconf
+- Special: see below
-Note that it is fully supported for more than one `tmwa-char` to connect
-to the same `tmwa-login`; the client will be presented with a list of
-"worlds" before leaving the login server.
+### Runtime dependencies
+#### glibc
+- Depends on what it was built against
-#### tmwa-map:
-Formerly known as `map-server`.
+#### libstdc++
+- Depends on what it was built against
-Connects to `tmwa-char`.
+### Instructions
+#### Configuration
+- Run `./configure` to configure the build. Most of the options are similar to GNU Autoconf's configure. For example, you can create a `build` directory and run `../configure` from there to keep the source directory clean.
-It is technically possible for more than one `tmwa-map` to connect to
-a single tmwa-char, but this is poorly supported by our current config
-and moderation tools, and there are likely undiscovered bugs.
+#### Build
+- Run `make -j$(nproc)` to build the project.
-#### About server data:
-Just having the binaries is not enough: you also need a complete set of
-content: config files, game scripts, savefiles, and client updates.
+#### Build test
+- Run `make test` to run the build tests. This requires Google Test available - it's added here as a submodule. Note that test coverage is not yet complete.
+- Run `make format` to format the code.
-A web server to serve the updates is also strongly recommended, as even
-developers get annoyed when wushin makes us work straight from his
-client-data repo.
+#### Install
+- Run `make install DESTDIR=/whatever` to install the project. See [what is installed](#what-is-installed) below.
-Currently, there is only *one* set of server data that is known to be
-compatible with TMWA: https://git.themanaworld.org/legacy/serverdata
+#### Install test
+- Not implemented
-The only recommended way of using this is by following the instructions
-in the [How to Develop](https://wiki.themanaworld.org/index.php/Dev:How_to_Develop) article. These instructions are only designed
-for people contributing to TMW itself, not to people trying to start
-a fork - we know all forks are doomed to be unsuccessful anyway, so
-please don't split the development effort, and you can't split the
-player community.
+#### Distribution tarballs
+- Run `make dist` or `make bindist` to create distribution tarballs.
-In particular, the instructions do NOT provide information on how to
-secure a server by changing all the default passwords.
+### Note about attoconf
+TMWA's `./configure` script is implemented using a python package called 'attoconf'. It provides a simpler alternative to GNU autoconf. Attoconf is available at [Github](https://github.com/o11c/attoconf/) and is a well-behaving python package. Please note that attoconf requires Python 2.7, however efforts to bring it to Python 3 are in progress.
-There are 3 other known sets of complete server data: regional ones
-for Germany and Brasil, and Evol. Evol requires their own fork of
-the tmwa server (for some reason they don't like me to call it evola),
-and nobody seems to know of the foreign servers are keeping active.
+### What is installed
+#### Overview
+Currently, `make install` installs 5 binaries, along with libraries, headers, data files, config files, and debug files. Each component has a specific `make install-something` target.
-Note also that The Mana World has not investigated the copyright status
-of other sets of server data.
+The 4 main programs listed below are typically running on the same machine, but they can be configured to run on different machines in a fast LAN. Please note that the internal protocol between the programs may change without notice, so it's important to upgrade them simultaneously.
-## 2. Contributors.
+- `tmwa-admin`: Formerly known as `ladmin` ("local"). This tool is used to maintain the server's flatfile "databases". It connects to `tmwa-login` to make changes.
+- `tmwa-login`: Formerly known as `login-server`. This server handles account checks and accepts internal connections from `tmwa-admin` and `tmwa-char`.
+- `tmwa-char`: Formerly known as `char-server`. This server handles character persistence, connects to `tmwa-login`, and accepts connections from `tmwa-map`. Multiple instances of `tmwa-char` can connect to the same `tmwa-login`. If this is the case, the client will be presented with a choice of worlds before leaving the login server. The Mana World sometimes runs a test `tmwa-char` server connected to the main `tmwa-login` for ease of access.
+- `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.
-You're welcome to help maintain this server, but please make sure to
-get in touch with the currently active maintainers first.
+#### 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.
-Remember that this server is what currently keeps The Mana World alive,
-so any changes should be made with extreme care. It is important that
-each change be tested and reviewed before it goes live.
+## 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.
-Finally, if you got yourself somewhat familiar with this code, please
-consider sticking around either to fix further issues as they come up
-or to help reviewing changes by others. Thanks!
+If you have become familiar with the codebase, we encourage you to stick around and help fix issues or review changes made by others. Your contributions are greatly appreciated!
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 b745e05..d66308d 100644
--- a/src/map/battle.cpp
+++ b/src/map/battle.cpp
@@ -2060,18 +2060,28 @@ ATK battle_weapon_attack(dumb_ptr<block_list> src, dumb_ptr<block_list> target,
// 攻撃対象となりうるので攻撃 | Attack because it can be attacked
if (sd && sd->status.weapon == ItemLook::W_BOW)
{
- IOff0 aidx = sd->equip_index_maybe[EQUIP::ARROW];
- if (aidx.ok())
- {
- if (battle_config.arrow_decrement)
- pc_delitem(sd, aidx, 1, 0);
- }
- else
+ IOff0 widx = sd->equip_index_maybe[EQUIP::WEAPON];
+
+ OMATCH_BEGIN_SOME (sdidw, sd->inventory_data[widx])
{
- clif_arrow_fail(sd, 0);
- return ATK::ZERO;
+ if (!bool(sdidw->mode & ItemMode::DONT_USE_AMMO))
+ {
+ IOff0 aidx = sd->equip_index_maybe[EQUIP::ARROW];
+ if (aidx.ok())
+ {
+ if (battle_config.arrow_decrement)
+ pc_delitem(sd, aidx, 1, 0);
+ }
+ else
+ {
+ clif_arrow_fail(sd, 0);
+ return ATK::ZERO;
+ }
+ }
}
+ OMATCH_END ();
}
+
wd = battle_calc_weapon_attack(src, target, SkillID::ZERO, 0, 0);
// significantly increase injuries for hasted characters
@@ -2081,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 9edf2af..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);
@@ -4617,11 +4623,18 @@ RecvResult clif_parse_DropItem(Session *s, dumb_ptr<map_session_data> sd)
clif_displaymessage(sd->sess, "Can't drop items here."_s);
return rv;
}
- if (bool(itemdb_search(sd->status.inventory[fixed.ioff2.unshift()].nameid)->mode & ItemMode::NO_DROP))
+
+ OMATCH_BEGIN_SOME (sdidn, sd->inventory_data[fixed.ioff2.unshift()])
{
- clif_displaymessage(sd->sess, "This item can't be dropped."_s);
- return rv;
+ 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;
+ }
}
+ OMATCH_END ();
+
if (sd->npc_id
|| sd->opt1 != Opt1::ZERO)
{
@@ -4901,11 +4914,17 @@ RecvResult clif_parse_TradeAddItem(Session *s, dumb_ptr<map_session_data> sd)
if (fixed.zeny_or_ioff2.index != 0 && !fixed.zeny_or_ioff2.ok())
return RecvResult::Error;
if (fixed.zeny_or_ioff2.ok())
- if (bool(itemdb_search(sd->status.inventory[fixed.zeny_or_ioff2.unshift()].nameid)->mode & ItemMode::NO_TRADE))
+ OMATCH_BEGIN_SOME (sdidn, sd->inventory_data[fixed.zeny_or_ioff2.unshift()])
{
- clif_displaymessage(sd->sess, "This item can't be traded."_s);
- return rv;
+ 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;
+ }
}
+ OMATCH_END ();
+
trade_tradeadditem(sd, fixed.zeny_or_ioff2, fixed.amount);
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 7cf43d5..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;
@@ -360,7 +360,11 @@ struct npc_data : block_list
Opt0 option;
short flag;
- bool deletion_pending;
+ enum {
+ NOT_DELETING = 0,
+ DELETION_QUEUED = 1,
+ DELETION_ACTIVE = 2
+ } deletion_pending;
Array<Timer, MAX_EVENTTIMER> eventtimer;
@@ -527,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 f2f6815..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++)
@@ -2556,10 +2557,12 @@ int mob_damage(dumb_ptr<block_list> src, dumb_ptr<mob_data> md, int damage,
// activity
if (sd)
+ {
if (sd->activity.attacks == 2147483647)
sd->activity.attacks = 1;
else
sd->activity.attacks++;
+ }
if (md->hp > 0)
{
@@ -2594,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;
@@ -2658,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]
}
@@ -2728,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
@@ -2743,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;
@@ -2794,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);
}
@@ -3499,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;
}
@@ -3652,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();
- 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
+ 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
- if (get_mob_db(mob_class).base_exp == 0)
- get_mob_db(mob_class).base_exp = mob_gen_exp(&get_mob_db(mob_class));
+ if (mdbv.base_exp == 0)
+ mdbv.base_exp = mob_gen_exp(&mdbv);
+
+ 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 47b851c..8865ea6 100644
--- a/src/map/npc-parse.cpp
+++ b/src/map/npc-parse.cpp
@@ -164,7 +164,7 @@ bool npc_load_warp(ast::npc::Warp& warp)
nd->warp.xs = xs;
nd->warp.ys = ys;
- nd->deletion_pending = false;
+ nd->deletion_pending = npc_data::NOT_DELETING;
npc_warp++;
nd->bl_type = BL::NPC;
@@ -228,7 +228,7 @@ bool npc_load_shop(ast::npc::Shop& shop)
nd->opt2 = Opt2::ZERO;
nd->opt3 = Opt3::ZERO;
- nd->deletion_pending = false;
+ nd->deletion_pending = npc_data::NOT_DELETING;
npc_shop++;
nd->bl_type = BL::NPC;
@@ -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;
@@ -458,7 +459,7 @@ bool npc_load_script_none(ast::script::ScriptBody& body, ast::npc::ScriptNone& s
nd->opt2 = Opt2::ZERO;
nd->opt3 = Opt3::ZERO;
- nd->deletion_pending = false;
+ nd->deletion_pending = npc_data::NOT_DELETING;
npc_script++;
nd->bl_type = BL::NPC;
@@ -568,7 +569,7 @@ bool npc_load_script_map(ast::script::ScriptBody& body, ast::npc::ScriptMap& scr
nd->opt2 = Opt2::ZERO;
nd->opt3 = Opt3::ZERO;
- nd->deletion_pending = false;
+ nd->deletion_pending = npc_data::NOT_DELETING;
npc_script++;
nd->bl_type = BL::NPC;
diff --git a/src/map/npc.cpp b/src/map/npc.cpp
index e8d6d4b..0a7bfc2 100644
--- a/src/map/npc.cpp
+++ b/src/map/npc.cpp
@@ -357,7 +357,7 @@ void npc_eventtimer(TimerData *, tick_t, BlockId, NpcEvent data)
data);
return;
});
- if ((nd = ev->nd) == nullptr || nd->deletion_pending == true)
+ if ((nd = ev->nd) == nullptr || nd->deletion_pending != npc_data::NOT_DELETING)
{
if (battle_config.error_log)
PRINTF("npc_event: event not found [%s]\n"_fmt,
@@ -591,7 +591,7 @@ int npc_event(dumb_ptr<map_session_data> sd, NpcEvent eventname,
ev.pos = ev2->pos;
}
- if ((nd = ev.nd) == nullptr || nd->deletion_pending == true)
+ if ((nd = ev.nd) == nullptr || nd->deletion_pending != npc_data::NOT_DELETING)
{
if (!mob_kill && battle_config.error_log)
PRINTF("npc_event: event not found [%s]\n"_fmt,
@@ -767,13 +767,22 @@ int npc_click(dumb_ptr<map_session_data> sd, BlockId id)
}
}
- if (npc_checknear(sd, id)) {
+ if (npc_checknear(sd, id))
+ {
clif_scriptclose(sd, id);
return 1;
}
nd = map_id_is_npc(id);
+ // If someone clicked on an NPC that is about to no longer exist, then
+ // release them
+ if (nd->deletion_pending != npc_data::NOT_DELETING)
+ {
+ clif_scriptclose(sd, id);
+ return 1;
+ }
+
if (nd->flag & 1) // 無効化されている
return 1;
@@ -818,18 +827,19 @@ int npc_scriptcont(dumb_ptr<map_session_data> sd, BlockId id)
nd = map_id_is_npc(id);
- if (!nd /* NPC was disposed? */)
+ // If the NPC is about to be deleted, release the PC
+ if (nd->deletion_pending != npc_data::NOT_DELETING)
{
clif_scriptclose(sd, id);
npc_event_dequeue(sd);
- return 0;
+ return 1;
}
if (nd->is_script()->scr.parent &&
map_id2bl(nd->is_script()->scr.parent) == nullptr)
{
npc_free(nd);
- return 0;
+ return 1;
}
sd->npc_pos = run_script(ScriptPointer(script_or_parent(nd->is_script()), sd->npc_pos), sd->bl_id, id);
@@ -993,12 +1003,19 @@ int npc_selllist(dumb_ptr<map_session_data> sd,
if (!nameid ||
sd->status.inventory[item_list[i].ioff2.unshift()].amount < item_list[i].count)
return 1;
- if (bool(itemdb_search(nameid)->mode & ItemMode::NO_SELL_TO_NPC))
+
+ OMATCH_BEGIN_SOME (sdidn, sd->inventory_data[item_list[i].ioff2.unshift()])
{
- //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.
- return 3;
+ 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.
+ return 3;
+ }
}
+ OMATCH_END ();
+
if (sd->trade_partner)
return 2; // cant sell while trading
z += static_cast<double>(itemdb_value_sell(nameid)) * item_list[i].count;
@@ -1091,10 +1108,10 @@ void npc_propagate_update(dumb_ptr<npc_data> nd)
void npc_free(dumb_ptr<npc_data> nd)
{
- if (nd == nullptr || nd->deletion_pending == true)
+ if (nd == nullptr || nd->deletion_pending == npc_data::DELETION_ACTIVE)
return;
- nd->deletion_pending = true;
+ nd->deletion_pending = npc_data::DELETION_ACTIVE;
nd->flag |= 1;
clif_clearchar(nd, BeingRemoveWhy::GONE);
npc_propagate_update(nd);
diff --git a/src/map/pc.cpp b/src/map/pc.cpp
index 12af48f..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,
@@ -2404,8 +2425,12 @@ int pc_useitem(dumb_ptr<map_session_data> sd, IOff0 n)
}
P<const ScriptBuffer> script = borrow(*sdidn->use_script);
- clif_useitemack(sd, n, amount - 1, 1);
- pc_delitem(sd, n, 1, 1);
+
+ if (!bool(sdidn->mode & ItemMode::KEEP_AFTER_USE))
+ {
+ clif_useitemack(sd, n, amount - 1, 1);
+ pc_delitem(sd, n, 1, 1);
+ }
// activity
if (sd)
@@ -3717,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;
@@ -3866,19 +3894,19 @@ int pc_readparam(dumb_ptr<block_list> bl, SP type)
val = sd ? sd->mute.guild : 0;
break;
case SP::KILLS:
- val = sd->activity.kills;
+ val = sd ? sd->activity.kills : 0;
break;
case SP::CASTS:
- val = sd->activity.casts;
+ val = sd ? sd->activity.casts : 0;
break;
case SP::ITEMS_USED:
- val = sd->activity.items_used;
+ val = sd ? sd->activity.items_used : 0;
break;
case SP::TILES_WALKED:
- val = sd->activity.tiles_walked;
+ val = sd ? sd->activity.tiles_walked : 0;
break;
case SP::ATTACKS:
- val = sd->activity.attacks;
+ val = sd ? sd->activity.attacks : 0;
break;
case SP::AUTOMOD:
val = sd ? static_cast<int>(sd->automod) : 0;
@@ -4044,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
@@ -4131,18 +4164,23 @@ int pc_setparam(dumb_ptr<block_list> bl, SP type, int val)
break;
// atm only setting of casts is needed since magic is handled in serverdata but I let the others here as well for whatever reason
case SP::KILLS:
+ nullpo_retz(sd);
sd->activity.kills = val;
break;
case SP::CASTS:
+ nullpo_retz(sd);
sd->activity.casts = val;
break;
case SP::ITEMS_USED:
+ nullpo_retz(sd);
sd->activity.items_used = val;
break;
case SP::TILES_WALKED:
+ nullpo_retz(sd);
sd->activity.tiles_walked = val;
break;
case SP::ATTACKS:
+ nullpo_retz(sd);
sd->activity.attacks = val;
break;
case SP::AUTOMOD:
diff --git a/src/map/script-fun.cpp b/src/map/script-fun.cpp
index deb781a..64ba542 100644
--- a/src/map/script-fun.cpp
+++ b/src/map/script-fun.cpp
@@ -111,7 +111,7 @@ static
void builtin_mes(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
sd->state.npc_dialog_mes = 1;
RString mes = HARG(0) ? conv_str(st, &AARG(0)) : ""_s;
clif_scriptmes(sd, st->oid, mes);
@@ -125,7 +125,7 @@ static
void builtin_mesq(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
sd->state.npc_dialog_mes = 1;
RString mes = HARG(0) ? conv_str(st, &AARG(0)) : ""_s;
MString mesq;
@@ -145,8 +145,8 @@ void builtin_mesn(ScriptState *st)
dumb_ptr<map_session_data> sd = script_rid2sd(st);
dumb_ptr<npc_data> nd;
nd = map_id_is_npc(st->oid);
- script_nullpo_end(nd, "npc not found");
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(nd, "npc not found"_s);
+ script_nullpo_end(sd, "player not found"_s);
sd->state.npc_dialog_mes = 1;
RString mes = HARG(0) ? conv_str(st, &AARG(0)) : RString(nd->name.xislice_h(std::find(nd->name.begin(), nd->name.end(), '#'))); // strnpcinf
MString mesq;
@@ -164,7 +164,7 @@ static
void builtin_clear(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
clif_npc_action(sd, st->oid, 9, 0, 0, 0);
}
@@ -389,7 +389,7 @@ static
void builtin_next(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
st->state = ScriptEndState::STOP;
clif_scriptnext(sd, st->oid);
}
@@ -411,7 +411,7 @@ void builtin_close(ScriptState *st)
}
st->state = ScriptEndState::END;
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
if (sd->state.npc_dialog_mes)
clif_scriptclose(sd, st->oid);
@@ -428,7 +428,7 @@ void builtin_close2(ScriptState *st)
{
st->state = ScriptEndState::STOP;
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
if (sd->state.npc_dialog_mes)
clif_scriptclose(sd, st->oid);
else
@@ -443,7 +443,7 @@ static
void builtin_menu(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
if (sd->state.menu_or_input == 0)
{
@@ -676,7 +676,7 @@ void builtin_isat(ScriptState *st)
x = conv_num(st, &AARG(1));
y = conv_num(st, &AARG(2));
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
push_int<ScriptDataInt>(st->stack,
(x == sd->bl_x) && (y == sd->bl_y)
@@ -695,7 +695,7 @@ void builtin_warp(ScriptState *st)
MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
x = conv_num(st, &AARG(1));
y = conv_num(st, &AARG(2));
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
pc_setpos(sd, str, x, y, BeingRemoveWhy::GONE);
}
@@ -748,7 +748,7 @@ void builtin_heal(ScriptState *st)
hp = conv_num(st, &AARG(0));
sp = conv_num(st, &AARG(1));
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
if(sd != nullptr && (sd->status.hp < 1 && hp > 0)){
pc_setstand(sd);
@@ -901,7 +901,7 @@ void builtin_input(ScriptState *st)
char postfix = name.back();
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
if (sd->state.menu_or_input)
{
// Second time (rerun)
@@ -962,7 +962,7 @@ void builtin_requestitem(ScriptState *st)
}
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
if (sd->state.menu_or_input)
{
// Second time (rerunline)
@@ -1055,7 +1055,7 @@ void builtin_requestlang(ScriptState *st)
ZString name = variable_names.outtern(reg.base());
char postfix = name.back();
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
if (postfix != '$')
{
@@ -1247,7 +1247,7 @@ void builtin_foreach(ScriptState *st)
else if (st->rid)
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
map_foreachinarea(std::bind(builtin_foreach_sub, ph::_1, event, sd->bl_id),
m,
@@ -1279,12 +1279,23 @@ void builtin_destroy(ScriptState *st)
/* Not safe to call destroy if others may also be paused on this NPC! */
if (st->rid) {
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
npc_event_dequeue(sd);
}
+ // Cancel all existing timers on the NPC.
+ // They "would" never fire, and we don't want race conditions here.
+ for (int i = 0; i < MAX_EVENTTIMER; i++)
+ {
+ nd->eventtimer[i].cancel();
+ }
+ // Schedule the NPC to be freed on the next available tick.
+ // Scripts can be invoked under iteration of the ev_db global event
+ // database, and we don't want to invalidate active iterators.
+ nd->deletion_pending = npc_data::DELETION_QUEUED;
+ nd->eventtimer[0] = Timer(gettick(), std::bind(npc_free, nd));
+
nd = nd->is_script();
- npc_free(nd);
st->oid = BlockId();
if (!HARG(0))
@@ -1350,7 +1361,7 @@ void builtin_puppet(ScriptState *st)
nd->npc_subtype = NpcSubtype::SCRIPT;
npc_script++;
- nd->deletion_pending = false;
+ nd->deletion_pending = npc_data::NOT_DELETING;
nd->n = map_addnpc(nd->bl_m, nd);
@@ -1453,7 +1464,7 @@ void builtin_set(ScriptState *st)
else
{
bl = script_rid2sd(st);
- script_nullpo_end(bl, "player not found");
+ script_nullpo_end(bl, "player not found"_s);
}
int val = conv_num(st, &AARG(1));
@@ -1628,14 +1639,14 @@ void builtin_setarray(ScriptState *st)
else
bl = map_id_is_npc(wrap<BlockId>(tid));
}
- script_nullpo_end(bl, "npc not found");
+ script_nullpo_end(bl, "npc not found"_s);
if (st->oid && bl->bl_id != st->oid)
j = getarraysize2(reg, bl);
}
else if (prefix != '$' && !name.startswith(".@"_s))
{
bl = map_id_is_player(st->rid);
- script_nullpo_end(bl, "player not found");
+ script_nullpo_end(bl, "player not found"_s);
}
for (; i < st->end - st->start - 2 && j < 256; i++, j++)
@@ -1674,7 +1685,7 @@ void builtin_cleararray(ScriptState *st)
else if (prefix != '$' && !name.startswith(".@"_s))
{
bl = map_id_is_player(st->rid);
- script_nullpo_end(bl, "player not found");
+ script_nullpo_end(bl, "player not found"_s);
}
for (int i = 0; i < sz; i++)
@@ -1847,7 +1858,7 @@ void builtin_gmlog(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
ZString message = ZString(conv_str(st, &AARG(0)));
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
log_atcommand(sd, STRPRINTF("{SCRIPT} %s"_fmt, message));
}
@@ -1861,7 +1872,7 @@ void builtin_setlook(ScriptState *st)
dumb_ptr<map_session_data> sd = script_rid2sd(st);
LOOK type = LOOK(conv_num(st, &AARG(0)));
int val = conv_num(st, &AARG(1));
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
pc_changelook(sd, type, val);
@@ -1881,7 +1892,7 @@ void builtin_countitem(ScriptState *st)
struct script_data *data;
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
data = &AARG(0);
get_val(st, data);
@@ -1929,7 +1940,7 @@ void builtin_checkweight(ScriptState *st)
struct script_data *data;
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
data = &AARG(0);
get_val(st, data);
@@ -1978,7 +1989,7 @@ void builtin_getitem(ScriptState *st)
struct script_data *data;
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
data = &AARG(0);
get_val(st, data);
@@ -2082,7 +2093,7 @@ void builtin_delitem(ScriptState *st)
struct script_data *data;
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
data = &AARG(0);
get_val(st, data);
@@ -2133,7 +2144,7 @@ static
void builtin_getversion(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
push_int<ScriptDataInt>(st->stack, unwrap<ClientVersion>(sd->client_version));
}
@@ -2220,7 +2231,7 @@ void builtin_strcharinfo(ScriptState *st)
else
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
num = conv_num(st, &AARG(0));
if (num == 0)
@@ -2277,7 +2288,7 @@ void builtin_getequipid(ScriptState *st)
else
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
num = conv_num(st, &AARG(0));
IOff0 i = pc_checkequip(sd, equip[num - 1]);
if (i.ok())
@@ -2336,7 +2347,7 @@ void builtin_bonus(ScriptState *st)
type = SP(conv_num(st, &AARG(0)));
int val = conv_num(st, &AARG(1));
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
pc_bonus(sd, type, val);
}
@@ -2357,7 +2368,7 @@ void builtin_bonus2(ScriptState *st)
int type2 = conv_num(st, &AARG(1));
int val = conv_num(st, &AARG(2));
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
pc_bonus2(sd, type, type2, val);
}
@@ -2378,7 +2389,7 @@ void builtin_skill(ScriptState *st)
if (HARG(2))
flag = conv_num(st, &AARG(2));
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
pc_skill(sd, id, level, flag);
clif_skillinfoblock(sd);
@@ -2397,7 +2408,7 @@ void builtin_setskill(ScriptState *st)
SkillID id = static_cast<SkillID>(conv_num(st, &AARG(0)));
level = conv_num(st, &AARG(1));
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
level = std::min(level, MAX_SKILL_LEVEL);
level = std::max(level, 0);
@@ -2415,7 +2426,7 @@ void builtin_getskilllv(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
SkillID id = SkillID(conv_num(st, &AARG(0)));
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
push_int<ScriptDataInt>(st->stack, pc_checkskill(sd, id));
}
@@ -2427,7 +2438,7 @@ static
void builtin_overrideattack(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
if (HARG(0))
{
@@ -2464,7 +2475,7 @@ static
void builtin_getgmlevel(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
push_int<ScriptDataInt>(st->stack, pc_isGM(sd).get_all_bits());
}
@@ -2497,7 +2508,7 @@ void builtin_getopt2(ScriptState *st)
dumb_ptr<map_session_data> sd;
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
push_int<ScriptDataInt>(st->stack, static_cast<uint16_t>(sd->opt2));
@@ -2514,7 +2525,7 @@ void builtin_setopt2(ScriptState *st)
Opt2 new_opt2 = Opt2(conv_num(st, &AARG(0)));
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
if (new_opt2 == sd->opt2)
return;
@@ -2535,7 +2546,7 @@ void builtin_savepoint(ScriptState *st)
dumb_ptr<map_session_data> sd = script_rid2sd(st);
int x, y;
MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
x = conv_num(st, &AARG(1));
y = conv_num(st, &AARG(2));
@@ -2635,7 +2646,7 @@ void builtin_openstorage(ScriptState *st)
// int sync = 0;
// if (st->end >= 3) sync = conv_num(st,& (st->stack->stack_data[st->start+2]));
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
// if (sync) {
st->state = ScriptEndState::STOP;
@@ -2655,7 +2666,7 @@ void builtin_getexp(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
int base = 0, job = 0;
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
base = conv_num(st, &AARG(0));
job = conv_num(st, &AARG(1));
@@ -2672,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)));
@@ -2686,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:
@@ -2789,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
@@ -2968,7 +2923,7 @@ void builtin_mobinfo_droparrays(ScriptState *st)
else if (prefix != '$' && !name.startswith(".@"_s))
{
bl = map_id_is_player(st->rid);
- script_nullpo_end(bl, "player not found");
+ script_nullpo_end(bl, "player not found"_s);
}
switch (request)
@@ -3011,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)
@@ -3019,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))
@@ -3042,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;
}
}
@@ -3056,7 +3013,7 @@ void builtin_mobinfo_droparrays(ScriptState *st)
status = 2;
break;
}
-
+ }
push_int<ScriptDataInt>(st->stack, status);
}
@@ -3086,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
{
@@ -3103,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);
@@ -3177,8 +3138,8 @@ void builtin_summon(ScriptState *st)
}
mob->mode |=
- MobMode::SUMMONED | MobMode::TURNS_AGAINST_BAD_MASTER;
-
+ MobMode::SUMMONED; // | MobMode::TURNS_AGAINST_BAD_MASTER; <- its fun but bugged.
+ // This flag identified to be source of AFK PK city exploits, etc.
mob->deletetimer = Timer(gettick() + lifespan,
std::bind(mob_timer_delete, ph::_1, ph::_2,
mob_id));
@@ -3321,7 +3282,7 @@ void builtin_addtimer(ScriptState *st)
else if (st->rid)
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
pc_addeventtimer(sd, tick, event);
}
@@ -3354,7 +3315,7 @@ void builtin_initnpctimer(ScriptState *st)
nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(0)))));
else
nd_ = map_id_is_npc(st->oid);
- script_nullpo_end(nd_, "no npc");
+ script_nullpo_end(nd_, "no npc"_s);
assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
dumb_ptr<npc_data_script> nd = nd_->is_script();
@@ -3375,7 +3336,7 @@ void builtin_startnpctimer(ScriptState *st)
nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(0)))));
else
nd_ = map_id_is_npc(st->oid);
- script_nullpo_end(nd_, "no npc");
+ script_nullpo_end(nd_, "no npc"_s);
assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
dumb_ptr<npc_data_script> nd = nd_->is_script();
@@ -3395,7 +3356,7 @@ void builtin_stopnpctimer(ScriptState *st)
nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(0)))));
else
nd_ = map_id_is_npc(st->oid);
- script_nullpo_end(nd_, "no npc");
+ script_nullpo_end(nd_, "no npc"_s);
assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
dumb_ptr<npc_data_script> nd = nd_->is_script();
@@ -3417,7 +3378,7 @@ void builtin_getnpctimer(ScriptState *st)
nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(1)))));
else
nd_ = map_id_is_npc(st->oid);
- script_nullpo_end(nd_, "no npc");
+ script_nullpo_end(nd_, "no npc"_s);
assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
dumb_ptr<npc_data_script> nd = nd_->is_script();
@@ -3450,7 +3411,7 @@ void builtin_setnpctimer(ScriptState *st)
nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(1)))));
else
nd_ = map_id_is_npc(st->oid);
- script_nullpo_end(nd_, "no npc");
+ script_nullpo_end(nd_, "no npc"_s);
assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
dumb_ptr<npc_data_script> nd = nd_->is_script();
@@ -3469,7 +3430,7 @@ void builtin_npcaction(ScriptState *st)
int id = 0;
short x = HARG(2) ? conv_num(st, &AARG(2)) : 0;
short y = HARG(3) ? conv_num(st, &AARG(3)) : 0;
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
if(HARG(1))
{
@@ -3494,7 +3455,7 @@ static
void builtin_camera(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
if (HARG(0))
{
@@ -3551,7 +3512,7 @@ void builtin_setnpcdirection(ScriptState *st)
else
nd_ = map_id_is_npc(st->oid);
- script_nullpo_end(nd_, "no npc");
+ script_nullpo_end(nd_, "no npc"_s);
if (bool(conv_num(st, &AARG(1))))
action = DamageType::SIT;
@@ -3567,7 +3528,7 @@ void builtin_setnpcdirection(ScriptState *st)
if (st->rid)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
clif_sitnpc_towards(sd, nd_, action);
clif_setnpcdirection_towards(sd, nd_, dir);
}
@@ -3597,7 +3558,7 @@ void builtin_announce(ScriptState *st)
bl = map_id2bl(st->oid);
else
bl = script_rid2sd(st);
- script_nullpo_end(bl, "player not found");
+ script_nullpo_end(bl, "player not found"_s);
clif_GMmessage(bl, str, flag);
}
else
@@ -3716,6 +3677,23 @@ void builtin_aggravate(ScriptState *st)
}
/*==========================================
+ * Check for summoned creature
+ *------------------------------------------
+ */
+static
+void builtin_issummon(ScriptState *st)
+{
+ dumb_ptr<mob_data> md = map_id_is_mob(wrap<BlockId>(conv_num(st, &AARG(0))));
+ int val = 0;
+ if (md)
+ {
+ val = bool(md->mode & MobMode::SUMMONED);
+ }
+
+ push_int<ScriptDataInt>(st->stack, val);
+}
+
+/*==========================================
* エリア指定ユーザー数所得
* Area Designated User Income
*------------------------------------------
@@ -3892,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:
@@ -3901,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:
@@ -3979,7 +3962,7 @@ void builtin_resetstatus(ScriptState *st)
{
dumb_ptr<map_session_data> sd;
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
pc_resetstate(sd);
}
@@ -4161,7 +4144,7 @@ void builtin_setpvpchannel(ScriptState *st)
if (flag < 1)
flag = 0;
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
sd->state.pvpchannel = flag;
}
@@ -4178,7 +4161,7 @@ void builtin_getpvpflag(ScriptState *st)
else
sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
int num = conv_num(st, &AARG(0));
int flag = 0;
@@ -4334,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)
@@ -4360,7 +4348,7 @@ static
void builtin_getpartnerid2(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
push_int<ScriptDataInt>(st->stack, unwrap<CharId>(sd->status.partner_id));
}
@@ -4425,7 +4413,7 @@ void builtin_explode(ScriptState *st)
else if (prefix != '$' && prefix != '.')
{
bl = map_id2bl(st->rid)->is_player();
- script_nullpo_end(bl, "target player not found");
+ script_nullpo_end(bl, "target player not found"_s);
}
@@ -4476,7 +4464,7 @@ void builtin_getinventorylist(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
int j = 0;
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
for (IOff0 i : IOff0::iter())
{
@@ -4507,7 +4495,7 @@ void builtin_getactivatedpoolskilllist(ScriptState *st)
int skill_pool_size = skill_pool(sd, pool_skills);
int i, count = 0;
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
for (i = 0; i < skill_pool_size; i++)
{
@@ -4540,7 +4528,7 @@ void builtin_getunactivatedpoolskilllist(ScriptState *st)
dumb_ptr<map_session_data> sd = script_rid2sd(st);
int i, count = 0;
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
for (i = 0; i < skill_pool_skills.size(); i++)
{
@@ -4572,7 +4560,7 @@ void builtin_poolskill(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
SkillID skill_id = SkillID(conv_num(st, &AARG(0)));
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
skill_pool_activate(sd, skill_id);
clif_skillinfoblock(sd);
@@ -4587,7 +4575,7 @@ void builtin_unpoolskill(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
SkillID skill_id = SkillID(conv_num(st, &AARG(0)));
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
skill_pool_deactivate(sd, skill_id);
clif_skillinfoblock(sd);
@@ -4831,7 +4819,7 @@ void builtin_nude(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
for (EQUIP i : EQUIPs)
{
@@ -4851,7 +4839,7 @@ static
void builtin_unequipbyid(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
EQUIP slot_id = EQUIP(conv_num(st, &AARG(0)));
@@ -4992,7 +4980,7 @@ void builtin_title(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
ZString msg = ZString(conv_str(st, &AARG(0)));
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
clif_npc_send_title(sd->sess, st->oid, msg);
}
@@ -5013,7 +5001,7 @@ void builtin_smsg(ScriptState *st)
int type = HARG(1) ? conv_num(st, &AARG(0)) : 0;
ZString msg = ZString(conv_str(st, (HARG(1) ? &AARG(1) : &AARG(0))));
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
if (type < 0 || type > 0xFF)
type = 0;
@@ -5028,7 +5016,7 @@ static
void builtin_remotecmd(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
if (HARG(1))
{
@@ -5055,7 +5043,7 @@ void builtin_sendcollision(ScriptState *st)
short x1, y1, x2, y2;
x1 = x2 = conv_num(st, &AARG(2));
y1 = y2 = conv_num(st, &AARG(3));
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
if (HARG(5))
{
@@ -5088,7 +5076,7 @@ void builtin_music(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
ZString msg = ZString(conv_str(st, &AARG(0)));
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
clif_change_music(sd, msg);
}
@@ -5113,7 +5101,7 @@ void builtin_mapmask(ScriptState *st)
else if(HARG(1) && nd)
nd->bl_m->mask = map_mask;
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
clif_send_mask(sd, map_mask);
}
@@ -5195,7 +5183,7 @@ void builtin_getlook(ScriptState *st)
dumb_ptr<map_session_data> sd = script_rid2sd(st);
LOOK type = LOOK(conv_num(st, &AARG(0)));
int val = -1;
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
switch (type)
{
@@ -5239,7 +5227,7 @@ void builtin_getsavepoint(ScriptState *st)
{
int x, y, type;
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
type = conv_num(st, &AARG(0));
@@ -5333,7 +5321,7 @@ void builtin_isin(ScriptState *st)
x2 = conv_num(st, &AARG(3));
y2 = conv_num(st, &AARG(4));
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
push_int<ScriptDataInt>(st->stack,
(sd->bl_x >= x1 && sd->bl_x <= x2)
@@ -5369,7 +5357,7 @@ void builtin_shop(ScriptState *st)
dumb_ptr<map_session_data> sd = script_rid2sd(st);
dumb_ptr<npc_data> nd;
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARG(0))));
nd = npc_name2id(name);
@@ -5387,7 +5375,7 @@ static
void builtin_isdead(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
push_int<ScriptDataInt>(st->stack, pc_isdead(sd));
}
@@ -5421,7 +5409,7 @@ static
void builtin_getx(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
push_int<ScriptDataInt>(st->stack, sd->bl_x);
}
@@ -5433,7 +5421,7 @@ static
void builtin_gety(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
push_int<ScriptDataInt>(st->stack, sd->bl_y);
}
@@ -5445,7 +5433,7 @@ static
void builtin_getdir(ScriptState *st)
{
dumb_ptr<map_session_data> sd = script_rid2sd(st);
- script_nullpo_end(sd, "player not found");
+ script_nullpo_end(sd, "player not found"_s);
push_int<ScriptDataInt>(st->stack, static_cast<uint8_t>(sd->dir));
}
@@ -5496,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
*------------------------------------------
*/
@@ -5524,7 +5592,7 @@ void builtin_strnpcinfo(ScriptState *st)
nd = map_id_is_npc(st->oid);
}
- script_nullpo_end(nd, "npc not found");
+ script_nullpo_end(nd, "npc not found"_s);
switch(num)
{
@@ -5562,7 +5630,7 @@ void builtin_getnpcx(ScriptState *st)
nd = map_id_is_npc(st->oid);
}
- script_nullpo_end(nd, "no npc");
+ script_nullpo_end(nd, "no npc"_s);
push_int<ScriptDataInt>(st->stack, nd->bl_x);
}
@@ -5584,7 +5652,7 @@ void builtin_getnpcy(ScriptState *st)
nd = map_id_is_npc(st->oid);
}
- script_nullpo_end(nd, "no npc");
+ script_nullpo_end(nd, "no npc"_s);
push_int<ScriptDataInt>(st->stack, nd->bl_y);
}
@@ -5746,6 +5814,7 @@ BuiltinFunction builtin_functions[] =
BUILTIN(shop, "s"_s, '\0'),
BUILTIN(isdead, ""_s, 'i'),
BUILTIN(aggravate, "i?"_s, '\0'),
+ BUILTIN(issummon, "i"_s, 'i'),
BUILTIN(fakenpcname, "ssi"_s, '\0'),
BUILTIN(puppet, "mxysi??"_s, 'i'),
BUILTIN(destroy, "?"_s, '\0'),
@@ -5758,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 54398f3..dc1fe62 100644
--- a/src/map/storage.cpp
+++ b/src/map/storage.cpp
@@ -186,11 +186,16 @@ int storage_storageadd(dumb_ptr<map_session_data> sd, IOff0 index, int amount)
if (amount < 1 || amount > sd->status.inventory[index].amount)
return 0;
- if (bool(itemdb_search(sd->status.inventory[index].nameid)->mode & ItemMode::NO_STORAGE))
+ OMATCH_BEGIN_SOME (sdidn, sd->inventory_data[index])
{
- clif_displaymessage(sd->sess, "This item can't be stored."_s);
- return 0;
+ 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;
+ }
}
+ OMATCH_END ();
// log_tostorage(sd, index, 0);
if (storage_additem(sd, stor, &sd->status.inventory[index], amount) == 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/enums.hpp b/src/mmo/enums.hpp
index c4a1b17..377f7d7 100644
--- a/src/mmo/enums.hpp
+++ b/src/mmo/enums.hpp
@@ -135,6 +135,8 @@ enum class ItemMode : uint8_t
NO_TRADE = 2,
NO_SELL_TO_NPC = 4,
NO_STORAGE = 8,
+ KEEP_AFTER_USE = 16,
+ DONT_USE_AMMO = 32,
};
ENUM_BITWISE_OPERATORS(ItemMode)
}
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 0eebf17..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,25 +35,37 @@
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/%s\n"_fmt, dir_path, filename);
abort();
+ }
AString line;
if (!rf.getline(line))
+ {
+ 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/%s\n"_fmt, dir_path, filename);
abort();
+ }
wf.put_line("Hello, World!"_s);
if (!wf.close())
+ {
+ FPRINTF(stderr, "Could not write to %s/%s\n"_fmt, dir_path, filename);
abort();
+ }
}
void check_paths()
@@ -65,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 2f0781d..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)
@@ -544,7 +542,7 @@ def build_config():
char_conf.opt('char_ip', IP4Address, '{}')
char_conf.opt('char_port', u16, '6121', min='1024')
char_conf.opt('char_txt', RString, '{}')
- char_conf.opt('max_connect_user', u32, '0')
+ char_conf.opt('max_connect_user', i32, '0')
char_conf.opt('autosave_time', seconds, 'DEFAULT_AUTOSAVE_INTERVAL', {char_h}, min='1_s')
char_conf.opt('start_point', Point, '{ {"001-1.gat"_s}, 273, 354 }')
char_conf.opt('unknown_char_name', CharName, 'stringish<CharName>("Unknown"_s)')
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..23bd071 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/legacy/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