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