Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "JSInterface_Simulation.h"
21 :
22 : #include "graphics/GameView.h"
23 : #include "ps/ConfigDB.h"
24 : #include "ps/Game.h"
25 : #include "ps/GameSetup/Config.h"
26 : #include "ps/Pyrogenesis.h"
27 : #include "scriptinterface/FunctionWrapper.h"
28 : #include "scriptinterface/StructuredClone.h"
29 : #include "simulation2/components/ICmpAIManager.h"
30 : #include "simulation2/components/ICmpCommandQueue.h"
31 : #include "simulation2/components/ICmpGuiInterface.h"
32 : #include "simulation2/components/ICmpObstruction.h"
33 : #include "simulation2/components/ICmpPosition.h"
34 : #include "simulation2/components/ICmpSelectable.h"
35 : #include "simulation2/helpers/Geometry.h"
36 : #include "simulation2/helpers/Selection.h"
37 : #include "simulation2/Simulation2.h"
38 : #include "simulation2/system/Entity.h"
39 :
40 : #include <array>
41 : #include <fstream>
42 :
43 : namespace JSI_Simulation
44 : {
45 0 : JS::Value GuiInterfaceCall(const ScriptInterface& scriptInterface, const std::wstring& name, JS::HandleValue data)
46 : {
47 0 : if (!g_Game)
48 0 : return JS::UndefinedValue();
49 :
50 0 : CSimulation2* sim = g_Game->GetSimulation2();
51 0 : ENSURE(sim);
52 :
53 0 : CmpPtr<ICmpGuiInterface> cmpGuiInterface(*sim, SYSTEM_ENTITY);
54 0 : if (!cmpGuiInterface)
55 0 : return JS::UndefinedValue();
56 :
57 0 : ScriptRequest rqSim(sim->GetScriptInterface());
58 0 : JS::RootedValue arg(rqSim.cx, Script::CloneValueFromOtherCompartment(sim->GetScriptInterface(), scriptInterface, data));
59 0 : JS::RootedValue ret(rqSim.cx);
60 0 : cmpGuiInterface->ScriptCall(g_Game->GetViewedPlayerID(), name, arg, &ret);
61 :
62 0 : return Script::CloneValueFromOtherCompartment(scriptInterface, sim->GetScriptInterface(), ret);
63 : }
64 :
65 0 : void PostNetworkCommand(const ScriptInterface& scriptInterface, JS::HandleValue cmd)
66 : {
67 0 : if (!g_Game)
68 0 : return;
69 :
70 0 : CSimulation2* sim = g_Game->GetSimulation2();
71 0 : ENSURE(sim);
72 :
73 0 : CmpPtr<ICmpCommandQueue> cmpCommandQueue(*sim, SYSTEM_ENTITY);
74 0 : if (!cmpCommandQueue)
75 0 : return;
76 :
77 0 : ScriptRequest rqSim(sim->GetScriptInterface());
78 0 : JS::RootedValue cmd2(rqSim.cx, Script::CloneValueFromOtherCompartment(sim->GetScriptInterface(), scriptInterface, cmd));
79 :
80 0 : cmpCommandQueue->PostNetworkCommand(cmd2);
81 : }
82 :
83 0 : void DumpSimState()
84 : {
85 0 : OsPath path = psLogDir()/"sim_dump.txt";
86 0 : std::ofstream file (OsString(path).c_str(), std::ofstream::out | std::ofstream::trunc);
87 0 : g_Game->GetSimulation2()->DumpDebugState(file);
88 0 : }
89 :
90 0 : entity_id_t PickEntityAtPoint(int x, int y)
91 : {
92 0 : return EntitySelection::PickEntityAtPoint(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x, y, g_Game->GetViewedPlayerID(), false);
93 : }
94 :
95 0 : std::vector<entity_id_t> PickPlayerEntitiesInRect(int x0, int y0, int x1, int y1, int player)
96 : {
97 0 : return EntitySelection::PickEntitiesInRect(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x0, y0, x1, y1, player, false);
98 : }
99 :
100 0 : std::vector<entity_id_t> PickPlayerEntitiesOnScreen(int player)
101 : {
102 0 : return EntitySelection::PickEntitiesInRect(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), 0, 0, g_xres, g_yres, player, false);
103 : }
104 :
105 0 : std::vector<entity_id_t> PickNonGaiaEntitiesOnScreen()
106 : {
107 0 : return EntitySelection::PickNonGaiaEntitiesInRect(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), 0, 0, g_xres, g_yres, false);
108 : }
109 :
110 0 : std::vector<entity_id_t> GetEntitiesWithStaticObstructionOnScreen()
111 : {
112 : struct StaticObstructionFilter
113 : {
114 0 : bool operator()(IComponent* cmp)
115 : {
116 0 : ICmpObstruction* cmpObstruction = static_cast<ICmpObstruction*>(cmp);
117 0 : return cmpObstruction->GetObstructionType() == ICmpObstruction::STATIC;
118 : }
119 : };
120 0 : return EntitySelection::GetEntitiesWithComponentInRect<StaticObstructionFilter>(*g_Game->GetSimulation2(), IID_Obstruction, *g_Game->GetView()->GetCamera(), 0, 0, g_xres, g_yres);
121 : }
122 :
123 0 : JS::Value GetEdgesOfStaticObstructionsOnScreenNearTo(const ScriptInterface& scriptInterface, entity_pos_t x, entity_pos_t z)
124 : {
125 0 : if (!g_Game)
126 0 : return JS::UndefinedValue();
127 :
128 0 : CSimulation2* sim = g_Game->GetSimulation2();
129 0 : ENSURE(sim);
130 :
131 0 : ScriptRequest rq(scriptInterface);
132 0 : JS::RootedValue edgeList(rq.cx);
133 0 : Script::CreateArray(rq, &edgeList);
134 0 : int edgeListIndex = 0;
135 :
136 0 : float distanceThreshold = 10.0f;
137 0 : CFG_GET_VAL("gui.session.snaptoedgesdistancethreshold", distanceThreshold);
138 0 : CFixedVector2D entityPos(x, z);
139 :
140 0 : std::vector<entity_id_t> entities = GetEntitiesWithStaticObstructionOnScreen();
141 0 : for (entity_id_t entity : entities)
142 : {
143 0 : CmpPtr<ICmpObstruction> cmpObstruction(sim->GetSimContext(), entity);
144 0 : if (!cmpObstruction)
145 0 : continue;
146 :
147 0 : CmpPtr<ICmpPosition> cmpPosition(sim->GetSimContext(), entity);
148 0 : if (!cmpPosition || !cmpPosition->IsInWorld())
149 0 : continue;
150 :
151 0 : CFixedVector2D halfSize = cmpObstruction->GetStaticSize() / 2;
152 0 : if (halfSize.X.IsZero() || halfSize.Y.IsZero() || std::max(halfSize.X, halfSize.Y) <= fixed::FromInt(2))
153 0 : continue;
154 :
155 : std::array<CFixedVector2D, 4> corners = {
156 : CFixedVector2D(-halfSize.X, -halfSize.Y),
157 : CFixedVector2D(-halfSize.X, halfSize.Y),
158 : halfSize,
159 : CFixedVector2D(halfSize.X, -halfSize.Y)
160 0 : };
161 0 : const fixed angle = cmpPosition->GetRotation().Y;
162 0 : for (CFixedVector2D& corner : corners)
163 0 : corner = corner.Rotate(angle) + cmpPosition->GetPosition2D();
164 :
165 0 : for (size_t i = 0; i < corners.size(); ++i)
166 : {
167 0 : JS::RootedValue edge(rq.cx);
168 0 : const CFixedVector2D& corner = corners[i];
169 0 : const CFixedVector2D& nextCorner = corners[(i + 1) % corners.size()];
170 :
171 : const fixed distanceToEdge =
172 0 : Geometry::DistanceToSegment(entityPos, corner, nextCorner);
173 0 : if (distanceToEdge.ToFloat() > distanceThreshold)
174 0 : continue;
175 :
176 0 : CFixedVector2D normal = -(nextCorner - corner).Perpendicular();
177 0 : normal.Normalize();
178 0 : Script::CreateObject(
179 : rq,
180 : &edge,
181 : "begin", corner,
182 : "end", nextCorner,
183 : "angle", angle,
184 : "normal", normal,
185 : "order", "cw");
186 :
187 0 : Script::SetPropertyInt(rq, edgeList, edgeListIndex++, edge);
188 : }
189 : }
190 0 : return edgeList;
191 : }
192 :
193 0 : std::vector<entity_id_t> PickSimilarPlayerEntities(const std::string& templateName, bool includeOffScreen, bool matchRank, bool allowFoundations)
194 : {
195 0 : return EntitySelection::PickSimilarEntities(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), templateName, g_Game->GetViewedPlayerID(), includeOffScreen, matchRank, false, allowFoundations);
196 : }
197 :
198 0 : JS::Value GetAIs(const ScriptInterface& scriptInterface)
199 : {
200 0 : return ICmpAIManager::GetAIs(scriptInterface);
201 : }
202 :
203 0 : void SetBoundingBoxDebugOverlay(bool enabled)
204 : {
205 0 : ICmpSelectable::ms_EnableDebugOverlays = enabled;
206 0 : }
207 :
208 12 : void RegisterScriptFunctions(const ScriptRequest& rq)
209 : {
210 12 : ScriptFunction::Register<&GuiInterfaceCall>(rq, "GuiInterfaceCall");
211 12 : ScriptFunction::Register<&PostNetworkCommand>(rq, "PostNetworkCommand");
212 12 : ScriptFunction::Register<&DumpSimState>(rq, "DumpSimState");
213 12 : ScriptFunction::Register<&GetAIs>(rq, "GetAIs");
214 12 : ScriptFunction::Register<&PickEntityAtPoint>(rq, "PickEntityAtPoint");
215 12 : ScriptFunction::Register<&PickPlayerEntitiesInRect>(rq, "PickPlayerEntitiesInRect");
216 12 : ScriptFunction::Register<&PickPlayerEntitiesOnScreen>(rq, "PickPlayerEntitiesOnScreen");
217 12 : ScriptFunction::Register<&PickNonGaiaEntitiesOnScreen>(rq, "PickNonGaiaEntitiesOnScreen");
218 12 : ScriptFunction::Register<&GetEntitiesWithStaticObstructionOnScreen>(rq, "GetEntitiesWithStaticObstructionOnScreen");
219 12 : ScriptFunction::Register<&GetEdgesOfStaticObstructionsOnScreenNearTo>(rq, "GetEdgesOfStaticObstructionsOnScreenNearTo");
220 12 : ScriptFunction::Register<&PickSimilarPlayerEntities>(rq, "PickSimilarPlayerEntities");
221 12 : ScriptFunction::Register<&SetBoundingBoxDebugOverlay>(rq, "SetBoundingBoxDebugOverlay");
222 12 : }
223 0 : }
|