OSDN Git Service

Merge pull request #595 from sikabane-works/release/3.0.0Alpha13 3.0.0Alpha13
authorDeskull <61610939+sikabane-works@users.noreply.github.com>
Sun, 21 Mar 2021 10:02:40 +0000 (19:02 +0900)
committerGitHub <noreply@github.com>
Sun, 21 Mar 2021 10:02:40 +0000 (19:02 +0900)
Release/3.0.0 alpha13

227 files changed:
.github/workflows/buildtest-on-linux.yml
Hengband/Hengband/Hengband.vcxproj
Hengband/Hengband/Hengband.vcxproj.filters
Hengband/Hengband/stdafx.cpp [new file with mode: 0644]
Hengband/Hengband/stdafx.h [new file with mode: 0644]
Makefile.am
configure.ac
doxygen/Hengband.doxyfile
lib/edit/m_info.txt
lib/edit/s_info.txt
lib/edit/t0000001.txt
src/Makefile.am
src/action/activation-execution.cpp
src/ang_eng.rc
src/ang_jp.rc [new file with mode: 0644]
src/angband.rc
src/artifact/random-art-characteristics.cpp
src/artifact/random-art-generator.cpp
src/autopick/autopick-editor-util.cpp
src/autopick/autopick-util.cpp
src/birth/birth-explanations-table.cpp
src/birth/birth-select-realm.cpp
src/birth/birth-stat.cpp
src/birth/birth-stat.h
src/birth/birth-wizard.cpp
src/birth/game-play-initializer.cpp
src/birth/initial-equipments-table.cpp
src/birth/inventory-initializer.cpp
src/blue-magic/blue-magic-checker.cpp
src/blue-magic/blue-magic-checker.h
src/cmd-action/cmd-others.cpp
src/cmd-action/cmd-pet.cpp
src/cmd-building/cmd-building.cpp
src/cmd-building/cmd-building.h
src/cmd-building/cmd-inn.cpp
src/cmd-io/cmd-lore.cpp
src/cmd-io/macro-util.cpp
src/cmd-item/cmd-eat.cpp
src/cmd-item/cmd-equipment.cpp
src/combat/shoot.cpp
src/combat/slaying.cpp
src/core/asking-player.cpp
src/core/hp-mp-processor.cpp
src/effect/effect-monster-switcher.cpp
src/effect/effect-monster.cpp
src/effect/effect-player.cpp
src/effect/effect-player.h
src/effect/effect-processor.cpp
src/effect/effect-processor.h
src/flavor/flavor-describer.cpp
src/flavor/object-flavor.cpp
src/floor/floor-generator.cpp
src/floor/floor-object.cpp
src/grid/grid.cpp
src/grid/grid.h
src/info-reader/general-parser.cpp
src/io-dump/character-dump.cpp
src/io/input-key-processor.cpp
src/io/interpret-pref-file.cpp
src/io/read-pref-file.cpp
src/io/report.cpp
src/io/store-key-processor.cpp
src/io/uid-checker.cpp
src/knowledge/knowledge-experiences.cpp
src/knowledge/knowledge-self.cpp
src/load/inventory-loader.cpp
src/load/world-loader.cpp
src/lore/combat-types-setter.cpp
src/lore/lore-util.cpp
src/lore/lore-util.h
src/lore/monster-lore.cpp
src/maid-x11.cpp
src/main-win.cpp
src/main-win/main-win-bg.cpp [new file with mode: 0644]
src/main-win/main-win-bg.h [new file with mode: 0644]
src/main-win/main-win-define.h [new file with mode: 0644]
src/main-win/main-win-file-utils.cpp [new file with mode: 0644]
src/main-win/main-win-file-utils.h [new file with mode: 0644]
src/main-win/main-win-mci.cpp [new file with mode: 0644]
src/main-win/main-win-mci.h [new file with mode: 0644]
src/main-win/main-win-mmsystem.h [new file with mode: 0644]
src/main-win/main-win-music.cpp [new file with mode: 0644]
src/main-win/main-win-music.h [new file with mode: 0644]
src/main-win/main-win-sound.cpp [new file with mode: 0644]
src/main-win/main-win-sound.h [new file with mode: 0644]
src/main-win/main-win-tokenizer.cpp [new file with mode: 0644]
src/main-win/main-win-tokenizer.h [new file with mode: 0644]
src/main-win/main-win-windows.h [new file with mode: 0644]
src/main/angband-headers.h
src/main/game-data-initializer.cpp
src/main/info-initializer.cpp
src/market/bounty.cpp
src/market/building-recharger.cpp
src/melee/melee-spell.cpp
src/melee/melee-switcher.cpp
src/mind/mind-elementalist.cpp [new file with mode: 0644]
src/mind/mind-elementalist.h [new file with mode: 0644]
src/mind/mind-mindcrafter.cpp
src/mind/mind-types.h
src/mind/mind-weaponsmith.cpp
src/monster-attack/monster-attack-describer.cpp
src/monster-attack/monster-attack-switcher.cpp
src/monster-floor/monster-death.cpp
src/monster-floor/monster-lite.cpp
src/monster-floor/quantum-effect.cpp
src/monster-race/monster-race-hook.cpp
src/monster/monster-list.cpp
src/mspell/assign-monster-spell.cpp
src/mspell/assign-monster-spell.h
src/mspell/mspell-attack.cpp
src/mspell/mspell-ball.cpp
src/mspell/mspell-ball.h
src/mspell/mspell-bolt.cpp
src/mspell/mspell-bolt.h
src/mspell/mspell-breath.cpp
src/mspell/mspell-breath.h
src/mspell/mspell-checker.cpp
src/mspell/mspell-checker.h
src/mspell/mspell-curse.cpp
src/mspell/mspell-curse.h
src/mspell/mspell-dispel.cpp
src/mspell/mspell-dispel.h
src/mspell/mspell-floor.cpp
src/mspell/mspell-floor.h
src/mspell/mspell-particularity.cpp
src/mspell/mspell-particularity.h
src/mspell/mspell-special.cpp
src/mspell/mspell-special.h
src/mspell/mspell-status.cpp
src/mspell/mspell-status.h
src/mspell/mspell-summon.cpp
src/mspell/mspell-summon.h
src/mspell/mspell.h [new file with mode: 0644]
src/object-activation/activation-switcher.cpp
src/object-activation/activation-switcher.h
src/object-enchant/apply-magic-accessory.cpp
src/object-enchant/apply-magic-armor.cpp
src/object-enchant/apply-magic-others.cpp
src/object-enchant/apply-magic-weapon.cpp
src/object-enchant/object-ego.cpp
src/object-enchant/trg-types.h
src/object-hook/hook-bow.cpp
src/object-hook/hook-magic.cpp
src/object-hook/hook-quest.cpp
src/object-hook/hook-weapon.cpp
src/object/object-broken.cpp
src/object/object-info.cpp
src/object/object-value.cpp
src/object/warning.cpp
src/perception/identification.cpp
src/perception/simple-perception.cpp
src/player-info/avatar.cpp
src/player-info/class-ability-info.cpp
src/player-status/player-basic-statistics.cpp
src/player-status/player-basic-statistics.h
src/player-status/player-charisma.cpp
src/player-status/player-charisma.h
src/player-status/player-speed.cpp
src/player-status/player-status-base.cpp
src/player-status/player-status-base.h
src/player-status/player-stealth.cpp
src/player-status/player-stealth.h
src/player/eldritch-horror.cpp
src/player/permanent-resistances.cpp
src/player/player-class.cpp
src/player/player-classes-types.h
src/player/player-realm.cpp
src/player/player-status-flags.cpp
src/player/player-status-resist.cpp
src/player/player-status.cpp
src/player/player-status.h
src/player/player-view.cpp
src/player/race-info-table.cpp
src/player/race-resistances.cpp
src/racial/class-racial-switcher.cpp
src/racial/racial-draconian.cpp
src/racial/racial-switcher.cpp
src/realm/realm-hex.cpp
src/room/cave-filler.cpp
src/room/pit-nest-kinds-table.cpp [deleted file]
src/room/pit-nest-kinds-table.h [deleted file]
src/room/room-generator.cpp
src/room/rooms-pit-nest.cpp
src/room/rooms-pit-nest.h
src/save/player-writer.cpp
src/specific-object/torch.cpp
src/spell-kind/spells-launcher.cpp
src/spell-kind/spells-lite.cpp
src/spell-kind/spells-neighbor.cpp
src/spell-kind/spells-sight.cpp
src/spell/spell-types.h
src/spell/spells-staff-only.cpp
src/spell/spells-status.cpp
src/spell/spells-status.h
src/status/buff-setter.cpp
src/store/rumor.cpp
src/system/alloc-entry-definition.h
src/system/angband-version.h
src/system/system-variables.cpp
src/target/grid-selector.cpp
src/target/target-preparation.cpp
src/target/target-preparation.h
src/target/target-setter.cpp
src/term/z-virt.cpp
src/term/z-virt.h
src/util/object-sort.cpp
src/util/probability-table.h [new file with mode: 0644]
src/util/string-processor.cpp
src/util/string-processor.h
src/view/display-lore-attacks.cpp
src/view/display-lore-drops.cpp
src/view/display-lore-magics.cpp
src/view/display-lore-status.cpp
src/view/display-lore.cpp
src/view/display-player.cpp
src/window/display-sub-window-spells.cpp
src/window/display-sub-windows.cpp
src/window/main-window-left-frame.cpp
src/window/main-window-stat-poster.cpp
src/wizard/cmd-wizard.cpp
src/wizard/monster-info-spoiler.cpp
src/wizard/monster-info-spoiler.h
src/wizard/wizard-item-modifier.cpp
src/wizard/wizard-item-modifier.h
src/wizard/wizard-spoiler.cpp
src/world/world-object.cpp
src/world/world.h

index 716d3f8..d8b671d 100644 (file)
@@ -12,7 +12,7 @@ jobs:
     name: Build test on Ubuntu-20.04
     runs-on: ubuntu-20.04
     env:
-      CFLAGS: "-pipe -O3 -Werror -Wall -Wextra -Wno-switch -Wno-sign-compare -Wno-unused-parameter -Wno-unused-function"
+      CXXFLAGS: "-pipe -O3 -Werror -Wall -Wextra"
     steps:
       - uses: actions/checkout@v2
 
@@ -24,6 +24,7 @@ jobs:
             libncursesw5-dev \
             libcurl4-openssl-dev \
             nkf \
+            clang-11 \
 
       - name: Generate configure
         run: ./bootstrap
@@ -32,13 +33,25 @@ jobs:
         run: ./configure
 
       - name: Build Japanese version
-        run: make -j$(nproc)
+        run: make -j$(nproc) 1> /dev/null
 
       - name: Clean source tree
         run: make clean
 
-      - name: Configure for English versoin
+      - name: Configure for English version
         run: ./configure --disable-japanese
 
       - name: Build English version
-        run: make -j$(nproc)
+        run: make -j$(nproc) 1> /dev/null
+
+      - name: Clean source tree
+        run: make clean
+
+      - name: Configure for compiling with clang
+        run: ./configure
+        env:
+          CXX: clang++-11
+          CXXFLAGS: "-pipe -O3 -Werror -Wall -Wextra -Wno-unused-const-variable -Wno-invalid-source-encoding"
+
+      - name: Build with clang
+        run: make -j$(nproc) 1> /dev/null
index e93ba86..c1dcadd 100644 (file)
       <AdditionalIncludeDirectories>..\..\src;curl\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r
       <LanguageStandard>stdcpplatest</LanguageStandard>\r
       <ExceptionHandling>SyncCThrow</ExceptionHandling>\r
+      <PrecompiledHeader>Use</PrecompiledHeader>\r
+      <ForcedIncludeFiles>stdafx.h</ForcedIncludeFiles>\r
     </ClCompile>\r
     <Link>\r
       <GenerateDebugInformation>true</GenerateDebugInformation>\r
       <Command>\r
       </Command>\r
     </PreBuildEvent>\r
+    <ResourceCompile>\r
+      <Culture>0x0411</Culture>\r
+      <PreprocessorDefinitions>JP</PreprocessorDefinitions>\r
+    </ResourceCompile>\r
   </ItemDefinitionGroup>\r
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='English-Debug|Win32'">\r
     <ClCompile>\r
       <AdditionalIncludeDirectories>..\..\src;curl\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r
       <ExceptionHandling>SyncCThrow</ExceptionHandling>\r
       <LanguageStandard>stdcpplatest</LanguageStandard>\r
+      <PrecompiledHeader>Use</PrecompiledHeader>\r
+      <ForcedIncludeFiles>stdafx.h</ForcedIncludeFiles>\r
     </ClCompile>\r
     <Link>\r
       <GenerateDebugInformation>true</GenerateDebugInformation>\r
       <CompileAs>CompileAsCpp</CompileAs>\r
       <ExceptionHandling>SyncCThrow</ExceptionHandling>\r
       <LanguageStandard>stdcpplatest</LanguageStandard>\r
+      <PrecompiledHeader>Use</PrecompiledHeader>\r
+      <ForcedIncludeFiles>stdafx.h</ForcedIncludeFiles>\r
     </ClCompile>\r
     <Link>\r
       <TargetMachine>MachineX86</TargetMachine>\r
       <EnableCOMDATFolding>true</EnableCOMDATFolding>\r
       <SubSystem>Windows</SubSystem>\r
     </Link>\r
+    <ResourceCompile>\r
+      <PreprocessorDefinitions>JP</PreprocessorDefinitions>\r
+    </ResourceCompile>\r
+    <ResourceCompile>\r
+      <Culture>0x0411</Culture>\r
+    </ResourceCompile>\r
   </ItemDefinitionGroup>\r
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='English-Release|Win32'">\r
     <ClCompile>\r
       <ExceptionHandling>SyncCThrow</ExceptionHandling>\r
       <LanguageStandard>stdcpplatest</LanguageStandard>\r
       <CompileAs>CompileAsCpp</CompileAs>\r
+      <PrecompiledHeader>Use</PrecompiledHeader>\r
+      <ForcedIncludeFiles>stdafx.h</ForcedIncludeFiles>\r
     </ClCompile>\r
     <Link>\r
       <AdditionalDependencies>winmm.lib;Ws2_32.lib;Wldap32.lib;Crypt32.lib;Normaliz.lib;curl\x86 Release\libcurl_a.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
     <ClCompile Include="..\..\src\io\store-key-processor.cpp" />\r
     <ClCompile Include="..\..\src\load\info-loader.cpp" />\r
     <ClCompile Include="..\..\src\locale\utf-8.cpp" />\r
+    <ClCompile Include="..\..\src\main-win\main-win-file-utils.cpp" />\r
+    <ClCompile Include="..\..\src\main-win\main-win-mci.cpp" />\r
+    <ClCompile Include="..\..\src\main-win\main-win-music.cpp" />\r
+    <ClCompile Include="..\..\src\main-win\main-win-sound.cpp" />\r
+    <ClCompile Include="..\..\src\main-win\main-win-tokenizer.cpp" />\r
     <ClCompile Include="..\..\src\main\angband-headers.cpp" />\r
     <ClCompile Include="..\..\src\main\game-data-initializer.cpp" />\r
     <ClCompile Include="..\..\src\main\info-initializer.cpp" />\r
     <ClCompile Include="..\..\src\main\init-error-messages-table.cpp" />\r
+    <ClCompile Include="..\..\src\main-win\main-win-bg.cpp" />\r
     <ClCompile Include="..\..\src\market\building-initializer.cpp" />\r
     <ClCompile Include="..\..\src\melee\melee-spell-flags-checker.cpp" />\r
     <ClCompile Include="..\..\src\melee\melee-spell-util.cpp" />\r
     <ClCompile Include="..\..\src\mind\mind-berserker.cpp" />\r
     <ClCompile Include="..\..\src\mind\mind-blue-mage.cpp" />\r
     <ClCompile Include="..\..\src\mind\mind-chaos-warrior.cpp" />\r
+    <ClCompile Include="..\..\src\mind\mind-elementalist.cpp" />\r
     <ClCompile Include="..\..\src\mind\mind-explanations-table.cpp" />\r
     <ClCompile Include="..\..\src\mind\mind-hobbit.cpp" />\r
     <ClCompile Include="..\..\src\mind\mind-info.cpp" />\r
     <ClCompile Include="..\..\src\load\quest-loader.cpp" />\r
     <ClCompile Include="..\..\src\view\display-store.cpp" />\r
     <ClCompile Include="..\..\src\action\throw-util.cpp" />\r
+    <ClCompile Include="stdafx.cpp">\r
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>\r
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='English-Release|Win32'">Create</PrecompiledHeader>\r
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>\r
+      <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='English-Debug|Win32'">Create</PrecompiledHeader>\r
+    </ClCompile>\r
     <ClInclude Include="..\..\src\action\action-limited.h" />\r
     <ClInclude Include="..\..\src\action\activation-execution.h" />\r
     <ClInclude Include="..\..\src\action\mutation-execution.h" />\r
     <ClInclude Include="..\..\src\load\info-loader.h" />\r
     <ClInclude Include="..\..\src\locale\language-switcher.h" />\r
     <ClInclude Include="..\..\src\locale\utf-8.h" />\r
+    <ClInclude Include="..\..\src\main-win\main-win-file-utils.h" />\r
+    <ClInclude Include="..\..\src\main-win\main-win-mci.h" />\r
+    <ClInclude Include="..\..\src\main-win\main-win-mmsystem.h" />\r
+    <ClInclude Include="..\..\src\main-win\main-win-music.h" />\r
+    <ClInclude Include="..\..\src\main-win\main-win-sound.h" />\r
+    <ClInclude Include="..\..\src\main-win\main-win-define.h" />\r
+    <ClInclude Include="..\..\src\main-win\main-win-tokenizer.h" />\r
     <ClInclude Include="..\..\src\main\angband-headers.h" />\r
     <ClInclude Include="..\..\src\main\game-data-initializer.h" />\r
     <ClInclude Include="..\..\src\main\info-initializer.h" />\r
     <ClInclude Include="..\..\src\main\init-error-messages-table.h" />\r
+    <ClInclude Include="..\..\src\main-win\main-win-bg.h" />\r
+    <ClInclude Include="..\..\src\main-win\main-win-windows.h" />\r
     <ClInclude Include="..\..\src\market\building-initializer.h" />\r
     <ClInclude Include="..\..\src\melee\melee-spell-flags-checker.h" />\r
     <ClInclude Include="..\..\src\melee\melee-spell-util.h" />\r
     <ClInclude Include="..\..\src\mind\mind-berserker.h" />\r
     <ClInclude Include="..\..\src\mind\mind-blue-mage.h" />\r
     <ClInclude Include="..\..\src\mind\mind-chaos-warrior.h" />\r
+    <ClInclude Include="..\..\src\mind\mind-elementalist.h" />\r
     <ClInclude Include="..\..\src\mind\mind-explanations-table.h" />\r
     <ClInclude Include="..\..\src\mind\mind-hobbit.h" />\r
     <ClInclude Include="..\..\src\mind\mind-info.h" />\r
     <ClInclude Include="..\..\src\mspell\mspell-type.h" />\r
     <ClInclude Include="..\..\src\mspell\mspell-util.h" />\r
     <ClInclude Include="..\..\src\mspell\mspell-ball.h" />\r
+    <ClInclude Include="..\..\src\mspell\mspell.h" />\r
     <ClInclude Include="..\..\src\player\player-personalities-types.h" />\r
     <ClInclude Include="..\..\src\player\player-race-types.h" />\r
     <ClInclude Include="..\..\src\player\player-classes-types.h" />\r
     <ClCompile Include="..\..\src\room\rooms-trap.cpp" />\r
     <ClCompile Include="..\..\src\room\rooms-vault.cpp" />\r
     <ClCompile Include="..\..\src\room\rooms-builder.cpp" />\r
-    <ClCompile Include="..\..\src\room\pit-nest-kinds-table.cpp" />\r
     <ClCompile Include="..\..\src\store\rumor.cpp" />\r
     <ClCompile Include="..\..\src\save\save.cpp" />\r
     <ClCompile Include="..\..\src\core\scores.cpp" />\r
     <ClInclude Include="..\..\src\player\race-info-table.h" />\r
     <ClInclude Include="..\..\src\player\race-resistances.h" />\r
     <ClInclude Include="..\..\src\player\temporary-resistances.h" />\r
-    <ClInclude Include="..\..\src\room\pit-nest-kinds-table.h" />\r
     <ClInclude Include="..\..\src\io\signal-handlers.h" />\r
     <ClInclude Include="..\..\src\io\uid-checker.h" />\r
     <ClInclude Include="..\..\src\spell-realm\spells-song.h" />\r
     <ClInclude Include="..\..\src\wizard\tval-descriptions-table.h" />\r
     <ClInclude Include="..\..\src\load\quest-loader.h" />\r
     <ClInclude Include="..\..\src\view\display-store.h" />\r
+    <ClInclude Include="stdafx.h" />\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="..\..\src\system\angband.h" />\r
index cf05897..60fbc31 100644 (file)
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>\r
+<?xml version="1.0" encoding="utf-8"?>\r
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
   <ItemGroup>\r
     <ClCompile Include="..\..\src\main-win.cpp" />\r
     <ClCompile Include="..\..\src\autopick\autopick-describer.cpp">\r
       <Filter>autopick</Filter>\r
     </ClCompile>\r
-    <ClCompile Include="..\..\src\room\pit-nest-kinds-table.cpp">\r
-      <Filter>room</Filter>\r
-    </ClCompile>\r
     <ClCompile Include="..\..\src\io\signal-handlers.cpp">\r
       <Filter>io</Filter>\r
     </ClCompile>\r
     <ClCompile Include="..\..\src\player-status\player-charisma.cpp">\r
       <Filter>player-status</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="..\..\src\mind\mind-elementalist.cpp">\r
+      <Filter>mind</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\src\main-win\main-win-bg.cpp">\r
+      <Filter>main-win</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\src\main-win\main-win-music.cpp">\r
+      <Filter>main-win</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\src\main-win\main-win-sound.cpp">\r
+      <Filter>main-win</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\src\main-win\main-win-mci.cpp">\r
+      <Filter>main-win</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\src\main-win\main-win-tokenizer.cpp">\r
+      <Filter>main-win</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="..\..\src\main-win\main-win-file-utils.cpp">\r
+      <Filter>main-win</Filter>\r
+    </ClCompile>\r
+    <ClCompile Include="stdafx.cpp" />\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ClInclude Include="..\..\src\combat\shoot.h">\r
     <ClInclude Include="..\..\src\autopick\autopick-describer.h">\r
       <Filter>autopick</Filter>\r
     </ClInclude>\r
-    <ClInclude Include="..\..\src\room\pit-nest-kinds-table.h">\r
-      <Filter>room</Filter>\r
-    </ClInclude>\r
     <ClInclude Include="..\..\src\io\signal-handlers.h">\r
       <Filter>io</Filter>\r
     </ClInclude>\r
     <ClInclude Include="..\..\src\mspell\mspell-lite.h">\r
       <Filter>mspell</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\..\src\mspell\mspell.h">\r
+      <Filter>mspell</Filter>\r
+    </ClInclude>\r
     <ClInclude Include="..\..\src\object-use\read-execution.h">\r
       <Filter>object-use</Filter>\r
     </ClInclude>\r
     <ClInclude Include="..\..\src\util\flag-group.h">\r
       <Filter>util</Filter>\r
     </ClInclude>\r
+    <ClInclude Include="..\..\src\mind\mind-elementalist.h">\r
+      <Filter>mind</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\src\main-win\main-win-bg.h">\r
+      <Filter>main-win</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\src\main-win\main-win-windows.h">\r
+      <Filter>main-win</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\src\main-win\main-win-music.h">\r
+      <Filter>main-win</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\src\main-win\main-win-sound.h">\r
+      <Filter>main-win</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\src\main-win\main-win-mmsystem.h">\r
+      <Filter>main-win</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\src\main-win\main-win-define.h">\r
+      <Filter>main-win</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\src\main-win\main-win-mci.h">\r
+      <Filter>main-win</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\src\main-win\main-win-tokenizer.h">\r
+      <Filter>main-win</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="..\..\src\main-win\main-win-file-utils.h">\r
+      <Filter>main-win</Filter>\r
+    </ClInclude>\r
+    <ClInclude Include="stdafx.h" />\r
   </ItemGroup>\r
   <ItemGroup>\r
     <None Include="..\..\src\wall.bmp" />\r
     <Filter Include="player-status">\r
       <UniqueIdentifier>{d6c9dc68-d79b-4509-bf9c-34ff9620bbf4}</UniqueIdentifier>\r
     </Filter>\r
+    <Filter Include="main-win">\r
+      <UniqueIdentifier>{8175f3d1-c8b3-4f2b-8536-8631d77eb53c}</UniqueIdentifier>\r
+    </Filter>\r
   </ItemGroup>\r
   <ItemGroup>\r
     <ResourceCompile Include="..\..\src\angband.rc" />\r
diff --git a/Hengband/Hengband/stdafx.cpp b/Hengband/Hengband/stdafx.cpp
new file mode 100644 (file)
index 0000000..d1303f3
--- /dev/null
@@ -0,0 +1 @@
+#include "stdafx.h"
diff --git a/Hengband/Hengband/stdafx.h b/Hengband/Hengband/stdafx.h
new file mode 100644 (file)
index 0000000..ff2f37b
--- /dev/null
@@ -0,0 +1,15 @@
+#pragma once
+#include <algorithm>
+#include <array>
+#include <bitset>
+#include <iterator>
+#include <functional>
+#include <map>
+#include <queue>
+#include <sstream>
+#include <stack>
+#include <string>
+#include <string_view>
+#include <unordered_map>
+#include <utility>
+#include <vector>
index 178e342..d1c3c31 100644 (file)
@@ -4,9 +4,21 @@ AUTOMAKE_OPTIONS = foreign
 
 visual_studio_files = \
        Hengband/Hengband.sln \
+       Hengband/Hengband/curl/include/curl/curl.h \
+       Hengband/Hengband/curl/include/curl/curlver.h \
+       Hengband/Hengband/curl/include/curl/easy.h \
+       Hengband/Hengband/curl/include/curl/mprintf.h \
+       Hengband/Hengband/curl/include/curl/multi.h \
+       Hengband/Hengband/curl/include/curl/options.h \
+       Hengband/Hengband/curl/include/curl/stdcheaders.h \
+       Hengband/Hengband/curl/include/curl/system.h \
+       Hengband/Hengband/curl/include/curl/typecheck-gcc.h \
+       Hengband/Hengband/curl/include/curl/urlapi.h \
        Hengband/Hengband/Hengband.vcxproj \
        Hengband/Hengband/Hengband.vcxproj.filters \
-       Hengband/Hengband/packages.config
+       Hengband/Hengband/packages.config \
+       Hengband/Hengband/stdafx.cpp \
+       Hengband/Hengband/stdafx.h
 
 EXTRA_DIST = \
        autopick.txt \
index b8f23a3..158f90a 100644 (file)
@@ -44,7 +44,11 @@ AC_SUBST(DEFAULT_VAR_PATH)
 dnl Checks for programs.
 AC_LANG(C++)
 AC_PROG_CXX
-AX_CXX_COMPILE_STDCXX_17
+m4_ifdef([AX_CXX_COMPILE_STDCXX_17], [
+  AX_CXX_COMPILE_STDCXX_17
+], [
+  AC_MSG_ERROR([AX_CXX_COMPILE_STDCXX_17 macro is not defined. You need to install autoconf-archive.])
+])
 PKG_PROG_PKG_CONFIG
 
 AC_ARG_ENABLE(japanese,
index 1be9032..64afefd 100644 (file)
@@ -38,7 +38,7 @@ PROJECT_NAME           = Hengband
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = 3.0.0Alpha10
+PROJECT_NUMBER         = 3.0.0Alpha13
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a
@@ -267,7 +267,7 @@ ALIASES                =
 # members will be omitted, etc.
 # The default value is: NO.
 
-OPTIMIZE_OUTPUT_FOR_C  = YES
+OPTIMIZE_OUTPUT_FOR_C  = NO
 
 # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
 # Python sources only. Doxygen will then generate output that is more tailored
@@ -592,7 +592,7 @@ CASE_SENSE_NAMES       = NO
 # scope will be hidden.
 # The default value is: NO.
 
-HIDE_SCOPE_NAMES       = YES
+HIDE_SCOPE_NAMES       = NO
 
 # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
 # append additional text to a page's title, such as Class Reference. If set to
@@ -885,7 +885,8 @@ INPUT_ENCODING         = UTF-8
 # *.ucf, *.qsf and *.ice.
 
 FILE_PATTERNS          = *.c \
-                         *.h
+                         *.h \
+                         *.cpp
 
 # The RECURSIVE tag can be used to specify whether or not subdirectories should
 # be searched for input files as well.
@@ -1035,7 +1036,7 @@ INLINE_SOURCES         = NO
 # Fortran comments will always remain visible.
 # The default value is: YES.
 
-STRIP_CODE_COMMENTS    = NO
+STRIP_CODE_COMMENTS    = YES
 
 # If the REFERENCED_BY_RELATION tag is set to YES then for each documented
 # entity all documented functions referencing it will be listed.
index 979ac21..7e57ffc 100644 (file)
@@ -3737,3 +3737,37 @@ R:8:0
 
 # Crusade
 R:9:0
+
+
+### Elementalist ###
+N:28
+I:NONE:WIS:0x00:0:1:430
+# Life
+R:0:0
+
+# Sorcery
+R:1:0
+
+# Neture
+R:2:0
+
+# Chaos
+R:3:0
+
+# Death
+R:4:0
+
+# Trump
+R:5:0
+
+# Arcane
+R:6:0
+
+# Craft
+R:7:0
+
+# Deamon
+R:8:0
+
+# Crusade
+R:9:0
index 09532fb..607dc54 100644 (file)
@@ -9327,3 +9327,336 @@ S:0:0:2000  # SUDE
 S:1:0:1000  # NITOURYU
 S:2:0:0     # RIDING
 S:3:0:0     # SHIELD
+
+### ELEMENTALIST ###
+N:28
+# Bow
+W:0:0:0:1  # 0
+W:0:1:0:1  # 
+W:0:2:1:2  # SLING
+W:0:3:0:1  # 
+W:0:4:0:1  # 
+W:0:5:0:1  # 
+W:0:6:0:1  # 
+W:0:7:0:1  # 
+W:0:8:0:1  # 
+W:0:9:0:1  # 
+W:0:10:0:1  # 10
+W:0:11:0:1  # 
+W:0:12:0:1  # SHORT_BOW
+W:0:13:0:1  # LONG_BOW
+W:0:14:0:1  # 
+W:0:15:0:1  # 
+W:0:16:0:1  # 
+W:0:17:0:1  # 
+W:0:18:0:1  # 
+W:0:19:0:1  # 
+W:0:20:0:1  # 20
+W:0:21:0:1  # 
+W:0:22:0:1  # 
+W:0:23:0:1  # LIGHT_XBOW
+W:0:24:0:1  # HEAVY_XBOW
+W:0:25:0:1  # 
+W:0:26:0:1  # 
+W:0:27:0:1  # 
+W:0:28:0:1  # 
+W:0:29:0:1  # 
+W:0:30:0:1  # 30
+W:0:31:0:1  # 
+W:0:32:0:1  # 
+W:0:33:0:1  # 
+W:0:34:0:1  # 
+W:0:35:0:1  # 
+W:0:36:0:1  # 
+W:0:37:0:1  # 
+W:0:38:0:1  # 
+W:0:39:0:1  # 
+W:0:40:0:1  # 40
+W:0:41:0:1  # 
+W:0:42:0:1  # 
+W:0:43:0:1  # 
+W:0:44:0:1  # 
+W:0:45:0:1  # 
+W:0:46:0:1  # 
+W:0:47:0:1  # 
+W:0:48:0:1  # 
+W:0:49:0:1  # 
+W:0:50:0:1  # 50
+W:0:51:0:1  # 
+W:0:52:0:1  # 
+W:0:53:0:1  # 
+W:0:54:0:1  # 
+W:0:55:0:1  # 
+W:0:56:0:1  # 
+W:0:57:0:1  # 
+W:0:58:0:1  # 
+W:0:59:0:1  # 
+W:0:60:0:1  # 60
+W:0:61:0:1  # 
+W:0:62:0:1  # 
+W:0:63:0:4  # NAMAKE
+# Digging
+W:1:0:0:1  # 0
+W:1:1:0:1  # SHOVEL
+W:1:2:0:1  # GNOMISH_SHOVEL
+W:1:3:0:1  # DWARVEN_SHOVEL
+W:1:4:0:1  # PICK
+W:1:5:0:1  # ORCISH_PICK
+W:1:6:0:1  # DWARVEN_PICK
+W:1:7:0:1  # MATTOCK
+W:1:8:0:1  # 
+W:1:9:0:1  # 
+W:1:10:0:1  # 10
+W:1:11:0:1  # 
+W:1:12:0:1  # 
+W:1:13:0:1  # 
+W:1:14:0:1  # 
+W:1:15:0:1  # 
+W:1:16:0:1  # 
+W:1:17:0:1  # 
+W:1:18:0:1  # 
+W:1:19:0:1  # 
+W:1:20:0:1  # 20
+W:1:21:0:1  # 
+W:1:22:0:1  # 
+W:1:23:0:1  # 
+W:1:24:0:1  # 
+W:1:25:0:1  # 
+W:1:26:0:1  # 
+W:1:27:0:1  # 
+W:1:28:0:1  # 
+W:1:29:0:1  # 
+W:1:30:0:1  # 30
+W:1:31:0:1  # 
+W:1:32:0:1  # 
+W:1:33:0:1  # 
+W:1:34:0:1  # 
+W:1:35:0:1  # 
+W:1:36:0:1  # 
+W:1:37:0:1  # 
+W:1:38:0:1  # 
+W:1:39:0:1  # 
+W:1:40:0:1  # 40
+W:1:41:0:1  # 
+W:1:42:0:1  # 
+W:1:43:0:1  # 
+W:1:44:0:1  # 
+W:1:45:0:1  # 
+W:1:46:0:1  # 
+W:1:47:0:1  # 
+W:1:48:0:1  # 
+W:1:49:0:1  # 
+W:1:50:0:1  # 50
+W:1:51:0:1  # 
+W:1:52:0:1  # 
+W:1:53:0:1  # 
+W:1:54:0:1  # 
+W:1:55:0:1  # 
+W:1:56:0:1  # 
+W:1:57:0:1  # 
+W:1:58:0:1  # 
+W:1:59:0:1  # 
+W:1:60:0:1  # 60
+W:1:61:0:1  # 
+W:1:62:0:1  # 
+W:1:63:0:1  # 
+# Hafted
+W:2:0:0:1  # 0
+W:2:1:0:1  # CLUB
+W:2:2:0:1  # WHIP
+W:2:3:1:4  # QUARTERSTAFF
+W:2:4:0:0  # NUNCHAKU
+W:2:5:0:1  # MACE
+W:2:6:0:1  # BALL_AND_CHAIN
+W:2:7:0:1  # JO_STAFF
+W:2:8:0:1  # WAR_HAMMER
+W:2:9:0:1  # 
+W:2:10:0:1  # 10
+W:2:11:0:1  # THREE_PIECE_ROD
+W:2:12:0:1  # MORNING_STAR
+W:2:13:0:1  # FLAIL
+W:2:14:0:1  # BO_STAFF
+W:2:15:0:1  # LEAD_FILLED_MACE
+W:2:16:0:1  # TETSUBO
+W:2:17:0:1  # 
+W:2:18:0:1  # TWO_HANDED_FLAIL
+W:2:19:0:1  # GREAT_HAMMER
+W:2:20:0:1  # MACE_OF_DISRUPTION
+W:2:21:1:4  # WIZSTAFF
+W:2:22:0:1  # 
+W:2:23:0:1  # 
+W:2:24:0:1  # 
+W:2:25:0:1  # 
+W:2:26:0:1  # 
+W:2:27:0:1  # 
+W:2:28:0:1  # 
+W:2:29:0:1  # 
+W:2:30:0:1  # 30
+W:2:31:0:1  # 
+W:2:32:0:1  # 
+W:2:33:0:1  # 
+W:2:34:0:1  # 
+W:2:35:0:1  # 
+W:2:36:0:1  # 
+W:2:37:0:1  # 
+W:2:38:0:1  # 
+W:2:39:0:1  # 
+W:2:40:0:0  # TSURIZAO
+W:2:41:0:1  # 
+W:2:42:0:1  # 
+W:2:43:0:1  # 
+W:2:44:0:1  # 
+W:2:45:0:1  # 
+W:2:46:0:1  # 
+W:2:47:0:1  # 
+W:2:48:0:1  # 
+W:2:49:0:1  # 
+W:2:50:0:1  # GROND
+W:2:51:0:1  # 
+W:2:52:0:1  # 
+W:2:53:0:1  # 
+W:2:54:0:1  # 
+W:2:55:0:1  # 
+W:2:56:0:1  # 
+W:2:57:0:1  # 
+W:2:58:0:1  # 
+W:2:59:0:1  # 
+W:2:60:0:1  # 60
+W:2:61:0:1  # 
+W:2:62:0:1  # 
+W:2:63:0:4  # NAMAKE
+# Polearm
+W:3:0:0:1  # 0
+W:3:1:0:1  # HATCHET
+W:3:2:0:1  # SPEAR
+W:3:3:0:1  # SICKLE
+W:3:4:0:1  # AWL_PIKE
+W:3:5:0:1  # TRIDENT
+W:3:6:0:1  # FAUCHARD
+W:3:7:0:1  # BROAD_SPEAR
+W:3:8:0:1  # PIKE
+W:3:9:0:1  # NAGINATA
+W:3:10:0:1  # BEAKED_AXE
+W:3:11:0:1  # BROAD_AXE
+W:3:12:0:1  # LUCERNE_HAMMER
+W:3:13:0:1  # GLAIVE
+W:3:14:0:1  # LAJATANG
+W:3:15:0:1  # HALBERD
+W:3:16:0:1  # GUISARME
+W:3:17:0:1  # SCYTHE
+W:3:18:0:1  # 
+W:3:19:0:1  # 
+W:3:20:0:1  # LANCE
+W:3:21:0:1  # 
+W:3:22:0:1  # BATTLE_AXE
+W:3:23:0:1  # 
+W:3:24:0:1  # 
+W:3:25:0:1  # GREAT_AXE
+W:3:26:0:1  # TRIFURCATE_SPEAR
+W:3:27:0:1  # 
+W:3:28:0:1  # LOCHABER_AXE
+W:3:29:0:1  # HEAVY_LANCE
+W:3:30:0:1  # SCYTHE_OF_SLICING
+W:3:31:0:1  # 
+W:3:32:0:1  # 
+W:3:33:0:1  # 
+W:3:34:0:1  # 
+W:3:35:0:1  # 
+W:3:36:0:1  # 
+W:3:37:0:1  # 
+W:3:38:0:1  # 
+W:3:39:0:1  # 
+W:3:40:0:1  # 40
+W:3:41:0:1  # 
+W:3:42:0:1  # 
+W:3:43:0:1  # 
+W:3:44:0:1  # 
+W:3:45:0:1  # 
+W:3:46:0:1  # 
+W:3:47:0:1  # 
+W:3:48:0:1  # 
+W:3:49:0:1  # 
+W:3:50:0:1  # DEATH_SCYTHE
+W:3:51:0:1  # 
+W:3:52:0:1  # 
+W:3:53:0:1  # 
+W:3:54:0:1  # 
+W:3:55:0:1  # 
+W:3:56:0:1  # 
+W:3:57:0:1  # 
+W:3:58:0:1  # 
+W:3:59:0:1  # 
+W:3:60:0:1  # 60
+W:3:61:0:1  # 
+W:3:62:0:1  # 
+W:3:63:0:1  # 
+# Sword
+W:4:0:0:1  # 0
+W:4:1:0:1  # BROKEN_DAGGER
+W:4:2:0:1  # BROKEN_SWORD
+W:4:3:0:1  # 
+W:4:4:1:4  # DAGGER
+W:4:5:0:1  # MAIN_GAUCHE
+W:4:6:0:2  # TANTO
+W:4:7:0:1  # RAPIER
+W:4:8:0:1  # SMALL_SWORD
+W:4:9:0:1  # BASILLARD
+W:4:10:0:1  # SHORT_SWORD
+W:4:11:0:1  # SABRE
+W:4:12:0:1  # CUTLASS
+W:4:13:0:1  # WAKIZASHI
+W:4:14:0:1  # KHOPESH
+W:4:15:0:1  # TULWAR
+W:4:16:0:1  # BROAD_SWORD
+W:4:17:0:1  # LONG_SWORD
+W:4:18:0:1  # SCIMITAR
+W:4:19:0:1  # NINJATO
+W:4:20:0:1  # KATANA
+W:4:21:0:1  # BASTARD_SWORD
+W:4:22:0:1  # GREAT_SCIMITAR
+W:4:23:0:1  # CLAYMORE
+W:4:24:0:1  # ESPADON
+W:4:25:0:1  # TWO_HANDED_SWORD
+W:4:26:0:1  # FLAMBERGE
+W:4:27:0:1  # NO_DACHI
+W:4:28:0:1  # EXECUTIOERS_SWORD
+W:4:29:0:1  # ZWEIHANDER
+W:4:30:0:1  # BLADE_OF_CHAOS
+W:4:31:0:1  # DIAMOND_EDGE
+W:4:32:0:4  # DOKUBARI
+W:4:33:0:1  # 
+W:4:34:0:1  # 
+W:4:35:0:1  # 
+W:4:36:0:1  # 
+W:4:37:0:1  # 
+W:4:38:0:1  # 
+W:4:39:0:1  # 
+W:4:40:0:1  # 40
+W:4:41:0:1  # 
+W:4:42:0:1  # 
+W:4:43:0:1  # 
+W:4:44:0:1  # 
+W:4:45:0:1  # 
+W:4:46:0:1  # 
+W:4:47:0:1  # 
+W:4:48:0:1  # 
+W:4:49:0:1  # 
+W:4:50:0:1  # 50
+W:4:51:0:1  # 
+W:4:52:0:1  # 
+W:4:53:0:1  # 
+W:4:54:0:1  # 
+W:4:55:0:1  # 
+W:4:56:0:1  # 
+W:4:57:0:1  # 
+W:4:58:0:1  # 
+W:4:59:0:1  # 
+W:4:60:0:1  # 60
+W:4:61:0:1  # 
+W:4:62:0:1  # 
+W:4:63:0:1  # 
+# Skill
+S:0:0:4000  # SUDE
+S:1:0:0  # NITOURYU
+S:2:0:0  # RIDING
+S:3:0:0  # SHIELD
index 776339c..f0aa77d 100644 (file)
@@ -484,6 +484,11 @@ F:@:FLOOR:3:0:0:0:220
 F:a:BUILDING_0:3
 F:@:FLOOR:3:0:0:0:223
 
+# Quest 27 rewarding (Elementalist gets The Stone of Laputa)
+?:[AND [EQU $QUEST27 3] [EQU $CLASS Elementalist] ]
+F:a:BUILDING_0:3
+F:@:FLOOR:3:0:0:0:147
+
 # Quest 27 finished, no new quest is available
 ?:[EQU $QUEST27 4]
 F:a:BUILDING_0:3
index cf35c4c..9759769 100644 (file)
@@ -418,6 +418,7 @@ hengband_SOURCES = \
        mind/mind-blue-mage.cpp mind/mind-blue-mage.h \
        mind/mind-cavalry.cpp mind/mind-cavalry.h \
        mind/mind-chaos-warrior.cpp mind/mind-chaos-warrior.h \
+       mind/mind-elementalist.cpp mind/mind-elementalist.h \
        mind/mind-explanations-table.cpp mind/mind-explanations-table.h \
        mind/mind-force-trainer.cpp mind/mind-force-trainer.h \
        mind/mind-info.cpp mind/mind-info.h \
@@ -525,6 +526,7 @@ hengband_SOURCES = \
        mspell/mspell-status.cpp mspell/mspell-status.h \
        mspell/mspell-particularity.cpp mspell/mspell-particularity.h \
        mspell/mspell-selector.cpp mspell/mspell-selector.h \
+       mspell/mspell.h \
        mspell/smart-mspell-util.cpp mspell/smart-mspell-util.h \
        mspell/specified-summon.cpp mspell/specified-summon.h \
        mspell/summon-checker.cpp mspell/summon-checker.h \
@@ -711,7 +713,6 @@ hengband_SOURCES = \
        room/rooms-trap.cpp room/rooms-trap.h \
        room/rooms-vault.cpp room/rooms-vault.h \
        room/space-finder.cpp room/space-finder.h \
-       room/pit-nest-kinds-table.cpp room/pit-nest-kinds-table.h \
        room/treasure-deployment.cpp room/treasure-deployment.h \
        room/vault-builder.cpp room/vault-builder.h \
        \
@@ -858,6 +859,7 @@ hengband_SOURCES = \
        util/flag-group.h \
        util/int-char-converter.h \
        util/object-sort.cpp util/object-sort.h \
+       util/probability-table.h \
        util/quarks.cpp util/quarks.h \
        util/sort.cpp util/sort.h \
        util/string-processor.cpp util/string-processor.h \
@@ -918,19 +920,28 @@ hengband_SOURCES = \
        world/world-turn-processor.cpp world/world-turn-processor.h
 
 EXTRA_hengband_SOURCES = \
-       angband.ico angband.rc ang_eng.rc maid-x11.cpp main-win.cpp \
+       angband.ico angband.rc ang_eng.rc ang_jp.rc maid-x11.cpp main-win.cpp \
+       main-win/main-win-bg.cpp main-win/main-win-bg.h \
+       main-win/main-win-define.h \
+       main-win/main-win-file-utils.cpp main-win/main-win-file-utils.h \
+       main-win/main-win-mci.cpp main-win/main-win-mci.h \
+       main-win/main-win-mmsystem.h \
+       main-win/main-win-music.cpp main-win/main-win-music.h \
+       main-win/main-win-sound.cpp main-win/main-win-sound.h \
+       main-win/main-win-tokenizer.cpp main-win/main-win-tokenizer.h \
+       main-win/main-win-windows.h \
        makefile.bcc makefile.std term/readdib.cpp term/readdib.h wall.bmp
 
 EXTRA_DIST = \
        gcc-wrap
 
 DEFAULT_INCLUDES = -I$(srcdir) -I$(top_builddir)/src
-CFLAGS += $(XFT_CFLAGS) $(libcurl_CFLAGS)
+CPPFLAGS += $(XFT_CFLAGS) $(libcurl_CFLAGS)
 LIBS += $(XFT_LIBS) $(libcurl_LIBS)
 COMPILE = $(srcdir)/gcc-wrap $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
        $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
 CXXCOMPILE = $(srcdir)/gcc-wrap $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
-       $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+       $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CXXFLAGS)
 
 install-exec-hook:
 if SET_GID
index a14cb4a..7190522 100644 (file)
@@ -139,7 +139,7 @@ static bool activate_artifact(player_type *user_ptr, object_type *o_ptr)
         return FALSE;
     }
 
-    if (!switch_activation(user_ptr, o_ptr, act_ptr, name))
+    if (!switch_activation(user_ptr, &o_ptr, act_ptr, name))
         return FALSE;
 
     if (act_ptr->timeout.constant >= 0) {
index 2bdabeb..b20ca4b 100644 (file)
@@ -52,6 +52,8 @@ ANGBAND MENU
                        MENUITEM "Term-7 window", 227
                }
 
+               MENUITEM "&Always display subwindows", 280
+
                MENUITEM SEPARATOR
 /*
                POPUP "Bizarre Display"
@@ -125,6 +127,7 @@ ANGBAND MENU
                        MENUITEM "&Bigtile mode", 409
                }
 
+               MENUITEM "&Music", 411
                MENUITEM "&Sound", 410
                MENUITEM "&Back ground pict", 440
                MENUITEM "Choose BG pict", 441
@@ -135,6 +138,7 @@ ANGBAND MENU
        POPUP "&Help"
        {
                MENUITEM "&Contents", 901
+               MENUITEM "&Spoilers", 902
        }
 */
 }
diff --git a/src/ang_jp.rc b/src/ang_jp.rc
new file mode 100644 (file)
index 0000000..0d9e6f9
--- /dev/null
@@ -0,0 +1,153 @@
+/* File: angband.rc */
+
+ANGBAND MENU
+{
+       POPUP "\83t\83@\83C\83\8b(&F)"
+       {
+               MENUITEM "\90V\8bK(&N)", 100
+               MENUITEM "\8aJ\82­(&O)...", 101
+               MENUITEM SEPARATOR
+               MENUITEM "\95Û\91¶(&S)", 110
+               MENUITEM SEPARATOR
+               MENUITEM "\83X\83R\83A(&C)", 120
+               MENUITEM "\83\80\81[\83r\81[(&M)", 121
+               MENUITEM SEPARATOR  
+               MENUITEM "\8fI\97¹(&X)", 130
+       }
+
+       POPUP "\83E\83B\83\93\83h\83E(&W)"
+       {
+               POPUP "\95\\8e¦(&V)"
+               {
+                       MENUITEM "\83\81\83C\83\93\83E\83B\83\93\83h\83E", 200
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 1", 201
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 2", 202
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 3", 203
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 4", 204
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 5", 205
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 6", 206
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 7", 207
+               }
+
+               POPUP "\83t\83H\83\93\83g(&F)"
+               {
+                       MENUITEM "\83\81\83C\83\93\83E\83B\83\93\83h\83E", 210
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 1", 211
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 2", 212
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 3", 213
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 4", 214
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 5", 215
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 6", 216
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 7", 217
+               }
+
+               POPUP "\91O\96Ê\82É\8cÅ\92è"
+               {
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 1", 221
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 2", 222
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 3", 223
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 4", 224
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 5", 225
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 6", 226
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 7", 227
+               }
+
+               MENUITEM "\8fí\82É\83T\83u\83E\83B\83\93\83h\83E\82ð\95\\8e¦(&K)", 280
+
+               MENUITEM SEPARATOR
+
+               /*  POPUP "Bizarre Display"
+               {
+                       MENUITEM "\83\81\83C\83\93\83E\83B\83\93\83h\83E", 230
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 1", 231
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 2", 232
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 3", 233
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 4", 234
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 5", 235
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 6", 236
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 7", 237
+               }
+               */
+
+               POPUP "\83^\83C\83\8b\82Ì\95\9d\82ð\8dL\82°\82é"
+               {
+                       MENUITEM "\83\81\83C\83\93\83E\83B\83\93\83h\83E", 240
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 1", 241
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 2", 242
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 3", 243
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 4", 244
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 5", 245
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 6", 246
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 7", 247
+               }
+
+               POPUP "\83^\83C\83\8b\82Ì\95\9d\82ð\8fk\82ß\82é"
+               {
+                       MENUITEM "\83\81\83C\83\93\83E\83B\83\93\83h\83E", 250
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 1", 251
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 2", 252
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 3", 253
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 4", 254
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 5", 255
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 6", 256
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 7", 257
+               }
+
+               POPUP "\83^\83C\83\8b\82Ì\8d\82\82³\82ð\8dL\82°\82é"
+               {
+                       MENUITEM "\83\81\83C\83\93\83E\83B\83\93\83h\83E", 260
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 1", 261
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 2", 262
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 3", 263
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 4", 264
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 5", 265
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 6", 266
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 7", 267
+               }
+
+               POPUP "\83^\83C\83\8b\82Ì\8d\82\82³\82ð\8fk\82ß\82é"
+               {
+                       MENUITEM "\83\81\83C\83\93\83E\83B\83\93\83h\83E", 270
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 1", 271
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 2", 272
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 3", 273
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 4", 274
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 5", 275
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 6", 276
+                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 7", 277
+               }
+       }
+
+       POPUP "\83I\83v\83V\83\87\83\93(&O)"
+       {
+               POPUP "\83O\83\89\83t\83B\83N\83X(&G)"
+               {
+                       MENUITEM "\82È\82µ(&N)", 400
+                       MENUITEM "\8b\8c\83^\83C\83\8b(&O)", 401
+                       MENUITEM "&Adam Bolt\82³\82ñ\83^\83C\83\8b", 402
+                       MENUITEM "\90V\83^\83C\83\8b", 403
+                       MENUITEM "\83^\83C\83\8b\82ð2\94{\95\9d\82Å\95\\8e¦", 409
+               }
+               MENUITEM "BGM(&M)", 411
+               MENUITEM "\8cø\89Ê\89¹(&S)", 410
+               MENUITEM "\95Ç\8e\86\82ð\8eg\82¤(&B)", 440
+               MENUITEM "\95Ç\8e\86\82ð\91I\91ð(&O)...", 441
+               MENUITEM "\89æ\96Ê\82ðHTML\82Å\95Û\91¶(&H)...", 450
+               /*  MENUITEM "\92n\90}(&M)", 430 */
+               /*  MENUITEM SEPARATOR
+               MENUITEM "\96¢\8eg\97p", 410
+               MENUITEM "\96¢\8eg\97p", 411*/
+       }
+
+       /*
+       POPUP "\83w\83\8b\83v(&H)"
+       {
+               MENUITEM "\88ê\94Ê(&G)",      901
+               MENUITEM "\83X\83|\83C\83\89\81[(&S)", 902
+       }
+       */
+}
+
+ANGBAND ICON "angband.ico"
+
+ANGBAND BITMAP "wall.bmp"
index bedfb6b..b68ed94 100644 (file)
@@ -1,153 +1,6 @@
 /* File: angband.rc */
-
-ANGBAND MENU
-{
-       POPUP "\83t\83@\83C\83\8b(&F)"
-       {
-               MENUITEM "\90V\8bK(&N)", 100
-               MENUITEM "\8aJ\82­(&O)...", 101
-               MENUITEM SEPARATOR
-               MENUITEM "\95Û\91¶(&S)", 110
-               MENUITEM SEPARATOR
-               MENUITEM "\83X\83R\83A(&C)", 120
-               MENUITEM "\83\80\81[\83r\81[(&M)", 121
-               MENUITEM SEPARATOR  
-               MENUITEM "\8fI\97¹(&X)", 130
-       }
-
-       POPUP "\83E\83B\83\93\83h\83E(&W)"
-       {
-               POPUP "\95\\8e¦(&V)"
-               {
-                       MENUITEM "\83\81\83C\83\93\83E\83B\83\93\83h\83E", 200
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 1", 201
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 2", 202
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 3", 203
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 4", 204
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 5", 205
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 6", 206
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 7", 207
-               }
-
-               POPUP "\83t\83H\83\93\83g(&F)"
-               {
-                       MENUITEM "\83\81\83C\83\93\83E\83B\83\93\83h\83E", 210
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 1", 211
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 2", 212
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 3", 213
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 4", 214
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 5", 215
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 6", 216
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 7", 217
-               }
-
-               POPUP "\91O\96Ê\82É\8cÅ\92è"
-               {
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 1", 221
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 2", 222
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 3", 223
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 4", 224
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 5", 225
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 6", 226
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 7", 227
-               }
-
-               MENUITEM "\8fí\82É\83T\83u\83E\83B\83\93\83h\83E\82ð\95\\8e¦(&K)", 280
-
-               MENUITEM SEPARATOR
-
-               /*  POPUP "Bizarre Display"
-               {
-                       MENUITEM "\83\81\83C\83\93\83E\83B\83\93\83h\83E", 230
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 1", 231
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 2", 232
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 3", 233
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 4", 234
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 5", 235
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 6", 236
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 7", 237
-               }
-               */
-
-               POPUP "\83^\83C\83\8b\82Ì\95\9d\82ð\8dL\82°\82é"
-               {
-                       MENUITEM "\83\81\83C\83\93\83E\83B\83\93\83h\83E", 240
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 1", 241
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 2", 242
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 3", 243
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 4", 244
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 5", 245
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 6", 246
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 7", 247
-               }
-
-               POPUP "\83^\83C\83\8b\82Ì\95\9d\82ð\8fk\82ß\82é"
-               {
-                       MENUITEM "\83\81\83C\83\93\83E\83B\83\93\83h\83E", 250
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 1", 251
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 2", 252
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 3", 253
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 4", 254
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 5", 255
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 6", 256
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 7", 257
-               }
-
-               POPUP "\83^\83C\83\8b\82Ì\8d\82\82³\82ð\8dL\82°\82é"
-               {
-                       MENUITEM "\83\81\83C\83\93\83E\83B\83\93\83h\83E", 260
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 1", 261
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 2", 262
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 3", 263
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 4", 264
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 5", 265
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 6", 266
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 7", 267
-               }
-
-               POPUP "\83^\83C\83\8b\82Ì\8d\82\82³\82ð\8fk\82ß\82é"
-               {
-                       MENUITEM "\83\81\83C\83\93\83E\83B\83\93\83h\83E", 270
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 1", 271
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 2", 272
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 3", 273
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 4", 274
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 5", 275
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 6", 276
-                       MENUITEM "\83T\83u\83E\83B\83\93\83h\83E 7", 277
-               }
-       }
-
-       POPUP "\83I\83v\83V\83\87\83\93(&O)"
-       {
-               POPUP "\83O\83\89\83t\83B\83N\83X(&G)"
-               {
-                       MENUITEM "\82È\82µ(&N)", 400
-                       MENUITEM "\8b\8c\83^\83C\83\8b(&O)", 401
-                       MENUITEM "&Adam Bolt\82³\82ñ\83^\83C\83\8b", 402
-                       MENUITEM "\90V\83^\83C\83\8b", 403
-                       MENUITEM "\83^\83C\83\8b\82ð2\94{\95\9d\82Å\95\\8e¦", 409
-               }
-               MENUITEM "BGM(&M)", 411
-               MENUITEM "\8cø\89Ê\89¹(&S)", 410
-               MENUITEM "\95Ç\8e\86\82ð\8eg\82¤(&B)", 440
-               MENUITEM "\95Ç\8e\86\82ð\91I\91ð(&O)...", 441
-               MENUITEM "\89æ\96Ê\82ðHTML\82Å\95Û\91¶(&H)...", 450
-               /*  MENUITEM "\92n\90}(&M)", 430 */
-               /*  MENUITEM SEPARATOR
-               MENUITEM "\96¢\8eg\97p", 410
-               MENUITEM "\96¢\8eg\97p", 411*/
-       }
-
-       /*
-       POPUP "\83w\83\8b\83v(&H)"
-       {
-               MENUITEM "\88ê\94Ê(&G)",       901
-               MENUITEM "\83X\83|\83C\83\89\81[(&S)", 902
-       }
-       */
-}
-
-ANGBAND ICON "angband.ico"
-
-ANGBAND BITMAP "wall.bmp"
+#ifdef JP
+#include "ang_jp.rc"
+#else
+#include "ang_eng.rc"
+#endif
index 72038c8..1a18479 100644 (file)
@@ -87,8 +87,7 @@ void curse_artifact(player_type *player_ptr, object_type *o_ptr)
 static concptr get_random_art_filename(const bool armour, const int power)
 {
     concptr filename;
-    switch (armour) {
-    case 1:
+    if (armour) {
         switch (power) {
         case 0:
             filename = _("a_cursed_j.txt", "a_cursed.txt");
@@ -102,8 +101,7 @@ static concptr get_random_art_filename(const bool armour, const int power)
         default:
             filename = _("a_high_j.txt", "a_high.txt");
         }
-        break;
-    default:
+    } else {
         switch (power) {
         case 0:
             filename = _("w_cursed_j.txt", "w_cursed.txt");
index dddb191..5fafff2 100644 (file)
@@ -141,7 +141,12 @@ static void set_artifact_bias(player_type *player_ptr, object_type *o_ptr, int *
             o_ptr->artifact_bias = BIAS_MAGE;
         else
             o_ptr->artifact_bias = BIAS_ROGUE;
-        
+        break;
+    case CLASS_ELEMENTALIST:
+        o_ptr->artifact_bias = one_in_(2) ? BIAS_MAGE : BIAS_INT;
+        break;
+
+    case MAX_CLASS:
         break;
     }
 }
index 5ae984e..52e15c2 100644 (file)
@@ -1,4 +1,6 @@
-#include "autopick/autopick-editor-util.h"
+#include <cstdlib>
+
+#include "autopick/autopick-editor-util.h"
 #include "autopick/autopick-flags-table.h"
 #include "autopick/autopick-methods-table.h"
 #include "autopick/autopick-dirty-flags.h"
@@ -206,9 +208,8 @@ bool add_empty_line(text_body_type *tb)
 
 static chain_str_type *new_chain_str(concptr str)
 {
-       chain_str_type *chain;
        size_t len = strlen(str);
-       chain = (chain_str_type *)ralloc(sizeof(chain_str_type) + len * sizeof(char));
+       auto *chain = static_cast<chain_str_type *>(std::malloc(sizeof(chain_str_type) + len * sizeof(char)));
        strcpy(chain->s, str);
        chain->next = NULL;
        return chain;
@@ -224,9 +225,8 @@ void kill_yank_chain(text_body_type *tb)
        while (chain)
        {
                chain_str_type *next = chain->next;
-               size_t len = strlen(chain->s);
 
-               rnfree(chain, sizeof(chain_str_type) + len * sizeof(char));
+               std::free(chain);
 
                chain = next;
        }
index 9dac736..623beef 100644 (file)
@@ -46,7 +46,7 @@ void free_text_lines(concptr *lines_list)
        }
 
        /* free list of pointers */
-       C_FREE((vptr)lines_list, MAX_LINES, concptr);
+       C_FREE(lines_list, MAX_LINES, concptr);
 }
 
 
index 80cf020..0917e95 100644 (file)
@@ -203,6 +203,9 @@ concptr class_explanations[MAX_CLASS] =
 
     _("スナイパーは一撃必殺を狙う恐るべき射手です。精神を高めることにより、射撃の威力と精度を高めます。また、魔法を使うことはできませんが、研ぎ澄まされた精神から繰り出される射撃術はさらなる威力をもたらすことでしょう。テクニックが必要とされる職業です。",
       "Snipers are good at shooting, and they can kill targets by a few shots. After they concentrate deeply, they can demonstrate their shooting talents. You can see the incredible firepower of their shots."),
+
+    _("元素使いはエレメンタルの力を駆使して戦う魔法使いです。扱える属性は限られますが、その力を十二分に引き出すことができます。魔法に必要な能力は賢さです。",
+      "An Elementalist is a spell caster that specializes in tapping elemental forces.  They have a limited, but powerful, repertoire of spells.  Wisdom determines an Elementalist's spell casting ability.")
 };
 
 /*! 性格の解説メッセージテーブル */
index 1d34e2c..7a73dbb 100644 (file)
@@ -3,6 +3,7 @@
 #include "birth/birth-util.h"
 #include "core/asking-player.h"
 #include "io/input-key-acceptor.h"
+#include "mind/mind-elementalist.h"
 #include "player/player-realm.h"
 #include "term/screen-processor.h"
 #include "term/term-color-types.h"
@@ -314,31 +315,37 @@ bool get_player_realms(player_type *creature_ptr)
     /* Select the first realm */
     creature_ptr->realm1 = REALM_NONE;
     creature_ptr->realm2 = REALM_SELECT_CANCEL;
-    while (TRUE) {
-        char temp[80 * 10];
-        int count = 0;
-        creature_ptr->realm1 = select_realm(creature_ptr, realm_choices1[creature_ptr->pclass], &count);
 
+    if (creature_ptr->pclass == CLASS_ELEMENTALIST) {
+        creature_ptr->realm1 = select_element_realm(creature_ptr);
         if (creature_ptr->realm1 == REALM_SELECT_CANCEL)
             return FALSE;
-        if (!creature_ptr->realm1)
-            break;
-
-        cleanup_realm_selection_window();
-        shape_buffer(realm_explanations[technic2magic(creature_ptr->realm1) - 1], 74, temp, sizeof(temp));
-        concptr t = temp;
-        for (int i = 0; i < 10; i++) {
-            if (t[0] == 0)
+    }
+    else
+        while (TRUE) {
+            char temp[80 * 10];
+            int count = 0;
+            creature_ptr->realm1 = select_realm(creature_ptr, realm_choices1[creature_ptr->pclass], &count);
+            if (creature_ptr->realm1 == REALM_SELECT_CANCEL)
+                return FALSE;
+            if (!creature_ptr->realm1)
                 break;
-            else {
-                prt(t, 12 + i, 3);
-                t += strlen(t) + 1;
+
+            cleanup_realm_selection_window();
+            shape_buffer(realm_explanations[technic2magic(creature_ptr->realm1) - 1], 74, temp, sizeof(temp));
+            concptr t = temp;
+            for (int i = 0; i < 10; i++) {
+                if (t[0] == 0)
+                    break;
+                else {
+                    prt(t, 12 + i, 3);
+                    t += strlen(t) + 1;
+                }
             }
-        }
 
-        if (check_realm_selection(creature_ptr, count))
-            break;
-    }
+            if (check_realm_selection(creature_ptr, count))
+                break;
+        }
 
     /* Select the second realm */
     creature_ptr->realm2 = REALM_NONE;
@@ -347,7 +354,10 @@ bool get_player_realms(player_type *creature_ptr)
 
     /* Print the realm */
     put_str(_("魔法        :", "Magic       :"), 6, 1);
-    c_put_str(TERM_L_BLUE, realm_names[creature_ptr->realm1], 6, 15);
+    if (creature_ptr->pclass == CLASS_ELEMENTALIST)
+        c_put_str(TERM_L_BLUE, get_element_title(creature_ptr->realm1), 6, 15);
+    else
+        c_put_str(TERM_L_BLUE, realm_names[creature_ptr->realm1], 6, 15);
 
     /* Select the second realm */
     while (TRUE) {
index 06e7776..0dc2e01 100644 (file)
@@ -86,18 +86,29 @@ void get_stats(player_type* creature_ptr)
 }
 
 /*!
+ * @brief 経験値修正の合計値を計算
+ * @return なし
+ */
+u16b get_expfact(player_type *creature_ptr)
+{
+    u16b expfact = rp_ptr->r_exp;
+
+    if (creature_ptr->prace != RACE_ANDROID)
+        expfact += cp_ptr->c_exp;
+    if (((creature_ptr->pclass == CLASS_MONK) || (creature_ptr->pclass == CLASS_FORCETRAINER) || (creature_ptr->pclass == CLASS_NINJA))
+        && ((creature_ptr->prace == RACE_KLACKON) || (creature_ptr->prace == RACE_SPRITE)))
+        expfact -= 15;
+
+    return expfact;
+}
+
+/*!
  * @brief その他「オートローラ中は算出の対象にしない」副次ステータスを処理する / Roll for some info that the auto-roller ignores
  * @return なし
  */
 void get_extra(player_type* creature_ptr, bool roll_hitdie)
 {
-    if (creature_ptr->prace == RACE_ANDROID)
-        creature_ptr->expfact = rp_ptr->r_exp;
-    else
-        creature_ptr->expfact = rp_ptr->r_exp + cp_ptr->c_exp;
-
-    if (((creature_ptr->pclass == CLASS_MONK) || (creature_ptr->pclass == CLASS_FORCETRAINER) || (creature_ptr->pclass == CLASS_NINJA)) && ((creature_ptr->prace == RACE_KLACKON) || (creature_ptr->prace == RACE_SPRITE)))
-        creature_ptr->expfact -= 15;
+    creature_ptr->expfact = get_expfact(creature_ptr);
 
     /* Reset record of race/realm changes */
     creature_ptr->start_race = creature_ptr->prace;
index 12d46e2..6d0393b 100644 (file)
@@ -4,6 +4,7 @@
 
 int adjust_stat(int value, int amount);
 void get_stats(player_type* creature_ptr);
-void get_extra(player_type* creature_ptr, bool roll_hitdie);
+u16b get_expfact(player_type *creature_ptr);
+void get_extra(player_type *creature_ptr, bool roll_hitdie);
 
 void get_max_stats(player_type* creature_ptr);
index 0287dcc..c14f5c0 100644 (file)
@@ -267,6 +267,7 @@ static bool let_player_build_character(player_type *creature_ptr)
 
 static void display_initial_options(player_type *creature_ptr)
 {
+    u16b expfact = get_expfact(creature_ptr) - 100;
     s16b adj[A_MAX];
     for (int i = 0; i < A_MAX; i++) {
         adj[i] = rp_ptr->r_adj[i] + cp_ptr->c_adj[i] + ap_ptr->a_adj[i];
@@ -276,7 +277,7 @@ static void display_initial_options(player_type *creature_ptr)
     put_str("                                   ", 3, 40);
     put_str(_("修正の合計値", "Your total modification"), 3, 40);
     put_str(_("腕力 知能 賢さ 器用 耐久 魅力 経験 ", "Str  Int  Wis  Dex  Con  Chr   EXP "), 4, 40);
-    sprintf(buf, "%+3d  %+3d  %+3d  %+3d  %+3d  %+3d %+4d%% ", adj[0], adj[1], adj[2], adj[3], adj[4], adj[5], cp_ptr->c_exp);
+    sprintf(buf, "%+3d  %+3d  %+3d  %+3d  %+3d  %+3d %+4d%% ", adj[0], adj[1], adj[2], adj[3], adj[4], adj[5], expfact);
     c_put_str(TERM_L_BLUE, buf, 5, 40);
 
     put_str("HD ", 6, 40);
index e821c19..55f3416 100644 (file)
@@ -50,7 +50,7 @@ void player_wipe_without_name(player_type *creature_ptr)
         string_free(creature_ptr->last_message);
 
     if (creature_ptr->inventory_list != NULL)
-        C_WIPE(creature_ptr->inventory_list, INVEN_TOTAL, object_type);
+        C_KILL(creature_ptr->inventory_list, INVEN_TOTAL, object_type);
 
     (void)WIPE(creature_ptr, player_type);
 
@@ -218,7 +218,7 @@ void init_dungeon_quests(player_type *creature_ptr)
  * @param creature_ptr プレーヤーへの参照ポインタ
  * @return なし
  * @details アンデッド系種族は開始時刻を夜からにする / Undead start just sunset
- * @details        
+ * @details
  */
 void init_turn(player_type *creature_ptr)
 {
index a3d0393..3ab6d4b 100644 (file)
@@ -171,4 +171,10 @@ byte player_init[MAX_CLASS][3][2] = {
         { TV_SOFT_ARMOR, SV_SOFT_LEATHER_ARMOR },
         { TV_SWORD, SV_DAGGER }
     },
+
+    { /* Elementalist */
+        { TV_POTION, SV_POTION_SPEED },
+        { TV_RING, SV_RING_SUSTAIN_WIS },
+        { TV_SWORD, SV_DAGGER }
+    },
 };
index 84d819a..d26373e 100644 (file)
@@ -180,7 +180,7 @@ void player_outfit(player_type *creature_ptr)
         object_prep(creature_ptr, q_ptr, lookup_kind(TV_ARROW, SV_AMMO_NORMAL));
         q_ptr->number = (ITEM_NUMBER)rand_range(15, 20);
         add_outfit(creature_ptr, q_ptr);
-    } else if (creature_ptr->pclass == CLASS_HIGH_MAGE) {
+    } else if (creature_ptr->pclass == CLASS_HIGH_MAGE || creature_ptr->pclass == CLASS_ELEMENTALIST) {
         object_prep(creature_ptr, q_ptr, lookup_kind(TV_WAND, SV_WAND_MAGIC_MISSILE));
         q_ptr->number = 1;
         q_ptr->pval = (PARAMETER_VALUE)rand_range(25, 30);
index 379a463..a14cb67 100644 (file)
 #include "view/display-messages.h"
 
 /*!
- * @brief モンスター特技がラーニング可能かどうかを返す
- * @param ms_type モンスター特技ID
- */
-bool monster_spell_is_learnable(const int monspell)
-{
-    switch (monspell) {
-    case MS_DISPEL:
-    case MS_ROCKET:
-    case MS_SHOOT:
-    case MS_BR_ACID:
-    case MS_BR_ELEC:
-    case MS_BR_FIRE:
-    case MS_BR_COLD:
-    case MS_BR_POIS:
-    case MS_BR_NETHER:
-    case MS_BR_LITE:
-    case MS_BR_DARK:
-    case MS_BR_CONF:
-    case MS_BR_SOUND:
-    case MS_BR_CHAOS:
-    case MS_BR_DISEN:
-    case MS_BR_NEXUS:
-    case MS_BR_TIME:
-    case MS_BR_INERTIA:
-    case MS_BR_GRAVITY:
-    case MS_BR_SHARDS:
-    case MS_BR_PLASMA:
-    case MS_BR_FORCE:
-    case MS_BR_MANA:
-    case MS_BALL_NUKE:
-    case MS_BR_NUKE:
-    case MS_BALL_CHAOS:
-    case MS_BR_DISI:
-    case MS_BALL_ACID:
-    case MS_BALL_ELEC:
-    case MS_BALL_FIRE:
-    case MS_BALL_COLD:
-    case MS_BALL_POIS:
-    case MS_BALL_NETHER:
-    case MS_BALL_WATER:
-    case MS_BALL_MANA:
-    case MS_BALL_DARK:
-    case MS_DRAIN_MANA:
-    case MS_MIND_BLAST:
-    case MS_BRAIN_SMASH:
-    case MS_CAUSE_1:
-    case MS_CAUSE_2:
-    case MS_CAUSE_3:
-    case MS_CAUSE_4:
-    case MS_BOLT_ACID:
-    case MS_BOLT_ELEC:
-    case MS_BOLT_FIRE:
-    case MS_BOLT_COLD:
-    case MS_STARBURST:
-    case MS_BOLT_NETHER:
-    case MS_BOLT_WATER:
-    case MS_BOLT_MANA:
-    case MS_BOLT_PLASMA:
-    case MS_BOLT_ICE:
-    case MS_MAGIC_MISSILE:
-    case MS_SCARE:
-    case MS_BLIND:
-    case MS_CONF:
-    case MS_SLOW:
-    case MS_SLEEP:
-    case MS_HAND_DOOM:
-    case MS_TELE_TO:
-    case MS_TELE_AWAY:
-    case MS_PSY_SPEAR:
-    case MS_DARKNESS:
-    case MS_MAKE_TRAP:
-    case MS_FORGET:
-    case MS_S_KIN:
-    case MS_S_CYBER:
-    case MS_S_MONSTER:
-    case MS_S_MONSTERS:
-    case MS_S_ANT:
-    case MS_S_SPIDER:
-    case MS_S_HOUND:
-    case MS_S_HYDRA:
-    case MS_S_ANGEL:
-    case MS_S_DEMON:
-    case MS_S_UNDEAD:
-    case MS_S_DRAGON:
-    case MS_S_HI_UNDEAD:
-    case MS_S_HI_DRAGON:
-    case MS_S_AMBERITE:
-    case MS_S_UNIQUE:
-        return TRUE;
-    default:
-        return FALSE;
-    }
-}
-
-/*!
  * @brief 青魔法のラーニング判定と成功した場合のラーニング処理
  * @param monspell ラーニングを試みるモンスター攻撃のID
  * @return なし
index ee09f0e..59387a8 100644 (file)
@@ -4,6 +4,5 @@
 
 enum blue_magic_type : int;
 
-bool monster_spell_is_learnable(int monspell);
 void learn_spell(player_type *learner_ptr, int monspell);
 void set_rf_masks(BIT_FLAGS *f4, BIT_FLAGS *f5, BIT_FLAGS *f6, blue_magic_type mode);
index 5e85540..4e8a813 100644 (file)
@@ -174,6 +174,7 @@ void do_cmd_suicide(player_type *creature_ptr)
     creature_ptr->is_dead = TRUE;
     creature_ptr->leaving = TRUE;
     if (!current_world_ptr->total_winner) {
+        play_music(TERM_XTRA_MUSIC_BASIC, MUSIC_BASIC_GAMEOVER);
         exe_write_diary(creature_ptr, DIARY_DESCRIPTION, 0, _("ダンジョンの探索に絶望して自殺した。", "gave up all hope to commit suicide."));
         exe_write_diary(creature_ptr, DIARY_GAMESTART, 1, _("-------- ゲームオーバー --------", "--------   Game  Over   --------"));
         exe_write_diary(creature_ptr, DIARY_DESCRIPTION, 1, "\n\n\n\n");
index c8d6151..a8b6ef0 100644 (file)
@@ -531,6 +531,9 @@ void do_cmd_pet(player_type *creature_ptr)
                     powers[num++] = PET_TWO_HANDS;
                 }
                 break;
+
+            default:
+                break;
             }
         }
     }
index f6f1cce..c786cff 100644 (file)
@@ -74,7 +74,6 @@ PRICE kakekin;
 int sel_monster;
 
 bool reinit_wilderness = FALSE;
-MONSTER_IDX today_mon;
 
 /*!
  * @brief 町に関するヘルプを表示する / Display town history
index 6e95a5c..f096767 100644 (file)
@@ -1,11 +1,13 @@
 #pragma once
 
-#include "system/angband.h"
 #include "object/tval-types.h"
+#include "system/angband.h"
 
+// clang-format off
 #define BUILDING_NON_MEMBER 0 /*!< 不明(現在未使用) */
 #define BUILDING_MEMBER     1 /*!< 不明(現在未使用) */
 #define BUILDING_OWNER      2 /*!< 施設の種族/職業条件が一致している状態値 */
+// clang-format on
 
 /*
  * Arena constants
@@ -13,7 +15,6 @@
 #define ARENA_DEFEATED_OLD_VER (-(MAX_SHORT)) /*<! 旧バージョンの闘技場敗北定義 */
 
 extern bool reinit_wilderness;
-extern MONRACE_IDX today_mon;
 
 extern u32b mon_odds[4];
 extern int battle_odds;
index af91014..3ad8575 100644 (file)
@@ -49,6 +49,7 @@ static bool is_healthy_stay(player_type *customer_ptr)
 }
 
 
+#ifdef JP
 static bool is_player_undead(player_type *customer_ptr)
 {
        bool is_player_undead = is_specific_player_race(customer_ptr, RACE_SKELETON) ||
@@ -57,6 +58,7 @@ static bool is_player_undead(player_type *customer_ptr)
                is_specific_player_race(customer_ptr, RACE_SPECTRE);
        return is_player_undead;
 }
+#endif
 
 
 /*!
index ddd1f18..49cc7ca 100644 (file)
@@ -79,7 +79,7 @@ void do_cmd_query_symbol(player_type *creature_ptr)
             temp[0] = 0;
             return;
         }
-        sprintf(buf, _("名前:%sにマッチ", "Monsters with a name \"%s\""), temp);
+        sprintf(buf, _("名前:%sにマッチ", "Monsters' names with \"%s\""), temp);
     } else if (ident_info[i]) {
         sprintf(buf, "%c - %s.", sym, ident_info[i] + 2);
     } else {
index 00097a2..897ad85 100644 (file)
@@ -122,6 +122,8 @@ errr macro_add(concptr pat, concptr act)
 {
     if (!pat || !act)
         return -1;
+    if (strlen(pat) == 0 || strlen(act) == 0)
+        return -1;
 
     int n = macro_find_exact(pat);
     if (n >= 0) {
index 95bc698..95e4f3e 100644 (file)
 #include "view/object-describer.h"
 
 /*!
- * @brief 食料を食べるコマンドのサブルーチン
- * @param item 食べるオブジェクトの所持品ID
- * @return なし
+ * @brief 食料タイプの食料を食べたときの効果を発動
+ * @param creature_ptr プレイヤー情報への参照ポインタ
+ * @param o_ptr 食べるオブジェクト
+ * @return 鑑定されるならTRUE、されないならFALSE
  */
-void exe_eat_food(player_type *creature_ptr, INVENTORY_IDX item)
+bool exe_eat_food_type_object(player_type *creature_ptr, object_type *o_ptr)
 {
-    int ident, lev;
-    object_type *o_ptr;
-
-    if (music_singing_any(creature_ptr))
-        stop_singing(creature_ptr);
-    if (hex_spelling_any(creature_ptr))
-        stop_hex_spell_all(creature_ptr);
-    o_ptr = ref_item(creature_ptr, item);
-
-    sound(SOUND_EAT);
-
-    take_turn(creature_ptr, 100);
-
-    /* Identity not known yet */
-    ident = FALSE;
-
-    /* Object level */
-    lev = k_info[o_ptr->k_idx].level;
-
-    if (o_ptr->tval == TV_FOOD) {
-        /* Analyze the food */
-        switch (o_ptr->sval) {
-        case SV_FOOD_POISON: {
-            if (!(has_resist_pois(creature_ptr) || is_oppose_pois(creature_ptr))) {
-                if (set_poisoned(creature_ptr, creature_ptr->poisoned + randint0(10) + 10)) {
-                    ident = TRUE;
-                }
-            }
-            break;
-        }
-
-        case SV_FOOD_BLINDNESS: {
-            if (!has_resist_blind(creature_ptr)) {
-                if (set_blind(creature_ptr, creature_ptr->blind + randint0(200) + 200)) {
-                    ident = TRUE;
-                }
-            }
-            break;
-        }
-
-        case SV_FOOD_PARANOIA: {
-            if (!has_resist_fear(creature_ptr)) {
-                if (set_afraid(creature_ptr, creature_ptr->afraid + randint0(10) + 10)) {
-                    ident = TRUE;
-                }
-            }
-            break;
-        }
-
-        case SV_FOOD_CONFUSION: {
-            if (!has_resist_conf(creature_ptr)) {
-                if (set_confused(creature_ptr, creature_ptr->confused + randint0(10) + 10)) {
-                    ident = TRUE;
-                }
-            }
-            break;
-        }
+    if (o_ptr->tval != TV_FOOD)
+        return FALSE;
+
+    switch (o_ptr->sval) {
+    case SV_FOOD_POISON:
+        if (!(has_resist_pois(creature_ptr) || is_oppose_pois(creature_ptr)))
+            if (set_poisoned(creature_ptr, creature_ptr->poisoned + randint0(10) + 10))
+                return TRUE;
+        break;
+    case SV_FOOD_BLINDNESS:
+        if (!has_resist_blind(creature_ptr))
+            if (set_blind(creature_ptr, creature_ptr->blind + randint0(200) + 200))
+                return TRUE;
+        break;
+    case SV_FOOD_PARANOIA:
+        if (!has_resist_fear(creature_ptr))
+            if (set_afraid(creature_ptr, creature_ptr->afraid + randint0(10) + 10))
+                return TRUE;
+        break;
+    case SV_FOOD_CONFUSION:
+        if (!has_resist_conf(creature_ptr))
+            if (set_confused(creature_ptr, creature_ptr->confused + randint0(10) + 10))
+                return TRUE;
+        break;
+    case SV_FOOD_HALLUCINATION:
+        if (!has_resist_chaos(creature_ptr))
+            if (set_image(creature_ptr, creature_ptr->image + randint0(250) + 250))
+                return TRUE;
+        break;
+    case SV_FOOD_PARALYSIS:
+        if (!creature_ptr->free_act)
+            if (set_paralyzed(creature_ptr, creature_ptr->paralyzed + randint0(10) + 10))
+                return TRUE;
+        break;
+    case SV_FOOD_WEAKNESS:
+        take_hit(creature_ptr, DAMAGE_NOESCAPE, damroll(6, 6), _("毒入り食料", "poisonous food"), -1);
+        (void)do_dec_stat(creature_ptr, A_STR);
+        return TRUE;
+    case SV_FOOD_SICKNESS:
+        take_hit(creature_ptr, DAMAGE_NOESCAPE, damroll(6, 6), _("毒入り食料", "poisonous food"), -1);
+        (void)do_dec_stat(creature_ptr, A_CON);
+        return TRUE;
+    case SV_FOOD_STUPIDITY:
+        take_hit(creature_ptr, DAMAGE_NOESCAPE, damroll(8, 8), _("毒入り食料", "poisonous food"), -1);
+        (void)do_dec_stat(creature_ptr, A_INT);
+        return TRUE;
+    case SV_FOOD_NAIVETY:
+        take_hit(creature_ptr, DAMAGE_NOESCAPE, damroll(8, 8), _("毒入り食料", "poisonous food"), -1);
+        (void)do_dec_stat(creature_ptr, A_WIS);
+        return TRUE;
+    case SV_FOOD_UNHEALTH:
+        take_hit(creature_ptr, DAMAGE_NOESCAPE, damroll(10, 10), _("毒入り食料", "poisonous food"), -1);
+        (void)do_dec_stat(creature_ptr, A_CON);
+        return TRUE;
+    case SV_FOOD_DISEASE:
+        take_hit(creature_ptr, DAMAGE_NOESCAPE, damroll(10, 10), _("毒入り食料", "poisonous food"), -1);
+        (void)do_dec_stat(creature_ptr, A_STR);
+        return TRUE;
+    case SV_FOOD_CURE_POISON:
+        if (set_poisoned(creature_ptr, 0))
+            return TRUE;
+        break;
+    case SV_FOOD_CURE_BLINDNESS:
+        if (set_blind(creature_ptr, 0))
+            return TRUE;
+        break;
+    case SV_FOOD_CURE_PARANOIA:
+        if (set_afraid(creature_ptr, 0))
+            return TRUE;
+        break;
+    case SV_FOOD_CURE_CONFUSION:
+        if (set_confused(creature_ptr, 0))
+            return TRUE;
+        break;
+    case SV_FOOD_CURE_SERIOUS:
+        return cure_serious_wounds(creature_ptr, 4, 8);
+    case SV_FOOD_RESTORE_STR:
+        if (do_res_stat(creature_ptr, A_STR))
+            return TRUE;
+        break;
+    case SV_FOOD_RESTORE_CON:
+        if (do_res_stat(creature_ptr, A_CON))
+            return TRUE;
+        break;
+    case SV_FOOD_RESTORING:
+        return restore_all_status(creature_ptr);
+#ifdef JP
+    /* それぞれの食べ物の感想をオリジナルより細かく表現 */
+    case SV_FOOD_BISCUIT:
+        msg_print("甘くてサクサクしてとてもおいしい。");
+        return TRUE;
+    case SV_FOOD_JERKY:
+        msg_print("歯ごたえがあっておいしい。");
+        return TRUE;
+    case SV_FOOD_SLIME_MOLD:
+        msg_print("これはなんとも形容しがたい味だ。");
+        return TRUE;
+    case SV_FOOD_RATION:
+        msg_print("これはおいしい。");
+        return TRUE;
+#else
+    case SV_FOOD_RATION:
+    case SV_FOOD_BISCUIT:
+    case SV_FOOD_JERKY:
+    case SV_FOOD_SLIME_MOLD:
+        msg_print("That tastes good.");
+        return TRUE;
+#endif
+    case SV_FOOD_WAYBREAD:
+        msg_print(_("これはひじょうに美味だ。", "That tastes good."));
+        (void)set_poisoned(creature_ptr, 0);
+        (void)hp_player(creature_ptr, damroll(4, 8));
+        return TRUE;
+    case SV_FOOD_PINT_OF_ALE:
+    case SV_FOOD_PINT_OF_WINE:
+        msg_print(_("のどごし爽やかだ。", "That tastes good."));
+        return TRUE;
+    }
 
-        case SV_FOOD_HALLUCINATION: {
-            if (!has_resist_chaos(creature_ptr)) {
-                if (set_image(creature_ptr, creature_ptr->image + randint0(250) + 250)) {
-                    ident = TRUE;
-                }
-            }
-            break;
-        }
+    return FALSE;
+}
 
-        case SV_FOOD_PARALYSIS: {
-            if (!creature_ptr->free_act) {
-                if (set_paralyzed(creature_ptr, creature_ptr->paralyzed + randint0(10) + 10)) {
-                    ident = TRUE;
-                }
-            }
-            break;
-        }
+/*!
+ * @brief 魔法道具のチャージをの食料として食べたときの効果を発動
+ * @param creature_ptr プレイヤー情報への参照ポインタ
+ * @param o_ptr 食べるオブジェクト
+ * @param item オブジェクトのインベントリ番号
+ * @return 食べようとしたらTRUE、しなかったらFALSE
+ */
+bool exe_eat_charge_of_magic_device(player_type *creature_ptr, object_type *o_ptr, INVENTORY_IDX item)
+{
+    if (o_ptr->tval != TV_STAFF && o_ptr->tval != TV_WAND)
+        return FALSE;
 
-        case SV_FOOD_WEAKNESS: {
-            take_hit(creature_ptr, DAMAGE_NOESCAPE, damroll(6, 6), _("毒入り食料", "poisonous food"), -1);
-            (void)do_dec_stat(creature_ptr, A_STR);
-            ident = TRUE;
-            break;
-        }
+    if (is_specific_player_race(creature_ptr, RACE_SKELETON) || is_specific_player_race(creature_ptr, RACE_GOLEM)
+            || is_specific_player_race(creature_ptr, RACE_ZOMBIE) || is_specific_player_race(creature_ptr, RACE_SPECTRE)) {
+        concptr staff;
 
-        case SV_FOOD_SICKNESS: {
-            take_hit(creature_ptr, DAMAGE_NOESCAPE, damroll(6, 6), _("毒入り食料", "poisonous food"), -1);
-            (void)do_dec_stat(creature_ptr, A_CON);
-            ident = TRUE;
-            break;
+        if (o_ptr->tval == TV_STAFF && (item < 0) && (o_ptr->number > 1)) {
+            msg_print(_("まずは杖を拾わなければ。", "You must first pick up the staffs."));
+            return TRUE;
         }
 
-        case SV_FOOD_STUPIDITY: {
-            take_hit(creature_ptr, DAMAGE_NOESCAPE, damroll(8, 8), _("毒入り食料", "poisonous food"), -1);
-            (void)do_dec_stat(creature_ptr, A_INT);
-            ident = TRUE;
-            break;
-        }
+        staff = (o_ptr->tval == TV_STAFF) ? _("杖", "staff") : _("魔法棒", "wand");
 
-        case SV_FOOD_NAIVETY: {
-            take_hit(creature_ptr, DAMAGE_NOESCAPE, damroll(8, 8), _("毒入り食料", "poisonous food"), -1);
-            (void)do_dec_stat(creature_ptr, A_WIS);
-            ident = TRUE;
-            break;
+        /* "Eat" charges */
+        if (o_ptr->pval == 0) {
+            msg_format(_("この%sにはもう魔力が残っていない。", "The %s has no charges left."), staff);
+            o_ptr->ident |= (IDENT_EMPTY);
+            creature_ptr->window_flags |= (PW_INVEN);
+            return TRUE;
         }
 
-        case SV_FOOD_UNHEALTH: {
-            take_hit(creature_ptr, DAMAGE_NOESCAPE, damroll(10, 10), _("毒入り食料", "poisonous food"), -1);
-            (void)do_dec_stat(creature_ptr, A_CON);
-            ident = TRUE;
-            break;
-        }
+        msg_format(_("あなたは%sの魔力をエネルギー源として吸収した。", "You absorb mana of the %s as your energy."), staff);
 
-        case SV_FOOD_DISEASE: {
-            take_hit(creature_ptr, DAMAGE_NOESCAPE, damroll(10, 10), _("毒入り食料", "poisonous food"), -1);
-            (void)do_dec_stat(creature_ptr, A_STR);
-            ident = TRUE;
-            break;
-        }
+        /* Use a single charge */
+        o_ptr->pval--;
 
-        case SV_FOOD_CURE_POISON: {
-            if (set_poisoned(creature_ptr, 0))
-                ident = TRUE;
-            break;
-        }
+        /* Eat a charge */
+        set_food(creature_ptr, creature_ptr->food + 5000);
 
-        case SV_FOOD_CURE_BLINDNESS: {
-            if (set_blind(creature_ptr, 0))
-                ident = TRUE;
-            break;
-        }
+        /* XXX Hack -- unstack if necessary */
+        if (o_ptr->tval == TV_STAFF && (item >= 0) && (o_ptr->number > 1)) {
+            object_type forge;
+            object_type *q_ptr;
+            q_ptr = &forge;
+            object_copy(q_ptr, o_ptr);
 
-        case SV_FOOD_CURE_PARANOIA: {
-            if (set_afraid(creature_ptr, 0))
-                ident = TRUE;
-            break;
-        }
+            /* Modify quantity */
+            q_ptr->number = 1;
 
-        case SV_FOOD_CURE_CONFUSION: {
-            if (set_confused(creature_ptr, 0))
-                ident = TRUE;
-            break;
-        }
+            /* Restore the charges */
+            o_ptr->pval++;
 
-        case SV_FOOD_CURE_SERIOUS: {
-            ident = cure_serious_wounds(creature_ptr, 4, 8);
-            break;
-        }
+            /* Unstack the used item */
+            o_ptr->number--;
+            item = store_item_to_inventory(creature_ptr, q_ptr);
 
-        case SV_FOOD_RESTORE_STR: {
-            if (do_res_stat(creature_ptr, A_STR))
-                ident = TRUE;
-            break;
+            msg_format(_("杖をまとめなおした。", "You unstack your staff."));
         }
 
-        case SV_FOOD_RESTORE_CON: {
-            if (do_res_stat(creature_ptr, A_CON))
-                ident = TRUE;
-            break;
+        if (item >= 0) {
+            inven_item_charges(creature_ptr, item);
+        } else {
+            floor_item_charges(creature_ptr->current_floor_ptr, 0 - item);
         }
 
-        case SV_FOOD_RESTORING: {
-            ident = restore_all_status(creature_ptr);
-            break;
-        }
+        creature_ptr->window_flags |= (PW_INVEN | PW_EQUIP);
+        return TRUE;
+    }
 
-#ifdef JP
-        /* それぞれの食べ物の感想をオリジナルより細かく表現 */
-        case SV_FOOD_BISCUIT: {
-            msg_print("甘くてサクサクしてとてもおいしい。");
-            ident = TRUE;
-            break;
-        }
+    return FALSE;
+}
 
-        case SV_FOOD_JERKY: {
-            msg_print("歯ごたえがあっておいしい。");
-            ident = TRUE;
-            break;
-        }
+/*!
+ * @brief 食料を食べるコマンドのサブルーチン
+ * @param item 食べるオブジェクトの所持品ID
+ * @return なし
+ */
+void exe_eat_food(player_type *creature_ptr, INVENTORY_IDX item)
+{
+    if (music_singing_any(creature_ptr))
+        stop_singing(creature_ptr);
+    if (hex_spelling_any(creature_ptr))
+        stop_hex_spell_all(creature_ptr);
 
-        case SV_FOOD_SLIME_MOLD: {
-            msg_print("これはなんとも形容しがたい味だ。");
-            ident = TRUE;
-            break;
-        }
+    object_type *o_ptr = ref_item(creature_ptr, item);
 
-        case SV_FOOD_RATION: {
-            msg_print("これはおいしい。");
-            ident = TRUE;
-            break;
-        }
-#else
-        case SV_FOOD_RATION:
-        case SV_FOOD_BISCUIT:
-        case SV_FOOD_JERKY:
-        case SV_FOOD_SLIME_MOLD: {
-            msg_print("That tastes good.");
-            ident = TRUE;
-            break;
-        }
-#endif
+    sound(SOUND_EAT);
 
-        case SV_FOOD_WAYBREAD: {
-            msg_print(_("これはひじょうに美味だ。", "That tastes good."));
-            (void)set_poisoned(creature_ptr, 0);
-            (void)hp_player(creature_ptr, damroll(4, 8));
-            ident = TRUE;
-            break;
-        }
+    take_turn(creature_ptr, 100);
 
-        case SV_FOOD_PINT_OF_ALE: {
-            msg_print(_("のどごし爽やかだ。", "That tastes good."));
-            ident = TRUE;
-            break;
-        }
+    /* Object level */
+    int lev = k_info[o_ptr->k_idx].level;
 
-        case SV_FOOD_PINT_OF_WINE: {
-            msg_print(_("のどごし爽やかだ。", "That tastes good."));
-            ident = TRUE;
-            break;
-        }
-        }
-    }
+    /* Identity not known yet */
+    int ident = exe_eat_food_type_object(creature_ptr, o_ptr);
 
     /*
      * Store what may have to be updated for the inventory (including
@@ -303,7 +300,7 @@ void exe_eat_food(player_type *creature_ptr, INVENTORY_IDX item)
 
     creature_ptr->window_flags |= (PW_INVEN | PW_EQUIP | PW_PLAYER);
 
-    /* Food can feed the player */
+    /* Vampires are filled only by bloods */
     if (is_specific_player_race(creature_ptr, RACE_VAMPIRE) || (creature_ptr->mimic_form == MIMIC_VAMPIRE)) {
         /* Reduced nutritional benefit */
         (void)set_food(creature_ptr, creature_ptr->food + (o_ptr->pval / 10));
@@ -311,80 +308,30 @@ void exe_eat_food(player_type *creature_ptr, INVENTORY_IDX item)
 
         if (creature_ptr->food < PY_FOOD_ALERT) /* Hungry */
             msg_print(_("あなたの飢えは新鮮な血によってのみ満たされる!", "Your hunger can only be satisfied with fresh blood!"));
-    } else if ((is_specific_player_race(creature_ptr, RACE_SKELETON) || is_specific_player_race(creature_ptr, RACE_GOLEM)
-                   || is_specific_player_race(creature_ptr, RACE_ZOMBIE) || is_specific_player_race(creature_ptr, RACE_SPECTRE))
-        && (o_ptr->tval == TV_STAFF || o_ptr->tval == TV_WAND)) {
-        concptr staff;
-
-        if (o_ptr->tval == TV_STAFF && (item < 0) && (o_ptr->number > 1)) {
-            creature_ptr->update |= inventory_flags;
-            msg_print(_("まずは杖を拾わなければ。", "You must first pick up the staffs."));
-            return;
-        }
-        staff = (o_ptr->tval == TV_STAFF) ? _("杖", "staff") : _("魔法棒", "wand");
-
-        /* "Eat" charges */
-        if (o_ptr->pval == 0) {
-            msg_format(_("この%sにはもう魔力が残っていない。", "The %s has no charges left."), staff);
-            o_ptr->ident |= (IDENT_EMPTY);
-            creature_ptr->update |= inventory_flags;
-            creature_ptr->window_flags |= (PW_INVEN);
-
-            return;
-        }
-        msg_format(_("あなたは%sの魔力をエネルギー源として吸収した。", "You absorb mana of the %s as your energy."), staff);
-
-        /* Use a single charge */
-        o_ptr->pval--;
-
-        /* Eat a charge */
-        set_food(creature_ptr, creature_ptr->food + 5000);
 
-        /* XXX Hack -- unstack if necessary */
-        if (o_ptr->tval == TV_STAFF && (item >= 0) && (o_ptr->number > 1)) {
-            object_type forge;
-            object_type *q_ptr;
-            q_ptr = &forge;
-            object_copy(q_ptr, o_ptr);
-
-            /* Modify quantity */
-            q_ptr->number = 1;
-
-            /* Restore the charges */
-            o_ptr->pval++;
-
-            /* Unstack the used item */
-            o_ptr->number--;
-            item = store_item_to_inventory(creature_ptr, q_ptr);
-
-            msg_format(_("杖をまとめなおした。", "You unstack your staff."));
-        }
-
-        /* Describe charges in the pack */
-        if (item >= 0) {
-            inven_item_charges(creature_ptr, item);
-        }
-
-        /* Describe charges on the floor */
-        else {
-            floor_item_charges(creature_ptr->current_floor_ptr, 0 - item);
-        }
+        return;
+    }
 
-        creature_ptr->window_flags |= (PW_INVEN | PW_EQUIP);
+    /* Undeads drain recharge of magic device */
+    if (exe_eat_charge_of_magic_device(creature_ptr, o_ptr, item)) {
         creature_ptr->update |= inventory_flags;
-
-        /* Don't eat a staff/wand itself */
         return;
     }
 
+    /* Balrogs change humanoid corpses to energy */
     if ((is_specific_player_race(creature_ptr, RACE_BALROG) || (mimic_info[creature_ptr->mimic_form].MIMIC_FLAGS & MIMIC_IS_DEMON))
         && (o_ptr->tval == TV_CORPSE && o_ptr->sval == SV_CORPSE && angband_strchr("pht", r_info[o_ptr->pval].d_char))) {
-        /* Drain vitality of humanoids */
         GAME_TEXT o_name[MAX_NLEN];
         describe_flavor(creature_ptr, o_name, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY));
         msg_format(_("%sは燃え上り灰になった。精力を吸収した気がする。", "%^s is burnt to ashes.  You absorb its vitality!"), o_name);
         (void)set_food(creature_ptr, PY_FOOD_MAX - 1);
-    } else if (is_specific_player_race(creature_ptr, RACE_SKELETON)) {
+
+        creature_ptr->update |= inventory_flags;
+        vary_item(creature_ptr, item, -1);
+        return;
+    }
+    
+    if (is_specific_player_race(creature_ptr, RACE_SKELETON)) {
         if (!((o_ptr->sval == SV_FOOD_WAYBREAD) || (o_ptr->sval < SV_FOOD_BISCUIT))) {
             object_type forge;
             object_type *q_ptr = &forge;
@@ -403,12 +350,14 @@ void exe_eat_food(player_type *creature_ptr, INVENTORY_IDX item)
         || (mimic_info[creature_ptr->mimic_form].MIMIC_FLAGS & MIMIC_IS_NONLIVING)) {
         msg_print(_("生者の食物はあなたにとってほとんど栄養にならない。", "The food of mortals is poor sustenance for you."));
         set_food(creature_ptr, creature_ptr->food + ((o_ptr->pval) / 20));
-    } else if (o_ptr->tval == TV_FOOD && o_ptr->sval == SV_FOOD_WAYBREAD) {
-        /* Waybread is always fully satisfying. */
-        set_food(creature_ptr, MAX(creature_ptr->food, PY_FOOD_MAX - 1));
     } else {
-        /* Food can feed the player */
-        (void)set_food(creature_ptr, creature_ptr->food + o_ptr->pval);
+        if (o_ptr->tval == TV_FOOD && o_ptr->sval == SV_FOOD_WAYBREAD) {
+            /* Waybread is always fully satisfying. */
+            set_food(creature_ptr, MAX(creature_ptr->food, PY_FOOD_MAX - 1));
+        } else {
+            /* Food can feed the player */
+            (void)set_food(creature_ptr, creature_ptr->food + o_ptr->pval);
+        }
     }
 
     creature_ptr->update |= inventory_flags;
index d526683..66fdfcb 100644 (file)
@@ -168,6 +168,9 @@ void do_cmd_wield(player_type *creature_ptr)
 
         creature_ptr->select_ring_slot = FALSE;
         break;
+
+    default:
+        break;
     }
 
     if (object_is_cursed(&creature_ptr->inventory_list[slot])) {
index 0fcdcf3..5f158fc 100644 (file)
@@ -322,6 +322,9 @@ static MULTIPLY calc_shot_damage_with_slay(player_type *sniper_ptr, object_type
         }
         break;
     }
+
+    default:
+        break;
     }
 
     /* Sniper */
index a9bcc7a..cdb5ee7 100644 (file)
@@ -194,6 +194,9 @@ HIT_POINT calc_attack_damage_with_slay(player_type *attacker_ptr, object_type *o
             mult = 150;
         break;
     }
+
+    default:
+        break;
     }
 
     if (mult > 150)
index e001487..1a445b1 100644 (file)
@@ -237,13 +237,6 @@ bool get_check(concptr prompt) { return get_check_strict(p_ptr, prompt, 0); }
 bool get_check_strict(player_type *player_ptr, concptr prompt, BIT_FLAGS mode)
 {
     char buf[80];
-    if (auto_more) {
-        player_ptr->window_flags |= PW_MESSAGE;
-        handle_stuff(player_ptr);
-        num_more = 0;
-    }
-
-    msg_print(NULL);
     if (!rogue_like_commands)
         mode &= ~CHECK_OKAY_CANCEL;
 
@@ -258,6 +251,14 @@ bool get_check_strict(player_type *player_ptr, concptr prompt, BIT_FLAGS mode)
         strcat(buf, "[y/n]");
     }
 
+    if (auto_more) {
+        player_ptr->window_flags |= PW_MESSAGE;
+        handle_stuff(player_ptr);
+        num_more = 0;
+    }
+
+    msg_print(NULL);
+
     prt(buf, 0, 0);
     if (!(mode & CHECK_NO_HISTORY) && player_ptr->playing) {
         message_add(buf);
index 17fb98f..ab865f5 100644 (file)
@@ -252,7 +252,7 @@ void process_player_hp_mp(player_type *creature_ptr)
         }
 
         if (damage) {
-            damage = calc_pois_damage_rate(creature_ptr) / 100;
+            damage *= (calc_pois_damage_rate(creature_ptr) / 100);
             if (creature_ptr->levitation)
                 damage = damage / 5;
 
@@ -265,13 +265,13 @@ void process_player_hp_mp(player_type *creature_ptr)
                         f_name + f_info[get_feat_mimic(&creature_ptr->current_floor_ptr->grid_array[creature_ptr->y][creature_ptr->x])].name),
                     -1);
                 if (!has_resist_pois(creature_ptr))
-                    (void)set_poisoned(creature_ptr, creature_ptr->poisoned + 1);
+                    (void)set_poisoned(creature_ptr, creature_ptr->poisoned + damage);
             } else {
                 concptr name = f_name + f_info[get_feat_mimic(&creature_ptr->current_floor_ptr->grid_array[creature_ptr->y][creature_ptr->x])].name;
                 msg_format(_("%sに毒された!", "The %s poisons you!"), name);
                 take_hit(creature_ptr, DAMAGE_NOESCAPE, damage, name, -1);
                 if (!has_resist_pois(creature_ptr))
-                    (void)set_poisoned(creature_ptr, creature_ptr->poisoned + 3);
+                    (void)set_poisoned(creature_ptr, creature_ptr->poisoned + damage);
             }
 
             cave_no_regen = TRUE;
index b26489f..ef5130f 100644 (file)
@@ -17,6 +17,7 @@
 #include "effect/effect-monster-resist-hurt.h"
 #include "effect/effect-monster-spirit.h"
 #include "effect/effect-monster-util.h"
+#include "mind/mind-elementalist.h"
 #include "monster-floor/monster-death.h"
 #include "monster-race/monster-race-hook.h"
 #include "monster-race/monster-race.h"
index 6ff0b54..c0f6c39 100644 (file)
@@ -76,6 +76,7 @@ static process_result is_affective(player_type *caster_ptr, effect_monster_type
     case GF_OLD_CONF:
     case GF_OLD_POLY:
     case GF_GENOCIDE:
+    case GF_E_GENOCIDE:
         return PROCESS_CONTINUE;
     default:
         break;
index 2b5c541..fd53216 100644 (file)
@@ -9,6 +9,7 @@
 #include "effect/effect-characteristics.h"
 #include "effect/effect-player-switcher.h"
 #include "effect/effect-player-util.h"
+#include "effect/effect-processor.h"
 #include "effect/spells-effect-util.h"
 #include "floor/cave.h"
 #include "main/sound-definitions-table.h"
index 00735eb..9cf50d4 100644 (file)
@@ -2,7 +2,9 @@
 
 #include "system/angband.h"
 
-typedef bool (*project_func)(
+struct ProjectResult;
+
+using project_func = ProjectResult (*)(
     player_type *caster_ptr, MONSTER_IDX who, POSITION rad, POSITION y, POSITION x, HIT_POINT dam, EFFECT_ID typ, BIT_FLAGS flag, int monspell);
 
 bool affect_player(MONSTER_IDX who, player_type *target_ptr, concptr who_name, int r, POSITION y, POSITION x, HIT_POINT dam, EFFECT_ID typ, BIT_FLAGS flag,
index 62d7e46..e52dc29 100644 (file)
@@ -79,11 +79,9 @@ static void next_mirror(player_type *creature_ptr, POSITION *next_y, POSITION *n
  * @param typ 効果属性 / Type of damage to apply to monsters (and objects)
  * @param flag 効果フラグ / Extra bit flags (see PROJECT_xxxx)
  * @param monspell 効果元のモンスター魔法ID
- * @return 何か一つでも効力があればTRUEを返す / TRUE if any "effects" of the
- * projection were observed, else FALSE
  */
-bool project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSITION y, POSITION x, const HIT_POINT dam, const EFFECT_ID typ, BIT_FLAGS flag,
-    const int monspell)
+ProjectResult project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSITION y, POSITION x, const HIT_POINT dam, const EFFECT_ID typ,
+    BIT_FLAGS flag, const int monspell)
 {
     int dist;
     POSITION y1;
@@ -93,7 +91,6 @@ bool project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSIT
     POSITION y_saver;
     POSITION x_saver;
     int msec = delay_factor * delay_factor * delay_factor;
-    bool notice = FALSE;
     bool visual = FALSE;
     bool drawn = FALSE;
     bool breath = FALSE;
@@ -115,6 +112,8 @@ bool project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSIT
     monster_target_y = caster_ptr->y;
     monster_target_x = caster_ptr->x;
 
+    ProjectResult res;
+
     if (flag & (PROJECT_JUMP)) {
         x1 = x;
         y1 = y;
@@ -224,7 +223,7 @@ bool project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSIT
             }
 
             if (affect_item(caster_ptr, 0, 0, y, x, dam, GF_SEEKER))
-                notice = TRUE;
+                res.notice = TRUE;
             if (!is_mirror_grid(&caster_ptr->current_floor_ptr->grid_array[y][x]))
                 continue;
 
@@ -237,7 +236,7 @@ bool project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSIT
                 y = get_grid_y(path_g[j]);
                 x = get_grid_x(path_g[j]);
                 if (affect_monster(caster_ptr, 0, 0, y, x, dam, GF_SEEKER, flag, TRUE))
-                    notice = TRUE;
+                    res.notice = TRUE;
                 if (!who && (project_m_n == 1) && !jump && (caster_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx > 0)) {
                     monster_type *m_ptr = &caster_ptr->current_floor_ptr->m_list[caster_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx];
                     if (m_ptr->ml) {
@@ -258,7 +257,7 @@ bool project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSIT
             py = get_grid_y(path_g[i]);
             px = get_grid_x(path_g[i]);
             if (affect_monster(caster_ptr, 0, 0, py, px, dam, GF_SEEKER, flag, TRUE))
-                notice = TRUE;
+                res.notice = TRUE;
             if (!who && (project_m_n == 1) && !jump) {
                 if (caster_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx > 0) {
                     monster_type *m_ptr = &caster_ptr->current_floor_ptr->m_list[caster_ptr->current_floor_ptr->grid_array[project_m_y][project_m_x].m_idx];
@@ -274,7 +273,7 @@ bool project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSIT
             (void)affect_feature(caster_ptr, 0, 0, py, px, dam, GF_SEEKER);
         }
 
-        return notice;
+        return res;
     } else if (typ == GF_SUPER_RAY) {
         int j;
         int second_step = 0;
@@ -321,7 +320,7 @@ bool project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSIT
             }
 
             if (affect_item(caster_ptr, 0, 0, y, x, dam, GF_SUPER_RAY))
-                notice = TRUE;
+                res.notice = TRUE;
             if (!cave_has_flag_bold(caster_ptr->current_floor_ptr, y, x, FF_PROJECT)) {
                 if (second_step)
                     continue;
@@ -378,7 +377,7 @@ bool project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSIT
             (void)affect_feature(caster_ptr, 0, 0, py, px, dam, GF_SUPER_RAY);
         }
 
-        return notice;
+        return res;
     }
 
     int k;
@@ -507,7 +506,7 @@ bool project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSIT
     }
 
     if (!grids)
-        return FALSE;
+        return res;
 
     if (!blind && !(flag & (PROJECT_HIDE)) && (msec > 0)) {
         for (int t = 0; t <= gm_rad; t++) {
@@ -564,10 +563,10 @@ bool project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSIT
             if (breath) {
                 int d = dist_to_line(y, x, y1, x1, by, bx);
                 if (affect_feature(caster_ptr, who, d, y, x, dam, typ))
-                    notice = TRUE;
+                    res.notice = TRUE;
             } else {
                 if (affect_feature(caster_ptr, who, dist, y, x, dam, typ))
-                    notice = TRUE;
+                    res.notice = TRUE;
             }
         }
     }
@@ -584,10 +583,10 @@ bool project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSIT
             if (breath) {
                 int d = dist_to_line(y, x, y1, x1, by, bx);
                 if (affect_item(caster_ptr, who, d, y, x, dam, typ))
-                    notice = TRUE;
+                    res.notice = TRUE;
             } else {
                 if (affect_item(caster_ptr, who, dist, y, x, dam, typ))
-                    notice = TRUE;
+                    res.notice = TRUE;
             }
         }
     }
@@ -709,7 +708,7 @@ bool project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSIT
             }
 
             if (affect_monster(caster_ptr, who, effective_dist, y, x, dam, typ, flag, see_s_msg))
-                notice = TRUE;
+                res.notice = TRUE;
         }
 
         /* Player affected one monster (without "jumping") */
@@ -779,8 +778,10 @@ bool project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSIT
                 }
             }
 
-            if (affect_player(who, caster_ptr, who_name, effective_dist, y, x, dam, typ, flag, monspell, project))
-                notice = TRUE;
+            if (affect_player(who, caster_ptr, who_name, effective_dist, y, x, dam, typ, flag, monspell, project)) {
+                res.notice = TRUE;
+                res.affected_player = TRUE;
+            }
         }
     }
 
@@ -800,5 +801,5 @@ bool project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSIT
         }
     }
 
-    return (notice);
+    return res;
 }
index ab2a951..680ff43 100644 (file)
@@ -2,5 +2,13 @@
 
 #include "system/angband.h"
 
-bool project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSITION y, POSITION x, const HIT_POINT dam, const EFFECT_ID typ, BIT_FLAGS flag,
-    const int monspell);
+//! project() の結果。
+struct ProjectResult {
+    bool notice{ false }; //!< プレイヤー/モンスター/アイテム/地形などに何らかの効果を及ぼしたか
+    bool affected_player{ false }; //!< プレイヤーに何らかの効果を及ぼしたか(ラーニング判定用)
+
+    ProjectResult() = default;
+};
+
+ProjectResult project(player_type *caster_ptr, const MONSTER_IDX who, POSITION rad, POSITION y, POSITION x, const HIT_POINT dam, const EFFECT_ID typ,
+    BIT_FLAGS flag, const int monspell);
index a5777b0..b9bb049 100644 (file)
@@ -180,6 +180,9 @@ static void describe_tval(player_type *player_ptr, flavor_type *flavor_ptr)
     case TV_BOW:
         describe_bow(player_ptr, flavor_ptr);
         break;
+
+    default:
+        break;
     }
 }
 
index d6b1720..53dd59e 100644 (file)
@@ -85,6 +85,9 @@ static bool object_easy_know(int i)
     case TV_SCROLL:
     case TV_ROD:
         return TRUE;
+
+    default:
+        break;
     }
 
     return FALSE;
@@ -289,4 +292,4 @@ void strip_name(char *buf, KIND_OBJECT_IDX k_idx)
     }
 
     *t = '\0';
-}
\ No newline at end of file
+}
index 7467638..346f9f4 100644 (file)
@@ -9,7 +9,9 @@
  * 2014 Deskull rearranged comment for Doxygen. \n
  */
 
-#include <assert.h>
+#include <algorithm>
+#include <array>
+#include <stack>
 
 #include "dungeon/dungeon-flag-types.h"
 #include "dungeon/dungeon.h"
 #include "wizard/wizard-messages.h"
 #include "world/world.h"
 
-//--------------------------------------------------------------------
-// 動的可変長配列
-//--------------------------------------------------------------------
-
-typedef struct Vec {
-    size_t len;
-    size_t cap;
-    int *data;
-} Vec;
-
-// 容量 cap の空 Vec を返す。
-static Vec vec_with_capacity(const size_t cap)
-{
-    assert(cap > 0);
-
-    Vec vec = { .len = 0, .cap = cap, .data = NULL };
-    vec.data = static_cast<int*>(malloc(sizeof(int) * cap));
-    if (!vec.data)
-        rpanic(cap);
-
-    return vec;
-}
-
-// サイズ len で、全要素が init で初期化された Vec を返す。
-static Vec vec_new(const size_t len, const int init)
-{
-    assert(len > 0);
-
-    const size_t cap = 2 * len;
-    Vec vec = vec_with_capacity(cap);
-
-    vec.len = len;
-    for (size_t i = 0; i < len; ++i)
-        vec.data[i] = init;
-
-    return vec;
-}
-
-static void vec_delete(Vec *const vec)
-{
-    free(vec->data);
-
-    vec->len = 0;
-    vec->cap = 0;
-    vec->data = NULL;
-}
-
-static size_t vec_size(const Vec *const vec) { return vec->len; }
-
-static bool vec_is_empty(const Vec *const vec) { return vec_size(vec) == 0; }
-
-static void vec_push_back(Vec *const vec, const int e)
-{
-    // 容量不足になったら容量を拡張する。
-    if (vec->len == vec->cap) {
-        vec->cap = vec->cap > 0 ? 2 * vec->cap : 1;
-        vec->data = static_cast<int*>(realloc(vec->data, sizeof(int) * vec->cap));
-        if (!vec->data)
-            rpanic(vec->cap);
-    }
-
-    vec->data[vec->len++] = e;
-}
-
-static int vec_pop_back(Vec *const vec)
-{
-    assert(!vec_is_empty(vec));
-
-    return vec->data[vec->len-- - 1];
-}
-
-//--------------------------------------------------------------------
-
 /*!
  * @brief 闘技場用のアリーナ地形を作成する / Builds the on_defeat_arena_monster after it is entered -KMW-
  * @param player_ptr プレーヤーへの参照ポインタ
@@ -448,8 +377,7 @@ static bool is_permanent_blocker(const floor_type *const floor_ptr, const int y,
     return has_flag(flags, FF_PERMANENT) && !has_flag(flags, FF_MOVE);
 }
 
-static void floor_is_connected_dfs(
-    const floor_type *const floor_ptr, const IsWallFunc is_wall, const int y_start, const int x_start, Vec *const stk, const Vec *const visited)
+static void floor_is_connected_dfs(const floor_type *const floor_ptr, const IsWallFunc is_wall, const int y_start, const int x_start, bool *const visited)
 {
     // clang-format off
     static const int DY[8] = { -1, -1, -1,  0, 0,  1, 1, 1 };
@@ -460,11 +388,16 @@ static void floor_is_connected_dfs(
     const int w = floor_ptr->width;
     const int start = w * y_start + x_start;
 
-    vec_push_back(stk, start);
-    visited->data[start] = 1;
+    // 深さ優先探索用のスタック。
+    // 最大フロアサイズが h=66, w=198 なので、スタックオーバーフロー防止のため再帰は使わない。
+    std::stack<int> stk;
 
-    while (!vec_is_empty(stk)) {
-        const int cur = vec_pop_back(stk);
+    stk.emplace(start);
+    visited[start] = TRUE;
+
+    while (!stk.empty()) {
+        const int cur = stk.top();
+        stk.pop();
         const int y = cur / w;
         const int x = cur % w;
 
@@ -474,13 +407,13 @@ static void floor_is_connected_dfs(
             if (y_nxt < 0 || h <= y_nxt || x_nxt < 0 || w <= x_nxt)
                 continue;
             const int nxt = w * y_nxt + x_nxt;
-            if (visited->data[nxt])
+            if (visited[nxt])
                 continue;
             if (is_wall(floor_ptr, y_nxt, x_nxt))
                 continue;
 
-            vec_push_back(stk, nxt);
-            visited->data[nxt] = 1;
+            stk.emplace(nxt);
+            visited[nxt] = TRUE;
         }
     }
 }
@@ -491,33 +424,29 @@ static void floor_is_connected_dfs(
 // 連結成分数が 0 の場合、偽を返す。
 static bool floor_is_connected(const floor_type *const floor_ptr, const IsWallFunc is_wall)
 {
+    static std::array<bool, MAX_HGT * MAX_WID> visited;
+
     const int h = floor_ptr->height;
     const int w = floor_ptr->width;
 
-    // ヒープ上に確保したスタックを用いてDFSする。
-    // 最大フロアサイズが h=66, w=198 なので、単純に再帰DFSするとスタックオーバーフローが不安。
-    Vec stk = vec_with_capacity(1024);
-    Vec visited = vec_new((size_t)h * (size_t)w, 0);
+    std::fill(begin(visited), end(visited), false);
+
     int n_component = 0; // 連結成分数
 
     for (int y = 0; y < h; ++y) {
         for (int x = 0; x < w; ++x) {
             const int idx = w * y + x;
-            if (visited.data[idx])
+            if (visited[idx])
                 continue;
             if (is_wall(floor_ptr, y, x))
                 continue;
 
             if (++n_component >= 2)
-                goto finish;
-            floor_is_connected_dfs(floor_ptr, is_wall, y, x, &stk, &visited);
+                break;
+            floor_is_connected_dfs(floor_ptr, is_wall, y, x, visited.data());
         }
     }
 
-finish:
-    vec_delete(&visited);
-    vec_delete(&stk);
-
     return n_component == 1;
 }
 
index 5390131..2503285 100644 (file)
@@ -128,6 +128,9 @@ bool make_object(player_type *owner_ptr, object_type *j_ptr, BIT_FLAGS mode)
         if (!j_ptr->name1)
             j_ptr->number = (byte)damroll(6, 7);
     }
+
+    default:
+        break;
     }
 
     if (cheat_peek)
index dc3f426..41c6708 100644 (file)
@@ -14,7 +14,8 @@
  * 2013 Deskull Doxygen向けのコメント整理\n
  */
 
-#include "grid/grid.h"
+#include <queue>
+
 #include "core/window-redrawer.h"
 #include "dungeon/dungeon-flag-types.h"
 #include "dungeon/dungeon.h"
@@ -27,6 +28,7 @@
 #include "game-option/map-screen-options.h"
 #include "game-option/special-options.h"
 #include "grid/feature.h"
+#include "grid/grid.h"
 #include "grid/object-placer.h"
 #include "grid/trap.h"
 #include "io/screen-util.h"
@@ -60,8 +62,6 @@
 #define FAF_NO_DROP 0x02
 #define FAF_CRASH_GLASS 0x04
 
-pos_list tmp_pos;
-
 /*!
  * @brief 地形状態フラグテーブル /
  * The table of features' actions
@@ -802,14 +802,18 @@ static POSITION flow_y = 0;
  */
 void update_flow(player_type *subject_ptr)
 {
+    struct Point {
+        int y;
+        int x;
+        Point(const int y, const int x)
+            : y(y)
+            , x(x)
+        {
+        }
+    };
+
     POSITION x, y;
     DIRECTION d;
-    int flow_head_grid = 1;
-    int flow_tail_grid = 0;
-
-    /* Paranoia -- make sure the array is empty */
-    if (tmp_pos.n)
-        return;
 
     /* The last way-point is on the map */
     if (subject_ptr->running && in_bounds(subject_ptr->current_floor_ptr, flow_y, flow_x)) {
@@ -830,28 +834,20 @@ void update_flow(player_type *subject_ptr)
     flow_y = subject_ptr->y;
     flow_x = subject_ptr->x;
 
-    /* Add the player's grid to the queue */
-    tmp_pos.y[0] = subject_ptr->y;
-    tmp_pos.x[0] = subject_ptr->x;
+    // 幅優先探索用のキュー。
+    std::queue<Point> que;
+    que.emplace(subject_ptr->y, subject_ptr->x);
 
     /* Now process the queue */
-    while (flow_head_grid != flow_tail_grid) {
-        int ty, tx;
-
+    while (!que.empty()) {
         /* Extract the next entry */
-        ty = tmp_pos.y[flow_tail_grid];
-        tx = tmp_pos.x[flow_tail_grid];
-
-        /* Forget that entry */
-        if (++flow_tail_grid == TEMP_MAX)
-            flow_tail_grid = 0;
+        const auto [ty, tx] = que.front();
+        que.pop();
 
         /* Add the "children" */
         for (d = 0; d < 8; d++) {
-            int old_head = flow_head_grid;
             byte m = subject_ptr->current_floor_ptr->grid_array[ty][tx].cost + 1;
             byte n = subject_ptr->current_floor_ptr->grid_array[ty][tx].dist + 1;
-            grid_type *g_ptr;
 
             /* Child location */
             y = ty + ddy_ddd[d];
@@ -861,7 +857,7 @@ void update_flow(player_type *subject_ptr)
             if (player_bold(subject_ptr, y, x))
                 continue;
 
-            g_ptr = &subject_ptr->current_floor_ptr->grid_array[y][x];
+            grid_type *g_ptr = &subject_ptr->current_floor_ptr->grid_array[y][x];
 
             if (is_closed_door(subject_ptr, g_ptr->feat))
                 m += 3;
@@ -885,16 +881,7 @@ void update_flow(player_type *subject_ptr)
                 continue;
 
             /* Enqueue that entry */
-            tmp_pos.y[flow_head_grid] = y;
-            tmp_pos.x[flow_head_grid] = x;
-
-            /* Advance the queue */
-            if (++flow_head_grid == TEMP_MAX)
-                flow_head_grid = 0;
-
-            /* Hack -- notice overflow by forgetting new entry */
-            if (flow_head_grid == flow_tail_grid)
-                flow_head_grid = old_head;
+            que.emplace(y, x);
         }
     }
 }
index e957e09..359878f 100644 (file)
  * included in all such copies.
  */
 
-#include "system/angband.h"
 #include "floor/geometry.h"
 #include "spell/spells-util.h"
+#include "system/angband.h"
 
- /*
 * A single "grid" in a Cave
 *
 * Note that several aspects of the code restrict the actual grid
 * to a max size of 256 by 256.  In partcular, locations are often
 * saved as bytes, limiting each coordinate to the 0-255 range.
 *
 * The "o_idx" and "m_idx" fields are very interesting.  There are
 * many places in the code where we need quick access to the actual
 * monster or object(s) in a given grid.  The easiest way to
 * do this is to simply keep the index of the monster and object
 * (if any) with the grid, but this takes 198*66*4 bytes of memory.
 * Several other methods come to mind, which require only half this
 * amound of memory, but they all seem rather complicated, and would
 * probably add enough code that the savings would be lost.  So for
 * these reasons, we simply store an index into the "o_list" and
 * ">m_list" arrays, using "zero" when no monster/object is present.
 *
 * Note that "o_idx" is the index of the top object in a stack of
 * objects, using the "next_o_idx" field of objects (see below) to
 * create the singly linked list of objects.  If "o_idx" is zero
 * then there are no objects in the grid.
 *
 * Note the special fields for the "MONSTER_FLOW" code.
 */
+/*
+ * A single "grid" in a Cave
+ *
+ * Note that several aspects of the code restrict the actual grid
+ * to a max size of 256 by 256.  In partcular, locations are often
+ * saved as bytes, limiting each coordinate to the 0-255 range.
+ *
+ * The "o_idx" and "m_idx" fields are very interesting.  There are
+ * many places in the code where we need quick access to the actual
+ * monster or object(s) in a given grid.  The easiest way to
+ * do this is to simply keep the index of the monster and object
+ * (if any) with the grid, but this takes 198*66*4 bytes of memory.
+ * Several other methods come to mind, which require only half this
+ * amound of memory, but they all seem rather complicated, and would
+ * probably add enough code that the savings would be lost.  So for
+ * these reasons, we simply store an index into the "o_list" and
+ * ">m_list" arrays, using "zero" when no monster/object is present.
+ *
+ * Note that "o_idx" is the index of the top object in a stack of
+ * objects, using the "next_o_idx" field of objects (see below) to
+ * create the singly linked list of objects.  If "o_idx" is zero
+ * then there are no objects in the grid.
+ *
+ * Note the special fields for the "MONSTER_FLOW" code.
+ */
 
 typedef struct grid_type {
-       BIT_FLAGS info;         /* Hack -- grid flags */
+    BIT_FLAGS info; /* Hack -- grid flags */
 
-       FEAT_IDX feat;          /* Hack -- feature type */
-       OBJECT_IDX o_idx;               /* Object in this grid */
-       MONSTER_IDX m_idx;              /* Monster in this grid */
+    FEAT_IDX feat; /* Hack -- feature type */
+    OBJECT_IDX o_idx; /* Object in this grid */
+    MONSTER_IDX m_idx; /* Monster in this grid */
 
-       /*! 地形の特別な情報を保存する / Special grid info
-        * 具体的な使用一覧はクエスト行き階段の移行先クエストID、
-        * 各ダンジョン入口の移行先ダンジョンID、
-        *
-        */
-       s16b special;
+    /*! 地形の特別な情報を保存する / Special grid info
+     * 具体的な使用一覧はクエスト行き階段の移行先クエストID、
+     * 各ダンジョン入口の移行先ダンジョンID、
+     *
+     */
+    s16b special;
 
-       FEAT_IDX mimic;         /* Feature to mimic */
+    FEAT_IDX mimic; /* Feature to mimic */
 
-       byte cost;              /* Hack -- cost of flowing */
-       byte dist;              /* Hack -- distance from player */
-       byte when;              /* Hack -- when cost was computed */
+    byte cost; /* Hack -- cost of flowing */
+    byte dist; /* Hack -- distance from player */
+    byte when; /* Hack -- when cost was computed */
 } grid_type;
 
 /*  A structure type for terrain template of saving dungeon floor */
 typedef struct grid_template_type {
-       BIT_FLAGS info;
-       FEAT_IDX feat;
-       FEAT_IDX mimic;
-       s16b special;
-       u16b occurrence;
+    BIT_FLAGS info;
+    FEAT_IDX feat;
+    FEAT_IDX mimic;
+    s16b special;
+    u16b occurrence;
 } grid_template_type;
 
 /*!
@@ -79,7 +79,7 @@ typedef struct grid_template_type {
  * @param Y 指定Y座標
  * @param X 指定X座標
  */
-#define place_rubble(F,Y,X)       set_cave_feat(F,Y,X,feat_rubble)
+#define place_rubble(F, Y, X) set_cave_feat(F, Y, X, feat_rubble)
 
 /*!
  * @brief 指定座標がFLOOR属性を持ったマスかどうかを返す
@@ -87,12 +87,12 @@ typedef struct grid_template_type {
  * @param X 指定X座標
  * @return FLOOR属性を持っているならばTRUE
  */
-#define is_floor_bold(F,Y,X) (F->grid_array[Y][X].info & CAVE_FLOOR)
-#define is_extra_bold(F,Y,X) (F->grid_array[Y][X].info & CAVE_EXTRA)
+#define is_floor_bold(F, Y, X) (F->grid_array[Y][X].info & CAVE_FLOOR)
+#define is_extra_bold(F, Y, X) (F->grid_array[Y][X].info & CAVE_EXTRA)
 
-#define is_inner_bold(F,Y,X) (F->grid_array[Y][X].info & CAVE_INNER)
-#define is_outer_bold(F,Y,X) (F->grid_array[Y][X].info & CAVE_OUTER)
-#define is_solid_bold(F,Y,X) (F->grid_array[Y][X].info & CAVE_SOLID)
+#define is_inner_bold(F, Y, X) (F->grid_array[Y][X].info & CAVE_INNER)
+#define is_outer_bold(F, Y, X) (F->grid_array[Y][X].info & CAVE_OUTER)
+#define is_solid_bold(F, Y, X) (F->grid_array[Y][X].info & CAVE_SOLID)
 
 #define is_floor_grid(C) ((C)->info & CAVE_FLOOR)
 #define is_extra_grid(C) ((C)->info & CAVE_EXTRA)
@@ -100,6 +100,8 @@ typedef struct grid_template_type {
 #define is_outer_grid(C) ((C)->info & CAVE_OUTER)
 #define is_solid_grid(C) ((C)->info & CAVE_SOLID)
 
+// clang-format off
+
 /*
  * 特殊なマス状態フラグ / Special grid flags
  */
@@ -115,7 +117,7 @@ typedef struct grid_template_type {
 #define CAVE_MNDK       0x8000    /*!< モンスターの暗源によって暗闇になっている / Darken by monster */
 
 /* Used only while floor generation */
-#define CAVE_FLOOR      0x0200 /*!< フロア属性のあるマス */
+#define CAVE_FLOOR      0x0200    /*!< フロア属性のあるマス */
 #define CAVE_EXTRA      0x0400
 #define CAVE_INNER      0x0800
 #define CAVE_OUTER      0x1000
@@ -146,8 +148,9 @@ typedef struct grid_template_type {
 #define DOOR_GLASS_DOOR  1
 #define DOOR_CURTAIN     2
 
+// clang-format on
+
 extern bool new_player_spot(player_type *creature_ptr);
-extern pos_list tmp_pos;
 
 extern void place_bound_perm_wall(player_type *player_ptr, grid_type *g_ptr);
 extern bool is_known_trap(player_type *player_ptr, grid_type *g_ptr);
@@ -162,7 +165,6 @@ extern bool player_can_enter(player_type *creature_ptr, FEAT_IDX feature, BIT_FL
  */
 bool feat_uses_special(FEAT_IDX f_idx);
 
-extern POSITION distance(POSITION y1, POSITION x1, POSITION y2, POSITION x2);
 extern void update_local_illumination(player_type *creature_ptr, POSITION y, POSITION x);
 extern bool no_lite(player_type *creature_ptr);
 extern void print_rel(player_type *subject_ptr, SYMBOL_CODE c, TERM_COLOR a, POSITION y, POSITION x);
@@ -178,19 +180,20 @@ extern bool check_local_illumination(player_type *creature_ptr, POSITION y, POSI
 extern bool cave_monster_teleportable_bold(player_type *player_ptr, MONSTER_IDX m_idx, POSITION y, POSITION x, teleport_flags mode);
 extern bool cave_player_teleportable_bold(player_type *player_ptr, POSITION y, POSITION x, teleport_flags mode);
 
-enum grid_bold_type
-{
-       GB_FLOOR,
-       GB_EXTRA,
-       GB_EXTRA_PERM,
-       GB_INNER,
-       GB_INNER_PERM,
-       GB_OUTER,
-       GB_OUTER_NOPERM,
-       GB_SOLID,
-       GB_SOLID_PERM,
-       GB_SOLID_NOPERM
+// clang-format off
+enum grid_bold_type {
+    GB_FLOOR,
+    GB_EXTRA,
+    GB_EXTRA_PERM,
+    GB_INNER,
+    GB_INNER_PERM,
+    GB_OUTER,
+    GB_OUTER_NOPERM,
+    GB_SOLID,
+    GB_SOLID_PERM,
+    GB_SOLID_NOPERM
 };
+// clang-format on
 
 void place_grid(player_type *player_ptr, grid_type *g_ptr, grid_bold_type pg_type);
 bool darkened_grid(player_type *player_ptr, grid_type *g_ptr);
@@ -200,6 +203,8 @@ void set_cave_feat(floor_type *floor_ptr, POSITION y, POSITION x, FEAT_IDX featu
 void add_cave_info(floor_type *floor_ptr, POSITION y, POSITION x, int cave_mask);
 FEAT_IDX get_feat_mimic(grid_type *g_ptr);
 
+// clang-format off
+
 /*
  * This macro allows us to efficiently add a grid to the "lite" array,
  * note that we are never called for illegal grids, or for grids which
@@ -208,12 +213,12 @@ FEAT_IDX get_feat_mimic(grid_type *g_ptr);
  */
 #define cave_lite_hack(F,Y,X) \
 {\
-       if (!((F)->grid_array[Y][X].info & (CAVE_LITE))) \
-       { \
-               (F)->grid_array[Y][X].info |= (CAVE_LITE); \
-               (F)->lite_y[(F)->lite_n] = (Y); \
-               (F)->lite_x[(F)->lite_n++] = (X); \
-       } \
+    if (!((F)->grid_array[Y][X].info & (CAVE_LITE))) \
+    { \
+        (F)->grid_array[Y][X].info |= (CAVE_LITE); \
+        (F)->lite_y[(F)->lite_n] = (Y); \
+        (F)->lite_x[(F)->lite_n++] = (X); \
+    } \
 }
 
 /*
@@ -221,8 +226,8 @@ FEAT_IDX get_feat_mimic(grid_type *g_ptr);
  */
 #define cave_note_and_redraw_later(F,C,Y,X) \
 {\
-       (C)->info |= CAVE_NOTE; \
-       cave_redraw_later((F), (C), (Y), (X)); \
+    (C)->info |= CAVE_NOTE; \
+    cave_redraw_later((F), (C), (Y), (X)); \
 }
 
 /*
@@ -230,12 +235,12 @@ FEAT_IDX get_feat_mimic(grid_type *g_ptr);
 */
 #define cave_redraw_later(F,G,Y,X) \
 {\
-       if (!((G)->info & CAVE_REDRAW)) \
-       { \
-               (G)->info |= CAVE_REDRAW; \
-               (F)->redraw_y[(F)->redraw_n] = (Y); \
-               (F)->redraw_x[(F)->redraw_n++] = (X); \
-       } \
+    if (!((G)->info & CAVE_REDRAW)) \
+    { \
+        (G)->info |= CAVE_REDRAW; \
+        (F)->redraw_y[(F)->redraw_n] = (Y); \
+        (F)->redraw_x[(F)->redraw_n++] = (X); \
+    } \
 }
 
 /*
@@ -253,4 +258,6 @@ FEAT_IDX get_feat_mimic(grid_type *g_ptr);
     (F)->view_n++;}\
 }
 
+// clang-format on
+
 int count_dt(player_type *creature_ptr, POSITION *y, POSITION *x, bool (*test)(player_type *, FEAT_IDX), bool under);
index 9e4c28d..bd0a46d 100644 (file)
@@ -52,8 +52,8 @@ errr init_info_txt(FILE *fp, char *buf, angband_header *head, parse_info_txt_fun
         if (buf[0] != 'N' && buf[0] != 'D') {
             int i;
             for (i = 0; buf[i]; i++) {
-                head->v_extra += (byte)buf[i];
-                head->v_extra ^= (1U << (i % 8));
+                head->checksum += (byte)buf[i];
+                head->checksum ^= (1U << (i % 8));
             }
         }
 
index a56f848..e3d0fa7 100644 (file)
@@ -548,8 +548,8 @@ static void dump_aux_home_museum(player_type *creature_ptr, FILE *fff)
  */
 static concptr get_check_sum(void)
 {
-    return format("%02x%02x%02x%02x%02x%02x%02x%02x%02x", f_head.v_extra, k_head.v_extra, a_head.v_extra, e_head.v_extra, r_head.v_extra, d_head.v_extra,
-        m_head.v_extra, s_head.v_extra, v_head.v_extra);
+    return format("%02x%02x%02x%02x%02x%02x%02x%02x%02x", f_head.checksum, k_head.checksum, a_head.checksum, e_head.checksum, r_head.checksum, d_head.checksum,
+        m_head.checksum, s_head.checksum, v_head.checksum);
 }
 
 /*!
index 20da27e..044d125 100644 (file)
@@ -72,6 +72,7 @@
 #include "main/sound-definitions-table.h"
 #include "main/sound-of-music.h"
 #include "mind/mind-blue-mage.h"
+#include "mind/mind-elementalist.h"
 #include "mind/mind-magic-eater.h"
 #include "mind/mind-sniper.h"
 #include "mind/snipe-types.h"
@@ -356,6 +357,8 @@ void process_command(player_type *creature_ptr)
         if ((creature_ptr->pclass == CLASS_MINDCRAFTER) || (creature_ptr->pclass == CLASS_BERSERKER) || (creature_ptr->pclass == CLASS_NINJA)
             || (creature_ptr->pclass == CLASS_MIRROR_MASTER))
             do_cmd_mind_browse(creature_ptr);
+        else if (creature_ptr->pclass == CLASS_ELEMENTALIST)
+            do_cmd_element_browse(creature_ptr);
         else if (creature_ptr->pclass == CLASS_SMITH)
             do_cmd_kaji(creature_ptr, TRUE);
         else if (creature_ptr->pclass == CLASS_MAGIC_EATER)
@@ -377,18 +380,30 @@ void process_command(player_type *creature_ptr)
                 msg_print(NULL);
             } else if (creature_ptr->anti_magic && (creature_ptr->pclass != CLASS_BERSERKER) && (creature_ptr->pclass != CLASS_SMITH)) {
                 concptr which_power = _("魔法", "magic");
-                if (creature_ptr->pclass == CLASS_MINDCRAFTER)
+                switch (creature_ptr->pclass) {
+                case CLASS_MINDCRAFTER:
                     which_power = _("超能力", "psionic powers");
-                else if (creature_ptr->pclass == CLASS_IMITATOR)
+                    break;
+                case CLASS_IMITATOR:
                     which_power = _("ものまね", "imitation");
-                else if (creature_ptr->pclass == CLASS_SAMURAI)
+                    break;
+                case CLASS_SAMURAI:
                     which_power = _("必殺剣", "hissatsu");
-                else if (creature_ptr->pclass == CLASS_MIRROR_MASTER)
+                    break;
+                case CLASS_MIRROR_MASTER:
                     which_power = _("鏡魔法", "mirror magic");
-                else if (creature_ptr->pclass == CLASS_NINJA)
+                    break;
+                case CLASS_NINJA:
                     which_power = _("忍術", "ninjutsu");
-                else if (mp_ptr->spell_book == TV_LIFE_BOOK)
-                    which_power = _("祈り", "prayer");
+                    break;
+                case CLASS_ELEMENTALIST:
+                    which_power = _("元素魔法", "magic");
+                    break;
+                default:
+                    if (mp_ptr->spell_book == TV_LIFE_BOOK)
+                        which_power = _("祈り", "prayer");
+                    break;
+                }
 
                 msg_format(_("反魔法バリアが%sを邪魔した!", "An anti-magic shell disrupts your %s!"), which_power);
                 free_turn(creature_ptr);
@@ -399,6 +414,8 @@ void process_command(player_type *creature_ptr)
                 if ((creature_ptr->pclass == CLASS_MINDCRAFTER) || (creature_ptr->pclass == CLASS_BERSERKER) || (creature_ptr->pclass == CLASS_NINJA)
                     || (creature_ptr->pclass == CLASS_MIRROR_MASTER))
                     do_cmd_mind(creature_ptr);
+                else if (creature_ptr->pclass == CLASS_ELEMENTALIST)
+                    do_cmd_element(creature_ptr);
                 else if (creature_ptr->pclass == CLASS_IMITATOR)
                     do_cmd_mane(creature_ptr, FALSE);
                 else if (creature_ptr->pclass == CLASS_MAGIC_EATER)
index 5de1ce5..3456f37 100644 (file)
@@ -226,8 +226,7 @@ static errr interpret_p_token(char *buf)
 {
        char tmp[1024];
        text_to_ascii(tmp, buf + 2);
-       macro_add(tmp, macro__buf);
-       return 0;
+       return macro_add(tmp, macro__buf);
 }
 
 
index 6fd3638..49fbee0 100644 (file)
@@ -285,7 +285,7 @@ void load_all_pref_files(player_type *player_ptr)
     process_pref_file(player_ptr, buf, process_autopick_file_command);
     sprintf(buf, "%s.prf", player_ptr->base_name);
     process_pref_file(player_ptr, buf, process_autopick_file_command);
-    if (player_ptr->realm1 != REALM_NONE) {
+    if (player_ptr->realm1 != REALM_NONE && player_ptr->pclass != CLASS_ELEMENTALIST) {
         sprintf(buf, "%s.prf", realm_names[player_ptr->realm1]);
         process_pref_file(player_ptr, buf, process_autopick_file_command);
     }
@@ -371,4 +371,4 @@ bool read_histpref(player_type *creature_ptr, void (*process_autopick_file_comma
 
     histpref_buf = NULL;
     return TRUE;
-}
\ No newline at end of file
+}
index 74c6f58..bb05177 100644 (file)
@@ -16,6 +16,7 @@
 #include "io-dump/character-dump.h"
 #include "io/inet.h"
 #include "io/input-key-acceptor.h"
+#include "mind/mind-elementalist.h"
 #include "player/player-class.h"
 #include "player/player-personality.h"
 #include "player/player-race.h"
@@ -406,6 +407,7 @@ errr report_score(player_type *creature_ptr, void (*update_playtime)(void), disp
     BUF *score;
     score = buf_new();
 
+    concptr realm1_name;
     char seikakutmp[128];
     char title[128];
     put_version(title);
@@ -415,6 +417,11 @@ errr report_score(player_type *creature_ptr, void (*update_playtime)(void), disp
     sprintf(seikakutmp, "%s ", ap_ptr->title);
 #endif
 
+    if (creature_ptr->pclass == CLASS_ELEMENTALIST)
+        realm1_name = get_element_title(creature_ptr->realm1);
+    else
+        realm1_name = realm_names[creature_ptr->realm1];
+
     buf_sprintf(score, "name: %s\n", creature_ptr->name);
     buf_sprintf(score, "version: %s\n", title);
     buf_sprintf(score, "score: %d\n", calc_score(creature_ptr));
@@ -428,7 +435,7 @@ errr report_score(player_type *creature_ptr, void (*update_playtime)(void), disp
     buf_sprintf(score, "race: %s\n", rp_ptr->title);
     buf_sprintf(score, "class: %s\n", cp_ptr->title);
     buf_sprintf(score, "seikaku: %s\n", seikakutmp);
-    buf_sprintf(score, "realm1: %s\n", realm_names[creature_ptr->realm1]);
+    buf_sprintf(score, "realm1: %s\n", realm1_name);
     buf_sprintf(score, "realm2: %s\n", realm_names[creature_ptr->realm2]);
     buf_sprintf(score, "killer: %s\n", creature_ptr->died_from);
     buf_sprintf(score, "-----charcter dump-----\n");
index 53ea4b1..265bab2 100644 (file)
@@ -21,6 +21,7 @@
 #include "game-option/input-options.h"
 #include "io/command-repeater.h"
 #include "io/input-key-requester.h"
+#include "mind/mind-elementalist.h"
 #include "mind/mind-sniper.h"
 #include "store/home.h"
 #include "store/museum.h"
@@ -154,6 +155,8 @@ void store_process_command(player_type *client_ptr)
         if ((client_ptr->pclass == CLASS_MINDCRAFTER) || (client_ptr->pclass == CLASS_BERSERKER) || (client_ptr->pclass == CLASS_NINJA)
             || (client_ptr->pclass == CLASS_MIRROR_MASTER))
             do_cmd_mind_browse(client_ptr);
+        else if (client_ptr->pclass == CLASS_ELEMENTALIST)
+            do_cmd_element_browse(client_ptr);
         else if (client_ptr->pclass == CLASS_SMITH)
             do_cmd_kaji(client_ptr, TRUE);
         else if (client_ptr->pclass == CLASS_MAGIC_EATER)
index f0b775f..5dc48e6 100644 (file)
@@ -50,6 +50,8 @@ void safe_setuid_grab(player_type *player_ptr)
         quit(_("setgid(): 正しく許可が取れません!", "setgid(): cannot set permissions correctly!"));
     }
 #else
+    (void)player_ptr;
+
     if (setreuid(geteuid(), getuid()) != 0) {
         quit(_("setreuid(): 正しく許可が取れません!", "setreuid(): cannot set permissions correctly!"));
     }
index 3ea7cab..65a52d5 100644 (file)
@@ -73,7 +73,7 @@ void do_cmd_knowledge_spell_exp(player_type *creature_ptr)
     if (!open_temporary_file(&fff, file_name))
         return;
 
-    if (creature_ptr->realm1 != REALM_NONE) {
+    if (creature_ptr->realm1 != REALM_NONE && creature_ptr->pclass != CLASS_ELEMENTALIST) {
         fprintf(fff, _("%sの魔法書\n", "%s Spellbook\n"), realm_names[creature_ptr->realm1]);
         for (SPELL_IDX i = 0; i < 32; i++) {
             const magic_type *s_ptr;
index 93dfa77..079fe54 100644 (file)
@@ -88,7 +88,7 @@ static void dump_yourself(player_type *creature_ptr, FILE *fff)
     }
 
     fprintf(fff, "\n");
-    if (creature_ptr->realm1) {
+    if (creature_ptr->realm1 && creature_ptr->pclass != CLASS_ELEMENTALIST) {
         shape_buffer(realm_explanations[technic2magic(creature_ptr->realm1) - 1], 78, temp, sizeof(temp));
         fprintf(fff, _("魔法: %s\n", "Realm: %s\n"), realm_names[creature_ptr->realm1]);
 
index 832b961..5b7394f 100644 (file)
@@ -25,7 +25,7 @@ static errr rd_inventory(player_type *player_ptr)
     player_ptr->equip_cnt = 0;
 
     if (player_ptr->inventory_list != NULL)
-        C_WIPE(player_ptr->inventory_list, INVEN_TOTAL, object_type);
+        C_KILL(player_ptr->inventory_list, INVEN_TOTAL, object_type);
     C_MAKE(player_ptr->inventory_list, INVEN_TOTAL, object_type);
 
     int slot = 0;
index 0ad0062..47eceae 100644 (file)
@@ -119,7 +119,7 @@ static void rd_world_info(player_type *creature_ptr)
     if (z_older_than(10, 0, 3))
         determine_daily_bounty(creature_ptr, TRUE);
     else {
-        rd_s16b(&today_mon);
+        rd_s16b(&current_world_ptr->today_mon);
         rd_s16b(&creature_ptr->today_mon);
     }
 }
index 45317ca..87f60bf 100644 (file)
@@ -98,6 +98,11 @@ void set_monster_blow_method(lore_type *lore_ptr, int m)
         lore_ptr->p = _("歌う", "sing");
         lore_ptr->pc = TERM_SLATE;
         break;
+
+    case RBM_NONE:
+    case RBM_SHOOT:
+    case NB_RBM_TYPE:
+        break;
     }
 }
 
@@ -249,5 +254,9 @@ void set_monster_blow_effect(lore_type *lore_ptr, int m)
     case RBE_FLAVOR:
         // フレーバー打撃には何の効果もないので付加説明もない。
         break;
+
+    case RBE_NONE:
+    case NB_RBE_TYPE:
+        break;
     }
 }
index 8300d2b..a4e6a3e 100644 (file)
@@ -4,8 +4,23 @@
 #include "term/screen-processor.h"
 #include "term/term-color-types.h"
 
-concptr wd_he[3] = { _("それ", "it"), _("彼", "he"), _("彼女", "she") };
-concptr wd_his[3] = { _("それの", "its"), _("彼の", "his"), _("彼女の", "her") };
+const who_word_definition Who::words = {
+    { WHO_WORD_TYPE::WHO,
+        {
+            { false, { { MSEX_NONE, _("それ", "it") }, { MSEX_MALE, _("彼", "he") }, { MSEX_FEMALE, _("彼女", "she") } } },
+            { true, { { MSEX_NONE, _("それら", "they") }, { MSEX_MALE, _("彼ら", "they") }, { MSEX_FEMALE, _("彼女ら", "they") } } },
+        } },
+    { WHO_WORD_TYPE::WHOSE,
+        {
+            { false, { { MSEX_NONE, _("それの", "its") }, { MSEX_MALE, _("彼の", "his") }, { MSEX_FEMALE, _("彼女の", "her") } } },
+            { true, { { MSEX_NONE, _("それらの", "their") }, { MSEX_MALE, _("彼らの", "their") }, { MSEX_FEMALE, _("彼女らの", "their") } } },
+        } },
+    { WHO_WORD_TYPE::WHOM,
+        {
+            { false, { { MSEX_NONE, _("それ", "it") }, { MSEX_MALE, _("彼", "him") }, { MSEX_FEMALE, _("彼女", "her") } } },
+            { true, { { MSEX_NONE, _("それら", "them") }, { MSEX_MALE, _("彼ら", "them") }, { MSEX_FEMALE, _("彼女ら", "them") } } },
+        } },
+};
 
 /*
  * Prepare hook for c_roff(). It will be changed for spoiler generation in wizard1.c.
index 1279343..f4609c3 100644 (file)
@@ -2,6 +2,8 @@
 
 #include "system/angband.h"
 #include "system/monster-race-definition.h"
+#include <string>
+#include <unordered_map>
 
 enum monster_sex {
     MSEX_NONE = 0,
@@ -60,8 +62,76 @@ enum monster_lore_mode {
 typedef void (*hook_c_roff_pf)(TERM_COLOR attr, concptr str);
 extern hook_c_roff_pf hook_c_roff;
 
-extern concptr wd_he[3];
-extern concptr wd_his[3];
-
 lore_type *initialize_lore_type(lore_type *lore_ptr, MONRACE_IDX r_idx, monster_lore_mode mode);
 void hooked_roff(concptr str);
+
+enum WHO_WORD_TYPE { WHO = 0, WHOSE = 1, WHOM = 2 };
+using who_word_definition = std::unordered_map<WHO_WORD_TYPE, const std::unordered_map<bool, const std::unordered_map<monster_sex, std::string>>>;
+
+class Who {
+public:
+    static const who_word_definition words;
+
+    /*!
+     * @brief 三人称主格を取得(単数のみ)
+     * @param msex モンスターの性別
+     * @return 主語
+     */
+    static concptr who(monster_sex msex)
+    {
+        return who(msex, false);
+    }
+
+    /*!
+     * @brief 三人称主格を取得
+     * @param msex モンスターの性別
+     * @param multi 複数かどうか
+     * @return 主語
+     */
+    static concptr who(monster_sex msex, bool multi)
+    {
+        return words.at(WHO_WORD_TYPE::WHO).at(multi).at(msex).data();
+    }
+
+    /*!
+     * @brief 三人称所有格を取得(単数のみ)
+     * @param msex モンスターの性別
+     * @return 所有格
+     */
+    static concptr whose(monster_sex msex)
+    {
+        return whose(msex, false);
+    }
+
+    /*!
+     * @brief 三人称所有格を取得
+     * @param msex モンスターの性別
+     * @param multi 複数かどうか
+     * @return 所有格
+     */
+    static concptr whose(monster_sex msex, bool multi)
+    {
+        return words.at(WHO_WORD_TYPE::WHOSE).at(multi).at(msex).data();
+    }
+
+    /*!
+     * @brief 三人称目的格を取得(単数のみ)
+     * @param msex モンスターの性別
+     * @return 目的語
+     */
+    static concptr whom(monster_sex msex)
+    {
+        return whom(msex, false);
+    }
+
+    /*!
+     * @brief 三人称目的格を取得
+     * @param msex モンスターの性別
+     * @param multi 複数かどうか
+     * @return 目的語
+     */
+    static concptr whom(monster_sex msex, bool multi)
+    {
+        return words.at(WHO_WORD_TYPE::WHOM).at(multi).at(msex).data();
+    }
+};
index 522ee70..267e9ab 100644 (file)
@@ -159,7 +159,7 @@ void process_monster_lore(player_type *player_ptr, MONRACE_IDX r_idx, monster_lo
     display_lore_this(player_ptr, lore_ptr);
     display_monster_aura(lore_ptr);
     if (lore_ptr->flags2 & RF2_REFLECTING)
-        hooked_roff(format(_("%^sは矢の呪文を跳ね返す。", "%^s reflects bolt spells.  "), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("%^sは矢の呪文を跳ね返す。", "%^s reflects bolt spells.  "), Who::who(lore_ptr->msex)));
 
     display_monster_collective(lore_ptr);
     lore_ptr->vn = 0;
index 904b073..c5ec734 100644 (file)
@@ -318,11 +318,11 @@ static XImage *ReadBMP(Display *dpy, char *Name)
                return (NULL);
        }
 
-       for (y = 0; y < infoheader.biHeight; y++)
+       for (y = 0; y < static_cast<int>(infoheader.biHeight); y++)
        {
                int y2 = infoheader.biHeight - y - 1;
 
-               for (x = 0; x < infoheader.biWidth; x++)
+               for (x = 0; x < static_cast<int>(infoheader.biWidth); x++)
                {
                        int ch = getc(f);
 
@@ -802,9 +802,9 @@ static XImage *ResizeImage(Display *dpy, XImage *Im,
        Visual *visual = DefaultVisual(dpy, DefaultScreen(dpy));
 
        int width1, height1, width2, height2;
-       int x1, x2, y1, y2, Tx, Ty;
-       int *px1, *px2, *dx1, *dx2;
-       int *py1, *py2, *dy1, *dy2;
+       volatile int x1, x2, y1, y2, Tx, Ty;
+       volatile int *px1, *px2, *dx1, *dx2;
+       volatile int *py1, *py2, *dy1, *dy2;
 
        XImage *Tmp;
 
index 44858c9..8a915c8 100644 (file)
@@ -1,6 +1,6 @@
 /*!
  * todo main関数を含むファイルの割に長過ぎる。main-win-utils.cなどといった形で分割したい
- * @file main-win.c
+ * @file main-win.cpp
  * @brief Windows版固有実装(メインエントリポイント含む)
  * @date 2018/03/16
  * @author Hengband Team
  * </p>
  */
 
+#ifdef WINDOWS
+
+#include <cstdlib>
+
 #include "autopick/autopick-pref-processor.h"
 #include "cmd-io/cmd-process-screen.h"
 #include "cmd-io/cmd-save.h"
 #include "dungeon/quest.h"
 #include "floor/floor-base-definitions.h"
 #include "floor/floor-events.h"
-#include "game-option/game-play-options.h"
 #include "game-option/runtime-arguments.h"
 #include "game-option/special-options.h"
 #include "io/files-util.h"
-#include "io/inet.h"
 #include "io/input-key-acceptor.h"
 #include "io/record-play-movie.h"
 #include "io/signal-handlers.h"
 #include "io/write-diary.h"
+#include "main-win/main-win-bg.h"
+#include "main-win/main-win-file-utils.h"
+#include "main-win/main-win-mci.h"
+#include "main-win/main-win-music.h"
+#include "main-win/main-win-sound.h"
 #include "main/angband-initializer.h"
-#include "main/music-definitions-table.h"
-#include "main/sound-definitions-table.h"
 #include "main/sound-of-music.h"
 #include "monster-floor/monster-lite.h"
+#include "save/save.h"
 #include "system/angband-version.h"
 #include "system/angband.h"
 #include "system/floor-type-definition.h"
 #include "wizard/wizard-spoiler.h"
 #include "world/world.h"
 
-#ifdef WINDOWS
-#include "dungeon/dungeon.h"
-#include "save/save.h"
+#include <commdlg.h>
 #include <direct.h>
 #include <locale.h>
-#include <windows.h>
+
+/*
+ * Include the support for loading bitmaps
+ */
+#include "term/readdib.h"
+
+#define MOUSE_SENS 40
 
 /*
  * Available graphic modes
 #define IDM_HELP_CONTENTS 901
 
 /*
- * Exclude parts of WINDOWS.H that are not needed (Win32)
- */
-#define WIN32_LEAN_AND_MEAN
-#define NONLS /* All NLS defines and routines */
-#define NOSERVICE /* All Service Controller routines, SERVICE_ equates, etc. */
-#define NOMCX /* Modem Configuration Extensions */
-
-/*
- * Include the "windows" support file
- */
-#include <windows.h>
-
-/*
- * Exclude parts of MMSYSTEM.H that are not needed
- */
-#define MMNODRV /* Installable driver support */
-#define MMNOWAVE /* Waveform support */
-#define MMNOMIDI /* MIDI support */
-#define MMNOAUX /* Auxiliary audio support */
-#define MMNOTIMER /* Timer support */
-#define MMNOJOY /* Joystick support */
-#define MMNOMCI /* MCI support */
-#define MMNOMMIO /* Multimedia file I/O support */
-
-#define INVALID_FILE_NAME (DWORD)0xFFFFFFFF
-#define MOUSE_SENS 40
-
-/*
- * Include some more files. Note: the Cygnus Cygwin compiler
- * doesn't use mmsystem.h instead it includes the winmm library
- * which performs a similar function.
- */
-#include <commdlg.h>
-#include <mmsystem.h>
-
-/*
- * Include the support for loading bitmaps
- */
-#include "term/readdib.h"
-
-#define MoveTo(H, X, Y) MoveToEx(H, X, Y, NULL)
-
-/*
  * Foreground color bits
  */
 #define VID_BLACK 0x00
@@ -420,9 +387,8 @@ static HICON hIcon;
 static HPALETTE hPal;
 
 /* bg */
-static HBITMAP hBG = NULL;
-static int use_bg = 0; //!< 背景使用フラグ、1なら私用。
-static char bg_bitmap_file[1024] = "bg.bmp"; //!< 現在の背景ビットマップファイル名。
+static int use_bg = 0; //!< 背景使用フラグ、1なら使用。
+#define DEFAULT_BG_FILENAME "bg.bmp"
 
 /*
  * The screen saver window
@@ -455,26 +421,11 @@ static bool can_use_sound = FALSE;
  */
 static bool keep_subwindows = TRUE;
 
-#define SAMPLE_SOUND_MAX 16
 /*
- * An array of sound file names
+ * Flag set once "music" has been initialized
  */
-static concptr sound_file[SOUND_MAX][SAMPLE_SOUND_MAX];
-
-#define SAMPLE_MUSIC_MAX 16
-static concptr music_file[MUSIC_BASIC_MAX][SAMPLE_MUSIC_MAX];
-static concptr dungeon_music_file[1000][SAMPLE_MUSIC_MAX];
-static concptr town_music_file[1000][SAMPLE_MUSIC_MAX];
-static concptr quest_music_file[1000][SAMPLE_MUSIC_MAX];
 static bool can_use_music = FALSE;
 
-static MCI_OPEN_PARMS mop;
-static char mci_device_type[256];
-
-static int current_music_type = TERM_XTRA_MUSIC_MUTE;
-static int current_music_id = 0;
-static char current_music_path[1024];
-
 /*
  * Full path to ANGBAND.INI
  */
@@ -494,9 +445,7 @@ static concptr AngList = "AngList";
  * Directory names
  */
 static concptr ANGBAND_DIR_XTRA_GRAF;
-static concptr ANGBAND_DIR_XTRA_SOUND;
 static concptr ANGBAND_DIR_XTRA_HELP;
-static concptr ANGBAND_DIR_XTRA_MUSIC;
 
 /*
  * The "complex" color values
@@ -565,107 +514,6 @@ static byte ignore_key_list[] = {
     VK_SHIFT, VK_CONTROL, VK_MENU, VK_LWIN, VK_RWIN, VK_LSHIFT, VK_RSHIFT, VK_LCONTROL, VK_RCONTROL, VK_LMENU, VK_RMENU, 0 /* End of List */
 };
 
-/* Function prototype */
-
-static bool is_already_running(void);
-
-/* bg */
-static void delete_bg(void)
-{
-    if (hBG != NULL) {
-        DeleteObject(hBG);
-        hBG = NULL;
-    }
-}
-
-static int init_bg(void)
-{
-    char *bmfile = bg_bitmap_file;
-    delete_bg();
-    if (use_bg == 0)
-        return 0;
-
-    hBG = static_cast<HBITMAP>(LoadImage(NULL, bmfile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE));
-    if (!hBG) {
-        plog_fmt(_("壁紙用ビットマップ '%s' を読み込めません。", "Can't load the bitmap file '%s'."), bmfile);
-        use_bg = 0;
-        return 0;
-    }
-
-    use_bg = 1;
-    return 1;
-}
-
-static void DrawBG(HDC hdc, RECT *r)
-{
-    if (!use_bg || !hBG)
-        return;
-
-    int x = r->left, y = r->top;
-    int nx = x;
-    int ny = y;
-    BITMAP bm;
-    GetObject(hBG, sizeof(bm), &bm);
-    int swid = bm.bmWidth;
-    int shgt = bm.bmHeight;
-
-    HDC hdcSrc = CreateCompatibleDC(hdc);
-    HBITMAP hOld = static_cast<HBITMAP>(SelectObject(hdcSrc, hBG));
-
-    do {
-        int sx = nx % swid;
-        int cwid = MIN(swid - sx, r->right - nx);
-        do {
-            int sy = ny % shgt;
-            int chgt = MIN(shgt - sy, r->bottom - ny);
-            BitBlt(hdc, nx, ny, cwid, chgt, hdcSrc, sx, sy, SRCCOPY);
-            ny += chgt;
-        } while (ny < r->bottom);
-
-        ny = y;
-        nx += cwid;
-    } while (nx < r->right);
-
-    SelectObject(hdcSrc, hOld);
-    DeleteDC(hdcSrc);
-}
-
-/*
- * Check for existance of a file
- */
-static bool check_file(concptr s)
-{
-    char path[1024];
-    strcpy(path, s);
-    DWORD attrib = GetFileAttributes(path);
-    if (attrib == INVALID_FILE_NAME)
-        return FALSE;
-    if (attrib & FILE_ATTRIBUTE_DIRECTORY)
-        return FALSE;
-
-    return TRUE;
-}
-
-/*
- * Check for existance of a directory
- */
-static bool check_dir(concptr s)
-{
-    char path[1024];
-    strcpy(path, s);
-    int i = strlen(path);
-    if (i && (path[i - 1] == '\\'))
-        path[--i] = '\0';
-
-    DWORD attrib = GetFileAttributes(path);
-    if (attrib == INVALID_FILE_NAME)
-        return FALSE;
-    if (!(attrib & FILE_ATTRIBUTE_DIRECTORY))
-        return FALSE;
-
-    return TRUE;
-}
-
 /*
  * Validate a file
  */
@@ -826,7 +674,7 @@ static void save_prefs(void)
 
     strcpy(buf, use_bg ? "1" : "0");
     WritePrivateProfileString("Angband", "BackGround", buf, ini_file);
-    WritePrivateProfileString("Angband", "BackGroundBitmap", bg_bitmap_file[0] != '\0' ? bg_bitmap_file : "bg.bmp", ini_file);
+    WritePrivateProfileString("Angband", "BackGroundBitmap", bg_bitmap_file[0] != '\0' ? bg_bitmap_file : DEFAULT_BG_FILENAME, ini_file);
 
     int path_length = strlen(ANGBAND_DIR) - 4; /* \libの4文字分を削除 */
     char tmp[1024] = "";
@@ -917,7 +765,7 @@ static void load_prefs(void)
     arg_sound = (GetPrivateProfileInt("Angband", "Sound", 0, ini_file) != 0);
     arg_music = (GetPrivateProfileInt("Angband", "Music", 0, ini_file) != 0);
     use_bg = GetPrivateProfileInt("Angband", "BackGround", 0, ini_file);
-    GetPrivateProfileString("Angband", "BackGroundBitmap", "bg.bmp", bg_bitmap_file, 1023, ini_file);
+    GetPrivateProfileString("Angband", "BackGroundBitmap", DEFAULT_BG_FILENAME, bg_bitmap_file, 1023, ini_file);
     GetPrivateProfileString("Angband", "SaveFile", "", savefile, 1023, ini_file);
 
     int n = strncmp(".\\", savefile, 2);
@@ -936,130 +784,6 @@ static void load_prefs(void)
 }
 
 /*
- * - Taken from files.c.
- *
- * Extract "tokens" from a buffer
- *
- * This function uses "whitespace" as delimiters, and treats any amount of
- * whitespace as a single delimiter.  We will never return any empty tokens.
- * When given an empty buffer, or a buffer containing only "whitespace", we
- * will return no tokens.  We will never extract more than "num" tokens.
- *
- * By running a token through the "text_to_ascii()" function, you can allow
- * that token to include (encoded) whitespace, using "\s" to encode spaces.
- *
- * We save pointers to the tokens in "tokens", and return the number found.
- */
-static s16b tokenize_whitespace(char *buf, s16b num, char **tokens)
-{
-    s16b k = 0;
-    char *s = buf;
-
-    while (k < num) {
-        char *t;
-        for (; *s && iswspace(*s); ++s) /* loop */
-            ;
-
-        if (!*s)
-            break;
-
-        for (t = s; *t && !iswspace(*t); ++t) /* loop */
-            ;
-
-        if (*t)
-            *t++ = '\0';
-
-        tokens[k++] = s;
-        s = t;
-    }
-
-    return k;
-}
-
-static void load_sound_prefs(void)
-{
-    char tmp[1024];
-    char ini_path[1024];
-    char wav_path[1024];
-    char *zz[SAMPLE_SOUND_MAX];
-
-    path_build(ini_path, 1024, ANGBAND_DIR_XTRA_SOUND, "sound_debug.cfg");
-    if (GetPrivateProfileString("Device", "type", "", mci_device_type, 256, ini_path) == 0) {
-        path_build(ini_path, 1024, ANGBAND_DIR_XTRA_SOUND, "sound.cfg");
-        GetPrivateProfileString("Device", "type", "", mci_device_type, 256, ini_path);
-    }
-
-    for (int i = 0; i < SOUND_MAX; i++) {
-        GetPrivateProfileString("Sound", angband_sound_name[i], "", tmp, 1024, ini_path);
-        int num = tokenize_whitespace(tmp, SAMPLE_SOUND_MAX, zz);
-        for (int j = 0; j < num; j++) {
-            path_build(wav_path, 1024, ANGBAND_DIR_XTRA_SOUND, zz[j]);
-            if (check_file(wav_path))
-                sound_file[i][j] = string_make(zz[j]);
-        }
-    }
-}
-
-static void load_music_prefs(void)
-{
-    char tmp[1024];
-    char ini_path[1024];
-    char wav_path[1024];
-    char *zz[SAMPLE_MUSIC_MAX];
-    char key[80];
-
-    path_build(ini_path, 1024, ANGBAND_DIR_XTRA_MUSIC, "music_debug.cfg");
-    if (GetPrivateProfileString("Device", "type", "", mci_device_type, 256, ini_path) == 0) {
-        path_build(ini_path, 1024, ANGBAND_DIR_XTRA_MUSIC, "music.cfg");
-        GetPrivateProfileString("Device", "type", "", mci_device_type, 256, ini_path);
-    }
-
-    for (int i = 0; i < MUSIC_BASIC_MAX; i++) {
-        GetPrivateProfileString("Basic", angband_music_basic_name[i], "", tmp, 1024, ini_path);
-        int num = tokenize_whitespace(tmp, SAMPLE_MUSIC_MAX, zz);
-        for (int j = 0; j < num; j++) {
-            path_build(wav_path, 1024, ANGBAND_DIR_XTRA_MUSIC, zz[j]);
-            if (check_file(wav_path))
-                music_file[i][j] = string_make(zz[j]);
-        }
-    }
-
-    for (int i = 0; i < current_world_ptr->max_d_idx; i++) {
-        sprintf(key, "dungeon%03d", i);
-        GetPrivateProfileString("Dungeon", key, "", tmp, 1024, ini_path);
-        int num = tokenize_whitespace(tmp, SAMPLE_MUSIC_MAX, zz);
-        for (int j = 0; j < num; j++) {
-            path_build(wav_path, 1024, ANGBAND_DIR_XTRA_MUSIC, zz[j]);
-            if (check_file(wav_path))
-                dungeon_music_file[i][j] = string_make(zz[j]);
-        }
-    }
-
-    for (int i = 0; i < max_q_idx; i++) {
-        sprintf(key, "quest%03d", i);
-        GetPrivateProfileString("Quest", key, "", tmp, 1024, ini_path);
-        int num = tokenize_whitespace(tmp, SAMPLE_MUSIC_MAX, zz);
-        for (int j = 0; j < num; j++) {
-            path_build(wav_path, 1024, ANGBAND_DIR_XTRA_MUSIC, zz[j]);
-            if (check_file(wav_path))
-                quest_music_file[i][j] = string_make(zz[j]);
-        }
-    }
-
-    for (int i = 0; i < 1000; i++) /*!< @todo 町最大数指定 */
-    {
-        sprintf(key, "town%03d", i);
-        GetPrivateProfileString("Town", key, "", tmp, 1024, ini_path);
-        int num = tokenize_whitespace(tmp, SAMPLE_MUSIC_MAX, zz);
-        for (int j = 0; j < num; j++) {
-            path_build(wav_path, 1024, ANGBAND_DIR_XTRA_MUSIC, zz[j]);
-            if (check_file(wav_path))
-                town_music_file[i][j] = string_make(zz[j]);
-        }
-    }
-}
-
-/*
  * Create the new global palette based on the bitmap palette
  * (if any), and the standard 16 entry palette derived from
  * "win_clr[]" which is used for the basic 16 Angband colors.
@@ -1089,17 +813,17 @@ static int new_palette(void)
     HPALETTE hBmPal = static_cast<HPALETTE>(infGraph.hPalette);
     if (hBmPal) {
         lppeSize = 256 * sizeof(PALETTEENTRY);
-        lppe = (LPPALETTEENTRY)ralloc(lppeSize);
+        lppe = (LPPALETTEENTRY)std::malloc(lppeSize);
         nEntries = GetPaletteEntries(hBmPal, 0, 255, lppe);
         if ((nEntries == 0) || (nEntries > 220)) {
             plog(_("画面を16ビットか24ビットカラーモードにして下さい。", "Please switch to high- or true-color mode."));
-            rnfree(lppe, lppeSize);
+            std::free(lppe);
             return FALSE;
         }
     }
 
     pLogPalSize = sizeof(LOGPALETTE) + (nEntries + 16) * sizeof(PALETTEENTRY);
-    pLogPal = (LPLOGPALETTE)ralloc(pLogPalSize);
+    pLogPal = (LPLOGPALETTE)std::malloc(pLogPalSize);
     pLogPal->palVersion = 0x300;
     pLogPal->palNumEntries = nEntries + 16;
     for (i = 0; i < nEntries; i++) {
@@ -1116,13 +840,13 @@ static int new_palette(void)
     }
 
     if (lppe)
-        rnfree(lppe, lppeSize);
+        std::free(lppe);
 
     HPALETTE hNewPal = CreatePalette(pLogPal);
     if (!hNewPal)
         quit(_("パレットを作成できません!", "Cannot create palette!"));
 
-    rnfree(pLogPal, pLogPalSize);
+    std::free(pLogPal);
     td = &data[0];
     HDC hdc = GetDC(td->w);
     SelectPalette(hdc, hNewPal, 0);
@@ -1234,24 +958,12 @@ static bool init_graphics(void)
 static void init_music(void)
 {
     if (!can_use_music) {
-        load_music_prefs();
+        main_win_music::load_music_prefs(current_world_ptr->max_d_idx, max_q_idx);
         can_use_music = TRUE;
     }
 }
 
 /*
- * Hack -- Stop a music
- */
-static void stop_music(void)
-{
-    mciSendCommand(mop.wDeviceID, MCI_STOP, 0, 0);
-    mciSendCommand(mop.wDeviceID, MCI_CLOSE, 0, 0);
-    current_music_type = TERM_XTRA_MUSIC_MUTE;
-    current_music_id = 0;
-    strcpy(current_music_path, "\0");
-}
-
-/*
  * Initialize sound
  */
 static void init_sound(void)
@@ -1339,7 +1051,10 @@ static void term_change_font(term_data *td)
 /*
  * Allow the user to lock this window.
  */
-static void term_window_pos(term_data *td, HWND hWnd) { SetWindowPos(td->w, hWnd, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); }
+static void term_window_pos(term_data *td, HWND hWnd)
+{
+    SetWindowPos(td->w, hWnd, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
+}
 
 static void windows_map(player_type *player_ptr);
 
@@ -1423,7 +1138,7 @@ static errr term_xtra_win_react(player_type *player_ptr)
         init_music();
         use_music = arg_music;
         if (!arg_music)
-            stop_music();
+            main_win_music::stop_music();
         else
             select_floor_music(player_ptr);
     }
@@ -1510,7 +1225,7 @@ static errr term_xtra_win_clear(void)
     if (use_bg) {
         rc.left = 0;
         rc.top = 0;
-        DrawBG(hdc, &rc);
+        draw_bg(hdc, &rc);
     }
 
     ReleaseDC(td->w, hdc);
@@ -1531,23 +1246,9 @@ static errr term_xtra_win_noise(void)
  */
 static errr term_xtra_win_sound(int v)
 {
-    char buf[1024];
     if (!use_sound)
         return 1;
-    if ((v < 0) || (v >= SOUND_MAX))
-        return 1;
-
-    int i;
-    for (i = 0; i < SAMPLE_SOUND_MAX; i++) {
-        if (!sound_file[v][i])
-            break;
-    }
-
-    if (i == 0)
-        return 1;
-
-    path_build(buf, 1024, ANGBAND_DIR_XTRA_SOUND, sound_file[v][Rand_external(i)]);
-    return (PlaySound(buf, 0, SND_FILENAME | SND_ASYNC));
+    return play_sound(v);
 }
 
 /*
@@ -1555,82 +1256,15 @@ static errr term_xtra_win_sound(int v)
  */
 static errr term_xtra_win_music(int n, int v)
 {
-    int i = 0;
-    char buf[1024];
-
+    // FIXME use_musicの値に関わらずミュートを実行している
     if (n == TERM_XTRA_MUSIC_MUTE)
-        stop_music();
+        main_win_music::stop_music();
 
     if (!use_music) {
         return 1;
     }
 
-    if (n == TERM_XTRA_MUSIC_BASIC && ((v < 0) || (v >= MUSIC_BASIC_MAX)))
-        return 1;
-    else if (v < 0 || v >= 1000)
-        return (1); /*!< TODO */
-
-    switch (n) {
-    case TERM_XTRA_MUSIC_BASIC:
-        for (i = 0; i < SAMPLE_MUSIC_MAX; i++)
-            if (!music_file[v][i])
-                break;
-        break;
-    case TERM_XTRA_MUSIC_DUNGEON:
-        for (i = 0; i < SAMPLE_MUSIC_MAX; i++)
-            if (!dungeon_music_file[v][i])
-                break;
-        break;
-    case TERM_XTRA_MUSIC_QUEST:
-        for (i = 0; i < SAMPLE_MUSIC_MAX; i++)
-            if (!quest_music_file[v][i])
-                break;
-        break;
-    case TERM_XTRA_MUSIC_TOWN:
-        for (i = 0; i < SAMPLE_MUSIC_MAX; i++)
-            if (!town_music_file[v][i])
-                break;
-        break;
-    }
-
-    if (i == 0) {
-        return 1;
-    }
-
-    switch (n) {
-    case TERM_XTRA_MUSIC_BASIC:
-        path_build(buf, 1024, ANGBAND_DIR_XTRA_MUSIC, music_file[v][Rand_external(i)]);
-        break;
-    case TERM_XTRA_MUSIC_DUNGEON:
-        path_build(buf, 1024, ANGBAND_DIR_XTRA_MUSIC, dungeon_music_file[v][Rand_external(i)]);
-        break;
-    case TERM_XTRA_MUSIC_QUEST:
-        path_build(buf, 1024, ANGBAND_DIR_XTRA_MUSIC, quest_music_file[v][Rand_external(i)]);
-        break;
-    case TERM_XTRA_MUSIC_TOWN:
-        path_build(buf, 1024, ANGBAND_DIR_XTRA_MUSIC, town_music_file[v][Rand_external(i)]);
-        break;
-    }
-
-    if (current_music_type == n && current_music_id == v)
-        return 0;
-
-    if (current_music_type != TERM_XTRA_MUSIC_MUTE && n != TERM_XTRA_MUSIC_MUTE)
-        if (0 == strcmp(current_music_path, buf))
-            return 0;
-
-    current_music_type = n;
-    current_music_id = v;
-    strcpy(current_music_path, buf);
-
-    mop.lpstrDeviceType = mci_device_type;
-    mop.lpstrElementName = buf;
-    mciSendCommand(mop.wDeviceID, MCI_STOP, 0, 0);
-    mciSendCommand(mop.wDeviceID, MCI_CLOSE, 0, 0);
-    mciSendCommand(mop.wDeviceID, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT, (DWORD)&mop);
-    mciSendCommand(mop.wDeviceID, MCI_SEEK, MCI_SEEK_TO_START, 0);
-    mciSendCommand(mop.wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)&mop);
-    return 0;
+    return main_win_music::play_music(n, v);
 }
 
 /*
@@ -1761,7 +1395,7 @@ static errr term_wipe_win(int x, int y, int n)
     SetBkColor(hdc, RGB(0, 0, 0));
     SelectObject(hdc, td->font_id);
     if (use_bg)
-        DrawBG(hdc, &rc);
+        draw_bg(hdc, &rc);
     else
         ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
 
@@ -1817,7 +1451,7 @@ static errr term_text_win(int x, int y, int n, TERM_COLOR a, concptr s)
     if (td->bizarre || (td->tile_hgt != td->font_hgt) || (td->tile_wid != td->font_wid)) {
         ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
         if (use_bg)
-            DrawBG(hdc, &rc);
+            draw_bg(hdc, &rc);
 
         rc.left += ((td->tile_wid - td->font_wid) / 2);
         rc.right = rc.left + td->font_wid;
@@ -2714,7 +2348,12 @@ static void process_menus(player_type *player_ptr, WORD wCmd)
         }
 
         use_bg = !use_bg;
-        init_bg();
+        if (use_bg) {
+            use_bg = init_bg();
+        } else {
+            delete_bg();
+        }
+
         term_xtra_win_react(player_ptr);
         term_key_push(KTRL('R'));
         break;
@@ -2737,8 +2376,7 @@ static void process_menus(player_type *player_ptr, WORD wCmd)
         ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
 
         if (GetOpenFileName(&ofn)) {
-            use_bg = 1;
-            init_bg();
+            use_bg = init_bg();
         }
 
         term_xtra_win_react(player_ptr);
@@ -2858,6 +2496,7 @@ static bool process_keydown(WPARAM wParam, LPARAM lParam)
         switch (wParam) {
         case VK_DIVIDE:
             term_no_press = TRUE;
+            [[fallthrough]]; /* Fall through */
         case VK_RETURN:
             numpad = ext_key;
             break;
@@ -2877,6 +2516,7 @@ static bool process_keydown(WPARAM wParam, LPARAM lParam)
         case VK_SEPARATOR:
         case VK_DECIMAL:
             term_no_press = TRUE;
+            [[fallthrough]]; /* Fall through */
         case VK_CLEAR:
         case VK_HOME:
         case VK_END:
@@ -3175,6 +2815,7 @@ LRESULT PASCAL AngbandWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam
         if ((HWND)wParam == hWnd)
             return 0;
     }
+        [[fallthrough]]; /* Fall through */
     case WM_QUERYNEWPALETTE: {
         if (!paletted)
             return 0;
@@ -3214,6 +2855,7 @@ LRESULT PASCAL AngbandWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam
             }
         }
     }
+        [[fallthrough]]; /* Fall through */
     case WM_ENABLE: {
         if (wParam == FALSE && keep_subwindows) {
             for (int i = 0; i < MAX_TERM_DATA; i++) {
@@ -3314,6 +2956,7 @@ LRESULT PASCAL AngbandListProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lPara
         if ((HWND)wParam == hWnd)
             return FALSE;
     }
+        [[fallthrough]]; /* Fall through */
     case WM_QUERYNEWPALETTE: {
         if (!paletted)
             return 0;
@@ -3591,7 +3234,7 @@ static bool is_already_running(void)
 /*!
  * @brief (Windows固有)Windowsアプリケーションとしてのエントリポイント
  */
-int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
+int WINAPI WinMain(_In_ HINSTANCE hInst, _In_opt_ HINSTANCE hPrevInst, _In_ LPSTR lpCmdLine, _In_ int nCmdShow)
 {
     WNDCLASS wc;
     HDC hdc;
@@ -3682,7 +3325,9 @@ int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC
     }
 
     init_windows();
-    init_bg();
+    if (use_bg) {
+        use_bg = init_bg();
+    }
 
     plog_aux = hook_plog;
     quit_aux = hook_quit;
diff --git a/src/main-win/main-win-bg.cpp b/src/main-win/main-win-bg.cpp
new file mode 100644 (file)
index 0000000..dbf3c9b
--- /dev/null
@@ -0,0 +1,77 @@
+/*!
+ * @file main-win-bg.cpp
+ * @brief Windows版固有実装(壁紙)
+ */
+
+#include "locale/language-switcher.h"
+#include "main-win/main-win-bg.h"
+#include "system/h-define.h"
+#include "term/z-form.h"
+
+static HBITMAP hBG = NULL;
+char bg_bitmap_file[MAIN_WIN_MAX_PATH] = ""; //!< 現在の背景ビットマップファイル名。
+
+void delete_bg(void)
+{
+    if (hBG != NULL) {
+        DeleteObject(hBG);
+        hBG = NULL;
+    }
+}
+
+static BOOL init_bg_impl(void)
+{
+    char *bmfile = bg_bitmap_file;
+    delete_bg();
+
+    hBG = static_cast<HBITMAP>(LoadImage(NULL, bmfile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE));
+    if (!hBG) {
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+BOOL init_bg(void)
+{
+    BOOL result = init_bg_impl();
+    if (!result) {
+        plog_fmt(_("壁紙用ビットマップ '%s' を読み込めません。", "Can't load the bitmap file '%s'."), bg_bitmap_file);
+    }
+
+    return result;
+}
+
+void draw_bg(HDC hdc, RECT *r)
+{
+    if (!hBG)
+        return;
+
+    int x = r->left, y = r->top;
+    int nx = x;
+    int ny = y;
+    BITMAP bm;
+    GetObject(hBG, sizeof(bm), &bm);
+    int swid = bm.bmWidth;
+    int shgt = bm.bmHeight;
+
+    HDC hdcSrc = CreateCompatibleDC(hdc);
+    HBITMAP hOld = static_cast<HBITMAP>(SelectObject(hdcSrc, hBG));
+
+    do {
+        int sx = nx % swid;
+        int cwid = MIN(swid - sx, r->right - nx);
+        do {
+            int sy = ny % shgt;
+            int chgt = MIN(shgt - sy, r->bottom - ny);
+            BitBlt(hdc, nx, ny, cwid, chgt, hdcSrc, sx, sy, SRCCOPY);
+            ny += chgt;
+        } while (ny < r->bottom);
+
+        ny = y;
+        nx += cwid;
+    } while (nx < r->right);
+
+    SelectObject(hdcSrc, hOld);
+    DeleteDC(hdcSrc);
+}
diff --git a/src/main-win/main-win-bg.h b/src/main-win/main-win-bg.h
new file mode 100644 (file)
index 0000000..e71712a
--- /dev/null
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "main-win/main-win-windows.h"
+#include "main-win/main-win-define.h"
+
+extern char bg_bitmap_file[MAIN_WIN_MAX_PATH]; //!< 現在の背景ビットマップファイル名。
+
+void delete_bg(void);
+BOOL init_bg(void);
+void draw_bg(HDC hdc, RECT *r);
diff --git a/src/main-win/main-win-define.h b/src/main-win/main-win-define.h
new file mode 100644 (file)
index 0000000..5ef951a
--- /dev/null
@@ -0,0 +1,6 @@
+#pragma once
+
+// max. length of full pathname
+#define MAIN_WIN_MAX_PATH 1024
+
+#define INVALID_FILE_NAME (DWORD)0xFFFFFFFF
diff --git a/src/main-win/main-win-file-utils.cpp b/src/main-win/main-win-file-utils.cpp
new file mode 100644 (file)
index 0000000..5709d65
--- /dev/null
@@ -0,0 +1,44 @@
+/*!
+ * @file main-win-file-utils.cpp
+ * @brief Windows版固有実装(ファイル関連処理)
+ */
+
+#include "main-win/main-win-file-utils.h"
+#include "main-win/main-win-define.h"
+#include "main-win/main-win-windows.h"
+
+/*
+ * Check for existance of a file
+ */
+bool check_file(concptr s)
+{
+    char path[MAIN_WIN_MAX_PATH];
+    strcpy(path, s);
+    DWORD attrib = GetFileAttributes(path);
+    if (attrib == INVALID_FILE_NAME)
+        return FALSE;
+    if (attrib & FILE_ATTRIBUTE_DIRECTORY)
+        return FALSE;
+
+    return TRUE;
+}
+
+/*
+ * Check for existance of a directory
+ */
+bool check_dir(concptr s)
+{
+    char path[MAIN_WIN_MAX_PATH];
+    strcpy(path, s);
+    int i = strlen(path);
+    if (i && (path[i - 1] == '\\'))
+        path[--i] = '\0';
+
+    DWORD attrib = GetFileAttributes(path);
+    if (attrib == INVALID_FILE_NAME)
+        return FALSE;
+    if (!(attrib & FILE_ATTRIBUTE_DIRECTORY))
+        return FALSE;
+
+    return TRUE;
+}
diff --git a/src/main-win/main-win-file-utils.h b/src/main-win/main-win-file-utils.h
new file mode 100644 (file)
index 0000000..0fc0b3f
--- /dev/null
@@ -0,0 +1,6 @@
+#pragma once
+
+#include "system/h-type.h"
+
+bool check_file(concptr s);
+bool check_dir(concptr s);
diff --git a/src/main-win/main-win-mci.cpp b/src/main-win/main-win-mci.cpp
new file mode 100644 (file)
index 0000000..31945f8
--- /dev/null
@@ -0,0 +1,9 @@
+/*!
+ * @file main-win-mci.cpp
+ * @brief Windows版固有実装(BGM再生用のMCI)
+ */
+
+#include "main-win/main-win-mci.h"
+
+MCI_OPEN_PARMS mop;
+char mci_device_type[MCI_DEVICE_TYPE_MAX_LENGTH];
diff --git a/src/main-win/main-win-mci.h b/src/main-win/main-win-mci.h
new file mode 100644 (file)
index 0000000..cde4e89
--- /dev/null
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "main-win/main-win-windows.h"
+
+#include <mciapi.h>
+
+extern MCI_OPEN_PARMS mop;
+#define MCI_DEVICE_TYPE_MAX_LENGTH 256
+extern char mci_device_type[MCI_DEVICE_TYPE_MAX_LENGTH];
diff --git a/src/main-win/main-win-mmsystem.h b/src/main-win/main-win-mmsystem.h
new file mode 100644 (file)
index 0000000..8314343
--- /dev/null
@@ -0,0 +1,22 @@
+#pragma once
+
+#include "main-win/main-win-windows.h"
+
+/*
+ * Exclude parts of MMSYSTEM.H that are not needed
+ */
+#define MMNODRV /* Installable driver support */
+#define MMNOWAVE /* Waveform support */
+#define MMNOMIDI /* MIDI support */
+#define MMNOAUX /* Auxiliary audio support */
+#define MMNOTIMER /* Timer support */
+#define MMNOJOY /* Joystick support */
+#define MMNOMCI /* MCI support */
+#define MMNOMMIO /* Multimedia file I/O support */
+
+/*
+ * Include some more files. Note: the Cygnus Cygwin compiler
+ * doesn't use mmsystem.h instead it includes the winmm library
+ * which performs a similar function.
+ */
+#include <mmsystem.h>
diff --git a/src/main-win/main-win-music.cpp b/src/main-win/main-win-music.cpp
new file mode 100644 (file)
index 0000000..41d557e
--- /dev/null
@@ -0,0 +1,181 @@
+/*!
+ * @file main-win-music.cpp
+ * @brief Windows版固有実装(BGM)
+ */
+
+#include "main-win/main-win-music.h"
+#include "main-win/main-win-define.h"
+#include "main-win/main-win-file-utils.h"
+#include "main-win/main-win-mci.h"
+#include "main-win/main-win-mmsystem.h"
+#include "main-win/main-win-tokenizer.h"
+#include "term/z-term.h"
+#include "util/angband-files.h"
+
+concptr music_file[MUSIC_BASIC_MAX][SAMPLE_MUSIC_MAX];
+// TODO マジックナンバー除去
+concptr dungeon_music_file[1000][SAMPLE_MUSIC_MAX];
+concptr town_music_file[1000][SAMPLE_MUSIC_MAX];
+concptr quest_music_file[1000][SAMPLE_MUSIC_MAX];
+
+static int current_music_type = TERM_XTRA_MUSIC_MUTE;
+static int current_music_id = 0;
+static char current_music_path[MAIN_WIN_MAX_PATH];
+
+/*
+ * Directory name
+ */
+concptr ANGBAND_DIR_XTRA_MUSIC;
+
+namespace main_win_music {
+void load_music_prefs(DUNGEON_IDX max_d_idx, QUEST_IDX max_q_idx)
+{
+    char tmp[MAIN_WIN_MAX_PATH];
+    char ini_path[MAIN_WIN_MAX_PATH];
+    char wav_path[MAIN_WIN_MAX_PATH];
+    char *zz[SAMPLE_MUSIC_MAX];
+    // TODO マジックナンバー除去
+    char key[80];
+
+    path_build(ini_path, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_MUSIC, "music_debug.cfg");
+    if (GetPrivateProfileString("Device", "type", "", mci_device_type, _countof(mci_device_type), ini_path) == 0) {
+        path_build(ini_path, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_MUSIC, "music.cfg");
+        GetPrivateProfileString("Device", "type", "", mci_device_type, _countof(mci_device_type), ini_path);
+    }
+
+    for (int i = 0; i < MUSIC_BASIC_MAX; i++) {
+        GetPrivateProfileString("Basic", angband_music_basic_name[i], "", tmp, MAIN_WIN_MAX_PATH, ini_path);
+        int num = tokenize_whitespace(tmp, SAMPLE_MUSIC_MAX, zz);
+        for (int j = 0; j < num; j++) {
+            path_build(wav_path, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_MUSIC, zz[j]);
+            if (check_file(wav_path))
+                music_file[i][j] = string_make(zz[j]);
+        }
+    }
+
+    for (int i = 0; i < max_d_idx; i++) {
+        sprintf(key, "dungeon%03d", i);
+        GetPrivateProfileString("Dungeon", key, "", tmp, MAIN_WIN_MAX_PATH, ini_path);
+        int num = tokenize_whitespace(tmp, SAMPLE_MUSIC_MAX, zz);
+        for (int j = 0; j < num; j++) {
+            path_build(wav_path, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_MUSIC, zz[j]);
+            if (check_file(wav_path))
+                dungeon_music_file[i][j] = string_make(zz[j]);
+        }
+    }
+
+    for (int i = 0; i < max_q_idx; i++) {
+        sprintf(key, "quest%03d", i);
+        GetPrivateProfileString("Quest", key, "", tmp, MAIN_WIN_MAX_PATH, ini_path);
+        int num = tokenize_whitespace(tmp, SAMPLE_MUSIC_MAX, zz);
+        for (int j = 0; j < num; j++) {
+            path_build(wav_path, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_MUSIC, zz[j]);
+            if (check_file(wav_path))
+                quest_music_file[i][j] = string_make(zz[j]);
+        }
+    }
+
+    for (int i = 0; i < 1000; i++) /*!< @todo 町最大数指定 */
+    {
+        sprintf(key, "town%03d", i);
+        GetPrivateProfileString("Town", key, "", tmp, MAIN_WIN_MAX_PATH, ini_path);
+        int num = tokenize_whitespace(tmp, SAMPLE_MUSIC_MAX, zz);
+        for (int j = 0; j < num; j++) {
+            path_build(wav_path, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_MUSIC, zz[j]);
+            if (check_file(wav_path))
+                town_music_file[i][j] = string_make(zz[j]);
+        }
+    }
+}
+
+/*
+ * Stop a music
+ */
+void stop_music(void)
+{
+    mciSendCommand(mop.wDeviceID, MCI_STOP, 0, 0);
+    mciSendCommand(mop.wDeviceID, MCI_CLOSE, 0, 0);
+    current_music_type = TERM_XTRA_MUSIC_MUTE;
+    current_music_id = 0;
+    strcpy(current_music_path, "\0");
+}
+
+/*
+ * Play a music
+ */
+errr play_music(int type, int val)
+{
+    int i = 0;
+    char buf[MAIN_WIN_MAX_PATH];
+
+    if (type == TERM_XTRA_MUSIC_MUTE)
+        stop_music();
+
+    if (type == TERM_XTRA_MUSIC_BASIC && ((val < 0) || (val >= MUSIC_BASIC_MAX)))
+        return 1;
+    else if (val < 0 || val >= 1000)
+        return (1); /*!< TODO */
+
+    switch (type) {
+    case TERM_XTRA_MUSIC_BASIC:
+        for (i = 0; i < SAMPLE_MUSIC_MAX; i++)
+            if (!music_file[val][i])
+                break;
+        break;
+    case TERM_XTRA_MUSIC_DUNGEON:
+        for (i = 0; i < SAMPLE_MUSIC_MAX; i++)
+            if (!dungeon_music_file[val][i])
+                break;
+        break;
+    case TERM_XTRA_MUSIC_QUEST:
+        for (i = 0; i < SAMPLE_MUSIC_MAX; i++)
+            if (!quest_music_file[val][i])
+                break;
+        break;
+    case TERM_XTRA_MUSIC_TOWN:
+        for (i = 0; i < SAMPLE_MUSIC_MAX; i++)
+            if (!town_music_file[val][i])
+                break;
+        break;
+    }
+
+    if (i == 0) {
+        return 1;
+    }
+
+    switch (type) {
+    case TERM_XTRA_MUSIC_BASIC:
+        path_build(buf, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_MUSIC, music_file[val][Rand_external(i)]);
+        break;
+    case TERM_XTRA_MUSIC_DUNGEON:
+        path_build(buf, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_MUSIC, dungeon_music_file[val][Rand_external(i)]);
+        break;
+    case TERM_XTRA_MUSIC_QUEST:
+        path_build(buf, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_MUSIC, quest_music_file[val][Rand_external(i)]);
+        break;
+    case TERM_XTRA_MUSIC_TOWN:
+        path_build(buf, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_MUSIC, town_music_file[val][Rand_external(i)]);
+        break;
+    }
+
+    if (current_music_type == type && current_music_id == val)
+        return 0;
+
+    if (current_music_type != TERM_XTRA_MUSIC_MUTE && type != TERM_XTRA_MUSIC_MUTE)
+        if (0 == strcmp(current_music_path, buf))
+            return 0;
+
+    current_music_type = type;
+    current_music_id = val;
+    strcpy(current_music_path, buf);
+
+    mop.lpstrDeviceType = mci_device_type;
+    mop.lpstrElementName = buf;
+    mciSendCommand(mop.wDeviceID, MCI_STOP, 0, 0);
+    mciSendCommand(mop.wDeviceID, MCI_CLOSE, 0, 0);
+    mciSendCommand(mop.wDeviceID, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_ELEMENT, (DWORD)&mop);
+    mciSendCommand(mop.wDeviceID, MCI_SEEK, MCI_SEEK_TO_START, 0);
+    mciSendCommand(mop.wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)&mop);
+    return 0;
+}
+}
diff --git a/src/main-win/main-win-music.h b/src/main-win/main-win-music.h
new file mode 100644 (file)
index 0000000..bec27f1
--- /dev/null
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "system/h-type.h"
+
+#include "main/music-definitions-table.h"
+
+#define SAMPLE_MUSIC_MAX 16
+extern concptr music_file[MUSIC_BASIC_MAX][SAMPLE_MUSIC_MAX];
+// TODO マジックナンバー除去
+extern concptr dungeon_music_file[1000][SAMPLE_MUSIC_MAX];
+extern concptr town_music_file[1000][SAMPLE_MUSIC_MAX];
+extern concptr quest_music_file[1000][SAMPLE_MUSIC_MAX];
+extern concptr ANGBAND_DIR_XTRA_MUSIC;
+
+namespace main_win_music {
+void load_music_prefs(DUNGEON_IDX max_d_idx, QUEST_IDX max_q_idx);
+void stop_music(void);
+errr play_music(int type, int val);
+}
diff --git a/src/main-win/main-win-sound.cpp b/src/main-win/main-win-sound.cpp
new file mode 100644 (file)
index 0000000..dc6e3b7
--- /dev/null
@@ -0,0 +1,67 @@
+/*!
+ * @file main-win-sound.cpp
+ * @brief Windows版固有実装(効果音)
+ */
+
+#include "main-win/main-win-define.h"
+#include "main-win/main-win-file-utils.h"
+#include "main-win/main-win-mci.h"
+#include "main-win/main-win-mmsystem.h"
+#include "main-win/main-win-sound.h"
+#include "main-win/main-win-tokenizer.h"
+#include "util/angband-files.h"
+
+/*
+ * An array of sound file names
+ */
+concptr sound_file[SOUND_MAX][SAMPLE_SOUND_MAX];
+
+/*
+ * Directory name
+ */
+concptr ANGBAND_DIR_XTRA_SOUND;
+
+void load_sound_prefs(void)
+{
+    char tmp[MAIN_WIN_MAX_PATH];
+    char ini_path[MAIN_WIN_MAX_PATH];
+    char wav_path[MAIN_WIN_MAX_PATH];
+    char *zz[SAMPLE_SOUND_MAX];
+
+    // FIXME sound.cfgとmusic.cfgに共通する「[Device]tyepe=~」項目の読込先変数が同じため競合する
+    // FIXME 効果音再生のPlaySound APIはMCIとは別のため、現在の所は効果音用のデバイスタイプ設定は不要
+    path_build(ini_path, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_SOUND, "sound_debug.cfg");
+    if (GetPrivateProfileString("Device", "type", "", mci_device_type, _countof(mci_device_type), ini_path) == 0) {
+        path_build(ini_path, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_SOUND, "sound.cfg");
+        GetPrivateProfileString("Device", "type", "", mci_device_type, _countof(mci_device_type), ini_path);
+    }
+
+    for (int i = 0; i < SOUND_MAX; i++) {
+        GetPrivateProfileString("Sound", angband_sound_name[i], "", tmp, MAIN_WIN_MAX_PATH, ini_path);
+        int num = tokenize_whitespace(tmp, SAMPLE_SOUND_MAX, zz);
+        for (int j = 0; j < num; j++) {
+            path_build(wav_path, MAIN_WIN_MAX_PATH, ANGBAND_DIR_XTRA_SOUND, zz[j]);
+            if (check_file(wav_path))
+                sound_file[i][j] = string_make(zz[j]);
+        }
+    }
+}
+
+errr play_sound(int val)
+{
+    char buf[MAIN_WIN_MAX_PATH];
+    if ((val < 0) || (val >= SOUND_MAX))
+        return 1;
+
+    int i;
+    for (i = 0; i < SAMPLE_SOUND_MAX; i++) {
+        if (!sound_file[val][i])
+            break;
+    }
+
+    if (i == 0)
+        return 1;
+
+    path_build(buf, 1024, ANGBAND_DIR_XTRA_SOUND, sound_file[val][Rand_external(i)]);
+    return (PlaySound(buf, 0, SND_FILENAME | SND_ASYNC));
+}
diff --git a/src/main-win/main-win-sound.h b/src/main-win/main-win-sound.h
new file mode 100644 (file)
index 0000000..3ac807f
--- /dev/null
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "system/h-type.h"
+
+#include "main/sound-definitions-table.h"
+
+#define SAMPLE_SOUND_MAX 16
+extern concptr sound_file[SOUND_MAX][SAMPLE_SOUND_MAX];
+extern concptr ANGBAND_DIR_XTRA_SOUND;
+
+void load_sound_prefs(void);
+errr play_sound(int val);
diff --git a/src/main-win/main-win-tokenizer.cpp b/src/main-win/main-win-tokenizer.cpp
new file mode 100644 (file)
index 0000000..06cdcbc
--- /dev/null
@@ -0,0 +1,49 @@
+/*!
+ * @file main-win-tokenizer.cpp
+ * @brief Windows版固有実装(トークン分割)
+ */
+
+#include "main-win/main-win-tokenizer.h"
+
+#include <ctype.h>
+
+/*
+ * - Taken from files.c.
+ *
+ * Extract "tokens" from a buffer
+ *
+ * This function uses "whitespace" as delimiters, and treats any amount of
+ * whitespace as a single delimiter.  We will never return any empty tokens.
+ * When given an empty buffer, or a buffer containing only "whitespace", we
+ * will return no tokens.  We will never extract more than "num" tokens.
+ *
+ * By running a token through the "text_to_ascii()" function, you can allow
+ * that token to include (encoded) whitespace, using "\s" to encode spaces.
+ *
+ * We save pointers to the tokens in "tokens", and return the number found.
+ */
+s16b tokenize_whitespace(char *buf, s16b num, char **tokens)
+{
+    s16b k = 0;
+    char *s = buf;
+
+    while (k < num) {
+        char *t;
+        for (; *s && iswspace(*s); ++s) /* loop */
+            ;
+
+        if (!*s)
+            break;
+
+        for (t = s; *t && !iswspace(*t); ++t) /* loop */
+            ;
+
+        if (*t)
+            *t++ = '\0';
+
+        tokens[k++] = s;
+        s = t;
+    }
+
+    return k;
+}
diff --git a/src/main-win/main-win-tokenizer.h b/src/main-win/main-win-tokenizer.h
new file mode 100644 (file)
index 0000000..b67d6ee
--- /dev/null
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "system/h-type.h"
+
+s16b tokenize_whitespace(char *buf, s16b num, char **tokens);
diff --git a/src/main-win/main-win-windows.h b/src/main-win/main-win-windows.h
new file mode 100644 (file)
index 0000000..39d1ec8
--- /dev/null
@@ -0,0 +1,14 @@
+#pragma once
+
+/*
+ * Exclude parts of WINDOWS.H that are not needed (Win32)
+ */
+#define WIN32_LEAN_AND_MEAN
+#define NONLS /* All NLS defines and routines */
+#define NOSERVICE /* All Service Controller routines, SERVICE_ equates, etc. */
+#define NOMCX /* Modem Configuration Extensions */
+
+/*
+ * Include the "windows" support file
+ */
+#include <windows.h>
index a2c8e25..24720cd 100644 (file)
@@ -9,28 +9,31 @@ typedef struct angband_header angband_header;
 typedef errr (*parse_info_txt_func)(char *buf, angband_header *head);
 
 struct angband_header {
-    byte v_major; /* Version -- major */
-    byte v_minor; /* Version -- minor */
-    byte v_patch; /* Version -- patch */
-    byte v_extra; /* Version -- extra */
+    byte v_major; //!< Major version
+    byte v_minor; //!< Minor version
+    byte v_patch; //!< Patch version
+    byte checksum; //!< Checksum of "info" records
 
-    u16b info_num; /* Number of "info" records */
-    int info_len; /* Size of each "info" record */
-    u16b head_size; /* Size of the "header" in bytes */
+    u16b info_num; //!< このinfoのデータ数
+    int info_len; //!< このinfoの総サイズ
+    u16b head_size; //!< このinfoのヘッダサイズ
 
-    STR_OFFSET info_size; /* Size of the "info" array in bytes */
-    STR_OFFSET name_size; /* Size of the "name" array in bytes */
-    STR_OFFSET text_size; /* Size of the "text" array in bytes */
-    STR_OFFSET tag_size; /* Size of the "tag" array in bytes */
+    STR_OFFSET info_size; //!< info配列サイズ
+    STR_OFFSET name_size; //!< 名前文字列群サイズ(総文字長)
+    STR_OFFSET text_size; //!< フレーバー文字列群サイズ(総文字長)
+    STR_OFFSET tag_size; //!< タグ文字列群サイズ(総文字長)
 
-    void *info_ptr;
-    char *name_ptr;
-    char *text_ptr;
-    char *tag_ptr;
+    void *info_ptr; //!< info配列へのポインタ
+    char *name_ptr; //!< 名前文字列群へのポインタ
+    char *text_ptr; //!< フレーバー文字列群へのポインタ
+    char *tag_ptr; //!< タグ文字列群へのポインタ
 
-    parse_info_txt_func parse_info_txt;
+    parse_info_txt_func parse_info_txt; //!< Pointer to parser callback function
 
-    void (*retouch)(angband_header *head);
+    void (*retouch)(angband_header *head); //!< 設定再読み込み用?
+
+    byte v_extra; ///< Extra version for Alpha, Beta
+    byte v_savefile; ///< Savefile version
 };
 
 extern angband_header f_head;
index b6e9960..777f6e9 100644 (file)
@@ -103,10 +103,10 @@ errr init_other(player_type *player_ptr)
 errr init_object_alloc(void)
 {
     s16b aux[MAX_DEPTH];
-    (void)C_WIPE(&aux, MAX_DEPTH, s16b);
+    (void)C_WIPE(aux, MAX_DEPTH, s16b);
 
     s16b num[MAX_DEPTH];
-    (void)C_WIPE(&num, MAX_DEPTH, s16b);
+    (void)C_WIPE(num, MAX_DEPTH, s16b);
 
     if (alloc_kind_table)
         C_KILL(alloc_kind_table, alloc_kind_size, alloc_entry);
@@ -147,7 +147,6 @@ errr init_object_alloc(void)
             table[z].level = (DEPTH)x;
             table[z].prob1 = (PROB)p;
             table[z].prob2 = (PROB)p;
-            table[z].prob3 = (PROB)p;
             aux[x]++;
         }
     }
@@ -184,7 +183,6 @@ errr init_alloc(void)
         alloc_race_table[i].level = (DEPTH)x;
         alloc_race_table[i].prob1 = (PROB)p;
         alloc_race_table[i].prob2 = (PROB)p;
-        alloc_race_table[i].prob3 = (PROB)p;
     }
 
     C_KILL(elements, max_r_idx, tag_type);
index 7b5ee6e..be228ef 100644 (file)
@@ -50,11 +50,12 @@ errr init_misc(player_type *player_ptr) { return parse_fixed_map(player_ptr, "mi
 static errr init_info_raw(int fd, angband_header *head)
 {
     angband_header test;
-    if (fd_read(fd, (char *)(&test), sizeof(angband_header)) || (test.v_major != head->v_major) || (test.v_minor != head->v_minor)
-        || (test.v_patch != head->v_patch) || (test.info_num != head->info_num) || (test.info_len != head->info_len) || (test.head_size != head->head_size)
-        || (test.info_size != head->info_size)) {
+    if (fd_read(fd, (char *)(&test), sizeof(angband_header))
+        || (test.v_major != head->v_major) || (test.v_minor != head->v_minor) || (test.v_patch != head->v_patch)
+        || (test.v_extra != head->v_extra) || (test.v_savefile != head->v_savefile)
+        || (test.head_size != head->head_size) || (test.info_size != head->info_size)
+        || (test.info_num != head->info_num) || (test.info_len != head->info_len))
         return -1;
-    }
 
     *head = test;
     C_MAKE(head->info_ptr, head->info_size, char);
@@ -105,13 +106,16 @@ static void init_header(angband_header *head, IDX num, int len)
     head->v_major = FAKE_VER_MAJOR;
     head->v_minor = FAKE_VER_MINOR;
     head->v_patch = FAKE_VER_PATCH;
-    head->v_extra = 0;
+    head->checksum = 0;
 
     head->info_num = (IDX)num;
     head->info_len = len;
 
     head->head_size = sizeof(angband_header);
     head->info_size = head->info_num * head->info_len;
+
+    head->v_extra = FAKE_VER_EXTRA;
+    head->v_savefile = SAVEFILE_VERSION;
 }
 
 /*!
@@ -232,7 +236,8 @@ static errr init_info(player_type *player_ptr, concptr filename, angband_header
         (void)fd_close(fd);
     }
 
-    C_KILL(head->info_ptr, head->info_size, char);
+    C_FREE(static_cast<char *>(head->info_ptr), head->info_size, char);
+    head->info_ptr = nullptr;
     if (name)
         C_KILL(head->name_ptr, FAKE_NAME_SIZE, char);
 
index 212045b..a1303b9 100644 (file)
@@ -99,13 +99,14 @@ bool exchange_cash(player_type *player_ptr)
 
     for (INVENTORY_IDX i = 0; i < INVEN_PACK; i++) {
         o_ptr = &player_ptr->inventory_list[i];
-        if ((o_ptr->tval == TV_CORPSE) && (o_ptr->sval == SV_CORPSE) && (streq(r_name + r_info[o_ptr->pval].name, r_name + r_info[today_mon].name))) {
+        if ((o_ptr->tval == TV_CORPSE) && (o_ptr->sval == SV_CORPSE)
+            && (streq(r_name + r_info[o_ptr->pval].name, r_name + r_info[current_world_ptr->today_mon].name))) {
             char buf[MAX_NLEN + 32];
             describe_flavor(player_ptr, o_name, o_ptr, 0);
             sprintf(buf, _("%s を換金しますか?", "Convert %s into money? "), o_name);
             if (get_check(buf)) {
-                msg_format(_("賞金 %ld$を手に入れた。", "You get %ldgp."), (long int)((r_info[today_mon].level * 50 + 100) * o_ptr->number));
-                player_ptr->au += (r_info[today_mon].level * 50 + 100) * o_ptr->number;
+                msg_format(_("賞金 %ld$を手に入れた。", "You get %ldgp."), (long int)((r_info[current_world_ptr->today_mon].level * 50 + 100) * o_ptr->number));
+                player_ptr->au += (r_info[current_world_ptr->today_mon].level * 50 + 100) * o_ptr->number;
                 player_ptr->redraw |= (PR_GOLD);
                 vary_item(player_ptr, i, -o_ptr->number);
             }
@@ -117,13 +118,14 @@ bool exchange_cash(player_type *player_ptr)
     for (INVENTORY_IDX i = 0; i < INVEN_PACK; i++) {
         o_ptr = &player_ptr->inventory_list[i];
 
-        if ((o_ptr->tval == TV_CORPSE) && (o_ptr->sval == SV_SKELETON) && (streq(r_name + r_info[o_ptr->pval].name, r_name + r_info[today_mon].name))) {
+        if ((o_ptr->tval == TV_CORPSE) && (o_ptr->sval == SV_SKELETON)
+            && (streq(r_name + r_info[o_ptr->pval].name, r_name + r_info[current_world_ptr->today_mon].name))) {
             char buf[MAX_NLEN + 32];
             describe_flavor(player_ptr, o_name, o_ptr, 0);
             sprintf(buf, _("%s を換金しますか?", "Convert %s into money? "), o_name);
             if (get_check(buf)) {
-                msg_format(_("賞金 %ld$を手に入れた。", "You get %ldgp."), (long int)((r_info[today_mon].level * 30 + 60) * o_ptr->number));
-                player_ptr->au += (r_info[today_mon].level * 30 + 60) * o_ptr->number;
+                msg_format(_("賞金 %ld$を手に入れた。", "You get %ldgp."), (long int)((r_info[current_world_ptr->today_mon].level * 30 + 60) * o_ptr->number));
+                player_ptr->au += (r_info[current_world_ptr->today_mon].level * 30 + 60) * o_ptr->number;
                 player_ptr->redraw |= (PR_GOLD);
                 vary_item(player_ptr, i, -o_ptr->number);
             }
@@ -196,7 +198,7 @@ bool exchange_cash(player_type *player_ptr)
 void today_target(player_type *player_ptr)
 {
     char buf[160];
-    monster_race *r_ptr = &r_info[today_mon];
+    monster_race *r_ptr = &r_info[current_world_ptr->today_mon];
 
     clear_bldg(4, 18);
     c_put_str(TERM_YELLOW, _("本日の賞金首", "Wanted monster that changes from day to day"), 5, 10);
@@ -206,7 +208,7 @@ void today_target(player_type *player_ptr)
     prt(buf, 8, 10);
     sprintf(buf, _("骨   ---- $%d", "skeleton ---- $%d"), (int)r_ptr->level * 30 + 60);
     prt(buf, 9, 10);
-    player_ptr->today_mon = today_mon;
+    player_ptr->today_mon = current_world_ptr->today_mon;
 }
 
 /*!
@@ -284,9 +286,9 @@ void determine_daily_bounty(player_type *player_ptr, bool conv_old)
     get_mon_num_prep_bounty(player_ptr);
 
     while (TRUE) {
-        today_mon = get_mon_num(player_ptr, MIN(max_dl / 2, 40), max_dl, GMN_ARENA);
+        current_world_ptr->today_mon = get_mon_num(player_ptr, MIN(max_dl / 2, 40), max_dl, GMN_ARENA);
         monster_race *r_ptr;
-        r_ptr = &r_info[today_mon];
+        r_ptr = &r_info[current_world_ptr->today_mon];
 
         if (cheat_hear) {
             msg_format("日替わり候補: %s ", r_ptr->name + r_name);
@@ -304,6 +306,9 @@ void determine_daily_bounty(player_type *player_ptr, bool conv_old)
             continue;
         break;
     }
+
+    // プレイヤーは日替わり賞金首に関する知識を失う。
+    player_ptr->today_mon = 0;
 }
 
 /*!
index e534eee..5759d1a 100644 (file)
@@ -211,6 +211,9 @@ void building_recharge_all(player_type *player_ptr)
             price = MAX(10, price);
             price = (o_ptr->number * k_ptr->pval - o_ptr->pval) * price;
             break;
+
+        default:
+            break;
         }
 
         if (price > 0)
@@ -262,6 +265,9 @@ void building_recharge_all(player_type *player_ptr)
 
             o_ptr->ident &= ~(IDENT_EMPTY);
             break;
+
+        default:
+            break;
         }
     }
 
index d4a92ce..e94437d 100644 (file)
@@ -10,6 +10,7 @@
 #include "mspell/assign-monster-spell.h"
 #include "mspell/mspell-checker.h"
 #include "mspell/mspell-util.h"
+#include "mspell/mspell.h"
 #include "spell-realm/spells-hex.h"
 #include "system/floor-type-definition.h"
 #include "view/display-messages.h"
@@ -85,7 +86,7 @@ static void process_rememberance(melee_spell_type *ms_ptr)
 
         return;
     }
-    
+
     if (ms_ptr->thrown_spell < RF5_SPELL_START + RF5_SPELL_SIZE) {
         ms_ptr->r_ptr->r_flags5 |= (1UL << (ms_ptr->thrown_spell - RF5_SPELL_START));
         if (ms_ptr->r_ptr->r_cast_spell < MAX_UCHAR)
@@ -93,7 +94,7 @@ static void process_rememberance(melee_spell_type *ms_ptr)
 
         return;
     }
-    
+
     if (ms_ptr->thrown_spell < RF6_SPELL_START + RF6_SPELL_SIZE) {
         ms_ptr->r_ptr->r_flags6 |= (1UL << (ms_ptr->thrown_spell - RF6_SPELL_START));
         if (ms_ptr->r_ptr->r_cast_spell < MAX_UCHAR)
@@ -141,8 +142,8 @@ bool monst_spell_monst(player_type *target_ptr, MONSTER_IDX m_idx)
         return TRUE;
 
     ms_ptr->can_remember = is_original_ap_and_seen(target_ptr, ms_ptr->m_ptr);
-    ms_ptr->dam = monspell_to_monster(target_ptr, ms_ptr->thrown_spell, ms_ptr->y, ms_ptr->x, m_idx, ms_ptr->target_idx, FALSE);
-    if (ms_ptr->dam < 0)
+    const auto res = monspell_to_monster(target_ptr, ms_ptr->thrown_spell, ms_ptr->y, ms_ptr->x, m_idx, ms_ptr->target_idx, FALSE);
+    if (!res.valid)
         return FALSE;
 
     process_special_melee_spell(target_ptr, ms_ptr);
index 1421c06..3c766ca 100644 (file)
@@ -137,6 +137,11 @@ void describe_melee_method(player_type *subject_ptr, mam_type *mam_ptr)
         mam_ptr->touched = FALSE;
         break;
     }
+
+    case RBM_NONE:
+    case RBM_SHOOT:
+    case NB_RBM_TYPE:
+        break;
     }
 }
 
diff --git a/src/mind/mind-elementalist.cpp b/src/mind/mind-elementalist.cpp
new file mode 100644 (file)
index 0000000..49a5067
--- /dev/null
@@ -0,0 +1,1331 @@
+/*!
+ * @brief 元素使いの魔法系統
+ */
+
+#include "mind-elementalist.h"
+#include "action/action-limited.h"
+#include "cmd-action/cmd-spell.h"
+#include "cmd-action/cmd-mind.h"
+#include "cmd-io/cmd-gameoption.h"
+#include "core/asking-player.h"
+#include "core/hp-mp-processor.h"
+#include "core/player-redraw-types.h"
+#include "core/stuff-handler.h"
+#include "core/window-redrawer.h"
+#include "effect/effect-characteristics.h"
+#include "effect/effect-monster-util.h"
+#include "effect/effect-processor.h"
+#include "effect/spells-effect-util.h"
+#include "floor/cave.h"
+#include "floor/floor-util.h"
+#include "game-option/disturbance-options.h"
+#include "game-option/input-options.h"
+#include "game-option/text-display-options.h"
+#include "io/command-repeater.h"
+#include "io/input-key-acceptor.h"
+#include "io/input-key-requester.h"
+#include "main/sound-definitions-table.h"
+#include "main/sound-of-music.h"
+#include "mind/mind-explanations-table.h"
+#include "monster/monster-describer.h"
+#include "monster-race/monster-race.h"
+#include "monster-race/race-flags3.h"
+#include "monster-race/race-flags-resistance.h"
+#include "mind/mind-mindcrafter.h"
+#include "player/player-status-table.h"
+#include "player-info/avatar.h"
+#include "player-status/player-status-base.h"
+#include "racial/racial-util.h"
+#include "spell-kind/earthquake.h"
+#include "spell-kind/spells-charm.h"
+#include "spell-kind/spells-detection.h"
+#include "spell-kind/spells-genocide.h"
+#include "spell-kind/spells-launcher.h"
+#include "spell-kind/spells-lite.h"
+#include "spell-kind/magic-item-recharger.h"
+#include "spell-kind/spells-beam.h"
+#include "spell-kind/spells-sight.h"
+#include "spell-kind/spells-world.h"
+#include "status/bad-status-setter.h"
+#include "status/base-status.h"
+#include "system/game-option-types.h"
+#include "util/bit-flags-calculator.h"
+#include "util/buffer-shaper.h"
+#include "util/int-char-converter.h"
+#include "target/target-getter.h"
+#include "term/screen-processor.h"
+#include "term/term-color-types.h"
+#include "view/display-messages.h"
+#include <string>
+#include <array>
+#include <unordered_map>
+
+/*!
+ * @brief 元素魔法呪文のID定義
+ */
+enum class ElementSpells {
+    BOLT_1ST = 0,
+    MON_DETECT = 1,
+    PERCEPT = 2,
+    CURE = 3,
+    BOLT_2ND = 4,
+    MAG_DETECT = 5,
+    BALL_3RD = 6,
+    BALL_1ST = 7,
+    BREATH_2ND = 8,
+    ANNIHILATE = 9,
+    BOLT_3RD = 10,
+    WAVE_1ST = 11,
+    BALL_2ND = 12,
+    BURST_1ST = 13,
+    STORM_2ND = 14,
+    BREATH_1ST = 15,
+    STORM_3ND = 16,
+    MAX
+};
+
+/*!
+ * @brief 元素魔法タイプ構造体
+ */
+struct element_type {
+    std::string_view title; //!< 領域名
+    std::array<spells_type, 3> type; //!< 属性タイプリスト
+    std::array<std::string_view, 3> name; //!< 属性名リスト
+    std::unordered_map<spells_type, spells_type> extra; //!< 追加属性タイプ
+};
+
+/*!
+ * @brief 元素魔法難易度構造体
+ */
+struct element_power {
+    int elem; //!< 使用属性番号
+    mind_type info; //!< 難易度構造体
+};
+
+using element_type_list = const std::unordered_map<ElementRealm, element_type>;
+using element_power_list = const std::unordered_map<ElementSpells, element_power>;
+using element_tip_list = const std::unordered_map<ElementSpells, std::string_view>;
+using element_text_list = const std::unordered_map<ElementRealm, std::string_view>;
+
+// clang-format off
+/*!
+ * @brief 元素魔法タイプ定義
+ */
+static element_type_list element_types = {
+    {
+        ElementRealm::FIRE, {
+            _("炎", "Fire"),
+            { GF_FIRE, GF_ROCKET, GF_PLASMA },
+            { _("火炎", "Fire"), _("爆発", "Explosion"), _("プラズマ", "Plasma") },
+            { },
+        }
+    },
+    {
+        ElementRealm::ICE, {
+            _("氷", "Ice"),
+            { GF_COLD, GF_INERTIAL, GF_TIME },
+            { _("冷気", "Ice"), _("遅鈍", "Inertia"), _("時間逆転", "Time Stream") },
+            { { GF_COLD, GF_ICE} },
+        }
+    },
+    {
+        ElementRealm::SKY, {
+            _("空", "Sky"),
+            { GF_ELEC, GF_LITE, GF_MANA },
+            { _("電撃", "Lightning"), _("光", "Light"), _("魔力", "Mana") },
+            { },
+        }
+    },
+    {
+        ElementRealm::SEA, {
+            _("海", "Sea"),
+            { GF_ACID, GF_WATER, GF_DISINTEGRATE },
+            { _("酸", "Acid"), _("水", "Water"), _("分解", "Disintegration") },
+            { },
+        }
+    },
+    {
+        ElementRealm::DARKNESS, {
+            _("闇", "Darkness"),
+            { GF_DARK, GF_NETHER, GF_HELL_FIRE },
+            { _("暗黒", "Darkness"), _("地獄", "Nether"), _("業火", "Hell Fire") },
+            { },
+        }
+    },
+    {
+        ElementRealm::CHAOS, {
+            _("混沌", "Chaos"),
+            { GF_CONFUSION, GF_CHAOS, GF_NEXUS },
+            { _("混乱", "Confusion"), _("カオス", "Chaos"), _("因果混乱", "Nexus") },
+            { },
+        }
+    },
+    {
+        ElementRealm::EARTH, {
+            _("地", "Earth"),
+            { GF_SHARDS, GF_FORCE, GF_METEOR },
+            { _("破片", "Shards"), _("フォース", "Force"), _("隕石", "Meteor") },
+            { },
+        }
+    },
+    {
+        ElementRealm::DEATH, {
+            _("瘴気", "Death"),
+            { GF_POIS, GF_HYPODYNAMIA, GF_DISENCHANT },
+            { _("毒", "Poison"), _("吸血", "Drain Life"), _("劣化", "Disenchantment") },
+            { },
+        }
+    },
+};
+
+/*!
+ * @brief 元素魔法呪文定義
+ */
+static element_power_list element_powers = {
+    { ElementSpells::BOLT_1ST,   { 0, {  1,  1,  15, _("%sの矢", "%s Bolt") }}},
+    { ElementSpells::MON_DETECT, { 0, {  2,  2,  20, _("モンスター感知", "Detect Monsters") }}},
+    { ElementSpells::PERCEPT,    { 0, {  5,  5,  50, _("擬似鑑定", "Psychometry") }}},
+    { ElementSpells::CURE,       { 0, {  6,  5,  35, _("傷の治癒", "Cure Wounds") }}},
+    { ElementSpells::BOLT_2ND,   { 1, {  8,  6,  35, _("%sの矢", "%s Bolt") }}},
+    { ElementSpells::MAG_DETECT, { 0, { 10,  8,  60, _("魔法感知", "Detect Magical Objs") }}},
+    { ElementSpells::BALL_3RD,   { 2, { 15, 10,  55, _("%s放射", "%s Spout") }}},
+    { ElementSpells::BALL_1ST,   { 0, { 18, 13,  65, _("%sの球", "%s Ball") }}},
+    { ElementSpells::BREATH_2ND, { 1, { 21, 20,  70, _("%sのブレス", "Breath of %s") }}},
+    { ElementSpells::ANNIHILATE, { 0, { 24, 20,  75, _("モンスター消滅", "Annihilation") }}},
+    { ElementSpells::BOLT_3RD,   { 2, { 25, 15,  60, _("%sの矢", "%s Bolt") }}},
+    { ElementSpells::WAVE_1ST,   { 0, { 28, 30,  75, _("元素の波動", "Elemental Wave") }}},
+    { ElementSpells::BALL_2ND,   { 1, { 28, 22,  75, _("%sの球", "%s Ball") }}},
+    { ElementSpells::BURST_1ST,  { 0, { 33, 35,  75, _("精気乱射", "%s Blast") }}},
+    { ElementSpells::STORM_2ND,  { 1, { 35, 30,  75, _("%sの嵐", "%s Storm") }}},
+    { ElementSpells::BREATH_1ST, { 0, { 42, 48,  75, _("%sのブレス", "Breath of %s") }}},
+    { ElementSpells::STORM_3ND,  { 2, { 45, 60,  80, _("%sの嵐", "%s Storm") }}},
+};
+
+/*!
+ * @brief 元素魔法呪文説明文定義
+ */
+static element_tip_list element_tips = {
+    { ElementSpells::BOLT_1ST,
+    _("弱い%sの矢を放つ。", "Fire a weak bolt of %s.") },
+    { ElementSpells::MON_DETECT,
+    _("近くの全てのモンスターを感知する。", "Detects monsters.") },
+    { ElementSpells::PERCEPT,
+    _("アイテムの雰囲気を知る。", "Gives feeling of an item.") },
+    { ElementSpells::CURE,
+    _("怪我と体力を少し回復させる。", "Heals HP and wounds a bit.") },
+    { ElementSpells::BOLT_2ND,
+    _("%sの矢を放つ。", "Fire a bolt of %s.") },
+    { ElementSpells::MAG_DETECT,
+    _("近くの魔法のアイテムを感知する。", "Detects magic devices.") },
+    { ElementSpells::BALL_3RD,
+    _("高威力で射程が短い%sの球を放つ。", "Fire a strong, short-range, ball of %s.") },
+    { ElementSpells::BALL_1ST,
+    _("%sの球を放つ。",  "Fire a ball of %s.") },
+    { ElementSpells::BREATH_2ND,
+    _("%sのブレスを吐く。", "Fire a breath of %s.") },
+    { ElementSpells::ANNIHILATE,
+    _("%s耐性のないモンスターを1体抹殺する。", "Erase a monster unless it resists %s.") },
+    { ElementSpells::BOLT_3RD,
+    _("%sの矢を放つ。", "Fire a bolt of %s.") },
+    { ElementSpells::WAVE_1ST,
+    _("視界内の全ての敵に%sによるダメージを与える。", "Inflict %s damage on all monsters in sight.") },
+    { ElementSpells::BALL_2ND,
+    _("%sの球を放つ。",  "Fire a ball of %s.") },
+    { ElementSpells::BURST_1ST,
+    _("ランダムな方向に%sの矢を放つ。", "Fire some bolts of %s in random direction.") },
+    { ElementSpells::STORM_2ND,
+    _("%sの巨大な球を放つ。", "Fire a large ball of %s.") },
+    { ElementSpells::BREATH_1ST,
+    _("%sのブレスを吐く。", "Fire a breath of %s.") },
+    { ElementSpells::STORM_3ND,
+    _("%sの巨大な球を放つ。", "Fire a large ball of %s.") },
+};
+
+/*!
+ * @brief 元素魔法選択時説明文定義
+ */
+static element_text_list element_texts = {
+    { ElementRealm::FIRE,
+    _("炎系統は巨大なエネルギーで灼熱を生み出し、全ての敵を燃やし尽くそうとします。",
+        "Great energy of Fire system will be able to burn out all of your enemies.")},
+    { ElementRealm::ICE,
+    _("氷系統の魔法はその冷たさで敵の動きを奪い尽くし、魂すらも止めてしまうでしょう。",
+        "Ice system will freeze your enemies, even their souls.")},
+    { ElementRealm::SKY,
+    _("空系統は大いなる天空のエネルギーを駆使して敵の全てを撃滅できます。",
+        "Sky system can terminate all of your enemies powerfully with the energy of the great sky.")},
+    { ElementRealm::SEA,
+    _("海系統はその敵の全てを溶かし、大いなる海へと返してしまいます。",
+        "Sea system melts all of your enemies and returns them to the great ocean.")},
+    { ElementRealm::DARKNESS,
+    _("闇系統は恐るべき力を常闇から引き出し、敵を地獄へと叩き落とすでしょう。",
+        "Dark system draws terrifying power from the darkness and knocks your enemies into hell.")},
+    { ElementRealm::CHAOS,
+    _("混沌系統は敵の意識も条理も捻じ曲げ、その存在をあの世に送ってしまいます。",
+        "Chaos system twists and wraps your enemies, even their souls, and scatters them as dust in the wind.")},
+    { ElementRealm::EARTH,
+    _("地系統は偉大なる大地の力を呼び出して、数多の敵のことごとくを粉砕しようとします。",
+        "Earth system smashes all of your enemies massively using its huge powers.")},
+    { ElementRealm::DEATH,
+    _("瘴気系統は全ての生ける者にとって途轍もない毒です。",
+        "Death system is a tremendous poison for all living enemies.")},
+};
+
+// clang-format on
+
+/*!
+ * @brief 元素魔法の領域名を返す
+ * @param realm_idx 領域番号
+ * @return 領域名
+ */
+concptr get_element_title(int realm_idx)
+{
+    auto realm = static_cast<ElementRealm>(realm_idx);
+    return element_types.at(realm).title.data();
+}
+
+/*!
+ * @brief 元素魔法領域の属性リストを返す
+ * @param realm_idx 領域番号
+ * @return 領域で使用できる属性リスト
+ */
+static std::array<spells_type, 3> get_element_types(int realm_idx)
+{
+    auto realm = static_cast<ElementRealm>(realm_idx);
+    return element_types.at(realm).type;
+}
+
+/*!
+ * @brief 元素魔法領域のn番目の属性を返す
+ * @param realm_idx 領域番号
+ * @param n 属性の何番目か
+ * @return 属性タイプ
+ */
+spells_type get_element_type(int realm_idx, int n)
+{
+    return get_element_types(realm_idx)[n];
+}
+
+/*!
+ * @brief 元素魔法領域のn番目の呪文用の属性を返す
+ * @param realm_idx 領域番号
+ * @param n 属性の何番目か
+ * @return 属性タイプ
+ */
+static spells_type get_element_spells_type(player_type *caster_ptr, int n) {
+    auto realm = element_types.at(static_cast<ElementRealm>(caster_ptr->realm1));
+    auto t = realm.type.at(n);
+    if (realm.extra.find(t) != realm.extra.end()) {
+        if (randint0(100) < caster_ptr->lev * 2)
+            return realm.extra.at(t);
+    }
+    return t;
+}
+
+/*!
+ * @brief 元素魔法領域の属性名リストを返す
+ * @param realm_idx 領域番号
+ * @return 領域で使用できる属性の名称リスト
+ */
+static std::array<std::string_view, 3> get_element_names(int realm_idx)
+{
+    auto realm = static_cast<ElementRealm>(realm_idx);
+    return element_types.at(realm).name;
+}
+
+/*!
+ * @brief 元素魔法領域のn番目の属性名を返す
+ * @param realm_idx 領域番号
+ * @param n 属性の何番目か
+ * @return 属性名
+ */
+concptr get_element_name(int realm_idx, int n)
+{
+    return get_element_names(realm_idx)[n].data();
+}
+
+/*!
+ * @brief 元素魔法の説明文を取得
+ * @param caster_ptr プレイヤー情報への参照ポインタ
+ * @param spell_idx 呪文番号
+ * @return 説明文
+ */
+static concptr get_element_tip(player_type *caster_ptr, int spell_idx)
+{
+    auto realm = static_cast<ElementRealm>(caster_ptr->realm1);
+    auto spell = static_cast<ElementSpells>(spell_idx);
+    auto elem = element_powers.at(spell).elem;
+    return format(element_tips.at(spell).data(), element_types.at(realm).name[elem].data());
+}
+
+/*!
+ * @brief 元素魔法の説明文を取得
+ * @param caster_ptr プレイヤー情報への参照ポインタ
+ * @param spell_idx 呪文番号
+ * @return 説明文
+ */
+static int get_elemental_elem(player_type *caster_ptr, int spell_idx)
+{
+    (void)caster_ptr;
+    auto spell = static_cast<ElementSpells>(spell_idx);
+    return element_powers.at(spell).elem;
+}
+
+/*!
+ * @brief 元素魔法呪文の難易度データを取得
+ * @param caster_ptr プレイヤー情報への参照ポインタ
+ * @param spell_idx 呪文番号
+ * @return 説明文
+ */
+static mind_type get_elemental_info(player_type *caster_ptr, int spell_idx)
+{
+    (void)caster_ptr;
+    auto spell = static_cast<ElementSpells>(spell_idx);
+    return element_powers.at(spell).info;
+}
+
+/*!
+ * @brief 元素魔法呪文の効果表示文字列を取得
+ * @param caster_ptr プレイヤー情報への参照ポインタ
+ * @param spell_idx 呪文番号
+ * @param p バッファ
+ * @return なし(pを更新)
+ */
+void get_element_effect_info(player_type *caster_ptr, int spell_idx, char *p)
+{
+    PLAYER_LEVEL plev = caster_ptr->lev;
+    auto spell = static_cast<ElementSpells>(spell_idx);
+    int dam = 0;
+
+    switch (spell) {
+    case ElementSpells::BOLT_1ST:
+        sprintf(p, " %s%dd%d", KWD_DAM, 3 + ((plev - 1) / 5), 4);
+        break;
+    case ElementSpells::CURE:
+        sprintf(p, " %s%dd%d", KWD_HEAL, 2, 8);
+        break;
+    case ElementSpells::BOLT_2ND:
+        sprintf(p, " %s%dd%d", KWD_DAM, 8 + ((plev - 5) / 4), 8);
+        break;
+    case ElementSpells::BALL_3RD:
+        sprintf(p, " %s%d", KWD_DAM, (50 + plev * 2));
+        break;
+    case ElementSpells::BALL_1ST:
+        sprintf(p, " %s%d", KWD_DAM, 55 + plev);
+        break;
+    case ElementSpells::BREATH_2ND:
+        dam = p_ptr->chp / 2;
+        sprintf(p, " %s%d", KWD_DAM, (dam > 150) ? 150 : dam);
+        break;
+    case ElementSpells::ANNIHILATE:
+        sprintf(p, " %s%d", _("効力:", "pow "), 50 + plev);
+        break;
+    case ElementSpells::BOLT_3RD:
+        sprintf(p, " %s%dd%d", KWD_DAM, 12 + ((plev - 5) / 4), 8);
+        break;
+    case ElementSpells::WAVE_1ST:
+        sprintf(p, " %sd%d", KWD_DAM, plev * 4);
+        break;
+    case ElementSpells::BALL_2ND:
+        sprintf(p, " %s%d", KWD_DAM, 75 + plev);
+        break;
+    case ElementSpells::BURST_1ST:
+        sprintf(p, " %s%dd%d", KWD_DAM, 6 + plev / 8, 8);
+        break;
+    case ElementSpells::STORM_2ND:
+        sprintf(p, " %s%d", KWD_DAM, 120 + plev * 2);
+        break;
+    case ElementSpells::BREATH_1ST:
+        sprintf(p, " %s%d", KWD_DAM, p_ptr->chp * 2 / 3);
+        break;
+    case ElementSpells::STORM_3ND:
+        sprintf(p, " %s%d", KWD_DAM, 300 + plev * 5);
+        break;
+    default:
+        p[0] = '\0';
+        break;
+    }
+}
+
+/*!
+ * @brief 元素魔法呪文を実行する
+ * @param caster_ptr プレイヤー情報への参照ポインタ
+ * @param spell_idx 呪文番号
+ * @return 実行したらTRUE、キャンセルならFALSE
+ */
+static bool cast_element_spell(player_type *caster_ptr, SPELL_IDX spell_idx)
+{
+    auto spell = static_cast<ElementSpells>(spell_idx);
+    auto power = element_powers.at(spell);
+    spells_type typ;
+    DIRECTION dir;
+    PLAYER_LEVEL plev = caster_ptr->lev;
+    HIT_POINT dam;
+    POSITION y, x;
+    int num;
+
+    switch (spell) {
+    case ElementSpells::BOLT_1ST:
+        if (!get_aim_dir(caster_ptr, &dir))
+            return FALSE;
+        dam = damroll(3 + ((plev - 1) / 5), 4);
+        typ = get_element_spells_type(caster_ptr, power.elem);
+        (void)fire_bolt(caster_ptr, typ, dir, dam);
+        break;
+    case ElementSpells::MON_DETECT:
+        (void)detect_monsters_normal(caster_ptr, DETECT_RAD_DEFAULT);
+        (void)detect_monsters_invis(caster_ptr, DETECT_RAD_DEFAULT);
+        break;
+    case ElementSpells::PERCEPT:
+        return psychometry(caster_ptr);
+    case ElementSpells::CURE:
+        (void)hp_player(caster_ptr, damroll(2, 8));
+        (void)set_cut(caster_ptr, caster_ptr->cut - 10);
+        break;
+    case ElementSpells::BOLT_2ND:
+        if (!get_aim_dir(caster_ptr, &dir))
+            return FALSE;
+        dam = damroll(8 + ((plev - 5) / 4), 8);
+        typ = get_element_spells_type(caster_ptr, power.elem);
+        if (fire_bolt_or_beam(caster_ptr, plev, typ, dir, dam)) {
+            if (typ == GF_HYPODYNAMIA) {
+                (void)hp_player(caster_ptr, dam / 2);
+            }
+        }
+        break;
+    case ElementSpells::MAG_DETECT:
+        (void)detect_objects_magic(caster_ptr, DETECT_RAD_DEFAULT);
+        break;
+    case ElementSpells::BALL_3RD:
+        project_length = 4;
+        if (!get_aim_dir(caster_ptr, &dir))
+            return FALSE;
+        typ = get_element_spells_type(caster_ptr, power.elem);
+        dam = 50 + plev * 2;
+        (void)fire_ball(caster_ptr, typ, dir, dam, 1);
+        project_length = 0;
+        break;
+    case ElementSpells::BALL_1ST:
+        if (!get_aim_dir(caster_ptr, &dir))
+            return FALSE;
+        dam = 55 + plev;
+        typ = get_element_spells_type(caster_ptr, power.elem);
+        (void)fire_ball(caster_ptr, typ, dir, dam, 2);
+        break;
+    case ElementSpells::BREATH_2ND:
+        if (!get_aim_dir(caster_ptr, &dir))
+            return FALSE;
+        dam = MIN(150, caster_ptr->chp / 2);
+        typ = get_element_spells_type(caster_ptr, power.elem);
+        if (fire_breath(caster_ptr, typ, dir, dam, 3)) {
+            if (typ == GF_HYPODYNAMIA) {
+                (void)hp_player(caster_ptr, dam / 2);
+            }
+        }
+        break;
+    case ElementSpells::ANNIHILATE:
+        if (!get_aim_dir(caster_ptr, &dir))
+            return FALSE;
+        fire_ball_hide(caster_ptr, GF_E_GENOCIDE, dir, plev + 50, 0);
+        break;
+    case ElementSpells::BOLT_3RD:
+        if (!get_aim_dir(caster_ptr, &dir))
+            return FALSE;
+        dam = damroll(12 + ((plev - 5) / 4), 8);
+        typ = get_element_spells_type(caster_ptr, power.elem);
+        fire_bolt_or_beam(caster_ptr, plev, typ, dir, dam);
+        break;
+    case ElementSpells::WAVE_1ST:
+        dam = randint1(plev * 4);
+        typ = get_element_spells_type(caster_ptr, power.elem);
+        project_all_los(caster_ptr, typ, dam);
+        break;
+    case ElementSpells::BALL_2ND:
+        if (!get_aim_dir(caster_ptr, &dir))
+            return FALSE;
+        dam = 75 + plev;
+        typ = get_element_spells_type(caster_ptr, power.elem);
+        if (fire_ball(caster_ptr, typ, dir, dam, 2)) {
+            if (typ == GF_HYPODYNAMIA) {
+                (void)hp_player(caster_ptr, dam / 2);
+            }
+        }
+        break;
+    case ElementSpells::BURST_1ST:
+        y = caster_ptr->y;
+        x = caster_ptr->x;
+        num = damroll(5, 3);
+        typ = get_element_spells_type(caster_ptr, power.elem);
+        for (int k = 0; k < num; k++) {
+            int attempts = 1000;
+            while (attempts--) {
+                scatter(caster_ptr, &y, &x, caster_ptr->y, caster_ptr->x, 4, PROJECT_NONE);
+                if (!player_bold(caster_ptr, y, x))
+                    break;
+            }
+            project(caster_ptr, 0, 0, y, x, damroll(6 + plev / 8, 10), typ, (PROJECT_BEAM | PROJECT_THRU | PROJECT_GRID | PROJECT_KILL), -1);
+        }
+        break;
+    case ElementSpells::STORM_2ND:
+        if (!get_aim_dir(caster_ptr, &dir))
+            return FALSE;
+        dam = 125 + plev * 2;
+        typ = get_element_spells_type(caster_ptr, power.elem);
+        if (fire_ball(caster_ptr, typ, dir, dam, 4)) {
+            if (typ == GF_HYPODYNAMIA) {
+                (void)hp_player(caster_ptr, dam / 2);
+            }
+        }
+        break;
+    case ElementSpells::BREATH_1ST:
+        if (!get_aim_dir(caster_ptr, &dir))
+            return FALSE;
+        dam = caster_ptr->chp * 2 / 3;
+        typ = get_element_spells_type(caster_ptr, power.elem);
+        (void)fire_breath(caster_ptr, typ, dir, dam, 3);
+        break;
+    case ElementSpells::STORM_3ND:
+        if (!get_aim_dir(caster_ptr, &dir))
+            return FALSE;
+        dam = 300 + plev * 5;
+        typ = get_element_spells_type(caster_ptr, power.elem);
+        (void)fire_ball(caster_ptr, typ, dir, dam, 5);
+        break;
+    default:
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/*!
+ * @brief 元素魔法呪文の失敗率を計算
+ * @param caster_ptr プレイヤー情報への参照ポインタ
+ * @param spell_idx 呪文番号
+ * @return 失敗率
+ */
+static PERCENTAGE decide_element_chance(player_type *caster_ptr, mind_type spell)
+{
+    PERCENTAGE chance = spell.fail;
+
+    chance -= 3 * (caster_ptr->lev - spell.min_lev);
+    chance += caster_ptr->to_m_chance;
+    chance -= 3 * (adj_mag_stat[caster_ptr->stat_index[A_WIS]] - 1);
+    
+    PERCENTAGE minfail = adj_mag_fail[caster_ptr->stat_index[A_WIS]];
+    if (chance < minfail)
+        chance = minfail;
+
+    if (caster_ptr->stun > 50)
+        chance += 25;
+    else if (caster_ptr->stun)
+        chance += 15;
+
+    if (heavy_armor(caster_ptr))
+        chance += 5;
+
+    if (caster_ptr->icky_wield[0])
+        chance += 5;
+
+    if (caster_ptr->icky_wield[1])
+        chance += 5;
+
+    if (chance > 95)
+        chance = 95;
+
+    return chance;
+}
+
+/*!
+ * @brief 元素魔法呪文の消費MPを計算
+ * @param caster_ptr プレイヤー情報への参照ポインタ
+ * @param spell_idx 呪文番号
+ * @return 消費MP
+ */
+static MANA_POINT decide_element_mana_cost(player_type *caster_ptr, mind_type spell)
+{
+    (void)caster_ptr;
+    return spell.mana_cost;
+}
+
+/*!
+ * @brief 元素魔法呪文を選択して取得
+ * @param caster_ptr プレイヤー情報への参照ポインタ
+ * @param sn 呪文番号
+ * @param only_browse 閲覧モードかどうか
+ * @return 選んだらTRUE、選ばなかったらFALSE
+ */
+bool get_element_power(player_type *caster_ptr, SPELL_IDX *sn, bool only_browse)
+{
+    SPELL_IDX i;
+    int num = 0;
+    TERM_LEN y = 1;
+    TERM_LEN x = 10;
+    PLAYER_LEVEL plev = caster_ptr->lev;
+    int ask = TRUE;
+    char choice;
+    char out_val[160];
+    char comment[80];
+    COMMAND_CODE code;
+    bool flag, redraw;
+    int menu_line = (use_menu ? 1 : 0);
+
+    *sn = -1;
+    if (repeat_pull(&code)) {
+        *sn = (SPELL_IDX)code;
+        if (get_elemental_info(caster_ptr, *sn).min_lev <= plev)
+            return TRUE;
+    }
+
+    concptr p = _("元素魔法", "magic");
+    flag = FALSE;
+    redraw = FALSE;
+
+    for (i = 0; i < static_cast<SPELL_IDX>(ElementSpells::MAX); i++) {
+        if (get_elemental_info(caster_ptr, i).min_lev <= plev)
+            num++;
+    }
+
+    if (only_browse)
+        (void)strnfmt(out_val, 78, 
+            _("(%^s %c-%c, '*'で一覧, ESC) どの%sについて知りますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
+            p, I2A(0), I2A(num - 1), p);
+    else
+        (void)strnfmt(out_val, 78, 
+            _("(%^s %c-%c, '*'で一覧, ESC) どの%sを使いますか?", "(%^ss %c-%c, *=List, ESC=exit) Use which %s? "),
+            p, I2A(0), I2A(num - 1), p);
+
+    if (use_menu && !only_browse)
+        screen_save();
+
+    int elem;
+    mind_type spell;
+    choice = (always_show_list || use_menu) ? ESCAPE : 1;
+    while (!flag) {
+        if (choice == ESCAPE)
+            choice = ' ';
+        else if (!get_com(out_val, &choice, TRUE))
+            break;
+
+        if (use_menu && choice != ' ') {
+            switch (choice) {
+            case '0':
+                if (!only_browse)
+                    screen_load();
+                return FALSE;
+            case '8':
+            case 'k':
+            case 'K':
+                menu_line += (num - 1);
+                break;
+            case '2':
+            case 'j':
+            case 'J':
+                menu_line++;
+                break;
+            case 'x':
+            case 'X':
+            case '\r':
+            case '\n':
+                i = menu_line - 1;
+                ask = FALSE;
+                break;
+            }
+
+            if (menu_line > num)
+                menu_line -= num;
+        }
+
+        int spell_max = static_cast<int>(ElementSpells::MAX);
+        if ((choice == ' ') || (choice == '*') || (choice == '?') || (use_menu && ask)) {
+            if (!redraw || use_menu) {
+                char desc[80];
+                char name[80];
+                redraw = TRUE;
+                if (!only_browse && !use_menu)
+                    screen_save();
+
+                prt("", y, x);
+                put_str(_("名前", "Name"), y, x + 5);
+                put_str(_("Lv   MP   失率 効果", "Lv   MP   Fail Info"), y, x + 35);
+                for (i = 0; i < spell_max; i++) {
+                    elem = get_elemental_elem(caster_ptr, i);
+                    spell = get_elemental_info(caster_ptr, i);
+
+                    if (spell.min_lev > plev)
+                        break;
+
+                    PERCENTAGE chance = decide_element_chance(caster_ptr, spell);
+                    int mana_cost = decide_element_mana_cost(caster_ptr, spell);
+                    get_element_effect_info(caster_ptr, i, comment);
+
+                    if (use_menu) {
+                        if (i == (menu_line - 1))
+                            strcpy(desc, _("  》 ", "  >  "));
+                        else
+                            strcpy(desc, "     ");
+                    } else
+                        sprintf(desc, "  %c) ", I2A(i));
+
+                    concptr s = get_element_name(caster_ptr->realm1, elem);
+                    sprintf(name, spell.name, s);
+                    strcat(desc,
+                        format("%-30s%2d %4d %3d%%%s", name, spell.min_lev, mana_cost, chance, comment));
+                    prt(desc, y + i + 1, x);
+                }
+
+                prt("", y + i + 1, x);
+            } else if (!only_browse) {
+                redraw = FALSE;
+                screen_load();
+            }
+
+            continue;
+        }
+
+        if (!use_menu) {
+            ask = isupper(choice);
+            if (ask)
+                choice = (char)tolower(choice);
+
+            i = (islower(choice) ? A2I(choice) : -1);
+        }
+
+        if ((i < 0) || (i >= num)) {
+            bell();
+            continue;
+        }
+
+        spell = get_elemental_info(caster_ptr, i);
+        if (ask) {
+            char tmp_val[160];
+            (void)strnfmt(tmp_val, 78, _("%sを使いますか?", "Use %s? "), spell.name);
+            if (!get_check(tmp_val))
+                continue;
+        }
+
+        flag = TRUE;
+    }
+
+    if (redraw && !only_browse)
+        screen_load();
+
+    set_bits(caster_ptr->window_flags, PW_SPELL);
+    handle_stuff(caster_ptr);
+    if (!flag)
+        return FALSE;
+
+    *sn = i;
+    repeat_push((COMMAND_CODE)i);
+    return TRUE;
+}
+
+/*!
+ * @brief 元素魔法呪文をMPがなくても挑戦するか確認する
+ * @param caster_ptr プレイヤー情報への参照ポインタ
+ * @param mana_cost 消費MP
+ * @return 詠唱するならTRUE、しないならFALSE
+ */
+static bool check_element_mp_sufficiency(player_type *caster_ptr, int mana_cost)
+{
+    if (mana_cost <= caster_ptr->csp)
+        return TRUE;
+
+    msg_print(_("MPが足りません。", "You do not have enough mana to use this power."));
+    if (!over_exert)
+        return FALSE;
+
+    return get_check(_("それでも挑戦しますか? ", "Attempt it anyway? "));
+}
+
+/*!
+ * @brief 元素魔法呪文の詠唱を試み、成功なら詠唱し、失敗ならファンブルする
+ * @param caster_ptr プレイヤー情報への参照ポインタ
+ * @param spell_idx 呪文番号
+ * @param chance 失敗率
+ * @return 詠唱して実行したらTRUE、されなかったらFALSE
+ */
+static bool try_cast_element_spell(player_type *caster_ptr, SPELL_IDX spell_idx, PERCENTAGE chance)
+{
+    if (randint0(100) >= chance) {
+        sound(SOUND_ZAP);
+        return cast_element_spell(caster_ptr, spell_idx);
+    }
+
+    if (flush_failure)
+        flush();
+
+    msg_format(_("魔力の集中に失敗した!", "You failed to concentrate hard enough for Mana!"));
+    sound(SOUND_FAIL);
+
+    if (randint1(100) < chance / 2) {
+        int plev = caster_ptr->lev;
+        msg_print(_("元素の力が制御できない氾流となって解放された!",
+            "Elemental power unleashes its power in an uncontrollable storm!"));
+        project(caster_ptr, PROJECT_WHO_UNCTRL_POWER, 2 + plev / 10, caster_ptr->y, caster_ptr->x, plev * 2,
+            get_element_types(caster_ptr->realm1)[0],
+            PROJECT_JUMP | PROJECT_KILL | PROJECT_GRID | PROJECT_ITEM, -1);
+        caster_ptr->csp = MAX(0, caster_ptr->csp - plev * MAX(1, plev / 10));
+
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+/*!
+ * @brief 元素魔法コマンドのメインルーチン
+ * @param caster_ptr プレイヤー情報への参照ポインタ
+ * @return なし
+ */
+void do_cmd_element(player_type *caster_ptr)
+{
+    SPELL_IDX i;
+    if (cmd_limit_confused(caster_ptr) || !get_element_power(caster_ptr, &i, FALSE))
+        return;
+
+    mind_type spell = get_elemental_info(caster_ptr, i);
+    PERCENTAGE chance = decide_element_chance(caster_ptr, spell);
+    int mana_cost = decide_element_mana_cost(caster_ptr, spell);
+
+    if (!check_element_mp_sufficiency(caster_ptr, mana_cost))
+        return;
+
+    if (!try_cast_element_spell(caster_ptr, i, chance))
+        return;
+
+    if (mana_cost <= caster_ptr->csp) {
+        caster_ptr->csp -= mana_cost;
+    } else {
+        int oops = mana_cost;
+        caster_ptr->csp = 0;
+        caster_ptr->csp_frac = 0;
+        msg_print(_("精神を集中しすぎて気を失ってしまった!", "You faint from the effort!"));
+        (void)set_paralyzed(caster_ptr, caster_ptr->paralyzed + randint1(5 * oops + 1));
+        chg_virtue(caster_ptr, V_KNOWLEDGE, -10);
+        if (randint0(100) < 50) {
+            bool perm = (randint0(100) < 25);
+            msg_print(_("体を悪くしてしまった!", "You have damaged your health!"));
+            (void)dec_stat(caster_ptr, A_CON, 15 + randint1(10), perm);
+        }
+    }
+
+    take_turn(caster_ptr, 100);
+    set_bits(caster_ptr->redraw, PR_MANA);
+    set_bits(caster_ptr->window_flags, PW_PLAYER | PW_SPELL);
+}
+
+/*!
+ * @brief 現在プレイヤーが使用可能な元素魔法の一覧表示
+ * @param caster_ptr プレイヤー情報への参照ポインタ
+ * @return なし
+ */
+void do_cmd_element_browse(player_type *caster_ptr)
+{
+    SPELL_IDX n = 0;
+    char temp[62 * 5];
+
+    screen_save();
+    while (TRUE) {
+        if (!get_element_power(caster_ptr, &n, TRUE)) {
+            screen_load();
+            return;
+        }
+
+        term_erase(12, 21, 255);
+        term_erase(12, 20, 255);
+        term_erase(12, 19, 255);
+        term_erase(12, 18, 255);
+        term_erase(12, 17, 255);
+        term_erase(12, 16, 255);
+        shape_buffer(get_element_tip(caster_ptr, n), 62, temp, sizeof(temp));
+        for (int j = 0, line = 17; temp[j]; j += (1 + strlen(&temp[j]))) {
+            prt(&temp[j], line, 15);
+            line++;
+        }
+
+        prt(_("何かキーを押して下さい。", "Hit any key."), 0, 0);
+        (void)inkey();
+    }
+}
+
+
+/*!
+ * @brief 元素魔法の単体抹殺の効果を発動する 
+ * @param caster_ptr プレイヤー情報への参照ポインタ
+ * @param em_ptr 魔法効果情報への参照ポインタ
+ * @return 効果処理を続けるかどうか
+ */
+process_result effect_monster_elemental_genocide(player_type *caster_ptr, effect_monster_type *em_ptr)
+{
+    auto types = get_element_types(caster_ptr->realm1);
+    char m_name[160];
+
+    monster_desc(caster_ptr, m_name, em_ptr->m_ptr, 0);
+    msg_format(_("%sが%sを包み込んだ。", "The %s surrounds %s."), types[0], m_name);
+
+    auto realm = static_cast<ElementRealm>(caster_ptr->realm1);
+    switch (realm) {
+    case ElementRealm::FIRE:
+        if (any_bits(em_ptr->r_ptr->r_flagsr, RFR_IM_FIRE))
+            return PROCESS_CONTINUE;
+        break;
+    case ElementRealm::ICE:
+        if (any_bits(em_ptr->r_ptr->r_flagsr, RFR_IM_COLD))
+            return PROCESS_CONTINUE;
+        break;
+    case ElementRealm::SKY:
+        if (any_bits(em_ptr->r_ptr->r_flagsr, RFR_IM_ELEC))
+            return PROCESS_CONTINUE;
+        break;
+    case ElementRealm::SEA:
+        if (any_bits(em_ptr->r_ptr->r_flagsr, RFR_IM_ACID))
+            return PROCESS_CONTINUE;
+        break;
+    case ElementRealm::DARKNESS:
+        if (any_bits(em_ptr->r_ptr->r_flagsr, RFR_RES_DARK) || any_bits(em_ptr->r_ptr->r_flags3, RF3_HURT_LITE))
+            return PROCESS_CONTINUE;
+        break;
+    case ElementRealm::CHAOS:
+        if (any_bits(em_ptr->r_ptr->r_flags3, RF3_NO_CONF))
+            return PROCESS_CONTINUE;
+        break;
+    case ElementRealm::EARTH:
+        if (any_bits(em_ptr->r_ptr->r_flagsr, RFR_RES_SHAR))
+            return PROCESS_CONTINUE;
+        break;
+    case ElementRealm::DEATH:
+        if (any_bits(em_ptr->r_ptr->r_flagsr, RFR_IM_POIS))
+            return PROCESS_CONTINUE;
+        break;
+    default:
+        return PROCESS_CONTINUE;
+    }
+
+    if (em_ptr->seen)
+        em_ptr->obvious = TRUE;
+
+    if (genocide_aux(caster_ptr, em_ptr->g_ptr->m_idx, em_ptr->dam, !em_ptr->who, (em_ptr->r_ptr->level + 1) / 2, _("モンスター消滅", "Genocide One"))) {
+        if (em_ptr->seen_msg)
+            msg_format(_("%sは消滅した!", "%^s disappeared!"), em_ptr->m_name);
+        chg_virtue(caster_ptr, V_VITALITY, -1);
+        return PROCESS_TRUE;
+    }
+
+    em_ptr->skipped = TRUE;
+    return PROCESS_CONTINUE;
+}
+
+/*!
+ * @brief 元素領域とレベルの条件に見合うかチェックする
+ * @param caster_ptr プレイヤー情報への参照ポインタ
+ * @param realm 領域
+ * @param lev プレイヤーレベル
+ * @return 見合うならTRUE、そうでなければFALSE
+ * @detail
+ * レベルに応じて取得する耐性などの判定に使用する
+ */
+bool has_element_resist(player_type *creature_ptr, ElementRealm realm, PLAYER_LEVEL lev)
+{
+    if (creature_ptr->pclass != CLASS_ELEMENTALIST)
+        return FALSE;
+
+    auto prealm = static_cast<ElementRealm>(creature_ptr->realm1);
+    return (prealm == realm && creature_ptr->lev >= lev);
+}
+
+/*!
+ * @brief 領域選択時のカーソル表示(シンボル+領域名)
+ * @param i 位置
+ * @param n 最後尾の位置
+ * @param color 表示色
+ * @return なし
+ */
+static void display_realm_cursor(int i, int n, term_color_type color)
+{
+    char cur[80];
+    char sym;
+    concptr name;
+    if (i == n) {
+        sym = '*';
+        name = _("ランダム", "Random");
+    } else {
+        sym = I2A(i);
+        name = element_types.at(static_cast<ElementRealm>(i + 1)).title.data();
+    }
+    sprintf(cur, "%c) %s", sym, name);
+
+    c_put_str(color, cur, 12 + (i / 5), 2 + 15 * (i % 5));
+}
+
+/*!
+ * @brief 領域選択時の移動キー処理
+ * @param cs 現在位置
+ * @param n 最後尾の位置
+ * @param c 入力キー
+ * @return 新しい位置
+ */
+static int interpret_realm_select_key(int cs, int n, char c)
+{
+    if (c == 'Q')
+        quit(NULL);
+
+    if (c == '8')
+        if (cs >= 5)
+            return cs - 5;
+
+    if (c == '4')
+        if (cs > 0)
+            return cs - 1;
+
+    if (c == '6')
+        if (cs < n)
+            return cs + 1;
+
+    if (c == '2')
+        if (cs + 5 <= n)
+            return cs + 5;
+
+    return cs;
+}
+
+/*!
+ * @brief 領域選択ループ処理
+ * @param creature_ptr プレイヤー情報への参照ポインタ
+ * @param n 最後尾の位置
+ * @return 領域番号
+ */
+static int get_element_realm(player_type *creature_ptr, int is, int n)
+{
+    int cs = MAX(0, is);
+    int os = cs;
+    int k;
+
+    char buf[80];
+    sprintf(buf, _("領域を選んで下さい(%c-%c) ('='初期オプション設定): ", "Choose a realm (%c-%c) ('=' for options): "), I2A(0), I2A(n - 1));
+
+    while (TRUE) {
+        display_realm_cursor(os, n, TERM_WHITE);
+        display_realm_cursor(cs, n, TERM_YELLOW);
+        put_str(buf, 10, 10);
+        os = cs;
+
+        char c = inkey();
+        cs = interpret_realm_select_key(cs, n, c);
+
+        if (c == 'S')
+            return 255;
+
+        if (c == ' ' || c == '\r' || c == '\n') {
+            if (cs == n) {
+                display_realm_cursor(cs, n, TERM_WHITE);
+                cs = randint0(n - 1);
+            }
+            break;
+        }
+
+        if (c == '*') {
+            display_realm_cursor(cs, n, TERM_WHITE);
+            cs = randint0(n - 1);
+            break;
+        }
+
+        k = islower(c) ? A2I(c) : -1;
+        if (k >= 0 && k < n) {
+            display_realm_cursor(cs, n, TERM_WHITE);
+            cs = k;
+            break;
+        }
+
+        k = isupper(c) ? (26 + c - 'A') : -1;
+        if (k >= 26 && k < n) {
+            display_realm_cursor(cs, n, TERM_WHITE);
+            cs = k;
+            break;
+        }
+
+        if (c == '=') {
+            screen_save();
+            do_cmd_options_aux(creature_ptr, OPT_PAGE_BIRTH, _("初期オプション((*)はスコアに影響)", "Birth Options ((*)) affect score"));
+            screen_load();
+        } else if (c != '2' && c != '4' && c != '6' && c != '8')
+            bell();
+    }
+
+    display_realm_cursor(cs, n, TERM_YELLOW);
+    return (cs + 1);
+}
+
+/*!
+ * @brief 領域選択
+ * @param creature_ptr プレイヤー情報への参照ポインタ
+ * @return 領域番号
+ */
+byte select_element_realm(player_type *creature_ptr)
+{
+    clear_from(10);
+
+    int realm_max = static_cast<int>(ElementRealm::MAX);
+    int realm_idx = 1;
+    int row = 16;
+    while (1) {
+        put_str(
+            _("注意:元素系統の選択によりあなたが習得する呪文のタイプが決まります。", "Note: The system of element will determine which spells you can learn."),
+            23, 5);
+
+        for (int i = 0; i < realm_max; i++) {
+            display_realm_cursor(i, realm_max - 1, TERM_WHITE);
+        }
+
+        realm_idx = get_element_realm(creature_ptr, realm_idx - 1, realm_max - 1);
+        if (realm_idx == 255)
+            break;
+
+        auto realm = static_cast<ElementRealm>(realm_idx);
+        char temp[80 * 5];
+        shape_buffer(element_texts.at(realm).data(), 74, temp, sizeof(temp));
+        concptr t = temp;
+        for (int i = 0; i < 5; i++) {
+            if (t[0] == 0)
+                break;
+            prt(t, row + i, 3);
+            t += strlen(t) + 1;
+        }
+
+        if (get_check_strict(creature_ptr, _("よろしいですか?", "Are you sure? "), CHECK_DEFAULT_Y))
+            break;
+
+        clear_from(row);
+    }
+
+    clear_from(10);
+    return (byte)realm_idx;
+}
+
+/*!
+ * @brief クラスパワー情報を追加
+ * @param creature_ptr プレイヤー情報への参照ポインタ
+ * @param rc_ptr レイシャルパワー情報への参照ポインタ
+ * @return なし
+ */
+void switch_element_racial(player_type *creature_ptr, rc_type *rc_ptr)
+{
+    auto realm = static_cast<ElementRealm>(creature_ptr->realm1);
+    switch (realm) {
+    case ElementRealm::FIRE:
+        strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("ライト・エリア", "Light area"));
+        rc_ptr->power_desc[rc_ptr->num].min_level = 3;
+        rc_ptr->power_desc[rc_ptr->num].cost = 5;
+        rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
+        rc_ptr->power_desc[rc_ptr->num].fail = 10;
+        rc_ptr->power_desc[rc_ptr->num++].number = -4;
+        break;
+    case ElementRealm::ICE:
+        strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("フリーズ・モンスター", "Sleep monster"));
+        rc_ptr->power_desc[rc_ptr->num].min_level = 10;
+        rc_ptr->power_desc[rc_ptr->num].cost = 10;
+        rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
+        rc_ptr->power_desc[rc_ptr->num].fail = 15;
+        rc_ptr->power_desc[rc_ptr->num++].number = -4;
+        break;
+    case ElementRealm::SKY:
+        strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("魔力充填", "Recharging"));
+        rc_ptr->power_desc[rc_ptr->num].min_level = 20;
+        rc_ptr->power_desc[rc_ptr->num].cost = 15;
+        rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
+        rc_ptr->power_desc[rc_ptr->num].fail = 25;
+        rc_ptr->power_desc[rc_ptr->num++].number = -4;
+        break;
+    case ElementRealm::SEA:
+        strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("岩石溶解", "Stone to mud"));
+        rc_ptr->power_desc[rc_ptr->num].min_level = 5;
+        rc_ptr->power_desc[rc_ptr->num].cost = 5;
+        rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
+        rc_ptr->power_desc[rc_ptr->num].fail = 10;
+        rc_ptr->power_desc[rc_ptr->num++].number = -4;
+        break;
+    case ElementRealm::DARKNESS:
+        strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("アンデッド従属", "Enslave undead"));
+        rc_ptr->power_desc[rc_ptr->num].min_level = 10;
+        rc_ptr->power_desc[rc_ptr->num].cost = 10;
+        rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
+        rc_ptr->power_desc[rc_ptr->num].fail = 20;
+        rc_ptr->power_desc[rc_ptr->num++].number = -4;
+        break;
+    case ElementRealm::CHAOS:
+        strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("現実変容", "Alter reality"));
+        rc_ptr->power_desc[rc_ptr->num].min_level = 35;
+        rc_ptr->power_desc[rc_ptr->num].cost = 30;
+        rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
+        rc_ptr->power_desc[rc_ptr->num].fail = 40;
+        rc_ptr->power_desc[rc_ptr->num++].number = -4;
+        break;
+    case ElementRealm::EARTH:
+        strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("地震", "Earthquake"));
+        rc_ptr->power_desc[rc_ptr->num].min_level = 25;
+        rc_ptr->power_desc[rc_ptr->num].cost = 15;
+        rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
+        rc_ptr->power_desc[rc_ptr->num].fail = 20;
+        rc_ptr->power_desc[rc_ptr->num++].number = -4;
+        break;
+    case ElementRealm::DEATH:
+        strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("害虫駆除", "Pesticide"));
+        rc_ptr->power_desc[rc_ptr->num].min_level = 5;
+        rc_ptr->power_desc[rc_ptr->num].cost = 3;
+        rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
+        rc_ptr->power_desc[rc_ptr->num].fail = 20;
+        rc_ptr->power_desc[rc_ptr->num++].number = -4;
+        break;
+    default:
+        break;
+    }
+}
+
+/*!
+ * @brief クラスパワーを実行
+ * @param creature_ptr プレイヤー情報への参照ポインタ
+ * @return 実行したらTRUE、しなかったらFALSE
+ */
+bool switch_element_execution(player_type *creature_ptr)
+{
+    auto realm = static_cast<ElementRealm>(creature_ptr->realm1);
+    PLAYER_LEVEL plev = creature_ptr->lev;
+    DIRECTION dir;
+
+    switch (realm) {
+    case ElementRealm::FIRE:
+        (void)lite_area(creature_ptr, damroll(2, plev / 2), plev / 10);
+        break;
+    case ElementRealm::ICE:
+        if (!get_aim_dir(creature_ptr, &dir))
+            return FALSE;
+        (void)project_hook(creature_ptr, GF_OLD_SLEEP, dir, (plev * 2), PROJECT_STOP | PROJECT_KILL | PROJECT_REFLECTABLE);
+        break;
+    case ElementRealm::SKY:
+        (void)recharge(creature_ptr, 120);
+        break;
+    case ElementRealm::SEA:
+        if (!get_aim_dir(creature_ptr, &dir))
+            return FALSE;
+        (void)wall_to_mud(creature_ptr, dir, plev * 3 / 2);
+        break;
+    case ElementRealm::DARKNESS:
+        if (!get_aim_dir(creature_ptr, &dir))
+            return FALSE;
+        (void)control_one_undead(creature_ptr, dir, plev * 3 / 2);
+        break;
+    case ElementRealm::CHAOS:
+        reserve_alter_reality(creature_ptr, randint0(21) + 15);
+        break;
+    case ElementRealm::EARTH:
+        (void)earthquake(creature_ptr, creature_ptr->y, creature_ptr->x, 10, 0);
+        break;
+    case ElementRealm::DEATH:
+        (void)dispel_monsters(creature_ptr, plev / 2);
+        break;
+    default:
+        return FALSE;
+    }
+
+    return TRUE;
+}
diff --git a/src/mind/mind-elementalist.h b/src/mind/mind-elementalist.h
new file mode 100644 (file)
index 0000000..89fba14
--- /dev/null
@@ -0,0 +1,31 @@
+#pragma once
+
+#include "system/angband.h"
+#include "spell/spell-types.h"
+
+enum class ElementRealm {
+       FIRE = 1,
+       ICE = 2,
+       SKY = 3,
+       SEA = 4,
+       DARKNESS = 5,
+       CHAOS = 6,
+       EARTH = 7,
+       DEATH = 8,
+       MAX
+};
+
+typedef struct player_type player_type;
+typedef struct effect_monster_type effect_monster_type;
+typedef struct rc_type rc_type;
+
+concptr get_element_title(int realm_idx);
+spells_type get_element_type(int realm_idx, int n);
+concptr get_element_name(int realm_idx, int n);
+void do_cmd_element(player_type *caster_ptr);
+void do_cmd_element_browse(player_type *caster_ptr);
+process_result effect_monster_elemental_genocide(player_type *caster_ptr, effect_monster_type *em_ptr);
+bool has_element_resist(player_type *creature_ptr, ElementRealm realm, PLAYER_LEVEL lev);
+byte select_element_realm(player_type *creature_ptr);
+void switch_element_racial(player_type *creature_ptr, rc_type *rc_ptr);
+bool switch_element_execution(player_type *creature_ptr);
index 22d0451..3ecdf3e 100644 (file)
@@ -113,6 +113,9 @@ bool psychometry(player_type *caster_ptr)
     case TV_FIGURINE:
         okay = TRUE;
         break;
+
+    default:
+        break;
     }
 
     autopick_alter_item(caster_ptr, item, (bool)(okay && destroy_feeling));
index c561ac3..29239a3 100644 (file)
@@ -6,4 +6,5 @@ enum mind_kind_type {
     MIND_BERSERKER = 2, /*!< 特殊能力: 怒り */
     MIND_MIRROR_MASTER = 3, /*!< 特殊能力: 鏡魔法 */
     MIND_NINJUTSU = 4, /*!< 特殊能力: 忍術 */
+    MIND_ELEMENTAL = 5, //!< 特殊能力: 元素魔法 */
 };
index 0f657bd..c6d7390 100644 (file)
@@ -26,6 +26,9 @@ bool item_tester_hook_melee_ammo(player_type *player_ptr, object_type *o_ptr)
         if (o_ptr->sval != SV_POISON_NEEDLE)
             return TRUE;
     }
+
+    default:
+        break;
     }
 
     return FALSE;
index f9c7344..66d8420 100644 (file)
@@ -239,5 +239,10 @@ void describe_monster_attack_method(monap_type *monap_ptr)
         monster_attack_show(monap_ptr);
         break;
     }
+
+    case RBM_NONE:
+    case RBM_SHOOT:
+    case NB_RBM_TYPE:
+        break;
     }
 }
index 64061f1..72b5b03 100644 (file)
@@ -478,5 +478,8 @@ void switch_monster_blow_to_player(player_type *target_ptr, monap_type *monap_pt
         monap_ptr->obvious = TRUE;
         monap_ptr->damage = 0;
         break;
+
+    case NB_RBE_TYPE:
+        break;
     }
 }
index b728915..54c7eb9 100644 (file)
@@ -109,7 +109,8 @@ static void drop_corpse(player_type *player_ptr, monster_death_type *md_ptr)
     floor_type *floor_ptr = player_ptr->current_floor_ptr;
     bool is_drop_corpse = one_in_(md_ptr->r_ptr->flags1 & RF1_UNIQUE ? 1 : 4);
     is_drop_corpse &= (md_ptr->r_ptr->flags9 & (RF9_DROP_CORPSE | RF9_DROP_SKELETON)) != 0;
-    is_drop_corpse &= !(floor_ptr->inside_arena || player_ptr->phase_out || md_ptr->cloned || ((md_ptr->m_ptr->r_idx == today_mon) && is_pet(md_ptr->m_ptr)));
+    is_drop_corpse &= !(floor_ptr->inside_arena || player_ptr->phase_out || md_ptr->cloned
+        || ((md_ptr->m_ptr->r_idx == current_world_ptr->today_mon) && is_pet(md_ptr->m_ptr)));
     if (!is_drop_corpse)
         return;
 
@@ -380,4 +381,4 @@ concptr extract_note_dies(MONRACE_IDX r_idx)
             return _("は爆発して粉々になった。", " explodes into tiny shreds.");
 
     return _("を倒した。", " is destroyed.");
-}
\ No newline at end of file
+}
index c625f48..668c9b6 100644 (file)
@@ -1,4 +1,5 @@
-#include "monster-floor/monster-lite.h"
+#include <vector>
+
 #include "core/player-update-types.h"
 #include "dungeon/dungeon-flag-types.h"
 #include "dungeon/dungeon.h"
@@ -6,6 +7,7 @@
 #include "grid/feature-flag-types.h"
 #include "grid/grid.h"
 #include "monster-floor/monster-lite-util.h"
+#include "monster-floor/monster-lite.h"
 #include "monster-race/monster-race.h"
 #include "monster-race/race-flags7.h"
 #include "monster/monster-status.h"
 #include "view/display-messages.h"
 #include "world/world.h"
 
+struct Point {
+    int y;
+    int x;
+    Point(const int y, const int x)
+        : y(y)
+        , x(x)
+    {
+    }
+};
+
 /*!
  * @brief モンスターによる光量状態更新 / Add a square to the changes array
  * @param subject_ptr 主観となるクリーチャーの参照ポインタ
+ * @param points 座標たちを記録する配列
  * @param y Y座標
  * @param x X座標
  */
-static void update_monster_lite(player_type *subject_ptr, const POSITION y, const POSITION x, monster_lite_type *ml_ptr)
+static void update_monster_lite(
+    player_type *const subject_ptr, std::vector<Point> &points, const POSITION y, const POSITION x, const monster_lite_type *const ml_ptr)
 {
     grid_type *g_ptr;
     int dpf, d;
@@ -61,9 +75,7 @@ static void update_monster_lite(player_type *subject_ptr, const POSITION y, cons
     }
 
     if (!(g_ptr->info & CAVE_MNDK)) {
-        tmp_pos.x[tmp_pos.n] = x;
-        tmp_pos.y[tmp_pos.n] = y;
-        tmp_pos.n++;
+        points.emplace_back(y, x);
     } else {
         g_ptr->info &= ~(CAVE_MNDK);
     }
@@ -74,7 +86,8 @@ static void update_monster_lite(player_type *subject_ptr, const POSITION y, cons
 /*
  * Add a square to the changes array
  */
-static void update_monster_dark(player_type *subject_ptr, const POSITION y, const POSITION x, monster_lite_type *ml_ptr)
+static void update_monster_dark(
+    player_type *const subject_ptr, std::vector<Point> &points, const POSITION y, const POSITION x, const monster_lite_type *const ml_ptr)
 {
     grid_type *g_ptr;
     int midpoint, dpf, d;
@@ -112,9 +125,7 @@ static void update_monster_dark(player_type *subject_ptr, const POSITION y, cons
         }
     }
 
-    tmp_pos.x[tmp_pos.n] = x;
-    tmp_pos.y[tmp_pos.n] = y;
-    tmp_pos.n++;
+    points.emplace_back(y, x);
     g_ptr->info |= CAVE_MNDK;
 }
 
@@ -127,7 +138,10 @@ static void update_monster_dark(player_type *subject_ptr, const POSITION y, cons
  */
 void update_mon_lite(player_type *subject_ptr)
 {
-    void (*add_mon_lite)(player_type *, const POSITION, const POSITION, monster_lite_type *);
+    // 座標たちを記録する配列。
+    std::vector<Point> points;
+
+    void (*add_mon_lite)(player_type *, std::vector<Point> &, const POSITION, const POSITION, const monster_lite_type *);
     int dis_lim = ((d_info[subject_ptr->dungeon_idx].flags1 & DF1_DARKNESS) && !subject_ptr->see_nocto) ? (MAX_SIGHT / 2 + 1) : (MAX_SIGHT + 3);
     floor_type *floor_ptr = subject_ptr->current_floor_ptr;
     for (int i = 0; i < floor_ptr->mon_lite_n; i++) {
@@ -137,7 +151,6 @@ void update_mon_lite(player_type *subject_ptr)
         g_ptr->info &= ~(CAVE_MNLT | CAVE_MNDK);
     }
 
-    tmp_pos.n = 0;
     if (!current_world_ptr->timewalk_m_idx) {
         monster_type *m_ptr;
         monster_race *r_ptr;
@@ -185,64 +198,64 @@ void update_mon_lite(player_type *subject_ptr)
 
             monster_lite_type tmp_ml;
             monster_lite_type *ml_ptr = initialize_monster_lite_type(floor_ptr, &tmp_ml, m_ptr);
-            add_mon_lite(subject_ptr, ml_ptr->mon_fy, ml_ptr->mon_fx, ml_ptr);
-            add_mon_lite(subject_ptr, ml_ptr->mon_fy + 1, ml_ptr->mon_fx, ml_ptr);
-            add_mon_lite(subject_ptr, ml_ptr->mon_fy - 1, ml_ptr->mon_fx, ml_ptr);
-            add_mon_lite(subject_ptr, ml_ptr->mon_fy, ml_ptr->mon_fx + 1, ml_ptr);
-            add_mon_lite(subject_ptr, ml_ptr->mon_fy, ml_ptr->mon_fx - 1, ml_ptr);
-            add_mon_lite(subject_ptr, ml_ptr->mon_fy + 1, ml_ptr->mon_fx + 1, ml_ptr);
-            add_mon_lite(subject_ptr, ml_ptr->mon_fy + 1, ml_ptr->mon_fx - 1, ml_ptr);
-            add_mon_lite(subject_ptr, ml_ptr->mon_fy - 1, ml_ptr->mon_fx + 1, ml_ptr);
-            add_mon_lite(subject_ptr, ml_ptr->mon_fy - 1, ml_ptr->mon_fx - 1, ml_ptr);
+            add_mon_lite(subject_ptr, points, ml_ptr->mon_fy, ml_ptr->mon_fx, ml_ptr);
+            add_mon_lite(subject_ptr, points, ml_ptr->mon_fy + 1, ml_ptr->mon_fx, ml_ptr);
+            add_mon_lite(subject_ptr, points, ml_ptr->mon_fy - 1, ml_ptr->mon_fx, ml_ptr);
+            add_mon_lite(subject_ptr, points, ml_ptr->mon_fy, ml_ptr->mon_fx + 1, ml_ptr);
+            add_mon_lite(subject_ptr, points, ml_ptr->mon_fy, ml_ptr->mon_fx - 1, ml_ptr);
+            add_mon_lite(subject_ptr, points, ml_ptr->mon_fy + 1, ml_ptr->mon_fx + 1, ml_ptr);
+            add_mon_lite(subject_ptr, points, ml_ptr->mon_fy + 1, ml_ptr->mon_fx - 1, ml_ptr);
+            add_mon_lite(subject_ptr, points, ml_ptr->mon_fy - 1, ml_ptr->mon_fx + 1, ml_ptr);
+            add_mon_lite(subject_ptr, points, ml_ptr->mon_fy - 1, ml_ptr->mon_fx - 1, ml_ptr);
             if (rad < 2)
                 continue;
 
             grid_type *g_ptr;
             if (cave_has_flag_bold(subject_ptr->current_floor_ptr, ml_ptr->mon_fy + 1, ml_ptr->mon_fx, f_flag)) {
-                add_mon_lite(subject_ptr, ml_ptr->mon_fy + 2, ml_ptr->mon_fx + 1, ml_ptr);
-                add_mon_lite(subject_ptr, ml_ptr->mon_fy + 2, ml_ptr->mon_fx, ml_ptr);
-                add_mon_lite(subject_ptr, ml_ptr->mon_fy + 2, ml_ptr->mon_fx - 1, ml_ptr);
+                add_mon_lite(subject_ptr, points, ml_ptr->mon_fy + 2, ml_ptr->mon_fx + 1, ml_ptr);
+                add_mon_lite(subject_ptr, points, ml_ptr->mon_fy + 2, ml_ptr->mon_fx, ml_ptr);
+                add_mon_lite(subject_ptr, points, ml_ptr->mon_fy + 2, ml_ptr->mon_fx - 1, ml_ptr);
                 g_ptr = &floor_ptr->grid_array[ml_ptr->mon_fy + 2][ml_ptr->mon_fx];
                 if ((rad == 3) && cave_has_flag_grid(g_ptr, f_flag)) {
-                    add_mon_lite(subject_ptr, ml_ptr->mon_fy + 3, ml_ptr->mon_fx + 1, ml_ptr);
-                    add_mon_lite(subject_ptr, ml_ptr->mon_fy + 3, ml_ptr->mon_fx, ml_ptr);
-                    add_mon_lite(subject_ptr, ml_ptr->mon_fy + 3, ml_ptr->mon_fx - 1, ml_ptr);
+                    add_mon_lite(subject_ptr, points, ml_ptr->mon_fy + 3, ml_ptr->mon_fx + 1, ml_ptr);
+                    add_mon_lite(subject_ptr, points, ml_ptr->mon_fy + 3, ml_ptr->mon_fx, ml_ptr);
+                    add_mon_lite(subject_ptr, points, ml_ptr->mon_fy + 3, ml_ptr->mon_fx - 1, ml_ptr);
                 }
             }
 
             if (cave_has_flag_bold(subject_ptr->current_floor_ptr, ml_ptr->mon_fy - 1, ml_ptr->mon_fx, f_flag)) {
-                add_mon_lite(subject_ptr, ml_ptr->mon_fy - 2, ml_ptr->mon_fx + 1, ml_ptr);
-                add_mon_lite(subject_ptr, ml_ptr->mon_fy - 2, ml_ptr->mon_fx, ml_ptr);
-                add_mon_lite(subject_ptr, ml_ptr->mon_fy - 2, ml_ptr->mon_fx - 1, ml_ptr);
+                add_mon_lite(subject_ptr, points, ml_ptr->mon_fy - 2, ml_ptr->mon_fx + 1, ml_ptr);
+                add_mon_lite(subject_ptr, points, ml_ptr->mon_fy - 2, ml_ptr->mon_fx, ml_ptr);
+                add_mon_lite(subject_ptr, points, ml_ptr->mon_fy - 2, ml_ptr->mon_fx - 1, ml_ptr);
                 g_ptr = &floor_ptr->grid_array[ml_ptr->mon_fy - 2][ml_ptr->mon_fx];
                 if ((rad == 3) && cave_has_flag_grid(g_ptr, f_flag)) {
-                    add_mon_lite(subject_ptr, ml_ptr->mon_fy - 3, ml_ptr->mon_fx + 1, ml_ptr);
-                    add_mon_lite(subject_ptr, ml_ptr->mon_fy - 3, ml_ptr->mon_fx, ml_ptr);
-                    add_mon_lite(subject_ptr, ml_ptr->mon_fy - 3, ml_ptr->mon_fx - 1, ml_ptr);
+                    add_mon_lite(subject_ptr, points, ml_ptr->mon_fy - 3, ml_ptr->mon_fx + 1, ml_ptr);
+                    add_mon_lite(subject_ptr, points, ml_ptr->mon_fy - 3, ml_ptr->mon_fx, ml_ptr);
+                    add_mon_lite(subject_ptr, points, ml_ptr->mon_fy - 3, ml_ptr->mon_fx - 1, ml_ptr);
                 }
             }
 
             if (cave_has_flag_bold(subject_ptr->current_floor_ptr, ml_ptr->mon_fy, ml_ptr->mon_fx + 1, f_flag)) {
-                add_mon_lite(subject_ptr, ml_ptr->mon_fy + 1, ml_ptr->mon_fx + 2, ml_ptr);
-                add_mon_lite(subject_ptr, ml_ptr->mon_fy, ml_ptr->mon_fx + 2, ml_ptr);
-                add_mon_lite(subject_ptr, ml_ptr->mon_fy - 1, ml_ptr->mon_fx + 2, ml_ptr);
+                add_mon_lite(subject_ptr, points, ml_ptr->mon_fy + 1, ml_ptr->mon_fx + 2, ml_ptr);
+                add_mon_lite(subject_ptr, points, ml_ptr->mon_fy, ml_ptr->mon_fx + 2, ml_ptr);
+                add_mon_lite(subject_ptr, points, ml_ptr->mon_fy - 1, ml_ptr->mon_fx + 2, ml_ptr);
                 g_ptr = &floor_ptr->grid_array[ml_ptr->mon_fy][ml_ptr->mon_fx + 2];
                 if ((rad == 3) && cave_has_flag_grid(g_ptr, f_flag)) {
-                    add_mon_lite(subject_ptr, ml_ptr->mon_fy + 1, ml_ptr->mon_fx + 3, ml_ptr);
-                    add_mon_lite(subject_ptr, ml_ptr->mon_fy, ml_ptr->mon_fx + 3, ml_ptr);
-                    add_mon_lite(subject_ptr, ml_ptr->mon_fy - 1, ml_ptr->mon_fx + 3, ml_ptr);
+                    add_mon_lite(subject_ptr, points, ml_ptr->mon_fy + 1, ml_ptr->mon_fx + 3, ml_ptr);
+                    add_mon_lite(subject_ptr, points, ml_ptr->mon_fy, ml_ptr->mon_fx + 3, ml_ptr);
+                    add_mon_lite(subject_ptr, points, ml_ptr->mon_fy - 1, ml_ptr->mon_fx + 3, ml_ptr);
                 }
             }
 
             if (cave_has_flag_bold(subject_ptr->current_floor_ptr, ml_ptr->mon_fy, ml_ptr->mon_fx - 1, f_flag)) {
-                add_mon_lite(subject_ptr, ml_ptr->mon_fy + 1, ml_ptr->mon_fx - 2, ml_ptr);
-                add_mon_lite(subject_ptr, ml_ptr->mon_fy, ml_ptr->mon_fx - 2, ml_ptr);
-                add_mon_lite(subject_ptr, ml_ptr->mon_fy - 1, ml_ptr->mon_fx - 2, ml_ptr);
+                add_mon_lite(subject_ptr, points, ml_ptr->mon_fy + 1, ml_ptr->mon_fx - 2, ml_ptr);
+                add_mon_lite(subject_ptr, points, ml_ptr->mon_fy, ml_ptr->mon_fx - 2, ml_ptr);
+                add_mon_lite(subject_ptr, points, ml_ptr->mon_fy - 1, ml_ptr->mon_fx - 2, ml_ptr);
                 g_ptr = &floor_ptr->grid_array[ml_ptr->mon_fy][ml_ptr->mon_fx - 2];
                 if ((rad == 3) && cave_has_flag_grid(g_ptr, f_flag)) {
-                    add_mon_lite(subject_ptr, ml_ptr->mon_fy + 1, ml_ptr->mon_fx - 3, ml_ptr);
-                    add_mon_lite(subject_ptr, ml_ptr->mon_fy, ml_ptr->mon_fx - 3, ml_ptr);
-                    add_mon_lite(subject_ptr, ml_ptr->mon_fy - 1, ml_ptr->mon_fx - 3, ml_ptr);
+                    add_mon_lite(subject_ptr, points, ml_ptr->mon_fy + 1, ml_ptr->mon_fx - 3, ml_ptr);
+                    add_mon_lite(subject_ptr, points, ml_ptr->mon_fy, ml_ptr->mon_fx - 3, ml_ptr);
+                    add_mon_lite(subject_ptr, points, ml_ptr->mon_fy - 1, ml_ptr->mon_fx - 3, ml_ptr);
                 }
             }
 
@@ -250,20 +263,20 @@ void update_mon_lite(player_type *subject_ptr)
                 continue;
 
             if (cave_has_flag_bold(subject_ptr->current_floor_ptr, ml_ptr->mon_fy + 1, ml_ptr->mon_fx + 1, f_flag))
-                add_mon_lite(subject_ptr, ml_ptr->mon_fy + 2, ml_ptr->mon_fx + 2, ml_ptr);
+                add_mon_lite(subject_ptr, points, ml_ptr->mon_fy + 2, ml_ptr->mon_fx + 2, ml_ptr);
 
             if (cave_has_flag_bold(subject_ptr->current_floor_ptr, ml_ptr->mon_fy + 1, ml_ptr->mon_fx - 1, f_flag))
-                add_mon_lite(subject_ptr, ml_ptr->mon_fy + 2, ml_ptr->mon_fx - 2, ml_ptr);
+                add_mon_lite(subject_ptr, points, ml_ptr->mon_fy + 2, ml_ptr->mon_fx - 2, ml_ptr);
 
             if (cave_has_flag_bold(subject_ptr->current_floor_ptr, ml_ptr->mon_fy - 1, ml_ptr->mon_fx + 1, f_flag))
-                add_mon_lite(subject_ptr, ml_ptr->mon_fy - 2, ml_ptr->mon_fx + 2, ml_ptr);
+                add_mon_lite(subject_ptr, points, ml_ptr->mon_fy - 2, ml_ptr->mon_fx + 2, ml_ptr);
 
             if (cave_has_flag_bold(subject_ptr->current_floor_ptr, ml_ptr->mon_fy - 1, ml_ptr->mon_fx - 1, f_flag))
-                add_mon_lite(subject_ptr, ml_ptr->mon_fy - 2, ml_ptr->mon_fx - 2, ml_ptr);
+                add_mon_lite(subject_ptr, points, ml_ptr->mon_fy - 2, ml_ptr->mon_fx - 2, ml_ptr);
         }
     }
 
-    s16b end_temp = tmp_pos.n;
+    const auto end_temp = size(points);
     for (int i = 0; i < floor_ptr->mon_lite_n; i++) {
         POSITION fx = floor_ptr->mon_lite_x[i];
         POSITION fy = floor_ptr->mon_lite_y[i];
@@ -275,17 +288,14 @@ void update_mon_lite(player_type *subject_ptr)
         } else if ((g_ptr->info & (CAVE_VIEW | CAVE_MNDK)) == CAVE_VIEW)
             cave_note_and_redraw_later(floor_ptr, g_ptr, fy, fx);
 
-        tmp_pos.x[tmp_pos.n] = fx;
-        tmp_pos.y[tmp_pos.n] = fy;
-        tmp_pos.n++;
+        points.emplace_back(fy, fx);
     }
 
     floor_ptr->mon_lite_n = 0;
-    for (int i = 0; i < end_temp; i++) {
-        POSITION fx = tmp_pos.x[i];
-        POSITION fy = tmp_pos.y[i];
-        grid_type *g_ptr;
-        g_ptr = &floor_ptr->grid_array[fy][fx];
+    for (size_t i = 0; i < end_temp; i++) {
+        const auto [fy, fx] = points[i];
+
+        grid_type *const g_ptr = &floor_ptr->grid_array[fy][fx];
         if (g_ptr->info & CAVE_MNLT) {
             if ((g_ptr->info & (CAVE_VIEW | CAVE_TEMP)) == CAVE_VIEW)
                 cave_note_and_redraw_later(floor_ptr, g_ptr, fy, fx);
@@ -297,10 +307,11 @@ void update_mon_lite(player_type *subject_ptr)
         floor_ptr->mon_lite_n++;
     }
 
-    for (int i = end_temp; i < tmp_pos.n; i++)
-        floor_ptr->grid_array[tmp_pos.y[i]][tmp_pos.x[i]].info &= ~(CAVE_TEMP | CAVE_XTRA);
+    for (size_t i = end_temp; i < size(points); i++) {
+        const auto [y, x] = points[i];
+        floor_ptr->grid_array[y][x].info &= ~(CAVE_TEMP | CAVE_XTRA);
+    }
 
-    tmp_pos.n = 0;
     subject_ptr->update |= PU_DELAY_VIS;
     subject_ptr->monlite = (floor_ptr->grid_array[subject_ptr->y][subject_ptr->x].info & CAVE_MNLT) != 0;
     if (!(subject_ptr->special_defense & NINJA_S_STEALTH)) {
index 72e1507..bc0c640 100644 (file)
@@ -10,6 +10,7 @@
 #include "monster/monster-info.h"
 #include "monster/smart-learn-types.h"
 #include "mspell/assign-monster-spell.h"
+#include "mspell/mspell.h"
 #include "spell-kind/spells-teleport.h"
 #include "system/floor-type-definition.h"
 #include "view/display-messages.h"
index 00ed481..290853d 100644 (file)
@@ -636,10 +636,13 @@ bool vault_aux_dragon(player_type *player_ptr, MONRACE_IDX r_idx)
     if (none_bits(r_ptr->flags3, RF3_DRAGON))
         return FALSE;
 
-    if (none_bits(r_ptr->flags4, vault_aux_dragon_mask4))
+    if (any_bits(r_ptr->flags3, RF3_UNDEAD))
         return FALSE;
 
-    if (any_bits(r_ptr->flags3, RF3_UNDEAD))
+    BIT_FLAGS flags = RF4_BREATH_MASK;
+    reset_bits(flags, vault_aux_dragon_mask4);
+
+    if (any_bits(r_ptr->flags4, flags) || !all_bits(r_ptr->flags4, vault_aux_dragon_mask4))
         return FALSE;
 
     return TRUE;
index df357e9..4c54fb3 100644 (file)
 #include "pet/pet-fall-off.h"
 #include "system/alloc-entries.h"
 #include "system/floor-type-definition.h"
+#include "util/probability-table.h"
 #include "view/display-messages.h"
 #include "world/world.h"
+#include <iterator>
 
 #define HORDE_NOGOOD 0x01 /*!< (未実装フラグ)HORDE生成でGOODなモンスターの生成を禁止する? */
 #define HORDE_NOEVIL 0x02 /*!< (未実装フラグ)HORDE生成でEVILなモンスターの生成を禁止する? */
@@ -81,10 +83,7 @@ MONSTER_IDX m_pop(floor_type *floor_ptr)
  */
 MONRACE_IDX get_mon_num(player_type *player_ptr, DEPTH min_level, DEPTH max_level, BIT_FLAGS option)
 {
-    int i, j, p;
     int r_idx;
-    long value, total_prob3;
-    int mon_num = 0;
     monster_race *r_ptr;
     alloc_entry *table = alloc_race_table;
 
@@ -130,11 +129,10 @@ MONRACE_IDX get_mon_num(player_type *player_ptr, DEPTH min_level, DEPTH max_leve
         }
     }
 
-    total_prob3 = 0L;
+    ProbabilityTable<int> prob_table;
 
     /* Process probabilities */
-    for (i = 0; i < alloc_race_size; i++) {
-        table[i].prob3 = 0;
+    for (int i = 0; i < alloc_race_size; i++) {
         if (table[i].level < min_level)
             continue;
         if (max_level < table[i].level)
@@ -158,63 +156,32 @@ MONRACE_IDX get_mon_num(player_type *player_ptr, DEPTH min_level, DEPTH max_leve
             }
         }
 
-        table[i].prob3 = table[i].prob2;
-
-        if (table[i].prob3 > 0) {
-            mon_num++;
-            total_prob3 += table[i].prob3;
-        }
+        prob_table.entry_item(i, table[i].prob2);
     }
 
     if (cheat_hear) {
-        msg_format(_("モンスター第3次候補数:%d(%d-%dF)%d ", "monster third selection:%d(%d-%dF)%d "), mon_num, min_level, max_level, total_prob3);
+        msg_format(_("モンスター第3次候補数:%d(%d-%dF)%d ", "monster third selection:%d(%d-%dF)%d "), prob_table.item_count(), min_level, max_level,
+            prob_table.total_prob());
     }
 
-    if (total_prob3 <= 0)
+    if (prob_table.empty())
         return 0;
 
-    value = randint0(total_prob3);
-    int found_count = 0;
-    for (i = 0; i < alloc_race_size; i++) {
-        if (value < table[i].prob3)
-            break;
-        value = value - table[i].prob3;
-        found_count++;
-    }
-
-    p = randint0(100);
+    // 40%で1回、50%で2回、10%で3回抽選し、その中で一番レベルが高いモンスターを選択する
+    int n = 1;
 
-    /* Try for a "harder" monster once (50%) or twice (10%) */
-    if (p < 60) {
-        j = found_count;
-        value = randint0(total_prob3);
-        for (found_count = 0; found_count < alloc_race_size; found_count++) {
-            if (value < table[found_count].prob3)
-                break;
+    const int p = randint0(100);
+    if (p < 60)
+        n++;
+    if (p < 10)
+        n++;
 
-            value = value - table[found_count].prob3;
-        }
+    std::vector<int> result;
+    ProbabilityTable<int>::lottery(std::back_inserter(result), prob_table, n);
 
-        if (table[found_count].level < table[j].level)
-            found_count = j;
-    }
-
-    /* Try for a "harder" monster twice (10%) */
-    if (p < 10) {
-        j = found_count;
-        value = randint0(total_prob3);
-        for (found_count = 0; found_count < alloc_race_size; found_count++) {
-            if (value < table[found_count].prob3)
-                break;
-
-            value = value - table[found_count].prob3;
-        }
-
-        if (table[found_count].level < table[j].level)
-            found_count = j;
-    }
+    auto it = std::max_element(result.begin(), result.end(), [table](int a, int b) { return table[a].level < table[b].level; });
 
-    return (table[found_count].index);
+    return (table[*it].index);
 }
 
 /*!
index de115e6..1c79221 100644 (file)
  */
 
 #include "mspell/assign-monster-spell.h"
+#include "blue-magic/blue-magic-checker.h"
 #include "mspell/mspell-ball.h"
 #include "mspell/mspell-bolt.h"
 #include "mspell/mspell-breath.h"
 #include "mspell/mspell-curse.h"
 #include "mspell/mspell-dispel.h"
 #include "mspell/mspell-floor.h"
+#include "mspell/mspell-learn-checker.h"
 #include "mspell/mspell-particularity.h"
 #include "mspell/mspell-special.h"
 #include "mspell/mspell-status.h"
 #include "mspell/mspell-summon.h"
 #include "mspell/mspell-type.h"
 #include "mspell/mspell-util.h"
+#include "mspell/mspell.h"
 #include "spell/spell-types.h"
 
 /*!
- * @brief モンスターからプレイヤーへの呪文の振り分け関数。 /
- * @param SPELL_NUM モンスター魔法ID (monster_spell_typeのenum値とは異なる)
+ * @brief SPELL_IDX を monster_spell_type に変換する。
+ */
+static int spell_idx_to_monster_spell_type(const SPELL_IDX idx)
+{
+    return idx - RF4_SPELL_START;
+}
+
+static MonsterSpellResult monspell_to_player_impl(player_type *target_ptr, SPELL_IDX ms_type, POSITION y, POSITION x, MONSTER_IDX m_idx)
+{
+    // clang-format off
+    switch (ms_type) {
+    case RF4_SPELL_START + 0:  return spell_RF4_SHRIEK(m_idx, target_ptr, 0, MONSTER_TO_PLAYER); /* RF4_SHRIEK */
+    case RF4_SPELL_START + 1:  break;   /* RF4_XXX1 */
+    case RF4_SPELL_START + 2:  return spell_RF4_DISPEL(m_idx, target_ptr, 0, MONSTER_TO_PLAYER); /* RF4_DISPEL */
+    case RF4_SPELL_START + 3:  return spell_RF4_ROCKET(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER);  /* RF4_ROCKET */
+    case RF4_SPELL_START + 4:  return spell_RF4_SHOOT(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER);   /* RF4_SHOOT */
+    case RF4_SPELL_START + 5:  break;   /* RF4_XXX2 */
+    case RF4_SPELL_START + 6:  break;   /* RF4_XXX3 */
+    case RF4_SPELL_START + 7:  break;   /* RF4_XXX4 */
+    case RF4_SPELL_START + 8:  return spell_RF4_BREATH(target_ptr, GF_ACID, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_ACID */
+    case RF4_SPELL_START + 9:  return spell_RF4_BREATH(target_ptr, GF_ELEC, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_ELEC */
+    case RF4_SPELL_START + 10: return spell_RF4_BREATH(target_ptr, GF_FIRE, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_FIRE */
+    case RF4_SPELL_START + 11: return spell_RF4_BREATH(target_ptr, GF_COLD, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_COLD */
+    case RF4_SPELL_START + 12: return spell_RF4_BREATH(target_ptr, GF_POIS, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_POIS */
+    case RF4_SPELL_START + 13: return spell_RF4_BREATH(target_ptr, GF_NETHER, y, x, m_idx, 0, MONSTER_TO_PLAYER);   /* RF4_BR_NETH */
+    case RF4_SPELL_START + 14: return spell_RF4_BREATH(target_ptr, GF_LITE, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_LITE */
+    case RF4_SPELL_START + 15: return spell_RF4_BREATH(target_ptr, GF_DARK, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_DARK */
+    case RF4_SPELL_START + 16: return spell_RF4_BREATH(target_ptr, GF_CONFUSION, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_CONF */
+    case RF4_SPELL_START + 17: return spell_RF4_BREATH(target_ptr, GF_SOUND, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_SOUN */
+    case RF4_SPELL_START + 18: return spell_RF4_BREATH(target_ptr, GF_CHAOS, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_CHAO */
+    case RF4_SPELL_START + 19: return spell_RF4_BREATH(target_ptr, GF_DISENCHANT, y, x, m_idx, 0, MONSTER_TO_PLAYER);   /* RF4_BR_DISE */
+    case RF4_SPELL_START + 20: return spell_RF4_BREATH(target_ptr, GF_NEXUS, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_NEXU */
+    case RF4_SPELL_START + 21: return spell_RF4_BREATH(target_ptr, GF_TIME, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_TIME */
+    case RF4_SPELL_START + 22: return spell_RF4_BREATH(target_ptr, GF_INERTIAL, y, x, m_idx, 0, MONSTER_TO_PLAYER);  /* RF4_BR_INER */
+    case RF4_SPELL_START + 23: return spell_RF4_BREATH(target_ptr, GF_GRAVITY, y, x, m_idx, 0, MONSTER_TO_PLAYER);  /* RF4_BR_GRAV */
+    case RF4_SPELL_START + 24: return spell_RF4_BREATH(target_ptr, GF_SHARDS, y, x, m_idx, 0, MONSTER_TO_PLAYER);   /* RF4_BR_SHAR */
+    case RF4_SPELL_START + 25: return spell_RF4_BREATH(target_ptr, GF_PLASMA, y, x, m_idx, 0, MONSTER_TO_PLAYER);   /* RF4_BR_PLAS */
+    case RF4_SPELL_START + 26: return spell_RF4_BREATH(target_ptr, GF_FORCE, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_WALL */
+    case RF4_SPELL_START + 27: return spell_RF4_BREATH(target_ptr, GF_MANA, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_MANA */
+    case RF4_SPELL_START + 28: return spell_RF4_BA_NUKE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BA_NUKE */
+    case RF4_SPELL_START + 29: return spell_RF4_BREATH(target_ptr, GF_NUKE, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_NUKE */
+    case RF4_SPELL_START + 30: return spell_RF4_BA_CHAO(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BA_CHAO */
+    case RF4_SPELL_START + 31: return spell_RF4_BREATH(target_ptr, GF_DISINTEGRATE, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_DISI */
+    case RF5_SPELL_START + 0:  return spell_RF5_BA_ACID(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_ACID */
+    case RF5_SPELL_START + 1:  return spell_RF5_BA_ELEC(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_ELEC */
+    case RF5_SPELL_START + 2:  return spell_RF5_BA_FIRE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_FIRE */
+    case RF5_SPELL_START + 3:  return spell_RF5_BA_COLD(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_COLD */
+    case RF5_SPELL_START + 4:  return spell_RF5_BA_POIS(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_POIS */
+    case RF5_SPELL_START + 5:  return spell_RF5_BA_NETH(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_NETH */
+    case RF5_SPELL_START + 6:  return spell_RF5_BA_WATE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_WATE */
+    case RF5_SPELL_START + 7:  return spell_RF5_BA_MANA(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_MANA */
+    case RF5_SPELL_START + 8:  return spell_RF5_BA_DARK(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_DARK */
+    case RF5_SPELL_START + 9:  return spell_RF5_DRAIN_MANA(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER);  /* RF5_DRAIN_MANA */
+    case RF5_SPELL_START + 10: return spell_RF5_MIND_BLAST(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER);  /* RF5_MIND_BLAST */
+    case RF5_SPELL_START + 11: return spell_RF5_BRAIN_SMASH(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_MIND_BLAST */
+    case RF5_SPELL_START + 12: return spell_RF5_CAUSE_1(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_CAUSE_1 */
+    case RF5_SPELL_START + 13: return spell_RF5_CAUSE_2(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_CAUSE_2 */
+    case RF5_SPELL_START + 14: return spell_RF5_CAUSE_3(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_CAUSE_3 */
+    case RF5_SPELL_START + 15: return spell_RF5_CAUSE_4(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_CAUSE_4 */
+    case RF5_SPELL_START + 16: return spell_RF5_BO_ACID(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_ACID */
+    case RF5_SPELL_START + 17: return spell_RF5_BO_ELEC(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_ELEC */
+    case RF5_SPELL_START + 18: return spell_RF5_BO_FIRE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_FIRE */
+    case RF5_SPELL_START + 19: return spell_RF5_BO_COLD(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_COLD */
+    case RF5_SPELL_START + 20: return spell_RF5_BA_LITE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_LITE */
+    case RF5_SPELL_START + 21: return spell_RF5_BO_NETH(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_NETH */
+    case RF5_SPELL_START + 22: return spell_RF5_BO_WATE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_WATE */
+    case RF5_SPELL_START + 23: return spell_RF5_BO_MANA(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_MANA */
+    case RF5_SPELL_START + 24: return spell_RF5_BO_PLAS(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_PLAS */
+    case RF5_SPELL_START + 25: return spell_RF5_BO_ICEE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_ICEE */
+    case RF5_SPELL_START + 26: return spell_RF5_MISSILE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_MISSILE */
+    case RF5_SPELL_START + 27: return spell_RF5_SCARE(m_idx, target_ptr, 0, MONSTER_TO_PLAYER); /* RF5_SCARE */
+    case RF5_SPELL_START + 28: return spell_RF5_BLIND(m_idx, target_ptr, 0, MONSTER_TO_PLAYER); /* RF5_BLIND */
+    case RF5_SPELL_START + 29: return spell_RF5_CONF(m_idx, target_ptr, 0, MONSTER_TO_PLAYER); /* RF5_CONF */
+    case RF5_SPELL_START + 30: return spell_RF5_SLOW(m_idx, target_ptr, 0, MONSTER_TO_PLAYER); /* RF5_SLOW */
+    case RF5_SPELL_START + 31: return spell_RF5_HOLD(m_idx, target_ptr, 0, MONSTER_TO_PLAYER); /* RF5_HOLD */
+    case RF6_SPELL_START + 0:  return spell_RF6_HASTE(target_ptr, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_HASTE */
+    case RF6_SPELL_START + 1:  return spell_RF6_HAND_DOOM(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_HAND_DOOM */
+    case RF6_SPELL_START + 2:  return spell_RF6_HEAL(target_ptr, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_HEAL */
+    case RF6_SPELL_START + 3:  return spell_RF6_INVULNER(target_ptr, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_INVULNER */
+    case RF6_SPELL_START + 4:  return spell_RF6_BLINK(target_ptr, m_idx, MONSTER_TO_PLAYER, FALSE); /* RF6_BLINK */
+    case RF6_SPELL_START + 5:  return spell_RF6_TPORT(target_ptr, m_idx, MONSTER_TO_PLAYER); /* RF6_TPORT */
+    case RF6_SPELL_START + 6:  return spell_RF6_WORLD(target_ptr, m_idx); /* RF6_WORLD */
+    case RF6_SPELL_START + 7:  return spell_RF6_SPECIAL(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER);   /* RF6_SPECIAL */
+    case RF6_SPELL_START + 8:  return spell_RF6_TELE_TO(target_ptr, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_TELE_TO */
+    case RF6_SPELL_START + 9:  return spell_RF6_TELE_AWAY(target_ptr, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_TELE_AWAY */
+    case RF6_SPELL_START + 10: return spell_RF6_TELE_LEVEL(target_ptr, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_TELE_LEVEL */
+    case RF6_SPELL_START + 11: return spell_RF6_PSY_SPEAR(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_PSY_SPEAR */
+    case RF6_SPELL_START + 12: return spell_RF6_DARKNESS(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_DARKNESS */
+    case RF6_SPELL_START + 13: return spell_RF6_TRAPS(target_ptr, y, x, m_idx); /* RF6_TRAPS */
+    case RF6_SPELL_START + 14: return spell_RF6_FORGET(target_ptr, m_idx); /* RF6_FORGET */
+    case RF6_SPELL_START + 15: return spell_RF6_RAISE_DEAD(target_ptr, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_RAISE_DEAD */
+    case RF6_SPELL_START + 16: return spell_RF6_S_KIN(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_S_KIN */
+    case RF6_SPELL_START + 17: return spell_RF6_S_CYBER(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_S_CYBER */
+    case RF6_SPELL_START + 18: return spell_RF6_S_MONSTER(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_S_MONSTER */
+    case RF6_SPELL_START + 19: return spell_RF6_S_MONSTERS(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_S_MONSTER */
+    case RF6_SPELL_START + 20: return spell_RF6_S_ANT(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_S_ANT */
+    case RF6_SPELL_START + 21: return spell_RF6_S_SPIDER(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_S_SPIDER */
+    case RF6_SPELL_START + 22: return spell_RF6_S_HOUND(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_S_HOUND */
+    case RF6_SPELL_START + 23: return spell_RF6_S_HYDRA(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_S_HYDRA */
+    case RF6_SPELL_START + 24: return spell_RF6_S_ANGEL(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_S_ANGEL */
+    case RF6_SPELL_START + 25: return spell_RF6_S_DEMON(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_S_DEMON */
+    case RF6_SPELL_START + 26: return spell_RF6_S_UNDEAD(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_S_UNDEAD */
+    case RF6_SPELL_START + 27: return spell_RF6_S_DRAGON(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_S_DRAGON */
+    case RF6_SPELL_START + 28: return spell_RF6_S_HI_UNDEAD(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_S_HI_UNDEAD */
+    case RF6_SPELL_START + 29: return spell_RF6_S_HI_DRAGON(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_S_HI_DRAGON */
+    case RF6_SPELL_START + 30: return spell_RF6_S_AMBERITES(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_S_AMBERITES */
+    case RF6_SPELL_START + 31: return spell_RF6_S_UNIQUE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_S_UNIQUE */
+    default: break;
+    }
+    // clang-format on
+
+    return MonsterSpellResult::make_invalid();
+}
+
+static MonsterSpellResult monspell_to_monster_impl(
+    player_type *target_ptr, SPELL_IDX ms_type, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, bool is_special_spell)
+{
+    // clang-format off
+    switch (ms_type) {
+    case RF4_SPELL_START + 0:  return spell_RF4_SHRIEK(m_idx, target_ptr, t_idx, MONSTER_TO_MONSTER); /* RF4_SHRIEK */
+    case RF4_SPELL_START + 1:  break;   /* RF4_XXX1 */
+    case RF4_SPELL_START + 2:  return spell_RF4_DISPEL(m_idx, target_ptr, t_idx, MONSTER_TO_MONSTER); /* RF4_DISPEL */
+    case RF4_SPELL_START + 3:  return spell_RF4_ROCKET(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_ROCKET */
+    case RF4_SPELL_START + 4:  return spell_RF4_SHOOT(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);  /* RF4_SHOOT */
+    case RF4_SPELL_START + 5:  break;   /* RF4_XXX2 */
+    case RF4_SPELL_START + 6:  break;   /* RF4_XXX3 */
+    case RF4_SPELL_START + 7:  break;   /* RF4_XXX4 */
+    case RF4_SPELL_START + 8:  return spell_RF4_BREATH(target_ptr, GF_ACID, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BR_ACID */
+    case RF4_SPELL_START + 9:  return spell_RF4_BREATH(target_ptr, GF_ELEC, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BR_ELEC */
+    case RF4_SPELL_START + 10: return spell_RF4_BREATH(target_ptr, GF_FIRE, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BR_FIRE */
+    case RF4_SPELL_START + 11: return spell_RF4_BREATH(target_ptr, GF_COLD, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BR_COLD */
+    case RF4_SPELL_START + 12: return spell_RF4_BREATH(target_ptr, GF_POIS, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BR_POIS */
+    case RF4_SPELL_START + 13: return spell_RF4_BREATH(target_ptr, GF_NETHER, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);  /* RF4_BR_NETH */
+    case RF4_SPELL_START + 14: return spell_RF4_BREATH(target_ptr, GF_LITE, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BR_LITE */
+    case RF4_SPELL_START + 15: return spell_RF4_BREATH(target_ptr, GF_DARK, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BR_DARK */
+    case RF4_SPELL_START + 16: return spell_RF4_BREATH(target_ptr, GF_CONFUSION, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);   /* RF4_BR_CONF */
+    case RF4_SPELL_START + 17: return spell_RF4_BREATH(target_ptr, GF_SOUND, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);   /* RF4_BR_SOUN */
+    case RF4_SPELL_START + 18: return spell_RF4_BREATH(target_ptr, GF_CHAOS, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);   /* RF4_BR_CHAO */
+    case RF4_SPELL_START + 19: return spell_RF4_BREATH(target_ptr, GF_DISENCHANT, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);  /* RF4_BR_DISE */
+    case RF4_SPELL_START + 20: return spell_RF4_BREATH(target_ptr, GF_NEXUS, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);   /* RF4_BR_NEXU */
+    case RF4_SPELL_START + 21: return spell_RF4_BREATH(target_ptr, GF_TIME, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BR_TIME */
+    case RF4_SPELL_START + 22: return spell_RF4_BREATH(target_ptr, GF_INERTIAL, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BR_INER */
+    case RF4_SPELL_START + 23: return spell_RF4_BREATH(target_ptr, GF_GRAVITY, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BR_GRAV */
+    case RF4_SPELL_START + 24: return spell_RF4_BREATH(target_ptr, GF_SHARDS, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);  /* RF4_BR_SHAR */
+    case RF4_SPELL_START + 25: return spell_RF4_BREATH(target_ptr, GF_PLASMA, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);  /* RF4_BR_PLAS */
+    case RF4_SPELL_START + 26: return spell_RF4_BREATH(target_ptr, GF_FORCE, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);   /* RF4_BR_WALL */
+    case RF4_SPELL_START + 27: return spell_RF4_BREATH(target_ptr, GF_MANA, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BR_MANA */
+    case RF4_SPELL_START + 28: return spell_RF4_BA_NUKE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BA_NUKE */
+    case RF4_SPELL_START + 29: return spell_RF4_BREATH(target_ptr, GF_NUKE, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BR_NUKE */
+    case RF4_SPELL_START + 30: return spell_RF4_BA_CHAO(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BA_CHAO */
+    case RF4_SPELL_START + 31: return spell_RF4_BREATH(target_ptr, GF_DISINTEGRATE, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BR_DISI */
+    case RF5_SPELL_START + 0:  return spell_RF5_BA_ACID(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BA_ACID */
+    case RF5_SPELL_START + 1:  return spell_RF5_BA_ELEC(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BA_ELEC */
+    case RF5_SPELL_START + 2:  return spell_RF5_BA_FIRE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BA_FIRE */
+    case RF5_SPELL_START + 3:  return spell_RF5_BA_COLD(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BA_COLD */
+    case RF5_SPELL_START + 4:  return spell_RF5_BA_POIS(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BA_POIS */
+    case RF5_SPELL_START + 5:  return spell_RF5_BA_NETH(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BA_NETH */
+    case RF5_SPELL_START + 6:  return spell_RF5_BA_WATE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BA_WATE */
+    case RF5_SPELL_START + 7:  return spell_RF5_BA_MANA(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BA_MANA */
+    case RF5_SPELL_START + 8:  return spell_RF5_BA_DARK(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BA_DARK */
+    case RF5_SPELL_START + 9:  return spell_RF5_DRAIN_MANA(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_DRAIN_MANA */
+    case RF5_SPELL_START + 10: return spell_RF5_MIND_BLAST(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_MIND_BLAST */
+    case RF5_SPELL_START + 11: return spell_RF5_BRAIN_SMASH(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BRAIN_SMASH */
+    case RF5_SPELL_START + 12: return spell_RF5_CAUSE_1(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_CAUSE_1 */
+    case RF5_SPELL_START + 13: return spell_RF5_CAUSE_2(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_CAUSE_2 */
+    case RF5_SPELL_START + 14: return spell_RF5_CAUSE_3(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_CAUSE_3 */
+    case RF5_SPELL_START + 15: return spell_RF5_CAUSE_4(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_CAUSE_4 */
+    case RF5_SPELL_START + 16: return spell_RF5_BO_ACID(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BO_ACID */
+    case RF5_SPELL_START + 17: return spell_RF5_BO_ELEC(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BO_ELEC */
+    case RF5_SPELL_START + 18: return spell_RF5_BO_FIRE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BO_FIRE */
+    case RF5_SPELL_START + 19: return spell_RF5_BO_COLD(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BO_COLD */
+    case RF5_SPELL_START + 20: return spell_RF5_BA_LITE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BA_LITE */
+    case RF5_SPELL_START + 21: return spell_RF5_BO_NETH(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BO_NETH */
+    case RF5_SPELL_START + 22: return spell_RF5_BO_WATE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BO_WATE */
+    case RF5_SPELL_START + 23: return spell_RF5_BO_MANA(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BO_MANA */
+    case RF5_SPELL_START + 24: return spell_RF5_BO_PLAS(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BO_PLAS */
+    case RF5_SPELL_START + 25: return spell_RF5_BO_ICEE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_BO_ICEE */
+    case RF5_SPELL_START + 26: return spell_RF5_MISSILE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_MISSILE */
+    case RF5_SPELL_START + 27: return spell_RF5_SCARE(m_idx, target_ptr, t_idx, MONSTER_TO_MONSTER); /* RF5_SCARE */
+    case RF5_SPELL_START + 28: return spell_RF5_BLIND(m_idx, target_ptr, t_idx, MONSTER_TO_MONSTER); /* RF5_BLIND */
+    case RF5_SPELL_START + 29: return spell_RF5_CONF(m_idx, target_ptr, t_idx, MONSTER_TO_MONSTER); /* RF5_CONF */
+    case RF5_SPELL_START + 30: return spell_RF5_SLOW(m_idx, target_ptr, t_idx, MONSTER_TO_MONSTER); /* RF5_SLOW */
+    case RF5_SPELL_START + 31: return spell_RF5_HOLD(m_idx, target_ptr, t_idx, MONSTER_TO_MONSTER); /* RF5_HOLD */
+    case RF6_SPELL_START + 0:  return spell_RF6_HASTE(target_ptr, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_HASTE */
+    case RF6_SPELL_START + 1:  return spell_RF6_HAND_DOOM(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_HAND_DOOM */
+    case RF6_SPELL_START + 2:  return spell_RF6_HEAL(target_ptr, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_HEAL */
+    case RF6_SPELL_START + 3:  return spell_RF6_INVULNER(target_ptr, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_INVULNER */
+    case RF6_SPELL_START + 4:  return spell_RF6_BLINK(target_ptr, m_idx, MONSTER_TO_MONSTER, is_special_spell); /* RF6_BLINK */
+    case RF6_SPELL_START + 5:  return spell_RF6_TPORT(target_ptr, m_idx, MONSTER_TO_MONSTER); /* RF6_TPORT */
+    case RF6_SPELL_START + 6:  break; /* RF6_WORLD */
+    case RF6_SPELL_START + 7:  return spell_RF6_SPECIAL(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);   /* RF6_SPECIAL */
+    case RF6_SPELL_START + 8:  return spell_RF6_TELE_TO(target_ptr, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_TELE_TO */
+    case RF6_SPELL_START + 9:  return spell_RF6_TELE_AWAY(target_ptr, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_TELE_AWAY */
+    case RF6_SPELL_START + 10: return spell_RF6_TELE_LEVEL(target_ptr, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_TELE_LEVEL */
+    case RF6_SPELL_START + 11: return spell_RF6_PSY_SPEAR(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_PSY_SPEAR */
+    case RF6_SPELL_START + 12: return spell_RF6_DARKNESS(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_DARKNESS */
+    case RF6_SPELL_START + 13: break; /* RF6_TRAPS */
+    case RF6_SPELL_START + 14: break; /* RF6_FORGET */
+    case RF6_SPELL_START + 15: return spell_RF6_RAISE_DEAD(target_ptr, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_RAISE_DEAD */
+    case RF6_SPELL_START + 16: return spell_RF6_S_KIN(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_S_KIN */
+    case RF6_SPELL_START + 17: return spell_RF6_S_CYBER(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_S_CYBER */
+    case RF6_SPELL_START + 18: return spell_RF6_S_MONSTER(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_S_MONSTER */
+    case RF6_SPELL_START + 19: return spell_RF6_S_MONSTERS(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_S_MONSTER */
+    case RF6_SPELL_START + 20: return spell_RF6_S_ANT(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_S_ANT */
+    case RF6_SPELL_START + 21: return spell_RF6_S_SPIDER(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_S_SPIDER */
+    case RF6_SPELL_START + 22: return spell_RF6_S_HOUND(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_S_HOUND */
+    case RF6_SPELL_START + 23: return spell_RF6_S_HYDRA(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_S_HYDRA */
+    case RF6_SPELL_START + 24: return spell_RF6_S_ANGEL(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_S_ANGEL */
+    case RF6_SPELL_START + 25: return spell_RF6_S_DEMON(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_S_DEMON */
+    case RF6_SPELL_START + 26: return spell_RF6_S_UNDEAD(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_S_UNDEAD */
+    case RF6_SPELL_START + 27: return spell_RF6_S_DRAGON(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_S_DRAGON */
+    case RF6_SPELL_START + 28: return spell_RF6_S_HI_UNDEAD(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_S_HI_UNDEAD */
+    case RF6_SPELL_START + 29: return spell_RF6_S_HI_DRAGON(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_S_HI_DRAGON */
+    case RF6_SPELL_START + 30: return spell_RF6_S_AMBERITES(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_S_AMBERITES */
+    case RF6_SPELL_START + 31: return spell_RF6_S_UNIQUE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_S_UNIQUE */
+    default: break;
+    }
+    // clang-format on
+
+    return MonsterSpellResult::make_invalid();
+}
+
+/*!
+ * @brief モンスターからプレイヤーへの魔法使用。ラーニング処理も行う。
+ * @param ms_type モンスター魔法ID (monster_spell_typeのenum値とは異なる)
  * @param y 対象の地点のy座標
  * @param x 対象の地点のx座標
  * @param m_idx 呪文を唱えるモンスターID
- * @return 攻撃呪文のダメージ、または召喚したモンスターの数を返す。その他の場合0。以降の処理を中断するなら-1を返す。
  */
-HIT_POINT monspell_to_player(player_type *target_ptr, SPELL_IDX ms_type, POSITION y, POSITION x, MONSTER_IDX m_idx)
+MonsterSpellResult monspell_to_player(player_type *target_ptr, SPELL_IDX ms_type, POSITION y, POSITION x, MONSTER_IDX m_idx)
 {
-    switch (ms_type)
-       {
-       case RF4_SPELL_START + 0:   spell_RF4_SHRIEK(m_idx, target_ptr, 0, MONSTER_TO_PLAYER); break;   /* RF4_SHRIEK */
-       case RF4_SPELL_START + 1:   break;   /* RF4_XXX1 */
-       case RF4_SPELL_START + 2:   spell_RF4_DISPEL(m_idx, target_ptr, 0, MONSTER_TO_PLAYER); break;   /* RF4_DISPEL */
-       case RF4_SPELL_START + 3:   return spell_RF4_ROCKET(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER);  /* RF4_ROCKET */
-       case RF4_SPELL_START + 4:   return spell_RF4_SHOOT(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER);   /* RF4_SHOOT */
-       case RF4_SPELL_START + 5:   break;   /* RF4_XXX2 */
-       case RF4_SPELL_START + 6:   break;   /* RF4_XXX3 */
-       case RF4_SPELL_START + 7:   break;   /* RF4_XXX4 */
-       case RF4_SPELL_START + 8:   return spell_RF4_BREATH(target_ptr, GF_ACID, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_ACID */
-       case RF4_SPELL_START + 9:   return spell_RF4_BREATH(target_ptr, GF_ELEC, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_ELEC */
-       case RF4_SPELL_START + 10:  return spell_RF4_BREATH(target_ptr, GF_FIRE, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_FIRE */
-       case RF4_SPELL_START + 11:  return spell_RF4_BREATH(target_ptr, GF_COLD, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_COLD */
-       case RF4_SPELL_START + 12:  return spell_RF4_BREATH(target_ptr, GF_POIS, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_POIS */
-       case RF4_SPELL_START + 13:  return spell_RF4_BREATH(target_ptr, GF_NETHER, y, x, m_idx, 0, MONSTER_TO_PLAYER);   /* RF4_BR_NETH */
-       case RF4_SPELL_START + 14:  return spell_RF4_BREATH(target_ptr, GF_LITE, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_LITE */
-       case RF4_SPELL_START + 15:  return spell_RF4_BREATH(target_ptr, GF_DARK, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_DARK */
-       case RF4_SPELL_START + 16:  return spell_RF4_BREATH(target_ptr, GF_CONFUSION, y, x, m_idx, 0, MONSTER_TO_PLAYER);       /* RF4_BR_CONF */
-       case RF4_SPELL_START + 17:  return spell_RF4_BREATH(target_ptr, GF_SOUND, y, x, m_idx, 0, MONSTER_TO_PLAYER);   /* RF4_BR_SOUN */
-       case RF4_SPELL_START + 18:  return spell_RF4_BREATH(target_ptr, GF_CHAOS, y, x, m_idx, 0, MONSTER_TO_PLAYER);   /* RF4_BR_CHAO */
-       case RF4_SPELL_START + 19:  return spell_RF4_BREATH(target_ptr, GF_DISENCHANT, y, x, m_idx, 0, MONSTER_TO_PLAYER);   /* RF4_BR_DISE */
-       case RF4_SPELL_START + 20:  return spell_RF4_BREATH(target_ptr, GF_NEXUS, y, x, m_idx, 0, MONSTER_TO_PLAYER);   /* RF4_BR_NEXU */
-       case RF4_SPELL_START + 21:  return spell_RF4_BREATH(target_ptr, GF_TIME, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_TIME */
-       case RF4_SPELL_START + 22:  return spell_RF4_BREATH(target_ptr, GF_INERTIAL, y, x, m_idx, 0, MONSTER_TO_PLAYER);  /* RF4_BR_INER */
-       case RF4_SPELL_START + 23:  return spell_RF4_BREATH(target_ptr, GF_GRAVITY, y, x, m_idx, 0, MONSTER_TO_PLAYER);  /* RF4_BR_GRAV */
-       case RF4_SPELL_START + 24:  return spell_RF4_BREATH(target_ptr, GF_SHARDS, y, x, m_idx, 0, MONSTER_TO_PLAYER);   /* RF4_BR_SHAR */
-       case RF4_SPELL_START + 25:  return spell_RF4_BREATH(target_ptr, GF_PLASMA, y, x, m_idx, 0, MONSTER_TO_PLAYER);   /* RF4_BR_PLAS */
-       case RF4_SPELL_START + 26:  return spell_RF4_BREATH(target_ptr, GF_FORCE, y, x, m_idx, 0, MONSTER_TO_PLAYER);   /* RF4_BR_WALL */
-       case RF4_SPELL_START + 27:  return spell_RF4_BREATH(target_ptr, GF_MANA, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_MANA */
-       case RF4_SPELL_START + 28:  return spell_RF4_BA_NUKE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BA_NUKE */
-       case RF4_SPELL_START + 29:  return spell_RF4_BREATH(target_ptr, GF_NUKE, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_NUKE */
-       case RF4_SPELL_START + 30:  return spell_RF4_BA_CHAO(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BA_CHAO */
-       case RF4_SPELL_START + 31:  return spell_RF4_BREATH(target_ptr, GF_DISINTEGRATE, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF4_BR_DISI */
-       case RF5_SPELL_START + 0:  return spell_RF5_BA_ACID(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_ACID */
-       case RF5_SPELL_START + 1:  return spell_RF5_BA_ELEC(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_ELEC */
-       case RF5_SPELL_START + 2:  return spell_RF5_BA_FIRE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_FIRE */
-       case RF5_SPELL_START + 3:  return spell_RF5_BA_COLD(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_COLD */
-       case RF5_SPELL_START + 4:  return spell_RF5_BA_POIS(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_POIS */
-       case RF5_SPELL_START + 5:  return spell_RF5_BA_NETH(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_NETH */
-       case RF5_SPELL_START + 6:  return spell_RF5_BA_WATE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_WATE */
-       case RF5_SPELL_START + 7:  return spell_RF5_BA_MANA(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_MANA */
-       case RF5_SPELL_START + 8:  return spell_RF5_BA_DARK(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_DARK */
-       case RF5_SPELL_START + 9:  return spell_RF5_DRAIN_MANA(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER);  /* RF5_DRAIN_MANA */
-       case RF5_SPELL_START + 10: return spell_RF5_MIND_BLAST(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER);  /* RF5_MIND_BLAST */
-       case RF5_SPELL_START + 11: return spell_RF5_BRAIN_SMASH(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_MIND_BLAST */
-       case RF5_SPELL_START + 12: return spell_RF5_CAUSE_1(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_CAUSE_1 */
-       case RF5_SPELL_START + 13: return spell_RF5_CAUSE_2(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_CAUSE_2 */
-       case RF5_SPELL_START + 14: return spell_RF5_CAUSE_3(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_CAUSE_3 */
-       case RF5_SPELL_START + 15: return spell_RF5_CAUSE_4(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_CAUSE_4 */
-       case RF5_SPELL_START + 16: return spell_RF5_BO_ACID(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_ACID */
-       case RF5_SPELL_START + 17: return spell_RF5_BO_ELEC(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_ELEC */
-       case RF5_SPELL_START + 18: return spell_RF5_BO_FIRE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_FIRE */
-       case RF5_SPELL_START + 19: return spell_RF5_BO_COLD(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_COLD */
-       case RF5_SPELL_START + 20: return spell_RF5_BA_LITE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BA_LITE */
-       case RF5_SPELL_START + 21: return spell_RF5_BO_NETH(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_NETH */
-       case RF5_SPELL_START + 22: return spell_RF5_BO_WATE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_WATE */
-       case RF5_SPELL_START + 23: return spell_RF5_BO_MANA(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_MANA */
-       case RF5_SPELL_START + 24: return spell_RF5_BO_PLAS(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_PLAS */
-       case RF5_SPELL_START + 25: return spell_RF5_BO_ICEE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_BO_ICEE */
-       case RF5_SPELL_START + 26: return spell_RF5_MISSILE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF5_MISSILE */
-       case RF5_SPELL_START + 27: spell_RF5_SCARE(m_idx, target_ptr, 0, MONSTER_TO_PLAYER); break;   /* RF5_SCARE */
-       case RF5_SPELL_START + 28: spell_RF5_BLIND(m_idx, target_ptr, 0, MONSTER_TO_PLAYER); break;   /* RF5_BLIND */
-       case RF5_SPELL_START + 29: spell_RF5_CONF(m_idx, target_ptr, 0, MONSTER_TO_PLAYER); break;  /* RF5_CONF */
-       case RF5_SPELL_START + 30: spell_RF5_SLOW(m_idx, target_ptr, 0, MONSTER_TO_PLAYER); break;  /* RF5_SLOW */
-       case RF5_SPELL_START + 31: spell_RF5_HOLD(m_idx, target_ptr, 0, MONSTER_TO_PLAYER); break;  /* RF5_HOLD */
-       case RF6_SPELL_START + 0:  spell_RF6_HASTE(target_ptr, m_idx, 0, MONSTER_TO_PLAYER); break;   /* RF6_HASTE */
-       case RF6_SPELL_START + 1:  return spell_RF6_HAND_DOOM(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); /* RF6_HAND_DOOM */
-       case RF6_SPELL_START + 2:  spell_RF6_HEAL(target_ptr, m_idx, 0, MONSTER_TO_PLAYER); break;      /* RF6_HEAL */
-       case RF6_SPELL_START + 3:  spell_RF6_INVULNER(target_ptr, m_idx, 0, MONSTER_TO_PLAYER); break;  /* RF6_INVULNER */
-       case RF6_SPELL_START + 4:  spell_RF6_BLINK(target_ptr, m_idx, MONSTER_TO_PLAYER, FALSE); break;   /* RF6_BLINK */
-       case RF6_SPELL_START + 5:  spell_RF6_TPORT(target_ptr, m_idx, MONSTER_TO_PLAYER); break;   /* RF6_TPORT */
-       case RF6_SPELL_START + 6:  return spell_RF6_WORLD(target_ptr, m_idx); break;    /* RF6_WORLD */
-       case RF6_SPELL_START + 7:  return spell_RF6_SPECIAL(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER);   /* RF6_SPECIAL */
-       case RF6_SPELL_START + 8:  spell_RF6_TELE_TO(target_ptr, m_idx, 0, MONSTER_TO_PLAYER); break; /* RF6_TELE_TO */
-       case RF6_SPELL_START + 9:  spell_RF6_TELE_AWAY(target_ptr, m_idx, 0, MONSTER_TO_PLAYER); break;   /* RF6_TELE_AWAY */
-       case RF6_SPELL_START + 10: spell_RF6_TELE_LEVEL(target_ptr, m_idx, 0, MONSTER_TO_PLAYER); break;  /* RF6_TELE_LEVEL */
-       case RF6_SPELL_START + 11: return spell_RF6_PSY_SPEAR(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break; /* RF6_PSY_SPEAR */
-       case RF6_SPELL_START + 12: spell_RF6_DARKNESS(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break;    /* RF6_DARKNESS */
-       case RF6_SPELL_START + 13: spell_RF6_TRAPS(target_ptr, y, x, m_idx); break; /* RF6_TRAPS */
-       case RF6_SPELL_START + 14: spell_RF6_FORGET(target_ptr, m_idx); break;  /* RF6_FORGET */
-       case RF6_SPELL_START + 15: spell_RF6_RAISE_DEAD(target_ptr, m_idx, 0, MONSTER_TO_PLAYER); break;  /* RF6_RAISE_DEAD */
-       case RF6_SPELL_START + 16: spell_RF6_S_KIN(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break; /* RF6_S_KIN */
-       case RF6_SPELL_START + 17: spell_RF6_S_CYBER(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break;   /* RF6_S_CYBER */
-       case RF6_SPELL_START + 18: spell_RF6_S_MONSTER(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break; /* RF6_S_MONSTER */
-       case RF6_SPELL_START + 19: spell_RF6_S_MONSTERS(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break;  /* RF6_S_MONSTER */
-       case RF6_SPELL_START + 20: spell_RF6_S_ANT(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break; /* RF6_S_ANT */
-       case RF6_SPELL_START + 21: spell_RF6_S_SPIDER(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break;  /* RF6_S_SPIDER */
-       case RF6_SPELL_START + 22: spell_RF6_S_HOUND(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break;   /* RF6_S_HOUND */
-       case RF6_SPELL_START + 23: spell_RF6_S_HYDRA(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break;   /* RF6_S_HYDRA */
-       case RF6_SPELL_START + 24: spell_RF6_S_ANGEL(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break;   /* RF6_S_ANGEL */
-       case RF6_SPELL_START + 25: spell_RF6_S_DEMON(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break;   /* RF6_S_DEMON */
-       case RF6_SPELL_START + 26: spell_RF6_S_UNDEAD(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break;  /* RF6_S_UNDEAD */
-       case RF6_SPELL_START + 27: spell_RF6_S_DRAGON(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break;  /* RF6_S_DRAGON */
-       case RF6_SPELL_START + 28: spell_RF6_S_HI_UNDEAD(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break;   /* RF6_S_HI_UNDEAD */
-       case RF6_SPELL_START + 29: spell_RF6_S_HI_DRAGON(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break;   /* RF6_S_HI_DRAGON */
-       case RF6_SPELL_START + 30: spell_RF6_S_AMBERITES(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break;   /* RF6_S_AMBERITES */
-       case RF6_SPELL_START + 31: spell_RF6_S_UNIQUE(target_ptr, y, x, m_idx, 0, MONSTER_TO_PLAYER); break;  /* RF6_S_UNIQUE */
-       }
+    // 特技使用前の時点でプレイヤーがモンスターを視認できているかチェック(ラーニングの必要条件)。
+    const bool player_could_see_monster = spell_learnable(target_ptr, m_idx);
 
-       return 0;
-}
+    auto res = monspell_to_player_impl(target_ptr, ms_type, y, x, m_idx);
+    if (!player_could_see_monster)
+        res.learnable = false;
 
+    // 条件を満たしていればラーニングを試みる。
+    if (res.valid && res.learnable) {
+        const auto monspell = spell_idx_to_monster_spell_type(ms_type);
+        learn_spell(target_ptr, monspell);
+    }
+
+    return res;
+}
 
 /*!
  * todo モンスターからモンスターへの呪文なのにplayer_typeが引数になり得るのは間違っている……
- * @brief モンスターからモンスターへの呪文の振り分け関数。 /
+ * @brief モンスターからモンスターへの魔法使用。ラーニング処理も行う。
  * @param target_ptr プレーヤーへの参照ポインタ (monster_spell_typeのenum値とは異なる)
- * @param SPELL_NUM モンスター魔法ID
+ * @param ms_type モンスター魔法ID
  * @param y 対象の地点のy座標
  * @param x 対象の地点のx座標
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param is_special_spell 特殊な行動である時TRUE
- * @return 攻撃呪文のダメージ、または召喚したモンスターの数を返す。その他の場合0。以降の処理を中断するなら-1を返す。
  */
-HIT_POINT monspell_to_monster(player_type *target_ptr, SPELL_IDX ms_type, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, bool is_special_spell)
+MonsterSpellResult monspell_to_monster(
+    player_type *target_ptr, SPELL_IDX ms_type, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, bool is_special_spell)
 {
-    switch (ms_type)
-       {
-       case RF4_SPELL_START + 0:   spell_RF4_SHRIEK(m_idx, target_ptr, t_idx, MONSTER_TO_MONSTER); break;   /* RF4_SHRIEK */
-       case RF4_SPELL_START + 1:   return -1;   /* RF4_XXX1 */
-       case RF4_SPELL_START + 2:   spell_RF4_DISPEL(m_idx, target_ptr, t_idx, MONSTER_TO_MONSTER); break;   /* RF4_DISPEL */
-       case RF4_SPELL_START + 3:   return spell_RF4_ROCKET(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_ROCKET */
-       case RF4_SPELL_START + 4:   return spell_RF4_SHOOT(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);  /* RF4_SHOOT */
-       case RF4_SPELL_START + 5:   return -1;   /* RF4_XXX2 */
-       case RF4_SPELL_START + 6:   return -1;   /* RF4_XXX3 */
-       case RF4_SPELL_START + 7:   return -1;   /* RF4_XXX4 */
-       case RF4_SPELL_START + 8:   return spell_RF4_BREATH(target_ptr, GF_ACID, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);       /* RF4_BR_ACID */
-       case RF4_SPELL_START + 9:   return spell_RF4_BREATH(target_ptr, GF_ELEC, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);       /* RF4_BR_ELEC */
-       case RF4_SPELL_START + 10:  return spell_RF4_BREATH(target_ptr, GF_FIRE, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);       /* RF4_BR_FIRE */
-       case RF4_SPELL_START + 11:  return spell_RF4_BREATH(target_ptr, GF_COLD, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);       /* RF4_BR_COLD */
-       case RF4_SPELL_START + 12:  return spell_RF4_BREATH(target_ptr, GF_POIS, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);       /* RF4_BR_POIS */
-       case RF4_SPELL_START + 13:  return spell_RF4_BREATH(target_ptr, GF_NETHER, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);  /* RF4_BR_NETH */
-       case RF4_SPELL_START + 14:  return spell_RF4_BREATH(target_ptr, GF_LITE, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);       /* RF4_BR_LITE */
-       case RF4_SPELL_START + 15:  return spell_RF4_BREATH(target_ptr, GF_DARK, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);       /* RF4_BR_DARK */
-       case RF4_SPELL_START + 16:  return spell_RF4_BREATH(target_ptr, GF_CONFUSION, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);   /* RF4_BR_CONF */
-       case RF4_SPELL_START + 17:  return spell_RF4_BREATH(target_ptr, GF_SOUND, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);   /* RF4_BR_SOUN */
-       case RF4_SPELL_START + 18:  return spell_RF4_BREATH(target_ptr, GF_CHAOS, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);   /* RF4_BR_CHAO */
-       case RF4_SPELL_START + 19:  return spell_RF4_BREATH(target_ptr, GF_DISENCHANT, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);  /* RF4_BR_DISE */
-       case RF4_SPELL_START + 20:  return spell_RF4_BREATH(target_ptr, GF_NEXUS, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);   /* RF4_BR_NEXU */
-       case RF4_SPELL_START + 21:  return spell_RF4_BREATH(target_ptr, GF_TIME, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);       /* RF4_BR_TIME */
-       case RF4_SPELL_START + 22:  return spell_RF4_BREATH(target_ptr, GF_INERTIAL, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BR_INER */
-       case RF4_SPELL_START + 23:  return spell_RF4_BREATH(target_ptr, GF_GRAVITY, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF4_BR_GRAV */
-       case RF4_SPELL_START + 24:  return spell_RF4_BREATH(target_ptr, GF_SHARDS, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);  /* RF4_BR_SHAR */
-       case RF4_SPELL_START + 25:  return spell_RF4_BREATH(target_ptr, GF_PLASMA, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);  /* RF4_BR_PLAS */
-       case RF4_SPELL_START + 26:  return spell_RF4_BREATH(target_ptr, GF_FORCE, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);   /* RF4_BR_WALL */
-       case RF4_SPELL_START + 27:  return spell_RF4_BREATH(target_ptr, GF_MANA, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);       /* RF4_BR_MANA */
-       case RF4_SPELL_START + 28:  return spell_RF4_BA_NUKE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);       /* RF4_BA_NUKE */
-       case RF4_SPELL_START + 29:  return spell_RF4_BREATH(target_ptr, GF_NUKE, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);       /* RF4_BR_NUKE */
-       case RF4_SPELL_START + 30:  return spell_RF4_BA_CHAO(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);       /* RF4_BA_CHAO */
-       case RF4_SPELL_START + 31:  return spell_RF4_BREATH(target_ptr, GF_DISINTEGRATE, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);       /* RF4_BR_DISI */
-       case RF5_SPELL_START + 0:  return spell_RF5_BA_ACID(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BA_ACID */
-       case RF5_SPELL_START + 1:  return spell_RF5_BA_ELEC(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BA_ELEC */
-       case RF5_SPELL_START + 2:  return spell_RF5_BA_FIRE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BA_FIRE */
-       case RF5_SPELL_START + 3:  return spell_RF5_BA_COLD(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BA_COLD */
-       case RF5_SPELL_START + 4:  return spell_RF5_BA_POIS(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BA_POIS */
-       case RF5_SPELL_START + 5:  return spell_RF5_BA_NETH(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BA_NETH */
-       case RF5_SPELL_START + 6:  return spell_RF5_BA_WATE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BA_WATE */
-       case RF5_SPELL_START + 7:  return spell_RF5_BA_MANA(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BA_MANA */
-       case RF5_SPELL_START + 8:  return spell_RF5_BA_DARK(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BA_DARK */
-       case RF5_SPELL_START + 9:  return spell_RF5_DRAIN_MANA(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_DRAIN_MANA */
-       case RF5_SPELL_START + 10: return spell_RF5_MIND_BLAST(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF5_MIND_BLAST */
-       case RF5_SPELL_START + 11: return spell_RF5_BRAIN_SMASH(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);    /* RF5_BRAIN_SMASH */
-       case RF5_SPELL_START + 12: return spell_RF5_CAUSE_1(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_CAUSE_1 */
-       case RF5_SPELL_START + 13: return spell_RF5_CAUSE_2(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_CAUSE_2 */
-       case RF5_SPELL_START + 14: return spell_RF5_CAUSE_3(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_CAUSE_3 */
-       case RF5_SPELL_START + 15: return spell_RF5_CAUSE_4(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_CAUSE_4 */
-       case RF5_SPELL_START + 16: return spell_RF5_BO_ACID(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BO_ACID */
-       case RF5_SPELL_START + 17: return spell_RF5_BO_ELEC(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BO_ELEC */
-       case RF5_SPELL_START + 18: return spell_RF5_BO_FIRE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BO_FIRE */
-       case RF5_SPELL_START + 19: return spell_RF5_BO_COLD(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BO_COLD */
-       case RF5_SPELL_START + 20: return spell_RF5_BA_LITE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BA_LITE */
-       case RF5_SPELL_START + 21: return spell_RF5_BO_NETH(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BO_NETH */
-       case RF5_SPELL_START + 22: return spell_RF5_BO_WATE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BO_WATE */
-       case RF5_SPELL_START + 23: return spell_RF5_BO_MANA(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BO_MANA */
-       case RF5_SPELL_START + 24: return spell_RF5_BO_PLAS(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BO_PLAS */
-       case RF5_SPELL_START + 25: return spell_RF5_BO_ICEE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_BO_ICEE */
-       case RF5_SPELL_START + 26: return spell_RF5_MISSILE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);        /* RF5_MISSILE */
-       case RF5_SPELL_START + 27: spell_RF5_SCARE(m_idx, target_ptr, t_idx, MONSTER_TO_MONSTER); break;  /* RF5_SCARE */
-       case RF5_SPELL_START + 28: spell_RF5_BLIND(m_idx, target_ptr, t_idx, MONSTER_TO_MONSTER); break;  /* RF5_BLIND */
-       case RF5_SPELL_START + 29: spell_RF5_CONF(m_idx, target_ptr, t_idx, MONSTER_TO_MONSTER); break;   /* RF5_CONF */
-       case RF5_SPELL_START + 30: spell_RF5_SLOW(m_idx, target_ptr, t_idx, MONSTER_TO_MONSTER); break;   /* RF5_SLOW */
-       case RF5_SPELL_START + 31: spell_RF5_HOLD(m_idx, target_ptr, t_idx, MONSTER_TO_MONSTER); break;  /* RF5_HOLD */
-       case RF6_SPELL_START + 0:  spell_RF6_HASTE(target_ptr, m_idx, t_idx, MONSTER_TO_MONSTER); break;   /* RF6_HASTE */
-       case RF6_SPELL_START + 1:  return spell_RF6_HAND_DOOM(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); /* RF6_HAND_DOOM */
-       case RF6_SPELL_START + 2:  spell_RF6_HEAL(target_ptr, m_idx, t_idx, MONSTER_TO_MONSTER); break; /* RF6_HEAL */
-       case RF6_SPELL_START + 3:  spell_RF6_INVULNER(target_ptr, m_idx, t_idx, MONSTER_TO_MONSTER); break;     /* RF6_INVULNER */
-       case RF6_SPELL_START + 4:  spell_RF6_BLINK(target_ptr, m_idx, MONSTER_TO_MONSTER, is_special_spell); break;   /* RF6_BLINK */
-       case RF6_SPELL_START + 5:  spell_RF6_TPORT(target_ptr, m_idx, MONSTER_TO_MONSTER); break;   /* RF6_TPORT */
-       case RF6_SPELL_START + 6:  return -1; break;    /* RF6_WORLD */
-       case RF6_SPELL_START + 7:  return spell_RF6_SPECIAL(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER);   /* RF6_SPECIAL */
-       case RF6_SPELL_START + 8:  spell_RF6_TELE_TO(target_ptr, m_idx, t_idx, MONSTER_TO_MONSTER); break; /* RF6_TELE_TO */
-       case RF6_SPELL_START + 9:  spell_RF6_TELE_AWAY(target_ptr, m_idx, t_idx, MONSTER_TO_MONSTER); break;   /* RF6_TELE_AWAY */
-       case RF6_SPELL_START + 10: spell_RF6_TELE_LEVEL(target_ptr, m_idx, t_idx, MONSTER_TO_MONSTER); break;  /* RF6_TELE_LEVEL */
-       case RF6_SPELL_START + 11: return spell_RF6_PSY_SPEAR(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break; /* RF6_PSY_SPEAR */
-       case RF6_SPELL_START + 12: spell_RF6_DARKNESS(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break;       /* RF6_DARKNESS */
-       case RF6_SPELL_START + 13: return -1; /* RF6_TRAPS */
-       case RF6_SPELL_START + 14: return -1;  /* RF6_FORGET */
-       case RF6_SPELL_START + 15: spell_RF6_RAISE_DEAD(target_ptr, m_idx, t_idx, MONSTER_TO_MONSTER); break;  /* RF6_RAISE_DEAD */
-       case RF6_SPELL_START + 16: spell_RF6_S_KIN(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break; /* RF6_S_KIN */
-       case RF6_SPELL_START + 17: spell_RF6_S_CYBER(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break;   /* RF6_S_CYBER */
-       case RF6_SPELL_START + 18: spell_RF6_S_MONSTER(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break; /* RF6_S_MONSTER */
-       case RF6_SPELL_START + 19: spell_RF6_S_MONSTERS(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break;     /* RF6_S_MONSTER */
-       case RF6_SPELL_START + 20: spell_RF6_S_ANT(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break; /* RF6_S_ANT */
-       case RF6_SPELL_START + 21: spell_RF6_S_SPIDER(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break;  /* RF6_S_SPIDER */
-       case RF6_SPELL_START + 22: spell_RF6_S_HOUND(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break;   /* RF6_S_HOUND */
-       case RF6_SPELL_START + 23: spell_RF6_S_HYDRA(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break;   /* RF6_S_HYDRA */
-       case RF6_SPELL_START + 24: spell_RF6_S_ANGEL(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break;   /* RF6_S_ANGEL */
-       case RF6_SPELL_START + 25: spell_RF6_S_DEMON(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break;   /* RF6_S_DEMON */
-       case RF6_SPELL_START + 26: spell_RF6_S_UNDEAD(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break;  /* RF6_S_UNDEAD */
-       case RF6_SPELL_START + 27: spell_RF6_S_DRAGON(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break;  /* RF6_S_DRAGON */
-       case RF6_SPELL_START + 28: spell_RF6_S_HI_UNDEAD(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break;   /* RF6_S_HI_UNDEAD */
-       case RF6_SPELL_START + 29: spell_RF6_S_HI_DRAGON(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break;   /* RF6_S_HI_DRAGON */
-       case RF6_SPELL_START + 30: spell_RF6_S_AMBERITES(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break;   /* RF6_S_AMBERITES */
-       case RF6_SPELL_START + 31: spell_RF6_S_UNIQUE(target_ptr, y, x, m_idx, t_idx, MONSTER_TO_MONSTER); break;  /* RF6_S_UNIQUE */
-       }
+    // 特技使用前の時点でプレイヤーがモンスターを視認できているかチェック(ラーニングの必要条件)。
+    const bool player_could_see_monster = spell_learnable(target_ptr, m_idx);
+
+    auto res = monspell_to_monster_impl(target_ptr, ms_type, y, x, m_idx, t_idx, is_special_spell);
+    if (!player_could_see_monster)
+        res.learnable = false;
+
+    // 条件を満たしていればラーニングを試みる。
+    if (res.valid && res.learnable) {
+        const auto monspell = spell_idx_to_monster_spell_type(ms_type);
+        learn_spell(target_ptr, monspell);
+    }
 
-       return 0;
+    return res;
 }
index b13312a..b454f2e 100644 (file)
@@ -2,5 +2,8 @@
 
 #include "system/angband.h"
 
-HIT_POINT monspell_to_player(player_type *target_ptr, SPELL_IDX ms_type, POSITION y, POSITION x, MONSTER_IDX m_idx);
-HIT_POINT monspell_to_monster(player_type *target_ptr, SPELL_IDX ms_type, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, bool is_special_spell);
+struct MonsterSpellResult;
+
+MonsterSpellResult monspell_to_player(player_type *target_ptr, SPELL_IDX ms_type, POSITION y, POSITION x, MONSTER_IDX m_idx);
+MonsterSpellResult monspell_to_monster(
+    player_type *target_ptr, SPELL_IDX ms_type, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, bool is_special_spell);
index 590cb56..ba5175f 100644 (file)
@@ -27,6 +27,7 @@
 #include "mspell/mspell-selector.h"
 #include "mspell/mspell-type.h"
 #include "mspell/mspell-util.h"
+#include "mspell/mspell.h"
 #include "player/attack-defense-types.h"
 #include "spell-kind/spells-world.h"
 #include "spell-realm/spells-hex.h"
@@ -368,25 +369,11 @@ bool make_attack_spell(player_type *target_ptr, MONSTER_IDX m_idx)
     if (!check_thrown_mspell(target_ptr, msa_ptr))
         return FALSE;
 
-    // 特技使用前の時点でプレイヤーがモンスターを視認できているかチェック(ラーニングの必要条件)。
-    const bool player_could_see_monster = spell_learnable(target_ptr, m_idx);
-
     // 特技を使う。
-    msa_ptr->dam = monspell_to_player(target_ptr, msa_ptr->thrown_spell, msa_ptr->y, msa_ptr->x, m_idx);
-    if (msa_ptr->dam < 0)
+    const auto monspell_res = monspell_to_player(target_ptr, msa_ptr->thrown_spell, msa_ptr->y, msa_ptr->x, m_idx);
+    if (!monspell_res.valid)
         return FALSE;
 
-    // 条件を満たしていればラーニングを試みる。
-    if (player_could_see_monster) {
-        const int monspell = msa_ptr->thrown_spell - RF4_SPELL_START;
-        // XXX: 「暗闇」は特定条件下でライトエリアになる関係上、やむを得ずラー
-        // ニング処理を特技処理に含めた。よって二重ラーニングを行わないようここ
-        // では除外する。
-        const bool try_learn = monster_spell_is_learnable(monspell) && monspell != MS_DARKNESS;
-        if (try_learn)
-            learn_spell(target_ptr, monspell);
-    }
-
     check_mspell_imitation(target_ptr, msa_ptr);
     remember_mspell(msa_ptr);
     if (target_ptr->is_dead && (msa_ptr->r_ptr->r_deaths < MAX_SHORT) && !target_ptr->current_floor_ptr->inside_arena)
index 23a72cf..51acd5b 100644 (file)
@@ -1,4 +1,5 @@
 #include "mspell/mspell-ball.h"
+#include "effect/effect-processor.h"
 #include "main/sound-of-music.h"
 #include "mind/drs-types.h"
 #include "monster-race/race-indice-types.h"
@@ -9,6 +10,7 @@
 #include "mspell/mspell-damage-calculator.h"
 #include "mspell/mspell-type.h"
 #include "mspell/mspell-util.h"
+#include "mspell/mspell.h"
 #include "spell/spell-types.h"
 #include "system/floor-type-definition.h"
 #include "view/display-messages.h"
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF4_BA_NUKE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF4_BA_NUKE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."), _("%^sが放射能球を放った。", "%^s casts a ball of radiation."),
         _("%^sが%sに放射能球を放った。", "%^s casts a ball of radiation at %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_BALL_NUKE), m_idx, DAM_ROLL);
-    breath(target_ptr, y, x, m_idx, GF_NUKE, dam, 2, FALSE, MS_BALL_NUKE, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_BALL_NUKE), m_idx, DAM_ROLL);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_NUKE, dam, 2, FALSE, MS_BALL_NUKE, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER)
         update_smart_learn(target_ptr, m_idx, DRS_POIS);
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -46,21 +50,23 @@ HIT_POINT spell_RF4_BA_NUKE(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF4_BA_CHAO(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF4_BA_CHAO(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが恐ろしげにつぶやいた。", "%^s mumbles frighteningly."),
         _("%^sが純ログルスを放った。", "%^s invokes a raw Logrus."), _("%^sが%sに純ログルスを放った。", "%^s invokes raw Logrus upon %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_BALL_CHAOS), m_idx, DAM_ROLL);
-    breath(target_ptr, y, x, m_idx, GF_CHAOS, dam, 4, FALSE, MS_BALL_CHAOS, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_BALL_CHAOS), m_idx, DAM_ROLL);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_CHAOS, dam, 4, FALSE, MS_BALL_CHAOS, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER)
         update_smart_learn(target_ptr, m_idx, DRS_CHAOS);
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -71,23 +77,25 @@ HIT_POINT spell_RF4_BA_CHAO(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BA_ACID(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BA_ACID(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam, rad;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sがアシッド・ボールの呪文を唱えた。", "%^s casts an acid ball."),
         _("%^sが%sに向かってアシッド・ボールの呪文を唱えた。", "%^s casts an acid ball at %s."), TARGET_TYPE);
 
-    rad = monster_is_powerful(target_ptr->current_floor_ptr, m_idx) ? 4 : 2;
-    dam = monspell_damage(target_ptr, (MS_BALL_ACID), m_idx, DAM_ROLL);
-    breath(target_ptr, y, x, m_idx, GF_ACID, dam, rad, FALSE, MS_BALL_ACID, TARGET_TYPE);
+    const auto rad = monster_is_powerful(target_ptr->current_floor_ptr, m_idx) ? 4 : 2;
+    const auto dam = monspell_damage(target_ptr, (MS_BALL_ACID), m_idx, DAM_ROLL);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_ACID, dam, rad, FALSE, MS_BALL_ACID, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER)
         update_smart_learn(target_ptr, m_idx, DRS_ACID);
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -98,23 +106,25 @@ HIT_POINT spell_RF5_BA_ACID(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BA_ELEC(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BA_ELEC(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam, rad;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
-        _("%^sã\81\8cã\82µã\83³ã\83\80ã\83¼ã\83»ã\83»ã\83\9cã\83¼ã\83«ã\81®å\91ªæ\96\87ã\82\92å\94±ã\81\88ã\81\9fã\80\82", "%^s casts a lightning ball."),
+        _("%^sがサンダー・ボールの呪文を唱えた。", "%^s casts a lightning ball."),
         _("%^sが%sに向かってサンダー・ボールの呪文を唱えた。", "%^s casts a lightning ball at %s."), TARGET_TYPE);
 
-    rad = monster_is_powerful(target_ptr->current_floor_ptr, m_idx) ? 4 : 2;
-    dam = monspell_damage(target_ptr, (MS_BALL_ELEC), m_idx, DAM_ROLL);
-    breath(target_ptr, y, x, m_idx, GF_ELEC, dam, rad, FALSE, MS_BALL_ELEC, TARGET_TYPE);
+    const auto rad = monster_is_powerful(target_ptr->current_floor_ptr, m_idx) ? 4 : 2;
+    const auto dam = monspell_damage(target_ptr, (MS_BALL_ELEC), m_idx, DAM_ROLL);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_ELEC, dam, rad, FALSE, MS_BALL_ELEC, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER)
         update_smart_learn(target_ptr, m_idx, DRS_ELEC);
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -125,11 +135,11 @@ HIT_POINT spell_RF5_BA_ELEC(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BA_FIRE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BA_FIRE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam, rad;
     monster_type *m_ptr = &target_ptr->current_floor_ptr->m_list[m_idx];
 
     if (m_ptr->r_idx == MON_ROLENTO) {
@@ -140,13 +150,17 @@ HIT_POINT spell_RF5_BA_FIRE(player_type *target_ptr, POSITION y, POSITION x, MON
             _("%^sがファイア・ボールの呪文を唱えた。", "%^s casts a fire ball."),
             _("%^sが%sに向かってファイア・ボールの呪文を唱えた。", "%^s casts a fire ball at %s."), TARGET_TYPE);
     }
-    rad = monster_is_powerful(target_ptr->current_floor_ptr, m_idx) ? 4 : 2;
-    dam = monspell_damage(target_ptr, (MS_BALL_FIRE), m_idx, DAM_ROLL);
-    breath(target_ptr, y, x, m_idx, GF_FIRE, dam, rad, FALSE, MS_BALL_FIRE, TARGET_TYPE);
+
+    const auto rad = monster_is_powerful(target_ptr->current_floor_ptr, m_idx) ? 4 : 2;
+    const auto dam = monspell_damage(target_ptr, (MS_BALL_FIRE), m_idx, DAM_ROLL);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_FIRE, dam, rad, FALSE, MS_BALL_FIRE, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER)
         update_smart_learn(target_ptr, m_idx, DRS_FIRE);
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -157,23 +171,25 @@ HIT_POINT spell_RF5_BA_FIRE(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BA_COLD(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BA_COLD(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam, rad;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sがアイス・ボールの呪文を唱えた。", "%^s casts a frost ball."),
         _("%^sが%sに向かってアイス・ボールの呪文を唱えた。", "%^s casts a frost ball at %s."), TARGET_TYPE);
 
-    rad = monster_is_powerful(target_ptr->current_floor_ptr, m_idx) ? 4 : 2;
-    dam = monspell_damage(target_ptr, (MS_BALL_COLD), m_idx, DAM_ROLL);
-    breath(target_ptr, y, x, m_idx, GF_COLD, dam, rad, FALSE, MS_BALL_COLD, TARGET_TYPE);
+    const auto rad = monster_is_powerful(target_ptr->current_floor_ptr, m_idx) ? 4 : 2;
+    const auto dam = monspell_damage(target_ptr, (MS_BALL_COLD), m_idx, DAM_ROLL);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_COLD, dam, rad, FALSE, MS_BALL_COLD, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER)
         update_smart_learn(target_ptr, m_idx, DRS_COLD);
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -184,21 +200,23 @@ HIT_POINT spell_RF5_BA_COLD(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BA_POIS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BA_POIS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."), _("%^sが悪臭雲の呪文を唱えた。", "%^s casts a stinking cloud."),
         _("%^sが%sに向かって悪臭雲の呪文を唱えた。", "%^s casts a stinking cloud at %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_BALL_POIS), m_idx, DAM_ROLL);
-    breath(target_ptr, y, x, m_idx, GF_POIS, dam, 2, FALSE, MS_BALL_POIS, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_BALL_POIS), m_idx, DAM_ROLL);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_POIS, dam, 2, FALSE, MS_BALL_POIS, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER)
         update_smart_learn(target_ptr, m_idx, DRS_POIS);
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -209,21 +227,23 @@ HIT_POINT spell_RF5_BA_POIS(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BA_NETH(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BA_NETH(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."), _("%^sが地獄球の呪文を唱えた。", "%^s casts a nether ball."),
         _("%^sが%sに向かって地獄球の呪文を唱えた。", "%^s casts a nether ball at %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_BALL_NETHER), m_idx, DAM_ROLL);
-    breath(target_ptr, y, x, m_idx, GF_NETHER, dam, 2, FALSE, MS_BALL_NETHER, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_BALL_NETHER), m_idx, DAM_ROLL);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_NETHER, dam, 2, FALSE, MS_BALL_NETHER, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER)
         update_smart_learn(target_ptr, m_idx, DRS_NETH);
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -234,11 +254,11 @@ HIT_POINT spell_RF5_BA_NETH(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BA_WATE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BA_WATE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
     bool known = monster_near_player(target_ptr->current_floor_ptr, m_idx, t_idx);
     bool see_either = see_monster(target_ptr, m_idx) || see_monster(target_ptr, t_idx);
     bool mon_to_mon = (TARGET_TYPE == MONSTER_TO_MONSTER);
@@ -255,9 +275,13 @@ HIT_POINT spell_RF5_BA_WATE(player_type *target_ptr, POSITION y, POSITION x, MON
         msg_format(_("%^sは渦巻に飲み込まれた。", "%^s is engulfed in a whirlpool."), t_name);
     }
 
-    dam = monspell_damage(target_ptr, (MS_BALL_WATER), m_idx, DAM_ROLL);
-    breath(target_ptr, y, x, m_idx, GF_WATER, dam, 4, FALSE, MS_BALL_WATER, TARGET_TYPE);
-    return dam;
+    const auto dam = monspell_damage(target_ptr, (MS_BALL_WATER), m_idx, DAM_ROLL);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_WATER, dam, 4, FALSE, MS_BALL_WATER, TARGET_TYPE);
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -268,19 +292,22 @@ HIT_POINT spell_RF5_BA_WATE(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BA_MANA(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BA_MANA(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かを力強くつぶやいた。", "%^s mumbles powerfully."),
         _("%^sが魔力の嵐の呪文を念じた。", "%^s invokes a mana storm."), _("%^sが%sに対して魔力の嵐の呪文を念じた。", "%^s invokes a mana storm upon %s."),
         TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_BALL_MANA), m_idx, DAM_ROLL);
-    breath(target_ptr, y, x, m_idx, GF_MANA, dam, 4, FALSE, MS_BALL_MANA, TARGET_TYPE);
-    return dam;
+    const auto dam = monspell_damage(target_ptr, (MS_BALL_MANA), m_idx, DAM_ROLL);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_MANA, dam, 4, FALSE, MS_BALL_MANA, TARGET_TYPE);
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -291,22 +318,24 @@ HIT_POINT spell_RF5_BA_MANA(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BA_DARK(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BA_DARK(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かを力強くつぶやいた。", "%^s mumbles powerfully."),
         _("%^sが暗黒の嵐の呪文を念じた。", "%^s invokes a darkness storm."),
         _("%^sが%sに対して暗黒の嵐の呪文を念じた。", "%^s invokes a darkness storm upon %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_BALL_DARK), m_idx, DAM_ROLL);
-    breath(target_ptr, y, x, m_idx, GF_DARK, dam, 4, FALSE, MS_BALL_DARK, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_BALL_DARK), m_idx, DAM_ROLL);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_DARK, dam, 4, FALSE, MS_BALL_DARK, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER)
         update_smart_learn(target_ptr, m_idx, DRS_DARK);
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -317,20 +346,22 @@ HIT_POINT spell_RF5_BA_DARK(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BA_LITE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BA_LITE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かを力強くつぶやいた。", "%^s mumbles powerfully."),
         _("%^sがスターバーストの呪文を念じた。", "%^s invokes a starburst."),
         _("%^sが%sに対してスターバーストの呪文を念じた。", "%^s invokes a starburst upon %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_STARBURST), m_idx, DAM_ROLL);
-    breath(target_ptr, y, x, m_idx, GF_LITE, dam, 4, FALSE, MS_STARBURST, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_STARBURST), m_idx, DAM_ROLL);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_LITE, dam, 4, FALSE, MS_STARBURST, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER)
         update_smart_learn(target_ptr, m_idx, DRS_LITE);
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
index 26283db..bfffe21 100644 (file)
@@ -2,15 +2,17 @@
 
 #include "system/angband.h"
 
-HIT_POINT spell_RF4_BA_NUKE(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF4_BA_CHAO(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BA_ACID(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BA_ELEC(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BA_FIRE(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BA_COLD(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BA_POIS(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BA_NETH(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BA_WATE(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BA_MANA(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BA_DARK(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BA_LITE(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+struct MonsterSpellResult;
+
+MonsterSpellResult spell_RF4_BA_NUKE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF4_BA_CHAO(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BA_ACID(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BA_ELEC(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BA_FIRE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BA_COLD(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BA_POIS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BA_NETH(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BA_WATE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BA_MANA(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BA_DARK(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BA_LITE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
index 11ce803..16b6e86 100644 (file)
@@ -1,4 +1,5 @@
 #include "mspell/mspell-bolt.h"
+#include "effect/effect-processor.h"
 #include "main/sound-definitions-table.h"
 #include "main/sound-of-music.h"
 #include "mind/drs-types.h"
@@ -8,6 +9,7 @@
 #include "mspell/mspell-damage-calculator.h"
 #include "mspell/mspell-type.h"
 #include "mspell/mspell-util.h"
+#include "mspell/mspell.h"
 #include "spell/spell-types.h"
 
 /*!
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF4_SHOOT(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF4_SHOOT(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが奇妙な音を発した。", "%^s makes a strange noise."), _("%^sが矢を放った。", "%^s fires an arrow."),
         _("%^sが%sに矢を放った。", "%^s fires an arrow at %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_SHOOT), m_idx, DAM_ROLL);
-    bolt(target_ptr, m_idx, y, x, GF_ARROW, dam, MS_SHOOT, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_SHOOT), m_idx, DAM_ROLL);
+    const auto proj_res = bolt(target_ptr, m_idx, y, x, GF_ARROW, dam, MS_SHOOT, TARGET_TYPE);
     sound(SOUND_SHOOT);
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -42,24 +46,26 @@ HIT_POINT spell_RF4_SHOOT(player_type *target_ptr, POSITION y, POSITION x, MONST
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BO_ACID(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BO_ACID(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sがアシッド・ボルトの呪文を唱えた。", "%^s casts an acid bolt."),
         _("%sが%sに向かってアシッド・ボルトの呪文を唱えた。", "%^s casts an acid bolt at %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_BOLT_ACID), m_idx, DAM_ROLL);
-    bolt(target_ptr, m_idx, y, x, GF_ACID, dam, MS_BOLT_ACID, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_BOLT_ACID), m_idx, DAM_ROLL);
+    const auto proj_res = bolt(target_ptr, m_idx, y, x, GF_ACID, dam, MS_BOLT_ACID, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER) {
         update_smart_learn(target_ptr, m_idx, DRS_ACID);
         update_smart_learn(target_ptr, m_idx, DRS_REFLECT);
     }
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -70,24 +76,26 @@ HIT_POINT spell_RF5_BO_ACID(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BO_ELEC(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BO_ELEC(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sがサンダー・ボルトの呪文を唱えた。", "%^s casts a lightning bolt."),
         _("%^sが%sに向かってサンダー・ボルトの呪文を唱えた。", "%^s casts a lightning bolt at %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_BOLT_ELEC), m_idx, DAM_ROLL);
-    bolt(target_ptr, m_idx, y, x, GF_ELEC, dam, MS_BOLT_ELEC, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_BOLT_ELEC), m_idx, DAM_ROLL);
+    const auto proj_res = bolt(target_ptr, m_idx, y, x, GF_ELEC, dam, MS_BOLT_ELEC, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER) {
         update_smart_learn(target_ptr, m_idx, DRS_ELEC);
         update_smart_learn(target_ptr, m_idx, DRS_REFLECT);
     }
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -98,24 +106,26 @@ HIT_POINT spell_RF5_BO_ELEC(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BO_FIRE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BO_FIRE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sがファイア・ボルトの呪文を唱えた。", "%^s casts a fire bolt."),
         _("%^sが%sに向かってファイア・ボルトの呪文を唱えた。", "%^s casts a fire bolt at %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_BOLT_FIRE), m_idx, DAM_ROLL);
-    bolt(target_ptr, m_idx, y, x, GF_FIRE, dam, MS_BOLT_FIRE, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_BOLT_FIRE), m_idx, DAM_ROLL);
+    const auto proj_res = bolt(target_ptr, m_idx, y, x, GF_FIRE, dam, MS_BOLT_FIRE, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER) {
         update_smart_learn(target_ptr, m_idx, DRS_FIRE);
         update_smart_learn(target_ptr, m_idx, DRS_REFLECT);
     }
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -126,24 +136,26 @@ HIT_POINT spell_RF5_BO_FIRE(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BO_COLD(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BO_COLD(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sがアイス・ボルトの呪文を唱えた。", "%^s casts a frost bolt."),
         _("%^sが%sに向かってアイス・ボルトの呪文を唱えた。", "%^s casts a frost bolt at %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_BOLT_COLD), m_idx, DAM_ROLL);
-    bolt(target_ptr, m_idx, y, x, GF_COLD, dam, MS_BOLT_COLD, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_BOLT_COLD), m_idx, DAM_ROLL);
+    const auto proj_res = bolt(target_ptr, m_idx, y, x, GF_COLD, dam, MS_BOLT_COLD, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER) {
         update_smart_learn(target_ptr, m_idx, DRS_COLD);
         update_smart_learn(target_ptr, m_idx, DRS_REFLECT);
     }
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -154,23 +166,25 @@ HIT_POINT spell_RF5_BO_COLD(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BO_NETH(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BO_NETH(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."), _("%^sが地獄の矢の呪文を唱えた。", "%^s casts a nether bolt."),
         _("%^sが%sに向かって地獄の矢の呪文を唱えた。", "%^s casts a nether bolt at %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_BOLT_NETHER), m_idx, DAM_ROLL);
-    bolt(target_ptr, m_idx, y, x, GF_NETHER, dam, MS_BOLT_NETHER, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_BOLT_NETHER), m_idx, DAM_ROLL);
+    const auto proj_res = bolt(target_ptr, m_idx, y, x, GF_NETHER, dam, MS_BOLT_NETHER, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER) {
         update_smart_learn(target_ptr, m_idx, DRS_NETH);
         update_smart_learn(target_ptr, m_idx, DRS_REFLECT);
     }
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -181,23 +195,25 @@ HIT_POINT spell_RF5_BO_NETH(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BO_WATE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BO_WATE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sがウォーター・ボルトの呪文を唱えた。", "%^s casts a water bolt."),
         _("%^sが%sに向かってウォーター・ボルトの呪文を唱えた。", "%^s casts a water bolt at %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_BOLT_WATER), m_idx, DAM_ROLL);
-    bolt(target_ptr, m_idx, y, x, GF_WATER, dam, MS_BOLT_WATER, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_BOLT_WATER), m_idx, DAM_ROLL);
+    const auto proj_res = bolt(target_ptr, m_idx, y, x, GF_WATER, dam, MS_BOLT_WATER, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER) {
         update_smart_learn(target_ptr, m_idx, DRS_REFLECT);
     }
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -208,22 +224,24 @@ HIT_POINT spell_RF5_BO_WATE(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BO_MANA(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BO_MANA(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."), _("%^sが魔力の矢の呪文を唱えた。", "%^s casts a mana bolt."),
         _("%^sが%sに向かって魔力の矢の呪文を唱えた。", "%^s casts a mana bolt at %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_BOLT_MANA), m_idx, DAM_ROLL);
-    bolt(target_ptr, m_idx, y, x, GF_MANA, dam, MS_BOLT_MANA, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_BOLT_MANA), m_idx, DAM_ROLL);
+    const auto proj_res = bolt(target_ptr, m_idx, y, x, GF_MANA, dam, MS_BOLT_MANA, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER) {
         update_smart_learn(target_ptr, m_idx, DRS_REFLECT);
     }
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -234,23 +252,25 @@ HIT_POINT spell_RF5_BO_MANA(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BO_PLAS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BO_PLAS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sがプラズマ・ボルトの呪文を唱えた。", "%^s casts a plasma bolt."),
         _("%^sが%sに向かってプラズマ・ボルトの呪文を唱えた。", "%^s casts a plasma bolt at %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_BOLT_PLASMA), m_idx, DAM_ROLL);
-    bolt(target_ptr, m_idx, y, x, GF_PLASMA, dam, MS_BOLT_PLASMA, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_BOLT_PLASMA), m_idx, DAM_ROLL);
+    const auto proj_res = bolt(target_ptr, m_idx, y, x, GF_PLASMA, dam, MS_BOLT_PLASMA, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER) {
         update_smart_learn(target_ptr, m_idx, DRS_REFLECT);
     }
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -261,23 +281,25 @@ HIT_POINT spell_RF5_BO_PLAS(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BO_ICEE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BO_ICEE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."), _("%^sが極寒の矢の呪文を唱えた。", "%^s casts an ice bolt."),
         _("%^sが%sに向かって極寒の矢の呪文を唱えた。", "%^s casts an ice bolt at %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_BOLT_ICE), m_idx, DAM_ROLL);
-    bolt(target_ptr, m_idx, y, x, GF_ICE, dam, MS_BOLT_ICE, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_BOLT_ICE), m_idx, DAM_ROLL);
+    const auto proj_res = bolt(target_ptr, m_idx, y, x, GF_ICE, dam, MS_BOLT_ICE, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER) {
         update_smart_learn(target_ptr, m_idx, DRS_COLD);
         update_smart_learn(target_ptr, m_idx, DRS_REFLECT);
     }
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -288,21 +310,23 @@ HIT_POINT spell_RF5_BO_ICEE(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_MISSILE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_MISSILE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sがマジック・ミサイルの呪文を唱えた。", "%^s casts a magic missile."),
         _("%^sが%sに向かってマジック・ミサイルの呪文を唱えた。", "%^s casts a magic missile at %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_MAGIC_MISSILE), m_idx, DAM_ROLL);
-    bolt(target_ptr, m_idx, y, x, GF_MISSILE, dam, MS_MAGIC_MISSILE, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_MAGIC_MISSILE), m_idx, DAM_ROLL);
+    const auto proj_res = bolt(target_ptr, m_idx, y, x, GF_MISSILE, dam, MS_MAGIC_MISSILE, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER) {
         update_smart_learn(target_ptr, m_idx, DRS_REFLECT);
     }
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
index 38b9d87..6788b6b 100644 (file)
@@ -2,14 +2,16 @@
 
 #include "system/angband.h"
 
-HIT_POINT spell_RF4_SHOOT(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BO_ACID(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BO_ELEC(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BO_FIRE(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BO_COLD(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BO_NETH(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BO_WATE(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BO_MANA(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BO_PLAS(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BO_ICEE(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_MISSILE(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+struct MonsterSpellResult;
+
+MonsterSpellResult spell_RF4_SHOOT(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BO_ACID(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BO_ELEC(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BO_FIRE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BO_COLD(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BO_NETH(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BO_WATE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BO_MANA(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BO_PLAS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BO_ICEE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_MISSILE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
index 405b958..35ce04a 100644 (file)
@@ -1,5 +1,6 @@
 #include "mspell/mspell-breath.h"
 #include "core/disturbance.h"
+#include "effect/effect-processor.h"
 #include "main/sound-definitions-table.h"
 #include "main/sound-of-music.h"
 #include "mind/drs-types.h"
@@ -9,6 +10,7 @@
 #include "mspell/mspell-checker.h"
 #include "mspell/mspell-damage-calculator.h"
 #include "mspell/mspell-util.h"
+#include "mspell/mspell.h"
 #include "spell/spell-types.h"
 #include "system/floor-type-definition.h"
 #include "view/display-messages.h"
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF4_BREATH(player_type *target_ptr, int GF_TYPE, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF4_BREATH(player_type *target_ptr, int GF_TYPE, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     HIT_POINT dam, ms_type, drs_type = 0;
     concptr type_s;
@@ -203,9 +206,12 @@ HIT_POINT spell_RF4_BREATH(player_type *target_ptr, int GF_TYPE, POSITION y, POS
         floor_ptr->monster_noise = TRUE;
 
     sound(SOUND_BREATH);
-    breath(target_ptr, y, x, m_idx, GF_TYPE, dam, 0, TRUE, ms_type, TARGET_TYPE);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_TYPE, dam, 0, TRUE, ms_type, TARGET_TYPE);
     if (smart_learn_aux && mon_to_player)
         update_smart_learn(target_ptr, m_idx, drs_type);
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
index b7a07c2..9020e27 100644 (file)
@@ -2,4 +2,6 @@
 
 #include "system/angband.h"
 
-HIT_POINT spell_RF4_BREATH(player_type* target_ptr, int GF_TYPE, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+struct MonsterSpellResult;
+
+MonsterSpellResult spell_RF4_BREATH(player_type *target_ptr, int GF_TYPE, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
index 0a9b2a1..9c38511 100644 (file)
@@ -186,9 +186,8 @@ bool clean_shot(player_type *target_ptr, POSITION y1, POSITION x1, POSITION y2,
  * @param dam_hp 威力
  * @param monspell モンスター魔法のID
  * @param target_type モンスターからモンスターへ撃つならMONSTER_TO_MONSTER、モンスターからプレイヤーならMONSTER_TO_PLAYER
- * @return なし
  */
-void bolt(player_type *target_ptr, MONSTER_IDX m_idx, POSITION y, POSITION x, EFFECT_ID typ, int dam_hp, int monspell, int target_type)
+ProjectResult bolt(player_type *target_ptr, MONSTER_IDX m_idx, POSITION y, POSITION x, EFFECT_ID typ, int dam_hp, int monspell, int target_type)
 {
     BIT_FLAGS flg = 0;
     switch (target_type) {
@@ -203,7 +202,7 @@ void bolt(player_type *target_ptr, MONSTER_IDX m_idx, POSITION y, POSITION x, EF
     if (typ != GF_ARROW)
         flg |= PROJECT_REFLECTABLE;
 
-    (void)project(target_ptr, m_idx, 0, y, x, dam_hp, typ, flg, monspell);
+    return project(target_ptr, m_idx, 0, y, x, dam_hp, typ, flg, monspell);
 }
 
 /*!
@@ -216,9 +215,8 @@ void bolt(player_type *target_ptr, MONSTER_IDX m_idx, POSITION y, POSITION x, EF
  * @param dam_hp 威力
  * @param monspell モンスター魔法のID
  * @param target_type モンスターからモンスターへ撃つならMONSTER_TO_MONSTER、モンスターからプレイヤーならMONSTER_TO_PLAYER
- * @return なし
  */
-void beam(player_type *target_ptr, MONSTER_IDX m_idx, POSITION y, POSITION x, EFFECT_ID typ, int dam_hp, int monspell, int target_type)
+ProjectResult beam(player_type *target_ptr, MONSTER_IDX m_idx, POSITION y, POSITION x, EFFECT_ID typ, int dam_hp, int monspell, int target_type)
 {
     BIT_FLAGS flg = 0;
     switch (target_type) {
@@ -230,7 +228,7 @@ void beam(player_type *target_ptr, MONSTER_IDX m_idx, POSITION y, POSITION x, EF
         break;
     }
 
-    (void)project(target_ptr, m_idx, 0, y, x, dam_hp, typ, flg, monspell);
+    return project(target_ptr, m_idx, 0, y, x, dam_hp, typ, flg, monspell);
 }
 
 /*!
@@ -246,9 +244,8 @@ void beam(player_type *target_ptr, MONSTER_IDX m_idx, POSITION y, POSITION x, EF
  * @param breath
  * @param monspell モンスター魔法のID
  * @param target_type モンスターからモンスターへ撃つならMONSTER_TO_MONSTER、モンスターからプレイヤーならMONSTER_TO_PLAYER
- * @return なし
  */
-void breath(
+ProjectResult breath(
     player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, EFFECT_ID typ, int dam_hp, POSITION rad, bool breath, int monspell, int target_type)
 {
     monster_type *m_ptr = &target_ptr->current_floor_ptr->m_list[m_idx];
@@ -285,7 +282,7 @@ void breath(
         break;
     }
 
-    (void)project(target_ptr, m_idx, rad, y, x, dam_hp, typ, flg, monspell);
+    return project(target_ptr, m_idx, rad, y, x, dam_hp, typ, flg, monspell);
 }
 
 /*!
index 24b9b23..3ada118 100644 (file)
@@ -3,11 +3,13 @@
 #include "system/angband.h"
 #include "system/monster-type-definition.h"
 
+struct ProjectResult;
+
 bool clean_shot(player_type *target_ptr, POSITION y1, POSITION x1, POSITION y2, POSITION x2, bool is_friend);
 bool summon_possible(player_type *target_ptr, POSITION y1, POSITION x1);
 bool raise_possible(player_type *target_ptr, monster_type *m_ptr);
 bool spell_is_inate(SPELL_IDX spell);
-void beam(player_type *target_ptr, MONSTER_IDX m_idx, POSITION y, POSITION x, EFFECT_ID typ, int dam_hp, int monspell, int target_type);
-void bolt(player_type *target_ptr, MONSTER_IDX m_idx, POSITION y, POSITION x, EFFECT_ID typ, int dam_hp, int monspell, int target_type);
-void breath(
+ProjectResult beam(player_type *target_ptr, MONSTER_IDX m_idx, POSITION y, POSITION x, EFFECT_ID typ, int dam_hp, int monspell, int target_type);
+ProjectResult bolt(player_type *target_ptr, MONSTER_IDX m_idx, POSITION y, POSITION x, EFFECT_ID typ, int dam_hp, int monspell, int target_type);
+ProjectResult breath(
     player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, EFFECT_ID typ, int dam_hp, POSITION rad, bool breath, int monspell, int target_type);
index 20885f7..bac6455 100644 (file)
@@ -1,17 +1,19 @@
 #include "mspell/mspell-curse.h"
 #include "core/disturbance.h"
+#include "effect/effect-processor.h"
 #include "monster/monster-info.h"
 #include "monster/monster-list.h"
 #include "mspell/mspell-checker.h"
 #include "mspell/mspell-damage-calculator.h"
 #include "mspell/mspell-type.h"
 #include "mspell/mspell-util.h"
+#include "mspell/mspell.h"
 #include "spell/spell-types.h"
 #include "system/floor-type-definition.h"
 #include "view/display-messages.h"
 
 /*!
- * @brief RF5_CAUSE_*のメッセージ処理関数 /
+ * @brief RF5_CAUSE_* の処理関数
  * @param target_ptr プレーヤーへの参照ポインタ
  * @param GF_TYPE 攻撃に使用する属性
  * @param dam 攻撃に使用するダメージ量
  * @param msg3 対モンスターのメッセージ
  * @param MS_TYPE 呪文の番号
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
  */
-void spell_RF5_CAUSE(player_type *target_ptr, int GF_TYPE, HIT_POINT dam, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, concptr msg1,
-    concptr msg2, concptr msg3, int MS_TYPE, int TARGET_TYPE)
+static MonsterSpellResult spell_RF5_CAUSE(player_type *target_ptr, int GF_TYPE, HIT_POINT dam, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx,
+    concptr msg1, concptr msg2, concptr msg3, int MS_TYPE, int TARGET_TYPE)
 {
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
     GAME_TEXT m_name[MAX_NLEN], t_name[MAX_NLEN];
     monster_name(target_ptr, m_idx, m_name);
     monster_name(target_ptr, t_idx, t_name);
@@ -40,7 +44,7 @@ void spell_RF5_CAUSE(player_type *target_ptr, int GF_TYPE, HIT_POINT dam, POSITI
         else
             msg_format(msg2, m_name);
         breath(target_ptr, y, x, m_idx, GF_TYPE, dam, 0, FALSE, MS_TYPE, TARGET_TYPE);
-        return;
+        return res;
     }
 
     if (TARGET_TYPE == MONSTER_TO_MONSTER) {
@@ -52,6 +56,8 @@ void spell_RF5_CAUSE(player_type *target_ptr, int GF_TYPE, HIT_POINT dam, POSITI
     }
 
     breath(target_ptr, y, x, m_idx, GF_TYPE, dam, 0, FALSE, MS_TYPE, TARGET_TYPE);
+
+    return res;
 }
 
 /*!
@@ -62,20 +68,18 @@ void spell_RF5_CAUSE(player_type *target_ptr, int GF_TYPE, HIT_POINT dam, POSITI
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-HIT_POINT spell_RF5_CAUSE_1(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_CAUSE_1(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    concptr msg1, msg2, msg3;
-    HIT_POINT dam;
-    dam = monspell_damage(target_ptr, (MS_CAUSE_1), m_idx, DAM_ROLL);
+    concptr msg1 = _("%^sが何かをつぶやいた。", "%^s mumbles.");
+    concptr msg2 = _("%^sがあなたを指さして呪った。", "%^s points at you and curses.");
+    concptr msg3 = _("%^sは%sを指さして呪いをかけた。", "%^s points at %s and curses.");
 
-    msg1 = _("%^sが何かをつぶやいた。", "%^s mumbles.");
-    msg2 = _("%^sがあなたを指さして呪った。", "%^s points at you and curses.");
-    msg3 = _("%^sは%sを指さして呪いをかけた。", "%^s points at %s and curses.");
+    const auto dam = monspell_damage(target_ptr, (MS_CAUSE_1), m_idx, DAM_ROLL);
 
-    spell_RF5_CAUSE(target_ptr, GF_CAUSE_1, dam, y, x, m_idx, t_idx, msg1, msg2, msg3, MS_CAUSE_1, TARGET_TYPE);
-    return dam;
+    return spell_RF5_CAUSE(target_ptr, GF_CAUSE_1, dam, y, x, m_idx, t_idx, msg1, msg2, msg3, MS_CAUSE_1, TARGET_TYPE);
 }
 
 /*!
@@ -86,20 +90,18 @@ HIT_POINT spell_RF5_CAUSE_1(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-HIT_POINT spell_RF5_CAUSE_2(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_CAUSE_2(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    concptr msg1, msg2, msg3;
-    HIT_POINT dam;
-    dam = monspell_damage(target_ptr, (MS_CAUSE_2), m_idx, DAM_ROLL);
+    concptr msg1 = _("%^sが何かをつぶやいた。", "%^s mumbles.");
+    concptr msg2 = _("%^sがあなたを指さして恐ろしげに呪った。", "%^s points at you and curses horribly.");
+    concptr msg3 = _("%^sは%sを指さして恐ろしげに呪いをかけた。", "%^s points at %s and curses horribly.");
 
-    msg1 = _("%^sが何かをつぶやいた。", "%^s mumbles.");
-    msg2 = _("%^sがあなたを指さして恐ろしげに呪った。", "%^s points at you and curses horribly.");
-    msg3 = _("%^sは%sを指さして恐ろしげに呪いをかけた。", "%^s points at %s and curses horribly.");
+    const auto dam = monspell_damage(target_ptr, (MS_CAUSE_2), m_idx, DAM_ROLL);
 
-    spell_RF5_CAUSE(target_ptr, GF_CAUSE_2, dam, y, x, m_idx, t_idx, msg1, msg2, msg3, MS_CAUSE_2, TARGET_TYPE);
-    return dam;
+    return spell_RF5_CAUSE(target_ptr, GF_CAUSE_2, dam, y, x, m_idx, t_idx, msg1, msg2, msg3, MS_CAUSE_2, TARGET_TYPE);
 }
 
 /*!
@@ -110,20 +112,18 @@ HIT_POINT spell_RF5_CAUSE_2(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-HIT_POINT spell_RF5_CAUSE_3(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_CAUSE_3(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    concptr msg1, msg2, msg3;
-    HIT_POINT dam;
-    dam = monspell_damage(target_ptr, (MS_CAUSE_3), m_idx, DAM_ROLL);
+    concptr msg1 = _("%^sが何かを大声で叫んだ。", "%^s mumbles loudly.");
+    concptr msg2 = _("%^sがあなたを指さして恐ろしげに呪文を唱えた!", "%^s points at you, incanting terribly!");
+    concptr msg3 = _("%^sは%sを指さし、恐ろしげに呪文を唱えた!", "%^s points at %s, incanting terribly!");
 
-    msg1 = _("%^sが何かを大声で叫んだ。", "%^s mumbles loudly.");
-    msg2 = _("%^sがあなたを指さして恐ろしげに呪文を唱えた!", "%^s points at you, incanting terribly!");
-    msg3 = _("%^sは%sを指さし、恐ろしげに呪文を唱えた!", "%^s points at %s, incanting terribly!");
+    const auto dam = monspell_damage(target_ptr, (MS_CAUSE_3), m_idx, DAM_ROLL);
 
-    spell_RF5_CAUSE(target_ptr, GF_CAUSE_3, dam, y, x, m_idx, t_idx, msg1, msg2, msg3, MS_CAUSE_3, TARGET_TYPE);
-    return dam;
+    return spell_RF5_CAUSE(target_ptr, GF_CAUSE_3, dam, y, x, m_idx, t_idx, msg1, msg2, msg3, MS_CAUSE_3, TARGET_TYPE);
 }
 
 /*!
@@ -134,18 +134,16 @@ HIT_POINT spell_RF5_CAUSE_3(player_type *target_ptr, POSITION y, POSITION x, MON
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-HIT_POINT spell_RF5_CAUSE_4(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_CAUSE_4(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    concptr msg1, msg2, msg3;
-    HIT_POINT dam;
-    dam = monspell_damage(target_ptr, (MS_CAUSE_4), m_idx, DAM_ROLL);
+    concptr msg1 = _("%^sが「お前は既に死んでいる」と叫んだ。", "%^s screams the word 'DIE!'");
+    concptr msg2 = _("%^sがあなたの秘孔を突いて「お前は既に死んでいる」と叫んだ。", "%^s points at you, screaming the word DIE!");
+    concptr msg3 = _("%^sが%sの秘孔を突いて、「お前は既に死んでいる」と叫んだ。", "%^s points at %s, screaming the word, 'DIE!'");
 
-    msg1 = _("%^sが「お前は既に死んでいる」と叫んだ。", "%^s screams the word 'DIE!'");
-    msg2 = _("%^sがあなたの秘孔を突いて「お前は既に死んでいる」と叫んだ。", "%^s points at you, screaming the word DIE!");
-    msg3 = _("%^sが%sの秘孔を突いて、「お前は既に死んでいる」と叫んだ。", "%^s points at %s, screaming the word, 'DIE!'");
+    const auto dam = monspell_damage(target_ptr, (MS_CAUSE_4), m_idx, DAM_ROLL);
 
-    spell_RF5_CAUSE(target_ptr, GF_CAUSE_4, dam, y, x, m_idx, t_idx, msg1, msg2, msg3, MS_CAUSE_4, TARGET_TYPE);
-    return dam;
+    return spell_RF5_CAUSE(target_ptr, GF_CAUSE_4, dam, y, x, m_idx, t_idx, msg1, msg2, msg3, MS_CAUSE_4, TARGET_TYPE);
 }
index dd9b7c4..48c60e7 100644 (file)
@@ -2,8 +2,9 @@
 
 #include "system/angband.h"
 
-void spell_RF5_CAUSE(player_type* target_ptr, int GF_TYPE, HIT_POINT dam, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, concptr msg1, concptr msg2, concptr msg3, int MS_TYPE, int TARGET_TYPE);
-HIT_POINT spell_RF5_CAUSE_1(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_CAUSE_2(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_CAUSE_3(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_CAUSE_4(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+struct MonsterSpellResult;
+
+MonsterSpellResult spell_RF5_CAUSE_1(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_CAUSE_2(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_CAUSE_3(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_CAUSE_4(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
index 63fd150..5afa156 100644 (file)
@@ -11,6 +11,7 @@
 #include "monster/monster-status.h"
 #include "mspell/mspell-type.h"
 #include "mspell/mspell-util.h"
+#include "mspell/mspell.h"
 #include "player/attack-defense-types.h"
 #include "realm/realm-song-numbers.h"
 #include "spell-realm/spells-craft.h"
@@ -104,9 +105,14 @@ static void dispel_player(player_type *creature_ptr)
  * @param target_ptr プレーヤーへの参照ポインタ
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF4_DISPEL(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF4_DISPEL(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
     GAME_TEXT m_name[MAX_NLEN], t_name[MAX_NLEN];
     monster_name(target_ptr, m_idx, m_name);
     monster_name(target_ptr, t_idx, t_name);
@@ -129,7 +135,7 @@ void spell_RF4_DISPEL(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_
                 msg_print(_("弱い者いじめは止めるんだ!", ""));
         }
 
-        return;
+        return res;
     }
 
     if (TARGET_TYPE == MONSTER_TO_MONSTER) {
@@ -138,4 +144,6 @@ void spell_RF4_DISPEL(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_
 
         dispel_monster_status(target_ptr, t_idx);
     }
+
+    return res;
 }
index 434fa15..d67fdd6 100644 (file)
@@ -2,4 +2,6 @@
 
 #include "system/angband.h"
 
-void spell_RF4_DISPEL(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE);
+struct MonsterSpellResult;
+
+MonsterSpellResult spell_RF4_DISPEL(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE);
index fe26e4f..d245c99 100644 (file)
@@ -24,6 +24,7 @@
 #include "mspell/mspell-status.h"
 #include "mspell/mspell-type.h"
 #include "mspell/mspell-util.h"
+#include "mspell/mspell.h"
 #include "player/player-personalities-types.h"
 #include "player/player-status-flags.h"
 #include "spell-kind/spells-lite.h"
  * @param target_ptr プレーヤーへの参照ポインタ
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
+ *
+ * ラーニング不可。
  */
-void spell_RF4_SHRIEK(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF4_SHRIEK(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     simple_monspell_message(target_ptr, m_idx, t_idx, _("%^sがかん高い金切り声をあげた。", "%^s makes a high pitched shriek."),
         _("%^sが%sに向かって叫んだ。", "%^s shrieks at %s."), TARGET_TYPE);
 
     if (TARGET_TYPE == MONSTER_TO_PLAYER) {
         aggravate_monsters(target_ptr, m_idx);
-        return;
-    }
-
-    if (TARGET_TYPE == MONSTER_TO_MONSTER) {
+    } else if (TARGET_TYPE == MONSTER_TO_MONSTER) {
         set_monster_csleep(target_ptr, t_idx, 0);
     }
+
+    return MonsterSpellResult::make_valid();
 }
 
 /*!
  * @brief RF6_WORLDの処理。時を止める。 /
  * @param target_ptr プレーヤーへの参照ポインタ
  * @param m_idx 呪文を唱えるモンスターID
+ *
+ * ラーニング不可。
  */
-HIT_POINT spell_RF6_WORLD(player_type *target_ptr, MONSTER_IDX m_idx)
+MonsterSpellResult spell_RF6_WORLD(player_type *target_ptr, MONSTER_IDX m_idx)
 {
     monster_type *m_ptr = &target_ptr->current_floor_ptr->m_list[m_idx];
     MONSTER_IDX who = 0;
@@ -75,9 +79,10 @@ HIT_POINT spell_RF6_WORLD(player_type *target_ptr, MONSTER_IDX m_idx)
         who = 1;
     else if (m_ptr->r_idx == MON_WONG)
         who = 3;
-    if (!set_monster_timewalk(target_ptr, randint1(2) + 2, who, TRUE))
-        return FALSE;
-    return who;
+
+    (void)set_monster_timewalk(target_ptr, randint1(2) + 2, who, TRUE);
+
+    return MonsterSpellResult::make_valid();
 }
 
 /*!
@@ -86,9 +91,13 @@ HIT_POINT spell_RF6_WORLD(player_type *target_ptr, MONSTER_IDX m_idx)
  * @param m_idx 呪文を唱えるモンスターID
  * @param is_quantum_effect 量子的効果によるショート・テレポートの場合時TRUE
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
+ *
+ * ラーニング不可。
  */
-void spell_RF6_BLINK(player_type *target_ptr, MONSTER_IDX m_idx, int TARGET_TYPE, bool is_quantum_effect)
+MonsterSpellResult spell_RF6_BLINK(player_type *target_ptr, MONSTER_IDX m_idx, int TARGET_TYPE, bool is_quantum_effect)
 {
+    const auto res = MonsterSpellResult::make_valid();
+
     GAME_TEXT m_name[MAX_NLEN];
     monster_name(target_ptr, m_idx, m_name);
 
@@ -98,7 +107,7 @@ void spell_RF6_BLINK(player_type *target_ptr, MONSTER_IDX m_idx, int TARGET_TYPE
     if (!is_quantum_effect && teleport_barrier(target_ptr, m_idx)) {
         if (see_monster(target_ptr, m_idx))
             msg_format(_("魔法のバリアが%^sのテレポートを邪魔した。", "Magic barrier obstructs teleporting of %^s."), m_name);
-        return;
+        return res;
     }
 
     if (see_monster(target_ptr, m_idx))
@@ -108,6 +117,8 @@ void spell_RF6_BLINK(player_type *target_ptr, MONSTER_IDX m_idx, int TARGET_TYPE
 
     if (TARGET_TYPE == MONSTER_TO_PLAYER)
         target_ptr->update |= (PU_MONSTERS);
+
+    return res;
 }
 
 /*!
@@ -115,9 +126,13 @@ void spell_RF6_BLINK(player_type *target_ptr, MONSTER_IDX m_idx, int TARGET_TYPE
  * @param target_ptr プレーヤーへの参照ポインタ
  * @param m_idx 呪文を唱えるモンスターID
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
+ *
+ * ラーニング不可。
  */
-void spell_RF6_TPORT(player_type *target_ptr, MONSTER_IDX m_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_TPORT(player_type *target_ptr, MONSTER_IDX m_idx, int TARGET_TYPE)
 {
+    const auto res = MonsterSpellResult::make_valid();
+
     GAME_TEXT m_name[MAX_NLEN];
     monster_name(target_ptr, m_idx, m_name);
 
@@ -126,13 +141,15 @@ void spell_RF6_TPORT(player_type *target_ptr, MONSTER_IDX m_idx, int TARGET_TYPE
     if (teleport_barrier(target_ptr, m_idx)) {
         if (see_monster(target_ptr, m_idx))
             msg_format(_("魔法のバリアが%^sのテレポートを邪魔した。", "Magic barrier obstructs teleporting of %^s."), m_name);
-        return;
+        return res;
     }
 
     if (see_monster(target_ptr, m_idx))
         msg_format(_("%^sがテレポートした。", "%^s teleports away."), m_name);
 
     teleport_away_followable(target_ptr, m_idx);
+
+    return res;
 }
 
 /*!
@@ -141,10 +158,14 @@ void spell_RF6_TPORT(player_type *target_ptr, MONSTER_IDX m_idx, int TARGET_TYPE
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_TELE_TO(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_TELE_TO(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
     floor_type *floor_ptr = target_ptr->current_floor_ptr;
     monster_type *m_ptr = &floor_ptr->m_list[m_idx];
     monster_type *t_ptr = &floor_ptr->m_list[t_idx];
@@ -155,11 +176,11 @@ void spell_RF6_TELE_TO(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t
 
     if (TARGET_TYPE == MONSTER_TO_PLAYER) {
         teleport_player_to(target_ptr, m_ptr->fy, m_ptr->fx, TELEPORT_PASSIVE);
-        return;
+        return res;
     }
 
     if (TARGET_TYPE != MONSTER_TO_MONSTER)
-        return;
+        return res;
 
     bool resists_tele = FALSE;
     GAME_TEXT t_name[MAX_NLEN];
@@ -185,7 +206,7 @@ void spell_RF6_TELE_TO(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t
 
     if (resists_tele) {
         set_monster_csleep(target_ptr, t_idx, 0);
-        return;
+        return res;
     }
 
     if (t_idx == target_ptr->riding)
@@ -193,6 +214,8 @@ void spell_RF6_TELE_TO(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t
     else
         teleport_monster_to(target_ptr, t_idx, m_ptr->fy, m_ptr->fx, 100, TELEPORT_PASSIVE);
     set_monster_csleep(target_ptr, t_idx, 0);
+
+    return res;
 }
 
 /*!
@@ -201,10 +224,14 @@ void spell_RF6_TELE_TO(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_TELE_AWAY(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_TELE_AWAY(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
     floor_type *floor_ptr = target_ptr->current_floor_ptr;
     monster_type *t_ptr = &floor_ptr->m_list[t_idx];
     monster_race *tr_ptr = &r_info[t_ptr->r_idx];
@@ -223,11 +250,11 @@ void spell_RF6_TELE_AWAY(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX
         }
 
         teleport_player_away(m_idx, target_ptr, 100, FALSE);
-        return;
+        return res;
     }
 
     if (TARGET_TYPE != MONSTER_TO_MONSTER)
-        return;
+        return res;
 
     bool resists_tele = FALSE;
     GAME_TEXT t_name[MAX_NLEN];
@@ -253,7 +280,7 @@ void spell_RF6_TELE_AWAY(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX
 
     if (resists_tele) {
         set_monster_csleep(target_ptr, t_idx, 0);
-        return;
+        return res;
     }
 
     if (t_idx == target_ptr->riding)
@@ -261,6 +288,8 @@ void spell_RF6_TELE_AWAY(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX
     else
         teleport_away(target_ptr, t_idx, MAX_SIGHT * 2 + 5, TELEPORT_PASSIVE);
     set_monster_csleep(target_ptr, t_idx, 0);
+
+    return res;
 }
 
 /*!
@@ -269,10 +298,13 @@ void spell_RF6_TELE_AWAY(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * ラーニング不可。
  */
-void spell_RF6_TELE_LEVEL(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_TELE_LEVEL(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
+    const auto res = MonsterSpellResult::make_valid();
+
     floor_type *floor_ptr = target_ptr->current_floor_ptr;
     monster_type *t_ptr = &floor_ptr->m_list[t_idx];
     monster_race *tr_ptr = &r_info[t_ptr->r_idx];
@@ -291,11 +323,11 @@ void spell_RF6_TELE_LEVEL(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_ID
         }
 
         update_smart_learn(target_ptr, m_idx, DRS_NEXUS);
-        return;
+        return res;
     }
 
     if (TARGET_TYPE != MONSTER_TO_MONSTER)
-        return;
+        return res;
 
     resist = tr_ptr->flagsr & (RFR_EFF_RES_NEXU_MASK | RFR_RES_TELE);
     saving_throw = (tr_ptr->flags1 & RF1_QUESTOR) || (tr_ptr->level > randint1((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10);
@@ -306,6 +338,8 @@ void spell_RF6_TELE_LEVEL(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_ID
     if (!resist && !saving_throw) {
         teleport_level(target_ptr, (t_idx == target_ptr->riding) ? 0 : t_idx);
     }
+
+    return res;
 }
 
 /*!
@@ -316,8 +350,10 @@ void spell_RF6_TELE_LEVEL(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_ID
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
+ *
+ * プレイヤーが対象かつ暗闇ならラーニング可。
  */
-void spell_RF6_DARKNESS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_DARKNESS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     floor_type *floor_ptr = target_ptr->current_floor_ptr;
     monster_type *m_ptr = &floor_ptr->m_list[m_idx];
@@ -335,6 +371,9 @@ void spell_RF6_DARKNESS(player_type *target_ptr, POSITION y, POSITION x, MONSTER
     if (monster_to_monster && !is_hostile(t_ptr))
         can_use_lite_area = FALSE;
 
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = monster_to_player && !can_use_lite_area;
+
     if (can_use_lite_area) {
         monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
             _("%^sが辺りを明るく照らした。", "%^s cast a spell to light up."), _("%^sが辺りを明るく照らした。", "%^s cast a spell to light up."), TARGET_TYPE);
@@ -355,20 +394,20 @@ void spell_RF6_DARKNESS(player_type *target_ptr, POSITION y, POSITION x, MONSTER
         if (can_use_lite_area) {
             (void)lite_area(target_ptr, 0, 3);
         } else {
-            // XXX: ラーニング条件が特技IDのみから決まらないため、やむを得ずここでラーニング処理を行う。
-            learn_spell(target_ptr, MS_DARKNESS);
             (void)unlite_area(target_ptr, 0, 3);
         }
 
-        return;
+        return res;
     }
 
     if (!monster_to_monster)
-        return;
+        return res;
 
     int lite_area = can_use_lite_area ? -1 : MS_DARKNESS;
     (void)project(target_ptr, m_idx, 3, y, x, 0, GF_LITE_WEAK, PROJECT_GRID | PROJECT_KILL, lite_area);
     lite_room(target_ptr, y, x);
+
+    return res;
 }
 
 /*!
@@ -377,9 +416,10 @@ void spell_RF6_DARKNESS(player_type *target_ptr, POSITION y, POSITION x, MONSTER
  * @param y 対象の地点のy座標
  * @param x 対象の地点のx座標
  * @param m_idx 呪文を唱えるモンスターID
- * @param なし
+ *
+ * ラーニング可。
  */
-void spell_RF6_TRAPS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx)
+MonsterSpellResult spell_RF6_TRAPS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx)
 {
     GAME_TEXT m_name[MAX_NLEN];
     monster_name(target_ptr, m_idx, m_name);
@@ -391,6 +431,11 @@ void spell_RF6_TRAPS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_ID
         msg_format(_("%^sが呪文を唱えて邪悪に微笑んだ。", "%^s casts a spell and cackles evilly."), m_name);
 
     (void)trap_creation(target_ptr, y, x);
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = true;
+
+    return res;
 }
 
 /*!
@@ -399,8 +444,10 @@ void spell_RF6_TRAPS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_ID
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
+ *
+ * ラーニング不可。
  */
-void spell_RF6_RAISE_DEAD(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_RAISE_DEAD(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monster_type *m_ptr = &target_ptr->current_floor_ptr->m_list[m_idx];
 
@@ -409,4 +456,6 @@ void spell_RF6_RAISE_DEAD(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_ID
         TARGET_TYPE);
 
     animate_dead(target_ptr, m_idx, m_ptr->fy, m_ptr->fx);
+
+    return MonsterSpellResult::make_valid();
 }
index 7eb4b4f..b5a71ed 100644 (file)
@@ -2,13 +2,15 @@
 
 #include "system/angband.h"
 
-void spell_RF4_SHRIEK(MONSTER_IDX m_idx, player_type* target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF6_WORLD(player_type* target_ptr, MONSTER_IDX m_idx);
-void spell_RF6_BLINK(player_type* target_ptr, MONSTER_IDX m_idx, int TARGET_TYPE, bool is_quantum_effect);
-void spell_RF6_TPORT(player_type* target_ptr, MONSTER_IDX m_idx, int TARGET_TYPE);
-void spell_RF6_TELE_TO(player_type* target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_TELE_AWAY(player_type* target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_TELE_LEVEL(player_type* target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_DARKNESS(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_TRAPS(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx);
-void spell_RF6_RAISE_DEAD(player_type* target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+struct MonsterSpellResult;
+
+MonsterSpellResult spell_RF4_SHRIEK(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_WORLD(player_type *target_ptr, MONSTER_IDX m_idx);
+MonsterSpellResult spell_RF6_BLINK(player_type *target_ptr, MONSTER_IDX m_idx, int TARGET_TYPE, bool is_quantum_effect);
+MonsterSpellResult spell_RF6_TPORT(player_type *target_ptr, MONSTER_IDX m_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_TELE_TO(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_TELE_AWAY(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_TELE_LEVEL(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_DARKNESS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_TRAPS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx);
+MonsterSpellResult spell_RF6_RAISE_DEAD(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
index fe5361e..4eea936 100644 (file)
@@ -9,12 +9,14 @@
  */
 
 #include "mspell/mspell-particularity.h"
+#include "effect/effect-processor.h"
 #include "mind/drs-types.h"
 #include "monster/monster-update.h"
 #include "mspell/mspell-checker.h"
 #include "mspell/mspell-damage-calculator.h"
 #include "mspell/mspell-type.h"
 #include "mspell/mspell-util.h"
+#include "mspell/mspell.h"
 #include "spell/spell-types.h"
 
 /*!
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF4_ROCKET(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF4_ROCKET(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
-
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かを射った。", "%^s shoots something."), _("%^sがロケットを発射した。", "%^s fires a rocket."),
         _("%^sが%sにロケットを発射した。", "%^s fires a rocket at %s."), TARGET_TYPE);
 
-    dam = monspell_damage(target_ptr, (MS_ROCKET), m_idx, DAM_ROLL);
-    breath(target_ptr, y, x, m_idx, GF_ROCKET, dam, 2, FALSE, MS_ROCKET, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_ROCKET), m_idx, DAM_ROLL);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_ROCKET, dam, 2, FALSE, MS_ROCKET, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER)
         update_smart_learn(target_ptr, m_idx, DRS_SHARD);
-    return dam;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -49,23 +54,27 @@ HIT_POINT spell_RF4_ROCKET(player_type *target_ptr, POSITION y, POSITION x, MONS
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF6_HAND_DOOM(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_HAND_DOOM(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     simple_monspell_message(target_ptr, m_idx, t_idx, _("%^sが<破滅の手>を放った!", "%^s invokes the Hand of Doom!"),
         _("%^sが%sに<破滅の手>を放った!", "%^s invokes the Hand of Doom upon %s!"), TARGET_TYPE);
 
-    HIT_POINT dam = 0;
+    ProjectResult proj_res;
     if (TARGET_TYPE == MONSTER_TO_PLAYER) {
-        dam = monspell_damage(target_ptr, (MS_HAND_DOOM), m_idx, DAM_ROLL);
-        breath(target_ptr, y, x, m_idx, GF_HAND_DOOM, dam, 0, FALSE, MS_HAND_DOOM, MONSTER_TO_PLAYER);
+        const auto dam = monspell_damage(target_ptr, (MS_HAND_DOOM), m_idx, DAM_ROLL);
+        proj_res = breath(target_ptr, y, x, m_idx, GF_HAND_DOOM, dam, 0, FALSE, MS_HAND_DOOM, MONSTER_TO_PLAYER);
     } else if (TARGET_TYPE == MONSTER_TO_MONSTER) {
-        dam = 20; /* Dummy power */
-        breath(target_ptr, y, x, m_idx, GF_HAND_DOOM, dam, 0, FALSE, MS_HAND_DOOM, MONSTER_TO_MONSTER);
+        const auto dam = 20; /* Dummy power */
+        proj_res = breath(target_ptr, y, x, m_idx, GF_HAND_DOOM, dam, 0, FALSE, MS_HAND_DOOM, MONSTER_TO_MONSTER);
     }
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -75,14 +84,19 @@ HIT_POINT spell_RF6_HAND_DOOM(player_type *target_ptr, POSITION y, POSITION x, M
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF6_PSY_SPEAR(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_PSY_SPEAR(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."), _("%^sが光の剣を放った。", "%^s throw a Psycho-Spear."),
         _("%^sが%sに向かって光の剣を放った。", "%^s throw a Psycho-spear at %s."), TARGET_TYPE);
 
-    HIT_POINT dam = monspell_damage(target_ptr, (MS_PSY_SPEAR), m_idx, DAM_ROLL);
-    beam(target_ptr, m_idx, y, x, GF_PSY_SPEAR, dam, MS_PSY_SPEAR, MONSTER_TO_PLAYER);
-    return dam;
+    const auto dam = monspell_damage(target_ptr, (MS_PSY_SPEAR), m_idx, DAM_ROLL);
+    const auto proj_res = beam(target_ptr, m_idx, y, x, GF_PSY_SPEAR, dam, MS_PSY_SPEAR, MONSTER_TO_PLAYER);
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
index 7e1487a..78c49ff 100644 (file)
@@ -2,6 +2,8 @@
 
 #include "system/angband.h"
 
-HIT_POINT spell_RF4_ROCKET(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF6_HAND_DOOM(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF6_PSY_SPEAR(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+struct MonsterSpellResult;
+
+MonsterSpellResult spell_RF4_ROCKET(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_HAND_DOOM(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_PSY_SPEAR(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
index 033af24..abeb343 100644 (file)
@@ -26,6 +26,7 @@
 #include "monster/monster-util.h"
 #include "mspell/mspell-checker.h"
 #include "mspell/mspell-util.h"
+#include "mspell/mspell.h"
 #include "player/player-damage.h"
 #include "spell-kind/spells-teleport.h"
 #include "spell-realm/spells-crusade.h"
@@ -38,7 +39,7 @@
  * @param player_ptr プレーヤーへの参照ポインタ
  * @param m_idx 呪文を唱えるモンスターID
  */
-HIT_POINT spell_RF6_SPECIAL_BANORLUPART(player_type *target_ptr, MONSTER_IDX m_idx)
+static MonsterSpellResult spell_RF6_SPECIAL_BANORLUPART(player_type *target_ptr, MONSTER_IDX m_idx)
 {
     floor_type *floor_ptr = target_ptr->current_floor_ptr;
     monster_type *m_ptr = &floor_ptr->m_list[m_idx];
@@ -53,7 +54,7 @@ HIT_POINT spell_RF6_SPECIAL_BANORLUPART(player_type *target_ptr, MONSTER_IDX m_i
         dummy_maxhp = m_ptr->maxhp / 2;
 
         if (floor_ptr->inside_arena || target_ptr->phase_out || !summon_possible(target_ptr, m_ptr->fy, m_ptr->fx))
-            return -1;
+            return MonsterSpellResult::make_invalid();
 
         delete_monster_idx(target_ptr, floor_ptr->grid_array[m_ptr->fy][m_ptr->fx].m_idx);
         summon_named_creature(target_ptr, 0, dummy_y, dummy_x, MON_BANOR, mode);
@@ -72,7 +73,7 @@ HIT_POINT spell_RF6_SPECIAL_BANORLUPART(player_type *target_ptr, MONSTER_IDX m_i
         dummy_maxhp = 0;
 
         if (!r_info[MON_BANOR].cur_num || !r_info[MON_LUPART].cur_num)
-            return -1;
+            return MonsterSpellResult::make_invalid();
 
         for (MONSTER_IDX k = 1; k < floor_ptr->m_max; k++) {
             if (floor_ptr->m_list[k].r_idx == MON_BANOR || floor_ptr->m_list[k].r_idx == MON_LUPART) {
@@ -93,7 +94,7 @@ HIT_POINT spell_RF6_SPECIAL_BANORLUPART(player_type *target_ptr, MONSTER_IDX m_i
         break;
     }
 
-    return 0;
+    return MonsterSpellResult::make_valid();
 }
 
 /*!
@@ -104,9 +105,8 @@ HIT_POINT spell_RF6_SPECIAL_BANORLUPART(player_type *target_ptr, MONSTER_IDX m_i
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
  */
-HIT_POINT spell_RF6_SPECIAL_ROLENTO(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+static MonsterSpellResult spell_RF6_SPECIAL_ROLENTO(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     int count = 0, k;
     int num = 1 + randint1(3);
@@ -121,7 +121,8 @@ HIT_POINT spell_RF6_SPECIAL_ROLENTO(player_type *target_ptr, POSITION y, POSITIO
     if (target_ptr->blind && count) {
         msg_print(_("多くのものが間近にばらまかれる音がする。", "You hear many things scattered nearby."));
     }
-    return 0;
+
+    return MonsterSpellResult::make_valid();
 }
 
 /*!
@@ -132,9 +133,8 @@ HIT_POINT spell_RF6_SPECIAL_ROLENTO(player_type *target_ptr, POSITION y, POSITIO
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
  */
-HIT_POINT spell_RF6_SPECIAL_B(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+static MonsterSpellResult spell_RF6_SPECIAL_B(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     floor_type *floor_ptr = target_ptr->current_floor_ptr;
     monster_type *m_ptr = &floor_ptr->m_list[m_idx];
@@ -153,10 +153,9 @@ HIT_POINT spell_RF6_SPECIAL_B(player_type *target_ptr, POSITION y, POSITION x, M
 
         teleport_away(target_ptr, m_idx, 10, TELEPORT_NONMAGICAL);
         target_ptr->update |= (PU_MONSTERS);
-        return 0;
+        return MonsterSpellResult::make_valid();
     }
 
-
     simple_monspell_message(target_ptr, m_idx, t_idx, _("%^sがあなたを掴んで空中から投げ落とした。", "%^s snatches you, soars into the sky, and drops you."),
         _("%^sが%sを掴んで空中から投げ落とした。", "%^s snatches %s, soars into the sky, and releases its grip."), TARGET_TYPE);
 
@@ -195,7 +194,8 @@ HIT_POINT spell_RF6_SPECIAL_B(player_type *target_ptr, POSITION y, POSITION x, M
 
     if (monster_to_monster)
         mon_take_hit_mon(target_ptr, t_idx, dam, &dead, &fear, extract_note_dies(real_r_idx(t_ptr)), m_idx);
-    return dam;
+
+    return MonsterSpellResult::make_valid();
 }
 
 /*!
@@ -206,9 +206,10 @@ HIT_POINT spell_RF6_SPECIAL_B(player_type *target_ptr, POSITION y, POSITION x, M
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * ラーニング不可。
  */
-HIT_POINT spell_RF6_SPECIAL(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_SPECIAL(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     floor_type *floor_ptr = target_ptr->current_floor_ptr;
     monster_type *m_ptr = &floor_ptr->m_list[m_idx];
@@ -217,7 +218,7 @@ HIT_POINT spell_RF6_SPECIAL(player_type *target_ptr, POSITION y, POSITION x, MON
     disturb(target_ptr, TRUE, TRUE);
     switch (m_ptr->r_idx) {
     case MON_OHMU:
-        return -1;
+        return MonsterSpellResult::make_invalid();
 
     case MON_BANORLUPART:
     case MON_BANOR:
@@ -232,9 +233,8 @@ HIT_POINT spell_RF6_SPECIAL(player_type *target_ptr, POSITION y, POSITION x, MON
         if (r_ptr->d_char == 'B') {
             return spell_RF6_SPECIAL_B(target_ptr, y, x, m_idx, t_idx, TARGET_TYPE);
             break;
+        } else {
+            return MonsterSpellResult::make_invalid();
         }
-
-        else
-            return -1;
     }
 }
index a771db6..fd618be 100644 (file)
@@ -2,4 +2,6 @@
 
 #include "system/angband.h"
 
-HIT_POINT spell_RF6_SPECIAL(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+struct MonsterSpellResult;
+
+MonsterSpellResult spell_RF6_SPECIAL(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
index d063214..a7a42c4 100644 (file)
@@ -9,6 +9,7 @@
 #include "blue-magic/blue-magic-checker.h"
 #include "core/disturbance.h"
 #include "core/player-redraw-types.h"
+#include "effect/effect-processor.h"
 #include "mind/drs-types.h"
 #include "monster-race/monster-race.h"
 #include "monster-race/race-flags1.h"
@@ -23,6 +24,7 @@
 #include "mspell/mspell-checker.h"
 #include "mspell/mspell-damage-calculator.h"
 #include "mspell/mspell-util.h"
+#include "mspell/mspell.h"
 #include "player/player-personalities-types.h"
 #include "player/player-status-flags.h"
 #include "spell/spell-types.h"
@@ -104,11 +106,11 @@ void spell_badstatus_message(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_DRAIN_MANA(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_DRAIN_MANA(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
-    HIT_POINT dam;
     GAME_TEXT m_name[MAX_NLEN], t_name[MAX_NLEN];
     monster_name(target_ptr, m_idx, m_name);
     monster_name(target_ptr, t_idx, t_name);
@@ -120,12 +122,15 @@ HIT_POINT spell_RF5_DRAIN_MANA(player_type *target_ptr, POSITION y, POSITION x,
         msg_format(_("%^sは精神エネルギーを%sから吸いとった。", "%^s draws psychic energy from %s."), m_name, t_name);
     }
 
-    dam = monspell_damage(target_ptr, (MS_DRAIN_MANA), m_idx, DAM_ROLL);
-    breath(target_ptr, y, x, m_idx, GF_DRAIN_MANA, dam, 0, FALSE, MS_DRAIN_MANA, TARGET_TYPE);
+    const auto dam = monspell_damage(target_ptr, (MS_DRAIN_MANA), m_idx, DAM_ROLL);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_DRAIN_MANA, dam, 0, FALSE, MS_DRAIN_MANA, TARGET_TYPE);
     if (TARGET_TYPE == MONSTER_TO_PLAYER)
         update_smart_learn(target_ptr, m_idx, DRS_MANA);
 
-    return dam;
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -136,13 +141,13 @@ HIT_POINT spell_RF5_DRAIN_MANA(player_type *target_ptr, POSITION y, POSITION x,
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_MIND_BLAST(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_MIND_BLAST(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monster_type *m_ptr = &target_ptr->current_floor_ptr->m_list[m_idx];
     bool seen = (!target_ptr->blind && m_ptr->ml);
-    HIT_POINT dam;
     GAME_TEXT m_name[MAX_NLEN], t_name[MAX_NLEN];
     monster_name(target_ptr, m_idx, m_name);
     monster_name(target_ptr, t_idx, t_name);
@@ -157,9 +162,13 @@ HIT_POINT spell_RF5_MIND_BLAST(player_type *target_ptr, POSITION y, POSITION x,
         msg_format(_("%^sは%sをじっと睨んだ。", "%^s gazes intently at %s."), m_name, t_name);
     }
 
-    dam = monspell_damage(target_ptr, (MS_MIND_BLAST), m_idx, DAM_ROLL);
-    breath(target_ptr, y, x, m_idx, GF_MIND_BLAST, dam, 0, FALSE, MS_MIND_BLAST, TARGET_TYPE);
-    return dam;
+    const auto dam = monspell_damage(target_ptr, (MS_MIND_BLAST), m_idx, DAM_ROLL);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_MIND_BLAST, dam, 0, FALSE, MS_MIND_BLAST, TARGET_TYPE);
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -170,13 +179,13 @@ HIT_POINT spell_RF5_MIND_BLAST(player_type *target_ptr, POSITION y, POSITION x,
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return ダメージ量を返す。
+ *
+ * プレイヤーに当たったらラーニング可。
  */
-HIT_POINT spell_RF5_BRAIN_SMASH(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BRAIN_SMASH(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monster_type *m_ptr = &target_ptr->current_floor_ptr->m_list[m_idx];
     bool seen = (!target_ptr->blind && m_ptr->ml);
-    HIT_POINT dam;
     GAME_TEXT m_name[MAX_NLEN], t_name[MAX_NLEN];
     monster_name(target_ptr, m_idx, m_name);
     monster_name(target_ptr, t_idx, t_name);
@@ -191,9 +200,13 @@ HIT_POINT spell_RF5_BRAIN_SMASH(player_type *target_ptr, POSITION y, POSITION x,
         msg_format(_("%^sは%sをじっと睨んだ。", "%^s gazes intently at %s."), m_name, t_name);
     }
 
-    dam = monspell_damage(target_ptr, (MS_BRAIN_SMASH), m_idx, DAM_ROLL);
-    breath(target_ptr, y, x, m_idx, GF_BRAIN_SMASH, dam, 0, FALSE, MS_BRAIN_SMASH, TARGET_TYPE);
-    return dam;
+    const auto dam = monspell_damage(target_ptr, (MS_BRAIN_SMASH), m_idx, DAM_ROLL);
+    const auto proj_res = breath(target_ptr, y, x, m_idx, GF_BRAIN_SMASH, dam, 0, FALSE, MS_BRAIN_SMASH, TARGET_TYPE);
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = proj_res.affected_player;
+
+    return res;
 }
 
 /*!
@@ -201,9 +214,14 @@ HIT_POINT spell_RF5_BRAIN_SMASH(player_type *target_ptr, POSITION y, POSITION x,
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF5_SCARE(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_SCARE(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
     floor_type *floor_ptr = target_ptr->current_floor_ptr;
     monster_type *t_ptr = &floor_ptr->m_list[t_idx];
     monster_race *tr_ptr = &r_info[t_ptr->r_idx];
@@ -222,11 +240,11 @@ void spell_RF5_SCARE(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_i
         }
 
         update_smart_learn(target_ptr, m_idx, DRS_FEAR);
-        return;
+        return res;
     }
 
     if (TARGET_TYPE != MONSTER_TO_MONSTER)
-        return;
+        return res;
 
     resist = ((tr_ptr->flags3 & RF3_NO_FEAR) != 0);
     saving_throw = (tr_ptr->level > randint1((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10);
@@ -238,6 +256,8 @@ void spell_RF5_SCARE(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_i
     if (!resist && !saving_throw) {
         set_monster_monfear(target_ptr, t_idx, monster_fear_remaining(t_ptr) + randint0(4) + 4);
     }
+
+    return res;
 }
 
 /*!
@@ -245,9 +265,14 @@ void spell_RF5_SCARE(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_i
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF5_BLIND(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_BLIND(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
     floor_type *floor_ptr = target_ptr->current_floor_ptr;
     monster_type *t_ptr = &floor_ptr->m_list[t_idx];
     monster_race *tr_ptr = &r_info[t_ptr->r_idx];
@@ -266,11 +291,11 @@ void spell_RF5_BLIND(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_i
         }
 
         update_smart_learn(target_ptr, m_idx, DRS_BLIND);
-        return;
+        return res;
     }
 
     if (TARGET_TYPE != MONSTER_TO_MONSTER)
-        return;
+        return res;
 
     concptr msg1;
     GAME_TEXT t_name[MAX_NLEN];
@@ -291,6 +316,8 @@ void spell_RF5_BLIND(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_i
     if (!resist && !saving_throw) {
         (void)set_monster_confused(target_ptr, t_idx, monster_confused_remaining(t_ptr) + 12 + randint0(4));
     }
+
+    return res;
 }
 
 /*!
@@ -298,9 +325,14 @@ void spell_RF5_BLIND(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_i
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF5_CONF(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_CONF(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
     floor_type *floor_ptr = target_ptr->current_floor_ptr;
     monster_type *t_ptr = &floor_ptr->m_list[t_idx];
     monster_race *tr_ptr = &r_info[t_ptr->r_idx];
@@ -320,11 +352,11 @@ void spell_RF5_CONF(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_id
         }
 
         update_smart_learn(target_ptr, m_idx, DRS_CONF);
-        return;
+        return res;
     }
 
     if (TARGET_TYPE != MONSTER_TO_MONSTER)
-        return;
+        return res;
 
     resist = ((tr_ptr->flags3 & RF3_NO_CONF) != 0);
     saving_throw = (tr_ptr->level > randint1((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10);
@@ -336,6 +368,8 @@ void spell_RF5_CONF(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_id
     if (!resist && !saving_throw) {
         (void)set_monster_confused(target_ptr, t_idx, monster_confused_remaining(t_ptr) + 12 + randint0(4));
     }
+
+    return res;
 }
 
 /*!
@@ -343,9 +377,14 @@ void spell_RF5_CONF(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_id
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF5_HOLD(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_HOLD(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
     floor_type *floor_ptr = target_ptr->current_floor_ptr;
     monster_type *t_ptr = &floor_ptr->m_list[t_idx];
     monster_race *tr_ptr = &r_info[t_ptr->r_idx];
@@ -364,11 +403,11 @@ void spell_RF5_HOLD(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_id
         }
 
         update_smart_learn(target_ptr, m_idx, DRS_FREE);
-        return;
+        return res;
     }
 
     if (TARGET_TYPE != MONSTER_TO_MONSTER)
-        return;
+        return res;
 
     resist = ((tr_ptr->flags1 & RF1_UNIQUE) != 0 || (tr_ptr->flags3 & RF3_NO_STUN) != 0);
     saving_throw = (tr_ptr->level > randint1((rlev - 10) < 1 ? 1 : (rlev - 10)) + 10);
@@ -380,6 +419,8 @@ void spell_RF5_HOLD(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_id
     if (!resist && !saving_throw) {
         (void)set_monster_stunned(target_ptr, t_idx, monster_stunned_remaining(t_ptr) + randint1(4) + 4);
     }
+
+    return res;
 }
 
 /*!
@@ -388,8 +429,10 @@ void spell_RF5_HOLD(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_id
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
+ *
+ * ラーニング不可。
  */
-void spell_RF6_HASTE(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_HASTE(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     bool see_m = see_monster(target_ptr, m_idx);
     monster_type *m_ptr = &target_ptr->current_floor_ptr->m_list[m_idx];
@@ -407,6 +450,8 @@ void spell_RF6_HASTE(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_i
         if (TARGET_TYPE == MONSTER_TO_PLAYER || (TARGET_TYPE == MONSTER_TO_MONSTER && see_m))
             msg_format(_("%^sの動きが速くなった。", "%^s starts moving faster."), m_name);
     }
+
+    return MonsterSpellResult::make_valid();
 }
 
 /*!
@@ -414,9 +459,14 @@ void spell_RF6_HASTE(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_i
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF5_SLOW(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF5_SLOW(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
     floor_type *floor_ptr = target_ptr->current_floor_ptr;
     monster_type *t_ptr = &floor_ptr->m_list[t_idx];
     monster_race *tr_ptr = &r_info[t_ptr->r_idx];
@@ -435,11 +485,11 @@ void spell_RF5_SLOW(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_id
         }
 
         update_smart_learn(target_ptr, m_idx, DRS_FREE);
-        return;
+        return res;
     }
 
     if (TARGET_TYPE != MONSTER_TO_MONSTER)
-        return;
+        return res;
 
     concptr msg1;
     GAME_TEXT t_name[MAX_NLEN];
@@ -460,6 +510,8 @@ void spell_RF5_SLOW(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_id
     if (!resist && !saving_throw) {
         set_monster_slow(target_ptr, t_idx, monster_slow_remaining(t_ptr) + 50);
     }
+
+    return res;
 }
 
 /*!
@@ -468,9 +520,13 @@ void spell_RF5_SLOW(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_id
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
+ *
+ * ラーニング不可。
  */
-void spell_RF6_HEAL(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_HEAL(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
+    const auto res = MonsterSpellResult::make_valid();
+
     floor_type *floor_ptr = target_ptr->current_floor_ptr;
     monster_type *m_ptr = &floor_ptr->m_list[m_idx];
     DEPTH rlev = monster_level_idx(floor_ptr, m_idx);
@@ -507,12 +563,14 @@ void spell_RF6_HEAL(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_id
         target_ptr->redraw |= (PR_UHEALTH);
 
     if (!monster_fear_remaining(m_ptr))
-        return;
+        return res;
 
     (void)set_monster_monfear(target_ptr, m_idx, 0);
 
     if (see_monster(target_ptr, m_idx))
         msg_format(_("%^sは勇気を取り戻した。", format("%%^s recovers %s courage.", m_poss)), m_name);
+
+    return res;
 }
 
 /*!
@@ -521,8 +579,10 @@ void spell_RF6_HEAL(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_id
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param TARGET_TYPE プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
+ *
+ * ラーニング不可。
  */
-void spell_RF6_INVULNER(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_INVULNER(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monster_type *m_ptr = &target_ptr->current_floor_ptr->m_list[m_idx];
     bool seen = (!target_ptr->blind && m_ptr->ml);
@@ -540,15 +600,18 @@ void spell_RF6_INVULNER(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX
 
     if (!monster_invulner_remaining(m_ptr))
         (void)set_monster_invulner(target_ptr, m_idx, randint1(4) + 4, FALSE);
+
+    return MonsterSpellResult::make_valid();
 }
 
 /*!
  * @brief RF6_FORGETの処理。記憶消去。 /
  * @param target_ptr プレーヤーへの参照ポインタ
  * @param m_idx 呪文を唱えるモンスターID
- * @param なし
+ *
+ * ラーニング可。
  */
-void spell_RF6_FORGET(player_type *target_ptr, MONSTER_IDX m_idx)
+MonsterSpellResult spell_RF6_FORGET(player_type *target_ptr, MONSTER_IDX m_idx)
 {
     DEPTH rlev = monster_level_idx(target_ptr->current_floor_ptr, m_idx);
     GAME_TEXT m_name[MAX_NLEN];
@@ -563,4 +626,9 @@ void spell_RF6_FORGET(player_type *target_ptr, MONSTER_IDX m_idx)
     } else if (lose_all_info(target_ptr)) {
         msg_print(_("記憶が薄れてしまった。", "Your memories fade away."));
     }
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = true;
+
+    return res;
 }
index 771b0b1..c70a7ff 100644 (file)
@@ -2,16 +2,19 @@
 
 #include "system/angband.h"
 
-void spell_badstatus_message(player_type* target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, concptr msg1, concptr msg2, concptr msg3, concptr msg4, bool resist, bool saving_throw, int TARGET_TYPE);
-HIT_POINT spell_RF5_DRAIN_MANA(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_MIND_BLAST(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-HIT_POINT spell_RF5_BRAIN_SMASH(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF5_SCARE(MONSTER_IDX m_idx, player_type* target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF5_BLIND(MONSTER_IDX m_idx, player_type* target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF5_CONF(MONSTER_IDX m_idx, player_type* target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF5_HOLD(MONSTER_IDX m_idx, player_type* target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_HASTE(player_type* target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF5_SLOW(MONSTER_IDX m_idx, player_type* target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_HEAL(player_type* target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_INVULNER(player_type* target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_FORGET(player_type* target_ptr, MONSTER_IDX m_idx);
+struct MonsterSpellResult;
+
+void spell_badstatus_message(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, concptr msg1, concptr msg2, concptr msg3, concptr msg4, bool resist,
+    bool saving_throw, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_DRAIN_MANA(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_MIND_BLAST(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BRAIN_SMASH(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_SCARE(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_BLIND(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_CONF(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_HOLD(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_HASTE(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF5_SLOW(MONSTER_IDX m_idx, player_type *target_ptr, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_HEAL(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_INVULNER(player_type *target_ptr, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_FORGET(player_type *target_ptr, MONSTER_IDX m_idx);
index 12bb6b6..8ed4eee 100644 (file)
@@ -14,6 +14,7 @@
 #include "monster/monster-info.h"
 #include "monster/monster-status.h"
 #include "mspell/mspell-util.h"
+#include "mspell/mspell.h"
 #include "mspell/specified-summon.h"
 #include "spell-kind/spells-launcher.h"
 #include "spell/spell-types.h"
@@ -51,7 +52,7 @@ static BIT_FLAGS monster_u_mode(floor_type *floor_ptr, MONSTER_IDX m_idx)
  * @param m_idx 呪文を唱えるモンスターID
  * @return 召喚したモンスターの数を返す。
  */
-MONSTER_NUMBER summon_Kin(player_type *target_ptr, POSITION y, POSITION x, int rlev, MONSTER_IDX m_idx)
+static MONSTER_NUMBER summon_Kin(player_type *target_ptr, POSITION y, POSITION x, int rlev, MONSTER_IDX m_idx)
 {
     int count = 0;
     for (int k = 0; k < 4; k++) {
@@ -103,9 +104,10 @@ static void decide_summon_kin_caster(
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return 召喚したモンスターの数を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_S_KIN(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int target_type)
+MonsterSpellResult spell_RF6_S_KIN(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int target_type)
 {
     floor_type *floor_ptr = target_ptr->current_floor_ptr;
     monster_type *m_ptr = &floor_ptr->m_list[m_idx];
@@ -169,6 +171,11 @@ void spell_RF6_S_KIN(player_type *target_ptr, POSITION y, POSITION x, MONSTER_ID
 
     if (known && !see_monster(target_ptr, t_idx) && count && (target_type == MONSTER_TO_MONSTER))
         floor_ptr->monster_noise = TRUE;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = target_type == MONSTER_TO_PLAYER;
+
+    return res;
 }
 
 /*!
@@ -179,9 +186,10 @@ void spell_RF6_S_KIN(player_type *target_ptr, POSITION y, POSITION x, MONSTER_ID
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return 召喚したモンスターの数を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_S_CYBER(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_S_CYBER(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     int count = 0;
     floor_type *floor_ptr = target_ptr->current_floor_ptr;
@@ -205,6 +213,11 @@ void spell_RF6_S_CYBER(player_type *target_ptr, POSITION y, POSITION x, MONSTER_
 
     if (monster_near_player(floor_ptr, m_idx, t_idx) && !see_monster(target_ptr, t_idx) && count && mon_to_mon)
         floor_ptr->monster_noise = TRUE;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
+    return res;
 }
 
 /*!
@@ -215,9 +228,10 @@ void spell_RF6_S_CYBER(player_type *target_ptr, POSITION y, POSITION x, MONSTER_
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return 召喚したモンスターの数を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_S_MONSTER(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_S_MONSTER(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."), _("%^sが魔法で仲間を召喚した!", "%^s magically summons help!"),
         _("%^sが魔法で仲間を召喚した!", "%^s magically summons help!"), TARGET_TYPE);
@@ -240,6 +254,11 @@ void spell_RF6_S_MONSTER(player_type *target_ptr, POSITION y, POSITION x, MONSTE
 
     if (monster_near_player(floor_ptr, m_idx, t_idx) && !see_monster(target_ptr, t_idx) && count && mon_to_mon)
         floor_ptr->monster_noise = TRUE;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
+    return res;
 }
 
 /*!
@@ -250,9 +269,10 @@ void spell_RF6_S_MONSTER(player_type *target_ptr, POSITION y, POSITION x, MONSTE
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return 召喚したモンスターの数を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_S_MONSTERS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_S_MONSTERS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sが魔法でモンスターを召喚した!", "%^s magically summons monsters!"), _("%^sが魔法でモンスターを召喚した!", "%^s magically summons monsters!"),
@@ -276,6 +296,11 @@ void spell_RF6_S_MONSTERS(player_type *target_ptr, POSITION y, POSITION x, MONST
 
     if (monster_near_player(floor_ptr, m_idx, t_idx) && !see_monster(target_ptr, t_idx) && count && mon_to_mon)
         floor_ptr->monster_noise = TRUE;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
+    return res;
 }
 
 /*!
@@ -286,9 +311,10 @@ void spell_RF6_S_MONSTERS(player_type *target_ptr, POSITION y, POSITION x, MONST
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return 召喚したモンスターの数を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_S_ANT(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_S_ANT(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."), _("%^sが魔法でアリを召喚した。", "%^s magically summons ants."),
         _("%^sが魔法でアリを召喚した。", "%^s magically summons ants."), TARGET_TYPE);
@@ -307,6 +333,11 @@ void spell_RF6_S_ANT(player_type *target_ptr, POSITION y, POSITION x, MONSTER_ID
 
     if (monster_near_player(floor_ptr, m_idx, t_idx) && !see_monster(target_ptr, t_idx) && count && mon_to_mon)
         floor_ptr->monster_noise = TRUE;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
+    return res;
 }
 
 /*!
@@ -317,9 +348,10 @@ void spell_RF6_S_ANT(player_type *target_ptr, POSITION y, POSITION x, MONSTER_ID
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return 召喚したモンスターの数を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_S_SPIDER(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_S_SPIDER(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."), _("%^sが魔法でクモを召喚した。", "%^s magically summons spiders."),
         _("%^sが魔法でクモを召喚した。", "%^s magically summons spiders."), TARGET_TYPE);
@@ -338,6 +370,11 @@ void spell_RF6_S_SPIDER(player_type *target_ptr, POSITION y, POSITION x, MONSTER
 
     if (monster_near_player(floor_ptr, m_idx, t_idx) && !see_monster(target_ptr, t_idx) && count && mon_to_mon)
         floor_ptr->monster_noise = TRUE;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
+    return res;
 }
 
 /*!
@@ -348,9 +385,10 @@ void spell_RF6_S_SPIDER(player_type *target_ptr, POSITION y, POSITION x, MONSTER
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return 召喚したモンスターの数を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_S_HOUND(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_S_HOUND(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sが魔法でハウンドを召喚した。", "%^s magically summons hounds."), _("%^sが魔法でハウンドを召喚した。", "%^s magically summons hounds."),
@@ -370,6 +408,11 @@ void spell_RF6_S_HOUND(player_type *target_ptr, POSITION y, POSITION x, MONSTER_
 
     if (monster_near_player(floor_ptr, m_idx, t_idx) && !see_monster(target_ptr, t_idx) && count && mon_to_mon)
         floor_ptr->monster_noise = TRUE;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
+    return res;
 }
 
 /*!
@@ -380,9 +423,10 @@ void spell_RF6_S_HOUND(player_type *target_ptr, POSITION y, POSITION x, MONSTER_
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return 召喚したモンスターの数を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_S_HYDRA(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_S_HYDRA(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sが魔法でヒドラを召喚した。", "%^s magically summons hydras."), _("%^sが魔法でヒドラを召喚した。", "%^s magically summons hydras."), TARGET_TYPE);
@@ -401,6 +445,11 @@ void spell_RF6_S_HYDRA(player_type *target_ptr, POSITION y, POSITION x, MONSTER_
 
     if (monster_near_player(floor_ptr, m_idx, t_idx) && !see_monster(target_ptr, t_idx) && count && mon_to_mon)
         floor_ptr->monster_noise = TRUE;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
+    return res;
 }
 
 /*!
@@ -411,9 +460,10 @@ void spell_RF6_S_HYDRA(player_type *target_ptr, POSITION y, POSITION x, MONSTER_
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return 召喚したモンスターの数を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_S_ANGEL(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_S_ANGEL(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sが魔法で天使を召喚した!", "%^s magically summons an angel!"), _("%^sが魔法で天使を召喚した!", "%^s magically summons an angel!"), TARGET_TYPE);
@@ -443,6 +493,11 @@ void spell_RF6_S_ANGEL(player_type *target_ptr, POSITION y, POSITION x, MONSTER_
     bool mon_to_mon = (TARGET_TYPE == MONSTER_TO_MONSTER);
     if (monster_near_player(floor_ptr, m_idx, t_idx) && !see_monster(target_ptr, t_idx) && count && mon_to_mon)
         floor_ptr->monster_noise = TRUE;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
+    return res;
 }
 
 /*!
@@ -453,9 +508,10 @@ void spell_RF6_S_ANGEL(player_type *target_ptr, POSITION y, POSITION x, MONSTER_
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return 召喚したモンスターの数を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_S_DEMON(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_S_DEMON(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sは魔法で混沌の宮廷から悪魔を召喚した!", "%^s magically summons a demon from the Courts of Chaos!"),
@@ -474,6 +530,11 @@ void spell_RF6_S_DEMON(player_type *target_ptr, POSITION y, POSITION x, MONSTER_
     bool mon_to_mon = (TARGET_TYPE == MONSTER_TO_MONSTER);
     if (monster_near_player(floor_ptr, m_idx, t_idx) && !see_monster(target_ptr, t_idx) && count && mon_to_mon)
         floor_ptr->monster_noise = TRUE;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
+    return res;
 }
 
 /*!
@@ -484,9 +545,10 @@ void spell_RF6_S_DEMON(player_type *target_ptr, POSITION y, POSITION x, MONSTER_
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return 召喚したモンスターの数を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_S_UNDEAD(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_S_UNDEAD(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sが魔法でアンデッドの強敵を召喚した!", "%^s magically summons an undead adversary!"),
@@ -505,6 +567,11 @@ void spell_RF6_S_UNDEAD(player_type *target_ptr, POSITION y, POSITION x, MONSTER
     bool mon_to_mon = (TARGET_TYPE == MONSTER_TO_MONSTER);
     if (monster_near_player(floor_ptr, m_idx, t_idx) && !see_monster(target_ptr, t_idx) && count && mon_to_mon)
         floor_ptr->monster_noise = TRUE;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
+    return res;
 }
 
 /*!
@@ -515,9 +582,10 @@ void spell_RF6_S_UNDEAD(player_type *target_ptr, POSITION y, POSITION x, MONSTER
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return 召喚したモンスターの数を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_S_DRAGON(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_S_DRAGON(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sが魔法でドラゴンを召喚した!", "%^s magically summons a dragon!"), _("%^sが魔法でドラゴンを召喚した!", "%^s magically summons a dragon!"),
@@ -536,6 +604,11 @@ void spell_RF6_S_DRAGON(player_type *target_ptr, POSITION y, POSITION x, MONSTER
     bool mon_to_mon = (TARGET_TYPE == MONSTER_TO_MONSTER);
     if (monster_near_player(floor_ptr, m_idx, t_idx) && !see_monster(target_ptr, t_idx) && count && mon_to_mon)
         floor_ptr->monster_noise = TRUE;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
+    return res;
 }
 
 /*!
@@ -546,9 +619,10 @@ void spell_RF6_S_DRAGON(player_type *target_ptr, POSITION y, POSITION x, MONSTER
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return 召喚したモンスターの数を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_S_HI_UNDEAD(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_S_HI_UNDEAD(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     GAME_TEXT m_name[MAX_NLEN];
     monster_name(target_ptr, m_idx, m_name);
@@ -584,6 +658,11 @@ void spell_RF6_S_HI_UNDEAD(player_type *target_ptr, POSITION y, POSITION x, MONS
 
     if (monster_near_player(floor_ptr, m_idx, t_idx) && !see_monster(target_ptr, t_idx) && count && mon_to_mon)
         floor_ptr->monster_noise = TRUE;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
+    return res;
 }
 
 /*!
@@ -594,9 +673,10 @@ void spell_RF6_S_HI_UNDEAD(player_type *target_ptr, POSITION y, POSITION x, MONS
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return 召喚したモンスターの数を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_S_HI_DRAGON(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_S_HI_DRAGON(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sが魔法で古代ドラゴンを召喚した!", "%^s magically summons ancient dragons!"),
@@ -621,6 +701,11 @@ void spell_RF6_S_HI_DRAGON(player_type *target_ptr, POSITION y, POSITION x, MONS
 
     if (monster_near_player(floor_ptr, m_idx, t_idx) && !see_monster(target_ptr, t_idx) && count && mon_to_mon)
         floor_ptr->monster_noise = TRUE;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
+    return res;
 }
 
 /*!
@@ -631,9 +716,10 @@ void spell_RF6_S_HI_DRAGON(player_type *target_ptr, POSITION y, POSITION x, MONS
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return 召喚したモンスターの数を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_S_AMBERITES(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_S_AMBERITES(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sがアンバーの王族を召喚した!", "%^s magically summons Lords of Amber!"),
@@ -654,6 +740,11 @@ void spell_RF6_S_AMBERITES(player_type *target_ptr, POSITION y, POSITION x, MONS
 
     if (monster_near_player(floor_ptr, m_idx, t_idx) && !see_monster(target_ptr, t_idx) && count && mon_to_mon)
         floor_ptr->monster_noise = TRUE;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
+    return res;
 }
 
 /*!
@@ -664,9 +755,10 @@ void spell_RF6_S_AMBERITES(player_type *target_ptr, POSITION y, POSITION x, MONS
  * @param m_idx 呪文を唱えるモンスターID
  * @param t_idx 呪文を受けるモンスターID。プレイヤーの場合はdummyで0とする。
  * @param target_type プレイヤーを対象とする場合MONSTER_TO_PLAYER、モンスターを対象とする場合MONSTER_TO_MONSTER
- * @return 召喚したモンスターの数を返す。
+ *
+ * プレイヤーが対象ならラーニング可。
  */
-void spell_RF6_S_UNIQUE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
+MonsterSpellResult spell_RF6_S_UNIQUE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE)
 {
     monspell_message(target_ptr, m_idx, t_idx, _("%^sが何かをつぶやいた。", "%^s mumbles."),
         _("%^sが魔法で特別な強敵を召喚した!", "%^s magically summons special opponents!"),
@@ -703,4 +795,9 @@ void spell_RF6_S_UNIQUE(player_type *target_ptr, POSITION y, POSITION x, MONSTER
 
     if (monster_near_player(floor_ptr, m_idx, t_idx) && !see_monster(target_ptr, t_idx) && count && mon_to_mon)
         floor_ptr->monster_noise = TRUE;
+
+    auto res = MonsterSpellResult::make_valid();
+    res.learnable = TARGET_TYPE == MONSTER_TO_PLAYER;
+
+    return res;
 }
index 54d5e72..d92a594 100644 (file)
@@ -1,21 +1,22 @@
-#pragma once
+#pragma once
 
 #include "system/angband.h"
 
-MONSTER_NUMBER summon_Kin(player_type* target_ptr, POSITION y, POSITION x, int rlev, MONSTER_IDX m_idx);
-void spell_RF6_S_KIN(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_S_CYBER(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_S_MONSTER(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_S_MONSTERS(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_S_ANT(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_S_SPIDER(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_S_HOUND(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_S_HYDRA(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_S_ANGEL(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_S_DEMON(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_S_UNDEAD(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_S_DRAGON(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_S_HI_UNDEAD(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_S_HI_DRAGON(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_S_AMBERITES(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
-void spell_RF6_S_UNIQUE(player_type* target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+struct MonsterSpellResult;
+
+MonsterSpellResult spell_RF6_S_KIN(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_S_CYBER(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_S_MONSTER(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_S_MONSTERS(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_S_ANT(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_S_SPIDER(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_S_HOUND(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_S_HYDRA(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_S_ANGEL(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_S_DEMON(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_S_UNDEAD(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_S_DRAGON(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_S_HI_UNDEAD(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_S_HI_DRAGON(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_S_AMBERITES(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
+MonsterSpellResult spell_RF6_S_UNIQUE(player_type *target_ptr, POSITION y, POSITION x, MONSTER_IDX m_idx, MONSTER_IDX t_idx, int TARGET_TYPE);
diff --git a/src/mspell/mspell.h b/src/mspell/mspell.h
new file mode 100644 (file)
index 0000000..cc6b835
--- /dev/null
@@ -0,0 +1,26 @@
+#pragma once
+
+//! モンスターが魔法を使った際の結果。
+struct MonsterSpellResult {
+private:
+    explicit MonsterSpellResult(const bool valid)
+        : valid(valid)
+    {
+    }
+
+public:
+    bool valid; //!< 通常は true。何か変なこと(無効な魔法IDなど)が起こったら false
+    bool learnable{ false }; //!< ラーニングを試みるか
+
+    MonsterSpellResult() = delete;
+
+    static MonsterSpellResult make_valid()
+    {
+        return MonsterSpellResult(true);
+    }
+
+    static MonsterSpellResult make_invalid()
+    {
+        return MonsterSpellResult(false);
+    }
+};
index ef9cd4e..79c6db7 100644 (file)
 #include "system/floor-type-definition.h"
 #include "view/display-messages.h"
 
-bool switch_activation(player_type *user_ptr, object_type *o_ptr, const activation_type *const act_ptr, concptr name)
+bool switch_activation(player_type *user_ptr, object_type **o_ptr_ptr, const activation_type *const act_ptr, concptr name)
 {
+    object_type *o_ptr = (*o_ptr_ptr);
+
     switch (act_ptr->index) {
     case ACT_SUNLIGHT:
         return activate_sunlight(user_ptr);
@@ -219,7 +221,7 @@ bool switch_activation(player_type *user_ptr, object_type *o_ptr, const activati
         true_healing(user_ptr, 0);
         return TRUE;
     case ACT_CURE_MANA_FULL:
-        msg_format(_("%sが青白く光った...", "The %s glows pale..."), name);
+        msg_format(_("%sが青白く光った...", "The %s glows palely..."), name);
         restore_mana(user_ptr, TRUE);
         return TRUE;
     case ACT_ESP:
@@ -338,7 +340,7 @@ bool switch_activation(player_type *user_ptr, object_type *o_ptr, const activati
     case ACT_ULTIMATE_RESIST:
         return activate_ultimate_resistance(user_ptr);
     case ACT_CAST_OFF:
-        (void)cosmic_cast_off(user_ptr, o_ptr);
+        (void)cosmic_cast_off(user_ptr, o_ptr_ptr);
         return TRUE;
     case ACT_FALLING_STAR:
         return activate_toragoroshi(user_ptr);
@@ -366,4 +368,4 @@ bool switch_activation(player_type *user_ptr, object_type *o_ptr, const activati
         msg_format(_("Unknown activation effect: %d.", "Unknown activation effect: %d."), act_ptr->index);
         return FALSE;
     }
-}
\ No newline at end of file
+}
index b86a854..db743d8 100644 (file)
@@ -3,4 +3,4 @@
 #include "system/angband.h"
 
 typedef struct activation_type activation_type;
-bool switch_activation(player_type *user_ptr, object_type *o_ptr, const activation_type *const act_ptr, concptr name);
+bool switch_activation(player_type *user_ptr, object_type **o_ptr_ptr, const activation_type *const act_ptr, concptr name);
index 2643565..444806c 100644 (file)
@@ -684,5 +684,8 @@ void apply_magic_accessary(player_type *owner_ptr, object_type *o_ptr, DEPTH lev
 
         break;
     }
+
+    default:
+        break;
     }
 }
index e77707f..ec86949 100644 (file)
@@ -17,6 +17,7 @@
 #include "sv-definition/sv-armor-types.h"
 #include "sv-definition/sv-protector-types.h"
 #include "util/bit-flags-calculator.h"
+#include "view/display-messages.h"
 
 /*!
  * @brief 防具系オブジェクトに生成ランクごとの強化を与えるサブルーチン
@@ -159,7 +160,8 @@ void apply_magic_armor(player_type *owner_ptr, object_type *o_ptr, DEPTH level,
                         add_flag(o_ptr->art_flags, TR_NO_TELE);
                     break;
                 default:
-                    okay_flag = false;
+                    msg_print(_("エラー:適した呪い鎧エゴがみつかりませんでした.", "Error:Suitable cursed armor ego not found."));
+                    okay_flag = true;
                     break;
                 }
 
@@ -462,5 +464,8 @@ void apply_magic_armor(player_type *owner_ptr, object_type *o_ptr, DEPTH level,
 
         break;
     }
+
+    default:
+        break;
     }
 }
index 1b57e5a..d364799 100644 (file)
@@ -217,5 +217,8 @@ void apply_magic_others(player_type *owner_ptr, object_type *o_ptr, int power)
 
         break;
     }
+
+    default:
+        break;
     }
-}
\ No newline at end of file
+}
index 9da78e7..986afeb 100644 (file)
@@ -257,5 +257,8 @@ void apply_magic_weapon(player_type *owner_ptr, object_type *o_ptr, DEPTH level,
 
         break;
     }
+
+    default:
+        break;
     }
 }
index 622579b..b3b3ad7 100644 (file)
@@ -7,6 +7,7 @@
 #include "object-enchant/object-ego.h"
 #include "object-enchant/trg-types.h"
 #include "util/bit-flags-calculator.h"
+#include "util/probability-table.h"
 #include <vector>
 
 /*
@@ -21,12 +22,6 @@ char *e_text;
  */
 EGO_IDX max_e_idx;
 
-struct random_ego_weight
-{
-    EGO_IDX idx;
-    long weight;
-};
-
 /*!
  * @brief アイテムのエゴをレア度の重みに合わせてランダムに選択する
  * Choose random ego type
@@ -36,8 +31,7 @@ struct random_ego_weight
  */
 byte get_random_ego(byte slot, bool good)
 {
-    std::vector<random_ego_weight> list;
-    long total = 0L;
+    ProbabilityTable<EGO_IDX> prob_table;
     for (EGO_IDX i = 1; i < max_e_idx; i++) {
         ego_item_type *e_ptr = &e_info[i];
         if (e_ptr->slot != slot || e_ptr->rarity <= 0)
@@ -46,17 +40,12 @@ byte get_random_ego(byte slot, bool good)
         bool worthless = e_ptr->rating == 0 || e_ptr->gen_flags.has_any_of({ TRG::CURSED, TRG::HEAVY_CURSE, TRG::PERMA_CURSE });
 
         if (good != worthless) {
-            random_ego_weight ew = { i, (255 / e_ptr->rarity) };
-            list.push_back(ew);
-            total += ew.weight;
+            prob_table.entry_item(i, (255 / e_ptr->rarity));
         }
     }
 
-    int value = randint1(total);
-    for (random_ego_weight &ew : list) {
-        value -= ew.weight;
-        if (value <= 0L)
-            return ew.idx;
+    if (!prob_table.empty()) {
+        return prob_table.pick_one_at_random();
     }
 
     return (EGO_IDX)0;
index 0a8737c..9a60412 100644 (file)
@@ -2,24 +2,24 @@
 
 // clang-format off
 enum class TRG {
-       INSTA_ART         =  1, /* Item must be an artifact */
-    QUESTITEM         =  2, /* quest level item -KMW- */
-    XTRA_POWER        =  3, /* Extra power */
-    ONE_SUSTAIN       =  4, /* One sustain */
-    XTRA_RES_OR_POWER =  5, /* Extra resistance or power */
-    XTRA_H_RES        =  6, /* Extra high resistance */
-    XTRA_E_RES        =  7, /* Extra element resistance */
-    XTRA_L_RES        =  8, /* Extra lordly resistance */
-    XTRA_D_RES        =  9, /* Extra dragon resistance */
-    XTRA_RES          = 10, /* Extra resistance */
-    CURSED            = 11, /* Item is Cursed */
-    HEAVY_CURSE       = 12, /* Item is Heavily Cursed */
-    PERMA_CURSE       = 13, /* Item is Perma Cursed */
-    RANDOM_CURSE0     = 14, /* Item is Random Cursed */
-    RANDOM_CURSE1     = 15, /* Item is Random Cursed */
-    RANDOM_CURSE2     = 16, /* Item is Random Cursed */
-    XTRA_DICE         = 17, /* Extra dice */
-    POWERFUL          = 18, /* Item has good value even if Cursed */
+       INSTA_ART         =  0, /* Item must be an artifact */
+    QUESTITEM         =  1, /* quest level item -KMW- */
+    XTRA_POWER        =  2, /* Extra power */
+    ONE_SUSTAIN       =  3, /* One sustain */
+    XTRA_RES_OR_POWER =  4, /* Extra resistance or power */
+    XTRA_H_RES        =  5, /* Extra high resistance */
+    XTRA_E_RES        =  6, /* Extra element resistance */
+    XTRA_L_RES        =  7, /* Extra lordly resistance */
+    XTRA_D_RES        =  8, /* Extra dragon resistance */
+    XTRA_RES          =  9, /* Extra resistance */
+    CURSED            = 10, /* Item is Cursed */
+    HEAVY_CURSE       = 11, /* Item is Heavily Cursed */
+    PERMA_CURSE       = 12, /* Item is Perma Cursed */
+    RANDOM_CURSE0     = 13, /* Item is Random Cursed */
+    RANDOM_CURSE1     = 14, /* Item is Random Cursed */
+    RANDOM_CURSE2     = 15, /* Item is Random Cursed */
+    XTRA_DICE         = 16, /* Extra dice */
+    POWERFUL          = 17, /* Item has good value even if Cursed */
     MAX,
 };
 // clang-format on
index 6d0047e..f9a7bc2 100644 (file)
@@ -36,6 +36,9 @@ bool item_tester_hook_ammo(player_type *player_ptr, object_type *o_ptr)
     case TV_BOLT: {
         return TRUE;
     }
+
+    default:
+        break;
     }
 
     return FALSE;
index 683ef96..235fa32 100644 (file)
@@ -96,6 +96,9 @@ bool item_tester_hook_recharge(player_type *player_ptr, object_type *o_ptr)
  */
 bool item_tester_learn_spell(player_type *player_ptr, object_type *o_ptr)
 {
+    if (player_ptr->pclass == CLASS_ELEMENTALIST)
+        return FALSE;
+
     s32b choices = realm_choices2[player_ptr->pclass];
     if (player_ptr->pclass == CLASS_PRIEST) {
         if (is_good_realm(player_ptr->realm1)) {
index 2d81628..5bbd242 100644 (file)
@@ -25,7 +25,7 @@ bool object_is_bounty(player_type *player_ptr, object_type *o_ptr)
     if (vanilla_town)
         return FALSE;
 
-    if (player_ptr->today_mon > 0 && (streq(r_name + r_info[o_ptr->pval].name, r_name + r_info[today_mon].name)))
+    if (player_ptr->today_mon > 0 && (streq(r_name + r_info[o_ptr->pval].name, r_name + r_info[current_world_ptr->today_mon].name)))
         return TRUE;
 
     if (o_ptr->pval == MON_TSUCHINOKO)
index 70c999e..f2e2a7a 100644 (file)
@@ -27,6 +27,9 @@ bool item_tester_hook_orthodox_melee_weapons(player_type *player_ptr, object_typ
         if (o_ptr->sval != SV_POISON_NEEDLE)
             return TRUE;
     }
+
+    default:
+        break;
     }
 
     return FALSE;
index e830705..8d59320 100644 (file)
@@ -68,6 +68,9 @@ bool hates_acid(object_type *o_ptr)
        {
                return TRUE;
        }
+
+       default:
+               break;
        }
 
        return FALSE;
@@ -89,6 +92,9 @@ bool hates_elec(object_type *o_ptr)
        {
                return TRUE;
        }
+
+       default:
+            break;
        }
 
        return FALSE;
@@ -153,6 +159,9 @@ bool hates_fire(object_type *o_ptr)
        {
                return TRUE;
        }
+
+       default:
+               break;
        }
 
        return FALSE;
@@ -175,6 +184,9 @@ bool hates_cold(object_type *o_ptr)
        {
                return TRUE;
        }
+
+       default:
+               break;
        }
 
        return FALSE;
index f4375a8..e386cdf 100644 (file)
@@ -257,6 +257,9 @@ s16b wield_slot(player_type *owner_ptr, object_type *o_ptr)
     case TV_BOOTS: {
         return (INVEN_FEET);
     }
+
+    default:
+        break;
     }
 
     return -1;
index a674356..f01a523 100644 (file)
@@ -60,6 +60,9 @@ static PRICE object_value_base(object_type *o_ptr)
             return 1000L;
         else
             return ((r_info[o_ptr->pval].level) * 50L + 1000);
+
+    default:
+        break;
     }
 
     return (0L);
@@ -220,6 +223,9 @@ PRICE object_value_real(player_type *player_ptr, object_type *o_ptr)
         if (has_flag(flgs, TR_SPEED))
             value += (o_ptr->pval * 10000L);
         break;
+
+    default:
+        break;
     }
 
     switch (o_ptr->tval) {
@@ -310,6 +316,9 @@ PRICE object_value_real(player_type *player_ptr, object_type *o_ptr)
             value = 0L;
         break;
     }
+
+    default:
+        break;
     }
 
     if (value < 0)
index 2771ee7..1652ddb 100644 (file)
@@ -319,6 +319,9 @@ static int blow_damcalc(monster_type *m_ptr, player_type *target_ptr, monster_bl
         dam = 0;
         check_wraith_form = FALSE;
         break;
+
+    default:
+        break;
     }
 
     if (check_wraith_form && target_ptr->wraith_form) {
index 7a5dd14..fc91036 100644 (file)
@@ -676,6 +676,9 @@ bool screen_object(player_type *player_ptr, object_type *o_ptr, BIT_FLAGS mode)
             }
 
             break;
+
+        default:
+            break;
         }
     }
 
index 785ea4b..7f92248 100644 (file)
@@ -82,6 +82,9 @@ static void sense_inventory_aux(player_type *creature_ptr, INVENTORY_IDX slot, b
             feel = FEEL_TERRIBLE;
             break;
         }
+
+        default:
+            break;
         }
     }
 
@@ -155,7 +158,8 @@ void sense_inventory1(player_type *creature_ptr)
     case CLASS_MAGE:
     case CLASS_HIGH_MAGE:
     case CLASS_SORCERER:
-    case CLASS_MAGIC_EATER: {
+    case CLASS_MAGIC_EATER:
+    case CLASS_ELEMENTALIST: {
         if (0 != randint0(240000L / (plev + 5)))
             return;
 
@@ -238,6 +242,9 @@ void sense_inventory1(player_type *creature_ptr)
         heavy = TRUE;
         break;
     }
+
+    case MAX_CLASS:
+        break;
     }
 
     if (compare_virtue(creature_ptr, V_KNOWLEDGE, 100, VIRTUE_LARGE))
@@ -273,6 +280,9 @@ void sense_inventory1(player_type *creature_ptr)
             okay = TRUE;
             break;
         }
+
+        default:
+            break;
         }
 
         if (!okay)
@@ -344,7 +354,8 @@ void sense_inventory2(player_type *creature_ptr)
     case CLASS_SORCERER:
     case CLASS_MAGIC_EATER:
     case CLASS_MIRROR_MASTER:
-    case CLASS_BLUE_MAGE: {
+    case CLASS_BLUE_MAGE:
+    case CLASS_ELEMENTALIST: {
         if (0 != randint0(9000L / (plev * plev + 40)))
             return;
 
@@ -356,6 +367,9 @@ void sense_inventory2(player_type *creature_ptr)
 
         break;
     }
+
+    case MAX_CLASS:
+        break;
     }
 
     for (INVENTORY_IDX i = 0; i < INVEN_TOTAL; i++) {
@@ -372,6 +386,9 @@ void sense_inventory2(player_type *creature_ptr)
             okay = TRUE;
             break;
         }
+
+        default:
+            break;
         }
 
         if (!okay)
index 3493ad4..3f342be 100644 (file)
@@ -325,6 +325,11 @@ void get_virtues(player_type *creature_ptr)
         creature_ptr->vir_types[i++] = V_FAITH;
         creature_ptr->vir_types[i++] = V_UNLIFE;
         break;
+    case CLASS_ELEMENTALIST:
+        creature_ptr->vir_types[i++] = V_NATURE;
+        break;
+    case MAX_CLASS:
+        break;
     };
 
     /* Get one virtue based on race */
@@ -337,6 +342,7 @@ void get_virtues(player_type *creature_ptr)
     case RACE_ELF:
     case RACE_SPRITE:
     case RACE_ENT:
+    case RACE_MERFOLK:
         creature_ptr->vir_types[i++] = V_NATURE;
         break;
     case RACE_HOBBIT:
@@ -400,6 +406,8 @@ void get_virtues(player_type *creature_ptr)
     case RACE_BEASTMAN:
         creature_ptr->vir_types[i++] = V_CHANCE;
         break;
+    case MAX_RACES:
+        break;
     }
 
     /* Get a virtue for realms */
index dc3cad3..5ca313f 100644 (file)
@@ -85,6 +85,7 @@ void set_class_ability_info(player_type *creature_ptr, self_info_type *self_ptr)
         break;
     case CLASS_MINDCRAFTER:
     case CLASS_FORCETRAINER:
+    case CLASS_ELEMENTALIST:
         if (creature_ptr->lev > 14)
             self_ptr->info[self_ptr->line++]
                 = _("あなたは精神を集中してMPを回復させることができる。(消費なし)", "You can concentrate to regenerate your mana (cost 0).");
@@ -164,5 +165,11 @@ void set_class_ability_info(player_type *creature_ptr, self_info_type *self_ptr)
             self_ptr->info[self_ptr->line++] = _("あなたは素早く移動することができる。(消費なし)", "You can walk extremely fast (cost 0).");
 
         break;
+
+    case CLASS_ARCHER:
+    case CLASS_BARD:
+    case CLASS_SNIPER:
+    case MAX_CLASS:
+        break;
     }
 }
index 3518852..789d763 100644 (file)
  * @details
  * * 各要素によるステータス修正値の合計
  */
-s16b PlayerBasicStatistics::ModificationValue() 
+s16b PlayerBasicStatistics::modification_value() 
 {
-    return PlayerStatusBase::getValue();
+    return PlayerStatusBase::get_value();
 }
 
 /*!
  * @brief 基礎ステータスの実値
  * @return status_typeに対応するステータスの実値を返す
  */
-s16b PlayerBasicStatistics::getValue()
+s16b PlayerBasicStatistics::get_value()
 {
     this->set_locals();
     return this->owner_ptr->stat_index[(int)this->status_type];
@@ -83,7 +83,7 @@ s16b PlayerBasicStatistics::personality_value()
  * @details
  * * owner_ptrのステータスを更新する
  */
-void PlayerBasicStatistics::updateValue()
+void PlayerBasicStatistics::update_value()
 {
     this->set_locals();
     this->update_top_status();
index 9c5e603..4885faf 100644 (file)
@@ -6,9 +6,9 @@ public:
     using PlayerStatusBase::PlayerStatusBase;
     PlayerBasicStatistics() = delete;
 
-    void updateValue();
-    s16b ModificationValue();
-    s16b getValue() override;
+    void update_value();
+    s16b modification_value();
+    s16b get_value() override;
 
 protected:
     base_status_type status_type;
index 4f4f4a5..c385851 100644 (file)
@@ -84,9 +84,9 @@ s16b PlayerCharisma::set_exception_value(s16b value)
     return result;
 }
 
-BIT_FLAGS PlayerCharisma::getAllFlags()
+BIT_FLAGS PlayerCharisma::get_all_flags()
 {
-    BIT_FLAGS flags = PlayerStatusBase::getAllFlags();
+    BIT_FLAGS flags = PlayerStatusBase::get_all_flags();
 
     if (any_bits(this->owner_ptr->muta3, MUT3_ILL_NORM)) {
         set_bits(flags, FLAG_CAUSE_MUTATION);
@@ -95,9 +95,9 @@ BIT_FLAGS PlayerCharisma::getAllFlags()
     return flags;
 }
 
-BIT_FLAGS PlayerCharisma::getBadFlags()
+BIT_FLAGS PlayerCharisma::get_bad_flags()
 {
-    BIT_FLAGS flags = PlayerStatusBase::getBadFlags();
+    BIT_FLAGS flags = PlayerStatusBase::get_bad_flags();
 
     if (any_bits(this->owner_ptr->muta3, MUT3_ILL_NORM)) {
         set_bits(flags, FLAG_CAUSE_MUTATION);
index f10c98d..5f8e573 100644 (file)
@@ -6,8 +6,8 @@ public:
     using PlayerBasicStatistics::PlayerBasicStatistics;
     PlayerCharisma() = delete;
 
-    BIT_FLAGS getAllFlags() override;
-    BIT_FLAGS getBadFlags() override;
+    BIT_FLAGS get_all_flags() override;
+    BIT_FLAGS get_bad_flags() override;
 
 protected:
     void set_locals() override;
index 4d09314..27c35b6 100644 (file)
@@ -369,6 +369,7 @@ s16b PlayerSpeed::set_exception_value(s16b value)
         value = this->default_value;
         value += this->riding_value();
         value += this->inventory_weight_value();
+        value += this->action_value();
     }
     return value;
 }
index bb5554f..47e7a88 100644 (file)
@@ -23,7 +23,7 @@ PlayerStatusBase::PlayerStatusBase(player_type *owner_ptr)
  * * 派生クラスからset_locals()をコールして初期値、上限、下限をセット。
  * * 各要素毎に計算した値を初期値に単純に加算し、上限と下限で丸める。
  */
-s16b PlayerStatusBase::getValue()
+s16b PlayerStatusBase::get_value()
 {
     this->set_locals(); /* 計算前に値のセット。派生クラスの値がセットされる。*/
     s16b pow = this->default_value;
@@ -55,7 +55,7 @@ s16b PlayerStatusBase::getValue()
  * @brief 修正値が0でないところにビットを立てて返す。
  * @return 判定結果のBIT_FLAGS
  */
-BIT_FLAGS PlayerStatusBase::getAllFlags()
+BIT_FLAGS PlayerStatusBase::get_all_flags()
 {
     this->set_locals(); /* 計算前に値のセット。派生クラスの値がセットされる。*/
     BIT_FLAGS result = equipments_flags(this->tr_flag);
@@ -94,7 +94,7 @@ BIT_FLAGS PlayerStatusBase::getAllFlags()
  * @brief 修正値が1以上のところにビットを立てて返す。
  * @return 判定結果のBIT_FLAGS
  */
-BIT_FLAGS PlayerStatusBase::getGoodFlags()
+BIT_FLAGS PlayerStatusBase::get_good_flags()
 {
     this->set_locals(); /* 計算前に値のセット。派生クラスの値がセットされる。*/
     BIT_FLAGS result = equipments_flags(this->tr_flag);
@@ -133,7 +133,7 @@ BIT_FLAGS PlayerStatusBase::getGoodFlags()
  * @brief 修正値が-1以下のところにビットを立てて返す。
  * @return 判定結果のBIT_FLAGS
  */
-BIT_FLAGS PlayerStatusBase::getBadFlags()
+BIT_FLAGS PlayerStatusBase::get_bad_flags()
 {
     this->set_locals(); /* 計算前に値のセット。派生クラスの値がセットされる。*/
     BIT_FLAGS result = equipments_bad_flags(this->tr_bad_flag);
index 4b47a17..3776143 100644 (file)
@@ -7,10 +7,10 @@ public:
     PlayerStatusBase(player_type *owner_ptr);
     PlayerStatusBase() = delete;
     virtual ~PlayerStatusBase() = default;
-    virtual s16b getValue();
-    virtual BIT_FLAGS getAllFlags();
-    virtual BIT_FLAGS getGoodFlags();
-    virtual BIT_FLAGS getBadFlags();
+    virtual s16b get_value();
+    virtual BIT_FLAGS get_all_flags();
+    virtual BIT_FLAGS get_good_flags();
+    virtual BIT_FLAGS get_bad_flags();
 
 protected:
     s16b default_value;
index e70ed57..4b17014 100644 (file)
@@ -144,9 +144,9 @@ s16b PlayerStealth::set_exception_value(s16b value)
  * @details
  * * TR_STELATHがマイナスの要素に加え、種族影フェアリーかつ反感のとき種族にマイナスフラグを与える
  */
-BIT_FLAGS PlayerStealth::getBadFlags()
+BIT_FLAGS PlayerStealth::get_bad_flags()
 {
-    BIT_FLAGS result = PlayerStatusBase::getBadFlags();
+    BIT_FLAGS result = PlayerStatusBase::get_bad_flags();
 
     if (this->is_aggravated_s_fairy())
         set_bits(result, FLAG_CAUSE_RACE);
index c4a85c8..e40ede7 100644 (file)
@@ -6,7 +6,7 @@ public:
     using PlayerStatusBase::PlayerStatusBase;
     PlayerStealth() = delete;
 
-    BIT_FLAGS getBadFlags() override;
+    BIT_FLAGS get_bad_flags() override;
 
 protected:
     void set_locals() override;
index 2a756c8..46bbe4a 100644 (file)
@@ -196,6 +196,9 @@ void sanity_blast(player_type *creature_ptr, monster_type *m_ptr, bool necro)
                 if (saving_throw(10 + creature_ptr->lev))
                     return;
                 break;
+
+            default:
+                break;
             }
         } else {
             if (mimic_info[creature_ptr->mimic_form].MIMIC_FLAGS & MIMIC_IS_DEMON) {
@@ -328,4 +331,4 @@ void sanity_blast(player_type *creature_ptr, monster_type *m_ptr, bool necro)
 
     creature_ptr->update |= PU_BONUS;
     handle_stuff(creature_ptr);
-}
\ No newline at end of file
+}
index 297c058..6ce23c0 100644 (file)
@@ -1,5 +1,6 @@
 #include "permanent-resistances.h"
 #include "inventory/inventory-slot-types.h"
+#include "mind/mind-elementalist.h"
 #include "mutation/mutation-flag-types.h"
 #include "object-enchant/tr-types.h"
 #include "player/player-personalities-types.h"
@@ -113,6 +114,32 @@ static void add_class_flags(player_type *creature_ptr, BIT_FLAGS *flags)
 
         break;
     }
+    case CLASS_ELEMENTALIST:
+        if (has_element_resist(creature_ptr, ElementRealm::FIRE, 1))
+            add_flag(flags, TR_RES_FIRE);
+        if (has_element_resist(creature_ptr, ElementRealm::ICE, 1))
+            add_flag(flags, TR_RES_COLD);
+        if (has_element_resist(creature_ptr, ElementRealm::SKY, 1))
+            add_flag(flags, TR_RES_ELEC);
+        if (has_element_resist(creature_ptr, ElementRealm::SEA, 1))
+            add_flag(flags, TR_RES_ACID);
+        if (has_element_resist(creature_ptr, ElementRealm::DARKNESS, 1))
+            add_flag(flags, TR_RES_DARK);
+        if (has_element_resist(creature_ptr, ElementRealm::DARKNESS, 30))
+            add_flag(flags, TR_RES_NETHER);
+        if (has_element_resist(creature_ptr, ElementRealm::CHAOS, 1))
+            add_flag(flags, TR_RES_CONF);
+        if (has_element_resist(creature_ptr, ElementRealm::CHAOS, 30))
+            add_flag(flags, TR_RES_CHAOS);
+        if (has_element_resist(creature_ptr, ElementRealm::EARTH, 1))
+            add_flag(flags, TR_RES_SHARDS);
+        if (has_element_resist(creature_ptr, ElementRealm::EARTH, 30))
+            add_flag(flags, TR_REFLECT);
+        if (has_element_resist(creature_ptr, ElementRealm::DEATH, 1))
+            add_flag(flags, TR_RES_POIS);
+        if (has_element_resist(creature_ptr, ElementRealm::DEATH, 30))
+            add_flag(flags, TR_RES_DISEN);
+        break;
     default:
         break;
     }
index 063a956..3871cb3 100644 (file)
@@ -389,6 +389,19 @@ const player_class class_info[MAX_CLASS] =
                2, 20, 40,
                4, 70, 2
        },
+
+       {
+#ifdef JP
+               "元素使い",
+#endif
+               "Elementalist",
+
+               {-3, 1, 3, 0, -1, 0},
+               30, 40, 38, 3, 16, 20, 34, 20,
+               7,  15, 11, 0,  0,  0,  6,  7,
+               0, 30, 25,
+               3, 100, 2
+       },
 };
 
 /*!
@@ -798,6 +811,20 @@ const concptr player_title[MAX_CLASS][PY_MAX_LEVEL / 5] =
                "伯爵",
                "領主",
        },
+
+    /* Elementalist */
+    {
+        "練習生",
+        "奇術師",
+        "幻術師",
+        "呪術師",
+        "召霊師",
+        "召魔師",
+        "魔術師",
+        "魔道師",
+        "イプシシマス",
+        "大魔道師",
+    },
 };
 
 #else
@@ -1194,5 +1221,19 @@ const concptr player_title[MAX_CLASS][PY_MAX_LEVEL / 5] =
                "Duke",
                "Lord",
        },
+
+    /* Elementalist */
+    {
+        "Apprentice",
+        "Trickster",
+        "Illusionist",
+        "Spellbinder",
+        "Evoker",
+        "Conjurer",
+        "Warlock",
+        "Sorcerer",
+        "Ipsissimus",
+        "Archmage",
+    },
 };
-#endif
\ No newline at end of file
+#endif
index 5a3cee3..2aa8b79 100644 (file)
@@ -29,5 +29,6 @@ enum player_class_type {
     CLASS_MIRROR_MASTER = 25,
     CLASS_NINJA = 26,
     CLASS_SNIPER = 27,
-    MAX_CLASS = 28, /*!< 職業の最大定義数 Maximum number of player "class" types (see "table.c", etc) */
+    CLASS_ELEMENTALIST = 28,
+    MAX_CLASS = 29, /*!< 職業の最大定義数 Maximum number of player "class" types (see "table.c", etc) */
 };
index 653a9fa..13db788 100644 (file)
@@ -33,6 +33,7 @@ const u32b realm_choices1[MAX_CLASS] = {
     (CH_NONE), /* Mirror-master */
     (CH_NONE), /* Ninja */
     (CH_NONE), /* Sniper */
+    (CH_NONE), /* Elementalist */
 };
 
 /*!
@@ -67,6 +68,7 @@ const u32b realm_choices2[MAX_CLASS] = {
     (CH_NONE), /* Mirror-master */
     (CH_NONE), /* Ninja */
     (CH_NONE), /* Sniper */
+    (CH_NONE), /* Elementalist */
 };
 
 REALM_IDX get_realm1_book(player_type *player_ptr) { return player_ptr->realm1 + TV_LIFE_BOOK - 1; }
@@ -76,5 +78,5 @@ REALM_IDX get_realm2_book(player_type *player_ptr) { return player_ptr->realm2 +
 bool is_wizard_class(player_type *player_ptr)
 {
     return (player_ptr->pclass == CLASS_MAGE || player_ptr->pclass == CLASS_HIGH_MAGE || player_ptr->pclass == CLASS_SORCERER || player_ptr->pclass == CLASS_MAGIC_EATER
-        || player_ptr->pclass == CLASS_BLUE_MAGE);
+        || player_ptr->pclass == CLASS_BLUE_MAGE || player_ptr->pclass == CLASS_ELEMENTALIST);
 }
index 8495582..fda31cf 100644 (file)
@@ -2,6 +2,7 @@
 #include "artifact/fixed-art-types.h"
 #include "grid/grid.h"
 #include "inventory/inventory-slot-types.h"
+#include "mind/mind-elementalist.h"
 #include "monster-race/monster-race.h"
 #include "monster-race/race-flags2.h"
 #include "monster-race/race-flags7.h"
@@ -149,23 +150,23 @@ BIT_FLAGS get_player_flags(player_type *creature_ptr, tr_type tr_flag)
 {
     switch (tr_flag) {
     case TR_STR:
-        return PlayerStrength(creature_ptr).getAllFlags();
+        return PlayerStrength(creature_ptr).get_all_flags();
     case TR_INT:
-        return PlayerIntelligence(creature_ptr).getAllFlags();
+        return PlayerIntelligence(creature_ptr).get_all_flags();
     case TR_WIS:
-        return PlayerWisdom(creature_ptr).getAllFlags();
+        return PlayerWisdom(creature_ptr).get_all_flags();
     case TR_DEX:
-        return PlayerDextarity(creature_ptr).getAllFlags();
+        return PlayerDextarity(creature_ptr).get_all_flags();
     case TR_CON:
-        return PlayerConstitution(creature_ptr).getAllFlags();
+        return PlayerConstitution(creature_ptr).get_all_flags();
     case TR_CHR:
-        return PlayerCharisma(creature_ptr).getAllFlags();
+        return PlayerCharisma(creature_ptr).get_all_flags();
     case TR_MAGIC_MASTERY:
         return has_magic_mastery(creature_ptr);
     case TR_FORCE_WEAPON:
         return check_equipment_flags(creature_ptr, tr_flag);
     case TR_STEALTH:
-        return PlayerStealth(creature_ptr).getAllFlags();
+        return PlayerStealth(creature_ptr).get_all_flags();
     case TR_SEARCH:
         return 0;
     case TR_INFRA:
@@ -173,7 +174,7 @@ BIT_FLAGS get_player_flags(player_type *creature_ptr, tr_type tr_flag)
     case TR_TUNNEL:
         return 0;
     case TR_SPEED:
-        return PlayerSpeed(creature_ptr).getAllFlags();
+        return PlayerSpeed(creature_ptr).get_all_flags();
     case TR_BLOWS:
         return 0;
     case TR_CHAOTIC:
@@ -395,6 +396,9 @@ BIT_FLAGS get_player_flags(player_type *creature_ptr, tr_type tr_flag)
     case TR_DARK_SOURCE:
     case TR_SUPPORTIVE:
         return check_equipment_flags(creature_ptr, tr_flag);
+
+    case TR_FLAG_MAX:
+        break;
     }
     return 0;
 }
@@ -746,6 +750,9 @@ BIT_FLAGS has_reflect(player_type *creature_ptr)
         result |= FLAG_CAUSE_MAGIC_TIME_EFFECT;
     }
 
+    if (has_element_resist(creature_ptr, ElementRealm::EARTH, 30))
+        result |= FLAG_CAUSE_CLASS;
+
     result |= check_equipment_flags(creature_ptr, TR_REFLECT);
     return result;
 }
@@ -1350,6 +1357,9 @@ BIT_FLAGS has_resist_acid(player_type *creature_ptr)
         result |= FLAG_CAUSE_MAGIC_TIME_EFFECT;
     }
 
+    if (has_element_resist(creature_ptr, ElementRealm::SEA, 1))
+        result |= FLAG_CAUSE_CLASS;
+
     result |= has_immune_acid(creature_ptr);
 
     result |= check_equipment_flags(creature_ptr, TR_RES_ACID);
@@ -1387,6 +1397,9 @@ BIT_FLAGS has_resist_elec(player_type *creature_ptr)
         result |= FLAG_CAUSE_MAGIC_TIME_EFFECT;
     }
 
+    if (has_element_resist(creature_ptr, ElementRealm::SKY, 1))
+        result |= FLAG_CAUSE_CLASS;
+
     result |= check_equipment_flags(creature_ptr, TR_RES_ELEC);
     result |= has_immune_elec(creature_ptr);
     return result;
@@ -1433,6 +1446,9 @@ BIT_FLAGS has_resist_fire(player_type *creature_ptr)
         result |= FLAG_CAUSE_MAGIC_TIME_EFFECT;
     }
 
+    if (has_element_resist(creature_ptr, ElementRealm::FIRE, 1))
+        result |= FLAG_CAUSE_CLASS;
+
     result |= check_equipment_flags(creature_ptr, TR_RES_FIRE);
     result |= has_immune_fire(creature_ptr);
     return result;
@@ -1483,6 +1499,9 @@ BIT_FLAGS has_resist_cold(player_type *creature_ptr)
         result |= FLAG_CAUSE_MAGIC_TIME_EFFECT;
     }
 
+    if (has_element_resist(creature_ptr, ElementRealm::ICE, 1))
+        result |= FLAG_CAUSE_CLASS;
+
     result |= check_equipment_flags(creature_ptr, TR_RES_COLD);
     result |= has_immune_cold(creature_ptr);
     return result;
@@ -1530,6 +1549,9 @@ BIT_FLAGS has_resist_pois(player_type *creature_ptr)
         result |= FLAG_CAUSE_MAGIC_TIME_EFFECT;
     }
 
+    if (has_element_resist(creature_ptr, ElementRealm::DEATH, 1))
+        result |= FLAG_CAUSE_CLASS;
+
     result |= check_equipment_flags(creature_ptr, TR_RES_POIS);
     return result;
 }
@@ -1561,6 +1583,9 @@ BIT_FLAGS has_resist_conf(player_type *creature_ptr)
         result |= FLAG_CAUSE_BATTLE_FORM;
     }
 
+    if (has_element_resist(creature_ptr, ElementRealm::CHAOS, 1))
+        result |= FLAG_CAUSE_CLASS;
+
     result |= check_equipment_flags(creature_ptr, TR_RES_CONF);
     return result;
 }
@@ -1646,6 +1671,9 @@ BIT_FLAGS has_resist_dark(player_type *creature_ptr)
         result |= FLAG_CAUSE_MAGIC_TIME_EFFECT;
     }
 
+    if (has_element_resist(creature_ptr, ElementRealm::DARKNESS, 1))
+        result |= FLAG_CAUSE_CLASS;
+
     result |= check_equipment_flags(creature_ptr, TR_RES_DARK);
     return result;
 }
@@ -1672,6 +1700,9 @@ BIT_FLAGS has_resist_chaos(player_type *creature_ptr)
         result |= FLAG_CAUSE_MAGIC_TIME_EFFECT;
     }
 
+    if (has_element_resist(creature_ptr, ElementRealm::CHAOS, 30))
+        result |= FLAG_CAUSE_CLASS;
+
     result |= check_equipment_flags(creature_ptr, TR_RES_CHAOS);
     return result;
 }
@@ -1695,6 +1726,9 @@ BIT_FLAGS has_resist_disen(player_type *creature_ptr)
         result |= FLAG_CAUSE_MAGIC_TIME_EFFECT;
     }
 
+    if (has_element_resist(creature_ptr, ElementRealm::DEATH, 30))
+        result |= FLAG_CAUSE_CLASS;
+
     result |= check_equipment_flags(creature_ptr, TR_RES_DISEN);
     return result;
 }
@@ -1714,6 +1748,9 @@ BIT_FLAGS has_resist_shard(player_type *creature_ptr)
         result |= FLAG_CAUSE_MAGIC_TIME_EFFECT;
     }
 
+    if (has_element_resist(creature_ptr, ElementRealm::EARTH, 1))
+        result |= FLAG_CAUSE_CLASS;
+
     result |= check_equipment_flags(creature_ptr, TR_RES_SHARDS);
     return result;
 }
@@ -1782,6 +1819,9 @@ BIT_FLAGS has_resist_neth(player_type *creature_ptr)
         result |= FLAG_CAUSE_MAGIC_TIME_EFFECT;
     }
 
+    if (has_element_resist(creature_ptr, ElementRealm::DARKNESS, 30))
+        result |= FLAG_CAUSE_CLASS;
+
     result |= check_equipment_flags(creature_ptr, TR_RES_NETHER);
     return result;
 }
@@ -1834,6 +1874,9 @@ BIT_FLAGS has_resist_fear(player_type *creature_ptr)
     case CLASS_NINJA:
         result |= FLAG_CAUSE_CLASS;
         break;
+
+    default:
+        break;
     }
 
     if (creature_ptr->mimic_form == MIMIC_DEMON_LORD) {
@@ -1866,6 +1909,9 @@ BIT_FLAGS has_immune_acid(player_type *creature_ptr)
             result |= FLAG_CAUSE_MAGIC_TIME_EFFECT;
     }
 
+    if (has_element_resist(creature_ptr, ElementRealm::SEA, 30))
+        result |= FLAG_CAUSE_CLASS;
+
     result |= check_equipment_flags(creature_ptr, TR_IM_ACID);
     return result;
 }
@@ -1879,6 +1925,9 @@ BIT_FLAGS has_immune_elec(player_type *creature_ptr)
             result |= FLAG_CAUSE_MAGIC_TIME_EFFECT;
     }
 
+    if (has_element_resist(creature_ptr, ElementRealm::SKY, 30))
+        result |= FLAG_CAUSE_CLASS;
+
     result |= check_equipment_flags(creature_ptr, TR_IM_ELEC);
     return result;
 }
@@ -1892,6 +1941,9 @@ BIT_FLAGS has_immune_fire(player_type *creature_ptr)
             result |= FLAG_CAUSE_MAGIC_TIME_EFFECT;
     }
 
+    if (has_element_resist(creature_ptr, ElementRealm::FIRE, 30))
+        result |= FLAG_CAUSE_CLASS;
+
     result |= check_equipment_flags(creature_ptr, TR_IM_FIRE);
     return result;
 }
@@ -1905,6 +1957,9 @@ BIT_FLAGS has_immune_cold(player_type *creature_ptr)
             result |= FLAG_CAUSE_MAGIC_TIME_EFFECT;
     }
 
+    if (has_element_resist(creature_ptr, ElementRealm::ICE, 30))
+        result |= FLAG_CAUSE_CLASS;
+
     result |= check_equipment_flags(creature_ptr, TR_IM_COLD);
     return result;
 }
index 3fdf3e8..ead1598 100644 (file)
@@ -219,6 +219,9 @@ PERCENTAGE calc_deathray_damage_rate(player_type *creature_ptr, rate_calc_type_m
     case RACE_SPECTRE:
         return 0;
         break;
+
+    default:
+        break;
     }
 
     return 100;
index 48dec0c..b34732d 100644 (file)
@@ -385,19 +385,19 @@ static void update_bonuses(player_type *creature_ptr)
         }
     }
 
-    creature_ptr->stat_add[A_STR] = PlayerStrength(creature_ptr).ModificationValue();
-    creature_ptr->stat_add[A_INT] = PlayerIntelligence(creature_ptr).ModificationValue();
-    creature_ptr->stat_add[A_WIS] = PlayerWisdom(creature_ptr).ModificationValue();
-    creature_ptr->stat_add[A_DEX] = PlayerDextarity(creature_ptr).ModificationValue();
-    creature_ptr->stat_add[A_CON] = PlayerConstitution(creature_ptr).ModificationValue();
-    creature_ptr->stat_add[A_CHR] = PlayerCharisma(creature_ptr).ModificationValue();
-
-    PlayerStrength(creature_ptr).updateValue();
-    PlayerIntelligence(creature_ptr).updateValue();
-    PlayerWisdom(creature_ptr).updateValue();
-    PlayerDextarity(creature_ptr).updateValue();
-    PlayerConstitution(creature_ptr).updateValue();
-    PlayerCharisma(creature_ptr).updateValue();
+    creature_ptr->stat_add[A_STR] = PlayerStrength(creature_ptr).modification_value();
+    creature_ptr->stat_add[A_INT] = PlayerIntelligence(creature_ptr).modification_value();
+    creature_ptr->stat_add[A_WIS] = PlayerWisdom(creature_ptr).modification_value();
+    creature_ptr->stat_add[A_DEX] = PlayerDextarity(creature_ptr).modification_value();
+    creature_ptr->stat_add[A_CON] = PlayerConstitution(creature_ptr).modification_value();
+    creature_ptr->stat_add[A_CHR] = PlayerCharisma(creature_ptr).modification_value();
+
+    PlayerStrength(creature_ptr).update_value();
+    PlayerIntelligence(creature_ptr).update_value();
+    PlayerWisdom(creature_ptr).update_value();
+    PlayerDextarity(creature_ptr).update_value();
+    PlayerConstitution(creature_ptr).update_value();
+    PlayerCharisma(creature_ptr).update_value();
 
     o_ptr = &creature_ptr->inventory_list[INVEN_BOW];
     if (o_ptr->k_idx) {
@@ -416,9 +416,9 @@ static void update_bonuses(player_type *creature_ptr)
         creature_ptr->to_ds[i] = calc_to_weapon_dice_side(creature_ptr, INVEN_MAIN_HAND + i);
     }
 
-    creature_ptr->pspeed = PlayerSpeed(creature_ptr).getValue();
+    creature_ptr->pspeed = PlayerSpeed(creature_ptr).get_value();
     creature_ptr->see_infra = calc_intra_vision(creature_ptr);
-    creature_ptr->skill_stl = PlayerStealth(creature_ptr).getValue();
+    creature_ptr->skill_stl = PlayerStealth(creature_ptr).get_value();
     creature_ptr->skill_dis = calc_disarming(creature_ptr);
     creature_ptr->skill_dev = calc_device_ability(creature_ptr);
     creature_ptr->skill_sav = calc_saving_throw(creature_ptr);
@@ -520,6 +520,9 @@ static void update_alignment(player_type *creature_ptr)
         case RACE_BALROG:
             creature_ptr->align -= 200;
             break;
+
+        default:
+            break;
         }
     }
 
@@ -894,7 +897,8 @@ static void update_max_mana(player_type *creature_ptr)
         return;
 
     int levels;
-    if ((creature_ptr->pclass == CLASS_MINDCRAFTER) || (creature_ptr->pclass == CLASS_MIRROR_MASTER) || (creature_ptr->pclass == CLASS_BLUE_MAGE)) {
+    if ((creature_ptr->pclass == CLASS_MINDCRAFTER) || (creature_ptr->pclass == CLASS_MIRROR_MASTER) || (creature_ptr->pclass == CLASS_BLUE_MAGE)
+        || creature_ptr->pclass == CLASS_ELEMENTALIST) {
         levels = creature_ptr->lev;
     } else {
         if (mp_ptr->spell_first > creature_ptr->lev) {
@@ -957,7 +961,8 @@ static void update_max_mana(player_type *creature_ptr)
     case CLASS_BLUE_MAGE:
     case CLASS_MONK:
     case CLASS_FORCETRAINER:
-    case CLASS_SORCERER: {
+    case CLASS_SORCERER:
+    case CLASS_ELEMENTALIST: {
         if (creature_ptr->inventory_list[INVEN_MAIN_HAND].tval <= TV_SWORD)
             cur_wgt += creature_ptr->inventory_list[INVEN_MAIN_HAND].weight;
         if (creature_ptr->inventory_list[INVEN_SUB_HAND].tval <= TV_SWORD)
@@ -1011,7 +1016,8 @@ static void update_max_mana(player_type *creature_ptr)
         switch (creature_ptr->pclass) {
         case CLASS_MAGE:
         case CLASS_HIGH_MAGE:
-        case CLASS_BLUE_MAGE: {
+        case CLASS_BLUE_MAGE:
+        case CLASS_ELEMENTALIST:  {
             msp -= msp * (cur_wgt - max_wgt) / 600;
             break;
         }
@@ -1951,7 +1957,7 @@ s16b calc_double_weapon_penalty(player_type *creature_ptr, INVENTORY_IDX slot)
             penalty = penalty / 2 - 5;
         }
 
-        for (int i = FLAG_CAUSE_INVEN_MAIN_HAND; i < FLAG_CAUSE_MAX; i <<= 1)
+        for (unsigned int i = FLAG_CAUSE_INVEN_MAIN_HAND; i < FLAG_CAUSE_MAX; i <<= 1)
             if (penalty > 0 && any_bits(creature_ptr->easy_2weapon, i))
                 penalty /= 2;
 
@@ -1986,6 +1992,9 @@ static bool is_riding_two_hands(player_type *creature_ptr)
             if ((empty_hands(creature_ptr, FALSE) != EMPTY_HAND_NONE) && !has_melee_weapon(creature_ptr, INVEN_MAIN_HAND)
                 && !has_melee_weapon(creature_ptr, INVEN_SUB_HAND))
                 return TRUE;
+
+        default:
+            break;
         }
     }
 
@@ -2300,6 +2309,10 @@ static s16b calc_to_hit(player_type *creature_ptr, INVENTORY_IDX slot, bool is_r
             /* fall through */
         case MELEE_TYPE_BAREHAND_TWO:
             hit += (creature_ptr->skill_exp[GINOU_SUDE] - WEAPON_EXP_BEGINNER) / 200;
+            break;
+
+        default:
+            break;
         }
 
         if ((is_martial_arts_mode(creature_ptr) && empty_hands(creature_ptr, FALSE) == (EMPTY_HAND_MAIN | EMPTY_HAND_SUB))
index a431435..07709de 100644 (file)
@@ -277,7 +277,7 @@ typedef struct player_type {
     s16b pet_follow_distance; /* Length of the imaginary "leash" for pets */
     s16b pet_extra_flags; /* Various flags for controling pets */
 
-    s16b today_mon; /* Wanted monster */
+    MONSTER_IDX today_mon; //!< 日替わり賞金首を知っていればそのモンスターID、知らなければ 0
 
     bool dtrap; /* Whether you are on trap-safe grids */
     FLOOR_IDX floor_id; /* Current floor location */
index da243a7..5ca8d6c 100644 (file)
@@ -1,9 +1,11 @@
-#include "player/player-view.h"
+#include <vector>
+
 #include "core/player-update-types.h"
 #include "floor/cave.h"
 #include "floor/line-of-sight.h"
 #include "game-option/map-screen-options.h"
 #include "grid/grid.h"
+#include "player/player-view.h"
 #include "system/floor-type-definition.h"
 
 /*
@@ -94,6 +96,19 @@ static bool update_view_aux(player_type *subject_ptr, POSITION y, POSITION x, PO
  */
 void update_view(player_type *subject_ptr)
 {
+    struct Point {
+        int y;
+        int x;
+        Point(const int y, const int x)
+            : y(y)
+            , x(x)
+        {
+        }
+    };
+
+    // 前回プレイヤーから見えていた座標たちを格納する配列。
+    std::vector<Point> points;
+
     int n, m, d, k, z;
     POSITION y, x;
 
@@ -120,9 +135,8 @@ void update_view(player_type *subject_ptr)
         g_ptr = &floor_ptr->grid_array[y][x];
         g_ptr->info &= ~(CAVE_VIEW);
         g_ptr->info |= CAVE_TEMP;
-        tmp_pos.y[tmp_pos.n] = y;
-        tmp_pos.x[tmp_pos.n] = x;
-        tmp_pos.n++;
+
+        points.emplace_back(y, x);
     }
 
     floor_ptr->view_n = 0;
@@ -334,9 +348,7 @@ void update_view(player_type *subject_ptr)
         cave_note_and_redraw_later(floor_ptr, g_ptr, y, x);
     }
 
-    for (n = 0; n < tmp_pos.n; n++) {
-        y = tmp_pos.y[n];
-        x = tmp_pos.x[n];
+    for (const auto &[y, x] : points) {
         g_ptr = &floor_ptr->grid_array[y][x];
         g_ptr->info &= ~(CAVE_TEMP);
         if (g_ptr->info & CAVE_VIEW)
@@ -345,6 +357,5 @@ void update_view(player_type *subject_ptr)
         cave_redraw_later(floor_ptr, g_ptr, y, x);
     }
 
-    tmp_pos.n = 0;
     subject_ptr->update |= PU_DELAY_VIS;
 }
index 187771c..9db8e9c 100644 (file)
@@ -31,7 +31,7 @@ const player_race race_info[MAX_RACES] =
                72,  6, 180, 25,
                66,  4, 150, 20,
                0,
-               0xFFFFFFF,
+               0x1FFFFFFF,
        },
        {
 #ifdef JP
@@ -46,7 +46,7 @@ const player_race race_info[MAX_RACES] =
                66,  6, 130, 15,
                62,  6, 100, 10,
                2,
-               0xE77E7FF,
+               0x1E77E7FF,
        },
        {
 #ifdef JP
@@ -61,7 +61,7 @@ const player_race race_info[MAX_RACES] =
                60,  4, 100,  6,
                54,  4, 80,  6,
                3,
-               0xE77E75B,
+               0x1E77E75B,
 
        },
        {
@@ -77,7 +77,7 @@ const player_race race_info[MAX_RACES] =
                36,  3, 60,  3,
                33,  3, 50,  3,
                4,
-               0xF6FFC0B,
+               0x1F6FFC0B,
        },
        {
 #ifdef JP
@@ -92,7 +92,7 @@ const player_race race_info[MAX_RACES] =
                42,  3, 90,  6,
                39,  3, 75,  3,
                4,
-               0xF67D60F,
+               0x1F67D60F,
        },
        {
 #ifdef JP
@@ -107,7 +107,7 @@ const player_race race_info[MAX_RACES] =
                48,  3, 150, 10,
                46,  3, 120, 10,
                5,
-               0x1890005,
+               0x01890005,
        },
        {
 #ifdef JP
@@ -122,7 +122,7 @@ const player_race race_info[MAX_RACES] =
                66,  1, 150,  5,
                62,  1, 120,  5,
                3,
-               0xDD8818D,
+               0x0DD8818D,
        },
        {
 #ifdef JP
@@ -137,7 +137,7 @@ const player_race race_info[MAX_RACES] =
                96, 10, 250, 50,
                84,  8, 225, 40,
                3,
-               0x0880005,
+               0x00880005,
        },
        {
 #ifdef JP
@@ -152,7 +152,7 @@ const player_race race_info[MAX_RACES] =
                82, 5, 190, 20,
                78,  6, 180, 15,
                0,
-               0xFFFF7FF,
+               0x1FFFF7FF,
        },
        {
 #ifdef JP
@@ -167,7 +167,7 @@ const player_race race_info[MAX_RACES] =
                90, 10, 190, 20,
                82, 10, 180, 15,
                4,
-               0xF77E75B,
+               0x1F77E75B,
        },
        {
 #ifdef JP
@@ -182,7 +182,7 @@ const player_race race_info[MAX_RACES] =
                82, 5, 200, 20,
                78,  6, 190, 15,
                0,
-               0x5C0A09D,
+               0x05C0A09D,
        },
        {
 #ifdef JP
@@ -197,7 +197,7 @@ const player_race race_info[MAX_RACES] =
                92, 10, 255, 60,
                80,  8, 235, 60,
                3,
-               0x0A80407,
+               0x00A80407,
        },
        {
 #ifdef JP
@@ -212,7 +212,7 @@ const player_race race_info[MAX_RACES] =
                100,10, 255, 65,
                80, 10, 240, 64,
                3,
-               0x8880011,
+               0x08880011,
        },
        {
 #ifdef JP
@@ -227,7 +227,7 @@ const player_race race_info[MAX_RACES] =
                111, 11, 255, 86,
                99, 11, 250, 86,
                0,
-               0x23D4727,
+               0x123D4727,
        },
        {
 #ifdef JP
@@ -242,7 +242,7 @@ const player_race race_info[MAX_RACES] =
                92, 10, 255, 60,
                80,  8, 235, 60,
                1,
-               0x0888005,
+               0x00888005,
        },
        {
 #ifdef JP
@@ -257,7 +257,7 @@ const player_race race_info[MAX_RACES] =
                50,  3, 90,  6,
                50,  3, 75,  3,
                2,
-               0x667360F,
+               0x1667360F,
        },
        {
 #ifdef JP
@@ -272,7 +272,7 @@ const player_race race_info[MAX_RACES] =
                60,  3, 80,  4,
                54,  3, 70,  4,
                2,
-               0x04D8011,
+               0x004D8011,
        },
        {
 #ifdef JP
@@ -287,7 +287,7 @@ const player_race race_info[MAX_RACES] =
                60,  1, 130,  5,
                55,  1, 100,  5,
                3,
-               0x444A009,
+               0x0444A009,
        },
        {
 #ifdef JP
@@ -302,7 +302,7 @@ const player_race race_info[MAX_RACES] =
                43,  3, 92,  6,
                40,  3, 78,  3,
                5,
-               0x569040F,
+               0x0569040F,
        },
        {
 #ifdef JP
@@ -317,7 +317,7 @@ const player_race race_info[MAX_RACES] =
                60,  4, 100,  6,
                54,  4, 80,  6,
                5,
-               0xE77C7DF,
+               0x1E77C7DF,
        },
        {
 #ifdef JP
@@ -332,7 +332,7 @@ const player_race race_info[MAX_RACES] =
                76,  1, 160,  5,
                72,  1, 130,  5,
                2,
-               0x7FFE757,
+               0x07FFE757,
        },
        {
 #ifdef JP
@@ -347,7 +347,7 @@ const player_race race_info[MAX_RACES] =
                68,  6, 142, 15,
                63,  6, 112, 10,
                4,
-               0x2334746,
+               0x12334746,
        },
        {
 #ifdef JP
@@ -362,7 +362,7 @@ const player_race race_info[MAX_RACES] =
                68,  1, 150,  5,
                64,  1, 120,  5,
                3,
-               0xDB537CB,
+               0x1DB537CB,
        },
        {
 #ifdef JP
@@ -377,7 +377,7 @@ const player_race race_info[MAX_RACES] =
                66,  1, 200,  6,
                62,  1, 180,  6,
                4,
-               0x0800001,
+               0x00800001,
        },
        {
 #ifdef JP
@@ -392,7 +392,7 @@ const player_race race_info[MAX_RACES] =
                72,  6, 50, 5,
                66,  4, 50, 5,
                2,
-               0x234070F,
+               0x1234070F,
        },
        {
 #ifdef JP
@@ -407,7 +407,7 @@ const player_race race_info[MAX_RACES] =
                72, 6, 100, 25,
                66, 4, 100, 20,
                2,
-               0x0800001,
+               0x00800001,
        },
        {
 #ifdef JP
@@ -422,7 +422,7 @@ const player_race race_info[MAX_RACES] =
                72,  6, 180, 25,
                66,  4, 150, 20,
                5,
-               0x67DC7FF,
+               0x167DC7FF,
        },
        {
 #ifdef JP
@@ -437,7 +437,7 @@ const player_race race_info[MAX_RACES] =
                72, 6, 100, 25,
                66, 4, 100, 20,
                5,
-               0x631474A,
+               0x1631474A,
        },
        {
 #ifdef JP
@@ -452,7 +452,7 @@ const player_race race_info[MAX_RACES] =
                32,  2, 75,  2,
                29,  2, 65,  2,
                4,
-               0x623F65E,
+               0x1623F65E,
        },
        {
 #ifdef JP
@@ -467,7 +467,7 @@ const player_race race_info[MAX_RACES] =
                65,  6, 150, 20,
                61,  6, 120, 15,
                0,
-               0x57887CF,
+               0x057887CF,
        },
        {
 #ifdef JP
@@ -481,7 +481,7 @@ const player_race race_info[MAX_RACES] =
                111, 11, 255, 50,
                99, 11, 250, 45,
                  0,
-               0x0010005,
+               0x00010005,
        },
        {
 #ifdef JP
@@ -496,7 +496,7 @@ const player_race race_info[MAX_RACES] =
                82, 5, 190, 20,
                78,  6, 180, 15,
                3,
-               0x779F777,
+               0x0779F777,
        },
        {
 #ifdef JP
@@ -513,7 +513,7 @@ const player_race race_info[MAX_RACES] =
                100,10, 255, 65,
                80, 10, 240, 64,
                5,
-               0x7EDC4DB,
+               0x17EDC4DB,
        },
        {
 #ifdef JP
@@ -528,7 +528,7 @@ const player_race race_info[MAX_RACES] =
                82, 5, 190, 20,
                78,  6, 180, 15,
                0,
-               0xFFFF7FF,
+               0x1FFFF7FF,
        },
        {
 #ifdef JP
@@ -542,7 +542,7 @@ const player_race race_info[MAX_RACES] =
                 80,  8, 90, 20,
                 73,  8, 80, 15,
                  4,
-               0xE33C7DF,
+               0x1E33C7DF,
        },
        {
 #ifdef JP
@@ -557,7 +557,7 @@ const player_race race_info[MAX_RACES] =
                48,  6, 150, 25,
                44,  4, 130, 20,
                0,
-               0xC18B7AD,
+               0x0C18B7AD,
        },
        {
 #ifdef JP
@@ -572,7 +572,7 @@ const player_race race_info[MAX_RACES] =
                72, 12, 240, 64,
                66, 12, 220, 64,
                0,
-               0x0800001,
+               0x00800001,
        },
        {
 #ifdef JP
@@ -587,7 +587,7 @@ const player_race race_info[MAX_RACES] =
                        66,  6, 130, 15,
                        62,  6, 100, 10,
                        2,
-                       0xE77E7FF,
+                       0x1E77E7FF,
        },
 
 };
index 6d50bc6..cf9e31f 100644 (file)
@@ -6,10 +6,11 @@
 #include "object-enchant/tr-types.h"
 #include "player/player-race.h"
 #include "player/special-defense-types.h"
+#include "mind/mind-elementalist.h"
 #include "util/bit-flags-calculator.h"
 
 /*!
- * @brief プレイヤーの種族による免疫フラグを返す
+ * @brief プレイヤーの職業/種族による免疫フラグを返す
  * @param creature_ptr プレーヤーへの参照ポインタ
  * @param flags フラグを保管する配列
  * @return なし
  */
 void player_immunity(player_type *creature_ptr, BIT_FLAGS *flags)
 {
-       for (int i = 0; i < TR_FLAG_SIZE; i++)
-               flags[i] = 0L;
+    for (int i = 0; i < TR_FLAG_SIZE; i++)
+        flags[i] = 0L;
 
-       if (is_specific_player_race(creature_ptr, RACE_SPECTRE))
-               add_flag(flags, TR_RES_NETHER);
-       if (creature_ptr->mimic_form == MIMIC_VAMPIRE || is_specific_player_race(creature_ptr, RACE_VAMPIRE))
-               add_flag(flags, TR_RES_DARK);
-       if (creature_ptr->mimic_form == MIMIC_DEMON_LORD)
-               add_flag(flags, TR_RES_FIRE);
-       else if (is_specific_player_race(creature_ptr, RACE_YEEK) && creature_ptr->lev > 19)
-               add_flag(flags, TR_RES_ACID);
+    if (is_specific_player_race(creature_ptr, RACE_SPECTRE))
+        add_flag(flags, TR_RES_NETHER);
+    if (creature_ptr->mimic_form == MIMIC_VAMPIRE || is_specific_player_race(creature_ptr, RACE_VAMPIRE))
+        add_flag(flags, TR_RES_DARK);
+    if (creature_ptr->mimic_form == MIMIC_DEMON_LORD)
+        add_flag(flags, TR_RES_FIRE);
+    else if (is_specific_player_race(creature_ptr, RACE_YEEK) && creature_ptr->lev > 19)
+        add_flag(flags, TR_RES_ACID);
+
+    if (creature_ptr->pclass == CLASS_ELEMENTALIST) {
+        if (has_element_resist(creature_ptr, ElementRealm::FIRE, 30))
+            add_flag(flags, TR_RES_FIRE);
+        if (has_element_resist(creature_ptr, ElementRealm::SKY, 30))
+            add_flag(flags, TR_RES_ELEC);
+        if (has_element_resist(creature_ptr, ElementRealm::SEA, 30))
+            add_flag(flags, TR_RES_ACID);
+        if (has_element_resist(creature_ptr, ElementRealm::ICE, 30))
+            add_flag(flags, TR_RES_COLD);
+    }
 }
 
 
index 3201e95..95929b9 100644 (file)
@@ -1,4 +1,5 @@
 #include "racial/class-racial-switcher.h"
+#include "mind/mind-elementalist.h"
 #include "racial/racial-util.h"
 #include "realm/realm-names-table.h"
 #include "realm/realm-types.h"
@@ -281,6 +282,16 @@ void switch_class_racial(player_type *creature_ptr, rc_type *rc_ptr)
         rc_ptr->power_desc[rc_ptr->num].fail = 0;
         rc_ptr->power_desc[rc_ptr->num++].number = -3;
         break;
+    case CLASS_ELEMENTALIST:
+        strcpy(rc_ptr->power_desc[rc_ptr->num].racial_name, _("明鏡止水", "Clear Mind"));
+        rc_ptr->power_desc[rc_ptr->num].min_level = 15;
+        rc_ptr->power_desc[rc_ptr->num].cost = 0;
+        rc_ptr->power_desc[rc_ptr->num].stat = A_WIS;
+        rc_ptr->power_desc[rc_ptr->num].fail = 10;
+        rc_ptr->power_desc[rc_ptr->num++].number = -3;
+
+        switch_element_racial(creature_ptr, rc_ptr);
+        break;
     default:
         strcpy(rc_ptr->power_desc[0].racial_name, _("(なし)", "(none)"));
         break;
index 026a59e..5096ad1 100644 (file)
@@ -1,4 +1,5 @@
 #include "racial/racial-draconian.h"
+#include "mind/mind-elementalist.h"
 #include "spell-kind/spells-launcher.h"
 #include "spell/spell-types.h"
 #include "target/target-getter.h"
@@ -107,6 +108,12 @@ static void decide_breath_kind(player_type *creature_ptr, int *breath_type, conc
         }
 
         break;
+    case CLASS_ELEMENTALIST:
+        *breath_type = get_element_type(creature_ptr->realm1, 0);
+        *breath_type_description = get_element_name(creature_ptr->realm1, 0);
+        break;
+    default:
+        break;
     }
 }
 
index 9a15128..6209217 100644 (file)
@@ -30,6 +30,7 @@
 #include "melee/melee-postprocess.h"
 #include "mind/mind-archer.h"
 #include "mind/mind-cavalry.h"
+#include "mind/mind-elementalist.h"
 #include "mind/mind-force-trainer.h"
 #include "mind/mind-hobbit.h"
 #include "mind/mind-mage.h"
@@ -250,6 +251,12 @@ bool switch_class_racial_execution(player_type *creature_ptr, const s32b command
         return TRUE;
     case CLASS_NINJA:
         return hayagake(creature_ptr);
+    case CLASS_ELEMENTALIST:
+        if (command == -3)
+            return clear_mind(creature_ptr);
+        if (command == -4)
+            return switch_element_execution(creature_ptr);
+        return TRUE;
     default:
         return TRUE;
     }
index 3449824..71dd5d9 100644 (file)
@@ -79,6 +79,9 @@ static bool item_tester_hook_weapon_except_bow(player_type *player_ptr, object_t
     case TV_DIGGING: {
         return TRUE;
     }
+
+    default:
+        break;
     }
 
     return FALSE;
index 94daf21..97a6729 100644 (file)
@@ -4,12 +4,14 @@
  * @author Hourier
  */
 
-#include "room/cave-filler.h"
+#include <queue>
+
 #include "dungeon/dungeon-flag-types.h"
 #include "dungeon/dungeon.h"
 #include "floor/cave.h"
 #include "grid/feature.h"
 #include "grid/grid.h"
+#include "room/cave-filler.h"
 #include "room/lake-types.h"
 #include "system/floor-type-definition.h"
 
@@ -223,46 +225,46 @@ static bool hack_isnt_wall(player_type *player_ptr, POSITION y, POSITION x, int
  * Quick and nasty fill routine used to find the connected region
  * of floor in the middle of the grids
  */
-static void cave_fill(player_type *player_ptr, POSITION y, POSITION x)
+static void cave_fill(player_type *player_ptr, const POSITION y, const POSITION x)
 {
-    int flow_tail_room = 1;
-    int flow_head_room = 0;
-    tmp_pos.y[0] = y;
-    tmp_pos.x[0] = x;
+    struct Point {
+        int y;
+        int x;
+        Point(const int y, const int x)
+            : y(y)
+            , x(x)
+        {
+        }
+    };
+
     floor_type *floor_ptr = player_ptr->current_floor_ptr;
-    while (flow_head_room != flow_tail_room) {
-        POSITION ty = tmp_pos.y[flow_head_room];
-        POSITION tx = tmp_pos.x[flow_head_room];
-        if (++flow_head_room == TEMP_MAX)
-            flow_head_room = 0;
+
+    // 幅優先探索用のキュー。
+    std::queue<Point> que;
+    que.emplace(y, x);
+
+    while (!que.empty()) {
+        const auto [y_cur, x_cur] = que.front();
+        que.pop();
 
         for (int d = 0; d < 8; d++) {
-            int old_head = flow_tail_room;
-            int j = ty + ddy_ddd[d];
-            int i = tx + ddx_ddd[d];
-            if (!in_bounds(floor_ptr, j, i)) {
-                floor_ptr->grid_array[j][i].info |= CAVE_ICKY;
+            int y_to = y_cur + ddy_ddd[d];
+            int x_to = x_cur + ddx_ddd[d];
+            if (!in_bounds(floor_ptr, y_to, x_to)) {
+                floor_ptr->grid_array[y_to][x_to].info |= CAVE_ICKY;
                 continue;
             }
 
-            if ((i <= fill_data.xmin) || (i >= fill_data.xmax) || (j <= fill_data.ymin) || (j >= fill_data.ymax)) {
-                floor_ptr->grid_array[j][i].info |= CAVE_ICKY;
+            if ((x_to <= fill_data.xmin) || (x_to >= fill_data.xmax) || (y_to <= fill_data.ymin) || (y_to >= fill_data.ymax)) {
+                floor_ptr->grid_array[y_to][x_to].info |= CAVE_ICKY;
                 continue;
             }
 
-            if (!hack_isnt_wall(player_ptr, j, i, fill_data.c1, fill_data.c2, fill_data.c3, fill_data.feat1, fill_data.feat2, fill_data.feat3, fill_data.info1,
-                    fill_data.info2, fill_data.info3))
+            if (!hack_isnt_wall(player_ptr, y_to, x_to, fill_data.c1, fill_data.c2, fill_data.c3, fill_data.feat1, fill_data.feat2, fill_data.feat3,
+                    fill_data.info1, fill_data.info2, fill_data.info3))
                 continue;
 
-            tmp_pos.y[flow_tail_room] = (byte)j;
-            tmp_pos.x[flow_tail_room] = (byte)i;
-            if (++flow_tail_room == TEMP_MAX)
-                flow_tail_room = 0;
-
-            if (flow_tail_room == flow_head_room) {
-                flow_tail_room = old_head;
-                continue;
-            }
+            que.emplace(y_to, x_to);
 
             (fill_data.amount)++;
         }
diff --git a/src/room/pit-nest-kinds-table.cpp b/src/room/pit-nest-kinds-table.cpp
deleted file mode 100644 (file)
index 809c714..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#include "room/pit-nest-kinds-table.h"
-#include "monster-race/monster-race-hook.h"
-
-/*!nest情報テーブル*/
-vault_aux_type nest_types[MAX_PIT_NEST_KINDS] =
-{
-       { _("クローン", "clone"),      vault_aux_clone,    vault_prep_clone,   5, 3 },
-       { _("ゼリー", "jelly"),        vault_aux_jelly,    NULL,               5, 6 },
-       { _("シンボル(善)", "symbol good"), vault_aux_symbol_g, vault_prep_symbol, 25, 2 },
-       { _("シンボル(悪)", "symbol evil"), vault_aux_symbol_e, vault_prep_symbol, 25, 2 },
-       { _("ミミック", "mimic"),      vault_aux_mimic,    NULL,              30, 4 },
-       { _("狂気", "lovecraftian"),   vault_aux_cthulhu,  NULL,              70, 2 },
-       { _("犬小屋", "kennel"),       vault_aux_kennel,   NULL,              45, 4 },
-       { _("動物園", "animal"),       vault_aux_animal,   NULL,              35, 5 },
-       { _("教会", "chapel"),         vault_aux_chapel_g, NULL,              75, 4 },
-       { _("アンデッド", "undead"),   vault_aux_undead,   NULL,              75, 5 },
-       { NULL,           NULL,               NULL,               0, 0 },
-};
-
-/*!pit情報テーブル*/
-vault_aux_type pit_types[MAX_PIT_NEST_KINDS] =
-{
-       { _("オーク", "orc"),            vault_aux_orc,      NULL,               5, 6 },
-       { _("トロル", "troll"),          vault_aux_troll,    NULL,              20, 6 },
-       { _("巨人", "giant"),    vault_aux_giant,    NULL,              50, 6 },
-       { _("狂気", "lovecraftian"),     vault_aux_cthulhu,  NULL,              80, 2 },
-       { _("シンボル(善)", "symbol good"), vault_aux_symbol_g, vault_prep_symbol, 70, 1 },
-       { _("シンボル(悪)", "symbol evil"), vault_aux_symbol_e, vault_prep_symbol, 70, 1 },
-       { _("教会", "chapel"),           vault_aux_chapel_g, NULL,              65, 2 },
-       { _("ドラゴン", "dragon"),       vault_aux_dragon,   vault_prep_dragon, 70, 6 },
-       { _("デーモン", "demon"),        vault_aux_demon,    NULL,              80, 6 },
-       { _("ダークエルフ", "dark elf"), vault_aux_dark_elf, NULL,              45, 4 },
-       { NULL,           NULL,               NULL,               0, 0 },
-};
-
-const int placing[MAX_MONSTER_PLACE][3] = {
-       { -2, -9, 0 },{ -2, -8, 0 },{ -3, -7, 0 },{ -3, -6, 0 },
-       { +2, -9, 0 },{ +2, -8, 0 },{ +3, -7, 0 },{ +3, -6, 0 },
-       { -2, +9, 0 },{ -2, +8, 0 },{ -3, +7, 0 },{ -3, +6, 0 },
-       { +2, +9, 0 },{ +2, +8, 0 },{ +3, +7, 0 },{ +3, +6, 0 },
-       { -2, -7, 1 },{ -3, -5, 1 },{ -3, -4, 1 },
-       { +2, -7, 1 },{ +3, -5, 1 },{ +3, -4, 1 },
-       { -2, +7, 1 },{ -3, +5, 1 },{ -3, +4, 1 },
-       { +2, +7, 1 },{ +3, +5, 1 },{ +3, +4, 1 },
-       { -2, -6, 2 },{ -2, -5, 2 },{ -3, -3, 2 },
-       { +2, -6, 2 },{ +2, -5, 2 },{ +3, -3, 2 },
-       { -2, +6, 2 },{ -2, +5, 2 },{ -3, +3, 2 },
-       { +2, +6, 2 },{ +2, +5, 2 },{ +3, +3, 2 },
-       { -2, -4, 3 },{ -3, -2, 3 },
-       { +2, -4, 3 },{ +3, -2, 3 },
-       { -2, +4, 3 },{ -3, +2, 3 },
-       { +2, +4, 3 },{ +3, +2, 3 },
-       { -2, -3, 4 },{ -3, -1, 4 },
-       { +2, -3, 4 },{ +3, -1, 4 },
-       { -2, +3, 4 },{ -3, +1, 4 },
-       { +2, +3, 4 },{ +3, +1, 4 },
-       { -2, -2, 5 },{ -3, 0, 5 },{ -2, +2, 5 },
-       { +2, -2, 5 },{ +3, 0, 5 },{ +2, +2, 5 },
-       { -2, -1, 6 },{ -2, +1, 6 },
-       { +2, -1, 6 },{ +2, +1, 6 },
-       { -2, 0, 7 },{ +2, 0, 7 },
-       { 0, 0, -1 }
-};
diff --git a/src/room/pit-nest-kinds-table.h b/src/room/pit-nest-kinds-table.h
deleted file mode 100644 (file)
index d9b89ee..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-#pragma once
-
-#include "system/angband.h"
-
-#define NUM_NEST_MON_TYPE 64 /*!<nestの種別数 */
-
-/*! nestのID定義 /  Nest types code */
-#define NEST_TYPE_CLONE        0
-#define NEST_TYPE_JELLY        1
-#define NEST_TYPE_SYMBOL_GOOD  2
-#define NEST_TYPE_SYMBOL_EVIL  3
-#define NEST_TYPE_MIMIC        4
-#define NEST_TYPE_LOVECRAFTIAN 5
-#define NEST_TYPE_KENNEL       6
-#define NEST_TYPE_ANIMAL       7
-#define NEST_TYPE_CHAPEL       8
-#define NEST_TYPE_UNDEAD       9
-
-/*! pitのID定義 / Pit types code */
-#define PIT_TYPE_ORC           0
-#define PIT_TYPE_TROLL         1
-#define PIT_TYPE_GIANT         2
-#define PIT_TYPE_LOVECRAFTIAN  3
-#define PIT_TYPE_SYMBOL_GOOD   4
-#define PIT_TYPE_SYMBOL_EVIL   5
-#define PIT_TYPE_CHAPEL        6
-#define PIT_TYPE_DRAGON        7
-#define PIT_TYPE_DEMON         8
-#define PIT_TYPE_DARK_ELF      9
-
-#define MAX_PIT_NEST_KINDS 11
-#define MAX_MONSTER_PLACE 69
-
-/*! pit/nest型情報の構造体定義 */
-typedef struct vault_aux_type
-{
-       concptr name;
-    bool (*hook_func)(player_type *player_ptr, MONRACE_IDX r_idx);
-       void (*prep_func)(player_type *player_ptr);
-       DEPTH level;
-       int chance;
-} vault_aux_type;
-
-extern vault_aux_type nest_types[MAX_PIT_NEST_KINDS];
-extern vault_aux_type pit_types[MAX_PIT_NEST_KINDS];
-
-extern const int placing[MAX_MONSTER_PLACE][3];
index 383230b..07998c4 100644 (file)
@@ -16,6 +16,7 @@
 #include "room/rooms-vault.h"
 #include "system/dungeon-data-definition.h"
 #include "system/floor-type-definition.h"
+#include "util/probability-table.h"
 #include "wizard/wizard-messages.h"
 
 /*!
@@ -146,24 +147,18 @@ bool generate_rooms(player_type *player_ptr, dun_data_type *dd_ptr)
     if (!(d_info[floor_ptr->dungeon_idx].flags1 & DF1_ARCADE))
         prob_list[ROOM_T_ARCADE] = 0;
 
-    int total_prob = 0;
+    ProbabilityTable<int> prob_table;
     for (int i = 0; i < ROOM_T_MAX; i++) {
         room_num[i] = 0;
-        total_prob += prob_list[i];
+        prob_table.entry_item(i, prob_list[i]);
     }
 
     for (int i = dun_rooms; i > 0; i--) {
         int room_type;
-        int rand = randint0(total_prob);
-        for (room_type = 0; room_type < ROOM_T_MAX; room_type++) {
-            if (rand < prob_list[room_type])
-                break;
-            else
-                rand -= prob_list[room_type];
-        }
-
-        if (room_type >= ROOM_T_MAX)
+        if (prob_table.empty())
             room_type = ROOM_T_NORMAL;
+        else
+            room_type = prob_table.pick_one_at_random();
 
         room_num[room_type]++;
         switch (room_type) {
index b079bc4..b4eb0b3 100644 (file)
 #include "monster/monster-info.h"
 #include "monster/monster-list.h"
 #include "monster/monster-util.h"
-#include "room/pit-nest-kinds-table.h"
 #include "room/space-finder.h"
 #include "system/floor-type-definition.h"
+#include "util/probability-table.h"
 #include "util/sort.h"
 #include "view/display-messages.h"
 #include "wizard/wizard-messages.h"
+#include <vector>
 
 /*!
  * @brief ダンジョン毎に指定されたピット配列を基準にランダムなpit/nestタイプを決める
  * @param allow_flag_mask 生成が許されるpit/nestのビット配列
  * @return 選択されたpit/nestのID、選択失敗した場合-1を返す。
  */
-static int pick_vault_type(floor_type *floor_ptr, vault_aux_type *l_ptr, BIT_FLAGS16 allow_flag_mask)
+static int pick_vault_type(floor_type *floor_ptr, std::vector<nest_pit_type>& l_ptr, BIT_FLAGS16 allow_flag_mask)
 {
-    int total;
-    int count;
-    vault_aux_type *n_ptr;
-    for (n_ptr = l_ptr, total = 0, count = 0; TRUE; n_ptr++, count++) {
-        if (!n_ptr->name)
-            break;
+    ProbabilityTable<int> table;
+    for (size_t i = 0; i < l_ptr.size(); i++) {
+        nest_pit_type *n_ptr = &l_ptr.at(i);
 
         if (n_ptr->level > floor_ptr->dun_level)
             continue;
 
-        if (!(allow_flag_mask & (1UL << count)))
+        if (!(allow_flag_mask & (1UL << i)))
             continue;
 
-        total += n_ptr->chance * MAX_DEPTH / (MIN(floor_ptr->dun_level, MAX_DEPTH - 1) - n_ptr->level + 5);
-    }
-
-    int random_type = randint0(total);
-    for (n_ptr = l_ptr, total = 0, count = 0; TRUE; n_ptr++, count++) {
-        if (!n_ptr->name)
-            break;
-
-        if (n_ptr->level > floor_ptr->dun_level)
-            continue;
-
-        if (!(allow_flag_mask & (1UL << count)))
-            continue;
-
-        total += n_ptr->chance * MAX_DEPTH / (MIN(floor_ptr->dun_level, MAX_DEPTH - 1) - n_ptr->level + 5);
-        if (random_type < total)
-            break;
+        table.entry_item(i, n_ptr->chance * MAX_DEPTH / (MIN(floor_ptr->dun_level, MAX_DEPTH - 1) - n_ptr->level + 5));
     }
 
-    return n_ptr->name ? count : -1;
+    return !table.empty() ? table.pick_one_at_random() : -1;
 }
 
 /*!
@@ -195,6 +177,22 @@ static void ang_sort_swap_nest_mon_info(player_type *player_ptr, vptr u, vptr v,
 }
 
 /*!
+ * @brief 生成するNestの情報テーブル
+ */
+std::vector<nest_pit_type> nest_types = {
+    { _("クローン", "clone"), vault_aux_clone, vault_prep_clone, 5, 3 },
+    { _("ゼリー", "jelly"), vault_aux_jelly, NULL, 5, 6 },
+    { _("シンボル(善)", "symbol good"), vault_aux_symbol_g, vault_prep_symbol, 25, 2 },
+    { _("シンボル(悪)", "symbol evil"), vault_aux_symbol_e, vault_prep_symbol, 25, 2 },
+    { _("ミミック", "mimic"), vault_aux_mimic, NULL, 30, 4 },
+    { _("狂気", "lovecraftian"), vault_aux_cthulhu, NULL, 70, 2 },
+    { _("犬小屋", "kennel"), vault_aux_kennel, NULL, 45, 4 },
+    { _("動物園", "animal"), vault_aux_animal, NULL, 35, 5 },
+    { _("教会", "chapel"), vault_aux_chapel_g, NULL, 75, 4 },
+    { _("アンデッド", "undead"), vault_aux_undead, NULL, 75, 5 },
+};
+
+/*!
  * @brief タイプ5の部屋…nestを生成する / Type 5 -- Monster nests
  * @param player_ptr プレーヤーへの参照ポインタ
  * @return なし
@@ -227,7 +225,7 @@ bool build_type5(player_type *player_ptr, dun_data_type *dd_ptr)
 
     floor_type *floor_ptr = player_ptr->current_floor_ptr;
     int cur_nest_type = pick_vault_type(floor_ptr, nest_types, d_info[floor_ptr->dungeon_idx].nest);
-    vault_aux_type *n_ptr;
+    nest_pit_type *n_ptr;
 
     /* No type available */
     if (cur_nest_type < 0)
@@ -390,6 +388,22 @@ bool build_type5(player_type *player_ptr, dun_data_type *dd_ptr)
 }
 
 /*!
+ * @brief 生成するPitの情報テーブル
+ */
+std::vector<nest_pit_type> pit_types = {
+    { _("オーク", "orc"), vault_aux_orc, NULL, 5, 6 },
+    { _("トロル", "troll"), vault_aux_troll, NULL, 20, 6 },
+    { _("巨人", "giant"), vault_aux_giant, NULL, 50, 6 },
+    { _("狂気", "lovecraftian"), vault_aux_cthulhu, NULL, 80, 2 },
+    { _("シンボル(善)", "symbol good"), vault_aux_symbol_g, vault_prep_symbol, 70, 1 },
+    { _("シンボル(悪)", "symbol evil"), vault_aux_symbol_e, vault_prep_symbol, 70, 1 },
+    { _("教会", "chapel"), vault_aux_chapel_g, NULL, 65, 2 },
+    { _("ドラゴン", "dragon"), vault_aux_dragon, vault_prep_dragon, 70, 6 },
+    { _("デーモン", "demon"), vault_aux_demon, NULL, 80, 6 },
+    { _("ダークエルフ", "dark elf"), vault_aux_dark_elf, NULL, 45, 4 },
+};
+
+/*!
  * @brief タイプ6の部屋…pitを生成する / Type 6 -- Monster pits
  * @return なし
  * @details
@@ -439,7 +453,7 @@ bool build_type6(player_type *player_ptr, dun_data_type *dd_ptr)
 
     floor_type *floor_ptr = player_ptr->current_floor_ptr;
     int cur_pit_type = pick_vault_type(floor_ptr, pit_types, d_info[floor_ptr->dungeon_idx].pit);
-    vault_aux_type *n_ptr;
+    nest_pit_type *n_ptr;
 
     /* No type available */
     if (cur_pit_type < 0)
@@ -638,8 +652,33 @@ bool build_type6(player_type *player_ptr, dun_data_type *dd_ptr)
     return TRUE;
 }
 
-/*
- * Helper function for "trapped monster pit"
+// clang-format off
+/*!
+ * @brief 開門トラップのモンスター配置テーブル
+ * @detail
+ * 中央からの相対座標(X,Y)、モンスターの強さ
+ */
+const int place_table_trapped_pit[TRAPPED_PIT_MONSTER_PLACE_MAX][3] = {
+    { -2, -9, 0 }, { -2, -8, 0 }, { -3, -7, 0 }, { -3, -6, 0 }, { +2, -9, 0 }, { +2, -8, 0 }, { +3, -7, 0 }, { +3, -6, 0 },
+    { -2, +9, 0 }, { -2, +8, 0 }, { -3, +7, 0 }, { -3, +6, 0 }, { +2, +9, 0 }, { +2, +8, 0 }, { +3, +7, 0 }, { +3, +6, 0 },
+    { -2, -7, 1 }, { -3, -5, 1 }, { -3, -4, 1 }, { -2, +7, 1 }, { -3, +5, 1 }, { -3, +4, 1 },
+    { +2, -7, 1 }, { +3, -5, 1 }, { +3, -4, 1 }, { +2, +7, 1 }, { +3, +5, 1 }, { +3, +4, 1 },
+    { -2, -6, 2 }, { -2, -5, 2 }, { -3, -3, 2 }, { -2, +6, 2 }, { -2, +5, 2 }, { -3, +3, 2 },
+    { +2, -6, 2 }, { +2, -5, 2 }, { +3, -3, 2 }, { +2, +6, 2 }, { +2, +5, 2 }, { +3, +3, 2 },
+    { -2, -4, 3 }, { -3, -2, 3 }, { -2, +4, 3 }, { -3, +2, 3 },
+    { +2, -4, 3 }, { +3, -2, 3 }, { +2, +4, 3 }, { +3, +2, 3 },
+    { -2, -3, 4 }, { -3, -1, 4 }, { +2, -3, 4 }, { +3, -1, 4 },
+    { -2, +3, 4 }, { -3, +1, 4 }, { +2, +3, 4 }, { +3, +1, 4 },
+    { -2, -2, 5 }, { -3, 0, 5 }, { -2, +2, 5 }, { +2, -2, 5 }, { +3, 0, 5 }, { +2, +2, 5 },
+    { -2, -1, 6 }, { -2, +1, 6 }, { +2, -1, 6 }, { +2, +1, 6 },
+    { -2, 0, 7 }, { +2, 0, 7 },
+    { 0, 0, -1 } };
+// clang-format on
+
+/*!
+ * @brief 開門トラップに配置するモンスターの条件フィルタ
+ * @detai;
+ * 穴を掘るモンスター、壁を抜けるモンスターは却下
  */
 static bool vault_aux_trapped_pit(player_type *player_ptr, MONRACE_IDX r_idx)
 {
@@ -659,7 +698,7 @@ static bool vault_aux_trapped_pit(player_type *player_ptr, MONRACE_IDX r_idx)
 }
 
 /*!
- * @brief タイプ13の部屋…トラップpitの生成 / Type 13 -- Trapped monster pits
+ * @brief タイプ13の部屋…開門トラップpitの生成 / Type 13 -- Trapped monster pits
  * @return なし
  * @details
  * A trapped monster pit is a "big" room with a straight corridor in\n
@@ -716,7 +755,7 @@ bool build_type13(player_type *player_ptr, dun_data_type *dd_ptr)
 
     floor_type *floor_ptr = player_ptr->current_floor_ptr;
     int cur_pit_type = pick_vault_type(floor_ptr, pit_types, d_info[floor_ptr->dungeon_idx].pit);
-    vault_aux_type *n_ptr;
+    nest_pit_type *n_ptr;
 
     /* Only in Angband */
     if (floor_ptr->dungeon_idx != DUNGEON_ANGBAND)
@@ -889,10 +928,10 @@ bool build_type13(player_type *player_ptr, dun_data_type *dd_ptr)
         }
     }
 
-    for (i = 0; placing[i][2] >= 0; i++) {
-        y = yval + placing[i][0];
-        x = xval + placing[i][1];
-        place_monster_aux(player_ptr, 0, y, x, what[placing[i][2]], PM_NO_KAGE);
+    for (i = 0; place_table_trapped_pit[i][2] >= 0; i++) {
+        y = yval + place_table_trapped_pit[i][0];
+        x = xval + place_table_trapped_pit[i][1];
+        place_monster_aux(player_ptr, 0, y, x, what[place_table_trapped_pit[i][2]], PM_NO_KAGE);
     }
 
     return TRUE;
index 231ce3c..508f891 100644 (file)
@@ -2,11 +2,51 @@
 
 #include "system/angband.h"
 
+#define NUM_NEST_MON_TYPE 64 /*!<nestの種別数 */
+#define TRAPPED_PIT_MONSTER_PLACE_MAX 69
+
+/*! nestのID定義 /  Nest types code */
+enum nest_type : int {
+    NEST_TYPE_CLONE = 0,
+    NEST_TYPE_JELLY = 1,
+    NEST_TYPE_SYMBOL_GOOD = 2,
+    NEST_TYPE_SYMBOL_EVIL = 3,
+    NEST_TYPE_MIMIC = 4,
+    NEST_TYPE_LOVECRAFTIAN = 5,
+    NEST_TYPE_KENNEL = 6,
+    NEST_TYPE_ANIMAL = 7,
+    NEST_TYPE_CHAPEL = 8,
+    NEST_TYPE_UNDEAD = 9,
+};
+
+/*! pitのID定義 / Pit types code */
+enum pit_type : int {
+    PIT_TYPE_ORC = 0,
+    PIT_TYPE_TROLL = 1,
+    PIT_TYPE_GIANT = 2,
+    PIT_TYPE_LOVECRAFTIAN = 3,
+    PIT_TYPE_SYMBOL_GOOD = 4,
+    PIT_TYPE_SYMBOL_EVIL = 5,
+    PIT_TYPE_CHAPEL = 6,
+    PIT_TYPE_DRAGON = 7,
+    PIT_TYPE_DEMON = 8,
+    PIT_TYPE_DARK_ELF = 9,
+};
+
+/*! pit/nest型情報の構造体定義 */
+struct nest_pit_type {
+    concptr name; //<! 部屋名
+    bool (*hook_func)(player_type *player_ptr, MONRACE_IDX r_idx); //<! モンスターフィルタ関数
+    void (*prep_func)(player_type *player_ptr); //<! 能力フィルタ関数
+    DEPTH level; //<! 相当階
+    int chance; //!< 生成確率
+};
+
 /*! デバッグ時にnestのモンスター情報を確認するための構造体 / A struct for nest monster information with cheat_hear */
-typedef struct nest_mon_info_type {
-       MONRACE_IDX r_idx;
-       bool used;
-} nest_mon_info_type;
+struct nest_mon_info_type {
+    MONRACE_IDX r_idx; //!< モンスター種族ID
+    bool used; //!< 既に選んだかどうか
+};
 
 typedef struct dun_data_type dun_data_type;
 bool build_type5(player_type *player_ptr, dun_data_type *dd_ptr);
index d672bee..af19101 100644 (file)
@@ -225,7 +225,7 @@ void wr_player(player_type *creature_ptr)
     wr_s32b(current_world_ptr->game_turn);
     wr_s32b(current_world_ptr->dungeon_turn);
     wr_s32b(current_world_ptr->arena_start_turn);
-    wr_s16b(today_mon);
+    wr_s16b(current_world_ptr->today_mon);
     wr_s16b(creature_ptr->today_mon);
     wr_s16b(creature_ptr->riding);
     wr_s16b(creature_ptr->floor_id);
index 1360dbd..4b0ec60 100644 (file)
@@ -1,4 +1,5 @@
-#include "specific-object/torch.h"
+#include <vector>
+
 #include "core/player-update-types.h"
 #include "dungeon/dungeon-flag-types.h"
 #include "dungeon/dungeon.h"
@@ -9,8 +10,9 @@
 #include "object-enchant/tr-types.h"
 #include "object/object-flags.h"
 #include "player/special-defense-types.h"
-#include "system/floor-type-definition.h"
+#include "specific-object/torch.h"
 #include "sv-definition/sv-lite-types.h"
+#include "system/floor-type-definition.h"
 #include "util/bit-flags-calculator.h"
 
 /*!
@@ -94,7 +96,6 @@ void update_lite_radius(player_type *creature_ptr)
             }
         }
 
-
         POSITION rad = 0;
         if (has_flag(flgs, TR_LITE_1) && !has_flag(flgs, TR_DARK_SOURCE))
             rad += 1;
@@ -165,17 +166,31 @@ void update_lite_radius(player_type *creature_ptr)
  */
 void update_lite(player_type *subject_ptr)
 {
+    struct Point {
+        int y;
+        int x;
+        Point(const int y, const int x)
+            : y(y)
+            , x(x)
+        {
+        }
+    };
+
+    // 前回照らされていた座標たちを格納する配列。
+    std::vector<Point> points;
+
     POSITION p = subject_ptr->cur_lite;
-    grid_type *g_ptr;
-    floor_type *floor_ptr = subject_ptr->current_floor_ptr;
+    floor_type *const floor_ptr = subject_ptr->current_floor_ptr;
+
+    // 前回照らされていた座標たちを記録。
     for (int i = 0; i < floor_ptr->lite_n; i++) {
-        POSITION y = floor_ptr->lite_y[i];
-        POSITION x = floor_ptr->lite_x[i];
+        const POSITION y = floor_ptr->lite_y[i];
+        const POSITION x = floor_ptr->lite_x[i];
+
         floor_ptr->grid_array[y][x].info &= ~(CAVE_LITE);
         floor_ptr->grid_array[y][x].info |= CAVE_TEMP;
-        tmp_pos.y[tmp_pos.n] = y;
-        tmp_pos.x[tmp_pos.n] = x;
-        tmp_pos.n++;
+
+        points.emplace_back(y, x);
     }
 
     floor_ptr->lite_n = 0;
@@ -270,17 +285,16 @@ void update_lite(player_type *subject_ptr)
     for (int i = 0; i < floor_ptr->lite_n; i++) {
         POSITION y = floor_ptr->lite_y[i];
         POSITION x = floor_ptr->lite_x[i];
-        g_ptr = &floor_ptr->grid_array[y][x];
+        grid_type *g_ptr = &floor_ptr->grid_array[y][x];
         if (g_ptr->info & CAVE_TEMP)
             continue;
 
         cave_note_and_redraw_later(floor_ptr, g_ptr, y, x);
     }
 
-    for (int i = 0; i < tmp_pos.n; i++) {
-        POSITION y = tmp_pos.y[i];
-        POSITION x = tmp_pos.x[i];
-        g_ptr = &floor_ptr->grid_array[y][x];
+    // 前回照らされていた座標たちのうち、状態が変わったものについて再描画フラグを立てる。
+    for (const auto &[y, x] : points) {
+        grid_type *g_ptr = &floor_ptr->grid_array[y][x];
         g_ptr->info &= ~(CAVE_TEMP);
         if (g_ptr->info & CAVE_LITE)
             continue;
@@ -288,6 +302,5 @@ void update_lite(player_type *subject_ptr)
         cave_redraw_later(floor_ptr, g_ptr, y, x);
     }
 
-    tmp_pos.n = 0;
     subject_ptr->update |= PU_DELAY_VIS;
 }
index 6a56a46..38d8cb7 100644 (file)
@@ -35,7 +35,7 @@ bool fire_ball(player_type *caster_ptr, EFFECT_ID typ, DIRECTION dir, HIT_POINT
         ty = target_row;
     }
 
-    return project(caster_ptr, 0, rad, ty, tx, dam, typ, flg, -1);
+    return project(caster_ptr, 0, rad, ty, tx, dam, typ, flg, -1).notice;
 }
 
 /*!
@@ -53,7 +53,10 @@ bool fire_ball(player_type *caster_ptr, EFFECT_ID typ, DIRECTION dir, HIT_POINT
  * Affect grids, objects, and monsters
  * </pre>
  */
-bool fire_breath(player_type *caster_ptr, EFFECT_ID typ, DIRECTION dir, HIT_POINT dam, POSITION rad) { return fire_ball(caster_ptr, typ, dir, dam, -rad); }
+bool fire_breath(player_type *caster_ptr, EFFECT_ID typ, DIRECTION dir, HIT_POINT dam, POSITION rad)
+{
+    return fire_ball(caster_ptr, typ, dir, dam, -rad);
+}
 
 /*!
  * @brief ロケット系スペルの発動(詳細な差は確認中) / Cast a ball spell
@@ -80,7 +83,7 @@ bool fire_rocket(player_type *caster_ptr, EFFECT_ID typ, DIRECTION dir, HIT_POIN
     }
 
     BIT_FLAGS flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
-    return (project(caster_ptr, 0, rad, ty, tx, dam, typ, flg, -1));
+    return project(caster_ptr, 0, rad, ty, tx, dam, typ, flg, -1).notice;
 }
 
 /*!
@@ -109,7 +112,7 @@ bool fire_ball_hide(player_type *caster_ptr, EFFECT_ID typ, DIRECTION dir, HIT_P
         ty = target_row;
     }
 
-    return (project(caster_ptr, 0, rad, ty, tx, dam, typ, flg, -1));
+    return project(caster_ptr, 0, rad, ty, tx, dam, typ, flg, -1).notice;
 }
 
 /*!
@@ -134,7 +137,7 @@ bool fire_ball_hide(player_type *caster_ptr, EFFECT_ID typ, DIRECTION dir, HIT_P
 bool fire_meteor(player_type *caster_ptr, MONSTER_IDX who, EFFECT_ID typ, POSITION y, POSITION x, HIT_POINT dam, POSITION rad)
 {
     BIT_FLAGS flg = PROJECT_STOP | PROJECT_GRID | PROJECT_ITEM | PROJECT_KILL;
-    return (project(caster_ptr, who, rad, y, x, dam, typ, flg, -1));
+    return project(caster_ptr, who, rad, y, x, dam, typ, flg, -1).notice;
 }
 
 /*!
@@ -177,9 +180,9 @@ bool fire_blast(player_type *caster_ptr, EFFECT_ID typ, DIRECTION dir, DICE_NUMB
         }
 
         /* Analyze the "dir" and the "target". */
-        if (!project(caster_ptr, 0, 0, y, x, damroll(dd, ds), typ, flg, -1)) {
+        const auto proj_res = project(caster_ptr, 0, 0, y, x, damroll(dd, ds), typ, flg, -1);
+        if (!proj_res.notice)
             result = FALSE;
-        }
     }
 
     return result;
@@ -267,5 +270,5 @@ bool project_hook(player_type *caster_ptr, EFFECT_ID typ, DIRECTION dir, HIT_POI
         ty = target_row;
     }
 
-    return (project(caster_ptr, 0, 0, ty, tx, dam, typ, flg, -1));
+    return project(caster_ptr, 0, 0, ty, tx, dam, typ, flg, -1).notice;
 }
index 4d7b756..db4ac30 100644 (file)
@@ -1,4 +1,5 @@
-#include "spell-kind/spells-lite.h"
+#include <vector>
+
 #include "dungeon/dungeon-flag-types.h"
 #include "dungeon/dungeon.h"
 #include "effect/effect-characteristics.h"
@@ -16,6 +17,7 @@
 #include "monster/monster-update.h"
 #include "player/special-defense-types.h"
 #include "spell-kind/spells-launcher.h"
+#include "spell-kind/spells-lite.h"
 #include "spell/spell-types.h"
 #include "system/floor-type-definition.h"
 #include "target/projection-path-calculator.h"
 #include "view/display-messages.h"
 #include "world/world.h"
 
+struct Point {
+    int y;
+    int x;
+    Point(const int y, const int x)
+        : y(y)
+        , x(x)
+    {
+    }
+};
+
+using PassBoldFunc = bool (*)(floor_type *, POSITION, POSITION);
+
 /*!
  * todo この辺、xとyが引数になっているが、caster_ptr->xとcaster_ptr->yで全て置き換えが効くはず……
- * @brief 部屋全体を照らすサブルーチン
+ * @brief 指定した座標全てを照らす。
  * @param caster_ptr プレーヤーへの参照ポインタ
- * @return なし
+ * @param points 明るくすべき座標たち
  * @details
  * <pre>
  * This routine clears the entire "temp" set.
  * STUPID monsters wake up 1/10 the time when illuminated
  * </pre>
  */
-static void cave_temp_room_lite(player_type *caster_ptr)
+static void cave_temp_room_lite(player_type *caster_ptr, const std::vector<Point> &points)
 {
-    for (int i = 0; i < tmp_pos.n; i++) {
-        POSITION y = tmp_pos.y[i];
-        POSITION x = tmp_pos.x[i];
+    for (const auto &point : points) {
+        const POSITION y = point.y;
+        const POSITION x = point.x;
+
         grid_type *g_ptr = &caster_ptr->current_floor_ptr->grid_array[y][x];
         g_ptr->info &= ~(CAVE_TEMP);
         g_ptr->info |= (CAVE_GLOW);
@@ -73,15 +88,13 @@ static void cave_temp_room_lite(player_type *caster_ptr)
         lite_spot(caster_ptr, y, x);
         update_local_illumination(caster_ptr, y, x);
     }
-
-    tmp_pos.n = 0;
 }
 
 /*!
  * todo この辺、xとyが引数になっているが、caster_ptr->xとcaster_ptr->yで全て置き換えが効くはず……
- * @brief 部屋全体を暗くするサブルーチン
+ * @brief 指定した座標全てを暗くする。
  * @param caster_ptr プレーヤーへの参照ポインタ
- * @return なし
+ * @param points 暗くすべき座標たち
  * @details
  * <pre>
  * This routine clears the entire "temp" set.
@@ -91,11 +104,12 @@ static void cave_temp_room_lite(player_type *caster_ptr)
  * Also, process all affected monsters
  * </pre>
  */
-static void cave_temp_room_unlite(player_type *caster_ptr)
+static void cave_temp_room_unlite(player_type *caster_ptr, const std::vector<Point> &points)
 {
-    for (int i = 0; i < tmp_pos.n; i++) {
-        POSITION y = tmp_pos.y[i];
-        POSITION x = tmp_pos.x[i];
+    for (const auto &point : points) {
+        const POSITION y = point.y;
+        const POSITION x = point.x;
+
         grid_type *g_ptr = &caster_ptr->current_floor_ptr->grid_array[y][x];
         bool do_dark = !is_mirror_grid(g_ptr);
         g_ptr->info &= ~(CAVE_TEMP);
@@ -135,8 +149,6 @@ static void cave_temp_room_unlite(player_type *caster_ptr)
         lite_spot(caster_ptr, y, x);
         update_local_illumination(caster_ptr, y, x);
     }
-
-    tmp_pos.n = 0;
 }
 
 /*!
@@ -147,7 +159,7 @@ static void cave_temp_room_unlite(player_type *caster_ptr)
  * @param pass_bold 地形条件を返す関数ポインタ
  * @return 該当地形の数
  */
-static int next_to_open(floor_type *floor_ptr, POSITION cy, POSITION cx, bool (*pass_bold)(floor_type *, POSITION, POSITION))
+static int next_to_open(floor_type *floor_ptr, const POSITION cy, const POSITION cx, const PassBoldFunc pass_bold)
 {
     int len = 0;
     int blen = 0;
@@ -176,7 +188,7 @@ static int next_to_open(floor_type *floor_ptr, POSITION cy, POSITION cx, bool (*
  * @param pass_bold 地形条件を返す関数ポインタ
  * @return 該当地形の数
  */
-static int next_to_walls_adj(floor_type *floor_ptr, POSITION cy, POSITION cx, bool (*pass_bold)(floor_type *, POSITION, POSITION))
+static int next_to_walls_adj(floor_type *floor_ptr, const POSITION cy, const POSITION cx, const PassBoldFunc pass_bold)
 {
     POSITION y, x;
     int c = 0;
@@ -192,19 +204,21 @@ static int next_to_walls_adj(floor_type *floor_ptr, POSITION cy, POSITION cx, bo
 }
 
 /*!
- * @brief 部屋内にある一点の周囲に該当する地形数かいくつあるかをグローバル変数tmp_pos.nに返す / Aux function -- see below
+ * @brief (y,x) が指定条件を満たすなら points に加える
  * @param caster_ptr プレーヤーへの参照ポインタ
+ * @param points 座標記録用配列
  * @param y 部屋内のy座標1点
  * @param x 部屋内のx座標1点
  * @param only_room 部屋内地形のみをチェック対象にするならば TRUE
  * @param pass_bold 地形条件を返す関数ポインタ
- * @return 該当地形の数
  */
-static void cave_temp_room_aux(player_type *caster_ptr, POSITION y, POSITION x, bool only_room, bool (*pass_bold)(floor_type *, POSITION, POSITION))
+static void cave_temp_room_aux(
+    player_type *caster_ptr, std::vector<Point> &points, const POSITION y, const POSITION x, const bool only_room, const PassBoldFunc pass_bold)
 {
-    grid_type *g_ptr;
     floor_type *floor_ptr = caster_ptr->current_floor_ptr;
-    g_ptr = &floor_ptr->grid_array[y][x];
+    grid_type *g_ptr = &floor_ptr->grid_array[y][x];
+
+    // 既に points に追加済みなら何もしない。
     if (g_ptr->info & (CAVE_TEMP))
         return;
 
@@ -230,23 +244,22 @@ static void cave_temp_room_aux(player_type *caster_ptr, POSITION y, POSITION x,
             return;
     }
 
-    if (tmp_pos.n == TEMP_MAX)
-        return;
-
+    // (y,x) を points に追加し、追加済みフラグを立てる。
+    points.emplace_back(y, x);
     g_ptr->info |= (CAVE_TEMP);
-    tmp_pos.y[tmp_pos.n] = y;
-    tmp_pos.x[tmp_pos.n] = x;
-    tmp_pos.n++;
 }
 
 /*!
- * @brief 部屋内にある一点の周囲がいくつ光を通すかをグローバル変数tmp_pos.nに返す / Aux function -- see below
+ * @brief (y,x) が明るくすべきマスなら points に加える
  * @param caster_ptr プレーヤーへの参照ポインタ
+ * @param points 座標記録用配列
  * @param y 指定Y座標
  * @param x 指定X座標
- * @return なし
  */
-static void cave_temp_lite_room_aux(player_type *caster_ptr, POSITION y, POSITION x) { cave_temp_room_aux(caster_ptr, y, x, FALSE, cave_los_bold); }
+static void cave_temp_lite_room_aux(player_type *caster_ptr, std::vector<Point> &points, const POSITION y, const POSITION x)
+{
+    cave_temp_room_aux(caster_ptr, points, y, x, FALSE, cave_los_bold);
+}
 
 /*!
  * @brief 指定のマスが光を通さず射線のみを通すかを返す。 / Aux function -- see below
@@ -258,44 +271,57 @@ static void cave_temp_lite_room_aux(player_type *caster_ptr, POSITION y, POSITIO
 static bool cave_pass_dark_bold(floor_type *floor_ptr, POSITION y, POSITION x) { return cave_has_flag_bold(floor_ptr, y, x, FF_PROJECT); }
 
 /*!
- * @brief 部屋内にある一点の周囲がいくつ射線を通すかをグローバル変数tmp_pos.nに返す / Aux function -- see below
+ * @brief (y,x) が暗くすべきマスなら points に加える
  * @param caster_ptr プレーヤーへの参照ポインタ
  * @param y 指定Y座標
  * @param x 指定X座標
- * @return なし
  */
-static void cave_temp_unlite_room_aux(player_type *caster_ptr, POSITION y, POSITION x) { cave_temp_room_aux(caster_ptr, y, x, TRUE, cave_pass_dark_bold); }
+static void cave_temp_unlite_room_aux(player_type *caster_ptr, std::vector<Point> &points, const POSITION y, const POSITION x)
+{
+    cave_temp_room_aux(caster_ptr, points, y, x, TRUE, cave_pass_dark_bold);
+}
 
 /*!
- * @brief 指定された部屋内を照らす / Illuminate any room containing the given location.
+ * @brief (y1,x1) を含む全ての部屋を照らす。 / Illuminate any room containing the given location.
  * @param caster_ptr プレーヤーへの参照ポインタ
  * @param y1 指定Y座標
  * @param x1 指定X座標
- * @return なし
+ *
+ * NOTE: 部屋に限らないかも?
  */
-void lite_room(player_type *caster_ptr, POSITION y1, POSITION x1)
+void lite_room(player_type *caster_ptr, const POSITION y1, const POSITION x1)
 {
-    cave_temp_lite_room_aux(caster_ptr, y1, x1);
+    // 明るくするマスを記録する配列。
+    std::vector<Point> points;
+
     floor_type *floor_ptr = caster_ptr->current_floor_ptr;
-    for (int i = 0; i < tmp_pos.n; i++) {
-        POSITION x = tmp_pos.x[i];
-        POSITION y = tmp_pos.y[i];
+
+    // (y1,x1) を起点として明るくするマスを記録していく。
+    // 実質幅優先探索。
+    cave_temp_lite_room_aux(caster_ptr, points, y1, x1);
+    for (size_t i = 0; i < size(points); i++) {
+        const auto &point = points[i];
+        const POSITION y = point.y;
+        const POSITION x = point.x;
 
         if (!cave_los_bold(floor_ptr, y, x))
             continue;
 
-        cave_temp_lite_room_aux(caster_ptr, y + 1, x);
-        cave_temp_lite_room_aux(caster_ptr, y - 1, x);
-        cave_temp_lite_room_aux(caster_ptr, y, x + 1);
-        cave_temp_lite_room_aux(caster_ptr, y, x - 1);
+        cave_temp_lite_room_aux(caster_ptr, points, y + 1, x);
+        cave_temp_lite_room_aux(caster_ptr, points, y - 1, x);
+        cave_temp_lite_room_aux(caster_ptr, points, y, x + 1);
+        cave_temp_lite_room_aux(caster_ptr, points, y, x - 1);
 
-        cave_temp_lite_room_aux(caster_ptr, y + 1, x + 1);
-        cave_temp_lite_room_aux(caster_ptr, y - 1, x - 1);
-        cave_temp_lite_room_aux(caster_ptr, y - 1, x + 1);
-        cave_temp_lite_room_aux(caster_ptr, y + 1, x - 1);
+        cave_temp_lite_room_aux(caster_ptr, points, y + 1, x + 1);
+        cave_temp_lite_room_aux(caster_ptr, points, y - 1, x - 1);
+        cave_temp_lite_room_aux(caster_ptr, points, y - 1, x + 1);
+        cave_temp_lite_room_aux(caster_ptr, points, y + 1, x - 1);
     }
 
-    cave_temp_room_lite(caster_ptr);
+    // 記録したマスを実際に明るくする。
+    cave_temp_room_lite(caster_ptr, points);
+
+    // 超隠密状態の更新。
     if (caster_ptr->special_defense & NINJA_S_STEALTH) {
         if (floor_ptr->grid_array[caster_ptr->y][caster_ptr->x].info & CAVE_GLOW)
             set_superstealth(caster_ptr, FALSE);
@@ -303,33 +329,42 @@ void lite_room(player_type *caster_ptr, POSITION y1, POSITION x1)
 }
 
 /*!
- * @brief 指定された部屋内を暗くする / Darken all rooms containing the given location
+ * @brief (y1,x1) を含む全ての部屋を暗くする。 / Darken all rooms containing the given location
  * @param caster_ptr プレーヤーへの参照ポインタ
  * @param y1 指定Y座標
  * @param x1 指定X座標
- * @return なし
  */
-void unlite_room(player_type *caster_ptr, POSITION y1, POSITION x1)
+void unlite_room(player_type *caster_ptr, const POSITION y1, const POSITION x1)
 {
-    cave_temp_unlite_room_aux(caster_ptr, y1, x1);
-    for (int i = 0; i < tmp_pos.n; i++) {
-        POSITION x = tmp_pos.x[i];
-        POSITION y = tmp_pos.y[i];
-        if (!cave_pass_dark_bold(caster_ptr->current_floor_ptr, y, x))
+    // 暗くするマスを記録する配列。
+    std::vector<Point> points;
+
+    floor_type *floor_ptr = caster_ptr->current_floor_ptr;
+
+    // (y1,x1) を起点として暗くするマスを記録していく。
+    // 実質幅優先探索。
+    cave_temp_unlite_room_aux(caster_ptr, points, y1, x1);
+    for (size_t i = 0; i < size(points); i++) {
+        const auto &point = points[i];
+        const POSITION y = point.y;
+        const POSITION x = point.x;
+
+        if (!cave_pass_dark_bold(floor_ptr, y, x))
             continue;
 
-        cave_temp_unlite_room_aux(caster_ptr, y + 1, x);
-        cave_temp_unlite_room_aux(caster_ptr, y - 1, x);
-        cave_temp_unlite_room_aux(caster_ptr, y, x + 1);
-        cave_temp_unlite_room_aux(caster_ptr, y, x - 1);
+        cave_temp_unlite_room_aux(caster_ptr, points, y + 1, x);
+        cave_temp_unlite_room_aux(caster_ptr, points, y - 1, x);
+        cave_temp_unlite_room_aux(caster_ptr, points, y, x + 1);
+        cave_temp_unlite_room_aux(caster_ptr, points, y, x - 1);
 
-        cave_temp_unlite_room_aux(caster_ptr, y + 1, x + 1);
-        cave_temp_unlite_room_aux(caster_ptr, y - 1, x - 1);
-        cave_temp_unlite_room_aux(caster_ptr, y - 1, x + 1);
-        cave_temp_unlite_room_aux(caster_ptr, y + 1, x - 1);
+        cave_temp_unlite_room_aux(caster_ptr, points, y + 1, x + 1);
+        cave_temp_unlite_room_aux(caster_ptr, points, y - 1, x - 1);
+        cave_temp_unlite_room_aux(caster_ptr, points, y - 1, x + 1);
+        cave_temp_unlite_room_aux(caster_ptr, points, y + 1, x - 1);
     }
 
-    cave_temp_room_unlite(caster_ptr);
+    // 記録したマスを実際に暗くする。
+    cave_temp_room_unlite(caster_ptr, points);
 }
 
 /*!
index c7f8365..d6b14d0 100644 (file)
@@ -19,7 +19,7 @@
 bool door_creation(player_type *caster_ptr, POSITION y, POSITION x)
 {
     BIT_FLAGS flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE;
-    return (project(caster_ptr, 0, 1, y, x, 0, GF_MAKE_DOOR, flg, -1));
+    return project(caster_ptr, 0, 1, y, x, 0, GF_MAKE_DOOR, flg, -1).notice;
 }
 
 /*!
@@ -32,7 +32,7 @@ bool door_creation(player_type *caster_ptr, POSITION y, POSITION x)
 bool trap_creation(player_type *caster_ptr, POSITION y, POSITION x)
 {
     BIT_FLAGS flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE;
-    return (project(caster_ptr, 0, 1, y, x, 0, GF_MAKE_TRAP, flg, -1));
+    return project(caster_ptr, 0, 1, y, x, 0, GF_MAKE_TRAP, flg, -1).notice;
 }
 
 /*!
@@ -43,7 +43,7 @@ bool trap_creation(player_type *caster_ptr, POSITION y, POSITION x)
 bool tree_creation(player_type *caster_ptr, POSITION y, POSITION x)
 {
     BIT_FLAGS flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE;
-    return (project(caster_ptr, 0, 1, y, x, 0, GF_MAKE_TREE, flg, -1));
+    return project(caster_ptr, 0, 1, y, x, 0, GF_MAKE_TREE, flg, -1).notice;
 }
 
 /*!
@@ -54,7 +54,7 @@ bool tree_creation(player_type *caster_ptr, POSITION y, POSITION x)
 bool create_rune_protection_area(player_type *caster_ptr, POSITION y, POSITION x)
 {
     BIT_FLAGS flg = PROJECT_GRID | PROJECT_ITEM;
-    return (project(caster_ptr, 0, 1, y, x, 0, GF_MAKE_RUNE_PROTECTION, flg, -1));
+    return project(caster_ptr, 0, 1, y, x, 0, GF_MAKE_RUNE_PROTECTION, flg, -1).notice;
 }
 
 /*!
@@ -65,7 +65,7 @@ bool create_rune_protection_area(player_type *caster_ptr, POSITION y, POSITION x
 bool wall_stone(player_type *caster_ptr)
 {
     BIT_FLAGS flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE;
-    bool dummy = (project(caster_ptr, 0, 1, caster_ptr->y, caster_ptr->x, 0, GF_STONE_WALL, flg, -1));
+    bool dummy = project(caster_ptr, 0, 1, caster_ptr->y, caster_ptr->x, 0, GF_STONE_WALL, flg, -1).notice;
     caster_ptr->update |= (PU_FLOW);
     caster_ptr->redraw |= (PR_MAP);
     return dummy;
@@ -79,7 +79,7 @@ bool wall_stone(player_type *caster_ptr)
 bool destroy_doors_touch(player_type *caster_ptr)
 {
     BIT_FLAGS flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE;
-    return (project(caster_ptr, 0, 1, caster_ptr->y, caster_ptr->x, 0, GF_KILL_DOOR, flg, -1));
+    return project(caster_ptr, 0, 1, caster_ptr->y, caster_ptr->x, 0, GF_KILL_DOOR, flg, -1).notice;
 }
 
 /*!
@@ -90,7 +90,7 @@ bool destroy_doors_touch(player_type *caster_ptr)
 bool disarm_traps_touch(player_type *caster_ptr)
 {
     BIT_FLAGS flg = PROJECT_GRID | PROJECT_ITEM | PROJECT_HIDE;
-    return (project(caster_ptr, 0, 1, caster_ptr->y, caster_ptr->x, 0, GF_KILL_TRAP, flg, -1));
+    return project(caster_ptr, 0, 1, caster_ptr->y, caster_ptr->x, 0, GF_KILL_TRAP, flg, -1).notice;
 }
 
 /*!
@@ -101,7 +101,7 @@ bool disarm_traps_touch(player_type *caster_ptr)
 bool sleep_monsters_touch(player_type *caster_ptr)
 {
     BIT_FLAGS flg = PROJECT_KILL | PROJECT_HIDE;
-    return (project(caster_ptr, 0, 1, caster_ptr->y, caster_ptr->x, caster_ptr->lev, GF_OLD_SLEEP, flg, -1));
+    return project(caster_ptr, 0, 1, caster_ptr->y, caster_ptr->x, caster_ptr->lev, GF_OLD_SLEEP, flg, -1).notice;
 }
 
 /*!
@@ -115,13 +115,12 @@ bool sleep_monsters_touch(player_type *caster_ptr)
 bool animate_dead(player_type *caster_ptr, MONSTER_IDX who, POSITION y, POSITION x)
 {
     BIT_FLAGS flg = PROJECT_ITEM | PROJECT_HIDE;
-    return (project(caster_ptr, who, 5, y, x, 0, GF_ANIM_DEAD, flg, -1));
+    return project(caster_ptr, who, 5, y, x, 0, GF_ANIM_DEAD, flg, -1).notice;
 }
 
 /*!
  * @brief 周辺破壊効果(プレイヤー中心)
  * @param caster_ptr プレーヤーへの参照ポインタ
- * @return 作用が実際にあった場合TRUEを返す
  */
 void wall_breaker(player_type *caster_ptr)
 {
index 1ea3755..e3f1470 100644 (file)
@@ -67,7 +67,7 @@ bool project_all_los(player_type *caster_ptr, EFFECT_ID typ, HIT_POINT dam)
         POSITION y = m_ptr->fy;
         POSITION x = m_ptr->fx;
 
-        if (project(caster_ptr, 0, 0, y, x, dam, typ, flg, -1))
+        if (project(caster_ptr, 0, 0, y, x, dam, typ, flg, -1).notice)
             obvious = TRUE;
     }
 
index ee26bbe..9492e0e 100644 (file)
@@ -102,5 +102,6 @@ enum spells_type
        GF_CRUSADE = 114,                       /*!< 魔法効果: 聖戦*/
        GF_STASIS_EVIL = 115,           /*!< 魔法効果: 邪悪拘束*/
        GF_WOUNDS = 116,                        /*!< 魔法効果: 創傷*/
-       MAX_GF = 117,                           /*!< 欠番を無視した最大サイズ (直上の値+1) */
+    GF_E_GENOCIDE = 117,               /*!< 魔法効果: 元素抹殺 */
+       MAX_GF = 118,                           /*!< 欠番を無視した最大サイズ (直上の値+1) */
 };
index b2fb752..5792630 100644 (file)
@@ -59,6 +59,7 @@ bool unleash_mana_storm(player_type *creature_ptr, bool powerful)
     is_special_class &= creature_ptr->pclass != CLASS_SORCERER;
     is_special_class &= creature_ptr->pclass != CLASS_MAGIC_EATER;
     is_special_class &= creature_ptr->pclass != CLASS_BLUE_MAGE;
+    is_special_class &= creature_ptr->pclass != CLASS_ELEMENTALIST;
     if (is_special_class)
         (void)take_hit(creature_ptr, DAMAGE_NOESCAPE, 50, _("コントロールし難い強力な魔力の解放", "unleashing magics too mighty to control"), -1);
 
index 665d274..23be762 100644 (file)
@@ -457,27 +457,38 @@ bool fishing(player_type *creature_ptr)
     return TRUE;
 }
 
-bool cosmic_cast_off(player_type *creature_ptr, object_type *o_ptr)
+/*!
+ * @brief 装備を脱ぎ捨てて小宇宙を燃やす
+ * @param creature_ptr プレイヤー情報への参照ポインタ
+ * @param o_ptr_ptr 脱ぐ装備品への参照ポインタのポインタ
+ * @return 脱いだらTRUE、脱がなかったらFALSE
+ * @detail
+ * 脱いで落とした装備にtimeoutを設定するために装備品のアドレスを返す。
+ */
+bool cosmic_cast_off(player_type *creature_ptr, object_type **o_ptr_ptr)
 {
+    object_type *o_ptr = (*o_ptr_ptr);
+
     /* Cast off activated item */
-    INVENTORY_IDX inv;
-    for (inv = INVEN_MAIN_HAND; inv <= INVEN_FEET; inv++) {
-        if (o_ptr == &creature_ptr->inventory_list[inv])
+    INVENTORY_IDX slot;
+    for (slot = INVEN_MAIN_HAND; slot <= INVEN_FEET; slot++) {
+        if (o_ptr == &creature_ptr->inventory_list[slot])
             break;
     }
 
-    if (inv > INVEN_FEET)
+    if (slot > INVEN_FEET)
         return FALSE;
 
     object_type forge;
     object_copy(&forge, o_ptr);
-    inven_item_increase(creature_ptr, inv, (0 - o_ptr->number));
-    inven_item_optimize(creature_ptr, inv);
-    OBJECT_IDX o_idx = drop_near(creature_ptr, &forge, 0, creature_ptr->y, creature_ptr->x);
-    o_ptr = &creature_ptr->current_floor_ptr->o_list[o_idx];
+    inven_item_increase(creature_ptr, slot, (0 - o_ptr->number));
+    inven_item_optimize(creature_ptr, slot);
+
+    OBJECT_IDX old_o_idx = drop_near(creature_ptr, &forge, 0, creature_ptr->y, creature_ptr->x);
+    *o_ptr_ptr = &creature_ptr->current_floor_ptr->o_list[old_o_idx];
 
     GAME_TEXT o_name[MAX_NLEN];
-    describe_flavor(creature_ptr, o_name, o_ptr, OD_NAME_ONLY);
+    describe_flavor(creature_ptr, o_name, &forge, OD_NAME_ONLY);
     msg_format(_("%sを脱ぎ捨てた。", "You cast off %s."), o_name);
 
     /* Get effects */
index 9eea63f..bb37757 100644 (file)
@@ -28,6 +28,6 @@ bool restore_mana(player_type *creature_ptr, bool magic_eater);
 bool restore_all_status(player_type *creature_ptr);
 
 bool fishing(player_type *creature_ptr);
-bool cosmic_cast_off(player_type *creature_ptr, object_type *o_ptr);
+bool cosmic_cast_off(player_type *creature_ptr, object_type **o_ptr_ptr);
 void apply_nexus(monster_type *m_ptr, player_type *target_ptr);
 void status_shuffle(player_type *creature_ptr);
index 41afdc8..a84cff9 100644 (file)
@@ -375,8 +375,11 @@ bool set_shero(player_type *creature_ptr, TIME_EFFECT v, bool do_dec)
     if (creature_ptr->is_dead)
         return FALSE;
 
-    if (creature_ptr->pclass == CLASS_BERSERKER)
+    if (creature_ptr->pclass == CLASS_BERSERKER) {
         v = 1;
+        return FALSE;
+    }
+
     if (v) {
         if (creature_ptr->shero && !do_dec) {
             if (creature_ptr->shero > v)
index 076ad5c..2ff4d3a 100644 (file)
@@ -113,7 +113,7 @@ void display_rumor(player_type *player_ptr, bool ex)
         IDX t_idx;
         while (TRUE) {
             t_idx = rumor_num(zz[1], NO_TOWN);
-            if (town_info[t_idx].name)
+            if (town_info[t_idx].name[0] != '\0')
                 break;
         }
 
index 5fc7082..c309185 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * @brief 
+ * @brief
  * @author
  * Copyright (c) 1997 Ben Harrison, James E. Wilson, Robert A. Koeneke
  * 2002/01/12 mogami
@@ -17,7 +17,6 @@
  *
  * Pass 1 is determined from allocation information
  * Pass 2 is determined from allocation restriction
- * Pass 3 is determined from allocation calculation
  */
 typedef struct alloc_entry {
     KIND_OBJECT_IDX index; /* The actual index */
@@ -25,7 +24,6 @@ typedef struct alloc_entry {
     DEPTH level; /* Base dungeon level */
     PROB prob1; /* Probability, pass 1 */
     PROB prob2; /* Probability, pass 2 */
-    PROB prob3; /* Probability, pass 3 */
 
     u16b total; /* Unused for now */
 } alloc_entry;
index 1aaf3d0..d4efdae 100644 (file)
@@ -19,7 +19,7 @@
 #define FAKE_VER_MAJOR 13 /*!< ゲームのバージョン番号定義(メジャー番号 + 10) */
 #define FAKE_VER_MINOR 0 /*!< ゲームのバージョン番号定義(マイナー番号) */
 #define FAKE_VER_PATCH 0 /*!< ゲームのバージョン番号定義(パッチ番号) */
-#define FAKE_VER_EXTRA 12 /*!< ゲームのバージョン番号定義(エクストラ番号) */
+#define FAKE_VER_EXTRA 13 /*!< ゲームのバージョン番号定義(エクストラ番号) */
 
 /*!
  * @brief バージョンが開発版が安定版かを返す
index de4ccaf..c9a4b5c 100644 (file)
@@ -10,6 +10,7 @@
   * todo どこからも呼ばれていない。main関数辺りに移設するか、そもそもコメントでいいと思われる
   * コピーライト情報 / Link a copyright message into the executable
   */
+/*
 const concptr copyright[5] =
 {
        "Copyright (c) 1989 James E. Wilson, Robert A. Keoneke",
@@ -18,6 +19,7 @@ const concptr copyright[5] =
        "and not for profit purposes provided that this copyright and statement",
        "are included in all such copies."
 };
+*/
 
 concptr ANGBAND_SYS = "xxx";
 concptr ANGBAND_KEYBOARD = _("JAPAN", "0");
index 7280bca..fd3dc4d 100644 (file)
@@ -1,8 +1,9 @@
-#include "target/grid-selector.h"
+#include <vector>
+
 #include "core/player-redraw-types.h"
 #include "core/player-update-types.h"
-#include "core/window-redrawer.h"
 #include "core/stuff-handler.h"
+#include "core/window-redrawer.h"
 #include "floor/cave.h"
 #include "game-option/game-play-options.h"
 #include "game-option/keymap-directory-getter.h"
@@ -12,6 +13,7 @@
 #include "io/input-key-acceptor.h"
 #include "io/screen-util.h"
 #include "system/floor-type-definition.h"
+#include "target/grid-selector.h"
 #include "target/target-checker.h"
 #include "term/screen-processor.h"
 #include "util/int-char-converter.h"
@@ -51,9 +53,8 @@ static bool tgt_pt_accept(player_type *creature_ptr, POSITION y, POSITION x)
  * XAngband: Prepare the "temp" array for "tget_pt"
  * based on target_set_prepare funciton.
  */
-static void tgt_pt_prepare(player_type *creature_ptr)
+static void tgt_pt_prepare(player_type *creature_ptr, std::vector<POSITION> &ys, std::vector<POSITION> &xs)
 {
-    tmp_pos.n = 0;
     if (!expand_list)
         return;
 
@@ -63,13 +64,12 @@ static void tgt_pt_prepare(player_type *creature_ptr)
             if (!tgt_pt_accept(creature_ptr, y, x))
                 continue;
 
-            tmp_pos.x[tmp_pos.n] = x;
-            tmp_pos.y[tmp_pos.n] = y;
-            tmp_pos.n++;
+            ys.emplace_back(y);
+            xs.emplace_back(x);
         }
     }
 
-    ang_sort(creature_ptr, tmp_pos.x, tmp_pos.y, tmp_pos.n, ang_sort_comp_distance, ang_sort_swap_position);
+    ang_sort(creature_ptr, xs.data(), ys.data(), size(ys), ang_sort_comp_distance, ang_sort_swap_position);
 }
 
 /*
@@ -77,19 +77,24 @@ static void tgt_pt_prepare(player_type *creature_ptr)
  */
 bool tgt_pt(player_type *creature_ptr, POSITION *x_ptr, POSITION *y_ptr)
 {
+    // "interesting" な座標たちを記録する配列。
+    // ang_sort() を利用する関係上、y/x 座標それぞれについて配列を作る。
+    std::vector<POSITION> ys;
+    std::vector<POSITION> xs;
+
     TERM_LEN wid, hgt;
     get_screen_size(&wid, &hgt);
 
     POSITION x = creature_ptr->x;
     POSITION y = creature_ptr->y;
     if (expand_list)
-        tgt_pt_prepare(creature_ptr);
+        tgt_pt_prepare(creature_ptr, ys, xs);
 
     msg_print(_("場所を選んでスペースキーを押して下さい。", "Select a point and press space."));
     msg_flag = FALSE;
 
     char ch = 0;
-    int n = 0;
+    size_t n = 0;
     bool success = FALSE;
     while ((ch != ESCAPE) && !success) {
         bool move_fast = FALSE;
@@ -111,20 +116,22 @@ bool tgt_pt(player_type *creature_ptr, POSITION *x_ptr, POSITION *y_ptr)
             break;
         case '>':
         case '<': {
-            if (!expand_list || !tmp_pos.n)
+            if (!expand_list || ys.empty())
                 break;
 
             int dx, dy;
             int cx = (panel_col_min + panel_col_max) / 2;
             int cy = (panel_row_min + panel_row_max) / 2;
             n++;
-            for (; n < tmp_pos.n; ++n) {
-                grid_type *g_ptr = &creature_ptr->current_floor_ptr->grid_array[tmp_pos.y[n]][tmp_pos.x[n]];
+            for (; n < size(ys); ++n) {
+                const POSITION y_cur = ys[n];
+                const POSITION x_cur = xs[n];
+                grid_type *g_ptr = &creature_ptr->current_floor_ptr->grid_array[y_cur][x_cur];
                 if (cave_has_flag_grid(g_ptr, FF_STAIRS) && cave_has_flag_grid(g_ptr, ch == '>' ? FF_MORE : FF_LESS))
                     break;
             }
 
-            if (n == tmp_pos.n) {
+            if (n == size(ys)) {
                 n = 0;
                 y = creature_ptr->y;
                 x = creature_ptr->x;
@@ -134,8 +141,8 @@ bool tgt_pt(player_type *creature_ptr, POSITION *x_ptr, POSITION *y_ptr)
                 creature_ptr->window_flags |= PW_OVERHEAD;
                 handle_stuff(creature_ptr);
             } else {
-                y = tmp_pos.y[n];
-                x = tmp_pos.x[n];
+                y = ys[n];
+                x = xs[n];
                 dy = 2 * (y - cy) / hgt;
                 dx = 2 * (x - cx) / wid;
                 if (dy || dx)
index e15c843..b1f6609 100644 (file)
@@ -1,4 +1,6 @@
-#include "target/target-preparation.h"
+#include <utility>
+#include <vector>
+
 #include "floor/cave.h"
 #include "game-option/input-options.h"
 #include "grid/feature.h"
@@ -12,6 +14,7 @@
 #include "system/floor-type-definition.h"
 #include "system/object-type-definition.h"
 #include "target/projection-path-calculator.h"
+#include "target/target-preparation.h"
 #include "target/target-types.h"
 #include "util/bit-flags-calculator.h"
 #include "util/sort.h"
@@ -98,12 +101,16 @@ static bool target_set_accept(player_type *creature_ptr, POSITION y, POSITION x)
     return FALSE;
 }
 
-/*
- * Prepare the "temp" array for "target_set"
+/*!
+ * @brief "interesting" な座標たちを ys, xs に返す。
+ * @param creature_ptr
+ * @param ys y座標たちを格納する配列 (POSITION 型)
+ * @param xs x座標たちを格納する配列 (POSITION 型)
+ * @param mode
  *
- * Return the number of target_able monsters in the set.
+ * ys, xs は処理開始時にクリアされる。
  */
-void target_set_prepare(player_type *creature_ptr, BIT_FLAGS mode)
+void target_set_prepare(player_type *creature_ptr, std::vector<POSITION> &ys, std::vector<POSITION> &xs, const BIT_FLAGS mode)
 {
     POSITION min_hgt, max_hgt, min_wid, max_wid;
     if (mode & TARGET_KILL) {
@@ -118,7 +125,9 @@ void target_set_prepare(player_type *creature_ptr, BIT_FLAGS mode)
         max_wid = panel_col_max;
     }
 
-    tmp_pos.n = 0;
+    ys.clear();
+    xs.clear();
+
     for (POSITION y = min_hgt; y <= max_hgt; y++) {
         for (POSITION x = min_wid; x <= max_wid; x++) {
             grid_type *g_ptr;
@@ -132,27 +141,25 @@ void target_set_prepare(player_type *creature_ptr, BIT_FLAGS mode)
             if ((mode & (TARGET_KILL)) && !target_pet && is_pet(&creature_ptr->current_floor_ptr->m_list[g_ptr->m_idx]))
                 continue;
 
-            tmp_pos.x[tmp_pos.n] = x;
-            tmp_pos.y[tmp_pos.n] = y;
-            tmp_pos.n++;
+            ys.emplace_back(y);
+            xs.emplace_back(x);
         }
     }
 
     if (mode & (TARGET_KILL)) {
-        ang_sort(creature_ptr, tmp_pos.x, tmp_pos.y, tmp_pos.n, ang_sort_comp_distance, ang_sort_swap_position);
+        ang_sort(creature_ptr, xs.data(), ys.data(), size(ys), ang_sort_comp_distance, ang_sort_swap_position);
     } else {
-        ang_sort(creature_ptr, tmp_pos.x, tmp_pos.y, tmp_pos.n, ang_sort_comp_importance, ang_sort_swap_position);
+        ang_sort(creature_ptr, xs.data(), ys.data(), size(ys), ang_sort_comp_importance, ang_sort_swap_position);
     }
 
-    if (creature_ptr->riding == 0 || !target_pet || (tmp_pos.n <= 1) || !(mode & (TARGET_KILL)))
+    // 乗っているモンスターがターゲットリストの先頭にならないようにする調整。
+
+    if (creature_ptr->riding == 0 || !target_pet || (size(ys) <= 1) || !(mode & (TARGET_KILL)))
         return;
 
-    POSITION tmp = tmp_pos.y[0];
-    tmp_pos.y[0] = tmp_pos.y[1];
-    tmp_pos.y[1] = tmp;
-    tmp = tmp_pos.x[0];
-    tmp_pos.x[0] = tmp_pos.x[1];
-    tmp_pos.x[1] = tmp;
+    // 0 番目と 1 番目を入れ替える。
+    std::swap(ys[0], ys[1]);
+    std::swap(xs[0], xs[1]);
 }
 
 void target_sensing_monsters_prepare(player_type *creature_ptr, std::vector<MONSTER_IDX> &monster_list)
index 5a61fa8..b2a974f 100644 (file)
@@ -1,9 +1,9 @@
-#pragma once
-
-#include "system/angband.h"
+#pragma once
 
 #include <vector>
 
+#include "system/angband.h"
+
 bool target_able(player_type *creature_ptr, MONSTER_IDX m_idx);
-void target_set_prepare(player_type *creature_ptr, BIT_FLAGS mode);
+void target_set_prepare(player_type *creature_ptr, std::vector<POSITION> &ys, std::vector<POSITION> &xs, BIT_FLAGS mode);
 void target_sensing_monsters_prepare(player_type *creature_ptr, std::vector<MONSTER_IDX> &monster_list);
index 424680c..589af5f 100644 (file)
@@ -1,4 +1,5 @@
-#include "target/target-setter.h"
+#include <vector>
+
 #include "core/player-redraw-types.h"
 #include "core/player-update-types.h"
 #include "core/stuff-handler.h"
@@ -17,6 +18,7 @@
 #include "target/target-checker.h"
 #include "target/target-describer.h"
 #include "target/target-preparation.h"
+#include "target/target-setter.h"
 #include "target/target-types.h"
 #include "term/screen-processor.h"
 #include "util/bit-flags-calculator.h"
 #include "window/display-sub-windows.h"
 #include "window/main-window-util.h"
 
+// "interesting" な座標たちを記録する配列。
+// ang_sort() を利用する関係上、y/x座標それぞれについて配列を作る。
+static std::vector<POSITION> ys_interest;
+static std::vector<POSITION> xs_interest;
+
 // Target Setter.
 typedef struct ts_type {
     target_type mode;
     POSITION y;
     POSITION x;
-    POSITION y2;
-    POSITION x2;
+    POSITION y2; // panel_row_min 退避用
+    POSITION x2; // panel_col_min 退避用
     bool done;
-    bool flag;
+    bool flag; // 移動コマンド入力時、"interesting" な座標へ飛ぶかどうか
     char query;
     char info[80];
     grid_type *g_ptr;
     TERM_LEN wid, hgt;
-    int m;
-    int distance;
-    int target_num;
-    bool move_fast;
+    int m; // "interesting" な座標たちのうち現在ターゲットしているもののインデックス
+    int distance; // カーソルの移動方向 (1,2,3,4,6,7,8,9)
+    int target_num; // target_pick() の結果
+    bool move_fast; // カーソル移動を粗くする(1マスずつ移動しない)
 } ts_type;
 
 static ts_type *initialize_target_set_type(player_type *creature_ptr, ts_type *ts_ptr, target_type mode)
@@ -89,32 +96,53 @@ static bool change_panel_xy(player_type *creature_ptr, POSITION y, POSITION x)
     return change_panel(creature_ptr, dy, dx);
 }
 
-/*
- * Help "select" a location (see below)
+/*!
+ * @brief "interesting" な座標たちのうち、(y1,x1) から (dy,dx) 方向にある最も近いもののインデックスを得る。
+ * @param y1 現在地座標y
+ * @param x1 現在地座標x
+ * @param dy 現在地からの向きy [-1,1]
+ * @param dx 現在地からの向きx [-1,1]
+ * @return 最も近い座標のインデックス。適切なものがない場合 -1
  */
-static POSITION_IDX target_pick(POSITION y1, POSITION x1, POSITION dy, POSITION dx)
+static POSITION_IDX target_pick(const POSITION y1, const POSITION x1, const POSITION dy, const POSITION dx)
 {
+    // 最も近いもののインデックスとその距離。
     POSITION_IDX b_i = -1, b_v = 9999;
-    for (POSITION_IDX i = 0; i < tmp_pos.n; i++) {
-        POSITION x2 = tmp_pos.x[i];
-        POSITION y2 = tmp_pos.y[i];
-        POSITION x3 = (x2 - x1);
-        POSITION y3 = (y2 - y1);
+
+    for (POSITION_IDX i = 0; i < (POSITION_IDX)size(ys_interest); i++) {
+        const POSITION x2 = xs_interest[i];
+        const POSITION y2 = ys_interest[i];
+
+        // (y1,x1) から (y2,x2) へ向かうベクトル。
+        const POSITION x3 = (x2 - x1);
+        const POSITION y3 = (y2 - y1);
+
+        // (dy,dx) 方向にないものを除外する。
+
+        // dx > 0 のとき、x3 <= 0 なるものは除外。
+        // dx < 0 のとき、x3 >= 0 なるものは除外。
         if (dx && (x3 * dx <= 0))
             continue;
 
+        // dy > 0 のとき、y3 <= 0 なるものは除外。
+        // dy < 0 のとき、y3 >= 0 なるものは除外。
         if (dy && (y3 * dy <= 0))
             continue;
 
-        POSITION x4 = ABS(x3);
-        POSITION y4 = ABS(y3);
+        const POSITION x4 = ABS(x3);
+        const POSITION y4 = ABS(y3);
+
+        // (dy,dx) が (-1,0) or (1,0) のとき、|x3| > |y3| なるものは除外。
         if (dy && !dx && (x4 > y4))
             continue;
 
+        // (dy,dx) が (0,-1) or (0,1) のとき、|y3| > |x3| なるものは除外。
         if (dx && !dy && (y4 > x4))
             continue;
 
-        POSITION_IDX v = ((x4 > y4) ? (x4 + x4 + y4) : (y4 + y4 + x4));
+        // (y1,x1), (y2,x2) 間の距離を求め、最も近いものを更新する。
+        // 距離の定義は v の式を参照。
+        const POSITION_IDX v = ((x4 > y4) ? (x4 + x4 + y4) : (y4 + y4 + x4));
         if ((b_i >= 0) && (v >= b_v))
             continue;
 
@@ -127,8 +155,8 @@ static POSITION_IDX target_pick(POSITION y1, POSITION x1, POSITION dy, POSITION
 
 static void describe_projectablity(player_type *creature_ptr, ts_type *ts_ptr)
 {
-    ts_ptr->y = tmp_pos.y[ts_ptr->m];
-    ts_ptr->x = tmp_pos.x[ts_ptr->m];
+    ts_ptr->y = ys_interest[ts_ptr->m];
+    ts_ptr->x = xs_interest[ts_ptr->m];
     change_panel_xy(creature_ptr, ts_ptr->y, ts_ptr->x);
     if ((ts_ptr->mode & TARGET_LOOK) == 0)
         print_path(creature_ptr, ts_ptr->y, ts_ptr->x);
@@ -183,7 +211,7 @@ static void switch_target_input(player_type *creature_ptr, ts_type *ts_ptr)
     case ' ':
     case '*':
     case '+':
-        if (++ts_ptr->m != tmp_pos.n)
+        if (++ts_ptr->m != (int)size(ys_interest))
             return;
 
         ts_ptr->m = 0;
@@ -195,7 +223,7 @@ static void switch_target_input(player_type *creature_ptr, ts_type *ts_ptr)
         if (ts_ptr->m-- != 0)
             return;
 
-        ts_ptr->m = tmp_pos.n - 1;
+        ts_ptr->m = (int)size(ys_interest) - 1;
         if (!expand_list)
             ts_ptr->done = TRUE;
 
@@ -206,7 +234,7 @@ static void switch_target_input(player_type *creature_ptr, ts_type *ts_ptr)
         creature_ptr->redraw |= PR_MAP;
         creature_ptr->window_flags |= PW_OVERHEAD;
         handle_stuff(creature_ptr);
-        target_set_prepare(creature_ptr, ts_ptr->mode);
+        target_set_prepare(creature_ptr, ys_interest, xs_interest, ts_ptr->mode);
         ts_ptr->y = creature_ptr->y;
         ts_ptr->x = creature_ptr->x;
     }
@@ -226,7 +254,7 @@ static void switch_target_input(player_type *creature_ptr, ts_type *ts_ptr)
             return;
         }
 
-        if (++ts_ptr->m != tmp_pos.n)
+        if (++ts_ptr->m != (int)size(ys_interest))
             return;
 
         ts_ptr->m = 0;
@@ -238,14 +266,34 @@ static void switch_target_input(player_type *creature_ptr, ts_type *ts_ptr)
     }
 }
 
+/*!
+ * @brief カーソル移動に伴い、描画範囲、"interesting" 座標リスト、現在のターゲットを更新する。
+ * @return カーソル移動によって描画範囲が変化したかどうか
+ */
 static bool check_panel_changed(player_type *creature_ptr, ts_type *ts_ptr)
 {
+    // カーソル移動によって描画範囲が変化しないなら何もせずその旨を返す。
     if (!change_panel(creature_ptr, ddy[ts_ptr->distance], ddx[ts_ptr->distance]))
         return FALSE;
 
-    int v = tmp_pos.y[ts_ptr->m];
-    int u = tmp_pos.x[ts_ptr->m];
-    target_set_prepare(creature_ptr, ts_ptr->mode);
+    // 描画範囲が変化した場合、"interesting" 座標リストおよび現在のターゲットを更新する必要がある。
+
+    // "interesting" 座標を探す起点。
+    // ts_ptr->m が有効な座標を指していればそれを使う。
+    // さもなくば (ts_ptr->y, ts_ptr->x) を使う。
+    int v, u;
+    if (ts_ptr->m < (int)size(ys_interest)) {
+        v = ys_interest[ts_ptr->m];
+        u = xs_interest[ts_ptr->m];
+    } else {
+        v = ts_ptr->y;
+        u = ts_ptr->x;
+    }
+
+    // 新たな描画範囲を用いて "interesting" 座標リストを更新。
+    target_set_prepare(creature_ptr, ys_interest, xs_interest, ts_ptr->mode);
+
+    // 新たな "interesting" 座標リストからターゲットを探す。
     ts_ptr->flag = TRUE;
     ts_ptr->target_num = target_pick(v, u, ddy[ts_ptr->distance], ddx[ts_ptr->distance]);
     if (ts_ptr->target_num >= 0)
@@ -254,10 +302,17 @@ static bool check_panel_changed(player_type *creature_ptr, ts_type *ts_ptr)
     return TRUE;
 }
 
+/*!
+ * @brief カーソル移動方向に "interesting" な座標がなかったとき、画面外まで探す。
+ *
+ * 既に "interesting" な座標を発見している場合、この関数は何もしない。
+ */
 static void sweep_targets(player_type *creature_ptr, ts_type *ts_ptr)
 {
     floor_type *floor_ptr = creature_ptr->current_floor_ptr;
     while (ts_ptr->flag && (ts_ptr->target_num < 0)) {
+        // カーソル移動に伴い、必要なだけ描画範囲を更新。
+        // "interesting" 座標リストおよび現在のターゲットも更新。
         if (check_panel_changed(creature_ptr, ts_ptr))
             continue;
 
@@ -270,7 +325,7 @@ static void sweep_targets(player_type *creature_ptr, ts_type *ts_ptr)
         creature_ptr->redraw |= PR_MAP;
         creature_ptr->window_flags |= PW_OVERHEAD;
         handle_stuff(creature_ptr);
-        target_set_prepare(creature_ptr, ts_ptr->mode);
+        target_set_prepare(creature_ptr, ys_interest, xs_interest, ts_ptr->mode);
         ts_ptr->flag = FALSE;
         ts_ptr->x += dx;
         ts_ptr->y += dy;
@@ -283,7 +338,7 @@ static void sweep_targets(player_type *creature_ptr, ts_type *ts_ptr)
         if ((ts_ptr->y >= panel_row_min + ts_ptr->hgt) || (ts_ptr->y < panel_row_min) || (ts_ptr->x >= panel_col_min + ts_ptr->wid)
             || (ts_ptr->x < panel_col_min)) {
             if (change_panel(creature_ptr, dy, dx))
-                target_set_prepare(creature_ptr, ts_ptr->mode);
+                target_set_prepare(creature_ptr, ys_interest, xs_interest, ts_ptr->mode);
         }
 
         if (ts_ptr->x >= floor_ptr->width - 1)
@@ -300,7 +355,7 @@ static void sweep_targets(player_type *creature_ptr, ts_type *ts_ptr)
 
 static bool set_target_grid(player_type *creature_ptr, ts_type *ts_ptr)
 {
-    if (!ts_ptr->flag || (tmp_pos.n == 0))
+    if (!ts_ptr->flag || ys_interest.empty())
         return FALSE;
 
     describe_projectablity(creature_ptr, ts_ptr);
@@ -319,7 +374,11 @@ static bool set_target_grid(player_type *creature_ptr, ts_type *ts_ptr)
 
     ts_ptr->y2 = panel_row_min;
     ts_ptr->x2 = panel_col_min;
-    ts_ptr->target_num = target_pick(tmp_pos.y[ts_ptr->m], tmp_pos.x[ts_ptr->m], ddy[ts_ptr->distance], ddx[ts_ptr->distance]);
+    {
+        const POSITION y = ys_interest[ts_ptr->m];
+        const POSITION x = xs_interest[ts_ptr->m];
+        ts_ptr->target_num = target_pick(y, x, ddy[ts_ptr->distance], ddx[ts_ptr->distance]);
+    }
     sweep_targets(creature_ptr, ts_ptr);
     ts_ptr->m = ts_ptr->target_num;
     return TRUE;
@@ -358,7 +417,7 @@ static void switch_next_grid_command(player_type *creature_ptr, ts_type *ts_ptr)
         creature_ptr->redraw |= PR_MAP;
         creature_ptr->window_flags |= PW_OVERHEAD;
         handle_stuff(creature_ptr);
-        target_set_prepare(creature_ptr, ts_ptr->mode);
+        target_set_prepare(creature_ptr, ys_interest, xs_interest, ts_ptr->mode);
         ts_ptr->y = creature_ptr->y;
         ts_ptr->x = creature_ptr->x;
     case 'o':
@@ -372,8 +431,10 @@ static void switch_next_grid_command(player_type *creature_ptr, ts_type *ts_ptr)
         ts_ptr->flag = TRUE;
         ts_ptr->m = 0;
         int bd = 999;
-        for (int i = 0; i < tmp_pos.n; i++) {
-            int t = distance(ts_ptr->y, ts_ptr->x, tmp_pos.y[i], tmp_pos.x[i]);
+        for (size_t i = 0; i < size(ys_interest); i++) {
+            const POSITION y = ys_interest[i];
+            const POSITION x = xs_interest[i];
+            int t = distance(ts_ptr->y, ts_ptr->x, y, x);
             if (t < bd) {
                 ts_ptr->m = i;
                 bd = t;
@@ -422,7 +483,7 @@ static void decide_change_panel(player_type *creature_ptr, ts_type *ts_ptr)
     if ((ts_ptr->y >= panel_row_min + ts_ptr->hgt) || (ts_ptr->y < panel_row_min) || (ts_ptr->x >= panel_col_min + ts_ptr->wid)
         || (ts_ptr->x < panel_col_min)) {
         if (change_panel(creature_ptr, dy, dx))
-            target_set_prepare(creature_ptr, ts_ptr->mode);
+            target_set_prepare(creature_ptr, ys_interest, xs_interest, ts_ptr->mode);
     }
 
     floor_type *floor_ptr = creature_ptr->current_floor_ptr;
@@ -473,9 +534,8 @@ bool target_set(player_type *creature_ptr, target_type mode)
     ts_type tmp_ts;
     ts_type *ts_ptr = initialize_target_set_type(creature_ptr, &tmp_ts, mode);
     target_who = 0;
-    target_set_prepare(creature_ptr, mode);
+    target_set_prepare(creature_ptr, ys_interest, xs_interest, mode);
     sweep_target_grids(creature_ptr, ts_ptr);
-    tmp_pos.n = 0;
     prt("", 0, 0);
     verify_panel(creature_ptr);
     set_bits(creature_ptr->update, PU_MONSTERS);
index 8e9c6e9..aa02fbb 100644 (file)
 
 /* Purpose: Memory management routines -BEN- */
 
-#include "term/z-virt.h"
-#include "term/z-util.h"
-
-/*
- * Optional auxiliary "rnfree" function
- */
-vptr (*rnfree_aux)(vptr, huge) = NULL;
-
-/*
- * Free some memory (allocated by ralloc), return NULL
- */
-vptr rnfree(vptr p, huge len)
-{
-       /* Easy to free zero bytes */
-       if (len == 0) return (NULL);
-
-       /* Use the "aux" function */
-       if (rnfree_aux) return ((*rnfree_aux)(p, len));
-
-       /* Use "free" */
-       free ((char*)(p));
-
-       return (NULL);
-}
-
-
-/*
- * Optional auxiliary "rpanic" function
- */
-vptr (*rpanic_aux)(huge) = NULL;
-
-/*
- * The system is out of memory, so panic.  If "rpanic_aux" is set,
- * it can be used to free up some memory and do a new "ralloc()",
- * or if not, it can be used to save things, clean up, and exit.
- * By default, this function simply crashes the computer.
- */
-vptr rpanic(huge len)
-{
-       /* Hopefully, we have a real "panic" function */
-       if (rpanic_aux) return ((*rpanic_aux)(len));
-
-       /* Attempt to crash before icky things happen */
-       core("Out of Memory!");
-       return ((vptr)(NULL));
-}
-
-
-/*
- * Optional auxiliary "ralloc" function
- */
-vptr (*ralloc_aux)(huge) = NULL;
-
-
-/*
- * Allocate some memory
- */
-vptr ralloc(huge len)
-{
-       vptr mem;
-
-       /* Allow allocation of "zero bytes" */
-       if (len == 0) return ((vptr)(NULL));
-
-#ifdef VERBOSE_RALLOC
-
-       /* Count allocated memory */
-       virt_make += len;
-
-       /* Log important allocations */
-       if (len > virt_size)
-       {
-               char buf[80];
-               sprintf(buf, "Make (%ld): %ld - %ld = %ld.",
-                       len, virt_make, virt_kill, virt_make - virt_kill);
-               plog(buf);
-       }
-
-#endif
-
-       /* Use the aux function if set */
-       if (ralloc_aux) mem = (*ralloc_aux)(len);
-
-       /* Use malloc() to allocate some memory */
-       else mem = ((vptr)(malloc((size_t)(len))));
-
-       /* We were able to acquire memory */
-       if (!mem) mem = rpanic(len);
-
-       /* Return the memory, if any */
-       return (mem);
-}
-
-
+#include <cstring>
 
+#include "term/z-virt.h"
 
-/*
- * Allocate a constant string, containing the same thing as 'str'
+/*!
+ * @brief str の複製を返す。戻り値は使用後に string_free() で解放すること。
+ *
+ * nullptr が渡された場合、nullptr を返す。
  */
-concptr string_make(concptr str)
+concptr string_make(const concptr str)
 {
-       huge len = 0;
-       concptr t = str;
-       char *s, *res;
-
-       /* Simple sillyness */
-       if (!str) return (str);
-
-       /* Get the number of chars in the string, including terminator */
-       while (str[len++]) /* loop */;
+    if (!str)
+        return nullptr;
 
-       /* Allocate space for the string */
-       s = res = (char*)(ralloc(len));
+    const auto bufsize = std::strlen(str) + 1;
+    auto *const buf = new char[bufsize];
+    std::strcpy(buf, str);
 
-       /* Copy the string (with terminator) */
-       while ((*s++ = *t++) != 0) /* loop */;
-
-       /* Return the allocated, initialized, string */
-       return (res);
+    return buf;
 }
 
-
-/*
- * Un-allocate a string allocated above.
- * Depends on no changes being made to the string.
+/*!
+ * @brief string_make() で割り当てたバッファを解放する。
+ * @return 常に 0
+ *
+ * nullptr が渡された場合、何もせず 0 を返す。
  */
-errr string_free(concptr str)
+errr string_free(const concptr str)
 {
-       huge len = 0;
-
-       /* Succeed on non-strings */
-       if (!str) return 0;
-
-       /* Count the number of chars in 'str' plus the terminator */
-       while (str[len++]) /* loop */;
+    if (!str)
+        return 0;
 
-       /* Kill the buffer of chars we must have allocated above */
-       (void)rnfree((vptr)(str), len);
+    delete[] str;
 
-       /* Success */
-       return 0;
+    return 0;
 }
index 3fb9f28..94de907 100644 (file)
 
 #include "system/h-basic.h"
 
+#include <type_traits>
+
 /*
  * Memory management routines.
  *
- * Set ralloc_aux to modify the memory allocation routine.
- * Set rnfree_aux to modify the memory de-allocation routine.
- * Set rpanic_aux to let the program react to memory failures.
- *
  * These routines work best as a *replacement* for malloc/free.
  *
  * The string_make() and string_free() routines handle dynamic strings.
  * in particular, that it returns its first argument.
  */
 
-
+//
+// データクリアマクロ WIPE/C_WIPE の実装
+//
+
+// トリビアル型は memset でゼロクリアする
+template <typename T, std::enable_if_t<std::is_trivial_v<T>, std::nullptr_t> = nullptr>
+inline T *c_wipe_impl(T *p, size_t n)
+{
+    return static_cast<T *>(memset(p, 0, sizeof(T) * n));
+}
+template <typename T, std::enable_if_t<std::is_trivial_v<T>, std::nullptr_t> = nullptr>
+inline T *wipe_impl(T *p)
+{
+    return static_cast<T *>(memset(p, 0, sizeof(T)));
+}
+
+// 非トリビアル型はデフォルトコンストラクタで生成したオブジェクトをコピーする
+template <typename T, std::enable_if_t<!std::is_trivial_v<T>, std::nullptr_t> = nullptr>
+inline T *c_wipe_impl(T *p, size_t n)
+{
+    T obj{};
+    for (size_t i = 0; i < n; i++) {
+        *p = obj;
+    }
+    return p;
+}
+template <typename T, std::enable_if_t<!std::is_trivial_v<T>, std::nullptr_t> = nullptr>
+inline T *wipe_impl(T *p)
+{
+    *p = T{};
+    return p;
+}
+
+//
+// データコピーマクロ COPY/C_COPY の実装
+//
+
+// トリビアルコピーが可能な型は memcpy でコピーする
+template <typename T, std::enable_if_t<std::is_trivially_copyable_v<T>, std::nullptr_t> = nullptr>
+inline T *c_copy_impl(T *p1, T *p2, size_t n)
+{
+    return static_cast<T *>(memcpy(p1, p2, sizeof(T) * n));
+}
+
+// トリビアルコピーが不可能な型は要素を1つずつコピー代入する
+template <typename T, std::enable_if_t<!std::is_trivially_copyable_v<T>, std::nullptr_t> = nullptr>
+inline T *c_copy_impl(T *p1, T *p2, size_t n)
+{
+    for (size_t i = 0; i < n; i++) {
+        *(p1 + i) = *(p2 + i);
+    }
+    return p1;
+}
+
+// 単要素の場合はトリビアルコピーの可/不可に関わらずコピー代入する
+template <typename T>
+inline T *copy_impl(T *p1, T *p2)
+{
+    *p1 = *p2;
+    return p1;
+}
+
+//
+// メモリ領域確保マクロ RNEW/C_RNEW の実装
+// MAKE/C_MAKE から最終的に呼ばれる
+//
+
+template <typename T>
+inline T *c_rnew_impl(size_t count)
+{
+    return new T[count];
+}
+
+template <typename T>
+inline T *rnew_impl()
+{
+    return new T;
+}
+
+//
+// メモリ領域解放マクロ FREE/C_FREE の実装
+// KILL/C_KILL からも FREE/C_FREE を介して呼ばれる
+//
+
+template <typename T>
+inline T *c_free_impl(T *p, size_t count)
+{
+    (void)count; // unused;
+
+    delete[] p;
+    return nullptr;
+}
+
+template <typename T>
+inline T *free_impl(T *p)
+{
+    delete p;
+    return nullptr;
+}
 
 /**** Available macros ****/
 
-
 /* Size of 'N' things of type 'T' */
-#define C_SIZE(N,T) \
-       ((huge)((N)*(sizeof(T))))
+#define C_SIZE(N, T) ((huge)((N) * (sizeof(T))))
 
 /* Size of one thing of type 'T' */
-#define SIZE(T) \
-       ((huge)(sizeof(T)))
-
+#define SIZE(T) ((huge)(sizeof(T)))
 
+#if 0
 /* Compare two arrays of type T[N], at locations P1 and P2 */
-#define C_DIFF(P1,P2,N,T) \
-       (memcmp((char*)(P1),(char*)(P2),C_SIZE(N,T)))
+#define C_DIFF(P1, P2, N, T) (memcmp((char *)(P1), (char *)(P2), C_SIZE(N, T)))
 
 /* Compare two things of type T, at locations P1 and P2 */
-#define DIFF(P1,P2,T) \
-       (memcmp((char*)(P1),(char*)(P2),SIZE(T)))
-
+#define DIFF(P1, P2, T) (memcmp((char *)(P1), (char *)(P2), SIZE(T)))
 
 /* Set every byte in an array of type T[N], at location P, to V, and return P */
-#define C_BSET(P,V,N,T) \
-       (T*)(memset((char*)(P),(V),C_SIZE(N,T)))
+#define C_BSET(P, V, N, T) (T *)(memset((char *)(P), (V), C_SIZE(N, T)))
 
 /* Set every byte in a thing of type T, at location P, to V, and return P */
-#define BSET(P,V,T) \
-       (T*)(memset((char*)(P),(V),SIZE(T)))
-
+#define BSET(P, V, T) (T *)(memset((char *)(P), (V), SIZE(T)))
+#endif
 
 /* Wipe an array of type T[N], at location P, and return P */
-#define C_WIPE(P,N,T) \
-       (T*)(memset((char*)(P),0,C_SIZE(N,T)))
+#define C_WIPE(P, N, T) (c_wipe_impl<T>(P, N))
 
 /* Wipe a thing of type T, at location P, and return P */
-#define WIPE(P,T) \
-       (T*)(memset((char*)(P),0,SIZE(T)))
-
+#define WIPE(P, T) (wipe_impl<T>(P))
 
 /* Load an array of type T[N], at location P1, from another, at location P2 */
-#define C_COPY(P1,P2,N,T) \
-       (T*)(memcpy((char*)(P1),(char*)(P2),C_SIZE(N,T)))
+#define C_COPY(P1, P2, N, T) (c_copy_impl<T>(P1, P2, N))
 
 /* Load a thing of type T, at location P1, from another, at location P2 */
-#define COPY(P1,P2,T) \
-       (T*)(memcpy((char*)(P1),(char*)(P2),SIZE(T)))
-
+#define COPY(P1, P2, T) (copy_impl<T>(P1, P2))
 
 /* Free an array of N things of type T at P, return NULL */
-#define C_FREE(P,N,T) \
-       (T*)(rnfree(P,C_SIZE(N,T)))
+#define C_FREE(P, N, T) (c_free_impl<T>(P, N))
 
 /* Free one thing of type T at P, return NULL */
-#define FREE(P,T) \
-       (T*)(rnfree(P,SIZE(T)))
-
+#define FREE(P, T) (free_impl<T>(P))
 
 /* Allocate, and return, an array of type T[N] */
-#define C_RNEW(N,T) \
-       ((T*)(ralloc(C_SIZE(N,T))))
+#define C_RNEW(N, T) (c_rnew_impl<T>(N))
 
 /* Allocate, and return, a thing of type T */
-#define RNEW(T) \
-       ((T*)(ralloc(SIZE(T))))
-
+#define RNEW(T) (rnew_impl<T>())
 
 /* Allocate, wipe, and return an array of type T[N] */
-#define C_ZNEW(N,T) \
-       ((T*)(C_WIPE(C_RNEW(N,T),N,T)))
+#define C_ZNEW(N, T) ((T *)(C_WIPE(C_RNEW(N, T), N, T)))
 
 /* Allocate, wipe, and return a thing of type T */
-#define ZNEW(T) \
-       ((T*)(WIPE(RNEW(T),T)))
-
+#define ZNEW(T) ((T *)(WIPE(RNEW(T), T)))
 
 /* Allocate a wiped array of type T[N], assign to pointer P */
-#define C_MAKE(P,N,T) \
-       ((P)=C_ZNEW(N,T))
+#define C_MAKE(P, N, T) ((P) = C_ZNEW(N, T))
 
 /* Allocate a wiped thing of type T, assign to pointer P */
-#define MAKE(P,T) \
-       ((P)=ZNEW(T))
-
+#define MAKE(P, T) ((P) = ZNEW(T))
 
 /* Free an array of type T[N], at location P, and set P to NULL */
-#define C_KILL(P,N,T) \
-       ((P)=C_FREE(P,N,T))
+#define C_KILL(P, N, T) ((P) = C_FREE(P, N, T))
 
 /* Free a thing of type T, at location P, and set P to NULL */
-#define KILL(P,T) \
-       ((P)=FREE(P,T))
-
-
-
-/**** Available variables ****/
-
-/* Replacement hook for "rnfree()" */
-extern vptr (*rnfree_aux)(vptr, huge);
-
-/* Replacement hook for "rpanic()" */
-extern vptr (*rpanic_aux)(huge);
-
-/* Replacement hook for "ralloc()" */
-extern vptr (*ralloc_aux)(huge);
-
+#define KILL(P, T) ((P) = FREE(P, T))
 
 /**** Available functions ****/
 
-/* De-allocate a given amount of memory */
-extern vptr rnfree(vptr p, huge len);
-
-/* Panic, attempt to Allocate 'len' bytes */
-extern vptr rpanic(huge len);
-
-/* Allocate (and return) 'len', or dump core */
-extern vptr ralloc(huge len);
-
 /* Create a "dynamic string" */
 extern concptr string_make(concptr str);
 
 /* Free a string allocated with "string_make()" */
 extern errr string_free(concptr str);
 
-
-
-
 #endif
-
-
-
index 82281cc..e752f1d 100644 (file)
@@ -104,6 +104,9 @@ bool object_sort_comp(player_type *player_ptr, object_type *o_ptr, s32b o_value,
         if (o_ptr->pval > j_ptr->pval)
             return FALSE;
         break;
+
+    default:
+        break;
     }
 
     return o_value > object_value(player_ptr, j_ptr);
diff --git a/src/util/probability-table.h b/src/util/probability-table.h
new file mode 100644 (file)
index 0000000..38734db
--- /dev/null
@@ -0,0 +1,142 @@
+#pragma once
+
+#include "term/z-rand.h"
+
+#include <algorithm>
+#include <exception>
+#include <stdexcept>
+#include <tuple>
+#include <vector>
+
+/**
+ * @brief 確率テーブルクラス
+ *
+ * 確率テーブルを作成し、確率に従った抽選を行うクラス
+ *
+ * @tparam IdType 確率テーブルに登録するIDの型
+ */
+template <typename IdType>
+class ProbabilityTable {
+public:
+    /**
+     * @brief コンストラクタ
+     *
+     * 空の確率テーブルを生成する
+     */
+    ProbabilityTable() = default;
+
+    /**
+     * @brief 確率テーブルを空にする
+     */
+    void clear()
+    {
+        return item_list_.clear();
+    }
+
+    /**
+     * @brief 確率テーブルに項目を登録する
+     *
+     * 確率テーブルに項目のIDと選択確率のセットを登録する。
+     * 追加した項目が選択される確率は、
+     * 追加した項目の確率(引数prob) / すべての項目のprobの合計
+     * となる。
+     * probが0もしくは負数の場合はなにも登録しない。
+     *
+     * @param id 項目のID
+     * @param prob 項目の選択確率
+     */
+    void entry_item(IdType id, int prob)
+    {
+        if (prob > 0) {
+            // 二分探索を行うため、probは累積値を格納する
+            auto cumulative_prob = item_list_.empty() ? 0 : std::get<1>(item_list_.back());
+            item_list_.emplace_back(id, cumulative_prob + prob);
+        }
+    }
+
+    /**
+     * @brief 現在の確率テーブルのすべての項目の選択確率の合計を取得する
+     *
+     * 確率テーブルに登録されているすべての項目の確率(登録時の引数prob)の合計を取得する。
+     *
+     * @return int 現在の確率テーブルのすべての項目の選択確率の合計
+     */
+    int total_prob() const
+    {
+        if (item_list_.empty())
+            return 0;
+
+        return std::get<1>(item_list_.back());
+    }
+
+    /**
+     * @brief 確率テーブルに登録されている項目の数を取得する
+     *
+     * 確率テーブルに登録されている項目の数を取得する。
+     * 登録されている項目のIDに被りがある場合は別の項目として加算した数となる。
+     *
+     * @return size_t 確率テーブルに登録されている項目の数
+     */
+    size_t item_count() const
+    {
+        return item_list_.size();
+    }
+
+    /**
+     * @brief 確率テーブルの項目が空かどうかを調べる
+     *
+     * @return bool 確率テーブルに項目が一つも登録されておらず空であれば true
+     *              項目が一つ以上登録されており空でなければ false
+     */
+    bool empty() const
+    {
+        return item_list_.empty();
+    }
+
+    /**
+     * @brief 確率テーブルから項目をランダムに1つ選択する
+     *
+     * 確率テーブルに登録されているすべての項目から、確率に従って項目を1つ選択する
+     * それぞれの項目が選択される確率は、確率設定probに対して
+     * 該当項目のprob / すべての項目のprobの合計
+     * となる。
+     * 抽選は独立試行で行われ、選択された項目がテーブルから取り除かれる事はない。
+     * 確率テーブルになにも登録されていない場合、std::runtime_error例外を送出する。
+     *
+     * @return int 選択された項目のID
+     */
+    IdType pick_one_at_random() const
+    {
+        if (empty()) {
+            throw std::runtime_error("There is no entry in the probability table.");
+        }
+
+        // probの合計の範囲からランダムでkeyを取得し、二分探索で選択する項目を決定する
+        const int key = randint0(total_prob());
+        auto it = std::partition_point(item_list_.begin(), item_list_.end(), [key](const auto &i) { return std::get<1>(i) <= key; });
+
+        return std::get<0>(*it);
+    }
+
+    /**
+     * @brief 確率テーブルから複数回抽選する
+     *
+     * 確率テーブルから引数 n で指定した回数抽選し、抽選の結果選択された項目のIDを
+     * 出力イテレータに n 個書き込む。
+     * 抽選は独立試行で行われ、選択された項目がテーブルから取り除かれる事はない。
+     *
+     * @tparam OutputIter 出力イテレータの型
+     * @param first 結果を書き込む出力イテレータ
+     * @param table 抽選を行う確率テーブル
+     * @param n 抽選を行う回数
+     */
+    template <typename OutputIter>
+    static void lottery(OutputIter first, const ProbabilityTable &table, size_t n)
+    {
+        std::generate_n(first, n, [&table] { return table.pick_one_at_random(); });
+    }
+
+private:
+    /** 項目のIDと確率のセットを格納する配列 */
+    std::vector<std::tuple<IdType, int>> item_list_;
+};
index 31c8c77..20c2bae 100644 (file)
@@ -474,3 +474,66 @@ char *angband_strchr(concptr ptr, char ch)
 
     return NULL;
 }
+
+/*!
+ * @brief 左側の空白を除去
+ * @param p char型のポインタ
+ * @return 除去後のポインタ
+ */
+char *ltrim(char *p)
+{
+    while (p[0] == ' ')
+        p++;
+    return p;
+}
+
+/*!
+ * @brief 右側の空白を除去
+ * @param p char型のポインタ
+ * @return 除去後のポインタ
+ */
+char *rtrim(char *p)
+{
+    int i = strlen(p) - 1;
+    while (p[i] == ' ')
+        p[i--] = '\0';
+    return p;
+}
+
+/*!
+ * @brief 文字列の後方から一致するかどうか比較する
+ * @param s1 比較元文字列ポインタ
+ * @param s2 比較先文字列ポインタ
+ * @param len 比較する長さ
+ * @return 等しい場合は0、p1が大きい場合は-1、p2が大きい場合は1
+ * @detail
+ * strncmpの後方から比較する版
+ */
+int strrncmp(const char *s1, const char *s2, int len)
+{
+    int i;
+    int l1 = strlen(s1);
+    int l2 = strlen(s2);
+
+    for (i = 1; i <= len; i++) {
+        int p1 = l1 - i;
+        int p2 = l2 - i;
+
+        if (l1 != l2) {
+            if (p1 < 0)
+                return (-1);
+            if (p2 < 0)
+                return (1);
+        } else {
+            if (p1 < 0)
+                return (0);
+        }
+
+        if (s1[p1] < s2[p2])
+            return (-1);
+        if (s1[p1] > s2[p2])
+            return (-1);
+    }
+
+    return (0);
+}
index ddc6d85..74bf815 100644 (file)
@@ -23,3 +23,6 @@ size_t angband_strcpy(char *buf, concptr src, size_t bufsize);
 size_t angband_strcat(char *buf, concptr src, size_t bufsize);
 char *angband_strstr(concptr haystack, concptr needle);
 char *angband_strchr(concptr ptr, char ch);
+char *ltrim(char *p);
+char *rtrim(char *p);
+int strrncmp(const char *s1, const char *s2, int len);
index 36f1aee..8b83daf 100644 (file)
@@ -21,7 +21,7 @@
 static void display_monster_blow_jp(lore_type *lore_ptr, int attack_numbers, int d1, int d2, int m)
 {
     if (attack_numbers == 0) {
-        hooked_roff(format("%^sは", wd_he[lore_ptr->msex]));
+        hooked_roff(format("%^sは", Who::who(lore_ptr->msex)));
     }
 
     if (d1 && d2 && (lore_ptr->know_everything || know_damage(lore_ptr->r_idx, m))) {
@@ -69,7 +69,7 @@ static void display_monster_blow_jp(lore_type *lore_ptr, int attack_numbers, int
 static void display_monster_blow_en(lore_type *lore_ptr, int attack_numbers, int d1, int d2, int m)
 {
     if (attack_numbers == 0) {
-        hooked_roff(format("%^s can ", wd_he[lore_ptr->msex]));
+        hooked_roff(format("%^s can ", Who::who(lore_ptr->msex)));
     } else if (attack_numbers < lore_ptr->count - 1) {
         hooked_roff(", ");
     } else {
@@ -139,8 +139,8 @@ void display_monster_blows(lore_type *lore_ptr)
     if (attack_numbers > 0) {
         hooked_roff(_("。", ".  "));
     } else if (lore_ptr->flags1 & RF1_NEVER_BLOW) {
-        hooked_roff(format(_("%^sは物理的な攻撃方法を持たない。", "%^s has no physical attacks.  "), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("%^sは物理的な攻撃方法を持たない。", "%^s has no physical attacks.  "), Who::who(lore_ptr->msex)));
     } else {
-        hooked_roff(format(_("%s攻撃については何も知らない。", "Nothing is known about %s attack.  "), wd_his[lore_ptr->msex]));
+        hooked_roff(format(_("%s攻撃については何も知らない。", "Nothing is known about %s attack.  "), Who::whose(lore_ptr->msex)));
     }
 }
index ae509fc..377c710 100644 (file)
@@ -89,7 +89,7 @@ void display_monster_drops(lore_type *lore_ptr)
     if ((lore_ptr->drop_gold == 0) && (lore_ptr->drop_item == 0))
         return;
 
-    hooked_roff(format(_("%^sは", "%^s may carry"), wd_he[lore_ptr->msex]));
+    hooked_roff(format(_("%^sは", "%^s may carry"), Who::who(lore_ptr->msex)));
 #ifdef JP
 #else
     lore_ptr->sin = FALSE;
index 311ab5b..24bb976 100644 (file)
@@ -9,7 +9,7 @@ void display_monster_breath(lore_type *lore_ptr)
         return;
 
     lore_ptr->breath = TRUE;
-    hooked_roff(format(_("%^sは", "%^s"), wd_he[lore_ptr->msex]));
+    hooked_roff(format(_("%^sは", "%^s"), Who::who(lore_ptr->msex)));
     for (int n = 0; n < lore_ptr->vn; n++) {
 #ifdef JP
         if (n != 0)
@@ -40,7 +40,7 @@ void display_monster_magic_types(lore_type *lore_ptr)
     if (lore_ptr->breath) {
         hooked_roff(_("、なおかつ", ", and is also"));
     } else {
-        hooked_roff(format(_("%^sは", "%^s is"), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("%^sは", "%^s is"), Who::who(lore_ptr->msex)));
     }
 
 #ifdef JP
index d20e020..5079fad 100644 (file)
@@ -16,7 +16,7 @@ void display_monster_hp_ac(lore_type *lore_ptr)
     if (!know_armour(lore_ptr->r_idx, lore_ptr->know_everything))
         return;
 
-    hooked_roff(format(_("%^sは AC%d の防御力と", "%^s has an armor rating of %d"), wd_he[lore_ptr->msex], lore_ptr->r_ptr->ac));
+    hooked_roff(format(_("%^sは AC%d の防御力と", "%^s has an armor rating of %d"), Who::who(lore_ptr->msex), lore_ptr->r_ptr->ac));
     if ((lore_ptr->flags1 & RF1_FORCE_MAXHP) || (lore_ptr->r_ptr->hside == 1)) {
         u32b hp = lore_ptr->r_ptr->hdice * (lore_ptr->nightmare ? 2 : 1) * lore_ptr->r_ptr->hside;
         hooked_roff(format(_(" %d の体力がある。", " and a life rating of %d.  "), (s16b)MIN(30000, hp)));
@@ -94,7 +94,7 @@ void display_monster_abilities(lore_type *lore_ptr)
     if (lore_ptr->vn <= 0)
         return;
 
-    hooked_roff(format(_("%^sは", "%^s"), wd_he[lore_ptr->msex]));
+    hooked_roff(format(_("%^sは", "%^s"), Who::who(lore_ptr->msex)));
     for (int n = 0; n < lore_ptr->vn; n++) {
 #ifdef JP
         if (n != lore_ptr->vn - 1) {
@@ -122,33 +122,33 @@ void display_monster_abilities(lore_type *lore_ptr)
 void display_monster_constitutions(lore_type *lore_ptr)
 {
     if (lore_ptr->flags7 & RF7_AQUATIC)
-        hooked_roff(format(_("%^sは水中に棲んでいる。", "%^s lives in water.  "), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("%^sは水中に棲んでいる。", "%^s lives in water.  "), Who::who(lore_ptr->msex)));
 
     if (lore_ptr->flags7 & (RF7_SELF_LITE_1 | RF7_SELF_LITE_2))
-        hooked_roff(format(_("%^sは光っている。", "%^s is shining.  "), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("%^sは光っている。", "%^s is shining.  "), Who::who(lore_ptr->msex)));
 
     if (lore_ptr->flags7 & (RF7_SELF_DARK_1 | RF7_SELF_DARK_2))
-        hook_c_roff(TERM_L_DARK, format(_("%^sは暗黒に包まれている。", "%^s is surrounded by darkness.  "), wd_he[lore_ptr->msex]));
+        hook_c_roff(TERM_L_DARK, format(_("%^sは暗黒に包まれている。", "%^s is surrounded by darkness.  "), Who::who(lore_ptr->msex)));
 
     if (lore_ptr->flags2 & RF2_INVISIBLE)
-        hooked_roff(format(_("%^sは透明で目に見えない。", "%^s is invisible.  "), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("%^sは透明で目に見えない。", "%^s is invisible.  "), Who::who(lore_ptr->msex)));
 
     if (lore_ptr->flags2 & RF2_COLD_BLOOD)
-        hooked_roff(format(_("%^sは冷血動物である。", "%^s is cold blooded.  "), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("%^sは冷血動物である。", "%^s is cold blooded.  "), Who::who(lore_ptr->msex)));
 
     if (lore_ptr->flags2 & RF2_EMPTY_MIND)
-        hooked_roff(format(_("%^sはテレパシーでは感知できない。", "%^s is not detected by telepathy.  "), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("%^sはテレパシーでは感知できない。", "%^s is not detected by telepathy.  "), Who::who(lore_ptr->msex)));
     else if (lore_ptr->flags2 & RF2_WEIRD_MIND)
-        hooked_roff(format(_("%^sはまれにテレパシーで感知できる。", "%^s is rarely detected by telepathy.  "), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("%^sはまれにテレパシーで感知できる。", "%^s is rarely detected by telepathy.  "), Who::who(lore_ptr->msex)));
 
     if (lore_ptr->flags2 & RF2_MULTIPLY)
-        hook_c_roff(TERM_L_UMBER, format(_("%^sは爆発的に増殖する。", "%^s breeds explosively.  "), wd_he[lore_ptr->msex]));
+        hook_c_roff(TERM_L_UMBER, format(_("%^sは爆発的に増殖する。", "%^s breeds explosively.  "), Who::who(lore_ptr->msex)));
 
     if (lore_ptr->flags2 & RF2_REGENERATE)
-        hook_c_roff(TERM_L_WHITE, format(_("%^sは素早く体力を回復する。", "%^s regenerates quickly.  "), wd_he[lore_ptr->msex]));
+        hook_c_roff(TERM_L_WHITE, format(_("%^sは素早く体力を回復する。", "%^s regenerates quickly.  "), Who::who(lore_ptr->msex)));
 
     if (lore_ptr->flags7 & RF7_RIDING)
-        hook_c_roff(TERM_SLATE, format(_("%^sに乗ることができる。", "%^s is suitable for riding.  "), wd_he[lore_ptr->msex]));
+        hook_c_roff(TERM_SLATE, format(_("%^sに乗ることができる。", "%^s is suitable for riding.  "), Who::who(lore_ptr->msex)));
 }
 
 void display_monster_concrete_weakness(lore_type *lore_ptr)
@@ -179,7 +179,7 @@ void display_monster_weakness(lore_type *lore_ptr)
     if (lore_ptr->vn <= 0)
         return;
 
-    hooked_roff(format(_("%^sには", "%^s"), wd_he[lore_ptr->msex]));
+    hooked_roff(format(_("%^sには", "%^s"), Who::who(lore_ptr->msex)));
     for (int n = 0; n < lore_ptr->vn; n++) {
 #ifdef JP
         if (n != 0)
@@ -311,7 +311,7 @@ void display_monster_resistances(lore_type *lore_ptr)
     if (lore_ptr->vn <= 0)
         return;
 
-    hooked_roff(format(_("%^sは", "%^s"), wd_he[lore_ptr->msex]));
+    hooked_roff(format(_("%^sは", "%^s"), Who::who(lore_ptr->msex)));
     for (int n = 0; n < lore_ptr->vn; n++) {
 #ifdef JP
         if (n != 0)
@@ -336,12 +336,12 @@ void display_monster_evolution(lore_type* lore_ptr)
         return;
 
     if (lore_ptr->r_ptr->next_r_idx) {
-        hooked_roff(format(_("%^sは経験を積むと、", "%^s will evolve into "), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("%^sは経験を積むと、", "%^s will evolve into "), Who::who(lore_ptr->msex)));
         hook_c_roff(TERM_YELLOW, format("%s", r_name + r_info[lore_ptr->r_ptr->next_r_idx].name));
 
-        hooked_roff(_(format("に進化する。"), format(" when %s gets enough experience.  ", wd_he[lore_ptr->msex])));
+        hooked_roff(_(format("に進化する。"), format(" when %s gets enough experience.  ", Who::who(lore_ptr->msex))));
     } else if (!(lore_ptr->r_ptr->flags1 & RF1_UNIQUE)) {
-        hooked_roff(format(_("%sは進化しない。", "%s won't evolve.  "), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("%sは進化しない。", "%s won't evolve.  "), Who::who(lore_ptr->msex)));
     }
 }
 
@@ -378,7 +378,7 @@ void display_monster_immunities(lore_type *lore_ptr)
     if (lore_ptr->vn <= 0)
         return;
 
-    hooked_roff(format(_("%^sは", "%^s"), wd_he[lore_ptr->msex]));
+    hooked_roff(format(_("%^sは", "%^s"), Who::who(lore_ptr->msex)));
     for (int n = 0; n < lore_ptr->vn; n++) {
 #ifdef JP
         if (n != 0)
@@ -431,6 +431,6 @@ void display_monster_alert(lore_type *lore_ptr)
         act = _("をかなり警戒しており", "is ever vigilant for");
     }
 
-    hooked_roff(_(format("%^sは侵入者%s、 %d フィート先から侵入者に気付くことがある。", wd_he[lore_ptr->msex], act, 10 * lore_ptr->r_ptr->aaf),
-        format("%^s %s intruders, which %s may notice from %d feet.  ", wd_he[lore_ptr->msex], act, wd_he[lore_ptr->msex], 10 * lore_ptr->r_ptr->aaf)));
+    hooked_roff(_(format("%^sは侵入者%s、 %d フィート先から侵入者に気付くことがある。", Who::who(lore_ptr->msex), act, 10 * lore_ptr->r_ptr->aaf),
+        format("%^s %s intruders, which %s may notice from %d feet.  ", Who::who(lore_ptr->msex), act, Who::who(lore_ptr->msex), 10 * lore_ptr->r_ptr->aaf)));
 }
index a737981..27c2cb1 100644 (file)
@@ -137,7 +137,7 @@ static bool display_kill_unique(lore_type *lore_ptr)
 
     bool dead = (lore_ptr->r_ptr->max_num == 0);
     if (lore_ptr->r_ptr->r_deaths) {
-        hooked_roff(format(_("%^sはあなたの先祖を %d 人葬っている", "%^s has slain %d of your ancestors"), wd_he[lore_ptr->msex], lore_ptr->r_ptr->r_deaths));
+        hooked_roff(format(_("%^sはあなたの先祖を %d 人葬っている", "%^s has slain %d of your ancestors"), Who::who(lore_ptr->msex), lore_ptr->r_ptr->r_deaths));
 
         if (dead) {
             hooked_roff(
@@ -148,19 +148,20 @@ static bool display_kill_unique(lore_type *lore_ptr)
         }
 
         hooked_roff("\n");
-    } else if (dead) {
-        hooked_roff(_("あなたはこの仇敵をすでに葬り去っている。", "You have slain this foe.  "));
+    } else {
+        if (dead)
+            hooked_roff(_("あなたはこの仇敵をすでに葬り去っている。", "You have slain this foe.  "));
+        else
+            hooked_roff(_("この仇敵はまだ生きている!", "This foe is still alive!  "));
+
         hooked_roff("\n");
     }
 
     return TRUE;
 }
 
-static bool display_killed(lore_type *lore_ptr)
+static void display_killed(lore_type *lore_ptr)
 {
-    if (lore_ptr->r_ptr->r_deaths == 0)
-        return FALSE;
-
     hooked_roff(_(format("このモンスターはあなたの先祖を %d 人葬っている", lore_ptr->r_ptr->r_deaths),
         format("%d of your ancestors %s been killed by this creature, ", lore_ptr->r_ptr->r_deaths, plural(lore_ptr->r_ptr->r_deaths, "has", "have"))));
 
@@ -172,24 +173,12 @@ static bool display_killed(lore_type *lore_ptr)
             _("が、あなたの先祖はこのモンスターを少なくとも %d 体は倒している。", "and your ancestors have exterminated at least %d of the creatures.  "),
             lore_ptr->r_ptr->r_tkills));
     } else {
-        hooked_roff(format(_("が、まだ%sを倒したことはない。", "and %s is not ever known to have been defeated.  "), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("が、まだ%sを倒したことはない。", "and %s is not ever known to have been defeated.  "), Who::who(lore_ptr->msex)));
     }
-
-    hooked_roff("\n");
-    return TRUE;
 }
 
-void display_kill_numbers(lore_type *lore_ptr)
+static void display_no_killed(lore_type *lore_ptr)
 {
-    if ((lore_ptr->mode & 0x02) != 0)
-        return;
-
-    if (display_kill_unique(lore_ptr))
-        return;
-
-    if (display_killed(lore_ptr))
-        return;
-
     if (lore_ptr->r_ptr->r_pkills) {
         hooked_roff(format(
             _("あなたはこのモンスターを少なくとも %d 体は殺している。", "You have killed at least %d of these creatures.  "), lore_ptr->r_ptr->r_pkills));
@@ -199,6 +188,52 @@ void display_kill_numbers(lore_type *lore_ptr)
     } else {
         hooked_roff(_("このモンスターを倒したことはない。", "No battles to the death are recalled.  "));
     }
+}
+
+/*!
+ * @brief 生存数制限のあるモンスターの最大生存数を表示する
+ * @param lore_ptr モンスターの思い出構造体への参照ポインタ
+ * @return なし
+ * @detail
+ * 一度も倒したことのないモンスターの情報は不明。
+ */
+static void display_number_of_nazguls(lore_type *lore_ptr)
+{
+    if (lore_ptr->mode != MONSTER_LORE_DEBUG && lore_ptr->r_ptr->r_tkills == 0)
+        return;
+
+    int remain = lore_ptr->r_ptr->max_num;
+    int killed = lore_ptr->r_ptr->r_akills;
+    if (remain == 0) {
+#ifdef JP
+        hooked_roff(format("%sはかつて %ld 体存在した。", Who::who(lore_ptr->msex, (killed > 1)), killed));
+#else
+        hooked_roff(format("You already killed all %ld of %s.  ", killed, Who::whom(lore_ptr->msex, (killed > 1))));
+#endif
+    } else {
+#ifdef JP
+        hooked_roff(format("%sはまだ %ld 体生きている。", Who::who(lore_ptr->msex, (remain + killed > 1)), remain));
+#else
+        concptr be = (remain > 1) ? "are" : "is";
+        hooked_roff(format("%ld of %s %s still alive.  ", remain, Who::whom(lore_ptr->msex, (remain + killed > 1)), be));
+#endif
+    }
+}
+
+void display_kill_numbers(lore_type *lore_ptr)
+{
+    if ((lore_ptr->mode & 0x02) != 0)
+        return;
+
+    if (display_kill_unique(lore_ptr))
+        return;
+
+    if (lore_ptr->r_ptr->r_deaths == 0)
+        display_no_killed(lore_ptr);
+    else
+        display_killed(lore_ptr);
+
+    display_number_of_nazguls(lore_ptr);
 
     hooked_roff("\n");
 }
@@ -212,14 +247,14 @@ bool display_where_to_appear(lore_type *lore_ptr)
 {
     lore_ptr->old = FALSE;
     if (lore_ptr->r_ptr->level == 0) {
-        hooked_roff(format(_("%^sは町に住み", "%^s lives in the town"), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("%^sは町に住み", "%^s lives in the town"), Who::who(lore_ptr->msex)));
         lore_ptr->old = TRUE;
     } else if (lore_ptr->r_ptr->r_tkills || lore_ptr->know_everything) {
         if (depth_in_feet) {
             hooked_roff(format(
-                _("%^sは通常地下 %d フィートで出現し", "%^s is normally found at depths of %d feet"), wd_he[lore_ptr->msex], lore_ptr->r_ptr->level * 50));
+                _("%^sは通常地下 %d フィートで出現し", "%^s is normally found at depths of %d feet"), Who::who(lore_ptr->msex), lore_ptr->r_ptr->level * 50));
         } else {
-            hooked_roff(format(_("%^sは通常地下 %d 階で出現し", "%^s is normally found on dungeon level %d"), wd_he[lore_ptr->msex], lore_ptr->r_ptr->level));
+            hooked_roff(format(_("%^sは通常地下 %d 階で出現し", "%^s is normally found on dungeon level %d"), Who::who(lore_ptr->msex), lore_ptr->r_ptr->level));
         }
 
         lore_ptr->old = TRUE;
@@ -233,7 +268,7 @@ bool display_where_to_appear(lore_type *lore_ptr)
     if (lore_ptr->old) {
         hooked_roff(_("、", ", and "));
     } else {
-        hooked_roff(format(_("%^sは", "%^s "), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("%^sは", "%^s "), Who::who(lore_ptr->msex)));
         lore_ptr->old = TRUE;
     }
 
@@ -305,7 +340,7 @@ void display_monster_never_move(lore_type *lore_ptr)
     if (lore_ptr->old) {
         hooked_roff(_("、しかし", ", but "));
     } else {
-        hooked_roff(format(_("%^sは", "%^s "), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("%^sは", "%^s "), Who::who(lore_ptr->msex)));
         lore_ptr->old = TRUE;
     }
 
@@ -424,19 +459,19 @@ void display_monster_aura(lore_type *lore_ptr)
 {
     if ((lore_ptr->flags2 & RF2_AURA_FIRE) && (lore_ptr->flags2 & RF2_AURA_ELEC) && (lore_ptr->flags3 & RF3_AURA_COLD))
         hook_c_roff(
-            TERM_VIOLET, format(_("%^sは炎と氷とスパークに包まれている。", "%^s is surrounded by flames, ice and electricity.  "), wd_he[lore_ptr->msex]));
+            TERM_VIOLET, format(_("%^sは炎と氷とスパークに包まれている。", "%^s is surrounded by flames, ice and electricity.  "), Who::who(lore_ptr->msex)));
     else if ((lore_ptr->flags2 & RF2_AURA_FIRE) && (lore_ptr->flags2 & RF2_AURA_ELEC))
-        hook_c_roff(TERM_L_RED, format(_("%^sは炎とスパークに包まれている。", "%^s is surrounded by flames and electricity.  "), wd_he[lore_ptr->msex]));
+        hook_c_roff(TERM_L_RED, format(_("%^sは炎とスパークに包まれている。", "%^s is surrounded by flames and electricity.  "), Who::who(lore_ptr->msex)));
     else if ((lore_ptr->flags2 & RF2_AURA_FIRE) && (lore_ptr->flags3 & RF3_AURA_COLD))
-        hook_c_roff(TERM_BLUE, format(_("%^sは炎と氷に包まれている。", "%^s is surrounded by flames and ice.  "), wd_he[lore_ptr->msex]));
+        hook_c_roff(TERM_BLUE, format(_("%^sは炎と氷に包まれている。", "%^s is surrounded by flames and ice.  "), Who::who(lore_ptr->msex)));
     else if ((lore_ptr->flags3 & RF3_AURA_COLD) && (lore_ptr->flags2 & RF2_AURA_ELEC))
-        hook_c_roff(TERM_L_GREEN, format(_("%^sは氷とスパークに包まれている。", "%^s is surrounded by ice and electricity.  "), wd_he[lore_ptr->msex]));
+        hook_c_roff(TERM_L_GREEN, format(_("%^sは氷とスパークに包まれている。", "%^s is surrounded by ice and electricity.  "), Who::who(lore_ptr->msex)));
     else if (lore_ptr->flags2 & RF2_AURA_FIRE)
-        hook_c_roff(TERM_RED, format(_("%^sは炎に包まれている。", "%^s is surrounded by flames.  "), wd_he[lore_ptr->msex]));
+        hook_c_roff(TERM_RED, format(_("%^sは炎に包まれている。", "%^s is surrounded by flames.  "), Who::who(lore_ptr->msex)));
     else if (lore_ptr->flags3 & RF3_AURA_COLD)
-        hook_c_roff(TERM_BLUE, format(_("%^sは氷に包まれている。", "%^s is surrounded by ice.  "), wd_he[lore_ptr->msex]));
+        hook_c_roff(TERM_BLUE, format(_("%^sは氷に包まれている。", "%^s is surrounded by ice.  "), Who::who(lore_ptr->msex)));
     else if (lore_ptr->flags2 & RF2_AURA_ELEC)
-        hook_c_roff(TERM_L_BLUE, format(_("%^sはスパークに包まれている。", "%^s is surrounded by electricity.  "), wd_he[lore_ptr->msex]));
+        hook_c_roff(TERM_L_BLUE, format(_("%^sはスパークに包まれている。", "%^s is surrounded by electricity.  "), Who::who(lore_ptr->msex)));
 }
 
 void display_lore_this(player_type *player_ptr, lore_type *lore_ptr)
@@ -505,10 +540,10 @@ static void display_monster_escort_contents(lore_type *lore_ptr)
 void display_monster_collective(lore_type *lore_ptr)
 {
     if ((lore_ptr->flags1 & RF1_ESCORT) || (lore_ptr->flags1 & RF1_ESCORTS) || lore_ptr->reinforce) {
-        hooked_roff(format(_("%^sは通常護衛を伴って現れる。", "%^s usually appears with escorts.  "), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("%^sは通常護衛を伴って現れる。", "%^s usually appears with escorts.  "), Who::who(lore_ptr->msex)));
         display_monster_escort_contents(lore_ptr);
     } else if (lore_ptr->flags1 & RF1_FRIENDS) {
-        hooked_roff(format(_("%^sは通常集団で現れる。", "%^s usually appears in groups.  "), wd_he[lore_ptr->msex]));
+        hooked_roff(format(_("%^sは通常集団で現れる。", "%^s usually appears in groups.  "), Who::who(lore_ptr->msex)));
     }
 }
 
@@ -569,7 +604,7 @@ void display_monster_sometimes(lore_type *lore_ptr)
     if (lore_ptr->vn <= 0)
         return;
 
-    hooked_roff(format(_("%^sは", "%^s"), wd_he[lore_ptr->msex]));
+    hooked_roff(format(_("%^sは", "%^s"), Who::who(lore_ptr->msex)));
     for (int n = 0; n < lore_ptr->vn; n++) {
 #ifdef JP
         if (n != lore_ptr->vn - 1) {
index be51d78..43d708a 100644 (file)
@@ -13,6 +13,7 @@
 #include "info-reader/fixed-map-parser.h"
 #include "inventory/inventory-slot-types.h"
 #include "knowledge/knowledge-mutations.h"
+#include "mind/mind-elementalist.h"
 #include "mutation/mutation-flag-types.h"
 #include "object/object-info.h"
 #include "object/object-kind.h"
@@ -95,10 +96,13 @@ static void display_player_basic_info(player_type *creature_ptr)
  */
 static void display_magic_realms(player_type *creature_ptr)
 {
-       if (creature_ptr->realm1 == 0) return;
+    if (creature_ptr->realm1 == 0)
+        return;
 
-       char tmp[64];
-       if (creature_ptr->realm2)
+    char tmp[64];
+    if (creature_ptr->pclass == CLASS_ELEMENTALIST)
+            sprintf(tmp, "%s", get_element_title(creature_ptr->realm1));
+       else if (creature_ptr->realm2)
                sprintf(tmp, "%s, %s", realm_names[creature_ptr->realm1], realm_names[creature_ptr->realm2]);
        else
                strcpy(tmp, realm_names[creature_ptr->realm1]);
index 56f522b..5d3a22a 100644 (file)
@@ -45,7 +45,7 @@ static void display_spell_list(player_type *caster_ptr)
     }
 
     if ((caster_ptr->pclass == CLASS_MINDCRAFTER) || (caster_ptr->pclass == CLASS_BERSERKER) || (caster_ptr->pclass == CLASS_NINJA)
-        || (caster_ptr->pclass == CLASS_MIRROR_MASTER) || (caster_ptr->pclass == CLASS_FORCETRAINER)) {
+        || (caster_ptr->pclass == CLASS_MIRROR_MASTER) || (caster_ptr->pclass == CLASS_FORCETRAINER) || caster_ptr->pclass == CLASS_ELEMENTALIST) {
         PERCENTAGE minfail = 0;
         PLAYER_LEVEL plev = caster_ptr->lev;
         PERCENTAGE chance = 0;
@@ -80,6 +80,9 @@ static void display_spell_list(player_type *caster_ptr)
             use_mind = MIND_NINJUTSU;
             use_hp = TRUE;
             break;
+        case CLASS_ELEMENTALIST:
+            use_mind = MIND_ELEMENTAL;
+            break;
         default:
             use_mind = 0;
             break;
index 51330d4..2bea763 100644 (file)
@@ -129,7 +129,7 @@ void print_monster_list(floor_type *floor_ptr, const std::vector<MONSTER_IDX> &m
     TERM_LEN line = y;
     monster_type *last_mons = NULL;
     int n_same = 0;
-    int i;
+    size_t i;
     for (i = 0; i < monster_list.size(); i++) {
         auto m_ptr = &floor_ptr->m_list[monster_list[i]];
         if (is_pet(m_ptr))
index f74f658..b31d6ec 100644 (file)
@@ -314,7 +314,7 @@ void health_redraw(player_type *creature_ptr, bool riding)
     m_ptr = &creature_ptr->current_floor_ptr->m_list[health_who];
 
     if (current_world_ptr->wizard && creature_ptr->phase_out) {
-        row = ROW_INFO - 2;
+        row = ROW_INFO - 1;
         col = COL_INFO + 2;
 
         term_putstr(col - 2, row, 12, TERM_WHITE, "      /     ");
index 6b4bc1e..75f6c1a 100644 (file)
@@ -271,7 +271,7 @@ void print_state(player_type *player_ptr)
 }
 
 /*!
- * @brief プレイヤーの行動速度を表示する / Prints the speed of a character.                     -CJS-
+ * @brief プレイヤーの行動速度を表示する / Prints the speed_value of a character.                       -CJS-
  * @param player_ptr プレーヤーへの参照ポインタ
  * @return なし
  */
@@ -282,15 +282,13 @@ void print_speed(player_type *player_ptr)
     TERM_LEN col_speed = wid + COL_SPEED;
     TERM_LEN row_speed = hgt + ROW_SPEED;
 
-    int i = player_ptr->pspeed;
-    if (player_ptr->action == ACTION_SEARCH && !player_ptr->lightspeed)
-        i += 10;
+    int speed_value = player_ptr->pspeed - 110;
 
     floor_type *floor_ptr = player_ptr->current_floor_ptr;
     bool is_player_fast = is_fast(player_ptr);
     char buf[32] = "";
     TERM_COLOR attr = TERM_WHITE;
-    if (i > 110) {
+    if (speed_value > 0) {
         if (player_ptr->riding) {
             monster_type *m_ptr = &floor_ptr->m_list[player_ptr->riding];
             if (monster_fast_remaining(m_ptr) && !monster_slow_remaining(m_ptr))
@@ -305,8 +303,8 @@ void print_speed(player_type *player_ptr)
             attr = TERM_VIOLET;
         else
             attr = TERM_L_GREEN;
-        sprintf(buf, "%s(+%d)", (player_ptr->riding ? _("乗馬", "Ride") : _("加速", "Fast")), (i - 110));
-    } else if (i < 110) {
+        sprintf(buf, "%s(+%d)", (player_ptr->riding ? _("乗馬", "Ride") : _("加速", "Fast")), speed_value);
+    } else if (speed_value < 0) {
         if (player_ptr->riding) {
             monster_type *m_ptr = &floor_ptr->m_list[player_ptr->riding];
             if (monster_fast_remaining(m_ptr) && !monster_slow_remaining(m_ptr))
@@ -321,7 +319,7 @@ void print_speed(player_type *player_ptr)
             attr = TERM_VIOLET;
         else
             attr = TERM_L_UMBER;
-        sprintf(buf, "%s(-%d)", (player_ptr->riding ? _("乗馬", "Ride") : _("減速", "Slow")), (110 - i));
+        sprintf(buf, "%s(%d)", (player_ptr->riding ? _("乗馬", "Ride") : _("減速", "Slow")), speed_value);
     } else if (player_ptr->riding) {
         attr = TERM_GREEN;
         strcpy(buf, _("乗馬中", "Riding"));
index a4f889b..1c461e4 100644 (file)
@@ -73,6 +73,7 @@ std::vector<std::vector<std::string>> debug_menu_table = {
     { "u", _("啓蒙(忍者以外)", "Wiz-lite all floor except Ninja") },
     { "v", _("特別品獲得ドロップ", "Drop special object") },
     { "w", _("啓蒙(忍者配慮)", "Wiz-lite all floor") },
+    { "W", _("願い", "Wishing") },
     { "x", _("経験値を得る(指定可)", "Get experience") },
     { "X", _("所持品を初期状態に戻す", "Return inventory to initial") },
     { "z", _("近隣のモンスター消去", "Terminate near monsters") },
@@ -236,6 +237,9 @@ bool exe_cmd_debug(player_type *creature_ptr, char cmd)
     case 'w':
         wiz_lite(creature_ptr, (bool)(creature_ptr->pclass == CLASS_NINJA));
         break;
+    case 'W':
+        do_cmd_wishing(creature_ptr, -1, TRUE, TRUE, TRUE);
+        break;
     case 'x':
         gain_exp(creature_ptr, command_arg ? command_arg : (creature_ptr->exp + 1));
         break;
index 4b6f0a3..b81feee 100644 (file)
@@ -68,7 +68,7 @@ static concptr attr_to_text(monster_race *r_ptr)
     return _("変な色の", "Icky");
 }
 
-spoiler_output_status spoil_mon_desc(concptr fname, bool show_all, race_flags8 RF8_flags)
+spoiler_output_status spoil_mon_desc(concptr fname, std::function<bool(const monster_race *)> filter_monster)
 {
     player_type dummy;
     u16b why = 2;
@@ -111,7 +111,7 @@ spoiler_output_status spoil_mon_desc(concptr fname, bool show_all, race_flags8 R
     for (int i = 0; i < n; i++) {
         monster_race *r_ptr = &r_info[who[i]];
         concptr name = (r_name + r_ptr->name);
-        if (!show_all && none_bits(r_ptr->flags8, RF8_flags))
+        if (filter_monster && !filter_monster(r_ptr))
             continue;
 
         char name_buf[41];
@@ -158,13 +158,6 @@ spoiler_output_status spoil_mon_desc(concptr fname, bool show_all, race_flags8 R
 }
 
 /*!
- * @brief モンスター簡易情報のスポイラー出力を行うメインルーチン /
- * Create a spoiler file for monsters   -BEN-
- * @param fname 生成ファイル名
- */
-spoiler_output_status spoil_mon_desc_all(concptr fname) { return spoil_mon_desc(fname, TRUE, RF8_WILD_ALL); }
-
-/*!
  * @brief 関数ポインタ用の出力関数 /
  * Hook function used in spoil_mon_info()
  * @param attr 未使用
index 976f845..380f73f 100644 (file)
@@ -3,7 +3,10 @@
 #include "system/angband.h"
 #include "wizard/spoiler-util.h"
 
-enum race_flags8 : uint32_t;
+#include <functional>
+
+struct monster_race;
+
 spoiler_output_status spoil_mon_desc_all(concptr fname);
-spoiler_output_status spoil_mon_desc(concptr fname, bool show_all, race_flags8 RF8_flags);
+spoiler_output_status spoil_mon_desc(concptr fname, std::function<bool(const monster_race *)> filter_monster = nullptr);
 spoiler_output_status spoil_mon_info(concptr fname);
index 8c32bf8..41cf3c7 100644 (file)
@@ -1,20 +1,30 @@
 #include "wizard/wizard-item-modifier.h"
+#include "artifact/fixed-art-generator.h"
 #include "artifact/random-art-generator.h"
 #include "core/asking-player.h"
+#include "core/show-file.h"
 #include "core/player-update-types.h"
 #include "core/window-redrawer.h"
 #include "flavor/flavor-describer.h"
 #include "flavor/object-flavor-types.h"
 #include "floor/floor-object.h"
+#include "game-option/cheat-options.h"
+#include "inventory/inventory-slot-types.h"
 #include "io/input-key-acceptor.h"
 #include "object-enchant/apply-magic.h"
 #include "object-enchant/item-apply-magic.h"
+#include "object-enchant/object-ego.h"
+#include "object-enchant/tr-types.h"
 #include "object-hook/hook-enchant.h"
+#include "object-hook/hook-checker.h"
 #include "object/item-use-flags.h"
 #include "object/object-flags.h"
 #include "object/object-generator.h"
+#include "object/object-info.h"
 #include "object/object-kind.h"
+#include "object/object-kind-hook.h"
 #include "object/object-value.h"
+#include "util/string-processor.h"
 #include "system/alloc-entries.h"
 #include "system/artifact-type-definition.h"
 #include "system/floor-type-definition.h"
@@ -23,6 +33,8 @@
 #include "term/term-color-types.h"
 #include "view/display-messages.h"
 #include "util/bit-flags-calculator.h"
+#include "world/world.h"
+#include <vector>
 
 #define K_MAX_DEPTH 110 /*!< アイテムの階層毎生成率を表示する最大階 */
 
@@ -493,3 +505,397 @@ void wiz_modify_item(player_type *creature_ptr)
         msg_print("Changes ignored.");
     }
 }
+
+/*!
+ * @brief オブジェクトの装備スロットがエゴが有効なスロットかどうか判定
+ */
+static int is_slot_able_to_be_ego(player_type *caster_ptr, object_type *o_ptr)
+{
+    int slot = wield_slot(caster_ptr, o_ptr);
+
+    if (slot > -1)
+        return slot;
+
+    if ((o_ptr->tval == TV_SHOT) || (o_ptr->tval == TV_ARROW) || (o_ptr->tval == TV_BOLT))
+        return (INVEN_AMMO);
+
+    return (-1);
+}
+
+/*!
+ * @brief 願ったが消えてしまった場合のメッセージ 
+ */
+static void wishing_puff_of_smoke(void)
+{
+    msg_print(_("何かが足下に転がってきたが、煙のように消えてしまった。",
+        "You feel something roll beneath your feet, but it disappears in a puff of smoke!"));
+}
+
+/*!
+ * @brief 願ったが消えてしまった場合のメッセージ
+ * @param caster_ptr 願ったプレイヤー情報への参照ポインタ
+ * @param prob ★などを願った場合の生成確率
+ * @param art_ok アーティファクトの生成を許すならTRUE
+ * @param ego_ok エゴの生成を許すならTRUE
+ * @param confirm 願わない場合に確認するかどうか
+ * @return 願った結果
+ */
+WishResult do_cmd_wishing(player_type *caster_ptr, int prob, bool allow_art, bool allow_ego, bool confirm)
+{
+    concptr fixed_str[] = {
+#ifdef JP
+        "燃えない",
+        "錆びない",
+        "腐食しない",
+        "安定した",
+#else
+        "rotproof",
+        "fireproof",
+        "rustproof",
+        "erodeproof",
+        "corrodeproof",
+        "fixed",
+#endif
+        NULL,
+    };
+
+    char buf[MAX_NLEN] = "\0";
+    char *str = buf;
+    object_type forge;
+    object_type *o_ptr = &forge;
+    char o_name[MAX_NLEN];
+
+    bool wish_art = FALSE;
+    bool wish_randart = FALSE;
+    bool wish_ego = FALSE;
+    bool exam_base = TRUE;
+    bool ok_art = (randint0(100) < prob) ? TRUE : FALSE;
+    bool ok_ego = (randint0(100) < 50 + prob) ? TRUE : FALSE;
+    bool must = (prob < 0) ? TRUE : FALSE;
+    bool blessed = FALSE;
+    bool fixed = TRUE;
+
+    while (1) {
+        if (get_string(_("何をお望み? ", "For what do you wish?"), buf, (MAX_NLEN - 1)))
+            break;
+        if (confirm) {
+            if (!get_check(_("何も願いません。本当によろしいですか?", "Do you wish nothing, really?")))
+                continue;
+        }
+        return WishResult::NOTHING;
+    }
+
+#ifndef JP
+    str_tolower(str);
+
+    /* remove 'a' */
+    if (!strncmp(buf, "a ", 2))
+        str = ltrim(str + 1);
+    else if (!strncmp(buf, "an ", 3))
+        str = ltrim(str + 2);
+#endif // !JP
+
+    str = rtrim(str);
+
+    if (!strncmp(str, _("祝福された", "blessed"), _(10, 7))) {
+        str = ltrim(str + _(10, 7));
+        blessed = TRUE;
+    }
+
+    for (int i = 0; fixed_str[i] != NULL; i++) {
+        int len = strlen(fixed_str[i]);
+        if (!strncmp(str, fixed_str[i], len)) {
+            str = ltrim(str + len);
+            fixed = TRUE;
+            break;
+        }
+    }
+
+#ifdef JP
+    if (!strncmp(str, "★", 2)) {
+        str = ltrim(str + 2);
+        wish_art = TRUE;
+        exam_base = FALSE;
+    } else
+#endif
+
+    if (!strncmp(str, _("☆", "The "), _(2, 4))) {
+        str = ltrim(str + _(2, 4));
+        wish_art = TRUE;
+        wish_randart = TRUE;
+    }
+
+    /* wishing random ego ? */
+    else if (!strncmp(str, _("高級な", "excellent "), _(6, 9))) {
+        str = ltrim(str + _(6, 9));
+        wish_ego = TRUE;
+    }
+
+    if (strlen(str) < 1) {
+        msg_print(_("名前がない!", "What?"));
+        return WishResult::NOTHING;
+    }
+
+    if (!allow_art && wish_art) {
+        msg_print(_("アーティファクトは願えない!", "You can not wish artifacts!"));
+        return WishResult::NOTHING;
+    }
+
+    if (cheat_xtra)
+        msg_format("Wishing %s....", buf);
+
+    std::vector<KIND_OBJECT_IDX> k_ids;
+    std::vector<EGO_IDX> e_ids;
+    if (exam_base) {
+        int len;
+        int max_len = 0;
+        for (KIND_OBJECT_IDX k = 1; k < max_k_idx; k++) {
+            object_kind *k_ptr = &k_info[k];
+            if (!k_ptr->name)
+                continue;
+
+            object_prep(caster_ptr, o_ptr, k);
+            describe_flavor(caster_ptr, o_name, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY | OD_STORE));
+#ifndef JP
+            str_tolower(o_name);
+#endif
+            if (cheat_xtra)
+                msg_format("Matching object No.%d %s", k, o_name);
+
+            len = strlen(o_name);
+
+            if (_(!strrncmp(str, o_name, len), !strncmp(str, o_name, len))) {
+                if (len > max_len) {
+                    k_ids.push_back(k);
+                    max_len = len;
+                }
+            }
+        }
+
+        if (allow_ego && k_ids.size() == 1) {
+            KIND_OBJECT_IDX k_idx = k_ids.back();
+            object_prep(caster_ptr, o_ptr, k_idx);
+
+            for (EGO_IDX k = 1; k < max_e_idx; k++) {
+                ego_item_type *e_ptr = &e_info[k];
+                if (!e_ptr->name)
+                    continue;
+
+                strcpy(o_name, (e_name + e_ptr->name));
+#ifndef JP
+                str_tolower(o_name);
+#endif
+                if (cheat_xtra)
+                    msg_format("Mathcing ego No.%d %s...", k, o_name);
+
+                if (_(!strncmp(str, o_name, strlen(o_name)), !strrncmp(str, o_name, strlen(o_name)))) {
+                    if (is_slot_able_to_be_ego(caster_ptr, o_ptr) != e_ptr->slot)
+                        continue;
+
+                    e_ids.push_back(k);
+                }
+            }
+        }
+    }
+
+    std::vector<ARTIFACT_IDX> a_ids;
+
+    if (allow_art) {
+        char a_desc[MAX_NLEN] = "\0";
+        char *a_str = a_desc;
+
+        int len;
+        int mlen = 0;
+        for (ARTIFACT_IDX i = 1; i < max_a_idx; i++) {
+            artifact_type *a_ptr = &a_info[i];
+            if (!a_ptr->name)
+                continue;
+
+            KIND_OBJECT_IDX k_idx = lookup_kind(a_ptr->tval, a_ptr->sval);
+            if (!k_idx)
+                continue;
+
+            object_prep(caster_ptr, o_ptr, k_idx);
+            o_ptr->name1 = i;
+
+            describe_flavor(caster_ptr, o_name, o_ptr, (OD_OMIT_PREFIX | OD_NAME_ONLY | OD_STORE));
+#ifndef JP
+            str_tolower(o_name);
+#endif
+            a_str = a_desc;
+            strcpy(a_desc, a_name + a_ptr->name);
+
+            if (*a_str == '$')
+                a_str++;
+#ifdef JP
+            /* remove quotes */
+            if (!strncmp(a_str, "『", 2)) {
+                a_str += 2;
+                char *s = strstr(a_str, "』");
+                *s = '\0';
+            }
+            /* remove 'of' */
+            else {
+                int l = strlen(a_str);
+                if (!strrncmp(a_str, "の", 2)) {
+                    a_str[l - 2] = '\0';
+                }
+            }
+#else
+            /* remove quotes */
+            if (a_str[0] == '\'') {
+                a_str += 1;
+                char *s = strchr(a_desc, '\'');
+                *s = '\0';
+            }
+            /* remove 'of ' */
+            else if (!strncmp(a_str, (const char *)"of ", 3)) {
+                a_str += 3;
+            }
+
+            str_tolower(a_str);
+#endif
+
+            if (cheat_xtra)
+                msg_format("Matching artifact No.%d %s(%s)", i, a_desc, _(&o_name[2], o_name));
+
+            std::vector<char *> l = { a_str, a_name + a_ptr->name, _(&o_name[2], o_name) };
+            for (size_t c = 0; c < l.size(); c++) {
+                if (!strcmp(str, l.at(c))) {
+                    len = strlen(l.at(c));
+                    if (len > mlen) {
+                        a_ids.push_back(i);
+                        mlen = len;
+                    }
+                }
+            }
+        }
+    }
+
+    if (current_world_ptr->wizard && (a_ids.size() > 1 || e_ids.size() > 1)) {
+        msg_print(_("候補が多すぎる!", "Too many matches!"));
+        return WishResult::FAIL;
+    }
+    
+    if (a_ids.size() == 1) {
+        ARTIFACT_IDX a_idx = a_ids.back();
+        if (must || (ok_art && !a_info[a_idx].cur_num)) {
+            create_named_art(caster_ptr, a_idx, caster_ptr->y, caster_ptr->x);
+            if (!current_world_ptr->wizard)
+                a_info[a_idx].cur_num = 1;
+        }
+        else
+            wishing_puff_of_smoke();
+        return WishResult::ARTIFACT;
+    }
+    
+    if (!allow_ego && (wish_ego || e_ids.size() > 0)) {
+        msg_print(_("エゴアイテムは願えない!", "Can not wish ego item."));
+        return WishResult::NOTHING;
+    }
+    
+    if (k_ids.size() == 1) {
+        KIND_OBJECT_IDX k_idx = k_ids.back();
+        object_kind *k_ptr = &k_info[k_idx];
+
+        artifact_type *a_ptr;
+        ARTIFACT_IDX a_idx = 0;
+        if (k_ptr->gen_flags.has(TRG::INSTA_ART)) {
+            for (ARTIFACT_IDX i = 1; i < max_a_idx; i++) {
+                a_ptr = &a_info[i];
+                if (a_ptr->tval != k_ptr->tval || a_ptr->sval != k_ptr->sval)
+                    continue;
+                a_idx = i;
+                break;
+            }
+        }
+
+        if (a_idx > 0) {
+            a_ptr = &a_info[a_idx];
+            if (must || (ok_art && !a_ptr->cur_num)) {
+                create_named_art(caster_ptr, a_idx, caster_ptr->y, caster_ptr->x);
+                if (!current_world_ptr->wizard)
+                    a_info[a_idx].cur_num = 1;
+            }
+            else
+                wishing_puff_of_smoke();
+            return WishResult::ARTIFACT;
+        }
+
+        if (wish_randart) {
+            if (must || ok_art) {
+                do {
+                    object_prep(caster_ptr, o_ptr, k_idx);
+                    apply_magic(caster_ptr, o_ptr, k_ptr->level, (AM_SPECIAL | AM_NO_FIXED_ART));
+                } while (!o_ptr->art_name || o_ptr->name1 || o_ptr->name2 || object_is_cursed(o_ptr));
+
+                if (o_ptr->art_name)
+                    drop_near(caster_ptr, o_ptr, -1, caster_ptr->y, caster_ptr->x);
+            } else {
+                wishing_puff_of_smoke();
+            }
+            return WishResult::ARTIFACT;
+        }
+
+        WishResult res = WishResult::NOTHING;
+        if (allow_ego && (wish_ego || e_ids.size() > 0)) {
+            if (must || ok_ego) {
+                int max_roll = 1000;
+                int i = 0;
+                for (i = 0; i < max_roll; i++) {
+                    object_prep(caster_ptr, o_ptr, k_idx);
+                    (void)apply_magic(caster_ptr, o_ptr, k_ptr->level, (AM_GREAT | AM_NO_FIXED_ART));
+
+                    if (o_ptr->name1 || o_ptr->art_name)
+                        continue;
+
+                    if (wish_ego)
+                        break;
+
+                    EGO_IDX e_idx = 0;
+                    for (auto e : e_ids) {
+                        if (o_ptr->name2 == e) {
+                            e_idx = e;
+                            break;
+                        }
+                    }
+
+                    if (e_idx != 0)
+                        break;
+                }
+
+                if (i == max_roll) {
+                    msg_print(_("失敗!もう一度願ってみてください。", "Failed! Try again."));
+                    return WishResult::FAIL;
+                }
+            } else {
+                wishing_puff_of_smoke();
+            }
+            
+            res = WishResult::EGO;
+        } else {
+            for (int i = 0; i < 100; i++) {
+                object_prep(caster_ptr, o_ptr, k_idx);
+                apply_magic(caster_ptr, o_ptr, 0, (AM_NO_FIXED_ART));
+                if (!object_is_cursed(o_ptr))
+                    break;
+            }
+            res = WishResult::NORMAL;
+        }
+
+        if (blessed && wield_slot(caster_ptr, o_ptr) != -1)
+            add_flag(o_ptr->art_flags, TR_BLESSED);
+
+        if (fixed && wield_slot(caster_ptr, o_ptr) != -1) {
+            add_flag(o_ptr->art_flags, TR_IGNORE_ACID);
+            add_flag(o_ptr->art_flags, TR_IGNORE_FIRE);
+        }
+
+        (void)drop_near(caster_ptr, o_ptr, -1, caster_ptr->y, caster_ptr->x);
+
+        return res;
+    }
+
+    msg_print(_("うーん、そんなものは存在しないようだ。", "Ummmm, that is not existing..."));
+    return WishResult::FAIL;
+}
index 7f34981..ac56015 100644 (file)
@@ -2,4 +2,7 @@
 
 #include "system/angband.h"
 
+enum class WishResult { FAIL = -1, NOTHING = 0, NORMAL = 1, EGO = 2, ARTIFACT = 3, MAX };
+
 void wiz_modify_item(player_type *creature_ptr);
+WishResult do_cmd_wishing(player_type *caster_ptr, int prob, bool art_ok, bool ego_ok, bool confirm);
index 8a149a5..6102e7f 100644 (file)
 #include "io/input-key-acceptor.h"
 #include "main/sound-of-music.h"
 #include "monster-race/monster-race.h"
+#include "monster-race/race-flags7.h"
 #include "monster-race/race-flags8.h"
 #include "system/angband-version.h"
 #include "term/screen-processor.h"
 #include "util/angband-files.h"
+#include "util/bit-flags-calculator.h"
 #include "util/int-char-converter.h"
 #include "util/sort.h"
 #include "view/display-messages.h"
@@ -162,6 +164,34 @@ static spoiler_output_status spoil_mon_evol(concptr fname)
     return SPOILER_OUTPUT_SUCCESS;
 }
 
+spoiler_output_status spoil_categorized_mon_desc()
+{
+    spoiler_output_status status = spoil_mon_desc("mon-desc-ridable.txt", [](const monster_race *r_ptr) { return any_bits(r_ptr->flags7, RF7_RIDING); });
+
+    if (status == SPOILER_OUTPUT_SUCCESS)
+        status = spoil_mon_desc("mon-desc-wildonly.txt", [](const monster_race *r_ptr) { return any_bits(r_ptr->flags8, RF8_WILD_ONLY); });
+    if (status == SPOILER_OUTPUT_SUCCESS)
+        status = spoil_mon_desc("mon-desc-town.txt", [](const monster_race *r_ptr) { return any_bits(r_ptr->flags8, RF8_WILD_TOWN); });
+    if (status == SPOILER_OUTPUT_SUCCESS)
+        status = spoil_mon_desc("mon-desc-shore.txt", [](const monster_race *r_ptr) { return any_bits(r_ptr->flags8, RF8_WILD_SHORE); });
+    if (status == SPOILER_OUTPUT_SUCCESS)
+        status = spoil_mon_desc("mon-desc-ocean.txt", [](const monster_race *r_ptr) { return any_bits(r_ptr->flags8, RF8_WILD_OCEAN); });
+    if (status == SPOILER_OUTPUT_SUCCESS)
+        status = spoil_mon_desc("mon-desc-waste.txt", [](const monster_race *r_ptr) { return any_bits(r_ptr->flags8, RF8_WILD_WASTE); });
+    if (status == SPOILER_OUTPUT_SUCCESS)
+        status = spoil_mon_desc("mon-desc-wood.txt", [](const monster_race *r_ptr) { return any_bits(r_ptr->flags8, RF8_WILD_WOOD); });
+    if (status == SPOILER_OUTPUT_SUCCESS)
+        status = spoil_mon_desc("mon-desc-volcano.txt", [](const monster_race *r_ptr) { return any_bits(r_ptr->flags8, RF8_WILD_VOLCANO); });
+    if (status == SPOILER_OUTPUT_SUCCESS)
+        status = spoil_mon_desc("mon-desc-mountain.txt", [](const monster_race *r_ptr) { return any_bits(r_ptr->flags8, RF8_WILD_MOUNTAIN); });
+    if (status == SPOILER_OUTPUT_SUCCESS)
+        status = spoil_mon_desc("mon-desc-grass.txt", [](const monster_race *r_ptr) { return any_bits(r_ptr->flags8, RF8_WILD_GRASS); });
+    if (status == SPOILER_OUTPUT_SUCCESS)
+        status = spoil_mon_desc("mon-desc-wildall.txt", [](const monster_race *r_ptr) { return any_bits(r_ptr->flags8, RF8_WILD_ALL); });
+
+    return status;
+}
+
 /*!
  * @brief スポイラー出力を行うコマンドのメインルーチン /
  * Create Spoiler files -BEN-
@@ -177,8 +207,9 @@ void exe_output_spoilers(void)
         prt("(1) Brief Object Info (obj-desc.txt)", 5, 5);
         prt("(2) Brief Artifact Info (artifact.txt)", 6, 5);
         prt("(3) Brief Monster Info (mon-desc.txt)", 7, 5);
-        prt("(4) Full Monster Info (mon-info.txt)", 8, 5);
-        prt("(5) Monster Evolution Info (mon-evol.txt)", 9, 5);
+        prt("(4) Brief Categorized Monster Info (mon-desc-*.txt)", 8, 5);
+        prt("(5) Full Monster Info (mon-info.txt)", 9, 5);
+        prt("(6) Monster Evolution Info (mon-evol.txt)", 10, 5);
         prt(_("コマンド:", "Command: "), _(18, 12), 0);
         switch (inkey()) {
         case ESCAPE:
@@ -191,12 +222,15 @@ void exe_output_spoilers(void)
             status = spoil_fixed_artifact("artifact.txt");
             break;
         case '3':
-            status = spoil_mon_desc_all("mon-desc.txt");
+            status = spoil_mon_desc("mon-desc.txt");
             break;
         case '4':
-            status = spoil_mon_info("mon-info.txt");
+            status = spoil_categorized_mon_desc();
             break;
         case '5':
+            status = spoil_mon_info("mon-info.txt");
+            break;
+        case '6':
             status = spoil_mon_evol("mon-evol.txt");
             break;
         default:
@@ -214,6 +248,8 @@ void exe_output_spoilers(void)
         case SPOILER_OUTPUT_SUCCESS:
             msg_print("Successfully created a spoiler file.");
             break;
+        case SPOILER_OUTPUT_CANCEL:
+            break;
         }
         msg_erase();
     }
@@ -235,38 +271,11 @@ spoiler_output_status output_all_spoilers(void)
     if (status != SPOILER_OUTPUT_SUCCESS)
         return status;
 
-    status = spoil_mon_desc_all("mon-desc.txt");
+    status = spoil_mon_desc("mon-desc.txt");
     if (status != SPOILER_OUTPUT_SUCCESS)
         return status;
 
-    status = spoil_mon_desc("mon-desc-wildonly.txt", FALSE, RF8_WILD_ONLY);
-    if (status != SPOILER_OUTPUT_SUCCESS)
-        return status;
-    status = spoil_mon_desc("mon-desc-town.txt", FALSE, RF8_WILD_TOWN);
-    if (status != SPOILER_OUTPUT_SUCCESS)
-        return status;
-    status = spoil_mon_desc("mon-desc-shore.txt", FALSE, RF8_WILD_SHORE);
-    if (status != SPOILER_OUTPUT_SUCCESS)
-        return status;
-    status = spoil_mon_desc("mon-desc-ocean.txt", FALSE, RF8_WILD_OCEAN);
-    if (status != SPOILER_OUTPUT_SUCCESS)
-        return status;
-    status = spoil_mon_desc("mon-desc-waste.txt", FALSE, RF8_WILD_WASTE);
-    if (status != SPOILER_OUTPUT_SUCCESS)
-        return status;
-    status = spoil_mon_desc("mon-desc-wood.txt", FALSE, RF8_WILD_WOOD);
-    if (status != SPOILER_OUTPUT_SUCCESS)
-        return status;
-    status = spoil_mon_desc("mon-desc-volcano.txt", FALSE, RF8_WILD_VOLCANO);
-    if (status != SPOILER_OUTPUT_SUCCESS)
-        return status;
-    status = spoil_mon_desc("mon-desc-mountain.txt", FALSE, RF8_WILD_MOUNTAIN);
-    if (status != SPOILER_OUTPUT_SUCCESS)
-        return status;
-    status = spoil_mon_desc("mon-desc-grass.txt", FALSE, RF8_WILD_GRASS);
-    if (status != SPOILER_OUTPUT_SUCCESS)
-        return status;
-    status = spoil_mon_desc("mon-desc-wildall.txt", FALSE, RF8_WILD_ALL);
+    status = spoil_categorized_mon_desc();
     if (status != SPOILER_OUTPUT_SUCCESS)
         return status;
 
index fc860d5..f28407d 100644 (file)
@@ -5,8 +5,10 @@
 #include "object/object-kind.h"
 #include "system/alloc-entries.h"
 #include "system/floor-type-definition.h"
+#include "util/probability-table.h"
 #include "view/display-messages.h"
 #include "world/world.h"
+#include <iterator>
 
 /*!
  * @brief グローバルオブジェクト配列から空きを取得する /
@@ -63,10 +65,6 @@ OBJECT_IDX o_pop(floor_type *floor_ptr)
  */
 OBJECT_IDX get_obj_num(player_type *owner_ptr, DEPTH level, BIT_FLAGS mode)
 {
-    int i, j, p;
-    KIND_OBJECT_IDX k_idx;
-    long value, total;
-    object_kind *k_ptr;
     alloc_entry *table = alloc_kind_table;
 
     if (level > MAX_DEPTH - 1)
@@ -78,61 +76,38 @@ OBJECT_IDX get_obj_num(player_type *owner_ptr, DEPTH level, BIT_FLAGS mode)
         }
     }
 
-    total = 0L;
-    for (i = 0; i < alloc_kind_size; i++) {
+    // 候補の確率テーブル生成
+    ProbabilityTable<int> prob_table;
+    for (int i = 0; i < alloc_kind_size; i++) {
         if (table[i].level > level)
             break;
 
-        table[i].prob3 = 0;
-        k_idx = table[i].index;
-        k_ptr = &k_info[k_idx];
+        KIND_OBJECT_IDX k_idx = table[i].index;
+        object_kind *k_ptr = &k_info[k_idx];
 
         if ((mode & AM_FORBID_CHEST) && (k_ptr->tval == TV_CHEST))
             continue;
 
-        table[i].prob3 = table[i].prob2;
-        total += table[i].prob3;
+        prob_table.entry_item(i, table[i].prob2);
     }
 
-    if (total <= 0)
+    // 候補なし
+    if (prob_table.empty())
         return 0;
 
-    value = randint0(total);
-    for (i = 0; i < alloc_kind_size; i++) {
-        if (value < table[i].prob3)
-            break;
-
-        value = value - table[i].prob3;
-    }
-
-    p = randint0(100);
-    if (p < 60) {
-        j = i;
-        value = randint0(total);
-        for (i = 0; i < alloc_kind_size; i++) {
-            if (value < table[i].prob3)
-                break;
-
-            value = value - table[i].prob3;
-        }
+    // 40%で1回、50%で2回、10%で3回抽選し、その中で一番レベルが高いアイテムを選択する
+    int n = 1;
 
-        if (table[i].level < table[j].level)
-            i = j;
-    }
+    const int p = randint0(100);
+    if (p < 60)
+        n++;
+    if (p < 10)
+        n++;
 
-    if (p >= 10)
-        return (table[i].index);
+    std::vector<int> result;
+    ProbabilityTable<int>::lottery(std::back_inserter(result), prob_table, n);
 
-    j = i;
-    value = randint0(total);
-    for (i = 0; i < alloc_kind_size; i++) {
-        if (value < table[i].prob3)
-            break;
-
-        value = value - table[i].prob3;
-    }
+    auto it = std::max_element(result.begin(), result.end(), [table](int a, int b) { return table[a].level < table[b].level; });
 
-    if (table[i].level < table[j].level)
-        i = j;
-    return (table[i].index);
+    return table[*it].index;
 }
index ecbbba9..394e20d 100644 (file)
@@ -6,66 +6,67 @@
 
 typedef struct world_type {
 
-       POSITION max_wild_x; /*!< Maximum size of the wilderness */
-       POSITION max_wild_y; /*!< Maximum size of the wilderness */
-       GAME_TURN game_turn;                    /*!< 画面表示上のゲーム時間基準となるターン / Current game turn */
-       GAME_TURN game_turn_limit;              /*!< game_turnの最大値 / Limit of game_turn */
-       GAME_TURN dungeon_turn;                 /*!< NASTY生成の計算に関わる内部ターン値 / Game turn in dungeon */
-       GAME_TURN dungeon_turn_limit;   /*!< dungeon_turnの最大値 / Limit of game_turn in dungeon */
-       GAME_TURN arena_start_turn;             /*!< 闘技場賭博の開始ターン値 */
-       u32b start_time;
-       u16b noscore;             /* Cheating flags */
-       u16b total_winner;        /* Total winner */
+    POSITION max_wild_x; /*!< Maximum size of the wilderness */
+    POSITION max_wild_y; /*!< Maximum size of the wilderness */
+    GAME_TURN game_turn; /*!< 画面表示上のゲーム時間基準となるターン / Current game turn */
+    GAME_TURN game_turn_limit; /*!< game_turnの最大値 / Limit of game_turn */
+    GAME_TURN dungeon_turn; /*!< NASTY生成の計算に関わる内部ターン値 / Game turn in dungeon */
+    GAME_TURN dungeon_turn_limit; /*!< dungeon_turnの最大値 / Limit of game_turn in dungeon */
+    GAME_TURN arena_start_turn; /*!< 闘技場賭博の開始ターン値 */
+    u32b start_time;
+    u16b noscore; /* Cheating flags */
+    u16b total_winner; /* Total winner */
 
-       MONSTER_IDX timewalk_m_idx;     /*!< 現在時間停止を行っているモンスターのID */
+    MONSTER_IDX timewalk_m_idx; /*!< 現在時間停止を行っているモンスターのID */
 
-       MONRACE_IDX bounty_r_idx[MAX_BOUNTY];
+    MONRACE_IDX bounty_r_idx[MAX_BOUNTY];
+    MONSTER_IDX today_mon; //!< 実際の日替わり賞金首
 
-       u32b play_time; /*!< 実プレイ時間 */
+    u32b play_time; /*!< 実プレイ時間 */
 
-       u32b seed_flavor;               /* Hack -- consistent object colors */
-       u32b seed_town;                 /* Hack -- consistent town layout */
+    u32b seed_flavor; /* Hack -- consistent object colors */
+    u32b seed_town; /* Hack -- consistent town layout */
 
-       bool is_loading_now;    /*!< ロード処理中フラグ...ロード直後にcalc_bonus()時の徳変化、及びsanity_blast()による異常を抑止する */
+    bool is_loading_now; /*!< ロード処理中フラグ...ロード直後にcalc_bonus()時の徳変化、及びsanity_blast()による異常を抑止する */
 
-       /*
-        * Savefile version
-        */
-       byte h_ver_major; /* Savefile version for Hengband 1.1.1 and later */
-       byte h_ver_minor;
-       byte h_ver_patch;
-       byte h_ver_extra;
+    /*
+     * Savefile version
+     */
+    byte h_ver_major; /* Savefile version for Hengband 1.1.1 and later */
+    byte h_ver_minor;
+    byte h_ver_patch;
+    byte h_ver_extra;
 
-       byte sf_extra;          /* Savefile's encoding key */
+    byte sf_extra; /* Savefile's encoding key */
 
-       byte z_major;           /* Savefile version for Hengband */
-       byte z_minor;
-       byte z_patch;
+    byte z_major; /* Savefile version for Hengband */
+    byte z_minor;
+    byte z_patch;
 
-       /*
-        * Savefile information
-        */
-       u32b sf_system;                 /* Operating system info */
-       u32b sf_when;                   /* Time when savefile created */
-       u16b sf_lives;                  /* Number of past "lives" with this file */
-       u16b sf_saves;                  /* Number of "saves" during this life */
+    /*
+     * Savefile information
+     */
+    u32b sf_system; /* Operating system info */
+    u32b sf_when; /* Time when savefile created */
+    u16b sf_lives; /* Number of past "lives" with this file */
+    u16b sf_saves; /* Number of "saves" during this life */
 
-       bool character_generated;       /* The character exists */
-       bool character_dungeon;         /* The character has a dungeon */
-       bool character_loaded;          /* The character was loaded from a savefile */
-       bool character_saved;           /* The character was just saved to a savefile */
+    bool character_generated; /* The character exists */
+    bool character_dungeon; /* The character has a dungeon */
+    bool character_loaded; /* The character was loaded from a savefile */
+    bool character_saved; /* The character was just saved to a savefile */
 
-       byte character_icky_depth;      /* The game is in an icky full screen mode */
-       bool character_xtra;            /* The game is in an icky startup mode */
+    byte character_icky_depth; /* The game is in an icky full screen mode */
+    bool character_xtra; /* The game is in an icky startup mode */
 
-       bool creating_savefile;         /* New savefile is currently created */
+    bool creating_savefile; /* New savefile is currently created */
 
-       bool wizard;              /* This world under wizard mode */
+    bool wizard; /* This world under wizard mode */
 
-       OBJECT_IDX max_o_idx; /*!< Maximum number of objects in the level */
-       MONSTER_IDX max_m_idx; /*!< Maximum number of monsters in the level */
+    OBJECT_IDX max_o_idx; /*!< Maximum number of objects in the level */
+    MONSTER_IDX max_m_idx; /*!< Maximum number of monsters in the level */
 
-       DUNGEON_IDX max_d_idx;
+    DUNGEON_IDX max_d_idx;
 
 } world_type;