1 /*
2 * This file is part of the Colobot: Gold Edition source code
3 * Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam
4 * http://epsitec.ch; http://colobot.info; http://github.com/colobot
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see http://gnu.org/licenses
18 */
19
20 #include "object/implementation/programmable_impl.h"
21
22 #include "CBot/CBot.h"
23
24 #include "common/global.h"
25 #include "common/profiler.h"
26
27 #include "level/robotmain.h"
28
29 #include "math/all.h"
30
31 #include "object/object.h"
32 #include "object/old_object.h"
33
34 #include "object/interface/controllable_object.h"
35 #include "object/interface/task_executor_object.h"
36
37 #include "object/motion/motion.h"
38 #include "object/motion/motionvehicle.h"
39
40 #include "physics/physics.h"
41
42 #include "script/script.h"
43
44 #include "ui/controls/edit.h"
45
46 #include <algorithm>
47 #include <iomanip>
48
CProgrammableObjectImpl(ObjectInterfaceTypes & types,CObject * object)49 CProgrammableObjectImpl::CProgrammableObjectImpl(ObjectInterfaceTypes& types, CObject* object)
50 : CProgrammableObject(types),
51 m_object(object),
52 m_activity(true),
53 m_cmdLine(),
54 m_currentProgram(nullptr),
55 m_traceRecord(false),
56 m_traceOper(TO_STOP),
57 m_traceAngle(0.0f),
58 m_traceColor(TraceColor::Default),
59 m_traceRecordIndex(0)
60 {
61 //assert(m_object->Implements(ObjectInterfaceType::TaskExecutor));
62 }
63
~CProgrammableObjectImpl()64 CProgrammableObjectImpl::~CProgrammableObjectImpl()
65 {}
66
EventProcess(const Event & event)67 bool CProgrammableObjectImpl::EventProcess(const Event &event)
68 {
69 if (event.type == EVENT_FRAME)
70 {
71 if ( m_object->Implements(ObjectInterfaceType::Destroyable) && dynamic_cast<CDestroyableObject&>(*m_object).IsDying() && IsProgram() )
72 {
73 StopProgram();
74 }
75
76 if ( GetActivity() )
77 {
78 CProfiler::StartPerformanceCounter(PCNT_UPDATE_CBOT);
79 if ( IsProgram() ) // current program?
80 {
81 if ( m_currentProgram->script->Continue() )
82 {
83 StopProgram();
84 }
85 }
86
87 if ( m_traceRecord ) // registration of the design in progress?
88 {
89 TraceRecordFrame();
90 }
91 CProfiler::StopPerformanceCounter(PCNT_UPDATE_CBOT);
92 }
93 }
94
95 return true;
96 }
97
98
SetActivity(bool activity)99 void CProgrammableObjectImpl::SetActivity(bool activity)
100 {
101 m_activity = activity;
102 }
103
GetActivity()104 bool CProgrammableObjectImpl::GetActivity()
105 {
106 return m_activity;
107 }
108
109
RunProgram(Program * program)110 void CProgrammableObjectImpl::RunProgram(Program* program)
111 {
112 if ( program->script->Run() )
113 {
114 m_currentProgram = program; // start new program
115 m_object->UpdateInterface();
116 if (m_object->Implements(ObjectInterfaceType::Controllable) && dynamic_cast<CControllableObject&>(*m_object).GetTrainer())
117 CRobotMain::GetInstancePointer()->StartMissionTimer();
118 }
119 }
120
StopProgram()121 void CProgrammableObjectImpl::StopProgram()
122 {
123 if ( m_currentProgram != nullptr )
124 {
125 m_currentProgram->script->Stop();
126 }
127
128 m_currentProgram = nullptr;
129
130 m_object->UpdateInterface();
131 }
132
GetCurrentProgram()133 Program* CProgrammableObjectImpl::GetCurrentProgram()
134 {
135 return m_currentProgram;
136 }
137
IsProgram()138 bool CProgrammableObjectImpl::IsProgram()
139 {
140 return m_currentProgram != nullptr;
141 }
142
143
144 // Load a stack of script implementation from a file.
145
ReadStack(std::istream & istr)146 bool CProgrammableObjectImpl::ReadStack(std::istream &istr)
147 {
148 short op;
149
150 if (!CBot::ReadShort(istr, op)) return false;
151 if ( op == 1 ) // run ?
152 {
153 if (!CBot::ReadShort(istr, op)) return false; // program rank
154 if ( op >= 0 )
155 {
156 if (m_object->Implements(ObjectInterfaceType::ProgramStorage))
157 {
158 int count = static_cast<int>(dynamic_cast<CProgramStorageObject&>(*m_object).GetProgramCount());
159 if (!(op < count))
160 {
161 GetLogger()->Info("Object program count: %i\n", count);
162 GetLogger()->Error("Error in file: program index out of range: %i\n", op);
163 return false;
164 }
165
166 m_currentProgram = dynamic_cast<CProgramStorageObject&>(*m_object).GetProgram(op);
167 if (!m_currentProgram->script->ReadStack(istr))
168 {
169 GetLogger()->Error("Restore state failed at program index: %i\n", op);
170 int errNum = m_currentProgram->script->GetError();
171 if (errNum != 0)
172 {
173 std::string errStr;
174 m_currentProgram->script->GetError(errStr);
175 GetLogger()->Error("Program reports error: %i:(%s)\n", errNum, errStr.c_str());
176 }
177 return false;
178 }
179 }
180 else
181 {
182 GetLogger()->Error("Object is not a program storage object\n");
183 return false;
184 }
185 }
186 }
187
188 return true;
189 }
190
191 // Save the script implementation stack of a file.
192
WriteStack(std::ostream & ostr)193 bool CProgrammableObjectImpl::WriteStack(std::ostream &ostr)
194 {
195 short op;
196
197 if ( m_currentProgram != nullptr && // current program?
198 m_currentProgram->script->IsRunning() )
199 {
200 op = 1; // run
201 if (!CBot::WriteShort(ostr, op)) return false;
202
203 op = -1;
204 if (m_object->Implements(ObjectInterfaceType::ProgramStorage))
205 {
206 op = dynamic_cast<CProgramStorageObject&>(*m_object).GetProgramIndex(m_currentProgram);
207 }
208 if (!CBot::WriteShort(ostr, op)) return false;
209
210 if (!m_currentProgram->script->WriteStack(ostr))
211 {
212 GetLogger()->Error("Save state failed at program index: %i\n", op);
213 return false;
214 }
215 return true;
216 }
217
218 op = 0; // stop
219 return CBot::WriteShort(ostr, op);
220 }
221
222
223
224 const int MAXTRACERECORD = 1000;
225
226 // Start of registration of the design.
227
TraceRecordStart()228 void CProgrammableObjectImpl::TraceRecordStart()
229 {
230 if (m_traceRecord)
231 {
232 TraceRecordStop();
233 }
234
235 assert(m_object->Implements(ObjectInterfaceType::TraceDrawing));
236 CTraceDrawingObject* traceDrawing = dynamic_cast<CTraceDrawingObject*>(m_object);
237
238 m_traceRecord = true;
239
240 m_traceOper = TO_STOP;
241
242 m_tracePos = m_object->GetPosition();
243 m_traceAngle = m_object->GetRotationY();
244
245 if ( traceDrawing->GetTraceDown() ) // pencil down?
246 {
247 m_traceColor = traceDrawing->GetTraceColor();
248 }
249 else // pen up?
250 {
251 m_traceColor = TraceColor::Default;
252 }
253
254 m_traceRecordBuffer = MakeUniqueArray<TraceRecord>(MAXTRACERECORD);
255 m_traceRecordIndex = 0;
256 }
257
258 // Saving the current drawing.
259
TraceRecordFrame()260 void CProgrammableObjectImpl::TraceRecordFrame()
261 {
262 TraceOper oper = TO_STOP;
263 Math::Vector pos;
264 float angle, len, speed;
265
266 assert(m_object->Implements(ObjectInterfaceType::TraceDrawing));
267 CTraceDrawingObject* traceDrawing = dynamic_cast<CTraceDrawingObject*>(m_object);
268
269 CPhysics* physics = dynamic_cast<CMovableObject&>(*m_object).GetPhysics();
270
271 speed = physics->GetLinMotionX(MO_REASPEED);
272 if ( speed > 0.0f ) oper = TO_ADVANCE;
273 if ( speed < 0.0f ) oper = TO_RECEDE;
274
275 speed = physics->GetCirMotionY(MO_REASPEED);
276 if ( speed != 0.0f ) oper = TO_TURN;
277
278 TraceColor color = TraceColor::Default;
279 if ( traceDrawing->GetTraceDown() ) // pencil down?
280 {
281 color = traceDrawing->GetTraceColor();
282 }
283
284 if ( oper != m_traceOper ||
285 color != m_traceColor )
286 {
287 if ( m_traceOper == TO_ADVANCE ||
288 m_traceOper == TO_RECEDE )
289 {
290 pos = m_object->GetPosition();
291 len = Math::DistanceProjected(pos, m_tracePos);
292 TraceRecordOper(m_traceOper, len);
293 }
294 if ( m_traceOper == TO_TURN )
295 {
296 angle = m_object->GetRotationY()-m_traceAngle;
297 TraceRecordOper(m_traceOper, angle);
298 }
299
300 if ( color != m_traceColor )
301 {
302 TraceRecordOper(TO_PEN, static_cast<float>(color));
303 }
304
305 m_traceOper = oper;
306 m_tracePos = m_object->GetPosition();
307 m_traceAngle = m_object->GetRotationY();
308 m_traceColor = color;
309 }
310 }
311
312 // End of the registration of the design. Program generates the CBOT.
313
TraceRecordStop()314 void CProgrammableObjectImpl::TraceRecordStop()
315 {
316 TraceOper lastOper, curOper;
317 float lastParam, curParam;
318
319 m_traceRecord = false;
320
321 std::stringstream buffer;
322 buffer << "extern void object::AutoDraw()\n{\n";
323
324 lastOper = TO_STOP;
325 lastParam = 0.0f;
326 for ( int i=0 ; i<m_traceRecordIndex ; i++ )
327 {
328 curOper = m_traceRecordBuffer[i].oper;
329 curParam = m_traceRecordBuffer[i].param;
330
331 if ( curOper == lastOper )
332 {
333 if ( curOper == TO_PEN )
334 {
335 lastParam = curParam;
336 }
337 else
338 {
339 lastParam += curParam;
340 }
341 }
342 else
343 {
344 TraceRecordPut(buffer, lastOper, lastParam);
345 lastOper = curOper;
346 lastParam = curParam;
347 }
348 }
349 TraceRecordPut(buffer, lastOper, lastParam);
350
351 m_traceRecordBuffer.reset();
352
353 buffer << "}\n";
354
355 assert(m_object->Implements(ObjectInterfaceType::ProgramStorage));
356 Program* prog = dynamic_cast<CProgramStorageObject&>(*m_object).AddProgram();
357 prog->script->SendScript(buffer.str().c_str());
358 }
359
360 // Saves an instruction CBOT.
361
TraceRecordOper(TraceOper oper,float param)362 bool CProgrammableObjectImpl::TraceRecordOper(TraceOper oper, float param)
363 {
364 int i;
365
366 i = m_traceRecordIndex;
367 if ( i >= MAXTRACERECORD ) return false;
368
369 m_traceRecordBuffer[i].oper = oper;
370 m_traceRecordBuffer[i].param = param;
371
372 m_traceRecordIndex = i+1;
373 return true;
374 }
375
376 // Generates an instruction CBOT.
377
TraceRecordPut(std::stringstream & buffer,TraceOper oper,float param)378 bool CProgrammableObjectImpl::TraceRecordPut(std::stringstream& buffer, TraceOper oper, float param)
379 {
380 if ( oper == TO_ADVANCE )
381 {
382 param /= g_unit;
383 buffer << "\tmove(" << std::fixed << std::setprecision(1) << param << ");\n";
384 }
385
386 if ( oper == TO_RECEDE )
387 {
388 param /= g_unit;
389 buffer << "\tmove(-" << std::fixed << std::setprecision(1) << param << ");\n";
390 }
391
392 if ( oper == TO_TURN )
393 {
394 param = -param*180.0f/Math::PI;
395 buffer << "\tturn(" << static_cast<int>(param) << ");\n";
396 }
397
398 if ( oper == TO_PEN )
399 {
400 TraceColor color = static_cast<TraceColor>(static_cast<int>(param));
401 if ( color == TraceColor::Default )
402 buffer << "\tpenup();\n";
403 else
404 buffer << "\tpendown(" << TraceColorName(color) << ");\n";
405 }
406
407 return true;
408 }
409
IsTraceRecord()410 bool CProgrammableObjectImpl::IsTraceRecord()
411 {
412 return m_traceRecord;
413 }
414
415
SetCmdLine(unsigned int rank,float value)416 void CProgrammableObjectImpl::SetCmdLine(unsigned int rank, float value)
417 {
418 if (rank == m_cmdLine.size())
419 {
420 m_cmdLine.push_back(value);
421 }
422 else if (rank < m_cmdLine.size())
423 {
424 m_cmdLine[rank] = value;
425 }
426 else
427 {
428 // should never happen
429 assert(false);
430 }
431 }
432
GetCmdLine(unsigned int rank)433 float CProgrammableObjectImpl::GetCmdLine(unsigned int rank)
434 {
435 if ( rank >= m_cmdLine.size() ) return 0.0f;
436 return m_cmdLine[rank];
437 }
438
GetCmdLine()439 std::vector<float>& CProgrammableObjectImpl::GetCmdLine()
440 {
441 return m_cmdLine;
442 }
443