# HG changeset patch # User unc0rr # Date 1372333880 -14400 # Node ID 6bc1df062f040446c410579edd72381118579862 # Parent 6e4feb4191a0581f01faca79f6d8e837f79ba36b# Parent bb9ad6a5f625d03910e64aab8723301500451312 Merge diff -r 6e4feb4191a0 -r 6bc1df062f04 CMakeLists.txt --- a/CMakeLists.txt Wed Jun 26 21:40:10 2013 -0400 +++ b/CMakeLists.txt Thu Jun 27 15:51:20 2013 +0400 @@ -30,11 +30,8 @@ option(BUILD_ENGINE_LIBRARY "Enable hwengine library (off)" OFF) option(ANDROID "Enable Android build (off)" OFF) -if(UNIX AND NOT APPLE) - option(MINIMAL_FLAGS "Respect system flags as much as possible (off)" OFF) -else() - option(NOAUTOUPDATE "Disable OS X Sparkle update checking" OFF) -endif() +option(MINIMAL_FLAGS "Respect system flags as much as possible (off)" OFF) +option(NOAUTOUPDATE "Disable OS X Sparkle update checking (off)" OFF) set(FPFLAGS "" CACHE STRING "Additional Freepascal flags") set(GHFLAGS "" CACHE STRING "Additional Haskell flags") @@ -75,43 +72,39 @@ #perform safe check that enable/disable compilation features include(${CMAKE_MODULE_PATH}/compilerchecks.cmake) -#set default flags values for all projects (unless MINIMAL_FLAGS is true) -if(NOT ${MINIMAL_FLAGS}) - set(CMAKE_C_FLAGS "-pipe ${CMAKE_C_FLAGS}") - set(CMAKE_C_FLAGS_RELEASE "-w -Os -fomit-frame-pointer ${CMAKE_C_FLAGS_RELEASE}") - set(CMAKE_C_FLAGS_DEBUG "-Wall -O0 -g ${CMAKE_C_FLAGS_DEBUG}") - set(CMAKE_CXX_FLAGS "-pipe ${CMAKE_CXX_FLAGS}") - set(CMAKE_CXX_FLAGS_RELEASE "-w -Os -fomit-frame-pointer ${CMAKE_CXX_FLAGS_RELEASE}") - set(CMAKE_CXX_FLAGS_DEBUG "-Wall -O0 -g ${CMAKE_CXX_FLAGS_DEBUG}") -else() - #CMake adds a lot of additional configuration flags, so let's clear them up - set(CMAKE_C_FLAGS_RELEASE "") - set(CMAKE_C_FLAGS_DEBUG "-Wall") - set(CMAKE_CXX_FLAGS_RELEASE "") - set(CMAKE_CXX_FLAGS_DEBUG "-Wall") +#set default compiler flags +add_flag_append(CMAKE_C_FLAGS "-Wall -pipe") +add_flag_append(CMAKE_C_FLAGS_RELEASE "-Os") +add_flag_append(CMAKE_C_FLAGS_DEBUG "-Wextra -O0") +add_flag_append(CMAKE_CXX_FLAGS "-Wall -pipe") +add_flag_append(CMAKE_CXX_FLAGS_RELEASE "-Os") +add_flag_append(CMAKE_CXX_FLAGS_DEBUG "-Wextra -O0") +add_flag_append(CMAKE_Pascal_FLAGS "-Cs2000000") +add_flag_append(CMAKE_Pascal_FLAGS_DEBUG "-O- -gv") +add_flag_append(CMAKE_Pascal_FLAGS_RELEASE "-Os -Xs") + +#CMake adds a lot of additional configuration flags, so let's clear them up +if(${MINIMAL_FLAGS}) + unset(CMAKE_C_FLAGS_RELEASE) + unset(CMAKE_C_FLAGS_DEBUG) + unset(CMAKE_CXX_FLAGS_RELEASE) + unset(CMAKE_CXX_FLAGS_DEBUG) endif() + #parse additional parameters if(FPFLAGS) add_flag_prepend(CMAKE_Pascal_FLAGS ${FPFLAGS}) endif() if(GHFLAGS) - if(${allow_parse_args}) + if(${CMAKE_VERSION} VERSION_GREATER 2.6) separate_arguments(ghflags_parsed UNIX_COMMAND ${GHFLAGS}) else() message(${WARNING} "FPFLAGS and GHFLAGS are available only when using CMake >= 2.8") endif() endif() - -list(APPEND haskell_flags ${ghflags_parsed} # user flags - "-O2" # optimise for faster code - ) - -#-vm4079,4080,4081 -add_flag_append(CMAKE_Pascal_FLAGS "-Cs2000000") -add_flag_append(CMAKE_Pascal_FLAGS_DEBUG "-O- -gv") -add_flag_append(CMAKE_Pascal_FLAGS_RELEASE "-Os -Xs") +list(APPEND haskell_flags ${ghflags_parsed} "-O2") #get BUILD_TYPE and enable/disable optimisation message(STATUS "Using ${CMAKE_BUILD_TYPE} configuration") diff -r 6e4feb4191a0 -r 6bc1df062f04 cmake_modules/platform.cmake --- a/cmake_modules/platform.cmake Wed Jun 26 21:40:10 2013 -0400 +++ b/cmake_modules/platform.cmake Thu Jun 27 15:51:20 2013 +0400 @@ -2,10 +2,10 @@ if(APPLE) set(CMAKE_FIND_FRAMEWORK "FIRST") -#what system are we building for + #what system are we building for set(minimum_macosx_version $ENV{MACOSX_DEPLOYMENT_TARGET}) -#detect on which system we are: if sw_vers cannot be found for any reason (re)use minimum_macosx_version + #detect on which system we are: if sw_vers cannot be found for any reason (re)use minimum_macosx_version find_program(sw_vers sw_vers) if(sw_vers) execute_process(COMMAND ${sw_vers} "-productVersion" @@ -21,41 +21,62 @@ endif() endif() -#if nothing is set, we deploy only for the current system + #if nothing is set, we deploy only for the current system if(NOT minimum_macosx_version) set(minimum_macosx_version ${current_macosx_version}) endif() -#lower systems don't have enough processing power anyway + #lower systems don't have enough processing power anyway if (minimum_macosx_version VERSION_LESS "10.4") message(FATAL_ERROR "Hedgewars is not supported on Mac OS X pre-10.4") endif() -#workaround for http://playcontrol.net/ewing/jibberjabber/big_behind-the-scenes_chang.html#SDL_mixer (Update 2) + #workaround for http://playcontrol.net/ewing/jibberjabber/big_behind-the-scenes_chang.html#SDL_mixer (Update 2) if(current_macosx_version VERSION_EQUAL "10.4") find_package(SDL_mixer REQUIRED) set(DYLIB_SMPEG "-dylib_file @loader_path/Frameworks/smpeg.framework/Versions/A/smpeg:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/smpeg.framework/Versions/A/smpeg") set(DYLIB_MIKMOD "-dylib_file @loader_path/Frameworks/mikmod.framework/Versions/A/mikmod:${SDLMIXER_LIBRARY}/Versions/A/Frameworks/mikmod.framework/Versions/A/mikmod") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${DYLIB_SMPEG} ${DYLIB_MIKMOD}") - set(CMAKE_CX_FLAGS "${CMAKE_CX_FLAGS} ${DYLIB_SMPEG} ${DYLIB_MIKMOD}") - list(APPEND pascal_flags "-k${DYLIB_SMPEG}" "-k${DYLIB_MIKMOD}") + add_flag_append(CMAKE_C_FLAGS "${DYLIB_SMPEG} ${DYLIB_MIKMOD}") + add_flag_append(CMAKE_CXX_FLAGS "${DYLIB_SMPEG} ${DYLIB_MIKMOD}") + add_flag_append(CMAKE_Pascal_FLAGS "-k${DYLIB_SMPEG} -k${DYLIB_MIKMOD}") endif() -#CMAKE_OSX_ARCHITECTURES and CMAKE_OSX_SYSROOT need to be set for universal binary and correct linking if(NOT CMAKE_OSX_ARCHITECTURES) if(current_macosx_version VERSION_LESS "10.6") + #SDL is only 32 bit on lower OS if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "powerpc*") set(CMAKE_OSX_ARCHITECTURES "ppc7400") else() set(CMAKE_OSX_ARCHITECTURES "i386") endif() - else() - set(CMAKE_OSX_ARCHITECTURES "x86_64") endif() endif() -#CMAKE_OSX_SYSROOT is set at the system version we are supposed to build on -#we need to provide the correct one when host and target differ + #parse this system variable and adjust only the powerpc syntax to be compatible with -P + if(CMAKE_OSX_ARCHITECTURES) + string(REGEX MATCH "[pP][pP][cC]+" powerpc_build "${CMAKE_OSX_ARCHITECTURES}") + string(REGEX MATCH "[iI]386+" i386_build "${CMAKE_OSX_ARCHITECTURES}") + string(REGEX MATCH "[xX]86_64+" x86_64_build "${CMAKE_OSX_ARCHITECTURES}") + if(x86_64_build) + add_flag_prepend(CMAKE_Pascal_FLAGS -Px86_64) + elseif(i386_build) + add_flag_prepend(CMAKE_Pascal_FLAGS -Pi386) + elseif(powerpc_build) + add_flag_prepend(CMAKE_Pascal_FLAGS -Ppowerpc) + else() + message(FATAL_ERROR "Unknown architecture present in CMAKE_OSX_ARCHITECTURES (${CMAKE_OSX_ARCHITECTURES})") + endif() + list(LENGTH CMAKE_OSX_ARCHITECTURES num_of_archs) + if(num_of_archs GREATER 1) + message(${WARNING} "Only one architecture in CMAKE_OSX_ARCHITECTURES is currently supported, picking the first one") + endif() + elseif(CMAKE_SIZEOF_VOID_P MATCHES "8") + #if that variable is not set check if we are on x86_64 and if so force it, else use default + add_flag_prepend(CMAKE_Pascal_FLAGS -Px86_64) + endif() + + #CMAKE_OSX_SYSROOT is set at the system version we are supposed to build on + #we need to provide the correct one when host and target differ if(NOT ${minimum_macosx_version} VERSION_EQUAL ${current_macosx_version}) if(minimum_macosx_version VERSION_EQUAL "10.4") set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX10.4u.sdk/") @@ -65,19 +86,18 @@ string(REGEX REPLACE "([0-9]+.[0-9]+).[0-9]+" "\\1" sdk_version ${minimum_macosx_version}) set(CMAKE_OSX_SYSROOT "/Developer/SDKs/MacOSX${sdk_version}.sdk/") endif() + add_flag_append(CMAKE_Pascal_FLAGS "-XR${CMAKE_OSX_SYSROOT}") + add_flag_append(CMAKE_Pascal_FLAGS "-k-macosx_version_min -k${minimum_macosx_version}") endif() -#add user framework directory, other paths can be passed via FPFLAGS - list(APPEND pascal_flags "-Ff~/Library/Frameworks") -#set deployment target - list(APPEND pascal_flags "-k-macosx_version_min" "-k${minimum_macosx_version}" "-XR${CMAKE_OSX_SYSROOT}") - + #add user framework directory, other paths can be passed via FPFLAGS + add_flag_append(CMAKE_Pascal_FLAGS "-Ff~/Library/Frameworks") endif(APPLE) if(MINGW) #this flags prevents a few dll hell problems - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc ") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc ") + add_flag_append(CMAKE_C_FLAGS "-static-libgcc") + add_flag_append(CMAKE_CXX_FLAGS "-static-libgcc") endif(MINGW) if(WIN32) diff -r 6e4feb4191a0 -r 6bc1df062f04 cmake_modules/utils.cmake --- a/cmake_modules/utils.cmake Wed Jun 26 21:40:10 2013 -0400 +++ b/cmake_modules/utils.cmake Thu Jun 27 15:51:20 2013 +0400 @@ -12,7 +12,7 @@ find_package(${_PKG_NAME}) string(TOUPPER ${_PKG_NAME} _PKG_NAME_UP) if(NOT ${_PKG_NAME_UP}_FOUND) - message(SEND_ERROR "Missing ${_PKG_NAME}! Rerun cmake with -D${_VAR_NAME}=1 to build without it.") + message(SEND_ERROR "Missing ${_PKG_NAME}! Rerun cmake with -D${_VAR_NAME}=1 to skip this error.") endif(NOT ${_PKG_NAME_UP}_FOUND) endmacro(find_package_or_disable _PKG_NAME _VAR_NAME) diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/ArgParsers.inc --- a/hedgewars/ArgParsers.inc Wed Jun 26 21:40:10 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,356 +0,0 @@ -(* - * Hedgewars, a free turn based strategy game - * Copyright (c) 2004-2013 Andrey Korotaev - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - *) - -procedure GciEasterEgg; -begin - WriteLn(stdout, ' '); - WriteLn(stdout, ' /\\\\\\\\\\\\ /\\\\\\\\\ /\\\\\\\\\\\ '); - WriteLn(stdout, ' /\\\////////// /\\\//////// \/////\\\/// '); - WriteLn(stdout, ' /\\\ /\\\/ \/\\\ '); - WriteLn(stdout, ' \/\\\ /\\\\\\\ /\\\ \/\\\ '); - WriteLn(stdout, ' \/\\\ \/////\\\ \/\\\ \/\\\ '); - WriteLn(stdout, ' \/\\\ \/\\\ \//\\\ \/\\\ '); - WriteLn(stdout, ' \/\\\ \/\\\ \///\\\ \/\\\ '); - WriteLn(stdout, ' \/\\\\\\\\\\\\\/ \////\\\\\\\\\ /\\\\\\\\\\\ '); - WriteLn(stdout, ' \///////////// \///////// \/////////// '); - WriteLn(stdout, ' '); - WriteLn(stdout, ' Command Line Parser Implementation by a Google Code-In Student '); - WriteLn(stdout, ' ASCII Art easter egg idea by @sheepluva '); - WriteLn(stdout, ' '); -end; - -procedure DisplayUsage; -begin - WriteLn(stdout, 'Usage: hwengine [options]'); - WriteLn(stdout, ''); - WriteLn(stdout, 'where [options] can be any of the following:'); - WriteLn(stdout, ' --prefix [path to folder]'); - WriteLn(stdout, ' --user-prefix [path to folder]'); - WriteLn(stdout, ' --locale [name of language file]'); - WriteLn(stdout, ' --nick [string]'); - WriteLn(stdout, ' --fullscreen-width [fullscreen width in pixels]'); - WriteLn(stdout, ' --fullscreen-height [fullscreen height in pixels]'); - WriteLn(stdout, ' --width [window width in pixels]'); - WriteLn(stdout, ' --height [window height in pixels]'); - WriteLn(stdout, ' --volume [sound level]'); - WriteLn(stdout, ' --frame-interval [milliseconds]'); - Writeln(stdout, ' --stereo [value]'); - WriteLn(stdout, ' --raw-quality [flags]'); - WriteLn(stdout, ' --low-quality'); - WriteLn(stdout, ' --nomusic'); - WriteLn(stdout, ' --nosound'); - WriteLn(stdout, ' --fullscreen'); - WriteLn(stdout, ' --showfps'); - WriteLn(stdout, ' --altdmg'); - WriteLn(stdout, ' --no-teamtag'); - WriteLn(stdout, ' --no-hogtag'); - WriteLn(stdout, ' --no-healthtag'); - WriteLn(stdout, ' --translucent-tags'); - WriteLn(stdout, ' --stats-only'); - WriteLn(stdout, ' --help'); - WriteLn(stdout, ''); - WriteLn(stdout, 'For more detailed help and examples go to:'); - WriteLn(stdout, 'http://code.google.com/p/hedgewars/wiki/CommandLineOptions'); - GameType:= gmtSyntax; -end; - -procedure setDepth(var paramIndex: LongInt); -begin - WriteLn(stdout, 'WARNING: --depth is a deprecated command, which could be removed in a future version!'); - WriteLn(stdout, ' This option no longer does anything, please consider removing it'); - WriteLn(stdout, ''); - inc(ParamIndex); -end; - -procedure statsOnlyGame; -begin - cOnlyStats:= true; - cReducedQuality:= $FFFFFFFF xor rqLowRes; - SetSound(false); - SetMusic(false); - SetVolume(0); -end; - -procedure setIpcPort(port: LongInt; var wrongParameter:Boolean); -begin - if isInternal then - ipcPort := port - else - begin - WriteLn(stderr, 'ERROR: use of --port is not allowed'); - wrongParameter := true; - end -end; - -function parseNick(nick: String): String; -begin - if isInternal then - parseNick:= DecodeBase64(nick) - else - parseNick:= nick; -end; - -procedure setStereoMode(tmp: LongInt); -begin - GrayScale:= false; -{$IFDEF USE_S3D_RENDERING} - if (tmp > 6) and (tmp < 13) then - begin - // set the gray anaglyph rendering - GrayScale:= true; - cStereoMode:= TStereoMode(max(0, min(ord(high(TStereoMode)), tmp-6))) - end - else if tmp <= 6 then - // set the fullcolor anaglyph - cStereoMode:= TStereoMode(max(0, min(ord(high(TStereoMode)), tmp))) - else - // any other mode - cStereoMode:= TStereoMode(max(0, min(ord(high(TStereoMode)), tmp-6))); -{$ELSE} - tmp:= tmp; - cStereoMode:= smNone; -{$ENDIF} -end; - -procedure startVideoRecording(var paramIndex: LongInt); -begin - // Silence the hint that appears when USE_VIDEO_RECORDING is not defined - paramIndex:= paramIndex; -{$IFDEF USE_VIDEO_RECORDING} - GameType:= gmtRecord; - inc(paramIndex); - cVideoFramerateNum:= StrToInt(ParamStr(paramIndex)); inc(paramIndex); - cVideoFramerateDen:= StrToInt(ParamStr(paramIndex)); inc(paramIndex); - RecPrefix:= ParamStr(paramIndex); inc(paramIndex); - cAVFormat:= ParamStr(paramIndex); inc(paramIndex); - cVideoCodec:= ParamStr(paramIndex); inc(paramIndex); - cVideoQuality:= StrToInt(ParamStr(paramIndex)); inc(paramIndex); - cAudioCodec:= ParamStr(paramIndex); inc(paramIndex); -{$ENDIF} -end; - -function getLongIntParameter(str:String; var paramIndex:LongInt; var wrongParameter:Boolean): LongInt; -var tmpInt, c: LongInt; -begin - inc(paramIndex); - val(str, tmpInt, c); - wrongParameter:= c <> 0; - if wrongParameter then - WriteLn(stderr, 'ERROR: '+ParamStr(paramIndex-1)+' expects a number, you passed "'+str+'"'); - getLongIntParameter:= tmpInt; -end; - -function getStringParameter(str:String; var paramIndex:LongInt; var wrongParameter:Boolean): String; -begin - inc(paramIndex); - wrongParameter:= (str='') or (Copy(str,1,2) = '--'); - if wrongParameter then - WriteLn(stderr, 'ERROR: '+ParamStr(paramIndex-1)+' expects a string, you passed "'+str+'"'); - getStringParameter:= str; -end; - - -procedure parseClassicParameter(cmdArray: Array of String; size:LongInt; var paramIndex:LongInt); Forward; - -function parseParameter(cmd:String; arg:String; var paramIndex:LongInt): Boolean; -const videoArray: Array [1..5] of String = ('--fullscreen-width','--fullscreen-height', '--width', '--height', '--depth'); - audioArray: Array [1..3] of String = ('--volume','--nomusic','--nosound'); - otherArray: Array [1..3] of String = ('--locale','--fullscreen','--showfps'); - mediaArray: Array [1..10] of String = ('--fullscreen-width', '--fullscreen-height', '--width', '--height', '--depth', '--volume','--nomusic','--nosound','--locale','--fullscreen'); - allArray: Array [1..18] of String = ('--fullscreen-width','--fullscreen-height', '--width', '--height', '--depth','--volume','--nomusic','--nosound','--locale','--fullscreen','--showfps','--altdmg','--frame-interval','--low-quality','--no-teamtag','--no-hogtag','--no-healthtag','--translucent-tags'); - reallyAll: array[0..34] of shortstring = ( - '--prefix', '--user-prefix', '--locale', '--fullscreen-width', '--fullscreen-height', '--width', - '--height', '--frame-interval', '--volume','--nomusic', '--nosound', - '--fullscreen', '--showfps', '--altdmg', '--low-quality', '--raw-quality', '--stereo', '--nick', - {deprecated} '--depth', '--set-video', '--set-audio', '--set-other', '--set-multimedia', '--set-everything', - {internal} '--internal', '--port', '--recorder', '--landpreview', - {misc} '--stats-only', '--gci', '--help','--no-teamtag','--no-hogtag','--no-healthtag','--translucent-tags'); -var cmdIndex: byte; -begin - parseParameter:= false; - cmdIndex:= 0; - - //NOTE: Any update to the list of parameters must be reflected in the case statement below, the reallyAll array above, - // the the DisplayUsage() procedure, the HWForm::getDemoArguments() function, and the online wiki - - while (cmdIndex <= High(reallyAll)) and (cmd <> reallyAll[cmdIndex]) do inc(cmdIndex); - case cmdIndex of - {--prefix} 0 : PathPrefix := getStringParameter (arg, paramIndex, parseParameter); - {--user-prefix} 1 : UserPathPrefix := getStringParameter (arg, paramIndex, parseParameter); - {--locale} 2 : cLocaleFName := getStringParameter (arg, paramIndex, parseParameter); - {--fullscreen-width} 3 : cFullscreenWidth := max(getLongIntParameter(arg, paramIndex, parseParameter), cMinScreenWidth); - {--fullscreen-height} 4 : cFullscreenHeight := max(getLongIntParameter(arg, paramIndex, parseParameter), cMinScreenHeight); - {--width} 5 : cWindowedWidth := max(2 * (getLongIntParameter(arg, paramIndex, parseParameter) div 2), cMinScreenWidth); - {--height} 6 : cWindowedHeight := max(2 * (getLongIntParameter(arg, paramIndex, parseParameter) div 2), cMinScreenHeight); - {--frame-interval} 7 : cTimerInterval := getLongIntParameter(arg, paramIndex, parseParameter); - {--volume} 8 : SetVolume ( max(getLongIntParameter(arg, paramIndex, parseParameter), 0) ); - {--nomusic} 9 : SetMusic ( false ); - {--nosound} 10 : SetSound ( false ); - {--fullscreen} 11 : cFullScreen := true; - {--showfps} 12 : cShowFPS := true; - {--altdmg} 13 : cAltDamage := true; - {--low-quality} 14 : cReducedQuality := $FFFFFFFF xor rqLowRes; - {--raw-quality} 15 : cReducedQuality := getLongIntParameter(arg, paramIndex, parseParameter); - {--stereo} 16 : setStereoMode ( getLongIntParameter(arg, paramIndex, parseParameter) ); - {--nick} 17 : UserNick := parseNick( getStringParameter(arg, paramIndex, parseParameter) ); - {deprecated options} - {--depth} 18 : setDepth(paramIndex); - {--set-video} 19 : parseClassicParameter(videoArray,5,paramIndex); - {--set-audio} 20 : parseClassicParameter(audioArray,3,paramIndex); - {--set-other} 21 : parseClassicParameter(otherArray,3,paramIndex); - {--set-multimedia} 22 : parseClassicParameter(mediaArray,10,paramIndex); - {--set-everything} 23 : parseClassicParameter(allArray,14,paramIndex); - {"internal" options} - {--internal} 24 : {$IFDEF HWLIBRARY}isInternal:= true{$ENDIF}; - {--port} 25 : setIpcPort( getLongIntParameter(arg, paramIndex, parseParameter), parseParameter ); - {--recorder} 26 : startVideoRecording(paramIndex); - {--landpreview} 27 : GameType := gmtLandPreview; - {anything else} - {--stats-only} 28 : statsOnlyGame(); - {--gci} 29 : GciEasterEgg(); - {--help} 30 : DisplayUsage(); - {--no-teamtag} 31 : cTagsMask := cTagsMask and not htTeamName; - {--no-hogtag} 32 : cTagsMask := cTagsMask and not htName; - {--no-healthtag} 33 : cTagsMask := cTagsMask and not htHealth; - {--translucent-tags} 34 : cTagsMask := cTagsMask or htTransparent - else - begin - //Asusme the first "non parameter" is the replay file, anything else is invalid - if (recordFileName = '') and (Copy(cmd,1,2) <> '--') then - recordFileName := cmd - else - begin - WriteLn(stderr, '"'+cmd+'" is not a valid option'); - parseParameter:= true; - end; - end; - end; -end; - -procedure parseClassicParameter(cmdArray: Array of String; size:LongInt; var paramIndex:LongInt); -var index, tmpInt: LongInt; - isBool, isValid: Boolean; - cmd, arg, newSyntax: String; -begin - WriteLn(stdout, 'WARNING: you are using a deprecated command, which could be removed in a future version!'); - WriteLn(stdout, ' Consider updating to the latest syntax, which is much more flexible!'); - WriteLn(stdout, ' Run `hwegine --help` to learn it!'); - WriteLn(stdout, ''); - - index:= 0; - tmpInt:= 1; - while (index < size) do - begin - newSyntax:= ''; - inc(paramIndex); - cmd:= cmdArray[index]; - arg:= ParamStr(paramIndex); - isValid:= (cmd<>'--depth'); - - // check if the parameter is a boolean one - isBool:= (cmd = '--nomusic') or (cmd = '--nosound') or (cmd = '--fullscreen') or (cmd = '--showfps') or (cmd = '--altdmg') or (cmd = '--no-teamtag') or (cmd = '--no-hogtag') or (cmd = '--no-healthtag') or (cmd = '--translucent-tags'); - if isBool and (arg='0') then - isValid:= false; - if (cmd='--nomusic') or (cmd='--nosound') then - isValid:= not isValid; - - if isValid then - begin - parseParameter(cmd, arg, tmpInt); - newSyntax := newSyntax + cmd + ' '; - if not isBool then - newSyntax := newSyntax + arg + ' '; - end; - inc(index); - end; - - WriteLn(stdout, 'Attempted to automatically convert to the new syntax:'); - WriteLn(stdout, newSyntax); - WriteLn(stdout, ''); -end; - -procedure parseCommandLine{$IFDEF HWLIBRARY}(argc: LongInt; argv: PPChar){$ENDIF}; -var paramIndex: LongInt; - paramTotal: LongInt; - index, nextIndex: LongInt; - wrongParameter: boolean; -//var tmpInt: LongInt; -begin - paramIndex:= {$IFDEF HWLIBRARY}0{$ELSE}1{$ENDIF}; - paramTotal:= {$IFDEF HWLIBRARY}argc-1{$ELSE}ParamCount{$ENDIF}; //-1 because pascal enumeration is inclusive - (* - WriteLn(stdout, 'total parameters: ' + inttostr(paramTotal)); - tmpInt:= 0; - while (tmpInt <= paramTotal) do - begin - WriteLn(stdout, inttostr(tmpInt) + ': ' + {$IFDEF HWLIBRARY}argv[tmpInt]{$ELSE}paramCount(tmpInt){$ENDIF}); - inc(tmpInt); - end; - *) - wrongParameter:= false; - while (paramIndex <= paramTotal) do - begin - // avoid going past the number of paramTotal (esp. w/ library) - index:= paramIndex; - if index = paramTotal then nextIndex:= index - else nextIndex:= index+1; - {$IFDEF HWLIBRARY} - wrongParameter:= parseParameter( argv[index], argv[nextIndex], paramIndex); - {$ELSE} - wrongParameter:= parseParameter( ParamStr(index), ParamStr(nextIndex), paramIndex); - {$ENDIF} - inc(paramIndex); - end; - if wrongParameter = true then - GameType:= gmtSyntax; -end; - -{$IFNDEF HWLIBRARY} -procedure GetParams; -begin - isInternal:= (ParamStr(1) = '--internal'); - - UserPathPrefix := '.'; - PathPrefix := cDefaultPathPrefix; - recordFileName := ''; - parseCommandLine(); - - if (isInternal) and (ParamCount<=1) then - begin - WriteLn(stderr, '--internal should not be manually used'); - GameType := gmtSyntax; - end; - - if (not isInternal) and (recordFileName = '') then - begin - WriteLn(stderr, 'You must specify a replay file'); - GameType := gmtSyntax; - end - else if (recordFileName <> '') then - WriteLn(stdout, 'Attempting to play demo file "' + recordFilename + '"'); - - if (GameType = gmtSyntax) then - WriteLn(stderr, 'Please use --help to see possible arguments and their usage'); - - (* - WriteLn(stdout,'PathPrefix: ' + PathPrefix); - WriteLn(stdout,'UserPathPrefix: ' + UserPathPrefix); - *) -end; -{$ENDIF} - diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/ArgParsers.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/ArgParsers.pas Thu Jun 27 15:51:20 2013 +0400 @@ -0,0 +1,369 @@ +(* + * Hedgewars, a free turn based strategy game + * Copyright (c) 2004-2013 Andrey Korotaev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + *) + +{$INCLUDE "options.inc"} + +unit ArgParsers; +interface + +procedure GetParams; + +implementation +uses uConsole, uVariables, uTypes, uUtils, uSound, uConsts; +var isInternal: Boolean; + +procedure GciEasterEgg; +begin + WriteLn(stdout, ' '); + WriteLn(stdout, ' /\\\\\\\\\\\\ /\\\\\\\\\ /\\\\\\\\\\\ '); + WriteLn(stdout, ' /\\\////////// /\\\//////// \/////\\\/// '); + WriteLn(stdout, ' /\\\ /\\\/ \/\\\ '); + WriteLn(stdout, ' \/\\\ /\\\\\\\ /\\\ \/\\\ '); + WriteLn(stdout, ' \/\\\ \/////\\\ \/\\\ \/\\\ '); + WriteLn(stdout, ' \/\\\ \/\\\ \//\\\ \/\\\ '); + WriteLn(stdout, ' \/\\\ \/\\\ \///\\\ \/\\\ '); + WriteLn(stdout, ' \/\\\\\\\\\\\\\/ \////\\\\\\\\\ /\\\\\\\\\\\ '); + WriteLn(stdout, ' \///////////// \///////// \/////////// '); + WriteLn(stdout, ' '); + WriteLn(stdout, ' Command Line Parser Implementation by a Google Code-In Student '); + WriteLn(stdout, ' ASCII Art easter egg idea by @sheepluva '); + WriteLn(stdout, ' '); +end; + +procedure DisplayUsage; +begin + WriteLn(stdout, 'Usage: hwengine [options]'); + WriteLn(stdout, ''); + WriteLn(stdout, 'where [options] can be any of the following:'); + WriteLn(stdout, ' --prefix [path to folder]'); + WriteLn(stdout, ' --user-prefix [path to folder]'); + WriteLn(stdout, ' --locale [name of language file]'); + WriteLn(stdout, ' --nick [string]'); + WriteLn(stdout, ' --fullscreen-width [fullscreen width in pixels]'); + WriteLn(stdout, ' --fullscreen-height [fullscreen height in pixels]'); + WriteLn(stdout, ' --width [window width in pixels]'); + WriteLn(stdout, ' --height [window height in pixels]'); + WriteLn(stdout, ' --volume [sound level]'); + WriteLn(stdout, ' --frame-interval [milliseconds]'); + Writeln(stdout, ' --stereo [value]'); + WriteLn(stdout, ' --raw-quality [flags]'); + WriteLn(stdout, ' --low-quality'); + WriteLn(stdout, ' --nomusic'); + WriteLn(stdout, ' --nosound'); + WriteLn(stdout, ' --fullscreen'); + WriteLn(stdout, ' --showfps'); + WriteLn(stdout, ' --altdmg'); + WriteLn(stdout, ' --no-teamtag'); + WriteLn(stdout, ' --no-hogtag'); + WriteLn(stdout, ' --no-healthtag'); + WriteLn(stdout, ' --translucent-tags'); + WriteLn(stdout, ' --stats-only'); + WriteLn(stdout, ' --help'); + WriteLn(stdout, ''); + WriteLn(stdout, 'For more detailed help and examples go to:'); + WriteLn(stdout, 'http://code.google.com/p/hedgewars/wiki/CommandLineOptions'); + GameType:= gmtSyntax; +end; + +procedure setDepth(var paramIndex: LongInt); +begin + WriteLn(stdout, 'WARNING: --depth is a deprecated command, which could be removed in a future version!'); + WriteLn(stdout, ' This option no longer does anything, please consider removing it'); + WriteLn(stdout, ''); + inc(ParamIndex); +end; + +procedure statsOnlyGame; +begin + cOnlyStats:= true; + cReducedQuality:= $FFFFFFFF xor rqLowRes; + SetSound(false); + SetMusic(false); + SetVolume(0); +end; + +procedure setIpcPort(port: LongInt; var wrongParameter:Boolean); +begin + if isInternal then + ipcPort := port + else + begin + WriteLn(stderr, 'ERROR: use of --port is not allowed'); + wrongParameter := true; + end +end; + +function parseNick(nick: String): String; +begin + if isInternal then + parseNick:= DecodeBase64(nick) + else + parseNick:= nick; +end; + +procedure setStereoMode(tmp: LongInt); +begin + GrayScale:= false; +{$IFDEF USE_S3D_RENDERING} + if (tmp > 6) and (tmp < 13) then + begin + // set the gray anaglyph rendering + GrayScale:= true; + cStereoMode:= TStereoMode(max(0, min(ord(high(TStereoMode)), tmp-6))) + end + else if tmp <= 6 then + // set the fullcolor anaglyph + cStereoMode:= TStereoMode(max(0, min(ord(high(TStereoMode)), tmp))) + else + // any other mode + cStereoMode:= TStereoMode(max(0, min(ord(high(TStereoMode)), tmp-6))); +{$ELSE} + tmp:= tmp; + cStereoMode:= smNone; +{$ENDIF} +end; + +procedure startVideoRecording(var paramIndex: LongInt); +begin + // Silence the hint that appears when USE_VIDEO_RECORDING is not defined + paramIndex:= paramIndex; +{$IFDEF USE_VIDEO_RECORDING} + GameType:= gmtRecord; + inc(paramIndex); + cVideoFramerateNum:= StrToInt(ParamStr(paramIndex)); inc(paramIndex); + cVideoFramerateDen:= StrToInt(ParamStr(paramIndex)); inc(paramIndex); + RecPrefix:= ParamStr(paramIndex); inc(paramIndex); + cAVFormat:= ParamStr(paramIndex); inc(paramIndex); + cVideoCodec:= ParamStr(paramIndex); inc(paramIndex); + cVideoQuality:= StrToInt(ParamStr(paramIndex)); inc(paramIndex); + cAudioCodec:= ParamStr(paramIndex); inc(paramIndex); +{$ENDIF} +end; + +function getLongIntParameter(str:String; var paramIndex:LongInt; var wrongParameter:Boolean): LongInt; +var tmpInt, c: LongInt; +begin + inc(paramIndex); + val(str, tmpInt, c); + wrongParameter:= c <> 0; + if wrongParameter then + WriteLn(stderr, 'ERROR: '+ParamStr(paramIndex-1)+' expects a number, you passed "'+str+'"'); + getLongIntParameter:= tmpInt; +end; + +function getStringParameter(str:String; var paramIndex:LongInt; var wrongParameter:Boolean): String; +begin + inc(paramIndex); + wrongParameter:= (str='') or (Copy(str,1,2) = '--'); + if wrongParameter then + WriteLn(stderr, 'ERROR: '+ParamStr(paramIndex-1)+' expects a string, you passed "'+str+'"'); + getStringParameter:= str; +end; + + +procedure parseClassicParameter(cmdArray: Array of String; size:LongInt; var paramIndex:LongInt); Forward; + +function parseParameter(cmd:String; arg:String; var paramIndex:LongInt): Boolean; +const videoArray: Array [1..5] of String = ('--fullscreen-width','--fullscreen-height', '--width', '--height', '--depth'); + audioArray: Array [1..3] of String = ('--volume','--nomusic','--nosound'); + otherArray: Array [1..3] of String = ('--locale','--fullscreen','--showfps'); + mediaArray: Array [1..10] of String = ('--fullscreen-width', '--fullscreen-height', '--width', '--height', '--depth', '--volume','--nomusic','--nosound','--locale','--fullscreen'); + allArray: Array [1..18] of String = ('--fullscreen-width','--fullscreen-height', '--width', '--height', '--depth','--volume','--nomusic','--nosound','--locale','--fullscreen','--showfps','--altdmg','--frame-interval','--low-quality','--no-teamtag','--no-hogtag','--no-healthtag','--translucent-tags'); + reallyAll: array[0..34] of shortstring = ( + '--prefix', '--user-prefix', '--locale', '--fullscreen-width', '--fullscreen-height', '--width', + '--height', '--frame-interval', '--volume','--nomusic', '--nosound', + '--fullscreen', '--showfps', '--altdmg', '--low-quality', '--raw-quality', '--stereo', '--nick', + {deprecated} '--depth', '--set-video', '--set-audio', '--set-other', '--set-multimedia', '--set-everything', + {internal} '--internal', '--port', '--recorder', '--landpreview', + {misc} '--stats-only', '--gci', '--help','--no-teamtag','--no-hogtag','--no-healthtag','--translucent-tags'); +var cmdIndex: byte; +begin + parseParameter:= false; + cmdIndex:= 0; + + //NOTE: Any update to the list of parameters must be reflected in the case statement below, the reallyAll array above, + // the the DisplayUsage() procedure, the HWForm::getDemoArguments() function, and the online wiki + + while (cmdIndex <= High(reallyAll)) and (cmd <> reallyAll[cmdIndex]) do inc(cmdIndex); + case cmdIndex of + {--prefix} 0 : PathPrefix := getStringParameter (arg, paramIndex, parseParameter); + {--user-prefix} 1 : UserPathPrefix := getStringParameter (arg, paramIndex, parseParameter); + {--locale} 2 : cLocaleFName := getStringParameter (arg, paramIndex, parseParameter); + {--fullscreen-width} 3 : cFullscreenWidth := max(getLongIntParameter(arg, paramIndex, parseParameter), cMinScreenWidth); + {--fullscreen-height} 4 : cFullscreenHeight := max(getLongIntParameter(arg, paramIndex, parseParameter), cMinScreenHeight); + {--width} 5 : cWindowedWidth := max(2 * (getLongIntParameter(arg, paramIndex, parseParameter) div 2), cMinScreenWidth); + {--height} 6 : cWindowedHeight := max(2 * (getLongIntParameter(arg, paramIndex, parseParameter) div 2), cMinScreenHeight); + {--frame-interval} 7 : cTimerInterval := getLongIntParameter(arg, paramIndex, parseParameter); + {--volume} 8 : SetVolume ( max(getLongIntParameter(arg, paramIndex, parseParameter), 0) ); + {--nomusic} 9 : SetMusic ( false ); + {--nosound} 10 : SetSound ( false ); + {--fullscreen} 11 : cFullScreen := true; + {--showfps} 12 : cShowFPS := true; + {--altdmg} 13 : cAltDamage := true; + {--low-quality} 14 : cReducedQuality := $FFFFFFFF xor rqLowRes; + {--raw-quality} 15 : cReducedQuality := getLongIntParameter(arg, paramIndex, parseParameter); + {--stereo} 16 : setStereoMode ( getLongIntParameter(arg, paramIndex, parseParameter) ); + {--nick} 17 : UserNick := parseNick( getStringParameter(arg, paramIndex, parseParameter) ); + {deprecated options} + {--depth} 18 : setDepth(paramIndex); + {--set-video} 19 : parseClassicParameter(videoArray,5,paramIndex); + {--set-audio} 20 : parseClassicParameter(audioArray,3,paramIndex); + {--set-other} 21 : parseClassicParameter(otherArray,3,paramIndex); + {--set-multimedia} 22 : parseClassicParameter(mediaArray,10,paramIndex); + {--set-everything} 23 : parseClassicParameter(allArray,14,paramIndex); + {"internal" options} + {--internal} 24 : {$IFDEF HWLIBRARY}isInternal:= true{$ENDIF}; + {--port} 25 : setIpcPort( getLongIntParameter(arg, paramIndex, parseParameter), parseParameter ); + {--recorder} 26 : startVideoRecording(paramIndex); + {--landpreview} 27 : GameType := gmtLandPreview; + {anything else} + {--stats-only} 28 : statsOnlyGame(); + {--gci} 29 : GciEasterEgg(); + {--help} 30 : DisplayUsage(); + {--no-teamtag} 31 : cTagsMask := cTagsMask and not htTeamName; + {--no-hogtag} 32 : cTagsMask := cTagsMask and not htName; + {--no-healthtag} 33 : cTagsMask := cTagsMask and not htHealth; + {--translucent-tags} 34 : cTagsMask := cTagsMask or htTransparent + else + begin + //Asusme the first "non parameter" is the replay file, anything else is invalid + if (recordFileName = '') and (Copy(cmd,1,2) <> '--') then + recordFileName := cmd + else + begin + WriteLn(stderr, '"'+cmd+'" is not a valid option'); + parseParameter:= true; + end; + end; + end; +end; + +procedure parseClassicParameter(cmdArray: Array of String; size:LongInt; var paramIndex:LongInt); +var index, tmpInt: LongInt; + isBool, isValid: Boolean; + cmd, arg, newSyntax: String; +begin + WriteLn(stdout, 'WARNING: you are using a deprecated command, which could be removed in a future version!'); + WriteLn(stdout, ' Consider updating to the latest syntax, which is much more flexible!'); + WriteLn(stdout, ' Run `hwegine --help` to learn it!'); + WriteLn(stdout, ''); + + index:= 0; + tmpInt:= 1; + while (index < size) do + begin + newSyntax:= ''; + inc(paramIndex); + cmd:= cmdArray[index]; + arg:= ParamStr(paramIndex); + isValid:= (cmd<>'--depth'); + + // check if the parameter is a boolean one + isBool:= (cmd = '--nomusic') or (cmd = '--nosound') or (cmd = '--fullscreen') or (cmd = '--showfps') or (cmd = '--altdmg') or (cmd = '--no-teamtag') or (cmd = '--no-hogtag') or (cmd = '--no-healthtag') or (cmd = '--translucent-tags'); + if isBool and (arg='0') then + isValid:= false; + if (cmd='--nomusic') or (cmd='--nosound') then + isValid:= not isValid; + + if isValid then + begin + parseParameter(cmd, arg, tmpInt); + newSyntax := newSyntax + cmd + ' '; + if not isBool then + newSyntax := newSyntax + arg + ' '; + end; + inc(index); + end; + + WriteLn(stdout, 'Attempted to automatically convert to the new syntax:'); + WriteLn(stdout, newSyntax); + WriteLn(stdout, ''); +end; + +procedure parseCommandLine{$IFDEF HWLIBRARY}(argc: LongInt; argv: PPChar){$ENDIF}; +var paramIndex: LongInt; + paramTotal: LongInt; + index, nextIndex: LongInt; + wrongParameter: boolean; +//var tmpInt: LongInt; +begin + paramIndex:= {$IFDEF HWLIBRARY}0{$ELSE}1{$ENDIF}; + paramTotal:= {$IFDEF HWLIBRARY}argc-1{$ELSE}ParamCount{$ENDIF}; //-1 because pascal enumeration is inclusive + (* + WriteLn(stdout, 'total parameters: ' + inttostr(paramTotal)); + tmpInt:= 0; + while (tmpInt <= paramTotal) do + begin + WriteLn(stdout, inttostr(tmpInt) + ': ' + {$IFDEF HWLIBRARY}argv[tmpInt]{$ELSE}paramCount(tmpInt){$ENDIF}); + inc(tmpInt); + end; + *) + wrongParameter:= false; + while (paramIndex <= paramTotal) do + begin + // avoid going past the number of paramTotal (esp. w/ library) + index:= paramIndex; + if index = paramTotal then nextIndex:= index + else nextIndex:= index+1; + {$IFDEF HWLIBRARY} + wrongParameter:= parseParameter( argv[index], argv[nextIndex], paramIndex); + {$ELSE} + wrongParameter:= parseParameter( ParamStr(index), ParamStr(nextIndex), paramIndex); + {$ENDIF} + inc(paramIndex); + end; + if wrongParameter = true then + GameType:= gmtSyntax; +end; + +{$IFNDEF HWLIBRARY} +procedure GetParams; +begin + isInternal:= (ParamStr(1) = '--internal'); + + UserPathPrefix := '.'; + PathPrefix := cDefaultPathPrefix; + recordFileName := ''; + parseCommandLine(); + + if (isInternal) and (ParamCount<=1) then + begin + WriteLn(stderr, '--internal should not be manually used'); + GameType := gmtSyntax; + end; + + if (not isInternal) and (recordFileName = '') then + begin + WriteLn(stderr, 'You must specify a replay file'); + GameType := gmtSyntax; + end + else if (recordFileName <> '') then + WriteLn(stdout, 'Attempting to play demo file "' + recordFilename + '"'); + + if (GameType = gmtSyntax) then + WriteLn(stderr, 'Please use --help to see possible arguments and their usage'); + + (* + WriteLn(stdout,'PathPrefix: ' + PathPrefix); + WriteLn(stdout,'UserPathPrefix: ' + UserPathPrefix); + *) +end; +{$ENDIF} + +end. + diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/CMakeLists.txt --- a/hedgewars/CMakeLists.txt Wed Jun 26 21:40:10 2013 -0400 +++ b/hedgewars/CMakeLists.txt Thu Jun 27 15:51:20 2013 +0400 @@ -73,9 +73,6 @@ uScript.pas hwengine.pas - GSHandlers.inc - VGSHandlers.inc - ArgParsers.inc options.inc ${CMAKE_CURRENT_BINARY_DIR}/config.inc ) @@ -93,48 +90,20 @@ #DEPENDECIES AND EXECUTABLES SECTION -if(APPLE) - if(CMAKE_OSX_ARCHITECTURES) - #parse this system variable and adjust only the powerpc syntax to be compatible with -P - string(REGEX MATCH "[pP][pP][cC]+" powerpc_build "${CMAKE_OSX_ARCHITECTURES}") - string(REGEX MATCH "[iI]386+" i386_build "${CMAKE_OSX_ARCHITECTURES}") - string(REGEX MATCH "[xX]86_64+" x86_64_build "${CMAKE_OSX_ARCHITECTURES}") - if(x86_64_build) - add_flag_prepend(CMAKE_Pascal_FLAGS -Px86_64) - elseif(i386_build) - add_flag_prepend(CMAKE_Pascal_FLAGS -Pi386) - elseif(powerpc_build) - add_flag_prepend(CMAKE_Pascal_FLAGS -Ppowerpc) - else() - message(FATAL_ERROR "Unknown architecture present in CMAKE_OSX_ARCHITECTURES (${CMAKE_OSX_ARCHITECTURES})") - endif() - list(LENGTH CMAKE_OSX_ARCHITECTURES num_of_archs) - if(num_of_archs GREATER 1) - message(${WARNING} "Only one architecture in CMAKE_OSX_ARCHITECTURES is currently supported, picking the first one") - endif() - elseif(CMAKE_SIZEOF_VOID_P MATCHES "8") - #if that variable is not set check if we are on x86_64 and if so force it, else use default - add_flag_prepend(CMAKE_Pascal_FLAGS -Px86_64) - endif() - +if(NOT ${BUILD_ENGINE_LIBRARY} AND APPLE) #on OSX we need to provide the SDL_main() function when building as executable - if(NOT BUILD_ENGINE_LIBRARY) - add_subdirectory(sdlmain) - list(APPEND HW_LINK_LIBS SDLmain) - add_flag_append(CMAKE_Pascal_FLAGS -Fl${LIBRARY_OUTPUT_PATH}) - endif() - - #when sysroot is set, make sure that fpc picks it - if(CMAKE_OSX_SYSROOT) - set(add_flag_append "-XD${CMAKE_OSX_SYSROOT}") - endif(CMAKE_OSX_SYSROOT) -endif(APPLE) + add_subdirectory(sdlmain) + list(APPEND HW_LINK_LIBS SDLmain) + add_flag_append(CMAKE_Pascal_FLAGS -Fl${LIBRARY_OUTPUT_PATH}) +endif() if(FFMPEG_FOUND) add_subdirectory(avwrapper) list(APPEND HW_LINK_LIBS avwrapper) add_definitions(-dUSE_VIDEO_RECORDING) add_flag_append(CMAKE_Pascal_FLAGS -Fl${LIBRARY_OUTPUT_PATH}) + #only for SDL < 2, linking carried out by fpc + find_package_or_disable_msg(GLUT NOVIDEOREC "Video recording will not be built") endif() find_package_or_disable_msg(PNG NOPNG "Screenshots will be saved in BMP") diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/GSHandlers.inc --- a/hedgewars/GSHandlers.inc Wed Jun 26 21:40:10 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5638 +0,0 @@ -(* - * Hedgewars, a free turn based strategy game - * Copyright (c) 2004-2013 Andrey Korotaev - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - *) - -(* - * This file contains the step handlers for gears. - * - * Important: Since gears change the course of the game, calculations that - * lead to different results for different clients/players/machines - * should NOT occur! - * Use safe functions and data types! (e.g. GetRandom() and hwFloat) - *) - -procedure doStepPerPixel(Gear: PGear; step: TGearStepProcedure; onlyCheckIfChanged: boolean); -var - dX, dY, sX, sY: hwFloat; - i, steps: LongWord; - caller: TGearStepProcedure; -begin - dX:= Gear^.dX; - dY:= Gear^.dY; - steps:= max(abs(hwRound(Gear^.X+dX)-hwRound(Gear^.X)), abs(hwRound(Gear^.Y+dY)-hwRound(Gear^.Y))); - - // Gear is still on the same Pixel it was before - if steps < 1 then - begin - if onlyCheckIfChanged then - begin - Gear^.X := Gear^.X + dX; - Gear^.Y := Gear^.Y + dY; - EXIT; - end - else - steps := 1; - end; - - if steps > 1 then - begin - sX:= dX / steps; - sY:= dY / steps; - end - - else - begin - sX:= dX; - sY:= dY; - end; - - caller:= Gear^.doStep; - - for i:= 1 to steps do - begin - Gear^.X := Gear^.X + sX; - Gear^.Y := Gear^.Y + sY; - step(Gear); - if (Gear^.doStep <> caller) - or ((Gear^.State and gstCollision) <> 0) - or ((Gear^.State and gstMoving) = 0) then - break; - end; -end; - -procedure makeHogsWorry(x, y: hwFloat; r: LongInt); -var - gi: PGear; - d: LongInt; -begin - gi := GearsList; - while gi <> nil do - begin - if (gi^.Kind = gtHedgehog) then - begin - d := r - hwRound(Distance(gi^.X - x, gi^.Y - y)); - if (d > 1) and (not gi^.Invulnerable) and (GetRandom(2) = 0) then - begin - if (CurrentHedgehog^.Gear = gi) then - PlaySoundV(sndOops, gi^.Hedgehog^.Team^.voicepack) - - else - begin - if ((gi^.State and gstMoving) = 0) and (gi^.Hedgehog^.Effects[heFrozen] = 0) then - begin - gi^.dX.isNegative:= X r div 2 then - PlaySoundV(sndNooo, gi^.Hedgehog^.Team^.voicepack) - else - PlaySoundV(sndUhOh, gi^.Hedgehog^.Team^.voicepack); - end; - end; - end; - - gi := gi^.NextGear - end; -end; - -procedure HideHog(HH: PHedgehog); -begin - ScriptCall('onHogHide', HH^.Gear^.Uid); - DeleteCI(HH^.Gear); - if FollowGear = HH^.Gear then - FollowGear:= nil; - - if lastGearByUID = HH^.Gear then - lastGearByUID := nil; - - HH^.Gear^.Message:= HH^.Gear^.Message or gmRemoveFromList; - with HH^.Gear^ do - begin - Z := cHHZ; - HH^.Gear^.Active:= false; - State:= State and (not (gstHHDriven or gstAttacking or gstAttacked)); - Message := Message and (not gmAttack); - end; - HH^.GearHidden:= HH^.Gear; - HH^.Gear:= nil -end; - - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepDrowningGear(Gear: PGear); - begin - AllInactive := false; - Gear^.Y := Gear^.Y + cDrownSpeed; - Gear^.X := Gear^.X + Gear^.dX * cDrownSpeed; - // Create some bubbles (0.5% might be better but causes too few bubbles sometimes) - if ((not SuddenDeathDmg and (WaterOpacity < $FF)) - or (SuddenDeathDmg and (SDWaterOpacity < $FF))) and ((GameTicks and $1F) = 0) then - if (Gear^.Kind = gtHedgehog) and (Random(4) = 0) then - AddVisualGear(hwRound(Gear^.X) - Gear^.Radius, hwRound(Gear^.Y) - Gear^.Radius, vgtBubble) - else if Random(12) = 0 then - AddVisualGear(hwRound(Gear^.X) - Gear^.Radius, hwRound(Gear^.Y) - Gear^.Radius, vgtBubble); - if (not SuddenDeathDmg and (WaterOpacity > $FE)) - or (SuddenDeathDmg and (SDWaterOpacity > $FE)) - or (hwRound(Gear^.Y) > Gear^.Radius + cWaterLine + cVisibleWater) then - DeleteGear(Gear); - end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepFallingGear(Gear: PGear); -var - isFalling: boolean; - //tmp: QWord; - tdX, tdY: hwFloat; - collV, collH: LongInt; - land: word; -begin - // clip velocity at 2 - over 1 per pixel, but really shouldn't cause many actual problems. - if Gear^.dX.Round > 2 then - Gear^.dX.QWordValue:= 8589934592; - if Gear^.dY.Round > 2 then - Gear^.dY.QWordValue:= 8589934592; - - if (Gear^.State and gstSubmersible <> 0) and (hwRound(Gear^.Y) > cWaterLine) then - begin - Gear^.dX:= Gear^.dX * _0_999; - Gear^.dY:= Gear^.dY * _0_999 - end; - - Gear^.State := Gear^.State and (not gstCollision); - collV := 0; - collH := 0; - tdX := Gear^.dX; - tdY := Gear^.dY; - - - -// might need some testing/adjustments - just to avoid projectiles to fly forever (accelerated by wind/skips) - if (hwRound(Gear^.X) < min(LAND_WIDTH div -2, -2048)) - or (hwRound(Gear^.X) > max(LAND_WIDTH * 3 div 2, 6144)) then - Gear^.State := Gear^.State or gstCollision; - - if Gear^.dY.isNegative then - begin - isFalling := true; - land:= TestCollisionYwithGear(Gear, -1); - if land <> 0 then - begin - collV := -1; - if land and lfIce <> 0 then - Gear^.dX := Gear^.dX * (_0_9 + Gear^.Friction * _0_1) - else - Gear^.dX := Gear^.dX * Gear^.Friction; - - Gear^.dY := - Gear^.dY * Gear^.Elasticity; - Gear^.State := Gear^.State or gstCollision - end - else if (Gear^.AdvBounce=1) and (TestCollisionYwithGear(Gear, 1) <> 0) then - collV := 1; - end - else - begin // Gear^.dY.isNegative is false - land:= TestCollisionYwithGear(Gear, 1); - if land <> 0 then - begin - collV := 1; - isFalling := false; - if land and lfIce <> 0 then - Gear^.dX := Gear^.dX * (_0_9 + Gear^.Friction * _0_1) - else - Gear^.dX := Gear^.dX * Gear^.Friction; - - Gear^.dY := - Gear^.dY * Gear^.Elasticity; - Gear^.State := Gear^.State or gstCollision - end - else - begin - isFalling := true; - if (Gear^.AdvBounce=1) and (TestCollisionYwithGear(Gear, -1) <> 0) then - collV := -1 - end - end; - - - if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then - begin - collH := hwSign(Gear^.dX); - Gear^.dX := - Gear^.dX * Gear^.Elasticity; - Gear^.dY := Gear^.dY * Gear^.Elasticity; - Gear^.State := Gear^.State or gstCollision - end - else if (Gear^.AdvBounce=1) and TestCollisionXwithGear(Gear, -hwSign(Gear^.dX)) then - collH := -hwSign(Gear^.dX); - //if Gear^.AdvBounce and (collV <>0) and (collH <> 0) and (hwSqr(tdX) + hwSqr(tdY) > _0_08) then - if (Gear^.AdvBounce=1) and (collV <>0) and (collH <> 0) and ((collV=-1) - or ((tdX.QWordValue + tdY.QWordValue) > _0_2.QWordValue)) then - begin - Gear^.dX := tdY*Gear^.Elasticity*Gear^.Friction; - Gear^.dY := tdX*Gear^.Elasticity; - //*Gear^.Friction; - Gear^.dY.isNegative := not tdY.isNegative; - isFalling := false; - Gear^.AdvBounce := 10; - end; - - if Gear^.AdvBounce > 1 then - dec(Gear^.AdvBounce); - - if isFalling then - begin - Gear^.dY := Gear^.dY + cGravity; - if (GameFlags and gfMoreWind) <> 0 then - Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density - end; - - Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + Gear^.dY; - if Gear^.Kind <> gtBee then - CheckGearDrowning(Gear); - //if (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) < _0_0002) and - if (not isFalling) and ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) < _0_02.QWordValue) then - Gear^.State := Gear^.State and (not gstMoving) - else - Gear^.State := Gear^.State or gstMoving; - - if (Gear^.nImpactSounds > 0) and - (Gear^.State and gstCollision <> 0) and - (((Gear^.Kind <> gtMine) and (Gear^.Damage <> 0)) or (Gear^.State and gstMoving <> 0)) and - (((Gear^.Radius < 3) and (Gear^.dY < -_0_1)) or - ((Gear^.Radius >= 3) and - ((Gear^.dX.QWordValue > _0_1.QWordValue) or (Gear^.dY.QWordValue > _0_1.QWordValue)))) then - PlaySound(TSound(ord(Gear^.ImpactSound) + LongInt(GetRandom(Gear^.nImpactSounds))), true); -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepBomb(Gear: PGear); -var - i, x, y: LongInt; - dX, dY, gdX: hwFloat; - vg: PVisualGear; -begin - AllInactive := false; - - doStepFallingGear(Gear); - - dec(Gear^.Timer); - if Gear^.Timer = 1000 then // might need adjustments - case Gear^.Kind of - gtGrenade: makeHogsWorry(Gear^.X, Gear^.Y, 50); - gtClusterBomb: makeHogsWorry(Gear^.X, Gear^.Y, 20); - gtWatermelon: makeHogsWorry(Gear^.X, Gear^.Y, 75); - gtHellishBomb: makeHogsWorry(Gear^.X, Gear^.Y, 90); - gtGasBomb: makeHogsWorry(Gear^.X, Gear^.Y, 50); - end; - - if (Gear^.Kind = gtBall) and ((Gear^.State and gstTmpFlag) <> 0) then - begin - CheckCollision(Gear); - if (Gear^.State and gstCollision) <> 0 then - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx); - end; - - if (Gear^.Kind = gtGasBomb) and ((GameTicks mod 200) = 0) then - begin - vg:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeWhite); - if vg <> nil then - vg^.Tint:= $FFC0C000; - end; - - if Gear^.Timer = 0 then - begin - case Gear^.Kind of - gtGrenade: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); - gtBall: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 40, Gear^.Hedgehog, EXPLAutoSound); - gtClusterBomb: - begin - x := hwRound(Gear^.X); - y := hwRound(Gear^.Y); - gdX:= Gear^.dX; - doMakeExplosion(x, y, 20, Gear^.Hedgehog, EXPLAutoSound); - for i:= 0 to 4 do - begin - dX := rndSign(GetRandomf * _0_1) + gdX / 5; - dY := (GetRandomf - _3) * _0_08; - FollowGear := AddGear(x, y, gtCluster, 0, dX, dY, 25) - end - end; - gtWatermelon: - begin - x := hwRound(Gear^.X); - y := hwRound(Gear^.Y); - gdX:= Gear^.dX; - doMakeExplosion(x, y, 75, Gear^.Hedgehog, EXPLAutoSound); - for i:= 0 to 5 do - begin - dX := rndSign(GetRandomf * _0_1) + gdX / 5; - dY := (GetRandomf - _1_5) * _0_3; - FollowGear:= AddGear(x, y, gtMelonPiece, 0, dX, dY, 75); - FollowGear^.DirAngle := i * 60 - end - end; - gtHellishBomb: - begin - x := hwRound(Gear^.X); - y := hwRound(Gear^.Y); - doMakeExplosion(x, y, 90, Gear^.Hedgehog, EXPLAutoSound); - - for i:= 0 to 127 do - begin - dX := AngleCos(i * 16) * _0_5 * (GetRandomf + _1); - dY := AngleSin(i * 16) * _0_5 * (GetRandomf + _1); - if i mod 2 = 0 then - begin - AddGear(x, y, gtFlame, gstTmpFlag, dX, dY, 0); - AddGear(x, y, gtFlame, 0, dX, -dY, 0) - end - else - begin - AddGear(x, y, gtFlame, 0, dX, dY, 0); - AddGear(x, y, gtFlame, gstTmpFlag, dX, -dY, 0) - end; - end - end; - gtGasBomb: - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLAutoSound); - for i:= 0 to 2 do - begin - x:= GetRandom(60); - y:= GetRandom(40); - FollowGear:= AddGear(hwRound(Gear^.X) - 30 + x, hwRound(Gear^.Y) - 20 + y, gtPoisonCloud, 0, _0, _0, 0); - end - end; - end; - DeleteGear(Gear); - exit - end; - - CalcRotationDirAngle(Gear); - - if Gear^.Kind = gtHellishBomb then - begin - - if Gear^.Timer = 3000 then - begin - Gear^.nImpactSounds := 0; - PlaySound(sndHellish); - end; - - if (GameTicks and $3F) = 0 then - if (Gear^.State and gstCollision) = 0 then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEvilTrace); - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepMolotov(Gear: PGear); -var - s: Longword; - i, gX, gY: LongInt; - dX, dY: hwFloat; - smoke, glass: PVisualGear; -begin - AllInactive := false; - - doStepFallingGear(Gear); - CalcRotationDirAngle(Gear); - - // let's add some smoke depending on speed - s:= max(32,152 - round((abs(hwFloat2FLoat(Gear^.dX))+abs(hwFloat2Float(Gear^.dY)))*120))+random(10); - if (GameTicks mod s) = 0 then - begin - // adjust angle to match the texture - if Gear^.dX.isNegative then - i:= 130 - else i:= 50; - - smoke:= AddVisualGear(hwRound(Gear^.X)-round(cos((Gear^.DirAngle+i) * pi / 180)*20), hwRound(Gear^.Y)-round(sin((Gear^.DirAngle+i) * pi / 180)*20), vgtSmoke); - if smoke <> nil then - smoke^.Scale:= 0.75; - end; - - if (Gear^.State and gstCollision) <> 0 then - begin - PlaySound(sndMolotov); - gX := hwRound(Gear^.X); - gY := hwRound(Gear^.Y); - for i:= 0 to 4 do - begin - (*glass:= AddVisualGear(gx+random(7)-3, gy+random(5)-2, vgtEgg); - if glass <> nil then - begin - glass^.Frame:= 2; - glass^.Tint:= $41B83ED0 - i * $10081000; - glass^.dX:= 1/(10*(random(11)-5)); - glass^.dY:= -1/(random(4)+5); - end;*) - glass:= AddVisualGear(gx+random(7)-3, gy+random(7)-3, vgtStraightShot); - if glass <> nil then - with glass^ do - begin - Frame:= 2; - Tint:= $41B83ED0 - i * $10081000; - Angle:= random(360); - dx:= 0.0000001; - dy:= 0; - if random(2) = 0 then - dx := -dx; - FrameTicks:= 750; - State:= ord(sprEgg) - end; - end; - for i:= 0 to 24 do - begin - dX := AngleCos(i * 2) * ((_0_15*(i div 5))) * (GetRandomf + _1); - dY := AngleSin(i * 8) * _0_5 * (GetRandomf + _1); - AddGear(gX, gY, gtFlame, gstTmpFlag, dX, dY, 0); - AddGear(gX, gY, gtFlame, gstTmpFlag, dX,-dY, 0); - AddGear(gX, gY, gtFlame, gstTmpFlag,-dX, dY, 0); - AddGear(gX, gY, gtFlame, gstTmpFlag,-dX,-dY, 0); - end; - DeleteGear(Gear); - exit - end; -end; - -//////////////////////////////////////////////////////////////////////////////// - -procedure doStepCluster(Gear: PGear); -begin - AllInactive := false; - doStepFallingGear(Gear); - if (Gear^.State and gstCollision) <> 0 then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Timer, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - exit - end; - - if (Gear^.Kind = gtMelonPiece) - or (Gear^.Kind = gtBall) then - CalcRotationDirAngle(Gear) - else if (GameTicks and $1F) = 0 then - begin - if hwRound(Gear^.Y) > cWaterLine then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble) - else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepShell(Gear: PGear); -begin - AllInactive := false; - if (GameFlags and gfMoreWind) = 0 then - Gear^.dX := Gear^.dX + cWindSpeed; - doStepFallingGear(Gear); - if (Gear^.State and gstCollision) <> 0 then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - exit - end; - if (GameTicks and $3F) = 0 then - begin - if hwRound(Gear^.Y) > cWaterLine then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble) - else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSnowball(Gear: PGear); -var kick, i: LongInt; - particle: PVisualGear; - gdX, gdY: hwFloat; -begin - AllInactive := false; - if (GameFlags and gfMoreWind) = 0 then - Gear^.dX := Gear^.dX + cWindSpeed; - gdX := Gear^.dX; - gdY := Gear^.dY; - doStepFallingGear(Gear); - CalcRotationDirAngle(Gear); - if (Gear^.State and gstCollision) <> 0 then - begin - kick:= hwRound((hwAbs(gdX)+hwAbs(gdY)) * _20); - Gear^.dX:= gdX; - Gear^.dY:= gdY; - AmmoShove(Gear, 0, kick); - for i:= 15 + kick div 10 downto 0 do - begin - particle := AddVisualGear(hwRound(Gear^.X) + Random(25), hwRound(Gear^.Y) + Random(25), vgtDust); - if particle <> nil then - particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480) - end; - DeleteGear(Gear); - exit - end; - if ((GameTicks and $1F) = 0) and (Random(3) = 0) then - begin - particle:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust); - if particle <> nil then - particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480) - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSnowflake(Gear: PGear); -var xx, yy, px, py, rx, ry, lx, ly: LongInt; - move, draw, allpx, gun: Boolean; - s: PSDL_Surface; - p: PLongwordArray; - lf: LongWord; -begin -inc(Gear^.Pos); -gun:= (Gear^.State and gstTmpFlag) <> 0; -move:= false; -draw:= false; -if gun then - begin - Gear^.State:= Gear^.State and (not gstInvisible); - doStepFallingGear(Gear); - CheckCollision(Gear); - if ((Gear^.State and gstCollision) <> 0) or ((Gear^.State and gstMoving) = 0) then - draw:= true; - xx:= hwRound(Gear^.X); - yy:= hwRound(Gear^.Y); - end -else if GameTicks and $7 = 0 then - begin - with Gear^ do - begin - State:= State and (not gstInvisible); - X:= X + cWindSpeed * 3200 + dX; - Y:= Y + dY + cGravity * vobFallSpeed * 8; // using same value as flakes to try and get similar results - xx:= hwRound(X); - yy:= hwRound(Y); - if vobVelocity <> 0 then - begin - DirAngle := DirAngle + (Damage / 1000); - if DirAngle < 0 then - DirAngle := DirAngle + 360 - else if 360 < DirAngle then - DirAngle := DirAngle - 360; - end; -(* -We aren't using frametick right now, so just a waste of cycles. - inc(Health, 8); - if longword(Health) > vobFrameTicks then - begin - dec(Health, vobFrameTicks); - inc(Timer); - if Timer = vobFramesCount then - Timer:= 0 - end; -*) - // move back to cloud layer - if yy > cWaterLine then - move:= true - else if (xx > snowRight) or (xx < snowLeft) then - move:=true - // Solid pixel encountered - else if ((yy and LAND_HEIGHT_MASK) = 0) and ((xx and LAND_WIDTH_MASK) = 0) and (Land[yy, xx] <> 0) then - begin - lf:= Land[yy, xx] and (lfObject or lfBasic or lfIndestructible); - if lf = 0 then lf:= lfObject; - // If there's room below keep falling - if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (Land[yy-1, xx] = 0) then - begin - X:= X - cWindSpeed * 1600 - dX; - end - // If there's room below, on the sides, fill the gaps - else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx-(1*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx-(1*hwSign(cWindSpeed)))] = 0) then - begin - X:= X - _0_8 * hwSign(cWindSpeed); - Y:= Y - dY - cGravity * vobFallSpeed * 8; - end - else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx-(2*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx-(2*hwSign(cWindSpeed)))] = 0) then - begin - X:= X - _0_8 * 2 * hwSign(cWindSpeed); - Y:= Y - dY - cGravity * vobFallSpeed * 8; - end - else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx+(1*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx+(1*hwSign(cWindSpeed)))] = 0) then - begin - X:= X + _0_8 * hwSign(cWindSpeed); - Y:= Y - dY - cGravity * vobFallSpeed * 8; - end - else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx+(2*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx+(2*hwSign(cWindSpeed)))] = 0) then - begin - X:= X + _0_8 * 2 * hwSign(cWindSpeed); - Y:= Y - dY - cGravity * vobFallSpeed * 8; - end - // if there's an hog/object below do nothing - else if ((((yy+1) and LAND_HEIGHT_MASK) = 0) and ((Land[yy+1, xx] and $FF) <> 0)) - then move:=true - else draw:= true - end - end - end; -if draw then - with Gear^ do - begin - // we've collided with land. draw some stuff and get back into the clouds - move:= true; - if (Pos > 20) and ((CurAmmoGear = nil) - or (CurAmmoGear^.Kind <> gtRope)) then - begin -////////////////////////////////// TODO - ASK UNC0RR FOR A GOOD HOME FOR THIS //////////////////////////////////// - if not gun then - begin - dec(yy,3); - dec(xx,1) - end; - s:= SpritesData[sprSnow].Surface; - p:= s^.pixels; - allpx:= true; - for py:= 0 to Pred(s^.h) do - begin - for px:= 0 to Pred(s^.w) do - begin - lx:=xx + px; ly:=yy + py; - if (ly and LAND_HEIGHT_MASK = 0) and (lx and LAND_WIDTH_MASK = 0) and (Land[ly, lx] and $FF = 0) then - begin - rx:= lx; - ry:= ly; - if cReducedQuality and rqBlurryLand <> 0 then - begin - rx:= rx div 2;ry:= ry div 2; - end; - if Land[yy + py, xx + px] <= lfAllObjMask then - if gun then - begin - LandDirty[yy div 32, xx div 32]:= 1; - if LandPixels[ry, rx] = 0 then - Land[ly, lx]:= lfDamaged or lfObject - else Land[ly, lx]:= lfDamaged or lfBasic - end - else Land[ly, lx]:= lf; - if gun then - LandPixels[ry, rx]:= (ExplosionBorderColor and (not AMask)) or (p^[px] and AMask) - else LandPixels[ry, rx]:= addBgColor(LandPixels[ry, rx], p^[px]); - end - else allpx:= false - end; - p:= @(p^[s^.pitch shr 2]) - end; - - // Why is this here. For one thing, there's no test on +1 being safe. - //Land[py, px+1]:= lfBasic; - - if allpx then - UpdateLandTexture(xx, Pred(s^.h), yy, Pred(s^.w), true) - else - begin - UpdateLandTexture( - max(0, min(LAND_WIDTH, xx)), - min(LAND_WIDTH - xx, Pred(s^.w)), - max(0, min(LAND_WIDTH, yy)), - min(LAND_HEIGHT - yy, Pred(s^.h)), false // could this be true without unnecessarily creating blanks? - ); - end; -////////////////////////////////// TODO - ASK UNC0RR FOR A GOOD HOME FOR THIS //////////////////////////////////// - end - end; - -if move then - begin - if gun then - begin - DeleteGear(Gear); - exit - end; - Gear^.Pos:= 0; - Gear^.X:= int2hwFloat(LongInt(GetRandom(snowRight - snowLeft)) + snowLeft); - Gear^.Y:= int2hwFloat(LAND_HEIGHT + LongInt(GetRandom(50)) - 1325); - Gear^.State:= Gear^.State or gstInvisible; - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepGrave(Gear: PGear); -begin - if (Gear^.Message and gmDestroy) <> 0 then - begin - DeleteGear(Gear); - exit - end; - - AllInactive := false; - - if Gear^.dY.isNegative then - if TestCollisionY(Gear, -1) then - Gear^.dY := _0; - - if not Gear^.dY.isNegative then - if TestCollisionY(Gear, 1) then - begin - Gear^.dY := - Gear^.dY * Gear^.Elasticity; - if Gear^.dY > - _1div1024 then - begin - Gear^.Active := false; - exit - end - else if Gear^.dY < - _0_03 then - PlaySound(Gear^.ImpactSound) - end; - - Gear^.Y := Gear^.Y + Gear^.dY; - CheckGearDrowning(Gear); - Gear^.dY := Gear^.dY + cGravity -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepBeeWork(Gear: PGear); -var - t: hwFloat; - gX,gY,i: LongInt; - uw, nuw: boolean; - flower: PVisualGear; - -begin - AllInactive := false; - gX := hwRound(Gear^.X); - gY := hwRound(Gear^.Y); - uw := (Gear^.Tag <> 0); // was bee underwater last tick? - nuw := (cWaterLine < gy + Gear^.Radius); // is bee underwater now? - - // if water entered or left - if nuw <> uw then - begin - AddVisualGear(gX, cWaterLine, vgtSplash); - AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet); - AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet); - AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet); - AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet); - StopSoundChan(Gear^.SoundChannel); - if nuw then - begin - Gear^.SoundChannel := LoopSound(sndBeeWater); - Gear^.Tag := 1; - end - else - begin - Gear^.SoundChannel := LoopSound(sndBee); - Gear^.Tag := 0; - end; - end; - - - if Gear^.Timer = 0 then - Gear^.RenderTimer:= false - else - begin - if (GameTicks and $F) = 0 then - begin - if (GameTicks and $30) = 0 then - AddVisualGear(gX, gY, vgtBeeTrace); - Gear^.dX := Gear^.Elasticity * (Gear^.dX + _0_000064 * (Gear^.Target.X - gX)); - Gear^.dY := Gear^.Elasticity * (Gear^.dY + _0_000064 * (Gear^.Target.Y - gY)); - // make sure new speed isn't higher than original one (which we stored in Friction variable) - t := Gear^.Friction / Distance(Gear^.dX, Gear^.dY); - Gear^.dX := Gear^.dX * t; - Gear^.dY := Gear^.dY * t; - end; - - Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + Gear^.dY; - - end; - - - CheckCollision(Gear); - if ((Gear^.State and gstCollision) <> 0) then - begin - StopSoundChan(Gear^.SoundChannel); - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); - for i:= 0 to 31 do - begin - flower:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot); - if flower <> nil then - with flower^ do - begin - Scale:= 0.75; - dx:= 0.001 * (random(200)); - dy:= 0.001 * (random(200)); - if random(2) = 0 then - dx := -dx; - if random(2) = 0 then - dy := -dy; - FrameTicks:= random(250) + 250; - State:= ord(sprTargetBee); - end; - end; - DeleteGear(Gear); - end; - - if (Gear^.Timer > 0) then - dec(Gear^.Timer) - else - begin - if nuw then - begin - StopSoundChan(Gear^.SoundChannel); - CheckGearDrowning(Gear); - end - else - doStepFallingGear(Gear); - end; -end; - -procedure doStepBee(Gear: PGear); -begin - AllInactive := false; - Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + Gear^.dY; - Gear^.dY := Gear^.dY + cGravity; - CheckCollision(Gear); - if (Gear^.State and gstCollision) <> 0 then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - exit - end; - dec(Gear^.Timer); - if Gear^.Timer = 0 then - begin - Gear^.Hedgehog^.Gear^.Message:= Gear^.Hedgehog^.Gear^.Message and (not gmAttack); - Gear^.Hedgehog^.Gear^.State:= Gear^.Hedgehog^.Gear^.State and (not gstAttacking); - AttackBar:= 0; - - Gear^.SoundChannel := LoopSound(sndBee); - Gear^.Timer := 5000; - // save initial speed in otherwise unused Friction variable - Gear^.Friction := Distance(Gear^.dX, Gear^.dY); - Gear^.doStep := @doStepBeeWork - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepShotIdle(Gear: PGear); -begin - AllInactive := false; - inc(Gear^.Timer); - if Gear^.Timer > 75 then - begin - DeleteGear(Gear); - AfterAttack - end -end; - -procedure doStepShotgunShot(Gear: PGear); -var - i: LongWord; - shell: PVisualGear; -begin - AllInactive := false; - - if ((Gear^.State and gstAnimation) = 0) then - begin - dec(Gear^.Timer); - if Gear^.Timer = 0 then - begin - PlaySound(sndShotgunFire); - shell := AddVisualGear(hwRound(Gear^.x), hwRound(Gear^.y), vgtShell); - if shell <> nil then - begin - shell^.dX := gear^.dX.QWordValue / -17179869184; - shell^.dY := gear^.dY.QWordValue / -17179869184; - shell^.Frame := 0 - end; - Gear^.State := Gear^.State or gstAnimation - end; - exit - end else - if(Gear^.Hedgehog^.Gear = nil) or ((Gear^.Hedgehog^.Gear^.State and gstMoving) <> 0) then - begin - DeleteGear(Gear); - AfterAttack; - exit - end - else - inc(Gear^.Timer); - - i := 200; - repeat - Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + Gear^.dY; - CheckCollision(Gear); - if (Gear^.State and gstCollision) <> 0 then - begin - Gear^.X := Gear^.X + Gear^.dX * 8; - Gear^.Y := Gear^.Y + Gear^.dY * 8; - ShotgunShot(Gear); - Gear^.doStep := @doStepShotIdle; - exit - end; - - CheckGearDrowning(Gear); - if (Gear^.State and gstDrowning) <> 0 then - begin - Gear^.doStep := @doStepShotIdle; - exit - end; - dec(i) - until i = 0; - if (hwRound(Gear^.X) and LAND_WIDTH_MASK <> 0) or (hwRound(Gear^.Y) and LAND_HEIGHT_MASK <> 0) then - Gear^.doStep := @doStepShotIdle -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure spawnBulletTrail(Bullet: PGear); -var oX, oY: hwFloat; - VGear: PVisualGear; -begin - if Bullet^.PortalCounter = 0 then - begin - ox:= CurrentHedgehog^.Gear^.X + Int2hwFloat(GetLaunchX(CurrentHedgehog^.CurAmmoType, hwSign(CurrentHedgehog^.Gear^.dX), CurrentHedgehog^.Gear^.Angle)); - oy:= CurrentHedgehog^.Gear^.Y + Int2hwFloat(GetLaunchY(CurrentHedgehog^.CurAmmoType, CurrentHedgehog^.Gear^.Angle)); - end - else - begin - ox:= Bullet^.Elasticity; - oy:= Bullet^.Friction; - end; - - // Bullet trail - VGear := AddVisualGear(hwRound(ox), hwRound(oy), vgtLineTrail); - - if VGear <> nil then - begin - VGear^.X:= hwFloat2Float(ox); - VGear^.Y:= hwFloat2Float(oy); - VGear^.dX:= hwFloat2Float(Bullet^.X); - VGear^.dY:= hwFloat2Float(Bullet^.Y); - - // reached edge of land. assume infinite beam. Extend it way out past camera - if (hwRound(Bullet^.X) and LAND_WIDTH_MASK <> 0) - or (hwRound(Bullet^.Y) and LAND_HEIGHT_MASK <> 0) then - // only extend if not under water - if hwRound(Bullet^.Y) < cWaterLine then - begin - VGear^.dX := VGear^.dX + max(LAND_WIDTH,4096) * (VGear^.dX - VGear^.X); - VGear^.dY := VGear^.dY + max(LAND_WIDTH,4096) * (VGear^.dY - VGear^.Y); - end; - - VGear^.Timer := 200; - end; -end; - -procedure doStepBulletWork(Gear: PGear); -var - i, x, y: LongWord; - oX, oY: hwFloat; - VGear: PVisualGear; -begin - AllInactive := false; - inc(Gear^.Timer); - i := 80; - oX := Gear^.X; - oY := Gear^.Y; - repeat - Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + Gear^.dY; - x := hwRound(Gear^.X); - y := hwRound(Gear^.Y); - - if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] <> 0) then - inc(Gear^.Damage); - // let's interrupt before a collision to give portals a chance to catch the bullet - if (Gear^.Damage = 1) and (Gear^.Tag = 0) and not(CheckLandValue(x, y, lfLandMask)) then - begin - Gear^.Tag := 1; - Gear^.Damage := 0; - Gear^.X := Gear^.X - Gear^.dX; - Gear^.Y := Gear^.Y - Gear^.dY; - CheckGearDrowning(Gear); - break; - end - else - Gear^.Tag := 0; - - if Gear^.Damage > 5 then - if Gear^.AmmoType = amDEagle then - AmmoShove(Gear, 7, 20) - else - AmmoShove(Gear, Gear^.Timer, 20); - CheckGearDrowning(Gear); - dec(i) - until (i = 0) or (Gear^.Damage > Gear^.Health) or ((Gear^.State and gstDrowning) <> 0); - - if Gear^.Damage > 0 then - begin - DrawTunnel(oX, oY, Gear^.dX, Gear^.dY, 82 - i, 1); - dec(Gear^.Health, Gear^.Damage); - Gear^.Damage := 0 - end; - if ((Gear^.State and gstDrowning) <> 0) and (Gear^.Damage < Gear^.Health) and ((not SuddenDeathDmg and (WaterOpacity < $FF)) or (SuddenDeathDmg and (SDWaterOpacity < $FF))) then - begin - for i:=(Gear^.Health - Gear^.Damage) * 4 downto 0 do - begin - if Random(6) = 0 then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble); - Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + Gear^.dY; - end; - end; - - if (Gear^.Health <= 0) - or (hwRound(Gear^.X) and LAND_WIDTH_MASK <> 0) - or (hwRound(Gear^.Y) and LAND_HEIGHT_MASK <> 0) then - begin - if (Gear^.Kind = gtSniperRifleShot) and ((GameFlags and gfLaserSight) = 0) then - cLaserSighting := false; - if (Ammoz[Gear^.AmmoType].Ammo.NumPerTurn <= CurrentHedgehog^.MultiShootAttacks) and ((GameFlags and gfArtillery) = 0) then - cArtillery := false; - - // Bullet Hit - if (hwRound(Gear^.X) and LAND_WIDTH_MASK = 0) and (hwRound(Gear^.Y) and LAND_HEIGHT_MASK = 0) then - begin - VGear := AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBulletHit); - if VGear <> nil then - begin - VGear^.Angle := DxDy2Angle(-Gear^.dX, Gear^.dY); - end; - end; - - spawnBulletTrail(Gear); - Gear^.doStep := @doStepShotIdle - end; -end; - -procedure doStepDEagleShot(Gear: PGear); -begin - PlaySound(sndGun); - // add 3 initial steps to avoid problem with ammoshove related to calculation of radius + 1 radius as gear widths, and also just plain old weird angles - Gear^.X := Gear^.X + Gear^.dX * 3; - Gear^.Y := Gear^.Y + Gear^.dY * 3; - Gear^.doStep := @doStepBulletWork -end; - -procedure doStepSniperRifleShot(Gear: PGear); -var - HHGear: PGear; - shell: PVisualGear; -begin - cArtillery := true; - HHGear := Gear^.Hedgehog^.Gear; - HHGear^.State := HHGear^.State or gstNotKickable; - HedgehogChAngle(HHGear); - if not cLaserSighting then - // game does not have default laser sight. turn it on and give them a chance to aim - begin - cLaserSighting := true; - HHGear^.Message := 0; - if (HHGear^.Angle >= 32) then - dec(HHGear^.Angle,32) - end; - - if (HHGear^.Message and gmAttack) <> 0 then - begin - shell := AddVisualGear(hwRound(Gear^.x), hwRound(Gear^.y), vgtShell); - if shell <> nil then - begin - shell^.dX := gear^.dX.QWordValue / -8589934592; - shell^.dY := gear^.dY.QWordValue / -8589934592; - shell^.Frame := 1 - end; - Gear^.State := Gear^.State or gstAnimation; - Gear^.dX := SignAs(AngleSin(HHGear^.Angle), HHGear^.dX) * _0_5; - Gear^.dY := -AngleCos(HHGear^.Angle) * _0_5; - PlaySound(sndGun); - // add 3 initial steps to avoid problem with ammoshove related to calculation of radius + 1 radius as gear widths, and also just weird angles - Gear^.X := Gear^.X + Gear^.dX * 3; - Gear^.Y := Gear^.Y + Gear^.dY * 3; - Gear^.doStep := @doStepBulletWork; - end - else - if (GameTicks mod 32) = 0 then - if (GameTicks mod 4096) < 2048 then - begin - if (HHGear^.Angle + 1 <= cMaxAngle) then - inc(HHGear^.Angle) - end - else - if (HHGear^.Angle >= 1) then - dec(HHGear^.Angle); - - if (TurnTimeLeft > 0) then - dec(TurnTimeLeft) - else - begin - DeleteGear(Gear); - AfterAttack - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepActionTimer(Gear: PGear); -begin -dec(Gear^.Timer); -case Gear^.Kind of - gtATStartGame: - begin - AllInactive := false; - if Gear^.Timer = 0 then - begin - AddCaption(trmsg[sidStartFight], cWhiteColor, capgrpGameState); - end - end; - gtATFinishGame: - begin - AllInactive := false; - if Gear^.Timer = 1000 then - begin - ScreenFade := sfToBlack; - ScreenFadeValue := 0; - ScreenFadeSpeed := 1; - end; - if Gear^.Timer = 0 then - begin - SendIPC(_S'N'); - SendIPC(_S'q'); - GameState := gsExit - end - end; - end; -if Gear^.Timer = 0 then - DeleteGear(Gear) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepPickHammerWork(Gear: PGear); -var - i, ei, x, y: LongInt; - HHGear: PGear; -begin - AllInactive := false; - HHGear := Gear^.Hedgehog^.Gear; - dec(Gear^.Timer); - if ((GameFlags and gfInfAttack) <> 0) and (TurnTimeLeft > 0) then - dec(TurnTimeLeft); - if (TurnTimeLeft = 0) or (Gear^.Timer = 0) - or((Gear^.Message and gmDestroy) <> 0) - or((HHGear^.State and gstHHDriven) =0) then - begin - StopSoundChan(Gear^.SoundChannel); - DeleteGear(Gear); - AfterAttack; - doStepHedgehogMoving(HHGear); // for gfInfAttack - exit - end; - - x:= hwRound(Gear^.X); - y:= hwRound(Gear^.Y); - if (Gear^.Timer mod 33) = 0 then - begin - HHGear^.State := HHGear^.State or gstNoDamage; - doMakeExplosion(x, y + 7, 6, Gear^.Hedgehog, EXPLDontDraw); - HHGear^.State := HHGear^.State and (not gstNoDamage) - end; - - if (Gear^.Timer mod 47) = 0 then - begin - // ok. this was an attempt to turn off dust if not actually drilling land. I have no idea why it isn't working as expected - if (( (y + 12) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y + 12, x] > 255) then - for i:= 0 to 1 do - AddVisualGear(x - 5 + Random(10), y + 12, vgtDust); - - i := x - Gear^.Radius - LongInt(GetRandom(2)); - ei := x + Gear^.Radius + LongInt(GetRandom(2)); - while i <= ei do - begin - DrawExplosion(i, y + 3, 3); - inc(i, 1) - end; - - if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9), lfIndestructible) then - begin - Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + _1_9; - end; - SetAllHHToActive; - end; - if TestCollisionYwithGear(Gear, 1) <> 0 then - begin - Gear^.dY := _0; - SetLittle(HHGear^.dX); - HHGear^.dY := _0; - end - else - begin - if CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y + Gear^.dY + cGravity), lfLandMask) then - begin - Gear^.dY := Gear^.dY + cGravity; - Gear^.Y := Gear^.Y + Gear^.dY - end; - if hwRound(Gear^.Y) > cWaterLine then - Gear^.Timer := 1 - end; - - Gear^.X := Gear^.X + HHGear^.dX; - if CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y)-cHHRadius, lfLandMask) then - begin - HHGear^.X := Gear^.X; - HHGear^.Y := Gear^.Y - int2hwFloat(cHHRadius) - end; - - if (Gear^.Message and gmAttack) <> 0 then - if (Gear^.State and gsttmpFlag) <> 0 then - Gear^.Timer := 1 - else //there would be a mistake. - else - if (Gear^.State and gsttmpFlag) = 0 then - Gear^.State := Gear^.State or gsttmpFlag; - if ((Gear^.Message and gmLeft) <> 0) then - Gear^.dX := - _0_3 - else - if ((Gear^.Message and gmRight) <> 0) then - Gear^.dX := _0_3 - else Gear^.dX := _0; -end; - -procedure doStepPickHammer(Gear: PGear); -var - i, y: LongInt; - ar: TRangeArray; - HHGear: PGear; -begin - i := 0; - HHGear := Gear^.Hedgehog^.Gear; - - y := hwRound(Gear^.Y) - cHHRadius * 2; - while y < hwRound(Gear^.Y) do - begin - ar[i].Left := hwRound(Gear^.X) - Gear^.Radius - LongInt(GetRandom(2)); - ar[i].Right := hwRound(Gear^.X) + Gear^.Radius + LongInt(GetRandom(2)); - inc(y, 2); - inc(i) - end; - - DrawHLinesExplosions(@ar, 3, hwRound(Gear^.Y) - cHHRadius * 2, 2, Pred(i)); - Gear^.dY := HHGear^.dY; - DeleteCI(HHGear); - - Gear^.SoundChannel := LoopSound(sndPickhammer); - doStepPickHammerWork(Gear); - Gear^.doStep := @doStepPickHammerWork -end; - -//////////////////////////////////////////////////////////////////////////////// -var - BTPrevAngle, BTSteps: LongInt; - -procedure doStepBlowTorchWork(Gear: PGear); -var - HHGear: PGear; - b: boolean; - prevX: LongInt; -begin - AllInactive := false; - dec(Gear^.Timer); - if ((GameFlags and gfInfAttack) <> 0) and (TurnTimeLeft > 0) then - dec(TurnTimeLeft); - - HHGear := Gear^.Hedgehog^.Gear; - - HedgehogChAngle(HHGear); - - b := false; - - if abs(LongInt(HHGear^.Angle) - BTPrevAngle) > 7 then - begin - Gear^.dX := SignAs(AngleSin(HHGear^.Angle) * _0_5, Gear^.dX); - Gear^.dY := AngleCos(HHGear^.Angle) * ( - _0_5); - BTPrevAngle := HHGear^.Angle; - b := true - end; - - if ((HHGear^.State and gstMoving) <> 0) then - begin - doStepHedgehogMoving(HHGear); - if (HHGear^.State and gstHHDriven) = 0 then - Gear^.Timer := 0 - end; - - if Gear^.Timer mod cHHStepTicks = 0 then - begin - b := true; - if Gear^.dX.isNegative then - HHGear^.Message := (HHGear^.Message and (gmAttack or gmUp or gmDown)) or gmLeft - else - HHGear^.Message := (HHGear^.Message and (gmAttack or gmUp or gmDown)) or gmRight; - - if ((HHGear^.State and gstMoving) = 0) then - begin - HHGear^.State := HHGear^.State and (not gstAttacking); - prevX := hwRound(HHGear^.X); - - // why the call to HedgehogStep then a further increment of X? - if (prevX = hwRound(HHGear^.X)) and - CheckLandValue(hwRound(HHGear^.X + SignAs(_6, HHGear^.dX)), hwRound(HHGear^.Y), - lfIndestructible) then HedgehogStep(HHGear); - - if (prevX = hwRound(HHGear^.X)) and - CheckLandValue(hwRound(HHGear^.X + SignAs(_6, HHGear^.dX)), hwRound(HHGear^.Y), - lfIndestructible) then HHGear^.X := HHGear^.X + SignAs(_1, HHGear^.dX); - HHGear^.State := HHGear^.State or gstAttacking - end; - - inc(BTSteps); - if BTSteps = 7 then - begin - BTSteps := 0; - if CheckLandValue(hwRound(HHGear^.X + Gear^.dX * (cHHRadius + cBlowTorchC) + SignAs(_6,Gear^.dX)), hwRound(HHGear^.Y + Gear^.dY * (cHHRadius + cBlowTorchC)),lfIndestructible) then - begin - Gear^.X := HHGear^.X + Gear^.dX * (cHHRadius + cBlowTorchC); - Gear^.Y := HHGear^.Y + Gear^.dY * (cHHRadius + cBlowTorchC); - end; - HHGear^.State := HHGear^.State or gstNoDamage; - AmmoShove(Gear, 2, 15); - HHGear^.State := HHGear^.State and (not gstNoDamage) - end; - end; - - if b then - begin - DrawTunnel(HHGear^.X + Gear^.dX * cHHRadius, - HHGear^.Y + Gear^.dY * cHHRadius - _1 - - ((hwAbs(Gear^.dX) / (hwAbs(Gear^.dX) + hwAbs(Gear^.dY))) * _0_5 * 7), - Gear^.dX, Gear^.dY, - cHHStepTicks, cHHRadius * 2 + 7); - end; - - if (TurnTimeLeft = 0) or (Gear^.Timer = 0) - or ((HHGear^.Message and gmAttack) <> 0) then - begin - HHGear^.Message := 0; - HHGear^.State := HHGear^.State and (not gstNotKickable); - DeleteGear(Gear); - AfterAttack - end -end; - -procedure doStepBlowTorch(Gear: PGear); -var - HHGear: PGear; -begin - BTPrevAngle := High(LongInt); - BTSteps := 0; - HHGear := Gear^.Hedgehog^.Gear; - HedgehogChAngle(HHGear); - Gear^.dX := SignAs(AngleSin(HHGear^.Angle) * _0_5, Gear^.dX); - Gear^.dY := AngleCos(HHGear^.Angle) * ( - _0_5); - DrawTunnel(HHGear^.X, - HHGear^.Y + Gear^.dY * cHHRadius - _1 - - ((hwAbs(Gear^.dX) / (hwAbs(Gear^.dX) + hwAbs(Gear^.dY))) * _0_5 * 7), - Gear^.dX, Gear^.dY, - cHHStepTicks, cHHRadius * 2 + 7); - HHGear^.Message := 0; - HHGear^.State := HHGear^.State or gstNotKickable; - Gear^.doStep := @doStepBlowTorchWork -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepMine(Gear: PGear); -var vg: PVisualGear; - dxdy: hwFloat; -begin - if Gear^.Health = 0 then dxdy:= hwAbs(Gear^.dX)+hwAbs(Gear^.dY); - if (Gear^.State and gstMoving) <> 0 then - begin - DeleteCI(Gear); - doStepFallingGear(Gear); - if (Gear^.State and gstMoving) = 0 then - begin - AddGearCI(Gear); - Gear^.dX := _0; - Gear^.dY := _0 - end; - CalcRotationDirAngle(Gear); - AllInactive := false - end - else if (GameTicks and $3F) = 25 then - doStepFallingGear(Gear); - if (Gear^.Health = 0) then - begin - if (dxdy > _0_4) and (Gear^.State and gstCollision <> 0) then - inc(Gear^.Damage, hwRound(dxdy * _50)); - - if ((GameTicks and $FF) = 0) and (Gear^.Damage > random(30)) then - begin - vg:= AddVisualGear(hwRound(Gear^.X) - 4 + Random(8), hwRound(Gear^.Y) - 4 - Random(4), vgtSmoke); - if vg <> nil then - vg^.Scale:= 0.5 - end; - - if (Gear^.Damage > 35) then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - exit - end - end; - - if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Health <> 0) then - if ((Gear^.State and gstAttacking) = 0) then - begin - if ((GameTicks and $1F) = 0) then - if CheckGearNear(Gear, gtHedgehog, 46, 32) <> nil then - Gear^.State := Gear^.State or gstAttacking - end - else // gstAttacking <> 0 - begin - AllInactive := false; - if (Gear^.Timer and $FF) = 0 then - PlaySound(sndMineTick); - if Gear^.Timer = 0 then - begin - if ((Gear^.State and gstWait) <> 0) - or (cMineDudPercent = 0) - or (getRandom(100) > cMineDudPercent) then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear) - end - else - begin - vg:= AddVisualGear(hwRound(Gear^.X) - 4 + Random(8), hwRound(Gear^.Y) - 4 - Random(4), vgtSmoke); - if vg <> nil then - vg^.Scale:= 0.5; - PlaySound(sndVaporize); - Gear^.Health := 0; - Gear^.Damage := 0; - Gear^.State := Gear^.State and (not gstAttacking) - end; - exit - end; - dec(Gear^.Timer); - end - else // gsttmpFlag = 0 - if (TurnTimeLeft = 0) - or ((GameFlags and gfInfAttack <> 0) and (GameTicks > Gear^.FlightTime)) - or (Gear^.Hedgehog^.Gear = nil) then - Gear^.State := Gear^.State or gsttmpFlag; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSMine(Gear: PGear); -begin - // TODO: do real calculation? - if TestCollisionXwithGear(Gear, 2) - or (TestCollisionYwithGear(Gear, -2) <> 0) - or TestCollisionXwithGear(Gear, -2) - or (TestCollisionYwithGear(Gear, 2) <> 0) then - begin - if (not isZero(Gear^.dX)) or (not isZero(Gear^.dY)) then - begin - PlaySound(sndRopeAttach); - Gear^.dX:= _0; - Gear^.dY:= _0; - AddGearCI(Gear); - end; - end - else - begin - DeleteCI(Gear); - doStepFallingGear(Gear); - AllInactive := false; - CalcRotationDirAngle(Gear); - end; - - if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Health <> 0) then - begin - if ((Gear^.State and gstAttacking) = 0) then - begin - if ((GameTicks and $1F) = 0) then - if CheckGearNear(Gear, gtHedgehog, 46, 32) <> nil then - Gear^.State := Gear^.State or gstAttacking - end - else // gstAttacking <> 0 - begin - AllInactive := false; - if Gear^.Timer = 0 then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - exit - end else - if (Gear^.Timer and $FF) = 0 then - PlaySound(sndMineTick); - - dec(Gear^.Timer); - end - end - else // gsttmpFlag = 0 - if (TurnTimeLeft = 0) - or ((GameFlags and gfInfAttack <> 0) and (GameTicks > Gear^.FlightTime)) - or (Gear^.Hedgehog^.Gear = nil) then - Gear^.State := Gear^.State or gsttmpFlag; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepDynamite(Gear: PGear); -begin - doStepFallingGear(Gear); - AllInactive := false; - if Gear^.Timer mod 166 = 0 then - inc(Gear^.Tag); - if Gear^.Timer = 1000 then // might need better timing - makeHogsWorry(Gear^.X, Gear^.Y, 75); - if Gear^.Timer = 0 then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 75, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - exit - end; - dec(Gear^.Timer); -end; - -/////////////////////////////////////////////////////////////////////////////// - -procedure doStepRollingBarrel(Gear: PGear); -var - i: LongInt; - particle: PVisualGear; - dxdy: hwFloat; -begin - if (Gear^.dY.QWordValue = 0) and (Gear^.dY.QWordValue = 0) and (TestCollisionYwithGear(Gear, 1) = 0) then - SetLittle(Gear^.dY); - Gear^.State := Gear^.State or gstAnimation; - if Gear^.Health < cBarrelHealth then Gear^.State:= Gear^.State and not gstFrozen; - - if ((Gear^.dX.QWordValue <> 0) - or (Gear^.dY.QWordValue <> 0)) then - begin - DeleteCI(Gear); - AllInactive := false; - dxdy:= hwAbs(Gear^.dX)+hwAbs(Gear^.dY); - doStepFallingGear(Gear); - if (Gear^.State and gstCollision <> 0) and(dxdy > _0_4) then - begin - if (TestCollisionYwithGear(Gear, 1) <> 0) then - begin - Gear^.State := Gear^.State or gsttmpFlag; - for i:= min(12, hwRound(dxdy*_10)) downto 0 do - begin - particle := AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12,vgtDust); - if particle <> nil then - particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480) - end - end; - inc(Gear^.Damage, hwRound(dxdy * _50)) - end; - CalcRotationDirAngle(Gear); - //CheckGearDrowning(Gear) - end - else - begin - Gear^.State := Gear^.State or gsttmpFlag; - AddGearCI(Gear) - end; - -(* -Attempt to make a barrel knock itself over an edge. Would need more checks to avoid issues like burn damage - begin - x:= hwRound(Gear^.X); - y:= hwRound(Gear^.Y); - if (((y+1) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) then - if (Land[y+1, x] = 0) then - begin - if (((y+1) and LAND_HEIGHT_MASK) = 0) and (((x+Gear^.Radius-2) and LAND_WIDTH_MASK) = 0) and (Land[y+1, x+Gear^.Radius-2] = 0) then - Gear^.dX:= -_0_08 - else if (((y+1 and LAND_HEIGHT_MASK)) = 0) and (((x-(Gear^.Radius-2)) and LAND_WIDTH_MASK) = 0) and (Land[y+1, x-(Gear^.Radius-2)] = 0) then - Gear^.dX:= _0_08; - end; - if Gear^.dX.QWordValue = 0 then AddGearCI(Gear) - end; *) - - if not Gear^.dY.isNegative and (Gear^.dY < _0_001) and (TestCollisionYwithGear(Gear, 1) <> 0) then - Gear^.dY := _0; - if hwAbs(Gear^.dX) < _0_001 then - Gear^.dX := _0; - - if (Gear^.Health > 0) and ((Gear^.Health * 100 div cBarrelHealth) < random(90)) and ((GameTicks and $FF) = 0) then - if (cBarrelHealth div Gear^.Health) > 2 then - AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmoke) - else - AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmokeWhite); - dec(Gear^.Health, Gear^.Damage); - Gear^.Damage := 0; - if Gear^.Health <= 0 then - doStepCase(Gear); -end; - -procedure doStepCase(Gear: PGear); -var - i, x, y: LongInt; - k: TGearType; - dX, dY: HWFloat; - hog: PHedgehog; - sparkles: PVisualGear; - gi: PGear; -begin - k := Gear^.Kind; - - if (Gear^.Message and gmDestroy) > 0 then - begin - DeleteGear(Gear); - FreeActionsList; - SetAllToActive; - // something (hh, mine, etc...) could be on top of the case - with CurrentHedgehog^ do - if Gear <> nil then - Gear^.Message := Gear^.Message and (not (gmLJump or gmHJump)); - exit - end; - if (k = gtExplosives) and (Gear^.Health < cBarrelHealth) then Gear^.State:= Gear^.State and not gstFrozen; - - if ((k <> gtExplosives) and (Gear^.Damage > 0)) or ((k = gtExplosives) and (Gear^.Health<=0)) then - begin - x := hwRound(Gear^.X); - y := hwRound(Gear^.Y); - hog:= Gear^.Hedgehog; - - DeleteGear(Gear); - // <-- delete gear! - - if k = gtCase then - begin - doMakeExplosion(x, y, 25, hog, EXPLAutoSound); - for i:= 0 to 63 do - AddGear(x, y, gtFlame, 0, _0, _0, 0); - end - else if k = gtExplosives then - begin - doMakeExplosion(x, y, 75, hog, EXPLAutoSound); - for i:= 0 to 31 do - begin - dX := AngleCos(i * 64) * _0_5 * (getrandomf + _1); - dY := AngleSin(i * 64) * _0_5 * (getrandomf + _1); - AddGear(x, y, gtFlame, 0, dX, dY, 0); - AddGear(x, y, gtFlame, gstTmpFlag, -dX, -dY, 0); - end - end; - exit - end; - - if k = gtExplosives then - begin - //if V > _0_03 then Gear^.State:= Gear^.State or gstAnimation; - if (hwAbs(Gear^.dX) > _0_15) or ((hwAbs(Gear^.dY) > _0_15) and (hwAbs(Gear^.dX) > _0_02)) then - begin - Gear^.doStep := @doStepRollingBarrel; - exit; - end - else Gear^.dX:= _0; - - if ((Gear^.Health * 100 div cBarrelHealth) < random(90)) and ((GameTicks and $FF) = 0) then - if (cBarrelHealth div Gear^.Health) > 2 then - AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmoke) - else - AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmokeWhite); - dec(Gear^.Health, Gear^.Damage); - Gear^.Damage := 0; - end - else - begin - if (Gear^.Pos <> posCaseHealth) and (GameTicks and $1FFF = 0) then // stir 'em up periodically - begin - gi := GearsList; - while gi <> nil do - begin - if gi^.Kind = gtGenericFaller then - begin - gi^.Active:= true; - gi^.X:= int2hwFloat(GetRandom(rightX-leftX)+leftX); - gi^.Y:= int2hwFloat(GetRandom(LAND_HEIGHT-topY)+topY); - gi^.dX:= _90-(GetRandomf*_360); - gi^.dY:= _90-(GetRandomf*_360) - end; - gi := gi^.NextGear - end - end; - - if Gear^.Timer = 500 then - begin -(* Can't make sparkles team coloured without working out what the next team is going to be. This should be solved, really, since it also screws up - voices. Reinforcements voices is heard for active team, not team-to-be. Either that or change crate spawn from end of turn to start, although that - has its own complexities. *) - // Abuse a couple of gear values to track origin - Gear^.Angle:= hwRound(Gear^.Y); - Gear^.Tag:= random(2); - inc(Gear^.Timer) - end; - if Gear^.Timer < 1833 then inc(Gear^.Timer); - if Gear^.Timer = 1000 then - begin - sparkles:= AddVisualGear(hwRound(Gear^.X), Gear^.Angle, vgtDust, 1); - if sparkles <> nil then - begin - sparkles^.dX:= 0; - sparkles^.dY:= 0; - sparkles^.Angle:= 270; - if Gear^.Tag = 1 then - sparkles^.Tint:= $3744D7FF - else sparkles^.Tint:= $FAB22CFF - end; - end; - if Gear^.Timer < 1000 then - begin - AllInactive:= false; - exit - end - end; - - - if (Gear^.dY.QWordValue <> 0) - or (TestCollisionYwithGear(Gear, 1) = 0) then - begin - AllInactive := false; - - Gear^.dY := Gear^.dY + cGravity; - - if (Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, -1) <> 0) then - Gear^.dY := _0; - - Gear^.Y := Gear^.Y + Gear^.dY; - - if (not Gear^.dY.isNegative) and (Gear^.dY > _0_001) then - SetAllHHToActive(false); - - if (not Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, 1) <> 0) then - begin - if (Gear^.dY > _0_2) and (k = gtExplosives) then - inc(Gear^.Damage, hwRound(Gear^.dY * _70)); - - if Gear^.dY > _0_2 then - for i:= min(12, hwRound(Gear^.dY*_10)) downto 0 do - AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust); - - Gear^.dY := - Gear^.dY * Gear^.Elasticity; - if Gear^.dY > - _0_001 then - Gear^.dY := _0 - else if Gear^.dY < - _0_03 then - PlaySound(Gear^.ImpactSound); - end; - //if Gear^.dY > - _0_001 then Gear^.dY:= _0 - CheckGearDrowning(Gear); - end; - - if (Gear^.dY.QWordValue = 0) then - AddGearCI(Gear) - else if (Gear^.dY.QWordValue <> 0) then - DeleteCI(Gear) -end; - -//////////////////////////////////////////////////////////////////////////////// - -procedure doStepTarget(Gear: PGear); -begin - if (Gear^.Timer = 0) and (Gear^.Tag = 0) then - PlaySound(sndWarp); - - if (Gear^.Tag = 0) and (Gear^.Timer < 1000) then - inc(Gear^.Timer) - else if Gear^.Tag = 1 then - Gear^.Tag := 2 - else if Gear^.Tag = 2 then - if Gear^.Timer > 0 then - dec(Gear^.Timer) - else - begin - DeleteGear(Gear); - exit; - end; - - doStepCase(Gear) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepIdle(Gear: PGear); -begin - AllInactive := false; - dec(Gear^.Timer); - if Gear^.Timer = 0 then - begin - DeleteGear(Gear); - AfterAttack - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepShover(Gear: PGear); -var - HHGear: PGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - HHGear^.State := HHGear^.State or gstNoDamage; - DeleteCI(HHGear); - - AmmoShove(Gear, 30, 115); - - HHGear^.State := (HHGear^.State and (not gstNoDamage)) or gstMoving; - Gear^.Timer := 250; - Gear^.doStep := @doStepIdle -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepWhip(Gear: PGear); -var - HHGear: PGear; - i: LongInt; -begin - HHGear := Gear^.Hedgehog^.Gear; - HHGear^.State := HHGear^.State or gstNoDamage; - DeleteCI(HHGear); - - for i:= 0 to 3 do - begin - AmmoShove(Gear, 30, 25); - Gear^.X := Gear^.X + Gear^.dX * 5 - end; - - HHGear^.State := (HHGear^.State and (not gstNoDamage)) or gstMoving; - - Gear^.Timer := 250; - Gear^.doStep := @doStepIdle -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepFlame(Gear: PGear); -var - gX,gY,i: LongInt; - sticky: Boolean; - vgt: PVisualGear; - tdX,tdY: HWFloat; -begin - sticky:= (Gear^.State and gsttmpFlag) <> 0; - if not sticky then AllInactive := false; - - if TestCollisionYwithGear(Gear, 1) = 0 then - begin - AllInactive := false; - - if ((GameTicks mod 100) = 0) then - begin - vgt:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFire, gstTmpFlag); - if vgt <> nil then - begin - vgt^.dx:= 0; - vgt^.dy:= 0; - vgt^.FrameTicks:= 1800 div (Gear^.Tag mod 3 + 2); - end; - end; - - - if Gear^.dX.QWordValue > _0_01.QWordValue then - Gear^.dX := Gear^.dX * _0_995; - - Gear^.dY := Gear^.dY + cGravity; - // if sticky then Gear^.dY := Gear^.dY + cGravity; - - if Gear^.dY.QWordValue > _0_2.QWordValue then - Gear^.dY := Gear^.dY * _0_995; - - //if sticky then Gear^.X := Gear^.X + Gear^.dX else - Gear^.X := Gear^.X + Gear^.dX + cWindSpeed * 640; - Gear^.Y := Gear^.Y + Gear^.dY; - - if (hwRound(Gear^.Y) > cWaterLine) then - begin - gX := hwRound(Gear^.X); - for i:= 0 to 3 do - AddVisualGear(gX - 16 + Random(32), cWaterLine - 16 + Random(16), vgtSteam); - PlaySound(sndVaporize); - DeleteGear(Gear); - exit - end - end - else - begin - if sticky and (GameTicks and $F = 0) then - begin - Gear^.Radius := 7; - tdX:= Gear^.dX; - tdY:= Gear^.dY; - Gear^.dX.QWordValue:= 120000000; - Gear^.dY.QWordValue:= 429496730; - Gear^.dX.isNegative:= getrandom(2)<>1; - Gear^.dY.isNegative:= true; - AmmoShove(Gear, 2, 125); - Gear^.dX:= tdX; - Gear^.dY:= tdY; - Gear^.Radius := 1 - end; - if Gear^.Timer > 0 then - begin - dec(Gear^.Timer); - inc(Gear^.Damage) - end - else - begin - gX := hwRound(Gear^.X); - gY := hwRound(Gear^.Y); - // Standard fire - if not sticky then - begin - if ((GameTicks and $1) = 0) then - begin - Gear^.Radius := 7; - tdX:= Gear^.dX; - tdY:= Gear^.dY; - Gear^.dX.QWordValue:= 214748365; - Gear^.dY.QWordValue:= 429496730; - Gear^.dX.isNegative:= getrandom(2)<>1; - Gear^.dY.isNegative:= true; - AmmoShove(Gear, 6, 100); - Gear^.dX:= tdX; - Gear^.dY:= tdY; - Gear^.Radius := 1; - end - else if ((GameTicks and $3) = 3) then - doMakeExplosion(gX, gY, 8, Gear^.Hedgehog, 0);//, EXPLNoDamage); - //DrawExplosion(gX, gY, 4); - - if ((GameTicks and $7) = 0) and (Random(2) = 0) then - for i:= Random(2) downto 0 do - AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke); - - if Gear^.Health > 0 then - dec(Gear^.Health); - Gear^.Timer := 450 - Gear^.Tag * 8 - end - else - begin - // Modified fire - if ((GameTicks and $7FF) = 0) and ((GameFlags and gfSolidLand) = 0) then - begin - DrawExplosion(gX, gY, 4); - - for i:= Random(3) downto 0 do - AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke); - end; - -// This one is interesting. I think I understand the purpose, but I wonder if a bit more fuzzy of kicking could be done with getrandom. - Gear^.Timer := 100 - Gear^.Tag * 3; - if (Gear^.Damage > 3000+Gear^.Tag*1500) then - Gear^.Health := 0 - end - end - end; - if Gear^.Health = 0 then - begin - gX := hwRound(Gear^.X); - gY := hwRound(Gear^.Y); - if not sticky then - begin - if ((GameTicks and $3) = 0) and (Random(1) = 0) then - for i:= Random(2) downto 0 do - AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke); - end - else - for i:= Random(3) downto 0 do - AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke); - - DeleteGear(Gear) - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepFirePunchWork(Gear: PGear); -var - HHGear: PGear; -begin - AllInactive := false; - if ((Gear^.Message and gmDestroy) <> 0) then - begin - DeleteGear(Gear); - AfterAttack; - exit - end; - - HHGear := Gear^.Hedgehog^.Gear; - if hwRound(HHGear^.Y) <= Gear^.Tag - 2 then - begin - Gear^.Tag := hwRound(HHGear^.Y); - DrawTunnel(HHGear^.X - int2hwFloat(cHHRadius), HHGear^.Y - _1, _0_5, _0, cHHRadius * 4, 2); - HHGear^.State := HHGear^.State or gstNoDamage; - Gear^.Y := HHGear^.Y; - AmmoShove(Gear, 30, 40); - HHGear^.State := HHGear^.State and (not gstNoDamage) - end; - - HHGear^.dY := HHGear^.dY + cGravity; - if not (HHGear^.dY.isNegative) then - begin - HHGear^.State := HHGear^.State or gstMoving; - DeleteGear(Gear); - AfterAttack; - exit - end; - - if CheckLandValue(hwRound(HHGear^.X), hwRound(HHGear^.Y + HHGear^.dY + SignAs(_6,Gear^.dY)), - lfIndestructible) then - HHGear^.Y := HHGear^.Y + HHGear^.dY -end; - -procedure doStepFirePunch(Gear: PGear); -var - HHGear: PGear; -begin - AllInactive := false; - HHGear := Gear^.Hedgehog^.Gear; - DeleteCI(HHGear); - //HHGear^.X := int2hwFloat(hwRound(HHGear^.X)) - _0_5; WTF? - HHGear^.dX := SignAs(cLittle, Gear^.dX); - - HHGear^.dY := - _0_3; - - Gear^.X := HHGear^.X; - Gear^.dX := SignAs(_0_45, Gear^.dX); - Gear^.dY := - _0_9; - Gear^.doStep := @doStepFirePunchWork; - DrawTunnel(HHGear^.X - int2hwFloat(cHHRadius), HHGear^.Y + _1, _0_5, _0, cHHRadius * 4, 5); - - PlaySoundV(TSound(ord(sndFirePunch1) + GetRandom(6)), HHGear^.Hedgehog^.Team^.voicepack) -end; - -//////////////////////////////////////////////////////////////////////////////// - -procedure doStepParachuteWork(Gear: PGear); -var - HHGear: PGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - - inc(Gear^.Timer); - - if (TestCollisionYwithGear(HHGear, 1) <> 0) - or ((HHGear^.State and gstHHDriven) = 0) - or CheckGearDrowning(HHGear) - or ((Gear^.Message and gmAttack) <> 0) then - begin - with HHGear^ do - begin - Message := 0; - SetLittle(dX); - dY := _0; - State := State or gstMoving; - end; - DeleteGear(Gear); - isCursorVisible := false; - ApplyAmmoChanges(HHGear^.Hedgehog^); - exit - end; - - HHGear^.X := HHGear^.X + cWindSpeed * 200; - - if (Gear^.Message and gmLeft) <> 0 then - HHGear^.X := HHGear^.X - cMaxWindSpeed * 80 - - else if (Gear^.Message and gmRight) <> 0 then - HHGear^.X := HHGear^.X + cMaxWindSpeed * 80; - - if (Gear^.Message and gmUp) <> 0 then - HHGear^.Y := HHGear^.Y - cGravity * 40 - - else if (Gear^.Message and gmDown) <> 0 then - HHGear^.Y := HHGear^.Y + cGravity * 40; - - // don't drift into obstacles - if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then - HHGear^.X := HHGear^.X - int2hwFloat(hwSign(HHGear^.dX)); - HHGear^.Y := HHGear^.Y + cGravity * 100; - Gear^.X := HHGear^.X; - Gear^.Y := HHGear^.Y -end; - -procedure doStepParachute(Gear: PGear); -var - HHGear: PGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - - DeleteCI(HHGear); - - AfterAttack; - - HHGear^.State := HHGear^.State and (not (gstAttacking or gstAttacked or gstMoving)); - HHGear^.Message := HHGear^.Message and (not gmAttack); - - Gear^.doStep := @doStepParachuteWork; - - Gear^.Message := HHGear^.Message; - doStepParachuteWork(Gear) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepAirAttackWork(Gear: PGear); -begin - AllInactive := false; - Gear^.X := Gear^.X + cAirPlaneSpeed * Gear^.Tag; - - if (Gear^.Health > 0)and(not (Gear^.X < Gear^.dX))and(Gear^.X < Gear^.dX + cAirPlaneSpeed) then - begin - dec(Gear^.Health); - case Gear^.State of - 0: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAirBomb, 0, cBombsSpeed * Gear^.Tag, _0, 0); - 1: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtMine, 0, cBombsSpeed * Gear^.Tag, _0, 0); - 2: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtNapalmBomb, 0, cBombsSpeed * Gear^.Tag, _0, 0); - 3: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtDrill, gsttmpFlag, cBombsSpeed * Gear^.Tag, _0, Gear^.Timer + 1); - //4: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtWaterMelon, 0, cBombsSpeed * - // Gear^.Tag, _0, 5000); - end; - Gear^.dX := Gear^.dX + int2hwFloat(30 * Gear^.Tag); - StopSoundChan(Gear^.SoundChannel, 4000); - end; - - if (GameTicks and $3F) = 0 then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace); - - if (hwRound(Gear^.X) > (max(LAND_WIDTH,4096)+2048)) or (hwRound(Gear^.X) < -2048) then - begin - // avoid to play forever (is this necessary?) - StopSoundChan(Gear^.SoundChannel); - DeleteGear(Gear) - end; -end; - -procedure doStepAirAttack(Gear: PGear); -begin - AllInactive := false; - - if Gear^.X.QWordValue = 0 then - begin - Gear^.Tag := 1; - Gear^.X := -_2048; - end - else - begin - Gear^.Tag := -1; - Gear^.X := int2hwFloat(max(LAND_WIDTH,4096) + 2048); - end; - - Gear^.Y := int2hwFloat(topY-300); - Gear^.dX := int2hwFloat(Gear^.Target.X - 5 * Gear^.Tag * 15); - - // calcs for Napalm Strike, so that it will hit the target (without wind at least :P) - if (Gear^.State = 2) then - Gear^.dX := Gear^.dX - cBombsSpeed * Gear^.Tag * 900 - // calcs for regular falling gears - else if (int2hwFloat(Gear^.Target.Y) - Gear^.Y > _0) then - Gear^.dX := Gear^.dX - cBombsSpeed * hwSqrt((int2hwFloat(Gear^.Target.Y) - Gear^.Y) * 2 / - cGravity) * Gear^.Tag; - - Gear^.Health := 6; - Gear^.doStep := @doStepAirAttackWork; - Gear^.SoundChannel := LoopSound(sndPlane, 4000); - -end; - -//////////////////////////////////////////////////////////////////////////////// - -procedure doStepAirBomb(Gear: PGear); -begin - AllInactive := false; - doStepFallingGear(Gear); - if (Gear^.State and gstCollision) <> 0 then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - with mobileRecord do - if (performRumble <> nil) and (not fastUntilLag) then - performRumble(kSystemSoundID_Vibrate); - exit - end; - if (GameTicks and $3F) = 0 then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) -end; - -//////////////////////////////////////////////////////////////////////////////// - -procedure doStepGirder(Gear: PGear); -var - HHGear: PGear; - x, y, tx, ty: hwFloat; -begin - AllInactive := false; - - HHGear := Gear^.Hedgehog^.Gear; - tx := int2hwFloat(Gear^.Target.X); - ty := int2hwFloat(Gear^.Target.Y); - x := HHGear^.X; - y := HHGear^.Y; - - if (Distance(tx - x, ty - y) > _256) - or (not TryPlaceOnLand(Gear^.Target.X - SpritesData[sprAmGirder].Width div 2, Gear^.Target.Y - SpritesData[sprAmGirder].Height div 2, sprAmGirder, Gear^.State, true, false)) then - begin - PlaySound(sndDenied); - HHGear^.Message := HHGear^.Message and (not gmAttack); - HHGear^.State := HHGear^.State and (not gstAttacking); - HHGear^.State := HHGear^.State or gstHHChooseTarget; - isCursorVisible := true; - DeleteGear(Gear) - end - else - begin - PlaySound(sndPlaced); - DeleteGear(Gear); - AfterAttack; - end; - - HHGear^.State := HHGear^.State and (not (gstAttacking or gstAttacked)); - HHGear^.Message := HHGear^.Message and (not gmAttack); -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepTeleportAfter(Gear: PGear); -var - HHGear: PGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - doStepHedgehogMoving(HHGear); - // if not infattack mode wait for hedgehog finish falling to collect cases - if ((GameFlags and gfInfAttack) <> 0) - or ((HHGear^.State and gstMoving) = 0) - or (Gear^.Hedgehog^.Gear^.Damage > 0) - or ((HHGear^.State and gstDrowning) = 1) then - begin - DeleteGear(Gear); - AfterAttack - end -end; - -procedure doStepTeleportAnim(Gear: PGear); -begin - if (Gear^.Hedgehog^.Gear^.Damage > 0) then - begin - DeleteGear(Gear); - AfterAttack; - end; - inc(Gear^.Timer); - if Gear^.Timer = 65 then - begin - Gear^.Timer := 0; - inc(Gear^.Pos); - if Gear^.Pos = 11 then - Gear^.doStep := @doStepTeleportAfter - end; -end; - -procedure doStepTeleport(Gear: PGear); -var - HHGear: PGear; -begin - AllInactive := false; - - HHGear := Gear^.Hedgehog^.Gear; - if not TryPlaceOnLand(Gear^.Target.X - SpritesData[sprHHTelepMask].Width div 2, - Gear^.Target.Y - SpritesData[sprHHTelepMask].Height div 2, - sprHHTelepMask, 0, false, false) then - begin - HHGear^.Message := HHGear^.Message and (not gmAttack); - HHGear^.State := HHGear^.State and (not gstAttacking); - HHGear^.State := HHGear^.State or gstHHChooseTarget; - DeleteGear(Gear); - isCursorVisible := true; - PlaySound(sndDenied) - end - else - begin - DeleteCI(HHGear); - SetAllHHToActive; - Gear^.doStep := @doStepTeleportAnim; - - // copy old HH position and direction to Gear (because we need them for drawing the vanishing hog) - Gear^.dX := HHGear^.dX; - // retrieve the cursor direction (it was previously copied to X so it doesn't get lost) - HHGear^.dX.isNegative := (Gear^.X.QWordValue <> 0); - Gear^.X := HHGear^.X; - Gear^.Y := HHGear^.Y; - HHGear^.X := int2hwFloat(Gear^.Target.X); - HHGear^.Y := int2hwFloat(Gear^.Target.Y); - HHGear^.State := HHGear^.State or gstMoving; - Gear^.Hedgehog^.Unplaced := false; - isCursorVisible := false; - playSound(sndWarp) - end; - Gear^.Target.X:= NoPointX -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSwitcherWork(Gear: PGear); -var - HHGear: PGear; - hedgehog: PHedgehog; - State: Longword; -begin - AllInactive := false; - - if ((Gear^.Message and (not gmSwitch)) <> 0) or (TurnTimeLeft = 0) then - begin - hedgehog := Gear^.Hedgehog; - //Msg := Gear^.Message and (not gmSwitch); - DeleteGear(Gear); - ApplyAmmoChanges(hedgehog^); - - HHGear := CurrentHedgehog^.Gear; - ApplyAmmoChanges(HHGear^.Hedgehog^); - //HHGear^.Message := Msg; - exit - end; - - if (Gear^.Message and gmSwitch) <> 0 then - begin - HHGear := CurrentHedgehog^.Gear; - HHGear^.Message := HHGear^.Message and (not gmSwitch); - Gear^.Message := Gear^.Message and (not gmSwitch); - State := HHGear^.State; - HHGear^.State := 0; - HHGear^.Z := cHHZ; - HHGear^.Active := false; - HHGear^.Message:= HHGear^.Message or gmRemoveFromList or gmAddToList; - - PlaySound(sndSwitchHog); - - repeat - CurrentTeam^.CurrHedgehog := Succ(CurrentTeam^.CurrHedgehog) mod (CurrentTeam^.HedgehogsNumber); - until (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear <> nil) and - (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear^.Damage = 0) and - (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Effects[heFrozen]=0); - - SwitchCurrentHedgehog(@CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog]); - AmmoMenuInvalidated:= true; - - HHGear := CurrentHedgehog^.Gear; - HHGear^.State := State; - HHGear^.Active := true; - FollowGear := HHGear; - HHGear^.Z := cCurrHHZ; - HHGear^.Message:= HHGear^.Message or gmRemoveFromList or gmAddToList; - Gear^.X := HHGear^.X; - Gear^.Y := HHGear^.Y - end; -end; - -procedure doStepSwitcher(Gear: PGear); -var - HHGear: PGear; -begin - Gear^.doStep := @doStepSwitcherWork; - - HHGear := Gear^.Hedgehog^.Gear; - OnUsedAmmo(HHGear^.Hedgehog^); - with HHGear^ do - begin - State := State and (not gstAttacking); - Message := Message and (not gmAttack) - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepMortar(Gear: PGear); -var - dX, dY, gdX, gdY: hwFloat; - i: LongInt; -begin - AllInactive := false; - gdX := Gear^.dX; - gdY := Gear^.dY; - - doStepFallingGear(Gear); - if (Gear^.State and gstCollision) <> 0 then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLAutoSound); - gdX.isNegative := not gdX.isNegative; - gdY.isNegative := not gdY.isNegative; - gdX:= gdX*_0_2; - gdY:= gdY*_0_2; - - for i:= 0 to 4 do - begin - dX := gdX + rndSign(GetRandomf) * _0_03; - dY := gdY + rndSign(GetRandomf) * _0_03; - AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtCluster, 0, dX, dY, 25); - end; - - DeleteGear(Gear); - exit - end; - - if (GameTicks and $3F) = 0 then - begin - if hwRound(Gear^.Y) > cWaterLine then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble) - else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepKamikazeWork(Gear: PGear); -var - i: LongWord; - HHGear: PGear; - sparkles: PVisualGear; - hasWishes: boolean; -begin - AllInactive := false; - hasWishes:= ((Gear^.Message and (gmPrecise or gmSwitch)) = (gmPrecise or gmSwitch)); - if hasWishes then - Gear^.AdvBounce:= 1; - - HHGear := Gear^.Hedgehog^.Gear; - if HHGear = nil then - begin - DeleteGear(Gear); - exit - end; - - HHGear^.State := HHGear^.State or gstNoDamage; - DeleteCI(HHGear); - - Gear^.X := HHGear^.X; - Gear^.Y := HHGear^.Y; - if (GameTicks mod 2 = 0) and hasWishes then - begin - sparkles:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust, 1); - if sparkles <> nil then - begin - sparkles^.Tint:= ((random(210)+45) shl 24) or ((random(210)+45) shl 16) or ((random(210)+45) shl 8) or $FF; - sparkles^.Angle:= random(360); - end - end; - - i := 2; - repeat - - Gear^.X := Gear^.X + HHGear^.dX; - Gear^.Y := Gear^.Y + HHGear^.dY; - HHGear^.X := Gear^.X; - HHGear^.Y := Gear^.Y; - - inc(Gear^.Damage, 2); - - // if TestCollisionXwithGear(HHGear, hwSign(Gear^.dX)) - // or TestCollisionYwithGear(HHGear, hwSign(Gear^.dY)) then inc(Gear^.Damage, 3); - - dec(i) - until (i = 0) - or (Gear^.Damage > Gear^.Health); - - inc(upd); - if upd > 3 then - begin - if Gear^.Health < 1500 then - begin - if Gear^.AdvBounce <> 0 then - Gear^.Pos := 3 - else - Gear^.Pos := 2; - end; - - AmmoShove(Gear, 30, 40); - - DrawTunnel(HHGear^.X - HHGear^.dX * 10, - HHGear^.Y - _2 - HHGear^.dY * 10 + hwAbs(HHGear^.dY) * 2, - HHGear^.dX, - HHGear^.dY, - 20 + cHHRadius * 2, - cHHRadius * 2 + 7); - - upd := 0 - end; - - if Gear^.Health < Gear^.Damage then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound); - if hasWishes then - for i:= 0 to 31 do - begin - sparkles:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot); - if sparkles <> nil then - with sparkles^ do - begin - Tint:= ((random(210)+45) shl 24) or ((random(210)+45) shl 16) or ((random(210)+45) shl 8) or $FF; - Angle:= random(360); - dx:= 0.001 * (random(200)); - dy:= 0.001 * (random(200)); - if random(2) = 0 then - dx := -dx; - if random(2) = 0 then - dy := -dy; - FrameTicks:= random(400) + 250 - end - end; - AfterAttack; - HHGear^.Message:= HHGear^.Message or gmDestroy; - DeleteGear(Gear); - end - else - begin - dec(Gear^.Health, Gear^.Damage); - Gear^.Damage := 0 - end -end; - -procedure doStepKamikazeIdle(Gear: PGear); -begin - AllInactive := false; - dec(Gear^.Timer); - if Gear^.Timer = 0 then - begin - Gear^.Pos := 1; - PlaySoundV(sndKamikaze, Gear^.Hedgehog^.Team^.voicepack); - Gear^.doStep := @doStepKamikazeWork - end -end; - -procedure doStepKamikaze(Gear: PGear); -var - HHGear: PGear; -begin - AllInactive := false; - - HHGear := Gear^.Hedgehog^.Gear; - - HHGear^.dX := Gear^.dX; - HHGear^.dY := Gear^.dY; - - Gear^.dX := SignAs(_0_45, Gear^.dX); - Gear^.dY := - _0_9; - - Gear^.Timer := 550; - - Gear^.doStep := @doStepKamikazeIdle -end; - -//////////////////////////////////////////////////////////////////////////////// - -const cakeh = 27; -var - CakePoints: array[0..Pred(cakeh)] of record - x, y: hwFloat; - end; - CakeI: Longword; - -procedure doStepCakeExpl(Gear: PGear); -begin - AllInactive := false; - - inc(Gear^.Tag); - if Gear^.Tag < 2250 then - exit; - - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cakeDmg, Gear^.Hedgehog, EXPLAutoSound); - AfterAttack; - DeleteGear(Gear) -end; - -procedure doStepCakeDown(Gear: PGear); -var - gi: PGear; - dmg, dmgBase: LongInt; - fX, fY, tdX, tdY: hwFloat; -begin - AllInactive := false; - - inc(Gear^.Tag); - if Gear^.Tag < 100 then - exit; - Gear^.Tag := 0; - - if Gear^.Pos = 0 then - begin -///////////// adapted from doMakeExplosion /////////////////////////// - //fX:= Gear^.X; - //fY:= Gear^.Y; - //fX.QWordValue:= fX.QWordValue and $FFFFFFFF00000000; - //fY.QWordValue:= fY.QWordValue and $FFFFFFFF00000000; - fX:= int2hwFloat(hwRound(Gear^.X)); - fY:= int2hwFloat(hwRound(Gear^.Y)); - dmgBase:= cakeDmg shl 1 + cHHRadius div 2; - gi := GearsList; - while gi <> nil do - begin - if gi^.Kind = gtHedgehog then - begin - dmg:= 0; - tdX:= gi^.X-fX; - tdY:= gi^.Y-fY; - if hwRound(hwAbs(tdX)+hwAbs(tdY)) < dmgBase then - dmg:= dmgBase - max(hwRound(Distance(tdX, tdY)),gi^.Radius); - if (dmg > 1) then dmg:= ModifyDamage(min(dmg div 2, cakeDmg), gi); - if (dmg > 1) then - if (CurrentHedgehog^.Gear = gi) and (not gi^.Invulnerable) then - gi^.State := gi^.State or gstLoser - else - gi^.State := gi^.State or gstWinner; - end; - gi := gi^.NextGear - end; -////////////////////////////////////////////////////////////////////// - Gear^.doStep := @doStepCakeExpl; - PlaySound(sndCake) - end - else dec(Gear^.Pos) -end; - - -procedure doStepCakeWork(Gear: PGear); -var - tdx, tdy: hwFloat; -begin - AllInactive := false; - - inc(Gear^.Tag); - if Gear^.Tag < 7 then - exit; - - dec(Gear^.Health); - Gear^.Timer := Gear^.Health*10; - if Gear^.Health mod 100 = 0 then - Gear^.PortalCounter:= 0; - // This is not seconds, but at least it is *some* feedback - if (Gear^.Health = 0) or ((Gear^.Message and gmAttack) <> 0) then - begin - FollowGear := Gear; - Gear^.RenderTimer := false; - Gear^.doStep := @doStepCakeDown; - exit - end; - - cakeStep(Gear); - - if Gear^.Tag = 0 then - begin - CakeI := (CakeI + 1) mod cakeh; - tdx := CakePoints[CakeI].x - Gear^.X; - tdy := - CakePoints[CakeI].y + Gear^.Y; - CakePoints[CakeI].x := Gear^.X; - CakePoints[CakeI].y := Gear^.Y; - Gear^.DirAngle := DxDy2Angle(tdx, tdy); - end; -end; - -procedure doStepCakeUp(Gear: PGear); -var - i: Longword; -begin - AllInactive := false; - - inc(Gear^.Tag); - if Gear^.Tag < 100 then - exit; - Gear^.Tag := 0; - - if Gear^.Pos = 6 then - begin - for i:= 0 to Pred(cakeh) do - begin - CakePoints[i].x := Gear^.X; - CakePoints[i].y := Gear^.Y - end; - CakeI := 0; - Gear^.doStep := @doStepCakeWork - end - else - inc(Gear^.Pos) -end; - -procedure doStepCakeFall(Gear: PGear); -begin - AllInactive := false; - - Gear^.dY := Gear^.dY + cGravity; - if TestCollisionYwithGear(Gear, 1) <> 0 then - Gear^.doStep := @doStepCakeUp - else - begin - Gear^.Y := Gear^.Y + Gear^.dY; - if CheckGearDrowning(Gear) then - AfterAttack - end -end; - -procedure doStepCake(Gear: PGear); -var - HHGear: PGear; -begin - AllInactive := false; - - HHGear := Gear^.Hedgehog^.Gear; - HHGear^.Message := HHGear^.Message and (not gmAttack); - Gear^.CollisionMask:= lfNotCurrentMask; - - FollowGear := Gear; - - Gear^.doStep := @doStepCakeFall -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSeductionWork(Gear: PGear); -var i: LongInt; - hogs: PGearArrayS; -begin - AllInactive := false; - hogs := GearsNear(Gear^.X, Gear^.Y, gtHedgehog, Gear^.Radius); - if hogs.size > 0 then - begin - for i:= 0 to hogs.size - 1 do - with hogs.ar^[i]^ do - begin - if hogs.ar^[i] <> CurrentHedgehog^.Gear then - begin - dX:= _50 * cGravity * (Gear^.X - X) / _25; - dY:= -_450 * cGravity; - Active:= true; - end - end; - end ; - AfterAttack; - DeleteGear(Gear); -(* - Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + Gear^.dY; - x := hwRound(Gear^.X); - y := hwRound(Gear^.Y); - - if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) then - if (Land[y, x] <> 0) then - begin - Gear^.dX.isNegative := not Gear^.dX.isNegative; - Gear^.dY.isNegative := not Gear^.dY.isNegative; - Gear^.dX := Gear^.dX * _1_5; - Gear^.dY := Gear^.dY * _1_5 - _0_3; - AmmoShove(Gear, 0, 40); - AfterAttack; - DeleteGear(Gear) - end - else - else - begin - AfterAttack; - DeleteGear(Gear) - end*) -end; - -procedure doStepSeductionWear(Gear: PGear); -var heart: PVisualGear; -begin - AllInactive := false; - inc(Gear^.Timer); - if Gear^.Timer > 250 then - begin - Gear^.Timer := 0; - inc(Gear^.Pos); - if Gear^.Pos = 5 then - PlaySoundV(sndYoohoo, Gear^.Hedgehog^.Team^.voicepack) - end; - - if (Gear^.Pos = 14) and (RealTicks and $3 = 0) then - begin - heart:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot); - if heart <> nil then - with heart^ do - begin - dx:= 0.001 * (random(200)); - dy:= 0.001 * (random(200)); - if random(2) = 0 then - dx := -dx; - if random(2) = 0 then - dy := -dy; - FrameTicks:= random(750) + 1000; - State:= ord(sprSeduction) - end; - end; - - if Gear^.Pos = 15 then - Gear^.doStep := @doStepSeductionWork -end; - -procedure doStepSeduction(Gear: PGear); -begin - AllInactive := false; - //DeleteCI(Gear^.Hedgehog^.Gear); - Gear^.doStep := @doStepSeductionWear -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepWaterUp(Gear: PGear); -var - i: LongWord; -begin - if (Gear^.Tag = 0) - or (cWaterLine = 0) then - begin - DeleteGear(Gear); - exit - end; - - AllInactive := false; - - inc(Gear^.Timer); - if Gear^.Timer = 17 then - Gear^.Timer := 0 - else - exit; - - if cWaterLine > 0 then - begin - dec(cWaterLine); - for i:= 0 to LAND_WIDTH - 1 do - Land[cWaterLine, i] := 0; - SetAllToActive - end; - - dec(Gear^.Tag); -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepDrill(Gear: PGear); -forward; - -procedure doStepDrillDrilling(Gear: PGear); -var - t: PGearArray; - tempColl: Word; -begin - AllInactive := false; - if (Gear^.Timer > 0) and (Gear^.Timer mod 10 <> 0) then - begin - dec(Gear^.Timer); - exit; - end; - - DrawTunnel(Gear^.X, Gear^.Y, Gear^.dX, Gear^.dY, 2, 6); - Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + Gear^.dY; - if (Gear^.Timer mod 30) = 0 then - AddVisualGear(hwRound(Gear^.X + _20 * Gear^.dX), hwRound(Gear^.Y + _20 * Gear^.dY), vgtDust); - if (CheckGearDrowning(Gear)) then - begin - StopSoundChan(Gear^.SoundChannel); - exit - end; - - tempColl:= Gear^.CollisionMask; - Gear^.CollisionMask:= $007F; - if (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) <> 0) or TestCollisionXWithGear(Gear, hwSign(Gear^.dX)) or (GameTicks > Gear^.FlightTime) then - t := CheckGearsCollision(Gear) - else t := nil; - Gear^.CollisionMask:= tempColl; - //fixes drill not exploding when touching HH bug - - if (Gear^.Timer = 0) or ((t <> nil) and (t^.Count <> 0)) - or ( ((Gear^.State and gsttmpFlag) = 0) and (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) = 0) and (not TestCollisionXWithGear(Gear, hwSign(Gear^.dX)))) -// CheckLandValue returns true if the type isn't matched - or (not CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y), lfIndestructible)) then - begin - //out of time or exited ground - StopSoundChan(Gear^.SoundChannel); - if (Gear^.State and gsttmpFlag) <> 0 then - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound) - else - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - exit - end - - else if (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) = 0) and (not TestCollisionXWithGear(Gear, hwSign(Gear^.dX))) then - begin - StopSoundChan(Gear^.SoundChannel); - Gear^.Tag := 1; - Gear^.doStep := @doStepDrill - end; - - dec(Gear^.Timer); -end; - -procedure doStepDrill(Gear: PGear); -var - t: PGearArray; - oldDx, oldDy: hwFloat; - t2: hwFloat; -begin - AllInactive := false; - - if (Gear^.State and gsttmpFlag) = 0 then - Gear^.dX := Gear^.dX + cWindSpeed; - - oldDx := Gear^.dX; - oldDy := Gear^.dY; - - doStepFallingGear(Gear); - - if (GameTicks and $3F) = 0 then - begin - if hwRound(Gear^.Y) > cWaterLine then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble) - else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) - end; - - if ((Gear^.State and gstCollision) <> 0) then - begin - //hit - Gear^.dX := oldDx; - Gear^.dY := oldDy; - - if GameTicks > Gear^.FlightTime then - t := CheckGearsCollision(Gear) - else - t := nil; - if (t = nil) or (t^.Count = 0) then - begin - //hit the ground not the HH - t2 := _0_5 / Distance(Gear^.dX, Gear^.dY); - Gear^.dX := Gear^.dX * t2; - Gear^.dY := Gear^.dY * t2; - end - - else if (t <> nil) then - begin - //explode right on contact with HH - if (Gear^.State and gsttmpFlag) <> 0 then - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound) - else - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - exit; - end; - - Gear^.SoundChannel := LoopSound(sndDrillRocket); - Gear^.doStep := @doStepDrillDrilling; - - if (Gear^.State and gsttmpFlag) <> 0 then - gear^.RenderTimer:= true; - if Gear^.Timer > 0 then dec(Gear^.Timer) - end - else if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Tag <> 0) then - begin - if Gear^.Timer > 0 then - dec(Gear^.Timer) - else - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound); - DeleteGear(Gear); - end - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepBallgunWork(Gear: PGear); -var - HHGear, ball: PGear; - rx, ry: hwFloat; - gX, gY: LongInt; -begin - AllInactive := false; - dec(Gear^.Timer); - HHGear := Gear^.Hedgehog^.Gear; - HedgehogChAngle(HHGear); - gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle); - gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle); - if (Gear^.Timer mod 100) = 0 then - begin - rx := rndSign(getRandomf * _0_1); - ry := rndSign(getRandomf * _0_1); - - ball:= AddGear(gx, gy, gtBall, 0, SignAs(AngleSin(HHGear^.Angle) * _0_8, HHGear^.dX) + rx, AngleCos(HHGear^.Angle) * ( - _0_8) + ry, 0); - ball^.CollisionMask:= lfNotCurrentMask; - - PlaySound(sndGun); - end; - - if (Gear^.Timer = 0) or ((HHGear^.State and gstHHDriven) = 0) then - begin - DeleteGear(Gear); - AfterAttack - end -end; - -procedure doStepBallgun(Gear: PGear); -var - HHGear: PGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown)); - HHGear^.State := HHGear^.State or gstNotKickable; - Gear^.doStep := @doStepBallgunWork -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepRCPlaneWork(Gear: PGear); - -const cAngleSpeed = 3; -var - HHGear: PGear; - i: LongInt; - dX, dY: hwFloat; - fChanged: boolean; - trueAngle: Longword; - t: PGear; -begin - AllInactive := false; - - HHGear := Gear^.Hedgehog^.Gear; - FollowGear := Gear; - - if Gear^.Timer > 0 then - dec(Gear^.Timer); - - fChanged := false; - if ((HHGear^.State and gstHHDriven) = 0) or (Gear^.Timer = 0) then - begin - fChanged := true; - if Gear^.Angle > 2048 then - dec(Gear^.Angle) - else if Gear^.Angle < 2048 then - inc(Gear^.Angle) - else fChanged := false - end - else - begin - if ((Gear^.Message and gmLeft) <> 0) then - begin - fChanged := true; - Gear^.Angle := (Gear^.Angle + (4096 - cAngleSpeed)) mod 4096 - end; - - if ((Gear^.Message and gmRight) <> 0) then - begin - fChanged := true; - Gear^.Angle := (Gear^.Angle + cAngleSpeed) mod 4096 - end - end; - - if fChanged then - begin - Gear^.dX.isNegative := (Gear^.Angle > 2048); - if Gear^.dX.isNegative then - trueAngle := 4096 - Gear^.Angle - else - trueAngle := Gear^.Angle; - - Gear^.dX := SignAs(AngleSin(trueAngle), Gear^.dX) * _0_25; - Gear^.dY := AngleCos(trueAngle) * -_0_25; - end; - - Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + Gear^.dY; - - if (GameTicks and $FF) = 0 then - if Gear^.Timer < 3500 then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEvilTrace) - else - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace); - - if ((HHGear^.Message and gmAttack) <> 0) and (Gear^.Health <> 0) then - begin - HHGear^.Message := HHGear^.Message and (not gmAttack); - AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAirBomb, 0, Gear^.dX * _0_5, Gear^.dY * - _0_5, 0); - dec(Gear^.Health) - end; - - if ((HHGear^.Message and gmLJump) <> 0) and ((Gear^.State and gsttmpFlag) = 0) then - begin - Gear^.State := Gear^.State or gsttmpFlag; - PauseMusic; - playSound(sndRideOfTheValkyries); - end; - - // pickup bonuses - t := CheckGearNear(Gear, gtCase, 36, 36); - if t <> nil then - PickUp(HHGear, t); - - CheckCollision(Gear); - - if ((Gear^.State and gstCollision) <> 0) or CheckGearDrowning(Gear) then - begin - StopSoundChan(Gear^.SoundChannel); - StopSound(sndRideOfTheValkyries); - ResumeMusic; - - if ((Gear^.State and gstCollision) <> 0) then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, Gear^.Hedgehog, EXPLAutoSound); - for i:= 0 to 15 do - begin - dX := AngleCos(i * 64) * _0_5 * (GetRandomf + _1); - dY := AngleSin(i * 64) * _0_5 * (GetRandomf + _1); - AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtFlame, 0, dX, dY, 0); - AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtFlame, 0, dX, -dY, 0); - end; - DeleteGear(Gear) - end; - - AfterAttack; - CurAmmoGear := nil; - if (GameFlags and gfInfAttack) = 0 then - begin - if TagTurnTimeLeft = 0 then - TagTurnTimeLeft:= TurnTimeLeft; - - TurnTimeLeft:= 14 * 125; - end; - - HHGear^.Message := 0; - ParseCommand('/taunt ' + #1, true) - end -end; - -procedure doStepRCPlane(Gear: PGear); -var - HHGear: PGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - HHGear^.Message := 0; - HHGear^.State := HHGear^.State or gstNotKickable; - Gear^.Angle := HHGear^.Angle; - Gear^.Tag := hwSign(HHGear^.dX); - - if HHGear^.dX.isNegative then - Gear^.Angle := 4096 - Gear^.Angle; - Gear^.doStep := @doStepRCPlaneWork -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepJetpackWork(Gear: PGear); -var - HHGear: PGear; - fuel, i: LongInt; - move: hwFloat; - isUnderwater: Boolean; - bubble: PVisualGear; -begin - isUnderwater:= cWaterLine < hwRound(Gear^.Y) + Gear^.Radius; - if Gear^.Pos > 0 then - dec(Gear^.Pos); - AllInactive := false; - HHGear := Gear^.Hedgehog^.Gear; - //dec(Gear^.Timer); - move := _0_2; - fuel := 50; -(*if (HHGear^.Message and gmPrecise) <> 0 then - begin - move:= _0_02; - fuel:= 5; - end;*) - if HHGear^.Message and gmPrecise <> 0 then - HedgehogChAngle(HHGear) - else if Gear^.Health > 0 then - begin - if HHGear^.Message and gmUp <> 0 then - begin - if (not HHGear^.dY.isNegative) or (HHGear^.Y > -_256) then - begin - if isUnderwater then - begin - HHGear^.dY := HHGear^.dY - (move * _0_7); - for i:= random(10)+10 downto 0 do - begin - bubble := AddVisualGear(hwRound(HHGear^.X) - 8 + random(16), hwRound(HHGear^.Y) + 16 + random(8), vgtBubble); - if bubble <> nil then - bubble^.dY:= random(20)/10+0.1; - end - end - else HHGear^.dY := HHGear^.dY - move; - end; - dec(Gear^.Health, fuel); - Gear^.MsgParam := Gear^.MsgParam or gmUp; - Gear^.Timer := GameTicks - end; - move.isNegative := (HHGear^.Message and gmLeft) <> 0; - if (HHGear^.Message and (gmLeft or gmRight)) <> 0 then - begin - HHGear^.dX := HHGear^.dX + (move * _0_1); - if isUnderwater then - begin - for i:= random(5)+5 downto 0 do - begin - bubble := AddVisualGear(hwRound(HHGear^.X)+random(8), hwRound(HHGear^.Y) - 8 + random(16), vgtBubble); - if bubble <> nil then - begin - bubble^.dX:= (random(10)/10 + 0.02) * -1; - if (move.isNegative) then - begin - bubble^.X := bubble^.X + 28; - bubble^.dX:= bubble^.dX * (-1) - end - else bubble^.X := bubble^.X - 28; - end; - end - end; - dec(Gear^.Health, fuel div 5); - Gear^.MsgParam := Gear^.MsgParam or (HHGear^.Message and (gmLeft or gmRight)); - Gear^.Timer := GameTicks - end - end; - - // erases them all at once :-/ - if (Gear^.Timer <> 0) and (GameTicks - Gear^.Timer > 250) then - begin - Gear^.Timer := 0; - Gear^.MsgParam := 0 - end; - - if Gear^.Health < 0 then - Gear^.Health := 0; - - i:= Gear^.Health div 20; - - if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then - begin - Gear^.Damage:= i; - //AddCaption('Fuel: '+inttostr(round(Gear^.Health/20))+'%', cWhiteColor, capgrpAmmostate); - FreeTexture(Gear^.Tex); - Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) + '%', cWhiteColor, fntSmall) - end; - - if (HHGear^.Message and (gmAttack or gmUp or gmLeft or gmRight) <> 0) and - (HHGear^.Message and gmPrecise = 0) then - Gear^.State := Gear^.State and (not gsttmpFlag); - - if HHGear^.Message and gmPrecise = 0 then - HHGear^.Message := HHGear^.Message and (not (gmUp or gmLeft or gmRight)); - HHGear^.State := HHGear^.State or gstMoving; - - Gear^.X := HHGear^.X; - Gear^.Y := HHGear^.Y; - - if not isUnderWater and hasBorder and ((HHGear^.X < _0) - or (hwRound(HHGear^.X) > LAND_WIDTH)) then - HHGear^.dY.isNegative:= false; - - if ((Gear^.State and gsttmpFlag) = 0) - or (HHGear^.dY < _0) then - doStepHedgehogMoving(HHGear); - - if // (Gear^.Health = 0) - (HHGear^.Damage <> 0) - //or CheckGearDrowning(HHGear) - or (cWaterLine + cVisibleWater * 4 < hwRound(HHGear^.Y)) - or (TurnTimeLeft = 0) - // allow brief ground touches - to be fair on this, might need another counter - or (((GameTicks and $1FF) = 0) and (not HHGear^.dY.isNegative) and (TestCollisionYwithGear(HHGear, 1) <> 0)) - or ((Gear^.Message and gmAttack) <> 0) then - begin - with HHGear^ do - begin - Message := 0; - Active := true; - State := State or gstMoving - end; - DeleteGear(Gear); - isCursorVisible := false; - ApplyAmmoChanges(HHGear^.Hedgehog^); - // if Gear^.Tex <> nil then FreeTexture(Gear^.Tex); - -// Gear^.Tex:= RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(round(Gear^.Health / 20)) + '%', cWhiteColor, fntSmall) - -//AddCaption(trmsg[sidFuel]+': '+inttostr(round(Gear^.Health/20))+'%', cWhiteColor, capgrpAmmostate); - end -end; - -procedure doStepJetpack(Gear: PGear); -var - HHGear: PGear; -begin - Gear^.Pos:= 0; - Gear^.doStep := @doStepJetpackWork; - - HHGear := Gear^.Hedgehog^.Gear; - FollowGear := HHGear; - AfterAttack; - with HHGear^ do - begin - State := State and (not gstAttacking); - Message := Message and (not (gmAttack or gmUp or gmPrecise or gmLeft or gmRight)); - - if (dY < _0_1) and (dY > -_0_1) then - begin - Gear^.State := Gear^.State or gsttmpFlag; - dY := dY - _0_2 - end - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepBirdyDisappear(Gear: PGear); -begin - AllInactive := false; - Gear^.Pos := 0; - if Gear^.Timer < 2000 then - inc(Gear^.Timer, 1) - else - begin - DeleteGear(Gear); - end; -end; - -procedure doStepBirdyFly(Gear: PGear); -var - HHGear: PGear; - fuel, i: LongInt; - move: hwFloat; -begin - HHGear := Gear^.Hedgehog^.Gear; - if HHGear = nil then - begin - DeleteGear(Gear); - exit - end; - - move := _0_2; - fuel := 50; - - if Gear^.Pos > 0 then - dec(Gear^.Pos, 1) - else if (HHGear^.Message and (gmLeft or gmRight or gmUp)) <> 0 then - Gear^.Pos := 500; - - if HHGear^.dX.isNegative then - Gear^.Tag := -1 - else - Gear^.Tag := 1; - - if (HHGear^.Message and gmUp) <> 0 then - begin - if (not HHGear^.dY.isNegative) - or (HHGear^.Y > -_256) then - HHGear^.dY := HHGear^.dY - move; - - dec(Gear^.Health, fuel); - Gear^.MsgParam := Gear^.MsgParam or gmUp; - end; - - if (HHGear^.Message and gmLeft) <> 0 then move.isNegative := true; - if (HHGear^.Message and (gmLeft or gmRight)) <> 0 then - begin - HHGear^.dX := HHGear^.dX + (move * _0_1); - dec(Gear^.Health, fuel div 5); - Gear^.MsgParam := Gear^.MsgParam or (HHGear^.Message and (gmLeft or gmRight)); - end; - - if Gear^.Health < 0 then - Gear^.Health := 0; - - if ((GameTicks and $FF) = 0) and (Gear^.Health < 500) then - for i:= ((500-Gear^.Health) div 250) downto 0 do - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFeather); - - if (HHGear^.Message and gmAttack <> 0) then - begin - HHGear^.Message := HHGear^.Message and (not gmAttack); - if Gear^.FlightTime > 0 then - begin - AddGear(hwRound(Gear^.X), hwRound(Gear^.Y) + 32, gtEgg, 0, Gear^.dX * _0_5, Gear^.dY, 0); - PlaySound(sndBirdyLay); - dec(Gear^.FlightTime) - end; - end; - - if HHGear^.Message and (gmUp or gmPrecise or gmLeft or gmRight) <> 0 then - Gear^.State := Gear^.State and (not gsttmpFlag); - - HHGear^.Message := HHGear^.Message and (not (gmUp or gmPrecise or gmLeft or gmRight)); - HHGear^.State := HHGear^.State or gstMoving; - - Gear^.X := HHGear^.X; - Gear^.Y := HHGear^.Y - int2hwFloat(32); - // For some reason I need to reapply followgear here, something else grabs it otherwise. - // this is probably not needed anymore - if not CurrentTeam^.ExtDriven then FollowGear := HHGear; - - if ((Gear^.State and gsttmpFlag) = 0) - or (HHGear^.dY < _0) then - doStepHedgehogMoving(HHGear); - - if (Gear^.Health = 0) - or (HHGear^.Damage <> 0) - or CheckGearDrowning(HHGear) - or (TurnTimeLeft = 0) - // allow brief ground touches - to be fair on this, might need another counter - or (((GameTicks and $1FF) = 0) and (not HHGear^.dY.isNegative) and (TestCollisionYwithGear(HHGear, 1) <> 0)) - or ((Gear^.Message and gmAttack) <> 0) then - begin - with HHGear^ do - begin - Message := 0; - Active := true; - State := State or gstMoving - end; - Gear^.State := Gear^.State or gstAnimation or gstTmpFlag; - if HHGear^.dY < _0 then - begin - Gear^.dX := HHGear^.dX; - Gear^.dY := HHGear^.dY; - end; - Gear^.Timer := 0; - Gear^.doStep := @doStepBirdyDisappear; - CurAmmoGear := nil; - isCursorVisible := false; - AfterAttack; - end -end; - -procedure doStepBirdyDescend(Gear: PGear); -var - HHGear: PGear; -begin - if Gear^.Timer > 0 then - dec(Gear^.Timer, 1) - else if Gear^.Hedgehog^.Gear = nil then - begin - DeleteGear(Gear); - AfterAttack; - exit - end; - HHGear := Gear^.Hedgehog^.Gear; - HHGear^.Message := HHGear^.Message and (not (gmUp or gmPrecise or gmLeft or gmRight)); - if abs(hwRound(HHGear^.Y - Gear^.Y)) > 32 then - begin - if Gear^.Timer = 0 then - Gear^.Y := Gear^.Y + _0_1 - end - else if Gear^.Timer = 0 then - begin - Gear^.doStep := @doStepBirdyFly; - HHGear^.dY := -_0_2 - end -end; - -procedure doStepBirdyAppear(Gear: PGear); -begin - Gear^.Pos := 0; - if Gear^.Timer < 2000 then - inc(Gear^.Timer, 1) - else - begin - Gear^.Timer := 500; - Gear^.dX := _0; - Gear^.dY := _0; - Gear^.State := Gear^.State and (not gstAnimation); - Gear^.doStep := @doStepBirdyDescend; - end -end; - -procedure doStepBirdy(Gear: PGear); -var - HHGear: PGear; -begin - gear^.State := gear^.State or gstAnimation and (not gstTmpFlag); - Gear^.doStep := @doStepBirdyAppear; - - if CurrentHedgehog = nil then - begin - DeleteGear(Gear); - exit - end; - - HHGear := CurrentHedgehog^.Gear; - - if HHGear^.dX.isNegative then - Gear^.Tag := -1 - else - Gear^.Tag := 1; - Gear^.Pos := 0; - AllInactive := false; - FollowGear := HHGear; - with HHGear^ do - begin - State := State and (not gstAttacking); - Message := Message and (not (gmAttack or gmUp or gmPrecise or gmLeft or gmRight)) - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepEggWork(Gear: PGear); -var - vg: PVisualGear; - i: LongInt; -begin - AllInactive := false; - Gear^.dX := Gear^.dX; - doStepFallingGear(Gear); - // CheckGearDrowning(Gear); // already checked for in doStepFallingGear - CalcRotationDirAngle(Gear); - - if (Gear^.State and gstCollision) <> 0 then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLPoisoned, $C0E0FFE0); - PlaySound(sndEggBreak); - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEgg); - vg := AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEgg); - if vg <> nil then - vg^.Frame := 2; - - for i:= 10 downto 0 do - begin - vg := AddVisualGear(hwRound(Gear^.X) - 3 + Random(6), hwRound(Gear^.Y) - 3 + Random(6), - vgtDust); - if vg <> nil then - vg^.dX := vg^.dX + (Gear^.dX.QWordValue / 21474836480); - end; - - DeleteGear(Gear); - exit - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doPortalColorSwitch(); -var CurWeapon: PAmmo; -begin - if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and ((CurrentHedgehog^.Gear^.Message and gmSwitch) <> 0) then - with CurrentHedgehog^ do - if (CurAmmoType = amPortalGun) then - begin - CurrentHedgehog^.Gear^.Message := CurrentHedgehog^.Gear^.Message and (not gmSwitch); - - CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^); - if CurWeapon^.Pos <> 0 then - CurWeapon^.Pos := 0 - - else - CurWeapon^.Pos := 1; - end; -end; - -procedure doStepPortal(Gear: PGear); -var - iterator, conPortal: PGear; - s, r, nx, ny, ox, oy, poffs, noffs, pspeed, nspeed, - resetx, resety, resetdx, resetdy: hwFloat; - sx, sy, rh, resetr: LongInt; - hasdxy, isbullet, iscake, isCollision: Boolean; -begin - doPortalColorSwitch(); - - // destroy portal if ground it was attached too is gone - if (Land[hwRound(Gear^.Y), hwRound(Gear^.X)] <= lfAllObjMask) - or (Gear^.Timer < 1) - or (Gear^.Hedgehog^.Team <> CurrentHedgehog^.Team) - or (hwRound(Gear^.Y) > cWaterLine) then - begin - deleteGear(Gear); - EXIT; - end; - - if (TurnTimeLeft < 1) - or (Gear^.Health < 1) then - dec(Gear^.Timer); - - if Gear^.Timer < 10000 then - gear^.RenderTimer := true; - - // abort if there is no other portal connected to this one - if (Gear^.LinkedGear = nil) then - exit; - if ((Gear^.LinkedGear^.Tag and 1) = 0) then // or if it's still moving; - exit; - - conPortal := Gear^.LinkedGear; - - // check all gears for stuff to port through - iterator := nil; - while true do - begin - - // iterate through GearsList - if iterator = nil then - iterator := GearsList - else - iterator := iterator^.NextGear; - - // end of list? - if iterator = nil then - break; - - // don't port portals or other gear that wouldn't make sense - if (iterator^.Kind in [gtPortal, gtRope, gtAirAttack, gtIceGun]) - or (iterator^.PortalCounter > 32) then - continue; - - // don't port hogs on rope - // TODO: this will also prevent hogs while falling after rope use from - // falling through portals... fix that! - - // check if gear fits through portal - if (iterator^.Radius > Gear^.Radius) then - continue; - - // this is the max range we accept incoming gears in - r := Int2hwFloat(iterator^.Radius+Gear^.Radius); - - // too far away? - if (iterator^.X < Gear^.X - r) - or (iterator^.X > Gear^.X + r) - or (iterator^.Y < Gear^.Y - r) - or (iterator^.Y > Gear^.Y + r) then - continue; - - hasdxy := (((iterator^.dX.QWordValue <> 0) or (iterator^.dY.QWordValue <> 0)) or ((iterator^.State or gstMoving) = 0)); - - // in case the object is not moving, let's asume it's falling towards the portal - if not hasdxy then - begin - if Gear^.Y < iterator^.Y then - continue; - ox:= Gear^.X - iterator^.X; - oy:= Gear^.Y - iterator^.Y; - end - else - begin - ox:= iterator^.dX; - oy:= iterator^.dY; - end; - - // cake will need extra treatment... it's so delicious and moist! - iscake:= (iterator^.Kind = gtCake); - - // won't port stuff that does not move towards the front/portal entrance - if iscake then - begin - if not (((iterator^.X - Gear^.X)*ox + (iterator^.Y - Gear^.Y)*oy).isNegative) then - continue; - end - else - if not ((Gear^.dX*ox + Gear^.dY*oy).isNegative) then - continue; - - isbullet:= (iterator^.Kind in [gtShotgunShot, gtDEagleShot, gtSniperRifleShot, gtSineGunShot]); - - r:= int2hwFloat(iterator^.Radius); - - if not (isbullet or iscake) then - begin - // wow! good candidate there, let's see if the distance and direction is okay! - if hasdxy then - begin - s := Distance(iterator^.dX, iterator^.dY); - // if the resulting distance is 0 skip this gear - if s.QWordValue = 0 then - continue; - s := r / s; - ox:= iterator^.X + s * iterator^.dX; - oy:= iterator^.Y + s * iterator^.dY; - end - else - begin - ox:= iterator^.X; - oy:= iterator^.Y + r; - end; - - if (hwRound(Distance(Gear^.X-ox,Gear^.Y-oy)) > Gear^.Radius + 1 ) then - continue; - end; - - // draw bullet trail - if isbullet then - spawnBulletTrail(iterator); - - // calc gear offset in portal vector direction - ox := (iterator^.X - Gear^.X); - oy := (iterator^.Y - Gear^.Y); - poffs:= (Gear^.dX * ox + Gear^.dY * oy); - - if not isBullet and poffs.isNegative then - continue; - - // only port bullets close to the portal - if isBullet and (not (hwAbs(poffs) < _3)) then - continue; - - // - // gears that make it till here will definately be ported - // - // (but old position/movement vector might be restored in case there's - // not enough space on the other side) - // - - resetr := iterator^.Radius; - resetx := iterator^.X; - resety := iterator^.Y; - resetdx := iterator^.dX; - resetdy := iterator^.dY; - - // create a normal of the portal vector, but ... - nx := Gear^.dY; - ny := Gear^.dX; - // ... decide where the top is based on the hog's direction when firing the portal - if Gear^.Elasticity.isNegative then - nx.isNegative := (not nx.isNegative) - else - ny.isNegative := not ny.isNegative; - - // calc gear offset in portal normal vector direction - noffs:= (nx * ox + ny * oy); - - if isBullet and (noffs.Round >= Longword(Gear^.Radius)) then - continue; - - // avoid gravity related loops of not really moving gear - if not (iscake or isbullet) - and (Gear^.dY.isNegative) - and (conPortal^.dY.isNegative) - and ((iterator^.dX.QWordValue + iterator^.dY.QWordValue) < _0_08.QWordValue) - and (iterator^.PortalCounter > 0) then - continue; - - // calc gear speed along to the vector and the normal vector of the portal - if hasdxy then - begin - pspeed:= (Gear^.dX * iterator^.dX + Gear^.dY * iterator^.dY); - nspeed:= (nx * iterator^.dX + ny * iterator^.dY); - end - else - begin - pspeed:= hwAbs(cGravity * oy); - nspeed:= _0; - end; - - // creating normal vector of connected (exit) portal - nx := conPortal^.dY; - ny := conPortal^.dX; - if conPortal^.Elasticity.isNegative then - nx.isNegative := (not nx.isNegative) - else - ny.isNegative := not ny.isNegative; - - // inverse cake's normal movement direction, - // as if it just walked through a hole - //if iscake then nspeed.isNegative:= not nspeed.isNegative; - -//AddFileLog('poffs:'+cstr(poffs)+' noffs:'+cstr(noffs)+' pspeed:'+cstr(pspeed)+' nspeed:'+cstr(nspeed)); - iterator^.dX := -pspeed * conPortal^.dX + nspeed * nx; - iterator^.dY := -pspeed * conPortal^.dY + nspeed * ny; - - // make the gear's exit position close to the portal while - // still respecting the movement direction - - // determine the distance (in exit vector direction) - // that we want the gear at - if iscake then - ox:= (r - _0_7) - else - ox:= (r * _1_5); - s:= ox / poffs; - poffs:= ox; - if (nspeed.QWordValue <> 0) - and (pspeed > _0) then - noffs:= noffs * s * (nspeed / pspeed); - - // move stuff with high normal offset closer to the portal's center - if not isbullet then - begin - s := hwAbs(noffs) + r - int2hwFloat(Gear^.Radius); - if s > _0 then - noffs:= noffs - SignAs(s,noffs) - end; - - iterator^.X := conPortal^.X + poffs * conPortal^.dX + noffs * nx; - iterator^.Y := conPortal^.Y + poffs * conPortal^.dY + noffs * ny; - - if not hasdxy and (not (conPortal^.dY.isNegative)) then - begin - iterator^.dY:= iterator^.dY + hwAbs(cGravity * (iterator^.Y - conPortal^.Y)) - end; - - // see if the space on the exit side actually is enough - - if not (isBullet or isCake) then - begin - // TestCollisionXwithXYShift requires a hwFloat for xShift - ox.QWordValue := _1.QWordValue; - ox.isNegative := not iterator^.dX.isNegative; - - sx := hwSign(iterator^.dX); - sy := hwSign(iterator^.dY); - - if iterator^.Radius > 1 then - iterator^.Radius := iterator^.Radius - 1; - - // check front - isCollision := TestCollisionY(iterator, sy) - or TestCollisionX(iterator, sx); - - if not isCollision then - begin - // check center area (with half the radius so that the - // the square check won't check more pixels than we want to) - iterator^.Radius := 1 + resetr div 2; - rh := resetr div 4; - isCollision := TestCollisionYwithXYShift(iterator, 0, -sy * rh, sy, false) - or TestCollisionXwithXYShift(iterator, ox * rh, 0, sx, false); - end; - - iterator^.Radius := resetr; - - if isCollision then - begin - // collision! oh crap! go back! - iterator^.X := resetx; - iterator^.Y := resety; - iterator^.dX := resetdx; - iterator^.dY := resetdy; - continue; - end; - end; - - // - // You're now officially portaled! - // - - // Until loops are reliably broken - if iscake then - iterator^.PortalCounter:= 33 - else - begin - inc(iterator^.PortalCounter); - iterator^.Active:= true; - iterator^.State:= iterator^.State and (not gstHHHJump) or gstMoving; - end; - - // is it worth adding an arcsin table? Just how often would we end up doing something like this? - // SYNCED ANGLE UPDATE - if iterator^.Kind = gtRCPlane then - begin - // recycling as temp vars - resety.isNegative:= false; - resety.QWordValue:= 4294967296 * 112; - resetx.isNegative:= false; - resetx.QWordValue:= 4294967296 * 35; - resetdx.isNegative:= false; - resetdx.QWordValue:= 4294967296 * 1152; - - resetdy:=hwAbs(iterator^.dX*4); - resetdy:= resetdy + hwPow(resetdy,3)/_6 + _3 * hwPow(resetdy,5) / _40 + _5 * hwPow(resetdy,7) / resety + resetx * hwPow(resetdy,9) / resetdx; - iterator^.Angle:= hwRound(resetdy*_2048 / _PI); - if not iterator^.dY.isNegative then iterator^.Angle:= 2048-iterator^.Angle; - if iterator^.dX.isNegative then iterator^.Angle:= 4096-iterator^.Angle; - end - // VISUAL USE OF ANGLE ONLY - else if (CurAmmoGear <> nil) and (CurAmmoGear^.Kind = gtKamikaze) and (CurAmmoGear^.Hedgehog = iterator^.Hedgehog) then - begin - iterator^.Angle:= DxDy2AttackAngle(iterator^.dX, iterator^.dY); - iterator^.Angle:= 2048-iterator^.Angle; - if iterator^.dX.isNegative then iterator^.Angle:= 4096-iterator^.Angle; - end; - - if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) - and (iterator = CurrentHedgehog^.Gear) - and (CurAmmoGear <> nil) - and (CurAmmoGear^.Kind =gtRope) then - CurAmmoGear^.PortalCounter:= 1; - - if not isbullet and (iterator^.State and gstInvisible = 0) - and (iterator^.Kind <> gtFlake) then - FollowGear := iterator; - - // store X/Y values of exit for net bullet trail - if isbullet then - begin - iterator^.Elasticity:= iterator^.X; - iterator^.Friction := iterator^.Y; - end; - - if Gear^.Health > 1 then - dec(Gear^.Health); - end; -end; - - - -procedure loadNewPortalBall(oldPortal: PGear; destroyGear: Boolean); -var - CurWeapon: PAmmo; -begin - if CurrentHedgehog <> nil then - with CurrentHedgehog^ do - begin - CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^); - if (CurAmmoType = amPortalGun) then - begin - if not destroyGear then - begin - // switch color of ball to opposite of oldPortal - if (oldPortal^.Tag and 2) = 0 then - CurWeapon^.Pos:= 1 - else - CurWeapon^.Pos:= 0; - end; - - // make the ball visible - CurWeapon^.Timer := 0; - end - end; - if destroyGear then - oldPortal^.Timer:= 0; -end; - -procedure doStepMovingPortal_real(Gear: PGear); -var - x, y, tx, ty: LongInt; - s: hwFloat; -begin - x := hwRound(Gear^.X); - y := hwRound(Gear^.Y); - tx := 0; - ty := 0; - // avoid compiler hints - - if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] > 255) then - begin - Gear^.State := Gear^.State or gstCollision; - Gear^.State := Gear^.State and (not gstMoving); - - if (Land[y, x] and lfBouncy <> 0) - or (not CalcSlopeTangent(Gear, x, y, tx, ty, 255)) - or (DistanceI(tx,ty) < _12) then // reject shots at too irregular terrain - begin - loadNewPortalBall(Gear, true); - EXIT; - end; - - // making a normalized normal vector - s := _1/DistanceI(tx,ty); - Gear^.dX := s * ty; - Gear^.dY := -s * tx; - - Gear^.DirAngle := DxDy2Angle(-Gear^.dY,Gear^.dX); - if not Gear^.dX.isNegative then - Gear^.DirAngle := 180-Gear^.DirAngle; - - if ((Gear^.LinkedGear = nil) - or (hwRound(Distance(Gear^.X - Gear^.LinkedGear^.X,Gear^.Y-Gear^.LinkedGear^.Y)) >=Gear^.Radius*2)) then - begin - loadNewPortalBall(Gear, false); - inc(Gear^.Tag); - Gear^.doStep := @doStepPortal; - end - else - loadNewPortalBall(Gear, true); - end - - else if (y > cWaterLine) - or (y < -max(LAND_WIDTH,4096)) - or (x > 2*max(LAND_WIDTH,4096)) - or (x < -max(LAND_WIDTH,4096)) then - loadNewPortalBall(Gear, true); -end; - -procedure doStepMovingPortal(Gear: PGear); -begin - doPortalColorSwitch(); - doStepPerPixel(Gear, @doStepMovingPortal_real, true); - if (Gear^.Timer < 1) - or (Gear^.Hedgehog^.Team <> CurrentHedgehog^.Team) then - deleteGear(Gear); -end; - -procedure doStepPortalShot(newPortal: PGear); -var - iterator: PGear; - s: hwFloat; - CurWeapon: PAmmo; -begin - s:= Distance (newPortal^.dX, newPortal^.dY); - - // Adds the hog speed (only that part in/directly against shot direction) - // to the shot speed (which we triple previously btw) - // (This is done my projecting the hog movement vector onto the shot movement vector and then adding the resulting length - // to the scaler) - s := (_2 * s + (newPortal^.dX * CurrentHedgehog^.Gear^.dX + newPortal^.dY * CurrentHedgehog^.Gear^.dY ) / s) / s; - newPortal^.dX := newPortal^.dX * s; - newPortal^.dY := newPortal^.dY * s; - - newPortal^.LinkedGear := nil; - - if CurrentHedgehog <> nil then - with CurrentHedgehog^ do - begin - CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^); - // let's save the HH's dX's direction so we can decide where the "top" of the portal hole - newPortal^.Elasticity.isNegative := CurrentHedgehog^.Gear^.dX.isNegative; - // when doing a backjump the dx is the opposite of the facing direction - if ((Gear^.State and gstHHHJump) <> 0) and (not cArtillery) then - newPortal^.Elasticity.isNegative := not newPortal^.Elasticity.isNegative; - - // make portal gun look unloaded - if (CurWeapon <> nil) and (CurAmmoType = amPortalGun) then - CurWeapon^.Timer := CurWeapon^.Timer or 2; - - iterator := GearsList; - while iterator <> nil do - begin - if (iterator^.Kind = gtPortal) then - if (iterator <> newPortal) and (iterator^.Timer > 0) and (iterator^.Hedgehog = CurrentHedgehog) then - begin - if ((iterator^.Tag and 2) = (newPortal^.Tag and 2)) then - begin - iterator^.Timer:= 0; - end - else - begin - // link portals with each other - newPortal^.LinkedGear := iterator; - iterator^.LinkedGear := newPortal; - iterator^.Health := newPortal^.Health; - end; - end; - iterator^.PortalCounter:= 0; - iterator := iterator^.NextGear - end; - - if newPortal^.LinkedGear <> nil then - begin - // This jiggles gears, to ensure a portal connection just placed under a gear takes effect. - iterator:= GearsList; - while iterator <> nil do - begin - if not (iterator^.Kind in [gtPortal, gtAirAttack, gtKnife]) and ((iterator^.Hedgehog <> CurrentHedgehog) - or ((iterator^.Message and gmAllStoppable) = 0)) then - begin - iterator^.Active:= true; - if iterator^.dY.QWordValue = 0 then - iterator^.dY.isNegative:= false; - iterator^.State:= iterator^.State or gstMoving; - DeleteCI(iterator); - //inc(iterator^.dY.QWordValue,10); - end; - iterator:= iterator^.NextGear - end - end - end; - newPortal^.State := newPortal^.State and (not gstCollision); - newPortal^.State := newPortal^.State or gstMoving; - newPortal^.doStep := @doStepMovingPortal; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepPiano(Gear: PGear); -var - r0, r1: LongInt; - odY: hwFloat; -begin - AllInactive := false; - if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and - ((CurrentHedgehog^.Gear^.Message and gmSlot) <> 0) then - begin - case CurrentHedgehog^.Gear^.MsgParam of - 0: PlaySound(sndPiano0); - 1: PlaySound(sndPiano1); - 2: PlaySound(sndPiano2); - 3: PlaySound(sndPiano3); - 4: PlaySound(sndPiano4); - 5: PlaySound(sndPiano5); - 6: PlaySound(sndPiano6); - 7: PlaySound(sndPiano7); - else PlaySound(sndPiano8); - end; - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtNote); - CurrentHedgehog^.Gear^.MsgParam := 0; - CurrentHedgehog^.Gear^.Message := CurrentHedgehog^.Gear^.Message and (not gmSlot); - end; - - if (*((Gear^.Pos = 3) and ((GameFlags and gfSolidLand) <> 0)) or*) (Gear^.Pos = 5) then - begin - Gear^.dY := Gear^.dY + cGravity * 2; - Gear^.Y := Gear^.Y + Gear^.dY; - if CheckGearDrowning(Gear) then - begin - Gear^.Y:= Gear^.Y + _50; - OnUsedAmmo(CurrentHedgehog^); - if CurrentHedgehog^.Gear <> nil then - begin - // Drown the hedgehog. Could also just delete it, but hey, this gets a caption - CurrentHedgehog^.Gear^.Active := true; - CurrentHedgehog^.Gear^.X := Gear^.X; - CurrentHedgehog^.Gear^.Y := int2hwFloat(cWaterLine+cVisibleWater)+_128; - CurrentHedgehog^.Unplaced := false; - if TagTurnTimeLeft = 0 then - TagTurnTimeLeft:= TurnTimeLeft; - TurnTimeLeft:= 0 - end; - ResumeMusic - end; - exit - end; - - odY:= Gear^.dY; - doStepFallingGear(Gear); - - if (Gear^.State and gstDrowning) <> 0 then - begin - Gear^.Y:= Gear^.Y + _50; - OnUsedAmmo(CurrentHedgehog^); - if CurrentHedgehog^.Gear <> nil then - begin - // Drown the hedgehog. Could also just delete it, but hey, this gets a caption - CurrentHedgehog^.Gear^.Active := true; - CurrentHedgehog^.Gear^.X := Gear^.X; - CurrentHedgehog^.Gear^.Y := int2hwFloat(cWaterLine+cVisibleWater)+_128; - CurrentHedgehog^.Unplaced := false; - if TagTurnTimeLeft = 0 then - TagTurnTimeLeft:= TurnTimeLeft; - TurnTimeLeft:= 0 - end; - ResumeMusic - end - else if (Gear^.State and gstCollision) <> 0 then - begin - r0 := GetRandom(21); - r1 := GetRandom(21); - doMakeExplosion(hwRound(Gear^.X) - 30 - r0, hwRound(Gear^.Y) + 40, 40 + r1, Gear^.Hedgehog, 0); - doMakeExplosion(hwRound(Gear^.X) + 30 + r1, hwRound(Gear^.Y) + 40, 40 + r0, Gear^.Hedgehog, 0); - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 80 + r0, Gear^.Hedgehog, EXPLAutoSound); - for r0:= 0 to 4 do - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtNote); - Gear^.dY := cGravity * 2 - odY; - Gear^.Pos := Gear^.Pos + 1; - end - else - Gear^.dY := Gear^.dY + cGravity * 2; - // let it fall faster so itdoesn't take too long for the whole attack -end; - - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSineGunShotWork(Gear: PGear); -var - x, y, rX, rY, t, tmp, initHealth: LongInt; - oX, oY, ldX, ldY, sdX, sdY, sine, lx, ly, amp: hwFloat; - justCollided: boolean; -begin - AllInactive := false; - initHealth := Gear^.Health; - lX := Gear^.X; - lY := Gear^.Y; - ldX := Gear^.dX; - ldY := Gear^.dY; - sdy := _0_5/Distance(Gear^.dX,Gear^.dY); - ldX := ldX * sdy; - ldY := ldY * sdy; - sdY := hwAbs(ldX) + hwAbs(ldY); - sdX := _1 - hwAbs(ldX/sdY); - sdY := _1 - hwAbs(ldY/sdY); - if (ldX.isNegative = ldY.isNegative) then - sdY := -sdY; - - // initial angle depends on current GameTicks - t := getRandom(4096); - - - // used for a work-around detection of area that is within land array, but outside borders - justCollided := false; - - repeat - lX := lX + ldX; - lY := lY + ldY; - oX := Gear^.X; - oY := Gear^.Y; - rX := hwRound(oX); - rY := hwRound(oY); - tmp := t mod 4096; - amp := _128 * (_1 - hwSqr(int2hwFloat(Gear^.Health)/initHealth)); - sine := amp * AngleSin(tmp mod 2048); - sine.isNegative := (tmp < 2048); - inc(t,Gear^.Health div 313); - Gear^.X := lX + (sine * sdX); - Gear^.Y := ly + (sine * sdY); - Gear^.dX := Gear^.X - oX; - Gear^.dY := Gear^.Y - oY; - - x := hwRound(Gear^.X); - y := hwRound(Gear^.Y); - - // if borders are on, stop outside land array - if hasBorder and (((x and LAND_WIDTH_MASK) <> 0) or ((y and LAND_HEIGHT_MASK) <> 0)) then - begin - Gear^.Damage := 0; - Gear^.Health := 0; - end - else - begin - if (rY <= cWaterLine) or (y <= cWaterLine) then - begin - if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) - and (Land[y, x] <> 0) then - begin - if justCollided then - begin - Gear^.Damage := 0; - Gear^.Health := 0; - end - else - begin - inc(Gear^.Damage,3); - justCollided := true; - end; - end - else - justCollided := false; - - // kick nearby hogs, dig tunnel and add some fire - // if at least 5 collisions occured - if Gear^.Damage > 0 then - begin - DrawExplosion(rX,rY,Gear^.Radius); - - // kick nearby hogs - AmmoShove(Gear, 35, 50); - - dec(Gear^.Health, Gear^.Damage); - Gear^.Damage := 0; - - // add some fire to the tunnel - if getRandom(6) = 0 then - begin - tmp:= GetRandom(2 * Gear^.Radius); - AddGear(x - Gear^.Radius + tmp, y - GetRandom(Gear^.Radius + 1), gtFlame, gsttmpFlag, _0, _0, 0) - end - end; - - if random(100) = 0 then - AddVisualGear(x, y, vgtSmokeTrace); - end - else dec(Gear^.Health, 5); // if underwater get additional damage - end; - - dec(Gear^.Health); - - // decrease bullet size towards the end - if (Gear^.Radius > 4) then - begin - if (Gear^.Health <= (initHealth div 3)) then - dec(Gear^.Radius) - end - else if (Gear^.Radius > 3) then - begin - if (Gear^.Health <= (initHealth div 4)) then - dec(Gear^.Radius) - end - else if (Gear^.Radius > 2) then begin - if (Gear^.Health <= (initHealth div 5)) then - dec(Gear^.Radius) - end - else if (Gear^.Radius > 1) then - begin - if (Gear^.Health <= (initHealth div 6)) then - dec(Gear^.Radius) - end; - - until (Gear^.Health <= 0); - - DeleteGear(Gear); - AfterAttack; -end; - -procedure doStepSineGunShot(Gear: PGear); -var - HHGear: PGear; -begin - PlaySound(sndSineGun); - - // push the shooting Hedgehog back - HHGear := CurrentHedgehog^.Gear; - Gear^.dX.isNegative := not Gear^.dX.isNegative; - Gear^.dY.isNegative := not Gear^.dY.isNegative; - HHGear^.dX := Gear^.dX; - HHGear^.dY := Gear^.dY; - AmmoShove(Gear, 0, 80); - Gear^.dX.isNegative := not Gear^.dX.isNegative; - Gear^.dY.isNegative := not Gear^.dY.isNegative; - - Gear^.doStep := @doStepSineGunShotWork; - with mobileRecord do - if (performRumble <> nil) and (not fastUntilLag) then - performRumble(kSystemSoundID_Vibrate); -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepFlamethrowerWork(Gear: PGear); -var - HHGear, flame: PGear; - rx, ry, speed: hwFloat; - i, gX, gY: LongInt; -begin - AllInactive := false; - HHGear := Gear^.Hedgehog^.Gear; - HedgehogChAngle(HHGear); - gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle); - gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle); - - if (GameTicks and $FF) = 0 then - begin - if (HHGear^.Message and gmRight) <> 0 then - begin - if HHGear^.dX.isNegative and (Gear^.Tag < 20) then - inc(Gear^.Tag) - else if Gear^.Tag > 5 then - dec(Gear^.Tag); - end - else if (HHGear^.Message and gmLeft) <> 0 then - begin - if HHGear^.dX.isNegative and (Gear^.Tag > 5) then - dec(Gear^.Tag) - else if Gear^.Tag < 20 then - inc(Gear^.Tag); - end - end; - - dec(Gear^.Timer); - if Gear^.Timer = 0 then - begin - dec(Gear^.Health); - if (Gear^.Health mod 5) = 0 then - begin - rx := rndSign(getRandomf * _0_1); - ry := rndSign(getRandomf * _0_1); - speed := _0_5 * (_10 / Gear^.Tag); - - flame:= AddGear(gx, gy, gtFlame, gstTmpFlag, - SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx, - AngleCos(HHGear^.Angle) * ( - speed) + ry, 0); - flame^.CollisionMask:= lfNotCurrentMask; - - if (Gear^.Health mod 30) = 0 then - begin - flame:= AddGear(gx, gy, gtFlame, 0, - SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx, - AngleCos(HHGear^.Angle) * ( - speed) + ry, 0); - flame^.CollisionMask:= lfNotCurrentMask; - end - end; - Gear^.Timer:= Gear^.Tag - end; - - if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) then - begin - DeleteGear(Gear); - AfterAttack - end - else - begin - i:= Gear^.Health div 5; - if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then - begin - Gear^.Damage:= i; - FreeTexture(Gear^.Tex); - Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) + - '%', cWhiteColor, fntSmall) - end - end -end; - -procedure doStepFlamethrower(Gear: PGear); -var - HHGear: PGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown or gmLeft or gmRight)); - HHGear^.State := HHGear^.State or gstNotKickable; - Gear^.doStep := @doStepFlamethrowerWork -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepLandGunWork(Gear: PGear); -var - HHGear, land: PGear; - rx, ry, speed: hwFloat; - i, gX, gY: LongInt; -begin - AllInactive := false; - HHGear := Gear^.Hedgehog^.Gear; - HedgehogChAngle(HHGear); - gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle); - gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle); - - if (GameTicks and $FF) = 0 then - begin - if (HHGear^.Message and gmRight) <> 0 then - begin - if HHGear^.dX.isNegative and (Gear^.Tag < 20) then - inc(Gear^.Tag) - else if Gear^.Tag > 5 then - dec(Gear^.Tag); - end - else if (HHGear^.Message and gmLeft) <> 0 then - begin - if HHGear^.dX.isNegative and (Gear^.Tag > 5) then - dec(Gear^.Tag) - else if Gear^.Tag < 20 then - inc(Gear^.Tag); - end - end; - - dec(Gear^.Timer); - if Gear^.Timer = 0 then - begin - dec(Gear^.Health); - - rx := rndSign(getRandomf * _0_1); - ry := rndSign(getRandomf * _0_1); - speed := (_3 / Gear^.Tag); - - land:= AddGear(gx, gy, gtFlake, gstTmpFlag, - SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx, - AngleCos(HHGear^.Angle) * ( - speed) + ry, 0); - land^.CollisionMask:= lfNotCurrentMask; - - Gear^.Timer:= Gear^.Tag - end; - - if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) or ((HHGear^.Message and gmAttack) <> 0) then - begin - HHGear^.Message:= HHGear^.Message and (not gmAttack); - DeleteGear(Gear); - AfterAttack - end - else - begin - i:= Gear^.Health div 10; - if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then - begin - Gear^.Damage:= i; - FreeTexture(Gear^.Tex); - Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) + - '%', cWhiteColor, fntSmall) - end - end -end; - -procedure doStepLandGun(Gear: PGear); -var - HHGear: PGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown or gmLeft or gmRight or gmAttack)); - HHGear^.State := HHGear^.State or gstNotKickable; - Gear^.doStep := @doStepLandGunWork -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepPoisonCloud(Gear: PGear); -begin - if Gear^.Timer = 0 then - begin - DeleteGear(Gear); - exit - end; - dec(Gear^.Timer); - Gear^.X:= Gear^.X + Gear^.dX; - Gear^.Y:= Gear^.Y + Gear^.dY; - Gear^.dX := Gear^.dX + cWindSpeed / 4; - Gear^.dY := Gear^.dY + cGravity / 100; - if (GameTicks and $FF) = 0 then - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx or EXPLNoDamage or EXPLDoNotTouchAny or EXPLPoisoned); - AllInactive:= false; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepHammer(Gear: PGear); -var HHGear, tmp, tmp2: PGear; - t: PGearArray; - i: LongInt; -begin -HHGear:= Gear^.Hedgehog^.Gear; -HHGear^.State:= HHGear^.State or gstNoDamage; -DeleteCI(HHGear); - -t:= CheckGearsCollision(Gear); - -for i:= 5 downto 0 do - AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust); - -i:= t^.Count; -while i > 0 do - begin - dec(i); - tmp:= t^.ar[i]; - if (tmp^.State and gstNoDamage) = 0 then - if (tmp^.Kind = gtHedgehog) or (tmp^.Kind = gtMine) or (tmp^.Kind = gtExplosives) then - begin - //tmp^.State:= tmp^.State or gstFlatened; - if not tmp^.Invulnerable then - ApplyDamage(tmp, CurrentHedgehog, tmp^.Health div 3, dsUnknown); - //DrawTunnel(tmp^.X, tmp^.Y - _1, _0, _0_5, cHHRadius * 6, cHHRadius * 3); - tmp2:= AddGear(hwRound(tmp^.X), hwRound(tmp^.Y), gtHammerHit, 0, _0, _0, 0); - tmp2^.LinkedGear:= tmp; - SetAllToActive - end - else - begin - end - end; - -HHGear^.State:= HHGear^.State and (not gstNoDamage); -Gear^.Timer:= 250; -Gear^.doStep:= @doStepIdle -end; - -procedure doStepHammerHitWork(Gear: PGear); -var - i, j, ei: LongInt; - HitGear: PGear; -begin - AllInactive := false; - HitGear := Gear^.LinkedGear; - dec(Gear^.Timer); - if (HitGear = nil) or (Gear^.Timer = 0) or ((Gear^.Message and gmDestroy) <> 0) then - begin - DeleteGear(Gear); - exit - end; - - if (Gear^.Timer mod 5) = 0 then - begin - AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust); - - i := hwRound(Gear^.X) - HitGear^.Radius + 2; - ei := hwRound(Gear^.X) + HitGear^.Radius - 2; - for j := 1 to 4 do DrawExplosion(i - GetRandom(5), hwRound(Gear^.Y) + 6*j, 3); - for j := 1 to 4 do DrawExplosion(ei + LongInt(GetRandom(5)), hwRound(Gear^.Y) + 6*j, 3); - while i <= ei do - begin - for j := 1 to 11 do DrawExplosion(i, hwRound(Gear^.Y) + 3*j, 3); - inc(i, 1) - end; - - if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9) - , lfIndestructible) then - begin - //Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + _1_9 - end; - end; - if TestCollisionYwithGear(Gear, 1) <> 0 then - begin - Gear^.dY := _0; - SetLittle(HitGear^.dX); - HitGear^.dY := _0; - end - else - begin - //Gear^.dY := Gear^.dY + cGravity; - //Gear^.Y := Gear^.Y + Gear^.dY; - if hwRound(Gear^.Y) > cWaterLine then - Gear^.Timer := 1 - end; - - //Gear^.X := Gear^.X + HitGear^.dX; - HitGear^.X := Gear^.X; - HitGear^.Y := Gear^.Y; - SetLittle(HitGear^.dY); - HitGear^.Active:= true; -end; - -procedure doStepHammerHit(Gear: PGear); -var - i, y: LongInt; - ar: TRangeArray; - HHGear: PGear; -begin - i := 0; - HHGear := Gear^.Hedgehog^.Gear; - - y := hwRound(Gear^.Y) - cHHRadius * 2; - while y < hwRound(Gear^.Y) do - begin - ar[i].Left := hwRound(Gear^.X) - Gear^.Radius - LongInt(GetRandom(2)); - ar[i].Right := hwRound(Gear^.X) + Gear^.Radius + LongInt(GetRandom(2)); - inc(y, 2); - inc(i) - end; - - DrawHLinesExplosions(@ar, 3, hwRound(Gear^.Y) - cHHRadius * 2, 2, Pred(i)); - Gear^.dY := HHGear^.dY; - DeleteCI(HHGear); - - doStepHammerHitWork(Gear); - Gear^.doStep := @doStepHammerHitWork -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepResurrectorWork(Gear: PGear); -var - graves: PGearArrayS; - resgear: PGear; - hh: PHedgehog; - i: LongInt; -begin - if (TurnTimeLeft > 0) then - dec(TurnTimeLeft); - - AllInactive := false; - hh := Gear^.Hedgehog; - - // no, you can't do that here - {DrawCentered(hwRound(hh^.Gear^.X) + WorldDx, hwRound(hh^.Gear^.Y) + WorldDy - - cHHRadius - 14 - hh^.HealthTagTex^.h, hh^.HealthTagTex); - } - (*DrawCircle(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Radius, 1.5, 0, 0, $FF, - $FF);*) - - if ((Gear^.Message and gmUp) <> 0) then - begin - if (GameTicks and $F) <> 0 then - exit; - end - else if (GameTicks and $1FF) <> 0 then - exit; - - if Gear^.Power < 45 then - begin - inc(Gear^.Power); - if TestCollisionYwithGear(hh^.Gear, -1) = 0 then - hh^.Gear^.Y := hh^.Gear^.Y - _1; - end; - - graves := GearsNear(Gear^.X, Gear^.Y, gtGrave, Gear^.Radius); - - if graves.size = 0 then - begin - StopSoundChan(Gear^.SoundChannel); - Gear^.Timer := 250; - Gear^.doStep := @doStepIdle; - exit; - end; - - if ((Gear^.Message and gmAttack) <> 0) and (hh^.Gear^.Health > 0) and (TurnTimeLeft > 0) then - begin - if LongInt(graves.size) <= Gear^.Tag then Gear^.Tag:= 0; - dec(hh^.Gear^.Health); - if (hh^.Gear^.Health = 0) and (hh^.Gear^.Damage = 0) then - hh^.Gear^.Damage:= 1; - RenderHealth(hh^); - RecountTeamHealth(hh^.Team); - inc(graves.ar^[Gear^.Tag]^.Health); - inc(Gear^.Tag) -{-for i:= 0 to High(graves) do begin - if hh^.Gear^.Health > 0 then begin - dec(hh^.Gear^.Health); - inc(graves[i]^.Health); - end; - end; -} - end - else - begin - // now really resurrect the hogs with the hp saved in the graves - for i:= 0 to graves.size - 1 do - if graves.ar^[i]^.Health > 0 then - begin - resgear := AddGear(hwRound(graves.ar^[i]^.X), hwRound(graves.ar^[i]^.Y), gtHedgehog, gstWait, _0, _0, 0); - resgear^.Hedgehog := graves.ar^[i]^.Hedgehog; - resgear^.Health := graves.ar^[i]^.Health; - PHedgehog(graves.ar^[i]^.Hedgehog)^.Gear := resgear; - graves.ar^[i]^.Message:= graves.ar^[i]^.Message or gmDestroy; - graves.ar^[i]^.Active:= true; - RenderHealth(resgear^.Hedgehog^); - RecountTeamHealth(resgear^.Hedgehog^.Team); - resgear^.Hedgehog^.Effects[heResurrected]:= 1; - // only make hat-less hedgehogs look like zombies, preserve existing hats - - if resgear^.Hedgehog^.Hat = 'NoHat' then - LoadHedgehogHat(resgear^.Hedgehog^, 'Reserved/Zombie'); - end; - - hh^.Gear^.dY := _0; - hh^.Gear^.dX := _0; - doStepHedgehogMoving(hh^.Gear); - StopSoundChan(Gear^.SoundChannel); - Gear^.Timer := 250; - Gear^.doStep := @doStepIdle; - end - //if hh^.Gear^.Health = 0 then doStepHedgehogFree(hh^.Gear); -end; - -procedure doStepResurrector(Gear: PGear); -var - graves: PGearArrayS; - hh: PHedgehog; - i: LongInt; -begin - AllInactive := false; - graves := GearsNear(Gear^.X, Gear^.Y, gtGrave, Gear^.Radius); - - if graves.size > 0 then - begin - hh := Gear^.Hedgehog; - for i:= 0 to graves.size - 1 do - begin - PHedgehog(graves.ar^[i]^.Hedgehog)^.Gear := nil; - graves.ar^[i]^.Health := 0; - end; - Gear^.doStep := @doStepResurrectorWork; - if ((Gear^.Message and gmAttack) <> 0) and (hh^.Gear^.Health > 0) and (TurnTimeLeft > 0) then - begin - if LongInt(graves.size) <= Gear^.Tag then Gear^.Tag:= 0; - dec(hh^.Gear^.Health); - if (hh^.Gear^.Health = 0) and (hh^.Gear^.Damage = 0) then - hh^.Gear^.Damage:= 1; - RenderHealth(hh^); - RecountTeamHealth(hh^.Team); - inc(graves.ar^[Gear^.Tag]^.Health); - inc(Gear^.Tag) - end - end - else - begin - StopSoundChan(Gear^.SoundChannel); - Gear^.Timer := 250; - Gear^.doStep := @doStepIdle; - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepNapalmBomb(Gear: PGear); -var - i, gX, gY: LongInt; - dX, dY: hwFloat; -begin - AllInactive := false; - doStepFallingGear(Gear); - if (Gear^.Timer > 0) and ((Gear^.State and gstCollision) <> 0) then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLAutoSound); - gX := hwRound(Gear^.X); - gY := hwRound(Gear^.Y); - for i:= 0 to 10 do - begin - dX := AngleCos(i * 2) * ((_0_1*(i div 5))) * (GetRandomf + _1); - dY := AngleSin(i * 8) * _0_5 * (GetRandomf + _1); - AddGear(gX, gY, gtFlame, 0, dX, dY, 0); - AddGear(gX, gY, gtFlame, 0, dX, -dY, 0); - AddGear(gX, gY, gtFlame, 0, -dX, dY, 0); - AddGear(gX, gY, gtFlame, 0, -dX, -dY, 0); - end; - DeleteGear(Gear); - exit - end; - if (Gear^.Timer = 0) then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLAutoSound); - for i:= -19 to 19 do - FollowGear := AddGear(hwRound(Gear^.X) + i div 3, hwRound(Gear^.Y), gtFlame, 0, _0_001 * i, _0, 0); - DeleteGear(Gear); - exit - end; - if (GameTicks and $3F) = 0 then - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace); - dec(Gear^.Timer) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepStructure(Gear: PGear); -var - x, y: LongInt; - HH: PHedgehog; - t: PGear; -begin - HH:= Gear^.Hedgehog; - - if (Gear^.State and gstMoving) <> 0 then - begin - AddGearCI(Gear); - Gear^.dX:= _0; - Gear^.dY:= _0; - Gear^.State:= Gear^.State and (not gstMoving); - end; - - dec(Gear^.Health, Gear^.Damage); - Gear^.Damage:= 0; - - if Gear^.Pos = 1 then - begin - AddGearCI(Gear); - AfterAttack; - if Gear = CurAmmoGear then - CurAmmoGear:= nil; - if HH^.Gear <> nil then - HideHog(HH); - Gear^.Pos:= 2 - end; - - if Gear^.Pos = 2 then - begin - if ((GameTicks mod 100) = 0) and (Gear^.Timer < 1000) then - begin - if (Gear^.Timer mod 10) = 0 then - begin - DeleteCI(Gear); - Gear^.Y:= Gear^.Y - _0_5; - AddGearCI(Gear); - end; - inc(Gear^.Timer); - end; - if Gear^.Tag <= TotalRounds then - Gear^.Pos:= 3; - end; - - if Gear^.Pos = 3 then - if Gear^.Timer < 1000 then - begin - if (Gear^.Timer mod 10) = 0 then - begin - DeleteCI(Gear); - Gear^.Y:= Gear^.Y - _0_5; - AddGearCI(Gear); - end; - inc(Gear^.Timer); - end - else - begin - if HH^.GearHidden <> nil then - RestoreHog(HH); - Gear^.Pos:= 4; - end; - - if Gear^.Pos = 4 then - if ((GameTicks mod 1000) = 0) and ((GameFlags and gfInvulnerable) = 0) then - begin - t:= GearsList; - while t <> nil do - begin - if (t^.Kind = gtHedgehog) and (t^.Hedgehog^.Team^.Clan = HH^.Team^.Clan) then - t^.Invulnerable:= true; - t:= t^.NextGear; - end; - end; - - if Gear^.Health <= 0 then - begin - if HH^.GearHidden <> nil then - RestoreHog(HH); - - x := hwRound(Gear^.X); - y := hwRound(Gear^.Y); - - DeleteCI(Gear); - DeleteGear(Gear); - - doMakeExplosion(x, y, 50, CurrentHedgehog, EXPLAutoSound); - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -(* - TARDIS needs - Warp in. Pos = 1 - Pause. Pos = 2 - Hide gear (TARDIS hedgehog was nil) - Warp out. Pos = 3 - ... idle active for some time period ... Pos = 4 - Warp in. Pos = 1 - Pause. Pos = 2 - Restore gear (TARDIS hedgehog was not nil) - Warp out. Pos = 3 -*) - -procedure doStepTardisWarp(Gear: PGear); -var HH: PHedgehog; - i,j,cnt: LongWord; -begin -HH:= Gear^.Hedgehog; -if Gear^.Pos = 2 then - begin - StopSoundChan(Gear^.SoundChannel); - if (Gear^.Timer = 0) then - begin - if (HH^.Gear <> nil) and (HH^.Gear^.State and gstInvisible = 0) then - begin - AfterAttack; - if Gear = CurAmmoGear then CurAmmoGear := nil; - if (HH^.Gear^.Damage = 0) and (HH^.Gear^.Health > 0) and - ((Gear^.State and (gstMoving or gstHHDeath or gstHHGone)) = 0) then - HideHog(HH) - end - //else if (HH^.Gear <> nil) and (HH^.Gear^.State and gstInvisible <> 0) then - else if (HH^.GearHidden <> nil) then// and (HH^.Gear^.State and gstInvisible <> 0) then - RestoreHog(HH) - end; - - inc(Gear^.Timer); - if (Gear^.Timer > 2000) and ((GameTicks mod 2000) = 1000) then - begin - Gear^.SoundChannel := LoopSound(sndTardis); - Gear^.Pos:= 3 - end - end; - -if (Gear^.Pos = 1) and (GameTicks and $1F = 0) and (Gear^.Power < 255) then - begin - inc(Gear^.Power); - if (Gear^.Power = 172) and (HH^.Gear <> nil) and - (HH^.Gear^.Damage = 0) and (HH^.Gear^.Health > 0) and - ((HH^.Gear^.State and (gstMoving or gstHHDeath or gstHHGone)) = 0) then - with HH^.Gear^ do - begin - State:= State or gstAnimation; - Tag:= 2; - Timer:= 0; - Pos:= 0 - end - end; -if (Gear^.Pos = 3) and (GameTicks and $1F = 0) and (Gear^.Power > 0) then - dec(Gear^.Power); -if (Gear^.Pos = 1) and (Gear^.Power = 255) and ((GameTicks mod 2000) = 1000) then - Gear^.Pos:= 2; -if (Gear^.Pos = 3) and (Gear^.Power = 0) then - begin - StopSoundChan(Gear^.SoundChannel); - if HH^.GearHidden = nil then - begin - DeleteGear(Gear); - exit - end; - Gear^.Pos:= 4; - // This condition might need tweaking - Gear^.Timer:= GetRandom(cHedgehogTurnTime*TeamsCount)+cHedgehogTurnTime - end; - -if (Gear^.Pos = 4) then - begin - cnt:= 0; - for j:= 0 to Pred(HH^.Team^.Clan^.TeamsNumber) do - for i:= 0 to Pred(HH^.Team^.Clan^.Teams[j]^.HedgehogsNumber) do - if (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear <> nil) - and ((HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.State and gstDrowning) = 0) - and (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Health > HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Damage) then - inc(cnt); - if (cnt = 0) or SuddenDeathDmg or (Gear^.Timer = 0) then - begin - if HH^.GearHidden <> nil then - FindPlace(HH^.GearHidden, false, 0, LAND_WIDTH,true); - - if HH^.GearHidden <> nil then - begin - Gear^.X:= HH^.GearHidden^.X; - Gear^.Y:= HH^.GearHidden^.Y; - end; - Gear^.Timer:= 0; - - if (HH^.GearHidden <> nil) and (cnt = 0) then // do an emergency jump back in this case. the team needs you! - begin - AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtExplosion); - Gear^.Pos:= 2; - Gear^.Power:= 255; - end - else begin - Gear^.SoundChannel := LoopSound(sndTardis); - Gear^.Pos:= 1; - Gear^.Power:= 0; - end - end - else if (CurrentHedgehog^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan) then dec(Gear^.Timer) - end; - -end; - -procedure doStepTardis(Gear: PGear); -var i,j,cnt: LongWord; - HH: PHedgehog; -begin -(* - Conditions for not activating. - 1. Hog is last of his clan - 2. Sudden Death is in play - 3. Hog is a king -*) - HH:= Gear^.Hedgehog; - if HH^.Gear <> nil then - if (HH^.Gear = nil) or (HH^.King) or (SuddenDeathDmg) then - begin - if HH^.Gear <> nil then - begin - HH^.Gear^.Message := HH^.Gear^.Message and (not gmAttack); - HH^.Gear^.State:= HH^.Gear^.State and (not gstAttacking); - end; - PlaySound(sndDenied); - DeleteGear(gear); - exit - end; - cnt:= 0; - for j:= 0 to Pred(HH^.Team^.Clan^.TeamsNumber) do - for i:= 0 to Pred(HH^.Team^.Clan^.Teams[j]^.HedgehogsNumber) do - if (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear <> nil) - and ((HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.State and gstDrowning) = 0) - and (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Health > HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Damage) then - inc(cnt); - if cnt < 2 then - begin - if HH^.Gear <> nil then - begin - HH^.Gear^.Message := HH^.Gear^.Message and (not gmAttack); - HH^.Gear^.State:= HH^.Gear^.State and (not gstAttacking); - end; - PlaySound(sndDenied); - DeleteGear(gear); - exit - end; - Gear^.SoundChannel := LoopSound(sndTardis); - Gear^.doStep:= @doStepTardisWarp -end; - -//////////////////////////////////////////////////////////////////////////////// - -(* -WIP. The ice gun will have the following effects. It has been proposed by sheepluva that it take the appearance of a large freezer -spewing ice cubes. The cubes will be visual gears only. The scatter from them and the impact snow dust should help hide imprecisions in things like the GearsNear effect. -For now we assume a "ray" like a deagle projected out from the gun. -All these effects assume the ray's angle is not changed and that the target type was unchanged over a number of ticks. This is a simplifying assumption for "gun was applying freezing effect to the same target". - * When fired at water a layer of ice textured land is added above the water. - * When fired at non-ice land (land and lfLandMask and not lfIce) the land is overlaid with a thin layer of ice textured land around that point (say, 1 or 2px into land, 1px above). For attractiveness, a slope would probably be needed. - * When fired at a hog (land and $00FF <> 0), while the hog is targetted, the hog's state is set to frozen. As long as the gun is on the hog, a frozen hog sprite creeps up from the feet to the head. If the effect is interrupted before reaching the top, the freezing state is cleared. -A frozen hog will animate differently. To be decided, but possibly in a similar fashion to a grave when it comes to explosions. The hog might (possibly) not be damaged by explosions. This might make freezing potentially useful for friendlies in a bad position. It might be better to allow damage though. -A frozen hog stays frozen for a certain number of turns. Each turn the frozen overlay becomes fainter, until it fades and the hog animates normally again. -*) - - -procedure updateFuel(Gear: PGear); -var - t:LongInt; -begin - t:= Gear^.Health div 10; - if (t <> Gear^.Damage) and ((GameTicks and $3F) = 0) then - begin - Gear^.Damage:= t; - FreeTexture(Gear^.Tex); - Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(t) + - '%', cWhiteColor, fntSmall) - end; - if Gear^.Message and (gmUp or gmDown) <> 0 then - begin - StopSoundChan(Gear^.SoundChannel); - Gear^.SoundChannel:= -1; - if GameTicks mod 40 = 0 then dec(Gear^.Health) - end - else - begin - if Gear^.SoundChannel = -1 then - Gear^.SoundChannel := LoopSound(sndIceBeam); - if GameTicks mod 10 = 0 then dec(Gear^.Health) - end -end; - - -procedure updateTarget(Gear:PGear; newX, newY:HWFloat); -// var -// iter:PGear; -begin - with Gear^ do - begin - dX:= newX; - dY:= newY; - Pos:= 0; - Target.X:= NoPointX; - LastDamage:= nil; - X:= Hedgehog^.Gear^.X; - Y:= Hedgehog^.Gear^.Y; - end; -end; - -procedure doStepIceGun(Gear: PGear); -const iceWaitCollision = 0; -const iceCollideWithGround = 1; -//const iceWaitNextTarget:Longint = 2; -//const iceCollideWithHog:Longint = 4; -const iceCollideWithWater = 5; -//const waterFreezingTime:Longint = 500; -const groundFreezingTime = 1000; -const iceRadius = 32; -const iceHeight = 40; -var - HHGear, iter: PGear; - landRect: TSDL_Rect; - ndX, ndY: hwFloat; - i, t, gX, gY: LongInt; - hogs: PGearArrayS; - vg: PVisualGear; -begin - HHGear := Gear^.Hedgehog^.Gear; - if (Gear^.Message and gmAttack <> 0) or (Gear^.Health = 0) or (HHGear = nil) or (HHGear^.Damage <> 0) or (HHGear^.dX.QWordValue > 4294967) then - begin - StopSoundChan(Gear^.SoundChannel); - DeleteGear(Gear); - AfterAttack; - exit - end; - updateFuel(Gear); - - with Gear^ do - begin - HedgehogChAngle(HHGear); - ndX:= SignAs(AngleSin(HHGear^.Angle), HHGear^.dX) * _4; - ndY:= -AngleCos(HHGear^.Angle) * _4; - if (ndX <> dX) or (ndY <> dY) or - ((Target.X <> NoPointX) and (Target.X and LAND_WIDTH_MASK = 0) and - (Target.Y and LAND_HEIGHT_MASK = 0) and ((Land[Target.Y, Target.X] = 0))) then - begin - updateTarget(Gear, ndX, ndY); - Timer := iceWaitCollision; - end - else - begin - X:= X + dX; - Y:= Y + dY; - gX:= hwRound(X); - gY:= hwRound(Y); - if Target.X = NoPointX then t:= hwRound(hwSqr(X-HHGear^.X)+hwSqr(Y-HHGear^.Y)); - - if Target.X <> NoPointX then - begin - CheckCollision(Gear); - if (State and gstCollision) <> 0 then - begin - if Timer = iceWaitCollision then - begin - Timer := iceCollideWithGround; - Power := GameTicks; - end - end - else if (target.y >= cWaterLine) then - begin - if Timer = iceWaitCollision then - begin - Timer := iceCollideWithWater; - Power := GameTicks; - end; - end; - - if (abs(gX-Target.X) < 2) and (abs(gY-Target.Y) < 2) then - begin - X:= HHGear^.X; - Y:= HHGear^.Y - end; - - if (Timer = iceCollideWithGround) and ((GameTicks - Power) > groundFreezingTime) then - begin - FillRoundInLand(target.x, target.y, iceRadius, icePixel); - landRect.x := min(max(target.x - iceRadius, 0), LAND_WIDTH - 1); - landRect.y := min(max(target.y - iceRadius, 0), LAND_HEIGHT - 1); - landRect.w := min(2*iceRadius, LAND_WIDTH - landRect.x - 1); - landRect.h := min(2*iceRadius, LAND_HEIGHT - landRect.y - 1); - UpdateLandTexture(landRect.x, landRect.w, landRect.y, landRect.h, true); - - // Freeze nearby mines/explosives/cases too - iter := GearsList; - while iter <> nil do - begin - if (iter^.State and gstFrozen = 0) and - ((iter^.Kind = gtExplosives) or (iter^.Kind = gtCase) or (iter^.Kind = gtMine)) and - (abs(iter^.X.Round-target.x)+abs(iter^.Y.Round-target.y)+2<2*iceRadius) and (Distance(iter^.X-int2hwFloat(target.x),iter^.Y-int2hwFloat(target.y)) nil then - begin - i:= random(100) + 155; - vg^.Tint:= (i shl 24) or (i shl 16) or ($FF shl 8) or (random(200) + 55); - vg^.Angle:= random(360); - vg^.dx:= 0.001 * random(80); - vg^.dy:= 0.001 * random(80) - end - end; - PlaySound(sndHogFreeze); - if iter^.Kind = gtMine then // dud mine block - begin - iter^.State:= iter^.State or gstFrozen; - vg:= AddVisualGear(hwRound(iter^.X) - 4 + Random(8), hwRound(iter^.Y) - 4 - Random(4), vgtSmoke); - if vg <> nil then - vg^.Scale:= 0.5; - PlaySound(sndVaporize); - iter^.Health := 0; - iter^.Damage := 0; - iter^.State := iter^.State and (not gstAttacking) - end - else if iter^.Kind = gtCase then - begin - DeleteCI(iter); - iter^.State:= iter^.State or gstFrozen; - AddGearCI(iter) - end - else // gtExplosives - begin - iter^.State:= iter^.State or gstFrozen; - iter^.Health:= iter^.Health + cBarrelHealth - end - end; - iter:= iter^.NextGear - end; - - // FillRoundInLandWithIce(Target.X, Target.Y, iceRadius); - SetAllHHToActive; - Timer := iceWaitCollision; - end; - - if (Timer = iceCollideWithWater) and ((GameTicks - Power) > groundFreezingTime) then - begin - PlaySound(sndHogFreeze); - DrawIceBreak(Target.X, cWaterLine - iceHeight, iceRadius, iceHeight); - SetAllHHToActive; - Timer := iceWaitCollision; - end; -(* - Any ideas for something that would look good here? - if (Target.X <> NoPointX) and ((Timer = iceCollideWithGround) or (Timer = iceCollideWithWater)) and (GameTicks mod max((groundFreezingTime-((GameTicks - Power)*2)),2) = 0) then //and CheckLandValue(Target.X, Target.Y, lfIce) then - begin - vg:= AddVisualGear(Target.X+random(20)-10, Target.Y+random(40)-10, vgtDust, 1); - if vg <> nil then - begin - i:= random(100) + 155; - vg^.Tint:= IceColor or $FF; - vg^.Angle:= random(360); - vg^.dx:= 0.001 * random(80); - vg^.dy:= 0.001 * random(80) - end - end; -*) - -// freeze nearby hogs - hogs := GearsNear(int2hwFloat(Target.X), int2hwFloat(Target.Y), gtHedgehog, Gear^.Radius*2); - if hogs.size > 0 then - for i:= 0 to hogs.size - 1 do - if hogs.ar^[i] <> HHGear then - if GameTicks mod 5 = 0 then - begin - hogs.ar^[i]^.Active:= true; - if hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] < 256 then - hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] := hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] + 1 - else if hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] = 256 then - begin - hogs.ar^[i]^.Hedgehog^.Effects[heFrozen]:= 200000-1;//cHedgehogTurnTime + cReadyDelay - PlaySound(sndHogFreeze); - end; - end; - inc(Pos) - end - else if (t > 400) and ((gY > cWaterLine) or - (((gX and LAND_WIDTH_MASK = 0) and (gY and LAND_HEIGHT_MASK = 0)) - and (Land[gY, gX] <> 0))) then - begin - Target.X:= gX; - Target.Y:= gY; - X:= HHGear^.X; - Y:= HHGear^.Y - end; - if (gX > max(LAND_WIDTH,4096)*2) or - (gX < -max(LAND_WIDTH,4096)) or - (gY < -max(LAND_HEIGHT,4096)) or - (gY > max(LAND_HEIGHT,4096)+512) then - begin - //X:= HHGear^.X; - //Y:= HHGear^.Y - Target.X:= gX; - Target.Y:= gY; - end - end - end; -end; - -procedure doStepAddAmmo(Gear: PGear); -var a: TAmmoType; - gi: PGear; -begin -if Gear^.Timer > 0 then dec(Gear^.Timer) -else - begin - if Gear^.Pos = posCaseUtility then - a:= GetUtility(Gear^.Hedgehog) - else - a:= GetAmmo(Gear^.Hedgehog); - CheckSum:= CheckSum xor GameTicks; - gi := GearsList; - while gi <> nil do - begin - with gi^ do CheckSum:= CheckSum xor X.round xor X.frac xor dX.round xor dX.frac xor Y.round xor Y.frac xor dY.round xor dY.frac; - AddRandomness(CheckSum); - if gi^.Kind = gtGenericFaller then gi^.State:= gi^.State and not gstTmpFlag; - gi := gi^.NextGear - end; - AddPickup(Gear^.Hedgehog^, a, Gear^.Power, hwRound(Gear^.X), hwRound(Gear^.Y)); - DeleteGear(Gear) - end; -end; - -procedure doStepGenericFaller(Gear: PGear); -begin -if Gear^.Timer < $FFFFFFFF then - if Gear^.Timer > 0 then - dec(Gear^.Timer) - else - begin - DeleteGear(Gear); - exit - end; -if (Gear^.State and gstTmpFlag <> 0) or (GameTicks and $7 = 0) then - begin - doStepFallingGear(Gear); - if (Gear^.State and gstInvisible <> 0) and (GameTicks and $FF = 0) and (hwRound(Gear^.X) < LongInt(leftX)) or (hwRound(Gear^.X) > LongInt(rightX)) or (hwRound(Gear^.Y) < LongInt(topY)) then - begin - Gear^.X:= int2hwFloat(GetRandom(rightX-leftX)+leftX); - Gear^.Y:= int2hwFloat(GetRandom(LAND_HEIGHT-topY)+topY); - Gear^.dX:= _90-(GetRandomf*_360); - Gear^.dY:= _90-(GetRandomf*_360) - end; - end -end; - -procedure doStepCreeper(Gear: PGear); -var hogs: PGearArrayS; - HHGear: PGear; - tdX: hwFloat; - dir: LongInt; -begin -doStepFallingGear(Gear); -if Gear^.Timer > 0 then dec(Gear^.Timer); -// creeper sleep phase -if (Gear^.Hedgehog = nil) and (Gear^.Timer > 0) then exit; - -if Gear^.Hedgehog <> nil then HHGear:= Gear^.Hedgehog^.Gear -else HHGear:= nil; - -// creeper boom phase -if (Gear^.State and gstTmpFlag <> 0) then - begin - if (Gear^.Timer = 0) then - begin - doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 300, CurrentHedgehog, EXPLAutoSound); - DeleteGear(Gear) - end; - // ssssss he essssscaped - if (Gear^.Timer > 250) and ((HHGear = nil) or - (((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) > 180) and - (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) > _180))) then - begin - Gear^.State:= Gear^.State and (not gstTmpFlag); - Gear^.Timer:= 0 - end; - exit - end; - -// Search out a new target, as target seek time has expired, target is dead, target is out of range, or we did not have a target -if (HHGear = nil) or (Gear^.Timer = 0) or - (((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) > Gear^.Angle) and - (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) > int2hwFloat(Gear^.Angle))) - then - begin - hogs := GearsNear(Gear^.X, Gear^.Y, gtHedgehog, Gear^.Angle); - if hogs.size > 1 then - Gear^.Hedgehog:= hogs.ar^[GetRandom(hogs.size)]^.Hedgehog - else if hogs.size = 1 then Gear^.Hedgehog:= hogs.ar^[0]^.Hedgehog - else Gear^.Hedgehog:= nil; - if Gear^.Hedgehog <> nil then Gear^.Timer:= 5000; - exit - end; - -// we have a target. move the creeper. -if HHGear <> nil then - begin - // GOTCHA - if ((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) < 50) and - (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) < _50) then - begin - // hisssssssssss - Gear^.State:= Gear^.State or gstTmpFlag; - Gear^.Timer:= 1500; - exit - end; - if (Gear^.State and gstMoving <> 0) then - begin - Gear^.dY:= _0; - Gear^.dX:= _0; - end - else if (GameTicks and $FF = 0) then - begin - tdX:= HHGear^.X-Gear^.X; - dir:= hwSign(tdX); - if not TestCollisionX(Gear, dir) then - Gear^.X:= Gear^.X + signAs(_1,tdX); - if TestCollisionXwithXYShift(Gear, signAs(_10,tdX), 0, dir) then - begin - Gear^.dX:= SignAs(_0_15, tdX); - Gear^.dY:= -_0_3; - Gear^.State:= Gear^.State or gstMoving - end - end; - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepKnife(Gear: PGear); -//var ox, oy: LongInt; -// la: hwFloat; -var a: real; -begin - // Gear is shrunk so it can actually escape the hog without carving into the terrain - if (Gear^.Radius = 4) and (Gear^.CollisionMask = $FFFF) then Gear^.Radius:= 7; - if Gear^.Damage > 100 then Gear^.CollisionMask:= 0 - else if Gear^.Damage > 30 then - if GetRandom(max(4,18-Gear^.Damage div 10)) < 3 then Gear^.CollisionMask:= 0; - Gear^.Damage:= 0; - if Gear^.Timer > 0 then dec(Gear^.Timer); - if (Gear^.State and gstMoving <> 0) and (Gear^.State and gstCollision = 0) then - begin - DeleteCI(Gear); - Gear^.Radius:= 7; - // used for damage and impact calc. needs balancing I think - Gear^.Health:= hwRound(hwSqr((hwAbs(Gear^.dY)+hwAbs(Gear^.dX))*_4)); - doStepFallingGear(Gear); - AllInactive := false; - a:= Gear^.DirAngle; - CalcRotationDirAngle(Gear); - Gear^.DirAngle:= a+(Gear^.DirAngle-a)*2*hwSign(Gear^.dX) // double rotation - end - else if (Gear^.CollisionIndex = -1) and (Gear^.Timer = 0) then - begin - (*ox:= 0; oy:= 0; - if TestCollisionYwithGear(Gear, -1) <> 0 then oy:= -1; - if TestCollisionXwithGear(Gear, 1) then ox:= 1; - if TestCollisionXwithGear(Gear, -1) then ox:= -1; - if TestCollisionYwithGear(Gear, 1) <> 0 then oy:= 1; - if Gear^.Health > 0 then - PlaySound(sndRopeAttach); - - la:= _10000; - if (ox <> 0) or (oy <> 0) then - la:= CalcSlopeNearGear(Gear, ox, oy); - if la = _10000 then - begin - // debug for when we couldn't get an angle - //AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeWhite); -*) - Gear^.DirAngle:= DxDy2Angle(Gear^.dX, Gear^.dY) + (random(30)-15); - if (Gear^.dX.isNegative and Gear^.dY.isNegative) or - ((not Gear^.dX.isNegative) and (not Gear^.dY.isNegative)) then Gear^.DirAngle:= Gear^.DirAngle-90; - // end - // else Gear^.DirAngle:= hwFloat2Float(la)*90; // sheepluva's comment claims 45deg = 0.5 - yet orientation doesn't seem consistent? - // AddFileLog('la: '+floattostr(la)+' DirAngle: '+inttostr(round(Gear^.DirAngle))); - Gear^.dX:= _0; - Gear^.dY:= _0; - Gear^.State:= Gear^.State and (not gstMoving) or gstCollision; - Gear^.Radius:= 16; - if Gear^.Health > 0 then AmmoShove(Gear, Gear^.Health, 0); - Gear^.Health:= 0; - Gear^.Timer:= 500; - AddGearCI(Gear) - end - else if GameTicks and $3F = 0 then - begin - if (TestCollisionYwithGear(Gear, -1) = 0) - and (not TestCollisionXwithGear(Gear, 1)) - and (not TestCollisionXwithGear(Gear, -1)) - and (TestCollisionYwithGear(Gear, 1) = 0) then Gear^.State:= Gear^.State and (not gstCollision) or gstMoving; - end -end; -(* - This didn't end up getting used, but, who knows, might be reasonable for javellin or something -// Make the knife initial angle based on the hog attack angle, or is that too hard? -procedure doStepKnife(Gear: PGear); -var t, - gx, gy, ga, // gear x,y,angle - lx, ly, la, // land x,y,angle - ox, oy, // x,y offset - w, h, // wXh of clip area - tx, ty // tip position in sprite - : LongInt; - surf: PSDL_Surface; - s: hwFloat; - -begin - Gear^.dY := Gear^.dY + cGravity; - if (GameFlags and gfMoreWind) <> 0 then - Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density; - Gear^.X := Gear^.X + Gear^.dX; - Gear^.Y := Gear^.Y + Gear^.dY; - CheckGearDrowning(Gear); - gx:= hwRound(Gear^.X); - gy:= hwRound(Gear^.Y); - if Gear^.State and gstDrowning <> 0 then exit; - with Gear^ do - begin - if CheckLandValue(gx, gy, lfLandMask) then - begin - t:= Angle + hwRound((hwAbs(dX)+hwAbs(dY)) * _10); - - if t < 0 then inc(t, 4096) - else if 4095 < t then dec(t, 4096); - Angle:= t; - - DirAngle:= Angle / 4096 * 360 - end - else - begin -//This is the set of postions for the knife. -//Using FlipSurface and copyToXY the knife can be written to the LandPixels at 32 positions, and an appropriate line drawn in Land. - t:= Angle mod 1024; - case t div 128 of - 0: begin - ox:= 2; oy:= 5; - w := 25; h:= 5; - tx:= 0; ty:= 2 - end; - 1: begin - ox:= 2; oy:= 15; - w:= 24; h:= 8; - tx:= 0; ty:= 7 - end; - 2: begin - ox:= 2; oy:= 27; - w:= 23; h:= 12; - tx:= -12; ty:= -5 - end; - 3: begin - ox:= 2; oy:= 43; - w:= 21; h:= 15; - tx:= 0; ty:= 14 - end; - 4: begin - ox:= 29; oy:= 8; - w:= 19; h:= 19; - tx:= 0; ty:= 17 - end; - 5: begin - ox:= 29; oy:= 32; - w:= 15; h:= 21; - tx:= 0; ty:= 20 - end; - 6: begin - ox:= 51; oy:= 3; - w:= 11; h:= 23; - tx:= 0; ty:= 22 - end; - 7: begin - ox:= 51; oy:= 34; - w:= 7; h:= 24; - tx:= 0; ty:= 23 - end - end; - - surf:= SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, RMask, GMask, BMask, AMask); - copyToXYFromRect(SpritesData[sprKnife].Surface, surf, ox, oy, w, h, 0, 0); - // try to make the knife hit point first - lx := 0; - ly := 0; - if CalcSlopeTangent(Gear, gx, gy, lx, ly, 255) then - begin - la:= vector2Angle(int2hwFloat(lx), int2hwFloat(ly)); - ga:= vector2Angle(dX, dY); - AddFileLog('la: '+inttostr(la)+' ga: '+inttostr(ga)+' Angle: '+inttostr(Angle)); - // change to 0 to 4096 forced by LongWord in Gear - if la < 0 then la:= 4096+la; - if ga < 0 then ga:= 4096+ga; - if ((Angle > ga) and (Angle < la)) or ((Angle < ga) and (Angle > la)) then - begin - if Angle >= 2048 then dec(Angle, 2048) - else if Angle < 2048 then inc(Angle, 2048) - end; - AddFileLog('la: '+inttostr(la)+' ga: '+inttostr(ga)+' Angle: '+inttostr(Angle)) - end; - case Angle div 1024 of - 0: begin - flipSurface(surf, true); - flipSurface(surf, true); - BlitImageAndGenerateCollisionInfo(gx-(w-tx), gy-(h-ty), w, surf) - end; - 1: begin - flipSurface(surf, false); - BlitImageAndGenerateCollisionInfo(gx-(w-tx), gy-ty, w, surf) - end; - 2: begin // knife was actually drawn facing this way... - BlitImageAndGenerateCollisionInfo(gx-tx, gy-ty, w, surf) - end; - 3: begin - flipSurface(surf, true); - BlitImageAndGenerateCollisionInfo(gx-tx, gy-(h-ty), w, surf) - end - end; - SDL_FreeSurface(surf); - // this needs to calculate actual width/height + land clipping since update texture doesn't. - // i.e. this will crash if you fire near sides of map, but until I get the blit right, not going to put real values - UpdateLandTexture(hwRound(X)-32, 64, hwRound(Y)-32, 64, true); - DeleteGear(Gear); - exit - end - end; -end; -*) diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/VGSHandlers.inc --- a/hedgewars/VGSHandlers.inc Wed Jun 26 21:40:10 2013 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,802 +0,0 @@ -(* - * Hedgewars, a free turn based strategy game - * Copyright (c) 2004-2013 Andrey Korotaev - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA - *) - -(* - * This file contains the step handlers for visual gears. - * - * Since the effects of visual gears do not affect the course of the game, - * no "synchronization" between players is required. - * => The usage of safe functions or data types (e.g. GetRandom() or hwFloat) - * is usually not necessary and therefore undesirable. - *) - -procedure doStepFlake(Gear: PVisualGear; Steps: Longword); -var sign: real; - moved: boolean; -begin -if vobCount = 0 then exit; - -sign:= 1; -with Gear^ do - begin - inc(FrameTicks, Steps); - if not SuddenDeathDmg and (FrameTicks > vobFrameTicks) then - begin - dec(FrameTicks, vobFrameTicks); - inc(Frame); - if Frame = vobFramesCount then - Frame:= 0 - end - else if SuddenDeathDmg and (FrameTicks > vobSDFrameTicks) then - begin - dec(FrameTicks, vobSDFrameTicks); - inc(Frame); - if Frame = vobSDFramesCount then - Frame:= 0 - end; - X:= X + (cWindSpeedf * 400 + dX + tdX) * Steps * Gear^.Scale; - if SuddenDeathDmg then - Y:= Y + (dY + tdY + cGravityf * vobSDFallSpeed) * Steps * Gear^.Scale - else - Y:= Y + (dY + tdY + cGravityf * vobFallSpeed) * Steps * Gear^.Scale; - Angle:= Angle + dAngle * Steps; - if Angle > 360 then - Angle:= Angle - 360 - else - if Angle < - 360 then - Angle:= Angle + 360; - - - if (round(X) >= cLeftScreenBorder) - and (round(X) <= cRightScreenBorder) - and (round(Y) - 75 <= LAND_HEIGHT) - and (Timer > 0) and (Timer-Steps > 0) then - begin - if tdX > 0 then - sign := 1 - else - sign:= -1; - tdX:= tdX - 0.005*Steps*sign; - if ((sign < 0) and (tdX > 0)) or ((sign > 0) and (tdX < 0)) then - tdX:= 0; - if tdX > 0 then - sign := 1 - else - sign:= -1; - tdY:= tdY - 0.005*Steps*sign; - if ((sign < 0) and (tdY > 0)) or ((sign > 0) and (tdY < 0)) then - tdY:= 0; - dec(Timer, Steps) - end - else - begin - moved:= false; - if round(X) < cLeftScreenBorder then - begin - X:= X + cScreenSpace; - moved:= true - end - else - if round(X) > cRightScreenBorder then - begin - X:= X - cScreenSpace; - moved:= true - end; - // if round(Y) < (LAND_HEIGHT - 1024 - 75) then Y:= Y + 25.0; // For if flag is set for flakes rising upwards? - if (Gear^.Layer = 2) and (round(Y) - 225 > LAND_HEIGHT) then - begin - X:= cLeftScreenBorder + random(cScreenSpace); - Y:= Y - (1024 + 250 + random(50)); // TODO - configure in theme (jellies for example could use limited range) - moved:= true - end - else if (Gear^.Layer <> 2) and (round(Y) + 50 > LAND_HEIGHT) then - begin - X:= cLeftScreenBorder + random(cScreenSpace); - Y:= Y - (1024 + random(25)); - moved:= true - end; - if moved then - begin - Angle:= random(360); - dx:= 0.0000038654705 * random(10000); - dy:= 0.000003506096 * random(7000); - if random(2) = 0 then dx := -dx - end; - Timer:= 0; - tdX:= 0; - tdY:= 0 - end; - end; - -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepBeeTrace(Gear: PVisualGear; Steps: Longword); -begin -if Gear^.FrameTicks > Steps then - dec(Gear^.FrameTicks, Steps) -else - DeleteVisualGear(Gear); -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepCloud(Gear: PVisualGear; Steps: Longword); -var s: Longword; - t: real; -begin -Gear^.X:= Gear^.X + (cWindSpeedf * 750 * Gear^.dX * Gear^.Scale) * Steps; - -// up-and-down-bounce magic -s := (GameTicks + Gear^.Timer) mod 4096; -t := 8 * Gear^.Scale * hwFloat2Float(AngleSin(s mod 2048)); -if (s < 2048) then t := -t; - -Gear^.Y := LAND_HEIGHT - 1184 + LongInt(Gear^.Timer mod 8) + t; - -if round(Gear^.X) < cLeftScreenBorder then - Gear^.X:= Gear^.X + cScreenSpace -else - if round(Gear^.X) > cRightScreenBorder then - Gear^.X:= Gear^.X - cScreenSpace -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepExpl(Gear: PVisualGear; Steps: Longword); -var s: LongInt; -begin -s:= min(Steps, cExplFrameTicks); - -Gear^.X:= Gear^.X + Gear^.dX * s; -Gear^.Y:= Gear^.Y + Gear^.dY * s; -//Gear^.dY:= Gear^.dY + cGravityf; - -if Gear^.FrameTicks <= Steps then - if Gear^.Frame = 0 then - DeleteVisualGear(Gear) - else - begin - dec(Gear^.Frame); - Gear^.FrameTicks:= cExplFrameTicks - end - else dec(Gear^.FrameTicks, Steps) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepNote(Gear: PVisualGear; Steps: Longword); -begin -Gear^.X:= Gear^.X + Gear^.dX * Steps; - -Gear^.Y:= Gear^.Y + Gear^.dY * Steps; -Gear^.dY:= Gear^.dY + cGravityf * Steps / 2; - -Gear^.Angle:= Gear^.Angle + (Gear^.Frame + 1) * Steps / 10; -while Gear^.Angle > cMaxAngle do - Gear^.Angle:= Gear^.Angle - cMaxAngle; - -if Gear^.FrameTicks <= Steps then - DeleteVisualGear(Gear) -else - dec(Gear^.FrameTicks, Steps) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepLineTrail(Gear: PVisualGear; Steps: Longword); -begin -Steps := Steps; -if Gear^.Timer <= Steps then - DeleteVisualGear(Gear) -else - dec(Gear^.Timer, Steps) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepEgg(Gear: PVisualGear; Steps: Longword); -begin -Gear^.X:= Gear^.X + Gear^.dX * Steps; - -Gear^.Y:= Gear^.Y + Gear^.dY * Steps; -Gear^.dY:= Gear^.dY + cGravityf * Steps; - -Gear^.Angle:= round(Gear^.Angle + Steps) mod cMaxAngle; - -if Gear^.FrameTicks <= Steps then - begin - DeleteVisualGear(Gear); - exit - end -else - dec(Gear^.FrameTicks, Steps); - -if Gear^.FrameTicks < $FF then - Gear^.Tint:= (Gear^.Tint and $FFFFFF00) or Gear^.FrameTicks -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepFire(Gear: PVisualGear; Steps: Longword); -var vgt: PVisualGear; -begin -Gear^.X:= Gear^.X + Gear^.dX * Steps; - -Gear^.Y:= Gear^.Y + Gear^.dY * Steps;// + cGravityf * (Steps * Steps); -if (Gear^.State and gstTmpFlag) = 0 then - begin - Gear^.dY:= Gear^.dY + cGravityf * Steps; - if ((GameTicks mod 200) < Steps + 1) then - begin - vgt:= AddVisualGear(round(Gear^.X), round(Gear^.Y), vgtFire); - if vgt <> nil then - begin - vgt^.dx:= 0; - vgt^.dy:= 0; - vgt^.State:= gstTmpFlag; - end; - end - end -else - inc(Steps, Steps); - -if Gear^.FrameTicks <= Steps then - DeleteVisualGear(Gear) -else - dec(Gear^.FrameTicks, Steps) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepShell(Gear: PVisualGear; Steps: Longword); -begin -Gear^.X:= Gear^.X + Gear^.dX * Steps; - -Gear^.Y:= Gear^.Y + Gear^.dY * Steps; -Gear^.dY:= Gear^.dY + cGravityf * Steps; - -Gear^.Angle:= round(Gear^.Angle + Steps) mod cMaxAngle; - -if Gear^.FrameTicks <= Steps then - DeleteVisualGear(Gear) -else - dec(Gear^.FrameTicks, Steps) -end; - -procedure doStepSmallDamage(Gear: PVisualGear; Steps: Longword); -begin -Gear^.Y:= Gear^.Y - 0.02 * Steps; - -if Gear^.FrameTicks <= Steps then - DeleteVisualGear(Gear) -else - dec(Gear^.FrameTicks, Steps) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepBubble(Gear: PVisualGear; Steps: Longword); -begin -Gear^.X:= Gear^.X + Gear^.dX * Steps; -Gear^.Y:= Gear^.Y + Gear^.dY * Steps; -Gear^.Y:= Gear^.Y - cDrownSpeedf * Steps; -Gear^.dX := Gear^.dX / (1.001 * Steps); -Gear^.dY := Gear^.dY / (1.001 * Steps); - -if (Gear^.FrameTicks <= Steps) or (round(Gear^.Y) < cWaterLine) then - DeleteVisualGear(Gear) -else - dec(Gear^.FrameTicks, Steps) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSteam(Gear: PVisualGear; Steps: Longword); -begin -Gear^.X:= Gear^.X + (cWindSpeedf * 100 + Gear^.dX) * Steps; -Gear^.Y:= Gear^.Y - cDrownSpeedf * Steps; - -if Gear^.FrameTicks <= Steps then - if Gear^.Frame = 0 then - DeleteVisualGear(Gear) - else - begin - if Random(2) = 0 then - dec(Gear^.Frame); - Gear^.FrameTicks:= cExplFrameTicks - end -else dec(Gear^.FrameTicks, Steps) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepAmmo(Gear: PVisualGear; Steps: Longword); -begin -Gear^.Y:= Gear^.Y - cDrownSpeedf * Steps; - -Gear^.scale:= Gear^.scale + 0.0025 * Steps; -Gear^.alpha:= Gear^.alpha - 0.0015 * Steps; - -if Gear^.alpha < 0 then - DeleteVisualGear(Gear) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSmoke(Gear: PVisualGear; Steps: Longword); -begin -Gear^.X:= Gear^.X + (cWindSpeedf + Gear^.dX) * Steps; -Gear^.Y:= Gear^.Y - (cDrownSpeedf + Gear^.dY) * Steps; - -Gear^.dX := Gear^.dX + (cWindSpeedf * 0.3 * Steps); -//Gear^.dY := Gear^.dY - (cDrownSpeedf * 0.995); - -if Gear^.FrameTicks <= Steps then - if Gear^.Frame = 0 then - DeleteVisualGear(Gear) - else - begin - if Random(2) = 0 then - dec(Gear^.Frame); - Gear^.FrameTicks:= cExplFrameTicks - end - else dec(Gear^.FrameTicks, Steps) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepDust(Gear: PVisualGear; Steps: Longword); -begin -Gear^.X:= Gear^.X + (cWindSpeedf + (cWindSpeedf * 0.03 * Steps) + Gear^.dX) * Steps; -Gear^.Y:= Gear^.Y - (Gear^.dY) * Steps; - -Gear^.dX := Gear^.dX - (Gear^.dX * 0.005 * Steps); -Gear^.dY := Gear^.dY - (cDrownSpeedf * 0.001 * Steps); - -if Gear^.FrameTicks <= Steps then - if Gear^.Frame = 0 then - DeleteVisualGear(Gear) - else - begin - dec(Gear^.Frame); - Gear^.FrameTicks:= cExplFrameTicks - end - else dec(Gear^.FrameTicks, Steps) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSplash(Gear: PVisualGear; Steps: Longword); -begin -if Gear^.FrameTicks <= Steps then - DeleteVisualGear(Gear) -else - dec(Gear^.FrameTicks, Steps); -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepDroplet(Gear: PVisualGear; Steps: Longword); -begin -Gear^.X:= Gear^.X + Gear^.dX * Steps; - -Gear^.Y:= Gear^.Y + Gear^.dY * Steps; -Gear^.dY:= Gear^.dY + cGravityf * Steps; - -if round(Gear^.Y) > cWaterLine then - begin - DeleteVisualGear(Gear); - PlaySound(TSound(ord(sndDroplet1) + Random(3))); - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSmokeRing(Gear: PVisualGear; Steps: Longword); -begin -inc(Gear^.Timer, Steps); -if Gear^.Timer >= Gear^.FrameTicks then - DeleteVisualGear(Gear) -else - begin - Gear^.scale := 1.25 * (-power(2, -10 * Int(Gear^.Timer)/Gear^.FrameTicks) + 1) + 0.4; - Gear^.alpha := 1 - power(Gear^.Timer / 350, 4); - if Gear^.alpha < 0 then - Gear^.alpha:= 0; - end; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepFeather(Gear: PVisualGear; Steps: Longword); -begin -Gear^.X:= Gear^.X + Gear^.dX * Steps; - -Gear^.Y:= Gear^.Y + Gear^.dY * Steps; -Gear^.dY:= Gear^.dY + cGravityf * Steps; - -Gear^.Angle:= round(Gear^.Angle + Steps) mod cMaxAngle; - -if Gear^.FrameTicks <= Steps then - DeleteVisualGear(Gear) -else - dec(Gear^.FrameTicks, Steps) -end; - -//////////////////////////////////////////////////////////////////////////////// -const cSorterWorkTime = 640; -var thexchar: array[0..cMaxTeams] of - record - dy, ny, dw: LongInt; - team: PTeam; - SortFactor: QWord; - end; - currsorter: PVisualGear = nil; - -procedure doStepTeamHealthSorterWork(Gear: PVisualGear; Steps: Longword); -var i, t: LongInt; -begin -for t:= 1 to min(Steps, Gear^.Timer) do - begin - dec(Gear^.Timer); - if (Gear^.Timer and 15) = 0 then - for i:= 0 to Pred(TeamsCount) do - with thexchar[i] do - begin - {$WARNINGS OFF} - team^.DrawHealthY:= ny + dy * LongInt(Gear^.Timer) div cSorterWorkTime; - team^.TeamHealthBarWidth:= team^.NewTeamHealthBarWidth + dw * LongInt(Gear^.Timer) div cSorterWorkTime; - {$WARNINGS ON} - end; - end; - -if (Gear^.Timer = 0) or (currsorter <> Gear) then - begin - if currsorter = Gear then - currsorter:= nil; - DeleteVisualGear(Gear); - exit - end -end; - -procedure doStepTeamHealthSorter(Gear: PVisualGear; Steps: Longword); -var i: Longword; - b: boolean; - t: LongInt; -begin -Steps:= Steps; // avoid compiler hint - -for t:= 0 to Pred(TeamsCount) do - with thexchar[t] do - begin - team:= TeamsArray[t]; - dy:= team^.DrawHealthY; - dw:= team^.TeamHealthBarWidth - team^.NewTeamHealthBarWidth; - if team^.TeamHealth > 0 then - begin - SortFactor:= team^.Clan^.ClanHealth; - SortFactor:= (SortFactor shl 3) + team^.Clan^.ClanIndex; - SortFactor:= (SortFactor shl 30) + team^.TeamHealth; - end - else - SortFactor:= 0; - end; - -if TeamsCount > 1 then - repeat - b:= true; - for t:= 0 to TeamsCount - 2 do - if (thexchar[t].SortFactor > thexchar[Succ(t)].SortFactor) then - begin - thexchar[cMaxTeams]:= thexchar[t]; - thexchar[t]:= thexchar[Succ(t)]; - thexchar[Succ(t)]:= thexchar[cMaxTeams]; - b:= false - end - until b; - -t:= - 4; -for i:= 0 to Pred(TeamsCount) do - with thexchar[i] do - if team^.TeamHealth > 0 then - begin - dec(t, team^.HealthTex^.h + 2); - ny:= t; - dy:= dy - ny - end; - -Gear^.Timer:= cSorterWorkTime; -Gear^.doStep:= @doStepTeamHealthSorterWork; -currsorter:= Gear; -//doStepTeamHealthSorterWork(Gear, Steps) -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSpeechBubbleWork(Gear: PVisualGear; Steps: Longword); -begin -if Gear^.Timer > Steps then dec(Gear^.Timer, Steps) else Gear^.Timer:= 0; - -if (Gear^.Hedgehog^.Gear <> nil) then - begin - Gear^.X:= hwFloat2Float(Gear^.Hedgehog^.Gear^.X) + (Gear^.Tex^.w div 2 - Gear^.FrameTicks); - Gear^.Y:= hwFloat2Float(Gear^.Hedgehog^.Gear^.Y) - (16 + Gear^.Tex^.h); - end; - -if Gear^.Timer = 0 then - begin - if Gear^.Hedgehog^.SpeechGear = Gear then - Gear^.Hedgehog^.SpeechGear:= nil; - DeleteVisualGear(Gear) - end; -end; - -procedure doStepSpeechBubble(Gear: PVisualGear; Steps: Longword); -begin -Steps:= Steps; // avoid compiler hint - -with Gear^.Hedgehog^ do - if SpeechGear <> nil then - SpeechGear^.Timer:= 0; - -Gear^.Hedgehog^.SpeechGear:= Gear; - -Gear^.Timer:= max(LongInt(Length(Gear^.Text)) * 150, 3000); - -Gear^.Tex:= RenderSpeechBubbleTex(Gear^.Text, Gear^.FrameTicks, fnt16); - -case Gear^.FrameTicks of - 1: Gear^.FrameTicks:= SpritesData[sprSpeechTail].Width-28; - 2: Gear^.FrameTicks:= SpritesData[sprThoughtTail].Width-20; - 3: Gear^.FrameTicks:= SpritesData[sprShoutTail].Width-10; - end; - -Gear^.doStep:= @doStepSpeechBubbleWork; - -Gear^.Y:= Gear^.Y - Gear^.Tex^.h -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepHealthTagWork(Gear: PVisualGear; Steps: Longword); -begin -if Steps > Gear^.Timer then - DeleteVisualGear(Gear) -else - begin - dec(Gear^.Timer, Steps); - Gear^.Y:= Gear^.Y + Gear^.dY * Steps; - Gear^.X:= Gear^.X + Gear^.dX * Steps - end; -end; - -procedure doStepHealthTagWorkUnderWater(Gear: PVisualGear; Steps: Longword); -begin -if round(Gear^.Y) - 10 < cWaterLine then - DeleteVisualGear(Gear) -else - Gear^.Y:= Gear^.Y - 0.08 * Steps; - -end; - -procedure doStepHealthTag(Gear: PVisualGear; Steps: Longword); -var s: shortstring; -begin -s:= ''; - -str(Gear^.State, s); -if Gear^.Hedgehog <> nil then - Gear^.Tex:= RenderStringTex(s, Gear^.Hedgehog^.Team^.Clan^.Color, fnt16) -else - Gear^.Tex:= RenderStringTex(s, cWhiteColor, fnt16); - -Gear^.doStep:= @doStepHealthTagWork; - -if (round(Gear^.Y) > cWaterLine) and (Gear^.Frame = 0) then - Gear^.doStep:= @doStepHealthTagWorkUnderWater; - -Gear^.Y:= Gear^.Y - Gear^.Tex^.h; - -if Steps > 1 then - Gear^.doStep(Gear, Steps-1); -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSmokeTrace(Gear: PVisualGear; Steps: Longword); -begin -inc(Gear^.Timer, Steps ); -if Gear^.Timer > 64 then - begin - if Gear^.State = 0 then - begin - DeleteVisualGear(Gear); - exit; - end; - dec(Gear^.State, Gear^.Timer div 65); - Gear^.Timer:= Gear^.Timer mod 65; - end; -Gear^.dX:= Gear^.dX + cWindSpeedf * Steps; -Gear^.X:= Gear^.X + Gear^.dX; -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepExplosionWork(Gear: PVisualGear; Steps: Longword); -begin -inc(Gear^.Timer, Steps); -if Gear^.Timer > 75 then - begin - inc(Gear^.State, Gear^.Timer div 76); - Gear^.Timer:= Gear^.Timer mod 76; - if Gear^.State > 5 then - DeleteVisualGear(Gear); - end; -end; - -procedure doStepExplosion(Gear: PVisualGear; Steps: Longword); -var i: LongWord; - gX,gY: LongInt; - vg: PVisualGear; -begin -gX:= round(Gear^.X); -gY:= round(Gear^.Y); -for i:= 0 to 31 do - begin - vg:= AddVisualGear(gX, gY, vgtFire); - if vg <> nil then - begin - vg^.State:= gstTmpFlag; - inc(vg^.FrameTicks, vg^.FrameTicks) - end - end; -for i:= 0 to 8 do AddVisualGear(gX, gY, vgtExplPart); -for i:= 0 to 8 do AddVisualGear(gX, gY, vgtExplPart2); -Gear^.doStep:= @doStepExplosionWork; -if Steps > 1 then - Gear^.doStep(Gear, Steps-1); -end; - - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepBigExplosionWork(Gear: PVisualGear; Steps: Longword); -var maxMovement: LongInt; -begin - -inc(Gear^.Timer, Steps); -if (Gear^.Timer and 5) = 0 then - begin - maxMovement := max(1, 13 - ((Gear^.Timer * 15) div 250)); - ShakeCamera(maxMovement); - end; - -if Gear^.Timer > 250 then - DeleteVisualGear(Gear); -end; - -procedure doStepBigExplosion(Gear: PVisualGear; Steps: Longword); -var i: LongWord; - gX,gY: LongInt; - vg: PVisualGear; -begin -//ScreenFade:= sfFromWhite; -//ScreenFadeValue:= round(60 * zoom * zoom); -//ScreenFadeSpeed:= 5; -gX:= round(Gear^.X); -gY:= round(Gear^.Y); -AddVisualGear(gX, gY, vgtSmokeRing); -for i:= 0 to 46 do - begin - vg:= AddVisualGear(gX, gY, vgtFire); - if vg <> nil then - begin - vg^.State:= gstTmpFlag; - inc(vg^.FrameTicks, vg^.FrameTicks) - end - end; -for i:= 0 to 15 do - AddVisualGear(gX, gY, vgtExplPart); -for i:= 0 to 15 do - AddVisualGear(gX, gY, vgtExplPart2); -Gear^.doStep:= @doStepBigExplosionWork; -if Steps > 1 then - Gear^.doStep(Gear, Steps-1); -with mobileRecord do - if (performRumble <> nil) and (not fastUntilLag) then - performRumble(kSystemSoundID_Vibrate); -end; - -procedure doStepChunk(Gear: PVisualGear; Steps: Longword); -begin -Gear^.X:= Gear^.X + Gear^.dX * Steps; - -Gear^.Y:= Gear^.Y + Gear^.dY * Steps; -Gear^.dY:= Gear^.dY + cGravityf * Steps; - -Gear^.Angle:= round(Gear^.Angle + Steps) mod cMaxAngle; - -if (round(Gear^.Y) > cWaterLine) and ((cReducedQuality and rqPlainSplash) = 0) then - begin - AddVisualGear(round(Gear^.X), round(Gear^.Y), vgtDroplet); - DeleteVisualGear(Gear); - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepBulletHit(Gear: PVisualGear; Steps: Longword); -begin -if Gear^.FrameTicks <= Steps then - DeleteVisualGear(Gear) -else - dec(Gear^.FrameTicks, Steps); -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepCircle(Gear: PVisualGear; Steps: Longword); -var tmp: LongInt; - i: LongWord; -begin -with Gear^ do - if Frame <> 0 then - for i:= 1 to Steps do - begin - inc(FrameTicks); - if (FrameTicks mod Frame) = 0 then - begin - tmp:= Gear^.Tint and $FF; - if tdY >= 0 then - inc(tmp) - else - dec(tmp); - if tmp < round(dX) then - tdY:= 1; - if tmp > round(dY) then - tdY:= -1; - if tmp > 255 then - tmp := 255; - if tmp < 0 then - tmp := 0; - Gear^.Tint:= (Gear^.Tint and $FFFFFF00) or Longword(tmp) - end - end -end; - -//////////////////////////////////////////////////////////////////////////////// -procedure doStepSmoothWindBar(Gear: PVisualGear; Steps: Longword); -begin -inc(Gear^.Timer, Steps); - -while Gear^.Timer >= 10 do - begin - dec(Gear^.Timer, 10); - if WindBarWidth < Gear^.Tag then - inc(WindBarWidth) - else if WindBarWidth > Gear^.Tag then - dec(WindBarWidth); - end; -if cWindspeedf > Gear^.dAngle then - begin - cWindspeedf := cWindspeedf - Gear^.Angle*Steps; - if cWindspeedf < Gear^.dAngle then cWindspeedf:= Gear^.dAngle; - end -else if cWindspeedf < Gear^.dAngle then - begin - cWindspeedf := cWindspeedf + Gear^.Angle*Steps; - if cWindspeedf > Gear^.dAngle then cWindspeedf:= Gear^.dAngle; - end; - -if (WindBarWidth = Gear^.Tag) and (cWindspeedf = Gear^.dAngle) then - DeleteVisualGear(Gear) -end; -//////////////////////////////////////////////////////////////////////////////// -procedure doStepStraightShot(Gear: PVisualGear; Steps: Longword); -begin -Gear^.X:= Gear^.X + Gear^.dX * Steps; -Gear^.Y:= Gear^.Y - Gear^.dY * Steps; - -if Gear^.FrameTicks <= Steps then - DeleteVisualGear(Gear) -else - begin - dec(Gear^.FrameTicks, Steps); - if (Gear^.FrameTicks < 501) and (Gear^.FrameTicks mod 5 = 0) then - Gear^.Tint:= (Gear^.Tint and $FFFFFF00) or (((Gear^.Tint and $000000FF) * Gear^.FrameTicks) div 500) - end -end; - diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/hwengine.pas --- a/hedgewars/hwengine.pas Wed Jun 26 21:40:10 2013 -0400 +++ b/hedgewars/hwengine.pas Thu Jun 27 15:51:20 2013 +0400 @@ -32,14 +32,12 @@ uses SDLh, uMisc, uConsole, uGame, uConsts, uLand, uAmmos, uVisualGears, uGears, uStore, uWorld, uInputHandler , uSound, uScript, uTeams, uStats, uIO, uLocale, uChat, uAI, uAIMisc, uAILandMarks, uLandTexture, uCollisions , SysUtils, uTypes, uVariables, uCommands, uUtils, uCaptions, uDebug, uCommandHandlers, uLandPainted - , uPhysFSLayer, uCursor, uRandom + , uPhysFSLayer, uCursor, uRandom, ArgParsers, uVisualGearsHandlers {$IFDEF USE_VIDEO_RECORDING}, uVideoRec {$ENDIF} {$IFDEF USE_TOUCH_INTERFACE}, uTouch {$ENDIF} {$IFDEF ANDROID}, GLUnit{$ENDIF} ; -var isInternal: Boolean; - {$IFDEF HWLIBRARY} procedure preInitEverything(); procedure initEverything(complete:boolean); @@ -54,8 +52,6 @@ procedure freeEverything(complete:boolean); forward; {$ENDIF} -{$INCLUDE "ArgParsers.inc"} - /////////////////////////////////////////////////////////////////////////////// function DoTimer(Lag: LongInt): boolean; var s: shortstring; @@ -461,6 +457,7 @@ uStore.initModule; uTeams.initModule; uVisualGears.initModule; + uVisualGearsHandlers.initModule; uWorld.initModule; end; end; diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/uGame.pas --- a/hedgewars/uGame.pas Wed Jun 26 21:40:10 2013 -0400 +++ b/hedgewars/uGame.pas Thu Jun 27 15:51:20 2013 +0400 @@ -27,7 +27,7 @@ implementation //////////////////// uses uInputHandler, uTeams, uIO, uAI, uGears, uSound, uLocale, uCaptions, - uVisualGears, uTypes, uVariables, uCommands, uConsts + uVisualGears, uTypes, uVariables, uCommands, uConsts, uVisualGearsList {$IFDEF USE_TOUCH_INTERFACE}, uTouch{$ENDIF}; procedure DoGameTick(Lag: LongInt); diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/uGears.pas --- a/hedgewars/uGears.pas Wed Jun 26 21:40:10 2013 -0400 +++ b/hedgewars/uGears.pas Thu Jun 27 15:51:20 2013 +0400 @@ -33,60 +33,37 @@ * effects are called "Visual Gears" and defined in the respective unit! *) interface -uses SDLh, uConsts, uFloat, uTypes; +uses uConsts, uFloat, uTypes; procedure initModule; procedure freeModule; function SpawnCustomCrateAt(x, y: LongInt; crate: TCrateType; content, cnt: Longword): PGear; function SpawnFakeCrateAt(x, y: LongInt; crate: TCrateType; explode: boolean; poison: boolean ): PGear; -function GetAmmo(Hedgehog: PHedgehog): TAmmoType; -function GetUtility(Hedgehog: PHedgehog): TAmmoType; -procedure HideHog(HH: PHedgehog); procedure ProcessGears; procedure EndTurnCleanup; -procedure SetAllToActive; -procedure SetAllHHToActive; inline; -procedure SetAllHHToActive(Ice: boolean); procedure DrawGears; procedure FreeGearsList; procedure AddMiscGears; procedure AssignHHCoords; function GearByUID(uid : Longword) : PGear; -procedure doStepDrowningGear(Gear: PGear); - implementation -uses uStore, uSound, uTeams, uRandom, uCollisions, uIO, uLandGraphics, {$IFDEF SDL13}uTouch,{$ENDIF} +uses uStore, uSound, uTeams, uRandom, uIO, uLandGraphics, {$IFDEF SDL13}uTouch,{$ENDIF} uLocale, uAI, uAmmos, uStats, uVisualGears, uScript, GLunit, uVariables, uCommands, uUtils, uTextures, uRenderUtils, uGearsRender, uCaptions, uDebug, uLandTexture, - uGearsHedgehog, uGearsUtils, uGearsList, uGearsHandlers, uGearsHandlersRope; + uGearsHedgehog, uGearsUtils, uGearsList, uGearsHandlersRope + , uVisualGearsList, uGearsHandlersMess; var skipFlag: boolean; -procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); forward; -//procedure AmmoFlameWork(Ammo: PGear); forward; -function GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): PGearArrayS; forward; -procedure SpawnBoxOfSmth; forward; -procedure ShotgunShot(Gear: PGear); forward; -procedure doStepCase(Gear: PGear); forward; - - var delay: LongWord; delay2: LongWord; step: (stDelay, stChDmg, stSweep, stTurnReact, stAfterDelay, stChWin, stWater, stChWin2, stHealth, stSpawn, stNTurn); - upd: Longword; - snowLeft,snowRight: LongInt; NewTurnTick: LongWord; //SDMusic: shortstring; -// For better maintainability the step handlers of gears are stored in -// separate files. -// Note: step handlers of gears that are hedgehogs are in a different file -// than the handlers for all other gears. -{$INCLUDE "GSHandlers.inc"} - function CheckNoDamage: boolean; // returns TRUE in case of no damaged hhs var Gear: PGear; dmg: LongInt; @@ -550,39 +527,6 @@ RecountTeamHealth(TeamsArray[i]) end; -procedure SetAllToActive; -var t: PGear; -begin -AllInactive:= false; -t:= GearsList; -while t <> nil do - begin - t^.Active:= true; - t:= t^.NextGear - end -end; - -procedure SetAllHHToActive; inline; -begin -SetAllHHToActive(true) -end; - -procedure SetAllHHToActive(Ice: boolean); -var t: PGear; -begin -AllInactive:= false; -t:= GearsList; -while t <> nil do - begin - if (t^.Kind = gtHedgehog) or (t^.Kind = gtExplosives) then - begin - if (t^.Kind = gtHedgehog) and Ice then CheckIce(t); - t^.Active:= true - end; - t:= t^.NextGear - end -end; - procedure DrawGears; var Gear: PGear; x, y: LongInt; @@ -677,210 +621,6 @@ AddGear(LongInt(GetRandom(snowRight - snowLeft)) + snowLeft, LAND_HEIGHT + LongInt(GetRandom(750)) - 1300, gtFlake, 0, _0, _0, 0); end; - -procedure ShotgunShot(Gear: PGear); -var t: PGear; - dmg, r, dist: LongInt; - dx, dy: hwFloat; -begin -Gear^.Radius:= cShotgunRadius; -t:= GearsList; -while t <> nil do - begin - case t^.Kind of - gtHedgehog, - gtMine, - gtSMine, - gtKnife, - gtCase, - gtTarget, - gtExplosives: begin//, -// gtStructure: begin -//addFileLog('ShotgunShot radius: ' + inttostr(Gear^.Radius) + ', t^.Radius = ' + inttostr(t^.Radius) + ', distance = ' + inttostr(dist) + ', dmg = ' + inttostr(dmg)); - dmg:= 0; - r:= Gear^.Radius + t^.Radius; - dx:= Gear^.X-t^.X; - dx.isNegative:= false; - dy:= Gear^.Y-t^.Y; - dy.isNegative:= false; - if r-hwRound(dx+dy) > 0 then - begin - dist:= hwRound(Distance(dx, dy)); - dmg:= ModifyDamage(min(r - dist, 25), t); - end; - if dmg > 0 then - begin - if (not t^.Invulnerable) then - ApplyDamage(t, Gear^.Hedgehog, dmg, dsBullet) - else - Gear^.State:= Gear^.State or gstWinner; - - DeleteCI(t); - t^.dX:= t^.dX + Gear^.dX * dmg * _0_01 + SignAs(cHHKick, Gear^.dX); - t^.dY:= t^.dY + Gear^.dY * dmg * _0_01; - t^.State:= t^.State or gstMoving; - if t^.Kind = gtKnife then t^.State:= t^.State and (not gstCollision); - t^.Active:= true; - FollowGear:= t - end - end; - gtGrave: begin - dmg:= 0; - r:= Gear^.Radius + t^.Radius; - dx:= Gear^.X-t^.X; - dx.isNegative:= false; - dy:= Gear^.Y-t^.Y; - dy.isNegative:= false; - if r-hwRound(dx+dy) > 0 then - begin - dist:= hwRound(Distance(dx, dy)); - dmg:= ModifyDamage(min(r - dist, 25), t); - end; - if dmg > 0 then - begin - t^.dY:= - _0_1; - t^.Active:= true - end - end; - end; - t:= t^.NextGear - end; -if (GameFlags and gfSolidLand) = 0 then - DrawExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cShotgunRadius) -end; - -procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); -var t: PGearArray; - Gear: PGear; - i, j, tmpDmg: LongInt; - VGear: PVisualGear; -begin -t:= CheckGearsCollision(Ammo); -// Just to avoid hogs on rope dodging fire. -if (CurAmmoGear <> nil) and ((CurAmmoGear^.Kind = gtRope) or (CurAmmoGear^.Kind = gtJetpack) or (CurAmmoGear^.Kind = gtBirdy)) -and (CurrentHedgehog^.Gear <> nil) and (CurrentHedgehog^.Gear^.CollisionIndex = -1) -and (sqr(hwRound(Ammo^.X) - hwRound(CurrentHedgehog^.Gear^.X)) + sqr(hwRound(Ammo^.Y) - hwRound(CurrentHedgehog^.Gear^.Y)) <= sqr(cHHRadius + Ammo^.Radius)) then - begin - t^.ar[t^.Count]:= CurrentHedgehog^.Gear; - inc(t^.Count) - end; - -i:= t^.Count; - -if (Ammo^.Kind = gtFlame) and (i > 0) then - Ammo^.Health:= 0; -while i > 0 do - begin - dec(i); - Gear:= t^.ar[i]; - if ((Ammo^.Kind = gtFlame) or (Ammo^.Kind = gtBlowTorch)) and - (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.Effects[heFrozen] > 255) then - Gear^.Hedgehog^.Effects[heFrozen]:= max(255,Gear^.Hedgehog^.Effects[heFrozen]-10000); - tmpDmg:= ModifyDamage(Damage, Gear); - if (Gear^.State and gstNoDamage) = 0 then - begin - - if (Ammo^.Kind = gtDEagleShot) or (Ammo^.Kind = gtSniperRifleShot) then - begin - VGear := AddVisualGear(hwround(Ammo^.X), hwround(Ammo^.Y), vgtBulletHit); - if VGear <> nil then - VGear^.Angle := DxDy2Angle(-Ammo^.dX, Ammo^.dY); - end; - - if (Gear^.Kind = gtHedgehog) and (Ammo^.State and gsttmpFlag <> 0) and (Ammo^.Kind = gtShover) then - Gear^.FlightTime:= 1; - - - case Gear^.Kind of - gtHedgehog, - gtMine, - gtSMine, - gtKnife, - gtTarget, - gtCase, - gtExplosives: //, - //gtStructure: - begin - if (Ammo^.Kind = gtDrill) then - begin - Ammo^.Timer:= 0; - exit; - end; - if (not Gear^.Invulnerable) then - begin - if (Ammo^.Kind = gtKnife) and (tmpDmg > 0) then - for j:= 1 to max(1,min(3,tmpDmg div 5)) do - begin - VGear:= AddVisualGear(hwRound(Ammo^.X-((Ammo^.X-Gear^.X)/_2)), hwRound(Ammo^.Y-((Ammo^.Y-Gear^.Y)/_2)), vgtStraightShot); - if VGear <> nil then - with VGear^ do - begin - Tint:= $FFCC00FF; - Angle:= random(360); - dx:= 0.0005 * (random(100)); - dy:= 0.0005 * (random(100)); - if random(2) = 0 then - dx := -dx; - if random(2) = 0 then - dy := -dy; - FrameTicks:= 600+random(200); - State:= ord(sprStar) - end - end; - ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg, dsShove) - end - else - Gear^.State:= Gear^.State or gstWinner; - if (Gear^.Kind = gtExplosives) and (Ammo^.Kind = gtBlowtorch) then - begin - if (Ammo^.Hedgehog^.Gear <> nil) then - Ammo^.Hedgehog^.Gear^.State:= Ammo^.Hedgehog^.Gear^.State and (not gstNotKickable); - ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg * 100, dsUnknown); // crank up damage for explosives + blowtorch - end; - - if (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.King or (Gear^.Hedgehog^.Effects[heFrozen] > 0)) then - begin - Gear^.dX:= Ammo^.dX * Power * _0_005; - Gear^.dY:= Ammo^.dY * Power * _0_005 - end - else if ((Ammo^.Kind <> gtFlame) or (Gear^.Kind = gtHedgehog)) and (Power <> 0) then - begin - Gear^.dX:= Ammo^.dX * Power * _0_01; - Gear^.dY:= Ammo^.dY * Power * _0_01 - end; - - if (not isZero(Gear^.dX)) or (not isZero(Gear^.dY)) then - begin - Gear^.Active:= true; - DeleteCI(Gear); - Gear^.State:= Gear^.State or gstMoving; - if Gear^.Kind = gtKnife then Gear^.State:= Gear^.State and (not gstCollision); - // move the gear upwards a bit to throw it over tiny obstacles at start - if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then - begin - if not (TestCollisionXwithXYShift(Gear, _0, -3, hwSign(Gear^.dX)) - or (TestCollisionYwithGear(Gear, -1) <> 0)) then - Gear^.Y:= Gear^.Y - _1; - if not (TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) - or (TestCollisionYwithGear(Gear, -1) <> 0)) then - Gear^.Y:= Gear^.Y - _1; - if not (TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX)) - or (TestCollisionYwithGear(Gear, -1) <> 0)) then - Gear^.Y:= Gear^.Y - _1; - end - end; - - - if (Ammo^.Kind <> gtFlame) or ((Ammo^.State and gsttmpFlag) = 0) then - FollowGear:= Gear - end; - end - end; - end; -if i <> 0 then - SetAllToActive -end; - procedure AssignHHCoords; var i, t, p, j: LongInt; ar: array[0..Pred(cMaxHHs)] of PHedgehog; @@ -945,31 +685,6 @@ end end; -var GearsNearArray : TPGearArray; -function GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): PGearArrayS; -var - t: PGear; - s: Longword; -begin - r:= r*r; - s:= 0; - SetLength(GearsNearArray, s); - t := GearsList; - while t <> nil do - begin - if (t^.Kind = Kind) - and ((X - t^.X)*(X - t^.X) + (Y - t^.Y)*(Y-t^.Y) < int2hwFloat(r)) then - begin - inc(s); - SetLength(GearsNearArray, s); - GearsNearArray[s - 1] := t; - end; - t := t^.NextGear; - end; - - GearsNear.size:= s; - GearsNear.ar:= @GearsNearArray -end; {procedure AmmoFlameWork(Ammo: PGear); var t: PGear; @@ -992,21 +707,6 @@ end;} -function CountGears(Kind: TGearType): Longword; -var t: PGear; - count: Longword = 0; -begin - -t:= GearsList; -while t <> nil do - begin - if t^.Kind = Kind then - inc(count); - t:= t^.NextGear - end; -CountGears:= count; -end; - function SpawnCustomCrateAt(x, y: LongInt; crate: TCrateType; content, cnt: Longword): PGear; begin FollowGear := AddGear(x, y, gtCase, 0, _0, _0, 0); @@ -1079,142 +779,6 @@ SpawnFakeCrateAt := FollowGear; end; -function GetAmmo(Hedgehog: PHedgehog): TAmmoType; -var t, aTot: LongInt; - i: TAmmoType; -begin -Hedgehog:= Hedgehog; // avoid hint - -aTot:= 0; -for i:= Low(TAmmoType) to High(TAmmoType) do - if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then - inc(aTot, Ammoz[i].Probability); - -t:= aTot; -i:= Low(TAmmoType); -if (t > 0) then - begin - t:= GetRandom(t); - while t >= 0 do - begin - inc(i); - if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then - dec(t, Ammoz[i].Probability) - end - end; -GetAmmo:= i -end; - -function GetUtility(Hedgehog: PHedgehog): TAmmoType; -var t, uTot: LongInt; - i: TAmmoType; -begin - -uTot:= 0; -for i:= Low(TAmmoType) to High(TAmmoType) do - if ((Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0) - and ((Hedgehog^.Team^.HedgehogsNumber > 1) or (Ammoz[i].Ammo.AmmoType <> amSwitch)) then - inc(uTot, Ammoz[i].Probability); - -t:= uTot; -i:= Low(TAmmoType); -if (t > 0) then - begin - t:= GetRandom(t); - while t >= 0 do - begin - inc(i); - if ((Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0) and ((Hedgehog^.Team^.HedgehogsNumber > 1) - or (Ammoz[i].Ammo.AmmoType <> amSwitch)) then - dec(t, Ammoz[i].Probability) - end - end; -GetUtility:= i -end; - - - -procedure SpawnBoxOfSmth; -var t, aTot, uTot, a, h: LongInt; - i: TAmmoType; -begin -if (PlacingHogs) or - (cCaseFactor = 0) - or (CountGears(gtCase) >= 5) - or (GetRandom(cCaseFactor) <> 0) then - exit; - -FollowGear:= nil; -aTot:= 0; -uTot:= 0; -for i:= Low(TAmmoType) to High(TAmmoType) do - if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then - inc(aTot, Ammoz[i].Probability) - else - inc(uTot, Ammoz[i].Probability); - -t:=0; -a:=aTot; -h:= 1; - -if (aTot+uTot) <> 0 then - if ((GameFlags and gfInvulnerable) = 0) then - begin - h:= cHealthCaseProb * 100; - t:= GetRandom(10000); - a:= (10000-h)*aTot div (aTot+uTot) - end - else - begin - t:= GetRandom(aTot+uTot); - h:= 0 - end; - - -if t 0) then - begin - FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0); - t:= GetRandom(t); - i:= Low(TAmmoType); - FollowGear^.Pos:= posCaseAmmo; - FollowGear^.AmmoType:= i; - AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo); - end - end -else - begin - t:= uTot; - if (t > 0) then - begin - FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0); - t:= GetRandom(t); - i:= Low(TAmmoType); - FollowGear^.Pos:= posCaseUtility; - FollowGear^.AmmoType:= i; - AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo); - end - end; - -// handles case of no ammo or utility crates - considered also placing booleans in uAmmos and altering probabilities -if (FollowGear <> nil) then - begin - FindPlace(FollowGear, true, 0, LAND_WIDTH); - - if (FollowGear <> nil) then - AddVoice(sndReinforce, CurrentTeam^.voicepack) - end -end; - function GearByUID(uid : Longword) : PGear; var gear: PGear; diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/uGearsHandlersMess.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uGearsHandlersMess.pas Thu Jun 27 15:51:20 2013 +0400 @@ -0,0 +1,5760 @@ +(* + * Hedgewars, a free turn based strategy game + * Copyright (c) 2004-2013 Andrey Korotaev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + *) + +(* + * This file contains the step handlers for gears. + * + * Important: Since gears change the course of the game, calculations that + * lead to different results for different clients/players/machines + * should NOT occur! + * Use safe functions and data types! (e.g. GetRandom() and hwFloat) + *) + + {$INCLUDE "options.inc"} + +unit uGearsHandlersMess; +interface +uses uTypes, uFloat; + +procedure doStepPerPixel(Gear: PGear; step: TGearStepProcedure; onlyCheckIfChanged: boolean); +procedure makeHogsWorry(x, y: hwFloat; r: LongInt); +procedure HideHog(HH: PHedgehog); +procedure doStepDrowningGear(Gear: PGear); +procedure doStepFallingGear(Gear: PGear); +procedure doStepBomb(Gear: PGear); +procedure doStepMolotov(Gear: PGear); +procedure doStepCluster(Gear: PGear); +procedure doStepShell(Gear: PGear); +procedure doStepSnowball(Gear: PGear); +procedure doStepSnowflake(Gear: PGear); +procedure doStepGrave(Gear: PGear); +procedure doStepBeeWork(Gear: PGear); +procedure doStepBee(Gear: PGear); +procedure doStepShotIdle(Gear: PGear); +procedure doStepShotgunShot(Gear: PGear); +procedure spawnBulletTrail(Bullet: PGear); +procedure doStepBulletWork(Gear: PGear); +procedure doStepDEagleShot(Gear: PGear); +procedure doStepSniperRifleShot(Gear: PGear); +procedure doStepActionTimer(Gear: PGear); +procedure doStepPickHammerWork(Gear: PGear); +procedure doStepPickHammer(Gear: PGear); +procedure doStepBlowTorchWork(Gear: PGear); +procedure doStepBlowTorch(Gear: PGear); +procedure doStepMine(Gear: PGear); +procedure doStepSMine(Gear: PGear); +procedure doStepDynamite(Gear: PGear); +procedure doStepRollingBarrel(Gear: PGear); +procedure doStepCase(Gear: PGear); +procedure doStepTarget(Gear: PGear); +procedure doStepIdle(Gear: PGear); +procedure doStepShover(Gear: PGear); +procedure doStepWhip(Gear: PGear); +procedure doStepFlame(Gear: PGear); +procedure doStepFirePunchWork(Gear: PGear); +procedure doStepFirePunch(Gear: PGear); +procedure doStepParachuteWork(Gear: PGear); +procedure doStepParachute(Gear: PGear); +procedure doStepAirAttackWork(Gear: PGear); +procedure doStepAirAttack(Gear: PGear); +procedure doStepAirBomb(Gear: PGear); +procedure doStepGirder(Gear: PGear); +procedure doStepTeleportAfter(Gear: PGear); +procedure doStepTeleportAnim(Gear: PGear); +procedure doStepTeleport(Gear: PGear); +procedure doStepSwitcherWork(Gear: PGear); +procedure doStepSwitcher(Gear: PGear); +procedure doStepMortar(Gear: PGear); +procedure doStepKamikazeWork(Gear: PGear); +procedure doStepKamikazeIdle(Gear: PGear); +procedure doStepKamikaze(Gear: PGear); +procedure doStepCakeExpl(Gear: PGear); +procedure doStepCakeDown(Gear: PGear); +procedure doStepCakeWork(Gear: PGear); +procedure doStepCakeUp(Gear: PGear); +procedure doStepCakeFall(Gear: PGear); +procedure doStepCake(Gear: PGear); +procedure doStepSeductionWork(Gear: PGear); +procedure doStepSeductionWear(Gear: PGear); +procedure doStepSeduction(Gear: PGear); +procedure doStepWaterUp(Gear: PGear); +procedure doStepDrillDrilling(Gear: PGear); +procedure doStepDrill(Gear: PGear); +procedure doStepBallgunWork(Gear: PGear); +procedure doStepBallgun(Gear: PGear); +procedure doStepRCPlaneWork(Gear: PGear); +procedure doStepRCPlane(Gear: PGear); +procedure doStepJetpackWork(Gear: PGear); +procedure doStepJetpack(Gear: PGear); +procedure doStepBirdyDisappear(Gear: PGear); +procedure doStepBirdyFly(Gear: PGear); +procedure doStepBirdyDescend(Gear: PGear); +procedure doStepBirdyAppear(Gear: PGear); +procedure doStepBirdy(Gear: PGear); +procedure doStepEggWork(Gear: PGear); +procedure doPortalColorSwitch(); +procedure doStepPortal(Gear: PGear); +procedure loadNewPortalBall(oldPortal: PGear; destroyGear: Boolean); +procedure doStepMovingPortal_real(Gear: PGear); +procedure doStepMovingPortal(Gear: PGear); +procedure doStepPortalShot(newPortal: PGear); +procedure doStepPiano(Gear: PGear); +procedure doStepSineGunShotWork(Gear: PGear); +procedure doStepSineGunShot(Gear: PGear); +procedure doStepFlamethrowerWork(Gear: PGear); +procedure doStepFlamethrower(Gear: PGear); +procedure doStepLandGunWork(Gear: PGear); +procedure doStepLandGun(Gear: PGear); +procedure doStepPoisonCloud(Gear: PGear); +procedure doStepHammer(Gear: PGear); +procedure doStepHammerHitWork(Gear: PGear); +procedure doStepHammerHit(Gear: PGear); +procedure doStepResurrectorWork(Gear: PGear); +procedure doStepResurrector(Gear: PGear); +procedure doStepNapalmBomb(Gear: PGear); +procedure doStepStructure(Gear: PGear); +procedure doStepTardisWarp(Gear: PGear); +procedure doStepTardis(Gear: PGear); +procedure updateFuel(Gear: PGear); +procedure updateTarget(Gear:PGear; newX, newY:HWFloat); +procedure doStepIceGun(Gear: PGear); +procedure doStepAddAmmo(Gear: PGear); +procedure doStepGenericFaller(Gear: PGear); +procedure doStepCreeper(Gear: PGear); +procedure doStepKnife(Gear: PGear); + +var + upd: Longword; + snowLeft,snowRight: LongInt; + +implementation +uses uConsts, uVariables, uVisualGearsList, uRandom, uCollisions, uGearsList, uUtils, uSound + , SDLh, uScript, uGearsHedgehog, uGearsUtils, uIO, uCaptions, uLandGraphics + , uGearsHandlers, uTextures, uRenderUtils, uAmmos, uTeams, uLandTexture, uCommands + , uStore, uAI, uStats; + +procedure doStepPerPixel(Gear: PGear; step: TGearStepProcedure; onlyCheckIfChanged: boolean); +var + dX, dY, sX, sY: hwFloat; + i, steps: LongWord; + caller: TGearStepProcedure; +begin + dX:= Gear^.dX; + dY:= Gear^.dY; + steps:= max(abs(hwRound(Gear^.X+dX)-hwRound(Gear^.X)), abs(hwRound(Gear^.Y+dY)-hwRound(Gear^.Y))); + + // Gear is still on the same Pixel it was before + if steps < 1 then + begin + if onlyCheckIfChanged then + begin + Gear^.X := Gear^.X + dX; + Gear^.Y := Gear^.Y + dY; + EXIT; + end + else + steps := 1; + end; + + if steps > 1 then + begin + sX:= dX / steps; + sY:= dY / steps; + end + + else + begin + sX:= dX; + sY:= dY; + end; + + caller:= Gear^.doStep; + + for i:= 1 to steps do + begin + Gear^.X := Gear^.X + sX; + Gear^.Y := Gear^.Y + sY; + step(Gear); + if (Gear^.doStep <> caller) + or ((Gear^.State and gstCollision) <> 0) + or ((Gear^.State and gstMoving) = 0) then + break; + end; +end; + +procedure makeHogsWorry(x, y: hwFloat; r: LongInt); +var + gi: PGear; + d: LongInt; +begin + gi := GearsList; + while gi <> nil do + begin + if (gi^.Kind = gtHedgehog) then + begin + d := r - hwRound(Distance(gi^.X - x, gi^.Y - y)); + if (d > 1) and (not gi^.Invulnerable) and (GetRandom(2) = 0) then + begin + if (CurrentHedgehog^.Gear = gi) then + PlaySoundV(sndOops, gi^.Hedgehog^.Team^.voicepack) + + else + begin + if ((gi^.State and gstMoving) = 0) and (gi^.Hedgehog^.Effects[heFrozen] = 0) then + begin + gi^.dX.isNegative:= X r div 2 then + PlaySoundV(sndNooo, gi^.Hedgehog^.Team^.voicepack) + else + PlaySoundV(sndUhOh, gi^.Hedgehog^.Team^.voicepack); + end; + end; + end; + + gi := gi^.NextGear + end; +end; + +procedure HideHog(HH: PHedgehog); +begin + ScriptCall('onHogHide', HH^.Gear^.Uid); + DeleteCI(HH^.Gear); + if FollowGear = HH^.Gear then + FollowGear:= nil; + + if lastGearByUID = HH^.Gear then + lastGearByUID := nil; + + HH^.Gear^.Message:= HH^.Gear^.Message or gmRemoveFromList; + with HH^.Gear^ do + begin + Z := cHHZ; + HH^.Gear^.Active:= false; + State:= State and (not (gstHHDriven or gstAttacking or gstAttacked)); + Message := Message and (not gmAttack); + end; + HH^.GearHidden:= HH^.Gear; + HH^.Gear:= nil +end; + + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepDrowningGear(Gear: PGear); + begin + AllInactive := false; + Gear^.Y := Gear^.Y + cDrownSpeed; + Gear^.X := Gear^.X + Gear^.dX * cDrownSpeed; + // Create some bubbles (0.5% might be better but causes too few bubbles sometimes) + if ((not SuddenDeathDmg and (WaterOpacity < $FF)) + or (SuddenDeathDmg and (SDWaterOpacity < $FF))) and ((GameTicks and $1F) = 0) then + if (Gear^.Kind = gtHedgehog) and (Random(4) = 0) then + AddVisualGear(hwRound(Gear^.X) - Gear^.Radius, hwRound(Gear^.Y) - Gear^.Radius, vgtBubble) + else if Random(12) = 0 then + AddVisualGear(hwRound(Gear^.X) - Gear^.Radius, hwRound(Gear^.Y) - Gear^.Radius, vgtBubble); + if (not SuddenDeathDmg and (WaterOpacity > $FE)) + or (SuddenDeathDmg and (SDWaterOpacity > $FE)) + or (hwRound(Gear^.Y) > Gear^.Radius + cWaterLine + cVisibleWater) then + DeleteGear(Gear); + end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepFallingGear(Gear: PGear); +var + isFalling: boolean; + //tmp: QWord; + tdX, tdY: hwFloat; + collV, collH: LongInt; + land: word; +begin + // clip velocity at 2 - over 1 per pixel, but really shouldn't cause many actual problems. + if Gear^.dX.Round > 2 then + Gear^.dX.QWordValue:= 8589934592; + if Gear^.dY.Round > 2 then + Gear^.dY.QWordValue:= 8589934592; + + if (Gear^.State and gstSubmersible <> 0) and (hwRound(Gear^.Y) > cWaterLine) then + begin + Gear^.dX:= Gear^.dX * _0_999; + Gear^.dY:= Gear^.dY * _0_999 + end; + + Gear^.State := Gear^.State and (not gstCollision); + collV := 0; + collH := 0; + tdX := Gear^.dX; + tdY := Gear^.dY; + + + +// might need some testing/adjustments - just to avoid projectiles to fly forever (accelerated by wind/skips) + if (hwRound(Gear^.X) < min(LAND_WIDTH div -2, -2048)) + or (hwRound(Gear^.X) > max(LAND_WIDTH * 3 div 2, 6144)) then + Gear^.State := Gear^.State or gstCollision; + + if Gear^.dY.isNegative then + begin + isFalling := true; + land:= TestCollisionYwithGear(Gear, -1); + if land <> 0 then + begin + collV := -1; + if land and lfIce <> 0 then + Gear^.dX := Gear^.dX * (_0_9 + Gear^.Friction * _0_1) + else + Gear^.dX := Gear^.dX * Gear^.Friction; + + Gear^.dY := - Gear^.dY * Gear^.Elasticity; + Gear^.State := Gear^.State or gstCollision + end + else if (Gear^.AdvBounce=1) and (TestCollisionYwithGear(Gear, 1) <> 0) then + collV := 1; + end + else + begin // Gear^.dY.isNegative is false + land:= TestCollisionYwithGear(Gear, 1); + if land <> 0 then + begin + collV := 1; + isFalling := false; + if land and lfIce <> 0 then + Gear^.dX := Gear^.dX * (_0_9 + Gear^.Friction * _0_1) + else + Gear^.dX := Gear^.dX * Gear^.Friction; + + Gear^.dY := - Gear^.dY * Gear^.Elasticity; + Gear^.State := Gear^.State or gstCollision + end + else + begin + isFalling := true; + if (Gear^.AdvBounce=1) and (TestCollisionYwithGear(Gear, -1) <> 0) then + collV := -1 + end + end; + + + if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then + begin + collH := hwSign(Gear^.dX); + Gear^.dX := - Gear^.dX * Gear^.Elasticity; + Gear^.dY := Gear^.dY * Gear^.Elasticity; + Gear^.State := Gear^.State or gstCollision + end + else if (Gear^.AdvBounce=1) and TestCollisionXwithGear(Gear, -hwSign(Gear^.dX)) then + collH := -hwSign(Gear^.dX); + //if Gear^.AdvBounce and (collV <>0) and (collH <> 0) and (hwSqr(tdX) + hwSqr(tdY) > _0_08) then + if (Gear^.AdvBounce=1) and (collV <>0) and (collH <> 0) and ((collV=-1) + or ((tdX.QWordValue + tdY.QWordValue) > _0_2.QWordValue)) then + begin + Gear^.dX := tdY*Gear^.Elasticity*Gear^.Friction; + Gear^.dY := tdX*Gear^.Elasticity; + //*Gear^.Friction; + Gear^.dY.isNegative := not tdY.isNegative; + isFalling := false; + Gear^.AdvBounce := 10; + end; + + if Gear^.AdvBounce > 1 then + dec(Gear^.AdvBounce); + + if isFalling then + begin + Gear^.dY := Gear^.dY + cGravity; + if (GameFlags and gfMoreWind) <> 0 then + Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density + end; + + Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + Gear^.dY; + if Gear^.Kind <> gtBee then + CheckGearDrowning(Gear); + //if (hwSqr(Gear^.dX) + hwSqr(Gear^.dY) < _0_0002) and + if (not isFalling) and ((Gear^.dX.QWordValue + Gear^.dY.QWordValue) < _0_02.QWordValue) then + Gear^.State := Gear^.State and (not gstMoving) + else + Gear^.State := Gear^.State or gstMoving; + + if (Gear^.nImpactSounds > 0) and + (Gear^.State and gstCollision <> 0) and + (((Gear^.Kind <> gtMine) and (Gear^.Damage <> 0)) or (Gear^.State and gstMoving <> 0)) and + (((Gear^.Radius < 3) and (Gear^.dY < -_0_1)) or + ((Gear^.Radius >= 3) and + ((Gear^.dX.QWordValue > _0_1.QWordValue) or (Gear^.dY.QWordValue > _0_1.QWordValue)))) then + PlaySound(TSound(ord(Gear^.ImpactSound) + LongInt(GetRandom(Gear^.nImpactSounds))), true); +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepBomb(Gear: PGear); +var + i, x, y: LongInt; + dX, dY, gdX: hwFloat; + vg: PVisualGear; +begin + AllInactive := false; + + doStepFallingGear(Gear); + + dec(Gear^.Timer); + if Gear^.Timer = 1000 then // might need adjustments + case Gear^.Kind of + gtGrenade: makeHogsWorry(Gear^.X, Gear^.Y, 50); + gtClusterBomb: makeHogsWorry(Gear^.X, Gear^.Y, 20); + gtWatermelon: makeHogsWorry(Gear^.X, Gear^.Y, 75); + gtHellishBomb: makeHogsWorry(Gear^.X, Gear^.Y, 90); + gtGasBomb: makeHogsWorry(Gear^.X, Gear^.Y, 50); + end; + + if (Gear^.Kind = gtBall) and ((Gear^.State and gstTmpFlag) <> 0) then + begin + CheckCollision(Gear); + if (Gear^.State and gstCollision) <> 0 then + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx); + end; + + if (Gear^.Kind = gtGasBomb) and ((GameTicks mod 200) = 0) then + begin + vg:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeWhite); + if vg <> nil then + vg^.Tint:= $FFC0C000; + end; + + if Gear^.Timer = 0 then + begin + case Gear^.Kind of + gtGrenade: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); + gtBall: doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 40, Gear^.Hedgehog, EXPLAutoSound); + gtClusterBomb: + begin + x := hwRound(Gear^.X); + y := hwRound(Gear^.Y); + gdX:= Gear^.dX; + doMakeExplosion(x, y, 20, Gear^.Hedgehog, EXPLAutoSound); + for i:= 0 to 4 do + begin + dX := rndSign(GetRandomf * _0_1) + gdX / 5; + dY := (GetRandomf - _3) * _0_08; + FollowGear := AddGear(x, y, gtCluster, 0, dX, dY, 25) + end + end; + gtWatermelon: + begin + x := hwRound(Gear^.X); + y := hwRound(Gear^.Y); + gdX:= Gear^.dX; + doMakeExplosion(x, y, 75, Gear^.Hedgehog, EXPLAutoSound); + for i:= 0 to 5 do + begin + dX := rndSign(GetRandomf * _0_1) + gdX / 5; + dY := (GetRandomf - _1_5) * _0_3; + FollowGear:= AddGear(x, y, gtMelonPiece, 0, dX, dY, 75); + FollowGear^.DirAngle := i * 60 + end + end; + gtHellishBomb: + begin + x := hwRound(Gear^.X); + y := hwRound(Gear^.Y); + doMakeExplosion(x, y, 90, Gear^.Hedgehog, EXPLAutoSound); + + for i:= 0 to 127 do + begin + dX := AngleCos(i * 16) * _0_5 * (GetRandomf + _1); + dY := AngleSin(i * 16) * _0_5 * (GetRandomf + _1); + if i mod 2 = 0 then + begin + AddGear(x, y, gtFlame, gstTmpFlag, dX, dY, 0); + AddGear(x, y, gtFlame, 0, dX, -dY, 0) + end + else + begin + AddGear(x, y, gtFlame, 0, dX, dY, 0); + AddGear(x, y, gtFlame, gstTmpFlag, dX, -dY, 0) + end; + end + end; + gtGasBomb: + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLAutoSound); + for i:= 0 to 2 do + begin + x:= GetRandom(60); + y:= GetRandom(40); + FollowGear:= AddGear(hwRound(Gear^.X) - 30 + x, hwRound(Gear^.Y) - 20 + y, gtPoisonCloud, 0, _0, _0, 0); + end + end; + end; + DeleteGear(Gear); + exit + end; + + CalcRotationDirAngle(Gear); + + if Gear^.Kind = gtHellishBomb then + begin + + if Gear^.Timer = 3000 then + begin + Gear^.nImpactSounds := 0; + PlaySound(sndHellish); + end; + + if (GameTicks and $3F) = 0 then + if (Gear^.State and gstCollision) = 0 then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEvilTrace); + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepMolotov(Gear: PGear); +var + s: Longword; + i, gX, gY: LongInt; + dX, dY: hwFloat; + smoke, glass: PVisualGear; +begin + AllInactive := false; + + doStepFallingGear(Gear); + CalcRotationDirAngle(Gear); + + // let's add some smoke depending on speed + s:= max(32,152 - round((abs(hwFloat2FLoat(Gear^.dX))+abs(hwFloat2Float(Gear^.dY)))*120))+random(10); + if (GameTicks mod s) = 0 then + begin + // adjust angle to match the texture + if Gear^.dX.isNegative then + i:= 130 + else i:= 50; + + smoke:= AddVisualGear(hwRound(Gear^.X)-round(cos((Gear^.DirAngle+i) * pi / 180)*20), hwRound(Gear^.Y)-round(sin((Gear^.DirAngle+i) * pi / 180)*20), vgtSmoke); + if smoke <> nil then + smoke^.Scale:= 0.75; + end; + + if (Gear^.State and gstCollision) <> 0 then + begin + PlaySound(sndMolotov); + gX := hwRound(Gear^.X); + gY := hwRound(Gear^.Y); + for i:= 0 to 4 do + begin + (*glass:= AddVisualGear(gx+random(7)-3, gy+random(5)-2, vgtEgg); + if glass <> nil then + begin + glass^.Frame:= 2; + glass^.Tint:= $41B83ED0 - i * $10081000; + glass^.dX:= 1/(10*(random(11)-5)); + glass^.dY:= -1/(random(4)+5); + end;*) + glass:= AddVisualGear(gx+random(7)-3, gy+random(7)-3, vgtStraightShot); + if glass <> nil then + with glass^ do + begin + Frame:= 2; + Tint:= $41B83ED0 - i * $10081000; + Angle:= random(360); + dx:= 0.0000001; + dy:= 0; + if random(2) = 0 then + dx := -dx; + FrameTicks:= 750; + State:= ord(sprEgg) + end; + end; + for i:= 0 to 24 do + begin + dX := AngleCos(i * 2) * ((_0_15*(i div 5))) * (GetRandomf + _1); + dY := AngleSin(i * 8) * _0_5 * (GetRandomf + _1); + AddGear(gX, gY, gtFlame, gstTmpFlag, dX, dY, 0); + AddGear(gX, gY, gtFlame, gstTmpFlag, dX,-dY, 0); + AddGear(gX, gY, gtFlame, gstTmpFlag,-dX, dY, 0); + AddGear(gX, gY, gtFlame, gstTmpFlag,-dX,-dY, 0); + end; + DeleteGear(Gear); + exit + end; +end; + +//////////////////////////////////////////////////////////////////////////////// + +procedure doStepCluster(Gear: PGear); +begin + AllInactive := false; + doStepFallingGear(Gear); + if (Gear^.State and gstCollision) <> 0 then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Timer, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + exit + end; + + if (Gear^.Kind = gtMelonPiece) + or (Gear^.Kind = gtBall) then + CalcRotationDirAngle(Gear) + else if (GameTicks and $1F) = 0 then + begin + if hwRound(Gear^.Y) > cWaterLine then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble) + else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepShell(Gear: PGear); +begin + AllInactive := false; + if (GameFlags and gfMoreWind) = 0 then + Gear^.dX := Gear^.dX + cWindSpeed; + doStepFallingGear(Gear); + if (Gear^.State and gstCollision) <> 0 then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + exit + end; + if (GameTicks and $3F) = 0 then + begin + if hwRound(Gear^.Y) > cWaterLine then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble) + else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSnowball(Gear: PGear); +var kick, i: LongInt; + particle: PVisualGear; + gdX, gdY: hwFloat; +begin + AllInactive := false; + if (GameFlags and gfMoreWind) = 0 then + Gear^.dX := Gear^.dX + cWindSpeed; + gdX := Gear^.dX; + gdY := Gear^.dY; + doStepFallingGear(Gear); + CalcRotationDirAngle(Gear); + if (Gear^.State and gstCollision) <> 0 then + begin + kick:= hwRound((hwAbs(gdX)+hwAbs(gdY)) * _20); + Gear^.dX:= gdX; + Gear^.dY:= gdY; + AmmoShove(Gear, 0, kick); + for i:= 15 + kick div 10 downto 0 do + begin + particle := AddVisualGear(hwRound(Gear^.X) + Random(25), hwRound(Gear^.Y) + Random(25), vgtDust); + if particle <> nil then + particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480) + end; + DeleteGear(Gear); + exit + end; + if ((GameTicks and $1F) = 0) and (Random(3) = 0) then + begin + particle:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust); + if particle <> nil then + particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480) + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSnowflake(Gear: PGear); +var xx, yy, px, py, rx, ry, lx, ly: LongInt; + move, draw, allpx, gun: Boolean; + s: PSDL_Surface; + p: PLongwordArray; + lf: LongWord; +begin +inc(Gear^.Pos); +gun:= (Gear^.State and gstTmpFlag) <> 0; +move:= false; +draw:= false; +if gun then + begin + Gear^.State:= Gear^.State and (not gstInvisible); + doStepFallingGear(Gear); + CheckCollision(Gear); + if ((Gear^.State and gstCollision) <> 0) or ((Gear^.State and gstMoving) = 0) then + draw:= true; + xx:= hwRound(Gear^.X); + yy:= hwRound(Gear^.Y); + end +else if GameTicks and $7 = 0 then + begin + with Gear^ do + begin + State:= State and (not gstInvisible); + X:= X + cWindSpeed * 3200 + dX; + Y:= Y + dY + cGravity * vobFallSpeed * 8; // using same value as flakes to try and get similar results + xx:= hwRound(X); + yy:= hwRound(Y); + if vobVelocity <> 0 then + begin + DirAngle := DirAngle + (Damage / 1000); + if DirAngle < 0 then + DirAngle := DirAngle + 360 + else if 360 < DirAngle then + DirAngle := DirAngle - 360; + end; +(* +We aren't using frametick right now, so just a waste of cycles. + inc(Health, 8); + if longword(Health) > vobFrameTicks then + begin + dec(Health, vobFrameTicks); + inc(Timer); + if Timer = vobFramesCount then + Timer:= 0 + end; +*) + // move back to cloud layer + if yy > cWaterLine then + move:= true + else if (xx > snowRight) or (xx < snowLeft) then + move:=true + // Solid pixel encountered + else if ((yy and LAND_HEIGHT_MASK) = 0) and ((xx and LAND_WIDTH_MASK) = 0) and (Land[yy, xx] <> 0) then + begin + lf:= Land[yy, xx] and (lfObject or lfBasic or lfIndestructible); + if lf = 0 then lf:= lfObject; + // If there's room below keep falling + if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (Land[yy-1, xx] = 0) then + begin + X:= X - cWindSpeed * 1600 - dX; + end + // If there's room below, on the sides, fill the gaps + else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx-(1*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx-(1*hwSign(cWindSpeed)))] = 0) then + begin + X:= X - _0_8 * hwSign(cWindSpeed); + Y:= Y - dY - cGravity * vobFallSpeed * 8; + end + else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx-(2*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx-(2*hwSign(cWindSpeed)))] = 0) then + begin + X:= X - _0_8 * 2 * hwSign(cWindSpeed); + Y:= Y - dY - cGravity * vobFallSpeed * 8; + end + else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx+(1*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx+(1*hwSign(cWindSpeed)))] = 0) then + begin + X:= X + _0_8 * hwSign(cWindSpeed); + Y:= Y - dY - cGravity * vobFallSpeed * 8; + end + else if (((yy-1) and LAND_HEIGHT_MASK) = 0) and (((xx+(2*hwSign(cWindSpeed))) and LAND_WIDTH_MASK) = 0) and (Land[yy-1, (xx+(2*hwSign(cWindSpeed)))] = 0) then + begin + X:= X + _0_8 * 2 * hwSign(cWindSpeed); + Y:= Y - dY - cGravity * vobFallSpeed * 8; + end + // if there's an hog/object below do nothing + else if ((((yy+1) and LAND_HEIGHT_MASK) = 0) and ((Land[yy+1, xx] and $FF) <> 0)) + then move:=true + else draw:= true + end + end + end; +if draw then + with Gear^ do + begin + // we've collided with land. draw some stuff and get back into the clouds + move:= true; + if (Pos > 20) and ((CurAmmoGear = nil) + or (CurAmmoGear^.Kind <> gtRope)) then + begin +////////////////////////////////// TODO - ASK UNC0RR FOR A GOOD HOME FOR THIS //////////////////////////////////// + if not gun then + begin + dec(yy,3); + dec(xx,1) + end; + s:= SpritesData[sprSnow].Surface; + p:= s^.pixels; + allpx:= true; + for py:= 0 to Pred(s^.h) do + begin + for px:= 0 to Pred(s^.w) do + begin + lx:=xx + px; ly:=yy + py; + if (ly and LAND_HEIGHT_MASK = 0) and (lx and LAND_WIDTH_MASK = 0) and (Land[ly, lx] and $FF = 0) then + begin + rx:= lx; + ry:= ly; + if cReducedQuality and rqBlurryLand <> 0 then + begin + rx:= rx div 2;ry:= ry div 2; + end; + if Land[yy + py, xx + px] <= lfAllObjMask then + if gun then + begin + LandDirty[yy div 32, xx div 32]:= 1; + if LandPixels[ry, rx] = 0 then + Land[ly, lx]:= lfDamaged or lfObject + else Land[ly, lx]:= lfDamaged or lfBasic + end + else Land[ly, lx]:= lf; + if gun then + LandPixels[ry, rx]:= (ExplosionBorderColor and (not AMask)) or (p^[px] and AMask) + else LandPixels[ry, rx]:= addBgColor(LandPixels[ry, rx], p^[px]); + end + else allpx:= false + end; + p:= @(p^[s^.pitch shr 2]) + end; + + // Why is this here. For one thing, there's no test on +1 being safe. + //Land[py, px+1]:= lfBasic; + + if allpx then + UpdateLandTexture(xx, Pred(s^.h), yy, Pred(s^.w), true) + else + begin + UpdateLandTexture( + max(0, min(LAND_WIDTH, xx)), + min(LAND_WIDTH - xx, Pred(s^.w)), + max(0, min(LAND_WIDTH, yy)), + min(LAND_HEIGHT - yy, Pred(s^.h)), false // could this be true without unnecessarily creating blanks? + ); + end; +////////////////////////////////// TODO - ASK UNC0RR FOR A GOOD HOME FOR THIS //////////////////////////////////// + end + end; + +if move then + begin + if gun then + begin + DeleteGear(Gear); + exit + end; + Gear^.Pos:= 0; + Gear^.X:= int2hwFloat(LongInt(GetRandom(snowRight - snowLeft)) + snowLeft); + Gear^.Y:= int2hwFloat(LAND_HEIGHT + LongInt(GetRandom(50)) - 1325); + Gear^.State:= Gear^.State or gstInvisible; + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepGrave(Gear: PGear); +begin + if (Gear^.Message and gmDestroy) <> 0 then + begin + DeleteGear(Gear); + exit + end; + + AllInactive := false; + + if Gear^.dY.isNegative then + if TestCollisionY(Gear, -1) then + Gear^.dY := _0; + + if not Gear^.dY.isNegative then + if TestCollisionY(Gear, 1) then + begin + Gear^.dY := - Gear^.dY * Gear^.Elasticity; + if Gear^.dY > - _1div1024 then + begin + Gear^.Active := false; + exit + end + else if Gear^.dY < - _0_03 then + PlaySound(Gear^.ImpactSound) + end; + + Gear^.Y := Gear^.Y + Gear^.dY; + CheckGearDrowning(Gear); + Gear^.dY := Gear^.dY + cGravity +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepBeeWork(Gear: PGear); +var + t: hwFloat; + gX,gY,i: LongInt; + uw, nuw: boolean; + flower: PVisualGear; + +begin + AllInactive := false; + gX := hwRound(Gear^.X); + gY := hwRound(Gear^.Y); + uw := (Gear^.Tag <> 0); // was bee underwater last tick? + nuw := (cWaterLine < gy + Gear^.Radius); // is bee underwater now? + + // if water entered or left + if nuw <> uw then + begin + AddVisualGear(gX, cWaterLine, vgtSplash); + AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet); + AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet); + AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet); + AddVisualGear(gX - 3 + Random(6), cWaterLine, vgtDroplet); + StopSoundChan(Gear^.SoundChannel); + if nuw then + begin + Gear^.SoundChannel := LoopSound(sndBeeWater); + Gear^.Tag := 1; + end + else + begin + Gear^.SoundChannel := LoopSound(sndBee); + Gear^.Tag := 0; + end; + end; + + + if Gear^.Timer = 0 then + Gear^.RenderTimer:= false + else + begin + if (GameTicks and $F) = 0 then + begin + if (GameTicks and $30) = 0 then + AddVisualGear(gX, gY, vgtBeeTrace); + Gear^.dX := Gear^.Elasticity * (Gear^.dX + _0_000064 * (Gear^.Target.X - gX)); + Gear^.dY := Gear^.Elasticity * (Gear^.dY + _0_000064 * (Gear^.Target.Y - gY)); + // make sure new speed isn't higher than original one (which we stored in Friction variable) + t := Gear^.Friction / Distance(Gear^.dX, Gear^.dY); + Gear^.dX := Gear^.dX * t; + Gear^.dY := Gear^.dY * t; + end; + + Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + Gear^.dY; + + end; + + + CheckCollision(Gear); + if ((Gear^.State and gstCollision) <> 0) then + begin + StopSoundChan(Gear^.SoundChannel); + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); + for i:= 0 to 31 do + begin + flower:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot); + if flower <> nil then + with flower^ do + begin + Scale:= 0.75; + dx:= 0.001 * (random(200)); + dy:= 0.001 * (random(200)); + if random(2) = 0 then + dx := -dx; + if random(2) = 0 then + dy := -dy; + FrameTicks:= random(250) + 250; + State:= ord(sprTargetBee); + end; + end; + DeleteGear(Gear); + end; + + if (Gear^.Timer > 0) then + dec(Gear^.Timer) + else + begin + if nuw then + begin + StopSoundChan(Gear^.SoundChannel); + CheckGearDrowning(Gear); + end + else + doStepFallingGear(Gear); + end; +end; + +procedure doStepBee(Gear: PGear); +begin + AllInactive := false; + Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + Gear^.dY; + Gear^.dY := Gear^.dY + cGravity; + CheckCollision(Gear); + if (Gear^.State and gstCollision) <> 0 then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + exit + end; + dec(Gear^.Timer); + if Gear^.Timer = 0 then + begin + Gear^.Hedgehog^.Gear^.Message:= Gear^.Hedgehog^.Gear^.Message and (not gmAttack); + Gear^.Hedgehog^.Gear^.State:= Gear^.Hedgehog^.Gear^.State and (not gstAttacking); + AttackBar:= 0; + + Gear^.SoundChannel := LoopSound(sndBee); + Gear^.Timer := 5000; + // save initial speed in otherwise unused Friction variable + Gear^.Friction := Distance(Gear^.dX, Gear^.dY); + Gear^.doStep := @doStepBeeWork + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepShotIdle(Gear: PGear); +begin + AllInactive := false; + inc(Gear^.Timer); + if Gear^.Timer > 75 then + begin + DeleteGear(Gear); + AfterAttack + end +end; + +procedure doStepShotgunShot(Gear: PGear); +var + i: LongWord; + shell: PVisualGear; +begin + AllInactive := false; + + if ((Gear^.State and gstAnimation) = 0) then + begin + dec(Gear^.Timer); + if Gear^.Timer = 0 then + begin + PlaySound(sndShotgunFire); + shell := AddVisualGear(hwRound(Gear^.x), hwRound(Gear^.y), vgtShell); + if shell <> nil then + begin + shell^.dX := gear^.dX.QWordValue / -17179869184; + shell^.dY := gear^.dY.QWordValue / -17179869184; + shell^.Frame := 0 + end; + Gear^.State := Gear^.State or gstAnimation + end; + exit + end else + if(Gear^.Hedgehog^.Gear = nil) or ((Gear^.Hedgehog^.Gear^.State and gstMoving) <> 0) then + begin + DeleteGear(Gear); + AfterAttack; + exit + end + else + inc(Gear^.Timer); + + i := 200; + repeat + Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + Gear^.dY; + CheckCollision(Gear); + if (Gear^.State and gstCollision) <> 0 then + begin + Gear^.X := Gear^.X + Gear^.dX * 8; + Gear^.Y := Gear^.Y + Gear^.dY * 8; + ShotgunShot(Gear); + Gear^.doStep := @doStepShotIdle; + exit + end; + + CheckGearDrowning(Gear); + if (Gear^.State and gstDrowning) <> 0 then + begin + Gear^.doStep := @doStepShotIdle; + exit + end; + dec(i) + until i = 0; + if (hwRound(Gear^.X) and LAND_WIDTH_MASK <> 0) or (hwRound(Gear^.Y) and LAND_HEIGHT_MASK <> 0) then + Gear^.doStep := @doStepShotIdle +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure spawnBulletTrail(Bullet: PGear); +var oX, oY: hwFloat; + VGear: PVisualGear; +begin + if Bullet^.PortalCounter = 0 then + begin + ox:= CurrentHedgehog^.Gear^.X + Int2hwFloat(GetLaunchX(CurrentHedgehog^.CurAmmoType, hwSign(CurrentHedgehog^.Gear^.dX), CurrentHedgehog^.Gear^.Angle)); + oy:= CurrentHedgehog^.Gear^.Y + Int2hwFloat(GetLaunchY(CurrentHedgehog^.CurAmmoType, CurrentHedgehog^.Gear^.Angle)); + end + else + begin + ox:= Bullet^.Elasticity; + oy:= Bullet^.Friction; + end; + + // Bullet trail + VGear := AddVisualGear(hwRound(ox), hwRound(oy), vgtLineTrail); + + if VGear <> nil then + begin + VGear^.X:= hwFloat2Float(ox); + VGear^.Y:= hwFloat2Float(oy); + VGear^.dX:= hwFloat2Float(Bullet^.X); + VGear^.dY:= hwFloat2Float(Bullet^.Y); + + // reached edge of land. assume infinite beam. Extend it way out past camera + if (hwRound(Bullet^.X) and LAND_WIDTH_MASK <> 0) + or (hwRound(Bullet^.Y) and LAND_HEIGHT_MASK <> 0) then + // only extend if not under water + if hwRound(Bullet^.Y) < cWaterLine then + begin + VGear^.dX := VGear^.dX + max(LAND_WIDTH,4096) * (VGear^.dX - VGear^.X); + VGear^.dY := VGear^.dY + max(LAND_WIDTH,4096) * (VGear^.dY - VGear^.Y); + end; + + VGear^.Timer := 200; + end; +end; + +procedure doStepBulletWork(Gear: PGear); +var + i, x, y: LongWord; + oX, oY: hwFloat; + VGear: PVisualGear; +begin + AllInactive := false; + inc(Gear^.Timer); + i := 80; + oX := Gear^.X; + oY := Gear^.Y; + repeat + Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + Gear^.dY; + x := hwRound(Gear^.X); + y := hwRound(Gear^.Y); + + if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] <> 0) then + inc(Gear^.Damage); + // let's interrupt before a collision to give portals a chance to catch the bullet + if (Gear^.Damage = 1) and (Gear^.Tag = 0) and not(CheckLandValue(x, y, lfLandMask)) then + begin + Gear^.Tag := 1; + Gear^.Damage := 0; + Gear^.X := Gear^.X - Gear^.dX; + Gear^.Y := Gear^.Y - Gear^.dY; + CheckGearDrowning(Gear); + break; + end + else + Gear^.Tag := 0; + + if Gear^.Damage > 5 then + if Gear^.AmmoType = amDEagle then + AmmoShove(Gear, 7, 20) + else + AmmoShove(Gear, Gear^.Timer, 20); + CheckGearDrowning(Gear); + dec(i) + until (i = 0) or (Gear^.Damage > Gear^.Health) or ((Gear^.State and gstDrowning) <> 0); + + if Gear^.Damage > 0 then + begin + DrawTunnel(oX, oY, Gear^.dX, Gear^.dY, 82 - i, 1); + dec(Gear^.Health, Gear^.Damage); + Gear^.Damage := 0 + end; + if ((Gear^.State and gstDrowning) <> 0) and (Gear^.Damage < Gear^.Health) and ((not SuddenDeathDmg and (WaterOpacity < $FF)) or (SuddenDeathDmg and (SDWaterOpacity < $FF))) then + begin + for i:=(Gear^.Health - Gear^.Damage) * 4 downto 0 do + begin + if Random(6) = 0 then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble); + Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + Gear^.dY; + end; + end; + + if (Gear^.Health <= 0) + or (hwRound(Gear^.X) and LAND_WIDTH_MASK <> 0) + or (hwRound(Gear^.Y) and LAND_HEIGHT_MASK <> 0) then + begin + if (Gear^.Kind = gtSniperRifleShot) and ((GameFlags and gfLaserSight) = 0) then + cLaserSighting := false; + if (Ammoz[Gear^.AmmoType].Ammo.NumPerTurn <= CurrentHedgehog^.MultiShootAttacks) and ((GameFlags and gfArtillery) = 0) then + cArtillery := false; + + // Bullet Hit + if (hwRound(Gear^.X) and LAND_WIDTH_MASK = 0) and (hwRound(Gear^.Y) and LAND_HEIGHT_MASK = 0) then + begin + VGear := AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBulletHit); + if VGear <> nil then + begin + VGear^.Angle := DxDy2Angle(-Gear^.dX, Gear^.dY); + end; + end; + + spawnBulletTrail(Gear); + Gear^.doStep := @doStepShotIdle + end; +end; + +procedure doStepDEagleShot(Gear: PGear); +begin + PlaySound(sndGun); + // add 3 initial steps to avoid problem with ammoshove related to calculation of radius + 1 radius as gear widths, and also just plain old weird angles + Gear^.X := Gear^.X + Gear^.dX * 3; + Gear^.Y := Gear^.Y + Gear^.dY * 3; + Gear^.doStep := @doStepBulletWork +end; + +procedure doStepSniperRifleShot(Gear: PGear); +var + HHGear: PGear; + shell: PVisualGear; +begin + cArtillery := true; + HHGear := Gear^.Hedgehog^.Gear; + HHGear^.State := HHGear^.State or gstNotKickable; + HedgehogChAngle(HHGear); + if not cLaserSighting then + // game does not have default laser sight. turn it on and give them a chance to aim + begin + cLaserSighting := true; + HHGear^.Message := 0; + if (HHGear^.Angle >= 32) then + dec(HHGear^.Angle,32) + end; + + if (HHGear^.Message and gmAttack) <> 0 then + begin + shell := AddVisualGear(hwRound(Gear^.x), hwRound(Gear^.y), vgtShell); + if shell <> nil then + begin + shell^.dX := gear^.dX.QWordValue / -8589934592; + shell^.dY := gear^.dY.QWordValue / -8589934592; + shell^.Frame := 1 + end; + Gear^.State := Gear^.State or gstAnimation; + Gear^.dX := SignAs(AngleSin(HHGear^.Angle), HHGear^.dX) * _0_5; + Gear^.dY := -AngleCos(HHGear^.Angle) * _0_5; + PlaySound(sndGun); + // add 3 initial steps to avoid problem with ammoshove related to calculation of radius + 1 radius as gear widths, and also just weird angles + Gear^.X := Gear^.X + Gear^.dX * 3; + Gear^.Y := Gear^.Y + Gear^.dY * 3; + Gear^.doStep := @doStepBulletWork; + end + else + if (GameTicks mod 32) = 0 then + if (GameTicks mod 4096) < 2048 then + begin + if (HHGear^.Angle + 1 <= cMaxAngle) then + inc(HHGear^.Angle) + end + else + if (HHGear^.Angle >= 1) then + dec(HHGear^.Angle); + + if (TurnTimeLeft > 0) then + dec(TurnTimeLeft) + else + begin + DeleteGear(Gear); + AfterAttack + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepActionTimer(Gear: PGear); +begin +dec(Gear^.Timer); +case Gear^.Kind of + gtATStartGame: + begin + AllInactive := false; + if Gear^.Timer = 0 then + begin + AddCaption(trmsg[sidStartFight], cWhiteColor, capgrpGameState); + end + end; + gtATFinishGame: + begin + AllInactive := false; + if Gear^.Timer = 1000 then + begin + ScreenFade := sfToBlack; + ScreenFadeValue := 0; + ScreenFadeSpeed := 1; + end; + if Gear^.Timer = 0 then + begin + SendIPC(_S'N'); + SendIPC(_S'q'); + GameState := gsExit + end + end; + end; +if Gear^.Timer = 0 then + DeleteGear(Gear) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepPickHammerWork(Gear: PGear); +var + i, ei, x, y: LongInt; + HHGear: PGear; +begin + AllInactive := false; + HHGear := Gear^.Hedgehog^.Gear; + dec(Gear^.Timer); + if ((GameFlags and gfInfAttack) <> 0) and (TurnTimeLeft > 0) then + dec(TurnTimeLeft); + if (TurnTimeLeft = 0) or (Gear^.Timer = 0) + or((Gear^.Message and gmDestroy) <> 0) + or((HHGear^.State and gstHHDriven) =0) then + begin + StopSoundChan(Gear^.SoundChannel); + DeleteGear(Gear); + AfterAttack; + doStepHedgehogMoving(HHGear); // for gfInfAttack + exit + end; + + x:= hwRound(Gear^.X); + y:= hwRound(Gear^.Y); + if (Gear^.Timer mod 33) = 0 then + begin + HHGear^.State := HHGear^.State or gstNoDamage; + doMakeExplosion(x, y + 7, 6, Gear^.Hedgehog, EXPLDontDraw); + HHGear^.State := HHGear^.State and (not gstNoDamage) + end; + + if (Gear^.Timer mod 47) = 0 then + begin + // ok. this was an attempt to turn off dust if not actually drilling land. I have no idea why it isn't working as expected + if (( (y + 12) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y + 12, x] > 255) then + for i:= 0 to 1 do + AddVisualGear(x - 5 + Random(10), y + 12, vgtDust); + + i := x - Gear^.Radius - LongInt(GetRandom(2)); + ei := x + Gear^.Radius + LongInt(GetRandom(2)); + while i <= ei do + begin + DrawExplosion(i, y + 3, 3); + inc(i, 1) + end; + + if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9), lfIndestructible) then + begin + Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + _1_9; + end; + SetAllHHToActive; + end; + if TestCollisionYwithGear(Gear, 1) <> 0 then + begin + Gear^.dY := _0; + SetLittle(HHGear^.dX); + HHGear^.dY := _0; + end + else + begin + if CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y + Gear^.dY + cGravity), lfLandMask) then + begin + Gear^.dY := Gear^.dY + cGravity; + Gear^.Y := Gear^.Y + Gear^.dY + end; + if hwRound(Gear^.Y) > cWaterLine then + Gear^.Timer := 1 + end; + + Gear^.X := Gear^.X + HHGear^.dX; + if CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y)-cHHRadius, lfLandMask) then + begin + HHGear^.X := Gear^.X; + HHGear^.Y := Gear^.Y - int2hwFloat(cHHRadius) + end; + + if (Gear^.Message and gmAttack) <> 0 then + if (Gear^.State and gsttmpFlag) <> 0 then + Gear^.Timer := 1 + else //there would be a mistake. + else + if (Gear^.State and gsttmpFlag) = 0 then + Gear^.State := Gear^.State or gsttmpFlag; + if ((Gear^.Message and gmLeft) <> 0) then + Gear^.dX := - _0_3 + else + if ((Gear^.Message and gmRight) <> 0) then + Gear^.dX := _0_3 + else Gear^.dX := _0; +end; + +procedure doStepPickHammer(Gear: PGear); +var + i, y: LongInt; + ar: TRangeArray; + HHGear: PGear; +begin + i := 0; + HHGear := Gear^.Hedgehog^.Gear; + + y := hwRound(Gear^.Y) - cHHRadius * 2; + while y < hwRound(Gear^.Y) do + begin + ar[i].Left := hwRound(Gear^.X) - Gear^.Radius - LongInt(GetRandom(2)); + ar[i].Right := hwRound(Gear^.X) + Gear^.Radius + LongInt(GetRandom(2)); + inc(y, 2); + inc(i) + end; + + DrawHLinesExplosions(@ar, 3, hwRound(Gear^.Y) - cHHRadius * 2, 2, Pred(i)); + Gear^.dY := HHGear^.dY; + DeleteCI(HHGear); + + Gear^.SoundChannel := LoopSound(sndPickhammer); + doStepPickHammerWork(Gear); + Gear^.doStep := @doStepPickHammerWork +end; + +//////////////////////////////////////////////////////////////////////////////// +var + BTPrevAngle, BTSteps: LongInt; + +procedure doStepBlowTorchWork(Gear: PGear); +var + HHGear: PGear; + b: boolean; + prevX: LongInt; +begin + AllInactive := false; + dec(Gear^.Timer); + if ((GameFlags and gfInfAttack) <> 0) and (TurnTimeLeft > 0) then + dec(TurnTimeLeft); + + HHGear := Gear^.Hedgehog^.Gear; + + HedgehogChAngle(HHGear); + + b := false; + + if abs(LongInt(HHGear^.Angle) - BTPrevAngle) > 7 then + begin + Gear^.dX := SignAs(AngleSin(HHGear^.Angle) * _0_5, Gear^.dX); + Gear^.dY := AngleCos(HHGear^.Angle) * ( - _0_5); + BTPrevAngle := HHGear^.Angle; + b := true + end; + + if ((HHGear^.State and gstMoving) <> 0) then + begin + doStepHedgehogMoving(HHGear); + if (HHGear^.State and gstHHDriven) = 0 then + Gear^.Timer := 0 + end; + + if Gear^.Timer mod cHHStepTicks = 0 then + begin + b := true; + if Gear^.dX.isNegative then + HHGear^.Message := (HHGear^.Message and (gmAttack or gmUp or gmDown)) or gmLeft + else + HHGear^.Message := (HHGear^.Message and (gmAttack or gmUp or gmDown)) or gmRight; + + if ((HHGear^.State and gstMoving) = 0) then + begin + HHGear^.State := HHGear^.State and (not gstAttacking); + prevX := hwRound(HHGear^.X); + + // why the call to HedgehogStep then a further increment of X? + if (prevX = hwRound(HHGear^.X)) and + CheckLandValue(hwRound(HHGear^.X + SignAs(_6, HHGear^.dX)), hwRound(HHGear^.Y), + lfIndestructible) then HedgehogStep(HHGear); + + if (prevX = hwRound(HHGear^.X)) and + CheckLandValue(hwRound(HHGear^.X + SignAs(_6, HHGear^.dX)), hwRound(HHGear^.Y), + lfIndestructible) then HHGear^.X := HHGear^.X + SignAs(_1, HHGear^.dX); + HHGear^.State := HHGear^.State or gstAttacking + end; + + inc(BTSteps); + if BTSteps = 7 then + begin + BTSteps := 0; + if CheckLandValue(hwRound(HHGear^.X + Gear^.dX * (cHHRadius + cBlowTorchC) + SignAs(_6,Gear^.dX)), hwRound(HHGear^.Y + Gear^.dY * (cHHRadius + cBlowTorchC)),lfIndestructible) then + begin + Gear^.X := HHGear^.X + Gear^.dX * (cHHRadius + cBlowTorchC); + Gear^.Y := HHGear^.Y + Gear^.dY * (cHHRadius + cBlowTorchC); + end; + HHGear^.State := HHGear^.State or gstNoDamage; + AmmoShove(Gear, 2, 15); + HHGear^.State := HHGear^.State and (not gstNoDamage) + end; + end; + + if b then + begin + DrawTunnel(HHGear^.X + Gear^.dX * cHHRadius, + HHGear^.Y + Gear^.dY * cHHRadius - _1 - + ((hwAbs(Gear^.dX) / (hwAbs(Gear^.dX) + hwAbs(Gear^.dY))) * _0_5 * 7), + Gear^.dX, Gear^.dY, + cHHStepTicks, cHHRadius * 2 + 7); + end; + + if (TurnTimeLeft = 0) or (Gear^.Timer = 0) + or ((HHGear^.Message and gmAttack) <> 0) then + begin + HHGear^.Message := 0; + HHGear^.State := HHGear^.State and (not gstNotKickable); + DeleteGear(Gear); + AfterAttack + end +end; + +procedure doStepBlowTorch(Gear: PGear); +var + HHGear: PGear; +begin + BTPrevAngle := High(LongInt); + BTSteps := 0; + HHGear := Gear^.Hedgehog^.Gear; + HedgehogChAngle(HHGear); + Gear^.dX := SignAs(AngleSin(HHGear^.Angle) * _0_5, Gear^.dX); + Gear^.dY := AngleCos(HHGear^.Angle) * ( - _0_5); + DrawTunnel(HHGear^.X, + HHGear^.Y + Gear^.dY * cHHRadius - _1 - + ((hwAbs(Gear^.dX) / (hwAbs(Gear^.dX) + hwAbs(Gear^.dY))) * _0_5 * 7), + Gear^.dX, Gear^.dY, + cHHStepTicks, cHHRadius * 2 + 7); + HHGear^.Message := 0; + HHGear^.State := HHGear^.State or gstNotKickable; + Gear^.doStep := @doStepBlowTorchWork +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepMine(Gear: PGear); +var vg: PVisualGear; + dxdy: hwFloat; +begin + if Gear^.Health = 0 then dxdy:= hwAbs(Gear^.dX)+hwAbs(Gear^.dY); + if (Gear^.State and gstMoving) <> 0 then + begin + DeleteCI(Gear); + doStepFallingGear(Gear); + if (Gear^.State and gstMoving) = 0 then + begin + AddGearCI(Gear); + Gear^.dX := _0; + Gear^.dY := _0 + end; + CalcRotationDirAngle(Gear); + AllInactive := false + end + else if (GameTicks and $3F) = 25 then + doStepFallingGear(Gear); + if (Gear^.Health = 0) then + begin + if (dxdy > _0_4) and (Gear^.State and gstCollision <> 0) then + inc(Gear^.Damage, hwRound(dxdy * _50)); + + if ((GameTicks and $FF) = 0) and (Gear^.Damage > random(30)) then + begin + vg:= AddVisualGear(hwRound(Gear^.X) - 4 + Random(8), hwRound(Gear^.Y) - 4 - Random(4), vgtSmoke); + if vg <> nil then + vg^.Scale:= 0.5 + end; + + if (Gear^.Damage > 35) then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + exit + end + end; + + if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Health <> 0) then + if ((Gear^.State and gstAttacking) = 0) then + begin + if ((GameTicks and $1F) = 0) then + if CheckGearNear(Gear, gtHedgehog, 46, 32) <> nil then + Gear^.State := Gear^.State or gstAttacking + end + else // gstAttacking <> 0 + begin + AllInactive := false; + if (Gear^.Timer and $FF) = 0 then + PlaySound(sndMineTick); + if Gear^.Timer = 0 then + begin + if ((Gear^.State and gstWait) <> 0) + or (cMineDudPercent = 0) + or (getRandom(100) > cMineDudPercent) then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear) + end + else + begin + vg:= AddVisualGear(hwRound(Gear^.X) - 4 + Random(8), hwRound(Gear^.Y) - 4 - Random(4), vgtSmoke); + if vg <> nil then + vg^.Scale:= 0.5; + PlaySound(sndVaporize); + Gear^.Health := 0; + Gear^.Damage := 0; + Gear^.State := Gear^.State and (not gstAttacking) + end; + exit + end; + dec(Gear^.Timer); + end + else // gsttmpFlag = 0 + if (TurnTimeLeft = 0) + or ((GameFlags and gfInfAttack <> 0) and (GameTicks > Gear^.FlightTime)) + or (Gear^.Hedgehog^.Gear = nil) then + Gear^.State := Gear^.State or gsttmpFlag; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSMine(Gear: PGear); +begin + // TODO: do real calculation? + if TestCollisionXwithGear(Gear, 2) + or (TestCollisionYwithGear(Gear, -2) <> 0) + or TestCollisionXwithGear(Gear, -2) + or (TestCollisionYwithGear(Gear, 2) <> 0) then + begin + if (not isZero(Gear^.dX)) or (not isZero(Gear^.dY)) then + begin + PlaySound(sndRopeAttach); + Gear^.dX:= _0; + Gear^.dY:= _0; + AddGearCI(Gear); + end; + end + else + begin + DeleteCI(Gear); + doStepFallingGear(Gear); + AllInactive := false; + CalcRotationDirAngle(Gear); + end; + + if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Health <> 0) then + begin + if ((Gear^.State and gstAttacking) = 0) then + begin + if ((GameTicks and $1F) = 0) then + if CheckGearNear(Gear, gtHedgehog, 46, 32) <> nil then + Gear^.State := Gear^.State or gstAttacking + end + else // gstAttacking <> 0 + begin + AllInactive := false; + if Gear^.Timer = 0 then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + exit + end else + if (Gear^.Timer and $FF) = 0 then + PlaySound(sndMineTick); + + dec(Gear^.Timer); + end + end + else // gsttmpFlag = 0 + if (TurnTimeLeft = 0) + or ((GameFlags and gfInfAttack <> 0) and (GameTicks > Gear^.FlightTime)) + or (Gear^.Hedgehog^.Gear = nil) then + Gear^.State := Gear^.State or gsttmpFlag; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepDynamite(Gear: PGear); +begin + doStepFallingGear(Gear); + AllInactive := false; + if Gear^.Timer mod 166 = 0 then + inc(Gear^.Tag); + if Gear^.Timer = 1000 then // might need better timing + makeHogsWorry(Gear^.X, Gear^.Y, 75); + if Gear^.Timer = 0 then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 75, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + exit + end; + dec(Gear^.Timer); +end; + +/////////////////////////////////////////////////////////////////////////////// + +procedure doStepRollingBarrel(Gear: PGear); +var + i: LongInt; + particle: PVisualGear; + dxdy: hwFloat; +begin + if (Gear^.dY.QWordValue = 0) and (Gear^.dY.QWordValue = 0) and (TestCollisionYwithGear(Gear, 1) = 0) then + SetLittle(Gear^.dY); + Gear^.State := Gear^.State or gstAnimation; + if Gear^.Health < cBarrelHealth then Gear^.State:= Gear^.State and not gstFrozen; + + if ((Gear^.dX.QWordValue <> 0) + or (Gear^.dY.QWordValue <> 0)) then + begin + DeleteCI(Gear); + AllInactive := false; + dxdy:= hwAbs(Gear^.dX)+hwAbs(Gear^.dY); + doStepFallingGear(Gear); + if (Gear^.State and gstCollision <> 0) and(dxdy > _0_4) then + begin + if (TestCollisionYwithGear(Gear, 1) <> 0) then + begin + Gear^.State := Gear^.State or gsttmpFlag; + for i:= min(12, hwRound(dxdy*_10)) downto 0 do + begin + particle := AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12,vgtDust); + if particle <> nil then + particle^.dX := particle^.dX + (Gear^.dX.QWordValue / 21474836480) + end + end; + inc(Gear^.Damage, hwRound(dxdy * _50)) + end; + CalcRotationDirAngle(Gear); + //CheckGearDrowning(Gear) + end + else + begin + Gear^.State := Gear^.State or gsttmpFlag; + AddGearCI(Gear) + end; + +(* +Attempt to make a barrel knock itself over an edge. Would need more checks to avoid issues like burn damage + begin + x:= hwRound(Gear^.X); + y:= hwRound(Gear^.Y); + if (((y+1) and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) then + if (Land[y+1, x] = 0) then + begin + if (((y+1) and LAND_HEIGHT_MASK) = 0) and (((x+Gear^.Radius-2) and LAND_WIDTH_MASK) = 0) and (Land[y+1, x+Gear^.Radius-2] = 0) then + Gear^.dX:= -_0_08 + else if (((y+1 and LAND_HEIGHT_MASK)) = 0) and (((x-(Gear^.Radius-2)) and LAND_WIDTH_MASK) = 0) and (Land[y+1, x-(Gear^.Radius-2)] = 0) then + Gear^.dX:= _0_08; + end; + if Gear^.dX.QWordValue = 0 then AddGearCI(Gear) + end; *) + + if not Gear^.dY.isNegative and (Gear^.dY < _0_001) and (TestCollisionYwithGear(Gear, 1) <> 0) then + Gear^.dY := _0; + if hwAbs(Gear^.dX) < _0_001 then + Gear^.dX := _0; + + if (Gear^.Health > 0) and ((Gear^.Health * 100 div cBarrelHealth) < random(90)) and ((GameTicks and $FF) = 0) then + if (cBarrelHealth div Gear^.Health) > 2 then + AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmoke) + else + AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmokeWhite); + dec(Gear^.Health, Gear^.Damage); + Gear^.Damage := 0; + if Gear^.Health <= 0 then + doStepCase(Gear); +end; + +procedure doStepCase(Gear: PGear); +var + i, x, y: LongInt; + k: TGearType; + dX, dY: HWFloat; + hog: PHedgehog; + sparkles: PVisualGear; + gi: PGear; +begin + k := Gear^.Kind; + + if (Gear^.Message and gmDestroy) > 0 then + begin + DeleteGear(Gear); + FreeActionsList; + SetAllToActive; + // something (hh, mine, etc...) could be on top of the case + with CurrentHedgehog^ do + if Gear <> nil then + Gear^.Message := Gear^.Message and (not (gmLJump or gmHJump)); + exit + end; + if (k = gtExplosives) and (Gear^.Health < cBarrelHealth) then Gear^.State:= Gear^.State and not gstFrozen; + + if ((k <> gtExplosives) and (Gear^.Damage > 0)) or ((k = gtExplosives) and (Gear^.Health<=0)) then + begin + x := hwRound(Gear^.X); + y := hwRound(Gear^.Y); + hog:= Gear^.Hedgehog; + + DeleteGear(Gear); + // <-- delete gear! + + if k = gtCase then + begin + doMakeExplosion(x, y, 25, hog, EXPLAutoSound); + for i:= 0 to 63 do + AddGear(x, y, gtFlame, 0, _0, _0, 0); + end + else if k = gtExplosives then + begin + doMakeExplosion(x, y, 75, hog, EXPLAutoSound); + for i:= 0 to 31 do + begin + dX := AngleCos(i * 64) * _0_5 * (getrandomf + _1); + dY := AngleSin(i * 64) * _0_5 * (getrandomf + _1); + AddGear(x, y, gtFlame, 0, dX, dY, 0); + AddGear(x, y, gtFlame, gstTmpFlag, -dX, -dY, 0); + end + end; + exit + end; + + if k = gtExplosives then + begin + //if V > _0_03 then Gear^.State:= Gear^.State or gstAnimation; + if (hwAbs(Gear^.dX) > _0_15) or ((hwAbs(Gear^.dY) > _0_15) and (hwAbs(Gear^.dX) > _0_02)) then + begin + Gear^.doStep := @doStepRollingBarrel; + exit; + end + else Gear^.dX:= _0; + + if ((Gear^.Health * 100 div cBarrelHealth) < random(90)) and ((GameTicks and $FF) = 0) then + if (cBarrelHealth div Gear^.Health) > 2 then + AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmoke) + else + AddVisualGear(hwRound(Gear^.X) - 16 + Random(32), hwRound(Gear^.Y) - 2, vgtSmokeWhite); + dec(Gear^.Health, Gear^.Damage); + Gear^.Damage := 0; + end + else + begin + if (Gear^.Pos <> posCaseHealth) and (GameTicks and $1FFF = 0) then // stir 'em up periodically + begin + gi := GearsList; + while gi <> nil do + begin + if gi^.Kind = gtGenericFaller then + begin + gi^.Active:= true; + gi^.X:= int2hwFloat(GetRandom(rightX-leftX)+leftX); + gi^.Y:= int2hwFloat(GetRandom(LAND_HEIGHT-topY)+topY); + gi^.dX:= _90-(GetRandomf*_360); + gi^.dY:= _90-(GetRandomf*_360) + end; + gi := gi^.NextGear + end + end; + + if Gear^.Timer = 500 then + begin +(* Can't make sparkles team coloured without working out what the next team is going to be. This should be solved, really, since it also screws up + voices. Reinforcements voices is heard for active team, not team-to-be. Either that or change crate spawn from end of turn to start, although that + has its own complexities. *) + // Abuse a couple of gear values to track origin + Gear^.Angle:= hwRound(Gear^.Y); + Gear^.Tag:= random(2); + inc(Gear^.Timer) + end; + if Gear^.Timer < 1833 then inc(Gear^.Timer); + if Gear^.Timer = 1000 then + begin + sparkles:= AddVisualGear(hwRound(Gear^.X), Gear^.Angle, vgtDust, 1); + if sparkles <> nil then + begin + sparkles^.dX:= 0; + sparkles^.dY:= 0; + sparkles^.Angle:= 270; + if Gear^.Tag = 1 then + sparkles^.Tint:= $3744D7FF + else sparkles^.Tint:= $FAB22CFF + end; + end; + if Gear^.Timer < 1000 then + begin + AllInactive:= false; + exit + end + end; + + + if (Gear^.dY.QWordValue <> 0) + or (TestCollisionYwithGear(Gear, 1) = 0) then + begin + AllInactive := false; + + Gear^.dY := Gear^.dY + cGravity; + + if (Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, -1) <> 0) then + Gear^.dY := _0; + + Gear^.Y := Gear^.Y + Gear^.dY; + + if (not Gear^.dY.isNegative) and (Gear^.dY > _0_001) then + SetAllHHToActive(false); + + if (not Gear^.dY.isNegative) and (TestCollisionYwithGear(Gear, 1) <> 0) then + begin + if (Gear^.dY > _0_2) and (k = gtExplosives) then + inc(Gear^.Damage, hwRound(Gear^.dY * _70)); + + if Gear^.dY > _0_2 then + for i:= min(12, hwRound(Gear^.dY*_10)) downto 0 do + AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust); + + Gear^.dY := - Gear^.dY * Gear^.Elasticity; + if Gear^.dY > - _0_001 then + Gear^.dY := _0 + else if Gear^.dY < - _0_03 then + PlaySound(Gear^.ImpactSound); + end; + //if Gear^.dY > - _0_001 then Gear^.dY:= _0 + CheckGearDrowning(Gear); + end; + + if (Gear^.dY.QWordValue = 0) then + AddGearCI(Gear) + else if (Gear^.dY.QWordValue <> 0) then + DeleteCI(Gear) +end; + +//////////////////////////////////////////////////////////////////////////////// + +procedure doStepTarget(Gear: PGear); +begin + if (Gear^.Timer = 0) and (Gear^.Tag = 0) then + PlaySound(sndWarp); + + if (Gear^.Tag = 0) and (Gear^.Timer < 1000) then + inc(Gear^.Timer) + else if Gear^.Tag = 1 then + Gear^.Tag := 2 + else if Gear^.Tag = 2 then + if Gear^.Timer > 0 then + dec(Gear^.Timer) + else + begin + DeleteGear(Gear); + exit; + end; + + doStepCase(Gear) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepIdle(Gear: PGear); +begin + AllInactive := false; + dec(Gear^.Timer); + if Gear^.Timer = 0 then + begin + DeleteGear(Gear); + AfterAttack + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepShover(Gear: PGear); +var + HHGear: PGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + HHGear^.State := HHGear^.State or gstNoDamage; + DeleteCI(HHGear); + + AmmoShove(Gear, 30, 115); + + HHGear^.State := (HHGear^.State and (not gstNoDamage)) or gstMoving; + Gear^.Timer := 250; + Gear^.doStep := @doStepIdle +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepWhip(Gear: PGear); +var + HHGear: PGear; + i: LongInt; +begin + HHGear := Gear^.Hedgehog^.Gear; + HHGear^.State := HHGear^.State or gstNoDamage; + DeleteCI(HHGear); + + for i:= 0 to 3 do + begin + AmmoShove(Gear, 30, 25); + Gear^.X := Gear^.X + Gear^.dX * 5 + end; + + HHGear^.State := (HHGear^.State and (not gstNoDamage)) or gstMoving; + + Gear^.Timer := 250; + Gear^.doStep := @doStepIdle +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepFlame(Gear: PGear); +var + gX,gY,i: LongInt; + sticky: Boolean; + vgt: PVisualGear; + tdX,tdY: HWFloat; +begin + sticky:= (Gear^.State and gsttmpFlag) <> 0; + if not sticky then AllInactive := false; + + if TestCollisionYwithGear(Gear, 1) = 0 then + begin + AllInactive := false; + + if ((GameTicks mod 100) = 0) then + begin + vgt:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFire, gstTmpFlag); + if vgt <> nil then + begin + vgt^.dx:= 0; + vgt^.dy:= 0; + vgt^.FrameTicks:= 1800 div (Gear^.Tag mod 3 + 2); + end; + end; + + + if Gear^.dX.QWordValue > _0_01.QWordValue then + Gear^.dX := Gear^.dX * _0_995; + + Gear^.dY := Gear^.dY + cGravity; + // if sticky then Gear^.dY := Gear^.dY + cGravity; + + if Gear^.dY.QWordValue > _0_2.QWordValue then + Gear^.dY := Gear^.dY * _0_995; + + //if sticky then Gear^.X := Gear^.X + Gear^.dX else + Gear^.X := Gear^.X + Gear^.dX + cWindSpeed * 640; + Gear^.Y := Gear^.Y + Gear^.dY; + + if (hwRound(Gear^.Y) > cWaterLine) then + begin + gX := hwRound(Gear^.X); + for i:= 0 to 3 do + AddVisualGear(gX - 16 + Random(32), cWaterLine - 16 + Random(16), vgtSteam); + PlaySound(sndVaporize); + DeleteGear(Gear); + exit + end + end + else + begin + if sticky and (GameTicks and $F = 0) then + begin + Gear^.Radius := 7; + tdX:= Gear^.dX; + tdY:= Gear^.dY; + Gear^.dX.QWordValue:= 120000000; + Gear^.dY.QWordValue:= 429496730; + Gear^.dX.isNegative:= getrandom(2)<>1; + Gear^.dY.isNegative:= true; + AmmoShove(Gear, 2, 125); + Gear^.dX:= tdX; + Gear^.dY:= tdY; + Gear^.Radius := 1 + end; + if Gear^.Timer > 0 then + begin + dec(Gear^.Timer); + inc(Gear^.Damage) + end + else + begin + gX := hwRound(Gear^.X); + gY := hwRound(Gear^.Y); + // Standard fire + if not sticky then + begin + if ((GameTicks and $1) = 0) then + begin + Gear^.Radius := 7; + tdX:= Gear^.dX; + tdY:= Gear^.dY; + Gear^.dX.QWordValue:= 214748365; + Gear^.dY.QWordValue:= 429496730; + Gear^.dX.isNegative:= getrandom(2)<>1; + Gear^.dY.isNegative:= true; + AmmoShove(Gear, 6, 100); + Gear^.dX:= tdX; + Gear^.dY:= tdY; + Gear^.Radius := 1; + end + else if ((GameTicks and $3) = 3) then + doMakeExplosion(gX, gY, 8, Gear^.Hedgehog, 0);//, EXPLNoDamage); + //DrawExplosion(gX, gY, 4); + + if ((GameTicks and $7) = 0) and (Random(2) = 0) then + for i:= Random(2) downto 0 do + AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke); + + if Gear^.Health > 0 then + dec(Gear^.Health); + Gear^.Timer := 450 - Gear^.Tag * 8 + end + else + begin + // Modified fire + if ((GameTicks and $7FF) = 0) and ((GameFlags and gfSolidLand) = 0) then + begin + DrawExplosion(gX, gY, 4); + + for i:= Random(3) downto 0 do + AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke); + end; + +// This one is interesting. I think I understand the purpose, but I wonder if a bit more fuzzy of kicking could be done with getrandom. + Gear^.Timer := 100 - Gear^.Tag * 3; + if (Gear^.Damage > 3000+Gear^.Tag*1500) then + Gear^.Health := 0 + end + end + end; + if Gear^.Health = 0 then + begin + gX := hwRound(Gear^.X); + gY := hwRound(Gear^.Y); + if not sticky then + begin + if ((GameTicks and $3) = 0) and (Random(1) = 0) then + for i:= Random(2) downto 0 do + AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke); + end + else + for i:= Random(3) downto 0 do + AddVisualGear(gX - 3 + Random(6), gY - 2, vgtSmoke); + + DeleteGear(Gear) + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepFirePunchWork(Gear: PGear); +var + HHGear: PGear; +begin + AllInactive := false; + if ((Gear^.Message and gmDestroy) <> 0) then + begin + DeleteGear(Gear); + AfterAttack; + exit + end; + + HHGear := Gear^.Hedgehog^.Gear; + if hwRound(HHGear^.Y) <= Gear^.Tag - 2 then + begin + Gear^.Tag := hwRound(HHGear^.Y); + DrawTunnel(HHGear^.X - int2hwFloat(cHHRadius), HHGear^.Y - _1, _0_5, _0, cHHRadius * 4, 2); + HHGear^.State := HHGear^.State or gstNoDamage; + Gear^.Y := HHGear^.Y; + AmmoShove(Gear, 30, 40); + HHGear^.State := HHGear^.State and (not gstNoDamage) + end; + + HHGear^.dY := HHGear^.dY + cGravity; + if not (HHGear^.dY.isNegative) then + begin + HHGear^.State := HHGear^.State or gstMoving; + DeleteGear(Gear); + AfterAttack; + exit + end; + + if CheckLandValue(hwRound(HHGear^.X), hwRound(HHGear^.Y + HHGear^.dY + SignAs(_6,Gear^.dY)), + lfIndestructible) then + HHGear^.Y := HHGear^.Y + HHGear^.dY +end; + +procedure doStepFirePunch(Gear: PGear); +var + HHGear: PGear; +begin + AllInactive := false; + HHGear := Gear^.Hedgehog^.Gear; + DeleteCI(HHGear); + //HHGear^.X := int2hwFloat(hwRound(HHGear^.X)) - _0_5; WTF? + HHGear^.dX := SignAs(cLittle, Gear^.dX); + + HHGear^.dY := - _0_3; + + Gear^.X := HHGear^.X; + Gear^.dX := SignAs(_0_45, Gear^.dX); + Gear^.dY := - _0_9; + Gear^.doStep := @doStepFirePunchWork; + DrawTunnel(HHGear^.X - int2hwFloat(cHHRadius), HHGear^.Y + _1, _0_5, _0, cHHRadius * 4, 5); + + PlaySoundV(TSound(ord(sndFirePunch1) + GetRandom(6)), HHGear^.Hedgehog^.Team^.voicepack) +end; + +//////////////////////////////////////////////////////////////////////////////// + +procedure doStepParachuteWork(Gear: PGear); +var + HHGear: PGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + + inc(Gear^.Timer); + + if (TestCollisionYwithGear(HHGear, 1) <> 0) + or ((HHGear^.State and gstHHDriven) = 0) + or CheckGearDrowning(HHGear) + or ((Gear^.Message and gmAttack) <> 0) then + begin + with HHGear^ do + begin + Message := 0; + SetLittle(dX); + dY := _0; + State := State or gstMoving; + end; + DeleteGear(Gear); + isCursorVisible := false; + ApplyAmmoChanges(HHGear^.Hedgehog^); + exit + end; + + HHGear^.X := HHGear^.X + cWindSpeed * 200; + + if (Gear^.Message and gmLeft) <> 0 then + HHGear^.X := HHGear^.X - cMaxWindSpeed * 80 + + else if (Gear^.Message and gmRight) <> 0 then + HHGear^.X := HHGear^.X + cMaxWindSpeed * 80; + + if (Gear^.Message and gmUp) <> 0 then + HHGear^.Y := HHGear^.Y - cGravity * 40 + + else if (Gear^.Message and gmDown) <> 0 then + HHGear^.Y := HHGear^.Y + cGravity * 40; + + // don't drift into obstacles + if TestCollisionXwithGear(HHGear, hwSign(HHGear^.dX)) then + HHGear^.X := HHGear^.X - int2hwFloat(hwSign(HHGear^.dX)); + HHGear^.Y := HHGear^.Y + cGravity * 100; + Gear^.X := HHGear^.X; + Gear^.Y := HHGear^.Y +end; + +procedure doStepParachute(Gear: PGear); +var + HHGear: PGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + + DeleteCI(HHGear); + + AfterAttack; + + HHGear^.State := HHGear^.State and (not (gstAttacking or gstAttacked or gstMoving)); + HHGear^.Message := HHGear^.Message and (not gmAttack); + + Gear^.doStep := @doStepParachuteWork; + + Gear^.Message := HHGear^.Message; + doStepParachuteWork(Gear) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepAirAttackWork(Gear: PGear); +begin + AllInactive := false; + Gear^.X := Gear^.X + cAirPlaneSpeed * Gear^.Tag; + + if (Gear^.Health > 0)and(not (Gear^.X < Gear^.dX))and(Gear^.X < Gear^.dX + cAirPlaneSpeed) then + begin + dec(Gear^.Health); + case Gear^.State of + 0: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAirBomb, 0, cBombsSpeed * Gear^.Tag, _0, 0); + 1: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtMine, 0, cBombsSpeed * Gear^.Tag, _0, 0); + 2: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtNapalmBomb, 0, cBombsSpeed * Gear^.Tag, _0, 0); + 3: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtDrill, gsttmpFlag, cBombsSpeed * Gear^.Tag, _0, Gear^.Timer + 1); + //4: FollowGear := AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtWaterMelon, 0, cBombsSpeed * + // Gear^.Tag, _0, 5000); + end; + Gear^.dX := Gear^.dX + int2hwFloat(30 * Gear^.Tag); + StopSoundChan(Gear^.SoundChannel, 4000); + end; + + if (GameTicks and $3F) = 0 then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace); + + if (hwRound(Gear^.X) > (max(LAND_WIDTH,4096)+2048)) or (hwRound(Gear^.X) < -2048) then + begin + // avoid to play forever (is this necessary?) + StopSoundChan(Gear^.SoundChannel); + DeleteGear(Gear) + end; +end; + +procedure doStepAirAttack(Gear: PGear); +begin + AllInactive := false; + + if Gear^.X.QWordValue = 0 then + begin + Gear^.Tag := 1; + Gear^.X := -_2048; + end + else + begin + Gear^.Tag := -1; + Gear^.X := int2hwFloat(max(LAND_WIDTH,4096) + 2048); + end; + + Gear^.Y := int2hwFloat(topY-300); + Gear^.dX := int2hwFloat(Gear^.Target.X - 5 * Gear^.Tag * 15); + + // calcs for Napalm Strike, so that it will hit the target (without wind at least :P) + if (Gear^.State = 2) then + Gear^.dX := Gear^.dX - cBombsSpeed * Gear^.Tag * 900 + // calcs for regular falling gears + else if (int2hwFloat(Gear^.Target.Y) - Gear^.Y > _0) then + Gear^.dX := Gear^.dX - cBombsSpeed * hwSqrt((int2hwFloat(Gear^.Target.Y) - Gear^.Y) * 2 / + cGravity) * Gear^.Tag; + + Gear^.Health := 6; + Gear^.doStep := @doStepAirAttackWork; + Gear^.SoundChannel := LoopSound(sndPlane, 4000); + +end; + +//////////////////////////////////////////////////////////////////////////////// + +procedure doStepAirBomb(Gear: PGear); +begin + AllInactive := false; + doStepFallingGear(Gear); + if (Gear^.State and gstCollision) <> 0 then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + with mobileRecord do + if (performRumble <> nil) and (not fastUntilLag) then + performRumble(kSystemSoundID_Vibrate); + exit + end; + if (GameTicks and $3F) = 0 then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) +end; + +//////////////////////////////////////////////////////////////////////////////// + +procedure doStepGirder(Gear: PGear); +var + HHGear: PGear; + x, y, tx, ty: hwFloat; +begin + AllInactive := false; + + HHGear := Gear^.Hedgehog^.Gear; + tx := int2hwFloat(Gear^.Target.X); + ty := int2hwFloat(Gear^.Target.Y); + x := HHGear^.X; + y := HHGear^.Y; + + if (Distance(tx - x, ty - y) > _256) + or (not TryPlaceOnLand(Gear^.Target.X - SpritesData[sprAmGirder].Width div 2, Gear^.Target.Y - SpritesData[sprAmGirder].Height div 2, sprAmGirder, Gear^.State, true, false)) then + begin + PlaySound(sndDenied); + HHGear^.Message := HHGear^.Message and (not gmAttack); + HHGear^.State := HHGear^.State and (not gstAttacking); + HHGear^.State := HHGear^.State or gstHHChooseTarget; + isCursorVisible := true; + DeleteGear(Gear) + end + else + begin + PlaySound(sndPlaced); + DeleteGear(Gear); + AfterAttack; + end; + + HHGear^.State := HHGear^.State and (not (gstAttacking or gstAttacked)); + HHGear^.Message := HHGear^.Message and (not gmAttack); +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepTeleportAfter(Gear: PGear); +var + HHGear: PGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + doStepHedgehogMoving(HHGear); + // if not infattack mode wait for hedgehog finish falling to collect cases + if ((GameFlags and gfInfAttack) <> 0) + or ((HHGear^.State and gstMoving) = 0) + or (Gear^.Hedgehog^.Gear^.Damage > 0) + or ((HHGear^.State and gstDrowning) = 1) then + begin + DeleteGear(Gear); + AfterAttack + end +end; + +procedure doStepTeleportAnim(Gear: PGear); +begin + if (Gear^.Hedgehog^.Gear^.Damage > 0) then + begin + DeleteGear(Gear); + AfterAttack; + end; + inc(Gear^.Timer); + if Gear^.Timer = 65 then + begin + Gear^.Timer := 0; + inc(Gear^.Pos); + if Gear^.Pos = 11 then + Gear^.doStep := @doStepTeleportAfter + end; +end; + +procedure doStepTeleport(Gear: PGear); +var + HHGear: PGear; +begin + AllInactive := false; + + HHGear := Gear^.Hedgehog^.Gear; + if not TryPlaceOnLand(Gear^.Target.X - SpritesData[sprHHTelepMask].Width div 2, + Gear^.Target.Y - SpritesData[sprHHTelepMask].Height div 2, + sprHHTelepMask, 0, false, false) then + begin + HHGear^.Message := HHGear^.Message and (not gmAttack); + HHGear^.State := HHGear^.State and (not gstAttacking); + HHGear^.State := HHGear^.State or gstHHChooseTarget; + DeleteGear(Gear); + isCursorVisible := true; + PlaySound(sndDenied) + end + else + begin + DeleteCI(HHGear); + SetAllHHToActive; + Gear^.doStep := @doStepTeleportAnim; + + // copy old HH position and direction to Gear (because we need them for drawing the vanishing hog) + Gear^.dX := HHGear^.dX; + // retrieve the cursor direction (it was previously copied to X so it doesn't get lost) + HHGear^.dX.isNegative := (Gear^.X.QWordValue <> 0); + Gear^.X := HHGear^.X; + Gear^.Y := HHGear^.Y; + HHGear^.X := int2hwFloat(Gear^.Target.X); + HHGear^.Y := int2hwFloat(Gear^.Target.Y); + HHGear^.State := HHGear^.State or gstMoving; + Gear^.Hedgehog^.Unplaced := false; + isCursorVisible := false; + playSound(sndWarp) + end; + Gear^.Target.X:= NoPointX +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSwitcherWork(Gear: PGear); +var + HHGear: PGear; + hedgehog: PHedgehog; + State: Longword; +begin + AllInactive := false; + + if ((Gear^.Message and (not gmSwitch)) <> 0) or (TurnTimeLeft = 0) then + begin + hedgehog := Gear^.Hedgehog; + //Msg := Gear^.Message and (not gmSwitch); + DeleteGear(Gear); + ApplyAmmoChanges(hedgehog^); + + HHGear := CurrentHedgehog^.Gear; + ApplyAmmoChanges(HHGear^.Hedgehog^); + //HHGear^.Message := Msg; + exit + end; + + if (Gear^.Message and gmSwitch) <> 0 then + begin + HHGear := CurrentHedgehog^.Gear; + HHGear^.Message := HHGear^.Message and (not gmSwitch); + Gear^.Message := Gear^.Message and (not gmSwitch); + State := HHGear^.State; + HHGear^.State := 0; + HHGear^.Z := cHHZ; + HHGear^.Active := false; + HHGear^.Message:= HHGear^.Message or gmRemoveFromList or gmAddToList; + + PlaySound(sndSwitchHog); + + repeat + CurrentTeam^.CurrHedgehog := Succ(CurrentTeam^.CurrHedgehog) mod (CurrentTeam^.HedgehogsNumber); + until (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear <> nil) and + (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Gear^.Damage = 0) and + (CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog].Effects[heFrozen]=0); + + SwitchCurrentHedgehog(@CurrentTeam^.Hedgehogs[CurrentTeam^.CurrHedgehog]); + AmmoMenuInvalidated:= true; + + HHGear := CurrentHedgehog^.Gear; + HHGear^.State := State; + HHGear^.Active := true; + FollowGear := HHGear; + HHGear^.Z := cCurrHHZ; + HHGear^.Message:= HHGear^.Message or gmRemoveFromList or gmAddToList; + Gear^.X := HHGear^.X; + Gear^.Y := HHGear^.Y + end; +end; + +procedure doStepSwitcher(Gear: PGear); +var + HHGear: PGear; +begin + Gear^.doStep := @doStepSwitcherWork; + + HHGear := Gear^.Hedgehog^.Gear; + OnUsedAmmo(HHGear^.Hedgehog^); + with HHGear^ do + begin + State := State and (not gstAttacking); + Message := Message and (not gmAttack) + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepMortar(Gear: PGear); +var + dX, dY, gdX, gdY: hwFloat; + i: LongInt; +begin + AllInactive := false; + gdX := Gear^.dX; + gdY := Gear^.dY; + + doStepFallingGear(Gear); + if (Gear^.State and gstCollision) <> 0 then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLAutoSound); + gdX.isNegative := not gdX.isNegative; + gdY.isNegative := not gdY.isNegative; + gdX:= gdX*_0_2; + gdY:= gdY*_0_2; + + for i:= 0 to 4 do + begin + dX := gdX + rndSign(GetRandomf) * _0_03; + dY := gdY + rndSign(GetRandomf) * _0_03; + AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtCluster, 0, dX, dY, 25); + end; + + DeleteGear(Gear); + exit + end; + + if (GameTicks and $3F) = 0 then + begin + if hwRound(Gear^.Y) > cWaterLine then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble) + else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepKamikazeWork(Gear: PGear); +var + i: LongWord; + HHGear: PGear; + sparkles: PVisualGear; + hasWishes: boolean; +begin + AllInactive := false; + hasWishes:= ((Gear^.Message and (gmPrecise or gmSwitch)) = (gmPrecise or gmSwitch)); + if hasWishes then + Gear^.AdvBounce:= 1; + + HHGear := Gear^.Hedgehog^.Gear; + if HHGear = nil then + begin + DeleteGear(Gear); + exit + end; + + HHGear^.State := HHGear^.State or gstNoDamage; + DeleteCI(HHGear); + + Gear^.X := HHGear^.X; + Gear^.Y := HHGear^.Y; + if (GameTicks mod 2 = 0) and hasWishes then + begin + sparkles:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtDust, 1); + if sparkles <> nil then + begin + sparkles^.Tint:= ((random(210)+45) shl 24) or ((random(210)+45) shl 16) or ((random(210)+45) shl 8) or $FF; + sparkles^.Angle:= random(360); + end + end; + + i := 2; + repeat + + Gear^.X := Gear^.X + HHGear^.dX; + Gear^.Y := Gear^.Y + HHGear^.dY; + HHGear^.X := Gear^.X; + HHGear^.Y := Gear^.Y; + + inc(Gear^.Damage, 2); + + // if TestCollisionXwithGear(HHGear, hwSign(Gear^.dX)) + // or TestCollisionYwithGear(HHGear, hwSign(Gear^.dY)) then inc(Gear^.Damage, 3); + + dec(i) + until (i = 0) + or (Gear^.Damage > Gear^.Health); + + inc(upd); + if upd > 3 then + begin + if Gear^.Health < 1500 then + begin + if Gear^.AdvBounce <> 0 then + Gear^.Pos := 3 + else + Gear^.Pos := 2; + end; + + AmmoShove(Gear, 30, 40); + + DrawTunnel(HHGear^.X - HHGear^.dX * 10, + HHGear^.Y - _2 - HHGear^.dY * 10 + hwAbs(HHGear^.dY) * 2, + HHGear^.dX, + HHGear^.dY, + 20 + cHHRadius * 2, + cHHRadius * 2 + 7); + + upd := 0 + end; + + if Gear^.Health < Gear^.Damage then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound); + if hasWishes then + for i:= 0 to 31 do + begin + sparkles:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot); + if sparkles <> nil then + with sparkles^ do + begin + Tint:= ((random(210)+45) shl 24) or ((random(210)+45) shl 16) or ((random(210)+45) shl 8) or $FF; + Angle:= random(360); + dx:= 0.001 * (random(200)); + dy:= 0.001 * (random(200)); + if random(2) = 0 then + dx := -dx; + if random(2) = 0 then + dy := -dy; + FrameTicks:= random(400) + 250 + end + end; + AfterAttack; + HHGear^.Message:= HHGear^.Message or gmDestroy; + DeleteGear(Gear); + end + else + begin + dec(Gear^.Health, Gear^.Damage); + Gear^.Damage := 0 + end +end; + +procedure doStepKamikazeIdle(Gear: PGear); +begin + AllInactive := false; + dec(Gear^.Timer); + if Gear^.Timer = 0 then + begin + Gear^.Pos := 1; + PlaySoundV(sndKamikaze, Gear^.Hedgehog^.Team^.voicepack); + Gear^.doStep := @doStepKamikazeWork + end +end; + +procedure doStepKamikaze(Gear: PGear); +var + HHGear: PGear; +begin + AllInactive := false; + + HHGear := Gear^.Hedgehog^.Gear; + + HHGear^.dX := Gear^.dX; + HHGear^.dY := Gear^.dY; + + Gear^.dX := SignAs(_0_45, Gear^.dX); + Gear^.dY := - _0_9; + + Gear^.Timer := 550; + + Gear^.doStep := @doStepKamikazeIdle +end; + +//////////////////////////////////////////////////////////////////////////////// + +const cakeh = 27; +var + CakePoints: array[0..Pred(cakeh)] of record + x, y: hwFloat; + end; + CakeI: Longword; + +procedure doStepCakeExpl(Gear: PGear); +begin + AllInactive := false; + + inc(Gear^.Tag); + if Gear^.Tag < 2250 then + exit; + + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cakeDmg, Gear^.Hedgehog, EXPLAutoSound); + AfterAttack; + DeleteGear(Gear) +end; + +procedure doStepCakeDown(Gear: PGear); +var + gi: PGear; + dmg, dmgBase: LongInt; + fX, fY, tdX, tdY: hwFloat; +begin + AllInactive := false; + + inc(Gear^.Tag); + if Gear^.Tag < 100 then + exit; + Gear^.Tag := 0; + + if Gear^.Pos = 0 then + begin +///////////// adapted from doMakeExplosion /////////////////////////// + //fX:= Gear^.X; + //fY:= Gear^.Y; + //fX.QWordValue:= fX.QWordValue and $FFFFFFFF00000000; + //fY.QWordValue:= fY.QWordValue and $FFFFFFFF00000000; + fX:= int2hwFloat(hwRound(Gear^.X)); + fY:= int2hwFloat(hwRound(Gear^.Y)); + dmgBase:= cakeDmg shl 1 + cHHRadius div 2; + gi := GearsList; + while gi <> nil do + begin + if gi^.Kind = gtHedgehog then + begin + dmg:= 0; + tdX:= gi^.X-fX; + tdY:= gi^.Y-fY; + if hwRound(hwAbs(tdX)+hwAbs(tdY)) < dmgBase then + dmg:= dmgBase - max(hwRound(Distance(tdX, tdY)),gi^.Radius); + if (dmg > 1) then dmg:= ModifyDamage(min(dmg div 2, cakeDmg), gi); + if (dmg > 1) then + if (CurrentHedgehog^.Gear = gi) and (not gi^.Invulnerable) then + gi^.State := gi^.State or gstLoser + else + gi^.State := gi^.State or gstWinner; + end; + gi := gi^.NextGear + end; +////////////////////////////////////////////////////////////////////// + Gear^.doStep := @doStepCakeExpl; + PlaySound(sndCake) + end + else dec(Gear^.Pos) +end; + + +procedure doStepCakeWork(Gear: PGear); +var + tdx, tdy: hwFloat; +begin + AllInactive := false; + + inc(Gear^.Tag); + if Gear^.Tag < 7 then + exit; + + dec(Gear^.Health); + Gear^.Timer := Gear^.Health*10; + if Gear^.Health mod 100 = 0 then + Gear^.PortalCounter:= 0; + // This is not seconds, but at least it is *some* feedback + if (Gear^.Health = 0) or ((Gear^.Message and gmAttack) <> 0) then + begin + FollowGear := Gear; + Gear^.RenderTimer := false; + Gear^.doStep := @doStepCakeDown; + exit + end; + + cakeStep(Gear); + + if Gear^.Tag = 0 then + begin + CakeI := (CakeI + 1) mod cakeh; + tdx := CakePoints[CakeI].x - Gear^.X; + tdy := - CakePoints[CakeI].y + Gear^.Y; + CakePoints[CakeI].x := Gear^.X; + CakePoints[CakeI].y := Gear^.Y; + Gear^.DirAngle := DxDy2Angle(tdx, tdy); + end; +end; + +procedure doStepCakeUp(Gear: PGear); +var + i: Longword; +begin + AllInactive := false; + + inc(Gear^.Tag); + if Gear^.Tag < 100 then + exit; + Gear^.Tag := 0; + + if Gear^.Pos = 6 then + begin + for i:= 0 to Pred(cakeh) do + begin + CakePoints[i].x := Gear^.X; + CakePoints[i].y := Gear^.Y + end; + CakeI := 0; + Gear^.doStep := @doStepCakeWork + end + else + inc(Gear^.Pos) +end; + +procedure doStepCakeFall(Gear: PGear); +begin + AllInactive := false; + + Gear^.dY := Gear^.dY + cGravity; + if TestCollisionYwithGear(Gear, 1) <> 0 then + Gear^.doStep := @doStepCakeUp + else + begin + Gear^.Y := Gear^.Y + Gear^.dY; + if CheckGearDrowning(Gear) then + AfterAttack + end +end; + +procedure doStepCake(Gear: PGear); +var + HHGear: PGear; +begin + AllInactive := false; + + HHGear := Gear^.Hedgehog^.Gear; + HHGear^.Message := HHGear^.Message and (not gmAttack); + Gear^.CollisionMask:= lfNotCurrentMask; + + FollowGear := Gear; + + Gear^.doStep := @doStepCakeFall +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSeductionWork(Gear: PGear); +var i: LongInt; + hogs: PGearArrayS; +begin + AllInactive := false; + hogs := GearsNear(Gear^.X, Gear^.Y, gtHedgehog, Gear^.Radius); + if hogs.size > 0 then + begin + for i:= 0 to hogs.size - 1 do + with hogs.ar^[i]^ do + begin + if hogs.ar^[i] <> CurrentHedgehog^.Gear then + begin + dX:= _50 * cGravity * (Gear^.X - X) / _25; + dY:= -_450 * cGravity; + Active:= true; + end + end; + end ; + AfterAttack; + DeleteGear(Gear); +(* + Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + Gear^.dY; + x := hwRound(Gear^.X); + y := hwRound(Gear^.Y); + + if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) then + if (Land[y, x] <> 0) then + begin + Gear^.dX.isNegative := not Gear^.dX.isNegative; + Gear^.dY.isNegative := not Gear^.dY.isNegative; + Gear^.dX := Gear^.dX * _1_5; + Gear^.dY := Gear^.dY * _1_5 - _0_3; + AmmoShove(Gear, 0, 40); + AfterAttack; + DeleteGear(Gear) + end + else + else + begin + AfterAttack; + DeleteGear(Gear) + end*) +end; + +procedure doStepSeductionWear(Gear: PGear); +var heart: PVisualGear; +begin + AllInactive := false; + inc(Gear^.Timer); + if Gear^.Timer > 250 then + begin + Gear^.Timer := 0; + inc(Gear^.Pos); + if Gear^.Pos = 5 then + PlaySoundV(sndYoohoo, Gear^.Hedgehog^.Team^.voicepack) + end; + + if (Gear^.Pos = 14) and (RealTicks and $3 = 0) then + begin + heart:= AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtStraightShot); + if heart <> nil then + with heart^ do + begin + dx:= 0.001 * (random(200)); + dy:= 0.001 * (random(200)); + if random(2) = 0 then + dx := -dx; + if random(2) = 0 then + dy := -dy; + FrameTicks:= random(750) + 1000; + State:= ord(sprSeduction) + end; + end; + + if Gear^.Pos = 15 then + Gear^.doStep := @doStepSeductionWork +end; + +procedure doStepSeduction(Gear: PGear); +begin + AllInactive := false; + //DeleteCI(Gear^.Hedgehog^.Gear); + Gear^.doStep := @doStepSeductionWear +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepWaterUp(Gear: PGear); +var + i: LongWord; +begin + if (Gear^.Tag = 0) + or (cWaterLine = 0) then + begin + DeleteGear(Gear); + exit + end; + + AllInactive := false; + + inc(Gear^.Timer); + if Gear^.Timer = 17 then + Gear^.Timer := 0 + else + exit; + + if cWaterLine > 0 then + begin + dec(cWaterLine); + for i:= 0 to LAND_WIDTH - 1 do + Land[cWaterLine, i] := 0; + SetAllToActive + end; + + dec(Gear^.Tag); +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepDrillDrilling(Gear: PGear); +var + t: PGearArray; + tempColl: Word; +begin + AllInactive := false; + if (Gear^.Timer > 0) and (Gear^.Timer mod 10 <> 0) then + begin + dec(Gear^.Timer); + exit; + end; + + DrawTunnel(Gear^.X, Gear^.Y, Gear^.dX, Gear^.dY, 2, 6); + Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + Gear^.dY; + if (Gear^.Timer mod 30) = 0 then + AddVisualGear(hwRound(Gear^.X + _20 * Gear^.dX), hwRound(Gear^.Y + _20 * Gear^.dY), vgtDust); + if (CheckGearDrowning(Gear)) then + begin + StopSoundChan(Gear^.SoundChannel); + exit + end; + + tempColl:= Gear^.CollisionMask; + Gear^.CollisionMask:= $007F; + if (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) <> 0) or TestCollisionXWithGear(Gear, hwSign(Gear^.dX)) or (GameTicks > Gear^.FlightTime) then + t := CheckGearsCollision(Gear) + else t := nil; + Gear^.CollisionMask:= tempColl; + //fixes drill not exploding when touching HH bug + + if (Gear^.Timer = 0) or ((t <> nil) and (t^.Count <> 0)) + or ( ((Gear^.State and gsttmpFlag) = 0) and (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) = 0) and (not TestCollisionXWithGear(Gear, hwSign(Gear^.dX)))) +// CheckLandValue returns true if the type isn't matched + or (not CheckLandValue(hwRound(Gear^.X), hwRound(Gear^.Y), lfIndestructible)) then + begin + //out of time or exited ground + StopSoundChan(Gear^.SoundChannel); + if (Gear^.State and gsttmpFlag) <> 0 then + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound) + else + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + exit + end + + else if (TestCollisionYWithGear(Gear, hwSign(Gear^.dY)) = 0) and (not TestCollisionXWithGear(Gear, hwSign(Gear^.dX))) then + begin + StopSoundChan(Gear^.SoundChannel); + Gear^.Tag := 1; + Gear^.doStep := @doStepDrill + end; + + dec(Gear^.Timer); +end; + +procedure doStepDrill(Gear: PGear); +var + t: PGearArray; + oldDx, oldDy: hwFloat; + t2: hwFloat; +begin + AllInactive := false; + + if (Gear^.State and gsttmpFlag) = 0 then + Gear^.dX := Gear^.dX + cWindSpeed; + + oldDx := Gear^.dX; + oldDy := Gear^.dY; + + doStepFallingGear(Gear); + + if (GameTicks and $3F) = 0 then + begin + if hwRound(Gear^.Y) > cWaterLine then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtBubble) + else AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace) + end; + + if ((Gear^.State and gstCollision) <> 0) then + begin + //hit + Gear^.dX := oldDx; + Gear^.dY := oldDy; + + if GameTicks > Gear^.FlightTime then + t := CheckGearsCollision(Gear) + else + t := nil; + if (t = nil) or (t^.Count = 0) then + begin + //hit the ground not the HH + t2 := _0_5 / Distance(Gear^.dX, Gear^.dY); + Gear^.dX := Gear^.dX * t2; + Gear^.dY := Gear^.dY * t2; + end + + else if (t <> nil) then + begin + //explode right on contact with HH + if (Gear^.State and gsttmpFlag) <> 0 then + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound) + else + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 50, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + exit; + end; + + Gear^.SoundChannel := LoopSound(sndDrillRocket); + Gear^.doStep := @doStepDrillDrilling; + + if (Gear^.State and gsttmpFlag) <> 0 then + gear^.RenderTimer:= true; + if Gear^.Timer > 0 then dec(Gear^.Timer) + end + else if ((Gear^.State and gsttmpFlag) <> 0) and (Gear^.Tag <> 0) then + begin + if Gear^.Timer > 0 then + dec(Gear^.Timer) + else + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 30, Gear^.Hedgehog, EXPLAutoSound); + DeleteGear(Gear); + end + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepBallgunWork(Gear: PGear); +var + HHGear, ball: PGear; + rx, ry: hwFloat; + gX, gY: LongInt; +begin + AllInactive := false; + dec(Gear^.Timer); + HHGear := Gear^.Hedgehog^.Gear; + HedgehogChAngle(HHGear); + gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle); + gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle); + if (Gear^.Timer mod 100) = 0 then + begin + rx := rndSign(getRandomf * _0_1); + ry := rndSign(getRandomf * _0_1); + + ball:= AddGear(gx, gy, gtBall, 0, SignAs(AngleSin(HHGear^.Angle) * _0_8, HHGear^.dX) + rx, AngleCos(HHGear^.Angle) * ( - _0_8) + ry, 0); + ball^.CollisionMask:= lfNotCurrentMask; + + PlaySound(sndGun); + end; + + if (Gear^.Timer = 0) or ((HHGear^.State and gstHHDriven) = 0) then + begin + DeleteGear(Gear); + AfterAttack + end +end; + +procedure doStepBallgun(Gear: PGear); +var + HHGear: PGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown)); + HHGear^.State := HHGear^.State or gstNotKickable; + Gear^.doStep := @doStepBallgunWork +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepRCPlaneWork(Gear: PGear); + +const cAngleSpeed = 3; +var + HHGear: PGear; + i: LongInt; + dX, dY: hwFloat; + fChanged: boolean; + trueAngle: Longword; + t: PGear; +begin + AllInactive := false; + + HHGear := Gear^.Hedgehog^.Gear; + FollowGear := Gear; + + if Gear^.Timer > 0 then + dec(Gear^.Timer); + + fChanged := false; + if ((HHGear^.State and gstHHDriven) = 0) or (Gear^.Timer = 0) then + begin + fChanged := true; + if Gear^.Angle > 2048 then + dec(Gear^.Angle) + else if Gear^.Angle < 2048 then + inc(Gear^.Angle) + else fChanged := false + end + else + begin + if ((Gear^.Message and gmLeft) <> 0) then + begin + fChanged := true; + Gear^.Angle := (Gear^.Angle + (4096 - cAngleSpeed)) mod 4096 + end; + + if ((Gear^.Message and gmRight) <> 0) then + begin + fChanged := true; + Gear^.Angle := (Gear^.Angle + cAngleSpeed) mod 4096 + end + end; + + if fChanged then + begin + Gear^.dX.isNegative := (Gear^.Angle > 2048); + if Gear^.dX.isNegative then + trueAngle := 4096 - Gear^.Angle + else + trueAngle := Gear^.Angle; + + Gear^.dX := SignAs(AngleSin(trueAngle), Gear^.dX) * _0_25; + Gear^.dY := AngleCos(trueAngle) * -_0_25; + end; + + Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + Gear^.dY; + + if (GameTicks and $FF) = 0 then + if Gear^.Timer < 3500 then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEvilTrace) + else + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace); + + if ((HHGear^.Message and gmAttack) <> 0) and (Gear^.Health <> 0) then + begin + HHGear^.Message := HHGear^.Message and (not gmAttack); + AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtAirBomb, 0, Gear^.dX * _0_5, Gear^.dY * + _0_5, 0); + dec(Gear^.Health) + end; + + if ((HHGear^.Message and gmLJump) <> 0) and ((Gear^.State and gsttmpFlag) = 0) then + begin + Gear^.State := Gear^.State or gsttmpFlag; + PauseMusic; + playSound(sndRideOfTheValkyries); + end; + + // pickup bonuses + t := CheckGearNear(Gear, gtCase, 36, 36); + if t <> nil then + PickUp(HHGear, t); + + CheckCollision(Gear); + + if ((Gear^.State and gstCollision) <> 0) or CheckGearDrowning(Gear) then + begin + StopSoundChan(Gear^.SoundChannel); + StopSound(sndRideOfTheValkyries); + ResumeMusic; + + if ((Gear^.State and gstCollision) <> 0) then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 25, Gear^.Hedgehog, EXPLAutoSound); + for i:= 0 to 15 do + begin + dX := AngleCos(i * 64) * _0_5 * (GetRandomf + _1); + dY := AngleSin(i * 64) * _0_5 * (GetRandomf + _1); + AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtFlame, 0, dX, dY, 0); + AddGear(hwRound(Gear^.X), hwRound(Gear^.Y), gtFlame, 0, dX, -dY, 0); + end; + DeleteGear(Gear) + end; + + AfterAttack; + CurAmmoGear := nil; + if (GameFlags and gfInfAttack) = 0 then + begin + if TagTurnTimeLeft = 0 then + TagTurnTimeLeft:= TurnTimeLeft; + + TurnTimeLeft:= 14 * 125; + end; + + HHGear^.Message := 0; + ParseCommand('/taunt ' + #1, true) + end +end; + +procedure doStepRCPlane(Gear: PGear); +var + HHGear: PGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + HHGear^.Message := 0; + HHGear^.State := HHGear^.State or gstNotKickable; + Gear^.Angle := HHGear^.Angle; + Gear^.Tag := hwSign(HHGear^.dX); + + if HHGear^.dX.isNegative then + Gear^.Angle := 4096 - Gear^.Angle; + Gear^.doStep := @doStepRCPlaneWork +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepJetpackWork(Gear: PGear); +var + HHGear: PGear; + fuel, i: LongInt; + move: hwFloat; + isUnderwater: Boolean; + bubble: PVisualGear; +begin + isUnderwater:= cWaterLine < hwRound(Gear^.Y) + Gear^.Radius; + if Gear^.Pos > 0 then + dec(Gear^.Pos); + AllInactive := false; + HHGear := Gear^.Hedgehog^.Gear; + //dec(Gear^.Timer); + move := _0_2; + fuel := 50; +(*if (HHGear^.Message and gmPrecise) <> 0 then + begin + move:= _0_02; + fuel:= 5; + end;*) + if HHGear^.Message and gmPrecise <> 0 then + HedgehogChAngle(HHGear) + else if Gear^.Health > 0 then + begin + if HHGear^.Message and gmUp <> 0 then + begin + if (not HHGear^.dY.isNegative) or (HHGear^.Y > -_256) then + begin + if isUnderwater then + begin + HHGear^.dY := HHGear^.dY - (move * _0_7); + for i:= random(10)+10 downto 0 do + begin + bubble := AddVisualGear(hwRound(HHGear^.X) - 8 + random(16), hwRound(HHGear^.Y) + 16 + random(8), vgtBubble); + if bubble <> nil then + bubble^.dY:= random(20)/10+0.1; + end + end + else HHGear^.dY := HHGear^.dY - move; + end; + dec(Gear^.Health, fuel); + Gear^.MsgParam := Gear^.MsgParam or gmUp; + Gear^.Timer := GameTicks + end; + move.isNegative := (HHGear^.Message and gmLeft) <> 0; + if (HHGear^.Message and (gmLeft or gmRight)) <> 0 then + begin + HHGear^.dX := HHGear^.dX + (move * _0_1); + if isUnderwater then + begin + for i:= random(5)+5 downto 0 do + begin + bubble := AddVisualGear(hwRound(HHGear^.X)+random(8), hwRound(HHGear^.Y) - 8 + random(16), vgtBubble); + if bubble <> nil then + begin + bubble^.dX:= (random(10)/10 + 0.02) * -1; + if (move.isNegative) then + begin + bubble^.X := bubble^.X + 28; + bubble^.dX:= bubble^.dX * (-1) + end + else bubble^.X := bubble^.X - 28; + end; + end + end; + dec(Gear^.Health, fuel div 5); + Gear^.MsgParam := Gear^.MsgParam or (HHGear^.Message and (gmLeft or gmRight)); + Gear^.Timer := GameTicks + end + end; + + // erases them all at once :-/ + if (Gear^.Timer <> 0) and (GameTicks - Gear^.Timer > 250) then + begin + Gear^.Timer := 0; + Gear^.MsgParam := 0 + end; + + if Gear^.Health < 0 then + Gear^.Health := 0; + + i:= Gear^.Health div 20; + + if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then + begin + Gear^.Damage:= i; + //AddCaption('Fuel: '+inttostr(round(Gear^.Health/20))+'%', cWhiteColor, capgrpAmmostate); + FreeTexture(Gear^.Tex); + Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) + '%', cWhiteColor, fntSmall) + end; + + if (HHGear^.Message and (gmAttack or gmUp or gmLeft or gmRight) <> 0) and + (HHGear^.Message and gmPrecise = 0) then + Gear^.State := Gear^.State and (not gsttmpFlag); + + if HHGear^.Message and gmPrecise = 0 then + HHGear^.Message := HHGear^.Message and (not (gmUp or gmLeft or gmRight)); + HHGear^.State := HHGear^.State or gstMoving; + + Gear^.X := HHGear^.X; + Gear^.Y := HHGear^.Y; + + if not isUnderWater and hasBorder and ((HHGear^.X < _0) + or (hwRound(HHGear^.X) > LAND_WIDTH)) then + HHGear^.dY.isNegative:= false; + + if ((Gear^.State and gsttmpFlag) = 0) + or (HHGear^.dY < _0) then + doStepHedgehogMoving(HHGear); + + if // (Gear^.Health = 0) + (HHGear^.Damage <> 0) + //or CheckGearDrowning(HHGear) + or (cWaterLine + cVisibleWater * 4 < hwRound(HHGear^.Y)) + or (TurnTimeLeft = 0) + // allow brief ground touches - to be fair on this, might need another counter + or (((GameTicks and $1FF) = 0) and (not HHGear^.dY.isNegative) and (TestCollisionYwithGear(HHGear, 1) <> 0)) + or ((Gear^.Message and gmAttack) <> 0) then + begin + with HHGear^ do + begin + Message := 0; + Active := true; + State := State or gstMoving + end; + DeleteGear(Gear); + isCursorVisible := false; + ApplyAmmoChanges(HHGear^.Hedgehog^); + // if Gear^.Tex <> nil then FreeTexture(Gear^.Tex); + +// Gear^.Tex:= RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(round(Gear^.Health / 20)) + '%', cWhiteColor, fntSmall) + +//AddCaption(trmsg[sidFuel]+': '+inttostr(round(Gear^.Health/20))+'%', cWhiteColor, capgrpAmmostate); + end +end; + +procedure doStepJetpack(Gear: PGear); +var + HHGear: PGear; +begin + Gear^.Pos:= 0; + Gear^.doStep := @doStepJetpackWork; + + HHGear := Gear^.Hedgehog^.Gear; + FollowGear := HHGear; + AfterAttack; + with HHGear^ do + begin + State := State and (not gstAttacking); + Message := Message and (not (gmAttack or gmUp or gmPrecise or gmLeft or gmRight)); + + if (dY < _0_1) and (dY > -_0_1) then + begin + Gear^.State := Gear^.State or gsttmpFlag; + dY := dY - _0_2 + end + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepBirdyDisappear(Gear: PGear); +begin + AllInactive := false; + Gear^.Pos := 0; + if Gear^.Timer < 2000 then + inc(Gear^.Timer, 1) + else + begin + DeleteGear(Gear); + end; +end; + +procedure doStepBirdyFly(Gear: PGear); +var + HHGear: PGear; + fuel, i: LongInt; + move: hwFloat; +begin + HHGear := Gear^.Hedgehog^.Gear; + if HHGear = nil then + begin + DeleteGear(Gear); + exit + end; + + move := _0_2; + fuel := 50; + + if Gear^.Pos > 0 then + dec(Gear^.Pos, 1) + else if (HHGear^.Message and (gmLeft or gmRight or gmUp)) <> 0 then + Gear^.Pos := 500; + + if HHGear^.dX.isNegative then + Gear^.Tag := -1 + else + Gear^.Tag := 1; + + if (HHGear^.Message and gmUp) <> 0 then + begin + if (not HHGear^.dY.isNegative) + or (HHGear^.Y > -_256) then + HHGear^.dY := HHGear^.dY - move; + + dec(Gear^.Health, fuel); + Gear^.MsgParam := Gear^.MsgParam or gmUp; + end; + + if (HHGear^.Message and gmLeft) <> 0 then move.isNegative := true; + if (HHGear^.Message and (gmLeft or gmRight)) <> 0 then + begin + HHGear^.dX := HHGear^.dX + (move * _0_1); + dec(Gear^.Health, fuel div 5); + Gear^.MsgParam := Gear^.MsgParam or (HHGear^.Message and (gmLeft or gmRight)); + end; + + if Gear^.Health < 0 then + Gear^.Health := 0; + + if ((GameTicks and $FF) = 0) and (Gear^.Health < 500) then + for i:= ((500-Gear^.Health) div 250) downto 0 do + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtFeather); + + if (HHGear^.Message and gmAttack <> 0) then + begin + HHGear^.Message := HHGear^.Message and (not gmAttack); + if Gear^.FlightTime > 0 then + begin + AddGear(hwRound(Gear^.X), hwRound(Gear^.Y) + 32, gtEgg, 0, Gear^.dX * _0_5, Gear^.dY, 0); + PlaySound(sndBirdyLay); + dec(Gear^.FlightTime) + end; + end; + + if HHGear^.Message and (gmUp or gmPrecise or gmLeft or gmRight) <> 0 then + Gear^.State := Gear^.State and (not gsttmpFlag); + + HHGear^.Message := HHGear^.Message and (not (gmUp or gmPrecise or gmLeft or gmRight)); + HHGear^.State := HHGear^.State or gstMoving; + + Gear^.X := HHGear^.X; + Gear^.Y := HHGear^.Y - int2hwFloat(32); + // For some reason I need to reapply followgear here, something else grabs it otherwise. + // this is probably not needed anymore + if not CurrentTeam^.ExtDriven then FollowGear := HHGear; + + if ((Gear^.State and gsttmpFlag) = 0) + or (HHGear^.dY < _0) then + doStepHedgehogMoving(HHGear); + + if (Gear^.Health = 0) + or (HHGear^.Damage <> 0) + or CheckGearDrowning(HHGear) + or (TurnTimeLeft = 0) + // allow brief ground touches - to be fair on this, might need another counter + or (((GameTicks and $1FF) = 0) and (not HHGear^.dY.isNegative) and (TestCollisionYwithGear(HHGear, 1) <> 0)) + or ((Gear^.Message and gmAttack) <> 0) then + begin + with HHGear^ do + begin + Message := 0; + Active := true; + State := State or gstMoving + end; + Gear^.State := Gear^.State or gstAnimation or gstTmpFlag; + if HHGear^.dY < _0 then + begin + Gear^.dX := HHGear^.dX; + Gear^.dY := HHGear^.dY; + end; + Gear^.Timer := 0; + Gear^.doStep := @doStepBirdyDisappear; + CurAmmoGear := nil; + isCursorVisible := false; + AfterAttack; + end +end; + +procedure doStepBirdyDescend(Gear: PGear); +var + HHGear: PGear; +begin + if Gear^.Timer > 0 then + dec(Gear^.Timer, 1) + else if Gear^.Hedgehog^.Gear = nil then + begin + DeleteGear(Gear); + AfterAttack; + exit + end; + HHGear := Gear^.Hedgehog^.Gear; + HHGear^.Message := HHGear^.Message and (not (gmUp or gmPrecise or gmLeft or gmRight)); + if abs(hwRound(HHGear^.Y - Gear^.Y)) > 32 then + begin + if Gear^.Timer = 0 then + Gear^.Y := Gear^.Y + _0_1 + end + else if Gear^.Timer = 0 then + begin + Gear^.doStep := @doStepBirdyFly; + HHGear^.dY := -_0_2 + end +end; + +procedure doStepBirdyAppear(Gear: PGear); +begin + Gear^.Pos := 0; + if Gear^.Timer < 2000 then + inc(Gear^.Timer, 1) + else + begin + Gear^.Timer := 500; + Gear^.dX := _0; + Gear^.dY := _0; + Gear^.State := Gear^.State and (not gstAnimation); + Gear^.doStep := @doStepBirdyDescend; + end +end; + +procedure doStepBirdy(Gear: PGear); +var + HHGear: PGear; +begin + gear^.State := gear^.State or gstAnimation and (not gstTmpFlag); + Gear^.doStep := @doStepBirdyAppear; + + if CurrentHedgehog = nil then + begin + DeleteGear(Gear); + exit + end; + + HHGear := CurrentHedgehog^.Gear; + + if HHGear^.dX.isNegative then + Gear^.Tag := -1 + else + Gear^.Tag := 1; + Gear^.Pos := 0; + AllInactive := false; + FollowGear := HHGear; + with HHGear^ do + begin + State := State and (not gstAttacking); + Message := Message and (not (gmAttack or gmUp or gmPrecise or gmLeft or gmRight)) + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepEggWork(Gear: PGear); +var + vg: PVisualGear; + i: LongInt; +begin + AllInactive := false; + Gear^.dX := Gear^.dX; + doStepFallingGear(Gear); + // CheckGearDrowning(Gear); // already checked for in doStepFallingGear + CalcRotationDirAngle(Gear); + + if (Gear^.State and gstCollision) <> 0 then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLPoisoned, $C0E0FFE0); + PlaySound(sndEggBreak); + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEgg); + vg := AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtEgg); + if vg <> nil then + vg^.Frame := 2; + + for i:= 10 downto 0 do + begin + vg := AddVisualGear(hwRound(Gear^.X) - 3 + Random(6), hwRound(Gear^.Y) - 3 + Random(6), + vgtDust); + if vg <> nil then + vg^.dX := vg^.dX + (Gear^.dX.QWordValue / 21474836480); + end; + + DeleteGear(Gear); + exit + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doPortalColorSwitch(); +var CurWeapon: PAmmo; +begin + if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and ((CurrentHedgehog^.Gear^.Message and gmSwitch) <> 0) then + with CurrentHedgehog^ do + if (CurAmmoType = amPortalGun) then + begin + CurrentHedgehog^.Gear^.Message := CurrentHedgehog^.Gear^.Message and (not gmSwitch); + + CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^); + if CurWeapon^.Pos <> 0 then + CurWeapon^.Pos := 0 + + else + CurWeapon^.Pos := 1; + end; +end; + +procedure doStepPortal(Gear: PGear); +var + iterator, conPortal: PGear; + s, r, nx, ny, ox, oy, poffs, noffs, pspeed, nspeed, + resetx, resety, resetdx, resetdy: hwFloat; + sx, sy, rh, resetr: LongInt; + hasdxy, isbullet, iscake, isCollision: Boolean; +begin + doPortalColorSwitch(); + + // destroy portal if ground it was attached too is gone + if (Land[hwRound(Gear^.Y), hwRound(Gear^.X)] <= lfAllObjMask) + or (Gear^.Timer < 1) + or (Gear^.Hedgehog^.Team <> CurrentHedgehog^.Team) + or (hwRound(Gear^.Y) > cWaterLine) then + begin + deleteGear(Gear); + EXIT; + end; + + if (TurnTimeLeft < 1) + or (Gear^.Health < 1) then + dec(Gear^.Timer); + + if Gear^.Timer < 10000 then + gear^.RenderTimer := true; + + // abort if there is no other portal connected to this one + if (Gear^.LinkedGear = nil) then + exit; + if ((Gear^.LinkedGear^.Tag and 1) = 0) then // or if it's still moving; + exit; + + conPortal := Gear^.LinkedGear; + + // check all gears for stuff to port through + iterator := nil; + while true do + begin + + // iterate through GearsList + if iterator = nil then + iterator := GearsList + else + iterator := iterator^.NextGear; + + // end of list? + if iterator = nil then + break; + + // don't port portals or other gear that wouldn't make sense + if (iterator^.Kind in [gtPortal, gtRope, gtAirAttack, gtIceGun]) + or (iterator^.PortalCounter > 32) then + continue; + + // don't port hogs on rope + // TODO: this will also prevent hogs while falling after rope use from + // falling through portals... fix that! + + // check if gear fits through portal + if (iterator^.Radius > Gear^.Radius) then + continue; + + // this is the max range we accept incoming gears in + r := Int2hwFloat(iterator^.Radius+Gear^.Radius); + + // too far away? + if (iterator^.X < Gear^.X - r) + or (iterator^.X > Gear^.X + r) + or (iterator^.Y < Gear^.Y - r) + or (iterator^.Y > Gear^.Y + r) then + continue; + + hasdxy := (((iterator^.dX.QWordValue <> 0) or (iterator^.dY.QWordValue <> 0)) or ((iterator^.State or gstMoving) = 0)); + + // in case the object is not moving, let's asume it's falling towards the portal + if not hasdxy then + begin + if Gear^.Y < iterator^.Y then + continue; + ox:= Gear^.X - iterator^.X; + oy:= Gear^.Y - iterator^.Y; + end + else + begin + ox:= iterator^.dX; + oy:= iterator^.dY; + end; + + // cake will need extra treatment... it's so delicious and moist! + iscake:= (iterator^.Kind = gtCake); + + // won't port stuff that does not move towards the front/portal entrance + if iscake then + begin + if not (((iterator^.X - Gear^.X)*ox + (iterator^.Y - Gear^.Y)*oy).isNegative) then + continue; + end + else + if not ((Gear^.dX*ox + Gear^.dY*oy).isNegative) then + continue; + + isbullet:= (iterator^.Kind in [gtShotgunShot, gtDEagleShot, gtSniperRifleShot, gtSineGunShot]); + + r:= int2hwFloat(iterator^.Radius); + + if not (isbullet or iscake) then + begin + // wow! good candidate there, let's see if the distance and direction is okay! + if hasdxy then + begin + s := Distance(iterator^.dX, iterator^.dY); + // if the resulting distance is 0 skip this gear + if s.QWordValue = 0 then + continue; + s := r / s; + ox:= iterator^.X + s * iterator^.dX; + oy:= iterator^.Y + s * iterator^.dY; + end + else + begin + ox:= iterator^.X; + oy:= iterator^.Y + r; + end; + + if (hwRound(Distance(Gear^.X-ox,Gear^.Y-oy)) > Gear^.Radius + 1 ) then + continue; + end; + + // draw bullet trail + if isbullet then + spawnBulletTrail(iterator); + + // calc gear offset in portal vector direction + ox := (iterator^.X - Gear^.X); + oy := (iterator^.Y - Gear^.Y); + poffs:= (Gear^.dX * ox + Gear^.dY * oy); + + if not isBullet and poffs.isNegative then + continue; + + // only port bullets close to the portal + if isBullet and (not (hwAbs(poffs) < _3)) then + continue; + + // + // gears that make it till here will definately be ported + // + // (but old position/movement vector might be restored in case there's + // not enough space on the other side) + // + + resetr := iterator^.Radius; + resetx := iterator^.X; + resety := iterator^.Y; + resetdx := iterator^.dX; + resetdy := iterator^.dY; + + // create a normal of the portal vector, but ... + nx := Gear^.dY; + ny := Gear^.dX; + // ... decide where the top is based on the hog's direction when firing the portal + if Gear^.Elasticity.isNegative then + nx.isNegative := (not nx.isNegative) + else + ny.isNegative := not ny.isNegative; + + // calc gear offset in portal normal vector direction + noffs:= (nx * ox + ny * oy); + + if isBullet and (noffs.Round >= Longword(Gear^.Radius)) then + continue; + + // avoid gravity related loops of not really moving gear + if not (iscake or isbullet) + and (Gear^.dY.isNegative) + and (conPortal^.dY.isNegative) + and ((iterator^.dX.QWordValue + iterator^.dY.QWordValue) < _0_08.QWordValue) + and (iterator^.PortalCounter > 0) then + continue; + + // calc gear speed along to the vector and the normal vector of the portal + if hasdxy then + begin + pspeed:= (Gear^.dX * iterator^.dX + Gear^.dY * iterator^.dY); + nspeed:= (nx * iterator^.dX + ny * iterator^.dY); + end + else + begin + pspeed:= hwAbs(cGravity * oy); + nspeed:= _0; + end; + + // creating normal vector of connected (exit) portal + nx := conPortal^.dY; + ny := conPortal^.dX; + if conPortal^.Elasticity.isNegative then + nx.isNegative := (not nx.isNegative) + else + ny.isNegative := not ny.isNegative; + + // inverse cake's normal movement direction, + // as if it just walked through a hole + //if iscake then nspeed.isNegative:= not nspeed.isNegative; + +//AddFileLog('poffs:'+cstr(poffs)+' noffs:'+cstr(noffs)+' pspeed:'+cstr(pspeed)+' nspeed:'+cstr(nspeed)); + iterator^.dX := -pspeed * conPortal^.dX + nspeed * nx; + iterator^.dY := -pspeed * conPortal^.dY + nspeed * ny; + + // make the gear's exit position close to the portal while + // still respecting the movement direction + + // determine the distance (in exit vector direction) + // that we want the gear at + if iscake then + ox:= (r - _0_7) + else + ox:= (r * _1_5); + s:= ox / poffs; + poffs:= ox; + if (nspeed.QWordValue <> 0) + and (pspeed > _0) then + noffs:= noffs * s * (nspeed / pspeed); + + // move stuff with high normal offset closer to the portal's center + if not isbullet then + begin + s := hwAbs(noffs) + r - int2hwFloat(Gear^.Radius); + if s > _0 then + noffs:= noffs - SignAs(s,noffs) + end; + + iterator^.X := conPortal^.X + poffs * conPortal^.dX + noffs * nx; + iterator^.Y := conPortal^.Y + poffs * conPortal^.dY + noffs * ny; + + if not hasdxy and (not (conPortal^.dY.isNegative)) then + begin + iterator^.dY:= iterator^.dY + hwAbs(cGravity * (iterator^.Y - conPortal^.Y)) + end; + + // see if the space on the exit side actually is enough + + if not (isBullet or isCake) then + begin + // TestCollisionXwithXYShift requires a hwFloat for xShift + ox.QWordValue := _1.QWordValue; + ox.isNegative := not iterator^.dX.isNegative; + + sx := hwSign(iterator^.dX); + sy := hwSign(iterator^.dY); + + if iterator^.Radius > 1 then + iterator^.Radius := iterator^.Radius - 1; + + // check front + isCollision := TestCollisionY(iterator, sy) + or TestCollisionX(iterator, sx); + + if not isCollision then + begin + // check center area (with half the radius so that the + // the square check won't check more pixels than we want to) + iterator^.Radius := 1 + resetr div 2; + rh := resetr div 4; + isCollision := TestCollisionYwithXYShift(iterator, 0, -sy * rh, sy, false) + or TestCollisionXwithXYShift(iterator, ox * rh, 0, sx, false); + end; + + iterator^.Radius := resetr; + + if isCollision then + begin + // collision! oh crap! go back! + iterator^.X := resetx; + iterator^.Y := resety; + iterator^.dX := resetdx; + iterator^.dY := resetdy; + continue; + end; + end; + + // + // You're now officially portaled! + // + + // Until loops are reliably broken + if iscake then + iterator^.PortalCounter:= 33 + else + begin + inc(iterator^.PortalCounter); + iterator^.Active:= true; + iterator^.State:= iterator^.State and (not gstHHHJump) or gstMoving; + end; + + // is it worth adding an arcsin table? Just how often would we end up doing something like this? + // SYNCED ANGLE UPDATE + if iterator^.Kind = gtRCPlane then + begin + // recycling as temp vars + resety.isNegative:= false; + resety.QWordValue:= 4294967296 * 112; + resetx.isNegative:= false; + resetx.QWordValue:= 4294967296 * 35; + resetdx.isNegative:= false; + resetdx.QWordValue:= 4294967296 * 1152; + + resetdy:=hwAbs(iterator^.dX*4); + resetdy:= resetdy + hwPow(resetdy,3)/_6 + _3 * hwPow(resetdy,5) / _40 + _5 * hwPow(resetdy,7) / resety + resetx * hwPow(resetdy,9) / resetdx; + iterator^.Angle:= hwRound(resetdy*_2048 / _PI); + if not iterator^.dY.isNegative then iterator^.Angle:= 2048-iterator^.Angle; + if iterator^.dX.isNegative then iterator^.Angle:= 4096-iterator^.Angle; + end + // VISUAL USE OF ANGLE ONLY + else if (CurAmmoGear <> nil) and (CurAmmoGear^.Kind = gtKamikaze) and (CurAmmoGear^.Hedgehog = iterator^.Hedgehog) then + begin + iterator^.Angle:= DxDy2AttackAngle(iterator^.dX, iterator^.dY); + iterator^.Angle:= 2048-iterator^.Angle; + if iterator^.dX.isNegative then iterator^.Angle:= 4096-iterator^.Angle; + end; + + if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) + and (iterator = CurrentHedgehog^.Gear) + and (CurAmmoGear <> nil) + and (CurAmmoGear^.Kind =gtRope) then + CurAmmoGear^.PortalCounter:= 1; + + if not isbullet and (iterator^.State and gstInvisible = 0) + and (iterator^.Kind <> gtFlake) then + FollowGear := iterator; + + // store X/Y values of exit for net bullet trail + if isbullet then + begin + iterator^.Elasticity:= iterator^.X; + iterator^.Friction := iterator^.Y; + end; + + if Gear^.Health > 1 then + dec(Gear^.Health); + end; +end; + + + +procedure loadNewPortalBall(oldPortal: PGear; destroyGear: Boolean); +var + CurWeapon: PAmmo; +begin + if CurrentHedgehog <> nil then + with CurrentHedgehog^ do + begin + CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^); + if (CurAmmoType = amPortalGun) then + begin + if not destroyGear then + begin + // switch color of ball to opposite of oldPortal + if (oldPortal^.Tag and 2) = 0 then + CurWeapon^.Pos:= 1 + else + CurWeapon^.Pos:= 0; + end; + + // make the ball visible + CurWeapon^.Timer := 0; + end + end; + if destroyGear then + oldPortal^.Timer:= 0; +end; + +procedure doStepMovingPortal_real(Gear: PGear); +var + x, y, tx, ty: LongInt; + s: hwFloat; +begin + x := hwRound(Gear^.X); + y := hwRound(Gear^.Y); + tx := 0; + ty := 0; + // avoid compiler hints + + if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) and (Land[y, x] > 255) then + begin + Gear^.State := Gear^.State or gstCollision; + Gear^.State := Gear^.State and (not gstMoving); + + if (Land[y, x] and lfBouncy <> 0) + or (not CalcSlopeTangent(Gear, x, y, tx, ty, 255)) + or (DistanceI(tx,ty) < _12) then // reject shots at too irregular terrain + begin + loadNewPortalBall(Gear, true); + EXIT; + end; + + // making a normalized normal vector + s := _1/DistanceI(tx,ty); + Gear^.dX := s * ty; + Gear^.dY := -s * tx; + + Gear^.DirAngle := DxDy2Angle(-Gear^.dY,Gear^.dX); + if not Gear^.dX.isNegative then + Gear^.DirAngle := 180-Gear^.DirAngle; + + if ((Gear^.LinkedGear = nil) + or (hwRound(Distance(Gear^.X - Gear^.LinkedGear^.X,Gear^.Y-Gear^.LinkedGear^.Y)) >=Gear^.Radius*2)) then + begin + loadNewPortalBall(Gear, false); + inc(Gear^.Tag); + Gear^.doStep := @doStepPortal; + end + else + loadNewPortalBall(Gear, true); + end + + else if (y > cWaterLine) + or (y < -max(LAND_WIDTH,4096)) + or (x > 2*max(LAND_WIDTH,4096)) + or (x < -max(LAND_WIDTH,4096)) then + loadNewPortalBall(Gear, true); +end; + +procedure doStepMovingPortal(Gear: PGear); +begin + doPortalColorSwitch(); + doStepPerPixel(Gear, @doStepMovingPortal_real, true); + if (Gear^.Timer < 1) + or (Gear^.Hedgehog^.Team <> CurrentHedgehog^.Team) then + deleteGear(Gear); +end; + +procedure doStepPortalShot(newPortal: PGear); +var + iterator: PGear; + s: hwFloat; + CurWeapon: PAmmo; +begin + s:= Distance (newPortal^.dX, newPortal^.dY); + + // Adds the hog speed (only that part in/directly against shot direction) + // to the shot speed (which we triple previously btw) + // (This is done my projecting the hog movement vector onto the shot movement vector and then adding the resulting length + // to the scaler) + s := (_2 * s + (newPortal^.dX * CurrentHedgehog^.Gear^.dX + newPortal^.dY * CurrentHedgehog^.Gear^.dY ) / s) / s; + newPortal^.dX := newPortal^.dX * s; + newPortal^.dY := newPortal^.dY * s; + + newPortal^.LinkedGear := nil; + + if CurrentHedgehog <> nil then + with CurrentHedgehog^ do + begin + CurWeapon:= GetCurAmmoEntry(CurrentHedgehog^); + // let's save the HH's dX's direction so we can decide where the "top" of the portal hole + newPortal^.Elasticity.isNegative := CurrentHedgehog^.Gear^.dX.isNegative; + // when doing a backjump the dx is the opposite of the facing direction + if ((Gear^.State and gstHHHJump) <> 0) and (not cArtillery) then + newPortal^.Elasticity.isNegative := not newPortal^.Elasticity.isNegative; + + // make portal gun look unloaded + if (CurWeapon <> nil) and (CurAmmoType = amPortalGun) then + CurWeapon^.Timer := CurWeapon^.Timer or 2; + + iterator := GearsList; + while iterator <> nil do + begin + if (iterator^.Kind = gtPortal) then + if (iterator <> newPortal) and (iterator^.Timer > 0) and (iterator^.Hedgehog = CurrentHedgehog) then + begin + if ((iterator^.Tag and 2) = (newPortal^.Tag and 2)) then + begin + iterator^.Timer:= 0; + end + else + begin + // link portals with each other + newPortal^.LinkedGear := iterator; + iterator^.LinkedGear := newPortal; + iterator^.Health := newPortal^.Health; + end; + end; + iterator^.PortalCounter:= 0; + iterator := iterator^.NextGear + end; + + if newPortal^.LinkedGear <> nil then + begin + // This jiggles gears, to ensure a portal connection just placed under a gear takes effect. + iterator:= GearsList; + while iterator <> nil do + begin + if not (iterator^.Kind in [gtPortal, gtAirAttack, gtKnife]) and ((iterator^.Hedgehog <> CurrentHedgehog) + or ((iterator^.Message and gmAllStoppable) = 0)) then + begin + iterator^.Active:= true; + if iterator^.dY.QWordValue = 0 then + iterator^.dY.isNegative:= false; + iterator^.State:= iterator^.State or gstMoving; + DeleteCI(iterator); + //inc(iterator^.dY.QWordValue,10); + end; + iterator:= iterator^.NextGear + end + end + end; + newPortal^.State := newPortal^.State and (not gstCollision); + newPortal^.State := newPortal^.State or gstMoving; + newPortal^.doStep := @doStepMovingPortal; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepPiano(Gear: PGear); +var + r0, r1: LongInt; + odY: hwFloat; +begin + AllInactive := false; + if (CurrentHedgehog <> nil) and (CurrentHedgehog^.Gear <> nil) and + ((CurrentHedgehog^.Gear^.Message and gmSlot) <> 0) then + begin + case CurrentHedgehog^.Gear^.MsgParam of + 0: PlaySound(sndPiano0); + 1: PlaySound(sndPiano1); + 2: PlaySound(sndPiano2); + 3: PlaySound(sndPiano3); + 4: PlaySound(sndPiano4); + 5: PlaySound(sndPiano5); + 6: PlaySound(sndPiano6); + 7: PlaySound(sndPiano7); + else PlaySound(sndPiano8); + end; + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtNote); + CurrentHedgehog^.Gear^.MsgParam := 0; + CurrentHedgehog^.Gear^.Message := CurrentHedgehog^.Gear^.Message and (not gmSlot); + end; + + if (*((Gear^.Pos = 3) and ((GameFlags and gfSolidLand) <> 0)) or*) (Gear^.Pos = 5) then + begin + Gear^.dY := Gear^.dY + cGravity * 2; + Gear^.Y := Gear^.Y + Gear^.dY; + if CheckGearDrowning(Gear) then + begin + Gear^.Y:= Gear^.Y + _50; + OnUsedAmmo(CurrentHedgehog^); + if CurrentHedgehog^.Gear <> nil then + begin + // Drown the hedgehog. Could also just delete it, but hey, this gets a caption + CurrentHedgehog^.Gear^.Active := true; + CurrentHedgehog^.Gear^.X := Gear^.X; + CurrentHedgehog^.Gear^.Y := int2hwFloat(cWaterLine+cVisibleWater)+_128; + CurrentHedgehog^.Unplaced := false; + if TagTurnTimeLeft = 0 then + TagTurnTimeLeft:= TurnTimeLeft; + TurnTimeLeft:= 0 + end; + ResumeMusic + end; + exit + end; + + odY:= Gear^.dY; + doStepFallingGear(Gear); + + if (Gear^.State and gstDrowning) <> 0 then + begin + Gear^.Y:= Gear^.Y + _50; + OnUsedAmmo(CurrentHedgehog^); + if CurrentHedgehog^.Gear <> nil then + begin + // Drown the hedgehog. Could also just delete it, but hey, this gets a caption + CurrentHedgehog^.Gear^.Active := true; + CurrentHedgehog^.Gear^.X := Gear^.X; + CurrentHedgehog^.Gear^.Y := int2hwFloat(cWaterLine+cVisibleWater)+_128; + CurrentHedgehog^.Unplaced := false; + if TagTurnTimeLeft = 0 then + TagTurnTimeLeft:= TurnTimeLeft; + TurnTimeLeft:= 0 + end; + ResumeMusic + end + else if (Gear^.State and gstCollision) <> 0 then + begin + r0 := GetRandom(21); + r1 := GetRandom(21); + doMakeExplosion(hwRound(Gear^.X) - 30 - r0, hwRound(Gear^.Y) + 40, 40 + r1, Gear^.Hedgehog, 0); + doMakeExplosion(hwRound(Gear^.X) + 30 + r1, hwRound(Gear^.Y) + 40, 40 + r0, Gear^.Hedgehog, 0); + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 80 + r0, Gear^.Hedgehog, EXPLAutoSound); + for r0:= 0 to 4 do + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtNote); + Gear^.dY := cGravity * 2 - odY; + Gear^.Pos := Gear^.Pos + 1; + end + else + Gear^.dY := Gear^.dY + cGravity * 2; + // let it fall faster so itdoesn't take too long for the whole attack +end; + + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSineGunShotWork(Gear: PGear); +var + x, y, rX, rY, t, tmp, initHealth: LongInt; + oX, oY, ldX, ldY, sdX, sdY, sine, lx, ly, amp: hwFloat; + justCollided: boolean; +begin + AllInactive := false; + initHealth := Gear^.Health; + lX := Gear^.X; + lY := Gear^.Y; + ldX := Gear^.dX; + ldY := Gear^.dY; + sdy := _0_5/Distance(Gear^.dX,Gear^.dY); + ldX := ldX * sdy; + ldY := ldY * sdy; + sdY := hwAbs(ldX) + hwAbs(ldY); + sdX := _1 - hwAbs(ldX/sdY); + sdY := _1 - hwAbs(ldY/sdY); + if (ldX.isNegative = ldY.isNegative) then + sdY := -sdY; + + // initial angle depends on current GameTicks + t := getRandom(4096); + + + // used for a work-around detection of area that is within land array, but outside borders + justCollided := false; + + repeat + lX := lX + ldX; + lY := lY + ldY; + oX := Gear^.X; + oY := Gear^.Y; + rX := hwRound(oX); + rY := hwRound(oY); + tmp := t mod 4096; + amp := _128 * (_1 - hwSqr(int2hwFloat(Gear^.Health)/initHealth)); + sine := amp * AngleSin(tmp mod 2048); + sine.isNegative := (tmp < 2048); + inc(t,Gear^.Health div 313); + Gear^.X := lX + (sine * sdX); + Gear^.Y := ly + (sine * sdY); + Gear^.dX := Gear^.X - oX; + Gear^.dY := Gear^.Y - oY; + + x := hwRound(Gear^.X); + y := hwRound(Gear^.Y); + + // if borders are on, stop outside land array + if hasBorder and (((x and LAND_WIDTH_MASK) <> 0) or ((y and LAND_HEIGHT_MASK) <> 0)) then + begin + Gear^.Damage := 0; + Gear^.Health := 0; + end + else + begin + if (rY <= cWaterLine) or (y <= cWaterLine) then + begin + if ((y and LAND_HEIGHT_MASK) = 0) and ((x and LAND_WIDTH_MASK) = 0) + and (Land[y, x] <> 0) then + begin + if justCollided then + begin + Gear^.Damage := 0; + Gear^.Health := 0; + end + else + begin + inc(Gear^.Damage,3); + justCollided := true; + end; + end + else + justCollided := false; + + // kick nearby hogs, dig tunnel and add some fire + // if at least 5 collisions occured + if Gear^.Damage > 0 then + begin + DrawExplosion(rX,rY,Gear^.Radius); + + // kick nearby hogs + AmmoShove(Gear, 35, 50); + + dec(Gear^.Health, Gear^.Damage); + Gear^.Damage := 0; + + // add some fire to the tunnel + if getRandom(6) = 0 then + begin + tmp:= GetRandom(2 * Gear^.Radius); + AddGear(x - Gear^.Radius + tmp, y - GetRandom(Gear^.Radius + 1), gtFlame, gsttmpFlag, _0, _0, 0) + end + end; + + if random(100) = 0 then + AddVisualGear(x, y, vgtSmokeTrace); + end + else dec(Gear^.Health, 5); // if underwater get additional damage + end; + + dec(Gear^.Health); + + // decrease bullet size towards the end + if (Gear^.Radius > 4) then + begin + if (Gear^.Health <= (initHealth div 3)) then + dec(Gear^.Radius) + end + else if (Gear^.Radius > 3) then + begin + if (Gear^.Health <= (initHealth div 4)) then + dec(Gear^.Radius) + end + else if (Gear^.Radius > 2) then begin + if (Gear^.Health <= (initHealth div 5)) then + dec(Gear^.Radius) + end + else if (Gear^.Radius > 1) then + begin + if (Gear^.Health <= (initHealth div 6)) then + dec(Gear^.Radius) + end; + + until (Gear^.Health <= 0); + + DeleteGear(Gear); + AfterAttack; +end; + +procedure doStepSineGunShot(Gear: PGear); +var + HHGear: PGear; +begin + PlaySound(sndSineGun); + + // push the shooting Hedgehog back + HHGear := CurrentHedgehog^.Gear; + Gear^.dX.isNegative := not Gear^.dX.isNegative; + Gear^.dY.isNegative := not Gear^.dY.isNegative; + HHGear^.dX := Gear^.dX; + HHGear^.dY := Gear^.dY; + AmmoShove(Gear, 0, 80); + Gear^.dX.isNegative := not Gear^.dX.isNegative; + Gear^.dY.isNegative := not Gear^.dY.isNegative; + + Gear^.doStep := @doStepSineGunShotWork; + with mobileRecord do + if (performRumble <> nil) and (not fastUntilLag) then + performRumble(kSystemSoundID_Vibrate); +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepFlamethrowerWork(Gear: PGear); +var + HHGear, flame: PGear; + rx, ry, speed: hwFloat; + i, gX, gY: LongInt; +begin + AllInactive := false; + HHGear := Gear^.Hedgehog^.Gear; + HedgehogChAngle(HHGear); + gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle); + gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle); + + if (GameTicks and $FF) = 0 then + begin + if (HHGear^.Message and gmRight) <> 0 then + begin + if HHGear^.dX.isNegative and (Gear^.Tag < 20) then + inc(Gear^.Tag) + else if Gear^.Tag > 5 then + dec(Gear^.Tag); + end + else if (HHGear^.Message and gmLeft) <> 0 then + begin + if HHGear^.dX.isNegative and (Gear^.Tag > 5) then + dec(Gear^.Tag) + else if Gear^.Tag < 20 then + inc(Gear^.Tag); + end + end; + + dec(Gear^.Timer); + if Gear^.Timer = 0 then + begin + dec(Gear^.Health); + if (Gear^.Health mod 5) = 0 then + begin + rx := rndSign(getRandomf * _0_1); + ry := rndSign(getRandomf * _0_1); + speed := _0_5 * (_10 / Gear^.Tag); + + flame:= AddGear(gx, gy, gtFlame, gstTmpFlag, + SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx, + AngleCos(HHGear^.Angle) * ( - speed) + ry, 0); + flame^.CollisionMask:= lfNotCurrentMask; + + if (Gear^.Health mod 30) = 0 then + begin + flame:= AddGear(gx, gy, gtFlame, 0, + SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx, + AngleCos(HHGear^.Angle) * ( - speed) + ry, 0); + flame^.CollisionMask:= lfNotCurrentMask; + end + end; + Gear^.Timer:= Gear^.Tag + end; + + if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) then + begin + DeleteGear(Gear); + AfterAttack + end + else + begin + i:= Gear^.Health div 5; + if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then + begin + Gear^.Damage:= i; + FreeTexture(Gear^.Tex); + Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) + + '%', cWhiteColor, fntSmall) + end + end +end; + +procedure doStepFlamethrower(Gear: PGear); +var + HHGear: PGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown or gmLeft or gmRight)); + HHGear^.State := HHGear^.State or gstNotKickable; + Gear^.doStep := @doStepFlamethrowerWork +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepLandGunWork(Gear: PGear); +var + HHGear, land: PGear; + rx, ry, speed: hwFloat; + i, gX, gY: LongInt; +begin + AllInactive := false; + HHGear := Gear^.Hedgehog^.Gear; + HedgehogChAngle(HHGear); + gX := hwRound(Gear^.X) + GetLaunchX(amBallgun, hwSign(HHGear^.dX), HHGear^.Angle); + gY := hwRound(Gear^.Y) + GetLaunchY(amBallgun, HHGear^.Angle); + + if (GameTicks and $FF) = 0 then + begin + if (HHGear^.Message and gmRight) <> 0 then + begin + if HHGear^.dX.isNegative and (Gear^.Tag < 20) then + inc(Gear^.Tag) + else if Gear^.Tag > 5 then + dec(Gear^.Tag); + end + else if (HHGear^.Message and gmLeft) <> 0 then + begin + if HHGear^.dX.isNegative and (Gear^.Tag > 5) then + dec(Gear^.Tag) + else if Gear^.Tag < 20 then + inc(Gear^.Tag); + end + end; + + dec(Gear^.Timer); + if Gear^.Timer = 0 then + begin + dec(Gear^.Health); + + rx := rndSign(getRandomf * _0_1); + ry := rndSign(getRandomf * _0_1); + speed := (_3 / Gear^.Tag); + + land:= AddGear(gx, gy, gtFlake, gstTmpFlag, + SignAs(AngleSin(HHGear^.Angle) * speed, HHGear^.dX) + rx, + AngleCos(HHGear^.Angle) * ( - speed) + ry, 0); + land^.CollisionMask:= lfNotCurrentMask; + + Gear^.Timer:= Gear^.Tag + end; + + if (Gear^.Health = 0) or ((HHGear^.State and gstHHDriven) = 0) or ((HHGear^.Message and gmAttack) <> 0) then + begin + HHGear^.Message:= HHGear^.Message and (not gmAttack); + DeleteGear(Gear); + AfterAttack + end + else + begin + i:= Gear^.Health div 10; + if (i <> Gear^.Damage) and ((GameTicks and $3F) = 0) then + begin + Gear^.Damage:= i; + FreeTexture(Gear^.Tex); + Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(i) + + '%', cWhiteColor, fntSmall) + end + end +end; + +procedure doStepLandGun(Gear: PGear); +var + HHGear: PGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + HHGear^.Message := HHGear^.Message and (not (gmUp or gmDown or gmLeft or gmRight or gmAttack)); + HHGear^.State := HHGear^.State or gstNotKickable; + Gear^.doStep := @doStepLandGunWork +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepPoisonCloud(Gear: PGear); +begin + if Gear^.Timer = 0 then + begin + DeleteGear(Gear); + exit + end; + dec(Gear^.Timer); + Gear^.X:= Gear^.X + Gear^.dX; + Gear^.Y:= Gear^.Y + Gear^.dY; + Gear^.dX := Gear^.dX + cWindSpeed / 4; + Gear^.dY := Gear^.dY + cGravity / 100; + if (GameTicks and $FF) = 0 then + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 20, Gear^.Hedgehog, EXPLDontDraw or EXPLNoGfx or EXPLNoDamage or EXPLDoNotTouchAny or EXPLPoisoned); + AllInactive:= false; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepHammer(Gear: PGear); +var HHGear, tmp, tmp2: PGear; + t: PGearArray; + i: LongInt; +begin +HHGear:= Gear^.Hedgehog^.Gear; +HHGear^.State:= HHGear^.State or gstNoDamage; +DeleteCI(HHGear); + +t:= CheckGearsCollision(Gear); + +for i:= 5 downto 0 do + AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust); + +i:= t^.Count; +while i > 0 do + begin + dec(i); + tmp:= t^.ar[i]; + if (tmp^.State and gstNoDamage) = 0 then + if (tmp^.Kind = gtHedgehog) or (tmp^.Kind = gtMine) or (tmp^.Kind = gtExplosives) then + begin + //tmp^.State:= tmp^.State or gstFlatened; + if not tmp^.Invulnerable then + ApplyDamage(tmp, CurrentHedgehog, tmp^.Health div 3, dsUnknown); + //DrawTunnel(tmp^.X, tmp^.Y - _1, _0, _0_5, cHHRadius * 6, cHHRadius * 3); + tmp2:= AddGear(hwRound(tmp^.X), hwRound(tmp^.Y), gtHammerHit, 0, _0, _0, 0); + tmp2^.LinkedGear:= tmp; + SetAllToActive + end + else + begin + end + end; + +HHGear^.State:= HHGear^.State and (not gstNoDamage); +Gear^.Timer:= 250; +Gear^.doStep:= @doStepIdle +end; + +procedure doStepHammerHitWork(Gear: PGear); +var + i, j, ei: LongInt; + HitGear: PGear; +begin + AllInactive := false; + HitGear := Gear^.LinkedGear; + dec(Gear^.Timer); + if (HitGear = nil) or (Gear^.Timer = 0) or ((Gear^.Message and gmDestroy) <> 0) then + begin + DeleteGear(Gear); + exit + end; + + if (Gear^.Timer mod 5) = 0 then + begin + AddVisualGear(hwRound(Gear^.X) - 5 + Random(10), hwRound(Gear^.Y) + 12, vgtDust); + + i := hwRound(Gear^.X) - HitGear^.Radius + 2; + ei := hwRound(Gear^.X) + HitGear^.Radius - 2; + for j := 1 to 4 do DrawExplosion(i - GetRandom(5), hwRound(Gear^.Y) + 6*j, 3); + for j := 1 to 4 do DrawExplosion(ei + LongInt(GetRandom(5)), hwRound(Gear^.Y) + 6*j, 3); + while i <= ei do + begin + for j := 1 to 11 do DrawExplosion(i, hwRound(Gear^.Y) + 3*j, 3); + inc(i, 1) + end; + + if CheckLandValue(hwRound(Gear^.X + Gear^.dX + SignAs(_6,Gear^.dX)), hwRound(Gear^.Y + _1_9) + , lfIndestructible) then + begin + //Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + _1_9 + end; + end; + if TestCollisionYwithGear(Gear, 1) <> 0 then + begin + Gear^.dY := _0; + SetLittle(HitGear^.dX); + HitGear^.dY := _0; + end + else + begin + //Gear^.dY := Gear^.dY + cGravity; + //Gear^.Y := Gear^.Y + Gear^.dY; + if hwRound(Gear^.Y) > cWaterLine then + Gear^.Timer := 1 + end; + + //Gear^.X := Gear^.X + HitGear^.dX; + HitGear^.X := Gear^.X; + HitGear^.Y := Gear^.Y; + SetLittle(HitGear^.dY); + HitGear^.Active:= true; +end; + +procedure doStepHammerHit(Gear: PGear); +var + i, y: LongInt; + ar: TRangeArray; + HHGear: PGear; +begin + i := 0; + HHGear := Gear^.Hedgehog^.Gear; + + y := hwRound(Gear^.Y) - cHHRadius * 2; + while y < hwRound(Gear^.Y) do + begin + ar[i].Left := hwRound(Gear^.X) - Gear^.Radius - LongInt(GetRandom(2)); + ar[i].Right := hwRound(Gear^.X) + Gear^.Radius + LongInt(GetRandom(2)); + inc(y, 2); + inc(i) + end; + + DrawHLinesExplosions(@ar, 3, hwRound(Gear^.Y) - cHHRadius * 2, 2, Pred(i)); + Gear^.dY := HHGear^.dY; + DeleteCI(HHGear); + + doStepHammerHitWork(Gear); + Gear^.doStep := @doStepHammerHitWork +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepResurrectorWork(Gear: PGear); +var + graves: PGearArrayS; + resgear: PGear; + hh: PHedgehog; + i: LongInt; +begin + if (TurnTimeLeft > 0) then + dec(TurnTimeLeft); + + AllInactive := false; + hh := Gear^.Hedgehog; + + // no, you can't do that here + {DrawCentered(hwRound(hh^.Gear^.X) + WorldDx, hwRound(hh^.Gear^.Y) + WorldDy - + cHHRadius - 14 - hh^.HealthTagTex^.h, hh^.HealthTagTex); + } + (*DrawCircle(hwRound(Gear^.X), hwRound(Gear^.Y), Gear^.Radius, 1.5, 0, 0, $FF, + $FF);*) + + if ((Gear^.Message and gmUp) <> 0) then + begin + if (GameTicks and $F) <> 0 then + exit; + end + else if (GameTicks and $1FF) <> 0 then + exit; + + if Gear^.Power < 45 then + begin + inc(Gear^.Power); + if TestCollisionYwithGear(hh^.Gear, -1) = 0 then + hh^.Gear^.Y := hh^.Gear^.Y - _1; + end; + + graves := GearsNear(Gear^.X, Gear^.Y, gtGrave, Gear^.Radius); + + if graves.size = 0 then + begin + StopSoundChan(Gear^.SoundChannel); + Gear^.Timer := 250; + Gear^.doStep := @doStepIdle; + exit; + end; + + if ((Gear^.Message and gmAttack) <> 0) and (hh^.Gear^.Health > 0) and (TurnTimeLeft > 0) then + begin + if LongInt(graves.size) <= Gear^.Tag then Gear^.Tag:= 0; + dec(hh^.Gear^.Health); + if (hh^.Gear^.Health = 0) and (hh^.Gear^.Damage = 0) then + hh^.Gear^.Damage:= 1; + RenderHealth(hh^); + RecountTeamHealth(hh^.Team); + inc(graves.ar^[Gear^.Tag]^.Health); + inc(Gear^.Tag) +{-for i:= 0 to High(graves) do begin + if hh^.Gear^.Health > 0 then begin + dec(hh^.Gear^.Health); + inc(graves[i]^.Health); + end; + end; -} + end + else + begin + // now really resurrect the hogs with the hp saved in the graves + for i:= 0 to graves.size - 1 do + if graves.ar^[i]^.Health > 0 then + begin + resgear := AddGear(hwRound(graves.ar^[i]^.X), hwRound(graves.ar^[i]^.Y), gtHedgehog, gstWait, _0, _0, 0); + resgear^.Hedgehog := graves.ar^[i]^.Hedgehog; + resgear^.Health := graves.ar^[i]^.Health; + PHedgehog(graves.ar^[i]^.Hedgehog)^.Gear := resgear; + graves.ar^[i]^.Message:= graves.ar^[i]^.Message or gmDestroy; + graves.ar^[i]^.Active:= true; + RenderHealth(resgear^.Hedgehog^); + RecountTeamHealth(resgear^.Hedgehog^.Team); + resgear^.Hedgehog^.Effects[heResurrected]:= 1; + // only make hat-less hedgehogs look like zombies, preserve existing hats + + if resgear^.Hedgehog^.Hat = 'NoHat' then + LoadHedgehogHat(resgear^.Hedgehog^, 'Reserved/Zombie'); + end; + + hh^.Gear^.dY := _0; + hh^.Gear^.dX := _0; + doStepHedgehogMoving(hh^.Gear); + StopSoundChan(Gear^.SoundChannel); + Gear^.Timer := 250; + Gear^.doStep := @doStepIdle; + end + //if hh^.Gear^.Health = 0 then doStepHedgehogFree(hh^.Gear); +end; + +procedure doStepResurrector(Gear: PGear); +var + graves: PGearArrayS; + hh: PHedgehog; + i: LongInt; +begin + AllInactive := false; + graves := GearsNear(Gear^.X, Gear^.Y, gtGrave, Gear^.Radius); + + if graves.size > 0 then + begin + hh := Gear^.Hedgehog; + for i:= 0 to graves.size - 1 do + begin + PHedgehog(graves.ar^[i]^.Hedgehog)^.Gear := nil; + graves.ar^[i]^.Health := 0; + end; + Gear^.doStep := @doStepResurrectorWork; + if ((Gear^.Message and gmAttack) <> 0) and (hh^.Gear^.Health > 0) and (TurnTimeLeft > 0) then + begin + if LongInt(graves.size) <= Gear^.Tag then Gear^.Tag:= 0; + dec(hh^.Gear^.Health); + if (hh^.Gear^.Health = 0) and (hh^.Gear^.Damage = 0) then + hh^.Gear^.Damage:= 1; + RenderHealth(hh^); + RecountTeamHealth(hh^.Team); + inc(graves.ar^[Gear^.Tag]^.Health); + inc(Gear^.Tag) + end + end + else + begin + StopSoundChan(Gear^.SoundChannel); + Gear^.Timer := 250; + Gear^.doStep := @doStepIdle; + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepNapalmBomb(Gear: PGear); +var + i, gX, gY: LongInt; + dX, dY: hwFloat; +begin + AllInactive := false; + doStepFallingGear(Gear); + if (Gear^.Timer > 0) and ((Gear^.State and gstCollision) <> 0) then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLAutoSound); + gX := hwRound(Gear^.X); + gY := hwRound(Gear^.Y); + for i:= 0 to 10 do + begin + dX := AngleCos(i * 2) * ((_0_1*(i div 5))) * (GetRandomf + _1); + dY := AngleSin(i * 8) * _0_5 * (GetRandomf + _1); + AddGear(gX, gY, gtFlame, 0, dX, dY, 0); + AddGear(gX, gY, gtFlame, 0, dX, -dY, 0); + AddGear(gX, gY, gtFlame, 0, -dX, dY, 0); + AddGear(gX, gY, gtFlame, 0, -dX, -dY, 0); + end; + DeleteGear(Gear); + exit + end; + if (Gear^.Timer = 0) then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 10, Gear^.Hedgehog, EXPLAutoSound); + for i:= -19 to 19 do + FollowGear := AddGear(hwRound(Gear^.X) + i div 3, hwRound(Gear^.Y), gtFlame, 0, _0_001 * i, _0, 0); + DeleteGear(Gear); + exit + end; + if (GameTicks and $3F) = 0 then + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeTrace); + dec(Gear^.Timer) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepStructure(Gear: PGear); +var + x, y: LongInt; + HH: PHedgehog; + t: PGear; +begin + HH:= Gear^.Hedgehog; + + if (Gear^.State and gstMoving) <> 0 then + begin + AddGearCI(Gear); + Gear^.dX:= _0; + Gear^.dY:= _0; + Gear^.State:= Gear^.State and (not gstMoving); + end; + + dec(Gear^.Health, Gear^.Damage); + Gear^.Damage:= 0; + + if Gear^.Pos = 1 then + begin + AddGearCI(Gear); + AfterAttack; + if Gear = CurAmmoGear then + CurAmmoGear:= nil; + if HH^.Gear <> nil then + HideHog(HH); + Gear^.Pos:= 2 + end; + + if Gear^.Pos = 2 then + begin + if ((GameTicks mod 100) = 0) and (Gear^.Timer < 1000) then + begin + if (Gear^.Timer mod 10) = 0 then + begin + DeleteCI(Gear); + Gear^.Y:= Gear^.Y - _0_5; + AddGearCI(Gear); + end; + inc(Gear^.Timer); + end; + if Gear^.Tag <= TotalRounds then + Gear^.Pos:= 3; + end; + + if Gear^.Pos = 3 then + if Gear^.Timer < 1000 then + begin + if (Gear^.Timer mod 10) = 0 then + begin + DeleteCI(Gear); + Gear^.Y:= Gear^.Y - _0_5; + AddGearCI(Gear); + end; + inc(Gear^.Timer); + end + else + begin + if HH^.GearHidden <> nil then + RestoreHog(HH); + Gear^.Pos:= 4; + end; + + if Gear^.Pos = 4 then + if ((GameTicks mod 1000) = 0) and ((GameFlags and gfInvulnerable) = 0) then + begin + t:= GearsList; + while t <> nil do + begin + if (t^.Kind = gtHedgehog) and (t^.Hedgehog^.Team^.Clan = HH^.Team^.Clan) then + t^.Invulnerable:= true; + t:= t^.NextGear; + end; + end; + + if Gear^.Health <= 0 then + begin + if HH^.GearHidden <> nil then + RestoreHog(HH); + + x := hwRound(Gear^.X); + y := hwRound(Gear^.Y); + + DeleteCI(Gear); + DeleteGear(Gear); + + doMakeExplosion(x, y, 50, CurrentHedgehog, EXPLAutoSound); + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +(* + TARDIS needs + Warp in. Pos = 1 + Pause. Pos = 2 + Hide gear (TARDIS hedgehog was nil) + Warp out. Pos = 3 + ... idle active for some time period ... Pos = 4 + Warp in. Pos = 1 + Pause. Pos = 2 + Restore gear (TARDIS hedgehog was not nil) + Warp out. Pos = 3 +*) + +procedure doStepTardisWarp(Gear: PGear); +var HH: PHedgehog; + i,j,cnt: LongWord; +begin +HH:= Gear^.Hedgehog; +if Gear^.Pos = 2 then + begin + StopSoundChan(Gear^.SoundChannel); + if (Gear^.Timer = 0) then + begin + if (HH^.Gear <> nil) and (HH^.Gear^.State and gstInvisible = 0) then + begin + AfterAttack; + if Gear = CurAmmoGear then CurAmmoGear := nil; + if (HH^.Gear^.Damage = 0) and (HH^.Gear^.Health > 0) and + ((Gear^.State and (gstMoving or gstHHDeath or gstHHGone)) = 0) then + HideHog(HH) + end + //else if (HH^.Gear <> nil) and (HH^.Gear^.State and gstInvisible <> 0) then + else if (HH^.GearHidden <> nil) then// and (HH^.Gear^.State and gstInvisible <> 0) then + RestoreHog(HH) + end; + + inc(Gear^.Timer); + if (Gear^.Timer > 2000) and ((GameTicks mod 2000) = 1000) then + begin + Gear^.SoundChannel := LoopSound(sndTardis); + Gear^.Pos:= 3 + end + end; + +if (Gear^.Pos = 1) and (GameTicks and $1F = 0) and (Gear^.Power < 255) then + begin + inc(Gear^.Power); + if (Gear^.Power = 172) and (HH^.Gear <> nil) and + (HH^.Gear^.Damage = 0) and (HH^.Gear^.Health > 0) and + ((HH^.Gear^.State and (gstMoving or gstHHDeath or gstHHGone)) = 0) then + with HH^.Gear^ do + begin + State:= State or gstAnimation; + Tag:= 2; + Timer:= 0; + Pos:= 0 + end + end; +if (Gear^.Pos = 3) and (GameTicks and $1F = 0) and (Gear^.Power > 0) then + dec(Gear^.Power); +if (Gear^.Pos = 1) and (Gear^.Power = 255) and ((GameTicks mod 2000) = 1000) then + Gear^.Pos:= 2; +if (Gear^.Pos = 3) and (Gear^.Power = 0) then + begin + StopSoundChan(Gear^.SoundChannel); + if HH^.GearHidden = nil then + begin + DeleteGear(Gear); + exit + end; + Gear^.Pos:= 4; + // This condition might need tweaking + Gear^.Timer:= GetRandom(cHedgehogTurnTime*TeamsCount)+cHedgehogTurnTime + end; + +if (Gear^.Pos = 4) then + begin + cnt:= 0; + for j:= 0 to Pred(HH^.Team^.Clan^.TeamsNumber) do + for i:= 0 to Pred(HH^.Team^.Clan^.Teams[j]^.HedgehogsNumber) do + if (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear <> nil) + and ((HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.State and gstDrowning) = 0) + and (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Health > HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Damage) then + inc(cnt); + if (cnt = 0) or SuddenDeathDmg or (Gear^.Timer = 0) then + begin + if HH^.GearHidden <> nil then + FindPlace(HH^.GearHidden, false, 0, LAND_WIDTH,true); + + if HH^.GearHidden <> nil then + begin + Gear^.X:= HH^.GearHidden^.X; + Gear^.Y:= HH^.GearHidden^.Y; + end; + Gear^.Timer:= 0; + + if (HH^.GearHidden <> nil) and (cnt = 0) then // do an emergency jump back in this case. the team needs you! + begin + AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtExplosion); + Gear^.Pos:= 2; + Gear^.Power:= 255; + end + else begin + Gear^.SoundChannel := LoopSound(sndTardis); + Gear^.Pos:= 1; + Gear^.Power:= 0; + end + end + else if (CurrentHedgehog^.Team^.Clan = Gear^.Hedgehog^.Team^.Clan) then dec(Gear^.Timer) + end; + +end; + +procedure doStepTardis(Gear: PGear); +var i,j,cnt: LongWord; + HH: PHedgehog; +begin +(* + Conditions for not activating. + 1. Hog is last of his clan + 2. Sudden Death is in play + 3. Hog is a king +*) + HH:= Gear^.Hedgehog; + if HH^.Gear <> nil then + if (HH^.Gear = nil) or (HH^.King) or (SuddenDeathDmg) then + begin + if HH^.Gear <> nil then + begin + HH^.Gear^.Message := HH^.Gear^.Message and (not gmAttack); + HH^.Gear^.State:= HH^.Gear^.State and (not gstAttacking); + end; + PlaySound(sndDenied); + DeleteGear(gear); + exit + end; + cnt:= 0; + for j:= 0 to Pred(HH^.Team^.Clan^.TeamsNumber) do + for i:= 0 to Pred(HH^.Team^.Clan^.Teams[j]^.HedgehogsNumber) do + if (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear <> nil) + and ((HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.State and gstDrowning) = 0) + and (HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Health > HH^.Team^.Clan^.Teams[j]^.Hedgehogs[i].Gear^.Damage) then + inc(cnt); + if cnt < 2 then + begin + if HH^.Gear <> nil then + begin + HH^.Gear^.Message := HH^.Gear^.Message and (not gmAttack); + HH^.Gear^.State:= HH^.Gear^.State and (not gstAttacking); + end; + PlaySound(sndDenied); + DeleteGear(gear); + exit + end; + Gear^.SoundChannel := LoopSound(sndTardis); + Gear^.doStep:= @doStepTardisWarp +end; + +//////////////////////////////////////////////////////////////////////////////// + +(* +WIP. The ice gun will have the following effects. It has been proposed by sheepluva that it take the appearance of a large freezer +spewing ice cubes. The cubes will be visual gears only. The scatter from them and the impact snow dust should help hide imprecisions in things like the GearsNear effect. +For now we assume a "ray" like a deagle projected out from the gun. +All these effects assume the ray's angle is not changed and that the target type was unchanged over a number of ticks. This is a simplifying assumption for "gun was applying freezing effect to the same target". + * When fired at water a layer of ice textured land is added above the water. + * When fired at non-ice land (land and lfLandMask and not lfIce) the land is overlaid with a thin layer of ice textured land around that point (say, 1 or 2px into land, 1px above). For attractiveness, a slope would probably be needed. + * When fired at a hog (land and $00FF <> 0), while the hog is targetted, the hog's state is set to frozen. As long as the gun is on the hog, a frozen hog sprite creeps up from the feet to the head. If the effect is interrupted before reaching the top, the freezing state is cleared. +A frozen hog will animate differently. To be decided, but possibly in a similar fashion to a grave when it comes to explosions. The hog might (possibly) not be damaged by explosions. This might make freezing potentially useful for friendlies in a bad position. It might be better to allow damage though. +A frozen hog stays frozen for a certain number of turns. Each turn the frozen overlay becomes fainter, until it fades and the hog animates normally again. +*) + + +procedure updateFuel(Gear: PGear); +var + t:LongInt; +begin + t:= Gear^.Health div 10; + if (t <> Gear^.Damage) and ((GameTicks and $3F) = 0) then + begin + Gear^.Damage:= t; + FreeTexture(Gear^.Tex); + Gear^.Tex := RenderStringTex(trmsg[sidFuel] + ': ' + inttostr(t) + + '%', cWhiteColor, fntSmall) + end; + if Gear^.Message and (gmUp or gmDown) <> 0 then + begin + StopSoundChan(Gear^.SoundChannel); + Gear^.SoundChannel:= -1; + if GameTicks mod 40 = 0 then dec(Gear^.Health) + end + else + begin + if Gear^.SoundChannel = -1 then + Gear^.SoundChannel := LoopSound(sndIceBeam); + if GameTicks mod 10 = 0 then dec(Gear^.Health) + end +end; + + +procedure updateTarget(Gear:PGear; newX, newY:HWFloat); +// var +// iter:PGear; +begin + with Gear^ do + begin + dX:= newX; + dY:= newY; + Pos:= 0; + Target.X:= NoPointX; + LastDamage:= nil; + X:= Hedgehog^.Gear^.X; + Y:= Hedgehog^.Gear^.Y; + end; +end; + +procedure doStepIceGun(Gear: PGear); +const iceWaitCollision = 0; +const iceCollideWithGround = 1; +//const iceWaitNextTarget:Longint = 2; +//const iceCollideWithHog:Longint = 4; +const iceCollideWithWater = 5; +//const waterFreezingTime:Longint = 500; +const groundFreezingTime = 1000; +const iceRadius = 32; +const iceHeight = 40; +var + HHGear, iter: PGear; + landRect: TSDL_Rect; + ndX, ndY: hwFloat; + i, t, gX, gY: LongInt; + hogs: PGearArrayS; + vg: PVisualGear; +begin + HHGear := Gear^.Hedgehog^.Gear; + if (Gear^.Message and gmAttack <> 0) or (Gear^.Health = 0) or (HHGear = nil) or (HHGear^.Damage <> 0) or (HHGear^.dX.QWordValue > 4294967) then + begin + StopSoundChan(Gear^.SoundChannel); + DeleteGear(Gear); + AfterAttack; + exit + end; + updateFuel(Gear); + + with Gear^ do + begin + HedgehogChAngle(HHGear); + ndX:= SignAs(AngleSin(HHGear^.Angle), HHGear^.dX) * _4; + ndY:= -AngleCos(HHGear^.Angle) * _4; + if (ndX <> dX) or (ndY <> dY) or + ((Target.X <> NoPointX) and (Target.X and LAND_WIDTH_MASK = 0) and + (Target.Y and LAND_HEIGHT_MASK = 0) and ((Land[Target.Y, Target.X] = 0))) then + begin + updateTarget(Gear, ndX, ndY); + Timer := iceWaitCollision; + end + else + begin + X:= X + dX; + Y:= Y + dY; + gX:= hwRound(X); + gY:= hwRound(Y); + if Target.X = NoPointX then t:= hwRound(hwSqr(X-HHGear^.X)+hwSqr(Y-HHGear^.Y)); + + if Target.X <> NoPointX then + begin + CheckCollision(Gear); + if (State and gstCollision) <> 0 then + begin + if Timer = iceWaitCollision then + begin + Timer := iceCollideWithGround; + Power := GameTicks; + end + end + else if (target.y >= cWaterLine) then + begin + if Timer = iceWaitCollision then + begin + Timer := iceCollideWithWater; + Power := GameTicks; + end; + end; + + if (abs(gX-Target.X) < 2) and (abs(gY-Target.Y) < 2) then + begin + X:= HHGear^.X; + Y:= HHGear^.Y + end; + + if (Timer = iceCollideWithGround) and ((GameTicks - Power) > groundFreezingTime) then + begin + FillRoundInLand(target.x, target.y, iceRadius, icePixel); + landRect.x := min(max(target.x - iceRadius, 0), LAND_WIDTH - 1); + landRect.y := min(max(target.y - iceRadius, 0), LAND_HEIGHT - 1); + landRect.w := min(2*iceRadius, LAND_WIDTH - landRect.x - 1); + landRect.h := min(2*iceRadius, LAND_HEIGHT - landRect.y - 1); + UpdateLandTexture(landRect.x, landRect.w, landRect.y, landRect.h, true); + + // Freeze nearby mines/explosives/cases too + iter := GearsList; + while iter <> nil do + begin + if (iter^.State and gstFrozen = 0) and + ((iter^.Kind = gtExplosives) or (iter^.Kind = gtCase) or (iter^.Kind = gtMine)) and + (abs(iter^.X.Round-target.x)+abs(iter^.Y.Round-target.y)+2<2*iceRadius) and (Distance(iter^.X-int2hwFloat(target.x),iter^.Y-int2hwFloat(target.y)) nil then + begin + i:= random(100) + 155; + vg^.Tint:= (i shl 24) or (i shl 16) or ($FF shl 8) or (random(200) + 55); + vg^.Angle:= random(360); + vg^.dx:= 0.001 * random(80); + vg^.dy:= 0.001 * random(80) + end + end; + PlaySound(sndHogFreeze); + if iter^.Kind = gtMine then // dud mine block + begin + iter^.State:= iter^.State or gstFrozen; + vg:= AddVisualGear(hwRound(iter^.X) - 4 + Random(8), hwRound(iter^.Y) - 4 - Random(4), vgtSmoke); + if vg <> nil then + vg^.Scale:= 0.5; + PlaySound(sndVaporize); + iter^.Health := 0; + iter^.Damage := 0; + iter^.State := iter^.State and (not gstAttacking) + end + else if iter^.Kind = gtCase then + begin + DeleteCI(iter); + iter^.State:= iter^.State or gstFrozen; + AddGearCI(iter) + end + else // gtExplosives + begin + iter^.State:= iter^.State or gstFrozen; + iter^.Health:= iter^.Health + cBarrelHealth + end + end; + iter:= iter^.NextGear + end; + + // FillRoundInLandWithIce(Target.X, Target.Y, iceRadius); + SetAllHHToActive; + Timer := iceWaitCollision; + end; + + if (Timer = iceCollideWithWater) and ((GameTicks - Power) > groundFreezingTime) then + begin + PlaySound(sndHogFreeze); + DrawIceBreak(Target.X, cWaterLine - iceHeight, iceRadius, iceHeight); + SetAllHHToActive; + Timer := iceWaitCollision; + end; +(* + Any ideas for something that would look good here? + if (Target.X <> NoPointX) and ((Timer = iceCollideWithGround) or (Timer = iceCollideWithWater)) and (GameTicks mod max((groundFreezingTime-((GameTicks - Power)*2)),2) = 0) then //and CheckLandValue(Target.X, Target.Y, lfIce) then + begin + vg:= AddVisualGear(Target.X+random(20)-10, Target.Y+random(40)-10, vgtDust, 1); + if vg <> nil then + begin + i:= random(100) + 155; + vg^.Tint:= IceColor or $FF; + vg^.Angle:= random(360); + vg^.dx:= 0.001 * random(80); + vg^.dy:= 0.001 * random(80) + end + end; +*) + +// freeze nearby hogs + hogs := GearsNear(int2hwFloat(Target.X), int2hwFloat(Target.Y), gtHedgehog, Gear^.Radius*2); + if hogs.size > 0 then + for i:= 0 to hogs.size - 1 do + if hogs.ar^[i] <> HHGear then + if GameTicks mod 5 = 0 then + begin + hogs.ar^[i]^.Active:= true; + if hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] < 256 then + hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] := hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] + 1 + else if hogs.ar^[i]^.Hedgehog^.Effects[heFrozen] = 256 then + begin + hogs.ar^[i]^.Hedgehog^.Effects[heFrozen]:= 200000-1;//cHedgehogTurnTime + cReadyDelay + PlaySound(sndHogFreeze); + end; + end; + inc(Pos) + end + else if (t > 400) and ((gY > cWaterLine) or + (((gX and LAND_WIDTH_MASK = 0) and (gY and LAND_HEIGHT_MASK = 0)) + and (Land[gY, gX] <> 0))) then + begin + Target.X:= gX; + Target.Y:= gY; + X:= HHGear^.X; + Y:= HHGear^.Y + end; + if (gX > max(LAND_WIDTH,4096)*2) or + (gX < -max(LAND_WIDTH,4096)) or + (gY < -max(LAND_HEIGHT,4096)) or + (gY > max(LAND_HEIGHT,4096)+512) then + begin + //X:= HHGear^.X; + //Y:= HHGear^.Y + Target.X:= gX; + Target.Y:= gY; + end + end + end; +end; + +procedure doStepAddAmmo(Gear: PGear); +var a: TAmmoType; + gi: PGear; +begin +if Gear^.Timer > 0 then dec(Gear^.Timer) +else + begin + if Gear^.Pos = posCaseUtility then + a:= GetUtility(Gear^.Hedgehog) + else + a:= GetAmmo(Gear^.Hedgehog); + CheckSum:= CheckSum xor GameTicks; + gi := GearsList; + while gi <> nil do + begin + with gi^ do CheckSum:= CheckSum xor X.round xor X.frac xor dX.round xor dX.frac xor Y.round xor Y.frac xor dY.round xor dY.frac; + AddRandomness(CheckSum); + if gi^.Kind = gtGenericFaller then gi^.State:= gi^.State and not gstTmpFlag; + gi := gi^.NextGear + end; + AddPickup(Gear^.Hedgehog^, a, Gear^.Power, hwRound(Gear^.X), hwRound(Gear^.Y)); + DeleteGear(Gear) + end; +end; + +procedure doStepGenericFaller(Gear: PGear); +begin +if Gear^.Timer < $FFFFFFFF then + if Gear^.Timer > 0 then + dec(Gear^.Timer) + else + begin + DeleteGear(Gear); + exit + end; +if (Gear^.State and gstTmpFlag <> 0) or (GameTicks and $7 = 0) then + begin + doStepFallingGear(Gear); + if (Gear^.State and gstInvisible <> 0) and (GameTicks and $FF = 0) and (hwRound(Gear^.X) < LongInt(leftX)) or (hwRound(Gear^.X) > LongInt(rightX)) or (hwRound(Gear^.Y) < LongInt(topY)) then + begin + Gear^.X:= int2hwFloat(GetRandom(rightX-leftX)+leftX); + Gear^.Y:= int2hwFloat(GetRandom(LAND_HEIGHT-topY)+topY); + Gear^.dX:= _90-(GetRandomf*_360); + Gear^.dY:= _90-(GetRandomf*_360) + end; + end +end; + +procedure doStepCreeper(Gear: PGear); +var hogs: PGearArrayS; + HHGear: PGear; + tdX: hwFloat; + dir: LongInt; +begin +doStepFallingGear(Gear); +if Gear^.Timer > 0 then dec(Gear^.Timer); +// creeper sleep phase +if (Gear^.Hedgehog = nil) and (Gear^.Timer > 0) then exit; + +if Gear^.Hedgehog <> nil then HHGear:= Gear^.Hedgehog^.Gear +else HHGear:= nil; + +// creeper boom phase +if (Gear^.State and gstTmpFlag <> 0) then + begin + if (Gear^.Timer = 0) then + begin + doMakeExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), 300, CurrentHedgehog, EXPLAutoSound); + DeleteGear(Gear) + end; + // ssssss he essssscaped + if (Gear^.Timer > 250) and ((HHGear = nil) or + (((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) > 180) and + (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) > _180))) then + begin + Gear^.State:= Gear^.State and (not gstTmpFlag); + Gear^.Timer:= 0 + end; + exit + end; + +// Search out a new target, as target seek time has expired, target is dead, target is out of range, or we did not have a target +if (HHGear = nil) or (Gear^.Timer = 0) or + (((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) > Gear^.Angle) and + (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) > int2hwFloat(Gear^.Angle))) + then + begin + hogs := GearsNear(Gear^.X, Gear^.Y, gtHedgehog, Gear^.Angle); + if hogs.size > 1 then + Gear^.Hedgehog:= hogs.ar^[GetRandom(hogs.size)]^.Hedgehog + else if hogs.size = 1 then Gear^.Hedgehog:= hogs.ar^[0]^.Hedgehog + else Gear^.Hedgehog:= nil; + if Gear^.Hedgehog <> nil then Gear^.Timer:= 5000; + exit + end; + +// we have a target. move the creeper. +if HHGear <> nil then + begin + // GOTCHA + if ((abs(HHGear^.X.Round-Gear^.X.Round) + abs(HHGear^.Y.Round-Gear^.Y.Round) + 2) < 50) and + (Distance(HHGear^.X-Gear^.X,HHGear^.Y-Gear^.Y) < _50) then + begin + // hisssssssssss + Gear^.State:= Gear^.State or gstTmpFlag; + Gear^.Timer:= 1500; + exit + end; + if (Gear^.State and gstMoving <> 0) then + begin + Gear^.dY:= _0; + Gear^.dX:= _0; + end + else if (GameTicks and $FF = 0) then + begin + tdX:= HHGear^.X-Gear^.X; + dir:= hwSign(tdX); + if not TestCollisionX(Gear, dir) then + Gear^.X:= Gear^.X + signAs(_1,tdX); + if TestCollisionXwithXYShift(Gear, signAs(_10,tdX), 0, dir) then + begin + Gear^.dX:= SignAs(_0_15, tdX); + Gear^.dY:= -_0_3; + Gear^.State:= Gear^.State or gstMoving + end + end; + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepKnife(Gear: PGear); +//var ox, oy: LongInt; +// la: hwFloat; +var a: real; +begin + // Gear is shrunk so it can actually escape the hog without carving into the terrain + if (Gear^.Radius = 4) and (Gear^.CollisionMask = $FFFF) then Gear^.Radius:= 7; + if Gear^.Damage > 100 then Gear^.CollisionMask:= 0 + else if Gear^.Damage > 30 then + if GetRandom(max(4,18-Gear^.Damage div 10)) < 3 then Gear^.CollisionMask:= 0; + Gear^.Damage:= 0; + if Gear^.Timer > 0 then dec(Gear^.Timer); + if (Gear^.State and gstMoving <> 0) and (Gear^.State and gstCollision = 0) then + begin + DeleteCI(Gear); + Gear^.Radius:= 7; + // used for damage and impact calc. needs balancing I think + Gear^.Health:= hwRound(hwSqr((hwAbs(Gear^.dY)+hwAbs(Gear^.dX))*_4)); + doStepFallingGear(Gear); + AllInactive := false; + a:= Gear^.DirAngle; + CalcRotationDirAngle(Gear); + Gear^.DirAngle:= a+(Gear^.DirAngle-a)*2*hwSign(Gear^.dX) // double rotation + end + else if (Gear^.CollisionIndex = -1) and (Gear^.Timer = 0) then + begin + (*ox:= 0; oy:= 0; + if TestCollisionYwithGear(Gear, -1) <> 0 then oy:= -1; + if TestCollisionXwithGear(Gear, 1) then ox:= 1; + if TestCollisionXwithGear(Gear, -1) then ox:= -1; + if TestCollisionYwithGear(Gear, 1) <> 0 then oy:= 1; + if Gear^.Health > 0 then + PlaySound(sndRopeAttach); + + la:= _10000; + if (ox <> 0) or (oy <> 0) then + la:= CalcSlopeNearGear(Gear, ox, oy); + if la = _10000 then + begin + // debug for when we couldn't get an angle + //AddVisualGear(hwRound(Gear^.X), hwRound(Gear^.Y), vgtSmokeWhite); +*) + Gear^.DirAngle:= DxDy2Angle(Gear^.dX, Gear^.dY) + (random(30)-15); + if (Gear^.dX.isNegative and Gear^.dY.isNegative) or + ((not Gear^.dX.isNegative) and (not Gear^.dY.isNegative)) then Gear^.DirAngle:= Gear^.DirAngle-90; + // end + // else Gear^.DirAngle:= hwFloat2Float(la)*90; // sheepluva's comment claims 45deg = 0.5 - yet orientation doesn't seem consistent? + // AddFileLog('la: '+floattostr(la)+' DirAngle: '+inttostr(round(Gear^.DirAngle))); + Gear^.dX:= _0; + Gear^.dY:= _0; + Gear^.State:= Gear^.State and (not gstMoving) or gstCollision; + Gear^.Radius:= 16; + if Gear^.Health > 0 then AmmoShove(Gear, Gear^.Health, 0); + Gear^.Health:= 0; + Gear^.Timer:= 500; + AddGearCI(Gear) + end + else if GameTicks and $3F = 0 then + begin + if (TestCollisionYwithGear(Gear, -1) = 0) + and (not TestCollisionXwithGear(Gear, 1)) + and (not TestCollisionXwithGear(Gear, -1)) + and (TestCollisionYwithGear(Gear, 1) = 0) then Gear^.State:= Gear^.State and (not gstCollision) or gstMoving; + end +end; +(* + This didn't end up getting used, but, who knows, might be reasonable for javellin or something +// Make the knife initial angle based on the hog attack angle, or is that too hard? +procedure doStepKnife(Gear: PGear); +var t, + gx, gy, ga, // gear x,y,angle + lx, ly, la, // land x,y,angle + ox, oy, // x,y offset + w, h, // wXh of clip area + tx, ty // tip position in sprite + : LongInt; + surf: PSDL_Surface; + s: hwFloat; + +begin + Gear^.dY := Gear^.dY + cGravity; + if (GameFlags and gfMoreWind) <> 0 then + Gear^.dX := Gear^.dX + cWindSpeed / Gear^.Density; + Gear^.X := Gear^.X + Gear^.dX; + Gear^.Y := Gear^.Y + Gear^.dY; + CheckGearDrowning(Gear); + gx:= hwRound(Gear^.X); + gy:= hwRound(Gear^.Y); + if Gear^.State and gstDrowning <> 0 then exit; + with Gear^ do + begin + if CheckLandValue(gx, gy, lfLandMask) then + begin + t:= Angle + hwRound((hwAbs(dX)+hwAbs(dY)) * _10); + + if t < 0 then inc(t, 4096) + else if 4095 < t then dec(t, 4096); + Angle:= t; + + DirAngle:= Angle / 4096 * 360 + end + else + begin +//This is the set of postions for the knife. +//Using FlipSurface and copyToXY the knife can be written to the LandPixels at 32 positions, and an appropriate line drawn in Land. + t:= Angle mod 1024; + case t div 128 of + 0: begin + ox:= 2; oy:= 5; + w := 25; h:= 5; + tx:= 0; ty:= 2 + end; + 1: begin + ox:= 2; oy:= 15; + w:= 24; h:= 8; + tx:= 0; ty:= 7 + end; + 2: begin + ox:= 2; oy:= 27; + w:= 23; h:= 12; + tx:= -12; ty:= -5 + end; + 3: begin + ox:= 2; oy:= 43; + w:= 21; h:= 15; + tx:= 0; ty:= 14 + end; + 4: begin + ox:= 29; oy:= 8; + w:= 19; h:= 19; + tx:= 0; ty:= 17 + end; + 5: begin + ox:= 29; oy:= 32; + w:= 15; h:= 21; + tx:= 0; ty:= 20 + end; + 6: begin + ox:= 51; oy:= 3; + w:= 11; h:= 23; + tx:= 0; ty:= 22 + end; + 7: begin + ox:= 51; oy:= 34; + w:= 7; h:= 24; + tx:= 0; ty:= 23 + end + end; + + surf:= SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, RMask, GMask, BMask, AMask); + copyToXYFromRect(SpritesData[sprKnife].Surface, surf, ox, oy, w, h, 0, 0); + // try to make the knife hit point first + lx := 0; + ly := 0; + if CalcSlopeTangent(Gear, gx, gy, lx, ly, 255) then + begin + la:= vector2Angle(int2hwFloat(lx), int2hwFloat(ly)); + ga:= vector2Angle(dX, dY); + AddFileLog('la: '+inttostr(la)+' ga: '+inttostr(ga)+' Angle: '+inttostr(Angle)); + // change to 0 to 4096 forced by LongWord in Gear + if la < 0 then la:= 4096+la; + if ga < 0 then ga:= 4096+ga; + if ((Angle > ga) and (Angle < la)) or ((Angle < ga) and (Angle > la)) then + begin + if Angle >= 2048 then dec(Angle, 2048) + else if Angle < 2048 then inc(Angle, 2048) + end; + AddFileLog('la: '+inttostr(la)+' ga: '+inttostr(ga)+' Angle: '+inttostr(Angle)) + end; + case Angle div 1024 of + 0: begin + flipSurface(surf, true); + flipSurface(surf, true); + BlitImageAndGenerateCollisionInfo(gx-(w-tx), gy-(h-ty), w, surf) + end; + 1: begin + flipSurface(surf, false); + BlitImageAndGenerateCollisionInfo(gx-(w-tx), gy-ty, w, surf) + end; + 2: begin // knife was actually drawn facing this way... + BlitImageAndGenerateCollisionInfo(gx-tx, gy-ty, w, surf) + end; + 3: begin + flipSurface(surf, true); + BlitImageAndGenerateCollisionInfo(gx-tx, gy-(h-ty), w, surf) + end + end; + SDL_FreeSurface(surf); + // this needs to calculate actual width/height + land clipping since update texture doesn't. + // i.e. this will crash if you fire near sides of map, but until I get the blit right, not going to put real values + UpdateLandTexture(hwRound(X)-32, 64, hwRound(Y)-32, 64, true); + DeleteGear(Gear); + exit + end + end; +end; +*) + +end. diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/uGearsHedgehog.pas --- a/hedgewars/uGearsHedgehog.pas Wed Jun 26 21:40:10 2013 -0400 +++ b/hedgewars/uGearsHedgehog.pas Thu Jun 27 15:51:20 2013 +0400 @@ -33,9 +33,9 @@ implementation uses uConsts, uVariables, uFloat, uAmmos, uSound, uCaptions, - uCommands, uLocale, uUtils, uVisualGears, uStats, uIO, uScript, - uGearsList, uGears, uCollisions, uRandom, uStore, uTeams, - uGearsUtils; + uCommands, uLocale, uUtils, uStats, uIO, uScript, + uGearsList, uCollisions, uRandom, uStore, uTeams, + uGearsUtils, uVisualGearsList; var GHStepTicks: LongWord = 0; diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/uGearsRender.pas --- a/hedgewars/uGearsRender.pas Wed Jun 26 21:40:10 2013 -0400 +++ b/hedgewars/uGearsRender.pas Thu Jun 27 15:51:20 2013 +0400 @@ -38,7 +38,7 @@ end; implementation -uses uRender, uUtils, uVariables, uAmmos, Math, uVisualGears; +uses uRender, uUtils, uVariables, uAmmos, Math, uVisualGearsList; procedure DrawRopeLinesRQ(Gear: PGear); begin diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/uGearsUtils.pas --- a/hedgewars/uGearsUtils.pas Wed Jun 26 21:40:10 2013 -0400 +++ b/hedgewars/uGearsUtils.pas Thu Jun 27 15:51:20 2013 +0400 @@ -20,7 +20,7 @@ unit uGearsUtils; interface -uses uTypes; +uses uTypes, uFloat; procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword); inline; procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword; const Tint: LongWord); @@ -41,16 +41,31 @@ procedure CheckCollision(Gear: PGear); inline; procedure CheckCollisionWithLand(Gear: PGear); inline; +procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); +function GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): PGearArrayS; +procedure SpawnBoxOfSmth; +procedure ShotgunShot(Gear: PGear); + +procedure SetAllToActive; +procedure SetAllHHToActive; inline; +procedure SetAllHHToActive(Ice: boolean); + +function GetAmmo(Hedgehog: PHedgehog): TAmmoType; +function GetUtility(Hedgehog: PHedgehog): TAmmoType; + + + function MakeHedgehogsStep(Gear: PGear) : boolean; var doStepHandlers: array[TGearType] of TGearStepProcedure; implementation -uses uFloat, uSound, uCollisions, uUtils, uConsts, uVisualGears, uAIMisc, +uses uSound, uCollisions, uUtils, uConsts, uVisualGears, uAIMisc, uVariables, uLandGraphics, uScript, uStats, uCaptions, uTeams, uStore, - uLocale, uTextures, uRenderUtils, uRandom, SDLh, uDebug, uGears, - uGearsList, Math; + uLocale, uTextures, uRenderUtils, uRandom, SDLh, uDebug, + uGearsList, Math, uVisualGearsList, uGearsHandlersMess, + uGearsHedgehog; procedure doMakeExplosion(X, Y, Radius: LongInt; AttackingHog: PHedgehog; Mask: Longword); inline; begin @@ -766,4 +781,422 @@ end; end; + +procedure ShotgunShot(Gear: PGear); +var t: PGear; + dmg, r, dist: LongInt; + dx, dy: hwFloat; +begin +Gear^.Radius:= cShotgunRadius; +t:= GearsList; +while t <> nil do + begin + case t^.Kind of + gtHedgehog, + gtMine, + gtSMine, + gtKnife, + gtCase, + gtTarget, + gtExplosives: begin//, +// gtStructure: begin +//addFileLog('ShotgunShot radius: ' + inttostr(Gear^.Radius) + ', t^.Radius = ' + inttostr(t^.Radius) + ', distance = ' + inttostr(dist) + ', dmg = ' + inttostr(dmg)); + dmg:= 0; + r:= Gear^.Radius + t^.Radius; + dx:= Gear^.X-t^.X; + dx.isNegative:= false; + dy:= Gear^.Y-t^.Y; + dy.isNegative:= false; + if r-hwRound(dx+dy) > 0 then + begin + dist:= hwRound(Distance(dx, dy)); + dmg:= ModifyDamage(min(r - dist, 25), t); + end; + if dmg > 0 then + begin + if (not t^.Invulnerable) then + ApplyDamage(t, Gear^.Hedgehog, dmg, dsBullet) + else + Gear^.State:= Gear^.State or gstWinner; + + DeleteCI(t); + t^.dX:= t^.dX + Gear^.dX * dmg * _0_01 + SignAs(cHHKick, Gear^.dX); + t^.dY:= t^.dY + Gear^.dY * dmg * _0_01; + t^.State:= t^.State or gstMoving; + if t^.Kind = gtKnife then t^.State:= t^.State and (not gstCollision); + t^.Active:= true; + FollowGear:= t + end + end; + gtGrave: begin + dmg:= 0; + r:= Gear^.Radius + t^.Radius; + dx:= Gear^.X-t^.X; + dx.isNegative:= false; + dy:= Gear^.Y-t^.Y; + dy.isNegative:= false; + if r-hwRound(dx+dy) > 0 then + begin + dist:= hwRound(Distance(dx, dy)); + dmg:= ModifyDamage(min(r - dist, 25), t); + end; + if dmg > 0 then + begin + t^.dY:= - _0_1; + t^.Active:= true + end + end; + end; + t:= t^.NextGear + end; +if (GameFlags and gfSolidLand) = 0 then + DrawExplosion(hwRound(Gear^.X), hwRound(Gear^.Y), cShotgunRadius) +end; + +procedure AmmoShove(Ammo: PGear; Damage, Power: LongInt); +var t: PGearArray; + Gear: PGear; + i, j, tmpDmg: LongInt; + VGear: PVisualGear; +begin +t:= CheckGearsCollision(Ammo); +// Just to avoid hogs on rope dodging fire. +if (CurAmmoGear <> nil) and ((CurAmmoGear^.Kind = gtRope) or (CurAmmoGear^.Kind = gtJetpack) or (CurAmmoGear^.Kind = gtBirdy)) +and (CurrentHedgehog^.Gear <> nil) and (CurrentHedgehog^.Gear^.CollisionIndex = -1) +and (sqr(hwRound(Ammo^.X) - hwRound(CurrentHedgehog^.Gear^.X)) + sqr(hwRound(Ammo^.Y) - hwRound(CurrentHedgehog^.Gear^.Y)) <= sqr(cHHRadius + Ammo^.Radius)) then + begin + t^.ar[t^.Count]:= CurrentHedgehog^.Gear; + inc(t^.Count) + end; + +i:= t^.Count; + +if (Ammo^.Kind = gtFlame) and (i > 0) then + Ammo^.Health:= 0; +while i > 0 do + begin + dec(i); + Gear:= t^.ar[i]; + if ((Ammo^.Kind = gtFlame) or (Ammo^.Kind = gtBlowTorch)) and + (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.Effects[heFrozen] > 255) then + Gear^.Hedgehog^.Effects[heFrozen]:= max(255,Gear^.Hedgehog^.Effects[heFrozen]-10000); + tmpDmg:= ModifyDamage(Damage, Gear); + if (Gear^.State and gstNoDamage) = 0 then + begin + + if (Ammo^.Kind = gtDEagleShot) or (Ammo^.Kind = gtSniperRifleShot) then + begin + VGear := AddVisualGear(hwround(Ammo^.X), hwround(Ammo^.Y), vgtBulletHit); + if VGear <> nil then + VGear^.Angle := DxDy2Angle(-Ammo^.dX, Ammo^.dY); + end; + + if (Gear^.Kind = gtHedgehog) and (Ammo^.State and gsttmpFlag <> 0) and (Ammo^.Kind = gtShover) then + Gear^.FlightTime:= 1; + + + case Gear^.Kind of + gtHedgehog, + gtMine, + gtSMine, + gtKnife, + gtTarget, + gtCase, + gtExplosives: //, + //gtStructure: + begin + if (Ammo^.Kind = gtDrill) then + begin + Ammo^.Timer:= 0; + exit; + end; + if (not Gear^.Invulnerable) then + begin + if (Ammo^.Kind = gtKnife) and (tmpDmg > 0) then + for j:= 1 to max(1,min(3,tmpDmg div 5)) do + begin + VGear:= AddVisualGear(hwRound(Ammo^.X-((Ammo^.X-Gear^.X)/_2)), hwRound(Ammo^.Y-((Ammo^.Y-Gear^.Y)/_2)), vgtStraightShot); + if VGear <> nil then + with VGear^ do + begin + Tint:= $FFCC00FF; + Angle:= random(360); + dx:= 0.0005 * (random(100)); + dy:= 0.0005 * (random(100)); + if random(2) = 0 then + dx := -dx; + if random(2) = 0 then + dy := -dy; + FrameTicks:= 600+random(200); + State:= ord(sprStar) + end + end; + ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg, dsShove) + end + else + Gear^.State:= Gear^.State or gstWinner; + if (Gear^.Kind = gtExplosives) and (Ammo^.Kind = gtBlowtorch) then + begin + if (Ammo^.Hedgehog^.Gear <> nil) then + Ammo^.Hedgehog^.Gear^.State:= Ammo^.Hedgehog^.Gear^.State and (not gstNotKickable); + ApplyDamage(Gear, Ammo^.Hedgehog, tmpDmg * 100, dsUnknown); // crank up damage for explosives + blowtorch + end; + + if (Gear^.Kind = gtHedgehog) and (Gear^.Hedgehog^.King or (Gear^.Hedgehog^.Effects[heFrozen] > 0)) then + begin + Gear^.dX:= Ammo^.dX * Power * _0_005; + Gear^.dY:= Ammo^.dY * Power * _0_005 + end + else if ((Ammo^.Kind <> gtFlame) or (Gear^.Kind = gtHedgehog)) and (Power <> 0) then + begin + Gear^.dX:= Ammo^.dX * Power * _0_01; + Gear^.dY:= Ammo^.dY * Power * _0_01 + end; + + if (not isZero(Gear^.dX)) or (not isZero(Gear^.dY)) then + begin + Gear^.Active:= true; + DeleteCI(Gear); + Gear^.State:= Gear^.State or gstMoving; + if Gear^.Kind = gtKnife then Gear^.State:= Gear^.State and (not gstCollision); + // move the gear upwards a bit to throw it over tiny obstacles at start + if TestCollisionXwithGear(Gear, hwSign(Gear^.dX)) then + begin + if not (TestCollisionXwithXYShift(Gear, _0, -3, hwSign(Gear^.dX)) + or (TestCollisionYwithGear(Gear, -1) <> 0)) then + Gear^.Y:= Gear^.Y - _1; + if not (TestCollisionXwithXYShift(Gear, _0, -2, hwSign(Gear^.dX)) + or (TestCollisionYwithGear(Gear, -1) <> 0)) then + Gear^.Y:= Gear^.Y - _1; + if not (TestCollisionXwithXYShift(Gear, _0, -1, hwSign(Gear^.dX)) + or (TestCollisionYwithGear(Gear, -1) <> 0)) then + Gear^.Y:= Gear^.Y - _1; + end + end; + + + if (Ammo^.Kind <> gtFlame) or ((Ammo^.State and gsttmpFlag) = 0) then + FollowGear:= Gear + end; + end + end; + end; +if i <> 0 then + SetAllToActive +end; + + +function CountGears(Kind: TGearType): Longword; +var t: PGear; + count: Longword = 0; +begin + +t:= GearsList; +while t <> nil do + begin + if t^.Kind = Kind then + inc(count); + t:= t^.NextGear + end; +CountGears:= count; +end; + +procedure SetAllToActive; +var t: PGear; +begin +AllInactive:= false; +t:= GearsList; +while t <> nil do + begin + t^.Active:= true; + t:= t^.NextGear + end +end; + +procedure SetAllHHToActive; inline; +begin +SetAllHHToActive(true) +end; + + +procedure SetAllHHToActive(Ice: boolean); +var t: PGear; +begin +AllInactive:= false; +t:= GearsList; +while t <> nil do + begin + if (t^.Kind = gtHedgehog) or (t^.Kind = gtExplosives) then + begin + if (t^.Kind = gtHedgehog) and Ice then CheckIce(t); + t^.Active:= true + end; + t:= t^.NextGear + end +end; + + +var GearsNearArray : TPGearArray; +function GearsNear(X, Y: hwFloat; Kind: TGearType; r: LongInt): PGearArrayS; +var + t: PGear; + s: Longword; +begin + r:= r*r; + s:= 0; + SetLength(GearsNearArray, s); + t := GearsList; + while t <> nil do + begin + if (t^.Kind = Kind) + and ((X - t^.X)*(X - t^.X) + (Y - t^.Y)*(Y-t^.Y) < int2hwFloat(r)) then + begin + inc(s); + SetLength(GearsNearArray, s); + GearsNearArray[s - 1] := t; + end; + t := t^.NextGear; + end; + + GearsNear.size:= s; + GearsNear.ar:= @GearsNearArray +end; + + +procedure SpawnBoxOfSmth; +var t, aTot, uTot, a, h: LongInt; + i: TAmmoType; +begin +if (PlacingHogs) or + (cCaseFactor = 0) + or (CountGears(gtCase) >= 5) + or (GetRandom(cCaseFactor) <> 0) then + exit; + +FollowGear:= nil; +aTot:= 0; +uTot:= 0; +for i:= Low(TAmmoType) to High(TAmmoType) do + if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then + inc(aTot, Ammoz[i].Probability) + else + inc(uTot, Ammoz[i].Probability); + +t:=0; +a:=aTot; +h:= 1; + +if (aTot+uTot) <> 0 then + if ((GameFlags and gfInvulnerable) = 0) then + begin + h:= cHealthCaseProb * 100; + t:= GetRandom(10000); + a:= (10000-h)*aTot div (aTot+uTot) + end + else + begin + t:= GetRandom(aTot+uTot); + h:= 0 + end; + + +if t 0) then + begin + FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0); + t:= GetRandom(t); + i:= Low(TAmmoType); + FollowGear^.Pos:= posCaseAmmo; + FollowGear^.AmmoType:= i; + AddCaption(GetEventString(eidNewAmmoPack), cWhiteColor, capgrpAmmoInfo); + end + end +else + begin + t:= uTot; + if (t > 0) then + begin + FollowGear:= AddGear(0, 0, gtCase, 0, _0, _0, 0); + t:= GetRandom(t); + i:= Low(TAmmoType); + FollowGear^.Pos:= posCaseUtility; + FollowGear^.AmmoType:= i; + AddCaption(GetEventString(eidNewUtilityPack), cWhiteColor, capgrpAmmoInfo); + end + end; + +// handles case of no ammo or utility crates - considered also placing booleans in uAmmos and altering probabilities +if (FollowGear <> nil) then + begin + FindPlace(FollowGear, true, 0, LAND_WIDTH); + + if (FollowGear <> nil) then + AddVoice(sndReinforce, CurrentTeam^.voicepack) + end +end; + + +function GetAmmo(Hedgehog: PHedgehog): TAmmoType; +var t, aTot: LongInt; + i: TAmmoType; +begin +Hedgehog:= Hedgehog; // avoid hint + +aTot:= 0; +for i:= Low(TAmmoType) to High(TAmmoType) do + if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then + inc(aTot, Ammoz[i].Probability); + +t:= aTot; +i:= Low(TAmmoType); +if (t > 0) then + begin + t:= GetRandom(t); + while t >= 0 do + begin + inc(i); + if (Ammoz[i].Ammo.Propz and ammoprop_Utility) = 0 then + dec(t, Ammoz[i].Probability) + end + end; +GetAmmo:= i +end; + +function GetUtility(Hedgehog: PHedgehog): TAmmoType; +var t, uTot: LongInt; + i: TAmmoType; +begin + +uTot:= 0; +for i:= Low(TAmmoType) to High(TAmmoType) do + if ((Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0) + and ((Hedgehog^.Team^.HedgehogsNumber > 1) or (Ammoz[i].Ammo.AmmoType <> amSwitch)) then + inc(uTot, Ammoz[i].Probability); + +t:= uTot; +i:= Low(TAmmoType); +if (t > 0) then + begin + t:= GetRandom(t); + while t >= 0 do + begin + inc(i); + if ((Ammoz[i].Ammo.Propz and ammoprop_Utility) <> 0) and ((Hedgehog^.Team^.HedgehogsNumber > 1) + or (Ammoz[i].Ammo.AmmoType <> amSwitch)) then + dec(t, Ammoz[i].Probability) + end + end; +GetUtility:= i +end; + + end. diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/uScript.pas --- a/hedgewars/uScript.pas Wed Jun 26 21:40:10 2013 -0400 +++ b/hedgewars/uScript.pas Thu Jun 27 15:51:20 2013 +0400 @@ -58,7 +58,6 @@ uses LuaPas, uConsole, uConsts, - uVisualGears, uGears, uGearsList, uGearsUtils, @@ -84,6 +83,8 @@ SDLh, SysUtils, uIO, + uVisualGearsList, + uGearsHandlersMess, uPhysFSLayer, typinfo ; diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/uTeams.pas --- a/hedgewars/uTeams.pas Wed Jun 26 21:40:10 2013 -0400 +++ b/hedgewars/uTeams.pas Thu Jun 27 15:51:20 2013 +0400 @@ -1,4 +1,4 @@ -(* + (* * Hedgewars, a free turn based strategy game * Copyright (c) 2004-2013 Andrey Korotaev * @@ -20,7 +20,7 @@ unit uTeams; interface -uses uConsts, uInputHandler, uRandom, uFloat, uStats, uVisualGears, +uses uConsts, uInputHandler, uRandom, uFloat, uStats, uCollisions, GLunit, uSound, uStore, uTypes, uScript {$IFDEF USE_TOUCH_INTERFACE}, uWorld{$ENDIF}; @@ -43,7 +43,7 @@ implementation uses uLocale, uAmmos, uChat, uVariables, uUtils, uIO, uCaptions, uCommands, uDebug, - uGearsUtils, uGearsList + uGearsUtils, uGearsList, uVisualGearsList {$IFDEF USE_TOUCH_INTERFACE}, uTouch{$ENDIF}; var MaxTeamHealth: LongInt; diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/uVisualGears.pas --- a/hedgewars/uVisualGears.pas Wed Jun 26 21:40:10 2013 -0400 +++ b/hedgewars/uVisualGears.pas Thu Jun 27 15:51:20 2013 +0400 @@ -29,19 +29,13 @@ * E.g.: background flakes, visual effects: explosion, smoke trails, etc. *) interface -uses uConsts, uFloat, GLunit, uTypes, uWorld; +uses uConsts, GLunit, uTypes; procedure initModule; procedure freeModule; -function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType): PVisualGear; inline; -function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord): PVisualGear; inline; -function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean): PVisualGear; - procedure ProcessVisualGears(Steps: Longword); procedure DrawVisualGears(Layer: LongWord); -procedure DeleteVisualGear(Gear: PVisualGear); -function VisualGearByUID(uid : Longword) : PVisualGear; procedure AddClouds; procedure AddFlakes; @@ -53,17 +47,8 @@ procedure KickFlakes(Radius, X, Y: LongInt); implementation -uses uSound, uVariables, uTextures, uRender, Math, uRenderUtils, uStore, uUtils; - -const - cExplFrameTicks = 110; - //cSmokeZ = 499; -var VGCounter: LongWord; - VisualGearLayers: array[0..6] of PVisualGear; - -// For better maintainability the step handlers of visual gears are stored -// in a separate file. -{$INCLUDE "VGSHandlers.inc"} +uses uVariables, uRender, Math, uRenderUtils, uStore, uUtils + , uVisualGearsList; procedure AddDamageTag(X, Y, Damage, Color: LongWord); var s: shortstring; @@ -84,436 +69,6 @@ // ================================================================== -// ================================================================== -const doStepHandlers: array[TVisualGearType] of TVGearStepProcedure = - ( - @doStepFlake, - @doStepCloud, - @doStepExpl, - @doStepExpl, - @doStepFire, - @doStepSmallDamage, - @doStepTeamHealthSorter, - @doStepSpeechBubble, - @doStepBubble, - @doStepSteam, - @doStepAmmo, - @doStepSmoke, - @doStepSmoke, - @doStepShell, - @doStepDust, - @doStepSplash, - @doStepDroplet, - @doStepSmokeRing, - @doStepBeeTrace, - @doStepEgg, - @doStepFeather, - @doStepHealthTag, - @doStepSmokeTrace, - @doStepSmokeTrace, - @doStepExplosion, - @doStepBigExplosion, - @doStepChunk, - @doStepNote, - @doStepLineTrail, - @doStepBulletHit, - @doStepCircle, - @doStepSmoothWindBar, - @doStepStraightShot - ); - -function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType): PVisualGear; inline; -begin - AddVisualGear:= AddVisualGear(X, Y, Kind, 0, false); -end; - -function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord): PVisualGear; inline; -begin - AddVisualGear:= AddVisualGear(X, Y, Kind, State, false); -end; - -function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean): PVisualGear; -var gear: PVisualGear; - t: Longword; - sp: real; -begin -AddVisualGear:= nil; -if ((GameType = gmtSave) or (fastUntilLag and (GameType = gmtNet)) or fastScrolling) and // we are scrolling now - ((Kind <> vgtCloud) and (not Critical)) then - exit; - -if ((cReducedQuality and rqAntiBoom) <> 0) and - (not Critical) and - (not (Kind in - [vgtTeamHealthSorter, - vgtSmallDamageTag, - vgtSpeechBubble, - vgtHealthTag, - vgtExplosion, - vgtSmokeTrace, - vgtEvilTrace, - vgtNote, - vgtSmoothWindBar])) then - - exit; - -inc(VGCounter); -New(gear); -FillChar(gear^, sizeof(TVisualGear), 0); -gear^.X:= real(X); -gear^.Y:= real(Y); -gear^.Kind := Kind; -gear^.doStep:= doStepHandlers[Kind]; -gear^.State:= 0; -gear^.Tint:= $FFFFFFFF; -gear^.uid:= VGCounter; -gear^.Layer:= 0; - -with gear^ do - case Kind of - vgtFlake: - begin - Timer:= 0; - tdX:= 0; - tdY:= 0; - Scale:= 1.0; - if SuddenDeathDmg then - begin - FrameTicks:= random(vobSDFrameTicks); - Frame:= random(vobSDFramesCount); - end - else - begin - FrameTicks:= random(vobFrameTicks); - Frame:= random(vobFramesCount); - end; - Angle:= random(360); - dx:= 0.0000038654705 * random(10000); - dy:= 0.000003506096 * random(7000); - if random(2) = 0 then - dx := -dx; - if SuddenDeathDmg then - dAngle:= (random(2) * 2 - 1) * (vobSDVelocity + random(vobSDVelocity)) / 1000 - else - dAngle:= (random(2) * 2 - 1) * (vobVelocity + random(vobVelocity)) / 1000 - end; - vgtCloud: - begin - Frame:= random(4); - dx:= 0.5 + 0.1 * random(5); // how much the cloud will be affected by wind - timer:= random(4096); - Scale:= 1.0 - end; - vgtExplPart, - vgtExplPart2: - begin - t:= random(1024); - sp:= 0.001 * (random(95) + 70); - dx:= hwFloat2Float(AngleSin(t)) * sp; - dy:= hwFloat2Float(AngleCos(t)) * sp; - if random(2) = 0 then - dx := -dx; - if random(2) = 0 then - dy := -dy; - Frame:= 7 - random(3); - FrameTicks:= cExplFrameTicks - end; - vgtFire: - begin - t:= random(1024); - sp:= 0.001 * (random(85) + 95); - dx:= hwFloat2Float(AngleSin(t)) * sp; - dy:= hwFloat2Float(AngleCos(t)) * sp; - if random(2) = 0 then - dx := -dx; - if random(2) = 0 then - dy := -dy; - FrameTicks:= 650 + random(250); - Frame:= random(8) - end; - vgtEgg: - begin - t:= random(1024); - sp:= 0.001 * (random(85) + 95); - dx:= hwFloat2Float(AngleSin(t)) * sp; - dy:= hwFloat2Float(AngleCos(t)) * sp; - if random(2) = 0 then - dx := -dx; - if random(2) = 0 then - dy := -dy; - FrameTicks:= 650 + random(250); - Frame:= 1 - end; - vgtShell: FrameTicks:= 500; - vgtSmallDamageTag: - begin - gear^.FrameTicks:= 1100 - end; - vgtBubble: - begin - dx:= 0.0000038654705 * random(10000); - dy:= 0; - if random(2) = 0 then - dx := -dx; - FrameTicks:= 250 + random(1751); - Frame:= random(5) - end; - vgtSteam: - begin - dx:= 0.0000038654705 * random(10000); - dy:= 0.001 * (random(85) + 95); - if random(2) = 0 then - dx := -dx; - Frame:= 7 - random(3); - FrameTicks:= cExplFrameTicks * 2; - end; - vgtAmmo: - begin - alpha:= 1.0; - scale:= 1.0 - end; - vgtSmokeWhite, - vgtSmoke: - begin - Scale:= 1.0; - dx:= 0.0002 * (random(45) + 10); - dy:= 0.0002 * (random(45) + 10); - if random(2) = 0 then - dx := -dx; - Frame:= 7 - random(2); - FrameTicks:= cExplFrameTicks * 2; - end; - vgtDust: - begin - dx:= 0.005 * (random(15) + 10); - dy:= 0.001 * (random(40) + 20); - if random(2) = 0 then dx := -dx; - if random(2) = 0 then Tag:= 1 - else Tag:= -1; - Frame:= 7 - random(2); - FrameTicks:= random(20) + 15; - end; - vgtSplash: - begin - dx:= 0; - dy:= 0; - FrameTicks:= 740; - Frame:= 19; - Scale:= 0.75; - Timer:= 1; - end; - vgtDroplet: - begin - dx:= 0.001 * (random(180) - 90); - dy:= -0.001 * (random(160) + 40); - FrameTicks:= 250 + random(1751); - Frame:= random(3) - end; - vgtBeeTrace: - begin - FrameTicks:= 1000; - Frame:= random(16); - end; - vgtSmokeRing: - begin - dx:= 0; - dy:= 0; - FrameTicks:= 600; - Timer:= 0; - Frame:= 0; - scale:= 0.6; - alpha:= 1; - angle:= random(360); - end; - vgtFeather: - begin - t:= random(1024); - sp:= 0.001 * (random(85) + 95); - dx:= hwFloat2Float(AngleSin(t)) * sp; - dy:= hwFloat2Float(AngleCos(t)) * sp; - if random(2) = 0 then - dx := -dx; - if random(2) = 0 then - dy := -dy; - FrameTicks:= 650 + random(250); - Frame:= 1 - end; - vgtHealthTag: - begin - Frame:= 0; - Timer:= 1500; - dY:= -0.08; - dX:= 0; - //gear^.Z:= 2002; - end; - vgtSmokeTrace, - vgtEvilTrace: - begin - gear^.X:= gear^.X - 16; - gear^.Y:= gear^.Y - 16; - gear^.State:= 8; - //gear^.Z:= cSmokeZ - end; -vgtBigExplosion: - begin - gear^.Angle:= random(360); - end; - vgtChunk: - begin - gear^.Frame:= random(4); - t:= random(1024); - sp:= 0.001 * (random(85) + 47); - dx:= hwFloat2Float(AngleSin(t)) * sp; - dy:= hwFloat2Float(AngleCos(t)) * sp * -2; - if random(2) = 0 then - dx := -dx; - end; - vgtNote: - begin - dx:= 0.005 * (random(15) + 10); - dy:= -0.001 * (random(40) + 20); - if random(2) = 0 then - dx := -dx; - Frame:= random(4); - FrameTicks:= random(2000) + 1500; - end; - vgtBulletHit: - begin - dx:= 0; - dy:= 0; - FrameTicks:= 350; - Frame:= 7; - Angle:= 0; - end; -vgtSmoothWindBar: - begin - Angle:= hwFloat2Float(cMaxWindSpeed)*2 / 1440; // seems rate below is supposed to change wind bar at 1px per 10ms. Max time, 1440ms. This tries to match the rate of change - Tag:= hwRound(cWindSpeed * 72 / cMaxWindSpeed); - end; - vgtStraightShot: - begin - Angle:= 0; - Scale:= 1.0; - dx:= 0.001 * random(45); - dy:= 0.001 * (random(20) + 25); - State:= ord(sprHealth); - if random(2) = 0 then - dx := -dx; - Frame:= 0; - FrameTicks:= random(750) + 1250; - State:= ord(sprSnowDust); - end; - end; - -if State <> 0 then - gear^.State:= State; - -case Gear^.Kind of - vgtFlake: if cFlattenFlakes then - gear^.Layer:= 0 - else if random(3) = 0 then - begin - gear^.Scale:= 0.5; - gear^.Layer:= 0 // 33% - far back - end - else if random(3) = 0 then - begin - gear^.Scale:= 0.8; - gear^.Layer:= 4 // 22% - mid-distance - end - else if random(3) <> 0 then - gear^.Layer:= 5 // 30% - just behind land - else if random(2) = 0 then - gear^.Layer:= 6 // 7% - just in front of land - else - begin - gear^.Scale:= 1.5; - gear^.Layer:= 2; // 7% - close up - end; - - vgtCloud: if cFlattenClouds then gear^.Layer:= 5 - else if random(3) = 0 then - begin - gear^.Scale:= 0.25; - gear^.Layer:= 0 - end - else if random(2) = 0 then - gear^.Layer:= 5 - else - begin - gear^.Scale:= 0.4; - gear^.Layer:= 4 - end; - - // 0: this layer is very distant in the background when in stereo - vgtTeamHealthSorter, - vgtSmoothWindBar: gear^.Layer:= 0; - - - // 1: this layer is on the land level (which is close but behind the screen plane) when stereo - vgtSmokeTrace, - vgtEvilTrace, - vgtLineTrail, - vgtSmoke, - vgtSmokeWhite, - vgtDust, - vgtFire, - vgtSplash, - vgtDroplet, - vgtBubble: gear^.Layer:= 1; - - // 3: this layer is on the screen plane (depth = 0) when stereo - vgtSpeechBubble, - vgtSmallDamageTag, - vgtHealthTag, - vgtStraightShot, - vgtChunk: gear^.Layer:= 3; - - // 2: this layer is outside the screen when stereo - vgtExplosion, - vgtBigExplosion, - vgtExplPart, - vgtExplPart2, - vgtSteam, - vgtAmmo, - vgtShell, - vgtFeather, - vgtEgg, - vgtBeeTrace, - vgtSmokeRing, - vgtNote, - vgtBulletHit, - vgtCircle: gear^.Layer:= 2 -end; - -if VisualGearLayers[gear^.Layer] <> nil then - begin - VisualGearLayers[gear^.Layer]^.PrevGear:= gear; - gear^.NextGear:= VisualGearLayers[gear^.Layer] - end; -VisualGearLayers[gear^.Layer]:= gear; - -AddVisualGear:= gear; -end; - -procedure DeleteVisualGear(Gear: PVisualGear); -begin - FreeTexture(Gear^.Tex); - Gear^.Tex:= nil; - - if Gear^.NextGear <> nil then - Gear^.NextGear^.PrevGear:= Gear^.PrevGear; - if Gear^.PrevGear <> nil then - Gear^.PrevGear^.NextGear:= Gear^.NextGear - else - VisualGearLayers[Gear^.Layer]:= Gear^.NextGear; - - if lastVisualGearByUID = Gear then - lastVisualGearByUID:= nil; - - Dispose(Gear); -end; - procedure ProcessVisualGears(Steps: Longword); var Gear, t: PVisualGear; i: LongWord; @@ -900,35 +455,6 @@ end; end; -function VisualGearByUID(uid : Longword) : PVisualGear; -var vg: PVisualGear; - i: LongWord; -begin -VisualGearByUID:= nil; -if uid = 0 then - exit; -if (lastVisualGearByUID <> nil) and (lastVisualGearByUID^.uid = uid) then - begin - VisualGearByUID:= lastVisualGearByUID; - exit - end; -// search in an order that is more likely to return layers they actually use. Could perhaps track statistically AddVisualGear in uScript, since that is most likely the ones they want -for i:= 2 to 5 do - begin - vg:= VisualGearLayers[i mod 4]; - while vg <> nil do - begin - if vg^.uid = uid then - begin - lastVisualGearByUID:= vg; - VisualGearByUID:= vg; - exit - end; - vg:= vg^.NextGear - end - end -end; - procedure AddClouds; var i: LongInt; begin diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/uVisualGearsHandlers.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uVisualGearsHandlers.pas Thu Jun 27 15:51:20 2013 +0400 @@ -0,0 +1,897 @@ +(* + * Hedgewars, a free turn based strategy game + * Copyright (c) 2004-2013 Andrey Korotaev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + *) + +(* + * This file contains the step handlers for visual gears. + * + * Since the effects of visual gears do not affect the course of the game, + * no "synchronization" between players is required. + * => The usage of safe functions or data types (e.g. GetRandom() or hwFloat) + * is usually not necessary and therefore undesirable. + *) + +{$INCLUDE "options.inc"} + +unit uVisualGearsHandlers; + +interface +uses uTypes; + +var doStepHandlers: array[TVisualGearType] of TVGearStepProcedure; + +procedure doStepFlake(Gear: PVisualGear; Steps: Longword); +procedure doStepBeeTrace(Gear: PVisualGear; Steps: Longword); +procedure doStepCloud(Gear: PVisualGear; Steps: Longword); +procedure doStepExpl(Gear: PVisualGear; Steps: Longword); +procedure doStepNote(Gear: PVisualGear; Steps: Longword); +procedure doStepLineTrail(Gear: PVisualGear; Steps: Longword); +procedure doStepEgg(Gear: PVisualGear; Steps: Longword); +procedure doStepFire(Gear: PVisualGear; Steps: Longword); +procedure doStepShell(Gear: PVisualGear; Steps: Longword); +procedure doStepSmallDamage(Gear: PVisualGear; Steps: Longword); +procedure doStepBubble(Gear: PVisualGear; Steps: Longword); +procedure doStepSteam(Gear: PVisualGear; Steps: Longword); +procedure doStepAmmo(Gear: PVisualGear; Steps: Longword); +procedure doStepSmoke(Gear: PVisualGear; Steps: Longword); +procedure doStepDust(Gear: PVisualGear; Steps: Longword); +procedure doStepSplash(Gear: PVisualGear; Steps: Longword); +procedure doStepDroplet(Gear: PVisualGear; Steps: Longword); +procedure doStepSmokeRing(Gear: PVisualGear; Steps: Longword); +procedure doStepFeather(Gear: PVisualGear; Steps: Longword); +procedure doStepTeamHealthSorterWork(Gear: PVisualGear; Steps: Longword); +procedure doStepTeamHealthSorter(Gear: PVisualGear; Steps: Longword); +procedure doStepSpeechBubbleWork(Gear: PVisualGear; Steps: Longword); +procedure doStepSpeechBubble(Gear: PVisualGear; Steps: Longword); +procedure doStepHealthTagWork(Gear: PVisualGear; Steps: Longword); +procedure doStepHealthTagWorkUnderWater(Gear: PVisualGear; Steps: Longword); +procedure doStepHealthTag(Gear: PVisualGear; Steps: Longword); +procedure doStepSmokeTrace(Gear: PVisualGear; Steps: Longword); +procedure doStepExplosionWork(Gear: PVisualGear; Steps: Longword); +procedure doStepExplosion(Gear: PVisualGear; Steps: Longword); +procedure doStepBigExplosionWork(Gear: PVisualGear; Steps: Longword); +procedure doStepBigExplosion(Gear: PVisualGear; Steps: Longword); +procedure doStepChunk(Gear: PVisualGear; Steps: Longword); +procedure doStepBulletHit(Gear: PVisualGear; Steps: Longword); +procedure doStepCircle(Gear: PVisualGear; Steps: Longword); +procedure doStepSmoothWindBar(Gear: PVisualGear; Steps: Longword); +procedure doStepStraightShot(Gear: PVisualGear; Steps: Longword); + +procedure initModule; + +implementation +uses uVariables, Math, uConsts, uVisualGearsList, uFloat, uSound, uRenderUtils, uWorld; + +procedure doStepFlake(Gear: PVisualGear; Steps: Longword); +var sign: real; + moved: boolean; +begin +if vobCount = 0 then exit; + +sign:= 1; +with Gear^ do + begin + inc(FrameTicks, Steps); + if not SuddenDeathDmg and (FrameTicks > vobFrameTicks) then + begin + dec(FrameTicks, vobFrameTicks); + inc(Frame); + if Frame = vobFramesCount then + Frame:= 0 + end + else if SuddenDeathDmg and (FrameTicks > vobSDFrameTicks) then + begin + dec(FrameTicks, vobSDFrameTicks); + inc(Frame); + if Frame = vobSDFramesCount then + Frame:= 0 + end; + X:= X + (cWindSpeedf * 400 + dX + tdX) * Steps * Gear^.Scale; + if SuddenDeathDmg then + Y:= Y + (dY + tdY + cGravityf * vobSDFallSpeed) * Steps * Gear^.Scale + else + Y:= Y + (dY + tdY + cGravityf * vobFallSpeed) * Steps * Gear^.Scale; + Angle:= Angle + dAngle * Steps; + if Angle > 360 then + Angle:= Angle - 360 + else + if Angle < - 360 then + Angle:= Angle + 360; + + + if (round(X) >= cLeftScreenBorder) + and (round(X) <= cRightScreenBorder) + and (round(Y) - 75 <= LAND_HEIGHT) + and (Timer > 0) and (Timer-Steps > 0) then + begin + if tdX > 0 then + sign := 1 + else + sign:= -1; + tdX:= tdX - 0.005*Steps*sign; + if ((sign < 0) and (tdX > 0)) or ((sign > 0) and (tdX < 0)) then + tdX:= 0; + if tdX > 0 then + sign := 1 + else + sign:= -1; + tdY:= tdY - 0.005*Steps*sign; + if ((sign < 0) and (tdY > 0)) or ((sign > 0) and (tdY < 0)) then + tdY:= 0; + dec(Timer, Steps) + end + else + begin + moved:= false; + if round(X) < cLeftScreenBorder then + begin + X:= X + cScreenSpace; + moved:= true + end + else + if round(X) > cRightScreenBorder then + begin + X:= X - cScreenSpace; + moved:= true + end; + // if round(Y) < (LAND_HEIGHT - 1024 - 75) then Y:= Y + 25.0; // For if flag is set for flakes rising upwards? + if (Gear^.Layer = 2) and (round(Y) - 225 > LAND_HEIGHT) then + begin + X:= cLeftScreenBorder + random(cScreenSpace); + Y:= Y - (1024 + 250 + random(50)); // TODO - configure in theme (jellies for example could use limited range) + moved:= true + end + else if (Gear^.Layer <> 2) and (round(Y) + 50 > LAND_HEIGHT) then + begin + X:= cLeftScreenBorder + random(cScreenSpace); + Y:= Y - (1024 + random(25)); + moved:= true + end; + if moved then + begin + Angle:= random(360); + dx:= 0.0000038654705 * random(10000); + dy:= 0.000003506096 * random(7000); + if random(2) = 0 then dx := -dx + end; + Timer:= 0; + tdX:= 0; + tdY:= 0 + end; + end; + +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepBeeTrace(Gear: PVisualGear; Steps: Longword); +begin +if Gear^.FrameTicks > Steps then + dec(Gear^.FrameTicks, Steps) +else + DeleteVisualGear(Gear); +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepCloud(Gear: PVisualGear; Steps: Longword); +var s: Longword; + t: real; +begin +Gear^.X:= Gear^.X + (cWindSpeedf * 750 * Gear^.dX * Gear^.Scale) * Steps; + +// up-and-down-bounce magic +s := (GameTicks + Gear^.Timer) mod 4096; +t := 8 * Gear^.Scale * hwFloat2Float(AngleSin(s mod 2048)); +if (s < 2048) then t := -t; + +Gear^.Y := LAND_HEIGHT - 1184 + LongInt(Gear^.Timer mod 8) + t; + +if round(Gear^.X) < cLeftScreenBorder then + Gear^.X:= Gear^.X + cScreenSpace +else + if round(Gear^.X) > cRightScreenBorder then + Gear^.X:= Gear^.X - cScreenSpace +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepExpl(Gear: PVisualGear; Steps: Longword); +var s: LongInt; +begin +s:= min(Steps, cExplFrameTicks); + +Gear^.X:= Gear^.X + Gear^.dX * s; +Gear^.Y:= Gear^.Y + Gear^.dY * s; +//Gear^.dY:= Gear^.dY + cGravityf; + +if Gear^.FrameTicks <= Steps then + if Gear^.Frame = 0 then + DeleteVisualGear(Gear) + else + begin + dec(Gear^.Frame); + Gear^.FrameTicks:= cExplFrameTicks + end + else dec(Gear^.FrameTicks, Steps) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepNote(Gear: PVisualGear; Steps: Longword); +begin +Gear^.X:= Gear^.X + Gear^.dX * Steps; + +Gear^.Y:= Gear^.Y + Gear^.dY * Steps; +Gear^.dY:= Gear^.dY + cGravityf * Steps / 2; + +Gear^.Angle:= Gear^.Angle + (Gear^.Frame + 1) * Steps / 10; +while Gear^.Angle > cMaxAngle do + Gear^.Angle:= Gear^.Angle - cMaxAngle; + +if Gear^.FrameTicks <= Steps then + DeleteVisualGear(Gear) +else + dec(Gear^.FrameTicks, Steps) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepLineTrail(Gear: PVisualGear; Steps: Longword); +begin +Steps := Steps; +if Gear^.Timer <= Steps then + DeleteVisualGear(Gear) +else + dec(Gear^.Timer, Steps) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepEgg(Gear: PVisualGear; Steps: Longword); +begin +Gear^.X:= Gear^.X + Gear^.dX * Steps; + +Gear^.Y:= Gear^.Y + Gear^.dY * Steps; +Gear^.dY:= Gear^.dY + cGravityf * Steps; + +Gear^.Angle:= round(Gear^.Angle + Steps) mod cMaxAngle; + +if Gear^.FrameTicks <= Steps then + begin + DeleteVisualGear(Gear); + exit + end +else + dec(Gear^.FrameTicks, Steps); + +if Gear^.FrameTicks < $FF then + Gear^.Tint:= (Gear^.Tint and $FFFFFF00) or Gear^.FrameTicks +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepFire(Gear: PVisualGear; Steps: Longword); +var vgt: PVisualGear; +begin +Gear^.X:= Gear^.X + Gear^.dX * Steps; + +Gear^.Y:= Gear^.Y + Gear^.dY * Steps;// + cGravityf * (Steps * Steps); +if (Gear^.State and gstTmpFlag) = 0 then + begin + Gear^.dY:= Gear^.dY + cGravityf * Steps; + if ((GameTicks mod 200) < Steps + 1) then + begin + vgt:= AddVisualGear(round(Gear^.X), round(Gear^.Y), vgtFire); + if vgt <> nil then + begin + vgt^.dx:= 0; + vgt^.dy:= 0; + vgt^.State:= gstTmpFlag; + end; + end + end +else + inc(Steps, Steps); + +if Gear^.FrameTicks <= Steps then + DeleteVisualGear(Gear) +else + dec(Gear^.FrameTicks, Steps) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepShell(Gear: PVisualGear; Steps: Longword); +begin +Gear^.X:= Gear^.X + Gear^.dX * Steps; + +Gear^.Y:= Gear^.Y + Gear^.dY * Steps; +Gear^.dY:= Gear^.dY + cGravityf * Steps; + +Gear^.Angle:= round(Gear^.Angle + Steps) mod cMaxAngle; + +if Gear^.FrameTicks <= Steps then + DeleteVisualGear(Gear) +else + dec(Gear^.FrameTicks, Steps) +end; + +procedure doStepSmallDamage(Gear: PVisualGear; Steps: Longword); +begin +Gear^.Y:= Gear^.Y - 0.02 * Steps; + +if Gear^.FrameTicks <= Steps then + DeleteVisualGear(Gear) +else + dec(Gear^.FrameTicks, Steps) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepBubble(Gear: PVisualGear; Steps: Longword); +begin +Gear^.X:= Gear^.X + Gear^.dX * Steps; +Gear^.Y:= Gear^.Y + Gear^.dY * Steps; +Gear^.Y:= Gear^.Y - cDrownSpeedf * Steps; +Gear^.dX := Gear^.dX / (1.001 * Steps); +Gear^.dY := Gear^.dY / (1.001 * Steps); + +if (Gear^.FrameTicks <= Steps) or (round(Gear^.Y) < cWaterLine) then + DeleteVisualGear(Gear) +else + dec(Gear^.FrameTicks, Steps) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSteam(Gear: PVisualGear; Steps: Longword); +begin +Gear^.X:= Gear^.X + (cWindSpeedf * 100 + Gear^.dX) * Steps; +Gear^.Y:= Gear^.Y - cDrownSpeedf * Steps; + +if Gear^.FrameTicks <= Steps then + if Gear^.Frame = 0 then + DeleteVisualGear(Gear) + else + begin + if Random(2) = 0 then + dec(Gear^.Frame); + Gear^.FrameTicks:= cExplFrameTicks + end +else dec(Gear^.FrameTicks, Steps) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepAmmo(Gear: PVisualGear; Steps: Longword); +begin +Gear^.Y:= Gear^.Y - cDrownSpeedf * Steps; + +Gear^.scale:= Gear^.scale + 0.0025 * Steps; +Gear^.alpha:= Gear^.alpha - 0.0015 * Steps; + +if Gear^.alpha < 0 then + DeleteVisualGear(Gear) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSmoke(Gear: PVisualGear; Steps: Longword); +begin +Gear^.X:= Gear^.X + (cWindSpeedf + Gear^.dX) * Steps; +Gear^.Y:= Gear^.Y - (cDrownSpeedf + Gear^.dY) * Steps; + +Gear^.dX := Gear^.dX + (cWindSpeedf * 0.3 * Steps); +//Gear^.dY := Gear^.dY - (cDrownSpeedf * 0.995); + +if Gear^.FrameTicks <= Steps then + if Gear^.Frame = 0 then + DeleteVisualGear(Gear) + else + begin + if Random(2) = 0 then + dec(Gear^.Frame); + Gear^.FrameTicks:= cExplFrameTicks + end + else dec(Gear^.FrameTicks, Steps) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepDust(Gear: PVisualGear; Steps: Longword); +begin +Gear^.X:= Gear^.X + (cWindSpeedf + (cWindSpeedf * 0.03 * Steps) + Gear^.dX) * Steps; +Gear^.Y:= Gear^.Y - (Gear^.dY) * Steps; + +Gear^.dX := Gear^.dX - (Gear^.dX * 0.005 * Steps); +Gear^.dY := Gear^.dY - (cDrownSpeedf * 0.001 * Steps); + +if Gear^.FrameTicks <= Steps then + if Gear^.Frame = 0 then + DeleteVisualGear(Gear) + else + begin + dec(Gear^.Frame); + Gear^.FrameTicks:= cExplFrameTicks + end + else dec(Gear^.FrameTicks, Steps) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSplash(Gear: PVisualGear; Steps: Longword); +begin +if Gear^.FrameTicks <= Steps then + DeleteVisualGear(Gear) +else + dec(Gear^.FrameTicks, Steps); +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepDroplet(Gear: PVisualGear; Steps: Longword); +begin +Gear^.X:= Gear^.X + Gear^.dX * Steps; + +Gear^.Y:= Gear^.Y + Gear^.dY * Steps; +Gear^.dY:= Gear^.dY + cGravityf * Steps; + +if round(Gear^.Y) > cWaterLine then + begin + DeleteVisualGear(Gear); + PlaySound(TSound(ord(sndDroplet1) + Random(3))); + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSmokeRing(Gear: PVisualGear; Steps: Longword); +begin +inc(Gear^.Timer, Steps); +if Gear^.Timer >= Gear^.FrameTicks then + DeleteVisualGear(Gear) +else + begin + Gear^.scale := 1.25 * (-power(2, -10 * Int(Gear^.Timer)/Gear^.FrameTicks) + 1) + 0.4; + Gear^.alpha := 1 - power(Gear^.Timer / 350, 4); + if Gear^.alpha < 0 then + Gear^.alpha:= 0; + end; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepFeather(Gear: PVisualGear; Steps: Longword); +begin +Gear^.X:= Gear^.X + Gear^.dX * Steps; + +Gear^.Y:= Gear^.Y + Gear^.dY * Steps; +Gear^.dY:= Gear^.dY + cGravityf * Steps; + +Gear^.Angle:= round(Gear^.Angle + Steps) mod cMaxAngle; + +if Gear^.FrameTicks <= Steps then + DeleteVisualGear(Gear) +else + dec(Gear^.FrameTicks, Steps) +end; + +//////////////////////////////////////////////////////////////////////////////// +const cSorterWorkTime = 640; +var thexchar: array[0..cMaxTeams] of + record + dy, ny, dw: LongInt; + team: PTeam; + SortFactor: QWord; + end; + currsorter: PVisualGear = nil; + +procedure doStepTeamHealthSorterWork(Gear: PVisualGear; Steps: Longword); +var i, t: LongInt; +begin +for t:= 1 to min(Steps, Gear^.Timer) do + begin + dec(Gear^.Timer); + if (Gear^.Timer and 15) = 0 then + for i:= 0 to Pred(TeamsCount) do + with thexchar[i] do + begin + {$WARNINGS OFF} + team^.DrawHealthY:= ny + dy * LongInt(Gear^.Timer) div cSorterWorkTime; + team^.TeamHealthBarWidth:= team^.NewTeamHealthBarWidth + dw * LongInt(Gear^.Timer) div cSorterWorkTime; + {$WARNINGS ON} + end; + end; + +if (Gear^.Timer = 0) or (currsorter <> Gear) then + begin + if currsorter = Gear then + currsorter:= nil; + DeleteVisualGear(Gear); + exit + end +end; + +procedure doStepTeamHealthSorter(Gear: PVisualGear; Steps: Longword); +var i: Longword; + b: boolean; + t: LongInt; +begin +Steps:= Steps; // avoid compiler hint + +for t:= 0 to Pred(TeamsCount) do + with thexchar[t] do + begin + team:= TeamsArray[t]; + dy:= team^.DrawHealthY; + dw:= team^.TeamHealthBarWidth - team^.NewTeamHealthBarWidth; + if team^.TeamHealth > 0 then + begin + SortFactor:= team^.Clan^.ClanHealth; + SortFactor:= (SortFactor shl 3) + team^.Clan^.ClanIndex; + SortFactor:= (SortFactor shl 30) + team^.TeamHealth; + end + else + SortFactor:= 0; + end; + +if TeamsCount > 1 then + repeat + b:= true; + for t:= 0 to TeamsCount - 2 do + if (thexchar[t].SortFactor > thexchar[Succ(t)].SortFactor) then + begin + thexchar[cMaxTeams]:= thexchar[t]; + thexchar[t]:= thexchar[Succ(t)]; + thexchar[Succ(t)]:= thexchar[cMaxTeams]; + b:= false + end + until b; + +t:= - 4; +for i:= 0 to Pred(TeamsCount) do + with thexchar[i] do + if team^.TeamHealth > 0 then + begin + dec(t, team^.HealthTex^.h + 2); + ny:= t; + dy:= dy - ny + end; + +Gear^.Timer:= cSorterWorkTime; +Gear^.doStep:= @doStepTeamHealthSorterWork; +currsorter:= Gear; +//doStepTeamHealthSorterWork(Gear, Steps) +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSpeechBubbleWork(Gear: PVisualGear; Steps: Longword); +begin +if Gear^.Timer > Steps then dec(Gear^.Timer, Steps) else Gear^.Timer:= 0; + +if (Gear^.Hedgehog^.Gear <> nil) then + begin + Gear^.X:= hwFloat2Float(Gear^.Hedgehog^.Gear^.X) + (Gear^.Tex^.w div 2 - Gear^.FrameTicks); + Gear^.Y:= hwFloat2Float(Gear^.Hedgehog^.Gear^.Y) - (16 + Gear^.Tex^.h); + end; + +if Gear^.Timer = 0 then + begin + if Gear^.Hedgehog^.SpeechGear = Gear then + Gear^.Hedgehog^.SpeechGear:= nil; + DeleteVisualGear(Gear) + end; +end; + +procedure doStepSpeechBubble(Gear: PVisualGear; Steps: Longword); +begin +Steps:= Steps; // avoid compiler hint + +with Gear^.Hedgehog^ do + if SpeechGear <> nil then + SpeechGear^.Timer:= 0; + +Gear^.Hedgehog^.SpeechGear:= Gear; + +Gear^.Timer:= max(LongInt(Length(Gear^.Text)) * 150, 3000); + +Gear^.Tex:= RenderSpeechBubbleTex(Gear^.Text, Gear^.FrameTicks, fnt16); + +case Gear^.FrameTicks of + 1: Gear^.FrameTicks:= SpritesData[sprSpeechTail].Width-28; + 2: Gear^.FrameTicks:= SpritesData[sprThoughtTail].Width-20; + 3: Gear^.FrameTicks:= SpritesData[sprShoutTail].Width-10; + end; + +Gear^.doStep:= @doStepSpeechBubbleWork; + +Gear^.Y:= Gear^.Y - Gear^.Tex^.h +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepHealthTagWork(Gear: PVisualGear; Steps: Longword); +begin +if Steps > Gear^.Timer then + DeleteVisualGear(Gear) +else + begin + dec(Gear^.Timer, Steps); + Gear^.Y:= Gear^.Y + Gear^.dY * Steps; + Gear^.X:= Gear^.X + Gear^.dX * Steps + end; +end; + +procedure doStepHealthTagWorkUnderWater(Gear: PVisualGear; Steps: Longword); +begin +if round(Gear^.Y) - 10 < cWaterLine then + DeleteVisualGear(Gear) +else + Gear^.Y:= Gear^.Y - 0.08 * Steps; + +end; + +procedure doStepHealthTag(Gear: PVisualGear; Steps: Longword); +var s: shortstring; +begin +s:= ''; + +str(Gear^.State, s); +if Gear^.Hedgehog <> nil then + Gear^.Tex:= RenderStringTex(s, Gear^.Hedgehog^.Team^.Clan^.Color, fnt16) +else + Gear^.Tex:= RenderStringTex(s, cWhiteColor, fnt16); + +Gear^.doStep:= @doStepHealthTagWork; + +if (round(Gear^.Y) > cWaterLine) and (Gear^.Frame = 0) then + Gear^.doStep:= @doStepHealthTagWorkUnderWater; + +Gear^.Y:= Gear^.Y - Gear^.Tex^.h; + +if Steps > 1 then + Gear^.doStep(Gear, Steps-1); +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSmokeTrace(Gear: PVisualGear; Steps: Longword); +begin +inc(Gear^.Timer, Steps ); +if Gear^.Timer > 64 then + begin + if Gear^.State = 0 then + begin + DeleteVisualGear(Gear); + exit; + end; + dec(Gear^.State, Gear^.Timer div 65); + Gear^.Timer:= Gear^.Timer mod 65; + end; +Gear^.dX:= Gear^.dX + cWindSpeedf * Steps; +Gear^.X:= Gear^.X + Gear^.dX; +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepExplosionWork(Gear: PVisualGear; Steps: Longword); +begin +inc(Gear^.Timer, Steps); +if Gear^.Timer > 75 then + begin + inc(Gear^.State, Gear^.Timer div 76); + Gear^.Timer:= Gear^.Timer mod 76; + if Gear^.State > 5 then + DeleteVisualGear(Gear); + end; +end; + +procedure doStepExplosion(Gear: PVisualGear; Steps: Longword); +var i: LongWord; + gX,gY: LongInt; + vg: PVisualGear; +begin +gX:= round(Gear^.X); +gY:= round(Gear^.Y); +for i:= 0 to 31 do + begin + vg:= AddVisualGear(gX, gY, vgtFire); + if vg <> nil then + begin + vg^.State:= gstTmpFlag; + inc(vg^.FrameTicks, vg^.FrameTicks) + end + end; +for i:= 0 to 8 do AddVisualGear(gX, gY, vgtExplPart); +for i:= 0 to 8 do AddVisualGear(gX, gY, vgtExplPart2); +Gear^.doStep:= @doStepExplosionWork; +if Steps > 1 then + Gear^.doStep(Gear, Steps-1); +end; + + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepBigExplosionWork(Gear: PVisualGear; Steps: Longword); +var maxMovement: LongInt; +begin + +inc(Gear^.Timer, Steps); +if (Gear^.Timer and 5) = 0 then + begin + maxMovement := max(1, 13 - ((Gear^.Timer * 15) div 250)); + ShakeCamera(maxMovement); + end; + +if Gear^.Timer > 250 then + DeleteVisualGear(Gear); +end; + +procedure doStepBigExplosion(Gear: PVisualGear; Steps: Longword); +var i: LongWord; + gX,gY: LongInt; + vg: PVisualGear; +begin +//ScreenFade:= sfFromWhite; +//ScreenFadeValue:= round(60 * zoom * zoom); +//ScreenFadeSpeed:= 5; +gX:= round(Gear^.X); +gY:= round(Gear^.Y); +AddVisualGear(gX, gY, vgtSmokeRing); +for i:= 0 to 46 do + begin + vg:= AddVisualGear(gX, gY, vgtFire); + if vg <> nil then + begin + vg^.State:= gstTmpFlag; + inc(vg^.FrameTicks, vg^.FrameTicks) + end + end; +for i:= 0 to 15 do + AddVisualGear(gX, gY, vgtExplPart); +for i:= 0 to 15 do + AddVisualGear(gX, gY, vgtExplPart2); +Gear^.doStep:= @doStepBigExplosionWork; +if Steps > 1 then + Gear^.doStep(Gear, Steps-1); +with mobileRecord do + if (performRumble <> nil) and (not fastUntilLag) then + performRumble(kSystemSoundID_Vibrate); +end; + +procedure doStepChunk(Gear: PVisualGear; Steps: Longword); +begin +Gear^.X:= Gear^.X + Gear^.dX * Steps; + +Gear^.Y:= Gear^.Y + Gear^.dY * Steps; +Gear^.dY:= Gear^.dY + cGravityf * Steps; + +Gear^.Angle:= round(Gear^.Angle + Steps) mod cMaxAngle; + +if (round(Gear^.Y) > cWaterLine) and ((cReducedQuality and rqPlainSplash) = 0) then + begin + AddVisualGear(round(Gear^.X), round(Gear^.Y), vgtDroplet); + DeleteVisualGear(Gear); + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepBulletHit(Gear: PVisualGear; Steps: Longword); +begin +if Gear^.FrameTicks <= Steps then + DeleteVisualGear(Gear) +else + dec(Gear^.FrameTicks, Steps); +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepCircle(Gear: PVisualGear; Steps: Longword); +var tmp: LongInt; + i: LongWord; +begin +with Gear^ do + if Frame <> 0 then + for i:= 1 to Steps do + begin + inc(FrameTicks); + if (FrameTicks mod Frame) = 0 then + begin + tmp:= Gear^.Tint and $FF; + if tdY >= 0 then + inc(tmp) + else + dec(tmp); + if tmp < round(dX) then + tdY:= 1; + if tmp > round(dY) then + tdY:= -1; + if tmp > 255 then + tmp := 255; + if tmp < 0 then + tmp := 0; + Gear^.Tint:= (Gear^.Tint and $FFFFFF00) or Longword(tmp) + end + end +end; + +//////////////////////////////////////////////////////////////////////////////// +procedure doStepSmoothWindBar(Gear: PVisualGear; Steps: Longword); +begin +inc(Gear^.Timer, Steps); + +while Gear^.Timer >= 10 do + begin + dec(Gear^.Timer, 10); + if WindBarWidth < Gear^.Tag then + inc(WindBarWidth) + else if WindBarWidth > Gear^.Tag then + dec(WindBarWidth); + end; +if cWindspeedf > Gear^.dAngle then + begin + cWindspeedf := cWindspeedf - Gear^.Angle*Steps; + if cWindspeedf < Gear^.dAngle then cWindspeedf:= Gear^.dAngle; + end +else if cWindspeedf < Gear^.dAngle then + begin + cWindspeedf := cWindspeedf + Gear^.Angle*Steps; + if cWindspeedf > Gear^.dAngle then cWindspeedf:= Gear^.dAngle; + end; + +if (WindBarWidth = Gear^.Tag) and (cWindspeedf = Gear^.dAngle) then + DeleteVisualGear(Gear) +end; +//////////////////////////////////////////////////////////////////////////////// +procedure doStepStraightShot(Gear: PVisualGear; Steps: Longword); +begin +Gear^.X:= Gear^.X + Gear^.dX * Steps; +Gear^.Y:= Gear^.Y - Gear^.dY * Steps; + +if Gear^.FrameTicks <= Steps then + DeleteVisualGear(Gear) +else + begin + dec(Gear^.FrameTicks, Steps); + if (Gear^.FrameTicks < 501) and (Gear^.FrameTicks mod 5 = 0) then + Gear^.Tint:= (Gear^.Tint and $FFFFFF00) or (((Gear^.Tint and $000000FF) * Gear^.FrameTicks) div 500) + end +end; + + +const handlers: array[TVisualGearType] of TVGearStepProcedure = + ( + @doStepFlake, + @doStepCloud, + @doStepExpl, + @doStepExpl, + @doStepFire, + @doStepSmallDamage, + @doStepTeamHealthSorter, + @doStepSpeechBubble, + @doStepBubble, + @doStepSteam, + @doStepAmmo, + @doStepSmoke, + @doStepSmoke, + @doStepShell, + @doStepDust, + @doStepSplash, + @doStepDroplet, + @doStepSmokeRing, + @doStepBeeTrace, + @doStepEgg, + @doStepFeather, + @doStepHealthTag, + @doStepSmokeTrace, + @doStepSmokeTrace, + @doStepExplosion, + @doStepBigExplosion, + @doStepChunk, + @doStepNote, + @doStepLineTrail, + @doStepBulletHit, + @doStepCircle, + @doStepSmoothWindBar, + @doStepStraightShot + ); + +procedure initModule; +begin + doStepHandlers:= handlers +end; + +end. diff -r 6e4feb4191a0 -r 6bc1df062f04 hedgewars/uVisualGearsList.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uVisualGearsList.pas Thu Jun 27 15:51:20 2013 +0400 @@ -0,0 +1,462 @@ +(* + * Hedgewars, a free turn based strategy game + * Copyright (c) 2004-2013 Andrey Korotaev + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + *) + +{$INCLUDE "options.inc"} + +unit uVisualGearsList; +interface +uses uTypes; + +function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType): PVisualGear; inline; +function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord): PVisualGear; inline; +function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean): PVisualGear; +procedure DeleteVisualGear(Gear: PVisualGear); +function VisualGearByUID(uid : Longword) : PVisualGear; + +const + cExplFrameTicks = 110; + +var VGCounter: LongWord; + VisualGearLayers: array[0..6] of PVisualGear; + +implementation +uses uFloat, uVariables, uConsts, uTextures, uVisualGearsHandlers; + +function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType): PVisualGear; inline; +begin + AddVisualGear:= AddVisualGear(X, Y, Kind, 0, false); +end; + +function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord): PVisualGear; inline; +begin + AddVisualGear:= AddVisualGear(X, Y, Kind, State, false); +end; + +function AddVisualGear(X, Y: LongInt; Kind: TVisualGearType; State: LongWord; Critical: Boolean): PVisualGear; +var gear: PVisualGear; + t: Longword; + sp: real; +begin +AddVisualGear:= nil; +if ((GameType = gmtSave) or (fastUntilLag and (GameType = gmtNet)) or fastScrolling) and // we are scrolling now + ((Kind <> vgtCloud) and (not Critical)) then + exit; + +if ((cReducedQuality and rqAntiBoom) <> 0) and + (not Critical) and + (not (Kind in + [vgtTeamHealthSorter, + vgtSmallDamageTag, + vgtSpeechBubble, + vgtHealthTag, + vgtExplosion, + vgtSmokeTrace, + vgtEvilTrace, + vgtNote, + vgtSmoothWindBar])) then + + exit; + +inc(VGCounter); +New(gear); +FillChar(gear^, sizeof(TVisualGear), 0); +gear^.X:= real(X); +gear^.Y:= real(Y); +gear^.Kind := Kind; +gear^.doStep:= doStepHandlers[Kind]; +gear^.State:= 0; +gear^.Tint:= $FFFFFFFF; +gear^.uid:= VGCounter; +gear^.Layer:= 0; + +with gear^ do + case Kind of + vgtFlake: + begin + Timer:= 0; + tdX:= 0; + tdY:= 0; + Scale:= 1.0; + if SuddenDeathDmg then + begin + FrameTicks:= random(vobSDFrameTicks); + Frame:= random(vobSDFramesCount); + end + else + begin + FrameTicks:= random(vobFrameTicks); + Frame:= random(vobFramesCount); + end; + Angle:= random(360); + dx:= 0.0000038654705 * random(10000); + dy:= 0.000003506096 * random(7000); + if random(2) = 0 then + dx := -dx; + if SuddenDeathDmg then + dAngle:= (random(2) * 2 - 1) * (vobSDVelocity + random(vobSDVelocity)) / 1000 + else + dAngle:= (random(2) * 2 - 1) * (vobVelocity + random(vobVelocity)) / 1000 + end; + vgtCloud: + begin + Frame:= random(4); + dx:= 0.5 + 0.1 * random(5); // how much the cloud will be affected by wind + timer:= random(4096); + Scale:= 1.0 + end; + vgtExplPart, + vgtExplPart2: + begin + t:= random(1024); + sp:= 0.001 * (random(95) + 70); + dx:= hwFloat2Float(AngleSin(t)) * sp; + dy:= hwFloat2Float(AngleCos(t)) * sp; + if random(2) = 0 then + dx := -dx; + if random(2) = 0 then + dy := -dy; + Frame:= 7 - random(3); + FrameTicks:= cExplFrameTicks + end; + vgtFire: + begin + t:= random(1024); + sp:= 0.001 * (random(85) + 95); + dx:= hwFloat2Float(AngleSin(t)) * sp; + dy:= hwFloat2Float(AngleCos(t)) * sp; + if random(2) = 0 then + dx := -dx; + if random(2) = 0 then + dy := -dy; + FrameTicks:= 650 + random(250); + Frame:= random(8) + end; + vgtEgg: + begin + t:= random(1024); + sp:= 0.001 * (random(85) + 95); + dx:= hwFloat2Float(AngleSin(t)) * sp; + dy:= hwFloat2Float(AngleCos(t)) * sp; + if random(2) = 0 then + dx := -dx; + if random(2) = 0 then + dy := -dy; + FrameTicks:= 650 + random(250); + Frame:= 1 + end; + vgtShell: FrameTicks:= 500; + vgtSmallDamageTag: + begin + gear^.FrameTicks:= 1100 + end; + vgtBubble: + begin + dx:= 0.0000038654705 * random(10000); + dy:= 0; + if random(2) = 0 then + dx := -dx; + FrameTicks:= 250 + random(1751); + Frame:= random(5) + end; + vgtSteam: + begin + dx:= 0.0000038654705 * random(10000); + dy:= 0.001 * (random(85) + 95); + if random(2) = 0 then + dx := -dx; + Frame:= 7 - random(3); + FrameTicks:= cExplFrameTicks * 2; + end; + vgtAmmo: + begin + alpha:= 1.0; + scale:= 1.0 + end; + vgtSmokeWhite, + vgtSmoke: + begin + Scale:= 1.0; + dx:= 0.0002 * (random(45) + 10); + dy:= 0.0002 * (random(45) + 10); + if random(2) = 0 then + dx := -dx; + Frame:= 7 - random(2); + FrameTicks:= cExplFrameTicks * 2; + end; + vgtDust: + begin + dx:= 0.005 * (random(15) + 10); + dy:= 0.001 * (random(40) + 20); + if random(2) = 0 then dx := -dx; + if random(2) = 0 then Tag:= 1 + else Tag:= -1; + Frame:= 7 - random(2); + FrameTicks:= random(20) + 15; + end; + vgtSplash: + begin + dx:= 0; + dy:= 0; + FrameTicks:= 740; + Frame:= 19; + Scale:= 0.75; + Timer:= 1; + end; + vgtDroplet: + begin + dx:= 0.001 * (random(180) - 90); + dy:= -0.001 * (random(160) + 40); + FrameTicks:= 250 + random(1751); + Frame:= random(3) + end; + vgtBeeTrace: + begin + FrameTicks:= 1000; + Frame:= random(16); + end; + vgtSmokeRing: + begin + dx:= 0; + dy:= 0; + FrameTicks:= 600; + Timer:= 0; + Frame:= 0; + scale:= 0.6; + alpha:= 1; + angle:= random(360); + end; + vgtFeather: + begin + t:= random(1024); + sp:= 0.001 * (random(85) + 95); + dx:= hwFloat2Float(AngleSin(t)) * sp; + dy:= hwFloat2Float(AngleCos(t)) * sp; + if random(2) = 0 then + dx := -dx; + if random(2) = 0 then + dy := -dy; + FrameTicks:= 650 + random(250); + Frame:= 1 + end; + vgtHealthTag: + begin + Frame:= 0; + Timer:= 1500; + dY:= -0.08; + dX:= 0; + //gear^.Z:= 2002; + end; + vgtSmokeTrace, + vgtEvilTrace: + begin + gear^.X:= gear^.X - 16; + gear^.Y:= gear^.Y - 16; + gear^.State:= 8; + //gear^.Z:= cSmokeZ + end; +vgtBigExplosion: + begin + gear^.Angle:= random(360); + end; + vgtChunk: + begin + gear^.Frame:= random(4); + t:= random(1024); + sp:= 0.001 * (random(85) + 47); + dx:= hwFloat2Float(AngleSin(t)) * sp; + dy:= hwFloat2Float(AngleCos(t)) * sp * -2; + if random(2) = 0 then + dx := -dx; + end; + vgtNote: + begin + dx:= 0.005 * (random(15) + 10); + dy:= -0.001 * (random(40) + 20); + if random(2) = 0 then + dx := -dx; + Frame:= random(4); + FrameTicks:= random(2000) + 1500; + end; + vgtBulletHit: + begin + dx:= 0; + dy:= 0; + FrameTicks:= 350; + Frame:= 7; + Angle:= 0; + end; +vgtSmoothWindBar: + begin + Angle:= hwFloat2Float(cMaxWindSpeed)*2 / 1440; // seems rate below is supposed to change wind bar at 1px per 10ms. Max time, 1440ms. This tries to match the rate of change + Tag:= hwRound(cWindSpeed * 72 / cMaxWindSpeed); + end; + vgtStraightShot: + begin + Angle:= 0; + Scale:= 1.0; + dx:= 0.001 * random(45); + dy:= 0.001 * (random(20) + 25); + State:= ord(sprHealth); + if random(2) = 0 then + dx := -dx; + Frame:= 0; + FrameTicks:= random(750) + 1250; + State:= ord(sprSnowDust); + end; + end; + +if State <> 0 then + gear^.State:= State; + +case Gear^.Kind of + vgtFlake: if cFlattenFlakes then + gear^.Layer:= 0 + else if random(3) = 0 then + begin + gear^.Scale:= 0.5; + gear^.Layer:= 0 // 33% - far back + end + else if random(3) = 0 then + begin + gear^.Scale:= 0.8; + gear^.Layer:= 4 // 22% - mid-distance + end + else if random(3) <> 0 then + gear^.Layer:= 5 // 30% - just behind land + else if random(2) = 0 then + gear^.Layer:= 6 // 7% - just in front of land + else + begin + gear^.Scale:= 1.5; + gear^.Layer:= 2; // 7% - close up + end; + + vgtCloud: if cFlattenClouds then gear^.Layer:= 5 + else if random(3) = 0 then + begin + gear^.Scale:= 0.25; + gear^.Layer:= 0 + end + else if random(2) = 0 then + gear^.Layer:= 5 + else + begin + gear^.Scale:= 0.4; + gear^.Layer:= 4 + end; + + // 0: this layer is very distant in the background when in stereo + vgtTeamHealthSorter, + vgtSmoothWindBar: gear^.Layer:= 0; + + + // 1: this layer is on the land level (which is close but behind the screen plane) when stereo + vgtSmokeTrace, + vgtEvilTrace, + vgtLineTrail, + vgtSmoke, + vgtSmokeWhite, + vgtDust, + vgtFire, + vgtSplash, + vgtDroplet, + vgtBubble: gear^.Layer:= 1; + + // 3: this layer is on the screen plane (depth = 0) when stereo + vgtSpeechBubble, + vgtSmallDamageTag, + vgtHealthTag, + vgtStraightShot, + vgtChunk: gear^.Layer:= 3; + + // 2: this layer is outside the screen when stereo + vgtExplosion, + vgtBigExplosion, + vgtExplPart, + vgtExplPart2, + vgtSteam, + vgtAmmo, + vgtShell, + vgtFeather, + vgtEgg, + vgtBeeTrace, + vgtSmokeRing, + vgtNote, + vgtBulletHit, + vgtCircle: gear^.Layer:= 2 +end; + +if VisualGearLayers[gear^.Layer] <> nil then + begin + VisualGearLayers[gear^.Layer]^.PrevGear:= gear; + gear^.NextGear:= VisualGearLayers[gear^.Layer] + end; +VisualGearLayers[gear^.Layer]:= gear; + +AddVisualGear:= gear; +end; + +procedure DeleteVisualGear(Gear: PVisualGear); +begin + FreeTexture(Gear^.Tex); + Gear^.Tex:= nil; + + if Gear^.NextGear <> nil then + Gear^.NextGear^.PrevGear:= Gear^.PrevGear; + if Gear^.PrevGear <> nil then + Gear^.PrevGear^.NextGear:= Gear^.NextGear + else + VisualGearLayers[Gear^.Layer]:= Gear^.NextGear; + + if lastVisualGearByUID = Gear then + lastVisualGearByUID:= nil; + + Dispose(Gear); +end; + +function VisualGearByUID(uid : Longword) : PVisualGear; +var vg: PVisualGear; + i: LongWord; +begin +VisualGearByUID:= nil; +if uid = 0 then + exit; +if (lastVisualGearByUID <> nil) and (lastVisualGearByUID^.uid = uid) then + begin + VisualGearByUID:= lastVisualGearByUID; + exit + end; +// search in an order that is more likely to return layers they actually use. Could perhaps track statistically AddVisualGear in uScript, since that is most likely the ones they want +for i:= 2 to 5 do + begin + vg:= VisualGearLayers[i mod 4]; + while vg <> nil do + begin + if vg^.uid = uid then + begin + lastVisualGearByUID:= vg; + VisualGearByUID:= vg; + exit + end; + vg:= vg^.NextGear + end + end +end; + + +end.