1 /** @file world/actions.cpp
2  *
3  * @authors Copyright (c) 2015-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  *
5  * @par License
6  * GPL: http://www.gnu.org/licenses/gpl.html
7  *
8  * <small>This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; either version 2 of the License, or (at your
11  * option) any later version. This program is distributed in the hope that it
12  * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
14  * Public License for more details. You should have received a copy of the GNU
15  * General Public License along with this program; if not, see:
16  * http://www.gnu.org/licenses</small>
17  */
18 
19 #include "doomsday/world/actions.h"
20 #include "doomsday/gameapi.h"
21 #include "doomsday/doomsdayapp.h"
22 #include "doomsday/players.h"
23 #include "doomsday/world/mobjthinkerdata.h"
24 #include "doomsday/defs/ded.h"
25 
26 #include <de/DictionaryValue>
27 #include <de/NativePointerValue>
28 #include <de/Process>
29 #include <de/String>
30 #include <de/TextValue>
31 #include <QMap>
32 
33 using namespace de;
34 
35 static QMap<String, acfnptr_t> s_actions; ///< name => native function pointer.
36 static String s_currentAction;
37 
A_DoomsdayScript(void * actor)38 static void C_DECL A_DoomsdayScript(void *actor)
39 {
40     struct mobj_s *mobj = reinterpret_cast<struct mobj_s *>(actor);
41     int plrNum = -1;
42 
43     auto &plrs = DoomsdayApp::players();
44 
45     // The actor can also be a player in the case of psprites.
46     // Look up the corresponding player.
47     {
48         for (int i = 0; i < DDMAXPLAYERS; ++i)
49         {
50             // Note: It is assumed that the player data structure begins with a pointer to
51             // the ddplayer_t.
52 
53             if (&plrs.at(i).publicData() ==
54                 *reinterpret_cast<const ddplayer_t * const *>(actor))
55             {
56                 // Refer to the player mobj instead.
57                 mobj = plrs.at(i).publicData().mo;
58                 plrNum = i;
59             }
60         }
61     }
62 
63     LOG_AS("A_DoomsdayScript");
64     try
65     {
66         const ThinkerData &data = THINKER_DATA(mobj->thinker, ThinkerData);
67         Record ns;
68         if (plrNum >= 0)
69         {
70             ns.add(new Variable(QStringLiteral("player"),
71                                 new RecordValue(plrs.at(plrNum).objectNamespace())));
72         }
73         ns.add(new Variable(QStringLiteral("self"), new RecordValue(data.objectNamespace())));
74         Process proc(&ns);
75         const Script script(s_currentAction);
76         proc.run(script);
77         proc.execute();
78     }
79     catch (const Error &er)
80     {
81         LOG_SCR_ERROR(er.asText());
82     }
83 }
84 
isScriptAction(const String & name)85 static bool isScriptAction(const String &name)
86 {
87     return !name.beginsWith("A_");
88 }
89 
P_GetGameActions()90 void P_GetGameActions()
91 {
92     s_actions.clear();
93 
94     // Action links are provided by the game (which owns the actual action functions).
95     if (auto getVar = DoomsdayApp::plugins().gameExports().GetPointer)
96     {
97         auto const *links = (actionlink_t const *) getVar(DD_ACTION_LINK);
98         for (actionlink_t const *link = links; link && link->name; link++)
99         {
100             s_actions.insert(String(link->name).toLower(), link->func);
101         }
102     }
103 }
104 
P_SetCurrentAction(const String & name)105 void P_SetCurrentAction(const String &name)
106 {
107     s_currentAction = name;
108 }
109 
P_SetCurrentActionState(int state)110 void P_SetCurrentActionState(int state)
111 {
112     P_SetCurrentAction(DED_Definitions()->states[state].gets(QStringLiteral("action")));
113 }
114 
P_GetAction(const String & name)115 acfnptr_t P_GetAction(const String &name)
116 {
117     if (!name.isEmpty())
118     {
119         if (isScriptAction(name))
120         {
121             return de::function_cast<acfnptr_t>(A_DoomsdayScript);
122         }
123         auto found = s_actions.find(name.toLower());
124         if (found != s_actions.end()) return found.value();
125     }
126     return nullptr;  // Not found.
127 }
128 
P_GetAction(const char * name)129 acfnptr_t P_GetAction(const char *name)
130 {
131     return P_GetAction(String(name));
132 }
133