Merge branch 'master' into qteditor

qteditor
Nicolas Hake 2016-04-03 21:06:32 +02:00
commit 9dddf289db
788 changed files with 11028 additions and 7811 deletions

View File

@ -1,6 +1,6 @@
# OpenClonk, http://www.openclonk.org
#
# Copyright (c) 2009-2015, The OpenClonk Team and contributors
# Copyright (c) 2009-2016, The OpenClonk Team and contributors
#
# Distributed under the terms of the ISC license; see accompanying file
# "COPYING" for details.
@ -12,6 +12,11 @@
# for the above references.
cmake_minimum_required (VERSION 3.0.2)
# Don't allow people to build "Release" builds because there's no reason to do that.
# Use one of RelWithDebInfo or MinSizeRel instead.
set(CMAKE_CONFIGURATION_TYPES Debug RelWithDebInfo MinSizeRel CACHE STRING "List of supported configuration types." FORCE)
project (openclonk CXX C)
# CMP0054: Only interpret if() arguments as variables or keywords when unquoted
@ -45,93 +50,116 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ${PROJECT_FOLDERS})
############################################################################
include(CheckCXXCompilerFlag)
set(OC_CXX_FLAGS ${CMAKE_CXX_FLAGS})
separate_arguments(OC_CXX_FLAGS)
set(OC_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
separate_arguments(OC_CXX_FLAGS_DEBUG)
set(OC_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS})
separate_arguments(OC_EXE_LINKER_FLAGS)
set(OC_EXE_LINKER_FLAGS_DEBUG ${CMAKE_EXE_LINKER_FLAGS_DEBUG})
separate_arguments(OC_EXE_LINKER_FLAGS_DEBUG)
CHECK_CXX_COMPILER_FLAG("-std=gnu++14" USE_GCC_STD_14)
if(USE_GCC_STD_14)
list(APPEND OC_CXX_FLAGS "-std=gnu++14")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++14")
endif()
# Enable link-time code generation. We have to do this manually because while
# there is a INTERPROCEDURAL_OPTIMIZATION cmake flag, it's only implemented
# for icc so far; https://cmake.org/Bug/view.php?id=15939
function(add_linker_flags)
include(CMakeParseArguments)
set(options optimized debug)
set(oneValueArgs FLAGS)
set(multiValueArgs MODULES)
cmake_parse_arguments(_alf "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
# Adds some linker flags to all optimized build configurations
set(_configurations "")
if(_alf_optimized)
list(APPEND _configurations MINSIZEREL RELWITHDEBINFO RELEASE)
endif()
if(_alf_debug)
list(APPEND _configurations DEBUG)
endif()
foreach(_module ${_alf_MODULES})
string(TOUPPER "${_module}" _obj_type)
foreach(_config ${_configurations})
set(CMAKE_${_obj_type}_LINKER_FLAGS_${_config} "${CMAKE_${_obj_type}_LINKER_FLAGS_${_config}} ${_alf_FLAGS}" PARENT_SCOPE)
endforeach()
endforeach()
endfunction()
CHECK_CXX_COMPILER_FLAG("-flto" USE_GCC_STYLE_LTCG)
if(USE_GCC_STYLE_LTCG)
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -flto")
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -flto")
add_linker_flags(optimized MODULES exe shared FLAGS -flto)
# Use GCC's ar and ranlib wrappers if necessary, because the plain ones
# don't understand lto objects without an explicit plugin parameter
if(CMAKE_C_COMPILER MATCHES "gcc$")
set(LTCG_NEEDS_AR_WRAPPER 1)
set(LTCG_AR_WRAPPER_PREFIX "${CMAKE_C_COMPILER}")
elseif(CMAKE_C_COMPILER MATCHES "cc$")
set(LTCG_NEEDS_AR_WRAPPER 1)
set(LTCG_AR_WRAPPER_PREFIX "gcc")
else()
set(LTCG_NEEDS_AR_WRAPPER 0)
endif()
if(LTCG_NEEDS_AR_WRAPPER)
find_program(AR_WRAPPER "${LTCG_AR_WRAPPER_PREFIX}-ar")
if (AR_WRAPPER)
message("Using ${AR_WRAPPER} instead of ${CMAKE_AR} to support lto objects.")
set(CMAKE_AR "${AR_WRAPPER}" CACHE FILEPATH "Path to an ar that supports lto objects." FORCE)
endif()
find_program(RANLIB_WRAPPER "${LTCG_AR_WRAPPER_PREFIX}-ranlib")
if (RANLIB_WRAPPER)
message("Using ${RANLIB_WRAPPER} instead of ${CMAKE_RANLIB} to support lto objects.")
set(CMAKE_RANLIB "${RANLIB_WRAPPER}" CACHE FILEPATH "Path to a ranlib that supports lto objects." FORCE)
endif()
endif()
endif()
if(MSVC)
list(APPEND OC_CXX_FLAGS /MP)
list(REMOVE_ITEM OC_CXX_FLAGS_DEBUG /Gm)
# Disable non-standard conversion from string literal to (nonconst) char*
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:strictStrings")
# Enable multi-core builds
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
# Enable LTCG for release builds
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} /Ob2 /GL")
set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} /Ob2 /GL")
add_linker_flags(optimized MODULES exe shared static FLAGS /LTCG)
# Activate edit-and-continue
list(REMOVE_ITEM OC_CXX_FLAGS_DEBUG /Zi)
list(APPEND OC_CXX_FLAGS_DEBUG /ZI /Gy)
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /ZI /Gy")
# do not link the release CRT in debug builds
list(APPEND OC_EXE_LINKER_FLAGS_DEBUG "/NODEFAULTLIB:MSVCRT")
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:MSVCRT")
set(HAVE_PRECOMPILED_HEADERS ON CACHE INTERNAL "Compiler supports precompiled headers")
# Suppress warnings about "non-secure" functions
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_WARNINGS)
# Disable warning C4244: 'conversion' conversion from 'type1' to 'type2', possible loss of data
list(APPEND OC_CXX_FLAGS "/wd4244")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4244")
# Disable warning C4267: 'var' : conversion from 'size_t' to 'type', possible loss of data (64 bit build only)
list(APPEND OC_CXX_FLAGS "/wd4267")
endif()
if(CMAKE_COMPILER_IS_GNUCXX)
list(APPEND OC_CXX_FLAGS -Wall -Wextra -Wredundant-decls -Wendif-labels -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -Winit-self -Wsign-promo -Wno-reorder -Wno-unused-parameter -Wnon-virtual-dtor -Woverloaded-virtual)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4267")
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
list(APPEND OC_CXX_FLAGS -Wall -Wextra -Wextra-tokens -Wpointer-arith -Wno-cast-align -Wno-reorder -Wno-unused-parameter -Wnon-virtual-dtor -Woverloaded-virtual)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wextra-tokens -Wpointer-arith -Wno-cast-align -Wno-reorder -Wno-unused-parameter -Wnon-virtual-dtor -Woverloaded-virtual")
elseif(CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wredundant-decls -Wendif-labels -Wpointer-arith -Wcast-qual -Wcast-align -Wwrite-strings -Winit-self -Wsign-promo -Wno-reorder -Wno-unused-parameter -Wnon-virtual-dtor -Woverloaded-virtual")
endif()
if(WIN32 AND MINGW)
# Activate DEP and ASLR
list(APPEND OC_EXE_LINKER_FLAGS -Wl,--nxcompat -Wl,--dynamicbase)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--nxcompat -Wl,--dynamicbase")
endif()
if(UNIX)
# Don't put this into CMAKE_CXX_FLAGS because otherwise it is cached,
# and when the path is changed both the old and new definition appears
# in the list of flags.
add_definitions("-DOC_SYSTEM_DATA_DIR=\"${CMAKE_INSTALL_PREFIX}/share/games/openclonk\"")
endif()
if(APPLE)
list(APPEND OC_CXX_FLAGS -fobjc-arc)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fobjc-arc")
endif()
if(OC_CXX_FLAGS)
list(REMOVE_DUPLICATES OC_CXX_FLAGS)
endif()
set(CMAKE_CXX_FLAGS "" CACHE STRING "C++ compiler flags" FORCE)
foreach(FLAG ${OC_CXX_FLAGS})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAG}" CACHE STRING "C++ compiler flags" FORCE)
endforeach()
if(OC_CXX_FLAGS_DEBUG)
list(REMOVE_DUPLICATES OC_CXX_FLAGS_DEBUG)
endif()
set(CMAKE_CXX_FLAGS_DEBUG "" CACHE STRING "Flags used by the compiler during debug builds." FORCE)
foreach(FLAG ${OC_CXX_FLAGS_DEBUG})
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAG}" CACHE STRING "Flags used by the compiler during debug builds." FORCE)
endforeach()
if(OC_EXE_LINKER_FLAGS)
list(REMOVE_DUPLICATES OC_EXE_LINKER_FLAGS)
endif()
set(CMAKE_EXE_LINKER_FLAGS "" CACHE STRING "Flags used by the linker." FORCE)
foreach(FLAG ${OC_EXE_LINKER_FLAGS})
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAG}" CACHE STRING "Flags used by the linker." FORCE)
endforeach()
if(OC_EXE_LINKER_FLAGS_DEBUG)
list(REMOVE_DUPLICATES OC_EXE_LINKER_FLAGS_DEBUG)
endif()
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "" CACHE STRING "Flags used by the linker during debug builds." FORCE)
foreach(FLAG ${OC_EXE_LINKER_FLAGS_DEBUG})
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${FLAG}" CACHE STRING "Flags used by the linker during debug builds." FORCE)
endforeach()
############################################################################
# Check for compiler quirks and features
############################################################################
@ -177,7 +205,6 @@ CHECK_INCLUDE_FILE_CXX(sys/socket.h HAVE_SYS_SOCKET_H)
CHECK_INCLUDE_FILE_CXX(sys/eventfd.h HAVE_SYS_EVENTFD_H)
CHECK_INCLUDE_FILE_CXX(sys/file.h HAVE_SYS_FILE_H)
CHECK_INCLUDE_FILES_CXX("X11/Xlib.h;X11/extensions/Xrandr.h" HAVE_X11_EXTENSIONS_XRANDR_H)
CHECK_INCLUDE_FILES_CXX("X11/Xlib.h;X11/keysym.h" HAVE_X11_KEYSYM_H)
CHECK_CXX_SOURCE_COMPILES("#include <getopt.h>\nint main(int argc, char * argv[]) { getopt_long(argc, argv, \"\", 0, 0); }" HAVE_GETOPT_H)
############################################################################
@ -302,7 +329,7 @@ endif()
# SDL
if(USE_SDL_MAINLOOP)
find_package(SDL2 REQUIRED)
elseif(NOT WIN32)
else()
# for gamepads
find_package(SDL2)
endif()
@ -480,20 +507,14 @@ set(OC_CLONK_SOURCES
src/game/C4FullScreen.h
src/game/C4Game.cpp
src/game/C4Game.h
src/game/C4GameScript.cpp
src/game/C4GameScript.h
src/game/C4GameVersion.h
src/game/C4GraphicsSystem.cpp
src/game/C4GraphicsSystem.h
src/game/C4Physics.h
src/game/C4Viewport.cpp
src/game/C4Viewport.h
src/gamescript/C4Effect.cpp
src/gamescript/C4Effect.h
src/gamescript/C4FindObject.cpp
src/gamescript/C4FindObject.h
src/gamescript/C4GameScript.cpp
src/gamescript/C4GameScript.h
src/gamescript/C4TransferZone.cpp
src/gamescript/C4TransferZone.h
src/graphics/Bitmap256.cpp
src/graphics/Bitmap256.h
src/graphics/C4Draw.cpp
@ -607,7 +628,6 @@ set(OC_CLONK_SOURCES
src/landscape/fow/C4FoWBeamTriangle.h
src/landscape/C4Landscape.cpp
src/landscape/C4Landscape.h
src/landscape/C4LandscapeRenderClassic.cpp
src/landscape/C4LandscapeRender.cpp
src/landscape/C4LandscapeRender.h
src/landscape/C4Map.cpp
@ -640,6 +660,8 @@ set(OC_CLONK_SOURCES
src/landscape/C4Texture.h
src/landscape/C4TextureShape.cpp
src/landscape/C4TextureShape.h
src/landscape/C4TransferZone.cpp
src/landscape/C4TransferZone.h
src/landscape/C4Weather.cpp
src/landscape/C4Weather.h
src/lib/C4LogBuf.cpp
@ -710,6 +732,8 @@ set(OC_CLONK_SOURCES
src/object/C4Def.h
src/object/C4DefList.cpp
src/object/C4DefList.h
src/object/C4FindObject.cpp
src/object/C4FindObject.h
src/object/C4GameObjects.cpp
src/object/C4GameObjects.h
src/object/C4Id.cpp
@ -956,8 +980,6 @@ elseif(USE_WIN32_WINDOWS)
endif()
list(APPEND OC_GUI_SOURCES
src/platform/C4WindowWin32.cpp
src/platform/StdJoystick.cpp
src/platform/StdJoystick.h
)
elseif(USE_COCOA)
list(APPEND OC_GUI_SOURCES
@ -1002,23 +1024,6 @@ endif()
include_directories(
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/src/c4group
${CMAKE_CURRENT_SOURCE_DIR}/src/config
${CMAKE_CURRENT_SOURCE_DIR}/src/control
${CMAKE_CURRENT_SOURCE_DIR}/src/editor
${CMAKE_CURRENT_SOURCE_DIR}/src/game
${CMAKE_CURRENT_SOURCE_DIR}/src/gamescript
${CMAKE_CURRENT_SOURCE_DIR}/src/graphics
${CMAKE_CURRENT_SOURCE_DIR}/src/gui
${CMAKE_CURRENT_SOURCE_DIR}/src/landscape
${CMAKE_CURRENT_SOURCE_DIR}/src/landscape/fow
${CMAKE_CURRENT_SOURCE_DIR}/src/lib
${CMAKE_CURRENT_SOURCE_DIR}/src/network
${CMAKE_CURRENT_SOURCE_DIR}/src/object
${CMAKE_CURRENT_SOURCE_DIR}/src/platform
${CMAKE_CURRENT_SOURCE_DIR}/src/player
${CMAKE_CURRENT_SOURCE_DIR}/src/res
${CMAKE_CURRENT_SOURCE_DIR}/src/script
)
# Mark thirdparty as system headers so we don't get warnings from them
@ -1103,6 +1108,8 @@ src/script/C4AulLink.cpp
src/script/C4AulParse.cpp
src/script/C4AulScriptFunc.cpp
src/script/C4AulScriptFunc.h
src/script/C4Effect.cpp
src/script/C4Effect.h
src/script/C4PropList.cpp
src/script/C4PropList.h
src/script/C4Script.cpp
@ -1176,12 +1183,13 @@ add_executable(openclonk-server
src/platform/C4StdInProc.cpp
src/platform/C4StdInProc.h
)
set_property(TARGET openclonk-server APPEND PROPERTY COMPILE_DEFINITIONS "USE_CONSOLE")
target_compile_definitions(openclonk-server PRIVATE "USE_CONSOLE")
target_link_libraries(openclonk-server
${PNG_LIBRARIES}
${JPEG_LIBRARIES}
${EXECINFO_LIBRARY}
${SDL2_LIBRARIES}
${READLINE_LIBRARIES}
${Audio_LIBRARIES}
${GETOPT_LIBRARIES}

View File

@ -1,5 +1,5 @@
Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
Copyright (c) 2009-2015, The OpenClonk Team and contributors
Copyright (c) 2009-2016, The OpenClonk Team and contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above

View File

@ -1,6 +1,6 @@
# OpenClonk, http://www.openclonk.org
#
# Copyright (c) 2009-2015, The OpenClonk Team and contributors
# Copyright (c) 2009-2016, The OpenClonk Team and contributors
#
# Distributed under the terms of the ISC license; see accompanying file
# "COPYING" for details.

View File

@ -1,6 +1,6 @@
# OpenClonk, http://www.openclonk.org
#
# Copyright (c) 2011-2015, The OpenClonk Team and contributors
# Copyright (c) 2011-2016, The OpenClonk Team and contributors
#
# Distributed under the terms of the ISC license; see accompanying file
# "COPYING" for details.

View File

@ -1,6 +1,6 @@
# OpenClonk, http://www.openclonk.org
#
# Copyright (c) 2014-2015, The OpenClonk Team and contributors
# Copyright (c) 2014-2016, The OpenClonk Team and contributors
#
# Distributed under the terms of the ISC license; see accompanying file
# "COPYING" for details.

View File

@ -1,6 +1,6 @@
# OpenClonk, http://www.openclonk.org
#
# Copyright (c) 2015, The OpenClonk Team and contributors
# Copyright (c) 2015-2016, The OpenClonk Team and contributors
#
# Distributed under the terms of the ISC license; see accompanying file
# "COPYING" for details.

View File

@ -1,6 +1,6 @@
# OpenClonk, http://www.openclonk.org
#
# Copyright (c) 2015, The OpenClonk Team and contributors
# Copyright (c) 2015-2016, The OpenClonk Team and contributors
#
# Distributed under the terms of the ISC license; see accompanying file
# "COPYING" for details.

View File

@ -1,6 +1,6 @@
# OpenClonk, http://www.openclonk.org
#
# Copyright (c) 2011-2013, The OpenClonk Team and contributors
# Copyright (c) 2011-2016, The OpenClonk Team and contributors
#
# Distributed under the terms of the ISC license; see accompanying file
# "COPYING" for details.
@ -26,6 +26,13 @@ ENDIF (READLINE_INCLUDE_DIR)
FIND_PATH(READLINE_INCLUDE_DIR readline.h PATH_SUFFIXES readline)
# Unmodified readline depends on symbols from termcap without explicitly
# linking to it. Several distributions patch this to make it link against
# terminfo from ncurses or another termcap library, but some don't. To avoid
# having to run link tests, we'll just look for and use any termcap providing
# library.
FIND_LIBRARY(TERMCAP_LIBRARY NAMES tinfo termcap ncursesw ncurses cursesw curses)
SET(READLINE_NAMES readline libreadline)
FIND_LIBRARY(READLINE_LIBRARY NAMES ${READLINE_NAMES} )
@ -35,10 +42,13 @@ INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(READLINE DEFAULT_MSG READLINE_LIBRARY READLINE_INCLUDE_DIR)
IF(READLINE_FOUND)
SET( READLINE_LIBRARIES ${READLINE_LIBRARY} )
if(TERMCAP_LIBRARY)
set(READLINE_LIBRARIES ${READLINE_LIBRARY} ${TERMCAP_LIBRARY})
else()
set(READLINE_LIBRARIES ${READLINE_LIBRARY})
endif()
ELSE(READLINE_FOUND)
SET( READLINE_LIBRARIES )
ENDIF(READLINE_FOUND)
MARK_AS_ADVANCED( READLINE_LIBRARY READLINE_INCLUDE_DIR )
MARK_AS_ADVANCED( READLINE_LIBRARY TERMINFO_LIBRARY READLINE_INCLUDE_DIR )

View File

@ -17,9 +17,19 @@
# SDL2_LIBRARIES - a list of libraries to link against to use SDL2
# SDL2_FOUND - if false, SDL2 cannot be used
find_path(SDL2_INCLUDE_DIR SDL.h PATH_SUFFIXES SDL2 HINTS ENV SDL2DIR)
find_path(SDL2_INCLUDE_DIR SDL.h
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES SDL2 include
)
mark_as_advanced(SDL2_INCLUDE_DIR)
find_library(SDL2_LIBRARY SDL2 HINTS ENV SDL2DIR)
find_library(SDL2_LIBRARY
SDL2
HINTS
$ENV{SDL2DIR}
PATH_SUFFIXES lib
)
mark_as_advanced(SDL2_LIBRARY)
include(FindPackageHandleStandardArgs)

View File

@ -1,6 +1,6 @@
# OpenClonk, http://www.openclonk.org
#
# Copyright (c) 2012-2015, The OpenClonk Team and contributors
# Copyright (c) 2012-2016, The OpenClonk Team and contributors
#
# Distributed under the terms of the ISC license; see accompanying file
# "COPYING" for details.

View File

@ -1,6 +1,6 @@
# OpenClonk, http://www.openclonk.org
#
# Copyright (c) 2012-2013, The OpenClonk Team and contributors
# Copyright (c) 2012-2016, The OpenClonk Team and contributors
#
# Distributed under the terms of the ISC license; see accompanying file
# "COPYING" for details.

View File

@ -1,6 +1,6 @@
# OpenClonk, http://www.openclonk.org
#
# Copyright (c) 2013, The OpenClonk Team and contributors
# Copyright (c) 2013-2016, The OpenClonk Team and contributors
#
# Distributed under the terms of the ISC license; see accompanying file
# "COPYING" for details.

View File

@ -1,6 +1,6 @@
# OpenClonk, http://www.openclonk.org
#
# Copyright (c) 2009-2015, The OpenClonk Team and contributors
# Copyright (c) 2009-2016, The OpenClonk Team and contributors
#
# Distributed under the terms of the ISC license; see accompanying file
# "COPYING" for details.

View File

@ -80,9 +80,6 @@
/* Define to 1 if you have the <X11/extensions/Xrandr.h> header file. */
#cmakedefine HAVE_X11_EXTENSIONS_XRANDR_H 1
/* Define to 1 if you have the <X11/keysym.h> header file. */
#cmakedefine HAVE_X11_KEYSYM_H 1
/* compile without debug options */
#cmakedefine NDEBUG 1

View File

@ -71,21 +71,21 @@ clean:
sdk/content.xml: sdk/content.xml.in $(xmlfiles) build_contents.py experimental.py
@echo generate $@
@python build_contents.py $(xmlfiles)
@python2 build_contents.py $(xmlfiles)
chm/en/Output.hhp: $(xmlfiles) chm/en/. build_hhp.py Template.hhp
@echo generate $@
@python build_hhp.py $@ Template.hhp $(xmlfiles)
@python2 build_hhp.py $@ Template.hhp $(xmlfiles)
chm/de/Output.hhp: $(xmlfiles) chm/de/. build_hhp.py Template.de.hhp
@echo generate $@
@python build_hhp.py $@ Template.de.hhp $(xmlfiles)
@python2 build_hhp.py $@ Template.de.hhp $(xmlfiles)
$(sdk-de-dirs) $(online-dirs) $(chm-dirs):
mkdir -p $@
doku.pot: $(xmlfiles) extra-strings.xml sdk/content.xml.in xml2po.py clonk.py
@echo extract strings to $@
@python xml2po.py -e -m clonk -o $@ $(xmlfiles) extra-strings.xml sdk/content.xml.in
@python2 xml2po.py -e -m clonk -o $@ $(xmlfiles) extra-strings.xml sdk/content.xml.in
%.po: doku.pot
@echo update $@
@ -98,7 +98,7 @@ doku.pot: $(xmlfiles) extra-strings.xml sdk/content.xml.in xml2po.py clonk.py
sdk-de/%.xml: sdk/%.xml de.mo xml2po.py clonk.py
@echo generate $@
@python xml2po.py -e -m clonk -t de.mo -o $@ $<
@python2 xml2po.py -e -m clonk -t de.mo -o $@ $<
define run-xslt
@echo generate $@

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
import sys
import xml.sax

View File

@ -5,7 +5,7 @@
<doc>
<title>CNAT - Contact Attachment</title>
<h>CNAT - Contact Attachment</h>
<text>In multiple places the engine uses ContactAttachment values (a bitmask) to manage the orientation of objects and processes. For example, a vertex of an object can have the <code id="CNAT">CNAT</code> bit <em>left</em>. If that object has <emlink href="definition/defcore.html">ContactCalls</emlink> activated, the engine calls on every contact of that vertex with the landscape the object script function <em>ContactLeft</em>. CNAT values are composed of the following bits:</text>
<text>In multiple places the engine uses ContactAttachment values (a bitmask) to manage the orientation of objects and processes. For example, a vertex of an object can have the <code id="CNAT">CNAT</code> bit <em>left</em>. If that object has <emlink href="definition/properties.html">ContactCalls</emlink> activated, the engine calls on every contact of that vertex with the landscape the object script function <em>ContactLeft</em>. CNAT values are composed of the following bits:</text>
<text>
<table>
<rowh>
@ -40,6 +40,10 @@
<col>CNAT_NoCollision</col>
<col>Extra flag: non-colliding vertex</col>
</row>
<row>
<col>CNAT_PhaseHalfVehicle</col>
<col>Extra flag: Entirely suppress collisions with HalfVehicle</col>
</row>
<bitmask>CNAT</bitmask>
</table>
</text>

View File

@ -35,11 +35,6 @@
<col>Integer</col>
<col>Category of the object. Also see <emlink href="definition/category.html">object categories</emlink>.</col>
</row>
<row>
<literal_col>ContactCalls</literal_col>
<col>Integer</col>
<col>0 or 1. If 1, Contactcalls are called in the object script.</col>
</row>
<row>
<literal_col>Width</literal_col>
<col>Integer</col>

View File

@ -101,7 +101,7 @@
<row id="CONNECT">
<col>DFA_CONNECT</col>
<col>Line connections</col>
<col>Only <emlink href="definition/lineconnect.html">line objects</emlink>. Connects <emlink href="script/fn/SetAction.html">target object 1</emlink> and <emlink href="script/fn/SetAction.html">target object 2</emlink>. If property LineMaxDistance is a nonzero integer, the line breaks when the target objects are further apart than the given distance.</col>
<col>Only <emlink href="definition/lineconnect.html">line objects</emlink>. Connects <emlink href="script/fn/SetAction.html">target object 1</emlink> and <emlink href="script/fn/SetAction.html">target object 2</emlink>.</col>
<col>CNAT_None</col>
</row>
<row id="PULL">

View File

@ -133,7 +133,7 @@ Stand = {
<row id="Plane">
<col><code>Plane</code></col>
<col>int</col>
<col>The Object's minor Z-Position. Negative values are behind the landscape, positive values before it. Use 1-399 for stuff behind Clonks, 401-999 for stuff before Clonks, and 1000+ for GUI objects.</col>
<col>The Object's minor Z-Position. Negative values are behind the landscape, positive values before it. Use 1-399 for stuff behind Clonks, 401-999 for stuff before Clonks, and 1000+ for GUI objects. Global particles are on 900.</col>
</row>
<row id="SolidMaskPlane">
<col><code>SolidMaskPlane</code></col>
@ -180,6 +180,11 @@ Stand = {
<col>Integer</col>
<col>Bit mask indicating object boundaries: stop at map sides (C4D_Border_Sides), stop at map top (C4D_Border_Top), stop at map bottom (C4D_Border_Sides), stop at object layer boundaries (C4D_Border_Layer). For example BorderBound = C4D_Border_Top | C4D_Border_Bottom.</col>
</row>
<row>
<literal_col>ContactCalls</literal_col>
<col>bool</col>
<col>True or false. If true, <emlink href="definition/cnat.html">ContactCalls</emlink> are called in the object script.</col>
</row>
</table>
</text>
</part>

View File

@ -188,11 +188,16 @@
<col>object by</col>
<col>Called before the object is hit or punched by another object. By returning <code>true</code>, QueryCatchBlow can reject physical blows.</col>
</row>
<row id="LineBreak">
<literal_col>LineBreak</literal_col>
<row id="OnLineBreak">
<literal_col>OnLineBreak</literal_col>
<col>int cause</col>
<col>When a line object is broken. cause: 0 by movement, 1 because of a missing or incomplete target object.</col>
</row>
<row id="OnLineChange">
<literal_col>OnLineChange</literal_col>
<col>int cause</col>
<col>When a line object is changed, that is when one of it vertices changed its position.</col>
</row>
<row id="BuildNeedsMaterial">
<literal_col>BuildNeedsMaterial</literal_col>
<col>id material_definition, int amount</col>

View File

@ -106,9 +106,12 @@
<col>Inflammability.</col>
</row>
<row>
<col>Incindiary</col>
<col>Incendiary</col>
<col>Integer</col>
<col>This material incinerates objects.</col>
<col>
This material incinerates objects.
<em>Note: For compatibility reasons, the misspelling &quot;Incindiary&quot; is also accepted if (and only if) no value with the correct spelling is set.</em>
</col>
</row>
<row>
<col>Corrode</col>
@ -372,8 +375,11 @@
<col>The background material.</col>
</row>
<row>
<col>Incindiary</col>
<col>All materials to be incinerated.</col>
<col>Incendiary</col>
<col>
All materials that can incinerate objects.
<em>Note: For compatibility reasons, the misspelling &quot;Incindiary&quot; is also accepted.</em>
</col>
</row>
<row>
<col>Extinguisher</col>

View File

@ -57,6 +57,8 @@
<funclink>PV_KeyFrames</funclink>
<funclink>PV_Wind</funclink>
<funclink>PV_Gravity</funclink>
<funclink>PV_Sin</funclink>
<funclink>PV_Cos</funclink>
</text>
<text>
<table>
@ -149,7 +151,7 @@
<row>
<col>Attach</col>
<col>bit mask</col>
<col>Defines the attachment of the particles to the calling object. Can be a combination of ATTACH_Front, ATTACH_Back, and ATTACH_MoveRelative. For example <code>ATTACH_Front | ATTACH_MoveRelative</code></col>
<col>Defines the attachment of the particles to the calling object. Can be a combination of ATTACH_Front, ATTACH_Back, and ATTACH_MoveRelative. For example <code>ATTACH_Front | ATTACH_MoveRelative. Non-attached particles are drawn on plane 900 (i.e. before most objects).</code></col>
</row>
</table>
</text>

View File

@ -273,7 +273,7 @@
return true;
}</code>
<text>most commands (except for asynchronous commands in the player menu) call a global script function:</text>
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, bool release)</code>
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, int state)</code>
<text>For an explanation of the parameters see <funclink>PlayerControl</funclink>. Amongst others, the function receives the calling player in player as well as the command to be executed in control.</text>
<text>As a simple example let's assume that in the global <em>PlayerControls.txt</em> the following command has been defined:</text>
<code>[ControlDefs]
@ -293,11 +293,11 @@
Control=Jump
Priority=50</code>
<text>This defines a Jump key and the corresponding standard mapping on the keyboard for the first player. The following script is used to handle the control:</text>
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, bool release)
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, int state)
{
// Which command has been issued?
// The constant CON_Jump has been declared automatically through the definition in PlayerControls.txt
if (control == CON_Jump &amp;&amp; !release)
if (control == CON_Jump &amp;&amp; state == CONS_Down)
{
// pressed the jump button. The clonk selected by the player shall jump
var player_clonk = GetCursor(player);
@ -319,17 +319,17 @@
GUIDesc=Going underground
ExtraData=Shovel</code>
<text>Let shovel be the ID of a shovel object. In the global script there could be the following, generic handling for unknown commands, for example:</text>
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, bool release)
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, int state)
{
// Handling of known controls
// [...]
// control with own handling
if (control_extra) return control_extra-&gt;PlayerControl(player, control, x, y, strength, repeat, release);
if (control_extra) return control_extra-&gt;PlayerControl(player, control, x, y, strength, repeat, state);
// unkown control
return false;
}</code>
<text>And in the script of the shovel:</text>
<code>func PlayerControl(int player, int control, int x, int y, int strength, bool repeated, bool release)
<code>func PlayerControl(int player, int control, int x, int y, int strength, bool repeated, int state)
{
// Handling of known controls
// Control dig directly in the shovel
@ -353,6 +353,7 @@
<li>Mappings can emulate permanent key presses using the <em>Hold</em>/<em>Release</em> flags.</li>
<li><emlink href="playercontrols.xml#Repeat">Key repeats</emlink> are generated.</li>
<li>The held state of the key can be queried in the script via <funclink>GetPlayerControlState</funclink>.</li>
<li>If the command is bound to an analog stick or trigger on a controller, every change in position causes in a call to PlayerControl() with state = CONS_Moved.</li>
</ul>
</text>
<text>A good example for this functionality is a directional command:</text>
@ -362,7 +363,7 @@
GUIDesc=Walk left
Hold=1</code>
<text>In the script the direction is transferred to the Clonk:</text>
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, bool release)
<code>global func PlayerControl(int player, int control, C4ID control_extra, int x, int y, int strength, bool repeated, int state)
{
if (control == CON_Left) return UpdateControlDir(player);
// ...

View File

@ -555,8 +555,8 @@ global func FxExplodeOnDeathCurseStop(object target, proplist effect, int reason
<text>new_timer is the timer interval of the new effect; var1 to var4 are the parameters from AddEffect. Notice: in temporary calls, these parameters are not available - here they will be 0.</text>
<text>If -1 is returned, the accepting effect is deleted also. Logically, the calling AddEffect function will then return -2.</text>
<h>Fx*Damage</h>
<text><code>int Fx*Damage (object target, proplist effect, int damage, int cause);</code></text>
<text>Every effect receives this callback whenever the energy or damage value of the target object is to change. If the function is defined, it should then return whether to allow the change.</text>
<text><code>int Fx*Damage (object target, proplist effect, int damage, int cause, int by_player);</code></text>
<text>Every effect receives this callback whenever the energy or damage value of the target object is to change. If the function is defined, it should then return the damage to be done to the target.</text>
<text id="damagecause">This callback is made upon life energy changes in living beings and damage value changes in non-livings - but not vice versa. cause contains the value change and reason:</text>
<text>
<table>

View File

@ -9,17 +9,17 @@
<subcat>Vertices</subcat>
<version>5.1 OC</version>
<syntax>
<rtype>int</rtype>
<rtype>bool</rtype>
<params>
<param>
<type>int</type>
<name>x</name>
<desc>X coordinate, relative to the object center</desc>
<desc>X coordinate, relative to the object center.</desc>
</param>
<param>
<type>int</type>
<name>y</name>
<desc>Y coordinate, relative to the object center</desc>
<desc>Y coordinate, relative to the object center.</desc>
</param>
</params>
</syntax>
@ -32,6 +32,7 @@
</example>
</examples>
<related>
<funclink>InsertVertex</funclink>
<funclink>GetVertex</funclink>
<funclink>SetVertex</funclink>
<funclink>GetVertexNum</funclink>

View File

@ -6,7 +6,7 @@
<func>
<title>GetPlayerControlState</title>
<category>Player</category>
<version>5.1 OC</version>
<version>5.1 OC (extended in 8.0 OC)</version>
<syntax>
<rtype>int</rtype>
<params>
@ -20,9 +20,15 @@
<name>control</name>
<desc>Control to query. A CON_* constant should be used here.</desc>
</param>
<param>
<type>bool</type>
<name>analog_strength</name>
<desc>If true: Query current state of an analog control on a gamepad instead of the emulated button state.</desc>
<optional />
</param>
</params>
</syntax>
<desc>Returns the current state of a control for a certain player. The return value is the strength of the control (e.g. for gamepad joysticks). If the control is assigned to a key, a value not equal to 0 means that the key is currently held down by the player.</desc>
<desc>Returns the current state of a control for a certain player. If the control is assigned to a key, a value not equal to 0 means that the key is currently held down by the player. For analog controls on gamepads, the function either queries the current emulated button state (analog_strength = false), or the current position of the stick or trigger (analog_strength = true).</desc>
<examples>
<example>
<code>
@ -36,4 +42,5 @@ if (GetPlayerControlState(GetOwner(), CON_Left) != 0)
</related>
</func>
<author>Zapper</author><date>2015-10</date>
<author>Luchs</author><date>2016-02</date>
</funcs>

View File

@ -14,7 +14,7 @@
<param>
<type>int</type>
<name>index</name>
<desc>Index of the vertex to be changed.</desc>
<desc>Index of the vertex to be changed, ranges from 0 to <funclink>GetVertexNum</funclink>.</desc>
</param>
<param>
<type>int</type>

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE funcs
SYSTEM '../../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<funcs>
<func>
<title>InsertVertex</title>
<category>Objects</category>
<subcat>Vertices</subcat>
<version>8.0 OC</version>
<syntax>
<rtype>bool</rtype>
<params>
<param>
<type>int</type>
<name>index</name>
<desc>Index of the vertex to be removed, ranges from 0 to <funclink>GetVertexNum</funclink>.</desc>
</param>
<param>
<type>int</type>
<name>x</name>
<desc>X coordinate, relative to the object center.</desc>
</param>
<param>
<type>int</type>
<name>y</name>
<desc>Y coordinate, relative to the object center.</desc>
</param>
</params>
</syntax>
<desc>Inserts a new vertex to an object.</desc>
<remark>Notice: with any vertex updated caused by stretching or rotation of the object (e.g. building or growth) the vertices will be reset to their original defined position unless a special vertex mode is selected.</remark>
<related>
<funclink>AddVertex</funclink>
<funclink>GetVertex</funclink>
<funclink>SetVertex</funclink>
<funclink>GetVertexNum</funclink>
<funclink>RemoveVertex</funclink>
</related>
</func>
<author>Maikel</author><date>2016-03</date>
</funcs>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE funcs
SYSTEM '../../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<funcs>
<func>
<title>PV_Cos</title>
<category>Particles</category>
<version>8.0 OC</version>
<syntax>
<rtype>array</rtype>
<params>
<param>
<type>int</type>
<name>value</name>
<desc>Value or value provider to calculate the cosine of. The result of <funclink>PV_Linear</funclink> can be used to evaluate the cosine function linearly over time.</desc>
</param>
<param>
<type>int</type>
<name>amplitude</name>
<desc>Amplitude of result. May be a value or value provider.</desc>
</param>
<param>
<type>int</type>
<name>offset</name>
<desc>Offset added to result. May be a value or value provider. Defaults to zero.</desc>
<optional />
</param>
</params>
</syntax>
<desc>The value will is calculates as cos(value) * amplitude + offset with value given in degrees.</desc>
<remark>See the <emlink href="particle/index.html">particle documentation</emlink> for further explanations of the particle system.</remark>
<remark>See <funclink>PV_Random</funclink> for another example with PV_Cos.</remark>
<related>
<funclink>CreateParticle</funclink>
<funclink>PV_Sin</funclink>
<funclink>PV_Direction</funclink>
<funclink>PV_Random</funclink>
<funclink>PV_Step</funclink>
<funclink>PV_Linear</funclink>
<funclink>PV_Speed</funclink>
<funclink>PV_KeyFrames</funclink>
</related>
</func>
<author>Zapper</author><date>2016-02</date>
</funcs>

View File

@ -27,12 +27,37 @@
<name>reroll_interval</name>
<desc>Interval in frames after which a new random number will be drawn.</desc>
<optional />
</param>
<param>
<type>int</type>
<name>seed</name>
<desc>Particle-local seed that is used for the random rool. Can be used to draw the same random number two times (see example).</desc>
<optional />
</param>
</params>
</syntax>
<desc>The value will be a random number in the interval from start_value to (not including) end_value. The values in between are not whole integers, but are also in fraction of integers. This means that PV_Random(0, 1) can not only return one value (the 0) but a lot of different values in the interval between 0 and 1.</desc>
<remark>See the <emlink href="particle/index.html">particle documentation</emlink> for further explanations of the particle system.</remark>
<related>
<examples>
<example>
<code><funclink>CreateParticle</funclink>("MagicRing", 0, 0, PV_Random(-100, 100), -20, 100, {Prototype = Particles_Glimmer(), Size = PV_Random(0, 3, 10)}, 100);</code>
<text>Creates 100 particles with a random speed in X direction and a random size (which changes every ten frames).</text>
</example>
<example>
<code>
var min_speed = 50;
var max_speed = 100;
var min_angle = 0;
var max_angle = 360;
<funclink>CreateParticle</funclink>("SphereSpark", 0, 0,
<funclink>PV_Sin</funclink>(<funclink>PV_Random</funclink>(min_angle, max_angle, 0, 1), <funclink>PV_Random</funclink>(min_speed, max_speed, 0, 2)),
<funclink>PV_Cos</funclink>(<funclink>PV_Random</funclink>(min_angle, max_angle, 0, 1), <funclink>PV_Random</funclink>(min_speed, max_speed, 0, 2)),
PV_Random(10, 200),
Particles_Glimmer(), 400);</code>
<text>Uses the particle-local seed to draw the same angle and radius for the X and Y speed of the particle. This leads to a radial distribution (instead of a square). The seed parameter is set to the same number (here 1 and 2) where the same result should be drawn.</text>
</example>
</examples>
<related>
<funclink>CreateParticle</funclink>
<funclink>PV_Linear</funclink>
<funclink>PV_Direction</funclink>
@ -40,6 +65,7 @@
<funclink>PV_Speed</funclink>
<funclink>PV_KeyFrames</funclink>
<funclink>PV_Sin</funclink>
<funclink>PV_Cos</funclink>
<funclink>PC_Die</funclink>
<funclink>PC_Bounce</funclink>
</related>

View File

@ -30,6 +30,7 @@
</syntax>
<desc>The value will is calculates as sin(value) * amplitude + offset with value given in degrees.</desc>
<remark>See the <emlink href="particle/index.html">particle documentation</emlink> for further explanations of the particle system.</remark>
<remark>See <funclink>PV_Random</funclink> for another example with PV_Sin.</remark>
<examples>
<example>
<code><funclink>CreateParticle</funclink>("MagicRing", 0, 0, 0, 0, 100, {R=0xff,G=0x00,B=0x30, Size = PV_Sin(<funclink>PV_Linear</funclink>(0,180),10,0)}, 1);</code>
@ -38,6 +39,7 @@
</examples>
<related>
<funclink>CreateParticle</funclink>
<funclink>PV_Cos</funclink>
<funclink>PV_Direction</funclink>
<funclink>PV_Random</funclink>
<funclink>PV_Step</funclink>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE funcs
SYSTEM '../../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<funcs>
<func>
<title>PlayRumble</title>
<category>Player</category>
<version>8.0 OC</version>
<syntax>
<rtype>bool</rtype>
<params>
<param>
<type>int</type>
<name>player</name>
<desc>Number of the player whose controller should rumble. Can be NO_OWNER to make all controllers rumble.</desc>
</param>
<param>
<type>int</type>
<name>strength</name>
<desc>Strength of the rumble, between 0 and 1000.</desc>
</param>
<param>
<type>int</type>
<name>length</name>
<desc>Duration of the rumble in milliseconds.</desc>
</param>
</params>
</syntax>
<desc>Plays a haptic effect on the given player's gamepad. Returns true if all parameters are valid; there is no way to know whether the rumble was actually played.</desc>
<examples>
<example>
<code>
<funclink>ShakeObjects</funclink>(<funclink>LandscapeWidth</funclink>()/2, <funclink>LandscapeHeight</funclink>()/2, <funclink>Distance</funclink>(<funclink>LandscapeWidth</funclink>(), <funclink>LandscapeHeight</funclink>())/2);
PlayRumble(NO_OWNER, 1000, 2000);
</code>
<text>Earthquake: Shakes all Clonks and rumbles all controllers at full strength for two seconds.</text>
</example>
</examples>
<related>
<funclink>StopRumble</funclink>
</related>
</func>
<author>Luchs</author><date>2016-02</date>
</funcs>

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE funcs
SYSTEM '../../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<funcs>
<func>
<title>PlayerControl</title>
<category>Callbacks</category>
<version>5.1 OC</version>
<syntax>
<rtype>bool</rtype>
<params>
<param>
<type>int</type>
<name>player</name>
<desc>Number of the player who pressed the control.</desc>
</param>
<param>
<type>int</type>
<name>control</name>
<desc>Number of the pressed control, defined as a CON_ constant via PlayerControls.txt.</desc>
</param>
<param>
<type>id</type>
<name>control_extra</name>
<desc>Optional id defined with ExtraData in PlayerControls.txt.</desc>
</param>
<param>
<type>int</type>
<name>x</name>
<desc>X coordinate for mouse controls.</desc>
</param>
<param>
<type>int</type>
<name>y</name>
<desc>Y coordinate for mouse controls.</desc>
</param>
<param>
<type>int</type>
<name>strength</name>
<desc>Current strength of the control. For key presses: 0 or 100. For analog stick or trigger movement (state = CONS_Moved): 0 to <code>PLRCON_MaxStrength</code>.</desc>
</param>
<param>
<type>bool</type>
<name>repeated</name>
<desc>Whether the call is generated because of a held button.</desc>
</param>
<param>
<type>int</type>
<name>state</name>
<desc>
State of the key press. Possible values:
<table>
<rowh>
<col>Constant</col>
<col>Description</col>
</rowh>
<row>
<literal_col>CONS_Down</literal_col>
<col>Key has been pressed down.</col>
</row>
<row>
<literal_col>CONS_Up</literal_col>
<col>Key has been released. Only generated for held keys.</col>
</row>
<row>
<literal_col>CONS_Moved</literal_col>
<col>An analog control on a gamepad has been moved. Only generated for held keys.</col>
</row>
</table>
</desc>
</param>
</params>
</syntax>
<desc>Called globally for each control command by players. See <emlink href="script/playercontrols.html">Player Controls</emlink>.</desc>
<related><funclink>GetPlayerControlState</funclink></related>
<related><emlink href="playercontrols.html">Player Controls</emlink></related>
</func>
<author>Luchs</author><date>2016-02</date>
</funcs>

View File

@ -9,12 +9,12 @@
<subcat>Vertices</subcat>
<version>5.1 OC</version>
<syntax>
<rtype>int</rtype>
<rtype>bool</rtype>
<params>
<param>
<type>int</type>
<name>index</name>
<desc>Index of the vertex to be removed.</desc>
<desc>Index of the vertex to be removed, ranges from 0 to <funclink>GetVertexNum</funclink>.</desc>
</param>
</params>
</syntax>
@ -28,6 +28,7 @@
</examples>
<related>
<funclink>AddVertex</funclink>
<funclink>InsertVertex</funclink>
<funclink>GetVertex</funclink>
<funclink>SetVertex</funclink>
<funclink>GetVertexNum</funclink>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE funcs
SYSTEM '../../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<funcs>
<func>
<title>SetHalfVehicleSolidMask</title>
<category>Objects</category>
<subcat>Status</subcat>
<version>5.1 OC</version>
<syntax>
<rtype>void</rtype>
<params>
<param>
<type>bool</type>
<name>set</name>
<desc>Whether to activate or deactivate</desc>
</param>
</params>
</syntax>
<desc>Change the material type of an activated solid mask. Activating HalfVehicle solid masks will allow objects to phase through the solid mask from below but keep normal behavior when standing on it or falling through. </desc>
<remark>Solid areas will internally be drawn in the landscape using the "Vehicle" material.</remark>
<examples>
<example>
<code>SetHalfVehicleSolidMask(true); // do not forget to pass the parameter!</code>
<text></text>
</example>
</examples>
</func>
<author>Caesar</author><date>2016-01</date>
</funcs>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE funcs
SYSTEM '../../../clonk.dtd'>
<?xml-stylesheet type="text/xsl" href="../../../clonk.xsl"?>
<funcs>
<func>
<title>StopRumble</title>
<category>Player</category>
<version>8.0 OC</version>
<syntax>
<rtype>bool</rtype>
<params>
<param>
<type>int</type>
<name>player</name>
<desc>Number of the player whose controller should stop rumbling. Can be NO_OWNER to make all controllers stop.</desc>
</param>
</params>
</syntax>
<desc>Stops a rumble effect that was started with <funclink>PlayRumble</funclink>. Returns true if the given player is valid; there is no way to know whether there was actually a playing rumble effect.</desc>
<related>
<funclink>PlayRumble</funclink>
</related>
</func>
<author>Luchs</author><date>2016-02</date>
</funcs>

View File

@ -1,7 +1,7 @@
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2012-2015, The OpenClonk Team and contributors
* Copyright (c) 2012-2016, The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.

View File

@ -25,5 +25,5 @@ InMatConvertDepth=1
[Reaction]
Type=Convert
TargetSpec=Incindiary
TargetSpec=Incendiary
ConvertMat=Water

View File

@ -28,7 +28,7 @@ func FxSparkleStart(target, effect, temp)
func FxSparkleTimer(target, effect, effect_time)
{
if(this()->Contained() || !Random(2)) return FX_OK;
if(this->Contained() || !Random(2)) return FX_OK;
CreateParticle("MagicRing", 0, 0, 0, 0, effect.Interval, effect.particles, 1);
return FX_OK;
}

View File

@ -7,10 +7,17 @@
static g_player_spawn_positions;
static g_map_width;
static g_no_map, g_seed;
// Called be the engine: draw the complete map here.
public func InitializeMap(proplist map)
{
// Don't draw a map when switching to the empty scenario section.
if (g_no_map) return true;
// Reloading the scenario section also resets the RNG. Call Random() a few times to get a new map each round.
var i = g_seed++;
while (i--) Random(2);
// Map type 0: One big island; more small islands above
// Map type 1: Only many small islands
var t = SCENPAR_MapType;

View File

@ -33,3 +33,31 @@ Default=0
Name=$WeaponsExplosive$
Description=$DescWeaponsExplosive$
Value=1
[ParameterDef]
Name=$Rounds$
Description=$DescRounds$
ID=Rounds
Default=1
[Options]
[Option]
Name=1
Value=1
[Option]
Name=2
Value=2
[Option]
Name=3
Value=3
[Option]
Name=5
Value=5
[Option]
Name=7
Value=7

View File

@ -1,7 +1,61 @@
/* Hot ice */
static g_remaining_rounds, g_winners, g_check_victory_effect;
static g_gameover;
func Initialize()
{
g_remaining_rounds = SCENPAR_Rounds;
g_winners = [];
InitializeRound();
Scoreboard->Init([
// Invisible team column for sorting players under their teams.
{key = "team", title = "", sorted = true, desc = false, default = "", priority = 90},
{key = "wins", title = "Wins", sorted = true, desc = true, default = 0, priority = 100},
{key = "death", title = "", sorted = false, default = "", priority = 0},
]);
}
// Resets the scenario, redrawing the map.
func ResetRound()
{
// Retrieve all Clonks.
var clonks = FindObjects(Find_OCF(OCF_CrewMember));
for (var clonk in clonks)
{
var container = clonk->Contained();
if (container)
{
clonk->Exit();
container->RemoveObject();
}
clonk->SetObjectStatus(C4OS_INACTIVE);
}
// Clear and redraw the map.
g_no_map = true;
LoadScenarioSection("Empty");
g_no_map = false;
LoadScenarioSection("main");
InitializeRound();
// Re-enable the players.
for (var clonk in clonks)
{
clonk->SetObjectStatus(C4OS_NORMAL);
SetCursor(clonk->GetOwner(), clonk);
// Select the first item. This fixes item ordering.
clonk->SetHandItemPos(0, 0);
InitPlayerRound(clonk->GetOwner());
}
}
func InitializeRound()
{
// Checking for victory: Only active after a Clonk dies.
g_check_victory_effect = AddEffect("CheckVictory", nil, 1, 0);
g_player_spawn_index = 0;
ShuffleArray(g_player_spawn_positions);
// Materials: Chests
var i,pos;
var ls_wdt = LandscapeWidth(), ls_hgt = LandscapeHeight();
@ -41,15 +95,37 @@ func Initialize()
if (pos=FindLocation(Loc_InRect(0,ls_hgt/2,ls_wdt,ls_hgt/3), Loc_Solid()))
if (IsFirestoneSpot(pos.x,pos.y))
CreateObjectAbove([Firestone,IronBomb][Random(Random(3))],pos.x,pos.y-1);
// The game starts after a delay to ensure that everyone is ready.
GUI_Clock->CreateCountdown(3);
return true;
}
static g_player_spawn_positions, g_map_width, g_player_spawn_index;
global func ScoreboardTeam(int team) { return team * 100; }
func InitializePlayer(int plr)
{
// Add the player and their team to the scoreboard.
Scoreboard->NewPlayerEntry(plr);
Scoreboard->SetPlayerData(plr, "wins", "");
var team = GetPlayerTeam(plr);
Scoreboard->NewEntry(ScoreboardTeam(team), GetTeamName(team));
Scoreboard->SetData(ScoreboardTeam(team), "team", "", ScoreboardTeam(team));
Scoreboard->SetPlayerData(plr, "team", "", ScoreboardTeam(team) + 1);
return InitPlayerRound(plr);
}
func InitPlayerRound(int plr)
{
// Unmark death on scoreboard.
Scoreboard->SetPlayerData(plr, "death", "");
// everything visible
SetFoW(false, plr);
SetPlayerViewLock(plr, true);
// Player positioning.
var ls_wdt = LandscapeWidth(), ls_hgt = LandscapeHeight();
var crew = GetCrew(plr), start_pos;
@ -86,13 +162,183 @@ func InitializePlayer(int plr)
{
var ammo = launcher->CreateContents(IronBomb);
launcher->AddTimer(Scenario.ReplenishLauncherAmmo, 10);
// Start reloading the launcher during the countdown.
crew->SetHandItemPos(0, crew->GetItemPos(launcher));
// This doesn't play the animation properly - simulate a click instead.
/* crew->StartLoad(launcher); */
crew->StartUseControl(CON_Use, 0, 0, launcher);
crew->StopUseControl(0, 0, launcher);
}
}
crew.MaxEnergy = 100000;
crew->DoEnergy(1000);
// Disable the Clonk during the countdown.
crew->SetCrewEnabled(false);
crew->SetComDir(COMD_Stop);
return true;
}
// Called by the round start countdown.
func OnCountdownFinished()
{
// Re-enable all Clonks.
for (var clonk in FindObjects(Find_OCF(OCF_CrewMember)))
{
clonk->SetCrewEnabled(true);
SetCursor(clonk->GetOwner(), clonk);
}
}
func OnClonkDeath(object clonk)
{
var plr = clonk->GetOwner();
// Mark death on scoreboard.
Scoreboard->SetPlayerData(plr, "death", "{{Scoreboard_Death}}");
// Skip eliminated players, NO_OWNER, etc.
if (GetPlayerName(plr))
{
var crew = CreateObject(Clonk, 0, 0, plr);
crew->MakeCrewMember(plr);
var relaunch = CreateObject(RelaunchContainer, LandscapeWidth() / 2, LandscapeHeight() / 2, plr);
// We just use the relaunch object as a dumb container.
crew->Enter(relaunch);
// Allow scrolling around the landscape.
SetPlayerViewLock(plr, false);
}
// Check for victory after three seconds to allow stalemates.
if (!g_gameover)
g_check_victory_effect.Interval = 36 * 5;
}
// Returns a list of colored player names, for example "Sven2, Maikel, Luchs"
global func GetTeamPlayerNames(int team)
{
var str = "";
for (var i = 0; i < GetPlayerCount(); i++)
{
var plr = GetPlayerByIndex(i);
if (GetPlayerTeam(plr) == team)
{
var comma = "";
if (str != "") comma = ", ";
str = Format("%s%s<c %x>%s</c>", str, comma, GetPlayerColor(plr), GetPlayerName(plr));
}
}
return str;
}
global func FxCheckVictoryTimer(_, proplist effect)
{
var find_living = Find_And(Find_OCF(OCF_CrewMember), Find_NoContainer());
var clonk = FindObject(find_living);
var msg;
if (!clonk)
{
// Stalemate!
msg = "$Stalemate$";
Log(msg);
GameCall("ResetRound");
}
else if (!FindObject(find_living, Find_Hostile(clonk->GetOwner())))
{
// We have a winner!
var team = GetPlayerTeam(clonk->GetOwner());
PushBack(g_winners, team);
// Announce the winning team.
msg = Format("$WinningTeam$", GetTeamPlayerNames(team));
Log(msg);
// Update the scoreboard.
UpdateScoreboardWins(team);
if (--g_remaining_rounds > 0 || GetLeadingTeam() == nil)
{
var msg2 = CurrentRoundStr();
Log(msg2);
msg = Format("%s|%s", msg, msg2);
GameCall("ResetRound");
}
else
{
GameCall("EliminateLosers");
}
}
// Switching scenario sections makes the Log() messages hard to see, so announce them using a message as well.
CustomMessage(msg);
// Go to sleep again.
effect.Interval = 0;
return FX_OK;
}
global func CurrentRoundStr()
{
if (g_remaining_rounds == 1)
return "$LastRound$";
else if (g_remaining_rounds > 1)
return Format("$RemainingRounds$", g_remaining_rounds);
else
return "$Tiebreak$";
}
global func UpdateScoreboardWins(int team)
{
var wins = GetTeamWins(team);
Scoreboard->SetData(ScoreboardTeam(team), "wins", wins, wins);
// We have to update each player as well to make the sorting work.
for (var i = 0; i < GetPlayerCount(); i++)
{
var plr = GetPlayerByIndex(i);
if (GetPlayerTeam(plr) == team)
{
Scoreboard->SetPlayerData(plr, "wins", "", wins);
}
}
}
global func GetTeamWins(int team)
{
var wins = 0;
for (var w in g_winners)
if (w == team)
wins++;
return wins;
}
// Returns the team which won the most rounds, or nil if there is a tie.
global func GetLeadingTeam()
{
var teams = [], winning_team = g_winners[0];
for (var w in g_winners)
{
teams[w] += 1;
if (teams[w] > teams[winning_team])
winning_team = w;
}
// Detect a tie.
for (var i = 0; i < GetLength(teams); i++)
{
if (i != winning_team && teams[i] == teams[winning_team])
return nil;
}
return winning_team;
}
func EliminateLosers()
{
g_gameover = true;
// Determine the winning team.
var winning_team = GetLeadingTeam();
// Eliminate everybody who isn't on the winning team.
for (var i = 0; i < GetPlayerCount(); i++)
{
var plr = GetPlayerByIndex(i);
if (GetPlayerTeam(plr) != winning_team)
EliminatePlayer(plr);
}
// The scenario goal will end the scenario.
}
/* Called periodically in grenade launcher */
func ReplenishLauncherAmmo()
{

View File

@ -10,3 +10,10 @@ WeaponsClassic=Klassisch
DescWeaponsClassic=Bögen, Speere, Keulen und einige Feuersteine
WeaponsExplosive=Explosiv
DescWeaponsExplosive=Nur Granatwerfer mit Endlosmunition
Rounds=Rundenzahl
DescRounds=Mehrere Runden spielen
Stalemate=Unentschieden!
WinningTeam=Gewinner: %s
RemainingRounds=Noch %d Runden.
LastRound=Letzte Runde!
Tiebreak=Entscheidende Runde!

View File

@ -10,3 +10,10 @@ WeaponsClassic=Classic
DescWeaponsClassic=Bows, spears and clubs available in chests
WeaponsExplosive=Explosive
DescWeaponsExplosive=Only grenade lauchers and wind bags available
Rounds=Number of rounds
DescRounds=Play for multiple rounds
Stalemate=Stalemate!
WinningTeam=Winning team: %s
RemainingRounds=%d rounds remaining.
LastRound=Last round!
Tiebreak=Tiebreak!

View File

@ -0,0 +1,17 @@
#appendto Goal_Melee
public func GetDescription(int plr)
{
// Count active enemy clonks.
var hostile_count = ObjectCount(Find_OCF(OCF_CrewMember), Find_NoContainer(), Find_Hostile(plr));
var message;
if (!hostile_count)
message = "$MsgGoalFulfilled$";
else
message = Format("$MsgGoalUnfulfilled$", hostile_count);
// Also report the remaining rounds.
message = Format("%s|%s", message, CurrentRoundStr());
return message;
}

View File

@ -0,0 +1,2 @@
MsgGoalFulfilled=Eure Gegner sind eliminiert.
MsgGoalUnfulfilled=Es sind noch %d Gegner im Spiel.

View File

@ -0,0 +1,2 @@
MsgGoalFulfilled=All opponents eliminated.
MsgGoalUnfulfilled=There are still %d opponents in the game.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -3,38 +3,38 @@
func InitializeObjects()
{
CreateObject(Rule_KillLogs, 50, 50);
CreateObject(Rule_Gravestones, 50, 50);
CreateObject(Rule_Restart, 50, 50);
CreateObject(Goal_Melee, 50, 50);
ItemSpawn->Create(Firestone,407,389);
ItemSpawn->Create(Firestone,423,389);
ItemSpawn->Create(PowderKeg,669,261);
ItemSpawn->Create(Bread,407,258);
ItemSpawn->Create(Bread,352,258);
ItemSpawn->Create(IronBomb,441,389);
ItemSpawn->Create(IceWallKit,990,400);
ItemSpawn->Create(Firestone,443,101);
ItemSpawn->Create(IceWallKit,372,258);
var Chest001 = CreateObjectAbove(Chest, 1047, 359);
CreateObjectAbove(Idol, 313, 254);
CreateObjectAbove(Idol, 315, 235);
var WoodenBridge001 = CreateObjectAbove(WoodenBridge, 1126, 372);
var WoodenBridge001 = CreateObjectAbove(WoodenBridge, 513, 282);
WoodenBridge001->SetCategory(C4D_StaticBack);
var WoodenBridge002 = CreateObjectAbove(WoodenBridge, 513, 282);
var WoodenBridge002 = CreateObjectAbove(WoodenBridge, 1126, 372);
WoodenBridge002->SetCategory(C4D_StaticBack);
CreateObjectAbove(Goal_Flag, 507, 180);
CreateObjectAbove(Goal_Flag, 497, 125);
CreateObjectAbove(Catapult, 728, 431);
CreateObjectAbove(Catapult, 627, 269);
CreateObjectAbove(Catapult, 558, 107);
var Cannon001 = CreateObjectAbove(Cannon, 692, 253);
Cannon001->SetRDir(3);
CreateObjectAbove(Cannon, 511, 179);
CreateObjectAbove(Airship, 369, 269);
Chest001->CreateContents(Boompack);
CreateObjectAbove(Airship, 383, 181);
Chest001->CreateContents(IronBomb, 2);

View File

@ -42,10 +42,12 @@ func Initialize()
flag.Destruction = Scenario.OnFlagDestruction;
flag.team = 1;
flag.Plane = 274; // cannot be moved by airship
flag.RejectWindbagForce = Clonk.IsClonk;
if (flag->GetX() > LandscapeWidth()/2) ++flag.team;
g_respawn_flags[flag.team] = flag;
}
// Weapon drops timer
g_num_avail_weapons = 6;
ScheduleCall(nil, Scenario.DoBalloonDrop, 36*8, 99999);
return true;
}
@ -56,14 +58,17 @@ local weapon_list = [ // id, vertex, offx, offy, deployy
[IceWallKit, 2, 0, -5, -10],
[DynamiteBox, 0, 3, -5, -10],
[BombArrow, 2, 0, -8, -10],
[Boompack, 0, 0, 0, 0],
[GrenadeLauncher, 3, 0, -4, -10]
[GrenadeLauncher, 3, 0, -4, -10],
[Boompack, 0, 0, 0, 0]
];
static g_num_avail_weapons; // boompack available later
func DoBalloonDrop()
{
// Random weapon
var wp = Scenario.weapon_list[Random(GetLength(Scenario.weapon_list))];
if (FrameCounter() > 36*60*4) g_num_avail_weapons = 7; // enable boompacks after some time
var wp = Scenario.weapon_list[Random(g_num_avail_weapons)];
var x = LandscapeWidth()/4 + Random(LandscapeWidth()/2);
var y = 0;
var balloon = CreateObject(BalloonDeployed, x, y);
@ -153,6 +158,8 @@ func RelaunchPlayer(int plr)
crew->MakeCrewMember(plr);
SetCursor(plr, crew, false);
}
// Reset available items in spawns
for (var item_spawn in FindObjects(Find_ID(ItemSpawn))) item_spawn->Reset(plr);
// Relaunch near current flag pos (will be adjusted on actual relaunch)
crew->SetPosition(start_x, start_y);
var relaunch = CreateObjectAbove(RelaunchContainer, start_x, start_y, plr);

View File

@ -2,7 +2,6 @@
id=Boomattack
Version=7,0
Category=C4D_Vehicle
ContactCalls=1
Width=15
Height=27
Offset=-7,-13

View File

@ -100,3 +100,4 @@ local PerspectiveTheta = 25;
local PerspectivePhi = 30;
local FlySpeed = 100;
local Name = "$Name$";
local ContactCalls = true;

View File

@ -2,7 +2,6 @@
id=BigBoomattack
Version=6,0
Category=C4D_Object
ContactCalls=1
Width=60
Height=100
Offset=-30,-50

View File

@ -109,4 +109,5 @@ local ActMap = {
Hgt = 100
},
};
local ContactCalls = true;
*/

View File

@ -2,7 +2,6 @@
id=Boomattack
Version=7,0
Category=C4D_Vehicle
ContactCalls=1
Width=15
Height=27
Offset=-7,-13

View File

@ -130,4 +130,5 @@ local ActMap = {
};
local FlySpeed = 100;
local Name = "$Name$";
local Name = "$Name$";
local ContactCalls = true;

View File

@ -1,25 +0,0 @@
/**
Tools Workshop
@author Clonkonaut
*/
#appendto ToolsWorkshop
local is_station;
protected func Initialize()
{
is_station = true;
return _inherited();
}
// Appears in the bottom interaction bar, if cable is connected
public func IsInteractable()
{
if (GetLength(this->~GetDestinations())) return true;
return _inherited(...);
}
public func GetCableXOffset() { return 15; }
public func GetCableYOffset() { return 5; }

View File

@ -1,9 +1,6 @@
[DefCore]
id=CableLine
Version=6,0
Version=7,0
Category=C4D_StaticBack
Width=12
Height=8
Offset=-6,-4
Vertices=2
#Line=1
Line=1

View File

@ -0,0 +1,106 @@
/*-- Cable line --*/
func Initialize()
{
SetAction("Connect");
SetVertexXY(0, GetX(), GetY());
SetVertexXY(1, GetX(), GetY());
SetProperty("LineColors", [RGB(20, 20, 50), RGB(20, 20, 50)]);
}
public func IsCableLine() { return GetAction() == "Connect"; }
/** Returns whether this cable is connected to an object. */
public func IsConnectedTo(object obj)
{
return GetActionTarget(0) == obj || GetActionTarget(1) == obj;
}
/** Connects this cable to obj1 and obj2. */
public func SetConnectedObjects(obj1, obj2)
{
if (GetActionTarget(0)) GetActionTarget(0)->~CableDeactivation(activations);
if (GetActionTarget(1)) GetActionTarget(1)->~CableDeactivation(activations);
SetVertexXY(0, obj1->GetX(), obj1->GetY());
SetVertexXY(1, obj2->GetX(), obj2->GetY());
SetActionTargets(obj1, obj2);
obj1->AddCableConnection(this);
}
/** Returns the object which is connected to obj through this cable. */
public func GetConnectedObject(object obj)
{
if (GetActionTarget(0) == obj)
return GetActionTarget(1);
if (GetActionTarget(1) == obj)
return GetActionTarget(0);
}
/* Breaking */
func LineBreak(bool no_msg)
{
Sound("Objects::Connect");
if (GetActionTarget(0)) GetActionTarget(0)->~CableDeactivation(activations);
if (GetActionTarget(1)) GetActionTarget(1)->~CableDeactivation(activations);
if (!no_msg)
BreakMessage();
}
func BreakMessage()
{
var line_end = GetActionTarget(0);
if (line_end->GetID() != CableLorryReel)
line_end = GetActionTarget(1);
if (line_end->Contained()) line_end = line_end->Contained();
line_end->Message("$TxtLinebroke$");
}
/* Activation */
local activations = 0;
/** Called by cable cars whenever one proceeds onto this cable. Will be forwarded to connected objects.
count increases the activation value. Stations will stop animation only if all activations are deactivated. */
public func Activation(int count)
{
// Count must be > 0
if (count < 1) return FatalError("Cable Line: Activation() was called with count < 1.");
activations += count;
if (GetActionTarget(0)) GetActionTarget(0)->~CableActivation(count);
if (GetActionTarget(1)) GetActionTarget(1)->~CableActivation(count);
}
/** Called by cable cars whenever one proceeds off this cable. Will be forwarded to connected objects.
count decreases the activation value. Stations will stop animation only if all activations are deactivated. */
public func Deactivation(int count)
{
// Count must be > 0
if (count < 1) return FatalError("Cable Line: Deactivation() was called with count < 1.");
activations -= count;
if (GetActionTarget(0)) GetActionTarget(0)->~CableDeactivation(count);
if (GetActionTarget(1)) GetActionTarget(1)->~CableDeactivation(count);
}
/* Saving */
public func SaveScenarioObject(props)
{
if (!inherited(props, ...)) return false;
SaveScenarioObjectAction(props);
if (IsCableLine()) props->AddCall("Connection", this, "SetConnectedObjects", GetActionTarget(0), GetActionTarget(1));
return true;
}
local ActMap = {
Connect = {
Prototype = Action,
Name = "Connect",
Procedure = DFA_CONNECT,
NextAction = "Connect"
}
};
local Name = "$Name$";

View File

@ -0,0 +1,2 @@
TxtLinebroke=Seil gerissen
Name=Seilbahnseil

View File

@ -9,7 +9,6 @@ public func IsToolProduct() { return true; }
/*-- Line connection --*/
// Use will connect power line to building at the clonk's position.
protected func ControlUse(object clonk, int x, int y)
{
// Is there an object which accepts power lines?
@ -17,20 +16,20 @@ protected func ControlUse(object clonk, int x, int y)
// No such object -> message.
if (!obj)
return clonk->Message("$TxtNoNewLine$");
// Is there a power line connected to this wire roll?
// Is there a cable connected to this wire roll?
var line = FindObject(Find_CableLine());
// There already is a power line.
// There already is a cable
if (line)
{
if (obj == line->GetActionTarget(0) || obj == line->GetActionTarget(1))
{
// Power line is already connected to obj -> remove line.
// Cable is already connected to obj -> remove line.
line->RemoveObject();
Sound("Objects::Connect");
clonk->Message("$TxtLineRemoval$");
return true;
}
else
else
{
// Connect existing power line to obj.
if(line->GetActionTarget(0) == this)
@ -40,15 +39,13 @@ protected func ControlUse(object clonk, int x, int y)
else
return;
Sound("Objects::Connect");
line->SetAction("Wait");
line->UpdateDraw();
obj->AddCableConnection(line);
clonk->Message("$TxtConnect$", obj->GetName());
//RemoveObject();
return true;
}
}
else // A new power line needs to be created.
else // A new cable needs to be created.
{
line = CreateObjectAbove(CableLine, 0, 0, NO_OWNER);
line->SetActionTargets(this, obj);
@ -68,4 +65,4 @@ private func Find_CableLine(object obj)
local Name = "$Name$";
local Description = "$Description$";
local Collectible = 1;
local Collectible = 1;

View File

@ -0,0 +1,4 @@
[DefCore]
id=GUI_DestinationSelectionMenu
Version=8,0
Category=C4D_StaticBack | C4D_Environment

View File

@ -0,0 +1,193 @@
/**
DestinationSelectionMenu
Handles the destination selection for cable cars.
@author Clonkonaut
*/
local Name = "$Name$";
local Description = "$Description$";
// used as a static function
public func CreateFor(object cursor, object car, object station)
{
if (!cursor) return;
if (!car) return;
if (!station) return;
var obj = CreateObject(GUI_DestinationSelectionMenu, AbsX(0), AbsY(0), cursor->GetOwner());
obj.Visibility = VIS_Owner;
obj->Init(cursor, car, station);
cursor->SetMenu(obj);
return obj;
}
// The Clonk whom the menu was opened for
local cursor;
// The menu id of the opened menu
local menu_id;
// The cable car which opened this menu
local cable_car;
// The station the cable car is hooked up to
local cable_station;
// A dummy object used to bring light to stations when previewed
local dummy;
public func Close() { return RemoveObject(); }
public func IsDestinationMenu() { return true; }
public func Show() { this.Visibility = VIS_Owner; return true; }
public func Hide() { this.Visibility = VIS_None; return true; }
// Called when the menu is open and the player clicks outside.
public func OnMouseClick() { return Close(); }
func Destruction()
{
if (menu_id)
GuiClose(menu_id);
if (dummy)
dummy->RemoveObject();
if (cursor)
SetPlrView(cursor->GetOwner(), cursor, true);
}
public func Init(object cursor, object car, object station)
{
this.cursor = cursor;
this.cable_car = car;
this.cable_station = station;
this.dummy = CreateObject(Dummy, AbsX(station->GetX()), AbsY(station->GetY()), GetOwner());
dummy.Visibility = VIS_Owner;
dummy->SetLightRange(5, 20);
var dest_list = station->GetDestinationList(nil);
var dest_menu = {
Target = this,
Decoration = GUI_MenuDeco,
BackgroundColor = RGB(0, 0, 0),
Bottom = "3em",
Left = "50% - 5em",
Right = "50% + 5em",
Priority = 1,
Player = cursor->GetOwner(),
caption = {
Text = "$SelectDestination$",
Bottom = "1em",
Priority = 2,
},
buttons = {
Top = "1em",
Style = GUI_GridLayout,
Priority = 999
}
};
FillDestinationButtons(dest_menu, dest_list);
GuiOpen(dest_menu);
}
func FillDestinationButtons(proplist menu, array list)
{
var priority = 1000;
// Left button
var left = {
Right = "2em",
Bottom = "2em",
Symbol = Icon_LibraryCableCar,
GraphicsName = "LeftGrey",
BackgroundColor = { Std = RGB(0, 0, 0), Hover = RGB(100, 30, 30) },
Priority = ++priority
};
if (list[3] > 3)
{
left.OnMouseIn = GuiAction_SetTag("Hover");
left.OnMouseOut = GuiAction_SetTag("Std");
left.GraphicsName = "Left";
left.OnClick = GuiAction_Call(this, "ShiftSelection", list[0]);
}
// List buttons
var list_button = {
Right = "2em",
Bottom = "2em",
BackgroundColor = { Std = RGB(0, 0, 0), Hover = RGB(100, 30, 30) },
OnMouseOut = GuiAction_SetTag("Std")
};
var buttons = CreateArray(3);
for (var i = 0; i < 3; i++)
{
if (list[i])
{
buttons[i] = new list_button{
Symbol = list[i],
Tooltip = Format("$SendTo$", list[i]->GetName()),
OnMouseIn = [GuiAction_SetTag("Hover"), GuiAction_Call(this, "PreviewDestination", list[i])],
OnClick = GuiAction_Call(this, "SelectDestination", list[i]),
Priority = ++priority
};
} else {
buttons[i] = new list_button {};
}
}
// Right button
var right = {
Right = "2em",
Bottom = "2em",
Symbol = Icon_LibraryCableCar,
GraphicsName = "Grey",
BackgroundColor = { Std = RGB(0, 0, 0), Hover = RGB(100, 30, 30) },
Priority = ++priority
};
if (list[3] > 3)
{
right.OnMouseIn = GuiAction_SetTag("Hover");
right.OnMouseOut = GuiAction_SetTag("Std");
right.GraphicsName = nil;
right.OnClick = GuiAction_Call(this, "ShiftSelection", list[2]);
}
// Assemble
menu.buttons.left = left;
menu.buttons.first = buttons[0];
menu.buttons.second = buttons[1];
menu.buttons.third = buttons[2];
menu.buttons.right = right;
}
func PreviewDestination(object to_preview, int plr, int menu_id, int submenu_id, object target)
{
if (to_preview == nil) return;
if (dummy == nil)
{
dummy = CreateObject(Dummy, AbsX(to_preview->GetX()), AbsY(to_preview->GetY()), GetOwner());
dummy.Visibility = VIS_Owner;
dummy->SetLightRange(5, 20);
}
SetPlrView(plr, to_preview, true);
dummy->SetPosition(to_preview->GetX(), to_preview->GetY());
}
func ShiftSelection(object new_middle, int plr, int menu_id, int submenu_id, object target)
{
if (!cable_station) return;
if (!menu_id) return;
var update = { buttons = { } };
var list = cable_station->GetDestinationList(new_middle);
FillDestinationButtons(update, list);
GuiUpdate(update, menu_id);
Sound("UI::Click", true, nil, plr);
}
func SelectDestination(object target, int plr, int menu_id, int submenu_id, object target)
{
if (target == nil) return;
if (!cable_car) return;
cable_car->SetDestination(target);
Sound("UI::Click", true, nil, plr);
RemoveObject();
}

View File

@ -0,0 +1,4 @@
Name=Destination Selection Menu
Description=
SelectDestination=Select a destination:
SendTo=Send the cable car to %s

View File

@ -0,0 +1,8 @@
[DefCore]
id=Icon_LibraryCableCar
Version=8,0
Category=C4D_StaticBack
Picture=0,0,32,32
Width=32
Height=22
Offset=-16,-16

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -1,209 +1,328 @@
/**
Cable Car
Library object for the cable car.
@author Randrian, Clonkonaut, Maikel
Cable cars must set up a movement speed using SetCableSpeed(x);
Cable cars handle movement on their own. In order to move along a rail, cable cars must call DoMovement() regularly.
E.g. using AddTimer("DoMovement", 1);
*/
//#appendto Lorry
// The speed with which the car travels along rails
local lib_ccar_speed;
// The rail (cable or crossing) that is currently traveled along or stayed at
local lib_ccar_rail;
// The direction (0 or 1) the rail is travelled along, translates into the cable's action targets (of which there should be two!)
local lib_ccar_direction;
// The travel progress on the current rail
local lib_ccar_progress;
// The length of the rail
local lib_ccar_max_progress;
// This target point for pathfinding
local lib_ccar_destination;
local iMovementSpeed;
/*--- Overloads ---*/
local pRailTarget;
local rail_direction; // 2 up the line, 1 down the line, 0 no movement
local rail_progress;
local rail_max_prog;
local rail_destination;
// Overload these functions as you feel fit
// Called after the car is attached to a rail
func Engaged() {}
// Called after the car is detached from the rail
func Disengaged() {}
// To offset the position on the cable from the object's center
// position is a 2-value-array [x,y]
// prec is nil or a value to multiply your calculations with
func GetCableOffset(array position, int prec) {}
// To add custom interaction menu entries after the regular cable car entries
// custom_entry is a prototype for proper spacing of buttons
// Use priorities > 2000 just to be sure
func GetCableCarExtraMenuEntries(array menu_entries, proplist custom_entry, object clonk) {}
// Whenever movement is about to begin
// Movement data like lib_ccar_direction is still nil at this moment
func OnStart() {}
// Whenever the car stops its movement
// failed is true if the movement to a destination was cancelled (usually because the path broke in the meantime)
func OnStop(bool failed) {}
/*--- Interface ---*/
// Sets the speed of the cable car
public func SetCableSpeed(int value)
{
lib_ccar_speed = value;
}
// Positioning of the car along the cable
// This should be called regularly, see header comment!
public func DoMovement()
{
if (!GetRailTarget()) return;
if (lib_ccar_destination == nil) return;
if (lib_ccar_direction == nil) return;
var start = 1;
var end = 0;
if (lib_ccar_direction == 1)
{
start = 0;
end = 1;
}
lib_ccar_progress += lib_ccar_speed;
var position = CreateArray(2);
if (lib_ccar_progress >= lib_ccar_max_progress)
{
lib_ccar_rail->~Deactivation(1);
lib_ccar_rail = lib_ccar_rail->GetActionTarget(end);
lib_ccar_rail->GetCablePosition(position);
GetCableOffset(position);
SetPosition(position[0], position[1]);
lib_ccar_direction = nil;
CrossingReached();
return;
}
var prec = 100;
var origin = CreateArray(2), ending = CreateArray(2);
lib_ccar_rail->GetActionTarget(start)->GetCablePosition(origin, prec);
lib_ccar_rail->GetActionTarget(end)->GetCablePosition(ending, prec);
position[0] = origin[0] + (ending[0] - origin[0]) * lib_ccar_progress/lib_ccar_max_progress;
position[1] = origin[1] + (ending[1] - origin[1]) * lib_ccar_progress/lib_ccar_max_progress;
GetCableOffset(position, prec);
SetPosition(position[0], position[1], 1, prec);
}
/*--- Status ---*/
public func IsCableCar() { return true; }
public func GetRailTarget() { return pRailTarget; }
public func GetRailTarget() { return lib_ccar_rail; }
public func EngageRail(object pRailpoint)
public func IsTravelling() { return lib_ccar_destination; }
/* Interaction */
// Provides an own interaction menu.
public func HasInteractionMenu() { return true; }
// Show settins in interaction menu
public func GetInteractionMenus(object clonk)
{
if (! pRailpoint->IsCableCrossing()) return false;
var menus = _inherited() ?? [];
var cablecar_menu =
{
title = "$CableCarOptions$",
entries_callback = this.GetCableCarMenuEntries,
callback = nil,
callback_hover = "OnCableCarHover",
callback_target = this,
BackgroundColor = RGB(0, 0, 50),
Priority = 20
};
PushBack(menus, cablecar_menu);
return menus;
}
public func GetCableCarMenuEntries(object clonk)
{
var control_prototype =
{
BackgroundColor = { Std = 0, Selected = RGB(100, 30, 30) },
OnMouseIn = GuiAction_SetTag("Selected"),
OnMouseOut = GuiAction_SetTag("Std"),
Right = "2em"
};
var custom_entry =
{
Right = "3em", Bottom = "2em",
image = { Prototype = control_prototype },
icon = { Left = "2em" }
};
var menu_entries = [];
// Clickable buttons
if (!GetRailTarget())
{
// Engaging onto a rail
var stations = FindObjects(Find_AtPoint(), Find_Func("IsCableStation"));
var i = 0;
for (var station in stations)
{
var engage = new custom_entry {
Priority = 1000 + i,
Tooltip = "$TooltipEngage$",
OnClick = GuiAction_Call(this, "EngageRail", station),
image = { Prototype = custom_entry.image, Symbol = station },
icon = { Prototype = custom_entry.icon, Symbol = Icon_LibraryCableCar, GraphicsName = "Engage" }
};
PushBack(menu_entries, { symbol = station, extra_data = "Engage", custom = engage });
i++;
}
// No station present
if (i == 0)
{
var search = {
Priority = 1000,
Right = "100%", Bottom = "2em",
text = { Text = "$NoStation$", Style = GUI_TextVCenter | GUI_TextHCenter}
};
PushBack(menu_entries, { symbol = this, extra_data = "NoStation", custom = search });
}
} else {
// Start the trip
if (!IsTravelling())
{
var go = new custom_entry {
Priority = 1000,
Tooltip = "$TooltipGo$",
OnClick = GuiAction_Call(this, "OpenDestinationSelection", clonk),
image = { Prototype = custom_entry.image, Symbol = Icon_Play }
};
PushBack(menu_entries, { symbol = this, extra_data = "Go", custom = go });
var disengage = new custom_entry {
Priority = 1001,
Tooltip = "$TooltipDisengage$",
OnClick = GuiAction_Call(this, "DisengageRail"),
image = { Prototype = custom_entry.image, Symbol = GetRailTarget() },
icon = { Prototype = custom_entry.icon, Symbol = Icon_LibraryCableCar, GraphicsName = "Disengage" }
};
PushBack(menu_entries, { symbol = GetRailTarget(), extra_data = "Disengage", custom = disengage });
}
}
// Add custom entries
GetCableCarExtraMenuEntries(menu_entries, custom_entry, clonk);
return menu_entries;
}
public func OnCableCarHover(symbol, extra_data, desc_menu_target, menu_id)
{
if (symbol == nil) return;
var text = "";
if (extra_data == "Engage")
text = Format("$DescEngage$", GetName(), symbol->GetName());
if (extra_data == "Go")
text = "$DescGo$";
GuiUpdate({ Text = text }, menu_id, 1, desc_menu_target);
}
/*--- Travelling ---*/
// Attach the car onto a crossing
public func EngageRail(object crossing, bool silent)
{
if (! crossing->~IsCableCrossing()) return false;
var position = CreateArray(2);
pRailpoint->GetCablePosition(position);
crossing->GetCablePosition(position);
GetCableOffset(position);
SetPosition(position[0], position[1]);
SetSpeed(0,0);
SetR(0);
SetAction("OnRail");
SetRDir(0);
SetComDir(COMD_None);
pRailTarget = pRailpoint;
rail_direction = 0;
lib_ccar_rail = crossing;
lib_ccar_direction = nil;
if (!silent) Sound("Objects::Connect");
UpdateInteractionMenus(this.GetCableCarMenuEntries);
Engaged();
}
// Detach the car from its current holding point (cable or crossing, does not matter)
public func DisengageRail()
{
pRailTarget = nil;
rail_direction = 0;
rail_progress = 0;
rail_max_prog = 0;
rail_destination = nil;
lib_ccar_rail = nil;
lib_ccar_direction = nil;
lib_ccar_progress = 0;
lib_ccar_max_progress = 0;
lib_ccar_destination = nil;
UpdateInteractionMenus(this.GetCableCarMenuEntries);
Disengaged();
}
// Sets a target point for travelling and starts the movement process
public func SetDestination(dest)
{
if(GetType(dest) == C4V_Int)
{
dest = FindObjects(Find_Func("IsCableCrossing"))[dest];
}
rail_destination = dest;
if(rail_direction == 0)
CrossingReached();
}
private func Disengaged() {}
/* Destination selection */
public func SelectDestination(object select_clonk, object station)
{
// Storage effect
var effect = AddEffect("DestinationSelection", this, 1, 1, this);
effect.select_clonk = select_clonk;
effect.station = station;
// Helping object
effect.cablecar_sel = CreateObjectAbove(CableCar_Selector, 0,0, select_clonk->GetOwner());
effect.cablecar_sel->FixTo(station);
effect.engaging_station = station;
}
public func ShiftSelection(int direction, object selector)
{
// Determine effect
var effect = nil, temp_effect;
for (var i = 0; temp_effect = GetEffect("DestinationSelection", this, i); i++)
if (temp_effect.cablecar_sel == selector)
effect = temp_effect;
if (!effect) return false;
var destinations = [];
var connection_list = effect.engaging_station->GetDestinations();
// Get every possible destination
for (var targets in connection_list)
{
// Is this a valid destination?
if (! targets[0]->IsCableStation()) continue;
// Check ownership
if (targets[0]->GetOwner() != effect.select_clonk->GetOwner() && targets[0]->GetOwner() != NO_OWNER) continue;
// Save it
destinations[GetLength(destinations)] = targets[0];
}
// Add current station, for it is a destination
destinations[GetLength(destinations)] = effect.engaging_station;
// Determine destination actually seen
for (var i = 0; i < GetLength(destinations); i++)
if (destinations[i] == effect.station)
break;
// Scale exceeded? Reset to current station
if (i == GetLength(destinations)) i--;
else { // Select the new destination
i += direction;
if (i < 0) i = GetLength(destinations) - 1;
if (i >= GetLength(destinations)) i = 0;
}
// Set the view
effect.station = destinations[i];
effect.cablecar_sel->FixTo(effect.station);
return true;
}
protected func FxDestinationSelectionTimer(object target, effect)
{
// Check cancellation conditions
if (! effect.select_clonk) return -1; // Clonk's gone
if (! effect.select_clonk->GetAlive()) return -1; // Clonk's dead
if (effect.select_clonk->GetActionTarget() != this) return -1; // Clonk's not grabbing anymore
if (! effect.cablecar_sel) return -1; // Selector's gone
if (! effect.engaging_station) return -1; // Engaging station's gone
if (effect.engaging_station->OnFire()) return -1; // Engaging station's burning (destroyed)
// Check view
if (! effect.station) ShiftSelection(0, effect.cablecar_sel); // Current view target is gone
if (effect.station->OnFire()) ShiftSelection(0, effect.cablecar_sel); // Current view target is burning (destroyed)
}
protected func FxDestinationSelectionStop(object target, effect, int reason, bool temp)
{
if (temp) return;
// Clonk's still alive? Reset Cursor
if (effect.select_clonk && effect.select_clonk->GetAlive())
{
SetPlrView(effect.select_clonk->GetOwner(), effect.select_clonk);
SetCursor(effect.select_clonk->GetOwner(), effect.select_clonk, true);
}
// Remove selector
effect.cablecar_sel->RemoveObject();
}
public func AcceptSelection(object selector)
{
// Determine effect
var effect = nil, temp_effect;
for (var i = 0; temp_effect = GetEffect("DestinationSelection", this, i); i++)
if (temp_effect.cablecar_sel == selector)
effect = temp_effect;
if (!effect) return false;
// Reset cursor
SetPlrView(effect.select_clonk->GetOwner(), effect.select_clonk);
SetCursor(effect.select_clonk->GetOwner(), effect.select_clonk, true);
effect.cablecar_sel->RemoveObject();
// Ungrab & start!
effect.select_clonk->ObjectControl(effect.select_clonk->GetOwner(), CON_Ungrab);
SetDestination(effect.station);
RemoveEffect(nil, this, effect, true);
return true;
}
public func AbortSelection(object selector)
{
// Determine effect
var effect = nil, temp_effect;
for (var i = 0; temp_effect = GetEffect("DestinationSelection", this, i); i++)
if (temp_effect.cablecar_sel == selector)
effect = temp_effect;
if (!effect) return false;
RemoveEffect(nil, this, effect);
}
/* Movement */
private func CrossingReached()
{
var target;
if(rail_destination != pRailTarget)
{
if(target = pRailTarget->GetNextWaypoint(rail_destination))
MoveTo(target);
else
DeliveryFailed();
}
else if (deliver_to) {
if (deliver_to == pRailTarget)
return DeliverObjects();
if (pRailTarget->RequestObjects(this, deliver_id, deliver_amount))
ReturnDelivery();
else
DeliveryFailed();
}
}
private func MoveTo(dest)
{
if(GetType(dest) == C4V_Int)
{
dest = FindObjects(Find_Func("IsCableCrossing"))[dest];
if (!dest) return;
}
lib_ccar_destination = dest;
if(lib_ccar_direction == nil)
{
OnStart();
CrossingReached();
}
}
// Whenever a crossing is reached it must be queried for the next crossing to go to
func CrossingReached()
{
var target;
if(lib_ccar_destination != lib_ccar_rail)
{
if(target = lib_ccar_rail->GetNextWaypoint(lib_ccar_destination))
MoveTo(target);
else
DestinationFailed();
}
// Destination reached
else {
DestinationReached();
}
}
// When the current destination is reached
func DestinationReached()
{
lib_ccar_destination = nil;
lib_ccar_direction = nil;
lib_ccar_progress = 0;
lib_ccar_max_progress = 0;
OnStop(false);
}
// When the way to the current destination has vanished somehow
func DestinationFailed()
{
lib_ccar_destination = nil;
lib_ccar_direction = nil;
lib_ccar_progress = 0;
lib_ccar_max_progress = 0;
OnStop(true);
}
// Setup movement process
func MoveTo(dest)
{
if(GetType(dest) == C4V_Int)
{
dest = FindObjects(Find_Func("IsCableCrossing"))[dest];
if (!dest) return;
}
var rail = 0;
for(var test_rail in FindObjects(Find_Func("IsConnectedTo", pRailTarget)))
for(var test_rail in FindObjects(Find_Func("IsConnectedTo", lib_ccar_rail)))
{
if(test_rail->IsConnectedTo(dest))
{
@ -212,74 +331,46 @@ private func MoveTo(dest)
}
}
if(!rail)
{
Message("No Rail available!");
return;
}
return DestinationFailed(); // Shouldn't happen
// Target the first or second action target?
if(rail->GetActionTarget(0) == dest)
{
rail_direction = 1;
rail_progress = 0;
}
lib_ccar_direction = 0;
else
{
rail_direction = 2;
rail_progress = 0;
}
rail->GetActionTarget(0)->AddActive(0);
rail->GetActionTarget(1)->AddActive(0);
rail->AddActive(0);
lib_ccar_direction = 1;
lib_ccar_progress = 0;
var origin = CreateArray(2), ending = CreateArray(2);
rail->GetActionTarget(0)->GetCablePosition(origin);
rail->GetActionTarget(1)->GetCablePosition(ending);
rail_max_prog = Distance(origin[0], origin[1], ending[0], ending[1]);
pRailTarget = rail;
rail->~Activation(1);
lib_ccar_max_progress = Distance(origin[0], origin[1], ending[0], ending[1]);
lib_ccar_rail = rail;
}
protected func OnRail()
/* Destination selection */
public func OpenDestinationSelection(object clonk)
{
if(rail_direction == 0 || rail_direction == nil) return;
var start = 0;
var end = 1;
if(rail_direction == 1)
{
start = 1;
end = 0;
}
if (!clonk) return;
if (!GetRailTarget()) return;
rail_progress += iMovementSpeed;
if(rail_progress >= rail_max_prog)
{
pRailTarget->GetActionTarget(0)->AddActive(1);
pRailTarget->GetActionTarget(1)->AddActive(1);
pRailTarget->AddActive(1);
pRailTarget = pRailTarget->GetActionTarget(end);
var position = CreateArray(2);
pRailTarget->GetCablePosition(position);
SetPosition(position[0], position[1]);
rail_direction = 0;
CrossingReached();
return;
}
var plr = clonk->GetOwner();
// Close interaction menu
if (clonk->GetMenu())
if (!clonk->TryCancelMenu())
return;
var prec = 100;
var origin = CreateArray(2), ending = CreateArray(2);
pRailTarget->GetActionTarget(start)->GetCablePosition(origin, prec);
pRailTarget->GetActionTarget(end)->GetCablePosition(ending, prec);
var x = origin[0] + (ending[0] - origin[0]) * rail_progress/rail_max_prog;
var y = origin[1] + (ending[1] - origin[1]) * rail_progress/rail_max_prog;
SetPosition(x, y, 1, prec);
GUI_DestinationSelectionMenu->CreateFor(clonk, this, GetRailTarget());
}
/*-- Delivery --*/
/*
local deliver_id, deliver_amount, deliver_to;
// Returns true when this car is not in move
public func IsAvailable()
{
return !rail_destination;
return !lib_ccar_destination;
}
public func AddDelivery(object from, object to, id object_id, int amount)
@ -299,8 +390,8 @@ public func DeliverObjects()
{
if (ContentsCount(deliver_id) < deliver_amount) return DeliveryFailed();
for (var i = 0; i < deliver_amount; i++)
FindContents(deliver_id)->Enter(pRailTarget);
pRailTarget->DeliveryDone(deliver_id, deliver_amount);
FindContents(deliver_id)->Enter(lib_ccar_rail);
lib_ccar_rail->DeliveryDone(deliver_id, deliver_amount);
deliver_id = false;
deliver_amount = false;
deliver_to = false;
@ -310,3 +401,4 @@ private func DeliveryFailed()
{
}
*/

View File

@ -1,7 +0,0 @@
[DefCore]
id=CableCar_Selector
Version=6,0
Category=C4D_StaticBack
Width=1
Height=1
Offset=-1,-1

View File

@ -1,43 +0,0 @@
/*-- Cable car selector --*/
local cable_car;
protected func Construction(object constructor)
{
cable_car = constructor;
SetLightRange(10);
this["Visibility"] = VIS_Owner;
AddEffect("Particles", this, 1, 2, this);
}
public func FixTo(object station)
{
// no owner? -> panic
if (GetOwner() == NO_OWNER) return RemoveObject();
// Set owner's view
SetPosition(station->GetX(), station->GetY());
SetPlrView(GetOwner(), this);
SetCursor(GetOwner(), this, true);
}
public func ObjectControl(int plr, int ctrl, int x, int y, int strength, bool repeat, bool release)
{
if (release) return false;
if (ctrl == CON_Left)
return cable_car->ShiftSelection(-1, this);
if (ctrl == CON_Right)
return cable_car->ShiftSelection(+1, this);
if (ctrl == CON_Use || ctrl == CON_Interact)
return cable_car->AcceptSelection(this);
if (ctrl == CON_UseAlt)
return cable_car->AbortSelection(this);
}
protected func FxParticlesTimer(object target, effect, int time)
{
var angle = time*10 % 360;
CreateParticle("SphereSpark", Sin(angle, 13), -Cos(angle, 13), 0, 0, PV_Random(20, 30), Particles_Spark());
}

View File

@ -0,0 +1,9 @@
CableCarOptions=Seilbahneinstellungen
TooltipEngage=Das Fahrzeug auf das Seil aufsetzen.
DescEngage=%s bei %s auf das Seil aufsetzen.
TooltipGo=Wähle ein Ziel für das Fahrzeug.
DescGo=Öffnet das Zielwahl-Menü für dieses Fahrzeug. Das Fahrzeug fährt los, sobald ein Ziel ausgewählt wurde.
NoStation=Schiebe das Fahrzeug vor eine Station, um es auf ein Seil zu setzen.

View File

@ -0,0 +1,9 @@
CableCarOptions=Cable Car Options
TooltipEngage=Put the car on the rail
DescEngage=Put %s on the rail at %s.
TooltipGo=Select a destination for the car.
DescGo=Opens the destination selection menu for this cable car. When you select a destination, the car will start going there.
NoStation=Push the cable car in front of a station to set it up.

View File

@ -1,102 +1,159 @@
/**
Cable Station
Library for cable stations and crossings. This is included by
Library for cable stations and crossings. This is included by
cable crossings, and should be included by structures which
want to make use of the cable network.
@author Randrian, Clonkonaut, Maikel
*/
//#appendto ToolsWorkshop
/*--- Overloads ---*/
// Set to true to make this a station
local is_station;
// Overload these functions as you feel fit
/*-- State --*/
// This function is called whenever a change in the cable network occured, i.e. destinations have been added / removed.
private func DestinationsUpdated() { }
/** This object is a cable crossing
* E.g. checked by whatever object wants to connect a cable (so it does not mean there is a cable connected!)
// Called by cable lines whenever a car starts travelling along a connected cable.
// Can be used to start animation or sounds or similar.
// count is a value indicating the amount of activations.
public func CableActivation(int count) { }
// Called likewise as Activation() whenever a car leaves the cable.
// count is a value indicating the amount of deactivations (e.g. a cable with more than one car broke).
public func CableDeactivation(int count) { }
/*--- Callbacks ---*/
// Be sure to always call these via _inherited();
func Initialize()
{
destination_list = [];
return _inherited(...);
}
/* Removes this crossing from the network
It first clears every waypoint from the network and then renews the whole information.
Optimisation welcome!
*/
func Destruction()
{
for (var connection in FindObjects(Find_Func("IsConnectedTo", this)))
{
if (! connection->~IsCableLine()) continue;
var other_crossing = connection->~GetConnectedObject(this);
if (! other_crossing->~IsCableCrossing()) continue;
other_crossing->ClearConnections(this);
}
for (var connection in FindObjects(Find_Func("IsConnectedTo", this)))
{
if (! connection->~IsCableLine()) continue;
var other_crossing = connection->~GetConnectedObject(this);
if (! other_crossing->~IsCableCrossing()) continue;
other_crossing->RenewConnections(this);
}
return _inherited(...);
}
/*--- Status ---*/
local lib_crossing_is_station;
/** This object is a cable crossing.
E.g. checked by whatever object wants to connect a cable.
Does not mean that there actually is a cable connected to this crossing.
*/
public func IsCableCrossing() { return true; }
/** Returns whether or not this object is a cable station
* A station is a possible (i.e. selectable) destination for cable cars (whereas normal crossings do not appear in the destination selection process)
*/
public func IsCableStation() { return is_station; }
// For setting up the cable
/** This function should return true if this crossing is considered a station.
A station is selectable as a target if a lorry is sent its way.
Functional buildings should always be a station, the 'crossing' building only if set to.
*/
public func IsCableStation() { return lib_crossing_is_station; }
/*--- Interface ---*/
// For switching the station status
public func SetCableStation(bool station)
{
lib_crossing_is_station = station;
}
// Returns the cable hookup position for proper positioning of a car along the line.
public func GetCablePosition(array coordinates, int prec)
{
if (!prec) prec = 1;
coordinates[0] = GetX(prec);
coordinates[1] = GetY(prec);
if (this->~GetCableXOffset()) coordinates[0] += this->~GetCableXOffset() * prec;
if (this->~GetCableYOffset()) coordinates[1] += this->~GetCableYOffset() * prec;
}
/* Local */
// Stores the next crossing (waypoint) to take when advancing to a certain final point
// Scheme (2D array): [Desired final point, Next waypoint to take, Distance (not airline!) until final point]
local destination_list;
// According to this scheme, some constants for easy reading
local const_finaldestination; // :D
local const_nextwaypoint;
local const_distance;
protected func Initialize() //
{
const_finaldestination = 0;
const_nextwaypoint = 1;
const_distance = 2;
destination_list = [];
return _inherited(...);
}
/* Pathfinding for cable cars */
/** Returns the waypoint to take next for the desired final point \a end
* @param end The final destination for the information is queried
*/
public func GetNextWaypoint(object end)
{
if (!destination_list) return nil;
for (var item in destination_list)
if (this.LineAttach)
{
if (!item) continue;
if (item[const_finaldestination] == end)
return item[const_nextwaypoint];
coordinates[0] += this.LineAttach[0] * prec;
coordinates[1] += this.LineAttach[1] * prec;
}
return nil;
}
/** Returns the actual traveling distance for the desired final point \a end
* This is not the airline distance but the length of all cables to take via traveling
* @param end The final destination for the information is queried
*/
public func GetLengthToTarget(object end)
// Usually called by cable cars to retrieve selectable destinations for the destination selection menu.
// Returns an array of three objects and one int, one station before and one station after the middle one and the middle one.
// The int (fourth array value) is the overall amount of stations found.
// If middle is not a station (anymore), the first three found objects are returned.
public func GetDestinationList(object middle)
{
if (!destination_list) return nil;
for (var item in destination_list)
{
if (!item) continue;
if (item[const_finaldestination] == end)
return item[const_distance];
}
return nil;
var list = CreateArray();
var ret = CreateArray(4);
for (var destination in destination_list)
if (destination[const_finaldestination]->IsCableStation())
PushBack(list, destination[const_finaldestination]);
if (GetLength(list) == 0) return ret;
if (GetLength(list) == 1) return [nil, list[0], nil, 1];
if (GetLength(list) == 2) return [list[0], list[1], nil, 2];
if (GetLength(list) == 3) return [list[0], list[1], list[2], 3];
var middle_index = GetIndexOf(list, middle);
if (middle_index == -1) middle_index = 1;
var left_index = middle_index - 1;
if (left_index < 0) left_index = GetLength(list) - 1;
var right_index = middle_index + 1;
if (right_index >= GetLength(list)) right_index = 0;
ret[0] = list[left_index];
ret[1] = list[middle_index];
ret[2] = list[right_index];
ret[3] = GetLength(list);
return ret;
}
/** Returns the destination array
/*--- Maintaining the destination list ---*/
/* Functions:
GetDestinations()
AddCableConnection(object cable)
AddCableDestinations(array new_list, object crossing)
AddCableDestination(object new_destination, object crossing, int distance_add)
ClearConnections(object crossing)
RenewConnections(object crossing)
*/
/** Returns the destination array so it can be used by other crossings.
*/
public func GetDestinations()
{
return destination_list[:];
}
/* Set up pathfinding information */
// Stores the next crossing (waypoint) to take when advancing to a certain final point
// Scheme (2D array): [Desired final point, Next waypoint to take, Distance (not airline!) until final point]
local destination_list;
// Constants for easier script reading
// These correspond to the aforementioned values of destination_list
local const_finaldestination = 0; // :D
local const_nextwaypoint = 1;
local const_distance = 2;
/** Adds a new connection via the cable \a cable to this crossing
* Does nothing if the other connected objects of the cable is not a cable crossing
* @param cable The newly connected cable
Does nothing if the other connected object of the cable is not a cable crossing
@param cable The newly connected cable
*/
public func AddCableConnection(object cable)
{
@ -104,7 +161,7 @@ public func AddCableConnection(object cable)
if (!cable || ! cable->~IsCableLine())
return false;
// Line setup finished?
var other_crossing = cable->~GetOtherConnection(this);
var other_crossing = cable->~GetConnectedObject(this);
if (! other_crossing->~IsCableCrossing())
return false;
// Acquire destinations of the other crossing, all these are now in reach
@ -112,7 +169,7 @@ public func AddCableConnection(object cable)
// Send own destinations, now in reach for the other one
other_crossing->AddCableDestinations(destination_list[:], this);
// Awesome, more power to the network!
CheckRailStation();
DestinationsUpdated();
return true;
}
@ -164,7 +221,7 @@ public func AddCableDestinations(array new_list, object crossing)
if (list_item[const_finaldestination] != crossing)
list_item[const_finaldestination]->AddCableDestination(this, crossing, distance_add);
}
CheckRailStation();
DestinationsUpdated();
return true;
}
@ -193,7 +250,7 @@ public func AddCableDestination(object new_destination, object crossing, int dis
if (!crossing_item) return false;
// Save the new destination
destination_list[GetLength(destination_list)] = [new_destination, crossing_item[const_nextwaypoint], crossing_item[const_distance] + distance_add];
CheckRailStation();
DestinationsUpdated();
return true;
}
@ -223,7 +280,7 @@ public func UpdateCableDestination(object known_destination, object crossing, in
// Save the updated path
destination_list[destination_item][const_nextwaypoint] = crossing_item[const_nextwaypoint];
destination_list[destination_item][const_distance] = crossing_item[const_distance] + distance_add;
CheckRailStation();
DestinationsUpdated();
return true;
}
@ -240,7 +297,7 @@ public func ClearConnections(object crossing)
for (var connection in FindObjects(Find_Func("IsConnectedTo", this)))
{
if (! connection->~IsCableLine()) continue;
var other_crossing = connection->~GetOtherConnection(this);
var other_crossing = connection->~GetConnectedObject(this);
if (! other_crossing->~IsCableCrossing()) continue;
other_crossing->ClearConnections();
}
@ -256,7 +313,7 @@ public func RenewConnections(object crossing)
for(var connection in FindObjects(Find_Func("IsConnectedTo", this)))
{
if (! connection->~IsCableLine()) continue;
var other_crossing = connection->~GetOtherConnection(this);
var other_crossing = connection->~GetConnectedObject(this);
if (! other_crossing->~IsCableCrossing()) continue;
if (other_crossing == crossing) continue;
destination_list[GetLength(destination_list)] = [other_crossing, other_crossing, ObjectDistance(other_crossing)];
@ -264,113 +321,46 @@ public func RenewConnections(object crossing)
}
}
/* Station behaviour */
/*--- Pathfinding ---*/
// Prevents the automatic change of the stations status when set to station mode
local bManualSetting;
// IsInteractable is stored in the appendto definitions!
// A clonk wants to change my station status
public func Interact(object pClonk)
{
// Check ownership
if (GetOwner() != NO_OWNER && GetOwner() != pClonk->GetOwner()) return false;
// Clonk pushes a cable car?
if (pClonk->GetActionTarget() && pClonk->GetActionTarget()->~IsCableCar())
{
var car = pClonk->GetActionTarget();
// Disengage
if (car->GetRailTarget() == this)
{
car->DisengageRail();
return true;
}
// Engage
car->EngageRail(this);
car->SelectDestination(pClonk, this);
return true;
}
// Change status
if (is_station)
bManualSetting = false;
else
bManualSetting = true;
CheckRailStation();
return _inherited(...);
}
public func GetInteractionMetaInfo(object clonk)
{
if (is_station)
return {IconID = Library_CableStation, IconName = "UnsetStation", Description = "$UnsetStationDesc$"};
else
return {IconID = Library_CableStation, IconName = "SetStation", Description = "$SetStationDesc$"};
}
/* Animation stuff */
/** Overload me to do wheel animation
@return \c nil.
/* Functions:
GetNextWaypoint(object end)
GetLengthToTarget(object end)
*/
protected func TurnWheel()
{
/* EMPTY: Should be overloaded */
return;
}
local iActiveCount;
// Start/End animation
// Call AddActive(0) if any next waypoint wants to start the animation (because a cable car is passing by)
// Call AddActive(1) if the cable car passed the line, stopping the animation
// Counts up to multiple animated connections
public func AddActive(fRemove)
{
if(!fRemove)
iActiveCount++;
else
iActiveCount--;
if(iActiveCount <= 0 && GetAction() == "Active")
SetAction("Wait");
if(iActiveCount > 0 && GetAction() == "Wait")
SetAction("Active");
}
/** Overload me to check station behaviour
@return \c nil.
/** Returns the waypoint to take next for the desired final point \a end
* @param end The final destination for the information is queried
*/
private func CheckRailStation()
public func GetNextWaypoint(object end)
{
/* EMPTY: Should be overloaded */
return _inherited(...);
if (!destination_list) return nil;
for (var item in destination_list)
{
if (!item) continue;
if (item[const_finaldestination] == end)
return item[const_nextwaypoint];
}
return nil;
}
/* Destruction */
/* Removes this crossing from the network
It first clears every waypoint from the network and then renews the whole information.
Optimisation welcome!
/** Returns the actual traveling distance for the desired final point \a end
* This is not the airline distance but the length of all cables to take via traveling
* @param end The final destination for the information is queried
*/
protected func Destruction()
public func GetLengthToTarget(object end)
{
for (var connection in FindObjects(Find_Func("IsConnectedTo", this)))
if (!destination_list) return nil;
for (var item in destination_list)
{
if (! connection->~IsCableLine()) continue;
var other_crossing = connection->~GetOtherConnection(this);
if (! other_crossing->~IsCableCrossing()) continue;
other_crossing->ClearConnections(this);
}
for (var connection in FindObjects(Find_Func("IsConnectedTo", this)))
{
if (! connection->~IsCableLine()) continue;
var other_crossing = connection->~GetOtherConnection(this);
if (! other_crossing->~IsCableCrossing()) continue;
other_crossing->RenewConnections(this);
if (!item) continue;
if (item[const_finaldestination] == end)
return item[const_distance];
}
return nil;
}
/*-- Auto production --*/
/*
public func CheckAcquire(id object_id, int amount)
{
var container = false;
@ -421,7 +411,7 @@ public func DeliveryDone(id object_id, int amount)
}
/*-- Object requests and deliveries --*/
/*
local reservations; // two dimensional array with ongoing deliveries: [[ID, amount]]
public func CheckAvailability(id object_id, int amount)
@ -469,4 +459,4 @@ public func RequestObjects(object requester, id request_id, int amount)
for (var i = 0; i < amount; i++)
FindContents(request_id)->Enter(requester);
return true;
}
}*/

View File

@ -1,5 +0,0 @@
Name=Cable Crossing
SetStation=Bahnhof einrichten
SetStationDesc=Diese Kreuzung zu einem Bahnhof umschalten
UnsetStation=Bahnhof abschalten
UnsetStationDesc=Diese Kreuzung von einem Bahnhof auf eine Kreuzung zurückschalten

View File

@ -1,5 +0,0 @@
Name=Cable Crossing
SetStation=Set station
SetStationDesc=Change crossing to station
UnsetStation=Reset station
UnsetStationDesc=Change station to crossing

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Some files were not shown because too many files have changed in this diff Show More